www.pudn.com > PCIConf.rar > Power.cpp


// Power request handler PCIConf driver 
// Copyright (C) 1999 by Walter Oney 
// All rights reserved 
 
#include "stddcls.h" 
#include "driver.h" 
 
NTSTATUS DefaultPowerHandler(PDEVICE_EXTENSION pdx, IN PIRP Irp); 
 
enum POWSTATE { 
	InitialState = 0,				// initial state of FSM 
	SysPowerUpPending,				// system power-up IRP forwarded 
	SubPowerUpPending,				// waiting for nested device power up to finish 
	SubPowerDownPending,			// waiting from device to power down before forwarding system power-down IRP 
	SysPowerDownPending,			// waiting for system power-down IRP to finish 
	DevPowerUpPending,				// waiting for device power-up IRP 
	DevPowerDownPending,			// waiting for device power-down IRP 
	ContextSavePending,				// context save is underway 
	ContextRestorePending,			// context restore is underway 
	DevQueryUpPending,				// device query for power-up pending 
	DevQueryDownPending,			// device query for power-down pending 
	QueueStallPending,				// waiting for device to be idle 
	FinalState,						// final state of FSM 
	NUMPOWSTATES, 
	}; 
 
enum POWEVENT { 
	NewIrp = 0,						// new query/set IRP 
	MainIrpComplete,				// the main IRP has finished 
	AsyncNotify,					// some other event has occurred 
	NUMPOWEVENTS, 
	}; 
 
typedef struct _POWCONTEXT { 
	PDEVICE_EXTENSION pdx;			// our own device extension 
	PIRP irp;						// the IRP we're processing 
	enum POWSTATE state;			// current state of FSM 
	NTSTATUS status;				// completion status for main IRP 
	DEVICE_POWER_STATE devstate;	// device power state to use 
	DEVICE_POWER_STATE oldpower;	// previous device power state 
	UCHAR MinorFunction;			// minor function to use in requested power IRP 
	} POWCONTEXT, *PPOWCONTEXT; 
 
NTSTATUS HandlePowerEvent(PPOWCONTEXT ctx, enum POWEVENT event); 
 
									   
/////////////////////////////////////////////////////////////////////////////// 
 
#pragma PAGEDCODE 
 
NTSTATUS DispatchPower(IN PDEVICE_OBJECT fdo, IN PIRP Irp) 
	{							// DispatchPower 
	PAGED_CODE(); 
	PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension; 
	NTSTATUS status = IoAcquireRemoveLock(&pdx->RemoveLock, Irp); 
	if (!NT_SUCCESS(status)) 
		return CompleteRequest(Irp, status, 0); 
 
	PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp); 
	ASSERT(stack->MajorFunction == IRP_MJ_POWER); 
 
	ULONG fcn = stack->MinorFunction; 
 
	if (fcn == IRP_MN_SET_POWER || fcn == IRP_MN_QUERY_POWER) 
		{						// handle set/query 
 
			{					// launch FSM 
			PPOWCONTEXT ctx = (PPOWCONTEXT) ExAllocatePool(NonPagedPool, sizeof(POWCONTEXT)); 
			if (!ctx) 
				{ 
				KdPrint((DRIVERNAME " - Can't allocate power context structure\n")); 
				status = CompleteRequest(Irp, STATUS_INSUFFICIENT_RESOURCES); 
				} 
			else 
				{				// process this IRP 
				RtlZeroMemory(ctx, sizeof(POWCONTEXT)); 
				ctx->pdx = pdx; 
				ctx->irp = Irp; 
				status = HandlePowerEvent(ctx, NewIrp); 
				}				// process this IRP 
			}					// launch FSM 
		}						// handle set/query 
 
	else 
		{						// handle other power request 
			status = DefaultPowerHandler(pdx, Irp); 
		}						// handle other power request 
 
	IoReleaseRemoveLock(&pdx->RemoveLock, Irp); 
	return status; 
	}							// DispatchPower 
 
/////////////////////////////////////////////////////////////////////////////// 
/////////////////////////////////////////////////////////////////////////////// 
 
NTSTATUS DefaultPowerHandler(PDEVICE_EXTENSION pdx, IN PIRP Irp) 
	{							// DefaultPowerHandler 
	PoStartNextPowerIrp(Irp);	// must be done while we own the IRP 
	IoSkipCurrentIrpStackLocation(Irp); 
	return PoCallDriver(pdx->LowerDeviceObject, Irp); 
	}							// DefaultPowerHandler 
 
/////////////////////////////////////////////////////////////////////////////// 
 
VOID SendAsyncNotification(PVOID context) 
	{							// SendAsyncNotification 
	HandlePowerEvent((PPOWCONTEXT) context, AsyncNotify); 
	}							// SendAsyncNotification 
 
/////////////////////////////////////////////////////////////////////////////// 
 
struct SDSP_CONTEXT { 
	PKEVENT pev;				// event to signal when request complete 
	NTSTATUS status;			// ending status 
	}; 
 
#pragma LOCKEDCODE 
 
VOID SendDeviceSetPowerComplete(PDEVICE_OBJECT junk, UCHAR fcn, POWER_STATE state, SDSP_CONTEXT* context, PIO_STATUS_BLOCK pstatus) 
	{							// SendDeviceSetPowerComplete 
	context->status = pstatus->Status; 
	KeSetEvent(context->pev, EVENT_INCREMENT, FALSE); 
	}							// SendDeviceSetPowerComplete 
 
NTSTATUS SendDeviceSetPower(PDEVICE_EXTENSION pdx, DEVICE_POWER_STATE devpower, BOOLEAN wait /* = FALSE */) 
	{							// SendDeviceSetPower 
	POWER_STATE state; 
	state.DeviceState = devpower; 
	NTSTATUS status; 
 
	if (wait) 
		{						// synchronous operation 
		KEVENT event; 
		KeInitializeEvent(&event, NotificationEvent, FALSE); 
		SDSP_CONTEXT context = {&event}; 
		status = PoRequestPowerIrp(pdx->Pdo, IRP_MN_SET_POWER, state, 
			(PREQUEST_POWER_COMPLETE) SendDeviceSetPowerComplete, &context, NULL); 
		if (status == STATUS_PENDING) 
			{ 
			KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); 
			status = context.status; 
			} 
		}						// synchronous operation 
	else 
		status = PoRequestPowerIrp(pdx->Pdo, IRP_MN_SET_POWER, state, NULL, NULL, NULL); 
	 
	return status; 
	}							// SendDeviceSetPower 
 
/////////////////////////////////////////////////////////////////////////////// 
/////////////////////////////////////////////////////////////////////////////// 
 
NTSTATUS MainCompletionRoutine(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); 
 
NTSTATUS HandlePowerEvent(PPOWCONTEXT ctx, enum POWEVENT event) 
	{							// HandlePowerEvent 
	NTSTATUS status = -1;		// an invalid value 
	ASSERT(ctx); 
	ASSERT((ULONG) event < NUMPOWEVENTS); 
 
	PIRP Irp = ctx->irp; 
	PIO_STACK_LOCATION stack = Irp ? IoGetCurrentIrpStackLocation(Irp) : NULL; 
 
	PDEVICE_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 
		}; 
 
	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}, 
/* 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). 
 
#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 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. 
 
		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; 
			}					// 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; 
			 
			// TODO Replace the "if (FALSE)" statement block immediately below with code 
			// to initiate an asynchronous process to restore device context information 
			// that was saved when power was lost. ctx->oldpower is the previous power 
			// state, and pdx->devpower is the new power state. When this process finishes, 
			// call SendAsyncNotification with "ctx" as the argument 
 
			if (FALSE) 
				{				// restore context 
				SETSTATE(ContextRestorePending); 
				// right here is where you start restoring context data 
				break; 
				}				// restore context 
 
			action = ContextRestoreComplete; 
			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; 
 
			ASSERT(stack->MinorFunction == IRP_MN_SET_POWER); // query should have gone directly to CompleteMainIrp from DevPowerUpComplete 
 
			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 
			ASSERT(stack->MinorFunction == IRP_MN_SET_POWER); 
			DEVICE_POWER_STATE devpower = stack->Parameters.Power.State.DeviceState; 
 
			// TODO Replace the following block of code with code that initiates 
			// an asynchronous process to save an relevant device context. When 
			// that process finishes, call SendAsyncNotification with "ctx" as 
			// the argument. 
 
			if (FALSE && devpower > pdx->devpower) 
				{				// save context 
				SETSTATE(ContextSavePending); 
				// put code here to start saving context 
				break; 
				}				// 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 
 
			SETSTATE(DevPowerDownPending); 
			action = ForwardMainIrp; 
 
			ASSERT(stack); 
			DEVICE_POWER_STATE devpower = stack->Parameters.Power.State.DeviceState; 
			if (devpower <= pdx->devpower) 
				continue;		// no actual change in device power 
 
			ASSERT(stack->MinorFunction == IRP_MN_SET_POWER); 
 
			pdx->devpower = devpower; 
 
			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)) 
				{				// ask client if change okay 
 
				// TODO replace the FALSE immediately below with some non-blocking test 
				// to see if it's okay to restore power. It would be very unusual to 
				// say "no". In making the test, pdx->devpower is the current power 
				// state and stack->Parameters.Power.State.DeviceState is the 
				// proposed new state. 
 
				if (FALSE) 
					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; 
 
			// TODO replace the FALSE immediately below with some non-blocking test 
			// to see if it's okay to remove power. In making the test, pdx->devpower 
			// is the current power state and devpower is the proposed new state. 
 
			if (devpower > pdx->devpower && FALSE) 
				{				// 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 
 
			action = CompleteMainIrp; 
			continue; 
			}					// DevQueryDownComplete 
 
		/////////////////////////////////////////////////////////////////////// 
		// InvalidAction is the action for any unexpected event. It should never occur. 
 
		case InvalidAction: 
		default: 
			ASSERT(FALSE); 
			status = STATUS_UNSUCCESSFUL; 
			break; 
			}					// perform next action 
 
		break;					// for cases that "break" from the switch 
		}						// handle this event 
 
	// Check to make sure the state got changed before we exit 
		 
	ASSERT(nextstate != originalstate); 
 
	// Check to make sure a valid status will be returned 
 
	ASSERT(status != -1); 
 
	return status; 
	}							// HandlePowerEvent 
 
/////////////////////////////////////////////////////////////////////////////// 
 
NTSTATUS MainCompletionRoutine(PDEVICE_OBJECT junk, PIRP Irp, PPOWCONTEXT ctx) 
	{							// MainCompletionRoutine 
	ctx->status = Irp->IoStatus.Status; 
	return HandlePowerEvent(ctx, MainIrpComplete); 
	}							// MainCompletionRoutine 
 
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 
 
	PWORK_QUEUE_ITEM item = (PWORK_QUEUE_ITEM) Irp->Tail.Overlay.DriverContext; 
	ExInitializeWorkItem(item, (PWORKER_THREAD_ROUTINE) PassivePowerCall, (PVOID) Irp); 
	ExQueueWorkItem(item, DelayedWorkQueue); 
 
	return STATUS_PENDING; 
	}							// SafePoCallDriver