xref: /reactos/base/setup/usetup/devinst.c (revision 8540ab04)
1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS text-mode setup
4  * FILE:            base/setup/usetup/devinst.c
5  * PURPOSE:         Device installation
6  * PROGRAMMER:      Herv� Poussineau (hpoussin@reactos.org)
7  */
8 
9 #include <usetup.h>
10 
11 #define NDEBUG
12 #include <debug.h>
13 
14 #define INITGUID
15 #include <guiddef.h>
16 #include <libs/umpnpmgr/sysguid.h>
17 
18 BOOLEAN
19 ResetDevice(
20     IN LPCWSTR DeviceId)
21 {
22     PLUGPLAY_CONTROL_RESET_DEVICE_DATA ResetDeviceData;
23     NTSTATUS Status;
24 
25     RtlInitUnicodeString(&ResetDeviceData.DeviceInstance, DeviceId);
26     Status = NtPlugPlayControl(PlugPlayControlResetDevice, &ResetDeviceData, sizeof(PLUGPLAY_CONTROL_RESET_DEVICE_DATA));
27     if (!NT_SUCCESS(Status))
28     {
29         DPRINT1("NtPlugPlayControl() failed with status 0x%08x\n", Status);
30         return FALSE;
31     }
32     return TRUE;
33 }
34 
35 BOOLEAN
36 InstallDriver(
37     IN HINF hInf,
38     IN HANDLE hServices,
39     IN HANDLE hDeviceKey,
40     IN LPCWSTR DeviceId,
41     IN LPCWSTR HardwareId)
42 {
43     UNICODE_STRING PathPrefix = RTL_CONSTANT_STRING(L"System32\\DRIVERS\\");
44     UNICODE_STRING ServiceU = RTL_CONSTANT_STRING(L"Service");
45     UNICODE_STRING ErrorControlU = RTL_CONSTANT_STRING(L"ErrorControl");
46     UNICODE_STRING ImagePathU = RTL_CONSTANT_STRING(L"ImagePath");
47     UNICODE_STRING StartU = RTL_CONSTANT_STRING(L"Start");
48     UNICODE_STRING TypeU = RTL_CONSTANT_STRING(L"Type");
49     UNICODE_STRING UpperFiltersU = RTL_CONSTANT_STRING(L"UpperFilters");
50     PWSTR keyboardClass = L"kbdclass\0";
51 
52     UNICODE_STRING StringU;
53     OBJECT_ATTRIBUTES ObjectAttributes;
54     HANDLE hService;
55     INFCONTEXT Context;
56     PCWSTR Driver, ClassGuid, ImagePath;
57     PWSTR FullImagePath;
58     ULONG dwValue;
59     ULONG Disposition;
60     NTSTATUS Status;
61     BOOLEAN deviceInstalled = FALSE;
62 
63     /* Check if we know the hardware */
64     if (!SpInfFindFirstLine(hInf, L"HardwareIdsDatabase", HardwareId, &Context))
65         return FALSE;
66     if (!INF_GetDataField(&Context, 1, &Driver))
67         return FALSE;
68 
69     /* Get associated class GUID (if any) */
70     if (!INF_GetDataField(&Context, 2, &ClassGuid))
71         ClassGuid = NULL;
72 
73     /* Find associated driver name */
74     /* FIXME: check in other sections too! */
75     if (!SpInfFindFirstLine(hInf, L"BootBusExtenders.Load", Driver, &Context)
76      && !SpInfFindFirstLine(hInf, L"BusExtenders.Load", Driver, &Context)
77      && !SpInfFindFirstLine(hInf, L"SCSI.Load", Driver, &Context)
78      && !SpInfFindFirstLine(hInf, L"InputDevicesSupport.Load", Driver, &Context)
79      && !SpInfFindFirstLine(hInf, L"Keyboard.Load", Driver, &Context))
80     {
81         INF_FreeData(ClassGuid);
82         INF_FreeData(Driver);
83         return FALSE;
84     }
85 
86     if (!INF_GetDataField(&Context, 1, &ImagePath))
87     {
88         INF_FreeData(ClassGuid);
89         INF_FreeData(Driver);
90         return FALSE;
91     }
92 
93     /* Prepare full driver path */
94     dwValue = PathPrefix.MaximumLength + wcslen(ImagePath) * sizeof(WCHAR);
95     FullImagePath = (PWSTR)RtlAllocateHeap(ProcessHeap, 0, dwValue);
96     if (!FullImagePath)
97     {
98         DPRINT1("RtlAllocateHeap() failed\n");
99         INF_FreeData(ImagePath);
100         INF_FreeData(ClassGuid);
101         INF_FreeData(Driver);
102         return FALSE;
103     }
104     RtlCopyMemory(FullImagePath, PathPrefix.Buffer, PathPrefix.MaximumLength);
105     ConcatPaths(FullImagePath, dwValue / sizeof(WCHAR), 1, ImagePath);
106 
107     DPRINT1("Using driver '%S' for device '%S'\n", ImagePath, DeviceId);
108 
109     /* Create service key */
110     RtlInitUnicodeString(&StringU, Driver);
111     InitializeObjectAttributes(&ObjectAttributes, &StringU, OBJ_CASE_INSENSITIVE, hServices, NULL);
112     Status = NtCreateKey(&hService, KEY_SET_VALUE, &ObjectAttributes, 0, NULL, REG_OPTION_NON_VOLATILE, &Disposition);
113     if (!NT_SUCCESS(Status))
114     {
115         DPRINT1("NtCreateKey('%wZ') failed with status 0x%08x\n", &StringU, Status);
116         RtlFreeHeap(ProcessHeap, 0, FullImagePath);
117         INF_FreeData(ImagePath);
118         INF_FreeData(ClassGuid);
119         INF_FreeData(Driver);
120         return FALSE;
121     }
122 
123     /* Fill service key */
124     if (Disposition == REG_CREATED_NEW_KEY)
125     {
126         dwValue = 0;
127         NtSetValueKey(hService,
128                       &ErrorControlU,
129                       0,
130                       REG_DWORD,
131                       &dwValue,
132                       sizeof(dwValue));
133 
134         dwValue = 0;
135         NtSetValueKey(hService,
136                       &StartU,
137                       0,
138                       REG_DWORD,
139                       &dwValue,
140                       sizeof(dwValue));
141 
142         dwValue = SERVICE_KERNEL_DRIVER;
143         NtSetValueKey(hService,
144                       &TypeU,
145                       0,
146                       REG_DWORD,
147                       &dwValue,
148                       sizeof(dwValue));
149     }
150     /* HACK: don't put any path in registry */
151     NtSetValueKey(hService,
152                   &ImagePathU,
153                   0,
154                   REG_SZ,
155                   (PVOID)ImagePath,
156                   (wcslen(ImagePath) + 1) * sizeof(WCHAR));
157 
158     INF_FreeData(ImagePath);
159 
160     if (ClassGuid &&_wcsicmp(ClassGuid, L"{4D36E96B-E325-11CE-BFC1-08002BE10318}") == 0)
161     {
162         DPRINT1("Installing keyboard class driver for '%S'\n", DeviceId);
163         NtSetValueKey(hDeviceKey,
164                       &UpperFiltersU,
165                       0,
166                       REG_MULTI_SZ,
167                       keyboardClass,
168                       (wcslen(keyboardClass) + 2) * sizeof(WCHAR));
169     }
170 
171     INF_FreeData(ClassGuid);
172 
173     /* Associate device with the service we just filled */
174     Status = NtSetValueKey(hDeviceKey,
175                            &ServiceU,
176                            0,
177                            REG_SZ,
178                            (PVOID)Driver,
179                            (wcslen(Driver) + 1) * sizeof(WCHAR));
180     if (NT_SUCCESS(Status))
181     {
182         /* Restart the device, so it will use the driver we registered */
183         deviceInstalled = ResetDevice(DeviceId);
184     }
185 
186     INF_FreeData(Driver);
187 
188     /* HACK: Update driver path */
189     NtSetValueKey(hService,
190                   &ImagePathU,
191                   0,
192                   REG_SZ,
193                   FullImagePath,
194                   (wcslen(FullImagePath) + 1) * sizeof(WCHAR));
195     RtlFreeHeap(ProcessHeap, 0, FullImagePath);
196 
197     NtClose(hService);
198 
199     return deviceInstalled;
200 }
201 
202 VOID
203 InstallDevice(
204     IN HINF hInf,
205     IN HANDLE hEnum,
206     IN HANDLE hServices,
207     IN LPCWSTR DeviceId)
208 {
209     UNICODE_STRING HardwareIDU = RTL_CONSTANT_STRING(L"HardwareID");
210     UNICODE_STRING CompatibleIDsU = RTL_CONSTANT_STRING(L"CompatibleIDs");
211 
212     UNICODE_STRING DeviceIdU;
213     OBJECT_ATTRIBUTES ObjectAttributes;
214     LPCWSTR HardwareID;
215     PKEY_VALUE_PARTIAL_INFORMATION pPartialInformation = NULL;
216     HANDLE hDeviceKey;
217     ULONG ulRequired;
218     BOOLEAN bDriverInstalled = FALSE;
219     NTSTATUS Status;
220 
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))
225     {
226         DPRINT("Unable to open subkey '%S'\n", DeviceId);
227         return;
228     }
229 
230     Status = NtQueryValueKey(
231         hDeviceKey,
232         &HardwareIDU,
233         KeyValuePartialInformation,
234         NULL,
235         0,
236         &ulRequired);
237     if (Status == STATUS_BUFFER_TOO_SMALL)
238     {
239         pPartialInformation = (PKEY_VALUE_PARTIAL_INFORMATION)RtlAllocateHeap(ProcessHeap, 0, ulRequired);
240         if (!pPartialInformation)
241         {
242             DPRINT1("RtlAllocateHeap() failed\n");
243             NtClose(hDeviceKey);
244             return;
245         }
246         Status = NtQueryValueKey(
247             hDeviceKey,
248             &HardwareIDU,
249             KeyValuePartialInformation,
250             pPartialInformation,
251             ulRequired,
252             &ulRequired);
253     }
254     if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
255     {
256         /* Nothing to do */
257     }
258     else if (!NT_SUCCESS(Status))
259     {
260         DPRINT1("NtQueryValueKey() failed with status 0x%08x\n", Status);
261         if (pPartialInformation)
262             RtlFreeHeap(ProcessHeap, 0, pPartialInformation);
263         NtClose(hDeviceKey);
264         return;
265     }
266     else if (pPartialInformation)
267     {
268         for (HardwareID = (LPCWSTR)pPartialInformation->Data;
269              (PUCHAR)HardwareID < pPartialInformation->Data + pPartialInformation->DataLength
270                  && *HardwareID
271                  && !bDriverInstalled;
272             HardwareID += wcslen(HardwareID) + 1)
273         {
274             bDriverInstalled = InstallDriver(hInf, hServices,hDeviceKey, DeviceId, HardwareID);
275         }
276     }
277 
278     if (!bDriverInstalled)
279     {
280         if (pPartialInformation)
281         {
282             RtlFreeHeap(ProcessHeap, 0, pPartialInformation);
283             pPartialInformation = NULL;
284         }
285         Status = NtQueryValueKey(
286             hDeviceKey,
287             &CompatibleIDsU,
288             KeyValuePartialInformation,
289             NULL,
290             0,
291             &ulRequired);
292         if (Status == STATUS_BUFFER_TOO_SMALL)
293         {
294             pPartialInformation = (PKEY_VALUE_PARTIAL_INFORMATION)RtlAllocateHeap(ProcessHeap, 0, ulRequired);
295             if (!pPartialInformation)
296             {
297                 DPRINT("RtlAllocateHeap() failed\n");
298                 NtClose(hDeviceKey);
299                 return;
300             }
301             Status = NtQueryValueKey(
302                 hDeviceKey,
303                 &CompatibleIDsU,
304                 KeyValuePartialInformation,
305                 pPartialInformation,
306                 ulRequired,
307                 &ulRequired);
308         }
309         if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
310         {
311             /* Nothing to do */
312         }
313         else if (!NT_SUCCESS(Status))
314         {
315             if (pPartialInformation)
316                 RtlFreeHeap(ProcessHeap, 0, pPartialInformation);
317             NtClose(hDeviceKey);
318             DPRINT1("NtQueryValueKey() failed with status 0x%08x\n", Status);
319             return;
320         }
321         else if (pPartialInformation)
322         {
323             for (HardwareID = (LPCWSTR)pPartialInformation->Data;
324                  (PUCHAR)HardwareID < pPartialInformation->Data + pPartialInformation->DataLength
325                      && *HardwareID
326                      && !bDriverInstalled;
327                 HardwareID += wcslen(HardwareID) + 1)
328             {
329                 bDriverInstalled = InstallDriver(hInf, hServices,hDeviceKey, DeviceId, HardwareID);
330             }
331         }
332     }
333     if (!bDriverInstalled)
334         DPRINT("No driver available for %S\n", DeviceId);
335 
336     RtlFreeHeap(ProcessHeap, 0, pPartialInformation);
337     NtClose(hDeviceKey);
338 }
339 
340 NTSTATUS
341 EventThread(IN LPVOID lpParameter)
342 {
343     UNICODE_STRING EnumU = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Enum");
344     UNICODE_STRING ServicesU = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Services");
345 
346     PPLUGPLAY_EVENT_BLOCK PnpEvent, NewPnpEvent;
347     OBJECT_ATTRIBUTES ObjectAttributes;
348     ULONG PnpEventSize;
349     HINF hInf;
350     HANDLE hEnum, hServices;
351     NTSTATUS Status;
352 
353     hInf = *(HINF*)lpParameter;
354 
355     InitializeObjectAttributes(&ObjectAttributes, &EnumU, OBJ_CASE_INSENSITIVE, NULL, NULL);
356     Status = NtOpenKey(&hEnum, KEY_QUERY_VALUE, &ObjectAttributes);
357     if (!NT_SUCCESS(Status))
358     {
359         DPRINT1("NtOpenKey('%wZ') failed with status 0x%08lx\n", &EnumU, Status);
360         return Status;
361     }
362 
363     InitializeObjectAttributes(&ObjectAttributes, &ServicesU, OBJ_CASE_INSENSITIVE, NULL, NULL);
364     Status = NtCreateKey(&hServices, KEY_ALL_ACCESS, &ObjectAttributes, 0, NULL, REG_OPTION_NON_VOLATILE, NULL);
365     if (!NT_SUCCESS(Status))
366     {
367         DPRINT1("NtCreateKey('%wZ') failed with status 0x%08lx\n", &ServicesU, Status);
368         NtClose(hEnum);
369         return Status;
370     }
371 
372     PnpEventSize = 0x1000;
373     PnpEvent = RtlAllocateHeap(ProcessHeap, 0, PnpEventSize);
374     if (PnpEvent == NULL)
375     {
376         Status = STATUS_NO_MEMORY;
377         goto Quit;
378     }
379 
380     for (;;)
381     {
382         DPRINT("Calling NtGetPlugPlayEvent()\n");
383 
384         /* Wait for the next PnP event */
385         Status = NtGetPlugPlayEvent(0, 0, PnpEvent, PnpEventSize);
386 
387         /* Resize the buffer for the PnP event if it's too small */
388         if (Status == STATUS_BUFFER_TOO_SMALL)
389         {
390             PnpEventSize += 0x400;
391             NewPnpEvent = RtlReAllocateHeap(ProcessHeap, 0, PnpEvent, PnpEventSize);
392             if (NewPnpEvent == NULL)
393             {
394                 Status = STATUS_NO_MEMORY;
395                 goto Quit;
396             }
397             PnpEvent = NewPnpEvent;
398             continue;
399         }
400 
401         if (!NT_SUCCESS(Status))
402         {
403             DPRINT("NtPlugPlayEvent() failed (Status %lx)\n", Status);
404             break;
405         }
406 
407         /* Process the PnP event */
408         DPRINT("Received PnP Event\n");
409         if (IsEqualIID(&PnpEvent->EventGuid, (REFGUID)&GUID_DEVICE_ENUMERATED))
410         {
411             DPRINT("Device arrival event: %S\n", PnpEvent->TargetDevice.DeviceIds);
412             InstallDevice(hInf, hEnum, hServices, PnpEvent->TargetDevice.DeviceIds);
413         }
414         else
415         {
416             DPRINT("Unknown event\n");
417         }
418 
419         /* Dequeue the current PnP event and signal the next one */
420         NtPlugPlayControl(PlugPlayControlUserResponse, NULL, 0);
421     }
422 
423     Status = STATUS_SUCCESS;
424 
425 Quit:
426     if (PnpEvent)
427         RtlFreeHeap(ProcessHeap, 0, PnpEvent);
428 
429     NtClose(hServices);
430     NtClose(hEnum);
431 
432     return Status;
433 }
434 
435 DWORD WINAPI
436 PnpEventThread(IN LPVOID lpParameter)
437 {
438     NTSTATUS Status;
439     Status = EventThread(lpParameter);
440     NtTerminateThread(NtCurrentThread(), Status);
441     return 0;
442 }
443