reactos/sdk/lib/drivers/wdf/shared/irphandlers/pnp/poxinterface.cpp
Victor Perevertkin 8a978a179f
[WDF] Add Windows Driver Framework files
Takern from Microsoft GitHub repo:
d9c6040fe9

Licensed under MIT
2020-11-03 00:06:26 +03:00

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;
}