AppleSmartBattery.cpp [plain text]
#include <IOKit/IOService.h>
#include <IOKit/IOWorkLoop.h>
#include <IOKit/IOTimerEventSource.h>
#include <IOKit/pwr_mgt/RootDomain.h>
#include <IOKit/pwr_mgt/IOPMPrivate.h>
#include <libkern/c++/OSObject.h>
#include "AppleSmartBatteryManager.h"
#include "AppleSmartBattery.h"
enum {
kExistingBatteryPath = 1,
kNewBatteryPath = 2
};
enum {
kRetryAttempts = 5,
kInitialPollCountdown = 5,
kIncompleteReadRetryMax = 10
};
enum {
kSecondsUntilValidOnWake = 30,
kPostChargeWaitSeconds = 120,
kPostDischargeWaitSeconds = 120
};
enum {
kDefaultPollInterval = 0,
kQuickPollInterval = 1
};
#define kWriteIndicatorBit 0x8000
#define kTransactionRestart 0xFFFFFFFF
#define kErrorRetryAttemptsExceeded "Read Retry Attempts Exceeded"
#define kErrorOverallTimeoutExpired "Overall Read Timeout Expired"
#define kErrorZeroCapacity "Capacity Read Zero"
#define kErrorPermanentFailure "Permanent Battery Failure"
#define kErrorNonRecoverableStatus "Non-recoverable status failure"
#define kErrorClearBattery "Clear Battery"
static uint32_t milliSecPollingTable[2] =
{
30000, 1000 };
static const uint32_t kBatteryReadAllTimeout = 10000;
static const uint32_t microSecDelayTable[kRetryAttempts] =
{ 10, 100, 1000, 10000, 250000 };
#define STATUS_ERROR_NEEDS_RETRY(err) \
((kIOSMBusStatusDeviceAddressNotAcknowledged == err) \
|| (kIOSMBusStatusDeviceCommandAccessDenied == err) \
|| (kIOSMBusStatusDeviceAccessDenied == err) \
|| (kIOSMBusStatusUnknownHostError == err) \
|| (kIOSMBusStatusUnknownFailure == err) \
|| (kIOSMBusStatusDeviceError == err) \
|| (kIOSMBusStatusTimeout == err) \
|| (kIOSMBusStatusBusy == err))
#define STATUS_ERROR_NON_RECOVERABLE(err) \
((kIOSMBusStatusHostUnsupportedProtocol == err) \
|| (kIOSMBusStatusPECError == err))
static const OSSymbol *_MaxErrSym =
OSSymbol::withCString(kIOPMPSMaxErrKey);
static const OSSymbol *_DeviceNameSym =
OSSymbol::withCString(kIOPMDeviceNameKey);
static const OSSymbol *_FullyChargedSym =
OSSymbol::withCString(kIOPMFullyChargedKey);
static const OSSymbol *_AvgTimeToEmptySym =
OSSymbol::withCString("AvgTimeToEmpty");
static const OSSymbol *_InstantTimeToEmptySym =
OSSymbol::withCString("InstantTimeToEmpty");
static const OSSymbol *_InstantAmperageSym =
OSSymbol::withCString("InstantAmperage");
static const OSSymbol *_AvgTimeToFullSym =
OSSymbol::withCString("AvgTimeToFull");
static const OSSymbol *_ManfDateSym =
OSSymbol::withCString(kIOPMPSManufactureDateKey);
static const OSSymbol *_DesignCapacitySym =
OSSymbol::withCString(kIOPMPSDesignCapacityKey);
static const OSSymbol *_CellVoltageSym =
OSSymbol::withCString("CellVoltage");
static const OSSymbol *_ManufacturerDataSym =
OSSymbol::withCString("ManufacturerData");
static const OSSymbol *_PFStatusSym =
OSSymbol::withCString("PermanentFailureStatus");
static const OSSymbol *_SerialNumberSym =
OSSymbol::withCString("FirmwareSerialNumber");
static const OSSymbol *_HardwareSerialSym =
OSSymbol::withCString("BatterySerialNumber");
#ifndef kIOPMPSBatteryChargeStatusKey
#define kIOPMPSBatteryChargeStatusKey "ChargeStatus"
#define kIOPMBatteryChargeStatusTooHot "HighTemperature"
#define kIOPMBatteryChargeStatusTooCold "LowTemperature"
#define kIOPMBatteryChargeStatusGradient "BatteryTemperatureGradient"
#endif
static const OSSymbol *_ChargeStatusSym =
OSSymbol::withCString(kIOPMPSBatteryChargeStatusKey);
#define super IOPMPowerSource
OSDefineMetaClassAndStructors(AppleSmartBattery,IOPMPowerSource)
AppleSmartBattery *
AppleSmartBattery::smartBattery(void)
{
AppleSmartBattery *me;
me = new AppleSmartBattery;
if (me && !me->init()) {
me->release();
return NULL;
}
return me;
}
bool AppleSmartBattery::init(void)
{
if (!super::init()) {
return false;
}
fProvider = NULL;
fWorkLoop = NULL;
fPollTimer = NULL;
return true;
}
bool AppleSmartBattery::start(IOService *provider)
{
IORegistryEntry *p = NULL;
OSNumber *debugPollingSetting;
BattLog("AppleSmartBattery loading...\n");
fProvider = OSDynamicCast(AppleSmartBatteryManager, provider);
if (!fProvider || !super::start(provider)) {
return false;
}
debugPollingSetting = (OSNumber *)fProvider->getProperty(kBatteryPollingDebugKey);
if (debugPollingSetting && OSDynamicCast(OSNumber, debugPollingSetting))
{
fPollingInterval = debugPollingSetting->unsigned32BitValue();
fPollingOverridden = true;
} else {
fPollingInterval = kDefaultPollInterval;
fPollingOverridden = false;
}
fPollingNow = false;
fCancelPolling = false;
fRetryAttempts = 0;
fPermanentFailure = false;
fFullyDischarged = false;
fFullyCharged = false;
fBatteryPresent = -1;
fACConnected = -1;
fAvgCurrent = 0;
fInflowDisabled = false;
fRebootPolling = false;
fCellVoltages = NULL;
fSystemSleeping = false;
fPowerServiceToAck = NULL;
fIncompleteReadRetries = kIncompleteReadRetryMax;
fInitialPollCountdown = kInitialPollCountdown;
fWorkLoop = getWorkLoop();
fPollTimer = IOTimerEventSource::timerEventSource(this,
OSMemberFunctionCast(IOTimerEventSource::Action,
this, &AppleSmartBattery::pollingTimeOut));
fBatteryReadAllTimer = IOTimerEventSource::timerEventSource(this,
OSMemberFunctionCast(IOTimerEventSource::Action,
this, &AppleSmartBattery::incompleteReadTimeOut));
if (!fWorkLoop || !fPollTimer
|| (kIOReturnSuccess != fWorkLoop->addEventSource(fPollTimer))
|| (kIOReturnSuccess != fWorkLoop->addEventSource(fBatteryReadAllTimer)))
{
return false;
}
fACPIProvider = NULL;
p = this;
while (p) {
p = p->getParentEntry(gIOServicePlane);
if (OSDynamicCast(IOACPIPlatformDevice, p)) {
fACPIProvider = (IOACPIPlatformDevice *)p;
break;
}
}
setProperty(kIOPMPSInvalidWakeSecondsKey, kSecondsUntilValidOnWake, 32);
setProperty(kIOPMPSPostChargeWaitSecondsKey, kPostChargeWaitSeconds, 32);
setProperty(kIOPMPSPostDishargeWaitSecondsKey, kPostDischargeWaitSeconds, 32);
clearBatteryState(false);
BattLog("AppleSmartBattery polling battery data.\n");
pollBatteryState(kNewBatteryPath);
return true;
}
void AppleSmartBattery::logReadError(
const char *error_type,
uint16_t additional_error,
IOSMBusTransaction *t)
{
if (!error_type) return;
BattLog("SmartBatteryManager Error: %s (%d)\n", error_type, additional_error);
if (t) {
BattLog("\tCorresponding transaction addr=0x%02x cmd=0x%02x status=0x%02x\n",
t->address, t->command, t->status);
}
return;
}
IOReturn AppleSmartBattery::handleSystemSleepWake(
IOService * powerService, bool isSystemSleep)
{
IOReturn ret = kIOPMAckImplied;
if (!powerService || (fSystemSleeping == isSystemSleep))
return kIOPMAckImplied;
if (fPowerServiceToAck)
{
fPowerServiceToAck->release();
fPowerServiceToAck = 0;
}
fSystemSleeping = isSystemSleep;
if (fSystemSleeping)
{
if (fPollingNow)
{
fPowerServiceToAck = powerService;
fPowerServiceToAck->retain();
fPollTimer->cancelTimeout();
fBatteryReadAllTimer->cancelTimeout();
ret = (kBatteryReadAllTimeout * 1000);
}
}
else {
fPowerServiceToAck = powerService;
fPowerServiceToAck->retain();
pollBatteryState(kExistingBatteryPath);
if (fPollingNow)
{
ret = (kBatteryReadAllTimeout * 1000);
}
else if (fPowerServiceToAck)
{
fPowerServiceToAck->release();
fPowerServiceToAck = 0;
}
}
BattLog("SmartBattery: handleSystemSleepWake(%d) = %u\n",
isSystemSleep, (uint32_t) ret);
return ret;
}
void AppleSmartBattery::acknowledgeSystemSleepWake(void)
{
if (fPowerServiceToAck)
{
fPowerServiceToAck->acknowledgeSetPowerState();
fPowerServiceToAck->release();
fPowerServiceToAck = 0;
}
}
void AppleSmartBattery::setPollingInterval(
int milliSeconds)
{
if (!fPollingOverridden) {
milliSecPollingTable[kDefaultPollInterval] = milliSeconds;
fPollingInterval = kDefaultPollInterval;
}
}
bool AppleSmartBattery::pollBatteryState(int path)
{
if (fStalledByUserClient)
{
return false;
}
fMachinePath = path;
if (!fPollingNow)
{
return transactionCompletion((void *)kTransactionRestart, NULL);
} else {
fRebootPolling = true;
return true;
}
}
void AppleSmartBattery::handleBatteryInserted(void)
{
clearBatteryState(false);
pollBatteryState(kNewBatteryPath);
return;
}
void AppleSmartBattery::handleBatteryRemoved(void)
{
if (fPollingNow) {
fCancelPolling = true;
fPollTimer->cancelTimeout();
fBatteryReadAllTimer->cancelTimeout();
}
clearBatteryState(true);
acknowledgeSystemSleepWake();
return;
}
void AppleSmartBattery::handleInflowDisabled(bool inflow_state)
{
fInflowDisabled = inflow_state;
pollBatteryState(kExistingBatteryPath);
return;
}
void AppleSmartBattery::handleChargeInhibited(bool charge_state)
{
fChargeInhibited = charge_state;
pollBatteryState(kExistingBatteryPath);
}
#define kWaitExclusiveIntervals 30
void AppleSmartBattery::handleExclusiveAccess(bool exclusive)
{
IOACPIAddress ecBehaviorsAddress;
IOReturn ret = kIOReturnSuccess;
UInt64 value64 = 0;
int waitCount = 0;
do {
if (!fACPIProvider)
break;
ecBehaviorsAddress.addr64 = 0x20 + 0x29;
ret = fACPIProvider->readAddressSpace(&value64,
kIOACPIAddressSpaceIDEmbeddedController,
ecBehaviorsAddress, 8, 0, 0);
if (kIOReturnSuccess != ret) {
break;
}
if (exclusive) {
value64 |= 1;
} else {
value64 &= ~1;
}
ret = fACPIProvider->writeAddressSpace(value64,
kIOACPIAddressSpaceIDEmbeddedController,
ecBehaviorsAddress, 8, 0, 0);
if (kIOReturnSuccess != ret) {
break;
}
waitCount = 0;
value64 = 0;
ret = kIOReturnSuccess;
while ((waitCount < kWaitExclusiveIntervals)
&& (kIOReturnSuccess == ret))
{
waitCount++;
IOSleep(100);
ret = fACPIProvider->readAddressSpace(&value64,
kIOACPIAddressSpaceIDEmbeddedController,
ecBehaviorsAddress, 8, 0, 0);
if (exclusive) {
if ((value64 & 0x10) == 0x10)
break;
} else {
if ((value64 & 0x10) == 0)
break;
}
}
} while (0);
if (exclusive)
{
setProperty("BatteryUpdatesBlockedExclusiveAccess", true);
fStalledByUserClient = true;
} else {
removeProperty("BatteryUpdatesBlockedExclusiveAccess");
fStalledByUserClient = false;
pollBatteryState(kNewBatteryPath);
}
}
void AppleSmartBattery::pollingTimeOut(void)
{
if (fPollingNow)
return;
if (fInitialPollCountdown > 0)
{
pollBatteryState(kNewBatteryPath);
} else {
pollBatteryState(kExistingBatteryPath);
}
}
void AppleSmartBattery::incompleteReadTimeOut(void)
{
logReadError(kErrorOverallTimeoutExpired, 0, NULL);
if (0 < fIncompleteReadRetries)
{
fIncompleteReadRetries--;
pollBatteryState(kNewBatteryPath);
}
}
bool AppleSmartBattery::transactionCompletion_shouldAbortTransactions(IOSMBusTransaction *transaction)
{
if (fSystemSleeping)
{
fPollingNow = false;
acknowledgeSystemSleepWake();
return true;
}
if (fStalledByUserClient)
{
fPollingNow = false;
return true;
}
if (fCancelPolling)
{
fCancelPolling = false;
if (transaction)
{
fPollingNow = false;
return true;
}
}
return false;
}
uint32_t AppleSmartBattery::transactionCompletion_requiresRetryGetMicroSec(IOSMBusTransaction *transaction)
{
IOSMBusStatus transaction_status = kIOSMBusStatusPECError;
bool transaction_needs_retry = false;
if (transaction)
transaction_status = transaction->status;
if (STATUS_ERROR_NEEDS_RETRY(transaction_status))
{
transaction_needs_retry = true;
} else if (STATUS_ERROR_NON_RECOVERABLE(transaction_status))
{
transaction_needs_retry = false;
logReadError(kErrorNonRecoverableStatus, transaction_status, transaction);
goto exit;
}
if (kIOSMBusStatusOK == transaction_status)
{
if (0 != fRetryAttempts) {
BattLog("SmartBattery: retry %d succeeded!\n", fRetryAttempts);
fRetryAttempts = 0;
transaction_needs_retry = false;
}
if (((kBFullChargeCapacityCmd == transaction->command)
|| (kBDesignCapacityCmd == transaction->command)
|| ((kBRemainingCapacityCmd == transaction->command)
&& !fFullyDischarged))
&& ((transaction->receiveData[1] == 0)
&& (transaction->receiveData[0] == 0)))
{
BattLog("SmartBatteryManager: retrying command 0x%02x; retry due to absurd value _zero_\n", transaction->command);
transaction_needs_retry = true;
goto exit;
}
if (((kBRemainingCapacityCmd == transaction->command)
|| (kBDesignCapacityCmd == transaction->command)
|| (kBFullChargeCapacityCmd == transaction->command))
&& ((transaction->receiveData[1] != 0)
|| (transaction->receiveData[0] != 0))
&& (0 != fRetryAttempts))
{
BattLog("SmartBatteryManager: Successfully read %d on retry %d\n",
transaction->command, fRetryAttempts);
fRetryAttempts = 0;
transaction_needs_retry = false;
goto exit;
}
}
if (transaction_needs_retry
&& (kRetryAttempts == fRetryAttempts))
{
BattLog("SmartBatteryManager: Giving up on retries\n");
BattLog("SmartBattery: Giving up on (0x%02x, 0x%02x) after %d retries.\n",
transaction->address, transaction->command, fRetryAttempts);
logReadError(kErrorRetryAttemptsExceeded, transaction_status, transaction);
fRetryAttempts = 0;
transaction_needs_retry = false;
acknowledgeSystemSleepWake();
goto exit;
}
exit:
if (transaction_needs_retry)
{
fRetryAttempts++;
return microSecDelayTable[fRetryAttempts];
} else {
return 0;
}
}
void AppleSmartBattery::transactionCompletion_handlePollingFinished(void)
{
fBatteryReadAllTimer->cancelTimeout();
rebuildLegacyIOBatteryInfo();
updateStatus();
fPollingNow = false;
acknowledgeSystemSleepWake();
if (fPollingOverridden && (fPollingInterval==0))
{
pollBatteryState(kNewBatteryPath);
return;
}
if (!fPermanentFailure)
{
if ((fInitialPollCountdown > 0)
|| fPollingOverridden
|| !fACConnected
|| (!fFullyCharged && fBatteryPresent))
{
if (fInitialPollCountdown > 0) {
fInitialPollCountdown--;
}
if (!fPollingOverridden)
{
fPollTimer->setTimeoutMS(milliSecPollingTable[fPollingInterval]);
} else {
fPollTimer->setTimeoutMS(1000 * fPollingInterval);
}
} else {
BattLog("SmartBattery: letting timeout expire.\n");
}
}
}
bool AppleSmartBattery::transactionCompletion(
void *ref,
IOSMBusTransaction *transaction)
{
IOSMBusStatus transaction_status = kIOSMBusStatusPECError;
int next_state = (uintptr_t)ref;
char recv_str[kIOSMBusMaxDataCount+1];
uint16_t val16 = 0;
bool transaction_success = false;
OSNumber *num = NULL;
if (this->transactionCompletion_shouldAbortTransactions(transaction)) {
return true;
}
if (!transaction || fRebootPolling)
{
transaction = NULL;
next_state = kTransactionRestart;
fRebootPolling = false;
}
if (transaction)
{
uint32_t delay_for = 0;
transaction_status = transaction->status;
BattLog("transaction state = 0x%02x; status = 0x%02x; word = %02x.%02x\n",
next_state, transaction->status,
transaction->receiveData[1], transaction->receiveData[0]);
delay_for = this->transactionCompletion_requiresRetryGetMicroSec(transaction);
if (0 != delay_for)
{
if (delay_for < 1000) {
IODelay(delay_for); } else {
IOSleep(delay_for / 1000); }
BattLog("SmartBattery: 0x%02x failed with 0x%02x; retry attempt %d of %d\n",
transaction->command, transaction_status, fRetryAttempts, kRetryAttempts);
readWordAsync(transaction->address, transaction->command);
return true;
}
}
transaction_success = (kIOSMBusStatusOK == transaction_status);
if (transaction_success) {
val16 = (transaction->receiveData[1] << 8) | transaction->receiveData[0];
} else {
val16 = 0;
}
switch(next_state)
{
case kTransactionRestart:
fPollTimer->cancelTimeout();
fCancelPolling = false;
fPollingNow = true;
fBatteryReadAllTimer->cancelTimeout();
fBatteryReadAllTimer->setTimeoutMS(kBatteryReadAllTimeout);
readWordAsync(kSMBusManagerAddr, kMStateContCmd);
break;
case kMStateContCmd:
if (transaction_success)
{
bool new_ac_connected = (!fInflowDisabled && (val16 & kMACPresentBit)) ? 1:0;
IOPMrootDomain *rd = getPMRootDomain();
if (rd && (new_ac_connected != fACConnected))
{
if (new_ac_connected) {
rd->receivePowerNotification(kIOPMSetACAdaptorConnected | kIOPMSetValue);
} else {
rd->receivePowerNotification(kIOPMSetACAdaptorConnected);
}
}
fACConnected = new_ac_connected;
setExternalConnected(fACConnected);
setExternalChargeCapable((val16 & kMPowerNotGoodBit) ? false:true);
} else {
fACConnected = false;
setExternalConnected(true);
setExternalChargeCapable(false);
}
readWordAsync(kSMBusManagerAddr, kMStateCmd);
break;
case kMStateCmd:
if (transaction_success)
{
fBatteryPresent = (val16 & kMPresentBatt_A_Bit) ? true : false;
setBatteryInstalled(fBatteryPresent);
setIsCharging((!fChargeInhibited && (val16 & kMChargingBatt_A_Bit)) ? true:false);
} else {
fBatteryPresent = false;
setBatteryInstalled(false);
setIsCharging(false);
}
readWordAsync(kSMBusBatteryAddr, kBBatteryStatusCmd);
break;
case kBBatteryStatusCmd:
if (!transaction_success)
{
fFullyCharged = false;
fFullyDischarged = false;
} else {
if (val16 & kBFullyChargedStatusBit) {
fFullyCharged = true;
} else {
fFullyCharged = false;
}
if (val16 & kBFullyDischargedStatusBit)
{
if (!fFullyDischarged) {
fFullyDischarged = true;
fProvider->handleFullDischarge();
}
} else {
fFullyDischarged = false;
}
if ((val16
& (kBTerminateDischargeAlarmBit | kBTerminateChargeAlarmBit))
== (kBTerminateDischargeAlarmBit | kBTerminateChargeAlarmBit))
{
const OSSymbol *permanentFailureSym = OSSymbol::withCString(kErrorPermanentFailure);
logReadError(kErrorPermanentFailure, 0, transaction);
setErrorCondition((OSSymbol *)permanentFailureSym);
permanentFailureSym->release();
fPermanentFailure = true;
fBatteryPresent = true;
setBatteryInstalled(true);
setIsCharging(false);
} else {
fPermanentFailure = false;
}
}
setFullyCharged(fFullyCharged);
if (!fBatteryPresent) {
fPollingNow = false;
clearBatteryState(true);
return true;
}
writeWordAsync(kSMBusBatteryAddr, kBManufacturerAccessCmd, kBExtendedPFStatusCmd);
break;
case (kWriteIndicatorBit | kBManufacturerAccessCmd):
if (transaction_success)
{
readWordAsync(kSMBusBatteryAddr, kBManufacturerAccessCmd);
}
break;
case kBManufacturerAccessCmd:
if(transaction_status)
num = OSNumber::withNumber((unsigned long long)val16, (int)32);
else
num = OSNumber::withNumber((unsigned long long)0, (int)32);
if (num)
{
setPSProperty(_PFStatusSym, num);
num->release();
}
if (kNewBatteryPath == fMachinePath) {
readBlockAsync(kSMBusBatteryAddr, kBManufactureNameCmd);
} else {
readWordAsync(kSMBusBatteryAddr, kBRemainingCapacityCmd);
}
break;
case kBManufactureNameCmd:
if (transaction_success)
{
if (0 != transaction->receiveDataCount)
{
const OSSymbol *manf_sym;
bzero(recv_str, sizeof(recv_str));
bcopy(transaction->receiveData, recv_str, transaction->receiveDataCount);
manf_sym = OSSymbol::withCString(recv_str);
if (manf_sym) {
setManufacturer((OSSymbol *)manf_sym);
manf_sym->release();
}
}
} else {
properties->removeObject(manufacturerKey);
}
readBlockAsync(kSMBusBatteryAddr, kBManufactureDataCmd);
break;
case kBManufactureDataCmd:
if (transaction_success)
{
if (0 != transaction->receiveDataCount)
{
setManufacturerData(transaction->receiveData, transaction->receiveDataCount);
}
} else {
properties->removeObject(_ManufacturerDataSym);
}
readWordAsync(kSMBusBatteryAddr, kBManufactureDateCmd);
break;
case kBManufactureDateCmd:
setManufactureDate(val16);
readBlockAsync(kSMBusBatteryAddr, kBDeviceNameCmd);
break;
case kBDeviceNameCmd:
if(transaction_success)
{
if(0 != transaction->receiveDataCount)
{
const OSSymbol *device_sym;
bzero(recv_str, sizeof(recv_str));
bcopy(transaction->receiveData, recv_str, transaction->receiveDataCount);
device_sym = OSSymbol::withCString(recv_str);
if(device_sym) {
setDeviceName((OSSymbol *)device_sym);
device_sym->release();
}
}
} else {
properties->removeObject(_DeviceNameSym);
}
readWordAsync(kSMBusBatteryAddr, kBSerialNumberCmd);
break;
case kBSerialNumberCmd:
if (transaction_success)
{
setSerialNumber(val16);
} else {
properties->removeObject(_SerialNumberSym);
}
readBlockAsync(kSMBusBatteryAddr, kBAppleHardwareSerialCmd);
break;
case kBAppleHardwareSerialCmd:
if( kIOSMBusStatusOK == transaction_status )
{
const OSSymbol *serialSymbol = OSSymbol::withCString(
(char *)transaction->receiveData);
if (serialSymbol) {
setPSProperty(_HardwareSerialSym, (OSObject *)serialSymbol);
serialSymbol->release();
}
} else {
properties->removeObject(_HardwareSerialSym);
}
readWordAsync(kSMBusBatteryAddr, kBDesignCapacityCmd);
break;
case kBDesignCapacityCmd:
num = OSNumber::withNumber(val16, 16);
if (num) {
properties->setObject(_DesignCapacitySym, num);
num->release();
}
readWordAsync(kSMBusBatteryAddr, kBRemainingCapacityCmd);
break;
case kBRemainingCapacityCmd:
fRemainingCapacity = val16;
setCurrentCapacity(val16);
if (!fPermanentFailure && (0 == fRemainingCapacity))
{
logReadError(kErrorZeroCapacity, kBRemainingCapacityCmd, transaction);
}
readWordAsync(kSMBusBatteryAddr, kBFullChargeCapacityCmd);
break;
case kBFullChargeCapacityCmd:
fFullChargeCapacity = val16;
setMaxCapacity(val16);
if (!fPermanentFailure && (0 == fFullChargeCapacity))
{
logReadError(kErrorZeroCapacity, kBFullChargeCapacityCmd, transaction);
}
readWordAsync(kSMBusBatteryAddr, kBAverageCurrentCmd);
break;
case kBAverageCurrentCmd:
setAmperage((int16_t)val16);
fAvgCurrent = (int16_t)val16;
if (0 == fAvgCurrent) {
setTimeRemaining(0);
}
readWordAsync(kSMBusBatteryAddr, kBVoltageCmd);
break;
case kBVoltageCmd:
setVoltage(val16);
readWordAsync(kSMBusBatteryAddr, kBMaxErrorCmd);
break;
case kBMaxErrorCmd:
setMaxErr(val16);
readWordAsync(kSMBusBatteryAddr, kBCycleCountCmd);
break;
case kBCycleCountCmd:
setCycleCount(val16);
readWordAsync(kSMBusBatteryAddr, kBAverageTimeToEmptyCmd);
break;
case kBAverageTimeToEmptyCmd:
if (!fPermanentFailure && transaction_success)
{
setAverageTimeToEmpty(val16);
if (fAvgCurrent < 0) {
setTimeRemaining(val16);
}
} else {
setTimeRemaining(0);
setAverageTimeToEmpty(0);
}
readWordAsync(kSMBusBatteryAddr, kBRunTimeToEmptyCmd);
break;
case kBRunTimeToEmptyCmd:
setInstantaneousTimeToEmpty(val16);
readWordAsync(kSMBusBatteryAddr, kBAverageTimeToFullCmd);
break;
case kBAverageTimeToFullCmd:
if (!fPermanentFailure && transaction_success)
{
setAverageTimeToFull(val16);
if (fAvgCurrent > 0) {
setTimeRemaining(val16);
}
} else {
setTimeRemaining(0);
setAverageTimeToFull(0);
}
readWordAsync(kSMBusBatteryAddr, kBTemperatureCmd);
break;
case kBTemperatureCmd:
setProperty("Temperature",
(long long unsigned int)val16,
(unsigned int)16);
readWordAsync(kSMBusBatteryAddr, kBReadCellVoltage1Cmd);
break;
case kBReadCellVoltage4Cmd:
case kBReadCellVoltage3Cmd:
case kBReadCellVoltage2Cmd:
case kBReadCellVoltage1Cmd:
if (transaction_success)
{
val16 = 0;
}
if (kBReadCellVoltage1Cmd == next_state) {
if (fCellVoltages)
{
fCellVoltages->release();
fCellVoltages = NULL;
}
fCellVoltages = OSArray::withCapacity(4);
}
if (fCellVoltages)
{
num = OSNumber::withNumber((unsigned long long)val16, 16);
fCellVoltages->setObject(num);
num->release();
}
if (kBReadCellVoltage4Cmd == next_state)
{
if (fCellVoltages)
{
setProperty(_CellVoltageSym, fCellVoltages);
fCellVoltages->release();
fCellVoltages = NULL;
} else {
removeProperty(_CellVoltageSym);
}
readWordAsync(kSMBusBatteryAddr, kBCurrentCmd);
} else {
readWordAsync(kSMBusBatteryAddr, next_state - 1);
}
break;
case kBCurrentCmd:
setInstantAmperage((int16_t)val16);
this->transactionCompletion_handlePollingFinished();
break;
default:
BattLog("SmartBattery: Error state %d not expected\n", next_state);
}
return true;
}
void AppleSmartBattery::clearBatteryState(bool do_update)
{
fRetryAttempts = 0;
fFullyDischarged = false;
fFullyCharged = false;
fBatteryPresent = false;
fACConnected = -1;
fAvgCurrent = 0;
setBatteryInstalled(false);
setIsCharging(false);
setCurrentCapacity(0);
setMaxCapacity(0);
setTimeRemaining(0);
setAmperage(0);
setVoltage(0);
setCycleCount(0);
setAdapterInfo(0);
setLocation(0);
properties->removeObject(manufacturerKey);
removeProperty(manufacturerKey);
properties->removeObject(serialKey);
removeProperty(serialKey);
properties->removeObject(batteryInfoKey);
removeProperty(batteryInfoKey);
properties->removeObject(errorConditionKey);
removeProperty(errorConditionKey);
properties->removeObject(_ChargeStatusSym);
removeProperty(_ChargeStatusSym);
properties->removeObject(_PFStatusSym);
removeProperty(_PFStatusSym);
rebuildLegacyIOBatteryInfo();
logReadError(kErrorClearBattery, 0, NULL);
if (do_update) {
updateStatus();
}
}
void AppleSmartBattery::rebuildLegacyIOBatteryInfo(void)
{
OSDictionary *legacyDict = OSDictionary::withCapacity(5);
uint32_t flags = 0;
OSNumber *flags_num = NULL;
if (externalConnected()) flags |= kIOPMACInstalled;
if (batteryInstalled()) flags |= kIOPMBatteryInstalled;
if (isCharging()) flags |= kIOPMBatteryCharging;
flags_num = OSNumber::withNumber((unsigned long long)flags, 32);
legacyDict->setObject(kIOBatteryFlagsKey, flags_num);
flags_num->release();
legacyDict->setObject(kIOBatteryCurrentChargeKey, properties->getObject(kIOPMPSCurrentCapacityKey));
legacyDict->setObject(kIOBatteryCapacityKey, properties->getObject(kIOPMPSMaxCapacityKey));
legacyDict->setObject(kIOBatteryVoltageKey, properties->getObject(kIOPMPSVoltageKey));
legacyDict->setObject(kIOBatteryAmperageKey, properties->getObject(kIOPMPSAmperageKey));
legacyDict->setObject(kIOBatteryCycleCountKey, properties->getObject(kIOPMPSCycleCountKey));
setLegacyIOBatteryInfo(legacyDict);
legacyDict->release();
}
#define CLASS AppleSmartBattery
#define IMPLEMENT_APPLESMARTBATTERY_INT_SETTER(methodName, pspropSYM, argNameX, argTypeX) \
void CLASS::methodName(argTypeX argNameX) { \
OSNumber *n = OSNumber::withNumber(argNameX, 32); \
if (n) { \
setPSProperty(pspropSYM, n); \
n->release(); \
} \
}
IMPLEMENT_APPLESMARTBATTERY_INT_SETTER(setAverageTimeToEmpty, _AvgTimeToEmptySym, seconds, int);
IMPLEMENT_APPLESMARTBATTERY_INT_SETTER(setAverageTimeToFull, _AvgTimeToFullSym, seconds, int);
IMPLEMENT_APPLESMARTBATTERY_INT_SETTER(setInstantaneousTimeToEmpty, _InstantTimeToEmptySym, seconds, int);
IMPLEMENT_APPLESMARTBATTERY_INT_SETTER(setInstantAmperage, _InstantAmperageSym, mA, int);
IMPLEMENT_APPLESMARTBATTERY_INT_SETTER(setManufactureDate, _ManfDateSym, date, int);
IMPLEMENT_APPLESMARTBATTERY_INT_SETTER(setSerialNumber, _SerialNumberSym, sernum, uint16_t);
IMPLEMENT_APPLESMARTBATTERY_INT_SETTER(setMaxErr, _MaxErrSym, error, int);
#define IMPLEMENT_APPLESMARTBATTERY_INT_GETTER(methodName, pspropSYM, return_type) \
return_type CLASS::methodName(void) { \
OSNumber *n = OSDynamicCast(OSNumber, properties->getObject(_SerialNumberSym)); \
if (n) { \
return n->unsigned16BitValue(); \
} else { \
return 0; \
} \
}
IMPLEMENT_APPLESMARTBATTERY_INT_GETTER(serialNumber, _SerialNumberSym, uint16_t);
IMPLEMENT_APPLESMARTBATTERY_INT_GETTER(maxErr, _MaxErrSym, int);
IMPLEMENT_APPLESMARTBATTERY_INT_GETTER(averageTimeToEmpty, _AvgTimeToEmptySym, int);
IMPLEMENT_APPLESMARTBATTERY_INT_GETTER(averageTimeToFull, _AvgTimeToFullSym, int);
IMPLEMENT_APPLESMARTBATTERY_INT_GETTER(manufactureDate, _ManfDateSym, int);
void AppleSmartBattery::setDeviceName(OSSymbol *sym)
{
if (sym)
setPSProperty(_DeviceNameSym, (OSObject *)sym);
}
OSSymbol * AppleSmartBattery::deviceName(void)
{
return OSDynamicCast(OSSymbol, properties->getObject(_DeviceNameSym));
}
void AppleSmartBattery::setFullyCharged(bool charged)
{
setPSProperty(_FullyChargedSym, (charged ? kOSBooleanTrue:kOSBooleanFalse));
}
bool AppleSmartBattery::fullyCharged(void)
{
return (kOSBooleanTrue == properties->getObject(_FullyChargedSym));
}
void AppleSmartBattery::setManufacturerData(uint8_t *buffer, uint32_t bufferSize)
{
OSData *newData = OSData::withBytes(buffer, bufferSize);
if (newData) {
setPSProperty(_ManufacturerDataSym, newData);
newData->release();
}
}
void AppleSmartBattery::setChargeStatus(const OSSymbol *sym)
{
if (NULL == sym) {
properties->removeObject(_ChargeStatusSym);
removeProperty(_ChargeStatusSym);
} else {
setPSProperty(_ChargeStatusSym, (OSObject *)sym);
}
}
const OSSymbol *AppleSmartBattery::chargeStatus(void)
{
return (const OSSymbol *)properties->getObject(_ChargeStatusSym);
}
IOReturn AppleSmartBattery::readWordAsync(
uint8_t address,
uint8_t cmd
) {
IOReturn ret = kIOReturnError;
bzero(&fTransaction, sizeof(IOSMBusTransaction));
fTransaction.protocol = kIOSMBusProtocolReadWord;
fTransaction.address = address;
fTransaction.command = cmd;
ret = fProvider->performTransaction(
&fTransaction,
OSMemberFunctionCast(IOSMBusTransactionCompletion,
this, &AppleSmartBattery::transactionCompletion),
(OSObject *)this,
(void *)cmd);
return ret;
}
IOReturn AppleSmartBattery::writeWordAsync(
uint8_t address,
uint8_t cmd,
uint16_t writeWord)
{
IOReturn ret = kIOReturnError;
bzero(&fTransaction, sizeof(IOSMBusTransaction));
fTransaction.protocol = kIOSMBusProtocolWriteWord;
fTransaction.address = address;
fTransaction.command = cmd;
fTransaction.sendData[0] = writeWord & 0xFF;
fTransaction.sendData[1] = (writeWord >> 8) & 0xFF;
fTransaction.sendDataCount = 2;
ret = fProvider->performTransaction(
&fTransaction,
OSMemberFunctionCast(IOSMBusTransactionCompletion,
this, &AppleSmartBattery::transactionCompletion),
(OSObject *)this,
(void *)((uint32_t)cmd | kWriteIndicatorBit));
return ret;
}
IOReturn AppleSmartBattery::readBlockAsync(
uint8_t address,
uint8_t cmd
) {
IOReturn ret = kIOReturnError;
bzero(&fTransaction, sizeof(IOSMBusTransaction));
fTransaction.protocol = kIOSMBusProtocolReadBlock;
fTransaction.address = address;
fTransaction.command = cmd;
ret = fProvider->performTransaction(
&fTransaction,
OSMemberFunctionCast(IOSMBusTransactionCompletion,
this, &AppleSmartBattery::transactionCompletion),
(OSObject *)this,
(void *)cmd);
return ret;
}