// This routine issues a request to a device. It returns TRUE if successful, // FALSE if there's a problem. static BOOL RequestStreamDevice(HANDLE hDevice, DWORD dwRequest, LPVOID pInBuf, DWORD dwInSize, LPVOID pOutBuf, DWORD dwOutSize, LPDWORD pdwBytesRet) { BOOL fOk; #ifndef SHIP_BUILD SETFNAME(_T("RequestStreamDevice")); #endif DEBUGCHK(hDevice != INVALID_HANDLE_VALUE); __try { PMLOGMSG(ZONE_IOCTL, (_T("%s: calling DeviceIoControl(0x%08x) w/ request %d ('%s')\r\n"), pszFname, hDevice, dwRequest, dwRequest == IOCTL_POWER_CAPABILITIES ? _T("IOCTL_POWER_CAPABILITIES") : dwRequest == IOCTL_POWER_GET ? _T("IOCTL_POWER_GET") : dwRequest == IOCTL_POWER_SET ? _T("IOCTL_POWER_SET") : dwRequest == IOCTL_POWER_QUERY ? _T("IOCTL_POWER_QUERY") : dwRequest == IOCTL_REGISTER_POWER_RELATIONSHIP ? _T("IOCTL_REGISTER_POWER_RELATIONSHIP") : _T("<UNKNOWN>"))); fOk = DeviceIoControl(hDevice, dwRequest, pInBuf, dwInSize, pOutBuf, dwOutSize, pdwBytesRet, NULL); } __except(EXCEPTION_EXECUTE_HANDLER) { PMLOGMSG(ZONE_WARN || ZONE_IOCTL, (_T("%s: exception in DeviceIoControl(%d)\r\n"), pszFname, dwRequest)); fOk = FALSE; } PMLOGMSG(!fOk && (ZONE_WARN || ZONE_IOCTL), (_T("%s: DeviceIoControl(%d) to 0x%08x failed %d (0x%08x)\r\n"), pszFname, dwRequest, hDevice, GetLastError(), GetLastError())); return fOk; }
// This routine releases a power relationship that was created with // PmRegisterPowerRelationship(). It returns // ERROR_SUCCESS - relationship removed // ERROR_INVALID_PARAMETER - bad handle // Deregistering a power relationship has the side effect of deregistering // the child device with the PM. Note that if the child exits while // the relationship is outstanding, the caller will get ERROR_INVALID_PARAMETER // when they attempt to release the relationship handle. EXTERN_C DWORD WINAPI PmReleasePowerRelationship(HANDLE h) { PDEVICE_STATE pds = (PDEVICE_STATE) h; DWORD dwStatus = ERROR_INVALID_PARAMETER; #ifndef SHIP_BUILD SETFNAME(_T("PmReleasePowerRelationship")); #endif PMLOGMSG(ZONE_API, (_T("%s: releasing 0x%08x\r\n"), pszFname, h)); // make sure that this pointer is a child node with a parent if(pds != NULL) { BOOL fExists = CheckDevicePointer(pds); // increments refcnt if TRUE if(fExists) { // delete the device PREFAST_DEBUGCHK(pds->pListHead != NULL); RemoveDevice(pds->pListHead->pGuid, pds->pszName); // done with the pointer DeviceStateDecRef(pds); // return a good status dwStatus = ERROR_SUCCESS; } } PMLOGMSG(ZONE_API || (dwStatus != ERROR_SUCCESS && ZONE_WARN), (_T("%s: returning %d\r\n"), pszFname, dwStatus)); return dwStatus; }
// This routine opens a handle to a given device, or to the parent device // that is its proxy. The return value is the device's handle, or // INVALID_HANDLE_VALUE if there's an error. static HANDLE OpenStreamDevice(PDEVICE_STATE pds) { PDEVICE_STATE pdsReal = pds; #ifndef SHIP_BUILD SETFNAME(_T("OpenStreamDevice")); #endif // determine what device to actually open if(pds->pParent != NULL) { pdsReal = pds->pParent; } // get a handle to the client HANDLE hRet = CreateFile(pdsReal->pszName, 0, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if(hRet == INVALID_HANDLE_VALUE) { PMLOGMSG(ZONE_WARN || ZONE_IOCTL, (_T("%s: OpenFile('%s') failed %d (0x%08x)\r\n"), pszFname, pdsReal->pszName, GetLastError(), GetLastError())); } PMLOGMSG(ZONE_DEVICE || ZONE_IOCTL, (_T("%s: handle to '%s' on behalf of '%s' is 0x%08x\r\n"), \ pszFname, pdsReal->pszName, pds->pszName, hRet)); return hRet; }
// This routine closes a handle opened with OpenStreamDevice(). It returns TRUE // if successful, FALSE if there's a problem. static BOOL CloseStreamDevice(HANDLE hDevice) { #ifndef SHIP_BUILD SETFNAME(_T("CloseStreamDevice")); #endif PMLOGMSG(ZONE_DEVICE || ZONE_IOCTL, (_T("%s: closing 0x%08x\r\n"), pszFname, hDevice)); BOOL fOk = CloseHandle(hDevice); PMLOGMSG(!fOk && (ZONE_WARN || ZONE_IOCTL), (_T("%s: CloseHandle(0x%08x) failed %d (0x%08x)\r\n"), pszFname, hDevice, GetLastError(), GetLastError())); return fOk; }
DWORD PowerState::StateValidateRegistry (DWORD dwDState, DWORD dwFlag) { HKEY hkPM = NULL, hkSubkey; TCHAR pszSubKey[MAX_PATH]; DWORD dwDisposition; SETFNAME (_T ("PowerState::StateValidateRegistry")); // Open the Power Manager registry key: DWORD dwStatus = RegCreateKeyEx (HKEY_LOCAL_MACHINE, PWRMGR_REG_KEY, 0, NULL, 0, 0, NULL, &hkPM, &dwDisposition); if (dwStatus != ERROR_SUCCESS) { PMLOGMSG (ZONE_ERROR, (_T ("%s: can't open '%s', error is %d\r\n"), pszFname, PWRMGR_REG_KEY, dwStatus)); } // Verify the system state: if (dwStatus == ERROR_SUCCESS) { StringCchPrintf (pszSubKey, MAX_PATH, _T ("State\\%s"), GetStateString ()); dwStatus = RegCreateKeyEx (hkPM, pszSubKey, 0, NULL, 0, 0, NULL, &hkSubkey, &dwDisposition); if (dwStatus == ERROR_SUCCESS) { if (dwDisposition == REG_CREATED_NEW_KEY) { // Allow devices to go to any passed-in power level: DWORD dwValue = dwDState; // D State dwStatus = RegSetValueEx (hkSubkey, NULL, 0, REG_DWORD, (LPBYTE) & dwValue, sizeof (dwValue)); // Write the passed flags value: if (dwStatus == ERROR_SUCCESS) { dwValue = dwFlag; dwStatus = RegSetValueEx (hkSubkey, _T ("Flags"), 0, REG_DWORD, (LPBYTE) & dwValue, sizeof (dwValue)); } } RegCloseKey (hkSubkey); } PMLOGMSG (dwStatus != ERROR_SUCCESS && ZONE_ERROR, (_T ("%s: error %d while creating or writing values in '%s\\%s'\r\n"), pszFname, dwStatus, PWRMGR_REG_KEY, pszSubKey)); } // Release resources: if (hkPM != NULL) RegCloseKey (hkPM); return dwStatus; }
PLATFORM_ACTIVITY_EVENT PowerState::MsgQueueEvent () { SETFNAME (_T ("PowerState::MsgQueueEvent")); PLATFORM_ACTIVITY_EVENT activeEvent = NoActivity; POWERPOLICYMESSAGE ppm; DWORD dwStatus = PmPolicyReadNotificationQueue (m_dwEventArray[PM_MSGQUEUE_EVENT], &ppm, sizeof (ppm)); if (dwStatus == ERROR_SUCCESS) { PMLOGMSG (ZONE_PLATFORM, (_T ("%s: got request 0x%04x (data 0x%08x) from process 0x%08x\r\n"), pszFname, ppm.dwMessage, ppm.dwData, ppm.hOwnerProcess)); switch (ppm.dwMessage) { case PPN_POWERCHANGE: if (PmUpdatePowerStatus ()) { activeEvent = PowerSourceChange; } break; case PPN_SUSPENDKEYPRESSED: activeEvent = PowerButtonPressed; break; case PPN_APPBUTTONPRESSED: activeEvent = AppButtonPressed; break; case PPN_UNATTENDEDMODE: // Somebody wants to enter or leave unattended mode: if (ppm.dwData != FALSE) { activeEvent = EnterUnattendedModeRequest; } else { activeEvent = LeaveUnattendedModeRequest; } break; default: // Unhandled notification type, so ignore it: PMLOGMSG (ZONE_WARN, (_T ("%s: unhandled policy notification 0x%04x (data 0x%08x)\r\n"), pszFname, ppm.dwMessage, ppm.dwData)); break; } } PMLOGMSG (ZONE_PLATFORM, (_T ("%s: return ActiveEvent = 0x%08x\r\n"), pszFname, activeEvent)); return activeEvent; }
// this routine reads device notifications from a message queue and updates // the PM's internal tables appropriately. BOOL ProcessPnPMsgQueue(HANDLE hMsgQ) { BOOL fOk = FALSE; UCHAR deviceBuf[PNP_QUEUE_SIZE]; DWORD iBytesInQueue = 0; DWORD dwFlags = 0; SETFNAME(_T("ProcessPnPMsgQueue")); // read a message from the message queue -- it should be a device advertisement memset(deviceBuf, 0, PNP_QUEUE_SIZE); if ( !ReadMsgQueue(hMsgQ, deviceBuf, PNP_QUEUE_SIZE, &iBytesInQueue, 0, &dwFlags)) { // nothing in the queue PMLOGMSG(ZONE_WARN, (_T("%s: ReadMsgQueue() failed %d\r\n"), pszFname, GetLastError())); } else if(iBytesInQueue >= sizeof(DEVDETAIL)) { // process the message PDEVDETAIL pDevDetail = (PDEVDETAIL) deviceBuf; DWORD dwMessageSize = sizeof(DEVDETAIL) + pDevDetail->cbName; // check for overlarge names if(pDevDetail->cbName > ((PNP_MAX_NAMELEN - 1) * sizeof(pDevDetail->szName[0]))) { PMLOGMSG(ZONE_WARN, (_T("%s: device name longer than %d characters\r\n"), pszFname, PNP_MAX_NAMELEN - 1)); } else { // convert the device name to lower case int i; for(i = 0; i < (PNP_MAX_NAMELEN - 1) && pDevDetail->szName[i] != 0; i++) { pDevDetail->szName[i] = _totlower(pDevDetail->szName[i]); } pDevDetail->szName[i] = 0; // add or remove the device -- note that a particular interface may be // advertised more than once, so these routines must handle that possibility. if(pDevDetail->fAttached) { AddDevice(&pDevDetail->guidDevClass, pDevDetail->szName, NULL, NULL); } else { RemoveDevice(&pDevDetail->guidDevClass, pDevDetail->szName); } fOk = TRUE; } } else { // not enough bytes for a message PMLOGMSG(ZONE_WARN, (_T("%s: got runt message (%d bytes)\r\n"), pszFname, iBytesInQueue)); } return fOk; }
BOOL PowerState::Init () { SETFNAME (_T ("PowerState::Init")); if (m_pPwrStateMgr && m_hUnsignaledHandle) { for (DWORD dwIndex = 0; dwIndex < m_dwNumOfEvent; dwIndex++) if (m_dwEventArray[dwIndex] == NULL) { ASSERT (FALSE); return FALSE; } m_LastNewState = GetState (); // Point to itself DWORD dwReturn = StateValidateRegistry (); if (dwReturn != ERROR_SUCCESS) { PMLOGMSG (ZONE_PLATFORM, (_T ("%s: StateValidateRegistry return (0x%08x) fails\r\n"), pszFname, dwReturn)); ASSERT (FALSE); return FALSE; } return TRUE; } return FALSE; }
// this thread is signaled when the system wakes from a suspend state DWORD WINAPI ResumeThreadProc(LPVOID lpvParam) { DWORD dwStatus; HANDLE hevReady = (HANDLE) lpvParam; HANDLE hEvents[2]; BOOL fDone = FALSE; INT iPriority; SETFNAME(_T("ResumeThreadProc")); PMLOGMSG(ZONE_INIT, (_T("+%s: thread 0x%08x\r\n"), pszFname, GetCurrentThreadId())); // set the thread priority if(!GetPMThreadPriority(_T("ResumePriority256"), &iPriority)) { iPriority = DEF_RESUME_THREAD_PRIORITY; } CeSetThreadPriority(GetCurrentThread(), iPriority); // we're up and running SetEvent(hevReady); // wait for new devices to arrive hEvents[0] = ghevResume; hEvents[1] = ghevPmShutdown; while(!fDone) { dwStatus = WaitForMultipleObjects(dim(hEvents), hEvents, FALSE, INFINITE); switch(dwStatus) { case (WAIT_OBJECT_0 + 0): PMLOGMSG(ZONE_RESUME, (_T("%s: resume event signaled\r\n"), pszFname)); PlatformResumeSystem(); break; case (WAIT_OBJECT_0 + 1): PMLOGMSG(ZONE_WARN, (_T("%s: shutdown event set\r\n"), pszFname)); fDone = TRUE; break; default: PMLOGMSG(ZONE_WARN, (_T("%s: WaitForMultipleObjects() returned %d, status is %d\r\n"), pszFname, dwStatus, GetLastError())); fDone = TRUE; break; } } PMLOGMSG(ZONE_INIT | ZONE_WARN, (_T("-%s: exiting\r\n"), pszFname)); return 0; }
DWORD WINAPI PnpThreadProc(LPVOID lpvParam) { DWORD dwStatus; HANDLE hnGeneric = NULL; HANDLE hevReady = (HANDLE) lpvParam; HANDLE hEvents[MAXIMUM_WAIT_OBJECTS]; DWORD dwNumEvents = 0; BOOL fDone = FALSE; BOOL fOk; INT iPriority; PDEVICE_LIST pdl; SETFNAME(_T("PnpThreadProc")); PMLOGMSG(ZONE_INIT, (_T("+%s: thread 0x%08x\r\n"), pszFname, GetCurrentThreadId())); // set the thread priority if(!GetPMThreadPriority(_T("PnPPriority256"), &iPriority)) { iPriority = DEF_PNP_THREAD_PRIORITY; } CeSetThreadPriority(GetCurrentThread(), iPriority); // first list entry is the exit event hEvents[dwNumEvents++] = ghevPmShutdown; // set up device notifications for(pdl = gpDeviceLists; pdl != NULL && dwNumEvents < dim(hEvents); pdl = pdl->pNext) { hEvents[dwNumEvents++] = pdl->hMsgQ; pdl->hnClass = RequestDeviceNotifications(pdl->pGuid, pdl->hMsgQ, TRUE); if(pdl->hnClass == NULL) { PMLOGMSG(ZONE_WARN, (_T("%s: RequestDeviceNotifications() failed %d\r\n"), pszFname, GetLastError())); } } DEBUGCHK(dwNumEvents > 1); // we're up and running SetEvent(hevReady); // Wait for Initalization complete. HANDLE hInit[2] = {ghevPowerManagerReady, ghevPmShutdown}; fDone = (WaitForMultipleObjects(_countof(hInit), hInit, FALSE, INFINITE)!= WAIT_OBJECT_0); // wait for new devices to arrive while(!fDone) { dwStatus = WaitForMultipleObjects(dwNumEvents, hEvents, FALSE, INFINITE); if(dwStatus == (WAIT_OBJECT_0 + 0)) { PMLOGMSG(ZONE_WARN, (_T("%s: shutdown event set\r\n"), pszFname)); fDone = TRUE; } else if(dwStatus > WAIT_OBJECT_0 && dwStatus <= (WAIT_OBJECT_0 + MAXIMUM_WAIT_OBJECTS)) { dwStatus -= WAIT_OBJECT_0; fOk = ProcessPnPMsgQueue(hEvents[dwStatus]); if(!fOk) { PMLOGMSG(ZONE_WARN, (_T("%s: ProcessPnPMsgQueue(0x%08x) failed\r\n"), pszFname, hEvents[dwStatus])); } } else { PMLOGMSG(ZONE_WARN, (_T("%s: WaitForMultipleObjects() returned %d, status is %d\r\n"), pszFname, dwStatus, GetLastError())); fDone = TRUE; break; } } // release resources for(pdl = gpDeviceLists; pdl != NULL; pdl = pdl->pNext) { if(pdl->hnClass != NULL) StopDeviceNotifications(pdl->hnClass); } // all done PMLOGMSG(ZONE_INIT | ZONE_WARN, (_T("-%s: exiting\r\n"), pszFname)); return 0; }
EXTERN_C BOOL WINAPI PlatformDeviceListInit (PDEVICE_LIST pdl) { BOOL fOk = FALSE; PDEVICE_INTERFACE pInterface; SETFNAME (_T ("PlatformDeviceListInit")); PREFAST_DEBUGCHK (pdl != NULL); DEBUGCHK (pdl->pGuid != NULL); if (*pdl->pGuid == idPMDisplayDeviceClass) { PMLOGMSG (ZONE_INIT || ZONE_PLATFORM, (_T ("%s: using display interface to access class %08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x\r\n"), pszFname, pdl->pGuid->Data1, pdl->pGuid->Data2, pdl->pGuid->Data3, (pdl->pGuid->Data4[0] << 8) + pdl->pGuid->Data4[1], pdl->pGuid->Data4[2], pdl->pGuid->Data4[3], pdl->pGuid->Data4[4], pdl->pGuid->Data4[5], pdl->pGuid->Data4[6], pdl->pGuid->Data4[7])); // Use the display driver interface to get to the device. To remove // display code from the link, edit or conditionally compile this code out // of the Power Manager. extern DEVICE_INTERFACE gDisplayInterface; // defined in the MDD pInterface = &gDisplayInterface; } else { // Use the standard stream interface to get to the device: PMLOGMSG (ZONE_INIT || ZONE_PLATFORM, (_T ("%s: using stream interface to access class %08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x\r\n"), pszFname, pdl->pGuid->Data1, pdl->pGuid->Data2, pdl->pGuid->Data3, (pdl->pGuid->Data4[0] << 8) + pdl->pGuid->Data4[1], pdl->pGuid->Data4[2], pdl->pGuid->Data4[3], pdl->pGuid->Data4[4], pdl->pGuid->Data4[5], pdl->pGuid->Data4[6], pdl->pGuid->Data4[7])); extern DEVICE_INTERFACE gStreamInterface; // defined in the MDD pInterface = &gStreamInterface; } // Try to initialize the interface: if (pInterface != NULL) { if (pInterface->pfnInitInterface () == FALSE) { PMLOGMSG (ZONE_WARN, (_T ("%s: warning: pfnInitInterface() failed for interface\r\n"), pszFname)); } else { // Pass back the pointer pdl->pInterface = pInterface; fOk = TRUE; } } return fOk; }
// This routine reads the registry to determine what type of device interfaces // we will be monitoring. The default PM GUID is ignored if present in the // registry and is always added last (so it's first in the list). BOOL DeviceListsInit(VOID) { BOOL fOk = TRUE; PDEVICE_LIST pdl; DWORD dwStatus; HKEY hk; TCHAR szBuf[MAX_PATH]; #ifndef SHIP_BUILD SETFNAME(_T("DeviceListsInit")); #endif // enumerate all the device classes StringCchPrintf(szBuf,_countof(szBuf), _T("%s\\Interfaces"), PWRMGR_REG_KEY); dwStatus = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szBuf, 0, 0, &hk); if(dwStatus == ERROR_SUCCESS) { DWORD dwIndex = 0; do { DWORD cbValueName = dim(szBuf), dwType; GUID idInterface; dwStatus = RegEnumValue(hk, dwIndex, szBuf, &cbValueName, NULL, &dwType, NULL, NULL); if(dwStatus == ERROR_SUCCESS) { if(dwType != REG_SZ) { PMLOGMSG(ZONE_WARN, (_T("%s: invalid type for value '%s'\r\n"), pszFname, szBuf)); } else if(!ConvertStringToGuid(szBuf, &idInterface)) { PMLOGMSG(ZONE_WARN, (_T("%s: can't convert '%s' to GUID\r\n"), pszFname, szBuf)); } else if(idInterface == idGenericPMDeviceClass) { PMLOGMSG(ZONE_INIT, (_T("%s: default GUID found in registry as expected\r\n"), pszFname)); } else if((pdl = DeviceListCreate(&idInterface)) == NULL) { PMLOGMSG(ZONE_WARN, (_T("%s: DeviceListCreate() failed\r\n"), pszFname)); } else if(PlatformDeviceListInit(pdl) == FALSE) { PMLOGMSG(ZONE_WARN, (_T("%s: PlatformDeviceListInit() failed\r\n"), pszFname)); DeviceListDestroy(pdl); } else { // add the new entry to the list pdl->pNext = gpDeviceLists; gpDeviceLists = pdl; } // update the index dwIndex++; } } while(dwStatus == ERROR_SUCCESS); // check for abnormal termination of the loop if(dwStatus != ERROR_NO_MORE_ITEMS) { fOk = FALSE; } // close the registry handle RegCloseKey(hk); } // add the default list last if(fOk) { fOk = FALSE; pdl = DeviceListCreate(&idGenericPMDeviceClass); if(pdl != NULL) { if(PlatformDeviceListInit(pdl) == FALSE) { PMLOGMSG(ZONE_INIT || ZONE_WARN, (_T("%s: PlatformDeviceListInit() failed for default class\r\n"), pszFname)); DeviceListDestroy(pdl); } else { pdl->pNext = gpDeviceLists; gpDeviceLists = pdl; fOk = TRUE; } } } // clean up if necessary if(!fOk) { PMLOGMSG(ZONE_WARN, (_T("%s: error during list initialization\r\n"), pszFname)); while(gpDeviceLists != NULL) { pdl = gpDeviceLists; gpDeviceLists = pdl->pNext; pdl->pNext = NULL; DeviceListDestroy(pdl); } } return fOk; }
EXTERN_C VOID WINAPI PlatformResumeSystem (void) { SETFNAME (_T ("PlatformResumeSystem")); PMLOGMSG (ZONE_RESUME, (_T ("+%s: suspend flag is %d\r\n"), pszFname, gfSystemSuspended)); // Was this an unexpected resume event? If so, there may be a thread priority problem // or some piece of software suspended the system without calling SetSystemPowerState(). DEBUGCHK (gfSystemSuspended); if (!gfSystemSuspended) { // Unexpected resume -- go to the resuming state. This should not happen unless // somebody is illegally calling PowerOffSystem() directly. PMLOGMSG (ZONE_WARN || ZONE_RESUME, (_T ("%s: WARNING: unexpected resume!\r\n"), pszFname)); // Go into the new state. OEMs that choose to support unexpected resumes may want to // lock PM variables with PMLOCK(), then set the curDx and actualDx values for all // devices to PwrDeviceUnspecified before calling PmSetSystemPowerState_I(). This will // force an update IOCTL to all devices. DEBUGCHK (ghevRestartTimers != NULL); SetEvent (ghevRestartTimers); } else { DWORD dwWakeSource, dwBytesReturned; BOOL fOk; // Get the system wake source to help determine which power state we resume into: fOk = KernelIoControl (IOCTL_HAL_GET_WAKE_SOURCE, NULL, 0, &dwWakeSource, sizeof (dwWakeSource), &dwBytesReturned); if (fOk) { // IOCTL succeeded (not all platforms necessarily support it), but sanity check // the return value, just in case. if (dwBytesReturned != sizeof (dwWakeSource)) { PMLOGMSG (ZONE_WARN, (_T ("%s: KernelIoControl() returned an invalid size %d\r\n"), pszFname, dwBytesReturned)); } else { // Look for an activity timer corresponding to this wake source. PACTIVITY_TIMER pat = ActivityTimerFindByWakeSource (dwWakeSource); if (pat != NULL) { PMLOGMSG (ZONE_RESUME, (_T ("%s: signaling '%s' activity at resume\r\n"), pszFname, pat->pszName)); // Is there an activity timer we need to reset? if (pat->hevReset != NULL) { // Found a timer, elevate the timer management priority thread so that it // executes before the suspending thread. DWORD dwOldPriority = CeGetThreadPriority (ghtActivityTimers); DWORD dwNewPriority = (CeGetThreadPriority (GetCurrentThread ()) - 1); DEBUGCHK (dwNewPriority >= 0); SetEvent (pat->hevReset); CeSetThreadPriority (ghtActivityTimers, dwNewPriority); CeSetThreadPriority (ghtActivityTimers, dwOldPriority); } } } } } PMLOGMSG (ZONE_RESUME, (_T ("-%s\r\n"), pszFname)); }
EXTERN_C VOID WINAPI PlatformManageSystemPower (HANDLE hevReady) { BOOL fDone = FALSE; HANDLE hqNotify = NULL; HMODULE hmCoreDll = NULL; SETFNAME (_T ("PlatformManageSystemPower")); PMLOGMSG (ZONE_INIT || ZONE_PLATFORM, (_T ("+%s\r\n"), pszFname)); // Initialize globals: ghevReloadActivityTimeouts = NULL; ghevRestartTimers = NULL; // Determine thread priority settings while we're suspending (in case // of priority inversion): if (!GetPMThreadPriority (_T ("PreSuspendPriority256"), &giPreSuspendPriority)) { giPreSuspendPriority = DEF_PRESUSPEND_THREAD_PRIORITY; } if (!GetPMThreadPriority (_T ("SuspendPriority256"), &giSuspendPriority)) { giSuspendPriority = DEF_SUSPEND_THREAD_PRIORITY; } // Get pointers to GWES's suspend/routine APIs. These require GWES, so the OEM may // not have them on this platform. Also get battery level APIs, which require a // battery driver and may not be present. hmCoreDll = (HMODULE) LoadLibrary (_T ("coredll.dll")); gfGwesReady = FALSE; PmInitPowerStatus (hmCoreDll); if (hmCoreDll != NULL) { gpfnGwesPowerDown = (PFN_GwesPowerDown) GetProcAddress (hmCoreDll, _T ("GwesPowerDown")); gpfnGwesPowerUp = (PFN_GwesPowerUp) GetProcAddress (hmCoreDll, _T ("GwesPowerUp")); // Do we have both GWES suspend/resume APIs? if (gpfnGwesPowerDown == NULL || gpfnGwesPowerUp == NULL) { // No, ignore GWES. gpfnGwesPowerDown = NULL; gpfnGwesPowerUp = NULL; } } // Create events: ghevReloadActivityTimeouts = CreateEvent (NULL, FALSE, FALSE, _T ("PowerManager/ReloadActivityTimeouts")); ghevRestartTimers = CreateEvent (NULL, FALSE, FALSE, NULL); if (ghevReloadActivityTimeouts == NULL || ghevRestartTimers == NULL) { PMLOGMSG (ZONE_WARN, (_T ("%s: CreateEvent() failed for system event\r\n"), pszFname)); goto done; } // Create our notification queue: hqNotify = PmPolicyCreateNotificationQueue (); if (hqNotify == NULL) { PMLOGMSG (ZONE_WARN, (_T ("%s: PmPolicyCreateNotificationQueue() failed\r\n"), pszFname)); goto done; } if (!fDone) { // Instantiate the PowerStateManager object and call its Init method: PowerStateManager *pPowerStateManager = new PowerStateManager (); if (pPowerStateManager && pPowerStateManager->Init ()) { g_pPowerStateManager = pPowerStateManager; // We're up and running: SetEvent (hevReady); g_pPowerStateManager->ThreadRun (); } else { // Power Manager initialization failed: ASSERT (FALSE); if (pPowerStateManager) delete pPowerStateManager; PMLOGMSG (ZONE_INIT || ZONE_ERROR, (_T ("%s: PowerStateManager Intialization Failed!!!\r\n"), pszFname)); } if (g_pPowerStateManager) { delete g_pPowerStateManager; g_pPowerStateManager = NULL; } } done: // Clean up before exiting: if (ghevReloadActivityTimeouts == NULL) { CloseHandle (ghevReloadActivityTimeouts); ghevReloadActivityTimeouts = NULL; } if (ghevRestartTimers != NULL) { CloseHandle (ghevRestartTimers); ghevRestartTimers = NULL; } if (hqNotify != NULL) { PmPolicyCloseNotificationQueue (hqNotify); hqNotify = NULL; } if (hmCoreDll != NULL) FreeLibrary (hmCoreDll); PMLOGMSG (ZONE_PLATFORM, (_T ("-%s: exiting\r\n"), pszFname)); }
EXTERN_C DWORD WINAPI PlatformSetSystemPowerState (LPCTSTR pszName, BOOL fForce, BOOL fInternal) { DWORD dwStatus = ERROR_SUCCESS; PSYSTEM_POWER_STATE pNewSystemPowerState = NULL; PDEVICE_POWER_RESTRICTION pNewCeilingDx = NULL; BOOL fDoTransition = FALSE; INT iPreSuspendPriority = 0; static BOOL fFirstCall = TRUE; SETFNAME (_T ("PlatformSetSystemPowerState")); // Read system power state variables and construct new lists: if (gfFileSystemsAvailable) PmUpdateSystemPowerStatesIfChanged (); dwStatus = RegReadSystemPowerState (pszName, &pNewSystemPowerState, &pNewCeilingDx); // Did we get registry information about the new power state? if (dwStatus == ERROR_SUCCESS) { BOOL fSuspendSystem = FALSE; static BOOL fWantStartupScreen = FALSE; DWORD dwNewStateFlags = pNewSystemPowerState->dwFlags; // Assume we will update the system power state: fDoTransition = TRUE; // Are we going to suspend the system as a whole? if ((dwNewStateFlags & (POWER_STATE_SUSPEND | POWER_STATE_OFF | POWER_STATE_CRITICAL | POWER_STATE_RESET)) != 0) { fSuspendSystem = TRUE; } // A "critical" suspend might mean we have totally lost battery power and need // to suspend really quickly. Depending on the platform, OEMs may be able // to bypass driver notification entirely and rely on xxx_PowerDown() notifications // to suspend gracefully. Or they may be able to implement a critical suspend // kernel ioctl. This sample implementation is very generic and simply sets the // POWER_FORCE flag, which is not used. if (dwNewStateFlags & (POWER_STATE_CRITICAL | POWER_STATE_OFF | POWER_STATE_RESET)) { fForce = TRUE; } // If everything seems OK, do the set operation: if (fDoTransition) { POWER_BROADCAST_BUFFER pbb; PDEVICE_LIST pdl; BOOL fResumeSystem = FALSE; // Send out system power state change notifications: pbb.Message = PBT_TRANSITION; pbb.Flags = pNewSystemPowerState->dwFlags; pbb.Length = _tcslen (pNewSystemPowerState->pszName) + 1; // Char count not byte count for now if (pbb.Length > MAX_PATH) { // Truncate the system power state name -- note, we actually have MAX_PATH + 1 // characters available. pbb.Length = MAX_PATH; } _tcsncpy_s (pbb.SystemPowerState, _countof (pbb.SystemPowerState), pNewSystemPowerState->pszName, pbb.Length); pbb.Length *= sizeof (pbb.SystemPowerState[0]); // Convert to byte count GenerateNotifications ((PPOWER_BROADCAST) & pbb); // Is GWES ready? if (!gfGwesReady) { if (WaitForAPIReady (SH_GDI, 0) == WAIT_OBJECT_0) { gfGwesReady = TRUE; } } // Are we suspending? if (fSuspendSystem && gpfnGwesPowerDown != NULL) { // Start the process of suspending GWES: if (gfGwesReady) { fWantStartupScreen = gpfnGwesPowerDown (); } } // Update global system state variables: PMLOCK (); PSYSTEM_POWER_STATE pOldSystemPowerState = gpSystemPowerState; PDEVICE_POWER_RESTRICTION pOldCeilingDx = gpCeilingDx; if (gpSystemPowerState != NULL && (gpSystemPowerState-> dwFlags & (POWER_STATE_SUSPEND | POWER_STATE_OFF | POWER_STATE_CRITICAL)) != 0) { // We are exiting a suspended state: fResumeSystem = TRUE; } gpSystemPowerState = pNewSystemPowerState; gpCeilingDx = pNewCeilingDx; PMUNLOCK (); // Are we suspending, resuming, or neither? if (fSuspendSystem) { INT iCurrentPriority; // We're suspending: update all devices other than block devices, // in case any of them need to access the registry or write files. PMLOGMSG (ZONE_PLATFORM || ZONE_RESUME, (_T ("%s: suspending - notifying non-block drivers\r\n"), pszFname)); for (pdl = gpDeviceLists; pdl != NULL; pdl = pdl->pNext) { if (*pdl->pGuid != idBlockDevices) { UpdateClassDeviceStates (pdl); } } // Notify the kernel that we are about to suspend. This gives the // kernel an opportunity to clear wake source flags before we initiate // the suspend process. If we don't do this and a wake source interrupt // occurs between the time we call PowerOffSystem() and the time // OEMPowerOff() is invoked, it is hard for the kernel to know whether or // not to suspend. PMLOGMSG (ZONE_PLATFORM || ZONE_RESUME, (_T ("%s: calling KernelIoControl(IOCTL_HAL_PRESUSPEND)\r\n"), pszFname)); KernelIoControl (IOCTL_HAL_PRESUSPEND, NULL, 0, NULL, 0, NULL); iCurrentPriority = CeGetThreadPriority (GetCurrentThread ()); DEBUGCHK (iCurrentPriority != THREAD_PRIORITY_ERROR_RETURN); if (iCurrentPriority != THREAD_PRIORITY_ERROR_RETURN) { CeSetThreadPriority (GetCurrentThread (), giPreSuspendPriority); Sleep (0); CeSetThreadPriority (GetCurrentThread (), iCurrentPriority); } // Notify file systems that their block drivers will soon go away. // After making this call, this thread is the only one that can access // the file system (including registry and device drivers) without // blocking. Unfortunately, this API takes and holds the file system // critical section, so other threads attempting to access the registry // or files may cause priority inversions. To avoid priority problem // that may starve the Power Manager, we may raise our own priority to a // high level. Do this if giSuspendPriority is non-zero. if (giSuspendPriority != 0) { iPreSuspendPriority = CeGetThreadPriority (GetCurrentThread ()); DEBUGCHK (iPreSuspendPriority != THREAD_PRIORITY_ERROR_RETURN); PMLOGMSG (ZONE_PLATFORM, (_T ("%s: suspending: raising thread priority for 0x%08x from %d to %d\r\n"), pszFname, GetCurrentThreadId (), iPreSuspendPriority, giSuspendPriority)); CeSetThreadPriority (GetCurrentThread (), giSuspendPriority); } // Discard code pages from drivers. This is a diagnostic tool to // forcibly expose paging-related bugs that could cause apparently // random system crashes or hangs. Optionally, OEMs can disable this // for production systems to speed up resume times. We have to call // PageOutMode before FileSys Shutdown. Otherwise, it cause dead lock // between filesystem and loader. if (gfPageOutAllModules) { ForcePageout (); } if (g_pSysRegistryAccess) g_pSysRegistryAccess->EnterLock (); gfFileSystemsAvailable = FALSE; if ((dwNewStateFlags & POWER_STATE_RESET) != 0) { // Is this to be a cold boot? if (_tcscmp (pszName, _T ("coldreboot")) == 0) { SetCleanRebootFlag (); } } FileSystemPowerFunction (FSNOTIFY_POWER_OFF); // Update block device power states: PMLOGMSG (ZONE_PLATFORM || ZONE_RESUME, (_T ("%s: suspending - notifying block drivers\r\n"), pszFname)); pdl = GetDeviceListFromClass (&idBlockDevices); if (pdl != NULL) { UpdateClassDeviceStates (pdl); } // Handle resets and shutdowns here, after flushing files. Since Windows CE does // not define a standard mechanism for handling shutdown (via POWER_STATE_OFF), // OEMs will need to fill in the appropriate code here. Similarly, if an OEM does // not support IOCTL_HAL_REBOOT, they should not support POWER_STATE_RESET. if ((dwNewStateFlags & POWER_STATE_RESET) != 0) { // Should not return from this call, but if we do just suspend the system: KernelLibIoControl ((HANDLE) KMOD_OAL, IOCTL_HAL_REBOOT, NULL, 0, NULL, 0, NULL); RETAILMSG (TRUE, (_T ("PM: PlatformSetSystemPowerState: KernelIoControl(IOCTL_HAL_REBOOT) returned!\r\n"))); DEBUGCHK (FALSE); // Break into the debugger. } } else if (fResumeSystem) { // We're waking up from a resume -- update block device power states // so we can access the registry and/or files. PMLOGMSG (ZONE_PLATFORM || ZONE_RESUME, (_T ("%s: resuming - notifying block drivers\r\n"), pszFname)); pdl = GetDeviceListFromClass (&idBlockDevices); if (pdl != NULL) { UpdateClassDeviceStates (pdl); } // Notify file systems that their block drivers are back. FileSystemPowerFunction (FSNOTIFY_POWER_ON); gfFileSystemsAvailable = TRUE; if (g_pSysRegistryAccess) g_pSysRegistryAccess->LeaveLock (); // Update all devices other than block devices: PMLOGMSG (ZONE_PLATFORM || ZONE_RESUME, (_T ("%s: resuming - notifying block drivers\r\n"), pszFname)); for (pdl = gpDeviceLists; pdl != NULL; pdl = pdl->pNext) { if (*pdl->pGuid != idBlockDevices) { UpdateClassDeviceStates (pdl); } } // Tell GWES to wake up: if (gpfnGwesPowerUp != NULL && gfGwesReady) { gpfnGwesPowerUp (fWantStartupScreen); fWantStartupScreen = FALSE; } // Send out resume notification: pbb.Message = PBT_RESUME; pbb.Flags = 0; pbb.Length = 0; pbb.SystemPowerState[0] = 0; GenerateNotifications ((PPOWER_BROADCAST) & pbb); } else { // Update all devices without any particular ordering: UpdateAllDeviceStates (); } // Release the old state information: SystemPowerStateDestroy (pOldSystemPowerState); while (pOldCeilingDx != NULL) { PDEVICE_POWER_RESTRICTION pdpr = pOldCeilingDx->pNext; PowerRestrictionDestroy (pOldCeilingDx); pOldCeilingDx = pdpr; } // Are we suspending? if (fSuspendSystem) { // Set a flag to notify the resume thread that this was a controlled suspend. gfSystemSuspended = TRUE; PMLOGMSG (ZONE_PLATFORM || ZONE_RESUME, (_T ("%s: calling PowerOffSystem()\r\n"), pszFname)); PowerOffSystem (); // Sets a flag in the kernel for the scheduler. Sleep (0); // Force the scheduler to run. PMLOGMSG (ZONE_PLATFORM || ZONE_RESUME, (_T ("%s: back from PowerOffSystem()\r\n"), pszFname)); // Clear the suspend flag: gfSystemSuspended = FALSE; } } else { // Release the unused new state information: SystemPowerStateDestroy (pNewSystemPowerState); while (pNewCeilingDx != NULL) { PDEVICE_POWER_RESTRICTION pdpr = pNewCeilingDx->pNext; PowerRestrictionDestroy (pNewCeilingDx); pNewCeilingDx = pdpr; } } } // Restore our priority if we updated it during a suspend transition: if (giSuspendPriority != 0 && iPreSuspendPriority != 0) { PMLOGMSG (ZONE_PLATFORM, (_T ("%s: restoring thread priority to %d\r\n"), pszFname, iPreSuspendPriority)); CeSetThreadPriority (GetCurrentThread (), iPreSuspendPriority); } return dwStatus; }
EXTERN_C DWORD WINAPI PlatformMapPowerStateHint (DWORD dwHint, LPTSTR pszBuf, DWORD dwBufChars) { DWORD dwStatus = ERROR_SUCCESS; LPTSTR pszMappedStateName = NULL; SETFNAME (_T ("PlatformMapPowerStateHint")); // Mask off unused bits: dwHint &= (POWER_STATE_ON | POWER_STATE_USERIDLE | POWER_STATE_IDLE | POWER_STATE_SUSPEND | POWER_STATE_OFF | POWER_STATE_RESET | POWER_STATE_CRITICAL); // Try to map the hint value. Note that only one bit at a time should be set. switch (dwHint) { case POWER_STATE_ON: pszMappedStateName = _T ("on"); break; case POWER_STATE_IDLE: pszMappedStateName = _T ("screenoff"); break; case POWER_STATE_SUSPEND: pszMappedStateName = _T ("suspend"); break; case POWER_STATE_OFF: // Power off, cold boot on resume. pszMappedStateName = _T ("off"); // Gard break; case POWER_STATE_CRITICAL: // Catastrophic power loss, shut down immediately. pszMappedStateName = _T ("suspend"); break; case POWER_STATE_RESET: // Flush files and reboot. pszMappedStateName = _T ("reboot"); break; case POWER_STATE_USERIDLE: pszMappedStateName = _T ("useridle"); break; default: PMLOGMSG (ZONE_PLATFORM | ZONE_WARN, (_T ("%s: bad hint value 0x%x\r\n"), pszFname, dwHint)); dwStatus = ERROR_FILE_NOT_FOUND; break; } DEBUGCHK (dwStatus == ERROR_SUCCESS); // If we were able to map the hint to a state name, copy it back to the caller: if (pszMappedStateName != NULL) { if (dwBufChars < (_tcslen (pszMappedStateName) + 1)) { dwStatus = ERROR_INSUFFICIENT_BUFFER; } else { _tcscpy_s (pszBuf, dwBufChars, pszMappedStateName); } } PMLOGMSG (dwStatus != ERROR_SUCCESS && ZONE_WARN, (_T ("%s: returning %d\r\n"), pszFname, dwStatus)); return dwStatus; }
// this thread handles activity timer events DWORD WINAPI ActivityTimersThreadProc(LPVOID lpvParam) { DWORD dwStatus, dwNumEvents, dwWaitInterval; HANDLE hevReady = (HANDLE) lpvParam; HANDLE hEvents[MAXIMUM_WAIT_OBJECTS]; BOOL fDone = FALSE; HANDLE hevDummy = NULL; INT iPriority; const DWORD cdwTimerBaseIndex = 2; #ifndef SHIP_BUILD SETFNAME(_T("ActivityTimersThreadProc")); #endif PMLOGMSG(ZONE_INIT, (_T("+%s: thread 0x%08x\r\n"), pszFname, GetCurrentThreadId())); // set the thread priority if(!GetPMThreadPriority(_T("TimerPriority256"), &iPriority)) { iPriority = DEF_ACTIVITY_TIMER_THREAD_PRIORITY; } CeSetThreadPriority(GetCurrentThread(), iPriority); // initialize the list of activity timers if(ActivityTimerInitList() != ERROR_SUCCESS) { PMLOGMSG(ZONE_WARN, (_T("%s: ActivityTimerInitList() failed\r\n"), pszFname)); goto done; } // create a dummy event that's never signaled hevDummy = CreateEvent(NULL, FALSE, FALSE, NULL); if(hevDummy == NULL) { PMLOGMSG(ZONE_WARN, (_T("%s: Couldn't create dummy event\r\n"), pszFname)); goto done; } // set up the list of events dwNumEvents = 0; hEvents[dwNumEvents++] = ghevPmShutdown; hEvents[dwNumEvents++] = ghevTimerResume; PMLOCK(); if(gppActivityTimers[0] == NULL) { // no activity timers defined PmFree(gppActivityTimers); gppActivityTimers = NULL; } else { // copy activity timer events into the event list while(dwNumEvents < dim(hEvents) && gppActivityTimers[dwNumEvents - cdwTimerBaseIndex] != NULL) { hEvents[dwNumEvents] = gppActivityTimers[dwNumEvents - cdwTimerBaseIndex]->hevReset; dwNumEvents++; } } PMUNLOCK(); // we're up and running SetEvent(hevReady); // are there actually any timers to wait on? if(dwNumEvents <= cdwTimerBaseIndex) { // no timers defined, so we don't need this thread to wait on them. PMLOGMSG(ZONE_INIT || ZONE_WARN, (_T("%s: no activity timers defined, exiting\r\n"), pszFname)); Sleep(1000); // don't want PM initialization to fail when we exit goto done; } // wait for these events to get signaled PMLOGMSG(ZONE_TIMERS, (_T("%s: entering wait loop, %d timers total\r\n"), pszFname, dwNumEvents - cdwTimerBaseIndex)); dwWaitInterval = 0; while(!fDone) { DWORD dwTimeout = GetNextInactivityTimeout(dwWaitInterval); DWORD dwWaitStart = GetTickCount(); PMLOGMSG(ZONE_TIMERS, (_T("%s: waiting %u (0x%08x) ms for next event, wait interval was %d\r\n"), pszFname, dwTimeout, dwTimeout, dwWaitInterval)); dwStatus = WaitForMultipleObjects(dwNumEvents, hEvents, FALSE, dwTimeout); dwWaitInterval = GetTickCount() - dwWaitStart; // figure out what caused the wakeup if(dwStatus == (WAIT_OBJECT_0 + 0)) { PMLOGMSG(ZONE_WARN, (_T("%s: shutdown event set\r\n"), pszFname)); fDone = TRUE; } else if(dwStatus == (WAIT_OBJECT_0 + 1)) { DWORD dwIndex; PACTIVITY_TIMER pat; // we've resumed, so re-enable all activity timers that can be reset PMLOGMSG(ZONE_TIMERS, (_T("%s: resume event set\r\n"), pszFname)); PMLOCK(); for(dwIndex = 0; (pat = gppActivityTimers[dwIndex]) != NULL; dwIndex++) { DWORD dwEventIndex = dwIndex + cdwTimerBaseIndex; if(hEvents[dwEventIndex] == hevDummy) { hEvents[dwEventIndex] = pat->hevReset; } pat->dwTimeLeft = pat->dwTimeout + dwWaitInterval; } PMUNLOCK(); } else if(dwStatus == WAIT_TIMEOUT) { DWORD dwIndex; PACTIVITY_TIMER pat; // figure out which event(s) timed out PMLOCK(); for(dwIndex = 0; (pat = gppActivityTimers[dwIndex]) != NULL; dwIndex++) { if(pat->dwTimeLeft <= dwWaitInterval && pat->dwTimeLeft != INFINITE) { // has the timer really expired? if(WaitForSingleObject(pat->hevReset, 0) == WAIT_OBJECT_0) { // The timer was reset while we weren't looking at it, so we'll look // at it again later. Calculate the new timeout, compensating for the update // that will occur in GetNextInactivityTimeout(). PMLOGMSG(ZONE_TIMERS, (_T("%s: timer '%s' reset after timeout\r\n"), pszFname, pat->pszName)); pat->dwTimeLeft = pat->dwTimeout + dwWaitInterval; pat->dwResetCount++; } else { // the timer has really expired, update events appropriately PMLOGMSG(ZONE_TIMERS, (_T("%s: timer '%s' has expired\r\n"), pszFname, pat->pszName)); ResetEvent(pat->hevActive); SetEvent(pat->hevInactive); // start looking at the reset event for this timer again hEvents[dwIndex + cdwTimerBaseIndex] = pat->hevReset; // update counts pat->dwTimeLeft = INFINITE; pat->dwExpiredCount++; } } } PMUNLOCK(); } else if(dwStatus > (WAIT_OBJECT_0 + 0) && dwStatus < (WAIT_OBJECT_0 + dwNumEvents)) { PACTIVITY_TIMER pat; DWORD dwEventIndex = dwStatus - WAIT_OBJECT_0; PMLOCK(); // get a pointer to the timer pat = gppActivityTimers[dwEventIndex - cdwTimerBaseIndex]; // handle its events DEBUGCHK(pat != NULL); if(pat->dwTimeout == 0) { // we're not using the event, so ignore it pat->dwTimeLeft = INFINITE; } else { PMLOGMSG(ZONE_TIMERS, (_T("%s: timer '%s' reset\r\n"), pszFname, pat->pszName)); // set events appropriately ResetEvent(pat->hevInactive); SetEvent(pat->hevActive); // don't look at this event again until it's about ready to time out hEvents[dwEventIndex] = hevDummy; // update time left on the timer, compensating for the update // that will occur in GetNextInactivityTimeout(). pat->dwTimeLeft = pat->dwTimeout + dwWaitInterval; } pat->dwResetCount++; PMUNLOCK(); } else { PMLOGMSG(ZONE_WARN, (_T("%s: WaitForMultipleObjects() returned %d, status is %d\r\n"), pszFname, dwStatus, GetLastError())); fDone = TRUE; } } done: // release resources if(hevDummy != NULL) CloseHandle(hevDummy); PMLOCK(); if(gppActivityTimers != NULL) { DWORD dwIndex = 0; while(gppActivityTimers[dwIndex] != NULL) { ActivityTimerDestroy(gppActivityTimers[dwIndex]); dwIndex++; } PmFree(gppActivityTimers); gppActivityTimers = NULL; } PMUNLOCK(); PMLOGMSG(ZONE_INIT | ZONE_WARN, (_T("-%s: exiting\r\n"), pszFname)); return 0; }
// This routine initializes the list of activity timers. It returns ERROR_SUCCESS // if successful or a Win32 error code otherwise. DWORD ActivityTimerInitList(VOID) { DWORD dwStatus; HKEY hk = NULL; TCHAR szSources[1024]; DWORD dwNumTimers = 0; PPACTIVITY_TIMER ppatList = NULL; #ifndef SHIP_BUILD SETFNAME(_T("ActivityTimerInitList")); #endif wsprintf(szSources, _T("%s\\ActivityTimers"), PWRMGR_REG_KEY); dwStatus = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szSources, 0, 0, &hk); if(dwStatus == ERROR_SUCCESS) { // figure out how many values are associated with the key dwStatus = RegQueryInfoKey(hk, NULL, NULL, NULL, &dwNumTimers, NULL, NULL, NULL, NULL, NULL, NULL, NULL); } else { // no timers configured in the registry dwNumTimers = 0; dwStatus = ERROR_SUCCESS; } // if there are any values, allocate an array to hold them if(dwStatus == ERROR_SUCCESS) { ppatList = (PPACTIVITY_TIMER) PmAlloc((dwNumTimers + 1) * sizeof(PACTIVITY_TIMER)); if(ppatList == NULL) { PMLOGMSG(ZONE_WARN, (_T("%s: couldn't allocate %d timers\r\n"), pszFname, dwNumTimers)); dwStatus = ERROR_NOT_ENOUGH_MEMORY; } else { memset(ppatList, 0, (dwNumTimers + 1) * sizeof(PACTIVITY_TIMER)); ppatList[dwNumTimers] = NULL; } } // read list of timers if(dwStatus == ERROR_SUCCESS && dwNumTimers != 0) { DWORD dwIndex = 0; do { TCHAR szName[256]; DWORD cbValueName = dim(szName); dwStatus = RegEnumKeyEx(hk, dwIndex, szName, &cbValueName, NULL, NULL, NULL, NULL); if(dwStatus == ERROR_SUCCESS) { HKEY hkSubKey = NULL; // open the subkey dwStatus = RegOpenKeyEx(hk, szName, 0, 0, &hkSubKey); if(dwStatus == ERROR_SUCCESS) { DWORD dwSize, dwType, dwTimeout; LPTSTR pszValueName; // read the timeout, expressed in seconds dwSize = sizeof(dwTimeout); pszValueName = _T("Timeout"); dwStatus = RegQueryValueEx(hkSubKey, pszValueName, NULL, &dwType, (LPBYTE) &dwTimeout, &dwSize); if(dwStatus == ERROR_SUCCESS) { if(dwType != REG_DWORD || dwTimeout > MAXTIMERINTERVAL) { PMLOGMSG(ZONE_WARN, (_T("%s: RegQueryValueEx('%s'\'%s') or returned invalid type %d or invalid value %d\r\n"), pszFname, szName, pszValueName, dwType, dwTimeout)); dwStatus = ERROR_INVALID_DATA; } // convert timeout to milliseconds dwTimeout *= 1000; } else { // no timeout in seconds, try milliseconds dwSize = sizeof(dwTimeout); pszValueName = _T("TimeoutMs"); dwStatus = RegQueryValueEx(hkSubKey, pszValueName, NULL, &dwType, (LPBYTE) &dwTimeout, &dwSize); if(dwStatus != ERROR_SUCCESS || dwType != REG_DWORD || dwTimeout > (MAXTIMERINTERVAL * 1000)) { PMLOGMSG(ZONE_WARN, (_T("%s: RegQueryValueEx('%s'\'%s') failed %d (or returned invalid type %d or invalid value %d)\r\n"), pszFname, szName, pszValueName, dwStatus, dwType, dwTimeout)); dwStatus = ERROR_INVALID_DATA; } } if(dwStatus == ERROR_SUCCESS) { // get wake sources dwSize = sizeof(szSources); pszValueName = _T("WakeSources"); dwStatus = RegQueryValueEx(hkSubKey, pszValueName, NULL, &dwType, (LPBYTE) szSources, &dwSize); if(dwStatus != ERROR_SUCCESS) { // no wake sources szSources[0] = 0; szSources[1] = 0; dwStatus = ERROR_SUCCESS; } else if(dwType != REG_MULTI_SZ) { PMLOGMSG(ZONE_WARN, (_T("%s: invalid type %d for '%s'\'%s'\r\n"), pszFname, dwType, szName, pszValueName)); dwStatus = ERROR_INVALID_DATATYPE; } else { szSources[dim(szSources) -1] = szSources[dim(szSources) -2] = 0; // Terminate MultiSZ } } // did we get the parameters? if(dwStatus == ERROR_SUCCESS) { ppatList[dwIndex] = ActivityTimerCreate(szName, dwTimeout, szSources); if(ppatList[dwIndex] == NULL) { dwStatus = ERROR_NOT_ENOUGH_MEMORY; } } } // release the registry key RegCloseKey(hkSubKey); } // update the index dwIndex++; } while(dwStatus == ERROR_SUCCESS && dwIndex < dwNumTimers); // did we read all items ok? if(dwStatus == ERROR_NO_MORE_ITEMS) { dwStatus = ERROR_SUCCESS; } // terminate the list with a NULL ppatList[dwIndex] = NULL; } // did we succeed? if(dwStatus == ERROR_SUCCESS) { PMLOCK(); gppActivityTimers = ppatList; PMUNLOCK(); } else { DWORD dwIndex; if(ppatList != NULL) { for(dwIndex = 0; dwIndex < dwNumTimers; dwIndex++) { if(ppatList[dwIndex] != NULL) { ActivityTimerDestroy(ppatList[dwIndex]); } } PmFree(ppatList); } } // release resources if(hk != NULL) RegCloseKey(hk); PMLOGMSG(dwStatus != ERROR_SUCCESS && ZONE_WARN, (_T("%s: returning %d\r\n"), pszFname, dwStatus)); return dwStatus; }
// This routine sets up a proxy relationship between a parent device // and a child device. This will overwrite any other proxy relationships // created for the child device. Only one level of relationships is // supported; that is, a parent device cannot itself be a child device. // This routine passes back an handle to the relationship and sets the // global error status to: // ERROR_SUCCESS - relationship set up ok // ERROR_INVALID_PARAMETER - bad parameter of some sort // ERROR_FILE_EXISTS - child device already registered // Note that in this implementation, a device cannot register itself // (using AdvertiseInterface()) and then have a parent register to // proxy for it. EXTERN_C HANDLE WINAPI PmRegisterPowerRelationship(PVOID pvParent, PVOID pvChild, PPOWER_CAPABILITIES pCaps, DWORD dwFlags) { PDEVICEID pdiParent = NULL, pdiChild = NULL; PDEVICE_LIST pdlParent = NULL; PDEVICE_LIST pdlChild = NULL; PDEVICE_STATE pdsParent = NULL; PDEVICE_STATE pdsChild = NULL; DWORD dwStatus = ERROR_SUCCESS; #ifndef SHIP_BUILD SETFNAME(_T("PmRegisterPowerRelationship")); #endif PMLOGMSG(ZONE_API, (_T("+%s\r\n"), pszFname)); // sanity check parameters if(pvParent == NULL || pvChild == NULL || (pdiParent = DeviceIdParseNameString((LPCTSTR) pvParent, dwFlags)) == NULL || (pdiChild = DeviceIdParseNameString((LPCTSTR) pvChild, dwFlags)) == NULL) { dwStatus = ERROR_INVALID_PARAMETER; } // parameters ok so far? if(dwStatus == ERROR_SUCCESS) { pdlChild = GetDeviceListFromClass(pdiChild->pGuid); if(dwStatus == ERROR_SUCCESS) { // Look up device lists for parent and child, plus the parent // and child device structures. The child cannot already exist. pdlParent = GetDeviceListFromClass(pdiParent->pGuid); if(pdlParent == NULL || pdlChild == NULL) { PMLOGMSG(ZONE_WARN, (_T("%s: can't find class for parent or child\r\n"), pszFname)); dwStatus = ERROR_INVALID_PARAMETER; } else if((pdsChild = DeviceStateFindList(pdlChild, pdiChild->pszName)) != NULL) { PMLOGMSG(ZONE_WARN, (_T("%s: child '%s' already exists\r\n"), pszFname, pdiChild->pszName)); DeviceStateDecRef(pdsChild); dwStatus = ERROR_FILE_EXISTS; } else { pdsParent = DeviceStateFindList(pdlParent, pdiParent->pszName); if(pdsParent == NULL) { PMLOGMSG(ZONE_WARN, (_T("%s: can't find parent '%s'\r\n"), pszFname, pdiParent->pszName)); dwStatus = ERROR_INVALID_PARAMETER; } else if(pdsParent->pParent != NULL) { PMLOGMSG(ZONE_WARN, (_T("%s: parent '%s' can't also be a child\r\n"), pszFname, pdsParent->pszName)); } } } } // if parameters were ok, proceed if(dwStatus == ERROR_SUCCESS) { // create and/or initialize the new device AddDevice(pdiChild->pGuid, pdiChild->pszName, pdsParent, pCaps); // get the return value pdsChild = DeviceStateFindList(pdlChild, pdiChild->pszName); if(pdsChild != NULL) { // we only want the pointer value for now DeviceStateDecRef(pdsChild); } else { // couldn't create the child device for some reason dwStatus = ERROR_GEN_FAILURE; } } // release resources if(pdsParent != NULL) DeviceStateDecRef(pdsParent); if(pdiParent != NULL) DeviceIdDestroy(pdiParent); if(pdiChild != NULL) DeviceIdDestroy(pdiChild); PMLOGMSG((dwStatus != ERROR_SUCCESS && ZONE_WARN) || ZONE_API, (_T("%s: returning 0x%08x, status is %d\r\n"), pszFname, pdsChild, dwStatus)); SetLastError(dwStatus); return (HANDLE) pdsChild; }
// This routine initializes the power manager and notifies the system that // its api set is ready to be used. It returns TRUE if successful and FALSE // if there's a problem. EXTERN_C BOOL WINAPI PmInit(VOID) { BOOL fOk = TRUE; HANDLE hevPnPReady = NULL, hevResumeReady = NULL, hevSystemReady = NULL; HANDLE hevActivityTimersReady = NULL, hevDummy = NULL; #ifndef SHIP_BUILD SETFNAME(_T("PmInit")); #endif PMLOGMSG(ZONE_INIT || ZONE_API, (_T("+%s\r\n"), pszFname)); // set up globals InitializeCriticalSection(&gcsPowerManager); InitializeCriticalSection(&gcsDeviceUpdateAPIs); gpFloorDx = NULL; gpCeilingDx = NULL; gpPowerNotifications = NULL; gpSystemPowerState = NULL; ghPmHeap = GetProcessHeap(); gpDeviceLists = NULL; gppActivityTimers = NULL; ghevPowerManagerReady = NULL; ghevResume = NULL; ghevTimerResume = NULL; ghtPnP = NULL; ghtResume = NULL; ghtActivityTimers = NULL; ghtSystem = NULL; // cleanup event (hopefully never used) ghevPmShutdown = CreateEvent(NULL, TRUE, FALSE, NULL); if(ghevPmShutdown == NULL) { PMLOGMSG(ZONE_ERROR, (_T("%s: CreateEvent() failed for shutdown event\r\n"), pszFname)); fOk = FALSE; } // validate the power management registry settings. OEM code should use this // routine to make sure that registry settings are present for all the power // states they expect to support. If the registry is not configured, the OEM // code can treat it as a fatal error or perform its own initialization. if(fOk) { DWORD dwStatus = PlatformValidatePMRegistry(); if(dwStatus != ERROR_SUCCESS) { PMLOGMSG(ZONE_ERROR, (_T("%s: PlatformValidatePMRegistry() failed %d\r\n"), pszFname, dwStatus)); fOk = FALSE; } else { // read the list of interface types we will monitor fOk = DeviceListsInit(); } } // create events if(fOk) { ghevPowerManagerReady = CreateEvent(NULL, TRUE, FALSE, _T("SYSTEM/PowerManagerReady")); ghevResume = CreateEvent(NULL, FALSE, FALSE, NULL); ghevTimerResume = CreateEvent(NULL, FALSE, FALSE, NULL); hevPnPReady = CreateEvent(NULL, FALSE, FALSE, NULL); hevResumeReady = CreateEvent(NULL, FALSE, FALSE, NULL); hevSystemReady = CreateEvent(NULL, FALSE, FALSE, NULL); hevActivityTimersReady = CreateEvent(NULL, FALSE, FALSE, NULL); hevDummy = CreateEvent(NULL, FALSE, FALSE, NULL); // check everything if(hevPnPReady == NULL || hevResumeReady == NULL || hevSystemReady == NULL || hevActivityTimersReady == NULL || hevDummy == NULL || ghevPowerManagerReady == NULL || ghevTimerResume == NULL || ghevResume == NULL) { PMLOGMSG(ZONE_ERROR, (_T("%s: event creation failure\r\n"), pszFname)); fOk = FALSE; } } // start threads if(fOk) { ghtPnP = CreateThread(NULL, 0, PnpThreadProc, (LPVOID) hevPnPReady, 0, NULL); ghtResume = CreateThread(NULL, 0, ResumeThreadProc, (LPVOID) hevResumeReady, 0, NULL); ghtActivityTimers = CreateThread(NULL, 0, ActivityTimersThreadProc, (LPVOID) hevActivityTimersReady, 0, NULL); // check everything if(ghtPnP == NULL || ghtResume == NULL || ghtActivityTimers == NULL) { PMLOGMSG(ZONE_ERROR, (_T("%s: thread creation failure\r\n"), pszFname)); fOk = FALSE; } } // wait for threads to initialize (or fail to initialize) #define NUM_OF_READY_EXIT_PAIR 3 HANDLE hEvents[] = { hevPnPReady, hevResumeReady, hevActivityTimersReady, ghtPnP, ghtResume, ghtActivityTimers }; int iReady = 0; while( iReady < NUM_OF_READY_EXIT_PAIR && fOk) { DWORD dwStatus = WaitForMultipleObjects(dim(hEvents), hEvents, FALSE, INFINITE); switch(dwStatus) { // thread ready events case (WAIT_OBJECT_0 + 0): // pnp ready case (WAIT_OBJECT_0 + 1): // resume ready case (WAIT_OBJECT_0 + 2): // activity timers ready // don't watch for the thread exiting now -- some may do // so if they don't have work to do. hEvents[dwStatus - WAIT_OBJECT_0 + NUM_OF_READY_EXIT_PAIR] = hevDummy; iReady++; break; // thread exiting events case (WAIT_OBJECT_0 + 3): // pnp exited case (WAIT_OBJECT_0 + 4): // resume exited case (WAIT_OBJECT_0 + 5): // activity timers exited PMLOGMSG(ZONE_ERROR, (_T("%s: thread initialization failure\r\n"), pszFname)); fOk = FALSE; break; default: PMLOGMSG(ZONE_ERROR, (_T("%s: WaitForMultipleObjects() returnd %d, status is %d\r\n"), pszFname, GetLastError())); fOk = FALSE; break; } } // load PMExt DLL, call init if (fOk) { TCHAR DevDll[DEVDLL_LEN]; DWORD cbData; DWORD Flags; HKEY hKey; gpPMExtInit = NULL; gpPMExtDeinit = NULL;; // Note: TEXT("\\Omap") is appended to the PMExt_Registry_Root because this is the only PMExt that we support if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, PMExt_Registry_Root TEXT("\\Omap"), 0, 0, &hKey)) { cbData = sizeof(DevDll); if (ERROR_SUCCESS == RegQueryValueEx(hKey, DEVLOAD_DLLNAME_VALNAME, NULL, NULL, (LPBYTE) DevDll, &cbData)) { cbData = sizeof(Flags); if (ERROR_SUCCESS != RegQueryValueEx(hKey, DEVLOAD_FLAGS_VALNAME, NULL, NULL, (LPBYTE) &Flags, &cbData)) { Flags = DEVFLAGS_NONE; } ghPMExtLib = (Flags & DEVFLAGS_LOADLIBRARY) ? LoadLibrary(DevDll) : LoadDriver(DevDll); if (!ghPMExtLib) { PMLOGMSG(ZONE_ERROR, (_T("%s: couldn't load PMExt \"%s\" -- error %d\r\n"), pszFname, DevDll, GetLastError())); } else { gpPMExtInit = (PFN_PMExt_Init) GetProcAddress(ghPMExtLib, PMExt_Init_NAME); if (!gpPMExtInit) PMLOGMSG(ZONE_ERROR, (_T("%s: \"%s\" can't GetProcAddress\r\n"), pszFname, PMExt_Init_NAME)); gpPMExtDeinit = (PFN_PMExt_DeInit) GetProcAddress(ghPMExtLib, PMExt_DeInit_NAME); } } else { PMLOGMSG(ZONE_INIT, (_T("%s: can't get value \"%s\" in key \"%s\"\r\n"), pszFname, DEVLOAD_DLLNAME_VALNAME, PMExt_Registry_Root TEXT("\\Omap"))); } if (gpPMExtInit && gpPMExtDeinit) { #ifdef DEBUG gdwPMExtContext = gpPMExtInit(HKEY_LOCAL_MACHINE, PMExt_Registry_Root TEXT("\\Omap")); #else __try { gdwPMExtContext = gpPMExtInit(HKEY_LOCAL_MACHINE, PMExt_Registry_Root TEXT("\\Omap")); } __except(EXCEPTION_EXECUTE_HANDLER) { gdwPMExtContext = 0; } #endif if (!gdwPMExtContext) PMLOGMSG(ZONE_ERROR, (_T("%s: \"%s\" failed\r\n"), pszFname, PMExt_Init_NAME)); else PMLOGMSG(ZONE_INIT, (_T("%s: \"%s\" success\r\n"), pszFname, PMExt_Init_NAME)); } RegCloseKey(hKey); }
EXTERN_C DWORD WINAPI PlatformValidatePMRegistry (VOID) { HKEY hkPM = NULL, hkSubkey; LPTSTR pszSubKey; DWORD dwStatus = ERROR_GEN_FAILURE; DWORD dwDisposition; SETFNAME (_T ("PlatformValidatePMRegistry")); PMLOGMSG (ZONE_INIT, (_T ("+%s\r\n"), pszFname)); // Open the Power Manager registry key: dwStatus = RegCreateKeyEx (HKEY_LOCAL_MACHINE, PWRMGR_REG_KEY, 0, NULL, 0, 0, NULL, &hkPM, &dwDisposition); if (dwStatus != ERROR_SUCCESS) { PMLOGMSG (ZONE_ERROR, (_T ("%s: can't open '%s', error is %d\r\n"), pszFname, PWRMGR_REG_KEY, dwStatus)); } // The registry key exists. Examine registry values and set global variables according // to registry settings: if (dwStatus == ERROR_SUCCESS && dwDisposition != REG_CREATED_NEW_KEY) { // Exit Key. DWORD dwValue = 0; DWORD dwSize = sizeof (DWORD); if (RegQueryTypedValue (hkPM, PM_SUPPORT_PB_RELEASE, &dwValue, &dwSize, REG_DWORD) == ERROR_SUCCESS) { gfSupportPowerButtonRelease = (dwValue != 0); } dwSize = sizeof (dwSize); if (RegQueryTypedValue (hkPM, L"PageOutAllModules", &dwValue, &dwSize, REG_DWORD) == ERROR_SUCCESS && dwValue != 0) gfPageOutAllModules = TRUE; else gfPageOutAllModules = FALSE; } // Verify interface GUIDs: if (dwStatus == ERROR_SUCCESS) { pszSubKey = _T ("Interfaces"); dwStatus = RegCreateKeyEx (hkPM, pszSubKey, 0, NULL, 0, 0, NULL, &hkSubkey, &dwDisposition); if (dwStatus == ERROR_SUCCESS) { if (dwDisposition == REG_CREATED_NEW_KEY) { // GUIDs for these interface classes are defined in public\COMMONk\oak\files\common.reg // and in public\COMMON\sdk\inc\pm.h LPTSTR pszName = PMCLASS_GENERIC_DEVICE; LPTSTR pszValue = _T ("Generic power-manageable devices"); dwStatus = RegSetValueEx (hkSubkey, pszName, 0, REG_SZ, (LPBYTE) pszValue, (_tcslen (pszValue) + 1) * sizeof (*pszValue)); if (dwStatus == ERROR_SUCCESS) { pszName = PMCLASS_BLOCK_DEVICE; pszValue = _T ("Power-manageable block devices"); dwStatus = RegSetValueEx (hkSubkey, pszName, 0, REG_SZ, (LPBYTE) pszValue, (_tcslen (pszValue) + 1) * sizeof (*pszValue)); } if (dwStatus == ERROR_SUCCESS) { pszName = PMCLASS_DISPLAY; pszValue = _T ("Power-manageable display drivers"); dwStatus = RegSetValueEx (hkSubkey, pszName, 0, REG_SZ, (LPBYTE) pszValue, (_tcslen (pszValue) + 1) * sizeof (*pszValue)); } if (dwStatus == ERROR_SUCCESS) { pszName = PMCLASS_NDIS_MINIPORT; pszValue = _T ("Power-manageable NDIS (network) miniport devices"); dwStatus = RegSetValueEx (hkSubkey, pszName, 0, REG_SZ, (LPBYTE) pszValue, (_tcslen (pszValue) + 1) * sizeof (*pszValue)); } if (dwStatus == ERROR_SUCCESS) { pszName = PMCLASS_BACKLIGHT; pszValue = _T ("Power-manageable backlight"); dwStatus = RegSetValueEx (hkSubkey, pszName, 0, REG_SZ, (LPBYTE) pszValue, (_tcslen (pszValue) + 1) * sizeof (*pszValue)); } if (dwStatus == ERROR_SUCCESS) { pszName = PMCLASS_TOUCH; pszValue = _T ("Power-manageable touch devices"); dwStatus = RegSetValueEx (hkSubkey, pszName, 0, REG_SZ, (LPBYTE) pszValue, (_tcslen (pszValue) + 1) * sizeof (*pszValue)); } // Other power-manageable device classes can be added here. } RegCloseKey (hkSubkey); } PMLOGMSG (dwStatus != ERROR_SUCCESS && ZONE_ERROR, (_T ("%s: error %d while creating or writing values in '%s\\%s'\r\n"), pszFname, dwStatus, PWRMGR_REG_KEY, pszSubKey)); } // Release resources if (hkPM != NULL) RegCloseKey (hkPM); PMLOGMSG (ZONE_INIT, (_T ("-%s: returning %d\r\n"), pszFname, dwStatus)); return dwStatus; }