[SHELL32] SHChangeNotify: Use tree for CDirectoryList (#6784)
[reactos.git] / ntoskrnl / io / pnpmgr / plugplay.c
index 8b088f5..63556d0 100644 (file)
@@ -3,7 +3,7 @@
  * COPYRIGHT:       GPL - See COPYING in the top level directory
  * FILE:            ntoskrnl/io/pnpmgr/plugplay.c
  * PURPOSE:         Plug-and-play interface routines
- * PROGRAMMERS:     Eric Kohl <eric.kohl@t-online.de>
+ * PROGRAMMERS:     Eric Kohl <eric.kohl@reactos.org>
  */
 
 /* INCLUDES *****************************************************************/
 #define NDEBUG
 #include <debug.h>
 
-#if defined (ALLOC_PRAGMA)
-#pragma alloc_text(INIT, IopInitPlugPlayEvents)
-#endif
-
 typedef struct _PNP_EVENT_ENTRY
 {
     LIST_ENTRY ListEntry;
     PLUGPLAY_EVENT_BLOCK Event;
 } PNP_EVENT_ENTRY, *PPNP_EVENT_ENTRY;
 
+typedef struct _IOP_FIND_DEVICE_INSTANCE_TRAVERSE_CONTEXT
+{
+    PCUNICODE_STRING InstancePath;
+    PDEVICE_OBJECT DeviceObject;
+} IOP_FIND_DEVICE_INSTANCE_TRAVERSE_CONTEXT, *PIOP_FIND_DEVICE_INSTANCE_TRAVERSE_CONTEXT;
+
 
 /* GLOBALS *******************************************************************/
 
@@ -30,7 +32,11 @@ static KEVENT IopPnpNotifyEvent;
 
 /* FUNCTIONS *****************************************************************/
 
-NTSTATUS INIT_FUNCTION
+NTSTATUS
+IopSetDeviceInstanceData(HANDLE InstanceKey, PDEVICE_NODE DeviceNode);
+
+CODE_SEG("INIT")
+NTSTATUS
 IopInitPlugPlayEvents(VOID)
 {
     InitializeListHead(&IopPnpEventQueueHead);
@@ -42,6 +48,91 @@ IopInitPlugPlayEvents(VOID)
     return STATUS_SUCCESS;
 }
 
+NTSTATUS
+IopQueueDeviceChangeEvent(
+    _In_ const GUID *EventGuid,
+    _In_ const GUID *InterfaceClassGuid,
+    _In_ PUNICODE_STRING SymbolicLinkName)
+{
+    PPNP_EVENT_ENTRY EventEntry;
+    UNICODE_STRING Copy;
+    ULONG TotalSize;
+
+    /* Allocate a big enough buffer */
+    Copy.Length = 0;
+    Copy.MaximumLength = SymbolicLinkName->Length + sizeof(UNICODE_NULL);
+    TotalSize =
+        FIELD_OFFSET(PLUGPLAY_EVENT_BLOCK, DeviceClass.SymbolicLinkName) +
+        Copy.MaximumLength;
+
+    EventEntry = ExAllocatePool(NonPagedPool,
+                                TotalSize + FIELD_OFFSET(PNP_EVENT_ENTRY, Event));
+    if (!EventEntry)
+        return STATUS_INSUFFICIENT_RESOURCES;
+    RtlZeroMemory(EventEntry, TotalSize + FIELD_OFFSET(PNP_EVENT_ENTRY, Event));
+
+    /* Fill the buffer with the event GUID */
+    RtlCopyMemory(&EventEntry->Event.EventGuid, EventGuid, sizeof(GUID));
+    EventEntry->Event.EventCategory = DeviceClassChangeEvent;
+    EventEntry->Event.TotalSize = TotalSize;
+
+    /* Fill the interface class GUID */
+    RtlCopyMemory(&EventEntry->Event.DeviceClass.ClassGuid, InterfaceClassGuid, sizeof(GUID));
+
+    /* Fill the symbolic link name */
+    RtlCopyMemory(&EventEntry->Event.DeviceClass.SymbolicLinkName,
+                  SymbolicLinkName->Buffer, SymbolicLinkName->Length);
+    EventEntry->Event.DeviceClass.SymbolicLinkName[SymbolicLinkName->Length / sizeof(WCHAR)] = UNICODE_NULL;
+
+    InsertHeadList(&IopPnpEventQueueHead,
+                   &EventEntry->ListEntry);
+    KeSetEvent(&IopPnpNotifyEvent,
+               0,
+               FALSE);
+
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS
+IopQueueDeviceInstallEvent(
+    _In_ const GUID *EventGuid,
+    _In_ PUNICODE_STRING DeviceId)
+{
+    PPNP_EVENT_ENTRY EventEntry;
+    UNICODE_STRING Copy;
+    ULONG TotalSize;
+
+    /* Allocate a big enough buffer */
+    Copy.Length = 0;
+    Copy.MaximumLength = DeviceId->Length + sizeof(UNICODE_NULL);
+    TotalSize =
+        FIELD_OFFSET(PLUGPLAY_EVENT_BLOCK, InstallDevice.DeviceId) +
+        Copy.MaximumLength;
+
+    EventEntry = ExAllocatePool(NonPagedPool,
+                                TotalSize + FIELD_OFFSET(PNP_EVENT_ENTRY, Event));
+    if (!EventEntry)
+        return STATUS_INSUFFICIENT_RESOURCES;
+    RtlZeroMemory(EventEntry, TotalSize + FIELD_OFFSET(PNP_EVENT_ENTRY, Event));
+
+    /* Fill the buffer with the event GUID */
+    RtlCopyMemory(&EventEntry->Event.EventGuid, EventGuid, sizeof(GUID));
+    EventEntry->Event.EventCategory = DeviceInstallEvent;
+    EventEntry->Event.TotalSize = TotalSize;
+
+    /* Fill the symbolic link name */
+    RtlCopyMemory(&EventEntry->Event.InstallDevice.DeviceId,
+                  DeviceId->Buffer, DeviceId->Length);
+    EventEntry->Event.InstallDevice.DeviceId[DeviceId->Length / sizeof(WCHAR)] = UNICODE_NULL;
+
+    InsertHeadList(&IopPnpEventQueueHead, &EventEntry->ListEntry);
+
+    KeSetEvent(&IopPnpNotifyEvent, 0, FALSE);
+
+    return STATUS_SUCCESS;
+}
+
+
 NTSTATUS
 IopQueueTargetDeviceEvent(const GUID *Guid,
                           PUNICODE_STRING DeviceIds)
@@ -64,6 +155,7 @@ IopQueueTargetDeviceEvent(const GUID *Guid,
                                 TotalSize + FIELD_OFFSET(PNP_EVENT_ENTRY, Event));
     if (!EventEntry)
         return STATUS_INSUFFICIENT_RESOURCES;
+    RtlZeroMemory(EventEntry, TotalSize + FIELD_OFFSET(PNP_EVENT_ENTRY, Event));
 
     /* Fill the buffer with the event GUID */
     RtlCopyMemory(&EventEntry->Event.EventGuid,
@@ -76,7 +168,10 @@ IopQueueTargetDeviceEvent(const GUID *Guid,
     Copy.Buffer = EventEntry->Event.TargetDevice.DeviceIds;
     Status = RtlAppendUnicodeStringToString(&Copy, DeviceIds);
     if (!NT_SUCCESS(Status))
+    {
+        ExFreePool(EventEntry);
         return Status;
+    }
 
     InsertHeadList(&IopPnpEventQueueHead,
                    &EventEntry->ListEntry);
@@ -87,63 +182,32 @@ IopQueueTargetDeviceEvent(const GUID *Guid,
     return STATUS_SUCCESS;
 }
 
-
-/*
- * Remove the current PnP event from the tail of the event queue
- * and signal IopPnpNotifyEvent if there is yet another event in the queue.
- */
-static NTSTATUS
-IopRemovePlugPlayEvent(VOID)
-{
-    /* Remove a pnp event entry from the tail of the queue */
-    if (!IsListEmpty(&IopPnpEventQueueHead))
-    {
-        ExFreePool(CONTAINING_RECORD(RemoveTailList(&IopPnpEventQueueHead), PNP_EVENT_ENTRY, ListEntry));
-    }
-
-    /* Signal the next pnp event in the queue */
-    if (!IsListEmpty(&IopPnpEventQueueHead))
-    {
-        KeSetEvent(&IopPnpNotifyEvent,
-                   0,
-                   FALSE);
-    }
-
-    return STATUS_SUCCESS;
-}
-
-static PDEVICE_OBJECT
-IopTraverseDeviceNode(PDEVICE_NODE Node, PUNICODE_STRING DeviceInstance)
+NTSTATUS
+IopFindDeviceInstanceTraverse(
+    _In_ PDEVICE_NODE DeviceNode,
+    _Inout_ PVOID Context)
 {
-    PDEVICE_OBJECT DeviceObject;
-    PDEVICE_NODE ChildNode;
+    PIOP_FIND_DEVICE_INSTANCE_TRAVERSE_CONTEXT DeviceInstanceContext = Context;
 
-    if (RtlEqualUnicodeString(&Node->InstancePath,
-                              DeviceInstance, TRUE))
+    if (RtlEqualUnicodeString(&DeviceNode->InstancePath,
+                              DeviceInstanceContext->InstancePath, TRUE))
     {
-        ObReferenceObject(Node->PhysicalDeviceObject);
-        return Node->PhysicalDeviceObject;
-    }
+        ObReferenceObject(DeviceNode->PhysicalDeviceObject);
+        DeviceInstanceContext->DeviceObject = DeviceNode->PhysicalDeviceObject;
 
-    /* Traversal of all children nodes */
-    for (ChildNode = Node->Child;
-         ChildNode != NULL;
-         ChildNode = ChildNode->Sibling)
-    {
-        DeviceObject = IopTraverseDeviceNode(ChildNode, DeviceInstance);
-        if (DeviceObject != NULL)
-        {
-            return DeviceObject;
-        }
+        /* Stop enumeration */
+        return STATUS_UNSUCCESSFUL;
     }
 
-    return NULL;
+    return STATUS_SUCCESS;
 }
 
-
 PDEVICE_OBJECT
 IopGetDeviceObjectFromDeviceInstance(PUNICODE_STRING DeviceInstance)
 {
+    DEVICETREE_TRAVERSE_CONTEXT Context;
+    IOP_FIND_DEVICE_INSTANCE_TRAVERSE_CONTEXT DeviceInstanceContext;
+
     if (IopRootDeviceNode == NULL)
         return NULL;
 
@@ -159,8 +223,17 @@ IopGetDeviceObjectFromDeviceInstance(PUNICODE_STRING DeviceInstance)
             return NULL;
     }
 
-    return IopTraverseDeviceNode(IopRootDeviceNode, DeviceInstance);
-
+    /* Traverse the device tree to find the matching device node */
+    DeviceInstanceContext.InstancePath = DeviceInstance;
+    DeviceInstanceContext.DeviceObject = NULL;
+    IopInitDeviceTreeTraverseContext(&Context,
+                                     IopRootDeviceNode,
+                                     IopFindDeviceInstanceTraverse,
+                                     &DeviceInstanceContext);
+    (void)IopTraverseDeviceTree(&Context);
+
+    /* In case of error or instance not found, this will still be NULL from above. */
+    return DeviceInstanceContext.DeviceObject;
 }
 
 static NTSTATUS
@@ -200,7 +273,9 @@ IopCaptureUnicodeString(PUNICODE_STRING DstName, PUNICODE_STRING SrcName)
     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
     {
         if (Name.Buffer)
+        {
             ExFreePool(Name.Buffer);
+        }
         Status = _SEH2_GetExceptionCode();
     }
     _SEH2_END;
@@ -208,15 +283,221 @@ IopCaptureUnicodeString(PUNICODE_STRING DstName, PUNICODE_STRING SrcName)
     return Status;
 }
 
+
+static
+NTSTATUS
+PiControlInitializeDevice(
+    _In_ PPLUGPLAY_CONTROL_DEVICE_CONTROL_DATA ControlData)
+{
+    UNICODE_STRING DeviceInstance;
+    PDEVICE_OBJECT DeviceObject;
+    PDEVICE_NODE DeviceNode;
+    NTSTATUS Status = STATUS_SUCCESS;
+    HANDLE InstanceKey;
+
+    DPRINT("PiControlInitializeDevice(%p)\n", ControlData);
+
+    Status = IopCaptureUnicodeString(&DeviceInstance, &ControlData->DeviceInstance);
+    if (!NT_SUCCESS(Status))
+    {
+        return Status;
+    }
+
+    DPRINT("Device: %wZ\n", &DeviceInstance);
+
+    /* Leave, if the device already exists */
+    DeviceObject = IopGetDeviceObjectFromDeviceInstance(&DeviceInstance);
+    if (DeviceObject != NULL)
+    {
+        DPRINT1("Device %wZ already exists!\n", &DeviceInstance);
+        ObDereferenceObject(DeviceObject);
+        Status = STATUS_SUCCESS;
+        goto done;
+    }
+
+    DPRINT("Device %wZ does not exist!\n", &DeviceInstance);
+
+    /* Create a device node for the device instance */
+    Status = PnpRootCreateDeviceObject(&DeviceObject);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("IoCreateDevice() failed (Status 0x%08lx)\n", Status);
+        goto done;
+    }
+
+    /* Allocate a new device node */
+    DeviceNode = PipAllocateDeviceNode(DeviceObject);
+    if (DeviceNode == NULL)
+    {
+        DPRINT1("Failed to allocate a device node!\n");
+        IoDeleteDevice(DeviceObject);
+        Status = STATUS_INSUFFICIENT_RESOURCES;
+        goto done;
+    }
+
+    // Set the device instance of the device node
+    // NOTE: a NULL-terminated string is required for PnpRootRegisterDevice
+    Status = RtlDuplicateUnicodeString(
+                RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE,
+                &DeviceInstance,
+                &DeviceNode->InstancePath);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("RtlDuplicateUnicodeString() failed (Status 0x%08lx)\n", Status);
+        IopFreeDeviceNode(DeviceNode);
+        IoDeleteDevice(DeviceObject);
+        goto done;
+    }
+
+    DeviceObject->Flags |= DO_BUS_ENUMERATED_DEVICE;
+    DeviceNode->Flags |= DNF_MADEUP | DNF_IDS_QUERIED | DNF_ENUMERATED;
+    PiSetDevNodeState(DeviceNode, DeviceNodeInitialized);
+
+    Status = IopCreateDeviceKeyPath(&DeviceInstance, REG_OPTION_NON_VOLATILE, &InstanceKey);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("Failed to create the instance key! (Status %lx)\n", Status);
+        IopFreeDeviceNode(DeviceNode);
+        IoDeleteDevice(DeviceObject);
+        goto done;
+    }
+
+    /* Write the resource information to the registry */
+    IopSetDeviceInstanceData(InstanceKey, DeviceNode);
+
+    // Finish the root device registration
+    PnpRootRegisterDevice(DeviceObject);
+
+    /* Insert as a root enumerated device node */
+    PiInsertDevNode(DeviceNode, IopRootDeviceNode);
+
+    /* Report the device to the user-mode pnp manager */
+    IopQueueDeviceInstallEvent(&GUID_DEVICE_ENUMERATED, &DeviceNode->InstancePath);
+
+    ZwClose(InstanceKey);
+done:
+    ExFreePool(DeviceInstance.Buffer);
+
+    return Status;
+}
+
+
+/*
+ * Remove the current PnP event from the tail of the event queue
+ * and signal IopPnpNotifyEvent if there is yet another event in the queue.
+ */
+static
+NTSTATUS
+IopRemovePlugPlayEvent(
+    _In_ PPLUGPLAY_CONTROL_USER_RESPONSE_DATA ResponseData)
+{
+    /* Remove a pnp event entry from the tail of the queue */
+    if (!IsListEmpty(&IopPnpEventQueueHead))
+    {
+        ExFreePool(CONTAINING_RECORD(RemoveTailList(&IopPnpEventQueueHead), PNP_EVENT_ENTRY, ListEntry));
+    }
+
+    /* Signal the next pnp event in the queue */
+    if (!IsListEmpty(&IopPnpEventQueueHead))
+    {
+        KeSetEvent(&IopPnpNotifyEvent,
+                   0,
+                   FALSE);
+    }
+
+    return STATUS_SUCCESS;
+}
+
+
+static NTSTATUS
+IopGetInterfaceDeviceList(PPLUGPLAY_CONTROL_INTERFACE_DEVICE_LIST_DATA DeviceList)
+{
+    NTSTATUS Status;
+    PLUGPLAY_CONTROL_INTERFACE_DEVICE_LIST_DATA StackList;
+    UNICODE_STRING DeviceInstance;
+    PDEVICE_OBJECT DeviceObject = NULL;
+    GUID FilterGuid;
+    PZZWSTR SymbolicLinkList = NULL, LinkList;
+    SIZE_T TotalLength;
+
+    _SEH2_TRY
+    {
+        RtlCopyMemory(&StackList, DeviceList, sizeof(PLUGPLAY_CONTROL_INTERFACE_DEVICE_LIST_DATA));
+
+        ProbeForRead(StackList.FilterGuid, sizeof(GUID), sizeof(UCHAR));
+        RtlCopyMemory(&FilterGuid, StackList.FilterGuid, sizeof(GUID));
+
+        if (StackList.Buffer != NULL && StackList.BufferSize != 0)
+        {
+            ProbeForWrite(StackList.Buffer, StackList.BufferSize, sizeof(UCHAR));
+        }
+    }
+    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+    {
+        _SEH2_YIELD(return _SEH2_GetExceptionCode());
+    }
+    _SEH2_END;
+
+    Status = IopCaptureUnicodeString(&DeviceInstance, &StackList.DeviceInstance);
+    if (NT_SUCCESS(Status))
+    {
+        /* Get the device object */
+        DeviceObject = IopGetDeviceObjectFromDeviceInstance(&DeviceInstance);
+        if (DeviceInstance.Buffer != NULL)
+        {
+            ExFreePool(DeviceInstance.Buffer);
+        }
+    }
+
+    Status = IoGetDeviceInterfaces(&FilterGuid, DeviceObject, StackList.Flags, &SymbolicLinkList);
+    ObDereferenceObject(DeviceObject);
+
+    if (!NT_SUCCESS(Status))
+    {
+        /* failed */
+        return Status;
+    }
+
+    LinkList = SymbolicLinkList;
+    while (*SymbolicLinkList != UNICODE_NULL)
+    {
+        SymbolicLinkList += wcslen(SymbolicLinkList) + (sizeof(UNICODE_NULL) / sizeof(WCHAR));
+    }
+    TotalLength = ((SymbolicLinkList - LinkList + 1) * sizeof(WCHAR));
+
+    _SEH2_TRY
+    {
+        if (StackList.Buffer != NULL &&
+            StackList.BufferSize >= TotalLength)
+        {
+            // We've already probed the buffer for writing above.
+            RtlCopyMemory(StackList.Buffer, LinkList, TotalLength);
+        }
+
+        DeviceList->BufferSize = TotalLength;
+    }
+    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+    {
+        ExFreePool(LinkList);
+        _SEH2_YIELD(return _SEH2_GetExceptionCode());
+    }
+    _SEH2_END;
+
+    ExFreePool(LinkList);
+    return STATUS_SUCCESS;
+}
+
 static NTSTATUS
 IopGetDeviceProperty(PPLUGPLAY_CONTROL_PROPERTY_DATA PropertyData)
 {
     PDEVICE_OBJECT DeviceObject = NULL;
-    NTSTATUS Status;
+    PDEVICE_NODE DeviceNode;
     UNICODE_STRING DeviceInstance;
     ULONG BufferSize;
-    ULONG Property = 0;
+    ULONG Property;
+    DEVICE_REGISTRY_PROPERTY DeviceProperty;
     PVOID Buffer;
+    NTSTATUS Status;
 
     DPRINT("IopGetDeviceProperty() called\n");
     DPRINT("Device name: %wZ\n", &PropertyData->DeviceInstance);
@@ -237,14 +518,20 @@ IopGetDeviceProperty(PPLUGPLAY_CONTROL_PROPERTY_DATA PropertyData)
     }
     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
     {
-        ExFreePool(DeviceInstance.Buffer);
+        if (DeviceInstance.Buffer != NULL)
+        {
+            ExFreePool(DeviceInstance.Buffer);
+        }
         _SEH2_YIELD(return _SEH2_GetExceptionCode());
     }
     _SEH2_END;
 
     /* Get the device object */
     DeviceObject = IopGetDeviceObjectFromDeviceInstance(&DeviceInstance);
-    ExFreePool(DeviceInstance.Buffer);
+    if (DeviceInstance.Buffer != NULL)
+    {
+        ExFreePool(DeviceInstance.Buffer);
+    }
     if (DeviceObject == NULL)
     {
         return STATUS_NO_SUCH_DEVICE;
@@ -253,14 +540,169 @@ IopGetDeviceProperty(PPLUGPLAY_CONTROL_PROPERTY_DATA PropertyData)
     Buffer = ExAllocatePool(NonPagedPool, BufferSize);
     if (Buffer == NULL)
     {
+        ObDereferenceObject(DeviceObject);
         return STATUS_INSUFFICIENT_RESOURCES;
     }
 
-    Status = IoGetDeviceProperty(DeviceObject,
-                                 Property,
-                                 BufferSize,
-                                 Buffer,
-                                 &BufferSize);
+
+    DeviceNode = ((PEXTENDED_DEVOBJ_EXTENSION)DeviceObject->DeviceObjectExtension)->DeviceNode;
+
+    if (Property == PNP_PROPERTY_POWER_DATA)
+    {
+        if (BufferSize < sizeof(CM_POWER_DATA))
+        {
+            BufferSize = 0;
+            Status = STATUS_BUFFER_TOO_SMALL;
+        }
+        else
+        {
+            DEVICE_CAPABILITIES DeviceCapabilities;
+            PCM_POWER_DATA PowerData;
+            IO_STACK_LOCATION Stack;
+            IO_STATUS_BLOCK IoStatusBlock;
+
+            PowerData = (PCM_POWER_DATA)Buffer;
+            RtlZeroMemory(PowerData, sizeof(CM_POWER_DATA));
+            PowerData->PD_Size = sizeof(CM_POWER_DATA);
+
+            RtlZeroMemory(&DeviceCapabilities, sizeof(DEVICE_CAPABILITIES));
+            DeviceCapabilities.Size = sizeof(DEVICE_CAPABILITIES);
+            DeviceCapabilities.Version = 1;
+            DeviceCapabilities.Address = -1;
+            DeviceCapabilities.UINumber = -1;
+
+            Stack.Parameters.DeviceCapabilities.Capabilities = &DeviceCapabilities;
+
+            Status = IopInitiatePnpIrp(DeviceObject,
+                                       &IoStatusBlock,
+                                       IRP_MN_QUERY_CAPABILITIES,
+                                       &Stack);
+            if (NT_SUCCESS(Status))
+            {
+                DPRINT("Got device capabiliities\n");
+
+                PowerData->PD_MostRecentPowerState = PowerDeviceD0; // FIXME
+                if (DeviceCapabilities.DeviceD1)
+                    PowerData->PD_Capabilities |= PDCAP_D1_SUPPORTED;
+                if (DeviceCapabilities.DeviceD2)
+                    PowerData->PD_Capabilities |= PDCAP_D2_SUPPORTED;
+                if (DeviceCapabilities.WakeFromD0)
+                    PowerData->PD_Capabilities |= PDCAP_WAKE_FROM_D0_SUPPORTED;
+                if (DeviceCapabilities.WakeFromD1)
+                    PowerData->PD_Capabilities |= PDCAP_WAKE_FROM_D1_SUPPORTED;
+                if (DeviceCapabilities.WakeFromD2)
+                    PowerData->PD_Capabilities |= PDCAP_WAKE_FROM_D2_SUPPORTED;
+                if (DeviceCapabilities.WakeFromD3)
+                    PowerData->PD_Capabilities |= PDCAP_WAKE_FROM_D3_SUPPORTED;
+                if (DeviceCapabilities.WarmEjectSupported)
+                    PowerData->PD_Capabilities |= PDCAP_WARM_EJECT_SUPPORTED;
+                PowerData->PD_D1Latency = DeviceCapabilities.D1Latency;
+                PowerData->PD_D2Latency = DeviceCapabilities.D2Latency;
+                PowerData->PD_D3Latency = DeviceCapabilities.D3Latency;
+                RtlCopyMemory(&PowerData->PD_PowerStateMapping,
+                              &DeviceCapabilities.DeviceState,
+                              sizeof(DeviceCapabilities.DeviceState));
+                PowerData->PD_DeepestSystemWake = DeviceCapabilities.SystemWake;
+            }
+            else
+            {
+                DPRINT("IRP_MN_QUERY_CAPABILITIES failed (Status 0x%08lx)\n", Status);
+
+                PowerData->PD_Capabilities = PDCAP_D0_SUPPORTED | PDCAP_D3_SUPPORTED;
+                PowerData->PD_MostRecentPowerState = PowerDeviceD0;
+            }
+        }
+    }
+    else if (Property == PNP_PROPERTY_REMOVAL_POLICY_OVERRIDE)
+    {
+        UNIMPLEMENTED;
+        BufferSize = 0;
+        Status = STATUS_NOT_IMPLEMENTED;
+    }
+    else if (Property == PNP_PROPERTY_REMOVAL_POLICY_HARDWARE_DEFAULT)
+    {
+        if (BufferSize < sizeof(DeviceNode->HardwareRemovalPolicy))
+        {
+            BufferSize = 0;
+            Status = STATUS_BUFFER_TOO_SMALL;
+        }
+        else
+        {
+            BufferSize = sizeof(DeviceNode->HardwareRemovalPolicy);
+            RtlCopyMemory(Buffer,
+                          &DeviceNode->HardwareRemovalPolicy,
+                          BufferSize);
+        }
+    }
+    else
+    {
+        switch (Property)
+        {
+            case PNP_PROPERTY_UI_NUMBER:
+                DeviceProperty = DevicePropertyUINumber;
+                break;
+
+            case PNP_PROPERTY_PHYSICAL_DEVICE_OBJECT_NAME:
+                DeviceProperty = DevicePropertyPhysicalDeviceObjectName;
+                break;
+
+            case PNP_PROPERTY_BUSTYPEGUID:
+                DeviceProperty = DevicePropertyBusTypeGuid;
+                break;
+
+            case PNP_PROPERTY_LEGACYBUSTYPE:
+                DeviceProperty = DevicePropertyLegacyBusType;
+                break;
+
+            case PNP_PROPERTY_BUSNUMBER:
+                DeviceProperty = DevicePropertyBusNumber;
+                break;
+
+            case PNP_PROPERTY_REMOVAL_POLICY:
+                DeviceProperty = DevicePropertyRemovalPolicy;
+                break;
+
+            case PNP_PROPERTY_ADDRESS:
+                DeviceProperty = DevicePropertyAddress;
+                break;
+
+            case PNP_PROPERTY_ENUMERATOR_NAME:
+                DeviceProperty = DevicePropertyEnumeratorName;
+                break;
+
+            case PNP_PROPERTY_INSTALL_STATE:
+                DeviceProperty = DevicePropertyInstallState;
+                break;
+
+#if (WINVER >= _WIN32_WINNT_WS03)
+            case PNP_PROPERTY_LOCATION_PATHS:
+                UNIMPLEMENTED;
+                BufferSize = 0;
+                Status = STATUS_NOT_IMPLEMENTED;
+                break;
+#endif
+
+#if (WINVER >= _WIN32_WINNT_WIN7)
+            case PNP_PROPERTY_CONTAINERID:
+                DeviceProperty = DevicePropertyContainerID;
+                break;
+#endif
+
+            default:
+                BufferSize = 0;
+                Status = STATUS_INVALID_PARAMETER;
+                break;
+        }
+
+        if (Status == STATUS_SUCCESS)
+        {
+            Status = IoGetDeviceProperty(DeviceObject,
+                                         DeviceProperty,
+                                         BufferSize,
+                                         Buffer,
+                                         &BufferSize);
+        }
+    }
 
     ObDereferenceObject(DeviceObject);
 
@@ -268,7 +710,7 @@ IopGetDeviceProperty(PPLUGPLAY_CONTROL_PROPERTY_DATA PropertyData)
     {
         _SEH2_TRY
         {
-            memcpy(PropertyData->Buffer, Buffer, BufferSize);
+            RtlCopyMemory(PropertyData->Buffer, Buffer, BufferSize);
             PropertyData->BufferSize = BufferSize;
         }
         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
@@ -314,7 +756,10 @@ IopGetRelatedDevice(PPLUGPLAY_CONTROL_RELATED_DEVICE_DATA RelatedDeviceData)
     }
     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
     {
-        ExFreePool(TargetDeviceInstance.Buffer);
+        if (TargetDeviceInstance.Buffer != NULL)
+        {
+            ExFreePool(TargetDeviceInstance.Buffer);
+        }
         _SEH2_YIELD(return _SEH2_GetExceptionCode());
     }
     _SEH2_END;
@@ -326,13 +771,19 @@ IopGetRelatedDevice(PPLUGPLAY_CONTROL_RELATED_DEVICE_DATA RelatedDeviceData)
                               TRUE))
     {
         DeviceNode = IopRootDeviceNode;
-        ExFreePool(TargetDeviceInstance.Buffer);
+        if (TargetDeviceInstance.Buffer != NULL)
+        {
+            ExFreePool(TargetDeviceInstance.Buffer);
+        }
     }
     else
     {
         /* Get the device object */
         DeviceObject = IopGetDeviceObjectFromDeviceInstance(&TargetDeviceInstance);
-        ExFreePool(TargetDeviceInstance.Buffer);
+        if (TargetDeviceInstance.Buffer != NULL)
+        {
+            ExFreePool(TargetDeviceInstance.Buffer);
+        }
         if (DeviceObject == NULL)
             return STATUS_NO_SUCH_DEVICE;
 
@@ -406,37 +857,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->Flags & DNF_PROCESSED))
-        Output |= DN_NEED_TO_ENUM;
+    if (DeviceNode->UserFlags & DNUF_WILL_BE_REMOVED)
+        Output |= DN_WILL_BE_REMOVED;
 
-    /* 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;
@@ -447,10 +915,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;
 }
 
@@ -469,7 +933,10 @@ IopDeviceStatus(PPLUGPLAY_CONTROL_STATUS_DATA StatusData)
 
     Status = IopCaptureUnicodeString(&DeviceInstance, &StatusData->DeviceInstance);
     if (!NT_SUCCESS(Status))
+    {
         return Status;
+    }
+
     DPRINT("Device name: '%wZ'\n", &DeviceInstance);
 
     _SEH2_TRY
@@ -483,16 +950,24 @@ IopDeviceStatus(PPLUGPLAY_CONTROL_STATUS_DATA StatusData)
     }
     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
     {
-        if (DeviceInstance.Buffer) ExFreePool(DeviceInstance.Buffer);
+        if (DeviceInstance.Buffer != NULL)
+        {
+            ExFreePool(DeviceInstance.Buffer);
+        }
         _SEH2_YIELD(return _SEH2_GetExceptionCode());
     }
     _SEH2_END;
 
     /* Get the device object */
     DeviceObject = IopGetDeviceObjectFromDeviceInstance(&DeviceInstance);
-    ExFreePool(DeviceInstance.Buffer);
+    if (DeviceInstance.Buffer != NULL)
+    {
+        ExFreePool(DeviceInstance.Buffer);
+    }
     if (DeviceObject == NULL)
+    {
         return STATUS_NO_SUCH_DEVICE;
+    }
 
     DeviceNode = IopGetDeviceNode(DeviceObject);
 
@@ -532,6 +1007,163 @@ IopDeviceStatus(PPLUGPLAY_CONTROL_STATUS_DATA StatusData)
     return Status;
 }
 
+static
+NTSTATUS
+IopGetDeviceRelations(PPLUGPLAY_CONTROL_DEVICE_RELATIONS_DATA RelationsData)
+{
+    UNICODE_STRING DeviceInstance;
+    PDEVICE_OBJECT DeviceObject = NULL;
+    IO_STACK_LOCATION Stack;
+    IO_STATUS_BLOCK IoStatusBlock;
+    PDEVICE_RELATIONS DeviceRelations = NULL;
+    PDEVICE_OBJECT ChildDeviceObject;
+    PDEVICE_NODE ChildDeviceNode;
+    ULONG i;
+    ULONG Relations;
+    ULONG BufferSize, RequiredSize;
+    ULONG BufferLeft;
+    PWCHAR Buffer, Ptr;
+    NTSTATUS Status = STATUS_SUCCESS;
+
+    DPRINT("IopGetDeviceRelations() called\n");
+    DPRINT("Device name: %wZ\n", &RelationsData->DeviceInstance);
+    DPRINT("Relations: %lu\n", RelationsData->Relations);
+    DPRINT("BufferSize: %lu\n", RelationsData->BufferSize);
+    DPRINT("Buffer: %p\n", RelationsData->Buffer);
+
+    _SEH2_TRY
+    {
+        Relations = RelationsData->Relations;
+        BufferSize = RelationsData->BufferSize;
+        Buffer = RelationsData->Buffer;
+
+        ProbeForWrite(Buffer, BufferSize, sizeof(CHAR));
+    }
+    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+    {
+        _SEH2_YIELD(return _SEH2_GetExceptionCode());
+    }
+    _SEH2_END;
+
+    Status = IopCaptureUnicodeString(&DeviceInstance, &RelationsData->DeviceInstance);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("IopCaptureUnicodeString() failed (Status 0x%08lx)\n", Status);
+        return Status;
+    }
+
+    /* Get the device object */
+    DeviceObject = IopGetDeviceObjectFromDeviceInstance(&DeviceInstance);
+    if (DeviceObject == NULL)
+    {
+        DPRINT1("IopGetDeviceObjectFromDeviceInstance() returned NULL\n");
+        Status = STATUS_NO_SUCH_DEVICE;
+        goto done;
+    }
+
+    switch (Relations)
+    {
+        case PNP_EJECT_RELATIONS:
+            Stack.Parameters.QueryDeviceRelations.Type = EjectionRelations;
+            break;
+
+        case PNP_REMOVAL_RELATIONS:
+            Stack.Parameters.QueryDeviceRelations.Type = RemovalRelations;
+            break;
+
+        case PNP_POWER_RELATIONS:
+            Stack.Parameters.QueryDeviceRelations.Type = PowerRelations;
+            break;
+
+        case PNP_BUS_RELATIONS:
+            Stack.Parameters.QueryDeviceRelations.Type = BusRelations;
+            break;
+
+        default:
+            Status = STATUS_INVALID_PARAMETER;
+            goto done;
+    }
+
+    Status = IopInitiatePnpIrp(DeviceObject,
+                               &IoStatusBlock,
+                               IRP_MN_QUERY_DEVICE_RELATIONS,
+                               &Stack);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("IopInitiatePnpIrp() failed (Status 0x%08lx)\n", Status);
+        goto done;
+    }
+
+    DeviceRelations = (PDEVICE_RELATIONS)IoStatusBlock.Information;
+
+    DPRINT("Found %d device relations\n", DeviceRelations->Count);
+
+    _SEH2_TRY
+    {
+        RequiredSize = 0;
+        BufferLeft = BufferSize;
+        Ptr = Buffer;
+
+        for (i = 0; i < DeviceRelations->Count; i++)
+        {
+            ChildDeviceObject = DeviceRelations->Objects[i];
+
+            ChildDeviceNode = IopGetDeviceNode(ChildDeviceObject);
+            if (ChildDeviceNode)
+            {
+                DPRINT("Device instance: %wZ\n", &ChildDeviceNode->InstancePath);
+                DPRINT("RequiredSize: %hu\n", ChildDeviceNode->InstancePath.Length + sizeof(WCHAR));
+
+                if (Ptr != NULL)
+                {
+                    if (BufferLeft < ChildDeviceNode->InstancePath.Length + 2 * sizeof(WCHAR))
+                    {
+                        Status = STATUS_BUFFER_TOO_SMALL;
+                        break;
+                    }
+
+                    RtlCopyMemory(Ptr,
+                                  ChildDeviceNode->InstancePath.Buffer,
+                                  ChildDeviceNode->InstancePath.Length);
+                    Ptr = (PWCHAR)((ULONG_PTR)Ptr + ChildDeviceNode->InstancePath.Length);
+                    *Ptr = UNICODE_NULL;
+                    Ptr = (PWCHAR)((ULONG_PTR)Ptr + sizeof(WCHAR));
+
+                    BufferLeft -= (ChildDeviceNode->InstancePath.Length + sizeof(WCHAR));
+                }
+
+                RequiredSize += (ChildDeviceNode->InstancePath.Length + sizeof(WCHAR));
+            }
+        }
+
+        if (Ptr != NULL && BufferLeft >= sizeof(WCHAR))
+            *Ptr = UNICODE_NULL;
+
+        if (RequiredSize > 0)
+            RequiredSize += sizeof(WCHAR);
+
+        DPRINT("BufferSize: %lu  RequiredSize: %lu\n", RelationsData->BufferSize, RequiredSize);
+
+        RelationsData->BufferSize = RequiredSize;
+    }
+    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+    {
+        Status = _SEH2_GetExceptionCode();
+    }
+    _SEH2_END;
+
+done:
+    if (DeviceRelations != NULL)
+        ExFreePool(DeviceRelations);
+
+    if (DeviceObject != NULL)
+        ObDereferenceObject(DeviceObject);
+
+    if (DeviceInstance.Buffer != NULL)
+        ExFreePool(DeviceInstance.Buffer);
+
+    return Status;
+}
 
 static NTSTATUS
 IopGetDeviceDepth(PPLUGPLAY_CONTROL_DEPTH_DATA DepthData)
@@ -552,9 +1184,14 @@ IopGetDeviceDepth(PPLUGPLAY_CONTROL_DEPTH_DATA DepthData)
 
     /* Get the device object */
     DeviceObject = IopGetDeviceObjectFromDeviceInstance(&DeviceInstance);
-    ExFreePool(DeviceInstance.Buffer);
+    if (DeviceInstance.Buffer != NULL)
+    {
+        ExFreePool(DeviceInstance.Buffer);
+    }
     if (DeviceObject == NULL)
+    {
         return STATUS_NO_SUCH_DEVICE;
+    }
 
     DeviceNode = IopGetDeviceNode(DeviceObject);
 
@@ -573,70 +1210,89 @@ IopGetDeviceDepth(PPLUGPLAY_CONTROL_DEPTH_DATA DepthData)
     return Status;
 }
 
-
-static NTSTATUS
-IopResetDevice(PPLUGPLAY_CONTROL_RESET_DEVICE_DATA ResetDeviceData)
+static
+NTSTATUS
+PiControlSyncDeviceAction(
+    _In_ PPLUGPLAY_CONTROL_DEVICE_CONTROL_DATA DeviceData,
+    _In_ PLUGPLAY_CONTROL_CLASS ControlClass)
 {
     PDEVICE_OBJECT DeviceObject;
-    PDEVICE_NODE DeviceNode;
-    NTSTATUS Status = STATUS_SUCCESS;
+    NTSTATUS Status;
     UNICODE_STRING DeviceInstance;
 
-    Status = IopCaptureUnicodeString(&DeviceInstance, &ResetDeviceData->DeviceInstance);
+    ASSERT(ControlClass == PlugPlayControlEnumerateDevice ||
+           ControlClass == PlugPlayControlStartDevice ||
+           ControlClass == PlugPlayControlResetDevice);
+
+    Status = IopCaptureUnicodeString(&DeviceInstance, &DeviceData->DeviceInstance);
     if (!NT_SUCCESS(Status))
+    {
         return Status;
+    }
 
-    DPRINT("IopResetDevice(%wZ)\n", &DeviceInstance);
-
-    /* Get the device object */
     DeviceObject = IopGetDeviceObjectFromDeviceInstance(&DeviceInstance);
-    ExFreePool(DeviceInstance.Buffer);
+    if (DeviceInstance.Buffer != NULL)
+    {
+        ExFreePool(DeviceInstance.Buffer);
+    }
     if (DeviceObject == NULL)
+    {
         return STATUS_NO_SUCH_DEVICE;
+    }
 
-    /* Get the device node */
-    DeviceNode = IopGetDeviceNode(DeviceObject);
-
-    ASSERT(DeviceNode->Flags & DNF_ENUMERATED);
-    ASSERT(DeviceNode->Flags & DNF_PROCESSED);
+    DEVICE_ACTION Action;
 
-    /* Check if there's already a driver loaded for this device */
-    if (DeviceNode->Flags & DNF_ADDED)
+    switch (ControlClass)
     {
-#if 0
-        /* Remove the device node */
-        Status = IopRemoveDevice(DeviceNode);
-        if (NT_SUCCESS(Status))
-        {
-            /* 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 = IoSynchronousInvalidateDeviceRelations(DeviceNode->Parent->PhysicalDeviceObject, BusRelations);
-        }
-        else
-#endif
-        {
-            /* A driver has already been loaded for this device */
-            DPRINT1("A reboot is required for the current driver for '%wZ' to be replaced\n", &DeviceNode->InstancePath);
-            DeviceNode->Problem = CM_PROB_NEED_RESTART;
-        }
+        case PlugPlayControlEnumerateDevice:
+            Action = PiActionEnumDeviceTree;
+            break;
+        case PlugPlayControlStartDevice:
+            Action = PiActionStartDevice;
+            break;
+        case PlugPlayControlResetDevice:
+            Action = PiActionResetDevice;
+            break;
+        default:
+            UNREACHABLE;
+            break;
     }
-    else
-    {
-        /* FIXME: What if the device really is disabled? */
-        DeviceNode->Flags &= ~DNF_DISABLED;
-        DeviceNode->Problem = 0;
 
-        /* Load service data from the registry */
-        Status = IopActionConfigureChildServices(DeviceNode, DeviceNode->Parent);
+    Status = PiPerformSyncDeviceAction(DeviceObject, Action);
 
-        if (NT_SUCCESS(Status))
-        {
-            /* Start the service and begin PnP initialization of the device again */
-            DPRINT1("A new driver will be loaded for '%wZ' (no FDO above)\n", &DeviceNode->InstancePath);
-            Status = IopActionInitChildServices(DeviceNode, DeviceNode->Parent);
-        }
+    ObDereferenceObject(DeviceObject);
+
+    return Status;
+}
+
+static
+NTSTATUS
+PiControlQueryRemoveDevice(
+    _In_ PPLUGPLAY_CONTROL_QUERY_REMOVE_DATA ControlData)
+{
+    PDEVICE_OBJECT DeviceObject;
+    NTSTATUS Status;
+    UNICODE_STRING DeviceInstance;
+
+    Status = IopCaptureUnicodeString(&DeviceInstance, &ControlData->DeviceInstance);
+    if (!NT_SUCCESS(Status))
+    {
+        return Status;
+    }
+
+    DeviceObject = IopGetDeviceObjectFromDeviceInstance(&DeviceInstance);
+    if (DeviceInstance.Buffer != NULL)
+    {
+        ExFreePool(DeviceInstance.Buffer);
+    }
+    if (DeviceObject == NULL)
+    {
+        return STATUS_NO_SUCH_DEVICE;
     }
 
+    UNIMPLEMENTED;
+    Status = STATUS_NOT_IMPLEMENTED;
+
     ObDereferenceObject(DeviceObject);
 
     return Status;
@@ -734,12 +1390,13 @@ NtGetPlugPlayEvent(IN ULONG Reserved1,
     DPRINT("Waiting for pnp notification event\n");
     Status = KeWaitForSingleObject(&IopPnpNotifyEvent,
                                    UserRequest,
-                                   KernelMode,
+                                   UserMode,
                                    FALSE,
                                    NULL);
-    if (!NT_SUCCESS(Status))
+    if (!NT_SUCCESS(Status) || Status == STATUS_USER_APC)
     {
-        DPRINT1("KeWaitForSingleObject() failed (Status %lx)\n", Status);
+        DPRINT("KeWaitForSingleObject() failed (Status %lx)\n", Status);
+        ASSERT(Status == STATUS_USER_APC);
         return Status;
     }
 
@@ -756,9 +1413,20 @@ NtGetPlugPlayEvent(IN ULONG Reserved1,
     }
 
     /* Copy event data to the user buffer */
-    memcpy(Buffer,
-           &Entry->Event,
-           Entry->Event.TotalSize);
+    _SEH2_TRY
+    {
+        ProbeForWrite(Buffer,
+                      Entry->Event.TotalSize,
+                      sizeof(UCHAR));
+        RtlCopyMemory(Buffer,
+                      &Entry->Event,
+                      Entry->Event.TotalSize);
+    }
+    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+    {
+        _SEH2_YIELD(return _SEH2_GetExceptionCode());
+    }
+    _SEH2_END;
 
     DPRINT("NtGetPlugPlayEvent() done\n");
 
@@ -860,21 +1528,60 @@ NtPlugPlayControl(IN PLUGPLAY_CONTROL_CLASS PlugPlayControlClass,
 
     switch (PlugPlayControlClass)
     {
+        case PlugPlayControlEnumerateDevice:
+            if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_ENUMERATE_DEVICE_DATA))
+                return STATUS_INVALID_PARAMETER;
+            // the Flags field is not used anyway
+            return PiControlSyncDeviceAction((PPLUGPLAY_CONTROL_DEVICE_CONTROL_DATA)Buffer,
+                                             PlugPlayControlClass);
+
+//        case PlugPlayControlRegisterNewDevice:
+//        case PlugPlayControlDeregisterDevice:
+
+        case PlugPlayControlInitializeDevice:
+            if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_DEVICE_CONTROL_DATA))
+                return STATUS_INVALID_PARAMETER;
+            return PiControlInitializeDevice((PPLUGPLAY_CONTROL_DEVICE_CONTROL_DATA)Buffer);
+
+        case PlugPlayControlStartDevice:
+        case PlugPlayControlResetDevice:
+            if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_DEVICE_CONTROL_DATA))
+                return STATUS_INVALID_PARAMETER;
+            return PiControlSyncDeviceAction((PPLUGPLAY_CONTROL_DEVICE_CONTROL_DATA)Buffer,
+                                             PlugPlayControlClass);
+
+//        case PlugPlayControlUnlockDevice:
+        case PlugPlayControlQueryAndRemoveDevice:
+              if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_QUERY_REMOVE_DATA))
+                  return STATUS_INVALID_PARAMETER;
+              return PiControlQueryRemoveDevice((PPLUGPLAY_CONTROL_QUERY_REMOVE_DATA)Buffer);
+
         case PlugPlayControlUserResponse:
-            if (Buffer || BufferLength != 0)
+            if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_USER_RESPONSE_DATA))
                 return STATUS_INVALID_PARAMETER;
-            return IopRemovePlugPlayEvent();
+            return IopRemovePlugPlayEvent((PPLUGPLAY_CONTROL_USER_RESPONSE_DATA)Buffer);
+
+//        case PlugPlayControlGenerateLegacyDevice:
+
+        case PlugPlayControlGetInterfaceDeviceList:
+            if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_INTERFACE_DEVICE_LIST_DATA))
+                return STATUS_INVALID_PARAMETER;
+            return IopGetInterfaceDeviceList((PPLUGPLAY_CONTROL_INTERFACE_DEVICE_LIST_DATA)Buffer);
 
         case PlugPlayControlProperty:
             if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_PROPERTY_DATA))
                 return STATUS_INVALID_PARAMETER;
             return IopGetDeviceProperty((PPLUGPLAY_CONTROL_PROPERTY_DATA)Buffer);
 
+//        case PlugPlayControlDeviceClassAssociation:
+
         case PlugPlayControlGetRelatedDevice:
             if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_RELATED_DEVICE_DATA))
                 return STATUS_INVALID_PARAMETER;
             return IopGetRelatedDevice((PPLUGPLAY_CONTROL_RELATED_DEVICE_DATA)Buffer);
 
+//        case PlugPlayControlGetInterfaceDeviceAlias:
+
         case PlugPlayControlDeviceStatus:
             if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_STATUS_DATA))
                 return STATUS_INVALID_PARAMETER;
@@ -885,10 +1592,16 @@ NtPlugPlayControl(IN PLUGPLAY_CONTROL_CLASS PlugPlayControlClass,
                 return STATUS_INVALID_PARAMETER;
             return IopGetDeviceDepth((PPLUGPLAY_CONTROL_DEPTH_DATA)Buffer);
 
-        case PlugPlayControlResetDevice:
-            if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_RESET_DEVICE_DATA))
+        case PlugPlayControlQueryDeviceRelations:
+            if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_DEVICE_RELATIONS_DATA))
                 return STATUS_INVALID_PARAMETER;
-            return IopResetDevice((PPLUGPLAY_CONTROL_RESET_DEVICE_DATA)Buffer);
+            return IopGetDeviceRelations((PPLUGPLAY_CONTROL_DEVICE_RELATIONS_DATA)Buffer);
+
+//        case PlugPlayControlTargetDeviceRelation:
+//        case PlugPlayControlQueryConflictList:
+//        case PlugPlayControlRetrieveDock:
+//        case PlugPlayControlHaltDevice:
+//        case PlugPlayControlGetBlockedDriverList:
 
         default:
             return STATUS_NOT_IMPLEMENTED;