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

📄 power.cpp

📁 Programming the Microsoft Windows driver model.2nd 随书光盘。内有很多作者送的实用工具和随书源码。WDM编程
💻 CPP
📖 第 1 页 / 共 5 页
字号:


NTSTATUS WritePowerInfoToRegistry(PGENERIC_EXTENSION pdx, PPOWERINFO pip)

	{							// WritePowerInfoToRegistry

	HANDLE hkey;

	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;

⌨️ 快捷键说明

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