2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS text-mode setup
4 * PURPOSE: Device installation
5 * PROGRAMMER: Hervé Poussineau (hpoussin@reactos.org)
16 #include <libs/umpnpmgr/sysguid.h>
18 /* LOCALS *******************************************************************/
20 static HANDLE hEnumKey
= NULL
;
21 static HANDLE hServicesKey
= NULL
;
23 static HANDLE hNoPendingInstalls
= NULL
;
25 static HANDLE hPnpThread
= NULL
;
26 static HANDLE hDeviceInstallThread
= NULL
;
28 /* Device-install event list */
29 static HANDLE hDeviceInstallListMutex
= NULL
;
30 static LIST_ENTRY DeviceInstallListHead
;
31 static HANDLE hDeviceInstallListNotEmpty
= NULL
;
36 WCHAR DeviceIds
[ANYSIZE_ARRAY
];
37 } DeviceInstallParams
;
39 /* FUNCTIONS ****************************************************************/
45 PLUGPLAY_CONTROL_DEVICE_CONTROL_DATA ResetDeviceData
;
48 RtlInitUnicodeString(&ResetDeviceData
.DeviceInstance
, DeviceId
);
49 Status
= NtPlugPlayControl(PlugPlayControlResetDevice
, &ResetDeviceData
, sizeof(PLUGPLAY_CONTROL_DEVICE_CONTROL_DATA
));
50 if (!NT_SUCCESS(Status
))
52 DPRINT1("NtPlugPlayControl() failed with status 0x%08x\n", Status
);
64 IN LPCWSTR HardwareId
)
66 UNICODE_STRING ServiceU
= RTL_CONSTANT_STRING(L
"Service");
67 UNICODE_STRING ErrorControlU
= RTL_CONSTANT_STRING(L
"ErrorControl");
68 UNICODE_STRING StartU
= RTL_CONSTANT_STRING(L
"Start");
69 UNICODE_STRING TypeU
= RTL_CONSTANT_STRING(L
"Type");
70 UNICODE_STRING UpperFiltersU
= RTL_CONSTANT_STRING(L
"UpperFilters");
71 PWSTR keyboardClass
= L
"kbdclass\0";
72 PWSTR partMgr
= L
"partmgr\0";
74 UNICODE_STRING StringU
;
75 OBJECT_ATTRIBUTES ObjectAttributes
;
78 PCWSTR Driver
, ClassGuid
, ImagePath
;
82 BOOLEAN deviceInstalled
= FALSE
;
84 /* Check if we know the hardware */
85 if (!SpInfFindFirstLine(hInf
, L
"HardwareIdsDatabase", HardwareId
, &Context
))
87 if (!INF_GetDataField(&Context
, 1, &Driver
))
90 /* Get associated class GUID (if any) */
91 if (!INF_GetDataField(&Context
, 2, &ClassGuid
))
94 /* Find associated driver name */
95 /* FIXME: check in other sections too! */
96 if (!SpInfFindFirstLine(hInf
, L
"BootBusExtenders.Load", Driver
, &Context
)
97 && !SpInfFindFirstLine(hInf
, L
"BusExtenders.Load", Driver
, &Context
)
98 && !SpInfFindFirstLine(hInf
, L
"SCSI.Load", Driver
, &Context
)
99 && !SpInfFindFirstLine(hInf
, L
"InputDevicesSupport.Load", Driver
, &Context
)
100 && !SpInfFindFirstLine(hInf
, L
"Keyboard.Load", Driver
, &Context
))
102 INF_FreeData(ClassGuid
);
103 INF_FreeData(Driver
);
107 if (!INF_GetDataField(&Context
, 1, &ImagePath
))
109 INF_FreeData(ClassGuid
);
110 INF_FreeData(Driver
);
114 DPRINT1("Using driver '%S' for device '%S'\n", ImagePath
, DeviceId
);
116 /* Create service key */
117 RtlInitUnicodeString(&StringU
, Driver
);
118 InitializeObjectAttributes(&ObjectAttributes
, &StringU
, OBJ_CASE_INSENSITIVE
, hServices
, NULL
);
119 Status
= NtCreateKey(&hService
, KEY_SET_VALUE
, &ObjectAttributes
, 0, NULL
, REG_OPTION_NON_VOLATILE
, &Disposition
);
120 if (!NT_SUCCESS(Status
))
122 DPRINT1("NtCreateKey('%wZ') failed with status 0x%08x\n", &StringU
, Status
);
123 INF_FreeData(ImagePath
);
124 INF_FreeData(ClassGuid
);
125 INF_FreeData(Driver
);
129 /* Fill service key */
130 if (Disposition
== REG_CREATED_NEW_KEY
)
133 NtSetValueKey(hService
,
141 NtSetValueKey(hService
,
148 dwValue
= SERVICE_KERNEL_DRIVER
;
149 NtSetValueKey(hService
,
157 INF_FreeData(ImagePath
);
160 /* Add kbdclass and partmgr upper filters */
161 if (ClassGuid
&&_wcsicmp(ClassGuid
, L
"{4D36E96B-E325-11CE-BFC1-08002BE10318}") == 0)
163 DPRINT1("Installing keyboard class driver for '%S'\n", DeviceId
);
164 NtSetValueKey(hDeviceKey
,
169 (wcslen(keyboardClass
) + 2) * sizeof(WCHAR
));
171 else if (ClassGuid
&& _wcsicmp(ClassGuid
, L
"{4D36E967-E325-11CE-BFC1-08002BE10318}") == 0)
173 DPRINT1("Installing partition manager driver for '%S'\n", DeviceId
);
174 NtSetValueKey(hDeviceKey
,
179 (wcslen(partMgr
) + 2) * sizeof(WCHAR
));
182 INF_FreeData(ClassGuid
);
184 /* Associate device with the service we just filled */
185 Status
= NtSetValueKey(hDeviceKey
,
190 (wcslen(Driver
) + 1) * sizeof(WCHAR
));
191 if (NT_SUCCESS(Status
))
193 /* Restart the device, so it will use the driver we registered */
194 deviceInstalled
= ResetDevice(DeviceId
);
197 INF_FreeData(Driver
);
199 return deviceInstalled
;
209 UNICODE_STRING HardwareIDU
= RTL_CONSTANT_STRING(L
"HardwareID");
210 UNICODE_STRING CompatibleIDsU
= RTL_CONSTANT_STRING(L
"CompatibleIDs");
212 UNICODE_STRING DeviceIdU
;
213 OBJECT_ATTRIBUTES ObjectAttributes
;
215 PKEY_VALUE_PARTIAL_INFORMATION pPartialInformation
= NULL
;
218 BOOLEAN bDriverInstalled
= FALSE
;
221 RtlInitUnicodeString(&DeviceIdU
, DeviceId
);
222 InitializeObjectAttributes(&ObjectAttributes
, &DeviceIdU
, OBJ_CASE_INSENSITIVE
, hEnum
, NULL
);
223 Status
= NtOpenKey(&hDeviceKey
, KEY_QUERY_VALUE
| KEY_SET_VALUE
, &ObjectAttributes
);
224 if (!NT_SUCCESS(Status
))
226 DPRINT("Unable to open subkey '%S'\n", DeviceId
);
230 Status
= NtQueryValueKey(
233 KeyValuePartialInformation
,
237 if (Status
== STATUS_BUFFER_TOO_SMALL
)
239 pPartialInformation
= (PKEY_VALUE_PARTIAL_INFORMATION
)RtlAllocateHeap(ProcessHeap
, 0, ulRequired
);
240 if (!pPartialInformation
)
242 DPRINT1("RtlAllocateHeap() failed\n");
246 Status
= NtQueryValueKey(
249 KeyValuePartialInformation
,
254 if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
)
258 else if (!NT_SUCCESS(Status
))
260 DPRINT1("NtQueryValueKey() failed with status 0x%08x\n", Status
);
261 if (pPartialInformation
)
262 RtlFreeHeap(ProcessHeap
, 0, pPartialInformation
);
266 else if (pPartialInformation
)
268 for (HardwareID
= (LPCWSTR
)pPartialInformation
->Data
;
269 (PUCHAR
)HardwareID
< pPartialInformation
->Data
+ pPartialInformation
->DataLength
271 && !bDriverInstalled
;
272 HardwareID
+= wcslen(HardwareID
) + 1)
274 bDriverInstalled
= InstallDriver(hInf
, hServices
,hDeviceKey
, DeviceId
, HardwareID
);
278 if (!bDriverInstalled
)
280 if (pPartialInformation
)
282 RtlFreeHeap(ProcessHeap
, 0, pPartialInformation
);
283 pPartialInformation
= NULL
;
285 Status
= NtQueryValueKey(
288 KeyValuePartialInformation
,
292 if (Status
== STATUS_BUFFER_TOO_SMALL
)
294 pPartialInformation
= (PKEY_VALUE_PARTIAL_INFORMATION
)RtlAllocateHeap(ProcessHeap
, 0, ulRequired
);
295 if (!pPartialInformation
)
297 DPRINT("RtlAllocateHeap() failed\n");
301 Status
= NtQueryValueKey(
304 KeyValuePartialInformation
,
309 if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
)
313 else if (!NT_SUCCESS(Status
))
315 if (pPartialInformation
)
316 RtlFreeHeap(ProcessHeap
, 0, pPartialInformation
);
318 DPRINT1("NtQueryValueKey() failed with status 0x%08x\n", Status
);
321 else if (pPartialInformation
)
323 for (HardwareID
= (LPCWSTR
)pPartialInformation
->Data
;
324 (PUCHAR
)HardwareID
< pPartialInformation
->Data
+ pPartialInformation
->DataLength
326 && !bDriverInstalled
;
327 HardwareID
+= wcslen(HardwareID
) + 1)
329 bDriverInstalled
= InstallDriver(hInf
, hServices
,hDeviceKey
, DeviceId
, HardwareID
);
333 if (!bDriverInstalled
)
334 DPRINT("No driver available for %S\n", DeviceId
);
336 RtlFreeHeap(ProcessHeap
, 0, pPartialInformation
);
340 /* Loop to install all queued devices installations */
342 DeviceInstallThread(IN PVOID Parameter
)
344 HINF hSetupInf
= *(HINF
*)Parameter
;
345 PLIST_ENTRY ListEntry
;
346 DeviceInstallParams
* Params
;
347 LARGE_INTEGER Timeout
;
351 /* Dequeue the next oldest device-install event */
352 NtWaitForSingleObject(hDeviceInstallListMutex
, FALSE
, NULL
);
353 ListEntry
= (IsListEmpty(&DeviceInstallListHead
)
354 ? NULL
: RemoveHeadList(&DeviceInstallListHead
));
355 NtReleaseMutant(hDeviceInstallListMutex
, NULL
);
357 if (ListEntry
== NULL
)
360 * The list is now empty, but there may be a new enumerated device
361 * that is going to be added to the list soon. In order to avoid
362 * setting the hNoPendingInstalls event to release it soon after,
363 * we wait for maximum 1 second for no PnP enumeration event being
364 * received before declaring that no pending installations are
365 * taking place and setting the corresponding event.
367 Timeout
.QuadPart
= -10000000LL; /* Wait for 1 second */
368 if (NtWaitForSingleObject(hDeviceInstallListNotEmpty
, FALSE
, &Timeout
) == STATUS_TIMEOUT
)
370 /* We timed out: set the event and do the actual wait */
371 NtSetEvent(hNoPendingInstalls
, NULL
);
372 NtWaitForSingleObject(hDeviceInstallListNotEmpty
, FALSE
, NULL
);
377 NtResetEvent(hNoPendingInstalls
, NULL
);
378 Params
= CONTAINING_RECORD(ListEntry
, DeviceInstallParams
, ListEntry
);
379 InstallDevice(hSetupInf
, hEnumKey
, hServicesKey
, Params
->DeviceIds
);
380 RtlFreeHeap(ProcessHeap
, 0, Params
);
388 PnpEventThread(IN PVOID Parameter
)
391 PLUGPLAY_CONTROL_USER_RESPONSE_DATA ResponseData
= {0, 0, 0, 0};
392 PPLUGPLAY_EVENT_BLOCK PnpEvent
, NewPnpEvent
;
395 UNREFERENCED_PARAMETER(Parameter
);
397 PnpEventSize
= 0x1000;
398 PnpEvent
= RtlAllocateHeap(ProcessHeap
, 0, PnpEventSize
);
399 if (PnpEvent
== NULL
)
401 Status
= STATUS_NO_MEMORY
;
407 DPRINT("Calling NtGetPlugPlayEvent()\n");
409 /* Wait for the next PnP event */
410 Status
= NtGetPlugPlayEvent(0, 0, PnpEvent
, PnpEventSize
);
412 /* Resize the buffer for the PnP event if it's too small */
413 if (Status
== STATUS_BUFFER_TOO_SMALL
)
415 PnpEventSize
+= 0x400;
416 NewPnpEvent
= RtlReAllocateHeap(ProcessHeap
, 0, PnpEvent
, PnpEventSize
);
417 if (NewPnpEvent
== NULL
)
419 Status
= STATUS_NO_MEMORY
;
422 PnpEvent
= NewPnpEvent
;
426 if (!NT_SUCCESS(Status
))
428 DPRINT1("NtGetPlugPlayEvent() failed (Status 0x%08lx)\n", Status
);
432 /* Process the PnP event */
433 DPRINT("Received PnP Event\n");
434 if (IsEqualGUID(&PnpEvent
->EventGuid
, &GUID_DEVICE_ENUMERATED
))
436 DeviceInstallParams
* Params
;
438 ULONG DeviceIdLength
;
440 DPRINT("Device enumerated: %S\n", PnpEvent
->TargetDevice
.DeviceIds
);
442 DeviceIdLength
= wcslen(PnpEvent
->TargetDevice
.DeviceIds
);
445 /* Allocate a new device-install event */
446 len
= FIELD_OFFSET(DeviceInstallParams
, DeviceIds
) + (DeviceIdLength
+ 1) * sizeof(WCHAR
);
447 Params
= RtlAllocateHeap(ProcessHeap
, 0, len
);
450 wcscpy(Params
->DeviceIds
, PnpEvent
->TargetDevice
.DeviceIds
);
452 /* Queue the event (will be dequeued by DeviceInstallThread) */
453 NtWaitForSingleObject(hDeviceInstallListMutex
, FALSE
, NULL
);
454 InsertTailList(&DeviceInstallListHead
, &Params
->ListEntry
);
455 NtReleaseMutant(hDeviceInstallListMutex
, NULL
);
457 NtSetEvent(hDeviceInstallListNotEmpty
, NULL
);
461 DPRINT1("Not enough memory (size %lu)\n", len
);
467 DPRINT("Unknown event, GUID {%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}\n",
468 PnpEvent
->EventGuid
.Data1
, PnpEvent
->EventGuid
.Data2
, PnpEvent
->EventGuid
.Data3
,
469 PnpEvent
->EventGuid
.Data4
[0], PnpEvent
->EventGuid
.Data4
[1], PnpEvent
->EventGuid
.Data4
[2],
470 PnpEvent
->EventGuid
.Data4
[3], PnpEvent
->EventGuid
.Data4
[4], PnpEvent
->EventGuid
.Data4
[5],
471 PnpEvent
->EventGuid
.Data4
[6], PnpEvent
->EventGuid
.Data4
[7]);
474 /* Dequeue the current PnP event and signal the next one */
475 Status
= NtPlugPlayControl(PlugPlayControlUserResponse
,
477 sizeof(ResponseData
));
478 if (!NT_SUCCESS(Status
))
480 DPRINT1("NtPlugPlayControl(PlugPlayControlUserResponse) failed (Status 0x%08lx)\n", Status
);
485 Status
= STATUS_SUCCESS
;
489 RtlFreeHeap(ProcessHeap
, 0, PnpEvent
);
491 NtTerminateThread(NtCurrentThread(), Status
);
496 WaitNoPendingInstallEvents(
497 IN PLARGE_INTEGER Timeout OPTIONAL
)
499 return NtWaitForSingleObject(hNoPendingInstalls
, FALSE
, Timeout
);
503 EnableUserModePnpManager(VOID
)
505 LARGE_INTEGER Timeout
;
507 /* Start the PnP thread */
508 if (hPnpThread
!= NULL
)
509 NtResumeThread(hPnpThread
, NULL
);
512 * Wait a little bit so that we get a chance to have some events being
513 * queued by the time the device-installation thread becomes resumed.
515 Timeout
.QuadPart
= -10000000LL; /* Wait for 1 second */
516 NtWaitForSingleObject(hDeviceInstallListNotEmpty
, FALSE
, &Timeout
);
518 /* Start the device installation thread */
519 if (hDeviceInstallThread
!= NULL
)
520 NtResumeThread(hDeviceInstallThread
, NULL
);
526 DisableUserModePnpManager(VOID
)
528 /* Wait until all pending installations are done, then freeze the threads */
529 if (WaitNoPendingInstallEvents(NULL
) != STATUS_WAIT_0
)
530 DPRINT1("WaitNoPendingInstallEvents() failed to wait!\n");
532 // TODO: use signalling events
534 NtSuspendThread(hPnpThread
, NULL
);
535 NtSuspendThread(hDeviceInstallThread
, NULL
);
541 InitializeUserModePnpManager(
545 OBJECT_ATTRIBUTES ObjectAttributes
;
547 UNICODE_STRING EnumU
= RTL_CONSTANT_STRING(L
"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Enum");
548 UNICODE_STRING ServicesU
= RTL_CONSTANT_STRING(L
"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Services");
550 Status
= NtCreateEvent(&hNoPendingInstalls
,
555 if (!NT_SUCCESS(Status
))
557 DPRINT1("Could not create the Pending-Install Event! (Status 0x%08lx)\n", Status
);
562 * Initialize the device-install event list
565 Status
= NtCreateEvent(&hDeviceInstallListNotEmpty
,
568 SynchronizationEvent
,
570 if (!NT_SUCCESS(Status
))
572 DPRINT1("Could not create the List Event! (Status 0x%08lx)\n", Status
);
576 Status
= NtCreateMutant(&hDeviceInstallListMutex
,
579 if (!NT_SUCCESS(Status
))
581 DPRINT1("Could not create the List Mutex! (Status 0x%08lx)\n", Status
);
584 InitializeListHead(&DeviceInstallListHead
);
586 InitializeObjectAttributes(&ObjectAttributes
, &EnumU
, OBJ_CASE_INSENSITIVE
, NULL
, NULL
);
587 Status
= NtOpenKey(&hEnumKey
, KEY_QUERY_VALUE
, &ObjectAttributes
);
588 if (!NT_SUCCESS(Status
))
590 DPRINT1("NtOpenKey('%wZ') failed (Status 0x%08lx)\n", &EnumU
, Status
);
594 InitializeObjectAttributes(&ObjectAttributes
, &ServicesU
, OBJ_CASE_INSENSITIVE
, NULL
, NULL
);
595 Status
= NtCreateKey(&hServicesKey
, KEY_ALL_ACCESS
, &ObjectAttributes
, 0, NULL
, REG_OPTION_NON_VOLATILE
, NULL
);
596 if (!NT_SUCCESS(Status
))
598 DPRINT1("NtCreateKey('%wZ') failed (Status 0x%08lx)\n", &ServicesU
, Status
);
602 /* Create the PnP event thread in suspended state */
603 Status
= RtlCreateUserThread(NtCurrentProcess(),
613 if (!NT_SUCCESS(Status
))
615 DPRINT1("Failed to create the PnP event thread (Status 0x%08lx)\n", Status
);
620 /* Create the device installation thread in suspended state */
621 Status
= RtlCreateUserThread(NtCurrentProcess(),
629 &hDeviceInstallThread
,
631 if (!NT_SUCCESS(Status
))
633 DPRINT1("Failed to create the device installation thread (Status 0x%08lx)\n", Status
);
634 hDeviceInstallThread
= NULL
;
638 return STATUS_SUCCESS
;
643 NtTerminateThread(hPnpThread
, STATUS_SUCCESS
);
649 NtClose(hServicesKey
);
656 if (hDeviceInstallListMutex
)
657 NtClose(hDeviceInstallListMutex
);
658 hDeviceInstallListMutex
= NULL
;
660 if (hDeviceInstallListNotEmpty
)
661 NtClose(hDeviceInstallListNotEmpty
);
662 hDeviceInstallListNotEmpty
= NULL
;
664 if (hNoPendingInstalls
)
665 NtClose(hNoPendingInstalls
);
666 hNoPendingInstalls
= NULL
;
672 TerminateUserModePnpManager(VOID
)
674 DisableUserModePnpManager();
676 // TODO: use signalling events
678 /* Kill the PnP thread as it blocks inside the NtGetPlugPlayEvent() call */
681 NtTerminateThread(hPnpThread
, STATUS_SUCCESS
);
686 /* Kill the device installation thread */
687 if (hDeviceInstallThread
)
689 NtTerminateThread(hDeviceInstallThread
, STATUS_SUCCESS
);
690 NtClose(hDeviceInstallThread
);
692 hDeviceInstallThread
= NULL
;
694 /* Close the opened handles */
697 NtClose(hServicesKey
);
704 if (hNoPendingInstalls
)
705 NtClose(hNoPendingInstalls
);
706 hNoPendingInstalls
= NULL
;
708 if (hDeviceInstallListNotEmpty
)
709 NtClose(hDeviceInstallListNotEmpty
);
710 hDeviceInstallListNotEmpty
= NULL
;