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