[NTOSKRNL]
[reactos.git] / reactos / ntoskrnl / io / pnpmgr / pnproot.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * COPYRIGHT: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/io/pnpmgr/pnproot.c
5 * PURPOSE: PnP manager root device
6 * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
7 * Copyright 2007 Herv? Poussineau (hpoussin@reactos.org)
8 */
9
10 /* INCLUDES ******************************************************************/
11
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15
16 /* GLOBALS *******************************************************************/
17
18 #define ENUM_NAME_ROOT L"Root"
19
20 /* DATA **********************************************************************/
21
22 typedef struct _PNPROOT_DEVICE
23 {
24 // Entry on device list
25 LIST_ENTRY ListEntry;
26 // Physical Device Object of device
27 PDEVICE_OBJECT Pdo;
28 // Device ID
29 UNICODE_STRING DeviceID;
30 // Instance ID
31 UNICODE_STRING InstanceID;
32 // Device description
33 UNICODE_STRING DeviceDescription;
34 // Resource requirement list
35 PIO_RESOURCE_REQUIREMENTS_LIST ResourceRequirementsList;
36 ULONG ResourceRequirementsListSize;
37 // Associated resource list
38 PCM_RESOURCE_LIST ResourceList;
39 ULONG ResourceListSize;
40 } PNPROOT_DEVICE, *PPNPROOT_DEVICE;
41
42 typedef enum
43 {
44 dsStopped,
45 dsStarted,
46 dsPaused,
47 dsRemoved,
48 dsSurpriseRemoved
49 } PNPROOT_DEVICE_STATE;
50
51 typedef struct _PNPROOT_COMMON_DEVICE_EXTENSION
52 {
53 // Wether this device extension is for an FDO or PDO
54 BOOLEAN IsFDO;
55 } PNPROOT_COMMON_DEVICE_EXTENSION, *PPNPROOT_COMMON_DEVICE_EXTENSION;
56
57 /* Physical Device Object device extension for a child device */
58 typedef struct _PNPROOT_PDO_DEVICE_EXTENSION
59 {
60 // Common device data
61 PNPROOT_COMMON_DEVICE_EXTENSION Common;
62 // Informations about the device
63 PPNPROOT_DEVICE DeviceInfo;
64 } PNPROOT_PDO_DEVICE_EXTENSION, *PPNPROOT_PDO_DEVICE_EXTENSION;
65
66 /* Physical Device Object device extension for the Root bus device object */
67 typedef struct _PNPROOT_FDO_DEVICE_EXTENSION
68 {
69 // Common device data
70 PNPROOT_COMMON_DEVICE_EXTENSION Common;
71 // Lower device object
72 PDEVICE_OBJECT Ldo;
73 // Current state of the driver
74 PNPROOT_DEVICE_STATE State;
75 // Namespace device list
76 LIST_ENTRY DeviceListHead;
77 // Number of (not removed) devices in device list
78 ULONG DeviceListCount;
79 // Lock for namespace device list
80 KGUARDED_MUTEX DeviceListLock;
81 } PNPROOT_FDO_DEVICE_EXTENSION, *PPNPROOT_FDO_DEVICE_EXTENSION;
82
83 typedef struct _BUFFER
84 {
85 PVOID *Data;
86 PULONG Length;
87 } BUFFER, *PBUFFER;
88
89 static PDEVICE_OBJECT PnpRootDeviceObject = NULL;
90
91 /* FUNCTIONS *****************************************************************/
92
93 static NTSTATUS
94 LocateChildDevice(
95 IN PPNPROOT_FDO_DEVICE_EXTENSION DeviceExtension,
96 IN PCWSTR DeviceId,
97 IN PCWSTR InstanceId,
98 OUT PPNPROOT_DEVICE* ChildDevice)
99 {
100 PPNPROOT_DEVICE Device;
101 UNICODE_STRING DeviceIdU, InstanceIdU;
102 PLIST_ENTRY NextEntry;
103
104 /* Initialize the strings to compare */
105 RtlInitUnicodeString(&DeviceIdU, DeviceId);
106 RtlInitUnicodeString(&InstanceIdU, InstanceId);
107
108 /* Start looping */
109 for (NextEntry = DeviceExtension->DeviceListHead.Flink;
110 NextEntry != &DeviceExtension->DeviceListHead;
111 NextEntry = NextEntry->Flink)
112 {
113 /* Get the entry */
114 Device = CONTAINING_RECORD(NextEntry, PNPROOT_DEVICE, ListEntry);
115
116 /* See if the strings match */
117 if (RtlEqualUnicodeString(&DeviceIdU, &Device->DeviceID, TRUE) &&
118 RtlEqualUnicodeString(&InstanceIdU, &Device->InstanceID, TRUE))
119 {
120 /* They do, so set the pointer and return success */
121 *ChildDevice = Device;
122 return STATUS_SUCCESS;
123 }
124 }
125
126 /* No device found */
127 return STATUS_NO_SUCH_DEVICE;
128 }
129
130 /* Creates a new PnP device for a legacy driver */
131 NTSTATUS
132 PnpRootCreateDevice(
133 IN PUNICODE_STRING ServiceName,
134 IN PDEVICE_OBJECT *PhysicalDeviceObject)
135 {
136 PPNPROOT_FDO_DEVICE_EXTENSION DeviceExtension;
137 UNICODE_STRING UnknownServiceName = RTL_CONSTANT_STRING(L"LEGACY_UNKNOWN");
138 PUNICODE_STRING LocalServiceName;
139 PPNPROOT_PDO_DEVICE_EXTENSION PdoDeviceExtension;
140 WCHAR DevicePath[MAX_PATH + 1];
141 WCHAR InstancePath[5];
142 PPNPROOT_DEVICE Device = NULL;
143 NTSTATUS Status;
144 ULONG i;
145
146 DeviceExtension = PnpRootDeviceObject->DeviceExtension;
147 KeAcquireGuardedMutex(&DeviceExtension->DeviceListLock);
148
149 if (ServiceName)
150 LocalServiceName = ServiceName;
151 else
152 LocalServiceName = &UnknownServiceName;
153
154 DPRINT("Creating a PnP root device for service '%wZ'\n", LocalServiceName);
155
156 /* Search for a free instance ID */
157 _snwprintf(DevicePath, sizeof(DevicePath) / sizeof(WCHAR), L"%s\\%wZ", REGSTR_KEY_ROOTENUM, LocalServiceName);
158 for (i = 0; i < 9999; i++)
159 {
160 _snwprintf(InstancePath, sizeof(InstancePath) / sizeof(WCHAR), L"%04lu", i);
161 Status = LocateChildDevice(DeviceExtension, DevicePath, InstancePath, &Device);
162 if (Status == STATUS_NO_SUCH_DEVICE)
163 break;
164 }
165 if (i == 9999)
166 {
167 DPRINT1("Too much legacy devices reported for service '%wZ'\n", &LocalServiceName);
168 Status = STATUS_INSUFFICIENT_RESOURCES;
169 goto cleanup;
170 }
171
172 /* Initialize a PNPROOT_DEVICE structure */
173 Device = ExAllocatePoolWithTag(PagedPool, sizeof(PNPROOT_DEVICE), TAG_PNP_ROOT);
174 if (!Device)
175 {
176 DPRINT("ExAllocatePoolWithTag() failed\n");
177 Status = STATUS_NO_MEMORY;
178 goto cleanup;
179 }
180 RtlZeroMemory(Device, sizeof(PNPROOT_DEVICE));
181 if (!RtlCreateUnicodeString(&Device->DeviceID, DevicePath))
182 {
183 Status = STATUS_NO_MEMORY;
184 goto cleanup;
185 }
186 if (!RtlCreateUnicodeString(&Device->InstanceID, InstancePath))
187 {
188 Status = STATUS_NO_MEMORY;
189 goto cleanup;
190 }
191
192 /* Initialize a device object */
193 Status = IoCreateDevice(
194 PnpRootDeviceObject->DriverObject,
195 sizeof(PNPROOT_PDO_DEVICE_EXTENSION),
196 NULL,
197 FILE_DEVICE_CONTROLLER,
198 FILE_AUTOGENERATED_DEVICE_NAME,
199 FALSE,
200 &Device->Pdo);
201 if (!NT_SUCCESS(Status))
202 {
203 DPRINT("IoCreateDevice() failed with status 0x%08lx\n", Status);
204 Status = STATUS_NO_MEMORY;
205 goto cleanup;
206 }
207
208 PdoDeviceExtension = (PPNPROOT_PDO_DEVICE_EXTENSION)Device->Pdo->DeviceExtension;
209 RtlZeroMemory(PdoDeviceExtension, sizeof(PNPROOT_PDO_DEVICE_EXTENSION));
210 PdoDeviceExtension->Common.IsFDO = FALSE;
211 PdoDeviceExtension->DeviceInfo = Device;
212
213 Device->Pdo->Flags |= DO_BUS_ENUMERATED_DEVICE;
214 Device->Pdo->Flags &= ~DO_DEVICE_INITIALIZING;
215
216 InsertTailList(
217 &DeviceExtension->DeviceListHead,
218 &Device->ListEntry);
219 DeviceExtension->DeviceListCount++;
220
221 *PhysicalDeviceObject = Device->Pdo;
222 DPRINT("Created PDO %p (%wZ\\%wZ)\n", *PhysicalDeviceObject, &Device->DeviceID, &Device->InstanceID);
223 Device = NULL;
224 Status = STATUS_SUCCESS;
225
226 cleanup:
227 KeReleaseGuardedMutex(&DeviceExtension->DeviceListLock);
228 if (Device)
229 {
230 if (Device->Pdo)
231 IoDeleteDevice(Device->Pdo);
232 RtlFreeUnicodeString(&Device->DeviceID);
233 RtlFreeUnicodeString(&Device->InstanceID);
234 ExFreePoolWithTag(Device, TAG_PNP_ROOT);
235 }
236 return Status;
237 }
238
239 static NTSTATUS NTAPI
240 QueryStringCallback(
241 IN PWSTR ValueName,
242 IN ULONG ValueType,
243 IN PVOID ValueData,
244 IN ULONG ValueLength,
245 IN PVOID Context,
246 IN PVOID EntryContext)
247 {
248 PUNICODE_STRING Destination = (PUNICODE_STRING)EntryContext;
249 UNICODE_STRING Source;
250
251 if (ValueType != REG_SZ || ValueLength == 0 || ValueLength % sizeof(WCHAR) != 0)
252 return STATUS_SUCCESS;
253
254 Source.MaximumLength = Source.Length = ValueLength;
255 Source.Buffer = ValueData;
256 if (Source.Length > 0 && Source.Buffer[Source.Length / sizeof(WCHAR) - 1] == UNICODE_NULL)
257 Source.Length -= sizeof(WCHAR);
258 return RtlDuplicateUnicodeString(RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE, &Source, Destination);
259 }
260
261 static NTSTATUS NTAPI
262 QueryBinaryValueCallback(
263 IN PWSTR ValueName,
264 IN ULONG ValueType,
265 IN PVOID ValueData,
266 IN ULONG ValueLength,
267 IN PVOID Context,
268 IN PVOID EntryContext)
269 {
270 PBUFFER Buffer = (PBUFFER)EntryContext;
271 PVOID BinaryValue;
272
273 if (ValueLength == 0)
274 {
275 *Buffer->Data = NULL;
276 return STATUS_SUCCESS;
277 }
278
279 BinaryValue = ExAllocatePoolWithTag(PagedPool, ValueLength, TAG_PNP_ROOT);
280 if (BinaryValue == NULL)
281 return STATUS_NO_MEMORY;
282 RtlCopyMemory(BinaryValue, ValueData, ValueLength);
283 *Buffer->Data = BinaryValue;
284 if (Buffer->Length) *Buffer->Length = ValueLength;
285 return STATUS_SUCCESS;
286 }
287
288 static NTSTATUS
289 EnumerateDevices(
290 IN PDEVICE_OBJECT DeviceObject)
291 {
292 PPNPROOT_FDO_DEVICE_EXTENSION DeviceExtension;
293 PKEY_BASIC_INFORMATION KeyInfo = NULL, SubKeyInfo = NULL;
294 UNICODE_STRING LegacyU = RTL_CONSTANT_STRING(L"LEGACY_");
295 UNICODE_STRING KeyName = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\" REGSTR_PATH_SYSTEMENUM L"\\" REGSTR_KEY_ROOTENUM);
296 UNICODE_STRING SubKeyName;
297 WCHAR DevicePath[MAX_PATH + 1];
298 RTL_QUERY_REGISTRY_TABLE QueryTable[5];
299 PPNPROOT_DEVICE Device = NULL;
300 HANDLE KeyHandle = INVALID_HANDLE_VALUE;
301 HANDLE SubKeyHandle = INVALID_HANDLE_VALUE;
302 HANDLE DeviceKeyHandle = INVALID_HANDLE_VALUE;
303 ULONG BufferSize;
304 ULONG ResultSize;
305 ULONG Index1, Index2;
306 BUFFER Buffer1, Buffer2;
307 NTSTATUS Status = STATUS_UNSUCCESSFUL;
308
309 DPRINT("EnumerateDevices(FDO %p)\n", DeviceObject);
310
311 DeviceExtension = (PPNPROOT_FDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
312 KeAcquireGuardedMutex(&DeviceExtension->DeviceListLock);
313
314 BufferSize = sizeof(KEY_BASIC_INFORMATION) + (MAX_PATH + 1) * sizeof(WCHAR);
315 KeyInfo = ExAllocatePoolWithTag(PagedPool, BufferSize, TAG_PNP_ROOT);
316 if (!KeyInfo)
317 {
318 DPRINT("ExAllocatePoolWithTag() failed\n");
319 Status = STATUS_NO_MEMORY;
320 goto cleanup;
321 }
322 SubKeyInfo = ExAllocatePoolWithTag(PagedPool, BufferSize, TAG_PNP_ROOT);
323 if (!SubKeyInfo)
324 {
325 DPRINT("ExAllocatePoolWithTag() failed\n");
326 Status = STATUS_NO_MEMORY;
327 goto cleanup;
328 }
329
330 Status = IopOpenRegistryKeyEx(&KeyHandle, NULL, &KeyName, KEY_ENUMERATE_SUB_KEYS);
331 if (!NT_SUCCESS(Status))
332 {
333 DPRINT("IopOpenRegistryKeyEx(%wZ) failed with status 0x%08lx\n", &KeyName, Status);
334 goto cleanup;
335 }
336
337 /* Devices are sub-sub-keys of 'KeyName'. KeyName is already opened as
338 * KeyHandle. We'll first do a first enumeration to have first level keys,
339 * and an inner one to have the real devices list.
340 */
341 Index1 = 0;
342 while (TRUE)
343 {
344 Status = ZwEnumerateKey(
345 KeyHandle,
346 Index1,
347 KeyBasicInformation,
348 KeyInfo,
349 BufferSize,
350 &ResultSize);
351 if (Status == STATUS_NO_MORE_ENTRIES)
352 {
353 Status = STATUS_SUCCESS;
354 break;
355 }
356 else if (!NT_SUCCESS(Status))
357 {
358 DPRINT("ZwEnumerateKey() failed with status 0x%08lx\n", Status);
359 goto cleanup;
360 }
361
362 /* Terminate the string */
363 KeyInfo->Name[KeyInfo->NameLength / sizeof(WCHAR)] = 0;
364
365 /* Check if it is a legacy driver */
366 RtlInitUnicodeString(&SubKeyName, KeyInfo->Name);
367 if (RtlPrefixUnicodeString(&LegacyU, &SubKeyName, FALSE))
368 {
369 DPRINT("Ignoring legacy driver '%wZ'\n", &SubKeyName);
370 Index1++;
371 continue;
372 }
373
374 /* Open the key */
375 Status = IopOpenRegistryKeyEx(&SubKeyHandle, KeyHandle, &SubKeyName, KEY_ENUMERATE_SUB_KEYS);
376 if (!NT_SUCCESS(Status))
377 {
378 DPRINT("IopOpenRegistryKeyEx() failed with status 0x%08lx\n", Status);
379 break;
380 }
381
382 /* Enumerate the sub-keys */
383 Index2 = 0;
384 while (TRUE)
385 {
386 Status = ZwEnumerateKey(
387 SubKeyHandle,
388 Index2,
389 KeyBasicInformation,
390 SubKeyInfo,
391 BufferSize,
392 &ResultSize);
393 if (Status == STATUS_NO_MORE_ENTRIES)
394 break;
395 else if (!NT_SUCCESS(Status))
396 {
397 DPRINT("ZwEnumerateKey() failed with status 0x%08lx\n", Status);
398 break;
399 }
400
401 /* Terminate the string */
402 SubKeyInfo->Name[SubKeyInfo->NameLength / sizeof(WCHAR)] = 0;
403
404 _snwprintf(
405 DevicePath,
406 sizeof(DevicePath) / sizeof(WCHAR),
407 L"%s\\%s", REGSTR_KEY_ROOTENUM, KeyInfo->Name);
408 DPRINT("Found device %S\\%s!\n", DevicePath, SubKeyInfo->Name);
409 if (LocateChildDevice(DeviceExtension, DevicePath, SubKeyInfo->Name, &Device) == STATUS_NO_SUCH_DEVICE)
410 {
411 /* Create a PPNPROOT_DEVICE object, and add if in the list of known devices */
412 Device = (PPNPROOT_DEVICE)ExAllocatePoolWithTag(PagedPool, sizeof(PNPROOT_DEVICE), TAG_PNP_ROOT);
413 if (!Device)
414 {
415 DPRINT("ExAllocatePoolWithTag() failed\n");
416 Status = STATUS_NO_MEMORY;
417 goto cleanup;
418 }
419 RtlZeroMemory(Device, sizeof(PNPROOT_DEVICE));
420
421 /* Fill device ID and instance ID */
422 if (!RtlCreateUnicodeString(&Device->DeviceID, DevicePath))
423 {
424 DPRINT("RtlCreateUnicodeString() failed\n");
425 Status = STATUS_NO_MEMORY;
426 goto cleanup;
427 }
428
429 if (!RtlCreateUnicodeString(&Device->InstanceID, SubKeyInfo->Name))
430 {
431 DPRINT("RtlCreateUnicodeString() failed\n");
432 Status = STATUS_NO_MEMORY;
433 goto cleanup;
434 }
435
436 /* Open registry key to fill other informations */
437 Status = IopOpenRegistryKeyEx(&DeviceKeyHandle, SubKeyHandle, &Device->InstanceID, KEY_READ);
438 if (!NT_SUCCESS(Status))
439 {
440 DPRINT("IopOpenRegistryKeyEx() failed with status 0x%08lx\n", Status);
441 break;
442 }
443
444 /* Fill other informations */
445 Buffer1.Data = (PVOID *)&Device->ResourceRequirementsList;
446 Buffer1.Length = NULL;
447 Buffer2.Data = (PVOID *)&Device->ResourceList;
448 Buffer2.Length = &Device->ResourceListSize;
449 RtlZeroMemory(QueryTable, sizeof(QueryTable));
450 QueryTable[0].QueryRoutine = QueryStringCallback;
451 QueryTable[0].Name = L"DeviceDesc";
452 QueryTable[0].EntryContext = &Device->DeviceDescription;
453 QueryTable[1].Flags = RTL_QUERY_REGISTRY_SUBKEY;
454 QueryTable[1].Name = L"LogConf";
455 QueryTable[2].QueryRoutine = QueryBinaryValueCallback;
456 QueryTable[2].Name = L"BasicConfigVector";
457 QueryTable[2].EntryContext = &Buffer1;
458 QueryTable[3].QueryRoutine = QueryBinaryValueCallback;
459 QueryTable[3].Name = L"BootConfig";
460 QueryTable[3].EntryContext = &Buffer2;
461
462 Status = RtlQueryRegistryValues(
463 RTL_REGISTRY_HANDLE,
464 (PCWSTR)DeviceKeyHandle,
465 QueryTable,
466 NULL,
467 NULL);
468 if (!NT_SUCCESS(Status))
469 {
470 DPRINT("RtlQueryRegistryValues() failed with status 0x%08lx\n", Status);
471 break;
472 }
473
474 ZwClose(DeviceKeyHandle);
475 DeviceKeyHandle = INVALID_HANDLE_VALUE;
476
477 /* Insert the newly created device into the list */
478 InsertTailList(
479 &DeviceExtension->DeviceListHead,
480 &Device->ListEntry);
481 DeviceExtension->DeviceListCount++;
482 }
483 Device = NULL;
484
485 Index2++;
486 }
487
488 ZwClose(SubKeyHandle);
489 SubKeyHandle = INVALID_HANDLE_VALUE;
490 Index1++;
491 }
492
493 cleanup:
494 if (Device)
495 {
496 /* We have a device that has not been added to device list. We need to clean it up */
497 /* FIXME */
498 ExFreePoolWithTag(Device, TAG_PNP_ROOT);
499 }
500 if (DeviceKeyHandle != INVALID_HANDLE_VALUE)
501 ZwClose(DeviceKeyHandle);
502 if (SubKeyHandle != INVALID_HANDLE_VALUE)
503 ZwClose(SubKeyHandle);
504 if (KeyHandle != INVALID_HANDLE_VALUE)
505 ZwClose(KeyHandle);
506 if (KeyInfo)
507 ExFreePoolWithTag(KeyInfo, TAG_PNP_ROOT);
508 if (SubKeyInfo)
509 ExFreePoolWithTag(SubKeyInfo, TAG_PNP_ROOT);
510 KeReleaseGuardedMutex(&DeviceExtension->DeviceListLock);
511 return Status;
512 }
513
514 /* FUNCTION: Handle IRP_MN_QUERY_DEVICE_RELATIONS IRPs for the root bus device object
515 * ARGUMENTS:
516 * DeviceObject = Pointer to functional device object of the root bus driver
517 * Irp = Pointer to IRP that should be handled
518 * RETURNS:
519 * Status
520 */
521 static NTSTATUS
522 PnpRootQueryDeviceRelations(
523 IN PDEVICE_OBJECT DeviceObject,
524 IN PIRP Irp)
525 {
526 PPNPROOT_PDO_DEVICE_EXTENSION PdoDeviceExtension;
527 PPNPROOT_FDO_DEVICE_EXTENSION DeviceExtension;
528 PDEVICE_RELATIONS Relations = NULL, OtherRelations = (PDEVICE_RELATIONS)Irp->IoStatus.Information;
529 PPNPROOT_DEVICE Device = NULL;
530 ULONG Size;
531 NTSTATUS Status;
532 PLIST_ENTRY NextEntry;
533
534 DPRINT("PnpRootQueryDeviceRelations(FDO %p, Irp %p)\n", DeviceObject, Irp);
535
536 Status = EnumerateDevices(DeviceObject);
537 if (!NT_SUCCESS(Status))
538 {
539 DPRINT("EnumerateDevices() failed with status 0x%08lx\n", Status);
540 return Status;
541 }
542
543 DeviceExtension = (PPNPROOT_FDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
544
545 Size = FIELD_OFFSET(DEVICE_RELATIONS, Objects) + sizeof(PDEVICE_OBJECT) * DeviceExtension->DeviceListCount;
546 if (OtherRelations)
547 {
548 /* Another bus driver has already created a DEVICE_RELATIONS
549 * structure so we must merge this structure with our own */
550
551 Size += sizeof(PDEVICE_OBJECT) * OtherRelations->Count;
552 }
553 Relations = (PDEVICE_RELATIONS)ExAllocatePool(PagedPool, Size);
554 if (!Relations)
555 {
556 DPRINT("ExAllocatePoolWithTag() failed\n");
557 Status = STATUS_NO_MEMORY;
558 goto cleanup;
559 }
560 RtlZeroMemory(Relations, Size);
561 if (OtherRelations)
562 {
563 Relations->Count = OtherRelations->Count;
564 RtlCopyMemory(Relations->Objects, OtherRelations->Objects, sizeof(PDEVICE_OBJECT) * OtherRelations->Count);
565 }
566
567 KeAcquireGuardedMutex(&DeviceExtension->DeviceListLock);
568
569 /* Start looping */
570 for (NextEntry = DeviceExtension->DeviceListHead.Flink;
571 NextEntry != &DeviceExtension->DeviceListHead;
572 NextEntry = NextEntry->Flink)
573 {
574 /* Get the entry */
575 Device = CONTAINING_RECORD(NextEntry, PNPROOT_DEVICE, ListEntry);
576
577 if (!Device->Pdo)
578 {
579 /* Create a physical device object for the
580 * device as it does not already have one */
581 Status = IoCreateDevice(
582 DeviceObject->DriverObject,
583 sizeof(PNPROOT_PDO_DEVICE_EXTENSION),
584 NULL,
585 FILE_DEVICE_CONTROLLER,
586 FILE_AUTOGENERATED_DEVICE_NAME,
587 FALSE,
588 &Device->Pdo);
589 if (!NT_SUCCESS(Status))
590 {
591 DPRINT("IoCreateDevice() failed with status 0x%08lx\n", Status);
592 break;
593 }
594
595 PdoDeviceExtension = (PPNPROOT_PDO_DEVICE_EXTENSION)Device->Pdo->DeviceExtension;
596 RtlZeroMemory(PdoDeviceExtension, sizeof(PNPROOT_PDO_DEVICE_EXTENSION));
597 PdoDeviceExtension->Common.IsFDO = FALSE;
598 PdoDeviceExtension->DeviceInfo = Device;
599
600 Device->Pdo->Flags |= DO_BUS_ENUMERATED_DEVICE;
601 Device->Pdo->Flags &= ~DO_DEVICE_INITIALIZING;
602 }
603
604 /* Reference the physical device object. The PnP manager
605 will dereference it again when it is no longer needed */
606 ObReferenceObject(Device->Pdo);
607
608 Relations->Objects[Relations->Count++] = Device->Pdo;
609 }
610 KeReleaseGuardedMutex(&DeviceExtension->DeviceListLock);
611
612 Irp->IoStatus.Information = (ULONG_PTR)Relations;
613
614 cleanup:
615 if (!NT_SUCCESS(Status))
616 {
617 if (OtherRelations)
618 ExFreePool(OtherRelations);
619 if (Relations)
620 ExFreePool(Relations);
621 if (Device && Device->Pdo)
622 {
623 IoDeleteDevice(Device->Pdo);
624 Device->Pdo = NULL;
625 }
626 }
627
628 return Status;
629 }
630
631 /*
632 * FUNCTION: Handle Plug and Play IRPs for the root bus device object
633 * ARGUMENTS:
634 * DeviceObject = Pointer to functional device object of the root bus driver
635 * Irp = Pointer to IRP that should be handled
636 * RETURNS:
637 * Status
638 */
639 static NTSTATUS
640 PnpRootFdoPnpControl(
641 IN PDEVICE_OBJECT DeviceObject,
642 IN PIRP Irp)
643 {
644 PPNPROOT_FDO_DEVICE_EXTENSION DeviceExtension;
645 PIO_STACK_LOCATION IrpSp;
646 NTSTATUS Status;
647
648 DeviceExtension = (PPNPROOT_FDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
649 Status = Irp->IoStatus.Status;
650 IrpSp = IoGetCurrentIrpStackLocation(Irp);
651
652 switch (IrpSp->MinorFunction)
653 {
654 case IRP_MN_QUERY_DEVICE_RELATIONS:
655 DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS\n");
656 Status = PnpRootQueryDeviceRelations(DeviceObject, Irp);
657 break;
658
659 case IRP_MN_START_DEVICE:
660 DPRINT("IRP_MJ_PNP / IRP_MN_START_DEVICE\n");
661 if (!IoForwardIrpSynchronously(DeviceExtension->Ldo, Irp))
662 Status = STATUS_UNSUCCESSFUL;
663 else
664 {
665 Status = Irp->IoStatus.Status;
666 if (NT_SUCCESS(Status))
667 DeviceExtension->State = dsStarted;
668 }
669 break;
670
671 case IRP_MN_STOP_DEVICE:
672 DPRINT("IRP_MJ_PNP / IRP_MN_STOP_DEVICE\n");
673 /* Root device cannot be stopped */
674 Status = STATUS_NOT_SUPPORTED;
675 break;
676
677 default:
678 DPRINT("IRP_MJ_PNP / Unknown minor function 0x%lx\n", IrpSp->MinorFunction);
679 Status = STATUS_NOT_IMPLEMENTED;
680 break;
681 }
682
683 if (Status != STATUS_PENDING)
684 {
685 Irp->IoStatus.Status = Status;
686 IoCompleteRequest(Irp, IO_NO_INCREMENT);
687 }
688
689 return Status;
690 }
691
692 static NTSTATUS
693 PdoQueryDeviceRelations(
694 IN PDEVICE_OBJECT DeviceObject,
695 IN PIRP Irp,
696 IN PIO_STACK_LOCATION IrpSp)
697 {
698 PDEVICE_RELATIONS Relations;
699 DEVICE_RELATION_TYPE RelationType;
700 NTSTATUS Status = Irp->IoStatus.Status;
701
702 RelationType = IrpSp->Parameters.QueryDeviceRelations.Type;
703
704 switch (RelationType)
705 {
706 /* FIXME: remove */
707 case BusRelations:
708 {
709 if (IoGetAttachedDevice(DeviceObject) != DeviceObject)
710 {
711 /* We're not alone in the stack */
712 DPRINT1("PnP is misbehaving ; don't know how to handle IRP_MN_QUERY_DEVICE_RELATIONS / BusRelations\n");
713 }
714 break;
715 }
716
717 case TargetDeviceRelation:
718 {
719 DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / TargetDeviceRelation\n");
720 Relations = (PDEVICE_RELATIONS)ExAllocatePool(PagedPool, sizeof(DEVICE_RELATIONS));
721 if (!Relations)
722 {
723 DPRINT("ExAllocatePoolWithTag() failed\n");
724 Status = STATUS_NO_MEMORY;
725 }
726 else
727 {
728 ObReferenceObject(DeviceObject);
729 Relations->Count = 1;
730 Relations->Objects[0] = DeviceObject;
731 Status = STATUS_SUCCESS;
732 Irp->IoStatus.Information = (ULONG_PTR)Relations;
733 }
734 break;
735 }
736
737 default:
738 {
739 DPRINT1("IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / unknown relation type 0x%lx\n", RelationType);
740 }
741 }
742
743 return Status;
744 }
745
746 static NTSTATUS
747 PdoQueryCapabilities(
748 IN PDEVICE_OBJECT DeviceObject,
749 IN PIRP Irp,
750 IN PIO_STACK_LOCATION IrpSp)
751 {
752 PPNPROOT_FDO_DEVICE_EXTENSION DeviceExtension;
753 PDEVICE_CAPABILITIES DeviceCapabilities;
754
755 DeviceExtension = (PPNPROOT_FDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
756 DeviceCapabilities = IrpSp->Parameters.DeviceCapabilities.Capabilities;
757
758 if (DeviceCapabilities->Version != 1)
759 return STATUS_REVISION_MISMATCH;
760
761 DeviceCapabilities->UniqueID = TRUE;
762 /* FIXME: Fill other fields */
763
764 return STATUS_SUCCESS;
765 }
766
767 static NTSTATUS
768 PdoQueryResources(
769 IN PDEVICE_OBJECT DeviceObject,
770 IN PIRP Irp,
771 IN PIO_STACK_LOCATION IrpSp)
772 {
773 PPNPROOT_PDO_DEVICE_EXTENSION DeviceExtension;
774 PCM_RESOURCE_LIST ResourceList;
775
776 DeviceExtension = (PPNPROOT_PDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
777
778 if (DeviceExtension->DeviceInfo->ResourceList == NULL)
779 {
780 /* Create an empty resource list */
781 ResourceList = ExAllocatePool(PagedPool, sizeof(CM_RESOURCE_LIST));
782 if (!ResourceList)
783 return STATUS_NO_MEMORY;
784
785 ResourceList->Count = 0;
786
787 Irp->IoStatus.Information = (ULONG_PTR)ResourceList;
788 }
789 else
790 {
791 /* Copy existing resource requirement list */
792 ResourceList = ExAllocatePool(
793 PagedPool,
794 FIELD_OFFSET(CM_RESOURCE_LIST, List) + DeviceExtension->DeviceInfo->ResourceListSize);
795 if (!ResourceList)
796 return STATUS_NO_MEMORY;
797
798 ResourceList->Count = 1;
799 RtlCopyMemory(
800 &ResourceList->List,
801 DeviceExtension->DeviceInfo->ResourceList,
802 DeviceExtension->DeviceInfo->ResourceListSize);
803 Irp->IoStatus.Information = (ULONG_PTR)ResourceList;
804 }
805
806 return STATUS_SUCCESS;
807 }
808
809 static NTSTATUS
810 PdoQueryResourceRequirements(
811 IN PDEVICE_OBJECT DeviceObject,
812 IN PIRP Irp,
813 IN PIO_STACK_LOCATION IrpSp)
814 {
815 PPNPROOT_PDO_DEVICE_EXTENSION DeviceExtension;
816 PIO_RESOURCE_REQUIREMENTS_LIST ResourceList;
817 ULONG ResourceListSize = FIELD_OFFSET(IO_RESOURCE_REQUIREMENTS_LIST, List);
818
819 DeviceExtension = (PPNPROOT_PDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
820
821 if (DeviceExtension->DeviceInfo->ResourceRequirementsList == NULL)
822 {
823 /* Create an empty resource list */
824 ResourceList = ExAllocatePool(PagedPool, ResourceListSize);
825 if (!ResourceList)
826 return STATUS_NO_MEMORY;
827
828 RtlZeroMemory(ResourceList, ResourceListSize);
829 ResourceList->ListSize = ResourceListSize;
830
831 Irp->IoStatus.Information = (ULONG_PTR)ResourceList;
832 }
833 else
834 {
835 /* Copy existing resource requirement list */
836 ResourceList = ExAllocatePool(PagedPool, DeviceExtension->DeviceInfo->ResourceRequirementsList->ListSize);
837 if (!ResourceList)
838 return STATUS_NO_MEMORY;
839
840 RtlCopyMemory(
841 ResourceList,
842 DeviceExtension->DeviceInfo->ResourceRequirementsList,
843 DeviceExtension->DeviceInfo->ResourceRequirementsList->ListSize);
844 Irp->IoStatus.Information = (ULONG_PTR)ResourceList;
845 }
846
847 return STATUS_SUCCESS;
848 }
849
850 static NTSTATUS
851 PdoQueryDeviceText(
852 IN PDEVICE_OBJECT DeviceObject,
853 IN PIRP Irp,
854 IN PIO_STACK_LOCATION IrpSp)
855 {
856 PPNPROOT_PDO_DEVICE_EXTENSION DeviceExtension;
857 DEVICE_TEXT_TYPE DeviceTextType;
858 NTSTATUS Status = Irp->IoStatus.Status;
859
860 DeviceExtension = (PPNPROOT_PDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
861 DeviceTextType = IrpSp->Parameters.QueryDeviceText.DeviceTextType;
862
863 switch (DeviceTextType)
864 {
865 case DeviceTextDescription:
866 {
867 UNICODE_STRING String;
868 DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_TEXT / DeviceTextDescription\n");
869
870 Status = RtlDuplicateUnicodeString(
871 RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE,
872 &DeviceExtension->DeviceInfo->DeviceDescription,
873 &String);
874 Irp->IoStatus.Information = (ULONG_PTR)String.Buffer;
875 break;
876 }
877
878 case DeviceTextLocationInformation:
879 {
880 DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_TEXT / DeviceTextLocationInformation\n");
881 Status = STATUS_NOT_SUPPORTED;
882 break;
883 }
884
885 default:
886 {
887 DPRINT1("IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_TEXT / unknown query id type 0x%lx\n", DeviceTextType);
888 }
889 }
890
891 return Status;
892 }
893
894 static NTSTATUS
895 PdoQueryId(
896 IN PDEVICE_OBJECT DeviceObject,
897 IN PIRP Irp,
898 IN PIO_STACK_LOCATION IrpSp)
899 {
900 PPNPROOT_PDO_DEVICE_EXTENSION DeviceExtension;
901 BUS_QUERY_ID_TYPE IdType;
902 NTSTATUS Status = Irp->IoStatus.Status;
903
904 DeviceExtension = (PPNPROOT_PDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
905 IdType = IrpSp->Parameters.QueryId.IdType;
906
907 switch (IdType)
908 {
909 case BusQueryDeviceID:
910 {
911 UNICODE_STRING String;
912 DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_ID / BusQueryDeviceID\n");
913
914 Status = RtlDuplicateUnicodeString(
915 RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE,
916 &DeviceExtension->DeviceInfo->DeviceID,
917 &String);
918 Irp->IoStatus.Information = (ULONG_PTR)String.Buffer;
919 break;
920 }
921
922 case BusQueryHardwareIDs:
923 case BusQueryCompatibleIDs:
924 {
925 /* Optional, do nothing */
926 break;
927 }
928
929 case BusQueryInstanceID:
930 {
931 UNICODE_STRING String;
932 DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_ID / BusQueryInstanceID\n");
933
934 Status = RtlDuplicateUnicodeString(
935 RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE,
936 &DeviceExtension->DeviceInfo->InstanceID,
937 &String);
938 Irp->IoStatus.Information = (ULONG_PTR)String.Buffer;
939 break;
940 }
941
942 default:
943 {
944 DPRINT1("IRP_MJ_PNP / IRP_MN_QUERY_ID / unknown query id type 0x%lx\n", IdType);
945 }
946 }
947
948 return Status;
949 }
950
951 static NTSTATUS
952 PdoQueryBusInformation(
953 IN PDEVICE_OBJECT DeviceObject,
954 IN PIRP Irp,
955 IN PIO_STACK_LOCATION IrpSp)
956 {
957 PPNP_BUS_INFORMATION BusInfo;
958 NTSTATUS Status;
959
960 BusInfo = (PPNP_BUS_INFORMATION)ExAllocatePoolWithTag(PagedPool, sizeof(PNP_BUS_INFORMATION), TAG_PNP_ROOT);
961 if (!BusInfo)
962 Status = STATUS_NO_MEMORY;
963 else
964 {
965 RtlCopyMemory(
966 &BusInfo->BusTypeGuid,
967 &GUID_BUS_TYPE_INTERNAL,
968 sizeof(BusInfo->BusTypeGuid));
969 BusInfo->LegacyBusType = PNPBus;
970 /* We're the only root bus enumerator on the computer */
971 BusInfo->BusNumber = 0;
972 Irp->IoStatus.Information = (ULONG_PTR)BusInfo;
973 Status = STATUS_SUCCESS;
974 }
975
976 return Status;
977 }
978
979 /*
980 * FUNCTION: Handle Plug and Play IRPs for the child device
981 * ARGUMENTS:
982 * DeviceObject = Pointer to physical device object of the child device
983 * Irp = Pointer to IRP that should be handled
984 * RETURNS:
985 * Status
986 */
987 static NTSTATUS
988 PnpRootPdoPnpControl(
989 IN PDEVICE_OBJECT DeviceObject,
990 IN PIRP Irp)
991 {
992 PPNPROOT_PDO_DEVICE_EXTENSION DeviceExtension;
993 PIO_STACK_LOCATION IrpSp;
994 NTSTATUS Status;
995
996 DeviceExtension = (PPNPROOT_PDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
997 Status = Irp->IoStatus.Status;
998 IrpSp = IoGetCurrentIrpStackLocation(Irp);
999
1000 switch (IrpSp->MinorFunction)
1001 {
1002 case IRP_MN_START_DEVICE: /* 0x00 */
1003 DPRINT("IRP_MJ_PNP / IRP_MN_START_DEVICE\n");
1004 Status = STATUS_SUCCESS;
1005 break;
1006
1007 case IRP_MN_QUERY_DEVICE_RELATIONS: /* 0x07 */
1008 Status = PdoQueryDeviceRelations(DeviceObject, Irp, IrpSp);
1009 break;
1010
1011 case IRP_MN_QUERY_CAPABILITIES: /* 0x09 */
1012 DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_CAPABILITIES\n");
1013 Status = PdoQueryCapabilities(DeviceObject, Irp, IrpSp);
1014 break;
1015
1016 case IRP_MN_QUERY_RESOURCES: /* 0x0a */
1017 DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_RESOURCES\n");
1018 Status = PdoQueryResources(DeviceObject, Irp, IrpSp);
1019 break;
1020
1021 case IRP_MN_QUERY_RESOURCE_REQUIREMENTS: /* 0x0b */
1022 DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_RESOURCE_REQUIREMENTS\n");
1023 Status = PdoQueryResourceRequirements(DeviceObject, Irp, IrpSp);
1024 break;
1025
1026 case IRP_MN_QUERY_DEVICE_TEXT: /* 0x0c */
1027 DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_RESOURCE_REQUIREMENTS\n");
1028 Status = PdoQueryDeviceText(DeviceObject, Irp, IrpSp);
1029 break;
1030
1031 case IRP_MN_FILTER_RESOURCE_REQUIREMENTS: /* 0x0d */
1032 DPRINT("IRP_MJ_PNP / IRP_MN_FILTER_RESOURCE_REQUIREMENTS\n");
1033 break;
1034
1035 case IRP_MN_QUERY_ID: /* 0x13 */
1036 Status = PdoQueryId(DeviceObject, Irp, IrpSp);
1037 break;
1038
1039 case IRP_MN_QUERY_BUS_INFORMATION: /* 0x15 */
1040 DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_BUS_INFORMATION\n");
1041 Status = PdoQueryBusInformation(DeviceObject, Irp, IrpSp);
1042 break;
1043
1044 default:
1045 DPRINT1("IRP_MJ_PNP / Unknown minor function 0x%lx\n", IrpSp->MinorFunction);
1046 Status = STATUS_NOT_IMPLEMENTED;
1047 break;
1048 }
1049
1050 if (Status != STATUS_PENDING)
1051 {
1052 Irp->IoStatus.Status = Status;
1053 IoCompleteRequest(Irp, IO_NO_INCREMENT);
1054 }
1055
1056 return Status;
1057 }
1058
1059 /*
1060 * FUNCTION: Handle Plug and Play IRPs
1061 * ARGUMENTS:
1062 * DeviceObject = Pointer to PDO or FDO
1063 * Irp = Pointer to IRP that should be handled
1064 * RETURNS:
1065 * Status
1066 */
1067 static NTSTATUS NTAPI
1068 PnpRootPnpControl(
1069 IN PDEVICE_OBJECT DeviceObject,
1070 IN PIRP Irp)
1071 {
1072 PPNPROOT_COMMON_DEVICE_EXTENSION DeviceExtension;
1073 NTSTATUS Status;
1074
1075 DeviceExtension = (PPNPROOT_COMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
1076
1077 if (DeviceExtension->IsFDO)
1078 Status = PnpRootFdoPnpControl(DeviceObject, Irp);
1079 else
1080 Status = PnpRootPdoPnpControl(DeviceObject, Irp);
1081
1082 return Status;
1083 }
1084
1085 NTSTATUS
1086 NTAPI
1087 PnpRootAddDevice(
1088 IN PDRIVER_OBJECT DriverObject,
1089 IN PDEVICE_OBJECT PhysicalDeviceObject)
1090 {
1091 PPNPROOT_FDO_DEVICE_EXTENSION DeviceExtension;
1092 NTSTATUS Status;
1093
1094 DPRINT("PnpRootAddDevice(DriverObject %p, Pdo %p)\n", DriverObject, PhysicalDeviceObject);
1095
1096 if (!PhysicalDeviceObject)
1097 {
1098 DPRINT("PhysicalDeviceObject 0x%p\n", PhysicalDeviceObject);
1099 Status = STATUS_INSUFFICIENT_RESOURCES;
1100 KeBugCheckEx(PHASE1_INITIALIZATION_FAILED, Status, 0, 0, 0);
1101 }
1102
1103 Status = IoCreateDevice(
1104 DriverObject,
1105 sizeof(PNPROOT_FDO_DEVICE_EXTENSION),
1106 NULL,
1107 FILE_DEVICE_BUS_EXTENDER,
1108 FILE_DEVICE_SECURE_OPEN,
1109 TRUE,
1110 &PnpRootDeviceObject);
1111 if (!NT_SUCCESS(Status))
1112 {
1113 DPRINT("IoCreateDevice() failed with status 0x%08lx\n", Status);
1114 KeBugCheckEx(PHASE1_INITIALIZATION_FAILED, Status, 0, 0, 0);
1115 }
1116 DPRINT("Created FDO %p\n", PnpRootDeviceObject);
1117
1118 DeviceExtension = (PPNPROOT_FDO_DEVICE_EXTENSION)PnpRootDeviceObject->DeviceExtension;
1119 RtlZeroMemory(DeviceExtension, sizeof(PNPROOT_FDO_DEVICE_EXTENSION));
1120
1121 DeviceExtension->Common.IsFDO = TRUE;
1122 DeviceExtension->State = dsStopped;
1123 InitializeListHead(&DeviceExtension->DeviceListHead);
1124 DeviceExtension->DeviceListCount = 0;
1125 KeInitializeGuardedMutex(&DeviceExtension->DeviceListLock);
1126
1127 Status = IoAttachDeviceToDeviceStackSafe(
1128 PnpRootDeviceObject,
1129 PhysicalDeviceObject,
1130 &DeviceExtension->Ldo);
1131 if (!NT_SUCCESS(Status))
1132 {
1133 DPRINT("IoAttachDeviceToDeviceStackSafe() failed with status 0x%08lx\n", Status);
1134 KeBugCheckEx(PHASE1_INITIALIZATION_FAILED, Status, 0, 0, 0);
1135 }
1136
1137 PnpRootDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
1138
1139 DPRINT("Done AddDevice()\n");
1140
1141 return STATUS_SUCCESS;
1142 }
1143
1144 NTSTATUS NTAPI
1145 PnpRootDriverEntry(
1146 IN PDRIVER_OBJECT DriverObject,
1147 IN PUNICODE_STRING RegistryPath)
1148 {
1149 DPRINT("PnpRootDriverEntry(%p %wZ)\n", DriverObject, RegistryPath);
1150
1151 DriverObject->DriverExtension->AddDevice = PnpRootAddDevice;
1152
1153 DriverObject->MajorFunction[IRP_MJ_PNP] = PnpRootPnpControl;
1154 //DriverObject->MajorFunction[IRP_MJ_POWER] = PnpRootPowerControl;
1155
1156 return STATUS_SUCCESS;
1157 }