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_STATUS_DATA PlugPlayData
;
48 RtlInitUnicodeString(&PlugPlayData
.DeviceInstance
, DeviceId
);
49 PlugPlayData
.Operation
= PNP_GET_DEVICE_STATUS
;
51 Status
= NtPlugPlayControl(PlugPlayControlDeviceStatus
, &PlugPlayData
, sizeof(PlugPlayData
));
52 if (NT_SUCCESS(Status
))
54 return (_Bool
)((PlugPlayData
.DeviceStatus
& DN_DRIVER_LOADED
) &&
55 !(PlugPlayData
.DeviceStatus
& DN_HAS_PROBLEM
));
69 IN LPCWSTR HardwareId
)
71 UNICODE_STRING ServiceU
= RTL_CONSTANT_STRING(L
"Service");
72 UNICODE_STRING ErrorControlU
= RTL_CONSTANT_STRING(L
"ErrorControl");
73 UNICODE_STRING StartU
= RTL_CONSTANT_STRING(L
"Start");
74 UNICODE_STRING TypeU
= RTL_CONSTANT_STRING(L
"Type");
75 UNICODE_STRING UpperFiltersU
= RTL_CONSTANT_STRING(L
"UpperFilters");
76 PWSTR keyboardClass
= L
"kbdclass\0";
77 PWSTR partMgr
= L
"partmgr\0";
79 UNICODE_STRING StringU
;
80 OBJECT_ATTRIBUTES ObjectAttributes
;
83 PCWSTR Driver
, ClassGuid
, ImagePath
;
87 BOOLEAN deviceInstalled
= FALSE
;
89 /* First check if the driver needs any action at all */
90 if (AreDriversLoaded(DeviceId
))
93 /* Check if we know the hardware */
94 if (!SpInfFindFirstLine(hInf
, L
"HardwareIdsDatabase", HardwareId
, &Context
))
96 if (!INF_GetDataField(&Context
, 1, &Driver
))
99 /* Get associated class GUID (if any) */
100 if (!INF_GetDataField(&Context
, 2, &ClassGuid
))
103 /* Find associated driver name */
104 /* FIXME: check in other sections too! */
105 if (!SpInfFindFirstLine(hInf
, L
"BootBusExtenders.Load", Driver
, &Context
)
106 && !SpInfFindFirstLine(hInf
, L
"BusExtenders.Load", Driver
, &Context
)
107 && !SpInfFindFirstLine(hInf
, L
"SCSI.Load", Driver
, &Context
)
108 && !SpInfFindFirstLine(hInf
, L
"InputDevicesSupport.Load", Driver
, &Context
)
109 && !SpInfFindFirstLine(hInf
, L
"Keyboard.Load", Driver
, &Context
))
111 INF_FreeData(ClassGuid
);
112 INF_FreeData(Driver
);
116 if (!INF_GetDataField(&Context
, 1, &ImagePath
))
118 INF_FreeData(ClassGuid
);
119 INF_FreeData(Driver
);
123 DPRINT1("Using driver '%S' for device '%S'\n", ImagePath
, DeviceId
);
125 /* Create service key */
126 RtlInitUnicodeString(&StringU
, Driver
);
127 InitializeObjectAttributes(&ObjectAttributes
, &StringU
, OBJ_CASE_INSENSITIVE
, hServices
, NULL
);
128 Status
= NtCreateKey(&hService
, KEY_SET_VALUE
, &ObjectAttributes
, 0, NULL
, REG_OPTION_NON_VOLATILE
, &Disposition
);
129 if (!NT_SUCCESS(Status
))
131 DPRINT1("NtCreateKey('%wZ') failed with status 0x%08x\n", &StringU
, Status
);
132 INF_FreeData(ImagePath
);
133 INF_FreeData(ClassGuid
);
134 INF_FreeData(Driver
);
138 /* Fill service key */
139 if (Disposition
== REG_CREATED_NEW_KEY
)
142 NtSetValueKey(hService
,
150 NtSetValueKey(hService
,
157 dwValue
= SERVICE_KERNEL_DRIVER
;
158 NtSetValueKey(hService
,
166 INF_FreeData(ImagePath
);
169 /* Add kbdclass and partmgr upper filters */
170 if (ClassGuid
&&_wcsicmp(ClassGuid
, L
"{4D36E96B-E325-11CE-BFC1-08002BE10318}") == 0)
172 DPRINT1("Installing keyboard class driver for '%S'\n", DeviceId
);
173 NtSetValueKey(hDeviceKey
,
178 (wcslen(keyboardClass
) + 2) * sizeof(WCHAR
));
180 else if (ClassGuid
&& _wcsicmp(ClassGuid
, L
"{4D36E967-E325-11CE-BFC1-08002BE10318}") == 0)
182 DPRINT1("Installing partition manager driver for '%S'\n", DeviceId
);
183 NtSetValueKey(hDeviceKey
,
188 (wcslen(partMgr
) + 2) * sizeof(WCHAR
));
191 INF_FreeData(ClassGuid
);
193 /* Associate device with the service we just filled */
194 Status
= NtSetValueKey(hDeviceKey
,
199 (wcslen(Driver
) + 1) * sizeof(WCHAR
));
200 if (NT_SUCCESS(Status
))
202 /* We've registered the driver, time to start a device */
203 PLUGPLAY_CONTROL_DEVICE_CONTROL_DATA ControlData
;
204 RtlInitUnicodeString(&ControlData
.DeviceInstance
, DeviceId
);
206 Status
= NtPlugPlayControl(PlugPlayControlStartDevice
, &ControlData
, sizeof(ControlData
));
207 if (!NT_SUCCESS(Status
))
209 DPRINT1("NtPlugPlayControl() failed with status 0x%08x\n", Status
);
212 deviceInstalled
= NT_SUCCESS(Status
);
215 INF_FreeData(Driver
);
217 return deviceInstalled
;
227 UNICODE_STRING HardwareIDU
= RTL_CONSTANT_STRING(L
"HardwareID");
228 UNICODE_STRING CompatibleIDsU
= RTL_CONSTANT_STRING(L
"CompatibleIDs");
230 UNICODE_STRING DeviceIdU
;
231 OBJECT_ATTRIBUTES ObjectAttributes
;
233 PKEY_VALUE_PARTIAL_INFORMATION pPartialInformation
= NULL
;
236 BOOLEAN bDriverInstalled
= FALSE
;
239 RtlInitUnicodeString(&DeviceIdU
, DeviceId
);
240 InitializeObjectAttributes(&ObjectAttributes
, &DeviceIdU
, OBJ_CASE_INSENSITIVE
, hEnum
, NULL
);
241 Status
= NtOpenKey(&hDeviceKey
, KEY_QUERY_VALUE
| KEY_SET_VALUE
, &ObjectAttributes
);
242 if (!NT_SUCCESS(Status
))
244 DPRINT("Unable to open subkey '%S'\n", DeviceId
);
248 Status
= NtQueryValueKey(
251 KeyValuePartialInformation
,
255 if (Status
== STATUS_BUFFER_TOO_SMALL
)
257 pPartialInformation
= (PKEY_VALUE_PARTIAL_INFORMATION
)RtlAllocateHeap(ProcessHeap
, 0, ulRequired
);
258 if (!pPartialInformation
)
260 DPRINT1("RtlAllocateHeap() failed\n");
264 Status
= NtQueryValueKey(
267 KeyValuePartialInformation
,
272 if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
)
276 else if (!NT_SUCCESS(Status
))
278 DPRINT1("NtQueryValueKey() failed with status 0x%08x\n", Status
);
279 if (pPartialInformation
)
280 RtlFreeHeap(ProcessHeap
, 0, pPartialInformation
);
284 else if (pPartialInformation
)
286 for (HardwareID
= (LPCWSTR
)pPartialInformation
->Data
;
287 (PUCHAR
)HardwareID
< pPartialInformation
->Data
+ pPartialInformation
->DataLength
289 && !bDriverInstalled
;
290 HardwareID
+= wcslen(HardwareID
) + 1)
292 bDriverInstalled
= InstallDriver(hInf
, hServices
,hDeviceKey
, DeviceId
, HardwareID
);
296 if (!bDriverInstalled
)
298 if (pPartialInformation
)
300 RtlFreeHeap(ProcessHeap
, 0, pPartialInformation
);
301 pPartialInformation
= NULL
;
303 Status
= NtQueryValueKey(
306 KeyValuePartialInformation
,
310 if (Status
== STATUS_BUFFER_TOO_SMALL
)
312 pPartialInformation
= (PKEY_VALUE_PARTIAL_INFORMATION
)RtlAllocateHeap(ProcessHeap
, 0, ulRequired
);
313 if (!pPartialInformation
)
315 DPRINT("RtlAllocateHeap() failed\n");
319 Status
= NtQueryValueKey(
322 KeyValuePartialInformation
,
327 if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
)
331 else if (!NT_SUCCESS(Status
))
333 if (pPartialInformation
)
334 RtlFreeHeap(ProcessHeap
, 0, pPartialInformation
);
336 DPRINT1("NtQueryValueKey() failed with status 0x%08x\n", Status
);
339 else if (pPartialInformation
)
341 for (HardwareID
= (LPCWSTR
)pPartialInformation
->Data
;
342 (PUCHAR
)HardwareID
< pPartialInformation
->Data
+ pPartialInformation
->DataLength
344 && !bDriverInstalled
;
345 HardwareID
+= wcslen(HardwareID
) + 1)
347 bDriverInstalled
= InstallDriver(hInf
, hServices
,hDeviceKey
, DeviceId
, HardwareID
);
351 if (!bDriverInstalled
)
352 DPRINT("No driver available for %S\n", DeviceId
);
354 RtlFreeHeap(ProcessHeap
, 0, pPartialInformation
);
358 /* Loop to install all queued devices installations */
360 DeviceInstallThread(IN PVOID Parameter
)
362 HINF hSetupInf
= *(HINF
*)Parameter
;
363 PLIST_ENTRY ListEntry
;
364 DeviceInstallParams
* Params
;
365 LARGE_INTEGER Timeout
;
369 /* Dequeue the next oldest device-install event */
370 NtWaitForSingleObject(hDeviceInstallListMutex
, FALSE
, NULL
);
371 ListEntry
= (IsListEmpty(&DeviceInstallListHead
)
372 ? NULL
: RemoveHeadList(&DeviceInstallListHead
));
373 NtReleaseMutant(hDeviceInstallListMutex
, NULL
);
375 if (ListEntry
== NULL
)
378 * The list is now empty, but there may be a new enumerated device
379 * that is going to be added to the list soon. In order to avoid
380 * setting the hNoPendingInstalls event to release it soon after,
381 * we wait for maximum 1 second for no PnP enumeration event being
382 * received before declaring that no pending installations are
383 * taking place and setting the corresponding event.
385 Timeout
.QuadPart
= -10000000LL; /* Wait for 1 second */
386 if (NtWaitForSingleObject(hDeviceInstallListNotEmpty
, FALSE
, &Timeout
) == STATUS_TIMEOUT
)
388 /* We timed out: set the event and do the actual wait */
389 NtSetEvent(hNoPendingInstalls
, NULL
);
390 NtWaitForSingleObject(hDeviceInstallListNotEmpty
, FALSE
, NULL
);
395 NtResetEvent(hNoPendingInstalls
, NULL
);
396 Params
= CONTAINING_RECORD(ListEntry
, DeviceInstallParams
, ListEntry
);
397 InstallDevice(hSetupInf
, hEnumKey
, hServicesKey
, Params
->DeviceIds
);
398 RtlFreeHeap(ProcessHeap
, 0, Params
);
406 PnpEventThread(IN PVOID Parameter
)
409 PLUGPLAY_CONTROL_USER_RESPONSE_DATA ResponseData
= {0, 0, 0, 0};
410 PPLUGPLAY_EVENT_BLOCK PnpEvent
, NewPnpEvent
;
413 UNREFERENCED_PARAMETER(Parameter
);
415 PnpEventSize
= 0x1000;
416 PnpEvent
= RtlAllocateHeap(ProcessHeap
, 0, PnpEventSize
);
417 if (PnpEvent
== NULL
)
419 Status
= STATUS_NO_MEMORY
;
425 DPRINT("Calling NtGetPlugPlayEvent()\n");
427 /* Wait for the next PnP event */
428 Status
= NtGetPlugPlayEvent(0, 0, PnpEvent
, PnpEventSize
);
430 /* Resize the buffer for the PnP event if it's too small */
431 if (Status
== STATUS_BUFFER_TOO_SMALL
)
433 PnpEventSize
+= 0x400;
434 NewPnpEvent
= RtlReAllocateHeap(ProcessHeap
, 0, PnpEvent
, PnpEventSize
);
435 if (NewPnpEvent
== NULL
)
437 Status
= STATUS_NO_MEMORY
;
440 PnpEvent
= NewPnpEvent
;
444 if (!NT_SUCCESS(Status
))
446 DPRINT1("NtGetPlugPlayEvent() failed (Status 0x%08lx)\n", Status
);
450 /* Process the PnP event */
451 DPRINT("Received PnP Event\n");
452 if (IsEqualGUID(&PnpEvent
->EventGuid
, &GUID_DEVICE_ENUMERATED
))
454 DeviceInstallParams
* Params
;
456 ULONG DeviceIdLength
;
458 DPRINT("Device enumerated: %S\n", PnpEvent
->TargetDevice
.DeviceIds
);
460 DeviceIdLength
= wcslen(PnpEvent
->TargetDevice
.DeviceIds
);
463 /* Allocate a new device-install event */
464 len
= FIELD_OFFSET(DeviceInstallParams
, DeviceIds
) + (DeviceIdLength
+ 1) * sizeof(WCHAR
);
465 Params
= RtlAllocateHeap(ProcessHeap
, 0, len
);
468 wcscpy(Params
->DeviceIds
, PnpEvent
->TargetDevice
.DeviceIds
);
470 /* Queue the event (will be dequeued by DeviceInstallThread) */
471 NtWaitForSingleObject(hDeviceInstallListMutex
, FALSE
, NULL
);
472 InsertTailList(&DeviceInstallListHead
, &Params
->ListEntry
);
473 NtReleaseMutant(hDeviceInstallListMutex
, NULL
);
475 NtSetEvent(hDeviceInstallListNotEmpty
, NULL
);
479 DPRINT1("Not enough memory (size %lu)\n", len
);
485 DPRINT("Unknown event, GUID {%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}\n",
486 PnpEvent
->EventGuid
.Data1
, PnpEvent
->EventGuid
.Data2
, PnpEvent
->EventGuid
.Data3
,
487 PnpEvent
->EventGuid
.Data4
[0], PnpEvent
->EventGuid
.Data4
[1], PnpEvent
->EventGuid
.Data4
[2],
488 PnpEvent
->EventGuid
.Data4
[3], PnpEvent
->EventGuid
.Data4
[4], PnpEvent
->EventGuid
.Data4
[5],
489 PnpEvent
->EventGuid
.Data4
[6], PnpEvent
->EventGuid
.Data4
[7]);
492 /* Dequeue the current PnP event and signal the next one */
493 Status
= NtPlugPlayControl(PlugPlayControlUserResponse
,
495 sizeof(ResponseData
));
496 if (!NT_SUCCESS(Status
))
498 DPRINT1("NtPlugPlayControl(PlugPlayControlUserResponse) failed (Status 0x%08lx)\n", Status
);
503 Status
= STATUS_SUCCESS
;
507 RtlFreeHeap(ProcessHeap
, 0, PnpEvent
);
509 NtTerminateThread(NtCurrentThread(), Status
);
514 WaitNoPendingInstallEvents(
515 IN PLARGE_INTEGER Timeout OPTIONAL
)
517 return NtWaitForSingleObject(hNoPendingInstalls
, FALSE
, Timeout
);
521 EnableUserModePnpManager(VOID
)
523 LARGE_INTEGER Timeout
;
525 /* Start the PnP thread */
526 if (hPnpThread
!= NULL
)
527 NtResumeThread(hPnpThread
, NULL
);
530 * Wait a little bit so that we get a chance to have some events being
531 * queued by the time the device-installation thread becomes resumed.
533 Timeout
.QuadPart
= -10000000LL; /* Wait for 1 second */
534 NtWaitForSingleObject(hDeviceInstallListNotEmpty
, FALSE
, &Timeout
);
536 /* Start the device installation thread */
537 if (hDeviceInstallThread
!= NULL
)
538 NtResumeThread(hDeviceInstallThread
, NULL
);
544 DisableUserModePnpManager(VOID
)
546 /* Wait until all pending installations are done, then freeze the threads */
547 if (WaitNoPendingInstallEvents(NULL
) != STATUS_WAIT_0
)
548 DPRINT1("WaitNoPendingInstallEvents() failed to wait!\n");
550 // TODO: use signalling events
552 NtSuspendThread(hPnpThread
, NULL
);
553 NtSuspendThread(hDeviceInstallThread
, NULL
);
559 InitializeUserModePnpManager(
563 OBJECT_ATTRIBUTES ObjectAttributes
;
565 UNICODE_STRING EnumU
= RTL_CONSTANT_STRING(L
"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Enum");
566 UNICODE_STRING ServicesU
= RTL_CONSTANT_STRING(L
"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Services");
568 Status
= NtCreateEvent(&hNoPendingInstalls
,
573 if (!NT_SUCCESS(Status
))
575 DPRINT1("Could not create the Pending-Install Event! (Status 0x%08lx)\n", Status
);
580 * Initialize the device-install event list
583 Status
= NtCreateEvent(&hDeviceInstallListNotEmpty
,
586 SynchronizationEvent
,
588 if (!NT_SUCCESS(Status
))
590 DPRINT1("Could not create the List Event! (Status 0x%08lx)\n", Status
);
594 Status
= NtCreateMutant(&hDeviceInstallListMutex
,
597 if (!NT_SUCCESS(Status
))
599 DPRINT1("Could not create the List Mutex! (Status 0x%08lx)\n", Status
);
602 InitializeListHead(&DeviceInstallListHead
);
604 InitializeObjectAttributes(&ObjectAttributes
, &EnumU
, OBJ_CASE_INSENSITIVE
, NULL
, NULL
);
605 Status
= NtOpenKey(&hEnumKey
, KEY_QUERY_VALUE
, &ObjectAttributes
);
606 if (!NT_SUCCESS(Status
))
608 DPRINT1("NtOpenKey('%wZ') failed (Status 0x%08lx)\n", &EnumU
, Status
);
612 InitializeObjectAttributes(&ObjectAttributes
, &ServicesU
, OBJ_CASE_INSENSITIVE
, NULL
, NULL
);
613 Status
= NtCreateKey(&hServicesKey
, KEY_ALL_ACCESS
, &ObjectAttributes
, 0, NULL
, REG_OPTION_NON_VOLATILE
, NULL
);
614 if (!NT_SUCCESS(Status
))
616 DPRINT1("NtCreateKey('%wZ') failed (Status 0x%08lx)\n", &ServicesU
, Status
);
620 /* Create the PnP event thread in suspended state */
621 Status
= RtlCreateUserThread(NtCurrentProcess(),
631 if (!NT_SUCCESS(Status
))
633 DPRINT1("Failed to create the PnP event thread (Status 0x%08lx)\n", Status
);
638 /* Create the device installation thread in suspended state */
639 Status
= RtlCreateUserThread(NtCurrentProcess(),
647 &hDeviceInstallThread
,
649 if (!NT_SUCCESS(Status
))
651 DPRINT1("Failed to create the device installation thread (Status 0x%08lx)\n", Status
);
652 hDeviceInstallThread
= NULL
;
656 return STATUS_SUCCESS
;
661 NtTerminateThread(hPnpThread
, STATUS_SUCCESS
);
667 NtClose(hServicesKey
);
674 if (hDeviceInstallListMutex
)
675 NtClose(hDeviceInstallListMutex
);
676 hDeviceInstallListMutex
= NULL
;
678 if (hDeviceInstallListNotEmpty
)
679 NtClose(hDeviceInstallListNotEmpty
);
680 hDeviceInstallListNotEmpty
= NULL
;
682 if (hNoPendingInstalls
)
683 NtClose(hNoPendingInstalls
);
684 hNoPendingInstalls
= NULL
;
690 TerminateUserModePnpManager(VOID
)
692 DisableUserModePnpManager();
694 // TODO: use signalling events
696 /* Kill the PnP thread as it blocks inside the NtGetPlugPlayEvent() call */
699 NtTerminateThread(hPnpThread
, STATUS_SUCCESS
);
704 /* Kill the device installation thread */
705 if (hDeviceInstallThread
)
707 NtTerminateThread(hDeviceInstallThread
, STATUS_SUCCESS
);
708 NtClose(hDeviceInstallThread
);
710 hDeviceInstallThread
= NULL
;
712 /* Close the opened handles */
715 NtClose(hServicesKey
);
722 if (hNoPendingInstalls
)
723 NtClose(hNoPendingInstalls
);
724 hNoPendingInstalls
= NULL
;
726 if (hDeviceInstallListNotEmpty
)
727 NtClose(hDeviceInstallListNotEmpty
);
728 hDeviceInstallListNotEmpty
= NULL
;