reactos/sdk/lib/drivers/wdf/shared/inc/private/common/fxpowerpolicystatemachine.hpp

812 lines
24 KiB
C++
Raw Normal View History

//
// Copyright (C) Microsoft. All rights reserved.
//
#ifndef _FXPOWERPOLICYSTATEMACHINE_H_
#define _FXPOWERPOLICYSTATEMACHINE_H_
#include "FxPowerIdleStateMachine.hpp"
#include "FxPoxInterface.hpp"
// @@SMVERIFY_SPLIT_BEGIN
//
// Treat these values as a bit field when comparing for known dropped events in
// the current state and treat them as values when they known transition events
// from state to the next.
//
enum FxPowerPolicyEvent {
PwrPolInvalid = 0x00000000,
PwrPolStart = 0x00000001,
PwrPolStop = 0x00000002,
PwrPolSx = 0x00000004,
PwrPolS0 = 0x00000008,
PwrPolPowerDown = 0x00000010,
PwrPolPowerUp = 0x00000020,
PwrPolPowerDownIoStopped = 0x00000040,
PwrPolPowerUpHwStarted = 0x00000080,
PwrPolWakeArrived = 0x00000100,
PwrPolWakeSuccess = 0x00000200,
PwrPolWakeFailed = 0x00000400,
PwrPolIoPresent = 0x00000800,
PwrPolPowerTimeoutExpired = 0x00001000,
PwrPolS0IdlePolicyChanged = 0x00002000,
PwrPolSurpriseRemove = 0x00004000,
PwrPolUsbSelectiveSuspendCallback = 0x00008000,
PwrPolUsbSelectiveSuspendCompleted = 0x00010000,
PwrPolPowerDownFailed = 0x00020000,
PwrPolPowerUpFailed = 0x00040000,
PwrPolImplicitPowerDown = 0x00080000,
PwrPolImplicitPowerDownFailed = 0x00100000,
PwrPolPowerUpNotSeen = 0x00200000,
PwrPolDevicePowerNotRequired = 0x00400000,
PwrPolDevicePowerRequired = 0x00800000,
PwrPolRemove = 0x01000000,
PwrPolWakeInterruptFired = 0x02000000,
//
// Not a real event, just a value that indicates all of the events which
// goto the head of the queue and are always processed, even if the state is
// locked. This applies to the power policy owner state machine.
//
PwrPolPriorityEventsMask = PwrPolPowerUp |
PwrPolPowerDown |
PwrPolPowerUpFailed |
PwrPolPowerDownFailed |
PwrPolPowerDownIoStopped |
PwrPolPowerUpHwStarted |
PwrPolImplicitPowerDown |
PwrPolImplicitPowerDownFailed |
PwrPolWakeArrived |
PwrPolWakeSuccess |
PwrPolWakeFailed |
PwrPolPowerUpNotSeen |
PwrPolUsbSelectiveSuspendCompleted |
PwrPolWakeInterruptFired,
//
// Not a real event, just a value that indicates all of the events which
// goto the head of the queue and are always processed, even if the state is
// locked. This applies to the not power policy owner state machine.
//
PwrPolNotOwnerPriorityEventsMask = PwrPolPowerUp |
PwrPolPowerUpFailed |
PwrPolPowerDown |
PwrPolPowerDownFailed,
//
// Not a real event, just a value that indicate all of the events which
// should not be in the queue, if a similar event is already enqueued.
//
PowerPolSingularEventMask = PwrPolS0IdlePolicyChanged |
//
// A device could have multiple wake interrupts that could each fire
// this event.
//
PwrPolWakeInterruptFired,
PwrPolNull = 0xFFFFFFFF,
};
//
// Bit packed ULONG.
//
union FxPwrPolStateInfo {
struct {
//
// Indicates whether the state is open to all events
//
ULONG QueueOpen : 1;
//
// Bit of events we know we can drop in this state
//
ULONG KnownDroppedEvents : 31;
} Bits;
struct {
//
// Maps to the same bit location as QueueOpen. Since we start
// KnownDroppedEvents at the next bit, start our bits by at the next
// bit as well.
//
ULONG Reserved : 1;
//
// These are defined so that we can easily tell in the debugger what
// each set bit in KnownDroppedEvents maps to in the FxPowerPolicyEvent
// enum
//
ULONG PwrPolStartKnown : 1;
ULONG PwrPolStopKnown : 1;
ULONG PwrPolSxKnown : 1;
ULONG PwrPolS0Known : 1;
ULONG PwrPolPowerDownKnown : 1;
ULONG PwrPolPowerUpKnown : 1;
ULONG PwrPolPowerDownIoStoppedKnown : 1;
ULONG PwrPolPowerUpHwStartedKnown : 1;
ULONG PwrPolWakeArrivedKnown : 1;
ULONG PwrPolWakeSuccessKnown : 1;
ULONG PwrPolWakeFailedKnown : 1;
ULONG PwrPolIoPresentKnown : 1;
ULONG PwrPolPowerTimeoutExpiredKnown : 1;
ULONG PwrPolS0IdlePolicyChangedKnown : 1;
ULONG PwrPolSurpriseRemoveKnown : 1;
ULONG PwrPolUsbSelectiveSuspendCallbackKnown : 1;
ULONG PwrPolUsbSelectiveSuspendCompletedKnown : 1;
ULONG PwrPolPowerDownFailedKnown : 1;
ULONG PwrPolPowerUpFailedKnown : 1;
} BitsByName;
};
struct POWER_POLICY_EVENT_TARGET_STATE {
FxPowerPolicyEvent PowerPolicyEvent;
WDF_DEVICE_POWER_POLICY_STATE TargetState;
EVENT_TRAP_FIELD
};
typedef const POWER_POLICY_EVENT_TARGET_STATE* CPPOWER_POLICY_EVENT_TARGET_STATE;
typedef
WDF_DEVICE_POWER_POLICY_STATE
(*PFN_POWER_POLICY_STATE_ENTRY_FUNCTION)(
FxPkgPnp* This
);
typedef struct POWER_POLICY_STATE_TABLE {
//
// Framework internal function to handle the transition into this state
//
PFN_POWER_POLICY_STATE_ENTRY_FUNCTION StateFunc;
//
// First state transition out of this state
//
POWER_POLICY_EVENT_TARGET_STATE FirstTargetState;
//
// Other state transitions out of this state if FirstTargetState is not
// matched. This is an array where we expect the final element to be
// { PwrPolNull, WdfDevStatePwrPolNull }
//
CPPOWER_POLICY_EVENT_TARGET_STATE OtherTargetStates;
//
// Whether we allow transitions out of this state that are not D state
// related events, ie if this is a green dot state, TRUE, if this is a red
// dot state, FALSE. D state events (PwrPolPowerUp, PwrPolPowerDown)
// are never affected by the queue state and are always processed.
//
FxPwrPolStateInfo StateInfo;
} *PPOWER_POLICY_STATE_TABLE;
typedef const POWER_POLICY_STATE_TABLE* CPPOWER_POLICY_STATE_TABLE;
typedef
WDF_DEVICE_POWER_POLICY_STATE
(*PFN_NOT_POWER_POLICY_OWNER_STATE_ENTRY_FUNCTION)(
FxPkgPnp* This
);
typedef struct NOT_POWER_POLICY_OWNER_STATE_TABLE {
//
// The current power policy state that this entry applies to
//
WDF_DEVICE_POWER_POLICY_STATE CurrentTargetState;
//
// Framework internal function to handle the transition into this state
//
PFN_NOT_POWER_POLICY_OWNER_STATE_ENTRY_FUNCTION StateFunc;
//
// Only state transition out of this state
//
CPPOWER_POLICY_EVENT_TARGET_STATE TargetStates;
UCHAR TargetStatesCount;
BOOLEAN QueueOpen;
} *PNOT_POWER_POLICY_OWNER_STATE_TABLE;
typedef const NOT_POWER_POLICY_OWNER_STATE_TABLE* CPNOT_POWER_POLICY_OWNER_STATE_TABLE;
#if FX_STATE_MACHINE_VERIFY
#define MAX_PWR_POL_STATE_ENTRY_FN_RETURN_STATES (5)
struct PWR_POL_STATE_ENTRY_FUNCTION_TARGET_STATE {
//
// Return value from state entry function
//
WDF_DEVICE_POWER_POLICY_STATE State;
//
// type of device the returning state applies to
//
FxStateMachineDeviceType DeviceType;
//
// Info about the state transition
//
PSTR Comment;
};
struct PWR_POL_STATE_ENTRY_FN_RETURN_STATE_TABLE {
//
// array of state transitions caused by state entry function
//
PWR_POL_STATE_ENTRY_FUNCTION_TARGET_STATE TargetStates[MAX_PWR_POL_STATE_ENTRY_FN_RETURN_STATES];
};
typedef const PWR_POL_STATE_ENTRY_FN_RETURN_STATE_TABLE* CPPWR_POL_STATE_ENTRY_FN_RETURN_STATE_TABLE;
#endif // FX_STATE_MACHINE_VERIFY
// @@SMVERIFY_SPLIT_END
enum FxPowerPolicyConstants {
FxPowerPolicyNoTimeout = 0,
FxPowerPolicyDefaultTimeout = 5000, // Timeout in milliseconds
};
enum CancelIrpCompletionOwnership {
CancelOwnershipUnclaimed = 0,
CancelOwnershipClaimed = 1,
};
enum FxPowerPolicySxWakeSettingsFlags {
FxPowerPolicySxWakeDeviceEnabledFlag = 0x1,
FxPowerPolicySxWakeChildrenArmedFlag = 0x2,
};
struct PolicySettings {
PolicySettings()
{
WmiInstance = NULL;
DxState = PowerDeviceD3;
Enabled = Overridable = Set = Dirty = FALSE;
}
~PolicySettings();
//
// Dx state to put the device in when the policy is applied
//
DEVICE_POWER_STATE DxState;
FxWmiInstanceInternal* WmiInstance;
BOOLEAN Enabled;
BOOLEAN Overridable;
BOOLEAN Set;
BOOLEAN Dirty;
};
typedef struct _POX_SETTINGS {
PFN_WDFDEVICE_WDM_POST_PO_FX_REGISTER_DEVICE
EvtDeviceWdmPostPoFxRegisterDevice;
PFN_WDFDEVICE_WDM_PRE_PO_FX_UNREGISTER_DEVICE
EvtDeviceWdmPrePoFxUnregisterDevice;
PPO_FX_COMPONENT Component;
PPO_FX_COMPONENT_ACTIVE_CONDITION_CALLBACK ComponentActiveConditionCallback;
PPO_FX_COMPONENT_IDLE_CONDITION_CALLBACK ComponentIdleConditionCallback;
PPO_FX_COMPONENT_IDLE_STATE_CALLBACK ComponentIdleStateCallback;
PPO_FX_POWER_CONTROL_CALLBACK PowerControlCallback;
PVOID PoFxDeviceContext;
} POX_SETTINGS, *PPOX_SETTINGS;
class IdleTimeoutManagement {
private:
//
// This member is used to control whether or not the idle timeout is
// determined by the power manager when running on Windows 8 and above.
// The value of this member is some combination of the flags defined below.
//
LONG volatile m_IdleTimeoutStatus;
//
// Flags for the m_IdleTimeoutStatus member
//
// IdleTimeoutStatusFrozen - This flag implies that the decision on
// whether the power manager determines the idle timeout is "frozen"
// and can no longer be changed. The decision is frozen during start
// IRP completion processing, just before WDF registers with the
// power manager.
//
// IdleTimeoutSystemManaged - This flag implies that the power manager
// determines the idle timeout on Windows 8 and above. If this flag
// is not set, the idle timeout specified by the client driver is
// used.
//
// IdleTimeoutPoxSettingsSpecified - This flag implies that the client
// driver has already specified the settings that need to be used
// when registering with the power framework. This flag is used to
// track that the settings are not specified more than once.
//
enum IdleTimeoutStatusFlag {
IdleTimeoutStatusFrozen = 0x00000001,
IdleTimeoutSystemManaged = 0x00000002,
IdleTimeoutPoxSettingsSpecified = 0x00000004,
};
//
// Result returned by the UpdateIdleTimeoutStatus() method
//
enum IdleTimeoutStatusUpdateResult {
//
// Flags were sucessfully updated
//
IdleTimeoutStatusFlagsUpdated,
//
// The flag we were trying to set was already set
//
IdleTimeoutStatusFlagAlreadySet,
//
// It is too late to set the flag. The flags have already been frozen.
// Flags are frozen the first time a device is started.
//
IdleTimeoutStatusFlagsAlreadyFrozen,
//
// Flags are being set by multiple threads in parallel. This is not
// supported.
//
IdleTimeoutStatusFlagsUnexpected
};
//
// This member contains the client driver's settings that will be used when
// we register with the power manager on Windows 8 and above.
//
PPOX_SETTINGS m_PoxSettings;
private:
IdleTimeoutStatusUpdateResult
UpdateIdleTimeoutStatus(
__in IdleTimeoutStatusFlag Flag
);
CfxDevice *
GetDevice(
VOID
);
public:
IdleTimeoutManagement(
VOID
) : m_IdleTimeoutStatus(0),
m_PoxSettings(NULL)
{
}
~IdleTimeoutManagement(
VOID
)
{
BYTE * buffer = NULL;
ULONG poxSettingsOffset;
if (NULL != m_PoxSettings) {
buffer = (BYTE*) m_PoxSettings;
//
// In the function FxPkgPnp::AssignPowerFrameworkSettings, we had
// allocated a buffer which we need to free now. Note that
// m_PoxSettings does not necessarily point to the beginning of the
// buffer. It points to the POX_SETTINGS structure in the buffer,
// which may or may not be in the beginning. If it is not in the
// beginning, figure out where the beginning of the buffer is.
//
if (m_PoxSettings->Component != NULL) {
//
// The computation below won't overflow because we already
// performed this computation successfully using safeint
// functions in FxPkgPnp::AssignPowerFrameworkSettings.
//
poxSettingsOffset =
(sizeof(*(m_PoxSettings->Component->IdleStates)) *
(m_PoxSettings->Component->IdleStateCount)) +
(sizeof(*(m_PoxSettings->Component)));
}
else {
poxSettingsOffset = 0;
}
//
// Move to the beginning of the buffer
//
buffer = buffer - poxSettingsOffset;
//
// Free the buffer
//
MxMemory::MxFreePool(buffer);
}
}
static
BOOLEAN
_SystemManagedIdleTimeoutAvailable(
VOID
);
NTSTATUS
UseSystemManagedIdleTimeout(
__in PFX_DRIVER_GLOBALS DriverGlobals
);
VOID
FreezeIdleTimeoutManagementStatus(
__in PFX_DRIVER_GLOBALS DriverGlobals
);
BOOLEAN
UsingSystemManagedIdleTimeout(
VOID
);
NTSTATUS
CommitPowerFrameworkSettings(
__in PFX_DRIVER_GLOBALS DriverGlobals,
__in PPOX_SETTINGS PoxSettings
);
BOOLEAN
DriverSpecifiedPowerFrameworkSettings(
VOID
);
PPOX_SETTINGS
GetPowerFrameworkSettings(
VOID
)
{
return m_PoxSettings;
}
};
struct IdlePolicySettings : PolicySettings {
IdlePolicySettings(
VOID
) : PolicySettings()
{
WakeFromS0Capable = FALSE;
UsbSSCapable = FALSE;
PowerUpIdleDeviceOnSystemWake = FALSE;
UsbSSCapabilityKnown = FALSE;
}
//
// TRUE if the device capable of waking from S0
//
BOOLEAN WakeFromS0Capable;
//
// This member is meaningful only if the WakeFromS0Capable member (above) is
// TRUE. The WakeFromS0Capable member indicates whether or not wake-from-S0
// is currently enabled. If wake-from-S0 is currently enabled, the
// UsbSSCapable member indicates whether the wake-from-S0 support is generic
// or USB SS specific. If wake-from-S0 is not enabled, the UsbSSCapable
// member is ignored.
//
BOOLEAN UsbSSCapable;
//
// TRUE if we know whether the device supports generic wake or USB SS wake.
// This value is initialized to FALSE and remains FALSE until the first time
// that the driver specifies S0-idle settings with an idle capability value
// of IdleCanWakeFromS0 or IdleUsbSelectiveSuspend. When the driver
// specifies one of these idle capabilities, this value is set to TRUE and
// remains TRUE for the lifetime of the device.
//
BOOLEAN UsbSSCapabilityKnown;
//
// TRUE if idle enabled device should be powered up even when idle,
// when resuming from Sx
//
BOOLEAN PowerUpIdleDeviceOnSystemWake;
//
// Member to manage interactions with the power manager for S0-idle support
// on Win8 and above
//
IdleTimeoutManagement m_TimeoutMgmt;
};
struct WakePolicySettings : PolicySettings {
WakePolicySettings(
VOID
) : PolicySettings()
{
ArmForWakeIfChildrenAreArmedForWake = FALSE;
IndicateChildWakeOnParentWake = FALSE;
}
//
// TRUE if the device should arm for wake when one or more children are
// armed for wake.
//
BOOLEAN ArmForWakeIfChildrenAreArmedForWake;
//
// TRUE if the device should propagate the wake status to its children.
//
BOOLEAN IndicateChildWakeOnParentWake;
};
struct FxPowerPolicyOwnerSettings : public FxStump {
friend FxPowerPolicyMachine;
public:
FxPowerPolicyOwnerSettings(
__in FxPkgPnp* PkgPnp
);
~FxPowerPolicyOwnerSettings(
VOID
);
_Must_inspect_result_
NTSTATUS
Init(
VOID
);
VOID
CleanupPowerCallback(
VOID
);
VOID
IncrementChildrenArmedForWakeCount(
VOID
)
{
InterlockedIncrement(&m_ChildrenArmedCount);
}
VOID
DecrementChildrenArmedForWakeCount(
VOID
)
{
InterlockedDecrement(&m_ChildrenArmedCount);
}
protected:
static
MdCallbackFunctionType
_PowerStateCallback;
public:
FxPowerIdleMachine m_PowerIdleMachine;
FxPoxInterface m_PoxInterface;
FxPowerDeviceArmWakeFromS0 m_DeviceArmWakeFromS0;
FxPowerDeviceArmWakeFromSx m_DeviceArmWakeFromSx;
FxPowerDeviceDisarmWakeFromS0 m_DeviceDisarmWakeFromS0;
FxPowerDeviceDisarmWakeFromSx m_DeviceDisarmWakeFromSx;
FxPowerDeviceWakeFromS0Triggered m_DeviceWakeFromS0Triggered;
FxPowerDeviceWakeFromSxTriggered m_DeviceWakeFromSxTriggered;
FxUsbIdleInfo* m_UsbIdle;
FxPkgPnp* m_PkgPnp;
WakePolicySettings m_WakeSettings;
IdlePolicySettings m_IdleSettings;
//
// Nibble packed structure. Each D state is encoded 4 bits. The S state is
// used as the "index" within the ULONG. PowerSystemUnspecified is the
// first 4 bits of the first byte, etc. etc. ...
//
ULONG m_SystemToDeviceStateMap;
//
// The number of children who are in the D0 state. If this count is > 0,
// then this parent cannot idle out while in S0. Note that each child also
// has an explicit call to PowerReference against this device which is used
// to control the idle timer for this device.
//
ULONG m_ChildrenPoweredOnCount;
//
// The number of children who are currently armed for wake. This count
// can be used by the the wake owner to determine whether wake should be
// enabled or not for a parent stack if arming for wake depends on
// children being armed for wake.
//
LONG m_ChildrenArmedCount;
//
// The status of the last wait wake IRP to complete in the stack
//
NTSTATUS m_WaitWakeStatus;
//
// Dx state to put the device into when an Sx irp arrives and the device is
// not armed for wake from Sx. DEVICE_POWER_STATE values are used.
//
BYTE m_IdealDxStateForSx;
//
// Track power requests to assert if someone other than this driver sent it
// and to determine if this driver has received the requested irp (to catch
// someone above completing irp w/o sending to this driver)
//
BOOLEAN m_RequestedPowerUpIrp;
BOOLEAN m_RequestedPowerDownIrp;
BOOLEAN m_RequestedWaitWakeIrp;
//
// Tracks wake event being dropped
//
BOOLEAN m_WakeCompletionEventDropped;
BOOLEAN m_PowerFailed;
//
// Indicates whether we can cause paging I/O by writing to the registry
//
BOOLEAN m_CanSaveState;
//
// Guard to stop children from powering up while the parent is in Dx or
// about to transition into Dx.
//
BOOLEAN m_ChildrenCanPowerUp;
//
// TRUE if our device caused the machine to wake up. Access to this value
// is not synchronized between the parent and PDO. The parent sets it to
// TRUE upon successful completion of the WW irp and cleared after
// EvtDeviceDisarmWakeFromSx. If a PDO's WW IRP is completed within this
// window, the PDO's WW IRP will have PoSetSystemWake called on it. It is
// acceptable if the PDO's WW IRP completion races with the clearing of the
// value and is not set as a source of wake.
//
BOOLEAN m_SystemWakeSource;
protected:
PCALLBACK_OBJECT m_PowerCallbackObject;
PVOID m_PowerCallbackRegistration;
LONG m_WaitWakeCancelCompletionOwnership;
};
//
// This type of union is done so that we can
// 1) shrink the array element to the smallest size possible
// 2) keep types within the structure so we can dump it in the debugger
//
union FxPowerPolicyMachineStateHistory {
struct {
WDF_DEVICE_POWER_POLICY_STATE State1 : 16;
WDF_DEVICE_POWER_POLICY_STATE State2 : 16;
WDF_DEVICE_POWER_POLICY_STATE State3 : 16;
WDF_DEVICE_POWER_POLICY_STATE State4 : 16;
WDF_DEVICE_POWER_POLICY_STATE State5 : 16;
WDF_DEVICE_POWER_POLICY_STATE State6 : 16;
WDF_DEVICE_POWER_POLICY_STATE State7 : 16;
WDF_DEVICE_POWER_POLICY_STATE State8 : 16;
} S;
USHORT History[FxPowerPolicyEventQueueDepth];
};
struct FxPowerPolicyMachine : public FxThreadedEventQueue {
FxPowerPolicyMachine(
VOID
);
~FxPowerPolicyMachine(
VOID
);
VOID
UsbSSCallbackProcessingComplete(
VOID
);
_Must_inspect_result_
NTSTATUS
InitUsbSS(
VOID
);
VOID
SetWaitWakeUnclaimed(
VOID
)
{
m_Owner->m_WaitWakeCancelCompletionOwnership = CancelOwnershipUnclaimed;
}
BOOLEAN
CanCompleteWaitWakeIrp(
VOID
)
{
//
// We have 2 potential call sites racing on trying to complete the wait
// wake irp. The first is the cancelling call site. The other is the
// irp's completion routine. What we want is for the *2nd* (and last)
// call site to actually complete the irp. This is why we check to see
// if the result of the exchange is that the ownership is already claimed
// (and not unclaimed as one might first be led to think).
//
if (InterlockedExchange(&m_Owner->m_WaitWakeCancelCompletionOwnership,
CancelOwnershipClaimed) == CancelOwnershipClaimed) {
return TRUE;
}
else {
return FALSE;
}
}
public:
FxPowerPolicyEvent m_Queue[FxPowerPolicyEventQueueDepth];
FxPowerPolicyMachineStateHistory m_States;
FxPowerPolicyOwnerSettings* m_Owner;
union {
ULONG m_SingularEventsPresent;
union {
//
// These are defined so that we can easily tell in the debugger what
// each set bit in m_SingularEventsPresent maps to in the
// FxPowerPolicyEvent enum.
//
ULONG PwrPolStartKnown : 1;
ULONG PwrPolStopKnown : 1;
ULONG PwrPolSxKnown : 1;
ULONG PwrPolS0Known : 1;
ULONG PwrPolPowerDownKnown : 1;
ULONG PwrPolPowerUpKnown : 1;
ULONG PwrPolPowerDownIoStoppedKnown : 1;
ULONG PwrPolPowerUpHwStartedKnown : 1;
ULONG PwrPolWakeArrivedKnown : 1;
ULONG PwrPolWakeSuccessKnown : 1;
ULONG PwrPolWakeFailedKnown : 1;
ULONG PwrPolIoPresentKnown : 1;
ULONG PwrPolPowerTimeoutExpiredKnown : 1;
ULONG PwrPolS0IdlePolicyChangedKnown : 1;
ULONG PwrPolSurpriseRemoveKnown : 1;
ULONG PwrPolUsbSelectiveSuspendCallbackKnown : 1;
ULONG PwrPolUsbSelectiveSuspendCompletedKnown : 1;
ULONG PwrPolPowerDownFailedKnown : 1;
ULONG PwrPolPowerUpFailedKnown : 1;
} m_SingularEventsPresentByName;
};
};
#endif // _FXPOWERPOLICYSTATEMACHINE_H_