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