FxPowerIdleStates
FxPowerIdleMachine::PowerUpComplete(
    __inout FxPowerIdleMachine* This
    )
/*++

Routine Description:
    The device is moving into D0, determine which D0 state to move into
    based on the enabled state and io count.

Arguments:
    This - instance of the state machine

Return Value:
    new state

  --*/
{
    if (This->m_Flags & FxPowerIdleTimerEnabled) {
        if (This->m_Flags & FxPowerIdleTimerStarted) {
            COVERAGE_TRAP();
            return FxIdleTimerRunning;
        }
        else {
            return FxIdleCheckIoCount;
        }
    }
    else {
        //
        // Not enabled, better not have a timer running
        //
        ASSERT((This->m_Flags & FxPowerIdleTimerStarted) == 0);

        return FxIdleDisabled;
    }
}
VOID
FxPowerIdleMachine::ProcessEventLocked(
    __in FxPowerIdleEvents Event
    )
/*++

Routine Description:
    Processes an event and runs it through the state machine

Arguments:


Return Value:


  --*/

{
    const FxIdleStateTable* entry;
    FxPowerIdleStates newState;
    FxPkgPnp* pPkgPnp;

    pPkgPnp = GetPnpPkg(this);

    m_EventHistory[m_EventHistoryIndex] = Event;
    m_EventHistoryIndex = (m_EventHistoryIndex + 1) %
                          (sizeof(m_EventHistory)/sizeof(m_EventHistory[0]));

    entry = &m_StateTable[m_CurrentIdleState-FxIdleStopped];
    newState = FxIdleMax;

    for (ULONG i = 0; i < entry->TargetStatesCount; i++) {
        if (entry->TargetStates[i].PowerIdleEvent == Event) {
            DO_EVENT_TRAP(&entry->TargetStates[i]);
            newState = entry->TargetStates[i].PowerIdleState;
            break;
        }
    }

    if (newState == FxIdleMax) {
        switch (Event) {
        case PowerIdleEventIoIncrement:
        case PowerIdleEventIoDecrement:
            //
            // We always can handle io increment, io decrement, and query return
            // to idle from any state...
            //
            break;

        case PowerIdleEventEnabled:
            if (m_Flags & FxPowerIdleTimerEnabled) {
                //
                // Getting an enable event while enabled is OK
                //
                break;
            }
            //  ||   ||  Fall    ||  ||
            //  \/   \/  through \/  \/

        default:
            //
            // ...but we should not be dropping any other events from this state.
            //

            //
            DoTraceLevelMessage(
                pPkgPnp->GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGPNP,
                "WDFDEVICE 0x%p !devobj 0x%p power idle state %!FxPowerIdleStates!"
                " dropping event %!FxPowerIdleEvents!",
                pPkgPnp->GetDevice()->GetHandle(),
                pPkgPnp->GetDevice()->GetDeviceObject(),
                m_CurrentIdleState, Event);

            COVERAGE_TRAP();
        }
    }

    while (newState != FxIdleMax) {

        DoTraceLevelMessage(
            pPkgPnp->GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGPNPPOWERSTATES,
            "WDFDEVICE 0x%p !devobj 0x%p entering power idle state "
            "%!FxPowerIdleStates! from %!FxPowerIdleStates!",
            pPkgPnp->GetDevice()->GetHandle(),
            pPkgPnp->GetDevice()->GetDeviceObject(),
            newState, m_CurrentIdleState);

        m_StateHistory[m_StateHistoryIndex] = newState;
        m_StateHistoryIndex = (m_StateHistoryIndex + 1) %
                              (sizeof(m_StateHistory)/sizeof(m_StateHistory[0]));

        m_CurrentIdleState = newState;
        entry = &m_StateTable[m_CurrentIdleState-FxIdleStopped];

        if (entry->StateFunc != NULL) {
            newState = entry->StateFunc(this);
        }
        else {
            newState = FxIdleMax;
        }
    }
}
_Must_inspect_result_
NTSTATUS
FxPowerIdleMachine::IoIncrementWithFlags(
    __in FxPowerReferenceFlags Flags,
    __out_opt PULONG Count
    )
/*++

Routine Description:
    An enchanced version of FxPowerIdleMachine::IoIncrement that has special 
    behavior based on flags passed in by the caller. Please read the routine
    description of FxPowerIdleMachine::IoIncrement as well.

Arguments:
    Flags - The following flags are defined -
         FxPowerReferenceDefault - No special behavior
         FxPowerReferenceSendPnpPowerUpEvent - Set the 
           FxPowerIdleSendPnpPowerUpEvent flag in the idle state machine flags.
           This will indicate to the idle state machine that when the device 
           powers up, it needs to send the PnpEventDeviceInD0 event to the PnP 
           state machine.

Return Value:
    STATUS_PENDING if the state machine is transition from idle to non idle

    STATUS_SUCCESS otherwise

  --*/
{
    NTSTATUS status;
    KIRQL irql;

    m_Lock.Acquire(&irql);

    if (m_Flags & FxPowerIdlePowerFailed) {
        //
        // fail without incrementing the count because we are in an
        // invalid power state
        //
        status = STATUS_POWER_STATE_INVALID;
        COVERAGE_TRAP();
    }
    else if ((m_Flags & FxPowerIdleIsStarted) == 0x0) {
        //
        // The state machine is not yet in a started state
        //
        status = STATUS_POWER_STATE_INVALID;
    }
    else {
        m_IoCount++;
        if (Count != NULL) {
            *Count = m_IoCount;
        }
        
        ProcessEventLocked(PowerIdleEventIoIncrement);

        if (InD0Locked()) {
            status = STATUS_SUCCESS;
        }
        else {
            status = STATUS_PENDING;
            if (Flags & FxPowerReferenceSendPnpPowerUpEvent) {
                m_Flags |= FxPowerIdleSendPnpPowerUpEvent;
            }
        }
    }
    m_Lock.Release(irql);

    return status;
}
_Must_inspect_result_
NTSTATUS
FxPowerIdleMachine::PowerReferenceWorker(
    __in BOOLEAN WaitForD0,
    __in FxPowerReferenceFlags Flags,
    __in_opt PVOID Tag,
    __in_opt LONG Line,
    __in_opt PSTR File
    )
/*++

Routine Description:
    Caller wants to move the device into D0 manually.  The caller may optionally
    wait synchronously for the transition to occur if the device is currently in
    Dx.

Arguments:
    WaitForD0 - TRUE if the caller wants to synchronously wait for the Dx to D0
                transition

    QueryPnpPending - TRUE if we are being called to bring the device back to 
                working state when a QueryRemove or a QueryStop 
                
Return Value:
    NTSTATUS

    STATUS_SUCCESS - success
    STATUS_PENDING - transition is occurring
    STATUS_POWER_STATE_INVALID - ower transition has failed

  --*/
{
    NTSTATUS status;
    KIRQL irql;
    ULONG count = 0;




















    // Poke the state machine
    //
    status = IoIncrementWithFlags(Flags, &count);

    //
    // STATUS_PENDING indicates a Dx to D0 transition is occurring right now
    //
    if (status == STATUS_PENDING) {
        if (WaitForD0) {
            FxPkgPnp* pPkgPnp;

            ASSERT(Mx::MxGetCurrentIrql() <= APC_LEVEL);

            //
            // With the current usage, if WaitForD0 is TRUE, then the only 
            // acceptable flag is FxPowerReferenceDefault. 
            //
            // If the usage changes in the future such that it is acceptable to
            // have WaitForD0 set to TRUE and some flag(s) set, then the ASSERT
            // below should be updated accordingly (or removed altogether).
            //
            ASSERT(FxPowerReferenceDefault == Flags);

            pPkgPnp = GetPnpPkg(this);

            DoTraceLevelMessage(
                pPkgPnp->GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP,
                "WDFDEVICE %p in thread %p waiting synchronously for Dx to D0 "
                "transition",
                pPkgPnp->GetDevice()->GetHandle(),
                Mx::MxGetCurrentThread());

            //
            // Returns success always
            //
            (void) FxPowerIdleMachine::WaitForD0();

            m_Lock.Acquire(&irql);

            //
            // If WaitForD0 is TRUE, then the FxPowerIdleSendPnpPowerUpEvent 
            // flag can't be set. That flag is only used when the PnP state 
            // machine waits asynchronously for the device to power up during
            // query-remove.
            //
            ASSERT(0 == (m_Flags & FxPowerIdleSendPnpPowerUpEvent));

            if ((m_Flags & FxPowerIdlePowerFailed) != 0x0 ||
                (m_Flags & FxPowerIdleIsStarted) == 0x0) {

                //
                // Event was set because a power up or down failure occurred
                //
                status = STATUS_POWER_STATE_INVALID;

                if (m_Flags & FxPowerIdlePowerFailed) {
                    DoTraceLevelMessage(
                        pPkgPnp->GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP,
                        "WDFDEVICE %p waiting for D0 in thread %p failed because of "
                        "power failure, %!STATUS!",
                        pPkgPnp->GetDevice()->GetHandle(),
                        Mx::MxGetCurrentThread(),
                        status);
                }
                else {
                    COVERAGE_TRAP();
                    DoTraceLevelMessage(
                        pPkgPnp->GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP,
                        "WDFDEVICE %p waiting for D0 in thread %p failed because of "
                        "invalid state , %!STATUS!",
                        pPkgPnp->GetDevice()->GetHandle(),
                        Mx::MxGetCurrentThread(), status);
                }

                //
                // Decrement the io count that was taken above
                //
                ASSERT(m_IoCount > 0);
                m_IoCount--;
                ProcessEventLocked(PowerIdleEventIoDecrement);
            }
            else {
                //
                // Successfully returned to D0
                //
                status = STATUS_SUCCESS;
            }
            m_Lock.Release(irql);
        }
    }

    if (m_TagTracker != NULL) {
        //
        // Only track the reference if the call was successful
        // and the counter was actually incremented.
        //
        if (status == STATUS_SUCCESS || status == STATUS_PENDING) {
            m_TagTracker->UpdateTagHistory(Tag, Line, File, TagAddRef, count);
        }
    }

    return status;
}
VOID
FxWakeInterruptMachine::ProcessEventInner(
    __inout FxPostProcessInfo* Info
    )
{
    KIRQL irql;
    FxWakeInterruptEvents event;
    const FxWakeInterruptStateTable* entry;
    FxWakeInterruptStates newState;

    //
    // Process as many events as we can
    //
    for ( ; ; ) {
        //
        // Acquire state machine *queue* lock
        //
        Lock(&irql);

        if (IsEmpty()) {
            //
            // The queue is empty.
            //
            GetFinishedState(Info);
            Unlock(irql);
            return;
        }

        //
        // Get the event from the queue
        //
        event = m_Queue[GetHead()];
        IncrementHead();

        //
        // Drop the state machine *queue* lock
        //
        Unlock(irql);

        //
        // Get the state table entry for the current state
        //
        // NOTE: Prefast complains about buffer overflow if (m_CurrentState ==
        // WakeInterruptMax), but that should never happen because WakeInterruptMax is not a real 
        // state. We just use it to represent the maximum value in the enum that
        // defines the states.
        //
        __analysis_assume(m_CurrentState < WakeInterruptMax);
        entry = &m_StateTable[m_CurrentState - WakeInterruptFailed];

        //
        // Based on the event received, figure out the next state
        //
        newState = WakeInterruptMax;
        for (ULONG i = 0; i < entry->TargetStatesCount; i++) {
            if (entry->TargetStates[i].WakeInterruptEvent == event) {
                DO_EVENT_TRAP(&entry->TargetStates[i]);
                newState = entry->TargetStates[i].WakeInterruptState;
                break;
            }
        }
        
        if (newState == WakeInterruptMax) {
            //
            // Unexpected event for this state
            //
            DoTraceLevelMessage(
                m_PkgPnp->GetDriverGlobals(), 
                TRACE_LEVEL_INFORMATION, 
                TRACINGPNP,
                "WDFDEVICE 0x%p !devobj 0x%p wake interrupt state "
                "%!FxWakeInterruptStates! dropping event "
                "%!FxWakeInterruptEvents!",
                m_PkgPnp->GetDevice()->GetHandle(),
                m_PkgPnp->GetDevice()->GetDeviceObject(),
                m_CurrentState, 
                event
                );

            COVERAGE_TRAP();
        }
        
        while (newState != WakeInterruptMax) {
            DoTraceLevelMessage(
                m_PkgPnp->GetDriverGlobals(), 
                TRACE_LEVEL_INFORMATION, 
                TRACINGPNPPOWERSTATES,
                "WDFDEVICE 0x%p !devobj 0x%p entering wake interrupt "
                "state %!FxWakeInterruptStates! from "
                "%!FxWakeInterruptStates!",
                m_PkgPnp->GetDevice()->GetHandle(),
                m_PkgPnp->GetDevice()->GetDeviceObject(),
                newState, 
                m_CurrentState
                );
        
            //
            // Update the state history array
            //
            m_States.History[IncrementHistoryIndex()] = (UCHAR) newState;
        
            //
            // Move to the new state
            //
            m_CurrentState = (BYTE) newState;
            entry = &m_StateTable[m_CurrentState-WakeInterruptFailed];
        
            //
            // Invoke the state entry function (if present) for the new state
            //
            if (entry->StateFunc != NULL) {
                newState = entry->StateFunc(this);
            }
            else {
                newState = WakeInterruptMax;
            }
        }
    }
    
    return;    
}