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/pwr_mgt/RootDomain.h>
25
26#include "AppleSmartBatteryManager.h"
27#include "AppleSmartBattery.h"
28
29#define kMaxRetries     5
30
31static int  retryDelaysTable[kMaxRetries] =
32    { 1, 10, 100, 1000, 1000 };
33
34// Power states!
35enum {
36    kMyOnPowerState = 1
37};
38
39// Commands!
40enum {
41    kInhibitChargingCmd = 0,
42    kDisableInflowCmd
43};
44
45static IOPMPowerState myTwoStates[2] = {
46    {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
47    {1, kIOPMPowerOn, kIOPMPowerOn, kIOPMPowerOn, 0, 0, 0, 0, 0, 0, 0, 0}
48};
49
50#define super IOService
51
52OSDefineMetaClassAndStructors(AppleSmartBatteryManager, IOService)
53
54bool AppleSmartBatteryManager::start(IOService *provider)
55{
56    IOCommandGate * gate;
57    IOWorkLoop *    wl;
58
59    if(!super::start(provider)) {
60        return false;
61    }
62
63    fProvider = OSDynamicCast(IOSMBusController, provider);
64    if(!fProvider) {
65        return false;
66    }
67
68    const OSSymbol *ucClassName =
69            OSSymbol::withCStringNoCopy("AppleSmartBatteryManagerUserClient");
70    setProperty(gIOUserClientClassKey, (OSObject *) ucClassName);
71    ucClassName->release();
72
73    wl = getWorkLoop();
74    if (!wl) {
75        return false;
76    }
77
78    // Join power management so that we can get a notification early during
79    // wakeup to re-sample our battery data. We don't actually power manage
80    // any devices.
81    PMinit();
82    registerPowerDriver(this, myTwoStates, 2);
83    provider->joinPMtree(this);
84
85    fBattery = AppleSmartBattery::smartBattery();
86
87    if(!fBattery) return false;
88
89    fBattery->attach(this);
90
91    fBattery->start(this);
92
93    // Command gate for SmartBatteryManager
94    fManagerGate = IOCommandGate::commandGate(this);
95    if (!fManagerGate) {
96        return false;
97    }
98    wl->addEventSource(fManagerGate);
99
100    // Command gate for SmartBattery
101    gate = IOCommandGate::commandGate(fBattery);
102    if (!gate) {
103        return false;
104    }
105    wl->addEventSource(gate);
106    fBatteryGate = gate;      // enable messages
107
108    // Track UserClient exclusive access to smartbattery
109    fExclusiveUserClient = false;
110
111    fBattery->registerService(0);
112
113    this->registerService(0);
114
115    return true;
116}
117
118// Default polling interval is 30 seconds
119/*
120IOReturn AppleSmartBatteryManager::setPollingInterval(
121    int milliSeconds)
122{
123    // Discard any negatize or zero arguments
124    if(milliSeconds <= 0) return kIOReturnBadArgument;
125
126    if(fBattery)
127        fBattery->setPollingInterval(milliSeconds);
128
129    setProperty("PollingInterval_msec", milliSeconds, 32);
130
131    return kIOReturnSuccess;
132}
133 */
134
135/*
136 * performExternalWordTransaction
137 *
138 * Called by AppleSmartBatteryManagerUserClient
139 */
140IOReturn AppleSmartBatteryManager::performExternalTransaction(
141    void *in,
142    void *out,
143    IOByteCount inSize,
144    IOByteCount *outSize)
145{
146    uint16_t                i;
147    uint16_t                retryAttempts = 0;
148    IOSMBusTransaction      newTransaction;
149    IOReturn                transactionSuccess;
150    EXSMBUSInputStruct      *inSMBus = (EXSMBUSInputStruct *)in;
151    EXSMBUSOutputStruct     *outSMBus = (EXSMBUSOutputStruct *)out;
152
153    if (!inSMBus || !outSMBus)
154        return kIOReturnBadArgument;
155
156    /* Attempt up to 5 transactions if we get failures
157     */
158    do {
159        bzero(&newTransaction, sizeof(IOSMBusTransaction));
160
161        // Input: bus address
162        if (kSMBusAppleDoublerAddr == inSMBus->batterySelector
163            || kSMBusBatteryAddr == inSMBus->batterySelector
164            || kSMBusManagerAddr == inSMBus->batterySelector
165            || kSMBusChargerAddr == inSMBus->batterySelector)
166        {
167            newTransaction.address = inSMBus->batterySelector;
168        } else {
169            if (0 == inSMBus->batterySelector)
170            {
171                newTransaction.address = kSMBusBatteryAddr;
172            } else {
173                newTransaction.address = kSMBusManagerAddr;
174            }
175        }
176
177        // Input: command
178        newTransaction.command = inSMBus->address;
179
180        // Input: Read/Write Word/Block
181        switch (inSMBus->type) {
182            case kEXWriteWord:
183                newTransaction.protocol = kIOSMBusProtocolWriteWord;
184                newTransaction.sendDataCount = 2;
185                break;
186            case kEXReadWord:
187                newTransaction.protocol = kIOSMBusProtocolReadWord;
188                newTransaction.sendDataCount = 0;
189                break;
190            case kEXWriteBlock:
191                newTransaction.protocol = kIOSMBusProtocolWriteBlock;
192                // rdar://5433060 workaround for SMC SMBus blockCount bug
193                // For block writes, clients always increment inByteCount +1
194                // greater than the actual byte count.
195                // We decrement it here for IOSMBusController.
196                newTransaction.sendDataCount = inSMBus->inByteCount - 1;
197                break;
198            case kEXReadBlock:
199                newTransaction.protocol = kIOSMBusProtocolReadBlock;
200                newTransaction.sendDataCount = 0;
201                break;
202            case kEXWriteByte:
203                newTransaction.protocol = kIOSMBusProtocolWriteByte;
204                newTransaction.sendDataCount = 1;
205                break;
206            case kEXReadByte:
207                newTransaction.protocol = kIOSMBusProtocolReadByte;
208                newTransaction.sendDataCount = 0;
209                break;
210            case kEXSendByte:
211                newTransaction.protocol = kIOSMBusProtocolSendByte;
212                newTransaction.sendDataCount = 0;
213                break;
214            default:
215                return kIOReturnBadArgument;
216        }
217
218        // Input: copy data into transaction
219        //  only need to copy data for write operations
220        if ((kIOSMBusProtocolWriteWord == newTransaction.protocol)
221             || (kIOSMBusProtocolWriteBlock == newTransaction.protocol))
222        {
223            for(i = 0; i<MAX_SMBUS_DATA_SIZE; i++) {
224                newTransaction.sendData[i] = inSMBus->inBuf[i];
225            }
226        }
227
228
229        if (inSMBus->flags & kEXFlagRetry)
230        {
231            if (retryAttempts >= kMaxRetries) {
232                // Don't read off the end of the table...
233                retryAttempts = kMaxRetries - 1;
234            }
235
236            // If this is a retry-on-failure, spin for a few microseconds
237            IODelay( retryDelaysTable[retryAttempts] );
238        }
239
240        fManagerGate->runAction(
241                            (IOCommandGate::Action)OSMemberFunctionCast(
242                                IOCommandGate::Action, this,
243                                &AppleSmartBatteryManager::performExternalTransactionGated),
244                           (void *)&newTransaction,
245                           (void *)&transactionSuccess,
246                           NULL,
247                           NULL);
248
249
250        /* Output: status */
251        if ((kIOReturnSuccess == transactionSuccess)
252            && (kIOSMBusStatusOK == newTransaction.status))
253        {
254            outSMBus->status = kIOReturnSuccess;
255        } else {
256            switch (newTransaction.status) {
257                case kIOSMBusStatusUnknownFailure:
258                case kIOSMBusStatusDeviceAddressNotAcknowledged:
259                case kIOSMBusStatusDeviceError:
260                case kIOSMBusStatusDeviceCommandAccessDenied:
261                case kIOSMBusStatusUnknownHostError:
262                    outSMBus->status = kIOReturnNoDevice;
263                    break;
264                case kIOSMBusStatusTimeout:
265                case kIOSMBusStatusBusy:
266                    outSMBus->status = kIOReturnTimeout;
267                    break;
268                case kIOSMBusStatusHostUnsupportedProtocol:
269                    outSMBus->status = kIOReturnUnsupported;
270                    break;
271                default:
272                    outSMBus->status = kIOReturnInternalError;
273                    break;
274           }
275        }
276
277    /* Retry this transaction if we received a failure
278     */
279    } while ((inSMBus->flags & kEXFlagRetry)
280          && (outSMBus->status != kIOReturnSuccess)
281          && (++retryAttempts < kMaxRetries));
282
283
284    /* Output: read word/read block results */
285    if (((kIOSMBusProtocolReadWord == newTransaction.protocol)
286         || (kIOSMBusProtocolReadBlock == newTransaction.protocol)
287         || (kIOSMBusProtocolReadByte == newTransaction.protocol))
288        && (kIOSMBusStatusOK == newTransaction.status))
289    {
290        outSMBus->outByteCount = newTransaction.receiveDataCount;
291
292        for(i = 0; i<outSMBus->outByteCount; i++) {
293            outSMBus->outBuf[i] = newTransaction.receiveData[i];
294        }
295    }
296
297    return kIOReturnSuccess;
298}
299
300/*
301 * performExternalTransactionGated
302 *
303 * Called by AppleSmartBatteryManagerUserClient
304 */
305IOReturn AppleSmartBatteryManager::performExternalTransactionGated(
306    void                    *arg0,
307    void                    *arg1,
308    void                    *arg2 __unused,
309    void                    *arg3 __unused)
310{
311    IOSMBusTransaction      *trans = (IOSMBusTransaction *)arg0;
312    IOReturn                *return_code = (IOReturn *)arg1;
313
314    *return_code = fProvider->performTransaction(
315                        trans,          /* transaction */
316                        OSMemberFunctionCast(
317                            IOSMBusTransactionCompletion, /* completion */
318                            this, &AppleSmartBatteryManager::transactionCompletion),
319                        (OSObject *)this,       /* target */
320                        (void *)trans);         /* ref */
321
322
323    if(kIOReturnSuccess != *return_code)
324        return kIOReturnSuccess;
325
326    /* Sleep the caller's thread until the transaction completion returns */
327    fManagerGate->commandSleep(trans, THREAD_UNINT);
328
329    /* at this point our transaction is complete; return */
330
331    return kIOReturnSuccess;
332}
333
334/*
335 * performTransaction
336 *
337 * Called by smart battery children
338 */
339IOReturn AppleSmartBatteryManager::performTransaction(
340    IOSMBusTransaction * transaction,
341    IOSMBusTransactionCompletion completion,
342    OSObject * target,
343    void * reference)
344{
345    /* directly pass bus transactions back up to SMBusController */
346    return fProvider->performTransaction(transaction,
347                completion,
348                target,
349                reference);
350}
351
352/*
353 * setPowerState
354 *
355 */
356IOReturn AppleSmartBatteryManager::setPowerState(
357    unsigned long which,
358    IOService *whom)
359{
360    IOReturn ret = IOPMAckImplied;
361
362    if( fBatteryGate )
363    {
364        // We are waking from sleep - kick off a battery read to make sure
365        // our battery concept is in line with reality.
366        ret = fBatteryGate->runAction(OSMemberFunctionCast(IOCommandGate::Action,
367                           fBattery, &AppleSmartBattery::handleSystemSleepWake),
368                           (void *) this, (void *) !which, NULL, NULL);
369    }
370    return ret;
371}
372
373
374/*
375 * message
376 *
377 */
378IOReturn AppleSmartBatteryManager::message(
379    UInt32 type,
380    IOService *provider,
381    void *argument )
382{
383    IOSMBusAlarmMessage     *alarm = (IOSMBusAlarmMessage *)argument;
384    static uint16_t         last_data = 0;
385    uint16_t                changed_bits = 0;
386    uint16_t                data = 0;
387
388    /* On SMBus alarms from the System Battery Manager, trigger a new
389       poll of battery state.   */
390
391    if(!alarm) return kIOReturnSuccess;
392
393    if( (kIOMessageSMBusAlarm == type)
394        && (kSMBusManagerAddr == alarm->fromAddress)
395        && fBatteryGate)
396    {
397        data = (uint16_t)(alarm->data[0] | (alarm->data[1] << 8));
398        changed_bits = data ^ last_data;
399        last_data = data;
400
401        if(changed_bits & kMPresentBatt_A_Bit)
402        {
403            if(data & kMPresentBatt_A_Bit) {
404                // Battery inserted
405                fBatteryGate->runAction(OSMemberFunctionCast(IOCommandGate::Action,
406                               fBattery, &AppleSmartBattery::handleBatteryInserted),
407                               NULL, NULL, NULL, NULL);
408            } else {
409                // Battery removed
410                fBatteryGate->runAction(OSMemberFunctionCast(IOCommandGate::Action,
411                               fBattery, &AppleSmartBattery::handleBatteryRemoved),
412                               NULL, NULL, NULL, NULL);
413            }
414        } else {
415            // Just an alarm; re-read battery state.
416            fBatteryGate->runAction(OSMemberFunctionCast(IOCommandGate::Action,
417                               fBattery, &AppleSmartBattery::pollBatteryState),
418                               NULL, NULL, NULL, NULL);
419        }
420    }
421
422    return kIOReturnSuccess;
423}
424
425/*
426 * inhibitCharging
427 *
428 * Called by AppleSmartBatteryManagerUserClient
429 */
430IOReturn AppleSmartBatteryManager::inhibitCharging(int level)
431{
432    IOReturn        ret = kIOReturnSuccess;
433
434    if(!fManagerGate) return kIOReturnInternalError;
435
436    this->setProperty("Charging Inhibited", level ? true : false);
437
438    fManagerGate->runAction(OSMemberFunctionCast(IOCommandGate::Action,
439                       this, &AppleSmartBatteryManager::gatedSendCommand),
440                       (void *)kInhibitChargingCmd, (void *)(uintptr_t)level,
441                       (void *)&ret, NULL);
442
443    return ret;
444}
445
446/*
447 * disableInflow
448 *
449 * Called by AppleSmartBatteryManagerUserClient
450 */
451IOReturn AppleSmartBatteryManager::disableInflow(int level)
452{
453    IOReturn        ret = kIOReturnSuccess;
454
455    if(!fManagerGate) return kIOReturnInternalError;
456
457    this->setProperty("Inflow Disabled", level ? true : false);
458
459    fManagerGate->runAction(OSMemberFunctionCast(IOCommandGate::Action,
460                       this, &AppleSmartBatteryManager::gatedSendCommand),
461                       (void *)kDisableInflowCmd, (void *)(uintptr_t)level,
462                       (void *)&ret, NULL);
463
464    return ret;
465}
466
467/*
468 * handleFullDischarge
469 *
470 * Called by AppleSmartBattery
471 * If inflow is disabled, by the user client, we re-enable it and send a message
472 * via IOPMrootDomain indicating that inflow has been re-enabled.
473 */
474void AppleSmartBatteryManager::handleFullDischarge(void)
475{
476    IOPMrootDomain      *root_domain = getPMRootDomain();
477    IOReturn            ret;
478    void *              messageArgument = NULL;
479
480    if( getProperty("Inflow Disabled") )
481    {
482        messageArgument = (void *)kInflowForciblyEnabledBit;
483
484        /*
485         * Send disable inflow command to SB Manager
486         */
487        fManagerGate->runAction(OSMemberFunctionCast(IOCommandGate::Action,
488                           this, &AppleSmartBatteryManager::gatedSendCommand),
489                           (void *)kDisableInflowCmd, (void *)0, /* OFF */
490                           (void *)&ret, NULL);
491    }
492
493
494    /*
495     * Message user space clients that battery is fully discharged.
496     * If appropriate, set kInflowForciblyEnabledBit
497     */
498    if(root_domain) {
499        root_domain->messageClients(
500                kIOPMMessageInternalBatteryFullyDischarged,
501                messageArgument );
502    }
503}
504
505/*
506 * requestExclusiveSMBusAcess
507 *
508 * Called by AppleSmartBatteryManagerUserClient
509 * When exclusive SMBus access is requested, we'll hold off on any SmartBattery bus reads.
510 * We do not do any periodic SMBus reads
511 */
512bool AppleSmartBatteryManager::requestExclusiveSMBusAccess(
513    bool request)
514{
515    if( request && fExclusiveUserClient) {
516        /* Oops - a second client reaching for exclusive access.
517         * This shouldn't happen.
518         */
519        return false;
520    }
521
522    fExclusiveUserClient = request;
523
524    /* Signal our driver, and the SMC firmware to either:
525        - stop communicating with the battery
526        - resume communications
527     */
528    fBatteryGate->runAction(
529                    OSMemberFunctionCast(
530                        IOCommandGate::Action, this,
531                        &AppleSmartBattery::handleExclusiveAccess),
532                    (void *)request, NULL, NULL, NULL);
533
534    return true;
535}
536
537bool AppleSmartBatteryManager::hasExclusiveClient(void) {
538    return fExclusiveUserClient;
539}
540
541bool AppleSmartBatteryManager::requestPoll(int type) {
542    IOReturn ret;
543
544    BattLog("AppleSmartBatteryManager requests a poll (type=%d).\n", type);
545    ret = fBatteryGate->runAction(OSMemberFunctionCast(IOCommandGate::Action,
546                           fBattery, &AppleSmartBattery::pollBatteryState),
547                           (void *)(uintptr_t) type, NULL, NULL, NULL);
548
549    return ret;
550}
551
552void AppleSmartBatteryManager::gatedSendCommand(
553    int cmd,
554    int level,
555    IOReturn *ret_code)
556{
557    if (fExclusiveUserClient) {
558        *ret_code = kIOReturnExclusiveAccess;
559        return;
560    }
561
562    *ret_code = kIOReturnError;
563    bzero(&fTransaction, sizeof(IOSMBusTransaction));
564
565    fTransaction.protocol      = kIOSMBusProtocolWriteWord;
566    fTransaction.sendDataCount = 2;
567
568    /*
569     * kDisableInflowCmd
570     */
571    if( kDisableInflowCmd == cmd)
572    {
573        fTransaction.address            = kSMBusManagerAddr;
574        fTransaction.command            = kMStateContCmd;
575        if( 0 != level ) {
576            fTransaction.sendData[0]    = kMCalibrateBit;
577        } else {
578            fTransaction.sendData[0]    = 0x0;
579        }
580    }
581
582    /*
583     * kInhibitChargingCmd
584     */
585    if( kInhibitChargingCmd == cmd)
586    {
587        fTransaction.address            = kSMBusChargerAddr;
588        fTransaction.command            = kBChargingCurrentCmd;
589        if( 0 != level ) {
590            // non-zero level for chargeinhibit means turn off charging.
591            // We set charge current to 0.
592            fTransaction.sendData[0]    = 0x0;
593        } else {
594            // We re-enable charging by setting it to 6000, a signifcantly
595            // large number (> 4500 per Chris C) to ensure charging will resume.
596            fTransaction.sendData[0]    = 0x70;
597            fTransaction.sendData[1]    = 0x17;
598        }
599    }
600
601    *ret_code = fProvider->performTransaction(
602                    &fTransaction,
603                    OSMemberFunctionCast( IOSMBusTransactionCompletion,
604                      this, &AppleSmartBatteryManager::transactionCompletion),
605                    (OSObject *)this);
606
607    return;
608}
609
610bool AppleSmartBatteryManager::transactionCompletion(
611    void *ref,
612    IOSMBusTransaction *transaction)
613{
614    /*
615     * Transactions may be initiated by SmartBatteryManager or
616     * by external user client; we react differently to each.
617     */
618
619    if (ref == this) {
620        /*
621         * transaction was initiated by AppleSmartBatteryManager
622         */
623
624        if( kIOSMBusStatusOK == transaction->status )
625        {
626            // If we just completed sending the DisableInflow command, give a
627            // callout to our attached battery and let them know whether its
628            // enabled or disabled.
629            if( (kMStateContCmd == transaction->command)
630                && (kSMBusManagerAddr == transaction->address) )
631            {
632                fBattery->handleInflowDisabled(
633                            transaction->sendData[0] ? true : false);
634            }
635
636            // If we just completed sending the ChargeInhibit command, give a
637            // callout to our attached battery and let them know whether its
638            // enabled or disabled.
639            if( (kBChargingCurrentCmd == transaction->command)
640                && (kSMBusChargerAddr == transaction->address) )
641            {
642                fBattery->handleChargeInhibited(
643                            (transaction->sendData[0] | transaction->sendData[1])
644                            ? false : true);
645            }
646
647        } else {
648            BattLog("AppleSmartBatteryManager::transactionCompletion:"\
649                                        "ERROR 0x%08x!\n", transaction->status);
650        }
651    } else {
652        /*
653         * transaction was initiated by user client
654         */
655
656        BattLog("completion: commandWaking: 0x%08x\n", transaction);
657
658        // If it's an external transaction completion; stash the result
659        // in (ref) data structure
660        fManagerGate->commandWakeup( transaction, /* wake all */ false);
661
662    }
663
664    return false;
665}
666
667void BattLog(const char *fmt, ...)
668{
669#if 0
670    va_list     listp;
671    char        buf[128];
672
673    va_start(listp, fmt);
674    vsnprintf(buf, sizeof(buf), fmt, listp);
675    va_end(listp);
676
677    IOLog("BattLog: %s", buf);
678
679    return;
680#endif
681}
682
683