mirror of
https://github.com/reactos/reactos.git
synced 2025-03-10 18:24:02 +00:00
5398 lines
139 KiB
C++
5398 lines
139 KiB
C++
/*++
|
|
Copyright (c) Microsoft. All rights reserved.
|
|
|
|
Module Name:
|
|
|
|
PowerStateMachine.cpp
|
|
|
|
Abstract:
|
|
|
|
This module implements the Power state machine for the driver framework.
|
|
This code was split out from FxPkgPnp.cpp.
|
|
|
|
Author:
|
|
|
|
|
|
|
|
|
|
Environment:
|
|
|
|
Both kernel and user mode
|
|
|
|
Revision History:
|
|
|
|
|
|
|
|
--*/
|
|
|
|
#include "pnppriv.hpp"
|
|
|
|
extern "C" {
|
|
#if defined(EVENT_TRACING)
|
|
#include "PowerStateMachine.tmh"
|
|
#endif
|
|
}
|
|
|
|
#if FX_STATE_MACHINE_VERIFY
|
|
#define VALIDATE_POWER_STATE(_CurrentState, _NewState) \
|
|
ValidatePowerStateEntryFunctionReturnValue((_CurrentState), (_NewState))
|
|
#else
|
|
#define VALIDATE_POWER_STATE(_CurrentState, _NewState) (0)
|
|
#endif //FX_STATE_MACHINE_VERIFY
|
|
|
|
|
|
// @@SMVERIFY_SPLIT_BEGIN
|
|
|
|
//
|
|
// The Power State Machine
|
|
//
|
|
// This state machine responds to power IRPs that relate
|
|
// to devices. It is a subordinate, responding either to IRPs
|
|
// that are sent by other drivers, or by IRPs that are
|
|
// sent by the Power Policy State Machine.
|
|
//
|
|
// It responds to:
|
|
//
|
|
// IRP_MN_SET_POWER -- device power IRPs only
|
|
// IRP_MN_WAIT_WAKE
|
|
// IRP_MN_WAIT_WAKE Complete
|
|
// PowerImplicitD0
|
|
// PowerImplicitD3
|
|
// ParentMovesToD0
|
|
// PowerPolicyStop
|
|
// PowerMarkPageable
|
|
// PowerMarkNonpageable
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const POWER_EVENT_TARGET_STATE FxPkgPnp::m_PowerD0OtherStates[] =
|
|
{
|
|
{ PowerImplicitD3, WdfDevStatePowerGotoD3Stopped DEBUGGED_EVENT },
|
|
{ PowerMarkNonpageable, WdfDevStatePowerDecideD0State DEBUGGED_EVENT },
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
};
|
|
|
|
const POWER_EVENT_TARGET_STATE FxPkgPnp::m_PowerD0NPOtherStates[] =
|
|
{
|
|
{ PowerImplicitD3, WdfDevStatePowerGotoD3Stopped DEBUGGED_EVENT },
|
|
{ PowerMarkPageable, WdfDevStatePowerDecideD0State DEBUGGED_EVENT },
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
};
|
|
|
|
const POWER_EVENT_TARGET_STATE FxPkgPnp::m_PowerD0BusWakeOwnerOtherStates[] =
|
|
{
|
|
{ PowerWakeArrival, WdfDevStatePowerEnablingWakeAtBus DEBUGGED_EVENT },
|
|
{ PowerImplicitD3, WdfDevStatePowerGotoD3Stopped DEBUGGED_EVENT },
|
|
{ PowerMarkNonpageable, WdfDevStatePowerDecideD0State DEBUGGED_EVENT },
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
};
|
|
|
|
const POWER_EVENT_TARGET_STATE FxPkgPnp::m_PowerD0BusWakeOwnerNPOtherStates[] =
|
|
{
|
|
{ PowerWakeArrival, WdfDevStatePowerEnablingWakeAtBusNP DEBUGGED_EVENT },
|
|
{ PowerImplicitD3, WdfDevStatePowerGotoD3Stopped DEBUGGED_EVENT },
|
|
{ PowerMarkPageable, WdfDevStatePowerD0BusWakeOwner DEBUGGED_EVENT },
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
};
|
|
|
|
const POWER_EVENT_TARGET_STATE FxPkgPnp::m_PowerD0ArmedForWakeOtherStates[] =
|
|
{
|
|
{ PowerWakeSucceeded, WdfDevStatePowerD0DisarmingWakeAtBus DEBUGGED_EVENT },
|
|
{ PowerWakeFailed, WdfDevStatePowerD0DisarmingWakeAtBus DEBUGGED_EVENT },
|
|
{ PowerWakeCanceled, WdfDevStatePowerD0DisarmingWakeAtBus DEBUGGED_EVENT },
|
|
{ PowerMarkNonpageable, WdfDevStatePowerD0ArmedForWakeNP TRAP_ON_EVENT },
|
|
{ PowerImplicitD3, WdfDevStatePowerGotoImplicitD3DisarmWakeAtBus DEBUGGED_EVENT },
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
};
|
|
|
|
const POWER_EVENT_TARGET_STATE FxPkgPnp::m_PowerD0ArmedForWakeNPOtherStates[] =
|
|
{
|
|
{ PowerWakeSucceeded, WdfDevStatePowerD0DisarmingWakeAtBusNP DEBUGGED_EVENT },
|
|
{ PowerWakeFailed, WdfDevStatePowerD0DisarmingWakeAtBusNP TRAP_ON_EVENT },
|
|
{ PowerWakeCanceled, WdfDevStatePowerD0DisarmingWakeAtBusNP DEBUGGED_EVENT },
|
|
{ PowerMarkPageable, WdfDevStatePowerD0ArmedForWake TRAP_ON_EVENT },
|
|
{ PowerImplicitD3, WdfDevStatePowerGotoImplicitD3DisarmWakeAtBus TRAP_ON_EVENT },
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
};
|
|
|
|
const POWER_EVENT_TARGET_STATE FxPkgPnp::m_PowerDNotZeroOtherStates[] =
|
|
{
|
|
{ PowerImplicitD3, WdfDevStatePowerGotoDxStoppedDisableInterrupt DEBUGGED_EVENT },
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
};
|
|
|
|
const POWER_EVENT_TARGET_STATE FxPkgPnp::m_PowerDNotZeroNPOtherStates[] =
|
|
{
|
|
{ PowerImplicitD3, WdfDevStatePowerGotoDxStoppedDisableInterruptNP DEBUGGED_EVENT },
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
};
|
|
|
|
const POWER_EVENT_TARGET_STATE FxPkgPnp::m_DxArmedForWakeOtherStates[] =
|
|
{
|
|
{ PowerWakeSucceeded, WdfDevStatePowerWakePending DEBUGGED_EVENT },
|
|
{ PowerWakeCanceled, WdfDevStatePowerWakePending DEBUGGED_EVENT },
|
|
{ PowerWakeFailed, WdfDevStatePowerWakePending DEBUGGED_EVENT },
|
|
{ PowerImplicitD3, WdfDevStatePowerDxStoppedDisarmWake TRAP_ON_EVENT },
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
};
|
|
|
|
const POWER_EVENT_TARGET_STATE FxPkgPnp::m_DxArmedForWakeNPOtherStates[] =
|
|
{
|
|
{ PowerWakeSucceeded, WdfDevStatePowerWakePendingNP DEBUGGED_EVENT },
|
|
{ PowerWakeFailed, WdfDevStatePowerWakePendingNP DEBUGGED_EVENT },
|
|
{ PowerWakeCanceled, WdfDevStatePowerWakePendingNP DEBUGGED_EVENT },
|
|
{ PowerImplicitD3, WdfDevStatePowerDxStoppedDisarmWakeNP TRAP_ON_EVENT },
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
};
|
|
|
|
const POWER_EVENT_TARGET_STATE FxPkgPnp::m_WakePendingOtherStates[] =
|
|
{
|
|
{ PowerImplicitD0, WdfDevStatePowerCheckParentStateArmedForWake TRAP_ON_EVENT },
|
|
{ PowerImplicitD3, WdfDevStatePowerDxStoppedDisarmWake DEBUGGED_EVENT },
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
};
|
|
|
|
const POWER_EVENT_TARGET_STATE FxPkgPnp::m_WakePendingNPOtherStates[] =
|
|
{
|
|
{ PowerImplicitD0, WdfDevStatePowerCheckParentStateArmedForWakeNP TRAP_ON_EVENT },
|
|
{ PowerImplicitD3, WdfDevStatePowerDxStoppedDisarmWakeNP DEBUGGED_EVENT },
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
};
|
|
|
|
const POWER_EVENT_TARGET_STATE FxPkgPnp::m_PowerStoppedOtherStates[] =
|
|
{
|
|
{ PowerDx, WdfDevStatePowerStoppedCompleteDx DEBUGGED_EVENT },
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
};
|
|
|
|
const POWER_EVENT_TARGET_STATE FxPkgPnp::m_PowerDxStoppedOtherStates[] =
|
|
{
|
|
{ PowerD0, WdfDevStatePowerGotoStopped DEBUGGED_EVENT },
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
};
|
|
|
|
const POWER_STATE_TABLE FxPkgPnp::m_WdfPowerStates[] =
|
|
{
|
|
// WdfDevStatePowerObjectCreated
|
|
{ NULL,
|
|
{ PowerImplicitD0, WdfDevStatePowerStartingCheckDeviceType DEBUGGED_EVENT },
|
|
NULL,
|
|
{ TRUE,
|
|
PowerMarkPageable | // parent sends usage notifications before the PDO is
|
|
PowerMarkNonpageable | // started
|
|
PowerParentToD0 | // parent powered up upon enumeration of child
|
|
PowerDx | // If we are on top of a power policy owner who sends a Dx
|
|
// during start (or after AddDevice, etc)
|
|
PowerD0 // If we are on top of a power policy owner who sends a D0
|
|
// to the stack in start device, we can get this event early
|
|
},
|
|
},
|
|
|
|
// WdfDevStatePowerCheckDeviceType
|
|
{ FxPkgPnp::PowerCheckDeviceType,
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerCheckDeviceTypeNP
|
|
{ FxPkgPnp::PowerCheckDeviceTypeNP,
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerCheckParentState
|
|
{ FxPkgPnp::PowerCheckParentState,
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerCheckParentStateNP
|
|
{ FxPkgPnp::PowerCheckParentStateNP,
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerEnablingWakeAtBus
|
|
{ FxPkgPnp::PowerEnablingWakeAtBus,
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerEnablingWakeAtBusNP
|
|
{ FxPkgPnp::PowerEnablingWakeAtBusNP,
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerD0
|
|
{ FxPkgPnp::PowerDZero,
|
|
{ PowerDx, WdfDevStatePowerGotoDx DEBUGGED_EVENT },
|
|
FxPkgPnp::m_PowerD0OtherStates,
|
|
{ TRUE,
|
|
PowerImplicitD3 |
|
|
PowerD0 // A non WDF power policy owner might send a D0 irp
|
|
// while we are in D0
|
|
},
|
|
},
|
|
|
|
// WdfDevStatePowerD0NP
|
|
{ FxPkgPnp::PowerD0NP,
|
|
{ PowerDx, WdfDevStatePowerGotoDxNP DEBUGGED_EVENT },
|
|
FxPkgPnp::m_PowerD0NPOtherStates,
|
|
{ TRUE,
|
|
PowerD0 // A non WDF power policy owner might send a D0 irp
|
|
// while we are in D0
|
|
},
|
|
},
|
|
|
|
// WdfDevStatePowerD0BusWakeOwner
|
|
{ FxPkgPnp::PowerD0BusWakeOwner,
|
|
{ PowerDx, WdfDevStatePowerGotoDx DEBUGGED_EVENT },
|
|
FxPkgPnp::m_PowerD0BusWakeOwnerOtherStates,
|
|
{ TRUE,
|
|
PowerWakeSucceeded | // During surprise remove, the pnp state machine
|
|
// could complete the ww request and result in
|
|
// this event before the pwr pol machine is stopped
|
|
PowerWakeCanceled | // while powering up, the wait wake owner canceled
|
|
// the ww irp
|
|
PowerWakeFailed | // while powering up, the wait wake owner failed
|
|
// the ww irp
|
|
PowerParentToD0 |
|
|
PowerD0 // A non WDF power policy owner might send a D0 irp
|
|
// while we are in D0
|
|
},
|
|
},
|
|
|
|
// WdfDevStatePowerD0BusWakeOwnerNP
|
|
{ FxPkgPnp::PowerD0BusWakeOwnerNP,
|
|
{ PowerDx, WdfDevStatePowerGotoDxNP DEBUGGED_EVENT },
|
|
FxPkgPnp::m_PowerD0BusWakeOwnerNPOtherStates,
|
|
{ TRUE,
|
|
PowerWakeSucceeded | // During surprise remove, the pnp state machine
|
|
// could complete the ww request and result in
|
|
// this event before the pwr pol machine is stopped
|
|
PowerWakeCanceled | // while powering up, the wait wake owner canceled
|
|
// the ww irp
|
|
PowerParentToD0 |
|
|
PowerD0 // A non WDF power policy owner might send a D0 irp
|
|
// while we are in D0
|
|
},
|
|
},
|
|
|
|
// WdfDevStatePowerD0ArmedForWake
|
|
{ FxPkgPnp::PowerD0ArmedForWake,
|
|
{ PowerDx, WdfDevStatePowerGotoDxArmedForWake DEBUGGED_EVENT },
|
|
FxPkgPnp::m_PowerD0ArmedForWakeOtherStates,
|
|
{ TRUE,
|
|
PowerParentToD0 |
|
|
PowerWakeArrival | // PowerIsWakeRequestPresent() returned true in
|
|
// WdfDevStatePowerD0BusWakeOwner and raced with
|
|
// this event being processed
|
|
PowerWakeCanceled |
|
|
PowerD0 // A non WDF power policy owner might send a D0 irp
|
|
// while we are in D0
|
|
},
|
|
},
|
|
|
|
// WdfDevStatePowerD0ArmedForWakeNP
|
|
{ FxPkgPnp::PowerD0ArmedForWakeNP,
|
|
{ PowerDx, WdfDevStatePowerGotoDxArmedForWakeNP DEBUGGED_EVENT },
|
|
FxPkgPnp::m_PowerD0ArmedForWakeNPOtherStates,
|
|
{ TRUE,
|
|
PowerParentToD0 |
|
|
PowerWakeArrival | // PowerIsWakeRequestPresent() returned true in
|
|
// WdfDevStatePowerD0BusWakeOwnerNP and raced with
|
|
// this event being processed
|
|
PowerWakeCanceled |
|
|
PowerD0 // A non WDF power policy owner might send a D0 irp
|
|
// while we are in D0
|
|
},
|
|
},
|
|
|
|
// WdfDevStatePowerD0DisarmingWakeAtBus
|
|
{ FxPkgPnp::PowerD0DisarmingWakeAtBus,
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerD0DisarmingWakeAtBusNP
|
|
{ FxPkgPnp::PowerD0DisarmingWakeAtBusNP,
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerD0Starting
|
|
{ FxPkgPnp::PowerD0Starting,
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerD0StartingConnectInterrupt
|
|
{ FxPkgPnp::PowerD0StartingConnectInterrupt,
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerD0StartingDmaEnable
|
|
{ FxPkgPnp::PowerD0StartingDmaEnable,
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerD0StartingStartSelfManagedIo
|
|
{ FxPkgPnp::PowerD0StartingStartSelfManagedIo,
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerDecideD0State
|
|
{ FxPkgPnp::PowerDecideD0State,
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerGotoD3Stopped
|
|
{ FxPkgPnp::PowerGotoD3Stopped,
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerStopped
|
|
{ NULL,
|
|
{ PowerImplicitD0, WdfDevStatePowerStartingCheckDeviceType DEBUGGED_EVENT },
|
|
FxPkgPnp::m_PowerStoppedOtherStates,
|
|
{ TRUE,
|
|
PowerD0 | // as a filter above the PPO and the PPO powers on the stack
|
|
// before seeing a surprise remove or remove irp
|
|
PowerWakeFailed | // power policy owner canceled the wake request while
|
|
// we were transitioning to stop (or after the
|
|
// transition succeeded)
|
|
PowerParentToD0 },
|
|
},
|
|
|
|
// WdfDevStatePowerStartingCheckDeviceType
|
|
{ FxPkgPnp::PowerStartingCheckDeviceType,
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerStartingChild
|
|
{ FxPkgPnp::PowerStartingChild,
|
|
{ PowerParentToD0, WdfDevStatePowerD0Starting DEBUGGED_EVENT },
|
|
NULL,
|
|
{ TRUE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerDxDisablingWakeAtBus
|
|
{ FxPkgPnp::PowerDxDisablingWakeAtBus,
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerDxDisablingWakeAtBusNP
|
|
{ FxPkgPnp::PowerDxDisablingWakeAtBusNP,
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerGotoDx
|
|
{ FxPkgPnp::PowerGotoDNotZero,
|
|
{ PowerCompleteDx, WdfDevStatePowerNotifyingD0ExitToWakeInterrupts DEBUGGED_EVENT },
|
|
NULL,
|
|
{ FALSE,
|
|
PowerWakeArrival | // on a PDO which is the PPO, it will send a wake
|
|
// request in this state.
|
|
|
|
|
|
PowerParentToD0 // Parent is powering up while this device is powering
|
|
// down
|
|
},
|
|
},
|
|
|
|
// WdfDevStatePowerGotoDxNP
|
|
{ FxPkgPnp::PowerGotoDNotZeroNP,
|
|
{ PowerCompleteDx, WdfDevStatePowerNotifyingD0ExitToWakeInterruptsNP DEBUGGED_EVENT },
|
|
NULL,
|
|
{ FALSE,
|
|
PowerWakeArrival | // on a PDO which is the PPO, it will send a wake
|
|
// request in this state.
|
|
|
|
|
|
PowerParentToD0 // Parent is powering up while this device is powering
|
|
// down
|
|
},
|
|
},
|
|
|
|
// WdfDevStatePowerGotoDxIoStopped
|
|
{ FxPkgPnp::PowerGotoDNotZeroIoStopped,
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerGotoDxIoStoppedNP
|
|
{ FxPkgPnp::PowerGotoDNotZeroIoStoppedNP,
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerGotoDxNPFailed
|
|
{ FxPkgPnp::PowerGotoDxNPFailed,
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// NOTE: can't use PowerDx as a func name since it's an enum value
|
|
// WdfDevStatePowerDx
|
|
{ NULL,
|
|
{ PowerD0, WdfDevStatePowerCheckDeviceType DEBUGGED_EVENT },
|
|
FxPkgPnp::m_PowerDNotZeroOtherStates,
|
|
{ TRUE,
|
|
PowerWakeArrival | // on a PDO which is the PPO, it will send a wake
|
|
// request in this state.
|
|
|
|
|
|
|
|
PowerWakeCanceled | // on a PDO which is the PPO, it will cancel the
|
|
// the wake request in this state. Since we didn't
|
|
// handle PowerWakeArrival in WdfDevStatePowerGotoDx
|
|
// for this scenario, we must also handle the canel
|
|
// here. Even if we handle wake arrived in that
|
|
// state, the PPO (non KMDF) could send a wake
|
|
// request while in Dx and then cancel it in Dx,
|
|
// so we must still ignore this event here.
|
|
|
|
PowerWakeSucceeded | // on a PDO which is the PPO, a completion of the
|
|
// wait wake can arrive in this state. This event
|
|
// can be ignored and the pending wait wake will
|
|
// be completed.
|
|
|
|
PowerImplicitD3 |
|
|
PowerParentToD0 | // parent went to D0 first while the PDO was still
|
|
// in Dx
|
|
|
|
PowerDx // power policy sent a Dx to Dx transition
|
|
},
|
|
},
|
|
|
|
// WdfDevStatePowerDxNP
|
|
{ NULL,
|
|
{ PowerD0, WdfDevStatePowerCheckDeviceTypeNP DEBUGGED_EVENT },
|
|
FxPkgPnp::m_PowerDNotZeroNPOtherStates,
|
|
{ TRUE,
|
|
PowerWakeArrival | // on a PDO which is the PPO, it will send a wake
|
|
// request in this state.
|
|
|
|
|
|
|
|
PowerWakeCanceled | // on a PDO which is the PPO, it will cancel the
|
|
// the wake request in this state. Since we didn't
|
|
// handle PowerWakeArrival in WdfDevStatePowerGotoDx
|
|
// for this scenario, we must also handle the canel
|
|
// here. Even if we handle wake arrived in that
|
|
// state, the PPO (non KMDF) could send a wake
|
|
// request while in Dx and then cancel it in Dx,
|
|
// so we must still ignore this event here.
|
|
|
|
PowerWakeSucceeded | // on a PDO which is the PPO, a completion of the
|
|
// wait wake can arrive in this state. This event
|
|
// can be ignored and the pending wait wake will
|
|
// be completed.
|
|
|
|
PowerParentToD0 | // parent went to D0 first while the PDO was still
|
|
// in Dx
|
|
|
|
PowerDx // power policy sent a Dx to Dx transition
|
|
},
|
|
},
|
|
|
|
// WdfDevStatePowerGotoDxArmedForWake
|
|
{ FxPkgPnp::PowerGotoDxArmedForWake,
|
|
{ PowerCompleteDx, WdfDevStatePowerGotoDxIoStoppedArmedForWake DEBUGGED_EVENT },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerGotoDxArmedForWakeNP
|
|
{ FxPkgPnp::PowerGotoDxArmedForWakeNP,
|
|
{ PowerCompleteDx, WdfDevStatePowerGotoDxIoStoppedArmedForWakeNP DEBUGGED_EVENT },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerGotoDxIoStoppedArmedForWake
|
|
{ FxPkgPnp::PowerGotoDxIoStoppedArmedForWake,
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerGotoDxIoStoppedArmedForWakeNP
|
|
{ FxPkgPnp::PowerGotoDxIoStoppedArmedForWakeNP,
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerDxArmedForWake
|
|
{ NULL,
|
|
{ PowerD0, WdfDevStatePowerCheckParentStateArmedForWake DEBUGGED_EVENT },
|
|
FxPkgPnp::m_DxArmedForWakeOtherStates,
|
|
{ TRUE,
|
|
PowerParentToD0 // can occur on a PDO when a Dx transition completes
|
|
// on the parent and it wakes up before the child
|
|
},
|
|
},
|
|
|
|
// WdfDevStatePowerDxArmedForWakeNP
|
|
{ NULL,
|
|
{ PowerD0, WdfDevStatePowerCheckParentStateArmedForWakeNP DEBUGGED_EVENT },
|
|
FxPkgPnp::m_DxArmedForWakeNPOtherStates,
|
|
{ TRUE,
|
|
PowerParentToD0 // can occur on a PDO when a Dx transition completes
|
|
// on the parent and it wakes up before the child
|
|
},
|
|
},
|
|
|
|
// WdfDevStatePowerCheckParentStateArmedForWake
|
|
{ FxPkgPnp::PowerCheckParentStateArmedForWake,
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerCheckParentStateArmedForWakeNP
|
|
{ FxPkgPnp::PowerCheckParentStateArmedForWakeNP,
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerWaitForParentArmedForWake
|
|
{ NULL,
|
|
{ PowerParentToD0, WdfDevStatePowerDxDisablingWakeAtBus DEBUGGED_EVENT },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerWaitForParentArmedForWakeNP
|
|
{ NULL,
|
|
{ PowerParentToD0, WdfDevStatePowerDxDisablingWakeAtBusNP DEBUGGED_EVENT },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerStartSelfManagedIo
|
|
{ FxPkgPnp::PowerStartSelfManagedIo,
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerStartSelfManagedIoNP
|
|
{ FxPkgPnp::PowerStartSelfManagedIoNP,
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerStartSelfManagedIoFailed
|
|
{ FxPkgPnp::PowerStartSelfManagedIoFailed,
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerStartSelfManagedIoFailedNP
|
|
{ FxPkgPnp::PowerStartSelfManagedIoFailedNP,
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerWaitForParent
|
|
{ NULL,
|
|
{ PowerParentToD0, WdfDevStatePowerWaking DEBUGGED_EVENT },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerWaitForParentNP
|
|
{ NULL,
|
|
{ PowerParentToD0, WdfDevStatePowerWakingNP DEBUGGED_EVENT },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerWakePending
|
|
{ FxPkgPnp::PowerWakePending,
|
|
{ PowerD0, WdfDevStatePowerCheckParentStateArmedForWake DEBUGGED_EVENT },
|
|
FxPkgPnp::m_WakePendingOtherStates,
|
|
{ TRUE,
|
|
PowerParentToD0 // parent moved to D0 while the child was moving to
|
|
// D0 from Dx armed for wake
|
|
},
|
|
},
|
|
|
|
// WdfDevStatePowerWakePendingNP
|
|
{ FxPkgPnp::PowerWakePendingNP,
|
|
{ PowerD0, WdfDevStatePowerCheckParentStateArmedForWakeNP DEBUGGED_EVENT },
|
|
FxPkgPnp::m_WakePendingNPOtherStates,
|
|
{ TRUE,
|
|
PowerParentToD0 // parent moved to D0 while the child was moving to
|
|
// D0 from Dx armed for wake
|
|
},
|
|
},
|
|
|
|
// WdfDevStatePowerWaking
|
|
{ FxPkgPnp::PowerWaking,
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerWakingNP
|
|
{ FxPkgPnp::PowerWakingNP,
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerWakingConnectInterrupt
|
|
{ FxPkgPnp::PowerWakingConnectInterrupt,
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerWakingConnectInterruptNP
|
|
{ FxPkgPnp::PowerWakingConnectInterruptNP,
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerWakingConnectInterruptFailed
|
|
{ FxPkgPnp::PowerWakingConnectInterruptFailed,
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerWakingConnectInterruptFailedNP
|
|
{ FxPkgPnp::PowerWakingConnectInterruptFailedNP,
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerWakingDmaEnable
|
|
{ FxPkgPnp::PowerWakingDmaEnable,
|
|
{ PowerCompleteD0, WdfDevStatePowerStartSelfManagedIo DEBUGGED_EVENT },
|
|
NULL,
|
|
{ FALSE,
|
|
PowerParentToD0 // parent moved to D0 while the child was moving to
|
|
// D0 from Dx armed for wake
|
|
},
|
|
},
|
|
|
|
// WdfDevStatePowerWakingDmaEnableNP
|
|
{ FxPkgPnp::PowerWakingDmaEnableNP,
|
|
{ PowerCompleteD0, WdfDevStatePowerStartSelfManagedIoNP DEBUGGED_EVENT },
|
|
NULL,
|
|
{ FALSE,
|
|
PowerParentToD0 // parent moved to D0 while the child was moving to
|
|
// D0 from Dx armed for wake
|
|
},
|
|
},
|
|
|
|
// WdfDevStatePowerWakingDmaEnableFailed
|
|
{ FxPkgPnp::PowerWakingDmaEnableFailed,
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerWakingDmaEnableFailedNP
|
|
{ FxPkgPnp::PowerWakingDmaEnableFailedNP,
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerReportPowerUpFailedDerefParent
|
|
{ FxPkgPnp::PowerReportPowerUpFailedDerefParent,
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
NULL,
|
|
{ FALSE,
|
|
0
|
|
},
|
|
},
|
|
|
|
// WdfDevStatePowerReportPowerUpFailed
|
|
{ FxPkgPnp::PowerReportPowerUpFailed,
|
|
{ PowerImplicitD3, WdfDevStatePowerPowerFailedPowerDown DEBUGGED_EVENT },
|
|
NULL,
|
|
{ TRUE,
|
|
0
|
|
},
|
|
},
|
|
|
|
// WdfDevStatePowerPowerFailedPowerDown
|
|
{ FxPkgPnp::PowerPowerFailedPowerDown,
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerReportPowerDownFailed
|
|
{ FxPkgPnp::PowerReportPowerDownFailed,
|
|
{ PowerImplicitD3, WdfDevStatePowerPowerFailedPowerDown DEBUGGED_EVENT },
|
|
NULL,
|
|
{ TRUE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerInitialConnectInterruptFailed
|
|
{ FxPkgPnp::PowerInitialConnectInterruptFailed,
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerInitialDmaEnableFailed
|
|
{ FxPkgPnp::PowerInitialDmaEnableFailed,
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerInitialSelfManagedIoFailed
|
|
{ FxPkgPnp::PowerInitialSelfManagedIoFailed,
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerInitialPowerUpFailedDerefParent
|
|
{ FxPkgPnp::PowerInitialPowerUpFailedDerefParent,
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerInitialPowerUpFailed
|
|
{ FxPkgPnp::PowerInitialPowerUpFailed,
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerDxStoppedDisarmWake
|
|
{ FxPkgPnp::PowerDxStoppedDisarmWake,
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerDxStoppedDisarmWakeNP
|
|
{ FxPkgPnp::PowerDxStoppedDisarmWakeNP,
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerGotoDxStoppedDisableInterruptNP
|
|
{ FxPkgPnp::PowerGotoDxStoppedDisableInterruptNP,
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerGotoDxStopped
|
|
{ FxPkgPnp::PowerGotoDxStopped,
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerDxStopped
|
|
{ NULL,
|
|
{ PowerImplicitD0, WdfDevStatePowerDxStoppedDecideDxState TRAP_ON_EVENT },
|
|
FxPkgPnp::m_PowerDxStoppedOtherStates,
|
|
{ TRUE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerGotoStopped
|
|
{ FxPkgPnp::PowerGotoStopped,
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerStoppedCompleteDx
|
|
{ FxPkgPnp::PowerStoppedCompleteDx,
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerDxStoppedDecideDxState
|
|
{ FxPkgPnp::PowerDxStoppedDecideDxState,
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerDxStoppedArmForWake
|
|
{ FxPkgPnp::PowerDxStoppedArmForWake,
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerDxStoppedArmForWakeNP
|
|
{ FxPkgPnp::PowerDxStoppedArmForWakeNP,
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerFinalPowerDownFailed
|
|
{ FxPkgPnp::PowerFinalPowerDownFailed,
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerFinal
|
|
{ NULL,
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerGotoImplicitD3DisarmWakeAtBus
|
|
{ FxPkgPnp::PowerGotoImplicitD3DisarmWakeAtBus,
|
|
{ PowerEventMaximum, WdfDevStatePowerNull DEBUGGED_EVENT },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerUpFailed
|
|
{ FxPkgPnp::PowerUpFailed,
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerUpFailedDerefParent
|
|
{ FxPkgPnp::PowerUpFailedDerefParent,
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerGotoDxFailed
|
|
{ FxPkgPnp::PowerGotoDxFailed,
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerGotoDxStoppedDisableInterrupt
|
|
{ FxPkgPnp::PowerGotoDxStoppedDisableInterrupt,
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerUpFailedNP
|
|
{ FxPkgPnp::PowerUpFailedNP,
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerUpFailedDerefParentNP
|
|
{ FxPkgPnp::PowerUpFailedDerefParentNP,
|
|
{ PowerEventMaximum, WdfDevStatePowerNull },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerNotifyingD0ExitToWakeInterrupts
|
|
{ FxPkgPnp::PowerNotifyingD0ExitToWakeInterrupts,
|
|
{ PowerWakeInterruptCompleteTransition, WdfDevStatePowerGotoDxIoStopped DEBUGGED_EVENT },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerNotifyingD0EntryToWakeInterrupts
|
|
{ FxPkgPnp::PowerNotifyingD0EntryToWakeInterrupts,
|
|
{ PowerWakeInterruptCompleteTransition, WdfDevStatePowerWakingConnectInterrupt DEBUGGED_EVENT },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
// WdfDevStatePowerNotifyingD0ExitToWakeInterruptsNP
|
|
{ FxPkgPnp::PowerNotifyingD0ExitToWakeInterruptsNP,
|
|
{ PowerWakeInterruptCompleteTransition, WdfDevStatePowerGotoDxIoStoppedNP TRAP_ON_EVENT },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerNotifyingD0EntryToWakeInterrupts
|
|
{ FxPkgPnp::PowerNotifyingD0EntryToWakeInterruptsNP,
|
|
{ PowerWakeInterruptCompleteTransition, WdfDevStatePowerWakingConnectInterruptNP TRAP_ON_EVENT },
|
|
NULL,
|
|
{ FALSE,
|
|
0 },
|
|
},
|
|
|
|
// WdfDevStatePowerNull
|
|
// *** no entry for this state ***
|
|
};
|
|
|
|
// @@SMVERIFY_SPLIT_END
|
|
|
|
_Must_inspect_result_
|
|
NTSTATUS
|
|
FxPowerMachine::Init(
|
|
__inout FxPkgPnp* Pnp,
|
|
__in PFN_PNP_EVENT_WORKER WorkerRoutine
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
|
|
status = FxThreadedEventQueue::Init(Pnp, WorkerRoutine);
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
FxPkgPnp::PowerCheckAssumptions(
|
|
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(FxPowerStateInfo) == sizeof(ULONG));
|
|
|
|
WDFCASSERT((sizeof(m_WdfPowerStates)/sizeof(m_WdfPowerStates[0]))
|
|
==
|
|
(WdfDevStatePowerNull - WdfDevStatePowerObjectCreated));
|
|
|
|
// we assume these are the same length when we update the history index
|
|
WDFCASSERT((sizeof(m_PowerMachine.m_Queue.Events)/
|
|
sizeof(m_PowerMachine.m_Queue.Events[0]))
|
|
==
|
|
(sizeof(m_PowerMachine.m_States.History)/
|
|
sizeof(m_PowerMachine.m_States.History[0])));
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
The locking model for the Power state machine requires that events be enqueued
|
|
possibly at DISPATCH_LEVEL. It also requires that the state machine be
|
|
runnable at PASSIVE_LEVEL. Consequently, we have two locks, one DISPATCH_LEVEL
|
|
lock that guards the event queue and one PASSIVE_LEVEL lock that guards the
|
|
state machine itself.
|
|
|
|
The Power state machine has a few constraints that the PnP state machine
|
|
doesn't. Sometimes it has to call some driver functions at PASSIVE_LEVEL, but
|
|
with the disks turned off. This means that these functions absolutely must not
|
|
page fault. You might think that this means that we should call the driver at
|
|
DISPATCH_LEVEL, and you'd be right if your only concern were for perfectly
|
|
safe code. The problem with that approach, though is that it will force much
|
|
of the rest of the driver to DISPATCH_LEVEL, which will only push the driver
|
|
writer into using lots of asynchronous work items, which will complicate their
|
|
code and make it unsafe in a new variety of ways. So we're going to go with
|
|
PASSIVE_LEVEL here and setting a timeout of 20 seconds. If the driver faults,
|
|
the timer will fire and log the failure. This also means that the driver must
|
|
complete these callbacks within 20 seconds. Even beyond that, it means that
|
|
the work items must be queued onto a special thread, one that once the machine
|
|
has started to go to sleep, never handles any work items that may fault.
|
|
|
|
Algorithm:
|
|
|
|
1) Acquire the Power queue lock.
|
|
2) Enqueue the request. Requests are put at the end of the queue.
|
|
3) Drop the Power queue lock.
|
|
4) If the thread is running at PASSIVE_LEVEL, skip to step 6.
|
|
5) Queue a work item onto the special power thread.
|
|
6) Attempt to acquire the state machine lock, with a near-zero-length timeout.
|
|
7) If successful, skip to step 9.
|
|
8) Queue a work item onto the special power thread.
|
|
9) Acquire the state machine lock.
|
|
10) Acquire the Power queue lock.
|
|
11) Attempt to dequeue an event.
|
|
12) Drop the Power queue lock.
|
|
13) If there was no event to dequeue, drop the state machine lock and exit.
|
|
14) Execute the state handler. This may involve taking one of the other state
|
|
machine queue locks, briefly, to deliver an event.
|
|
15) Go to Step 10.
|
|
|
|
Implementing this algorithm requires three functions.
|
|
|
|
PowerProcessEvent -- Implements steps 1-8.
|
|
_PowerProcessEventInner -- Implements step 9.
|
|
PowerProcessEventInner -- Implements steps 10-15.
|
|
|
|
--*/
|
|
|
|
VOID
|
|
FxPkgPnp::PowerProcessEvent(
|
|
__in FxPowerEvent Event,
|
|
__in BOOLEAN ProcessOnDifferentThread
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
This function implements steps 1-8 of the algorithm described above.
|
|
|
|
Arguments:
|
|
Event - Current Power event
|
|
|
|
ProcessOnDifferentThread - Process the event on a different thread
|
|
regardless of IRQL. By default this is FALSE as per the declaration.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
KIRQL irql;
|
|
|
|
//
|
|
// Take the lock, raising to DISPATCH_LEVEL.
|
|
//
|
|
m_PowerMachine.Lock(&irql);
|
|
|
|
//
|
|
// If the input Event is any of the events described by PowerSingularEventMask,
|
|
// then check whether it is already queued up. If so, then dont enqueue this
|
|
// Event.
|
|
//
|
|
if (Event & PowerSingularEventMask) {
|
|
if ((m_PowerMachine.m_SingularEventsPresent & Event) == 0x00) {
|
|
m_PowerMachine.m_SingularEventsPresent |= Event;
|
|
}
|
|
else {
|
|
DoTraceLevelMessage(
|
|
GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGPNP,
|
|
"WDFDEVICE 0x%p !devobj 0x%p current pwr pol state "
|
|
"%!WDF_DEVICE_POWER_STATE! dropping event %!FxPowerEvent! because "
|
|
"the Event is already enqueued.", m_Device->GetHandle(),
|
|
m_Device->GetDeviceObject(),
|
|
m_Device->GetDevicePowerState(),
|
|
Event);
|
|
|
|
m_PowerMachine.Unlock(irql);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (m_PowerMachine.IsFull()) {
|
|
//
|
|
// The queue is full. Bail.
|
|
//
|
|
m_PowerMachine.Unlock(irql);
|
|
|
|
ASSERT(!"The Power queue is full. This shouldn't be able to happen.");
|
|
return;
|
|
}
|
|
|
|
if (m_PowerMachine.IsClosedLocked()) {
|
|
DoTraceLevelMessage(
|
|
GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGPNP,
|
|
"WDFDEVICE 0x%p !devobj 0x%p current pwr pol state "
|
|
"%!WDF_DEVICE_POWER_STATE! dropping event %!FxPowerEvent! because "
|
|
"of a closed queue", m_Device->GetHandle(),
|
|
m_Device->GetDeviceObject(),
|
|
m_Device->GetDevicePowerState(),
|
|
Event);
|
|
|
|
//
|
|
// The queue is closed. Bail
|
|
//
|
|
m_PowerMachine.Unlock(irql);
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Enqueue the event. Whether the event goes on the front
|
|
// or the end of the queue depends on which event it is.
|
|
//
|
|
if (Event & PowerPriorityEventsMask) {
|
|
//
|
|
// Stick it on the front of the queue, making it the next
|
|
// event that will be processed.
|
|
//
|
|
m_PowerMachine.m_Queue.Events[m_PowerMachine.InsertAtHead()] = (USHORT) Event;
|
|
}
|
|
else {
|
|
//
|
|
// Stick it on the end of the queue.
|
|
//
|
|
m_PowerMachine.m_Queue.Events[m_PowerMachine.InsertAtTail()] = (USHORT) Event;
|
|
}
|
|
|
|
//
|
|
// Drop the lock.
|
|
//
|
|
m_PowerMachine.Unlock(irql);
|
|
|
|
//
|
|
// Now, if we are running at PASSIVE_LEVEL, attempt to run the state
|
|
// machine on this thread. If we can't do that, then queue a work item.
|
|
//
|
|
|
|
if (irql == PASSIVE_LEVEL &&
|
|
FALSE == ProcessOnDifferentThread) {
|
|
LONGLONG timeout = 0;
|
|
|
|
status = m_PowerMachine.m_StateMachineLock.AcquireLock(
|
|
GetDriverGlobals(), &timeout);
|
|
|
|
if (FxWaitLockInternal::IsLockAcquired(status)) {
|
|
FxPostProcessInfo info;
|
|
|
|
//
|
|
// We now hold the state machine lock. So call the function that
|
|
// dispatches the next state.
|
|
//
|
|
PowerProcessEventInner(&info);
|
|
|
|
//
|
|
// The pnp state machine should be the only one deleting the object
|
|
//
|
|
ASSERT(info.m_DeleteObject == FALSE);
|
|
|
|
m_PowerMachine.m_StateMachineLock.ReleaseLock(GetDriverGlobals());
|
|
|
|
info.Evaluate(this);
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
//
|
|
// The tag added above will be released when the work item runs
|
|
//
|
|
|
|
// For one reason or another, we couldn't run the state machine on this
|
|
// thread. So queue a work item to do it. If m_PnPWorkItemEnqueuing
|
|
// is non-zero, that means that the work item is already being enqueued
|
|
// on another thread. This is significant, since it means that we can't do
|
|
// anything with the work item on this thread, but it's okay, since the
|
|
// work item will pick up our work and do it.
|
|
//
|
|
m_PowerMachine.QueueToThread();
|
|
}
|
|
|
|
VOID
|
|
FxPkgPnp::_PowerProcessEventInner(
|
|
__in FxPkgPnp* This,
|
|
__in FxPostProcessInfo* Info,
|
|
__in PVOID WorkerContext
|
|
)
|
|
{
|
|
|
|
UNREFERENCED_PARAMETER(WorkerContext);
|
|
|
|
//
|
|
// Take the state machine lock.
|
|
//
|
|
This->m_PowerMachine.m_StateMachineLock.AcquireLock(
|
|
This->GetDriverGlobals()
|
|
);
|
|
|
|
//
|
|
// Call the function that will actually run the state machine.
|
|
//
|
|
This->PowerProcessEventInner(Info);
|
|
|
|
//
|
|
// We are being called from the work item and m_WorkItemRunning is > 0, so
|
|
// we cannot be deleted yet.
|
|
//
|
|
ASSERT(Info->SomethingToDo() == FALSE);
|
|
|
|
//
|
|
// Now release the lock
|
|
//
|
|
This->m_PowerMachine.m_StateMachineLock.ReleaseLock(
|
|
This->GetDriverGlobals()
|
|
);
|
|
}
|
|
|
|
VOID
|
|
FxPkgPnp::PowerProcessEventInner(
|
|
__inout FxPostProcessInfo* Info
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
This routine runs the state machine. It implements steps 10-15 of the
|
|
algorithm described above.
|
|
|
|
--*/
|
|
{
|
|
WDF_DEVICE_POWER_STATE currentPowerState, newState;
|
|
CPPOWER_STATE_TABLE entry;
|
|
FxPowerEvent event;
|
|
KIRQL oldIrql;
|
|
|
|
//
|
|
// Process as many events as we can.
|
|
//
|
|
for (;;) {
|
|
|
|
newState = WdfDevStatePowerNull;
|
|
currentPowerState = m_Device->GetDevicePowerState();
|
|
entry = GetPowerTableEntry(currentPowerState);
|
|
|
|
//
|
|
// Get an event from the queue.
|
|
//
|
|
m_PowerMachine.Lock(&oldIrql);
|
|
|
|
if (m_PowerMachine.IsEmpty()) {
|
|
m_PowerMachine.GetFinishedState(Info);
|
|
|
|
//
|
|
// The queue is empty.
|
|
//
|
|
m_PowerMachine.Unlock(oldIrql);
|
|
return;
|
|
}
|
|
|
|
event = (FxPowerEvent) m_PowerMachine.m_Queue.Events[m_PowerMachine.GetHead()];
|
|
|
|
//
|
|
// At this point, we need to determine whether we can process this
|
|
// event.
|
|
//
|
|
if (event & PowerPriorityEventsMask) {
|
|
//
|
|
// These are always possible to handle.
|
|
//
|
|
DO_NOTHING();
|
|
}
|
|
else {
|
|
//
|
|
// Check to see if this state can handle new events.
|
|
//
|
|
if (entry->StateInfo.Bits.QueueOpen == FALSE) {
|
|
//
|
|
// This state can't handle new events.
|
|
//
|
|
m_PowerMachine.Unlock(oldIrql);
|
|
return;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the event obtained from the queue was a singular event, then
|
|
// clear the flag to allow other similar events to be put into this
|
|
// queue for processing.
|
|
//
|
|
if (m_PowerMachine.m_SingularEventsPresent & event) {
|
|
m_PowerMachine.m_SingularEventsPresent &= ~event;
|
|
}
|
|
|
|
m_PowerMachine.IncrementHead();
|
|
m_PowerMachine.Unlock(oldIrql);
|
|
|
|
//
|
|
// Find the entry in the power state table that corresponds to this event
|
|
//
|
|
if (entry->FirstTargetState.PowerEvent == event) {
|
|
newState = entry->FirstTargetState.TargetState;
|
|
|
|
DO_EVENT_TRAP(&entry->FirstTargetState);
|
|
}
|
|
else if (entry->OtherTargetStates != NULL) {
|
|
ULONG i = 0;
|
|
|
|
for (i = 0;
|
|
entry->OtherTargetStates[i].PowerEvent != PowerEventMaximum;
|
|
i++) {
|
|
if (entry->OtherTargetStates[i].PowerEvent == event) {
|
|
newState = entry->OtherTargetStates[i].TargetState;
|
|
DO_EVENT_TRAP(&entry->OtherTargetStates[i]);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (newState == WdfDevStatePowerNull) {
|
|
DoTraceLevelMessage(
|
|
GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP,
|
|
"WDFDEVICE 0x%p !devobj 0x%p current power state "
|
|
"%!WDF_DEVICE_POWER_STATE! dropping event %!FxPowerEvent!",
|
|
m_Device->GetHandle(),
|
|
m_Device->GetDeviceObject(),
|
|
m_Device->GetDevicePowerState(), event);
|
|
|
|
//
|
|
// This state doesn't respond to the Event. Potentially throw
|
|
// the event away.
|
|
//
|
|
if ((entry->StateInfo.Bits.KnownDroppedEvents & event) == 0) {
|
|
COVERAGE_TRAP();
|
|
|
|
DoTraceLevelMessage(
|
|
GetDriverGlobals(), TRACE_LEVEL_WARNING, TRACINGPNP,
|
|
"WDFDEVICE %p !devobj 0x%p current state "
|
|
"%!WDF_DEVICE_POWER_STATE! event %!FxPowerEvent! is not a "
|
|
"known dropped event, known dropped events are "
|
|
"%!FxPowerEvent!", m_Device->GetHandle(),
|
|
m_Device->GetDeviceObject(),
|
|
m_Device->GetDevicePowerState(),
|
|
event, entry->StateInfo.Bits.KnownDroppedEvents);
|
|
|
|
|
|
}
|
|
|
|
switch (event) {
|
|
case PowerWakeSucceeded:
|
|
case PowerWakeFailed:
|
|
case PowerWakeCanceled:
|
|
//
|
|
// There are states where we don't care if the wake completed.
|
|
// Since the completion/cancellation of the wake request posts
|
|
// an event which it assumes will complete the request, we must
|
|
// catch these events here and complete the request.
|
|
//
|
|
PowerCompletePendedWakeIrp();
|
|
break;
|
|
|
|
case PowerD0:
|
|
case PowerDx:
|
|
//
|
|
// There are some (non WDF) power policy owner implementations
|
|
// which send Dx to Dx or D0 to D0 transitions to the stack.
|
|
//
|
|
// We don't explicitly handle them in the state machine.
|
|
//
|
|
// Instead, we complete the pended irp if are about to drop it
|
|
// on the floor.
|
|
//
|
|
PowerReleasePendingDeviceIrp();
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// Now enter the new state.
|
|
//
|
|
PowerEnterNewState(newState);
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID
|
|
FxPkgPnp::PowerEnterNewState(
|
|
__in WDF_DEVICE_POWER_STATE State
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
This function looks up the handler for a state and
|
|
then calls it.
|
|
|
|
Arguments:
|
|
Event - Current PnP event
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
CPPOWER_STATE_TABLE entry;
|
|
WDF_DEVICE_POWER_STATE currentState, newState;
|
|
WDF_DEVICE_POWER_NOTIFICATION_DATA data;
|
|
FxWatchdog watchdog(this);
|
|
|
|
currentState = m_Device->GetDevicePowerState();
|
|
newState = State;
|
|
|
|
while (newState != WdfDevStatePowerNull) {
|
|
DoTraceLevelMessage(
|
|
GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGPNPPOWERSTATES,
|
|
"WDFDEVICE 0x%p !devobj 0x%p entering Power State "
|
|
"%!WDF_DEVICE_POWER_STATE! from %!WDF_DEVICE_POWER_STATE!",
|
|
m_Device->GetHandle(),
|
|
m_Device->GetDeviceObject(),
|
|
newState, currentState);
|
|
|
|
if (m_PowerStateCallbacks != NULL) {
|
|
//
|
|
// Callback for leaving the old state
|
|
//
|
|
RtlZeroMemory(&data, sizeof(data));
|
|
|
|
data.Type = StateNotificationLeaveState;
|
|
data.Data.LeaveState.CurrentState = currentState;
|
|
data.Data.LeaveState.NewState = newState;
|
|
|
|
m_PowerStateCallbacks->Invoke(currentState,
|
|
StateNotificationLeaveState,
|
|
m_Device->GetHandle(),
|
|
&data);
|
|
}
|
|
|
|
m_PowerMachine.m_States.History[m_PowerMachine.IncrementHistoryIndex()] =
|
|
(USHORT) newState;
|
|
|
|
if (m_PowerStateCallbacks != NULL) {
|
|
//
|
|
// Callback for entering the new state
|
|
//
|
|
RtlZeroMemory(&data, sizeof(data));
|
|
|
|
data.Type = StateNotificationEnterState;
|
|
data.Data.EnterState.CurrentState = currentState;
|
|
data.Data.EnterState.NewState = newState;
|
|
|
|
m_PowerStateCallbacks->Invoke(newState,
|
|
StateNotificationEnterState,
|
|
m_Device->GetHandle(),
|
|
&data);
|
|
}
|
|
|
|
m_Device->SetDevicePowerState(newState);
|
|
currentState = newState;
|
|
|
|
entry = GetPowerTableEntry(currentState);
|
|
|
|
//
|
|
// And call the state handler, if there is one.
|
|
//
|
|
if (entry->StateFunc != NULL) {
|
|
watchdog.StartTimer(currentState);
|
|
newState = entry->StateFunc(this);
|
|
watchdog.CancelTimer(currentState);
|
|
|
|
//
|
|
// Validate the return value if FX_STATE_MACHINE_VERIFY is enabled
|
|
//
|
|
VALIDATE_POWER_STATE(currentState, newState);
|
|
|
|
}
|
|
else {
|
|
newState = WdfDevStatePowerNull;
|
|
}
|
|
|
|
if (m_PowerStateCallbacks != NULL) {
|
|
//
|
|
// Callback for post processing the new state
|
|
//
|
|
RtlZeroMemory(&data, sizeof(data));
|
|
|
|
data.Type = StateNotificationPostProcessState;
|
|
data.Data.PostProcessState.CurrentState = currentState;
|
|
|
|
m_PowerStateCallbacks->Invoke(currentState,
|
|
StateNotificationPostProcessState,
|
|
m_Device->GetHandle(),
|
|
&data);
|
|
}
|
|
}
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerCheckParentState(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
This function implements the Check Parent state. Its only task
|
|
is to dispatch to the FDO and PDO logic and handle error.
|
|
|
|
Arguments:
|
|
none
|
|
|
|
Return Value:
|
|
|
|
new power state
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
BOOLEAN parentOn;
|
|
|
|
status = This->PowerCheckParentOverload(&parentOn);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
return WdfDevStatePowerUpFailed;
|
|
}
|
|
else if (parentOn) {
|
|
return WdfDevStatePowerWaking;
|
|
}
|
|
else {
|
|
return WdfDevStatePowerWaitForParent;
|
|
}
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerCheckParentStateNP(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
This function implements the Check Parent (NP) state. Its only task
|
|
is to dispatch to the FDO and PDO logic.
|
|
|
|
Arguments:
|
|
none
|
|
|
|
Return Value:
|
|
|
|
new power state
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
BOOLEAN parentOn;
|
|
|
|
status = This->PowerCheckParentOverload(&parentOn);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
return WdfDevStatePowerUpFailedNP;
|
|
}
|
|
else if (parentOn) {
|
|
return WdfDevStatePowerWakingNP;
|
|
}
|
|
else {
|
|
return WdfDevStatePowerWaitForParentNP;
|
|
}
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerCheckDeviceType(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
This function implements the Check Type state. Its only task
|
|
is to dispatch to the FDO and PDO logic.
|
|
|
|
Arguments:
|
|
none
|
|
|
|
Return Value:
|
|
|
|
new power state
|
|
|
|
--*/
|
|
{
|
|
return This->PowerCheckDeviceTypeOverload();
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerCheckDeviceTypeNP(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
This function implements the Check Type (NP) state. Its only task
|
|
is to dispatch to the FDO and PDO logic.
|
|
|
|
Arguments:
|
|
none
|
|
|
|
Return Value:
|
|
|
|
new power state
|
|
|
|
--*/
|
|
{
|
|
return This->PowerCheckDeviceTypeNPOverload();
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerEnablingWakeAtBus(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
This function requests the driver to arm the device in a bus generic fashion.
|
|
|
|
Arguments:
|
|
The package which contains this instance of the state machine
|
|
|
|
Return Value:
|
|
new power state
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
|
|
//
|
|
// We should only get into this state when this devobj is not a PDO and a
|
|
// power policy owner.
|
|
//
|
|
ASSERT((This->m_Device->IsPdo() && This->IsPowerPolicyOwner()) == FALSE);
|
|
|
|
status = This->PowerEnableWakeAtBusOverload();
|
|
|
|
ASSERT(status != STATUS_CANCELLED);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
//
|
|
// No matter of the irp status (canceled, pending, completed), we always
|
|
// transition to the D0ArmedForWake state because that is where we
|
|
// we handle the change in the irp's status.
|
|
//
|
|
return WdfDevStatePowerD0ArmedForWake;
|
|
}
|
|
else {
|
|
This->PowerCompleteWakeRequestFromWithinMachine(status);
|
|
|
|
return WdfDevStatePowerD0BusWakeOwner;
|
|
}
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerEnablingWakeAtBusNP(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
This function requests the driver to arm the device in a bus generic fashion.
|
|
|
|
Arguments:
|
|
The package which contains this instance of the state machine
|
|
|
|
Return Value:
|
|
new power state
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
|
|
//
|
|
// We should only get into this state when this devobj is not a PDO and a
|
|
// power policy owner.
|
|
//
|
|
ASSERT((This->m_Device->IsPdo() &&
|
|
This->IsPowerPolicyOwner()) == FALSE);
|
|
|
|
status = This->PowerEnableWakeAtBusOverload();
|
|
|
|
ASSERT(status != STATUS_CANCELLED);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
//
|
|
// No matter of the irp status (canceled, pending, completed), we always
|
|
// transition to the D0ArmedForWake state because that is where we
|
|
// we handle the change in the irp's status.
|
|
//
|
|
return WdfDevStatePowerD0ArmedForWakeNP;
|
|
}
|
|
else {
|
|
//
|
|
// Complete the irp with the error that callback indicated
|
|
//
|
|
COVERAGE_TRAP();
|
|
|
|
This->PowerCompleteWakeRequestFromWithinMachine(status);
|
|
|
|
return WdfDevStatePowerD0BusWakeOwnerNP;
|
|
}
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerDZero(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
D0 state where we cannot wake the machine
|
|
|
|
Arguments:
|
|
This - Instance of this state machine
|
|
|
|
Return Value:
|
|
new power state machine state
|
|
|
|
--*/
|
|
{
|
|
if ((This->m_Device->GetDeviceObjectFlags() & DO_POWER_PAGABLE) == 0) {
|
|
//
|
|
// We are non pageable, go to that state now
|
|
//
|
|
COVERAGE_TRAP();
|
|
|
|
return WdfDevStatePowerDecideD0State;
|
|
}
|
|
|
|
return WdfDevStatePowerNull;
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerD0NP(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
D0 state where we can't cause paging IO and we cannot wake the machine
|
|
|
|
Arguments:
|
|
This - Instance of this state machine
|
|
|
|
Return Value:
|
|
new power state machine state
|
|
|
|
--*/
|
|
{
|
|
if (This->m_Device->GetDeviceObjectFlags() & DO_POWER_PAGABLE) {
|
|
//
|
|
// We are pageable, go to that state now
|
|
//
|
|
COVERAGE_TRAP();
|
|
return WdfDevStatePowerDecideD0State;
|
|
}
|
|
|
|
return WdfDevStatePowerNull;
|
|
}
|
|
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerD0BusWakeOwner(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
This function implements the D0 state. It's job is to figure out whether
|
|
we need to swtich to the D0NP state, and to wait around for an event
|
|
of some kind or another.
|
|
|
|
Arguments:
|
|
none
|
|
|
|
Return Value:
|
|
|
|
new power state
|
|
|
|
--*/
|
|
{
|
|
if ((This->m_Device->GetDeviceObjectFlags() & DO_POWER_PAGABLE) == 0) {
|
|
//
|
|
// We are non pageable, go to that state now
|
|
//
|
|
COVERAGE_TRAP();
|
|
|
|
return WdfDevStatePowerDecideD0State;
|
|
}
|
|
else if (This->PowerIsWakeRequestPresent()) {
|
|
return WdfDevStatePowerEnablingWakeAtBus;
|
|
}
|
|
else {
|
|
return WdfDevStatePowerNull;
|
|
}
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerD0BusWakeOwnerNP(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
This function implements the D0NP state. It's job is to figure out whether
|
|
we need to swtich to the D0 state, and to wait around for an event
|
|
of some kind or another.
|
|
|
|
Arguments:
|
|
none
|
|
|
|
Return Value:
|
|
|
|
new power state
|
|
|
|
--*/
|
|
{
|
|
if (This->m_Device->GetDeviceObjectFlags() & DO_POWER_PAGABLE) {
|
|
//
|
|
// Pageable.
|
|
//
|
|
COVERAGE_TRAP();
|
|
|
|
return WdfDevStatePowerDecideD0State;
|
|
}
|
|
else if (This->PowerIsWakeRequestPresent()) {
|
|
return WdfDevStatePowerEnablingWakeAtBusNP;
|
|
}
|
|
else {
|
|
return WdfDevStatePowerNull;
|
|
}
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerD0ArmedForWake(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Device is in D0 and armed for wake. Complete any pended D0 irp if the power
|
|
policy owner make a D0 to D0 transition. Transition the NP version of
|
|
this state if we are no longer pageable.
|
|
|
|
Arguments:
|
|
This - instance of the state machine
|
|
|
|
Return Value:
|
|
new state machine state
|
|
|
|
--*/
|
|
{
|
|
if ((This->m_Device->GetDeviceObjectFlags() & DO_POWER_PAGABLE) == 0) {
|
|
//
|
|
// We are non pageable, go to that state now
|
|
//
|
|
COVERAGE_TRAP();
|
|
|
|
return WdfDevStatePowerDecideD0State;
|
|
}
|
|
|
|
return WdfDevStatePowerNull;
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerD0ArmedForWakeNP(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Device is in D0 and armed for wake. Complete any pended D0 irp if the power
|
|
policy owner make a D0 to D0 transition. Transition the pageable version of
|
|
this state if we are no longer NP.
|
|
|
|
Arguments:
|
|
This - instance of the state machine
|
|
|
|
Return Value:
|
|
new state machine state
|
|
|
|
--*/
|
|
{
|
|
if (This->m_Device->GetDeviceObjectFlags() & DO_POWER_PAGABLE) {
|
|
//
|
|
// We are pageable, go to that state now
|
|
//
|
|
COVERAGE_TRAP();
|
|
|
|
return WdfDevStatePowerDecideD0State;
|
|
}
|
|
|
|
return WdfDevStatePowerNull;
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerGotoImplicitD3DisarmWakeAtBus(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Disarms the bus after we have armed it. The device is going to implicit D3
|
|
and it has not yet been powered down.
|
|
|
|
Arguments:
|
|
This - instance of the state machine
|
|
|
|
Return Value:
|
|
new state machine state
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// We should only get into this state when this devobj is a PDO and not a
|
|
// power policy owner.
|
|
//
|
|
ASSERT((This->m_Device->IsPdo() &&
|
|
This->IsPowerPolicyOwner()) == FALSE);
|
|
|
|
//
|
|
// Disarm
|
|
// No need to complete the pended ww irp. State machine will complete it
|
|
// in PnpFailed handler, or upper driver will cancel it.
|
|
//
|
|
This->PowerDisableWakeAtBusOverload();
|
|
|
|
return WdfDevStatePowerGotoD3Stopped;
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerD0DisarmingWakeAtBus(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Disarms the bus after we have armed it. The device is still in D0 so it has
|
|
not yet powered down.
|
|
|
|
Arguments:
|
|
This - This instance of the state machine
|
|
|
|
Return Value:
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// We should only get into this state when this devobj is not a PDO and a
|
|
// power policy owner.
|
|
//
|
|
ASSERT((This->m_Device->IsPdo() &&
|
|
This->IsPowerPolicyOwner()) == FALSE);
|
|
|
|
//
|
|
// Disarm
|
|
//
|
|
This->PowerDisableWakeAtBusOverload();
|
|
This->PowerCompletePendedWakeIrp();
|
|
|
|
//
|
|
// Go back to normal unarmed D0 with bus wake ownership
|
|
//
|
|
return WdfDevStatePowerD0BusWakeOwner;
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerD0DisarmingWakeAtBusNP(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Disarms the bus after we have armed it. The device is still in D0 so it has
|
|
not yet powered down.
|
|
|
|
Arguments:
|
|
This - This instance of the state machine
|
|
|
|
Return Value:
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// We should only get into this state when this devobj is not a PDO and a
|
|
// power policy owner.
|
|
//
|
|
ASSERT((This->m_Device->IsPdo() && This->IsPowerPolicyOwner()) == FALSE);
|
|
|
|
//
|
|
// Disarm
|
|
//
|
|
This->PowerDisableWakeAtBusOverload();
|
|
This->PowerCompletePendedWakeIrp();
|
|
|
|
//
|
|
// Go back to normal unarmed D0 with bus wake ownership
|
|
//
|
|
return WdfDevStatePowerD0BusWakeOwnerNP;
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerD0Starting(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
This function calls D0Entry and the moves to the next state based on the
|
|
result.
|
|
|
|
Arguments:
|
|
This - instance of the state machine
|
|
|
|
Return Value:
|
|
new power state
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
|
|
//
|
|
// Call the driver to tell it to put the hardware into the working
|
|
// state.
|
|
//
|
|
// m_DevicePowerState is the "old" state because we update it after the
|
|
// D0Entry callback.
|
|
//
|
|
status = This->m_DeviceD0Entry.Invoke(
|
|
This->m_Device->GetHandle(),
|
|
(WDF_POWER_DEVICE_STATE) This->m_DevicePowerState);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
DoTraceLevelMessage(
|
|
This->GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
"EvtDeviceD0Entry WDFDEVICE 0x%p !devobj 0x%p, old state "
|
|
"%!WDF_POWER_DEVICE_STATE! failed, %!STATUS!",
|
|
This->m_Device->GetHandle(),
|
|
This->m_Device->GetDeviceObject(),
|
|
This->m_DevicePowerState, status);
|
|
|
|
return WdfDevStatePowerInitialPowerUpFailedDerefParent;
|
|
}
|
|
|
|
return WdfDevStatePowerD0StartingConnectInterrupt;
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerD0StartingConnectInterrupt(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Continues bringing the device into D0 from the D3 final state. This routine
|
|
connects and enables the interrupts. If successful it will open up the power
|
|
managed i/o queues.
|
|
|
|
Arguments:
|
|
This - instance of the state machine
|
|
|
|
Return Value:
|
|
new state machine state
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
|
|
//
|
|
// Connect the interrupt and enable it
|
|
//
|
|
status = This->NotifyResourceObjectsD0(NotifyResourcesNoFlags);
|
|
if (!NT_SUCCESS(status)) {
|
|
//
|
|
// NotifyResourceObjectsD0 has already logged the error, no need to
|
|
// repeat any error messsages here
|
|
//
|
|
return WdfDevStatePowerInitialConnectInterruptFailed;
|
|
}
|
|
|
|
status = This->m_DeviceD0EntryPostInterruptsEnabled.Invoke(
|
|
This->m_Device->GetHandle(),
|
|
(WDF_POWER_DEVICE_STATE) This->m_DevicePowerState);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
DoTraceLevelMessage(
|
|
This->GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
"EvtDeviceD0EntryPostInterruptsEnabed WDFDEVICE 0x%p !devobj 0x%p, "
|
|
"old state %!WDF_POWER_DEVICE_STATE! failed, %!STATUS!",
|
|
This->m_Device->GetHandle(),
|
|
This->m_Device->GetDeviceObject(),
|
|
This->m_DevicePowerState, status);
|
|
return WdfDevStatePowerInitialConnectInterruptFailed;
|
|
}
|
|
|
|
//
|
|
// Last, figure out which state to drop into. This is the juncture
|
|
// where we figure out if we're doing power in a pageable fashion.
|
|
//
|
|
return WdfDevStatePowerD0StartingDmaEnable;
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerD0StartingDmaEnable(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
The device is movig to D0 for the first time. Enable any DMA enablers
|
|
attached to the device.
|
|
|
|
Arguments:
|
|
This - instance of the state machine
|
|
|
|
Return Value:
|
|
new machine state
|
|
|
|
--*/
|
|
{
|
|
if (This->PowerDmaEnableAndScan(TRUE) == FALSE) {
|
|
return WdfDevStatePowerInitialDmaEnableFailed;
|
|
}
|
|
|
|
return WdfDevStatePowerD0StartingStartSelfManagedIo;
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerD0StartingStartSelfManagedIo(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
The device is entering D0 from the final Dx state (either start or restart
|
|
perhaps). Send a start event to self managed io and then release
|
|
|
|
Arguments:
|
|
This - instance of the state machine
|
|
|
|
Return Value:
|
|
new state machine state
|
|
|
|
--*/
|
|
{
|
|
|
|
|
|
|
|
|
|
This->m_Device->m_PkgIo->ResumeProcessingForPower();
|
|
|
|
if (This->m_SelfManagedIoMachine != NULL) {
|
|
NTSTATUS status;
|
|
|
|
status = This->m_SelfManagedIoMachine->Start();
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
return WdfDevStatePowerInitialSelfManagedIoFailed;
|
|
}
|
|
}
|
|
|
|
This->PowerSetDevicePowerState(WdfPowerDeviceD0);
|
|
|
|
//
|
|
// Send the PowerUp event to both the PnP and the Power Policy state machines.
|
|
//
|
|
This->PowerSendPowerUpEvents();
|
|
|
|
return WdfDevStatePowerDecideD0State;
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerDecideD0State(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Decide which D0 state we should transition to given the wait wake ownership
|
|
of this device and if DO_POWER_PAGABLE is set or not.
|
|
|
|
Arguments:
|
|
This - instance of the state machine
|
|
|
|
Return Value:
|
|
new power state
|
|
|
|
--*/
|
|
{
|
|
if (This->m_Device->GetDeviceObjectFlags() & DO_POWER_PAGABLE) {
|
|
//
|
|
// Pageable.
|
|
//
|
|
if (This->m_SharedPower.m_WaitWakeOwner) {
|
|
return WdfDevStatePowerD0BusWakeOwner;
|
|
}
|
|
else {
|
|
return WdfDevStatePowerD0;
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// Non-pageable.
|
|
//
|
|
if (This->m_SharedPower.m_WaitWakeOwner) {
|
|
return WdfDevStatePowerD0BusWakeOwnerNP;
|
|
}
|
|
else {
|
|
return WdfDevStatePowerD0NP;
|
|
}
|
|
}
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerGotoD3Stopped(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
This function implements the D3 Stopped state.
|
|
|
|
Arguments:
|
|
none
|
|
|
|
Return Value:
|
|
|
|
new power state
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
BOOLEAN failed;
|
|
|
|
failed = FALSE;
|
|
|
|
//
|
|
// We *must* call suspend here even though the pnp state machine called self
|
|
// managed io stop. Consider the following:
|
|
// 1 this device is a filter
|
|
// 2 the power policy owner has idle enabled and the device stack is
|
|
// currently idled out (in Dx)
|
|
// 3 the query remove comes, this driver processes it and succeeds
|
|
// self managed io stop
|
|
// 4 before the PwrPolStop event is processed in this driver, the pwr policy
|
|
// owner moves the stack into D0.
|
|
// 5 now this driver processed the PwrPolStop and moves into this state. We
|
|
// now need to make sure self managed i/o is in the stopped state before
|
|
// doing anything else.
|
|
//
|
|
|
|
//
|
|
// The self managed io state machine can handle a suspend event when it is
|
|
// already in the stopped state.
|
|
//
|
|
// Tell the driver to stop its self-managed I/O.
|
|
//
|
|
if (This->m_SelfManagedIoMachine != NULL) {
|
|
status = This->m_SelfManagedIoMachine->Suspend();
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
DoTraceLevelMessage(
|
|
This->GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
"EvtDeviceSelfManagedIoStop failed %!STATUS!", status);
|
|
failed = TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Top-edge queue hold.
|
|
This->m_Device->m_PkgIo->StopProcessingForPower(FxIoStopProcessingForPowerHold);
|
|
|
|
if (This->PowerDmaPowerDown() == FALSE) {
|
|
failed = TRUE;
|
|
}
|
|
|
|
status = This->m_DeviceD0ExitPreInterruptsDisabled.Invoke(
|
|
This->m_Device->GetHandle(),
|
|
WdfPowerDeviceD3Final
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
failed = TRUE;
|
|
|
|
DoTraceLevelMessage(
|
|
This->GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
"EvtDeviceD0ExitPreInterruptsDisabled WDFDEVICE 0x%p !devobj 0x%p, "
|
|
"new state %!WDF_POWER_DEVICE_STATE! failed, %!STATUS!",
|
|
This->m_Device->GetHandle(),
|
|
This->m_Device->GetDeviceObject(),
|
|
WdfPowerDeviceD3Final, status);
|
|
}
|
|
|
|
//
|
|
// Disconnect the interrupt.
|
|
//
|
|
status = This->NotifyResourceObjectsDx(NotifyResourcesForceDisconnect);
|
|
if (!NT_SUCCESS(status)) {
|
|
//
|
|
// NotifyResourceObjectsDx already traced the error
|
|
//
|
|
failed = TRUE;
|
|
}
|
|
|
|
//
|
|
// Call the driver to tell it to put the hardware into a sleeping
|
|
// state.
|
|
//
|
|
status = This->m_DeviceD0Exit.Invoke(This->m_Device->GetHandle(),
|
|
WdfPowerDeviceD3Final);
|
|
if (!NT_SUCCESS(status)) {
|
|
failed = TRUE;
|
|
|
|
DoTraceLevelMessage(
|
|
This->GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
"EvtDeviceD0Exit WDFDEVICE 0x%p !devobj 0x%p, new state "
|
|
"%!WDF_POWER_DEVICE_STATE! failed, %!STATUS!",
|
|
This->m_Device->GetHandle(),
|
|
This->m_Device->GetDeviceObject(),
|
|
WdfPowerDeviceD3Final, status);
|
|
}
|
|
|
|
This->PowerSetDevicePowerState(WdfPowerDeviceD3Final);
|
|
|
|
//
|
|
// If this is a child, release the power reference on the parent
|
|
//
|
|
This->PowerParentPowerDereference();
|
|
|
|
if (failed) {
|
|
return WdfDevStatePowerFinalPowerDownFailed;
|
|
}
|
|
|
|
This->PowerSendPowerDownEvents(FxPowerDownTypeImplicit);
|
|
|
|
//
|
|
// If we are not the PPO for the stack we could receive a power irp
|
|
// during the middle of an implicit power down so we cannot assume
|
|
// that there will be no pended power irp during an implicit power down.
|
|
//
|
|
ASSERT(This->IsPowerPolicyOwner() ? This->m_PendingDevicePowerIrp == NULL : TRUE);
|
|
|
|
return WdfDevStatePowerStopped;
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerStartingCheckDeviceType(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
The device is implicitly powering up from the created or stopped state.
|
|
Determine if this is a PDO or not to determine if we must bring the parent
|
|
back into D0.
|
|
|
|
Arguments:
|
|
This - instance of the state machine
|
|
|
|
Return Value:
|
|
WdfDevStatePowerStartingChild or WdfDevStatePowerD0Starting
|
|
|
|
--*/
|
|
{
|
|
if (This->m_Device->IsPdo()) {
|
|
return WdfDevStatePowerStartingChild;
|
|
}
|
|
else {
|
|
return WdfDevStatePowerD0Starting;
|
|
}
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerStartingChild(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Get the parent into a D0 state
|
|
|
|
Arguments:
|
|
This - instance of the state machine
|
|
|
|
Return Value:
|
|
WdfDevStatePowerNull or WdfDevStatePowerD0Starting
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
BOOLEAN parentOn;
|
|
|
|
status = This->PowerCheckParentOverload(&parentOn);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
DoTraceLevelMessage(
|
|
This->GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
"PowerReference on parent WDFDEVICE %p for child WDFDEVICE %p "
|
|
"failed, %!STATUS!", This->m_Device->m_ParentDevice->m_Device->GetHandle(),
|
|
This->m_Device->GetHandle(),
|
|
status);
|
|
|
|
return WdfDevStatePowerInitialPowerUpFailed;
|
|
}
|
|
else if (parentOn) {
|
|
//
|
|
// Parent is powered on, start the power up sequence
|
|
//
|
|
return WdfDevStatePowerD0Starting;
|
|
}
|
|
else {
|
|
//
|
|
// The call to PowerReference will bring the parent into D0 and
|
|
// move us out of this state after we return.
|
|
//
|
|
return WdfDevStatePowerNull;
|
|
}
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerDxDisablingWakeAtBus(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
This function implements the Disable Wake at Bus state.
|
|
|
|
Arguments:
|
|
This - instance of the state machine
|
|
|
|
Return Value:
|
|
WdfDevStatePowerWaking
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// We should only get into this state when this devobj is not a PDO and a
|
|
// power policy owner.
|
|
//
|
|
ASSERT((This->m_Device->IsPdo() && This->IsPowerPolicyOwner()) == FALSE);
|
|
|
|
This->PowerDisableWakeAtBusOverload();
|
|
|
|
return WdfDevStatePowerWaking;
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerDxDisablingWakeAtBusNP(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
This function implements the Disable Wake at Bus state.
|
|
|
|
Arguments:
|
|
This - instance of the state machine
|
|
|
|
Return Value:
|
|
WdfDevStatePowerWakingNP
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// We should only get into this state when this devobj is not a PDO and a
|
|
// power policy owner.
|
|
//
|
|
ASSERT((This->m_Device->IsPdo() && This->IsPowerPolicyOwner()) == FALSE);
|
|
|
|
This->PowerDisableWakeAtBusOverload();
|
|
|
|
return WdfDevStatePowerWakingNP;
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerGotoDNotZero(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
State where we go into Dx in the pageable path.
|
|
|
|
Arguments:
|
|
The instance of this state machine
|
|
|
|
Return Value:
|
|
new power state
|
|
|
|
--*/
|
|
{
|
|
This->PowerGotoDx();
|
|
|
|
return WdfDevStatePowerNull;
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerGotoDNotZeroNP(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
State where we go into Dx in the NP path.
|
|
|
|
Arguments:
|
|
The instance of this state machine
|
|
|
|
Return Value:
|
|
new power state
|
|
|
|
--*/
|
|
{
|
|
This->PowerGotoDx();
|
|
|
|
return WdfDevStatePowerNull;
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerGotoDNotZeroIoStopped(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
{
|
|
if (This->PowerGotoDxIoStopped() == FALSE) {
|
|
return WdfDevStatePowerGotoDxFailed;
|
|
}
|
|
|
|
return WdfDevStatePowerDx;
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerGotoDNotZeroIoStoppedNP(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
{
|
|
if (This->PowerGotoDxIoStoppedNP() == FALSE) {
|
|
return WdfDevStatePowerGotoDxNPFailed;
|
|
}
|
|
|
|
return WdfDevStatePowerDxNP;
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerGotoDxNPFailed(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Going to Dx in the NP path failed. Disconnect all the interrupts.
|
|
|
|
Arguments:
|
|
This - instance of the state machine
|
|
|
|
Return Value:
|
|
WdfDevStatePowerReportPowerDownFailed
|
|
|
|
--*/
|
|
{
|
|
This->DisconnectInterruptNP();
|
|
|
|
return WdfDevStatePowerReportPowerDownFailed;
|
|
}
|
|
|
|
VOID
|
|
FxPkgPnp::PowerGotoDx(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Implements the going into Dx logic for the pageable path.
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Return Value:
|
|
None
|
|
|
|
--*/
|
|
{
|
|
if (m_SelfManagedIoMachine != NULL) {
|
|
NTSTATUS status;
|
|
|
|
//
|
|
// Tell the driver to stop its self-managed I/O
|
|
//
|
|
status = m_SelfManagedIoMachine->Suspend();
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
DoTraceLevelMessage(
|
|
GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
"EvtDeviceSelfManagedIoStop failed %!STATUS!", status);
|
|
|
|
m_PowerMachine.m_IoCallbackFailure = TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Top-edge queue hold
|
|
m_Device->m_PkgIo->StopProcessingForPower(FxIoStopProcessingForPowerHold);
|
|
|
|
PowerPolicyProcessEvent(PwrPolPowerDownIoStopped);
|
|
}
|
|
|
|
BOOLEAN
|
|
FxPkgPnp::PowerGotoDxIoStopped(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Implements the going into Dx logic for the pageable path.
|
|
|
|
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Return Value:
|
|
TRUE if the power down succeeded, FALSE otherwise
|
|
|
|
--*/
|
|
{
|
|
WDF_POWER_DEVICE_STATE state;
|
|
NTSTATUS status;
|
|
BOOLEAN failed;
|
|
FxIrp irp;
|
|
ULONG notifyFlags;
|
|
|
|
failed = FALSE;
|
|
|
|
//
|
|
// First determine the state that will be indicated to the driver
|
|
//
|
|
irp.SetIrp(m_PendingDevicePowerIrp);
|
|
|
|
switch (irp.GetParameterPowerShutdownType()) {
|
|
case PowerActionShutdown:
|
|
case PowerActionShutdownReset:
|
|
case PowerActionShutdownOff:
|
|
state = WdfPowerDeviceD3Final;
|
|
break;
|
|
|
|
default:
|
|
state = (WDF_POWER_DEVICE_STATE) irp.GetParameterPowerStateDeviceState();
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Can we even be a power pageable device and be in hibernation path?
|
|
//
|
|
if (m_SystemPowerState == PowerSystemHibernate &&
|
|
GetUsageCount(WdfSpecialFileHibernation) != 0) {
|
|
COVERAGE_TRAP();
|
|
|
|
//
|
|
// This device is in the hibernation path and the target system state is
|
|
// S4. Tell the driver that it should do special handling.
|
|
//
|
|
state = WdfPowerDevicePrepareForHibernation;
|
|
}
|
|
|
|
if (PowerDmaPowerDown() == FALSE) {
|
|
failed = TRUE;
|
|
}
|
|
|
|
status = m_DeviceD0ExitPreInterruptsDisabled.Invoke(
|
|
m_Device->GetHandle(),
|
|
state
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
failed = TRUE;
|
|
|
|
DoTraceLevelMessage(
|
|
GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
"EvtDeviceD0ExitPreInterruptsDisabled WDFDEVICE 0x%p !devobj 0x%p, "
|
|
"new state %!WDF_POWER_DEVICE_STATE! failed, %!STATUS!",
|
|
m_Device->GetHandle(),
|
|
m_Device->GetDeviceObject(), state, status);
|
|
}
|
|
|
|
//
|
|
// interrupt disable & disconnect
|
|
//
|
|
|
|
notifyFlags = NotifyResourcesExplicitPowerDown;
|
|
|
|
//
|
|
// In general, m_WaitWakeIrp is accessed through guarded InterlockedExchange
|
|
// operations. However, following is a special case where we just want to know
|
|
// the current value. It is possible that the value of m_WaitWakeIrp can
|
|
// change right after we query it. Users of NotifyResourcesArmedForWake will
|
|
// need to be aware of this fact.
|
|
//
|
|
// Note that relying on m_WaitWakeIrp to decide whether to disconnect the wake
|
|
// interrupts or not is unreliable and may result in a race condition between
|
|
// the device powering down and a wake interrupt firing:
|
|
//
|
|
// Thread A: Device is powering down and is going to disconnect wake interrupts
|
|
// unless m_WaitWakeIrp is not NULL.
|
|
// Thread B: Wake interrupt fires (holding the OS interrupt lock) which results
|
|
// in completing the IRP_MN_WAIT_WAKE and setting m_WaitWakeIrp to NULL.
|
|
// Thread then blocks waiting for the device to power up.
|
|
// Thread A: m_WaitWakeIrp is NULL so we disconnect the wake interrupt, but are
|
|
// blocked waiting to acquire the lock held by the ISR. The deadlock
|
|
// results in bugcheck 0x9F since the Dx IRP is being blocked.
|
|
//
|
|
// The m_WakeInterruptsKeepConnected flag is set when we request a IRP_MN_WAIT_WAKE
|
|
// in the device powering down path, and is cleared below once it is used.
|
|
//
|
|
if (m_SharedPower.m_WaitWakeIrp != NULL || m_WakeInterruptsKeepConnected == TRUE) {
|
|
notifyFlags |= NotifyResourcesArmedForWake;
|
|
m_WakeInterruptsKeepConnected = FALSE;
|
|
}
|
|
|
|
status = NotifyResourceObjectsDx(notifyFlags);
|
|
if (!NT_SUCCESS(status)) {
|
|
//
|
|
// NotifyResourceObjectsDx already traced the error
|
|
//
|
|
failed = TRUE;
|
|
}
|
|
|
|
//
|
|
// Call the driver to tell it to put the hardware into a sleeping
|
|
// state.
|
|
//
|
|
|
|
status = m_DeviceD0Exit.Invoke(m_Device->GetHandle(), state);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
DoTraceLevelMessage(
|
|
GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
"EvtDeviceD0Exit WDFEVICE 0x%p !devobj 0x%p, new state "
|
|
"%!WDF_POWER_DEVICE_STATE! failed, %!STATUS!",
|
|
m_Device->GetHandle(),
|
|
m_Device->GetDeviceObject(), state, status);
|
|
|
|
failed = TRUE;
|
|
}
|
|
|
|
//
|
|
// If this is a child, release the power reference on the parent
|
|
//
|
|
PowerParentPowerDereference();
|
|
|
|
//
|
|
// Set our state no matter if power down failed or not
|
|
//
|
|
PowerSetDevicePowerState(state);
|
|
|
|
//
|
|
// Stopping self managed io previously failed, convert that failure into
|
|
// a local failure here.
|
|
//
|
|
if (m_PowerMachine.m_IoCallbackFailure) {
|
|
m_PowerMachine.m_IoCallbackFailure = FALSE;
|
|
failed = TRUE;
|
|
}
|
|
|
|
if (failed) {
|
|
//
|
|
// Power policy will use this property when it is processing the
|
|
// completion of the Dx irp.
|
|
//
|
|
m_PowerMachine.m_PowerDownFailure = TRUE;
|
|
|
|
//
|
|
// This state will record that we encountered an internal error.
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
PowerSendPowerDownEvents(FxPowerDownTypeExplicit);
|
|
|
|
PowerReleasePendingDeviceIrp();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN
|
|
FxPkgPnp::PowerGotoDxIoStoppedNP(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
This function implements going into the Dx state in the NP path.
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Return Value:
|
|
TRUE if the power down succeeded, FALSE otherwise
|
|
|
|
--*/
|
|
{
|
|
WDF_POWER_DEVICE_STATE state;
|
|
NTSTATUS status;
|
|
BOOLEAN failed;
|
|
FxIrp irp;
|
|
|
|
failed = FALSE;
|
|
|
|
//
|
|
// First determine the state that will be indicated to the driver
|
|
//
|
|
irp.SetIrp(m_PendingDevicePowerIrp);
|
|
|
|
switch (irp.GetParameterPowerShutdownType()) {
|
|
case PowerActionShutdown:
|
|
case PowerActionShutdownReset:
|
|
case PowerActionShutdownOff:
|
|
state = WdfPowerDeviceD3Final;
|
|
break;
|
|
|
|
default:
|
|
state = (WDF_POWER_DEVICE_STATE) irp.GetParameterPowerStateDeviceState();
|
|
break;
|
|
}
|
|
|
|
if (m_SystemPowerState == PowerSystemHibernate &&
|
|
GetUsageCount(WdfSpecialFileHibernation) != 0) {
|
|
//
|
|
// This device is in the hibernation path and the target system state is
|
|
// S4. Tell the driver that it should do special handling.
|
|
//
|
|
state = WdfPowerDevicePrepareForHibernation;
|
|
}
|
|
|
|
if (PowerDmaPowerDown() == FALSE) {
|
|
failed = TRUE;
|
|
}
|
|
|
|
status = m_DeviceD0ExitPreInterruptsDisabled.Invoke(
|
|
m_Device->GetHandle(),
|
|
state
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
failed = TRUE;
|
|
|
|
DoTraceLevelMessage(
|
|
GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
"EvtDeviceD0ExitPreInterruptsDisabled WDFDEVICE 0x%p !devobj 0x%p, "
|
|
"new state %!WDF_POWER_DEVICE_STATE! failed, %!STATUS!",
|
|
m_Device->GetHandle(),
|
|
m_Device->GetDeviceObject(), state, status);
|
|
}
|
|
|
|
//
|
|
// Interrupt disable (and NO disconnect)
|
|
//
|
|
status = NotifyResourceObjectsDx(NotifyResourcesNP);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
//
|
|
// NotifyResourceObjectsDx already traced the error
|
|
//
|
|
failed = TRUE;
|
|
}
|
|
|
|
//
|
|
// Call the driver to tell it to put the hardware into a sleeping
|
|
// state.
|
|
//
|
|
|
|
status = m_DeviceD0Exit.Invoke(m_Device->GetHandle(), state);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
DoTraceLevelMessage(
|
|
GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
"EvtDeviceD0Exit WDFDEVICE 0x%p !devobj 0x%p, new state "
|
|
"%!WDF_POWER_DEVICE_STATE! failed, %!STATUS!",
|
|
m_Device->GetHandle(),
|
|
m_Device->GetDeviceObject(), state, status);
|
|
|
|
failed = TRUE;
|
|
}
|
|
|
|
//
|
|
// If this is a child, release the power reference on the parent
|
|
//
|
|
PowerParentPowerDereference();
|
|
|
|
//
|
|
// Set our state no matter if power down failed or not
|
|
//
|
|
PowerSetDevicePowerState(state);
|
|
|
|
//
|
|
// Stopping self managed io previously failed, convert that failure into
|
|
// a local failure here.
|
|
//
|
|
if (m_PowerMachine.m_IoCallbackFailure) {
|
|
m_PowerMachine.m_IoCallbackFailure = FALSE;
|
|
failed = TRUE;
|
|
}
|
|
|
|
if (failed) {
|
|
//
|
|
// Power policy will use this property when it is processing the
|
|
// completion of the Dx irp.
|
|
//
|
|
m_PowerMachine.m_PowerDownFailure = TRUE;
|
|
|
|
//
|
|
// This state will record that we encountered an internal error.
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
PowerSendPowerDownEvents(FxPowerDownTypeExplicit);
|
|
|
|
PowerReleasePendingDeviceIrp();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerGotoDxArmedForWake(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
This function implements the Dx state when we are armed for wake.
|
|
|
|
Arguments:
|
|
This - The instance of the state machine
|
|
|
|
Return Value:
|
|
|
|
new power state
|
|
|
|
--*/
|
|
{
|
|
This->PowerGotoDx();
|
|
|
|
return WdfDevStatePowerNull;
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerGotoDxArmedForWakeNP(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
This function implements the Dx state when we are armed for wake in the NP
|
|
path.
|
|
|
|
Arguments:
|
|
This - The instance of the state machine
|
|
|
|
Return Value:
|
|
|
|
new power state
|
|
|
|
--*/
|
|
{
|
|
This->PowerGotoDx();
|
|
|
|
return WdfDevStatePowerNull;
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerGotoDxIoStoppedArmedForWake(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
{
|
|
if (This->PowerGotoDxIoStopped() == FALSE) {
|
|
return WdfDevStatePowerGotoDxFailed;
|
|
}
|
|
|
|
return WdfDevStatePowerDxArmedForWake;
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerGotoDxIoStoppedArmedForWakeNP(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
{
|
|
if (This->PowerGotoDxIoStoppedNP() == FALSE) {
|
|
return WdfDevStatePowerGotoDxNPFailed;
|
|
}
|
|
|
|
return WdfDevStatePowerDxArmedForWakeNP;
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerCheckParentStateArmedForWake(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
The PDO was armed for wake in Dx and needs to be disarmed at the bus level.
|
|
The child can only be disarmed while the parent is in D0, so check the state
|
|
of the parent. If in D0, move directly to the disarm state, otherwise move
|
|
into a wait state and disarm once the parent is in D0.
|
|
|
|
Arguments:
|
|
This - instance of the state machine
|
|
|
|
Return Value:
|
|
new state
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
BOOLEAN parentOn;
|
|
|
|
status = This->PowerCheckParentOverload(&parentOn);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
return WdfDevStatePowerUpFailed;
|
|
}
|
|
else if (parentOn) {
|
|
return WdfDevStatePowerDxDisablingWakeAtBus;
|
|
}
|
|
else {
|
|
return WdfDevStatePowerWaitForParentArmedForWake;
|
|
}
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerCheckParentStateArmedForWakeNP(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Same as PowerCheckParentStateArmedForWake, but we are in the NP path
|
|
|
|
Arguments:
|
|
This - instance of the state machine
|
|
|
|
Return Value:
|
|
new state
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
BOOLEAN parentOn;
|
|
|
|
status = This->PowerCheckParentOverload(&parentOn);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
return WdfDevStatePowerUpFailedNP;
|
|
}
|
|
else if (parentOn) {
|
|
return WdfDevStatePowerDxDisablingWakeAtBusNP;
|
|
}
|
|
else {
|
|
return WdfDevStatePowerWaitForParentArmedForWakeNP;
|
|
}
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerStartSelfManagedIo(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
This function implements the Start Self-Managed I/O state. It tells the
|
|
driver that it can resume operations that were not interlocked with
|
|
the PnP and Power state machines.
|
|
|
|
Arguments:
|
|
This - The instance of the state machine
|
|
|
|
Return Value:
|
|
|
|
new power state
|
|
|
|
--*/
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
// Top-edge queue release
|
|
This->m_Device->m_PkgIo->ResumeProcessingForPower();
|
|
|
|
if (This->m_SelfManagedIoMachine != NULL) {
|
|
NTSTATUS status;
|
|
|
|
status = This->m_SelfManagedIoMachine->Start();
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
DoTraceLevelMessage(
|
|
This->GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
"EvtDeviceSelfManagedIoRestart failed - %!STATUS!", status);
|
|
|
|
return WdfDevStatePowerStartSelfManagedIoFailed;
|
|
}
|
|
}
|
|
|
|
This->PowerSetDevicePowerState(WdfPowerDeviceD0);
|
|
|
|
//
|
|
// Send the PowerUp event to both the PnP and the Power Policy state
|
|
// machines.
|
|
//
|
|
This->PowerSendPowerUpEvents();
|
|
|
|
This->PowerReleasePendingDeviceIrp();
|
|
|
|
if (This->m_SharedPower.m_WaitWakeOwner) {
|
|
return WdfDevStatePowerD0BusWakeOwner;
|
|
}
|
|
else {
|
|
return WdfDevStatePowerD0;
|
|
}
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerStartSelfManagedIoNP(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
This function implements the Start Self-Managed I/O state. It tells the
|
|
driver that it can resume operations that were not interlocked with
|
|
the PnP and Power state machines.
|
|
|
|
Arguments:
|
|
This - The instance of the state machine
|
|
|
|
Return Value:
|
|
|
|
new power state
|
|
|
|
--*/
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
// Top-edge queue release
|
|
This->m_Device->m_PkgIo->ResumeProcessingForPower();
|
|
|
|
if (This->m_SelfManagedIoMachine != NULL) {
|
|
NTSTATUS status;
|
|
|
|
status = This->m_SelfManagedIoMachine->Start();
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
DoTraceLevelMessage(
|
|
This->GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
"EvtDeviceSelfManagedIoRestart failed - %!STATUS!", status);
|
|
|
|
return WdfDevStatePowerStartSelfManagedIoFailedNP;
|
|
}
|
|
}
|
|
|
|
This->PowerSetDevicePowerState(WdfPowerDeviceD0);
|
|
|
|
//
|
|
// Send the PowerUp event to both the PnP and the Power Policy state machines.
|
|
//
|
|
This->PowerSendPowerUpEvents();
|
|
|
|
This->PowerReleasePendingDeviceIrp();
|
|
|
|
if (This->m_SharedPower.m_WaitWakeOwner) {
|
|
return WdfDevStatePowerD0BusWakeOwnerNP;
|
|
}
|
|
else {
|
|
return WdfDevStatePowerD0NP;
|
|
}
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerStartSelfManagedIoFailed(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Starting self managed io back up from an explicit Dx to D0 transition failed.
|
|
Hold the power managed queues and proceed down the power up failure path.
|
|
|
|
Arguments:
|
|
This - instance of the state machine
|
|
|
|
Return Value:
|
|
WdfDevStatePowerWakingDmaEnableFailed
|
|
|
|
--*/
|
|
{
|
|
|
|
|
|
|
|
|
|
This->m_Device->m_PkgIo->StopProcessingForPower(FxIoStopProcessingForPowerHold);
|
|
|
|
return WdfDevStatePowerWakingDmaEnableFailed;
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerStartSelfManagedIoFailedNP(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Starting self managed io back up from an explicit Dx to D0 transition failed.
|
|
Hold the power managed queues and proceed down the power up failure path.
|
|
|
|
Arguments:
|
|
This - instance of the state machine
|
|
|
|
Return Value:
|
|
WdfDevStatePowerWakingDmaEnableFailedNP
|
|
|
|
--*/
|
|
{
|
|
|
|
|
|
|
|
|
|
This->m_Device->m_PkgIo->StopProcessingForPower(FxIoStopProcessingForPowerHold);
|
|
|
|
return WdfDevStatePowerWakingDmaEnableFailedNP;
|
|
}
|
|
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerWakePending(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
State that indicates a successful wake from Dx. Primarily exists so that
|
|
the driver writer can register to know about the entrance into this state.
|
|
It also completes the pended wait wake request (which posts the appopriate
|
|
events to the power policy state machine if it's listening).
|
|
|
|
Arguments:
|
|
This - The instance of the state machine
|
|
|
|
Return Value:
|
|
return WdfDevStatePowerNull
|
|
|
|
--*/
|
|
{
|
|
This->PowerCompletePendedWakeIrp();
|
|
return WdfDevStatePowerNull;
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerWakePendingNP(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
State that indicates a successful wake from Dx. Primarily exists so that
|
|
the driver writer can register to know about the entrance into this state.
|
|
It also completes the pended wait wake request (which posts the appopriate
|
|
events to the power policy state machine if it's listening).
|
|
|
|
Arguments:
|
|
This - The instance of the state machine
|
|
|
|
Return Value:
|
|
return WdfDevStatePowerNull
|
|
|
|
--*/
|
|
{
|
|
This->PowerCompletePendedWakeIrp();
|
|
return WdfDevStatePowerNull;
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerWaking(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
This function implements the Waking state. Its job is to call into the
|
|
driver to tell it to restore its hardware, and then to connect interrupts
|
|
and release the queues.
|
|
|
|
Arguments:
|
|
This - The instance of the state machine
|
|
|
|
Return Value:
|
|
|
|
new power state
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
|
|
//
|
|
// m_DevicePowerState is the "old" state because we update it after the
|
|
// D0Entry callback in SelfManagedIo or PowerPolicyStopped
|
|
//
|
|
status = This->m_DeviceD0Entry.Invoke(
|
|
This->m_Device->GetHandle(),
|
|
(WDF_POWER_DEVICE_STATE) This->m_DevicePowerState);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
DoTraceLevelMessage(
|
|
This->GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
"EvtDeviceD0Entry WDFDEVICE 0x%p !devobj 0x%p, old state "
|
|
"%!WDF_POWER_DEVICE_STATE! failed, %!STATUS!",
|
|
This->m_Device->GetHandle(),
|
|
This->m_Device->GetDeviceObject(),
|
|
This->m_DevicePowerState, status);
|
|
|
|
return WdfDevStatePowerUpFailedDerefParent;
|
|
}
|
|
|
|
return WdfDevStatePowerNotifyingD0EntryToWakeInterrupts;
|
|
}
|
|
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerWakingNP(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
This function implements the WakingNP state. Its job is to call into the
|
|
driver to tell it to restore its hardware and release the queues.
|
|
|
|
Arguments:
|
|
This - The instance of the state machine
|
|
|
|
Return Value:
|
|
|
|
new power state
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
|
|
//
|
|
// m_DevicePowerState is the "old" state because we update it after the
|
|
// D0Entry callback in SelfManagedIoNP or PowerPolicyStopped
|
|
//
|
|
status = This->m_DeviceD0Entry.Invoke(
|
|
This->m_Device->GetHandle(),
|
|
(WDF_POWER_DEVICE_STATE) This->m_DevicePowerState);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
DoTraceLevelMessage(
|
|
This->GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
"EvtDeviceD0Entry WDFDEVICE 0x%p !devobj 0x%p, old state "
|
|
"%!WDF_POWER_DEVICE_STATE! failed, %!STATUS!",
|
|
This->m_Device->GetHandle(),
|
|
This->m_Device->GetDeviceObject(),
|
|
This->m_DevicePowerState, status);
|
|
|
|
return WdfDevStatePowerUpFailedDerefParentNP;
|
|
}
|
|
|
|
return WdfDevStatePowerNotifyingD0EntryToWakeInterruptsNP;
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerWakingConnectInterrupt(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
The device is returning to D0 in the power pageable path. Connect and
|
|
enable the interrupts. If that succeeds, scan for children and then
|
|
open the power managed I/O queues.
|
|
|
|
Arguments:
|
|
This - instance of the state machine
|
|
|
|
Return Value:
|
|
new state
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
|
|
//
|
|
// interrupt connect and enable
|
|
//
|
|
status = This->NotifyResourceObjectsD0(NotifyResourcesExplicitPowerup);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
return WdfDevStatePowerWakingConnectInterruptFailed;
|
|
}
|
|
|
|
status = This->m_DeviceD0EntryPostInterruptsEnabled.Invoke(
|
|
This->m_Device->GetHandle(),
|
|
(WDF_POWER_DEVICE_STATE) This->m_DevicePowerState);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
DoTraceLevelMessage(
|
|
This->GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
"EvtDeviceD0EntryPostInterruptsEnabed WDFDEVICE 0x%p !devobj 0x%p, "
|
|
"old state %!WDF_POWER_DEVICE_STATE! failed, %!STATUS!",
|
|
This->m_Device->GetHandle(),
|
|
This->m_Device->GetDeviceObject(),
|
|
This->m_DevicePowerState, status);
|
|
return WdfDevStatePowerWakingConnectInterruptFailed;
|
|
}
|
|
|
|
return WdfDevStatePowerWakingDmaEnable;
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerWakingConnectInterruptNP(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
The device is returning to D0 in the power pageable path.
|
|
Enable the interrupts. If that succeeds, scan for children and then
|
|
open the power managed I/O queues.
|
|
|
|
Arguments:
|
|
This - instance of the state machine
|
|
|
|
Return Value:
|
|
new state
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
|
|
//
|
|
// interrupt enable (already connected b/c they were never disconnected
|
|
// during the Dx transition).
|
|
//
|
|
status = This->NotifyResourceObjectsD0(NotifyResourcesNP);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
return WdfDevStatePowerWakingConnectInterruptFailedNP;
|
|
}
|
|
|
|
status = This->m_DeviceD0EntryPostInterruptsEnabled.Invoke(
|
|
This->m_Device->GetHandle(),
|
|
(WDF_POWER_DEVICE_STATE) This->m_DevicePowerState);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
DoTraceLevelMessage(
|
|
This->GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
"EvtDeviceD0EntryPostInterruptsEnabed WDFDEVICE 0x%p !devobj 0x%p, "
|
|
"old state %!WDF_POWER_DEVICE_STATE! failed, %!STATUS!",
|
|
This->m_Device->GetHandle(),
|
|
This->m_Device->GetDeviceObject(),
|
|
This->m_DevicePowerState, status);
|
|
return WdfDevStatePowerWakingConnectInterruptFailedNP;
|
|
}
|
|
|
|
return WdfDevStatePowerWakingDmaEnableNP;
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerWakingConnectInterruptFailed(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Connecting or enabling the interrupts failed. Disable and disconnect any
|
|
interrupts which have been connected and maybe enabled.
|
|
|
|
Arguments:
|
|
This - instance of the state machine
|
|
|
|
Return Value:
|
|
WdfDevStatePowerReportPowerUpFailed
|
|
|
|
--*/
|
|
{
|
|
This->PowerConnectInterruptFailed();
|
|
|
|
return WdfDevStatePowerReportPowerUpFailedDerefParent;
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerWakingConnectInterruptFailedNP(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Enabling the interrupts failed. Disable and disconnect any
|
|
interrupts which have been connected and maybe enabled.
|
|
|
|
Arguments:
|
|
This - instance of the state machine
|
|
|
|
Return Value:
|
|
WdfDevStatePowerReportPowerUpFailed
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// PowerConnectInterruptFailed will call IoDisconnectInterrupt. Since we
|
|
// are in the NP path, this may cause a deadlock between this thread and
|
|
// paging I/O. Log something to the IFR so that if the watchdog timer kicks
|
|
// in, at least we have context as to why we died.
|
|
//
|
|
DoTraceLevelMessage(
|
|
This->GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
"Force disconnecting interupts on !devobj %p, WDFDEVICE %p",
|
|
This->m_Device->GetDeviceObject(),
|
|
This->m_Device->GetHandle());
|
|
|
|
This->PowerConnectInterruptFailed();
|
|
|
|
return WdfDevStatePowerReportPowerUpFailedDerefParent;
|
|
}
|
|
|
|
BOOLEAN
|
|
FxPkgPnp::PowerDmaEnableAndScan(
|
|
__in BOOLEAN ImplicitPowerUp
|
|
)
|
|
{
|
|
FxTransactionedEntry* ple;
|
|
|
|
if (PowerDmaPowerUp() == FALSE) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (m_EnumInfo != NULL) {
|
|
//
|
|
// Scan for children
|
|
//
|
|
m_EnumInfo->m_ChildListList.LockForEnum(GetDriverGlobals());
|
|
|
|
ple = NULL;
|
|
while ((ple = m_EnumInfo->m_ChildListList.GetNextEntry(ple)) != NULL) {
|
|
((FxChildList*) ple->GetTransactionedObject())->ScanForChildren();
|
|
}
|
|
|
|
m_EnumInfo->m_ChildListList.UnlockFromEnum(GetDriverGlobals());
|
|
}
|
|
|
|
if (ImplicitPowerUp == FALSE) {
|
|
PowerPolicyProcessEvent(PwrPolPowerUpHwStarted);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerWakingDmaEnable(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
The device is returning to D0 from Dx. Power up all DMA enablers and scan
|
|
for children.
|
|
|
|
Arguments:
|
|
This - instance of the state machine
|
|
|
|
Return Value:
|
|
new machine state
|
|
|
|
--*/
|
|
{
|
|
if (This->PowerDmaEnableAndScan(FALSE) == FALSE) {
|
|
return WdfDevStatePowerWakingDmaEnableFailed;
|
|
}
|
|
|
|
//
|
|
// Return the state that we should drop into next.
|
|
//
|
|
return WdfDevStatePowerNull;
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerWakingDmaEnableNP(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
The device is returning to D0 from Dx in the NP path. Power up all DMA
|
|
enablers and scan for children.
|
|
|
|
Arguments:
|
|
This - instance of the state machine
|
|
|
|
Return Value:
|
|
new machine state
|
|
|
|
--*/
|
|
{
|
|
if (This->PowerDmaEnableAndScan(FALSE) == FALSE) {
|
|
return WdfDevStatePowerWakingDmaEnableFailedNP;
|
|
}
|
|
|
|
//
|
|
// Return the state that we should drop into next.
|
|
//
|
|
return WdfDevStatePowerNull;
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerWakingDmaEnableFailed(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Powering up a DMA enabler failed. Power down all DMA enablers and progress
|
|
down the failed power up path.
|
|
|
|
Arguments:
|
|
This - instance of the state machine
|
|
|
|
Return Value:
|
|
WdfDevStatePowerWakingConnectInterruptFailed
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
|
|
(void) This->PowerDmaPowerDown();
|
|
|
|
status = This->m_DeviceD0ExitPreInterruptsDisabled.Invoke(
|
|
This->m_Device->GetHandle(),
|
|
WdfPowerDeviceD3Final
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
//
|
|
// Report the error, but continue forward
|
|
//
|
|
DoTraceLevelMessage(
|
|
This->GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
"EvtDeviceD0ExitPreInterruptsDisabled WDFDEVICE 0x%p !devobj 0x%p "
|
|
"new state %!WDF_POWER_DEVICE_STATE! failed, %!STATUS!",
|
|
This->m_Device->GetHandle(),
|
|
This->m_Device->GetDeviceObject(),
|
|
WdfPowerDeviceD3Final, status);
|
|
}
|
|
|
|
return WdfDevStatePowerWakingConnectInterruptFailed;
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerWakingDmaEnableFailedNP(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Powering up a DMA enabler failed in the NP path. Power down all DMA
|
|
enablers and progress down the failed power up path.
|
|
|
|
Arguments:
|
|
This - instance of the state machine
|
|
|
|
Return Value:
|
|
WdfDevStatePowerWakingConnectInterruptFailedNP
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
|
|
COVERAGE_TRAP();
|
|
|
|
(void) This->PowerDmaPowerDown();
|
|
|
|
status = This->m_DeviceD0ExitPreInterruptsDisabled.Invoke(
|
|
This->m_Device->GetHandle(),
|
|
WdfPowerDeviceD3Final
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
//
|
|
// Report the error, but continue forward
|
|
//
|
|
DoTraceLevelMessage(
|
|
This->GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
"EvtDeviceD0ExitPreInterruptsDisabled WDFDEVICE 0x%p !devobj 0x%p "
|
|
"new state %!WDF_POWER_DEVICE_STATE! failed, %!STATUS!",
|
|
This->m_Device->GetHandle(),
|
|
This->m_Device->GetDeviceObject(),
|
|
WdfPowerDeviceD3Final, status);
|
|
}
|
|
|
|
return WdfDevStatePowerWakingConnectInterruptFailedNP;
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerReportPowerUpFailedDerefParent(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Power up failed. Release the reference on the parent that was taken at the
|
|
start of power up.
|
|
|
|
Arguments:
|
|
This - instance of the state machine.
|
|
|
|
Return Value:
|
|
WdfDevStatePowerInitialPowerUpFailed
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// If this is a child, release the power reference on the parent
|
|
//
|
|
This->PowerParentPowerDereference();
|
|
|
|
return WdfDevStatePowerReportPowerUpFailed;
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerReportPowerUpFailed(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Posts PowerUpFailed event to the PnP state machine and then waits for death.
|
|
The pended Power IRP is also completed.
|
|
|
|
Arguments:
|
|
This - The instance of the state machine
|
|
|
|
Return Value:
|
|
new power state
|
|
|
|
--*/
|
|
{
|
|
This->m_SystemPowerAction = PowerActionNone;
|
|
|
|
This->PowerReleasePendingDeviceIrp();
|
|
This->PowerSendPowerUpFailureEvent();
|
|
|
|
return WdfDevStatePowerNull;
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerPowerFailedPowerDown(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
We failed to power up/down properly and now the power policy state machine wants
|
|
to power down the device. Since power policy and pnp rely on power posting
|
|
power down events to make forward progress, we must do that here
|
|
|
|
Arguments:
|
|
This - instance of the state machine
|
|
|
|
Return Value:
|
|
WdfDevStatePowerStopped
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// Even though we failed power up and never really powered down, record our
|
|
// state as powered down, so that if we are restarted (can easily happen for
|
|
// a PDO), we have the correct previous state.
|
|
//
|
|
This->PowerSetDevicePowerState(WdfPowerDeviceD3Final);
|
|
|
|
This->PowerSendPowerDownEvents(FxPowerDownTypeImplicit);
|
|
|
|
return WdfDevStatePowerStopped;
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerReportPowerDownFailed(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Posts PowerDownFailed event to the PnP state machine and then waits for death.
|
|
The pended Power IRP is also completed.
|
|
|
|
Arguments:
|
|
This - The instance of the state machine
|
|
|
|
Return Value:
|
|
WdfDevStatePowerNull
|
|
|
|
--*/
|
|
{
|
|
|
|
|
|
|
|
|
|
This->PowerReleasePendingDeviceIrp();
|
|
This->PowerSendPowerDownFailureEvent(FxPowerDownTypeExplicit);
|
|
|
|
return WdfDevStatePowerNull;
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerInitialConnectInterruptFailed(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
When bringing the device out of the D3 final state, connecting or enabling
|
|
the interrupts failed. Disconnect and disable any interrupts which are
|
|
connected and possibly enabled.
|
|
|
|
Arguments:
|
|
This - instance of the state machine
|
|
|
|
Return Value:
|
|
WdfDevStatePowerInitialPowerUpFailedDerefParent
|
|
|
|
--*/
|
|
{
|
|
This->PowerConnectInterruptFailed();
|
|
|
|
return WdfDevStatePowerInitialPowerUpFailedDerefParent;
|
|
}
|
|
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerInitialDmaEnableFailed(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Initial power up of the device failed while enabling DMA. Disable any
|
|
started DMA enablers and proceed down the initial power up failure path.
|
|
|
|
Arguments:
|
|
This - instance of the state machine
|
|
|
|
Return Value:
|
|
WdfDevStatePowerInitialConnectInterruptFailed
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
|
|
(void) This->PowerDmaPowerDown();
|
|
|
|
status = This->m_DeviceD0ExitPreInterruptsDisabled.Invoke(
|
|
This->m_Device->GetHandle(),
|
|
WdfPowerDeviceD3Final
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
//
|
|
// Report the error, but continue forward
|
|
//
|
|
DoTraceLevelMessage(
|
|
This->GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
"EvtDeviceD0ExitPreInterruptsDisabled WDFDEVICE 0x%p !devobj 0x%p "
|
|
"new state %!WDF_POWER_DEVICE_STATE! failed, %!STATUS!",
|
|
This->m_Device->GetHandle(),
|
|
This->m_Device->GetDeviceObject(),
|
|
WdfPowerDeviceD3Final, status);
|
|
}
|
|
|
|
return WdfDevStatePowerInitialConnectInterruptFailed;
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerInitialSelfManagedIoFailed(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
The self managed io start failed when bringing the device out of Dx with an
|
|
implicit D0 transition. Hold the power queues that were previous open can
|
|
continue to cleanup
|
|
|
|
Arguments:
|
|
This - instance of the state machine
|
|
|
|
Return Value:
|
|
WdfDevStatePowerInitialDmaEnableFailed
|
|
|
|
--*/
|
|
{
|
|
|
|
|
|
|
|
|
|
This->m_Device->m_PkgIo->StopProcessingForPower(FxIoStopProcessingForPowerHold);
|
|
|
|
return WdfDevStatePowerInitialDmaEnableFailed;
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerInitialPowerUpFailedDerefParent(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Dereferences the parent's power ref count we took during start up
|
|
|
|
Arguments:
|
|
This - The instance of the state machine
|
|
|
|
Return Value:
|
|
WdfDevStatePowerInitialPowerUpFailed
|
|
|
|
--*/
|
|
{
|
|
|
|
//
|
|
// If this is a child, release the power reference on the parent
|
|
//
|
|
This->PowerParentPowerDereference();
|
|
|
|
return WdfDevStatePowerInitialPowerUpFailed;
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerInitialPowerUpFailed(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Posts PowerUpFailed event to the PnP state machine and then waits for death.
|
|
|
|
Arguments:
|
|
This - The instance of the state machine
|
|
|
|
Return Value:
|
|
WdfDevStatePowerStopped
|
|
|
|
--*/
|
|
{
|
|
This->PowerSendPowerUpFailureEvent();
|
|
|
|
return WdfDevStatePowerStopped;
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerDxStoppedDisarmWake(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Disarms the device from wake because the device is being stopped/removed
|
|
while in Dx.
|
|
|
|
Arguments:
|
|
This - instance of the state machine
|
|
|
|
Return Value:
|
|
WdfDevStatePowerDxStopped
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// We should only get into this state when this devobj is not a PDO and a
|
|
// power policy owner.
|
|
//
|
|
ASSERT((This->m_Device->IsPdo() && This->IsPowerPolicyOwner()) == FALSE);
|
|
|
|
This->PowerDisableWakeAtBusOverload();
|
|
|
|
return WdfDevStatePowerGotoDxStoppedDisableInterrupt;
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerDxStoppedDisarmWakeNP(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Disarms the device from wake because the device is being stopped/removed
|
|
while in Dx.
|
|
|
|
Arguments:
|
|
This - instance of the state machine
|
|
|
|
Return Value:
|
|
WdfDevStatePowerGotoDxStoppedDisableInterruptNP
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// We should only get into this state when this devobj is not a PDO and a
|
|
// power policy owner.
|
|
//
|
|
ASSERT((This->m_Device->IsPdo() && This->IsPowerPolicyOwner()) == FALSE);
|
|
|
|
This->PowerDisableWakeAtBusOverload();
|
|
|
|
return WdfDevStatePowerGotoDxStoppedDisableInterruptNP;
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerGotoDxStoppedDisableInterruptNP(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Device is in Dx and was implicitly powered down. Disable the interrupt since
|
|
the interrupt was not disconnected in the NP power down path.
|
|
|
|
Arguments:
|
|
This - instance of the state machine
|
|
|
|
Return Value:
|
|
WdfDevStatePowerGotoDxStopped
|
|
|
|
--*/
|
|
{
|
|
This->NotifyResourceObjectsDx(NotifyResourcesSurpriseRemoved);
|
|
|
|
return WdfDevStatePowerGotoDxStopped;
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerGotoDxStopped(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Device is in Dx and being removed/stopped. Inform the other state machines
|
|
that the device is powered down so that they may continue.
|
|
|
|
Arguments:
|
|
This - instance of the state machine
|
|
|
|
Return Value:
|
|
WdfDevStatePowerStopped
|
|
|
|
--*/
|
|
{
|
|
This->PowerSendPowerDownEvents(FxPowerDownTypeImplicit);
|
|
|
|
return WdfDevStatePowerStopped;
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerGotoStopped(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
We are in the Dx state and the power policy owner sent an explicit D0 irp
|
|
to the stack. Transition to the stopped state which will transition to the
|
|
D0 state when receiving an implicit D0.
|
|
|
|
Arguments:
|
|
This - instance of the state machine
|
|
|
|
Return Value:
|
|
WdfDevStatePowerStopped
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// We should only get into this state when this devobj is not the power
|
|
// policy owner.
|
|
//
|
|
ASSERT(This->IsPowerPolicyOwner() == FALSE);
|
|
|
|
This->PowerReleasePendingDeviceIrp();
|
|
return WdfDevStatePowerStopped;
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerStoppedCompleteDx(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
We were in the Stopped state and the power policy owner sent an explicit
|
|
Dx request to the stack. We will move to the DxStopped state where we will
|
|
transition to a Dx state when an implicit D0 is received.
|
|
|
|
Arguments:
|
|
This - instance of the state machine
|
|
|
|
Return Value:
|
|
WdfDevStatePowerDxStopped
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// We should only get into this state when this devobj is not the power
|
|
// policy owner.
|
|
//
|
|
ASSERT(This->IsPowerPolicyOwner() == FALSE);
|
|
|
|
This->PowerReleasePendingDeviceIrp();
|
|
return WdfDevStatePowerDxStopped;
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerDxStoppedDecideDxState(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
We are implicitly powering up the stack while in the Dx state. Decide
|
|
which Dx "holding" state to transition to.
|
|
|
|
Arguments:
|
|
This - instance of the state machine
|
|
|
|
Return Value:
|
|
new power state
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// We should only get into this state when this devobj is not the power
|
|
// policy owner.
|
|
//
|
|
ASSERT(This->IsPowerPolicyOwner() == FALSE);
|
|
|
|
//
|
|
// Move power policy back into a working state
|
|
//
|
|
// While it seems odd to send a power up in a Dx state, the power up is
|
|
// really an indication to the power policy state machine that the implicit
|
|
// D0 has been successfully processed, basically, it is a status event more
|
|
// then an indication of true power state.
|
|
//
|
|
This->PowerSendPowerUpEvents();
|
|
|
|
if (This->m_Device->GetDeviceObjectFlags() & DO_POWER_PAGABLE) {
|
|
if (This->PowerIsWakeRequestPresent()) {
|
|
COVERAGE_TRAP();
|
|
return WdfDevStatePowerDxStoppedArmForWake;
|
|
}
|
|
else {
|
|
return WdfDevStatePowerDx;
|
|
}
|
|
}
|
|
else {
|
|
if (This->PowerIsWakeRequestPresent()) {
|
|
COVERAGE_TRAP();
|
|
return WdfDevStatePowerDxStoppedArmForWakeNP;
|
|
}
|
|
else {
|
|
COVERAGE_TRAP();
|
|
return WdfDevStatePowerDxNP;
|
|
}
|
|
}
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerDxStoppedArmForWake(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
We are implicitly powering the stack back up while the device is in Dx
|
|
and there is a wait wake request presnt on this device. Enable wake at
|
|
the bus level and then move to the appropriate state based on the enabling
|
|
status.
|
|
|
|
Arguments:
|
|
This - instance of the state machine
|
|
|
|
Return Value:
|
|
new power state
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
|
|
COVERAGE_TRAP();
|
|
|
|
//
|
|
// We should only get into this state when this devobj is not a PDO and a
|
|
// power policy owner.
|
|
//
|
|
ASSERT((This->m_Device->IsPdo() && This->IsPowerPolicyOwner()) == FALSE);
|
|
|
|
status = This->PowerEnableWakeAtBusOverload();
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
//
|
|
// No matter of the irp status (canceled, pending, completed), we always
|
|
// transition to the D0ArmedForWake state because that is where we
|
|
// we handle the change in the irp's status.
|
|
//
|
|
COVERAGE_TRAP();
|
|
return WdfDevStatePowerDxArmedForWake;
|
|
}
|
|
else {
|
|
COVERAGE_TRAP();
|
|
This->PowerCompleteWakeRequestFromWithinMachine(status);
|
|
return WdfDevStatePowerDx;
|
|
}
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerDxStoppedArmForWakeNP(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
We are implicitly powering the stack back up while the device is in Dx
|
|
and there is a wait wake request presnt on this device. Enable wake at
|
|
the bus level and then move to the appropriate state based on the enabling
|
|
status.
|
|
|
|
Arguments:
|
|
This - instance of the state machine
|
|
|
|
Return Value:
|
|
new power state
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
|
|
COVERAGE_TRAP();
|
|
|
|
//
|
|
// We should only get into this state when this devobj is not a PDO and a
|
|
// power policy owner.
|
|
//
|
|
ASSERT((This->m_Device->IsPdo() && This->IsPowerPolicyOwner()) == FALSE);
|
|
|
|
status = This->PowerEnableWakeAtBusOverload();
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
//
|
|
// No matter of the irp status (canceled, pending, completed), we always
|
|
// transition to the D0ArmedForWake state because that is where we
|
|
// we handle the change in the irp's status.
|
|
//
|
|
COVERAGE_TRAP();
|
|
return WdfDevStatePowerDxArmedForWakeNP;
|
|
}
|
|
else {
|
|
COVERAGE_TRAP();
|
|
This->PowerCompleteWakeRequestFromWithinMachine(status);
|
|
return WdfDevStatePowerDxNP;
|
|
}
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerFinalPowerDownFailed(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Posts PowerDownFailed event to the PnP state machine and then waits for death.
|
|
|
|
Arguments:
|
|
This - The instance of the state machine
|
|
|
|
Return Value:
|
|
WdfDevStatePowerStopped
|
|
|
|
--*/
|
|
{
|
|
This->PowerSendPowerDownFailureEvent(FxPowerDownTypeImplicit);
|
|
|
|
//
|
|
// If we are not the PPO for the stack we could receive a power irp
|
|
// during the middle of an implicit power down so we cannot assume
|
|
// that there will be no pended power irp during an implicit power down.
|
|
//
|
|
ASSERT(This->IsPowerPolicyOwner() ? This->m_PendingDevicePowerIrp == NULL : TRUE);
|
|
|
|
return WdfDevStatePowerStopped;
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerUpFailedDerefParent(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Disconnects interrupts
|
|
|
|
Arguments:
|
|
This - The instance of the state machine
|
|
|
|
Return Value:
|
|
WdfDevStatePowerReportPowerUpFailed
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// Notify the interrupt state machines that the power up failed
|
|
//
|
|
This->SendEventToAllWakeInterrupts(WakeInterruptEventD0EntryFailed);
|
|
//
|
|
// NotifyResourceObjectsDx will log any errors
|
|
//
|
|
(void) This->NotifyResourceObjectsDx(NotifyResourcesForceDisconnect |
|
|
NotifyResourcesDisconnectInactive);
|
|
|
|
return WdfDevStatePowerReportPowerUpFailedDerefParent;
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerUpFailed(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Disconnects interrupts
|
|
|
|
Arguments:
|
|
This - The instance of the state machine
|
|
|
|
Return Value:
|
|
WdfDevStatePowerReportPowerUpFailed
|
|
|
|
--*/
|
|
{
|
|
COVERAGE_TRAP();
|
|
//
|
|
// Notify the interrupt state machines that the power up failed
|
|
//
|
|
This->SendEventToAllWakeInterrupts(WakeInterruptEventD0EntryFailed);
|
|
//
|
|
// NotifyResourceObjectsDx will log any errors
|
|
//
|
|
(void) This->NotifyResourceObjectsDx(NotifyResourcesForceDisconnect |
|
|
NotifyResourcesDisconnectInactive);
|
|
|
|
return WdfDevStatePowerReportPowerUpFailed;
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerGotoDxFailed(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Disconnects interrupts
|
|
|
|
Arguments:
|
|
This - The instance of the state machine
|
|
|
|
Return Value:
|
|
WdfDevStatePowerReportPowerDownFailed
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// NotifyResourceObjectsDx will log any errors
|
|
//
|
|
(void) This->NotifyResourceObjectsDx(NotifyResourcesForceDisconnect |
|
|
NotifyResourcesDisconnectInactive);
|
|
|
|
return WdfDevStatePowerReportPowerDownFailed;
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerGotoDxStoppedDisableInterrupt(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Disconnects interrupts
|
|
|
|
Arguments:
|
|
This - The instance of the state machine
|
|
|
|
Return Value:
|
|
WdfDevStatePowerGotoDxStopped
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// NotifyResourceObjectsDx will log any errors
|
|
//
|
|
(void) This->NotifyResourceObjectsDx(NotifyResourcesForceDisconnect |
|
|
NotifyResourcesDisconnectInactive);
|
|
|
|
return WdfDevStatePowerGotoDxStopped;
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerUpFailedDerefParentNP(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Disconnects interrupts
|
|
|
|
Arguments:
|
|
This - The instance of the state machine
|
|
|
|
Return Value:
|
|
WdfDevStatePowerReportPowerUpFailed
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// Notify the interrupt state machines that the power up failed
|
|
//
|
|
This->SendEventToAllWakeInterrupts(WakeInterruptEventD0EntryFailed);
|
|
This->DisconnectInterruptNP();
|
|
|
|
return WdfDevStatePowerReportPowerUpFailedDerefParent;
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerUpFailedNP(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Disconnects interrupts
|
|
|
|
Arguments:
|
|
This - The instance of the state machine
|
|
|
|
Return Value:
|
|
WdfDevStatePowerReportPowerUpFailed
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// Notify the interrupt state machines that the power up failed
|
|
//
|
|
This->SendEventToAllWakeInterrupts(WakeInterruptEventD0EntryFailed);
|
|
This->DisconnectInterruptNP();
|
|
|
|
return WdfDevStatePowerReportPowerUpFailed;
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerNotifyingD0EntryToWakeInterrupts(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Notifies the wake interrupt state machines that the device has entered
|
|
D0
|
|
|
|
Arguments:
|
|
This - The instance of the state machine
|
|
|
|
Return Value:
|
|
WdfDevStatePowerWakingConnectInterrupt if there are no wake interrupts
|
|
or WdfDevStatePowerNull otherwise
|
|
|
|
--*/
|
|
{
|
|
if (This->m_WakeInterruptCount == 0) {
|
|
return WdfDevStatePowerWakingConnectInterrupt;
|
|
}
|
|
|
|
This->SendEventToAllWakeInterrupts(WakeInterruptEventEnteringD0);
|
|
|
|
return WdfDevStatePowerNull;;
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerNotifyingD0ExitToWakeInterrupts(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Notifies the wake interrupt state machines that the device is about
|
|
to exit D0
|
|
|
|
Arguments:
|
|
This - The instance of the state machine
|
|
|
|
Return Value:
|
|
WdfDevStatePowerDx if there are no wake interrupts or WdfDevStatePowerNull
|
|
otherwise
|
|
|
|
--*/
|
|
{
|
|
if (This->m_WakeInterruptCount == 0) {
|
|
return WdfDevStatePowerGotoDxIoStopped;
|
|
}
|
|
|
|
//
|
|
// Indiciate to the wake interrupt state machine that the device is
|
|
// leaving D0 and also whether the device is armed for wake. The wake
|
|
// interrupt machine treats these differently as described below.
|
|
//
|
|
if (This->m_WakeInterruptsKeepConnected == TRUE ||
|
|
This->m_SharedPower.m_WaitWakeIrp != NULL) {
|
|
This->SendEventToAllWakeInterrupts(WakeInterruptEventLeavingD0);
|
|
}
|
|
else {
|
|
//
|
|
// When a wake interrupt is not armed for wake it will be disconnected
|
|
// by the power state machine once the wake interrupt state machine
|
|
// acknowledges the transition. If the interrupt fires between
|
|
// the time this event is posted and it is disconnected, it needs to be
|
|
// delivered to the driver or a deadlock could occur between PO state machine
|
|
// trying to disconnect the interrupt and the wake interrupt machine
|
|
// holding on to the ISR waiting for the device to return to D0 before
|
|
// delivering the interrupt.
|
|
//
|
|
This->SendEventToAllWakeInterrupts(WakeInterruptEventLeavingD0NotArmedForWake);
|
|
}
|
|
|
|
return WdfDevStatePowerNull;;
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerNotifyingD0EntryToWakeInterruptsNP(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Notifies the wake interrupt state machines that the device has entered
|
|
D0
|
|
|
|
Arguments:
|
|
This - The instance of the state machine
|
|
|
|
Return Value:
|
|
WdfDevStatePowerWakingConnectInterruptNP if there are no wake interrupts
|
|
or WdfDevStatePowerNull otherwise
|
|
|
|
--*/
|
|
{
|
|
if (This->m_WakeInterruptCount == 0) {
|
|
return WdfDevStatePowerWakingConnectInterruptNP;
|
|
}
|
|
|
|
This->SendEventToAllWakeInterrupts(WakeInterruptEventEnteringD0);
|
|
|
|
return WdfDevStatePowerNull;;
|
|
}
|
|
|
|
WDF_DEVICE_POWER_STATE
|
|
FxPkgPnp::PowerNotifyingD0ExitToWakeInterruptsNP(
|
|
__inout FxPkgPnp* This
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Notifies the wake interrupt state machines that the device is about
|
|
to exit D0
|
|
|
|
Arguments:
|
|
This - The instance of the state machine
|
|
|
|
Return Value:
|
|
WdfDevStatePowerDxNP if there are no wake interrupts or WdfDevStatePowerNull
|
|
otherwise
|
|
|
|
--*/
|
|
{
|
|
if (This->m_WakeInterruptCount == 0) {
|
|
return WdfDevStatePowerGotoDxIoStoppedNP;
|
|
}
|
|
|
|
//
|
|
// Indiciate to the wake interrupt state machine that the device is
|
|
// leaving D0 and also whether the device is armed for wake. The wake
|
|
// interrupt machine treats these differently as described below
|
|
//
|
|
if (This->m_WakeInterruptsKeepConnected == TRUE ||
|
|
This->m_SharedPower.m_WaitWakeIrp != NULL) {
|
|
This->SendEventToAllWakeInterrupts(WakeInterruptEventLeavingD0);
|
|
}
|
|
else {
|
|
//
|
|
// When a wake interrupt is not armed for wake it will be disconnected by
|
|
// the power state machine once the wake interrupt state machine
|
|
// acknowledges the transition. If the interrupt fires between
|
|
// the time this event is posted and it is disconnected, it needs to be
|
|
// delivered to the driver or a deadlock could occur between PO state machine
|
|
// trying to disconnect the interrupt and the wake interrupt machine holding on
|
|
// to the ISR waiting for the device to return to D0 before delivering the
|
|
// interrupt.
|
|
//
|
|
This->SendEventToAllWakeInterrupts(WakeInterruptEventLeavingD0NotArmedForWake);
|
|
}
|
|
|
|
return WdfDevStatePowerNull;;
|
|
}
|
|
|
|
VOID
|
|
FxPkgPnp::DisconnectInterruptNP(
|
|
VOID
|
|
)
|
|
{
|
|
//
|
|
// NotifyResourceObjectsDx will call IoDisconnectInterrupt. Since we
|
|
// are in the NP path, this may cause a deadlock between this thread and
|
|
// paging I/O. Log something to the IFR so that if the watchdog timer kicks
|
|
// in, at least we have context as to why we died.
|
|
//
|
|
DoTraceLevelMessage(
|
|
GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
"Force disconnecting interrupts on !devobj 0x%p, WDFDEVICE %p",
|
|
m_Device->GetDeviceObject(),
|
|
m_Device->GetHandle());
|
|
|
|
//
|
|
// NotifyResourceObjectsDx will log any errors
|
|
//
|
|
(void) NotifyResourceObjectsDx(NotifyResourcesForceDisconnect);
|
|
}
|
|
|
|
BOOLEAN
|
|
FxPkgPnp::PowerIndicateWaitWakeStatus(
|
|
__in NTSTATUS WaitWakeStatus
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
If there is a pended wait wake request, this routine will remove the cancel
|
|
routine and post the appropriate event to the power state machine. The
|
|
consumer of this event will do the actual completion of the wait wake
|
|
request.
|
|
|
|
The difference between this routine and
|
|
PowerCompleteWakeRequestFromWithinMachine is that
|
|
PowerCompleteWakeRequestFromWithinMachine is a private API to the state
|
|
machine and will attempt to complete the request immediately instead of
|
|
deferring the completion through the posting of a power state machine event.
|
|
|
|
PowerCompletePendedWakeIrp is used within the state machine to complete the
|
|
IRP deferred by this routine.
|
|
|
|
Arguments:
|
|
WaitWakeStatus - The final status of the wait wake request
|
|
|
|
Return Value:
|
|
TRUE if there there was a request to cancel
|
|
|
|
--*/
|
|
{
|
|
if (PowerMakeWakeRequestNonCancelable(WaitWakeStatus)) {
|
|
//
|
|
// The power machine will eventually call PowerCompletePendedWakeIrp
|
|
// to complete the request.
|
|
//
|
|
if (WaitWakeStatus == STATUS_CANCELLED) {
|
|
PowerProcessEvent(PowerWakeCanceled);
|
|
}
|
|
else if (NT_SUCCESS(WaitWakeStatus)) {
|
|
PowerProcessEvent(PowerWakeSucceeded);
|
|
}
|
|
else {
|
|
PowerProcessEvent(PowerWakeFailed);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
VOID
|
|
FxPkgPnp::PowerCompletePendedWakeIrp(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Completes the wait wake request that was pended by the power state machine.
|
|
It is valid if there is no request to complete (the only time there will be
|
|
a request to complete is when this power state machine is the wait wake owner
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Return Value:
|
|
None
|
|
|
|
--*/
|
|
{
|
|
PLIST_ENTRY ple;
|
|
KIRQL irql;
|
|
|
|
if (m_SharedPower.m_WaitWakeOwner == FALSE) {
|
|
COVERAGE_TRAP();
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Pop an irp off of the list
|
|
//
|
|
m_PowerMachine.m_WaitWakeLock.Acquire(&irql);
|
|
ASSERT(IsListEmpty(&m_PowerMachine.m_WaitWakeIrpToBeProcessedList) == FALSE);
|
|
ple = RemoveHeadList(&m_PowerMachine.m_WaitWakeIrpToBeProcessedList);
|
|
m_PowerMachine.m_WaitWakeLock.Release(irql);
|
|
|
|
InitializeListHead(ple);
|
|
|
|
FxIrp irp(FxIrp::GetIrpFromListEntry(ple));
|
|
|
|
CompletePowerRequest(&irp, irp.GetStatus());
|
|
}
|
|
|
|
VOID
|
|
FxPkgPnp::PowerCompleteWakeRequestFromWithinMachine(
|
|
__in NTSTATUS WaitWakeStatus
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Completes a wait wake from within the power state machine. Contrary to
|
|
PowerIndicateWaitWakeStatus which posts an event to the state machine to
|
|
process the irp's status change, this routine attempts to complete the
|
|
irp immediately.
|
|
|
|
Arguments:
|
|
Status - Final status of the wake irp
|
|
|
|
Return Value:
|
|
None
|
|
|
|
--*/
|
|
{
|
|
if (PowerMakeWakeRequestNonCancelable(WaitWakeStatus)) {
|
|
PowerCompletePendedWakeIrp();
|
|
}
|
|
}
|
|
|
|
BOOLEAN
|
|
FxPkgPnp::PowerMakeWakeRequestNonCancelable(
|
|
__in NTSTATUS WaitWakeStatus
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Attempts to clear the cancel routine from the pended wake request. If
|
|
successful, it will put the wake request on a pending wake list to be
|
|
completed later by PowerCompletePendedWakeIrp .
|
|
|
|
Arguments:
|
|
WaitWakeStatus - final status for the wake request
|
|
|
|
Return Value:
|
|
TRUE if there was a request and we cleared the cancel routine, FALSE
|
|
otherwise
|
|
|
|
--*/
|
|
{
|
|
KIRQL irql;
|
|
BOOLEAN result;
|
|
|
|
//
|
|
// Currently we assume that if we are the bus wait wake owner and that only
|
|
// PDOs can be bus wake owners, so we must have a parent device.
|
|
//
|
|
ASSERT(m_SharedPower.m_WaitWakeOwner &&
|
|
(m_Device->m_ParentDevice != NULL));
|
|
|
|
result = FALSE;
|
|
|
|
//
|
|
// Attempt to retrieve the wait wake irp. We can safely dereference the
|
|
// PIRP in while holding the lock as long as it is not NULL.
|
|
//
|
|
m_PowerMachine.m_WaitWakeLock.Acquire(&irql);
|
|
|
|
if (m_SharedPower.m_WaitWakeIrp != NULL) {
|
|
MdCancelRoutine pOldCancelRoutine;
|
|
FxIrp wwIrp;
|
|
|
|
wwIrp.SetIrp(m_SharedPower.m_WaitWakeIrp);
|
|
|
|
pOldCancelRoutine = wwIrp.SetCancelRoutine(NULL);
|
|
|
|
if (pOldCancelRoutine != NULL) {
|
|
FxPkgPnp* pParentPkg;
|
|
|
|
pParentPkg = m_Device->m_ParentDevice->m_PkgPnp;
|
|
|
|
//
|
|
// Propagate the successful wake status from the parent to this
|
|
// child's WW IRP if the parent is the PPO for its stack.
|
|
//
|
|
if (NT_SUCCESS(WaitWakeStatus) &&
|
|
pParentPkg->IsPowerPolicyOwner() &&
|
|
pParentPkg->m_PowerPolicyMachine.m_Owner->m_SystemWakeSource) {
|
|
//
|
|
// The only way that m_SystemWakeSource can be TRUE is if
|
|
// FxLibraryGlobals.PoGetSystemWake != NULL and if it is not
|
|
// NULL, then FxLibraryGlobals.PoSetSystemWake cannot be NULL
|
|
// either.
|
|
|
|
|
|
|
|
|
|
|
|
FxPkgPnp::_PowerSetSystemWakeSource(&wwIrp);
|
|
|
|
//
|
|
// If this PDO is the PPO for its stack, then we must mark this
|
|
// device as the system wake source if we have any
|
|
// enumerated PDOs off of this PDO so that we can propagate the
|
|
// system wake source attribute to our children stacks.
|
|
// (For a FDO which is the PPO, we do this in the WW completion
|
|
// routine.)
|
|
//
|
|
if (IsPowerPolicyOwner()) {
|
|
DoTraceLevelMessage(
|
|
GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGPNP,
|
|
"WDFDEVICE 0x%p !devobj 0x%p WW !irp 0x%p is a source "
|
|
"of wake", m_Device->GetHandle(),
|
|
m_Device->GetDeviceObject(),
|
|
m_SharedPower.m_WaitWakeIrp);
|
|
|
|
m_PowerPolicyMachine.m_Owner->m_SystemWakeSource = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set the status for the irp when it is completed later
|
|
//
|
|
wwIrp.SetStatus(WaitWakeStatus);
|
|
|
|
//
|
|
// Queue the irp for completion later
|
|
//
|
|
InsertTailList(&m_PowerMachine.m_WaitWakeIrpToBeProcessedList,
|
|
wwIrp.ListEntry());
|
|
|
|
wwIrp.SetIrp(NULL);
|
|
m_SharedPower.m_WaitWakeIrp = NULL;
|
|
result = TRUE;
|
|
}
|
|
else {
|
|
//
|
|
// The irp is being canceled as we run here. As soon as the spin
|
|
// lock is dropped, the cancel routine will run (or continue if it
|
|
// is blocked on this lock). Do nothing here and let the cancel
|
|
// routine run its course.
|
|
//
|
|
ASSERT(wwIrp.IsCanceled());
|
|
DO_NOTHING();
|
|
}
|
|
}
|
|
m_PowerMachine.m_WaitWakeLock.Release(irql);
|
|
|
|
return result;
|
|
}
|
|
|
|
VOID
|
|
FxPkgPnp::PowerSendIdlePowerEvent(
|
|
__in FxPowerIdleEvents Event
|
|
)
|
|
{
|
|
if (IsPowerPolicyOwner()) {
|
|
m_PowerPolicyMachine.m_Owner->m_PowerIdleMachine.ProcessPowerEvent(Event);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
FxPkgPnp::PowerSendPowerDownEvents(
|
|
__in FxPowerDownType Type
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
The device has powered down, inform the power policy state machine
|
|
|
|
Arguments:
|
|
Type - the type of power down being performed
|
|
|
|
Return Value:
|
|
None
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// If this is an implicit power type, there is completion routine on the
|
|
// power irp and we must send these events in the power state machine
|
|
// regardless if we are the PPO or not.
|
|
//
|
|
if (Type == FxPowerDownTypeImplicit) {
|
|
PowerSendIdlePowerEvent(PowerIdleEventPowerDown);
|
|
|
|
//
|
|
// If we are the power policy owner, there is no need to distinguish
|
|
// between an implicit power down or an explicit power down since the
|
|
// PPO controls all power irps in the stack and a power policy stop
|
|
// (e.g. an implicit power down) will not be racing with a real power
|
|
// irp.
|
|
//
|
|
// The non PPO state machine needs to distinguish between the 2 types
|
|
// of power downs because both of them may occur simultaneously and we
|
|
// don't want to interpret the power down event for the real Dx irp with
|
|
// the power down event for the (final) implicit power down.
|
|
//
|
|
PowerPolicyProcessEvent(IsPowerPolicyOwner() ? PwrPolPowerDown
|
|
: PwrPolImplicitPowerDown);
|
|
return;
|
|
}
|
|
|
|
ASSERT(Type == FxPowerDownTypeExplicit);
|
|
|
|
//
|
|
// If we are the PPO, then we will send PwrPolPowerDown in the completion
|
|
// routine passed to PoRequestPowerIrp. If we are not the PPO, we must send
|
|
// the event now because we have no such completion routine.
|
|
//
|
|
if (IsPowerPolicyOwner()) {
|
|
//
|
|
// Transition the idle state machine to off immediately.
|
|
//
|
|
m_PowerPolicyMachine.m_Owner->m_PowerIdleMachine.ProcessPowerEvent(
|
|
PowerIdleEventPowerDown
|
|
);
|
|
}
|
|
else {
|
|
//
|
|
// If we are not the PPO, there is no idle state machine to send an
|
|
// event to and there is no Po request completion routine to send this
|
|
// event, so we send it now.
|
|
//
|
|
PowerPolicyProcessEvent(PwrPolPowerDown);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
FxPkgPnp::PowerSendPowerUpEvents(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Sends power up events to the pnp and power policy state machines
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Return Value:
|
|
None
|
|
|
|
--*/
|
|
{
|
|
PowerSendIdlePowerEvent(PowerIdleEventPowerUpComplete);
|
|
|
|
//
|
|
// This must be called *before* PowerPostParentToD0ToChildren so that we
|
|
// clear the child power up guard variable in the power policy state machine
|
|
// and then post an event which will unblock the child.
|
|
//
|
|
PowerPolicyProcessEvent(PwrPolPowerUp);
|
|
}
|
|
|
|
VOID
|
|
FxPkgPnp::PowerSendPowerDownFailureEvent(
|
|
__in FxPowerDownType Type
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Sends a power down failure event to the pnp state machine marks an internal
|
|
error.
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Return Value:
|
|
None
|
|
|
|
--*/
|
|
{
|
|
SetInternalFailure();
|
|
|
|
if (IsPowerPolicyOwner()) {
|
|
//
|
|
// If we are the PPO and this is an explicit power operation, then we
|
|
// will send PwrPolPowerDownFailed in the completion routine passed to
|
|
// PoRequestPowerIrp. Otherwise, if this is an implicit power operation
|
|
// that failed, we need to send the event now because there is no
|
|
// Po completion routine to send the event later.
|
|
//
|
|
if (Type == FxPowerDownTypeImplicit) {
|
|
//
|
|
// Since there is no power irp to complete, we must send the event
|
|
// now.
|
|
//
|
|
// We only send PwrPolImplicitPowerDownFailed if we are not the
|
|
// PPO, so we can't share code with the !PPO path.
|
|
//
|
|
PowerPolicyProcessEvent(PwrPolPowerDownFailed);
|
|
}
|
|
else {
|
|
ASSERT(Type == FxPowerDownTypeExplicit);
|
|
|
|
//
|
|
// Process the state change immediately in the idle state machine.
|
|
// We should do this only on an explicit power down since the PPO
|
|
// will have disabled the idle state machine before attempting an
|
|
// implicit power down.
|
|
//
|
|
m_PowerPolicyMachine.m_Owner->m_PowerIdleMachine.ProcessPowerEvent(
|
|
PowerIdleEventPowerDownFailed
|
|
);
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// We are not the PPO, so we must send the event now because we have no
|
|
// such completion routine. Decide which event to send to the
|
|
// !PPO state machine.
|
|
//
|
|
PowerPolicyProcessEvent(
|
|
Type == FxPowerDownTypeImplicit ? PwrPolImplicitPowerDownFailed
|
|
: PwrPolPowerDownFailed
|
|
);
|
|
|
|
//
|
|
// Send the pnp event last becuase if we are in the middle of a Dx
|
|
// transition during S0, there is no S irp to guard a pnp state change
|
|
// from occurring immediately after sending this event. If we sent it
|
|
// before sending the power policy message, power policy might not
|
|
// transition to the failed state by the time pnp does creating a
|
|
// mismatch.
|
|
//
|
|
if (FALSE == m_ReleaseHardwareAfterDescendantsOnFailure) {
|
|
PnpProcessEvent(PnpEventPowerDownFailed);
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID
|
|
FxPkgPnp::PowerSendPowerUpFailureEvent(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Sends a power up failure event to the pnp state machine marks an internal
|
|
error.
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Return Value:
|
|
None
|
|
|
|
--*/
|
|
{
|
|
SetInternalFailure();
|
|
PowerSendIdlePowerEvent(PowerIdleEventPowerUpFailed);
|
|
|
|
PowerPolicyProcessEvent(PwrPolPowerUpFailed);
|
|
|
|
if (FALSE == m_ReleaseHardwareAfterDescendantsOnFailure) {
|
|
PnpProcessEvent(PnpEventPowerUpFailed);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
FxPkgPnp::PowerSetDevicePowerState(
|
|
__in WDF_POWER_DEVICE_STATE State
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Stores the state in the object and notifies the system of the change.
|
|
|
|
Arguments:
|
|
State - new device state
|
|
|
|
Return Value:
|
|
VOID
|
|
|
|
--*/
|
|
{
|
|
POWER_STATE powerState;
|
|
|
|
//
|
|
// Remember our previous state
|
|
//
|
|
m_DevicePowerStateOld = m_DevicePowerState;
|
|
|
|
//
|
|
// Set our new state
|
|
//
|
|
ASSERT(State <= 0xFF);
|
|
m_DevicePowerState = (BYTE) State;
|
|
|
|
//
|
|
// Notify the system of the new power state.
|
|
//
|
|
switch (State) {
|
|
case WdfPowerDeviceD3Final:
|
|
case WdfPowerDevicePrepareForHibernation:
|
|
powerState.DeviceState = PowerDeviceD3;
|
|
break;
|
|
|
|
case WdfPowerDeviceD0:
|
|
m_SystemPowerAction = PowerActionNone;
|
|
__fallthrough;
|
|
|
|
default:
|
|
powerState.DeviceState = (DEVICE_POWER_STATE) State;
|
|
break;
|
|
}
|
|
|
|
MxDeviceObject deviceObject(m_Device->GetDeviceObject());
|
|
deviceObject.SetPowerState(
|
|
DevicePowerState,
|
|
powerState);
|
|
}
|
|
|
|
VOID
|
|
FxPkgPnp::PowerConnectInterruptFailed(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Worker routine for all the paths where we are trying to bring the device into
|
|
D0 and failed while trying to connect or enable an interrupt. This routine
|
|
disables and disconnects all interrupts, calls D0Exit, and sets the device
|
|
state.
|
|
|
|
Arguments:
|
|
This - instance of the state machine
|
|
|
|
Return Value:
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
|
|
status = NotifyResourceObjectsDx(NotifyResourcesForceDisconnect);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
//
|
|
// Report the error, but continue forward
|
|
//
|
|
DoTraceLevelMessage(
|
|
GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
"Interrupt(s) disconnect on WDFDEVICE %p failed, %!STATUS!",
|
|
m_Device->GetHandle(), status);
|
|
}
|
|
|
|
status = m_DeviceD0Exit.Invoke(m_Device->GetHandle(),
|
|
WdfPowerDeviceD3Final);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
//
|
|
// Report the error, but continue forward
|
|
//
|
|
DoTraceLevelMessage(
|
|
GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
"EvtDeviceD0Exit WDFDEVICE 0x%p !devobj 0x%p failed, %!STATUS!",
|
|
m_Device->GetHandle(),
|
|
m_Device->GetDeviceObject(), status);
|
|
}
|
|
|
|
PowerSetDevicePowerState(WdfPowerDeviceD3Final);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VOID
|
|
FxPkgPnp::_PowerWaitWakeCancelRoutine(
|
|
__in MdDeviceObject DeviceObject,
|
|
__in MdIrp Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Cancel routine for the pended wait wake irp. This will post an event to
|
|
the power state machine to process the cancellation under the machine's
|
|
lock and complete the irp later.
|
|
|
|
Arguments:
|
|
DeviceObject - Instance of this state machine
|
|
Irp - The wait wake request being canceled
|
|
|
|
Return Value:
|
|
None
|
|
|
|
--*/
|
|
{
|
|
CfxDevice* pDevice;
|
|
FxPkgPdo* pThis;
|
|
FxIrp irp(Irp);
|
|
KIRQL irql;
|
|
|
|
//
|
|
// Release the IO cancel spinlock because we use our own
|
|
//
|
|
Mx::ReleaseCancelSpinLock(irp.GetCancelIrql());
|
|
|
|
pDevice = FxDevice::GetFxDevice(DeviceObject);
|
|
pThis = pDevice->GetPdoPkg();
|
|
|
|
ASSERT(pThis->m_SharedPower.m_WaitWakeOwner);
|
|
|
|
//
|
|
// Clear out the IRQL and set our state to disarming
|
|
//
|
|
pThis->m_PowerMachine.m_WaitWakeLock.Acquire(&irql);
|
|
|
|
ASSERT(pThis->m_SharedPower.m_WaitWakeIrp == Irp &&
|
|
pThis->m_SharedPower.m_WaitWakeIrp != NULL);
|
|
|
|
InsertTailList(&pThis->m_PowerMachine.m_WaitWakeIrpToBeProcessedList,
|
|
irp.ListEntry());
|
|
|
|
//
|
|
// Set the status for the irp when it is completed later
|
|
//
|
|
irp.SetStatus(STATUS_CANCELLED);
|
|
|
|
pThis->m_SharedPower.m_WaitWakeIrp = NULL;
|
|
|
|
pThis->m_PowerMachine.m_WaitWakeLock.Release(irql);
|
|
|
|
pThis->PowerProcessEvent(PowerWakeCanceled);
|
|
}
|