/*
 * PROJECT:     ReactOS USB Port Driver
 * LICENSE:     GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
 * PURPOSE:     USBPort power handling functions
 * COPYRIGHT:   Copyright 2017 Vadim Galyant <vgal@rambler.ru>
 */

#include "usbport.h"

#define NDEBUG
#include <debug.h>

VOID
NTAPI
USBPORT_CompletePdoWaitWake(IN PDEVICE_OBJECT FdoDevice)
{
    PUSBPORT_DEVICE_EXTENSION FdoExtension;
    PDEVICE_OBJECT PdoDevice;
    PUSBPORT_RHDEVICE_EXTENSION PdoExtension;
    PIRP Irp;
    KIRQL OldIrql;

    DPRINT("USBPORT_CompletePdoWaitWake: ... \n");

    FdoExtension = FdoDevice->DeviceExtension;
    PdoDevice = FdoExtension->RootHubPdo;
    PdoExtension = PdoDevice->DeviceExtension;

    KeAcquireSpinLock(&FdoExtension->PowerWakeSpinLock, &OldIrql);

    Irp = PdoExtension->WakeIrp;

    if (Irp && IoSetCancelRoutine(Irp, NULL))
    {
        PdoExtension->WakeIrp = NULL;
        KeReleaseSpinLock(&FdoExtension->PowerWakeSpinLock, OldIrql);

        DPRINT("USBPORT_CompletePdoWaitWake: Complete Irp - %p\n", Irp);

        Irp->IoStatus.Status = STATUS_SUCCESS;
        Irp->IoStatus.Information = 0;
        IoCompleteRequest(Irp, IO_NO_INCREMENT);

        return;
    }

    KeReleaseSpinLock(&FdoExtension->PowerWakeSpinLock, OldIrql);
}

VOID
NTAPI
USBPORT_HcWakeDpc(IN PRKDPC Dpc,
                  IN PVOID DeferredContext,
                  IN PVOID SystemArgument1,
                  IN PVOID SystemArgument2)
{
    DPRINT("USBPORT_HcWakeDpc: ... \n");
    USBPORT_CompletePdoWaitWake((PDEVICE_OBJECT)DeferredContext);
}

VOID
NTAPI
USBPORT_HcQueueWakeDpc(IN PDEVICE_OBJECT FdoDevice)
{
    PUSBPORT_DEVICE_EXTENSION FdoExtension;

    DPRINT("USBPORT_HcQueueWakeDpc: ... \n");

    FdoExtension = FdoDevice->DeviceExtension;
    KeInsertQueueDpc(&FdoExtension->HcWakeDpc, NULL, NULL);
}

VOID
NTAPI
USBPORT_CompletePendingIdleIrp(IN PDEVICE_OBJECT PdoDevice)
{
    PUSBPORT_RHDEVICE_EXTENSION PdoExtension;
    PDEVICE_OBJECT FdoDevice;
    PUSBPORT_DEVICE_EXTENSION FdoExtension;
    PIRP Irp;

    DPRINT("USBPORT_CompletePendingIdleIrp: ... \n");

    PdoExtension = PdoDevice->DeviceExtension;
    FdoDevice = PdoExtension->FdoDevice;
    FdoExtension = FdoDevice->DeviceExtension;

    Irp = IoCsqRemoveNextIrp(&FdoExtension->IdleIoCsq, 0);

    if (Irp)
    {
        InterlockedDecrement(&FdoExtension->IdleLockCounter);

        DPRINT("USBPORT_CompletePendingIdleIrp: Complete Irp - %p\n", Irp);

        Irp->IoStatus.Status = STATUS_CANCELLED;
        Irp->IoStatus.Information = 0;
        IoCompleteRequest(Irp, IO_NO_INCREMENT);
    }
}

VOID
NTAPI
USBPORT_DoSetPowerD0(IN PDEVICE_OBJECT FdoDevice)
{
    DPRINT("USBPORT_DoSetPowerD0: FIXME!\n");
    return;
}

VOID
NTAPI
USBPORT_SuspendController(IN PDEVICE_OBJECT FdoDevice)
{
    PUSBPORT_DEVICE_EXTENSION  FdoExtension;
    PUSBPORT_REGISTRATION_PACKET Packet;

    DPRINT1("USBPORT_SuspendController \n");

    FdoExtension = FdoDevice->DeviceExtension;
    Packet = &FdoExtension->MiniPortInterface->Packet;

    FdoExtension->TimerFlags |= USBPORT_TMFLAG_RH_SUSPENDED;

    USBPORT_FlushController(FdoDevice);

    if (FdoExtension->Flags & USBPORT_FLAG_HC_SUSPEND)
    {
        return;
    }

    FdoExtension->TimerFlags |= USBPORT_TMFLAG_HC_SUSPENDED;

    if (FdoExtension->MiniPortFlags & USBPORT_MPFLAG_INTERRUPTS_ENABLED)
    {
        FdoExtension->MiniPortFlags |= USBPORT_MPFLAG_SUSPENDED;

        USBPORT_Wait(FdoDevice, 10);
        Packet->SuspendController(FdoExtension->MiniPortExt);
    }

    FdoExtension->Flags |= USBPORT_FLAG_HC_SUSPEND;
}

NTSTATUS
NTAPI
USBPORT_ResumeController(IN PDEVICE_OBJECT FdoDevice)
{
    NTSTATUS Status = STATUS_UNSUCCESSFUL;
    PUSBPORT_DEVICE_EXTENSION  FdoExtension;
    PUSBPORT_REGISTRATION_PACKET Packet;
    KIRQL OldIrql;
    MPSTATUS MpStatus;

    DPRINT1("USBPORT_ResumeController: ... \n");

    FdoExtension = FdoDevice->DeviceExtension;
    Packet = &FdoExtension->MiniPortInterface->Packet;

    if (!(FdoExtension->Flags & USBPORT_FLAG_HC_SUSPEND))
    {
        return Status;
    }

    KeAcquireSpinLock(&FdoExtension->TimerFlagsSpinLock, &OldIrql);

    FdoExtension->TimerFlags &= ~(USBPORT_TMFLAG_HC_SUSPENDED |
                                  USBPORT_TMFLAG_RH_SUSPENDED);

    KeReleaseSpinLock(&FdoExtension->TimerFlagsSpinLock, OldIrql);

    if (!(FdoExtension->MiniPortFlags & USBPORT_MPFLAG_SUSPENDED))
    {
        FdoExtension->Flags &= ~USBPORT_FLAG_HC_SUSPEND;
        return Status;
    }

    FdoExtension->MiniPortFlags &= ~USBPORT_MPFLAG_SUSPENDED;

    if (!Packet->ResumeController(FdoExtension->MiniPortExt))
    {
        Status = USBPORT_Wait(FdoDevice, 100);

        FdoExtension->Flags &= ~USBPORT_FLAG_HC_SUSPEND;
        return Status;
    }

    KeAcquireSpinLock(&FdoExtension->TimerFlagsSpinLock, &OldIrql);
    FdoExtension->TimerFlags |= (USBPORT_TMFLAG_HC_SUSPENDED |
                                 USBPORT_TMFLAG_HC_RESUME);
    KeReleaseSpinLock(&FdoExtension->TimerFlagsSpinLock, OldIrql);

    USBPORT_MiniportInterrupts(FdoDevice, FALSE);

    Packet->StopController(FdoExtension->MiniPortExt, 1);

    USBPORT_NukeAllEndpoints(FdoDevice);

    RtlZeroMemory(FdoExtension->MiniPortExt, Packet->MiniPortExtensionSize);

    RtlZeroMemory((PVOID)FdoExtension->UsbPortResources.StartVA,
                  Packet->MiniPortResourcesSize);

    FdoExtension->UsbPortResources.IsChirpHandled = TRUE;

    MpStatus = Packet->StartController(FdoExtension->MiniPortExt,
                                       &FdoExtension->UsbPortResources);

    FdoExtension->UsbPortResources.IsChirpHandled = FALSE;

    if (!MpStatus)
    {
        USBPORT_MiniportInterrupts(FdoDevice, TRUE);
    }

    KeAcquireSpinLock(&FdoExtension->TimerFlagsSpinLock, &OldIrql);

    FdoExtension->TimerFlags &= ~(USBPORT_TMFLAG_HC_SUSPENDED |
                                  USBPORT_TMFLAG_HC_RESUME |
                                  USBPORT_TMFLAG_RH_SUSPENDED);

    KeReleaseSpinLock(&FdoExtension->TimerFlagsSpinLock, OldIrql);

    Status = USBPORT_Wait(FdoDevice, 100);

    FdoExtension->Flags &= ~USBPORT_FLAG_HC_SUSPEND;

    return Status;
}

NTSTATUS
NTAPI
USBPORT_PdoDevicePowerState(IN PDEVICE_OBJECT PdoDevice,
                            IN PIRP Irp)
{
    PUSBPORT_RHDEVICE_EXTENSION PdoExtension;
    PDEVICE_OBJECT FdoDevice;
    PUSBPORT_DEVICE_EXTENSION FdoExtension;
    PIO_STACK_LOCATION IoStack;
    NTSTATUS Status = STATUS_SUCCESS;
    POWER_STATE State;

    PdoExtension = PdoDevice->DeviceExtension;
    FdoDevice = PdoExtension->FdoDevice;
    FdoExtension = FdoDevice->DeviceExtension;
    IoStack = IoGetCurrentIrpStackLocation(Irp);

    State = IoStack->Parameters.Power.State;

    DPRINT1("USBPORT_PdoDevicePowerState: Irp - %p, State - %x\n",
            Irp,
            State.DeviceState);

    if (State.DeviceState == PowerDeviceD0)
    {
        if (FdoExtension->CommonExtension.DevicePowerState == PowerDeviceD0)
        {
            // FIXME FdoExtension->Flags
            while (FdoExtension->SetPowerLockCounter)
            {
                USBPORT_Wait(FdoDevice, 10);
            }

            USBPORT_ResumeController(FdoDevice);

            PdoExtension->CommonExtension.DevicePowerState = PowerDeviceD0;

            USBPORT_CompletePdoWaitWake(FdoDevice);
            USBPORT_CompletePendingIdleIrp(PdoDevice);
        }
        else
        {
            DPRINT1("USBPORT_PdoDevicePowerState: FdoExtension->Flags - %lx\n",
                    FdoExtension->Flags);

            DbgBreakPoint();
            Status = STATUS_UNSUCCESSFUL;
        }
    }
    else if (State.DeviceState  == PowerDeviceD1 ||
             State.DeviceState  == PowerDeviceD2 ||
             State.DeviceState  == PowerDeviceD3)
    {
        FdoExtension->TimerFlags |= USBPORT_TMFLAG_WAKE;
        USBPORT_SuspendController(FdoDevice);
        PdoExtension->CommonExtension.DevicePowerState = State.DeviceState;
    }

    return Status;
}

VOID
NTAPI
USBPORT_CancelPendingWakeIrp(IN PDEVICE_OBJECT PdoDevice,
                             IN PIRP Irp)
{
    PUSBPORT_DEVICE_EXTENSION FdoExtension;
    KIRQL OldIrql;
    PUSBPORT_RHDEVICE_EXTENSION PdoExtension;

    DPRINT("USBPORT_CancelPendingWakeIrp: ... \n");

    IoReleaseCancelSpinLock(Irp->CancelIrql);
    PdoExtension = PdoDevice->DeviceExtension;
    FdoExtension = PdoExtension->FdoDevice->DeviceExtension;

    KeAcquireSpinLock(&FdoExtension->PowerWakeSpinLock, &OldIrql);

    if (PdoExtension->WakeIrp == Irp)
    {
        PdoExtension->WakeIrp = NULL;
    }

    KeReleaseSpinLock(&FdoExtension->PowerWakeSpinLock, OldIrql);

    Irp->IoStatus.Status = STATUS_CANCELLED;
    Irp->IoStatus.Information = 0;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);
}

NTSTATUS
NTAPI
USBPORT_PdoPower(IN PDEVICE_OBJECT PdoDevice,
                 IN PIRP Irp)
{
    PUSBPORT_RHDEVICE_EXTENSION PdoExtension;
    PDEVICE_OBJECT FdoDevice;
    PIO_STACK_LOCATION IoStack;
    PUSBPORT_DEVICE_EXTENSION FdoExtension;
    NTSTATUS Status;
    KIRQL OldIrql;

    DPRINT("USBPORT_PdoPower: Irp - %p\n", Irp);

    PdoExtension = PdoDevice->DeviceExtension;
    FdoDevice = PdoExtension->FdoDevice;
    FdoExtension = FdoDevice->DeviceExtension;
    IoStack = IoGetCurrentIrpStackLocation(Irp);

    Status = Irp->IoStatus.Status;

    switch (IoStack->MinorFunction)
    {
      case IRP_MN_WAIT_WAKE:
          DPRINT("USBPORT_PdoPower: IRP_MN_WAIT_WAKE\n");

          if (!(FdoExtension->Flags & USBPORT_FLAG_HC_STARTED))
          {
              /* The device does not support wake-up */
              Status = STATUS_NOT_SUPPORTED;
              break;
          }

          KeAcquireSpinLock(&FdoExtension->PowerWakeSpinLock, &OldIrql);

          IoSetCancelRoutine(Irp, USBPORT_CancelPendingWakeIrp);

          /* Check if the IRP has been cancelled */
          if (Irp->Cancel)
          {
              if (IoSetCancelRoutine(Irp, NULL))
              {
                  /* IRP has been cancelled, release cancel spinlock */
                  KeReleaseSpinLock(&FdoExtension->PowerWakeSpinLock, OldIrql);

                  DPRINT("USBPORT_PdoPower: IRP_MN_WAIT_WAKE - STATUS_CANCELLED\n");

                  /* IRP is cancelled */
                  Status = STATUS_CANCELLED;
                  break;
              }
          }

          if (!PdoExtension->WakeIrp)
          {
              /* The driver received the IRP
                 and is waiting for the device to signal wake-up. */

              DPRINT("USBPORT_PdoPower: IRP_MN_WAIT_WAKE - No WakeIrp\n");

              IoMarkIrpPending(Irp);
              PdoExtension->WakeIrp = Irp;

              KeReleaseSpinLock(&FdoExtension->PowerWakeSpinLock, OldIrql);
              return STATUS_PENDING;
          }
          else
          {
              /* An IRP_MN_WAIT_WAKE request is already pending and must be
                 completed or canceled before another IRP_MN_WAIT_WAKE request
                 can be issued. */

              if (IoSetCancelRoutine(Irp, NULL))
              {
                  DPRINT("USBPORT_PdoPower: IRP_MN_WAIT_WAKE - STATUS_DEVICE_BUSY\n");

                  KeReleaseSpinLock(&FdoExtension->PowerWakeSpinLock, OldIrql);
                  PoStartNextPowerIrp(Irp);
                  Status = STATUS_DEVICE_BUSY;
                  break;
              }
              else
              {
                  ASSERT(FALSE);
                  KeReleaseSpinLock(&FdoExtension->PowerWakeSpinLock, OldIrql);
                  return Status;
              }
          }

      case IRP_MN_POWER_SEQUENCE:
          DPRINT("USBPORT_PdoPower: IRP_MN_POWER_SEQUENCE\n");
          PoStartNextPowerIrp(Irp);
          break;

      case IRP_MN_SET_POWER:
          DPRINT("USBPORT_PdoPower: IRP_MN_SET_POWER\n");

          if (IoStack->Parameters.Power.Type == DevicePowerState)
          {
              DPRINT("USBPORT_PdoPower: IRP_MN_SET_POWER/DevicePowerState\n");
              Status = USBPORT_PdoDevicePowerState(PdoDevice, Irp);
              PoStartNextPowerIrp(Irp);
              break;
          }

          DPRINT("USBPORT_PdoPower: IRP_MN_SET_POWER/SystemPowerState \n");

          if (IoStack->Parameters.Power.State.SystemState == PowerSystemWorking)
          {
              FdoExtension->TimerFlags |= USBPORT_TMFLAG_WAKE;
          }
          else
          {
              FdoExtension->TimerFlags &= ~USBPORT_TMFLAG_WAKE;
          }

          Status = STATUS_SUCCESS;

          PoStartNextPowerIrp(Irp);
          break;

      case IRP_MN_QUERY_POWER:
          DPRINT("USBPORT_PdoPower: IRP_MN_QUERY_POWER\n");
          Status = STATUS_SUCCESS;
          PoStartNextPowerIrp(Irp);
          break;

      default:
          DPRINT1("USBPORT_PdoPower: unknown IRP_MN_POWER!\n");
          PoStartNextPowerIrp(Irp);
          break;
    }

    Irp->IoStatus.Status = Status;
    Irp->IoStatus.Information = 0;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);

    return Status;
}

NTSTATUS
NTAPI
USBPORT_HcWake(IN PDEVICE_OBJECT FdoDevice,
               IN PIRP Irp)
{
    DPRINT1("USBPORT_HcWake: UNIMPLEMENTED. FIXME. \n");
    return STATUS_SUCCESS;
}

NTSTATUS
NTAPI
USBPORT_DevicePowerState(IN PDEVICE_OBJECT FdoDevice,
                         IN PIRP Irp)
{
    DPRINT1("USBPORT_DevicePowerState: UNIMPLEMENTED. FIXME. \n");
    return STATUS_SUCCESS;
}

NTSTATUS
NTAPI
USBPORT_SystemPowerState(IN PDEVICE_OBJECT FdoDevice,
                         IN PIRP Irp)
{
    DPRINT1("USBPORT_SystemPowerState: UNIMPLEMENTED. FIXME. \n");
    return STATUS_SUCCESS;
}

NTSTATUS
NTAPI
USBPORT_FdoPower(IN PDEVICE_OBJECT FdoDevice,
                 IN PIRP Irp)
{
    PUSBPORT_DEVICE_EXTENSION FdoExtension;
    PIO_STACK_LOCATION IoStack;
    NTSTATUS Status;

    DPRINT("USBPORT_FdoPower: ... \n");

    FdoExtension = FdoDevice->DeviceExtension;
    IoStack = IoGetCurrentIrpStackLocation(Irp);

    switch (IoStack->MinorFunction)
    {
      case IRP_MN_WAIT_WAKE:
          DPRINT("USBPORT_FdoPower: IRP_MN_WAIT_WAKE\n");
          Status = USBPORT_HcWake(FdoDevice, Irp);
          return Status;

      case IRP_MN_POWER_SEQUENCE:
          DPRINT("USBPORT_FdoPower: IRP_MN_POWER_SEQUENCE\n");
          break;

      case IRP_MN_SET_POWER:
          DPRINT("USBPORT_FdoPower: IRP_MN_SET_POWER\n");
          if (IoStack->Parameters.Power.Type == DevicePowerState)
          {
              Status = USBPORT_DevicePowerState(FdoDevice, Irp);
          }
          else
          {
              Status = USBPORT_SystemPowerState(FdoDevice, Irp);
          }

          if (Status != STATUS_PENDING)
              break;

          return Status;

      case IRP_MN_QUERY_POWER:
          DPRINT("USBPORT_FdoPower: IRP_MN_QUERY_POWER\n");
          Irp->IoStatus.Status = STATUS_SUCCESS;
          break;

      default:
          DPRINT1("USBPORT_FdoPower: unknown IRP_MN_POWER!\n");
          break;
    }

    IoCopyCurrentIrpStackLocationToNext(Irp);
    PoStartNextPowerIrp(Irp);
    return PoCallDriver(FdoExtension->CommonExtension.LowerDevice, Irp);
}

VOID
NTAPI
USBPORT_DoIdleNotificationCallback(IN PVOID Context)
{
    PIO_STACK_LOCATION IoStack;
    PDEVICE_OBJECT FdoDevice;
    PUSBPORT_DEVICE_EXTENSION FdoExtension;
    PUSBPORT_RHDEVICE_EXTENSION PdoExtension;
    PIRP NextIrp;
    LARGE_INTEGER CurrentTime = {{0, 0}};
    PTIMER_WORK_QUEUE_ITEM IdleQueueItem;
    PDEVICE_OBJECT PdoDevice;
    PUSB_IDLE_CALLBACK_INFO IdleCallbackInfo;
    KIRQL OldIrql;

    DPRINT("USBPORT_DoIdleNotificationCallback \n");

    IdleQueueItem = Context;

    FdoDevice = IdleQueueItem->FdoDevice;
    FdoExtension = FdoDevice->DeviceExtension;
    PdoDevice = FdoExtension->RootHubPdo;
    PdoExtension = PdoDevice->DeviceExtension;

    KeQuerySystemTime(&CurrentTime);

    if ((FdoExtension->IdleTime.QuadPart == 0) ||
        (((CurrentTime.QuadPart - FdoExtension->IdleTime.QuadPart) / 10000) >= 500))
    {
        if (PdoExtension->CommonExtension.DevicePowerState == PowerDeviceD0 &&
            FdoExtension->CommonExtension.DevicePowerState == PowerDeviceD0)
        {
            NextIrp = IoCsqRemoveNextIrp(&FdoExtension->IdleIoCsq, NULL);

            if (NextIrp)
            {
                IoStack = IoGetCurrentIrpStackLocation(NextIrp);
                IdleCallbackInfo = IoStack->Parameters.DeviceIoControl.Type3InputBuffer;

                if (IdleCallbackInfo && IdleCallbackInfo->IdleCallback)
                {
                    IdleCallbackInfo->IdleCallback(IdleCallbackInfo->IdleContext);
                }

                if (NextIrp->Cancel)
                {
                    InterlockedDecrement(&FdoExtension->IdleLockCounter);

                    NextIrp->IoStatus.Status = STATUS_CANCELLED;
                    NextIrp->IoStatus.Information = 0;
                    IoCompleteRequest(NextIrp, IO_NO_INCREMENT);
                }
                else
                {
                    IoCsqInsertIrp(&FdoExtension->IdleIoCsq, NextIrp, NULL);
                }
            }
        }
    }

    KeAcquireSpinLock(&FdoExtension->TimerFlagsSpinLock, &OldIrql);
    FdoExtension->TimerFlags &= ~USBPORT_TMFLAG_IDLE_QUEUEITEM_ON;
    KeReleaseSpinLock(&FdoExtension->TimerFlagsSpinLock, OldIrql);

    ExFreePoolWithTag(IdleQueueItem, USB_PORT_TAG);
}

NTSTATUS
NTAPI
USBPORT_IdleNotification(IN PDEVICE_OBJECT PdoDevice,
                         IN PIRP Irp)
{
    PUSBPORT_RHDEVICE_EXTENSION PdoExtension;
    PDEVICE_OBJECT FdoDevice;
    PUSBPORT_DEVICE_EXTENSION FdoExtension;
    LONG LockCounter;
    NTSTATUS Status = STATUS_PENDING;

    DPRINT("USBPORT_IdleNotification: Irp - %p\n", Irp);

    PdoExtension = PdoDevice->DeviceExtension;
    FdoDevice = PdoExtension->FdoDevice;
    FdoExtension = FdoDevice->DeviceExtension;

    LockCounter = InterlockedIncrement(&FdoExtension->IdleLockCounter);

    if (LockCounter != 0)
    {
        if (Status != STATUS_PENDING)
        {
            InterlockedDecrement(&FdoExtension->IdleLockCounter);

            Irp->IoStatus.Status = Status;
            Irp->IoStatus.Information = 0;
            IoCompleteRequest(Irp, IO_NO_INCREMENT);

            return Status;
        }

        Status = STATUS_DEVICE_BUSY;
    }

    if (Status != STATUS_PENDING)
    {
        InterlockedDecrement(&FdoExtension->IdleLockCounter);

        Irp->IoStatus.Status = Status;
        Irp->IoStatus.Information = 0;
        IoCompleteRequest(Irp, IO_NO_INCREMENT);

        return Status;
    }

    Irp->IoStatus.Status = STATUS_PENDING;
    IoMarkIrpPending(Irp);

    KeQuerySystemTime(&FdoExtension->IdleTime);

    IoCsqInsertIrp(&FdoExtension->IdleIoCsq, Irp, 0);

    return Status;
}

VOID
NTAPI
USBPORT_AdjustDeviceCapabilities(IN PDEVICE_OBJECT FdoDevice,
                                 IN PDEVICE_OBJECT PdoDevice)
{
    PUSBPORT_DEVICE_EXTENSION FdoExtension;
    PUSBPORT_RHDEVICE_EXTENSION PdoExtension;
    PDEVICE_CAPABILITIES Capabilities;

    DPRINT("USBPORT_AdjustDeviceCapabilities: ... \n");

    FdoExtension = FdoDevice->DeviceExtension;
    PdoExtension = PdoDevice->DeviceExtension;
    Capabilities = &PdoExtension->Capabilities;

    RtlCopyMemory(Capabilities,
                  &FdoExtension->Capabilities,
                  sizeof(DEVICE_CAPABILITIES));

    Capabilities->DeviceD1 = FALSE;
    Capabilities->DeviceD2 = TRUE;

    Capabilities->Removable = FALSE;
    Capabilities->UniqueID = FALSE;

    Capabilities->WakeFromD0 = TRUE;
    Capabilities->WakeFromD1 = FALSE;
    Capabilities->WakeFromD2 = TRUE;
    Capabilities->WakeFromD3 = FALSE;

    Capabilities->Address = 0;
    Capabilities->UINumber = 0;

    if (Capabilities->SystemWake == PowerSystemUnspecified)
        Capabilities->SystemWake = PowerSystemWorking;

    Capabilities->DeviceWake = PowerDeviceD2;

    Capabilities->DeviceState[PowerSystemSleeping1] = PowerDeviceD3;
    Capabilities->DeviceState[PowerSystemSleeping2] = PowerDeviceD3;
    Capabilities->DeviceState[PowerSystemSleeping3] = PowerDeviceD3;
    Capabilities->DeviceState[PowerSystemHibernate] = PowerDeviceD3;
}