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