AppleSmartBatteryManager.cpp [plain text]
#include <IOKit/pwr_mgt/RootDomain.h>
#include "AppleSmartBatteryManager.h"
#include "AppleSmartBattery.h"
#define kMaxRetries 5
static int retryDelaysTable[kMaxRetries] =
{ 1, 10, 100, 1000, 1000 };
enum {
kMyOnPowerState = 1
};
enum {
kInhibitChargingCmd = 0,
kDisableInflowCmd
};
static IOPMPowerState myTwoStates[2] = {
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{1, kIOPMPowerOn, kIOPMPowerOn, kIOPMPowerOn, 0, 0, 0, 0, 0, 0, 0, 0}
};
#define super IOService
OSDefineMetaClassAndStructors(AppleSmartBatteryManager, IOService)
bool AppleSmartBatteryManager::start(IOService *provider)
{
bool ret_bool;
IOCommandGate * gate;
IOWorkLoop * wl;
if(!super::start(provider)) {
return false;
}
fProvider = OSDynamicCast(IOSMBusController, provider);
if(!fProvider) {
return false;
}
const OSSymbol *ucClassName =
OSSymbol::withCStringNoCopy("AppleSmartBatteryManagerUserClient");
setProperty(gIOUserClientClassKey, (OSObject *) ucClassName);
ucClassName->release();
wl = getWorkLoop();
if (!wl) {
return false;
}
PMinit();
registerPowerDriver(this, myTwoStates, 2);
provider->joinPMtree(this);
fBattery = AppleSmartBattery::smartBattery();
if(!fBattery) return false;
ret_bool = fBattery->attach(this);
ret_bool = fBattery->start(this);
fManagerGate = IOCommandGate::commandGate(this);
if (!fManagerGate) {
return false;
}
wl->addEventSource(fManagerGate);
gate = IOCommandGate::commandGate(fBattery);
if (!gate) {
return false;
}
wl->addEventSource(gate);
fBatteryGate = gate;
fExclusiveUserClient = false;
fBattery->registerService(0);
this->registerService(0);
return true;
}
IOReturn AppleSmartBatteryManager::setPollingInterval(
int milliSeconds)
{
if(milliSeconds <= 0) return kIOReturnBadArgument;
if(fBattery)
fBattery->setPollingInterval(milliSeconds);
setProperty("PollingInterval_msec", milliSeconds, 32);
return kIOReturnSuccess;
}
IOReturn AppleSmartBatteryManager::performExternalTransaction(
void *in,
void *out,
IOByteCount inSize,
IOByteCount *outSize)
{
uint16_t i;
uint16_t retryAttempts = 0;
IOSMBusTransaction newTransaction;
IOReturn transactionSuccess;
EXSMBUSInputStruct *inSMBus = (EXSMBUSInputStruct *)in;
EXSMBUSOutputStruct *outSMBus = (EXSMBUSOutputStruct *)out;
if (!inSMBus || !outSMBus)
return kIOReturnBadArgument;
do {
bzero(&newTransaction, sizeof(IOSMBusTransaction));
if (kSMBusAppleDoublerAddr == inSMBus->batterySelector
|| kSMBusBatteryAddr == inSMBus->batterySelector
|| kSMBusManagerAddr == inSMBus->batterySelector
|| kSMBusChargerAddr == inSMBus->batterySelector)
{
newTransaction.address = inSMBus->batterySelector;
} else {
if (0 == inSMBus->batterySelector)
{
newTransaction.address = kSMBusBatteryAddr;
} else {
newTransaction.address = kSMBusManagerAddr;
}
}
newTransaction.command = inSMBus->address;
switch (inSMBus->type) {
case kEXWriteWord:
newTransaction.protocol = kIOSMBusProtocolWriteWord;
newTransaction.sendDataCount = 2;
break;
case kEXReadWord:
newTransaction.protocol = kIOSMBusProtocolReadWord;
newTransaction.sendDataCount = 0;
break;
case kEXWriteBlock:
newTransaction.protocol = kIOSMBusProtocolWriteBlock;
newTransaction.sendDataCount = inSMBus->inByteCount - 1;
break;
case kEXReadBlock:
newTransaction.protocol = kIOSMBusProtocolReadBlock;
newTransaction.sendDataCount = 0;
break;
case kEXWriteByte:
newTransaction.protocol = kIOSMBusProtocolWriteByte;
newTransaction.sendDataCount = 1;
break;
case kEXReadByte:
newTransaction.protocol = kIOSMBusProtocolReadByte;
newTransaction.sendDataCount = 0;
break;
case kEXSendByte:
newTransaction.protocol = kIOSMBusProtocolSendByte;
newTransaction.sendDataCount = 0;
break;
default:
return kIOReturnBadArgument;
}
if ((kIOSMBusProtocolWriteWord == newTransaction.protocol)
|| (kIOSMBusProtocolWriteBlock == newTransaction.protocol))
{
for(i = 0; i<MAX_SMBUS_DATA_SIZE; i++) {
newTransaction.sendData[i] = inSMBus->inBuf[i];
}
}
if (inSMBus->flags & kEXFlagRetry)
{
if (retryAttempts >= kMaxRetries) {
retryAttempts = kMaxRetries - 1;
}
IODelay( retryDelaysTable[retryAttempts] );
}
fManagerGate->runAction(
(IOCommandGate::Action)OSMemberFunctionCast(
IOCommandGate::Action, this,
&AppleSmartBatteryManager::performExternalTransactionGated),
(void *)&newTransaction,
(void *)&transactionSuccess,
NULL,
NULL);
if ((kIOReturnSuccess == transactionSuccess)
&& (kIOSMBusStatusOK == newTransaction.status))
{
outSMBus->status = kIOReturnSuccess;
} else {
switch (newTransaction.status) {
case kIOSMBusStatusUnknownFailure:
case kIOSMBusStatusDeviceAddressNotAcknowledged:
case kIOSMBusStatusDeviceError:
case kIOSMBusStatusDeviceCommandAccessDenied:
case kIOSMBusStatusUnknownHostError:
outSMBus->status = kIOReturnNoDevice;
break;
case kIOSMBusStatusTimeout:
case kIOSMBusStatusBusy:
outSMBus->status = kIOReturnTimeout;
break;
case kIOSMBusStatusHostUnsupportedProtocol:
outSMBus->status = kIOReturnUnsupported;
break;
default:
outSMBus->status = kIOReturnInternalError;
break;
}
}
} while ((inSMBus->flags & kEXFlagRetry)
&& (outSMBus->status != kIOReturnSuccess)
&& (++retryAttempts < kMaxRetries));
if (((kIOSMBusProtocolReadWord == newTransaction.protocol)
|| (kIOSMBusProtocolReadBlock == newTransaction.protocol)
|| (kIOSMBusProtocolReadByte == newTransaction.protocol))
&& (kIOSMBusStatusOK == newTransaction.status))
{
outSMBus->outByteCount = newTransaction.receiveDataCount;
for(i = 0; i<outSMBus->outByteCount; i++) {
outSMBus->outBuf[i] = newTransaction.receiveData[i];
}
}
return kIOReturnSuccess;
}
IOReturn AppleSmartBatteryManager::performExternalTransactionGated(
void *arg0,
void *arg1,
void *arg2 __unused,
void *arg3 __unused)
{
IOSMBusTransaction *trans = (IOSMBusTransaction *)arg0;
IOReturn *return_code = (IOReturn *)arg1;
if (!fExclusiveUserClient) {
return kIOReturnExclusiveAccess;
}
*return_code = fProvider->performTransaction(
trans,
OSMemberFunctionCast(
IOSMBusTransactionCompletion,
this, &AppleSmartBatteryManager::transactionCompletion),
(OSObject *)this,
(void *)trans);
if(kIOReturnSuccess != *return_code)
return kIOReturnSuccess;
fManagerGate->commandSleep(trans, THREAD_UNINT);
return kIOReturnSuccess;
}
IOReturn AppleSmartBatteryManager::performTransaction(
IOSMBusTransaction * transaction,
IOSMBusTransactionCompletion completion,
OSObject * target,
void * reference)
{
return fProvider->performTransaction(transaction,
completion,
target,
reference);
}
IOReturn AppleSmartBatteryManager::setPowerState(
unsigned long which,
IOService *whom)
{
IOReturn ret = IOPMAckImplied;
if( fBatteryGate )
{
ret = fBatteryGate->runAction(OSMemberFunctionCast(IOCommandGate::Action,
fBattery, &AppleSmartBattery::handleSystemSleepWake),
(void *) this, (void *) !which, NULL, NULL);
}
return ret;
}
IOReturn AppleSmartBatteryManager::message(
UInt32 type,
IOService *provider,
void *argument )
{
IOSMBusAlarmMessage *alarm = (IOSMBusAlarmMessage *)argument;
static uint16_t last_data = 0;
uint16_t changed_bits = 0;
uint16_t data = 0;
if(!alarm) return kIOReturnSuccess;
if( (kIOMessageSMBusAlarm == type)
&& (kSMBusManagerAddr == alarm->fromAddress)
&& fBatteryGate)
{
data = (uint16_t)(alarm->data[0] | (alarm->data[1] << 8));
changed_bits = data ^ last_data;
last_data = data;
if(changed_bits & kMPresentBatt_A_Bit)
{
if(data & kMPresentBatt_A_Bit) {
fBatteryGate->runAction(OSMemberFunctionCast(IOCommandGate::Action,
fBattery, &AppleSmartBattery::handleBatteryInserted),
NULL, NULL, NULL, NULL);
} else {
fBatteryGate->runAction(OSMemberFunctionCast(IOCommandGate::Action,
fBattery, &AppleSmartBattery::handleBatteryRemoved),
NULL, NULL, NULL, NULL);
}
} else {
fBatteryGate->runAction(OSMemberFunctionCast(IOCommandGate::Action,
fBattery, &AppleSmartBattery::pollBatteryState),
NULL, NULL, NULL, NULL);
}
}
return kIOReturnSuccess;
}
IOReturn AppleSmartBatteryManager::inhibitCharging(int level)
{
IOReturn ret = kIOReturnSuccess;
if(!fManagerGate) return kIOReturnInternalError;
this->setProperty("Charging Inhibited", level ? true : false);
fManagerGate->runAction(OSMemberFunctionCast(IOCommandGate::Action,
this, &AppleSmartBatteryManager::gatedSendCommand),
(void *)kInhibitChargingCmd, (void *)level,
(void *)&ret, NULL);
return ret;
}
IOReturn AppleSmartBatteryManager::disableInflow(int level)
{
IOReturn ret = kIOReturnSuccess;
if(!fManagerGate) return kIOReturnInternalError;
this->setProperty("Inflow Disabled", level ? true : false);
fManagerGate->runAction(OSMemberFunctionCast(IOCommandGate::Action,
this, &AppleSmartBatteryManager::gatedSendCommand),
(void *)kDisableInflowCmd, (void *)level,
(void *)&ret, NULL);
return ret;
}
void AppleSmartBatteryManager::handleFullDischarge(void)
{
IOPMrootDomain *root_domain = getPMRootDomain();
IOReturn ret;
void * messageArgument = NULL;
if( getProperty("Inflow Disabled") )
{
messageArgument = (void *)kInflowForciblyEnabledBit;
fManagerGate->runAction(OSMemberFunctionCast(IOCommandGate::Action,
this, &AppleSmartBatteryManager::gatedSendCommand),
(void *)kDisableInflowCmd, (void *)0,
(void *)&ret, NULL);
}
if(root_domain) {
root_domain->messageClients(
kIOPMMessageInternalBatteryFullyDischarged,
messageArgument );
}
}
bool AppleSmartBatteryManager::requestExclusiveSMBusAccess(
bool request)
{
if( request && fExclusiveUserClient) {
return false;
}
fExclusiveUserClient = request;
fBatteryGate->runAction(
OSMemberFunctionCast(
IOCommandGate::Action, this,
&AppleSmartBattery::handleExclusiveAccess),
(void *)request, NULL, NULL, NULL);
return true;
}
void AppleSmartBatteryManager::gatedSendCommand(
int cmd,
int level,
IOReturn *ret_code)
{
if (fExclusiveUserClient) {
*ret_code = kIOReturnExclusiveAccess;
return;
}
*ret_code = kIOReturnError;
bzero(&fTransaction, sizeof(IOSMBusTransaction));
fTransaction.protocol = kIOSMBusProtocolWriteWord;
fTransaction.sendDataCount = 2;
if( kDisableInflowCmd == cmd)
{
fTransaction.address = kSMBusManagerAddr;
fTransaction.command = kMStateContCmd;
if( 0 != level ) {
fTransaction.sendData[0] = kMCalibrateBit;
} else {
fTransaction.sendData[0] = 0x0;
}
}
if( kInhibitChargingCmd == cmd)
{
fTransaction.address = kSMBusChargerAddr;
fTransaction.command = kBChargingCurrentCmd;
if( 0 != level ) {
fTransaction.sendData[0] = 0x0;
} else {
fTransaction.sendData[0] = 0x70;
fTransaction.sendData[1] = 0x17;
}
}
*ret_code = fProvider->performTransaction(
&fTransaction,
OSMemberFunctionCast( IOSMBusTransactionCompletion,
this, &AppleSmartBatteryManager::transactionCompletion),
(OSObject *)this);
return;
}
bool AppleSmartBatteryManager::transactionCompletion(
void *ref,
IOSMBusTransaction *transaction)
{
if (ref == this) {
if( kIOSMBusStatusOK == transaction->status )
{
if( (kMStateContCmd == transaction->command)
&& (kSMBusManagerAddr == transaction->address) )
{
fBattery->handleInflowDisabled(
transaction->sendData[0] ? true : false);
}
if( (kBChargingCurrentCmd == transaction->command)
&& (kSMBusChargerAddr == transaction->address) )
{
fBattery->handleChargeInhibited(
(transaction->sendData[0] | transaction->sendData[1])
? false : true);
}
} else {
BattLog("AppleSmartBatteryManager::transactionCompletion:"\
"ERROR 0x%08x!\n", transaction->status);
}
} else {
BattLog("completion: commandWaking: 0x%08x\n", transaction);
fManagerGate->commandWakeup( transaction, false);
}
return false;
}
void BattLog(const char *fmt, ...)
{
#if 0
va_list listp;
char buf[128];
va_start(listp, fmt);
vsnprintf(buf, sizeof(buf), fmt, listp);
va_end(listp);
kprintf("BattLog: %s", buf);
return;
#endif
}