2c067d72a0acf12c402ffb4a6515ae65a5a689fb
[reactos.git] / base / setup / usetup / devinst.c
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 }