[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH v2] Implement device power state transitions.



Implement device power state transition boilerplate so that xenhid can
release its interfaces before the system enters S3. Let hidclass handle
other power types.

Signed-off-by: Troy Crosley <troycrosley@xxxxxxxxx>
---
 src/xenhid/fdo.c             | 290 +++++++++++++++++++++++++++++++++--
 src/xenhid/names.h           | 278 +++++++++++++++++++++++++++++++++
 src/xenhid/thread.c          | 228 +++++++++++++++++++++++++++
 src/xenhid/thread.h          |  75 +++++++++
 vs2015/xenhid/xenhid.vcxproj |   1 +
 vs2017/xenhid/xenhid.vcxproj |   1 +
 vs2019/xenhid/xenhid.vcxproj |   1 +
 7 files changed, 862 insertions(+), 12 deletions(-)
 create mode 100644 src/xenhid/names.h
 create mode 100644 src/xenhid/thread.c
 create mode 100644 src/xenhid/thread.h

diff --git a/src/xenhid/fdo.c b/src/xenhid/fdo.c
index 860c7bc..706fa33 100644
--- a/src/xenhid/fdo.c
+++ b/src/xenhid/fdo.c
@@ -41,10 +41,12 @@
 #include <suspend_interface.h>
 
 #include "fdo.h"
+#include "thread.h"
 #include "driver.h"
 #include "dbg_print.h"
 #include "assert.h"
 #include "util.h"
+#include "names.h"
 #include "string.h"
 
 #define MAXNAMELEN  128
@@ -52,6 +54,9 @@
 struct _XENHID_FDO {
     PDEVICE_OBJECT              DeviceObject;
     PDEVICE_OBJECT              LowerDeviceObject;
+    PXENHID_THREAD              DevicePowerThread;
+    PIRP                        DevicePowerIrp;
+    DEVICE_POWER_STATE          DevicePowerState;
     BOOLEAN                     Enabled;
     XENHID_HID_INTERFACE        HidInterface;
     XENBUS_STORE_INTERFACE      StoreInterface;
@@ -220,6 +225,23 @@ __FdoFree(
     ExFreePoolWithTag(Buffer, FDO_POOL_TAG);
 }
 
+static FORCEINLINE VOID
+__FdoSetDevicePowerState(
+    IN  PXENHID_FDO         Fdo,
+    IN  DEVICE_POWER_STATE  State
+)
+{
+    Fdo->DevicePowerState = State;
+}
+
+static FORCEINLINE DEVICE_POWER_STATE
+__FdoGetDevicePowerState(
+    IN  PXENHID_FDO     Fdo
+)
+{
+    return Fdo->DevicePowerState;
+}
+
 static FORCEINLINE PANSI_STRING
 __FdoMultiSzToUpcaseAnsi(
     IN  PCHAR       Buffer
@@ -588,6 +610,8 @@ FdoD3ToD0(
 {
     NTSTATUS        status;
 
+    ASSERT3U(__FdoGetDevicePowerState(Fdo), ==, PowerDeviceD3);
+
     Trace("=====>\n");
 
     if (Fdo->Enabled)
@@ -621,6 +645,7 @@ FdoD3ToD0(
 
     Fdo->Enabled = TRUE;
 done:
+    __FdoSetDevicePowerState(Fdo, PowerDeviceD0);
     Trace("<=====\n");
     return STATUS_SUCCESS;
 
@@ -659,6 +684,8 @@ FdoD0ToD3(
 {
     Trace("=====>\n");
 
+    __FdoSetDevicePowerState(Fdo, PowerDeviceD3);
+
     if (!Fdo->Enabled)
         goto done;
 
@@ -868,6 +895,222 @@ FdoDispatchPnp(
     return status;
 }
 
+static FORCEINLINE NTSTATUS
+__FdoSetDevicePowerUp(
+    IN  PXENHID_FDO     Fdo,
+    IN  PIRP            Irp
+)
+{
+    PIO_STACK_LOCATION  StackLocation;
+    DEVICE_POWER_STATE  DeviceState;
+    NTSTATUS            status;
+
+    Trace("====>\n");
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+    DeviceState = StackLocation->Parameters.Power.State.DeviceState;
+
+    ASSERT3U(DeviceState, < , __FdoGetDevicePowerState(Fdo));
+
+    status = FdoForwardIrpSynchronously(Fdo, Irp);
+    if (!NT_SUCCESS(status))
+        goto done;
+
+    Info("%s -> %s\n",
+        PowerDeviceStateName(__FdoGetDevicePowerState(Fdo)),
+        PowerDeviceStateName(DeviceState));
+
+    ASSERT3U(DeviceState, ==, PowerDeviceD0);
+    status = FdoD3ToD0(Fdo);
+    ASSERT(NT_SUCCESS(status));
+
+done:
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    Trace("<==== (%08x)\n", status);
+    return status;
+}
+
+static FORCEINLINE NTSTATUS
+__FdoSetDevicePowerDown(
+    IN  PXENHID_FDO     Fdo,
+    IN  PIRP            Irp
+)
+{
+    PIO_STACK_LOCATION  StackLocation;
+    DEVICE_POWER_STATE  DeviceState;
+    NTSTATUS            status;
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+    DeviceState = StackLocation->Parameters.Power.State.DeviceState;
+
+    ASSERT3U(DeviceState, > , __FdoGetDevicePowerState(Fdo));
+
+    Info("%s -> %s\n",
+        PowerDeviceStateName(__FdoGetDevicePowerState(Fdo)),
+        PowerDeviceStateName(DeviceState));
+
+    ASSERT3U(DeviceState, ==, PowerDeviceD3);
+
+    if (__FdoGetDevicePowerState(Fdo) == PowerDeviceD0)
+        FdoD0ToD3(Fdo);
+
+    IoSkipCurrentIrpStackLocation(Irp);
+    status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+
+    return status;
+}
+
+static FORCEINLINE NTSTATUS
+__FdoSetDevicePower(
+    IN  PXENHID_FDO     Fdo,
+    IN  PIRP            Irp
+)
+{
+    PIO_STACK_LOCATION  StackLocation;
+    DEVICE_POWER_STATE  DeviceState;
+    POWER_ACTION        PowerAction;
+    NTSTATUS            status;
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+    DeviceState = StackLocation->Parameters.Power.State.DeviceState;
+    PowerAction = StackLocation->Parameters.Power.ShutdownType;
+
+    Trace("====> (%s:%s)\n",
+        PowerDeviceStateName(DeviceState),
+        PowerActionName(PowerAction));
+
+    ASSERT3U(PowerAction, < , PowerActionShutdown);
+
+    if (DeviceState == __FdoGetDevicePowerState(Fdo)) {
+        IoSkipCurrentIrpStackLocation(Irp);
+        status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+
+        goto done;
+    }
+
+    status = (DeviceState < __FdoGetDevicePowerState(Fdo)) ?
+        __FdoSetDevicePowerUp(Fdo, Irp) :
+        __FdoSetDevicePowerDown(Fdo, Irp);
+
+done:
+    Trace("<==== (%s:%s)(%08x)\n",
+        PowerDeviceStateName(DeviceState),
+        PowerActionName(PowerAction),
+        status);
+    return status;
+}
+
+static NTSTATUS
+FdoDevicePower(
+    IN  PXENHID_THREAD  Self,
+    IN  PVOID           Context
+)
+{
+    PXENHID_FDO         Fdo = (PXENHID_FDO)Context;
+    PKEVENT             Event;
+
+    Event = ThreadGetEvent(Self);
+
+    for (;;) {
+        PIRP                Irp;
+        PIO_STACK_LOCATION  StackLocation;
+        UCHAR               MinorFunction;
+
+        if (Fdo->DevicePowerIrp == NULL) {
+            (VOID)KeWaitForSingleObject(Event,
+                Executive,
+                KernelMode,
+                FALSE,
+                NULL);
+            KeClearEvent(Event);
+        }
+
+        if (ThreadIsAlerted(Self))
+            break;
+
+        Irp = Fdo->DevicePowerIrp;
+
+        if (Irp == NULL)
+            continue;
+
+        Fdo->DevicePowerIrp = NULL;
+        KeMemoryBarrier();
+
+        StackLocation = IoGetCurrentIrpStackLocation(Irp);
+        MinorFunction = StackLocation->MinorFunction;
+
+        switch (StackLocation->MinorFunction) {
+        case IRP_MN_SET_POWER:
+            (VOID)__FdoSetDevicePower(Fdo, Irp);
+            break;
+
+        default:
+            ASSERT(FALSE);
+            break;
+        }
+    }
+
+    return STATUS_SUCCESS;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+FdoDispatchPower(
+    IN  PXENHID_FDO     Fdo,
+    IN  PIRP            Irp
+)
+{
+    PIO_STACK_LOCATION  StackLocation;
+    UCHAR               MinorFunction;
+    POWER_STATE_TYPE    PowerType;
+    POWER_ACTION        PowerAction;
+    NTSTATUS            status;
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+    MinorFunction = StackLocation->MinorFunction;
+
+    if (MinorFunction != IRP_MN_SET_POWER) {
+        IoSkipCurrentIrpStackLocation(Irp);
+        status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+
+        goto done;
+    }
+
+    PowerType = StackLocation->Parameters.Power.Type;
+    PowerAction = StackLocation->Parameters.Power.ShutdownType;
+
+    if (PowerAction >= PowerActionShutdown) {
+        IoSkipCurrentIrpStackLocation(Irp);
+        status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+
+        goto done;
+    }
+
+    switch (PowerType) {
+    case DevicePowerState:
+        IoMarkIrpPending(Irp);
+
+        ASSERT3P(Fdo->DevicePowerIrp, ==, NULL);
+        Fdo->DevicePowerIrp = Irp;
+        KeMemoryBarrier();
+
+        ThreadWake(Fdo->DevicePowerThread);
+
+        status = STATUS_PENDING;
+        break;
+
+    case SystemPowerState:
+    default:
+        IoSkipCurrentIrpStackLocation(Irp);
+        status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+        break;
+    }
+
+done:
+    return status;
+}
+
 static DECLSPEC_NOINLINE NTSTATUS
 FdoDispatchInternal(
     IN  PXENHID_FDO Fdo,
@@ -1031,6 +1274,10 @@ FdoDispatch(
         status = FdoDispatchPnp(Fdo, Irp);
         break;
 
+    case IRP_MJ_POWER:
+        status = FdoDispatchPower(Fdo, Irp);
+        break;
+
     default:
         status = FdoDispatchDefault(Fdo, Irp);
         break;
@@ -1118,6 +1365,11 @@ FdoCreate(
 
     Fdo->DeviceObject = DeviceObject;
     Fdo->LowerDeviceObject = LowerDeviceObject;
+    Fdo->DevicePowerState = PowerDeviceD3;
+
+    status = ThreadCreate(FdoDevicePower, Fdo, &Fdo->DevicePowerThread);
+    if (!NT_SUCCESS(status))
+        goto fail1;
 
     InitializeListHead(&Fdo->List);
     KeInitializeSpinLock(&Fdo->Lock);
@@ -1130,7 +1382,7 @@ FdoCreate(
                              FdoCsqReleaseLock,
                              FdoCsqCompleteCanceledIrp);
     if (!NT_SUCCESS(status))
-        goto fail1;
+        goto fail2;
 
     status = FdoQueryInterface(Fdo,
                                &GUID_XENBUS_SUSPEND_INTERFACE,
@@ -1138,7 +1390,7 @@ FdoCreate(
                                (PINTERFACE)&Fdo->SuspendInterface,
                                sizeof(XENBUS_SUSPEND_INTERFACE));
     if (!NT_SUCCESS(status))
-        goto fail2;
+        goto fail3;
 
     status = FdoQueryInterface(Fdo,
                                &GUID_XENBUS_STORE_INTERFACE,
@@ -1146,7 +1398,7 @@ FdoCreate(
                                (PINTERFACE)&Fdo->StoreInterface,
                                sizeof(XENBUS_STORE_INTERFACE));
     if (!NT_SUCCESS(status))
-        goto fail3;
+        goto fail4;
 
     status = FdoQueryInterface(Fdo,
                                &GUID_XENHID_HID_INTERFACE,
@@ -1154,34 +1406,41 @@ FdoCreate(
                                (PINTERFACE)&Fdo->HidInterface,
                                sizeof(XENHID_HID_INTERFACE));
     if (!NT_SUCCESS(status))
-        goto fail4;
+        goto fail5;
 
     Trace("<=====\n");
     return STATUS_SUCCESS;
 
-fail4:
-    Error("fail4\n");
+fail5:
+    Error("fail5\n");
 
     RtlZeroMemory(&Fdo->StoreInterface,
                   sizeof(XENBUS_STORE_INTERFACE));
 
-fail3:
-    Error("fail3\n");
+fail4:
+    Error("fail4\n");
 
     RtlZeroMemory(&Fdo->SuspendInterface,
                   sizeof(XENBUS_SUSPEND_INTERFACE));
 
-fail2:
-    Error("fail2\n");
+fail3:
+    Error("fail3\n");
 
     RtlZeroMemory(&Fdo->Queue, sizeof(IO_CSQ));
 
-fail1:
-    Error("fail1 %08x\n", status);
+fail2:
+    Error("fail2 %08x\n", status);
+
+    ThreadAlert(Fdo->DevicePowerThread);
+    ThreadJoin(Fdo->DevicePowerThread);
+    Fdo->DevicePowerThread = NULL;
 
     RtlZeroMemory(&Fdo->List, sizeof(LIST_ENTRY));
     RtlZeroMemory(&Fdo->Lock, sizeof(KSPIN_LOCK));
 
+fail1:
+    Error("fail1 %08x\n", status);
+
     Fdo->DeviceObject = NULL;
     Fdo->LowerDeviceObject = NULL;
 
@@ -1202,6 +1461,13 @@ FdoDestroy(
                   sizeof(XENBUS_SUSPEND_INTERFACE));
     RtlZeroMemory(&Fdo->StoreInterface,
                   sizeof(XENBUS_STORE_INTERFACE));
+
+    ThreadAlert(Fdo->DevicePowerThread);
+    ThreadJoin(Fdo->DevicePowerThread);
+    Fdo->DevicePowerThread = NULL;
+    Fdo->DevicePowerIrp = NULL;
+    Fdo->DevicePowerState = 0;
+
     RtlZeroMemory(&Fdo->Queue, sizeof(IO_CSQ));
     RtlZeroMemory(&Fdo->List, sizeof(LIST_ENTRY));
     RtlZeroMemory(&Fdo->Lock, sizeof(KSPIN_LOCK));
diff --git a/src/xenhid/names.h b/src/xenhid/names.h
new file mode 100644
index 0000000..39e0716
--- /dev/null
+++ b/src/xenhid/names.h
@@ -0,0 +1,278 @@
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, 
+ * with or without modification, are permitted provided 
+ * that the following conditions are met:
+ * 
+ * *   Redistributions of source code must retain the above 
+ *     copyright notice, this list of conditions and the 
+ *     following disclaimer.
+ * *   Redistributions in binary form must reproduce the above 
+ *     copyright notice, this list of conditions and the 
+ *     following disclaimer in the documentation and/or other 
+ *     materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
+ * SUCH DAMAGE.
+ */
+
+#ifndef _XENHID_NAMES_H_
+#define _XENHID_NAMES_H_
+
+#include <ntddk.h>
+
+#include "types.h"
+
+static FORCEINLINE const CHAR *
+PowerTypeName(
+    IN  POWER_STATE_TYPE    Type
+    )
+{
+#define _POWER_TYPE_NAME(_Type) \
+        case _Type:             \
+            return #_Type;
+
+    switch (Type) {
+    _POWER_TYPE_NAME(SystemPowerState);
+    _POWER_TYPE_NAME(DevicePowerState);
+    default:
+        break;
+    }
+
+    return ("UNKNOWN");
+#undef  _POWER_ACTION_NAME
+}
+
+static FORCEINLINE const CHAR *
+PowerSystemStateName(
+    IN  SYSTEM_POWER_STATE State
+    )
+{
+#define _POWER_SYSTEM_STATE_NAME(_State)    \
+        case PowerSystem ## _State:         \
+            return #_State;
+
+    switch (State) {
+    _POWER_SYSTEM_STATE_NAME(Unspecified);
+    _POWER_SYSTEM_STATE_NAME(Working);
+    _POWER_SYSTEM_STATE_NAME(Sleeping1);
+    _POWER_SYSTEM_STATE_NAME(Sleeping2);
+    _POWER_SYSTEM_STATE_NAME(Sleeping3);
+    _POWER_SYSTEM_STATE_NAME(Hibernate);
+    _POWER_SYSTEM_STATE_NAME(Shutdown);
+    _POWER_SYSTEM_STATE_NAME(Maximum);
+    default:
+        break;
+    }
+
+    return ("UNKNOWN");
+#undef  _POWER_SYSTEM_STATE_NAME
+}
+
+static FORCEINLINE const CHAR *
+PowerDeviceStateName(
+    IN  DEVICE_POWER_STATE State
+    )
+{
+#define _POWER_DEVICE_STATE_NAME(_State)    \
+        case PowerDevice ## _State:         \
+            return #_State;
+
+    switch (State) {
+    _POWER_DEVICE_STATE_NAME(Unspecified);
+    _POWER_DEVICE_STATE_NAME(D0);
+    _POWER_DEVICE_STATE_NAME(D1);
+    _POWER_DEVICE_STATE_NAME(D2);
+    _POWER_DEVICE_STATE_NAME(D3);
+    _POWER_DEVICE_STATE_NAME(Maximum);
+    default:
+        break;
+    }
+
+    return ("UNKNOWN");
+#undef  _POWER_DEVICE_STATE_NAME
+}
+
+static FORCEINLINE const CHAR *
+PowerActionName(
+    IN  POWER_ACTION    Type
+    )
+{
+#define _POWER_ACTION_NAME(_Type)   \
+        case PowerAction ## _Type:  \
+            return #_Type;
+
+    switch (Type) {
+    _POWER_ACTION_NAME(None);
+    _POWER_ACTION_NAME(Reserved);
+    _POWER_ACTION_NAME(Sleep);
+    _POWER_ACTION_NAME(Hibernate);
+    _POWER_ACTION_NAME(Shutdown);
+    _POWER_ACTION_NAME(ShutdownReset);
+    _POWER_ACTION_NAME(ShutdownOff);
+    _POWER_ACTION_NAME(WarmEject);
+    default:
+        break;
+    }
+
+    return ("UNKNOWN");
+#undef  _POWER_ACTION_NAME
+}
+
+static FORCEINLINE const CHAR *
+PowerMinorFunctionName(
+    IN  ULONG   MinorFunction
+    )
+{
+#define _POWER_MINOR_FUNCTION_NAME(_Function)   \
+    case IRP_MN_ ## _Function:                  \
+        return #_Function;
+
+    switch (MinorFunction) {
+    _POWER_MINOR_FUNCTION_NAME(WAIT_WAKE);
+    _POWER_MINOR_FUNCTION_NAME(POWER_SEQUENCE);
+    _POWER_MINOR_FUNCTION_NAME(SET_POWER);
+    _POWER_MINOR_FUNCTION_NAME(QUERY_POWER);
+
+    default:
+        return "UNKNOWN";
+    }
+
+#undef  _POWER_MINOR_FUNCTION_NAME
+}
+
+static FORCEINLINE const CHAR *
+PnpDeviceStateName(
+    IN  DEVICE_PNP_STATE    State
+    )
+{
+#define _PNP_DEVICE_STATE_NAME(_State) \
+    case  _State:               \
+        return #_State;
+
+    switch (State) {
+    _PNP_DEVICE_STATE_NAME(Invalid);
+    _PNP_DEVICE_STATE_NAME(Added);
+    _PNP_DEVICE_STATE_NAME(Started);
+    _PNP_DEVICE_STATE_NAME(StopPending);
+    _PNP_DEVICE_STATE_NAME(Stopped);
+    _PNP_DEVICE_STATE_NAME(RemovePending);
+    _PNP_DEVICE_STATE_NAME(SurpriseRemovePending);
+    _PNP_DEVICE_STATE_NAME(Deleted);
+    default:
+        break;
+    }
+
+    return "UNKNOWN";
+
+#undef  _STATE_NAME
+}
+
+static FORCEINLINE const CHAR *
+PnpMinorFunctionName(
+    IN  ULONG   Function
+    )
+{
+#define _PNP_MINOR_FUNCTION_NAME(_Function) \
+    case IRP_MN_ ## _Function:              \
+        return #_Function;
+
+    switch (Function) {
+    _PNP_MINOR_FUNCTION_NAME(START_DEVICE);
+    _PNP_MINOR_FUNCTION_NAME(QUERY_REMOVE_DEVICE);
+    _PNP_MINOR_FUNCTION_NAME(REMOVE_DEVICE);
+    _PNP_MINOR_FUNCTION_NAME(CANCEL_REMOVE_DEVICE);
+    _PNP_MINOR_FUNCTION_NAME(STOP_DEVICE);
+    _PNP_MINOR_FUNCTION_NAME(QUERY_STOP_DEVICE);
+    _PNP_MINOR_FUNCTION_NAME(CANCEL_STOP_DEVICE);
+    _PNP_MINOR_FUNCTION_NAME(QUERY_DEVICE_RELATIONS);
+    _PNP_MINOR_FUNCTION_NAME(QUERY_INTERFACE);
+    _PNP_MINOR_FUNCTION_NAME(QUERY_CAPABILITIES);
+    _PNP_MINOR_FUNCTION_NAME(QUERY_RESOURCES);
+    _PNP_MINOR_FUNCTION_NAME(QUERY_RESOURCE_REQUIREMENTS);
+    _PNP_MINOR_FUNCTION_NAME(QUERY_DEVICE_TEXT);
+    _PNP_MINOR_FUNCTION_NAME(FILTER_RESOURCE_REQUIREMENTS);
+    _PNP_MINOR_FUNCTION_NAME(READ_CONFIG);
+    _PNP_MINOR_FUNCTION_NAME(WRITE_CONFIG);
+    _PNP_MINOR_FUNCTION_NAME(EJECT);
+    _PNP_MINOR_FUNCTION_NAME(SET_LOCK);
+    _PNP_MINOR_FUNCTION_NAME(QUERY_ID);
+    _PNP_MINOR_FUNCTION_NAME(QUERY_PNP_DEVICE_STATE);
+    _PNP_MINOR_FUNCTION_NAME(QUERY_BUS_INFORMATION);
+    _PNP_MINOR_FUNCTION_NAME(DEVICE_USAGE_NOTIFICATION);
+    _PNP_MINOR_FUNCTION_NAME(SURPRISE_REMOVAL);
+    _PNP_MINOR_FUNCTION_NAME(QUERY_LEGACY_BUS_INFORMATION);
+    default:
+        break;
+    }
+
+    return "UNKNOWN";
+
+#undef  _PNP_MINOR_FUNCTION_NAME
+}
+
+static FORCEINLINE const CHAR *
+PartialResourceDescriptorTypeName(
+    IN  UCHAR   Type
+    )
+{
+#define _PARTIAL_RESOURCE_DESCRIPTOR_TYPE_NAME(_Type)   \
+    case CmResourceType ## _Type:                       \
+        return #_Type;
+
+    switch (Type) {
+    _PARTIAL_RESOURCE_DESCRIPTOR_TYPE_NAME(Null);
+    _PARTIAL_RESOURCE_DESCRIPTOR_TYPE_NAME(Port);
+    _PARTIAL_RESOURCE_DESCRIPTOR_TYPE_NAME(Interrupt);
+    _PARTIAL_RESOURCE_DESCRIPTOR_TYPE_NAME(Memory);
+    _PARTIAL_RESOURCE_DESCRIPTOR_TYPE_NAME(Dma);
+    _PARTIAL_RESOURCE_DESCRIPTOR_TYPE_NAME(DeviceSpecific);
+    _PARTIAL_RESOURCE_DESCRIPTOR_TYPE_NAME(BusNumber);
+    _PARTIAL_RESOURCE_DESCRIPTOR_TYPE_NAME(MemoryLarge);
+    _PARTIAL_RESOURCE_DESCRIPTOR_TYPE_NAME(ConfigData);
+    _PARTIAL_RESOURCE_DESCRIPTOR_TYPE_NAME(DevicePrivate);
+    default:
+        break;
+    }
+
+    return "UNKNOWN";
+
+#undef  _PARTIAL_RESOURCE_DESCRIPTOR_TYPE_NAME
+}
+
+static FORCEINLINE const CHAR *
+DeviceUsageTypeName(
+    IN  DEVICE_USAGE_NOTIFICATION_TYPE  Type
+    )
+{
+#define _DEVICE_USAGE_TYPE_NAME(_Type)  \
+    case DeviceUsageType ## _Type:      \
+        return #_Type;
+
+    switch (Type) {
+    _DEVICE_USAGE_TYPE_NAME(Paging);
+    _DEVICE_USAGE_TYPE_NAME(Hibernation);
+    _DEVICE_USAGE_TYPE_NAME(DumpFile);
+    default:
+        break;
+    }
+
+    return "UNKNOWN";
+
+#undef  _DEVICE_USAGE_TYPE_NAME
+}
+
+#endif // _XENHID_NAMES_H_
diff --git a/src/xenhid/thread.c b/src/xenhid/thread.c
new file mode 100644
index 0000000..cdfb976
--- /dev/null
+++ b/src/xenhid/thread.c
@@ -0,0 +1,228 @@
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, 
+ * with or without modification, are permitted provided 
+ * that the following conditions are met:
+ * 
+ * *   Redistributions of source code must retain the above 
+ *     copyright notice, this list of conditions and the 
+ *     following disclaimer.
+ * *   Redistributions in binary form must reproduce the above 
+ *     copyright notice, this list of conditions and the 
+ *     following disclaimer in the documentation and/or other 
+ *     materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
+ * SUCH DAMAGE.
+ */
+
+#include <ntddk.h>
+
+#include "thread.h"
+#include "util.h"
+#include "dbg_print.h"
+#include "assert.h"
+
+#define THREAD_POOL 'ERHT'
+
+struct _XENHID_THREAD {
+    XENHID_THREAD_FUNCTION  Function;
+    PVOID                   Context;
+    KEVENT                  Event;
+    BOOLEAN                 Alerted;
+    LONG                    References;
+    PKTHREAD                Thread;
+};
+
+static FORCEINLINE PVOID
+__ThreadAllocate(
+    IN  ULONG   Length
+    )
+{
+    return __AllocatePoolWithTag(NonPagedPool, Length, THREAD_POOL);
+}
+
+static FORCEINLINE VOID
+__ThreadFree(
+    IN  PVOID   Buffer
+    )
+{
+    __FreePoolWithTag(Buffer, THREAD_POOL);
+}
+
+static FORCEINLINE VOID
+__ThreadWake(
+    IN  PXENHID_THREAD  Thread
+    )
+{
+    KeSetEvent(&Thread->Event, IO_NO_INCREMENT, FALSE);
+}
+
+VOID
+ThreadWake(
+    IN  PXENHID_THREAD  Thread
+    )
+{
+    __ThreadWake(Thread);
+}
+
+static FORCEINLINE VOID
+__ThreadAlert(
+    IN  PXENHID_THREAD  Thread
+    )
+{
+    Thread->Alerted = TRUE;
+    __ThreadWake(Thread);
+}
+
+VOID
+ThreadAlert(
+    IN  PXENHID_THREAD  Thread
+    )
+{
+    __ThreadAlert(Thread);
+}
+
+KSTART_ROUTINE  ThreadFunction;
+
+VOID
+ThreadFunction(
+    IN  PVOID       Argument
+    )
+{
+    PXENHID_THREAD  Self = Argument;
+    NTSTATUS        status;
+
+    status = Self->Function(Self, Self->Context);
+
+    if (InterlockedDecrement(&Self->References) == 0)
+        __ThreadFree(Self);
+
+    PsTerminateSystemThread(status);
+    // NOT REACHED
+}
+
+__drv_requiresIRQL(PASSIVE_LEVEL)
+NTSTATUS
+ThreadCreate(
+    IN  XENHID_THREAD_FUNCTION  Function,
+    IN  PVOID                   Context,
+    OUT PXENHID_THREAD          *Thread
+    )
+{
+    HANDLE                      Handle;
+    NTSTATUS                    status;
+
+    ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+
+    (*Thread) = __ThreadAllocate(sizeof (XENHID_THREAD));
+
+    Warning("Create thread %p %p\n", Function, Thread);
+
+    status = STATUS_NO_MEMORY;
+    if (*Thread == NULL)
+        goto fail1;
+
+    (*Thread)->Function = Function;
+    (*Thread)->Context = Context;
+    (*Thread)->Alerted = FALSE;
+    (*Thread)->References = 2; // One for us, one for the thread function
+
+    KeInitializeEvent(&(*Thread)->Event, NotificationEvent, FALSE);
+
+    status = PsCreateSystemThread(&Handle,
+                                  STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL,
+                                  NULL,
+                                  NULL,
+                                  NULL,
+                                  ThreadFunction,
+                                  *Thread);
+    if (!NT_SUCCESS(status)) {
+        --(*Thread)->References;    // Fake thread function termination
+        goto fail2;
+    }
+
+    status = ObReferenceObjectByHandle(Handle,
+                                       SYNCHRONIZE,
+                                       *PsThreadType,
+                                       KernelMode,
+                                       &(*Thread)->Thread,
+                                       NULL);
+    if (!NT_SUCCESS(status))
+        goto fail3;
+
+    ZwClose(Handle);
+
+    return STATUS_SUCCESS;
+
+fail3:
+    Error("fail3\n");
+
+    __ThreadAlert(*Thread);
+    ZwClose(Handle);
+
+fail2:
+    Error("fail2\n");
+
+    if (InterlockedDecrement(&(*Thread)->References) == 0)
+        __ThreadFree(*Thread);
+
+    *Thread = NULL;
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+PKEVENT
+ThreadGetEvent(
+    IN  PXENHID_THREAD  Thread
+    )
+{
+    return &Thread->Event;
+}
+
+BOOLEAN
+ThreadIsAlerted(
+    IN  PXENHID_THREAD  Thread
+    )
+{
+    return Thread->Alerted;
+}
+
+VOID
+ThreadJoin(
+    IN  PXENHID_THREAD  Thread
+    )
+{
+    LONG                References;
+
+    ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+    ASSERT3P(KeGetCurrentThread(), !=, Thread->Thread);
+
+    (VOID) KeWaitForSingleObject(Thread->Thread,
+                                 Executive,
+                                 KernelMode,
+                                 FALSE,
+                                 NULL);
+
+    References = InterlockedDecrement(&Thread->References);
+    ASSERT3U(References, ==, 0);
+
+    __ThreadFree(Thread);
+}
+
diff --git a/src/xenhid/thread.h b/src/xenhid/thread.h
new file mode 100644
index 0000000..30369c0
--- /dev/null
+++ b/src/xenhid/thread.h
@@ -0,0 +1,75 @@
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, 
+ * with or without modification, are permitted provided 
+ * that the following conditions are met:
+ * 
+ * *   Redistributions of source code must retain the above 
+ *     copyright notice, this list of conditions and the 
+ *     following disclaimer.
+ * *   Redistributions in binary form must reproduce the above 
+ *     copyright notice, this list of conditions and the 
+ *     following disclaimer in the documentation and/or other 
+ *     materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
+ * SUCH DAMAGE.
+ */
+
+#ifndef _XENHID_THREAD_H
+#define _XENHID_THREAD_H
+
+#include <ntddk.h>
+
+typedef struct _XENHID_THREAD XENHID_THREAD, *PXENHID_THREAD;
+
+typedef NTSTATUS (*XENHID_THREAD_FUNCTION)(PXENHID_THREAD, PVOID);
+
+__drv_requiresIRQL(PASSIVE_LEVEL)
+extern NTSTATUS
+ThreadCreate(
+    IN  XENHID_THREAD_FUNCTION  Function,
+    IN  PVOID                   Context,
+    OUT PXENHID_THREAD          *Thread
+    );
+
+extern PKEVENT
+ThreadGetEvent(
+    IN  PXENHID_THREAD  Self
+    );
+
+extern BOOLEAN
+ThreadIsAlerted(
+    IN  PXENHID_THREAD  Self
+    );
+
+extern VOID
+ThreadWake(
+    IN  PXENHID_THREAD  Thread
+    );
+
+extern VOID
+ThreadAlert(
+    IN  PXENHID_THREAD  Thread
+    );
+
+extern VOID
+ThreadJoin(
+    IN  PXENHID_THREAD  Thread
+    );
+
+#endif  // _XENHID_THREAD_H
+
diff --git a/vs2015/xenhid/xenhid.vcxproj b/vs2015/xenhid/xenhid.vcxproj
index 35ba30f..e15b581 100644
--- a/vs2015/xenhid/xenhid.vcxproj
+++ b/vs2015/xenhid/xenhid.vcxproj
@@ -53,6 +53,7 @@
   <ItemGroup>
     <ClCompile Include="../../src/xenhid/driver.c" />
     <ClCompile Include="../../src/xenhid/fdo.c" />
+    <ClCompile Include="../../src/xenhid/thread.c" />
     <ClCompile Include="../../src/xenhid/string.c" />
   </ItemGroup>
   <ItemGroup>
diff --git a/vs2017/xenhid/xenhid.vcxproj b/vs2017/xenhid/xenhid.vcxproj
index a113120..3ecd8d9 100644
--- a/vs2017/xenhid/xenhid.vcxproj
+++ b/vs2017/xenhid/xenhid.vcxproj
@@ -61,6 +61,7 @@
   <ItemGroup>
     <ClCompile Include="../../src/xenhid/driver.c" />
     <ClCompile Include="../../src/xenhid/fdo.c" />
+    <ClCompile Include="../../src/xenhid/thread.c" />
     <ClCompile Include="../../src/xenhid/string.c" />
   </ItemGroup>
   <ItemGroup>
diff --git a/vs2019/xenhid/xenhid.vcxproj b/vs2019/xenhid/xenhid.vcxproj
index 996df2c..4573a59 100644
--- a/vs2019/xenhid/xenhid.vcxproj
+++ b/vs2019/xenhid/xenhid.vcxproj
@@ -61,6 +61,7 @@
   <ItemGroup>
     <ClCompile Include="../../src/xenhid/driver.c" />
     <ClCompile Include="../../src/xenhid/fdo.c" />
+    <ClCompile Include="../../src/xenhid/thread.c" />
     <ClCompile Include="../../src/xenhid/string.c" />
   </ItemGroup>
   <ItemGroup>
-- 
2.20.1




 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.