[NTOS:PNP] Implement NT5.2-like DEVICE_NODE state management
authorVictor Perevertkin <victor.perevertkin@reactos.org>
Thu, 10 Dec 2020 00:19:31 +0000 (03:19 +0300)
committerVictor Perevertkin <victor.perevertkin@reactos.org>
Wed, 27 Jan 2021 02:15:15 +0000 (05:15 +0300)
- Use DeviceNode->State field and its values, instead of
  DeviceNode->Flags for tracking current node state
- Change DNF_* flags to the ones compatible with Windows XP+
- Simplify state changes for device nodes and encapsulate all the logic
  inside the PiDevNodeStateMachine routine. This makes the ground for
  future improvements in the device removal sequence and
  resource management
- Now values inside DeviceNode->State and ->Flags are compatible with
  the windbg !devnode macro and can be tracked using it
- BUGFIX: fixed cases where IRP_MN_START_DEVICE or
  IRP_MN_QUERY_DEVICE_RELATIONS may be sent to a device after a
  IRP_MN_REMOVE_DEVICE

CORE-7826

12 files changed:
ntoskrnl/include/internal/io.h
ntoskrnl/io/iomgr/iomgr.c
ntoskrnl/io/pnpmgr/devaction.c
ntoskrnl/io/pnpmgr/devnode.c
ntoskrnl/io/pnpmgr/plugplay.c
ntoskrnl/io/pnpmgr/pnpinit.c
ntoskrnl/io/pnpmgr/pnpirp.c [new file with mode: 0644]
ntoskrnl/io/pnpmgr/pnpmgr.c
ntoskrnl/io/pnpmgr/pnpreport.c
ntoskrnl/io/pnpmgr/pnpres.c
ntoskrnl/ntos.cmake
sdk/include/ndk/iotypes.h

index 6500537..3819bea 100644 (file)
@@ -567,11 +567,6 @@ IopDetectResourceConflict(
 //
 // PNP Routines
 //
-NTSTATUS
-PiCallDriverAddDevice(
-    _In_ PDEVICE_NODE DeviceNode,
-    _In_ BOOLEAN LoadDrivers);
-
 NTSTATUS
 NTAPI
 IopInitializePlugPlayServices(
@@ -609,6 +604,11 @@ PiInsertDevNode(
     _In_ PDEVICE_NODE DeviceNode,
     _In_ PDEVICE_NODE ParentNode);
 
+PNP_DEVNODE_STATE
+PiSetDevNodeState(
+    _In_ PDEVICE_NODE DeviceNode,
+    _In_ PNP_DEVNODE_STATE NewState);
+
 VOID
 PiSetDevNodeProblem(
     _In_ PDEVICE_NODE DeviceNode,
@@ -629,7 +629,6 @@ IopQueryDeviceCapabilities(PDEVICE_NODE DeviceNode,
                            PDEVICE_CAPABILITIES DeviceCaps);
 
 NTSTATUS
-NTAPI
 IopSynchronousCall(
     IN PDEVICE_OBJECT DeviceObject,
     IN PIO_STACK_LOCATION IoStackLocation,
@@ -651,18 +650,6 @@ IopGetDeviceNode(
     IN PDEVICE_OBJECT DeviceObject
 );
 
-NTSTATUS
-IopActionConfigureChildServices(
-    IN PDEVICE_NODE DeviceNode,
-    IN PVOID Context
-);
-
-NTSTATUS
-IopActionInitChildServices(
-    IN PDEVICE_NODE DeviceNode,
-    IN PVOID Context
-);
-
 NTSTATUS
 IoCreateDriverList(
     VOID
@@ -682,10 +669,6 @@ IopQueueTargetDeviceEvent(
     PUNICODE_STRING DeviceIds
 );
 
-NTSTATUS
-IopInitializePnpServices(
-    IN PDEVICE_NODE DeviceNode);
-
 NTSTATUS
 NTAPI
 IopOpenRegistryKeyEx(
@@ -817,21 +800,6 @@ IopReadyDeviceObjects(
     IN PDRIVER_OBJECT Driver
 );
 
-NTSTATUS
-IopStartDevice(
-    IN PDEVICE_NODE DeviceNode
-);
-
-NTSTATUS
-IopStopDevice(
-    IN PDEVICE_NODE DeviceNode
-);
-
-NTSTATUS
-IopRemoveDevice(
-    IN PDEVICE_NODE DeviceNode
-);
-
 PVPB
 NTAPI
 IopCheckVpbMounted(
@@ -1387,6 +1355,35 @@ PiNotifyTargetDeviceChange(
     _In_ PDEVICE_OBJECT DeviceObject,
     _In_opt_ PTARGET_DEVICE_CUSTOM_NOTIFICATION CustomNotification);
 
+//
+// PnP IRPs
+//
+NTSTATUS
+PiIrpStartDevice(
+    _In_ PDEVICE_NODE DeviceNode);
+
+NTSTATUS
+PiIrpStopDevice(
+    _In_ PDEVICE_NODE DeviceNode);
+
+NTSTATUS
+PiIrpQueryStopDevice(
+    _In_ PDEVICE_NODE DeviceNode);
+
+NTSTATUS
+PiIrpCancelStopDevice(
+    _In_ PDEVICE_NODE DeviceNode);
+
+NTSTATUS
+PiIrpQueryDeviceRelations(
+    _In_ PDEVICE_NODE DeviceNode,
+    _In_ DEVICE_RELATION_TYPE Type);
+
+NTSTATUS
+PiIrpQueryPnPDeviceState(
+    _In_ PDEVICE_NODE DeviceNode,
+    _Out_ PPNP_DEVICE_STATE DeviceState);
+
 //
 // Global I/O Data
 //
index 5d272cb..06df1ce 100644 (file)
@@ -596,9 +596,6 @@ IoInitSystem(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
     KdInitSystem(3, LoaderBlock);
 #endif
 
-    /* Load services for devices found by PnP manager */
-    IopInitializePnpServices(IopRootDeviceNode);
-
     /* Load system start drivers */
     IopInitializeSystemDrivers();
     PnpSystemInit = TRUE;
index 8b4db9d..166a1d6 100644 (file)
@@ -113,6 +113,12 @@ static
 NTSTATUS
 IopPrepareDeviceForRemoval(PDEVICE_OBJECT DeviceObject, BOOLEAN Force);
 
+static
+NTSTATUS
+IopSetServiceEnumData(
+    _In_ PDEVICE_NODE DeviceNode,
+    _In_ HANDLE InstanceHandle);
+
 static
 BOOLEAN
 IopValidateID(
@@ -256,14 +262,9 @@ IopCreateDeviceInstancePath(
     if (DeviceCapabilities.HardwareDisabled)
     {
         /* FIXME: Cleanup device */
-        DeviceNode->Flags |= DNF_DISABLED;
         RtlFreeUnicodeString(&DeviceId);
         return STATUS_PLUGPLAY_NO_DEVICE;
     }
-    else
-    {
-        DeviceNode->Flags &= ~DNF_DISABLED;
-    }
 
     if (!DeviceCapabilities.UniqueID)
     {
@@ -584,6 +585,7 @@ PiAttachFilterDrivers(
  * @param[in]  LoadDrivers  Whether to load drivers if they are not loaded yet
  *                          (used when storage subsystem is not yet initialized)
  */
+static
 NTSTATUS
 PiCallDriverAddDevice(
     _In_ PDEVICE_NODE DeviceNode,
@@ -636,7 +638,7 @@ PiCallDriverAddDevice(
         }
         else
         {
-            // open the CCS\Constol\Class\<ClassGUID> key
+            // open the CCS\Control\Class\<ClassGUID> key
             Status = IopOpenRegistryKeyEx(&ClassKey, ccsControlHandle, &classGUID, KEY_READ);
             ZwClose(ccsControlHandle);
             if (!NT_SUCCESS(Status))
@@ -794,15 +796,13 @@ PiCallDriverAddDevice(
                 }
 
                 ObDereferenceObject(fdo);
-
-                IopDeviceNodeSetFlag(DeviceNode, DNF_ADDED);
+                PiSetDevNodeState(DeviceNode, DeviceNodeDriversAdded);
             }
             else
             {
                 // lower filters (if already started) will be removed upon this request
                 PiSetDevNodeProblem(DeviceNode, CM_PROB_FAILED_ADD);
-                IopDeviceNodeSetFlag(DeviceNode, DNF_DISABLED);
-                IopRemoveDevice(DeviceNode);
+                PiSetDevNodeState(DeviceNode, DeviceNodeAwaitingQueuedRemoval);
                 break;
             }
         }
@@ -837,11 +837,6 @@ Cleanup:
         ZwClose(ClassKey);
     }
 
-    if (DeviceNode->Flags & DNF_ADDED)
-    {
-        IopStartDevice(DeviceNode);
-    }
-
     return Status;
 }
 
@@ -1078,30 +1073,14 @@ IopQueryCompatibleIds(PDEVICE_NODE DeviceNode,
     return Status;
 }
 
-/*
- * IopActionInterrogateDeviceStack
- *
- * Retrieve information for all (direct) child nodes of a parent node.
- *
- * Parameters
- *    DeviceNode
- *       Pointer to device node.
- *    Context
- *       Pointer to parent node to retrieve child node information for.
- *
- * Remarks
- *    Any errors that occur are logged instead so that all child services have a chance
- *    of being interrogated.
- */
-
+static
 NTSTATUS
-IopActionInterrogateDeviceStack(PDEVICE_NODE DeviceNode,
-                                PVOID Context)
+PiInitializeDevNode(
+    _In_ PDEVICE_NODE DeviceNode)
 {
     IO_STATUS_BLOCK IoStatusBlock;
     PWSTR DeviceDescription;
     PWSTR LocationInformation;
-    PDEVICE_NODE ParentDeviceNode;
     IO_STACK_LOCATION Stack;
     NTSTATUS Status;
     ULONG RequiredLength;
@@ -1111,38 +1090,9 @@ IopActionInterrogateDeviceStack(PDEVICE_NODE DeviceNode,
     UNICODE_STRING InstancePathU;
     PDEVICE_OBJECT OldDeviceObject;
 
-    DPRINT("IopActionInterrogateDeviceStack(%p, %p)\n", DeviceNode, Context);
+    DPRINT("PiProcessNewDevNode(%p)\n", DeviceNode);
     DPRINT("PDO 0x%p\n", DeviceNode->PhysicalDeviceObject);
 
-    ParentDeviceNode = (PDEVICE_NODE)Context;
-
-    /*
-     * We are called for the parent too, but we don't need to do special
-     * handling for this node
-     */
-    if (DeviceNode == ParentDeviceNode)
-    {
-        DPRINT("Success\n");
-        return STATUS_SUCCESS;
-    }
-
-    /*
-     * Make sure this device node is a direct child of the parent device node
-     * that is given as an argument
-     */
-    if (DeviceNode->Parent != ParentDeviceNode)
-    {
-        DPRINT("Skipping 2+ level child\n");
-        return STATUS_SUCCESS;
-    }
-
-    /* Skip processing if it was already completed before */
-    if (DeviceNode->Flags & DNF_PROCESSED)
-    {
-        /* Nothing to do */
-        return STATUS_SUCCESS;
-    }
-
     /* Get Locale ID */
     Status = ZwQueryDefaultLocale(FALSE, &LocaleId);
     if (!NT_SUCCESS(Status))
@@ -1163,9 +1113,7 @@ IopActionInterrogateDeviceStack(PDEVICE_NODE DeviceNode,
         {
             DPRINT1("IopCreateDeviceInstancePath() failed with status 0x%lx\n", Status);
         }
-
-        /* We have to return success otherwise we abort the traverse operation */
-        return STATUS_SUCCESS;
+        return Status;
     }
 
     /* Verify that this is not a duplicate */
@@ -1205,6 +1153,8 @@ IopActionInterrogateDeviceStack(PDEVICE_NODE DeviceNode,
 
     IopQueryCompatibleIds(DeviceNode, InstanceKey);
 
+    DeviceNode->Flags |= DNF_IDS_QUERIED;
+
     DPRINT("Sending IRP_MN_QUERY_DEVICE_TEXT.DeviceTextDescription to device stack\n");
 
     Stack.Parameters.QueryDeviceText.DeviceTextType = DeviceTextDescription;
@@ -1348,9 +1298,15 @@ IopActionInterrogateDeviceStack(PDEVICE_NODE DeviceNode,
         IopSetDeviceInstanceData(InstanceKey, DeviceNode);
     }
 
+    // Try installing a critical device, so its Service key is populated
+    // then call IopSetServiceEnumData to populate service's Enum key.
+    // That allows us to start devices during an early boot
+    IopInstallCriticalDevice(DeviceNode);
+    IopSetServiceEnumData(DeviceNode, InstanceKey);
+
     ZwClose(InstanceKey);
 
-    IopDeviceNodeSetFlag(DeviceNode, DNF_PROCESSED);
+    PiSetDevNodeState(DeviceNode, DeviceNodeInitialized);
 
     if (!IopDeviceNodeHasFlag(DeviceNode, DNF_LEGACY_DRIVER))
     {
@@ -1362,235 +1318,46 @@ IopActionInterrogateDeviceStack(PDEVICE_NODE DeviceNode,
     return STATUS_SUCCESS;
 }
 
-/*
- * IopActionConfigureChildServices
- *
- * Retrieve configuration for all (direct) child nodes of a parent node.
- *
- * Parameters
- *    DeviceNode
- *       Pointer to device node.
- *    Context
- *       Pointer to parent node to retrieve child node configuration for.
- *
- * Remarks
- *    Any errors that occur are logged instead so that all child services have a chance of beeing
- *    configured.
- */
-
-NTSTATUS
-IopActionConfigureChildServices(PDEVICE_NODE DeviceNode,
-                                PVOID Context)
-{
-    RTL_QUERY_REGISTRY_TABLE QueryTable[3];
-    PDEVICE_NODE ParentDeviceNode;
-    PUNICODE_STRING Service;
-    UNICODE_STRING ClassGUID;
-    NTSTATUS Status;
-    DEVICE_CAPABILITIES DeviceCaps;
-
-    DPRINT("IopActionConfigureChildServices(%p, %p)\n", DeviceNode, Context);
-
-    ParentDeviceNode = (PDEVICE_NODE)Context;
-
-    /*
-     * We are called for the parent too, but we don't need to do special
-     * handling for this node
-     */
-    if (DeviceNode == ParentDeviceNode)
-    {
-        DPRINT("Success\n");
-        return STATUS_SUCCESS;
-    }
-
-    /*
-     * Make sure this device node is a direct child of the parent device node
-     * that is given as an argument
-     */
-
-    if (DeviceNode->Parent != ParentDeviceNode)
-    {
-        DPRINT("Skipping 2+ level child\n");
-        return STATUS_SUCCESS;
-    }
-
-    if (!(DeviceNode->Flags & DNF_PROCESSED))
-    {
-        DPRINT1("Child not ready to be configured\n");
-        return STATUS_SUCCESS;
-    }
-
-    if (!(DeviceNode->Flags & (DNF_DISABLED | DNF_STARTED | DNF_ADDED)))
-    {
-        UNICODE_STRING RegKey;
-
-        /* Install the service for this if it's in the CDDB */
-        IopInstallCriticalDevice(DeviceNode);
-
-        /*
-         * Retrieve configuration from Enum key
-         */
-
-        Service = &DeviceNode->ServiceName;
-
-        RtlZeroMemory(QueryTable, sizeof(QueryTable));
-        RtlInitUnicodeString(Service, NULL);
-        RtlInitUnicodeString(&ClassGUID, NULL);
-
-        QueryTable[0].Name = L"Service";
-        QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
-        QueryTable[0].EntryContext = Service;
-
-        QueryTable[1].Name = L"ClassGUID";
-        QueryTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT;
-        QueryTable[1].EntryContext = &ClassGUID;
-        QueryTable[1].DefaultType = REG_SZ;
-        QueryTable[1].DefaultData = L"";
-        QueryTable[1].DefaultLength = 0;
-
-        RegKey.Length = 0;
-        RegKey.MaximumLength = sizeof(ENUM_ROOT) + sizeof(WCHAR) + DeviceNode->InstancePath.Length;
-        RegKey.Buffer = ExAllocatePoolWithTag(PagedPool,
-                                              RegKey.MaximumLength,
-                                              TAG_IO);
-        if (RegKey.Buffer == NULL)
-        {
-            IopDeviceNodeSetFlag(DeviceNode, DNF_DISABLED);
-            return STATUS_INSUFFICIENT_RESOURCES;
-        }
-
-        RtlAppendUnicodeToString(&RegKey, ENUM_ROOT);
-        RtlAppendUnicodeToString(&RegKey, L"\\");
-        RtlAppendUnicodeStringToString(&RegKey, &DeviceNode->InstancePath);
-
-        Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
-            RegKey.Buffer, QueryTable, NULL, NULL);
-        ExFreePoolWithTag(RegKey.Buffer, TAG_IO);
-
-        if (!NT_SUCCESS(Status))
-        {
-            /* FIXME: Log the error */
-            DPRINT("Could not retrieve configuration for device %wZ (Status 0x%08x)\n",
-                   &DeviceNode->InstancePath, Status);
-            IopDeviceNodeSetFlag(DeviceNode, DNF_DISABLED);
-            return STATUS_SUCCESS;
-        }
-
-        if (Service->Buffer == NULL)
-        {
-            if (NT_SUCCESS(IopQueryDeviceCapabilities(DeviceNode, &DeviceCaps)) &&
-                DeviceCaps.RawDeviceOK)
-            {
-                DPRINT("%wZ is using parent bus driver (%wZ)\n", &DeviceNode->InstancePath, &ParentDeviceNode->ServiceName);
-                RtlInitEmptyUnicodeString(&DeviceNode->ServiceName, NULL, 0);
-            }
-            else if (ClassGUID.Length != 0)
-            {
-                /* Device has a ClassGUID value, but no Service value.
-                 * Suppose it is using the NULL driver, so state the
-                 * device is started */
-                DPRINT("%wZ is using NULL driver\n", &DeviceNode->InstancePath);
-                IopDeviceNodeSetFlag(DeviceNode, DNF_STARTED);
-            }
-            else
-            {
-                DeviceNode->Problem = CM_PROB_FAILED_INSTALL;
-                IopDeviceNodeSetFlag(DeviceNode, DNF_DISABLED);
-            }
-            return STATUS_SUCCESS;
-        }
-
-        DPRINT("Got Service %S\n", Service->Buffer);
-    }
-
-    return STATUS_SUCCESS;
-}
-
-/*
- * IopActionInitChildServices
- *
- * Initialize the service for all (direct) child nodes of a parent node
- *
- * Parameters
- *    DeviceNode
- *       Pointer to device node.
- *    Context
- *       Pointer to parent node to initialize child node services for.
- *
- * Remarks
- *    If the driver image for a service is not loaded and initialized
- *    it is done here too. Any errors that occur are logged instead so
- *    that all child services have a chance of being initialized.
- */
-
-NTSTATUS
-IopActionInitChildServices(PDEVICE_NODE DeviceNode,
-                           PVOID Context)
-{
-    PDEVICE_NODE ParentDeviceNode;
-
-    DPRINT("IopActionInitChildServices(%p, %p)\n", DeviceNode, Context);
-
-    ParentDeviceNode = Context;
-
-    /*
-     * We are called for the parent too, but we don't need to do special
-     * handling for this node
-     */
-    if (DeviceNode == ParentDeviceNode)
-    {
-        DPRINT("Success\n");
-        return STATUS_SUCCESS;
-    }
-
-    /*
-     * We don't want to check for a direct child because
-     * this function is called during boot to reinitialize
-     * devices with drivers that couldn't load yet due to
-     * stage 0 limitations (ie can't load from disk yet).
-     */
-
-    if (!(DeviceNode->Flags & DNF_PROCESSED))
-    {
-        DPRINT1("Child not ready to be added\n");
-        return STATUS_SUCCESS;
-    }
-
-    if (IopDeviceNodeHasFlag(DeviceNode, DNF_STARTED) ||
-        IopDeviceNodeHasFlag(DeviceNode, DNF_ADDED) ||
-        IopDeviceNodeHasFlag(DeviceNode, DNF_DISABLED))
-        return STATUS_SUCCESS;
-
-    PiCallDriverAddDevice(DeviceNode, PnPBootDriversInitialized);
-    return STATUS_SUCCESS;
-}
-
 static
 NTSTATUS
-IopSetServiceEnumData(PDEVICE_NODE DeviceNode)
+IopSetServiceEnumData(
+    _In_ PDEVICE_NODE DeviceNode,
+    _In_ HANDLE InstanceHandle)
 {
     UNICODE_STRING ServicesKeyPath = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\");
     UNICODE_STRING ServiceKeyName;
     UNICODE_STRING EnumKeyName;
     UNICODE_STRING ValueName;
-    PKEY_VALUE_FULL_INFORMATION KeyValueInformation;
+    UNICODE_STRING ServiceName;
+    PKEY_VALUE_FULL_INFORMATION KeyValueInformation, kvInfo2;
     HANDLE ServiceKey = NULL, ServiceEnumKey = NULL;
     ULONG Disposition;
     ULONG Count = 0, NextInstance = 0;
     WCHAR ValueBuffer[6];
     NTSTATUS Status = STATUS_SUCCESS;
 
-    DPRINT("IopSetServiceEnumData(%p)\n", DeviceNode);
-    DPRINT("Instance: %wZ\n", &DeviceNode->InstancePath);
-    DPRINT("Service: %wZ\n", &DeviceNode->ServiceName);
+    // obtain the device node's ServiceName
+    Status = IopGetRegistryValue(InstanceHandle, L"Service", &kvInfo2);
+    if (!NT_SUCCESS(Status))
+    {
+        return Status;
+    }
 
-    if (DeviceNode->ServiceName.Buffer == NULL)
+    if (kvInfo2->Type != REG_SZ || kvInfo2->DataLength <= sizeof(WCHAR))
     {
-        DPRINT1("No service!\n");
-        return STATUS_SUCCESS;
+        ExFreePool(kvInfo2);
+        return STATUS_UNSUCCESSFUL;
     }
 
-    ServiceKeyName.MaximumLength = ServicesKeyPath.Length + DeviceNode->ServiceName.Length + sizeof(UNICODE_NULL);
+    ServiceName.MaximumLength = kvInfo2->DataLength;
+    ServiceName.Length = kvInfo2->DataLength - sizeof(UNICODE_NULL);
+    ServiceName.Buffer = (PVOID)((ULONG_PTR)kvInfo2 + kvInfo2->DataOffset);
+
+    DPRINT("IopSetServiceEnumData(%p)\n", DeviceNode);
+    DPRINT("Instance: %wZ\n", &DeviceNode->InstancePath);
+    DPRINT("Service: %wZ\n", &ServiceName);
+
+    ServiceKeyName.MaximumLength = ServicesKeyPath.Length + ServiceName.Length + sizeof(UNICODE_NULL);
     ServiceKeyName.Length = 0;
     ServiceKeyName.Buffer = ExAllocatePool(PagedPool, ServiceKeyName.MaximumLength);
     if (ServiceKeyName.Buffer == NULL)
@@ -1600,7 +1367,7 @@ IopSetServiceEnumData(PDEVICE_NODE DeviceNode)
     }
 
     RtlAppendUnicodeStringToString(&ServiceKeyName, &ServicesKeyPath);
-    RtlAppendUnicodeStringToString(&ServiceKeyName, &DeviceNode->ServiceName);
+    RtlAppendUnicodeStringToString(&ServiceKeyName, &ServiceName);
 
     DPRINT("ServiceKeyName: %wZ\n", &ServiceKeyName);
 
@@ -1694,6 +1461,10 @@ IopSetServiceEnumData(PDEVICE_NODE DeviceNode)
                                sizeof(NextInstance));
     }
 
+    RtlDuplicateUnicodeString(RTL_DUPLICATE_UNICODE_STRING_ALLOCATE_NULL_STRING,
+                              &ServiceName,
+                              &DeviceNode->ServiceName);
+
 done:
     if (ServiceEnumKey != NULL)
         ZwClose(ServiceEnumKey);
@@ -1702,51 +1473,53 @@ done:
         ZwClose(ServiceKey);
 
     ExFreePool(ServiceKeyName.Buffer);
+    ExFreePool(kvInfo2);
 
     return Status;
 }
 
 static
-VOID
-NTAPI
-IopStartDevice2(IN PDEVICE_OBJECT DeviceObject)
+NTSTATUS
+PiStartDeviceFinal(
+    _In_ PDEVICE_NODE DeviceNode)
 {
-    IO_STACK_LOCATION Stack;
-    PDEVICE_NODE DeviceNode;
-    NTSTATUS Status;
-    PVOID Dummy;
     DEVICE_CAPABILITIES DeviceCapabilities;
+    NTSTATUS Status;
 
-    /* Get the device node */
-    DeviceNode = IopGetDeviceNode(DeviceObject);
-
-    ASSERT(!(DeviceNode->Flags & DNF_DISABLED));
-
-    /* Build the I/O stack location */
-    RtlZeroMemory(&Stack, sizeof(IO_STACK_LOCATION));
-    Stack.MajorFunction = IRP_MJ_PNP;
-    Stack.MinorFunction = IRP_MN_START_DEVICE;
+    if (!(DeviceNode->Flags & DNF_IDS_QUERIED))
+    {
+        // query ids (for reported devices)
+        UNICODE_STRING enumRoot = RTL_CONSTANT_STRING(ENUM_ROOT);
+        HANDLE enumRootHandle, instanceHandle;
 
-    Stack.Parameters.StartDevice.AllocatedResources =
-         DeviceNode->ResourceList;
-    Stack.Parameters.StartDevice.AllocatedResourcesTranslated =
-         DeviceNode->ResourceListTranslated;
+        // open the enumeration root key
+        Status = IopOpenRegistryKeyEx(&enumRootHandle, NULL, &enumRoot, KEY_READ);
+        if (!NT_SUCCESS(Status))
+        {
+            DPRINT1("IopOpenRegistryKeyEx() failed for \"%wZ\" (status %x)\n", &enumRoot, Status);
+            return Status;
+        }
 
-    /* Do the call */
-    Status = IopSynchronousCall(DeviceObject, &Stack, &Dummy);
-    if (!NT_SUCCESS(Status))
-    {
-        /* Send an IRP_MN_REMOVE_DEVICE request */
-        IopRemoveDevice(DeviceNode);
+        // open an instance subkey
+        Status = IopOpenRegistryKeyEx(&instanceHandle, enumRootHandle, &DeviceNode->InstancePath, KEY_READ);
+        ZwClose(enumRootHandle);
+        if (!NT_SUCCESS(Status))
+        {
+            DPRINT1("Failed to open a devnode instance key for \"%wZ\" (status %x)\n",
+                    &DeviceNode->InstancePath, Status);
+            return Status;
+        }
 
-        /* Set the appropriate flag */
-        DeviceNode->Flags |= DNF_START_FAILED;
-        DeviceNode->Problem = CM_PROB_FAILED_START;
+        IopQueryHardwareIds(DeviceNode, instanceHandle);
+        IopQueryCompatibleIds(DeviceNode, instanceHandle);
 
-        DPRINT1("Warning: PnP Start failed (%wZ) [Status: 0x%x]\n", &DeviceNode->InstancePath, Status);
-        return;
+        DeviceNode->Flags |= DNF_IDS_QUERIED;
+        ZwClose(instanceHandle);
     }
 
+    // we're about to start - needs enumeration
+    DeviceNode->Flags |= DNF_REENUMERATE;
+
     DPRINT("Sending IRP_MN_QUERY_CAPABILITIES to device stack (after start)\n");
 
     Status = IopQueryDeviceCapabilities(DeviceNode, &DeviceCapabilities);
@@ -1756,181 +1529,14 @@ IopStartDevice2(IN PDEVICE_OBJECT DeviceObject)
     }
 
     /* Invalidate device state so IRP_MN_QUERY_PNP_DEVICE_STATE is sent */
-    IoInvalidateDeviceState(DeviceObject);
+    IoInvalidateDeviceState(DeviceNode->PhysicalDeviceObject);
 
-    /* Otherwise, mark us as started */
-    DeviceNode->Flags |= DNF_STARTED;
-    DeviceNode->Flags &= ~DNF_STOPPED;
+    DPRINT("Sending GUID_DEVICE_ARRIVAL %wZ\n", &DeviceNode->InstancePath);
+    IopQueueTargetDeviceEvent(&GUID_DEVICE_ARRIVAL, &DeviceNode->InstancePath);
 
-    /* We now need enumeration */
-    DeviceNode->Flags |= DNF_NEED_ENUMERATION_ONLY;
-}
+    PiSetDevNodeState(DeviceNode, DeviceNodeStarted);
 
-static
-NTSTATUS
-NTAPI
-IopStartAndEnumerateDevice(IN PDEVICE_NODE DeviceNode)
-{
-    PDEVICE_OBJECT DeviceObject;
-    NTSTATUS Status;
-    PAGED_CODE();
-
-    /* Sanity check */
-    ASSERT((DeviceNode->Flags & DNF_ADDED));
-    ASSERT((DeviceNode->Flags & (DNF_RESOURCE_ASSIGNED |
-                                 DNF_RESOURCE_REPORTED |
-                                 DNF_NO_RESOURCE_REQUIRED)));
-
-    /* Get the device object */
-    DeviceObject = DeviceNode->PhysicalDeviceObject;
-
-    /* Check if we're not started yet */
-    if (!(DeviceNode->Flags & DNF_STARTED))
-    {
-        /* Start us */
-        IopStartDevice2(DeviceObject);
-    }
-
-    /* Do we need to query IDs? This happens in the case of manual reporting */
-#if 0
-    if (DeviceNode->Flags & DNF_NEED_QUERY_IDS)
-    {
-        DPRINT1("Warning: Device node has DNF_NEED_QUERY_IDS\n");
-        /* And that case shouldn't happen yet */
-        ASSERT(FALSE);
-    }
-#endif
-
-    IopSetServiceEnumData(DeviceNode);
-
-    /* Make sure we're started, and check if we need enumeration */
-    if ((DeviceNode->Flags & DNF_STARTED) &&
-        (DeviceNode->Flags & DNF_NEED_ENUMERATION_ONLY))
-    {
-        /* Enumerate us */
-        IoInvalidateDeviceRelations(DeviceObject, BusRelations);
-        Status = STATUS_SUCCESS;
-    }
-    else
-    {
-        /* Nothing to do */
-        Status = STATUS_SUCCESS;
-    }
-
-    /* Return */
-    return Status;
-}
-
-NTSTATUS
-IopStartDevice(
-    PDEVICE_NODE DeviceNode)
-{
-    NTSTATUS Status;
-    HANDLE InstanceHandle = NULL, ControlHandle = NULL;
-    UNICODE_STRING KeyName, ValueString;
-    OBJECT_ATTRIBUTES ObjectAttributes;
-
-    if (DeviceNode->Flags & DNF_DISABLED)
-        return STATUS_SUCCESS;
-
-    Status = IopAssignDeviceResources(DeviceNode);
-    if (!NT_SUCCESS(Status))
-        goto ByeBye;
-
-    /* New PnP ABI */
-    IopStartAndEnumerateDevice(DeviceNode);
-
-    /* FIX: Should be done in new device instance code */
-    Status = IopCreateDeviceKeyPath(&DeviceNode->InstancePath, REG_OPTION_NON_VOLATILE, &InstanceHandle);
-    if (!NT_SUCCESS(Status))
-        goto ByeBye;
-
-    /* FIX: Should be done in IoXxxPrepareDriverLoading */
-    // {
-    RtlInitUnicodeString(&KeyName, L"Control");
-    InitializeObjectAttributes(&ObjectAttributes,
-                               &KeyName,
-                               OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
-                               InstanceHandle,
-                               NULL);
-    Status = ZwCreateKey(&ControlHandle,
-                         KEY_SET_VALUE,
-                         &ObjectAttributes,
-                         0,
-                         NULL,
-                         REG_OPTION_VOLATILE,
-                         NULL);
-    if (!NT_SUCCESS(Status))
-        goto ByeBye;
-
-    RtlInitUnicodeString(&KeyName, L"ActiveService");
-    ValueString = DeviceNode->ServiceName;
-    if (!ValueString.Buffer)
-        RtlInitUnicodeString(&ValueString, L"");
-    Status = ZwSetValueKey(ControlHandle, &KeyName, 0, REG_SZ, ValueString.Buffer, ValueString.Length + sizeof(UNICODE_NULL));
-    // }
-
-ByeBye:
-    if (ControlHandle != NULL)
-        ZwClose(ControlHandle);
-
-    if (InstanceHandle != NULL)
-        ZwClose(InstanceHandle);
-
-    return Status;
-}
-
-static
-NTSTATUS
-NTAPI
-IopQueryStopDevice(IN PDEVICE_OBJECT DeviceObject)
-{
-    IO_STACK_LOCATION Stack;
-    PVOID Dummy;
-
-    RtlZeroMemory(&Stack, sizeof(IO_STACK_LOCATION));
-    Stack.MajorFunction = IRP_MJ_PNP;
-    Stack.MinorFunction = IRP_MN_QUERY_STOP_DEVICE;
-
-    return IopSynchronousCall(DeviceObject, &Stack, &Dummy);
-}
-
-static
-VOID
-NTAPI
-IopSendStopDevice(IN PDEVICE_OBJECT DeviceObject)
-{
-    IO_STACK_LOCATION Stack;
-    PVOID Dummy;
-
-    RtlZeroMemory(&Stack, sizeof(IO_STACK_LOCATION));
-    Stack.MajorFunction = IRP_MJ_PNP;
-    Stack.MinorFunction = IRP_MN_STOP_DEVICE;
-
-    /* Drivers should never fail a IRP_MN_STOP_DEVICE request */
-    IopSynchronousCall(DeviceObject, &Stack, &Dummy);
-}
-
-NTSTATUS
-IopStopDevice(
-    PDEVICE_NODE DeviceNode)
-{
-    NTSTATUS Status;
-
-    DPRINT("Stopping device: %wZ\n", &DeviceNode->InstancePath);
-
-    Status = IopQueryStopDevice(DeviceNode->PhysicalDeviceObject);
-    if (NT_SUCCESS(Status))
-    {
-        IopSendStopDevice(DeviceNode->PhysicalDeviceObject);
-
-        DeviceNode->Flags &= ~(DNF_STARTED | DNF_START_REQUEST_PENDING);
-        DeviceNode->Flags |= DNF_STOPPED;
-
-        return STATUS_SUCCESS;
-    }
-
-    return Status;
+    return STATUS_SUCCESS;
 }
 
 /* PUBLIC FUNCTIONS **********************************************************/
@@ -2013,12 +1619,12 @@ IopSendRemoveDevice(IN PDEVICE_OBJECT DeviceObject)
 {
     PDEVICE_NODE DeviceNode = IopGetDeviceNode(DeviceObject);
 
-    /* Drop all our state for this device in case it isn't really going away */
-    DeviceNode->Flags &= DNF_ENUMERATED | DNF_PROCESSED;
+    ASSERT(DeviceNode->State == DeviceNodeAwaitingQueuedRemoval);
 
     /* Drivers should never fail a IRP_MN_REMOVE_DEVICE request */
     PiIrpSendRemoveCheckVpb(DeviceObject, IRP_MN_REMOVE_DEVICE);
 
+    PiSetDevNodeState(DeviceNode, DeviceNodeRemoved);
     PiNotifyTargetDeviceChange(&GUID_TARGET_DEVICE_REMOVE_COMPLETE, DeviceObject, NULL);
     LONG_PTR refCount = ObDereferenceObject(DeviceObject);
     if (refCount != 0)
@@ -2072,6 +1678,7 @@ VOID
 NTAPI
 IopSendSurpriseRemoval(IN PDEVICE_OBJECT DeviceObject)
 {
+    ASSERT(IopGetDeviceNode(DeviceObject)->State == DeviceNodeAwaitingQueuedRemoval);
     /* Drivers should never fail a IRP_MN_SURPRISE_REMOVAL request */
     PiIrpSendRemoveCheckVpb(DeviceObject, IRP_MN_SURPRISE_REMOVAL);
 }
@@ -2200,6 +1807,7 @@ IopQueryRemoveChildDevices(PDEVICE_NODE ParentDeviceNode, BOOLEAN Force)
     {
         NextDeviceNode = ChildDeviceNode->Sibling;
         KeReleaseSpinLock(&IopDeviceTreeLock, OldIrql);
+        PiSetDevNodeState(ChildDeviceNode, DeviceNodeAwaitingQueuedRemoval);
 
         Status = IopPrepareDeviceForRemoval(ChildDeviceNode->PhysicalDeviceObject, Force);
         if (!NT_SUCCESS(Status))
@@ -2344,65 +1952,35 @@ IopPrepareDeviceForRemoval(IN PDEVICE_OBJECT DeviceObject, BOOLEAN Force)
 }
 
 static
-VOID
-IopHandleDeviceRemoval(
-    IN PDEVICE_NODE DeviceNode,
-    IN PDEVICE_RELATIONS DeviceRelations)
+NTSTATUS
+IopRemoveDevice(PDEVICE_NODE DeviceNode)
 {
-    PDEVICE_NODE Child = DeviceNode->Child, NextChild;
-    ULONG i;
-    BOOLEAN Found;
-
-    if (DeviceNode == IopRootDeviceNode)
-        return;
-
-    while (Child != NULL)
-    {
-        NextChild = Child->Sibling;
-        Found = FALSE;
-
-        for (i = 0; DeviceRelations && i < DeviceRelations->Count; i++)
-        {
-            if (IopGetDeviceNode(DeviceRelations->Objects[i]) == Child)
-            {
-                Found = TRUE;
-                break;
-            }
-        }
+    NTSTATUS Status;
 
-        if (!Found && !(Child->Flags & DNF_WILL_BE_REMOVED))
-        {
-            /* Send removal IRPs to all of its children */
-            IopPrepareDeviceForRemoval(Child->PhysicalDeviceObject, TRUE);
+    // This function removes the device subtree, with the root in DeviceNode
+    // atm everyting is in fact done inside this function, which is completely wrong.
+    // The right implementation should have a separate removal worker thread and
+    // properly do device node state transitions
 
-            /* Send the surprise removal IRP */
-            IopSendSurpriseRemoval(Child->PhysicalDeviceObject);
+    DPRINT("Removing device: %wZ\n", &DeviceNode->InstancePath);
 
-            /* Tell the user-mode PnP manager that a device was removed */
-            IopQueueTargetDeviceEvent(&GUID_DEVICE_SURPRISE_REMOVAL,
-                                      &Child->InstancePath);
+    BOOLEAN surpriseRemoval = (_Bool)(DeviceNode->Flags & DNF_DEVICE_GONE);
 
-            /* Send the remove device IRP */
-            IopSendRemoveDevice(Child->PhysicalDeviceObject);
-        }
+    Status = IopPrepareDeviceForRemoval(DeviceNode->PhysicalDeviceObject, surpriseRemoval);
 
-        Child = NextChild;
+    if (surpriseRemoval)
+    {
+        IopSendSurpriseRemoval(DeviceNode->PhysicalDeviceObject);
+        IopQueueTargetDeviceEvent(&GUID_DEVICE_SURPRISE_REMOVAL, &DeviceNode->InstancePath);
     }
-}
 
-NTSTATUS
-IopRemoveDevice(PDEVICE_NODE DeviceNode)
-{
-    NTSTATUS Status;
-
-    DPRINT("Removing device: %wZ\n", &DeviceNode->InstancePath);
-
-    Status = IopPrepareDeviceForRemoval(DeviceNode->PhysicalDeviceObject, FALSE);
     if (NT_SUCCESS(Status))
     {
         IopSendRemoveDevice(DeviceNode->PhysicalDeviceObject);
-        IopQueueTargetDeviceEvent(&GUID_DEVICE_SAFE_REMOVAL,
-                                  &DeviceNode->InstancePath);
+        if (surpriseRemoval)
+        {
+            IopQueueTargetDeviceEvent(&GUID_DEVICE_SAFE_REMOVAL, &DeviceNode->InstancePath);
+        }
         return STATUS_SUCCESS;
     }
 
@@ -2417,16 +1995,10 @@ NTAPI
 IoInvalidateDeviceState(IN PDEVICE_OBJECT PhysicalDeviceObject)
 {
     PDEVICE_NODE DeviceNode = IopGetDeviceNode(PhysicalDeviceObject);
-    IO_STACK_LOCATION Stack;
-    ULONG_PTR PnPFlags;
+    PNP_DEVICE_STATE PnPFlags;
     NTSTATUS Status;
-    IO_STATUS_BLOCK IoStatusBlock;
-
-    RtlZeroMemory(&Stack, sizeof(IO_STACK_LOCATION));
-    Stack.MajorFunction = IRP_MJ_PNP;
-    Stack.MinorFunction = IRP_MN_QUERY_PNP_DEVICE_STATE;
 
-    Status = IopSynchronousCall(PhysicalDeviceObject, &Stack, (PVOID*)&PnPFlags);
+    Status = PiIrpQueryPnPDeviceState(DeviceNode, &PnPFlags);
     if (!NT_SUCCESS(Status))
     {
         if (Status != STATUS_NOT_SUPPORTED)
@@ -2450,20 +2022,16 @@ IoInvalidateDeviceState(IN PDEVICE_OBJECT PhysicalDeviceObject)
         ((PnPFlags & PNP_DEVICE_FAILED) && !(PnPFlags & PNP_DEVICE_RESOURCE_REQUIREMENTS_CHANGED)))
     {
         /* Flag it if it's failed */
-        if (PnPFlags & PNP_DEVICE_FAILED) DeviceNode->Problem = CM_PROB_FAILED_POST_START;
-
-        /* Send removal IRPs to all of its children */
-        IopPrepareDeviceForRemoval(PhysicalDeviceObject, TRUE);
-
-        /* Send surprise removal */
-        IopSendSurpriseRemoval(PhysicalDeviceObject);
-
-        /* Tell the user-mode PnP manager that a device was removed */
-        IopQueueTargetDeviceEvent(&GUID_DEVICE_SURPRISE_REMOVAL,
-                                  &DeviceNode->InstancePath);
+        if (PnPFlags & PNP_DEVICE_FAILED)
+        {
+            PiSetDevNodeProblem(DeviceNode, CM_PROB_FAILED_POST_START);
+        }
 
-        IopSendRemoveDevice(PhysicalDeviceObject);
+        DeviceNode->Flags |= DNF_DEVICE_GONE;
+        PiSetDevNodeState(DeviceNode, DeviceNodeAwaitingQueuedRemoval);
     }
+    // it doesn't work anyway. A real resource rebalancing should be implemented
+#if 0
     else if ((PnPFlags & PNP_DEVICE_FAILED) && (PnPFlags & PNP_DEVICE_RESOURCE_REQUIREMENTS_CHANGED))
     {
         /* Stop for resource rebalance */
@@ -2526,95 +2094,47 @@ IoInvalidateDeviceState(IN PDEVICE_OBJECT PhysicalDeviceObject)
             IopRemoveDevice(DeviceNode);
         }
     }
-}
-
-/*
- * IopInitializePnpServices
- *
- * Initialize services for discovered children
- *
- * Parameters
- *    DeviceNode
- *       Top device node to start initializing services.
- *
- * Return Value
- *    Status
- */
-NTSTATUS
-IopInitializePnpServices(IN PDEVICE_NODE DeviceNode)
-{
-    DEVICETREE_TRAVERSE_CONTEXT Context;
-
-    DPRINT("IopInitializePnpServices(%p)\n", DeviceNode);
-
-    IopInitDeviceTreeTraverseContext(
-        &Context,
-        DeviceNode,
-        IopActionInitChildServices,
-        DeviceNode);
-
-    return IopTraverseDeviceTree(&Context);
+#endif
 }
 
 static
 NTSTATUS
-PipEnumerateDevice(
+PiEnumerateDevice(
     _In_ PDEVICE_NODE DeviceNode)
 {
-    DEVICETREE_TRAVERSE_CONTEXT Context;
-    PDEVICE_RELATIONS DeviceRelations;
     PDEVICE_OBJECT ChildDeviceObject;
-    IO_STATUS_BLOCK IoStatusBlock;
     PDEVICE_NODE ChildDeviceNode;
-    IO_STACK_LOCATION Stack;
-    NTSTATUS Status;
     ULONG i;
 
-    if (DeviceNode->Flags & DNF_NEED_ENUMERATION_ONLY)
-    {
-        DeviceNode->Flags &= ~DNF_NEED_ENUMERATION_ONLY;
+    // bus relations are already obtained for this device node
 
-        DPRINT("Sending GUID_DEVICE_ARRIVAL %wZ\n", &DeviceNode->InstancePath);
-        IopQueueTargetDeviceEvent(&GUID_DEVICE_ARRIVAL,
-                                  &DeviceNode->InstancePath);
-    }
-
-    DPRINT("Sending IRP_MN_QUERY_DEVICE_RELATIONS to device stack\n");
-
-    Stack.Parameters.QueryDeviceRelations.Type = BusRelations;
-
-    Status = IopInitiatePnpIrp(
-        DeviceNode->PhysicalDeviceObject,
-        &IoStatusBlock,
-        IRP_MN_QUERY_DEVICE_RELATIONS,
-        &Stack);
-    if (!NT_SUCCESS(Status))
+    if (!NT_SUCCESS(DeviceNode->CompletionStatus))
     {
-        DPRINT("IopInitiatePnpIrp() failed with status 0x%08lx\n", Status);
-        return Status;
+        DPRINT("QDR request failed for %wZ, status %x\n",
+            &DeviceNode->InstancePath, DeviceNode->CompletionStatus);
+        // treat as if there are no child objects
     }
 
-    DeviceRelations = (PDEVICE_RELATIONS)IoStatusBlock.Information;
+    PDEVICE_RELATIONS DeviceRelations = DeviceNode->OverUsed1.PendingDeviceRelations;
+    DeviceNode->OverUsed1.PendingDeviceRelations = NULL;
 
-    /*
-     * Send removal IRPs for devices that have disappeared
-     * NOTE: This code handles the case where no relations are specified
-     */
-    IopHandleDeviceRemoval(DeviceNode, DeviceRelations);
-
-    /* Now we bail if nothing was returned */
+    // it's acceptable not to have PDOs
     if (!DeviceRelations)
     {
-        /* We're all done */
+        PiSetDevNodeState(DeviceNode, DeviceNodeStarted);
         DPRINT("No PDOs\n");
         return STATUS_SUCCESS;
     }
 
-    DPRINT("Got %u PDOs\n", DeviceRelations->Count);
+    // mark children nodes as non-present (those not returned in DR request will be removed)
+    for (PDEVICE_NODE child = DeviceNode->Child; child != NULL; child = child->Sibling)
+    {
+        child->Flags &= ~DNF_ENUMERATED;
+    }
 
-    /*
-     * Create device nodes for all discovered devices
-     */
+    DPRINT("PiEnumerateDevice: enumerating %u children\n", DeviceRelations->Count);
+
+    // create device nodes for all new children and set DNF_ENUMERATED back for old ones
     for (i = 0; i < DeviceRelations->Count; i++)
     {
         ChildDeviceObject = DeviceRelations->Objects[i];
@@ -2638,7 +2158,7 @@ PipEnumerateDevice(
             else
             {
                 /* Ignore this DO */
-                DPRINT1("IopCreateDeviceNode() failed with status 0x%08x. Skipping PDO %u\n", Status, i);
+                DPRINT1("PipAllocateDeviceNode() failed. Skipping PDO %u\n", i);
                 ObDereferenceObject(ChildDeviceObject);
             }
         }
@@ -2651,49 +2171,18 @@ PipEnumerateDevice(
     }
     ExFreePool(DeviceRelations);
 
-    /*
-     * Retrieve information about all discovered children from the bus driver
-     */
-    IopInitDeviceTreeTraverseContext(
-        &Context,
-        DeviceNode,
-        IopActionInterrogateDeviceStack,
-        DeviceNode);
-
-    Status = IopTraverseDeviceTree(&Context);
-    if (!NT_SUCCESS(Status))
-    {
-        DPRINT("IopTraverseDeviceTree() failed with status 0x%08lx\n", Status);
-        return Status;
-    }
-
-    /*
-     * Retrieve configuration from the registry for discovered children
-     */
-    IopInitDeviceTreeTraverseContext(
-        &Context,
-        DeviceNode,
-        IopActionConfigureChildServices,
-        DeviceNode);
-
-    Status = IopTraverseDeviceTree(&Context);
-    if (!NT_SUCCESS(Status))
+    // time to remove non-reported devices
+    for (PDEVICE_NODE child = DeviceNode->Child; child != NULL; child = child->Sibling)
     {
-        DPRINT("IopTraverseDeviceTree() failed with status 0x%08lx\n", Status);
-        return Status;
-    }
-
-    /*
-     * Initialize services for discovered children.
-     */
-    Status = IopInitializePnpServices(DeviceNode);
-    if (!NT_SUCCESS(Status))
-    {
-        DPRINT("IopInitializePnpServices() failed with status 0x%08lx\n", Status);
-        return Status;
+        if (!(child->Flags & (DNF_ENUMERATED|DNF_DEVICE_GONE)))
+        {
+            // this flag indicates that this is a surprise removal
+            child->Flags |= DNF_DEVICE_GONE;
+            PiSetDevNodeState(child, DeviceNodeAwaitingQueuedRemoval);
+        }
     }
 
-    DPRINT("IopEnumerateDevice() finished\n");
+    PiSetDevNodeState(DeviceNode, DeviceNodeStarted);
     return STATUS_SUCCESS;
 }
 
@@ -2787,7 +2276,7 @@ IoRequestDeviceEject(IN PDEVICE_OBJECT PhysicalDeviceObject)
     }
     else
     {
-        DeviceNode->Flags |= DNF_DISABLED;
+        // DeviceNode->Flags |= DNF_DISABLED;
     }
 
     IopQueueTargetDeviceEvent(&GUID_DEVICE_EJECT,
@@ -2801,54 +2290,180 @@ cleanup:
 }
 
 static
-NTSTATUS
-PipResetDevice(
-    _In_ PDEVICE_NODE DeviceNode)
+VOID
+PiDevNodeStateMachine(
+    _In_ PDEVICE_NODE RootNode)
 {
-    NTSTATUS Status = STATUS_SUCCESS;
-
-    ASSERT(DeviceNode->Flags & DNF_ENUMERATED);
-    ASSERT(DeviceNode->Flags & DNF_PROCESSED);
+    NTSTATUS status;
+    BOOLEAN doProcessAgain;
+    PDEVICE_NODE currentNode = RootNode;
+    PDEVICE_OBJECT referencedObject;
 
-    /* Check if there's already a driver loaded for this device */
-    if (DeviceNode->Flags & DNF_ADDED)
+    do
     {
-        /* FIXME: our drivers do not handle device removal well enough */
-#if 0
-        /* Remove the device node */
-        Status = IopRemoveDevice(DeviceNode);
-        if (NT_SUCCESS(Status))
+        doProcessAgain = FALSE;
+
+        // The device can be removed during processing, but we still need its Parent and Sibling
+        // links to continue the tree traversal. So keep the link till the and of a cycle
+        referencedObject = currentNode->PhysicalDeviceObject;
+        ObReferenceObject(referencedObject);
+
+        // Devices with problems are skipped (unless they are not being removed)
+        if (currentNode->Flags & DNF_HAS_PROBLEM &&
+            currentNode->State != DeviceNodeAwaitingQueuedRemoval)
         {
-            /* Invalidate device relations for the parent to reenumerate the device */
-            DPRINT1("A new driver will be loaded for '%wZ' (FDO above removed)\n", &DeviceNode->InstancePath);
-            Status = IoInvalidateDeviceRelations(DeviceNode->Parent->PhysicalDeviceObject, BusRelations);
+            goto skipEnum;
         }
-        else
-#endif
+
+        switch (currentNode->State)
         {
-            /* A driver has already been loaded for this device */
-            DPRINT("A reboot is required for the current driver for '%wZ' to be replaced\n", &DeviceNode->InstancePath);
-            DeviceNode->Problem = CM_PROB_NEED_RESTART;
-        }
-    }
-    else
-    {
-        /* FIXME: What if the device really is disabled? */
-        DeviceNode->Flags &= ~DNF_DISABLED;
-        DeviceNode->Problem = 0;
+            case DeviceNodeUnspecified: // this state is not used
+                break;
+            case DeviceNodeUninitialized:
+                DPRINT("DeviceNodeUninitialized %wZ\n", &currentNode->InstancePath);
+                status = PiInitializeDevNode(currentNode);
+                doProcessAgain = NT_SUCCESS(status);
+                break;
+            case DeviceNodeInitialized:
+                DPRINT("DeviceNodeInitialized %wZ\n", &currentNode->InstancePath);
+                status = PiCallDriverAddDevice(currentNode, PnPBootDriversInitialized);
+                doProcessAgain = NT_SUCCESS(status);
+                break;
+            case DeviceNodeDriversAdded:
+                DPRINT("DeviceNodeDriversAdded %wZ\n", &currentNode->InstancePath);
+                status = IopAssignDeviceResources(currentNode);
+                doProcessAgain = NT_SUCCESS(status);
+                break;
+            case DeviceNodeResourcesAssigned:
+                DPRINT("DeviceNodeResourcesAssigned %wZ\n", &currentNode->InstancePath);
+                // send IRP_MN_START_DEVICE
+                PiIrpStartDevice(currentNode);
+
+                // skip DeviceNodeStartPending, it is probably used for an async IRP_MN_START_DEVICE
+                PiSetDevNodeState(currentNode, DeviceNodeStartCompletion);
+                doProcessAgain = TRUE;
+                break;
+            case DeviceNodeStartPending: // skipped on XP/2003
+                break;
+            case DeviceNodeStartCompletion:
+                DPRINT("DeviceNodeStartCompletion %wZ\n", &currentNode->InstancePath);
+                status = currentNode->CompletionStatus;
+                doProcessAgain = TRUE;
+                if (!NT_SUCCESS(status))
+                {
+                    UINT32 problem = (status == STATUS_PNP_REBOOT_REQUIRED)
+                                     ? CM_PROB_NEED_RESTART
+                                     : CM_PROB_FAILED_START;
 
-        /* Load service data from the registry */
-        Status = IopActionConfigureChildServices(DeviceNode, DeviceNode->Parent);
+                    PiSetDevNodeProblem(currentNode, problem);
+                    PiSetDevNodeState(currentNode, DeviceNodeAwaitingQueuedRemoval);
+                }
+                else
+                {
+                    // TODO: IopDoDeferredSetInterfaceState and IopAllocateLegacyBootResources
+                    // are called here too
 
-        if (NT_SUCCESS(Status))
-        {
-            /* Start the service and begin PnP initialization of the device again */
-            DPRINT("A new driver will be loaded for '%wZ' (no FDO above)\n", &DeviceNode->InstancePath);
-            Status = IopActionInitChildServices(DeviceNode, DeviceNode->Parent);
+                    PiSetDevNodeState(currentNode, DeviceNodeStartPostWork);
+                }
+                break;
+            case DeviceNodeStartPostWork:
+                DPRINT("DeviceNodeStartPostWork %wZ\n", &currentNode->InstancePath);
+                status = PiStartDeviceFinal(currentNode);
+                doProcessAgain = TRUE;
+                break;
+            case DeviceNodeStarted:
+                if (currentNode->Flags & DNF_REENUMERATE)
+                {
+                    DPRINT("DeviceNodeStarted REENUMERATE %wZ\n", &currentNode->InstancePath);
+                    currentNode->Flags &= ~DNF_REENUMERATE;
+                    status = PiIrpQueryDeviceRelations(currentNode, BusRelations);
+
+                    // again, skip DeviceNodeEnumeratePending as with the starting sequence
+                    PiSetDevNodeState(currentNode, DeviceNodeEnumerateCompletion);
+                    doProcessAgain = TRUE;
+                }
+                break;
+            case DeviceNodeQueryStopped:
+                // we're here after sending IRP_MN_QUERY_STOP_DEVICE
+                status = currentNode->CompletionStatus;
+                if (NT_SUCCESS(status))
+                {
+                    PiSetDevNodeState(currentNode, DeviceNodeStopped);
+                }
+                else
+                {
+                    PiIrpCancelStopDevice(currentNode);
+                    PiSetDevNodeState(currentNode, DeviceNodeStarted);
+                }
+                break;
+            case DeviceNodeStopped:
+                // TODO: do resource rebalance (not implemented)
+                ASSERT(FALSE);
+                break;
+            case DeviceNodeRestartCompletion:
+                break;
+            case DeviceNodeEnumeratePending: // skipped on XP/2003
+                break;
+            case DeviceNodeEnumerateCompletion:
+                DPRINT("DeviceNodeEnumerateCompletion %wZ\n", &currentNode->InstancePath);
+                status = PiEnumerateDevice(currentNode);
+                doProcessAgain = TRUE;
+                break;
+            case DeviceNodeAwaitingQueuedDeletion:
+                break;
+            case DeviceNodeAwaitingQueuedRemoval:
+                DPRINT("DeviceNodeAwaitingQueuedRemoval %wZ\n", &currentNode->InstancePath);
+                status = IopRemoveDevice(currentNode);
+                break;
+            case DeviceNodeQueryRemoved:
+                break;
+            case DeviceNodeRemovePendingCloses:
+                break;
+            case DeviceNodeRemoved:
+                break;
+            case DeviceNodeDeletePendingCloses:
+                break;
+            case DeviceNodeDeleted:
+                break;
+            default:
+                break;
         }
-    }
 
-    return Status;
+skipEnum:
+        if (!doProcessAgain)
+        {
+            KIRQL OldIrql;
+            KeAcquireSpinLock(&IopDeviceTreeLock, &OldIrql);
+            /* If we have a child, simply go down the tree */
+            if (currentNode->State != DeviceNodeRemoved && currentNode->Child != NULL)
+            {
+                ASSERT(currentNode->Child->Parent == currentNode);
+                currentNode = currentNode->Child;
+            }
+            else
+            {
+                while (currentNode != RootNode)
+                {
+                    /* All children processed -- go sideways */
+                    if (currentNode->Sibling != NULL)
+                    {
+                        ASSERT(currentNode->Sibling->Parent == currentNode->Parent);
+                        currentNode = currentNode->Sibling;
+                        break;
+                    }
+                    else
+                    {
+                        /* We're the last sibling -- go back up */
+                        ASSERT(currentNode->Parent->LastChild == currentNode);
+                        currentNode = currentNode->Parent;
+                    }
+                    /* We already visited the parent and all its children, so keep looking */
+                }
+            }
+            KeReleaseSpinLock(&IopDeviceTreeLock, OldIrql);
+        }
+        ObDereferenceObject(referencedObject);
+    } while (doProcessAgain || currentNode != RootNode);
 }
 
 #ifdef DBG
@@ -2904,11 +2519,14 @@ PipDeviceActionWorker(
         {
             case PiActionEnumRootDevices:
             case PiActionEnumDeviceTree:
-                status = PipEnumerateDevice(deviceNode);
+                deviceNode->Flags |= DNF_REENUMERATE;
+                PiDevNodeStateMachine(deviceNode);
                 break;
 
             case PiActionResetDevice:
-                status = PipResetDevice(deviceNode);
+                // TODO: the operation is a no-op for everything except removed nodes
+                // for removed nodes, it returns them back to DeviceNodeUninitialized
+                status = STATUS_SUCCESS;
                 break;
 
             default:
index 090419f..f59a3dd 100644 (file)
@@ -70,6 +70,8 @@ PipAllocateDeviceNode(
         PhysicalDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
     }
 
+    DPRINT("Allocated devnode 0x%p\n", DeviceNode);
+
     /* Return the node */
     return DeviceNode;
 }
@@ -98,6 +100,32 @@ PiInsertDevNode(
     }
     KeReleaseSpinLock(&IopDeviceTreeLock, oldIrql);
     DeviceNode->Level = ParentNode->Level + 1;
+
+    DPRINT("Inserted devnode 0x%p to parent 0x%p\n", DeviceNode, ParentNode);
+}
+
+PNP_DEVNODE_STATE
+PiSetDevNodeState(
+    _In_ PDEVICE_NODE DeviceNode,
+    _In_ PNP_DEVNODE_STATE NewState)
+{
+    KIRQL oldIrql;
+
+    KeAcquireSpinLock(&IopDeviceTreeLock, &oldIrql);
+
+    PNP_DEVNODE_STATE prevState = DeviceNode->State;
+    if (prevState != NewState)
+    {
+        DeviceNode->State = NewState;
+        DeviceNode->PreviousState = prevState;
+        DeviceNode->StateHistory[DeviceNode->StateHistoryEntry++] = prevState;
+        DeviceNode->StateHistoryEntry %= DEVNODE_HISTORY_SIZE;
+    }
+
+    KeReleaseSpinLock(&IopDeviceTreeLock, oldIrql);
+
+    DPRINT("%wZ Changed state 0x%x => 0x%x\n", &DeviceNode->InstancePath, prevState, NewState);
+    return prevState;
 }
 
 VOID
@@ -302,9 +330,11 @@ IopFreeDeviceNode(
     KIRQL OldIrql;
     PDEVICE_NODE PrevSibling = NULL;
 
-    /* All children must be deleted before a parent is deleted */
-    ASSERT(!DeviceNode->Child);
     ASSERT(DeviceNode->PhysicalDeviceObject);
+    /* All children must be deleted before a parent is deleted */
+    ASSERT(DeviceNode->Child == NULL);
+    /* This is the only state where we are allowed to remove the node */
+    ASSERT(DeviceNode->State == DeviceNodeRemoved);
     /* No notifications should be registered for this device */
     ASSERT(IsListEmpty(&DeviceNode->TargetDeviceNotify));
 
index 4f930e9..66c1da7 100644 (file)
@@ -689,37 +689,54 @@ IopGetRelatedDevice(PPLUGPLAY_CONTROL_RELATED_DEVICE_DATA RelatedDeviceData)
     return Status;
 }
 
+static
+BOOLEAN
+PiIsDevNodeStarted(
+    _In_ PDEVICE_NODE DeviceNode)
+{
+    return (DeviceNode->State == DeviceNodeStartPending ||
+            DeviceNode->State == DeviceNodeStartCompletion ||
+            DeviceNode->State == DeviceNodeStartPostWork ||
+            DeviceNode->State == DeviceNodeStarted ||
+            DeviceNode->State == DeviceNodeQueryStopped ||
+            DeviceNode->State == DeviceNodeEnumeratePending ||
+            DeviceNode->State == DeviceNodeEnumerateCompletion ||
+            DeviceNode->State == DeviceNodeStopped ||
+            DeviceNode->State == DeviceNodeRestartCompletion);
+}
+
 static ULONG
 IopGetDeviceNodeStatus(PDEVICE_NODE DeviceNode)
 {
-    ULONG Output = 0;
+    ULONG Output = DN_NT_ENUMERATOR | DN_NT_DRIVER;
 
     if (DeviceNode->Parent == IopRootDeviceNode)
         Output |= DN_ROOT_ENUMERATED;
 
-    if (DeviceNode->Flags & DNF_ADDED)
+    // FIXME: review for deleted and removed states
+    if (DeviceNode->State >= DeviceNodeDriversAdded)
         Output |= DN_DRIVER_LOADED;
 
-    /* FIXME: DN_ENUM_LOADED */
-
-    if (DeviceNode->Flags & DNF_STARTED)
+    if (PiIsDevNodeStarted(DeviceNode))
         Output |= DN_STARTED;
 
-    /* FIXME: Manual */
+    if (DeviceNode->UserFlags & DNUF_WILL_BE_REMOVED)
+        Output |= DN_WILL_BE_REMOVED;
 
-    if (!(DeviceNode->Flags & DNF_PROCESSED))
-        Output |= DN_NEED_TO_ENUM;
-
-    /* DN_NOT_FIRST_TIME is 9x only */
+    if (DeviceNode->Flags & DNF_HAS_PROBLEM)
+        Output |= DN_HAS_PROBLEM;
 
-    /* FIXME: DN_HARDWARE_ENUM */
+    if (DeviceNode->Flags & DNF_HAS_PRIVATE_PROBLEM)
+        Output |= DN_PRIVATE_PROBLEM;
 
-    /* DN_LIAR and DN_HAS_MARK are 9x only */
+    if (DeviceNode->Flags & DNF_DRIVER_BLOCKED)
+        Output |= DN_DRIVER_BLOCKED;
 
-    if (DeviceNode->Problem != 0)
-        Output |= DN_HAS_PROBLEM;
+    if (DeviceNode->Flags & DNF_CHILD_WITH_INVALID_ID)
+        Output |= DN_CHILD_WITH_INVALID_ID;
 
-    /* FIXME: DN_FILTERED */
+    if (DeviceNode->Flags & DNF_HAS_PRIVATE_PROBLEM)
+        Output |= DN_PRIVATE_PROBLEM;
 
     if (DeviceNode->Flags & DNF_LEGACY_DRIVER)
         Output |= DN_LEGACY_DRIVER;
@@ -730,10 +747,6 @@ IopGetDeviceNodeStatus(PDEVICE_NODE DeviceNode)
     if (!(DeviceNode->UserFlags & DNUF_NOT_DISABLEABLE))
         Output |= DN_DISABLEABLE;
 
-    /* FIXME: Implement the rest */
-
-    Output |= DN_NT_ENUMERATOR | DN_NT_DRIVER;
-
     return Output;
 }
 
index 0667fd7..b189ac8 100644 (file)
@@ -431,9 +431,8 @@ IopInitializePlugPlayServices(VOID)
     IopRootDeviceNode = PipAllocateDeviceNode(Pdo);
 
     /* Set flags */
-    IopRootDeviceNode->Flags |= DNF_STARTED + DNF_PROCESSED + DNF_ENUMERATED +
-                                DNF_MADEUP + DNF_NO_RESOURCE_REQUIRED +
-                                DNF_ADDED;
+    IopRootDeviceNode->Flags |= DNF_MADEUP | DNF_ENUMERATED |
+                                DNF_IDS_QUERIED | DNF_NO_RESOURCE_REQUIRED;
 
     /* Create instance path */
     RtlCreateUnicodeString(&IopRootDeviceNode->InstancePath,
@@ -443,19 +442,20 @@ IopInitializePlugPlayServices(VOID)
     IopRootDriverObject->DriverExtension->AddDevice(IopRootDriverObject,
                                                     IopRootDeviceNode->PhysicalDeviceObject);
 
+    PiSetDevNodeState(IopRootDeviceNode, DeviceNodeStarted);
+
     /* Initialize PnP-Event notification support */
     Status = IopInitPlugPlayEvents();
     if (!NT_SUCCESS(Status)) return Status;
 
-    /* Report the device to the user-mode pnp manager */
-    IopQueueTargetDeviceEvent(&GUID_DEVICE_ARRIVAL,
-                              &IopRootDeviceNode->InstancePath);
-
     /* Initialize the Bus Type GUID List */
     PnpBusTypeGuidList = ExAllocatePool(PagedPool, sizeof(IO_BUS_TYPE_GUID_LIST));
     RtlZeroMemory(PnpBusTypeGuidList, sizeof(IO_BUS_TYPE_GUID_LIST));
     ExInitializeFastMutex(&PnpBusTypeGuidList->Lock);
 
+    /* Initialize PnP root relations (this is a syncronous operation) */
+    PiQueueDeviceAction(IopRootDeviceNode->PhysicalDeviceObject, PiActionEnumRootDevices, NULL, NULL);
+
     /* Launch the firmware mapper */
     Status = IopUpdateRootKey();
     if (!NT_SUCCESS(Status)) return Status;
diff --git a/ntoskrnl/io/pnpmgr/pnpirp.c b/ntoskrnl/io/pnpmgr/pnpirp.c
new file mode 100644 (file)
index 0000000..03fc0d1
--- /dev/null
@@ -0,0 +1,225 @@
+/*
+ * PROJECT:     ReactOS Kernel
+ * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE:     Shortcuts for sending different IRP_MJ_PNP requests
+ * COPYRIGHT:   Copyright 2010 Sir Richard <sir_richard@svn.reactos.org>
+ *              Copyright 2020 Victor Perevertkin <victor.perevertkin@reactos.org>
+ */
+
+#include <ntoskrnl.h>
+#define NDEBUG
+#include <debug.h>
+
+NTSTATUS
+IopSynchronousCall(
+    _In_ PDEVICE_OBJECT DeviceObject,
+    _In_ PIO_STACK_LOCATION IoStackLocation,
+    _Out_ PVOID *Information)
+{
+    PIRP Irp;
+    PIO_STACK_LOCATION IrpStack;
+    IO_STATUS_BLOCK IoStatusBlock;
+    KEVENT Event;
+    NTSTATUS Status;
+    PDEVICE_OBJECT TopDeviceObject;
+    PAGED_CODE();
+
+    /* Call the top of the device stack */
+    TopDeviceObject = IoGetAttachedDeviceReference(DeviceObject);
+
+    /* Allocate an IRP */
+    Irp = IoAllocateIrp(TopDeviceObject->StackSize, FALSE);
+    if (!Irp)
+    {
+        ObDereferenceObject(TopDeviceObject);
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    /* Initialize to failure */
+    Irp->IoStatus.Status = IoStatusBlock.Status = STATUS_NOT_SUPPORTED;
+    Irp->IoStatus.Information = IoStatusBlock.Information = 0;
+
+    /* Special case for IRP_MN_FILTER_RESOURCE_REQUIREMENTS */
+    if ((IoStackLocation->MajorFunction == IRP_MJ_PNP) &&
+        (IoStackLocation->MinorFunction == IRP_MN_FILTER_RESOURCE_REQUIREMENTS))
+    {
+        /* Copy the resource requirements list into the IOSB */
+        Irp->IoStatus.Information =
+        IoStatusBlock.Information = (ULONG_PTR)IoStackLocation->Parameters.FilterResourceRequirements.IoResourceRequirementList;
+    }
+
+    /* Initialize the event */
+    KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
+
+    /* Set them up */
+    Irp->UserIosb = &IoStatusBlock;
+    Irp->UserEvent = &Event;
+
+    /* Queue the IRP */
+    Irp->Tail.Overlay.Thread = PsGetCurrentThread();
+    IoQueueThreadIrp(Irp);
+
+    /* Copy-in the stack */
+    IrpStack = IoGetNextIrpStackLocation(Irp);
+    *IrpStack = *IoStackLocation;
+
+    /* Call the driver */
+    Status = IoCallDriver(TopDeviceObject, Irp);
+    /* Otherwise we may get stuck here or have IoStatusBlock not populated */
+    ASSERT(!KeAreAllApcsDisabled());
+    if (Status == STATUS_PENDING)
+    {
+        /* Wait for it */
+        KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
+        Status = IoStatusBlock.Status;
+    }
+
+    /* Remove the reference */
+    ObDereferenceObject(TopDeviceObject);
+
+    /* Return the information */
+    *Information = (PVOID)IoStatusBlock.Information;
+    return Status;
+}
+
+// IRP_MN_START_DEVICE (0x00)
+NTSTATUS
+PiIrpStartDevice(
+    _In_ PDEVICE_NODE DeviceNode)
+{
+    PAGED_CODE();
+
+    ASSERT(DeviceNode);
+    ASSERT(DeviceNode->State == DeviceNodeResourcesAssigned);
+
+    PVOID info;
+    IO_STACK_LOCATION stack = {
+        .MajorFunction = IRP_MJ_PNP,
+        .MinorFunction = IRP_MN_START_DEVICE,
+        .Parameters.StartDevice.AllocatedResources = DeviceNode->ResourceList,
+        .Parameters.StartDevice.AllocatedResourcesTranslated = DeviceNode->ResourceListTranslated
+    };
+
+    // Vista+ does an asynchronous call
+    NTSTATUS status = IopSynchronousCall(DeviceNode->PhysicalDeviceObject, &stack, &info);
+    DeviceNode->CompletionStatus = status;
+    return status;
+}
+
+// IRP_MN_STOP_DEVICE (0x04)
+NTSTATUS
+PiIrpStopDevice(
+    _In_ PDEVICE_NODE DeviceNode)
+{
+    PAGED_CODE();
+
+    ASSERT(DeviceNode);
+    ASSERT(DeviceNode->State == DeviceNodeQueryStopped);
+
+    PVOID info;
+    IO_STACK_LOCATION stack = {
+        .MajorFunction = IRP_MJ_PNP,
+        .MinorFunction = IRP_MN_STOP_DEVICE
+    };
+
+    // Drivers should never fail a IRP_MN_STOP_DEVICE request
+    NTSTATUS status = IopSynchronousCall(DeviceNode->PhysicalDeviceObject, &stack, &info);
+    ASSERT(NT_SUCCESS(status));
+    return status;
+}
+
+// IRP_MN_QUERY_STOP_DEVICE (0x05)
+NTSTATUS
+PiIrpQueryStopDevice(
+    _In_ PDEVICE_NODE DeviceNode)
+{
+    PAGED_CODE();
+
+    ASSERT(DeviceNode);
+    ASSERT(DeviceNode->State == DeviceNodeStarted);
+
+    PVOID info;
+    IO_STACK_LOCATION stack = {
+        .MajorFunction = IRP_MJ_PNP,
+        .MinorFunction = IRP_MN_QUERY_STOP_DEVICE
+    };
+
+    NTSTATUS status = IopSynchronousCall(DeviceNode->PhysicalDeviceObject, &stack, &info);
+    DeviceNode->CompletionStatus = status;
+    return status;
+}
+
+// IRP_MN_CANCEL_STOP_DEVICE (0x06)
+NTSTATUS
+PiIrpCancelStopDevice(
+    _In_ PDEVICE_NODE DeviceNode)
+{
+    PAGED_CODE();
+
+    ASSERT(DeviceNode);
+    ASSERT(DeviceNode->State == DeviceNodeQueryStopped);
+
+    PVOID info;
+    IO_STACK_LOCATION stack = {
+        .MajorFunction = IRP_MJ_PNP,
+        .MinorFunction = IRP_MN_CANCEL_STOP_DEVICE
+    };
+
+    // in fact we don't care which status is returned here
+    NTSTATUS status = IopSynchronousCall(DeviceNode->PhysicalDeviceObject, &stack, &info);
+    ASSERT(NT_SUCCESS(status));
+    return status;
+}
+
+// IRP_MN_QUERY_DEVICE_RELATIONS (0x07)
+NTSTATUS
+PiIrpQueryDeviceRelations(
+    _In_ PDEVICE_NODE DeviceNode,
+    _In_ DEVICE_RELATION_TYPE Type)
+{
+    PAGED_CODE();
+
+    ASSERT(DeviceNode);
+    ASSERT(DeviceNode->State == DeviceNodeStarted);
+
+    IO_STACK_LOCATION stack = {
+        .MajorFunction = IRP_MJ_PNP,
+        .MinorFunction = IRP_MN_QUERY_DEVICE_RELATIONS,
+        .Parameters.QueryDeviceRelations.Type = Type
+    };
+
+    // Vista+ does an asynchronous call
+    NTSTATUS status = IopSynchronousCall(DeviceNode->PhysicalDeviceObject,
+                                         &stack,
+                                         (PVOID)&DeviceNode->OverUsed1.PendingDeviceRelations);
+    DeviceNode->CompletionStatus = status;
+    return status;
+}
+
+// IRP_MN_QUERY_PNP_DEVICE_STATE (0x14)
+NTSTATUS
+PiIrpQueryPnPDeviceState(
+    _In_ PDEVICE_NODE DeviceNode,
+    _Out_ PPNP_DEVICE_STATE DeviceState)
+{
+    PAGED_CODE();
+
+    ASSERT(DeviceNode);
+    ASSERT(DeviceNode->State == DeviceNodeStartPostWork || 
+           DeviceNode->State == DeviceNodeStarted);
+
+    ULONG_PTR longState;
+    IO_STACK_LOCATION stack = {
+        .MajorFunction = IRP_MJ_PNP,
+        .MinorFunction = IRP_MN_QUERY_PNP_DEVICE_STATE
+    };
+
+    NTSTATUS status;
+    status = IopSynchronousCall(DeviceNode->PhysicalDeviceObject, &stack, (PVOID)&longState);
+    if (NT_SUCCESS(status))
+    {
+        *DeviceState = longState;
+    }
+
+    return status;
+}
index 3729033..d4f5cd9 100644 (file)
@@ -475,82 +475,6 @@ Quickie:
     return FoundIndex;
 }
 
-NTSTATUS
-NTAPI
-IopSynchronousCall(IN PDEVICE_OBJECT DeviceObject,
-                   IN PIO_STACK_LOCATION IoStackLocation,
-                   OUT PVOID *Information)
-{
-    PIRP Irp;
-    PIO_STACK_LOCATION IrpStack;
-    IO_STATUS_BLOCK IoStatusBlock;
-    KEVENT Event;
-    NTSTATUS Status;
-    PDEVICE_OBJECT TopDeviceObject;
-    PAGED_CODE();
-
-    /* Call the top of the device stack */
-    TopDeviceObject = IoGetAttachedDeviceReference(DeviceObject);
-
-    /* Allocate an IRP */
-    Irp = IoAllocateIrp(TopDeviceObject->StackSize, FALSE);
-    if (!Irp)
-    {
-        ObDereferenceObject(TopDeviceObject);
-        return STATUS_INSUFFICIENT_RESOURCES;
-    }
-
-    /* Initialize to failure */
-    Irp->IoStatus.Status = IoStatusBlock.Status = STATUS_NOT_SUPPORTED;
-    Irp->IoStatus.Information = IoStatusBlock.Information = 0;
-
-    /* Special case for IRP_MN_FILTER_RESOURCE_REQUIREMENTS */
-    if ((IoStackLocation->MajorFunction == IRP_MJ_PNP) &&
-        (IoStackLocation->MinorFunction == IRP_MN_FILTER_RESOURCE_REQUIREMENTS))
-    {
-        /* Copy the resource requirements list into the IOSB */
-        Irp->IoStatus.Information =
-        IoStatusBlock.Information = (ULONG_PTR)IoStackLocation->Parameters.FilterResourceRequirements.IoResourceRequirementList;
-    }
-
-    /* Initialize the event */
-    KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
-
-    /* Set them up */
-    Irp->UserIosb = &IoStatusBlock;
-    Irp->UserEvent = &Event;
-
-    /* Queue the IRP */
-    Irp->Tail.Overlay.Thread = PsGetCurrentThread();
-    IoQueueThreadIrp(Irp);
-
-    /* Copy-in the stack */
-    IrpStack = IoGetNextIrpStackLocation(Irp);
-    *IrpStack = *IoStackLocation;
-
-    /* Call the driver */
-    Status = IoCallDriver(TopDeviceObject, Irp);
-    /* Otherwise we may get stuck here or have IoStatusBlock not populated */
-    ASSERT(!KeAreAllApcsDisabled());
-    if (Status == STATUS_PENDING)
-    {
-        /* Wait for it */
-        KeWaitForSingleObject(&Event,
-                              Executive,
-                              KernelMode,
-                              FALSE,
-                              NULL);
-        Status = IoStatusBlock.Status;
-    }
-
-    /* Remove the reference */
-    ObDereferenceObject(TopDeviceObject);
-
-    /* Return the information */
-    *Information = (PVOID)IoStatusBlock.Information;
-    return Status;
-}
-
 NTSTATUS
 NTAPI
 IopInitiatePnpIrp(IN PDEVICE_OBJECT DeviceObject,
index 4c5e36c..8104a8f 100644 (file)
@@ -28,10 +28,6 @@ NTSTATUS
 IopSetDeviceInstanceData(HANDLE InstanceKey,
                          PDEVICE_NODE DeviceNode);
 
-NTSTATUS
-IopActionInterrogateDeviceStack(PDEVICE_NODE DeviceNode,
-                                PVOID Context);
-
 NTSTATUS
 PpSetCustomTargetEvent(IN PDEVICE_OBJECT DeviceObject,
                        IN OUT PKEVENT SyncEvent OPTIONAL,
@@ -149,20 +145,20 @@ PpSetCustomTargetEvent(IN PDEVICE_OBJECT DeviceObject,
  */
 NTSTATUS
 NTAPI
-IoReportDetectedDevice(IN PDRIVER_OBJECT DriverObject,
-                       IN INTERFACE_TYPE LegacyBusType,
-                       IN ULONG BusNumber,
-                       IN ULONG SlotNumber,
-                       IN PCM_RESOURCE_LIST ResourceList,
-                       IN PIO_RESOURCE_REQUIREMENTS_LIST ResourceRequirements OPTIONAL,
-                       IN BOOLEAN ResourceAssigned,
-                       IN OUT PDEVICE_OBJECT *DeviceObject OPTIONAL)
+IoReportDetectedDevice(
+    _In_ PDRIVER_OBJECT DriverObject,
+    _In_ INTERFACE_TYPE LegacyBusType,
+    _In_ ULONG BusNumber,
+    _In_ ULONG SlotNumber,
+    _In_opt_ PCM_RESOURCE_LIST ResourceList,
+    _In_opt_ PIO_RESOURCE_REQUIREMENTS_LIST ResourceRequirements,
+    _In_ BOOLEAN ResourceAssigned,
+    _Inout_ PDEVICE_OBJECT *DeviceObject)
 {
     PDEVICE_NODE DeviceNode;
     PDEVICE_OBJECT Pdo;
     NTSTATUS Status;
     HANDLE InstanceKey;
-    ULONG RequiredLength;
     UNICODE_STRING ValueName, ServiceLongName, ServiceName;
     WCHAR HardwareId[256];
     PWCHAR IfString;
@@ -223,6 +219,7 @@ IoReportDetectedDevice(IN PDRIVER_OBJECT DriverObject,
     }
 
     /* We use the caller's PDO if they supplied one */
+    UNICODE_STRING instancePath;
     if (DeviceObject && *DeviceObject)
     {
         Pdo = *DeviceObject;
@@ -230,10 +227,7 @@ IoReportDetectedDevice(IN PDRIVER_OBJECT DriverObject,
     else
     {
         /* Create the PDO */
-        Status = PnpRootCreateDevice(&ServiceName,
-                                     NULL,
-                                     &Pdo,
-                                     NULL);
+        Status = PnpRootCreateDevice(&ServiceName, NULL, &Pdo, &instancePath);
         if (!NT_SUCCESS(Status))
         {
             DPRINT("PnpRootCreateDevice() failed (Status 0x%08lx)\n", Status);
@@ -249,28 +243,7 @@ IoReportDetectedDevice(IN PDRIVER_OBJECT DriverObject,
         return STATUS_INSUFFICIENT_RESOURCES;
     }
 
-    PiInsertDevNode(DeviceNode, IopRootDeviceNode);
-
-    /* We're enumerated already */
-    IopDeviceNodeSetFlag(DeviceNode, DNF_ENUMERATED);
-
-    /* We don't call AddDevice for devices reported this way */
-    IopDeviceNodeSetFlag(DeviceNode, DNF_ADDED);
-
-    /* We don't send IRP_MN_START_DEVICE */
-    IopDeviceNodeSetFlag(DeviceNode, DNF_STARTED);
-
-    /* We need to get device IDs */
-#if 0
-    IopDeviceNodeSetFlag(DeviceNode, DNF_NEED_QUERY_IDS);
-#endif
-
-    /* This is a legacy driver for this device */
-    IopDeviceNodeSetFlag(DeviceNode, DNF_LEGACY_DRIVER);
-
-    /* Perform a manual configuration of our device */
-    IopActionInterrogateDeviceStack(DeviceNode, DeviceNode->Parent);
-    IopActionConfigureChildServices(DeviceNode, DeviceNode->Parent);
+    Status = RtlDuplicateUnicodeString(0, &instancePath, &DeviceNode->InstancePath);
 
     /* Open a handle to the instance path key */
     Status = IopCreateDeviceKeyPath(&DeviceNode->InstancePath, REG_OPTION_NON_VOLATILE, &InstanceKey);
@@ -321,30 +294,6 @@ IoReportDetectedDevice(IN PDRIVER_OBJECT DriverObject,
         return Status;
     }
 
-    /* Add a hardware ID if the driver didn't report one */
-    RtlInitUnicodeString(&ValueName, L"HardwareID");
-    if (ZwQueryValueKey(InstanceKey, &ValueName, KeyValueBasicInformation, NULL, 0, &RequiredLength) == STATUS_OBJECT_NAME_NOT_FOUND)
-    {
-        /* Just use our most specific compatible ID */
-        IdLength = 0;
-        IdLength += swprintf(&HardwareId[IdLength],
-                             L"DETECTED%ls\\%wZ",
-                             IfString,
-                             &ServiceName);
-        IdLength++;
-
-        HardwareId[IdLength++] = UNICODE_NULL;
-
-        /* Write the value to the registry */
-        Status = ZwSetValueKey(InstanceKey, &ValueName, 0, REG_MULTI_SZ, HardwareId, IdLength * sizeof(WCHAR));
-        if (!NT_SUCCESS(Status))
-        {
-            DPRINT("Failed to write the hardware ID: 0x%x\n", Status);
-            ZwClose(InstanceKey);
-            return Status;
-        }
-    }
-
     /* Assign the resources to the device node */
     DeviceNode->BootResources = ResourceList;
     DeviceNode->ResourceRequirements = ResourceRequirements;
@@ -380,13 +329,11 @@ IoReportDetectedDevice(IN PDRIVER_OBJECT DriverObject,
     if (DeviceObject && *DeviceObject)
         PnpRootRegisterDevice(*DeviceObject);
 
-    /* Report the device's enumeration to umpnpmgr */
-    IopQueueTargetDeviceEvent(&GUID_DEVICE_ENUMERATED,
-                              &DeviceNode->InstancePath);
+    PiInsertDevNode(DeviceNode, IopRootDeviceNode);
+    DeviceNode->Flags |= DNF_MADEUP | DNF_ENUMERATED;
 
-    /* Report the device's arrival to umpnpmgr */
-    IopQueueTargetDeviceEvent(&GUID_DEVICE_ARRIVAL,
-                              &DeviceNode->InstancePath);
+    // we still need to query IDs, send events and reenumerate this node
+    PiSetDevNodeState(DeviceNode, DeviceNodeStartPostWork);
 
     DPRINT("Reported device: %S (%wZ)\n", HardwareId, &DeviceNode->InstancePath);
 
index 11e7b1b..f8a4797 100644 (file)
@@ -698,7 +698,7 @@ ByeBye:
 
     // Hacked, because after fixing resource list parsing
     // we actually detect resource conflicts
-    return Silent ? Result : FALSE; // Result; 
+    return Silent ? Result : FALSE; // Result;
 }
 
 static
@@ -1109,20 +1109,17 @@ IopAssignDeviceResources(
    NTSTATUS Status;
    ULONG ListSize;
 
-   IopDeviceNodeSetFlag(DeviceNode, DNF_ASSIGNING_RESOURCES);
-
    Status = IopFilterResourceRequirements(DeviceNode);
    if (!NT_SUCCESS(Status))
        goto ByeBye;
 
    if (!DeviceNode->BootResources && !DeviceNode->ResourceRequirements)
    {
-      DeviceNode->Flags |= DNF_NO_RESOURCE_REQUIRED;
-      DeviceNode->Flags &= ~DNF_ASSIGNING_RESOURCES;
-
       /* No resource needed for this device */
       DeviceNode->ResourceList = NULL;
       DeviceNode->ResourceListTranslated = NULL;
+      PiSetDevNodeState(DeviceNode, DeviceNodeResourcesAssigned);
+      DeviceNode->Flags |= DNF_NO_RESOURCE_REQUIRED;
 
       return STATUS_SUCCESS;
    }
@@ -1191,9 +1188,7 @@ Finish:
    if (!NT_SUCCESS(Status))
        goto ByeBye;
 
-   IopDeviceNodeSetFlag(DeviceNode, DNF_RESOURCE_ASSIGNED);
-
-   IopDeviceNodeClearFlag(DeviceNode, DNF_ASSIGNING_RESOURCES);
+   PiSetDevNodeState(DeviceNode, DeviceNodeResourcesAssigned);
 
    return STATUS_SUCCESS;
 
@@ -1206,8 +1201,6 @@ ByeBye:
 
    DeviceNode->ResourceListTranslated = NULL;
 
-   IopDeviceNodeClearFlag(DeviceNode, DNF_ASSIGNING_RESOURCES);
-
    return Status;
 }
 
index 7b1c0f9..b815c62 100644 (file)
@@ -157,6 +157,7 @@ list(APPEND SOURCE
     ${REACTOS_SOURCE_DIR}/ntoskrnl/io/pnpmgr/plugplay.c
     ${REACTOS_SOURCE_DIR}/ntoskrnl/io/pnpmgr/pnpdma.c
     ${REACTOS_SOURCE_DIR}/ntoskrnl/io/pnpmgr/pnpinit.c
+    ${REACTOS_SOURCE_DIR}/ntoskrnl/io/pnpmgr/pnpirp.c
     ${REACTOS_SOURCE_DIR}/ntoskrnl/io/pnpmgr/pnpmgr.c
     ${REACTOS_SOURCE_DIR}/ntoskrnl/io/pnpmgr/pnpnotify.c
     ${REACTOS_SOURCE_DIR}/ntoskrnl/io/pnpmgr/pnpreport.c
index 84cf6c1..ace4d6f 100644 (file)
@@ -164,44 +164,53 @@ extern POBJECT_TYPE NTSYSAPI IoDriverObjectType;
 //
 // Device Node Flags
 //
-#define DNF_PROCESSED                           0x00000001
-#define DNF_STARTED                             0x00000002
-#define DNF_START_FAILED                        0x00000004
-#define DNF_ENUMERATED                          0x00000008
-#define DNF_DELETED                             0x00000010
-#define DNF_MADEUP                              0x00000020
-#define DNF_START_REQUEST_PENDING               0x00000040
-#define DNF_NO_RESOURCE_REQUIRED                0x00000080
-#define DNF_INSUFFICIENT_RESOURCES              0x00000100
-#define DNF_RESOURCE_ASSIGNED                   0x00000200
-#define DNF_RESOURCE_REPORTED                   0x00000400
-#define DNF_HAL_NODE                            0x00000800 // ???
-#define DNF_ADDED                               0x00001000
-#define DNF_ADD_FAILED                          0x00002000
-#define DNF_LEGACY_DRIVER                       0x00004000
-#define DNF_STOPPED                             0x00008000
-#define DNF_WILL_BE_REMOVED                     0x00010000
+
+// this set of flags is relevant for w2k3 and newer
+// w2k has a completely different set of flags
+#define DNF_MADEUP                              0x00000001
+#define DNF_DUPLICATE                           0x00000002
+#define DNF_HAL_NODE                            0x00000004
+#define DNF_REENUMERATE                         0x00000008
+#define DNF_ENUMERATED                          0x00000010
+#define DNF_IDS_QUERIED                         0x00000020
+#define DNF_HAS_BOOT_CONFIG                     0x00000040
+#define DNF_BOOT_CONFIG_RESERVED                0x00000080
+#define DNF_NO_RESOURCE_REQUIRED                0x00000100
+#define DNF_RESOURCE_REQUIREMENTS_NEED_FILTERED 0x00000200
+#define DNF_RESOURCE_REQUIREMENTS_CHANGED       0x00000400
+#define DNF_NON_STOPPED_REBALANCE               0x00000800
+#define DNF_LEGACY_DRIVER                       0x00001000
+#define DNF_HAS_PROBLEM                         0x00002000
+#define DNF_HAS_PRIVATE_PROBLEM                 0x00004000
+#define DNF_HARDWARE_VERIFICATION               0x00008000
+#define DNF_DEVICE_GONE                         0x00010000
 #define DNF_LEGACY_RESOURCE_DEVICENODE          0x00020000
-#define DNF_NOT_CONFIGURED                      0x00040000
-#define DNF_REINSTALL                           0x00080000
-#define DNF_RESOURCE_REQUIREMENTS_NEED_FILTERED 0x00100000 // ???
-#define DNF_DISABLED                            0x00200000
-#define DNF_RESTART_OK                          0x00400000
-#define DNF_NEED_RESTART                        0x00800000
-#define DNF_VISITED                             0x01000000
-#define DNF_ASSIGNING_RESOURCES                 0x02000000
-#define DNF_BEEING_ENUMERATED                   0x04000000
-#define DNF_NEED_ENUMERATION_ONLY               0x08000000
-#define DNF_LOCKED                              0x10000000
-#define DNF_HAS_BOOT_CONFIG                     0x20000000
-#define DNF_BOOT_CONFIG_RESERVED                0x40000000
-#define DNF_HAS_PROBLEM                         0x80000000 // ???
+#define DNF_NEEDS_REBALANCE                     0x00040000
+#define DNF_LOCKED_FOR_EJECT                    0x00080000
+#define DNF_DRIVER_BLOCKED                      0x00100000
+#define DNF_CHILD_WITH_INVALID_ID               0x00200000
+
+// these flags were added in Vista or later
+#define DNF_ASYNC_START_NOT_SUPPORTED           0x00400000
+#define DNF_ASYNC_ENUMERATION_NOT_SUPPORTED     0x00800000
+#define DNF_LOCKED_FOR_REBALANCE                0x01000000
+#define DNF_UNINSTALLED                         0x02000000
+#define DNF_NO_LOWER_DEVICE_FILTERS             0x04000000
+#define DNF_NO_LOWER_CLASS_FILTERS              0x08000000
+#define DNF_NO_SERVICE                          0x10000000
+#define DNF_NO_UPPER_DEVICE_FILTERS             0x20000000
+#define DNF_NO_UPPER_CLASS_FILTERS              0x40000000
+#define DNF_WAITING_FOR_FDO                     0x80000000
 
 //
 // Device Node User Flags
 //
+#define DNUF_WILL_BE_REMOVED                    0x0001
 #define DNUF_DONT_SHOW_IN_UI                    0x0002
+#define DNUF_NEED_RESTART                       0x0004
 #define DNUF_NOT_DISABLEABLE                    0x0008
+#define DNUF_SHUTDOWN_QUERIED                   0x0010
+#define DNUF_SHUTDOWN_SUBTREE_DONE              0x0020
 
 //
 // Internal Option Flags
@@ -815,6 +824,8 @@ typedef struct _IO_CLIENT_EXTENSION
     PVOID ClientIdentificationAddress;
 } IO_CLIENT_EXTENSION, *PIO_CLIENT_EXTENSION;
 
+#define DEVNODE_HISTORY_SIZE 20
+
 //
 // Device Node
 //
@@ -829,7 +840,7 @@ typedef struct _DEVICE_NODE
     PO_IRP_MANAGER PoIrpManager;
     PNP_DEVNODE_STATE State;
     PNP_DEVNODE_STATE PreviousState;
-    PNP_DEVNODE_STATE StateHistory[20];
+    PNP_DEVNODE_STATE StateHistory[DEVNODE_HISTORY_SIZE];
     ULONG StateHistoryEntry;
     NTSTATUS CompletionStatus;
     PIRP PendingIrp;