mirror of
https://github.com/reactos/reactos.git
synced 2024-08-19 18:02:55 +00:00
![Vadim Galyant](/assets/img/avatar_default.png)
Useful functions for debugging IO and PNP managers: PipDumpDeviceNodes() - displays information about a node(s) in the device tree; PipDumpResourceRequirementsList() - displays information about a Io List; PipDumpCmResourceList() - displays information about a Cm List The tree list of devices (DEVICE_NODE structures) is perhaps the main one in the PnP manager. They also store information about the hardware resources required and assigned to devices. These functions can help with debugging. For example, you can call PipDumpDeviceNodes() before and after device enumeration and compare the resulting information. For PipDumpDeviceNodes() it is possible to optionally output: - allocated resources and boot configuration resources - resources required - translated resources It is possible to displays both a single node and the entire tree. Optionally, you can display child nodes. The information output format for resource lists is maximally compressed, since often the only debugging port is a monitor. The DebugLevel parameter allows dumping in two modes: 0 - unconditional; 1 - if NDEBUG is not defined in "debug.c".
536 lines
16 KiB
C
536 lines
16 KiB
C
/*
|
|
* PROJECT: ReactOS Kernel
|
|
* COPYRIGHT: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
|
|
* FILE: ntoskrnl/io/debug.c
|
|
* PURPOSE: Useful functions for debugging IO and PNP managers
|
|
* PROGRAMMERS: Copyright 2020 Vadim Galyant <vgal@rambler.ru>
|
|
*/
|
|
|
|
#include <ntoskrnl.h>
|
|
#include "pnpio.h"
|
|
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
/* GLOBALS ********************************************************************/
|
|
|
|
extern PDEVICE_NODE IopRootDeviceNode;
|
|
|
|
/* FUNCTIONS ******************************************************************/
|
|
|
|
/* CmResource */
|
|
|
|
/* PipDumpCmResourceDescriptor() displays information about a Cm Descriptor
|
|
|
|
DebugLevel: 0 - always dump
|
|
1 - dump if not defined NDEBUG
|
|
*/
|
|
VOID
|
|
NTAPI
|
|
PipDumpCmResourceDescriptor(
|
|
_In_ PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor,
|
|
_In_ ULONG DebugLevel)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
if (DebugLevel != 0)
|
|
{
|
|
#ifdef NDEBUG
|
|
return;
|
|
#endif
|
|
}
|
|
|
|
if (Descriptor == NULL)
|
|
{
|
|
DPRINT1("Dump CmDescriptor: Descriptor == NULL\n");
|
|
return;
|
|
}
|
|
|
|
switch (Descriptor->Type)
|
|
{
|
|
case CmResourceTypePort:
|
|
DPRINT1("[%p:%X:%X] IO: Start %X:%X, Len %X\n", Descriptor, Descriptor->ShareDisposition, Descriptor->Flags, Descriptor->u.Port.Start.HighPart, Descriptor->u.Port.Start.LowPart, Descriptor->u.Port.Length);
|
|
break;
|
|
|
|
case CmResourceTypeInterrupt:
|
|
DPRINT1("[%p:%X:%X] INT: Lev %X Vec %X Aff %IX\n", Descriptor, Descriptor->ShareDisposition, Descriptor->Flags, Descriptor->u.Interrupt.Level, Descriptor->u.Interrupt.Vector, Descriptor->u.Interrupt.Affinity);
|
|
break;
|
|
|
|
case CmResourceTypeMemory:
|
|
DPRINT1("[%p:%X:%X] MEM: %X:%X Len %X\n", Descriptor, Descriptor->ShareDisposition, Descriptor->Flags, Descriptor->u.Memory.Start.HighPart, Descriptor->u.Memory.Start.LowPart, Descriptor->u.Memory.Length);
|
|
break;
|
|
|
|
case CmResourceTypeDma:
|
|
DPRINT1("[%p:%X:%X] DMA: Channel %X Port %X\n", Descriptor, Descriptor->ShareDisposition, Descriptor->Flags, Descriptor->u.Dma.Channel, Descriptor->u.Dma.Port);
|
|
break;
|
|
|
|
case CmResourceTypeDeviceSpecific:
|
|
DPRINT1("[%p:%X:%X] DAT: DataSize %X\n", Descriptor, Descriptor->ShareDisposition, Descriptor->Flags, Descriptor->u.DeviceSpecificData.DataSize);
|
|
break;
|
|
|
|
case CmResourceTypeBusNumber:
|
|
DPRINT1("[%p:%X:%X] BUS: Start %X Len %X Reserv %X\n", Descriptor, Descriptor->ShareDisposition, Descriptor->Flags, Descriptor->u.BusNumber.Start, Descriptor->u.BusNumber.Length, Descriptor->u.BusNumber.Reserved);
|
|
break;
|
|
|
|
case CmResourceTypeDevicePrivate:
|
|
DPRINT1("[%p:%X:%X] PVT: D[0] %X D[1] %X D[2] %X\n", Descriptor, Descriptor->ShareDisposition, Descriptor->Flags, Descriptor->u.DevicePrivate.Data[0], Descriptor->u.DevicePrivate.Data[1], Descriptor->u.DevicePrivate.Data[2]);
|
|
break;
|
|
|
|
default:
|
|
DPRINT1("[%p] Unknown type %X\n", Descriptor, Descriptor->Type);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* PipGetNextCmPartialDescriptor() return poiner to next a Cm Descriptor */
|
|
|
|
PCM_PARTIAL_RESOURCE_DESCRIPTOR
|
|
NTAPI
|
|
PipGetNextCmPartialDescriptor(
|
|
_In_ PCM_PARTIAL_RESOURCE_DESCRIPTOR CmDescriptor)
|
|
{
|
|
PCM_PARTIAL_RESOURCE_DESCRIPTOR NextDescriptor;
|
|
|
|
/* Assume the descriptors are the fixed size ones */
|
|
NextDescriptor = CmDescriptor + 1;
|
|
|
|
/* But check if this is actually a variable-sized descriptor */
|
|
if (CmDescriptor->Type == CmResourceTypeDeviceSpecific)
|
|
{
|
|
/* Add the size of the variable section as well */
|
|
NextDescriptor = (PVOID)((ULONG_PTR)NextDescriptor +
|
|
CmDescriptor->u.DeviceSpecificData.DataSize);
|
|
}
|
|
|
|
/* Now the correct pointer has been computed, return it */
|
|
return NextDescriptor;
|
|
}
|
|
|
|
/* PipDumpCmResourceList() displays information about a Cm List
|
|
|
|
DebugLevel: 0 - always dump
|
|
1 - dump if not defined NDEBUG
|
|
*/
|
|
VOID
|
|
NTAPI
|
|
PipDumpCmResourceList(
|
|
_In_ PCM_RESOURCE_LIST CmResource,
|
|
_In_ ULONG DebugLevel)
|
|
{
|
|
PCM_FULL_RESOURCE_DESCRIPTOR FullList;
|
|
PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor;
|
|
ULONG ix;
|
|
ULONG jx;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (DebugLevel != 0)
|
|
{
|
|
#ifdef NDEBUG
|
|
return;
|
|
#endif
|
|
}
|
|
|
|
DPRINT1("Dump CmList: CmResource %p\n", CmResource);
|
|
|
|
if (CmResource == NULL)
|
|
{
|
|
DPRINT1("PipDumpCmResourceList: CmResource == NULL\n");
|
|
return;
|
|
}
|
|
|
|
if (CmResource->Count == 0)
|
|
{
|
|
DPRINT1("PipDumpCmResourceList: CmResource->Count == 0\n");
|
|
return;
|
|
}
|
|
|
|
DPRINT1("FullList Count %x\n", CmResource->Count);
|
|
|
|
FullList = &CmResource->List[0];
|
|
|
|
for (ix = 0; ix < CmResource->Count; ix++)
|
|
{
|
|
DPRINT1("List #%X Iface %X Bus #%X Ver.%X Rev.%X Count %X\n",
|
|
ix,
|
|
FullList->InterfaceType,
|
|
FullList->BusNumber,
|
|
FullList->PartialResourceList.Version,
|
|
FullList->PartialResourceList.Revision,
|
|
FullList->PartialResourceList.Count);
|
|
|
|
Descriptor = FullList->PartialResourceList.PartialDescriptors;
|
|
|
|
for (jx = 0; jx < FullList->PartialResourceList.Count; jx++)
|
|
{
|
|
PipDumpCmResourceDescriptor(Descriptor, DebugLevel);
|
|
Descriptor = PipGetNextCmPartialDescriptor(Descriptor);
|
|
}
|
|
|
|
FullList = (PCM_FULL_RESOURCE_DESCRIPTOR)Descriptor;
|
|
}
|
|
}
|
|
|
|
/* IoResource */
|
|
|
|
/* PipDumpIoResourceDescriptor() displays information about a Io Descriptor
|
|
|
|
DebugLevel: 0 - always dump
|
|
1 - dump if not defined NDEBUG
|
|
*/
|
|
VOID
|
|
NTAPI
|
|
PipDumpIoResourceDescriptor(
|
|
_In_ PIO_RESOURCE_DESCRIPTOR Descriptor,
|
|
_In_ ULONG DebugLevel)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
if (DebugLevel != 0)
|
|
{
|
|
#ifdef NDEBUG
|
|
return;
|
|
#endif
|
|
}
|
|
|
|
if (Descriptor == NULL)
|
|
{
|
|
DPRINT1("DumpResourceDescriptor: Descriptor == 0\n");
|
|
return;
|
|
}
|
|
|
|
switch (Descriptor->Type)
|
|
{
|
|
case CmResourceTypeNull:
|
|
DPRINT1("[%p:%X:%X] O: Len %X Align %X Min %I64X, Max %I64X\n", Descriptor, Descriptor->Option, Descriptor->ShareDisposition, Descriptor->u.Generic.Length, Descriptor->u.Generic.Alignment, Descriptor->u.Generic.MinimumAddress.QuadPart, Descriptor->u.Generic.MaximumAddress.QuadPart);
|
|
break;
|
|
|
|
case CmResourceTypePort:
|
|
DPRINT1("[%p:%X:%X] IO: Min %X:%X, Max %X:%X, Align %X Len %X\n", Descriptor, Descriptor->Option, Descriptor->ShareDisposition, Descriptor->u.Port.MinimumAddress.HighPart, Descriptor->u.Port.MinimumAddress.LowPart, Descriptor->u.Port.MaximumAddress.HighPart, Descriptor->u.Port.MaximumAddress.LowPart, Descriptor->u.Port.Alignment, Descriptor->u.Port.Length);
|
|
break;
|
|
|
|
case CmResourceTypeInterrupt:
|
|
DPRINT1("[%p:%X:%X] INT: Min %X Max %X\n", Descriptor, Descriptor->Option, Descriptor->ShareDisposition, Descriptor->u.Interrupt.MinimumVector, Descriptor->u.Interrupt.MaximumVector);
|
|
break;
|
|
|
|
case CmResourceTypeMemory:
|
|
DPRINT1("[%p:%X:%X] MEM: Min %X:%X, Max %X:%X, Align %X Len %X\n", Descriptor, Descriptor->Option, Descriptor->ShareDisposition, Descriptor->u.Memory.MinimumAddress.HighPart, Descriptor->u.Memory.MinimumAddress.LowPart, Descriptor->u.Memory.MaximumAddress.HighPart, Descriptor->u.Memory.MaximumAddress.LowPart, Descriptor->u.Memory.Alignment, Descriptor->u.Memory.Length);
|
|
break;
|
|
|
|
case CmResourceTypeDma:
|
|
DPRINT1("[%p:%X:%X] DMA: Min %X Max %X\n", Descriptor, Descriptor->Option, Descriptor->ShareDisposition, Descriptor->u.Dma.MinimumChannel, Descriptor->u.Dma.MaximumChannel);
|
|
break;
|
|
|
|
case CmResourceTypeBusNumber:
|
|
DPRINT1("[%p:%X:%X] BUS: Min %X Max %X Len %X\n", Descriptor, Descriptor->Option, Descriptor->ShareDisposition, Descriptor->u.BusNumber.MinBusNumber, Descriptor->u.BusNumber.MaxBusNumber, Descriptor->u.BusNumber.Length);
|
|
break;
|
|
|
|
case CmResourceTypeConfigData:
|
|
DPRINT1("[%p:%X:%X] CFG: Priority %X\n", Descriptor, Descriptor->Option, Descriptor->ShareDisposition, Descriptor->u.ConfigData.Priority);
|
|
break;
|
|
|
|
case CmResourceTypeDevicePrivate:
|
|
DPRINT1("[%p:%X:%X] DAT: %X %X %X\n", Descriptor, Descriptor->Option, Descriptor->ShareDisposition, Descriptor->u.DevicePrivate.Data[0], Descriptor->u.DevicePrivate.Data[1], Descriptor->u.DevicePrivate.Data[2]);
|
|
break;
|
|
|
|
default:
|
|
DPRINT1("[%p:%X:%X]. Unknown type %X\n", Descriptor, Descriptor->Option, Descriptor->ShareDisposition, Descriptor->Type);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* PipDumpResourceRequirementsList() displays information about a Io List
|
|
|
|
DebugLevel: 0 - always dump
|
|
1 - dump if not defined NDEBUG
|
|
*/
|
|
VOID
|
|
NTAPI
|
|
PipDumpResourceRequirementsList(
|
|
_In_ PIO_RESOURCE_REQUIREMENTS_LIST IoResource,
|
|
_In_ ULONG DebugLevel)
|
|
{
|
|
PIO_RESOURCE_LIST AltList;
|
|
PIO_RESOURCE_DESCRIPTOR Descriptor;
|
|
ULONG ix;
|
|
ULONG jx;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (DebugLevel != 0)
|
|
{
|
|
#ifdef NDEBUG
|
|
return;
|
|
#endif
|
|
}
|
|
|
|
if (IoResource == NULL)
|
|
{
|
|
DPRINT1("PipDumpResourceRequirementsList: IoResource == 0\n");
|
|
return;
|
|
}
|
|
|
|
DPRINT1("Dump RequirementsList: IoResource %p\n", IoResource);
|
|
DPRINT1("Interface %X Bus %X Slot %X AlternativeLists %X\n",
|
|
IoResource->InterfaceType,
|
|
IoResource->BusNumber,
|
|
IoResource->SlotNumber,
|
|
IoResource->AlternativeLists);
|
|
|
|
AltList = &IoResource->List[0];
|
|
|
|
if (IoResource->AlternativeLists < 1)
|
|
{
|
|
DPRINT1("PipDumpResourceRequirementsList: AlternativeLists < 1\n");
|
|
return;
|
|
}
|
|
|
|
for (ix = 0; ix < IoResource->AlternativeLists; ix++)
|
|
{
|
|
DPRINT1("AltList %p, AltList->Count %X\n", AltList, AltList->Count);
|
|
|
|
for (jx = 0; jx < AltList->Count; jx++)
|
|
{
|
|
Descriptor = &AltList->Descriptors[jx];
|
|
PipDumpIoResourceDescriptor(Descriptor, DebugLevel);
|
|
}
|
|
|
|
AltList = (PIO_RESOURCE_LIST)(AltList->Descriptors + AltList->Count);
|
|
DPRINT1("End Descriptors %p\n", AltList);
|
|
}
|
|
}
|
|
|
|
/* Gets the name of the device node state. */
|
|
|
|
PWSTR
|
|
NTAPI
|
|
PipGetDeviceNodeStateName(
|
|
_In_ PNP_DEVNODE_STATE State)
|
|
{
|
|
switch (State)
|
|
{
|
|
case DeviceNodeUnspecified:
|
|
return L"DeviceNodeUnspecified";
|
|
|
|
case DeviceNodeUninitialized:
|
|
return L"DeviceNodeUninitialized";
|
|
|
|
case DeviceNodeInitialized:
|
|
return L"DeviceNodeInitialized";
|
|
|
|
case DeviceNodeDriversAdded:
|
|
return L"DeviceNodeDriversAdded";
|
|
|
|
case DeviceNodeResourcesAssigned:
|
|
return L"DeviceNodeResourcesAssigned";
|
|
|
|
case DeviceNodeStartPending:
|
|
return L"DeviceNodeStartPending";
|
|
|
|
case DeviceNodeStartCompletion:
|
|
return L"DeviceNodeStartCompletion";
|
|
|
|
case DeviceNodeStartPostWork:
|
|
return L"DeviceNodeStartPostWork";
|
|
|
|
case DeviceNodeStarted:
|
|
return L"DeviceNodeStarted";
|
|
|
|
case DeviceNodeQueryStopped:
|
|
return L"DeviceNodeQueryStopped";
|
|
|
|
case DeviceNodeStopped:
|
|
return L"DeviceNodeStopped";
|
|
|
|
case DeviceNodeRestartCompletion:
|
|
return L"DeviceNodeRestartCompletion";
|
|
|
|
case DeviceNodeEnumeratePending:
|
|
return L"DeviceNodeEnumeratePending";
|
|
|
|
case DeviceNodeEnumerateCompletion:
|
|
return L"DeviceNodeEnumerateCompletion";
|
|
|
|
case DeviceNodeAwaitingQueuedDeletion:
|
|
return L"DeviceNodeAwaitingQueuedDeletion";
|
|
|
|
case DeviceNodeAwaitingQueuedRemoval:
|
|
return L"DeviceNodeAwaitingQueuedRemoval";
|
|
|
|
case DeviceNodeQueryRemoved:
|
|
return L"DeviceNodeQueryRemoved";
|
|
|
|
case DeviceNodeRemovePendingCloses:
|
|
return L"DeviceNodeRemovePendingCloses";
|
|
|
|
case DeviceNodeRemoved:
|
|
return L"DeviceNodeRemoved";
|
|
|
|
case DeviceNodeDeletePendingCloses:
|
|
return L"DeviceNodeDeletePendingCloses";
|
|
|
|
case DeviceNodeDeleted:
|
|
return L"DeviceNodeDeleted";
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (State != MaxDeviceNodeState)
|
|
{
|
|
DPRINT1("PipGetDeviceNodeStateName: Unknown State %X\n", State);
|
|
}
|
|
|
|
return L"";
|
|
}
|
|
|
|
/* Dump list arbiters for the device node */
|
|
|
|
VOID
|
|
NTAPI
|
|
PipDumpArbiters(
|
|
_In_ PDEVICE_NODE DeviceNode,
|
|
_In_ ULONG NodeLevel)
|
|
{
|
|
DPRINT("Level %X DevNode %p for PDO %p\n", NodeLevel, DeviceNode, DeviceNode->PhysicalDeviceObject);
|
|
|
|
/* Definitions needed for arbiter */
|
|
UNIMPLEMENTED;
|
|
}
|
|
|
|
/* PipDumpDeviceNode() displays information about a device node
|
|
|
|
Flags is bitmask parametr:
|
|
1 - traversal of all children nodes
|
|
2 - dump allocated resources and boot configuration resources
|
|
4 - dump resources required
|
|
8 - dump translated resources
|
|
|
|
DebugLevel: 0 - always dump
|
|
1 - dump if not defined NDEBUG
|
|
*/
|
|
VOID
|
|
NTAPI
|
|
PipDumpDeviceNode(
|
|
_In_ PDEVICE_NODE DeviceNode,
|
|
_In_ ULONG NodeLevel,
|
|
_In_ ULONG Flags,
|
|
_In_ ULONG DebugLevel)
|
|
{
|
|
PDEVICE_NODE ChildDeviceNode;
|
|
|
|
if (DebugLevel != 0)
|
|
{
|
|
#ifdef NDEBUG
|
|
return;
|
|
#endif
|
|
}
|
|
|
|
DPRINT1("* Level %X DevNode %p for PDO %p\n", NodeLevel, DeviceNode, DeviceNode->PhysicalDeviceObject);
|
|
DPRINT1("Instance %wZ\n", &DeviceNode->InstancePath);
|
|
if (DeviceNode->ServiceName.Length)
|
|
DPRINT1("Service %wZ\n", &DeviceNode->ServiceName);
|
|
#if 0
|
|
/* It is not used yet */
|
|
DPRINT1("State %X %S\n", DeviceNode->State, PipGetDeviceNodeStateName(DeviceNode->State));
|
|
DPRINT1("Prev State %X %S\n", DeviceNode->PreviousState, PipGetDeviceNodeStateName(DeviceNode->PreviousState));
|
|
#endif
|
|
if (DeviceNode->Problem)
|
|
DPRINT1("Problem %X\n", DeviceNode->Problem);
|
|
#if 0
|
|
/* It is not implemeted yet */
|
|
PipDumpArbiters(DeviceNode, NodeLevel);
|
|
#endif
|
|
|
|
/* Allocated resources and Boot configuration (reported by IRP_MN_QUERY_RESOURCES)*/
|
|
if (Flags & PIP_DUMP_FL_RES_ALLOCATED)
|
|
{
|
|
if (DeviceNode->ResourceList)
|
|
{
|
|
DPRINT1("---------- ResourceList ----------\n");
|
|
PipDumpCmResourceList(DeviceNode->ResourceList, DebugLevel);
|
|
}
|
|
|
|
if (DeviceNode->BootResources)
|
|
{
|
|
DPRINT1("---------- BootResources ----------\n");
|
|
PipDumpCmResourceList(DeviceNode->BootResources, DebugLevel);
|
|
}
|
|
}
|
|
|
|
/* Resources required (reported by IRP_MN_FILTER_RESOURCE_REQUIREMENTS) */
|
|
if (Flags & PIP_DUMP_FL_RES_REQUIREMENTS)
|
|
{
|
|
if (DeviceNode->ResourceRequirements)
|
|
{
|
|
DPRINT1("---------- ResourceRequirements ----------\n");
|
|
PipDumpResourceRequirementsList(DeviceNode->ResourceRequirements, DebugLevel);
|
|
}
|
|
}
|
|
|
|
/* Translated resources (AllocatedResourcesTranslated) */
|
|
if (Flags & PIP_DUMP_FL_RES_TRANSLATED)
|
|
{
|
|
if (DeviceNode->ResourceListTranslated)
|
|
{
|
|
DPRINT1("---------- ResourceListTranslated ----------\n");
|
|
PipDumpCmResourceList(DeviceNode->ResourceListTranslated, DebugLevel);
|
|
}
|
|
}
|
|
|
|
/* Traversal of all children nodes */
|
|
if (Flags & PIP_DUMP_FL_ALL_NODES)
|
|
{
|
|
for (ChildDeviceNode = DeviceNode->Child;
|
|
ChildDeviceNode != NULL;
|
|
ChildDeviceNode = ChildDeviceNode->Sibling)
|
|
{
|
|
/* Recursive call */
|
|
PipDumpDeviceNode(ChildDeviceNode, (NodeLevel + 1), Flags, DebugLevel);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* PipDumpDeviceNodes() displays information about a node(s) in the device tree
|
|
|
|
If DeviceNode is NULL, then the dump starts at the beginning of the device tree.
|
|
|
|
Flags is bitmask parametr:
|
|
1 - traversal of all children nodes
|
|
2 - dump allocated resources and boot configuration resources
|
|
4 - dump resources required
|
|
8 - dump translated resources
|
|
|
|
DebugLevel: 0 - always dump
|
|
1 - dump if not defined NDEBUG
|
|
|
|
See also: Windows DDK -> General Driver Information ->
|
|
Kernel-Mode Driver Architecture -> Design Guide -> Plug and Play ->
|
|
Introduction to Plug and Play -> Hardware Resources
|
|
*/
|
|
VOID
|
|
NTAPI
|
|
PipDumpDeviceNodes(
|
|
_In_ PDEVICE_NODE DeviceNode,
|
|
_In_ ULONG Flags,
|
|
_In_ ULONG DebugLevel)
|
|
{
|
|
if (DebugLevel != 0)
|
|
{
|
|
#ifdef NDEBUG
|
|
return;
|
|
#endif
|
|
}
|
|
|
|
DPRINT1("PipDumpDeviceNodes: DeviceNode %X, Flags %X Level %X\n", DeviceNode, Flags, DebugLevel);
|
|
|
|
if (DeviceNode == NULL)
|
|
{
|
|
DeviceNode = IopRootDeviceNode;
|
|
}
|
|
|
|
PipDumpDeviceNode(DeviceNode, 0, Flags, DebugLevel);
|
|
}
|
|
|
|
/* EOF */
|