📄 pmdevice.cpp
字号:
// return no change if the new state is unchanged or is invalid
// (for example, if DevicePowerNotify() while processing
// IOCTL_POWER_CAPABILITIES)
if(curDx == newDx || ! VALID_DX(newDx)) {
newDx = PwrDeviceUnspecified;
}
return newDx;
}
// This routine examines a device's current power state information with
// regards to system power state and floor and ceiling restrictions. It
// returns TRUE if successful and FALSE otherwise. If successful, new
// values for the device's current, floor, and ceiling power states are
// passed back via pointers. If the caller is not interested in the
// new values for floor and ceiling power levels, it may pass in null
// for these parameters. The caller should hold the PM lock.
BOOL
GetNewDeviceStateInfo(PCEDEVICE_POWER_STATE pNewFloorDx,
PCEDEVICE_POWER_STATE pNewCeilingDx,
PDEVICE_STATE pds, PSYSTEM_POWER_STATE psps,
PDEVICE_POWER_RESTRICTION pFloorDxList,
PDEVICE_POWER_RESTRICTION pCeilingDxList)
{
BOOL fOk = TRUE;
LPCGUID pGuid = pds->pListHead->pGuid;
DEVICEID devId;
PDEVICE_POWER_RESTRICTION pdpr;
CEDEVICE_POWER_STATE newFloorDx, newCeilingDx;
SETFNAME(_T("GetNewDeviceStateInfo"));
PREFAST_DEBUGCHK(psps != NULL);
PREFAST_DEBUGCHK(pNewFloorDx != NULL);
PREFAST_DEBUGCHK(pNewCeilingDx != NULL);
// Assume the default ceiling state. Since power ceilings are defined in
// the registry, there should be at most one that matches the device's
// class and one that matches the device exactly.
newCeilingDx = psps->defaultCeilingDx;
// look for power restrictions that match this device's class
devId.pGuid = pds->pListHead->pGuid;
devId.pszName = NULL;
if((pdpr = PowerRestrictionFindList(pCeilingDxList, &devId, NULL)) != NULL) {
newCeilingDx = pdpr->devDx;
}
// look for power restrictions that match this device exactly
devId.pszName = pds->pszName;
if((pdpr = PowerRestrictionFindList(pCeilingDxList, &devId, NULL)) != NULL) {
newCeilingDx = pdpr->devDx;
}
// Assume the default floor (device turned off). There may be multiple
// power floors specified in the registry so we will have to look for
// matches of several different types.
newFloorDx = D4;
// are we ignoring application requirements in this system power state?
if((psps->dwFlags & (POWER_STATE_CRITICAL | POWER_STATE_OFF)) == 0) {
BOOL fNeedForce;
// is the default for this power state to ignore application requirements?
if((psps->dwFlags & POWER_STATE_SUSPEND) != 0) {
fNeedForce = TRUE;
} else {
fNeedForce = FALSE;
}
// no, look for the least restricted floor based on device class for any
// system power state
devId.pszName = NULL;
pdpr = pFloorDxList;
while((pdpr = PowerRestrictionFindList(pdpr, &devId, NULL)) != NULL) {
if(pdpr->devDx < newFloorDx
&& (fNeedForce == FALSE || (pdpr->dwFlags & POWER_FORCE) != 0)) {
newFloorDx = pdpr->devDx;
}
pdpr = pdpr->pNext;
}
// look for the least restricted floor based on device class for
// this system power state
devId.pszName = NULL;
pdpr = pFloorDxList;
while((pdpr = PowerRestrictionFindList(pdpr, &devId, psps->pszName))
!= NULL) {
if(pdpr->devDx < newFloorDx
&& (fNeedForce == FALSE || (pdpr->dwFlags & POWER_FORCE) != 0)) {
newFloorDx = pdpr->devDx;
}
pdpr = pdpr->pNext;
}
// look for the least restricted floor based on an exact
// match with this device for any system power state
devId.pszName = pds->pszName;
pdpr = pFloorDxList;
while((pdpr = PowerRestrictionFindList(pdpr, &devId, NULL)) != NULL) {
if(pdpr->devDx < newFloorDx
&& (fNeedForce == FALSE || (pdpr->dwFlags & POWER_FORCE) != 0)) {
newFloorDx = pdpr->devDx;
}
pdpr = pdpr->pNext;
}
// look for the least restricted floor based on an exact
// match with this device for this system power state
devId.pszName = pds->pszName;
pdpr = pFloorDxList;
while((pdpr = PowerRestrictionFindList(pdpr, &devId, psps->pszName))
!= NULL) {
if(pdpr->devDx < newFloorDx
&& (fNeedForce == FALSE || (pdpr->dwFlags & POWER_FORCE) != 0)) {
newFloorDx = pdpr->devDx;
}
pdpr = pdpr->pNext;
}
}
// pass back values
if(fOk) {
*pNewCeilingDx = newCeilingDx;
*pNewFloorDx = newFloorDx;
}
return fOk;
}
// This routine updates a device's power state variables. This routine
// should be called whenever a device is added to the system or whenever
// a device power restriction is created, modified, or destroyed.
BOOL
UpdateDeviceState(PDEVICE_STATE pds)
{
BOOL fOk = TRUE;
DWORD dwStatus = ERROR_SUCCESS;
DWORD dwRetryCount = 0 ;
SETFNAME(_T("UpdateDeviceState"));
do {
CEDEVICE_POWER_STATE newDx = PwrDeviceUnspecified;
CEDEVICE_POWER_STATE oldLastReqDx;
BOOL fForce = FALSE;
PMLOCK();
// get the new device power state (based on the last device power state
// the device wanted -- this value is initialized to D0 in case the
// device never makes any power requests on its own.
fOk = GetNewDeviceStateInfo(&pds->floorDx, &pds->ceilingDx,
pds, gpSystemPowerState, gpFloorDx, gpCeilingDx);
if(fOk) {
// We have to monitor changes to pds->lastReqDx, since multiple threads can be
// in the PM at once calling DevicePowerNotify().
newDx = GetNewDeviceDx(pds->lastReqDx, pds->curDx, pds->setDx, pds->floorDx, pds->ceilingDx);
if(newDx == PwrDeviceUnspecified && (pds->dwNumPending != 0 || dwStatus == ERROR_RETRY)) {
PMLOGMSG(ZONE_DEVICE,
(_T("%s: reentrant set for '%s', setting %d\r\n"), pszFname, pds->pszName, pds->curDx));
newDx = pds->curDx;
fForce = TRUE;
}
PMLOGMSG(ZONE_DEVICE,
(_T("%s: new state for '%s' is %d (current %d, req %d, set %d, floor %d, ceiling %d, actual %d)\r\n"),
pszFname, pds->pszName, newDx, pds->curDx, pds->lastReqDx, pds->setDx, pds->floorDx, pds->ceilingDx, pds->actualDx));
oldLastReqDx = pds->lastReqDx;
dwStatus = ERROR_SUCCESS;
}
PMUNLOCK();
// do we need to update the device?
if(fOk && newDx != PwrDeviceUnspecified) {
// yes, set its power state to the new value
dwStatus = SetDevicePower(pds, newDx, fForce);
if(dwStatus == ERROR_SUCCESS) {
PMLOCK();
PMLOGMSG(ZONE_DEVICE,
(_T("%s: updated state for '%s' is current %d, req %d, set %d, floor %d, ceiling %d, actual %d\r\n"),
pszFname, pds->pszName, pds->curDx, pds->lastReqDx, pds->setDx, pds->floorDx, pds->ceilingDx, pds->actualDx));
if(pds->lastReqDx != oldLastReqDx) {
PMLOGMSG(ZONE_DEVICE, (_T("%s: race detected on '%s', setting ERROR_RETRY\r\n"),
pszFname, pds->pszName));
dwStatus = ERROR_RETRY;
}
PMUNLOCK();
}
if(dwStatus == ERROR_RETRY) {
dwRetryCount++;
PMLOGMSG(ZONE_DEVICE, (_T("%s: retrying(%d) SetDevicePower('%s', D%d)\r\n"),
pszFname,dwRetryCount, pds->pszName, newDx));
} else if(dwStatus != ERROR_SUCCESS) {
PMLOGMSG(ZONE_DEVICE, (_T("%s: SetDevicePower('%s', D%d) failed %d\r\n"),
pszFname, pds->pszName, newDx, dwStatus));
fOk = FALSE;
}
}
} while(fOk && dwStatus == ERROR_RETRY);
return fOk;
}
#ifdef PM_SUPPORTS_DEVICE_QUERIES
// NOTE -- Devices are not required to implement IOCTL_POWER_QUERY, nor is the
// PM required to pay attention to the value that devices return in response to the
// query. If the POWER_FORCE flag is set, the PM is not required to make the request.
// This means that device drivers cannot count on IOCTL_POWER_QUERY being invoked
// at all, so by default, the PM does not use IOCTL_POWER_QUERY at all.
//
// However, some OEMs may choose to query drivers on every system power state
// transition and to always honor the device's response. Such OEMs may
// find the code inside this #ifdef useful. This code may not be present
// in future source updates to the Power Manager.
// This routine queries a driver as to whether it is ready to handle a device
// power state transition. It returns TRUE or FALSE according to the response;
// FALSE may also indicate an error talking to the device.
BOOL
QueryDevicePowerUpdate(PDEVICE_STATE pds, CEDEVICE_POWER_STATE newDx)
{
BOOL fOk = TRUE;
POWER_RELATIONSHIP pr;
PPOWER_RELATIONSHIP ppr = NULL;
CEDEVICE_POWER_STATE reqDx;
BOOL fDoSet;
BOOL fOpenDevice = FALSE;
HANDLE hDevice = INVALID_HANDLE_VALUE;
SETFNAME(_T("QueryDevicePower"));
DEBUGCHK(pds != NULL && 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)\r\n"),
pszFname, pds->pszName, newDx, reqDx));
// make a last check to see if we really need to send the device an update
PMLOCK();
if(reqDx != pds->actualDx) {
fDoSet = TRUE;
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);
}
// do we have one?
if(hDevice == INVALID_HANDLE_VALUE) {
PMLOGMSG(ZONE_WARN, (_T("%s: couldn't open '%s'\r\n"), pszFname,
pds->pszName));
fOk = FALSE;
} else {
CEDEVICE_POWER_STATE tmpDx = newDx;
DWORD dwBytesReturned;
DEBUGCHK(pds->pInterface->pfnRequestDevice != NULL);
fOk = pds->pInterface->RequestDevice(hDevice, IOCTL_POWER_QUERY, ppr,
ppr == NULL ? 0 : sizeof(*ppr), &tmpDx, sizeof(tmpDx),
&dwBytesReturned);
if(!fOk) {
PMLOGMSG(ZONE_WARN,
(_T("%s: '%s' failed IOCTL_POWER_QUERY D%d, status is %d\r\n"),
pszFname, pds->pszName, newDx, GetLastError()));
}
// close the device handle if we opened it in this routine
if(fOpenDevice) {
DEBUGCHK(pds->pInterface->pfnCloseDevice != NULL);
pds->pInterface->pfnCloseDevice(hDevice);
}
// if the device permitted the ioctl, determine its response to the
// query.
if(fOk) {
if(tmpDx == PwrDeviceUnspecified) {
PMLOGMSG(ZONE_WARN, (_T("%s: '%s' rejected IOCTL_POWER_QUERY D%d\r\n"),
pszFname, pds->pszName, newDx));
fOk = FALSE;
}
}
}
}
return fOk;
}
// This routine determines what power state a device would enter in a new
// system power state and asks the device whether it is ready to enter
// that state. It returns TRUE or FALSE depending on the device's response;
// false may also indicate an error.
BOOL
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -