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
;
28 IopSetDeviceInstanceData(HANDLE InstanceKey
,
29 PDEVICE_NODE DeviceNode
);
32 IopActionInterrogateDeviceStack(PDEVICE_NODE DeviceNode
,
36 PpSetCustomTargetEvent(IN PDEVICE_OBJECT DeviceObject
,
37 IN OUT PKEVENT SyncEvent OPTIONAL
,
38 IN OUT PNTSTATUS SyncStatus OPTIONAL
,
39 IN PDEVICE_CHANGE_COMPLETE_CALLBACK Callback OPTIONAL
,
40 IN PVOID Context OPTIONAL
,
41 IN PTARGET_DEVICE_CUSTOM_NOTIFICATION NotificationStructure
);
43 /* PRIVATE FUNCTIONS *********************************************************/
46 IopGetInterfaceTypeString(INTERFACE_TYPE IfType
)
60 return L
"MicroChannel";
63 return L
"TurboChannel";
86 case ProcessorInternal
:
87 return L
"ProcessorInternal";
99 DPRINT1("Invalid bus type: %d\n", IfType
);
106 IopReportTargetDeviceChangeAsyncWorker(PVOID Context
)
108 PINTERNAL_WORK_QUEUE_ITEM Item
;
110 Item
= (PINTERNAL_WORK_QUEUE_ITEM
)Context
;
111 PpSetCustomTargetEvent(Item
->PhysicalDeviceObject
, NULL
, NULL
, Item
->Callback
, Item
->Context
, Item
->NotificationStructure
);
112 ObDereferenceObject(Item
->PhysicalDeviceObject
);
113 ExFreePoolWithTag(Context
, ' pP');
117 PpSetCustomTargetEvent(IN PDEVICE_OBJECT DeviceObject
,
118 IN OUT PKEVENT SyncEvent OPTIONAL
,
119 IN OUT PNTSTATUS SyncStatus OPTIONAL
,
120 IN PDEVICE_CHANGE_COMPLETE_CALLBACK Callback OPTIONAL
,
121 IN PVOID Context OPTIONAL
,
122 IN PTARGET_DEVICE_CUSTOM_NOTIFICATION NotificationStructure
)
124 ASSERT(NotificationStructure
!= NULL
);
125 ASSERT(DeviceObject
!= NULL
);
130 *SyncStatus
= STATUS_PENDING
;
133 /* That call is totally wrong but notifications handler must be fixed first */
134 IopNotifyPlugPlayNotification(DeviceObject
,
135 EventCategoryTargetDeviceChange
,
136 &GUID_PNP_CUSTOM_NOTIFICATION
,
137 NotificationStructure
,
142 KeSetEvent(SyncEvent
, IO_NO_INCREMENT
, FALSE
);
143 *SyncStatus
= STATUS_SUCCESS
;
146 return STATUS_SUCCESS
;
149 /* PUBLIC FUNCTIONS **********************************************************/
156 IoReportDetectedDevice(IN PDRIVER_OBJECT DriverObject
,
157 IN INTERFACE_TYPE LegacyBusType
,
160 IN PCM_RESOURCE_LIST ResourceList
,
161 IN PIO_RESOURCE_REQUIREMENTS_LIST ResourceRequirements OPTIONAL
,
162 IN BOOLEAN ResourceAssigned
,
163 IN OUT PDEVICE_OBJECT
*DeviceObject OPTIONAL
)
165 PDEVICE_NODE DeviceNode
;
169 ULONG RequiredLength
;
170 UNICODE_STRING ValueName
, ServiceLongName
, ServiceName
;
171 WCHAR HardwareId
[256];
176 DPRINT("IoReportDetectedDevice (DeviceObject %p, *DeviceObject %p)\n",
177 DeviceObject
, DeviceObject
? *DeviceObject
: NULL
);
179 ServiceLongName
= DriverObject
->DriverExtension
->ServiceKeyName
;
180 ServiceName
= ServiceLongName
;
182 /* If the interface type is unknown, treat it as internal */
183 if (LegacyBusType
== InterfaceTypeUndefined
)
184 LegacyBusType
= Internal
;
186 /* Get the string equivalent of the interface type */
187 IfString
= IopGetInterfaceTypeString(LegacyBusType
);
189 /* If NULL is returned then it's a bad type */
191 return STATUS_INVALID_PARAMETER
;
194 * Drivers that have been created via a direct IoCreateDriver() call
195 * have their ServiceKeyName set to \Driver\DriverName. We need to
196 * strip everything up to the last path separator and keep what remains.
198 if (DriverObject
->Flags
& DRVO_BUILTIN_DRIVER
)
201 * Find the last path separator.
202 * NOTE: Since ServiceName is not necessarily NULL-terminated,
203 * we cannot use wcsrchr().
205 if (ServiceName
.Buffer
&& ServiceName
.Length
>= sizeof(WCHAR
))
207 ValueName
.Length
= 1;
208 ValueName
.Buffer
= ServiceName
.Buffer
+ (ServiceName
.Length
/ sizeof(WCHAR
)) - 1;
210 while ((ValueName
.Buffer
> ServiceName
.Buffer
) && (*ValueName
.Buffer
!= L
'\\'))
215 if (*ValueName
.Buffer
== L
'\\')
220 ValueName
.Length
*= sizeof(WCHAR
);
222 /* Shorten the string */
223 ServiceName
.MaximumLength
-= (ServiceName
.Length
- ValueName
.Length
);
224 ServiceName
.Length
= ValueName
.Length
;
225 ServiceName
.Buffer
= ValueName
.Buffer
;
229 /* We use the caller's PDO if they supplied one */
230 if (DeviceObject
&& *DeviceObject
)
237 Status
= PnpRootCreateDevice(&ServiceName
,
241 if (!NT_SUCCESS(Status
))
243 DPRINT("PnpRootCreateDevice() failed (Status 0x%08lx)\n", Status
);
248 /* Create the device node for the new PDO */
249 Status
= IopCreateDeviceNode(IopRootDeviceNode
,
253 if (!NT_SUCCESS(Status
))
255 DPRINT("IopCreateDeviceNode() failed (Status 0x%08lx)\n", Status
);
259 /* We're enumerated already */
260 IopDeviceNodeSetFlag(DeviceNode
, DNF_ENUMERATED
);
262 /* We don't call AddDevice for devices reported this way */
263 IopDeviceNodeSetFlag(DeviceNode
, DNF_ADDED
);
265 /* We don't send IRP_MN_START_DEVICE */
266 IopDeviceNodeSetFlag(DeviceNode
, DNF_STARTED
);
268 /* We need to get device IDs */
270 IopDeviceNodeSetFlag(DeviceNode
, DNF_NEED_QUERY_IDS
);
273 /* This is a legacy driver for this device */
274 IopDeviceNodeSetFlag(DeviceNode
, DNF_LEGACY_DRIVER
);
276 /* Perform a manual configuration of our device */
277 IopActionInterrogateDeviceStack(DeviceNode
, DeviceNode
->Parent
);
278 IopActionConfigureChildServices(DeviceNode
, DeviceNode
->Parent
);
280 /* Open a handle to the instance path key */
281 Status
= IopCreateDeviceKeyPath(&DeviceNode
->InstancePath
, REG_OPTION_NON_VOLATILE
, &InstanceKey
);
282 if (!NT_SUCCESS(Status
))
285 /* Save the driver name */
286 RtlInitUnicodeString(&ValueName
, L
"Service");
287 Status
= ZwSetValueKey(InstanceKey
, &ValueName
, 0, REG_SZ
, ServiceLongName
.Buffer
, ServiceLongName
.Length
+ sizeof(UNICODE_NULL
));
288 if (!NT_SUCCESS(Status
))
290 DPRINT("Failed to write the Service name value: 0x%x\n", Status
);
293 /* Report as non-legacy driver */
294 RtlInitUnicodeString(&ValueName
, L
"Legacy");
296 Status
= ZwSetValueKey(InstanceKey
, &ValueName
, 0, REG_DWORD
, &LegacyValue
, sizeof(LegacyValue
));
297 if (!NT_SUCCESS(Status
))
299 DPRINT("Failed to write the Legacy value: 0x%x\n", Status
);
302 /* Add DETECTEDInterfaceType\DriverName */
304 IdLength
+= swprintf(&HardwareId
[IdLength
],
310 /* Add DETECTED\DriverName */
311 IdLength
+= swprintf(&HardwareId
[IdLength
],
316 /* Terminate the string with another null */
317 HardwareId
[IdLength
++] = UNICODE_NULL
;
319 /* Store the value for CompatibleIDs */
320 RtlInitUnicodeString(&ValueName
, L
"CompatibleIDs");
321 Status
= ZwSetValueKey(InstanceKey
, &ValueName
, 0, REG_MULTI_SZ
, HardwareId
, IdLength
* sizeof(WCHAR
));
322 if (!NT_SUCCESS(Status
))
324 DPRINT("Failed to write the compatible IDs: 0x%x\n", Status
);
325 ZwClose(InstanceKey
);
329 /* Add a hardware ID if the driver didn't report one */
330 RtlInitUnicodeString(&ValueName
, L
"HardwareID");
331 if (ZwQueryValueKey(InstanceKey
, &ValueName
, KeyValueBasicInformation
, NULL
, 0, &RequiredLength
) == STATUS_OBJECT_NAME_NOT_FOUND
)
333 /* Just use our most specific compatible ID */
335 IdLength
+= swprintf(&HardwareId
[IdLength
],
341 HardwareId
[IdLength
++] = UNICODE_NULL
;
343 /* Write the value to the registry */
344 Status
= ZwSetValueKey(InstanceKey
, &ValueName
, 0, REG_MULTI_SZ
, HardwareId
, IdLength
* sizeof(WCHAR
));
345 if (!NT_SUCCESS(Status
))
347 DPRINT("Failed to write the hardware ID: 0x%x\n", Status
);
348 ZwClose(InstanceKey
);
353 /* Assign the resources to the device node */
354 DeviceNode
->BootResources
= ResourceList
;
355 DeviceNode
->ResourceRequirements
= ResourceRequirements
;
357 /* Set appropriate flags */
358 if (DeviceNode
->BootResources
)
359 IopDeviceNodeSetFlag(DeviceNode
, DNF_HAS_BOOT_CONFIG
);
361 if (!DeviceNode
->ResourceRequirements
&& !DeviceNode
->BootResources
)
362 IopDeviceNodeSetFlag(DeviceNode
, DNF_NO_RESOURCE_REQUIRED
);
364 /* Write the resource information to the registry */
365 IopSetDeviceInstanceData(InstanceKey
, DeviceNode
);
367 /* If the caller didn't get the resources assigned for us, do it now */
368 if (!ResourceAssigned
)
370 Status
= IopAssignDeviceResources(DeviceNode
);
372 /* See if we failed */
373 if (!NT_SUCCESS(Status
))
375 DPRINT("Assigning resources failed: 0x%x\n", Status
);
376 ZwClose(InstanceKey
);
381 /* Close the instance key handle */
382 ZwClose(InstanceKey
);
384 /* Register the given DO with PnP root if required */
385 if (DeviceObject
&& *DeviceObject
)
386 PnpRootRegisterDevice(*DeviceObject
);
388 /* Report the device's enumeration to umpnpmgr */
389 IopQueueTargetDeviceEvent(&GUID_DEVICE_ENUMERATED
,
390 &DeviceNode
->InstancePath
);
392 /* Report the device's arrival to umpnpmgr */
393 IopQueueTargetDeviceEvent(&GUID_DEVICE_ARRIVAL
,
394 &DeviceNode
->InstancePath
);
396 DPRINT("Reported device: %S (%wZ)\n", HardwareId
, &DeviceNode
->InstancePath
);
399 if (DeviceObject
) *DeviceObject
= Pdo
;
401 return STATUS_SUCCESS
;
409 IoReportResourceForDetection(IN PDRIVER_OBJECT DriverObject
,
410 IN PCM_RESOURCE_LIST DriverList OPTIONAL
,
411 IN ULONG DriverListSize OPTIONAL
,
412 IN PDEVICE_OBJECT DeviceObject OPTIONAL
,
413 IN PCM_RESOURCE_LIST DeviceList OPTIONAL
,
414 IN ULONG DeviceListSize OPTIONAL
,
415 OUT PBOOLEAN ConflictDetected
)
417 PCM_RESOURCE_LIST ResourceList
;
420 *ConflictDetected
= FALSE
;
422 if (!DriverList
&& !DeviceList
)
423 return STATUS_INVALID_PARAMETER
;
425 /* Find the real list */
427 ResourceList
= DeviceList
;
429 ResourceList
= DriverList
;
431 /* Look for a resource conflict */
432 Status
= IopDetectResourceConflict(ResourceList
, FALSE
, NULL
);
433 if (Status
== STATUS_CONFLICTING_ADDRESSES
)
436 *ConflictDetected
= TRUE
;
438 else if (NT_SUCCESS(Status
))
440 /* Looks like we're good to go */
442 /* TODO: Claim the resources in the ResourceMap */
450 IopSetEvent(IN PVOID Context
)
452 PKEVENT Event
= Context
;
455 KeSetEvent(Event
, IO_NO_INCREMENT
, FALSE
);
463 IoReportTargetDeviceChange(IN PDEVICE_OBJECT PhysicalDeviceObject
,
464 IN PVOID NotificationStructure
)
467 NTSTATUS Status
, NotifyStatus
;
468 PTARGET_DEVICE_CUSTOM_NOTIFICATION notifyStruct
= (PTARGET_DEVICE_CUSTOM_NOTIFICATION
)NotificationStructure
;
470 ASSERT(notifyStruct
);
472 /* Check for valid PDO */
473 if (!IopIsValidPhysicalDeviceObject(PhysicalDeviceObject
))
475 KeBugCheckEx(PNP_DETECTED_FATAL_ERROR
, 0x2, (ULONG_PTR
)PhysicalDeviceObject
, 0, 0);
478 /* FileObject must be null. PnP will fill in it */
479 ASSERT(notifyStruct
->FileObject
== NULL
);
481 /* Do not handle system PnP events */
482 if ((RtlCompareMemory(&(notifyStruct
->Event
), &(GUID_TARGET_DEVICE_QUERY_REMOVE
), sizeof(GUID
)) != sizeof(GUID
)) ||
483 (RtlCompareMemory(&(notifyStruct
->Event
), &(GUID_TARGET_DEVICE_REMOVE_CANCELLED
), sizeof(GUID
)) != sizeof(GUID
)) ||
484 (RtlCompareMemory(&(notifyStruct
->Event
), &(GUID_TARGET_DEVICE_REMOVE_COMPLETE
), sizeof(GUID
)) != sizeof(GUID
)))
486 return STATUS_INVALID_DEVICE_REQUEST
;
489 if (notifyStruct
->Version
!= 1)
491 return STATUS_INVALID_DEVICE_REQUEST
;
494 /* Initialize even that will let us know when PnP will have finished notify */
495 KeInitializeEvent(&NotifyEvent
, NotificationEvent
, FALSE
);
497 Status
= PpSetCustomTargetEvent(PhysicalDeviceObject
, &NotifyEvent
, &NotifyStatus
, NULL
, NULL
, notifyStruct
);
498 /* If no error, wait for the notify to end and return the status of the notify and not of the event */
499 if (NT_SUCCESS(Status
))
501 KeWaitForSingleObject(&NotifyEvent
, Executive
, KernelMode
, FALSE
, NULL
);
502 Status
= NotifyStatus
;
513 IoReportTargetDeviceChangeAsynchronous(IN PDEVICE_OBJECT PhysicalDeviceObject
,
514 IN PVOID NotificationStructure
,
515 IN PDEVICE_CHANGE_COMPLETE_CALLBACK Callback OPTIONAL
,
516 IN PVOID Context OPTIONAL
)
518 PINTERNAL_WORK_QUEUE_ITEM Item
= NULL
;
519 PTARGET_DEVICE_CUSTOM_NOTIFICATION notifyStruct
= (PTARGET_DEVICE_CUSTOM_NOTIFICATION
)NotificationStructure
;
521 ASSERT(notifyStruct
);
523 /* Check for valid PDO */
524 if (!IopIsValidPhysicalDeviceObject(PhysicalDeviceObject
))
526 KeBugCheckEx(PNP_DETECTED_FATAL_ERROR
, 0x2, (ULONG_PTR
)PhysicalDeviceObject
, 0, 0);
529 /* FileObject must be null. PnP will fill in it */
530 ASSERT(notifyStruct
->FileObject
== NULL
);
532 /* Do not handle system PnP events */
533 if ((RtlCompareMemory(&(notifyStruct
->Event
), &(GUID_TARGET_DEVICE_QUERY_REMOVE
), sizeof(GUID
)) != sizeof(GUID
)) ||
534 (RtlCompareMemory(&(notifyStruct
->Event
), &(GUID_TARGET_DEVICE_REMOVE_CANCELLED
), sizeof(GUID
)) != sizeof(GUID
)) ||
535 (RtlCompareMemory(&(notifyStruct
->Event
), &(GUID_TARGET_DEVICE_REMOVE_COMPLETE
), sizeof(GUID
)) != sizeof(GUID
)))
537 return STATUS_INVALID_DEVICE_REQUEST
;
540 if (notifyStruct
->Version
!= 1)
542 return STATUS_INVALID_DEVICE_REQUEST
;
545 /* We need to store all the data given by the caller with the WorkItem, so use our own struct */
546 Item
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(INTERNAL_WORK_QUEUE_ITEM
), ' pP');
547 if (!Item
) return STATUS_INSUFFICIENT_RESOURCES
;
549 /* Initialize all stuff */
550 ObReferenceObject(PhysicalDeviceObject
);
551 Item
->NotificationStructure
= notifyStruct
;
552 Item
->PhysicalDeviceObject
= PhysicalDeviceObject
;
553 Item
->Callback
= Callback
;
554 Item
->Context
= Context
;
555 ExInitializeWorkItem(&(Item
->WorkItem
), IopReportTargetDeviceChangeAsyncWorker
, Item
);
557 /* Finally, queue the item, our work here is done */
558 ExQueueWorkItem(&(Item
->WorkItem
), DelayedWorkQueue
);
560 return STATUS_PENDING
;