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

[win-pv-devel] [PATCH 03/14 v2] Add boilerplate Pdo



From: Owen Smith <owen.smith@xxxxxxxxxx>

Enumerates a single static RawPdo.

Signed-off-by: Owen Smith <owen.smith@xxxxxxxxxx>
---
 src/xencons/driver.c           |    8 +
 src/xencons/driver.h           |   12 +-
 src/xencons/fdo.c              |  426 ++++++++-
 src/xencons/fdo.h              |   43 +
 src/xencons/mutex.h            |   82 ++
 src/xencons/pdo.c              | 1865 ++++++++++++++++++++++++++++++++++++++++
 src/xencons/pdo.h              |  113 +++
 vs2015/xencons/xencons.vcxproj |    1 +
 vs2017/xencons/xencons.vcxproj |    1 +
 9 files changed, 2541 insertions(+), 10 deletions(-)
 create mode 100755 src/xencons/mutex.h
 create mode 100755 src/xencons/pdo.c
 create mode 100755 src/xencons/pdo.h

diff --git a/src/xencons/driver.c b/src/xencons/driver.c
index 3121b99..5a4d1d3 100644
--- a/src/xencons/driver.c
+++ b/src/xencons/driver.c
@@ -36,6 +36,7 @@
 
 #include "registry.h"
 #include "fdo.h"
+#include "pdo.h"
 #include "driver.h"
 #include "dbg_print.h"
 #include "assert.h"
@@ -208,6 +209,13 @@ Dispatch(
         status = FdoDispatch(Fdo, Irp);
         break;
     }
+    case PHYSICAL_DEVICE_OBJECT:
+    {
+        PXENCONS_PDO Pdo = Dx->Pdo;
+
+        status = PdoDispatch(Pdo, Irp);
+        break;
+    }
     default:
         ASSERT(FALSE);
         break;
diff --git a/src/xencons/driver.h b/src/xencons/driver.h
index 4a2eb61..fb5ece0 100644
--- a/src/xencons/driver.h
+++ b/src/xencons/driver.h
@@ -50,12 +50,17 @@ DriverGetParametersKey(
     );
 
 typedef struct _XENCONS_FDO XENCONS_FDO, *PXENCONS_FDO;
+typedef struct _XENCONS_PDO XENCONS_PDO, *PXENCONS_PDO;
 
 #include "fdo.h"
+#include "pdo.h"
 
 #define MAX_DEVICE_ID_LEN   200
 #define MAX_GUID_STRING_LEN 39
 
+#pragma warning(push)
+#pragma warning(disable:4201) // nonstandard extension used : nameless 
struct/union
+
 typedef struct _XENCONS_DX {
     PDEVICE_OBJECT      DeviceObject;
     DEVICE_OBJECT_TYPE  Type;
@@ -72,7 +77,12 @@ typedef struct _XENCONS_DX {
 
     LIST_ENTRY          ListEntry;
 
-    PXENCONS_FDO        Fdo;
+    union {
+        PXENCONS_FDO    Fdo;
+        PXENCONS_PDO    Pdo;
+    };
 } XENCONS_DX, *PXENCONS_DX;
 
+#pragma warning(pop)
+
 #endif  // _XENCONS_DRIVER_H
diff --git a/src/xencons/fdo.c b/src/xencons/fdo.c
index 0170ef6..1c5323c 100644
--- a/src/xencons/fdo.c
+++ b/src/xencons/fdo.c
@@ -47,6 +47,8 @@
 #include "driver.h"
 #include "registry.h"
 #include "fdo.h"
+#include "pdo.h"
+#include "mutex.h"
 #include "console.h"
 #include "thread.h"
 #include "names.h"
@@ -85,6 +87,9 @@ struct _XENCONS_FDO {
 
     CHAR                        VendorName[MAXNAMELEN];
 
+    MUTEX                       Mutex;
+    ULONG                       References;
+
     FDO_RESOURCE                Resource[RESOURCE_COUNT];
 
     PXENCONS_CONSOLE            Console;
@@ -210,6 +215,14 @@ __FdoGetPhysicalDeviceObject(
     return Fdo->PhysicalDeviceObject;
 }
 
+PDEVICE_OBJECT
+FdoGetPhysicalDeviceObject(
+    IN  PXENCONS_FDO    Fdo
+    )
+{
+    return __FdoGetPhysicalDeviceObject(Fdo);
+}
+
 __drv_requiresIRQL(PASSIVE_LEVEL)
 static FORCEINLINE NTSTATUS
 __FdoAcquireLowerBusInterface(
@@ -358,6 +371,14 @@ __FdoGetVendorName(
     return Fdo->VendorName;
 }
 
+PCHAR
+FdoGetVendorName(
+    IN  PXENCONS_FDO    Fdo
+    )
+{
+    return __FdoGetVendorName(Fdo);
+}
+
 static FORCEINLINE VOID
 __FdoSetName(
     IN  PXENCONS_FDO    Fdo
@@ -383,6 +404,99 @@ __FdoGetName(
     return Dx->Name;
 }
 
+PCHAR
+FdoGetName(
+    IN  PXENCONS_FDO    Fdo
+    )
+{
+    return __FdoGetName(Fdo);
+}
+
+__drv_functionClass(IO_COMPLETION_ROUTINE)
+__drv_sameIRQL
+static NTSTATUS
+__FdoDelegateIrp(
+    IN  PDEVICE_OBJECT  DeviceObject,
+    IN  PIRP            Irp,
+    IN  PVOID           Context
+    )
+{
+    PKEVENT             Event = Context;
+
+    UNREFERENCED_PARAMETER(DeviceObject);
+    UNREFERENCED_PARAMETER(Irp);
+
+    KeSetEvent(Event, IO_NO_INCREMENT, FALSE);
+
+    return STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+NTSTATUS
+FdoDelegateIrp(
+    IN  PXENCONS_FDO    Fdo,
+    IN  PIRP            Irp
+    )
+{
+    PDEVICE_OBJECT      DeviceObject;
+    PIO_STACK_LOCATION  StackLocation;
+    PIRP                SubIrp;
+    KEVENT              Event;
+    PIO_STACK_LOCATION  SubStackLocation;
+    NTSTATUS            status;
+
+    ASSERT3U(KeGetCurrentIrql(), == , PASSIVE_LEVEL);
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+
+    // Find the top of the FDO stack and hold a reference
+    DeviceObject = IoGetAttachedDeviceReference(Fdo->Dx->DeviceObject);
+
+    // Get a new IRP for the FDO stack
+    SubIrp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
+
+    status = STATUS_NO_MEMORY;
+    if (SubIrp == NULL)
+        goto done;
+
+    // Copy in the information from the original IRP
+    SubStackLocation = IoGetNextIrpStackLocation(SubIrp);
+
+    KeInitializeEvent(&Event, NotificationEvent, FALSE);
+
+    RtlCopyMemory(SubStackLocation, StackLocation,
+                  FIELD_OFFSET(IO_STACK_LOCATION, CompletionRoutine));
+    SubStackLocation->Control = 0;
+
+    IoSetCompletionRoutine(SubIrp,
+                           __FdoDelegateIrp,
+                           &Event,
+                           TRUE,
+                           TRUE,
+                           TRUE);
+
+    // Default completion status
+    SubIrp->IoStatus.Status = Irp->IoStatus.Status;
+
+    status = IoCallDriver(DeviceObject, SubIrp);
+    if (status == STATUS_PENDING) {
+        (VOID)KeWaitForSingleObject(&Event,
+                                    Executive,
+                                    KernelMode,
+                                    FALSE,
+                                    NULL);
+        status = SubIrp->IoStatus.Status;
+    } else {
+        ASSERT3U(status, == , SubIrp->IoStatus.Status);
+    }
+
+    IoFreeIrp(SubIrp);
+
+done:
+    ObDereferenceObject(DeviceObject);
+
+    return status;
+}
+
 __drv_functionClass(IO_COMPLETION_ROUTINE)
 __drv_sameIRQL
 static NTSTATUS
@@ -487,6 +601,99 @@ FdoParseResources(
     }
 }
 
+NTSTATUS
+FdoAddPhysicalDeviceObject(
+    IN  PXENCONS_FDO    Fdo,
+    IN  PXENCONS_PDO    Pdo
+    )
+{
+    PDEVICE_OBJECT      DeviceObject;
+    PXENCONS_DX         Dx;
+    NTSTATUS            status;
+
+    DeviceObject = PdoGetDeviceObject(Pdo);
+    Dx = (PXENCONS_DX)DeviceObject->DeviceExtension;
+    ASSERT3U(Dx->Type, == , PHYSICAL_DEVICE_OBJECT);
+
+    if (__FdoGetDevicePowerState(Fdo) == PowerDeviceD3)
+        goto done;
+
+    status = PdoResume(Pdo);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+done:
+    InsertTailList(&Fdo->Dx->ListEntry, &Dx->ListEntry);
+    ASSERT3U(Fdo->References, != , 0);
+    Fdo->References++;
+
+    return STATUS_SUCCESS;
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+VOID
+FdoRemovePhysicalDeviceObject(
+    IN  PXENCONS_FDO    Fdo,
+    IN  PXENCONS_PDO    Pdo
+    )
+{
+    PDEVICE_OBJECT      DeviceObject;
+    PXENCONS_DX         Dx;
+
+    DeviceObject = PdoGetDeviceObject(Pdo);
+    Dx = (PXENCONS_DX)DeviceObject->DeviceExtension;
+    ASSERT3U(Dx->Type, == , PHYSICAL_DEVICE_OBJECT);
+
+    if (__FdoGetDevicePowerState(Fdo) == PowerDeviceD3)
+        goto done;
+
+    PdoSuspend(Pdo);
+
+done:
+    RemoveEntryList(&Dx->ListEntry);
+    ASSERT3U(Fdo->References, != , 0);
+    --Fdo->References;
+}
+
+static FORCEINLINE VOID
+__FdoAcquireMutex(
+    IN  PXENCONS_FDO    Fdo
+    )
+{
+    AcquireMutex(&Fdo->Mutex);
+}
+
+VOID
+FdoAcquireMutex(
+    IN  PXENCONS_FDO    Fdo
+    )
+{
+    __FdoAcquireMutex(Fdo);
+}
+
+static FORCEINLINE VOID
+__FdoReleaseMutex(
+    IN  PXENCONS_FDO    Fdo
+    )
+{
+    ReleaseMutex(&Fdo->Mutex);
+}
+
+VOID
+FdoReleaseMutex(
+    IN  PXENCONS_FDO    Fdo
+    )
+{
+    __FdoReleaseMutex(Fdo);
+
+    if (Fdo->References == 0)
+        FdoDestroy(Fdo);
+}
+
 static FORCEINLINE PANSI_STRING
 __FdoMultiSzToUpcaseAnsi(
     IN  PCHAR       Buffer
@@ -831,9 +1038,9 @@ FdoD3ToD0(
     IN  PXENCONS_FDO    Fdo
     )
 {
-    PXENCONS_DX         Dx = Fdo->Dx;
     POWER_STATE         PowerState;
     KIRQL               Irql;
+    PLIST_ENTRY         ListEntry;
     NTSTATUS            status;
 
     ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
@@ -873,11 +1080,27 @@ FdoD3ToD0(
                     DevicePowerState,
                     PowerState);
 
+    __FdoAcquireMutex(Fdo);
+
+    for (ListEntry = Fdo->Dx->ListEntry.Flink;
+         ListEntry != &Fdo->Dx->ListEntry;
+         ListEntry = ListEntry->Flink) {
+        PXENCONS_DX  Dx = CONTAINING_RECORD(ListEntry, XENCONS_DX, ListEntry);
+        PXENCONS_PDO Pdo = Dx->Pdo;
+
+        ASSERT3U(Dx->Type, == , PHYSICAL_DEVICE_OBJECT);
+
+        status = PdoResume(Pdo);
+        ASSERT(NT_SUCCESS(status));
+    }
+
+    __FdoReleaseMutex(Fdo);
+
     status = ConsoleD3ToD0(Fdo->Console);
     ASSERT(NT_SUCCESS(status));
 
 #pragma prefast(suppress:28123)
-    (VOID) IoSetDeviceInterfaceState(&Dx->Link, TRUE);
+    (VOID) IoSetDeviceInterfaceState(&Fdo->Dx->Link, TRUE);
 
     Trace("<====\n");
 
@@ -912,8 +1135,8 @@ FdoD0ToD3(
     IN  PXENCONS_FDO    Fdo
     )
 {
-    PXENCONS_DX         Dx = Fdo->Dx;
     POWER_STATE         PowerState;
+    PLIST_ENTRY         ListEntry;
     KIRQL               Irql;
 
     ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
@@ -922,10 +1145,29 @@ FdoD0ToD3(
     Trace("====>\n");
 
 #pragma prefast(suppress:28123)
-    (VOID) IoSetDeviceInterfaceState(&Dx->Link, FALSE);
+    (VOID) IoSetDeviceInterfaceState(&Fdo->Dx->Link, FALSE);
 
     ConsoleD0ToD3(Fdo->Console);
 
+    __FdoAcquireMutex(Fdo);
+
+    for (ListEntry = Fdo->Dx->ListEntry.Flink;
+         ListEntry != &Fdo->Dx->ListEntry;
+         ListEntry = ListEntry->Flink) {
+        PXENCONS_DX  Dx = CONTAINING_RECORD(ListEntry, XENCONS_DX, ListEntry);
+        PXENCONS_PDO Pdo = Dx->Pdo;
+
+        ASSERT3U(Dx->Type, == , PHYSICAL_DEVICE_OBJECT);
+
+        if (PdoGetDevicePnpState(Pdo) == Deleted ||
+            PdoIsMissing(Pdo))
+            continue;
+
+        PdoSuspend(Pdo);
+    }
+
+    __FdoReleaseMutex(Fdo);
+
     PowerState.DeviceState = PowerDeviceD3;
     PoSetPowerState(Fdo->Dx->DeviceObject,
                     DevicePowerState,
@@ -1117,10 +1359,27 @@ FdoSurpriseRemoval(
     IN  PIRP            Irp
     )
 {
+    PLIST_ENTRY         ListEntry;
     NTSTATUS            status;
 
     __FdoSetDevicePnpState(Fdo, SurpriseRemovePending);
 
+    __FdoAcquireMutex(Fdo);
+
+    for (ListEntry = Fdo->Dx->ListEntry.Flink;
+         ListEntry != &Fdo->Dx->ListEntry;
+         ListEntry = ListEntry->Flink) {
+        PXENCONS_DX  Dx = CONTAINING_RECORD(ListEntry, XENCONS_DX, ListEntry);
+        PXENCONS_PDO Pdo = Dx->Pdo;
+
+        ASSERT3U(Dx->Type, == , PHYSICAL_DEVICE_OBJECT);
+
+        if (!PdoIsMissing(Pdo))
+            PdoSetMissing(Pdo, "FDO surprise removed");
+    }
+
+    __FdoReleaseMutex(Fdo);
+
     Irp->IoStatus.Status = STATUS_SUCCESS;
 
     IoSkipCurrentIrpStackLocation(Irp);
@@ -1135,6 +1394,7 @@ FdoRemoveDevice(
     IN  PIRP            Irp
     )
 {
+    PLIST_ENTRY         ListEntry;
     NTSTATUS            status;
 
     ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
@@ -1142,6 +1402,30 @@ FdoRemoveDevice(
     if (__FdoGetPreviousDevicePnpState(Fdo) != Started)
         goto done;
 
+    __FdoAcquireMutex(Fdo);
+
+    ListEntry = Fdo->Dx->ListEntry.Flink;
+    while (ListEntry != &Fdo->Dx->ListEntry) {
+        PLIST_ENTRY Flink = ListEntry->Flink;
+        PXENCONS_DX  Dx = CONTAINING_RECORD(ListEntry, XENCONS_DX, ListEntry);
+        PXENCONS_PDO Pdo = Dx->Pdo;
+
+        ASSERT3U(Dx->Type, == , PHYSICAL_DEVICE_OBJECT);
+
+        if (!PdoIsMissing(Pdo))
+            PdoSetMissing(Pdo, "FDO removed");
+
+        if (PdoGetDevicePnpState(Pdo) != SurpriseRemovePending)
+            PdoSetDevicePnpState(Pdo, Deleted);
+
+        if (PdoGetDevicePnpState(Pdo) == Deleted)
+            PdoDestroy(Pdo);
+
+        ListEntry = Flink;
+    }
+
+    __FdoReleaseMutex(Fdo);
+
     if (__FdoGetDevicePowerState(Fdo) == PowerDeviceD0)
         FdoD0ToD3(Fdo);
 
@@ -1157,7 +1441,13 @@ done:
     IoSkipCurrentIrpStackLocation(Irp);
     status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
 
-    FdoDestroy(Fdo);
+    __FdoAcquireMutex(Fdo);
+    ASSERT3U(Fdo->References, != , 0);
+    --Fdo->References;
+    __FdoReleaseMutex(Fdo);
+
+    if (Fdo->References == 0)
+        FdoDestroy(Fdo);
 
     return status;
 }
@@ -1169,16 +1459,110 @@ FdoQueryDeviceRelations(
     )
 {
     PIO_STACK_LOCATION  StackLocation;
+    ULONG               Size;
+    PDEVICE_RELATIONS   Relations;
+    ULONG               Count;
+    PLIST_ENTRY         ListEntry;
     NTSTATUS            status;
 
-    ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+    ASSERT3U(KeGetCurrentIrql(), == , PASSIVE_LEVEL);
 
     StackLocation = IoGetCurrentIrpStackLocation(Irp);
 
     status = Irp->IoStatus.Status;
 
-    IoSkipCurrentIrpStackLocation(Irp);
-    status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+    if (StackLocation->Parameters.QueryDeviceRelations.Type != BusRelations) {
+        IoSkipCurrentIrpStackLocation(Irp);
+        status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+
+        goto done;
+    }
+
+    __FdoAcquireMutex(Fdo);
+
+    Count = 0;
+    for (ListEntry = Fdo->Dx->ListEntry.Flink;
+         ListEntry != &Fdo->Dx->ListEntry;
+         ListEntry = ListEntry->Flink)
+        Count++;
+
+    Size = FIELD_OFFSET(DEVICE_RELATIONS, Objects) + (sizeof(PDEVICE_OBJECT) * 
__max(Count, 1));
+
+    Relations = ExAllocatePoolWithTag(PagedPool, Size, FDO_POOL);
+
+    status = STATUS_NO_MEMORY;
+    if (Relations == NULL)
+        goto fail1;
+
+    RtlZeroMemory(Relations, Size);
+
+    for (ListEntry = Fdo->Dx->ListEntry.Flink;
+         ListEntry != &Fdo->Dx->ListEntry;
+         ListEntry = ListEntry->Flink) {
+        PXENCONS_DX  Dx = CONTAINING_RECORD(ListEntry, XENCONS_DX, ListEntry);
+        PXENCONS_PDO Pdo = Dx->Pdo;
+
+        ASSERT3U(Dx->Type, == , PHYSICAL_DEVICE_OBJECT);
+
+        if (PdoIsMissing(Pdo))
+            continue;
+
+        if (PdoGetDevicePnpState(Pdo) == Present)
+            PdoSetDevicePnpState(Pdo, Enumerated);
+
+        ObReferenceObject(Dx->DeviceObject);
+        Relations->Objects[Relations->Count++] = Dx->DeviceObject;
+    }
+
+    ASSERT3U(Relations->Count, <= , Count);
+
+    Trace("%d PDO(s)\n", Relations->Count);
+
+    __FdoReleaseMutex(Fdo);
+
+    Irp->IoStatus.Information = (ULONG_PTR)Relations;
+    Irp->IoStatus.Status = STATUS_SUCCESS;
+
+    status = FdoForwardIrpSynchronously(Fdo, Irp);
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    __FdoAcquireMutex(Fdo);
+
+    ListEntry = Fdo->Dx->ListEntry.Flink;
+    while (ListEntry != &Fdo->Dx->ListEntry) {
+        PXENCONS_DX  Dx = CONTAINING_RECORD(ListEntry, XENCONS_DX, ListEntry);
+        PXENCONS_PDO Pdo = Dx->Pdo;
+        PLIST_ENTRY Next = ListEntry->Flink;
+
+        ASSERT3U(Dx->Type, == , PHYSICAL_DEVICE_OBJECT);
+
+        if (PdoGetDevicePnpState(Pdo) == Deleted &&
+            PdoIsMissing(Pdo))
+            PdoDestroy(Pdo);
+
+        ListEntry = Next;
+    }
+
+    __FdoReleaseMutex(Fdo);
+
+done:
+    return status;
+
+fail2:
+    Error("fail2\n");
+
+    __FdoAcquireMutex(Fdo);
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    __FdoReleaseMutex(Fdo);
+
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
 
     return status;
 }
@@ -2469,13 +2853,33 @@ FdoCreate(
 
     Dx->Fdo = Fdo;
 
+    InitializeMutex(&Fdo->Mutex);
+    InitializeListHead(&Dx->ListEntry);
+    Fdo->References = 1;
+
     Info("%p (%s)\n",
          FunctionDeviceObject,
          __FdoGetName(Fdo));
 
+    status = PdoCreate(Fdo, NULL);
+    if (!NT_SUCCESS(status))
+        goto fail13;
+
     FunctionDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
     return STATUS_SUCCESS;
 
+fail13:
+    Error("fail13\n");
+
+    Dx->Fdo = Fdo;
+
+    RtlZeroMemory(&Fdo->Mutex, sizeof(MUTEX));
+    RtlZeroMemory(&Dx->ListEntry, sizeof(LIST_ENTRY));
+    Fdo->References = 0;
+
+    ConsoleDestroy(Fdo->Console);
+    Fdo->Console = NULL;
+
 fail12:
     Error("fail12\n");
 
@@ -2561,7 +2965,9 @@ FdoDestroy(
     PXENCONS_DX         Dx = Fdo->Dx;
     PDEVICE_OBJECT      FunctionDeviceObject = Dx->DeviceObject;
 
-    ASSERT3U(__FdoGetDevicePnpState(Fdo), ==, Deleted);
+    ASSERT(IsListEmpty(&Dx->ListEntry));
+    ASSERT3U(Fdo->References, == , 0);
+    ASSERT3U(__FdoGetDevicePnpState(Fdo), == , Deleted);
 
     Fdo->NotDisableable = FALSE;
 
@@ -2569,6 +2975,8 @@ FdoDestroy(
          FunctionDeviceObject,
          __FdoGetName(Fdo));
 
+    RtlZeroMemory(&Fdo->Mutex, sizeof(MUTEX));
+
     Dx->Fdo = NULL;
 
     ConsoleDestroy(Fdo->Console);
diff --git a/src/xencons/fdo.h b/src/xencons/fdo.h
index 19eaa42..f8ebaf3 100644
--- a/src/xencons/fdo.h
+++ b/src/xencons/fdo.h
@@ -40,6 +40,49 @@
 
 #include "driver.h"
 
+extern PCHAR
+FdoGetVendorName(
+    IN  PXENCONS_FDO    Fdo
+    );
+
+extern PCHAR
+FdoGetName(
+    IN  PXENCONS_FDO    Fdo
+    );
+
+extern NTSTATUS
+FdoAddPhysicalDeviceObject(
+    IN  PXENCONS_FDO    Fdo,
+    IN  PXENCONS_PDO    Pdo
+    );
+
+extern VOID
+FdoRemovePhysicalDeviceObject(
+    IN  PXENCONS_FDO     Fdo,
+    IN  PXENCONS_PDO     Pdo
+    );
+
+extern VOID
+FdoAcquireMutex(
+    IN  PXENCONS_FDO     Fdo
+    );
+
+extern VOID
+FdoReleaseMutex(
+    IN  PXENCONS_FDO     Fdo
+    );
+
+extern PDEVICE_OBJECT
+FdoGetPhysicalDeviceObject(
+    IN  PXENCONS_FDO    Fdo
+    );
+
+extern NTSTATUS
+FdoDelegateIrp(
+    IN  PXENCONS_FDO    Fdo,
+    IN  PIRP            Irp
+    );
+
 extern NTSTATUS
 FdoDispatch(
     IN  PXENCONS_FDO    Fdo,
diff --git a/src/xencons/mutex.h b/src/xencons/mutex.h
new file mode 100755
index 0000000..904b21d
--- /dev/null
+++ b/src/xencons/mutex.h
@@ -0,0 +1,82 @@
+/* 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 _XENCONS_MUTEX_H
+#define _XENCONS_MUTEX_H
+
+#include <ntddk.h>
+
+#include "assert.h"
+
+typedef struct _MUTEX {
+    PKTHREAD    Owner;
+    KEVENT      Event;
+} MUTEX, *PMUTEX;
+
+static FORCEINLINE VOID
+InitializeMutex(
+    IN  PMUTEX  Mutex
+)
+{
+    RtlZeroMemory(Mutex, sizeof(MUTEX));
+
+    KeInitializeEvent(&Mutex->Event, SynchronizationEvent, TRUE);
+}
+
+static FORCEINLINE VOID
+__drv_maxIRQL(PASSIVE_LEVEL)
+AcquireMutex(
+    IN  PMUTEX  Mutex
+)
+{
+    (VOID)KeWaitForSingleObject(&Mutex->Event,
+                                Executive,
+                                KernelMode,
+                                FALSE,
+                                NULL);
+
+    ASSERT3P(Mutex->Owner, == , NULL);
+    Mutex->Owner = KeGetCurrentThread();
+}
+
+static FORCEINLINE VOID
+__drv_maxIRQL(PASSIVE_LEVEL)
+ReleaseMutex(
+    IN  PMUTEX  Mutex
+)
+{
+    ASSERT3P(Mutex->Owner, == , KeGetCurrentThread());
+    Mutex->Owner = NULL;
+
+    KeSetEvent(&Mutex->Event, IO_NO_INCREMENT, FALSE);
+}
+
+#endif  // _XENCONS_MUTEX_H
diff --git a/src/xencons/pdo.c b/src/xencons/pdo.c
new file mode 100755
index 0000000..5e90844
--- /dev/null
+++ b/src/xencons/pdo.c
@@ -0,0 +1,1865 @@
+/* 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.
+*/
+
+#define INITGUID 1
+
+#include <ntddk.h>
+#include <wdmguid.h>
+#include <ntstrsafe.h>
+#include <stdlib.h>
+
+#include <suspend_interface.h>
+#include <version.h>
+
+#include "driver.h"
+#include "names.h"
+#include "fdo.h"
+#include "pdo.h"
+#include "thread.h"
+#include "dbg_print.h"
+#include "assert.h"
+#include "util.h"
+
+#define PDO_POOL 'ODP'
+
+#define MAXNAMELEN  128
+#define MAXTEXTLEN  1024
+
+struct _XENCONS_PDO {
+    PXENCONS_DX                 Dx;
+
+    PXENCONS_THREAD             SystemPowerThread;
+    PIRP                        SystemPowerIrp;
+    PXENCONS_THREAD             DevicePowerThread;
+    PIRP                        DevicePowerIrp;
+
+    PXENCONS_FDO                Fdo;
+    BOOLEAN                     Missing;
+    const CHAR                  *Reason;
+    LONG                        Eject;
+
+    XENBUS_SUSPEND_INTERFACE    SuspendInterface;
+    PXENBUS_SUSPEND_CALLBACK    SuspendCallbackLate;
+};
+
+static FORCEINLINE PVOID
+__PdoAllocate(
+    IN  ULONG   Length
+    )
+{
+    return __AllocatePoolWithTag(NonPagedPool, Length, PDO_POOL);
+}
+
+static FORCEINLINE VOID
+__PdoFree(
+    IN  PVOID   Buffer
+    )
+{
+    __FreePoolWithTag(Buffer, PDO_POOL);
+}
+
+static FORCEINLINE VOID
+__PdoSetDevicePnpState(
+    IN  PXENCONS_PDO        Pdo,
+    IN  DEVICE_PNP_STATE    State
+    )
+{
+    PXENCONS_DX             Dx = Pdo->Dx;
+
+    // We can never transition out of the deleted state
+    ASSERT(Dx->DevicePnpState != Deleted || State == Deleted);
+
+    Dx->PreviousDevicePnpState = Dx->DevicePnpState;
+    Dx->DevicePnpState = State;
+}
+
+VOID
+PdoSetDevicePnpState(
+    IN  PXENCONS_PDO        Pdo,
+    IN  DEVICE_PNP_STATE    State
+    )
+{
+    __PdoSetDevicePnpState(Pdo, State);
+}
+
+static FORCEINLINE VOID
+__PdoRestoreDevicePnpState(
+    IN  PXENCONS_PDO        Pdo,
+    IN  DEVICE_PNP_STATE    State
+    )
+{
+    PXENCONS_DX             Dx = Pdo->Dx;
+
+    if (Dx->DevicePnpState == State)
+        Dx->DevicePnpState = Dx->PreviousDevicePnpState;
+}
+
+static FORCEINLINE DEVICE_PNP_STATE
+__PdoGetDevicePnpState(
+    IN  PXENCONS_PDO    Pdo
+    )
+{
+    PXENCONS_DX         Dx = Pdo->Dx;
+
+    return Dx->DevicePnpState;
+}
+
+DEVICE_PNP_STATE
+PdoGetDevicePnpState(
+    IN  PXENCONS_PDO    Pdo
+    )
+{
+    return __PdoGetDevicePnpState(Pdo);
+}
+
+static FORCEINLINE VOID
+__PdoSetSystemPowerState(
+    IN  PXENCONS_PDO        Pdo,
+    IN  SYSTEM_POWER_STATE  State
+    )
+{
+    PXENCONS_DX             Dx = Pdo->Dx;
+
+    Dx->SystemPowerState = State;
+}
+
+static FORCEINLINE SYSTEM_POWER_STATE
+__PdoGetSystemPowerState(
+    IN  PXENCONS_PDO    Pdo
+    )
+{
+    PXENCONS_DX         Dx = Pdo->Dx;
+
+    return Dx->SystemPowerState;
+}
+
+static FORCEINLINE VOID
+__PdoSetDevicePowerState(
+    IN  PXENCONS_PDO        Pdo,
+    IN  DEVICE_POWER_STATE  State
+    )
+{
+    PXENCONS_DX             Dx = Pdo->Dx;
+
+    Dx->DevicePowerState = State;
+}
+
+static FORCEINLINE DEVICE_POWER_STATE
+__PdoGetDevicePowerState(
+    IN  PXENCONS_PDO    Pdo
+    )
+{
+    PXENCONS_DX         Dx = Pdo->Dx;
+
+    return Dx->DevicePowerState;
+}
+
+static FORCEINLINE VOID
+__PdoSetMissing(
+    IN  PXENCONS_PDO    Pdo,
+    IN  const CHAR      *Reason
+    )
+{
+    Pdo->Reason = Reason;
+    Pdo->Missing = TRUE;
+}
+
+VOID
+PdoSetMissing(
+    IN  PXENCONS_PDO    Pdo,
+    IN  const CHAR      *Reason
+    )
+{
+    __PdoSetMissing(Pdo, Reason);
+}
+
+static FORCEINLINE BOOLEAN
+__PdoIsMissing(
+    IN  PXENCONS_PDO    Pdo
+    )
+{
+    return Pdo->Missing;
+}
+
+BOOLEAN
+PdoIsMissing(
+    IN  PXENCONS_PDO    Pdo
+    )
+{
+    return __PdoIsMissing(Pdo);
+}
+
+static FORCEINLINE PDEVICE_OBJECT
+__PdoGetDeviceObject(
+    IN  PXENCONS_PDO    Pdo
+    )
+{
+    PXENCONS_DX         Dx = Pdo->Dx;
+
+    return (Dx->DeviceObject);
+}
+
+PDEVICE_OBJECT
+PdoGetDeviceObject(
+    IN  PXENCONS_PDO    Pdo
+    )
+{
+    return __PdoGetDeviceObject(Pdo);
+}
+
+static FORCEINLINE PXENCONS_FDO
+__PdoGetFdo(
+    IN  PXENCONS_PDO    Pdo
+    )
+{
+    return Pdo->Fdo;
+}
+
+PXENCONS_FDO
+PdoGetFdo(
+    IN  PXENCONS_PDO    Pdo
+    )
+{
+    return __PdoGetFdo(Pdo);
+}
+
+static FORCEINLINE VOID
+__PdoSetName(
+    IN  PXENCONS_PDO    Pdo,
+    IN  PANSI_STRING    Device
+    )
+{
+    PXENCONS_DX         Dx = Pdo->Dx;
+    NTSTATUS            status;
+
+    if (Device == NULL)
+        status = RtlStringCbPrintfA(Dx->Name,
+                                    MAX_DEVICE_ID_LEN,
+                                    "0");
+    else
+        status = RtlStringCbPrintfA(Dx->Name,
+                                    MAX_DEVICE_ID_LEN,
+                                    "%Z",
+                                    Device);
+    ASSERT(NT_SUCCESS(status));
+}
+
+static FORCEINLINE PCHAR
+__PdoGetName(
+    IN  PXENCONS_PDO    Pdo
+    )
+{
+    PXENCONS_DX         Dx = Pdo->Dx;
+
+    return Dx->Name;
+}
+
+static FORCEINLINE PCHAR
+__PdoGetVendorName(
+    IN  PXENCONS_PDO    Pdo
+    )
+{
+    return FdoGetVendorName(__PdoGetFdo(Pdo));
+}
+
+static FORCEINLINE BOOLEAN
+__PdoSetEjectRequested(
+    IN  PXENCONS_PDO    Pdo
+    )
+{
+    return (InterlockedBitTestAndSet(&Pdo->Eject, 0) == 0) ? TRUE : FALSE;
+}
+
+VOID
+PdoRequestEject(
+    IN  PXENCONS_PDO    Pdo
+    )
+{
+    PXENCONS_DX         Dx = Pdo->Dx;
+    PDEVICE_OBJECT      PhysicalDeviceObject = Dx->DeviceObject;
+    PXENCONS_FDO        Fdo = __PdoGetFdo(Pdo);
+
+    if (!__PdoSetEjectRequested(Pdo))
+        return;
+
+    Info("%p (%s)\n",
+         PhysicalDeviceObject,
+         __PdoGetName(Pdo));
+
+    IoInvalidateDeviceRelations(FdoGetPhysicalDeviceObject(Fdo),
+                                BusRelations);
+}
+
+static FORCEINLINE BOOLEAN
+__PdoClearEjectRequested(
+    IN  PXENCONS_PDO    Pdo
+    )
+{
+    return (InterlockedBitTestAndReset(&Pdo->Eject, 0) != 0) ? TRUE : FALSE;
+}
+
+static FORCEINLINE BOOLEAN
+__PdoIsEjectRequested(
+    IN  PXENCONS_PDO    Pdo
+    )
+{
+    KeMemoryBarrier();
+    return (Pdo->Eject & 1) ? TRUE : FALSE;
+}
+
+BOOLEAN
+PdoIsEjectRequested(
+    IN  PXENCONS_PDO    Pdo
+    )
+{
+    return __PdoIsEjectRequested(Pdo);
+}
+
+static FORCEINLINE NTSTATUS
+__PdoD3ToD0(
+    IN  PXENCONS_PDO    Pdo
+    )
+{
+    POWER_STATE         PowerState;
+
+    Trace("(%s) ====>\n", __PdoGetName(Pdo));
+
+    ASSERT3U(KeGetCurrentIrql(), == , DISPATCH_LEVEL);
+    ASSERT3U(__PdoGetDevicePowerState(Pdo), == , PowerDeviceD3);
+
+    __PdoSetDevicePowerState(Pdo, PowerDeviceD0);
+
+    PowerState.DeviceState = PowerDeviceD0;
+    PoSetPowerState(__PdoGetDeviceObject(Pdo),
+                    DevicePowerState,
+                    PowerState);
+
+    Trace("(%s) <====\n", __PdoGetName(Pdo));
+
+    return STATUS_SUCCESS;
+}
+
+static FORCEINLINE VOID
+__PdoD0ToD3(
+    IN  PXENCONS_PDO    Pdo
+    )
+{
+    POWER_STATE         PowerState;
+
+    Trace("(%s) ====>\n", __PdoGetName(Pdo));
+
+    ASSERT3U(KeGetCurrentIrql(), == , DISPATCH_LEVEL);
+    ASSERT3U(__PdoGetDevicePowerState(Pdo), == , PowerDeviceD0);
+
+    PowerState.DeviceState = PowerDeviceD3;
+    PoSetPowerState(__PdoGetDeviceObject(Pdo),
+                    DevicePowerState,
+                    PowerState);
+
+    __PdoSetDevicePowerState(Pdo, PowerDeviceD3);
+
+    Trace("(%s) <====\n", __PdoGetName(Pdo));
+}
+
+static DECLSPEC_NOINLINE VOID
+PdoSuspendCallbackLate(
+    IN  PVOID               Argument
+    )
+{
+    PXENCONS_PDO            Pdo = Argument;
+    NTSTATUS                status;
+
+    __PdoD0ToD3(Pdo);
+
+    status = __PdoD3ToD0(Pdo);
+    ASSERT(NT_SUCCESS(status));
+}
+
+// This function must not touch pageable code or data
+static DECLSPEC_NOINLINE NTSTATUS
+PdoD3ToD0(
+    IN  PXENCONS_PDO    Pdo
+    )
+{
+    KIRQL               Irql;
+    NTSTATUS            status;
+
+    Trace("(%s) ====>\n", __PdoGetName(Pdo));
+
+    ASSERT3U(KeGetCurrentIrql(), == , PASSIVE_LEVEL);
+
+    KeRaiseIrql(DISPATCH_LEVEL, &Irql);
+
+    status = XENBUS_SUSPEND(Acquire, &Pdo->SuspendInterface);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    status = __PdoD3ToD0(Pdo);
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    status = XENBUS_SUSPEND(Register,
+                            &Pdo->SuspendInterface,
+                            SUSPEND_CALLBACK_LATE,
+                            PdoSuspendCallbackLate,
+                            Pdo,
+                            &Pdo->SuspendCallbackLate);
+    if (!NT_SUCCESS(status))
+        goto fail3;
+
+    KeLowerIrql(Irql);
+
+    Trace("(%s) <====\n", __PdoGetName(Pdo));
+
+    return STATUS_SUCCESS;
+
+fail3:
+    Error("fail3\n");
+
+    __PdoD0ToD3(Pdo);
+
+fail2:
+    Error("fail2\n");
+
+    XENBUS_SUSPEND(Release, &Pdo->SuspendInterface);
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    KeLowerIrql(Irql);
+
+    return status;
+}
+
+// This function must not touch pageable code or data
+static DECLSPEC_NOINLINE VOID
+PdoD0ToD3(
+    IN  PXENCONS_PDO    Pdo
+    )
+{
+    KIRQL               Irql;
+
+    Trace("(%s) ====>\n", __PdoGetName(Pdo));
+
+    ASSERT3U(KeGetCurrentIrql(), == , PASSIVE_LEVEL);
+
+    KeRaiseIrql(DISPATCH_LEVEL, &Irql);
+
+    XENBUS_SUSPEND(Deregister,
+                   &Pdo->SuspendInterface,
+                   Pdo->SuspendCallbackLate);
+    Pdo->SuspendCallbackLate = NULL;
+
+    __PdoD0ToD3(Pdo);
+
+    XENBUS_SUSPEND(Release, &Pdo->SuspendInterface);
+
+    KeLowerIrql(Irql);
+
+    Trace("(%s) <====\n", __PdoGetName(Pdo));
+}
+
+// This function must not touch pageable code or data
+static DECLSPEC_NOINLINE VOID
+PdoS4ToS3(
+    IN  PXENCONS_PDO    Pdo
+    )
+{
+    Trace("(%s) ====>\n", __PdoGetName(Pdo));
+
+    ASSERT3U(KeGetCurrentIrql(), == , PASSIVE_LEVEL);
+    ASSERT3U(__PdoGetSystemPowerState(Pdo), == , PowerSystemHibernate);
+
+    __PdoSetSystemPowerState(Pdo, PowerSystemSleeping3);
+
+    Trace("(%s) <====\n", __PdoGetName(Pdo));
+}
+
+// This function must not touch pageable code or data
+static DECLSPEC_NOINLINE VOID
+PdoS3ToS4(
+    IN  PXENCONS_PDO    Pdo
+    )
+{
+    Trace("(%s) ====>\n", __PdoGetName(Pdo));
+
+    ASSERT3U(KeGetCurrentIrql(), == , PASSIVE_LEVEL);
+    ASSERT3U(__PdoGetSystemPowerState(Pdo), == , PowerSystemSleeping3);
+
+    __PdoSetSystemPowerState(Pdo, PowerSystemHibernate);
+
+    Trace("(%s) <====\n", __PdoGetName(Pdo));
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoStartDevice(
+    IN  PXENCONS_PDO    Pdo,
+    IN  PIRP            Irp
+    )
+{
+    NTSTATUS            status;
+
+    status = PdoD3ToD0(Pdo);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    __PdoSetDevicePnpState(Pdo, Started);
+
+    Irp->IoStatus.Status = STATUS_SUCCESS;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return STATUS_SUCCESS;
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoQueryStopDevice(
+    IN  PXENCONS_PDO    Pdo,
+    IN  PIRP            Irp
+    )
+{
+    NTSTATUS            status;
+
+    __PdoSetDevicePnpState(Pdo, StopPending);
+    status = STATUS_SUCCESS;
+
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoCancelStopDevice(
+    IN  PXENCONS_PDO    Pdo,
+    IN  PIRP            Irp
+    )
+{
+    NTSTATUS            status;
+
+    __PdoRestoreDevicePnpState(Pdo, StopPending);
+    status = STATUS_SUCCESS;
+
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoStopDevice(
+    IN  PXENCONS_PDO    Pdo,
+    IN  PIRP            Irp
+    )
+{
+    NTSTATUS            status;
+
+    if (__PdoGetDevicePowerState(Pdo) != PowerDeviceD0)
+        goto done;
+
+    PdoD0ToD3(Pdo);
+
+done:
+    __PdoSetDevicePnpState(Pdo, Stopped);
+    status = STATUS_SUCCESS;
+
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoQueryRemoveDevice(
+    IN  PXENCONS_PDO    Pdo,
+    IN  PIRP            Irp
+    )
+{
+    NTSTATUS            status;
+
+    __PdoSetDevicePnpState(Pdo, RemovePending);
+    status = STATUS_SUCCESS;
+
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoCancelRemoveDevice(
+    IN  PXENCONS_PDO    Pdo,
+    IN  PIRP            Irp
+    )
+{
+    NTSTATUS            status;
+
+    if (__PdoClearEjectRequested(Pdo))
+        Trace("(%s) Eject Failed\n", __PdoGetName(Pdo));
+
+    __PdoRestoreDevicePnpState(Pdo, RemovePending);
+    status = STATUS_SUCCESS;
+
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoSurpriseRemoval(
+    IN  PXENCONS_PDO    Pdo,
+    IN  PIRP            Irp
+    )
+{
+    NTSTATUS            status;
+
+    Warning("%s\n", __PdoGetName(Pdo));
+
+    __PdoSetDevicePnpState(Pdo, SurpriseRemovePending);
+    status = STATUS_SUCCESS;
+
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoRemoveDevice(
+    IN  PXENCONS_PDO    Pdo,
+    IN  PIRP            Irp
+    )
+{
+    PXENCONS_FDO        Fdo = __PdoGetFdo(Pdo);
+    BOOLEAN             NeedInvalidate;
+    NTSTATUS            status;
+
+    if (__PdoGetDevicePowerState(Pdo) != PowerDeviceD0)
+        goto done;
+
+    PdoD0ToD3(Pdo);
+
+done:
+    NeedInvalidate = FALSE;
+
+    FdoAcquireMutex(Fdo);
+
+    if (__PdoIsMissing(Pdo)) {
+        DEVICE_PNP_STATE    State = __PdoGetDevicePnpState(Pdo);
+
+        __PdoSetDevicePnpState(Pdo, Deleted);
+
+        if (State == SurpriseRemovePending)
+            PdoDestroy(Pdo);
+        else
+            NeedInvalidate = TRUE;
+    } else {
+        __PdoSetDevicePnpState(Pdo, Enumerated);
+    }
+
+    FdoReleaseMutex(Fdo);
+
+    if (NeedInvalidate)
+        IoInvalidateDeviceRelations(FdoGetPhysicalDeviceObject(Fdo),
+                                    BusRelations);
+
+    status = STATUS_SUCCESS;
+
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoQueryDeviceRelations(
+    IN  PXENCONS_PDO    Pdo,
+    IN  PIRP            Irp
+    )
+{
+    PIO_STACK_LOCATION  StackLocation;
+    PDEVICE_RELATIONS   Relations;
+    NTSTATUS            status;
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+
+    status = Irp->IoStatus.Status;
+
+    if (StackLocation->Parameters.QueryDeviceRelations.Type != 
TargetDeviceRelation)
+        goto done;
+
+    Relations = ExAllocatePoolWithTag(PagedPool, sizeof(DEVICE_RELATIONS), 
PDO_POOL);
+
+    status = STATUS_NO_MEMORY;
+    if (Relations == NULL)
+        goto done;
+
+    RtlZeroMemory(Relations, sizeof(DEVICE_RELATIONS));
+
+    Relations->Count = 1;
+    ObReferenceObject(__PdoGetDeviceObject(Pdo));
+    Relations->Objects[0] = __PdoGetDeviceObject(Pdo);
+
+    Irp->IoStatus.Information = (ULONG_PTR)Relations;
+    status = STATUS_SUCCESS;
+
+done:
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoQueryCapabilities(
+    IN  PXENCONS_PDO        Pdo,
+    IN  PIRP                Irp
+    )
+{
+    PIO_STACK_LOCATION      StackLocation;
+    PDEVICE_CAPABILITIES    Capabilities;
+    SYSTEM_POWER_STATE      SystemPowerState;
+    NTSTATUS                status;
+
+    UNREFERENCED_PARAMETER(Pdo);
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+    Capabilities = StackLocation->Parameters.DeviceCapabilities.Capabilities;
+
+    status = STATUS_INVALID_PARAMETER;
+    if (Capabilities->Version != 1)
+        goto done;
+
+    Capabilities->DeviceD1 = 0;
+    Capabilities->DeviceD2 = 0;
+    Capabilities->LockSupported = 0;
+    Capabilities->EjectSupported = 1;
+    Capabilities->Removable = 1;
+    Capabilities->DockDevice = 0;
+    Capabilities->UniqueID = 1;
+    Capabilities->SilentInstall = 1;
+    Capabilities->RawDeviceOK = 1;
+    Capabilities->SurpriseRemovalOK = 1;
+    Capabilities->HardwareDisabled = 0;
+    Capabilities->NoDisplayInUI = 0;
+
+    Capabilities->Address = 0xffffffff;
+    Capabilities->UINumber = 0xffffffff;
+
+    for (SystemPowerState = 0; SystemPowerState < PowerSystemMaximum; 
SystemPowerState++) {
+        switch (SystemPowerState) {
+        case PowerSystemUnspecified:
+        case PowerSystemSleeping1:
+        case PowerSystemSleeping2:
+            break;
+
+        case PowerSystemWorking:
+            Capabilities->DeviceState[SystemPowerState] = PowerDeviceD0;
+            break;
+
+        default:
+            Capabilities->DeviceState[SystemPowerState] = PowerDeviceD3;
+            break;
+        }
+    }
+
+    Capabilities->SystemWake = PowerSystemUnspecified;
+    Capabilities->DeviceWake = PowerDeviceUnspecified;
+    Capabilities->D1Latency = 0;
+    Capabilities->D2Latency = 0;
+    Capabilities->D3Latency = 0;
+
+    status = STATUS_SUCCESS;
+
+done:
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoQueryDeviceText(
+    IN  PXENCONS_PDO    Pdo,
+    IN  PIRP            Irp
+    )
+{
+    PIO_STACK_LOCATION  StackLocation;
+    PWCHAR              Buffer;
+    UNICODE_STRING      Text;
+    NTSTATUS            status;
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+
+    switch (StackLocation->Parameters.QueryDeviceText.DeviceTextType) {
+    case DeviceTextDescription:
+        Trace("DeviceTextDescription\n");
+        break;
+
+    case DeviceTextLocationInformation:
+        Trace("DeviceTextLocationInformation\n");
+        break;
+
+    default:
+        Irp->IoStatus.Information = 0;
+        status = STATUS_NOT_SUPPORTED;
+        goto done;
+    }
+
+    Buffer = ExAllocatePoolWithTag(PagedPool, MAXTEXTLEN, PDO_POOL);
+
+    status = STATUS_NO_MEMORY;
+    if (Buffer == NULL)
+        goto done;
+
+    RtlZeroMemory(Buffer, MAXTEXTLEN);
+
+    Text.Buffer = Buffer;
+    Text.MaximumLength = MAXTEXTLEN;
+    Text.Length = 0;
+
+    switch (StackLocation->Parameters.QueryDeviceText.DeviceTextType) {
+    case DeviceTextDescription:
+        status = RtlStringCbPrintfW(Buffer,
+                                    MAXTEXTLEN,
+                                    L"%hs PV Console #%hs",
+                                    PRODUCT_NAME_STR,
+                                    __PdoGetName(Pdo));
+        ASSERT(NT_SUCCESS(status));
+
+        Buffer += wcslen(Buffer);
+
+        break;
+
+    case DeviceTextLocationInformation:
+        status = RtlStringCbPrintfW(Buffer,
+                                    MAXTEXTLEN,
+                                    L"%hs",
+                                    __PdoGetName(Pdo));
+        ASSERT(NT_SUCCESS(status));
+
+        Buffer += wcslen(Buffer);
+
+        break;
+
+    default:
+        ASSERT(FALSE);
+        break;
+    }
+
+    Text.Length = (USHORT)((ULONG_PTR)Buffer - (ULONG_PTR)Text.Buffer);
+
+    ASSERT3U(KeGetCurrentIrql(), == , PASSIVE_LEVEL);
+
+    Trace("%s: %wZ\n", __PdoGetName(Pdo), &Text);
+
+    Irp->IoStatus.Information = (ULONG_PTR)Text.Buffer;
+    status = STATUS_SUCCESS;
+
+done:
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoReadConfig(
+    IN  PXENCONS_PDO    Pdo,
+    IN  PIRP            Irp
+    )
+{
+    UNREFERENCED_PARAMETER(Pdo);
+
+    Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return STATUS_NOT_SUPPORTED;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoWriteConfig(
+    IN  PXENCONS_PDO    Pdo,
+    IN  PIRP            Irp
+    )
+{
+    UNREFERENCED_PARAMETER(Pdo);
+
+    Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return STATUS_NOT_SUPPORTED;
+}
+
+#define REGSTR_VAL_MAX_HCID_LEN 1024
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoQueryId(
+    IN  PXENCONS_PDO    Pdo,
+    IN  PIRP            Irp
+    )
+{
+    PIO_STACK_LOCATION  StackLocation;
+    PWCHAR              Buffer;
+    UNICODE_STRING      Id;
+    ULONG               Type;
+    NTSTATUS            status;
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+
+    switch (StackLocation->Parameters.QueryId.IdType) {
+    case BusQueryInstanceID:
+        Trace("BusQueryInstanceID\n");
+        Id.MaximumLength = (USHORT)(strlen(__PdoGetName(Pdo)) + 1) * 
sizeof(WCHAR);
+        break;
+
+    case BusQueryDeviceID:
+        Trace("BusQueryDeviceID\n");
+        Id.MaximumLength = (MAX_DEVICE_ID_LEN - 2) * sizeof(WCHAR);
+        break;
+
+    case BusQueryHardwareIDs:
+        Trace("BusQueryHardwareIDs\n");
+        Id.MaximumLength = (USHORT)(MAX_DEVICE_ID_LEN * sizeof(WCHAR));
+        break;
+
+    case BusQueryCompatibleIDs:
+        Trace("BusQueryCompatibleIDs\n");
+        Id.MaximumLength = (USHORT)(MAX_DEVICE_ID_LEN * sizeof(WCHAR));
+        break;
+
+    default:
+        Irp->IoStatus.Information = 0;
+        status = STATUS_NOT_SUPPORTED;
+        goto done;
+    }
+
+    Buffer = ExAllocatePoolWithTag(PagedPool, Id.MaximumLength, PDO_POOL);
+
+    status = STATUS_NO_MEMORY;
+    if (Buffer == NULL)
+        goto done;
+
+    RtlZeroMemory(Buffer, Id.MaximumLength);
+
+    Id.Buffer = Buffer;
+    Id.Length = 0;
+
+    switch (StackLocation->Parameters.QueryId.IdType) {
+    case BusQueryInstanceID:
+        Type = REG_SZ;
+
+        status = RtlStringCbPrintfW(Buffer,
+                                    Id.MaximumLength,
+                                    L"%hs",
+                                    __PdoGetName(Pdo));
+        ASSERT(NT_SUCCESS(status));
+
+        Buffer += wcslen(Buffer);
+
+        break;
+
+    case BusQueryDeviceID:
+        Type = REG_SZ;
+
+        status = RtlStringCbPrintfW(Buffer,
+                                    Id.MaximumLength,
+                                    L"XENCONS\\VEN_%hs&DEV_CONSOLE",
+                                    __PdoGetVendorName(Pdo));
+        ASSERT(NT_SUCCESS(status));
+
+        Buffer += wcslen(Buffer);
+
+        break;
+
+    case BusQueryHardwareIDs:
+    case BusQueryCompatibleIDs:
+    {
+        ULONG   Length;
+
+        Type = REG_MULTI_SZ;
+
+        Length = Id.MaximumLength;
+
+        status = RtlStringCbPrintfW(Buffer,
+                                    Length,
+                                    L"XENCONS\\VEN_%hs&DEV_CONSOLE",
+                                    __PdoGetVendorName(Pdo));
+        ASSERT(NT_SUCCESS(status));
+
+        Buffer += wcslen(Buffer);
+        Length -= (ULONG)(wcslen(Buffer) * sizeof(WCHAR));
+
+        Buffer++;
+        Length -= sizeof(WCHAR);
+
+        status = RtlStringCbPrintfW(Buffer,
+                                    Length,
+                                    L"XENDEVICE");
+        ASSERT(NT_SUCCESS(status));
+
+        Buffer += wcslen(Buffer);
+        Buffer++;
+
+        ASSERT3U((ULONG_PTR)Buffer - (ULONG_PTR)Id.Buffer, <,
+                 REGSTR_VAL_MAX_HCID_LEN);
+        break;
+    }
+    default:
+        Type = REG_NONE;
+
+        ASSERT(FALSE);
+        break;
+    }
+
+    ASSERT3U(KeGetCurrentIrql(), == , PASSIVE_LEVEL);
+
+    Id.Length = (USHORT)((ULONG_PTR)Buffer - (ULONG_PTR)Id.Buffer);
+    Buffer = Id.Buffer;
+
+    switch (Type) {
+    case REG_SZ:
+        Trace("- %ws\n", Buffer);
+        break;
+
+    case REG_MULTI_SZ:
+        do {
+            Trace("- %ws\n", Buffer);
+            Buffer += wcslen(Buffer);
+            Buffer++;
+        } while (*Buffer != L'\0');
+        break;
+
+    default:
+        ASSERT(FALSE);
+        break;
+    }
+
+    Irp->IoStatus.Information = (ULONG_PTR)Id.Buffer;
+    status = STATUS_SUCCESS;
+
+done:
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoQueryBusInformation(
+    IN  PXENCONS_PDO        Pdo,
+    IN  PIRP                Irp
+    )
+{
+    PPNP_BUS_INFORMATION    Info;
+    NTSTATUS                status;
+
+    UNREFERENCED_PARAMETER(Pdo);
+
+    Info = ExAllocatePoolWithTag(PagedPool, sizeof(PNP_BUS_INFORMATION), 
PDO_POOL);
+
+    status = STATUS_NO_MEMORY;
+    if (Info == NULL)
+        goto done;
+
+    RtlZeroMemory(Info, sizeof(PNP_BUS_INFORMATION));
+
+    Info->BusTypeGuid = GUID_BUS_TYPE_INTERNAL;
+    Info->LegacyBusType = PNPBus;
+    Info->BusNumber = 0;
+
+    Irp->IoStatus.Information = (ULONG_PTR)Info;
+    status = STATUS_SUCCESS;
+
+done:
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static NTSTATUS
+PdoDelegateIrp(
+    IN  PXENCONS_PDO    Pdo,
+    IN  PIRP            Irp
+    )
+{
+    return FdoDelegateIrp(__PdoGetFdo(Pdo), Irp);
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoDeviceUsageNotification(
+    IN  PXENCONS_PDO    Pdo,
+    IN  PIRP            Irp
+    )
+{
+    NTSTATUS            status;
+
+    status = PdoDelegateIrp(Pdo, Irp);
+
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoEject(
+    IN  PXENCONS_PDO    Pdo,
+    IN  PIRP            Irp
+    )
+{
+    PXENCONS_FDO        Fdo = __PdoGetFdo(Pdo);
+    NTSTATUS            status;
+
+    Trace("%s\n", __PdoGetName(Pdo));
+
+    FdoAcquireMutex(Fdo);
+
+    __PdoSetDevicePnpState(Pdo, Deleted);
+    __PdoSetMissing(Pdo, "device ejected");
+
+    FdoReleaseMutex(Fdo);
+
+    IoInvalidateDeviceRelations(FdoGetPhysicalDeviceObject(Fdo),
+                                BusRelations);
+
+    status = STATUS_SUCCESS;
+
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoDispatchPnp(
+    IN  PXENCONS_PDO    Pdo,
+    IN  PIRP            Irp
+    )
+{
+    PIO_STACK_LOCATION  StackLocation;
+    UCHAR               MinorFunction;
+    NTSTATUS            status;
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+    MinorFunction = StackLocation->MinorFunction;
+
+    Trace("====> (%s) (%02x:%s)\n",
+          __PdoGetName(Pdo),
+          MinorFunction,
+          PnpMinorFunctionName(MinorFunction));
+
+    switch (StackLocation->MinorFunction) {
+    case IRP_MN_START_DEVICE:
+        status = PdoStartDevice(Pdo, Irp);
+        break;
+
+    case IRP_MN_QUERY_STOP_DEVICE:
+        status = PdoQueryStopDevice(Pdo, Irp);
+        break;
+
+    case IRP_MN_CANCEL_STOP_DEVICE:
+        status = PdoCancelStopDevice(Pdo, Irp);
+        break;
+
+    case IRP_MN_STOP_DEVICE:
+        status = PdoStopDevice(Pdo, Irp);
+        break;
+
+    case IRP_MN_QUERY_REMOVE_DEVICE:
+        status = PdoQueryRemoveDevice(Pdo, Irp);
+        break;
+
+    case IRP_MN_CANCEL_REMOVE_DEVICE:
+        status = PdoCancelRemoveDevice(Pdo, Irp);
+        break;
+
+    case IRP_MN_SURPRISE_REMOVAL:
+        status = PdoSurpriseRemoval(Pdo, Irp);
+        break;
+
+    case IRP_MN_REMOVE_DEVICE:
+        status = PdoRemoveDevice(Pdo, Irp);
+        break;
+
+    case IRP_MN_QUERY_DEVICE_RELATIONS:
+        status = PdoQueryDeviceRelations(Pdo, Irp);
+        break;
+
+    case IRP_MN_QUERY_CAPABILITIES:
+        status = PdoQueryCapabilities(Pdo, Irp);
+        break;
+
+    case IRP_MN_QUERY_DEVICE_TEXT:
+        status = PdoQueryDeviceText(Pdo, Irp);
+        break;
+
+    case IRP_MN_READ_CONFIG:
+        status = PdoReadConfig(Pdo, Irp);
+        break;
+
+    case IRP_MN_WRITE_CONFIG:
+        status = PdoWriteConfig(Pdo, Irp);
+        break;
+
+    case IRP_MN_QUERY_ID:
+        status = PdoQueryId(Pdo, Irp);
+        break;
+
+    case IRP_MN_QUERY_BUS_INFORMATION:
+        status = PdoQueryBusInformation(Pdo, Irp);
+        break;
+
+    case IRP_MN_DEVICE_USAGE_NOTIFICATION:
+        status = PdoDeviceUsageNotification(Pdo, Irp);
+        break;
+
+    case IRP_MN_EJECT:
+        status = PdoEject(Pdo, Irp);
+        break;
+
+    default:
+        status = Irp->IoStatus.Status;
+        IoCompleteRequest(Irp, IO_NO_INCREMENT);
+        break;
+    }
+
+    Trace("<==== (%02x:%s)(%08x)\n",
+          MinorFunction,
+          PnpMinorFunctionName(MinorFunction),
+          status);
+
+    return status;
+}
+
+static FORCEINLINE NTSTATUS
+__PdoSetDevicePower(
+    IN  PXENCONS_PDO    Pdo,
+    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:%s)\n",
+          __PdoGetName(Pdo),
+          PowerDeviceStateName(DeviceState),
+          PowerActionName(PowerAction));
+
+    ASSERT3U(PowerAction, <, PowerActionShutdown);
+
+    if (__PdoGetDevicePowerState(Pdo) > DeviceState) {
+        Trace("%s: POWERING UP: %s -> %s\n",
+              __PdoGetName(Pdo),
+              PowerDeviceStateName(__PdoGetDevicePowerState(Pdo)),
+              PowerDeviceStateName(DeviceState));
+
+        ASSERT3U(DeviceState, == , PowerDeviceD0);
+        status = PdoD3ToD0(Pdo);
+        ASSERT(NT_SUCCESS(status));
+    } else if (__PdoGetDevicePowerState(Pdo) < DeviceState) {
+        Trace("%s: POWERING DOWN: %s -> %s\n",
+              __PdoGetName(Pdo),
+              PowerDeviceStateName(__PdoGetDevicePowerState(Pdo)),
+              PowerDeviceStateName(DeviceState));
+
+        ASSERT3U(DeviceState, == , PowerDeviceD3);
+        PdoD0ToD3(Pdo);
+    }
+
+    Irp->IoStatus.Status = STATUS_SUCCESS;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    Trace("<==== (%s:%s)\n",
+          PowerDeviceStateName(DeviceState),
+          PowerActionName(PowerAction));
+
+    return STATUS_SUCCESS;
+}
+
+static NTSTATUS
+PdoDevicePower(
+    IN  PXENCONS_THREAD Self,
+    IN  PVOID           Context
+    )
+{
+    PXENCONS_PDO        Pdo = Context;
+    PKEVENT             Event;
+
+    Event = ThreadGetEvent(Self);
+
+    for (;;) {
+        PIRP    Irp;
+
+        if (Pdo->DevicePowerIrp == NULL) {
+            (VOID)KeWaitForSingleObject(Event,
+                                        Executive,
+                                        KernelMode,
+                                        FALSE,
+                                        NULL);
+            KeClearEvent(Event);
+        }
+
+        if (ThreadIsAlerted(Self))
+            break;
+
+        Irp = Pdo->DevicePowerIrp;
+
+        if (Irp == NULL)
+            continue;
+
+        Pdo->DevicePowerIrp = NULL;
+        KeMemoryBarrier();
+
+        (VOID)__PdoSetDevicePower(Pdo, Irp);
+    }
+
+    return STATUS_SUCCESS;
+}
+
+__drv_functionClass(REQUEST_POWER_COMPLETE)
+__drv_sameIRQL
+VOID
+__PdoRequestSetDevicePower(
+    IN  PDEVICE_OBJECT      DeviceObject,
+    IN  UCHAR               MinorFunction,
+    IN  POWER_STATE         PowerState,
+    IN  PVOID               Context,
+    IN  PIO_STATUS_BLOCK    IoStatus
+    )
+{
+    PKEVENT                 Event = Context;
+
+    UNREFERENCED_PARAMETER(DeviceObject);
+    UNREFERENCED_PARAMETER(MinorFunction);
+    UNREFERENCED_PARAMETER(PowerState);
+
+    ASSERT(NT_SUCCESS(IoStatus->Status));
+
+    KeSetEvent(Event, IO_NO_INCREMENT, FALSE);
+}
+
+static VOID
+PdoRequestSetDevicePower(
+    IN  PXENCONS_PDO        Pdo,
+    IN  DEVICE_POWER_STATE  DeviceState
+    )
+{
+    POWER_STATE             PowerState;
+    KEVENT                  Event;
+    NTSTATUS                status;
+
+    Trace("%s\n", PowerDeviceStateName(DeviceState));
+
+    ASSERT3U(KeGetCurrentIrql(), == , PASSIVE_LEVEL);
+
+    PowerState.DeviceState = DeviceState;
+    KeInitializeEvent(&Event, NotificationEvent, FALSE);
+
+    status = PoRequestPowerIrp(__PdoGetDeviceObject(Pdo),
+                               IRP_MN_SET_POWER,
+                               PowerState,
+                               __PdoRequestSetDevicePower,
+                               &Event,
+                               NULL);
+    ASSERT(NT_SUCCESS(status));
+
+    (VOID)KeWaitForSingleObject(&Event,
+                                Executive,
+                                KernelMode,
+                                FALSE,
+                                NULL);
+}
+
+// Define a static SystemPower to DevicePower map
+// This would normally be queried from the power policy owner,
+// but since this Pdo is "raw", the Pdo is the power policy owner
+static const DEVICE_POWER_STATE
+DevicePowerStateMap[PowerSystemMaximum] =
+{
+    PowerDeviceUnspecified, // PowerSystemUnspecified
+    PowerDeviceD0,          // PowerSystemWorking
+    PowerDeviceD3,          // PowerSystemSleeping1
+    PowerDeviceD3,          // PowerSystemSleeping2
+    PowerDeviceD3,          // PowerSystemSleeping3
+    PowerDeviceD3,          // PowerSystemHibernate
+    PowerDeviceD3           // PowerSystemShutdown
+};
+
+static FORCEINLINE NTSTATUS
+__PdoSetSystemPower(
+    IN  PXENCONS_PDO        Pdo,
+    IN  PIRP                Irp
+    )
+{
+    PIO_STACK_LOCATION      StackLocation;
+    SYSTEM_POWER_STATE      SystemState;
+    POWER_ACTION            PowerAction;
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+    SystemState = StackLocation->Parameters.Power.State.SystemState;
+    PowerAction = StackLocation->Parameters.Power.ShutdownType;
+
+    Trace("====> (%s) (%s:%s)\n",
+          __PdoGetName(Pdo),
+          PowerSystemStateName(SystemState),
+          PowerActionName(PowerAction));
+
+    ASSERT3U(PowerAction, <, PowerActionShutdown);
+
+    if (__PdoGetSystemPowerState(Pdo) > SystemState) {
+        if (SystemState < PowerSystemHibernate &&
+            __PdoGetSystemPowerState(Pdo) >= PowerSystemHibernate) {
+            __PdoSetSystemPowerState(Pdo, PowerSystemHibernate);
+            PdoS4ToS3(Pdo);
+        }
+
+        Trace("%s: POWERING UP: %s -> %s\n",
+              __PdoGetName(Pdo),
+              PowerSystemStateName(__PdoGetSystemPowerState(Pdo)),
+              PowerSystemStateName(SystemState));
+
+        PdoRequestSetDevicePower(Pdo, DevicePowerStateMap[SystemState]);
+    } else if (__PdoGetSystemPowerState(Pdo) < SystemState) {
+        PdoRequestSetDevicePower(Pdo, DevicePowerStateMap[SystemState]);
+
+        Trace("%s: POWERING DOWN: %s -> %s\n",
+              __PdoGetName(Pdo),
+              PowerSystemStateName(__PdoGetSystemPowerState(Pdo)),
+              PowerSystemStateName(SystemState));
+
+        if (SystemState >= PowerSystemHibernate &&
+            __PdoGetSystemPowerState(Pdo) < PowerSystemHibernate) {
+            __PdoSetSystemPowerState(Pdo, PowerSystemSleeping3);
+            PdoS3ToS4(Pdo);
+        }
+    }
+
+    __PdoSetSystemPowerState(Pdo, SystemState);
+
+    Irp->IoStatus.Status = STATUS_SUCCESS;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    Trace("<==== (%s:%s)\n",
+          PowerSystemStateName(SystemState),
+          PowerActionName(PowerAction));
+
+    return STATUS_SUCCESS;
+}
+
+static NTSTATUS
+PdoSystemPower(
+    IN  PXENCONS_THREAD Self,
+    IN  PVOID           Context
+    )
+{
+    PXENCONS_PDO        Pdo = Context;
+    PKEVENT             Event;
+
+    Event = ThreadGetEvent(Self);
+
+    for (;;) {
+        PIRP    Irp;
+
+        if (Pdo->SystemPowerIrp == NULL) {
+            (VOID)KeWaitForSingleObject(Event,
+                                        Executive,
+                                        KernelMode,
+                                        FALSE,
+                                        NULL);
+            KeClearEvent(Event);
+        }
+
+        if (ThreadIsAlerted(Self))
+            break;
+
+        Irp = Pdo->SystemPowerIrp;
+
+        if (Irp == NULL)
+            continue;
+
+        Pdo->SystemPowerIrp = NULL;
+        KeMemoryBarrier();
+
+        (VOID)__PdoSetSystemPower(Pdo, Irp);
+    }
+
+    return STATUS_SUCCESS;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoSetPower(
+    IN  PXENCONS_PDO    Pdo,
+    IN  PIRP            Irp
+    )
+{
+    PIO_STACK_LOCATION  StackLocation;
+    POWER_STATE_TYPE    PowerType;
+    POWER_ACTION        PowerAction;
+    NTSTATUS            status;
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+    PowerType = StackLocation->Parameters.Power.Type;
+    PowerAction = StackLocation->Parameters.Power.ShutdownType;
+
+    if (PowerAction >= PowerActionShutdown) {
+        Irp->IoStatus.Status = STATUS_SUCCESS;
+
+        status = Irp->IoStatus.Status;
+        IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+        goto done;
+    }
+
+    switch (PowerType) {
+    case DevicePowerState:
+        IoMarkIrpPending(Irp);
+
+        ASSERT3P(Pdo->DevicePowerIrp, == , NULL);
+        Pdo->DevicePowerIrp = Irp;
+        KeMemoryBarrier();
+
+        ThreadWake(Pdo->DevicePowerThread);
+
+        status = STATUS_PENDING;
+        break;
+
+    case SystemPowerState:
+        IoMarkIrpPending(Irp);
+
+        ASSERT3P(Pdo->SystemPowerIrp, == , NULL);
+        Pdo->SystemPowerIrp = Irp;
+        KeMemoryBarrier();
+
+        ThreadWake(Pdo->SystemPowerThread);
+
+        status = STATUS_PENDING;
+        break;
+
+    default:
+        status = Irp->IoStatus.Status;
+        IoCompleteRequest(Irp, IO_NO_INCREMENT);
+        break;
+    }
+
+done:
+    return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoQueryPower(
+    IN  PXENCONS_PDO    Pdo,
+    IN  PIRP            Irp
+    )
+{
+    NTSTATUS            status;
+
+    UNREFERENCED_PARAMETER(Pdo);
+
+    Irp->IoStatus.Status = STATUS_SUCCESS;
+
+    status = Irp->IoStatus.Status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoDispatchPower(
+    IN  PXENCONS_PDO    Pdo,
+    IN  PIRP            Irp
+    )
+{
+    PIO_STACK_LOCATION  StackLocation;
+    UCHAR               MinorFunction;
+    NTSTATUS            status;
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+    MinorFunction = StackLocation->MinorFunction;
+
+    switch (StackLocation->MinorFunction) {
+    case IRP_MN_SET_POWER:
+        status = PdoSetPower(Pdo, Irp);
+        break;
+
+    case IRP_MN_QUERY_POWER:
+        status = PdoQueryPower(Pdo, Irp);
+        break;
+
+    default:
+        status = Irp->IoStatus.Status;
+        IoCompleteRequest(Irp, IO_NO_INCREMENT);
+        break;
+    }
+
+    return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoDispatchDefault(
+    IN  PXENCONS_PDO    Pdo,
+    IN  PIRP            Irp
+    )
+{
+    NTSTATUS            status;
+
+    UNREFERENCED_PARAMETER(Pdo);
+
+    status = Irp->IoStatus.Status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+NTSTATUS
+PdoDispatch(
+    IN  PXENCONS_PDO    Pdo,
+    IN  PIRP            Irp
+    )
+{
+    PIO_STACK_LOCATION  StackLocation;
+    NTSTATUS            status;
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+
+    switch (StackLocation->MajorFunction) {
+    case IRP_MJ_PNP:
+        status = PdoDispatchPnp(Pdo, Irp);
+        break;
+
+    case IRP_MJ_POWER:
+        status = PdoDispatchPower(Pdo, Irp);
+        break;
+
+    default:
+        status = PdoDispatchDefault(Pdo, Irp);
+        break;
+    }
+
+    return status;
+}
+
+NTSTATUS
+PdoResume(
+    IN  PXENCONS_PDO    Pdo
+    )
+{
+    Trace("(%s) ====>\n", __PdoGetName(Pdo));
+    Trace("(%s) <====\n", __PdoGetName(Pdo));
+    return STATUS_SUCCESS;
+}
+
+VOID
+PdoSuspend(
+    IN  PXENCONS_PDO    Pdo
+    )
+{
+    Trace("(%s) ====>\n", __PdoGetName(Pdo));
+    Trace("(%s) <====\n", __PdoGetName(Pdo));
+}
+
+NTSTATUS
+PdoCreate(
+    IN  PXENCONS_FDO    Fdo,
+    IN  PANSI_STRING    Device
+    )
+{
+    PDEVICE_OBJECT      PhysicalDeviceObject;
+    PXENCONS_DX         Dx;
+    PXENCONS_PDO        Pdo;
+    NTSTATUS            status;
+
+#pragma prefast(suppress:28197) // Possibly leaking memory 
'PhysicalDeviceObject'
+    status = IoCreateDevice(DriverGetDriverObject(),
+                            sizeof(XENCONS_DX),
+                            NULL,
+                            FILE_DEVICE_UNKNOWN,
+                            FILE_DEVICE_SECURE_OPEN | 
FILE_AUTOGENERATED_DEVICE_NAME,
+                            FALSE,
+                            &PhysicalDeviceObject);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    Dx = (PXENCONS_DX)PhysicalDeviceObject->DeviceExtension;
+    RtlZeroMemory(Dx, sizeof(XENCONS_DX));
+
+    Dx->Type = PHYSICAL_DEVICE_OBJECT;
+    Dx->DeviceObject = PhysicalDeviceObject;
+    Dx->DevicePnpState = Present;
+    Dx->SystemPowerState = PowerSystemWorking;
+    Dx->DevicePowerState = PowerDeviceD3;
+
+    Pdo = __PdoAllocate(sizeof(XENCONS_PDO));
+
+    status = STATUS_NO_MEMORY;
+    if (Pdo == NULL)
+        goto fail2;
+
+    Pdo->Dx = Dx;
+    Pdo->Fdo = Fdo;
+
+    status = ThreadCreate(PdoSystemPower, Pdo, &Pdo->SystemPowerThread);
+    if (!NT_SUCCESS(status))
+        goto fail3;
+
+    status = ThreadCreate(PdoDevicePower, Pdo, &Pdo->DevicePowerThread);
+    if (!NT_SUCCESS(status))
+        goto fail4;
+
+    __PdoSetName(Pdo, Device);
+
+    FdoGetSuspendInterface(Fdo, &Pdo->SuspendInterface);
+
+    Dx->Pdo = Pdo;
+
+    status = FdoAddPhysicalDeviceObject(Fdo, Pdo);
+    if (!NT_SUCCESS(status))
+        goto fail5;
+
+    status = STATUS_UNSUCCESSFUL;
+    if (__PdoIsEjectRequested(Pdo))
+        goto fail6;
+
+    Info("%p (%s)\n",
+         PhysicalDeviceObject,
+         __PdoGetName(Pdo));
+
+    PhysicalDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
+    return STATUS_SUCCESS;
+
+fail6:
+    Error("fail6\n");
+
+    FdoRemovePhysicalDeviceObject(Fdo, Pdo);
+
+fail5:
+    Error("fail5\n");
+
+    (VOID)__PdoClearEjectRequested(Pdo);
+
+    Dx->Pdo = NULL;
+
+    RtlZeroMemory(&Pdo->SuspendInterface,
+                  sizeof(XENBUS_SUSPEND_INTERFACE));
+
+    ThreadAlert(Pdo->DevicePowerThread);
+    ThreadJoin(Pdo->DevicePowerThread);
+    Pdo->DevicePowerThread = NULL;
+
+fail4:
+    Error("fail4\n");
+
+    ThreadAlert(Pdo->SystemPowerThread);
+    ThreadJoin(Pdo->SystemPowerThread);
+    Pdo->SystemPowerThread = NULL;
+
+fail3:
+    Error("fail3\n");
+
+    Pdo->Fdo = NULL;
+    Pdo->Dx = NULL;
+
+    ASSERT(IsZeroMemory(Pdo, sizeof(XENCONS_PDO)));
+    __PdoFree(Pdo);
+
+fail2:
+    Error("fail2\n");
+
+    IoDeleteDevice(PhysicalDeviceObject);
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+VOID
+PdoDestroy(
+    IN  PXENCONS_PDO    Pdo
+    )
+{
+    PXENCONS_DX         Dx = Pdo->Dx;
+    PDEVICE_OBJECT      PhysicalDeviceObject = Dx->DeviceObject;
+    PXENCONS_FDO        Fdo = __PdoGetFdo(Pdo);
+
+    ASSERT3U(__PdoGetDevicePnpState(Pdo), == , Deleted);
+
+    ASSERT(__PdoIsMissing(Pdo));
+    Pdo->Missing = FALSE;
+
+    Info("%p (%s) (%s)\n",
+         PhysicalDeviceObject,
+         __PdoGetName(Pdo),
+         Pdo->Reason);
+
+    Pdo->Reason = NULL;
+
+    FdoRemovePhysicalDeviceObject(Fdo, Pdo);
+
+    (VOID)__PdoClearEjectRequested(Pdo);
+
+    Dx->Pdo = NULL;
+
+    RtlZeroMemory(&Pdo->SuspendInterface,
+                  sizeof(XENBUS_SUSPEND_INTERFACE));
+
+    ThreadAlert(Pdo->DevicePowerThread);
+    ThreadJoin(Pdo->DevicePowerThread);
+    Pdo->DevicePowerThread = NULL;
+
+    ThreadAlert(Pdo->SystemPowerThread);
+    ThreadJoin(Pdo->SystemPowerThread);
+    Pdo->SystemPowerThread = NULL;
+
+    Pdo->Fdo = NULL;
+    Pdo->Dx = NULL;
+
+    ASSERT(IsZeroMemory(Pdo, sizeof(XENCONS_PDO)));
+    __PdoFree(Pdo);
+
+    IoDeleteDevice(PhysicalDeviceObject);
+}
+
diff --git a/src/xencons/pdo.h b/src/xencons/pdo.h
new file mode 100755
index 0000000..0605483
--- /dev/null
+++ b/src/xencons/pdo.h
@@ -0,0 +1,113 @@
+/* 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 _XENCONS_PDO_H
+#define _XENCONS_PDO_H
+
+#include <ntddk.h>
+
+#include "driver.h"
+
+extern VOID
+PdoSetDevicePnpState(
+    IN  PXENCONS_PDO        Pdo,
+    IN  DEVICE_PNP_STATE    State
+    );
+
+extern DEVICE_PNP_STATE
+PdoGetDevicePnpState(
+    IN  PXENCONS_PDO    Pdo
+    );
+
+extern VOID
+PdoSetMissing(
+    IN  PXENCONS_PDO    Pdo,
+    IN  const CHAR      *Reason
+    );
+
+extern BOOLEAN
+PdoIsMissing(
+    IN  PXENCONS_PDO    Pdo
+    );
+
+extern VOID
+PdoRequestEject(
+    IN  PXENCONS_PDO    Pdo
+    );
+
+extern BOOLEAN
+PdoIsEjectRequested(
+    IN  PXENCONS_PDO    Pdo
+    );
+
+extern PCHAR
+PdoGetName(
+    IN  PXENCONS_PDO    Pdo
+    );
+
+extern PXENCONS_FDO
+PdoGetFdo(
+    IN  PXENCONS_PDO    Pdo
+    );
+
+extern PDEVICE_OBJECT
+PdoGetDeviceObject(
+    IN  PXENCONS_PDO    Pdo
+    );
+
+extern NTSTATUS
+PdoCreate(
+    IN  PXENCONS_FDO    Fdo,
+    IN  PANSI_STRING    Device
+    );
+
+extern NTSTATUS
+PdoResume(
+    IN  PXENCONS_PDO    Pdo
+    );
+
+extern VOID
+PdoSuspend(
+    IN  PXENCONS_PDO    Pdo
+    );
+
+extern VOID
+PdoDestroy(
+    IN  PXENCONS_PDO    Pdo
+    );
+
+extern NTSTATUS
+PdoDispatch(
+    IN  PXENCONS_PDO    Pdo,
+    IN  PIRP            Irp
+    );
+
+#endif  // _XENCONS_PDO_H
diff --git a/vs2015/xencons/xencons.vcxproj b/vs2015/xencons/xencons.vcxproj
index 12f5b70..2e9d208 100644
--- a/vs2015/xencons/xencons.vcxproj
+++ b/vs2015/xencons/xencons.vcxproj
@@ -66,6 +66,7 @@
   <ItemGroup>
     <ClCompile Include="../../src/xencons/driver.c" />
     <ClCompile Include="../../src/xencons/fdo.c" />
+    <ClCompile Include="../../src/xencons/pdo.c" />
     <ClCompile Include="../../src/xencons/registry.c" />
     <ClCompile Include="../../src/xencons/console.c" />
     <ClCompile Include="../../src/xencons/stream.c" />
diff --git a/vs2017/xencons/xencons.vcxproj b/vs2017/xencons/xencons.vcxproj
index 510acb6..6c67fe8 100644
--- a/vs2017/xencons/xencons.vcxproj
+++ b/vs2017/xencons/xencons.vcxproj
@@ -74,6 +74,7 @@
   <ItemGroup>
     <ClCompile Include="../../src/xencons/driver.c" />
     <ClCompile Include="../../src/xencons/fdo.c" />
+    <ClCompile Include="../../src/xencons/pdo.c" />
     <ClCompile Include="../../src/xencons/registry.c" />
     <ClCompile Include="../../src/xencons/console.c" />
     <ClCompile Include="../../src/xencons/stream.c" />
-- 
2.8.3


_______________________________________________
win-pv-devel mailing list
win-pv-devel@xxxxxxxxxxxxxxxxxxxx
https://lists.xenproject.org/mailman/listinfo/win-pv-devel

 


Rackspace

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