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

📄 power.cpp

📁 Programming the Microsoft Windows Driver Model(2nd)
💻 CPP
字号:
// Illustrative power management routines for WAKEUP sample driver

// Copyright (C) 2002 by Walter Oney

// All rights reserved



#include "stddcls.h"

#include "driver.h"



VOID CancelSelectiveSuspend(PDEVICE_EXTENSION pdx);

NTSTATUS IdleDevice(PDEVICE_EXTENSION pdx);

NTSTATUS IssueSelectiveSuspendRequest(PDEVICE_EXTENSION pdx);

VOID SelectiveSuspendCallback(PDEVICE_EXTENSION pdx);

NTSTATUS SelectiveSuspendCompletionRoutine(PDEVICE_OBJECT junk, PIRP Irp, PDEVICE_EXTENSION pdx);



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

// CancelSelectiveSuspend cancels the outstanding selective-suspend IRP, if

// there is one. Refer to the Chapter 5 discussion about how to safely cancel

// an asynchronous IRP for an explanation



#pragma LOCKEDCODE



VOID CancelSelectiveSuspend(PDEVICE_EXTENSION pdx)

	{							// CancelSelectiveSuspend

	PIRP Irp = (PIRP) InterlockedExchangePointer((PVOID*) &pdx->SuspendIrp, NULL);

	if (Irp)

		{						// cancel irp

		KdPrint((DRIVERNAME " - Canceling SUBMIT_IDLE_NOTIFICATION request\n"));

		IoCancelIrp(Irp);

		if (InterlockedExchange(&pdx->SuspendIrpCancelled, 1))

			IoFreeIrp(Irp);

		}						// cancel irp

	}							// CancelSelectiveSuspend



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

// ChangeAutoPower is called by the WMI IRP handler whenever a change is made

// to the MSPower_DeviceEnable setting. TRUE indicates that we should depower

// the device when it's not in use. FALSE indicates that we should leave the

// device powered at all times.



// Since we're not really driving the device, this sample pretends that the device

// is *never* in use. A real driver would call GenericWakeupFromIdle and

// GenericIdleDevice from, e.g., the IRP_MJ_CREATE and IRP_MJ_CLOSE dispatch

// routines, respectively.



// If your driver doesn't support automatic power-down, you could leave out this

// routine and the support in WMI.CPP for MSPower_DeviceEnable.



#pragma PAGEDCODE



VOID ChangeAutoPower(PDEVICE_EXTENSION pdx, BOOLEAN enable)

	{							// ChangeAutoPower

	PAGED_CODE();

	if (enable == pdx->autopower)

		return;					// no change, so nothing to do

	pdx->autopower = enable;



	// If disabling auto-power behavior, put the device into D0.



	if (!enable)

		{

		CancelSelectiveSuspend(pdx);

		GenericWakeupFromIdle(pdx->pgx, TRUE);

		}



	// If enabling auto-power behavior, we want to depower the device.



	else

		IdleDevice(pdx);



	// Update registry parameter



	HANDLE hkey;

	NTSTATUS status = IoOpenDeviceRegistryKey(pdx->Pdo, PLUGPLAY_REGKEY_DEVICE, KEY_SET_VALUE, &hkey);

	if (!NT_SUCCESS(status))

		{

		KdPrint((DRIVERNAME " - IoOpenDeviceRegistryKey failed - %X\n", status));

		return;

		}



	UNICODE_STRING valname;

	RtlInitUnicodeString(&valname, L"DeviceEnable");



	ULONG value = enable;

	status = ZwSetValueKey(hkey, &valname, 0, REG_DWORD, &value, sizeof(value));

	if (!NT_SUCCESS(status))

		KdPrint((DRIVERNAME " - ZwSetValueKey failed - %X\n", status));

	ZwClose(hkey);

	}							// ChangeAutoPower



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

// ChangeWakeupEnable is called by the WMI IRP handler whenever a change is made

// to the MSPower_DeviceWakeEnable setting. TRUE indicates we should program the

// device to wake the sytem. FALSE means we should not.



// If your device doesn't support system wakeup, you could leave out this function

// and the support in WMI.CPP for MSPower_DeviceWakeEnable.



#pragma PAGEDCODE



VOID ChangeWakeupEnable(PDEVICE_EXTENSION pdx, BOOLEAN enable)

	{							// ChangeWakeupEnable

	PAGED_CODE();

	if (enable == pdx->wakeup)

		return;					// no change, so nothing to do

	pdx->wakeup = enable;



	// If enabling wakeup, make sure we're in the D0 state before proceeding

	// because USBHUB requires us to be in the D0 state when we issue IRP_MN_WAIT_WAKE.

	// If your driver doesn't support automatic power-down, you could leave

	// out this step.



	if (enable)

		GenericWakeupFromIdle(pdx->pgx, TRUE);	// i.e., wait for power up



	// Based on the enable/disable flag, either issue or cancel a WAIT_WAKE request.



	GenericWakeupControl(pdx->pgx, enable ? EnableWakeup : DisableWakeup);



	// If we've been told to automatically power down the device, turn the device off. 

	// If your driver doesn't support automatic power-down, you could leave out this step.



	if (pdx->autopower)

		IdleDevice(pdx);



	// Update registry parameter



	HANDLE hkey;

	NTSTATUS status = IoOpenDeviceRegistryKey(pdx->Pdo, PLUGPLAY_REGKEY_DEVICE, KEY_SET_VALUE, &hkey);

	if (!NT_SUCCESS(status))

		{

		KdPrint((DRIVERNAME " - IoOpenDeviceRegistryKey failed - %X\n", status));

		return;

		}



	UNICODE_STRING valname;

	RtlInitUnicodeString(&valname, L"DeviceWakeEnable");



	ULONG value = enable;

	status = ZwSetValueKey(hkey, &valname, 0, REG_DWORD, &value, sizeof(value));

	if (!NT_SUCCESS(status))

		KdPrint((DRIVERNAME " - ZwSetValueKey failed - %X\n", status));

	ZwClose(hkey);

	}							// ChangeWakeupEnable



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

// GetDevicePowerState returns the device power state we wish to enter for a

// given system power state. The USB bus driver seems to fail our wait_wake

// requests unless we're in the D0 state (I don't know why it should), so this

// function is here to force D0 in that case.



#pragma LOCKEDCODE



DEVICE_POWER_STATE GetDevicePowerState(PDEVICE_OBJECT fdo, SYSTEM_POWER_STATE sstate, DEVICE_POWER_STATE dstate)

	{							// GetDevicePowerState

	PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;

	if (sstate > PowerSystemWorking || !pdx->wakeup)

		return dstate;

	return PowerDeviceD0;

	}							// GetDevicePowerState



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



#pragma PAGEDCODE



NTSTATUS IdleDevice(PDEVICE_EXTENSION pdx)

	{							// IdleDevice

	if (IoIsWdmVersionAvailable(1, 0x20))

		return IssueSelectiveSuspendRequest(pdx);

	else

		return GenericIdleDevice(pdx->pgx, PowerDeviceD3, TRUE);

	}							// IdleDevice



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

// IssueSelectiveSuspendRequest issues the selective suspend request defined in

// XP and later operating systems.



#pragma PAGEDCODE



NTSTATUS IssueSelectiveSuspendRequest(PDEVICE_EXTENSION pdx)

	{							// IssueSelectiveSuspendRequest

	PAGED_CODE();



	// Out of an excess of caution, use a mutex to ensure that only one invocation

	// of this function can be active at one time. In this driver, there is only

	// one code path leading here, and that's through the WMI dispatch routine, which

	// is being serialized by WMI. But you might just lift this function into some

	// other environment where simultaneous calls would be possible...



	ExAcquireFastMutex(&pdx->SuspendMutex);

	if (pdx->SuspendIrp)

		{						// IRP already outstanding

		ExReleaseFastMutex(&pdx->SuspendMutex);

		return STATUS_SUCCESS;

		}						// IRP already outstanding



	PIRP Irp = IoAllocateIrp(pdx->LowerDeviceObject->StackSize, FALSE);

	if (!Irp)

		{

		ExReleaseFastMutex(&pdx->SuspendMutex);

		return STATUS_INSUFFICIENT_RESOURCES;

		}



	// Initialize the selective suspend IRP



	pdx->cbinfo.IdleCallback = (USB_IDLE_CALLBACK) SelectiveSuspendCallback;

	pdx->cbinfo.IdleContext = (PVOID) pdx;



	PIO_STACK_LOCATION stack = IoGetNextIrpStackLocation(Irp);

	stack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;

	stack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION;

	stack->Parameters.DeviceIoControl.Type3InputBuffer = &pdx->cbinfo;



	pdx->SuspendIrp = Irp;

	pdx->SuspendIrpCancelled = 0;



	ExReleaseFastMutex(&pdx->SuspendMutex);



	// Install a completion routine for this asynchronous IRP



	IoSetCompletionRoutine(Irp, (PIO_COMPLETION_ROUTINE) SelectiveSuspendCompletionRoutine,

		(PVOID) pdx, TRUE, TRUE, TRUE);



	// Send the IRP to the USB bus driver



	KdPrint((DRIVERNAME " - Issuing SUBMIT_IDLE_NOTIFICATION request\n"));

	IoCallDriver(pdx->LowerDeviceObject, Irp);



	// Return STATUS_SUCCESS to indicate that the IRP was issued. It may

	// have been completed with an error already, but that's not relevant

	// to our return code here.



	return STATUS_SUCCESS;

	}							// IssueSelectiveSuspendRequest



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

// GENERIC's power management code calls RestoreDeviceContext whenever power is

// restored. For purposes of this sample driver, we need to pretend to the system

// that the end user is present. With a real HID device, the regular class driver makes

// this call whenever an input event occurs (not just when the device powers on).

// If we didn't do this highly unusual thing here, the display wouldn't come on

// when you cause the system to wakeup using this driver.



#pragma LOCKEDCODE



VOID RestoreDeviceContext(PDEVICE_OBJECT fdo, DEVICE_POWER_STATE oldstate, DEVICE_POWER_STATE newstate, PVOID context)

	{							// RestoreDeviceContext

	PoSetSystemState(ES_USER_PRESENT);

	GenericSaveRestoreComplete(context);

	}							// RestoreDeviceContext



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

// The USB bus driver calls this callback routine to depower our device



#pragma PAGEDCODE



VOID SelectiveSuspendCallback(PDEVICE_EXTENSION pdx)

	{							// SelectiveSuspendCallback

	KdPrint((DRIVERNAME " - Selective suspend callback\n"));

	

	// Make sure any necessary wait-wake gets issued.



	GenericWakeupControl(pdx->pgx, ManageWaitWake);



	// Depower the device. In this driver, we're picking D3 unless wakeup is

	// enabled, in which case we pick D2. Note: these will *always* be the right values

	// for a USB device, which is the only kind of device for which this

	// selective suspend protocol is ever used.



	GenericIdleDevice(pdx->pgx, pdx->wakeup ? PowerDeviceD2 : PowerDeviceD3, TRUE);	// wait for it to finish

	}							// SelectiveSuspendCallback



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

// This is the I/O completion routine for the selective-suspend IRP. Its job is

// to free the IRP, which we created as an asynchronous IRP. See the discussion in 

// Chapter 5 about how to safely cancel an async IRP for an explanation...



#pragma LOCKEDCODE



NTSTATUS SelectiveSuspendCompletionRoutine(PDEVICE_OBJECT junk, PIRP Irp, PDEVICE_EXTENSION pdx)

	{							// SelectiveSuspendCompletionRoutine

	NTSTATUS status = Irp->IoStatus.Status;

	KdPrint((DRIVERNAME " - SUBMIT_IDLE_NOTIFICATION request completing - %X\n", status));



	// Free the IRP safely.



	if (InterlockedExchangePointer((PVOID*) &pdx->SuspendIrp, NULL)

		|| InterlockedExchange(&pdx->SuspendIrpCancelled, 1))

		IoFreeIrp(Irp);



	// If the IRP finished with an error status other than STATUS_POWER_STATE_INVALID,

	// repower the device because the hub driver may have left it depowered.



	if (!NT_SUCCESS(status) && status != STATUS_POWER_STATE_INVALID)

		GenericWakeupFromIdle(pdx->pgx, FALSE);	// mustn't block if at DISPATCH_LEVEL



	return STATUS_MORE_PROCESSING_REQUIRED;

	}							// SelectiveSuspendCompletionRoutine



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



#pragma PAGEDCODE



NTSTATUS StartDevice(PDEVICE_OBJECT fdo, PCM_PARTIAL_RESOURCE_LIST raw, PCM_PARTIAL_RESOURCE_LIST translated)

	{							// StartDevice

	PAGED_CODE();

	PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;



	// Examine device capabilities.



	PDEVICE_CAPABILITIES pdc = GenericGetDeviceCapabilities(pdx->pgx);

	if (pdc)

		{

		if (pdc->SystemWake <= PowerSystemWorking)

			{

			KdPrint((DRIVERNAME " - System wakeup not supported on this platform\n"));

			if (pdc->DeviceWake > PowerDeviceD0)

				KdPrint((DRIVERNAME " - However, this device can wake itself while system is working\n"));

			}

		if (pdc->DeviceWake == PowerDeviceUnspecified)

			KdPrint((DRIVERNAME " - Device wakeup not supported\n"));

		}



	// Use GENERIC to issue (or not) a WAIT_WAKE.



	GenericWakeupControl(pdx->pgx, pdx->wakeup ? EnableWakeup : DisableWakeup);



	// Set the initial power state of the device



	if (pdx->autopower && !pdx->wakeup)

		IdleDevice(pdx);



	return STATUS_SUCCESS;

	}							// StartDevice



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



#pragma PAGEDCODE



VOID StopDevice(IN PDEVICE_OBJECT fdo, BOOLEAN oktouch /* = FALSE */)

	{							// StopDevice

	PAGED_CODE();

	PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;



	// Disable wakeup while stopped. This is a requirement for the USB stack,

	// and possibly others too.



	GenericWakeupControl(pdx->pgx, DisableWakeup);



	// Cancel any selective-suspend IRP that might be outstanding



	CancelSelectiveSuspend(pdx);

	}							// StopDevice

⌨️ 快捷键说明

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