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 PDEVICE_NODE DeviceNode; 159 PDEVICE_OBJECT Pdo; 160 NTSTATUS Status; 161 HANDLE InstanceKey; 162 UNICODE_STRING ValueName, ServiceLongName, ServiceName; 163 WCHAR HardwareId[256]; 164 PWCHAR IfString; 165 ULONG IdLength; 166 ULONG LegacyValue; 167 168 DPRINT("IoReportDetectedDevice (DeviceObject %p, *DeviceObject %p)\n", 169 DeviceObject, DeviceObject ? *DeviceObject : NULL); 170 171 ServiceLongName = DriverObject->DriverExtension->ServiceKeyName; 172 ServiceName = ServiceLongName; 173 174 /* If the interface type is unknown, treat it as internal */ 175 if (LegacyBusType == InterfaceTypeUndefined) 176 LegacyBusType = Internal; 177 178 /* Get the string equivalent of the interface type */ 179 IfString = IopGetInterfaceTypeString(LegacyBusType); 180 181 /* If NULL is returned then it's a bad type */ 182 if (!IfString) 183 return STATUS_INVALID_PARAMETER; 184 185 /* 186 * Drivers that have been created via a direct IoCreateDriver() call 187 * have their ServiceKeyName set to \Driver\DriverName. We need to 188 * strip everything up to the last path separator and keep what remains. 189 */ 190 if (DriverObject->Flags & DRVO_BUILTIN_DRIVER) 191 { 192 /* 193 * Find the last path separator. 194 * NOTE: Since ServiceName is not necessarily NULL-terminated, 195 * we cannot use wcsrchr(). 196 */ 197 if (ServiceName.Buffer && ServiceName.Length >= sizeof(WCHAR)) 198 { 199 ValueName.Length = 1; 200 ValueName.Buffer = ServiceName.Buffer + (ServiceName.Length / sizeof(WCHAR)) - 1; 201 202 while ((ValueName.Buffer > ServiceName.Buffer) && (*ValueName.Buffer != L'\\')) 203 { 204 --ValueName.Buffer; 205 ++ValueName.Length; 206 } 207 if (*ValueName.Buffer == L'\\') 208 { 209 ++ValueName.Buffer; 210 --ValueName.Length; 211 } 212 ValueName.Length *= sizeof(WCHAR); 213 214 /* Shorten the string */ 215 ServiceName.MaximumLength -= (ServiceName.Length - ValueName.Length); 216 ServiceName.Length = ValueName.Length; 217 ServiceName.Buffer = ValueName.Buffer; 218 } 219 } 220 221 /* We use the caller's PDO if they supplied one */ 222 UNICODE_STRING instancePath; 223 if (DeviceObject && *DeviceObject) 224 { 225 Pdo = *DeviceObject; 226 } 227 else 228 { 229 /* Create the PDO */ 230 Status = PnpRootCreateDevice(&ServiceName, NULL, &Pdo, &instancePath); 231 if (!NT_SUCCESS(Status)) 232 { 233 DPRINT("PnpRootCreateDevice() failed (Status 0x%08lx)\n", Status); 234 return Status; 235 } 236 } 237 238 /* Create the device node for the new PDO */ 239 DeviceNode = PipAllocateDeviceNode(Pdo); 240 if (!DeviceNode) 241 { 242 DPRINT("PipAllocateDeviceNode() failed\n"); 243 return STATUS_INSUFFICIENT_RESOURCES; 244 } 245 246 Status = RtlDuplicateUnicodeString(0, &instancePath, &DeviceNode->InstancePath); 247 248 /* Open a handle to the instance path key */ 249 Status = IopCreateDeviceKeyPath(&DeviceNode->InstancePath, REG_OPTION_NON_VOLATILE, &InstanceKey); 250 if (!NT_SUCCESS(Status)) 251 return Status; 252 253 /* Save the driver name */ 254 RtlInitUnicodeString(&ValueName, L"Service"); 255 Status = ZwSetValueKey(InstanceKey, &ValueName, 0, REG_SZ, ServiceLongName.Buffer, ServiceLongName.Length + sizeof(UNICODE_NULL)); 256 if (!NT_SUCCESS(Status)) 257 { 258 DPRINT("Failed to write the Service name value: 0x%x\n", Status); 259 } 260 261 /* Report as non-legacy driver */ 262 RtlInitUnicodeString(&ValueName, L"Legacy"); 263 LegacyValue = 0; 264 Status = ZwSetValueKey(InstanceKey, &ValueName, 0, REG_DWORD, &LegacyValue, sizeof(LegacyValue)); 265 if (!NT_SUCCESS(Status)) 266 { 267 DPRINT("Failed to write the Legacy value: 0x%x\n", Status); 268 } 269 270 /* Add DETECTEDInterfaceType\DriverName */ 271 IdLength = 0; 272 IdLength += swprintf(&HardwareId[IdLength], 273 L"DETECTED%ls\\%wZ", 274 IfString, 275 &ServiceName); 276 IdLength++; 277 278 /* Add DETECTED\DriverName */ 279 IdLength += swprintf(&HardwareId[IdLength], 280 L"DETECTED\\%wZ", 281 &ServiceName); 282 IdLength++; 283 284 /* Terminate the string with another null */ 285 HardwareId[IdLength++] = UNICODE_NULL; 286 287 /* Store the value for CompatibleIDs */ 288 RtlInitUnicodeString(&ValueName, L"CompatibleIDs"); 289 Status = ZwSetValueKey(InstanceKey, &ValueName, 0, REG_MULTI_SZ, HardwareId, IdLength * sizeof(WCHAR)); 290 if (!NT_SUCCESS(Status)) 291 { 292 DPRINT("Failed to write the compatible IDs: 0x%x\n", Status); 293 ZwClose(InstanceKey); 294 return Status; 295 } 296 297 /* Assign the resources to the device node */ 298 DeviceNode->BootResources = ResourceList; 299 DeviceNode->ResourceRequirements = ResourceRequirements; 300 301 /* Set appropriate flags */ 302 if (DeviceNode->BootResources) 303 IopDeviceNodeSetFlag(DeviceNode, DNF_HAS_BOOT_CONFIG); 304 305 if (!DeviceNode->ResourceRequirements && !DeviceNode->BootResources) 306 IopDeviceNodeSetFlag(DeviceNode, DNF_NO_RESOURCE_REQUIRED); 307 308 /* Write the resource information to the registry */ 309 IopSetDeviceInstanceData(InstanceKey, DeviceNode); 310 311 /* If the caller didn't get the resources assigned for us, do it now */ 312 if (!ResourceAssigned) 313 { 314 Status = IopAssignDeviceResources(DeviceNode); 315 316 /* See if we failed */ 317 if (!NT_SUCCESS(Status)) 318 { 319 DPRINT("Assigning resources failed: 0x%x\n", Status); 320 ZwClose(InstanceKey); 321 return Status; 322 } 323 } 324 325 /* Close the instance key handle */ 326 ZwClose(InstanceKey); 327 328 /* Register the given DO with PnP root if required */ 329 if (DeviceObject && *DeviceObject) 330 PnpRootRegisterDevice(*DeviceObject); 331 332 PiInsertDevNode(DeviceNode, IopRootDeviceNode); 333 DeviceNode->Flags |= DNF_MADEUP | DNF_ENUMERATED; 334 335 // we still need to query IDs, send events and reenumerate this node 336 PiSetDevNodeState(DeviceNode, DeviceNodeStartPostWork); 337 338 DPRINT("Reported device: %S (%wZ)\n", HardwareId, &DeviceNode->InstancePath); 339 340 PiQueueDeviceAction(Pdo, PiActionEnumDeviceTree, NULL, NULL); 341 342 /* Return the PDO */ 343 if (DeviceObject) *DeviceObject = Pdo; 344 345 return STATUS_SUCCESS; 346 } 347 348 /* 349 * @halfplemented 350 */ 351 NTSTATUS 352 NTAPI 353 IoReportResourceForDetection(IN PDRIVER_OBJECT DriverObject, 354 IN PCM_RESOURCE_LIST DriverList OPTIONAL, 355 IN ULONG DriverListSize OPTIONAL, 356 IN PDEVICE_OBJECT DeviceObject OPTIONAL, 357 IN PCM_RESOURCE_LIST DeviceList OPTIONAL, 358 IN ULONG DeviceListSize OPTIONAL, 359 OUT PBOOLEAN ConflictDetected) 360 { 361 PCM_RESOURCE_LIST ResourceList; 362 NTSTATUS Status; 363 364 *ConflictDetected = FALSE; 365 366 if (!DriverList && !DeviceList) 367 return STATUS_INVALID_PARAMETER; 368 369 /* Find the real list */ 370 if (!DriverList) 371 ResourceList = DeviceList; 372 else 373 ResourceList = DriverList; 374 375 /* Look for a resource conflict */ 376 Status = IopDetectResourceConflict(ResourceList, FALSE, NULL); 377 if (Status == STATUS_CONFLICTING_ADDRESSES) 378 { 379 /* Oh noes */ 380 *ConflictDetected = TRUE; 381 } 382 else if (NT_SUCCESS(Status)) 383 { 384 /* Looks like we're good to go */ 385 386 /* TODO: Claim the resources in the ResourceMap */ 387 } 388 389 return Status; 390 } 391 392 VOID 393 NTAPI 394 IopSetEvent(IN PVOID Context) 395 { 396 PKEVENT Event = Context; 397 398 /* Set the event */ 399 KeSetEvent(Event, IO_NO_INCREMENT, FALSE); 400 } 401 402 /* 403 * @implemented 404 */ 405 NTSTATUS 406 NTAPI 407 IoReportTargetDeviceChange(IN PDEVICE_OBJECT PhysicalDeviceObject, 408 IN PVOID NotificationStructure) 409 { 410 KEVENT NotifyEvent; 411 NTSTATUS Status, NotifyStatus; 412 PTARGET_DEVICE_CUSTOM_NOTIFICATION notifyStruct = (PTARGET_DEVICE_CUSTOM_NOTIFICATION)NotificationStructure; 413 414 ASSERT(notifyStruct); 415 416 /* Check for valid PDO */ 417 if (!IopIsValidPhysicalDeviceObject(PhysicalDeviceObject)) 418 { 419 KeBugCheckEx(PNP_DETECTED_FATAL_ERROR, 0x2, (ULONG_PTR)PhysicalDeviceObject, 0, 0); 420 } 421 422 /* FileObject must be null. PnP will fill in it */ 423 ASSERT(notifyStruct->FileObject == NULL); 424 425 /* Do not handle system PnP events */ 426 if ((RtlCompareMemory(&(notifyStruct->Event), &(GUID_TARGET_DEVICE_QUERY_REMOVE), sizeof(GUID)) != sizeof(GUID)) || 427 (RtlCompareMemory(&(notifyStruct->Event), &(GUID_TARGET_DEVICE_REMOVE_CANCELLED), sizeof(GUID)) != sizeof(GUID)) || 428 (RtlCompareMemory(&(notifyStruct->Event), &(GUID_TARGET_DEVICE_REMOVE_COMPLETE), sizeof(GUID)) != sizeof(GUID))) 429 { 430 return STATUS_INVALID_DEVICE_REQUEST; 431 } 432 433 if (notifyStruct->Version != 1) 434 { 435 return STATUS_INVALID_DEVICE_REQUEST; 436 } 437 438 /* Initialize even that will let us know when PnP will have finished notify */ 439 KeInitializeEvent(&NotifyEvent, NotificationEvent, FALSE); 440 441 Status = PpSetCustomTargetEvent(PhysicalDeviceObject, &NotifyEvent, &NotifyStatus, NULL, NULL, notifyStruct); 442 /* If no error, wait for the notify to end and return the status of the notify and not of the event */ 443 if (NT_SUCCESS(Status)) 444 { 445 KeWaitForSingleObject(&NotifyEvent, Executive, KernelMode, FALSE, NULL); 446 Status = NotifyStatus; 447 } 448 449 return Status; 450 } 451 452 /* 453 * @implemented 454 */ 455 NTSTATUS 456 NTAPI 457 IoReportTargetDeviceChangeAsynchronous(IN PDEVICE_OBJECT PhysicalDeviceObject, 458 IN PVOID NotificationStructure, 459 IN PDEVICE_CHANGE_COMPLETE_CALLBACK Callback OPTIONAL, 460 IN PVOID Context OPTIONAL) 461 { 462 PINTERNAL_WORK_QUEUE_ITEM Item = NULL; 463 PTARGET_DEVICE_CUSTOM_NOTIFICATION notifyStruct = (PTARGET_DEVICE_CUSTOM_NOTIFICATION)NotificationStructure; 464 465 ASSERT(notifyStruct); 466 467 /* Check for valid PDO */ 468 if (!IopIsValidPhysicalDeviceObject(PhysicalDeviceObject)) 469 { 470 KeBugCheckEx(PNP_DETECTED_FATAL_ERROR, 0x2, (ULONG_PTR)PhysicalDeviceObject, 0, 0); 471 } 472 473 /* FileObject must be null. PnP will fill in it */ 474 ASSERT(notifyStruct->FileObject == NULL); 475 476 /* Do not handle system PnP events */ 477 if ((RtlCompareMemory(&(notifyStruct->Event), &(GUID_TARGET_DEVICE_QUERY_REMOVE), sizeof(GUID)) != sizeof(GUID)) || 478 (RtlCompareMemory(&(notifyStruct->Event), &(GUID_TARGET_DEVICE_REMOVE_CANCELLED), sizeof(GUID)) != sizeof(GUID)) || 479 (RtlCompareMemory(&(notifyStruct->Event), &(GUID_TARGET_DEVICE_REMOVE_COMPLETE), sizeof(GUID)) != sizeof(GUID))) 480 { 481 return STATUS_INVALID_DEVICE_REQUEST; 482 } 483 484 if (notifyStruct->Version != 1) 485 { 486 return STATUS_INVALID_DEVICE_REQUEST; 487 } 488 489 /* We need to store all the data given by the caller with the WorkItem, so use our own struct */ 490 Item = ExAllocatePoolWithTag(NonPagedPool, sizeof(INTERNAL_WORK_QUEUE_ITEM), ' pP'); 491 if (!Item) return STATUS_INSUFFICIENT_RESOURCES; 492 493 /* Initialize all stuff */ 494 ObReferenceObject(PhysicalDeviceObject); 495 Item->NotificationStructure = notifyStruct; 496 Item->PhysicalDeviceObject = PhysicalDeviceObject; 497 Item->Callback = Callback; 498 Item->Context = Context; 499 ExInitializeWorkItem(&(Item->WorkItem), IopReportTargetDeviceChangeAsyncWorker, Item); 500 501 /* Finally, queue the item, our work here is done */ 502 ExQueueWorkItem(&(Item->WorkItem), DelayedWorkQueue); 503 504 return STATUS_PENDING; 505 } 506