Windows2000/private/ntos/io/pnpirp.c
2020-09-30 17:12:32 +02:00

2171 lines
85 KiB
C

/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
util.c
Abstract:
This module contains IRP related routines.
Author:
Shie-Lin Tzong (shielint) 13-Sept-1996
*/
#include "iop.h"
#pragma hdrstop
#if DBG_SCOPE
#define PnpIrpStatusTracking(Status, IrpCode, Device) \
if (PnpIrpMask & (1 << IrpCode)) { \
if (!NT_SUCCESS(Status) || Status == STATUS_PENDING) { \
DbgPrint(" ++ %s Driver ( %wZ ) return status %08lx\n", \
IrpName[IrpCode], \
&Device->DriverObject->DriverName, \
Status); \
} \
}
ULONG PnpIrpMask;
PCHAR IrpName[] = {
"IRP_MN_START_DEVICE - ", // 0x00
"IRP_MN_QUERY_REMOVE_DEVICE - ", // 0x01
"IRP_MN_REMOVE_DEVICE - ", // 0x02
"IRP_MN_CANCEL_REMOVE_DEVICE - ", // 0x03
"IRP_MN_STOP_DEVICE - ", // 0x04
"IRP_MN_QUERY_STOP_DEVICE - ", // 0x05
"IRP_MN_CANCEL_STOP_DEVICE - ", // 0x06
"IRP_MN_QUERY_DEVICE_RELATIONS - ", // 0x07
"IRP_MN_QUERY_INTERFACE - ", // 0x08
"IRP_MN_QUERY_CAPABILITIES - ", // 0x09
"IRP_MN_QUERY_RESOURCES - ", // 0x0A
"IRP_MN_QUERY_RESOURCE_REQUIREMENTS - ", // 0x0B
"IRP_MN_QUERY_DEVICE_TEXT - ", // 0x0C
"IRP_MN_FILTER_RESOURCE_REQUIREMENTS - ", // 0x0D
"INVALID_IRP_CODE - ", //
"IRP_MN_READ_CONFIG - ", // 0x0F
"IRP_MN_WRITE_CONFIG - ", // 0x10
"IRP_MN_EJECT - ", // 0x11
"IRP_MN_SET_LOCK - ", // 0x12
"IRP_MN_QUERY_ID - ", // 0x13
"IRP_MN_QUERY_PNP_DEVICE_STATE - ", // 0x14
"IRP_MN_QUERY_BUS_INFORMATION - ", // 0x15
"IRP_MN_DEVICE_USAGE_NOTIFICATION - ", // 0x16
NULL
};
#else
#define PnpIrpStatusTracking(Status, IrpCode, Device)
#endif
// Hash routine from CNTFS (see cntfs\prefxsup.c)
// (used here in the construction of unique ids)
#define HASH_UNICODE_STRING( _pustr, _phash ) { \
PWCHAR _p = (_pustr)->Buffer; \
PWCHAR _ep = _p + ((_pustr)->Length/sizeof(WCHAR)); \
ULONG _chHolder =0; \
\
while( _p < _ep ) { \
_chHolder = 37 * _chHolder + (unsigned int) (*_p++); \
} \
\
*(_phash) = abs(314159269 * _chHolder) % 1000000007; \
}
// Parent prefixes are of the form %x&%x&%x
#define MAX_PARENT_PREFIX (8 + 8 + 8 + 2)
// Internal definitions
typedef struct _DEVICE_COMPLETION_CONTEXT
{
PDEVICE_NODE DeviceNode;
ERESOURCE_THREAD Thread;
ULONG IrpMinorCode;
#if DBG
PVOID Id;
#endif
} DEVICE_COMPLETION_CONTEXT, *PDEVICE_COMPLETION_CONTEXT;
typedef struct _LOCK_MOUNTABLE_DEVICE_CONTEXT
{
PDEVICE_OBJECT MountedDevice;
} LOCK_MOUNTABLE_DEVICE_CONTEXT, *PLOCK_MOUNTABLE_DEVICE_CONTEXT;
// Internal references
NTSTATUS IopAsynchronousCall(IN PDEVICE_OBJECT DeviceObject,
IN PIO_STACK_LOCATION TopStackLocation,
IN PDEVICE_COMPLETION_CONTEXT CompletionContext,
IN PVOID CompletionRoutine);
NTSTATUS IopDeviceEjectComplete(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context);
NTSTATUS IopDeviceStartComplete(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context);
NTSTATUS IopDeviceRelationsComplete(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context);
PDEVICE_OBJECT IopFindMountableDevice(IN PDEVICE_OBJECT DeviceObject);
PDEVICE_OBJECT IopLockMountedDeviceForRemove(IN PDEVICE_OBJECT DeviceObject, IN ULONG IrpMinorCode, OUT PLOCK_MOUNTABLE_DEVICE_CONTEXT Context);
VOID IopUnlockMountedDeviceForRemove(IN PDEVICE_OBJECT DeviceObject, IN ULONG IrpMinorCode, IN PLOCK_MOUNTABLE_DEVICE_CONTEXT Context);
NTSTATUS IopFilterResourceRequirementsCall(IN PDEVICE_OBJECT DeviceObject, IN PIO_RESOURCE_REQUIREMENTS_LIST ResReqList, OUT PVOID *Information);
// External reference
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, IopAsynchronousCall)
#pragma alloc_text(PAGE, IopSynchronousCall)
//#pragma alloc_text(PAGE, IopStartDevice)
#pragma alloc_text(PAGE, IopEjectDevice)
#pragma alloc_text(PAGE, IopRemoveDevice)
//#pragma alloc_text(PAGE, IopQueryDeviceRelations)
#pragma alloc_text(PAGE, IopQueryDeviceId)
#pragma alloc_text(PAGE, IopQueryUniqueId)
#pragma alloc_text(PAGE, IopQueryCompatibleIds)
#pragma alloc_text(PAGE, IopQueryDeviceResources)
#pragma alloc_text(PAGE, IopQueryDockRemovalInterface)
#pragma alloc_text(PAGE, IopQueryLegacyBusInformation)
#pragma alloc_text(PAGE, IopQueryPnpBusInformation)
#pragma alloc_text(PAGE, IopQueryResourceHandlerInterface)
#pragma alloc_text(PAGE, IopQueryReconfiguration)
#pragma alloc_text(PAGE, IopUncacheInterfaceInformation)
#pragma alloc_text(PAGE, IopFindMountableDevice)
#pragma alloc_text(PAGE, IopFilterResourceRequirementsCall)
#pragma alloc_text(PAGE, IopMakeGloballyUniqueId)
#endif // ALLOC_PRAGMA
VOID IopUncacheInterfaceInformation(IN PDEVICE_OBJECT DeviceObject)
/*++
Routine Description:
This function removes all the cached translators and arbiters information from the device object.
Parameters:
DeviceObject - Supplies the device object of the device being removed.
Return Value:
NTSTATUS code.
*/
{
PDEVICE_NODE deviceNode = (PDEVICE_NODE)DeviceObject->DeviceObjectExtension->DeviceNode;
PLIST_ENTRY listHead, nextEntry, entry;
PPI_RESOURCE_TRANSLATOR_ENTRY handlerEntry;
PINTERFACE interface;
// Dereference all the arbiters on this PDO.
listHead = &deviceNode->DeviceArbiterList;
nextEntry = listHead->Flink;
while (nextEntry != listHead) {
entry = nextEntry;
nextEntry = nextEntry->Flink;
handlerEntry = CONTAINING_RECORD(entry, PI_RESOURCE_TRANSLATOR_ENTRY, DeviceTranslatorList);
if (interface = (PINTERFACE)handlerEntry->TranslatorInterface) {
(interface->InterfaceDereference)(interface->Context);
ExFreePool(interface);
}
ExFreePool(entry);
}
InitializeListHead(&deviceNode->DeviceArbiterList);
// Dereference all the translators on this PDO.
listHead = &deviceNode->DeviceTranslatorList;
nextEntry = listHead->Flink;
while (nextEntry != listHead) {
entry = nextEntry;
nextEntry = nextEntry->Flink;
handlerEntry = CONTAINING_RECORD(entry, PI_RESOURCE_TRANSLATOR_ENTRY, DeviceTranslatorList);
if (interface = (PINTERFACE)handlerEntry->TranslatorInterface) {
(interface->InterfaceDereference)(interface->Context);
ExFreePool(interface);
}
ExFreePool(entry);
}
InitializeListHead(&deviceNode->DeviceTranslatorList);
deviceNode->NoArbiterMask = 0;
deviceNode->QueryArbiterMask = 0;
deviceNode->NoTranslatorMask = 0;
deviceNode->QueryTranslatorMask = 0;
}
NTSTATUS IopAsynchronousCall(IN PDEVICE_OBJECT TargetDevice,
IN PIO_STACK_LOCATION TopStackLocation,
IN PDEVICE_COMPLETION_CONTEXT CompletionContext,
IN PVOID CompletionRoutine)
/*++
Routine Description:
This function sends an Asynchronous irp to the top level device object which roots on DeviceObject.
Parameters:
DeviceObject - Supplies the device object of the device being removed.
TopStackLocation - Supplies a pointer to the parameter block for the irp.
CompletionContext -
CompletionRoutine -
Return Value:
NTSTATUS code.
*/
{
PDEVICE_OBJECT deviceObject;
PIRP irp;
PIO_STACK_LOCATION irpSp;
NTSTATUS status;
PAGED_CODE();
// If the target device object has a VPB associated with it and a file system is mounted,
// make the filesystem's volume device object the irp target.
// BUGBUG - I don't think we need to do this. The filesystem
// driver should attach to the PDO chain or registered for device change registration.
if ((TargetDevice->Vpb != NULL) && (TargetDevice->Vpb->Flags & VPB_MOUNTED)) {
deviceObject = TargetDevice->Vpb->DeviceObject;
} else {
deviceObject = TargetDevice;
}
ASSERT(deviceObject != NULL);
// Get a pointer to the topmost device object in the stack of devices, beginning with the deviceObject.
deviceObject = IoGetAttachedDevice(deviceObject);
// Allocate an I/O Request Packet (IRP) for this device removal operation.
irp = IoAllocateIrp((CCHAR)(deviceObject->StackSize), FALSE);
if (!irp) {
return STATUS_INSUFFICIENT_RESOURCES;
}
SPECIALIRP_WATERMARK_IRP(irp, IRP_SYSTEM_RESTRICTED);
// Initialize it to failure.
irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
irp->IoStatus.Information = 0;
// Get a pointer to the next stack location in the packet. This location
// will be used to pass the function codes and parameters to the first driver.
irpSp = IoGetNextIrpStackLocation(irp);
// Fill in the IRP according to this request.
irp->Tail.Overlay.Thread = PsGetCurrentThread();
irp->RequestorMode = KernelMode;
irp->UserIosb = NULL;
irp->UserEvent = NULL;
// Copy in the caller-supplied stack location contents
*irpSp = *TopStackLocation;
#if DBG
CompletionContext->Id = irp;
#endif
IoSetCompletionRoutine(irp,
CompletionRoutine,
CompletionContext, /* Completion context */
TRUE, /* Invoke on success */
TRUE, /* Invoke on error */
TRUE /* Invoke on cancel */
);
status = IoCallDriver(deviceObject, irp);
return status;
}
NTSTATUS IopSynchronousCall(IN PDEVICE_OBJECT DeviceObject, IN PIO_STACK_LOCATION TopStackLocation, OUT PVOID *Information)
/*++
Routine Description:
This function sends a synchronous irp to the top level device object which roots on DeviceObject.
Parameters:
DeviceObject - Supplies the device object of the device being removed.
TopStackLocation - Supplies a pointer to the parameter block for the irp.
Information - Supplies a pointer to a variable to receive the returned information of the irp.
Return Value:
NTSTATUS code.
*/
{
PIRP irp;
PIO_STACK_LOCATION irpSp;
IO_STATUS_BLOCK statusBlock;
KEVENT event;
NTSTATUS status;
PULONG_PTR returnInfo = (PULONG_PTR)Information;
PDEVICE_OBJECT deviceObject;
PAGED_CODE();
// Get a pointer to the topmost device object in the stack of devices, beginning with the deviceObject.
deviceObject = IoGetAttachedDevice(DeviceObject);
// Begin by allocating the IRP for this request.
// Do not charge quota to the current process for this IRP.
irp = IoAllocateIrp(deviceObject->StackSize, FALSE);
if (irp == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
SPECIALIRP_WATERMARK_IRP(irp, IRP_SYSTEM_RESTRICTED);
// Initialize it to failure.
irp->IoStatus.Status = statusBlock.Status = STATUS_NOT_SUPPORTED;
irp->IoStatus.Information = statusBlock.Information = 0;
// Set the pointer to the status block and initialized event.
KeInitializeEvent(&event, SynchronizationEvent, FALSE);
irp->UserIosb = &statusBlock;
irp->UserEvent = &event;
irp->Tail.Overlay.Thread = PsGetCurrentThread();// Set the address of the current thread
IopQueueThreadIrp(irp);// Queue this irp onto the current thread
// Get a pointer to the stack location of the first driver which will be invoked.
// This is where the function codes and parameters are set.
irpSp = IoGetNextIrpStackLocation(irp);
*irpSp = *TopStackLocation;// Copy in the caller-supplied stack location contents
// Call the driver
status = IoCallDriver(deviceObject, irp);
PnpIrpStatusTracking(status, TopStackLocation->MinorFunction, deviceObject);
if (status == STATUS_PENDING) {// If a driver returns STATUS_PENDING, we will wait for it to complete
(VOID)KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, (PLARGE_INTEGER)NULL);
status = statusBlock.Status;
}
*returnInfo = (ULONG_PTR)statusBlock.Information;
return status;
}
VOID IopStartDevice(IN PDEVICE_OBJECT DeviceObject)
/*++
Routine Description:
This function sends a start device irp to the top level device object which roots on DeviceObject.
Parameters:
DeviceObject - Supplies the pointer to the device object of the device being removed.
Return Value:
NTSTATUS code.
*/
{
IO_STACK_LOCATION irpSp;
PVOID dummy;
PDEVICE_NODE deviceNode = (PDEVICE_NODE)DeviceObject->DeviceObjectExtension->DeviceNode;
PDEVICE_COMPLETION_CONTEXT completionContext;
NTSTATUS status;
PNP_VETO_TYPE vetoType;
// PAGED_CODE();
// Initialize the stack location to pass to IopSynchronousCall()
RtlZeroMemory(&irpSp, sizeof(IO_STACK_LOCATION));
// Set the function codes.
irpSp.MajorFunction = IRP_MJ_PNP;
irpSp.MinorFunction = IRP_MN_START_DEVICE;
// Set the pointers for the raw and translated resource lists
if (!(deviceNode->Flags & DNF_RESOURCE_REPORTED)) {
irpSp.Parameters.StartDevice.AllocatedResources = deviceNode->ResourceList;
irpSp.Parameters.StartDevice.AllocatedResourcesTranslated = deviceNode->ResourceListTranslated;
}
if (!(deviceNode->Flags & DNF_STOPPED)) {
IopUncacheInterfaceInformation(DeviceObject);
}
if (PnpAsyncOk) {
// ADRIAO BUGBUG 11/18/98 -
// The dock code has not been duplicated in the async case, but as
// PnpAsync support is totally broken and disabled anyway, this should not matter right now.
ASSERT(0);
completionContext = (PDEVICE_COMPLETION_CONTEXT)ExAllocatePool(NonPagedPool, sizeof(DEVICE_COMPLETION_CONTEXT));
if (completionContext == NULL) {
return; // BUGBUG - Should try it again *explicitly*
}
completionContext->DeviceNode = deviceNode;
completionContext->IrpMinorCode = IRP_MN_START_DEVICE;
completionContext->Thread = ExGetCurrentResourceThread();
// Make the call and return.
IopAcquireEnumerationLock(NULL); // To block IopAcquireTreeLock();
status = IopAsynchronousCall(DeviceObject, &irpSp, completionContext, IopDeviceStartComplete);
if (status == STATUS_PENDING) {
KIRQL oldIrql;
// Set DNF_START_REQUEST_PENDING flag such that the completion routine knows it needs to request enumeration when the start completed successfully.
ExAcquireSpinLock(&IopPnPSpinLock, &oldIrql);
if (!(IopDoesDevNodeHaveProblem(deviceNode) || (deviceNode->Flags & DNF_STARTED))) {
deviceNode->Flags |= DNF_START_REQUEST_PENDING;
}
ExReleaseSpinLock(&IopPnPSpinLock, oldIrql);
}
} else {
if ((deviceNode->Flags & DNF_STOPPED) || (deviceNode->DockInfo.DockStatus == DOCK_NOTDOCKDEVICE)) {
// This is either the rebalance case in which we are restarting a temporarily stopped device,
// or we are starting a non-dock device, and it was previously off.
status = IopSynchronousCall(DeviceObject, &irpSp, &dummy);
} else {
// This is a dock so we a little bit of work before starting it.
// Take the profile change semaphore.
// We do this whenever a dock is in our list, even if no query is going to occur.
IopHardwareProfileBeginTransition(FALSE);
// Tell the profile code what dock device object may be bringing the new hardware profile online.
IopHardwareProfileMarkDock(deviceNode, DOCK_ARRIVING);
// Ask everyone if this is really a good idea right now.
// Note that PiProcessStart calls IopNewDevice calls IopStartAndEnumerateDevice who calls this function on that thread.
// Therefore we may indded be in an event, although this would probably be a fairly rare event for a dock.
status = IopHardwareProfileQueryChange(FALSE, PROFILE_PERHAPS_IN_PNPEVENT, &vetoType, NULL);
if (NT_SUCCESS(status)) {
status = IopSynchronousCall(DeviceObject, &irpSp, &dummy);
}
if (NT_SUCCESS(status)) {
// Commit the current Hardware Profile as necessary.
IopHardwareProfileCommitStartedDock(deviceNode);
} else {
IopHardwareProfileCancelTransition();
}
}
if (!NT_SUCCESS(status)) {
ULONG Problem = CM_PROB_FAILED_START;
SAVE_FAILURE_INFO(deviceNode, status);
// Handle certain problems determined by the status code
switch (status) {
case STATUS_PNP_REBOOT_REQUIRED:
Problem = CM_PROB_NEED_RESTART;
break;
default:
Problem = CM_PROB_FAILED_START;
}
IopSetDevNodeProblem(deviceNode, Problem);
if (deviceNode->DockInfo.DockStatus == DOCK_NOTDOCKDEVICE) {
IopRequestDeviceRemoval(deviceNode->PhysicalDeviceObject, CM_PROB_FAILED_START);
} else {
ASSERT(deviceNode->DockInfo.DockStatus == DOCK_QUIESCENT);
PpSetTargetDeviceRemove(deviceNode->PhysicalDeviceObject,
FALSE,
TRUE,
TRUE,
CM_PROB_DEVICE_NOT_THERE,
NULL,
NULL,
NULL,
NULL);
}
} else {
IopDoDeferredSetInterfaceState(deviceNode);
deviceNode->Flags |= DNF_STARTED;
if (deviceNode->Flags & DNF_STOPPED) {
// If the start is initiated by rebalancing, do NOT do enumeration
deviceNode->Flags &= ~DNF_STOPPED;
} else {
deviceNode->Flags |= DNF_NEED_ENUMERATION_ONLY;
}
}
}
}
NTSTATUS IopEjectDevice(IN PDEVICE_OBJECT DeviceObject, IN OUT PPENDING_RELATIONS_LIST_ENTRY PendingEntry)
/*++
Routine Description:
This function sends an eject device irp to the top level device object which roots on DeviceObject.
Parameters:
DeviceObject - Supplies a pointer to the device object of the device being removed.
Return Value:
NTSTATUS code.
*/
{
PIO_STACK_LOCATION irpSp;
NTSTATUS status;
PDEVICE_NODE deviceNode = (PDEVICE_NODE)DeviceObject->DeviceObjectExtension->DeviceNode;
PDEVICE_OBJECT deviceObject;
PIRP irp;
PAGED_CODE();
if (PendingEntry->LightestSleepState != PowerSystemWorking) {
// We have to warm eject.
if (PendingEntry->DockInterface) {
PendingEntry->DockInterface->ProfileDepartureSetMode(PendingEntry->DockInterface->Context, PDS_UPDATE_ON_EJECT);
}
PendingEntry->EjectIrp = NULL;
InitializeListHead(&PendingEntry->Link);
IopQueuePendingEject(PendingEntry);
ExInitializeWorkItem(&PendingEntry->WorkItem, IopProcessCompletedEject, PendingEntry);
ExQueueWorkItem(&PendingEntry->WorkItem, DelayedWorkQueue);
return STATUS_SUCCESS;
}
if (PendingEntry->DockInterface) {
// Notify dock that now is a good time to update it's hardware profile.
PendingEntry->DockInterface->ProfileDepartureSetMode(PendingEntry->DockInterface->Context, PDS_UPDATE_ON_INTERFACE);
PendingEntry->DockInterface->ProfileDepartureUpdate(PendingEntry->DockInterface->Context);
if (PendingEntry->DisplaySafeRemovalDialog) {
PpNotifyUserModeRemovalSafe(DeviceObject);
PendingEntry->DisplaySafeRemovalDialog = FALSE;
}
}
// Get a pointer to the topmost device object in the stack of devices, beginning with the deviceObject.
deviceObject = IoGetAttachedDevice(DeviceObject);
// Allocate an I/O Request Packet (IRP) for this device removal operation.
irp = IoAllocateIrp((CCHAR)(deviceObject->StackSize), FALSE);
if (!irp) {
PendingEntry->EjectIrp = NULL;
InitializeListHead(&PendingEntry->Link);
IopQueuePendingEject(PendingEntry);
ExInitializeWorkItem(&PendingEntry->WorkItem, IopProcessCompletedEject, PendingEntry);
ExQueueWorkItem(&PendingEntry->WorkItem, DelayedWorkQueue);
return STATUS_INSUFFICIENT_RESOURCES;
}
SPECIALIRP_WATERMARK_IRP(irp, IRP_SYSTEM_RESTRICTED);
// Initialize it to failure.
irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
irp->IoStatus.Information = 0;
// Get a pointer to the next stack location in the packet.
// This location will be used to pass the function codes and parameters to the first driver.
irpSp = IoGetNextIrpStackLocation(irp);
RtlZeroMemory(irpSp, sizeof(IO_STACK_LOCATION));// Initialize the stack location to pass to IopSynchronousCall()
// Set the function codes.
irpSp->MajorFunction = IRP_MJ_PNP;
irpSp->MinorFunction = IRP_MN_EJECT;
// Fill in the IRP according to this request.
irp->Tail.Overlay.Thread = PsGetCurrentThread();
irp->RequestorMode = KernelMode;
irp->UserIosb = NULL;
irp->UserEvent = NULL;
PendingEntry->EjectIrp = irp;
IopQueuePendingEject(PendingEntry);
IoSetCompletionRoutine(irp,
IopDeviceEjectComplete,
PendingEntry, /* Completion context */
TRUE, /* Invoke on success */
TRUE, /* Invoke on error */
TRUE /* Invoke on cancel */
);
status = IoCallDriver(deviceObject, irp);
return status;
}
NTSTATUS IopDeviceEjectComplete(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context)
{
PPENDING_RELATIONS_LIST_ENTRY entry = (PPENDING_RELATIONS_LIST_ENTRY)Context;
PIRP ejectIrp;
ejectIrp = InterlockedExchangePointer(&entry->EjectIrp, NULL);
ASSERT(ejectIrp == NULL || ejectIrp == Irp);
// Queue a work item to finish up the eject.
// We queue a work item because we are probably running at dispatch level in some random context.
ExInitializeWorkItem(&entry->WorkItem, IopProcessCompletedEject, entry);
ExQueueWorkItem(&entry->WorkItem, DelayedWorkQueue);
IoFreeIrp(Irp);
return STATUS_MORE_PROCESSING_REQUIRED;
}
NTSTATUS IopRemoveDevice(IN PDEVICE_OBJECT TargetDevice, IN ULONG IrpMinorCode)
/*++
Routine Description:
This function sends a requested DeviceRemoval related irp to the top level device object which roots on TargetDevice.
If there is a VPB associated with the TargetDevice, the corresponding filesystem's VDO will be used.
Otherwise the irp will be sent directly to the target device/ or its assocated device object.
Parameters:
TargetDevice - Supplies the device object of the device being removed.
Operation - Specifies the operation requested.
The following IRP codes are used with IRP_MJ_DEVICE_CHANGE for removing
devices:
IRP_MN_QUERY_REMOVE_DEVICE
IRP_MN_CANCEL_REMOVE_DEVICE
IRP_MN_REMOVE_DEVICE
IRP_MN_EJECT
Return Value:
NTSTATUS code.
*/
{
PDEVICE_OBJECT deviceObject;
PIRP irp;
IO_STACK_LOCATION irpSp;
NTSTATUS status;
BOOLEAN isMountable = FALSE;
PDEVICE_OBJECT mountedDevice;
PVOID dummy;
LOCK_MOUNTABLE_DEVICE_CONTEXT lockContext;
PAGED_CODE();
ASSERT(IrpMinorCode == IRP_MN_QUERY_REMOVE_DEVICE ||
IrpMinorCode == IRP_MN_CANCEL_REMOVE_DEVICE ||
IrpMinorCode == IRP_MN_REMOVE_DEVICE ||
IrpMinorCode == IRP_MN_SURPRISE_REMOVAL ||
IrpMinorCode == IRP_MN_EJECT);
if (IrpMinorCode == IRP_MN_REMOVE_DEVICE || IrpMinorCode == IRP_MN_QUERY_REMOVE_DEVICE) {
IopUncacheInterfaceInformation(TargetDevice);
}
// Initialize the stack location to pass to IopSynchronousCall()
RtlZeroMemory(&irpSp, sizeof(IO_STACK_LOCATION));
irpSp.MajorFunction = IRP_MJ_PNP;
irpSp.MinorFunction = (UCHAR)IrpMinorCode;
// BUGBUG - this should probably go at a higher level but then it goes in way too many places.
// The only thing we do to the VPB is make sure that it won't go away while the operation is in the file system and
// that no one new can mount on the device if the FS decides to bail out.
// Check to see if there's a VPB anywhere in the device stack.
// If there is then we'll have to lock the stack.
mountedDevice = IopFindMountableDevice(TargetDevice);
if (mountedDevice != NULL) {
// This routine will cause any mount operations on the VPB to fail.
// It will also release the VPB spinlock.
mountedDevice = IopLockMountedDeviceForRemove(TargetDevice, IrpMinorCode, &lockContext);
isMountable = TRUE;
} else {
ASSERTMSG("Mass storage device does not have VPB - this is odd",
!((TargetDevice->Type == FILE_DEVICE_DISK) ||
(TargetDevice->Type == FILE_DEVICE_CD_ROM) ||
(TargetDevice->Type == FILE_DEVICE_TAPE) ||
(TargetDevice->Type == FILE_DEVICE_VIRTUAL_DISK)));
mountedDevice = TargetDevice;
}
// Make the call and return.
if (IrpMinorCode == IRP_MN_SURPRISE_REMOVAL || IrpMinorCode == IRP_MN_REMOVE_DEVICE) {
// if device was not disableable, we cleanup the tree and debug-trace that we surprise-removed a non-disableable device
PDEVICE_NODE deviceNode = TargetDevice->DeviceObjectExtension->DeviceNode;
if (deviceNode->UserFlags & DNUF_NOT_DISABLEABLE) {
// this device was marked as disableable, update the depends before this device disappears
// (by momentarily marking this node as disableable)
deviceNode->UserFlags &= ~DNUF_NOT_DISABLEABLE;
IopDecDisableableDepends(deviceNode);
}
}
status = IopSynchronousCall(mountedDevice, &irpSp, &dummy);
if (isMountable) {
IopUnlockMountedDeviceForRemove(TargetDevice, IrpMinorCode, &lockContext);
// Succesful query should follow up with invalidation of all volumes which have been on this device but which are not currently mounted.
if (IrpMinorCode == IRP_MN_QUERY_REMOVE_DEVICE && NT_SUCCESS(status)) {
status = IopInvalidateVolumesForDevice(TargetDevice);
}
}
if (IrpMinorCode == IRP_MN_REMOVE_DEVICE) {
((PDEVICE_NODE)TargetDevice->DeviceObjectExtension->DeviceNode)->Flags &=
~(DNF_ADDED | DNF_STARTED | DNF_LEGACY_DRIVER | DNF_NEED_QUERY_IDS | DNF_NEED_ENUMERATION_ONLY);
}
return status;
}
PDEVICE_OBJECT IopLockMountedDeviceForRemove(IN PDEVICE_OBJECT DeviceObject,
IN ULONG IrpMinorCode,
OUT PLOCK_MOUNTABLE_DEVICE_CONTEXT Context)
/*++
Routine Description:
This routine will scan up the device stack and mark each unmounted VPB it finds with the VPB_REMOVE_PENDING bit (or clear it in the case of cancel) and
increment (or decrement in the case of cancel) the reference count in the VPB.
This is to ensure that no new file system can get mounted on the device stack while the remove operation is in place.
The search will terminate once all the attached device objects have been marked, or once a mounted device object has been marked.
Arguments:
DeviceObject - the PDO we are attempting to remove
IrpMinorCode - the remove-type operation we are going to perform
Context - a context block which must be passed in to the unlock operation
Return Value:
A pointer to the device object stack which the remove request should be sent to.
If a mounted file system was found, this will be the lowest file system device object in the mounted stack.
Otherwise this will be the PDO which was passed in.
*/
{
PVPB vpb;
PDEVICE_OBJECT device = DeviceObject;
PDEVICE_OBJECT fsDevice = NULL;
BOOLEAN isRemovePendingSet;
KIRQL oldIrql;
RtlZeroMemory(Context, sizeof(LOCK_MOUNTABLE_DEVICE_CONTEXT));
Context->MountedDevice = DeviceObject;
do {
// Walk up each device object in the stack.
// For each one, if a VPB exists, grab the database resource exclusive followed by the device lock.
// Then acquire the Vpb spinlock and perform the appropriate magic on the device object.
// NOTE - Its unfortunate that the locking order includes grabbing the device specific lock first followed by the global lock.
if (device->Vpb != NULL) {
// Grab the device lock. This will ensure that there are no mount or verify operations in progress.
KeWaitForSingleObject(&(device->DeviceLock), Executive, KernelMode, FALSE, NULL);
// Now set the remove pending flag, which will prevent new mounts from occuring on this stack once the current (if existant)
// filesystem dismounts. Filesystems will preserve the flag across vpb swaps.
IoAcquireVpbSpinLock(&oldIrql);
vpb = device->Vpb;
ASSERT(vpb != NULL);
switch (IrpMinorCode) {
case IRP_MN_QUERY_REMOVE_DEVICE:
case IRP_MN_SURPRISE_REMOVAL:
case IRP_MN_REMOVE_DEVICE:
{
vpb->Flags |= VPB_REMOVE_PENDING;
break;
}
case IRP_MN_CANCEL_REMOVE_DEVICE:
{
vpb->Flags &= ~VPB_REMOVE_PENDING;
break;
}
default:
break;
}
// Note the device object that has the filesystem stack attached.
// We must remember the vpb we referenced that had the fs because it may be swapped off of the storage device during a dismount operation.
if (vpb->Flags & VPB_MOUNTED) {
Context->MountedDevice = device;
fsDevice = vpb->DeviceObject;
}
IoReleaseVpbSpinLock(oldIrql);
KeSetEvent(&(device->DeviceLock), IO_NO_INCREMENT, FALSE);
// Stop if we hit a device with a mounted filesystem.
if (Context->MountedDevice != NULL) {
// We found and setup a mounted device. Time to return.
break;
}
}
ExAcquireFastLock(&IopDatabaseLock, &oldIrql);
device = device->AttachedDevice;
ExReleaseFastLock(&IopDatabaseLock, oldIrql);
} while (device != NULL);
if (fsDevice != NULL) {
return fsDevice;
}
return Context->MountedDevice;
}
VOID IopUnlockMountedDeviceForRemove(IN PDEVICE_OBJECT DeviceObject, IN ULONG IrpMinorCode, IN PLOCK_MOUNTABLE_DEVICE_CONTEXT Context)
{
PVPB vpb;
BOOLEAN removePendingSet;
PDEVICE_OBJECT device = DeviceObject;
do {
KIRQL oldIrql;
// Walk up each device object in the stack.
// For each one, if a VPB exists, grab the database resource exclusive followed by the device lock.
// Then acquire the Vpb spinlock and perform the appropriate magic on the device object.
// NOTE - It's unfortunate that the locking order includes grabing the device specific lock first followed by the global lock.
if (device->Vpb != NULL) {
// Grab the device lock.
// This will ensure that there are no mount or verify operations in progress, which in turn will ensure that any mounted file system won't go away.
KeWaitForSingleObject(&(device->DeviceLock), Executive, KernelMode, FALSE, NULL);
// Now decrement the reference count in the VPB.
// If the remove pending flag has been set in the VPB (if this is a QUERY or a REMOVE) then even on a dismount no new file system will be allowed onto the device.
IoAcquireVpbSpinLock(&oldIrql);
if (IrpMinorCode == IRP_MN_REMOVE_DEVICE) {
device->Vpb->Flags &= ~VPB_REMOVE_PENDING;
}
IoReleaseVpbSpinLock(oldIrql);
KeSetEvent(&(device->DeviceLock), IO_NO_INCREMENT, FALSE);
}
// Continue up the chain until we know we hit the device the fs mounted on, if any.
if (Context->MountedDevice == device) {
break;
} else {
ExAcquireFastLock(&IopDatabaseLock, &oldIrql);
device = device->AttachedDevice;
ExReleaseFastLock(&IopDatabaseLock, oldIrql);
}
} while (device != NULL);
}
PDEVICE_OBJECT IopFindMountableDevice(IN PDEVICE_OBJECT DeviceObject)
/*++
Routine Description:
This routine will scan up the device stack and find a device which could finds with the VPB_REMOVE_PENDING bit (or clear it in the case of cancel)
and increment (or decrement in the case of cancel) the reference count in the VPB.
This is to ensure that no new file system can get mounted on the device stack while the remove operation is in place.
The search will terminate once all the attached device objects have been marked, or once a mounted device object has been marked.
Arguments:
DeviceObject - the PDO we are attempting to remove
IrpMinorCode - the remove-type operation we are going to perform
Context - a context block which must be passed in to the unlock operation
Return Value:
A pointer to the device object stack which the remove request should be sent to.
If a mounted file system was found, this will be the lowest file system device object in the mounted stack.
Otherwise this will be the PDO which was passed in.
*/
{
PVPB vpb;
PDEVICE_OBJECT mountableDevice = DeviceObject;
while (mountableDevice != NULL) {
if ((mountableDevice->Flags & DO_DEVICE_HAS_NAME) && (mountableDevice->Vpb != NULL)) {
return mountableDevice;
}
mountableDevice = mountableDevice->AttachedDevice;
}
return NULL;
}
NTSTATUS IopDeviceStartComplete(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context)
/*++
Routine Description:
Completion function for an Async Start IRP.
Arguments:
DeviceObject - NULL.
Irp - SetPower irp which has completed
Context - a pointer to the DEVICE_CHANGE_COMPLETION_CONTEXT.
Return Value:
STATUS_MORE_PROCESSING_REQUIRED is returned to IoCompleteRequest to signify that IoCompleteRequest should not continue processing the IRP.
*/
{
PDEVICE_NODE deviceNode = ((PDEVICE_COMPLETION_CONTEXT)Context)->DeviceNode;
ERESOURCE_THREAD LockingThread = ((PDEVICE_COMPLETION_CONTEXT)Context)->Thread;
ULONG oldFlags;
KIRQL oldIrql;
// Read state from Irp.
#if DBG
if (((PDEVICE_COMPLETION_CONTEXT)Context)->Id != (PVOID)Irp) {
ASSERT(0);
IoFreeIrp(Irp);
ExFreePool(Context);
return STATUS_MORE_PROCESSING_REQUIRED;
}
#endif
PnpIrpStatusTracking(Irp->IoStatus.Status, IRP_MN_START_DEVICE, deviceNode->PhysicalDeviceObject);
ExAcquireSpinLock(&IopPnPSpinLock, &oldIrql);
oldFlags = deviceNode->Flags;
deviceNode->Flags &= ~DNF_START_REQUEST_PENDING;
if (NT_SUCCESS(Irp->IoStatus.Status)) {
if (deviceNode->Flags & DNF_STOPPED) {
// If the start is initiated by rebalancing, do NOT do enumeration
deviceNode->Flags &= ~DNF_STOPPED;
deviceNode->Flags |= DNF_STARTED;
ExReleaseSpinLock(&IopPnPSpinLock, oldIrql);
} else {
// Otherwise, we need to queue a request to enumerate the device if DNF_START_REQUEST_PENDING is set.
// (IopStartDevice sets the flag if status of start irp returns pending.)
deviceNode->Flags |= DNF_NEED_ENUMERATION_ONLY + DNF_STARTED;
ExReleaseSpinLock(&IopPnPSpinLock, oldIrql);
if (oldFlags & DNF_START_REQUEST_PENDING) {
IopRequestDeviceAction(deviceNode->PhysicalDeviceObject, ReenumerateDeviceTree, NULL, NULL);
}
}
} else {
// The start failed. We will remove the device
deviceNode->Flags &= ~(DNF_STOPPED | DNF_STARTED);
ExReleaseSpinLock(&IopPnPSpinLock, oldIrql);
SAVE_FAILURE_INFO(deviceNode, Irp->IoStatus.Status);
IopSetDevNodeProblem(deviceNode, CM_PROB_FAILED_START);
if (deviceNode->DockInfo.DockStatus == DOCK_NOTDOCKDEVICE) {
IopRequestDeviceRemoval(deviceNode->PhysicalDeviceObject, CM_PROB_FAILED_START);
} else {
ASSERT(deviceNode->DockInfo.DockStatus == DOCK_ARRIVING);
// ADRIAO BUGBUG 05/12/1999 -
// We never go down this path, but if we fix it a function
// similar to IopRequestDeviceEjectWorker needs to be queued, one that passes in "FALSE" for the kernel initiated parameter.
ASSERT(0);
IoRequestDeviceEject(deviceNode->PhysicalDeviceObject);
}
}
IopReleaseEnumerationLockForThread(NULL, LockingThread);
// Irp processing is complete, free the irp and then return more_processing_required which causes IoCompleteRequest to stop "completing" this irp any future.
IoFreeIrp(Irp);
ExFreePool(Context);
return STATUS_MORE_PROCESSING_REQUIRED;
}
NTSTATUS IopDeviceRelationsComplete(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context)
/*++
Routine Description:
Completion function for a QueryDeviceRelations IRP. T
Arguments:
DeviceObject - NULL.
Irp - SetPower irp which has completed
Context - a pointer to the DEVICE_CHANGE_COMPLETION_CONTEXT.
Return Value:
STATUS_MORE_PROCESSING_REQUIRED is returned to IoCompleteRequest to signify that IoCompleteRequest should not continue processing the IRP.
*/
{
PDEVICE_NODE deviceNode = ((PDEVICE_COMPLETION_CONTEXT)Context)->DeviceNode;
ERESOURCE_THREAD LockingThread = ((PDEVICE_COMPLETION_CONTEXT)Context)->Thread;
KIRQL oldIrql;
BOOLEAN requestEnumeration = FALSE;
#if DBG
if (((PDEVICE_COMPLETION_CONTEXT)Context)->Id != (PVOID)Irp) {
ASSERT(0);
IoFreeIrp(Irp);
ExFreePool(Context);
return STATUS_MORE_PROCESSING_REQUIRED;
}
#endif
PnpIrpStatusTracking(Irp->IoStatus.Status, IRP_MN_QUERY_DEVICE_RELATIONS, deviceNode->PhysicalDeviceObject);
ExAcquireSpinLock(&IopPnPSpinLock, &oldIrql);
deviceNode->Flags &= ~DNF_BEING_ENUMERATED;
// Read state from Irp.
if (NT_SUCCESS(Irp->IoStatus.Status) && (Irp->IoStatus.Information)) {
// It is VERY important that we set PendingDeviceRealtions field before checking DNF_ENUMERATION_REQUEST_PENDING.
ASSERT(deviceNode->OverUsed1.PendingDeviceRelations == NULL);
deviceNode->OverUsed1.PendingDeviceRelations = (PDEVICE_RELATIONS)Irp->IoStatus.Information;
}
if (deviceNode->Flags & DNF_ENUMERATION_REQUEST_PENDING) {
ExReleaseSpinLock(&IopPnPSpinLock, oldIrql);
if (deviceNode->OverUsed1.PendingDeviceRelations) {
// If the query_device_relations irp returns something, we will make a request to process the childrens.
IopRequestDeviceAction(deviceNode->PhysicalDeviceObject, RestartEnumeration, NULL, NULL);
} else {
deviceNode->Flags &= ~DNF_ENUMERATION_REQUEST_PENDING;
}
} else {
deviceNode->Flags |= DNF_ENUMERATION_REQUEST_PENDING;
ExReleaseSpinLock(&IopPnPSpinLock, oldIrql);
}
IopReleaseEnumerationLockForThread(NULL, LockingThread);
// Irp processing is complete,
// free the irp and then return more_processing_required which causes IoCompleteRequest to stop "completing" this irp any future.
IoFreeIrp(Irp);
ExFreePool(Context);
ExAcquireSpinLock(&IopPnPSpinLock, &oldIrql);
if (deviceNode->Flags & DNF_IO_INVALIDATE_DEVICE_RELATIONS_PENDING) {
deviceNode->Flags &= ~DNF_IO_INVALIDATE_DEVICE_RELATIONS_PENDING;
requestEnumeration = TRUE;
}
ExReleaseSpinLock(&IopPnPSpinLock, oldIrql);
if (requestEnumeration) {
IopRequestDeviceAction(DeviceObject, ReenumerateDeviceTree, NULL, NULL);
}
return STATUS_MORE_PROCESSING_REQUIRED;
}
NTSTATUS IopQueryDeviceRelations(IN DEVICE_RELATION_TYPE Relations,
IN PDEVICE_OBJECT DeviceObject,
IN BOOLEAN AsyncOk,
OUT PDEVICE_RELATIONS *DeviceRelations)
/*++
Routine Description:
This routine sends query device relation irp to the specified device object.
Parameters:
Relations - specifies the type of relation interested.
DeviceObjet - Supplies the device object of the device being queried.
AsyncOk - Specifies if we can perform Async QueryDeviceRelations
DeviceRelations - Supplies a pointer to a variable to receive the returned relation information. This must be freed by the caller.
Return Value:
NTSTATUS code.
*/
{
IO_STACK_LOCATION irpSp;
NTSTATUS status;
PDEVICE_RELATIONS deviceRelations;
PDEVICE_NODE deviceNode = (PDEVICE_NODE)DeviceObject->DeviceObjectExtension->DeviceNode;
PDEVICE_COMPLETION_CONTEXT completionContext;
BOOLEAN requestEnumeration = FALSE;
KIRQL oldIrql;
// Do not allow two bus relations at the same time
if (Relations == BusRelations) {
ExAcquireSpinLock(&IopPnPSpinLock, &oldIrql);
if (deviceNode->Flags & DNF_BEING_ENUMERATED) {
ExReleaseSpinLock(&IopPnPSpinLock, oldIrql);
return STATUS_UNSUCCESSFUL;
}
deviceNode->Flags &= ~DNF_ENUMERATION_REQUEST_QUEUED;
deviceNode->Flags |= DNF_BEING_ENUMERATED;
ExReleaseSpinLock(&IopPnPSpinLock, oldIrql);
}
// Initialize the stack location to pass to IopSynchronousCall()
RtlZeroMemory(&irpSp, sizeof(IO_STACK_LOCATION));
// Set the function codes.
irpSp.MajorFunction = IRP_MJ_PNP;
irpSp.MinorFunction = IRP_MN_QUERY_DEVICE_RELATIONS;
// Set the pointer to the resource list
irpSp.Parameters.QueryDeviceRelations.Type = Relations;
// Make the call and return.
if (AsyncOk && Relations == BusRelations) {
completionContext = (PDEVICE_COMPLETION_CONTEXT)ExAllocatePool(NonPagedPool, sizeof(DEVICE_COMPLETION_CONTEXT));
if (completionContext == NULL) {
return STATUS_INSUFFICIENT_RESOURCES; // BUGBUG - Should try it again.
}
completionContext->DeviceNode = deviceNode;
completionContext->IrpMinorCode = IRP_MN_QUERY_DEVICE_RELATIONS;
completionContext->Thread = ExGetCurrentResourceThread();
// Make the call and return.
IopAcquireEnumerationLock(NULL); // To block IopAcquireTreeLock();
status = IopAsynchronousCall(DeviceObject, &irpSp, completionContext, IopDeviceRelationsComplete);
if (status == STATUS_PENDING) {
KIRQL oldIrql;
ExAcquireSpinLock(&IopPnPSpinLock, &oldIrql);
// Check if the completion routine completes before we setting the DNF_ENUMERATION_REQUEST_PENDING flags.
if (deviceNode->Flags & DNF_ENUMERATION_REQUEST_PENDING) {
deviceNode->Flags &= ~DNF_ENUMERATION_REQUEST_PENDING;
*DeviceRelations = deviceNode->OverUsed1.PendingDeviceRelations;
deviceNode->OverUsed1.PendingDeviceRelations = NULL;
status = STATUS_SUCCESS;
} else {
// Set DNF_ENUMERATION_REQUEST_PENDING such that the completion routine knows it needs to request enumeration when the Q_bus_relations completed successfully.
deviceNode->Flags |= DNF_ENUMERATION_REQUEST_PENDING;
}
ExReleaseSpinLock(&IopPnPSpinLock, oldIrql);
} else {
deviceNode->Flags &= ~DNF_ENUMERATION_REQUEST_PENDING;
*DeviceRelations = deviceNode->OverUsed1.PendingDeviceRelations;
deviceNode->OverUsed1.PendingDeviceRelations = NULL;
}
return status;
} else {
status = IopSynchronousCall(DeviceObject, &irpSp, DeviceRelations);
// To prevent the scenario that a driver calls IoInvalidateDeviceRelations while servicing an Async Q-D-R irp,
// and receives another q-d-r irp before first one completed, we will
// set a flag when it calls IoInvalidateDeviceRelations and delay queuing the request till the original one is completed.
if (Relations == BusRelations) {
ExAcquireSpinLock(&IopPnPSpinLock, &oldIrql);
deviceNode->Flags &= ~DNF_BEING_ENUMERATED;
if (deviceNode->Flags & DNF_IO_INVALIDATE_DEVICE_RELATIONS_PENDING) {
deviceNode->Flags &= ~DNF_IO_INVALIDATE_DEVICE_RELATIONS_PENDING;
requestEnumeration = TRUE;
}
ExReleaseSpinLock(&IopPnPSpinLock, oldIrql);
if (requestEnumeration) {
IopRequestDeviceAction(DeviceObject, ReenumerateDeviceTree, NULL, NULL);
}
}
}
return status;
}
NTSTATUS IopQueryDeviceId(IN PDEVICE_OBJECT DeviceObject, OUT PWCHAR *DeviceId)
/*++
Routine Description:
This routine sends a query device id irp to the specified device object.
Parameters:
DeviceObject - Supplies the device object of the device being queried/
DeviceId - Supplies a pointer to a variable to receive the returned Id.
This must be freed by the caller.
Return Value:
NTSTATUS code.
*/
{
IO_STACK_LOCATION irpSp;
NTSTATUS status;
PAGED_CODE();
// Initialize the stack location to pass to IopSynchronousCall()
RtlZeroMemory(&irpSp, sizeof(IO_STACK_LOCATION));
// Set the function codes.
irpSp.MajorFunction = IRP_MJ_PNP;
irpSp.MinorFunction = IRP_MN_QUERY_ID;
irpSp.Parameters.QueryId.IdType = BusQueryDeviceID;// Set the pointer to the resource list
// Make the call and return.
status = IopSynchronousCall(DeviceObject, &irpSp, DeviceId);
return status;
}
NTSTATUS IopQueryUniqueId(IN PDEVICE_OBJECT DeviceObject, OUT PWCHAR *UniqueId)
/*++
Routine Description:
This routine generates a unique id for the specified device object.
Parameters:
DeviceObject - Supplies the device object of the device being queried
UniqueId - Supplies a pointer to a variable to receive the returned Id.
This must be freed by the caller.
GloballyUnique - Indicates (from a previous call to query capabilities whether the id is globally unique.
Return Value:
NTSTATUS code.
*/
{
IO_STACK_LOCATION irpSp;
NTSTATUS status;
PAGED_CODE();
*UniqueId = NULL;
// First ask for for InstanceId.
// Initialize the stack location to pass to IopSynchronousCall()
RtlZeroMemory(&irpSp, sizeof(IO_STACK_LOCATION));
// Set the function codes.
irpSp.MajorFunction = IRP_MJ_PNP;
irpSp.MinorFunction = IRP_MN_QUERY_ID;
irpSp.Parameters.QueryId.IdType = BusQueryInstanceID;// Set the pointer to the resource list
status = IopSynchronousCall(DeviceObject, &irpSp, UniqueId);// Make the call and return.
if (!NT_SUCCESS(status)) {
*UniqueId = NULL;
}
return status;
}
NTSTATUS IopMakeGloballyUniqueId(IN PDEVICE_OBJECT DeviceObject, IN PWCHAR UniqueId, OUT PWCHAR *GloballyUniqueId)
{
NTSTATUS status;
ULONG length;
PWSTR id, Prefix = NULL;
HANDLE enumKey;
HANDLE instanceKey;
UCHAR keyBuffer[FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data) + sizeof(ULONG)];
PKEY_VALUE_PARTIAL_INFORMATION keyValue, stringValueBuffer = NULL;
UNICODE_STRING valueName;
ULONG uniqueIdValue, Hash, hashInstance;
PDEVICE_NODE parentNode;
PAGED_CODE();
// We need to build an instance id to uniquely identify this device.
// We will accomplish this by producing a prefix that will be prepended to the non-unique device id supplied.
// To 'unique-ify' the child's instance ID, we will retrieve the unique "UniqueParentID" number that has been assigned to the parent and use it to construct a prefix.
// This is the legacy mechanism supported here so that existing device settings are not lost on upgrade.
KeEnterCriticalRegion();
ExAcquireResourceShared(&PpRegistryDeviceResource, TRUE);
parentNode = ((PDEVICE_NODE)DeviceObject->DeviceObjectExtension->DeviceNode)->Parent;
status = IopOpenRegistryKeyEx(&enumKey, NULL, &CmRegistryMachineSystemCurrentControlSetEnumName, KEY_READ | KEY_WRITE);
if (!NT_SUCCESS(status)) {
DbgPrint("IopQueryUniqueId:\tUnable to open HKLM\\SYSTEM\\CCS\\ENUM (status %08lx)\n", status);
goto clean0;
}
// Open the instance key for this devnode
status = IopOpenRegistryKeyEx(&instanceKey, enumKey, &parentNode->InstancePath, KEY_READ | KEY_WRITE);
if (!NT_SUCCESS(status)) {
DbgPrint("IopQueryUniqueId:\tUnable to open registry key for %wZ (status %08lx)\n", &parentNode->InstancePath, status);
goto clean1;
}
// Attempt to retrieve the "UniqueParentID" value from the device instance key.
keyValue = (PKEY_VALUE_PARTIAL_INFORMATION)keyBuffer;
PiWstrToUnicodeString(&valueName, REGSTR_VALUE_UNIQUE_PARENT_ID);
status = ZwQueryValueKey(instanceKey, &valueName, KeyValuePartialInformation, keyValue, sizeof(keyBuffer), &length);
if (NT_SUCCESS(status)) {
ASSERT(keyValue->Type == REG_DWORD);
ASSERT(keyValue->DataLength == sizeof(ULONG));
if ((keyValue->Type != REG_DWORD) ||
(keyValue->DataLength != sizeof(ULONG))) {
status = STATUS_INVALID_PARAMETER;
goto clean2;
}
uniqueIdValue = *(PULONG)(keyValue->Data);
// OK, we have a unique parent ID number to prefix to the instance ID.
Prefix = (PWSTR)ExAllocatePool(PagedPool, 9 * sizeof(WCHAR));
if (!Prefix) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto clean2;
}
swprintf(Prefix, L"%x", uniqueIdValue);
} else {
// This is the current mechanism for finding existing device instance prefixes and calculating new ones if required.
// Attempt to retrieve the "ParentIdPrefix" value from the device instance key.
PiWstrToUnicodeString(&valueName, REGSTR_VALUE_PARENT_ID_PREFIX);
length = (MAX_PARENT_PREFIX + 1) * sizeof(WCHAR) + FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data);
stringValueBuffer = ExAllocatePool(PagedPool, length);
if (stringValueBuffer) {
status = ZwQueryValueKey(instanceKey, &valueName, KeyValuePartialInformation, stringValueBuffer, length, &length);
} else {
status = STATUS_INSUFFICIENT_RESOURCES;
goto clean2;
}
if (NT_SUCCESS(status)) {
ASSERT(stringValueBuffer->Type == REG_SZ);
if (stringValueBuffer->Type != REG_SZ) {
status = STATUS_INVALID_PARAMETER;
goto clean2;
}
// Parent has already been assigned a "ParentIdPrefix".
Prefix = (PWSTR)ExAllocatePool(PagedPool, stringValueBuffer->DataLength);
if (!Prefix) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto clean2;
}
wcscpy(Prefix, (PWSTR)stringValueBuffer->Data);
} else {
// Parent has not been assigned a "ParentIdPrefix".
// Compute the prefix:
// * Compute Hash
// * Look for value of the form:
// NextParentId.<level>.<hash>:REG_DWORD: <NextInstance>
// under CCS\Enum. If not present, create it.
// * Assign the new "ParentIdPrefix" which will be of
// of the form:
// <level>&<hash>&<instance>
// Allocate a buffer once for the NextParentId... value and for the prefix.
length = max(wcslen(REGSTR_VALUE_NEXT_PARENT_ID) + 2 + 8 + 8, MAX_PARENT_PREFIX) + 1;
// Device instances are case in-sensitive. Upcase before performing hash to ensure that the hash is case-insensitve.
status = RtlUpcaseUnicodeString(&valueName, &parentNode->InstancePath, TRUE);
if (!NT_SUCCESS(status)) {
goto clean2;
}
HASH_UNICODE_STRING(&valueName, &Hash);
RtlFreeUnicodeString(&valueName);
Prefix = (PWSTR)ExAllocatePool(PagedPool, length * sizeof(WCHAR));
if (!Prefix) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto clean2;
}
// Check for existence of "NextParentId...." value and update.
swprintf(Prefix, L"%s.%x.%x", REGSTR_VALUE_NEXT_PARENT_ID, Hash, parentNode->Level);
RtlInitUnicodeString(&valueName, Prefix);
keyValue = (PKEY_VALUE_PARTIAL_INFORMATION)keyBuffer;
status = ZwQueryValueKey(enumKey, &valueName, KeyValuePartialInformation, keyValue, sizeof(keyBuffer), &length);
if (NT_SUCCESS(status) && (keyValue->Type == REG_DWORD) && (keyValue->DataLength == sizeof(ULONG))) {
hashInstance = *(PULONG)(keyValue->Data);
} else {
hashInstance = 0;
}
hashInstance++;
status = ZwSetValueKey(enumKey, &valueName, TITLE_INDEX_VALUE, REG_DWORD, &hashInstance, sizeof(hashInstance));
if (!NT_SUCCESS(status)) {
goto clean2;
}
hashInstance--;
// Create actual ParentIdPrefix string
PiWstrToUnicodeString(&valueName, REGSTR_VALUE_PARENT_ID_PREFIX);
length = swprintf(Prefix, L"%x&%x&%x", parentNode->Level, Hash, hashInstance) + 1;
status = ZwSetValueKey(instanceKey, &valueName, TITLE_INDEX_VALUE, REG_SZ, Prefix, length * sizeof(WCHAR));
if (!NT_SUCCESS(status)) {
goto clean2;
}
}
}
// Construct the instance id from the non-unique id (if any) provided by the child and the prefix we've constructed.
length = wcslen(Prefix) + (UniqueId ? wcslen(UniqueId) : 0) + 2;
id = (PWSTR)ExAllocatePool(PagedPool, length * sizeof(WCHAR));
if (!id) {
status = STATUS_INSUFFICIENT_RESOURCES;
} else if (UniqueId) {
swprintf(id, L"%s&%s", Prefix, UniqueId);
} else {
wcscpy(id, Prefix);
}
clean2:
ZwClose(instanceKey);
clean1:
ZwClose(enumKey);
clean0:
ExReleaseResource(&PpRegistryDeviceResource);
KeLeaveCriticalRegion();
if (stringValueBuffer) {
ExFreePool(stringValueBuffer);
}
if (Prefix) {
ExFreePool(Prefix);
}
*GloballyUniqueId = id;
return status;
}
NTSTATUS IopQueryCompatibleIds(IN PDEVICE_OBJECT DeviceObject,
IN BUS_QUERY_ID_TYPE IdType,
OUT PWCHAR *CompatibleIds,
OUT ULONG *Length)
/*++
Routine Description:
This routine sends irp to query HardwareIds or CompatibleIds. This rotine queries MULTISZ Ids.
Parameters:
DeviceObject - Supplies the device object of the device being queried/
IdType - Specifies the Id type interested. Only HardwareIDs and CompatibleIDs are supported by this routine.
CompatibleId - Supplies a pointer to a variable to receive the returned Ids.
This must be freed by the caller.
Length - Supplies a pointer to a variable to receive the length of the IDs.
Return Value:
NTSTATUS code.
*/
{
IO_STACK_LOCATION irpSp;
NTSTATUS status;
PAGED_CODE();
*Length = 0;
if ((IdType != BusQueryHardwareIDs) && (IdType != BusQueryCompatibleIDs)) {
return STATUS_INVALID_PARAMETER_2;
}
// Initialize the stack location to pass to IopSynchronousCall()
RtlZeroMemory(&irpSp, sizeof(IO_STACK_LOCATION));
// Set the function codes.
irpSp.MajorFunction = IRP_MJ_PNP;
irpSp.MinorFunction = IRP_MN_QUERY_ID;
irpSp.Parameters.QueryId.IdType = IdType;// Set the pointer to the resource list
// Make the call and return.
status = IopSynchronousCall(DeviceObject, &irpSp, CompatibleIds);
if (NT_SUCCESS(status) && *CompatibleIds) {
// The Compatible IDs and Hardware IDs are multi_sz, try to determine its size.
PWCHAR wp;
for (wp = *CompatibleIds; (*wp != UNICODE_NULL) || (*(wp + 1) != UNICODE_NULL); wp++) {
*Length += 2;
}
*Length += 4;
}
return status;
}
NTSTATUS IopQueryDeviceResources(IN PDEVICE_OBJECT DeviceObject, IN ULONG ResourceType, OUT PVOID *Resource, OUT ULONG *Length)
/*++
Routine Description:
This routine sends irp to queries resources or resource requirements list of the specified device object.
If the device object is a detected device, its resources will be read from registry.
Otherwise, an irp is sent to the bus driver to query its resources.
Parameters:
DeviceObject - Supplies the device object of the device being queries.
ResourceType - 0 for device resources and 1 for resource requirements list.
Resource - Supplies a pointer to a variable to receive the returned resources
Length - Supplies a pointer to a variable to receive the length of the returned resources or resource requirements list.
Return Value:
NTSTATUS code.
*/
{
IO_STACK_LOCATION irpSp;
PDEVICE_NODE deviceNode;
NTSTATUS status;
PIO_RESOURCE_REQUIREMENTS_LIST resReqList, newResources;
ULONG junk;
PCM_RESOURCE_LIST cmList;
PIO_RESOURCE_REQUIREMENTS_LIST filteredList, mergedList;
BOOLEAN exactMatch;
PAGED_CODE();
#if DBG
if ((ResourceType != QUERY_RESOURCE_LIST) && (ResourceType != QUERY_RESOURCE_REQUIREMENTS)) {
return STATUS_INVALID_PARAMETER_2;
}
#endif
*Resource = NULL;
*Length = 0;
// Initialize the stack location to pass to IopSynchronousCall()
RtlZeroMemory(&irpSp, sizeof(IO_STACK_LOCATION));
deviceNode = (PDEVICE_NODE)DeviceObject->DeviceObjectExtension->DeviceNode;
if (ResourceType == QUERY_RESOURCE_LIST) {
// caller is asked for RESOURCE_LIST. If this is a madeup device, we will read it from registry. Otherwise, we ask drivers.
if (deviceNode->Flags & DNF_MADEUP) {
status = IopGetDeviceResourcesFromRegistry(
DeviceObject,
ResourceType,
REGISTRY_ALLOC_CONFIG + REGISTRY_FORCED_CONFIG + REGISTRY_BOOT_CONFIG,
Resource,
Length);
if (status == STATUS_OBJECT_NAME_NOT_FOUND) {
status = STATUS_SUCCESS;
}
return status;
} else {
irpSp.MinorFunction = IRP_MN_QUERY_RESOURCES;
irpSp.MajorFunction = IRP_MJ_PNP;
status = IopSynchronousCall(DeviceObject, &irpSp, Resource);
if (status == STATUS_NOT_SUPPORTED) {
// If driver doesn't implement this request, it doesn't consume any resources.
*Resource = NULL;
status = STATUS_SUCCESS;
}
if (NT_SUCCESS(status)) {
*Length = IopDetermineResourceListSize((PCM_RESOURCE_LIST)*Resource);
}
return status;
}
} else {
// Caller is asked for resource requirements list. We will check:
// if there is a ForcedConfig, it will be converted to resource requirements list and return. Otherwise,
// If there is an OVerrideConfigVector, we will use it as our
// FilterConfigVector. Otherwise we ask driver for the config vector and use it as our FilterConfigVector.
// Finaly, we pass the FilterConfigVector to driver stack to let drivers filter the requirements.
status = IopGetDeviceResourcesFromRegistry(DeviceObject, QUERY_RESOURCE_LIST, REGISTRY_FORCED_CONFIG, Resource, &junk);
if (status == STATUS_OBJECT_NAME_NOT_FOUND) {
status = IopGetDeviceResourcesFromRegistry(DeviceObject, QUERY_RESOURCE_REQUIREMENTS, REGISTRY_OVERRIDE_CONFIGVECTOR, &resReqList, &junk);
if (status == STATUS_OBJECT_NAME_NOT_FOUND) {
if (deviceNode->Flags & DNF_MADEUP) {
status = IopGetDeviceResourcesFromRegistry(DeviceObject, QUERY_RESOURCE_REQUIREMENTS, REGISTRY_BASIC_CONFIGVECTOR, &resReqList, &junk);
if (status == STATUS_OBJECT_NAME_NOT_FOUND) {
status = STATUS_SUCCESS;
resReqList = NULL;
}
} else {
// We are going to ask the bus driver ...
if (deviceNode->ResourceRequirements) {
ASSERT(deviceNode->Flags & DNF_RESOURCE_REQUIREMENTS_NEED_FILTERED);
resReqList = ExAllocatePool(PagedPool, deviceNode->ResourceRequirements->ListSize);
if (resReqList) {
RtlMoveMemory(resReqList, deviceNode->ResourceRequirements, deviceNode->ResourceRequirements->ListSize);
status = STATUS_SUCCESS;
} else {
return STATUS_NO_MEMORY;
}
} else {
irpSp.MinorFunction = IRP_MN_QUERY_RESOURCE_REQUIREMENTS;
irpSp.MajorFunction = IRP_MJ_PNP;
status = IopSynchronousCall(DeviceObject, &irpSp, &resReqList);
if (status == STATUS_NOT_SUPPORTED) {
// If driver doesn't implement this request, it doesn't require any resources.
status = STATUS_SUCCESS;
resReqList = NULL;
}
}
}
if (!NT_SUCCESS(status)) {
return status;
}
}
// For devices with boot config, we need to filter the resource requirements list against boot config.
status = IopGetDeviceResourcesFromRegistry(DeviceObject, QUERY_RESOURCE_LIST, REGISTRY_BOOT_CONFIG, &cmList, &junk);
if (NT_SUCCESS(status) && (!cmList || cmList->Count == 0 || cmList->List[0].InterfaceType != PCIBus)) {
status = IopFilterResourceRequirementsList(resReqList, cmList, &filteredList, &exactMatch);
if (cmList) {
ExFreePool(cmList);
}
if (!NT_SUCCESS(status)) {
if (resReqList) {
ExFreePool(resReqList);
}
return status;
} else {
// For non-root-enumerated devices, we merge filtered config with basic config vectors to form a new res req list.
// For root-enumerated devices, we don't consider Basic config vector.
if (!(deviceNode->Flags & DNF_MADEUP) && (exactMatch == FALSE || resReqList->AlternativeLists > 1)) {
status = IopMergeFilteredResourceRequirementsList(filteredList, resReqList, &mergedList);
if (resReqList) {
ExFreePool(resReqList);
}
if (filteredList) {
ExFreePool(filteredList);
}
if (NT_SUCCESS(status)) {
resReqList = mergedList;
} else {
return status;
}
} else {
if (resReqList) {
ExFreePool(resReqList);
}
resReqList = filteredList;
}
}
}
} else {
ASSERT(NT_SUCCESS(status));
// We have Forced Config. Convert it to resource requirements and return it.
if (*Resource) {
resReqList = IopCmResourcesToIoResources(0, (PCM_RESOURCE_LIST)*Resource, LCPRI_FORCECONFIG);
ExFreePool(*Resource);
if (resReqList) {
*Resource = (PVOID)resReqList;
*Length = resReqList->ListSize;
} else {
*Resource = NULL;
*Length = 0;
status = STATUS_INSUFFICIENT_RESOURCES;
return status;
}
} else {
resReqList = NULL;
}
}
// If we are here, we have a resource requirements list for drivers to examine ...
// NOTE: Per Lonny's request, we let drivers filter ForcedConfig
status = IopFilterResourceRequirementsCall(DeviceObject, resReqList, &newResources);
if (NT_SUCCESS(status)) {
UNICODE_STRING unicodeName;
HANDLE handle, handlex;
#if DBG
if (newResources == NULL && resReqList) {
DbgPrint("PnpMgr: Non-NULL resource requirements list filtered to NULL\n");
}
#endif
if (newResources) {
*Length = newResources->ListSize;
ASSERT(*Length);
// Make our own copy of the allocation.
// We do this so that the verifier doesn't believe the driver has leaked memory if unloaded.
*Resource = (PVOID)ExAllocatePool(PagedPool, *Length);
if (*Resource == NULL) {
ExFreePool(newResources);
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlCopyMemory(*Resource, newResources, *Length);
ExFreePool(newResources);
} else {
*Length = 0;
*Resource = NULL;
}
// Write filtered res req to registry
status = IopDeviceObjectToDeviceInstance(DeviceObject, &handlex, KEY_ALL_ACCESS);
if (NT_SUCCESS(status)) {
PiWstrToUnicodeString(&unicodeName, REGSTR_KEY_CONTROL);
status = IopOpenRegistryKeyEx(&handle, handlex, &unicodeName, KEY_READ);
if (NT_SUCCESS(status)) {
PiWstrToUnicodeString(&unicodeName, REGSTR_VALUE_FILTERED_CONFIG_VECTOR);
ZwSetValueKey(handle, &unicodeName, TITLE_INDEX_VALUE, REG_RESOURCE_REQUIREMENTS_LIST, *Resource, *Length);
ZwClose(handle);
ZwClose(handlex);
}
}
} else {
// ADRIAO BUGBUG 05/26/1999 -
// Why do we not bubble up non-STATUS_NOT_SUPPORTED failure
// codes?
*Resource = resReqList;
if (resReqList) {
*Length = resReqList->ListSize;
} else {
*Length = 0;
}
}
return STATUS_SUCCESS;
}
}
NTSTATUS IopQueryResourceHandlerInterface(IN RESOURCE_HANDLER_TYPE HandlerType,
IN PDEVICE_OBJECT DeviceObject,
IN UCHAR ResourceType,
IN OUT PVOID *Interface)
/*++
Routine Description:
This routine queries the specified DeviceObject for the specified ResourceType resource translator.
Parameters:
HandlerType - specifies Arbiter or Translator
DeviceObject - Supplies a pointer to the Device object to be queried.
ResourceType - Specifies the desired type of translator.
Interface - supplies a variable to receive the desired interface.
Return Value:
Status code that indicates whether or not the function was successful.
*/
{
IO_STACK_LOCATION irpSp;
NTSTATUS status;
PVOID dummy;
PINTERFACE interface;
USHORT size;
GUID interfaceType;
PDEVICE_NODE deviceNode = (PDEVICE_NODE)DeviceObject->DeviceObjectExtension->DeviceNode;
PAGED_CODE();
// If this device object is created by pnp mgr for legacy resource allocation, skip it.
if ((deviceNode->DuplicatePDO == (PDEVICE_OBJECT)DeviceObject->DriverObject) ||
!(DeviceObject->Flags & DO_BUS_ENUMERATED_DEVICE)) {
return STATUS_NOT_SUPPORTED;
}
switch (HandlerType) {
case ResourceTranslator:
size = sizeof(TRANSLATOR_INTERFACE) + 4; // Pnptest
//size = sizeof(TRANSLATOR_INTERFACE);
interfaceType = GUID_TRANSLATOR_INTERFACE_STANDARD;
break;
case ResourceArbiter:
size = sizeof(ARBITER_INTERFACE);
interfaceType = GUID_ARBITER_INTERFACE_STANDARD;
break;
case ResourceLegacyDeviceDetection:
size = sizeof(LEGACY_DEVICE_DETECTION_INTERFACE);
interfaceType = GUID_LEGACY_DEVICE_DETECTION_STANDARD;
break;
default:
return STATUS_INVALID_PARAMETER;
}
interface = (PINTERFACE)ExAllocatePool(PagedPool, size);
if (interface == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlZeroMemory(interface, size);
interface->Size = size;
// Initialize the stack location to pass to IopSynchronousCall()
RtlZeroMemory(&irpSp, sizeof(IO_STACK_LOCATION));
// Set the function codes.
irpSp.MajorFunction = IRP_MJ_PNP;
irpSp.MinorFunction = IRP_MN_QUERY_INTERFACE;
// Set the pointer to the resource list
irpSp.Parameters.QueryInterface.InterfaceType = &interfaceType;
irpSp.Parameters.QueryInterface.Size = interface->Size;
irpSp.Parameters.QueryInterface.Version = interface->Version = 0;
irpSp.Parameters.QueryInterface.Interface = interface;
irpSp.Parameters.QueryInterface.InterfaceSpecificData = (PVOID)ResourceType;
// Make the call and return.
status = IopSynchronousCall(DeviceObject, &irpSp, &dummy);
if (NT_SUCCESS(status)) {
*Interface = interface;
} else {
ExFreePool(interface);
}
return status;
}
NTSTATUS IopQueryReconfiguration(IN UCHAR Request, IN PDEVICE_OBJECT DeviceObject)
/*++
Routine Description:
This routine queries the specified DeviceObject for the specified ResourceType resource translator.
Parameters:
HandlerType - specifies Arbiter or Translator
DeviceObject - Supplies a pointer to the Device object to be queried.
ResourceType - Specifies the desired type of translator.
Interface - supplies a variable to receive the desired interface.
Return Value:
Status code that indicates whether or not the function was successful.
*/
{
IO_STACK_LOCATION irpSp;
NTSTATUS status;
PVOID dummy;
PAGED_CODE();
ASSERT(Request == IRP_MN_QUERY_STOP_DEVICE || Request == IRP_MN_STOP_DEVICE || Request == IRP_MN_CANCEL_STOP_DEVICE);
// Initialize the stack location to pass to IopSynchronousCall()
RtlZeroMemory(&irpSp, sizeof(IO_STACK_LOCATION));
// Set the function codes.
irpSp.MajorFunction = IRP_MJ_PNP;
irpSp.MinorFunction = Request;
// Make the call and return.
status = IopSynchronousCall(DeviceObject, &irpSp, &dummy);
return status;
}
NTSTATUS
IopQueryLegacyBusInformation(
IN PDEVICE_OBJECT DeviceObject,
OUT LPGUID InterfaceGuid, OPTIONAL
OUT INTERFACE_TYPE *InterfaceType, OPTIONAL
OUT ULONG *BusNumber OPTIONAL
)
/*++
Routine Description:
This routine queries the specified DeviceObject for its legacy bus information.
Parameters:
DeviceObject - The device object to be queried.
InterfaceGuid = Supplies a pointer to receive the device's interface type GUID.
Interface = Supplies a pointer to receive the device's interface type.
BusNumber = Supplies a pointer to receive the device's bus number.
Return Value:
Returns NTSTATUS.
*/
{
IO_STACK_LOCATION irpSp;
NTSTATUS status;
PLEGACY_BUS_INFORMATION busInfo;
PAGED_CODE();
// Initialize the stack location to pass to IopSynchronousCall()
RtlZeroMemory(&irpSp, sizeof(IO_STACK_LOCATION));
// Set the function codes.
irpSp.MajorFunction = IRP_MJ_PNP;
irpSp.MinorFunction = IRP_MN_QUERY_LEGACY_BUS_INFORMATION;
// Make the call and return.
status = IopSynchronousCall(DeviceObject, &irpSp, &busInfo);
if (NT_SUCCESS(status)) {
if (busInfo == NULL) {
// The device driver LIED to us. Bad, bad, bad device driver.
PDEVICE_NODE deviceNode;
deviceNode = DeviceObject->DeviceObjectExtension->DeviceNode;
if (deviceNode && deviceNode->ServiceName.Buffer) {
DbgPrint("*** IopQueryLegacyBusInformation - Driver %wZ returned STATUS_SUCCESS\n", &deviceNode->ServiceName);
DbgPrint(" for IRP_MN_QUERY_LEGACY_BUS_INFORMATION, and a NULL POINTER.\n");
}
ASSERT(busInfo != NULL);
} else {
if (ARGUMENT_PRESENT(InterfaceGuid)) {
*InterfaceGuid = busInfo->BusTypeGuid;
}
if (ARGUMENT_PRESENT(InterfaceType)) {
*InterfaceType = busInfo->LegacyBusType;
}
if (ARGUMENT_PRESENT(BusNumber)) {
*BusNumber = busInfo->BusNumber;
}
ExFreePool(busInfo);
}
}
return status;
}
NTSTATUS IopQueryPnpBusInformation(IN PDEVICE_OBJECT DeviceObject,
OUT LPGUID InterfaceGuid, OPTIONAL
OUT INTERFACE_TYPE *InterfaceType, OPTIONAL
OUT ULONG *BusNumber OPTIONAL
)
/*++
Routine Description:
This routine queries the specified DeviceObject for the specified ResourceType resource translator.
Parameters:
HandlerType - specifies Arbiter or Translator
DeviceObject - Supplies a pointer to the Device object to be queried.
ResourceType - Specifies the desired type of translator.
Interface - supplies a variable to receive the desired interface.
Return Value:
Status code that indicates whether or not the function was successful.
*/
{
IO_STACK_LOCATION irpSp;
NTSTATUS status;
PPNP_BUS_INFORMATION busInfo;
PAGED_CODE();
// Initialize the stack location to pass to IopSynchronousCall()
RtlZeroMemory(&irpSp, sizeof(IO_STACK_LOCATION));
// Set the function codes.
irpSp.MajorFunction = IRP_MJ_PNP;
irpSp.MinorFunction = IRP_MN_QUERY_BUS_INFORMATION;
// Make the call and return.
status = IopSynchronousCall(DeviceObject, &irpSp, &busInfo);
if (NT_SUCCESS(status)) {
if (busInfo == NULL) {
// The device driver LIED to us. Bad, bad, bad device driver.
PDEVICE_NODE deviceNode;
deviceNode = DeviceObject->DeviceObjectExtension->DeviceNode;
if (deviceNode && deviceNode->ServiceName.Buffer) {
DbgPrint("*** IopQueryPnpBusInformation - Driver %wZ returned STATUS_SUCCESS\n", &deviceNode->ServiceName);
DbgPrint(" for IRP_MN_QUERY_BUS_INFORMATION, and a NULL POINTER.\n");
}
ASSERT(busInfo != NULL);
} else {
if (ARGUMENT_PRESENT(InterfaceGuid)) {
*InterfaceGuid = busInfo->BusTypeGuid;
}
if (ARGUMENT_PRESENT(InterfaceType)) {
*InterfaceType = busInfo->LegacyBusType;
}
if (ARGUMENT_PRESENT(BusNumber)) {
*BusNumber = busInfo->BusNumber;
}
ExFreePool(busInfo);
}
}
return status;
}
NTSTATUS IopQueryDeviceState(IN PDEVICE_OBJECT DeviceObject)
/*++
Routine Description:
This routine sends query device state irp to the specified device object.
Parameters:
DeviceObjet - Supplies the device object of the device being queried.
Return Value:
NTSTATUS code.
*/
{
IO_STACK_LOCATION irpSp;
PDEVICE_NODE deviceNode;
PNP_DEVICE_STATE deviceState;
PDEVICE_RELATIONS deviceRelations;
KEVENT userEvent;
ULONG eventResult;
NTSTATUS status;
PAGED_CODE();
// Initialize the stack location to pass to IopSynchronousCall()
RtlZeroMemory(&irpSp, sizeof(IO_STACK_LOCATION));
// Set the function codes.
irpSp.MajorFunction = IRP_MJ_PNP;
irpSp.MinorFunction = IRP_MN_QUERY_PNP_DEVICE_STATE;
status = IopSynchronousCall(DeviceObject, &irpSp, (PVOID *)&deviceState);// Make the call.
// Now perform the appropriate action based on the returned state
if (NT_SUCCESS(status)) {
deviceNode = DeviceObject->DeviceObjectExtension->DeviceNode;
if (deviceState != 0) {
// everything here can only be turned on (state set)
if (deviceState & PNP_DEVICE_DONT_DISPLAY_IN_UI) {
deviceNode->UserFlags |= DNUF_DONT_SHOW_IN_UI;
}
if (deviceState & PNP_DEVICE_NOT_DISABLEABLE) {
if ((deviceNode->UserFlags & DNUF_NOT_DISABLEABLE) == 0) {
deviceNode->UserFlags |= DNUF_NOT_DISABLEABLE;// this node itself is not disableable
IopIncDisableableDepends(deviceNode);// propagate up tree
}
}
if (deviceState & (PNP_DEVICE_DISABLED | PNP_DEVICE_REMOVED)) {
IopRequestDeviceRemoval(DeviceObject, (deviceState & PNP_DEVICE_DISABLED) ? CM_PROB_HARDWARE_DISABLED : CM_PROB_DEVICE_NOT_THERE);
} else if (deviceState & PNP_DEVICE_RESOURCE_REQUIREMENTS_CHANGED) {
if (deviceState & PNP_DEVICE_FAILED) {
IopResourceRequirementsChanged(DeviceObject, TRUE);
} else {
IopResourceRequirementsChanged(DeviceObject, FALSE);
}
} else if (deviceState & PNP_DEVICE_FAILED) {
deviceNode->Flags |= DNF_HAS_PRIVATE_PROBLEM;
IopRequestDeviceRemoval(DeviceObject, 0);
}
} else {
// handle things that can be turned off (state cleared)
if (deviceNode->UserFlags & DNUF_NOT_DISABLEABLE) {
// this node itself is now disableable
// check tree
IopDecDisableableDepends(deviceNode);
}
deviceNode->UserFlags &= ~(DNUF_DONT_SHOW_IN_UI | DNUF_NOT_DISABLEABLE);
}
}
return status;
}
VOID IopIncDisableableDepends(IN OUT PDEVICE_NODE DeviceNode)
/*++
Routine Description:
Increments the DisableableDepends field of this devicenode and potentially every parent device node up the tree
A parent devicenode is only incremented if the child in question is incremented from 0 to 1
Parameters:
DeviceNode - Supplies the device node where the depends is to be incremented
*/
{
while (DeviceNode != NULL) {
LONG newval;
newval = InterlockedIncrement(&DeviceNode->DisableableDepends);
if (newval != 1) {
// we were already non-disableable, so we don't have to bother parent
break;
}
DeviceNode = DeviceNode->Parent;
}
}
VOID IopDecDisableableDepends(IN OUT PDEVICE_NODE DeviceNode)
/*++
Routine Description:
Decrements the DisableableDepends field of this devicenode and potentially every parent device node up the tree
A parent devicenode is only decremented if the child in question is decremented from 1 to 0
Parameters:
DeviceNode - Supplies the device node where the depends is to be decremented
*/
{
while (DeviceNode != NULL) {
LONG newval;
newval = InterlockedDecrement(&DeviceNode->DisableableDepends);
if (newval != 0) {
// we are still non-disableable, so we don't have to bother parent
break;
}
DeviceNode = DeviceNode->Parent;
}
}
NTSTATUS IopQueryDeviceSerialNumber(IN PDEVICE_OBJECT DeviceObject, OUT PWCHAR *SerialNumber)
/*++
Routine Description:
This routine retrieves a hardware serial number for the specified device object.
If the routine fails, SerialNumber is guarenteed to be to NULL.
Parameters:
DeviceObject - Supplies the device object of the device being queried
SerialNumber - Supplies a pointer to a variable to receive the returned Id.
This must be freed by the caller.
Return Value:
NTSTATUS code.
*/
{
IO_STACK_LOCATION irpSp;
NTSTATUS status;
PAGED_CODE();
*SerialNumber = NULL;
// Initialize the stack location to pass to IopSynchronousCall()
RtlZeroMemory(&irpSp, sizeof(IO_STACK_LOCATION));
// Set the function codes.
irpSp.MajorFunction = IRP_MJ_PNP;
irpSp.MinorFunction = IRP_MN_QUERY_ID;
irpSp.Parameters.QueryId.IdType = BusQueryDeviceSerialNumber;
// Make the call and return.
status = IopSynchronousCall(DeviceObject, &irpSp, SerialNumber);
ASSERT(NT_SUCCESS(status) || (*SerialNumber == NULL));
if (!NT_SUCCESS(status)) {
*SerialNumber = NULL;
}
return status;
}
NTSTATUS IopFilterResourceRequirementsCall(IN PDEVICE_OBJECT DeviceObject,
IN PIO_RESOURCE_REQUIREMENTS_LIST ResReqList OPTIONAL,
OUT PVOID *Information)
/*++
Routine Description:
This function sends a synchronous filter resource requirements irp to the top level device object which roots on DeviceObject.
Parameters:
DeviceObject - Supplies the device object of the device being removed.
ResReqList - Supplies a pointer to the resource requirements requiring filtering.
Information - Supplies a pointer to a variable that receives the returned information of the irp.
Return Value:
NTSTATUS code.
*/
{
PIRP irp;
PIO_STACK_LOCATION irpSp;
IO_STATUS_BLOCK statusBlock;
KEVENT event;
NTSTATUS status;
PULONG_PTR returnInfo = (PULONG_PTR)Information;
PDEVICE_OBJECT deviceObject;
PAGED_CODE();
// Get a pointer to the topmost device object in the stack of devices, beginning with the deviceObject.
deviceObject = IoGetAttachedDevice(DeviceObject);
// Begin by allocating the IRP for this request. Do not charge quota to the current process for this IRP.
irp = IoAllocateIrp(deviceObject->StackSize, FALSE);
if (irp == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
SPECIALIRP_WATERMARK_IRP(irp, IRP_SYSTEM_RESTRICTED);
// Initialize it to success. This is a special hack for WDM (ie 9x)
// compatibility. The driver verifier is in on this one.
if (ResReqList) {
irp->IoStatus.Status = statusBlock.Status = STATUS_SUCCESS;
irp->IoStatus.Information = statusBlock.Information = (ULONG_PTR)ResReqList;
} else {
irp->IoStatus.Status = statusBlock.Status = STATUS_NOT_SUPPORTED;
}
// Set the pointer to the status block and initialized event.
KeInitializeEvent(&event, SynchronizationEvent, FALSE);
irp->UserIosb = &statusBlock;
irp->UserEvent = &event;
// Set the address of the current thread
irp->Tail.Overlay.Thread = PsGetCurrentThread();
// Queue this irp onto the current thread
IopQueueThreadIrp(irp);
// Get a pointer to the stack location of the first driver which will be invoked.
// This is where the function codes and parameters are set.
irpSp = IoGetNextIrpStackLocation(irp);
// Setup the stack location contents
irpSp->MinorFunction = IRP_MN_FILTER_RESOURCE_REQUIREMENTS;
irpSp->MajorFunction = IRP_MJ_PNP;
irpSp->Parameters.FilterResourceRequirements.IoResourceRequirementList = ResReqList;
// Call the driver
status = IoCallDriver(deviceObject, irp);
PnpIrpStatusTracking(status, IRP_MN_FILTER_RESOURCE_REQUIREMENTS, deviceObject);
// If a driver returns STATUS_PENDING, we will wait for it to complete
if (status == STATUS_PENDING) {
(VOID)KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, (PLARGE_INTEGER)NULL);
status = statusBlock.Status;
}
*returnInfo = (ULONG_PTR)statusBlock.Information;
return status;
}
NTSTATUS IopQueryDockRemovalInterface(IN PDEVICE_OBJECT DeviceObject, IN OUT PDOCK_INTERFACE *DockInterface)
/*++
Routine Description:
This routine queries the specified DeviceObject for the dock removal interface.
We use this interface to send pseudo-remove's.
We use this to solve the removal orderings problem.
Parameters:
DeviceObject - Supplies a pointer to the Device object to be queried.
Interface - supplies a variable to receive the desired interface.
Return Value:
Status code that indicates whether or not the function was successful.
*/
{
IO_STACK_LOCATION irpSp;
NTSTATUS status;
PVOID dummy;
PINTERFACE interface;
USHORT size;
GUID interfaceType;
PAGED_CODE();
size = sizeof(DOCK_INTERFACE);
interfaceType = GUID_DOCK_INTERFACE;
interface = (PINTERFACE)ExAllocatePool(PagedPool, size);
if (interface == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlZeroMemory(interface, size);
interface->Size = size;
// Initialize the stack location to pass to IopSynchronousCall()
RtlZeroMemory(&irpSp, sizeof(IO_STACK_LOCATION));
// Set the function codes.
irpSp.MajorFunction = IRP_MJ_PNP;
irpSp.MinorFunction = IRP_MN_QUERY_INTERFACE;
// Set the pointer to the resource list
irpSp.Parameters.QueryInterface.InterfaceType = &interfaceType;
irpSp.Parameters.QueryInterface.Size = interface->Size;
irpSp.Parameters.QueryInterface.Version = interface->Version = 0;
irpSp.Parameters.QueryInterface.Interface = interface;
irpSp.Parameters.QueryInterface.InterfaceSpecificData = NULL;
// Make the call and return.
status = IopSynchronousCall(DeviceObject, &irpSp, &dummy);
if (NT_SUCCESS(status)) {
*DockInterface = (PDOCK_INTERFACE)interface;
} else {
ExFreePool(interface);
}
return status;
}