* FILE: ntoskrnl/io/pnpmgr/pnpreport.c
* PURPOSE: Device Changes Reporting Functions
* PROGRAMMERS: Cameron Gutman (cameron.gutman@reactos.org)
+ * Pierre Schweitzer
*/
/* INCLUDES ******************************************************************/
#define NDEBUG
#include <debug.h>
+/* TYPES *******************************************************************/
+
+typedef struct _INTERNAL_WORK_QUEUE_ITEM
+{
+ WORK_QUEUE_ITEM WorkItem;
+ PDEVICE_OBJECT PhysicalDeviceObject;
+ PDEVICE_CHANGE_COMPLETE_CALLBACK Callback;
+ PVOID Context;
+ PTARGET_DEVICE_CUSTOM_NOTIFICATION NotificationStructure;
+} INTERNAL_WORK_QUEUE_ITEM, *PINTERNAL_WORK_QUEUE_ITEM;
+
NTSTATUS
NTAPI
IopCreateDeviceKeyPath(IN PCUNICODE_STRING RegistryPath,
PVOID Context);
NTSTATUS
-IopDetectResourceConflict(
- IN PCM_RESOURCE_LIST ResourceList);
+PpSetCustomTargetEvent(IN PDEVICE_OBJECT DeviceObject,
+ IN OUT PKEVENT SyncEvent OPTIONAL,
+ IN OUT PNTSTATUS SyncStatus OPTIONAL,
+ IN PDEVICE_CHANGE_COMPLETE_CALLBACK Callback OPTIONAL,
+ IN PVOID Context OPTIONAL,
+ IN PTARGET_DEVICE_CUSTOM_NOTIFICATION NotificationStructure);
/* PRIVATE FUNCTIONS *********************************************************/
}
}
+VOID
+IopReportTargetDeviceChangeAsyncWorker(PVOID Context)
+{
+ PINTERNAL_WORK_QUEUE_ITEM Item;
+
+ Item = (PINTERNAL_WORK_QUEUE_ITEM)Context;
+ PpSetCustomTargetEvent(Item->PhysicalDeviceObject, NULL, NULL, Item->Callback, Item->Context, Item->NotificationStructure);
+ ObDereferenceObject(Item->PhysicalDeviceObject);
+ ExFreePoolWithTag(Context, ' pP');
+}
+
+NTSTATUS
+PpSetCustomTargetEvent(IN PDEVICE_OBJECT DeviceObject,
+ IN OUT PKEVENT SyncEvent OPTIONAL,
+ IN OUT PNTSTATUS SyncStatus OPTIONAL,
+ IN PDEVICE_CHANGE_COMPLETE_CALLBACK Callback OPTIONAL,
+ IN PVOID Context OPTIONAL,
+ IN PTARGET_DEVICE_CUSTOM_NOTIFICATION NotificationStructure)
+{
+ ASSERT(NotificationStructure != NULL);
+ ASSERT(DeviceObject != NULL);
+
+ if (SyncEvent)
+ {
+ ASSERT(SyncStatus);
+ *SyncStatus = STATUS_PENDING;
+ }
+
+ /* That call is totally wrong but notifications handler must be fixed first */
+ IopNotifyPlugPlayNotification(DeviceObject,
+ EventCategoryTargetDeviceChange,
+ &GUID_PNP_CUSTOM_NOTIFICATION,
+ NotificationStructure,
+ NULL);
+
+ if (SyncEvent)
+ {
+ KeSetEvent(SyncEvent, IO_NO_INCREMENT, FALSE);
+ *SyncStatus = STATUS_SUCCESS;
+ }
+
+ return STATUS_SUCCESS;
+}
+
/* PUBLIC FUNCTIONS **********************************************************/
/*
if (DeviceObject && *DeviceObject)
{
Pdo = *DeviceObject;
- DeviceNode = IopGetDeviceNode(*DeviceObject);
}
else
{
/* Create the PDO */
Status = PnpRootCreateDevice(&ServiceName,
+ NULL,
&Pdo,
NULL);
if (!NT_SUCCESS(Status))
DPRINT("PnpRootCreateDevice() failed (Status 0x%08lx)\n", Status);
return Status;
}
+ }
- /* Create the device node for the new PDO */
- Status = IopCreateDeviceNode(IopRootDeviceNode,
- Pdo,
- NULL,
- &DeviceNode);
+ /* Create the device node for the new PDO */
+ Status = IopCreateDeviceNode(IopRootDeviceNode,
+ Pdo,
+ NULL,
+ &DeviceNode);
- if (!NT_SUCCESS(Status))
- {
- DPRINT("IopCreateDeviceNode() failed (Status 0x%08lx)\n", Status);
- return Status;
- }
+ if (!NT_SUCCESS(Status))
+ {
+ DPRINT("IopCreateDeviceNode() failed (Status 0x%08lx)\n", Status);
+ return Status;
}
+ /* We're enumerated already */
+ IopDeviceNodeSetFlag(DeviceNode, DNF_ENUMERATED);
+
/* We don't call AddDevice for devices reported this way */
IopDeviceNodeSetFlag(DeviceNode, DNF_ADDED);
IopActionConfigureChildServices(DeviceNode, DeviceNode->Parent);
/* Open a handle to the instance path key */
- Status = IopCreateDeviceKeyPath(&DeviceNode->InstancePath, 0, &InstanceKey);
+ /* REG_OPTION_VOLATILE is a HACK!!! */
+ Status = IopCreateDeviceKeyPath(&DeviceNode->InstancePath, REG_OPTION_VOLATILE, &InstanceKey);
if (!NT_SUCCESS(Status))
return Status;
L"DETECTED%ls\\%wZ",
IfString,
&ServiceName);
- HardwareId[IdLength++] = UNICODE_NULL;
+ IdLength++;
/* Add DETECTED\DriverName */
IdLength += swprintf(&HardwareId[IdLength],
L"DETECTED\\%wZ",
&ServiceName);
- HardwareId[IdLength++] = UNICODE_NULL;
+ IdLength++;
/* Terminate the string with another null */
- HardwareId[IdLength] = UNICODE_NULL;
+ HardwareId[IdLength++] = UNICODE_NULL;
/* Store the value for CompatibleIDs */
RtlInitUnicodeString(&ValueName, L"CompatibleIDs");
L"DETECTED%ls\\%wZ",
IfString,
&ServiceName);
- HardwareId[++IdLength] = UNICODE_NULL;
+ IdLength++;
+
+ HardwareId[IdLength++] = UNICODE_NULL;
/* Write the value to the registry */
- Status = ZwSetValueKey(InstanceKey, &ValueName, 0, REG_SZ, HardwareId, IdLength * sizeof(WCHAR));
+ Status = ZwSetValueKey(InstanceKey, &ValueName, 0, REG_MULTI_SZ, HardwareId, IdLength * sizeof(WCHAR));
if (!NT_SUCCESS(Status))
{
DPRINT("Failed to write the hardware ID: 0x%x\n", Status);
/* Close the instance key handle */
ZwClose(InstanceKey);
+ /* Register the given DO with PnP root if required */
+ if (DeviceObject && *DeviceObject)
+ PnpRootRegisterDevice(*DeviceObject);
+
/* Report the device's enumeration to umpnpmgr */
IopQueueTargetDeviceEvent(&GUID_DEVICE_ENUMERATED,
&DeviceNode->InstancePath);
ResourceList = DriverList;
/* Look for a resource conflict */
- Status = IopDetectResourceConflict(ResourceList);
+ Status = IopDetectResourceConflict(ResourceList, FALSE, NULL);
if (Status == STATUS_CONFLICTING_ADDRESSES)
{
/* Oh noes */
}
/*
- * @unimplemented
+ * @implemented
*/
NTSTATUS
NTAPI
IoReportTargetDeviceChange(IN PDEVICE_OBJECT PhysicalDeviceObject,
IN PVOID NotificationStructure)
{
- UNIMPLEMENTED;
- return STATUS_NOT_IMPLEMENTED;
+ KEVENT NotifyEvent;
+ NTSTATUS Status, NotifyStatus;
+ PTARGET_DEVICE_CUSTOM_NOTIFICATION notifyStruct = (PTARGET_DEVICE_CUSTOM_NOTIFICATION)NotificationStructure;
+
+ ASSERT(notifyStruct);
+
+ /* Check for valid PDO */
+ if (!IopIsValidPhysicalDeviceObject(PhysicalDeviceObject))
+ {
+ KeBugCheckEx(PNP_DETECTED_FATAL_ERROR, 0x2, (ULONG)PhysicalDeviceObject, 0, 0);
+ }
+
+ /* FileObject must be null. PnP will fill in it */
+ ASSERT(notifyStruct->FileObject == NULL);
+
+ /* Do not handle system PnP events */
+ if ((RtlCompareMemory(&(notifyStruct->Event), &(GUID_TARGET_DEVICE_QUERY_REMOVE), sizeof(GUID)) != sizeof(GUID)) ||
+ (RtlCompareMemory(&(notifyStruct->Event), &(GUID_TARGET_DEVICE_REMOVE_CANCELLED), sizeof(GUID)) != sizeof(GUID)) ||
+ (RtlCompareMemory(&(notifyStruct->Event), &(GUID_TARGET_DEVICE_REMOVE_COMPLETE), sizeof(GUID)) != sizeof(GUID)))
+ {
+ return STATUS_INVALID_DEVICE_REQUEST;
+ }
+
+ if (notifyStruct->Version != 1)
+ {
+ return STATUS_INVALID_DEVICE_REQUEST;
+ }
+
+ /* Initialize even that will let us know when PnP will have finished notify */
+ KeInitializeEvent(&NotifyEvent, NotificationEvent, FALSE);
+
+ Status = PpSetCustomTargetEvent(PhysicalDeviceObject, &NotifyEvent, &NotifyStatus, NULL, NULL, notifyStruct);
+ /* If no error, wait for the notify to end and return the status of the notify and not of the event */
+ if (NT_SUCCESS(Status))
+ {
+ KeWaitForSingleObject(&NotifyEvent, Executive, KernelMode, FALSE, NULL);
+ Status = NotifyStatus;
+ }
+
+ return Status;
}
/*
- * @unimplemented
+ * @implemented
*/
NTSTATUS
NTAPI
IN PDEVICE_CHANGE_COMPLETE_CALLBACK Callback OPTIONAL,
IN PVOID Context OPTIONAL)
{
- UNIMPLEMENTED;
- return STATUS_NOT_IMPLEMENTED;
+ PINTERNAL_WORK_QUEUE_ITEM Item = NULL;
+ PTARGET_DEVICE_CUSTOM_NOTIFICATION notifyStruct = (PTARGET_DEVICE_CUSTOM_NOTIFICATION)NotificationStructure;
+
+ ASSERT(notifyStruct);
+
+ /* Check for valid PDO */
+ if (!IopIsValidPhysicalDeviceObject(PhysicalDeviceObject))
+ {
+ KeBugCheckEx(PNP_DETECTED_FATAL_ERROR, 0x2, (ULONG)PhysicalDeviceObject, 0, 0);
+ }
+
+ /* FileObject must be null. PnP will fill in it */
+ ASSERT(notifyStruct->FileObject == NULL);
+
+ /* Do not handle system PnP events */
+ if ((RtlCompareMemory(&(notifyStruct->Event), &(GUID_TARGET_DEVICE_QUERY_REMOVE), sizeof(GUID)) != sizeof(GUID)) ||
+ (RtlCompareMemory(&(notifyStruct->Event), &(GUID_TARGET_DEVICE_REMOVE_CANCELLED), sizeof(GUID)) != sizeof(GUID)) ||
+ (RtlCompareMemory(&(notifyStruct->Event), &(GUID_TARGET_DEVICE_REMOVE_COMPLETE), sizeof(GUID)) != sizeof(GUID)))
+ {
+ return STATUS_INVALID_DEVICE_REQUEST;
+ }
+
+ if (notifyStruct->Version != 1)
+ {
+ return STATUS_INVALID_DEVICE_REQUEST;
+ }
+
+ /* We need to store all the data given by the caller with the WorkItem, so use our own struct */
+ Item = ExAllocatePoolWithTag(NonPagedPool, sizeof(INTERNAL_WORK_QUEUE_ITEM), ' pP');
+ if (!Item) return STATUS_INSUFFICIENT_RESOURCES;
+
+ /* Initialize all stuff */
+ ObReferenceObject(PhysicalDeviceObject);
+ Item->NotificationStructure = notifyStruct;
+ Item->PhysicalDeviceObject = PhysicalDeviceObject;
+ Item->Callback = Callback;
+ Item->Context = Context;
+ ExInitializeWorkItem(&(Item->WorkItem), (PWORKER_THREAD_ROUTINE)IopReportTargetDeviceChangeAsyncWorker, Item);
+
+ /* Finally, queue the item, our work here is done */
+ ExQueueWorkItem(&(Item->WorkItem), DelayedWorkQueue);
+
+ return STATUS_PENDING;
}