mirror of https://github.com/lianthony/NT4.0
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.
5086 lines
140 KiB
5086 lines
140 KiB
/*++
|
|
|
|
Copyright (c) 1990 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
port.c
|
|
|
|
Abstract:
|
|
|
|
This is the NT SCSI port driver. This file contains the initialization
|
|
code.
|
|
|
|
Authors:
|
|
|
|
Mike Glass
|
|
Jeff Havens
|
|
|
|
Environment:
|
|
|
|
kernel mode only
|
|
|
|
Notes:
|
|
|
|
This module is a driver dll for scsi miniports.
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "port.h"
|
|
|
|
UCHAR MaxLuCount = SCSI_MAXIMUM_LOGICAL_UNITS;
|
|
|
|
VOID
|
|
SpBuildConfiguration(
|
|
IN PHW_INITIALIZATION_DATA HwInitializationData,
|
|
IN PCM_FULL_RESOURCE_DESCRIPTOR ControllerData,
|
|
IN PPORT_CONFIGURATION_INFORMATION ConfigInformation
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, ScsiPortInitialize)
|
|
#pragma alloc_text(PAGE, SpGetCommonBuffer)
|
|
#pragma alloc_text(PAGE, SpInitializeConfiguration)
|
|
#pragma alloc_text(PAGE, SpDeviceCleanup)
|
|
#pragma alloc_text(PAGE, SpBuildResourceList)
|
|
#pragma alloc_text(PAGE, SpParseDevice)
|
|
#pragma alloc_text(PAGE, SpConfiguarionCallout)
|
|
#pragma alloc_text(PAGE, CreateLogicalUnitExtension)
|
|
#pragma alloc_text(PAGE, IssueInquiry)
|
|
#pragma alloc_text(PAGE, ScsiBusScan)
|
|
#pragma alloc_text(PAGE, SpBuildDeviceMap)
|
|
#pragma alloc_text(PAGE, SpCreateNumericKey)
|
|
#pragma alloc_text(PAGE, GetPciConfiguration)
|
|
#pragma alloc_text(PAGE, SpBuildConfiguration)
|
|
#pragma alloc_text(PAGE, GetPcmciaConfiguration)
|
|
#endif
|
|
|
|
|
|
ULONG
|
|
ScsiPortInitialize(
|
|
IN PVOID Argument1,
|
|
IN PVOID Argument2,
|
|
IN PHW_INITIALIZATION_DATA HwInitializationData,
|
|
IN PVOID HwContext OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
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;
|
|
ULONG slotNumber = 0;
|
|
ULONG functionNumber = 0;
|
|
PDEVICE_EXTENSION deviceExtension = NULL;
|
|
NTSTATUS returnStatus = STATUS_DEVICE_DOES_NOT_EXIST;
|
|
NTSTATUS status;
|
|
STRING deviceName;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
UNICODE_STRING unicodeString;
|
|
UNICODE_STRING dosUnicodeString;
|
|
PDEVICE_OBJECT deviceObject;
|
|
PSRB_DATA srbData;
|
|
ULONG extensionAllocationSize;
|
|
ULONG uniqueId;
|
|
ULONG findStatus;
|
|
ULONG numberOfMapRegisters;
|
|
ULONG vector,
|
|
vector2;
|
|
ULONG count;
|
|
PULONG scsiPortNumber;
|
|
CCHAR deviceNameBuffer[256];
|
|
UCHAR scsiBus;
|
|
BOOLEAN interruptSharable;
|
|
BOOLEAN callAgain;
|
|
BOOLEAN initialCall;
|
|
BOOLEAN conflict;
|
|
KAFFINITY affinity,
|
|
affinity2;
|
|
KIRQL irql,
|
|
irql2,
|
|
syncIrql;
|
|
PCM_RESOURCE_LIST resourceList;
|
|
PPORT_CONFIGURATION_INFORMATION configInfo;
|
|
PORT_CONFIGURATION_INFORMATION originalConfigInfo;
|
|
PCONFIGURATION_INFORMATION configurationInformation;
|
|
PIO_SCSI_CAPABILITIES capabilities;
|
|
PIO_ERROR_LOG_PACKET errorLogEntry;
|
|
CONFIGURATION_CONTEXT configurationContext;
|
|
DEVICE_DESCRIPTION deviceDescription;
|
|
|
|
//
|
|
// Check that the length of this structure is equal to or less than
|
|
// what the port driver expects it to be. This is effectively a
|
|
// version check.
|
|
//
|
|
|
|
if (HwInitializationData->HwInitializationDataSize > sizeof(HW_INITIALIZATION_DATA)) {
|
|
|
|
DebugPrint((0,"ScsiPortInitialize: Miniport driver wrong version\n"));
|
|
return (ULONG) STATUS_REVISION_MISMATCH;
|
|
}
|
|
|
|
//
|
|
// Check that each required entry is not NULL.
|
|
//
|
|
|
|
if ((!HwInitializationData->HwInitialize) ||
|
|
(!HwInitializationData->HwFindAdapter) ||
|
|
(!HwInitializationData->HwStartIo) ||
|
|
(!HwInitializationData->HwResetBus)) {
|
|
|
|
DebugPrint((0,
|
|
"ScsiPortInitialize: Miniport driver missing required entry\n"));
|
|
|
|
return (ULONG) STATUS_REVISION_MISMATCH;
|
|
}
|
|
|
|
//
|
|
// Get the configuration information
|
|
//
|
|
|
|
configurationInformation = IoGetConfigurationInformation();
|
|
scsiPortNumber = &configurationInformation->ScsiPortCount;
|
|
|
|
//
|
|
// Set up the device driver entry points.
|
|
//
|
|
|
|
driverObject->DriverStartIo = ScsiPortStartIo;
|
|
|
|
driverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = ScsiPortDispatch;
|
|
driverObject->MajorFunction[IRP_MJ_SCSI] = ScsiPortDispatch;
|
|
driverObject->MajorFunction[IRP_MJ_CREATE] = ScsiPortCreateClose;
|
|
driverObject->MajorFunction[IRP_MJ_CLOSE] = ScsiPortCreateClose;
|
|
driverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ScsiPortDeviceControl;
|
|
|
|
//
|
|
// Initialize the configuration context.
|
|
//
|
|
|
|
RtlZeroMemory(&configurationContext, sizeof(CONFIGURATION_CONTEXT));
|
|
|
|
//
|
|
// Allocate an access range array.
|
|
//
|
|
|
|
if (HwInitializationData->NumberOfAccessRanges != 0) {
|
|
|
|
configurationContext.AccessRanges = ExAllocatePool(PagedPool,
|
|
HwInitializationData->NumberOfAccessRanges * sizeof(ACCESS_RANGE));
|
|
|
|
if (configurationContext.AccessRanges == NULL) {
|
|
return (ULONG) STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Open the service node.
|
|
//
|
|
|
|
InitializeObjectAttributes(&objectAttributes,
|
|
Argument2,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
(PSECURITY_DESCRIPTOR) NULL);
|
|
|
|
status = ZwOpenKey(&configurationContext.ServiceKey,
|
|
KEY_READ,
|
|
&objectAttributes);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
DebugPrint((1, "ScsiPortInitialize: Cannot open sevice node for driver. Name: %WS Status: %lx\n",
|
|
Argument2, status));
|
|
configurationContext.ServiceKey = NULL;
|
|
}
|
|
|
|
//
|
|
// Try to open the parameters key. It it exists then replace the service
|
|
// key with the new key. This allows the Device nodes to be placed
|
|
// under DriverName\Parameters\Deivce? or DriverName\Device?
|
|
//
|
|
|
|
if (configurationContext.ServiceKey != NULL) {
|
|
|
|
//
|
|
// Check for a Device node. The device node applies to every device.
|
|
//
|
|
|
|
RtlInitUnicodeString(&unicodeString, L"Parameters");
|
|
InitializeObjectAttributes(&objectAttributes,
|
|
&unicodeString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
configurationContext.ServiceKey,
|
|
(PSECURITY_DESCRIPTOR) NULL);
|
|
//
|
|
// Attempt to open the parameters key.
|
|
//
|
|
|
|
status = ZwOpenKey(&configurationContext.DeviceKey,
|
|
KEY_READ,
|
|
&objectAttributes);
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// There is a Parameters key. Use that instead of the service
|
|
// node key. Close the service node and set the new value.
|
|
//
|
|
|
|
ZwClose(configurationContext.ServiceKey);
|
|
configurationContext.ServiceKey = configurationContext.DeviceKey;
|
|
configurationContext.DeviceKey = NULL;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check for a Device node. The device node applies to every device.
|
|
//
|
|
|
|
RtlInitUnicodeString(&unicodeString, L"Device");
|
|
InitializeObjectAttributes(&objectAttributes,
|
|
&unicodeString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
configurationContext.ServiceKey,
|
|
(PSECURITY_DESCRIPTOR) NULL);
|
|
//
|
|
// It doesn't matter if this call fails or not. If it fails, then there
|
|
// is no default device node. If it works, then the handle will be set.
|
|
//
|
|
|
|
ZwOpenKey(&configurationContext.DeviceKey,
|
|
KEY_READ,
|
|
&objectAttributes);
|
|
|
|
//
|
|
// Set last adapter number to an uninitialized value.
|
|
//
|
|
|
|
configurationContext.LastAdapterNumber = SP_UNINITIALIZED_VALUE;
|
|
|
|
//
|
|
// Determine size of extensions.
|
|
//
|
|
|
|
extensionAllocationSize =
|
|
DEVICE_EXTENSION_SIZE + HwInitializationData->DeviceExtensionSize;
|
|
|
|
initialCall = TRUE;
|
|
callAgain = FALSE;
|
|
|
|
//
|
|
// Keep calling the miniport's find adapter routine until the miniport
|
|
// indicates it is done and there is no more configuartion information.
|
|
// The loop is terminated when the SpInitializeConfiguration routine
|
|
// indicates there is no more configuration information or an error occurs.
|
|
//
|
|
|
|
while (TRUE) {
|
|
|
|
//
|
|
// Clear the deviceExtension.
|
|
//
|
|
|
|
deviceExtension = NULL;
|
|
|
|
//
|
|
// Create port driver name.
|
|
//
|
|
|
|
sprintf(deviceNameBuffer, "\\Device\\ScsiPort%d", *scsiPortNumber);
|
|
RtlInitString(&deviceName, deviceNameBuffer);
|
|
status = RtlAnsiStringToUnicodeString(&unicodeString,
|
|
&deviceName,
|
|
TRUE);
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
uniqueId = 260;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Create a device object to represent the SCSI Adapter.
|
|
//
|
|
|
|
status = IoCreateDevice(driverObject,
|
|
extensionAllocationSize,
|
|
&unicodeString,
|
|
FILE_DEVICE_CONTROLLER,
|
|
0,
|
|
FALSE,
|
|
&deviceObject);
|
|
RtlFreeUnicodeString(&unicodeString);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
DebugPrint((1,"ScsiPortInitialize: Could not create device object\n"));
|
|
deviceExtension = NULL;
|
|
uniqueId = 261;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Set up device extension pointers
|
|
//
|
|
|
|
deviceExtension = deviceObject->DeviceExtension;
|
|
deviceExtension->DeviceObject = deviceObject;
|
|
deviceExtension->PortNumber = *scsiPortNumber;
|
|
|
|
//
|
|
// Save the dependent driver routines in the device extension.
|
|
//
|
|
|
|
deviceExtension->HwInitialize = HwInitializationData->HwInitialize;
|
|
deviceExtension->HwStartIo = HwInitializationData->HwStartIo;
|
|
deviceExtension->HwInterrupt = HwInitializationData->HwInterrupt;
|
|
deviceExtension->HwResetBus = HwInitializationData->HwResetBus;
|
|
deviceExtension->HwDmaStarted = HwInitializationData->HwDmaStarted;
|
|
deviceExtension->HwLogicalUnitExtensionSize =
|
|
HwInitializationData->SpecificLuExtensionSize;
|
|
deviceExtension->HwDeviceExtension = (PVOID)(deviceExtension + 1);
|
|
|
|
//
|
|
// 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 this memory. Round the size
|
|
// a multiple of quadwords
|
|
//
|
|
|
|
deviceExtension->SrbExtensionSize = ~(sizeof(LONGLONG) - 1) &
|
|
(HwInitializationData->SrbExtensionSize + sizeof(LONGLONG) - 1);
|
|
|
|
//
|
|
// Initialize the maximum lu count variable.
|
|
//
|
|
|
|
deviceExtension->MaxLuCount = MaxLuCount;
|
|
|
|
deviceExtension->NumberOfRequests = MINIMUM_SRB_EXTENSIONS;
|
|
|
|
//
|
|
// Allocate spin lock for critical sections.
|
|
//
|
|
|
|
KeInitializeSpinLock(&deviceExtension->SpinLock);
|
|
|
|
//
|
|
// Spin lock to sync. multiple IRQ's (PCI IDE).
|
|
//
|
|
|
|
KeInitializeSpinLock(&deviceExtension->MultipleIrqSpinLock);
|
|
|
|
|
|
//
|
|
// Initialize DPC routine.
|
|
//
|
|
|
|
IoInitializeDpcRequest(deviceObject, ScsiPortCompletionDpc);
|
|
|
|
//
|
|
// Initialize the port timeout counter.
|
|
//
|
|
|
|
deviceExtension->PortTimeoutCounter = PD_TIMER_STOPPED;
|
|
|
|
//
|
|
// Initialize timer.
|
|
//
|
|
|
|
IoInitializeTimer(deviceObject, ScsiPortTickHandler, NULL);
|
|
|
|
//
|
|
// Initialize miniport timer and timer DPC.
|
|
//
|
|
|
|
KeInitializeTimer(&deviceExtension->MiniPortTimer);
|
|
KeInitializeDpc(&deviceExtension->MiniPortTimerDpc,
|
|
SpMiniPortTimerDpc,
|
|
deviceObject );
|
|
|
|
NewConfiguration:
|
|
|
|
//
|
|
// Initialize the miniport config info buffer.
|
|
//
|
|
|
|
status = SpInitializeConfiguration(deviceExtension,
|
|
HwInitializationData,
|
|
&configurationContext,
|
|
&originalConfigInfo,
|
|
initialCall);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
uniqueId = 263;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Allocate a configuration structure and access ranges for the
|
|
// miniport drivers to use.
|
|
//
|
|
|
|
configInfo = ExAllocatePool(NonPagedPool,
|
|
(sizeof(PORT_CONFIGURATION_INFORMATION) +
|
|
HwInitializationData->NumberOfAccessRanges *
|
|
sizeof(ACCESS_RANGE) + 7) & ~7);
|
|
|
|
if (configInfo == NULL) {
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
uniqueId = 264;
|
|
break;
|
|
}
|
|
|
|
deviceExtension->ConfigurationInformation = configInfo;
|
|
|
|
//
|
|
// Copy the current structure to the writable copy
|
|
//
|
|
|
|
RtlCopyMemory(configInfo,
|
|
&originalConfigInfo,
|
|
sizeof(PORT_CONFIGURATION_INFORMATION));
|
|
|
|
//
|
|
// Copy the SrbExtensionSize from deviceExtension to ConfigInfo.
|
|
// A check will be made later to determine if the miniport
|
|
// updated this value.
|
|
//
|
|
|
|
configInfo->SrbExtensionSize = deviceExtension->SrbExtensionSize;
|
|
configInfo->SpecificLuExtensionSize = deviceExtension->HwLogicalUnitExtensionSize;
|
|
|
|
//
|
|
// Initialize the access range array.
|
|
//
|
|
|
|
if (HwInitializationData->NumberOfAccessRanges != 0) {
|
|
|
|
configInfo->AccessRanges = (PVOID) (configInfo+1);
|
|
|
|
//
|
|
// Quadword align this.
|
|
//
|
|
|
|
(ULONG)(configInfo->AccessRanges) += 7;
|
|
(ULONG)(configInfo->AccessRanges) &= ~7;
|
|
|
|
RtlCopyMemory(configInfo->AccessRanges,
|
|
configurationContext.AccessRanges,
|
|
HwInitializationData->NumberOfAccessRanges * sizeof(ACCESS_RANGE));
|
|
}
|
|
|
|
//
|
|
// If PCI bus initialize configuration information with slot information.
|
|
//
|
|
|
|
if (HwInitializationData->AdapterInterfaceType == PCIBus &&
|
|
HwInitializationData->VendorIdLength > 0 &&
|
|
HwInitializationData->DeviceIdLength > 0 &&
|
|
HwInitializationData->DeviceId &&
|
|
HwInitializationData->VendorId) {
|
|
|
|
configInfo->BusInterruptLevel = 0;
|
|
if (!GetPciConfiguration(driverObject,
|
|
deviceObject,
|
|
HwInitializationData,
|
|
configInfo,
|
|
Argument2,
|
|
configurationContext.BusNumber,
|
|
&slotNumber,
|
|
&functionNumber)) {
|
|
|
|
//
|
|
// Adapter not found. Continue search with next bus.
|
|
//
|
|
|
|
configurationContext.BusNumber++;
|
|
deviceExtension->ConfigurationInformation = NULL;
|
|
ExFreePool(configInfo);
|
|
callAgain = FALSE;
|
|
goto NewConfiguration;
|
|
|
|
}
|
|
|
|
if (!configInfo->BusInterruptLevel) {
|
|
|
|
//
|
|
// No interrupt was assigned - skip this slot and call again
|
|
//
|
|
|
|
deviceExtension->ConfigurationInformation = NULL;
|
|
ExFreePool(configInfo);
|
|
goto NewConfiguration;
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// Check for PCMCIA enabled adapters. All miniports pass
|
|
// through the same initialization structure, but change the
|
|
// bus type. Searching for PCMCIA adapters should only
|
|
// happen once regardless of bus type.
|
|
//
|
|
|
|
if (!HwInitializationData->ReservedUshort) {
|
|
HwInitializationData->ReservedUshort = 1;
|
|
|
|
deviceExtension->PCCard = GetPcmciaConfiguration(Argument2, // service key
|
|
HwInitializationData,
|
|
configInfo);
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get the miniport configuration information.
|
|
//
|
|
|
|
callAgain = FALSE;
|
|
findStatus = HwInitializationData->HwFindAdapter(deviceExtension->HwDeviceExtension,
|
|
HwContext,
|
|
NULL, // BusInformation
|
|
configurationContext.Parameter,
|
|
configInfo,
|
|
&callAgain);
|
|
//
|
|
// Check to see if an error was logged.
|
|
//
|
|
|
|
if (deviceExtension->InterruptData.InterruptFlags & PD_LOG_ERROR) {
|
|
|
|
deviceExtension->InterruptData.InterruptFlags &=
|
|
~(PD_LOG_ERROR | PD_NOTIFICATION_REQUIRED);
|
|
|
|
LogErrorEntry(deviceExtension,
|
|
&deviceExtension->InterruptData.LogEntry);
|
|
}
|
|
|
|
//
|
|
// Free the pointer to the bus data at map register base. This was
|
|
// allocated by ScsiPortGetBusData.
|
|
//
|
|
|
|
if (deviceExtension->MapRegisterBase != NULL) {
|
|
ExFreePool(deviceExtension->MapRegisterBase);
|
|
deviceExtension->MapRegisterBase = NULL;
|
|
}
|
|
|
|
//
|
|
// If no device was found then set the error and return.
|
|
//
|
|
|
|
if (findStatus != SP_RETURN_FOUND) {
|
|
|
|
switch (findStatus) {
|
|
case SP_RETURN_NOT_FOUND:
|
|
status = STATUS_DEVICE_DOES_NOT_EXIST;
|
|
|
|
//
|
|
// The driver could not find any devices on this bus.
|
|
// Try the next bus.
|
|
//
|
|
|
|
configurationContext.BusNumber++;
|
|
deviceExtension->ConfigurationInformation = NULL;
|
|
ExFreePool(configInfo);
|
|
callAgain = FALSE;
|
|
goto NewConfiguration;
|
|
|
|
case SP_RETURN_BAD_CONFIG:
|
|
status = STATUS_INVALID_PARAMETER;
|
|
uniqueId = 265;
|
|
break;
|
|
|
|
case SP_RETURN_ERROR:
|
|
status = STATUS_ADAPTER_HARDWARE_ERROR;
|
|
uniqueId = 266;
|
|
break;
|
|
|
|
default:
|
|
status = STATUS_INTERNAL_ERROR;
|
|
uniqueId = 267;
|
|
break;
|
|
}
|
|
|
|
DebugPrint((1,
|
|
"ScsiPortInitialize: Miniport find adapter reported an error %d",
|
|
returnStatus));
|
|
break;
|
|
}
|
|
|
|
DebugPrint((1,
|
|
"ScsiPortInitialize: SCSI adapter ID is %d\n",
|
|
configInfo->InitiatorBusId[0]));
|
|
|
|
//
|
|
// Update SrbExtensionSize and SpecificLuExtensionSize, if necessary.
|
|
// If the common buffer has already been allocated, this has already been
|
|
// done.
|
|
//
|
|
|
|
if (!deviceExtension->NonCachedExtension &&
|
|
(configInfo->SrbExtensionSize != deviceExtension->SrbExtensionSize)) {
|
|
deviceExtension->SrbExtensionSize =
|
|
(configInfo->SrbExtensionSize + sizeof(LONGLONG)) &
|
|
~(sizeof(LONGLONG) - 1);
|
|
}
|
|
|
|
if (configInfo->SpecificLuExtensionSize != deviceExtension->HwLogicalUnitExtensionSize) {
|
|
deviceExtension->HwLogicalUnitExtensionSize = configInfo->SpecificLuExtensionSize;
|
|
}
|
|
|
|
//
|
|
// Check the resource requirements against the registry. This will
|
|
// check for conflicts and store the information is none were found.
|
|
//
|
|
|
|
if (!(HwInitializationData->AdapterInterfaceType == PCIBus &&
|
|
HwInitializationData->VendorIdLength > 0 &&
|
|
HwInitializationData->DeviceIdLength > 0 &&
|
|
HwInitializationData->DeviceId &&
|
|
HwInitializationData->VendorId)) {
|
|
|
|
resourceList = SpBuildResourceList(deviceExtension,
|
|
configInfo);
|
|
|
|
if (resourceList) {
|
|
|
|
RtlInitUnicodeString(&unicodeString, L"ScsiAdapter");
|
|
status = IoReportResourceUsage(&unicodeString,
|
|
driverObject,
|
|
NULL,
|
|
0,
|
|
deviceObject,
|
|
resourceList,
|
|
FIELD_OFFSET(CM_RESOURCE_LIST,
|
|
List[0].PartialResourceList.PartialDescriptors) +
|
|
resourceList->List[0].PartialResourceList.Count
|
|
* sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR),
|
|
FALSE,
|
|
&conflict);
|
|
ExFreePool(resourceList);
|
|
if ((!NT_SUCCESS(status)) || conflict) {
|
|
uniqueId=273;
|
|
if (conflict) {
|
|
status = STATUS_CONFLICTING_ADDRESSES;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
conflict = FALSE;
|
|
|
|
//
|
|
// Get maximum target IDs.
|
|
//
|
|
|
|
if (configInfo->MaximumNumberOfTargets > SCSI_MAXIMUM_TARGETS_PER_BUS) {
|
|
deviceExtension->MaximumTargetIds = SCSI_MAXIMUM_TARGETS_PER_BUS;
|
|
} else {
|
|
deviceExtension->MaximumTargetIds = configInfo->MaximumNumberOfTargets;
|
|
}
|
|
|
|
//
|
|
// Get number of SCSI buses.
|
|
//
|
|
|
|
deviceExtension->NumberOfBuses = configInfo->NumberOfBuses;
|
|
|
|
//
|
|
// Remember if the adapter caches data.
|
|
//
|
|
|
|
deviceExtension->CachesData = configInfo->CachesData;
|
|
|
|
//
|
|
// Save away the some of the attributes.
|
|
//
|
|
|
|
deviceExtension->ReceiveEvent = configInfo->ReceiveEvent;
|
|
deviceExtension->TaggedQueuing = configInfo->TaggedQueuing;
|
|
deviceExtension->MultipleRequestPerLu = configInfo->MultipleRequestPerLu;
|
|
|
|
//
|
|
// Clear those options which have been disabled in the registry.
|
|
//
|
|
|
|
if (configurationContext.DisableMultipleLu) {
|
|
deviceExtension->MultipleRequestPerLu =
|
|
configInfo->MultipleRequestPerLu = FALSE;
|
|
|
|
}
|
|
|
|
if (configurationContext.DisableTaggedQueueing) {
|
|
deviceExtension->TaggedQueuing =
|
|
configInfo->MultipleRequestPerLu = FALSE;
|
|
|
|
}
|
|
|
|
//
|
|
// If the adapter supports tagged queuing or multiple requests per
|
|
// logical unit, then SRB data needs to be allocated.
|
|
//
|
|
|
|
if (deviceExtension->TaggedQueuing ||
|
|
deviceExtension->MultipleRequestPerLu) {
|
|
|
|
deviceExtension->AllocateSrbData = TRUE;
|
|
} else {
|
|
|
|
deviceExtension->AllocateSrbData = FALSE;
|
|
}
|
|
|
|
//
|
|
// Initialize the capabilities pointer.
|
|
//
|
|
|
|
capabilities = &deviceExtension->Capabilities;
|
|
|
|
//
|
|
// Set indicater as to whether adapter needs kernel mapped buffers.
|
|
//
|
|
|
|
deviceExtension->MapBuffers = configInfo->MapBuffers;
|
|
capabilities->AdapterUsesPio = configInfo->MapBuffers;
|
|
|
|
//
|
|
// Determine if a Dma Adapter must be allocated.
|
|
//
|
|
|
|
if (deviceExtension->DmaAdapterObject == NULL &&
|
|
(configInfo->Master || configInfo->DmaChannel != SP_UNINITIALIZED_VALUE)) {
|
|
|
|
//
|
|
// Get the adapter object for this card.
|
|
//
|
|
|
|
RtlZeroMemory(&deviceDescription, sizeof(deviceDescription));
|
|
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.ScatterGather = configInfo->ScatterGather;
|
|
deviceDescription.Master = configInfo->Master;
|
|
deviceDescription.DmaPort = configInfo->DmaPort;
|
|
deviceDescription.Dma32BitAddresses = configInfo->Dma32BitAddresses;
|
|
deviceDescription.AutoInitialize = FALSE;
|
|
deviceDescription.DemandMode = configInfo->DemandMode;
|
|
deviceDescription.MaximumLength = configInfo->MaximumTransferLength;
|
|
|
|
deviceExtension->DmaAdapterObject = HalGetAdapter(&deviceDescription,
|
|
&numberOfMapRegisters);
|
|
ASSERT(deviceExtension->DmaAdapterObject);
|
|
|
|
//
|
|
// Set maximum number of page breaks.
|
|
//
|
|
|
|
if (numberOfMapRegisters > configInfo->NumberOfPhysicalBreaks) {
|
|
capabilities->MaximumPhysicalPages = configInfo->NumberOfPhysicalBreaks;
|
|
} else {
|
|
capabilities->MaximumPhysicalPages = numberOfMapRegisters;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Allocate memory for the noncached extension if it has not already been
|
|
// allocated. If the adapter supports AutoRequestSense or
|
|
// needs SRB extensions then an SRB list needs to be allocated.
|
|
//
|
|
|
|
if ((deviceExtension->SrbExtensionSize != 0 ||
|
|
configInfo->AutoRequestSense) &&
|
|
deviceExtension->SrbExtensionBuffer == NULL) {
|
|
|
|
//
|
|
// Capture the auto request sense flag when the common buffer
|
|
// is allocated.
|
|
//
|
|
|
|
deviceExtension->AutoRequestSense = configInfo->AutoRequestSense;
|
|
deviceExtension->AllocateSrbExtension = TRUE;
|
|
|
|
status = SpGetCommonBuffer(deviceExtension, 0);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
uniqueId = 269;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Allocate the per SRB data if necessary.
|
|
//
|
|
|
|
if (deviceExtension->AllocateSrbData) {
|
|
|
|
//
|
|
// Determine the number of SRB extension which have been allocated.
|
|
//
|
|
|
|
if (deviceExtension->SrbDataCount != 0) {
|
|
count = deviceExtension->SrbDataCount;
|
|
} else {
|
|
count = deviceExtension->NumberOfRequests * 2;
|
|
}
|
|
|
|
srbData = ExAllocatePool(NonPagedPool, count * sizeof(SRB_DATA));
|
|
if (srbData == NULL) {
|
|
return (ULONG) STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
RtlZeroMemory(srbData, count * sizeof(SRB_DATA));
|
|
|
|
deviceExtension->SrbData = srbData;
|
|
deviceExtension->FreeSrbData = srbData;
|
|
deviceExtension->SrbDataCount = count;
|
|
|
|
//
|
|
// Link the SRB data structures into a free list.
|
|
//
|
|
|
|
for (count; count > 0; count--) {
|
|
|
|
srbData->RequestList.Flink = (PLIST_ENTRY) (srbData + 1);
|
|
srbData++;
|
|
}
|
|
|
|
//
|
|
// Fix up the last element of the list.
|
|
//
|
|
|
|
--srbData;
|
|
srbData->RequestList.Flink = NULL;
|
|
}
|
|
|
|
//
|
|
// Initailize the capabilities structure.
|
|
//
|
|
|
|
capabilities->Length = sizeof(IO_SCSI_CAPABILITIES);
|
|
capabilities->MaximumTransferLength = configInfo->MaximumTransferLength;
|
|
|
|
if (configInfo->ReceiveEvent) {
|
|
capabilities->SupportedAsynchronousEvents |=
|
|
SRBEV_SCSI_ASYNC_NOTIFICATION;
|
|
}
|
|
|
|
capabilities->TaggedQueuing = deviceExtension->TaggedQueuing;
|
|
capabilities->AdapterScansDown = configInfo->AdapterScansDown;
|
|
|
|
//
|
|
// Update the device object alignment if necessary.
|
|
//
|
|
|
|
if (configInfo->AlignmentMask > deviceObject->AlignmentRequirement) {
|
|
|
|
deviceObject->AlignmentRequirement = configInfo->AlignmentMask;
|
|
}
|
|
|
|
capabilities->AlignmentMask = deviceObject->AlignmentRequirement;
|
|
|
|
//
|
|
// Make sure maximum number of pages is set to a reasonable value.
|
|
// This occurs for miniports with no Dma adapter.
|
|
//
|
|
|
|
if (capabilities->MaximumPhysicalPages == 0) {
|
|
|
|
capabilities->MaximumPhysicalPages =
|
|
BYTES_TO_PAGES(capabilities->MaximumTransferLength);
|
|
|
|
//
|
|
// Honor any limit requested by the miniport.
|
|
//
|
|
|
|
if (configInfo->NumberOfPhysicalBreaks < capabilities->MaximumPhysicalPages) {
|
|
|
|
capabilities->MaximumPhysicalPages =
|
|
configInfo->NumberOfPhysicalBreaks;
|
|
}
|
|
}
|
|
|
|
if (deviceExtension->HwInterrupt == NULL ||
|
|
(configInfo->BusInterruptLevel == 0 &&
|
|
configInfo->BusInterruptVector == 0)) {
|
|
|
|
//
|
|
// There is no interrupt so use the dummy routine.
|
|
//
|
|
|
|
KeInitializeSpinLock(&deviceExtension->InterruptSpinLock);
|
|
deviceExtension->SynchronizeExecution = SpSynchronizeExecution;
|
|
deviceExtension->InterruptObject = (PVOID) deviceExtension;
|
|
|
|
DebugPrint((1, "ScsiPortInitialize: Adapter has no interrupt.\n"));
|
|
|
|
} else {
|
|
|
|
//
|
|
// Determine if 2 interrupt sync. is needed.
|
|
//
|
|
|
|
if (deviceExtension->HwInterrupt != NULL &&
|
|
(configInfo->BusInterruptLevel != 0 ||
|
|
configInfo->BusInterruptVector != 0) &&
|
|
(configInfo->BusInterruptLevel2 != 0 ||
|
|
configInfo->BusInterruptVector2 != 0)) {
|
|
|
|
syncIrql = 0;
|
|
irql2 = 0;
|
|
vector2 = 0;
|
|
affinity2 = 0;
|
|
|
|
//
|
|
// Save interrupt level.
|
|
//
|
|
|
|
deviceExtension->InterruptLevel = configInfo->BusInterruptLevel;
|
|
|
|
//
|
|
// Set up for a real interrupt.
|
|
//
|
|
|
|
deviceExtension->SynchronizeExecution = KeSynchronizeExecution;
|
|
|
|
//
|
|
// Call HAL to get system interrupt parameters for the first interrupt.
|
|
//
|
|
|
|
vector = HalGetInterruptVector(configInfo->AdapterInterfaceType,
|
|
configInfo->SystemIoBusNumber,
|
|
configInfo->BusInterruptLevel,
|
|
configInfo->BusInterruptVector,
|
|
&irql,
|
|
&affinity);
|
|
|
|
//
|
|
// Call HAL to get system interrupt parameters for the second interrupt.
|
|
//
|
|
|
|
vector2 = HalGetInterruptVector(configInfo->AdapterInterfaceType,
|
|
configInfo->SystemIoBusNumber,
|
|
configInfo->BusInterruptLevel2,
|
|
configInfo->BusInterruptVector2,
|
|
&irql2,
|
|
&affinity2);
|
|
|
|
syncIrql = (irql > irql2) ? irql : irql2;
|
|
|
|
if (configInfo->AdapterInterfaceType == MicroChannel ||
|
|
configInfo->InterruptMode == LevelSensitive) {
|
|
interruptSharable = TRUE;
|
|
} else {
|
|
interruptSharable = FALSE;
|
|
}
|
|
|
|
status = IoConnectInterrupt(&deviceExtension->InterruptObject,
|
|
(PKSERVICE_ROUTINE)ScsiPortInterrupt,
|
|
deviceObject,
|
|
&deviceExtension->MultipleIrqSpinLock,
|
|
vector,
|
|
irql,
|
|
syncIrql,
|
|
configInfo->InterruptMode,
|
|
interruptSharable,
|
|
affinity,
|
|
FALSE);
|
|
|
|
if (!(NT_SUCCESS(status))) {
|
|
|
|
DebugPrint((1,"ScsiPortInitialize: Can't connect interrupt %d\n", vector));
|
|
deviceExtension->InterruptObject = NULL;
|
|
uniqueId = 270;
|
|
break;
|
|
}
|
|
|
|
DebugPrint((1,
|
|
"ScsiPortInitialize: SCSI adapter Second IRQ is %d\n",
|
|
configInfo->BusInterruptLevel2));
|
|
|
|
status = IoConnectInterrupt(&deviceExtension->InterruptObject2,
|
|
(PKSERVICE_ROUTINE)ScsiPortInterrupt,
|
|
deviceObject,
|
|
&deviceExtension->MultipleIrqSpinLock,
|
|
vector2,
|
|
irql2,
|
|
syncIrql,
|
|
configInfo->InterruptMode2,
|
|
interruptSharable,
|
|
affinity2,
|
|
FALSE);
|
|
|
|
if (!(NT_SUCCESS(status))) {
|
|
|
|
//
|
|
// If we needed both interrupts, we will continue but not claim any of the resources
|
|
// for the second one.
|
|
//
|
|
|
|
DebugPrint((1,"ScsiPortInitialize: Can't connect second interrupt %d\n", vector2));
|
|
deviceExtension->InterruptObject2 = NULL;
|
|
configInfo->BusInterruptVector2 = 0;
|
|
configInfo->BusInterruptLevel2 = 0;
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// Normal path. Only one interrupt is active for this device extension.
|
|
//
|
|
|
|
DebugPrint((1,
|
|
"ScsiPortInitialize: SCSI adapter IRQ is %d\n",
|
|
configInfo->BusInterruptLevel));
|
|
|
|
//
|
|
// Save interrupt level.
|
|
//
|
|
|
|
deviceExtension->InterruptLevel = configInfo->BusInterruptLevel;
|
|
|
|
//
|
|
// Set up for a real interrupt.
|
|
//
|
|
|
|
deviceExtension->SynchronizeExecution = KeSynchronizeExecution;
|
|
|
|
//
|
|
// Call HAL to get system interrupt parameters.
|
|
//
|
|
|
|
vector = HalGetInterruptVector(configInfo->AdapterInterfaceType,
|
|
configInfo->SystemIoBusNumber,
|
|
configInfo->BusInterruptLevel,
|
|
configInfo->BusInterruptVector,
|
|
&irql,
|
|
&affinity);
|
|
|
|
//
|
|
// Initialize interrupt object and connect to interrupt.
|
|
//
|
|
|
|
if (configInfo->AdapterInterfaceType == MicroChannel ||
|
|
configInfo->InterruptMode == LevelSensitive) {
|
|
interruptSharable = TRUE;
|
|
} else {
|
|
interruptSharable = FALSE;
|
|
}
|
|
|
|
status = IoConnectInterrupt(&deviceExtension->InterruptObject,
|
|
(PKSERVICE_ROUTINE)ScsiPortInterrupt,
|
|
deviceObject,
|
|
(PKSPIN_LOCK)NULL,
|
|
vector,
|
|
irql,
|
|
irql,
|
|
configInfo->InterruptMode,
|
|
interruptSharable,
|
|
affinity,
|
|
FALSE);
|
|
|
|
if (!(NT_SUCCESS(status))) {
|
|
|
|
DebugPrint((1,"ScsiPortInitialize: Can't connect interrupt %d\n", vector));
|
|
deviceExtension->InterruptObject = NULL;
|
|
uniqueId = 270;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Record first access range if it exists.
|
|
//
|
|
|
|
if (HwInitializationData->NumberOfAccessRanges != 0) {
|
|
deviceExtension->IoAddress =
|
|
((*(configInfo->AccessRanges))[0]).RangeStart.LowPart;
|
|
DebugPrint((1,
|
|
"ScsiportInitialize: IO Base address %x\n",
|
|
deviceExtension->IoAddress));
|
|
}
|
|
|
|
//
|
|
// Indicate that a disconnect allowed command running. This bit is
|
|
// normally on.
|
|
//
|
|
|
|
deviceExtension->Flags |= PD_DISCONNECT_RUNNING;
|
|
|
|
//
|
|
// Initialize the request count to -1. This count is biased by -1 so
|
|
// that a value of zero indicates the adapter must be allocated.
|
|
//
|
|
|
|
deviceExtension->ActiveRequestCount = -1;
|
|
|
|
//
|
|
// Indicate if a scatter/gather list needs to be built.
|
|
//
|
|
|
|
if (deviceExtension->DmaAdapterObject != NULL && configInfo->Master
|
|
&& configInfo->NeedPhysicalAddresses) {
|
|
|
|
deviceExtension->MasterWithAdapter = TRUE;
|
|
|
|
} else {
|
|
|
|
deviceExtension->MasterWithAdapter = FALSE;
|
|
|
|
} // end if (deviceExtension->DmaAdapterObject != NULL)
|
|
|
|
//
|
|
// Call the hardware dependent driver to do its initialization.
|
|
// This routine must be called at DISPATCH_LEVEL.
|
|
//
|
|
|
|
KeRaiseIrql(DISPATCH_LEVEL, &irql);
|
|
|
|
if (!deviceExtension->SynchronizeExecution(deviceExtension->InterruptObject,
|
|
deviceExtension->HwInitialize,
|
|
deviceExtension->HwDeviceExtension)) {
|
|
|
|
DebugPrint((1,"ScsiPortInitialize: initialization failed\n"));
|
|
KeLowerIrql(irql);
|
|
status = STATUS_ADAPTER_HARDWARE_ERROR;
|
|
uniqueId = 271;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Check for miniport work requests. Note this is an unsynchonized
|
|
// test on a bit that can be set by the interrupt routine; however,
|
|
// the worst that can happen is that the completion DPC checks for work
|
|
// twice.
|
|
//
|
|
|
|
if (deviceExtension->InterruptData.InterruptFlags & PD_NOTIFICATION_REQUIRED) {
|
|
|
|
//
|
|
// Call the completion DPC directly. It must be called at
|
|
// dispatch level.
|
|
//
|
|
|
|
ScsiPortCompletionDpc(NULL,
|
|
deviceExtension->DeviceObject,
|
|
NULL,
|
|
NULL);
|
|
}
|
|
|
|
KeLowerIrql(irql);
|
|
|
|
//
|
|
// Start timer. Request timeout counters
|
|
// in the logical units have already been
|
|
// initialized.
|
|
//
|
|
|
|
IoStartTimer(deviceObject);
|
|
|
|
//
|
|
// Allocate buffer for SCSI bus scan information.
|
|
//
|
|
|
|
deviceExtension->ScsiInfo =
|
|
ExAllocatePool(PagedPool,
|
|
sizeof(PSCSI_BUS_SCAN_DATA) *
|
|
deviceExtension->NumberOfBuses + 4);
|
|
|
|
if (!deviceExtension->ScsiInfo) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
uniqueId = 272;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Zero buffer. Fields in ScsiInfo are used to distinguish a SCSI
|
|
// bus reset from initialization.
|
|
//
|
|
|
|
RtlZeroMemory(deviceExtension->ScsiInfo,
|
|
sizeof(PSCSI_BUS_SCAN_DATA) * deviceExtension->NumberOfBuses
|
|
+ 4);
|
|
|
|
//
|
|
// Save number of buses.
|
|
//
|
|
|
|
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);
|
|
}
|
|
|
|
//
|
|
// Update the device map.
|
|
//
|
|
|
|
SpBuildDeviceMap(deviceExtension, Argument2);
|
|
|
|
//
|
|
// Create the dos port driver name.
|
|
//
|
|
|
|
sprintf(deviceNameBuffer, "\\DosDevices\\Scsi%d:", *scsiPortNumber);
|
|
RtlInitString(&deviceName, deviceNameBuffer);
|
|
status = RtlAnsiStringToUnicodeString(&dosUnicodeString,
|
|
&deviceName,
|
|
TRUE);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
dosUnicodeString.Buffer = NULL;
|
|
}
|
|
|
|
//
|
|
// Create port driver name.
|
|
//
|
|
|
|
sprintf(deviceNameBuffer, "\\Device\\ScsiPort%d", *scsiPortNumber);
|
|
RtlInitString(&deviceName, deviceNameBuffer);
|
|
status = RtlAnsiStringToUnicodeString(&unicodeString,
|
|
&deviceName,
|
|
TRUE);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
unicodeString.Buffer = NULL;
|
|
}
|
|
|
|
if (dosUnicodeString.Buffer != NULL && unicodeString.Buffer != NULL) {
|
|
IoAssignArcName(&dosUnicodeString, &unicodeString);
|
|
}
|
|
|
|
if (dosUnicodeString.Buffer != NULL ) {
|
|
RtlFreeUnicodeString(&dosUnicodeString);
|
|
}
|
|
|
|
if (unicodeString.Buffer != NULL ) {
|
|
RtlFreeUnicodeString(&unicodeString);
|
|
}
|
|
|
|
//
|
|
// Bump SCSI host bus adapters count.
|
|
//
|
|
|
|
(*scsiPortNumber)++;
|
|
|
|
//
|
|
// Update the local adapter count.
|
|
//
|
|
|
|
configurationContext.AdapterNumber++;
|
|
initialCall = FALSE;
|
|
|
|
//
|
|
// Bump the bus number if miniport indicated that it should not be
|
|
// called again on this bus.
|
|
//
|
|
|
|
if (!callAgain) {
|
|
configurationContext.BusNumber++;
|
|
}
|
|
|
|
//
|
|
// Set the return status to STATUS_SUCCESS to indicate that one HBA
|
|
// was found.
|
|
//
|
|
|
|
returnStatus = STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// At this point either all of the devices have been found or an error has
|
|
// occured. If in an error has occured, then log an error. If one or more
|
|
// HBAs has been found, then return success. If an error occured, log it.
|
|
// In either case the last device object needs to be cleaned up.
|
|
//
|
|
|
|
if (status != STATUS_DEVICE_DOES_NOT_EXIST) {
|
|
|
|
//
|
|
// An error occured log it.
|
|
//
|
|
|
|
errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry(deviceExtension->DeviceObject,
|
|
sizeof(IO_ERROR_LOG_PACKET));
|
|
|
|
if (errorLogEntry != NULL) {
|
|
errorLogEntry->ErrorCode = IO_ERR_DRIVER_ERROR;
|
|
errorLogEntry->UniqueErrorValue = uniqueId;
|
|
errorLogEntry->FinalStatus = status;
|
|
errorLogEntry->DumpDataSize = 0;
|
|
IoWriteErrorLogEntry(errorLogEntry);
|
|
}
|
|
}
|
|
|
|
if (!NT_SUCCESS(returnStatus)) {
|
|
|
|
//
|
|
// If no devices were found then return the current status.
|
|
//
|
|
|
|
returnStatus = status;
|
|
}
|
|
|
|
//
|
|
// Clean up the last device object which is not used.
|
|
//
|
|
|
|
SpDeviceCleanup(deviceExtension);
|
|
|
|
//
|
|
// Close the keys that were opened.
|
|
//
|
|
|
|
if (configurationContext.ServiceKey != NULL) {
|
|
ZwClose(configurationContext.ServiceKey);
|
|
}
|
|
|
|
if (configurationContext.DeviceKey != NULL) {
|
|
ZwClose(configurationContext.DeviceKey);
|
|
}
|
|
|
|
if (configurationContext.BusKey != NULL) {
|
|
ZwClose(configurationContext.BusKey);
|
|
}
|
|
|
|
if (configurationContext.AccessRanges != NULL) {
|
|
ExFreePool(configurationContext.AccessRanges);
|
|
}
|
|
|
|
if (configurationContext.Parameter != NULL) {
|
|
ExFreePool(configurationContext.Parameter);
|
|
}
|
|
|
|
return returnStatus;
|
|
|
|
} // end ScsiPortInitialize()
|
|
|
|
NTSTATUS
|
|
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:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
PIRP irp;
|
|
PIO_STACK_LOCATION irpStack;
|
|
SCSI_REQUEST_BLOCK srb;
|
|
PCDB cdb;
|
|
KEVENT event;
|
|
IO_STATUS_BLOCK ioStatusBlock;
|
|
KIRQL currentIrql;
|
|
PINQUIRYDATA inquiryDataBuffer;
|
|
PSENSE_DATA senseInfoBuffer;
|
|
NTSTATUS status;
|
|
ULONG retryCount = 0;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Allocate properly aligned INQUIRY buffer.
|
|
//
|
|
|
|
inquiryDataBuffer = ExAllocatePool(NonPagedPoolCacheAligned, INQUIRYDATABUFFERSIZE);
|
|
|
|
if (inquiryDataBuffer == NULL) {
|
|
DebugPrint((1,"IssueInquiry: Can't allocate inquiry buffer\n"));
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Sense buffer is in non-paged pool.
|
|
//
|
|
|
|
senseInfoBuffer = ExAllocatePool( NonPagedPoolCacheAligned, SENSE_BUFFER_SIZE);
|
|
|
|
if (senseInfoBuffer == NULL) {
|
|
ExFreePool(inquiryDataBuffer);
|
|
DebugPrint((1,"SendSrbSynchronous: Can't allocate request sense buffer\n"));
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
inquiryRetry:
|
|
|
|
//
|
|
// Initialize the notification event.
|
|
//
|
|
|
|
KeInitializeEvent(&event,
|
|
NotificationEvent,
|
|
FALSE);
|
|
|
|
//
|
|
// Build IRP for this request.
|
|
//
|
|
|
|
irp = IoBuildDeviceIoControlRequest(
|
|
IOCTL_SCSI_EXECUTE_IN,
|
|
DeviceExtension->DeviceObject,
|
|
NULL,
|
|
0,
|
|
inquiryDataBuffer,
|
|
INQUIRYDATABUFFERSIZE,
|
|
TRUE,
|
|
&event,
|
|
&ioStatusBlock);
|
|
|
|
irpStack = IoGetNextIrpStackLocation(irp);
|
|
|
|
//
|
|
// Fill in SRB fields.
|
|
//
|
|
|
|
RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK));
|
|
|
|
irpStack->Parameters.Scsi.Srb = &srb;
|
|
|
|
srb.PathId = LunInfo->PathId;
|
|
srb.TargetId = LunInfo->TargetId;
|
|
srb.Lun = LunInfo->Lun;
|
|
|
|
srb.Function = SRB_FUNCTION_EXECUTE_SCSI;
|
|
srb.Length = sizeof(SCSI_REQUEST_BLOCK);
|
|
|
|
//
|
|
// Set flags to disable synchronous negociation.
|
|
//
|
|
|
|
srb.SrbFlags = SRB_FLAGS_DATA_IN | SRB_FLAGS_DISABLE_SYNCH_TRANSFER;
|
|
|
|
srb.SrbStatus = srb.ScsiStatus = 0;
|
|
|
|
srb.NextSrb = 0;
|
|
|
|
srb.OriginalRequest = irp;
|
|
|
|
//
|
|
// Set timeout to 2 seconds.
|
|
//
|
|
|
|
srb.TimeOutValue = 4;
|
|
|
|
srb.CdbLength = 6;
|
|
|
|
//
|
|
// Enable auto request sense.
|
|
//
|
|
|
|
srb.SenseInfoBuffer = senseInfoBuffer;
|
|
srb.SenseInfoBufferLength = SENSE_BUFFER_SIZE;
|
|
|
|
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.
|
|
//
|
|
|
|
IoCallDriver(DeviceExtension->DeviceObject, irp);
|
|
|
|
//
|
|
// Wait for request to complete.
|
|
//
|
|
|
|
KeWaitForSingleObject(&event,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
|
|
if (SRB_STATUS(srb.SrbStatus) != SRB_STATUS_SUCCESS) {
|
|
|
|
DebugPrint((2,"IssueInquiry: Inquiry failed SRB status %x\n",
|
|
srb.SrbStatus));
|
|
|
|
//
|
|
// Unfreeze queue if necessary
|
|
//
|
|
|
|
if (srb.SrbStatus & SRB_STATUS_QUEUE_FROZEN) {
|
|
|
|
PLOGICAL_UNIT_EXTENSION logicalUnit =
|
|
GetLogicalUnitExtension(DeviceExtension,
|
|
LunInfo->PathId,
|
|
LunInfo->TargetId,
|
|
LunInfo->Lun);
|
|
|
|
DebugPrint((3, "IssueInquiry: Unfreeze Queue TID %d\n",
|
|
srb.TargetId));
|
|
|
|
logicalUnit->LuFlags &= ~PD_QUEUE_FROZEN;
|
|
|
|
KeAcquireSpinLock(&DeviceExtension->SpinLock, ¤tIrql);
|
|
|
|
GetNextLuRequest(DeviceExtension, logicalUnit);
|
|
KeLowerIrql(currentIrql);
|
|
}
|
|
|
|
//
|
|
// 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));
|
|
|
|
RtlCopyMemory(LunInfo->InquiryData,
|
|
inquiryDataBuffer,
|
|
srb.DataTransferLength > INQUIRYDATABUFFERSIZE ?
|
|
INQUIRYDATABUFFERSIZE : srb.DataTransferLength);
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
} else if ((srb.SrbStatus & SRB_STATUS_AUTOSENSE_VALID) &&
|
|
senseInfoBuffer->SenseKey == SCSI_SENSE_ILLEGAL_REQUEST){
|
|
|
|
//
|
|
// A sense key of illegal request was recieved. This indicates
|
|
// that the logical unit number of not valid but there is a
|
|
// target device out there.
|
|
//
|
|
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
|
|
} else {
|
|
|
|
//
|
|
// If the selection did not time out then retry the request.
|
|
//
|
|
|
|
if ((SRB_STATUS(srb.SrbStatus) != SRB_STATUS_SELECTION_TIMEOUT) &&
|
|
(SRB_STATUS(srb.SrbStatus) != SRB_STATUS_NO_DEVICE) &&
|
|
(retryCount++ < INQUIRY_RETRY_COUNT)) {
|
|
|
|
DebugPrint((2,"IssueInquiry: Retry %d\n", retryCount));
|
|
goto inquiryRetry;
|
|
}
|
|
|
|
status = SpTranslateScsiStatus(&srb);
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Copy INQUIRY buffer to LUNINFO.
|
|
//
|
|
|
|
RtlCopyMemory(LunInfo->InquiryData,
|
|
inquiryDataBuffer,
|
|
INQUIRYDATABUFFERSIZE);
|
|
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Free INQUIRY and request sense buffer.
|
|
//
|
|
|
|
ExFreePool(inquiryDataBuffer);
|
|
ExFreePool(senseInfoBuffer);
|
|
|
|
return status;
|
|
|
|
} // end IssueInquiry()
|
|
|
|
|
|
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 logicalUnit;
|
|
ULONG size;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Round the size of the Hardware logical extension to the size of a
|
|
// PVOID and add it to the port driver's logical extension.
|
|
//
|
|
|
|
size = (DeviceExtension->HwLogicalUnitExtensionSize + sizeof(LONGLONG) - 1)
|
|
& ~(sizeof(LONGLONG) -1);
|
|
size += sizeof(LOGICAL_UNIT_EXTENSION);
|
|
|
|
//
|
|
// Create logical unit extension.
|
|
//
|
|
|
|
logicalUnit =
|
|
ExAllocatePool(NonPagedPool, size);
|
|
|
|
if (logicalUnit == NULL) {
|
|
DebugPrint((1,"CreateLogicalUnitExtension: Can't allocate logicalUnit\n"));
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Zero logical unit extension.
|
|
//
|
|
|
|
RtlZeroMemory(logicalUnit, size);
|
|
|
|
//
|
|
// Initialize the queue, associating spinlock with device queue.
|
|
//
|
|
|
|
KeInitializeDeviceQueue(&logicalUnit->RequestQueue);
|
|
|
|
//
|
|
// Set timer counters in LogicalUnits to -1 to indicate no
|
|
// outstanding requests.
|
|
//
|
|
|
|
logicalUnit->RequestTimeoutCounter = -1;
|
|
|
|
//
|
|
// Initialize the maximum queue depth size.
|
|
//
|
|
|
|
logicalUnit->MaxQueueDepth = 0xFF;
|
|
|
|
//
|
|
// Initialize the request list.
|
|
//
|
|
|
|
InitializeListHead(&logicalUnit->SrbData.RequestList);
|
|
|
|
return logicalUnit;
|
|
|
|
} // end CreateLogicalUnitExtension()
|
|
|
|
|
|
PVOID
|
|
ScsiPortGetDeviceBase(
|
|
IN PVOID HwDeviceExtension,
|
|
IN INTERFACE_TYPE BusType,
|
|
IN ULONG SystemIoBusNumber,
|
|
SCSI_PHYSICAL_ADDRESS IoAddress,
|
|
ULONG NumberOfBytes,
|
|
BOOLEAN InIoSpace
|
|
)
|
|
|
|
/*++
|
|
|
|
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.
|
|
InIoSpace - indicates an IO address.
|
|
|
|
Return Value:
|
|
|
|
Mapped address.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension =
|
|
((PDEVICE_EXTENSION) HwDeviceExtension) - 1;
|
|
PHYSICAL_ADDRESS cardAddress;
|
|
ULONG addressSpace = InIoSpace;
|
|
PVOID mappedAddress;
|
|
PMAPPED_ADDRESS newMappedAddress;
|
|
BOOLEAN b;
|
|
|
|
b = HalTranslateBusAddress(
|
|
BusType, // AdapterInterfaceType
|
|
SystemIoBusNumber, // SystemIoBusNumber
|
|
IoAddress, // Bus Address
|
|
&addressSpace, // AddressSpace
|
|
&cardAddress
|
|
);
|
|
|
|
if ( !b ) {
|
|
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);
|
|
|
|
//
|
|
// Allocate memory to store mapped address for unmap.
|
|
//
|
|
|
|
newMappedAddress = ExAllocatePool(NonPagedPool,
|
|
sizeof(MAPPED_ADDRESS));
|
|
|
|
if (newMappedAddress == NULL) {
|
|
|
|
//
|
|
// No memory to keep track of the mapped address. Just return
|
|
// the mapped value.
|
|
//
|
|
|
|
return mappedAddress;
|
|
}
|
|
|
|
//
|
|
// Store mapped address, bytes count, etc.
|
|
//
|
|
|
|
newMappedAddress->MappedAddress = mappedAddress;
|
|
newMappedAddress->NumberOfBytes = NumberOfBytes;
|
|
newMappedAddress->IoAddress = IoAddress;
|
|
newMappedAddress->BusNumber = SystemIoBusNumber;
|
|
|
|
//
|
|
// Link current list to new entry.
|
|
//
|
|
|
|
newMappedAddress->NextMappedAddress =
|
|
deviceExtension->MappedAddressList;
|
|
|
|
//
|
|
// Point anchor at new list.
|
|
//
|
|
|
|
deviceExtension->MappedAddressList = newMappedAddress;
|
|
|
|
} else {
|
|
|
|
mappedAddress = (PVOID)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 - address is in IO space.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
PMAPPED_ADDRESS nextMappedAddress;
|
|
PMAPPED_ADDRESS lastMappedAddress;
|
|
|
|
deviceExtension =
|
|
((PDEVICE_EXTENSION) HwDeviceExtension) - 1;
|
|
|
|
nextMappedAddress = deviceExtension->MappedAddressList;
|
|
lastMappedAddress = deviceExtension->MappedAddressList;
|
|
|
|
while (nextMappedAddress) {
|
|
|
|
if (nextMappedAddress->MappedAddress == MappedAddress) {
|
|
|
|
//
|
|
// Unmap address.
|
|
//
|
|
|
|
MmUnmapIoSpace(MappedAddress,
|
|
nextMappedAddress->NumberOfBytes);
|
|
|
|
//
|
|
// Remove mapped address from list.
|
|
//
|
|
|
|
if (nextMappedAddress == deviceExtension->MappedAddressList) {
|
|
|
|
//
|
|
// Removing the first entry
|
|
//
|
|
deviceExtension->MappedAddressList =
|
|
nextMappedAddress->NextMappedAddress;
|
|
} else {
|
|
|
|
lastMappedAddress->NextMappedAddress =
|
|
nextMappedAddress->NextMappedAddress;
|
|
}
|
|
|
|
ExFreePool(nextMappedAddress);
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
lastMappedAddress = nextMappedAddress;
|
|
nextMappedAddress = nextMappedAddress->NextMappedAddress;
|
|
}
|
|
}
|
|
|
|
return;
|
|
|
|
} // end ScsiPortFreeDeviceBase()
|
|
|
|
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 miniport 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 miniports 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.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension =
|
|
((PDEVICE_EXTENSION) HwDeviceExtension) - 1;
|
|
DEVICE_DESCRIPTION deviceDescription;
|
|
ULONG numberOfMapRegisters;
|
|
NTSTATUS status;
|
|
|
|
//
|
|
// Make sure that an common buffer has not already been allocated.
|
|
//
|
|
|
|
if (deviceExtension->SrbExtensionBuffer != NULL) {
|
|
return(NULL);
|
|
}
|
|
|
|
//
|
|
// If there no adapter object then try and get one.
|
|
//
|
|
|
|
if (deviceExtension->DmaAdapterObject == NULL) {
|
|
|
|
RtlZeroMemory(&deviceDescription, sizeof(DEVICE_DESCRIPTION));
|
|
|
|
deviceDescription.Version = DEVICE_DESCRIPTION_VERSION;
|
|
deviceDescription.DmaChannel = ConfigInfo->DmaChannel;
|
|
deviceDescription.InterfaceType = ConfigInfo->AdapterInterfaceType;
|
|
deviceDescription.DmaWidth = ConfigInfo->DmaWidth;
|
|
deviceDescription.DmaSpeed = ConfigInfo->DmaSpeed;
|
|
deviceDescription.ScatterGather = ConfigInfo->ScatterGather;
|
|
deviceDescription.Master = ConfigInfo->Master;
|
|
deviceDescription.DmaPort = ConfigInfo->DmaPort;
|
|
deviceDescription.Dma32BitAddresses = ConfigInfo->Dma32BitAddresses;
|
|
deviceDescription.BusNumber = ConfigInfo->SystemIoBusNumber;
|
|
deviceDescription.AutoInitialize = FALSE;
|
|
deviceDescription.DemandMode = FALSE;
|
|
deviceDescription.MaximumLength = ConfigInfo->MaximumTransferLength;
|
|
|
|
deviceExtension->DmaAdapterObject = HalGetAdapter(
|
|
&deviceDescription,
|
|
&numberOfMapRegisters
|
|
);
|
|
|
|
//
|
|
// If an adapter could not be allocated then return NULL.
|
|
//
|
|
|
|
if (deviceExtension->DmaAdapterObject == NULL) {
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
//
|
|
// Determine the number of page breaks allowed.
|
|
//
|
|
|
|
if (numberOfMapRegisters > ConfigInfo->NumberOfPhysicalBreaks &&
|
|
ConfigInfo->NumberOfPhysicalBreaks != 0) {
|
|
|
|
deviceExtension->Capabilities.MaximumPhysicalPages =
|
|
ConfigInfo->NumberOfPhysicalBreaks;
|
|
} else {
|
|
|
|
deviceExtension->Capabilities.MaximumPhysicalPages =
|
|
numberOfMapRegisters;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set auto request sense in device extension.
|
|
//
|
|
|
|
deviceExtension->AutoRequestSense = ConfigInfo->AutoRequestSense;
|
|
|
|
//
|
|
// Update SrbExtensionSize, if necessary. The miniport's FindAdapter routine
|
|
// has the opportunity to adjust it after being called, depending upon
|
|
// it's Scatter/Gather List requirements.
|
|
//
|
|
|
|
if (deviceExtension->SrbExtensionSize != ConfigInfo->SrbExtensionSize) {
|
|
deviceExtension->SrbExtensionSize = ConfigInfo->SrbExtensionSize;
|
|
}
|
|
|
|
//
|
|
// If the adapter supports AutoRequestSense or needs SRB extensions
|
|
// then an SRB list needs to be allocated.
|
|
//
|
|
|
|
if (deviceExtension->SrbExtensionSize != 0 ||
|
|
ConfigInfo->AutoRequestSense) {
|
|
|
|
deviceExtension->AllocateSrbExtension = TRUE;
|
|
}
|
|
|
|
//
|
|
// Allocate the common buffer.
|
|
//
|
|
|
|
status = SpGetCommonBuffer( deviceExtension, NumberOfBytes);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
return(NULL);
|
|
}
|
|
|
|
return(deviceExtension->NonCachedExtension);
|
|
}
|
|
|
|
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 list. 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 miniport driver.
|
|
|
|
Return Value:
|
|
|
|
Returns the status of the allocate operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
PVOID buffer;
|
|
ULONG length;
|
|
ULONG blockSize;
|
|
PVOID *srbExtension;
|
|
|
|
//
|
|
// To ensure that we never transfer normal request data to the SrbExtension
|
|
// (ie. the case of Srb->SenseInfoBuffer == VirtualAddress in
|
|
// ScsiPortGetPhysicalAddress) on some platforms where an inconsistency in
|
|
// MM can result in the same Virtual address supplied for 2 different
|
|
// physical addresses, bump the SrbExtensionSize if it's zero.
|
|
//
|
|
|
|
if (DeviceExtension->SrbExtensionSize == 0) {
|
|
DeviceExtension->SrbExtensionSize = 16;
|
|
}
|
|
|
|
//
|
|
// Calculate the block size for the list elements based on the Srb
|
|
// Extension.
|
|
//
|
|
|
|
blockSize = DeviceExtension->SrbExtensionSize;
|
|
|
|
//
|
|
// If auto request sense is supported then add in space for the request
|
|
// sense data.
|
|
//
|
|
|
|
if (DeviceExtension->AutoRequestSense) {
|
|
|
|
blockSize += sizeof(SENSE_DATA);
|
|
}
|
|
|
|
//
|
|
// Round blocksize up to the size of a PVOID.
|
|
//
|
|
|
|
blockSize = (blockSize + sizeof(LONGLONG) - 1) & ~(sizeof(LONGLONG) - 1);
|
|
|
|
//
|
|
// The length of the common buffer should be equal to the size of the
|
|
// noncached extension and a minimum number of srb extension
|
|
//
|
|
|
|
length = NonCachedExtensionSize + blockSize * DeviceExtension->NumberOfRequests;
|
|
|
|
//
|
|
// Round the length up to a page size, since HalAllocateCommonBuffer
|
|
// allocates in pages anyway.
|
|
//
|
|
|
|
length = ROUND_TO_PAGES(length);
|
|
|
|
//
|
|
// Allocate the common buffer.
|
|
//
|
|
|
|
if (DeviceExtension->DmaAdapterObject == NULL) {
|
|
|
|
//
|
|
// Since there is no adapter just allocate from non-paged pool.
|
|
//
|
|
|
|
buffer = ExAllocatePool(NonPagedPool, length);
|
|
|
|
} else {
|
|
|
|
buffer = HalAllocateCommonBuffer(DeviceExtension->DmaAdapterObject,
|
|
length,
|
|
&DeviceExtension->PhysicalCommonBuffer,
|
|
FALSE );
|
|
}
|
|
|
|
if (buffer == NULL) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Clear the common buffer.
|
|
//
|
|
|
|
RtlZeroMemory(buffer, length);
|
|
|
|
DeviceExtension->CommonBufferSize = length;
|
|
|
|
//
|
|
// Set the Srb Extension to the start of the buffer. This address
|
|
// is used to deallocate the common buffer so it must be
|
|
// set whether the device is using an Srb Extension or not.
|
|
//
|
|
|
|
DeviceExtension->SrbExtensionBuffer = buffer;
|
|
|
|
//
|
|
// Subtract out the memory for the NonCachedExtensionSize.
|
|
//
|
|
|
|
if (NonCachedExtensionSize) {
|
|
|
|
length -= NonCachedExtensionSize;
|
|
|
|
//
|
|
// Set the noncached extension to the end of the buffer.
|
|
//
|
|
|
|
DeviceExtension->NonCachedExtension = (PUCHAR) buffer + length;
|
|
|
|
} else {
|
|
|
|
DeviceExtension->NonCachedExtension = NULL;
|
|
|
|
}
|
|
|
|
if (DeviceExtension->AllocateSrbExtension) {
|
|
|
|
//
|
|
// Calculate the number of SRB extension which were allocated. This
|
|
// will be used to determine how many SRB data strucuture to allocate.
|
|
//
|
|
|
|
DeviceExtension->SrbDataCount = length / blockSize;
|
|
|
|
//
|
|
// Initialize the SRB extension list.
|
|
//
|
|
|
|
srbExtension = (PVOID *) buffer;
|
|
DeviceExtension->SrbExtensionListHeader = srbExtension;
|
|
|
|
while (length >= blockSize * 2) {
|
|
|
|
*srbExtension = (PVOID *) ((PCHAR) srbExtension + blockSize);
|
|
srbExtension = *srbExtension;
|
|
|
|
length -= blockSize;
|
|
|
|
}
|
|
}
|
|
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
|
|
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.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension = (PDEVICE_EXTENSION) DeviceExtension - 1;
|
|
CM_EISA_SLOT_INFORMATION slotInformation;
|
|
|
|
//
|
|
// If the length is nonzero, the the requested data.
|
|
//
|
|
|
|
if (Length != 0) {
|
|
|
|
return HalGetBusData(BusDataType,
|
|
SystemIoBusNumber,
|
|
SlotNumber,
|
|
Buffer,
|
|
Length);
|
|
}
|
|
|
|
//
|
|
// Free any previously allocated data.
|
|
//
|
|
|
|
if (deviceExtension->MapRegisterBase != NULL) {
|
|
ExFreePool(deviceExtension->MapRegisterBase);
|
|
deviceExtension->MapRegisterBase = NULL;
|
|
}
|
|
|
|
if (BusDataType == EisaConfiguration) {
|
|
|
|
//
|
|
// Determine 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
|
|
//
|
|
|
|
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 if (BusDataType == PCIConfiguration) {
|
|
|
|
//
|
|
// Read only the header.
|
|
//
|
|
|
|
Length = PCI_COMMON_HDR_LENGTH;
|
|
|
|
} else {
|
|
|
|
Length = PAGE_SIZE;
|
|
}
|
|
|
|
deviceExtension->MapRegisterBase = ExAllocatePool(NonPagedPool, Length);
|
|
|
|
if (deviceExtension->MapRegisterBase == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// Return the pointer to the miniport driver.
|
|
//
|
|
|
|
*((PVOID *)Buffer) = deviceExtension->MapRegisterBase;
|
|
|
|
return HalGetBusData(BusDataType,
|
|
SystemIoBusNumber,
|
|
SlotNumber,
|
|
deviceExtension->MapRegisterBase,
|
|
Length);
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
DriverEntry(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PUNICODE_STRING RegistryPath
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Temporary entry point needed to initialize the scsi port driver.
|
|
|
|
Arguments:
|
|
|
|
DriverObject - Pointer to the driver object created by the system.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS
|
|
|
|
--*/
|
|
|
|
{
|
|
//
|
|
// NOTE: This routine should not be needed ! DriverEntry is defined
|
|
// in the miniport driver.
|
|
//
|
|
|
|
UNREFERENCED_PARAMETER(DriverObject);
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
} // end DriverEntry()
|
|
|
|
VOID
|
|
SpDeviceCleanup(
|
|
PDEVICE_EXTENSION DeviceExtension
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This functions deletes all of the storage associated with a device
|
|
extension, disconnects from the timers and interrupts and then deletes the
|
|
object. This function can be called any time during the initialization.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - Supplies a pointer to the device extension to be deleted.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG j;
|
|
PLUNINFO lunInfo;
|
|
PVOID tempPointer;
|
|
|
|
//
|
|
// Make sure there is a device extension to clean up.
|
|
//
|
|
|
|
if (DeviceExtension == NULL) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// First stop the time and disconnect the interrupt if they have been
|
|
// initialized. The interrupt object is connected after
|
|
// timer has been initialized, and the interrupt object connected, but
|
|
// before the timer is started.
|
|
//
|
|
|
|
if (DeviceExtension->InterruptObject != NULL) {
|
|
|
|
IoStopTimer(DeviceExtension->DeviceObject);
|
|
IoDisconnectInterrupt(DeviceExtension->InterruptObject);
|
|
|
|
}
|
|
|
|
//
|
|
// Delete the configuration data.
|
|
//
|
|
|
|
if (DeviceExtension->ScsiInfo != NULL) {
|
|
|
|
for (j = 0; j < DeviceExtension->NumberOfBuses; j++) {
|
|
|
|
if (!DeviceExtension->ScsiInfo->BusScanData[j]) {
|
|
continue;
|
|
}
|
|
|
|
lunInfo = DeviceExtension->ScsiInfo->BusScanData[j]->LunInfoList;
|
|
|
|
//
|
|
// Delete the lun info elements from the list.
|
|
//
|
|
|
|
while (!lunInfo) {
|
|
|
|
//
|
|
// Save the forward pointer.
|
|
//
|
|
|
|
tempPointer = lunInfo->NextLunInfo;
|
|
ExFreePool(lunInfo);
|
|
lunInfo = tempPointer;
|
|
}
|
|
|
|
//
|
|
// Free the SCSI_BUS_SCAN_DATA structure.
|
|
//
|
|
|
|
ExFreePool(DeviceExtension->ScsiInfo->BusScanData[j]);
|
|
}
|
|
|
|
//
|
|
// Free the SCSI_CONFIGURATION_INFO structure.
|
|
//
|
|
|
|
ExFreePool(DeviceExtension->ScsiInfo);
|
|
}
|
|
|
|
//
|
|
// Free the configuration information structure.
|
|
//
|
|
|
|
if (DeviceExtension->ConfigurationInformation) {
|
|
|
|
ExFreePool(DeviceExtension->ConfigurationInformation);
|
|
}
|
|
|
|
//
|
|
// Free the logical unit extension structures.
|
|
//
|
|
|
|
for(j = 0; j < NUMBER_LOGICAL_UNIT_BINS; j++) {
|
|
|
|
while (DeviceExtension->LogicalUnitList[j] != NULL) {
|
|
|
|
tempPointer = DeviceExtension->LogicalUnitList[j];
|
|
DeviceExtension->LogicalUnitList[j] =
|
|
DeviceExtension->LogicalUnitList[j]->NextLogicalUnit;
|
|
|
|
ExFreePool(tempPointer);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Free the common buffer.
|
|
//
|
|
|
|
if (DeviceExtension->SrbExtensionBuffer != NULL &&
|
|
DeviceExtension->CommonBufferSize != 0) {
|
|
|
|
if (DeviceExtension->DmaAdapterObject == NULL) {
|
|
|
|
//
|
|
// Since there is no adapter just free the non-paged pool.
|
|
//
|
|
|
|
ExFreePool(DeviceExtension->SrbExtensionBuffer);
|
|
|
|
} else {
|
|
|
|
HalFreeCommonBuffer(
|
|
DeviceExtension->DmaAdapterObject,
|
|
DeviceExtension->CommonBufferSize,
|
|
DeviceExtension->PhysicalCommonBuffer,
|
|
DeviceExtension->SrbExtensionBuffer,
|
|
FALSE
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Free the SRB data array.
|
|
//
|
|
|
|
if (DeviceExtension->SrbData != NULL) {
|
|
|
|
ExFreePool(DeviceExtension->SrbData);
|
|
}
|
|
|
|
//
|
|
// Unmap any mapped areas.
|
|
//
|
|
|
|
while (DeviceExtension->MappedAddressList != NULL) {
|
|
|
|
MmUnmapIoSpace(
|
|
DeviceExtension->MappedAddressList->MappedAddress,
|
|
DeviceExtension->MappedAddressList->NumberOfBytes
|
|
);
|
|
|
|
tempPointer = DeviceExtension->MappedAddressList;
|
|
DeviceExtension->MappedAddressList =
|
|
DeviceExtension->MappedAddressList->NextMappedAddress;
|
|
|
|
ExFreePool(tempPointer);
|
|
|
|
}
|
|
|
|
//
|
|
// Delete the device object.
|
|
//
|
|
|
|
IoDeleteDevice(DeviceExtension->DeviceObject);
|
|
}
|
|
|
|
NTSTATUS
|
|
SpInitializeConfiguration(
|
|
IN PDEVICE_EXTENSION DeviceExtension,
|
|
IN PHW_INITIALIZATION_DATA HwInitData,
|
|
IN PCONFIGURATION_CONTEXT Context,
|
|
IN 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.
|
|
|
|
HwInitData - Supplies the initial miniport data.
|
|
|
|
Context - Supplies the context data used access calls.
|
|
|
|
ConfigInfo - Supplies the configuration information to be
|
|
initialized.
|
|
|
|
InitialCall - Indicates that this is first call to this function with this
|
|
PORT CONFIGURATION structure. If InitialCall is TRUE, then the static
|
|
fields in this structure will be intialized. Otherwise, they are assumed
|
|
to be set up already.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Success if requested bus type exists and additional
|
|
configuration information is available.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG j;
|
|
NTSTATUS status;
|
|
UNICODE_STRING unicodeString;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
PCONFIGURATION_INFORMATION configurationInformation;
|
|
HANDLE key;
|
|
HANDLE rootKey;
|
|
BOOLEAN found;
|
|
ANSI_STRING ansiString;
|
|
CCHAR deviceBuffer[16];
|
|
CCHAR nodeBuffer[SP_REG_BUFFER_SIZE];
|
|
|
|
//
|
|
// 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));
|
|
|
|
RtlZeroMemory(
|
|
Context->AccessRanges,
|
|
HwInitData->NumberOfAccessRanges * sizeof(ACCESS_RANGE)
|
|
);
|
|
|
|
ConfigInfo->Length = sizeof(PORT_CONFIGURATION_INFORMATION);
|
|
ConfigInfo->AdapterInterfaceType = HwInitData->AdapterInterfaceType;
|
|
ConfigInfo->InterruptMode = Latched;
|
|
ConfigInfo->MaximumTransferLength = SP_UNINITIALIZED_VALUE;
|
|
ConfigInfo->DmaChannel = SP_UNINITIALIZED_VALUE;
|
|
ConfigInfo->DmaPort = SP_UNINITIALIZED_VALUE;
|
|
ConfigInfo->NumberOfAccessRanges = HwInitData->NumberOfAccessRanges;
|
|
ConfigInfo->MaximumNumberOfTargets = 8;
|
|
|
|
//
|
|
// Save away the some of the attributes.
|
|
//
|
|
|
|
ConfigInfo->NeedPhysicalAddresses = HwInitData->NeedPhysicalAddresses;
|
|
ConfigInfo->MapBuffers = HwInitData->MapBuffers;
|
|
ConfigInfo->AutoRequestSense = HwInitData->AutoRequestSense;
|
|
ConfigInfo->ReceiveEvent = HwInitData->ReceiveEvent;
|
|
ConfigInfo->TaggedQueuing = HwInitData->TaggedQueuing;
|
|
ConfigInfo->MultipleRequestPerLu = HwInitData->MultipleRequestPerLu;
|
|
|
|
//
|
|
// Indicate the current AT disk usage.
|
|
//
|
|
|
|
configurationInformation = IoGetConfigurationInformation();
|
|
ConfigInfo->AtdiskPrimaryClaimed = configurationInformation->AtDiskPrimaryAddressClaimed;
|
|
ConfigInfo->AtdiskSecondaryClaimed = configurationInformation->AtDiskSecondaryAddressClaimed;
|
|
|
|
for (j = 0; j < 8; j++) {
|
|
ConfigInfo->InitiatorBusId[j] = (CCHAR)SP_UNINITIALIZED_VALUE;
|
|
}
|
|
}
|
|
|
|
ConfigInfo->NumberOfPhysicalBreaks = SP_NORMAL_PHYSICAL_BREAK_VALUE;
|
|
|
|
//
|
|
// Clear some of the context information.
|
|
//
|
|
|
|
Context->DisableTaggedQueueing = FALSE;
|
|
Context->DisableMultipleLu = FALSE;
|
|
|
|
//
|
|
// Record the system bus number.
|
|
//
|
|
|
|
ConfigInfo->SystemIoBusNumber = Context->BusNumber;
|
|
|
|
NewAdapter:
|
|
|
|
//
|
|
// If this is an internal adapter, check for data returned by dectection
|
|
// code.
|
|
//
|
|
|
|
if (ConfigInfo->AdapterInterfaceType == Internal) {
|
|
|
|
//
|
|
// Open the hardware data base key.
|
|
//
|
|
|
|
InitializeObjectAttributes( &objectAttributes,
|
|
DeviceExtension->DeviceObject->DriverObject->HardwareDatabase,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
(PSECURITY_DESCRIPTOR) NULL
|
|
);
|
|
|
|
status = ZwOpenKey(
|
|
&rootKey,
|
|
KEY_READ,
|
|
&objectAttributes
|
|
);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Create the relative name off the hardware data base.
|
|
//
|
|
|
|
sprintf(nodeBuffer, "ScsiAdapter\\%d",
|
|
Context->AdapterNumber);
|
|
|
|
RtlInitAnsiString(&ansiString, nodeBuffer);
|
|
|
|
status = RtlAnsiStringToUnicodeString(&unicodeString, &ansiString, TRUE);
|
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
ZwClose(rootKey);
|
|
return(status);
|
|
}
|
|
|
|
//
|
|
// Open the device specific node which was created by the frimware.
|
|
//
|
|
|
|
InitializeObjectAttributes( &objectAttributes,
|
|
&unicodeString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
rootKey,
|
|
(PSECURITY_DESCRIPTOR) NULL
|
|
);
|
|
|
|
status = ZwOpenKey(
|
|
&key,
|
|
KEY_READ,
|
|
&objectAttributes
|
|
);
|
|
|
|
//
|
|
// Free the unused resources.
|
|
//
|
|
|
|
RtlFreeUnicodeString(&unicodeString);
|
|
ZwClose(rootKey);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
if (Context->LastAdapterNumber != Context->AdapterNumber) {
|
|
|
|
DebugPrint((1, "SpInitializeConfiguration: Found hardware information at %s\n",
|
|
nodeBuffer));
|
|
|
|
SpParseDevice(
|
|
DeviceExtension,
|
|
key,
|
|
ConfigInfo,
|
|
Context,
|
|
nodeBuffer
|
|
);
|
|
|
|
//
|
|
// Set the bus number to zero. This is because the
|
|
// configuration code sets the bus number equal to the
|
|
// adapter number which is not valid for internal bus
|
|
// numbers. This will not work if there really is multiple
|
|
// bus numbers.
|
|
//
|
|
|
|
Context->BusNumber = 0;
|
|
|
|
} else {
|
|
|
|
//
|
|
// The specified device was not found update the
|
|
// adapter number and try again.
|
|
//
|
|
|
|
Context->AdapterNumber++;
|
|
goto NewAdapter;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Go away.
|
|
//
|
|
|
|
return(STATUS_DEVICE_DOES_NOT_EXIST);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check for device parameters.
|
|
//
|
|
|
|
key = NULL;
|
|
if (Context->Parameter) {
|
|
ExFreePool(Context->Parameter);
|
|
Context->Parameter = NULL;
|
|
}
|
|
|
|
if (Context->ServiceKey != NULL) {
|
|
|
|
sprintf(deviceBuffer, "Device%d", Context->AdapterNumber);
|
|
RtlInitAnsiString(&ansiString, deviceBuffer);
|
|
status = RtlAnsiStringToUnicodeString(&unicodeString, &ansiString, TRUE);
|
|
if (!NT_SUCCESS(status)) {
|
|
return(status);
|
|
}
|
|
|
|
//
|
|
// Open the device specific node.
|
|
//
|
|
|
|
InitializeObjectAttributes( &objectAttributes,
|
|
&unicodeString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
Context->ServiceKey,
|
|
(PSECURITY_DESCRIPTOR) NULL
|
|
);
|
|
|
|
status = ZwOpenKey(
|
|
&key,
|
|
KEY_READ,
|
|
&objectAttributes
|
|
);
|
|
|
|
RtlFreeUnicodeString(&unicodeString);
|
|
}
|
|
|
|
//
|
|
// First parse the device information.
|
|
//
|
|
|
|
if (Context->DeviceKey != NULL) {
|
|
|
|
SpParseDevice(DeviceExtension,
|
|
Context->DeviceKey,
|
|
ConfigInfo,
|
|
Context,
|
|
nodeBuffer);
|
|
}
|
|
|
|
//
|
|
// Next parse the specific device information so that it can override the
|
|
// general device information. This node is not used if the last adapter
|
|
// was not found.
|
|
//
|
|
|
|
if (key != NULL) {
|
|
if (Context->LastAdapterNumber != Context->AdapterNumber) {
|
|
|
|
SpParseDevice(DeviceExtension,
|
|
key,
|
|
ConfigInfo,
|
|
Context,
|
|
nodeBuffer);
|
|
|
|
ZwClose(key);
|
|
|
|
} else {
|
|
|
|
//
|
|
// The specified device was not found.
|
|
// Update the adapter number and try again.
|
|
//
|
|
|
|
Context->AdapterNumber++;
|
|
ZwClose(key);
|
|
goto NewAdapter;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Remember the last adapter number to determine if progress is
|
|
// being made.
|
|
//
|
|
|
|
Context->LastAdapterNumber = Context->AdapterNumber;
|
|
|
|
//
|
|
// Determine if the requested bus type is on this system.
|
|
//
|
|
|
|
found = FALSE;
|
|
|
|
status = IoQueryDeviceDescription(&HwInitData->AdapterInterfaceType,
|
|
&Context->BusNumber,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
SpConfiguarionCallout,
|
|
&found);
|
|
|
|
//
|
|
// If the request failed, then assume this type of bus is not here.
|
|
//
|
|
|
|
if (!found) {
|
|
|
|
INTERFACE_TYPE interfaceType = Eisa;
|
|
|
|
if (HwInitData->AdapterInterfaceType == Isa) {
|
|
|
|
//
|
|
// Check for an Eisa bus.
|
|
//
|
|
|
|
status = IoQueryDeviceDescription(&interfaceType,
|
|
&Context->BusNumber,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
SpConfiguarionCallout,
|
|
&found);
|
|
|
|
//
|
|
// If the request failed, then assume this type of bus is not here.
|
|
//
|
|
|
|
if (found) {
|
|
return(STATUS_SUCCESS);
|
|
} else {
|
|
return(STATUS_DEVICE_DOES_NOT_EXIST);
|
|
}
|
|
|
|
} else {
|
|
return(STATUS_DEVICE_DOES_NOT_EXIST);
|
|
}
|
|
|
|
} else {
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
}
|
|
|
|
PCM_RESOURCE_LIST
|
|
SpBuildResourceList(
|
|
PDEVICE_EXTENSION DeviceExtension,
|
|
PPORT_CONFIGURATION_INFORMATION ConfigInfo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Creates a resource list which is used to query or report resource usage
|
|
in the system
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - Pointer to the port's deviceExtension.
|
|
|
|
ConfigInfo - Pointer to the information structure filled out by the
|
|
miniport findAdapter routine.
|
|
|
|
Return Value:
|
|
|
|
Returns a pointer to a filled up resource list, or 0 if the call failed.
|
|
|
|
Note:
|
|
|
|
Memory is allocated by the routine for the resourcelist. It must be
|
|
freed up by the caller by calling ExFreePool();
|
|
|
|
--*/
|
|
{
|
|
PCM_RESOURCE_LIST resourceList;
|
|
PCM_PARTIAL_RESOURCE_DESCRIPTOR resourceDescriptor;
|
|
PCONFIGURATION_INFORMATION configurationInformation;
|
|
PACCESS_RANGE accessRange;
|
|
ULONG listLength = 0;
|
|
ULONG hasInterrupt;
|
|
ULONG i;
|
|
BOOLEAN hasDma;
|
|
|
|
//
|
|
// Indicate the current AT disk usage.
|
|
//
|
|
|
|
configurationInformation = IoGetConfigurationInformation();
|
|
|
|
if (ConfigInfo->AtdiskPrimaryClaimed) {
|
|
configurationInformation->AtDiskPrimaryAddressClaimed = TRUE;
|
|
}
|
|
|
|
if (ConfigInfo->AtdiskSecondaryClaimed) {
|
|
configurationInformation->AtDiskSecondaryAddressClaimed = TRUE;
|
|
}
|
|
|
|
//
|
|
// Determine if adapter uses DMA. Only report the DMA channel if a
|
|
// channel number is used.
|
|
//
|
|
|
|
if (ConfigInfo->DmaChannel != SP_UNINITIALIZED_VALUE ||
|
|
ConfigInfo->DmaPort != SP_UNINITIALIZED_VALUE) {
|
|
|
|
hasDma = TRUE;
|
|
listLength++;
|
|
|
|
} else {
|
|
|
|
hasDma = FALSE;
|
|
}
|
|
|
|
if (DeviceExtension->HwInterrupt == NULL ||
|
|
(ConfigInfo->BusInterruptLevel == 0 &&
|
|
ConfigInfo->BusInterruptVector == 0)) {
|
|
|
|
hasInterrupt = 0;
|
|
|
|
} else {
|
|
|
|
hasInterrupt = 1;
|
|
listLength++;
|
|
}
|
|
|
|
//
|
|
// Detemine whether the second interrupt is used.
|
|
//
|
|
|
|
if (DeviceExtension->HwInterrupt != NULL &&
|
|
(ConfigInfo->BusInterruptLevel2 != 0 ||
|
|
ConfigInfo->BusInterruptVector2 != 0)) {
|
|
|
|
hasInterrupt++;
|
|
listLength++;
|
|
}
|
|
|
|
//
|
|
// Determine the number of access ranges used.
|
|
//
|
|
|
|
accessRange = &((*(ConfigInfo->AccessRanges))[0]);
|
|
for (i = 0; i < ConfigInfo->NumberOfAccessRanges; i++) {
|
|
|
|
if (accessRange->RangeLength != 0) {
|
|
listLength++;
|
|
}
|
|
|
|
accessRange++;
|
|
}
|
|
|
|
resourceList = (PCM_RESOURCE_LIST) ExAllocatePool(PagedPool,
|
|
sizeof(CM_RESOURCE_LIST) + (listLength - 1)
|
|
* sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR));
|
|
|
|
//
|
|
// Return NULL if the structure could not be allocated.
|
|
// Otherwise, fill it out.
|
|
//
|
|
|
|
if (!resourceList) {
|
|
|
|
return NULL;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Clear the resource list.
|
|
//
|
|
|
|
RtlZeroMemory(
|
|
resourceList,
|
|
sizeof(CM_RESOURCE_LIST) + (listLength - 1)
|
|
* sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR)
|
|
);
|
|
|
|
//
|
|
// Initialize the various fields.
|
|
//
|
|
|
|
resourceList->Count = 1;
|
|
resourceList->List[0].InterfaceType = ConfigInfo->AdapterInterfaceType;
|
|
resourceList->List[0].BusNumber = ConfigInfo->SystemIoBusNumber;
|
|
resourceList->List[0].PartialResourceList.Count = listLength;
|
|
resourceDescriptor =
|
|
resourceList->List[0].PartialResourceList.PartialDescriptors;
|
|
|
|
//
|
|
// For each entry in the access range, fill in an entry in the
|
|
// resource list
|
|
//
|
|
|
|
for (i = 0; i < ConfigInfo->NumberOfAccessRanges; i++) {
|
|
|
|
accessRange = &((*(ConfigInfo->AccessRanges))[i]);
|
|
|
|
if (accessRange->RangeLength == 0) {
|
|
|
|
//
|
|
// Skip the empty ranges.
|
|
//
|
|
|
|
continue;
|
|
}
|
|
|
|
if (accessRange->RangeInMemory) {
|
|
resourceDescriptor->Type = CmResourceTypeMemory;
|
|
resourceDescriptor->Flags = CM_RESOURCE_MEMORY_READ_WRITE;
|
|
} else {
|
|
resourceDescriptor->Type = CmResourceTypePort;
|
|
resourceDescriptor->Flags = CM_RESOURCE_PORT_IO;
|
|
}
|
|
|
|
resourceDescriptor->ShareDisposition = CmResourceShareDeviceExclusive;
|
|
|
|
resourceDescriptor->u.Memory.Start = accessRange->RangeStart;
|
|
resourceDescriptor->u.Memory.Length = accessRange->RangeLength;
|
|
|
|
|
|
resourceDescriptor++;
|
|
}
|
|
|
|
//
|
|
// Fill in the entry for the interrupt if it was present.
|
|
//
|
|
|
|
if (hasInterrupt) {
|
|
|
|
resourceDescriptor->Type = CmResourceTypeInterrupt;
|
|
|
|
if (ConfigInfo->AdapterInterfaceType == MicroChannel ||
|
|
ConfigInfo->InterruptMode == LevelSensitive) {
|
|
resourceDescriptor->ShareDisposition = CmResourceShareShared;
|
|
resourceDescriptor->Flags = CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE;
|
|
} else {
|
|
resourceDescriptor->ShareDisposition = CmResourceShareDeviceExclusive;
|
|
resourceDescriptor->Flags = CM_RESOURCE_INTERRUPT_LATCHED;
|
|
}
|
|
|
|
resourceDescriptor->u.Interrupt.Level =
|
|
ConfigInfo->BusInterruptLevel;
|
|
resourceDescriptor->u.Interrupt.Vector =
|
|
ConfigInfo->BusInterruptVector;
|
|
resourceDescriptor->u.Interrupt.Affinity = 0;
|
|
|
|
resourceDescriptor++;
|
|
--hasInterrupt;
|
|
}
|
|
|
|
if (hasInterrupt) {
|
|
|
|
resourceDescriptor->Type = CmResourceTypeInterrupt;
|
|
|
|
if (ConfigInfo->AdapterInterfaceType == MicroChannel ||
|
|
ConfigInfo->InterruptMode2 == LevelSensitive) {
|
|
resourceDescriptor->ShareDisposition = CmResourceShareShared;
|
|
resourceDescriptor->Flags = CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE;
|
|
} else {
|
|
resourceDescriptor->ShareDisposition = CmResourceShareDeviceExclusive;
|
|
resourceDescriptor->Flags = CM_RESOURCE_INTERRUPT_LATCHED;
|
|
}
|
|
|
|
resourceDescriptor->u.Interrupt.Level =
|
|
ConfigInfo->BusInterruptLevel2;
|
|
resourceDescriptor->u.Interrupt.Vector =
|
|
ConfigInfo->BusInterruptVector2;
|
|
resourceDescriptor->u.Interrupt.Affinity = 0;
|
|
|
|
resourceDescriptor++;
|
|
}
|
|
|
|
if (hasDma) {
|
|
|
|
//
|
|
// Fill out DMA information;
|
|
//
|
|
|
|
resourceDescriptor->Type = CmResourceTypeDma;
|
|
resourceDescriptor->ShareDisposition = CmResourceShareDeviceExclusive;
|
|
resourceDescriptor->u.Dma.Channel = ConfigInfo->DmaChannel;
|
|
resourceDescriptor->u.Dma.Port = ConfigInfo->DmaPort;
|
|
resourceDescriptor->Flags = 0;
|
|
|
|
//
|
|
// Set the initialized values to zero.
|
|
//
|
|
|
|
if (ConfigInfo->DmaChannel == SP_UNINITIALIZED_VALUE) {
|
|
resourceDescriptor->u.Dma.Channel = 0;
|
|
}
|
|
|
|
if (ConfigInfo->DmaPort == SP_UNINITIALIZED_VALUE) {
|
|
resourceDescriptor->u.Dma.Port = 0;
|
|
}
|
|
}
|
|
|
|
return resourceList;
|
|
}
|
|
|
|
} // end SpBuildResourceList()
|
|
|
|
|
|
VOID
|
|
SpParseDevice(
|
|
IN PDEVICE_EXTENSION DeviceExtension,
|
|
IN HANDLE Key,
|
|
IN PPORT_CONFIGURATION_INFORMATION ConfigInfo,
|
|
IN PCONFIGURATION_CONTEXT Context,
|
|
IN PUCHAR Buffer
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine parses a device key node and updates the configuration
|
|
information.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - Supplies the device extension.
|
|
|
|
Key - Supplies an open key to the device node.
|
|
|
|
ConfigInfo - Supplies the configuration information to be
|
|
initialized.
|
|
|
|
Context - Supplies the configuration context.
|
|
|
|
Buffer - Supplies a scratch buffer for temporary data storage.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
PKEY_VALUE_FULL_INFORMATION keyValueInformation;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PCM_FULL_RESOURCE_DESCRIPTOR resource;
|
|
PCM_PARTIAL_RESOURCE_DESCRIPTOR descriptor;
|
|
PCM_SCSI_DEVICE_DATA scsiData;
|
|
UNICODE_STRING unicodeString;
|
|
ANSI_STRING ansiString;
|
|
ULONG length;
|
|
ULONG index = 0;
|
|
ULONG rangeCount = 0;
|
|
ULONG count;
|
|
|
|
keyValueInformation = (PKEY_VALUE_FULL_INFORMATION) Buffer;
|
|
|
|
//
|
|
// Look at each of the values in the device node.
|
|
//
|
|
|
|
while(TRUE){
|
|
|
|
status = ZwEnumerateValueKey(
|
|
Key,
|
|
index,
|
|
KeyValueFullInformation,
|
|
Buffer,
|
|
SP_REG_BUFFER_SIZE,
|
|
&length
|
|
);
|
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
#if DBG
|
|
if (status != STATUS_NO_MORE_ENTRIES) {
|
|
DebugPrint((1, "SpParseDevice: ZwEnumerateValueKey failed. Status: %lx", status));
|
|
}
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Update the index for the next time around the loop.
|
|
//
|
|
|
|
index++;
|
|
|
|
//
|
|
// Check that the length is reasonable.
|
|
//
|
|
|
|
if (keyValueInformation->Type == REG_DWORD &&
|
|
keyValueInformation->DataLength != sizeof(ULONG)) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Check for a maximum lu number.
|
|
//
|
|
|
|
if (_wcsnicmp(keyValueInformation->Name, L"MaximumLogicalUnit",
|
|
keyValueInformation->NameLength/2) == 0) {
|
|
|
|
if (keyValueInformation->Type != REG_DWORD) {
|
|
|
|
DebugPrint((1, "SpParseDevice: Bad data type for MaximumLogicalUnit.\n"));
|
|
continue;
|
|
}
|
|
|
|
DeviceExtension->MaxLuCount = *((PUCHAR)
|
|
(Buffer + keyValueInformation->DataOffset));
|
|
DebugPrint((1, "SpParseDevice: MaximumLogicalUnit = %d found.\n",
|
|
DeviceExtension->MaxLuCount));
|
|
|
|
//
|
|
// If the value is out of bounds, then reset it.
|
|
//
|
|
|
|
if (DeviceExtension->MaxLuCount > SCSI_MAXIMUM_LOGICAL_UNITS) {
|
|
DeviceExtension->MaxLuCount = SCSI_MAXIMUM_LOGICAL_UNITS;
|
|
}
|
|
}
|
|
|
|
if (_wcsnicmp(keyValueInformation->Name, L"InitiatorTargetId",
|
|
keyValueInformation->NameLength/2) == 0) {
|
|
|
|
if (keyValueInformation->Type != REG_DWORD) {
|
|
|
|
DebugPrint((1, "SpParseDevice: Bad data type for InitiatorTargetId.\n"));
|
|
continue;
|
|
}
|
|
|
|
ConfigInfo->InitiatorBusId[0] = *((PUCHAR)
|
|
(Buffer + keyValueInformation->DataOffset));
|
|
DebugPrint((1, "SpParseDevice: InitiatorTargetId = %d found.\n",
|
|
ConfigInfo->InitiatorBusId[0]));
|
|
|
|
//
|
|
// If the value is out of bounds, then reset it.
|
|
//
|
|
|
|
if (ConfigInfo->InitiatorBusId[0] > ConfigInfo->MaximumNumberOfTargets - 1) {
|
|
ConfigInfo->InitiatorBusId[0] = (CCHAR)SP_UNINITIALIZED_VALUE;
|
|
}
|
|
}
|
|
|
|
if (_wcsnicmp(keyValueInformation->Name, L"ScsiDebug",
|
|
keyValueInformation->NameLength/2) == 0) {
|
|
|
|
if (keyValueInformation->Type != REG_DWORD) {
|
|
|
|
DebugPrint((1, "SpParseDevice: Bad data type for ScsiDebug.\n"));
|
|
continue;
|
|
}
|
|
#if DBG
|
|
ScsiDebug = *((PULONG) (Buffer + keyValueInformation->DataOffset));
|
|
#endif
|
|
}
|
|
|
|
if (_wcsnicmp(keyValueInformation->Name, L"BreakPointOnEntry",
|
|
keyValueInformation->NameLength/2) == 0) {
|
|
|
|
DebugPrint((0, "SpParseDevice: Break point requested on entry.\n"));
|
|
DbgBreakPoint();
|
|
}
|
|
|
|
//
|
|
// Check for disabled synchonous tranfers.
|
|
//
|
|
|
|
if (_wcsnicmp(keyValueInformation->Name, L"DisableSynchronousTransfers",
|
|
keyValueInformation->NameLength/2) == 0) {
|
|
|
|
DeviceExtension->SrbFlags |= SRB_FLAGS_DISABLE_SYNCH_TRANSFER;
|
|
DebugPrint((1, "SpParseDevice: Disabling synchonous transfers\n"));
|
|
}
|
|
|
|
//
|
|
// Check for disabled disconnects.
|
|
//
|
|
|
|
if (_wcsnicmp(keyValueInformation->Name, L"DisableDisconnects",
|
|
keyValueInformation->NameLength/2) == 0) {
|
|
|
|
DeviceExtension->SrbFlags |= SRB_FLAGS_DISABLE_DISCONNECT;
|
|
DebugPrint((1, "SpParseDevice: Disabling disconnects\n"));
|
|
}
|
|
|
|
//
|
|
// Check for disabled tagged queuing.
|
|
//
|
|
|
|
if (_wcsnicmp(keyValueInformation->Name, L"DisableTaggedQueuing",
|
|
keyValueInformation->NameLength/2) == 0) {
|
|
|
|
Context->DisableTaggedQueueing = TRUE;
|
|
DebugPrint((1, "SpParseDevice: Disabling tagged queueing\n"));
|
|
}
|
|
|
|
//
|
|
// Check for disabled multiple requests per logical unit.
|
|
//
|
|
|
|
if (_wcsnicmp(keyValueInformation->Name, L"DisableMultipleRequests",
|
|
keyValueInformation->NameLength/2) == 0) {
|
|
|
|
Context->DisableMultipleLu = TRUE;
|
|
DebugPrint((1, "SpParseDevice: Disabling multiple requests\n"));
|
|
}
|
|
|
|
//
|
|
// Check for driver parameters tranfers.
|
|
//
|
|
|
|
if (_wcsnicmp(keyValueInformation->Name, L"DriverParameters",
|
|
keyValueInformation->NameLength/2) == 0) {
|
|
|
|
if (keyValueInformation->DataLength == 0) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Free any previous driver parameters.
|
|
//
|
|
|
|
if (Context->Parameter != NULL) {
|
|
ExFreePool(Context->Parameter);
|
|
}
|
|
|
|
Context->Parameter =
|
|
ExAllocatePool(NonPagedPool, keyValueInformation->DataLength);
|
|
|
|
if (Context->Parameter != NULL) {
|
|
|
|
if (keyValueInformation->Type != REG_SZ) {
|
|
|
|
//
|
|
// This is some random information just copy it.
|
|
//
|
|
|
|
RtlCopyMemory(
|
|
Context->Parameter,
|
|
(PCCHAR) keyValueInformation + keyValueInformation->DataOffset,
|
|
keyValueInformation->DataLength
|
|
);
|
|
|
|
} else {
|
|
|
|
//
|
|
// This is a unicode string. Convert it to a ANSI string.
|
|
// Initialize the strings.
|
|
//
|
|
|
|
unicodeString.Buffer = (PWSTR) ((PCCHAR) keyValueInformation +
|
|
keyValueInformation->DataOffset);
|
|
unicodeString.Length = (USHORT) keyValueInformation->DataLength;
|
|
unicodeString.MaximumLength = (USHORT) keyValueInformation->DataLength;
|
|
|
|
ansiString.Buffer = (PCHAR) Context->Parameter;
|
|
ansiString.Length = 0;
|
|
ansiString.MaximumLength = (USHORT) keyValueInformation->DataLength;
|
|
|
|
status = RtlUnicodeStringToAnsiString(
|
|
&ansiString,
|
|
&unicodeString,
|
|
FALSE
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Free the context.
|
|
//
|
|
|
|
ExFreePool(Context->Parameter);
|
|
Context->Parameter = NULL;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
DebugPrint((1, "SpParseDevice: Found driver parameter.\n"));
|
|
}
|
|
|
|
//
|
|
// See if an entry for Maximum Scatter-Gather List has been
|
|
// set.
|
|
//
|
|
|
|
if (_wcsnicmp(keyValueInformation->Name, L"MaximumSGList",
|
|
keyValueInformation->NameLength/2) == 0) {
|
|
|
|
if (keyValueInformation->Type != REG_DWORD) {
|
|
|
|
DebugPrint((1, "SpParseDevice: Bad data type for MaximumSGList.\n"));
|
|
continue;
|
|
}
|
|
|
|
ConfigInfo->NumberOfPhysicalBreaks = *((PUCHAR)(Buffer + keyValueInformation->DataOffset));
|
|
DebugPrint((1, "SpParseDevice: MaximumSGList = %d found.\n",
|
|
ConfigInfo->NumberOfPhysicalBreaks));
|
|
|
|
//
|
|
// If the value is out of bounds, then reset it.
|
|
//
|
|
|
|
if (ConfigInfo->NumberOfPhysicalBreaks > SCSI_MAXIMUM_PHYSICAL_BREAKS) {
|
|
ConfigInfo->NumberOfPhysicalBreaks = SCSI_MAXIMUM_PHYSICAL_BREAKS;
|
|
} else if (ConfigInfo->NumberOfPhysicalBreaks < SCSI_MINIMUM_PHYSICAL_BREAKS) {
|
|
ConfigInfo->NumberOfPhysicalBreaks = SCSI_MINIMUM_PHYSICAL_BREAKS;
|
|
}
|
|
}
|
|
|
|
//
|
|
// See if an entry for Number of request has been set.
|
|
//
|
|
|
|
if (_wcsnicmp(keyValueInformation->Name, L"NumberOfRequests",
|
|
keyValueInformation->NameLength/2) == 0) {
|
|
|
|
if (keyValueInformation->Type != REG_DWORD) {
|
|
|
|
DebugPrint((1, "SpParseDevice: Bad data type for NumberOfRequests.\n"));
|
|
continue;
|
|
}
|
|
|
|
DeviceExtension->NumberOfRequests = *((PUCHAR)(Buffer + keyValueInformation->DataOffset));
|
|
DebugPrint((1, "SpParseDevice: Number Of Requests = %d found.\n",
|
|
DeviceExtension->NumberOfRequests));
|
|
|
|
//
|
|
// If the value is out of bounds, then reset it.
|
|
//
|
|
|
|
if (DeviceExtension->NumberOfRequests < MINIMUM_SRB_EXTENSIONS) {
|
|
DeviceExtension->NumberOfRequests = MINIMUM_SRB_EXTENSIONS;
|
|
} else if (DeviceExtension->NumberOfRequests > MAXIMUM_SRB_EXTENSIONS) {
|
|
DeviceExtension->NumberOfRequests = MAXIMUM_SRB_EXTENSIONS;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check for resource list.
|
|
//
|
|
|
|
if (_wcsnicmp(keyValueInformation->Name, L"ResourceList",
|
|
keyValueInformation->NameLength/2) == 0 ||
|
|
_wcsnicmp(keyValueInformation->Name, L"Configuration Data",
|
|
keyValueInformation->NameLength/2) == 0 ) {
|
|
|
|
if (keyValueInformation->Type != REG_FULL_RESOURCE_DESCRIPTOR ||
|
|
keyValueInformation->DataLength < sizeof(REG_FULL_RESOURCE_DESCRIPTOR)) {
|
|
|
|
DebugPrint((1, "SpParseDevice: Bad data type for ResourceList.\n"));
|
|
continue;
|
|
} else {
|
|
DebugPrint((1, "SpParseDevice: ResourceList found!\n"));
|
|
}
|
|
|
|
resource = (PCM_FULL_RESOURCE_DESCRIPTOR)
|
|
(Buffer + keyValueInformation->DataOffset);
|
|
|
|
//
|
|
// Set the bus number equal to the bus number for the
|
|
// resouce. Note the context value is also set to the
|
|
// new bus number.
|
|
//
|
|
|
|
Context->BusNumber = resource->BusNumber;
|
|
ConfigInfo->SystemIoBusNumber = resource->BusNumber;
|
|
|
|
//
|
|
// Walk the resource list and update the configuration.
|
|
//
|
|
|
|
for (count = 0; count < resource->PartialResourceList.Count; count++) {
|
|
descriptor = &resource->PartialResourceList.PartialDescriptors[count];
|
|
|
|
//
|
|
// Verify size is ok.
|
|
//
|
|
|
|
if ((ULONG)((PCHAR) (descriptor + 1) - (PCHAR) resource) >
|
|
keyValueInformation->DataLength) {
|
|
|
|
DebugPrint((1, "SpParseDevice: Resource data too small.\n"));
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Switch on descriptor type;
|
|
//
|
|
|
|
switch (descriptor->Type) {
|
|
case CmResourceTypePort:
|
|
|
|
if (rangeCount >= ConfigInfo->NumberOfAccessRanges) {
|
|
DebugPrint((1, "SpParseDevice: Too many access ranges.\n"));
|
|
continue;
|
|
}
|
|
|
|
Context->AccessRanges[rangeCount].RangeStart =
|
|
descriptor->u.Port.Start;
|
|
Context->AccessRanges[rangeCount].RangeLength =
|
|
descriptor->u.Port.Length;
|
|
Context->AccessRanges[rangeCount].RangeInMemory = FALSE;
|
|
rangeCount++;
|
|
|
|
break;
|
|
|
|
case CmResourceTypeMemory:
|
|
|
|
if (rangeCount >= ConfigInfo->NumberOfAccessRanges) {
|
|
DebugPrint((1, "SpParseDevice: Too many access ranges.\n"));
|
|
continue;
|
|
}
|
|
|
|
Context->AccessRanges[rangeCount].RangeStart =
|
|
descriptor->u.Memory.Start;
|
|
|
|
Context->AccessRanges[rangeCount].RangeLength =
|
|
descriptor->u.Memory.Length;
|
|
Context->AccessRanges[rangeCount].RangeInMemory = TRUE;
|
|
rangeCount++;
|
|
|
|
break;
|
|
|
|
case CmResourceTypeInterrupt:
|
|
|
|
ConfigInfo->BusInterruptVector =
|
|
descriptor->u.Interrupt.Vector;
|
|
ConfigInfo->BusInterruptLevel =
|
|
descriptor->u.Interrupt.Level;
|
|
break;
|
|
|
|
case CmResourceTypeDma:
|
|
|
|
ConfigInfo->DmaChannel = descriptor->u.Dma.Channel;
|
|
ConfigInfo->DmaPort = descriptor->u.Dma.Port;
|
|
break;
|
|
|
|
case CmResourceTypeDeviceSpecific:
|
|
|
|
if (descriptor->u.DeviceSpecificData.DataSize <
|
|
sizeof(CM_SCSI_DEVICE_DATA) ||
|
|
(PCHAR) (descriptor + 1) - (PCHAR) resource +
|
|
descriptor->u.DeviceSpecificData.DataSize >
|
|
keyValueInformation->DataLength) {
|
|
|
|
DebugPrint((1, "SpParseDevice: Device specific resource data too small.\n"));
|
|
break;
|
|
|
|
}
|
|
|
|
//
|
|
// The actual data follows the descriptor.
|
|
//
|
|
|
|
scsiData = (PCM_SCSI_DEVICE_DATA) (descriptor+1);
|
|
ConfigInfo->InitiatorBusId[0] = scsiData->HostIdentifier;
|
|
break;
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
SpConfiguarionCallout(
|
|
IN PVOID Context,
|
|
IN PUNICODE_STRING PathName,
|
|
IN INTERFACE_TYPE BusType,
|
|
IN ULONG BusNumber,
|
|
IN PKEY_VALUE_FULL_INFORMATION *BusInformation,
|
|
IN CONFIGURATION_TYPE ControllerType,
|
|
IN ULONG ControllerNumber,
|
|
IN PKEY_VALUE_FULL_INFORMATION *ControllerInformation,
|
|
IN CONFIGURATION_TYPE PeripheralType,
|
|
IN ULONG PeripheralNumber,
|
|
IN PKEY_VALUE_FULL_INFORMATION *PeripheralInformation
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine indicate that the requested perpherial data was found.
|
|
|
|
Arguments:
|
|
|
|
Context - Supplies a pointer to boolean which is set to TURE when this
|
|
routine is call.
|
|
|
|
The remaining arguments are unsed.
|
|
|
|
Return Value:
|
|
|
|
Returns success.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
*(PBOOLEAN) Context = TRUE;
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
|
|
|
|
PSCSI_BUS_SCAN_DATA
|
|
ScsiBusScan(
|
|
IN PDEVICE_EXTENSION DeviceExtension,
|
|
IN UCHAR ScsiBus
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Send INQUIRIES to each SCSI bus address and store INQUIRY data
|
|
for class drivers.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension
|
|
ScsiBus - which bus on adapter
|
|
|
|
Return Value:
|
|
|
|
SCSI scan data for this bus
|
|
|
|
|
|
--*/
|
|
{
|
|
PSCSI_BUS_SCAN_DATA scsiInfo;
|
|
PLUNINFO lunInfo;
|
|
PLUNINFO lastLunInfo;
|
|
PLUNINFO existingLunInfo;
|
|
UCHAR target;
|
|
UCHAR lun;
|
|
UCHAR bin;
|
|
UCHAR device = 0;
|
|
BOOLEAN existingDevice;
|
|
PLOGICAL_UNIT_EXTENSION logicalUnit;
|
|
NTSTATUS status;
|
|
UCHAR targetNumber;
|
|
|
|
PAGED_CODE();
|
|
|
|
scsiInfo = DeviceExtension->ScsiInfo->BusScanData[ScsiBus];
|
|
|
|
//
|
|
// If this is the first scan then allocate the buffer.
|
|
//
|
|
|
|
if (scsiInfo == NULL) {
|
|
|
|
scsiInfo = ExAllocatePool(NonPagedPool, sizeof(SCSI_BUS_SCAN_DATA));
|
|
|
|
if (scsiInfo == NULL) {
|
|
return(NULL);
|
|
}
|
|
|
|
scsiInfo->Length = sizeof(SCSI_BUS_SCAN_DATA);
|
|
scsiInfo->InitiatorBusId =
|
|
DeviceExtension->ConfigurationInformation->InitiatorBusId[ScsiBus];
|
|
scsiInfo->NumberOfLogicalUnits = 0;
|
|
scsiInfo->LunInfoList = 0;
|
|
lastLunInfo = 0;
|
|
|
|
} else {
|
|
|
|
lunInfo = lastLunInfo = scsiInfo->LunInfoList;
|
|
|
|
//
|
|
// Walk out to tail of list.
|
|
//
|
|
|
|
while (lunInfo) {
|
|
lastLunInfo = lunInfo;
|
|
lunInfo = lunInfo->NextLunInfo;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Create first LUNINFO.
|
|
//
|
|
|
|
lunInfo = ExAllocatePool(PagedPool, sizeof(LUNINFO));
|
|
|
|
if (lunInfo == NULL) {
|
|
|
|
//
|
|
// Insufficient system resources to complete bus scan.
|
|
//
|
|
|
|
DebugPrint((1,"ScsiBusScan: Can't allocate logical unit info buffer\n"));
|
|
return scsiInfo;
|
|
}
|
|
|
|
RtlZeroMemory(lunInfo, sizeof(LUNINFO));
|
|
|
|
//
|
|
// Create first logical unit extension.
|
|
//
|
|
|
|
logicalUnit =
|
|
CreateLogicalUnitExtension(DeviceExtension);
|
|
|
|
//
|
|
// Issue inquiry command to each target id to find devices.
|
|
//
|
|
|
|
for (targetNumber = 0;
|
|
targetNumber < DeviceExtension->MaximumTargetIds;
|
|
targetNumber++) {
|
|
|
|
if (DeviceExtension->Capabilities.AdapterScansDown) {
|
|
|
|
target = (DeviceExtension->MaximumTargetIds - 1) - targetNumber;
|
|
|
|
} else {
|
|
|
|
target = targetNumber;
|
|
}
|
|
|
|
//
|
|
// Do not scan initiator's SCSI address.
|
|
//
|
|
|
|
if (target == scsiInfo->InitiatorBusId) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Search list for existing device at this address.
|
|
//
|
|
|
|
existingDevice = FALSE;
|
|
existingLunInfo = scsiInfo->LunInfoList;
|
|
|
|
while (existingLunInfo) {
|
|
|
|
//
|
|
// Check if address on this bus matches.
|
|
//
|
|
|
|
if (existingLunInfo->TargetId == target) {
|
|
existingDevice = TRUE;
|
|
break;
|
|
}
|
|
|
|
existingLunInfo = existingLunInfo->NextLunInfo;
|
|
}
|
|
|
|
//
|
|
// Do not rescan addresses where a device already exist. System
|
|
// must issue IOCTL to clear this entry before it can be rescanned.
|
|
// This gives the system a chance to clean up old references to a
|
|
// previously existing device.
|
|
//
|
|
|
|
if (existingDevice) {
|
|
continue;
|
|
}
|
|
|
|
for (lun = 0; lun < DeviceExtension->MaxLuCount; lun++) {
|
|
|
|
if (logicalUnit == NULL) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Link logical unit extension on list.
|
|
//
|
|
|
|
bin = (target + lun) % NUMBER_LOGICAL_UNIT_BINS;
|
|
|
|
logicalUnit->NextLogicalUnit =
|
|
DeviceExtension->LogicalUnitList[bin];
|
|
|
|
DeviceExtension->LogicalUnitList[bin] = logicalUnit;
|
|
|
|
|
|
logicalUnit->PathId = lunInfo->PathId = ScsiBus;
|
|
logicalUnit->TargetId = lunInfo->TargetId = target;
|
|
logicalUnit->Lun = lunInfo->Lun = lun;
|
|
|
|
//
|
|
// Set the flag to ensure that IOCTL_SCSI_MINIPORT requests
|
|
// don't attach to this logical unit.
|
|
//
|
|
|
|
logicalUnit->LuFlags |= PD_RESCAN_ACTIVE;
|
|
|
|
//
|
|
// Rezero hardware logical unit extension if it's being recycled.
|
|
//
|
|
|
|
if (DeviceExtension->HwLogicalUnitExtensionSize) {
|
|
|
|
RtlZeroMemory(logicalUnit+1,
|
|
DeviceExtension->HwLogicalUnitExtensionSize);
|
|
}
|
|
|
|
//
|
|
// Issue inquiry command.
|
|
//
|
|
|
|
DebugPrint((2,
|
|
"ScsiBusScan: Try Bus %d TargetId %d LUN %d\n",
|
|
ScsiBus,
|
|
target,
|
|
lun));
|
|
|
|
status = IssueInquiry(DeviceExtension, lunInfo);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
PINQUIRYDATA inquiryData = (PINQUIRYDATA)lunInfo->InquiryData;
|
|
|
|
//
|
|
// Make sure there is really a device here.
|
|
//
|
|
|
|
if (inquiryData->DeviceTypeQualifier == DEVICE_QUALIFIER_NOT_SUPPORTED) {
|
|
|
|
//
|
|
// This logical unit is not supported; continue looking for
|
|
// logical units on this target.
|
|
//
|
|
|
|
//
|
|
// Remove unused logical unit extension from list.
|
|
//
|
|
|
|
DeviceExtension->LogicalUnitList[bin] =
|
|
DeviceExtension->LogicalUnitList[bin]->NextLogicalUnit;
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Clear rescan flag. Since this LogicalUnit will not be freed,
|
|
// the IOCTL_SCSI_MINIPORT requests can safely attach.
|
|
//
|
|
|
|
logicalUnit->LuFlags &= ~PD_RESCAN_ACTIVE;
|
|
|
|
DebugPrint((1,"ScsiBusScan: Found Device %d", device));
|
|
DebugPrint((1," Bus %d", lunInfo->PathId));
|
|
DebugPrint((1," Target Id %d", lunInfo->TargetId));
|
|
DebugPrint((1," LUN %d\n", lunInfo->Lun));
|
|
|
|
//
|
|
// Link LUN information on the end of the list.
|
|
//
|
|
|
|
lunInfo->NextLunInfo = NULL;
|
|
|
|
if (lastLunInfo != NULL) {
|
|
|
|
lastLunInfo->NextLunInfo = lunInfo;
|
|
|
|
} else {
|
|
|
|
scsiInfo->LunInfoList = lunInfo;
|
|
}
|
|
|
|
lastLunInfo = lunInfo;
|
|
|
|
//
|
|
// Set the device object.
|
|
//
|
|
|
|
lunInfo->DeviceObject = DeviceExtension->DeviceObject;
|
|
|
|
//
|
|
// This buffer is used. Get another.
|
|
//
|
|
|
|
lunInfo = ExAllocatePool(PagedPool, sizeof(LUNINFO));
|
|
|
|
if (lunInfo == NULL) {
|
|
|
|
//
|
|
// Insufficient system resources to complete bus scan.
|
|
//
|
|
|
|
DebugPrint((1,"ScsiBusScan: Can't allocate logical unit info buffer\n"));
|
|
break;
|
|
}
|
|
|
|
RtlZeroMemory(lunInfo, sizeof(LUNINFO));
|
|
|
|
//
|
|
// Current logical unit extension claimed.
|
|
// Create next logical unit.
|
|
//
|
|
|
|
logicalUnit =
|
|
CreateLogicalUnitExtension(DeviceExtension);
|
|
|
|
//
|
|
// Increment the devices found count.
|
|
//
|
|
|
|
device++;
|
|
|
|
} else if (status == STATUS_INVALID_DEVICE_REQUEST) {
|
|
|
|
//
|
|
// This request failed but there may be other logical units on
|
|
// this device, so keep looking.
|
|
//
|
|
|
|
//
|
|
// Remove unused logical unit extension from list.
|
|
//
|
|
|
|
DeviceExtension->LogicalUnitList[bin] =
|
|
DeviceExtension->LogicalUnitList[bin]->NextLogicalUnit;
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Remove unused logical unit extension from list.
|
|
//
|
|
|
|
DeviceExtension->LogicalUnitList[bin] =
|
|
DeviceExtension->LogicalUnitList[bin]->NextLogicalUnit;
|
|
|
|
//
|
|
// Skip the rest of the logical units if the inquiry failed.
|
|
//
|
|
|
|
break;
|
|
}
|
|
|
|
|
|
} // end for (lun ...
|
|
|
|
|
|
} // end for (target ...
|
|
|
|
if (logicalUnit != NULL) {
|
|
ExFreePool(logicalUnit);
|
|
|
|
}
|
|
|
|
if (lunInfo != NULL) {
|
|
ExFreePool(lunInfo);
|
|
}
|
|
|
|
scsiInfo->NumberOfLogicalUnits += device;
|
|
DebugPrint((1,
|
|
"ScsiBusScan: Found %d new devices on SCSI bus %d\n",
|
|
device,
|
|
ScsiBus));
|
|
|
|
return scsiInfo;
|
|
|
|
} // end ScsiBusScan()
|
|
|
|
|
|
VOID
|
|
SpBuildDeviceMap(
|
|
IN PDEVICE_EXTENSION DeviceExtension,
|
|
IN PUNICODE_STRING ServiceKey
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The routine takes the inquiry data which has been collected and creates
|
|
a device map for it.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - Pointer to a device map.
|
|
|
|
ServiceKey - Suppiles the name of the service key.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
UNICODE_STRING name;
|
|
UNICODE_STRING unicodeString;
|
|
ANSI_STRING ansiString;
|
|
HANDLE key;
|
|
HANDLE busKey;
|
|
HANDLE targetKey;
|
|
HANDLE lunKey;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
NTSTATUS status;
|
|
ULONG disposition;
|
|
PWSTR start;
|
|
WCHAR buffer[32];
|
|
UCHAR lastTarget;
|
|
PLUNINFO lunInfo;
|
|
ULONG numberOfBuses;
|
|
ULONG i, busNumber;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Create the SCSI key in the device map.
|
|
//
|
|
|
|
RtlInitUnicodeString(&name,
|
|
L"\\Registry\\Machine\\Hardware\\DeviceMap\\Scsi");
|
|
|
|
//
|
|
// Initialize the object for the key.
|
|
//
|
|
|
|
InitializeObjectAttributes(&objectAttributes,
|
|
&name,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
(PSECURITY_DESCRIPTOR) NULL);
|
|
|
|
//
|
|
// Create the key or open it.
|
|
//
|
|
|
|
status = ZwCreateKey(&lunKey,
|
|
KEY_READ | KEY_WRITE,
|
|
&objectAttributes,
|
|
0,
|
|
(PUNICODE_STRING) NULL,
|
|
REG_OPTION_VOLATILE,
|
|
&disposition );
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
return;
|
|
}
|
|
|
|
status = SpCreateNumericKey(lunKey,
|
|
DeviceExtension->PortNumber,
|
|
L"Scsi Port ",
|
|
&key);
|
|
|
|
ZwClose(lunKey);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Indicate if it is a PCCARD.
|
|
//
|
|
|
|
if (DeviceExtension->PCCard) {
|
|
RtlInitUnicodeString(&name, L"PCCARD");
|
|
i = 1;
|
|
|
|
status = ZwSetValueKey(key,
|
|
&name,
|
|
0,
|
|
REG_DWORD,
|
|
&i,
|
|
sizeof(ULONG));
|
|
}
|
|
|
|
//
|
|
// indicate whether DMA is on or not
|
|
//
|
|
{
|
|
ULONG dmaOn = DeviceExtension->DmaAdapterObject ? 1 : 0;
|
|
|
|
RtlInitUnicodeString(&name, L"DMAEnabled");
|
|
|
|
status = ZwSetValueKey(key,
|
|
&name,
|
|
0,
|
|
REG_DWORD,
|
|
&dmaOn,
|
|
4);
|
|
}
|
|
|
|
//
|
|
// Add Interrupt value.
|
|
//
|
|
|
|
if (DeviceExtension->InterruptLevel) {
|
|
|
|
RtlInitUnicodeString(&name, L"Interrupt");
|
|
|
|
status = ZwSetValueKey(key,
|
|
&name,
|
|
0,
|
|
REG_DWORD,
|
|
&DeviceExtension->InterruptLevel,
|
|
4);
|
|
}
|
|
|
|
//
|
|
// Add base IO address value.
|
|
//
|
|
|
|
if (DeviceExtension->IoAddress) {
|
|
|
|
RtlInitUnicodeString(&name, L"IOAddress");
|
|
|
|
status = ZwSetValueKey(key,
|
|
&name,
|
|
0,
|
|
REG_DWORD,
|
|
&DeviceExtension->IoAddress,
|
|
4);
|
|
}
|
|
|
|
if (ServiceKey != NULL) {
|
|
|
|
//
|
|
// Add identifier value. This value is equal to the name of the driver
|
|
// in the from the service key. Note the service key name is not NULL
|
|
// terminated.
|
|
//
|
|
|
|
RtlInitUnicodeString(&name, L"Driver");
|
|
|
|
//
|
|
// Get the name of the driver from the service key name.
|
|
//
|
|
|
|
start = (PWSTR) ((PCHAR) ServiceKey->Buffer + ServiceKey->Length);
|
|
start--;
|
|
while (*start != L'\\' && start > ServiceKey->Buffer) {
|
|
start--;
|
|
}
|
|
|
|
if (*start != L'\\') {
|
|
ZwClose(key);
|
|
return;
|
|
}
|
|
|
|
start++;
|
|
for (i = 0; i < 31; i++) {
|
|
|
|
buffer[i] = *start++;
|
|
|
|
if (start >= ServiceKey->Buffer + ServiceKey->Length / sizeof(wchar_t)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
i++;
|
|
buffer[i] = L'\0';
|
|
|
|
status = ZwSetValueKey(key,
|
|
&name,
|
|
0,
|
|
REG_SZ,
|
|
buffer,
|
|
(i + 1) * sizeof(wchar_t));
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
ZwClose(key);
|
|
return;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Cycle through each of the busses.
|
|
//
|
|
|
|
numberOfBuses = DeviceExtension->ScsiInfo->NumberOfBuses;
|
|
|
|
DebugPrint((1,
|
|
"SpBuildDeviceMap: Number of SCSI buses %x\n",
|
|
numberOfBuses));
|
|
|
|
for (busNumber = 0; busNumber < numberOfBuses; busNumber++) {
|
|
|
|
//
|
|
// Create a key entry for the bus.
|
|
//
|
|
|
|
status = SpCreateNumericKey(key, busNumber, L"Scsi Bus ", &busKey);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
ZwClose(key);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Create a key entry for the Scsi bus adapter.
|
|
//
|
|
|
|
status = SpCreateNumericKey(busKey,
|
|
DeviceExtension->ScsiInfo->BusScanData[busNumber]->InitiatorBusId,
|
|
L"Initiator Id ",
|
|
&targetKey);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
ZwClose(key);
|
|
ZwClose(busKey);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Process the data for the logical units.
|
|
//
|
|
|
|
lunInfo = DeviceExtension->ScsiInfo->BusScanData[busNumber]->LunInfoList;
|
|
|
|
lastTarget = DeviceExtension->ScsiInfo->BusScanData[busNumber]->InitiatorBusId;
|
|
|
|
while (lunInfo != NULL) {
|
|
|
|
//
|
|
// If this is a new target Id then create a new target entry.
|
|
//
|
|
|
|
if (lastTarget != lunInfo->TargetId) {
|
|
|
|
ZwClose(targetKey);
|
|
|
|
status = SpCreateNumericKey(busKey,
|
|
lunInfo->TargetId,
|
|
L"Target Id ",
|
|
&targetKey);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
ZwClose(key);
|
|
ZwClose(busKey);
|
|
return;
|
|
}
|
|
|
|
lastTarget = lunInfo->TargetId;
|
|
}
|
|
|
|
//
|
|
// Create the Lun entry.
|
|
//
|
|
|
|
status = SpCreateNumericKey(targetKey,
|
|
lunInfo->Lun,
|
|
L"Logical Unit Id ",
|
|
&lunKey);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
ZwClose(key);
|
|
ZwClose(busKey);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Create identifier value.
|
|
//
|
|
|
|
RtlInitUnicodeString(&name, L"Identifier");
|
|
|
|
//
|
|
// Get the Identifier from the inquiry data.
|
|
//
|
|
|
|
ansiString.MaximumLength = 28;
|
|
ansiString.Length = 28;
|
|
ansiString.Buffer =((PINQUIRYDATA) lunInfo->InquiryData)->VendorId;
|
|
|
|
status = RtlAnsiStringToUnicodeString(&unicodeString,
|
|
&ansiString,
|
|
TRUE);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
ZwClose(key);
|
|
ZwClose(busKey);
|
|
ZwClose(targetKey);
|
|
ZwClose(lunKey);
|
|
return;
|
|
}
|
|
|
|
status = ZwSetValueKey(lunKey,
|
|
&name,
|
|
0,
|
|
REG_SZ,
|
|
unicodeString.Buffer,
|
|
unicodeString.Length + sizeof(wchar_t));
|
|
|
|
RtlFreeUnicodeString(&unicodeString);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
ZwClose(key);
|
|
ZwClose(busKey);
|
|
ZwClose(targetKey);
|
|
ZwClose(lunKey);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Determine the perpherial type.
|
|
//
|
|
|
|
switch (lunInfo->InquiryData[0] & 0x1f) {
|
|
case DIRECT_ACCESS_DEVICE:
|
|
start = L"DiskPeripheral";
|
|
break;
|
|
|
|
case SEQUENTIAL_ACCESS_DEVICE:
|
|
start = L"TapePeripheral";
|
|
break;
|
|
|
|
case PRINTER_DEVICE:
|
|
start = L"PrinterPeripheral";
|
|
break;
|
|
|
|
case WRITE_ONCE_READ_MULTIPLE_DEVICE:
|
|
start = L"WormPeripheral";
|
|
break;
|
|
|
|
case READ_ONLY_DIRECT_ACCESS_DEVICE:
|
|
start = L"CdRomPeripheral";
|
|
break;
|
|
|
|
case SCANNER_DEVICE:
|
|
start = L"ScannerPeripheral";
|
|
break;
|
|
|
|
case OPTICAL_DEVICE:
|
|
start = L"OpticalDiskPeripheral";
|
|
break;
|
|
|
|
case MEDIUM_CHANGER:
|
|
start = L"MediumChangerPeripheral";
|
|
break;
|
|
|
|
case COMMUNICATION_DEVICE:
|
|
start = L"CommunicationPeripheral";
|
|
break;
|
|
|
|
default:
|
|
start = L"OtherPeripheral";
|
|
}
|
|
|
|
//
|
|
// Set type value.
|
|
//
|
|
|
|
RtlInitUnicodeString(&name, L"Type");
|
|
|
|
status = ZwSetValueKey(lunKey,
|
|
&name,
|
|
0,
|
|
REG_SZ,
|
|
start,
|
|
wcslen(start) * sizeof(wchar_t) + sizeof(wchar_t));
|
|
|
|
|
|
ZwClose(lunKey);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
ZwClose(key);
|
|
ZwClose(busKey);
|
|
ZwClose(targetKey);
|
|
return;
|
|
}
|
|
|
|
lunInfo = lunInfo->NextLunInfo;
|
|
}
|
|
|
|
ZwClose(busKey);
|
|
ZwClose(targetKey);
|
|
}
|
|
|
|
ZwClose(key);
|
|
}
|
|
|
|
NTSTATUS
|
|
SpCreateNumericKey(
|
|
IN HANDLE Root,
|
|
IN ULONG Name,
|
|
IN PWSTR Prefix,
|
|
OUT PHANDLE NewKey
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function creates a registry key. The name of the key is a string
|
|
version of numeric value passed in.
|
|
|
|
Arguments:
|
|
|
|
RootKey - Supplies a handle to the key where the new key should be inserted.
|
|
|
|
Name - Supplies the numeric value to name the key.
|
|
|
|
Prefix - Supplies a prefix name to add to name.
|
|
|
|
NewKey - Returns the handle for the new key.
|
|
|
|
Return Value:
|
|
|
|
Returns the status of the operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
UNICODE_STRING string;
|
|
UNICODE_STRING stringNum;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
WCHAR bufferNum[16];
|
|
WCHAR buffer[64];
|
|
ULONG disposition;
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Copy the Prefix into a string.
|
|
//
|
|
|
|
string.Length = 0;
|
|
string.MaximumLength=64;
|
|
string.Buffer = buffer;
|
|
|
|
RtlInitUnicodeString(&stringNum, Prefix);
|
|
|
|
RtlCopyUnicodeString(&string, &stringNum);
|
|
|
|
//
|
|
// Create a port number key entry.
|
|
//
|
|
|
|
stringNum.Length = 0;
|
|
stringNum.MaximumLength = 16;
|
|
stringNum.Buffer = bufferNum;
|
|
|
|
status = RtlIntegerToUnicodeString(Name, 10, &stringNum);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Append the prefix and the numeric name.
|
|
//
|
|
|
|
RtlAppendUnicodeStringToString(&string, &stringNum);
|
|
|
|
InitializeObjectAttributes( &objectAttributes,
|
|
&string,
|
|
OBJ_CASE_INSENSITIVE,
|
|
Root,
|
|
(PSECURITY_DESCRIPTOR) NULL );
|
|
|
|
status = ZwCreateKey(NewKey,
|
|
KEY_READ | KEY_WRITE,
|
|
&objectAttributes,
|
|
0,
|
|
(PUNICODE_STRING) NULL,
|
|
REG_OPTION_VOLATILE,
|
|
&disposition );
|
|
|
|
return(status);
|
|
}
|
|
|
|
|
|
VOID
|
|
SpBuildConfiguration(
|
|
IN PHW_INITIALIZATION_DATA HwInitializationData,
|
|
IN PCM_FULL_RESOURCE_DESCRIPTOR ControllerData,
|
|
IN PPORT_CONFIGURATION_INFORMATION ConfigInformation
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Given a full resource description, fill in the port configuration
|
|
information.
|
|
|
|
Arguments:
|
|
|
|
HwInitializationData - to know maximum resources for device.
|
|
ControllerData - the CM_FULL_RESOURCE list for this configuration
|
|
ConfigInformation - the config info structure to be filled in
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG rangeNumber;
|
|
ULONG index;
|
|
PACCESS_RANGE accessRange;
|
|
PCM_PARTIAL_RESOURCE_DESCRIPTOR partialData;
|
|
|
|
rangeNumber = 0;
|
|
|
|
for (index = 0; index < ControllerData->PartialResourceList.Count; index++) {
|
|
partialData = &ControllerData->PartialResourceList.PartialDescriptors[index];
|
|
|
|
switch (partialData->Type) {
|
|
case CmResourceTypePort:
|
|
|
|
//
|
|
// Verify range count does not exceed what the
|
|
// miniport indicated.
|
|
//
|
|
|
|
if (HwInitializationData->NumberOfAccessRanges > rangeNumber) {
|
|
|
|
//
|
|
// Get next access range.
|
|
//
|
|
|
|
accessRange =
|
|
&((*(ConfigInformation->AccessRanges))[rangeNumber]);
|
|
|
|
accessRange->RangeStart = partialData->u.Port.Start;
|
|
accessRange->RangeLength = partialData->u.Port.Length;
|
|
|
|
accessRange->RangeInMemory = FALSE;
|
|
rangeNumber++;
|
|
}
|
|
break;
|
|
|
|
case CmResourceTypeInterrupt:
|
|
ConfigInformation->BusInterruptLevel = partialData->u.Interrupt.Level;
|
|
ConfigInformation->BusInterruptVector = partialData->u.Interrupt.Vector;
|
|
|
|
//
|
|
// Check interrupt mode.
|
|
//
|
|
|
|
if (partialData->Flags == CM_RESOURCE_INTERRUPT_LATCHED) {
|
|
ConfigInformation->InterruptMode = Latched;
|
|
} else if (partialData->Flags == CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE) {
|
|
ConfigInformation->InterruptMode = LevelSensitive;
|
|
}
|
|
break;
|
|
|
|
case CmResourceTypeMemory:
|
|
|
|
//
|
|
// Verify range count does not exceed what the
|
|
// miniport indicated.
|
|
//
|
|
|
|
if (HwInitializationData->NumberOfAccessRanges > rangeNumber) {
|
|
|
|
//
|
|
// Get next access range.
|
|
//
|
|
|
|
accessRange =
|
|
&((*(ConfigInformation->AccessRanges))[rangeNumber]);
|
|
|
|
accessRange->RangeStart = partialData->u.Memory.Start;
|
|
accessRange->RangeLength = partialData->u.Memory.Length;
|
|
|
|
accessRange->RangeInMemory = TRUE;
|
|
rangeNumber++;
|
|
}
|
|
break;
|
|
|
|
case CmResourceTypeDma:
|
|
ConfigInformation->DmaChannel = partialData->u.Dma.Channel;
|
|
ConfigInformation->DmaPort = partialData->u.Dma.Port;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
BOOLEAN
|
|
GetPciConfiguration(
|
|
PDRIVER_OBJECT DriverObject,
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PHW_INITIALIZATION_DATA HwInitializationData,
|
|
PPORT_CONFIGURATION_INFORMATION ConfigInformation,
|
|
PVOID RegistryPath,
|
|
ULONG BusNumber,
|
|
PULONG SlotNumber,
|
|
PULONG FunctionNumber
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Walk PCI slot information looking for Vendor and Product ID matches.
|
|
Get slot information for matches and register with hal for the resources.
|
|
|
|
Arguments:
|
|
|
|
DriverObject - Miniport driver object.
|
|
DeviceObject - Represents this adapter.
|
|
HwInitializationData - Miniport description.
|
|
ConfigInformation - Template for configuration information passed to a
|
|
miniport driver via the FindAdapter routine.
|
|
RegistryPath - Service key path.
|
|
BusNumber - PCI bus number for this search.
|
|
SlotNumber - Starting slot number for this search.
|
|
FunctionNumber - Starting function number for this search.
|
|
|
|
Return Value:
|
|
|
|
TRUE if card found. Slot and function numbers will return values that
|
|
should be used to continue the search for additional cards, when a card
|
|
is found.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG rangeNumber = 0;
|
|
ULONG pciBuffer;
|
|
ULONG slotNumber;
|
|
ULONG functionNumber;
|
|
ULONG status;
|
|
PCI_SLOT_NUMBER slotData;
|
|
PPCI_COMMON_CONFIG pciData;
|
|
PCM_RESOURCE_LIST resourceList;
|
|
UNICODE_STRING unicodeString;
|
|
UCHAR vendorString[5];
|
|
UCHAR deviceString[5];
|
|
|
|
pciData = (PPCI_COMMON_CONFIG)&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;
|
|
|
|
//
|
|
// Look at each device.
|
|
//
|
|
|
|
for (slotNumber = *SlotNumber;
|
|
slotNumber < 32;
|
|
slotNumber++) {
|
|
|
|
slotData.u.bits.DeviceNumber = slotNumber;
|
|
|
|
//
|
|
// Look at each function.
|
|
//
|
|
|
|
for (functionNumber= *FunctionNumber;
|
|
functionNumber < 8;
|
|
functionNumber++) {
|
|
|
|
slotData.u.bits.FunctionNumber = functionNumber;
|
|
|
|
if (!HalGetBusData(PCIConfiguration,
|
|
BusNumber,
|
|
slotData.u.AsULONG,
|
|
pciData,
|
|
sizeof(ULONG))) {
|
|
|
|
//
|
|
// Out of PCI data.
|
|
//
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
if (pciData->VendorID == PCI_INVALID_VENDORID) {
|
|
|
|
//
|
|
// No PCI device, or no more functions on device
|
|
// move to next PCI device.
|
|
//
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// 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\n",
|
|
BusNumber,
|
|
slotNumber,
|
|
functionNumber,
|
|
vendorString,
|
|
deviceString));
|
|
|
|
//
|
|
// Compare strings.
|
|
//
|
|
|
|
if (_strnicmp(vendorString,
|
|
HwInitializationData->VendorId,
|
|
HwInitializationData->VendorIdLength) ||
|
|
_strnicmp(deviceString,
|
|
HwInitializationData->DeviceId,
|
|
HwInitializationData->DeviceIdLength)) {
|
|
|
|
//
|
|
// Not our PCI device. Try next device/function
|
|
//
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// This is the miniport drivers slot. Allocate the
|
|
// resources.
|
|
//
|
|
|
|
RtlInitUnicodeString(&unicodeString, L"ScsiAdapter");
|
|
status = HalAssignSlotResources(RegistryPath,
|
|
&unicodeString,
|
|
DriverObject,
|
|
DeviceObject,
|
|
PCIBus,
|
|
BusNumber,
|
|
slotData.u.AsULONG,
|
|
&resourceList);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// ToDo: Log this error.
|
|
//
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Construct the configuration information.
|
|
//
|
|
|
|
SpBuildConfiguration(HwInitializationData,
|
|
resourceList->List,
|
|
ConfigInformation);
|
|
ExFreePool(resourceList);
|
|
|
|
//
|
|
// Update slot and function numbers.
|
|
//
|
|
|
|
*SlotNumber = slotNumber;
|
|
*FunctionNumber = functionNumber + 1;
|
|
|
|
//
|
|
// Record PCI slot number for miniport.
|
|
//
|
|
|
|
ConfigInformation->SlotNumber = slotData.u.AsULONG;
|
|
|
|
return TRUE;
|
|
|
|
} // next PCI function
|
|
|
|
*FunctionNumber = 0;
|
|
|
|
} // next PCI slot
|
|
|
|
*SlotNumber = 0;
|
|
|
|
return FALSE;
|
|
|
|
} // GetPciConfiguration()
|
|
|
|
|
|
BOOLEAN
|
|
GetPcmciaConfiguration(
|
|
IN PVOID RegistryPath,
|
|
PHW_INITIALIZATION_DATA HwInitializationData,
|
|
PPORT_CONFIGURATION_INFORMATION ConfigInformation
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Search to see if there is a PCMCIA based adapter in the system.
|
|
If so, fill in the ConfigInformation structure with the enabled
|
|
values.
|
|
|
|
Arguments:
|
|
|
|
RegistryPath - the path to the services node in the registry
|
|
ConfigInformation - The configuration structure
|
|
|
|
Return Value:
|
|
|
|
TRUE - it is a PCMCIA adapter
|
|
|
|
--*/
|
|
|
|
{
|
|
PUNICODE_STRING regPath = RegistryPath;
|
|
UNICODE_STRING unicodeName;
|
|
UNICODE_STRING serviceName;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
HANDLE handle;
|
|
ULONG size;
|
|
PWCHAR wchar;
|
|
PUCHAR buffer;
|
|
NTSTATUS status;
|
|
PKEY_VALUE_FULL_INFORMATION keyValueInformation;
|
|
PCM_FULL_RESOURCE_DESCRIPTOR controllerData;
|
|
|
|
//
|
|
// Get driver name from the RegistryPath
|
|
//
|
|
|
|
wchar = regPath->Buffer;
|
|
wchar += ((regPath->Length / sizeof(WCHAR)) - 1);
|
|
|
|
size = 0;
|
|
while (*wchar) {
|
|
if (*wchar == (WCHAR) '\\') {
|
|
break;
|
|
}
|
|
wchar--;
|
|
size++;
|
|
}
|
|
|
|
serviceName.Buffer = (++wchar);
|
|
serviceName.MaximumLength = serviceName.Length = (USHORT) (size * sizeof(WCHAR));
|
|
|
|
buffer = ExAllocatePool(NonPagedPool, 2048);
|
|
if (!buffer) {
|
|
return FALSE;
|
|
}
|
|
|
|
unicodeName.Buffer = (PWSTR) buffer;
|
|
unicodeName.MaximumLength = (2048 / sizeof(WCHAR));
|
|
|
|
RtlInitUnicodeString(&unicodeName,
|
|
L"\\Registry\\Machine\\Hardware\\Description\\System\\PCMCIA PCCARDs");
|
|
InitializeObjectAttributes(&objectAttributes,
|
|
&unicodeName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
//
|
|
// Check for entry in DeviceMap
|
|
//
|
|
|
|
if (!NT_SUCCESS(ZwOpenKey(&handle, MAXIMUM_ALLOWED, &objectAttributes))) {
|
|
|
|
//
|
|
// Nothing there
|
|
//
|
|
|
|
ExFreePool(buffer);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// See if key value for this driver is present
|
|
//
|
|
|
|
keyValueInformation = (PKEY_VALUE_FULL_INFORMATION) ExAllocatePool(NonPagedPool,
|
|
2048);
|
|
if (!keyValueInformation) {
|
|
ExFreePool(buffer);
|
|
ZwClose(handle);
|
|
return FALSE;
|
|
}
|
|
|
|
status = ZwQueryValueKey(handle,
|
|
&serviceName,
|
|
KeyValueFullInformation,
|
|
keyValueInformation,
|
|
2048,
|
|
&size);
|
|
|
|
ZwClose(handle);
|
|
ExFreePool(buffer);
|
|
if ((!NT_SUCCESS(status)) || (!keyValueInformation->DataLength)) {
|
|
|
|
//
|
|
// No value present
|
|
//
|
|
|
|
ExFreePool(keyValueInformation);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Set up structures for call to find adapter.
|
|
//
|
|
|
|
buffer = (PUCHAR)keyValueInformation + keyValueInformation->DataOffset;
|
|
controllerData = (PCM_FULL_RESOURCE_DESCRIPTOR) buffer;
|
|
SpBuildConfiguration(HwInitializationData,
|
|
controllerData,
|
|
ConfigInformation);
|
|
ExFreePool(keyValueInformation);
|
|
return TRUE;
|
|
} // GetPcmciaConfiguration()
|
|
|
|
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()
|