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