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; }
// 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; }