[USB] Update the names of new USB drivers
[reactos.git] / drivers / usb / usbhub / power.c
diff --git a/drivers/usb/usbhub/power.c b/drivers/usb/usbhub/power.c
new file mode 100644 (file)
index 0000000..f2b1e34
--- /dev/null
@@ -0,0 +1,801 @@
+/*
+ * PROJECT:     ReactOS USB Hub Driver
+ * LICENSE:     GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
+ * PURPOSE:     USBHub power handling functions
+ * COPYRIGHT:   Copyright 2017 Vadim Galyant <vgal@rambler.ru>
+ */
+
+#include "usbhub.h"
+
+#define NDEBUG
+#include <debug.h>
+
+#define NDEBUG_USBHUB_POWER
+#include "dbg_uhub.h"
+
+VOID
+NTAPI
+USBH_CompletePowerIrp(IN PUSBHUB_FDO_EXTENSION HubExtension,
+                      IN PIRP Irp,
+                      IN NTSTATUS NtStatus)
+{
+    DPRINT("USBH_CompletePowerIrp: HubExtension - %p, Irp - %p, NtStatus - %lX\n",
+           HubExtension,
+           Irp,
+           NtStatus);
+
+    Irp->IoStatus.Status = NtStatus;
+
+    PoStartNextPowerIrp(Irp);
+
+    if (!InterlockedDecrement(&HubExtension->PendingRequestCount))
+    {
+        KeSetEvent(&HubExtension->PendingRequestEvent,
+                   EVENT_INCREMENT,
+                   FALSE);
+    }
+
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+}
+
+VOID
+NTAPI
+USBH_HubCancelWakeIrp(IN PUSBHUB_FDO_EXTENSION HubExtension,
+                      IN PIRP Irp)
+{
+    DPRINT("USBH_HubCancelWakeIrp: HubExtension - %p, Irp - %p\n",
+           HubExtension,
+           Irp);
+
+    IoCancelIrp(Irp);
+
+    if (InterlockedExchange((PLONG)&HubExtension->FdoWaitWakeLock, 1))
+    {
+        PoStartNextPowerIrp(Irp);
+        Irp->IoStatus.Status = STATUS_CANCELLED;
+        IoCompleteRequest(Irp, IO_NO_INCREMENT);
+    }
+}
+
+VOID
+NTAPI
+USBH_HubESDRecoverySetD3Completion(IN PDEVICE_OBJECT DeviceObject,
+                                   IN UCHAR MinorFunction,
+                                   IN POWER_STATE PowerState,
+                                   IN PVOID Context,
+                                   IN PIO_STATUS_BLOCK IoStatus)
+{
+    DPRINT("USBH_HubESDRecoverySetD3Completion ... \n");
+
+    KeSetEvent((PRKEVENT)Context,
+               EVENT_INCREMENT,
+               FALSE);
+}
+
+NTSTATUS
+NTAPI
+USBH_HubSetD0(IN PUSBHUB_FDO_EXTENSION HubExtension)
+{
+    PUSBHUB_FDO_EXTENSION RootHubDevExt;
+    NTSTATUS Status;
+    KEVENT Event;
+    POWER_STATE PowerState;
+
+    DPRINT("USBH_HubSetD0: HubExtension - %p\n", HubExtension);
+
+    RootHubDevExt = USBH_GetRootHubExtension(HubExtension);
+
+    if (RootHubDevExt->SystemPowerState.SystemState != PowerSystemWorking)
+    {
+        Status = STATUS_INVALID_DEVICE_STATE;
+        return Status;
+    }
+
+    if (HubExtension->HubFlags & USBHUB_FDO_FLAG_WAIT_IDLE_REQUEST)
+    {
+        DPRINT("USBH_HubSetD0: HubFlags - %lX\n", HubExtension->HubFlags);
+
+        KeWaitForSingleObject(&HubExtension->IdleEvent,
+                              Suspended,
+                              KernelMode,
+                              FALSE,
+                              NULL);
+    }
+
+    KeInitializeEvent(&Event, NotificationEvent, FALSE);
+
+    PowerState.DeviceState = PowerDeviceD0;
+
+    Status = PoRequestPowerIrp(HubExtension->LowerPDO,
+                               IRP_MN_SET_POWER,
+                               PowerState,
+                               USBH_HubESDRecoverySetD3Completion,
+                               &Event,
+                               NULL);
+
+    if (Status == STATUS_PENDING)
+    {
+       Status = KeWaitForSingleObject(&Event,
+                                      Suspended,
+                                      KernelMode,
+                                      FALSE,
+                                      NULL);
+    }
+
+    while (HubExtension->HubFlags & USBHUB_FDO_FLAG_WAKEUP_START)
+    {
+        USBH_Wait(10);
+    }
+
+    return Status;
+}
+
+VOID
+NTAPI
+USBH_IdleCancelPowerHubWorker(IN PUSBHUB_FDO_EXTENSION HubExtension,
+                              IN PVOID Context)
+{
+    PUSBHUB_IDLE_PORT_CANCEL_CONTEXT WorkItemIdlePower;
+    PIRP Irp;
+
+    DPRINT("USBH_IdleCancelPowerHubWorker: ... \n");
+
+    WorkItemIdlePower = Context;
+
+    if (HubExtension &&
+        HubExtension->CurrentPowerState.DeviceState != PowerDeviceD0 &&
+        HubExtension->HubFlags & USBHUB_FDO_FLAG_DEVICE_STARTED)
+    {
+        USBH_HubSetD0(HubExtension);
+    }
+
+    Irp = WorkItemIdlePower->Irp;
+    Irp->IoStatus.Status = STATUS_CANCELLED;
+
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+}
+
+VOID
+NTAPI
+USBH_HubQueuePortWakeIrps(IN PUSBHUB_FDO_EXTENSION HubExtension,
+                          IN PLIST_ENTRY ListIrps)
+{
+    PDEVICE_OBJECT PortDevice;
+    PUSBHUB_PORT_PDO_EXTENSION PortExtension;
+    USHORT NumPorts;
+    USHORT Port;
+    PIRP WakeIrp;
+    KIRQL OldIrql;
+
+    DPRINT("USBH_HubQueuePortWakeIrps ... \n");
+
+    NumPorts = HubExtension->HubDescriptor->bNumberOfPorts;
+
+    InitializeListHead(ListIrps);
+
+    IoAcquireCancelSpinLock(&OldIrql);
+
+    for (Port = 0; Port < NumPorts; ++Port)
+    {
+        PortDevice = HubExtension->PortData[Port].DeviceObject;
+
+        if (PortDevice)
+        {
+            PortExtension = PortDevice->DeviceExtension;
+
+            WakeIrp = PortExtension->PdoWaitWakeIrp;
+            PortExtension->PdoWaitWakeIrp = NULL;
+
+            if (WakeIrp)
+            {
+                DPRINT1("USBH_HubQueuePortWakeIrps: UNIMPLEMENTED. FIXME\n");
+                DbgBreakPoint();
+            }
+        }
+    }
+
+    IoReleaseCancelSpinLock(OldIrql);
+}
+
+VOID
+NTAPI
+USBH_HubCompleteQueuedPortWakeIrps(IN PUSBHUB_FDO_EXTENSION HubExtension,
+                                   IN PLIST_ENTRY ListIrps,
+                                   IN NTSTATUS NtStatus)
+{
+    DPRINT("USBH_HubCompleteQueuedPortWakeIrps ... \n");
+
+    while (!IsListEmpty(ListIrps))
+    {
+        DPRINT1("USBH_HubCompleteQueuedPortWakeIrps: UNIMPLEMENTED. FIXME\n");
+        DbgBreakPoint();
+    }
+}
+
+VOID
+NTAPI
+USBH_HubCompletePortWakeIrps(IN PUSBHUB_FDO_EXTENSION HubExtension,
+                             IN NTSTATUS NtStatus)
+{
+    LIST_ENTRY ListIrps;
+
+    DPRINT("USBH_HubCompletePortWakeIrps: NtStatus - %x\n", NtStatus);
+
+    if (HubExtension->HubFlags & USBHUB_FDO_FLAG_DEVICE_STARTED)
+    {
+        USBH_HubQueuePortWakeIrps(HubExtension, &ListIrps);
+
+        USBH_HubCompleteQueuedPortWakeIrps(HubExtension,
+                                           &ListIrps,
+                                           NtStatus);
+    }
+}
+
+VOID
+NTAPI
+USBH_FdoPoRequestD0Completion(IN PDEVICE_OBJECT DeviceObject,
+                              IN UCHAR MinorFunction,
+                              IN POWER_STATE PowerState,
+                              IN PVOID Context,
+                              IN PIO_STATUS_BLOCK IoStatus)
+{
+    PUSBHUB_FDO_EXTENSION HubExtension;
+
+    DPRINT("USBH_FdoPoRequestD0Completion ... \n");
+
+    HubExtension = Context;
+
+    USBH_HubCompletePortWakeIrps(HubExtension, STATUS_SUCCESS);
+
+    HubExtension->HubFlags &= ~USBHUB_FDO_FLAG_WAKEUP_START;
+
+    if (!InterlockedDecrement(&HubExtension->PendingRequestCount))
+    {
+        KeSetEvent(&HubExtension->PendingRequestEvent,
+                   EVENT_INCREMENT,
+                   FALSE);
+    }
+}
+
+VOID
+NTAPI
+USBH_CompletePortWakeIrpsWorker(IN PUSBHUB_FDO_EXTENSION HubExtension,
+                                IN PVOID Context)
+{
+    DPRINT1("USBH_CompletePortWakeIrpsWorker: UNIMPLEMENTED. FIXME\n");
+    DbgBreakPoint();
+}
+
+NTSTATUS
+NTAPI
+USBH_FdoWWIrpIoCompletion(IN PDEVICE_OBJECT DeviceObject,
+                          IN PIRP Irp,
+                          IN PVOID Context)
+{
+    PUSBHUB_FDO_EXTENSION HubExtension;
+    NTSTATUS Status;
+    KIRQL OldIrql;
+    POWER_STATE PowerState;
+    PIRP WakeIrp;
+
+    DPRINT("USBH_FdoWWIrpIoCompletion: DeviceObject - %p, Irp - %p\n",
+            DeviceObject,
+            Irp);
+
+    HubExtension = Context;
+
+    Status = Irp->IoStatus.Status;
+
+    IoAcquireCancelSpinLock(&OldIrql);
+
+    HubExtension->HubFlags &= ~USBHUB_FDO_FLAG_PENDING_WAKE_IRP;
+
+    WakeIrp = InterlockedExchangePointer((PVOID *)&HubExtension->PendingWakeIrp,
+                                         NULL);
+
+    if (!InterlockedDecrement(&HubExtension->PendingRequestCount))
+    {
+        KeSetEvent(&HubExtension->PendingRequestEvent,
+                   EVENT_INCREMENT,
+                   FALSE);
+    }
+
+    IoReleaseCancelSpinLock(OldIrql);
+
+    DPRINT("USBH_FdoWWIrpIoCompletion: Status - %lX\n", Status);
+
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("USBH_FdoWWIrpIoCompletion: DbgBreakPoint() \n");
+        DbgBreakPoint();
+    }
+    else
+    {
+        PowerState.DeviceState = PowerDeviceD0;
+
+        HubExtension->HubFlags |= USBHUB_FDO_FLAG_WAKEUP_START;
+        InterlockedIncrement(&HubExtension->PendingRequestCount);
+
+        Status = STATUS_SUCCESS;
+
+        PoRequestPowerIrp(HubExtension->LowerPDO,
+                          IRP_MN_SET_POWER,
+                          PowerState,
+                          USBH_FdoPoRequestD0Completion,
+                          (PVOID)HubExtension,
+                          NULL);
+    }
+
+    if (!WakeIrp)
+    {
+        if (!InterlockedExchange(&HubExtension->FdoWaitWakeLock, 1))
+        {
+            Status = STATUS_MORE_PROCESSING_REQUIRED;
+        }
+    }
+
+    DPRINT("USBH_FdoWWIrpIoCompletion: Status - %lX\n", Status);
+
+    if (Status != STATUS_MORE_PROCESSING_REQUIRED)
+    {
+        PoStartNextPowerIrp(Irp);
+    }
+
+    return Status;
+}
+
+NTSTATUS
+NTAPI
+USBH_PowerIrpCompletion(IN PDEVICE_OBJECT DeviceObject,
+                        IN PIRP Irp,
+                        IN PVOID Context)
+{
+    PUSBHUB_FDO_EXTENSION HubExtension;
+    PIO_STACK_LOCATION IoStack;
+    DEVICE_POWER_STATE OldDeviceState;
+    NTSTATUS Status;
+    POWER_STATE PowerState;
+
+    DPRINT("USBH_PowerIrpCompletion: DeviceObject - %p, Irp - %p\n",
+           DeviceObject,
+           Irp);
+
+    HubExtension = Context;
+
+    IoStack = IoGetCurrentIrpStackLocation(Irp);
+    PowerState = IoStack->Parameters.Power.State;
+
+    Status = Irp->IoStatus.Status;
+    DPRINT("USBH_PowerIrpCompletion: Status - %lX\n", Status);
+
+    if (!NT_SUCCESS(Status))
+    {
+        if (PowerState.DeviceState == PowerDeviceD0)
+        {
+            PoStartNextPowerIrp(Irp);
+            HubExtension->HubFlags &= ~USBHUB_FDO_FLAG_SET_D0_STATE;
+        }
+    }
+    else if (PowerState.DeviceState == PowerDeviceD0)
+    {
+        HubExtension->HubFlags &= ~USBHUB_FDO_FLAG_SET_D0_STATE;
+
+        OldDeviceState = HubExtension->CurrentPowerState.DeviceState;
+        HubExtension->CurrentPowerState.DeviceState = PowerDeviceD0;
+
+        DPRINT("USBH_PowerIrpCompletion: OldDeviceState - %x\n", OldDeviceState);
+
+        if (HubExtension->HubFlags & USBHUB_FDO_FLAG_HIBERNATE_STATE)
+        {
+            DPRINT1("USBH_PowerIrpCompletion: USBHUB_FDO_FLAG_HIBERNATE_STATE. FIXME\n");
+            DbgBreakPoint();
+        }
+
+        HubExtension->HubFlags &= ~USBHUB_FDO_FLAG_HIBERNATE_STATE;
+
+        if (OldDeviceState == PowerDeviceD3)
+        {
+            DPRINT1("USBH_PowerIrpCompletion: PowerDeviceD3. FIXME\n");
+            DbgBreakPoint();
+        }
+
+        if (!(HubExtension->HubFlags & USBHUB_FDO_FLAG_DEVICE_STOPPED) &&
+            HubExtension->HubFlags & USBHUB_FDO_FLAG_DO_ENUMERATION)
+        {
+            USBH_SubmitStatusChangeTransfer(HubExtension);
+        }
+
+        DPRINT("USBH_PowerIrpCompletion: Status - %lX\n", Status);
+
+        if (Status != STATUS_MORE_PROCESSING_REQUIRED)
+        {
+            PoStartNextPowerIrp(Irp);
+            return Status;
+        }
+    }
+
+    return Status;
+}
+
+VOID
+NTAPI
+USBH_FdoDeferPoRequestCompletion(IN PDEVICE_OBJECT DeviceObject,
+                                 IN UCHAR MinorFunction,
+                                 IN POWER_STATE PowerState,
+                                 IN PVOID Context,
+                                 IN PIO_STATUS_BLOCK IoStatus)
+{
+    PUSBHUB_FDO_EXTENSION Extension;
+    PUSBHUB_FDO_EXTENSION HubExtension = NULL;
+    PIRP PowerIrp;
+    PIO_STACK_LOCATION IoStack;
+
+    DPRINT("USBH_FdoDeferPoRequestCompletion ... \n");
+
+    Extension = Context;
+
+    PowerIrp = Extension->PowerIrp;
+
+    if (Extension->Common.ExtensionType == USBH_EXTENSION_TYPE_HUB)
+    {
+        HubExtension = Context;
+    }
+
+    IoStack = IoGetCurrentIrpStackLocation(PowerIrp);
+
+    if (IoStack->Parameters.Power.State.SystemState == PowerSystemWorking &&
+        HubExtension && HubExtension->LowerPDO == HubExtension->RootHubPdo)
+    {
+        HubExtension->SystemPowerState.SystemState = PowerSystemWorking;
+        USBH_CheckIdleDeferred(HubExtension);
+    }
+
+    IoCopyCurrentIrpStackLocationToNext(PowerIrp);
+    PoStartNextPowerIrp(PowerIrp);
+    PoCallDriver(Extension->LowerDevice, PowerIrp);
+}
+
+NTSTATUS
+NTAPI
+USBH_FdoPower(IN PUSBHUB_FDO_EXTENSION HubExtension,
+              IN PIRP Irp,
+              IN UCHAR Minor)
+{
+    NTSTATUS Status;
+    PIO_STACK_LOCATION IoStack;
+    POWER_STATE PowerState;
+    POWER_STATE DevicePwrState;
+    BOOLEAN IsAllPortsD3;
+    PUSBHUB_PORT_DATA PortData;
+    PDEVICE_OBJECT PdoDevice;
+    PUSBHUB_PORT_PDO_EXTENSION PortExtension;
+    ULONG Port;
+
+    DPRINT_PWR("USBH_FdoPower: HubExtension - %p, Irp - %p, Minor - %X\n",
+               HubExtension,
+               Irp,
+               Minor);
+
+    switch (Minor)
+    {
+        case IRP_MN_WAIT_WAKE:
+            DPRINT_PWR("USBH_FdoPower: IRP_MN_WAIT_WAKE\n");
+
+            IoCopyCurrentIrpStackLocationToNext(Irp);
+
+            IoSetCompletionRoutine(Irp,
+                                   USBH_FdoWWIrpIoCompletion,
+                                   HubExtension,
+                                   TRUE,
+                                   TRUE,
+                                   TRUE);
+
+            PoStartNextPowerIrp(Irp);
+            IoMarkIrpPending(Irp);
+            PoCallDriver(HubExtension->LowerDevice, Irp);
+
+            return STATUS_PENDING;
+
+        case IRP_MN_POWER_SEQUENCE:
+            DPRINT_PWR("USBH_FdoPower: IRP_MN_POWER_SEQUENCE\n");
+            break;
+
+        case IRP_MN_SET_POWER:
+            DPRINT_PWR("USBH_FdoPower: IRP_MN_SET_POWER\n");
+
+            IoStack = IoGetCurrentIrpStackLocation(Irp);
+            DPRINT_PWR("USBH_FdoPower: IRP_MN_SET_POWER/DevicePowerState\n");
+            PowerState = IoStack->Parameters.Power.State;
+
+            if (IoStack->Parameters.Power.Type == DevicePowerState)
+            {
+                DPRINT_PWR("USBH_FdoPower: PowerState - %x\n",
+                           PowerState.DeviceState);
+
+                if (HubExtension->CurrentPowerState.DeviceState == PowerState.DeviceState)
+                {
+                    IoCopyCurrentIrpStackLocationToNext(Irp);
+
+                    PoStartNextPowerIrp(Irp);
+                    IoMarkIrpPending(Irp);
+                    PoCallDriver(HubExtension->LowerDevice, Irp);
+
+                    return STATUS_PENDING;
+                }
+
+                switch (PowerState.DeviceState)
+                {
+                    case PowerDeviceD0:
+                        if (!(HubExtension->HubFlags & USBHUB_FDO_FLAG_SET_D0_STATE))
+                        {
+                            HubExtension->HubFlags &= ~(USBHUB_FDO_FLAG_NOT_D0_STATE |
+                                                        USBHUB_FDO_FLAG_DEVICE_STOPPING);
+
+                            HubExtension->HubFlags |= USBHUB_FDO_FLAG_SET_D0_STATE;
+
+                            IoCopyCurrentIrpStackLocationToNext(Irp);
+
+                            IoSetCompletionRoutine(Irp,
+                                                   USBH_PowerIrpCompletion,
+                                                   HubExtension,
+                                                   TRUE,
+                                                   TRUE,
+                                                   TRUE);
+                        }
+                        else
+                        {
+                            IoCopyCurrentIrpStackLocationToNext(Irp);
+                            PoStartNextPowerIrp(Irp);
+                        }
+
+                        IoMarkIrpPending(Irp);
+                        PoCallDriver(HubExtension->LowerDevice, Irp);
+                        return STATUS_PENDING;
+
+                    case PowerDeviceD1:
+                    case PowerDeviceD2:
+                    case PowerDeviceD3:
+                        if (HubExtension->ResetRequestCount)
+                        {
+                            IoCancelIrp(HubExtension->ResetPortIrp);
+
+                            KeWaitForSingleObject(&HubExtension->ResetEvent,
+                                                  Executive,
+                                                  KernelMode,
+                                                  FALSE,
+                                                  NULL);
+                        }
+
+                        if (!(HubExtension->HubFlags & USBHUB_FDO_FLAG_DEVICE_STOPPED))
+                        {
+                            HubExtension->HubFlags |= (USBHUB_FDO_FLAG_NOT_D0_STATE |
+                                                       USBHUB_FDO_FLAG_DEVICE_STOPPING);
+
+                            IoCancelIrp(HubExtension->SCEIrp);
+
+                            KeWaitForSingleObject(&HubExtension->StatusChangeEvent,
+                                                  Executive,
+                                                  KernelMode,
+                                                  FALSE,
+                                                  NULL);
+                        }
+
+                        HubExtension->CurrentPowerState.DeviceState = PowerState.DeviceState;
+
+                        if (HubExtension->HubFlags & USBHUB_FDO_FLAG_DO_SUSPENSE &&
+                            USBH_CheckIdleAbort(HubExtension, TRUE, TRUE) == TRUE)
+                        {
+                            HubExtension->HubFlags &= ~(USBHUB_FDO_FLAG_NOT_D0_STATE |
+                                                        USBHUB_FDO_FLAG_DEVICE_STOPPING);
+
+                            HubExtension->CurrentPowerState.DeviceState = PowerDeviceD0;
+
+                            USBH_SubmitStatusChangeTransfer(HubExtension);
+
+                            PoStartNextPowerIrp(Irp);
+
+                            Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
+                            IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+                            HubExtension->HubFlags &= ~USBHUB_FDO_FLAG_DO_SUSPENSE;
+
+                            KeReleaseSemaphore(&HubExtension->IdleSemaphore,
+                                               LOW_REALTIME_PRIORITY,
+                                               1,
+                                               FALSE);
+
+                            return STATUS_UNSUCCESSFUL;
+                        }
+
+                        IoCopyCurrentIrpStackLocationToNext(Irp);
+
+                        IoSetCompletionRoutine(Irp,
+                                               USBH_PowerIrpCompletion,
+                                               HubExtension,
+                                               TRUE,
+                                               TRUE,
+                                               TRUE);
+
+                        PoStartNextPowerIrp(Irp);
+                        IoMarkIrpPending(Irp);
+                        PoCallDriver(HubExtension->LowerDevice, Irp);
+
+                        if (HubExtension->HubFlags & USBHUB_FDO_FLAG_DO_SUSPENSE)
+                        {
+                            HubExtension->HubFlags &= ~USBHUB_FDO_FLAG_DO_SUSPENSE;
+
+                            KeReleaseSemaphore(&HubExtension->IdleSemaphore,
+                                               LOW_REALTIME_PRIORITY,
+                                               1,
+                                               FALSE);
+                        }
+
+                        return STATUS_PENDING;
+
+                    default:
+                        DPRINT1("USBH_FdoPower: Unsupported PowerState.DeviceState\n");
+                        DbgBreakPoint();
+                        break;
+                }
+            }
+            else
+            {
+                if (PowerState.SystemState != PowerSystemWorking)
+                {
+                    USBH_GetRootHubExtension(HubExtension)->SystemPowerState.SystemState =
+                                                            PowerState.SystemState;
+                }
+
+                if (PowerState.SystemState == PowerSystemHibernate)
+                {
+                    HubExtension->HubFlags |= USBHUB_FDO_FLAG_HIBERNATE_STATE;
+                }
+
+                PortData = HubExtension->PortData;
+
+                IsAllPortsD3 = TRUE;
+
+                if (PortData && HubExtension->HubDescriptor)
+                {
+                    for (Port = 0;
+                         Port < HubExtension->HubDescriptor->bNumberOfPorts;
+                         Port++)
+                    {
+                        PdoDevice = PortData[Port].DeviceObject;
+
+                        if (PdoDevice)
+                        {
+                            PortExtension = PdoDevice->DeviceExtension;
+
+                            if (PortExtension->CurrentPowerState.DeviceState != PowerDeviceD3)
+                            {
+                                IsAllPortsD3 = FALSE;
+                                break;
+                            }
+                        }
+                    }
+                }
+
+                if (PowerState.SystemState == PowerSystemWorking)
+                {
+                    DevicePwrState.DeviceState = PowerDeviceD0;
+                }
+                else if (HubExtension->HubFlags & USBHUB_FDO_FLAG_PENDING_WAKE_IRP ||
+                         !IsAllPortsD3)
+                {
+                    DevicePwrState.DeviceState = HubExtension->DeviceState[PowerState.SystemState];
+
+                    if (DevicePwrState.DeviceState == PowerDeviceUnspecified)
+                    {
+                        goto Exit;
+                    }
+                }
+                else
+                {
+                    DevicePwrState.DeviceState = PowerDeviceD3;
+                }
+
+                if (DevicePwrState.DeviceState != HubExtension->CurrentPowerState.DeviceState &&
+                    HubExtension->HubFlags & USBHUB_FDO_FLAG_DEVICE_STARTED)
+                {
+                    HubExtension->PowerIrp = Irp;
+
+                    IoMarkIrpPending(Irp);
+
+                    if (PoRequestPowerIrp(HubExtension->LowerPDO,
+                                          IRP_MN_SET_POWER,
+                                          DevicePwrState,
+                                          USBH_FdoDeferPoRequestCompletion,
+                                          (PVOID)HubExtension,
+                                          NULL) == STATUS_PENDING)
+                    {
+                        return STATUS_PENDING;
+                    }
+
+                    IoCopyCurrentIrpStackLocationToNext(Irp);
+                    PoStartNextPowerIrp(Irp);
+                    PoCallDriver(HubExtension->LowerDevice, Irp);
+
+                    return STATUS_PENDING;
+                }
+
+            Exit:
+
+                HubExtension->SystemPowerState.SystemState = PowerState.SystemState;
+
+                if (PowerState.SystemState == PowerSystemWorking)
+                {
+                    USBH_CheckIdleDeferred(HubExtension);
+                }
+
+                IoCopyCurrentIrpStackLocationToNext(Irp);
+                PoStartNextPowerIrp(Irp);
+
+                return PoCallDriver(HubExtension->LowerDevice, Irp);
+            }
+
+            break;
+
+        case IRP_MN_QUERY_POWER:
+            DPRINT_PWR("USBH_FdoPower: IRP_MN_QUERY_POWER\n");
+            break;
+
+        default:
+            DPRINT1("USBH_FdoPower: unknown IRP_MN_POWER!\n");
+            break;
+    }
+
+    IoCopyCurrentIrpStackLocationToNext(Irp);
+    PoStartNextPowerIrp(Irp);
+    Status = PoCallDriver(HubExtension->LowerDevice, Irp);
+
+    return Status;
+}
+
+NTSTATUS
+NTAPI
+USBH_PdoPower(IN PUSBHUB_PORT_PDO_EXTENSION PortExtension,
+              IN PIRP Irp,
+              IN UCHAR Minor)
+{
+    NTSTATUS Status = Irp->IoStatus.Status;
+
+    DPRINT_PWR("USBH_FdoPower: PortExtension - %p, Irp - %p, Minor - %X\n",
+               PortExtension,
+               Irp,
+               Minor);
+
+    switch (Minor)
+    {
+      case IRP_MN_WAIT_WAKE:
+          DPRINT_PWR("USBHUB_PdoPower: IRP_MN_WAIT_WAKE\n");
+          PoStartNextPowerIrp(Irp);
+          break;
+
+      case IRP_MN_POWER_SEQUENCE:
+          DPRINT_PWR("USBHUB_PdoPower: IRP_MN_POWER_SEQUENCE\n");
+          PoStartNextPowerIrp(Irp);
+          break;
+
+      case IRP_MN_SET_POWER:
+          DPRINT_PWR("USBHUB_PdoPower: IRP_MN_SET_POWER\n");
+          PoStartNextPowerIrp(Irp);
+          break;
+
+      case IRP_MN_QUERY_POWER:
+          DPRINT_PWR("USBHUB_PdoPower: IRP_MN_QUERY_POWER\n");
+          PoStartNextPowerIrp(Irp);
+          break;
+
+      default:
+          DPRINT1("USBHUB_PdoPower: unknown IRP_MN_POWER!\n");
+          PoStartNextPowerIrp(Irp);
+          break;
+    }
+
+    Irp->IoStatus.Status = Status;
+    Irp->IoStatus.Information = 0;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return Status;
+}