You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2561 lines
70 KiB
2561 lines
70 KiB
/*++
|
|
|
|
Copyright (c) Microsoft Corporation. All rights reserved.
|
|
|
|
Module Name:
|
|
|
|
pnpirp.c
|
|
|
|
Abstract:
|
|
|
|
This module contains IRP related routines.
|
|
|
|
Author:
|
|
|
|
Shie-Lin Tzong (shielint) 13-Sept-1996
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "pnpmgrp.h"
|
|
#pragma hdrstop
|
|
|
|
#define FAULT_INJECT_INVALID_ID DBG
|
|
|
|
#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
|
|
|
|
//
|
|
// 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;
|
|
PDEVICE_OBJECT FsDevice;
|
|
} LOCK_MOUNTABLE_DEVICE_CONTEXT, *PLOCK_MOUNTABLE_DEVICE_CONTEXT;
|
|
|
|
//
|
|
// Internal references
|
|
//
|
|
|
|
NTSTATUS
|
|
IopDeviceEjectComplete (
|
|
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, IopSynchronousCall)
|
|
#pragma alloc_text(PAGE, IopStartDevice)
|
|
#pragma alloc_text(PAGE, IopEjectDevice)
|
|
#pragma alloc_text(PAGE, IopCancelPendingEject)
|
|
#pragma alloc_text(PAGE, IopRemoveDevice)
|
|
//#pragma alloc_text(PAGE, IopQueryDeviceRelations)
|
|
#pragma alloc_text(PAGE, IopQueryDeviceResources)
|
|
#pragma alloc_text(PAGE, IopQueryDockRemovalInterface)
|
|
#pragma alloc_text(PAGE, IopQueryLegacyBusInformation)
|
|
#pragma alloc_text(PAGE, IopQueryResourceHandlerInterface)
|
|
#pragma alloc_text(PAGE, IopQueryReconfiguration)
|
|
#pragma alloc_text(PAGE, IopFindMountableDevice)
|
|
#pragma alloc_text(PAGE, IopFilterResourceRequirementsCall)
|
|
#pragma alloc_text(PAGE, IopQueryDeviceState)
|
|
#pragma alloc_text(PAGE, IopIncDisableableDepends)
|
|
#pragma alloc_text(PAGE, IopDecDisableableDepends)
|
|
#pragma alloc_text(PAGE, PpIrpQueryDeviceText)
|
|
#pragma alloc_text(PAGE, PpIrpQueryID)
|
|
#pragma alloc_text(PAGE, PpIrpQueryResourceRequirements)
|
|
#pragma alloc_text(PAGE, PpIrpQueryCapabilities)
|
|
#pragma alloc_text(PAGE, PpIrpQueryBusInformation)
|
|
#endif // ALLOC_PRAGMA
|
|
|
|
NTSTATUS
|
|
IopSynchronousCall(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIO_STACK_LOCATION TopStackLocation,
|
|
OUT PULONG_PTR 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;
|
|
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;
|
|
|
|
//
|
|
// 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);
|
|
|
|
//
|
|
// Copy in the caller-supplied stack location contents
|
|
//
|
|
|
|
*irpSp = *TopStackLocation;
|
|
|
|
//
|
|
// Call the driver
|
|
//
|
|
|
|
status = IoCallDriver(deviceObject, irp);
|
|
|
|
PnpIrpStatusTracking(status, TopStackLocation->MinorFunction, 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;
|
|
}
|
|
|
|
if (Information != NULL) {
|
|
*Information = statusBlock.Information;
|
|
}
|
|
|
|
ASSERT(status != STATUS_PENDING);
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
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;
|
|
PDEVICE_NODE deviceNode = (PDEVICE_NODE)DeviceObject->DeviceObjectExtension->DeviceNode;
|
|
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_START_DEVICE;
|
|
|
|
//
|
|
// Set the pointers for the raw and translated resource lists
|
|
//
|
|
|
|
irpSp.Parameters.StartDevice.AllocatedResources = deviceNode->ResourceList;
|
|
irpSp.Parameters.StartDevice.AllocatedResourcesTranslated = deviceNode->ResourceListTranslated;
|
|
|
|
status = IopSynchronousCall(DeviceObject, &irpSp, NULL);
|
|
|
|
return status;
|
|
}
|
|
|
|
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_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 = IoGetAttachedDeviceReference(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 );
|
|
|
|
ObDereferenceObject(deviceObject);
|
|
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);
|
|
|
|
//
|
|
// 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_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;
|
|
PendingEntry->Lock = IRPLOCK_CANCELABLE;
|
|
|
|
IopQueuePendingEject(PendingEntry);
|
|
|
|
IoSetCompletionRoutine(irp,
|
|
IopDeviceEjectComplete,
|
|
PendingEntry, /* Completion context */
|
|
TRUE, /* Invoke on success */
|
|
TRUE, /* Invoke on error */
|
|
TRUE /* Invoke on cancel */
|
|
);
|
|
|
|
status = IoCallDriver( deviceObject, irp );
|
|
|
|
ObDereferenceObject(deviceObject);
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
IopDeviceEjectComplete (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
{
|
|
PPENDING_RELATIONS_LIST_ENTRY entry = (PPENDING_RELATIONS_LIST_ENTRY)Context;
|
|
IRPLOCK oldState;
|
|
|
|
UNREFERENCED_PARAMETER( DeviceObject );
|
|
|
|
ASSERT(entry->EjectIrp == Irp);
|
|
|
|
//
|
|
// Indicate the IRP has been completed. After this point, the IRP may be
|
|
// freed.
|
|
//
|
|
oldState = InterlockedExchange((PLONG) &entry->Lock, IRPLOCK_COMPLETED);
|
|
|
|
//
|
|
// 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 );
|
|
|
|
if (oldState != IRPLOCK_CANCEL_STARTED) {
|
|
|
|
//
|
|
// The oldstate was either IRPLOCK_CANCELABLE, or
|
|
// IRPLOCK_CANCEL_COMPLETE.
|
|
//
|
|
IoFreeIrp( Irp );
|
|
|
|
} else {
|
|
|
|
//
|
|
// The IRP is actively being cancelled. When the cancelling routine
|
|
// tries to change the state, it will find out it owns the IRP cleanup.
|
|
//
|
|
NOTHING;
|
|
}
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
|
|
}
|
|
|
|
VOID
|
|
IopCancelPendingEject(
|
|
IN PPENDING_RELATIONS_LIST_ENTRY Entry
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function safely cancels a pending eject. The caller must ensure
|
|
relation list containing the IRP lock is valid throughout the duration
|
|
of this call. The IRP is not gauranteed to have been completed by the
|
|
time this call returns.
|
|
|
|
Parameters:
|
|
|
|
Entry - Relation list containing the eject IRP to cancel.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
if (Entry->EjectIrp == NULL) {
|
|
|
|
return;
|
|
}
|
|
|
|
if (InterlockedExchange((PLONG) &Entry->Lock, IRPLOCK_CANCEL_STARTED) == IRPLOCK_CANCELABLE) {
|
|
|
|
//
|
|
// We got it to the IRP before it was completed. We can cancel
|
|
// the IRP without fear of losing it, as the completion routine
|
|
// won't let go of the IRP until we say so.
|
|
//
|
|
IoCancelIrp(Entry->EjectIrp);
|
|
|
|
//
|
|
// Release the completion routine. If it already got there,
|
|
// then we need to handle post-processing ourselves. Otherwise we got
|
|
// through IoCancelIrp before the IRP completed entirely.
|
|
//
|
|
if (InterlockedExchange((PLONG) &Entry->Lock, IRPLOCK_CANCEL_COMPLETE) == IRPLOCK_COMPLETED) {
|
|
|
|
//
|
|
// Free the IRP.
|
|
//
|
|
IoFreeIrp(Entry->EjectIrp);
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// The entry was completed, meaning the IRP is gone.
|
|
//
|
|
NOTHING;
|
|
}
|
|
}
|
|
|
|
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.
|
|
|
|
--*/
|
|
{
|
|
IO_STACK_LOCATION irpSp;
|
|
NTSTATUS status;
|
|
|
|
BOOLEAN isMountable = FALSE;
|
|
PDEVICE_OBJECT mountedDevice;
|
|
|
|
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;
|
|
|
|
//
|
|
// Check to see if there's a VPB anywhere in the device stack. If there
|
|
// is then we'll have to lock the stack. This is to make sure that the VPB
|
|
// does not 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.
|
|
//
|
|
|
|
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, NULL);
|
|
IopDbgPrint((IOP_INFO_LEVEL, "IopRemoveDevice: MinorCode = %d, Status = %08x\n", IrpMinorCode, status));
|
|
|
|
if (isMountable) {
|
|
|
|
IopUnlockMountedDeviceForRemove(TargetDevice,
|
|
IrpMinorCode,
|
|
&lockContext);
|
|
|
|
//
|
|
// Successful 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 ||
|
|
IrpMinorCode == IRP_MN_SURPRISE_REMOVAL) &&
|
|
NT_SUCCESS( status )) {
|
|
|
|
status = IopInvalidateVolumesForDevice( TargetDevice );
|
|
}
|
|
}
|
|
|
|
if (IrpMinorCode == IRP_MN_REMOVE_DEVICE) {
|
|
((PDEVICE_NODE)TargetDevice->DeviceObjectExtension->DeviceNode)->Flags &=
|
|
~(DNF_LEGACY_DRIVER | DNF_REENUMERATE);
|
|
}
|
|
|
|
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;
|
|
|
|
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;
|
|
}
|
|
|
|
Context->FsDevice = fsDevice;
|
|
|
|
IoReleaseVpbSpinLock(oldIrql);
|
|
|
|
//
|
|
// Bump the fs device handle count. This prevent the filesystem filter stack
|
|
// from being torn down while a PNP IRP is in progress.
|
|
//
|
|
|
|
if (fsDevice) {
|
|
IopIncrementDeviceObjectHandleCount(fsDevice);
|
|
}
|
|
|
|
KeSetEvent(&(device->DeviceLock), IO_NO_INCREMENT, FALSE);
|
|
|
|
//
|
|
// Stop if we hit a device with a mounted filesystem.
|
|
//
|
|
|
|
if (NULL != fsDevice) {
|
|
|
|
//
|
|
// We found and setup a mounted device. Time to return.
|
|
//
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
oldIrql = KeAcquireQueuedSpinLock( LockQueueIoDatabaseLock );
|
|
device = device->AttachedDevice;
|
|
KeReleaseQueuedSpinLock( LockQueueIoDatabaseLock, 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
|
|
)
|
|
{
|
|
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) {
|
|
|
|
//
|
|
// Decrement the fs device handle count. This prevented the filesystem filter stack
|
|
// from being torn down while a PNP IRP is in progress.
|
|
//
|
|
|
|
if (Context->FsDevice) {
|
|
IopDecrementDeviceObjectHandleCount(Context->FsDevice);
|
|
}
|
|
break;
|
|
|
|
} else {
|
|
|
|
oldIrql = KeAcquireQueuedSpinLock( LockQueueIoDatabaseLock );
|
|
device = device->AttachedDevice;
|
|
KeReleaseQueuedSpinLock( LockQueueIoDatabaseLock, oldIrql );
|
|
}
|
|
|
|
} while (device != NULL);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
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.
|
|
|
|
--*/
|
|
|
|
{
|
|
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
|
|
IopQueryDeviceRelations(
|
|
IN DEVICE_RELATION_TYPE Relations,
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN BOOLEAN Synchronous,
|
|
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_NODE deviceNode = (PDEVICE_NODE)DeviceObject->DeviceObjectExtension->DeviceNode;
|
|
|
|
UNREFERENCED_PARAMETER (Synchronous);
|
|
|
|
//
|
|
// 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.
|
|
//
|
|
status = IopSynchronousCall(DeviceObject, &irpSp, (PULONG_PTR)DeviceRelations);
|
|
|
|
if (Relations == BusRelations) {
|
|
|
|
deviceNode->CompletionStatus = status;
|
|
|
|
PipSetDevNodeState( deviceNode, DeviceNodeEnumerateCompletion, NULL );
|
|
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
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)) {
|
|
|
|
ASSERT(0);
|
|
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, (PULONG_PTR)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) {
|
|
RtlCopyMemory(resReqList,
|
|
deviceNode->ResourceRequirements,
|
|
deviceNode->ResourceRequirements->ListSize
|
|
);
|
|
status = STATUS_SUCCESS;
|
|
} else {
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
} else {
|
|
status = PpIrpQueryResourceRequirements(DeviceObject, &resReqList);
|
|
if (status == STATUS_NOT_SUPPORTED) {
|
|
|
|
ASSERT(resReqList == NULL);
|
|
resReqList = NULL;
|
|
//
|
|
// If driver doesn't implement this request, it
|
|
// doesn't require any resources.
|
|
//
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
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 {
|
|
|
|
//
|
|
// NTRAID #61058-2001/01/05 - ADRIAO
|
|
// We might want to consider bubbling up
|
|
// non-STATUS_NOT_SUPPORTED failure codes and fail the entire
|
|
// devnode if one is seen.
|
|
//
|
|
ASSERT(status == STATUS_NOT_SUPPORTED);
|
|
*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;
|
|
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) (ULONG_PTR) ResourceType;
|
|
|
|
//
|
|
// Make the call and return.
|
|
//
|
|
|
|
status = IopSynchronousCall(DeviceObject, &irpSp, NULL);
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
switch (HandlerType) {
|
|
|
|
case ResourceTranslator:
|
|
if ( ((PTRANSLATOR_INTERFACE)interface)->TranslateResources == NULL ||
|
|
((PTRANSLATOR_INTERFACE)interface)->TranslateResourceRequirements == NULL) {
|
|
|
|
IopDbgPrint((IOP_ERROR_LEVEL,
|
|
"!devstack %p returned success for IRP_MN_QUERY_INTERFACE (GUID_TRANSLATOR_INTERFACE_STANDARD) but did not fill in the required data\n",
|
|
DeviceObject));
|
|
ASSERT(!NT_SUCCESS(status));
|
|
status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
break;
|
|
|
|
case ResourceArbiter:
|
|
if (((PARBITER_INTERFACE)interface)->ArbiterHandler == NULL) {
|
|
|
|
IopDbgPrint((IOP_ERROR_LEVEL,
|
|
"!devstack %p returned success for IRP_MN_QUERY_INTERFACE (GUID_ARBITER_INTERFACE_STANDARD) but did not fill in the required data\n",
|
|
DeviceObject));
|
|
ASSERT(!NT_SUCCESS(status));
|
|
status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
break;
|
|
|
|
case ResourceLegacyDeviceDetection:
|
|
if (((PLEGACY_DEVICE_DETECTION_INTERFACE)interface)->LegacyDeviceDetection == NULL) {
|
|
|
|
IopDbgPrint((IOP_ERROR_LEVEL,
|
|
"!devstack %p returned success for IRP_MN_QUERY_INTERFACE (GUID_LEGACY_DEVICE_DETECTION_STANDARD) but did not fill in the required data\n",
|
|
DeviceObject));
|
|
ASSERT(!NT_SUCCESS(status));
|
|
status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
//
|
|
// This should never happen.
|
|
//
|
|
IopDbgPrint((IOP_ERROR_LEVEL,
|
|
"IopQueryResourceHandlerInterface: Possible stack corruption\n"));
|
|
ASSERT(0);
|
|
status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
}
|
|
|
|
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;
|
|
PDEVICE_NODE deviceNode = (PDEVICE_NODE)DeviceObject->DeviceObjectExtension->DeviceNode;
|
|
|
|
PAGED_CODE();
|
|
|
|
switch (Request) {
|
|
case IRP_MN_QUERY_STOP_DEVICE:
|
|
|
|
if (deviceNode->State != DeviceNodeStarted) {
|
|
|
|
IopDbgPrint(( IOP_RESOURCE_ERROR_LEVEL,
|
|
"An attempt made to send IRP_MN_QUERY_STOP_DEVICE to an unstarted device %wZ!\n",
|
|
&deviceNode->InstancePath));
|
|
ASSERT(0);
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
break;
|
|
|
|
case IRP_MN_STOP_DEVICE:
|
|
//
|
|
// Fall through
|
|
//
|
|
if (deviceNode->State != DeviceNodeQueryStopped) {
|
|
|
|
IopDbgPrint(( IOP_RESOURCE_ERROR_LEVEL,
|
|
"An attempt made to send IRP_MN_STOP_DEVICE to an unqueried device %wZ!\n",
|
|
&deviceNode->InstancePath));
|
|
ASSERT(0);
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
break;
|
|
|
|
case IRP_MN_CANCEL_STOP_DEVICE:
|
|
|
|
if ( deviceNode->State != DeviceNodeQueryStopped &&
|
|
deviceNode->State != DeviceNodeStarted) {
|
|
|
|
IopDbgPrint(( IOP_RESOURCE_ERROR_LEVEL,
|
|
"An attempt made to send IRP_MN_CANCEL_STOP_DEVICE to an unqueried\\unstarted device %wZ!\n",
|
|
&deviceNode->InstancePath));
|
|
ASSERT(0);
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
ASSERT(0);
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
//
|
|
// 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, NULL);
|
|
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, (PULONG_PTR)&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->Parent && deviceNode->Parent->ServiceName.Buffer) {
|
|
|
|
DbgPrint("*** IopQueryLegacyBusInformation - Driver %wZ returned STATUS_SUCCESS\n", &deviceNode->Parent->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
|
|
IopQueryDeviceState(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
OUT PPNP_DEVICE_STATE DeviceState
|
|
)
|
|
|
|
/*++
|
|
|
|
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;
|
|
ULONG_PTR stateValue;
|
|
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;
|
|
|
|
//
|
|
// Make the call.
|
|
//
|
|
|
|
status = IopSynchronousCall(DeviceObject, &irpSp, &stateValue);
|
|
|
|
//
|
|
// Now perform the appropriate action based on the returned state
|
|
//
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
*DeviceState = (PNP_DEVICE_STATE)stateValue;
|
|
}
|
|
|
|
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
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
{
|
|
|
|
while (DeviceNode != NULL) {
|
|
|
|
LONG newval;
|
|
|
|
newval = InterlockedIncrement((PLONG)&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
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
{
|
|
|
|
while (DeviceNode != NULL) {
|
|
|
|
LONG newval;
|
|
|
|
newval = InterlockedDecrement((PLONG)&DeviceNode->DisableableDepends);
|
|
if (newval != 0) {
|
|
//
|
|
// we are still non-disableable, so we don't have to bother parent
|
|
//
|
|
break;
|
|
}
|
|
|
|
DeviceNode = DeviceNode ->Parent;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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;
|
|
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, NULL);
|
|
if (NT_SUCCESS(status)) {
|
|
*DockInterface = (PDOCK_INTERFACE) interface;
|
|
} else {
|
|
ExFreePool(interface);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
PpIrpQueryDeviceText(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN DEVICE_TEXT_TYPE DeviceTextType,
|
|
IN LCID POINTER_ALIGNMENT LocaleId,
|
|
OUT PWCHAR *DeviceText
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will issue IRP_MN_QUERY_DEVICE_TEXT to the DeviceObject
|
|
to retrieve its specified device text. If this routine fails,
|
|
DeviceText will be set to NULL.
|
|
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - The device object the request should be sent to.
|
|
|
|
DeviceTextType - Text type to be queried.
|
|
|
|
LocaleId - LCID specifying the locale for the requested text.
|
|
|
|
DeviceText - Receives the device text returned by the driver if any.
|
|
Caller is expected to free the storage for DeviceText on success.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
--*/
|
|
|
|
{
|
|
IO_STACK_LOCATION irpSp;
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(DeviceTextType == DeviceTextDescription || DeviceTextType == DeviceTextLocationInformation);
|
|
|
|
*DeviceText = NULL;
|
|
|
|
RtlZeroMemory(&irpSp, sizeof(IO_STACK_LOCATION));
|
|
|
|
irpSp.MajorFunction = IRP_MJ_PNP;
|
|
irpSp.MinorFunction = IRP_MN_QUERY_DEVICE_TEXT;
|
|
|
|
irpSp.Parameters.QueryDeviceText.DeviceTextType = DeviceTextType;
|
|
irpSp.Parameters.QueryDeviceText.LocaleId = LocaleId;
|
|
|
|
status = IopSynchronousCall(DeviceObject, &irpSp, (PULONG_PTR)DeviceText);
|
|
|
|
ASSERT(NT_SUCCESS(status) || (*DeviceText == NULL));
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
if(*DeviceText == NULL) {
|
|
|
|
status = STATUS_NOT_SUPPORTED;
|
|
}
|
|
} else {
|
|
|
|
*DeviceText = NULL;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
PpIrpQueryResourceRequirements(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
OUT PIO_RESOURCE_REQUIREMENTS_LIST *Requirements
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will issue IRP_MN_QUERY_RESOURCE_REQUIREMENTS to the
|
|
DeviceObject to retrieve its resource requirements. If this routine
|
|
failes, Requirements will be set to NULL.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - The device object the request should be sent to.
|
|
|
|
Requirements - Receives the requirements returned by the driver if any.
|
|
The caller is expected to free the storage for Requirements on success.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
--*/
|
|
|
|
{
|
|
IO_STACK_LOCATION irpSp;
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
*Requirements = NULL;
|
|
|
|
RtlZeroMemory(&irpSp, sizeof(IO_STACK_LOCATION));
|
|
|
|
irpSp.MajorFunction = IRP_MJ_PNP;
|
|
irpSp.MinorFunction = IRP_MN_QUERY_RESOURCE_REQUIREMENTS;
|
|
|
|
status = IopSynchronousCall(DeviceObject, &irpSp, (PULONG_PTR)Requirements);
|
|
|
|
ASSERT(NT_SUCCESS(status) || (*Requirements == NULL));
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
if(*Requirements == NULL) {
|
|
|
|
status = STATUS_NOT_SUPPORTED;
|
|
}
|
|
} else {
|
|
|
|
*Requirements = NULL;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
#if FAULT_INJECT_INVALID_ID
|
|
//
|
|
// Fault injection for invalid IDs
|
|
//
|
|
ULONG PiFailQueryID = 0;
|
|
#endif
|
|
|
|
NTSTATUS
|
|
PpIrpQueryID(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN BUS_QUERY_ID_TYPE IDType,
|
|
OUT PWCHAR *ID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will issue IRP_MN_QUERY_ID to the DeviceObject
|
|
to retrieve the specified ID. If this routine fails, ID will
|
|
be set to NULL.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - The device object the request should be sent to.
|
|
|
|
IDType - Type of ID to be queried.
|
|
|
|
ID - Receives the ID returned by the driver if any. The caller
|
|
is expected to free the storage for ID on success.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
--*/
|
|
|
|
{
|
|
IO_STACK_LOCATION irpSp;
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(IDType == BusQueryDeviceID || IDType == BusQueryInstanceID ||
|
|
IDType == BusQueryHardwareIDs || IDType == BusQueryCompatibleIDs ||
|
|
IDType == BusQueryDeviceSerialNumber);
|
|
|
|
*ID = NULL;
|
|
|
|
RtlZeroMemory(&irpSp, sizeof(IO_STACK_LOCATION));
|
|
|
|
irpSp.MajorFunction = IRP_MJ_PNP;
|
|
irpSp.MinorFunction = IRP_MN_QUERY_ID;
|
|
|
|
irpSp.Parameters.QueryId.IdType = IDType;
|
|
|
|
status = IopSynchronousCall(DeviceObject, &irpSp, (PULONG_PTR)ID);
|
|
|
|
ASSERT(NT_SUCCESS(status) || (*ID == NULL));
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
if(*ID == NULL) {
|
|
|
|
status = STATUS_NOT_SUPPORTED;
|
|
}
|
|
} else {
|
|
|
|
*ID = NULL;
|
|
}
|
|
|
|
#if FAULT_INJECT_INVALID_ID
|
|
//
|
|
// Fault injection for invalid IDs
|
|
//
|
|
if (*ID){
|
|
|
|
static LARGE_INTEGER seed = {0};
|
|
|
|
if(seed.LowPart == 0) {
|
|
|
|
KeQuerySystemTime(&seed);
|
|
}
|
|
|
|
if(PnPBootDriversInitialized && PiFailQueryID && RtlRandom(&seed.LowPart) % 10 > 7) {
|
|
|
|
**ID = L',';
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
PpIrpQueryCapabilities(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
OUT PDEVICE_CAPABILITIES Capabilities
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will issue IRP_MN_QUERY_CAPABILITIES to the DeviceObject
|
|
to retrieve the pnp device capabilities.
|
|
Should only be called twice - first from PipProcessNewDeviceNode,
|
|
and second from IopQueryAndSaveDeviceNodeCapabilities, called after
|
|
device is started.
|
|
If you consider calling this, see if DeviceNode->CapabilityFlags does
|
|
what you need instead (accessed via IopDeviceNodeFlagsToCapabilities(...).
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - The device object the request should be sent to.
|
|
|
|
Capabilities - A capabilities structure to be filled in by the driver.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
--*/
|
|
|
|
{
|
|
IO_STACK_LOCATION irpStack;
|
|
|
|
PAGED_CODE();
|
|
|
|
RtlZeroMemory(Capabilities, sizeof(DEVICE_CAPABILITIES));
|
|
Capabilities->Size = sizeof(DEVICE_CAPABILITIES);
|
|
Capabilities->Version = 1;
|
|
Capabilities->Address = Capabilities->UINumber = (ULONG)-1;
|
|
|
|
RtlZeroMemory(&irpStack, sizeof(IO_STACK_LOCATION));
|
|
|
|
irpStack.MajorFunction = IRP_MJ_PNP;
|
|
irpStack.MinorFunction = IRP_MN_QUERY_CAPABILITIES;
|
|
|
|
irpStack.Parameters.DeviceCapabilities.Capabilities = Capabilities;
|
|
|
|
return IopSynchronousCall(DeviceObject, &irpStack, NULL);
|
|
}
|
|
|
|
NTSTATUS
|
|
PpIrpQueryBusInformation(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
OUT PPNP_BUS_INFORMATION *BusInfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine queries bus information. If this routine fails, BusInfo
|
|
will be set to NULL.
|
|
|
|
Parameters:
|
|
|
|
DeviceObject - Pointer to the Device object to be queried.
|
|
|
|
BusInfo - Receives the bus information returned by the driver if any.
|
|
The caller is expected to free the storage for BusInfo on success.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
--*/
|
|
{
|
|
IO_STACK_LOCATION irpSp;
|
|
NTSTATUS status;
|
|
PDEVICE_NODE deviceNode;
|
|
|
|
PAGED_CODE();
|
|
|
|
*BusInfo = NULL;
|
|
|
|
RtlZeroMemory(&irpSp, sizeof(IO_STACK_LOCATION));
|
|
|
|
irpSp.MajorFunction = IRP_MJ_PNP;
|
|
irpSp.MinorFunction = IRP_MN_QUERY_BUS_INFORMATION;
|
|
|
|
status = IopSynchronousCall(DeviceObject, &irpSp, (PULONG_PTR)BusInfo);
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
if (BusInfo == NULL) {
|
|
//
|
|
// The device driver LIED to us. Bad, bad, bad device driver.
|
|
//
|
|
deviceNode = DeviceObject->DeviceObjectExtension->DeviceNode;
|
|
if (deviceNode && deviceNode->Parent && deviceNode->Parent->ServiceName.Buffer) {
|
|
|
|
DbgPrint("*** IopQueryPnpBusInformation - Driver %wZ returned STATUS_SUCCESS\n", &deviceNode->Parent->ServiceName);
|
|
DbgPrint(" for IRP_MN_QUERY_BUS_INFORMATION, and a NULL POINTER.\n");
|
|
}
|
|
|
|
ASSERT(BusInfo != NULL);
|
|
status = STATUS_NOT_SUPPORTED;
|
|
}
|
|
} else {
|
|
|
|
*BusInfo = NULL;
|
|
}
|
|
|
|
return status;
|
|
}
|