Windows2003-3790/drivers/storage/scsiport/pnp.c
2020-09-30 16:53:55 +02:00

2950 lines
81 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (C) Microsoft Corporation, 1990 - 1999
Module Name:
pnp.c
Abstract:
This is the NT SCSI port driver. This file contains the self-contained plug
and play code.
Authors:
Peter Wieland
Environment:
kernel mode only
Notes:
This module is a driver dll for scsi miniports.
Revision History:
--*/
#include "port.h"
#include <wdmguid.h>
#define __FILE_ID__ 'pnp '
#if DBG
static const char *__file__ = __FILE__;
#endif
#define NUM_DEVICE_TYPE_INFO_ENTRIES 18
extern SCSIPORT_DEVICE_TYPE DeviceTypeInfo[];
ULONG SpAdapterStopRemoveSupported = TRUE;
NTSTATUS
SpQueryCapabilities(
IN PADAPTER_EXTENSION Adapter
);
PWCHAR
ScsiPortAddGenericControllerId(
IN PDRIVER_OBJECT DriverObject,
IN PWCHAR IdList
);
VOID
CopyField(
IN PUCHAR Destination,
IN PUCHAR Source,
IN ULONG Count,
IN UCHAR Change
);
NTSTATUS
ScsiPortInitPnpAdapter(
IN PDEVICE_OBJECT Fdo
);
NTSTATUS
SpStartLowerDevice(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
VOID
SpGetSlotNumber(
IN PDEVICE_OBJECT Fdo,
IN PPORT_CONFIGURATION_INFORMATION ConfigInfo,
IN PCM_RESOURCE_LIST ResourceList
);
BOOLEAN
SpGetInterrupt(
IN PCM_RESOURCE_LIST FullResourceList,
OUT ULONG *Irql,
OUT ULONG *Vector,
OUT KAFFINITY *Affinity
);
VOID
SpQueryDeviceRelationsCompletion(
IN PADAPTER_EXTENSION Adapter,
IN PSP_ENUMERATION_REQUEST Request,
IN NTSTATUS Status
);
NTSTATUS
ScsiPortDetermineGenId(
IN PDRIVER_OBJECT DriverObject,
IN PINQUIRYDATA InquiryData,
OUT PUCHAR GenericId
);
//
// Routines start
//
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, ScsiPortAddDevice)
#pragma alloc_text(PAGE, ScsiPortUnload)
#pragma alloc_text(PAGE, ScsiPortFdoPnp)
#pragma alloc_text(PAGE, ScsiPortStartAdapter)
#pragma alloc_text(PAGE, ScsiPortGetDeviceId)
#pragma alloc_text(PAGE, ScsiPortGetInstanceId)
#pragma alloc_text(PAGE, ScsiPortGetHardwareIds)
#pragma alloc_text(PAGE, ScsiPortGetCompatibleIds)
#pragma alloc_text(PAGE, ScsiPortDetermineGenId)
#pragma alloc_text(PAGE, CopyField)
#pragma alloc_text(PAGE, SpFindInitData)
#pragma alloc_text(PAGE, SpStartLowerDevice)
#pragma alloc_text(PAGE, SpGetSlotNumber)
#pragma alloc_text(PAGE, SpGetDeviceTypeInfo)
#pragma alloc_text(PAGE, ScsiPortAddGenericControllerId)
#pragma alloc_text(PAGE, SpQueryCapabilities)
#pragma alloc_text(PAGE, SpGetInterrupt)
#pragma alloc_text(PAGE, SpQueryDeviceRelationsCompletion)
#pragma alloc_text(PAGELOCK, ScsiPortInitPnpAdapter)
#endif
#ifdef ALLOC_PRAGMA
#pragma code_seg("PAGE")
#endif
SCSIPORT_DEVICE_TYPE DeviceTypeInfo[NUM_DEVICE_TYPE_INFO_ENTRIES] = {
{"Disk", "GenDisk", L"DiskPeripheral", TRUE},
{"Sequential", "", L"TapePeripheral", TRUE},
{"Printer", "GenPrinter", L"PrinterPeripheral", FALSE},
{"Processor", "", L"OtherPeripheral", FALSE},
{"Worm", "GenWorm", L"WormPeripheral", TRUE},
{"CdRom", "GenCdRom", L"CdRomPeripheral", TRUE},
{"Scanner", "GenScanner", L"ScannerPeripheral", FALSE},
{"Optical", "GenOptical", L"OpticalDiskPeripheral", TRUE},
{"Changer", "ScsiChanger", L"MediumChangerPeripheral", TRUE},
{"Net", "ScsiNet", L"CommunicationsPeripheral", FALSE},
{"ASCIT8", "ScsiASCIT8", L"ASCPrePressGraphicsPeripheral", FALSE},
{"ASCIT8", "ScsiASCIT8", L"ASCPrePressGraphicsPeripheral", FALSE},
{"Array", "ScsiArray", L"ArrayPeripheral", FALSE},
{"Enclosure", "ScsiEnclosure", L"EnclosurePeripheral", FALSE},
{"RBC", "ScsiRBC", L"RBCPeripheral", TRUE},
{"CardReader", "ScsiCardReader", L"CardReaderPeripheral", FALSE},
{"Bridge", "ScsiBridge", L"BridgePeripheral", FALSE},
{"Other", "ScsiOther", L"OtherPeripheral", FALSE}
};
#ifdef ALLOC_PRAGMA
#pragma code_seg()
#endif
NTSTATUS
ScsiPortAddDevice(
IN PDRIVER_OBJECT DriverObject,
IN PDEVICE_OBJECT PhysicalDeviceObject
)
/*++
Routine Description:
This routine handles add-device requests for the scsi port driver
Arguments:
DriverObject - a pointer to the driver object for this device
PhysicalDeviceObject - a pointer to the PDO we are being added to
Return Value:
STATUS_SUCCESS
--*/
{
PSCSIPORT_DRIVER_EXTENSION driverExtension;
PDEVICE_OBJECT newFdo;
NTSTATUS status;
PAGED_CODE();
status = SpCreateAdapter(DriverObject, &newFdo);
if(newFdo != NULL) {
PADAPTER_EXTENSION adapter;
PCOMMON_EXTENSION commonExtension;
PDEVICE_OBJECT newStack;
adapter = newFdo->DeviceExtension;
commonExtension = &(adapter->CommonExtension);
adapter->IsMiniportDetected = FALSE;
adapter->IsPnp = TRUE;
driverExtension = IoGetDriverObjectExtension(DriverObject,
ScsiPortInitialize);
switch(driverExtension->BusType) {
#if 0
case BusTypeFibre: {
adapter->DisablePower = TRUE;
adapter->DisableStop = TRUE;
break;
}
#endif
default: {
adapter->DisablePower = FALSE;
adapter->DisableStop = FALSE;
break;
}
}
newStack = IoAttachDeviceToDeviceStack(newFdo, PhysicalDeviceObject);
adapter->CommonExtension.LowerDeviceObject = newStack;
adapter->LowerPdo = PhysicalDeviceObject;
if(newStack == NULL) {
status = STATUS_UNSUCCESSFUL;
} else {
status = STATUS_SUCCESS;
}
}
return status;
}
VOID
ScsiPortUnload(
IN PDRIVER_OBJECT DriverObject
)
/*++
Routine Description:
This routine will shut down all device objects for this miniport and
clean up all allocated resources
Arguments:
DriverObject - the driver being unloaded
Return Value:
none
--*/
{
PVOID Packet;
PSCSIPORT_DRIVER_EXTENSION DriverExtension;
PVOID CurrentValue;
PVOID InvalidPage;
PAGED_CODE();
//
// See if there is a driver extension for this driver. It is possible
// that one has not been created yet, so this may fail, in which case
// we give up and return.
//
DriverExtension = IoGetDriverObjectExtension(
DriverObject,
ScsiPortInitialize
);
if (DriverExtension == NULL) {
return;
}
//
// Get the reserve event in the driver extension. The reserve event
// may not have already been used, so it's possible that it is NULL. If
// this is the case, we give up and return.
//
Packet = DriverExtension->ReserveAllocFailureLogEntry;
if (Packet != NULL) {
//
// We have to ensure that we are the only instance to use this
// event. To do so, we attempt to NULL the event in the driver
// extension. If somebody else beats us to it, they own the
// event and we have to give up.
//
CurrentValue = InterlockedCompareExchangePointer(
&(DriverExtension->ReserveAllocFailureLogEntry),
NULL,
Packet);
if (Packet == CurrentValue) {
IoFreeErrorLogEntry(Packet);
}
}
//
// Free the invalid page we created to catch misbehaving miniports.
//
InvalidPage = DriverExtension->InvalidPage;
if (InvalidPage != NULL) {
CurrentValue = InterlockedCompareExchangePointer(
&(DriverExtension->InvalidPage),
NULL,
InvalidPage);
if (InvalidPage == CurrentValue) {
MmProtectMdlSystemAddress(DriverExtension->UnusedPageMdl, PAGE_READWRITE);
MmUnlockPages(DriverExtension->UnusedPageMdl);
IoFreeMdl(DriverExtension->UnusedPageMdl);
ExFreePool(DriverExtension->UnusedPage);
}
}
#ifdef ALLOC_PRAGMA
if (VerifierApiCodeSectionHandle != NULL) {
PVOID Handle = VerifierApiCodeSectionHandle;
CurrentValue = InterlockedCompareExchangePointer(
&VerifierApiCodeSectionHandle,
NULL,
Handle);
if (CurrentValue == Handle) {
MmUnlockPagableImageSection(Handle);
}
}
#endif
return;
}
NTSTATUS
ScsiPortFdoPnp(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
PCOMMON_EXTENSION commonExtension = DeviceObject->DeviceExtension;
PADAPTER_EXTENSION adapter = DeviceObject->DeviceExtension;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST;
ULONG isRemoved;
BOOLEAN sendDown = TRUE;
PAGED_CODE();
isRemoved = SpAcquireRemoveLock(DeviceObject, Irp);
DebugPrint((2, "ScsiPortFdoPnp: FDO %p IRP %p MinorFunction %x isRemoved %d\n",
DeviceObject,
Irp,
irpStack->MinorFunction,
isRemoved));
switch (irpStack->MinorFunction) {
case IRP_MN_QUERY_PNP_DEVICE_STATE: {
//
// Extract the address of the device state buffer from the IRP.
//
PPNP_DEVICE_STATE deviceState;
deviceState = (PPNP_DEVICE_STATE) &(Irp->IoStatus.Information);
//
// Copy our saved device state into the supplied address.
//
*deviceState = adapter->DeviceState;
//
// If the device is in the paging path, mark it as not-disableable.
//
if (commonExtension->PagingPathCount != 0) {
SET_FLAG((*deviceState), PNP_DEVICE_NOT_DISABLEABLE);
}
//
// Leave sendDown set to TRUE so request will be forwarded to
// lower driver below.
//
break;
}
case IRP_MN_START_DEVICE: {
PSCSIPORT_DRIVER_EXTENSION driverExtension =
IoGetDriverObjectExtension(DeviceObject->DriverObject,
ScsiPortInitialize);
PCM_RESOURCE_LIST allocatedResources =
irpStack->Parameters.StartDevice.AllocatedResources;
PCM_RESOURCE_LIST translatedResources =
irpStack->Parameters.StartDevice.AllocatedResourcesTranslated;
ULONG interfaceFlags;
irpStack = IoGetCurrentIrpStackLocation(Irp);
//
// Override sendDown so we complete this request below instead of
// forwarding it to the lower driver.
//
sendDown = FALSE;
//
// Make sure this device was created by an add rather than the
// one that was found by port or miniport.
//
if(adapter->IsPnp == FALSE) {
DebugPrint((1, "ScsiPortFdoPnp - asked to start non-pnp "
"adapter\n"));
status = STATUS_UNSUCCESSFUL;
break;
}
if(commonExtension->CurrentPnpState == IRP_MN_START_DEVICE) {
DebugPrint((1, "ScsiPortFdoPnp - already started - nothing "
"to do\n"));
status = STATUS_SUCCESS;
break;
}
//
// Now make sure that pnp handed us some resources. It may not
// if this is a phantom of a PCI device which we reported on a
// previous boot. In that case pnp thinks we'll allocate the
// resources ourselves.
//
if(allocatedResources == NULL) {
//
// This happens with reported devices when PCI moves them from
// boot to boot.
//
Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
break;
}
ASSERT(allocatedResources->Count);
//
// Make sure that adapters with this interface type can be
// initialized as pnp drivers.
//
interfaceFlags = SpQueryPnpInterfaceFlags(
driverExtension,
allocatedResources->List[0].InterfaceType);
if(interfaceFlags == SP_PNP_NOT_SAFE) {
//
// Nope - not safe. We cannot start this device so return
// failure.
//
DebugPrint((1, "ScsiPortFdoPnp - Miniport cannot be run in "
"pnp mode for interface type %#08lx\n",
allocatedResources->List[0].InterfaceType));
//
// Mark the device as not being pnp - that way we won't get
// removed.
//
adapter->IsPnp = FALSE;
status = STATUS_UNSUCCESSFUL;
break;
}
//
// Check to see if this interface requires slot/function numbers.
// If not then zero out the virtual slot number.
//
if(!TEST_FLAG(interfaceFlags, SP_PNP_NEEDS_LOCATION)) {
adapter->VirtualSlotNumber.u.AsULONG = 0;
}
//
// Determine if we should have found this device during
// our detection scan. We do this by checking to see if the pdo
// has a pnp bus type. If not and the detection flag is set then
// assume duplicate detection has failed and don't start this
// device.
//
{
status = SpGetBusTypeGuid(adapter);
if((status == STATUS_OBJECT_NAME_NOT_FOUND) &&
((driverExtension->LegacyAdapterDetection == TRUE) &&
(interfaceFlags & SP_PNP_NON_ENUMERABLE))) {
DbgPrint("ScsiPortFdoPnp: device has no pnp bus type but "
"was not found as a duplicate during "
"detection\n");
status = STATUS_UNSUCCESSFUL;
//
// make sure this one doesn't get removed though - if it's
// removed then the resources may be disabled for the
// ghost.
//
adapter->IsPnp = FALSE;
break;
}
}
//
// Finally, if this is a PCI adapter make sure we were given
// an interrupt. The current assumption is that there aren't
// any polled-mode PCI SCSI adapters in the market.
//
if(TEST_FLAG(interfaceFlags, SP_PNP_INTERRUPT_REQUIRED)) {
ULONG irql, vector;
KAFFINITY affinity;
if(SpGetInterrupt(allocatedResources,
&irql,
&vector,
&affinity) == FALSE) {
PIO_ERROR_LOG_PACKET error =
IoAllocateErrorLogEntry(DeviceObject,
sizeof(IO_ERROR_LOG_PACKET));
status = STATUS_DEVICE_CONFIGURATION_ERROR;
if(error != NULL) {
error->MajorFunctionCode = IRP_MJ_PNP;
error->UniqueErrorValue = 0x418;
error->ErrorCode = IO_ERR_INCORRECT_IRQL;
IoWriteErrorLogEntry(error);
}
break;
}
}
status = SpStartLowerDevice(DeviceObject, Irp);
if(NT_SUCCESS(status)) {
//
// If we haven't allocated a HwDeviceExtension for this thing
// yet then we'll need to set it up
//
if(commonExtension->IsInitialized == FALSE) {
DebugPrint((1, "ScsiPortFdoPnp - find and init adapter %#p\n",
DeviceObject));
if(allocatedResources == NULL) {
status = STATUS_INVALID_PARAMETER;
} else {
adapter->AllocatedResources =
RtlDuplicateCmResourceList(
DeviceObject->DriverObject,
NonPagedPool,
allocatedResources,
SCSIPORT_TAG_RESOURCE_LIST);
adapter->TranslatedResources =
RtlDuplicateCmResourceList(
DeviceObject->DriverObject,
NonPagedPool,
translatedResources,
SCSIPORT_TAG_RESOURCE_LIST);
commonExtension->IsInitialized = TRUE;
status = ScsiPortInitPnpAdapter(DeviceObject);
}
if(!NT_SUCCESS(status)) {
DebugPrint((1, "ScsiPortInitializeAdapter failed "
"%#08lx\n", status));
break;
}
}
//
// Start up the adapter.
//
status = ScsiPortStartAdapter(DeviceObject);
if(NT_SUCCESS(status)) {
commonExtension->PreviousPnpState = 0xff;
commonExtension->CurrentPnpState = IRP_MN_START_DEVICE;
}
}
break;
}
case IRP_MN_FILTER_RESOURCE_REQUIREMENTS: {
PIO_RESOURCE_REQUIREMENTS_LIST requirements;
//
// Grab the bus and slot numbers out of the resource requirements
// list.
//
requirements = irpStack->Parameters.FilterResourceRequirements.
IoResourceRequirementList;
if (requirements != NULL) {
adapter->RealBusNumber = requirements->BusNumber;
adapter->RealSlotNumber = requirements->SlotNumber;
}
//
// Leave sendDown in its default TRUE state so we will forward
// this request to the lower driver.
//
break;
}
case IRP_MN_CANCEL_STOP_DEVICE: {
sendDown = TRUE;
Irp->IoStatus.Status = STATUS_SUCCESS;
if(commonExtension->CurrentPnpState == IRP_MN_QUERY_STOP_DEVICE) {
commonExtension->CurrentPnpState =
commonExtension->PreviousPnpState;
commonExtension->PreviousPnpState = 0xff;
}
break;
}
case IRP_MN_CANCEL_REMOVE_DEVICE: {
sendDown = TRUE;
Irp->IoStatus.Status = STATUS_SUCCESS;
if(commonExtension->CurrentPnpState == IRP_MN_QUERY_REMOVE_DEVICE) {
commonExtension->CurrentPnpState =
commonExtension->PreviousPnpState;
commonExtension->PreviousPnpState = 0xff;
}
break;
}
case IRP_MN_QUERY_STOP_DEVICE: {
//
// If stop has been disabled, fail the IRP and override sendDown
// so we complete the request instead of forwarding it.
//
if (adapter->DisableStop) {
status = STATUS_NOT_SUPPORTED;
sendDown = FALSE;
break;
}
//
// Fall through.
//
}
case IRP_MN_QUERY_REMOVE_DEVICE: {
//
// No problem with this request on our part. Just send it down
// to the next driver.
//
if (SpAdapterStopRemoveSupported) {
if((adapter->IsPnp) &&
SpIsAdapterControlTypeSupported(adapter,
ScsiStopAdapter)) {
Irp->IoStatus.Status = STATUS_SUCCESS;
sendDown = TRUE;
} else {
status = STATUS_UNSUCCESSFUL;
sendDown = FALSE;
}
if(NT_SUCCESS(status)) {
commonExtension->PreviousPnpState =
commonExtension->CurrentPnpState;
commonExtension->CurrentPnpState = irpStack->MinorFunction;
}
} else {
Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
status = STATUS_UNSUCCESSFUL;
sendDown = FALSE;
}
break;
}
case IRP_MN_SURPRISE_REMOVAL: {
PDEVICE_OBJECT lowerDevice = commonExtension->LowerDeviceObject;
//
// First mark the device as REMOVE_PENDING - this should keep
// us from starting up any new i/o requests.
//
commonExtension->IsRemoved = REMOVE_PENDING;
//
// Terminate the device. This shuts down the miniport as quickly
// as possible and aborts all i/o requests.
//
if(commonExtension->CurrentPnpState == IRP_MN_START_DEVICE) {
SpTerminateAdapter(adapter);
}
//
// Release the remove lock and wait for any in-flight requests to
// complete.
//
SpReleaseRemoveLock(DeviceObject, Irp);
SpWaitForRemoveLock(DeviceObject, DeviceObject);
//
// Go do the surprise remove portion of removing the adapter.
//
ScsiPortRemoveAdapter(DeviceObject, TRUE);
//
// Save the new state of this device.
//
commonExtension->PreviousPnpState = commonExtension->CurrentPnpState;
commonExtension->CurrentPnpState = IRP_MN_SURPRISE_REMOVAL;
//
// Since we have already released the adapter remove lock, we
// the request here instead of doing it at the bottom of the
// function.
//
IoCopyCurrentIrpStackLocationToNext(Irp);
return IoCallDriver(commonExtension->LowerDeviceObject, Irp);
}
case IRP_MN_REMOVE_DEVICE:
//
// Asked to remove the adapter. We'll ask the port driver to
// stop it's adapter and release it's resources. We can
// detach and delete our device object as the lower driver
// completes the remove request.
//
ASSERT(isRemoved != REMOVE_COMPLETE);
//
// If the device has been started then make sure we've got the
// necessary code to disable it. If it isn't currently started
// then either it's got the code we need or it's never been
// started - in either case we can just tear it down.
//
if((adapter->IsPnp == FALSE) ||
((commonExtension->CurrentPnpState == IRP_MN_START_DEVICE) &&
(!SpIsAdapterControlTypeSupported(adapter,
ScsiStopAdapter)))) {
//
// the miniport needs to be stopped but we cannot do it.
// Fail the request.
//
status = STATUS_UNSUCCESSFUL;
sendDown = FALSE;
break;
}
//
// Clear the interface if it exists.
//
if(adapter->InterfaceName.Buffer != NULL) {
IoSetDeviceInterfaceState(
&(adapter->InterfaceName),
FALSE);
RtlFreeUnicodeString(&(adapter->InterfaceName));
RtlInitUnicodeString(&(adapter->InterfaceName), NULL);
}
SpReleaseRemoveLock(DeviceObject, Irp);
ScsiPortRemoveAdapter(DeviceObject, FALSE);
//
// The adapter's been removed. Set the new state now.
//
commonExtension->CurrentPnpState = IRP_MN_REMOVE_DEVICE;
commonExtension->PreviousPnpState = 0xff;
//
// Forward the request down and wait for it to complete.
//
status = SpSendIrpSynchronous(
commonExtension->LowerDeviceObject,
Irp);
//
// Indicate the the adapter is fully removed.
//
commonExtension->IsRemoved = REMOVE_COMPLETE;
//
// Complete the IRP.
//
IoCompleteRequest(Irp, IO_NO_INCREMENT);
//
// Detach and delete the FDO.
//
IoDetachDevice(commonExtension->LowerDeviceObject);
IoDeleteDevice(DeviceObject);
return status;
case IRP_MN_STOP_DEVICE: {
sendDown = TRUE;
ASSERT(adapter->IsPnp);
ASSERT(adapter->HwAdapterControl != NULL);
status = ScsiPortStopAdapter(DeviceObject, Irp);
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = 0L;
if(!NT_SUCCESS(status)) {
sendDown = FALSE;
} else {
commonExtension->PreviousPnpState = commonExtension->CurrentPnpState;
commonExtension->CurrentPnpState = IRP_MN_STOP_DEVICE;
}
break;
}
case IRP_MN_QUERY_DEVICE_RELATIONS: {
DEVICE_RELATION_TYPE type =
irpStack->Parameters.QueryDeviceRelations.Type;
DebugPrint((1, "ScsiPortFdoPnp - got "
"IRP_MJ_QUERY_DEVICE_RELATIONS\n"));
DebugPrint((1, "\ttype is %d\n", type));
if (type == BusRelations) {
PSP_ENUMERATION_REQUEST request;
request = InterlockedCompareExchangePointer(
&adapter->PnpEnumRequestPtr,
NULL,
&(adapter->PnpEnumerationRequest));
if (request != NULL) {
RtlZeroMemory(request, sizeof(SP_ENUMERATION_REQUEST));
request->CompletionRoutine = SpQueryDeviceRelationsCompletion;
request->Context = Irp;
request->CompletionStatus = &(Irp->IoStatus.Status);
IoMarkIrpPending(Irp);
SpEnumerateAdapterAsynchronous(adapter, request, FALSE);
return STATUS_PENDING;
} else {
ASSERT(FALSE && "Unexpected!! Concurrent QDR requests");
Irp->IoStatus.Status = STATUS_DEVICE_BUSY;
Irp->IoStatus.Information = 0L;
sendDown = FALSE;
}
}
break;
}
case IRP_MN_DEVICE_USAGE_NOTIFICATION:
//
// Send the irp down to the device below us.
// Since there's a remove lock outstanding on the PDO we can release
// the lock on the FDO before sending this down.
//
Irp->IoStatus.Status = STATUS_SUCCESS;
break;
case IRP_MN_QUERY_ID: {
PWCHAR newIdList;
//
// We add the id GEN_SCSIADAPTER to the compatible ID's for any
// adapters controlled by scsiport.
//
DebugPrint((2, "ScsiPortFdoPnp: got IRP_MN_QUERY_ID\n"));
if(irpStack->Parameters.QueryId.IdType != BusQueryCompatibleIDs) {
sendDown = TRUE;
break;
}
status = SpSendIrpSynchronous(commonExtension->LowerDeviceObject,
Irp);
newIdList = ScsiPortAddGenericControllerId(
DeviceObject->DriverObject,
(PWCHAR) (Irp->IoStatus.Information));
if(newIdList == NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
} else {
status = STATUS_SUCCESS;
if(Irp->IoStatus.Information != 0L) {
ExFreePool((PVOID) Irp->IoStatus.Information);
}
Irp->IoStatus.Information = (ULONG_PTR) newIdList;
}
sendDown = FALSE;
break;
}
default: {
PIO_STACK_LOCATION nextIrpStack = IoGetNextIrpStackLocation(Irp);
DebugPrint((1, "ScsiPortFdoPnp: Unimplemented PNP/POWER minor "
"code %d\n", irpStack->MinorFunction));
break;
}
}
if (sendDown) {
IoCopyCurrentIrpStackLocationToNext(Irp);
SpReleaseRemoveLock(DeviceObject, Irp);
status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
} else {
SpReleaseRemoveLock(DeviceObject, Irp);
Irp->IoStatus.Status = status;
SpCompleteRequest(DeviceObject, Irp, NULL, IO_NO_INCREMENT);
}
return status;
}
NTSTATUS
ScsiPortStartAdapter(
IN PDEVICE_OBJECT Adapter
)
/*++
Routine Descripion:
This routine will start an adapter.
It is illegal to start the device if it has already been started.
Arguments:
Adapter - a pointer to the functional device object (adapter) being started
Return Value:
STATUS_SUCCESS if the device was properly started and enumeration was
attempted - or if the device had previously been started.
error value indicating the cause of the failure otherwise
--*/
{
PSCSIPORT_DRIVER_EXTENSION
driverExtension = IoGetDriverObjectExtension(Adapter->DriverObject,
ScsiPortInitialize);
PADAPTER_EXTENSION adapterExtension = Adapter->DeviceExtension;
PCOMMON_EXTENSION commonExtension = Adapter->DeviceExtension;
UCHAR pathId;
PAGED_CODE();
ASSERT(driverExtension != NULL);
ASSERT_FDO(Adapter);
ASSERT(commonExtension->CurrentPnpState != IRP_MN_START_DEVICE);
ASSERT(commonExtension->IsInitialized);
ASSERT(((Adapter->Flags & DO_DEVICE_INITIALIZING) == 0));
DebugPrint((1, "ScsiPortStartAdapter - starting adapter %#p\n", Adapter));
//
// Start timer. Request timeout counters
// in the logical units have already been
// initialized.
//
adapterExtension->TickCount = 0;
IoStartTimer(Adapter);
//
// Initialize WMI support.
//
if (adapterExtension->CommonExtension.WmiInitialized == FALSE) {
//
// Build the SCSIPORT WMI registration information buffer for this FDO.
//
SpWmiInitializeSpRegInfo(Adapter);
//
// Register this device object only if the miniport supports WMI and/or
// SCSIPORT will support certain WMI GUIDs on behalf of the miniport.
//
if (adapterExtension->CommonExtension.WmiScsiPortRegInfoBuf != NULL) {
//
// Register this functional device object as a WMI data provider,
// instructing WMI that it is ready to receive WMI IRPs.
//
DebugPrint((1, "ScsiPortStartAdapter: REGISTER FDO:%p\n", Adapter));
IoWMIRegistrationControl(Adapter, WMIREG_ACTION_REGISTER);
adapterExtension->CommonExtension.WmiInitialized = TRUE;
}
//
// Allocate several WMI_MINIPORT_REQUEST_ITEM blocks to satisfy a
// potential onslaught of WMIEvent notifications by the miniport.
//
if (adapterExtension->CommonExtension.WmiMiniPortSupport) {
//
// Currently, we only allocate two per new adapter (FDO).
//
SpWmiInitializeFreeRequestList(Adapter, 2);
}
}
//
// Create a well known name for this device object by making a symbolic
// link to the PDO. Even if this fails, the start should still succeed.
//
if(adapterExtension->PortNumber == -1) {
NTSTATUS status;
UNICODE_STRING unicodePdoName;
ULONG number;
UNICODE_STRING interfaceName;
RtlInitUnicodeString(&unicodePdoName, adapterExtension->DeviceName);
//
// Start at zero and keep going through all the possible numbers
// until we find a hole. This is an unfortunate requirement for
// legacy support since most old class drivers will give up if
// they find a hole in the scsiport numbers.
//
number = 0;
do {
WCHAR wideLinkName[64];
UNICODE_STRING unicodeLinkName;
//
// Create the well known name string first.
//
swprintf(wideLinkName, L"\\Device\\ScsiPort%d", number);
RtlInitUnicodeString(&unicodeLinkName, wideLinkName);
status = IoCreateSymbolicLink(&unicodeLinkName, &unicodePdoName);
if(NT_SUCCESS(status)) {
//
// Found a spot - mark this one as named so we don't go
// through this trouble again and save the port number
//
adapterExtension->PortNumber = number;
//
// Create the dos port driver name. If there's a collision
// then just forget it.
//
swprintf(wideLinkName, L"\\DosDevices\\Scsi%d:", number);
RtlInitUnicodeString(&unicodeLinkName, wideLinkName);
IoCreateSymbolicLink(&unicodeLinkName, &unicodePdoName);
} else {
number++;
}
} while (status == STATUS_OBJECT_NAME_COLLISION);
//
// Increment the count of scsiport device
//
IoGetConfigurationInformation()->ScsiPortCount++;
//
// Create a device map entry for this adapter.
//
SpBuildDeviceMapEntry(commonExtension);
//
// Register our device interface.
//
status = IoRegisterDeviceInterface(adapterExtension->LowerPdo,
&StoragePortClassGuid,
NULL,
&interfaceName);
if(NT_SUCCESS(status)) {
adapterExtension->InterfaceName = interfaceName;
status = IoSetDeviceInterfaceState(&interfaceName, TRUE);
if(!NT_SUCCESS(status)) {
RtlFreeUnicodeString(&interfaceName);
RtlInitUnicodeString(&(adapterExtension->InterfaceName), NULL);
}
}
}
//
// Set the force next bus scan bit.
//
adapterExtension->ForceNextBusScan = TRUE;
return STATUS_SUCCESS;
}
NTSTATUS
ScsiPortGetDeviceId(
IN PDEVICE_OBJECT Pdo,
OUT PUNICODE_STRING UnicodeString
)
/*++
Routine Description:
This routine will allocate space for and fill in a device id string for
the specified Pdo. This string is generated from the bus type (scsi) and
the type of the device.
Arguments:
Pdo - a pointer to the physical device object being queried
UnicodeString - a pointer to an already allocated unicode string structure.
This routine will allocate and fill in the buffer of this
structure
Returns:
status
--*/
{
PLOGICAL_UNIT_EXTENSION physicalExtension = Pdo->DeviceExtension;
PINQUIRYDATA inquiryData = &(physicalExtension->InquiryData);
UCHAR buffer[256];
PUCHAR rawIdString = buffer;
ANSI_STRING ansiIdString;
ULONG whichString;
PAGED_CODE();
ASSERT(UnicodeString != NULL);
RtlZeroMemory(buffer, sizeof(buffer));
sprintf(rawIdString,
"SCSI\\%s",
SpGetDeviceTypeInfo(inquiryData->DeviceType)->DeviceTypeString);
rawIdString += strlen(rawIdString);
ASSERT(*rawIdString == '\0');
for(whichString = 0; whichString < 3; whichString++) {
PUCHAR headerString;
PUCHAR sourceString;
ULONG sourceStringLength;
ULONG i;
switch(whichString) {
//
// Vendor Id
//
case 0: {
sourceString = inquiryData->VendorId;
sourceStringLength = sizeof(inquiryData->VendorId);
headerString = "Ven";
break;
}
//
// Product Id
//
case 1: {
sourceString = inquiryData->ProductId;
sourceStringLength = sizeof(inquiryData->ProductId);
headerString = "Prod";
break;
}
//
// Product Revision Level
//
case 2: {
sourceString = inquiryData->ProductRevisionLevel;
sourceStringLength = sizeof(inquiryData->ProductRevisionLevel);
headerString = "Rev";
break;
}
}
//
// Start at the end of the source string and back up until we find a
// non-space, non-null character.
//
for(; sourceStringLength > 0; sourceStringLength--) {
if((sourceString[sourceStringLength - 1] != ' ') &&
(sourceString[sourceStringLength - 1] != '\0')) {
break;
}
}
//
// Throw the header string into the block
//
sprintf(rawIdString, "&%s_", headerString);
rawIdString += strlen(headerString) + 2;
//
// Spew the string into the device id
//
for(i = 0; i < sourceStringLength; i++) {
*rawIdString = (sourceString[i] != ' ') ? (sourceString[i]) :
('_');
rawIdString++;
}
ASSERT(*rawIdString == '\0');
}
RtlInitAnsiString(&ansiIdString, buffer);
DebugPrint((1, "DeviceId for logical unit %#p is %Z\n",
Pdo, &ansiIdString));
return RtlAnsiStringToUnicodeString(UnicodeString, &ansiIdString, TRUE);
}
NTSTATUS
ScsiPortGetInstanceId(
IN PDEVICE_OBJECT Pdo,
OUT PUNICODE_STRING UnicodeString
)
/*++
Routine Description:
This routine will allocate space for and fill in an instance id string for
the specified Pdo. This string will be generated either from the device
type + serial number of the device (if it has a serial number) or from
the address of the device.
Arguments:
Pdo - a pointer to the physical device object being queried
UnicodeString - a pointer to an already allocated unicode string structure.
This routine will allocate and fill in the buffer of this
structure
Returns:
status
--*/
{
PLOGICAL_UNIT_EXTENSION physicalExtension = Pdo->DeviceExtension;
PDRIVER_OBJECT driverObject = Pdo->DriverObject;
PSCSIPORT_DEVICE_TYPE deviceTypeInfo;
UCHAR idStringBuffer[64];
ANSI_STRING ansiIdString;
PAGED_CODE();
ASSERT(UnicodeString != NULL);
//
// can't use serial number even if it exists since a device which is
// multiply connected to the same bus (dual-ported device) will have
// the same serial number at each connection and would confuse the PNP.
//
sprintf(idStringBuffer,
"%x%x%x",
physicalExtension->PathId,
physicalExtension->TargetId,
physicalExtension->Lun
);
RtlInitAnsiString(&ansiIdString, idStringBuffer);
return RtlAnsiStringToUnicodeString(UnicodeString, &ansiIdString, TRUE);
}
NTSTATUS
ScsiPortGetCompatibleIds(
IN PDRIVER_OBJECT DriverObject,
IN PINQUIRYDATA InquiryData,
OUT PUNICODE_STRING UnicodeString
)
/*++
Routine Description:
This routine will allocate space for and fill in a compatible id multi
string for the specified Pdo. This string is generated using the bus and
device types for the device
Arguments:
InquiryData - the inquiry data to generate compatible ids from.
UnicodeString - a pointer to an already allocated unicode string structure.
This routine will allocate and fill in the buffer of this
structure
Returns:
status
--*/
{
UCHAR s[sizeof("SCSI\\DEVICE_TYPE_GOES_HERE")];
PSTR stringBuffer[] = {
s,
"SCSI\\RAW",
NULL};
//
// Fill in the scsi specific string
//
sprintf(stringBuffer[0],
"SCSI\\%s",
SpGetDeviceTypeInfo(InquiryData->DeviceType)->DeviceTypeString);
//
// Set up the first id string
//
return ScsiPortStringArrayToMultiString(
DriverObject,
UnicodeString,
stringBuffer);
}
#define MPIO_DEVICE_LIST L"\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Control\\MPDEV"
NTSTATUS
ScsiPortDetermineGenId(
IN PDRIVER_OBJECT DriverObject,
IN PINQUIRYDATA InquiryData,
OUT PUCHAR GenericId
)
/*++
Routine Description:
This routine will return the correct Compatible ID for the device in the caller
supplied 'CompatibleId'.
The value will either be a GenXXX or MPIODisk, depending upon results from routines
exported by portlib.lib
Arguments:
DriverObject - The port-driver's DriverObject.
InquiryData - The inquiry data of the device for which a compatID is being built.
GenericId - Storage for the NULL-terminated buffer that will contain the correct ID.
Returns:
SUCCESS or some error status from the library routines - (INSUFFICIENT_RESOURCES or some
registry access error).
--*/
{
PSCSIPORT_DEVICE_TYPE devTypeInfo;
PSCSIPORT_DRIVER_EXTENSION driverExtension;
UNICODE_STRING mpioRegistryPath;
UCHAR vendorId[9];
UCHAR productId[17];
UCHAR genericId[40];
BOOLEAN mpio = FALSE;
NTSTATUS status = STATUS_SUCCESS;
PAGED_CODE();
//
// Init compatibleId, as it will always contain something.
//
RtlZeroMemory(genericId, 40);
//
// Get the type info for this device.
//
devTypeInfo = SpGetDeviceTypeInfo(InquiryData->DeviceType);
//
// Default to the GenXXX of the device. If it's not a disk, errors occur
// in this routine, or it's a non-mpio supported disk, then this value
// will be returned.
//
RtlCopyMemory(genericId,
devTypeInfo->GenericTypeString,
strlen(devTypeInfo->GenericTypeString));
//
// Check whether this is a disk or not. If not, the generic id
// built earlier will be used.
//
if (InquiryData->DeviceType == DIRECT_ACCESS_DEVICE) {
NTSTATUS status2;
//
// Get the driverObject extension. This contains storage for the
// supported device list.
//
driverExtension = IoGetDriverObjectExtension(
DriverObject,
ScsiPortInitialize);
ASSERT(driverExtension != NULL);
//
// Determine whether this contains the list, or is uninitialised.
//
if (driverExtension->MPIOSupportedDeviceList.Buffer == NULL) {
//
// Build the registry path string.
//
RtlInitUnicodeString(&mpioRegistryPath, MPIO_DEVICE_LIST);
//
// Call into the port-driver library to get the list.
// Any failures here will be dealt with below.
//
status2 = PortGetMPIODeviceList(
&mpioRegistryPath,
&driverExtension->MPIOSupportedDeviceList);
} else {
//
// Buffer is there, so load up 'success'.
//
status2 = STATUS_SUCCESS;
}
if (NT_SUCCESS(status2)) {
//
// Get NULL terminated copies of Vendor and Product ID from the
// passed-in inquiry data.
//
RtlZeroMemory(vendorId, 9);
RtlZeroMemory(productId, 17);
RtlCopyMemory(vendorId, InquiryData->VendorId, 8);
RtlCopyMemory(productId, InquiryData->ProductId, 16);
//
// Call into the port-driver library to determine whether this
// device is supported under multipath.
//
mpio = PortIsDeviceMPIOSupported(
&driverExtension->MPIOSupportedDeviceList,
vendorId,
productId);
if (mpio) {
//
// Build MPIODisk instead of GenDisk. Mpdev.sys loads against
// this.
//
RtlCopyMemory(genericId,
"MPIODisk",
8);
}
}
}
if (NT_SUCCESS(status)) {
//
// Copy the built ID into the caller's buffer.
//
RtlCopyMemory(GenericId,
genericId,
strlen(genericId));
}
return status;
}
NTSTATUS
ScsiPortGetHardwareIds(
IN PDRIVER_OBJECT DriverObject,
IN PINQUIRYDATA InquiryData,
OUT PUNICODE_STRING UnicodeString
)
/*++
Routine Description:
This routine will allocate space for and fill in a hardware id multi
string for the specified Pdo. This string is generated using the device
type and the inquiry data.
Arguments:
InquiryData - the inquiry data to be converted into id strings.
UnicodeString - a pointer to an already allocated unicode string structure.
This routine will allocate and fill in the buffer of this
structure
Returns:
status
--*/
#define NUMBER_HARDWARE_STRINGS 6
{
PSCSIPORT_DEVICE_TYPE devTypeInfo =
SpGetDeviceTypeInfo(InquiryData->DeviceType);
ULONG i;
PSTR strings[NUMBER_HARDWARE_STRINGS + 1];
UCHAR scratch[64];
UCHAR genericId[40];
NTSTATUS status;
SIZE_T len;
PAGED_CODE();
//
// Zero out the string buffer
//
RtlZeroMemory(strings, sizeof(strings));
RtlZeroMemory(genericId, 40);
//
// Build the compatible ID for this device.
//
status = ScsiPortDetermineGenId(DriverObject,
InquiryData,
genericId);
if (!NT_SUCCESS(status)) {
return status;
}
try {
for(i = 0; i < NUMBER_HARDWARE_STRINGS; i++) {
RtlZeroMemory(scratch, sizeof(scratch));
//
// Build each of the hardware id's
//
switch(i) {
//
// Bus + Dev Type + Vendor + Product + Revision
//
case 0: {
sprintf(scratch, "SCSI\\%s", devTypeInfo->DeviceTypeString);
CopyField(scratch + strlen(scratch),
InquiryData->VendorId,
8,
'_');
CopyField(scratch + strlen(scratch),
InquiryData->ProductId,
16,
'_');
CopyField(scratch + strlen(scratch),
InquiryData->ProductRevisionLevel,
4,
'_');
break;
}
//
// bus + device + vendor + product
//
case 1: {
sprintf(scratch, "SCSI\\%s", devTypeInfo->DeviceTypeString);
CopyField(scratch + strlen(scratch),
InquiryData->VendorId,
8,
'_');
CopyField(scratch + strlen(scratch),
InquiryData->ProductId,
16,
'_');
break;
}
//
// bus + device + vendor
//
case 2: {
sprintf(scratch, "SCSI\\%s", devTypeInfo->DeviceTypeString);
CopyField(scratch + strlen(scratch),
InquiryData->VendorId,
8,
'_');
break;
}
//
// bus \ vendor + product + revision[0]
//
case 3: {
sprintf(scratch, "SCSI\\");
//
// Fall through to the next set.
//
}
//
// vendor + product + revision[0] (win9x)
//
case 4: {
CopyField(scratch + strlen(scratch),
InquiryData->VendorId,
8,
'_');
CopyField(scratch + strlen(scratch),
InquiryData->ProductId,
16,
'_');
CopyField(scratch + strlen(scratch),
InquiryData->ProductRevisionLevel,
1,
'_');
break;
}
case 5: {
//
// Copy over the previously built generic id.
//
RtlCopyMemory(scratch,
genericId,
strlen(genericId));
break;
}
default: {
ASSERT(FALSE);
break;
}
}
len = strlen(scratch);
if(len != 0) {
strings[i] =
SpAllocatePool(PagedPool,
strlen(scratch) + sizeof(UCHAR),
SCSIPORT_TAG_PNP_ID,
DriverObject);
if(strings[i] == NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
leave;
}
RtlCopyMemory(strings[i], scratch, len+1);
} else {
break;
}
}
status = ScsiPortStringArrayToMultiString(DriverObject,
UnicodeString,
strings);
leave;
} finally {
for(i = 0; i < NUMBER_HARDWARE_STRINGS; i++) {
if(strings[i]) {
ExFreePool(strings[i]);
}
}
}
return status;
}
#undef NUMBER_HARDWARE_STRINGS
VOID
CopyField(
IN PUCHAR Destination,
IN PUCHAR Source,
IN ULONG Count,
IN UCHAR Change
)
/*++
Routine Description:
This routine will copy Count string bytes from Source to Destination. If
it finds a nul byte in the Source it will translate that and any subsequent
bytes into Change. It will also replace spaces with the specified character.
Arguments:
Destination - the location to copy bytes
Source - the location to copy bytes from
Count - the number of bytes to be copied
Return Value:
none
--*/
{
ULONG i = 0;
BOOLEAN pastEnd = FALSE;
PAGED_CODE();
for(i = 0; i < Count; i++) {
if(!pastEnd) {
if(Source[i] == 0) {
pastEnd = TRUE;
Destination[i] = Change;
} else if(Source[i] == ' ') {
Destination[i] = Change;
} else {
Destination[i] = Source[i];
}
} else {
Destination[i] = Change;
}
}
return;
}
NTSTATUS
ScsiPortInitPnpAdapter(
IN PDEVICE_OBJECT Fdo
)
/*++
Routine Description:
This routine will find and (if found) initialize a specific adapter. The
adapter is specified by the ResourceList passed in.
This routine will initialize a port configuration structure using the
information provided in the resource list and call the miniport's find
adapter routine to locate the adapter. If that completes successfully, the
miniport's initialize routine will be called. This will connect the
interrupts and initialize the timers and DPCs as well as allocating
common buffers and request data structures.
Arguments:
Fdo - the device object for the adapter being initialized
Return Value:
status
--*/
{
PADAPTER_EXTENSION adapter = Fdo->DeviceExtension;
PSCSIPORT_DRIVER_EXTENSION
driverExtension = IoGetDriverObjectExtension(Fdo->DriverObject,
ScsiPortInitialize);
INTERFACE_TYPE interfaceType;
ULONG resultLength;
PHW_INITIALIZATION_DATA hwInitializationData = NULL;
CONFIGURATION_CONTEXT configurationContext;
PPORT_CONFIGURATION_INFORMATION configInfo = NULL;
BOOLEAN callAgain;
OBJECT_ATTRIBUTES objectAttributes;
ULONG uniqueId;
PHW_DEVICE_EXTENSION hwDeviceExtension;
ULONG hwDeviceExtensionSize;
PUNICODE_STRING registryPath = &(driverExtension->RegistryPath);
NTSTATUS status;
PAGED_CODE();
//
// Find the init data for this interface type
//
interfaceType = SpGetPdoInterfaceType(adapter->LowerPdo);
hwInitializationData = SpFindInitData(driverExtension, interfaceType);
if(hwInitializationData == NULL) {
//
// Hmmm. The miniport never reported this adapter type. We can't
// start the device since we don't know what the correct entry points
// are. Pretend it doesn't exist
//
return STATUS_NO_SUCH_DEVICE;
}
hwDeviceExtensionSize = hwInitializationData->DeviceExtensionSize +
sizeof(HW_DEVICE_EXTENSION);
RtlZeroMemory(&configurationContext, sizeof(configurationContext));
if(hwInitializationData->NumberOfAccessRanges != 0) {
configurationContext.AccessRanges =
SpAllocatePool(PagedPool,
(hwInitializationData->NumberOfAccessRanges *
sizeof(ACCESS_RANGE)),
SCSIPORT_TAG_ACCESS_RANGE,
Fdo->DriverObject);
if(configurationContext.AccessRanges == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
}
try {
ULONG portConfigSize;
//
// Allocate the HwDeviceExtension first - it's easier to deallocate :)
//
hwDeviceExtension = SpAllocatePool(NonPagedPool,
hwDeviceExtensionSize,
SCSIPORT_TAG_DEV_EXT,
Fdo->DriverObject);
if(hwDeviceExtension == NULL) {
DebugPrint((1, "ScsiPortInitialize: Could not allocate "
"HwDeviceExtension\n"));
status = STATUS_INSUFFICIENT_RESOURCES;
uniqueId = __LINE__;
leave;
}
RtlZeroMemory(hwDeviceExtension, hwDeviceExtensionSize);
//
// Setup device extension pointers
//
SpInitializeAdapterExtension(adapter,
hwInitializationData,
hwDeviceExtension);
//
// initialize the miniport config info buffer
//
status = SpInitializeConfiguration(
adapter,
registryPath,
hwInitializationData,
&configurationContext);
if(!NT_SUCCESS(status)) {
uniqueId = __LINE__;
leave;
}
//
// Allocate a config-info structure and access ranges for the
// miniport drivers to use
//
portConfigSize = sizeof(PORT_CONFIGURATION_INFORMATION);
portConfigSize += hwInitializationData->NumberOfAccessRanges *
sizeof(ACCESS_RANGE);
portConfigSize += 7;
portConfigSize &= ~7;
configInfo = SpAllocatePool(NonPagedPool,
portConfigSize,
SCSIPORT_TAG_PORT_CONFIG,
Fdo->DriverObject);
if(configInfo == NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
uniqueId = __LINE__;
leave;
}
adapter->PortConfig = configInfo;
//
// Copy the current structure to the writable copy
//
RtlCopyMemory(configInfo,
&configurationContext.PortConfig,
sizeof(PORT_CONFIGURATION_INFORMATION));
//
// Copy the SrbExtensionSize from device extension to ConfigInfo.
// A check will be made later to determine if the miniport updated
// this value
//
configInfo->SrbExtensionSize = adapter->SrbExtensionSize;
configInfo->SpecificLuExtensionSize = adapter->HwLogicalUnitExtensionSize;
//
// initialize the access range array
//
if(hwInitializationData->NumberOfAccessRanges != 0) {
configInfo->AccessRanges = (PVOID) (configInfo + 1);
//
// Quadword align this
//
(ULONG_PTR) (configInfo->AccessRanges) += 7;
(ULONG_PTR) (configInfo->AccessRanges) &= ~7;
RtlCopyMemory(configInfo->AccessRanges,
configurationContext.AccessRanges,
(hwInitializationData->NumberOfAccessRanges *
sizeof(ACCESS_RANGE)));
}
//
// Set the adapter interface type.
//
configInfo->AdapterInterfaceType = interfaceType;
//
// Since we've been handed resources we need to build a config info
// structure before we can call the find adapter routine
//
SpBuildConfiguration(adapter,
hwInitializationData,
configInfo);
SpGetSlotNumber(Fdo, configInfo, adapter->AllocatedResources);
//
// Get the miniport configuration inofmraiton
//
status = SpCallHwFindAdapter(Fdo,
hwInitializationData,
NULL,
&configurationContext,
configInfo,
&callAgain);
if(status == STATUS_DEVICE_DOES_NOT_EXIST) {
adapter->PortConfig = NULL;
ExFreePool(configInfo);
} else if(NT_SUCCESS(status)) {
status = SpAllocateAdapterResources(Fdo);
if(NT_SUCCESS(status)) {
PCOMMON_EXTENSION commonExtension = Fdo->DeviceExtension;
BOOLEAN stopped;
//
// If the device's previous state is IRP_MN_STOP_DEVICE then
// it should have a disable count of 1. Clear the disabled
// state.
//
stopped =
((commonExtension->CurrentPnpState == IRP_MN_STOP_DEVICE) ?
TRUE :
FALSE);
if(stopped) {
ASSERT(adapter->CommonExtension.PreviousPnpState == IRP_MN_START_DEVICE);
ASSERT(adapter->DisableCount == 1);
adapter->DisableCount = 0;
CLEAR_FLAG(adapter->InterruptData.InterruptFlags,
PD_DISABLE_INTERRUPTS);
}
status = SpCallHwInitialize(Fdo);
if(stopped) {
KIRQL oldIrql;
PVOID sectionHandle;
//
// Restart i/o processing.
//
sectionHandle =
MmLockPagableCodeSection(ScsiPortInitPnpAdapter);
KeRaiseIrql(DISPATCH_LEVEL, &oldIrql);
IoStartNextPacket(Fdo, FALSE);
KeLowerIrql(oldIrql);
MmUnlockPagableImageSection(sectionHandle);
}
}
}
} finally {
if(!NT_SUCCESS(status)) {
PIO_ERROR_LOG_PACKET errorLogEntry;
//
// An error occured - log it.
//
errorLogEntry = (PIO_ERROR_LOG_PACKET)
IoAllocateErrorLogEntry(
Fdo,
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);
}
//
// Clean up the last device object which is not used.
//
SpDestroyAdapter(adapter, FALSE);
if (configurationContext.AccessRanges != NULL) {
ExFreePool(configurationContext.AccessRanges);
}
if (configurationContext.Parameter != NULL) {
ExFreePool(configurationContext.Parameter);
}
} else {
//
// Determine which adapter control functions this miniport will
// support for the adapter.
//
SpGetSupportedAdapterControlFunctions(adapter);
}
}
return status;
}
PHW_INITIALIZATION_DATA
SpFindInitData(
IN PSCSIPORT_DRIVER_EXTENSION DriverExtension,
IN INTERFACE_TYPE InterfaceType
)
/*++
Routine Description:
This routine will search the list of chained init structures looking for
the first one that matches the interface type in the resource list.
Arguments:
DriverExtension - The driver extension to be searched
ResourceList - this resource list describes the (interface) type of the
adapter we are looking for
Return Value:
a pointer to the HW_INITIALIZATION_DATA structure for this interface type
NULL if none was found
--*/
{
PSP_INIT_CHAIN_ENTRY chainEntry = DriverExtension->InitChain;
PAGED_CODE();
while(chainEntry != NULL) {
if(chainEntry->InitData.AdapterInterfaceType == InterfaceType) {
return &(chainEntry->InitData);
}
chainEntry = chainEntry->NextEntry;
}
return NULL;
}
NTSTATUS
SpStartLowerDevice(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine will forward the start request to the next lower device and
block until it's completion.
Arguments:
DeviceObject - the device to which the start request was issued.
Irp - the start request
Return Value:
status
--*/
{
PADAPTER_EXTENSION adapter = DeviceObject->DeviceExtension;
PCOMMON_EXTENSION commonExtension = DeviceObject->DeviceExtension;
PKEVENT event;
NTSTATUS status;
PAGED_CODE();
event = SpAllocatePool(NonPagedPool,
sizeof(KEVENT),
SCSIPORT_TAG_EVENT,
DeviceObject->DriverObject);
if(event == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
KeInitializeEvent(event, SynchronizationEvent, FALSE);
IoCopyCurrentIrpStackLocationToNext(Irp);
IoSetCompletionRoutine(Irp,
SpSignalCompletion,
event,
TRUE,
TRUE,
TRUE);
status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
if(status == STATUS_PENDING) {
KeWaitForSingleObject(event, Executive, KernelMode, FALSE, NULL);
status = Irp->IoStatus.Status;
}
if(NT_SUCCESS(status)) {
PIO_STACK_LOCATION irpStack;
//
// Now go and retrieve any interfaces we need from the lower device.
//
Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
irpStack = IoGetNextIrpStackLocation(Irp);
irpStack->Parameters.QueryInterface.InterfaceType =
(LPGUID) &GUID_BUS_INTERFACE_STANDARD;
irpStack->Parameters.QueryInterface.Size =
sizeof(BUS_INTERFACE_STANDARD);
irpStack->Parameters.QueryInterface.Version = 1;
irpStack->Parameters.QueryInterface.Interface =
(PINTERFACE) &(adapter->LowerBusInterfaceStandard);
irpStack->MajorFunction = IRP_MJ_PNP;
irpStack->MinorFunction = IRP_MN_QUERY_INTERFACE;
KeResetEvent(event);
IoSetCompletionRoutine(Irp,
SpSignalCompletion,
event,
TRUE,
TRUE,
TRUE);
IoCallDriver(commonExtension->LowerDeviceObject, Irp);
KeWaitForSingleObject(event, Executive, KernelMode, FALSE, NULL);
if(NT_SUCCESS(Irp->IoStatus.Status)) {
adapter->LowerBusInterfaceStandardRetrieved = TRUE;
} else {
DebugPrint((1, "LowerBusInterfaceStandard request returned "
"%#08lx\n", Irp->IoStatus.Status));
adapter->LowerBusInterfaceStandardRetrieved = FALSE;
}
Irp->IoStatus.Status = status;
}
ExFreePool(event);
return status;
}
VOID
SpGetSlotNumber(
IN PDEVICE_OBJECT Fdo,
IN PPORT_CONFIGURATION_INFORMATION ConfigInfo,
IN PCM_RESOURCE_LIST ResourceList
)
/*++
Routine Description:
This routine will open the registry key for the associated Pdo and try
to retrieve the bus, slot and function numbers that will be stored there
if this was a device we detected or one that the user has configured by
hand. These values will be stored in the ConfigInfo structure for the
adapter.
If this information does not exist then the values will be filled with
zeros and the IsVirtualSlot flag will be set in the Fdo for use by other
routines.
Arguments:
Fdo - a pointer to the functional device object for this adapter
ConfigInfo - the ConfigInfo structure to be changed
Return Value:
None
--*/
{
PADAPTER_EXTENSION adapter = Fdo->DeviceExtension;
HANDLE instanceHandle = NULL;
HANDLE parametersHandle = NULL;
NTSTATUS status;
PAGED_CODE();
adapter->IsInVirtualSlot = FALSE;
try {
OBJECT_ATTRIBUTES objectAttributes;
UNICODE_STRING unicodeKeyName;
status = IoOpenDeviceRegistryKey(adapter->LowerPdo,
PLUGPLAY_REGKEY_DEVICE,
KEY_READ,
&instanceHandle);
if(!NT_SUCCESS(status)) {
leave;
}
RtlInitUnicodeString(&unicodeKeyName, L"Scsiport");
InitializeObjectAttributes(
&objectAttributes,
&unicodeKeyName,
OBJ_CASE_INSENSITIVE,
instanceHandle,
NULL);
status = ZwOpenKey(&parametersHandle,
KEY_READ,
&objectAttributes);
if(!NT_SUCCESS(status)) {
leave;
} else {
RTL_QUERY_REGISTRY_TABLE queryTable[3];
ULONG busNumber;
ULONG slotNumber;
ULONG negativeOne = 0xffffffff;
RtlZeroMemory(queryTable, sizeof(queryTable));
queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
queryTable[0].Name = L"SlotNumber";
queryTable[0].EntryContext = &slotNumber;
queryTable[0].DefaultType = REG_DWORD;
queryTable[0].DefaultData = &negativeOne;
queryTable[0].DefaultLength = sizeof(ULONG);
queryTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT;
queryTable[1].Name = L"BusNumber";
queryTable[1].EntryContext = &busNumber;
queryTable[1].DefaultType = REG_DWORD;
queryTable[1].DefaultData = &negativeOne;
queryTable[1].DefaultLength = sizeof(ULONG);
status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
(PWSTR) parametersHandle,
queryTable,
NULL,
NULL);
if(!NT_SUCCESS(status)) {
leave;
}
if((busNumber == negativeOne) || (slotNumber == negativeOne)) {
ConfigInfo->SystemIoBusNumber = ResourceList->List[0].BusNumber;
ConfigInfo->SlotNumber = 0;
adapter->IsInVirtualSlot = TRUE;
} else {
ConfigInfo->SystemIoBusNumber = busNumber;
ConfigInfo->SlotNumber = slotNumber;
adapter->IsInVirtualSlot = FALSE;
}
}
} finally {
//
// If an error occurred then we'll need to try virtualizing this
// adapter
//
if(!NT_SUCCESS(status)) {
//
// Send ourself a query capabilities IRP so that we can retrieve
// slot and function numbers from PCI.
//
status = SpQueryCapabilities(adapter);
if(NT_SUCCESS(status)) {
ConfigInfo->SystemIoBusNumber = ResourceList->List[0].BusNumber;
ConfigInfo->SlotNumber = adapter->VirtualSlotNumber.u.AsULONG;
adapter->IsInVirtualSlot = TRUE;
}
}
if(instanceHandle != NULL) {
ZwClose(instanceHandle);
}
if(parametersHandle != NULL) {
ZwClose(parametersHandle);
}
}
return;
}
PSCSIPORT_DEVICE_TYPE
SpGetDeviceTypeInfo(
IN UCHAR DeviceType
)
{
PAGED_CODE();
if(DeviceType >= NUM_DEVICE_TYPE_INFO_ENTRIES) {
return &(DeviceTypeInfo[NUM_DEVICE_TYPE_INFO_ENTRIES - 1]);
} else {
return &(DeviceTypeInfo[DeviceType]);
}
};
PWCHAR
ScsiPortAddGenericControllerId(
IN PDRIVER_OBJECT DriverObject,
IN PWCHAR IdList
)
/*++
Routine Description:
This routine will attempt to add the id GEN_SCSIADAPTER to the provided
list of compatible id's.
Arguments:
Pdo - a pointer to the physical device object being queried
UnicodeString - a pointer to an already allocated unicode string structure.
This routine will allocate and fill in the buffer of this
structure
Returns:
status
--*/
{
ULONG stringLength = 0;
ULONG i = 0;
PWCHAR addedString = L"GEN_SCSIADAPTER";
PWCHAR newList;
PWCHAR p;
PAGED_CODE();
//
// If strings were provided then count them to determine a length for the
// new id list.
//
if(IdList != NULL) {
i = 0;
while((IdList[i] != UNICODE_NULL) || (IdList[i+1] != UNICODE_NULL)) {
i++;
}
//
// Compensate for the fact that we stopped counting just before the
// first byte of the double-null.
//
i += 2;
stringLength = i;
}
stringLength += wcslen(L"GEN_SCSIADAPTER");
//
// We'll need to add in yet another NULL to terminate the current ending
// string.
//
stringLength += 2;
//
// Allocate a new string list to replace the existing one with.
//
newList = SpAllocatePool(PagedPool,
(stringLength * sizeof(WCHAR)),
SCSIPORT_TAG_PNP_ID,
DriverObject);
if(newList == NULL) {
return NULL;
}
RtlFillMemory(newList, (stringLength * sizeof(WCHAR)), '@');
//
// If we were provided with a string then copy it into the buffer we just
// allocated.
//
if(ARGUMENT_PRESENT(IdList)) {
i = 0;
while((IdList[i] != UNICODE_NULL) || (IdList[i+1] != UNICODE_NULL)) {
newList[i] = IdList[i];
i++;
}
//
// Terminate the string we just wrote.
//
newList[i] = UNICODE_NULL;
p = &(newList[i+1]);
} else {
p = newList;
}
//
// Copy the new id string into the buffer.
//
for(i = 0; addedString[i] != UNICODE_NULL; i++) {
*p = addedString[i];
p++;
}
//
// Write two unicode nulls to the string to terminate it.
//
*p = UNICODE_NULL;
p++;
*p = UNICODE_NULL;
//
// Set up the first id string
//
return newList;
}
NTSTATUS
SpQueryCapabilities(
IN PADAPTER_EXTENSION Adapter
)
{
DEVICE_CAPABILITIES capabilities;
PIRP irp;
PIO_STACK_LOCATION irpStack;
KEVENT event;
NTSTATUS status;
PAGED_CODE();
//
// Initialize the capabilities structure.
//
RtlZeroMemory(&capabilities, sizeof(DEVICE_CAPABILITIES));
capabilities.Size = sizeof(DEVICE_CAPABILITIES);
capabilities.Version = 1;
capabilities.Address = capabilities.UINumber = (ULONG)-1;
//
// Initialize the event we're going to wait on.
//
KeInitializeEvent(&event, SynchronizationEvent, FALSE);
//
// Allocate a new irp.
//
irp = SpAllocateIrp((CCHAR) (Adapter->DeviceObject->StackSize + 1),
FALSE,
Adapter->DeviceObject->DriverObject);
if(irp == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
irpStack = IoGetNextIrpStackLocation(irp);
irpStack->MajorFunction = IRP_MJ_PNP;
irpStack->MinorFunction = IRP_MN_QUERY_CAPABILITIES;
irpStack->Parameters.DeviceCapabilities.Capabilities = &capabilities;
irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
IoSetCompletionRoutine(irp,
SpSignalCompletion,
&event,
TRUE,
TRUE,
TRUE);
//
// Send the irp to ourself ... just in case we ever start modifying
// the contents of the capabilities in our PNP dispatch routine.
//
IoCallDriver(Adapter->DeviceObject, irp);
KeWaitForSingleObject(&event,
Executive,
KernelMode,
FALSE,
NULL);
status = irp->IoStatus.Status;
if(NT_SUCCESS(status)) {
USHORT device;
USHORT function;
device = (USHORT) (capabilities.Address >> 0x10);
function = (USHORT) (capabilities.Address & 0x0000ffff);
Adapter->VirtualSlotNumber.u.bits.DeviceNumber = device;
Adapter->VirtualSlotNumber.u.bits.FunctionNumber = function;
} else {
Adapter->VirtualSlotNumber.u.AsULONG = 0;
}
IoFreeIrp(irp);
return status;
}
BOOLEAN
SpGetInterrupt(
IN PCM_RESOURCE_LIST FullResourceList,
OUT ULONG *Irql,
OUT ULONG *Vector,
OUT KAFFINITY *Affinity
)
/*++
Routine Description:
Given a full resource list returns the interrupt.
Arguments:
FullResourceList - the resource list.
Irql - returns the irql for the interrupt.
Vector - returns the vector for the interrupt.
Affinity - returns the affinity for the interrupt.
Return Value:
TRUE if an interrupt is found.
FALSE if none was found (in which case the output parameters are not valid.
--*/
{
ULONG rangeNumber;
ULONG index;
PCM_FULL_RESOURCE_DESCRIPTOR resourceList;
PCM_PARTIAL_RESOURCE_DESCRIPTOR partialData;
PAGED_CODE();
rangeNumber = 0;
resourceList = FullResourceList->List;
for (index = 0; index < resourceList->PartialResourceList.Count; index++) {
partialData = &resourceList->PartialResourceList.PartialDescriptors[index];
if(partialData->Type == CmResourceTypeInterrupt) {
*Irql = partialData->u.Interrupt.Level;
*Vector = partialData->u.Interrupt.Vector;
*Affinity = partialData->u.Interrupt.Affinity;
return TRUE;
}
}
return FALSE;
}
VOID
SpQueryDeviceRelationsCompletion(
IN PADAPTER_EXTENSION Adapter,
IN PSP_ENUMERATION_REQUEST Request,
IN NTSTATUS Unused
)
{
PIRP irp = (PIRP) Request->Context;
PDEVICE_RELATIONS deviceRelations;
PDEVICE_OBJECT lowerDevice = Adapter->CommonExtension.LowerDeviceObject;
NTSTATUS status;
PAGED_CODE();
ASSERT_FDO(Adapter->DeviceObject);
//
// Pnp is done in a system thread - we shouldn't get a user-mode APC to this
// thread.
//
ASSERT(Unused != STATUS_USER_APC);
//
// Return the list of devices on the bus
//
status = SpExtractDeviceRelations(Adapter, BusRelations, &deviceRelations);
if(NT_SUCCESS(status)) {
ULONG i;
DebugPrint((2, "SpQueryDeviceRelationsCompletion: Found %d devices "
"on adapter %#p\n",
deviceRelations->Count,
Adapter));
for(i = 0; i < deviceRelations->Count; i++) {
DebugPrint((2, "/t#%2d: device %#p\n",
i,
deviceRelations->Objects[i]));
}
}
//
// Put the pointer to the enumeration request object back.
//
Request = InterlockedCompareExchangePointer(
&Adapter->PnpEnumRequestPtr,
&(Adapter->PnpEnumerationRequest),
NULL);
ASSERT(Request == NULL);
//
// Store the status and the return information in the IRP.
//
irp->IoStatus.Status = status;
if(NT_SUCCESS(status)) {
irp->IoStatus.Information = (ULONG_PTR) deviceRelations;
} else {
irp->IoStatus.Information = (ULONG_PTR) NULL;
}
return;
}