⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 power.cpp

📁 WDM驱动程序的范例程序
💻 CPP
📖 第 1 页 / 共 5 页
字号:
	NTSTATUS status = IoOpenDeviceRegistryKey(pdx->Pdo, PLUGPLAY_REGKEY_DEVICE, KEY_ALL_ACCESS, &hkey);
	if (!NT_SUCCESS(status))
		{
		KdPrint((DRIVERNAME " - Error %X trying to open registry key\n", status));
		return status;
		}

	UNICODE_STRING valname;
	RtlInitUnicodeString(&valname, L"PowerSettings");

	status = ZwSetValueKey(hkey, &valname, 0, REG_BINARY, pip, 2 * sizeof(ULONG));
	if (!NT_SUCCESS(status))
		KdPrint((DRIVERNAME " - ZwSetValueKey failed - %X\n", status));

	ZwClose(hkey);
	return status;
	}							// WritePowerInfoToRegistry

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

#pragma LOCKEDCODE

NTSTATUS MainCompletionRoutine(PDEVICE_OBJECT junk, PIRP Irp, PPOWCONTEXT ctx);
NTSTATUS SequenceCompletionRoutine(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);
VOID PassivePowerComplete(PPOWCONTEXT ctx) {HandlePowerEvent(ctx, AsyncNotify);}

NTSTATUS HandlePowerEvent(PPOWCONTEXT ctx, enum POWEVENT event)
	{							// HandlePowerEvent
	NTSTATUS status = -1;		// an invalid value
	ASSERT(ctx);
	ASSERT((ULONG) event < NUMPOWEVENTS);

	PIRP Irp = ctx->irp;		// could be null if we did an early S0 completion
	PIO_STACK_LOCATION stack = Irp ? IoGetCurrentIrpStackLocation(Irp) : NULL;

	PGENERIC_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
		SendPowerSequence,		// create and send POWER_SEQUENCE request
		SaveSeqComplete,		// sequence fetch after context save complete
		ForwardDevDown,			// forward device set-power down IRP
		RestoreSeqComplete,		// sequence fetch before context save complete
		RestoreContext,			// restore device context
		};

#ifdef VERBOSETRACE
	static char* powstatenames[] = {
		"InitialState",
		"SysPowerUpPending",
		"SubPowerUpPending",
		"SubPowerDownPending",
		"SysPowerDownPending",
		"DevPowerUpPending",
		"DevPowerDownPending",
		"ContextSavePending",
		"ContextRestorePending",
		"DevQueryUpPending",
		"DevQueryDownPending",
		"QueueStallPending",
		"SaveSeqPending",
		"RestoreSeqPending",
		"PassiveCompletePending",
		"FinalState",
		};

	static char* eventnames[] = {
		"NewIrp",
		"MainIrpComplete",
		"AsyncNotify",
		};

	static char* actionnames[] = {
		"InvalidAction",
		"TriageNewIrp",
		"QueueStallComplete",
		"ForwardMainIrp",
		"SysPowerUpComplete",
		"SysPowerDownComplete",
		"SelectDState",
		"SendDeviceIrp",
		"CompleteMainIrp",
		"DestroyContext",
		"SubPowerUpComplete",
		"SubPowerDownComplete",
		"DevPowerUpComplete",
		"SaveContext",
		"ContextSaveComplete",
		"ContextRestoreComplete",
		"DevQueryUpComplete",
		"DevQueryDown",
		"DevQueryDownComplete",
		"SendPowerSequence",
		"SaveSeqComplete",
		"ForwardDevDown",
		"RestoreSeqComplete",
		"RestoreContext",
		};
#endif // VERBOSETRACE

	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},
/* SaveSeqPending */		{InvalidAction,		InvalidAction,				SaveSeqComplete},
/* RestoreSeqPending */		{InvalidAction,		InvalidAction,				RestoreSeqComplete},
/* 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).

	// Note: avoid doing ASSERTs in this code because they're basically impossible to debug.
	// (Usually, the display is turned off before anything else, and then the serial port.
	// So the debugger is usually deaf and dumb when something goes wrong here.)

#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(("%s - %d.%d is %s in state %s\n", pdx->DebugName, ctxid, eventid, eventnames[event], powstatenames[originalstate]));

	while (TRUE)
		{						// handle this event
		POWTRACE(("%s -  %d.%d %s\n", pdx->DebugName, 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

			// 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.

			if (pdx->RemoveLock)
				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);

				DEVICE_POWER_STATE newstate = stack->Parameters.Power.State.DeviceState;

				// Conceivably, we could cancel any outstanding wait-wake when going to a D-state
				// that doesn't support wakeup. In practice, the bus driver will do this for us.

				// According to the DDK, we're supposed to call PoSetPowerState any time we
				// change the power state, *before* calling PoStartNextPowerIrp and at IRQL
				// < DISPATCH_LEVEL (except = DISPATCH_LEVEL is okay for D0). In fact, it seems
				// to make no difference whether we do this or not, and bus drivers seem to make
				// all the necessary calls. Therefore, don't bother.

				// If the device is currently sleeping or off, the queue should already be
				// stalled. If the device is in D0, however, it might be servicing a
				// request right now, and there might be requests queued up behind the
				// active one. StallRequestsAndNotify will stall the queue and establish
				// a callback for when the client driver calls StartNextPacket.

				if (pdx->nqueues && !pdx->StalledForPower)
					{			// stall request queue

					// We mustn't touch the context structure after calling a function that
					// might recursively invoke this routine...

					ctx->UnstallQueue = TRUE;
					pdx->StalledForPower = TRUE;

					UCHAR minor = stack->MinorFunction;
					DEVICE_POWER_STATE oldstate = pdx->devpower;

					NTSTATUS qstatus = StallAllRequestsAndNotify(pdx->queues, pdx->nqueues, GenericSaveRestoreComplete, ctx);
					if (!NT_SUCCESS(qstatus))
						{		// can't stall queue
						ctx->status = qstatus;
						action = CompleteMainIrp;
						ctx->UnstallQueue = FALSE;
						pdx->StalledForPower = FALSE;
						continue;
						}		// can't stall queue

					// The UnstallQueue flag tells CompleteMainIrp to unstall the queue as part
					// of the cleanup process. We may clear this flag somewhere along the way
					// in order to keep the queue stalled during a period of low power.

					POWTRACE(("%s -  %d.%d stalling queue\n", pdx->DebugName, ctxid, eventid));

					if (qstatus == STATUS_PENDING)
						{
						if (pdx->FlushPendingIo)
							(*pdx->FlushPendingIo)(pdx->DeviceObject, IRP_MJ_POWER, minor, oldstate, newstate);
						break;	// wait for notification that device is idle
						}
					}			// stall request queue

				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.

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -