📄 powerfdo.cpp
字号:
// 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
#else
#define SETSTATE(s) ctx->state = s
#endif
while (TRUE)
{ // handle this event
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 a MainIrpComplete event in th
// 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
{
if (stack->MinorFunction == IRP_MN_SET_POWER)
pdx->syspower = stack->Parameters.Power.State.SystemState;
action = SelectDState;
SETSTATE(SubPowerUpPending);
status = STATUS_MORE_PROCESSING_REQUIRED;
}
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;
// TODO In my testing, if I didn't go to D0 here, Win2K wouldn't come out
// of suspend. Oddly enough, Win98SE automatically repowers the device the next time
// a R/W IRP comes along if I just leave the device in D3. The DDK says
// you can just leave the device depowered until another IRP comes along
// so long as you're not an INRUSH device. It also says you must repower
// yourself when you get a new IRP. This all needs more investigation...
if (sysstate == PowerSystemWorking)
ctx->devstate = PowerDeviceD0;
else
{
DEVICE_POWER_STATE maxstate = pdx->devcaps.DeviceState[sysstate];
DEVICE_POWER_STATE minstate = PowerDeviceD3;
ctx->devstate = minstate > maxstate ? minstate : maxstate;
}
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
// Win98 has a bug such that, if you request a set-power IRP for the same
// D-state you're already in, PoRequestPowerIrp will report success but
// CONFIGMG won't generate the configuration event that causes the IRP to
// actually be sent. Without the following test, we would wait forever
// for this expected IRP to complete.
if (win98 && ctx->devstate == pdx->devpower)
{ // pretend success
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
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
{
ASSERT(ctx->status != STATUS_PENDING);
Irp->IoStatus.Status = ctx->status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
}
// Release the remove lock to balance the extra acquisition in TriageNewIrp
IoReleaseRemoveLock(&pdx->RemoveLock, Irp);
action = DestroyContext;
continue;
} // CompleteMainIrp
///////////////////////////////////////////////////////////////////////
// DestroyContext is the last action for an IRP.
case DestroyContext:
{ // DestroyContext
SETSTATE(FinalState);
ExFreePool(ctx);
break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -