📄 power.cpp
字号:
} // save context
action = ContextSaveComplete;
continue;
} // SaveContext
///////////////////////////////////////////////////////////////////////
// ContextSaveComplete is the action for an AsyncNotify event in the
// ContextSavePending state. It should be entered from GenericSaveRestoreComplete,
// which in turn should have been called by the client driver when its
// context save operation finished. It can also be entered directly from
// SaveContext when there is no context save routine.
case ContextSaveComplete:
{ // ContextSaveComplete
if (event == AsyncNotify)
status = STATUS_SUCCESS; // doesn't actually matter in this case
// Obtain a set of power-state sequence numbers to optimize state restoration later.
// In the book, I didn't include this bit of nonsense. The code in ForwardDevDown
// appears right here in the book. [SP-10: see note elsewhere]
if (pdx->PerfBoundary && stack->Parameters.Power.State.DeviceState >= pdx->PerfBoundary)
{ // context restore will be expensive
SETSTATE(SaveSeqPending);
action = SendPowerSequence;
continue;
} // context restore will be expensive/
action = ForwardDevDown;
continue;
} // ContextSaveComplete
///////////////////////////////////////////////////////////////////////
// DevQueryUpComplete is the action for a MainIrpComplete event in the
// DevQueryUpPending state. This should be called by MainCompletionRoutine
// when a device query-power-up IRP completes. We don't expect to ever get this
// kind of a query, by the way, but we should handle it nontheless.
case DevQueryUpComplete:
{ // DevQueryUpComplete
if (NT_SUCCESS(ctx->status) && pdx->QueryPower)
{ // ask client if change okay
// Everybody belows us has acquiesced in restoring power. Ask the
// client driver if it's cool by this too.
if (!(*pdx->QueryPower)(pdx->DeviceObject, pdx->devpower, stack->Parameters.Power.State.DeviceState))
ctx->status = STATUS_UNSUCCESSFUL; // fail the query
} // ask client if change okay
action = CompleteMainIrp;
continue;
} // DevQueryUpComplete
///////////////////////////////////////////////////////////////////////
// DevQueryDown is the second action (after TriageNewIrp) for a device
// query-power that specifies less than or equal to the current device
// power state.
case DevQueryDown:
{ // DevQueryDown
DEVICE_POWER_STATE devpower = stack->Parameters.Power.State.DeviceState;
// If the proposed power state is less than the current state,
// ask the client driver if it's okay to change.
if (devpower > pdx->devpower && pdx->QueryPower && !(*pdx->QueryPower)(pdx->DeviceObject, pdx->devpower, devpower))
{ // fail the query
ctx->status = STATUS_UNSUCCESSFUL;
action = DevQueryDownComplete;
continue;
} // fail the query
SETSTATE(DevQueryDownPending);
action = ForwardMainIrp;
continue;
} // DevQueryDown
///////////////////////////////////////////////////////////////////////
// DevQueryDownComplete is the action for a MainIrpComplete event in the
// DevQueryDownPending state. It can be reached from MainCompletionRoutine
// or directly from DevQueryDown.
case DevQueryDownComplete:
{ // DevQueryDownComplete
// The Windows 98 version of the USB hub driver fails device power
// query operations, which is incorrect...
if (ctx->status == STATUS_INVALID_PARAMETER)
ctx->status = STATUS_SUCCESS;
// If the query is succeeding, leave the request queue stalled
if (NT_SUCCESS(ctx->status))
ctx->UnstallQueue = FALSE;
action = CompleteMainIrp;
continue;
} // DevQueryDownComplete
///////////////////////////////////////////////////////////////////////
// SendPowerSequence creates an IRP_MN_POWER_SEQUENCE request. It can be
// entered from DevPowerUpComplete or ContextSaveComplete
case SendPowerSequence:
{ // SendPowerSequence
PIRP seqirp = IoAllocateIrp(pdx->LowerDeviceObject->StackSize, FALSE);
if (seqirp)
{ // sequence IRP created
PIO_STACK_LOCATION seqstack = IoGetNextIrpStackLocation(seqirp);
seqstack->MajorFunction = IRP_MJ_POWER;
seqstack->MinorFunction = IRP_MN_POWER_SEQUENCE;
seqstack->Parameters.PowerSequence.PowerSequence = &ctx->sequence;
IoSetCompletionRoutine(seqirp, (PIO_COMPLETION_ROUTINE) SequenceCompletionRoutine, (PVOID) ctx, TRUE, TRUE, TRUE);
PoCallDriver(pdx->LowerDeviceObject, seqirp);
break;
} // sequence IRP created
else
{ // couldn't create IRP
if (ctx->state == SaveSeqPending)
action = ForwardDevDown;
else
action = RestoreContext;
continue;
} // couldn't create IRP
} // SendPowerSequence
///////////////////////////////////////////////////////////////////////
// SaveSeqComplete is the action for an AsyncNotify event in the
// SaveSeqPending state. It should be entered from SequenceCompletionRoutine
// when an IRP_MN_POWER_SEQUENCE request completes.
case SaveSeqComplete:
{ // SaveSeqComplete
if (NT_SUCCESS(ctx->status))
{
KdPrint(("%s - Someone actually succeeded a POWER_SEQUENCE request!\n", pdx->DebugName));
pdx->oldseq = ctx->sequence; // save sequence numbers for power-up
}
else
RtlZeroMemory(&pdx->oldseq, sizeof(pdx->oldseq));
status = STATUS_MORE_PROCESSING_REQUIRED; // won't actually be used
ctx->status = STATUS_SUCCESS; // status from POWER_SEQUENCE not actually relevant
action = ForwardDevDown;
continue;
} // SaveSeqComplete
///////////////////////////////////////////////////////////////////////
// ForwardDevDown forwards a device set-power down IRP. It can be reached
// from SaveSeqComplete or from ContextSaveComplete.
case ForwardDevDown:
{ // ForwardDevDown
SETSTATE(DevPowerDownPending);
action = ForwardMainIrp;
DEVICE_POWER_STATE devpower = stack->Parameters.Power.State.DeviceState;
if (devpower <= pdx->devpower)
continue; // no actual change in device power
pdx->devpower = devpower;
KdPrint(("%s - Now in %s\n", pdx->DebugName, devstate[pdx->devpower]));
// If power has now been removed, leave the queue stalled
if (devpower > PowerDeviceD0)
ctx->UnstallQueue = FALSE;
continue;
} // ForwardDevDown
///////////////////////////////////////////////////////////////////////
// RestoreSeqComplete is the action for an AsyncNotify event in the
// RestoreSeqPending state. It should be entered from SequenceCompletionRoutine
// when an IRP_MN_POWER_SEQUENCE request completes
case RestoreSeqComplete:
{ // RestoreSeqComplete
// Now decide if we really need to restore the device context by comparing
// the sequence numbers for the performance boundary state
action = RestoreContext; // assume we'll want to restore context
if (NT_SUCCESS(ctx->status))
switch (pdx->PerfBoundary)
{ // check relevant sequence numbers
case PowerDeviceD1:
if (ctx->sequence.SequenceD1 <= pdx->oldseq.SequenceD1)
action = ContextRestoreComplete;
break;
case PowerDeviceD2:
if (ctx->sequence.SequenceD2 <= pdx->oldseq.SequenceD2)
action = ContextRestoreComplete;
break;
case PowerDeviceD3:
if (ctx->sequence.SequenceD3 <= pdx->oldseq.SequenceD3)
action = ContextRestoreComplete;
break;
} // check relevant sequence numbers
#if DBG
if (action != RestoreContext)
KdPrint(("%s - Bypassing context restore due to sequence number optimization\n", pdx->DebugName));
#endif
ctx->status = STATUS_SUCCESS; // ignore status from POWER_SEQUENCE
status = STATUS_MORE_PROCESSING_REQUIRED;
continue;
} // RestoreSeqComplete
///////////////////////////////////////////////////////////////////////
// RestoreContext restores device context if necessary. It can be entered
// from RestoreSeqComplete or from DevPowerUpComplete
case RestoreContext:
{ // RestoreContext
// Allow the client driver to restore any device context. This occurs
// asynchronously. The client notifies us when the the restore operation
// finishes by calling GenericSaveRestoreComplete.
if (pdx->RestoreDeviceContext)
{ // restore context
SETSTATE(ContextRestorePending);
(*pdx->RestoreDeviceContext)(pdx->DeviceObject, ctx->oldpower, pdx->devpower, ctx);
break;
} // restore context
action = ContextRestoreComplete;
continue;
} // RestoreContext
///////////////////////////////////////////////////////////////////////
// InvalidAction is the action for any unexpected event. It should never occur.
case InvalidAction:
default:
status = STATUS_UNSUCCESSFUL;
break;
} // perform next action
break; // for cases that "break" from the switch
} // handle this event
#if DBG
// Check to make sure the state got changed before we exit
if (nextstate == originalstate)
KdPrint(("%s - State didn't change from %d in HandlePowerEvent!\n", pdx->DebugName, originalstate));
// Check to make sure a valid status will be returned
if (status == -1)
KdPrint(("%s - returned status didn't change in HandlePowerEvent!\n", pdx->DebugName));
#endif // DBG
POWTRACE(("%s - %d.%d returning %X, state is %s\n", pdx->DebugName, ctxid, eventid, status, powstatenames[nextstate]));
return status;
} // HandlePowerEvent
///////////////////////////////////////////////////////////////////////////////
NTSTATUS MainCompletionRoutine(PDEVICE_OBJECT junk, PIRP Irp, PPOWCONTEXT ctx)
{ // MainCompletionRoutine
ctx->status = Irp->IoStatus.Status;
return HandlePowerEvent(ctx, MainIrpComplete);
} // MainCompletionRoutine
NTSTATUS SequenceCompletionRoutine(PDEVICE_OBJECT junk, PIRP Irp, PPOWCONTEXT ctx)
{ // SequenceCompletionRoutine
ctx->status = Irp->IoStatus.Status;
HandlePowerEvent(ctx, AsyncNotify);
IoFreeIrp(Irp);
return STATUS_MORE_PROCESSING_REQUIRED;
} // SequenceCompletionRoutine
VOID PoCompletionRoutine(PDEVICE_OBJECT junk, UCHAR fcn, POWER_STATE state, PPOWCONTEXT ctx, PIO_STATUS_BLOCK pstatus)
{ // PoCompletionRoutine
ctx->status = pstatus->Status;
HandlePowerEvent(ctx, AsyncNotify);
} // PoCompletionRoutine
VOID PassivePowerCall(PIRP Irp)
{ // PassivePowerCall
PoCallDriver(IoGetNextIrpStackLocation(Irp)->DeviceObject, Irp);
} // PassivePowerCall
NTSTATUS SafePoCallDriver(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{ // SafePoCallDriver
// If running in Win2K, or if Win98 and already at PASSIVE_LEVEL, just call
// PoCallDriver.
if (!win98 || KeGetCurrentIrql() == PASSIVE_LEVEL)
return PoCallDriver(DeviceObject, Irp);
// Win98's PoCallDriver is the same as IoCallDriver, and it won't do anything to
// present the IRP at passive level if we're currently above. Build a work queue
// item in the DriverContext field of the IRP and queue the work item so we
// can present the IRP properly. Boy, is this something we shouldn't have to
// worry about!
IoMarkIrpPending(Irp); // be sure it's marked pending
IoGetNextIrpStackLocation(Irp)->DeviceObject = DeviceObject; // so PassivePowerCall can find it
// Note: The ExXxxWorkItem functions are deprecated for Win2K and WinXp but
// okay for win98. This code only executes in Win98, so these should be okay
#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) PassivePowerCall, (PVOID) Irp);
ExQueueWorkItem(item, CriticalWorkQueue);
#pragma warning(default:4995)
#pragma warning(default:4996)
return STATUS_PENDING;
} // SafePoCallDriver
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -