BatteryTimeRemaining.c [plain text]
#include <syslog.h>
#include <unistd.h>
#include <stdlib.h>
#include <notify.h>
#include <mach/mach.h>
#include <mach/mach_port.h>
#include <servers/bootstrap.h>
#include <asl.h>
#include "powermanagementServer.h" // mig generated
#include "BatteryTimeRemaining.h"
#include "PMAssertions.h"
#include "PrivateLib.h"
#include "PMStore.h"
#ifndef kIOPSFailureKey
#define kIOPSFailureKey "Failure"
#endif
#define kBatteryPermFailureString "Permanent Battery Failure"
#ifndef kIOPMBatteryPercentageFactors
#define kIOPMBatteryPercentageFactors CFSTR("IOPMBatteryPercentageFactors")
#endif
#ifndef kIOPSDynamicStorePowerAdapterKey
#define kIOPSDynamicStorePowerAdapterKey "/IOKit/PowerAdapter"
#endif
#ifndef kIOPSDynamicStoreLowBattPathKey
#define kIOPSDynamicStoreLowBattPathKey "/IOKit/LowBatteryWarning"
#endif
#ifndef kIOPSDynamicStoreLowBattPathKey
#define kIOPSDynamicStoreLowBattPathKey "/IOKit/LowBatteryWarning"
#endif
extern CFMachPortRef pmServerMachPort;
extern CFMutableSetRef _publishedBatteryKeysSet;
int _showWhichBatteries = kBatteryShowReal;
typedef struct {
mach_port_t connection;
CFStringRef scdsKey;
} OpaqueIOPSPowerSourceID;
#define kPSMaxTrackedPowerSources 20
static OpaqueIOPSPowerSourceID gPSList[kPSMaxTrackedPowerSources];
typedef OpaqueIOPSPowerSourceID *PSTracker;
#define kSlewStepMin 2
#define kSlewStepMax 10
#define kDiscontinuitySettle 60
static CFAbsoluteTime lastDiscontinuity;
typedef struct {
int showingTime;
bool settled;
} SlewStruct;
SlewStruct *slew = NULL;
enum {
kNothingToSeeHere = 0,
kNoTimeEstimate,
};
#define kSmartBattReserve_mAh 200.0
#define kMaxBattMinutes 1200
static int _systemBatteryWarningLevel = 0;
static bool _warningsShouldResetForSleep = false;
static bool _readACAdapterAgain = false;
static bool _batterySelectionHasSwitched = false;
static int _psTimeRemainingNotifyToken = 0;
static void _initializeBatteryCalculations(void);
static int _populateTimeRemaining(IOPMBattery **batts);
static void _packageBatteryInfo(CFDictionaryRef *);
static void _discontinuityOccurred(void);
static IOReturn _readAndPublishACAdapter(bool, CFDictionaryRef);
__private_extern__ void
BatteryTimeRemaining_prime(void)
{
bzero(gPSList, sizeof(gPSList));
_initializeBatteryCalculations();
notify_register_check(kIOPSTimeRemainingNotificationKey, &_psTimeRemainingNotifyToken);
return;
}
__private_extern__ void
BatteryTimeRemainingSleepWakeNotification(natural_t messageType)
{
if (kIOMessageSystemWillPowerOn == messageType)
{
_warningsShouldResetForSleep = true;
_readACAdapterAgain = true;
BatteryTimeRemainingBatteriesHaveChanged(NULL);
}
}
__private_extern__ void
BatteryTimeRemainingRTCDidResync(void)
{
_discontinuityOccurred();
}
static void _discontinuityOccurred(void)
{
if (slew) {
bzero(slew, sizeof(SlewStruct));
}
lastDiscontinuity = CFAbsoluteTimeGetCurrent();
}
static void _initializeBatteryCalculations(void)
{
if (_batteryCount() == 0) {
return;
}
lastDiscontinuity = CFAbsoluteTimeGetCurrent();
BatteryTimeRemainingBatteriesHaveChanged(_batteries());
return;
}
__private_extern__ void switchActiveBatterySet(int which)
{
_batterySelectionHasSwitched = true;
if (kBatteryShowReal == which) {
_showWhichBatteries = kBatteryShowReal;
} else if (kBatteryShowFake == which) {
_showWhichBatteries = kBatteryShowFake;
}
BatteryTimeRemainingBatteriesHaveChanged(NULL);
}
__private_extern__ void
BatteryTimeRemainingBatteriesHaveChanged(IOPMBattery **batteries)
{
static CFStringRef lowBatteryKey = NULL;
static CFDictionaryRef *old_battery = NULL;
CFDictionaryRef *result = NULL;
int i;
int batCount = _batteryCount();
IOPMBattery *b = NULL;
bool external = false;
static bool _lastExternal = false;
if (_batterySelectionHasSwitched)
{
CFStringRef *publishedKeys = NULL;
int publishedKeysCount = 0;
int i;
if (_publishedBatteryKeysSet)
{
publishedKeysCount = CFSetGetCount(_publishedBatteryKeysSet);
if (0 < publishedKeysCount) {
publishedKeys = calloc(publishedKeysCount, sizeof(void *));
CFSetGetValues(_publishedBatteryKeysSet, (const void **)publishedKeys);
for (i=0; i<publishedKeysCount; i++) {
PMStoreRemoveValue(publishedKeys[i]);
}
free(publishedKeys);
}
}
if (old_battery) {
for (i=0; i<batCount; i++) {
CFRelease(old_battery[i]);
}
free(old_battery);
old_battery = NULL;
}
_batterySelectionHasSwitched = false;
}
if (0 == _batteryCount()) {
return;
}
if (!batteries) {
batteries = _batteries();
}
result = (CFDictionaryRef *) calloc(1, batCount * sizeof(CFDictionaryRef));
if ( NULL == old_battery ) {
old_battery = (CFDictionaryRef *) calloc(1, batCount * sizeof(CFDictionaryRef));
}
if ( NULL == old_battery
|| NULL == result ) {
return;
}
b = batteries[0];
if (b->externalConnected) {
external = true;
}
if (_lastExternal != external) {
_discontinuityOccurred();
}
_lastExternal = external;
_readAndPublishACAdapter(external, CFDictionaryGetValue(b->properties, CFSTR(kIOPMPSAdapterDetailsKey)));
b->isTimeRemainingUnknown = !_populateTimeRemaining(batteries);
int combinedTime = 0;
int newWarningLevel = 0;
int combinedLevel = 0;
for(i=0; i<batCount; i++)
{
b = batteries[i];
if (b->isPresent) {
if (0 != b->maxCap) {
combinedLevel += (100 * b->currentCap)/b->maxCap;
}
combinedTime += b->swCalculatedTR;
}
}
if (_warningsShouldResetForSleep)
{
_warningsShouldResetForSleep = false;
_systemBatteryWarningLevel = 0;
newWarningLevel = kIOPSLowBatteryWarningNone;
} else if (b->externalConnected || !b->isPresent)
{
newWarningLevel = kIOPSLowBatteryWarningNone;
} else if ((combinedLevel > 0) && (combinedTime > 0))
{
if ( (combinedLevel >= 22)
&& (_systemBatteryWarningLevel != kIOPSLowBatteryWarningEarly)
&& (_systemBatteryWarningLevel != kIOPSLowBatteryWarningFinal))
{
newWarningLevel = kIOPSLowBatteryWarningNone;
} else if (combinedTime < 10) {
newWarningLevel = kIOPSLowBatteryWarningFinal;
} else if (_systemBatteryWarningLevel != kIOPSLowBatteryWarningFinal)
{
newWarningLevel = kIOPSLowBatteryWarningEarly;
}
}
_packageBatteryInfo(result);
if ( (newWarningLevel != _systemBatteryWarningLevel)
&& (0 != newWarningLevel) )
{
CFNumberRef newWarningLevelNumber =
CFNumberCreate(0, kCFNumberIntType, &newWarningLevel);
if (newWarningLevelNumber)
{
if (!lowBatteryKey) {
lowBatteryKey = SCDynamicStoreKeyCreate(
kCFAllocatorDefault, CFSTR("%@%@"),
kSCDynamicStoreDomainState, CFSTR(kIOPSDynamicStoreLowBattPathKey));
}
PMStoreSetValue(lowBatteryKey, newWarningLevelNumber );
CFRelease(newWarningLevelNumber);
notify_post( kIOPSNotifyLowBattery );
}
_systemBatteryWarningLevel = newWarningLevel;
}
#define _kPSTimeRemainingNotifyExternalBit (1 << 16)
#define _kPSTimeRemainingNotifyChargingBit (1 << 17)
#define _kPSTimeRemainingNotifyUnknownBit (1 << 18)
#define _kPSTimeRemainingNotifyValidBit (1 << 19)
uint64_t powerSourcesBitsForNotify = (uint64_t)(combinedTime & 0xFFFF);
static uint64_t lastPSBitsNotify = 0;
powerSourcesBitsForNotify |= _kPSTimeRemainingNotifyValidBit;
if (external) {
powerSourcesBitsForNotify |= _kPSTimeRemainingNotifyExternalBit;
}
if (batteries[0]->isTimeRemainingUnknown) {
powerSourcesBitsForNotify |= _kPSTimeRemainingNotifyUnknownBit;
}
if (batteries[0]->isCharging) {
powerSourcesBitsForNotify |= _kPSTimeRemainingNotifyChargingBit;
}
if (lastPSBitsNotify != powerSourcesBitsForNotify)
{
lastPSBitsNotify = powerSourcesBitsForNotify;
notify_set_state(_psTimeRemainingNotifyToken, powerSourcesBitsForNotify);
notify_post(kIOPSTimeRemainingNotificationKey);
}
for(i=0; i<batCount; i++)
{
if(result[i])
{
if( !old_battery[i] || !CFEqual(old_battery[i], result[i]))
{
PMStoreSetValue(batteries[i]->dynamicStoreKey, result[i]);
}
if (old_battery[i]) {
CFRelease(old_battery[i]);
}
old_battery[i] = result[i];
}
}
if(result) {
free(result);
}
}
static int slewTime(int hw)
{
if (!slew) {
slew = calloc(1, sizeof(SlewStruct));
}
if (!slew->settled)
{
if (CFAbsoluteTimeGetCurrent() >= (lastDiscontinuity + kDiscontinuitySettle)) {
slew->settled = true;
slew->showingTime = hw;
} else {
slew->showingTime = -1;
}
} else {
int step = 0;
step = slew->showingTime ? (slew->showingTime/10) : kSlewStepMax;
if (step > kSlewStepMax) {
step = kSlewStepMax;
} else if (step < kSlewStepMin) {
step = kSlewStepMin;
}
if (slew->showingTime == hw) {
} else if (abs(slew->showingTime - hw) < step) {
slew->showingTime = hw;
} else if (slew->showingTime > hw) {
slew->showingTime -= step;
} else if (slew->showingTime < hw) {
slew->showingTime += step;
}
}
return slew->showingTime;
}
static int _populateTimeRemaining(IOPMBattery **batts)
{
int i;
IOPMBattery *b;
int batCount = _batteryCount();
double lowerAmperageBound;
double upperAmperageBound;
double absValAvgCurrent;
double absValInstantCurrent;
for(i=0; i<batCount; i++)
{
b = batts[i];
absValAvgCurrent = abs(b->avgAmperage);
absValInstantCurrent = abs(b->instantAmperage);
if (_batteryHas(b, CFSTR("InstantAmperage")))
{
lowerAmperageBound = (double) absValInstantCurrent * 0.5;
upperAmperageBound = (double) absValInstantCurrent * 2.0;
} else {
lowerAmperageBound = 5;
upperAmperageBound = 15000;
}
if( (0 == b->avgAmperage)
|| (absValAvgCurrent < lowerAmperageBound)
|| (absValAvgCurrent > upperAmperageBound))
{
b->swCalculatedTR = -1;
continue;
}
b->swCalculatedTR = slewTime(b->hwAverageTR);
if (b->swCalculatedTR < 0) {
b->swCalculatedTR = -1;
}
if (kMaxBattMinutes < b->swCalculatedTR) {
b->swCalculatedTR = kMaxBattMinutes;
}
}
return (-1 != batts[0]->swCalculatedTR);
}
void _setBatteryHealthConfidence(
CFMutableDictionaryRef outDict,
IOPMBattery *b)
{
CFMutableArrayRef permanentFailures = NULL;
if(!outDict || !b || !b->isPresent)
return;
if ( 0!= b->pfStatus) {
permanentFailures = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
if (!permanentFailures)
return;
if (kSmartBattPFExternalInput & b->pfStatus) {
CFArrayAppendValue( permanentFailures, CFSTR(kIOPSFailureExternalInput) );
}
if (kSmartBattPFSafetyOverVoltage & b->pfStatus) {
CFArrayAppendValue( permanentFailures, CFSTR(kIOPSFailureSafetyOverVoltage) );
}
if (kSmartBattPFChargeSafeOverTemp & b->pfStatus) {
CFArrayAppendValue( permanentFailures, CFSTR(kIOPSFailureChargeOverTemp) );
}
if (kSmartBattPFDischargeSafeOverTemp & b->pfStatus) {
CFArrayAppendValue( permanentFailures, CFSTR(kIOPSFailureDischargeOverTemp) );
}
if (kSmartBattPFCellImbalance & b->pfStatus) {
CFArrayAppendValue( permanentFailures, CFSTR(kIOPSFailureCellImbalance) );
}
if (kSmartBattPFChargeFETFailure & b->pfStatus) {
CFArrayAppendValue( permanentFailures, CFSTR(kIOPSFailureChargeFET) );
}
if (kSmartBattPFDischargeFETFailure & b->pfStatus) {
CFArrayAppendValue( permanentFailures, CFSTR(kIOPSFailureDischargeFET) );
}
if (kSmartBattPFDataFlushFault & b->pfStatus) {
CFArrayAppendValue( permanentFailures, CFSTR(kIOPSFailureDataFlushFault) );
}
if (kSmartBattPFPermanentAFECommFailure & b->pfStatus) {
CFArrayAppendValue( permanentFailures, CFSTR(kIOPSFailurePermanentAFEComms) );
}
if (kSmartBattPFPeriodicAFECommFailure & b->pfStatus) {
CFArrayAppendValue( permanentFailures, CFSTR(kIOPSFailurePeriodicAFEComms) );
}
if (kSmartBattPFChargeSafetyOverCurrent & b->pfStatus) {
CFArrayAppendValue( permanentFailures, CFSTR(kIOPSFailureChargeOverCurrent) );
}
if (kSmartBattPFDischargeSafetyOverCurrent & b->pfStatus) {
CFArrayAppendValue( permanentFailures, CFSTR(kIOPSFailureDischargeOverCurrent) );
}
if (kSmartBattPFOpenThermistor & b->pfStatus) {
CFArrayAppendValue( permanentFailures, CFSTR(kIOPSFailureOpenThermistor) );
}
if (kSmartBattPFFuseBlown & b->pfStatus) {
CFArrayAppendValue( permanentFailures, CFSTR(kIOPSFailureFuseBlown) );
}
CFDictionarySetValue( outDict, CFSTR(kIOPSBatteryFailureModesKey), permanentFailures);
CFRelease(permanentFailures);
}
if (_batteryHas(b, CFSTR(kIOPMPSErrorConditionKey)))
{
if (CFEqual(b->failureDetected, CFSTR(kBatteryPermFailureString)))
{
CFDictionarySetValue(outDict,
CFSTR(kIOPSBatteryHealthKey), CFSTR(kIOPSPoorValue));
CFDictionarySetValue(outDict,
CFSTR(kIOPSHealthConfidenceKey), CFSTR(kIOPSGoodValue));
CFDictionarySetValue(outDict,
CFSTR(kIOPSBatteryHealthConditionKey), CFSTR(kIOPSPermanentFailureValue));
return;
}
}
double compareRatioTo = 0.80;
double capRatio = 1.0;
if (0 != b->designCap)
{
capRatio = ((double)b->maxCap + kSmartBattReserve_mAh) / (double)b->designCap;
}
bool cyclesExceedStandard = false;
if (b->markedDeclining) {
compareRatioTo = 0.83;
} else {
compareRatioTo = 0.80;
}
if (capRatio > 1.5) {
CFDictionarySetValue(outDict, CFSTR(kIOPSBatteryHealthKey), CFSTR(kIOPSPoorValue));
CFDictionarySetValue(outDict, CFSTR(kIOPSBatteryHealthConditionKey), CFSTR(kIOPSPermanentFailureValue));
} else if (capRatio >= compareRatioTo) {
b->markedDeclining = 0;
CFDictionarySetValue(outDict, CFSTR(kIOPSBatteryHealthKey), CFSTR(kIOPSGoodValue));
} else {
b->markedDeclining = 1;
if (cyclesExceedStandard) {
if (capRatio >= 0.50)
{
CFDictionarySetValue(outDict, CFSTR(kIOPSBatteryHealthKey), CFSTR(kIOPSFairValue));
} else {
CFDictionarySetValue(outDict, CFSTR(kIOPSBatteryHealthKey), CFSTR(kIOPSPoorValue));
}
CFDictionarySetValue(outDict, CFSTR(kIOPSBatteryHealthConditionKey), CFSTR(kIOPSCheckBatteryValue));
} else {
CFDictionarySetValue(outDict, CFSTR(kIOPSBatteryHealthKey), CFSTR(kIOPSCheckBatteryValue));
}
}
return;
}
void _packageBatteryInfo(CFDictionaryRef *ret)
{
CFNumberRef n, n0;
CFMutableDictionaryRef mutDict = NULL;
int i;
int temp;
int minutes;
int set_capacity, set_charge;
bool is_charged;
IOPMBattery *b;
IOPMBattery **batts = _batteries();
int batCount = _batteryCount();
for(i=0; i<batCount; i++)
{
b = batts[i];
mutDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if(!mutDict)
return;
CFDictionarySetValue(mutDict, CFSTR("Battery Provides Time Remaining"), kCFBooleanTrue);
if (b->failureDetected) {
CFDictionarySetValue(mutDict, CFSTR(kIOPSFailureKey), b->failureDetected);
}
if (b->chargeStatus) {
CFDictionarySetValue(mutDict, CFSTR(kIOPMPSBatteryChargeStatusKey), b->chargeStatus);
}
if (b->batterySerialNumber) {
CFDictionarySetValue(mutDict, CFSTR(kIOPSHardwareSerialNumberKey), b->batterySerialNumber);
}
CFDictionarySetValue(mutDict, CFSTR(kIOPSTransportTypeKey), CFSTR(kIOPSInternalType));
CFDictionarySetValue(mutDict, CFSTR(kIOPSTypeKey), CFSTR(kIOPSInternalBatteryType));
CFDictionarySetValue(mutDict, CFSTR(kIOPSPowerSourceStateKey),
(b->externalConnected ? CFSTR(kIOPSACPowerValue):CFSTR(kIOPSBatteryPowerValue)));
if(0 != b->maxCap)
{
set_capacity = 100;
set_charge = (int)lround((double)b->currentCap*100.0/(double)b->maxCap);
if( (100 == set_charge) && b->isCharging)
{
set_charge = 99;
}
} else {
set_capacity = set_charge = 0;
}
n = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &set_capacity);
if(n) {
CFDictionarySetValue(mutDict, CFSTR(kIOPSMaxCapacityKey), n);
CFRelease(n);
}
n = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &set_charge);
if(n) {
CFDictionarySetValue(mutDict, CFSTR(kIOPSCurrentCapacityKey), n);
CFRelease(n);
}
CFDictionarySetValue(mutDict, CFSTR(kIOPSIsPresentKey),
b->isPresent ? kCFBooleanTrue:kCFBooleanFalse);
minutes = b->swCalculatedTR;
temp = 0;
n0 = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &temp);
if( !b->isPresent ) {
CFDictionarySetValue(mutDict, CFSTR(kIOPSIsChargingKey), kCFBooleanFalse);
CFDictionarySetValue(mutDict, CFSTR(kIOPSTimeToFullChargeKey), n0);
CFDictionarySetValue(mutDict, CFSTR(kIOPSTimeToEmptyKey), n0);
} else {
if(b->isCharging) {
CFDictionarySetValue(mutDict, CFSTR(kIOPSIsChargingKey), kCFBooleanTrue);
CFDictionarySetValue(mutDict, CFSTR(kIOPSIsFinishingChargeKey),
(b->maxCap && (99 <= (100*b->currentCap/b->maxCap))) ? kCFBooleanTrue:kCFBooleanFalse);
n = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &minutes);
if(n) {
CFDictionarySetValue(mutDict, CFSTR(kIOPSTimeToFullChargeKey), n);
CFRelease(n);
}
CFDictionarySetValue(mutDict, CFSTR(kIOPSTimeToEmptyKey), n0);
} else {
CFDictionarySetValue(mutDict, CFSTR(kIOPSIsChargingKey), kCFBooleanFalse);
if(b->externalConnected)
{
CFDictionarySetValue(mutDict, CFSTR(kIOPSTimeToFullChargeKey), n0);
CFDictionarySetValue(mutDict, CFSTR(kIOPSTimeToEmptyKey), n0);
if (0 != b->maxCap) {
is_charged = ((100*b->currentCap/b->maxCap) >= 95);
} else {
is_charged = false;
}
CFDictionarySetValue(mutDict, CFSTR(kIOPSIsChargedKey),
is_charged ? kCFBooleanTrue:kCFBooleanFalse);
} else {
n = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &minutes);
if(n) {
CFDictionarySetValue(mutDict, CFSTR(kIOPSTimeToEmptyKey), n);
CFRelease(n);
}
CFDictionarySetValue(mutDict, CFSTR(kIOPSTimeToFullChargeKey), n0);
}
}
}
CFRelease(n0);
_setBatteryHealthConfidence(mutDict, b);
if(b->name) {
CFDictionarySetValue(mutDict, CFSTR(kIOPSNameKey), b->name);
} else {
CFDictionarySetValue(mutDict, CFSTR(kIOPSNameKey), CFSTR("Unnamed"));
}
ret[i] = mutDict;
}
return;
}
#define kACCRCBit 56 // size 8
#define kACIDBit 44 // size 12
#define kACPowerBit 36 // size 8
#define kACRevisionBit 32 // size 4
#define kACSerialBit 8 // size 24
#define kACFamilyBit 0 // size 8
#define kACCurrentIdBit 48 // 8 bits
#define kACSourceIdBit 44 // 3 bits
#define kACVoltageIDBit 33 // 3 bits
#define k3BitMask 0x7
typedef struct {
uint32_t valCommEn;
uint32_t valVoltageID;
uint32_t valID;
uint32_t valPower;
uint32_t valRevision;
uint32_t valSerial;
uint32_t valFamily;
uint32_t valCurrent;
uint32_t valSource;
} AdapterAttributes;
static void stuffInt32(CFMutableDictionaryRef d, CFStringRef k, uint32_t n)
{
CFNumberRef stuffNum = NULL;
if ((stuffNum = CFNumberCreate(0, kCFNumberSInt32Type, &n)))
{
CFDictionarySetValue(d, k, stuffNum);
CFRelease(stuffNum);
}
}
static IOReturn _readAndPublishACAdapter(bool adapterExists, CFDictionaryRef batteryACDict)
{
static bool adapterInfoPublished = false;
static CFDictionaryRef oldACDict = NULL;
CFStringRef key = NULL;
CFMutableDictionaryRef acDict = NULL;
IOReturn ret = kIOReturnSuccess;
Boolean success = FALSE;
int j = 0;
AdapterAttributes info;
bzero(&info, sizeof(info));
if (_readACAdapterAgain) {
adapterInfoPublished = false;
_readACAdapterAgain = false;
}
if (adapterExists && batteryACDict && oldACDict && !CFEqual(oldACDict, batteryACDict))
{
adapterInfoPublished = false;
}
if (adapterExists && adapterInfoPublished)
{
return kIOReturnSuccess;
}
if (adapterExists && !batteryACDict)
{
uint64_t acBits;
ret = _getACAdapterInfo(&acBits);
if (kIOReturnSuccess != ret) {
return ret;
}
info.valID = (acBits >> kACIDBit) & 0xFFF;
info.valFamily = (acBits >> kACFamilyBit) & 0xFF;
info.valPower = (acBits >> kACPowerBit) & 0xFF;
if ( (info.valSource = (acBits >> kACSourceIdBit) & k3BitMask))
{
info.valSerial = (acBits >> kACSerialBit) & 0x1FFFFFF;
info.valCurrent = ((acBits >> kACCurrentIdBit) & 0xFF) * 25;
info.valVoltageID = (acBits >> kACVoltageIDBit) & k3BitMask;
} else {
info.valSerial = (acBits >> kACSerialBit) & 0xFFFFFF;
info.valRevision = (acBits >> kACRevisionBit) & 0xF;
}
acDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (!acDict) {
ret = kIOReturnNoMemory;
goto exit;
}
if (info.valSource) {
stuffInt32(acDict, CFSTR(kIOPSPowerAdapterCurrentKey), info.valCurrent);
stuffInt32(acDict, CFSTR(kIOPSPowerAdapterSourceKey), info.valSource);
}
else {
stuffInt32(acDict, CFSTR(kIOPSPowerAdapterRevisionKey), info.valRevision);
}
if (0 != info.valPower) {
stuffInt32(acDict, CFSTR(kIOPSPowerAdapterWattsKey), info.valPower);
}
stuffInt32(acDict, CFSTR(kIOPSPowerAdapterIDKey), info.valID);
stuffInt32(acDict, CFSTR(kIOPSPowerAdapterSerialNumberKey), info.valSerial);
stuffInt32(acDict, CFSTR(kIOPSPowerAdapterFamilyKey), info.valFamily);
batteryACDict = acDict;
}
key = SCDynamicStoreKeyCreate(
kCFAllocatorDefault,
CFSTR("%@%@"),
kSCDynamicStoreDomainState,
CFSTR(kIOPSDynamicStorePowerAdapterKey));
if (!key) {
ret = kIOReturnError;
goto exit;
}
if (oldACDict) {
CFRelease(oldACDict);
oldACDict = NULL;
}
if (!adapterExists) {
success = PMStoreRemoveValue(key);
adapterInfoPublished = false;
} else {
success = PMStoreSetValue(key, batteryACDict);
adapterInfoPublished = true;
oldACDict = batteryACDict;
CFRetain(oldACDict);
}
if (success)
ret = kIOReturnSuccess;
else
ret = kIOReturnLockedRead;
if (success)
notify_post("com.apple.system.powermanagement.poweradapter");
exit:
if (acDict)
CFRelease(acDict);
if (key)
CFRelease(key);
return ret;
}
static CFStringRef _copyNewKeyForType(char *type)
{
CFStringRef scKey = NULL;
CFStringRef typeString = NULL;
static const unsigned long long kCounterWrapAround = 100000;
static unsigned long long psCounter = 1000;
typeString = CFStringCreateWithCString(0, type, kCFStringEncodingMacRoman);
if (!typeString)
return NULL;
if (psCounter > kCounterWrapAround)
psCounter = 1000;
scKey = SCDynamicStoreKeyCreate(
kCFAllocatorDefault,
CFSTR("%@%@/%@-%ld"),
kSCDynamicStoreDomainState,
CFSTR(kIOPSDynamicStorePath),
typeString,
psCounter++);
CFRelease(typeString);
return scKey;
}
static IOReturn _new_psTracker(PSTracker *new_ps)
{
int i = 0;
*new_ps = NULL;
for (i=0; i<kPSMaxTrackedPowerSources; i++)
{
if (MACH_PORT_NULL == gPSList[i].connection)
break;
}
if (i >= kPSMaxTrackedPowerSources) {
return kIOReturnNoMemory;
}
*new_ps = &gPSList[i];
return kIOReturnSuccess;
}
static PSTracker _psTrackerForPort(mach_port_t target_port)
{
int i;
for (i=0; i<kPSMaxTrackedPowerSources; i++)
{
if (target_port == gPSList[i].connection)
break;
}
if (i >= kPSMaxTrackedPowerSources) {
return NULL;
}
return &gPSList[i];
}
__private_extern__ bool BatteryHandleDeadName(mach_port_t deadName)
{
PSTracker reap_me = _psTrackerForPort(deadName);
if (!reap_me) {
return false;
}
if (reap_me->scdsKey)
{
PMStoreRemoveValue(reap_me->scdsKey);
CFRelease(reap_me->scdsKey);
}
if (reap_me->connection != MACH_PORT_NULL)
{
__MACH_PORT_DEBUG(true, "_io_pm_new_pspowersource deadname", reap_me->connection);
mach_port_deallocate(mach_task_self(), reap_me->connection);
}
reap_me->scdsKey = NULL;
reap_me->connection = MACH_PORT_NULL;
return true;
}
kern_return_t _io_pm_new_pspowersource(
mach_port_t server,
mach_port_t clientport,
string_t clienttype, string_t dskey, int *result)
{
PSTracker new_tracker = NULL;
static const int kDSKeyMIGBufferSize = 1024;
mach_port_t oldNotify;
if (MACH_PORT_NULL == clientport
|| NULL == clienttype
|| NULL == result
|| NULL == dskey)
{
if (result) *result = kIOReturnBadArgument;
goto exit;
}
if (kIOReturnSuccess != _new_psTracker(&new_tracker))
{
*result = kIOReturnNoSpace;
goto exit;
}
__MACH_PORT_DEBUG(true, "_io_pm_new_pspowersource client", clientport);
new_tracker->connection = clientport;
mach_port_request_notification(
mach_task_self(), clientport, MACH_NOTIFY_DEAD_NAME, 1, CFMachPortGetPort(pmServerMachPort), MACH_MSG_TYPE_MAKE_SEND_ONCE, &oldNotify);
new_tracker->scdsKey = _copyNewKeyForType(clienttype);
if (new_tracker->scdsKey)
{
CFStringGetCString(new_tracker->scdsKey, (void *)dskey,
kDSKeyMIGBufferSize, kCFStringEncodingUTF8);
}
*result = kIOReturnSuccess;
exit:
__MACH_PORT_DEBUG(true, "_io_pm_new_pspowersource client - exit", clientport);
return KERN_SUCCESS;
}
kern_return_t _io_pm_update_pspowersource(
mach_port_t server __unused,
string_t dskey,
vm_offset_t details_ptr,
mach_msg_type_number_t details_len,
int *return_code)
{
CFStringRef dskeyCFSTR = NULL;
CFDictionaryRef details = NULL;
dskeyCFSTR = CFStringCreateWithCString(0, dskey, kCFStringEncodingUTF8);
if (!dskeyCFSTR)
goto exit;
details = IOCFUnserialize((const char *)details_ptr, NULL, 0, NULL);
if (!details)
goto exit;
if (isA_CFDictionary(details)) {
PMStoreSetValue(dskeyCFSTR, details);
*return_code = kIOReturnSuccess;
}
exit:
if (dskeyCFSTR)
CFRelease(dskeyCFSTR);
if (details)
CFRelease(details);
return 0;
}