mirror of
https://github.com/reactos/reactos.git
synced 2025-03-10 18:24:02 +00:00
2024 lines
46 KiB
C++
2024 lines
46 KiB
C++
![]() |
/*++
|
||
|
|
||
|
Copyright (c) Microsoft. All rights reserved.
|
||
|
|
||
|
Module Name:
|
||
|
|
||
|
PowerIdleStateMachine.cpp
|
||
|
|
||
|
Abstract:
|
||
|
|
||
|
This module implements the Power Policy idle state machine for the driver
|
||
|
framework.
|
||
|
|
||
|
Author:
|
||
|
|
||
|
|
||
|
|
||
|
Environment:
|
||
|
|
||
|
Both kernel and user mode
|
||
|
|
||
|
Revision History:
|
||
|
|
||
|
|
||
|
|
||
|
--*/
|
||
|
|
||
|
#include "pnppriv.hpp"
|
||
|
|
||
|
extern "C" {
|
||
|
#if defined(EVENT_TRACING)
|
||
|
#include "PowerIdleStateMachine.tmh"
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
const FxPowerIdleTargetState FxPowerIdleMachine::m_StoppedStates[] =
|
||
|
{
|
||
|
{ PowerIdleEventStart, FxIdleStarted DEBUGGED_EVENT },
|
||
|
};
|
||
|
|
||
|
const FxPowerIdleTargetState FxPowerIdleMachine::m_StartedStates[] =
|
||
|
{
|
||
|
{ PowerIdleEventPowerUpComplete, FxIdleStartedPowerUp DEBUGGED_EVENT },
|
||
|
{ PowerIdleEventPowerUpFailed, FxIdleStartedPowerFailed DEBUGGED_EVENT },
|
||
|
{ PowerIdleEventStop, FxIdleStopped DEBUGGED_EVENT },
|
||
|
};
|
||
|
|
||
|
const FxPowerIdleTargetState FxPowerIdleMachine::m_DisabledStates[] =
|
||
|
{
|
||
|
{ PowerIdleEventEnabled, FxIdleCheckIoCount DEBUGGED_EVENT },
|
||
|
{ PowerIdleEventDisabled, FxIdleDisabled DEBUGGED_EVENT },
|
||
|
{ PowerIdleEventPowerDown, FxIdleGoingToDx DEBUGGED_EVENT },
|
||
|
{ PowerIdleEventPowerDownFailed, FxIdlePowerFailed DEBUGGED_EVENT },
|
||
|
{ PowerIdleEventPowerUpFailed, FxIdlePowerFailed DEBUGGED_EVENT },
|
||
|
{ PowerIdleEventStop, FxIdleStopped DEBUGGED_EVENT },
|
||
|
};
|
||
|
|
||
|
const FxPowerIdleTargetState FxPowerIdleMachine::m_BusyStates[] =
|
||
|
{
|
||
|
{ PowerIdleEventIoDecrement, FxIdleDecrementIo DEBUGGED_EVENT },
|
||
|
{ PowerIdleEventDisabled, FxIdleDisabled DEBUGGED_EVENT },
|
||
|
{ PowerIdleEventPowerDown, FxIdleGoingToDx TRAP_ON_EVENT },
|
||
|
{ PowerIdleEventPowerDownFailed, FxIdlePowerFailed TRAP_ON_EVENT },
|
||
|
};
|
||
|
|
||
|
const FxPowerIdleTargetState FxPowerIdleMachine::m_TimerRunningStates[] =
|
||
|
{
|
||
|
{ PowerIdleEventDisabled, FxIdleDisabling DEBUGGED_EVENT },
|
||
|
{ PowerIdleEventIoIncrement, FxIdleCancelTimer DEBUGGED_EVENT },
|
||
|
{ PowerIdleEventEnabled, FxIdleCancelTimer DEBUGGED_EVENT },
|
||
|
{ PowerIdleEventTimerExpired, FxIdleTimingOut DEBUGGED_EVENT },
|
||
|
};
|
||
|
|
||
|
const FxPowerIdleTargetState FxPowerIdleMachine::m_TimedOutStates[] =
|
||
|
{
|
||
|
{ PowerIdleEventPowerDown, FxIdleTimedOutPowerDown DEBUGGED_EVENT },
|
||
|
{ PowerIdleEventPowerDownFailed, FxIdleTimedOutPowerDownFailed DEBUGGED_EVENT },
|
||
|
{ PowerIdleEventDisabled, FxIdleTimedOutDisabled DEBUGGED_EVENT },
|
||
|
{ PowerIdleEventEnabled, FxIdleTimedOutEnabled DEBUGGED_EVENT },
|
||
|
{ PowerIdleEventIoIncrement, FxIdleTimedOutIoIncrement DEBUGGED_EVENT },
|
||
|
};
|
||
|
|
||
|
const FxPowerIdleTargetState FxPowerIdleMachine::m_InDxStates[] =
|
||
|
{
|
||
|
{ PowerIdleEventPowerUpComplete, FxIdlePowerUp DEBUGGED_EVENT },
|
||
|
{ PowerIdleEventPowerUpFailed, FxIdleInDxPowerUpFailure DEBUGGED_EVENT },
|
||
|
{ PowerIdleEventPowerDownFailed, FxIdleInDxPowerUpFailure TRAP_ON_EVENT },
|
||
|
{ PowerIdleEventStop, FxIdleInDxStopped DEBUGGED_EVENT },
|
||
|
{ PowerIdleEventDisabled, FxIdleInDxDisabled DEBUGGED_EVENT },
|
||
|
{ PowerIdleEventIoIncrement, FxIdleInDxIoIncrement DEBUGGED_EVENT },
|
||
|
{ PowerIdleEventIoDecrement, FxIdleInDx DEBUGGED_EVENT },
|
||
|
{ PowerIdleEventPowerDown, FxIdleInDx DEBUGGED_EVENT },
|
||
|
{ PowerIdleEventEnabled, FxIdleInDxEnabled DEBUGGED_EVENT },
|
||
|
};
|
||
|
|
||
|
const FxPowerIdleTargetState FxPowerIdleMachine::m_WaitForTimeoutStates[] =
|
||
|
{
|
||
|
{ PowerIdleEventTimerExpired, FxIdleTimerExpired DEBUGGED_EVENT },
|
||
|
{ PowerIdleEventPowerDownFailed, FxIdlePowerFailedWaitForTimeout TRAP_ON_EVENT },
|
||
|
{ PowerIdleEventDisabled, FxIdleDisablingWaitForTimeout DEBUGGED_EVENT },
|
||
|
};
|
||
|
|
||
|
const FxPowerIdleTargetState FxPowerIdleMachine::m_DisablingWaitForTimeoutStates[] =
|
||
|
{
|
||
|
{ PowerIdleEventTimerExpired, FxIdleDisablingTimerExpired DEBUGGED_EVENT },
|
||
|
};
|
||
|
|
||
|
const FxPowerIdleTargetState FxPowerIdleMachine::m_PowerFailedWaitForTimeoutStates[] =
|
||
|
{
|
||
|
{ PowerIdleEventTimerExpired, FxIdlePowerFailed TRAP_ON_EVENT },
|
||
|
};
|
||
|
|
||
|
const FxIdleStateTable FxPowerIdleMachine::m_StateTable[] =
|
||
|
{
|
||
|
// FxIdleStopped
|
||
|
{ FxPowerIdleMachine::Stopped,
|
||
|
FxPowerIdleMachine::m_StoppedStates,
|
||
|
ARRAY_SIZE(FxPowerIdleMachine::m_StoppedStates),
|
||
|
},
|
||
|
|
||
|
// FxIdleStarted
|
||
|
{ FxPowerIdleMachine::Started,
|
||
|
FxPowerIdleMachine::m_StartedStates,
|
||
|
ARRAY_SIZE(FxPowerIdleMachine::m_StartedStates),
|
||
|
},
|
||
|
|
||
|
// FxIdleStartedPowerUp
|
||
|
{ FxPowerIdleMachine::StartedPowerUp,
|
||
|
NULL,
|
||
|
0,
|
||
|
},
|
||
|
|
||
|
// FxIdleStartedPowerFailed
|
||
|
{ FxPowerIdleMachine::StartedPowerFailed,
|
||
|
NULL,
|
||
|
0,
|
||
|
},
|
||
|
|
||
|
// FxIdleDisabled
|
||
|
{ FxPowerIdleMachine::Disabled,
|
||
|
FxPowerIdleMachine::m_DisabledStates,
|
||
|
ARRAY_SIZE(FxPowerIdleMachine::m_DisabledStates),
|
||
|
},
|
||
|
|
||
|
// FxIdleCheckIoCount
|
||
|
{ FxPowerIdleMachine::CheckIoCount,
|
||
|
NULL,
|
||
|
0,
|
||
|
},
|
||
|
|
||
|
// FxIdleBusy
|
||
|
{ NULL,
|
||
|
FxPowerIdleMachine::m_BusyStates,
|
||
|
ARRAY_SIZE(FxPowerIdleMachine::m_BusyStates),
|
||
|
},
|
||
|
|
||
|
// FxIdleDecrementIo
|
||
|
{ FxPowerIdleMachine::DecrementIo,
|
||
|
NULL,
|
||
|
0,
|
||
|
},
|
||
|
|
||
|
// FxIdleStartTimer
|
||
|
{ FxPowerIdleMachine::StartTimer,
|
||
|
NULL,
|
||
|
0,
|
||
|
},
|
||
|
|
||
|
// FxIdleTimerRunning
|
||
|
{ NULL,
|
||
|
FxPowerIdleMachine::m_TimerRunningStates,
|
||
|
ARRAY_SIZE(FxPowerIdleMachine::m_TimerRunningStates)
|
||
|
},
|
||
|
|
||
|
// FxIdleTimingOut
|
||
|
{ FxPowerIdleMachine::TimingOut,
|
||
|
NULL,
|
||
|
0,
|
||
|
},
|
||
|
|
||
|
// FxIdleTimedOut
|
||
|
{ NULL,
|
||
|
FxPowerIdleMachine::m_TimedOutStates,
|
||
|
ARRAY_SIZE(FxPowerIdleMachine::m_TimedOutStates),
|
||
|
},
|
||
|
|
||
|
// FxIdleTimedOutIoIncrement
|
||
|
{ FxPowerIdleMachine::TimedOutIoIncrement,
|
||
|
NULL,
|
||
|
0,
|
||
|
},
|
||
|
|
||
|
// FxIdleTimedOutPowerDown
|
||
|
{ FxPowerIdleMachine::TimedOutPowerDown,
|
||
|
NULL,
|
||
|
0,
|
||
|
},
|
||
|
|
||
|
// FxIdleTimedOutPowerDownFailed
|
||
|
{ FxPowerIdleMachine::TimedOutPowerDownFailed,
|
||
|
NULL,
|
||
|
0,
|
||
|
},
|
||
|
|
||
|
// FxIdleGoingToDx,
|
||
|
{ FxPowerIdleMachine::GoingToDx,
|
||
|
NULL,
|
||
|
0,
|
||
|
},
|
||
|
|
||
|
// FxIdleInDx,
|
||
|
{ FxPowerIdleMachine::InDx,
|
||
|
FxPowerIdleMachine::m_InDxStates,
|
||
|
ARRAY_SIZE(FxPowerIdleMachine::m_InDxStates),
|
||
|
},
|
||
|
|
||
|
// FxIdleInDxIoIncrement
|
||
|
{ FxPowerIdleMachine::InDxIoIncrement,
|
||
|
NULL,
|
||
|
0,
|
||
|
},
|
||
|
|
||
|
// FxIdleInDxPowerUpFailure
|
||
|
{ FxPowerIdleMachine::InDxPowerUpFailure,
|
||
|
NULL,
|
||
|
0,
|
||
|
},
|
||
|
|
||
|
// FxIdleInDxStopped
|
||
|
{ FxPowerIdleMachine::InDxStopped,
|
||
|
NULL,
|
||
|
0,
|
||
|
},
|
||
|
|
||
|
// FxIdleInDxDisabled
|
||
|
{ FxPowerIdleMachine::InDxDisabled,
|
||
|
NULL,
|
||
|
0,
|
||
|
},
|
||
|
|
||
|
// FxIdleInDxEnabled
|
||
|
{ FxPowerIdleMachine::InDxEnabled,
|
||
|
NULL,
|
||
|
0,
|
||
|
},
|
||
|
|
||
|
// FxIdlePowerUp
|
||
|
{ FxPowerIdleMachine::PowerUp,
|
||
|
NULL,
|
||
|
0,
|
||
|
},
|
||
|
|
||
|
// FxIdlePowerUpComplete
|
||
|
{ FxPowerIdleMachine::PowerUpComplete,
|
||
|
NULL,
|
||
|
0,
|
||
|
},
|
||
|
|
||
|
// FxIdleTimedOutDisabled
|
||
|
{ FxPowerIdleMachine::TimedOutDisabled,
|
||
|
NULL,
|
||
|
0,
|
||
|
},
|
||
|
|
||
|
// FxIdleTimedOutEnabled
|
||
|
{ FxPowerIdleMachine::TimedOutEnabled,
|
||
|
NULL,
|
||
|
0,
|
||
|
},
|
||
|
|
||
|
// FxIdleCancelTimer
|
||
|
{ FxPowerIdleMachine::CancelTimer,
|
||
|
NULL,
|
||
|
0,
|
||
|
},
|
||
|
|
||
|
// FxIdleWaitForTimeout
|
||
|
{ NULL,
|
||
|
FxPowerIdleMachine::m_WaitForTimeoutStates,
|
||
|
ARRAY_SIZE(FxPowerIdleMachine::m_WaitForTimeoutStates),
|
||
|
},
|
||
|
|
||
|
// FxIdleTimerExpired
|
||
|
{ FxPowerIdleMachine::TimerExpired,
|
||
|
NULL,
|
||
|
0,
|
||
|
},
|
||
|
|
||
|
// FxIdleDisabling
|
||
|
{ FxPowerIdleMachine::Disabling,
|
||
|
NULL,
|
||
|
0,
|
||
|
},
|
||
|
|
||
|
// FxIdleDisablingWaitForTimeout
|
||
|
{ NULL,
|
||
|
FxPowerIdleMachine::m_DisablingWaitForTimeoutStates,
|
||
|
ARRAY_SIZE(FxPowerIdleMachine::m_DisablingWaitForTimeoutStates),
|
||
|
},
|
||
|
|
||
|
// FxIdleDisablingTimerExpired
|
||
|
{ FxPowerIdleMachine::DisablingTimerExpired,
|
||
|
NULL,
|
||
|
0,
|
||
|
},
|
||
|
|
||
|
// FxIdlePowerFailedWaitForTimeout
|
||
|
{ NULL,
|
||
|
FxPowerIdleMachine::m_PowerFailedWaitForTimeoutStates,
|
||
|
ARRAY_SIZE(FxPowerIdleMachine::m_PowerFailedWaitForTimeoutStates),
|
||
|
},
|
||
|
|
||
|
// FxIdlePowerFailed
|
||
|
{ FxPowerIdleMachine::PowerFailed,
|
||
|
NULL,
|
||
|
0,
|
||
|
},
|
||
|
};
|
||
|
|
||
|
__inline
|
||
|
FxPkgPnp*
|
||
|
GetPnpPkg(
|
||
|
__inout FxPowerIdleMachine* This
|
||
|
)
|
||
|
{
|
||
|
return CONTAINING_RECORD(This,
|
||
|
FxPowerPolicyOwnerSettings,
|
||
|
m_PowerIdleMachine)->m_PkgPnp;
|
||
|
}
|
||
|
|
||
|
|
||
|
FxPowerIdleMachine::FxPowerIdleMachine(
|
||
|
VOID
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
Constructs the power idle state machine
|
||
|
|
||
|
Arguments:
|
||
|
None
|
||
|
|
||
|
Return Value:
|
||
|
None
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
//
|
||
|
// m_Lock and m_PowerTimeoutTimer are now being initialized in Init method
|
||
|
// since they may fail for UM.
|
||
|
//
|
||
|
|
||
|
m_PowerTimeout.QuadPart = 0;
|
||
|
m_CurrentIdleState = FxIdleStopped;
|
||
|
|
||
|
m_EventHistoryIndex = 0;
|
||
|
m_StateHistoryIndex = 0;
|
||
|
|
||
|
RtlZeroMemory(&m_EventHistory[0], sizeof(m_EventHistory));
|
||
|
RtlZeroMemory(&m_StateHistory[0], sizeof(m_StateHistory));
|
||
|
|
||
|
m_TagTracker = NULL;
|
||
|
}
|
||
|
|
||
|
FxPowerIdleMachine::~FxPowerIdleMachine(
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
if (m_TagTracker != NULL) {
|
||
|
delete m_TagTracker;
|
||
|
m_TagTracker = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_Must_inspect_result_
|
||
|
NTSTATUS
|
||
|
FxPowerIdleMachine::Init(
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
NTSTATUS status;
|
||
|
|
||
|
//
|
||
|
// For KM, event initialize always succeeds. For UM, it might fail.
|
||
|
//
|
||
|
status = m_D0NotificationEvent.Initialize(NotificationEvent, TRUE);
|
||
|
if (!NT_SUCCESS(status)) {
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// For KM, timer initialize always succeeds. For UM, it might fail.
|
||
|
//
|
||
|
status = m_PowerTimeoutTimer.Initialize(this, _PowerTimeoutDpcRoutine, 0);
|
||
|
if (!NT_SUCCESS(status)) {
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
Reset();
|
||
|
|
||
|
return STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
FxPowerIdleMachine::CheckAssumptions(
|
||
|
VOID
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
This routine is never actually called by running code, it just has
|
||
|
WDFCASSERTs who upon failure, would not allow this file to be compiled.
|
||
|
|
||
|
DO NOT REMOVE THIS FUNCTION just because it is not called by any running
|
||
|
code.
|
||
|
|
||
|
Arguments:
|
||
|
None
|
||
|
|
||
|
Return Value:
|
||
|
None
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
WDFCASSERT((sizeof(m_StateTable)/sizeof(m_StateTable[0]))
|
||
|
==
|
||
|
(FxIdleMax - FxIdleStopped));
|
||
|
}
|
||
|
|
||
|
FxPowerIdleStates
|
||
|
FxPowerIdleMachine::Stopped(
|
||
|
__inout FxPowerIdleMachine* This
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
State machine has entered the stopped state, clear the started flag
|
||
|
|
||
|
Arguments:
|
||
|
This - instance of the state machine
|
||
|
|
||
|
Return Value:
|
||
|
FxIdleMax
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
This->m_Flags &= ~FxPowerIdleIsStarted;
|
||
|
|
||
|
return FxIdleMax;
|
||
|
}
|
||
|
|
||
|
FxPowerIdleStates
|
||
|
FxPowerIdleMachine::Started(
|
||
|
__inout FxPowerIdleMachine* This
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
State machine has entered the started state, set the started flag
|
||
|
|
||
|
Arguments:
|
||
|
This - instance of the state machine
|
||
|
|
||
|
Return Value:
|
||
|
FxIdleMax
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
This->m_Flags |= FxPowerIdleIsStarted;
|
||
|
|
||
|
//
|
||
|
// We are in the started state, but we are not powered up.
|
||
|
//
|
||
|
This->m_D0NotificationEvent.Clear();
|
||
|
|
||
|
return FxIdleMax;
|
||
|
}
|
||
|
|
||
|
FxPowerIdleStates
|
||
|
FxPowerIdleMachine::StartedPowerUp(
|
||
|
__inout FxPowerIdleMachine* This
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
We were in the started and powered off state. We are powered up,
|
||
|
so set the event now so that we can wake up any waiters.
|
||
|
|
||
|
Arguments:
|
||
|
This - instance of the state machine
|
||
|
|
||
|
Return Value:
|
||
|
FxIdleDisabled
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
//
|
||
|
// Moving from the started state to the powered on state
|
||
|
//
|
||
|
This->SendD0Notification();
|
||
|
|
||
|
return FxIdleDisabled;
|
||
|
}
|
||
|
|
||
|
FxPowerIdleStates
|
||
|
FxPowerIdleMachine::StartedPowerFailed(
|
||
|
__inout FxPowerIdleMachine* This
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
The state machine was started, but the initial power up failed. Mark the
|
||
|
failure.
|
||
|
|
||
|
Arguments:
|
||
|
This - instance of the state machine
|
||
|
|
||
|
Return Value:
|
||
|
FxIdleStarted
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
//
|
||
|
// Failed to initially power up
|
||
|
//
|
||
|
This->m_Flags |= FxPowerIdlePowerFailed;
|
||
|
|
||
|
//
|
||
|
// We assume in the started state that the event is set
|
||
|
//
|
||
|
ASSERT(This->m_D0NotificationEvent.ReadState() == 0);
|
||
|
|
||
|
return FxIdleStarted;
|
||
|
}
|
||
|
|
||
|
FxPowerIdleStates
|
||
|
FxPowerIdleMachine::Disabled(
|
||
|
__inout FxPowerIdleMachine* This
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
State machine has entered the disabled state, unblock all waiters
|
||
|
|
||
|
Arguments:
|
||
|
This - instance of the state machine
|
||
|
|
||
|
Return Value:
|
||
|
FxIdleMax
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
This->m_Flags &= ~FxPowerIdleTimerEnabled;
|
||
|
|
||
|
return FxIdleMax;
|
||
|
}
|
||
|
|
||
|
FxPowerIdleStates
|
||
|
FxPowerIdleMachine::CheckIoCount(
|
||
|
__inout FxPowerIdleMachine* This
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
Checks the IO count and transitions the appropriate state. This is the
|
||
|
first state we are in after being disabled or after transitioning from Dx to
|
||
|
D0.
|
||
|
|
||
|
Arguments:
|
||
|
This - instance of the state machine
|
||
|
|
||
|
Return Value:
|
||
|
new state machine state
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
This->m_Flags |= FxPowerIdleTimerEnabled;
|
||
|
|
||
|
if (This->m_IoCount == 0) {
|
||
|
return FxIdleStartTimer;
|
||
|
}
|
||
|
else {
|
||
|
return FxIdleBusy;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
FxPowerIdleStates
|
||
|
FxPowerIdleMachine::DecrementIo(
|
||
|
__inout FxPowerIdleMachine* This
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
Checks the IO count and returns a new state
|
||
|
|
||
|
Arguments:
|
||
|
This - instance of the state machine
|
||
|
|
||
|
Return Value:
|
||
|
new state machine state
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
if (This->m_IoCount == 0) {
|
||
|
return FxIdleStartTimer;
|
||
|
}
|
||
|
else {
|
||
|
return FxIdleBusy;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
FxPowerIdleStates
|
||
|
FxPowerIdleMachine::StartTimer(
|
||
|
__inout FxPowerIdleMachine* This
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
The io count is now at zero. Start the idle timer so that when it expires,
|
||
|
the device will move into Dx.
|
||
|
|
||
|
Arguments:
|
||
|
This - instance of the state machine
|
||
|
|
||
|
Return Value:
|
||
|
FxIdleMax
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
ASSERT((This->m_Flags & FxPowerIdleTimerEnabled) && This->m_IoCount == 0);
|
||
|
|
||
|
This->m_Flags |= FxPowerIdleTimerStarted;
|
||
|
This->m_PowerTimeoutTimer.Start(This->m_PowerTimeout,
|
||
|
m_IdleTimerTolerableDelayMS);
|
||
|
|
||
|
return FxIdleTimerRunning;
|
||
|
}
|
||
|
|
||
|
|
||
|
FxPowerIdleStates
|
||
|
FxPowerIdleMachine::TimingOut(
|
||
|
__inout FxPowerIdleMachine* This
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
The idle timer has expired. Indicate to the power policy state machine
|
||
|
that it should power down.
|
||
|
|
||
|
Arguments:
|
||
|
This - instance of the state machine
|
||
|
|
||
|
Return Value:
|
||
|
FxIdleTimedOut
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
#if (FX_CORE_MODE==FX_CORE_KERNEL_MODE)
|
||
|
GetPnpPkg(This)->PowerPolicyProcessEvent(PwrPolPowerTimeoutExpired);
|
||
|
#else
|
||
|
GetPnpPkg(This)->PowerPolicyProcessEvent(
|
||
|
PwrPolPowerTimeoutExpired,
|
||
|
TRUE // ProcessEventOnDifferentThread
|
||
|
);
|
||
|
#endif
|
||
|
|
||
|
//
|
||
|
// Timer is no longer running. Used when we disable the state machine and
|
||
|
// need to know the timer's running state.
|
||
|
//
|
||
|
//
|
||
|
This->m_Flags &= ~FxPowerIdleTimerStarted;
|
||
|
|
||
|
//
|
||
|
// While the device is still powered up, we are no longer in D0 in terms of
|
||
|
// PowerReference returning immmediately if TRUE is specified to that call.
|
||
|
//
|
||
|
This->m_D0NotificationEvent.Clear();
|
||
|
|
||
|
return FxIdleTimedOut;
|
||
|
}
|
||
|
|
||
|
FxPowerIdleStates
|
||
|
FxPowerIdleMachine::TimedOutIoIncrement(
|
||
|
__inout FxPowerIdleMachine* This
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
A power reference occurred after we notified the power policy machine of
|
||
|
a power timeout, but before we timed out. Send an io present event to the
|
||
|
power policy machine so that it can move into the D0 state/not timed out
|
||
|
state again.
|
||
|
|
||
|
Arguments:
|
||
|
This - instance of the state machine
|
||
|
|
||
|
Return Value:
|
||
|
FxIdleTimedOut
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
FxPkgPnp* pPkgPnp;
|
||
|
|
||
|
pPkgPnp = GetPnpPkg(This);
|
||
|
|
||
|
if (This->m_Flags & FxPowerIdleIoPresentSent) {
|
||
|
DoTraceLevelMessage(
|
||
|
pPkgPnp->GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGPNP,
|
||
|
"WDFDEVICE %p idle (in D0) not sending io present event (already sent)",
|
||
|
pPkgPnp->GetDevice()->GetHandle());
|
||
|
}
|
||
|
else {
|
||
|
#if (FX_CORE_MODE==FX_CORE_KERNEL_MODE)
|
||
|
pPkgPnp->PowerPolicyProcessEvent(PwrPolIoPresent);
|
||
|
#else
|
||
|
pPkgPnp->PowerPolicyProcessEvent(
|
||
|
PwrPolIoPresent,
|
||
|
TRUE // ProcessEventOnDifferentThread
|
||
|
);
|
||
|
#endif
|
||
|
|
||
|
This->m_Flags |= FxPowerIdleIoPresentSent;
|
||
|
}
|
||
|
|
||
|
return FxIdleTimedOut;
|
||
|
}
|
||
|
|
||
|
FxPowerIdleStates
|
||
|
FxPowerIdleMachine::TimedOutPowerDown(
|
||
|
__inout FxPowerIdleMachine* This
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
The idle timer fired and we are now powering down. Clear the flag that
|
||
|
limits our sending of the io present event to one time while in the timed
|
||
|
out state.
|
||
|
|
||
|
Arguments:
|
||
|
This - instance of the state machine
|
||
|
|
||
|
Return Value:
|
||
|
FxIdleGoingToDx
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
//
|
||
|
// We can send the io present event again
|
||
|
//
|
||
|
This->m_Flags &= ~FxPowerIdleIoPresentSent;
|
||
|
|
||
|
return FxIdleGoingToDx;
|
||
|
}
|
||
|
|
||
|
FxPowerIdleStates
|
||
|
FxPowerIdleMachine::TimedOutPowerDownFailed(
|
||
|
__inout FxPowerIdleMachine* This
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
The idle timer fired and we could no power down. Clear the flag that
|
||
|
limits our sending of the io present event to one time while in the timed
|
||
|
out state.
|
||
|
|
||
|
Arguments:
|
||
|
This - instance of the state machine
|
||
|
|
||
|
Return Value:
|
||
|
FxIdlePowerFailed
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
//
|
||
|
// We can send the io present event again
|
||
|
//
|
||
|
This->m_Flags &= ~FxPowerIdleIoPresentSent;
|
||
|
|
||
|
return FxIdlePowerFailed;
|
||
|
}
|
||
|
|
||
|
FxPowerIdleStates
|
||
|
FxPowerIdleMachine::GoingToDx(
|
||
|
__inout FxPowerIdleMachine* This
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
The device is going into Dx. It could be going into Dx because the idle
|
||
|
timer expired or because the machine is moving into Sx, the reason doesn't
|
||
|
matter though. Clear the in D0 event.
|
||
|
|
||
|
Arguments:
|
||
|
This - instance of the state machine
|
||
|
|
||
|
Return Value:
|
||
|
FxIdleInDx
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
This->m_D0NotificationEvent.Clear();
|
||
|
return FxIdleInDx;
|
||
|
}
|
||
|
|
||
|
FxPowerIdleStates
|
||
|
FxPowerIdleMachine::InDx(
|
||
|
__inout FxPowerIdleMachine* This
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
The device has moved into Dx. If there is no pending io, mark the device
|
||
|
as idle. We can be in Dx with pending io if IoIncrement was called after
|
||
|
the device moved into Dx.
|
||
|
|
||
|
Arguments:
|
||
|
This - instance of the state machine
|
||
|
|
||
|
Return Value:
|
||
|
FxIdleMax
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
This->m_Flags |= FxPowerIdleInDx;
|
||
|
|
||
|
return FxIdleMax;
|
||
|
}
|
||
|
|
||
|
FxPowerIdleStates
|
||
|
FxPowerIdleMachine::InDxIoIncrement(
|
||
|
__inout FxPowerIdleMachine* This
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
In Dx and the io count went up. Send the event to the power policy state
|
||
|
machine indicating new io is present.
|
||
|
|
||
|
Arguments:
|
||
|
This - instance of the state machine
|
||
|
|
||
|
Return Value:
|
||
|
FxIdleInDx
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
FxPkgPnp* pPkgPnp;
|
||
|
|
||
|
pPkgPnp = GetPnpPkg(This);
|
||
|
|
||
|
if (This->m_Flags & FxPowerIdleIoPresentSent) {
|
||
|
DoTraceLevelMessage(
|
||
|
pPkgPnp->GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGPNP,
|
||
|
"WDFDEVICE %p idle (in Dx) not sending io present event (already sent)",
|
||
|
pPkgPnp->GetDevice()->GetHandle());
|
||
|
}
|
||
|
else {
|
||
|
#if (FX_CORE_MODE==FX_CORE_KERNEL_MODE)
|
||
|
pPkgPnp->PowerPolicyProcessEvent(PwrPolIoPresent);
|
||
|
#else
|
||
|
pPkgPnp->PowerPolicyProcessEvent(
|
||
|
PwrPolIoPresent,
|
||
|
TRUE // ProcessEventOnDifferentThread
|
||
|
);
|
||
|
#endif
|
||
|
This->m_Flags |= FxPowerIdleIoPresentSent;
|
||
|
}
|
||
|
|
||
|
return FxIdleInDx;
|
||
|
}
|
||
|
|
||
|
FxPowerIdleStates
|
||
|
FxPowerIdleMachine::InDxPowerUpFailure(
|
||
|
__inout FxPowerIdleMachine* This
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
Device is in Dx and there was a failure in the power up path. The device
|
||
|
is no longer idle, even though it is stil in Dx.
|
||
|
|
||
|
Arguments:
|
||
|
This - instance of the state machine
|
||
|
|
||
|
Return Value:
|
||
|
FxIdlePowerFailed
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
//
|
||
|
// FxPowerIdleInDx - We are no longer in Dx
|
||
|
// FxPowerIdleIoPresentSent = We can send the io present event again
|
||
|
//
|
||
|
This->m_Flags &= ~(FxPowerIdleInDx | FxPowerIdleIoPresentSent);
|
||
|
|
||
|
return FxIdlePowerFailed;
|
||
|
}
|
||
|
|
||
|
FxPowerIdleStates
|
||
|
FxPowerIdleMachine::InDxStopped(
|
||
|
__inout FxPowerIdleMachine* This
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
The state machine was stopped while in the Dx state. When the machine is in
|
||
|
the stopped state, the notification event is in the signaled state.
|
||
|
|
||
|
Arguments:
|
||
|
This - instance of the state machine
|
||
|
|
||
|
Return Value:
|
||
|
FxIdleStopped
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
//
|
||
|
// When the machine is in the stopped state, the notification event is in
|
||
|
// the signaled state.
|
||
|
//
|
||
|
This->SendD0Notification();
|
||
|
|
||
|
//
|
||
|
// We are not longer idle since we are not in the Dx state anymore
|
||
|
//
|
||
|
This->m_Flags &= ~FxPowerIdleInDx;
|
||
|
|
||
|
//
|
||
|
// We are no longer enabled since we are stopped from the Dx state
|
||
|
//
|
||
|
This->m_Flags &= ~FxPowerIdleTimerEnabled;
|
||
|
|
||
|
//
|
||
|
// We can send the io present event again
|
||
|
//
|
||
|
This->m_Flags &= ~FxPowerIdleIoPresentSent;
|
||
|
|
||
|
return FxIdleStopped;
|
||
|
}
|
||
|
|
||
|
FxPowerIdleStates
|
||
|
FxPowerIdleMachine::InDxDisabled(
|
||
|
__inout FxPowerIdleMachine* This
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
The device is in Dx and the state machine is being disabled (most likely due
|
||
|
to a surprise remove).
|
||
|
|
||
|
Arguments:
|
||
|
This - instance of the state machine
|
||
|
|
||
|
Return Value:
|
||
|
FxIdleDisabled
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
//
|
||
|
// Idle timer is being disabled while in Dx. Remain in Dx.
|
||
|
//
|
||
|
This->m_Flags &= ~FxPowerIdleTimerEnabled;
|
||
|
|
||
|
return FxIdleInDx;
|
||
|
}
|
||
|
|
||
|
FxPowerIdleStates
|
||
|
FxPowerIdleMachine::InDxEnabled(
|
||
|
__inout FxPowerIdleMachine* This
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
The device is in Dx and the state machine is being enabled (most like due
|
||
|
to trying to remain in Dx after Sx->S0.
|
||
|
|
||
|
Arguments:
|
||
|
This - instance of the state machine
|
||
|
|
||
|
Return Value:
|
||
|
FxIdleInDx
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
//
|
||
|
// Idle timer is being enabled while in Dx. Remain in Dx.
|
||
|
//
|
||
|
This->m_Flags |= FxPowerIdleTimerEnabled;
|
||
|
|
||
|
return FxIdleInDx;
|
||
|
}
|
||
|
|
||
|
FxPowerIdleStates
|
||
|
FxPowerIdleMachine::PowerUp(
|
||
|
__inout FxPowerIdleMachine* This
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
The device powered up enough to where we can let waiters go and start pounding
|
||
|
on their hardware.
|
||
|
|
||
|
Arguments:
|
||
|
This - instance of the state machine
|
||
|
|
||
|
Return Value:
|
||
|
FxIdlePowerUpComplete
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
//
|
||
|
// FxPowerIdleInDx - we are not longer idle since we are not in the Dx state
|
||
|
// anymore
|
||
|
// FxPowerIdleIoPresentSent - We can send the io present event again
|
||
|
//
|
||
|
This->m_Flags &= ~(FxPowerIdleInDx | FxPowerIdleIoPresentSent);
|
||
|
|
||
|
This->SendD0Notification();
|
||
|
|
||
|
return FxIdlePowerUpComplete;
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
FxPowerIdleStates
|
||
|
FxPowerIdleMachine::TimedOutDisabled(
|
||
|
__inout FxPowerIdleMachine* This
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
The power idle state machine is moving into the disabled state. Set the
|
||
|
D0 event.
|
||
|
|
||
|
Arguments:
|
||
|
This - instance of the state machine
|
||
|
|
||
|
Return Value:
|
||
|
FxIdleDisabled
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
//
|
||
|
// Notify any waiters that we are now in D0
|
||
|
//
|
||
|
This->SendD0Notification();
|
||
|
|
||
|
//
|
||
|
// We can send the io present event again
|
||
|
//
|
||
|
This->m_Flags &= ~FxPowerIdleIoPresentSent;
|
||
|
|
||
|
return FxIdleDisabled;
|
||
|
}
|
||
|
|
||
|
FxPowerIdleStates
|
||
|
FxPowerIdleMachine::TimedOutEnabled(
|
||
|
__inout FxPowerIdleMachine* This
|
||
|
)
|
||
|
{
|
||
|
//
|
||
|
// Notify any waiters that we are now in D0
|
||
|
//
|
||
|
This->SendD0Notification();
|
||
|
|
||
|
//
|
||
|
// We can send the io present event again
|
||
|
//
|
||
|
This->m_Flags &= ~FxPowerIdleIoPresentSent;
|
||
|
|
||
|
return FxIdleCheckIoCount;
|
||
|
}
|
||
|
|
||
|
FxPowerIdleStates
|
||
|
FxPowerIdleMachine::CancelTimer(
|
||
|
__inout FxPowerIdleMachine* This
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
The timer is running and we need to cancel it because of an io increment or
|
||
|
the state machine being disabled.
|
||
|
|
||
|
Arguments:
|
||
|
This - instance of the state machine
|
||
|
|
||
|
Return Value:
|
||
|
new state
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
if (This->CancelIdleTimer()) {
|
||
|
return FxIdleCheckIoCount;
|
||
|
}
|
||
|
else {
|
||
|
return FxIdleWaitForTimeout;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
FxPowerIdleStates
|
||
|
FxPowerIdleMachine::TimerExpired(
|
||
|
__inout FxPowerIdleMachine* This
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
The timer was not canceled because it was running. The timer has now
|
||
|
fired, so we can move forward.
|
||
|
|
||
|
Arguments:
|
||
|
This - instance of the state machine
|
||
|
|
||
|
Return Value:
|
||
|
FxIdleCheckIoCount
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
This->m_Flags &= ~FxPowerIdleTimerStarted;
|
||
|
|
||
|
return FxIdleCheckIoCount;
|
||
|
}
|
||
|
|
||
|
FxPowerIdleStates
|
||
|
FxPowerIdleMachine::Disabling(
|
||
|
__inout FxPowerIdleMachine* This
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
Timer is running and the state machine is being disabled. Cancel the idle
|
||
|
timer.
|
||
|
|
||
|
Arguments:
|
||
|
This - instance of the state machine
|
||
|
|
||
|
Return Value:
|
||
|
new state
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
if (This->CancelIdleTimer()) {
|
||
|
return FxIdleDisabled;
|
||
|
}
|
||
|
else {
|
||
|
return FxIdleDisablingWaitForTimeout;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
FxPowerIdleStates
|
||
|
FxPowerIdleMachine::DisablingTimerExpired(
|
||
|
__inout FxPowerIdleMachine* This
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
When disabling the state machine, the timer could not be canceled. The
|
||
|
timer has now expired and the state machine can move forward.
|
||
|
|
||
|
Arguments:
|
||
|
This - instance of the state machine
|
||
|
|
||
|
Return Value:
|
||
|
FxIdleDisabled
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
This->m_Flags &= ~FxPowerIdleTimerStarted;
|
||
|
|
||
|
#if (FX_CORE_MODE==FX_CORE_KERNEL_MODE)
|
||
|
GetPnpPkg(This)->PowerPolicyProcessEvent(PwrPolPowerTimeoutExpired);
|
||
|
#else
|
||
|
GetPnpPkg(This)->PowerPolicyProcessEvent(
|
||
|
PwrPolPowerTimeoutExpired,
|
||
|
TRUE // ProcessEventOnDifferentThread
|
||
|
);
|
||
|
#endif
|
||
|
|
||
|
return FxIdleDisabled;
|
||
|
}
|
||
|
|
||
|
FxPowerIdleStates
|
||
|
FxPowerIdleMachine::PowerFailed(
|
||
|
__inout FxPowerIdleMachine* This
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
A power operation (up or down) failed. Mark the machine as failed so that
|
||
|
PowerReference will fail properly.
|
||
|
|
||
|
Arguments:
|
||
|
This - instance of the state machine
|
||
|
|
||
|
Return Value:
|
||
|
FxIdleDisabled
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
//
|
||
|
// By this time, the timer should be stopped
|
||
|
//
|
||
|
ASSERT((This->m_Flags & FxPowerIdleTimerStarted) == 0);
|
||
|
|
||
|
This->m_Flags |= FxPowerIdlePowerFailed;
|
||
|
|
||
|
//
|
||
|
// We are no longer enabled to time out since we are in a failed state
|
||
|
//
|
||
|
This->m_Flags &= ~FxPowerIdleTimerEnabled;
|
||
|
|
||
|
//
|
||
|
// Wake up any waiters and indicate failure to them.
|
||
|
//
|
||
|
This->SendD0Notification();
|
||
|
|
||
|
return FxIdleDisabled;
|
||
|
}
|
||
|
|
||
|
__drv_maxIRQL(DISPATCH_LEVEL)
|
||
|
__drv_minIRQL(DISPATCH_LEVEL)
|
||
|
__drv_requiresIRQL(DISPATCH_LEVEL)
|
||
|
__drv_sameIRQL
|
||
|
VOID
|
||
|
FxPowerIdleMachine::_PowerTimeoutDpcRoutine(
|
||
|
__in PKDPC Dpc,
|
||
|
__in_opt PVOID Context,
|
||
|
__in_opt PVOID SystemArgument1,
|
||
|
__in_opt PVOID SystemArgument2
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
Timer DPC which posts the timeout expired event to the power policy state
|
||
|
machine
|
||
|
|
||
|
Arguments:
|
||
|
Dpc - DPC
|
||
|
Context, SysArg1, SysArg2 - Unused
|
||
|
|
||
|
Return Value:
|
||
|
None
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
FxPowerIdleMachine* pThis;
|
||
|
|
||
|
UNREFERENCED_PARAMETER(Dpc);
|
||
|
UNREFERENCED_PARAMETER(SystemArgument1);
|
||
|
UNREFERENCED_PARAMETER(SystemArgument2);
|
||
|
|
||
|
pThis = (FxPowerIdleMachine*) Context;
|
||
|
|
||
|
pThis->m_Lock.AcquireAtDpcLevel();
|
||
|
pThis->ProcessEventLocked(PowerIdleEventTimerExpired);
|
||
|
|
||
|
#if FX_IS_KERNEL_MODE
|
||
|
PFX_DRIVER_GLOBALS pFxDriverGlobals;
|
||
|
PFN_WDF_DRIVER_DEVICE_ADD pDriverDeviceAdd;
|
||
|
|
||
|
pFxDriverGlobals = GetPnpPkg(pThis)->GetDriverGlobals();
|
||
|
|
||
|
//
|
||
|
// We need to provide XPerf with a symbol of the client to figure out
|
||
|
// which component this idle timer is for. Since AddDevice is always there
|
||
|
// we use that to pass the symbol along.
|
||
|
//
|
||
|
pDriverDeviceAdd = pFxDriverGlobals->Driver->GetDriverDeviceAddMethod();
|
||
|
|
||
|
FxPerfTraceDpc(&pDriverDeviceAdd);
|
||
|
#endif
|
||
|
|
||
|
pThis->m_Lock.ReleaseFromDpcLevel();
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
FxPowerIdleMachine::Reset(
|
||
|
VOID
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
Reset the state machine to a known state on a PDO restart.
|
||
|
|
||
|
Arguments:
|
||
|
None
|
||
|
|
||
|
Return Value:
|
||
|
None
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
FxPkgPnp* pPkgPnp;
|
||
|
PFX_DRIVER_GLOBALS pFxDriverGlobals;
|
||
|
|
||
|
ASSERT(m_CurrentIdleState == FxIdleStopped);
|
||
|
|
||
|
m_IoCount = 0;
|
||
|
m_Flags = 0x0;
|
||
|
|
||
|
pPkgPnp = GetPnpPkg(this);
|
||
|
pFxDriverGlobals = pPkgPnp->GetDriverGlobals();
|
||
|
|
||
|
if (pFxDriverGlobals->DebugExtension != NULL &&
|
||
|
pFxDriverGlobals->DebugExtension->TrackPower != FxTrackPowerNone) {
|
||
|
//
|
||
|
// Ignore potential failure, power ref tracking is not an essential feature.
|
||
|
//
|
||
|
(void)FxTagTracker::CreateAndInitialize(&m_TagTracker,
|
||
|
pFxDriverGlobals,
|
||
|
FxTagTrackerTypePower,
|
||
|
pFxDriverGlobals->DebugExtension->TrackPower == FxTrackPowerRefsAndStack,
|
||
|
pPkgPnp->GetDevice());
|
||
|
}
|
||
|
|
||
|
SendD0Notification();
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
FxPowerIdleMachine::EnableTimer(
|
||
|
VOID
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
Public function that the power policy state machine uses to put this state
|
||
|
machine in to an enabled state and potentially start the idle timer.
|
||
|
|
||
|
Arguments:
|
||
|
None
|
||
|
|
||
|
Return Value:
|
||
|
None
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
KIRQL irql;
|
||
|
|
||
|
m_Lock.Acquire(&irql);
|
||
|
ProcessEventLocked(PowerIdleEventEnabled);
|
||
|
m_Lock.Release(irql);
|
||
|
}
|
||
|
|
||
|
BOOLEAN
|
||
|
FxPowerIdleMachine::DisableTimer(
|
||
|
VOID
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
Public function which the power policy state machine uses to put this state
|
||
|
machine into a disabled state. If necessary, the state machine will attempt
|
||
|
to cancel the idle timer.
|
||
|
|
||
|
Arguments:
|
||
|
None
|
||
|
|
||
|
Return Value:
|
||
|
TRUE if the idle timer was cancelled and the caller may proceed directly to
|
||
|
its new state
|
||
|
|
||
|
FALSE if the idle timer was not cancelled and the caller must wait for the
|
||
|
io timeout event to be posted before proceeding.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
KIRQL irql;
|
||
|
BOOLEAN disabledImmediately;
|
||
|
|
||
|
m_Lock.Acquire(&irql);
|
||
|
|
||
|
ProcessEventLocked(PowerIdleEventDisabled);
|
||
|
|
||
|
//
|
||
|
// If FxPowerIdleTimerStarted is still set after disabling the state machine,
|
||
|
// then we could not cancel the timer and we must wait for the timer expired
|
||
|
// event to be posted to this state machine. This state machine will then
|
||
|
// post a PwrPolIoPresent event to power policy.
|
||
|
//
|
||
|
if (m_Flags & FxPowerIdleTimerStarted) {
|
||
|
disabledImmediately = FALSE;
|
||
|
}
|
||
|
else {
|
||
|
disabledImmediately = TRUE;
|
||
|
}
|
||
|
|
||
|
m_Lock.Release(irql);
|
||
|
|
||
|
return disabledImmediately;
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
FxPowerIdleMachine::Start(
|
||
|
VOID
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
Public function that the power policy state machine uses to put this state
|
||
|
machine into a started state so that the caller can call PowerReference
|
||
|
successfully.
|
||
|
|
||
|
Arguments:
|
||
|
None
|
||
|
|
||
|
Return Value:
|
||
|
None
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
KIRQL irql;
|
||
|
m_Lock.Acquire(&irql);
|
||
|
ProcessEventLocked(PowerIdleEventStart);
|
||
|
m_Lock.Release(irql);
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
FxPowerIdleMachine::Stop(
|
||
|
VOID
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
Public function which the power policy state machine uses to put this state
|
||
|
machine into a state where PowerReference will no longer work.
|
||
|
|
||
|
Arguments:
|
||
|
None
|
||
|
|
||
|
Return Value:
|
||
|
None
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
KIRQL irql;
|
||
|
|
||
|
m_Lock.Acquire(&irql);
|
||
|
ProcessEventLocked(PowerIdleEventStop);
|
||
|
m_Lock.Release(irql);
|
||
|
}
|
||
|
|
||
|
_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;
|
||
|
}
|
||
|
|
||
|
_Must_inspect_result_
|
||
|
NTSTATUS
|
||
|
FxPowerIdleMachine::IoIncrement(
|
||
|
VOID
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
Public function for any component to increment the io count. The increment
|
||
|
may cause the state machine to move out of the enabled state depending on
|
||
|
the current state.
|
||
|
|
||
|
Arguments:
|
||
|
None
|
||
|
|
||
|
Return Value:
|
||
|
STATUS_PENDING if the state machine is transition from idle to non idle
|
||
|
|
||
|
STATUS_SUCCESS otherwise
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
return IoIncrementWithFlags(FxPowerReferenceDefault);
|
||
|
}
|
||
|
|
||
|
_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;
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
FxPowerIdleMachine::IoDecrement(
|
||
|
__in_opt PVOID Tag,
|
||
|
__in_opt LONG Line,
|
||
|
__in_opt PSTR File
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
Public function which allows the caller decrement the pending io count on
|
||
|
this state machine. If the count goes to zero and idle is enabled, then
|
||
|
the timer is started.
|
||
|
|
||
|
Arguments:
|
||
|
None
|
||
|
|
||
|
Return Value:
|
||
|
None
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
KIRQL irql;
|
||
|
FxPkgPnp* pPkgPnp;
|
||
|
PFX_DRIVER_GLOBALS pFxDriverGlobals;
|
||
|
ULONG count;
|
||
|
|
||
|
pPkgPnp = GetPnpPkg(this);
|
||
|
pFxDriverGlobals = pPkgPnp->GetDriverGlobals();
|
||
|
|
||
|
m_Lock.Acquire(&irql);
|
||
|
|
||
|
if (m_IoCount == 0) {
|
||
|
//
|
||
|
// We can get here for the following reasons:
|
||
|
// 1. Driver called WdfDevicveStopIdle/WdfDeviceResumeIdle in a mismatched
|
||
|
// manner. This is a driver bug.
|
||
|
// 2. Framework did power deref without a corresponding power ref.
|
||
|
// This would be a framework bug.
|
||
|
//
|
||
|
// We will break into debugger if verifier is turned on. This will allow
|
||
|
// developers to catch this problem during develeopment.
|
||
|
// We limit this break to version 1.11+ because otherwise older drivers
|
||
|
// may hit this, and if they cannot be fixed for some reason, then
|
||
|
// verifier would need to be turned off to avoid the break which is not
|
||
|
// desirable.
|
||
|
//
|
||
|
DoTraceLevelMessage(
|
||
|
pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
|
||
|
"WDFDEVICE 0x%p !devobj 0x%p The device is being power-dereferenced"
|
||
|
" without a matching power-reference. This could occur if driver"
|
||
|
" incorrectly calls WdfDeviceResumeIdle without a matching call to"
|
||
|
" WdfDeviceStopIdle.",
|
||
|
pPkgPnp->GetDevice()->GetHandle(),
|
||
|
pPkgPnp->GetDevice()->GetDeviceObject());
|
||
|
|
||
|
if (pFxDriverGlobals->IsVerificationEnabled(1, 11, OkForDownLevel)) {
|
||
|
FxVerifierDbgBreakPoint(pFxDriverGlobals);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ASSERT(m_IoCount > 0);
|
||
|
count = --m_IoCount;
|
||
|
ProcessEventLocked(PowerIdleEventIoDecrement);
|
||
|
m_Lock.Release(irql);
|
||
|
|
||
|
if (m_TagTracker != NULL) {
|
||
|
m_TagTracker->UpdateTagHistory(Tag, Line, File, TagRelease, count);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
BOOLEAN
|
||
|
FxPowerIdleMachine::QueryReturnToIdle(
|
||
|
VOID
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
Public function which allows the caller to query the current io count on
|
||
|
this state machine. If the count non zero, the device will be brought back
|
||
|
to D0. If zero, the device will remain in Dx.
|
||
|
|
||
|
Arguments:
|
||
|
None
|
||
|
|
||
|
Return Value:
|
||
|
if TRUE is returned, there is an outstanding IO.
|
||
|
if FALSE is returned, the device is idle.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
KIRQL irql;
|
||
|
BOOLEAN result;
|
||
|
|
||
|
//
|
||
|
// To return to idle, the following must be true
|
||
|
// 1) the device must be in Dx (FxPowerIdleInDx)
|
||
|
// 2) the timer must *NOT* be running
|
||
|
// 3) an IO count of zero
|
||
|
//
|
||
|
m_Lock.Acquire(&irql);
|
||
|
|
||
|
// 1
|
||
|
if ((m_Flags & FxPowerIdleInDx) == FxPowerIdleInDx &&
|
||
|
// 2 3
|
||
|
(m_Flags & FxPowerIdleTimerStarted) == 0 && m_IoCount == 0x0) {
|
||
|
result = TRUE;
|
||
|
}
|
||
|
else {
|
||
|
result = FALSE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If the caller is querying about returning to idle, then they have
|
||
|
// processed the io present event that we previously sent. We must clear
|
||
|
// the flag, otherwise the following can occur
|
||
|
// 1) new io arrives, send the io present message
|
||
|
// 2) return to idle (io count goes to zero)
|
||
|
// 3) get queried to return to idle, return TRUE
|
||
|
// 4) new io arrives, we do not send the new io present message because the
|
||
|
// FxPowerIdleIoPresentSent flag is set.
|
||
|
//
|
||
|
m_Flags &= ~FxPowerIdleIoPresentSent;
|
||
|
|
||
|
m_Lock.Release(irql);
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
FxPowerIdleMachine::SendD0Notification(
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
m_D0NotificationEvent.Set();
|
||
|
|
||
|
if (m_Flags & FxPowerIdleSendPnpPowerUpEvent) {
|
||
|
|
||
|
m_Flags &= ~FxPowerIdleSendPnpPowerUpEvent;
|
||
|
|
||
|
//
|
||
|
// Send an event to the Pnp state machine indicating that the device is
|
||
|
// now in D0.
|
||
|
//
|
||
|
#if (FX_CORE_MODE==FX_CORE_KERNEL_MODE)
|
||
|
GetPnpPkg(this)->PnpProcessEvent(PnpEventDeviceInD0);
|
||
|
#else
|
||
|
GetPnpPkg(this)->PnpProcessEvent(
|
||
|
PnpEventDeviceInD0,
|
||
|
TRUE // ProcessEventOnDifferentThread
|
||
|
);
|
||
|
#endif
|
||
|
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
FxPowerIdleMachine::ProcessPowerEvent(
|
||
|
__in FxPowerIdleEvents Event
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
Post a power related event to the state machine.
|
||
|
|
||
|
Arguments:
|
||
|
Event - the event to post
|
||
|
|
||
|
Return Value:
|
||
|
None
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
KIRQL irql;
|
||
|
|
||
|
//
|
||
|
// All other event types have specialized public functions
|
||
|
//
|
||
|
ASSERT(Event == PowerIdleEventPowerUpComplete ||
|
||
|
Event == PowerIdleEventPowerUpFailed ||
|
||
|
Event == PowerIdleEventPowerDown ||
|
||
|
Event == PowerIdleEventPowerDownFailed);
|
||
|
|
||
|
m_Lock.Acquire(&irql);
|
||
|
ProcessEventLocked(Event);
|
||
|
m_Lock.Release(irql);
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
}
|
||
|
}
|