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 *_DesignCycleCount70Sym =
OSSymbol::withCString("DesignCycleCount70");
static const OSSymbol *_DesignCycleCount9CSym =
OSSymbol::withCString("DesignCycleCount9C");
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);
static const OSSymbol *_ChargeStatusTooHot =
OSSymbol::withCString(kIOPMBatteryChargeStatusTooHot);
static const OSSymbol *_ChargeStatusTooCold =
OSSymbol::withCString(kIOPMBatteryChargeStatusTooCold);
static const OSSymbol *_ChargeStatusGradient =
OSSymbol::withCString(kIOPMBatteryChargeStatusGradient);
#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)
{
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(
void *ref,
IOSMBusTransaction *transaction)
{
int next_state = (uintptr_t)ref;
uint32_t delay_for = 0;
IOSMBusStatus transaction_status = kIOSMBusStatusPECError;
bool transaction_needs_retry = false;
char recv_str[kIOSMBusMaxDataCount+1];
int16_t my_signed_16;
uint16_t my_unsigned_16;
OSNumber *cell_volt_num = NULL;
OSNumber *pfstatus_num = NULL;
if (fSystemSleeping)
{
fPollingNow = false;
acknowledgeSystemSleepWake();
return true;
}
if (fStalledByUserClient)
{
fPollingNow = false;
return true;
}
if( fCancelPolling )
{
fCancelPolling = false;
if( transaction )
{
fPollingNow = false;
return true;
}
}
if( NULL == transaction || fRebootPolling )
{
next_state = kTransactionRestart;
fRebootPolling = false;
} else {
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]);
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);
}
if( (kIOSMBusStatusOK == transaction_status)
&& (0 != fRetryAttempts) )
{
BattLog("SmartBattery: retry %d succeeded!\n", fRetryAttempts);
fRetryAttempts = 0;
transaction_needs_retry = false;
}
if( kIOSMBusStatusOK == transaction_status )
{
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;
}
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;
}
}
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();
}
if( transaction_needs_retry )
{
delay_for = microSecDelayTable[fRetryAttempts];
if( 0 != delay_for ) {
if( delay_for < 1000 ) {
IODelay(delay_for);
} else {
IOSleep(delay_for / 1000);
}
}
fRetryAttempts++;
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;
}
}
switch(next_state)
{
case kTransactionRestart:
fPollTimer->cancelTimeout();
fCancelPolling = false;
fPollingNow = true;
fBatteryReadAllTimer->cancelTimeout();
fBatteryReadAllTimer->setTimeoutMS( kBatteryReadAllTimeout );
readWordAsync(kSMBusManagerAddr, kMStateContCmd);
break;
case kMStateContCmd:
if( kIOSMBusStatusOK == transaction_status )
{
int new_ac_connected = 0;
uint16_t charge_disrupted = 0;
my_unsigned_16 = (transaction->receiveData[1] << 8)
| transaction->receiveData[0];
#if 0
IOLog("AppleSmartBattery::MStateCont = 0x%04x\n", my_unsigned_16);
#endif
new_ac_connected = ( !fInflowDisabled
&& (my_unsigned_16 & 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(
(my_unsigned_16 & kMPowerNotGoodBit) ? false:true);
charge_disrupted = my_unsigned_16 & (kMReservedNoChargeBit14
| kMReservedNoChargeBit15);
#if 0
IOLog("AppleSmartBattery: Charge Disrupted = 0x%04x\n", charge_disrupted);
#endif
if (kMReservedNoChargeBit14 == charge_disrupted)
{
setChargeStatus(_ChargeStatusTooCold);
} else if (charge_disrupted == kMReservedNoChargeBit15)
{
setChargeStatus(_ChargeStatusTooHot);
} else if (charge_disrupted == (kMReservedNoChargeBit14
| kMReservedNoChargeBit15))
{
setChargeStatus(_ChargeStatusGradient);
} else {
if (chargeStatus()) {
setChargeStatus(NULL);
}
}
} else {
fACConnected = false;
setExternalConnected(true);
setExternalChargeCapable(false);
}
readWordAsync(kSMBusManagerAddr, kMStateCmd);
break;
case kMStateCmd:
if( kIOSMBusStatusOK == transaction_status )
{
my_unsigned_16 = (transaction->receiveData[1] << 8)
| transaction->receiveData[0];
fBatteryPresent = (my_unsigned_16 & kMPresentBatt_A_Bit)
? true : false;
setBatteryInstalled(fBatteryPresent);
setIsCharging( !fChargeInhibited
&& (my_unsigned_16 & kMChargingBatt_A_Bit) ? true:false);
} else {
fBatteryPresent = false;
setBatteryInstalled(false);
setIsCharging(false);
}
readWordAsync(kSMBusBatteryAddr, kBBatteryStatusCmd);
break;
case kBBatteryStatusCmd:
if( kIOSMBusStatusOK != transaction_status )
{
fFullyCharged = false;
fFullyDischarged = false;
} else {
my_unsigned_16 = (transaction->receiveData[1] << 8)
| transaction->receiveData[0];
if ( my_unsigned_16 & kBFullyChargedStatusBit) {
fFullyCharged = true;
} else {
fFullyCharged = false;
}
if ( my_unsigned_16 & kBFullyDischargedStatusBit)
{
if(!fFullyDischarged) {
fFullyDischarged = true;
fProvider->handleFullDischarge();
}
} else {
fFullyDischarged = false;
}
if( (my_unsigned_16
& (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 (kIOSMBusStatusOK == transaction_status)
{
readWordAsync(kSMBusBatteryAddr, kBManufacturerAccessCmd);
}
break;
case kBManufacturerAccessCmd:
my_unsigned_16 = ((uint16_t)transaction->receiveData[1] << 8)
| (uint16_t)transaction->receiveData[0];
if (kIOSMBusStatusOK == transaction_status) {
pfstatus_num = OSNumber::withNumber((unsigned long long)my_unsigned_16, (int)32);
} else {
pfstatus_num = OSNumber::withNumber((unsigned long long)0, (int)32);
}
if (pfstatus_num)
{
setPSProperty(_PFStatusSym, pfstatus_num);
pfstatus_num->release();
}
if(kNewBatteryPath == fMachinePath) {
readBlockAsync(kSMBusBatteryAddr, kBManufactureNameCmd);
} else {
readWordAsync(kSMBusBatteryAddr, kBRemainingCapacityCmd);
}
break;
case kBManufactureNameCmd:
if( kIOSMBusStatusOK == transaction_status )
{
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 (kIOSMBusStatusOK == transaction_status)
{
if(0 != transaction->receiveDataCount)
{
setManufacturerData( transaction->receiveData,
transaction->receiveDataCount );
}
} else {
properties->removeObject( _ManufacturerDataSym );
}
readWordAsync(kSMBusBatteryAddr, kBManufactureDateCmd);
break;
case kBManufactureDateCmd:
if( kIOSMBusStatusOK == transaction_status )
{
setManufactureDate(
(uint32_t)(transaction->receiveData[0]
| (transaction->receiveData[1] << 8)));
} else {
setManufactureDate(0);
}
readBlockAsync(kSMBusBatteryAddr, kBDeviceNameCmd);
break;
case kBDeviceNameCmd:
if( kIOSMBusStatusOK == transaction_status )
{
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( kIOSMBusStatusOK == transaction_status )
{
setSerialNumber((uint16_t)
(transaction->receiveData[0]
| (transaction->receiveData[1] << 8) ));
} 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:
if( kIOSMBusStatusOK == transaction_status )
{
OSNumber *design_cap;
design_cap = OSNumber::withNumber(
(uint32_t)(transaction->receiveData[0]
| (transaction->receiveData[1] << 8)), 32);
if(design_cap) {
properties->setObject(_DesignCapacitySym, design_cap);
design_cap->release();
}
} else {
OSNumber *zero_num = OSNumber::withNumber((long long unsigned int)0, 32);
if(zero_num) {
properties->setObject(_DesignCapacitySym, zero_num);
zero_num->release();
}
}
readWordAsync(kSMBusBatteryAddr, kBRemainingCapacityCmd);
break;
case kBRemainingCapacityCmd:
if( !fPermanentFailure
&& (kIOSMBusStatusOK == transaction_status) )
{
my_unsigned_16 = (transaction->receiveData[1] << 8)
| transaction->receiveData[0];
fRemainingCapacity = my_unsigned_16;
setCurrentCapacity( (unsigned int)my_unsigned_16 );
} else {
fRemainingCapacity = 0;
setCurrentCapacity(0);
}
if ( !fPermanentFailure && (0 == fRemainingCapacity) )
{
logReadError(kErrorZeroCapacity, kBRemainingCapacityCmd, transaction);
}
readWordAsync(kSMBusBatteryAddr, kBFullChargeCapacityCmd);
break;
case kBFullChargeCapacityCmd:
if( !fPermanentFailure
&& (kIOSMBusStatusOK == transaction_status) )
{
my_unsigned_16 = (transaction->receiveData[1] << 8)
| transaction->receiveData[0];
fFullChargeCapacity = my_unsigned_16;
setMaxCapacity( my_unsigned_16 );
} else {
fFullChargeCapacity = 0;
setMaxCapacity(0);
}
if ( !fPermanentFailure && (0 == fFullChargeCapacity) )
{
logReadError(kErrorZeroCapacity, kBFullChargeCapacityCmd, transaction);
}
readWordAsync(kSMBusBatteryAddr, kBAverageCurrentCmd);
break;
case kBAverageCurrentCmd:
if( kIOSMBusStatusOK == transaction_status )
{
my_signed_16 = (transaction->receiveData[1] << 8)
| transaction->receiveData[0];
setAmperage( my_signed_16 );
fAvgCurrent = my_signed_16;
} else {
fAvgCurrent = 0;
setAmperage(0);
setTimeRemaining(0);
}
readWordAsync(kSMBusBatteryAddr, kBVoltageCmd);
break;
case kBVoltageCmd:
if( kIOSMBusStatusOK == transaction_status )
{
my_unsigned_16 = (transaction->receiveData[1] << 8)
| transaction->receiveData[0];
setVoltage( my_unsigned_16 );
} else {
setVoltage(0);
}
readWordAsync(kSMBusBatteryAddr, kBMaxErrorCmd);
break;
case kBMaxErrorCmd:
if( kIOSMBusStatusOK == transaction_status )
{
my_unsigned_16 = (transaction->receiveData[1] << 8)
| transaction->receiveData[0];
setMaxErr( my_unsigned_16 );
} else {
setMaxErr(0);
}
readWordAsync(kSMBusBatteryAddr, kBCycleCountCmd);
break;
case kBCycleCountCmd:
if( kIOSMBusStatusOK == transaction_status )
{
my_unsigned_16 = (transaction->receiveData[1] << 8)
| transaction->receiveData[0];
setCycleCount( my_unsigned_16 );
} else {
setCycleCount(0);
}
readWordAsync(kSMBusBatteryAddr, kBAverageTimeToEmptyCmd);
break;
case kBAverageTimeToEmptyCmd:
if( !fPermanentFailure
&& (kIOSMBusStatusOK == transaction_status) )
{
my_unsigned_16 = (transaction->receiveData[1] << 8)
| transaction->receiveData[0];
setAverageTimeToEmpty( my_unsigned_16 );
if(fAvgCurrent < 0) {
setTimeRemaining( my_unsigned_16 );
}
} else {
setTimeRemaining(0);
setAverageTimeToEmpty(0);
}
readWordAsync(kSMBusBatteryAddr, kBRunTimeToEmptyCmd);
break;
case kBRunTimeToEmptyCmd:
if( !fPermanentFailure
&& (kIOSMBusStatusOK == transaction_status) )
{
my_unsigned_16 = (transaction->receiveData[1] << 8)
| transaction->receiveData[0];
setInstantaneousTimeToEmpty( my_unsigned_16 );
} else {
setInstantaneousTimeToEmpty(0);
}
readWordAsync(kSMBusBatteryAddr, kBAverageTimeToFullCmd);
break;
case kBAverageTimeToFullCmd:
if( !fPermanentFailure
&& (kIOSMBusStatusOK == transaction_status) )
{
my_unsigned_16 = (transaction->receiveData[1] << 8)
| transaction->receiveData[0];
setAverageTimeToFull( my_unsigned_16 );
if(fAvgCurrent > 0) {
setTimeRemaining( my_unsigned_16 );
}
} else {
setTimeRemaining(0);
setAverageTimeToFull(0);
}
readWordAsync(kSMBusBatteryAddr, kBTemperatureCmd);
break;
case kBTemperatureCmd:
if( kIOSMBusStatusOK == transaction_status )
{
my_unsigned_16 = (transaction->receiveData[1] << 8)
| transaction->receiveData[0];
setProperty("Temperature",
(long long unsigned int)my_unsigned_16,
(unsigned int)16);
} else {
setProperty("Temperature",
(long long unsigned int)0,
(unsigned int)16 );
}
readWordAsync(kSMBusBatteryAddr, kBReadCellVoltage1Cmd);
break;
case kBReadCellVoltage4Cmd:
case kBReadCellVoltage3Cmd:
case kBReadCellVoltage2Cmd:
case kBReadCellVoltage1Cmd:
my_unsigned_16 = 0;
if( kIOSMBusStatusOK == transaction_status )
{
my_unsigned_16 = (transaction->receiveData[1] << 8)
| transaction->receiveData[0];
}
if( kBReadCellVoltage1Cmd == next_state) {
if( fCellVoltages )
{
fCellVoltages->release();
fCellVoltages = NULL;
}
fCellVoltages = OSArray::withCapacity(4);
}
if( fCellVoltages )
{
cell_volt_num = OSNumber::withNumber(
(unsigned long long)my_unsigned_16, 16);
fCellVoltages->setObject(cell_volt_num);
cell_volt_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:
if( kIOSMBusStatusOK == transaction_status )
{
my_signed_16 = (transaction->receiveData[1] << 8)
| transaction->receiveData[0];
setInstantAmperage( my_signed_16 );
} else {
setInstantAmperage( 0 );
}
fBatteryReadAllTimer->cancelTimeout();
rebuildLegacyIOBatteryInfo();
updateStatus();
fPollingNow = false;
acknowledgeSystemSleepWake();
if( fPollingOverridden && (fPollingInterval==0))
{
pollBatteryState( kNewBatteryPath );
return true;
}
if ( ((fInitialPollCountdown > 0)
|| fPollingOverridden )
|| ((!fACConnected
|| (!fFullyCharged && fBatteryPresent))
&& !fPermanentFailure))
{
if( fInitialPollCountdown > 0) {
fInitialPollCountdown--;
}
if( !fPollingOverridden )
{
fPollTimer->setTimeoutMS( milliSecPollingTable[fPollingInterval] );
} else {
fPollTimer->setTimeoutMS( 1000 * fPollingInterval );
}
} else {
BattLog("SmartBattery: letting timeout expire.\n");
}
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);
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();
}
void AppleSmartBattery::setSerialNumber(uint16_t sernum)
{
OSNumber *n = OSNumber::withNumber(sernum, 16);
if (n) {
setPSProperty(_SerialNumberSym, n);
n->release();
}
}
uint16_t AppleSmartBattery::serialNumber(void)
{
OSNumber *n = OSDynamicCast(OSNumber, properties->getObject(_SerialNumberSym));
if (n) {
return n->unsigned16BitValue();
} else {
return 0;
}
}
void AppleSmartBattery::setMaxErr(int error)
{
OSNumber *n = OSNumber::withNumber(error, 32);
if (n) {
setPSProperty(_MaxErrSym, n);
n->release();
}
}
int AppleSmartBattery::maxErr(void)
{
OSNumber *n = OSDynamicCast(OSNumber, properties->getObject(_MaxErrSym));
if (n) {
return n->unsigned32BitValue();
} else {
return 0;
}
}
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::setAverageTimeToEmpty(int seconds)
{
OSNumber *n = OSNumber::withNumber(seconds, 32);
if (n) {
setPSProperty(_AvgTimeToEmptySym, n);
n->release();
}
}
int AppleSmartBattery::averageTimeToEmpty(void)
{
OSNumber *n = OSDynamicCast(OSNumber, properties->getObject(_AvgTimeToEmptySym));
if (n) {
return n->unsigned32BitValue();
} else {
return 0;
}
}
void AppleSmartBattery::setAverageTimeToFull(int seconds)
{
OSNumber *n = OSNumber::withNumber(seconds, 32);
if (n) {
setPSProperty(_AvgTimeToFullSym, n);
n->release();
}
}
int AppleSmartBattery::averageTimeToFull(void)
{
OSNumber *n = OSDynamicCast(OSNumber, properties->getObject(_AvgTimeToFullSym));
if (n) {
return n->unsigned32BitValue();
} else {
return 0;
}
}
void AppleSmartBattery::setInstantaneousTimeToEmpty(int seconds)
{
OSNumber *n = OSNumber::withNumber(seconds, 32);
if (n) {
setPSProperty(_InstantTimeToEmptySym, n);
n->release();
}
}
void AppleSmartBattery::setInstantAmperage(int mA)
{
OSNumber *n = OSNumber::withNumber(mA, 32);
if (n) {
setPSProperty(_InstantAmperageSym, n);
n->release();
}
}
void AppleSmartBattery::setManufactureDate(int date)
{
OSNumber *n = OSNumber::withNumber(date, 32);
if (n) {
properties->setObject(_ManfDateSym, n);
n->release();
}
}
int AppleSmartBattery::manufactureDate(void)
{
OSNumber *n = OSDynamicCast(OSNumber, properties->getObject(_ManfDateSym));
if (n) {
return n->unsigned32BitValue();
} else {
return 0;
}
}
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;
}