354be13075c94ba1a1335a56fd83dac9f299e218
[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: base/setup/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, 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 LPWSTR 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 = (LPWSTR)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 wcscat(FullImagePath, 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 }