// // Copyright (C) Microsoft. All rights reserved. // #ifndef _FXPOWERIDLESTATEMACHINE_H_ #define _FXPOWERIDLESTATEMACHINE_H_ // // This is a magical number based on inspection. If the queue overflows, // it is OK to increase these numbers without fear of either dependencies or // weird side affects. // const UCHAR FxPowerIdleEventQueueDepth = 8; enum FxPowerIdleEvents { // CAN BE REUSED = 0x0001, PowerIdleEventPowerUpFailed = 0x0002, PowerIdleEventPowerUpComplete = 0x0004, PowerIdleEventPowerDown = 0x0008, PowerIdleEventPowerDownFailed = 0x0010, PowerIdleEventTimerExpired = 0x0020, PowerIdleEventEnabled = 0x0040, PowerIdleEventDisabled = 0x0080, PowerIdleEventIoDecrement = 0x0100, PowerIdleEventIoIncrement = 0x0200, PowerIdleEventStart = 0x0400, PowerIdleEventStop = 0x0800, PowerIdleNull = 0x0000, }; // begin_wpp config // CUSTOM_TYPE(FxPowerIdleEvents, ItemEnum(FxPowerIdleEvents)); // end_wpp enum FxPowerIdleStates { FxIdleStopped = 1, FxIdleStarted, FxIdleStartedPowerUp, FxIdleStartedPowerFailed, FxIdleDisabled, FxIdleCheckIoCount, FxIdleBusy, FxIdleDecrementIo, FxIdleStartTimer, FxIdleTimerRunning, FxIdleTimingOut, FxIdleTimedOut, FxIdleTimedOutIoIncrement, FxIdleTimedOutPowerDown, FxIdleTimedOutPowerDownFailed, FxIdleGoingToDx, FxIdleInDx, FxIdleInDxIoIncrement, FxIdleInDxPowerUpFailure, FxIdleInDxStopped, FxIdleInDxDisabled, FxIdleInDxEnabled, FxIdlePowerUp, FxIdlePowerUpComplete, FxIdleTimedOutDisabled, FxIdleTimedOutEnabled, FxIdleCancelTimer, FxIdleWaitForTimeout, FxIdleTimerExpired, FxIdleDisabling, FxIdleDisablingWaitForTimeout, FxIdleDisablingTimerExpired, FxIdlePowerFailedWaitForTimeout, FxIdlePowerFailed, FxIdleMax, }; // // NOTE: if you change these flags (order, values, etc), you must also modify // m_FlagsByName to match your changes. // enum FxPowerIdleFlags { FxPowerIdleTimerEnabled = 0x01, FxPowerIdleInDx = 0x02, FxPowerIdleTimerCanceled = 0x04, FxPowerIdleTimerStarted = 0x08, FxPowerIdlePowerFailed = 0x10, FxPowerIdleIsStarted = 0x20, FxPowerIdleIoPresentSent = 0x40, FxPowerIdleSendPnpPowerUpEvent = 0x80, }; enum FxPowerReferenceFlags { FxPowerReferenceDefault = 0x0, FxPowerReferenceSendPnpPowerUpEvent = 0x1 }; typedef FxPowerIdleStates (*PFN_POWER_IDLE_STATE_ENTRY_FUNCTION)( FxPowerIdleMachine* ); struct FxPowerIdleTargetState { FxPowerIdleEvents PowerIdleEvent; FxPowerIdleStates PowerIdleState; #if FX_SUPER_DBG BOOLEAN EventDebugged; #endif }; struct FxIdleStateTable { PFN_POWER_IDLE_STATE_ENTRY_FUNCTION StateFunc; const FxPowerIdleTargetState* TargetStates; ULONG TargetStatesCount; }; class FxPowerIdleMachine : public FxStump { public: FxPowerIdleMachine( VOID ); ~FxPowerIdleMachine( VOID ); _Must_inspect_result_ NTSTATUS Init( VOID ); VOID EnableTimer( VOID ); BOOLEAN DisableTimer( VOID ); VOID Start( VOID ); VOID Stop( VOID ); VOID Reset( VOID ); _Must_inspect_result_ NTSTATUS IoIncrement( VOID ); _Must_inspect_result_ NTSTATUS IoIncrementWithFlags( __in FxPowerReferenceFlags Flags, __out_opt PULONG Count = NULL ); VOID IoDecrement( __in_opt PVOID Tag = NULL, __in_opt LONG Line = 0, __in_opt PSTR File = NULL ); BOOLEAN QueryReturnToIdle( VOID ); VOID WaitForD0( VOID ) { m_D0NotificationEvent.EnterCRAndWaitAndLeave(); } _Must_inspect_result_ NTSTATUS PowerReference( __in BOOLEAN WaitForD0, __in_opt PVOID Tag = NULL, __in_opt LONG Line = 0, __in_opt PSTR File = NULL ) { return PowerReferenceWorker(WaitForD0, FxPowerReferenceDefault, Tag, Line, File); } _Must_inspect_result_ NTSTATUS PowerReferenceWithFlags( __in FxPowerReferenceFlags Flags ) { return PowerReferenceWorker(FALSE, // WaitForD0 Flags); } VOID ProcessPowerEvent( __in FxPowerIdleEvents Event ); protected: VOID ProcessEventLocked( __in FxPowerIdleEvents Event ); BOOLEAN IsTransitioning( VOID ) { return m_D0NotificationEvent.ReadState() ? FALSE : TRUE; } BOOLEAN CancelIdleTimer( VOID ) { // // If we are canceling the timer, be well sure it is started // ASSERT(m_Flags & FxPowerIdleTimerStarted); if (m_PowerTimeoutTimer.Stop()) { m_Flags &= ~FxPowerIdleTimerStarted; return TRUE; } else { return FALSE; } } BOOLEAN InD0Locked( VOID ) { return m_D0NotificationEvent.ReadState() ? TRUE : FALSE; } VOID SendD0Notification( VOID ); _Must_inspect_result_ NTSTATUS PowerReferenceWorker( __in BOOLEAN WaitForD0, __in FxPowerReferenceFlags Flags, __in_opt PVOID Tag = NULL, __in_opt LONG Line = 0, __in_opt PSTR File = NULL ); static MdDeferredRoutineType _PowerTimeoutDpcRoutine; static FxPowerIdleStates Stopped( __inout FxPowerIdleMachine* This ); static FxPowerIdleStates Started( __inout FxPowerIdleMachine* This ); static FxPowerIdleStates StartedPowerUp( __inout FxPowerIdleMachine* This ); static FxPowerIdleStates StartedPowerFailed( __inout FxPowerIdleMachine* This ); static FxPowerIdleStates Disabled( __inout FxPowerIdleMachine* This ); static FxPowerIdleStates CheckIoCount( __inout FxPowerIdleMachine* This ); static FxPowerIdleStates DecrementIo( __inout FxPowerIdleMachine* This ); static FxPowerIdleStates StartTimer( __inout FxPowerIdleMachine* This ); static FxPowerIdleStates TimingOut( __inout FxPowerIdleMachine* This ); static FxPowerIdleStates TimedOutIoIncrement( __inout FxPowerIdleMachine* This ); static FxPowerIdleStates TimedOutPowerDown( __inout FxPowerIdleMachine* This ); static FxPowerIdleStates TimedOutPowerDownFailed( __inout FxPowerIdleMachine* This ); static FxPowerIdleStates GoingToDx( __inout FxPowerIdleMachine* This ); static FxPowerIdleStates InDx( __inout FxPowerIdleMachine* This ); static FxPowerIdleStates InDxIoIncrement( __inout FxPowerIdleMachine* This ); static FxPowerIdleStates InDxPowerUpFailure( __inout FxPowerIdleMachine* This ); static FxPowerIdleStates InDxStopped( __inout FxPowerIdleMachine* This ); static FxPowerIdleStates InDxDisabled( __inout FxPowerIdleMachine* This ); static FxPowerIdleStates InDxEnabled( __inout FxPowerIdleMachine* This ); static FxPowerIdleStates PowerUp( __inout FxPowerIdleMachine* This ); static FxPowerIdleStates PowerUpComplete( __inout FxPowerIdleMachine* This ); static FxPowerIdleStates TimedOutDisabled( __inout FxPowerIdleMachine* This ); static FxPowerIdleStates TimedOutEnabled( __inout FxPowerIdleMachine* This ); static FxPowerIdleStates CancelTimer( __inout FxPowerIdleMachine* This ); static FxPowerIdleStates TimerExpired( __inout FxPowerIdleMachine* This ); static FxPowerIdleStates Disabling( __inout FxPowerIdleMachine* This ); static FxPowerIdleStates DisablingTimerExpired( __inout FxPowerIdleMachine* This ); static FxPowerIdleStates PowerFailed( __inout FxPowerIdleMachine* This ); private: VOID CheckAssumptions( VOID ); public: LARGE_INTEGER m_PowerTimeout; protected: // // Lock which guards state // MxLock m_Lock; // // Number of pending requests which require being in D0 // ULONG m_IoCount; // // Tracks power references and releases. // FxTagTracker* m_TagTracker; // // Timer which will be set when the I/O count goes to zero // MxTimer m_PowerTimeoutTimer; // // Event to wait on when transitioning from Dx to D0 // FxCREvent m_D0NotificationEvent; union { // // Combintaion of FxPowerIdleFlags enum values // UCHAR m_Flags; // // Not used in the code. Here so that you can easily decode m_Flags in // the debugger without needing the enum definition. // struct { UCHAR TimerEnabled : 1; UCHAR InDx : 1; UCHAR TimerCanceled : 1; UCHAR TimerStarted : 1; UCHAR TimerPowerFailed : 1; UCHAR IsStarted : 1; UCHAR IoPresentSent : 1; UCHAR SendPnpPowerUpEvent : 1; } m_FlagsByName; }; // // Index into m_EventHistory where to place the next value // UCHAR m_EventHistoryIndex; // // Index into m_StateHistory where to place the next value UCHAR m_StateHistoryIndex; // // our current state // FxPowerIdleStates m_CurrentIdleState; // // Circular history of events fed into this state machine // FxPowerIdleEvents m_EventHistory[FxPowerIdleEventQueueDepth]; // // Circular history of states the state machine was in // FxPowerIdleStates m_StateHistory[FxPowerIdleEventQueueDepth]; static const FxPowerIdleTargetState m_StoppedStates[]; static const FxPowerIdleTargetState m_StartedStates[]; static const FxPowerIdleTargetState m_DisabledStates[]; static const FxPowerIdleTargetState m_BusyStates[]; static const FxPowerIdleTargetState m_TimerRunningStates[]; static const FxPowerIdleTargetState m_TimedOutStates[]; static const FxPowerIdleTargetState m_InDxStates[]; static const FxPowerIdleTargetState m_WaitForTimeoutStates[]; static const FxPowerIdleTargetState m_DisablingWaitForTimeoutStates[]; static const FxPowerIdleTargetState m_PowerFailedWaitForTimeoutStates[]; static const FxIdleStateTable m_StateTable[]; // // We use a coalescable timer for idle timeout. The tolerable delay for the // idle timer is defined below. The value below is an arbitrary choice and // can be changed if necessary. MSDN documentation suggests 100 ms as being // a reasonable choice. // static const ULONG m_IdleTimerTolerableDelayMS = 100; }; #endif // _FXPOWERIDLESTATEMACHINE_H_