1805 lines
54 KiB
C
1805 lines
54 KiB
C
/*++
|
|
Copyright (c) 1996 Microsoft Corporation
|
|
|
|
Module Name:
|
|
enum.c
|
|
|
|
Abstract:
|
|
This module contains routines to perform device removal
|
|
|
|
Author:
|
|
Robert B. Nelson (RobertN) Jun 1, 1998.
|
|
--*/
|
|
|
|
#include "iop.h"
|
|
#include "wdmguid.h"
|
|
|
|
#ifdef POOL_TAGGING
|
|
#undef ExAllocatePool
|
|
#define ExAllocatePool(a,b) ExAllocatePoolWithTag(a,b,'edpP')
|
|
#endif
|
|
|
|
|
|
// Kernel mode PNP specific routines.
|
|
|
|
|
|
NTSTATUS
|
|
IopCancelPendingEject(
|
|
IN PPENDING_RELATIONS_LIST_ENTRY EjectEntry
|
|
);
|
|
|
|
VOID
|
|
IopDelayedRemoveWorker(
|
|
IN PVOID Context
|
|
);
|
|
|
|
BOOLEAN
|
|
IopDeleteLockedDeviceNode(
|
|
IN PDEVICE_NODE DeviceNode,
|
|
IN ULONG IrpCode,
|
|
IN PRELATION_LIST RelationsList,
|
|
IN BOOLEAN IsKernelInitiated,
|
|
IN ULONG Problem
|
|
);
|
|
|
|
NTSTATUS
|
|
IopProcessRelation(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PLUGPLAY_DEVICE_DELETE_TYPE OperationCode,
|
|
IN PRELATION_LIST RelationsList,
|
|
IN BOOLEAN IsKernelInitiated,
|
|
IN BOOLEAN IsDirectDescendant
|
|
);
|
|
|
|
NTSTATUS
|
|
IopUnloadAttachedDriver(
|
|
IN PDRIVER_OBJECT DriverObject
|
|
);
|
|
|
|
WORK_QUEUE_ITEM IopDeviceRemovalWorkItem;
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, IopChainDereferenceComplete)
|
|
#pragma alloc_text(PAGE, IopDelayedRemoveWorker)
|
|
#pragma alloc_text(PAGE, IopDeleteLockedDeviceNode)
|
|
#pragma alloc_text(PAGE, IopDeleteLockedDeviceNodes)
|
|
#pragma alloc_text(PAGE, IopLockDeviceRemovalRelations)
|
|
#pragma alloc_text(PAGE, IopProcessCompletedEject)
|
|
#pragma alloc_text(PAGE, IopProcessRelation)
|
|
#pragma alloc_text(PAGE, IopQueuePendingEject)
|
|
#pragma alloc_text(PAGE, IopQueuePendingSurpriseRemoval)
|
|
#pragma alloc_text(PAGE, IopRequestDeviceRemoval)
|
|
#pragma alloc_text(PAGE, IopUnloadAttachedDriver)
|
|
#pragma alloc_text(PAGE, IopUnlockDeviceRemovalRelations)
|
|
#endif
|
|
|
|
VOID
|
|
IopChainDereferenceComplete(
|
|
IN PDEVICE_OBJECT PhysicalDeviceObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is invoked when the reference count on a PDO and all its
|
|
attached devices transitions to a zero. It tags the devnode as ready for
|
|
removal. If all the devnodes are tagged then IopDelayedRemoveWorker is
|
|
called to actually send the remove IRPs.
|
|
|
|
Arguments:
|
|
|
|
PhysicalDeviceObject - Supplies a pointer to the PDO whose references just
|
|
went to zero.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PPENDING_RELATIONS_LIST_ENTRY entry;
|
|
PLIST_ENTRY link;
|
|
ULONG count;
|
|
ULONG taggedCount;
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
// Lock the whole device node tree so no one can touch the tree.
|
|
|
|
|
|
IopAcquireDeviceTreeLock();
|
|
|
|
|
|
// Find the relation list this devnode is a member of.
|
|
|
|
for (link = IopPendingSurpriseRemovals.Flink;
|
|
link != &IopPendingSurpriseRemovals;
|
|
link = link->Flink) {
|
|
|
|
entry = CONTAINING_RECORD(link, PENDING_RELATIONS_LIST_ENTRY, Link);
|
|
|
|
|
|
// Tag the devnode as ready for remove. If it isn't in this list
|
|
|
|
status = IopSetRelationsTag( entry->RelationsList, PhysicalDeviceObject, TRUE );
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
taggedCount = IopGetRelationsTaggedCount( entry->RelationsList );
|
|
count = IopGetRelationsCount( entry->RelationsList );
|
|
|
|
if (taggedCount == count) {
|
|
|
|
// Remove relations list from list of pending surprise removals.
|
|
|
|
RemoveEntryList( link );
|
|
|
|
IopReleaseDeviceTreeLock();
|
|
|
|
if (PsGetCurrentProcess() != PsInitialSystemProcess) {
|
|
|
|
|
|
// Queue a work item to do the removal so we call the driver
|
|
// in the system process context rather than the random one
|
|
// we're in now.
|
|
|
|
|
|
ExInitializeWorkItem( &entry->WorkItem,
|
|
IopDelayedRemoveWorker,
|
|
entry);
|
|
|
|
ExQueueWorkItem(&entry->WorkItem, DelayedWorkQueue);
|
|
|
|
} else {
|
|
|
|
|
|
// We are already in the system process, so call the
|
|
// worker inline.
|
|
|
|
|
|
IopDelayedRemoveWorker( entry );
|
|
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
IopReleaseDeviceTreeLock();
|
|
|
|
ASSERT(link != &IopPendingSurpriseRemovals);
|
|
}
|
|
|
|
VOID
|
|
IopDelayedRemoveWorker(
|
|
IN PVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is usually called from a worker thread to actually send the
|
|
remove IRPs once the reference count on a PDO and all its attached devices
|
|
transitions to a zero.
|
|
|
|
Arguments:
|
|
|
|
Context - Supplies a pointer to the pending relations list entry which has
|
|
the relations list of PDOs we need to remove.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PPENDING_RELATIONS_LIST_ENTRY entry = (PPENDING_RELATIONS_LIST_ENTRY)Context;
|
|
|
|
PAGED_CODE();
|
|
|
|
IopSetAllRelationsTags( entry->RelationsList, FALSE );
|
|
|
|
IopDeleteLockedDeviceNodes( entry->DeviceObject,
|
|
entry->RelationsList,
|
|
RemoveDevice, // OperationCode
|
|
TRUE, // IsKernelInitiated
|
|
FALSE, // ProcessIndirectDescendants
|
|
entry->Problem, // Problem
|
|
NULL); // VetoingDevice
|
|
|
|
IopFreeRelationList( entry->RelationsList );
|
|
|
|
ExFreePool( entry );
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
IopDeleteLockedDeviceNode(
|
|
IN PDEVICE_NODE DeviceNode,
|
|
IN ULONG IrpCode,
|
|
IN PRELATION_LIST RelationsList,
|
|
IN BOOLEAN IsKernelInitiated,
|
|
IN ULONG Problem
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function assumes that the specified device is a bus and will
|
|
recursively remove all its children.
|
|
|
|
Arguments:
|
|
|
|
DeviceNode - Supplies a pointer to the device node to be removed.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS code.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_OBJECT deviceObject = DeviceNode->PhysicalDeviceObject;
|
|
PDEVICE_OBJECT *attachedDevices, device1, *device2;
|
|
PDRIVER_OBJECT *attachedDrivers, *driver;
|
|
ULONG length = 0;
|
|
BOOLEAN success = TRUE;
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
PIDBGMSG( PIDBG_REMOVAL,
|
|
("IopDeleteLockedDeviceNode: Entered\n DeviceNode = 0x%p\n IrpCode = 0x%08X\n RelationsList = 0x%p\n IsKernelInitiated = %d\n Problem = %d\n",
|
|
DeviceNode,
|
|
IrpCode,
|
|
RelationsList,
|
|
IsKernelInitiated,
|
|
Problem));
|
|
|
|
|
|
// If this device has been deleted, simply return.
|
|
|
|
|
|
if (!IsKernelInitiated && !(DeviceNode->Flags & DNF_ADDED)) {
|
|
PIDBGMSG(PIDBG_REMOVAL, ("IopDeleteLockedDeviceNode: Device already deleted\n"));
|
|
if (IrpCode == IRP_MN_REMOVE_DEVICE) {
|
|
while (deviceObject) {
|
|
deviceObject->DeviceObjectExtension->ExtensionFlags &= ~(DOE_REMOVE_PENDING | DOE_REMOVE_PROCESSED);
|
|
deviceObject->DeviceObjectExtension->ExtensionFlags |= DOE_START_PENDING;
|
|
deviceObject = deviceObject->AttachedDevice;
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
if (IrpCode == IRP_MN_SURPRISE_REMOVAL) {
|
|
|
|
|
|
// Send irp to remove the device...
|
|
|
|
|
|
PIDBGMSG( PIDBG_REMOVAL,
|
|
("IopDeleteLockedDeviceNode: Sending surprise remove irp to device = 0x%p\n",
|
|
deviceObject));
|
|
|
|
status = IopRemoveDevice(deviceObject, IRP_MN_SURPRISE_REMOVAL);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
PIDBGMSG(PIDBG_REMOVAL, ("IopDeleteLockedDeviceNode: Releasing devices resources\n"));
|
|
|
|
IopReleaseDeviceResources(DeviceNode, FALSE);
|
|
|
|
} else if (!(DeviceNode->Flags & DNF_NO_RESOURCE_REQUIRED)) {
|
|
success = FALSE;
|
|
}
|
|
|
|
ASSERT(DeviceNode->DockInfo.DockStatus != DOCK_ARRIVING);
|
|
|
|
} else if (IrpCode == IRP_MN_REMOVE_DEVICE) {
|
|
|
|
PDEVICE_NODE child, nextChild, parent;
|
|
PDEVICE_OBJECT childPDO;
|
|
|
|
|
|
// Make sure we WILL drop our references to its children.
|
|
|
|
|
|
child = DeviceNode->Child;
|
|
while (child) {
|
|
if (child->Flags & DNF_ENUMERATED) {
|
|
child->Flags &= ~DNF_ENUMERATED;
|
|
}
|
|
|
|
ASSERT(!(child->Flags & DNF_ADDED));
|
|
|
|
if (child->Flags & (DNF_HAS_RESOURCE | DNF_RESOURCE_REPORTED | DNF_HAS_BOOT_CONFIG)) {
|
|
|
|
|
|
// Send irp to remove the about to be orphaned child device...
|
|
|
|
|
|
PIDBGMSG( PIDBG_REMOVAL,
|
|
("IopDeleteLockedDeviceNode: Sending remove irp to orphaned child device = 0x%p\n",
|
|
child->PhysicalDeviceObject));
|
|
|
|
IopRemoveDevice(child->PhysicalDeviceObject, IRP_MN_REMOVE_DEVICE);
|
|
|
|
PIDBGMSG(PIDBG_REMOVAL,
|
|
("IopDeleteLockedDeviceNode: Releasing resources for child device = 0x%p\n",
|
|
child->PhysicalDeviceObject));
|
|
|
|
IopReleaseDeviceResources(child, FALSE);
|
|
|
|
|
|
// BUGBUG - what if the resources aren't released???
|
|
|
|
}
|
|
|
|
|
|
|
|
nextChild = child->Sibling;
|
|
parent = child->Parent;
|
|
|
|
PIDBGMSG( PIDBG_REMOVAL,
|
|
("IopDeleteLockedDeviceNode: Cleaning up registry values, instance = %wZ\n",
|
|
&child->InstancePath));
|
|
|
|
KeEnterCriticalRegion();
|
|
ExAcquireResourceExclusive(&PpRegistryDeviceResource, TRUE);
|
|
|
|
IopCleanupDeviceRegistryValues(&child->InstancePath, FALSE);
|
|
|
|
PIDBGMSG( PIDBG_REMOVAL,
|
|
("IopDeleteLockedDeviceNode: Removing DevNode tree, DevNode = 0x%p\n",
|
|
child));
|
|
|
|
childPDO = child->PhysicalDeviceObject;
|
|
IopRemoveTreeDeviceNode(child);
|
|
IopRemoveRelationFromList(RelationsList, childPDO);
|
|
ObDereferenceObject(childPDO); // Added during Enum
|
|
|
|
ExReleaseResource(&PpRegistryDeviceResource);
|
|
KeLeaveCriticalRegion();
|
|
|
|
if (child->LockCount != 0) {
|
|
|
|
ASSERT(child->LockCount == 1);
|
|
|
|
child->LockCount = 0;
|
|
|
|
KeSetEvent(&child->EnumerationMutex, 0, FALSE);
|
|
ObDereferenceObject(childPDO);
|
|
|
|
ASSERT(parent->LockCount > 0);
|
|
|
|
if (--parent->LockCount == 0) {
|
|
KeSetEvent(&parent->EnumerationMutex, 0, FALSE);
|
|
ObDereferenceObject(parent->PhysicalDeviceObject);
|
|
}
|
|
}
|
|
|
|
child = nextChild;
|
|
}
|
|
|
|
|
|
// Add a reference to each FDO attached to the PDO such that the FDOs won't
|
|
// actually go away until the removal operation is completed.
|
|
// Note we need to make a copy of all the attached devices because we won't be
|
|
// able to traverse the attached chain when the removal operation is done.
|
|
|
|
|
|
device1 = deviceObject->AttachedDevice;
|
|
while (device1) {
|
|
length++;
|
|
device1 = device1->AttachedDevice;
|
|
}
|
|
|
|
attachedDevices = NULL;
|
|
attachedDrivers = NULL;
|
|
if (length != 0) {
|
|
length = (length + 2) * sizeof(PDEVICE_OBJECT);
|
|
attachedDevices = (PDEVICE_OBJECT *)ExAllocatePool (PagedPool, length);
|
|
if (!attachedDevices) {
|
|
return FALSE;
|
|
}
|
|
attachedDrivers = (PDRIVER_OBJECT *)ExAllocatePool (PagedPool, length);
|
|
if (!attachedDrivers) {
|
|
ExFreePool(attachedDevices);
|
|
return FALSE;
|
|
}
|
|
RtlZeroMemory(attachedDevices, length);
|
|
RtlZeroMemory(attachedDrivers, length);
|
|
device1 = deviceObject->AttachedDevice;
|
|
device2 = attachedDevices;
|
|
driver = attachedDrivers;
|
|
while (device1) {
|
|
ObReferenceObject(device1);
|
|
*device2++ = device1;
|
|
*driver++ = device1->DriverObject;
|
|
device1 = device1->AttachedDevice;
|
|
}
|
|
}
|
|
|
|
|
|
// Send irp to remove the device...
|
|
|
|
|
|
PIDBGMSG( PIDBG_REMOVAL,
|
|
("IopDeleteLockedDeviceNode: Sending remove irp to device = 0x%p\n",
|
|
deviceObject));
|
|
|
|
IopRemoveDevice(deviceObject, IRP_MN_REMOVE_DEVICE);
|
|
|
|
PIDBGMSG(PIDBG_REMOVAL, ("IopDeleteLockedDeviceNode: Releasing devices resources\n"));
|
|
|
|
IopReleaseDeviceResources(DeviceNode, (BOOLEAN)!(DeviceNode->Flags & DNF_DEVICE_GONE));
|
|
|
|
if (!(DeviceNode->Flags & DNF_ENUMERATED)) {
|
|
|
|
// If the device is a dock, remove it from the list of dock devices
|
|
// and change the current Hardware Profile, if necessary.
|
|
|
|
ASSERT(DeviceNode->DockInfo.DockStatus != DOCK_ARRIVING) ;
|
|
if ((DeviceNode->DockInfo.DockStatus == DOCK_DEPARTING)||
|
|
(DeviceNode->DockInfo.DockStatus == DOCK_EJECTIRP_COMPLETED)) {
|
|
IopHardwareProfileCommitRemovedDock(DeviceNode);
|
|
}
|
|
}
|
|
|
|
|
|
// Remove the reference to the attached FDOs to allow them to be actually
|
|
// deleted.
|
|
|
|
|
|
if (device2 = attachedDevices) {
|
|
driver = attachedDrivers;
|
|
while (*device2) {
|
|
(*device2)->DeviceObjectExtension->ExtensionFlags &= ~(DOE_REMOVE_PENDING | DOE_REMOVE_PROCESSED);
|
|
(*device2)->DeviceObjectExtension->ExtensionFlags |= DOE_START_PENDING;
|
|
IopUnloadAttachedDriver(*driver);
|
|
ObDereferenceObject(*device2);
|
|
device2++;
|
|
driver++;
|
|
}
|
|
ExFreePool(attachedDevices);
|
|
ExFreePool(attachedDrivers);
|
|
}
|
|
|
|
|
|
// Now mark this one deleted.
|
|
|
|
|
|
if (!(DeviceNode->Flags & DNF_HAS_PRIVATE_PROBLEM)) {
|
|
|
|
ASSERT(!IopDoesDevNodeHaveProblem(DeviceNode) ||
|
|
IopIsDevNodeProblem(DeviceNode, Problem) ||
|
|
Problem == CM_PROB_DEVICE_NOT_THERE ||
|
|
Problem == CM_PROB_DISABLED);
|
|
|
|
IopClearDevNodeProblem(DeviceNode);
|
|
IopSetDevNodeProblem(DeviceNode, Problem);
|
|
}
|
|
|
|
deviceObject->DeviceObjectExtension->ExtensionFlags &= ~(DOE_REMOVE_PENDING | DOE_REMOVE_PROCESSED);
|
|
deviceObject->DeviceObjectExtension->ExtensionFlags |= DOE_START_PENDING;
|
|
|
|
if (DeviceNode->Flags & DNF_REMOVE_PENDING_CLOSES) {
|
|
|
|
ASSERT(DeviceNode->LockCount == 0);
|
|
|
|
DeviceNode->Flags &= ~DNF_REMOVE_PENDING_CLOSES;
|
|
|
|
if ((DeviceNode->Flags & DNF_DEVICE_GONE) && DeviceNode->Parent != NULL) {
|
|
|
|
ASSERT(DeviceNode->Child == NULL);
|
|
|
|
PIDBGMSG( PIDBG_REMOVAL,
|
|
("IopDeleteLockedDeviceNode: Cleaning up registry values, instance = %wZ\n",
|
|
&DeviceNode->InstancePath));
|
|
|
|
KeEnterCriticalRegion();
|
|
ExAcquireResourceExclusive(&PpRegistryDeviceResource, TRUE);
|
|
|
|
IopCleanupDeviceRegistryValues(&DeviceNode->InstancePath, FALSE);
|
|
|
|
ExReleaseResource(&PpRegistryDeviceResource);
|
|
KeLeaveCriticalRegion();
|
|
|
|
PIDBGMSG( PIDBG_REMOVAL,
|
|
("IopDeleteLockedDeviceNode: Removing DevNode tree, DevNode = 0x%p\n",
|
|
DeviceNode));
|
|
|
|
IopRemoveTreeDeviceNode(DeviceNode);
|
|
ObDereferenceObject(deviceObject); // Added during Enum
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
|
|
// The request is QUERY or cancel. If it is query return FALSE on failure.
|
|
|
|
|
|
PIDBGMSG( PIDBG_REMOVAL,
|
|
("IopDeleteLockedDeviceNode: Sending QueryRemove/CancelRemove irp to device = 0x%p\n",
|
|
deviceObject));
|
|
|
|
status = IopRemoveDevice(deviceObject, IrpCode);
|
|
if (!NT_SUCCESS(status) && IrpCode == IRP_MN_QUERY_REMOVE_DEVICE) {
|
|
PIDBGMSG( PIDBG_REMOVAL,
|
|
("IopDeleteLockedDeviceNode: QueryRemove vetoed by device = 0x%p\n",
|
|
deviceObject));
|
|
|
|
success = FALSE;
|
|
}
|
|
}
|
|
return success;
|
|
}
|
|
|
|
NTSTATUS
|
|
IopDeleteLockedDeviceNodes(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PRELATION_LIST RelationsList,
|
|
IN PLUGPLAY_DEVICE_DELETE_TYPE OperationCode,
|
|
IN BOOLEAN IsKernelInitiated,
|
|
IN BOOLEAN ProcessIndirectDescendants,
|
|
IN ULONG Problem,
|
|
OUT PDEVICE_OBJECT *VetoingDevice OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine performs requested operation on the DeviceObject and
|
|
the device objects specified in the DeviceRelations.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Supplies a pointer to the device object.
|
|
|
|
DeviceRelations - supplies a pointer to the device's removal relations.
|
|
|
|
OperationCode - Operation code, i.e., QueryRemove, CancelRemove, Remove...
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS code.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PDEVICE_NODE deviceNode;
|
|
PDEVICE_OBJECT deviceObject, relatedDeviceObject;
|
|
ULONG i;
|
|
ULONG marker;
|
|
ULONG irpCode;
|
|
BOOLEAN tagged, directDescendant;
|
|
|
|
PAGED_CODE();
|
|
|
|
PIDBGMSG( PIDBG_REMOVAL,
|
|
("IopDeleteLockedDeviceNodes: Entered\n DeviceObject = 0x%p\n RelationsList = 0x%p\n OperationCode = %d\n",
|
|
DeviceObject,
|
|
RelationsList,
|
|
OperationCode));
|
|
|
|
deviceNode = (PDEVICE_NODE) DeviceObject->DeviceObjectExtension->DeviceNode;
|
|
|
|
switch (OperationCode) {
|
|
case QueryRemoveDevice:
|
|
irpCode = IRP_MN_QUERY_REMOVE_DEVICE;
|
|
break;
|
|
|
|
case CancelRemoveDevice:
|
|
irpCode = IRP_MN_CANCEL_REMOVE_DEVICE;
|
|
break;
|
|
|
|
case RemoveDevice:
|
|
irpCode = IRP_MN_REMOVE_DEVICE;
|
|
break;
|
|
|
|
case SurpriseRemoveDevice:
|
|
irpCode = IRP_MN_SURPRISE_REMOVAL;
|
|
break;
|
|
|
|
case EjectDevice:
|
|
default:
|
|
ASSERT(0);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
marker = 0;
|
|
while (IopEnumerateRelations( RelationsList,
|
|
&marker,
|
|
&deviceObject,
|
|
&directDescendant,
|
|
&tagged,
|
|
TRUE)) {
|
|
|
|
|
|
// Depending on the operation we need to do different things.
|
|
|
|
// QueryRemoveDevice / CancelRemoveDevice
|
|
// Ignore tagged relations - they were processed by a previous eject
|
|
// Process both direct and indirect descendants
|
|
|
|
// SurpriseRemoveDevice / RemoveDevice
|
|
// None of the relations should be tagged
|
|
// Ignore indirect descendants
|
|
|
|
|
|
if (!tagged && (directDescendant || ProcessIndirectDescendants)) {
|
|
deviceNode = (PDEVICE_NODE)deviceObject->DeviceObjectExtension->DeviceNode;
|
|
|
|
if (OperationCode == SurpriseRemoveDevice) {
|
|
deviceNode->Flags |= DNF_REMOVE_PENDING_CLOSES;
|
|
}
|
|
|
|
if (OperationCode == QueryRemoveDevice && !(deviceNode->Flags & DNF_STARTED)) {
|
|
|
|
// Don't send Queries to devices without FDOs (or filters)
|
|
|
|
continue;
|
|
}
|
|
|
|
if (!IopDeleteLockedDeviceNode( deviceNode,
|
|
irpCode,
|
|
RelationsList,
|
|
IsKernelInitiated,
|
|
Problem)) {
|
|
|
|
if (OperationCode == QueryRemoveDevice) {
|
|
|
|
if (VetoingDevice != NULL) {
|
|
*VetoingDevice = deviceObject;
|
|
}
|
|
|
|
IopDeleteLockedDeviceNode( deviceNode,
|
|
IRP_MN_CANCEL_REMOVE_DEVICE,
|
|
RelationsList,
|
|
IsKernelInitiated,
|
|
Problem);
|
|
|
|
while (IopEnumerateRelations( RelationsList,
|
|
&marker,
|
|
&deviceObject,
|
|
NULL,
|
|
&tagged,
|
|
FALSE)) {
|
|
|
|
if (!tagged) {
|
|
|
|
deviceNode = (PDEVICE_NODE)deviceObject->DeviceObjectExtension->DeviceNode;
|
|
|
|
IopDeleteLockedDeviceNode( deviceNode,
|
|
IRP_MN_CANCEL_REMOVE_DEVICE,
|
|
RelationsList,
|
|
IsKernelInitiated,
|
|
Problem);
|
|
}
|
|
}
|
|
|
|
status = STATUS_UNSUCCESSFUL;
|
|
goto exit;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// As long as there is device removed, try satisfy the DNF_INSUFFICIENT_RESOURCES
|
|
// devices.
|
|
|
|
|
|
if (OperationCode == RemoveDevice) {
|
|
PIDBGMSG( PIDBG_REMOVAL,
|
|
("IopDeleteLockedDeviceNodes: Calling IopRequestDeviceEnumeration\n"));
|
|
|
|
IopRequestDeviceAction(NULL, AssignResources, NULL, NULL);
|
|
}
|
|
|
|
exit:
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
IopLockDeviceRemovalRelations(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PLUGPLAY_DEVICE_DELETE_TYPE OperationCode,
|
|
OUT PRELATION_LIST *RelationsList,
|
|
IN BOOLEAN IsKernelInitiated
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine locks the device subtrees for removal operation and returns
|
|
a list of device objects which need to be removed with the specified
|
|
DeviceObject.
|
|
|
|
Caller must hold a reference to the DeviceObject.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Supplies a pointer to the device object to be removed.
|
|
|
|
OperationCode - Operation code, i.e., QueryEject, CancelEject, Eject...
|
|
|
|
DeviceRelations - supplies a pointer to a variable to receive the device's
|
|
removal relations.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS code.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
PDEVICE_OBJECT deviceObject;
|
|
PDEVICE_NODE deviceNode, parent;
|
|
PRELATION_LIST relationsList;
|
|
ULONG marker;
|
|
BOOLEAN tagged;
|
|
|
|
PAGED_CODE();
|
|
|
|
*RelationsList = NULL;
|
|
|
|
deviceNode = (PDEVICE_NODE)DeviceObject->DeviceObjectExtension->DeviceNode;
|
|
|
|
if (!IsKernelInitiated && !(deviceNode->Flags & DNF_ADDED) && OperationCode != EjectDevice) {
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
// Obviously no one should try to delete the whole device node tree.
|
|
|
|
|
|
ASSERT(DeviceObject != IopRootDeviceNode->PhysicalDeviceObject);
|
|
|
|
|
|
// Lock the whole device node tree so no one can touch the tree.
|
|
|
|
|
|
IopAcquireDeviceTreeLock();
|
|
|
|
if (IsKernelInitiated) {
|
|
|
|
// For kernel initiated removes it means that the device itself is
|
|
// physically gone.
|
|
|
|
|
|
|
|
// We'll take advantage of that signal to go and check if there
|
|
// previously was a QueryRemove done on this devnode. If
|
|
// so, we need to reenumerate the parents of all the relations in
|
|
// case some of them were speculative.
|
|
|
|
}
|
|
|
|
if ((relationsList = IopAllocateRelationList()) == NULL) {
|
|
|
|
IopReleaseDeviceTreeLock();
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
|
|
// First process the object itself
|
|
|
|
status = IopProcessRelation( DeviceObject,
|
|
OperationCode,
|
|
relationsList,
|
|
IsKernelInitiated,
|
|
TRUE);
|
|
|
|
ASSERT(status != STATUS_INVALID_DEVICE_REQUEST);
|
|
|
|
marker = 0;
|
|
while (IopEnumerateRelations( relationsList,
|
|
&marker,
|
|
&deviceObject,
|
|
NULL,
|
|
&tagged,
|
|
FALSE)) {
|
|
|
|
deviceNode = (PDEVICE_NODE)deviceObject->DeviceObjectExtension->DeviceNode;
|
|
|
|
|
|
// If we were successful we need to lock the parents of each of the
|
|
// relations, otherwise we need to unlock each relation.
|
|
|
|
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
// For all the device nodes in the DeviceRelations, we need to lock
|
|
// their parents' enumeration mutex.
|
|
|
|
|
|
if (tagged) {
|
|
|
|
|
|
// If the tagged bit is set then the relation was merged from
|
|
// a pending eject. In this case the devnode isn't locked so
|
|
// we do it now.
|
|
|
|
|
|
if (deviceNode->LockCount++ == 0) {
|
|
ObReferenceObject(deviceNode->PhysicalDeviceObject);
|
|
KeClearEvent(&deviceNode->EnumerationMutex);
|
|
}
|
|
}
|
|
|
|
parent = deviceNode->Parent;
|
|
|
|
if (parent->LockCount++ == 0) {
|
|
ObReferenceObject(parent->PhysicalDeviceObject);
|
|
KeClearEvent(&parent->EnumerationMutex);
|
|
}
|
|
|
|
} else {
|
|
|
|
if (!tagged) {
|
|
|
|
|
|
// If the tagged bit is set then the relation was merged from
|
|
// an pending eject. In this case the devnode isn't locked so
|
|
// we don't need to unlock it, all the others we unlock now.
|
|
|
|
|
|
ASSERT(deviceNode->LockCount > 0);
|
|
|
|
if (--deviceNode->LockCount == 0) {
|
|
KeSetEvent(&deviceNode->EnumerationMutex, 0, FALSE);
|
|
ObDereferenceObject(deviceObject);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Release the device tree.
|
|
|
|
|
|
IopReleaseDeviceTreeLock();
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
IopCompressRelationList(&relationsList);
|
|
*RelationsList = relationsList;
|
|
|
|
|
|
// At this point we have a list of all the relations, those that are
|
|
// direct descendants of the original device we are ejecting or
|
|
// removing have the DirectDescendant bit set.
|
|
|
|
// Relations which were merged from an existing eject have the tagged
|
|
// bit set.
|
|
|
|
// All of the relations and their parents are locked.
|
|
|
|
// There is a reference on each device object by virtue of it being in
|
|
// the list. There is another one on each device object because it is
|
|
// locked and the lock count is >= 1.
|
|
|
|
// There is also a reference on each relation's parent and it's lock
|
|
// count is >= 1.
|
|
|
|
} else {
|
|
IopFreeRelationList(relationsList);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
IopProcessRelation(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PLUGPLAY_DEVICE_DELETE_TYPE OperationCode,
|
|
IN PRELATION_LIST RelationsList,
|
|
IN BOOLEAN IsKernelInitiated,
|
|
IN BOOLEAN IsDirectDescendant
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine builds the list of device objects that need to be removed or
|
|
examined when the passed in device object is torn down.
|
|
|
|
Caller must hold the device tree lock.
|
|
|
|
Arguments:
|
|
|
|
ADRIAO BUGBUG 01/07/1999 - fill this out.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS code.
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_NODE deviceNode, relatedDeviceNode;
|
|
PDEVICE_OBJECT relatedDeviceObject;
|
|
PDEVICE_RELATIONS deviceRelations;
|
|
PLIST_ENTRY ejectLink;
|
|
PPENDING_RELATIONS_LIST_ENTRY ejectEntry;
|
|
PRELATION_LIST pendingRelationList;
|
|
PIRP pendingIrp;
|
|
NTSTATUS status;
|
|
ULONG i;
|
|
|
|
PAGED_CODE();
|
|
|
|
deviceNode = (PDEVICE_NODE)DeviceObject->DeviceObjectExtension->DeviceNode;
|
|
|
|
if (deviceNode == NULL || deviceNode->Parent == NULL) {
|
|
ASSERT(deviceNode != NULL);
|
|
|
|
|
|
// The device has already been removed
|
|
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
if ((OperationCode == QueryRemoveDevice || OperationCode == EjectDevice) &&
|
|
(deviceNode->Flags & DNF_REMOVE_PENDING_CLOSES)) {
|
|
|
|
|
|
// The device is in the process of being surprise removed, let it finish
|
|
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
status = IopAddRelationToList( RelationsList,
|
|
DeviceObject,
|
|
IsDirectDescendant,
|
|
FALSE);
|
|
|
|
if (status == STATUS_SUCCESS) {
|
|
|
|
ASSERT(deviceNode->LockCount == 0);
|
|
|
|
if (!(deviceNode->Flags & DNF_LOCKED_FOR_EJECT)) {
|
|
ObReferenceObject(DeviceObject);
|
|
|
|
KeClearEvent(&deviceNode->EnumerationMutex);
|
|
|
|
deviceNode->LockCount++;
|
|
|
|
|
|
// Then process the bus relations
|
|
|
|
for (relatedDeviceNode = deviceNode->Child;
|
|
relatedDeviceNode != NULL;
|
|
) {
|
|
|
|
relatedDeviceObject = relatedDeviceNode->PhysicalDeviceObject;
|
|
|
|
relatedDeviceNode = relatedDeviceNode->Sibling;
|
|
|
|
status = IopProcessRelation( relatedDeviceObject,
|
|
OperationCode,
|
|
RelationsList,
|
|
IsKernelInitiated,
|
|
IsDirectDescendant
|
|
);
|
|
|
|
ASSERT(status == STATUS_SUCCESS || status == STATUS_UNSUCCESSFUL);
|
|
|
|
if (status == STATUS_INSUFFICIENT_RESOURCES ||
|
|
status == STATUS_INVALID_DEVICE_REQUEST) {
|
|
return status;
|
|
}
|
|
}
|
|
|
|
|
|
// Next the removal relations
|
|
|
|
|
|
|
|
if (deviceNode->Flags & DNF_STARTED) {
|
|
status = IopQueryDeviceRelations( RemovalRelations,
|
|
DeviceObject,
|
|
FALSE,
|
|
&deviceRelations);
|
|
|
|
if (NT_SUCCESS(status) && deviceRelations) {
|
|
|
|
for (i = 0; i < deviceRelations->Count; i++) {
|
|
|
|
relatedDeviceObject = deviceRelations->Objects[i];
|
|
|
|
status = IopProcessRelation( relatedDeviceObject,
|
|
OperationCode,
|
|
RelationsList,
|
|
IsKernelInitiated,
|
|
FALSE);
|
|
|
|
|
|
ObDereferenceObject( relatedDeviceObject );
|
|
|
|
ASSERT(status == STATUS_SUCCESS ||
|
|
status == STATUS_UNSUCCESSFUL);
|
|
|
|
if (status == STATUS_INSUFFICIENT_RESOURCES ||
|
|
status == STATUS_INVALID_DEVICE_REQUEST) {
|
|
|
|
ExFreePool(deviceRelations);
|
|
|
|
return status;
|
|
}
|
|
}
|
|
|
|
ExFreePool(deviceRelations);
|
|
} else {
|
|
if (status != STATUS_NOT_SUPPORTED) {
|
|
PIDBGMSG( PIDBG_WARNING,
|
|
("IopProcessRelation: IopQueryDeviceRelations failed, DeviceObject = 0x%p, status = 0x%08X\n",
|
|
DeviceObject, status));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Finally the eject relations if we are doing an eject operation
|
|
|
|
|
|
if (OperationCode != QueryRemoveDevice &&
|
|
OperationCode != RemoveFailedDevice &&
|
|
OperationCode != RemoveUnstartedFailedDevice) {
|
|
status = IopQueryDeviceRelations( EjectionRelations,
|
|
DeviceObject,
|
|
FALSE,
|
|
&deviceRelations);
|
|
|
|
if (NT_SUCCESS(status) && deviceRelations) {
|
|
|
|
for (i = 0; i < deviceRelations->Count; i++) {
|
|
|
|
relatedDeviceObject = deviceRelations->Objects[i];
|
|
|
|
status = IopProcessRelation( relatedDeviceObject,
|
|
OperationCode,
|
|
RelationsList,
|
|
IsKernelInitiated,
|
|
FALSE);
|
|
|
|
ObDereferenceObject( relatedDeviceObject );
|
|
|
|
ASSERT(status == STATUS_SUCCESS ||
|
|
status == STATUS_UNSUCCESSFUL);
|
|
|
|
if (status == STATUS_INSUFFICIENT_RESOURCES ||
|
|
status == STATUS_INVALID_DEVICE_REQUEST) {
|
|
|
|
ExFreePool(deviceRelations);
|
|
|
|
return status;
|
|
}
|
|
}
|
|
|
|
ExFreePool(deviceRelations);
|
|
} else {
|
|
if (status != STATUS_NOT_SUPPORTED) {
|
|
PIDBGMSG( PIDBG_WARNING,
|
|
("IopProcessRelation: IopQueryDeviceRelations failed, DeviceObject = 0x%p, status = 0x%08X\n",
|
|
DeviceObject, status));
|
|
}
|
|
}
|
|
}
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
} else {
|
|
|
|
|
|
// Look to see if this device is already part of a pending ejection.
|
|
// If it is and we are doing an ejection then we will subsume it
|
|
// within the larger ejection. If we aren't doing an ejection then
|
|
// we better be processing the removal of one of the ejected devices.
|
|
|
|
|
|
for (ejectLink = IopPendingEjects.Flink;
|
|
ejectLink != &IopPendingEjects;
|
|
ejectLink = ejectLink->Flink) {
|
|
|
|
ejectEntry = CONTAINING_RECORD( ejectLink,
|
|
PENDING_RELATIONS_LIST_ENTRY,
|
|
Link);
|
|
|
|
if (ejectEntry->RelationsList != NULL &&
|
|
IopIsRelationInList(ejectEntry->RelationsList, DeviceObject)) {
|
|
|
|
|
|
if (OperationCode == EjectDevice) {
|
|
|
|
status = IopRemoveRelationFromList(RelationsList, DeviceObject);
|
|
|
|
ASSERT(NT_SUCCESS(status));
|
|
|
|
pendingIrp = InterlockedExchangePointer(&ejectEntry->EjectIrp, NULL);
|
|
pendingRelationList = ejectEntry->RelationsList;
|
|
ejectEntry->RelationsList = NULL;
|
|
|
|
if (pendingIrp != NULL) {
|
|
IoCancelIrp(pendingIrp);
|
|
}
|
|
|
|
|
|
// ADRIAO BUGBUG 11/10/98 -
|
|
// If a parent fails eject and it has a child that is
|
|
// infinitely pending an eject, this means the child now
|
|
// wakes up. One suggestion brought up that does not involve
|
|
// a code change is to amend the WDM spec to say if driver
|
|
// gets a start IRP for a device pending eject, it should
|
|
// cancel the eject IRP automatically.
|
|
|
|
IopMergeRelationLists(RelationsList, pendingRelationList, TRUE);
|
|
|
|
IopFreeRelationList(pendingRelationList);
|
|
|
|
if (IsDirectDescendant) {
|
|
|
|
// If IsDirectDescendant is specified then we need to
|
|
// get that bit set on the relation that caused us to
|
|
// do the merge. IopAddRelationToList will fail with
|
|
// STATUS_OBJECT_NAME_COLLISION but the bit will still
|
|
// be set as a side effect.
|
|
|
|
IopAddRelationToList( RelationsList,
|
|
DeviceObject,
|
|
TRUE,
|
|
FALSE);
|
|
}
|
|
} else if (OperationCode == RemoveDevice) {
|
|
|
|
|
|
// We haven't processed the completion of the eject IRP
|
|
// before this device went away. We'll remove it from
|
|
// the list in the pending ejection and return it.
|
|
|
|
|
|
status = IopRemoveRelationFromList( ejectEntry->RelationsList,
|
|
DeviceObject);
|
|
|
|
deviceNode->Flags &= ~DNF_LOCKED_FOR_EJECT;
|
|
|
|
ASSERT(NT_SUCCESS(status));
|
|
|
|
ObReferenceObject(DeviceObject);
|
|
|
|
KeClearEvent(&deviceNode->EnumerationMutex);
|
|
|
|
deviceNode->LockCount++;
|
|
} else {
|
|
|
|
ASSERT(0);
|
|
|
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
ASSERT(ejectLink != &IopPendingEjects);
|
|
|
|
if (ejectLink == &IopPendingEjects) {
|
|
KeBugCheckEx( PNP_DETECTED_FATAL_ERROR,
|
|
PNP_ERR_DEVICE_MISSING_FROM_EJECT_LIST,
|
|
(ULONG_PTR)DeviceObject,
|
|
0,
|
|
0);
|
|
}
|
|
}
|
|
} else if (status == STATUS_OBJECT_NAME_COLLISION) {
|
|
PIDBGMSG( PIDBG_INFORMATION,
|
|
("IopProcessRelation: Duplicate relation, DeviceObject = 0x%p\n",
|
|
DeviceObject));
|
|
|
|
status = STATUS_SUCCESS;
|
|
} else if (status != STATUS_INSUFFICIENT_RESOURCES) {
|
|
KeBugCheckEx( PNP_DETECTED_FATAL_ERROR,
|
|
PNP_ERR_UNEXPECTED_ADD_RELATION_ERR,
|
|
(ULONG_PTR)DeviceObject,
|
|
(ULONG_PTR)RelationsList,
|
|
status);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
BOOLEAN
|
|
IopQueuePendingEject(
|
|
PPENDING_RELATIONS_LIST_ENTRY Entry
|
|
)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
IopAcquireDeviceTreeLock();
|
|
|
|
InsertTailList(&IopPendingEjects, &Entry->Link);
|
|
|
|
IopReleaseDeviceTreeLock();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
NTSTATUS
|
|
IopInvalidateRelationsInList(
|
|
PRELATION_LIST RelationsList,
|
|
BOOLEAN OnlyIndirectDescendants,
|
|
BOOLEAN UnlockDevNode,
|
|
BOOLEAN RestartDevNode
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Iterate over the relations in the list creating a second list containing the
|
|
parent of each entry skipping parents which are also in the list. In other
|
|
words, if the list contains node P and node C where node C is a child of node
|
|
P then the parent of node P would be added but not node P itself.
|
|
|
|
|
|
Arguments:
|
|
|
|
RelationsList - List of relations
|
|
|
|
OnlyIndirectDescendants - Indirect relations are those which aren't direct
|
|
descendants (bus relations) of the PDO originally
|
|
targetted for the operation or its direct
|
|
descendants. This would include Removal or
|
|
Eject relations.
|
|
|
|
UnlockDevNode - If true then any node who's parent was invalidated
|
|
is unlocked. In the case where the parent is also
|
|
in the list the node is still unlocked as will the
|
|
parent be once we process it.
|
|
|
|
RestartDevNode - If true then any node who's parent was invalidated
|
|
is restarted. This flag requires that all the
|
|
relations in the list have been previously
|
|
sent a remove IRP.
|
|
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS code.
|
|
|
|
--*/
|
|
|
|
{
|
|
PRELATION_LIST parentsList;
|
|
PDEVICE_OBJECT deviceObject, parentObject;
|
|
PDEVICE_NODE deviceNode, parentNode;
|
|
ULONG marker;
|
|
BOOLEAN directDescendant, tagged;
|
|
|
|
PAGED_CODE();
|
|
|
|
parentsList = IopAllocateRelationList();
|
|
|
|
if (parentsList == NULL) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
IopSetAllRelationsTags( RelationsList, FALSE );
|
|
|
|
|
|
// Traverse the list creating a new list with the topmost parents of
|
|
// each sublist contained in RelationsList.
|
|
|
|
|
|
marker = 0;
|
|
|
|
while (IopEnumerateRelations( RelationsList,
|
|
&marker,
|
|
&deviceObject,
|
|
&directDescendant,
|
|
&tagged,
|
|
TRUE)) {
|
|
|
|
if (!OnlyIndirectDescendants || !directDescendant) {
|
|
|
|
if (!tagged) {
|
|
|
|
parentObject = deviceObject;
|
|
|
|
while (IopSetRelationsTag( RelationsList, parentObject, TRUE ) == STATUS_SUCCESS) {
|
|
|
|
deviceNode = parentObject->DeviceObjectExtension->DeviceNode;
|
|
|
|
if (RestartDevNode) {
|
|
|
|
deviceNode->Flags &= ~DNF_LOCKED_FOR_EJECT;
|
|
|
|
if ((deviceNode->Flags & (DNF_PROCESSED | DNF_ENUMERATED)) ==
|
|
(DNF_PROCESSED | DNF_ENUMERATED)) {
|
|
|
|
ASSERT(deviceNode->Child == NULL);
|
|
ASSERT(!(deviceNode->Flags & DNF_ADDED));
|
|
|
|
IopClearDevNodeProblem( deviceNode );
|
|
IopRestartDeviceNode( deviceNode );
|
|
}
|
|
}
|
|
|
|
if (UnlockDevNode) {
|
|
parentNode = deviceNode->Parent;
|
|
|
|
ASSERT(parentNode != NULL);
|
|
|
|
ASSERT(deviceNode->LockCount > 0);
|
|
|
|
if (--deviceNode->LockCount == 0) {
|
|
KeSetEvent(&deviceNode->EnumerationMutex, 0, FALSE);
|
|
ObDereferenceObject(deviceNode->PhysicalDeviceObject);
|
|
}
|
|
|
|
ASSERT(parentNode->LockCount > 0);
|
|
|
|
if (--parentNode->LockCount == 0) {
|
|
KeSetEvent(&parentNode->EnumerationMutex, 0, FALSE);
|
|
ObDereferenceObject(parentNode->PhysicalDeviceObject);
|
|
}
|
|
}
|
|
|
|
if (deviceNode->Parent != NULL) {
|
|
|
|
parentObject = deviceNode->Parent->PhysicalDeviceObject;
|
|
|
|
} else {
|
|
parentObject = NULL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (parentObject != NULL) {
|
|
IopAddRelationToList( parentsList, parentObject, FALSE, FALSE );
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
// Reenumerate each of the parents
|
|
|
|
|
|
marker = 0;
|
|
|
|
while (IopEnumerateRelations( parentsList,
|
|
&marker,
|
|
&deviceObject,
|
|
NULL,
|
|
NULL,
|
|
FALSE)) {
|
|
|
|
IopRequestDeviceAction( deviceObject,
|
|
ReenumerateDeviceTree,
|
|
NULL,
|
|
NULL );
|
|
}
|
|
|
|
|
|
// Free the parents list
|
|
|
|
|
|
IopFreeRelationList( parentsList );
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
IopProcessCompletedEject(
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called at passive level from a worker thread that was queued
|
|
either when an eject IRP completed (see io\pnpirp.c - IopDeviceEjectComplete
|
|
or io\pnpirp.c - IopEjectDevice), or when a warm eject needs to be performed.
|
|
We also may need to fire off any enumerations of parents of ejected devices
|
|
to verify they have indeed left.
|
|
|
|
Arguments:
|
|
|
|
Context - Pointer to the pending relations list which contains the device
|
|
to eject (warm) and the list of parents to reenumerate.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PPENDING_RELATIONS_LIST_ENTRY entry = (PPENDING_RELATIONS_LIST_ENTRY)Context;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
PAGED_CODE();
|
|
|
|
if ((entry->LightestSleepState != PowerSystemWorking) &&
|
|
(entry->LightestSleepState != PowerSystemUnspecified)) {
|
|
|
|
|
|
// For docks, WinLogon gets to do the honors. For other devices, the
|
|
// user must infer when it's safe to remove the device (if we've powered
|
|
// up, it may not be safe now!)
|
|
|
|
entry->DisplaySafeRemovalDialog = FALSE;
|
|
|
|
|
|
// This is a warm eject request, initiate it here.
|
|
|
|
status = IopWarmEjectDevice(entry->DeviceObject, entry->LightestSleepState);
|
|
|
|
|
|
// We're back and we either succeeded or failed. Either way...
|
|
|
|
}
|
|
|
|
if (entry->DockInterface) {
|
|
|
|
entry->DockInterface->ProfileDepartureSetMode(
|
|
entry->DockInterface->Context,
|
|
PDS_UPDATE_DEFAULT
|
|
);
|
|
|
|
entry->DockInterface->InterfaceDereference(
|
|
entry->DockInterface->Context
|
|
);
|
|
}
|
|
|
|
IopAcquireDeviceTreeLock();
|
|
|
|
RemoveEntryList( &entry->Link );
|
|
|
|
|
|
// Check if the RelationsList pointer in the context structure is NULL. If
|
|
// so, this means we were cancelled because this eject is part of a new
|
|
// larger eject. In that case all we want to do is unlink and free the
|
|
// context structure.
|
|
|
|
|
|
|
|
// Two interesting points about such code.
|
|
|
|
// 1) If you wait forever to complete an eject of a dock, we *wait* forever
|
|
// in the Query profile change state. No sneaky adding another dock. You
|
|
// must finish what you started...
|
|
// 2) Let's say you are ejecting a dock, and it is taking a long time. If
|
|
// you try to eject the parent, that eject will *not* grab this lower
|
|
// eject as we will block on the profile change semaphore. Again, finish
|
|
// what you started...
|
|
|
|
|
|
if (entry->RelationsList != NULL) {
|
|
|
|
if (entry->ProfileChangingEject) {
|
|
|
|
IopHardwareProfileSetMarkedDocksEjected();
|
|
}
|
|
|
|
IopInvalidateRelationsInList( entry->RelationsList, FALSE, FALSE, TRUE );
|
|
|
|
|
|
// Free the relations list
|
|
|
|
|
|
IopFreeRelationList( entry->RelationsList );
|
|
|
|
} else {
|
|
|
|
entry->DisplaySafeRemovalDialog = FALSE;
|
|
}
|
|
|
|
IopReleaseDeviceTreeLock();
|
|
|
|
|
|
// Complete the event
|
|
|
|
if (entry->DeviceEvent != NULL ) {
|
|
|
|
PpCompleteDeviceEvent( entry->DeviceEvent, status );
|
|
}
|
|
|
|
if (entry->DisplaySafeRemovalDialog) {
|
|
|
|
PpSetDeviceRemovalSafe(entry->DeviceObject, NULL, NULL);
|
|
}
|
|
|
|
ExFreePool( entry );
|
|
}
|
|
|
|
BOOLEAN
|
|
IopQueuePendingSurpriseRemoval(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PRELATION_LIST List,
|
|
IN ULONG Problem
|
|
)
|
|
{
|
|
PPENDING_RELATIONS_LIST_ENTRY entry;
|
|
|
|
PAGED_CODE();
|
|
|
|
entry = ExAllocatePool( NonPagedPool, sizeof(PENDING_RELATIONS_LIST_ENTRY) );
|
|
|
|
if (entry != NULL) {
|
|
|
|
entry->DeviceObject = DeviceObject;
|
|
entry->RelationsList = List;
|
|
entry->Problem = Problem;
|
|
entry->ProfileChangingEject = FALSE ;
|
|
|
|
IopAcquireDeviceTreeLock();
|
|
|
|
InsertTailList(&IopPendingSurpriseRemovals, &entry->Link);
|
|
|
|
IopReleaseDeviceTreeLock();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
NTSTATUS
|
|
IopUnlockDeviceRemovalRelations(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PRELATION_LIST RelationsList,
|
|
IN UNLOCK_UNLINK_ACTION UnlinkAction
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine unlocks the device tree deletion operation.
|
|
If there is any pending kernel deletion, this routine initiates
|
|
a worker thread to perform the work.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Supplies a pointer to the device object to which the remove
|
|
was originally targetted (as opposed to one of the relations).
|
|
|
|
DeviceRelations - supplies a pointer to the device's removal relations.
|
|
|
|
UnlinkAction - Specifies which devnodes will be unlinked from the devnode
|
|
tree.
|
|
|
|
UnLinkRemovedDeviceNodes - Devnodes which are no longer enumerated and
|
|
have been sent a REMOVE_DEVICE IRP are unlinked.
|
|
|
|
UnlinkAllDeviceNodesPendingClose - This is used when a device is
|
|
surprise removed. Devnodes in RelationsList are unlinked from the
|
|
tree if they don't have children and aren't consuming any resources.
|
|
|
|
UnlinkOnlyChildDeviceNodesPendingClose - This is used when a device fails
|
|
while started. We unlink any child devnodes of the device which
|
|
failed but not the failed device's devnode.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS code.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
PDEVICE_NODE deviceNode, parent;
|
|
PDEVICE_OBJECT deviceObject, relatedDeviceObject;
|
|
ULONG i;
|
|
ULONG marker;
|
|
|
|
PAGED_CODE();
|
|
|
|
KeEnterCriticalRegion();
|
|
ExAcquireResourceExclusive(&PpRegistryDeviceResource, TRUE);
|
|
|
|
if (ARGUMENT_PRESENT(RelationsList)) {
|
|
marker = 0;
|
|
while (IopEnumerateRelations( RelationsList,
|
|
&marker,
|
|
&deviceObject,
|
|
NULL,
|
|
NULL,
|
|
TRUE)) {
|
|
|
|
deviceNode = (PDEVICE_NODE)deviceObject->DeviceObjectExtension->DeviceNode;
|
|
parent = deviceNode->Parent;
|
|
|
|
|
|
// There are three different scenarios in which we want to unlink a
|
|
// devnode from the tree. The first case is tested in the first
|
|
// part of the condition. The other two in the second part.
|
|
|
|
// 1) A devnode is no longer enumerated and has been sent a
|
|
// remove IRP.
|
|
|
|
// 2) A devnode has been surprise removed, has no children, has
|
|
// no resources or they've been freed. UnlinkAction will be
|
|
// UnlinkAllDeviceNodesPendingClose.
|
|
|
|
// 3) A devnode has failed and a surprise remove IRP has been sent.
|
|
// Then we want to remove children without resources but not the
|
|
// failed devnode itself. UnlinkAction will be
|
|
// UnlinkOnlyChildDeviceNodesPendingClose.
|
|
|
|
|
|
if ((!(deviceNode->Flags & (DNF_ENUMERATED | DNF_REMOVE_PENDING_CLOSES)) &&
|
|
IopDoesDevNodeHaveProblem(deviceNode)) ||
|
|
(UnlinkAction != UnlinkRemovedDeviceNodes &&
|
|
(deviceNode->Flags & DNF_REMOVE_PENDING_CLOSES) &&
|
|
!(deviceNode->Flags & DNF_RESOURCE_ASSIGNED) &&
|
|
deviceNode->Child == NULL &&
|
|
(UnlinkAction == UnlinkAllDeviceNodesPendingClose ||
|
|
deviceObject != DeviceObject))) {
|
|
|
|
PIDBGMSG( PIDBG_REMOVAL,
|
|
("IopUnlockDeviceRemovalRelations: Cleaning up registry values, instance = %wZ\n",
|
|
&deviceNode->InstancePath));
|
|
|
|
IopCleanupDeviceRegistryValues(&deviceNode->InstancePath, FALSE);
|
|
|
|
PIDBGMSG( PIDBG_REMOVAL,
|
|
("IopUnlockDeviceRemovalRelations: Removing DevNode tree, DevNode = 0x%p\n",
|
|
deviceNode));
|
|
|
|
IopRemoveTreeDeviceNode(deviceNode);
|
|
|
|
if (!(deviceNode->Flags & DNF_REMOVE_PENDING_CLOSES)) {
|
|
IopRemoveRelationFromList(RelationsList, deviceObject);
|
|
}
|
|
ObDereferenceObject(deviceObject); // Added during enum
|
|
}
|
|
|
|
ASSERT(deviceNode->LockCount > 0);
|
|
|
|
if (--deviceNode->LockCount == 0) {
|
|
KeSetEvent(&deviceNode->EnumerationMutex, 0, FALSE);
|
|
ObDereferenceObject(deviceObject);
|
|
}
|
|
|
|
ASSERT(parent->LockCount > 0);
|
|
|
|
if (--parent->LockCount == 0) {
|
|
KeSetEvent(&parent->EnumerationMutex, 0, FALSE);
|
|
ObDereferenceObject(parent->PhysicalDeviceObject);
|
|
}
|
|
}
|
|
}
|
|
|
|
ExReleaseResource(&PpRegistryDeviceResource);
|
|
KeLeaveCriticalRegion();
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
// The routines below are specific to kernel mode PnP configMgr.
|
|
|
|
NTSTATUS
|
|
IopRequestDeviceRemoval(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN ULONG Problem
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine queues a work item to delete a device. (This is because
|
|
to delete a device we need to lock device tree. Most of the places where
|
|
we want to delete a device have DeviceNode Enumeration lock acquired.)
|
|
This is for IO internal use only.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Supplies a pointer to the device object to be eject.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS code.
|
|
|
|
--*/
|
|
|
|
{
|
|
PAGED_CODE();
|
|
|
|
ASSERT(DeviceObject->DeviceObjectExtension->DeviceNode != NULL);
|
|
|
|
ASSERT(((PDEVICE_NODE)DeviceObject->DeviceObjectExtension->DeviceNode)->InstancePath.Length != 0);
|
|
|
|
|
|
// Queue the event, we'll return immediately after it's queued.
|
|
|
|
|
|
return PpSetTargetDeviceRemove( DeviceObject,
|
|
TRUE,
|
|
TRUE,
|
|
FALSE,
|
|
Problem,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL);
|
|
}
|
|
|
|
NTSTATUS
|
|
IopUnloadAttachedDriver(
|
|
IN PDRIVER_OBJECT DriverObject
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This function unloads the driver for the specified device object if it does not control any other device object.
|
|
Arguments:
|
|
DeviceObject - Supplies a pointer to a device object
|
|
Return Value:
|
|
NTSTATUS code.
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
PWCHAR buffer;
|
|
UNICODE_STRING unicodeName;
|
|
PUNICODE_STRING serviceName = &DriverObject->DriverExtension->ServiceKeyName;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (DriverObject->DriverSection != NULL) {
|
|
if (DriverObject->DeviceObject == NULL) {
|
|
buffer = (PWCHAR) ExAllocatePool(
|
|
PagedPool,
|
|
CmRegistryMachineSystemCurrentControlSetServices.Length +
|
|
serviceName->Length + sizeof(WCHAR) +
|
|
sizeof(L"\\"));
|
|
if (!buffer) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
swprintf(buffer,
|
|
L"%s\\%s",
|
|
CmRegistryMachineSystemCurrentControlSetServices.Buffer,
|
|
serviceName->Buffer);
|
|
RtlInitUnicodeString(&unicodeName, buffer);
|
|
status = ZwUnloadDriver(&unicodeName);
|
|
#if DBG
|
|
if (NT_SUCCESS(status)) {
|
|
DbgPrint("****** Unloaded driver (%wZ)\n", serviceName);
|
|
} else {
|
|
DbgPrint("****** Error unloading driver (%wZ), status = 0x%08X\n", serviceName, status);
|
|
}
|
|
#endif
|
|
ExFreePool(unicodeName.Buffer);
|
|
}
|
|
#if DBG
|
|
else {
|
|
DbgPrint("****** Skipping unload of driver (%wZ), DriverObject->DeviceObject != NULL\n", serviceName);
|
|
}
|
|
#endif
|
|
}
|
|
#if DBG
|
|
else {
|
|
// This is a boot driver, can't be unloaded just return SUCCESS
|
|
DbgPrint("****** Skipping unload of boot driver (%wZ)\n", serviceName);
|
|
}
|
|
#endif
|
|
return STATUS_SUCCESS;
|
|
} |