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