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

[win-pv-devel] [PATCH 06/14] Enumerate ~/device/console for non-default consoles



From: Owen Smith <owen.smith@xxxxxxxxxx>

Signed-off-by: Owen Smith <owen.smith@xxxxxxxxxx>
---
 src/xencons/driver.c           |    6 +
 src/xencons/driver.h           |   12 +-
 src/xencons/fdo.c              |  721 +++++++++++++++-
 src/xencons/fdo.h              |   43 +
 src/xencons/mutex.h            |   82 ++
 src/xencons/pdo.c              | 1786 ++++++++++++++++++++++++++++++++++++++++
 src/xencons/pdo.h              |  114 +++
 vs2015/xencons/xencons.vcxproj |    1 +
 8 files changed, 2755 insertions(+), 10 deletions(-)
 create mode 100644 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..beac030 100644
--- a/src/xencons/driver.c
+++ b/src/xencons/driver.c
@@ -202,6 +202,12 @@ Dispatch(
 
     status = STATUS_NOT_SUPPORTED;
     switch (Dx->Type) {
+    case PHYSICAL_DEVICE_OBJECT: {
+        PXENCONS_PDO Pdo = Dx->Pdo;
+
+        status = PdoDispatch(Pdo, Irp);
+        break;
+    }
     case FUNCTION_DEVICE_OBJECT: {
         PXENCONS_FDO Fdo = Dx->Fdo;
 
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 8e72dc6..5296c7f 100644
--- a/src/xencons/fdo.c
+++ b/src/xencons/fdo.c
@@ -49,6 +49,7 @@
 #include "fdo.h"
 #include "console.h"
 #include "thread.h"
+#include "mutex.h"
 #include "names.h"
 #include "dbg_print.h"
 #include "assert.h"
@@ -85,6 +86,12 @@ struct _XENCONS_FDO {
 
     CHAR                        VendorName[MAXNAMELEN];
 
+    PXENCONS_THREAD             ScanThread;
+    KEVENT                      ScanEvent;
+    PXENBUS_STORE_WATCH         ScanWatch;
+    MUTEX                       Mutex;
+    ULONG                       References;
+
     FDO_RESOURCE                Resource[RESOURCE_COUNT];
 
     PXENCONS_CONSOLE            Console;
@@ -210,6 +217,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 +373,14 @@ __FdoGetVendorName(
     return Fdo->VendorName;
 }
 
+PCHAR
+FdoGetVendorName(
+    IN  PXENCONS_FDO    Fdo
+    )
+{
+    return __FdoGetVendorName(Fdo);
+}
+
 static FORCEINLINE VOID
 __FdoSetName(
     IN  PXENCONS_FDO    Fdo
@@ -383,6 +406,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 +603,204 @@ 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;
+
+    if (Fdo->ScanThread)
+        ThreadWake(Fdo->ScanThread);
+}
+
+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 BOOLEAN
+__FdoEnumerate(
+    IN  PXENCONS_FDO    Fdo,
+    IN  PANSI_STRING    Devices
+    )
+{
+    BOOLEAN             NeedInvalidate;
+    HANDLE              ParametersKey;
+    ULONG               Enumerate;
+    PLIST_ENTRY         ListEntry;
+    ULONG               Index;
+    NTSTATUS            status;
+
+    Trace("====>\n");
+
+    NeedInvalidate = FALSE;
+
+    ParametersKey = DriverGetParametersKey();
+
+    status = RegistryQueryDwordValue(ParametersKey,
+                                     "Enumerate",
+                                     &Enumerate);
+    if (!NT_SUCCESS(status))
+        Enumerate = 1;
+
+    if (Enumerate == 0)
+        goto done;
+
+    __FdoAcquireMutex(Fdo);
+
+    ListEntry = Fdo->Dx->ListEntry.Flink;
+    while (ListEntry != &Fdo->Dx->ListEntry) {
+        PLIST_ENTRY     Next = ListEntry->Flink;
+        PXENCONS_DX     Dx = CONTAINING_RECORD(ListEntry, XENCONS_DX, 
ListEntry);
+        PXENCONS_PDO    Pdo = Dx->Pdo;
+
+        if (PdoGetDevicePnpState(Pdo) != Deleted) {
+            PCHAR           Name;
+            BOOLEAN         Missing;
+
+            Name = PdoGetName(Pdo);
+            Missing = TRUE;
+
+            // If the PDO already exists and its name is in the device list
+            // then we don't want to remove it.
+            for (Index = 0; Devices[Index].Buffer != NULL; Index++) {
+                PANSI_STRING Device = &Devices[Index];
+
+                if (Device->Length == 0)
+                    continue;
+
+                if (strcmp(Name, Device->Buffer) == 0) {
+                    Missing = FALSE;
+                    Device->Length = 0;  // avoid duplication
+                    break;
+                }
+            }
+
+            if (!PdoIsMissing(Pdo)) {
+                if (PdoIsEjectRequested(Pdo)) {
+                    IoRequestDeviceEject(PdoGetDeviceObject(Pdo));
+                } else if (Missing) {
+                    PdoSetMissing(Pdo, "device disappeared");
+
+                    // If the PDO has not yet been enumerated then we can
+                    // go ahead and mark it as deleted, otherwise we need
+                    // to notify PnP manager and wait for the REMOVE_DEVICE
+                    // IRP.
+                    if (PdoGetDevicePnpState(Pdo) == Present) {
+                        PdoSetDevicePnpState(Pdo, Deleted);
+                        PdoDestroy(Pdo);
+                    } else {
+                        NeedInvalidate = TRUE;
+                    }
+                }
+            }
+        }
+
+        ListEntry = Next;
+    }
+
+    // Walk the class list and create PDOs for any new device
+    for (Index = 0; Devices[Index].Buffer != NULL; Index++) {
+        PANSI_STRING Device = &Devices[Index];
+
+        if (Device->Length == 0)
+            continue;
+
+        status = PdoCreate(Fdo, Device);
+        if (NT_SUCCESS(status))
+            NeedInvalidate = TRUE;
+    }
+
+    __FdoReleaseMutex(Fdo);
+
+done:
+    Trace("<====\n");
+
+    return NeedInvalidate;
+}
+
 static FORCEINLINE PANSI_STRING
 __FdoMultiSzToUpcaseAnsi(
     IN  PCHAR       Buffer
@@ -565,6 +879,126 @@ __FdoFreeAnsi(
     __FdoFree(Ansi);
 }
 
+
+static NTSTATUS
+FdoScan(
+    PXENCONS_THREAD     Self,
+    PVOID               Context
+    )
+{
+    PXENCONS_FDO        Fdo = Context;
+    PKEVENT             Event;
+    HANDLE              ParametersKey;
+    NTSTATUS            status;
+
+    Trace("====>\n");
+
+    Event = ThreadGetEvent(Self);
+
+    ParametersKey = DriverGetParametersKey();
+
+    for (;;) {
+        PCHAR           Buffer;
+        PANSI_STRING    Devices;
+        PANSI_STRING    UnsupportedDevices;
+        ULONG           Index;
+        BOOLEAN         NeedInvalidate;
+
+        Trace("waiting...\n");
+
+        (VOID)KeWaitForSingleObject(Event,
+                                    Executive,
+                                    KernelMode,
+                                    FALSE,
+                                    NULL);
+        KeClearEvent(Event);
+
+        if (ThreadIsAlerted(Self))
+            break;
+
+        // It is not safe to use interfaces before this point
+        if (__FdoGetDevicePnpState(Fdo) != Started) {
+            KeSetEvent(&Fdo->ScanEvent, IO_NO_INCREMENT, FALSE);
+            continue;
+        }
+
+        status = XENBUS_STORE(Directory,
+                              &Fdo->StoreInterface,
+                              NULL,
+                              "device",
+                              "console",
+                              &Buffer);
+        if (NT_SUCCESS(status)) {
+            Devices = __FdoMultiSzToUpcaseAnsi(Buffer);
+
+            XENBUS_STORE(Free,
+                         &Fdo->StoreInterface,
+                         Buffer);
+        } else {
+            Devices = NULL;
+        }
+
+        if (Devices == NULL)
+            goto loop;
+
+        if (ParametersKey != NULL) {
+            status = RegistryQuerySzValue(ParametersKey,
+                                          "UnsupportedDevices",
+                                          NULL,
+                                          &UnsupportedDevices);
+            if (!NT_SUCCESS(status))
+                UnsupportedDevices = NULL;
+        } else {
+            UnsupportedDevices = NULL;
+        }
+
+        // NULL out anything in the Devices list that is in the
+        // UnsupportedDevices list    
+        for (Index = 0; Devices[Index].Buffer != NULL; Index++) {
+            PANSI_STRING    Device = &Devices[Index];
+            ULONG           Entry;
+            BOOLEAN         Supported;
+
+            Supported = TRUE;
+
+            for (Entry = 0;
+                 UnsupportedDevices != NULL && 
UnsupportedDevices[Entry].Buffer != NULL;
+                 Entry++) {
+                if (strncmp(Device->Buffer,
+                            UnsupportedDevices[Entry].Buffer,
+                            Device->Length) == 0) {
+                    Supported = FALSE;
+                    break;
+                }
+            }
+
+            if (!Supported)
+                Device->Length = 0;
+        }
+
+        if (UnsupportedDevices != NULL)
+            RegistryFreeSzValue(UnsupportedDevices);
+
+        NeedInvalidate = __FdoEnumerate(Fdo, Devices);
+
+        __FdoFreeAnsi(Devices);
+
+        if (NeedInvalidate) {
+            NeedInvalidate = FALSE;
+            IoInvalidateDeviceRelations(__FdoGetPhysicalDeviceObject(Fdo),
+                                        BusRelations);
+        }
+
+    loop:
+        KeSetEvent(&Fdo->ScanEvent, IO_NO_INCREMENT, FALSE);
+    }
+
+    KeSetEvent(&Fdo->ScanEvent, IO_NO_INCREMENT, FALSE);
+
+    Trace("<====\n");
+    return STATUS_SUCCESS;
+}
+
 static FORCEINLINE BOOLEAN
 __FdoMatchDistribution(
     IN  PXENCONS_FDO    Fdo,
@@ -796,12 +1230,34 @@ __FdoD3ToD0(
     if (!NT_SUCCESS(status))
         goto fail1;
 
+    status = XENBUS_STORE(WatchAdd,
+                          &Fdo->StoreInterface,
+                          "device",
+                          "console",
+                          ThreadGetEvent(Fdo->ScanThread),
+                          &Fdo->ScanWatch);
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    (VOID)XENBUS_STORE(Printf,
+                       &Fdo->StoreInterface,
+                       NULL,
+                       "feature/hotplug",
+                       "console",
+                       "%u",
+                       TRUE);
+
     (VOID) FdoSetDistribution(Fdo);
 
     Trace("<====\n");
 
     return STATUS_SUCCESS;
 
+fail2:
+    Error("fail2\n");
+
+    ConsoleDisable(Fdo->Console);
+
 fail1:
     Error("fail1 (%08x)\n", status);
 
@@ -819,6 +1275,17 @@ __FdoD0ToD3(
 
     FdoClearDistribution(Fdo);
 
+    (VOID)XENBUS_STORE(Remove,
+                       &Fdo->StoreInterface,
+                       NULL,
+                       "feature/hotplug",
+                       "console");
+
+    (VOID)XENBUS_STORE(WatchRemove,
+                       &Fdo->StoreInterface,
+                       Fdo->ScanWatch);
+    Fdo->ScanWatch = NULL;
+
     ConsoleDisable(Fdo->Console);
 
     Trace("<====\n");
@@ -844,9 +1311,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);
@@ -886,8 +1353,24 @@ 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);
+
 #pragma prefast(suppress:28123)
-    (VOID) IoSetDeviceInterfaceState(&Dx->Link, TRUE);
+    (VOID) IoSetDeviceInterfaceState(&Fdo->Dx->Link, TRUE);
 
     Trace("<====\n");
 
@@ -924,8 +1407,8 @@ FdoD0ToD3(
     IN  PXENCONS_FDO    Fdo
     )
 {
-    PXENCONS_DX         Dx = Fdo->Dx;
     POWER_STATE         PowerState;
+    PLIST_ENTRY         ListEntry;
     KIRQL               Irql;
 
     ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
@@ -934,7 +1417,26 @@ FdoD0ToD3(
     Trace("====>\n");
 
 #pragma prefast(suppress:28123)
-    (VOID) IoSetDeviceInterfaceState(&Dx->Link, FALSE);
+    (VOID) IoSetDeviceInterfaceState(&Fdo->Dx->Link, FALSE);
+
+    __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,
@@ -1004,20 +1506,36 @@ FdoStartDevice(
                       StackLocation->Parameters.StartDevice.AllocatedResources,
                       
StackLocation->Parameters.StartDevice.AllocatedResourcesTranslated);
 
-    status = FdoD3ToD0(Fdo);
+    KeInitializeEvent(&Fdo->ScanEvent, NotificationEvent, FALSE);
+
+    status = ThreadCreate(FdoScan, Fdo, &Fdo->ScanThread);
     if (!NT_SUCCESS(status))
         goto fail2;
 
+    status = FdoD3ToD0(Fdo);
+    if (!NT_SUCCESS(status))
+        goto fail3;
+
     __FdoSetDevicePnpState(Fdo, Started);
+    ThreadWake(Fdo->ScanThread);
 
     status = Irp->IoStatus.Status;
     IoCompleteRequest(Irp, IO_NO_INCREMENT);
 
     return status;
 
+fail3:
+    Error("fail3\n");
+
+    ThreadAlert(Fdo->ScanThread);
+    ThreadJoin(Fdo->ScanThread);
+    Fdo->ScanThread = NULL;
+
 fail2:
     Error("fail2\n");
 
+    RtlZeroMemory(&Fdo->ScanEvent, sizeof(KEVENT));
+
     RtlZeroMemory(&Fdo->Resource, sizeof (FDO_RESOURCE) * RESOURCE_COUNT);
 
 fail1:
@@ -1075,6 +1593,12 @@ FdoStopDevice(
     if (__FdoGetDevicePowerState(Fdo) == PowerDeviceD0)
         FdoD0ToD3(Fdo);
 
+    ThreadAlert(Fdo->ScanThread);
+    ThreadJoin(Fdo->ScanThread);
+    Fdo->ScanThread = NULL;
+
+    RtlZeroMemory(&Fdo->ScanEvent, sizeof(KEVENT));
+
     RtlZeroMemory(&Fdo->Resource, sizeof (FDO_RESOURCE) * RESOURCE_COUNT);
 
     __FdoSetDevicePnpState(Fdo, Stopped);
@@ -1127,10 +1651,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);
@@ -1145,6 +1686,7 @@ FdoRemoveDevice(
     IN  PIRP            Irp
     )
 {
+    PLIST_ENTRY         ListEntry;
     NTSTATUS            status;
 
     ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
@@ -1152,9 +1694,50 @@ FdoRemoveDevice(
     if (__FdoGetPreviousDevicePnpState(Fdo) != Started)
         goto done;
 
+    KeClearEvent(&Fdo->ScanEvent);
+    ThreadWake(Fdo->ScanThread);
+
+    Trace("waiting for scan thread\n");
+
+    (VOID)KeWaitForSingleObject(&Fdo->ScanEvent,
+                                Executive,
+                                KernelMode,
+                                FALSE,
+                                NULL);
+
+    __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);
 
+    ThreadAlert(Fdo->ScanThread);
+    ThreadJoin(Fdo->ScanThread);
+    Fdo->ScanThread = NULL;
+
+    RtlZeroMemory(&Fdo->ScanEvent, sizeof(KEVENT));
+
     RtlZeroMemory(&Fdo->Resource, sizeof (FDO_RESOURCE) * RESOURCE_COUNT);
 
 done:
@@ -1167,7 +1750,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;
 }
@@ -1179,16 +1768,121 @@ 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;
+    }
+
+    KeClearEvent(&Fdo->ScanEvent);
+    ThreadWake(Fdo->ScanThread);
+
+    Trace("waiting for scan thread\n");
+
+    (VOID)KeWaitForSingleObject(&Fdo->ScanEvent,
+                                Executive,
+                                KernelMode,
+                                FALSE,
+                                NULL);
+
+    __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;
 }
@@ -2566,6 +3260,11 @@ FdoCreate(
 
     Dx->Fdo = Fdo;
 
+    InitializeMutex(&Fdo->Mutex);
+    InitializeListHead(&Dx->ListEntry);
+
+    Fdo->References = 1;
+
     Info("%p (%s)\n",
          FunctionDeviceObject,
          __FdoGetName(Fdo));
@@ -2658,6 +3357,8 @@ FdoDestroy(
     PXENCONS_DX         Dx = Fdo->Dx;
     PDEVICE_OBJECT      FunctionDeviceObject = Dx->DeviceObject;
 
+    ASSERT(IsListEmpty(&Dx->ListEntry));
+    ASSERT3U(Fdo->References, == , 0);
     ASSERT3U(__FdoGetDevicePnpState(Fdo), ==, Deleted);
 
     Fdo->NotDisableable = FALSE;
@@ -2666,6 +3367,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..189c70f 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 100644
index 0000000..faf1a98
--- /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..7a9c1e6
--- /dev/null
+++ b/src/xencons/pdo.c
@@ -0,0 +1,1786 @@
+/* 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 "names.h"
+#include "fdo.h"
+#include "pdo.h"
+#include "driver.h"
+#include "registry.h"
+#include "thread.h"
+#include "dbg_print.h"
+#include "assert.h"
+#include "util.h"
+#include "version.h"
+
+#define PDO_POOL 'ODP'
+
+#define MAXNAMELEN  128
+
+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 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    Name
+    )
+{
+    PXENCONS_DX         Dx = Pdo->Dx;
+    NTSTATUS            status;
+
+    status = RtlStringCbPrintfA(Dx->Name,
+                                MAX_DEVICE_ID_LEN,
+                                "%Z",
+                                Name);
+    ASSERT(NT_SUCCESS(status));
+}
+
+static FORCEINLINE PCHAR
+__PdoGetName(
+    IN  PXENCONS_PDO    Pdo
+    )
+{
+    PXENCONS_DX         Dx = Pdo->Dx;
+
+    return Dx->Name;
+}
+
+PCHAR
+PdoGetName(
+    IN  PXENCONS_PDO    Pdo
+    )
+{
+    return __PdoGetName(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 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 PCHAR
+__PdoGetVendorName(
+    IN  PXENCONS_PDO    Pdo
+    )
+{
+    return FdoGetVendorName(__PdoGetFdo(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;
+
+    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);
+
+    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;
+
+    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);
+}
+
+// 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))
+    //    FrontendEjectFailed(__PdoGetFrontend(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;
+}
+
+#define  MAXTEXTLEN     1024
+
+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",
+                                    FdoGetName(__PdoGetFdo(Pdo)),
+                                    __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_CONS",
+                                    __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_CONS",
+                                    __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(Pdo->Fdo, 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;
+}
+
+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));
+    } else if (__PdoGetSystemPowerState(Pdo) < 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
+    )
+{
+    UNREFERENCED_PARAMETER(Pdo);
+    return STATUS_SUCCESS;
+}
+
+VOID
+PdoSuspend(
+    IN  PXENCONS_PDO    Pdo
+    )
+{
+    UNREFERENCED_PARAMETER(Pdo);
+}
+
+NTSTATUS
+PdoCreate(
+    IN  PXENCONS_FDO    Fdo,
+    IN  PANSI_STRING    Name
+    )
+{
+    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, Name);
+
+    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_BUFFERED_IO;
+    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..2164542
--- /dev/null
+++ b/src/xencons/pdo.h
@@ -0,0 +1,114 @@
+/* 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"
+#include "types.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    Name
+    );
+
+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" />
-- 
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®.