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