1 /* 2 * PROJECT: ReactOS Kernel 3 * COPYRIGHT: GPL - See COPYING in the top level directory 4 * FILE: ntoskrnl/io/pnpmgr/pnpreport.c 5 * PURPOSE: Device Changes Reporting Functions 6 * PROGRAMMERS: Cameron Gutman (cameron.gutman@reactos.org) 7 * Pierre Schweitzer 8 */ 9 10 /* INCLUDES ******************************************************************/ 11 12 #include <ntoskrnl.h> 13 #define NDEBUG 14 #include <debug.h> 15 16 /* TYPES *******************************************************************/ 17 18 typedef struct _INTERNAL_WORK_QUEUE_ITEM 19 { 20 WORK_QUEUE_ITEM WorkItem; 21 PDEVICE_OBJECT PhysicalDeviceObject; 22 PDEVICE_CHANGE_COMPLETE_CALLBACK Callback; 23 PVOID Context; 24 PTARGET_DEVICE_CUSTOM_NOTIFICATION NotificationStructure; 25 } INTERNAL_WORK_QUEUE_ITEM, *PINTERNAL_WORK_QUEUE_ITEM; 26 27 NTSTATUS 28 IopSetDeviceInstanceData(HANDLE InstanceKey, 29 PDEVICE_NODE DeviceNode); 30 31 NTSTATUS 32 IopActionInterrogateDeviceStack(PDEVICE_NODE DeviceNode, 33 PVOID Context); 34 35 NTSTATUS 36 PpSetCustomTargetEvent(IN PDEVICE_OBJECT DeviceObject, 37 IN OUT PKEVENT SyncEvent OPTIONAL, 38 IN OUT PNTSTATUS SyncStatus OPTIONAL, 39 IN PDEVICE_CHANGE_COMPLETE_CALLBACK Callback OPTIONAL, 40 IN PVOID Context OPTIONAL, 41 IN PTARGET_DEVICE_CUSTOM_NOTIFICATION NotificationStructure); 42 43 /* PRIVATE FUNCTIONS *********************************************************/ 44 45 PWCHAR 46 IopGetInterfaceTypeString(INTERFACE_TYPE IfType) 47 { 48 switch (IfType) 49 { 50 case Internal: 51 return L"Internal"; 52 53 case Isa: 54 return L"Isa"; 55 56 case Eisa: 57 return L"Eisa"; 58 59 case MicroChannel: 60 return L"MicroChannel"; 61 62 case TurboChannel: 63 return L"TurboChannel"; 64 65 case PCIBus: 66 return L"PCIBus"; 67 68 case VMEBus: 69 return L"VMEBus"; 70 71 case NuBus: 72 return L"NuBus"; 73 74 case PCMCIABus: 75 return L"PCMCIABus"; 76 77 case CBus: 78 return L"CBus"; 79 80 case MPIBus: 81 return L"MPIBus"; 82 83 case MPSABus: 84 return L"MPSABus"; 85 86 case ProcessorInternal: 87 return L"ProcessorInternal"; 88 89 case PNPISABus: 90 return L"PNPISABus"; 91 92 case PNPBus: 93 return L"PNPBus"; 94 95 case Vmcs: 96 return L"Vmcs"; 97 98 default: 99 DPRINT1("Invalid bus type: %d\n", IfType); 100 return NULL; 101 } 102 } 103 104 VOID 105 NTAPI 106 IopReportTargetDeviceChangeAsyncWorker(PVOID Context) 107 { 108 PINTERNAL_WORK_QUEUE_ITEM Item; 109 110 Item = (PINTERNAL_WORK_QUEUE_ITEM)Context; 111 PpSetCustomTargetEvent(Item->PhysicalDeviceObject, NULL, NULL, Item->Callback, Item->Context, Item->NotificationStructure); 112 ObDereferenceObject(Item->PhysicalDeviceObject); 113 ExFreePoolWithTag(Context, ' pP'); 114 } 115 116 NTSTATUS 117 PpSetCustomTargetEvent(IN PDEVICE_OBJECT DeviceObject, 118 IN OUT PKEVENT SyncEvent OPTIONAL, 119 IN OUT PNTSTATUS SyncStatus OPTIONAL, 120 IN PDEVICE_CHANGE_COMPLETE_CALLBACK Callback OPTIONAL, 121 IN PVOID Context OPTIONAL, 122 IN PTARGET_DEVICE_CUSTOM_NOTIFICATION NotificationStructure) 123 { 124 ASSERT(NotificationStructure != NULL); 125 ASSERT(DeviceObject != NULL); 126 127 if (SyncEvent) 128 { 129 ASSERT(SyncStatus); 130 *SyncStatus = STATUS_PENDING; 131 } 132 133 /* That call is totally wrong but notifications handler must be fixed first */ 134 IopNotifyPlugPlayNotification(DeviceObject, 135 EventCategoryTargetDeviceChange, 136 &GUID_PNP_CUSTOM_NOTIFICATION, 137 NotificationStructure, 138 NULL); 139 140 if (SyncEvent) 141 { 142 KeSetEvent(SyncEvent, IO_NO_INCREMENT, FALSE); 143 *SyncStatus = STATUS_SUCCESS; 144 } 145 146 return STATUS_SUCCESS; 147 } 148 149 /* PUBLIC FUNCTIONS **********************************************************/ 150 151 /* 152 * @implemented 153 */ 154 NTSTATUS 155 NTAPI 156 IoReportDetectedDevice(IN PDRIVER_OBJECT DriverObject, 157 IN INTERFACE_TYPE LegacyBusType, 158 IN ULONG BusNumber, 159 IN ULONG SlotNumber, 160 IN PCM_RESOURCE_LIST ResourceList, 161 IN PIO_RESOURCE_REQUIREMENTS_LIST ResourceRequirements OPTIONAL, 162 IN BOOLEAN ResourceAssigned, 163 IN OUT PDEVICE_OBJECT *DeviceObject OPTIONAL) 164 { 165 PDEVICE_NODE DeviceNode; 166 PDEVICE_OBJECT Pdo; 167 NTSTATUS Status; 168 HANDLE InstanceKey; 169 ULONG RequiredLength; 170 UNICODE_STRING ValueName, ServiceLongName, ServiceName; 171 WCHAR HardwareId[256]; 172 PWCHAR IfString; 173 ULONG IdLength; 174 ULONG LegacyValue; 175 176 DPRINT("IoReportDetectedDevice (DeviceObject %p, *DeviceObject %p)\n", 177 DeviceObject, DeviceObject ? *DeviceObject : NULL); 178 179 ServiceLongName = DriverObject->DriverExtension->ServiceKeyName; 180 ServiceName = ServiceLongName; 181 182 /* If the interface type is unknown, treat it as internal */ 183 if (LegacyBusType == InterfaceTypeUndefined) 184 LegacyBusType = Internal; 185 186 /* Get the string equivalent of the interface type */ 187 IfString = IopGetInterfaceTypeString(LegacyBusType); 188 189 /* If NULL is returned then it's a bad type */ 190 if (!IfString) 191 return STATUS_INVALID_PARAMETER; 192 193 /* 194 * Drivers that have been created via a direct IoCreateDriver() call 195 * have their ServiceKeyName set to \Driver\DriverName. We need to 196 * strip everything up to the last path separator and keep what remains. 197 */ 198 if (DriverObject->Flags & DRVO_BUILTIN_DRIVER) 199 { 200 /* 201 * Find the last path separator. 202 * NOTE: Since ServiceName is not necessarily NULL-terminated, 203 * we cannot use wcsrchr(). 204 */ 205 if (ServiceName.Buffer && ServiceName.Length >= sizeof(WCHAR)) 206 { 207 ValueName.Length = 1; 208 ValueName.Buffer = ServiceName.Buffer + (ServiceName.Length / sizeof(WCHAR)) - 1; 209 210 while ((ValueName.Buffer > ServiceName.Buffer) && (*ValueName.Buffer != L'\\')) 211 { 212 --ValueName.Buffer; 213 ++ValueName.Length; 214 } 215 if (*ValueName.Buffer == L'\\') 216 { 217 ++ValueName.Buffer; 218 --ValueName.Length; 219 } 220 ValueName.Length *= sizeof(WCHAR); 221 222 /* Shorten the string */ 223 ServiceName.MaximumLength -= (ServiceName.Length - ValueName.Length); 224 ServiceName.Length = ValueName.Length; 225 ServiceName.Buffer = ValueName.Buffer; 226 } 227 } 228 229 /* We use the caller's PDO if they supplied one */ 230 if (DeviceObject && *DeviceObject) 231 { 232 Pdo = *DeviceObject; 233 } 234 else 235 { 236 /* Create the PDO */ 237 Status = PnpRootCreateDevice(&ServiceName, 238 NULL, 239 &Pdo, 240 NULL); 241 if (!NT_SUCCESS(Status)) 242 { 243 DPRINT("PnpRootCreateDevice() failed (Status 0x%08lx)\n", Status); 244 return Status; 245 } 246 } 247 248 /* Create the device node for the new PDO */ 249 Status = IopCreateDeviceNode(IopRootDeviceNode, 250 Pdo, 251 NULL, 252 &DeviceNode); 253 if (!NT_SUCCESS(Status)) 254 { 255 DPRINT("IopCreateDeviceNode() failed (Status 0x%08lx)\n", Status); 256 return Status; 257 } 258 259 /* We're enumerated already */ 260 IopDeviceNodeSetFlag(DeviceNode, DNF_ENUMERATED); 261 262 /* We don't call AddDevice for devices reported this way */ 263 IopDeviceNodeSetFlag(DeviceNode, DNF_ADDED); 264 265 /* We don't send IRP_MN_START_DEVICE */ 266 IopDeviceNodeSetFlag(DeviceNode, DNF_STARTED); 267 268 /* We need to get device IDs */ 269 #if 0 270 IopDeviceNodeSetFlag(DeviceNode, DNF_NEED_QUERY_IDS); 271 #endif 272 273 /* This is a legacy driver for this device */ 274 IopDeviceNodeSetFlag(DeviceNode, DNF_LEGACY_DRIVER); 275 276 /* Perform a manual configuration of our device */ 277 IopActionInterrogateDeviceStack(DeviceNode, DeviceNode->Parent); 278 IopActionConfigureChildServices(DeviceNode, DeviceNode->Parent); 279 280 /* Open a handle to the instance path key */ 281 Status = IopCreateDeviceKeyPath(&DeviceNode->InstancePath, REG_OPTION_NON_VOLATILE, &InstanceKey); 282 if (!NT_SUCCESS(Status)) 283 return Status; 284 285 /* Save the driver name */ 286 RtlInitUnicodeString(&ValueName, L"Service"); 287 Status = ZwSetValueKey(InstanceKey, &ValueName, 0, REG_SZ, ServiceLongName.Buffer, ServiceLongName.Length + sizeof(UNICODE_NULL)); 288 if (!NT_SUCCESS(Status)) 289 { 290 DPRINT("Failed to write the Service name value: 0x%x\n", Status); 291 } 292 293 /* Report as non-legacy driver */ 294 RtlInitUnicodeString(&ValueName, L"Legacy"); 295 LegacyValue = 0; 296 Status = ZwSetValueKey(InstanceKey, &ValueName, 0, REG_DWORD, &LegacyValue, sizeof(LegacyValue)); 297 if (!NT_SUCCESS(Status)) 298 { 299 DPRINT("Failed to write the Legacy value: 0x%x\n", Status); 300 } 301 302 /* Add DETECTEDInterfaceType\DriverName */ 303 IdLength = 0; 304 IdLength += swprintf(&HardwareId[IdLength], 305 L"DETECTED%ls\\%wZ", 306 IfString, 307 &ServiceName); 308 IdLength++; 309 310 /* Add DETECTED\DriverName */ 311 IdLength += swprintf(&HardwareId[IdLength], 312 L"DETECTED\\%wZ", 313 &ServiceName); 314 IdLength++; 315 316 /* Terminate the string with another null */ 317 HardwareId[IdLength++] = UNICODE_NULL; 318 319 /* Store the value for CompatibleIDs */ 320 RtlInitUnicodeString(&ValueName, L"CompatibleIDs"); 321 Status = ZwSetValueKey(InstanceKey, &ValueName, 0, REG_MULTI_SZ, HardwareId, IdLength * sizeof(WCHAR)); 322 if (!NT_SUCCESS(Status)) 323 { 324 DPRINT("Failed to write the compatible IDs: 0x%x\n", Status); 325 ZwClose(InstanceKey); 326 return Status; 327 } 328 329 /* Add a hardware ID if the driver didn't report one */ 330 RtlInitUnicodeString(&ValueName, L"HardwareID"); 331 if (ZwQueryValueKey(InstanceKey, &ValueName, KeyValueBasicInformation, NULL, 0, &RequiredLength) == STATUS_OBJECT_NAME_NOT_FOUND) 332 { 333 /* Just use our most specific compatible ID */ 334 IdLength = 0; 335 IdLength += swprintf(&HardwareId[IdLength], 336 L"DETECTED%ls\\%wZ", 337 IfString, 338 &ServiceName); 339 IdLength++; 340 341 HardwareId[IdLength++] = UNICODE_NULL; 342 343 /* Write the value to the registry */ 344 Status = ZwSetValueKey(InstanceKey, &ValueName, 0, REG_MULTI_SZ, HardwareId, IdLength * sizeof(WCHAR)); 345 if (!NT_SUCCESS(Status)) 346 { 347 DPRINT("Failed to write the hardware ID: 0x%x\n", Status); 348 ZwClose(InstanceKey); 349 return Status; 350 } 351 } 352 353 /* Assign the resources to the device node */ 354 DeviceNode->BootResources = ResourceList; 355 DeviceNode->ResourceRequirements = ResourceRequirements; 356 357 /* Set appropriate flags */ 358 if (DeviceNode->BootResources) 359 IopDeviceNodeSetFlag(DeviceNode, DNF_HAS_BOOT_CONFIG); 360 361 if (!DeviceNode->ResourceRequirements && !DeviceNode->BootResources) 362 IopDeviceNodeSetFlag(DeviceNode, DNF_NO_RESOURCE_REQUIRED); 363 364 /* Write the resource information to the registry */ 365 IopSetDeviceInstanceData(InstanceKey, DeviceNode); 366 367 /* If the caller didn't get the resources assigned for us, do it now */ 368 if (!ResourceAssigned) 369 { 370 Status = IopAssignDeviceResources(DeviceNode); 371 372 /* See if we failed */ 373 if (!NT_SUCCESS(Status)) 374 { 375 DPRINT("Assigning resources failed: 0x%x\n", Status); 376 ZwClose(InstanceKey); 377 return Status; 378 } 379 } 380 381 /* Close the instance key handle */ 382 ZwClose(InstanceKey); 383 384 /* Register the given DO with PnP root if required */ 385 if (DeviceObject && *DeviceObject) 386 PnpRootRegisterDevice(*DeviceObject); 387 388 /* Report the device's enumeration to umpnpmgr */ 389 IopQueueTargetDeviceEvent(&GUID_DEVICE_ENUMERATED, 390 &DeviceNode->InstancePath); 391 392 /* Report the device's arrival to umpnpmgr */ 393 IopQueueTargetDeviceEvent(&GUID_DEVICE_ARRIVAL, 394 &DeviceNode->InstancePath); 395 396 DPRINT("Reported device: %S (%wZ)\n", HardwareId, &DeviceNode->InstancePath); 397 398 PiQueueDeviceAction(Pdo, PiActionEnumDeviceTree, NULL, NULL); 399 400 /* Return the PDO */ 401 if (DeviceObject) *DeviceObject = Pdo; 402 403 return STATUS_SUCCESS; 404 } 405 406 /* 407 * @halfplemented 408 */ 409 NTSTATUS 410 NTAPI 411 IoReportResourceForDetection(IN PDRIVER_OBJECT DriverObject, 412 IN PCM_RESOURCE_LIST DriverList OPTIONAL, 413 IN ULONG DriverListSize OPTIONAL, 414 IN PDEVICE_OBJECT DeviceObject OPTIONAL, 415 IN PCM_RESOURCE_LIST DeviceList OPTIONAL, 416 IN ULONG DeviceListSize OPTIONAL, 417 OUT PBOOLEAN ConflictDetected) 418 { 419 PCM_RESOURCE_LIST ResourceList; 420 NTSTATUS Status; 421 422 *ConflictDetected = FALSE; 423 424 if (!DriverList && !DeviceList) 425 return STATUS_INVALID_PARAMETER; 426 427 /* Find the real list */ 428 if (!DriverList) 429 ResourceList = DeviceList; 430 else 431 ResourceList = DriverList; 432 433 /* Look for a resource conflict */ 434 Status = IopDetectResourceConflict(ResourceList, FALSE, NULL); 435 if (Status == STATUS_CONFLICTING_ADDRESSES) 436 { 437 /* Oh noes */ 438 *ConflictDetected = TRUE; 439 } 440 else if (NT_SUCCESS(Status)) 441 { 442 /* Looks like we're good to go */ 443 444 /* TODO: Claim the resources in the ResourceMap */ 445 } 446 447 return Status; 448 } 449 450 VOID 451 NTAPI 452 IopSetEvent(IN PVOID Context) 453 { 454 PKEVENT Event = Context; 455 456 /* Set the event */ 457 KeSetEvent(Event, IO_NO_INCREMENT, FALSE); 458 } 459 460 /* 461 * @implemented 462 */ 463 NTSTATUS 464 NTAPI 465 IoReportTargetDeviceChange(IN PDEVICE_OBJECT PhysicalDeviceObject, 466 IN PVOID NotificationStructure) 467 { 468 KEVENT NotifyEvent; 469 NTSTATUS Status, NotifyStatus; 470 PTARGET_DEVICE_CUSTOM_NOTIFICATION notifyStruct = (PTARGET_DEVICE_CUSTOM_NOTIFICATION)NotificationStructure; 471 472 ASSERT(notifyStruct); 473 474 /* Check for valid PDO */ 475 if (!IopIsValidPhysicalDeviceObject(PhysicalDeviceObject)) 476 { 477 KeBugCheckEx(PNP_DETECTED_FATAL_ERROR, 0x2, (ULONG_PTR)PhysicalDeviceObject, 0, 0); 478 } 479 480 /* FileObject must be null. PnP will fill in it */ 481 ASSERT(notifyStruct->FileObject == NULL); 482 483 /* Do not handle system PnP events */ 484 if ((RtlCompareMemory(&(notifyStruct->Event), &(GUID_TARGET_DEVICE_QUERY_REMOVE), sizeof(GUID)) != sizeof(GUID)) || 485 (RtlCompareMemory(&(notifyStruct->Event), &(GUID_TARGET_DEVICE_REMOVE_CANCELLED), sizeof(GUID)) != sizeof(GUID)) || 486 (RtlCompareMemory(&(notifyStruct->Event), &(GUID_TARGET_DEVICE_REMOVE_COMPLETE), sizeof(GUID)) != sizeof(GUID))) 487 { 488 return STATUS_INVALID_DEVICE_REQUEST; 489 } 490 491 if (notifyStruct->Version != 1) 492 { 493 return STATUS_INVALID_DEVICE_REQUEST; 494 } 495 496 /* Initialize even that will let us know when PnP will have finished notify */ 497 KeInitializeEvent(&NotifyEvent, NotificationEvent, FALSE); 498 499 Status = PpSetCustomTargetEvent(PhysicalDeviceObject, &NotifyEvent, &NotifyStatus, NULL, NULL, notifyStruct); 500 /* If no error, wait for the notify to end and return the status of the notify and not of the event */ 501 if (NT_SUCCESS(Status)) 502 { 503 KeWaitForSingleObject(&NotifyEvent, Executive, KernelMode, FALSE, NULL); 504 Status = NotifyStatus; 505 } 506 507 return Status; 508 } 509 510 /* 511 * @implemented 512 */ 513 NTSTATUS 514 NTAPI 515 IoReportTargetDeviceChangeAsynchronous(IN PDEVICE_OBJECT PhysicalDeviceObject, 516 IN PVOID NotificationStructure, 517 IN PDEVICE_CHANGE_COMPLETE_CALLBACK Callback OPTIONAL, 518 IN PVOID Context OPTIONAL) 519 { 520 PINTERNAL_WORK_QUEUE_ITEM Item = NULL; 521 PTARGET_DEVICE_CUSTOM_NOTIFICATION notifyStruct = (PTARGET_DEVICE_CUSTOM_NOTIFICATION)NotificationStructure; 522 523 ASSERT(notifyStruct); 524 525 /* Check for valid PDO */ 526 if (!IopIsValidPhysicalDeviceObject(PhysicalDeviceObject)) 527 { 528 KeBugCheckEx(PNP_DETECTED_FATAL_ERROR, 0x2, (ULONG_PTR)PhysicalDeviceObject, 0, 0); 529 } 530 531 /* FileObject must be null. PnP will fill in it */ 532 ASSERT(notifyStruct->FileObject == NULL); 533 534 /* Do not handle system PnP events */ 535 if ((RtlCompareMemory(&(notifyStruct->Event), &(GUID_TARGET_DEVICE_QUERY_REMOVE), sizeof(GUID)) != sizeof(GUID)) || 536 (RtlCompareMemory(&(notifyStruct->Event), &(GUID_TARGET_DEVICE_REMOVE_CANCELLED), sizeof(GUID)) != sizeof(GUID)) || 537 (RtlCompareMemory(&(notifyStruct->Event), &(GUID_TARGET_DEVICE_REMOVE_COMPLETE), sizeof(GUID)) != sizeof(GUID))) 538 { 539 return STATUS_INVALID_DEVICE_REQUEST; 540 } 541 542 if (notifyStruct->Version != 1) 543 { 544 return STATUS_INVALID_DEVICE_REQUEST; 545 } 546 547 /* We need to store all the data given by the caller with the WorkItem, so use our own struct */ 548 Item = ExAllocatePoolWithTag(NonPagedPool, sizeof(INTERNAL_WORK_QUEUE_ITEM), ' pP'); 549 if (!Item) return STATUS_INSUFFICIENT_RESOURCES; 550 551 /* Initialize all stuff */ 552 ObReferenceObject(PhysicalDeviceObject); 553 Item->NotificationStructure = notifyStruct; 554 Item->PhysicalDeviceObject = PhysicalDeviceObject; 555 Item->Callback = Callback; 556 Item->Context = Context; 557 ExInitializeWorkItem(&(Item->WorkItem), IopReportTargetDeviceChangeAsyncWorker, Item); 558 559 /* Finally, queue the item, our work here is done */ 560 ExQueueWorkItem(&(Item->WorkItem), DelayedWorkQueue); 561 562 return STATUS_PENDING; 563 } 564