NtPlugPlayControl: Implement PLUGPLAY_GET_RELATED_DEVICE and PLUGPLAY_DEVICE_STATUS.
Modified: trunk/reactos/include/ntos/ntpnp.h
Modified: trunk/reactos/ntoskrnl/io/plugplay.c

Modified: trunk/reactos/include/ntos/ntpnp.h
--- trunk/reactos/include/ntos/ntpnp.h	2005-06-06 20:27:49 UTC (rev 15821)
+++ trunk/reactos/include/ntos/ntpnp.h	2005-06-06 20:31:56 UTC (rev 15822)
@@ -38,9 +38,10 @@
 DEFINE_GUID(GUID_DEVICE_HIBERNATE_VETOED, 0x61173AD9, 0x194F, 0x11D3, 0x97, 0xDC, 0x00, 0xA0, 0xC9, 0x40, 0x52, 0x2E);
 DEFINE_GUID(GUID_DEVICE_BATTERY, 0x72631E54, 0x78A4, 0x11D0, 0xBC, 0xF7, 0x00, 0xAA, 0x00, 0xB7, 0xB3, 0x2A);
 DEFINE_GUID(GUID_DEVICE_SAFE_REMOVAL, 0x8FBEF967, 0xD6C5, 0x11D2, 0x97, 0xB5, 0x00, 0xA0, 0xC9, 0x40, 0x52, 0x2E);
-/* These GUIDs are documented, and they are defined in wdmguid.h */
-/* DEFINE_GUID(GUID_DEVICE_INTERFACE_ARRIVAL, 0xCB3A4004, 0x46F0, 0x11D0, 0xB0, 0x8F, 0x00, 0x60, 0x97, 0x13, 0x05, 0x3F); */
-/* DEFINE_GUID(GUID_DEVICE_INTERFACE_REMOVAL, 0xCB3A4005, 0x46F0, 0x11D0, 0xB0, 0x8F, 0x00, 0x60, 0x97, 0x13, 0x05, 0x3F); */
+#ifndef __USE_W32API
+DEFINE_GUID(GUID_DEVICE_INTERFACE_ARRIVAL, 0xCB3A4004, 0x46F0, 0x11D0, 0xB0, 0x8F, 0x00, 0x60, 0x97, 0x13, 0x05, 0x3F);
+DEFINE_GUID(GUID_DEVICE_INTERFACE_REMOVAL, 0xCB3A4005, 0x46F0, 0x11D0, 0xB0, 0x8F, 0x00, 0x60, 0x97, 0x13, 0x05, 0x3F);
+#endif
 DEFINE_GUID(GUID_DEVICE_ARRIVAL, 0xCB3A4009, 0x46F0, 0x11D0, 0xB0, 0x8F, 0x00, 0x60, 0x97, 0x13, 0x05, 0x3F);
 DEFINE_GUID(GUID_DEVICE_ENUMERATED, 0xCB3A400A, 0x46F0, 0x11D0, 0xB0, 0x8F, 0x00, 0x60, 0x97, 0x13, 0x05, 0x3F);
 DEFINE_GUID(GUID_DEVICE_ENUMERATE_REQUEST, 0xCB3A400B, 0x46F0, 0x11D0, 0xB0, 0x8F, 0x00, 0x60, 0x97, 0x13, 0x05, 0x3F);
@@ -220,7 +221,7 @@
  *       0x0B   Device class association (Registration)
  *       0x0C   Get related device
  *       0x0D   Get device interface alias
- *       0x0E   Get/set device status
+ *       0x0E   Get/set/clear device status
  *       0x0F   Get device depth
  *       0x10   Query device relations
  *       0x11   Query target device relation
@@ -245,8 +246,53 @@
  *    ...
  */
 
-#define PLUGPLAY_USER_RESPONSE 0x07
+#define PLUGPLAY_USER_RESPONSE      0x07
+#define PLUGPLAY_GET_PROPERTY       0x0A
+#define PLUGPLAY_GET_RELATED_DEVICE 0x0C
+#define PLUGPLAY_DEVICE_STATUS      0x0E
 
+
+typedef struct _PLUGPLAY_PROPERTY_DATA
+{
+  UNICODE_STRING DeviceInstance;
+  ULONG Property;
+  PVOID Buffer;
+  ULONG BufferSize;
+} PLUGPLAY_PROPERTY_DATA, *PPLUGPLAY_PROPERTY_DATA;
+
+
+/* PLUGPLAY_GET_RELATED_DEVICE (Code 0x0C) */
+
+/* Relation values */
+#define PNP_GET_PARENT_DEVICE  1
+#define PNP_GET_CHILD_DEVICE   2
+#define PNP_GET_SIBLING_DEVICE 3
+
+typedef struct _PLUGPLAY_RELATED_DEVICE_DATA
+{
+  UNICODE_STRING DeviceInstance;
+  UNICODE_STRING RelatedDeviceInstance;
+  ULONG Relation; /* 1: Parent  2: Child  3: Sibling */
+} PLUGPLAY_RELATED_DEVICE_DATA, *PPLUGPLAY_RELATED_DEVICE_DATA;
+
+
+/* PLUGPLAY_DEVICE_STATUS (Code 0x0E) */
+
+/* Action values */
+#define PNP_GET_DEVICE_STATUS    0
+#define PNP_SET_DEVICE_STATUS    1
+#define PNP_CLEAR_DEVICE_STATUS  2
+
+
+typedef struct _PLUGPLAY_DEVICE_STATUS_DATA
+{
+  UNICODE_STRING DeviceInstance;
+  ULONG Action;   /* 0: Get  1: Set  2: Clear */
+  ULONG Problem;  /* CM_PROB_  see cfg.h */
+  ULONG Flags;    /* DN_       see cfg.h */
+} PLUGPLAY_DEVICE_STATUS_DATA, *PPLUGPLAY_DEVICE_STATUS_DATA;
+
+
 NTSTATUS STDCALL
 NtPlugPlayControl(
    ULONG ControlCode,

Modified: trunk/reactos/ntoskrnl/io/plugplay.c
--- trunk/reactos/ntoskrnl/io/plugplay.c	2005-06-06 20:27:49 UTC (rev 15821)
+++ trunk/reactos/ntoskrnl/io/plugplay.c	2005-06-06 20:31:56 UTC (rev 15822)
@@ -1,5 +1,4 @@
-/* $Id$
- *
+/*
  * COPYRIGHT:       See COPYING in the top level directory
  * PROJECT:         ReactOS kernel
  * FILE:            ntoskrnl/io/plugplay.c
@@ -33,14 +32,13 @@
 NTSTATUS INIT_FUNCTION
 IopInitPlugPlayEvents(VOID)
 {
+    InitializeListHead(&IopPnpEventQueueHead);
 
-  InitializeListHead(&IopPnpEventQueueHead);
+    KeInitializeEvent(&IopPnpNotifyEvent,
+                      SynchronizationEvent,
+                      FALSE);
 
-  KeInitializeEvent(&IopPnpNotifyEvent,
-		    SynchronizationEvent,
-		    FALSE);
-
-  return STATUS_SUCCESS;
+    return STATUS_SUCCESS;
 }
 
 
@@ -48,35 +46,35 @@
 IopQueueTargetDeviceEvent(const GUID *Guid,
                           PUNICODE_STRING DeviceIds)
 {
-  PPNP_EVENT_ENTRY EventEntry;
-  DWORD TotalSize;
+    PPNP_EVENT_ENTRY EventEntry;
+    DWORD TotalSize;
 
-  TotalSize =
-    FIELD_OFFSET(PLUGPLAY_EVENT_BLOCK, TargetDevice.DeviceIds) +
-    DeviceIds->MaximumLength;
+    TotalSize =
+        FIELD_OFFSET(PLUGPLAY_EVENT_BLOCK, TargetDevice.DeviceIds) +
+        DeviceIds->MaximumLength;
 
-  EventEntry = ExAllocatePool(NonPagedPool,
-                              TotalSize + FIELD_OFFSET(PNP_EVENT_ENTRY, Event));
-  if (EventEntry == NULL)
-    return STATUS_INSUFFICIENT_RESOURCES;
+    EventEntry = ExAllocatePool(NonPagedPool,
+                                TotalSize + FIELD_OFFSET(PNP_EVENT_ENTRY, Event));
+    if (EventEntry == NULL)
+        return STATUS_INSUFFICIENT_RESOURCES;
 
-  memcpy(&EventEntry->Event.EventGuid,
-         Guid,
-         sizeof(GUID));
-  EventEntry->Event.EventCategory = TargetDeviceChangeEvent;
-  EventEntry->Event.TotalSize = TotalSize;
+    memcpy(&EventEntry->Event.EventGuid,
+           Guid,
+           sizeof(GUID));
+    EventEntry->Event.EventCategory = TargetDeviceChangeEvent;
+    EventEntry->Event.TotalSize = TotalSize;
 
-  memcpy(&EventEntry->Event.TargetDevice.DeviceIds,
-         DeviceIds->Buffer,
-         DeviceIds->MaximumLength);
+    memcpy(&EventEntry->Event.TargetDevice.DeviceIds,
+           DeviceIds->Buffer,
+           DeviceIds->MaximumLength);
 
-  InsertHeadList(&IopPnpEventQueueHead,
-                 &EventEntry->ListEntry);
-  KeSetEvent(&IopPnpNotifyEvent,
-             0,
-             FALSE);
+    InsertHeadList(&IopPnpEventQueueHead,
+                   &EventEntry->ListEntry);
+    KeSetEvent(&IopPnpNotifyEvent,
+               0,
+               FALSE);
 
-  return STATUS_SUCCESS;
+    return STATUS_SUCCESS;
 }
 
 
@@ -84,7 +82,7 @@
  * Remove the current PnP event from the tail of the event queue
  * and signal IopPnpNotifyEvent if there is yet another event in the queue.
  */
-static VOID
+static NTSTATUS
 IopRemovePlugPlayEvent(VOID)
 {
   /* Remove a pnp event entry from the tail of the queue */
@@ -100,6 +98,8 @@
                0,
                FALSE);
   }
+
+  return STATUS_SUCCESS;
 }
 
 
@@ -168,6 +168,256 @@
 }
 
 
+static PDEVICE_OBJECT
+IopGetDeviceObjectFromDeviceInstance(PUNICODE_STRING DeviceInstance)
+{
+    OBJECT_ATTRIBUTES ObjectAttributes;
+    UNICODE_STRING KeyName, ValueName;
+    LPWSTR KeyNameBuffer;
+    HANDLE InstanceKeyHandle;
+    HANDLE ControlKeyHandle;
+    NTSTATUS Status;
+    PKEY_VALUE_PARTIAL_INFORMATION ValueInformation;
+    ULONG ValueInformationLength;
+    PDEVICE_OBJECT DeviceObject = NULL;
+
+    DPRINT("IopGetDeviceObjectFromDeviceInstance(%wZ) called\n", DeviceInstance);
+
+    KeyNameBuffer = ExAllocatePool(PagedPool,
+                                   (49 * sizeof(WCHAR)) + DeviceInstance->Length);
+    if (KeyNameBuffer == NULL)
+    {
+        DPRINT1("Failed to allocate key name buffer!\n");
+        return NULL;
+    }
+
+    wcscpy(KeyNameBuffer, L"\\Registry\\Machine\\System\\CurrentControlSet\\Enum\\");
+    wcscat(KeyNameBuffer, DeviceInstance->Buffer);
+
+    RtlInitUnicodeString(&KeyName,
+                         KeyNameBuffer);
+    InitializeObjectAttributes(&ObjectAttributes,
+                               &KeyName,
+                               OBJ_CASE_INSENSITIVE,
+                               NULL,
+                               NULL);
+
+    Status = ZwOpenKey(&InstanceKeyHandle,
+                       KEY_READ,
+                       &ObjectAttributes);
+    ExFreePool(KeyNameBuffer);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("Failed to open the instance key (Status %lx)\n", Status);
+        return NULL;
+    }
+
+    /* Open the 'Control' subkey */
+    RtlInitUnicodeString(&KeyName,
+                         L"Control");
+    InitializeObjectAttributes(&ObjectAttributes,
+                               &KeyName,
+                               OBJ_CASE_INSENSITIVE,
+                               InstanceKeyHandle,
+                               NULL);
+
+    Status = ZwOpenKey(&ControlKeyHandle,
+                       KEY_READ,
+                       &ObjectAttributes);
+    ZwClose(InstanceKeyHandle);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("Failed to open the 'Control' key (Status %lx)\n", Status);
+        return NULL;
+    }
+
+    /* Query the 'DeviceReference' value */
+    RtlInitUnicodeString(&ValueName,
+                         L"DeviceReference");
+    ValueInformationLength = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION,
+                             Data[0]) + sizeof(ULONG);
+    ValueInformation = ExAllocatePool(PagedPool, ValueInformationLength);
+    if (ValueInformation == NULL)
+    {
+        DPRINT1("Failed to allocate the name information buffer!\n");
+        ZwClose(ControlKeyHandle);
+        return NULL;
+    }
+
+    Status = ZwQueryValueKey(ControlKeyHandle,
+                             &ValueName,
+                             KeyValuePartialInformation,
+                             ValueInformation,
+                             ValueInformationLength,
+                             &ValueInformationLength);
+    ZwClose(ControlKeyHandle);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("Failed to open the 'Control' key (Status %lx)\n", Status);
+        return NULL;
+    }
+
+    /* Check the device object */
+    RtlCopyMemory(&DeviceObject,
+                  ValueInformation->Data,
+                  sizeof(PDEVICE_OBJECT));
+
+    DPRINT("DeviceObject: %p\n", DeviceObject);
+
+    if (DeviceObject->Type != IO_TYPE_DEVICE ||
+        DeviceObject->DeviceObjectExtension == NULL ||
+        DeviceObject->DeviceObjectExtension->DeviceNode == NULL ||
+        !RtlEqualUnicodeString(&DeviceObject->DeviceObjectExtension->DeviceNode->InstancePath,
+                               DeviceInstance, TRUE))
+    {
+        DPRINT1("Invalid object type!\n");
+        return NULL;
+    }
+
+    DPRINT("Instance path: %wZ\n", &DeviceObject->DeviceObjectExtension->DeviceNode->InstancePath);
+
+    ObReferenceObject(DeviceObject);
+
+    DPRINT("IopGetDeviceObjectFromDeviceInstance() done\n");
+
+    return DeviceObject;
+}
+
+
+static NTSTATUS
+IopGetRelatedDevice(PPLUGPLAY_RELATED_DEVICE_DATA RelatedDeviceData)
+{
+    UNICODE_STRING RootDeviceName;
+    PDEVICE_OBJECT DeviceObject = NULL;
+    PDEVICE_NODE DeviceNode = NULL;
+    PDEVICE_NODE RelatedDeviceNode;
+
+    DPRINT("IopGetRelatedDevice() called\n");
+
+    DPRINT("Device name: %wZ\n", &RelatedDeviceData->DeviceInstance);
+
+    RtlInitUnicodeString(&RootDeviceName,
+                         L"HTREE\\ROOT\\0");
+    if (RtlEqualUnicodeString(&RelatedDeviceData->DeviceInstance,
+                              &RootDeviceName,
+                              TRUE))
+    {
+        DeviceNode = IopRootDeviceNode;
+    }
+    else
+    {
+        /* Get the device object */
+        DeviceObject = IopGetDeviceObjectFromDeviceInstance(&RelatedDeviceData->DeviceInstance);
+        if (DeviceObject == NULL)
+            return STATUS_NO_SUCH_DEVICE;
+
+        DeviceNode = DeviceObject->DeviceObjectExtension->DeviceNode;
+    }
+
+    switch (RelatedDeviceData->Relation)
+    {
+        case PNP_GET_PARENT_DEVICE:
+            RelatedDeviceNode = DeviceNode->Parent;
+            break;
+
+        case PNP_GET_CHILD_DEVICE:
+            RelatedDeviceNode = DeviceNode->Child;
+            break;
+
+        case PNP_GET_SIBLING_DEVICE:
+            RelatedDeviceNode = DeviceNode->NextSibling;
+            break;
+
+        default:
+            if (DeviceObject != NULL)
+            {
+                ObDereferenceObject(DeviceObject);
+            }
+
+            return STATUS_INVALID_PARAMETER;
+    }
+
+    if (RelatedDeviceNode == NULL)
+    {
+        if (DeviceObject)
+        {
+            ObDereferenceObject(DeviceObject);
+        }
+
+        return STATUS_NO_SUCH_DEVICE;
+    }
+
+    if (RelatedDeviceNode->InstancePath.Length >
+        RelatedDeviceData->RelatedDeviceInstance.MaximumLength)
+    {
+        if (DeviceObject)
+        {
+            ObDereferenceObject(DeviceObject);
+        }
+
+        return STATUS_BUFFER_TOO_SMALL;
+    }
+
+    /* Copy related device instance name */
+    RtlCopyMemory(RelatedDeviceData->RelatedDeviceInstance.Buffer,
+                  RelatedDeviceNode->InstancePath.Buffer,
+                  RelatedDeviceNode->InstancePath.Length);
+    RelatedDeviceData->RelatedDeviceInstance.Length =
+        RelatedDeviceNode->InstancePath.Length;
+
+    if (DeviceObject != NULL)
+    {
+        ObDereferenceObject(DeviceObject);
+    }
+
+    DPRINT("IopGetRelatedDevice() done\n");
+
+    return STATUS_SUCCESS;
+}
+
+
+static NTSTATUS
+IopDeviceStatus(PPLUGPLAY_DEVICE_STATUS_DATA DeviceStatusData)
+{
+    PDEVICE_OBJECT DeviceObject;
+    PDEVICE_NODE DeviceNode;
+
+    DPRINT("IopDeviceStatus() called\n");
+
+    DPRINT("Device name: %wZ\n", &DeviceStatusData->DeviceInstance);
+
+    /* Get the device object */
+    DeviceObject = IopGetDeviceObjectFromDeviceInstance(&DeviceStatusData->DeviceInstance);
+    if (DeviceObject == NULL)
+        return STATUS_NO_SUCH_DEVICE;
+
+    DeviceNode = DeviceObject->DeviceObjectExtension->DeviceNode;
+
+    switch (DeviceStatusData->Action)
+    {
+        case PNP_GET_DEVICE_STATUS:
+            DPRINT("Get status data\n");
+            DeviceStatusData->Problem = DeviceNode->Problem;
+            DeviceStatusData->Flags = DeviceNode->Flags;
+            break;
+
+        case PNP_SET_DEVICE_STATUS:
+            DPRINT("Set status data\n");
+            DeviceNode->Problem = DeviceStatusData->Problem;
+            DeviceNode->Flags = DeviceStatusData->Flags;
+            break;
+
+        case PNP_CLEAR_DEVICE_STATUS:
+            DPRINT1("FIXME: Clear status data!\n");
+            break;
+    }
+
+    ObDereferenceObject(DeviceObject);
+
+    return STATUS_SUCCESS;
+}
+
+
 /*
  * @unimplemented
  */
@@ -176,17 +426,63 @@
                   IN OUT PVOID Buffer,
                   IN ULONG BufferLength)
 {
-  DPRINT("NtPlugPlayControl(%lu %p %lu) called\n",
-         ControlCode, Buffer, BufferLength);
+    NTSTATUS Status = STATUS_SUCCESS;
 
-  switch (ControlCode)
-  {
-    case PLUGPLAY_USER_RESPONSE:
-      IopRemovePlugPlayEvent();
-      return STATUS_SUCCESS;
-  }
+    DPRINT("NtPlugPlayControl(%lu %p %lu) called\n",
+           ControlCode, Buffer, BufferLength);
 
-  return STATUS_NOT_IMPLEMENTED;
+    /* Function can only be called from user-mode */
+    if (KeGetPreviousMode() != UserMode)
+    {
+        DPRINT1("NtGetPlugPlayEvent cannot be called from kernel mode!\n");
+        return STATUS_ACCESS_DENIED;
+    }
+
+    /* Check for Tcb privilege */
+    if (!SeSinglePrivilegeCheck(SeTcbPrivilege,
+                                UserMode))
+    {
+        DPRINT1("NtGetPlugPlayEvent: Caller does not hold the SeTcbPrivilege privilege!\n");
+        return STATUS_PRIVILEGE_NOT_HELD;
+    }
+
+    /* Probe the buffer */
+    _SEH_TRY
+    {
+        ProbeForWrite(Buffer,
+                      BufferLength,
+                      sizeof(ULONG));
+    }
+    _SEH_HANDLE
+    {
+        Status = _SEH_GetExceptionCode();
+    }
+    _SEH_END;
+
+    if (!NT_SUCCESS(Status))
+    {
+        return Status;
+    }
+
+    switch (ControlCode)
+    {
+        case PLUGPLAY_USER_RESPONSE:
+            if (Buffer || BufferLength != 0)
+                return STATUS_INVALID_PARAMETER;
+            return IopRemovePlugPlayEvent();
+
+        case PLUGPLAY_GET_RELATED_DEVICE:
+            if (!Buffer || BufferLength < sizeof(PLUGPLAY_RELATED_DEVICE_DATA))
+                return STATUS_INVALID_PARAMETER;
+            return IopGetRelatedDevice((PPLUGPLAY_RELATED_DEVICE_DATA)Buffer);
+
+        case PLUGPLAY_DEVICE_STATUS:
+            if (!Buffer || BufferLength < sizeof(PLUGPLAY_DEVICE_STATUS_DATA))
+                return STATUS_INVALID_PARAMETER;
+            return IopDeviceStatus((PPLUGPLAY_DEVICE_STATUS_DATA)Buffer);
+    }
+
+    return STATUS_NOT_IMPLEMENTED;
 }
 
 /* EOF */