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 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
);
49 /* PRIVATE FUNCTIONS *********************************************************/
52 IopGetInterfaceTypeString(INTERFACE_TYPE IfType
)
66 return L
"MicroChannel";
69 return L
"TurboChannel";
92 case ProcessorInternal
:
93 return L
"ProcessorInternal";
105 DPRINT1("Invalid bus type: %d\n", IfType
);
112 IopReportTargetDeviceChangeAsyncWorker(PVOID Context
)
114 PINTERNAL_WORK_QUEUE_ITEM Item
;
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');
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
)
130 ASSERT(NotificationStructure
!= NULL
);
131 ASSERT(DeviceObject
!= NULL
);
136 *SyncStatus
= STATUS_PENDING
;
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
,
148 KeSetEvent(SyncEvent
, IO_NO_INCREMENT
, FALSE
);
149 *SyncStatus
= STATUS_SUCCESS
;
152 return STATUS_SUCCESS
;
155 /* PUBLIC FUNCTIONS **********************************************************/
162 IoReportDetectedDevice(IN PDRIVER_OBJECT DriverObject
,
163 IN INTERFACE_TYPE LegacyBusType
,
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
)
171 PDEVICE_NODE DeviceNode
;
175 ULONG RequiredLength
;
176 UNICODE_STRING ValueName
, ServiceLongName
, ServiceName
;
177 WCHAR HardwareId
[256];
182 DPRINT("IoReportDetectedDevice (DeviceObject %p, *DeviceObject %p)\n",
183 DeviceObject
, DeviceObject
? *DeviceObject
: NULL
);
185 ServiceLongName
= DriverObject
->DriverExtension
->ServiceKeyName
;
186 ServiceName
= ServiceLongName
;
188 /* If the interface type is unknown, treat it as internal */
189 if (LegacyBusType
== InterfaceTypeUndefined
)
190 LegacyBusType
= Internal
;
192 /* Get the string equivalent of the interface type */
193 IfString
= IopGetInterfaceTypeString(LegacyBusType
);
195 /* If NULL is returned then it's a bad type */
197 return STATUS_INVALID_PARAMETER
;
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.
204 if (DriverObject
->Flags
& DRVO_BUILTIN_DRIVER
)
207 * Find the last path separator.
208 * NOTE: Since ServiceName is not necessarily NULL-terminated,
209 * we cannot use wcsrchr().
211 if (ServiceName
.Buffer
&& ServiceName
.Length
>= sizeof(WCHAR
))
213 ValueName
.Length
= 1;
214 ValueName
.Buffer
= ServiceName
.Buffer
+ (ServiceName
.Length
/ sizeof(WCHAR
)) - 1;
216 while ((ValueName
.Buffer
> ServiceName
.Buffer
) && (*ValueName
.Buffer
!= L
'\\'))
221 if (*ValueName
.Buffer
== L
'\\')
226 ValueName
.Length
*= sizeof(WCHAR
);
228 /* Shorten the string */
229 ServiceName
.MaximumLength
-= (ServiceName
.Length
- ValueName
.Length
);
230 ServiceName
.Length
= ValueName
.Length
;
231 ServiceName
.Buffer
= ValueName
.Buffer
;
235 /* We use the caller's PDO if they supplied one */
236 if (DeviceObject
&& *DeviceObject
)
243 Status
= PnpRootCreateDevice(&ServiceName
,
247 if (!NT_SUCCESS(Status
))
249 DPRINT("PnpRootCreateDevice() failed (Status 0x%08lx)\n", Status
);
254 /* Create the device node for the new PDO */
255 Status
= IopCreateDeviceNode(IopRootDeviceNode
,
259 if (!NT_SUCCESS(Status
))
261 DPRINT("IopCreateDeviceNode() failed (Status 0x%08lx)\n", Status
);
265 /* We're enumerated already */
266 IopDeviceNodeSetFlag(DeviceNode
, DNF_ENUMERATED
);
268 /* We don't call AddDevice for devices reported this way */
269 IopDeviceNodeSetFlag(DeviceNode
, DNF_ADDED
);
271 /* We don't send IRP_MN_START_DEVICE */
272 IopDeviceNodeSetFlag(DeviceNode
, DNF_STARTED
);
274 /* We need to get device IDs */
276 IopDeviceNodeSetFlag(DeviceNode
, DNF_NEED_QUERY_IDS
);
279 /* This is a legacy driver for this device */
280 IopDeviceNodeSetFlag(DeviceNode
, DNF_LEGACY_DRIVER
);
282 /* Perform a manual configuration of our device */
283 IopActionInterrogateDeviceStack(DeviceNode
, DeviceNode
->Parent
);
284 IopActionConfigureChildServices(DeviceNode
, DeviceNode
->Parent
);
286 /* Open a handle to the instance path key */
287 /* REG_OPTION_VOLATILE is a HACK!!! */
288 Status
= IopCreateDeviceKeyPath(&DeviceNode
->InstancePath
, REG_OPTION_VOLATILE
, &InstanceKey
);
289 if (!NT_SUCCESS(Status
))
292 /* Save the driver name */
293 RtlInitUnicodeString(&ValueName
, L
"Service");
294 Status
= ZwSetValueKey(InstanceKey
, &ValueName
, 0, REG_SZ
, ServiceLongName
.Buffer
, ServiceLongName
.Length
+ sizeof(UNICODE_NULL
));
295 if (!NT_SUCCESS(Status
))
297 DPRINT("Failed to write the Service name value: 0x%x\n", Status
);
300 /* Report as non-legacy driver */
301 RtlInitUnicodeString(&ValueName
, L
"Legacy");
303 Status
= ZwSetValueKey(InstanceKey
, &ValueName
, 0, REG_DWORD
, &LegacyValue
, sizeof(LegacyValue
));
304 if (!NT_SUCCESS(Status
))
306 DPRINT("Failed to write the Legacy value: 0x%x\n", Status
);
309 /* Add DETECTEDInterfaceType\DriverName */
311 IdLength
+= swprintf(&HardwareId
[IdLength
],
317 /* Add DETECTED\DriverName */
318 IdLength
+= swprintf(&HardwareId
[IdLength
],
323 /* Terminate the string with another null */
324 HardwareId
[IdLength
++] = UNICODE_NULL
;
326 /* Store the value for CompatibleIDs */
327 RtlInitUnicodeString(&ValueName
, L
"CompatibleIDs");
328 Status
= ZwSetValueKey(InstanceKey
, &ValueName
, 0, REG_MULTI_SZ
, HardwareId
, IdLength
* sizeof(WCHAR
));
329 if (!NT_SUCCESS(Status
))
331 DPRINT("Failed to write the compatible IDs: 0x%x\n", Status
);
332 ZwClose(InstanceKey
);
336 /* Add a hardware ID if the driver didn't report one */
337 RtlInitUnicodeString(&ValueName
, L
"HardwareID");
338 if (ZwQueryValueKey(InstanceKey
, &ValueName
, KeyValueBasicInformation
, NULL
, 0, &RequiredLength
) == STATUS_OBJECT_NAME_NOT_FOUND
)
340 /* Just use our most specific compatible ID */
342 IdLength
+= swprintf(&HardwareId
[IdLength
],
348 HardwareId
[IdLength
++] = UNICODE_NULL
;
350 /* Write the value to the registry */
351 Status
= ZwSetValueKey(InstanceKey
, &ValueName
, 0, REG_MULTI_SZ
, HardwareId
, IdLength
* sizeof(WCHAR
));
352 if (!NT_SUCCESS(Status
))
354 DPRINT("Failed to write the hardware ID: 0x%x\n", Status
);
355 ZwClose(InstanceKey
);
360 /* Assign the resources to the device node */
361 DeviceNode
->BootResources
= ResourceList
;
362 DeviceNode
->ResourceRequirements
= ResourceRequirements
;
364 /* Set appropriate flags */
365 if (DeviceNode
->BootResources
)
366 IopDeviceNodeSetFlag(DeviceNode
, DNF_HAS_BOOT_CONFIG
);
368 if (!DeviceNode
->ResourceRequirements
&& !DeviceNode
->BootResources
)
369 IopDeviceNodeSetFlag(DeviceNode
, DNF_NO_RESOURCE_REQUIRED
);
371 /* Write the resource information to the registry */
372 IopSetDeviceInstanceData(InstanceKey
, DeviceNode
);
374 /* If the caller didn't get the resources assigned for us, do it now */
375 if (!ResourceAssigned
)
377 Status
= IopAssignDeviceResources(DeviceNode
);
379 /* See if we failed */
380 if (!NT_SUCCESS(Status
))
382 DPRINT("Assigning resources failed: 0x%x\n", Status
);
383 ZwClose(InstanceKey
);
388 /* Close the instance key handle */
389 ZwClose(InstanceKey
);
391 /* Register the given DO with PnP root if required */
392 if (DeviceObject
&& *DeviceObject
)
393 PnpRootRegisterDevice(*DeviceObject
);
395 /* Report the device's enumeration to umpnpmgr */
396 IopQueueTargetDeviceEvent(&GUID_DEVICE_ENUMERATED
,
397 &DeviceNode
->InstancePath
);
399 /* Report the device's arrival to umpnpmgr */
400 IopQueueTargetDeviceEvent(&GUID_DEVICE_ARRIVAL
,
401 &DeviceNode
->InstancePath
);
403 DPRINT("Reported device: %S (%wZ)\n", HardwareId
, &DeviceNode
->InstancePath
);
406 if (DeviceObject
) *DeviceObject
= Pdo
;
408 return STATUS_SUCCESS
;
416 IoReportResourceForDetection(IN PDRIVER_OBJECT DriverObject
,
417 IN PCM_RESOURCE_LIST DriverList OPTIONAL
,
418 IN ULONG DriverListSize OPTIONAL
,
419 IN PDEVICE_OBJECT DeviceObject OPTIONAL
,
420 IN PCM_RESOURCE_LIST DeviceList OPTIONAL
,
421 IN ULONG DeviceListSize OPTIONAL
,
422 OUT PBOOLEAN ConflictDetected
)
424 PCM_RESOURCE_LIST ResourceList
;
427 *ConflictDetected
= FALSE
;
429 if (!DriverList
&& !DeviceList
)
430 return STATUS_INVALID_PARAMETER
;
432 /* Find the real list */
434 ResourceList
= DeviceList
;
436 ResourceList
= DriverList
;
438 /* Look for a resource conflict */
439 Status
= IopDetectResourceConflict(ResourceList
, FALSE
, NULL
);
440 if (Status
== STATUS_CONFLICTING_ADDRESSES
)
443 *ConflictDetected
= TRUE
;
445 else if (NT_SUCCESS(Status
))
447 /* Looks like we're good to go */
449 /* TODO: Claim the resources in the ResourceMap */
457 IopSetEvent(IN PVOID Context
)
459 PKEVENT Event
= Context
;
462 KeSetEvent(Event
, IO_NO_INCREMENT
, FALSE
);
470 IoReportTargetDeviceChange(IN PDEVICE_OBJECT PhysicalDeviceObject
,
471 IN PVOID NotificationStructure
)
474 NTSTATUS Status
, NotifyStatus
;
475 PTARGET_DEVICE_CUSTOM_NOTIFICATION notifyStruct
= (PTARGET_DEVICE_CUSTOM_NOTIFICATION
)NotificationStructure
;
477 ASSERT(notifyStruct
);
479 /* Check for valid PDO */
480 if (!IopIsValidPhysicalDeviceObject(PhysicalDeviceObject
))
482 KeBugCheckEx(PNP_DETECTED_FATAL_ERROR
, 0x2, (ULONG_PTR
)PhysicalDeviceObject
, 0, 0);
485 /* FileObject must be null. PnP will fill in it */
486 ASSERT(notifyStruct
->FileObject
== NULL
);
488 /* Do not handle system PnP events */
489 if ((RtlCompareMemory(&(notifyStruct
->Event
), &(GUID_TARGET_DEVICE_QUERY_REMOVE
), sizeof(GUID
)) != sizeof(GUID
)) ||
490 (RtlCompareMemory(&(notifyStruct
->Event
), &(GUID_TARGET_DEVICE_REMOVE_CANCELLED
), sizeof(GUID
)) != sizeof(GUID
)) ||
491 (RtlCompareMemory(&(notifyStruct
->Event
), &(GUID_TARGET_DEVICE_REMOVE_COMPLETE
), sizeof(GUID
)) != sizeof(GUID
)))
493 return STATUS_INVALID_DEVICE_REQUEST
;
496 if (notifyStruct
->Version
!= 1)
498 return STATUS_INVALID_DEVICE_REQUEST
;
501 /* Initialize even that will let us know when PnP will have finished notify */
502 KeInitializeEvent(&NotifyEvent
, NotificationEvent
, FALSE
);
504 Status
= PpSetCustomTargetEvent(PhysicalDeviceObject
, &NotifyEvent
, &NotifyStatus
, NULL
, NULL
, notifyStruct
);
505 /* If no error, wait for the notify to end and return the status of the notify and not of the event */
506 if (NT_SUCCESS(Status
))
508 KeWaitForSingleObject(&NotifyEvent
, Executive
, KernelMode
, FALSE
, NULL
);
509 Status
= NotifyStatus
;
520 IoReportTargetDeviceChangeAsynchronous(IN PDEVICE_OBJECT PhysicalDeviceObject
,
521 IN PVOID NotificationStructure
,
522 IN PDEVICE_CHANGE_COMPLETE_CALLBACK Callback OPTIONAL
,
523 IN PVOID Context OPTIONAL
)
525 PINTERNAL_WORK_QUEUE_ITEM Item
= NULL
;
526 PTARGET_DEVICE_CUSTOM_NOTIFICATION notifyStruct
= (PTARGET_DEVICE_CUSTOM_NOTIFICATION
)NotificationStructure
;
528 ASSERT(notifyStruct
);
530 /* Check for valid PDO */
531 if (!IopIsValidPhysicalDeviceObject(PhysicalDeviceObject
))
533 KeBugCheckEx(PNP_DETECTED_FATAL_ERROR
, 0x2, (ULONG_PTR
)PhysicalDeviceObject
, 0, 0);
536 /* FileObject must be null. PnP will fill in it */
537 ASSERT(notifyStruct
->FileObject
== NULL
);
539 /* Do not handle system PnP events */
540 if ((RtlCompareMemory(&(notifyStruct
->Event
), &(GUID_TARGET_DEVICE_QUERY_REMOVE
), sizeof(GUID
)) != sizeof(GUID
)) ||
541 (RtlCompareMemory(&(notifyStruct
->Event
), &(GUID_TARGET_DEVICE_REMOVE_CANCELLED
), sizeof(GUID
)) != sizeof(GUID
)) ||
542 (RtlCompareMemory(&(notifyStruct
->Event
), &(GUID_TARGET_DEVICE_REMOVE_COMPLETE
), sizeof(GUID
)) != sizeof(GUID
)))
544 return STATUS_INVALID_DEVICE_REQUEST
;
547 if (notifyStruct
->Version
!= 1)
549 return STATUS_INVALID_DEVICE_REQUEST
;
552 /* We need to store all the data given by the caller with the WorkItem, so use our own struct */
553 Item
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(INTERNAL_WORK_QUEUE_ITEM
), ' pP');
554 if (!Item
) return STATUS_INSUFFICIENT_RESOURCES
;
556 /* Initialize all stuff */
557 ObReferenceObject(PhysicalDeviceObject
);
558 Item
->NotificationStructure
= notifyStruct
;
559 Item
->PhysicalDeviceObject
= PhysicalDeviceObject
;
560 Item
->Callback
= Callback
;
561 Item
->Context
= Context
;
562 ExInitializeWorkItem(&(Item
->WorkItem
), IopReportTargetDeviceChangeAsyncWorker
, Item
);
564 /* Finally, queue the item, our work here is done */
565 ExQueueWorkItem(&(Item
->WorkItem
), DelayedWorkQueue
);
567 return STATUS_PENDING
;