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

614 lines
15 KiB
C

/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
devnode.c
Abstract:
This file contains routines to maintain our private device node list.
Author:
Forrest Foltz (forrestf) 27-Mar-1996
Revision History:
Modified for nt kernel.
--*/
#include "iop.h"
// Internal definitions
typedef struct _ENUM_CONTEXT{
PENUM_CALLBACK CallersCallback;
PVOID CallersContext;
} ENUM_CONTEXT, *PENUM_CONTEXT;
// Internal References
NTSTATUS
IopForAllDeviceNodesCallback(
IN PDEVICE_NODE DeviceNode,
IN PVOID Context
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, IopAllocateDeviceNode)
#pragma alloc_text(PAGE, IopForAllDeviceNodes)
#pragma alloc_text(PAGE, IopForAllChildDeviceNodes)
#pragma alloc_text(PAGE, IopForAllDeviceNodesCallback)
#pragma alloc_text(PAGE, IopDestroyDeviceNode)
#pragma alloc_text(PAGE, IopInsertTreeDeviceNode)
#pragma alloc_text(PAGE, IopRemoveTreeDeviceNode)
#endif
PDEVICE_NODE
IopAllocateDeviceNode(
IN PDEVICE_OBJECT PhysicalDeviceObject
)
/*++
Routine Description:
This function allocates a device node from nonpaged pool and initializes
the fields which do not require to hold lock to do so. Since adding
the device node to pnp mgr's device node tree requires acquiring lock,
this routine does not add the device node to device node tree.
Arguments:
PhysicalDeviceObject - Supplies a pointer to its corresponding physical device
object.
Return Value:
a pointer to the newly created device node. Null is returned if failed.
--*/
{
PDEVICE_NODE deviceNode;
PAGED_CODE();
deviceNode = ExAllocatePoolWithTag(NonPagedPool, sizeof(DEVICE_NODE), IOP_DNOD_TAG);
if (deviceNode == NULL ){
return NULL;
}
InterlockedIncrement (&IopNumberDeviceNodes);
RtlZeroMemory(deviceNode, sizeof(DEVICE_NODE));
deviceNode->InterfaceType = InterfaceTypeUndefined;
deviceNode->BusNumber = (ULONG)-1;
deviceNode->ChildInterfaceType = InterfaceTypeUndefined;
deviceNode->ChildBusNumber = (ULONG)-1;
deviceNode->ChildBusTypeIndex = (USHORT)-1;
KeInitializeEvent( &deviceNode->EnumerationMutex, SynchronizationEvent, TRUE );
InitializeListHead(&deviceNode->DeviceArbiterList);
InitializeListHead(&deviceNode->DeviceTranslatorList);
if (PhysicalDeviceObject){
deviceNode->PhysicalDeviceObject = PhysicalDeviceObject;
PhysicalDeviceObject->DeviceObjectExtension->DeviceNode = (PVOID)deviceNode;
PhysicalDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
}
InitializeListHead(&deviceNode->TargetDeviceNotify);
InitializeListHead(&deviceNode->DockInfo.ListEntry);
InitializeListHead(&deviceNode->PendedSetInterfaceState);
return deviceNode;
}
NTSTATUS IopForAllDeviceNodes(IN PENUM_CALLBACK Callback, IN PVOID Context)
/*++
Routine Description:
This function walks the device node tree and perform caller specified
'Callback' function for each device node.
Note, this routine (or its worker routine) traverses the tree in a top down manner.
Arguments:
Callback - Supplies the call back routine for each device node.
Context - Supplies a parameter/context for the callback function.
Return Value:
Status returned from Callback, if not successfull then the tree walking stops.
--*/
{
ENUM_CONTEXT enumContext;
NTSTATUS status;
PAGED_CODE();
enumContext.CallersCallback = Callback;
enumContext.CallersContext = Context;
// Start with a pointer to the root device node, recursively examine all the
// children until we the callback function says stop or we've looked at all
// of them.
IopAcquireEnumerationLock(IopRootDeviceNode);
status = IopForAllChildDeviceNodes(IopRootDeviceNode,
IopForAllDeviceNodesCallback,
(PVOID)&enumContext );
IopReleaseEnumerationLock(IopRootDeviceNode);
return status;
}
NTSTATUS
IopForAllChildDeviceNodes(
IN PDEVICE_NODE Parent,
IN PENUM_CALLBACK Callback,
IN PVOID Context
)
/*++
Routine Description:
This function walks the Parent's device node subtree and perform caller specified
'Callback' function for each device node under Parent.
Note, befor calling this rotuine, callers must acquire the enumeration mutex
of the 'Parent' device node to make sure its children won't go away unless the
call tells them to.
Arguments:
Parent - Supplies a pointer to the device node whose subtree is to be walked.
Callback - Supplies the call back routine for each device node.
Context - Supplies a parameter/context for the callback function.
Return Value:
NTSTATUS value.
--*/
{
PDEVICE_NODE nextChild = Parent->Child;
PDEVICE_NODE child;
NTSTATUS status = STATUS_SUCCESS;
PAGED_CODE();
// Process siblings until we find the end of the sibling list or
// the Callback() returns FALSE. Set result = TRUE at the top of
// the loop so that if there are no siblings we will return TRUE,
// e.g. Keep Enumerating.
// Note, we need to find next child before calling Callback function
// in case the current child is deleted by the Callback function.
while (nextChild && NT_SUCCESS(status)) {
child = nextChild;
nextChild = child->Sibling;
status = Callback(child, Context);
}
return status;
}
NTSTATUS
IopForAllDeviceNodesCallback(
IN PDEVICE_NODE DeviceNode,
IN PVOID Context
)
/*++
Routine Description:
This function is the worker routine for IopForAllChildDeviceNodes routine.
Arguments:
DeviceNode - Supplies a pointer to the device node whose subtree is to be walked.
Context - Supplies a context which contains the caller specified call back
function and parameter.
Return Value:
NTSTATUS value.
--*/
{
PENUM_CONTEXT enumContext;
NTSTATUS status;
PAGED_CODE();
enumContext = (PENUM_CONTEXT)Context;
// First call the caller's callback for this devnode
status =
enumContext->CallersCallback(DeviceNode, enumContext->CallersContext);
if (NT_SUCCESS(status)) {
// Now enumerate the children, if any.
IopAcquireEnumerationLock(DeviceNode);
if( DeviceNode->Child) {
status = IopForAllChildDeviceNodes(
DeviceNode,
IopForAllDeviceNodesCallback,
Context);
}
IopReleaseEnumerationLock(DeviceNode);
}
return status;
}
VOID
IopDestroyDeviceNode(
IN PDEVICE_NODE DeviceNode
)
/*++
Routine Description:
This function is invoked by IopDeleteDevice to clean up the device object's
device node structure.
Arguments:
DeviceNode - Supplies a pointer to the device node whose subtree is to be walked.
Context - Supplies a context which contains the caller specified call back
function and parameter.
Return Value:
NTSTATUS value.
--*/
{
PLIST_ENTRY listHead, nextEntry, entry;
PPI_RESOURCE_TRANSLATOR_ENTRY handlerEntry;
PINTERFACE interface;
PAGED_CODE();
if (DeviceNode) {
if ((DeviceNode->PhysicalDeviceObject->Flags & DO_BUS_ENUMERATED_DEVICE) &&
DeviceNode->Parent != NULL) {
KeBugCheckEx( PNP_DETECTED_FATAL_ERROR,
PNP_ERR_ACTIVE_PDO_FREED,
(ULONG_PTR)DeviceNode->PhysicalDeviceObject,
0,
0);
}
#if DBG
// If Only Parent is NOT NULL, most likely the driver forgot to
// release resources before deleting its FDO. (The driver previously
// call legacy assign resource interface.)
ASSERT(DeviceNode->Child == NULL &&
DeviceNode->Sibling == NULL &&
DeviceNode->LastChild == NULL
);
ASSERT(DeviceNode->DockInfo.SerialNumber == NULL &&
IsListEmpty(&DeviceNode->DockInfo.ListEntry));
if (DeviceNode->PhysicalDeviceObject->Flags & DO_BUS_ENUMERATED_DEVICE) {
ASSERT (DeviceNode->Parent == 0);
}
if (DeviceNode->PreviousResourceList) {
ExFreePool(DeviceNode->PreviousResourceList);
}
if (DeviceNode->PreviousResourceRequirements) {
ExFreePool(DeviceNode->PreviousResourceRequirements);
}
// device should not appear to be not-disableable if/when we get here
// if either of these two lines ASSERT, email: jamiehun
ASSERT((DeviceNode->UserFlags & DNUF_NOT_DISABLEABLE) == 0);
ASSERT(DeviceNode->DisableableDepends == 0);
#endif
// If this devicenode is our internal one used for legacy
// resource allocation, then clean up. Find this devicenode
// in the list of legacy resource devnodes hanging off the
// legacy devicenode in the tree and remove it.
// BUGBUG: SantoshJ 10/15/99: We should be releasing
// resources assigned to this device.
if (DeviceNode->Flags & DNF_LEGACY_RESOURCE_DEVICENODE) {
PDEVICE_NODE resourceDeviceNode;
for ( resourceDeviceNode = (PDEVICE_NODE)DeviceNode->OverUsed1.LegacyDeviceNode;
resourceDeviceNode;
resourceDeviceNode = resourceDeviceNode->OverUsed2.NextResourceDeviceNode) {
if (resourceDeviceNode->OverUsed2.NextResourceDeviceNode == DeviceNode) {
resourceDeviceNode->OverUsed2.NextResourceDeviceNode = DeviceNode->OverUsed2.NextResourceDeviceNode;
break;
}
}
}
if (DeviceNode->DuplicatePDO) {
ObDereferenceObject(DeviceNode->DuplicatePDO);
}
if (DeviceNode->ServiceName.Length != 0) {
ExFreePool(DeviceNode->ServiceName.Buffer);
}
if (DeviceNode->InstancePath.Length != 0) {
ExFreePool(DeviceNode->InstancePath.Buffer);
}
if (DeviceNode->ResourceRequirements) {
ExFreePool(DeviceNode->ResourceRequirements);
}
// Dereference all the arbiters and translators on this PDO.
IopUncacheInterfaceInformation(DeviceNode->PhysicalDeviceObject) ;
// Release any pended IoSetDeviceInterface structures
while (!IsListEmpty(&DeviceNode->PendedSetInterfaceState)) {
PPENDING_SET_INTERFACE_STATE entry;
entry = (PPENDING_SET_INTERFACE_STATE)RemoveHeadList(&DeviceNode->PendedSetInterfaceState);
ExFreePool(entry->LinkName.Buffer);
ExFreePool(entry);
}
DeviceNode->PhysicalDeviceObject->DeviceObjectExtension->DeviceNode = NULL;
ExFreePool(DeviceNode);
IopNumberDeviceNodes--;
}
}
// Code to support Power manager's need to traverse the device node tree in inverse order,
// (from the leaves towards the root.)
VOID
IopInsertTreeDeviceNode (
IN PDEVICE_NODE ParentNode,
IN PDEVICE_NODE DeviceNode
)
// N.B. Caller must own the device tree lock
{
PDEVICE_NODE deviceNode;
PLIST_ENTRY *p;
LONG i;
// Put this devnode at the end of the parent's list of children.
DeviceNode->Parent = ParentNode;
if (ParentNode->LastChild) {
ASSERT(ParentNode->LastChild->Sibling == NULL);
ParentNode->LastChild->Sibling = DeviceNode;
ParentNode->LastChild = DeviceNode;
} else {
ASSERT(ParentNode->Child == NULL);
ParentNode->Child = ParentNode->LastChild = DeviceNode;
}
// Determine the depth of the devnode.
for (deviceNode = DeviceNode;
deviceNode != IopRootDeviceNode;
deviceNode = deviceNode->Parent) {
DeviceNode->Level++;
}
if (DeviceNode->Level > IopMaxDeviceNodeLevel) {
IopMaxDeviceNodeLevel = DeviceNode->Level;
}
// Tree has changed
IoDeviceNodeTreeSequence += 1;
}
VOID
IopRemoveTreeDeviceNode (
IN PDEVICE_NODE DeviceNode
)
/*++
Routine Description:
This function removes the device node from the device node tree
N.B. The caller must own the device tree lock of the parent's enumeration lock
Arguments:
DeviceNode - Device node to remove
Return Value:
--*/
{
PDEVICE_NODE *Node;
// Ulink the pointer to this device node. (If this is the
// first entry, unlink it from the parents child pointer, else
// remove it from the sibling list)
Node = &DeviceNode->Parent->Child;
while (*Node != DeviceNode) {
Node = &(*Node)->Sibling;
}
*Node = DeviceNode->Sibling;
if (DeviceNode->Parent->Child == NULL) {
DeviceNode->Parent->LastChild = NULL;
} else {
while (*Node) {
Node = &(*Node)->Sibling;
}
DeviceNode->Parent->LastChild = CONTAINING_RECORD(Node, DEVICE_NODE, Sibling);
}
// Orphan any outstanding device change notifications on these nodes.
IopOrphanNotification(DeviceNode);
// No longer linked
DeviceNode->Parent = NULL;
DeviceNode->Child = NULL;
DeviceNode->Sibling = NULL;
DeviceNode->LastChild = NULL;
}
#if DBG
VOID
IopCheckForTargetDevice (
IN PDEVICE_OBJECT TargetDevice,
IN PDEVICE_OBJECT DeviceObject
)
{
if (!TargetDevice || !DeviceObject) {
return ;
}
ASSERT (DeviceObject != TargetDevice);
while (DeviceObject->AttachedDevice) {
DeviceObject = DeviceObject->AttachedDevice;
ASSERT (DeviceObject != TargetDevice);
}
}
VOID
IopCheckDeviceNodeTree (
IN PDEVICE_OBJECT TargetDevice OPTIONAL,
IN PDEVICE_NODE TargetNode OPTIONAL
)
// scan the device node tree and make sure this TargetDevice and TargetNode
// are not in the tree
{
KIRQL OldIrql;
PDEVICE_NODE Node;
return ; // bugbug: not tested
IopAcquireDeviceTreeLock();
ExAcquireSpinLock (&IopDatabaseLock, &OldIrql);
// Find left most node
Node = IopRootDeviceNode;
while (Node->Child) {
Node = Node->Child;
}
// Run the entire tree
while (Node != IopRootDeviceNode) {
// Verify this isn't the target node
ASSERT (Node != TargetNode);
// Verify target device isn't on the node somehow
IopCheckForTargetDevice (TargetDevice, Node->PhysicalDeviceObject);
IopCheckForTargetDevice (TargetDevice, Node->DuplicatePDO);
// Next node
if (Node->Sibling) {
Node = Node->Sibling;
while (Node->Child) {
Node = Node->Child;
}
} else {
Node = Node->Parent;
}
}
ExReleaseSpinLock (&IopDatabaseLock, OldIrql);
IopReleaseDeviceTreeLock ();
}
#endif