1/* 2 * Copyright (c) 2002 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23/* 24 * Copyright (c) 2002 Apple Computer, Inc. All rights reserved. 25 * 26 * HISTORY 27 * 28 * 29-Aug-02 ebold created 29 * 30 */ 31 32#include <sys/stat.h> 33#include <sys/fcntl.h> 34#include <sys/sysctl.h> 35#include <sys/mount.h> 36#include <unistd.h> 37#include <dlfcn.h> 38#include <IOKit/pwr_mgt/IOPMLibPrivate.h> 39#include <IOKit/IOHibernatePrivate.h> 40#include <pthread.h> 41 42#include "PMSettings.h" 43#include "PrivateLib.h" 44#include "PMStore.h" 45#include "PMAssertions.h" 46 47/* Arguments to CopyPMSettings functions */ 48enum { 49 kIOPMUnabridgedSettings = false, 50 kIOPMRemoveUnsupportedSettings = true 51}; 52 53/* Global - energySettings 54 * Keeps track of current Energy Saver settings. 55 */ 56static CFDictionaryRef energySettings = NULL; 57 58/* Global - currentPowerSource 59 * Keeps track of current power - battery or AC 60 */ 61static CFStringRef currentPowerSource = NULL; 62 63/* g_overrides 64 * Tracks active PM usage profiles 65 */ 66static unsigned long g_overrides = 0; 67static unsigned long gLastOverrideState = 0; 68static long gSleepSetting = -1; 69 70static io_connect_t gPowerManager; 71 72/* Tracking sleeping state */ 73static unsigned long deferredPSChangeNotify = 0; 74static unsigned long _pmcfgd_impendingSleep = 0; 75 76 77/* Forward Declarations */ 78static CFDictionaryRef _copyPMSettings(bool removeUnsupported); 79static IOReturn activate_profiles( 80 CFDictionaryRef d, 81 CFStringRef s, 82 bool removeUnsupported); 83 84 85/* overrideSetting 86 * Must be followed by a call to activateSettingOverrides 87 */ 88__private_extern__ void overrideSetting 89( 90 int bit, 91 int val 92) 93{ 94 if(val) { 95 g_overrides |= bit; 96 } else { 97 g_overrides &= ~bit; 98 } 99} 100 101 102__private_extern__ bool 103GetPMSettingBool(CFStringRef which) 104{ 105 CFDictionaryRef current_settings; 106 CFNumberRef n; 107 int nint = 0; 108 CFStringRef pwrSrc; 109 110 if (!energySettings || !which) 111 return false; 112 113 114 if (_getPowerSource() == kBatteryPowered) 115 pwrSrc = CFSTR(kIOPMBatteryPowerKey); 116 else 117 pwrSrc = CFSTR(kIOPMACPowerKey); 118 // Don't use 'currentPowerSource' here as that gets updated 119 // little slowly after this function is called to get a setting 120 // on new power source. 121 current_settings = (CFDictionaryRef)isA_CFDictionary( 122 CFDictionaryGetValue(energySettings, pwrSrc)); 123 124 if (current_settings) { 125 n = CFDictionaryGetValue(current_settings, which); 126 if (n) { 127 CFNumberGetValue(n, kCFNumberIntType, &nint); 128 } 129 return (0 != nint); 130 } 131 return false; 132} 133 134 135 136__private_extern__ IOReturn 137GetPMSettingNumber(CFStringRef which, int64_t *value) 138{ 139 CFDictionaryRef current_settings; 140 CFNumberRef n; 141 CFStringRef pwrSrc; 142 143 if (!energySettings || !which) 144 return kIOReturnBadArgument; 145 146 if (_getPowerSource() == kBatteryPowered) 147 pwrSrc = CFSTR(kIOPMBatteryPowerKey); 148 else 149 pwrSrc = CFSTR(kIOPMACPowerKey); 150 // Don't use 'currentPowerSource' here as that gets updated 151 // little slowly after this function is called to get a setting 152 // on new power source. 153 current_settings = (CFDictionaryRef)isA_CFDictionary( 154 CFDictionaryGetValue(energySettings, pwrSrc)); 155 156 if (current_settings) { 157 n = CFDictionaryGetValue(current_settings, which); 158 if (isA_CFNumber(n)) { 159 CFNumberGetValue(n, kCFNumberSInt64Type, value); 160 return kIOReturnSuccess; 161 } 162 } 163 return kIOReturnError; 164} 165 166/* Returns Display sleep time in minutes */ 167__private_extern__ IOReturn 168getDisplaySleepTimer(uint32_t *displaySleepTimer) 169{ 170 CFDictionaryRef current_settings; 171 172 if (!energySettings || !displaySleepTimer) 173 return kIOReturnError; 174 175 current_settings = (CFDictionaryRef)isA_CFDictionary( 176 CFDictionaryGetValue(energySettings, currentPowerSource)); 177 if (getAggressivenessValue(current_settings, CFSTR(kIOPMDisplaySleepKey), 178 kCFNumberSInt32Type, displaySleepTimer) ) { 179 return kIOReturnSuccess; 180 } 181 182 return kIOReturnError; 183} 184 185/* Returns Idle sleep time in minutes */ 186__private_extern__ IOReturn 187getIdleSleepTimer(uint32_t *idleSleepTimer) 188{ 189 CFDictionaryRef current_settings; 190 191 if (!energySettings || !idleSleepTimer) 192 return kIOReturnError; 193 194 if (gSleepSetting != -1) { 195 *idleSleepTimer = gSleepSetting; 196 return kIOReturnSuccess; 197 } 198 199 current_settings = (CFDictionaryRef)isA_CFDictionary( 200 CFDictionaryGetValue(energySettings, currentPowerSource)); 201 if (getAggressivenessValue(current_settings, CFSTR(kIOPMSystemSleepKey), 202 kCFNumberSInt32Type, idleSleepTimer) ) { 203 return kIOReturnSuccess; 204 } 205 206 return kIOReturnError; 207} 208 209// Providing activateSettingsOverrides to PMAssertions.c 210// So that it may set multiple assertions without triggering a prefs 211// re-evaluate each time. PMAssertions.c can call overrideSetting() n times 212// and only call activateSettingsOverrides once. 213__private_extern__ void 214activateSettingOverrides(void) 215{ 216 if (!energySettings) 217 return; 218 219 if (gLastOverrideState != g_overrides) 220 { 221 if ((kPMPreventIdleSleep == (gLastOverrideState ^ g_overrides)) 222 && (-1 != gSleepSetting)) do 223 { 224 static io_connect_t gIOPMConnection = MACH_PORT_NULL; 225 IOReturn kr; 226 227 if (!gIOPMConnection) gIOPMConnection = IOPMFindPowerManagement(0); 228 if (!gIOPMConnection) break; 229 kr = IOPMSetAggressiveness(gIOPMConnection, kPMMinutesToSleep, 230 (kPMPreventIdleSleep & g_overrides) ? 0 : gSleepSetting); 231 if (kIOReturnSuccess != kr) 232 { 233 gIOPMConnection = MACH_PORT_NULL; 234 break; 235 } 236 gLastOverrideState = g_overrides; 237 return; 238 } 239 while (false); 240 241 gLastOverrideState = g_overrides; 242 activate_profiles( energySettings, 243 currentPowerSource, 244 kIOPMRemoveUnsupportedSettings); 245 } 246} 247 248__private_extern__ void 249PMSettingsSleepWakeNotification(natural_t messageType) 250{ 251 // note: The sleepwake handler in pmconfigd.c does all the dirty work like 252 // acknowledging this sleep notification with IOAllowPowerChange(). That's 253 // why we don't make that call here. 254 255 switch (messageType) { 256 case kIOMessageSystemWillSleep: 257 _pmcfgd_impendingSleep = 1; 258 break; 259 260 case kIOMessageSystemHasPoweredOn: 261 _pmcfgd_impendingSleep = 0; 262 if(deferredPSChangeNotify) 263 { 264 deferredPSChangeNotify = 0; 265 _pmcfgd_impendingSleep = 0; 266 267 if(currentPowerSource && CFEqual(currentPowerSource, CFSTR(kIOPMACPowerKey))) 268 { 269 // ac power 270 IOPMSetAggressiveness(gPowerManager, kPMPowerSource, kIOPMExternalPower); 271 } else { 272 // battery power 273 IOPMSetAggressiveness(gPowerManager, kPMPowerSource, kIOPMInternalPower); 274 } 275 } 276 break; 277 } 278 279 return; 280} 281 282__private_extern__ CFDictionaryRef 283PMSettings_CopyActivePMSettings(void) 284{ 285 CFDictionaryRef copy_all_settings; 286 CFDictionaryRef energySettings; 287 CFDictionaryRef return_val; 288 289 copy_all_settings = _copyPMSettings(kIOPMRemoveUnsupportedSettings); 290 if(!copy_all_settings) return NULL; 291 energySettings = isA_CFDictionary(CFDictionaryGetValue(copy_all_settings,currentPowerSource)); 292 if(energySettings) 293 return_val = CFDictionaryCreateCopy(kCFAllocatorDefault, energySettings); 294 else 295 return_val = NULL; 296 297 CFRelease(copy_all_settings); 298 return return_val; 299} 300 301 302/* _DWBT_enabled() returns true if the system supports DWBT and if user has opted in */ 303__private_extern__ bool _DWBT_enabled(void) 304{ 305 CFDictionaryRef current_settings; 306 CFNumberRef n; 307 int nint = 0; 308 309 310 if (!energySettings) 311 return false; 312 313 current_settings = (CFDictionaryRef)isA_CFDictionary( 314 CFDictionaryGetValue(energySettings, CFSTR(kIOPMACPowerKey))); 315 if (current_settings) { 316 n = CFDictionaryGetValue(current_settings, CFSTR(kIOPMDarkWakeBackgroundTaskKey)); 317 if (n) { 318 CFNumberGetValue(n, kCFNumberIntType, &nint); 319 } 320 return (0 != nint); 321 } 322 323 return false; 324} 325 326/* _DWBT_allowed() tells if a DWBT wake can be scheduled at this moment */ 327__private_extern__ bool 328_DWBT_allowed(void) 329{ 330#if TARGET_OS_EMBEDDED 331 return false; 332#else 333 return ( (GetPMSettingBool(CFSTR(kIOPMDarkWakeBackgroundTaskKey))) && 334 (kACPowered == _getPowerSource()) ); 335#endif 336 337} 338 339/* Is Sleep Services allowed */ 340__private_extern__ bool _SS_allowed(void) 341{ 342#if TARGET_OS_EMBEDDED 343 return false; 344#else 345 if (_DWBT_allowed()) 346 return true; 347 348 return ( (GetPMSettingBool(CFSTR(kIOPMDarkWakeBackgroundTaskKey))) && 349 (kBatteryPowered == _getPowerSource()) ); 350#endif 351 352} 353 354/* _copyPMSettings 355 * The returned dictionary represents the "currently selected" 356 * per-power source settings. 357 */ 358static CFDictionaryRef 359_copyPMSettings(bool removeUnsupported) 360{ 361 if(removeUnsupported) { 362 return IOPMCopyActivePMPreferences(); 363 } else { 364 return IOPMCopyUnabridgedActivePMPreferences(); 365 } 366} 367 368/**************************************************/ 369 370 /* activate_profiles 371 * 372 * A wrapper for ActivatePMSettings. We get a chance here to apply modifications 373 * to the Energy Saver settings before sending them to the kernel. 374 * Profiles (like LidClosed or ForceLowSpeed) have affects like accelerating idle 375 * times or forcing ReduceProcessorSpeed on. 376 */ 377static IOReturn 378activate_profiles(CFDictionaryRef d, CFStringRef s, bool removeUnsupported) 379{ 380 CFDictionaryRef energy_settings; 381 CFDictionaryRef activePMPrefs = NULL; 382 CFMutableDictionaryRef profiles_activated; 383 IOReturn ret; 384 CFNumberRef n1, n0; 385 CFNumberRef sleepSetting; 386 int one = 1; 387 int zero = 0; 388 389 if(NULL == d) { 390 return kIOReturnBadArgument; 391 } 392 393 if(NULL == s) { 394 s = CFSTR(kIOPMACPowerKey); 395 } 396 397 energy_settings = (CFDictionaryRef)isA_CFDictionary(CFDictionaryGetValue(d, s)); 398 if (!energy_settings) { 399 return kIOReturnError; 400 } 401 402 403 sleepSetting = (CFNumberRef)isA_CFNumber(CFDictionaryGetValue(energy_settings, CFSTR(kIOPMSystemSleepKey))); 404 if (sleepSetting) { 405 CFNumberGetValue(sleepSetting, kCFNumberLongType, &gSleepSetting); 406 } 407 408 if(g_overrides) 409 { 410 profiles_activated = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 411 CFDictionaryGetCount(energy_settings), energy_settings); 412 if(!profiles_activated) 413 return kIOReturnError; 414 415 n1 = CFNumberCreate(0, kCFNumberIntType, &one); 416 n0 = CFNumberCreate(0, kCFNumberIntType, &zero); 417 // If the "force low speed" profile is set, flip the ReduceSpeed bit on 418 if(g_overrides & kPMForceLowSpeedProfile) 419 { 420 if(n1) CFDictionarySetValue(profiles_activated, CFSTR(kIOPMReduceSpeedKey), n1); 421 } 422 423 if(g_overrides & kPMForceHighSpeed) 424 { 425 if(n0) CFDictionarySetValue(profiles_activated, CFSTR(kIOPMReduceSpeedKey), n0); 426 if(n0) CFDictionarySetValue(profiles_activated, CFSTR(kIOPMDynamicPowerStepKey), n0); 427 } 428 429 if(g_overrides & kPMPreventIdleSleep) 430 { 431 if(n0) CFDictionarySetValue(profiles_activated, CFSTR(kIOPMSystemSleepKey), n0); 432 } 433 434 if(g_overrides & kPMPreventDisplaySleep) 435 { 436 if(n0) CFDictionarySetValue(profiles_activated, CFSTR(kIOPMDisplaySleepKey), n0); 437 } 438 if (g_overrides & kPMPreventDiskSleep) 439 { 440 if (n0) CFDictionarySetValue(profiles_activated, CFSTR(kIOPMDiskSleepKey), n0); 441 } 442 443 444 if (n0) 445 CFRelease(n0); 446 if (n1) 447 CFRelease(n1); 448 449 ret = ActivatePMSettings(profiles_activated, removeUnsupported); 450 451 CFRelease(profiles_activated); 452 } else { 453 ret = ActivatePMSettings(energy_settings, removeUnsupported); 454 } 455 456 activePMPrefs = SCDynamicStoreCopyValue(_getSharedPMDynamicStore(), 457 CFSTR(kIOPMDynamicStoreSettingsKey)); 458 459 // If there isn't currently a value for kIOPMDynamicStoreSettingsKey, 460 // or the current value is different than the new value, 461 // Put the new settings in the SCDynamicStore for interested apps. 462 463 if( !isA_CFDictionary(activePMPrefs) || !CFEqual(activePMPrefs, energy_settings) ) 464 { 465 PMStoreSetValue(CFSTR(kIOPMDynamicStoreSettingsKey), energy_settings); 466 } 467 468 if (activePMPrefs) 469 CFRelease(activePMPrefs); 470 471 return ret; 472} 473 474 475__private_extern__ void PMSettings_prime(void) 476{ 477 CFTypeRef ps_blob; 478 479 // Open a connection to the Power Manager. 480 gPowerManager = IOPMFindPowerManagement(MACH_PORT_NULL); 481 if (gPowerManager == 0) return; 482 483 // Activate non-power source specific, PM settings 484 // namely disable sleep, where appropriate 485 IOPMActivateSystemPowerSettings(); 486 487 /* 488 * determine current power source for separate Battery/AC settings 489 */ 490 ps_blob = IOPSCopyPowerSourcesInfo(); 491 if(ps_blob) 492 { 493 currentPowerSource = IOPSGetProvidingPowerSourceType(ps_blob); 494 CFRelease(ps_blob); 495 } else currentPowerSource = CFSTR(kIOPMACPowerKey); 496 497 // load the initial configuration from the database 498 energySettings = _copyPMSettings(kIOPMRemoveUnsupportedSettings); 499 500 // send the initial configuration to the kernel 501 if(energySettings) { 502 activate_profiles( energySettings, 503 currentPowerSource, 504 kIOPMRemoveUnsupportedSettings); 505 } 506 507 // send initial power source info to drivers 508 if(CFEqual(currentPowerSource, CFSTR(kIOPMACPowerKey))) 509 IOPMSetAggressiveness(gPowerManager, kPMPowerSource, kIOPMExternalPower); 510 else IOPMSetAggressiveness(gPowerManager, kPMPowerSource, kIOPMInternalPower); 511} 512 513__private_extern__ void 514PMSettingsSupportedPrefsListHasChanged(void) 515{ 516 // The "supported prefs have changed" notification is generated 517 // by a kernel driver annnouncing a new supported feature, or unloading 518 // and removing support. Let's re-evaluate our known settings. 519 520 PMSettingsPrefsHaveChanged(); 521} 522 523 524/* ESPrefsHaveChanged 525 * 526 * Is the handler that configd calls when someone "applies" new Energy Saver 527 * Preferences. Since the preferences have probably changed, we re-read them 528 * from disk and transmit the new settings to the kernel. 529 */ 530__private_extern__ void 531PMSettingsPrefsHaveChanged(void) 532{ 533 // re-blast system-wide settings 534 IOPMActivateSystemPowerSettings(); 535 536 // re-read preferences into memory 537 if(energySettings) CFRelease(energySettings); 538 539 energySettings = _copyPMSettings(kIOPMRemoveUnsupportedSettings); 540 541 // push new preferences out to the kernel 542 if(isA_CFDictionary(energySettings)) { 543 activate_profiles(energySettings, 544 currentPowerSource, 545 kIOPMRemoveUnsupportedSettings); 546 } else { 547 if (energySettings) { 548 CFRelease(energySettings); 549 } 550 energySettings = NULL; 551 } 552 PMAssertions_SettingsHaveChanged(); 553 554 return; 555} 556 557 558/* PMSettingsPSChange 559 * 560 * A power source has changed. Has the current power provider changed? 561 * If so, get new settings down to the kernel. 562 */ 563__private_extern__ void PMSettingsPSChange(CFTypeRef ps_blob) 564{ 565 CFStringRef newPowerSource; 566 567 newPowerSource = IOPSGetProvidingPowerSourceType(ps_blob); 568 569 if(!CFEqual(currentPowerSource, newPowerSource)) 570 { 571 currentPowerSource = newPowerSource; 572 573 // Are we in the middle of a sleep? 574 if(!_pmcfgd_impendingSleep) 575 { 576 // If not, tell drivers that the power source changed 577 if(CFEqual(CFSTR(kIOPMACPowerKey), currentPowerSource)) 578 { 579 // Running off of external power 580 IOPMSetAggressiveness(gPowerManager, kPMPowerSource, kIOPMExternalPower); 581 } else { 582 // This is either battery power or UPS power, "internal power" 583 IOPMSetAggressiveness(gPowerManager, kPMPowerSource, kIOPMInternalPower); 584 } 585 } else { 586 // If we WERE in the middle of a sleep, delay notification until we're awake. 587 deferredPSChangeNotify = 1; 588 } 589 590 if(energySettings) { 591 activate_profiles( energySettings, 592 currentPowerSource, 593 kIOPMRemoveUnsupportedSettings); 594 } 595 } 596 597} 598 599/* activateForcedSettings 600 * 601 */ 602__private_extern__ IOReturn 603_activateForcedSettings(CFDictionaryRef forceSettings) 604{ 605 // Calls to "pmset force" end up here 606 return activate_profiles( forceSettings, 607 currentPowerSource, 608 kIOPMRemoveUnsupportedSettings); 609} 610 611 612