summaryrefslogtreecommitdiffstats
path: root/src/detection/battery/battery_apple.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/detection/battery/battery_apple.c')
-rw-r--r--src/detection/battery/battery_apple.c117
1 files changed, 117 insertions, 0 deletions
diff --git a/src/detection/battery/battery_apple.c b/src/detection/battery/battery_apple.c
new file mode 100644
index 0000000..c22aa6d
--- /dev/null
+++ b/src/detection/battery/battery_apple.c
@@ -0,0 +1,117 @@
+#include "fastfetch.h"
+#include "battery.h"
+#include "common/apple/cf_helpers.h"
+#include "common/apple/smc_temps.h"
+
+#include <IOKit/IOKitLib.h>
+#include <IOKit/pwr_mgt/IOPM.h>
+
+const char* ffDetectBattery(FFBatteryOptions* options, FFlist* results) {
+ FF_IOOBJECT_AUTO_RELEASE io_iterator_t iterator = IO_OBJECT_NULL;
+ if (IOServiceGetMatchingServices(MACH_PORT_NULL, IOServiceMatching("AppleSmartBattery"), &iterator) != kIOReturnSuccess) {
+ return "IOServiceGetMatchingServices() failed";
+ }
+
+ io_registry_entry_t registryEntry;
+ while ((registryEntry = IOIteratorNext(iterator)) != IO_OBJECT_NULL) {
+ FF_IOOBJECT_AUTO_RELEASE io_registry_entry_t entryBattery = registryEntry;
+ FF_CFTYPE_AUTO_RELEASE CFMutableDictionaryRef properties = NULL;
+ if (IORegistryEntryCreateCFProperties(entryBattery, &properties, kCFAllocatorDefault, kNilOptions) != kIOReturnSuccess) {
+ continue;
+ }
+
+ int currentCapacity, maxCapacity;
+
+ if (ffCfDictGetInt(properties, CFSTR(kIOPMPSMaxCapacityKey), &maxCapacity) != NULL || maxCapacity <= 0) {
+ continue;
+ }
+
+ if (ffCfDictGetInt(properties, CFSTR(kIOPMPSCurrentCapacityKey), &currentCapacity) != NULL || currentCapacity <= 0) {
+ continue;
+ }
+
+ bool boolValue;
+
+ FFBatteryResult* battery = FF_LIST_ADD(FFBatteryResult, *results);
+ battery->temperature = FF_BATTERY_TEMP_UNSET;
+ ffStrbufInit(&battery->manufacturer);
+ ffStrbufInit(&battery->modelName);
+ ffStrbufInit(&battery->serial);
+ ffStrbufInit(&battery->technology);
+ ffStrbufInit(&battery->manufactureDate);
+ battery->status = FF_BATTERY_STATUS_NONE;
+ battery->capacity = currentCapacity * 100.0 / maxCapacity;
+ battery->cycleCount = 0;
+ battery->timeRemaining = -1;
+
+ ffCfDictGetString(properties, CFSTR(kIOPMDeviceNameKey), &battery->modelName);
+ ffCfDictGetString(properties, CFSTR(kIOPMPSSerialKey), &battery->serial);
+ ffCfDictGetString(properties, CFSTR(kIOPMPSManufacturerKey), &battery->manufacturer);
+
+ if (!ffCfDictGetBool(properties, CFSTR("built-in"), &boolValue) && boolValue) {
+ if (!battery->manufacturer.length) {
+ ffStrbufAppendS(&battery->manufacturer, "Apple Inc.");
+ }
+ ffStrbufAppendS(&battery->technology, "Lithium");
+ if (!battery->modelName.length) {
+ ffStrbufAppendS(&battery->modelName, "Built-in");
+ }
+ }
+
+ int32_t cycleCount = 0;
+ ffCfDictGetInt(properties, CFSTR(kIOPMPSCycleCountKey), &cycleCount);
+ battery->cycleCount = cycleCount < 0 ? 0 : (uint32_t) cycleCount;
+
+ battery->timeRemaining = -1;
+ if (ffCfDictGetBool(properties, CFSTR(kIOPMPSExternalConnectedKey), &boolValue) == NULL) {
+ if (boolValue) {
+ battery->status |= FF_BATTERY_STATUS_AC_CONNECTED;
+ } else {
+ battery->status |= FF_BATTERY_STATUS_DISCHARGING;
+ ffCfDictGetInt(properties, CFSTR("AvgTimeToEmpty"), &battery->timeRemaining); // in minutes
+ if (battery->timeRemaining < 0 || battery->timeRemaining >= 0xFFFF) {
+ battery->timeRemaining = -1;
+ } else {
+ battery->timeRemaining *= 60;
+ }
+ }
+ }
+ if (ffCfDictGetBool(properties, CFSTR(kIOPMPSIsChargingKey), &boolValue) == NULL && boolValue) {
+ battery->status |= FF_BATTERY_STATUS_CHARGING;
+ }
+ if (ffCfDictGetBool(properties, CFSTR(kIOPMPSAtCriticalLevelKey), &boolValue) == NULL && boolValue) {
+ battery->status |= FF_BATTERY_STATUS_CRITICAL;
+ }
+
+ int sbdsManufactureDate = 0;
+ if (ffCfDictGetInt(properties, CFSTR(kIOPMPSManufactureDateKey), &sbdsManufactureDate) == NULL) {
+ int day = sbdsManufactureDate & 0b11111;
+ int month = (sbdsManufactureDate >> 5) & 0b1111;
+ int year = (sbdsManufactureDate >> 9) + 1800;
+ ffStrbufSetF(&battery->manufactureDate, "%.4d-%.2d-%.2d", year, month, day);
+ } else {
+ CFDictionaryRef batteryData;
+ if (ffCfDictGetDict(properties, CFSTR("BatteryData"), &batteryData) == NULL) {
+ char manufactureDate[sizeof(uint64_t)];
+ if (ffCfDictGetInt64(batteryData, CFSTR(kIOPMPSManufactureDateKey), (int64_t*) manufactureDate) == NULL) {
+ // https://github.com/AsahiLinux/linux/blob/b5c05cbffb0488c7618106926d522cc3b43d93d5/drivers/power/supply/macsmc_power.c#L410-L419
+ int year = (manufactureDate[0] - '0') * 10 + (manufactureDate[1] - '0') + 2000 - 8;
+ int month = (manufactureDate[2] - '0') * 10 + (manufactureDate[3] - '0');
+ int day = (manufactureDate[4] - '0') * 10 + (manufactureDate[3] - '0');
+ ffStrbufSetF(&battery->manufactureDate, "%.4d-%.2d-%.2d", year, month, day);
+ }
+ }
+ }
+
+ if (options->temp) {
+ int64_t temp;
+ if (!ffCfDictGetInt64(properties, CFSTR(kIOPMPSBatteryTemperatureKey), &temp)) {
+ battery->temperature = (double) temp / 10 - 273.15;
+ } else {
+ ffDetectSmcTemps(FF_TEMP_BATTERY, &battery->temperature);
+ }
+ }
+ }
+
+ return NULL;
+}