xref: /reactos/ntoskrnl/io/pnpmgr/devnode.c (revision 8a92b556)
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 NTAPI
35 PipAllocateDeviceNode(
36     _In_opt_ PDEVICE_OBJECT PhysicalDeviceObject)
37 {
38     PDEVICE_NODE DeviceNode;
39     PAGED_CODE();
40 
41     /* Allocate it */
42     DeviceNode = ExAllocatePoolWithTag(NonPagedPool, sizeof(DEVICE_NODE), TAG_IO_DEVNODE);
43     if (!DeviceNode) return DeviceNode;
44 
45     /* Statistics */
46     InterlockedIncrement(&IopNumberDeviceNodes);
47 
48     /* Set it up */
49     RtlZeroMemory(DeviceNode, sizeof(DEVICE_NODE));
50     DeviceNode->InterfaceType = InterfaceTypeUndefined;
51     DeviceNode->BusNumber = -1;
52     DeviceNode->ChildInterfaceType = InterfaceTypeUndefined;
53     DeviceNode->ChildBusNumber = -1;
54     DeviceNode->ChildBusTypeIndex = -1;
55 //    KeInitializeEvent(&DeviceNode->EnumerationMutex, SynchronizationEvent, TRUE);
56     InitializeListHead(&DeviceNode->DeviceArbiterList);
57     InitializeListHead(&DeviceNode->DeviceTranslatorList);
58     InitializeListHead(&DeviceNode->TargetDeviceNotify);
59     InitializeListHead(&DeviceNode->DockInfo.ListEntry);
60     InitializeListHead(&DeviceNode->PendedSetInterfaceState);
61 
62     /* Check if there is a PDO */
63     if (PhysicalDeviceObject)
64     {
65         /* Link it and remove the init flag */
66         DeviceNode->PhysicalDeviceObject = PhysicalDeviceObject;
67         ((PEXTENDED_DEVOBJ_EXTENSION)PhysicalDeviceObject->DeviceObjectExtension)->DeviceNode = DeviceNode;
68         PhysicalDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
69     }
70 
71     /* Return the node */
72     return DeviceNode;
73 }
74 
75 /**
76  * @brief      Creates a device node
77  *
78  * @param[in]  ParentNode           Pointer to parent device node
79  * @param[in]  PhysicalDeviceObject Pointer to PDO for device object. Pass NULL to have
80  *                                  the root device node create one (eg. for legacy drivers)
81  * @param[in]  ServiceName          The service (driver) name for a node. Pass NULL
82  *                                  to set UNKNOWN as a service
83  * @param[out] DeviceNode           Pointer to storage for created device node
84  *
85  * @return     Status, indicating the result of an operation
86  */
87 
88 NTSTATUS
89 IopCreateDeviceNode(
90     _In_ PDEVICE_NODE ParentNode,
91     _In_opt_ PDEVICE_OBJECT PhysicalDeviceObject,
92     _In_opt_ PUNICODE_STRING ServiceName,
93     _Out_ PDEVICE_NODE *DeviceNode)
94 {
95     PDEVICE_NODE Node;
96     NTSTATUS Status;
97     KIRQL OldIrql;
98     UNICODE_STRING FullServiceName;
99     UNICODE_STRING LegacyPrefix = RTL_CONSTANT_STRING(L"LEGACY_");
100     UNICODE_STRING UnknownDeviceName = RTL_CONSTANT_STRING(L"UNKNOWN");
101     UNICODE_STRING KeyName, ClassName;
102     PUNICODE_STRING ServiceName1;
103     ULONG LegacyValue;
104     UNICODE_STRING ClassGUID;
105     HANDLE InstanceHandle;
106 
107     DPRINT("ParentNode 0x%p PhysicalDeviceObject 0x%p ServiceName %wZ\n",
108            ParentNode, PhysicalDeviceObject, ServiceName);
109 
110     Node = ExAllocatePoolWithTag(NonPagedPool, sizeof(DEVICE_NODE), TAG_IO_DEVNODE);
111     if (!Node)
112     {
113         return STATUS_INSUFFICIENT_RESOURCES;
114     }
115 
116     RtlZeroMemory(Node, sizeof(DEVICE_NODE));
117     InitializeListHead(&Node->TargetDeviceNotify);
118 
119     if (!ServiceName)
120         ServiceName1 = &UnknownDeviceName;
121     else
122         ServiceName1 = ServiceName;
123 
124     if (!PhysicalDeviceObject)
125     {
126         FullServiceName.MaximumLength = LegacyPrefix.Length + ServiceName1->Length + sizeof(UNICODE_NULL);
127         FullServiceName.Length = 0;
128         FullServiceName.Buffer = ExAllocatePool(PagedPool, FullServiceName.MaximumLength);
129         if (!FullServiceName.Buffer)
130         {
131             ExFreePoolWithTag(Node, TAG_IO_DEVNODE);
132             return STATUS_INSUFFICIENT_RESOURCES;
133         }
134 
135         RtlAppendUnicodeStringToString(&FullServiceName, &LegacyPrefix);
136         RtlAppendUnicodeStringToString(&FullServiceName, ServiceName1);
137         RtlUpcaseUnicodeString(&FullServiceName, &FullServiceName, FALSE);
138 
139         Status = PnpRootCreateDevice(&FullServiceName, NULL, &PhysicalDeviceObject, &Node->InstancePath);
140         if (!NT_SUCCESS(Status))
141         {
142             DPRINT1("PnpRootCreateDevice() failed with status 0x%08X\n", Status);
143             ExFreePool(FullServiceName.Buffer);
144             ExFreePoolWithTag(Node, TAG_IO_DEVNODE);
145             return Status;
146         }
147 
148         /* Create the device key for legacy drivers */
149         Status = IopCreateDeviceKeyPath(&Node->InstancePath, REG_OPTION_VOLATILE, &InstanceHandle);
150         if (!NT_SUCCESS(Status))
151         {
152             ExFreePool(FullServiceName.Buffer);
153             ExFreePoolWithTag(Node, TAG_IO_DEVNODE);
154             return Status;
155         }
156 
157         Node->ServiceName.MaximumLength = ServiceName1->Length + sizeof(UNICODE_NULL);
158         Node->ServiceName.Length = 0;
159         Node->ServiceName.Buffer = ExAllocatePool(PagedPool, Node->ServiceName.MaximumLength);
160         if (!Node->ServiceName.Buffer)
161         {
162             ZwClose(InstanceHandle);
163             ExFreePool(FullServiceName.Buffer);
164             ExFreePoolWithTag(Node, TAG_IO_DEVNODE);
165             return Status;
166         }
167 
168         RtlCopyUnicodeString(&Node->ServiceName, ServiceName1);
169 
170         if (ServiceName)
171         {
172             RtlInitUnicodeString(&KeyName, L"Service");
173             Status = ZwSetValueKey(InstanceHandle, &KeyName, 0, REG_SZ, ServiceName->Buffer, ServiceName->Length + sizeof(UNICODE_NULL));
174         }
175 
176         if (NT_SUCCESS(Status))
177         {
178             RtlInitUnicodeString(&KeyName, L"Legacy");
179             LegacyValue = 1;
180             Status = ZwSetValueKey(InstanceHandle, &KeyName, 0, REG_DWORD, &LegacyValue, sizeof(LegacyValue));
181 
182             RtlInitUnicodeString(&KeyName, L"ConfigFlags");
183             LegacyValue = 0;
184             ZwSetValueKey(InstanceHandle, &KeyName, 0, REG_DWORD, &LegacyValue, sizeof(LegacyValue));
185 
186             if (NT_SUCCESS(Status))
187             {
188                 RtlInitUnicodeString(&KeyName, L"Class");
189                 RtlInitUnicodeString(&ClassName, L"LegacyDriver");
190                 Status = ZwSetValueKey(InstanceHandle, &KeyName, 0, REG_SZ, ClassName.Buffer, ClassName.Length + sizeof(UNICODE_NULL));
191                 if (NT_SUCCESS(Status))
192                 {
193                     RtlInitUnicodeString(&KeyName, L"ClassGUID");
194                     RtlInitUnicodeString(&ClassGUID, L"{8ECC055D-047F-11D1-A537-0000F8753ED1}");
195                     Status = ZwSetValueKey(InstanceHandle, &KeyName, 0, REG_SZ, ClassGUID.Buffer, ClassGUID.Length + sizeof(UNICODE_NULL));
196                     if (NT_SUCCESS(Status))
197                     {
198                         // FIXME: Retrieve the real "description" by looking at the "DisplayName" string
199                         // of the corresponding CurrentControlSet\Services\xxx entry for this driver.
200                         RtlInitUnicodeString(&KeyName, L"DeviceDesc");
201                         Status = ZwSetValueKey(InstanceHandle, &KeyName, 0, REG_SZ, ServiceName1->Buffer, ServiceName1->Length + sizeof(UNICODE_NULL));
202                     }
203                 }
204             }
205         }
206 
207         ZwClose(InstanceHandle);
208         ExFreePool(FullServiceName.Buffer);
209 
210         if (!NT_SUCCESS(Status))
211         {
212             ExFreePool(Node->ServiceName.Buffer);
213             ExFreePoolWithTag(Node, TAG_IO_DEVNODE);
214             return Status;
215         }
216 
217         IopDeviceNodeSetFlag(Node, DNF_LEGACY_DRIVER);
218         IopDeviceNodeSetFlag(Node, DNF_PROCESSED);
219         IopDeviceNodeSetFlag(Node, DNF_ADDED);
220         IopDeviceNodeSetFlag(Node, DNF_STARTED);
221     }
222 
223     Node->PhysicalDeviceObject = PhysicalDeviceObject;
224 
225     ((PEXTENDED_DEVOBJ_EXTENSION)PhysicalDeviceObject->DeviceObjectExtension)->DeviceNode = Node;
226 
227     if (ParentNode)
228     {
229         KeAcquireSpinLock(&IopDeviceTreeLock, &OldIrql);
230         Node->Parent = ParentNode;
231         Node->Sibling = NULL;
232         if (ParentNode->LastChild == NULL)
233         {
234             ParentNode->Child = Node;
235             ParentNode->LastChild = Node;
236         }
237         else
238         {
239             ParentNode->LastChild->Sibling = Node;
240             ParentNode->LastChild = Node;
241         }
242         KeReleaseSpinLock(&IopDeviceTreeLock, OldIrql);
243         Node->Level = ParentNode->Level + 1;
244     }
245 
246     PhysicalDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
247 
248     *DeviceNode = Node;
249 
250     return STATUS_SUCCESS;
251 }
252 
253 NTSTATUS
254 IopFreeDeviceNode(
255     _In_ PDEVICE_NODE DeviceNode)
256 {
257     KIRQL OldIrql;
258     PDEVICE_NODE PrevSibling = NULL;
259 
260     /* All children must be deleted before a parent is deleted */
261     ASSERT(!DeviceNode->Child);
262     ASSERT(DeviceNode->PhysicalDeviceObject);
263     /* No notifications should be registered for this device */
264     ASSERT(IsListEmpty(&DeviceNode->TargetDeviceNotify));
265 
266     KeAcquireSpinLock(&IopDeviceTreeLock, &OldIrql);
267 
268     /* Get previous sibling */
269     if (DeviceNode->Parent && DeviceNode->Parent->Child != DeviceNode)
270     {
271         PrevSibling = DeviceNode->Parent->Child;
272         while (PrevSibling->Sibling != DeviceNode)
273             PrevSibling = PrevSibling->Sibling;
274     }
275 
276     /* Unlink from parent if it exists */
277     if (DeviceNode->Parent)
278     {
279         if (DeviceNode->Parent->LastChild == DeviceNode)
280         {
281             DeviceNode->Parent->LastChild = PrevSibling;
282             if (PrevSibling)
283                 PrevSibling->Sibling = NULL;
284         }
285         if (DeviceNode->Parent->Child == DeviceNode)
286             DeviceNode->Parent->Child = DeviceNode->Sibling;
287     }
288 
289     /* Unlink from sibling list */
290     if (PrevSibling)
291         PrevSibling->Sibling = DeviceNode->Sibling;
292 
293     KeReleaseSpinLock(&IopDeviceTreeLock, OldIrql);
294 
295     RtlFreeUnicodeString(&DeviceNode->InstancePath);
296 
297     RtlFreeUnicodeString(&DeviceNode->ServiceName);
298 
299     if (DeviceNode->ResourceList)
300     {
301         ExFreePool(DeviceNode->ResourceList);
302     }
303 
304     if (DeviceNode->ResourceListTranslated)
305     {
306         ExFreePool(DeviceNode->ResourceListTranslated);
307     }
308 
309     if (DeviceNode->ResourceRequirements)
310     {
311         ExFreePool(DeviceNode->ResourceRequirements);
312     }
313 
314     if (DeviceNode->BootResources)
315     {
316         ExFreePool(DeviceNode->BootResources);
317     }
318 
319     ((PEXTENDED_DEVOBJ_EXTENSION)DeviceNode->PhysicalDeviceObject->DeviceObjectExtension)->DeviceNode = NULL;
320     ExFreePoolWithTag(DeviceNode, TAG_IO_DEVNODE);
321 
322     return STATUS_SUCCESS;
323 }
324 
325 static
326 NTSTATUS
327 IopFindNextDeviceNodeForTraversal(
328     _In_ PDEVICETREE_TRAVERSE_CONTEXT Context)
329 {
330     /* If we have a child, simply go down the tree */
331     if (Context->DeviceNode->Child != NULL)
332     {
333         ASSERT(Context->DeviceNode->Child->Parent == Context->DeviceNode);
334         Context->DeviceNode = Context->DeviceNode->Child;
335         return STATUS_SUCCESS;
336     }
337 
338     while (Context->DeviceNode != Context->FirstDeviceNode)
339     {
340         /* All children processed -- go sideways */
341         if (Context->DeviceNode->Sibling != NULL)
342         {
343             ASSERT(Context->DeviceNode->Sibling->Parent == Context->DeviceNode->Parent);
344             Context->DeviceNode = Context->DeviceNode->Sibling;
345             return STATUS_SUCCESS;
346         }
347 
348         /* We're the last sibling -- go back up */
349         ASSERT(Context->DeviceNode->Parent->LastChild == Context->DeviceNode);
350         Context->DeviceNode = Context->DeviceNode->Parent;
351 
352         /* We already visited the parent and all its children, so keep looking */
353     }
354 
355     /* Done with all children of the start node -- stop enumeration */
356     return STATUS_UNSUCCESSFUL;
357 }
358 
359 NTSTATUS
360 IopTraverseDeviceTree(
361     _In_ PDEVICETREE_TRAVERSE_CONTEXT Context)
362 {
363     NTSTATUS Status;
364     PDEVICE_NODE DeviceNode;
365 
366     DPRINT("Context 0x%p\n", Context);
367 
368     DPRINT("IopTraverseDeviceTree(DeviceNode 0x%p  FirstDeviceNode 0x%p  Action %p  Context 0x%p)\n",
369            Context->DeviceNode, Context->FirstDeviceNode, Context->Action, Context->Context);
370 
371     /* Start from the specified device node */
372     Context->DeviceNode = Context->FirstDeviceNode;
373 
374     /* Traverse the device tree */
375     do
376     {
377         DeviceNode = Context->DeviceNode;
378 
379         /* HACK: Keep a reference to the PDO so we can keep traversing the tree
380          * if the device is deleted. In a perfect world, children would have to be
381          * deleted before their parents, and we'd restart the traversal after
382          * deleting a device node. */
383         ObReferenceObject(DeviceNode->PhysicalDeviceObject);
384 
385         /* Call the action routine */
386         Status = (Context->Action)(DeviceNode, Context->Context);
387         if (NT_SUCCESS(Status))
388         {
389             /* Find next device node */
390             ASSERT(Context->DeviceNode == DeviceNode);
391             Status = IopFindNextDeviceNodeForTraversal(Context);
392         }
393 
394         /* We need to either abort or make progress */
395         ASSERT(!NT_SUCCESS(Status) || Context->DeviceNode != DeviceNode);
396 
397         ObDereferenceObject(DeviceNode->PhysicalDeviceObject);
398     } while (NT_SUCCESS(Status));
399 
400     if (Status == STATUS_UNSUCCESSFUL)
401     {
402         /* The action routine just wanted to terminate the traversal with status
403         code STATUS_SUCCESS */
404         Status = STATUS_SUCCESS;
405     }
406 
407     return Status;
408 }
409