1/* 2 * Copyright (c) 2012 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) 2012 Apple Computer, Inc. All rights reserved. 25 * 26 */ 27#include <syslog.h> 28#include <unistd.h> 29#include <stdlib.h> 30#include <notify.h> 31#include <mach/mach.h> 32#include <mach/mach_port.h> 33#include <servers/bootstrap.h> 34#include <asl.h> 35#include <bsm/libbsm.h> 36#include <sys/time.h> 37 38#include "powermanagementServer.h" // mig generated 39#include "BatteryTimeRemaining.h" 40#include "PMSettings.h" 41#include "UPSLowPower.h" 42#include "PMAssertions.h" 43#include "PrivateLib.h" 44#include "PMStore.h" 45 46 47/**** PMBattery configd plugin 48 We clean up, massage, and re-package the data from the batteries and publish 49 it in the more palatable form described in IOKit/Headers/IOPowerSource.h 50 51 All kernel batteries conform to the IOPMPowerSource base class. 52 53 We provide the following information in a CFDictionary and publish it for 54 all user processes to see: 55 Name 56 CurrentCapacity 57 MaxCapacity 58 Remaining Time To Empty 59 Remaining Time To Full Charge 60 IsCharging 61 IsPresent 62 Type 63****/ 64 65 66/* PSStruct 67 * Contains all the details about each power source that the system describes. 68 * This struct is the backbone of the IOPowerSources() IOKit API for 69 * power source reporting. 70 */ 71typedef struct { 72 // powerd will assign a unique psid to all sources. 73 long psid; 74 75 // Ensure that only the process that created 76 // a ps may modify it or destroy it, by recording caller's pid. 77 int pid; 78 dispatch_source_t procdeathsrc; 79 80 // This is the most current recorded state of this power source. 81 CFDictionaryRef description; 82 83 // log of previous battery updates, maintained as ring buffer 84 CFMutableArrayRef log; 85 CFIndex logIdx; // Index for next record 86 uint64_t logUpdate_ts; // Timestamp of last log 87} PSStruct; 88 89#define kBattLogMaxEntries 64 90#define kBattLogUpdateFreq (5*60) // 5 mins 91 92#define kPSMaxCount 7 93 94static PSStruct gPSList[kPSMaxCount]; 95 96// kBattNotCharging checks for (int16_t)-1 invalid current readings 97#define kBattNotCharging 0xffff 98 99#define kSlewStepMin 2 100#define kSlewStepMax 10 101#define kDiscontinuitySettle 60 102typedef struct { 103 int showingTime; 104 bool settled; 105} SlewStruct; 106SlewStruct *slew = NULL; 107 108 109// Battery health calculation constants 110#define kSmartBattReserve_mAh 200.0 111#define kMaxBattMinutes 1200 112 113 114// static global variables for tracking battery state 115typedef struct { 116 CFAbsoluteTime lastDiscontinuity; 117 int systemWarningLevel; 118 bool warningsShouldResetForSleep; 119 bool readACAdapterAgain; 120 bool selectionHasSwitched; 121 int psTimeRemainingNotifyToken; 122 int psPercentChangeNotifyToken; 123 bool noPoll; 124 bool needsNotifyAC; 125 PSStruct *internal; 126} BatteryControl; 127static BatteryControl control; 128 129 130// forward declarations 131static PSStruct *iops_newps(int pid, int psid); 132static void _initializeBatteryCalculations(void); 133static void checkTimeRemainingValid(IOPMBattery **batts); 134static CFDictionaryRef packageKernelPowerSource(IOPMBattery *b); 135 136static void _discontinuityOccurred(void); 137static IOReturn _readAndPublishACAdapter(bool, CFDictionaryRef); 138static void publish_IOPSBatteryGetWarningLevel(IOPMBattery *b, 139 int combinedTime); 140static bool publish_IOPSGetTimeRemainingEstimate(int timeRemaining, 141 bool external, 142 bool timeRemainingUnknown, 143 bool isCharging, 144 bool noPoll); 145static void publish_IOPSGetPercentRemaining(int percent, 146 bool external, 147 bool isCharging, 148 bool fullyCharged, 149 IOPMBattery *b); 150 151static void HandlePublishAllPowerSources(void); 152 153 154 155// Arguments For startBatteryPoll() 156typedef enum { 157 kPeriodicPoll = 0, 158 kImmediateFullPoll = 1 159} PollCommand; 160static bool startBatteryPoll(PollCommand x); 161 162 163__private_extern__ void 164BatteryTimeRemaining_prime(void) 165{ 166 167 bzero(gPSList, sizeof(gPSList)); 168 bzero(&control, sizeof(BatteryControl)); 169 170 notify_register_check(kIOPSTimeRemainingNotificationKey, 171 &control.psTimeRemainingNotifyToken); 172 notify_register_check(kIOPSNotifyPercentChange, 173 &control.psPercentChangeNotifyToken); 174 175 // Initialize tracing battery events to FDR 176 recordFDREvent(kFDRInit, false, NULL); 177 178#if !TARGET_OS_EMBEDDED 179#endif 180 _initializeBatteryCalculations(); 181 182 /* 183 *Initiate the next battery poll; or start a timer to poll 184 * when the 60sec user visible polling timer expres. 185 */ 186 startBatteryPoll(kPeriodicPoll); 187 return; 188} 189 190__private_extern__ void 191BatteryTimeRemainingSleepWakeNotification(natural_t messageType) 192{ 193 if (kIOMessageSystemWillPowerOn == messageType) 194 { 195 control.warningsShouldResetForSleep = true; 196 control.readACAdapterAgain = true; 197 198 _discontinuityOccurred(); 199 } 200} 201 202/* 203 * When we wake from sleep, we call this function to make note of the 204 * battery time remaining discontinuity after the RTC resyncs with the CPU. 205 */ 206__private_extern__ void 207BatteryTimeRemainingRTCDidResync(void) 208{ 209 _discontinuityOccurred(); 210} 211 212/* 213 * A battery time remaining discontinuity has occurred 214 * Make sure we don't publish a time remaining estimate at all 215 * until a given period has elapsed. 216 */ 217static void _discontinuityOccurred(void) 218{ 219 if (slew) { 220 bzero(slew, sizeof(SlewStruct)); 221 } 222 control.lastDiscontinuity = CFAbsoluteTimeGetCurrent(); 223 224 // Kick off a battery poll now, 225 // and schedule the next poll in exactly 60 seconds. 226 startBatteryPoll(kImmediateFullPoll); 227} 228 229static void _initializeBatteryCalculations(void) 230{ 231 if (_batteryCount() == 0) { 232 return; 233 } 234 235 // Does this Mac have an internal battery 236 // reported through IOPMPowerSource? 237 // If so, we'll track it in the gPSList. 238 239 // Any other processes that publish power sources (like upsd) 240 // will get a powersource id > 5000 241 const int kSpecialInternalBatteryID = 99; 242 control.internal = iops_newps(getpid(), kSpecialInternalBatteryID); 243 244 control.lastDiscontinuity = CFAbsoluteTimeGetCurrent(); 245 246 // make initial call to populate array and publish state 247 kernelPowerSourcesDidChange(kInternalBattery); 248 249 return; 250} 251 252#if !TARGET_OS_EMBEDDED 253static CFAbsoluteTime getASBMPropertyCFAbsoluteTime(CFStringRef key) 254{ 255 CFNumberRef secSince1970 = NULL; 256 IOPMBattery **b = _batteries(); 257 uint32_t secs = 0; 258 CFAbsoluteTime return_val = 0.0; 259/* 260 if (b && b[0] && b[0]->me) 261 { 262 secSince1970 = IORegistryEntryCreateCFProperty(b[0]->me, key, 0, 0); 263 if (secSince1970) { 264*/ 265 if (b && b[0] && b[0]->properties) 266 { 267 secSince1970 = CFDictionaryGetValue(b[0]->properties, key); 268 if (secSince1970) { 269 CFNumberGetValue(secSince1970, kCFNumberIntType, &secs); 270// CFRelease(secSince1970); 271 return_val = (CFAbsoluteTime)secs - kCFAbsoluteTimeIntervalSince1970; 272 } 273 } 274 275 return return_val; 276} 277 278static CFTimeInterval mostRecent(CFTimeInterval a, CFTimeInterval b, CFTimeInterval c) 279{ 280 if ((a >= b) && (a >= c) && a!= 0.0) { 281 return a; 282 } else if ((b >= a) && (b>= c) && b!= 0.0) { 283 return b; 284 } else return c; 285} 286 287static dispatch_source_t batteryPollingTimer = NULL; 288#endif 289 290static void updateLogBuffer(PSStruct *ps, bool asyncEvent) 291{ 292 uint64_t curTime = getMonotonicTime(); 293 CFTypeRef n; 294 CFDateRef date = NULL; 295 CFTimeZoneRef tz = NULL; 296 CFTimeInterval diff = 0; 297 CFAbsoluteTime absTime; 298 299 CFMutableDictionaryRef entry = NULL; 300 301 if ((ps == NULL) || (isA_CFDictionary(ps->description) == NULL)) return; 302 303 if ((!asyncEvent) && (curTime - ps->logUpdate_ts < kBattLogUpdateFreq)) 304 return; 305 306 if (ps->log == NULL) { 307 ps->log = CFArrayCreateMutable(NULL, kBattLogMaxEntries, &kCFTypeArrayCallBacks); 308 309 if (ps->log == NULL) return; 310 } 311 312 entry = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, 313 &kCFTypeDictionaryValueCallBacks); 314 if (!entry) return; 315 316 // Current time of this activity 317 tz = CFTimeZoneCopySystem(); 318 if (tz == NULL) { 319 goto exit; 320 } 321 absTime = CFAbsoluteTimeGetCurrent(); 322 date = CFDateCreate(0, absTime); 323 if (date == NULL) { 324 goto exit; 325 } 326 CFDictionarySetValue(entry, CFSTR(kIOPSBattLogEntryTime), date); 327 328 diff = CFTimeZoneGetSecondsFromGMT(tz, absTime); 329 n = CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &diff); 330 if (n) { 331 CFDictionarySetValue(entry, CFSTR(kIOPSBattLogEntryTZ), n); 332 CFRelease(n); 333 } 334 335 n = CFDictionaryGetValue(ps->description, CFSTR(kIOPSCurrentCapacityKey)); 336 if (n) CFDictionarySetValue(entry, CFSTR(kIOPSCurrentCapacityKey), n); 337 338 n = CFDictionaryGetValue(ps->description, CFSTR(kIOPSMaxCapacityKey)); 339 if (n) CFDictionarySetValue(entry, CFSTR(kIOPSMaxCapacityKey), n); 340 341 n = CFDictionaryGetValue(ps->description, CFSTR(kIOPSPowerSourceStateKey)); 342 if (n) CFDictionarySetValue(entry, CFSTR(kIOPSPowerSourceStateKey), n); 343 344 n = CFDictionaryGetValue(ps->description, CFSTR(kIOPSIsChargingKey)); 345 if (n) CFDictionarySetValue(entry, CFSTR(kIOPSIsChargingKey), n); 346 347 n = CFDictionaryGetValue(ps->description, CFSTR(kIOPSCurrentKey)); 348 if (n) CFDictionarySetValue(entry, CFSTR(kIOPSCurrentKey), n); 349 350 n = CFDictionaryGetValue(ps->description, CFSTR(kIOPSIsChargedKey)); 351 if (n) 352 CFDictionarySetValue(entry, CFSTR(kIOPSIsChargedKey), n); 353 else 354 CFDictionarySetValue(entry, CFSTR(kIOPSIsChargedKey), kCFBooleanFalse); 355 356 CFArraySetValueAtIndex(ps->log, ps->logIdx , entry); 357 ps->logIdx = (++ps->logIdx) % kBattLogMaxEntries; 358 359 360 ps->logUpdate_ts = curTime; 361 362exit: 363 if (entry) CFRelease(entry); 364 if (tz) CFRelease(tz); 365 if (date) CFRelease(date); 366} 367 368#ifndef kBootPathKey 369#define kBootPathKey "BootPathUpdated" 370#define kFullPathKey "FullPathUpdated" 371#define kUserVisPathKey "UserVisiblePathUpdated" 372#endif 373 374static bool startBatteryPoll(PollCommand doCommand) 375{ 376#if !TARGET_OS_EMBEDDED 377 const static CFTimeInterval kUserVisibleMinFrequency = 55.0; 378 const static CFTimeInterval kFullMinFrequency = 595.0; 379 const static uint64_t kPollIntervalNS = 60ULL * NSEC_PER_SEC; 380 381 CFAbsoluteTime lastBootUpdate = 0.0; 382 CFAbsoluteTime lastUserVisibleUpdate = 0.0; 383 CFAbsoluteTime lastFullUpdate = 0.0; 384 CFAbsoluteTime now = CFAbsoluteTimeGetCurrent(); 385 CFAbsoluteTime lastUpdateTime; 386 CFTimeInterval sinceUserVisible = 0.0; 387 CFTimeInterval sinceFull = 0.0; 388 bool doUserVisible = false; 389 bool doFull = false; 390 391 if (!_batteries()) 392 return false; 393 394 if (control.noPoll) 395 { 396 asl_log(0, 0, ASL_LEVEL_ERR, "Battery polling is disabled. powerd is skipping this battery udpate request."); 397 return false; 398 } 399 400 if (kImmediateFullPoll == doCommand) { 401 doFull = true; 402 } else { 403 404 lastUpdateTime = getASBMPropertyCFAbsoluteTime(CFSTR(kBootPathKey)); 405 if (lastUpdateTime < now) lastBootUpdate = lastUpdateTime; 406 lastUpdateTime = getASBMPropertyCFAbsoluteTime(CFSTR(kFullPathKey)); 407 if (lastUpdateTime < now) lastFullUpdate = lastUpdateTime; 408 lastUpdateTime = getASBMPropertyCFAbsoluteTime(CFSTR(kUserVisPathKey)); 409 if (lastUpdateTime < now) lastUserVisibleUpdate = lastUpdateTime; 410 411 sinceUserVisible = now - mostRecent(lastBootUpdate, lastFullUpdate, lastUserVisibleUpdate); 412 if (sinceUserVisible > kUserVisibleMinFrequency) { 413 doUserVisible = true; 414 } 415 416 sinceFull = now - mostRecent(lastBootUpdate, lastFullUpdate, 0); 417 if (sinceFull > kFullMinFrequency) { 418 doFull = true; 419 } 420 } 421 422 if (doFull) { 423 IOPSRequestBatteryUpdate(kIOPSReadAll); 424 } else if (doUserVisible) { 425 IOPSRequestBatteryUpdate(kIOPSReadUserVisible); 426 } else { 427 // We'll wait until kPollIntervalNS has elapsed since the last user visible poll. 428 uint64_t checkAgainNS = kPollIntervalNS - (sinceUserVisible*NSEC_PER_SEC); 429 430 if (!batteryPollingTimer) { 431 batteryPollingTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue()); 432 dispatch_source_set_event_handler(batteryPollingTimer, ^() { startBatteryPoll(kPeriodicPoll); }); 433 dispatch_resume(batteryPollingTimer); 434 } 435 dispatch_source_set_timer(batteryPollingTimer, dispatch_time(DISPATCH_TIME_NOW, checkAgainNS), DISPATCH_TIME_FOREVER, 0); 436 } 437#endif 438 return true; 439} 440 441__private_extern__ void BatterySetNoPoll(bool noPoll) 442{ 443 444 if (control.noPoll != noPoll) 445 { 446 control.noPoll = noPoll; 447 if (!noPoll) { 448 startBatteryPoll(kImmediateFullPoll); 449 } else { 450 // Announce the cessation of polling to the world 451 // (by publishing 55% 5:55 to BatteryMonitor) 452 kernelPowerSourcesDidChange(kInternalBattery); 453 } 454 455 asl_log(0, 0, ASL_LEVEL_ERR, "Battery polling is now %s\n", noPoll ? "disabled." : "enabled. Initiating a battery poll."); 456 } 457} 458 459 460#define kTimeThresholdEarly 20 461#define kTimeThresholdFinal 10 462 463static void publish_IOPSBatteryGetWarningLevel( 464 IOPMBattery *b, 465 int combinedTime) 466{ 467 /* Display a system low battery warning? 468 * 469 * No Warning == AC Power or >= 20 minutes battery remaining 470 * Early Warning == On Battery with < 20 minutes 471 * Final Warning == On Battery with < 10 Minutes 472 * 473 */ 474 475 static CFStringRef lowBatteryKey = NULL; 476 static int prevLoggedLevel = kIOPSLowBatteryWarningNone; 477 int newWarningLevel = kIOPSLowBatteryWarningNone; 478 479 if (control.warningsShouldResetForSleep) 480 { 481 // We reset the warning level upon system sleep. 482 control.warningsShouldResetForSleep = false; 483 control.systemWarningLevel = 0; 484 newWarningLevel = kIOPSLowBatteryWarningNone; 485 486 } else if (b->externalConnected) 487 { 488 // We reset the warning level whenever AC is attached. 489 control.systemWarningLevel = 0; 490 newWarningLevel = kIOPSLowBatteryWarningNone; 491 492 } else if (combinedTime > 0) 493 { 494 if (combinedTime < kTimeThresholdFinal) 495 { 496 newWarningLevel = kIOPSLowBatteryWarningFinal; 497 } else if (combinedTime < kTimeThresholdEarly) 498 { 499 newWarningLevel = kIOPSLowBatteryWarningEarly; 500 } 501 } 502 503 if (newWarningLevel < control.systemWarningLevel) { 504 // kIOPSLowBatteryWarningNone = 1, 505 // kIOPSLowBatteryWarningEarly = 2, 506 // kIOPSLowBatteryWarningFinal = 3 507 // 508 // Warning level may only increase. 509 // Once we enter a >1 warning level, we can only reset it by 510 // (1) having AC power re-applied, or (2) hibernating 511 // and waking with a new battery. 512 // 513 // This prevents fluctuations in battery capacity from causing 514 // multiple battery warnings. 515 516 newWarningLevel = control.systemWarningLevel; 517 } 518 519 if ( (newWarningLevel != control.systemWarningLevel) 520 && (0 != newWarningLevel) ) 521 { 522 CFNumberRef newlevel = CFNumberCreate(0, kCFNumberIntType, &newWarningLevel); 523 524 if (newlevel) 525 { 526 if (!lowBatteryKey) { 527 lowBatteryKey = SCDynamicStoreKeyCreate( 528 kCFAllocatorDefault, CFSTR("%@%@"), 529 kSCDynamicStoreDomainState, CFSTR(kIOPSDynamicStoreLowBattPathKey)); 530 } 531 532 PMStoreSetValue(lowBatteryKey, newlevel ); 533 CFRelease(newlevel); 534 535 notify_post(kIOPSNotifyLowBattery); 536 if ((newWarningLevel != prevLoggedLevel) && (newWarningLevel != kIOPSLowBatteryWarningNone)) { 537 logASLLowBatteryWarning(newWarningLevel, combinedTime, b->currentCap); 538 prevLoggedLevel = newWarningLevel; 539 } 540 } 541 542 control.systemWarningLevel = newWarningLevel; 543 } 544 545 return; 546} 547 548static bool publish_IOPSGetTimeRemainingEstimate( 549 int timeRemaining, 550 bool external, 551 bool timeRemainingUnknown, 552 bool isCharging, 553 bool noPoll) 554{ 555 uint64_t powerSourcesBitsForNotify = (uint64_t)(timeRemaining & 0xFFFF); 556 static uint64_t lastPSBitsNotify = 0; 557 bool posted = false; 558 559 // Presence of bit kPSTimeRemainingNotifyValidBit means IOPSGetTimeRemainingEstimate 560 // should trust this as a valid chunk of battery data. 561 powerSourcesBitsForNotify |= kPSTimeRemainingNotifyValidBit; 562 563 if (external) { 564 powerSourcesBitsForNotify |= kPSTimeRemainingNotifyExternalBit; 565 } 566 if (timeRemainingUnknown) { 567 powerSourcesBitsForNotify |= kPSTimeRemainingNotifyUnknownBit; 568 } 569 if (isCharging) { 570 powerSourcesBitsForNotify |= kPSTimeRemainingNotifyChargingBit; 571 } 572 if (control.noPoll) { 573 powerSourcesBitsForNotify |= kPSTimeRemainingNotifyNoPollBit; 574 } 575 576 /* These bits feed the SPI IOKit:IOPSGetSupportedPowerSources() 577 * - battery supported, UPS supported, active power sourecs 578 */ 579 if (getActiveBatteryDictionary()) { 580 powerSourcesBitsForNotify |= kPSTimeRemainingNotifyBattSupportBit; 581 } 582 if (getActiveUPSDictionary()) { 583 powerSourcesBitsForNotify |= kPSTimeRemainingNotifyUPSSupportBit; 584 } 585 uint64_t activePS = getActivePSType(); 586 powerSourcesBitsForNotify |= 587 (activePS & 0xFF) << kPSTimeRemainingNotifyActivePS8BitsStarts; 588 589 590 591 if (lastPSBitsNotify != powerSourcesBitsForNotify) 592 { 593 lastPSBitsNotify = powerSourcesBitsForNotify; 594 notify_set_state(control.psTimeRemainingNotifyToken, powerSourcesBitsForNotify); 595 notify_post(kIOPSNotifyTimeRemaining); 596 posted = true; 597 } 598 599 return posted; 600} 601 602static void publish_IOPSGetPercentRemaining( 603 int percentRemaining, 604 bool isExternal, 605 bool isCharging, 606 bool fullyCharged, 607 IOPMBattery *b) 608{ 609 uint64_t currentStateBits, changedStateBits; 610 static uint64_t lastStateBits = 0; 611 uint64_t ignoreBits; 612 613 // Presence of bit kPSTimeRemainingNotifyValidBit means IOPSGetPercentRemaining 614 // should trust this as a valid chunk of battery data. 615 currentStateBits = kPSTimeRemainingNotifyValidBit; 616 617 if ((percentRemaining >= 0) && (percentRemaining <= 100)) 618 currentStateBits |= percentRemaining; 619 if (isExternal) 620 currentStateBits |= kPSTimeRemainingNotifyExternalBit; 621 if (isCharging) 622 currentStateBits |= kPSTimeRemainingNotifyChargingBit; 623 if (fullyCharged) 624 currentStateBits |= kPSTimeRemainingNotifyFullyChargedBit; 625#if TARGET_OS_EMBEDDED 626 if (b && b->isCritical) 627 currentStateBits |= kPSCriticalLevelBit; 628 if (b && b->isRestricted) 629 currentStateBits |= kPSRestrictedLevelBit; 630#endif 631 632 changedStateBits = lastStateBits ^ currentStateBits; 633 if (changedStateBits) 634 { 635 lastStateBits = currentStateBits; 636 notify_set_state(control.psPercentChangeNotifyToken, currentStateBits); 637 638 // Suppress notification for charging state changes 639 ignoreBits = (kPSTimeRemainingNotifyChargingBit 640 |kPSTimeRemainingNotifyFullyChargedBit 641#if TARGET_OS_EMBEDDED 642 |kPSCriticalLevelBit 643 |kPSRestrictedLevelBit 644#endif 645 ); 646 if (changedStateBits & ~ignoreBits) 647 { 648 notify_post(kIOPSNotifyPercentChange); 649 } 650#if TARGET_OS_EMBEDDED 651 if (changedStateBits & kPSCriticalLevelBit) 652 notify_post(kIOPSNotifyCriticalLevel); 653 if (changedStateBits & kPSRestrictedLevelBit) 654 notify_post(kIOPSNotifyRestrictedMode); 655#endif 656 if ((changedStateBits & kPSTimeRemainingNotifyExternalBit) && control.internal) 657 updateLogBuffer(control.internal, true); 658 } 659} 660 661 662__private_extern__ void 663kernelPowerSourcesDidChange(IOPMBattery *b) 664{ 665 static int _lastExternalConnected = -1; 666 int _nowExternalConnected = 0; 667 int percentRemaining = 0; 668 IOPMBattery **_batts = _batteries(); 669 670 /* 671 * Initiate the next battery poll; or start a timer to poll 672 * when the 60sec user visible polling timer expres. 673 */ 674 startBatteryPoll(kPeriodicPoll); 675 676 if (0 == _batteryCount()) { 677 return; 678 } 679 680 if (!b) { 681 b = _batts[0]; 682 } 683 684 _nowExternalConnected = (b->externalConnected ? 1 : 0); 685 if (_lastExternalConnected != _nowExternalConnected) { 686 // If AC has changed, we must invalidate time remaining. 687 _discontinuityOccurred(); 688 control.needsNotifyAC = true; 689 690 _lastExternalConnected = _nowExternalConnected; 691 } 692 _readAndPublishACAdapter(b->externalConnected, 693 CFDictionaryGetValue(b->properties, CFSTR(kIOPMPSAdapterDetailsKey))); 694 695 696 697 checkTimeRemainingValid(_batts); 698 699 if (b->maxCap) { 700 double percent = (double)(b->currentCap * 100) / (double)b->maxCap; 701 percentRemaining = (int) lround(percent); 702 if (percentRemaining > 100) 703 percentRemaining = 100; 704 } 705 // b->swCalculatedPR is used by packageKernelPowerSource() 706 b->swCalculatedPR = percentRemaining; 707 708 /************************************************************************ 709 * 710 * PUBLISH: SCDynamicStoreSetValue / IOPSCopyPowerSourcesInfo() 711 * 712 ************************************************************************/ 713 if (control.internal) { 714 if (control.internal->description) { 715 CFRelease(control.internal->description); 716 } 717 control.internal->description = packageKernelPowerSource(b); 718 updateLogBuffer(control.internal, false); 719 } 720 721 HandlePublishAllPowerSources(); 722} 723 724static void HandlePublishAllPowerSources(void) 725{ 726 IOPMBattery **batteries = _batteries(); 727 IOPMBattery *b = NULL; 728 int combinedTime = 0; 729 int percentRemaining = 0; 730 static int prev_percentRemaining = 0; 731 bool tr_posted; 732 bool ups_externalConnected = false; 733 bool externalConnected, tr_unknown, is_charging, fully_charged; 734 CFDictionaryRef ups = NULL; 735 int ups_tr = -1; 736 737 if ((0 == _batteryCount()) && ((ups = getActiveUPSDictionary()) == NULL) ) { 738 return; 739 } 740 741 if (_batteryCount()) 742 b = batteries[0]; 743 744 externalConnected = tr_unknown = is_charging = fully_charged = false; 745 for(int i=0; i<_batteryCount(); i++) 746 { 747 if (batteries[i]->isPresent) { 748 combinedTime += batteries[i]->swCalculatedTR; 749 750 } 751 752 } 753 754 if (ups) { 755 CFNumberRef num_cf = CFDictionaryGetValue(ups, CFSTR(kIOPSTimeToEmptyKey)); 756 if (num_cf) { 757 CFNumberGetValue(num_cf, kCFNumberIntType, &ups_tr); 758 if (ups_tr != -1) combinedTime += ups_tr; 759 } 760 761 CFStringRef src = CFDictionaryGetValue(ups, CFSTR(kIOPSPowerSourceStateKey)); 762 if (src && (CFStringCompare(src, CFSTR(kIOPSACPowerValue), kNilOptions) == kCFCompareEqualTo)) 763 ups_externalConnected = true; 764 } 765 766 if (b) { 767 tr_unknown = b->isTimeRemainingUnknown; 768 is_charging = b->isCharging; 769 percentRemaining = b->swCalculatedPR;; 770 fully_charged = isFullyCharged(b); 771 772 if (ups) 773 externalConnected = b->externalConnected && ups_externalConnected; 774 else 775 externalConnected = b->externalConnected; 776 } 777 else { 778 int mcap = 0, ccap = 0; 779 CFNumberRef mcap_cf = NULL, ccap_cf = NULL; 780 781 /* ups must be non-NULL */ 782 externalConnected = ups_externalConnected; 783 784 if (!externalConnected && (ups_tr == -1)) { 785 tr_unknown = false; 786 } 787 else { 788 tr_unknown = true; 789 } 790 791 if (CFDictionaryGetValue(ups, CFSTR(kIOPSIsChargingKey)) == kCFBooleanTrue) 792 is_charging = true; 793 794 ccap_cf = CFDictionaryGetValue(ups, CFSTR(kIOPSCurrentCapacityKey)); 795 if (ccap_cf) 796 CFNumberGetValue(ccap_cf, kCFNumberIntType, &ccap); 797 798 mcap_cf = CFDictionaryGetValue(ups, CFSTR(kIOPSMaxCapacityKey)); 799 if (mcap_cf) 800 CFNumberGetValue(mcap_cf, kCFNumberIntType, &mcap); 801 802 if (ccap && mcap) 803 percentRemaining = (ccap*100)/mcap; 804 805 if ((percentRemaining >= 95) && externalConnected && (!is_charging)) 806 fully_charged = true; 807 } 808 809 tr_posted = publish_IOPSGetTimeRemainingEstimate(combinedTime, 810 externalConnected, 811 tr_unknown, 812 is_charging, 813 control.noPoll); 814 815 if (b) { 816 publish_IOPSBatteryGetWarningLevel(b, combinedTime); 817 } 818 819 publish_IOPSGetPercentRemaining(percentRemaining, 820 externalConnected, 821 is_charging, 822 fully_charged, 823 b); 824 825 if ((percentRemaining != prev_percentRemaining) && !tr_posted) { 826 notify_post(kIOPSNotifyTimeRemaining); 827 } 828 829 prev_percentRemaining = percentRemaining; 830 831 /************************************************************************ 832 * 833 * TELL: powerd-internal code that responds to power changes 834 ************************************************************************/ 835 836 837 #if !TARGET_OS_EMBEDDED 838 // Notifiy PSLowPower of power sources change 839 UPSLowPowerPSChange(); 840 PMSettingsPSChange(); 841 #endif 842 843 844 /************************************************************************ 845 * 846 * NOTIFY: Providing power source changed. 847 * via notify(3) 848 ************************************************************************/ 849 if (control.needsNotifyAC) { 850 control.needsNotifyAC = false; 851 852 recordFDREvent(kFDRACChanged, false, batteries); 853 854 notify_post(kIOPSNotifyPowerSource); 855 } 856 857 858 notify_post(kIOPSNotifyAnyPowerSource); 859 860 /************************************************************************ 861 * 862 * PUBLISH: Flight Data Recorder trace 863 * 864 ************************************************************************/ 865 recordFDREvent(kFDRBattEventPeriodic, false, batteries); 866 867 return; 868} 869 870 871 872 873 874 875/* checkTimeRemainingValid 876 * Implicit inputs: battery state; battery's own time remaining estimate 877 * Implicit output: estimated time remaining placed in b->swCalculatedTR; or -1 if indeterminate 878 * returns 1 if we reached a valid estimate 879 * returns 0 if we're still calculating 880 */ 881static void checkTimeRemainingValid(IOPMBattery **batts) 882{ 883 884 int i; 885 IOPMBattery *b; 886 int batCount = _batteryCount(); 887 888 for(i=0; i<batCount; i++) 889 { 890 b = batts[i]; 891 // Did our calculation come out negative? 892 // The average current must still be out of whack! 893 if ((b->swCalculatedTR < 0) || (false == b->isPresent)) { 894 b->swCalculatedTR = -1; 895 } 896 897 // Cap all times remaining to 10 hours. We don't ship any 898 // 44 hour batteries just yet. 899 if (kMaxBattMinutes < b->swCalculatedTR) { 900 b->swCalculatedTR = kMaxBattMinutes; 901 } 902 } 903 904 if (-1 == batts[0]->swCalculatedTR) { 905 batts[0]->isTimeRemainingUnknown = true; 906 } else { 907 batts[0]->isTimeRemainingUnknown = false; 908 } 909 910} 911 912// Set health & confidence 913void _setBatteryHealthConfidence( 914 CFMutableDictionaryRef outDict, 915 IOPMBattery *b) 916{ 917 CFMutableArrayRef permanentFailures = NULL; 918 919 // no battery present? no health & confidence then! 920 // If we return without setting the health and confidence values in 921 // outDict, that is OK, it just means they were indeterminate. 922 if(!outDict || !b || !b->isPresent) 923 return; 924 925 /** Report any failure status from the PFStatus register **/ 926 /***********************************************************************************/ 927 /***********************************************************************************/ 928 if ( 0!= b->pfStatus) { 929 permanentFailures = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); 930 if (!permanentFailures) 931 return; 932 if (kSmartBattPFExternalInput & b->pfStatus) { 933 CFArrayAppendValue( permanentFailures, CFSTR(kIOPSFailureExternalInput) ); 934 } 935 if (kSmartBattPFSafetyOverVoltage & b->pfStatus) { 936 CFArrayAppendValue( permanentFailures, CFSTR(kIOPSFailureSafetyOverVoltage) ); 937 } 938 if (kSmartBattPFChargeSafeOverTemp & b->pfStatus) { 939 CFArrayAppendValue( permanentFailures, CFSTR(kIOPSFailureChargeOverTemp) ); 940 } 941 if (kSmartBattPFDischargeSafeOverTemp & b->pfStatus) { 942 CFArrayAppendValue( permanentFailures, CFSTR(kIOPSFailureDischargeOverTemp) ); 943 } 944 if (kSmartBattPFCellImbalance & b->pfStatus) { 945 CFArrayAppendValue( permanentFailures, CFSTR(kIOPSFailureCellImbalance) ); 946 } 947 if (kSmartBattPFChargeFETFailure & b->pfStatus) { 948 CFArrayAppendValue( permanentFailures, CFSTR(kIOPSFailureChargeFET) ); 949 } 950 if (kSmartBattPFDischargeFETFailure & b->pfStatus) { 951 CFArrayAppendValue( permanentFailures, CFSTR(kIOPSFailureDischargeFET) ); 952 } 953 if (kSmartBattPFDataFlushFault & b->pfStatus) { 954 CFArrayAppendValue( permanentFailures, CFSTR(kIOPSFailureDataFlushFault) ); 955 } 956 if (kSmartBattPFPermanentAFECommFailure & b->pfStatus) { 957 CFArrayAppendValue( permanentFailures, CFSTR(kIOPSFailurePermanentAFEComms) ); 958 } 959 if (kSmartBattPFPeriodicAFECommFailure & b->pfStatus) { 960 CFArrayAppendValue( permanentFailures, CFSTR(kIOPSFailurePeriodicAFEComms) ); 961 } 962 if (kSmartBattPFChargeSafetyOverCurrent & b->pfStatus) { 963 CFArrayAppendValue( permanentFailures, CFSTR(kIOPSFailureChargeOverCurrent) ); 964 } 965 if (kSmartBattPFDischargeSafetyOverCurrent & b->pfStatus) { 966 CFArrayAppendValue( permanentFailures, CFSTR(kIOPSFailureDischargeOverCurrent) ); 967 } 968 if (kSmartBattPFOpenThermistor & b->pfStatus) { 969 CFArrayAppendValue( permanentFailures, CFSTR(kIOPSFailureOpenThermistor) ); 970 } 971 if (kSmartBattPFFuseBlown & b->pfStatus) { 972 CFArrayAppendValue( permanentFailures, CFSTR(kIOPSFailureFuseBlown) ); 973 } 974 CFDictionarySetValue( outDict, CFSTR(kIOPSBatteryFailureModesKey), permanentFailures); 975 CFRelease(permanentFailures); 976 } 977 978 static char* batteryHealth = ""; 979 980 // Permanent failure -> Poor health 981 if (_batteryHas(b, CFSTR(kIOPMPSErrorConditionKey))) 982 { 983 if (CFEqual(b->failureDetected, CFSTR(kBatteryPermFailureString))) 984 { 985 CFDictionarySetValue(outDict, 986 CFSTR(kIOPSBatteryHealthKey), CFSTR(kIOPSPoorValue)); 987 CFDictionarySetValue(outDict, 988 CFSTR(kIOPSHealthConfidenceKey), CFSTR(kIOPSGoodValue)); 989 // Specifically log that the battery condition is permanent failure 990 CFDictionarySetValue(outDict, 991 CFSTR(kIOPSBatteryHealthConditionKey), CFSTR(kIOPSPermanentFailureValue)); 992 993 if (strncmp(batteryHealth, kIOPSPoorValue, sizeof(kIOPSPoorValue))) { 994 logASLBatteryHealthChanged(kIOPSPoorValue, 995 batteryHealth, 996 kIOPSPermanentFailureValue); 997 batteryHealth = kIOPSPoorValue; 998 } 999 1000 return; 1001 } 1002 } 1003 1004 double compareRatioTo = 0.80; 1005 double capRatio = 1.0; 1006 1007 if (0 != b->designCap) 1008 { 1009 capRatio = ((double)b->maxCap + kSmartBattReserve_mAh) / (double)b->designCap; 1010 } 1011 bool cyclesExceedStandard = false; 1012 1013 if (b->markedDeclining) { 1014 // The battery status should not fluctuate as battery re-learns and adjusts 1015 // its FullChargeCapacity. This number may fluctuate in normal operation. 1016 // Hysteresis: a battery that has previously been marked as 'declining' 1017 // will continue to be marked as declining until capacity ratio exceeds 83%. 1018 compareRatioTo = 0.83; 1019 } else { 1020 compareRatioTo = 0.80; 1021 } 1022 1023 struct timeval t; 1024 time_t currentTime = 0; 1025 bool canCompareTime = true; 1026 1027 1028#if !TARGET_OS_EMBEDDED 1029 // retrieve current time 1030 if (gettimeofday(&t, NULL) == -1) { 1031 canCompareTime = false; // do not use 7-day observation period. 1032 } 1033 else { 1034 currentTime = t.tv_sec; 1035 } 1036#else 1037 canCompareTime = false; // Don't use 7-day observation for this case 1038#endif 1039 1040 if (capRatio > 1.2) { 1041 // Poor|Perm Failure = max-capacity is more than 1.2x of the design-capacity. 1042 CFDictionarySetValue(outDict, CFSTR(kIOPSBatteryHealthKey), CFSTR(kIOPSPoorValue)); 1043 CFDictionarySetValue(outDict, CFSTR(kIOPSBatteryHealthConditionKey), 1044 CFSTR(kIOPSPermanentFailureValue)); 1045 1046 if (strncmp(batteryHealth, kIOPSPoorValue, sizeof(kIOPSPoorValue))) { 1047 logASLBatteryHealthChanged(kIOPSPoorValue, 1048 batteryHealth, 1049 kIOPSPermanentFailureValue); 1050 batteryHealth = kIOPSPoorValue; 1051 } 1052 if (b->hasLowCapRatio == true) { 1053 b->hasLowCapRatio = false; 1054 _setLowCapRatioTime(b->batterySerialNumber, 1055 false, 1056 0); 1057 } 1058 } else if (capRatio >= compareRatioTo) { 1059 b->markedDeclining = 0; 1060 // Good = CapRatio > 80% (plus or minus the 3% hysteresis mentioned above) 1061 CFDictionarySetValue(outDict, CFSTR(kIOPSBatteryHealthKey), CFSTR(kIOPSGoodValue)); 1062 1063 if ((batteryHealth[0] != 0) && (strncmp(batteryHealth, kIOPSGoodValue, sizeof(kIOPSGoodValue)))) { 1064 logASLBatteryHealthChanged(kIOPSGoodValue, 1065 batteryHealth, 1066 ""); 1067 } 1068 batteryHealth = kIOPSGoodValue; 1069 if (b->hasLowCapRatio == true) { 1070 b->hasLowCapRatio = false; 1071 _setLowCapRatioTime(b->batterySerialNumber, 1072 false, 1073 0); 1074 } 1075 } else { 1076 if (b->hasLowCapRatio == false) { 1077 b->hasLowCapRatio = true; 1078 b->lowCapRatioSinceTime = currentTime; 1079 _setLowCapRatioTime(b->batterySerialNumber, 1080 true, 1081 currentTime); 1082 } 1083 // mark as declining to use hysteresis. 1084 b->markedDeclining = 1; 1085 1086 // battery health status must be confirmed over a 7-day observation 1087 // period [7*86400] 1088 if (canCompareTime && (currentTime - b->lowCapRatioSinceTime <= 604800)) { 1089 // 7-day observation period is not complete, set the battery to Good 1090 CFDictionarySetValue(outDict, CFSTR(kIOPSBatteryHealthKey), 1091 CFSTR(kIOPSGoodValue)); 1092 if (strncmp(batteryHealth, kIOPSGoodValue, sizeof(kIOPSGoodValue))) { 1093 logASLBatteryHealthChanged(kIOPSGoodValue, 1094 batteryHealth, 1095 ""); 1096 batteryHealth = kIOPSGoodValue; 1097 } 1098 } 1099 else { 1100 // the 7-day observation period is complete, or the timestamps cannot 1101 // be compared now; set the kIOPSBatteryHealthKey to Fair/Poor/Check 1102 1103 if (cyclesExceedStandard) { 1104 if (capRatio >= 0.50) { 1105 // Fair = ExceedingCycles && CapRatio >= 50% && CapRatio < 80% 1106 CFDictionarySetValue(outDict, CFSTR(kIOPSBatteryHealthKey), 1107 CFSTR(kIOPSFairValue)); 1108 if (strncmp(batteryHealth, kIOPSFairValue, sizeof(kIOPSFairValue))) { 1109 logASLBatteryHealthChanged(kIOPSFairValue, 1110 batteryHealth, 1111 kIOPSCheckBatteryValue); 1112 batteryHealth = kIOPSFairValue; 1113 } 1114 } else { 1115 // Poor = ExceedingCycles && CapRatio < 50% 1116 CFDictionarySetValue(outDict, CFSTR(kIOPSBatteryHealthKey), 1117 CFSTR(kIOPSPoorValue)); 1118 if (strncmp(batteryHealth, kIOPSPoorValue, sizeof(kIOPSPoorValue))) { 1119 logASLBatteryHealthChanged(kIOPSPoorValue, 1120 batteryHealth, 1121 kIOPSCheckBatteryValue); 1122 batteryHealth = kIOPSPoorValue; 1123 } 1124 } 1125 // HealthCondition == CheckBattery to distinguish the Fair & Poor 1126 // cases from from permanent failure (above), where 1127 // HealthCondition == PermanentFailure 1128 CFDictionarySetValue(outDict, CFSTR(kIOPSBatteryHealthConditionKey), 1129 CFSTR(kIOPSCheckBatteryValue)); 1130 } else { 1131 // Check battery = NOT ExceedingCycles && CapRatio < 80% 1132 CFDictionarySetValue(outDict, CFSTR(kIOPSBatteryHealthKey), 1133 CFSTR(kIOPSCheckBatteryValue)); 1134 if (strncmp(batteryHealth, kIOPSCheckBatteryValue, 1135 sizeof(kIOPSCheckBatteryValue))) { 1136 logASLBatteryHealthChanged(kIOPSCheckBatteryValue, 1137 batteryHealth, 1138 ""); 1139 batteryHealth = kIOPSCheckBatteryValue; 1140 } 1141 } 1142 } 1143 } 1144 return; 1145} 1146 1147bool isFullyCharged(IOPMBattery *b) 1148{ 1149 bool is_charged = false; 1150 1151 if (!b) return false; 1152 1153 // Set IsCharged if capacity >= 95% 1154 // - Some portables will not initiate a battery charge if AC is 1155 // connected when copacity is >= 95%. 1156 // - We consider > 95% to be fully charged; the battery will not charge 1157 // any higher until AC is unplugged and re-attached. 1158 // - IsCharged should be true when the external power adapter LED is Green; 1159 // should be false when the external power adapter LED is Orange. 1160 1161 if (b->isPresent && (0 != b->maxCap)) { 1162 is_charged = ((100*b->currentCap/b->maxCap) >= 95); 1163 } 1164 1165 return is_charged; 1166} 1167 1168/* 1169 * Implicit argument: All the global variables that track battery state 1170 */ 1171CFDictionaryRef packageKernelPowerSource(IOPMBattery *b) 1172{ 1173 CFNumberRef n, n0; 1174 CFMutableDictionaryRef mDict = NULL; 1175 int temp; 1176 int minutes; 1177 int set_capacity, set_charge; 1178 1179 if (!b) { 1180 IOPMBattery **batts = _batteries(); 1181 b = batts[0]; 1182 } 1183 1184 // Create the battery info dictionary 1185 mDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, 1186 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 1187 if(!mDict) 1188 return NULL; 1189 1190 // Does the battery provide its own time remaining estimate? 1191 CFDictionarySetValue(mDict, CFSTR("Battery Provides Time Remaining"), kCFBooleanTrue); 1192 1193 // Was there an error/failure? Set that. 1194 if (b->failureDetected) { 1195 CFDictionarySetValue(mDict, CFSTR(kIOPSFailureKey), b->failureDetected); 1196 } 1197 1198 // Is there a charging problem? 1199 if (b->chargeStatus) { 1200 CFDictionarySetValue(mDict, CFSTR(kIOPMPSBatteryChargeStatusKey), b->chargeStatus); 1201 } 1202 1203 // Battery provided serial number 1204 if (b->batterySerialNumber) { 1205 CFDictionarySetValue(mDict, CFSTR(kIOPSHardwareSerialNumberKey), b->batterySerialNumber); 1206 } 1207 1208 // Type = "InternalBattery", and "Transport Type" = "Internal" 1209 CFDictionarySetValue(mDict, CFSTR(kIOPSTransportTypeKey), CFSTR(kIOPSInternalType)); 1210 CFDictionarySetValue(mDict, CFSTR(kIOPSTypeKey), CFSTR(kIOPSInternalBatteryType)); 1211 1212 // Set Power Source State to AC/Battery 1213 CFDictionarySetValue(mDict, CFSTR(kIOPSPowerSourceStateKey), 1214 (b->externalConnected ? CFSTR(kIOPSACPowerValue):CFSTR(kIOPSBatteryPowerValue))); 1215 1216 // round charge and capacity down to a % scale 1217 if(0 != b->maxCap) 1218 { 1219 set_capacity = 100; 1220 set_charge = b->swCalculatedPR; 1221 1222 if( (100 == set_charge) && b->isCharging) 1223 { 1224 // We will artificially cap the percentage to 99% while charging 1225 // Batteries may take 10-20 min beyond 100% of charging to 1226 // relearn their absolute maximum capacity. Leave cap at 99% 1227 // to indicate we're not done charging. (4482296, 3285870) 1228 set_charge = 99; 1229 } 1230 } else { 1231 // Bad battery or bad reading => 0 capacity 1232 set_capacity = set_charge = 0; 1233 } 1234 1235 if (control.noPoll) { 1236 // 55% & 5:55 remaining means that battery polling is stopped for performance testing. 1237 set_charge = 55; 1238 } 1239 1240 // Set maximum capacity 1241 n = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &set_capacity); 1242 if(n) { 1243 CFDictionarySetValue(mDict, CFSTR(kIOPSMaxCapacityKey), n); 1244 CFRelease(n); 1245 } 1246 1247 // Set current charge 1248 n = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &set_charge); 1249 if(n) { 1250 CFDictionarySetValue(mDict, CFSTR(kIOPSCurrentCapacityKey), n); 1251 CFRelease(n); 1252 } 1253 1254 // Set Amperage 1255 n = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &b->avgAmperage); 1256 if(n) { 1257 CFDictionarySetValue(mDict, CFSTR(kIOPSCurrentKey), n); 1258 CFRelease(n); 1259 } 1260 1261 // Set isPresent flag 1262 CFDictionarySetValue(mDict, CFSTR(kIOPSIsPresentKey), 1263 b->isPresent ? kCFBooleanTrue:kCFBooleanFalse); 1264 1265 if (control.noPoll) { 1266 // 55% & 5:55 remaining means that battery polling is stopped for performance testing. 1267 minutes = 355; 1268 } else { 1269 minutes = b->swCalculatedTR; 1270 } 1271 1272 temp = 0; 1273 n0 = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &temp); 1274 1275 if( !b->isPresent ) { 1276 // remaining time calculations only have meaning if the battery is present 1277 CFDictionarySetValue(mDict, CFSTR(kIOPSIsChargingKey), kCFBooleanFalse); 1278 CFDictionarySetValue(mDict, CFSTR(kIOPSTimeToFullChargeKey), n0); 1279 CFDictionarySetValue(mDict, CFSTR(kIOPSTimeToEmptyKey), n0); 1280 } else { 1281 // There IS a battery installed. 1282 if(b->isCharging) { 1283 // Set _isCharging to True 1284 CFDictionarySetValue(mDict, CFSTR(kIOPSIsChargingKey), kCFBooleanTrue); 1285 // Set IsFinishingCharge 1286 CFDictionarySetValue(mDict, CFSTR(kIOPSIsFinishingChargeKey), 1287 (b->maxCap && (99 <= (100*b->currentCap/b->maxCap))) ? kCFBooleanTrue:kCFBooleanFalse); 1288 n = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &minutes); 1289 if(n) { 1290 CFDictionarySetValue(mDict, CFSTR(kIOPSTimeToFullChargeKey), n); 1291 CFRelease(n); 1292 } 1293 CFDictionarySetValue(mDict, CFSTR(kIOPSTimeToEmptyKey), n0); 1294 } else { 1295 // Not Charging 1296 // Set _isCharging to False 1297 CFDictionarySetValue(mDict, CFSTR(kIOPSIsChargingKey), kCFBooleanFalse); 1298 // But are we plugged in? 1299 if(b->externalConnected) 1300 { 1301 // plugged in but not charging == fully charged 1302 CFDictionarySetValue(mDict, CFSTR(kIOPSTimeToFullChargeKey), n0); 1303 CFDictionarySetValue(mDict, CFSTR(kIOPSTimeToEmptyKey), n0); 1304 1305 CFDictionarySetValue(mDict, CFSTR(kIOPSIsChargedKey), 1306 isFullyCharged(b) ? kCFBooleanTrue:kCFBooleanFalse); 1307 } else { 1308 // not charging, not plugged in == d_isCharging 1309 n = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &minutes); 1310 if(n) { 1311 CFDictionarySetValue(mDict, CFSTR(kIOPSTimeToEmptyKey), n); 1312 CFRelease(n); 1313 } 1314 CFDictionarySetValue(mDict, CFSTR(kIOPSTimeToFullChargeKey), n0); 1315 } 1316 } 1317 1318 } 1319 CFRelease(n0); 1320 1321 // Set health & confidence 1322 _setBatteryHealthConfidence(mDict, b); 1323 1324 1325 // Set name 1326 if(b->name) { 1327 CFDictionarySetValue(mDict, CFSTR(kIOPSNameKey), b->name); 1328 } else { 1329 CFDictionarySetValue(mDict, CFSTR(kIOPSNameKey), CFSTR("Unnamed")); 1330 } 1331 1332 return mDict; 1333} 1334 1335// _readAndPublicACAdapter 1336// These keys describe the bit-layout of the 64-bit AC info structure. 1337 1338/* Legacy format */ 1339 1340#define kACCRCBit 56 // size 8 1341#define kACIDBit 44 // size 12 1342#define kACPowerBit 36 // size 8 1343#define kACRevisionBit 32 // size 4 1344#define kACSerialBit 8 // size 24 1345#define kACFamilyBit 0 // size 8 1346 1347/* New format (intro'd in Jan 2012) */ 1348 1349//#define kACCRCBit 56 // 8 bits, same as in legacy 1350#define kACCurrentIdBit 48 // 8 bits 1351//#define kACCommEnableBit 45 // 1 bit; doesn't contain meaningful information 1352#define kACSourceIdBit 44 // 3 bits 1353//#define kACPowerBit 36 // 8 bits, same as in legacy 1354#define kACVoltageIDBit 33 // 3 bits 1355//#define kACSerialBit 8 // 25 bits 1356//#define kACFamilyBit 0 // 8 bits, same as in legacy 1357 1358#define k3BitMask 0x7 1359 1360 1361typedef struct { 1362 uint32_t valCommEn; 1363 uint32_t valVoltageID; 1364 uint32_t valID; 1365 uint32_t valPower; 1366 uint32_t valRevision; 1367 uint32_t valSerial; 1368 uint32_t valFamily; 1369 uint32_t valCurrent; 1370 uint32_t valSource; 1371} AdapterAttributes; 1372 1373static void stuffInt32(CFMutableDictionaryRef d, CFStringRef k, uint32_t n) 1374{ 1375 CFNumberRef stuffNum = NULL; 1376 if ((stuffNum = CFNumberCreate(0, kCFNumberSInt32Type, &n))) 1377 { 1378 CFDictionarySetValue(d, k, stuffNum); 1379 CFRelease(stuffNum); 1380 } 1381} 1382 1383static IOReturn _readAndPublishACAdapter(bool adapterExists, CFDictionaryRef batteryACDict) 1384{ 1385 static bool adapterInfoPublished = false; 1386 static CFDictionaryRef oldACDict = NULL; 1387 CFStringRef key = NULL; 1388 CFMutableDictionaryRef acDict = NULL; 1389 IOReturn ret = kIOReturnSuccess; 1390 Boolean success = FALSE; 1391#if !TARGET_OS_EMBEDDED 1392 int j = 0; 1393#endif 1394 AdapterAttributes info; 1395 1396 bzero(&info, sizeof(info)); 1397 1398 // Make sure we re-read the adapter on wake from sleep 1399 if (control.readACAdapterAgain) { 1400 adapterInfoPublished = false; 1401 control.readACAdapterAgain = false; 1402 } 1403 1404 // Always republish AC info if it comes from the battery 1405 if (adapterExists && batteryACDict && oldACDict && !CFEqual(oldACDict, batteryACDict)) 1406 { 1407 adapterInfoPublished = false; 1408 } 1409 1410 // don't re-publish AC info until the adapter changes 1411 if (adapterExists && adapterInfoPublished) 1412 { 1413 return kIOReturnSuccess; 1414 } 1415 1416 if (adapterExists && !batteryACDict) 1417 { 1418 uint64_t acBits; 1419 1420 ret = _getACAdapterInfo(&acBits); 1421 if (kIOReturnSuccess != ret) { 1422 return ret; 1423 } 1424 1425 // Decode SMC key 1426 info.valID = (acBits >> kACIDBit) & 0xFFF; 1427 info.valFamily = (acBits >> kACFamilyBit) & 0xFF; 1428 info.valPower = (acBits >> kACPowerBit) & 0xFF; 1429 if ( (info.valSource = (acBits >> kACSourceIdBit) & k3BitMask)) 1430 { 1431 // New format 1432 info.valSerial = (acBits >> kACSerialBit) & 0x1FFFFFF; 1433 info.valCurrent = ((acBits >> kACCurrentIdBit) & 0xFF) * 25; 1434 info.valVoltageID = (acBits >> kACVoltageIDBit) & k3BitMask; 1435 } else { 1436 // Legacy format 1437 info.valSerial = (acBits >> kACSerialBit) & 0xFFFFFF; 1438 info.valRevision = (acBits >> kACRevisionBit) & 0xF; 1439 } 1440 1441 // Publish values in dictionary 1442 acDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, 1443 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 1444 1445 if (!acDict) { 1446 ret = kIOReturnNoMemory; 1447 goto exit; 1448 } 1449 1450 if (info.valSource) { 1451 // New format 1452 stuffInt32(acDict, CFSTR(kIOPSPowerAdapterCurrentKey), info.valCurrent); 1453 stuffInt32(acDict, CFSTR(kIOPSPowerAdapterSourceKey), info.valSource); 1454 1455 } 1456 else { 1457 // Legacy format 1458 stuffInt32(acDict, CFSTR(kIOPSPowerAdapterRevisionKey), info.valRevision); 1459 } 1460 1461 if (0 != info.valPower) { 1462 stuffInt32(acDict, CFSTR(kIOPSPowerAdapterWattsKey), info.valPower); 1463 } 1464 1465 stuffInt32(acDict, CFSTR(kIOPSPowerAdapterIDKey), info.valID); 1466 stuffInt32(acDict, CFSTR(kIOPSPowerAdapterSerialNumberKey), info.valSerial); 1467 stuffInt32(acDict, CFSTR(kIOPSPowerAdapterFamilyKey), info.valFamily); 1468 1469 batteryACDict = acDict; 1470 } 1471 1472 // Write dictionary into dynamic store 1473 key = SCDynamicStoreKeyCreate( 1474 kCFAllocatorDefault, 1475 CFSTR("%@%@"), 1476 kSCDynamicStoreDomainState, 1477 CFSTR(kIOPSDynamicStorePowerAdapterKey)); 1478 if (!key) { 1479 ret = kIOReturnError; 1480 goto exit; 1481 } 1482 1483 if (oldACDict) { 1484 CFRelease(oldACDict); 1485 oldACDict = NULL; 1486 } 1487 1488 if (!adapterExists) { 1489 success = PMStoreRemoveValue(key); 1490 adapterInfoPublished = false; 1491 } else { 1492 success = PMStoreSetValue(key, batteryACDict); 1493 adapterInfoPublished = true; 1494 oldACDict = batteryACDict; 1495 CFRetain(oldACDict); 1496 } 1497 1498 if (success) { 1499 notify_post("com.apple.system.powermanagement.poweradapter"); 1500 ret = kIOReturnSuccess; 1501 } else { 1502 ret = kIOReturnLockedRead; 1503 } 1504 1505exit: 1506 if (acDict) 1507 CFRelease(acDict); 1508 if (key) 1509 CFRelease(key); 1510 return ret; 1511} 1512 1513/**** User-space power source code lives below here ********************************/ 1514/***********************************************************************************/ 1515/***********************************************************************************/ 1516/***********************************************************************************/ 1517 1518 1519 1520/***********************************************************************************/ 1521static PSStruct *iops_newps(int pid, int psid) 1522{ 1523 // Find the first empty slot in gPSList 1524 for (int i=0; i<kPSMaxCount; i++) 1525 { 1526 if (0 == gPSList[i].psid) 1527 { 1528 bzero(&gPSList[i], sizeof(PSStruct)); 1529 gPSList[i].pid = pid; 1530 gPSList[i].psid = psid; 1531 return &gPSList[i]; 1532 } 1533 } 1534 1535 return NULL; 1536} 1537 1538static PSStruct *iopsFromPSID(int _pid, int _psid) 1539{ 1540 for (int i=0; i<kPSMaxCount; i++) 1541 { 1542 if (gPSList[i].psid == _psid 1543 && gPSList[i].pid == _pid) 1544 { 1545 return &gPSList[i]; 1546 } 1547 } 1548 1549 return NULL; 1550} 1551 1552 1553__private_extern__ CFDictionaryRef getActiveBatteryDictionary(void) 1554{ 1555 for (int i=0; i<kPSMaxCount; i++) 1556 { 1557 if (!gPSList[i].description) { 1558 continue; 1559 } 1560 1561 CFStringRef transport_type = NULL; 1562 transport_type = CFDictionaryGetValue(gPSList[i].description, 1563 CFSTR(kIOPSTransportTypeKey)); 1564 if (isA_CFString(transport_type) 1565 && ( CFEqual(transport_type, CFSTR(kIOPSInternalType)))) 1566 { 1567 return gPSList[i].description; 1568 } 1569 } 1570 return NULL; 1571} 1572 1573__private_extern__ CFDictionaryRef getActiveUPSDictionary(void) 1574{ 1575 for (int i=0; i<kPSMaxCount; i++) 1576 { 1577 if (!gPSList[i].description) { 1578 continue; 1579 } 1580 1581 CFStringRef transport_type = NULL; 1582 transport_type = CFDictionaryGetValue(gPSList[i].description, 1583 CFSTR(kIOPSTransportTypeKey)); 1584 if (isA_CFString(transport_type) 1585 && ( CFEqual(transport_type, CFSTR(kIOPSSerialTransportType)) 1586 || CFEqual(transport_type, CFSTR(kIOPSUSBTransportType)) 1587 || CFEqual(transport_type, CFSTR(kIOPSNetworkTransportType)) )) 1588 { 1589 return gPSList[i].description; 1590 } 1591 } 1592 return NULL; 1593} 1594 1595 1596 1597__private_extern__ int getActivePSType(void) 1598{ 1599 CFDictionaryRef activeBattery = getActiveBatteryDictionary(); 1600 CFDictionaryRef activeUPS = getActiveUPSDictionary(); 1601 CFStringRef ps_state = NULL; 1602 1603 /* if (!activeBattery) is testing for whether batteries are supported on 1604 * this system at all, e.g. mobile vs desktop. */ 1605 if(!activeBattery) 1606 { 1607 if(!activeUPS) { 1608 // no batteries, no UPS -> AC Power 1609 return kIOPSProvidedByAC; 1610 } else { 1611 ps_state = CFDictionaryGetValue(activeUPS, 1612 CFSTR(kIOPSPowerSourceStateKey)); 1613 if(ps_state && CFEqual(ps_state, CFSTR(kIOPSACPowerValue))) 1614 { 1615 // no batteries, yes UPS, UPS is running off of AC power -> AC Power 1616 return kIOPSProvidedByAC; 1617 } else if(ps_state && CFEqual(ps_state, CFSTR(kIOPSBatteryPowerValue))) 1618 { 1619 // no batteries, yes UPS, UPS is running drawing its Battery power -> UPS Power 1620 return kIOPSProvidedByExternalBattery; 1621 } 1622 1623 } 1624 // Error in the data we were passed 1625 return kIOPSProvidedByAC; 1626 } else { 1627 1628 ps_state = CFDictionaryGetValue(activeBattery, 1629 CFSTR(kIOPSPowerSourceStateKey)); 1630 if(ps_state && CFEqual(ps_state, 1631 CFSTR(kIOPSBatteryPowerValue))) 1632 { 1633 // Yes batteries, yes running on battery power -> Battery power 1634 return kIOPSProvidedByBattery; 1635 } 1636 else 1637 { 1638 // batteries are on AC power. let's check if UPS is present. 1639 if (!activeUPS) 1640 { 1641 // yes batteries on AC power, no UPS present -> AC Power 1642 return kIOPSProvidedByAC; 1643 } else { 1644 ps_state = CFDictionaryGetValue(activeUPS, 1645 CFSTR(kIOPSPowerSourceStateKey)); 1646 if(ps_state && CFEqual(ps_state, CFSTR(kIOPSBatteryPowerValue))) 1647 { 1648 // yes batteries on AC power, UPS is on its battery -> UPS Power 1649 return kIOPSProvidedByExternalBattery; 1650 } else if(ps_state && CFEqual(ps_state, CFSTR(kIOPSACPowerValue))) 1651 { 1652 // yes batteries on AC Power, UPS is drawing AC Power -> AC Power 1653 return kIOPSProvidedByAC; 1654 } 1655 } 1656 } 1657 } 1658 1659 // Should not reach this point. Return something safe. 1660 return kIOPSProvidedByAC; 1661} 1662 1663 1664/***********************************************************************************/ 1665// MIG handler - back end for IOKit API IOPSCreatePowerSource 1666kern_return_t _io_ps_new_pspowersource( 1667 mach_port_t server __unused, 1668 audit_token_t token, 1669 int *psid, // out 1670 int *result) 1671{ 1672 static unsigned int gPSID = 5000; 1673 int callerPID; 1674 PSStruct *ps; 1675 1676 audit_token_to_au32(token, NULL, NULL, NULL, NULL, NULL, 1677 &callerPID, NULL, NULL); 1678 1679 *result = kIOReturnError; 1680 1681 ps = iops_newps(callerPID, gPSID); 1682 if (!ps) 1683 { 1684 *result = kIOReturnNoSpace; 1685 goto exit; 1686 } 1687 1688 ps->procdeathsrc= dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, 1689 callerPID, 1690 DISPATCH_PROC_EXIT, 1691 dispatch_get_main_queue()); 1692 1693 /* Setup automatic cleanup if client process dies 1694 */ 1695 dispatch_source_set_cancel_handler(ps->procdeathsrc, ^{ 1696 /* 1697 * When the client process dies, remove 1698 * this power source and stop showing it to IOPS API clients. 1699 * 1700 */ 1701 1702 if (ps->procdeathsrc) { 1703 dispatch_release(ps->procdeathsrc); 1704 } 1705 if (ps->description) { 1706 CFRelease(ps->description); 1707 } 1708 if (ps->log) { 1709 CFRelease(ps->log); 1710 } 1711 bzero(ps, sizeof(PSStruct)); 1712 1713 dispatch_async(dispatch_get_main_queue(), ^() 1714 { HandlePublishAllPowerSources(); }); 1715 }); 1716 1717 dispatch_source_set_event_handler(ps->procdeathsrc, ^{ 1718 dispatch_source_cancel(ps->procdeathsrc); 1719 }); 1720 1721 dispatch_resume(ps->procdeathsrc); 1722 1723 1724 *psid = gPSID++; 1725 *result = kIOReturnSuccess; 1726 1727exit: 1728 return KERN_SUCCESS; 1729} 1730 1731/***********************************************************************************/ 1732// MIG handler - back end for IOKit API IOPSSetPowerSourceDetails 1733 1734kern_return_t _io_ps_update_pspowersource( 1735 mach_port_t server __unused, 1736 audit_token_t token, 1737 int psid, 1738 vm_offset_t details_ptr, 1739 mach_msg_type_number_t details_len, 1740 int *return_code) 1741{ 1742 CFDictionaryRef details = NULL; 1743 int callerPID; 1744 audit_token_to_au32(token, NULL, NULL, NULL, NULL, NULL, 1745 &callerPID, NULL, NULL); 1746 1747 syslog(0,0, ASL_LEVEL_ERR, "updating power source id = %d\n", psid); 1748 1749 *return_code = kIOReturnError; 1750 1751 details = IOCFUnserialize((const char *)details_ptr, NULL, 0, NULL); 1752 1753 if (!isA_CFDictionary(details)) 1754 { 1755 *return_code = kIOReturnBadArgument; 1756 } else { 1757 PSStruct *next = iopsFromPSID(callerPID, psid); 1758 if (!next) { 1759 *return_code = kIOReturnNotFound; 1760 } else { 1761 if (next->description) { 1762 CFRelease(next->description); 1763 } 1764 next->description = details; 1765 updateLogBuffer(next, false); 1766 *return_code = kIOReturnSuccess; 1767 dispatch_async(dispatch_get_main_queue(), ^() 1768 { HandlePublishAllPowerSources(); }); 1769 } 1770 } 1771 1772 if (kIOReturnSuccess != *return_code) { 1773 CFRelease(details); 1774 } 1775 1776 vm_deallocate(mach_task_self(), details_ptr, details_len); 1777 return 0; 1778} 1779 1780kern_return_t _io_ps_release_pspowersource( 1781 mach_port_t server __unused, 1782 audit_token_t token, 1783 int psid) 1784{ 1785 int callerPID; 1786 audit_token_to_au32(token, NULL, NULL, NULL, NULL, NULL, 1787 &callerPID, NULL, NULL); 1788 1789 syslog(0,0, ASL_LEVEL_ERR, "releasing power source id = %d\n", psid); 1790 1791 PSStruct *toRelease = iopsFromPSID(callerPID, psid); 1792 if (toRelease) { 1793 dispatch_source_cancel(toRelease->procdeathsrc); 1794 } 1795 1796 return 0; 1797} 1798 1799kern_return_t _io_ps_copy_powersources_info( 1800 mach_port_t server __unused, 1801 vm_offset_t *ps_ptr, 1802 mach_msg_type_number_t *ps_len, 1803 int *return_code) 1804{ 1805 CFMutableArrayRef return_value = NULL; 1806 1807 for (int i=0; i<kPSMaxCount; i++) { 1808 if (gPSList[i].description) { 1809 if (!return_value) { 1810 return_value = CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks); 1811 } 1812 CFArrayAppendValue(return_value, 1813 (const void *)gPSList[i].description); 1814 } 1815 } 1816 1817 if (!return_value) { 1818 *ps_ptr = 0; 1819 *ps_len = 0; 1820 } else { 1821 CFDataRef d = CFPropertyListCreateData(0, return_value, 1822 kCFPropertyListBinaryFormat_v1_0, 1823 0, NULL); 1824 CFRelease(return_value); 1825 1826 if (d) { 1827 *ps_len = CFDataGetLength(d); 1828 1829 vm_allocate(mach_task_self(), (vm_address_t *)ps_ptr, *ps_len, TRUE); 1830 1831 memcpy((void *)*ps_ptr, CFDataGetBytePtr(d), *ps_len); 1832 1833 CFRelease(d); 1834 } 1835 } 1836 *return_code = kIOReturnSuccess; 1837 1838 return 0; 1839} 1840 1841 1842CFArrayRef copyPowerSourceLog(PSStruct *ps, CFAbsoluteTime ts) 1843{ 1844 CFIndex i, arrCnt; 1845 CFDateRef entry_ts = NULL; 1846 CFDateRef input_ts = NULL; 1847 1848 CFDictionaryRef entry = NULL; 1849 CFMutableArrayRef updates = NULL; 1850 1851 1852 arrCnt = CFArrayGetCount(ps->log); 1853 1854 if (arrCnt == 0) 1855 goto exit; 1856 1857 updates = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 1858 if (updates == NULL) { 1859 goto exit; 1860 } 1861 1862 input_ts = CFDateCreate(NULL, ts); 1863 if (input_ts == NULL) { 1864 goto exit; 1865 } 1866 1867 if (arrCnt < kBattLogMaxEntries) 1868 ps->logIdx = 0; 1869 1870 i = ps->logIdx; 1871 do { 1872 entry = NULL; 1873 if (i < arrCnt) 1874 entry = CFArrayGetValueAtIndex(ps->log, i); 1875 if (!isA_CFDictionary(entry)) { 1876 i = (i+1) % kBattLogMaxEntries; 1877 if (i >= arrCnt) i = 0; 1878 continue; 1879 } 1880 1881 if (!entry_ts) { 1882 entry_ts = CFDictionaryGetValue(entry, CFSTR(kIOPSBattLogEntryTime)); 1883 1884 if ((entry_ts == NULL) || (CFDateCompare(entry_ts, input_ts, NULL) == kCFCompareLessThan)) { 1885 i = (i+1) % kBattLogMaxEntries; 1886 if (i >= arrCnt) i = 0; 1887 entry_ts = NULL; 1888 continue; 1889 } 1890 } 1891 1892 CFArrayAppendValue(updates, entry); 1893 i = (i+1) % kBattLogMaxEntries; 1894 if (i >= arrCnt) i = 0; 1895 1896 } while (i != ps->logIdx); 1897 1898 CFArrayRemoveAllValues(ps->log); 1899 ps->logIdx = 0; 1900 1901exit: 1902 1903 if (input_ts) 1904 CFRelease(input_ts); 1905 1906 return updates; 1907} 1908 1909kern_return_t _io_ps_copy_chargelog( 1910 mach_port_t server __unused, 1911 audit_token_t token, 1912 double ts, 1913 vm_offset_t *updates, 1914 mach_msg_type_number_t *updates_len, 1915 int *rc) 1916{ 1917 CFDataRef serializedLog = NULL; 1918 CFArrayRef psLog = NULL; 1919 CFStringRef name = NULL; 1920 CFMutableDictionaryRef logDict = NULL; 1921 CFErrorRef err = NULL; 1922 1923 *updates = NULL; *updates_len = 0; 1924 *rc = kIOReturnNotFound; 1925 1926 if (!auditTokenHasEntitlement(token, CFSTR("com.apple.private.iokit.powerlogging"))) 1927 { 1928 *rc = kIOReturnNotPrivileged; 1929 goto exit; 1930 } 1931 1932 logDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, 1933 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 1934 if (!logDict) 1935 goto exit; 1936 1937 for (int i=0; i<kPSMaxCount; i++) 1938 { 1939 if (!gPSList[i].log) { 1940 continue; 1941 } 1942 1943 name = CFDictionaryGetValue(gPSList[i].description, CFSTR(kIOPSNameKey)); 1944 if (!isA_CFString(name)) { 1945 continue; 1946 } 1947 1948 psLog = copyPowerSourceLog(&gPSList[i], ts); 1949 if (!psLog) 1950 continue; 1951 1952 CFDictionarySetValue(logDict, name, psLog); 1953 CFRelease(psLog); 1954 1955 } 1956 1957 serializedLog = CFPropertyListCreateData(0, logDict, 1958 kCFPropertyListBinaryFormat_v1_0, 0, &err); 1959 1960 if (!serializedLog) 1961 goto exit; 1962 1963 *updates_len = (mach_msg_type_number_t)CFDataGetLength(serializedLog); 1964 vm_allocate(mach_task_self(), (vm_address_t *)updates, *updates_len, TRUE); 1965 if (*updates == 0) 1966 goto exit; 1967 1968 memcpy((void *)*updates, CFDataGetBytePtr(serializedLog), *updates_len); 1969 *rc = kIOReturnSuccess; 1970 1971 1972exit: 1973 if (logDict) CFRelease(logDict); 1974 if (serializedLog) CFRelease(serializedLog); 1975 1976 return KERN_SUCCESS; 1977 1978} 1979 1980 1981 1982