📄 pmdevsample.c
字号:
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft end-user
// license agreement (EULA) under which you licensed this SOFTWARE PRODUCT.
// If you did not accept the terms of the EULA, you are not authorized to use
// this source code. For a copy of the EULA, please see the LICENSE.RTF on your
// install media.
//
/*++
Module Name:
pmdevsample.c
Abstract:
Sample power managed device driver.
Notes:
This device driver doesn't talk to any actual devices. It is intended
to provide a simple example of how to write a real power managed driver.
PDX --> Power-managed Device Example
Revision History:
--*/
#include <windows.h>
#include <devload.h>
#include <pm.h>
#include <nkintr.h>
#ifndef DEBUG
#define DEBUG // always turn on debug output
#endif // DEBUG
#ifdef DEBUG
#define DEBUGMASK(bit) (1 << (bit))
#define MASK_ERROR DEBUGMASK(0)
#define MASK_WARN DEBUGMASK(1)
#define MASK_INIT DEBUGMASK(2)
#define MASK_FUNCTION DEBUGMASK(3)
#define MASK_IOCTL DEBUGMASK(4)
#define MASK_DEVICE DEBUGMASK(5)
#define MASK_ACTIVITY DEBUGMASK(6)
DBGPARAM dpCurSettings = {
_T("PMSampleDev"),
{
_T("Errors"), _T("Warnings"), _T("Init"), _T("Function"),
_T("Ioctl"), _T("Device"), _T("Activity"), _T(""),
_T(""),_T(""),_T(""),_T(""),
_T(""),_T(""),_T(""),_T("")
},
MASK_ERROR | MASK_WARN | MASK_INIT | MASK_IOCTL | MASK_DEVICE
};
#define ZONE_ERROR DEBUGZONE(0)
#define ZONE_WARN DEBUGZONE(1)
#define ZONE_INIT DEBUGZONE(2)
#define ZONE_FUNCTION DEBUGZONE(3)
#define ZONE_IOCTL DEBUGZONE(4)
#define ZONE_DEVICE DEBUGZONE(5)
#define ZONE_ACTIVITY DEBUGZONE(6)
#endif
// this structure keeps track of each device instance
typedef struct _DeviceState_tag {
CRITICAL_SECTION csDevice; // serialize access to this device's state
HANDLE hevStop; // when signaled, the device thread exits
HANDLE htDevice; // device thread handle
HANDLE htActivity; // activity generating thread
TCHAR szName[16]; // should be of the format"PDXn" (no colon)
CEDEVICE_POWER_STATE CurrentDx; // current power level
DWORD dwInactivityTimeout; // in ms
DWORD dwMaxActivityTimeout; // in ms
BOOL fBoostRequested; // TRUE if we request a power state increase
BOOL fReductionRequested; // TRUE if we request a power state decrease
} DEVICESTATE, *PDEVICESTATE;
#define LOCK(pd) EnterCriticalSection(&(pd)->csDevice)
#define UNLOCK(pd) LeaveCriticalSection(&(pd)->csDevice)
#define dim(x) (sizeof(x) / sizeof(x[0]))
#define MAXDEVICES 1
#define SYSERRORSTRING(s) SysErrorString((s), szErrorString, dim(szErrorString))
// global variables
DEVICESTATE gDevices[MAXDEVICES];
INT giDevices = 0;
HANDLE ghevResume = NULL;
// this routine returns a pointer to an error string for a given GetLastError() value.
// See public\common\sdk\winerror.h for error codes.
LPTSTR
SysErrorString(DWORD dwStatus, LPTSTR pszBuf, DWORD nSize)
{
if(FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwStatus, 0, pszBuf, nSize, NULL) == 0) {
_tcsncpy(pszBuf, _T("<FormatMessage() failed>"), nSize);
}
return pszBuf;
}
// This thread imitates a device that is somewhat intelligent about its usage patterns.
//
// During periods of inactivity, it will reduce step down its power consumption by
// one level every INACTIVITY_TIMEOUT seconds until it gets to D3 (sleep). That is,
// it won't turn itself all the way off as a result of inactivity. The device must notify
// the Power Manager of each self-initiated state transition; the power manager will
// not allow the change unless it fits within the boundaries set up by system power
// state configuration and requirements imposed by applications. Although this one
// does note, some devices may choose to attempt to go into D3 or D4 automatically.
//
// When the device detects activity it will attempt to set its power level
// to the highest it supports, which is D0.
//
// To keep things interesting, let's assume that this driver doesn't support D3. Requests
// to go to D3 will actually turn the device off (D4).
DWORD WINAPI
DeviceThreadProc(PVOID pvParam)
{
PDEVICESTATE pds = (PDEVICESTATE) pvParam;
TCHAR szBuf[128], szErrorString[256];
HANDLE hevActivity, hev[3];
BOOL fDone = FALSE;
DWORD dwTimeout;
LPTSTR pszFname = _T("DeviceThreadProc");
UNREFERENCED_PARAMETER(szErrorString[0]);
// create an event that indicates something is happening with our device. This
// might normally be an interrupt event, and this thread might be our IST. This
// sample program relies on the user to set this event manually. Note that a
// real IST can't call WaitForMultipleObjects() on an interrupt event; we do it
// here to make the code easier to read.
_stprintf(szBuf, _T("PmDevSampleActivityEvent_%s"), pds->szName);
szBuf[_tcslen(szBuf) - 1] = 0; // remove the trailing colon
hevActivity = CreateEvent(NULL, FALSE, FALSE, szBuf);
if(hevActivity == NULL) {
DEBUGMSG(ZONE_ERROR, (_T("%s: CreateEvent('%s') failed %d\r\n"), pszFname, szBuf, GetLastError()));
return 1;
}
DEBUGMSG(ZONE_DEVICE, (_T("%s(%x): activity event name is '%s'\r\n"), pszFname, pds, szBuf));
// wait until we're told to exit or until something happens
hev[0] = hevActivity;
hev[1] = pds->hevStop;
hev[2] = ghevResume;
dwTimeout = pds->dwInactivityTimeout;
_stprintf(szBuf, _T("DeviceThreadProc(%08x)"), pds);
pszFname = szBuf;
DEBUGMSG(ZONE_DEVICE, (_T("%s: entering device loop, device name is '%s'\r\n"), pszFname, pds->szName));
while(!fDone) {
DWORD dwStatus = WaitForMultipleObjects(dim(hev), hev, FALSE, dwTimeout);
switch(dwStatus) {
case WAIT_TIMEOUT:
LOCK(pds);
#if 0
// if we're "off" don't display a timeout message
if(pds->CurrentDx != D4) {
#endif
DEBUGMSG(ZONE_DEVICE, (_T("%s: inactivity timeout, Dx is %d, fBoost %d, fReduction %d\r\n"),
pszFname, pds->CurrentDx, pds->fBoostRequested, pds->fReductionRequested));
#if 0
}
#endif
// we don't want to automatically go all the way off (D4), or to "sleep" (D3), but in other cases
// step down our power consumption one level
if(pds->fReductionRequested == FALSE && pds->CurrentDx < D2) {
CEDEVICE_POWER_STATE NewDx = (CEDEVICE_POWER_STATE) ((DWORD) pds->CurrentDx + 1);
pds->fReductionRequested = TRUE;
dwStatus = DevicePowerNotify(pds->szName, NewDx, POWER_NAME);
if(dwStatus != ERROR_SUCCESS) {
DEBUGMSG(ZONE_DEVICE | ZONE_WARN, (_T("%s: DevicePowerNotify(%u) failed %d ('%s')\r\n"), pszFname, NewDx,
dwStatus, SYSERRORSTRING(dwStatus)));
}
}
// don't need to maintain an inactivity timeout if we've gotten to our lowest power state
if(pds->CurrentDx >= D2) {
dwTimeout = INFINITE;
}
UNLOCK(pds);
break;
case (WAIT_OBJECT_0 + 0):
LOCK(pds);
DEBUGMSG(ZONE_DEVICE, (_T("%s: device activity, Dx is %d, fBoost %d, fReduction %d\r\n"),
pszFname, pds->CurrentDx, pds->fBoostRequested, pds->fReductionRequested));
// are we at our top activity level?
if(pds->fBoostRequested == FALSE && pds->CurrentDx != D0) {
// no, try to go to D0
pds->fBoostRequested = TRUE;
dwStatus = DevicePowerNotify(pds->szName, D0, POWER_NAME);
if(dwStatus != ERROR_SUCCESS) {
DEBUGMSG(ZONE_DEVICE | ZONE_WARN, (_T("%s: DevicePowerNotify(D0) failed %d ('%s')\r\n"), pszFname,
dwStatus, SYSERRORSTRING(dwStatus)));
}
}
dwTimeout = pds->dwInactivityTimeout;
UNLOCK(pds);
break;
case (WAIT_OBJECT_0 + 1):
DEBUGMSG(ZONE_DEVICE, (_T("%s: stop event set, exiting...\r\n"), pszFname));
fDone = TRUE;
break;
case (WAIT_OBJECT_0 + 2):
DEBUGMSG(ZONE_DEVICE, (_T("%s: system resuming\r\n"), pszFname));
break;
default:
DEBUGMSG(ZONE_ERROR, (_T("%s: WaitForMultipleObjects() returned %u, error %u\r\n"), pszFname, dwStatus, GetLastError()));
fDone = TRUE;
break;
}
}
// release resources
CloseHandle(hevActivity);
DEBUGMSG(ZONE_DEVICE, (_T("%s: all done\r\n"), pszFname));
return 0;
}
// This thread waits a random amount of time before simulating device activity by setting
// the sample device's activity event. However, if the device is in D4 this thread stops
// simulating activity.
DWORD WINAPI
ActivityThreadProc(PVOID pvParam)
{
PDEVICESTATE pds = (PDEVICESTATE) pvParam;
TCHAR szBuf[128];
HANDLE hevActivity;
BOOL fDone = FALSE;
DWORD dwTimeout, dwStatus;
LPTSTR pszFname = _T("ActivityThreadProc");
// open the device activity event
_stprintf(szBuf, _T("PmDevSampleActivityEvent_%s"), pds->szName);
szBuf[_tcslen(szBuf) - 1] = 0; // remove the trailing colon
hevActivity = CreateEvent(NULL, FALSE, FALSE, szBuf);
if(hevActivity == NULL) {
DEBUGMSG(ZONE_ERROR, (_T("%s: CreateEvent('%s') failed %d\r\n"), pszFname, szBuf, GetLastError()));
return 1;
}
// wait until we're told to exit or until something happens
_stprintf(szBuf, _T("ActivityThreadProc(%08x)"), pds);
pszFname = szBuf;
DEBUGMSG(ZONE_ACTIVITY | ZONE_DEVICE, (_T("%s: entering device loop, device name is '%s'\r\n"), pszFname, pds->szName));
while(!fDone) {
if(pds->dwMaxActivityTimeout == INFINITE) {
dwTimeout = INFINITE;
} else {
dwTimeout = Random() % pds->dwMaxActivityTimeout;
}
DEBUGMSG(ZONE_ACTIVITY, (_T("%s: timeout %u ms\r\n"), pszFname, dwTimeout));
dwStatus = WaitForSingleObject(pds->hevStop, dwTimeout);
switch(dwStatus) {
case WAIT_TIMEOUT:
LOCK(pds);
if(pds->CurrentDx != D4) {
DEBUGMSG(ZONE_ACTIVITY, (_T("%s: generating activity\r\n"), pszFname));
SetEvent(hevActivity);
}
UNLOCK(pds);
break;
case WAIT_OBJECT_0:
DEBUGMSG(ZONE_ACTIVITY, (_T("%s: stop event set, exiting...\r\n"), pszFname));
fDone = TRUE;
break;
default:
DEBUGMSG(ZONE_ERROR, (_T("%s: WaitForSingleObject() returned %u, error %u\r\n"), pszFname, dwStatus, GetLastError()));
fDone = TRUE;
break;
}
}
// release resources
CloseHandle(hevActivity);
DEBUGMSG(ZONE_ACTIVITY, (_T("%s: all done\r\n"), pszFname));
return 0;
}
DWORD
PDX_Init(
PVOID Context
)
{
DWORD dwIndex, dwStatus, dwType, dwSize;
DWORD dwHandle = 0;
HKEY hkDevice;
TCHAR szName[16];
UNREFERENCED_PARAMETER(Context);
DEBUGMSG(ZONE_INIT, (_T("PDX_Init: context %s\n"), Context));
// get our activation information
dwStatus = RegOpenKeyEx(HKEY_LOCAL_MACHINE, (LPCTSTR) Context, 0, 0, &hkDevice);
if(dwStatus != ERROR_SUCCESS) {
DEBUGMSG(ZONE_ERROR, (_T("PDX_Init: OpenDeviceKey('%s') failed %u\r\n"), Context, dwStatus));
return 0;
}
dwSize = sizeof(szName);
dwStatus = RegQueryValueEx(hkDevice, DEVLOAD_DEVNAME_VALNAME, NULL, &dwType, (LPBYTE) szName, &dwSize);
if(dwStatus != ERROR_SUCCESS || dwType != DEVLOAD_DEVNAME_VALTYPE) {
DEBUGMSG(ZONE_ERROR, (_T("PDX_Init: RegQueryValueEx('%s', '%s') failed %u\r\n"),
Context, DEVLOAD_DEVNAME_VALNAME, dwStatus));
RegCloseKey(hkDevice);
return 0;
}
DEBUGMSG(ZONE_INIT, (_T("PDX_Init: device name is '%s'\r\n"), szName));
RegCloseKey(hkDevice);
// look for a free device instance
for(dwIndex = 0; dwIndex < dim(gDevices) && dwHandle == 0; dwIndex++) {
PDEVICESTATE pds = &gDevices[dwIndex];
if(pds->htDevice == NULL) {
// initialize this device instance
InitializeCriticalSection(&pds->csDevice);
_tcsncpy(pds->szName, szName, dim(pds->szName));
pds->dwInactivityTimeout = 2000;
pds->dwMaxActivityTimeout = 15000;
// override defaults with registry settings, if present
hkDevice = OpenDeviceKey(Context);
if(hkDevice != NULL) {
DWORD dwVal;
// inactivity timeout
dwSize = sizeof(dwVal);
dwStatus = RegQueryValueEx(hkDevice, _T("InactivityTimeout"), NULL, &dwType, (LPBYTE) &dwVal, &dwSize);
if(dwStatus == ERROR_SUCCESS && dwType == REG_DWORD) {
pds->dwInactivityTimeout = dwVal;
}
// activity generation timeout
dwSize = sizeof(dwVal);
dwStatus = RegQueryValueEx(hkDevice, _T("MaxActivityTimeout"), NULL, &dwType, (LPBYTE) &dwVal, &dwSize);
if(dwStatus == ERROR_SUCCESS && dwType == REG_DWORD) {
pds->dwMaxActivityTimeout = dwVal;
}
RegCloseKey(hkDevice);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -