📄 powerfdo.cpp
字号:
// Power request handler notify driver
// Copyright (C) 1999 by Walter Oney
// All rights reserved
#include "stddcls.h"
#include "driver.h"
NTSTATUS DefaultPowerHandler(PDEVICE_EXTENSION pdx, IN PIRP Irp);
enum POWSTATE {
InitialState = 0, // initial state of FSM
SysPowerUpPending, // system power-up IRP forwarded
SubPowerUpPending, // waiting for nested device power up to finish
SubPowerDownPending, // waiting from device to power down before forwarding system power-down IRP
SysPowerDownPending, // waiting for system power-down IRP to finish
DevPowerUpPending, // waiting for device power-up IRP
DevPowerDownPending, // waiting for device power-down IRP
ContextSavePending, // context save is underway
ContextRestorePending, // context restore is underway
DevQueryUpPending, // device query for power-up pending
DevQueryDownPending, // device query for power-down pending
QueueStallPending, // waiting for device to be idle
FinalState, // final state of FSM
NUMPOWSTATES,
};
enum POWEVENT {
NewIrp = 0, // new query/set IRP
MainIrpComplete, // the main IRP has finished
AsyncNotify, // some other event has occurred
NUMPOWEVENTS,
};
typedef struct _POWCONTEXT {
PDEVICE_EXTENSION pdx; // our own device extension
PIRP irp; // the IRP we're processing
enum POWSTATE state; // current state of FSM
NTSTATUS status; // completion status for main IRP
PKEVENT pev; // event to set when IRP is complete
DEVICE_POWER_STATE devstate; // device power state to use
DEVICE_POWER_STATE oldpower; // previous device power state
UCHAR MinorFunction; // minor function to use in requested power IRP
} POWCONTEXT, *PPOWCONTEXT;
NTSTATUS HandlePowerEvent(PPOWCONTEXT ctx, enum POWEVENT event);
#if DBG
static char* fcnname[] = {
"IRP_MN_WAIT_WAKE",
"IRP_MN_POWER_SEQUENCE",
"IRP_MN_SET_POWER",
"IRP_MN_QUERY_POWER",
};
static char* sysstate[] = {
"PowerSystemUnspecified",
"PowerSystemWorking",
"PowerSystemSleeping1",
"PowerSystemSleeping2",
"PowerSystemSleeping3",
"PowerSystemHibernate",
"PowerSystemShutdown",
"PowerSystemMaximum",
};
static char* devstate[] = {
"PowerDeviceUnspecified",
"PowerDeviceD0",
"PowerDeviceD1",
"PowerDeviceD2",
"PowerDeviceD3",
"PowerDeviceMaximum",
};
#endif // DBG
///////////////////////////////////////////////////////////////////////////////
#pragma PAGEDCODE
NTSTATUS DispatchPowerFdo(IN PDEVICE_OBJECT fdo, IN PIRP Irp)
{ // DispatchPowerFdo
PAGED_CODE();
PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;
NTSTATUS status = IoAcquireRemoveLock(&pdx->RemoveLock, Irp);
if (!NT_SUCCESS(status))
return CompleteRequest(Irp, status, 0);
PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);
ASSERT(stack->MajorFunction == IRP_MJ_POWER);
ULONG fcn = stack->MinorFunction;
if (fcn == IRP_MN_SET_POWER || fcn == IRP_MN_QUERY_POWER)
{ // handle set/query
#if DBG
KdPrint((DRIVERNAME " - POWER Request (%s)", fcnname[fcn]));
if (stack->Parameters.Power.Type == SystemPowerState)
KdPrint((", S-state = %s\n", sysstate[stack->Parameters.Power.State.SystemState]));
else
KdPrint((", D-state = %s\n", devstate[stack->Parameters.Power.State.DeviceState]));
#endif // DBG
{ // launch FSM
PPOWCONTEXT ctx = (PPOWCONTEXT) ExAllocatePool(NonPagedPool, sizeof(POWCONTEXT));
if (!ctx)
{
KdPrint((DRIVERNAME " - Can't allocate power context structure\n"));
status = CompleteRequest(Irp, STATUS_INSUFFICIENT_RESOURCES);
}
else
{ // process this IRP
RtlZeroMemory(ctx, sizeof(POWCONTEXT));
ctx->pdx = pdx;
ctx->irp = Irp;
status = HandlePowerEvent(ctx, NewIrp);
} // process this IRP
} // launch FSM
} // handle set/query
else
{ // handle other power request
KdPrint((DRIVERNAME " - POWER Request (%s)\n", fcn < arraysize(fcnname) ? fcnname[fcn] : "??"));
status = DefaultPowerHandler(pdx, Irp);
} // handle other power request
IoReleaseRemoveLock(&pdx->RemoveLock, Irp);
return status;
} // DispatchPowerFdo
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
NTSTATUS DefaultPowerHandler(PDEVICE_EXTENSION pdx, IN PIRP Irp)
{ // DefaultPowerHandler
PoStartNextPowerIrp(Irp); // must be done while we own the IRP
IoSkipCurrentIrpStackLocation(Irp);
return PoCallDriver(pdx->LowerDeviceObject, Irp);
} // DefaultPowerHandler
///////////////////////////////////////////////////////////////////////////////
VOID SendAsyncNotification(PVOID context)
{ // SendAsyncNotification
HandlePowerEvent((PPOWCONTEXT) context, AsyncNotify);
} // SendAsyncNotification
///////////////////////////////////////////////////////////////////////////////
struct SDSP_CONTEXT {
PKEVENT pev; // event to signal when request complete
NTSTATUS status; // ending status
};
#pragma LOCKEDCODE
VOID SendDeviceSetPowerComplete(PDEVICE_OBJECT junk, UCHAR fcn, POWER_STATE state, SDSP_CONTEXT* context, PIO_STATUS_BLOCK pstatus)
{ // SendDeviceSetPowerComplete
context->status = pstatus->Status;
KeSetEvent(context->pev, EVENT_INCREMENT, FALSE);
} // SendDeviceSetPowerComplete
NTSTATUS SendDeviceSetPower(PDEVICE_EXTENSION pdx, DEVICE_POWER_STATE devpower, BOOLEAN wait /* = FALSE */)
{ // SendDeviceSetPower
POWER_STATE state;
state.DeviceState = devpower;
NTSTATUS status;
if (wait)
{ // synchronous operation
KEVENT event;
KeInitializeEvent(&event, NotificationEvent, FALSE);
SDSP_CONTEXT context = {&event};
status = PoRequestPowerIrp(pdx->Pdo, IRP_MN_SET_POWER, state,
(PREQUEST_POWER_COMPLETE) SendDeviceSetPowerComplete, &context, NULL);
if (status == STATUS_PENDING)
{
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
status = context.status;
}
} // synchronous operation
else
status = PoRequestPowerIrp(pdx->Pdo, IRP_MN_SET_POWER, state, NULL, NULL, NULL);
return status;
} // SendDeviceSetPower
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
NTSTATUS MainCompletionRoutine(PDEVICE_OBJECT junk, PIRP Irp, PPOWCONTEXT ctx);
VOID PoCompletionRoutine(PDEVICE_OBJECT junk, UCHAR fcn, POWER_STATE state, PPOWCONTEXT ctx, PIO_STATUS_BLOCK pstatus);
NTSTATUS SafePoCallDriver(PDEVICE_OBJECT DeviceObject, PIRP Irp);
NTSTATUS HandlePowerEvent(PPOWCONTEXT ctx, enum POWEVENT event)
{ // HandlePowerEvent
NTSTATUS status = -1; // an invalid value
ASSERT(ctx);
ASSERT((ULONG) event < NUMPOWEVENTS);
PIRP Irp = ctx->irp;
PIO_STACK_LOCATION stack = Irp ? IoGetCurrentIrpStackLocation(Irp) : NULL;
PDEVICE_EXTENSION pdx = ctx->pdx;
enum POWACTION {
InvalidAction, // code for invalid state/event combinations
TriageNewIrp, // decide what to do with new IRP
QueueStallComplete, // device queue has been stalled
ForwardMainIrp, // begin system or device IRP for more power
SysPowerUpComplete, // system power-up IRP completed
SysPowerDownComplete, // system power-down IRP completed
SelectDState, // choose D-state corresponding to main IRP's S-state
SendDeviceIrp, // send device IRP
CompleteMainIrp, // complete the main IRP
DestroyContext, // terminate FSM
SubPowerUpComplete, // nested power-up IRP finished or failed
SubPowerDownComplete, // nested power-down IRP finished or failed
DevPowerUpComplete, // device power-up IRP has completed
SaveContext, // save context in preparation for powering down
ContextSaveComplete, // device context has been saved
ContextRestoreComplete, // device context has been restored
DevQueryUpComplete, // device query for power-up complete
DevQueryDown, // see if device can power down
DevQueryDownComplete, // device query for power-down complete
};
static enum POWACTION actiontable[NUMPOWSTATES][NUMPOWEVENTS] = {
/* NewIrp MainIrpComplete AsyncNotify */
/* InitialState */ {TriageNewIrp, InvalidAction, InvalidAction},
/* SysPowerUpPending */ {InvalidAction, SysPowerUpComplete, InvalidAction},
/* SubPowerUpPending */ {InvalidAction, InvalidAction, SubPowerUpComplete},
/* SubPowerDownPending */ {InvalidAction, InvalidAction, SubPowerDownComplete},
/* SysPowerDownPending */ {InvalidAction, SysPowerDownComplete, InvalidAction},
/* DevPowerUpPending */ {InvalidAction, DevPowerUpComplete, InvalidAction},
/* DevPowerDownPending */ {InvalidAction, CompleteMainIrp, InvalidAction},
/* ContextSavePending */ {InvalidAction, InvalidAction, ContextSaveComplete},
/* ContextRestorePending */ {InvalidAction, InvalidAction, ContextRestoreComplete},
/* DevQueryUpPending */ {InvalidAction, DevQueryUpComplete, InvalidAction},
/* DevQueryDownPending */ {InvalidAction, DevQueryDownComplete, InvalidAction},
/* QueueStallPending */ {InvalidAction, InvalidAction, QueueStallComplete},
/* FinalState */ {InvalidAction, InvalidAction, InvalidAction},
};
// Determine the first action to take based on the current state of the FSM and the event that occurred.
// Note that this isn't as complicated as the use of 2-D array might suggest: all states except
// the initial state lead to a single action for the one-and-only event that's possible to get in
// that state.
enum POWACTION action = actiontable[ctx->state][event];
// Structurally, the following code is a switch on "action" imbedded within an
// infinite loop. A case that does a "break" from the switch executes a "break"
// from the loop, whereupon we return whatever value is left in "status". A case
// that does a "continue" from the switch repeats the loop -- this is how actions
// can be strung together during one call to this routine. I coded it this way to
// avoid return statements in the middle that make it harder to prove that the
// routine behaves in a predictable way. Note that any "break" should be preceded
// by a change to the state recorded in the context structure and to the initially
// invalid valid of "status". There are ASSERTs at the end to check this.
// Concerning the required change to "ctx->state": there are many cases where we
// call PoRequestPowerIrp or PoCallDriver, whereupon the context structure gets
// released before those routines return. We use a SETSTATE macro so we don't
// have to dereference a possibly invalid "ctx" pointer at the end of the loop. Any
// action that calls a routine that might result in completing the current IRP
// should also take care not to touch "ctx" afterwards. (These are always cases that
// "break" from the switch, so you can just verify that the break always immediately
// follows the PoXxx call.)
// Concerning the required change to "status": only TriageNewIrp
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -