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