?? power.cpp
字號(hào):
/* 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},
/* PassiveCompletePending */{InvalidAction, InvalidAction, CompleteMainIrp},
/* 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
// will arrange to return STATUS_PENDING. Many of the other initial actions are entered
// from a standard I/O completion routine and will need to return STATUS_MORE_PROCESSING_REQUIRED
// to hold off final completion. Any action for MainIrpComplete that goes out through
// CompleteMainIrp will end up returning ctx->status, which gets set in MainCompletionRoutine
// to whatever's in the IRP -- this allows the IRP to complete normally. Any action off of
// AsyncNotify should be changing "status" explicitly (and they do -- I checked).
#if DBG
enum POWSTATE originalstate = ctx->state;
enum POWSTATE nextstate = originalstate;
#define SETSTATE(s) ctx->state = nextstate = s
LONG eventid = InterlockedIncrement(&ctx->eventcount);
LONG ctxid = ctx->id;
#else
#define SETSTATE(s) ctx->state = s
#endif
POWTRACE((DRIVERNAME " - %d.%d is %s in state %s\n", ctxid, eventid, eventnames[event], powstatenames[originalstate]));
while (TRUE)
{ // handle this event
POWTRACE((DRIVERNAME " - %d.%d %s\n", ctxid, eventid, actionnames[action]));
switch (action)
{ // perform next action
///////////////////////////////////////////////////////////////////////
// TriageNewIrp is the first action for a newly receive query or set IRP
case TriageNewIrp:
{ // TriageNewIrp
ASSERT(stack->MajorFunction == IRP_MJ_POWER);
ASSERT(stack->MinorFunction == IRP_MN_QUERY_POWER || stack->MinorFunction == IRP_MN_SET_POWER);
ASSERT(ctx->state == InitialState);
// We want the power dispatch routine to return STATUS_PENDING unless
// something goes wrong right away. If we do return STATUS_PENDING, we
// need to be sure we mark the IRP pending,
status = STATUS_PENDING;
IoMarkIrpPending(Irp);
// Acquire remove lock an extra time. We'll release it when we eventually
// complete this IRP.
IoAcquireRemoveLock(&pdx->RemoveLock, Irp);
// For a system IRP, we'll request the corresponding device IRP. If system power is
// being restored, we wait until the lower level drivers finish the system IRP. If
// system power is being removed, we do it now and forward the system IRP when the
// device IRP finishes.
if (stack->Parameters.Power.Type == SystemPowerState)
{ // system IRP
if (stack->Parameters.Power.State.SystemState < pdx->syspower)
{
action = ForwardMainIrp;
SETSTATE(SysPowerUpPending);
}
else
{
action = SelectDState;
SETSTATE(SubPowerDownPending);
}
} // system IRP
// For a device set-power IRP, we have a variety of tasks to carry out. If device
// power is being restored, we do those tasks when the lower level drivers complete
// the IRP. If device power is being removed or staying the same, we do those tasks
// before passing this IRP down. In either case, we ensure that the device isn't busy
// with any substantive IRPs first.
else
{ // device IRP
SETSTATE(QueueStallPending);
action = QueueStallComplete;
} // device IRP
continue;
} // TriageNewIrp
///////////////////////////////////////////////////////////////////////
// QueueStallComplete is the action for an AsyncNotify event in the
// QueueStallPending state. It's reached when StartNextPacket calls
// GenericSaveRestoreComplete, which we specified as the current-irp
// complete notification routine in our earlier call to StallRequestsAndNotify.
// This action can also be reached directly from TriageNewIrp if the
// device was idle to begin with or if we were already in a low-power
// state (so that the queue should have been stalled)
case QueueStallComplete:
{ // QueueStallComplete
if (stack->MinorFunction == IRP_MN_SET_POWER)
{ // device set-power IRP
if (stack->Parameters.Power.State.DeviceState < pdx->devpower)
{
action = ForwardMainIrp;
SETSTATE(DevPowerUpPending);
}
else
action = SaveContext;
} // device set-power IRP
else
{ // device query-power IRP
if (stack->Parameters.Power.State.DeviceState < pdx->devpower)
{
action = ForwardMainIrp;
SETSTATE(DevQueryUpPending);
}
else
action = DevQueryDown;
} // device query-power IRP
continue;
} // QueueStallComplete
///////////////////////////////////////////////////////////////////////
// ForwardMainIrp sends the current power IRP to the next driver in the
// stack. We regain control in MainCompletionRoutine.
case ForwardMainIrp:
{ // ForwardMainIrp
IoCopyCurrentIrpStackLocationToNext(Irp);
IoSetCompletionRoutine(Irp, (PIO_COMPLETION_ROUTINE) MainCompletionRoutine, (PVOID) ctx, TRUE, TRUE, TRUE);
SafePoCallDriver(pdx->LowerDeviceObject, Irp); // avoid Win98 problem later on
break;
} // ForwardMainIrp
///////////////////////////////////////////////////////////////////////
// SysPowerUpComplete is the action for a MainIrpComplete event in the
// SysPowerUpPending state. If the IRP succeeded, request the corresponding
// D-state IRP. When the subsidiary IRP finishes, we'll complete this
// S-state IRP as well.
//
// The DDK doesn't explicitly say you need to send a D-state query when you
// get an S-state query. It simplifies our own logic a good deal to do this,
// however.
case SysPowerUpComplete:
{ // SysPowerUpComplete
ASSERT(event == MainIrpComplete);
if (!NT_SUCCESS(ctx->status))
action = CompleteMainIrp;
else
{ // S-irp succeeded
action = SelectDState;
SETSTATE(SubPowerUpPending);
status = STATUS_MORE_PROCESSING_REQUIRED; // defer completion of S-IRP
if (stack->MinorFunction == IRP_MN_SET_POWER)
{ // S-set
pdx->syspower = stack->Parameters.Power.State.SystemState;
// Except in 98/Me, complete an S0 IRP right away to allow
// faster restart after suspend.
if (pdx->syspower == PowerSystemWorking && !win98)
{ // S0
ASSERT(Irp);
PoStartNextPowerIrp(Irp);
status = STATUS_SUCCESS; // allows S-IRP to complete
IoReleaseRemoveLock(&pdx->RemoveLock, Irp);
ctx->irp = NULL; // flag for eventual CompleteMainIrp step
} // S0
} // S-set
} // S-irp succeeded
continue;
} // SysPowerUpComplete
///////////////////////////////////////////////////////////////////////
// SysPowerDownComplete is the action for a MainIrpComplete event in the
// SysPowerDownPending state.
case SysPowerDownComplete:
{ // SysPowerDownComplete
if (stack->MinorFunction == IRP_MN_SET_POWER)
pdx->syspower = stack->Parameters.Power.State.SystemState;
action = CompleteMainIrp;
continue;
} // SysPowerDownComplete
///////////////////////////////////////////////////////////////////////
// SelectDState is used to establish the power state and minor function
// code for a D-state IRP that corresponds to the S-state IRP we're
// processing. After doing that, we do the SendDeviceIrp action.
case SelectDState:
{ // SelectDState
SYSTEM_POWER_STATE sysstate = stack->Parameters.Power.State.SystemState;
ctx->devstate = GetLowestDevicePowerState(pdx, sysstate);
ctx->MinorFunction = stack->MinorFunction;
action = SendDeviceIrp;
continue;
} // SelectDState
///////////////////////////////////////////////////////////////////////
// SendDeviceIrp requests a device set- or query-power IRP using the power
// state and minor function code currently in the context block. SelectDState
// put them there.
case SendDeviceIrp:
{ // SendDeviceIrp
// If we want the device in the same state it's already in, bypass sending
// the D-state IRP. This is necessary in Win98 due to a bug that causes
// PoRequestPowerIrp to report success in a situation where CONFIGMG won't
// generate the configuration event. This has also turned out to be necessary
// when bringing Win2K/Xp out of standby or hibernate, although I don't
// exactly understand why.
if (ctx->devstate == pdx->devpower)
{ // pretend success
POWTRACE((DRIVERNAME " - %d.%d pretending to succeed D-state IRP for Win98\n", ctxid, eventid));
ctx->status = STATUS_SUCCESS;
action = actiontable[ctx->state][AsyncNotify];
continue;
} // pretend success
// Ask the power manager to send us an IRP. In Win98, we need to supply the
// PDO as the device object address because NTKERN needs to go directly from
// there to the devnode address.
POWER_STATE powstate;
powstate.DeviceState = ctx->devstate;
NTSTATUS postatus = PoRequestPowerIrp(pdx->Pdo, ctx->MinorFunction, powstate,
(PREQUEST_POWER_COMPLETE) PoCompletionRoutine, ctx, NULL);
// If PoRequestPowerIrp fails, it never actually sent an IRP down the stack,
// so we can certain that PoCompletionRoutine never ran
if (NT_SUCCESS(postatus))
break; // started device IRP okay
KdPrint((DRIVERNAME " - PoRequestPowerIrp failed - %X\n", postatus));
action = CompleteMainIrp;
ctx->status = postatus;
continue;
} // SendDeviceIrp
///////////////////////////////////////////////////////////////////////
// CompleteMainIrp is the penultimate action of the finite state machine.
// This is where we actually complete the power IRP we've been handling.
case CompleteMainIrp:
{ // CompleteMainIrp
// In Win98, we must not complete a power IRP at DISPATCH_LEVEL, so
// queue a work item in that case.
if (win98 && KeGetCurrentIrql() > PASSIVE_LEVEL)
{ // defer completion to passive level
#pragma warning(disable:4995)
#pragma warning(disable:4996)
C_ASSERT(sizeof(Irp->Tail.Overlay.DriverContext) >= sizeof(WORK_QUEUE_ITEM));
ASSERT(Irp);
PWORK_QUEUE_ITEM item = (PWORK_QUEUE_ITEM) Irp->Tail.Overlay.DriverContext;
ExInitializeWorkItem(item, (PWORKER_THREAD_ROUTINE) PassivePowerComplete, (PVOID) ctx);
ExQueueWorkItem(item, CriticalWorkQueue);
#pragma warning(default:4995)
#pragma warning(default:4996)
if (event == MainIrpComplete)
status = STATUS_MORE_PROCESSING_REQUIRED;
SETSTATE(PassiveCompletePending);
break;
} // defer completion to passive level
// Skip completion processing if we've already completed an S0 SET_POWER.
// Otherwise, complete (or allow to complete) the main IRP
if (Irp)
{ // not previously completed
// Tell the power manager to release the next IRP of the same flavor
// as we're currently processing.
PoStartNextPowerIrp(Irp);
// If called from MainCompletionRoutine, just allow the completion process
// to take its course. Otherwise, explicitly complete the main IRP.
if (event == MainIrpComplete)
status = ctx->status; // might have been STATUS_MORE_PROCESSING_REQUIRED until now
else
{
?? 快捷鍵說(shuō)明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -