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)
10 /* INCLUDES ******************************************************************/
16 /* TYPES *******************************************************************/
18 typedef struct _INTERNAL_WORK_QUEUE_ITEM
20 WORK_QUEUE_ITEM WorkItem
;
21 PDEVICE_OBJECT PhysicalDeviceObject
;
22 PDEVICE_CHANGE_COMPLETE_CALLBACK Callback
;
24 PTARGET_DEVICE_CUSTOM_NOTIFICATION NotificationStructure
;
25 } INTERNAL_WORK_QUEUE_ITEM
, *PINTERNAL_WORK_QUEUE_ITEM
;
29 IopCreateDeviceKeyPath(IN PCUNICODE_STRING RegistryPath
,
30 IN ULONG CreateOptions
,
34 IopSetDeviceInstanceData(HANDLE InstanceKey
,
35 PDEVICE_NODE DeviceNode
);
38 IopActionInterrogateDeviceStack(PDEVICE_NODE DeviceNode
,
42 IopDetectResourceConflict(IN PCM_RESOURCE_LIST ResourceList
);
45 PpSetCustomTargetEvent(IN PDEVICE_OBJECT DeviceObject
,
46 IN OUT PKEVENT SyncEvent OPTIONAL
,
47 IN OUT PNTSTATUS SyncStatus OPTIONAL
,
48 IN PDEVICE_CHANGE_COMPLETE_CALLBACK Callback OPTIONAL
,
49 IN PVOID Context OPTIONAL
,
50 IN PTARGET_DEVICE_CUSTOM_NOTIFICATION NotificationStructure
);
52 /* PRIVATE FUNCTIONS *********************************************************/
55 IopGetInterfaceTypeString(INTERFACE_TYPE IfType
)
69 return L
"MicroChannel";
72 return L
"TurboChannel";
95 case ProcessorInternal
:
96 return L
"ProcessorInternal";
108 DPRINT1("Invalid bus type: %d\n", IfType
);
114 IopReportTargetDeviceChangeAsyncWorker(PVOID Context
)
116 PINTERNAL_WORK_QUEUE_ITEM Item
;
118 Item
= (PINTERNAL_WORK_QUEUE_ITEM
)Context
;
119 PpSetCustomTargetEvent(Item
->PhysicalDeviceObject
, NULL
, NULL
, Item
->Callback
, Item
->Context
, Item
->NotificationStructure
);
120 ObDereferenceObject(Item
->PhysicalDeviceObject
);
121 ExFreePoolWithTag(Context
, ' pP');
125 PpSetCustomTargetEvent(IN PDEVICE_OBJECT DeviceObject
,
126 IN OUT PKEVENT SyncEvent OPTIONAL
,
127 IN OUT PNTSTATUS SyncStatus OPTIONAL
,
128 IN PDEVICE_CHANGE_COMPLETE_CALLBACK Callback OPTIONAL
,
129 IN PVOID Context OPTIONAL
,
130 IN PTARGET_DEVICE_CUSTOM_NOTIFICATION NotificationStructure
)
132 ASSERT(NotificationStructure
!= NULL
);
133 ASSERT(DeviceObject
!= NULL
);
135 /* That call is totally wrong but notifications handler must be fixed first */
136 IopNotifyPlugPlayNotification(DeviceObject
,
137 EventCategoryTargetDeviceChange
,
138 &GUID_PNP_CUSTOM_NOTIFICATION
,
139 NotificationStructure
,
141 return STATUS_SUCCESS
;
144 /* PUBLIC FUNCTIONS **********************************************************/
151 IoReportDetectedDevice(IN PDRIVER_OBJECT DriverObject
,
152 IN INTERFACE_TYPE LegacyBusType
,
155 IN PCM_RESOURCE_LIST ResourceList
,
156 IN PIO_RESOURCE_REQUIREMENTS_LIST ResourceRequirements OPTIONAL
,
157 IN BOOLEAN ResourceAssigned
,
158 IN OUT PDEVICE_OBJECT
*DeviceObject OPTIONAL
)
160 PDEVICE_NODE DeviceNode
;
164 ULONG RequiredLength
;
165 UNICODE_STRING ValueName
, ServiceName
;
166 WCHAR HardwareId
[256];
170 DPRINT("IoReportDetectedDevice (DeviceObject %p, *DeviceObject %p)\n",
171 DeviceObject
, DeviceObject
? *DeviceObject
: NULL
);
173 /* Create the service name (eg. ACPI_HAL) */
174 ServiceName
.Buffer
= DriverObject
->DriverName
.Buffer
+
175 sizeof(DRIVER_ROOT_NAME
) / sizeof(WCHAR
) - 1;
176 ServiceName
.Length
= DriverObject
->DriverName
.Length
-
177 sizeof(DRIVER_ROOT_NAME
) + sizeof(WCHAR
);
178 ServiceName
.MaximumLength
= ServiceName
.Length
;
180 /* If the interface type is unknown, treat it as internal */
181 if (LegacyBusType
== InterfaceTypeUndefined
)
182 LegacyBusType
= Internal
;
184 /* Get the string equivalent of the interface type */
185 IfString
= IopGetInterfaceTypeString(LegacyBusType
);
187 /* If NULL is returned then it's a bad type */
189 return STATUS_INVALID_PARAMETER
;
191 /* We use the caller's PDO if they supplied one */
192 if (DeviceObject
&& *DeviceObject
)
195 DeviceNode
= IopGetDeviceNode(*DeviceObject
);
200 Status
= PnpRootCreateDevice(&ServiceName
,
203 if (!NT_SUCCESS(Status
))
205 DPRINT("PnpRootCreateDevice() failed (Status 0x%08lx)\n", Status
);
209 /* Create the device node for the new PDO */
210 Status
= IopCreateDeviceNode(IopRootDeviceNode
,
215 if (!NT_SUCCESS(Status
))
217 DPRINT("IopCreateDeviceNode() failed (Status 0x%08lx)\n", Status
);
222 /* We don't call AddDevice for devices reported this way */
223 IopDeviceNodeSetFlag(DeviceNode
, DNF_ADDED
);
225 /* We don't send IRP_MN_START_DEVICE */
226 IopDeviceNodeSetFlag(DeviceNode
, DNF_STARTED
);
228 /* We need to get device IDs */
230 IopDeviceNodeSetFlag(DeviceNode
, DNF_NEED_QUERY_IDS
);
233 /* This is a legacy driver for this device */
234 IopDeviceNodeSetFlag(DeviceNode
, DNF_LEGACY_DRIVER
);
236 /* Perform a manual configuration of our device */
237 IopActionInterrogateDeviceStack(DeviceNode
, DeviceNode
->Parent
);
238 IopActionConfigureChildServices(DeviceNode
, DeviceNode
->Parent
);
240 /* Open a handle to the instance path key */
241 Status
= IopCreateDeviceKeyPath(&DeviceNode
->InstancePath
, 0, &InstanceKey
);
242 if (!NT_SUCCESS(Status
))
245 /* Add DETECTEDInterfaceType\DriverName */
247 IdLength
+= swprintf(&HardwareId
[IdLength
],
251 HardwareId
[IdLength
++] = UNICODE_NULL
;
253 /* Add DETECTED\DriverName */
254 IdLength
+= swprintf(&HardwareId
[IdLength
],
257 HardwareId
[IdLength
++] = UNICODE_NULL
;
259 /* Terminate the string with another null */
260 HardwareId
[IdLength
] = UNICODE_NULL
;
262 /* Store the value for CompatibleIDs */
263 RtlInitUnicodeString(&ValueName
, L
"CompatibleIDs");
264 Status
= ZwSetValueKey(InstanceKey
, &ValueName
, 0, REG_MULTI_SZ
, HardwareId
, IdLength
* sizeof(WCHAR
));
265 if (!NT_SUCCESS(Status
))
267 DPRINT("Failed to write the compatible IDs: 0x%x\n", Status
);
268 ZwClose(InstanceKey
);
272 /* Add a hardware ID if the driver didn't report one */
273 RtlInitUnicodeString(&ValueName
, L
"HardwareID");
274 if (ZwQueryValueKey(InstanceKey
, &ValueName
, KeyValueBasicInformation
, NULL
, 0, &RequiredLength
) == STATUS_OBJECT_NAME_NOT_FOUND
)
276 /* Just use our most specific compatible ID */
278 IdLength
+= swprintf(&HardwareId
[IdLength
],
282 HardwareId
[++IdLength
] = UNICODE_NULL
;
284 /* Write the value to the registry */
285 Status
= ZwSetValueKey(InstanceKey
, &ValueName
, 0, REG_SZ
, HardwareId
, IdLength
* sizeof(WCHAR
));
286 if (!NT_SUCCESS(Status
))
288 DPRINT("Failed to write the hardware ID: 0x%x\n", Status
);
289 ZwClose(InstanceKey
);
294 /* Assign the resources to the device node */
295 DeviceNode
->BootResources
= ResourceList
;
296 DeviceNode
->ResourceRequirements
= ResourceRequirements
;
298 /* Set appropriate flags */
299 if (DeviceNode
->BootResources
)
300 IopDeviceNodeSetFlag(DeviceNode
, DNF_HAS_BOOT_CONFIG
);
302 if (!DeviceNode
->ResourceRequirements
&& !DeviceNode
->BootResources
)
303 IopDeviceNodeSetFlag(DeviceNode
, DNF_NO_RESOURCE_REQUIRED
);
305 /* Write the resource information to the registry */
306 IopSetDeviceInstanceData(InstanceKey
, DeviceNode
);
308 /* If the caller didn't get the resources assigned for us, do it now */
309 if (!ResourceAssigned
)
311 Status
= IopAssignDeviceResources(DeviceNode
);
313 /* See if we failed */
314 if (!NT_SUCCESS(Status
))
316 DPRINT("Assigning resources failed: 0x%x\n", Status
);
317 ZwClose(InstanceKey
);
322 /* Close the instance key handle */
323 ZwClose(InstanceKey
);
325 /* Report the device's enumeration to umpnpmgr */
326 IopQueueTargetDeviceEvent(&GUID_DEVICE_ENUMERATED
,
327 &DeviceNode
->InstancePath
);
329 /* Report the device's arrival to umpnpmgr */
330 IopQueueTargetDeviceEvent(&GUID_DEVICE_ARRIVAL
,
331 &DeviceNode
->InstancePath
);
333 DPRINT1("Reported device: %S (%wZ)\n", HardwareId
, &DeviceNode
->InstancePath
);
336 if (DeviceObject
) *DeviceObject
= Pdo
;
338 return STATUS_SUCCESS
;
346 IoReportResourceForDetection(IN PDRIVER_OBJECT DriverObject
,
347 IN PCM_RESOURCE_LIST DriverList OPTIONAL
,
348 IN ULONG DriverListSize OPTIONAL
,
349 IN PDEVICE_OBJECT DeviceObject OPTIONAL
,
350 IN PCM_RESOURCE_LIST DeviceList OPTIONAL
,
351 IN ULONG DeviceListSize OPTIONAL
,
352 OUT PBOOLEAN ConflictDetected
)
354 PCM_RESOURCE_LIST ResourceList
;
357 *ConflictDetected
= FALSE
;
359 if (!DriverList
&& !DeviceList
)
360 return STATUS_INVALID_PARAMETER
;
362 /* Find the real list */
364 ResourceList
= DeviceList
;
366 ResourceList
= DriverList
;
368 /* Look for a resource conflict */
369 Status
= IopDetectResourceConflict(ResourceList
);
370 if (Status
== STATUS_CONFLICTING_ADDRESSES
)
373 *ConflictDetected
= TRUE
;
375 else if (NT_SUCCESS(Status
))
377 /* Looks like we're good to go */
379 /* TODO: Claim the resources in the ResourceMap */
387 IopSetEvent(IN PVOID Context
)
389 PKEVENT Event
= Context
;
392 KeSetEvent(Event
, IO_NO_INCREMENT
, FALSE
);
400 IoReportTargetDeviceChange(IN PDEVICE_OBJECT PhysicalDeviceObject
,
401 IN PVOID NotificationStructure
)
404 NTSTATUS Status
, NotifyStatus
;
405 PTARGET_DEVICE_CUSTOM_NOTIFICATION notifyStruct
= (PTARGET_DEVICE_CUSTOM_NOTIFICATION
)NotificationStructure
;
407 ASSERT(notifyStruct
);
409 /* Check for valid PDO */
410 if (!IopIsValidPhysicalDeviceObject(PhysicalDeviceObject
))
412 KeBugCheckEx(PNP_DETECTED_FATAL_ERROR
, 0x2, (ULONG
)PhysicalDeviceObject
, 0, 0);
415 /* FileObject must be null. PnP will fill in it */
416 ASSERT(notifyStruct
->FileObject
== NULL
);
418 /* Do not handle system PnP events */
419 if ((RtlCompareMemory(&(notifyStruct
->Event
), &(GUID_TARGET_DEVICE_QUERY_REMOVE
), sizeof(GUID
)) != sizeof(GUID
)) ||
420 (RtlCompareMemory(&(notifyStruct
->Event
), &(GUID_TARGET_DEVICE_REMOVE_CANCELLED
), sizeof(GUID
)) != sizeof(GUID
)) ||
421 (RtlCompareMemory(&(notifyStruct
->Event
), &(GUID_TARGET_DEVICE_REMOVE_COMPLETE
), sizeof(GUID
)) != sizeof(GUID
)))
423 return STATUS_INVALID_DEVICE_REQUEST
;
426 if (notifyStruct
->Version
!= 1)
428 return STATUS_INVALID_DEVICE_REQUEST
;
431 /* Initialize even that will let us know when PnP will have finished notify */
432 KeInitializeEvent(&NotifyEvent
, NotificationEvent
, FALSE
);
434 Status
= PpSetCustomTargetEvent(PhysicalDeviceObject
, &NotifyEvent
, &NotifyStatus
, NULL
, NULL
, notifyStruct
);
435 /* If no error, wait for the notify to end and return the status of the notify and not of the event */
436 if (NT_SUCCESS(Status
))
438 KeWaitForSingleObject(&NotifyEvent
, Executive
, KernelMode
, FALSE
, NULL
);
439 Status
= NotifyStatus
;
450 IoReportTargetDeviceChangeAsynchronous(IN PDEVICE_OBJECT PhysicalDeviceObject
,
451 IN PVOID NotificationStructure
,
452 IN PDEVICE_CHANGE_COMPLETE_CALLBACK Callback OPTIONAL
,
453 IN PVOID Context OPTIONAL
)
455 PINTERNAL_WORK_QUEUE_ITEM Item
= NULL
;
456 PTARGET_DEVICE_CUSTOM_NOTIFICATION notifyStruct
= (PTARGET_DEVICE_CUSTOM_NOTIFICATION
)NotificationStructure
;
458 ASSERT(notifyStruct
);
460 /* Check for valid PDO */
461 if (!IopIsValidPhysicalDeviceObject(PhysicalDeviceObject
))
463 KeBugCheckEx(PNP_DETECTED_FATAL_ERROR
, 0x2, (ULONG
)PhysicalDeviceObject
, 0, 0);
466 /* FileObject must be null. PnP will fill in it */
467 ASSERT(notifyStruct
->FileObject
== NULL
);
469 /* Do not handle system PnP events */
470 if ((RtlCompareMemory(&(notifyStruct
->Event
), &(GUID_TARGET_DEVICE_QUERY_REMOVE
), sizeof(GUID
)) != sizeof(GUID
)) ||
471 (RtlCompareMemory(&(notifyStruct
->Event
), &(GUID_TARGET_DEVICE_REMOVE_CANCELLED
), sizeof(GUID
)) != sizeof(GUID
)) ||
472 (RtlCompareMemory(&(notifyStruct
->Event
), &(GUID_TARGET_DEVICE_REMOVE_COMPLETE
), sizeof(GUID
)) != sizeof(GUID
)))
474 return STATUS_INVALID_DEVICE_REQUEST
;
477 if (notifyStruct
->Version
!= 1)
479 return STATUS_INVALID_DEVICE_REQUEST
;
482 /* We need to store all the data given by the caller with the WorkItem, so use our own struct */
483 Item
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(INTERNAL_WORK_QUEUE_ITEM
), ' pP');
484 if (!Item
) return STATUS_INSUFFICIENT_RESOURCES
;
486 /* Initialize all stuff */
487 ObReferenceObject(PhysicalDeviceObject
);
488 Item
->NotificationStructure
= notifyStruct
;
489 Item
->PhysicalDeviceObject
= PhysicalDeviceObject
;
490 Item
->Callback
= Callback
;
491 Item
->Context
= Context
;
492 ExInitializeWorkItem(&(Item
->WorkItem
), (PWORKER_THREAD_ROUTINE
)IopReportTargetDeviceChangeAsyncWorker
, Item
);
494 /* Finally, queue the item, our work here is done */
495 ExQueueWorkItem(&(Item
->WorkItem
), DelayedWorkQueue
);
497 return STATUS_PENDING
;