* Sync up to trunk head (r64921).
[reactos.git] / ntoskrnl / io / pnpmgr / pnpreport.c
index f69bce0..206fccf 100644 (file)
@@ -4,6 +4,7 @@
  * 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,
@@ -27,8 +39,12 @@ IopActionInterrogateDeviceStack(PDEVICE_NODE DeviceNode,
                                 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 *********************************************************/
 
@@ -91,6 +107,50 @@ IopGetInterfaceTypeString(INTERFACE_TYPE IfType)
     }
 }
 
+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 **********************************************************/
 
 /*
@@ -142,12 +202,12 @@ IoReportDetectedDevice(IN PDRIVER_OBJECT DriverObject,
     if (DeviceObject && *DeviceObject)
     {
         Pdo = *DeviceObject;
-        DeviceNode = IopGetDeviceNode(*DeviceObject);
     }
     else
     {
         /* Create the PDO */
         Status = PnpRootCreateDevice(&ServiceName,
+                                     NULL,
                                      &Pdo,
                                      NULL);
         if (!NT_SUCCESS(Status))
@@ -155,20 +215,23 @@ IoReportDetectedDevice(IN PDRIVER_OBJECT DriverObject,
             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);
 
@@ -188,7 +251,8 @@ IoReportDetectedDevice(IN PDRIVER_OBJECT DriverObject,
     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;
 
@@ -198,16 +262,16 @@ IoReportDetectedDevice(IN PDRIVER_OBJECT DriverObject,
                          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");
@@ -229,10 +293,12 @@ IoReportDetectedDevice(IN PDRIVER_OBJECT DriverObject,
                             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);
@@ -272,6 +338,10 @@ IoReportDetectedDevice(IN PDRIVER_OBJECT DriverObject,
     /* 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);
@@ -316,7 +386,7 @@ IoReportResourceForDetection(IN PDRIVER_OBJECT DriverObject,
         ResourceList = DriverList;
 
     /* Look for a resource conflict */
-    Status = IopDetectResourceConflict(ResourceList);
+    Status = IopDetectResourceConflict(ResourceList, FALSE, NULL);
     if (Status == STATUS_CONFLICTING_ADDRESSES)
     {
         /* Oh noes */
@@ -343,19 +413,57 @@ IopSetEvent(IN PVOID Context)
 }
 
 /*
- * @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
@@ -364,6 +472,47 @@ IoReportTargetDeviceChangeAsynchronous(IN PDEVICE_OBJECT PhysicalDeviceObject,
                                        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;
 }