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