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; }