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