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 Status = IopCreateDeviceKeyPath(&DeviceNode->InstancePath, REG_OPTION_NON_VOLATILE, &InstanceKey); 288 if (!NT_SUCCESS(Status)) 289 return Status; 290 291 /* Save the driver name */ 292 RtlInitUnicodeString(&ValueName, L"Service"); 293 Status = ZwSetValueKey(InstanceKey, &ValueName, 0, REG_SZ, ServiceLongName.Buffer, ServiceLongName.Length + sizeof(UNICODE_NULL)); 294 if (!NT_SUCCESS(Status)) 295 { 296 DPRINT("Failed to write the Service name value: 0x%x\n", Status); 297 } 298 299 /* Report as non-legacy driver */ 300 RtlInitUnicodeString(&ValueName, L"Legacy"); 301 LegacyValue = 0; 302 Status = ZwSetValueKey(InstanceKey, &ValueName, 0, REG_DWORD, &LegacyValue, sizeof(LegacyValue)); 303 if (!NT_SUCCESS(Status)) 304 { 305 DPRINT("Failed to write the Legacy value: 0x%x\n", Status); 306 } 307 308 /* Add DETECTEDInterfaceType\DriverName */ 309 IdLength = 0; 310 IdLength += swprintf(&HardwareId[IdLength], 311 L"DETECTED%ls\\%wZ", 312 IfString, 313 &ServiceName); 314 IdLength++; 315 316 /* Add DETECTED\DriverName */ 317 IdLength += swprintf(&HardwareId[IdLength], 318 L"DETECTED\\%wZ", 319 &ServiceName); 320 IdLength++; 321 322 /* Terminate the string with another null */ 323 HardwareId[IdLength++] = UNICODE_NULL; 324 325 /* Store the value for CompatibleIDs */ 326 RtlInitUnicodeString(&ValueName, L"CompatibleIDs"); 327 Status = ZwSetValueKey(InstanceKey, &ValueName, 0, REG_MULTI_SZ, HardwareId, IdLength * sizeof(WCHAR)); 328 if (!NT_SUCCESS(Status)) 329 { 330 DPRINT("Failed to write the compatible IDs: 0x%x\n", Status); 331 ZwClose(InstanceKey); 332 return Status; 333 } 334 335 /* Add a hardware ID if the driver didn't report one */ 336 RtlInitUnicodeString(&ValueName, L"HardwareID"); 337 if (ZwQueryValueKey(InstanceKey, &ValueName, KeyValueBasicInformation, NULL, 0, &RequiredLength) == STATUS_OBJECT_NAME_NOT_FOUND) 338 { 339 /* Just use our most specific compatible ID */ 340 IdLength = 0; 341 IdLength += swprintf(&HardwareId[IdLength], 342 L"DETECTED%ls\\%wZ", 343 IfString, 344 &ServiceName); 345 IdLength++; 346 347 HardwareId[IdLength++] = UNICODE_NULL; 348 349 /* Write the value to the registry */ 350 Status = ZwSetValueKey(InstanceKey, &ValueName, 0, REG_MULTI_SZ, HardwareId, IdLength * sizeof(WCHAR)); 351 if (!NT_SUCCESS(Status)) 352 { 353 DPRINT("Failed to write the hardware ID: 0x%x\n", Status); 354 ZwClose(InstanceKey); 355 return Status; 356 } 357 } 358 359 /* Assign the resources to the device node */ 360 DeviceNode->BootResources = ResourceList; 361 DeviceNode->ResourceRequirements = ResourceRequirements; 362 363 /* Set appropriate flags */ 364 if (DeviceNode->BootResources) 365 IopDeviceNodeSetFlag(DeviceNode, DNF_HAS_BOOT_CONFIG); 366 367 if (!DeviceNode->ResourceRequirements && !DeviceNode->BootResources) 368 IopDeviceNodeSetFlag(DeviceNode, DNF_NO_RESOURCE_REQUIRED); 369 370 /* Write the resource information to the registry */ 371 IopSetDeviceInstanceData(InstanceKey, DeviceNode); 372 373 /* If the caller didn't get the resources assigned for us, do it now */ 374 if (!ResourceAssigned) 375 { 376 Status = IopAssignDeviceResources(DeviceNode); 377 378 /* See if we failed */ 379 if (!NT_SUCCESS(Status)) 380 { 381 DPRINT("Assigning resources failed: 0x%x\n", Status); 382 ZwClose(InstanceKey); 383 return Status; 384 } 385 } 386 387 /* Close the instance key handle */ 388 ZwClose(InstanceKey); 389 390 /* Register the given DO with PnP root if required */ 391 if (DeviceObject && *DeviceObject) 392 PnpRootRegisterDevice(*DeviceObject); 393 394 /* Report the device's enumeration to umpnpmgr */ 395 IopQueueTargetDeviceEvent(&GUID_DEVICE_ENUMERATED, 396 &DeviceNode->InstancePath); 397 398 /* Report the device's arrival to umpnpmgr */ 399 IopQueueTargetDeviceEvent(&GUID_DEVICE_ARRIVAL, 400 &DeviceNode->InstancePath); 401 402 DPRINT("Reported device: %S (%wZ)\n", HardwareId, &DeviceNode->InstancePath); 403 404 /* Return the PDO */ 405 if (DeviceObject) *DeviceObject = Pdo; 406 407 return STATUS_SUCCESS; 408 } 409 410 /* 411 * @halfplemented 412 */ 413 NTSTATUS 414 NTAPI 415 IoReportResourceForDetection(IN PDRIVER_OBJECT DriverObject, 416 IN PCM_RESOURCE_LIST DriverList OPTIONAL, 417 IN ULONG DriverListSize OPTIONAL, 418 IN PDEVICE_OBJECT DeviceObject OPTIONAL, 419 IN PCM_RESOURCE_LIST DeviceList OPTIONAL, 420 IN ULONG DeviceListSize OPTIONAL, 421 OUT PBOOLEAN ConflictDetected) 422 { 423 PCM_RESOURCE_LIST ResourceList; 424 NTSTATUS Status; 425 426 *ConflictDetected = FALSE; 427 428 if (!DriverList && !DeviceList) 429 return STATUS_INVALID_PARAMETER; 430 431 /* Find the real list */ 432 if (!DriverList) 433 ResourceList = DeviceList; 434 else 435 ResourceList = DriverList; 436 437 /* Look for a resource conflict */ 438 Status = IopDetectResourceConflict(ResourceList, FALSE, NULL); 439 if (Status == STATUS_CONFLICTING_ADDRESSES) 440 { 441 /* Oh noes */ 442 *ConflictDetected = TRUE; 443 } 444 else if (NT_SUCCESS(Status)) 445 { 446 /* Looks like we're good to go */ 447 448 /* TODO: Claim the resources in the ResourceMap */ 449 } 450 451 return Status; 452 } 453 454 VOID 455 NTAPI 456 IopSetEvent(IN PVOID Context) 457 { 458 PKEVENT Event = Context; 459 460 /* Set the event */ 461 KeSetEvent(Event, IO_NO_INCREMENT, FALSE); 462 } 463 464 /* 465 * @implemented 466 */ 467 NTSTATUS 468 NTAPI 469 IoReportTargetDeviceChange(IN PDEVICE_OBJECT PhysicalDeviceObject, 470 IN PVOID NotificationStructure) 471 { 472 KEVENT NotifyEvent; 473 NTSTATUS Status, NotifyStatus; 474 PTARGET_DEVICE_CUSTOM_NOTIFICATION notifyStruct = (PTARGET_DEVICE_CUSTOM_NOTIFICATION)NotificationStructure; 475 476 ASSERT(notifyStruct); 477 478 /* Check for valid PDO */ 479 if (!IopIsValidPhysicalDeviceObject(PhysicalDeviceObject)) 480 { 481 KeBugCheckEx(PNP_DETECTED_FATAL_ERROR, 0x2, (ULONG_PTR)PhysicalDeviceObject, 0, 0); 482 } 483 484 /* FileObject must be null. PnP will fill in it */ 485 ASSERT(notifyStruct->FileObject == NULL); 486 487 /* Do not handle system PnP events */ 488 if ((RtlCompareMemory(&(notifyStruct->Event), &(GUID_TARGET_DEVICE_QUERY_REMOVE), sizeof(GUID)) != sizeof(GUID)) || 489 (RtlCompareMemory(&(notifyStruct->Event), &(GUID_TARGET_DEVICE_REMOVE_CANCELLED), sizeof(GUID)) != sizeof(GUID)) || 490 (RtlCompareMemory(&(notifyStruct->Event), &(GUID_TARGET_DEVICE_REMOVE_COMPLETE), sizeof(GUID)) != sizeof(GUID))) 491 { 492 return STATUS_INVALID_DEVICE_REQUEST; 493 } 494 495 if (notifyStruct->Version != 1) 496 { 497 return STATUS_INVALID_DEVICE_REQUEST; 498 } 499 500 /* Initialize even that will let us know when PnP will have finished notify */ 501 KeInitializeEvent(&NotifyEvent, NotificationEvent, FALSE); 502 503 Status = PpSetCustomTargetEvent(PhysicalDeviceObject, &NotifyEvent, &NotifyStatus, NULL, NULL, notifyStruct); 504 /* If no error, wait for the notify to end and return the status of the notify and not of the event */ 505 if (NT_SUCCESS(Status)) 506 { 507 KeWaitForSingleObject(&NotifyEvent, Executive, KernelMode, FALSE, NULL); 508 Status = NotifyStatus; 509 } 510 511 return Status; 512 } 513 514 /* 515 * @implemented 516 */ 517 NTSTATUS 518 NTAPI 519 IoReportTargetDeviceChangeAsynchronous(IN PDEVICE_OBJECT PhysicalDeviceObject, 520 IN PVOID NotificationStructure, 521 IN PDEVICE_CHANGE_COMPLETE_CALLBACK Callback OPTIONAL, 522 IN PVOID Context OPTIONAL) 523 { 524 PINTERNAL_WORK_QUEUE_ITEM Item = NULL; 525 PTARGET_DEVICE_CUSTOM_NOTIFICATION notifyStruct = (PTARGET_DEVICE_CUSTOM_NOTIFICATION)NotificationStructure; 526 527 ASSERT(notifyStruct); 528 529 /* Check for valid PDO */ 530 if (!IopIsValidPhysicalDeviceObject(PhysicalDeviceObject)) 531 { 532 KeBugCheckEx(PNP_DETECTED_FATAL_ERROR, 0x2, (ULONG_PTR)PhysicalDeviceObject, 0, 0); 533 } 534 535 /* FileObject must be null. PnP will fill in it */ 536 ASSERT(notifyStruct->FileObject == NULL); 537 538 /* Do not handle system PnP events */ 539 if ((RtlCompareMemory(&(notifyStruct->Event), &(GUID_TARGET_DEVICE_QUERY_REMOVE), sizeof(GUID)) != sizeof(GUID)) || 540 (RtlCompareMemory(&(notifyStruct->Event), &(GUID_TARGET_DEVICE_REMOVE_CANCELLED), sizeof(GUID)) != sizeof(GUID)) || 541 (RtlCompareMemory(&(notifyStruct->Event), &(GUID_TARGET_DEVICE_REMOVE_COMPLETE), sizeof(GUID)) != sizeof(GUID))) 542 { 543 return STATUS_INVALID_DEVICE_REQUEST; 544 } 545 546 if (notifyStruct->Version != 1) 547 { 548 return STATUS_INVALID_DEVICE_REQUEST; 549 } 550 551 /* We need to store all the data given by the caller with the WorkItem, so use our own struct */ 552 Item = ExAllocatePoolWithTag(NonPagedPool, sizeof(INTERNAL_WORK_QUEUE_ITEM), ' pP'); 553 if (!Item) return STATUS_INSUFFICIENT_RESOURCES; 554 555 /* Initialize all stuff */ 556 ObReferenceObject(PhysicalDeviceObject); 557 Item->NotificationStructure = notifyStruct; 558 Item->PhysicalDeviceObject = PhysicalDeviceObject; 559 Item->Callback = Callback; 560 Item->Context = Context; 561 ExInitializeWorkItem(&(Item->WorkItem), IopReportTargetDeviceChangeAsyncWorker, Item); 562 563 /* Finally, queue the item, our work here is done */ 564 ExQueueWorkItem(&(Item->WorkItem), DelayedWorkQueue); 565 566 return STATUS_PENDING; 567 } 568