mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
6159 lines
138 KiB
6159 lines
138 KiB
/*++
|
|
|
|
Copyright (c) 1990 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
scsiboot.c
|
|
|
|
Abstract:
|
|
|
|
This is the NT SCSI port driver.
|
|
|
|
Author:
|
|
|
|
Mike Glass
|
|
Jeff Havens
|
|
|
|
Environment:
|
|
|
|
kernel mode only
|
|
|
|
Notes:
|
|
|
|
This module is linked into the kernel.
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#if !defined(DECSTATION)
|
|
|
|
#include "stdarg.h"
|
|
#include "stdio.h"
|
|
#if defined(_MIPS_)
|
|
#include "..\fw\mips\fwp.h"
|
|
#elif defined(_ALPHA_)
|
|
#include "bldr.h"
|
|
#elif defined(_PPC_)
|
|
#include "..\fw\ppc\fwp.h"
|
|
#elif defined(_IA64_)
|
|
#include "bootia64.h"
|
|
#else
|
|
#include "bootx86.h"
|
|
#endif
|
|
#include "scsi.h"
|
|
#include "scsiboot.h"
|
|
#include "pci.h"
|
|
|
|
#if DBG
|
|
ULONG ScsiDebug = 0;
|
|
#endif
|
|
|
|
ULONG ScsiPortCount;
|
|
PDEVICE_OBJECT ScsiPortDeviceObject[MAXIMUM_NUMBER_OF_SCSIPORT_OBJECTS];
|
|
PINQUIRYDATA InquiryDataBuffer;
|
|
FULL_SCSI_REQUEST_BLOCK PrimarySrb;
|
|
FULL_SCSI_REQUEST_BLOCK RequestSenseSrb;
|
|
FULL_SCSI_REQUEST_BLOCK AbortSrb;
|
|
|
|
|
|
extern PDRIVER_UNLOAD AEDriverUnloadRoutine;
|
|
|
|
//
|
|
// Function declarations
|
|
//
|
|
|
|
ARC_STATUS
|
|
ScsiPortDispatch(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
VOID
|
|
ScsiPortExecute(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
VOID
|
|
ScsiPortStartIo (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
BOOLEAN
|
|
ScsiPortInterrupt(
|
|
IN PKINTERRUPT InterruptObject,
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
);
|
|
|
|
VOID
|
|
ScsiPortCompletionDpc(
|
|
IN PKDPC Dpc,
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
);
|
|
|
|
VOID
|
|
ScsiPortTickHandler(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PVOID Context
|
|
);
|
|
|
|
IO_ALLOCATION_ACTION
|
|
ScsiPortAllocationRoutine (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID MapRegisterBase,
|
|
IN PVOID Context
|
|
);
|
|
|
|
ARC_STATUS
|
|
IssueInquiry(
|
|
IN PDEVICE_EXTENSION deviceExtension,
|
|
IN PLUNINFO LunInfo
|
|
);
|
|
|
|
VOID
|
|
IssueRequestSense(
|
|
IN PDEVICE_EXTENSION deviceExtension,
|
|
IN PSCSI_REQUEST_BLOCK FailingSrb
|
|
);
|
|
|
|
VOID
|
|
ScsiPortInternalCompletion(
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PIRP Irp,
|
|
PVOID Context
|
|
);
|
|
|
|
PSCSI_BUS_SCAN_DATA
|
|
ScsiBusScan(
|
|
IN PDEVICE_EXTENSION DeviceExtension,
|
|
IN UCHAR ScsiBus,
|
|
IN UCHAR InitiatorBusId
|
|
);
|
|
|
|
PLOGICAL_UNIT_EXTENSION
|
|
CreateLogicalUnitExtension(
|
|
IN PDEVICE_EXTENSION DeviceExtension
|
|
);
|
|
|
|
BOOLEAN
|
|
SpStartIoSynchronized (
|
|
PVOID ServiceContext
|
|
);
|
|
|
|
VOID
|
|
IssueAbortRequest(
|
|
IN PDEVICE_EXTENSION DeviceExtension,
|
|
IN PIRP FailingIrp
|
|
);
|
|
|
|
VOID
|
|
SpCheckResetDelay(
|
|
IN PDEVICE_EXTENSION deviceExtension
|
|
);
|
|
|
|
IO_ALLOCATION_ACTION
|
|
SpBuildScatterGather(
|
|
IN struct _DEVICE_OBJECT *DeviceObject,
|
|
IN struct _IRP *Irp,
|
|
IN PVOID MapRegisterBase,
|
|
IN PVOID Context
|
|
);
|
|
|
|
BOOLEAN
|
|
SpGetInterruptState(
|
|
IN PVOID ServiceContext
|
|
);
|
|
|
|
VOID
|
|
SpTimerDpc(
|
|
IN PKDPC Dpc,
|
|
IN PVOID Context,
|
|
IN PVOID SystemContext1,
|
|
IN PVOID SystemContext2
|
|
);
|
|
|
|
PLOGICAL_UNIT_EXTENSION
|
|
GetLogicalUnitExtension(
|
|
PDEVICE_EXTENSION DeviceExtension,
|
|
UCHAR TargetId
|
|
);
|
|
|
|
NTSTATUS
|
|
SpInitializeConfiguration(
|
|
IN PDEVICE_EXTENSION DeviceExtension,
|
|
IN PHW_INITIALIZATION_DATA HwInitData,
|
|
OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo,
|
|
IN BOOLEAN InitialCall
|
|
);
|
|
|
|
NTSTATUS
|
|
SpGetCommonBuffer(
|
|
PDEVICE_EXTENSION DeviceExtension,
|
|
ULONG NonCachedExtensionSize
|
|
);
|
|
|
|
BOOLEAN
|
|
GetPciConfiguration(
|
|
PDRIVER_OBJECT DriverObject,
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PPORT_CONFIGURATION_INFORMATION ConfigInformation,
|
|
ULONG NumberOfAccessRanges,
|
|
PVOID RegistryPath,
|
|
BOOLEAN IsMultiFunction,
|
|
PULONG BusNumber,
|
|
PULONG SlotNumber,
|
|
PULONG FunctionNumber
|
|
);
|
|
|
|
BOOLEAN
|
|
FindPciDevice(
|
|
PHW_INITIALIZATION_DATA HwInitializationData,
|
|
PULONG BusNumber,
|
|
PULONG SlotNumber,
|
|
PULONG FunctionNumber,
|
|
PBOOLEAN IsMultiFunction
|
|
);
|
|
|
|
#ifdef i386
|
|
ULONG
|
|
HalpGetCmosData(
|
|
IN ULONG SourceLocation,
|
|
IN ULONG SourceAddress,
|
|
IN PVOID ReturnBuffer,
|
|
IN ULONG ByteCount
|
|
);
|
|
#endif
|
|
|
|
VOID
|
|
ScsiPortInitializeMdlPages (
|
|
IN OUT PMDL Mdl
|
|
);
|
|
|
|
SCSI_ADAPTER_CONTROL_STATUS
|
|
SpCallAdapterControl(
|
|
IN PDEVICE_EXTENSION Adapter,
|
|
IN SCSI_ADAPTER_CONTROL_TYPE ControlType,
|
|
IN PVOID Parameters
|
|
);
|
|
|
|
VOID
|
|
SpGetSupportedAdapterControlFunctions(
|
|
PDEVICE_EXTENSION Adapter
|
|
);
|
|
|
|
VOID
|
|
SpUnload(
|
|
IN PDRIVER_OBJECT DriverObject
|
|
);
|
|
|
|
//
|
|
// Routines start
|
|
//
|
|
|
|
BOOLEAN
|
|
GetNextPciBus(
|
|
PULONG BusNumber
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Advance ConfigInformation to the next PCI bus if one exists in the
|
|
system. Advances the bus number and calls HalGetBusDataByOffset to
|
|
see if the bus number is valid.
|
|
|
|
Note: *BusNumber has already been scanned in its entirety, we
|
|
simply advance to the start of the next bus. No need to keep
|
|
track of where we might be on this bus.
|
|
|
|
Arguments:
|
|
|
|
BusNumber Pointer to a ULONG containing the last BusNumber tested.
|
|
Will be updated to the next bus number if another PCI
|
|
bus exists.
|
|
|
|
Return Value:
|
|
|
|
TRUE If ConfigInformation has been advanced,
|
|
FALSE If there are not more PCI busses in the system.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG pciBus;
|
|
USHORT pciData;
|
|
ULONG length;
|
|
|
|
pciBus = *BusNumber;
|
|
|
|
DebugPrint((3,"GetNextPciBus: try to advance from bus %d\n", pciBus));
|
|
|
|
if (++pciBus < 256) {
|
|
length = HalGetBusDataByOffset(
|
|
PCIConfiguration,
|
|
pciBus,
|
|
0, // slot number
|
|
&pciData,
|
|
0,
|
|
sizeof(pciData));
|
|
|
|
if (length == sizeof(pciData)) {
|
|
|
|
//
|
|
// HalGetBusDataByOffset returns zero when out of
|
|
// busses. If not out of busses it should always
|
|
// succeed a length = 2 read at offset 0 even if
|
|
// just to return PCI_INVALID_VENDORID.
|
|
//
|
|
|
|
*BusNumber = pciBus;
|
|
return TRUE;
|
|
}
|
|
}
|
|
DebugPrint((3,"GetNextPciBus: test bus %d returned %d\n", pciBus, length));
|
|
return FALSE;
|
|
}
|
|
|
|
ULONG
|
|
ScsiPortInitialize(
|
|
IN PVOID Argument1,
|
|
IN PVOID Argument2,
|
|
IN struct _HW_INITIALIZATION_DATA *HwInitializationData,
|
|
IN PVOID HwContext
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine initializes the port driver.
|
|
|
|
Arguments:
|
|
|
|
Argument1 - Pointer to driver object created by system
|
|
HwInitializationData - Miniport initialization structure
|
|
HwContext - Value passed to miniport driver's config routine
|
|
|
|
Return Value:
|
|
|
|
The function value is the final status from the initialization operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDRIVER_OBJECT driverObject = Argument1;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
PDEVICE_OBJECT deviceObject;
|
|
BOOLEAN checkAdapterControl = FALSE;
|
|
PORT_CONFIGURATION_INFORMATION configInfo;
|
|
KEVENT allocateAdapterEvent;
|
|
ULONG ExtensionAllocationSize;
|
|
ULONG j;
|
|
UCHAR scsiBus;
|
|
PULONG scsiPortNumber;
|
|
ULONG numberOfPageBreaks;
|
|
PIO_SCSI_CAPABILITIES capabilities;
|
|
BOOLEAN callAgain;
|
|
UCHAR ldrString[] = {'n', 't', 'l', 'd','r','=', '1', ';', 0 };
|
|
DEVICE_DESCRIPTION deviceDescription;
|
|
ARC_CODES status;
|
|
BOOLEAN isPci = FALSE;
|
|
BOOLEAN isMultiFunction = FALSE;
|
|
ULONG busNumber = 0;
|
|
ULONG slotNumber = 0;
|
|
ULONG functionNumber = 0;
|
|
BOOLEAN foundOne = FALSE;
|
|
|
|
UNREFERENCED_PARAMETER(Argument2);
|
|
|
|
AEDriverUnloadRoutine = SpUnload;
|
|
|
|
if (HwInitializationData->HwInitializationDataSize > sizeof(HW_INITIALIZATION_DATA)) {
|
|
|
|
DebugPrint((0,"ScsiPortInitialize: Miniport driver wrong version\n"));
|
|
return EBADF;
|
|
}
|
|
|
|
if (HwInitializationData->HwInitializationDataSize >=
|
|
(FIELD_OFFSET(HW_INITIALIZATION_DATA, HwAdapterControl) +
|
|
sizeof(PVOID))) {
|
|
|
|
DebugPrint((2, "ScsiPortInitialize: Miniport may have adapter "
|
|
"control routine\n"));
|
|
checkAdapterControl = TRUE;
|
|
}
|
|
|
|
//
|
|
// Check that each required entry is not NULL.
|
|
//
|
|
|
|
if ((!HwInitializationData->HwInitialize) ||
|
|
(!HwInitializationData->HwFindAdapter) ||
|
|
(!HwInitializationData->HwResetBus)) {
|
|
|
|
DebugPrint((0,
|
|
"ScsiPortInitialize: Miniport driver missing required entry\n"));
|
|
|
|
return EBADF;
|
|
}
|
|
|
|
CallAgain:
|
|
|
|
//
|
|
// Get the configuration information
|
|
//
|
|
|
|
scsiPortNumber = &ScsiPortCount;
|
|
|
|
//
|
|
// Determine if there is room for the next port device object.
|
|
//
|
|
|
|
if (*scsiPortNumber >= MAXIMUM_NUMBER_OF_SCSIPORT_OBJECTS) {
|
|
return foundOne ? ESUCCESS : EIO;
|
|
}
|
|
|
|
//
|
|
// If this is a PCI card then do a quick scan to see if we can find
|
|
// the device.
|
|
//
|
|
|
|
if (HwInitializationData->AdapterInterfaceType == PCIBus &&
|
|
HwInitializationData->VendorIdLength > 0 &&
|
|
HwInitializationData->DeviceIdLength > 0 &&
|
|
HwInitializationData->DeviceId &&
|
|
HwInitializationData->VendorId) {
|
|
|
|
if (!FindPciDevice(HwInitializationData,
|
|
&busNumber,
|
|
&slotNumber,
|
|
&functionNumber,
|
|
&isMultiFunction)) {
|
|
|
|
DebugPrint((1,
|
|
"ScsiPortInitialize: FindPciDevice failed\n"));
|
|
return foundOne ? ESUCCESS : EIO;
|
|
}
|
|
|
|
isPci = TRUE;
|
|
}
|
|
|
|
//
|
|
// Determine size of extensions.
|
|
//
|
|
|
|
ExtensionAllocationSize = DEVICE_EXTENSION_SIZE +
|
|
HwInitializationData->DeviceExtensionSize + sizeof(DEVICE_OBJECT);
|
|
|
|
deviceObject = ExAllocatePool(NonPagedPool, ExtensionAllocationSize);
|
|
|
|
if (deviceObject == NULL) {
|
|
return ENOMEM;
|
|
}
|
|
|
|
RtlZeroMemory(deviceObject, ExtensionAllocationSize);
|
|
|
|
//
|
|
// Set up device extension pointers
|
|
//
|
|
|
|
deviceExtension = deviceObject->DeviceExtension = (PVOID) (deviceObject + 1);
|
|
deviceExtension->DeviceObject = deviceObject;
|
|
|
|
//
|
|
// Save the dependent driver routines in the device extension.
|
|
//
|
|
|
|
deviceExtension->HwInitialize = HwInitializationData->HwInitialize;
|
|
deviceExtension->HwStartIo = HwInitializationData->HwStartIo;
|
|
deviceExtension->HwInterrupt = HwInitializationData->HwInterrupt;
|
|
deviceExtension->HwReset = HwInitializationData->HwResetBus;
|
|
deviceExtension->HwDmaStarted = HwInitializationData->HwDmaStarted;
|
|
deviceExtension->HwLogicalUnitExtensionSize =
|
|
HwInitializationData->SpecificLuExtensionSize;
|
|
|
|
if(checkAdapterControl) {
|
|
deviceExtension->HwAdapterControl = HwInitializationData->HwAdapterControl;
|
|
}
|
|
|
|
deviceExtension->HwDeviceExtension =
|
|
(PVOID)(deviceExtension + 1);
|
|
|
|
//
|
|
// Set indicater as to whether adapter needs kernel mapped buffers.
|
|
//
|
|
|
|
deviceExtension->MapBuffers = HwInitializationData->MapBuffers;
|
|
|
|
//
|
|
// Mark this object as supporting direct I/O so that I/O system
|
|
// will supply mdls in irps.
|
|
//
|
|
|
|
deviceObject->Flags |= DO_DIRECT_IO;
|
|
|
|
//
|
|
// Check if miniport driver requires any noncached memory.
|
|
// SRB extensions will come from zoned memory. A page is
|
|
// allocated as it is the smallest unit of noncached memory
|
|
// allocation.
|
|
//
|
|
|
|
deviceExtension->SrbExtensionSize = HwInitializationData->SrbExtensionSize;
|
|
|
|
//
|
|
// Get the miniport configuration information.
|
|
//
|
|
|
|
capabilities = &deviceExtension->Capabilities;
|
|
|
|
capabilities->Length = sizeof(IO_SCSI_CAPABILITIES);
|
|
|
|
callAgain = FALSE;
|
|
|
|
//
|
|
// This call can't really fail - if it does something is seriously wrong and
|
|
// should fail on the first try.
|
|
//
|
|
|
|
if (!NT_SUCCESS(SpInitializeConfiguration(
|
|
deviceExtension,
|
|
HwInitializationData,
|
|
&configInfo,
|
|
TRUE
|
|
))) {
|
|
|
|
DebugPrint((2, "ScsiPortInitialize: No config info found\n"));
|
|
return(ENODEV);
|
|
}
|
|
|
|
configInfo.NumberOfAccessRanges = HwInitializationData->NumberOfAccessRanges;
|
|
|
|
configInfo.AccessRanges = ExAllocatePool(NonPagedPool,
|
|
sizeof(ACCESS_RANGE) *
|
|
HwInitializationData->NumberOfAccessRanges);
|
|
|
|
if (configInfo.AccessRanges == NULL) {
|
|
|
|
//
|
|
// We're out of memory - it's probably not worth continuing on to
|
|
// try and find more adapters.
|
|
//
|
|
|
|
return (foundOne ? ESUCCESS : EIO);
|
|
}
|
|
|
|
RtlZeroMemory(configInfo.AccessRanges,
|
|
HwInitializationData->NumberOfAccessRanges * sizeof(ACCESS_RANGE));
|
|
|
|
//
|
|
// Initialize configuration information with slot information if PCI bus.
|
|
//
|
|
|
|
if (isPci) {
|
|
|
|
if (!GetPciConfiguration(driverObject,
|
|
deviceObject,
|
|
&configInfo,
|
|
HwInitializationData->NumberOfAccessRanges,
|
|
Argument2,
|
|
isMultiFunction,
|
|
&busNumber,
|
|
&slotNumber,
|
|
&functionNumber)) {
|
|
|
|
DebugPrint((1,
|
|
"ScsiPortInitialize: GetPciConfiguration failed\n"));
|
|
return foundOne ? ESUCCESS : EIO;
|
|
}
|
|
|
|
//
|
|
// Call miniport driver's find adapter routine to search for adapters.
|
|
//
|
|
|
|
if (HwInitializationData->HwFindAdapter(
|
|
deviceExtension->HwDeviceExtension, // DeviceExtension
|
|
HwContext, // HwContext
|
|
NULL, // BusInformation
|
|
(PCHAR)&ldrString, // ArgumentString
|
|
&configInfo, // ConfigurationInformation
|
|
&callAgain // Again
|
|
) != SP_RETURN_FOUND) {
|
|
|
|
return foundOne ? ESUCCESS : EIO;
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// Not PCI, or PCI but the miniport is fundamentally broken
|
|
// and wants to do its own search.
|
|
//
|
|
|
|
//
|
|
// Starting at the current config, examine each bus
|
|
// until we run out of busses.
|
|
//
|
|
|
|
configInfo.SystemIoBusNumber = busNumber;
|
|
|
|
if (HwInitializationData->HwFindAdapter(
|
|
deviceExtension->HwDeviceExtension, // DeviceExtension
|
|
HwContext, // HwContext
|
|
NULL, // BusInformation
|
|
(PCHAR)&ldrString, // ArgumentString
|
|
&configInfo, // ConfigurationInformation
|
|
&callAgain // Again
|
|
) != SP_RETURN_FOUND) {
|
|
|
|
//
|
|
// No device found on this bus, try next bus.
|
|
//
|
|
|
|
if ((HwInitializationData->AdapterInterfaceType != PCIBus) ||
|
|
!GetNextPciBus(&busNumber)) {
|
|
|
|
return foundOne ? ESUCCESS : EIO;
|
|
}
|
|
|
|
goto CallAgain;
|
|
}
|
|
}
|
|
|
|
DebugPrint((1,"ScsiPortInitialize: SCSI adapter IRQ is %d\n",
|
|
configInfo.BusInterruptLevel));
|
|
|
|
DebugPrint((1,"ScsiPortInitialize: SCSI adapter ID is %d\n",
|
|
configInfo.InitiatorBusId[0]));
|
|
|
|
deviceExtension->NumberOfBuses = configInfo.NumberOfBuses;
|
|
|
|
//
|
|
// Free the pointer to the bus data at map register base. This was
|
|
// allocated by ScsiPortGetBusData.
|
|
//
|
|
|
|
if (deviceExtension->MapRegisterBase != NULL) {
|
|
ExFreePool(deviceExtension->MapRegisterBase);
|
|
}
|
|
|
|
//
|
|
// Get the adapter object for this card.
|
|
//
|
|
|
|
if ( deviceExtension->DmaAdapterObject == NULL &&
|
|
(configInfo.Master || configInfo.DmaChannel != 0xFFFFFFFF) ) {
|
|
|
|
deviceDescription.Version = DEVICE_DESCRIPTION_VERSION;
|
|
deviceDescription.DmaChannel = configInfo.DmaChannel;
|
|
deviceDescription.InterfaceType = configInfo.AdapterInterfaceType;
|
|
deviceDescription.BusNumber = configInfo.SystemIoBusNumber;
|
|
deviceDescription.DmaWidth = configInfo.DmaWidth;
|
|
deviceDescription.DmaSpeed = configInfo.DmaSpeed;
|
|
deviceDescription.DmaPort = configInfo.DmaPort;
|
|
deviceDescription.MaximumLength = configInfo.MaximumTransferLength;
|
|
deviceDescription.ScatterGather = configInfo.ScatterGather;
|
|
deviceDescription.Master = configInfo.Master;
|
|
deviceDescription.AutoInitialize = FALSE;
|
|
deviceDescription.DemandMode = FALSE;
|
|
|
|
if (configInfo.MaximumTransferLength > 0x11000) {
|
|
|
|
deviceDescription.MaximumLength = 0x11000;
|
|
|
|
} else {
|
|
|
|
deviceDescription.MaximumLength = configInfo.MaximumTransferLength;
|
|
|
|
}
|
|
|
|
deviceExtension->DmaAdapterObject = HalGetAdapter(
|
|
&deviceDescription,
|
|
&numberOfPageBreaks
|
|
);
|
|
|
|
//
|
|
// Set maximum number of page breaks.
|
|
//
|
|
|
|
if (numberOfPageBreaks > configInfo.NumberOfPhysicalBreaks) {
|
|
capabilities->MaximumPhysicalPages = configInfo.NumberOfPhysicalBreaks;
|
|
} else {
|
|
capabilities->MaximumPhysicalPages = numberOfPageBreaks;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Allocate memory for the non cached extension if it has not already been
|
|
// allocated. If we can't get any abort the search for adapters but
|
|
// succeed if we've managed to initialize at least one.
|
|
//
|
|
|
|
if (deviceExtension->SrbExtensionSize != 0 &&
|
|
deviceExtension->SrbExtensionZonePool == NULL) {
|
|
|
|
status = SpGetCommonBuffer(deviceExtension, 0);
|
|
|
|
if (status != ESUCCESS) {
|
|
|
|
return (foundOne ? ESUCCESS : status);
|
|
}
|
|
}
|
|
|
|
capabilities->Length = sizeof(IO_SCSI_CAPABILITIES);
|
|
capabilities->MaximumTransferLength = configInfo.MaximumTransferLength;
|
|
DebugPrint((1,
|
|
"Maximum physical page breaks = %d. Maximum transfer length = %x\n",
|
|
capabilities->MaximumPhysicalPages,
|
|
capabilities->MaximumTransferLength));
|
|
|
|
if (HwInitializationData->ReceiveEvent) {
|
|
capabilities->SupportedAsynchronousEvents |=
|
|
SRBEV_SCSI_ASYNC_NOTIFICATION;
|
|
}
|
|
|
|
capabilities->TaggedQueuing = HwInitializationData->TaggedQueuing;
|
|
capabilities->AdapterScansDown = configInfo.AdapterScansDown;
|
|
capabilities->AlignmentMask = configInfo.AlignmentMask;
|
|
|
|
//
|
|
// Make sure maximum nuber of pages is set to a reasonable value.
|
|
// This occurs for mini-ports with no Dma adapter.
|
|
//
|
|
|
|
if (capabilities->MaximumPhysicalPages == 0) {
|
|
|
|
capabilities->MaximumPhysicalPages =
|
|
(ULONG)ROUND_TO_PAGES(capabilities->MaximumTransferLength) + 1;
|
|
|
|
//
|
|
// Honor any limit requested by the mini-port.
|
|
//
|
|
|
|
if (configInfo.NumberOfPhysicalBreaks < capabilities->MaximumPhysicalPages) {
|
|
|
|
capabilities->MaximumPhysicalPages =
|
|
configInfo.NumberOfPhysicalBreaks;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get maximum target IDs.
|
|
//
|
|
|
|
if (configInfo.MaximumNumberOfTargets > SCSI_MAXIMUM_TARGETS_PER_BUS) {
|
|
deviceExtension->MaximumTargetIds = SCSI_MAXIMUM_TARGETS_PER_BUS;
|
|
} else {
|
|
deviceExtension->MaximumTargetIds =
|
|
configInfo.MaximumNumberOfTargets;
|
|
}
|
|
|
|
if (deviceExtension->DmaAdapterObject != NULL &&
|
|
!HwInitializationData->NeedPhysicalAddresses) {
|
|
|
|
//
|
|
// Allocate the adapter object. For the port driver the adapter object
|
|
// and map registers are permentently allocated and used shared between
|
|
// all logical units. The adapter is allocated by initializing an event,
|
|
// calling IoAllocateAdapterChannel and waiting on the event. When the
|
|
// adapter and map registers are available, ScsiPortAllocationRoutine is
|
|
// called which set the event. In reality, all this takes zero time since
|
|
// the stuff is available immediately.
|
|
//
|
|
// Allocate the AdapterObject. The number of registers is equal to the
|
|
// maximum transfer length supported by the adapter + 1. This insures
|
|
// that there will always be a sufficient number of registers.
|
|
//
|
|
/* TODO: Fix this for the case when there is no maximum transfer length. */
|
|
|
|
IoAllocateAdapterChannel(
|
|
deviceExtension->DmaAdapterObject,
|
|
deviceObject,
|
|
capabilities->MaximumPhysicalPages,
|
|
ScsiPortAllocationRoutine,
|
|
&allocateAdapterEvent
|
|
);
|
|
|
|
//
|
|
// Wait for adapter object.
|
|
//
|
|
|
|
ASSERT(deviceExtension->MapRegisterBase);
|
|
|
|
deviceExtension->MasterWithAdapter = FALSE;
|
|
|
|
} else if (deviceExtension->DmaAdapterObject != NULL) {
|
|
|
|
//
|
|
// This SCSI adapter is a master with an adapter so a scatter/gather
|
|
// list needs to be allocated for each transfer.
|
|
//
|
|
|
|
deviceExtension->MasterWithAdapter = TRUE;
|
|
|
|
} else {
|
|
|
|
deviceExtension->MasterWithAdapter = FALSE;
|
|
|
|
} // end if (deviceExtension->DmaAdapterObject != NULL)
|
|
|
|
//
|
|
// Call the hardware dependent driver to do its initialization. If it fails
|
|
// then continue the search for adapters.
|
|
//
|
|
|
|
if (!KeSynchronizeExecution(
|
|
deviceExtension->InterruptObject,
|
|
deviceExtension->HwInitialize,
|
|
deviceExtension->HwDeviceExtension
|
|
)) {
|
|
|
|
DebugPrint((1,"ScsiPortInitialize: initialization failed\n"));
|
|
|
|
if(callAgain) {
|
|
goto CallAgain;
|
|
} else {
|
|
return foundOne ? ESUCCESS : ENODEV;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Find out if the miniport supports AdapterControl routines to shutdown.
|
|
//
|
|
|
|
SpGetSupportedAdapterControlFunctions(deviceExtension);
|
|
|
|
//
|
|
// Allocate properly aligned INQUIRY buffer. If we can't get one we're
|
|
// out of memory so searching for more adapters is pointless.
|
|
//
|
|
|
|
InquiryDataBuffer = ExAllocatePool(NonPagedPool, INQUIRYDATABUFFERSIZE);
|
|
|
|
if (InquiryDataBuffer == NULL) {
|
|
return foundOne ? ESUCCESS : ENOMEM;
|
|
}
|
|
|
|
//
|
|
// Reset the scsi bus.
|
|
//
|
|
|
|
if (!deviceExtension->HwReset(
|
|
deviceExtension->HwDeviceExtension,
|
|
0)){
|
|
|
|
DebugPrint((1,"Reset SCSI bus failed\n"));
|
|
}
|
|
|
|
//
|
|
// Call the interupt handler for a few microseconds to clear any reset
|
|
// interrupts.
|
|
//
|
|
|
|
for (j = 0; j < 1000 * 100; j++) {
|
|
|
|
FwStallExecution(10);
|
|
if (deviceExtension->HwInterrupt != NULL) {
|
|
deviceExtension->HwInterrupt(deviceExtension->HwDeviceExtension);
|
|
}
|
|
}
|
|
|
|
deviceExtension->PortTimeoutCounter = PD_TIMER_RESET_HOLD_TIME;
|
|
SpCheckResetDelay( deviceExtension );
|
|
|
|
//
|
|
// Find devices on each SCSI bus.
|
|
//
|
|
|
|
//
|
|
// Allocate buffer for SCSI bus scan information.
|
|
//
|
|
|
|
deviceExtension->ScsiInfo = ExAllocatePool(NonPagedPool,
|
|
deviceExtension->NumberOfBuses * sizeof(PSCSI_BUS_SCAN_DATA) +
|
|
4);
|
|
|
|
if (deviceExtension->ScsiInfo) {
|
|
|
|
deviceExtension->ScsiInfo->NumberOfBuses = deviceExtension->NumberOfBuses;
|
|
|
|
//
|
|
// Find devices on each SCSI bus.
|
|
//
|
|
|
|
for (scsiBus = 0; scsiBus < deviceExtension->NumberOfBuses; scsiBus++) {
|
|
deviceExtension->ScsiInfo->BusScanData[scsiBus] =
|
|
ScsiBusScan(deviceExtension,
|
|
scsiBus,
|
|
configInfo.InitiatorBusId[scsiBus]);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Save the device object for use by the driver.
|
|
//
|
|
|
|
ScsiPortDeviceObject[*scsiPortNumber] = deviceObject;
|
|
|
|
//
|
|
// Bump SCSI host bus adapters count.
|
|
//
|
|
|
|
(*scsiPortNumber)++;
|
|
|
|
foundOne = TRUE;
|
|
|
|
//
|
|
// If the adapter wants to be called again with the same configuration data
|
|
// then start over from the beginning again.
|
|
//
|
|
|
|
if (callAgain) {
|
|
goto CallAgain;
|
|
}
|
|
|
|
return ESUCCESS;
|
|
|
|
} // end ScsiPortInitialize()
|
|
|
|
IO_ALLOCATION_ACTION
|
|
ScsiPortAllocationRoutine (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID MapRegisterBase,
|
|
IN PVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is called by IoAllocateAdapterChannel when sufficent resources
|
|
are available to the driver. This routine saves the MapRegisterBase in the
|
|
device object and set the event pointed to by the context parameter.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to the device object to which the adapter is being
|
|
allocated.
|
|
|
|
Irp - Unused.
|
|
|
|
MapRegisterBase - Supplied by the Io subsystem for use in IoMapTransfer.
|
|
|
|
Context - Supplies a pointer to an event which is set to indicate the
|
|
AdapterObject has been allocated.
|
|
|
|
Return Value:
|
|
|
|
KeepObject - Indicates the adapter and mapregisters should remain allocated
|
|
after return.
|
|
|
|
--*/
|
|
|
|
{
|
|
((PDEVICE_EXTENSION) DeviceObject->DeviceExtension)->MapRegisterBase =
|
|
MapRegisterBase;
|
|
|
|
UNREFERENCED_PARAMETER(Irp);
|
|
UNREFERENCED_PARAMETER(Context);
|
|
|
|
return(KeepObject);
|
|
}
|
|
|
|
IO_ALLOCATION_ACTION
|
|
SpBuildScatterGather(
|
|
IN struct _DEVICE_OBJECT *DeviceObject,
|
|
IN struct _IRP *Irp,
|
|
IN PVOID MapRegisterBase,
|
|
IN PVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is called by the I/O system when an adapter object and map
|
|
registers have been allocated. This routine then builds a scatter/gather
|
|
list for use by the mini-port driver. Next it sets the timeout and
|
|
the current Irp for the logical unit. Finally it calls the mini-port
|
|
StartIo routine. Once that routines complete, this routine will return
|
|
requesting that the adapter be freed and but the registers remain allocated.
|
|
The registers will be freed the request completes.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Supplies a pointer to the port driver device object.
|
|
|
|
Irp - Supplies a pointer to the current Irp.
|
|
|
|
MapRegisterBase - Supplies a context pointer to be used with calls the
|
|
adapter object routines.
|
|
|
|
Context - Supplies a pointer to the logical unit structure.
|
|
|
|
Return Value:
|
|
|
|
Returns DeallocateObjectKeepRegisters so that the adapter object can be
|
|
used by other logical units.
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOLEAN writeToDevice;
|
|
PIO_STACK_LOCATION irpstack = IoGetCurrentIrpStackLocation(Irp);
|
|
PLOGICAL_UNIT_EXTENSION logicalUnit;
|
|
PSCSI_REQUEST_BLOCK srb;
|
|
PSRB_SCATTER_GATHER scatterList;
|
|
ULONG totalLength;
|
|
|
|
logicalUnit = Context;
|
|
srb = (PSCSI_REQUEST_BLOCK)irpstack->Parameters.Others.Argument1;
|
|
scatterList = logicalUnit->ScatterGather;
|
|
totalLength = 0;
|
|
|
|
//
|
|
// Save the MapRegisterBase for later use to deallocate the map registers.
|
|
//
|
|
|
|
logicalUnit->MapRegisterBase = MapRegisterBase;
|
|
|
|
//
|
|
// Build the scatter/gather list by looping throught the transfer calling
|
|
// I/O map transfer.
|
|
//
|
|
|
|
writeToDevice = srb->SrbFlags & SRB_FLAGS_DATA_OUT ? TRUE : FALSE;
|
|
|
|
while (totalLength < srb->DataTransferLength) {
|
|
|
|
//
|
|
// Request that the rest of the transfer be mapped.
|
|
//
|
|
|
|
scatterList->Length = srb->DataTransferLength - totalLength;
|
|
|
|
//
|
|
// Since we are a master call I/O map transfer with a NULL adapter.
|
|
//
|
|
|
|
scatterList->PhysicalAddress = IoMapTransfer(
|
|
NULL,
|
|
Irp->MdlAddress,
|
|
MapRegisterBase,
|
|
(PCCHAR) srb->DataBuffer + totalLength,
|
|
&scatterList->Length,
|
|
writeToDevice
|
|
).LowPart;
|
|
|
|
totalLength += scatterList->Length;
|
|
scatterList++;
|
|
}
|
|
|
|
//
|
|
// Set request timeout value from Srb SCSI extension in Irp.
|
|
//
|
|
|
|
logicalUnit->RequestTimeoutCounter = srb->TimeOutValue;
|
|
|
|
//
|
|
// Set current request for this logical unit.
|
|
//
|
|
|
|
logicalUnit->CurrentRequest = Irp;
|
|
|
|
/* TODO: Check the return value. */
|
|
KeSynchronizeExecution(
|
|
((PDEVICE_EXTENSION) DeviceObject->DeviceExtension)->InterruptObject,
|
|
SpStartIoSynchronized,
|
|
DeviceObject
|
|
);
|
|
|
|
return(DeallocateObjectKeepRegisters);
|
|
|
|
}
|
|
|
|
VOID
|
|
ScsiPortExecute(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine calls the start I/O routine an waits for the request to
|
|
complete. During the wait for complete the interrupt routine is called,
|
|
also the timer routines are called at the appropriate times. After the
|
|
request completes a check is made to determine if an request sense needs
|
|
to be issued.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Supplies pointer to Adapter device object.
|
|
|
|
Irp - Supplies a pointer to an IRP.
|
|
|
|
Return Value:
|
|
|
|
Nothing.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG milliSecondTime;
|
|
ULONG secondTime;
|
|
ULONG completionDelay;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
PIO_STACK_LOCATION irpstack = IoGetCurrentIrpStackLocation(Irp);
|
|
PSCSI_REQUEST_BLOCK srb = (PSCSI_REQUEST_BLOCK)irpstack->Parameters.Others.Argument1;
|
|
PVOID logicalUnit;
|
|
|
|
deviceExtension = DeviceObject->DeviceExtension;
|
|
logicalUnit = GetLogicalUnitExtension(deviceExtension, srb->TargetId);
|
|
|
|
if (logicalUnit == NULL) {
|
|
Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
srb->SrbStatus = SRB_STATUS_NO_DEVICE;
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Make sure the adapter is ready to accept requests.
|
|
//
|
|
|
|
SpCheckResetDelay( deviceExtension );
|
|
|
|
//
|
|
// Mark IRP as pending.
|
|
//
|
|
|
|
Irp->PendingReturned = TRUE;
|
|
|
|
//
|
|
// Start the request.
|
|
//
|
|
|
|
ScsiPortStartIo( DeviceObject, Irp);
|
|
|
|
//
|
|
// The completion delay controls how long interrupts are serviced after
|
|
// a request has been completed. This allows interrupts which occur after
|
|
// a completion to be serviced.
|
|
//
|
|
|
|
completionDelay = COMPLETION_DELAY;
|
|
|
|
//
|
|
// Wait for the IRP to complete.
|
|
//
|
|
|
|
while (Irp->PendingReturned && completionDelay) {
|
|
|
|
//
|
|
// Wait 1 second then call the scsi port timer routine.
|
|
//
|
|
|
|
for (secondTime = 0; secondTime < 1000/ 250; secondTime++) {
|
|
|
|
for (milliSecondTime = 0; milliSecondTime < (250 * 1000 / PD_INTERLOOP_STALL); milliSecondTime++) {
|
|
|
|
ScsiPortInterrupt(NULL, DeviceObject);
|
|
|
|
if (!Irp->PendingReturned) {
|
|
if (completionDelay-- == 0) {
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
if (deviceExtension->Flags & PD_ENABLE_CALL_REQUEST) {
|
|
|
|
//
|
|
// Call the mini-port requested routine.
|
|
//
|
|
|
|
deviceExtension->Flags &= ~PD_ENABLE_CALL_REQUEST;
|
|
deviceExtension->HwRequestInterrupt(deviceExtension->HwDeviceExtension);
|
|
|
|
if (deviceExtension->Flags & PD_DISABLE_CALL_REQUEST) {
|
|
|
|
deviceExtension->Flags &= ~(PD_DISABLE_INTERRUPTS | PD_DISABLE_CALL_REQUEST);
|
|
deviceExtension->HwRequestInterrupt(deviceExtension->HwDeviceExtension);
|
|
|
|
}
|
|
}
|
|
|
|
|
|
if (deviceExtension->Flags & PD_CALL_DMA_STARTED) {
|
|
|
|
deviceExtension->Flags &= ~PD_CALL_DMA_STARTED;
|
|
|
|
//
|
|
// Notify the mini-port driver that the DMA has been
|
|
// started.
|
|
//
|
|
|
|
if (deviceExtension->HwDmaStarted) {
|
|
KeSynchronizeExecution(
|
|
&deviceExtension->InterruptObject,
|
|
(PKSYNCHRONIZE_ROUTINE) deviceExtension->HwDmaStarted,
|
|
deviceExtension->HwDeviceExtension
|
|
);
|
|
}
|
|
|
|
}
|
|
|
|
FwStallExecution(PD_INTERLOOP_STALL);
|
|
|
|
//
|
|
// Check the miniport timer.
|
|
//
|
|
|
|
if (deviceExtension->TimerValue != 0) {
|
|
|
|
deviceExtension->TimerValue--;
|
|
|
|
if (deviceExtension->TimerValue == 0) {
|
|
|
|
//
|
|
// The timer timed out so called requested timer routine.
|
|
//
|
|
|
|
deviceExtension->HwTimerRequest(deviceExtension->HwDeviceExtension);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ScsiPortTickHandler(DeviceObject, NULL);
|
|
}
|
|
|
|
done:
|
|
|
|
if (!NT_SUCCESS(Irp->IoStatus.Status)) {
|
|
PIO_STACK_LOCATION irpstack = IoGetCurrentIrpStackLocation(Irp);
|
|
PSCSI_REQUEST_BLOCK Srb = (PSCSI_REQUEST_BLOCK)irpstack->Parameters.Others.Argument1;
|
|
|
|
//
|
|
// Determine if a REQUEST SENSE command needs to be done.
|
|
// Check that a CHECK_CONDITION was received, an autosense has not
|
|
// been done already, and that autosense has been requested.
|
|
//
|
|
|
|
if (srb->ScsiStatus == SCSISTAT_CHECK_CONDITION &&
|
|
!(srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID) &&
|
|
srb->SenseInfoBuffer) {
|
|
|
|
//
|
|
// Call IssueRequestSense and it will complete the request after
|
|
// the REQUEST SENSE completes.
|
|
//
|
|
|
|
IssueRequestSense(deviceExtension, Srb);
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID
|
|
ScsiPortStartIo (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Supplies pointer to Adapter device object.
|
|
Irp - Supplies a pointer to an IRP.
|
|
|
|
Return Value:
|
|
|
|
Nothing.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION irpstack = IoGetCurrentIrpStackLocation(Irp);
|
|
PSCSI_REQUEST_BLOCK Srb = (PSCSI_REQUEST_BLOCK)irpstack->Parameters.Others.Argument1;
|
|
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
PLOGICAL_UNIT_EXTENSION logicalUnit;
|
|
PFULL_SCSI_REQUEST_BLOCK FullSrb;
|
|
NTSTATUS status;
|
|
|
|
DebugPrint((3,"ScsiPortStartIo: Enter routine\n"));
|
|
|
|
FullSrb = CONTAINING_RECORD(Srb, FULL_SCSI_REQUEST_BLOCK, Srb);
|
|
|
|
if (deviceExtension->SrbExtensionZonePool &&
|
|
(Srb->SrbExtension == NULL ||
|
|
(deviceExtension->SrbExtensionSize > FullSrb->SrbExtensionSize))) {
|
|
|
|
//
|
|
// Allocate SRB extension from zone.
|
|
//
|
|
|
|
Srb->SrbExtension = deviceExtension->SrbExtensionPointer;
|
|
|
|
(PCCHAR) deviceExtension->SrbExtensionPointer +=
|
|
deviceExtension->SrbExtensionSize;
|
|
|
|
FullSrb->SrbExtensionSize = deviceExtension->SrbExtensionSize;
|
|
|
|
if ((ULONG_PTR) deviceExtension->SrbExtensionPointer >
|
|
(ULONG_PTR) deviceExtension->NonCachedExtension) {
|
|
DebugPrint((0, "NtLdr: ScsiPortStartIo: Srb extension overflow. Too many srb extension allocated.\n"));
|
|
DbgBreakPoint();
|
|
}
|
|
|
|
DebugPrint((3,"ExInterlockedAllocateFromZone: %lx\n",
|
|
Srb->SrbExtension));
|
|
|
|
DebugPrint((3,"Srb %lx\n",Srb));
|
|
}
|
|
|
|
//
|
|
// Get logical unit extension.
|
|
//
|
|
|
|
logicalUnit = GetLogicalUnitExtension(deviceExtension, Srb->TargetId);
|
|
|
|
//
|
|
// Flush the data buffer if necessary.
|
|
//
|
|
|
|
if (Srb->SrbFlags & (SRB_FLAGS_DATA_IN | SRB_FLAGS_DATA_OUT)) {
|
|
|
|
if (Srb->DataTransferLength > deviceExtension->Capabilities.MaximumTransferLength) {
|
|
|
|
DebugPrint((1, "Scsiboot: ScsiPortStartIo Length Exceeds limit %x, %x\n",
|
|
Srb->DataTransferLength,
|
|
deviceExtension->Capabilities.MaximumTransferLength));
|
|
}
|
|
|
|
KeFlushIoBuffers(
|
|
Irp->MdlAddress,
|
|
(BOOLEAN) (Srb->SrbFlags & SRB_FLAGS_DATA_IN ? TRUE : FALSE),
|
|
TRUE
|
|
);
|
|
|
|
//
|
|
// Determine if this adapter needs map registers
|
|
//
|
|
|
|
if (deviceExtension->MasterWithAdapter) {
|
|
|
|
//
|
|
// Calculate the number of map registers needed for this transfer.
|
|
//
|
|
|
|
logicalUnit->NumberOfMapRegisters = ADDRESS_AND_SIZE_TO_SPAN_PAGES(
|
|
Srb->DataBuffer,
|
|
Srb->DataTransferLength
|
|
);
|
|
|
|
//
|
|
// Allocate the adapter channel with sufficient map registers
|
|
// for the transfer.
|
|
//
|
|
|
|
status = IoAllocateAdapterChannel(
|
|
deviceExtension->DmaAdapterObject, // AdapterObject
|
|
DeviceObject, // DeviceObject.
|
|
logicalUnit->NumberOfMapRegisters, // NumberOfMapRegisters
|
|
SpBuildScatterGather, // ExecutionRoutine
|
|
logicalUnit // Context
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
;
|
|
}
|
|
|
|
//
|
|
// The execution routine called by IoAllocateChannel will do the
|
|
// rest of the work so just return.
|
|
//
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set request timeout value from Srb SCSI extension in Irp.
|
|
//
|
|
|
|
logicalUnit->RequestTimeoutCounter = Srb->TimeOutValue;
|
|
|
|
//
|
|
// Set current request for this logical unit.
|
|
//
|
|
|
|
logicalUnit->CurrentRequest = Irp;
|
|
|
|
/* TODO: Check the return value. */
|
|
KeSynchronizeExecution(
|
|
deviceExtension->InterruptObject,
|
|
SpStartIoSynchronized,
|
|
DeviceObject
|
|
);
|
|
|
|
return;
|
|
|
|
} // end ScsiPortStartIO()
|
|
|
|
|
|
BOOLEAN
|
|
SpStartIoSynchronized (
|
|
PVOID ServiceContext
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine calls the dependent driver start io routine.
|
|
|
|
Arguments:
|
|
|
|
ServiceContext - Supplies the pointer to the device object.
|
|
|
|
Return Value:
|
|
|
|
Returns the value returned by the dependent start I/O routine.
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_OBJECT DeviceObject = ServiceContext;
|
|
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
PIO_STACK_LOCATION irpstack;
|
|
PSCSI_REQUEST_BLOCK Srb;
|
|
|
|
DebugPrint((3, "ScsiPortStartIoSynchronized: Enter routine\n"));
|
|
|
|
irpstack = IoGetCurrentIrpStackLocation(DeviceObject->CurrentIrp);
|
|
Srb = (PSCSI_REQUEST_BLOCK)irpstack->Parameters.Others.Argument1;
|
|
|
|
DebugPrint((3, "SpPortStartIoSynchronized: SRB %lx\n",
|
|
Srb));
|
|
|
|
DebugPrint((3, "SpPortStartIoSynchronized: IRP %lx\n",
|
|
DeviceObject->CurrentIrp));
|
|
|
|
//
|
|
// Disable all synchronous transfers.
|
|
//
|
|
|
|
Srb->SrbFlags |=
|
|
(SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_DISABLE_DISCONNECT | SRB_FLAGS_DISABLE_AUTOSENSE);
|
|
|
|
return deviceExtension->HwStartIo(
|
|
deviceExtension->HwDeviceExtension,
|
|
Srb
|
|
);
|
|
|
|
} // end SpStartIoSynchronized()
|
|
|
|
|
|
BOOLEAN
|
|
ScsiPortInterrupt(
|
|
IN PKINTERRUPT Interrupt,
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
Interrupt
|
|
|
|
Device Object
|
|
|
|
Return Value:
|
|
|
|
Returns TRUE if interrupt expected.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
|
|
UNREFERENCED_PARAMETER(Interrupt);
|
|
|
|
if (deviceExtension->Flags & PD_DISABLE_INTERRUPTS) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (deviceExtension->HwInterrupt != NULL) {
|
|
|
|
if (deviceExtension->HwInterrupt(deviceExtension->HwDeviceExtension)) {
|
|
|
|
return TRUE;
|
|
|
|
} else {
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return(FALSE);
|
|
|
|
} // end ScsiPortInterrupt()
|
|
|
|
|
|
VOID
|
|
ScsiPortCompletionDpc(
|
|
IN PKDPC Dpc,
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Dpc
|
|
DeviceObject
|
|
Irp - not used
|
|
Context - not used
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
PIO_STACK_LOCATION irpstack;
|
|
PSCSI_REQUEST_BLOCK Srb;
|
|
PLOGICAL_UNIT_EXTENSION luExtension;
|
|
|
|
UNREFERENCED_PARAMETER(Dpc);
|
|
UNREFERENCED_PARAMETER(Context);
|
|
|
|
DebugPrint((3, "ScsiPortCompletionDpc Entered\n"));
|
|
|
|
//
|
|
// Acquire the spinlock to protect the flags structure and the saved
|
|
// interrupt context.
|
|
//
|
|
|
|
KeAcquireSpinLock(&deviceExtension->SpinLock, ¤tIrql);
|
|
|
|
//
|
|
// Check for a flush DMA adapter object request.
|
|
//
|
|
|
|
if (deviceExtension->InterruptFlags & PD_FLUSH_ADAPTER_BUFFERS) {
|
|
|
|
//
|
|
// Call IoFlushAdapterBuffers using the parameters saved from the last
|
|
// IoMapTransfer call.
|
|
//
|
|
|
|
IoFlushAdapterBuffers(
|
|
deviceExtension->DmaAdapterObject,
|
|
((PIRP)deviceExtension->FlushAdapterParameters.Srb->OriginalRequest)
|
|
->MdlAddress,
|
|
deviceExtension->MapRegisterBase,
|
|
deviceExtension->FlushAdapterParameters.LogicalAddress,
|
|
deviceExtension->FlushAdapterParameters.Length,
|
|
(BOOLEAN)(deviceExtension->FlushAdapterParameters.Srb->SrbFlags
|
|
& SRB_FLAGS_DATA_OUT ? TRUE : FALSE)
|
|
);
|
|
|
|
deviceExtension->InterruptFlags &= ~PD_FLUSH_ADAPTER_BUFFERS;
|
|
}
|
|
|
|
//
|
|
// Check for an IoMapTransfer DMA request.
|
|
//
|
|
|
|
if (deviceExtension->InterruptFlags & PD_MAP_TRANSFER) {
|
|
|
|
//
|
|
// Call IoMapTransfer using the parameters saved from the
|
|
// interrupt level.
|
|
//
|
|
|
|
IoMapTransfer(
|
|
deviceExtension->DmaAdapterObject,
|
|
((PIRP)deviceExtension->MapTransferParameters.Srb->OriginalRequest)
|
|
->MdlAddress,
|
|
deviceExtension->MapRegisterBase,
|
|
deviceExtension->MapTransferParameters.LogicalAddress,
|
|
&deviceExtension->MapTransferParameters.Length,
|
|
(BOOLEAN)(deviceExtension->MapTransferParameters.Srb->SrbFlags
|
|
& SRB_FLAGS_DATA_OUT ? TRUE : FALSE)
|
|
);
|
|
|
|
//
|
|
// Save the paramters for IoFlushAdapterBuffers.
|
|
//
|
|
|
|
deviceExtension->FlushAdapterParameters =
|
|
deviceExtension->MapTransferParameters;
|
|
|
|
deviceExtension->InterruptFlags &= ~PD_MAP_TRANSFER;
|
|
deviceExtension->Flags |= PD_CALL_DMA_STARTED;
|
|
|
|
}
|
|
|
|
//
|
|
// Process any completed requests.
|
|
//
|
|
|
|
while (deviceExtension->CompletedRequests != NULL) {
|
|
|
|
Irp = deviceExtension->CompletedRequests;
|
|
irpstack = IoGetCurrentIrpStackLocation(Irp);
|
|
Srb = (PSCSI_REQUEST_BLOCK)irpstack->Parameters.Others.Argument1;
|
|
luExtension =
|
|
GetLogicalUnitExtension(deviceExtension, Srb->TargetId);
|
|
|
|
DebugPrint((3, "ScsiPortCompletionDpc: SRB %lx\n", Srb));
|
|
DebugPrint((3, "ScsiPortCompletionDpc: IRP %lx\n", Irp));
|
|
|
|
//
|
|
// Remove the request from the linked-list.
|
|
//
|
|
|
|
deviceExtension->CompletedRequests =
|
|
irpstack->Parameters.Others.Argument3;
|
|
|
|
//
|
|
// Check for very unlikely return of NULL.
|
|
//
|
|
if (luExtension == NULL) {
|
|
|
|
ASSERT(luExtension != NULL); // Debug why this happened. It should not.
|
|
|
|
//
|
|
// But in retail, if some reason it did, complete the IRP and continue.
|
|
//
|
|
Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
|
|
Irp->IoStatus.Information = Srb->DataTransferLength;
|
|
|
|
//
|
|
// Free SrbExtension if allocated.
|
|
//
|
|
if (Srb->SrbExtension == (deviceExtension->SrbExtensionPointer -
|
|
deviceExtension->SrbExtensionSize) ) {
|
|
|
|
Srb->SrbExtension = NULL;
|
|
|
|
(PCCHAR) deviceExtension->SrbExtensionPointer -=
|
|
deviceExtension->SrbExtensionSize;
|
|
}
|
|
|
|
IoCompleteRequest(Irp, 2);
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Reset request timeout counter.
|
|
//
|
|
|
|
luExtension->RequestTimeoutCounter = -1;
|
|
|
|
//
|
|
// Flush the adapter buffers if necessary.
|
|
//
|
|
|
|
if (luExtension->MapRegisterBase) {
|
|
|
|
//
|
|
// Since we are a master call I/O flush adapter buffers with a NULL
|
|
// adapter.
|
|
//
|
|
|
|
IoFlushAdapterBuffers(
|
|
NULL,
|
|
Irp->MdlAddress,
|
|
luExtension->MapRegisterBase,
|
|
Srb->DataBuffer,
|
|
Srb->DataTransferLength,
|
|
(BOOLEAN) (Srb->SrbFlags & SRB_FLAGS_DATA_OUT ? TRUE : FALSE)
|
|
);
|
|
|
|
//
|
|
// Free the map registers.
|
|
//
|
|
|
|
IoFreeMapRegisters(
|
|
deviceExtension->DmaAdapterObject,
|
|
luExtension->MapRegisterBase,
|
|
luExtension->NumberOfMapRegisters
|
|
);
|
|
|
|
//
|
|
// Clear the MapRegisterBase.
|
|
//
|
|
|
|
luExtension->MapRegisterBase = NULL;
|
|
|
|
}
|
|
|
|
//
|
|
// Set IRP status. Class drivers will reset IRP status based
|
|
// on request sense if error.
|
|
//
|
|
|
|
if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_SUCCESS) {
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
} else {
|
|
Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
//
|
|
// Move bytes transfered to IRP.
|
|
//
|
|
|
|
Irp->IoStatus.Information = Srb->DataTransferLength;
|
|
|
|
//
|
|
// If success then start next packet.
|
|
// Not starting packet effectively
|
|
// freezes the queue.
|
|
//
|
|
|
|
if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_SUCCESS) {
|
|
|
|
DebugPrint((
|
|
2,
|
|
"ScsiPortCompletionDpc: Iocompletion IRP %lx\n",
|
|
Irp));
|
|
|
|
//
|
|
// Free SrbExtension if allocated.
|
|
//
|
|
|
|
if (Srb->SrbExtension == (deviceExtension->SrbExtensionPointer -
|
|
deviceExtension->SrbExtensionSize) ) {
|
|
|
|
Srb->SrbExtension = NULL;
|
|
|
|
(PCCHAR) deviceExtension->SrbExtensionPointer -=
|
|
deviceExtension->SrbExtensionSize;
|
|
}
|
|
|
|
IoCompleteRequest(Irp, 2);
|
|
|
|
} else {
|
|
|
|
if ( Srb->ScsiStatus == SCSISTAT_BUSY &&
|
|
(luExtension->RetryCount++ < 20)) {
|
|
//
|
|
// If busy status is returned, then indicate that the logical
|
|
// unit is busy. The timeout code will restart the request
|
|
// when it fires. Reset the status to pending.
|
|
//
|
|
Srb->SrbStatus = SRB_STATUS_PENDING;
|
|
luExtension->CurrentRequest = Irp;
|
|
luExtension->Flags |= PD_LOGICAL_UNIT_IS_BUSY;
|
|
|
|
//
|
|
// Restore the data transfer length.
|
|
//
|
|
|
|
if (Irp->MdlAddress != NULL) {
|
|
Srb->DataTransferLength = Irp->MdlAddress->ByteCount;
|
|
}
|
|
|
|
DebugPrint((1, "ScsiPortCompletionDpc: Busy returned. Length = %lx\n", Srb->DataTransferLength));
|
|
|
|
} else {
|
|
|
|
|
|
DebugPrint((
|
|
3,
|
|
"ScsiPortCompletionDpc: Iocompletion IRP %lx\n",
|
|
Irp));
|
|
|
|
//
|
|
// Free SrbExtension if allocated.
|
|
//
|
|
|
|
if (Srb->SrbExtension == (deviceExtension->SrbExtensionPointer -
|
|
deviceExtension->SrbExtensionSize) ) {
|
|
|
|
Srb->SrbExtension = NULL;
|
|
|
|
(PCCHAR) deviceExtension->SrbExtensionPointer -=
|
|
deviceExtension->SrbExtensionSize;
|
|
}
|
|
|
|
IoCompleteRequest(Irp, 2);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Release the spinlock.
|
|
//
|
|
|
|
KeReleaseSpinLock(&deviceExtension->SpinLock, currentIrql);
|
|
|
|
return;
|
|
|
|
} // end ScsiPortCompletionDpc()
|
|
|
|
|
|
ARC_STATUS
|
|
IssueInquiry(
|
|
IN PDEVICE_EXTENSION DeviceExtension,
|
|
IN PLUNINFO LunInfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Build IRP, SRB and CDB for SCSI INQUIRY command.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - address of adapter's device object extension.
|
|
LunInfo - address of buffer for INQUIRY information.
|
|
|
|
Return Value:
|
|
|
|
ARC_STATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
PIRP irp;
|
|
PIO_STACK_LOCATION irpstack;
|
|
PCDB cdb;
|
|
PSCSI_REQUEST_BLOCK srb;
|
|
ARC_STATUS status;
|
|
ULONG retryCount = 0;
|
|
|
|
DebugPrint((3,"IssueInquiry: Enter routine\n"));
|
|
|
|
if (InquiryDataBuffer == NULL) {
|
|
return ENOMEM;
|
|
}
|
|
|
|
inquiryRetry:
|
|
|
|
//
|
|
// Build IRP for this request.
|
|
//
|
|
|
|
irp = InitializeIrp(
|
|
&PrimarySrb,
|
|
IRP_MJ_SCSI,
|
|
DeviceExtension->DeviceObject,
|
|
(PVOID)InquiryDataBuffer,
|
|
INQUIRYDATABUFFERSIZE
|
|
);
|
|
|
|
irpstack = IoGetNextIrpStackLocation(irp);
|
|
|
|
//
|
|
// Set major and minor codes.
|
|
//
|
|
|
|
irpstack->MajorFunction = IRP_MJ_SCSI;
|
|
|
|
//
|
|
// Fill in SRB fields.
|
|
//
|
|
|
|
irpstack->Parameters.Others.Argument1 = &PrimarySrb;
|
|
srb = &PrimarySrb.Srb;
|
|
|
|
srb->Length = sizeof(SCSI_REQUEST_BLOCK);
|
|
srb->PathId = LunInfo->PathId;
|
|
srb->TargetId = LunInfo->TargetId;
|
|
srb->Lun = LunInfo->Lun;
|
|
|
|
srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
|
|
|
|
srb->SrbFlags = SRB_FLAGS_DATA_IN | SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_DISABLE_DISCONNECT;
|
|
|
|
srb->SrbStatus = srb->ScsiStatus = 0;
|
|
|
|
srb->OriginalRequest = irp;
|
|
|
|
srb->NextSrb = 0;
|
|
|
|
//
|
|
// Set timeout to 5 seconds.
|
|
//
|
|
|
|
srb->TimeOutValue = 5;
|
|
|
|
srb->CdbLength = 6;
|
|
|
|
srb->SenseInfoBufferLength = 0;
|
|
srb->SenseInfoBuffer = 0;
|
|
|
|
srb->DataBuffer = MmGetMdlVirtualAddress(irp->MdlAddress);
|
|
srb->DataTransferLength = INQUIRYDATABUFFERSIZE;
|
|
|
|
cdb = (PCDB)srb->Cdb;
|
|
|
|
//
|
|
// Set CDB operation code.
|
|
//
|
|
|
|
cdb->CDB6INQUIRY.OperationCode = SCSIOP_INQUIRY;
|
|
|
|
//
|
|
// Set CDB LUN.
|
|
//
|
|
|
|
cdb->CDB6INQUIRY.LogicalUnitNumber = LunInfo->Lun;
|
|
cdb->CDB6INQUIRY.Reserved1 = 0;
|
|
|
|
//
|
|
// Set allocation length to inquiry data buffer size.
|
|
//
|
|
|
|
cdb->CDB6INQUIRY.AllocationLength = INQUIRYDATABUFFERSIZE;
|
|
|
|
//
|
|
// Zero reserve field and
|
|
// Set EVPD Page Code to zero.
|
|
// Set Control field to zero.
|
|
// (See SCSI-II Specification.)
|
|
//
|
|
|
|
cdb->CDB6INQUIRY.PageCode = 0;
|
|
cdb->CDB6INQUIRY.IReserved = 0;
|
|
cdb->CDB6INQUIRY.Control = 0;
|
|
|
|
//
|
|
// Call port driver to handle this request.
|
|
//
|
|
|
|
(VOID)IoCallDriver(DeviceExtension->DeviceObject, irp);
|
|
|
|
|
|
if (SRB_STATUS(srb->SrbStatus) != SRB_STATUS_SUCCESS) {
|
|
|
|
DebugPrint((2,"IssueInquiry: Inquiry failed SRB status %x\n",
|
|
srb->SrbStatus));
|
|
|
|
//
|
|
// NOTE: if INQUIRY fails with a data underrun,
|
|
// indicate success and let the class drivers
|
|
// determine whether the inquiry information
|
|
// is useful.
|
|
//
|
|
|
|
if (SRB_STATUS(srb->SrbStatus) == SRB_STATUS_DATA_OVERRUN) {
|
|
|
|
//
|
|
// Copy INQUIRY buffer to LUNINFO.
|
|
//
|
|
|
|
DebugPrint((1,"IssueInquiry: Data underrun at TID %d\n",
|
|
LunInfo->TargetId));
|
|
|
|
RtlMoveMemory(LunInfo->InquiryData,
|
|
InquiryDataBuffer,
|
|
INQUIRYDATABUFFERSIZE);
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
} else if ((SRB_STATUS(srb->SrbStatus) != SRB_STATUS_SELECTION_TIMEOUT) && (retryCount++ < 2)) {
|
|
|
|
//
|
|
// If the selection did not time out then retry the request.
|
|
//
|
|
|
|
DebugPrint((2,"IssueInquiry: Retry %d\n", retryCount));
|
|
goto inquiryRetry;
|
|
|
|
} else {
|
|
|
|
status = EIO;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Copy INQUIRY buffer to LUNINFO.
|
|
//
|
|
|
|
RtlMoveMemory(LunInfo->InquiryData,
|
|
InquiryDataBuffer,
|
|
INQUIRYDATABUFFERSIZE);
|
|
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
return status;
|
|
|
|
} // end IssueInquiry()
|
|
|
|
|
|
PSCSI_BUS_SCAN_DATA
|
|
ScsiBusScan(
|
|
IN PDEVICE_EXTENSION DeviceExtension,
|
|
IN UCHAR ScsiBus,
|
|
IN UCHAR InitiatorBusId
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension
|
|
ScsiBus
|
|
|
|
Return Value:
|
|
|
|
SCSI configuration information
|
|
|
|
|
|
--*/
|
|
{
|
|
PSCSI_BUS_SCAN_DATA busScanData;
|
|
PLUNINFO lunInfo;
|
|
UCHAR target;
|
|
UCHAR device = 0;
|
|
PLOGICAL_UNIT_EXTENSION nextLogicalUnitExtension;
|
|
|
|
DebugPrint((3,"ScsiBusScan: Enter routine\n"));
|
|
|
|
busScanData = ExAllocatePool(NonPagedPool,
|
|
sizeof(SCSI_BUS_SCAN_DATA));
|
|
|
|
if (busScanData == NULL) {
|
|
|
|
//
|
|
// Insufficient system resources to complete bus scan.
|
|
//
|
|
|
|
return NULL;
|
|
}
|
|
|
|
RtlZeroMemory(busScanData,sizeof(SCSI_BUS_SCAN_DATA));
|
|
|
|
busScanData->Length = sizeof(SCSI_CONFIGURATION_INFO);
|
|
|
|
//
|
|
// Create first LUNINFO.
|
|
//
|
|
|
|
lunInfo = ExAllocatePool(NonPagedPool, sizeof(LUNINFO));
|
|
|
|
if (lunInfo == NULL) {
|
|
|
|
//
|
|
// Insufficient system resources to complete bus scan.
|
|
//
|
|
|
|
return NULL;
|
|
}
|
|
|
|
RtlZeroMemory(lunInfo, sizeof(LUNINFO));
|
|
|
|
//
|
|
// Create first logical unit extension.
|
|
//
|
|
|
|
nextLogicalUnitExtension = CreateLogicalUnitExtension(DeviceExtension);
|
|
|
|
if (nextLogicalUnitExtension == NULL) {
|
|
return(NULL);
|
|
}
|
|
|
|
//
|
|
// Link logical unit extension on list.
|
|
//
|
|
|
|
nextLogicalUnitExtension->NextLogicalUnit = DeviceExtension->LogicalUnitList;
|
|
|
|
DeviceExtension->LogicalUnitList = nextLogicalUnitExtension;
|
|
|
|
//
|
|
// Issue inquiry command to each target id to find devices.
|
|
//
|
|
// NOTE: Does not handle multiple logical units per target id.
|
|
//
|
|
|
|
for (target = DeviceExtension->MaximumTargetIds; target > 0; target--) {
|
|
|
|
if (InitiatorBusId == target-1) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Record address.
|
|
//
|
|
|
|
nextLogicalUnitExtension->PathId = lunInfo->PathId = ScsiBus;
|
|
|
|
nextLogicalUnitExtension->TargetId = lunInfo->TargetId = target-1;
|
|
|
|
nextLogicalUnitExtension->Lun = lunInfo->Lun = 0;
|
|
|
|
//
|
|
// Rezero hardware logigal unit extension if it's being recycled.
|
|
//
|
|
|
|
if (DeviceExtension->HwLogicalUnitExtensionSize) {
|
|
|
|
if (nextLogicalUnitExtension->SpecificLuExtension) {
|
|
|
|
RtlZeroMemory(nextLogicalUnitExtension->SpecificLuExtension,
|
|
DeviceExtension->HwLogicalUnitExtensionSize);
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Issue inquiry command.
|
|
//
|
|
|
|
DebugPrint((2,"ScsiBusScan: Try TargetId %d LUN 0\n", target-1));
|
|
|
|
if (IssueInquiry(DeviceExtension, lunInfo) == ESUCCESS) {
|
|
|
|
PINQUIRYDATA inquiryData = (PINQUIRYDATA)lunInfo->InquiryData;
|
|
|
|
//
|
|
// Make sure we can use the device.
|
|
//
|
|
|
|
if (inquiryData->DeviceTypeQualifier & 0x04) {
|
|
|
|
//
|
|
// This device is not supported; continue looking for
|
|
// other devices.
|
|
//
|
|
|
|
continue;
|
|
}
|
|
|
|
DebugPrint((1,
|
|
"ScsiBusScan: Found Device %d at TID %d LUN %d\n",
|
|
device,
|
|
lunInfo->TargetId,
|
|
lunInfo->Lun));
|
|
|
|
//
|
|
// Link LUN information on list.
|
|
//
|
|
|
|
lunInfo->NextLunInfo = busScanData->LunInfoList;
|
|
busScanData->LunInfoList = lunInfo;
|
|
|
|
//
|
|
// This buffer is used. Get another.
|
|
//
|
|
|
|
lunInfo = ExAllocatePool(NonPagedPool, sizeof(LUNINFO));
|
|
|
|
if (lunInfo == NULL) {
|
|
|
|
//
|
|
// Insufficient system resources to complete bus scan.
|
|
//
|
|
|
|
return busScanData;
|
|
}
|
|
|
|
RtlZeroMemory(lunInfo, sizeof(LUNINFO));
|
|
|
|
//
|
|
// Current logical unit extension claimed.
|
|
// Create next logical unit.
|
|
//
|
|
|
|
nextLogicalUnitExtension =
|
|
CreateLogicalUnitExtension(DeviceExtension);
|
|
|
|
if (nextLogicalUnitExtension == NULL) {
|
|
return busScanData;
|
|
}
|
|
|
|
//
|
|
// Link logical unit extension on list.
|
|
//
|
|
|
|
nextLogicalUnitExtension->NextLogicalUnit =
|
|
DeviceExtension->LogicalUnitList;
|
|
|
|
DeviceExtension->LogicalUnitList = nextLogicalUnitExtension;
|
|
|
|
device++;
|
|
}
|
|
|
|
} // end for (target ...
|
|
|
|
//
|
|
// Remove unused logicalunit extension from list.
|
|
//
|
|
|
|
DeviceExtension->LogicalUnitList =
|
|
DeviceExtension->LogicalUnitList->NextLogicalUnit;
|
|
|
|
ExFreePool(nextLogicalUnitExtension);
|
|
ExFreePool(lunInfo);
|
|
|
|
busScanData->NumberOfLogicalUnits = device;
|
|
DebugPrint((1,
|
|
"ScsiBusScan: Found %d devices on SCSI bus %d\n",
|
|
device,
|
|
ScsiBus));
|
|
|
|
return busScanData;
|
|
|
|
} // end ScsiBusScan()
|
|
|
|
|
|
PLOGICAL_UNIT_EXTENSION
|
|
CreateLogicalUnitExtension(
|
|
IN PDEVICE_EXTENSION DeviceExtension
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Create logical unit extension.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension
|
|
PathId
|
|
|
|
Return Value:
|
|
|
|
Logical unit extension
|
|
|
|
|
|
--*/
|
|
{
|
|
PLOGICAL_UNIT_EXTENSION logicalUnitExtension;
|
|
|
|
//
|
|
// Create logical unit extension and link in chain.
|
|
//
|
|
|
|
logicalUnitExtension =
|
|
ExAllocatePool(NonPagedPool, sizeof(LOGICAL_UNIT_EXTENSION));
|
|
|
|
if (logicalUnitExtension == NULL) {
|
|
return(NULL);
|
|
}
|
|
|
|
//
|
|
// Zero logical unit extension.
|
|
//
|
|
|
|
RtlZeroMemory(logicalUnitExtension, sizeof(LOGICAL_UNIT_EXTENSION));
|
|
|
|
//
|
|
// Allocate miniport driver logical unit extension if necessary.
|
|
//
|
|
|
|
if (DeviceExtension->HwLogicalUnitExtensionSize) {
|
|
|
|
logicalUnitExtension->SpecificLuExtension =
|
|
ExAllocatePool(NonPagedPool,
|
|
DeviceExtension->HwLogicalUnitExtensionSize);
|
|
|
|
if (logicalUnitExtension->SpecificLuExtension == NULL) {
|
|
return(NULL);
|
|
}
|
|
|
|
//
|
|
// Zero hardware logical unit extension.
|
|
//
|
|
|
|
RtlZeroMemory(logicalUnitExtension->SpecificLuExtension,
|
|
DeviceExtension->HwLogicalUnitExtensionSize);
|
|
}
|
|
|
|
//
|
|
// Set timer counters in LogicalUnits to -1 to indicate no
|
|
// outstanding requests.
|
|
//
|
|
|
|
logicalUnitExtension->RequestTimeoutCounter = -1;
|
|
|
|
//
|
|
// Clear the current request field.
|
|
//
|
|
|
|
logicalUnitExtension->CurrentRequest = NULL;
|
|
|
|
return logicalUnitExtension;
|
|
|
|
} // end CreateLogicalUnitExtension()
|
|
|
|
|
|
//
|
|
// Routines providing service to hardware dependent driver.
|
|
//
|
|
|
|
SCSI_PHYSICAL_ADDRESS
|
|
ScsiPortGetPhysicalAddress(
|
|
IN PVOID HwDeviceExtension,
|
|
IN PSCSI_REQUEST_BLOCK Srb,
|
|
IN PVOID VirtualAddress,
|
|
OUT ULONG *Length
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension =
|
|
((PDEVICE_EXTENSION) HwDeviceExtension) - 1;
|
|
PSRB_SCATTER_GATHER scatterList;
|
|
PIRP irp;
|
|
PMDL mdl;
|
|
ULONG byteOffset;
|
|
ULONG whichPage;
|
|
PULONG pages;
|
|
ULONG_PTR address;
|
|
|
|
if (Srb == NULL) {
|
|
|
|
//
|
|
// The virtual address is required to be within the non-cached extension
|
|
// and we can't allocate 4GB of non-cached extension in the loader so
|
|
// truncate the offset to a ULONG.
|
|
//
|
|
|
|
byteOffset = (ULONG) ((PUCHAR) deviceExtension->NonCachedExtension -
|
|
(PUCHAR) VirtualAddress);
|
|
|
|
if (deviceExtension->SrbExtensionZonePool) {
|
|
|
|
address = (PUCHAR) VirtualAddress -
|
|
(PUCHAR) deviceExtension->SrbExtensionZonePool +
|
|
deviceExtension->PhysicalZoneBase;
|
|
|
|
} else {
|
|
|
|
address = MmGetPhysicalAddress(VirtualAddress).LowPart;
|
|
}
|
|
|
|
//
|
|
// Return the requested length.
|
|
//
|
|
|
|
*Length = deviceExtension->NonCachedExtensionSize - byteOffset;
|
|
|
|
} else if (deviceExtension->MasterWithAdapter) {
|
|
|
|
//
|
|
// A scatter/gather list has already been allocated use it to determine
|
|
// the physical address and length. Get the scatter/gather list.
|
|
//
|
|
|
|
scatterList = GetLogicalUnitExtension(deviceExtension, Srb->TargetId)
|
|
->ScatterGather;
|
|
|
|
//
|
|
// Calculate byte offset into the data buffer.
|
|
//
|
|
|
|
byteOffset = (ULONG)((PCHAR) VirtualAddress - (PCHAR) Srb->DataBuffer);
|
|
|
|
//
|
|
// Find the appropirate entry in the scatter/gatter list.
|
|
//
|
|
|
|
while (byteOffset >= scatterList->Length) {
|
|
|
|
byteOffset -= scatterList->Length;
|
|
scatterList++;
|
|
}
|
|
|
|
//
|
|
// Calculate the physical address and length to be returned.
|
|
//
|
|
|
|
*Length = scatterList->Length - byteOffset;
|
|
return(ScsiPortConvertUlongToPhysicalAddress(scatterList->PhysicalAddress + byteOffset));
|
|
|
|
} else {
|
|
|
|
//
|
|
// Get IRP from SRB.
|
|
//
|
|
|
|
irp = Srb->OriginalRequest;
|
|
|
|
//
|
|
// Get MDL from IRP.
|
|
//
|
|
|
|
mdl = irp->MdlAddress;
|
|
|
|
//
|
|
// Calculate byte offset from
|
|
// beginning of first physical page.
|
|
//
|
|
|
|
byteOffset = (ULONG)((PCHAR)VirtualAddress - (PCHAR)mdl->StartVa);
|
|
|
|
//
|
|
// Calculate which physical page.
|
|
//
|
|
|
|
whichPage = byteOffset >> PAGE_SHIFT;
|
|
|
|
//
|
|
// Calculate beginning of physical page array.
|
|
//
|
|
|
|
pages = (PULONG)(mdl + 1);
|
|
|
|
//
|
|
// Calculate physical address.
|
|
//
|
|
|
|
address = (pages[whichPage] << PAGE_SHIFT) +
|
|
BYTE_OFFSET(VirtualAddress);
|
|
|
|
//
|
|
// Assume the buffer is contiguous. Just return the requested length.
|
|
//
|
|
}
|
|
|
|
return ScsiPortConvertUlongToPhysicalAddress(address);
|
|
|
|
} // end ScsiPortGetPhysicalAddress()
|
|
|
|
|
|
PVOID
|
|
ScsiPortGetVirtualAddress(
|
|
IN PVOID HwDeviceExtension,
|
|
IN SCSI_PHYSICAL_ADDRESS PhysicalAddress
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is returns a virtual address associated with a
|
|
physical address, if the physical address was obtained by a
|
|
call to ScsiPortGetPhysicalAddress.
|
|
|
|
Arguments:
|
|
|
|
PhysicalAddress
|
|
|
|
Return Value:
|
|
|
|
Virtual address if physical page hashed.
|
|
NULL if physical page not found in hash.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension =
|
|
((PDEVICE_EXTENSION) HwDeviceExtension) - 1;
|
|
PVOID address;
|
|
|
|
|
|
|
|
address = ScsiPortConvertPhysicalAddressToUlong(PhysicalAddress)
|
|
- deviceExtension->PhysicalZoneBase +
|
|
(PUCHAR)deviceExtension->SrbExtensionZonePool;
|
|
|
|
return address;
|
|
|
|
} // end ScsiPortGetVirtualAddress()
|
|
|
|
|
|
PVOID
|
|
ScsiPortGetLogicalUnit(
|
|
IN PVOID HwDeviceExtension,
|
|
IN UCHAR PathId,
|
|
IN UCHAR TargetId,
|
|
IN UCHAR Lun
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Walk port driver's logical unit extension list searching
|
|
for entry.
|
|
|
|
Arguments:
|
|
|
|
HwDeviceExtension - The port driver's device extension follows
|
|
the miniport's device extension and contains a pointer to
|
|
the logical device extension list.
|
|
|
|
PathId, TargetId and Lun - identify which logical unit on the
|
|
SCSI buses.
|
|
|
|
Return Value:
|
|
|
|
If entry found return miniport driver's logical unit extension.
|
|
Else, return NULL.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
PLOGICAL_UNIT_EXTENSION logicalUnit;
|
|
|
|
//
|
|
// Get pointer to port driver device extension.
|
|
//
|
|
|
|
deviceExtension = (PDEVICE_EXTENSION)HwDeviceExtension -1;
|
|
|
|
//
|
|
// Get pointer to logical unit list.
|
|
//
|
|
|
|
logicalUnit = deviceExtension->LogicalUnitList;
|
|
|
|
//
|
|
// Walk list looking at target id for requested logical unit extension.
|
|
//
|
|
|
|
while (logicalUnit != NULL) {
|
|
|
|
if ((logicalUnit->TargetId == TargetId) &&
|
|
(logicalUnit->PathId == PathId) &&
|
|
(logicalUnit->Lun == Lun)) {
|
|
|
|
//
|
|
// Logical unit extension found.
|
|
// Return specific logical unit extension.
|
|
//
|
|
|
|
return logicalUnit->SpecificLuExtension;
|
|
}
|
|
|
|
//
|
|
// Get next logical unit.
|
|
//
|
|
|
|
logicalUnit = logicalUnit->NextLogicalUnit;
|
|
}
|
|
|
|
//
|
|
// Requested logical unit extension not found.
|
|
//
|
|
|
|
return NULL;
|
|
|
|
} // end ScsiPortGetLogicalUnit()
|
|
|
|
VOID
|
|
ScsiPortNotification(
|
|
IN SCSI_NOTIFICATION_TYPE NotificationType,
|
|
IN PVOID HwDeviceExtension,
|
|
...
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension =
|
|
(PDEVICE_EXTENSION) HwDeviceExtension - 1;
|
|
PIO_STACK_LOCATION irpstack;
|
|
PLOGICAL_UNIT_EXTENSION logicalUnit;
|
|
PSCSI_REQUEST_BLOCK srb;
|
|
va_list(ap);
|
|
|
|
va_start(ap, HwDeviceExtension);
|
|
|
|
switch (NotificationType) {
|
|
|
|
case NextLuRequest:
|
|
case NextRequest:
|
|
|
|
//
|
|
// Start next packet on adapter's queue.
|
|
//
|
|
|
|
deviceExtension->InterruptFlags |= PD_READY_FOR_NEXT_REQUEST;
|
|
break;
|
|
|
|
case RequestComplete:
|
|
|
|
srb = va_arg(ap, PSCSI_REQUEST_BLOCK);
|
|
|
|
if (srb->SrbStatus == SRB_STATUS_ERROR) {
|
|
}
|
|
|
|
//
|
|
// Link the completed request into a forward-linked list of IRPs.
|
|
//
|
|
|
|
irpstack = IoGetCurrentIrpStackLocation(
|
|
((PIRP) srb->OriginalRequest)
|
|
);
|
|
|
|
irpstack->Parameters.Others.Argument3 =
|
|
deviceExtension->CompletedRequests;
|
|
|
|
deviceExtension->CompletedRequests = srb->OriginalRequest;
|
|
|
|
//
|
|
// Set logical unit current request to NULL
|
|
// to prevent race condition.
|
|
//
|
|
|
|
logicalUnit = GetLogicalUnitExtension(deviceExtension, srb->TargetId);
|
|
|
|
if (logicalUnit != NULL) {
|
|
logicalUnit->CurrentRequest = NULL;
|
|
} else {
|
|
ASSERT(logicalUnit != NULL); // Logic error, must debug this.
|
|
}
|
|
|
|
break;
|
|
|
|
case ResetDetected:
|
|
|
|
deviceExtension->PortTimeoutCounter = PD_TIMER_RESET_HOLD_TIME;
|
|
break;
|
|
|
|
case CallDisableInterrupts:
|
|
|
|
ASSERT(deviceExtension->Flags & PD_DISABLE_INTERRUPTS);
|
|
|
|
//
|
|
// The mini-port wants us to call the specified routine
|
|
// with interrupts disabled. This is done after the current
|
|
// HwRequestInterrutp routine completes. Indicate the call is
|
|
// needed and save the routine to be called.
|
|
//
|
|
|
|
deviceExtension->Flags |= PD_DISABLE_CALL_REQUEST;
|
|
|
|
deviceExtension->HwRequestInterrupt = va_arg(ap, PHW_INTERRUPT);
|
|
|
|
break;
|
|
|
|
case CallEnableInterrupts:
|
|
|
|
ASSERT(!(deviceExtension->Flags & PD_DISABLE_INTERRUPTS));
|
|
|
|
//
|
|
// The mini-port wants us to call the specified routine
|
|
// with interrupts enabled this is done from the DPC.
|
|
// Disable calls to the interrupt routine, indicate the call is
|
|
// needed and save the routine to be called.
|
|
//
|
|
|
|
deviceExtension->Flags |= PD_DISABLE_INTERRUPTS | PD_ENABLE_CALL_REQUEST;
|
|
|
|
deviceExtension->HwRequestInterrupt = va_arg(ap, PHW_INTERRUPT);
|
|
|
|
break;
|
|
|
|
case RequestTimerCall:
|
|
|
|
deviceExtension->HwTimerRequest = va_arg(ap, PHW_INTERRUPT);
|
|
deviceExtension->TimerValue = va_arg(ap, ULONG);
|
|
|
|
if (deviceExtension->TimerValue) {
|
|
|
|
//
|
|
// Round up the timer value to the stall time.
|
|
//
|
|
|
|
deviceExtension->TimerValue = (deviceExtension->TimerValue
|
|
+ PD_INTERLOOP_STALL - 1)/ PD_INTERLOOP_STALL;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
va_end(ap);
|
|
|
|
//
|
|
// Check to see if the last DPC has been processed yet. If so
|
|
// queue another DPC.
|
|
//
|
|
|
|
ScsiPortCompletionDpc(
|
|
NULL, // Dpc
|
|
deviceExtension->DeviceObject, // DeviceObject
|
|
NULL, // Irp
|
|
NULL // Context
|
|
);
|
|
|
|
} // end ScsiPortNotification()
|
|
|
|
|
|
VOID
|
|
ScsiPortFlushDma(
|
|
IN PVOID HwDeviceExtension
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine checks to see if the perivious IoMapTransfer has been done
|
|
started. If it has not, then the PD_MAP_TRANSER flag is cleared, and the
|
|
routine returns; otherwise, this routine schedules a DPC which will call
|
|
IoFlushAdapter buffers.
|
|
|
|
Arguments:
|
|
|
|
HwDeviceExtension - Supplies a the hardware device extension for the
|
|
host bus adapter which will be doing the data transfer.
|
|
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
|
|
deviceExtension = ((PDEVICE_EXTENSION) HwDeviceExtension) - 1;
|
|
|
|
if (deviceExtension->InterruptFlags & PD_MAP_TRANSFER) {
|
|
|
|
//
|
|
// The transfer has not been started so just clear the map transfer
|
|
// flag and return.
|
|
//
|
|
|
|
deviceExtension->InterruptFlags &= ~PD_MAP_TRANSFER;
|
|
return;
|
|
}
|
|
|
|
deviceExtension->InterruptFlags |= PD_FLUSH_ADAPTER_BUFFERS;
|
|
|
|
//
|
|
// Check to see if the last DPC has been processed yet. If so
|
|
// queue another DPC.
|
|
//
|
|
|
|
ScsiPortCompletionDpc(
|
|
NULL, // Dpc
|
|
deviceExtension->DeviceObject, // DeviceObject
|
|
NULL, // Irp
|
|
NULL // Context
|
|
);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
VOID
|
|
ScsiPortIoMapTransfer(
|
|
IN PVOID HwDeviceExtension,
|
|
IN PSCSI_REQUEST_BLOCK Srb,
|
|
IN PVOID LogicalAddress,
|
|
IN ULONG Length
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Saves the parameters for the call to IoMapTransfer and schedules the DPC
|
|
if necessary.
|
|
|
|
Arguments:
|
|
|
|
HwDeviceExtension - Supplies a the hardware device extension for the
|
|
host bus adapter which will be doing the data transfer.
|
|
|
|
Srb - Supplies the particular request that data transfer is for.
|
|
|
|
LogicalAddress - Supplies the logical address where the transfer should
|
|
begin.
|
|
|
|
Length - Supplies the maximum length in bytes of the transfer.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
|
|
deviceExtension = ((PDEVICE_EXTENSION) HwDeviceExtension) - 1;
|
|
|
|
//
|
|
// Make sure this host bus adapter has an Dma adapter object.
|
|
//
|
|
|
|
if (deviceExtension->DmaAdapterObject == NULL) {
|
|
//
|
|
// No DMA adapter, no work.
|
|
//
|
|
return;
|
|
}
|
|
|
|
deviceExtension->MapTransferParameters.Srb = Srb;
|
|
deviceExtension->MapTransferParameters.LogicalAddress = LogicalAddress;
|
|
deviceExtension->MapTransferParameters.Length = Length;
|
|
|
|
deviceExtension->InterruptFlags |= PD_MAP_TRANSFER;
|
|
|
|
//
|
|
// Check to see if the last DPC has been processed yet. If so
|
|
// queue another DPC.
|
|
//
|
|
|
|
ScsiPortCompletionDpc(
|
|
NULL, // Dpc
|
|
deviceExtension->DeviceObject, // DeviceObject
|
|
NULL, // Irp
|
|
NULL // Context
|
|
);
|
|
|
|
} // end ScsiPortIoMapTransfer()
|
|
|
|
|
|
VOID
|
|
IssueRequestSense(
|
|
IN PDEVICE_EXTENSION deviceExtension,
|
|
IN PSCSI_REQUEST_BLOCK FailingSrb
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine creates a REQUEST SENSE request and uses IoCallDriver to
|
|
renter the driver. The completion routine cleans up the data structures
|
|
and processes the logical unit queue according to the flags.
|
|
|
|
A pointer to failing SRB is stored at the end of the request sense
|
|
Srb, so that the completion routine can find it.
|
|
|
|
Arguments:
|
|
|
|
DeviceExension - Supplies a pointer to the device extension for this
|
|
SCSI port.
|
|
|
|
FailingSrb - Supplies a pointer to the request that the request sense
|
|
is being done for.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION irpstack;
|
|
PIRP Irp;
|
|
PSCSI_REQUEST_BLOCK Srb;
|
|
PCDB cdb;
|
|
PVOID *Pointer;
|
|
|
|
//
|
|
// Allocate Srb from non-paged pool
|
|
// plus room for a pointer to the failing IRP.
|
|
// Since this routine is in an error-handling
|
|
// path and a shortterm allocation
|
|
// NonPagedMustSucceed is requested.
|
|
//
|
|
|
|
Srb = &RequestSenseSrb.Srb;
|
|
|
|
//
|
|
// Allocate an IRP to issue the REQUEST SENSE request.
|
|
//
|
|
|
|
Irp = InitializeIrp(
|
|
&RequestSenseSrb,
|
|
IRP_MJ_READ,
|
|
deviceExtension->DeviceObject,
|
|
FailingSrb->SenseInfoBuffer,
|
|
FailingSrb->SenseInfoBufferLength
|
|
);
|
|
|
|
irpstack = IoGetNextIrpStackLocation(Irp);
|
|
|
|
irpstack->MajorFunction = IRP_MJ_SCSI;
|
|
|
|
//
|
|
// Save the Failing SRB after the request sense Srb.
|
|
//
|
|
|
|
Pointer = (PVOID *) (Srb+1);
|
|
*Pointer = FailingSrb;
|
|
|
|
//
|
|
// Build the REQUEST SENSE CDB.
|
|
//
|
|
|
|
Srb->CdbLength = 6;
|
|
cdb = (PCDB)Srb->Cdb;
|
|
|
|
cdb->CDB6INQUIRY.OperationCode = SCSIOP_REQUEST_SENSE;
|
|
cdb->CDB6INQUIRY.LogicalUnitNumber = 0;
|
|
cdb->CDB6INQUIRY.Reserved1 = 0;
|
|
cdb->CDB6INQUIRY.PageCode = 0;
|
|
cdb->CDB6INQUIRY.IReserved = 0;
|
|
cdb->CDB6INQUIRY.AllocationLength =
|
|
(UCHAR)FailingSrb->SenseInfoBufferLength;
|
|
cdb->CDB6INQUIRY.Control = 0;
|
|
|
|
//
|
|
// Save SRB address in next stack for port driver.
|
|
//
|
|
|
|
irpstack->Parameters.Others.Argument1 = (PVOID)Srb;
|
|
|
|
//
|
|
// Set up IRP Address.
|
|
//
|
|
|
|
Srb->OriginalRequest = Irp;
|
|
|
|
Srb->NextSrb = 0;
|
|
|
|
//
|
|
// Set up SCSI bus address.
|
|
//
|
|
|
|
Srb->TargetId = FailingSrb->TargetId;
|
|
Srb->Lun = FailingSrb->Lun;
|
|
Srb->PathId = FailingSrb->PathId;
|
|
Srb->Length = sizeof(SCSI_REQUEST_BLOCK);
|
|
Srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
|
|
|
|
//
|
|
// Set timeout value to 2 seconds.
|
|
//
|
|
|
|
Srb->TimeOutValue = 2;
|
|
|
|
//
|
|
// Disable auto request sense.
|
|
//
|
|
|
|
Srb->SenseInfoBufferLength = 0;
|
|
|
|
//
|
|
// Sense buffer is in stack.
|
|
//
|
|
|
|
Srb->SenseInfoBuffer = NULL;
|
|
|
|
//
|
|
// Set read and bypass frozen queue bits in flags.
|
|
//
|
|
|
|
//
|
|
// Set a speical flags to indicate the logical unit queue should be by
|
|
// passed and that no queue processing should be done when the request
|
|
// completes.
|
|
//
|
|
|
|
Srb->SrbFlags = SRB_FLAGS_DATA_IN | SRB_FLAGS_BYPASS_FROZEN_QUEUE |
|
|
SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_DISABLE_DISCONNECT;
|
|
|
|
Srb->DataBuffer = FailingSrb->SenseInfoBuffer;
|
|
|
|
//
|
|
// Set the transfer length.
|
|
//
|
|
|
|
Srb->DataTransferLength = FailingSrb->SenseInfoBufferLength;
|
|
|
|
//
|
|
// Zero out status.
|
|
//
|
|
|
|
Srb->ScsiStatus = Srb->SrbStatus = 0;
|
|
|
|
(VOID)IoCallDriver(deviceExtension->DeviceObject, Irp);
|
|
|
|
ScsiPortInternalCompletion(deviceExtension->DeviceObject, Irp, Srb);
|
|
|
|
return;
|
|
|
|
} // end IssueRequestSense()
|
|
|
|
|
|
VOID
|
|
ScsiPortInternalCompletion(
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PIRP Irp,
|
|
PVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Device object
|
|
IRP
|
|
Context - pointer to SRB
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PSCSI_REQUEST_BLOCK srb = Context;
|
|
PIO_STACK_LOCATION irpstack = IoGetCurrentIrpStackLocation(Irp);
|
|
PSCSI_REQUEST_BLOCK failingSrb;
|
|
PIRP failingIrp;
|
|
|
|
UNREFERENCED_PARAMETER(DeviceObject);
|
|
|
|
//
|
|
// Request sense completed. If successful or data over/underrun
|
|
// get the failing SRB and indicate that the sense information
|
|
// is valid. The class driver will check for underrun and determine
|
|
// if there is enough sense information to be useful.
|
|
//
|
|
|
|
if ((SRB_STATUS(srb->SrbStatus) == SRB_STATUS_SUCCESS) ||
|
|
(SRB_STATUS(srb->SrbStatus) == SRB_STATUS_DATA_OVERRUN)) {
|
|
|
|
//
|
|
// Get a pointer to failing Irp and Srb.
|
|
//
|
|
|
|
failingSrb = *((PVOID *) (srb+1));
|
|
failingIrp = failingSrb->OriginalRequest;
|
|
|
|
//
|
|
// Report sense buffer is valid.
|
|
//
|
|
|
|
failingSrb->SrbStatus |= SRB_STATUS_AUTOSENSE_VALID;
|
|
|
|
//
|
|
// Copy bytes transferred to failing SRB
|
|
// request sense length field to communicate
|
|
// to the class drivers the number of valid
|
|
// sense bytes.
|
|
//
|
|
|
|
failingSrb->SenseInfoBufferLength = (UCHAR) srb->DataTransferLength;
|
|
|
|
}
|
|
|
|
} // ScsiPortInternalCompletion()
|
|
|
|
|
|
VOID
|
|
ScsiPortTickHandler(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension =
|
|
(PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
|
PLOGICAL_UNIT_EXTENSION logicalUnit;
|
|
|
|
UNREFERENCED_PARAMETER(Context);
|
|
|
|
logicalUnit = deviceExtension->LogicalUnitList;
|
|
|
|
//
|
|
// NOTE: The use of Current request needs to be synchronized with the
|
|
// clearing of current request.
|
|
//
|
|
|
|
while (logicalUnit != NULL) {
|
|
|
|
//
|
|
// Check for busy requests.
|
|
//
|
|
|
|
if (logicalUnit->Flags & PD_LOGICAL_UNIT_IS_BUSY) {
|
|
|
|
DebugPrint((1,"ScsiPortTickHandler: Retrying busy status request\n"));
|
|
|
|
//
|
|
// Clear the busy flag and retry the request.
|
|
//
|
|
|
|
logicalUnit->Flags &= ~PD_LOGICAL_UNIT_IS_BUSY;
|
|
|
|
ScsiPortStartIo(DeviceObject, logicalUnit->CurrentRequest);
|
|
|
|
} else if (logicalUnit->RequestTimeoutCounter == 0) {
|
|
|
|
//
|
|
// Request timed out.
|
|
//
|
|
|
|
DebugPrint((1, "ScsiPortTickHandler: Request timed out\n"));
|
|
|
|
//
|
|
// Reset request timeout counter to unused state.
|
|
//
|
|
|
|
logicalUnit->RequestTimeoutCounter = -1;
|
|
|
|
//
|
|
// Build and send request to abort command.
|
|
//
|
|
|
|
IssueAbortRequest(deviceExtension, logicalUnit->CurrentRequest);
|
|
} else if (logicalUnit->RequestTimeoutCounter != -1) {
|
|
|
|
DebugPrint((1, "ScsiPortTickHandler: Timeout value %lx\n",logicalUnit->RequestTimeoutCounter));
|
|
logicalUnit->RequestTimeoutCounter--;
|
|
}
|
|
|
|
logicalUnit = logicalUnit->NextLogicalUnit;
|
|
}
|
|
|
|
return;
|
|
|
|
} // end ScsiPortTickHandler()
|
|
|
|
|
|
VOID
|
|
IssueAbortRequest(
|
|
IN PDEVICE_EXTENSION DeviceExtension,
|
|
IN PIRP FailingIrp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
A request timed out and to clear the request at the HBA
|
|
an ABORT request is issued. But first, if the request
|
|
that timed out was an ABORT command, then reset the
|
|
adapter instead.
|
|
|
|
Arguments:
|
|
|
|
DeviceExension - Supplies a pointer to the device extension for this
|
|
SCSI port.
|
|
|
|
FailingIrp - Supplies a pointer to the request that is to be aborted.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
ULONG j;
|
|
|
|
//
|
|
// A request to abort failed.
|
|
// Need to reset the adapter.
|
|
//
|
|
|
|
DebugPrint((1,"IssueAbort: Request timed out, resetting the bus.\n"));
|
|
|
|
|
|
if (!DeviceExtension->HwReset(
|
|
DeviceExtension->HwDeviceExtension,
|
|
0)){
|
|
|
|
DebugPrint((1,"Reset SCSI bus failed\n"));
|
|
}
|
|
|
|
//
|
|
// Call the interupt handler for a few microseconds to clear any reset
|
|
// interrupts.
|
|
//
|
|
|
|
for (j = 0; j < 1000 * 100; j++) {
|
|
|
|
FwStallExecution(10);
|
|
if (DeviceExtension->HwInterrupt != NULL) {
|
|
DeviceExtension->HwInterrupt(DeviceExtension->HwDeviceExtension);
|
|
}
|
|
|
|
}
|
|
|
|
DeviceExtension->PortTimeoutCounter = PD_TIMER_RESET_HOLD_TIME;
|
|
SpCheckResetDelay( DeviceExtension );
|
|
|
|
return;
|
|
|
|
|
|
} // end IssueAbortRequest()
|
|
|
|
|
|
VOID
|
|
SpCheckResetDelay(
|
|
IN PDEVICE_EXTENSION deviceExtension
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
If there is a pending reset delay, this routine stalls the execution
|
|
for the specified number of seconds. During the delay the timer
|
|
routines are called at the appropriate times.
|
|
|
|
Arguments:
|
|
|
|
DeviceExension - Supplies a pointer to the device extension for this
|
|
SCSI port.
|
|
|
|
Return Value:
|
|
|
|
Nothing.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG milliSecondTime;
|
|
|
|
//
|
|
// Check if the adapter is ready to accept requests.
|
|
//
|
|
|
|
while (deviceExtension->PortTimeoutCounter) {
|
|
|
|
deviceExtension->PortTimeoutCounter--;
|
|
|
|
//
|
|
// One second delay.
|
|
//
|
|
|
|
for ( milliSecondTime = 0;
|
|
milliSecondTime < ((1000*1000)/PD_INTERLOOP_STALL);
|
|
milliSecondTime++ ) {
|
|
|
|
FwStallExecution(PD_INTERLOOP_STALL);
|
|
|
|
//
|
|
// Check the miniport timer.
|
|
//
|
|
|
|
if (deviceExtension->TimerValue != 0) {
|
|
|
|
deviceExtension->TimerValue--;
|
|
|
|
if (deviceExtension->TimerValue == 0) {
|
|
|
|
//
|
|
// The timer timed out so called requested timer routine.
|
|
//
|
|
|
|
deviceExtension->HwTimerRequest(deviceExtension->HwDeviceExtension);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
BOOLEAN
|
|
SpGetInterruptState(
|
|
IN PVOID ServiceContext
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine saves the InterruptFlags, MapTransferParameters and
|
|
CompletedRequests fields and clears the InterruptFlags.
|
|
|
|
Arguments:
|
|
|
|
ServiceContext - Supplies a pointer to the device extension for this
|
|
SCSI port.
|
|
|
|
Return Value:
|
|
|
|
Always returns TRUE.
|
|
|
|
Notes:
|
|
|
|
Called via KeSynchronizeExecution.
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension = ServiceContext;
|
|
|
|
//
|
|
// Move the interrupt state to save area.
|
|
//
|
|
|
|
deviceExtension->InterruptFlags = deviceExtension->InterruptFlags;
|
|
deviceExtension->CompletedRequests = deviceExtension->CompletedRequests;
|
|
deviceExtension->MapTransferParameters = deviceExtension->MapTransferParameters;
|
|
|
|
//
|
|
// Clear the interrupt state.
|
|
//
|
|
|
|
deviceExtension->InterruptFlags = 0;
|
|
deviceExtension->CompletedRequests = NULL;
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
VOID
|
|
ScsiDebugPause(
|
|
VOID
|
|
)
|
|
{
|
|
#if DBG
|
|
#define SCSIDEBUG_PAUSE 0x100
|
|
#define SCSIDEBUG_PAUSE_LIMIT 20
|
|
|
|
static ULONG ScsiDebugPauseCount;
|
|
|
|
if (++ScsiDebugPauseCount > SCSIDEBUG_PAUSE_LIMIT) {
|
|
ScsiDebugPauseCount = 0;
|
|
if (ScsiDebug & SCSIDEBUG_PAUSE) {
|
|
DebugPrint((1, "Hit any key.\n"));
|
|
while(!GET_KEY()); // DEBUG Only!
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
VOID
|
|
ScsiPortLogError(
|
|
IN PVOID HwDeviceExtension,
|
|
IN PSCSI_REQUEST_BLOCK Srb OPTIONAL,
|
|
IN UCHAR PathId,
|
|
IN UCHAR TargetId,
|
|
IN UCHAR Lun,
|
|
IN ULONG ErrorCode,
|
|
IN ULONG UniqueId
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine allocates an error log entry, copies the supplied text
|
|
to it, and requests that it be written to the error log file.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtenson - Supplies the HBA mini-port driver's adapter data storage.
|
|
|
|
TargetId, Lun and PathId - specify device address on a SCSI bus.
|
|
|
|
ErrorCode - Supplies an error code indicating the type of error.
|
|
|
|
UniqueId - Supplies a unique identifier for the error.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PCHAR errorCodeString;
|
|
|
|
switch (ErrorCode) {
|
|
case SP_BUS_PARITY_ERROR:
|
|
errorCodeString = "SCSI bus partity error";
|
|
break;
|
|
|
|
case SP_UNEXPECTED_DISCONNECT:
|
|
errorCodeString = "Unexpected disconnect";
|
|
break;
|
|
|
|
case SP_INVALID_RESELECTION:
|
|
errorCodeString = "Invalid reselection";
|
|
break;
|
|
|
|
case SP_BUS_TIME_OUT:
|
|
errorCodeString = "SCSI bus time out";
|
|
break;
|
|
|
|
case SP_PROTOCOL_ERROR:
|
|
errorCodeString = "SCSI protocol error";
|
|
break;
|
|
|
|
case SP_INTERNAL_ADAPTER_ERROR:
|
|
errorCodeString = "Internal adapter error";
|
|
break;
|
|
|
|
default:
|
|
errorCodeString = "Unknown error code";
|
|
break;
|
|
|
|
}
|
|
|
|
DebugPrint((1,"\n\nLogErrorEntry: Logging SCSI error packet. ErrorCode = %s.\n",
|
|
errorCodeString));
|
|
DebugPrint((1,
|
|
"PathId = %2x, TargetId = %2x, Lun = %2x, UniqueId = %x.\n\n",
|
|
PathId,
|
|
TargetId,
|
|
Lun,
|
|
UniqueId));
|
|
|
|
#if DBG
|
|
ScsiDebugPause();
|
|
#endif
|
|
|
|
return;
|
|
|
|
} // end ScsiPortLogError()
|
|
|
|
|
|
VOID
|
|
ScsiPortCompleteRequest(
|
|
IN PVOID HwDeviceExtension,
|
|
IN UCHAR PathId,
|
|
IN UCHAR TargetId,
|
|
IN UCHAR Lun,
|
|
IN UCHAR SrbStatus
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Complete all active requests for the specified logical unit.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtenson - Supplies the HBA mini-port driver's adapter data storage.
|
|
|
|
TargetId, Lun and PathId - specify device address on a SCSI bus.
|
|
|
|
SrbStatus - Status to be returned in each completed SRB.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension =
|
|
((PDEVICE_EXTENSION) HwDeviceExtension) - 1;
|
|
PSCSI_REQUEST_BLOCK Srb;
|
|
PSCSI_REQUEST_BLOCK failingSrb;
|
|
PLOGICAL_UNIT_EXTENSION luExtension;
|
|
PIRP nextIrp;
|
|
PIO_STACK_LOCATION irpstack;
|
|
|
|
UNREFERENCED_PARAMETER(PathId);
|
|
UNREFERENCED_PARAMETER(Lun);
|
|
|
|
if (TargetId == (UCHAR)(-1)) {
|
|
|
|
//
|
|
// Complete requests for all units on this bus.
|
|
//
|
|
|
|
luExtension = deviceExtension->LogicalUnitList;
|
|
|
|
while (luExtension != NULL) {
|
|
|
|
//
|
|
// Complete requests until queue is empty.
|
|
//
|
|
|
|
if ((nextIrp = luExtension->CurrentRequest) != NULL &&
|
|
!(luExtension->Flags & PD_LOGICAL_UNIT_IS_BUSY)) {
|
|
|
|
//
|
|
// Get SRB address from current IRP stack.
|
|
//
|
|
|
|
irpstack = IoGetCurrentIrpStackLocation(nextIrp);
|
|
|
|
Srb = (PSCSI_REQUEST_BLOCK)irpstack->Parameters.Others.Argument1;
|
|
|
|
//
|
|
// Just in case this is an abort request,
|
|
// get pointer to failingSrb.
|
|
//
|
|
|
|
failingSrb = Srb->NextSrb;
|
|
|
|
//
|
|
// Update SRB status.
|
|
//
|
|
|
|
Srb->SrbStatus = SrbStatus;
|
|
|
|
//
|
|
// Indicate no bytes transferred.
|
|
//
|
|
|
|
Srb->DataTransferLength = 0;
|
|
|
|
//
|
|
// Set IRP status.
|
|
//
|
|
|
|
nextIrp->IoStatus.Status = STATUS_UNSUCCESSFUL;
|
|
|
|
//
|
|
// Move bytes transferred to IRP.
|
|
//
|
|
|
|
nextIrp->IoStatus.Information = Srb->DataTransferLength;
|
|
|
|
//
|
|
// Call notification routine.
|
|
//
|
|
|
|
ScsiPortNotification(RequestComplete,
|
|
(PVOID)HwDeviceExtension,
|
|
Srb);
|
|
|
|
if (failingSrb) {
|
|
|
|
//
|
|
// This was an abort request. The failing
|
|
// SRB must also be completed.
|
|
//
|
|
|
|
failingSrb->SrbStatus = SrbStatus;
|
|
failingSrb->DataTransferLength = 0;
|
|
|
|
//
|
|
// Get IRP from SRB.
|
|
//
|
|
|
|
nextIrp = failingSrb->OriginalRequest;
|
|
|
|
//
|
|
// Set IRP status.
|
|
//
|
|
|
|
nextIrp->IoStatus.Status = STATUS_UNSUCCESSFUL;
|
|
|
|
//
|
|
// Move bytes transferred to IRP.
|
|
//
|
|
|
|
nextIrp->IoStatus.Information =
|
|
failingSrb->DataTransferLength;
|
|
|
|
//
|
|
// Call notification routine.
|
|
//
|
|
|
|
ScsiPortNotification(RequestComplete,
|
|
(PVOID)HwDeviceExtension,
|
|
failingSrb);
|
|
}
|
|
|
|
} // end if
|
|
|
|
luExtension = luExtension->NextLogicalUnit;
|
|
|
|
} // end while
|
|
|
|
} else {
|
|
|
|
//
|
|
// Complete all requests for this logical unit.
|
|
//
|
|
|
|
luExtension =
|
|
GetLogicalUnitExtension(deviceExtension, TargetId);
|
|
|
|
ASSERT(luExtension != NULL);
|
|
|
|
//
|
|
// Complete requests until queue is empty.
|
|
//
|
|
|
|
if ((luExtension != NULL) && ((nextIrp = luExtension->CurrentRequest) != NULL)) {
|
|
|
|
//
|
|
// Get SRB address from current IRP stack.
|
|
//
|
|
|
|
irpstack = IoGetCurrentIrpStackLocation(nextIrp);
|
|
|
|
Srb = (PSCSI_REQUEST_BLOCK)irpstack->Parameters.Others.Argument1;
|
|
|
|
//
|
|
// Update SRB status.
|
|
//
|
|
|
|
Srb->SrbStatus = SrbStatus;
|
|
|
|
//
|
|
// Indicate no bytes transferred.
|
|
//
|
|
|
|
Srb->DataTransferLength = 0;
|
|
|
|
//
|
|
// Set IRP status.
|
|
//
|
|
|
|
nextIrp->IoStatus.Status = STATUS_UNSUCCESSFUL;
|
|
|
|
//
|
|
// Move bytes transferred to IRP.
|
|
//
|
|
|
|
nextIrp->IoStatus.Information = Srb->DataTransferLength;
|
|
|
|
//
|
|
// Call notification routine.
|
|
//
|
|
|
|
ScsiPortNotification(RequestComplete,
|
|
(PVOID)HwDeviceExtension,
|
|
Srb);
|
|
|
|
} // end if
|
|
|
|
} // end if ... else
|
|
|
|
return;
|
|
|
|
|
|
} // end ScsiPortCompleteRequest()
|
|
|
|
|
|
VOID
|
|
ScsiPortMoveMemory(
|
|
IN PVOID WriteBuffer,
|
|
IN PVOID ReadBuffer,
|
|
IN ULONG Length
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Copy from one buffer into another.
|
|
|
|
Arguments:
|
|
|
|
ReadBuffer - source
|
|
WriteBuffer - destination
|
|
Length - number of bytes to copy
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
RtlMoveMemory(WriteBuffer, ReadBuffer, Length);
|
|
|
|
} // end ScsiPortMoveMemory()
|
|
|
|
|
|
VOID
|
|
ScsiPortStallExecution(
|
|
ULONG Delay
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Wait number of microseconds in tight processor loop.
|
|
|
|
Arguments:
|
|
|
|
Delay - number of microseconds to wait.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
FwStallExecution(Delay);
|
|
|
|
} // end ScsiPortStallExecution()
|
|
|
|
|
|
PLOGICAL_UNIT_EXTENSION
|
|
GetLogicalUnitExtension(
|
|
PDEVICE_EXTENSION deviceExtension,
|
|
UCHAR TargetId
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Walk logical unit extension list looking for
|
|
extension with matching target id.
|
|
|
|
Arguments:
|
|
|
|
deviceExtension
|
|
TargetId
|
|
|
|
Return Value:
|
|
|
|
Requested logical unit extension if found,
|
|
else NULL.
|
|
|
|
--*/
|
|
|
|
{
|
|
PLOGICAL_UNIT_EXTENSION logicalUnit = deviceExtension->LogicalUnitList;
|
|
|
|
while (logicalUnit != NULL) {
|
|
|
|
if (logicalUnit->TargetId == TargetId) {
|
|
|
|
return logicalUnit;
|
|
}
|
|
|
|
logicalUnit = logicalUnit->NextLogicalUnit;
|
|
}
|
|
|
|
//
|
|
// Logical unit extension not found.
|
|
//
|
|
|
|
return (PLOGICAL_UNIT_EXTENSION)NULL;
|
|
|
|
} // end GetLogicalUnitExtension()
|
|
|
|
#if DBG
|
|
|
|
|
|
VOID
|
|
ScsiDebugPrint(
|
|
ULONG DebugPrintLevel,
|
|
PCCHAR DebugMessage,
|
|
...
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Debug print for all SCSI drivers
|
|
|
|
Arguments:
|
|
|
|
Debug print level between 0 and 3, with 3 being the most verbose.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
va_list ap;
|
|
|
|
va_start( ap, DebugMessage );
|
|
|
|
if (DebugPrintLevel <= (ScsiDebug & (SCSIDEBUG_PAUSE-1))) {
|
|
|
|
char buffer[256];
|
|
|
|
_vsnprintf(buffer, sizeof(buffer), DebugMessage, ap);
|
|
#if defined(_MIPS_) || defined(_ALPHA_) || defined(_PPC_)
|
|
FwPrint(buffer);
|
|
FwPrint("\r");
|
|
#else
|
|
BlPrint(buffer);
|
|
BlPrint("\r");
|
|
#endif
|
|
DbgPrint(buffer);
|
|
}
|
|
|
|
va_end(ap);
|
|
}
|
|
|
|
#else
|
|
|
|
//
|
|
// ScsiDebugPrint stub
|
|
//
|
|
|
|
VOID
|
|
ScsiDebugPrint(
|
|
ULONG DebugPrintLevel,
|
|
PCCHAR DebugMessage,
|
|
...
|
|
)
|
|
{
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
UCHAR
|
|
ScsiPortReadPortUchar(
|
|
IN PUCHAR Port
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Read from the specificed port address.
|
|
|
|
Arguments:
|
|
|
|
Port - Supplies a pointer to the port address.
|
|
|
|
Return Value:
|
|
|
|
Returns the value read from the specified port address.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
#ifdef MIPS
|
|
|
|
return(READ_REGISTER_UCHAR(Port));
|
|
|
|
#else
|
|
|
|
return(READ_PORT_UCHAR(Port));
|
|
|
|
#endif
|
|
}
|
|
|
|
USHORT
|
|
ScsiPortReadPortUshort(
|
|
IN PUSHORT Port
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Read from the specificed port address.
|
|
|
|
Arguments:
|
|
|
|
Port - Supplies a pointer to the port address.
|
|
|
|
Return Value:
|
|
|
|
Returns the value read from the specified port address.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
#ifdef MIPS
|
|
|
|
return(READ_REGISTER_USHORT(Port));
|
|
|
|
#else
|
|
|
|
return(READ_PORT_USHORT(Port));
|
|
|
|
#endif
|
|
}
|
|
|
|
ULONG
|
|
ScsiPortReadPortUlong(
|
|
IN PULONG Port
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Read from the specificed port address.
|
|
|
|
Arguments:
|
|
|
|
Port - Supplies a pointer to the port address.
|
|
|
|
Return Value:
|
|
|
|
Returns the value read from the specified port address.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
#ifdef MIPS
|
|
|
|
return(READ_REGISTER_ULONG(Port));
|
|
|
|
#else
|
|
|
|
return(READ_PORT_ULONG(Port));
|
|
|
|
#endif
|
|
}
|
|
|
|
UCHAR
|
|
ScsiPortReadRegisterUchar(
|
|
IN PUCHAR Register
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Read from the specificed register address.
|
|
|
|
Arguments:
|
|
|
|
Register - Supplies a pointer to the register address.
|
|
|
|
Return Value:
|
|
|
|
Returns the value read from the specified register address.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
return(READ_REGISTER_UCHAR(Register));
|
|
|
|
}
|
|
|
|
USHORT
|
|
ScsiPortReadRegisterUshort(
|
|
IN PUSHORT Register
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Read from the specificed register address.
|
|
|
|
Arguments:
|
|
|
|
Register - Supplies a pointer to the register address.
|
|
|
|
Return Value:
|
|
|
|
Returns the value read from the specified register address.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
return(READ_REGISTER_USHORT(Register));
|
|
|
|
}
|
|
|
|
ULONG
|
|
ScsiPortReadRegisterUlong(
|
|
IN PULONG Register
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Read from the specificed register address.
|
|
|
|
Arguments:
|
|
|
|
Register - Supplies a pointer to the register address.
|
|
|
|
Return Value:
|
|
|
|
Returns the value read from the specified register address.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
return(READ_REGISTER_ULONG(Register));
|
|
|
|
}
|
|
|
|
VOID
|
|
ScsiPortReadRegisterBufferUchar(
|
|
IN PUCHAR Register,
|
|
IN PUCHAR Buffer,
|
|
IN ULONG Count
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Read a buffer of unsigned bytes from the specified register address.
|
|
|
|
Arguments:
|
|
|
|
Register - Supplies a pointer to the port address.
|
|
Buffer - Supplies a pointer to the data buffer area.
|
|
Count - The count of items to move.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
READ_REGISTER_BUFFER_UCHAR(Register, Buffer, Count);
|
|
|
|
}
|
|
|
|
VOID
|
|
ScsiPortReadRegisterBufferUshort(
|
|
IN PUSHORT Register,
|
|
IN PUSHORT Buffer,
|
|
IN ULONG Count
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Read a buffer of unsigned shorts from the specified register address.
|
|
|
|
Arguments:
|
|
|
|
Register - Supplies a pointer to the port address.
|
|
Buffer - Supplies a pointer to the data buffer area.
|
|
Count - The count of items to move.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
READ_REGISTER_BUFFER_USHORT(Register, Buffer, Count);
|
|
|
|
}
|
|
|
|
VOID
|
|
ScsiPortReadRegisterBufferUlong(
|
|
IN PULONG Register,
|
|
IN PULONG Buffer,
|
|
IN ULONG Count
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Read a buffer of unsigned longs from the specified register address.
|
|
|
|
Arguments:
|
|
|
|
Register - Supplies a pointer to the port address.
|
|
Buffer - Supplies a pointer to the data buffer area.
|
|
Count - The count of items to move.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
READ_REGISTER_BUFFER_ULONG(Register, Buffer, Count);
|
|
|
|
}
|
|
|
|
VOID
|
|
ScsiPortWritePortUchar(
|
|
IN PUCHAR Port,
|
|
IN UCHAR Value
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Write to the specificed port address.
|
|
|
|
Arguments:
|
|
|
|
Port - Supplies a pointer to the port address.
|
|
|
|
Value - Supplies the value to be written.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
#ifdef MIPS
|
|
|
|
WRITE_REGISTER_UCHAR(Port, Value);
|
|
|
|
#else
|
|
|
|
WRITE_PORT_UCHAR(Port, Value);
|
|
|
|
#endif
|
|
}
|
|
|
|
VOID
|
|
ScsiPortWritePortUshort(
|
|
IN PUSHORT Port,
|
|
IN USHORT Value
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Write to the specificed port address.
|
|
|
|
Arguments:
|
|
|
|
Port - Supplies a pointer to the port address.
|
|
|
|
Value - Supplies the value to be written.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
#ifdef MIPS
|
|
|
|
WRITE_REGISTER_USHORT(Port, Value);
|
|
|
|
#else
|
|
|
|
WRITE_PORT_USHORT(Port, Value);
|
|
|
|
#endif
|
|
}
|
|
|
|
VOID
|
|
ScsiPortWritePortUlong(
|
|
IN PULONG Port,
|
|
IN ULONG Value
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Write to the specificed port address.
|
|
|
|
Arguments:
|
|
|
|
Port - Supplies a pointer to the port address.
|
|
|
|
Value - Supplies the value to be written.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
#ifdef MIPS
|
|
|
|
WRITE_REGISTER_ULONG(Port, Value);
|
|
|
|
#else
|
|
|
|
WRITE_PORT_ULONG(Port, Value);
|
|
|
|
#endif
|
|
}
|
|
|
|
VOID
|
|
ScsiPortWriteRegisterUchar(
|
|
IN PUCHAR Register,
|
|
IN UCHAR Value
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Write to the specificed register address.
|
|
|
|
Arguments:
|
|
|
|
Register - Supplies a pointer to the register address.
|
|
|
|
Value - Supplies the value to be written.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
WRITE_REGISTER_UCHAR(Register, Value);
|
|
|
|
}
|
|
|
|
VOID
|
|
ScsiPortWriteRegisterUshort(
|
|
IN PUSHORT Register,
|
|
IN USHORT Value
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Write to the specificed register address.
|
|
|
|
Arguments:
|
|
|
|
Register - Supplies a pointer to the register address.
|
|
|
|
Value - Supplies the value to be written.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
WRITE_REGISTER_USHORT(Register, Value);
|
|
|
|
}
|
|
|
|
VOID
|
|
ScsiPortWriteRegisterUlong(
|
|
IN PULONG Register,
|
|
IN ULONG Value
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Write to the specificed register address.
|
|
|
|
Arguments:
|
|
|
|
Register - Supplies a pointer to the register address.
|
|
|
|
Value - Supplies the value to be written.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
WRITE_REGISTER_ULONG(Register, Value);
|
|
|
|
}
|
|
|
|
VOID
|
|
ScsiPortWriteRegisterBufferUchar(
|
|
IN PUCHAR Register,
|
|
IN PUCHAR Buffer,
|
|
IN ULONG Count
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Write a buffer of unsigned bytes from the specified register address.
|
|
|
|
Arguments:
|
|
|
|
Register - Supplies a pointer to the port address.
|
|
Buffer - Supplies a pointer to the data buffer area.
|
|
Count - The count of items to move.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
WRITE_REGISTER_BUFFER_UCHAR(Register, Buffer, Count);
|
|
|
|
}
|
|
|
|
VOID
|
|
ScsiPortWriteRegisterBufferUshort(
|
|
IN PUSHORT Register,
|
|
IN PUSHORT Buffer,
|
|
IN ULONG Count
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Write a buffer of unsigned shorts from the specified register address.
|
|
|
|
Arguments:
|
|
|
|
Register - Supplies a pointer to the port address.
|
|
Buffer - Supplies a pointer to the data buffer area.
|
|
Count - The count of items to move.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
WRITE_REGISTER_BUFFER_USHORT(Register, Buffer, Count);
|
|
|
|
}
|
|
|
|
VOID
|
|
ScsiPortWriteRegisterBufferUlong(
|
|
IN PULONG Register,
|
|
IN PULONG Buffer,
|
|
IN ULONG Count
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Write a buffer of unsigned longs from the specified register address.
|
|
|
|
Arguments:
|
|
|
|
Register - Supplies a pointer to the port address.
|
|
Buffer - Supplies a pointer to the data buffer area.
|
|
Count - The count of items to move.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
WRITE_REGISTER_BUFFER_ULONG(Register, Buffer, Count);
|
|
|
|
}
|
|
|
|
SCSI_PHYSICAL_ADDRESS
|
|
ScsiPortConvertUlongToPhysicalAddress(
|
|
ULONG_PTR UlongAddress
|
|
)
|
|
|
|
{
|
|
SCSI_PHYSICAL_ADDRESS physicalAddress;
|
|
|
|
physicalAddress.QuadPart = UlongAddress;
|
|
return(physicalAddress);
|
|
}
|
|
|
|
#undef ScsiPortConvertPhysicalAddressToUlong
|
|
|
|
ULONG
|
|
ScsiPortConvertPhysicalAddressToUlong(
|
|
SCSI_PHYSICAL_ADDRESS Address
|
|
)
|
|
{
|
|
|
|
return(Address.LowPart);
|
|
}
|
|
|
|
|
|
|
|
PIRP
|
|
InitializeIrp(
|
|
PFULL_SCSI_REQUEST_BLOCK FullSrb,
|
|
CCHAR MajorFunction,
|
|
PVOID DeviceObject,
|
|
PVOID Buffer,
|
|
ULONG Length
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This funcition builds an IRP for use by the SCSI port driver and builds a
|
|
MDL list.
|
|
|
|
Arguments:
|
|
|
|
FullSrb - Supplies a pointer to the full srb structure which contains the
|
|
Irp and Mdl.
|
|
|
|
MajorFunction - Supplies the major function code to initialize the Irp
|
|
entry.
|
|
|
|
DeviceObject - Supplies the device Object pointer to initialize the Irp
|
|
with.
|
|
|
|
Buffer - Supplies the virual address of the buffer for which the
|
|
Mdl should be built.
|
|
|
|
Length - Supplies the size of buffer for which the Mdl should be built.
|
|
|
|
Return Value:
|
|
|
|
Returns a pointer to the initialized IRP.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIRP irp;
|
|
PMDL mdl;
|
|
|
|
irp = &FullSrb->Irp;
|
|
mdl = &FullSrb->Mdl;
|
|
|
|
irp->Tail.Overlay.CurrentStackLocation = &FullSrb->IrpStack[IRP_STACK_SIZE];
|
|
|
|
if (Buffer != NULL && Length != 0) {
|
|
|
|
//
|
|
// Build the memory descriptor list.
|
|
//
|
|
|
|
irp->MdlAddress = mdl;
|
|
mdl->Next = NULL;
|
|
mdl->Size = (CSHORT)(sizeof(MDL) +
|
|
ADDRESS_AND_SIZE_TO_SPAN_PAGES(Buffer, Length) * sizeof(ULONG));
|
|
mdl->StartVa = (PVOID)PAGE_ALIGN(Buffer);
|
|
mdl->ByteCount = Length;
|
|
mdl->ByteOffset = BYTE_OFFSET(Buffer);
|
|
mdl->MappedSystemVa = Buffer;
|
|
mdl->MdlFlags = MDL_MAPPED_TO_SYSTEM_VA;
|
|
ScsiPortInitializeMdlPages (mdl);
|
|
|
|
} else {
|
|
irp->MdlAddress = NULL;
|
|
}
|
|
|
|
return(irp);
|
|
}
|
|
|
|
PVOID
|
|
ScsiPortGetDeviceBase(
|
|
IN PVOID HwDeviceExtension,
|
|
IN INTERFACE_TYPE BusType,
|
|
IN ULONG SystemIoBusNumber,
|
|
SCSI_PHYSICAL_ADDRESS IoAddress,
|
|
ULONG NumberOfBytes,
|
|
BOOLEAN InMemorySpace
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine maps an IO address to system address space.
|
|
Use ScsiPortFreeDeviceBase to unmap address.
|
|
|
|
Arguments:
|
|
|
|
HwDeviceExtension - used to find port device extension.
|
|
BusType - what type of bus - eisa, mca, isa
|
|
SystemIoBusNumber - which IO bus (for machines with multiple buses).
|
|
IoAddress - base device address to be mapped.
|
|
NumberOfBytes - number of bytes for which address is valid.
|
|
|
|
Return Value:
|
|
|
|
Mapped address
|
|
|
|
--*/
|
|
|
|
{
|
|
PHYSICAL_ADDRESS cardAddress;
|
|
ULONG addressSpace = InMemorySpace;
|
|
PVOID mappedAddress;
|
|
|
|
if (!HalTranslateBusAddress(
|
|
BusType, // AdapterInterfaceType
|
|
SystemIoBusNumber, // SystemIoBusNumber
|
|
IoAddress, // Bus Address
|
|
&addressSpace, // AddressSpace
|
|
&cardAddress // Translated address
|
|
)) {
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Map the device base address into the virtual address space
|
|
// if the address is in memory space.
|
|
//
|
|
|
|
if (!addressSpace) {
|
|
|
|
mappedAddress = MmMapIoSpace(cardAddress,
|
|
NumberOfBytes,
|
|
FALSE);
|
|
|
|
|
|
} else {
|
|
|
|
mappedAddress = (PVOID)((ULONG_PTR)cardAddress.LowPart);
|
|
}
|
|
|
|
return mappedAddress;
|
|
|
|
} // end ScsiPortGetDeviceBase()
|
|
|
|
VOID
|
|
ScsiPortFreeDeviceBase(
|
|
IN PVOID HwDeviceExtension,
|
|
IN PVOID MappedAddress
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine unmaps an IO address that has been previously mapped
|
|
to system address space using ScsiPortGetDeviceBase().
|
|
|
|
Arguments:
|
|
|
|
HwDeviceExtension - used to find port device extension.
|
|
MappedAddress - address to unmap.
|
|
NumberOfBytes - number of bytes mapped.
|
|
InIoSpace - addresses in IO space don't get mapped.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
UNREFERENCED_PARAMETER(HwDeviceExtension);
|
|
UNREFERENCED_PARAMETER(MappedAddress);
|
|
|
|
return;
|
|
|
|
} // end ScsiPortFreeDeviceBase()
|
|
|
|
ARC_STATUS
|
|
GetAdapterCapabilities(
|
|
IN PDEVICE_OBJECT PortDeviceObject,
|
|
OUT PIO_SCSI_CAPABILITIES *PortCapabilities
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
Status is returned.
|
|
|
|
--*/
|
|
|
|
{
|
|
*PortCapabilities = &((PDEVICE_EXTENSION)PortDeviceObject->DeviceExtension)
|
|
->Capabilities;
|
|
|
|
return(ESUCCESS);
|
|
} // end GetAdapterCapabilities()
|
|
|
|
|
|
ARC_STATUS
|
|
GetInquiryData(
|
|
IN PDEVICE_OBJECT PortDeviceObject,
|
|
OUT PSCSI_CONFIGURATION_INFO *ConfigInfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine sends a request to a port driver to return
|
|
configuration information.
|
|
|
|
Arguments:
|
|
|
|
The address of the configuration information is returned in
|
|
the formal parameter ConfigInfo.
|
|
|
|
Return Value:
|
|
|
|
Status is returned.
|
|
|
|
--*/
|
|
{
|
|
*ConfigInfo = ((PDEVICE_EXTENSION)PortDeviceObject->DeviceExtension)
|
|
->ScsiInfo;
|
|
return(ESUCCESS);
|
|
} // end GetInquiryData()
|
|
|
|
NTSTATUS
|
|
SpInitializeConfiguration(
|
|
IN PDEVICE_EXTENSION DeviceExtension,
|
|
IN PHW_INITIALIZATION_DATA HwInitData,
|
|
OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo,
|
|
IN BOOLEAN InitialCall
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine initializes the port configuration information structure.
|
|
Any necessary information is extracted from the registery.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - Supplies the device extension.
|
|
|
|
HwInitializationData - Supplies the initial miniport data.
|
|
|
|
ConfigInfo - Supplies the configuration information to be
|
|
initialized.
|
|
|
|
InitialCall - Indicates that this is first call to this function.
|
|
If InitialCall is FALSE, then the perivous configuration information
|
|
is used to determine the new information.
|
|
|
|
Return Value:
|
|
|
|
Returns a status indicating the success or fail of the initializaiton.
|
|
|
|
--*/
|
|
|
|
{
|
|
#ifdef i386
|
|
extern ULONG MachineType;
|
|
#endif
|
|
|
|
ULONG j;
|
|
|
|
//
|
|
// If this is the initial call then zero the information and set
|
|
// the structure to the uninitialized values.
|
|
//
|
|
|
|
if (InitialCall) {
|
|
|
|
RtlZeroMemory(ConfigInfo, sizeof(PORT_CONFIGURATION_INFORMATION));
|
|
|
|
ConfigInfo->Length = sizeof(PORT_CONFIGURATION_INFORMATION);
|
|
ConfigInfo->AdapterInterfaceType = HwInitData->AdapterInterfaceType;
|
|
ConfigInfo->InterruptMode = Latched;
|
|
ConfigInfo->MaximumTransferLength = 0xffffffff;
|
|
// ConfigInfo->NumberOfPhysicalBreaks = 0x17;
|
|
ConfigInfo->NumberOfPhysicalBreaks = 0xffffffff;
|
|
ConfigInfo->DmaChannel = 0xffffffff;
|
|
ConfigInfo->NumberOfAccessRanges = HwInitData->NumberOfAccessRanges;
|
|
ConfigInfo->MaximumNumberOfTargets = 8;
|
|
|
|
#if defined(_MIPS_) || defined(_ALPHA_) || defined(_PPC_)
|
|
{
|
|
PCONFIGURATION_COMPONENT Component;
|
|
PCM_SCSI_DEVICE_DATA ScsiDeviceData;
|
|
UCHAR Buffer[sizeof(CM_PARTIAL_RESOURCE_LIST) +
|
|
(sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR) * 5) +
|
|
sizeof(CM_SCSI_DEVICE_DATA)];
|
|
PCM_PARTIAL_RESOURCE_LIST Descriptor = (PCM_PARTIAL_RESOURCE_LIST)&Buffer;
|
|
ULONG Count;
|
|
ULONG ScsiHostId;
|
|
|
|
if (((Component = ArcGetComponent("scsi(0)")) != NULL) &&
|
|
(Component->Class == AdapterClass) && (Component->Type == ScsiAdapter) &&
|
|
(ArcGetConfigurationData((PVOID)Descriptor, Component) == ESUCCESS) &&
|
|
((Count = Descriptor->Count) < 6)) {
|
|
|
|
ScsiDeviceData = (PCM_SCSI_DEVICE_DATA)&Descriptor->PartialDescriptors[Count];
|
|
|
|
if (ScsiDeviceData->HostIdentifier > 7) {
|
|
ScsiHostId = 7;
|
|
} else {
|
|
ScsiHostId = ScsiDeviceData->HostIdentifier;
|
|
}
|
|
} else {
|
|
ScsiHostId = 7;
|
|
}
|
|
|
|
for (j = 0; j < 8; j++) {
|
|
ConfigInfo->InitiatorBusId[j] = ScsiHostId;
|
|
}
|
|
}
|
|
|
|
#else
|
|
|
|
for (j = 0; j < 8; j++) {
|
|
ConfigInfo->InitiatorBusId[j] = ~0;
|
|
}
|
|
|
|
#endif
|
|
|
|
#if defined(i386)
|
|
switch (HwInitData->AdapterInterfaceType) {
|
|
case Isa:
|
|
if ((MachineType & 0xff) == MACHINE_TYPE_ISA) {
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
case Eisa:
|
|
if ((MachineType & 0xff) == MACHINE_TYPE_EISA) {
|
|
return(STATUS_SUCCESS);
|
|
} else {
|
|
return(STATUS_DEVICE_DOES_NOT_EXIST);
|
|
}
|
|
case MicroChannel:
|
|
if ((MachineType & 0xff) == MACHINE_TYPE_MCA) {
|
|
return(STATUS_SUCCESS);
|
|
} else {
|
|
return(STATUS_DEVICE_DOES_NOT_EXIST);
|
|
}
|
|
case PCIBus:
|
|
return(STATUS_SUCCESS);
|
|
default:
|
|
return(STATUS_DEVICE_DOES_NOT_EXIST);
|
|
}
|
|
#elif defined(_MIPS_)
|
|
if (HwInitData->AdapterInterfaceType != Internal) {
|
|
return(STATUS_DEVICE_DOES_NOT_EXIST);
|
|
}
|
|
#elif defined(_ALPHA_)
|
|
if ( (HwInitData->AdapterInterfaceType != Internal) &&
|
|
(HwInitData->AdapterInterfaceType != Eisa) &&
|
|
(HwInitData->AdapterInterfaceType != PCIBus) &&
|
|
(HwInitData->AdapterInterfaceType != Isa) ) {
|
|
return(STATUS_DEVICE_DOES_NOT_EXIST);
|
|
}
|
|
#elif defined(_PPC_)
|
|
if ( (HwInitData->AdapterInterfaceType != Internal) &&
|
|
(HwInitData->AdapterInterfaceType != Eisa) &&
|
|
(HwInitData->AdapterInterfaceType != Isa) ) {
|
|
return(STATUS_DEVICE_DOES_NOT_EXIST);
|
|
}
|
|
#endif
|
|
|
|
return(STATUS_SUCCESS);
|
|
|
|
} else {
|
|
|
|
return(STATUS_DEVICE_DOES_NOT_EXIST);
|
|
}
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SpGetCommonBuffer(
|
|
PDEVICE_EXTENSION DeviceExtension,
|
|
ULONG NonCachedExtensionSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine determines the required size of the common buffer. Allocates
|
|
the common buffer and finally sets up the srb extension zone. This routine
|
|
expects that the adapter object has already been allocated.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - Supplies a pointer to the device extension.
|
|
|
|
NonCachedExtensionSize - Supplies the size of the noncached device
|
|
extension for the mini-port driver.
|
|
|
|
Return Value:
|
|
|
|
Returns the status of the allocate operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
PHYSICAL_ADDRESS pAddress;
|
|
PVOID buffer;
|
|
ULONG length;
|
|
ULONG blockSize;
|
|
|
|
//
|
|
// Calculate the block size for the zone elements based on the Srb
|
|
// Extension.
|
|
//
|
|
|
|
blockSize = DeviceExtension->SrbExtensionSize;
|
|
|
|
//
|
|
// Last three bits of blocksize must be zero.
|
|
// Round blocksize up.
|
|
//
|
|
|
|
blockSize = (blockSize + 7) & ~7;
|
|
|
|
//
|
|
// Same for the noncached extension size.
|
|
//
|
|
|
|
NonCachedExtensionSize += 7;
|
|
NonCachedExtensionSize &= ~7;
|
|
|
|
length = NonCachedExtensionSize + blockSize * MINIMUM_SRB_EXTENSIONS;
|
|
|
|
//
|
|
// Round the length up to a page size, since HalGetCommonBuffer allocates
|
|
// in pages anyway.
|
|
//
|
|
|
|
length = (ULONG)ROUND_TO_PAGES(length);
|
|
|
|
//
|
|
// Allocate one page for noncached deviceextension
|
|
// and srbextension zoned pool.
|
|
//
|
|
|
|
if (DeviceExtension->DmaAdapterObject == NULL) {
|
|
|
|
//
|
|
// Since there is no adapter just allocate from non-paged pool.
|
|
//
|
|
|
|
if (buffer = MmAllocateNonCachedMemory(length)) {
|
|
DeviceExtension->PhysicalZoneBase = MmGetPhysicalAddress(buffer).LowPart;
|
|
}
|
|
|
|
} else {
|
|
#ifdef AXP_FIRMWARE
|
|
buffer = HalAllocateCommonBuffer(DeviceExtension->DmaAdapterObject,
|
|
length,
|
|
&pAddress,
|
|
FALSE );
|
|
DeviceExtension->PhysicalZoneBase = pAddress.LowPart;
|
|
#else
|
|
if (buffer = MmAllocateNonCachedMemory(length)) {
|
|
DeviceExtension->PhysicalZoneBase = MmGetPhysicalAddress(buffer).LowPart;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (buffer == NULL) {
|
|
return ENOMEM;
|
|
}
|
|
|
|
//
|
|
// Truncate Physical address to 32 bits.
|
|
//
|
|
// Determine length and starting address of zone.
|
|
// If noncached device extension required then
|
|
// subtract size from page leaving rest for zone.
|
|
//
|
|
|
|
length -= NonCachedExtensionSize;
|
|
|
|
DeviceExtension->NonCachedExtension = (PUCHAR)buffer + length;
|
|
DeviceExtension->NonCachedExtensionSize = NonCachedExtensionSize;
|
|
|
|
if (DeviceExtension->SrbExtensionSize) {
|
|
|
|
//
|
|
// Get block size.
|
|
//
|
|
|
|
blockSize = DeviceExtension->SrbExtensionSize;
|
|
|
|
//
|
|
// Record starting virtual address of zone.
|
|
//
|
|
|
|
DeviceExtension->SrbExtensionZonePool = buffer;
|
|
DeviceExtension->SrbExtensionPointer = buffer;
|
|
DeviceExtension->SrbExtensionSize = blockSize;
|
|
|
|
|
|
} else {
|
|
DeviceExtension->SrbExtensionZonePool = NULL;
|
|
}
|
|
|
|
return(ESUCCESS);
|
|
}
|
|
|
|
PVOID
|
|
ScsiPortGetUncachedExtension(
|
|
IN PVOID HwDeviceExtension,
|
|
IN PPORT_CONFIGURATION_INFORMATION ConfigInfo,
|
|
IN ULONG NumberOfBytes
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function allocates a common buffer to be used as the uncached device
|
|
extension for the mini-port driver. This function will also allocate any
|
|
required SRB extensions. The DmaAdapter is allocated if it has not been
|
|
allocated previously.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - Supplies a pointer to the mini-ports device extension.
|
|
|
|
ConfigInfo - Supplies a pointer to the partially initialized configuraiton
|
|
information. This is used to get an DMA adapter object.
|
|
|
|
NumberOfBytes - Supplies the size of the extension which needs to be
|
|
allocated
|
|
|
|
Return Value:
|
|
|
|
A pointer to the uncached device extension or NULL if the extension could
|
|
not be allocated or was previously allocated.
|
|
|
|
--*/
|
|
|
|
{
|
|
DEVICE_DESCRIPTION deviceDescription;
|
|
PDEVICE_EXTENSION deviceExtension =
|
|
((PDEVICE_EXTENSION) HwDeviceExtension) - 1;
|
|
NTSTATUS status;
|
|
ULONG numberOfPageBreaks;
|
|
|
|
//
|
|
// Make sure that an common buffer has not already been allocated.
|
|
//
|
|
|
|
if (deviceExtension->SrbExtensionZonePool != NULL) {
|
|
return(NULL);
|
|
}
|
|
|
|
if ( deviceExtension->DmaAdapterObject == NULL ) {
|
|
|
|
RtlZeroMemory( &deviceDescription, sizeof(DEVICE_DESCRIPTION) );
|
|
|
|
deviceDescription.Version = DEVICE_DESCRIPTION_VERSION;
|
|
deviceDescription.DmaChannel = ConfigInfo->DmaChannel;
|
|
deviceDescription.InterfaceType = ConfigInfo->AdapterInterfaceType;
|
|
deviceDescription.BusNumber = ConfigInfo->SystemIoBusNumber;
|
|
deviceDescription.DmaWidth = ConfigInfo->DmaWidth;
|
|
deviceDescription.DmaSpeed = ConfigInfo->DmaSpeed;
|
|
deviceDescription.DmaPort = ConfigInfo->DmaPort;
|
|
deviceDescription.Dma32BitAddresses = ConfigInfo->Dma32BitAddresses;
|
|
deviceDescription.MaximumLength = ConfigInfo->MaximumTransferLength;
|
|
deviceDescription.ScatterGather = ConfigInfo->ScatterGather;
|
|
deviceDescription.Master = ConfigInfo->Master;
|
|
deviceDescription.AutoInitialize = FALSE;
|
|
deviceDescription.DemandMode = FALSE;
|
|
|
|
if (ConfigInfo->MaximumTransferLength > 0x11000) {
|
|
|
|
deviceDescription.MaximumLength = 0x11000;
|
|
|
|
} else {
|
|
|
|
deviceDescription.MaximumLength = ConfigInfo->MaximumTransferLength;
|
|
|
|
}
|
|
|
|
deviceExtension->DmaAdapterObject = HalGetAdapter(
|
|
&deviceDescription,
|
|
&numberOfPageBreaks
|
|
);
|
|
|
|
//
|
|
// Set maximum number of page breaks.
|
|
//
|
|
|
|
if (numberOfPageBreaks > ConfigInfo->NumberOfPhysicalBreaks) {
|
|
deviceExtension->Capabilities.MaximumPhysicalPages =
|
|
ConfigInfo->NumberOfPhysicalBreaks;
|
|
} else {
|
|
deviceExtension->Capabilities.MaximumPhysicalPages =
|
|
numberOfPageBreaks;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Allocate the common buffer.
|
|
//
|
|
|
|
status = SpGetCommonBuffer( deviceExtension, NumberOfBytes);
|
|
|
|
if (status != ESUCCESS) {
|
|
return(NULL);
|
|
}
|
|
|
|
return(deviceExtension->NonCachedExtension);
|
|
}
|
|
|
|
ULONG
|
|
ScsiPortGetBusData(
|
|
IN PVOID DeviceExtension,
|
|
IN ULONG BusDataType,
|
|
IN ULONG SystemIoBusNumber,
|
|
IN ULONG SlotNumber,
|
|
IN PVOID Buffer,
|
|
IN ULONG Length
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The function returns the bus data for an adapter slot or CMOS address.
|
|
|
|
Arguments:
|
|
|
|
BusDataType - Supplies the type of bus.
|
|
|
|
BusNumber - Indicates which bus.
|
|
|
|
Buffer - Supplies the space to store the data.
|
|
|
|
Length - Supplies a count in bytes of the maximum amount to return.
|
|
|
|
Return Value:
|
|
|
|
Returns the amount of data stored into the buffer.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG DataLength = 0;
|
|
|
|
PDEVICE_EXTENSION deviceExtension =
|
|
(PDEVICE_EXTENSION) DeviceExtension - 1;
|
|
|
|
//
|
|
// If the length is non-zero, the the requested data.
|
|
//
|
|
|
|
if (Length != 0) {
|
|
|
|
ULONG ret;
|
|
|
|
ret = HalGetBusData( BusDataType,
|
|
SystemIoBusNumber,
|
|
SlotNumber,
|
|
Buffer,
|
|
Length
|
|
);
|
|
return ret;
|
|
}
|
|
|
|
//
|
|
// Free any previously allocated data.
|
|
//
|
|
|
|
if (deviceExtension->MapRegisterBase != NULL) {
|
|
ExFreePool(deviceExtension->MapRegisterBase);
|
|
}
|
|
|
|
if (BusDataType == EisaConfiguration) {
|
|
|
|
#if 0
|
|
//
|
|
// Deteremine the length to allocate based on the number of functions
|
|
// for the slot.
|
|
//
|
|
|
|
Length = HalGetBusData( BusDataType,
|
|
SystemIoBusNumber,
|
|
SlotNumber,
|
|
&slotInformation,
|
|
sizeof(CM_EISA_SLOT_INFORMATION));
|
|
|
|
|
|
if (Length < sizeof(CM_EISA_SLOT_INFORMATION)) {
|
|
|
|
//
|
|
// The data is messed up since this should never occur
|
|
//
|
|
|
|
DebugPrint((1, "ScsiPortGetBusData: Slot information not returned. Length = %d\n", Length));
|
|
return(0);
|
|
}
|
|
|
|
//
|
|
// Calculate the required length based on the number of functions.
|
|
//
|
|
|
|
Length = sizeof(CM_EISA_SLOT_INFORMATION) +
|
|
(sizeof(CM_EISA_FUNCTION_INFORMATION) * slotInformation.NumberFunctions);
|
|
|
|
#else
|
|
|
|
//
|
|
// Since the loader does not really support freeing data and the EISA
|
|
// configuration data can be very large. Hal get bus data has be changed
|
|
// to accept a length of zero for EIAS configuration data.
|
|
//
|
|
|
|
DataLength = HalGetBusData( BusDataType,
|
|
SystemIoBusNumber,
|
|
SlotNumber,
|
|
Buffer,
|
|
Length
|
|
);
|
|
|
|
DebugPrint((1, "ScsiPortGetBusData: Returning data. Length = %d\n", DataLength));
|
|
return(DataLength);
|
|
#endif
|
|
|
|
} else {
|
|
|
|
Length = PAGE_SIZE;
|
|
}
|
|
|
|
deviceExtension->MapRegisterBase = ExAllocatePool(NonPagedPool, Length);
|
|
|
|
if (deviceExtension->MapRegisterBase == NULL) {
|
|
DebugPrint((1, "ScsiPortGetBusData: Memory allocation failed. Length = %d\n", Length));
|
|
return(0);
|
|
}
|
|
|
|
//
|
|
// Return the pointer to the mini-port driver.
|
|
//
|
|
|
|
*((PVOID *)Buffer) = deviceExtension->MapRegisterBase;
|
|
|
|
DataLength = HalGetBusData( BusDataType,
|
|
SystemIoBusNumber,
|
|
SlotNumber,
|
|
deviceExtension->MapRegisterBase,
|
|
Length
|
|
);
|
|
|
|
return(DataLength);
|
|
}
|
|
|
|
PSCSI_REQUEST_BLOCK
|
|
ScsiPortGetSrb(
|
|
IN PVOID HwDeviceExtension,
|
|
IN UCHAR PathId,
|
|
IN UCHAR TargetId,
|
|
IN UCHAR Lun,
|
|
IN LONG QueueTag
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine retrieves an active SRB for a particuliar logical unit.
|
|
|
|
Arguments:
|
|
|
|
HwDeviceExtension
|
|
PathId, TargetId, Lun - identify logical unit on SCSI bus.
|
|
QueueTag - -1 indicates request is not tagged.
|
|
|
|
Return Value:
|
|
|
|
SRB, if one exists. Otherwise, NULL.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension =
|
|
((PDEVICE_EXTENSION) HwDeviceExtension) - 1;
|
|
PLOGICAL_UNIT_EXTENSION luExtension;
|
|
PIRP irp;
|
|
PIO_STACK_LOCATION irpstack;
|
|
|
|
|
|
luExtension = GetLogicalUnitExtension(deviceExtension, TargetId);
|
|
|
|
|
|
if (luExtension == NULL) {
|
|
return(NULL);
|
|
}
|
|
|
|
irp = luExtension->CurrentRequest;
|
|
irpstack = IoGetCurrentIrpStackLocation(irp);
|
|
return ((PSCSI_REQUEST_BLOCK)irpstack->Parameters.Others.Argument1);
|
|
|
|
} // end ScsiPortGetSrb()
|
|
|
|
BOOLEAN
|
|
ScsiPortValidateRange(
|
|
IN PVOID HwDeviceExtension,
|
|
IN INTERFACE_TYPE BusType,
|
|
IN ULONG SystemIoBusNumber,
|
|
IN SCSI_PHYSICAL_ADDRESS IoAddress,
|
|
IN ULONG NumberOfBytes,
|
|
IN BOOLEAN InIoSpace
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine should take an IO range and make sure that it is not already
|
|
in use by another adapter. This allows miniport drivers to probe IO where
|
|
an adapter could be, without worrying about messing up another card.
|
|
|
|
Arguments:
|
|
|
|
HwDeviceExtension - Used to find scsi managers internal structures
|
|
BusType - EISA, PCI, PC/MCIA, MCA, ISA, what?
|
|
SystemIoBusNumber - Which system bus?
|
|
IoAddress - Start of range
|
|
NumberOfBytes - Length of range
|
|
InIoSpace - Is range in IO space?
|
|
|
|
Return Value:
|
|
|
|
TRUE if range not claimed by another driver.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension =
|
|
((PDEVICE_EXTENSION) HwDeviceExtension) - 1;
|
|
|
|
//
|
|
// This is not implemented in NT.
|
|
//
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
VOID
|
|
ScsiPortReadPortBufferUchar(
|
|
IN PUCHAR Port,
|
|
IN PUCHAR Buffer,
|
|
IN ULONG Count
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Read a buffer of unsigned bytes from the specified port address.
|
|
|
|
Arguments:
|
|
|
|
Port - Supplies a pointer to the port address.
|
|
Buffer - Supplies a pointer to the data buffer area.
|
|
Count - The count of items to move.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
READ_PORT_BUFFER_UCHAR(Port, Buffer, Count);
|
|
|
|
}
|
|
|
|
VOID
|
|
ScsiPortReadPortBufferUshort(
|
|
IN PUSHORT Port,
|
|
IN PUSHORT Buffer,
|
|
IN ULONG Count
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Read a buffer of unsigned shorts from the specified port address.
|
|
|
|
Arguments:
|
|
|
|
Port - Supplies a pointer to the port address.
|
|
Buffer - Supplies a pointer to the data buffer area.
|
|
Count - The count of items to move.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
READ_PORT_BUFFER_USHORT(Port, Buffer, Count);
|
|
|
|
}
|
|
|
|
VOID
|
|
ScsiPortReadPortBufferUlong(
|
|
IN PULONG Port,
|
|
IN PULONG Buffer,
|
|
IN ULONG Count
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Read a buffer of unsigned longs from the specified port address.
|
|
|
|
Arguments:
|
|
|
|
Port - Supplies a pointer to the port address.
|
|
Buffer - Supplies a pointer to the data buffer area.
|
|
Count - The count of items to move.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
READ_PORT_BUFFER_ULONG(Port, Buffer, Count);
|
|
|
|
}
|
|
|
|
VOID
|
|
ScsiPortWritePortBufferUchar(
|
|
IN PUCHAR Port,
|
|
IN PUCHAR Buffer,
|
|
IN ULONG Count
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Write a buffer of unsigned bytes from the specified port address.
|
|
|
|
Arguments:
|
|
|
|
Port - Supplies a pointer to the port address.
|
|
Buffer - Supplies a pointer to the data buffer area.
|
|
Count - The count of items to move.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
WRITE_PORT_BUFFER_UCHAR(Port, Buffer, Count);
|
|
|
|
}
|
|
|
|
VOID
|
|
ScsiPortWritePortBufferUshort(
|
|
IN PUSHORT Port,
|
|
IN PUSHORT Buffer,
|
|
IN ULONG Count
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Write a buffer of unsigned shorts from the specified port address.
|
|
|
|
Arguments:
|
|
|
|
Port - Supplies a pointer to the port address.
|
|
Buffer - Supplies a pointer to the data buffer area.
|
|
Count - The count of items to move.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
WRITE_PORT_BUFFER_USHORT(Port, Buffer, Count);
|
|
|
|
}
|
|
|
|
VOID
|
|
ScsiPortWritePortBufferUlong(
|
|
IN PULONG Port,
|
|
IN PULONG Buffer,
|
|
IN ULONG Count
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Write a buffer of unsigned longs from the specified port address.
|
|
|
|
Arguments:
|
|
|
|
Port - Supplies a pointer to the port address.
|
|
Buffer - Supplies a pointer to the data buffer area.
|
|
Count - The count of items to move.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
WRITE_PORT_BUFFER_ULONG(Port, Buffer, Count);
|
|
|
|
}
|
|
|
|
VOID
|
|
ScsiPortQuerySystemTime(
|
|
OUT PLARGE_INTEGER Port
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Return a dummy system time to caller. This routine is present only to
|
|
satisfy scsi miniport drivers that require the export.
|
|
|
|
Arguments:
|
|
|
|
CurrentTime - Supplies a pointer to a data buffer into which
|
|
to copy the system time.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
Port->QuadPart = 0;
|
|
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
GetPciConfiguration(
|
|
PDRIVER_OBJECT DriverObject,
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PPORT_CONFIGURATION_INFORMATION ConfigInformation,
|
|
ULONG NumberOfAccessRanges,
|
|
PVOID RegistryPath,
|
|
BOOLEAN IsMultiFunction,
|
|
PULONG BusNumber,
|
|
PULONG SlotNumber,
|
|
PULONG FunctionNumber
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Uses the Bus/Slot/Function numbers provided and gets slot information for
|
|
the device and register with hal for the resources.
|
|
|
|
Arguments:
|
|
|
|
DriverObject - Miniport driver object.
|
|
DeviceObject - Represents this adapter.
|
|
ConfigInformation - Template for configuration information passed to a
|
|
miniport driver via the FindAdapter routine.
|
|
NumberOfAccessRanges - from the HwInitializationData provided by the
|
|
miniport
|
|
RegistryPath - Service key path.
|
|
IsMultiFunctionDevice - as returned by FindPciDevice.
|
|
BusNumber - PCI Bus number provided by FindPciDevice.
|
|
SlotNumber - Slot number provided by FindPciDevice.
|
|
FunctionNumber - FunctionNumber provided by FindPciDevice.
|
|
|
|
Return Value:
|
|
|
|
TRUE if card found. BusNumber and Slotnumber will return values that
|
|
should be used to continue the search for additional cards, when a card
|
|
is found.
|
|
|
|
--*/
|
|
|
|
{
|
|
PCI_SLOT_NUMBER slotData;
|
|
PPCI_COMMON_CONFIG pciData;
|
|
PCI_COMMON_CONFIG pciBuffer;
|
|
ULONG pciBus = *BusNumber;
|
|
ULONG slotNumber = *SlotNumber;
|
|
ULONG functionNumber = *FunctionNumber;
|
|
ULONG i;
|
|
ULONG length;
|
|
ULONG rangeNumber = 0;
|
|
PACCESS_RANGE accessRange;
|
|
BOOLEAN moreSlots = TRUE;
|
|
ULONG status;
|
|
PCM_RESOURCE_LIST resourceList;
|
|
PCM_PARTIAL_RESOURCE_DESCRIPTOR resourceDescriptor;
|
|
UNICODE_STRING unicodeString;
|
|
UCHAR vendorString[5];
|
|
UCHAR deviceString[5];
|
|
|
|
pciData = &pciBuffer;
|
|
|
|
//
|
|
//
|
|
// typedef struct _PCI_SLOT_NUMBER {
|
|
// union {
|
|
// struct {
|
|
// ULONG DeviceNumber:5;
|
|
// ULONG FunctionNumber:3;
|
|
// ULONG Reserved:24;
|
|
// } bits;
|
|
// ULONG AsULONG;
|
|
// } u;
|
|
// } PCI_SLOT_NUMBER, *PPCI_SLOT_NUMBER;
|
|
//
|
|
|
|
slotData.u.AsULONG = 0;
|
|
|
|
//
|
|
// Search each PCI bus.
|
|
//
|
|
|
|
//
|
|
// Look at each device.
|
|
//
|
|
|
|
slotData.u.bits.DeviceNumber = slotNumber;
|
|
slotData.u.bits.FunctionNumber = functionNumber;
|
|
|
|
//
|
|
// Look at each function.
|
|
//
|
|
|
|
length = HalGetBusDataByOffset(
|
|
PCIConfiguration,
|
|
pciBus,
|
|
slotData.u.AsULONG,
|
|
pciData,
|
|
0,
|
|
FIELD_OFFSET(PCI_COMMON_CONFIG, DeviceSpecific));
|
|
|
|
ASSERT(length != 0);
|
|
ASSERT(pciData->VendorID != PCI_INVALID_VENDORID);
|
|
|
|
//
|
|
// Translate hex ids to strings.
|
|
//
|
|
|
|
sprintf(vendorString, "%04x", pciData->VendorID);
|
|
sprintf(deviceString, "%04x", pciData->DeviceID);
|
|
|
|
DebugPrint((1,
|
|
"GetPciConfiguration: Bus %x Slot %x Function %x Vendor %s Product %s %s\n",
|
|
pciBus,
|
|
slotNumber,
|
|
functionNumber,
|
|
vendorString,
|
|
deviceString,
|
|
(IsMultiFunction ? "MF" : "")));
|
|
|
|
//
|
|
// This is the miniport drivers slot. Allocate the
|
|
// resources.
|
|
//
|
|
|
|
RtlInitUnicodeString(&unicodeString, L"ScsiAdapter");
|
|
|
|
status = HalAssignSlotResources(RegistryPath,
|
|
&unicodeString,
|
|
DriverObject,
|
|
DeviceObject,
|
|
PCIBus,
|
|
pciBus,
|
|
slotData.u.AsULONG,
|
|
&resourceList);
|
|
|
|
if(!NT_SUCCESS(status)) {
|
|
DebugPrint((0, "GetPciConfiguration: HalAssignSlotResources failed with %x\n", status));
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Walk resource list to update configuration information.
|
|
//
|
|
|
|
for (i = 0;
|
|
i < resourceList->List->PartialResourceList.Count;
|
|
i++) {
|
|
|
|
//
|
|
// Get resource descriptor.
|
|
//
|
|
|
|
resourceDescriptor =
|
|
&resourceList->List->PartialResourceList.PartialDescriptors[i];
|
|
|
|
//
|
|
// Check for interrupt descriptor.
|
|
//
|
|
|
|
if (resourceDescriptor->Type == CmResourceTypeInterrupt) {
|
|
ConfigInformation->BusInterruptLevel =
|
|
resourceDescriptor->u.Interrupt.Level;
|
|
ConfigInformation->BusInterruptVector =
|
|
resourceDescriptor->u.Interrupt.Vector;
|
|
|
|
//
|
|
// Check interrupt mode.
|
|
//
|
|
|
|
if ((resourceDescriptor->Flags ==
|
|
CM_RESOURCE_INTERRUPT_LATCHED)) {
|
|
ConfigInformation->InterruptMode = Latched;
|
|
} else if (resourceDescriptor->Flags ==
|
|
CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE) {
|
|
ConfigInformation->InterruptMode = LevelSensitive;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check for port descriptor.
|
|
//
|
|
|
|
if (resourceDescriptor->Type == CmResourceTypePort) {
|
|
|
|
//
|
|
// Verify range count does not exceed what the
|
|
// miniport indicated.
|
|
//
|
|
|
|
if (NumberOfAccessRanges > rangeNumber) {
|
|
|
|
//
|
|
// Get next access range.
|
|
//
|
|
|
|
accessRange =
|
|
&((*(ConfigInformation->AccessRanges))[rangeNumber]);
|
|
|
|
accessRange->RangeStart =
|
|
resourceDescriptor->u.Port.Start;
|
|
accessRange->RangeLength =
|
|
resourceDescriptor->u.Port.Length;
|
|
|
|
accessRange->RangeInMemory = FALSE;
|
|
rangeNumber++;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check for memory descriptor.
|
|
//
|
|
|
|
if (resourceDescriptor->Type == CmResourceTypeMemory) {
|
|
|
|
//
|
|
// Verify range count does not exceed what the
|
|
// miniport indicated.
|
|
//
|
|
|
|
if (NumberOfAccessRanges > rangeNumber) {
|
|
|
|
//
|
|
// Get next access range.
|
|
//
|
|
|
|
accessRange =
|
|
&((*(ConfigInformation->AccessRanges))[rangeNumber]);
|
|
|
|
accessRange->RangeStart =
|
|
resourceDescriptor->u.Memory.Start;
|
|
accessRange->RangeLength =
|
|
resourceDescriptor->u.Memory.Length;
|
|
|
|
accessRange->RangeInMemory = TRUE;
|
|
rangeNumber++;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check for DMA descriptor.
|
|
//
|
|
|
|
if (resourceDescriptor->Type == CmResourceTypeDma) {
|
|
ConfigInformation->DmaChannel =
|
|
resourceDescriptor->u.Dma.Channel;
|
|
ConfigInformation->DmaPort =
|
|
resourceDescriptor->u.Dma.Port;
|
|
}
|
|
|
|
} // next resource descriptor
|
|
|
|
ExFreePool(resourceList);
|
|
|
|
//
|
|
// Update bus and slot numbers.
|
|
//
|
|
|
|
*BusNumber = pciBus;
|
|
*SlotNumber = slotNumber;
|
|
|
|
if(IsMultiFunction) {
|
|
//
|
|
// Save away the next function number to check.
|
|
//
|
|
|
|
*FunctionNumber = functionNumber + 1;
|
|
} else {
|
|
//
|
|
// this isn't multifunction so make sure we loop around
|
|
// to the next one.
|
|
//
|
|
|
|
*FunctionNumber = PCI_MAX_FUNCTION;
|
|
}
|
|
|
|
ConfigInformation->SystemIoBusNumber = pciBus;
|
|
ConfigInformation->SlotNumber = slotData.u.AsULONG;
|
|
|
|
return TRUE;
|
|
|
|
} // GetPciConfiguration()
|
|
|
|
|
|
ULONG
|
|
ScsiPortSetBusDataByOffset(
|
|
IN PVOID DeviceExtension,
|
|
IN ULONG BusDataType,
|
|
IN ULONG SystemIoBusNumber,
|
|
IN ULONG SlotNumber,
|
|
IN PVOID Buffer,
|
|
IN ULONG Offset,
|
|
IN ULONG Length
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The function returns writes bus data to a specific offset within a slot.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - State information for a particular adapter.
|
|
|
|
BusDataType - Supplies the type of bus.
|
|
|
|
SystemIoBusNumber - Indicates which system IO bus.
|
|
|
|
SlotNumber - Indicates which slot.
|
|
|
|
Buffer - Supplies the data to write.
|
|
|
|
Offset - Byte offset to begin the write.
|
|
|
|
Length - Supplies a count in bytes of the maximum amount to return.
|
|
|
|
Return Value:
|
|
|
|
Number of bytes written.
|
|
|
|
--*/
|
|
|
|
{
|
|
return(HalSetBusDataByOffset(BusDataType,
|
|
SystemIoBusNumber,
|
|
SlotNumber,
|
|
Buffer,
|
|
Offset,
|
|
Length));
|
|
|
|
} // end ScsiPortSetBusDataByOffset()
|
|
|
|
|
|
BOOLEAN
|
|
FindPciDevice(
|
|
PHW_INITIALIZATION_DATA HwInitializationData,
|
|
PULONG BusNumber,
|
|
PULONG SlotNumber,
|
|
PULONG FunctionNumber,
|
|
PBOOLEAN IsMultiFunction
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Walk PCI slot information looking for Vendor and Product ID matches.
|
|
|
|
Arguments:
|
|
|
|
HwInitializationData - Miniport description.
|
|
BusNumber - Starting PCI bus for this search.
|
|
SlotNumber - Starting slot number for this search.
|
|
FunctionNumber - Starting function number for this search.
|
|
|
|
Return Value:
|
|
|
|
TRUE if card found.
|
|
|
|
Bus, Slot and Function number will contain the address of the adapter
|
|
found once this routine completes. These values should be provided to
|
|
GetPciConfiguration.
|
|
|
|
--*/
|
|
|
|
{
|
|
PCI_SLOT_NUMBER slotData;
|
|
PPCI_COMMON_CONFIG pciData;
|
|
PCI_COMMON_CONFIG pciBuffer;
|
|
ULONG pciBus;
|
|
ULONG slotNumber;
|
|
ULONG functionNumber;
|
|
ULONG i;
|
|
ULONG length;
|
|
BOOLEAN moreSlots = TRUE;
|
|
ULONG status;
|
|
UCHAR vendorString[5];
|
|
UCHAR deviceString[5];
|
|
|
|
pciData = &pciBuffer;
|
|
|
|
//
|
|
//
|
|
// typedef struct _PCI_SLOT_NUMBER {
|
|
// union {
|
|
// struct {
|
|
// ULONG DeviceNumber:5;
|
|
// ULONG FunctionNumber:3;
|
|
// ULONG Reserved:24;
|
|
// } bits;
|
|
// ULONG AsULONG;
|
|
// } u;
|
|
// } PCI_SLOT_NUMBER, *PPCI_SLOT_NUMBER;
|
|
//
|
|
|
|
slotData.u.AsULONG = 0;
|
|
|
|
//
|
|
// Search each PCI bus.
|
|
//
|
|
|
|
for (pciBus = *BusNumber; moreSlots && pciBus < 256; pciBus++) {
|
|
|
|
//
|
|
// Look at each device.
|
|
//
|
|
|
|
for (slotNumber = *SlotNumber;
|
|
moreSlots && slotNumber < PCI_MAX_DEVICES;
|
|
slotNumber++) {
|
|
|
|
slotData.u.bits.DeviceNumber = slotNumber;
|
|
*IsMultiFunction = FALSE;
|
|
|
|
//
|
|
// Look at each function.
|
|
//
|
|
|
|
for (functionNumber = *FunctionNumber;
|
|
moreSlots && functionNumber < PCI_MAX_FUNCTION;
|
|
functionNumber++) {
|
|
|
|
slotData.u.bits.FunctionNumber = functionNumber;
|
|
|
|
length = HalGetBusDataByOffset(
|
|
PCIConfiguration,
|
|
pciBus,
|
|
slotData.u.AsULONG,
|
|
pciData,
|
|
0,
|
|
FIELD_OFFSET(PCI_COMMON_CONFIG, DeviceSpecific));
|
|
|
|
if (length == 0) {
|
|
|
|
//
|
|
// Out of PCI buses, all done.
|
|
//
|
|
|
|
moreSlots = FALSE;
|
|
break;
|
|
}
|
|
|
|
if (pciData->VendorID == PCI_INVALID_VENDORID) {
|
|
if(*IsMultiFunction) {
|
|
//
|
|
// Of course function numbers may be sparse - keep
|
|
// checking anyway.
|
|
//
|
|
continue;
|
|
} else {
|
|
//
|
|
// But since this isn't a multifunction card there's
|
|
// nothing else to check in this slot. Or if the
|
|
// function is zero then it's not MF.
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
|
|
if((slotData.u.bits.FunctionNumber == 0) &&
|
|
PCI_MULTIFUNCTION_DEVICE(pciData)) {
|
|
*IsMultiFunction = TRUE;
|
|
}
|
|
|
|
//
|
|
// Translate hex ids to strings.
|
|
//
|
|
|
|
sprintf(vendorString, "%04x", pciData->VendorID);
|
|
sprintf(deviceString, "%04x", pciData->DeviceID);
|
|
|
|
DebugPrint((1,
|
|
"FindPciDevice: Bus %x Slot %x Function %x Vendor %s Product %s %s\n",
|
|
pciBus,
|
|
slotNumber,
|
|
functionNumber,
|
|
vendorString,
|
|
deviceString,
|
|
(*IsMultiFunction ? "MF" : "")));
|
|
|
|
//
|
|
// Compare strings.
|
|
//
|
|
|
|
if (_strnicmp(vendorString,
|
|
HwInitializationData->VendorId,
|
|
HwInitializationData->VendorIdLength) ||
|
|
_strnicmp(deviceString,
|
|
HwInitializationData->DeviceId,
|
|
HwInitializationData->DeviceIdLength)) {
|
|
|
|
//
|
|
// Not our PCI device. Try next device/function
|
|
//
|
|
|
|
if(*IsMultiFunction) {
|
|
// check next function.
|
|
continue;
|
|
} else {
|
|
// check next slot.
|
|
break;
|
|
}
|
|
}
|
|
|
|
*BusNumber = pciBus;
|
|
*SlotNumber = slotNumber;
|
|
*FunctionNumber = functionNumber;
|
|
|
|
return TRUE;
|
|
|
|
} // next PCI function
|
|
|
|
*FunctionNumber = 0;
|
|
|
|
} // next PCI slot
|
|
|
|
*SlotNumber = 0;
|
|
|
|
} // next PCI bus
|
|
|
|
return FALSE;
|
|
|
|
} // GetPciConfiguration()
|
|
|
|
|
|
|
|
VOID
|
|
SpGetSupportedAdapterControlFunctions(
|
|
PDEVICE_EXTENSION Adapter
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will query the miniport to determine which adapter control
|
|
types are supported for the specified adapter. The
|
|
SupportedAdapterControlBitmap in the adapter extension will be updated with
|
|
the data returned by the miniport. These flags are used to determine
|
|
what functionality (for power management and such) the miniport will support
|
|
|
|
Arguments:
|
|
|
|
Adapter - the adapter to query
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
|
|
{
|
|
UCHAR buffer[sizeof(SCSI_SUPPORTED_CONTROL_TYPE_LIST) +
|
|
(sizeof(BOOLEAN) * (ScsiAdapterControlMax + 1))];
|
|
|
|
PSCSI_SUPPORTED_CONTROL_TYPE_LIST typeList =
|
|
(PSCSI_SUPPORTED_CONTROL_TYPE_LIST) &buffer;
|
|
|
|
SCSI_ADAPTER_CONTROL_STATUS status;
|
|
|
|
if(Adapter->HwAdapterControl == NULL) {
|
|
|
|
//
|
|
// Adapter control is not supported by the miniport or the miniport
|
|
// isn't pnp (in which case it's not supported by scsiport) - the
|
|
// supported array has already been cleared so we can just quit now.
|
|
//
|
|
return;
|
|
}
|
|
|
|
RtlZeroMemory(typeList, (sizeof(SCSI_SUPPORTED_CONTROL_TYPE_LIST) +
|
|
sizeof(BOOLEAN) * (ScsiAdapterControlMax + 1)));
|
|
|
|
typeList->MaxControlType = ScsiAdapterControlMax;
|
|
|
|
#if DBG
|
|
typeList->SupportedTypeList[ScsiAdapterControlMax] = 0x63;
|
|
#endif
|
|
|
|
status = SpCallAdapterControl(Adapter,
|
|
ScsiQuerySupportedControlTypes,
|
|
typeList);
|
|
|
|
if(status == ScsiAdapterControlSuccess) {
|
|
ULONG i;
|
|
|
|
Adapter->HasShutdown = typeList->SupportedTypeList[ScsiStopAdapter];
|
|
Adapter->HasSetBoot = typeList->SupportedTypeList[ScsiSetBootConfig];
|
|
}
|
|
return;
|
|
}
|
|
|
|
SCSI_ADAPTER_CONTROL_STATUS
|
|
SpCallAdapterControl(
|
|
IN PDEVICE_EXTENSION Adapter,
|
|
IN SCSI_ADAPTER_CONTROL_TYPE ControlType,
|
|
IN PVOID Parameters
|
|
)
|
|
{
|
|
DebugPrint((2, "SpCallAdapterControl: Calling adapter control %x for adapter %#08lx with param %#08lx\n", ControlType, Adapter, Parameters));
|
|
return Adapter->HwAdapterControl(
|
|
Adapter->HwDeviceExtension,
|
|
ControlType,
|
|
Parameters);
|
|
}
|
|
|
|
|
|
VOID
|
|
SpUnload(
|
|
IN PDRIVER_OBJECT DriverObject
|
|
)
|
|
{
|
|
ULONG i;
|
|
|
|
for(i = 0; i < MAXIMUM_NUMBER_OF_SCSIPORT_OBJECTS; i++) {
|
|
PDEVICE_OBJECT deviceObject;
|
|
|
|
deviceObject = ScsiPortDeviceObject[i];
|
|
|
|
if(deviceObject != NULL) {
|
|
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
|
|
deviceExtension = deviceObject->DeviceExtension;
|
|
|
|
if(deviceExtension->HasShutdown) {
|
|
SpCallAdapterControl(deviceExtension, ScsiStopAdapter, NULL);
|
|
|
|
if(deviceExtension->HasSetBoot) {
|
|
SpCallAdapterControl(deviceExtension,
|
|
ScsiSetBootConfig,
|
|
NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now that we've shut this one down we can't use it anymore.
|
|
// Since the memory will be reclaimed by the OS we can just throw it
|
|
// away.
|
|
//
|
|
|
|
ScsiPortDeviceObject[i] = NULL;
|
|
}
|
|
return;
|
|
}
|
|
#endif /* DECSTATION */
|
|
|