reactos/sdk/lib/drivers/wdf/shared/irphandlers/pnp/fdopower.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

586 lines
13 KiB
C++

/*++
Copyright (c) Microsoft Corporation
Module Name:
FdoPower.cpp
Abstract:
This module implements the pnp/power package for the driver
framework. This, specifically, is the power code.
Author:
Environment:
Both kernel and user mode
Revision History:
--*/
#include "pnppriv.hpp"
#if defined(EVENT_TRACING)
extern "C" {
#include "FdoPower.tmh"
}
#endif
_Must_inspect_result_
NTSTATUS
FxPkgFdo::_PowerPassDown(
__inout FxPkgPnp* This,
__in FxIrp *Irp
)
/*++
Routine Description:
This method is invoked when a Power Irp we don't handle comes into the
driver.
Arguemnts:
This - the package
Irp - a pointer to the FxIrp
Returns:
NTSTATUS
--*/
{
FxPkgFdo* pThis;
MdDeviceObject pDevObj;
NTSTATUS status;
MdIrp pIrp;
pIrp = Irp->GetIrp();
pThis = (FxPkgFdo*) This;
pDevObj = pThis->m_Device->GetDeviceObject();
//
// FDOs don't handle this IRP, so simply pass it down.
//
Irp->StartNextPowerIrp();
Irp->CopyCurrentIrpStackLocationToNext();
status = Irp->PoCallDriver(pThis->m_Device->GetAttachedDevice());
Mx::MxReleaseRemoveLock(pThis->m_Device->GetRemoveLock(),
pIrp);
return status;
}
_Must_inspect_result_
NTSTATUS
FxPkgFdo::_DispatchSetPower(
__inout FxPkgPnp* This,
__in FxIrp *Irp
)
/*++
Routine Description:
This method is invoked when a SetPower IRP enters the driver.
Arguemnts:
Device - a pointer to the FxDevice
Irp - a pointer to the FxIrp
Returns:
NTSTATUS
--*/
{
if (Irp->GetParameterPowerType() == SystemPowerState) {
return ((FxPkgFdo*) This)->DispatchSystemSetPower(Irp);
}
else {
return ((FxPkgFdo*) This)->DispatchDeviceSetPower(Irp);
}
}
_Must_inspect_result_
NTSTATUS
FxPkgFdo::_DispatchQueryPower(
__inout FxPkgPnp* This,
__in FxIrp *Irp
)
/*++
Routine Description:
This method is invoked when a QueryPower IRP enters the driver.
Arguemnts:
This - The package
Irp - a pointer to the FxIrp
Returns:
NTSTATUS
--*/
{
if (Irp->GetParameterPowerType() == SystemPowerState) {
return ((FxPkgFdo*) This)->DispatchSystemQueryPower(Irp);
}
else {
return ((FxPkgFdo*) This)->DispatchDeviceQueryPower(Irp);
}
}
_Must_inspect_result_
NTSTATUS
FxPkgFdo::_SystemPowerS0Completion(
__in MdDeviceObject DeviceObject,
__in MdIrp OriginalIrp,
__in PVOID Context
)
{
FxPkgPnp* pPkgPnp;
KIRQL irql;
FxIrp irp(OriginalIrp);
pPkgPnp = (FxPkgPnp*) Context;
//
// Ideally we would like to complete the S0 irp before we start
// processing the event in the state machine so that the D0 irp
// comes after the S0 is moving up the stack...
//
// ... BUT ...
//
// ... by allowing the S0 irp to go up the stack first, we must then
// handle pnp requests from the current power policy state (because
// the S0 irp could be the last S irp in the system and when completed,
// the pnp lock is released). So, we process the event first so
// that we can move into a state where we can handle pnp events in
// the power policy state machine.
//
// We mitigate the situation a little bit by forcing the processing of the
// event to occur on the power policy thread rather then in the current
// context.
//
Mx::MxRaiseIrql(DISPATCH_LEVEL, &irql);
pPkgPnp->PowerPolicyProcessEvent(PwrPolS0);
Mx::MxLowerIrql(irql);
irp.StartNextPowerIrp();
//
// Let the irp continue on its way
//
if (irp.PendingReturned()) {
irp.MarkIrpPending();
}
Mx::MxReleaseRemoveLock((&FxDevice::_GetFxWdmExtension(DeviceObject)->IoRemoveLock),
OriginalIrp);
return irp.GetStatus();
}
_Must_inspect_result_
NTSTATUS
FxPkgFdo::_SystemPowerSxCompletion(
__in MdDeviceObject DeviceObject,
__in MdIrp OriginalIrp,
__in PVOID Context
)
{
FxPkgFdo *pThis;
FxIrp irp(OriginalIrp);
UNREFERENCED_PARAMETER(DeviceObject);
pThis = (FxPkgFdo*) Context;
ASSERT(pThis->IsPowerPolicyOwner());
ASSERT(OriginalIrp == pThis->GetPendingSystemPowerIrp());
pThis->PowerPolicyProcessEvent(PwrPolSx);
//
// Power policy will complete the system irp
//
return STATUS_MORE_PROCESSING_REQUIRED;
}
_Must_inspect_result_
NTSTATUS
FxPkgFdo::DispatchSystemSetPower(
__in FxIrp *Irp
)
{
NTSTATUS status;
MxDeviceObject deviceObject(m_Device->GetDeviceObject());
m_SystemPowerState = (BYTE) Irp->GetParameterPowerStateSystemState();
deviceObject.SetPowerState(SystemPowerState,
Irp->GetParameterPowerState());
if (IsPowerPolicyOwner()) {
//
// If we are going to S0, we just notify the power policy state machine
// and then let the request go (per the fast resume spec). Otherwise,
// send the request down and on the way up, send the Dx request.
//
if (m_SystemPowerState == PowerSystemWorking) {
//
// Post the event into the state machine when the irp is going up
// the stack. See the comment in _SystemPowerS0Completion for more
// detail as to why.
//
Irp->CopyCurrentIrpStackLocationToNext();
Irp->SetCompletionRoutineEx(deviceObject.GetObject(),
_SystemPowerS0Completion,
this);
return Irp->PoCallDriver(m_Device->GetAttachedDevice());
}
else {
//
// Stash away the irp for the power policy state machine. We will
// post the event to the power policy state machine when the S irp
// completes back to this driver.
//
SetPendingSystemPowerIrp(Irp);
Irp->CopyCurrentIrpStackLocationToNext();
Irp->SetCompletionRoutineEx(deviceObject.GetObject(),
_SystemPowerSxCompletion,
this);
Irp->PoCallDriver(m_Device->GetAttachedDevice());
status = STATUS_PENDING;
}
}
else {
//
// We don't do anything with S irps if we are not the power policy
// owner.
//
// This will release the remove lock as well.
//
status = _PowerPassDown(this, Irp);
}
return status;
}
_Must_inspect_result_
NTSTATUS
FxPkgFdo::DispatchDeviceSetPower(
__in FxIrp *Irp
)
{
NTSTATUS status;
if (IsPowerPolicyOwner()) {
if (m_PowerPolicyMachine.m_Owner->m_RequestedPowerUpIrp == FALSE &&
m_PowerPolicyMachine.m_Owner->m_RequestedPowerDownIrp == FALSE) {
//
// A power irp arrived, but we did not request it. log and bugcheck
//
DoTraceLevelMessage(
GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
"Received set device power irp 0x%p on WDFDEVICE 0x%p !devobj 0x%p, "
"but the irp was not requested by the device (the power policy owner)",
Irp->GetIrp(), m_Device->GetHandle(),
m_Device->GetDeviceObject());
FxVerifierBugCheck(GetDriverGlobals(), // globals
WDF_POWER_MULTIPLE_PPO, // specific type
(ULONG_PTR)m_Device->GetDeviceObject(), //parm 2
(ULONG_PTR)Irp->GetIrp()); // parm 3
/* NOTREACHED */
}
//
// We are no longer requesting a power irp because we received the one
// we requested.
//
if (m_PowerPolicyMachine.m_Owner->m_RequestedPowerUpIrp) {
m_PowerPolicyMachine.m_Owner->m_RequestedPowerUpIrp = FALSE;
} else {
m_PowerPolicyMachine.m_Owner->m_RequestedPowerDownIrp = FALSE;
}
}
//
// Determine if we are raising or lowering the device power state.
//
if (Irp->GetParameterPowerStateDeviceState() == PowerDeviceD0) {
status = RaiseDevicePower(Irp);
}
else {
status = LowerDevicePower(Irp);
}
return status;
}
_Must_inspect_result_
NTSTATUS
FxPkgFdo::RaiseDevicePower(
__in FxIrp *Irp
)
{
Irp->MarkIrpPending();
Irp->CopyCurrentIrpStackLocationToNext();
Irp->SetCompletionRoutineEx(m_Device->GetDeviceObject(),
RaiseDevicePowerCompletion,
this);
Irp->PoCallDriver(m_Device->GetAttachedDevice());
return STATUS_PENDING;
}
_Must_inspect_result_
NTSTATUS
FxPkgFdo::RaiseDevicePowerCompletion(
__in MdDeviceObject DeviceObject,
__in MdIrp OriginalIrp,
__in PVOID Context
)
{
FxPkgFdo* pThis;
FxIrp irp(OriginalIrp);
UNREFERENCED_PARAMETER(DeviceObject);
pThis = (FxPkgFdo*) Context;
//
// We can safely cache away the device power irp in our fdo package
// storage because we know we can only get one device power irp at
// a time.
//
pThis->SetPendingDevicePowerIrp(&irp);
//
// Kick off the power state machine.
//
pThis->PowerProcessEvent(PowerD0);
return STATUS_MORE_PROCESSING_REQUIRED;
}
_Must_inspect_result_
NTSTATUS
FxPkgFdo::LowerDevicePower(
__in FxIrp *Irp
)
{
SetPendingDevicePowerIrp(Irp);
//
// Kick off the power state machine.
//
PowerProcessEvent(PowerDx);
return STATUS_PENDING;
}
_Must_inspect_result_
NTSTATUS
FxPkgFdo::DispatchSystemQueryPower(
__in FxIrp *Irp
)
{
if (PowerPolicyIsWakeEnabled()) {
NTSTATUS status;
status = PowerPolicyHandleSystemQueryPower(
Irp->GetParameterPowerStateSystemState()
);
Irp->SetStatus(status);
if (!NT_SUCCESS(status)) {
return CompletePowerRequest(Irp, status);
}
}
//
// Passing down the irp because one of the following
// a) We don't care b/c we don't control the power policy
// b) we are not enabled for arming for wake from Sx
// c) we can wake from the queried S state
//
return _PowerPassDown(this, Irp);
}
_Must_inspect_result_
NTSTATUS
FxPkgFdo::DispatchDeviceQueryPower(
__in FxIrp *Irp
)
{
//
// Either the framework is the power policy owner and we wouldn't be sending
// a device query power or we are a subordinate will do what the power
// policy owner wants 100% of the time.
//
Irp->SetStatus(STATUS_SUCCESS);
//
// This will release the remove lock
//
return _PowerPassDown(this, Irp);
}
VOID
FxPkgFdo::PowerReleasePendingDeviceIrp(
__in BOOLEAN IrpMustBePresent
)
{
MdIrp pIrp;
pIrp = ClearPendingDevicePowerIrp();
UNREFERENCED_PARAMETER(IrpMustBePresent);
ASSERT(IrpMustBePresent == FALSE || pIrp != NULL);
if (pIrp != NULL) {
FxIrp irp(pIrp);
if (irp.GetParameterPowerStateDeviceState() == PowerDeviceD0) {
//
// We catch D0 irps on the way up, so complete it
//
CompletePowerRequest(&irp, STATUS_SUCCESS);
}
else {
irp.SetStatus(STATUS_SUCCESS);
//
// We catch Dx irps on the way down, so send it on its way
//
// This will also release the remove lock
//
(void) _PowerPassDown(this, &irp);
}
}
}
WDF_DEVICE_POWER_STATE
FxPkgFdo::PowerCheckDeviceTypeOverload(
VOID
)
/*++
Routine Description:
This function implements the Check Type state. This is FDO code,
so the answer is reductionistly simple.
Arguments:
none
Return Value:
new power state
--*/
{
return WdfDevStatePowerWaking;
}
WDF_DEVICE_POWER_STATE
FxPkgFdo::PowerCheckDeviceTypeNPOverload(
VOID
)
/*++
Routine Description:
This function implements the Check Type state. This is FDO code,
so the answer is reductionistly simple.
Arguments:
none
Return Value:
new power state
--*/
{
return WdfDevStatePowerWakingNP;
}
_Must_inspect_result_
NTSTATUS
FxPkgFdo::PowerCheckParentOverload(
__out BOOLEAN* ParentOn
)
/*++
Routine Description:
This function implements the CheckParent state. Its
job is to determine which state we should go to next based on whether
the parent is in D0. But since this is the FDO code, we can't know
that. So just assume that the PDO will guarantee it.
Arguments:
none
Return Value:
new power state
--*/
{
ASSERT(!"This state shouldn't be reachable for an FDO.");
*ParentOn = TRUE;
return STATUS_SUCCESS;
}
VOID
FxPkgFdo::PowerParentPowerDereference(
VOID
)
/*++
Routine Description:
This virtual function is a nop for an FDO. PDOs implement this function
Arguments:
None
Return Value:
None
--*/
{
DO_NOTHING();
}