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