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