1/*
2 * Copyright (c) 2005 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#include <IOKit/IOService.h>
25#include <IOKit/IOWorkLoop.h>
26#include <IOKit/IOTimerEventSource.h>
27#include <IOKit/pwr_mgt/RootDomain.h>
28#include <IOKit/pwr_mgt/IOPMPrivate.h>
29#include <libkern/c++/OSObject.h>
30#include <kern/clock.h>
31#include "AppleSmartBatteryManager.h"
32#include "AppleSmartBattery.h"
33
34
35// Retry attempts on SMBus command failure
36enum {
37    kRetryAttempts = 5,
38    kInitialPollCountdown = 5,
39    kIncompleteReadRetryMax = 10
40};
41
42enum {
43    kSecondsUntilValidOnWake    = 30,
44    kPostChargeWaitSeconds      = 120,
45    kPostDischargeWaitSeconds   = 120
46};
47
48
49
50// This bit lets us distinguish between reads & writes in the transactionCompletion switch statement
51#define kStage2                             0x8000
52
53// Argument to transactionCompletion indicating we should start/re-start polling
54#define kTransactionRestart                 0x9999
55
56#define kErrorRetryAttemptsExceeded         "Read Retry Attempts Exceeded"
57#define kErrorOverallTimeoutExpired         "Overall Read Timeout Expired"
58#define kErrorZeroCapacity                  "Capacity Read Zero"
59#define kErrorPermanentFailure              "Permanent Battery Failure"
60#define kErrorNonRecoverableStatus          "Non-recoverable status failure"
61#define kErrorClearBattery                  "Clear Battery"
62
63static const uint32_t kBatteryReadAllTimeout = 10000;       // 10 seconds
64
65// Delays to use on subsequent SMBus re-read failures.
66// In microseconds.
67static const uint32_t microSecDelayTable[kRetryAttempts] =
68    { 10, 100, 1000, 10000, 250000 };
69
70/* The union of the errors listed in STATUS_ERROR_NEEDS_RETRY
71 * and STATUS_ERROR_NON_RECOVERABLE should equal the entirety of
72 * SMBus errors listed in IOSMBusController.h
73 */
74#define STATUS_ERROR_NEEDS_RETRY(err)                           \
75     ((kIOSMBusStatusDeviceAddressNotAcknowledged == err)       \
76   || (kIOSMBusStatusDeviceCommandAccessDenied == err)          \
77   || (kIOSMBusStatusDeviceAccessDenied == err)                 \
78   || (kIOSMBusStatusUnknownHostError == err)                   \
79   || (kIOSMBusStatusUnknownFailure == err)                     \
80   || (kIOSMBusStatusDeviceError == err)                        \
81   || (kIOSMBusStatusTimeout == err)                            \
82   || (kIOSMBusStatusBusy == err))
83
84#define STATUS_ERROR_NON_RECOVERABLE(err)                       \
85     ((kIOSMBusStatusHostUnsupportedProtocol == err)            \
86   || (kIOSMBusStatusPECError == err))
87
88
89// Keys we use to publish battery state in our IOPMPowerSource::properties array
90static const OSSymbol *_MaxErrSym               = OSSymbol::withCString(kIOPMPSMaxErrKey);
91static const OSSymbol *_DeviceNameSym           = OSSymbol::withCString(kIOPMDeviceNameKey);
92static const OSSymbol *_FullyChargedSym         = OSSymbol::withCString(kIOPMFullyChargedKey);
93static const OSSymbol *_AvgTimeToEmptySym       = OSSymbol::withCString("AvgTimeToEmpty");
94static const OSSymbol *_InstantTimeToEmptySym   = OSSymbol::withCString("InstantTimeToEmpty");
95static const OSSymbol *_InstantAmperageSym      = OSSymbol::withCString("InstantAmperage");
96static const OSSymbol *_AvgTimeToFullSym        = OSSymbol::withCString("AvgTimeToFull");
97static const OSSymbol *_ManfDateSym             = OSSymbol::withCString(kIOPMPSManufactureDateKey);
98static const OSSymbol *_DesignCapacitySym       = OSSymbol::withCString(kIOPMPSDesignCapacityKey);
99static const OSSymbol *_TemperatureSym          = OSSymbol::withCString("Temperature");
100static const OSSymbol *_CellVoltageSym          = OSSymbol::withCString("CellVoltage");
101static const OSSymbol *_ManufacturerDataSym     = OSSymbol::withCString("ManufacturerData");
102static const OSSymbol *_PFStatusSym             = OSSymbol::withCString("PermanentFailureStatus");
103static const OSSymbol *_DesignCycleCount70Sym   = OSSymbol::withCString("DesignCycleCount70");
104static const OSSymbol *_DesignCycleCount9CSym   = OSSymbol::withCString("DesignCycleCount9C");
105static const OSSymbol *_PackReserveSym          = OSSymbol::withCString("PackReserve");
106static const OSSymbol *_OpStatusSym      = OSSymbol::withCString("OperationStatus");
107static const OSSymbol *_PermanentFailureSym     = OSSymbol::withCString(kErrorPermanentFailure);
108/* _SerialNumberSym represents the manufacturer's 16-bit serial number in
109    numeric format.
110 */
111static const OSSymbol *_SerialNumberSym         = OSSymbol::withCString("FirmwareSerialNumber");
112/* _HardwareSerialSym == AppleSoftwareSerial
113   represents the Apple-defined 12+ character string in firmware.
114 */
115static const OSSymbol *_HardwareSerialSym       = OSSymbol::withCString("BatterySerialNumber");
116
117// CommandMachine::pathBits
118enum {
119    kUseLastPath    = 0,
120    kBoot           = 1,
121    kFull           = 2,
122    kUserVis        = 4,
123};
124typedef int MachinePath;
125
126#define kBootPathKey             "BootPathUpdated"
127#define kFullPathKey             "FullPathUpdated"
128#define kUserVisPathKey          "UserVisiblePathUpdated"
129
130// CommandMachine::protocol
131#define kWord                   kIOSMBusProtocolReadWord
132#define kBlock                  kIOSMBusProtocolReadBlock
133#define kBlockData              (kIOSMBusProtocolReadBlock | 0x1000)
134#define kWriteWord              kIOSMBusProtocolWriteWord
135#define kBatt                   kSMBusBatteryAddr
136#define kMgr                    kSMBusManagerAddr
137
138#define kFinishPolling          0xF1
139
140
141#define super IOPMPowerSource
142
143OSDefineMetaClassAndStructors(AppleSmartBattery,IOPMPowerSource)
144
145/******************************************************************************
146 * AppleSmartBattery::smartBattery
147 *
148 ******************************************************************************/
149
150AppleSmartBattery *
151AppleSmartBattery::smartBattery(void)
152{
153    AppleSmartBattery  *me;
154    me = new AppleSmartBattery;
155
156    if (me && !me->init()) {
157        me->release();
158        return NULL;
159    }
160
161    return me;
162}
163
164/******************************************************************************
165 * AppleSmartBattery::init
166 *
167 ******************************************************************************/
168
169bool AppleSmartBattery::init(void)
170{
171    if (!super::init()) {
172        return false;
173    }
174
175    fProvider = NULL;
176    fWorkLoop = NULL;
177
178    return true;
179}
180
181
182/******************************************************************************
183 * AppleSmartBattery::start
184 *
185 ******************************************************************************/
186
187bool AppleSmartBattery::start(IOService *provider)
188{
189    IORegistryEntry *p = NULL;
190
191    BattLog("AppleSmartBattery loading...\n");
192
193    fProvider = OSDynamicCast(AppleSmartBatteryManager, provider);
194
195    if (!fProvider || !super::start(provider)) {
196        return false;
197    }
198
199    fPollingNow             = false;
200    fCancelPolling          = false;
201    fRetryAttempts          = 0;
202    fPermanentFailure       = false;
203    fFullyDischarged        = false;
204    fFullyCharged           = false;
205    fBatteryPresent         = -1;
206    fACConnected            = -1;
207    fAvgCurrent             = 0;
208    fInflowDisabled         = false;
209    fRebootPolling          = false;
210    fCellVoltages           = NULL;
211    fSystemSleeping         = false;
212    fPowerServiceToAck      = NULL;
213
214    fIncompleteReadRetries = kIncompleteReadRetryMax;
215
216    initializeCommands();
217
218    // Make sure that we read battery state at least 5 times at 30 second intervals
219    // after system boot.
220    fInitialPollCountdown = kInitialPollCountdown;
221
222    fWorkLoop = getWorkLoop();
223
224    fBatteryReadAllTimer = IOTimerEventSource::timerEventSource(this,
225                    OSMemberFunctionCast(IOTimerEventSource::Action,
226                    this, &AppleSmartBattery::incompleteReadTimeOut));
227
228    if (!fWorkLoop
229      || (kIOReturnSuccess != fWorkLoop->addEventSource(fBatteryReadAllTimer)))
230    {
231        return false;
232    }
233
234    // Find an object of class IOACPIPlatformDevice in my parent's
235    // IORegistry service plane ancsetry.
236    fACPIProvider = NULL;
237    p = this;
238    while (p) {
239        p = p->getParentEntry(gIOServicePlane);
240        if (OSDynamicCast(IOACPIPlatformDevice, p)) {
241            fACPIProvider = (IOACPIPlatformDevice *)p;
242            break;
243        }
244    }
245
246
247    // Publish the intended period in seconds that our "time remaining"
248    // estimate is wildly inaccurate after wake from sleep.
249    setProperty(kIOPMPSInvalidWakeSecondsKey, kSecondsUntilValidOnWake, 32);
250
251    // Publish the necessary time period (in seconds) that a battery
252    // calibrating tool must wait to allow the battery to settle after
253    // charge and after discharge.
254    setProperty(kIOPMPSPostChargeWaitSecondsKey, kPostChargeWaitSeconds, 32);
255    setProperty(kIOPMPSPostDishargeWaitSecondsKey, kPostDischargeWaitSeconds, 32);
256
257
258    // zero out battery state with argument (do_update == true)
259    clearBatteryState(false);
260
261    BattLog("AppleSmartBattery::start(). Initiating a full poll.\n");
262
263    // Kick off the 30 second timer and do an initial poll
264    pollBatteryState(kBoot);
265
266    return true;
267}
268
269/******************************************************************************
270 * AppleSmartBattery::initializeCommands
271 *
272 ******************************************************************************/
273void AppleSmartBattery::initializeCommands(void)
274{
275    CommandStruct local_cmd[] =
276    {
277        {kTransactionRestart,       0, 0, 0, NULL,                              kBoot | kFull | kUserVis},
278        {kMStateContCmd,            kMgr,  kWord, 0, NULL,                      kBoot | kFull | kUserVis},
279        {kMStateCmd,                kMgr,  kWord, 0, NULL,                      kBoot | kFull | kUserVis},
280        {kBBatteryStatusCmd,        kBatt, kWord, 0, NULL,                      kBoot | kFull | kUserVis},
281        {kBExtendedPFStatusCmd,     kBatt, kWriteWord, 0, NULL,                 kBoot | kFull},
282        {kStage2 | kBExtendedPFStatusCmd, kBatt, kWord, 0, _PFStatusSym,        kBoot | kFull},
283        {kBExtendedOperationStatusCmd, kBatt, kWriteWord, 0, NULL,              kBoot | kFull},
284        {kStage2 | kBExtendedOperationStatusCmd, kBatt, kWord, 0, _OpStatusSym, kBoot | kFull},
285        {kBManufactureNameCmd,      kBatt, kBlock, 0, manufacturerKey,          kBoot},
286        {kBManufactureDataCmd,      kBatt, kBlockData, 0, _ManufacturerDataSym, kBoot},
287        {kBManufacturerInfoCmd,     kBatt, kBlock, 0, NULL,                     kBoot},
288        {kBDeviceNameCmd,           kBatt, kBlock, 0, _DeviceNameSym,           kBoot},
289        {kBAppleHardwareSerialCmd,  kBatt, kBlock, 0, _HardwareSerialSym,       kBoot},
290        {kBPackReserveCmd,          kBatt, kWord, 0, _PackReserveSym,           kBoot},
291        {kBDesignCycleCount9CCmd,   kBatt, kWord, 0, _DesignCycleCount9CSym,    kBoot},
292        {kBManufactureDateCmd,      kBatt, kWord, 0, _ManfDateSym,              kBoot},
293        {kBSerialNumberCmd,         kBatt, kWord, 0, _SerialNumberSym,          kBoot},
294        {kBDesignCapacityCmd,       kBatt, kWord, 0, _DesignCapacitySym,        kBoot},
295        {kBVoltageCmd,              kBatt, kWord, 0, voltageKey,                kBoot | kFull},
296        {kBMaxErrorCmd,             kBatt, kWord, 0, _MaxErrSym,                kBoot | kFull},
297        {kBCycleCountCmd,           kBatt, kWord, 0, cycleCountKey,             kBoot | kFull},
298        {kBRunTimeToEmptyCmd,       kBatt, kWord, 0, _InstantTimeToEmptySym,    kBoot | kFull},
299        {kBTemperatureCmd,          kBatt, kWord, 0, _TemperatureSym,           kBoot | kFull},
300        {kBReadCellVoltage1Cmd,     kBatt, kWord, 0, NULL,                      kBoot | kFull},
301        {kBReadCellVoltage2Cmd,     kBatt, kWord, 0, NULL,                      kBoot | kFull},
302        {kBReadCellVoltage3Cmd,     kBatt, kWord, 0, NULL,                      kBoot | kFull},
303        {kBReadCellVoltage4Cmd,     kBatt, kWord, 0, NULL,                      kBoot | kFull},
304        {kBCurrentCmd,              kBatt, kWord, 0, NULL,                      kBoot | kFull | kUserVis},
305        {kBAverageCurrentCmd,       kBatt, kWord, 0, NULL,                      kBoot | kFull | kUserVis},
306        {kBAverageTimeToEmptyCmd,   kBatt, kWord, 0, NULL,                      kBoot | kFull | kUserVis},
307        {kBAverageTimeToFullCmd,    kBatt, kWord, 0, NULL,                      kBoot | kFull | kUserVis},
308        {kBRemainingCapacityCmd,    kBatt, kWord, 0, NULL,                      kBoot | kFull | kUserVis},
309        {kBFullChargeCapacityCmd,   kBatt, kWord, 0, maxCapacityKey,            kBoot | kFull | kUserVis},
310        {kFinishPolling,            0, 0, 0, NULL,                              kBoot | kFull | kUserVis}
311    };
312
313    cmdTable.table = NULL;
314    cmdTable.count = 0;
315
316    if ((cmdTable.table = (CommandStruct *)IOMalloc(sizeof(local_cmd)))) {
317        cmdTable.count = sizeof(local_cmd) / sizeof(CommandStruct);
318        bcopy(&local_cmd, cmdTable.table, sizeof(local_cmd));
319    }
320}
321
322/******************************************************************************
323 * AppleSmartBattery::commandForState
324 *
325 ******************************************************************************/
326CommandStruct *AppleSmartBattery::commandForState(uint32_t state)
327{
328    if (cmdTable.table) {
329        for (int i=0; i<cmdTable.count; i++) {
330            if (state == cmdTable.table[i].cmd) {
331                return &cmdTable.table[i];
332            }
333        }
334    }
335    return NULL;
336}
337
338/******************************************************************************
339 * AppleSmartBattery::initiateTransaction
340 *
341 ******************************************************************************/
342bool AppleSmartBattery::initiateTransaction(const CommandStruct *cs, bool retry)
343{
344    uint32_t cmd = cs->cmd;
345
346    if (cmd == kFinishPolling)
347    {
348        this->handlePollingFinished(true);
349    }
350    else if ((cmd == kBExtendedPFStatusCmd)
351        || (cmd == kBExtendedOperationStatusCmd))
352    {
353        // Extended commands require a 2-stage write & read.
354        writeWordAsync(cmd, cs->addr, kBManufacturerAccessCmd, cmd);
355        goto command_started;
356    }
357    else if ((cmd == (kStage2 | kBExtendedPFStatusCmd))
358        || (cmd == (kStage2 | kBExtendedOperationStatusCmd)))
359    {
360        readWordAsync(cmd, cs->addr, kBManufacturerAccessCmd);
361        goto command_started;
362    }
363    else if (cs->protocol == kWord)
364    {
365        readWordAsync(cmd, cs->addr, cmd);
366        goto command_started;
367    } else if ((cs->protocol == kBlock)
368            || (cs->protocol == kBlockData))
369    {
370        readBlockAsync(cmd, cs->addr, cmd);
371        goto command_started;
372    }
373
374    return false;
375
376command_started:
377    return true;
378}
379
380/******************************************************************************
381 * AppleSmartBattery::initiateNextTransaction
382 *
383 ******************************************************************************/
384bool AppleSmartBattery::initiateNextTransaction(uint32_t state)
385{
386    int found_current_index = 0;
387    const CommandStruct *cs = NULL;
388
389    if (!cmdTable.table) {
390        return false;
391    }
392
393    // Find index for "state" in cmd_machine
394    for (found_current_index = 0; found_current_index < cmdTable.count; found_current_index++) {
395        if (cmdTable.table[found_current_index].cmd == state) {
396            break;
397        }
398    }
399    // Find next state to read for fMachinePath
400    if (++found_current_index < cmdTable.count) {
401        for (; found_current_index<cmdTable.count; found_current_index++)
402        {
403            if (0 != (cmdTable.table[found_current_index].pathBits & fMachinePath))
404            {
405                cs = &cmdTable.table[found_current_index];
406                break;
407            }
408        }
409    }
410
411    if (cs)
412        return initiateTransaction(cs, false);
413
414    return false;
415}
416
417/******************************************************************************
418 * AppleSmartBattery::retryCurrentTransaction
419 *
420 ******************************************************************************/
421bool AppleSmartBattery::retryCurrentTransaction(uint32_t state)
422{
423    int found_current_index = 0;
424    const CommandStruct *cs = NULL;
425
426    if (!cmdTable.table) {
427        return false;
428    }
429
430    // Find index for "state" in cmd_machine
431    for (found_current_index = 0; found_current_index < cmdTable.count; found_current_index++) {
432        if (cmdTable.table[found_current_index].cmd == state) {
433            cs = &cmdTable.table[found_current_index];
434            break;
435        }
436    }
437
438    if (cs)
439        return initiateTransaction(cs, true);
440
441    return false;
442}
443
444/******************************************************************************
445 * AppleSmartBattery::logReadError
446 *
447 ******************************************************************************/
448void AppleSmartBattery::logReadError(
449    const char              *error_type,
450    uint16_t                additional_error,
451    IOSMBusTransaction      *t)
452{
453
454    if (!error_type) return;
455
456    BattLog("SmartBatteryManager Error: %s (%d)\n", error_type, additional_error);
457    if (t) {
458        BattLog("\tCorresponding transaction addr=0x%02x cmd=0x%02x status=0x%02x\n",
459                                            t->address, t->command, t->status);
460    }
461
462    return;
463}
464
465/******************************************************************************
466 * AppleSmartBattery::handleSystemSleepWake
467 *
468 * Caller must hold the gate.
469 ******************************************************************************/
470
471IOReturn AppleSmartBattery::handleSystemSleepWake(
472    IOService * powerService, bool isSystemSleep)
473{
474    IOReturn ret = kIOPMAckImplied;
475
476    if (!powerService || (fSystemSleeping == isSystemSleep))
477        return kIOPMAckImplied;
478
479    if (fPowerServiceToAck)
480    {
481        fPowerServiceToAck->release();
482        fPowerServiceToAck = 0;
483    }
484
485    fSystemSleeping = isSystemSleep;
486    if (fSystemSleeping)
487    {
488        // Stall PM until battery poll in progress is cancelled.
489        if (fPollingNow)
490        {
491            fPowerServiceToAck = powerService;
492            fPowerServiceToAck->retain();
493            if (fBatteryReadAllTimer) {
494                fBatteryReadAllTimer->cancelTimeout();
495            }
496            ret = (kBatteryReadAllTimeout * 1000);
497        }
498    }
499    else // System Wake
500    {
501        fPowerServiceToAck = powerService;
502        fPowerServiceToAck->retain();
503        pollBatteryState(kFull);
504
505        if (fPollingNow)
506        {
507            // Transaction started, wait for completion.
508            ret = (kBatteryReadAllTimeout * 1000);
509        }
510        else if (fPowerServiceToAck)
511        {
512            fPowerServiceToAck->release();
513            fPowerServiceToAck = 0;
514        }
515    }
516
517    BattLog("SmartBattery: handleSystemSleepWake(%d) = %u\n",
518        isSystemSleep, (uint32_t) ret);
519    return ret;
520}
521
522/******************************************************************************
523 * AppleSmartBattery::acknowledgeSystemSleepWake
524 *
525 * Caller must hold the gate.
526 ******************************************************************************/
527
528void AppleSmartBattery::acknowledgeSystemSleepWake(void)
529{
530    if (fPowerServiceToAck)
531    {
532        fPowerServiceToAck->acknowledgeSetPowerState();
533        fPowerServiceToAck->release();
534        fPowerServiceToAck = 0;
535    }
536}
537
538/******************************************************************************
539 * AppleSmartBattery::pollBatteryState
540 *
541 * Asynchronously kicks off the register poll.
542 ******************************************************************************/
543
544#define kMinimumPollingFrequencyMS      1000
545
546bool AppleSmartBattery::pollBatteryState(int type)
547{
548    /* Don't perform any SMBus activity if a AppleSmartBatteryManagerUserClient
549       has grabbed exclusive access
550     */
551    if (fStalledByUserClient) {
552        BattLog("AppleSmartBattery::pollBatteryState was stalled by an exclusive user client.\n");
553        return false;
554    }
555
556    /*  kUseLastPath    = 0,
557     *  kBoot           = 1,
558     *  kFull           = 2,
559     *  kUserVis        = 4
560     */
561
562    if (fPollingNow && (fMachinePath <= type)) {
563        /* We're already in the middle of a poll for a superset of
564         * the requested battery data.
565         */
566         BattLog("AppleSmartBattery::pollBatteryState already polling (%d <= %d)\n", fMachinePath, type);
567        return true;
568    }
569
570    if (type != kUseLastPath) {
571        fMachinePath = type;
572    }
573
574    if (fInitialPollCountdown > 0) {
575        // We're going out of our way to make sure that we get a successfull
576        // initial poll at boot. Upgrade all early boot polls to kBoot.
577        fMachinePath = kBoot;
578    }
579
580    if (!fPollingNow)
581    {
582        /* Start the battery polling state machine (resetting it if it's already in progress) */
583        return transactionCompletion((void *)kTransactionRestart, NULL);
584    } else {
585        /* Outstanding transaction in process; flag it to restart polling from
586           scratch when this flag is noticed.
587         */
588        fRebootPolling = true;
589        return true;
590    }
591}
592
593void AppleSmartBattery::handleBatteryInserted(void)
594{
595    clearBatteryState(false);
596    pollBatteryState(kBoot);
597    return;
598}
599
600void AppleSmartBattery::handleBatteryRemoved(void)
601{
602    if (fPollingNow) {
603        fCancelPolling = true;
604        if (fBatteryReadAllTimer) {
605            fBatteryReadAllTimer->cancelTimeout();
606        }
607    }
608
609    clearBatteryState(true);
610    acknowledgeSystemSleepWake();
611    return;
612}
613
614void AppleSmartBattery::handleInflowDisabled(bool inflow_state)
615{
616    fInflowDisabled = inflow_state;
617    // And kick off a re-poll using this new information
618    pollBatteryState(kFull);
619
620    return;
621}
622
623void AppleSmartBattery::handleChargeInhibited(bool charge_state)
624{
625    fChargeInhibited = charge_state;
626    // And kick off a re-poll using this new information
627    pollBatteryState(kFull);
628}
629
630// One "wait interval" is 100ms
631#define kWaitExclusiveIntervals 30
632
633void AppleSmartBattery::handleExclusiveAccess(bool exclusive)
634{
635    /* Write exclusive access bit to SMC via ACPI registers
636     *
637     * #define ACPIIO_SMB_MODE       ACPIIO_SMB_BASE + 0x29
638     * Mode bit masks: 0x01 - OS requests exclusive access to battery
639     *                 0x10 - SMC acknowledges OS exclusive access to battery
640     *                            (resets to non-exclusive (0) on warm restart
641     *                              or leaving S0 or timeout)
642     */
643
644    IOACPIAddress       ecBehaviorsAddress;
645    IOReturn            ret = kIOReturnSuccess;
646    UInt64              value64 = 0;
647    int                 waitCount = 0;
648
649    // Shut off SMC hardware communications with the batteries
650    do {
651
652        if (!fACPIProvider)
653            break;
654
655        /* Read BYTE */
656        // Register address is 0x29 + SMB base 0x20
657        ecBehaviorsAddress.addr64 = 0x20 + 0x29;
658        ret = fACPIProvider->readAddressSpace(&value64,
659                        kIOACPIAddressSpaceIDEmbeddedController,
660                        ecBehaviorsAddress, 8, 0, 0);
661        if (kIOReturnSuccess != ret) {
662            break;
663        }
664
665        /* Modify - set 0x01 to indicate the SMC should not communicate with battery*/
666        if (exclusive) {
667            value64 |= 1;
668        } else {
669            // Zero'ing out bit 0x01
670            value64 &= ~1;
671        }
672
673        /* Write BYTE */
674        ret = fACPIProvider->writeAddressSpace(value64,
675                        kIOACPIAddressSpaceIDEmbeddedController,
676                        ecBehaviorsAddress, 8, 0, 0);
677        if (kIOReturnSuccess != ret) {
678            break;
679        }
680
681        // Wait up to 3 seconds for the SMC to set/clear bit 0x10
682        // As-implemented, this waits at least 100 msec before proceeding.
683        // That is OK - this is a very infrequent code path.
684
685        waitCount = 0;
686        value64 = 0;
687        ret = kIOReturnSuccess;
688        while ((waitCount < kWaitExclusiveIntervals)
689            && (kIOReturnSuccess == ret))
690        {
691            waitCount++;
692            IOSleep(100);
693
694            ret = fACPIProvider->readAddressSpace(&value64,
695                            kIOACPIAddressSpaceIDEmbeddedController,
696                            ecBehaviorsAddress, 8, 0, 0);
697
698            if (exclusive) {
699                // wait for 0x10 bit to set
700                if ((value64 & 0x10) == 0x10)
701                    break;
702            } else {
703                // wait for 0x10 bit to clear
704                if ((value64 & 0x10) == 0)
705                    break;
706            }
707        }
708    } while (0);
709
710
711    if (exclusive)
712    {
713        // Communications with battery have been shutdown for
714        // exclusive access by the user client.
715        setProperty("BatteryUpdatesBlockedExclusiveAccess", true);
716        fStalledByUserClient = true;
717
718    } else {
719        // Exclusive access disabled! restart polling
720        removeProperty("BatteryUpdatesBlockedExclusiveAccess");
721        fStalledByUserClient = false;
722
723        // Restore battery state
724        // Do a complete battery poll
725        pollBatteryState(kFull);
726    }
727}
728
729/******************************************************************************
730 * incompleteReadTimeOut
731 *
732 * The complete battery read has not completed in the allowed timeframe.
733 * We assume this is for several reasons:
734 *    - The EC has dropped an SMBus packet (probably recoverable)
735 *    - The EC has stalled an SMBus request; IOSMBusController is hung (probably not recoverable)
736 *
737 * Start the battery read over from scratch.
738 *****************************************************************************/
739
740void AppleSmartBattery::incompleteReadTimeOut(void)
741{
742    logReadError(kErrorOverallTimeoutExpired, 0, NULL);
743
744    /* Don't launch infinite re-tries if the system isn't completing my transactions
745     *  (and thus probably leaking a lot of memory every time.
746     *  Quit after kIncompleteReadRetryMax
747     */
748    if (0 < fIncompleteReadRetries)
749    {
750        fIncompleteReadRetries--;
751        pollBatteryState(kUseLastPath);
752    }
753}
754
755bool AppleSmartBattery::transactionCompletion_shouldAbortTransactions(IOSMBusTransaction *transaction)
756{
757    /* Stop battery work when system is going to sleep.
758     */
759    if (fSystemSleeping)
760    {
761        return true;
762    }
763
764    /* If a user client has exclusive access to the SMBus,
765     * we'll exit immediately.
766     */
767    if (fStalledByUserClient)
768    {
769        return true;
770    }
771
772    /* Do we need to abort an ongoing polling session?
773     Example: If a battery has just been removed in the midst of our polling, we
774     need to abort the remainder of our scheduled SMBus reads.
775
776     We do not abort newly started polling sessions where (NULL == transaction).
777     */
778    if (fCancelPolling)
779    {
780        fCancelPolling = false;
781        if (transaction)
782        {
783            return true;
784        }
785    }
786    return false;
787}
788
789uint32_t AppleSmartBattery::transactionCompletion_requiresRetryGetMicroSec(IOSMBusTransaction *transaction)
790{
791    IOSMBusStatus       transaction_status = kIOSMBusStatusPECError;
792    bool                transaction_needs_retry = false;
793
794    if (transaction)
795        transaction_status = transaction->status;
796    else
797        return 0;
798
799    /******************************************************************************************
800     ******************************************************************************************/
801
802    /* If the last transaction wasn't successful at the SMBus level, retry.
803     */
804    if (STATUS_ERROR_NEEDS_RETRY(transaction_status))
805    {
806        transaction_needs_retry = true;
807    } else if (STATUS_ERROR_NON_RECOVERABLE(transaction_status))
808    {
809        transaction_needs_retry = false;
810        logReadError(kErrorNonRecoverableStatus, transaction_status, transaction);
811        goto exit;
812    }
813
814    /******************************************************************************************
815     ******************************************************************************************/
816
817    if (kIOSMBusStatusOK == transaction_status)
818    {
819        if (0 != fRetryAttempts) {
820            BattLog("SmartBattery: retry %d succeeded!\n", fRetryAttempts);
821
822            fRetryAttempts = 0;
823            transaction_needs_retry = false;    /* potentially overridden below */
824        }
825
826        /* Check for absurd return value for RemainingCapacity or FullChargeCapacity.
827         If the returned value is zero, re-read until it's non-zero (or until we
828         try too many times).
829
830         (FullChargeCapacity = 0) is NOT a valid state
831         (DesignCapacity = 0) is NOT a valid state
832         (RemainingCapacity = 0) is a valid state
833         (RemainingCapacity = 0) && !fFullyDischarged is NOT a valid state
834         */
835        if (((kBFullChargeCapacityCmd == transaction->command)
836             || (kBDesignCapacityCmd == transaction->command)
837             || ((kBRemainingCapacityCmd == transaction->command)
838                 && !fFullyDischarged))
839           && ((transaction->receiveData[1] == 0)
840               && (transaction->receiveData[0] == 0)))
841        {
842            transaction_needs_retry = true;
843        }
844    }
845
846    /* Too many retries already?
847     */
848    if (transaction_needs_retry && (kRetryAttempts == fRetryAttempts))
849    {
850        // Too many consecutive failures to read this entry. Give up, and
851        // go on to attempt a read on the next element in the state machine.
852
853        BattLog("SmartBattery: Giving up on (0x%02x, 0x%02x) after %d retries.\n",
854                transaction->address, transaction->command, fRetryAttempts);
855
856        logReadError(kErrorRetryAttemptsExceeded, transaction_status, transaction);
857
858        fRetryAttempts = 0;
859
860        transaction_needs_retry = false;
861
862        // After too many retries, unblock PM state machine in case it is
863        // waiting for the first battery poll after wake to complete,
864        // avoiding a setPowerState timeout.
865        acknowledgeSystemSleepWake();
866
867        goto exit;
868    }
869
870exit:
871    if (transaction_needs_retry)
872    {
873        fRetryAttempts++;
874        return microSecDelayTable[fRetryAttempts];
875    } else {
876        return 0;
877    }
878}
879
880void AppleSmartBattery::handlePollingFinished(bool visitedEntirePath)
881{
882    if (fBatteryReadAllTimer) {
883        fBatteryReadAllTimer->cancelTimeout();
884    }
885
886    const char *reportPathFinishedKey;
887    if (kBoot == fMachinePath) {
888        reportPathFinishedKey = kBootPathKey;
889    } else if (kFull == fMachinePath) {
890        reportPathFinishedKey = kFullPathKey;
891    } else if (kUserVis == fMachinePath) {
892        reportPathFinishedKey = kUserVisPathKey;
893    } else {
894        reportPathFinishedKey = NULL;
895    }
896
897    if (reportPathFinishedKey) {
898        clock_sec_t secs;
899        clock_usec_t microsecs;
900        clock_get_calendar_microtime(&secs, &microsecs);
901        setProperty(reportPathFinishedKey, secs, 32);
902    }
903
904    if (visitedEntirePath) {
905        if (fInitialPollCountdown > 0) {
906            fInitialPollCountdown--;
907        }
908
909        rebuildLegacyIOBatteryInfo();
910        updateStatus();
911    }
912
913    fPollingNow = false;
914
915    acknowledgeSystemSleepWake();
916}
917
918
919bool AppleSmartBattery::handleSetItAndForgetIt(int state, int val16, const uint8_t *str32, uint32_t len)
920{
921    CommandStruct   *this_command = NULL;
922    const OSData    *publishData;
923    const OSSymbol  *publishSym;
924    OSNumber        *val16num = NULL;
925
926    /* Set it and forget it
927     *
928     * These commands specify an OSSymbol in their CommandStruct.
929     * We directly publish the data into these registry keys.
930     */
931    if ((this_command = commandForState(state)) && this_command->setItAndForgetItSym)
932    {
933        if (this_command->protocol == kWord) {
934            val16num = OSNumber::withNumber(val16, 16);
935            if (val16num) {
936                setPSProperty(this_command->setItAndForgetItSym, val16num);
937                val16num->release();
938            }
939            return true;
940        }
941
942        else if (this_command->protocol == kBlock) {
943            publishSym = OSSymbol::withCString((const char *)str32);
944            if (publishSym) {
945                setPSProperty(this_command->setItAndForgetItSym, (OSObject *)publishSym);
946                publishSym->release();
947                return true;
948            }
949        }
950        else if (this_command->protocol == kBlockData) {
951            publishData = OSData::withBytes((const void *)str32, len);
952            if (publishData) {
953                setPSProperty(this_command->setItAndForgetItSym, (OSObject *)publishData);
954                publishData->release();
955                return true;
956            }
957        }
958    }
959
960    return false;
961}
962
963/******************************************************************************
964 * AppleSmartBattery::transactionCompletion
965 * -> Runs in workloop context
966 *
967 ******************************************************************************/
968
969bool AppleSmartBattery::transactionCompletion(
970    void *ref,
971    IOSMBusTransaction *transaction)
972{
973    IOSMBusStatus   transaction_status = kIOSMBusStatusPECError;
974    bool            transaction_success = false;
975    int             next_state = (uintptr_t)ref;
976    uint16_t        val16 = 0;
977    uint32_t        delay_for = 0;
978    OSNumber        *num = NULL;
979
980
981    if (transaction) {
982        BattLog("transaction state = 0x%02x; status = 0x%02x; prot = 0x%02x; word = 0x%04x; %d us\n",
983                next_state, transaction->status, transaction->protocol,
984                (transaction->receiveData[1] << 8) | transaction->receiveData[0]);
985    }
986
987    // Abort?
988    if (transactionCompletion_shouldAbortTransactions(transaction)) {
989        goto abort;
990    }
991
992    // Restart?
993    if (!transaction || fRebootPolling)
994    {
995        // NULL argument for transaction means we should start the state machine from scratch.
996        transaction = NULL;
997        next_state = kTransactionRestart;
998        fRebootPolling = false;
999    }
1000
1001    if (transaction)
1002    {
1003        // Retry?
1004        delay_for = this->transactionCompletion_requiresRetryGetMicroSec(transaction);
1005        if (0 != delay_for)
1006        {
1007            // The transaction failed. We'll delay for a bit, then retry the transaction.
1008            if (delay_for < 1000) {
1009                IODelay(delay_for); // microseconds
1010            } else {
1011                IOSleep(delay_for / 1000); // milliseconds
1012            }
1013
1014            BattLog("SmartBattery: 0x%02x failed with 0x%02x; retry attempt %d of %d\n",
1015                    transaction->command, transaction_status, fRetryAttempts, kRetryAttempts);
1016
1017            // Kick off the same transaction that just failed
1018            retryCurrentTransaction(next_state);
1019            return true; // not exit/abort
1020        }
1021
1022        transaction_success = (kIOSMBusStatusOK == transaction->status);
1023        if (transaction_success) {
1024            val16 = (transaction->receiveData[1] << 8) | transaction->receiveData[0];
1025        }
1026
1027        // Is it a set it and forget it command?
1028        if (handleSetItAndForgetIt(next_state, val16, transaction->receiveData,
1029                                   transaction->receiveDataCount))
1030        {
1031            goto exit;
1032        }
1033    }
1034
1035    switch(next_state)
1036    {
1037    case kTransactionRestart:
1038
1039        fCancelPolling = false;
1040        fPollingNow = true;
1041
1042        /* Initialize battery read timeout to catch any longstanding stalls. */
1043        if (fBatteryReadAllTimer) {
1044            fBatteryReadAllTimer->cancelTimeout();
1045            fBatteryReadAllTimer->setTimeoutMS(kBatteryReadAllTimeout);
1046        }
1047        break;
1048
1049    case kMStateContCmd:
1050
1051        // Determines if AC is plugged or unplugged
1052        // Determines if AC is "charge capable"
1053        if (transaction_success)
1054        {
1055            /* If fInflowDisabled is currently set, then we acknowledge
1056             * our lack of AC power. inflow disable means the system is not drawing power from AC.
1057             * (Having inflow disabled is uncommon.)
1058             *
1059             * Even with inflow disabled, the AC bit is still true if AC
1060             * is attached. We zero the bit instead, so that it looks
1061             * more accurate in BatteryMonitor.
1062             */
1063            bool new_ac_connected = (!fInflowDisabled && (val16 & kMACPresentBit)) ? 1:0;
1064
1065            // Tell IOPMrootDomain on ac connect/disconnect
1066            IOPMrootDomain *rd = getPMRootDomain();
1067            if (rd && (new_ac_connected != fACConnected))
1068            {
1069                if (new_ac_connected) {
1070                    rd->receivePowerNotification(kIOPMSetACAdaptorConnected | kIOPMSetValue);
1071                } else {
1072                    rd->receivePowerNotification(kIOPMSetACAdaptorConnected);
1073                }
1074            }
1075
1076            fACConnected = new_ac_connected;
1077            setExternalConnected(fACConnected);
1078            setExternalChargeCapable((val16 & kMPowerNotGoodBit) ? false:true);
1079
1080        } else {
1081            fACConnected = false;
1082            setExternalConnected(true);
1083            setExternalChargeCapable(false);
1084        }
1085        break;
1086
1087    case kMStateCmd:
1088
1089        // Determines if battery is present
1090        // Determines if battery is charging
1091        if (transaction_success)
1092        {
1093            fBatteryPresent = (val16 & kMPresentBatt_A_Bit) ? true : false;
1094
1095            setBatteryInstalled(fBatteryPresent);
1096
1097            // If fChargeInhibit is currently set, then we acknowledge
1098            // our lack of charging and force the "isCharging" bit to false.
1099            //
1100            // charge inhibit means the battery will not charge, even if
1101            // AC is attached.
1102            // Without marking this lack of charging here, it can take
1103            // up to 30 seconds for the charge disable to be reflected in
1104            // the UI.
1105
1106            setIsCharging((!fChargeInhibited && (val16 & kMChargingBatt_A_Bit)) ? true:false);
1107        } else {
1108            fBatteryPresent = false;
1109            setBatteryInstalled(false);
1110            setIsCharging(false);
1111        }
1112
1113        break;
1114
1115    case kBBatteryStatusCmd:
1116
1117        if (!transaction_success)
1118        {
1119            fFullyCharged = false;
1120            fFullyDischarged = false;
1121        } else {
1122
1123            if (val16 & kBFullyChargedStatusBit) {
1124                fFullyCharged = true;
1125            } else {
1126                fFullyCharged = false;
1127            }
1128
1129            if (val16 & kBFullyDischargedStatusBit)
1130            {
1131                if (!fFullyDischarged) {
1132                    fFullyDischarged = true;
1133
1134                    // Immediately cancel AC Inflow disable
1135                    fProvider->handleFullDischarge();
1136                }
1137            } else {
1138                fFullyDischarged = false;
1139            }
1140
1141            /* Detect battery permanent failure
1142             * Permanent battery failure is marked by
1143             * (TerminateDischarge & TerminateCharge) bits being set simultaneously.
1144             */
1145            if ((val16
1146                & (kBTerminateDischargeAlarmBit | kBTerminateChargeAlarmBit))
1147                == (kBTerminateDischargeAlarmBit | kBTerminateChargeAlarmBit))
1148            {
1149                logReadError(kErrorPermanentFailure, 0, transaction);
1150                setErrorCondition((OSSymbol *)_PermanentFailureSym);
1151
1152                fPermanentFailure = true;
1153
1154                /* We want to display the battery as present & completely discharged, not charging */
1155                fBatteryPresent = true;
1156                setBatteryInstalled(true);
1157                setIsCharging(false);
1158            } else {
1159                fPermanentFailure = false;
1160            }
1161        }
1162
1163        setFullyCharged(fFullyCharged);
1164
1165        /* If the battery is present, we continue with our state machine
1166           and read battery state below.
1167           Otherwise, if the battery is not present, we zero out all
1168           the settings that would have been set in a connected battery.
1169        */
1170        if (!fBatteryPresent) {
1171            // Clean-up battery state for absent battery; do no further
1172            // battery work until messaged that another battery has
1173            // arrived.
1174
1175            // zero out battery state with argument (do_update == true)
1176            clearBatteryState(true);
1177            goto abort;
1178        }
1179
1180        break;
1181
1182    case kBRemainingCapacityCmd:
1183
1184        fRemainingCapacity = val16;
1185        setCurrentCapacity(val16);
1186
1187        if (!fPermanentFailure && (0 == fRemainingCapacity))
1188        {
1189            // fRemainingCapacity == 0 is an absurd value.
1190            // We have already retried several times, so we accept this value and move on.
1191            logReadError(kErrorZeroCapacity, kBRemainingCapacityCmd, transaction);
1192        }
1193        break;
1194
1195    /* *Instant* current */
1196    case kBCurrentCmd:
1197        if ((num = OSNumber::withNumber(val16, 16))) {
1198            setPSProperty(_InstantAmperageSym, num);
1199            num->release();
1200        }
1201        fInstantCurrent = (int)(int16_t)val16;
1202        break;
1203
1204    /* Average current */
1205    case kBAverageCurrentCmd:
1206        setAmperage((int16_t)val16);
1207        fAvgCurrent = (int16_t)val16;
1208        if (0 == fAvgCurrent) {
1209            // Battery not present, or fully charged, or general error
1210            setTimeRemaining(0);
1211        }
1212        break;
1213
1214    case kBAverageTimeToEmptyCmd:
1215
1216        setAverageTimeToEmpty(val16);
1217
1218        if (fInstantCurrent < 0) {
1219            setTimeRemaining(val16);
1220        }
1221        break;
1222
1223    case kBAverageTimeToFullCmd:
1224
1225        setAverageTimeToFull(val16);
1226
1227        if (fInstantCurrent > 0) {
1228            setTimeRemaining(val16);
1229        }
1230        break;
1231
1232    case kBReadCellVoltage4Cmd:
1233    case kBReadCellVoltage3Cmd:
1234    case kBReadCellVoltage2Cmd:
1235    case kBReadCellVoltage1Cmd:
1236
1237        if (kBReadCellVoltage1Cmd == next_state) {
1238            if (fCellVoltages)
1239            {
1240                fCellVoltages->release();
1241                fCellVoltages = NULL;
1242            }
1243            fCellVoltages = OSArray::withCapacity(4);
1244        }
1245
1246        // Executed for all 4 CellVoltage calls through here
1247        if (fCellVoltages)
1248        {
1249            num = OSNumber::withNumber(val16, 16);
1250            if (num) {
1251                fCellVoltages->setObject(num);
1252                num->release();
1253            }
1254        }
1255
1256        // Executed for CellVoltage4
1257        if (kBReadCellVoltage4Cmd == next_state)
1258        {
1259            if (fCellVoltages)
1260            {
1261                setProperty(_CellVoltageSym, fCellVoltages);
1262                fCellVoltages->release();
1263                fCellVoltages = NULL;
1264            } else {
1265                removeProperty(_CellVoltageSym);
1266            }
1267        }
1268        break;
1269
1270    case kBExtendedPFStatusCmd:
1271    case kBExtendedOperationStatusCmd:
1272        // 2 stage commands, first stage SMBus write completed.
1273        // Do nothing other than to prevent the error log in the default case.
1274        break;
1275
1276    default:
1277        BattLog("SmartBattery: Error state %x not expected\n", next_state);
1278    }
1279
1280exit:
1281    /* Kick off the next transaction */
1282    if (kFinishPolling != next_state) {
1283        this->initiateNextTransaction(next_state);
1284    }
1285    return true;
1286
1287abort:
1288    handlePollingFinished(false);
1289    return true;
1290}
1291
1292
1293void AppleSmartBattery::clearBatteryState(bool do_update)
1294{
1295    // Only clear out battery state; don't clear manager state like AC Power.
1296    // We just zero out the int and bool values, but remove the OSType values.
1297
1298    fRetryAttempts          = 0;
1299    fFullyDischarged        = false;
1300    fFullyCharged           = false;
1301    fBatteryPresent         = false;
1302    fACConnected            = -1;
1303    fAvgCurrent             = 0;
1304
1305    setBatteryInstalled(false);
1306    setIsCharging(false);
1307    setCurrentCapacity(0);
1308    setMaxCapacity(0);
1309    setTimeRemaining(0);
1310    setAmperage(0);
1311    setVoltage(0);
1312    setCycleCount(0);
1313    setAdapterInfo(0);
1314    setLocation(0);
1315
1316    properties->removeObject(manufacturerKey);
1317    removeProperty(manufacturerKey);
1318    properties->removeObject(serialKey);
1319    removeProperty(serialKey);
1320
1321    // Let rebuildLegacyIOBatteryInfo() update batteryInfoKey and detect
1322    // if any value in the dictionary has changed. Removing batteryInfoKey
1323    // from properties will always dirty the battery state and will cause
1324    // IOPMPowerSource::updateStatus() to message clients unnecessarily
1325    // when battery is not present.
1326    // properties->removeObject(batteryInfoKey);
1327
1328    removeProperty(batteryInfoKey);
1329    properties->removeObject(errorConditionKey);
1330    removeProperty(errorConditionKey);
1331    properties->removeObject(_PFStatusSym);
1332    removeProperty(_PFStatusSym);
1333
1334    rebuildLegacyIOBatteryInfo();
1335
1336    logReadError(kErrorClearBattery, 0, NULL);
1337
1338    if (do_update) {
1339        updateStatus();
1340    }
1341}
1342
1343
1344/******************************************************************************
1345 *  Package battery data in "legacy battery info" format, readable by
1346 *  any applications using the not-so-friendly IOPMCopyBatteryInfo()
1347 ******************************************************************************/
1348
1349 void AppleSmartBattery::rebuildLegacyIOBatteryInfo(void)
1350 {
1351    OSDictionary        *legacyDict = OSDictionary::withCapacity(5);
1352    uint32_t            flags = 0;
1353    OSNumber            *flags_num = NULL;
1354
1355    if (externalConnected()) flags |= kIOPMACInstalled;
1356    if (batteryInstalled()) flags |= kIOPMBatteryInstalled;
1357    if (isCharging()) flags |= kIOPMBatteryCharging;
1358
1359    flags_num = OSNumber::withNumber((unsigned long long)flags, 32);
1360    legacyDict->setObject(kIOBatteryFlagsKey, flags_num);
1361    flags_num->release();
1362
1363    legacyDict->setObject(kIOBatteryCurrentChargeKey, properties->getObject(kIOPMPSCurrentCapacityKey));
1364    legacyDict->setObject(kIOBatteryCapacityKey, properties->getObject(kIOPMPSMaxCapacityKey));
1365    legacyDict->setObject(kIOBatteryVoltageKey, properties->getObject(kIOPMPSVoltageKey));
1366    legacyDict->setObject(kIOBatteryAmperageKey, properties->getObject(kIOPMPSAmperageKey));
1367    legacyDict->setObject(kIOBatteryCycleCountKey, properties->getObject(kIOPMPSCycleCountKey));
1368
1369    setLegacyIOBatteryInfo(legacyDict);
1370
1371    legacyDict->release();
1372}
1373
1374
1375/******************************************************************************
1376 *  Power Source value accessors
1377 *  These supplement the built-in accessors in IOPMPowerSource.h.
1378 ******************************************************************************/
1379
1380#define CLASS   AppleSmartBattery
1381
1382void AppleSmartBattery::setAverageTimeToEmpty(int seconds) {
1383    OSNumber *n = OSNumber::withNumber(seconds, 32);
1384    if (n) {
1385        setPSProperty(_AvgTimeToEmptySym, n);
1386        n->release();
1387    }
1388}
1389void AppleSmartBattery::setAverageTimeToFull(int seconds) {
1390    OSNumber *n = OSNumber::withNumber(seconds, 32);
1391    if (n) {
1392        setPSProperty(_AvgTimeToFullSym, n);
1393        n->release();
1394    }
1395}
1396
1397#define IMPLEMENT_APPLESMARTBATTERY_INT_GETTER(methodName, pspropSYM, return_type) \
1398    return_type CLASS::methodName(void) {                       \
1399        OSNumber *n = OSDynamicCast(OSNumber, properties->getObject(_SerialNumberSym)); \
1400        if (n) {                                                \
1401            return n->unsigned16BitValue();                     \
1402        } else {                                                \
1403            return 0;                                           \
1404        }                                                       \
1405    }
1406IMPLEMENT_APPLESMARTBATTERY_INT_GETTER(serialNumber, _SerialNumberSym, uint16_t);
1407IMPLEMENT_APPLESMARTBATTERY_INT_GETTER(maxErr, _MaxErrSym, int);
1408IMPLEMENT_APPLESMARTBATTERY_INT_GETTER(averageTimeToEmpty, _AvgTimeToEmptySym, int);
1409IMPLEMENT_APPLESMARTBATTERY_INT_GETTER(averageTimeToFull, _AvgTimeToFullSym, int);
1410IMPLEMENT_APPLESMARTBATTERY_INT_GETTER(manufactureDate, _ManfDateSym, int);
1411
1412OSSymbol * AppleSmartBattery::deviceName(void)
1413{
1414    return OSDynamicCast(OSSymbol, properties->getObject(_DeviceNameSym));
1415}
1416
1417void AppleSmartBattery::setFullyCharged(bool charged)
1418{
1419    setPSProperty(_FullyChargedSym, (charged ? kOSBooleanTrue:kOSBooleanFalse));
1420}
1421
1422bool AppleSmartBattery::fullyCharged(void)
1423{
1424    return (kOSBooleanTrue == properties->getObject(_FullyChargedSym));
1425}
1426
1427
1428/******************************************************************************
1429 ******************************************************************************
1430 **
1431 **  Async SmartBattery read convenience functions
1432 **
1433 ******************************************************************************
1434 ******************************************************************************/
1435IOReturn AppleSmartBattery::readWordAsync(
1436    uint32_t refnum,
1437    uint8_t address,
1438    uint8_t cmd
1439) {
1440    IOReturn                ret = kIOReturnError;
1441    bzero(&fTransaction, sizeof(IOSMBusTransaction));
1442
1443    // All transactions are performed async
1444    fTransaction.protocol      = kIOSMBusProtocolReadWord;
1445    fTransaction.address       = address;
1446    fTransaction.command       = cmd;
1447
1448    ret = fProvider->performTransaction(
1449                    &fTransaction,
1450                    OSMemberFunctionCast(IOSMBusTransactionCompletion,
1451                      this, &AppleSmartBattery::transactionCompletion),
1452                    (OSObject *)this,
1453                    (void *)(uintptr_t)refnum);
1454
1455    return ret;
1456}
1457
1458IOReturn AppleSmartBattery::writeWordAsync(
1459    uint32_t refnum,
1460    uint8_t address,
1461    uint8_t cmd,
1462    uint16_t writeWord)
1463{
1464    IOReturn                ret = kIOReturnError;
1465    bzero(&fTransaction, sizeof(IOSMBusTransaction));
1466
1467    // All transactions are performed async
1468    fTransaction.protocol      = kIOSMBusProtocolWriteWord;
1469    fTransaction.address       = address;
1470    fTransaction.command       = cmd;
1471    fTransaction.sendData[0]   = writeWord & 0xFF;
1472    fTransaction.sendData[1]   = (writeWord >> 8) & 0xFF;
1473    fTransaction.sendDataCount = 2;
1474
1475    ret = fProvider->performTransaction(
1476                    &fTransaction,
1477                    OSMemberFunctionCast(IOSMBusTransactionCompletion,
1478                      this, &AppleSmartBattery::transactionCompletion),
1479                    (OSObject *)this,
1480                    (void *)((uintptr_t)refnum));
1481
1482    return ret;
1483
1484}
1485
1486IOReturn AppleSmartBattery::readBlockAsync(
1487    uint32_t refnum,
1488    uint8_t address,
1489    uint8_t cmd
1490) {
1491    IOReturn                ret = kIOReturnError;
1492    bzero(&fTransaction, sizeof(IOSMBusTransaction));
1493
1494    // All transactions are performed async
1495    fTransaction.protocol      = kIOSMBusProtocolReadBlock;
1496    fTransaction.address       = address;
1497    fTransaction.command       = cmd;
1498
1499    ret = fProvider->performTransaction(
1500                    &fTransaction,
1501                    OSMemberFunctionCast(IOSMBusTransactionCompletion,
1502                      this, &AppleSmartBattery::transactionCompletion),
1503                    (OSObject *)this,
1504                    (void *)(uintptr_t)refnum);
1505
1506    return ret;
1507}
1508
1509