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

📄 power.cpp

📁 一本在讲述USB驱动程式的书 及其范例原码
💻 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 + -