📄 power.cpp
字号:
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
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
POWTRACE(("%s - %d.%d Completing S0 IRP early\n", pdx->DebugName, ctxid, eventid));
PoStartNextPowerIrp(Irp);
status = STATUS_SUCCESS; // allows S-IRP to complete
if (pdx->RemoveLock)
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 (!NT_SUCCESS(ctx->status))
KdPrint(("%s - System power-down IRP failed - %X\n", pdx->DebugName, ctx->status));
if (stack->MinorFunction == IRP_MN_SET_POWER)
pdx->syspower = stack->Parameters.Power.State.SystemState; // (note that this IRP could not legally have been failed)
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 sstate = stack->Parameters.Power.State.SystemState;
ctx->devstate = GetLowestDevicePowerState(pdx, sstate);
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(("%s - %d.%d pretending to succeed D-state IRP for same state\n", pdx->DebugName, 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(("%s - 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));
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
{
Irp->IoStatus.Status = ctx->status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
if (status == -1)
status = STATUS_SUCCESS;
}
// Release the remove lock to balance the extra acquisition in TriageNewIrp
if (pdx->RemoveLock)
IoReleaseRemoveLock(pdx->RemoveLock, Irp);
} // not previously completed
// If we stalled the request queue for the pendency of this IRP, unstall it now.
// Sometimes, we will have cleared the flag in order to leave the queue stalled
// after completing a power-down.
if (ctx->UnstallQueue)
{
pdx->StalledForPower = FALSE;
POWTRACE(("%s - %d.%d unstalling queue\n", pdx->DebugName, ctxid, eventid));
RestartAllRequests(pdx->queues, pdx->nqueues, pdx->DeviceObject);
}
action = DestroyContext;
continue;
} // CompleteMainIrp
///////////////////////////////////////////////////////////////////////
// DestroyContext is the last action for an IRP.
case DestroyContext:
{ // DestroyContext
SETSTATE(FinalState);
ExFreePool(ctx);
break;
} // DestroyContext
///////////////////////////////////////////////////////////////////////
// SubPowerUpComplete is the action for a AsyncNotify event in the
// SubPowerUpPending state. This should be called from PoCompletionRoutine.
// We can also get here from SendDeviceIrp to avoid the Win98 no-D-IRP bug,
// in which case we don't want to alter "status" from its current value.
case SubPowerUpComplete:
{ // SubPowerUpComplete
if (status == -1)
status = STATUS_SUCCESS; // don't actually care, since called from PoCompletionRoutine
action = CompleteMainIrp;
continue;
} // SubPowerUpComplete
///////////////////////////////////////////////////////////////////////
// SubPowerDownComplete is the action for a AsyncNotify event in the
// SubPowerDownPending state. This should be called from PoCompletionRoutine.
// We can also get here from SendDeviceIrp to avoid the Win98 no-D-IRP bug,
// in which case we don't want to alter "status" from its current value.
case SubPowerDownComplete:
{ // SubPowerDownComplete
if (status == -1)
status = STATUS_SUCCESS; // don't actually care, since called from PoCompletionRoutine
if (NT_SUCCESS(ctx->status))
{
SETSTATE(SysPowerDownPending);
action = ForwardMainIrp;
}
else
action = CompleteMainIrp; // D-state IRP failed, so fail S-state IRP too
continue;
} // SubPowerDownComplete
///////////////////////////////////////////////////////////////////////
// DevPowerUpComplete is the action for a MainIrpComplete event in the
// DevPowerUpPending state. This should be called from MainCompletionRoutine
// when a device power-up IRP finishes in the lower layers.
case DevPowerUpComplete:
{ // DevPowerUpComplete
// If this IRP failed, or if we're just dealing with a query, we're done.
if (!NT_SUCCESS(ctx->status) || stack->MinorFunction != IRP_MN_SET_POWER)
{
action = CompleteMainIrp;
continue;
}
status = STATUS_MORE_PROCESSING_REQUIRED; // defer completion of the main IRP while we restore context
ctx->oldpower = pdx->devpower;
pdx->devpower = stack->Parameters.Power.State.DeviceState;
KdPrint(("%s - Now in %s\n", pdx->DebugName, devstate[pdx->devpower]));
// Since no bus driver currently implements IRP_MN_POWER_SEQUENCE, you
// don't really need to bother with it. But doing it correctly is enough of a pain
// in the neck that it's worthwhile showing you how to do it just in case it ever
// becomes relevant. I thought this was marginal enough that I didn't discuss
// the extra states in the book, though. In the book, I showed this action as
// including the stuff that's in RestoreContext, too. [SP-10: don't do this unless
// PerfBoundary is nonzero. The comment in generic.h says that leaving it zero
// will suppress the IRP, and a reader reported a problem with PCMCIA.SYS barfing
// on one of them.]
if (pdx->PerfBoundary && ctx->oldpower >= pdx->PerfBoundary)
{ // context restore will be expensive
SETSTATE(RestoreSeqPending);
action = SendPowerSequence;
continue;
} // context restore will be expensive
action = RestoreContext;
continue;
} // DevPowerUpComplete
///////////////////////////////////////////////////////////////////////
// ContextRestoreComplete is the last action for a device set power up
// operation. It's ordinarily reached when GenericSaveRestoreComplete
// signals a MainIrpComplete event from the ContextRestorePending state.
// It can also be reached directly from DevPowerUpComplete when there is
// no context restore function.
case ContextRestoreComplete:
{ // ContextRestoreComplete
if (event == AsyncNotify)
status = STATUS_SUCCESS; // doesn't actually matter
action = CompleteMainIrp;
// If the device IRP failed, just go ahead and let it complete. If we've
// successfully resumed to a sleeping state (> D0), skip restarting the
// substantive IRP queue and complete the IRP as well.
if (!NT_SUCCESS(ctx->status) || pdx->devpower != PowerDeviceD0)
continue;
// We've just gone to D0, unstall the request queue by setting
// the flag that will cause CompleteMainIrp to do so. It's very
// important in our scheme that we stall the queue for any device
// IRP that arrives while we're in D0 and omit to unstall it when
// we complete a SET for a lower power state. We thereby arrange
// to stall the queue just once across an entire period of low
// power.
if (pdx->nqueues)
ctx->UnstallQueue = TRUE;
continue;
} // ContextRestoreComplete
///////////////////////////////////////////////////////////////////////
// SaveContext initiates a context save operation if necessary. This will
// be the second action for a new device set-power IRP.
case SaveContext:
{ // SaveContext
DEVICE_POWER_STATE devpower = stack->Parameters.Power.State.DeviceState;
// If we're actually changing to a lower power state, give the client driver
// a chance to save any device context information asynchronously. It will
// call GenericSaveRestoreComplete when it finishes.
if (pdx->SaveDeviceContext && devpower > pdx->devpower)
{ // save context
SETSTATE(ContextSavePending);
(*pdx->SaveDeviceContext)(pdx->DeviceObject, pdx->devpower, devpower, ctx);
break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -