[UMPNPMGR][USETUP] Use PlugPlayControlStartDevice in usetup and umpnpmgr
[reactos.git] / base / setup / usetup / devinst.c
index bd9af3a..eee7c25 100644 (file)
@@ -1,9 +1,9 @@
 /*
  * COPYRIGHT:       See COPYING in the top level directory
  * PROJECT:         ReactOS text-mode setup
- * FILE:            base/setup/usetup/devinst.c
  * PURPOSE:         Device installation
  * PROGRAMMER:      HervĂ© Poussineau (hpoussin@reactos.org)
+ *                  Hermes Belusca-Maito
  */
 
 #include <usetup.h>
 #include <guiddef.h>
 #include <libs/umpnpmgr/sysguid.h>
 
-BOOLEAN
-ResetDevice(
-    IN LPCWSTR DeviceId)
+/* LOCALS *******************************************************************/
+
+static HANDLE hEnumKey = NULL;
+static HANDLE hServicesKey = NULL;
+
+static HANDLE hNoPendingInstalls = NULL;
+
+static HANDLE hPnpThread = NULL;
+static HANDLE hDeviceInstallThread = NULL;
+
+/* Device-install event list */
+static HANDLE hDeviceInstallListMutex = NULL;
+static LIST_ENTRY DeviceInstallListHead;
+static HANDLE hDeviceInstallListNotEmpty = NULL;
+
+typedef struct
+{
+    LIST_ENTRY ListEntry;
+    WCHAR DeviceIds[ANYSIZE_ARRAY];
+} DeviceInstallParams;
+
+/* FUNCTIONS ****************************************************************/
+
+static BOOLEAN
+AreDriversLoaded(
+    IN PCWSTR DeviceId)
 {
-    PLUGPLAY_CONTROL_RESET_DEVICE_DATA ResetDeviceData;
+    PLUGPLAY_CONTROL_STATUS_DATA PlugPlayData;
     NTSTATUS Status;
 
-    RtlInitUnicodeString(&ResetDeviceData.DeviceInstance, DeviceId);
-    Status = NtPlugPlayControl(PlugPlayControlResetDevice, &ResetDeviceData, sizeof(PLUGPLAY_CONTROL_RESET_DEVICE_DATA));
-    if (!NT_SUCCESS(Status))
+    RtlInitUnicodeString(&PlugPlayData.DeviceInstance, DeviceId);
+    PlugPlayData.Operation = PNP_GET_DEVICE_STATUS;
+
+    Status = NtPlugPlayControl(PlugPlayControlDeviceStatus, &PlugPlayData, sizeof(PlugPlayData));
+    if (NT_SUCCESS(Status))
+    {
+        return (_Bool)((PlugPlayData.DeviceStatus & DN_DRIVER_LOADED) &&
+                       !(PlugPlayData.DeviceStatus & DN_HAS_PROBLEM));
+    }
+    else
     {
-        DPRINT1("NtPlugPlayControl() failed with status 0x%08x\n", Status);
         return FALSE;
     }
-    return TRUE;
 }
 
-BOOLEAN
+static BOOLEAN
 InstallDriver(
     IN HINF hInf,
     IN HANDLE hServices,
@@ -40,26 +68,30 @@ InstallDriver(
     IN LPCWSTR DeviceId,
     IN LPCWSTR HardwareId)
 {
-    UNICODE_STRING PathPrefix = RTL_CONSTANT_STRING(L"System32\\DRIVERS\\");
     UNICODE_STRING ServiceU = RTL_CONSTANT_STRING(L"Service");
     UNICODE_STRING ErrorControlU = RTL_CONSTANT_STRING(L"ErrorControl");
-    UNICODE_STRING ImagePathU = RTL_CONSTANT_STRING(L"ImagePath");
     UNICODE_STRING StartU = RTL_CONSTANT_STRING(L"Start");
     UNICODE_STRING TypeU = RTL_CONSTANT_STRING(L"Type");
+    UNICODE_STRING UpperFiltersU = RTL_CONSTANT_STRING(L"UpperFilters");
+    PWSTR keyboardClass = L"kbdclass\0";
+    PWSTR partMgr = L"partmgr\0";
+
     UNICODE_STRING StringU;
     OBJECT_ATTRIBUTES ObjectAttributes;
     HANDLE hService;
     INFCONTEXT Context;
-    PWSTR Driver, ClassGuid, ImagePath, FullImagePath;
+    PCWSTR Driver, ClassGuid, ImagePath;
     ULONG dwValue;
     ULONG Disposition;
     NTSTATUS Status;
     BOOLEAN deviceInstalled = FALSE;
-    UNICODE_STRING UpperFiltersU = RTL_CONSTANT_STRING(L"UpperFilters");
-    PWSTR keyboardClass = L"kbdclass\0";
+
+    /* First check if the driver needs any action at all */
+    if (AreDriversLoaded(DeviceId))
+        return TRUE;
 
     /* Check if we know the hardware */
-    if (!SetupFindFirstLineW(hInf, L"HardwareIdsDatabase", HardwareId, &Context))
+    if (!SpInfFindFirstLine(hInf, L"HardwareIdsDatabase", HardwareId, &Context))
         return FALSE;
     if (!INF_GetDataField(&Context, 1, &Driver))
         return FALSE;
@@ -70,39 +102,36 @@ InstallDriver(
 
     /* Find associated driver name */
     /* FIXME: check in other sections too! */
-    if (!SetupFindFirstLineW(hInf, L"BootBusExtenders.Load", Driver, &Context)
-     && !SetupFindFirstLineW(hInf, L"BusExtenders.Load", Driver, &Context)
-     && !SetupFindFirstLineW(hInf, L"SCSI.Load", Driver, &Context)
-     && !SetupFindFirstLineW(hInf, L"InputDevicesSupport.Load", Driver, &Context)
-     && !SetupFindFirstLineW(hInf, L"Keyboard.Load", Driver, &Context))
+    if (!SpInfFindFirstLine(hInf, L"BootBusExtenders.Load", Driver, &Context)
+     && !SpInfFindFirstLine(hInf, L"BusExtenders.Load", Driver, &Context)
+     && !SpInfFindFirstLine(hInf, L"SCSI.Load", Driver, &Context)
+     && !SpInfFindFirstLine(hInf, L"InputDevicesSupport.Load", Driver, &Context)
+     && !SpInfFindFirstLine(hInf, L"Keyboard.Load", Driver, &Context))
     {
+        INF_FreeData(ClassGuid);
+        INF_FreeData(Driver);
         return FALSE;
     }
 
     if (!INF_GetDataField(&Context, 1, &ImagePath))
-        return FALSE;
-
-    /* Prepare full driver path */
-    dwValue = PathPrefix.MaximumLength + wcslen(ImagePath) * sizeof(WCHAR);
-    FullImagePath = (PWSTR)RtlAllocateHeap(ProcessHeap, 0, dwValue);
-    if (!FullImagePath)
     {
-        DPRINT1("RtlAllocateHeap() failed\n");
+        INF_FreeData(ClassGuid);
+        INF_FreeData(Driver);
         return FALSE;
     }
-    RtlCopyMemory(FullImagePath, PathPrefix.Buffer, PathPrefix.MaximumLength);
-    ConcatPaths(FullImagePath, dwValue / sizeof(WCHAR), 1, ImagePath);
 
     DPRINT1("Using driver '%S' for device '%S'\n", ImagePath, DeviceId);
 
     /* Create service key */
     RtlInitUnicodeString(&StringU, Driver);
-    InitializeObjectAttributes(&ObjectAttributes, &StringU, 0, hServices, NULL);
+    InitializeObjectAttributes(&ObjectAttributes, &StringU, OBJ_CASE_INSENSITIVE, hServices, NULL);
     Status = NtCreateKey(&hService, KEY_SET_VALUE, &ObjectAttributes, 0, NULL, REG_OPTION_NON_VOLATILE, &Disposition);
     if (!NT_SUCCESS(Status))
     {
         DPRINT1("NtCreateKey('%wZ') failed with status 0x%08x\n", &StringU, Status);
-        RtlFreeHeap(ProcessHeap, 0, FullImagePath);
+        INF_FreeData(ImagePath);
+        INF_FreeData(ClassGuid);
+        INF_FreeData(Driver);
         return FALSE;
     }
 
@@ -110,39 +139,34 @@ InstallDriver(
     if (Disposition == REG_CREATED_NEW_KEY)
     {
         dwValue = 0;
-        NtSetValueKey(
-            hService,
-            &ErrorControlU,
-            0,
-            REG_DWORD,
-            &dwValue,
-            sizeof(dwValue));
+        NtSetValueKey(hService,
+                      &ErrorControlU,
+                      0,
+                      REG_DWORD,
+                      &dwValue,
+                      sizeof(dwValue));
+
         dwValue = 0;
-        NtSetValueKey(
-            hService,
-            &StartU,
-            0,
-            REG_DWORD,
-            &dwValue,
-            sizeof(dwValue));
+        NtSetValueKey(hService,
+                      &StartU,
+                      0,
+                      REG_DWORD,
+                      &dwValue,
+                      sizeof(dwValue));
+
         dwValue = SERVICE_KERNEL_DRIVER;
-        NtSetValueKey(
-            hService,
-            &TypeU,
-            0,
-            REG_DWORD,
-            &dwValue,
-            sizeof(dwValue));
+        NtSetValueKey(hService,
+                      &TypeU,
+                      0,
+                      REG_DWORD,
+                      &dwValue,
+                      sizeof(dwValue));
     }
-    /* HACK: don't put any path in registry */
-    NtSetValueKey(
-        hService,
-        &ImagePathU,
-        0,
-        REG_SZ,
-        ImagePath,
-        (wcslen(ImagePath) + 1) * sizeof(WCHAR));
 
+    INF_FreeData(ImagePath);
+    NtClose(hService);
+
+    /* Add kbdclass and partmgr upper filters */
     if (ClassGuid &&_wcsicmp(ClassGuid, L"{4D36E96B-E325-11CE-BFC1-08002BE10318}") == 0)
     {
         DPRINT1("Installing keyboard class driver for '%S'\n", DeviceId);
@@ -153,36 +177,47 @@ InstallDriver(
                       keyboardClass,
                       (wcslen(keyboardClass) + 2) * sizeof(WCHAR));
     }
+    else if (ClassGuid && _wcsicmp(ClassGuid, L"{4D36E967-E325-11CE-BFC1-08002BE10318}") == 0)
+    {
+        DPRINT1("Installing partition manager driver for '%S'\n", DeviceId);
+        NtSetValueKey(hDeviceKey,
+                      &UpperFiltersU,
+                      0,
+                      REG_MULTI_SZ,
+                      partMgr,
+                      (wcslen(partMgr) + 2) * sizeof(WCHAR));
+    }
+
+    INF_FreeData(ClassGuid);
 
     /* Associate device with the service we just filled */
-    Status = NtSetValueKey(
-        hDeviceKey,
-        &ServiceU,
-        0,
-        REG_SZ,
-        Driver,
-        (wcslen(Driver) + 1) * sizeof(WCHAR));
+    Status = NtSetValueKey(hDeviceKey,
+                           &ServiceU,
+                           0,
+                           REG_SZ,
+                           (PVOID)Driver,
+                           (wcslen(Driver) + 1) * sizeof(WCHAR));
     if (NT_SUCCESS(Status))
     {
-        /* Restart the device, so it will use the driver we registered */
-        deviceInstalled = ResetDevice(DeviceId);
+        /* We've registered the driver, time to start a device */
+        PLUGPLAY_CONTROL_DEVICE_CONTROL_DATA ControlData;
+        RtlInitUnicodeString(&ControlData.DeviceInstance, DeviceId);
+
+        Status = NtPlugPlayControl(PlugPlayControlStartDevice, &ControlData, sizeof(ControlData));
+        if (!NT_SUCCESS(Status))
+        {
+            DPRINT1("NtPlugPlayControl() failed with status 0x%08x\n", Status);
+        }
+
+        deviceInstalled = NT_SUCCESS(Status);
     }
 
-    /* HACK: Update driver path */
-    NtSetValueKey(
-        hService,
-        &ImagePathU,
-        0,
-        REG_SZ,
-        FullImagePath,
-        (wcslen(FullImagePath) + 1) * sizeof(WCHAR));
-    RtlFreeHeap(ProcessHeap, 0, FullImagePath);
-    NtClose(hService);
+    INF_FreeData(Driver);
 
     return deviceInstalled;
 }
 
-VOID
+static VOID
 InstallDevice(
     IN HINF hInf,
     IN HANDLE hEnum,
@@ -191,6 +226,7 @@ InstallDevice(
 {
     UNICODE_STRING HardwareIDU = RTL_CONSTANT_STRING(L"HardwareID");
     UNICODE_STRING CompatibleIDsU = RTL_CONSTANT_STRING(L"CompatibleIDs");
+
     UNICODE_STRING DeviceIdU;
     OBJECT_ATTRIBUTES ObjectAttributes;
     LPCWSTR HardwareID;
@@ -201,7 +237,7 @@ InstallDevice(
     NTSTATUS Status;
 
     RtlInitUnicodeString(&DeviceIdU, DeviceId);
-    InitializeObjectAttributes(&ObjectAttributes, &DeviceIdU, 0, hEnum, NULL);
+    InitializeObjectAttributes(&ObjectAttributes, &DeviceIdU, OBJ_CASE_INSENSITIVE, hEnum, NULL);
     Status = NtOpenKey(&hDeviceKey, KEY_QUERY_VALUE | KEY_SET_VALUE, &ObjectAttributes);
     if (!NT_SUCCESS(Status))
     {
@@ -319,102 +355,375 @@ InstallDevice(
     NtClose(hDeviceKey);
 }
 
-NTSTATUS
-EventThread(IN LPVOID lpParameter)
+/* Loop to install all queued devices installations */
+static ULONG NTAPI
+DeviceInstallThread(IN PVOID Parameter)
 {
-    UNICODE_STRING EnumU = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Enum");
-    UNICODE_STRING ServicesU = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Services");
-    PPLUGPLAY_EVENT_BLOCK PnpEvent;
-    OBJECT_ATTRIBUTES ObjectAttributes;
-    ULONG PnpEventSize;
-    HINF hInf;
-    HANDLE hEnum, hServices;
-    NTSTATUS Status;
+    HINF hSetupInf = *(HINF*)Parameter;
+    PLIST_ENTRY ListEntry;
+    DeviceInstallParams* Params;
+    LARGE_INTEGER Timeout;
 
-    hInf = *(HINF *)lpParameter;
-
-    InitializeObjectAttributes(&ObjectAttributes, &EnumU, OBJ_CASE_INSENSITIVE, NULL, NULL);
-    Status = NtOpenKey(&hEnum, KEY_QUERY_VALUE, &ObjectAttributes);
-    if (!NT_SUCCESS(Status))
+    for (;;)
     {
-        DPRINT1("NtOpenKey('%wZ') failed with status 0x%08lx\n", &EnumU, Status);
-        return Status;
-    }
+        /* Dequeue the next oldest device-install event */
+        NtWaitForSingleObject(hDeviceInstallListMutex, FALSE, NULL);
+        ListEntry = (IsListEmpty(&DeviceInstallListHead)
+                        ? NULL : RemoveHeadList(&DeviceInstallListHead));
+        NtReleaseMutant(hDeviceInstallListMutex, NULL);
 
-    InitializeObjectAttributes(&ObjectAttributes, &ServicesU, OBJ_CASE_INSENSITIVE, NULL, NULL);
-    Status = NtCreateKey(&hServices, KEY_ALL_ACCESS, &ObjectAttributes, 0, NULL, REG_OPTION_NON_VOLATILE, NULL);
-    if (!NT_SUCCESS(Status))
-    {
-        DPRINT1("NtCreateKey('%wZ') failed with status 0x%08lx\n", &ServicesU, Status);
-        NtClose(hEnum);
-        return Status;
+        if (ListEntry == NULL)
+        {
+            /*
+             * The list is now empty, but there may be a new enumerated device
+             * that is going to be added to the list soon. In order to avoid
+             * setting the hNoPendingInstalls event to release it soon after,
+             * we wait for maximum 1 second for no PnP enumeration event being
+             * received before declaring that no pending installations are
+             * taking place and setting the corresponding event.
+             */
+            Timeout.QuadPart = -10000000LL; /* Wait for 1 second */
+            if (NtWaitForSingleObject(hDeviceInstallListNotEmpty, FALSE, &Timeout) == STATUS_TIMEOUT)
+            {
+                /* We timed out: set the event and do the actual wait */
+                NtSetEvent(hNoPendingInstalls, NULL);
+                NtWaitForSingleObject(hDeviceInstallListNotEmpty, FALSE, NULL);
+            }
+        }
+        else
+        {
+            NtResetEvent(hNoPendingInstalls, NULL);
+            Params = CONTAINING_RECORD(ListEntry, DeviceInstallParams, ListEntry);
+            InstallDevice(hSetupInf, hEnumKey, hServicesKey, Params->DeviceIds);
+            RtlFreeHeap(ProcessHeap, 0, Params);
+        }
     }
 
+    return 0;
+}
+
+static ULONG NTAPI
+PnpEventThread(IN PVOID Parameter)
+{
+    NTSTATUS Status;
+    PLUGPLAY_CONTROL_USER_RESPONSE_DATA ResponseData = {0, 0, 0, 0};
+    PPLUGPLAY_EVENT_BLOCK PnpEvent, NewPnpEvent;
+    ULONG PnpEventSize;
+
+    UNREFERENCED_PARAMETER(Parameter);
+
     PnpEventSize = 0x1000;
-    PnpEvent = (PPLUGPLAY_EVENT_BLOCK)RtlAllocateHeap(ProcessHeap, 0, PnpEventSize);
+    PnpEvent = RtlAllocateHeap(ProcessHeap, 0, PnpEventSize);
     if (PnpEvent == NULL)
     {
-        NtClose(hEnum);
-        NtClose(hServices);
-        return STATUS_NO_MEMORY;
+        Status = STATUS_NO_MEMORY;
+        goto Quit;
     }
 
     for (;;)
     {
         DPRINT("Calling NtGetPlugPlayEvent()\n");
 
-        /* Wait for the next pnp event */
+        /* Wait for the next PnP event */
         Status = NtGetPlugPlayEvent(0, 0, PnpEvent, PnpEventSize);
 
-        /* Resize the buffer for the PnP event if it's too small. */
+        /* Resize the buffer for the PnP event if it's too small */
         if (Status == STATUS_BUFFER_TOO_SMALL)
         {
             PnpEventSize += 0x400;
-            RtlFreeHeap(ProcessHeap, 0, PnpEvent);
-            PnpEvent = (PPLUGPLAY_EVENT_BLOCK)RtlAllocateHeap(ProcessHeap, 0, PnpEventSize);
-            if (PnpEvent == NULL)
+            NewPnpEvent = RtlReAllocateHeap(ProcessHeap, 0, PnpEvent, PnpEventSize);
+            if (NewPnpEvent == NULL)
             {
-                NtClose(hEnum);
-                NtClose(hServices);
-                return STATUS_NO_MEMORY;
+                Status = STATUS_NO_MEMORY;
+                goto Quit;
             }
+            PnpEvent = NewPnpEvent;
             continue;
         }
 
         if (!NT_SUCCESS(Status))
         {
-            DPRINT("NtPlugPlayEvent() failed (Status %lx)\n", Status);
-            break;
+            DPRINT1("NtGetPlugPlayEvent() failed (Status 0x%08lx)\n", Status);
+            goto Quit;
         }
 
-        /* Process the pnp event */
+        /* Process the PnP event */
         DPRINT("Received PnP Event\n");
-        if (IsEqualIID(&PnpEvent->EventGuid, (REFGUID)&GUID_DEVICE_ENUMERATED))
+        if (IsEqualGUID(&PnpEvent->EventGuid, &GUID_DEVICE_ENUMERATED))
         {
-            DPRINT("Device arrival event: %S\n", PnpEvent->TargetDevice.DeviceIds);
-            InstallDevice(hInf, hEnum, hServices, PnpEvent->TargetDevice.DeviceIds);
+            DeviceInstallParams* Params;
+            ULONG len;
+            ULONG DeviceIdLength;
+
+            DPRINT("Device enumerated: %S\n", PnpEvent->TargetDevice.DeviceIds);
+
+            DeviceIdLength = wcslen(PnpEvent->TargetDevice.DeviceIds);
+            if (DeviceIdLength)
+            {
+                /* Allocate a new device-install event */
+                len = FIELD_OFFSET(DeviceInstallParams, DeviceIds) + (DeviceIdLength + 1) * sizeof(WCHAR);
+                Params = RtlAllocateHeap(ProcessHeap, 0, len);
+                if (Params)
+                {
+                    wcscpy(Params->DeviceIds, PnpEvent->TargetDevice.DeviceIds);
+
+                    /* Queue the event (will be dequeued by DeviceInstallThread) */
+                    NtWaitForSingleObject(hDeviceInstallListMutex, FALSE, NULL);
+                    InsertTailList(&DeviceInstallListHead, &Params->ListEntry);
+                    NtReleaseMutant(hDeviceInstallListMutex, NULL);
+
+                    NtSetEvent(hDeviceInstallListNotEmpty, NULL);
+                }
+                else
+                {
+                    DPRINT1("Not enough memory (size %lu)\n", len);
+                }
+            }
         }
         else
         {
-            DPRINT("Unknown event\n");
+            DPRINT("Unknown event, GUID {%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}\n",
+                PnpEvent->EventGuid.Data1, PnpEvent->EventGuid.Data2, PnpEvent->EventGuid.Data3,
+                PnpEvent->EventGuid.Data4[0], PnpEvent->EventGuid.Data4[1], PnpEvent->EventGuid.Data4[2],
+                PnpEvent->EventGuid.Data4[3], PnpEvent->EventGuid.Data4[4], PnpEvent->EventGuid.Data4[5],
+                PnpEvent->EventGuid.Data4[6], PnpEvent->EventGuid.Data4[7]);
         }
 
-        /* Dequeue the current pnp event and signal the next one */
-        NtPlugPlayControl(PlugPlayControlUserResponse, NULL, 0);
+        /* Dequeue the current PnP event and signal the next one */
+        Status = NtPlugPlayControl(PlugPlayControlUserResponse,
+                                   &ResponseData,
+                                   sizeof(ResponseData));
+        if (!NT_SUCCESS(Status))
+        {
+            DPRINT1("NtPlugPlayControl(PlugPlayControlUserResponse) failed (Status 0x%08lx)\n", Status);
+            goto Quit;
+        }
     }
 
-    RtlFreeHeap(ProcessHeap, 0, PnpEvent);
-    NtClose(hEnum);
-    NtClose(hServices);
+    Status = STATUS_SUCCESS;
 
-    return STATUS_SUCCESS;
+Quit:
+    if (PnpEvent)
+        RtlFreeHeap(ProcessHeap, 0, PnpEvent);
+
+    NtTerminateThread(NtCurrentThread(), Status);
+    return Status;
+}
+
+NTSTATUS
+WaitNoPendingInstallEvents(
+    IN PLARGE_INTEGER Timeout OPTIONAL)
+{
+    return NtWaitForSingleObject(hNoPendingInstalls, FALSE, Timeout);
 }
 
-DWORD WINAPI
-PnpEventThread(IN LPVOID lpParameter)
+BOOLEAN
+EnableUserModePnpManager(VOID)
+{
+    LARGE_INTEGER Timeout;
+
+    /* Start the PnP thread */
+    if (hPnpThread != NULL)
+        NtResumeThread(hPnpThread, NULL);
+
+    /*
+     * Wait a little bit so that we get a chance to have some events being
+     * queued by the time the device-installation thread becomes resumed.
+     */
+    Timeout.QuadPart = -10000000LL; /* Wait for 1 second */
+    NtWaitForSingleObject(hDeviceInstallListNotEmpty, FALSE, &Timeout);
+
+    /* Start the device installation thread */
+    if (hDeviceInstallThread != NULL)
+        NtResumeThread(hDeviceInstallThread, NULL);
+
+    return TRUE;
+}
+
+BOOLEAN
+DisableUserModePnpManager(VOID)
+{
+    /* Wait until all pending installations are done, then freeze the threads */
+    if (WaitNoPendingInstallEvents(NULL) != STATUS_WAIT_0)
+        DPRINT1("WaitNoPendingInstallEvents() failed to wait!\n");
+
+    // TODO: use signalling events
+
+    NtSuspendThread(hPnpThread, NULL);
+    NtSuspendThread(hDeviceInstallThread, NULL);
+
+    return TRUE;
+}
+
+NTSTATUS
+InitializeUserModePnpManager(
+    IN HINF* phSetupInf)
 {
     NTSTATUS Status;
-    Status = EventThread(lpParameter);
-    NtTerminateThread(NtCurrentThread(), Status);
-    return 0;
+    OBJECT_ATTRIBUTES ObjectAttributes;
+
+    UNICODE_STRING EnumU = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Enum");
+    UNICODE_STRING ServicesU = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Services");
+
+    Status = NtCreateEvent(&hNoPendingInstalls,
+                           EVENT_ALL_ACCESS,
+                           NULL,
+                           NotificationEvent,
+                           FALSE);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("Could not create the Pending-Install Event! (Status 0x%08lx)\n", Status);
+        goto Failure;
+    }
+
+    /*
+     * Initialize the device-install event list
+     */
+
+    Status = NtCreateEvent(&hDeviceInstallListNotEmpty,
+                           EVENT_ALL_ACCESS,
+                           NULL,
+                           SynchronizationEvent,
+                           FALSE);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("Could not create the List Event! (Status 0x%08lx)\n", Status);
+        goto Failure;
+    }
+
+    Status = NtCreateMutant(&hDeviceInstallListMutex,
+                            MUTANT_ALL_ACCESS,
+                            NULL, FALSE);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("Could not create the List Mutex! (Status 0x%08lx)\n", Status);
+        goto Failure;
+    }
+    InitializeListHead(&DeviceInstallListHead);
+
+    InitializeObjectAttributes(&ObjectAttributes, &EnumU, OBJ_CASE_INSENSITIVE, NULL, NULL);
+    Status = NtOpenKey(&hEnumKey, KEY_QUERY_VALUE, &ObjectAttributes);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("NtOpenKey('%wZ') failed (Status 0x%08lx)\n", &EnumU, Status);
+        goto Failure;
+    }
+
+    InitializeObjectAttributes(&ObjectAttributes, &ServicesU, OBJ_CASE_INSENSITIVE, NULL, NULL);
+    Status = NtCreateKey(&hServicesKey, KEY_ALL_ACCESS, &ObjectAttributes, 0, NULL, REG_OPTION_NON_VOLATILE, NULL);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("NtCreateKey('%wZ') failed (Status 0x%08lx)\n", &ServicesU, Status);
+        goto Failure;
+    }
+
+    /* Create the PnP event thread in suspended state */
+    Status = RtlCreateUserThread(NtCurrentProcess(),
+                                 NULL,
+                                 TRUE,
+                                 0,
+                                 0,
+                                 0,
+                                 PnpEventThread,
+                                 NULL,
+                                 &hPnpThread,
+                                 NULL);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("Failed to create the PnP event thread (Status 0x%08lx)\n", Status);
+        hPnpThread = NULL;
+        goto Failure;
+    }
+
+    /* Create the device installation thread in suspended state */
+    Status = RtlCreateUserThread(NtCurrentProcess(),
+                                 NULL,
+                                 TRUE,
+                                 0,
+                                 0,
+                                 0,
+                                 DeviceInstallThread,
+                                 phSetupInf,
+                                 &hDeviceInstallThread,
+                                 NULL);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("Failed to create the device installation thread (Status 0x%08lx)\n", Status);
+        hDeviceInstallThread = NULL;
+        goto Failure;
+    }
+
+    return STATUS_SUCCESS;
+
+Failure:
+    if (hPnpThread)
+    {
+        NtTerminateThread(hPnpThread, STATUS_SUCCESS);
+        NtClose(hPnpThread);
+    }
+    hPnpThread = NULL;
+
+    if (hServicesKey)
+        NtClose(hServicesKey);
+    hServicesKey = NULL;
+
+    if (hEnumKey)
+        NtClose(hEnumKey);
+    hEnumKey = NULL;
+
+    if (hDeviceInstallListMutex)
+        NtClose(hDeviceInstallListMutex);
+    hDeviceInstallListMutex = NULL;
+
+    if (hDeviceInstallListNotEmpty)
+        NtClose(hDeviceInstallListNotEmpty);
+    hDeviceInstallListNotEmpty = NULL;
+
+    if (hNoPendingInstalls)
+        NtClose(hNoPendingInstalls);
+    hNoPendingInstalls = NULL;
+
+    return Status;
+}
+
+VOID
+TerminateUserModePnpManager(VOID)
+{
+    DisableUserModePnpManager();
+
+    // TODO: use signalling events
+
+    /* Kill the PnP thread as it blocks inside the NtGetPlugPlayEvent() call */
+    if (hPnpThread)
+    {
+        NtTerminateThread(hPnpThread, STATUS_SUCCESS);
+        NtClose(hPnpThread);
+    }
+    hPnpThread = NULL;
+
+    /* Kill the device installation thread */
+    if (hDeviceInstallThread)
+    {
+        NtTerminateThread(hDeviceInstallThread, STATUS_SUCCESS);
+        NtClose(hDeviceInstallThread);
+    }
+    hDeviceInstallThread = NULL;
+
+    /* Close the opened handles */
+
+    if (hServicesKey)
+        NtClose(hServicesKey);
+    hServicesKey = NULL;
+
+    if (hEnumKey)
+        NtClose(hEnumKey);
+    hEnumKey = NULL;
+
+    if (hNoPendingInstalls)
+        NtClose(hNoPendingInstalls);
+    hNoPendingInstalls = NULL;
+
+    if (hDeviceInstallListNotEmpty)
+        NtClose(hDeviceInstallListNotEmpty);
+    hDeviceInstallListNotEmpty = NULL;
 }