xref: /reactos/ntoskrnl/io/pnpmgr/devnode.c (revision a1fc312a)
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 
18 PDEVICE_NODE IopRootDeviceNode;
19 KSPIN_LOCK IopDeviceTreeLock;
20 
21 LONG IopNumberDeviceNodes;
22 
23 /* FUNCTIONS *****************************************************************/
24 
25 PDEVICE_NODE
26 FASTCALL
27 IopGetDeviceNode(
28     _In_ PDEVICE_OBJECT DeviceObject)
29 {
30     return ((PEXTENDED_DEVOBJ_EXTENSION)DeviceObject->DeviceObjectExtension)->DeviceNode;
31 }
32 
33 PDEVICE_NODE
34 PipAllocateDeviceNode(
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 
79 VOID
80 PiInsertDevNode(
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 
107 PNP_DEVNODE_STATE
108 PiSetDevNodeState(
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 
131 VOID
132 PiSetDevNodeProblem(
133     _In_ PDEVICE_NODE DeviceNode,
134     _In_ UINT32 Problem)
135 {
136     DeviceNode->Flags |= DNF_HAS_PROBLEM;
137     DeviceNode->Problem = Problem;
138 }
139 
140 VOID
141 PiClearDevNodeProblem(
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
161 NTSTATUS
162 IopCreateDeviceNode(
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 
326 NTSTATUS
327 IopFreeDeviceNode(
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 
400 static
401 NTSTATUS
402 IopFindNextDeviceNodeForTraversal(
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 
434 NTSTATUS
435 IopTraverseDeviceTree(
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 }
484