// This routine marks the disk as in use. If it is powered down, // it spins it up and waits for it to become ready. The caller // must hold the disk critical section. BOOL CDiskPower::RequestDevice(void) { BOOL fOk = TRUE; PREFAST_DEBUGCHK(m_pfnDevicePowerNotify != NULL); TakeCS(); // is the disk powered up? if(m_curDx != D0) { // don't bother requesting from the PM if we've already asked in a previous request DEBUGMSG(ZONE_POWER, (_T("CDiskPower::RequestDevice: device at D%d, m_fBoostRequested is %d\r\n"), m_curDx, m_fBoostRequested)); if(!m_fBoostRequested) { // request that the PM make us available m_fBoostRequested = TRUE; DWORD dwStatus = m_pfnDevicePowerNotify((PVOID) m_pszPMName, D0, POWER_NAME); if(dwStatus != ERROR_SUCCESS) { DEBUGMSG(ZONE_WARNING, (_T("CDiskPower::RequestDevice: DevicePowerNotify('%s') failed %d\r\n"), m_pszPMName, dwStatus)); m_fBoostRequested = FALSE; fOk = FALSE; } } } if(m_curDx == D0) { // wait for the disk to spin up so that we can do I/O DEBUGCHK(m_UseCount == 0); m_UseCount++; } else { fOk = FALSE; } ReleaseCS(); return fOk; }
// 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 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; }
/// /// @brief Init required IO-pins. This function should be called as /// early as possible in a systems with several SPI-devices. /// /// @param None /// @return None /// void libRFM69_InitHW(void) { InitReset(); InitCS(); ReleaseCS(); InitIO(); return; }
// This API signals that the caller is done doing I/O. The caller must hold the // parent disk's critical section. void CDiskPower::ReleaseDevice(void) { PREFAST_DEBUGCHK(m_pfnDevicePowerNotify); TakeCS(); // update the usage counter DEBUGCHK(m_UseCount != 0); m_UseCount--; DEBUGCHK(m_UseCount == 0); // wake the timeout thread to restart its countdown SignalActivity(); if(m_fReductionRequested) { // cancel outstanding requests to spin down the disk DWORD dwStatus = m_pfnDevicePowerNotify((PVOID) m_pszPMName, D0, POWER_NAME); if(dwStatus != ERROR_SUCCESS) { DEBUGMSG(ZONE_WARNING, (_T("CDiskPower::DiskPowerThread: DevicePowerNotify('%s', D%d) failed %d\r\n"), m_pszPMName, D0, dwStatus)); } else { m_fReductionRequested = FALSE; } } ReleaseCS(); }
// 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); if (GetCurrentProcessId() != (DWORD)GetDirectCallerProcessId()) { RETAILMSG(1, (L"ERROR: ATAPI: " L"Power IOCTLs can be called only from device process (caller process id 0x%08x)\n", GetDirectCallerProcessId())); SetLastError(ERROR_ACCESS_DENIED); return FALSE; } 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; }
DWORD CDiskPower::DiskPowerThread(void) { BOOL fDone = FALSE; DWORD dwTimeout = m_dwPowerTimeout; HANDLE hev = m_hevPowerSignal; PREFAST_DEBUGCHK(m_pfnDevicePowerNotify != NULL); DEBUGCHK(m_hevPowerSignal); DEBUGCHK(m_hevDummy); DEBUGMSG(ZONE_INIT, (_T("CDiskPower::DiskPowerThread: starting up for '%s', timeout is %d ms\r\n"), m_pszPMName, m_dwPowerTimeout)); while(!fDone) { //DEBUGMSG(ZONE_POWER, (_T("CDiskPower::DiskPowerThread: waiting on '%s', timeout is %u\r\n"), m_pszPMName, dwTimeout)); DWORD dwStatus = WaitForSingleObject(hev, dwTimeout); //DEBUGMSG(ZONE_POWER, (_T("CDiskPower::DiskPowerThread: WaitForSingleObject() returned %u\r\n"), dwStatus)); switch(dwStatus) { case WAIT_OBJECT_0: // are we supposed to exit? if(m_fShutdownPowerThread) { DEBUGMSG(ZONE_INIT, (_T("CDiskPower::DiskPowerThread: shutdown event signaled\r\n"))); fDone = TRUE; } else { // ignore further activity until the timeout expires TakeCS(); // Note: if you take the disk cs here, take it first DEBUGMSG(ZONE_POWER, (_T("CDiskPower::DiskPowerThread: disk activity detected on '%s', use count is %d\r\n"), m_pszPMName, m_UseCount)); DEBUGCHK(hev != m_hevDummy); hev = m_hevDummy; dwTimeout = m_dwPowerTimeout; ReleaseCS(); } break; case WAIT_TIMEOUT: // inactivity timeout -- see if we should spin down the disk m_pDisk->TakeCS(); TakeCS(); // we should be the only thread in the driver at this point DEBUGCHK(m_UseCount == 0); // By the time we have acquired these critical sections, we may have seen // some disk activity from an I/O thread that held them previously. Check // for this by polling our timeout event. if(WaitForSingleObject(m_hevPowerSignal, 0) == WAIT_TIMEOUT) { // don't bother asking the PM if we've already requested to spin down DEBUGMSG(ZONE_POWER, (_T("CDiskPower::DiskPowerThread: no disk activity on '%s', m_fReductionRequested is %d\r\n"), m_pszPMName, m_fReductionRequested)); if(!m_fReductionRequested) { // spin down the disk to m_timeoutDx m_fReductionRequested = TRUE; dwStatus = m_pfnDevicePowerNotify((PVOID) m_pszPMName, m_timeoutDx, POWER_NAME); if(dwStatus != ERROR_SUCCESS) { DEBUGMSG(ZONE_WARNING, (_T("CDiskPower::DiskPowerThread: DevicePowerNotify('%s', D%d) failed %d\r\n"), m_pszPMName, m_timeoutDx, dwStatus)); m_fReductionRequested = FALSE; } } // no need for more timeouts until the disk spins up again hev = m_hevPowerSignal; dwTimeout = INFINITE; } else { DEBUGMSG(ZONE_POWER, (_T("CDiskPower::DiskPowerThread: activity on '%s' after timeout, device at D%d\r\n"), m_pszPMName, m_curDx)); DEBUGCHK(hev == m_hevDummy); // if we are already at or below the spin-down disk power state we don't need // to have a timeout. The comparison relies on the fact that D0 >= Dx >= D4. if(m_curDx < m_timeoutDx) { dwTimeout = m_dwPowerTimeout; } else { dwTimeout = INFINITE; } // if we are not spun up, allow disk activity to wake us up if(m_curDx != D0) { hev = m_hevPowerSignal; } } // release resources ReleaseCS(); m_pDisk->ReleaseCS(); break; default: DEBUGMSG(ZONE_WARNING, (_T("CDiskPower::DiskPowerThread: WaitForSingleObject() returned %d, error %d\r\n"), dwStatus, GetLastError())); break; } } DEBUGMSG(ZONE_INIT, (_T("CDiskPower::DiskPowerThread: all done\r\n"))); return 0; }