Reactos
1/*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4 * PURPOSE: PnP manager device tree functions
5 * COPYRIGHT: Casper S. Hornstrup (chorns@users.sourceforge.net)
6 * 2007 Hervé Poussineau (hpoussin@reactos.org)
7 * 2010-2012 Cameron Gutman (cameron.gutman@reactos.org)
8 */
9
10/* INCLUDES ******************************************************************/
11
12#include <ntoskrnl.h>
13#define NDEBUG
14#include <debug.h>
15
16/* GLOBALS *******************************************************************/
17
18PDEVICE_NODE IopRootDeviceNode;
19KSPIN_LOCK IopDeviceTreeLock;
20
21LONG IopNumberDeviceNodes;
22
23/* FUNCTIONS *****************************************************************/
24
25PDEVICE_NODE
26FASTCALL
27IopGetDeviceNode(
28 _In_ PDEVICE_OBJECT DeviceObject)
29{
30 return ((PEXTENDED_DEVOBJ_EXTENSION)DeviceObject->DeviceObjectExtension)->DeviceNode;
31}
32
33PDEVICE_NODE
34PipAllocateDeviceNode(
35 _In_opt_ PDEVICE_OBJECT PhysicalDeviceObject)
36{
37 PDEVICE_NODE DeviceNode;
38 PAGED_CODE();
39
40 /* Allocate it */
41 DeviceNode = ExAllocatePoolZero(NonPagedPool, sizeof(DEVICE_NODE), TAG_IO_DEVNODE);
42 if (!DeviceNode)
43 {
44 return NULL;
45 }
46
47 /* Statistics */
48 InterlockedIncrement(&IopNumberDeviceNodes);
49
50 /* Set it up */
51 DeviceNode->InterfaceType = InterfaceTypeUndefined;
52 DeviceNode->BusNumber = -1;
53 DeviceNode->ChildInterfaceType = InterfaceTypeUndefined;
54 DeviceNode->ChildBusNumber = -1;
55 DeviceNode->ChildBusTypeIndex = -1;
56 DeviceNode->State = DeviceNodeUninitialized;
57// KeInitializeEvent(&DeviceNode->EnumerationMutex, SynchronizationEvent, TRUE);
58 InitializeListHead(&DeviceNode->DeviceArbiterList);
59 InitializeListHead(&DeviceNode->DeviceTranslatorList);
60 InitializeListHead(&DeviceNode->TargetDeviceNotify);
61 InitializeListHead(&DeviceNode->DockInfo.ListEntry);
62 InitializeListHead(&DeviceNode->PendedSetInterfaceState);
63
64 /* Check if there is a PDO */
65 if (PhysicalDeviceObject)
66 {
67 /* Link it and remove the init flag */
68 DeviceNode->PhysicalDeviceObject = PhysicalDeviceObject;
69 ((PEXTENDED_DEVOBJ_EXTENSION)PhysicalDeviceObject->DeviceObjectExtension)->DeviceNode = DeviceNode;
70 PhysicalDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
71 }
72
73 DPRINT("Allocated devnode 0x%p\n", DeviceNode);
74
75 /* Return the node */
76 return DeviceNode;
77}
78
79VOID
80PiInsertDevNode(
81 _In_ PDEVICE_NODE DeviceNode,
82 _In_ PDEVICE_NODE ParentNode)
83{
84 KIRQL oldIrql;
85
86 ASSERT(DeviceNode->Parent == NULL);
87
88 KeAcquireSpinLock(&IopDeviceTreeLock, &oldIrql);
89 DeviceNode->Parent = ParentNode;
90 DeviceNode->Sibling = NULL;
91 if (ParentNode->LastChild == NULL)
92 {
93 ParentNode->Child = DeviceNode;
94 ParentNode->LastChild = DeviceNode;
95 }
96 else
97 {
98 ParentNode->LastChild->Sibling = DeviceNode;
99 ParentNode->LastChild = DeviceNode;
100 }
101 KeReleaseSpinLock(&IopDeviceTreeLock, oldIrql);
102 DeviceNode->Level = ParentNode->Level + 1;
103
104 DPRINT("Inserted devnode 0x%p to parent 0x%p\n", DeviceNode, ParentNode);
105}
106
107PNP_DEVNODE_STATE
108PiSetDevNodeState(
109 _In_ PDEVICE_NODE DeviceNode,
110 _In_ PNP_DEVNODE_STATE NewState)
111{
112 KIRQL oldIrql;
113
114 KeAcquireSpinLock(&IopDeviceTreeLock, &oldIrql);
115
116 PNP_DEVNODE_STATE prevState = DeviceNode->State;
117 if (prevState != NewState)
118 {
119 DeviceNode->State = NewState;
120 DeviceNode->PreviousState = prevState;
121 DeviceNode->StateHistory[DeviceNode->StateHistoryEntry++] = prevState;
122 DeviceNode->StateHistoryEntry %= DEVNODE_HISTORY_SIZE;
123 }
124
125 KeReleaseSpinLock(&IopDeviceTreeLock, oldIrql);
126
127 DPRINT("%wZ Changed state 0x%x => 0x%x\n", &DeviceNode->InstancePath, prevState, NewState);
128 return prevState;
129}
130
131VOID
132PiSetDevNodeProblem(
133 _In_ PDEVICE_NODE DeviceNode,
134 _In_ UINT32 Problem)
135{
136 DeviceNode->Flags |= DNF_HAS_PROBLEM;
137 DeviceNode->Problem = Problem;
138}
139
140VOID
141PiClearDevNodeProblem(
142 _In_ PDEVICE_NODE DeviceNode)
143{
144 DeviceNode->Flags &= ~DNF_HAS_PROBLEM;
145 DeviceNode->Problem = 0;
146}
147
148/**
149 * @brief Creates a device node
150 *
151 * @param[in] ParentNode Pointer to parent device node
152 * @param[in] PhysicalDeviceObject Pointer to PDO for device object. Pass NULL to have
153 * the root device node create one (eg. for legacy drivers)
154 * @param[in] ServiceName The service (driver) name for a node. Pass NULL
155 * to set UNKNOWN as a service
156 * @param[out] DeviceNode Pointer to storage for created device node
157 *
158 * @return Status, indicating the result of an operation
159 */
160#if 0
161NTSTATUS
162IopCreateDeviceNode(
163 _In_ PDEVICE_NODE ParentNode,
164 _In_opt_ PDEVICE_OBJECT PhysicalDeviceObject,
165 _In_opt_ PUNICODE_STRING ServiceName,
166 _Out_ PDEVICE_NODE *DeviceNode)
167{
168 PDEVICE_NODE Node;
169 NTSTATUS Status;
170 UNICODE_STRING FullServiceName;
171 UNICODE_STRING LegacyPrefix = RTL_CONSTANT_STRING(L"LEGACY_");
172 UNICODE_STRING UnknownDeviceName = RTL_CONSTANT_STRING(L"UNKNOWN");
173 UNICODE_STRING KeyName, ClassName;
174 PUNICODE_STRING ServiceName1;
175 ULONG LegacyValue;
176 UNICODE_STRING ClassGUID;
177 HANDLE InstanceHandle;
178
179 DPRINT("ParentNode 0x%p PhysicalDeviceObject 0x%p ServiceName %wZ\n",
180 ParentNode, PhysicalDeviceObject, ServiceName);
181
182 Node = ExAllocatePoolWithTag(NonPagedPool, sizeof(DEVICE_NODE), TAG_IO_DEVNODE);
183 if (!Node)
184 {
185 return STATUS_INSUFFICIENT_RESOURCES;
186 }
187
188 RtlZeroMemory(Node, sizeof(DEVICE_NODE));
189 InitializeListHead(&Node->TargetDeviceNotify);
190
191 if (!ServiceName)
192 ServiceName1 = &UnknownDeviceName;
193 else
194 ServiceName1 = ServiceName;
195
196 if (!PhysicalDeviceObject)
197 {
198 FullServiceName.MaximumLength = LegacyPrefix.Length + ServiceName1->Length + sizeof(UNICODE_NULL);
199 FullServiceName.Length = 0;
200 FullServiceName.Buffer = ExAllocatePool(PagedPool, FullServiceName.MaximumLength);
201 if (!FullServiceName.Buffer)
202 {
203 ExFreePoolWithTag(Node, TAG_IO_DEVNODE);
204 return STATUS_INSUFFICIENT_RESOURCES;
205 }
206
207 RtlAppendUnicodeStringToString(&FullServiceName, &LegacyPrefix);
208 RtlAppendUnicodeStringToString(&FullServiceName, ServiceName1);
209 RtlUpcaseUnicodeString(&FullServiceName, &FullServiceName, FALSE);
210
211 Status = PnpRootCreateDevice(&FullServiceName, NULL, &PhysicalDeviceObject, &Node->InstancePath);
212 if (!NT_SUCCESS(Status))
213 {
214 DPRINT1("PnpRootCreateDevice() failed with status 0x%08X\n", Status);
215 ExFreePool(FullServiceName.Buffer);
216 ExFreePoolWithTag(Node, TAG_IO_DEVNODE);
217 return Status;
218 }
219
220 /* Create the device key for legacy drivers */
221 Status = IopCreateDeviceKeyPath(&Node->InstancePath, REG_OPTION_VOLATILE, &InstanceHandle);
222 if (!NT_SUCCESS(Status))
223 {
224 ExFreePool(FullServiceName.Buffer);
225 ExFreePoolWithTag(Node, TAG_IO_DEVNODE);
226 return Status;
227 }
228
229 Node->ServiceName.MaximumLength = ServiceName1->Length + sizeof(UNICODE_NULL);
230 Node->ServiceName.Length = 0;
231 Node->ServiceName.Buffer = ExAllocatePool(PagedPool, Node->ServiceName.MaximumLength);
232 if (!Node->ServiceName.Buffer)
233 {
234 ZwClose(InstanceHandle);
235 ExFreePool(FullServiceName.Buffer);
236 ExFreePoolWithTag(Node, TAG_IO_DEVNODE);
237 return Status;
238 }
239
240 RtlCopyUnicodeString(&Node->ServiceName, ServiceName1);
241
242 if (ServiceName)
243 {
244 RtlInitUnicodeString(&KeyName, L"Service");
245 Status = ZwSetValueKey(InstanceHandle, &KeyName, 0, REG_SZ, ServiceName->Buffer, ServiceName->Length + sizeof(UNICODE_NULL));
246 }
247
248 if (NT_SUCCESS(Status))
249 {
250 RtlInitUnicodeString(&KeyName, L"Legacy");
251 LegacyValue = 1;
252 Status = ZwSetValueKey(InstanceHandle, &KeyName, 0, REG_DWORD, &LegacyValue, sizeof(LegacyValue));
253
254 RtlInitUnicodeString(&KeyName, L"ConfigFlags");
255 LegacyValue = 0;
256 ZwSetValueKey(InstanceHandle, &KeyName, 0, REG_DWORD, &LegacyValue, sizeof(LegacyValue));
257
258 if (NT_SUCCESS(Status))
259 {
260 RtlInitUnicodeString(&KeyName, L"Class");
261 RtlInitUnicodeString(&ClassName, L"LegacyDriver");
262 Status = ZwSetValueKey(InstanceHandle, &KeyName, 0, REG_SZ, ClassName.Buffer, ClassName.Length + sizeof(UNICODE_NULL));
263 if (NT_SUCCESS(Status))
264 {
265 RtlInitUnicodeString(&KeyName, L"ClassGUID");
266 RtlInitUnicodeString(&ClassGUID, L"{8ECC055D-047F-11D1-A537-0000F8753ED1}");
267 Status = ZwSetValueKey(InstanceHandle, &KeyName, 0, REG_SZ, ClassGUID.Buffer, ClassGUID.Length + sizeof(UNICODE_NULL));
268 if (NT_SUCCESS(Status))
269 {
270 // FIXME: Retrieve the real "description" by looking at the "DisplayName" string
271 // of the corresponding CurrentControlSet\Services\xxx entry for this driver.
272 RtlInitUnicodeString(&KeyName, L"DeviceDesc");
273 Status = ZwSetValueKey(InstanceHandle, &KeyName, 0, REG_SZ, ServiceName1->Buffer, ServiceName1->Length + sizeof(UNICODE_NULL));
274 }
275 }
276 }
277 }
278
279 ZwClose(InstanceHandle);
280 ExFreePool(FullServiceName.Buffer);
281
282 if (!NT_SUCCESS(Status))
283 {
284 ExFreePool(Node->ServiceName.Buffer);
285 ExFreePoolWithTag(Node, TAG_IO_DEVNODE);
286 return Status;
287 }
288
289 IopDeviceNodeSetFlag(Node, DNF_LEGACY_DRIVER);
290 IopDeviceNodeSetFlag(Node, DNF_PROCESSED);
291 IopDeviceNodeSetFlag(Node, DNF_ADDED);
292 IopDeviceNodeSetFlag(Node, DNF_STARTED);
293 }
294
295 Node->PhysicalDeviceObject = PhysicalDeviceObject;
296
297 ((PEXTENDED_DEVOBJ_EXTENSION)PhysicalDeviceObject->DeviceObjectExtension)->DeviceNode = Node;
298
299 if (ParentNode)
300 {
301 KeAcquireSpinLock(&IopDeviceTreeLock, &OldIrql);
302 Node->Parent = ParentNode;
303 Node->Sibling = NULL;
304 if (ParentNode->LastChild == NULL)
305 {
306 ParentNode->Child = Node;
307 ParentNode->LastChild = Node;
308 }
309 else
310 {
311 ParentNode->LastChild->Sibling = Node;
312 ParentNode->LastChild = Node;
313 }
314 KeReleaseSpinLock(&IopDeviceTreeLock, OldIrql);
315 Node->Level = ParentNode->Level + 1;
316 }
317
318 PhysicalDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
319
320 *DeviceNode = Node;
321
322 return STATUS_SUCCESS;
323}
324#endif
325
326NTSTATUS
327IopFreeDeviceNode(
328 _In_ PDEVICE_NODE DeviceNode)
329{
330 KIRQL OldIrql;
331 PDEVICE_NODE PrevSibling = NULL;
332
333 ASSERT(DeviceNode->PhysicalDeviceObject);
334 /* All children must be deleted before a parent is deleted */
335 ASSERT(DeviceNode->Child == NULL);
336 /* This is the only state where we are allowed to remove the node */
337 ASSERT(DeviceNode->State == DeviceNodeRemoved);
338 /* No notifications should be registered for this device */
339 ASSERT(IsListEmpty(&DeviceNode->TargetDeviceNotify));
340
341 KeAcquireSpinLock(&IopDeviceTreeLock, &OldIrql);
342
343 /* Get previous sibling */
344 if (DeviceNode->Parent && DeviceNode->Parent->Child != DeviceNode)
345 {
346 PrevSibling = DeviceNode->Parent->Child;
347 while (PrevSibling->Sibling != DeviceNode)
348 PrevSibling = PrevSibling->Sibling;
349 }
350
351 /* Unlink from parent if it exists */
352 if (DeviceNode->Parent)
353 {
354 if (DeviceNode->Parent->LastChild == DeviceNode)
355 {
356 DeviceNode->Parent->LastChild = PrevSibling;
357 if (PrevSibling)
358 PrevSibling->Sibling = NULL;
359 }
360 if (DeviceNode->Parent->Child == DeviceNode)
361 DeviceNode->Parent->Child = DeviceNode->Sibling;
362 }
363
364 /* Unlink from sibling list */
365 if (PrevSibling)
366 PrevSibling->Sibling = DeviceNode->Sibling;
367
368 KeReleaseSpinLock(&IopDeviceTreeLock, OldIrql);
369
370 RtlFreeUnicodeString(&DeviceNode->InstancePath);
371
372 RtlFreeUnicodeString(&DeviceNode->ServiceName);
373
374 if (DeviceNode->ResourceList)
375 {
376 ExFreePool(DeviceNode->ResourceList);
377 }
378
379 if (DeviceNode->ResourceListTranslated)
380 {
381 ExFreePool(DeviceNode->ResourceListTranslated);
382 }
383
384 if (DeviceNode->ResourceRequirements)
385 {
386 ExFreePool(DeviceNode->ResourceRequirements);
387 }
388
389 if (DeviceNode->BootResources)
390 {
391 ExFreePool(DeviceNode->BootResources);
392 }
393
394 ((PEXTENDED_DEVOBJ_EXTENSION)DeviceNode->PhysicalDeviceObject->DeviceObjectExtension)->DeviceNode = NULL;
395 ExFreePoolWithTag(DeviceNode, TAG_IO_DEVNODE);
396
397 return STATUS_SUCCESS;
398}
399
400static
401NTSTATUS
402IopFindNextDeviceNodeForTraversal(
403 _In_ PDEVICETREE_TRAVERSE_CONTEXT Context)
404{
405 /* If we have a child, simply go down the tree */
406 if (Context->DeviceNode->Child != NULL)
407 {
408 ASSERT(Context->DeviceNode->Child->Parent == Context->DeviceNode);
409 Context->DeviceNode = Context->DeviceNode->Child;
410 return STATUS_SUCCESS;
411 }
412
413 while (Context->DeviceNode != Context->FirstDeviceNode)
414 {
415 /* All children processed -- go sideways */
416 if (Context->DeviceNode->Sibling != NULL)
417 {
418 ASSERT(Context->DeviceNode->Sibling->Parent == Context->DeviceNode->Parent);
419 Context->DeviceNode = Context->DeviceNode->Sibling;
420 return STATUS_SUCCESS;
421 }
422
423 /* We're the last sibling -- go back up */
424 ASSERT(Context->DeviceNode->Parent->LastChild == Context->DeviceNode);
425 Context->DeviceNode = Context->DeviceNode->Parent;
426
427 /* We already visited the parent and all its children, so keep looking */
428 }
429
430 /* Done with all children of the start node -- stop enumeration */
431 return STATUS_UNSUCCESSFUL;
432}
433
434NTSTATUS
435IopTraverseDeviceTree(
436 _In_ PDEVICETREE_TRAVERSE_CONTEXT Context)
437{
438 NTSTATUS Status;
439 PDEVICE_NODE DeviceNode;
440
441 DPRINT("Context 0x%p\n", Context);
442
443 DPRINT("IopTraverseDeviceTree(DeviceNode 0x%p FirstDeviceNode 0x%p Action %p Context 0x%p)\n",
444 Context->DeviceNode, Context->FirstDeviceNode, Context->Action, Context->Context);
445
446 /* Start from the specified device node */
447 Context->DeviceNode = Context->FirstDeviceNode;
448
449 /* Traverse the device tree */
450 do
451 {
452 DeviceNode = Context->DeviceNode;
453
454 /* HACK: Keep a reference to the PDO so we can keep traversing the tree
455 * if the device is deleted. In a perfect world, children would have to be
456 * deleted before their parents, and we'd restart the traversal after
457 * deleting a device node. */
458 ObReferenceObject(DeviceNode->PhysicalDeviceObject);
459
460 /* Call the action routine */
461 Status = (Context->Action)(DeviceNode, Context->Context);
462 if (NT_SUCCESS(Status))
463 {
464 /* Find next device node */
465 ASSERT(Context->DeviceNode == DeviceNode);
466 Status = IopFindNextDeviceNodeForTraversal(Context);
467 }
468
469 /* We need to either abort or make progress */
470 ASSERT(!NT_SUCCESS(Status) || Context->DeviceNode != DeviceNode);
471
472 ObDereferenceObject(DeviceNode->PhysicalDeviceObject);
473 } while (NT_SUCCESS(Status));
474
475 if (Status == STATUS_UNSUCCESSFUL)
476 {
477 /* The action routine just wanted to terminate the traversal with status
478 code STATUS_SUCCESS */
479 Status = STATUS_SUCCESS;
480 }
481
482 return Status;
483}