?? atapipm.cpp
字號:
GUID gPMClass;
int nPriority = 250; // THREAD_PRIORITY_ABOVE_NORMAL
HANDLE hActive = NULL;
BOOL fOk = TRUE;
PREFAST_DEBUGCHK(pDiskParent != NULL);
DEBUGMSG(ZONE_INIT, (_T("+CDiskPower::Init(): parent is 0x%08x\r\n"), pDiskParent));
// record the parent device
m_pDisk = pDiskParent;
// get a pointer to the PM APIs we need
if(fOk) {
HMODULE hmCoreDll = LoadLibrary(L"coredll.dll");
if(hmCoreDll == NULL) {
DEBUGMSG(ZONE_INIT || ZONE_ERROR, (_T("CDevicePower::Init: LoadLibrary('coredll.dll') failed %d\r\n"), GetLastError()));
fOk = FALSE;
} else {
m_pfnDevicePowerNotify = (DWORD ((*)(PVOID, CEDEVICE_POWER_STATE, DWORD))) GetProcAddress(hmCoreDll, L"DevicePowerNotify");
if(m_pfnDevicePowerNotify == NULL) {
DEBUGMSG(ZONE_INIT || ZONE_ERROR, (_T("CDevicePower::Init: GetProcAddress('DevicePowerNotify') failed %d\r\n"), GetLastError()));
fOk = FALSE;
}
// we're explicitly linked with coredll so we don't need the handle
FreeLibrary(hmCoreDll);
}
}
// read registry configuration
if(fOk) {
HKEY hk;
BOOL fGotClass = FALSE;
WCHAR szClass[64]; // big enough for a GUID
// determine the power class we are advertising
dwStatus = RegOpenKeyEx(HKEY_LOCAL_MACHINE, m_pDisk->m_szDeviceKey, 0, 0, &hk);
if(dwStatus == ERROR_SUCCESS) {
// read the PM class
DWORD dwSize = sizeof(szClass);
dwStatus = RegQueryValueEx(hk, L"PowerClass", NULL, NULL, (LPBYTE) szClass, &dwSize);
if(dwStatus == ERROR_SUCCESS) {
fGotClass = TRUE;
}
// get the inactivity timeout
DWORD dwValue;
dwSize = sizeof(dwValue);
dwStatus = RegQueryValueEx(hk, L"InactivityTimeout", NULL, NULL, (LPBYTE) &dwValue, &dwSize);
if(dwStatus == ERROR_SUCCESS) {
m_dwPowerTimeout = dwValue;
}
DEBUGMSG(ZONE_INIT, (_T("CDiskPower::Init: inactivity timeout is %u ms\r\n"), m_dwPowerTimeout));
// get the inactivity timeout
dwSize = sizeof(dwValue);
dwStatus = RegQueryValueEx(hk, L"TimeoutDx", NULL, NULL, (LPBYTE) &dwValue, &dwSize);
if(dwStatus == ERROR_SUCCESS) {
if(VALID_DX((CEDEVICE_POWER_STATE)dwValue) && dwValue != D3) {
m_timeoutDx = (CEDEVICE_POWER_STATE) dwValue;
} else {
DEBUGMSG(ZONE_WARNING, (_T("CDiskPower::Init: invalid or unsupported timeout device power state %d (0x%x)\r\n"), dwValue, dwValue));
}
}
DEBUGMSG(ZONE_INIT, (_T("CDiskPower::Init: timeout state is D%d\r\n"), m_timeoutDx));
// get the inactivity timeout
dwSize = sizeof(dwValue);
dwStatus = RegQueryValueEx(hk, L"InactivityPriority256", NULL, NULL, (LPBYTE) &dwValue, &dwSize);
if(dwStatus == ERROR_SUCCESS) {
nPriority = (int) dwValue;
}
DEBUGMSG(ZONE_INIT, (_T("CDiskPower::Init: inactivity timeout thread priority is %d\r\n"), nPriority));
RegCloseKey(hk);
}
// did we get a class string?
if(!fGotClass) {
// no, use the default disk class
wcsncpy(szClass, PMCLASS_BLOCK_DEVICE, dim(szClass));
szClass[dim(szClass) - 1] = 0;
}
// convert to a GUID
fOk = GUIDFromString(szClass, &gPMClass);
if(!fOk) {
DEBUGMSG(ZONE_WARNING || ZONE_INIT, (_T("CDiskPower::Init: invalid power management class '%s'\r\n"),
szClass));
}
}
// get our active key from the registry
if(fOk) {
HKEY hk;
dwStatus = RegOpenKeyEx(HKEY_LOCAL_MACHINE, m_pDisk->m_szActiveKey, 0, 0, &hk);
if(dwStatus == ERROR_SUCCESS) {
DWORD dwValue;
DWORD dwSize = sizeof(dwValue);
dwStatus = RegQueryValueEx(hk, DEVLOAD_HANDLE_VALNAME, NULL, NULL, (LPBYTE) &dwValue, &dwSize);
if(dwStatus != ERROR_SUCCESS) {
DEBUGMSG(ZONE_WARNING || ZONE_INIT, (_T("CDiskPower::Init: can't read '%s' from '%s'\r\n"),
DEVLOAD_HANDLE_VALNAME, m_pDisk->m_szActiveKey));
fOk = FALSE;
} else {
DEBUGCHK(dwValue != 0);
hActive = (HANDLE) dwValue;
}
}
}
// figure out the name we are using
if(fOk) {
WCHAR szName[MAX_PATH];
DWORD dwIndex = 0;
do {
DWORD dwSize = sizeof(szName);
GUID gClass;
fOk = EnumDeviceInterfaces(hActive, dwIndex, &gClass, szName, &dwSize);
if(fOk && gPMClass == gClass) {
// we found the interface
break;
}
dwIndex++;
} while(fOk);
DEBUGMSG(!fOk && (ZONE_WARNING || ZONE_INIT), (_T("CDiskPower::Init: can't find PM interface\r\n")));
// did we find the name?
if(fOk) {
// yes, allocate a name buffer to use to talk to the power manager
DWORD dwChars = wcslen(PMCLASS_BLOCK_DEVICE) + wcslen(szName) + 2; // class + separator + name + null
LPWSTR pszPMName = (LPWSTR) LocalAlloc(LPTR, dwChars * sizeof(WCHAR));
fOk = FALSE; // assume failure
if(pszPMName) {
HRESULT hr = StringCchPrintfW(pszPMName, dwChars, L"{%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x}\\%s",
gPMClass.Data1, gPMClass.Data2, gPMClass.Data3,
(gPMClass.Data4[0] << 8) + gPMClass.Data4[1], gPMClass.Data4[2], gPMClass.Data4[3],
gPMClass.Data4[4], gPMClass.Data4[5], gPMClass.Data4[6], gPMClass.Data4[7],
szName);
if(SUCCEEDED(hr)) {
m_pszPMName = (LPCWSTR) pszPMName;
fOk = TRUE;
}
}
DEBUGMSG(!fOk && (ZONE_WARNING || ZONE_INIT), (_T("CDiskPower::Init: can't find PM interface\r\n")));
}
}
// create an event to tell the timeout thread about activity
if(fOk) {
m_hevPowerSignal = CreateEvent(NULL, FALSE, FALSE, NULL);
m_hevDummy = CreateEvent(NULL, FALSE, FALSE, NULL);
if(!m_hevPowerSignal || !m_hevDummy) {
DEBUGMSG(ZONE_WARNING || ZONE_INIT, (_T("CDiskPower::Init: couldn't create status events\r\n")));
fOk = FALSE;
}
}
// create the timeout thread
if(fOk) {
m_htPower = CreateThread(NULL, 0, DiskPowerThreadStub, this, 0, NULL);
if(!m_htPower) {
DEBUGMSG(ZONE_WARNING || ZONE_INIT, (_T("CDiskPower::Init: CreateThread() failed %d\r\n"), GetLastError()));
fOk = FALSE;
} else {
BOOL fSuccess = CeSetThreadPriority(m_htPower, nPriority);
DEBUGMSG(!fSuccess && (ZONE_WARNING || ZONE_INIT), (_T("CDiskPower::Init: CeSetThreadPriority(%d) failed %d\r\n"), nPriority, GetLastError()));
}
}
// disable the standby timer, since the PM is going to be controlling
// the disk power state
if(fOk) {
if(!m_pDisk->SendDiskPowerCommand(ATA_NEW_CMD_IDLE, 0)) {
DEBUGMSG(ZONE_WARNING || ZONE_INIT, (_T("CDiskPower::Init: disable standby timer failed\r\n")));
}
}
// on error, cleanup will happen in the destructor
DEBUGMSG(ZONE_INIT, (_T("-CDiskPower::Init(): returning %d\r\n"), fOk));
return fOk;
}
// This routine issues the ATAPI commands necessary to put the disk into a new power state.
// The caller must hold the disk critical section.
DWORD CDiskPower::SetDiskPower(CEDEVICE_POWER_STATE newDx)
{
DWORD dwStatus = ERROR_SUCCESS;
DEBUGCHK(VALID_DX(newDx));
PREFAST_DEBUGCHK(m_pDisk != NULL);
TakeCS();
DEBUGMSG(ZONE_POWER, (_T("CDiskPower::SetDiskPower: updating from D%d to D%d\r\n"), m_curDx, newDx));
if(newDx != m_curDx) {
switch(newDx) {
case D0:
case D1:
case D2:
if(m_curDx == D4) {
// have to reset and reinitialize to come out of SLEEP mode
if(!m_pDisk->WakeUp()) {
DEBUGMSG(ZONE_ERROR, (_T("CDiskPower::SetDiskPower: couldn't re-initialize hard drive\r\n")));
dwStatus = ERROR_GEN_FAILURE;
}
}
break;
case D3:
case D4:
newDx = D4; // no D3 support
break;
}
// enter the new device state
if(dwStatus == ERROR_SUCCESS && !m_pDisk->SetDiskPowerState(newDx)) {
DEBUGMSG(ZONE_WARNING, (_T("CDiskPower::SetDiskPower: SetDiskPowerState(D%d) failed\r\n"), newDx));
dwStatus = ERROR_GEN_FAILURE;
}
// update the device power status
if(dwStatus == ERROR_SUCCESS) {
LARGE_INTEGER li;
BOOL fGotQPC = QueryPerformanceCounter(&li);
if(!fGotQPC) {
DEBUGMSG(ZONE_WARNING, (_T("CDiskPower::SetDiskPower: QueryPerformanceCounter() failed, can't update statistics\r\n")));
} else {
m_dxInfo[m_curDx].totalQPC.QuadPart += li.QuadPart - m_startQPC.QuadPart;
}
m_curDx = newDx;
m_dxInfo[m_curDx].dwCount++;
if(fGotQPC) {
m_startQPC = li;
}
}
}
ReleaseCS();
return dwStatus;
}
// This routine returns the disk's current device power state. The caller must hold the disk
// critical section.
CEDEVICE_POWER_STATE CDiskPower::GetDiskPower(void)
{
CEDEVICE_POWER_STATE curDx;
TakeCS();
curDx = m_curDx;
DEBUGMSG(ZONE_POWER, (_T("CDiskPower::GetDiskPower: returning D%d\r\n"), curDx));
ReleaseCS();
return curDx;
}
// This routine fills in the POWER_CAPABILITIES structure with information for this driver
DWORD CDiskPower::GetDiskCapabilities(PPOWER_CAPABILITIES pCap)
{
DWORD dwStatus = ERROR_SUCCESS;
DEBUGCHK(pCap != NULL);
PREFAST_DEBUGCHK(m_pDisk != NULL);
// clear the capabilities structure
memset(pCap, 0, sizeof(*pCap));
// has power management been enabled for this drive?
if(!m_pDisk->IsPMEnabled()) {
// no, just report D0 support
pCap->DeviceDx = 0x01;
} else {
pCap->DeviceDx = 0x17; // support D4, D2, D1, D0
}
return dwStatus;
}
// This routine handles Power Manager IOCTLs for the disk. It returns a Win32 error
// code if there's a problem, ERROR_SUCCESS if the IOCTL was handled successfully, or
// ERROR_NOT_SUPPORTED if the IOCTL was not from the PM. The caller must hold the
// disk critical section.
DWORD CDiskPower::DiskPowerIoctl(PIOREQ pIOReq)
{
DWORD dwStatus = ERROR_INVALID_PARAMETER;
PREFAST_DEBUGCHK(pIOReq != NULL);
switch(pIOReq->dwCode) {
case IOCTL_POWER_CAPABILITIES:
if(pIOReq->pOutBuf != NULL && pIOReq->dwOutBufSize >= sizeof(POWER_CAPABILITIES) && pIOReq->pBytesReturned != NULL) {
POWER_CAPABILITIES pc;
dwStatus = GetDiskCapabilities(&pc);
if(dwStatus == ERROR_SUCCESS) {
PPOWER_CAPABILITIES ppc = (PPOWER_CAPABILITIES) pIOReq->pOutBuf;
*ppc = pc;
*pIOReq->pBytesReturned = sizeof(*ppc);
}
}
break;
case IOCTL_POWER_SET:
if(pIOReq->pOutBuf != NULL && pIOReq->dwOutBufSize == sizeof(CEDEVICE_POWER_STATE) && pIOReq->pBytesReturned != NULL) {
CEDEVICE_POWER_STATE newDx = *(PCEDEVICE_POWER_STATE) pIOReq->pOutBuf;
m_fReductionRequested = FALSE;
m_fBoostRequested = FALSE;
dwStatus = SetDiskPower(newDx);
*pIOReq->pBytesReturned = sizeof(newDx);
dwStatus = ERROR_SUCCESS;
}
break;
case IOCTL_POWER_GET:
if(pIOReq->pOutBuf != NULL && pIOReq->dwOutBufSize == sizeof(CEDEVICE_POWER_STATE) && pIOReq->pBytesReturned != NULL) {
CEDEVICE_POWER_STATE curDx = GetDiskPower();
*(PCEDEVICE_POWER_STATE) pIOReq->pOutBuf = curDx;
*pIOReq->pBytesReturned = sizeof(curDx);
dwStatus = ERROR_SUCCESS;
}
break;
case IOCTL_DISK_GETPMTIMINGS:
if(pIOReq->pInBuf != NULL && pIOReq->dwInBufSize >= sizeof(PowerTimings)) {
PowerTimings pt;
memset(&pt, 0, sizeof(pt));
pt.dwSize = sizeof(pt);
TakeCS();
pt.dwLoadedTicks = GetTickCount() - m_dwStartTickCount;
for(int i = 0; i < PwrDeviceMaximum; i++) {
pt.DxTiming[i].dwCount = m_dxInfo[i].dwCount;
pt.DxTiming[i].liElapsed = m_dxInfo[i].totalQPC;
}
LARGE_INTEGER li;
if(QueryPerformanceCounter(&li)) {
pt.DxTiming[m_curDx].liElapsed.QuadPart += li.QuadPart - m_startQPC.QuadPart;
}
ReleaseCS();
// copy the data to the user buffer
pPowerTimings ppt = (pPowerTimings) pIOReq->pInBuf;
if(ppt->dwSize >= sizeof(PowerTimings) && ppt->dwSize <= pIOReq->dwInBufSize) {
*ppt = pt;
dwStatus = ERROR_SUCCESS;
} else {
dwStatus = ERROR_INVALID_PARAMETER;
}
}
break;
default: // not a PM ioctl
dwStatus = ERROR_NOT_SUPPORTED;
break;
}
DEBUGMSG(dwStatus != ERROR_NOT_SUPPORTED && dwStatus != ERROR_SUCCESS && ZONE_WARNING,
(_T("CDiskPower::DiskPowerIoctl: ioctl 0x%x failed %u\r\n"), pIOReq->dwCode, dwStatus));
return dwStatus;
}
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -