📄 pmdevice.cpp
字号:
//
// 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.
//
//
// This module contains routines for keeping track of devices and
// managing device power.
//
#include <pmimpl.h>
// This routine determines how to map a device power state that the
// PM wants to use into the device power states that a particular
// device actually supports.
CEDEVICE_POWER_STATE
MapDevicePowerState(CEDEVICE_POWER_STATE newDx, UCHAR ucSupportedDx)
{
CEDEVICE_POWER_STATE reqDx;
SETFNAME(_T("MapDevicePowerState"));
DEBUGCHK(newDx >= D0 && newDx <= D4);
// map the power level to whatever the device actually supports. All devices
// are required to support D0 so we won't even check for it.
reqDx = newDx;
if(reqDx == D3 && (ucSupportedDx & (1 << reqDx)) == 0) reqDx = D4;
if(reqDx == D4) {
if((ucSupportedDx & (1 << reqDx)) == 0) reqDx = D3;
if((ucSupportedDx & (1 << reqDx)) == 0) reqDx = D2;
}
if(reqDx == D2 && (ucSupportedDx & (1 << reqDx)) == 0) reqDx = D1;
if(reqDx == D1 && (ucSupportedDx & (1 << reqDx)) == 0) reqDx = D0;
PMLOGMSG(ZONE_DEVICE, (_T("%s: mapping D%d to D%d\r\n"), pszFname, newDx, reqDx));
return reqDx;
}
// This routine actually tells a device to update its current power state. It
// returns TRUE if successful, FALSE otherwise. Note that devices don't always
// update their power state to the level that the PM wants. Some devices may
// not support all power states, for example. Devices are responsible for mapping
// PM requests to device power levels that they actually support.
DWORD
SetDevicePower(PDEVICE_STATE pds, CEDEVICE_POWER_STATE newDx, BOOL fForceSet = FALSE)
{
DWORD dwStatus = ERROR_SUCCESS;
static DWORD dwStaticRefCount = 0;
DWORD dwCurRefCount = 0;
CEDEVICE_POWER_STATE reqDx;
CEDEVICE_POWER_STATE oldActualDx, oldCurDx;
POWER_RELATIONSHIP pr;
PPOWER_RELATIONSHIP ppr = NULL;
BOOL fDoSet;
BOOL fOpenDevice = FALSE;
HANDLE hDevice = INVALID_HANDLE_VALUE;
SETFNAME(_T("SetDevicePower"));
PREFAST_DEBUGCHK(pds != NULL);
DEBUGCHK( newDx >= D0 && newDx <= D4);
DEBUGCHK(pds->pInterface != NULL);
// map the power level to whatever the device actually supports
reqDx = MapDevicePowerState(newDx, pds->caps.DeviceDx);
PMLOGMSG(ZONE_DEVICE, (_T("%s: setting '%s' to D%d (mapped to D%d), fForceSet is %d\r\n"),
pszFname, pds->pszName, newDx, reqDx, fForceSet));
// make a last check to see if we really need to send the device an update
PMLOCK();
DEBUGCHK(pds->dwNumPending == 0 || pds->pendingDx != PwrDeviceUnspecified);
DEBUGCHK(pds->dwNumPending != 0 || pds->pendingDx == PwrDeviceUnspecified);
if(reqDx != pds->actualDx || pds->dwNumPending != 0 || fForceSet) {
fDoSet = TRUE;
// record what we're trying to set, visible to other threads
pds->pendingDx = reqDx;
pds->dwNumPending++;
// Re-Enter Checking Count.
dwStaticRefCount ++ ;
dwCurRefCount = dwStaticRefCount;
// remember what we're trying to set in this thread
oldCurDx = pds->curDx;
oldActualDx = pds->actualDx;
// get a handle to the device to make the request
hDevice = pds->hDevice;
if(hDevice != INVALID_HANDLE_VALUE) {
fOpenDevice = FALSE; // already have a handle
} else {
fOpenDevice = TRUE; // need to open a handle
}
} else {
fDoSet = FALSE;
}
PMUNLOCK();
// are we doing an update?
if(!fDoSet) {
PMLOGMSG(ZONE_DEVICE, (_T("%s: device is already at D%d\r\n"), pszFname, reqDx));
} else {
// initialize parameters
memset(&pr, 0, sizeof(pr));
if(pds->pParent != NULL) {
PMLOGMSG(ZONE_DEVICE, (_T("%s: parent of '%s' is '%s'\r\n"),
pszFname, pds->pszName, pds->pParent->pszName));
pr.hParent = (HANDLE) pds->pParent;
pr.pwsParent = pds->pParent->pszName;
pr.hChild = (HANDLE) pds;
pr.pwsChild = pds->pszName;
ppr = ≺
}
// get a handle to the device
if(fOpenDevice) {
DEBUGCHK(pds->pInterface->pfnOpenDevice != NULL);
hDevice = pds->pInterface->pfnOpenDevice(pds);
}
// did we get a handle?
if(hDevice == INVALID_HANDLE_VALUE) {
PMLOGMSG(ZONE_WARN, (_T("%s: couldn't open '%s'\r\n"), pszFname,
pds->pszName));
dwStatus = ERROR_INVALID_HANDLE;
} else {
DWORD dwBytesReturned;
DEBUGCHK(pds->pInterface->pfnRequestDevice != NULL);
BOOL fOk = pds->pInterface->pfnRequestDevice(hDevice, IOCTL_POWER_SET, ppr,
ppr == NULL ? 0 : sizeof(*ppr), &reqDx, sizeof(reqDx),
&dwBytesReturned);
if(fOk) {
// Check for races to update the driver -- it is possible for the device to call
// DevicePowerNotify() when another thread is calling SetDevicePower(),
// SetSystemPowerState(), or Set(/Release)PowerRequirement().
PMLOCK();
if(pds->pendingDx == reqDx) {
// record the new values
pds->curDx = newDx;
pds->actualDx = reqDx;
}
else if (dwCurRefCount != dwStaticRefCount || pds->dwNumPending > 1 ) {
PMLOGMSG(ZONE_DEVICE, (_T("%s: race detected on '%s', returning ERROR_RETRY\r\n"),
pszFname, pds->pszName));
dwStatus = ERROR_RETRY;
}
else { // This condition indicate no SetDevicePower called by others. It must be wrong value return from device driver
RETAILMSG(1, (_T("%s: Wrong Return Value from '%s', returning ERROR_GEN_FAILURE\r\n"),
pszFname, pds->pszName));
ASSERT(FALSE);
dwStatus = ERROR_GEN_FAILURE;
}
dwStaticRefCount ++ ;
PMUNLOCK();
} else {
dwStatus = GetLastError();
if(dwStatus == ERROR_SUCCESS) {
dwStatus = ERROR_GEN_FAILURE;
}
PMLOGMSG(ZONE_WARN, (_T("%s: '%s' failed IOCTL_POWER_SET D%d, status is %d\r\n"),
pszFname, pds->pszName, newDx, dwStatus));
PMLOCK();
// the set operation failed -- if the device power state appears unchanged,
// restore our state variables. In general, devices should never fail a
// set request.
if(reqDx == oldActualDx) {
pds->curDx = oldCurDx;
pds->actualDx = oldActualDx;
}
PMUNLOCK();
}
// close the device handle if we opened it in this routine
if(fOpenDevice) {
DEBUGCHK(pds->pInterface->pfnCloseDevice != NULL);
pds->pInterface->pfnCloseDevice(hDevice);
}
}
// update reference counts
PMLOCK();
DEBUGCHK(pds->dwNumPending != 0);
DEBUGCHK(pds->pendingDx != PwrDeviceUnspecified);
pds->dwNumPending--;
if(pds->dwNumPending == 0) {
pds->pendingDx = PwrDeviceUnspecified;
}
PMUNLOCK();
}
return dwStatus;
}
// This routine sends an IOCTL_POWER_GET to a device to obtain its current
// device power state. It returns ERROR_SUCCESS and fills in pCurDx if
// successful. Otherwise it returns an error code.
DWORD
GetDevicePower(PDEVICE_STATE pds, PCEDEVICE_POWER_STATE pCurDx)
{
DWORD dwStatus = ERROR_GEN_FAILURE;
POWER_RELATIONSHIP pr;
PPOWER_RELATIONSHIP ppr = NULL;
BOOL fOpenDevice;
HANDLE hDevice;
SETFNAME(_T("GetDevicePower"));
PREFAST_DEBUGCHK(pds != NULL );
PREFAST_DEBUGCHK(pCurDx != NULL);
PREFAST_DEBUGCHK(pds->pInterface != NULL);
// initialize parameters
memset(&pr, 0, sizeof(pr));
if(pds->pParent != NULL) {
PMLOGMSG(ZONE_DEVICE, (_T("%s: parent of '%s' is '%s'\r\n"),
pszFname, pds->pszName, pds->pParent->pszName));
pr.hParent = (HANDLE) pds->pParent;
pr.pwsParent = pds->pParent->pszName;
pr.hChild = (HANDLE) pds;
pr.pwsChild = pds->pszName;
ppr = ≺
}
// get a handle to the device
hDevice = pds->hDevice;
if(hDevice != INVALID_HANDLE_VALUE) {
fOpenDevice = FALSE; // already have a handle
} else {
fOpenDevice = TRUE; // need to open a handle
DEBUGCHK(pds->pInterface->pfnOpenDevice != NULL);
hDevice = pds->pInterface->pfnOpenDevice(pds);
}
// did we get a handle?
if(hDevice == INVALID_HANDLE_VALUE) {
PMLOGMSG(ZONE_WARN, (_T("%s: couldn't open '%s'\r\n"), pszFname,
pds->pszName));
} else {
CEDEVICE_POWER_STATE tmpDx = PwrDeviceUnspecified;
DWORD dwBytesReturned;
DEBUGCHK(pds->pInterface->pfnRequestDevice != NULL);
BOOL fOk = pds->pInterface->pfnRequestDevice(hDevice, IOCTL_POWER_GET, ppr,
ppr == NULL ? 0 : sizeof(*ppr), &tmpDx, sizeof(tmpDx),
&dwBytesReturned);
// update variables and clear the requestor thread ID
if(fOk) {
if(VALID_DX(tmpDx)) {
*pCurDx = tmpDx;
dwStatus = ERROR_SUCCESS;
} else {
PMLOGMSG(ZONE_WARN,
(_T("%s: '%s' IOCTL_POWER_GET returned invalid state %u\r\n"),
pszFname, pds->pszName, tmpDx));
dwStatus = ERROR_INVALID_DATA;
}
} else {
dwStatus = GetLastError();
}
PMLOGMSG(!fOk && ZONE_WARN,
(_T("%s: '%s' failed IOCTL_POWER_GET, status is %d\r\n"),
pszFname, pds->pszName, GetLastError()));
// close the device handle if we opened it in this routine
if(fOpenDevice) {
DEBUGCHK(pds->pInterface->pfnCloseDevice != NULL);
pds->pInterface->pfnCloseDevice(hDevice);
}
}
return dwStatus;
}
// This routine determines the mapping from a requested device power level
// to an actual new device power level. This is based on whether or not
// a device power level has been set by the user and what the current
// floor and ceiling device power levels are. This routine returns the new
// device power level, or PwrDeviceUnspecified if the device is already
// at the desired power level.
CEDEVICE_POWER_STATE
GetNewDeviceDx(CEDEVICE_POWER_STATE reqDx,
CEDEVICE_POWER_STATE curDx,
CEDEVICE_POWER_STATE setDx,
CEDEVICE_POWER_STATE floorDx,
CEDEVICE_POWER_STATE ceilingDx)
{
CEDEVICE_POWER_STATE newDx = reqDx;
DEBUGCHK(reqDx != PwrDeviceUnspecified);
DEBUGCHK(VALID_DX(reqDx));
// determine which power state state to put the device into as a result
// of this update. The device may already be in a valid state, in which
// case we don't need to do anything.
if(setDx != PwrDeviceUnspecified) {
newDx = setDx;
} else {
// no explicitly set power level, compare current values against
// boundaries
newDx = reqDx; // assume this setting is ok
if(newDx < ceilingDx) {
// lower power to the level of the ceiling
newDx = ceilingDx;
}
if(floorDx < newDx) {
// raise power to the level of the floor
newDx = floorDx;
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -