mirror of
https://github.com/reactos/reactos.git
synced 2025-03-10 18:24:02 +00:00
579 lines
16 KiB
C++
579 lines
16 KiB
C++
/*++
|
|
Copyright (c) Microsoft. All rights reserved.
|
|
|
|
Module Name:
|
|
|
|
PoxInterface.cpp
|
|
|
|
Abstract:
|
|
|
|
This module implements the power-framework-related logic in WDF.
|
|
|
|
--*/
|
|
|
|
#include "pnppriv.hpp"
|
|
|
|
extern "C" {
|
|
#if defined(EVENT_TRACING)
|
|
#include "PoxInterface.tmh"
|
|
#endif
|
|
}
|
|
|
|
FxPoxInterface::FxPoxInterface(
|
|
__in FxPkgPnp* PkgPnp
|
|
)
|
|
{
|
|
m_PkgPnp = PkgPnp;
|
|
m_PoHandle = NULL;
|
|
m_DevicePowerRequired = TRUE;
|
|
m_DevicePowerRequirementMachine = NULL;
|
|
m_CurrentIdleTimeoutHint = 0;
|
|
m_NextIdleTimeoutHint = 0;
|
|
}
|
|
|
|
FxPoxInterface::~FxPoxInterface(
|
|
VOID
|
|
)
|
|
{
|
|
if (NULL != m_DevicePowerRequirementMachine) {
|
|
delete m_DevicePowerRequirementMachine;
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
FxPoxInterface::CreateDevicePowerRequirementMachine(
|
|
VOID
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
FxDevicePwrRequirementMachine * fxDprMachine = NULL;
|
|
|
|
ASSERT(NULL == m_DevicePowerRequirementMachine);
|
|
|
|
fxDprMachine = new (m_PkgPnp->GetDriverGlobals())
|
|
FxDevicePwrRequirementMachine(this);
|
|
if (NULL == fxDprMachine) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
DoTraceLevelMessage(
|
|
m_PkgPnp->GetDriverGlobals(),
|
|
TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
"WDFDEVICE 0x%p !devobj 0x%p failed to allocate "
|
|
"FxDevicePwrRequirementMachine. %!STATUS!.",
|
|
m_PkgPnp->GetDevice()->GetHandle(),
|
|
m_PkgPnp->GetDevice()->GetDeviceObject(),
|
|
status);
|
|
goto exit;
|
|
}
|
|
|
|
status = fxDprMachine->Initialize(m_PkgPnp->GetDriverGlobals());
|
|
if (FALSE == NT_SUCCESS(status)) {
|
|
DoTraceLevelMessage(
|
|
m_PkgPnp->GetDriverGlobals(),
|
|
TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
"WDFDEVICE 0x%p !devobj 0x%p Device Power Requirement State Machine"
|
|
" Initialize() failed, %!STATUS!",
|
|
m_PkgPnp->GetDevice()->GetHandle(),
|
|
m_PkgPnp->GetDevice()->GetDeviceObject(),
|
|
status);
|
|
goto exit;
|
|
}
|
|
|
|
status = fxDprMachine->Init(
|
|
m_PkgPnp,
|
|
FxDevicePwrRequirementMachine::_ProcessEventInner
|
|
);
|
|
if (!NT_SUCCESS(status)) {
|
|
DoTraceLevelMessage(
|
|
m_PkgPnp->GetDriverGlobals(),
|
|
TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
"WDFDEVICE 0x%p !devobj 0x%p Device Power Requirement State Machine"
|
|
" Init() failed, %!STATUS!",
|
|
m_PkgPnp->GetDevice()->GetHandle(),
|
|
m_PkgPnp->GetDevice()->GetDeviceObject(),
|
|
status);
|
|
goto exit;
|
|
}
|
|
|
|
m_DevicePowerRequirementMachine = fxDprMachine;
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
exit:
|
|
if (FALSE == NT_SUCCESS(status)) {
|
|
if (NULL != fxDprMachine) {
|
|
delete fxDprMachine;
|
|
}
|
|
}
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
FxPoxInterface::InitializeComponents(
|
|
VOID
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
PPOX_SETTINGS poxSettings = NULL;
|
|
WDFDEVICE fxDevice = NULL;
|
|
|
|
|
|
if (FALSE == m_PkgPnp->m_PowerPolicyMachine.m_Owner->m_IdleSettings.
|
|
m_TimeoutMgmt.UsingSystemManagedIdleTimeout()) {
|
|
//
|
|
// Driver-managed idle timeout. Nothing to do.
|
|
//
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// We create the device power requirement state machine only if system-
|
|
// managed idle timeout is being used.
|
|
//
|
|
if (NULL == m_DevicePowerRequirementMachine) {
|
|
status = CreateDevicePowerRequirementMachine();
|
|
if (FALSE == NT_SUCCESS(status)) {
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
ASSERT(NULL != m_DevicePowerRequirementMachine);
|
|
|
|
//
|
|
// Register with the power framework
|
|
//
|
|
status = PoxRegisterDevice();
|
|
|
|
if (FALSE == NT_SUCCESS(status)) {
|
|
DoTraceLevelMessage(
|
|
m_PkgPnp->GetDriverGlobals(),
|
|
TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
"WDFDEVICE 0x%p !devobj 0x%p FxPox::PoxRegisterDevice failed. "
|
|
"%!STATUS!.",
|
|
m_PkgPnp->GetDevice()->GetHandle(),
|
|
m_PkgPnp->GetDevice()->GetDeviceObject(),
|
|
status);
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// At the time of registration, all components are active. When we start the
|
|
// power framework's device power management (see below), all components are
|
|
// moved to the idle state by default. Take an extra reference on the
|
|
// component to prevent this from happening. The power policy state machine
|
|
// will evaluate the S0-idle policy later and ask us to drop this reference
|
|
// if the policy requires it.
|
|
//
|
|
PoxActivateComponent();
|
|
|
|
//
|
|
// Tell the power framework to start its device power management. This will
|
|
// drop a reference on the component, but the component will still remain
|
|
// active because of the extra reference we took above.
|
|
//
|
|
PoxStartDevicePowerManagement();
|
|
|
|
//
|
|
// If the client driver has specified power framework settings, retrieve
|
|
// them.
|
|
//
|
|
poxSettings = GetPowerFrameworkSettings();
|
|
|
|
//
|
|
// If the driver wanted to receive the POHANDLE, invoke their callback now
|
|
//
|
|
if ((NULL != poxSettings) &&
|
|
(NULL != poxSettings->EvtDeviceWdmPostPoFxRegisterDevice)) {
|
|
|
|
fxDevice = m_PkgPnp->GetDevice()->GetHandle();
|
|
|
|
status = poxSettings->EvtDeviceWdmPostPoFxRegisterDevice(
|
|
fxDevice,
|
|
m_PoHandle
|
|
);
|
|
if (FALSE == NT_SUCCESS(status)) {
|
|
|
|
DoTraceLevelMessage(
|
|
m_PkgPnp->GetDriverGlobals(),
|
|
TRACE_LEVEL_ERROR, TRACINGPNP,
|
|
"WDFDEVICE 0x%p !devobj 0x%p. The client driver has failed the "
|
|
"EvtDeviceWdmPostPoFxRegisterDevice callback with %!STATUS!.",
|
|
m_PkgPnp->GetDevice()->GetHandle(),
|
|
m_PkgPnp->GetDevice()->GetDeviceObject(),
|
|
status);
|
|
|
|
//
|
|
// Notify the driver that the POHANDLE is about to become invalid
|
|
//
|
|
if (NULL != poxSettings->EvtDeviceWdmPrePoFxUnregisterDevice) {
|
|
poxSettings->EvtDeviceWdmPrePoFxUnregisterDevice(
|
|
fxDevice,
|
|
m_PoHandle
|
|
);
|
|
}
|
|
|
|
//
|
|
// Unregister with the power framework
|
|
//
|
|
PoxUnregisterDevice();
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Tell the device power requirement state machine that we have registered
|
|
// with the power framework
|
|
//
|
|
m_DevicePowerRequirementMachine->ProcessEvent(DprEventRegisteredWithPox);
|
|
|
|
exit:
|
|
return status;
|
|
}
|
|
|
|
VOID
|
|
FxPoxInterface::UninitializeComponents(
|
|
VOID
|
|
)
|
|
{
|
|
PPOX_SETTINGS poxSettings = NULL;
|
|
|
|
if (FALSE == m_PkgPnp->m_PowerPolicyMachine.m_Owner->m_IdleSettings.
|
|
m_TimeoutMgmt.UsingSystemManagedIdleTimeout()) {
|
|
//
|
|
// Driver-managed idle timeout. Nothing to do.
|
|
//
|
|
return;
|
|
}
|
|
|
|
ASSERT(NULL != m_DevicePowerRequirementMachine);
|
|
|
|
//
|
|
// If the client driver has specified power framework settings, retrieve
|
|
// them.
|
|
//
|
|
poxSettings = GetPowerFrameworkSettings();
|
|
|
|
//
|
|
// Notify the client driver that the POHANDLE is about to become invalid
|
|
//
|
|
if ((NULL != poxSettings) &&
|
|
(NULL != poxSettings->EvtDeviceWdmPrePoFxUnregisterDevice)) {
|
|
|
|
poxSettings->EvtDeviceWdmPrePoFxUnregisterDevice(
|
|
m_PkgPnp->GetDevice()->GetHandle(),
|
|
m_PoHandle
|
|
);
|
|
}
|
|
|
|
//
|
|
// Unregister with the power framework
|
|
//
|
|
PoxUnregisterDevice();
|
|
|
|
//
|
|
// Tell the device power requirement state machine that we have unregistered
|
|
// with the power framework
|
|
//
|
|
m_DevicePowerRequirementMachine->ProcessEvent(
|
|
DprEventUnregisteredWithPox
|
|
);
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
FxPoxInterface::RequestComponentActive(
|
|
VOID
|
|
)
|
|
{
|
|
if (FALSE == m_PkgPnp->m_PowerPolicyMachine.m_Owner->m_IdleSettings.
|
|
m_TimeoutMgmt.UsingSystemManagedIdleTimeout()) {
|
|
//
|
|
// Driver-managed idle timeout. Nothing to do.
|
|
//
|
|
return;
|
|
}
|
|
|
|
PoxActivateComponent();
|
|
return;
|
|
}
|
|
|
|
BOOLEAN
|
|
FxPoxInterface::DeclareComponentIdle(
|
|
VOID
|
|
)
|
|
{
|
|
BOOLEAN canPowerDown;
|
|
|
|
if (FALSE == m_PkgPnp->m_PowerPolicyMachine.m_Owner->m_IdleSettings.
|
|
m_TimeoutMgmt.UsingSystemManagedIdleTimeout()) {
|
|
//
|
|
// Driver-managed idle timeout. We can power down immediately, without
|
|
// waiting for device-power-not-required notification.
|
|
//
|
|
canPowerDown = TRUE;
|
|
} else {
|
|
//
|
|
// System-managed idle timeout
|
|
//
|
|
PoxIdleComponent();
|
|
|
|
//
|
|
// We must wait for device-power-not-required notification before
|
|
// powering down.
|
|
//
|
|
canPowerDown = FALSE;
|
|
}
|
|
|
|
return canPowerDown;
|
|
}
|
|
|
|
VOID
|
|
FxPoxInterface::UpdateIdleTimeoutHint(
|
|
VOID
|
|
)
|
|
{
|
|
ULONGLONG idleTimeoutHint;
|
|
|
|
if (FALSE == m_PkgPnp->m_PowerPolicyMachine.m_Owner->m_IdleSettings.
|
|
m_TimeoutMgmt.UsingSystemManagedIdleTimeout()) {
|
|
//
|
|
// Driver-managed idle timeout. Nothing to do.
|
|
//
|
|
return;
|
|
}
|
|
|
|
if (m_NextIdleTimeoutHint != m_CurrentIdleTimeoutHint) {
|
|
m_CurrentIdleTimeoutHint = m_NextIdleTimeoutHint;
|
|
|
|
//
|
|
// Convert the idle timeout from milliseconds to 100-nanosecond units
|
|
//
|
|
idleTimeoutHint = ((ULONGLONG) m_CurrentIdleTimeoutHint) * 10 * 1000;
|
|
PoxSetDeviceIdleTimeout(idleTimeoutHint);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
FxPoxInterface::NotifyDevicePowerDown(
|
|
VOID
|
|
)
|
|
{
|
|
KIRQL irql;
|
|
BOOLEAN canPowerOff;
|
|
|
|
if (FALSE == m_PkgPnp->m_PowerPolicyMachine.m_Owner->m_IdleSettings.
|
|
m_TimeoutMgmt.UsingSystemManagedIdleTimeout()) {
|
|
//
|
|
// Driver-managed idle timeout. We don't have to take power framework's
|
|
// device power requirement into consideration. Just return success.
|
|
//
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Acquire the lock to ensure that device power requirement doesn't change.
|
|
//
|
|
m_DevicePowerRequiredLock.Acquire(&irql);
|
|
if (FALSE == m_DevicePowerRequired) {
|
|
//
|
|
// Send an event to the device power requirement state machine to tell
|
|
// it that we are about to go to Dx.
|
|
//
|
|
// We send the event inside a lock in order to handle the race condition
|
|
// when the power framework notifies us that device power is required at
|
|
// the same time that we are about to go to Dx. By sending the event
|
|
// inside the lock, we ensure that the DprEventDeviceGoingToDx event is
|
|
// always queued to device power requirement state machine before the
|
|
// DprEventPoxRequiresPower.
|
|
//
|
|
// This allows for a clean design in the device power requirement state
|
|
// machine by ensuring that it does not have to handle a non-intuitive
|
|
// sequence, i.e. DprEventPoxRequiresPower followed by
|
|
// DprEventDeviceGoingToDx. This sequence is non-intuitive because it
|
|
// doesn't make sense for a device to go to Dx after it has been
|
|
// informed that device power is required. Avoiding this non-intuitive
|
|
// sequence via locking enables a clean design for the device power
|
|
// requirement state machine.
|
|
//
|
|
m_DevicePowerRequirementMachine->ProcessEvent(DprEventDeviceGoingToDx);
|
|
canPowerOff = TRUE;
|
|
|
|
} else {
|
|
canPowerOff = FALSE;
|
|
}
|
|
m_DevicePowerRequiredLock.Release(irql);
|
|
|
|
return canPowerOff ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
VOID
|
|
FxPoxInterface::DeviceIsPoweredOn(
|
|
VOID
|
|
)
|
|
{
|
|
|
|
if (FALSE == m_PkgPnp->m_PowerPolicyMachine.m_Owner->m_IdleSettings.
|
|
m_TimeoutMgmt.UsingSystemManagedIdleTimeout()) {
|
|
//
|
|
// Driver-managed idle timeout. Nothing to do.
|
|
//
|
|
return;
|
|
}
|
|
|
|
//
|
|
// System-managed idle timeout. Notify the device power requirement state
|
|
// machine that we are back in D0.
|
|
//
|
|
m_DevicePowerRequirementMachine->ProcessEvent(
|
|
DprEventDeviceReturnedToD0
|
|
);
|
|
return;
|
|
}
|
|
|
|
PPOX_SETTINGS
|
|
FxPoxInterface::GetPowerFrameworkSettings(
|
|
VOID
|
|
)
|
|
{
|
|
PPOX_SETTINGS poxSettings = NULL;
|
|
|
|
if (m_PkgPnp->m_PowerPolicyMachine.m_Owner->
|
|
m_IdleSettings.m_TimeoutMgmt.DriverSpecifiedPowerFrameworkSettings()) {
|
|
|
|
poxSettings = m_PkgPnp->m_PowerPolicyMachine.m_Owner->
|
|
m_IdleSettings.m_TimeoutMgmt.GetPowerFrameworkSettings();
|
|
|
|
ASSERT(NULL != poxSettings);
|
|
}
|
|
|
|
return poxSettings;
|
|
}
|
|
|
|
VOID
|
|
FxPoxInterface::DprProcessEventFromPoxCallback(
|
|
__in FxDevicePwrRequirementEvents Event
|
|
)
|
|
{
|
|
KIRQL irql;
|
|
|
|
//
|
|
// We should not run the state machine from within a power framework
|
|
// callback because we might end up reaching a state where we unregister
|
|
// with the power framework. Unregistering from a callback leads to a
|
|
// deadlock. Therefore, we raise IRQL before queueing an event to the state
|
|
// machine. Raising IRQL causes the event processing to be deferred to a
|
|
// worker thread.
|
|
//
|
|
|
|
//
|
|
// This path should only be invoked for kernel mode. For user mode, this
|
|
// condition is avoided by reflector guranteeing that it queues a worker
|
|
// item to send a Pofx event corresponding to any PoFx callback
|
|
//
|
|
ASSERT(FX_IS_KERNEL_MODE);
|
|
|
|
Mx::MxRaiseIrql(DISPATCH_LEVEL, &irql);
|
|
m_DevicePowerRequirementMachine->ProcessEvent(Event);
|
|
Mx::MxLowerIrql(irql);
|
|
}
|
|
|
|
VOID
|
|
FxPoxInterface::SimulateDevicePowerRequired(
|
|
VOID
|
|
)
|
|
{
|
|
|
|
if (FALSE == m_PkgPnp->m_PowerPolicyMachine.m_Owner->m_IdleSettings.
|
|
m_TimeoutMgmt.UsingSystemManagedIdleTimeout()) {
|
|
//
|
|
// Driver-managed idle timeout. Nothing to do.
|
|
//
|
|
return;
|
|
}
|
|
|
|
//
|
|
// System-managed idle timeout. Notify the device power requirement state
|
|
// machine that device power is required.
|
|
//
|
|
PowerRequiredCallbackWorker(FALSE /* InvokedFromPoxCallback */);
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
FxPoxInterface::SimulateDevicePowerNotRequired(
|
|
VOID
|
|
)
|
|
{
|
|
|
|
if (FALSE == m_PkgPnp->m_PowerPolicyMachine.m_Owner->m_IdleSettings.
|
|
m_TimeoutMgmt.UsingSystemManagedIdleTimeout()) {
|
|
//
|
|
// Driver-managed idle timeout. Nothing to do.
|
|
//
|
|
return;
|
|
}
|
|
|
|
//
|
|
// System-managed idle timeout. Notify the device power requirement state
|
|
// machine that device power is not required.
|
|
//
|
|
PowerNotRequiredCallbackWorker(FALSE /* InvokedFromPoxCallback */);
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
FxPoxInterface::PowerRequiredCallbackWorker(
|
|
__in BOOLEAN InvokedFromPoxCallback
|
|
)
|
|
{
|
|
KIRQL irql;
|
|
|
|
//
|
|
// Make a note of the fact that device power is required
|
|
//
|
|
m_DevicePowerRequiredLock.Acquire(&irql);
|
|
m_DevicePowerRequired = TRUE;
|
|
m_DevicePowerRequiredLock.Release(irql);
|
|
|
|
//
|
|
// Send the device-power-required event to the device power requirement
|
|
// state machine.
|
|
//
|
|
if (InvokedFromPoxCallback) {
|
|
DprProcessEventFromPoxCallback(DprEventPoxRequiresPower);
|
|
} else {
|
|
m_DevicePowerRequirementMachine->ProcessEvent(DprEventPoxRequiresPower);
|
|
}
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
FxPoxInterface::PowerNotRequiredCallbackWorker(
|
|
__in BOOLEAN InvokedFromPoxCallback
|
|
)
|
|
{
|
|
KIRQL irql;
|
|
|
|
//
|
|
// Make a note of the fact that device power is not required
|
|
//
|
|
m_DevicePowerRequiredLock.Acquire(&irql);
|
|
m_DevicePowerRequired = FALSE;
|
|
m_DevicePowerRequiredLock.Release(irql);
|
|
|
|
//
|
|
// Send the device-power-not-required event to the device power
|
|
// requirement state machine.
|
|
//
|
|
if (InvokedFromPoxCallback) {
|
|
DprProcessEventFromPoxCallback(DprEventPoxDoesNotRequirePower);
|
|
} else {
|
|
m_DevicePowerRequirementMachine->ProcessEvent(
|
|
DprEventPoxDoesNotRequirePower
|
|
);
|
|
}
|
|
return;
|
|
}
|
|
|