master ohmu/fastfetch-openbsd / src / detection / battery / battery_sunos.c
ohmu  ·  2026-10-15
  1#include "battery.h"
  2#include "common/debug.h"
  3
  4#include <kstat.h>
  5
  6static inline void kstatFreeWrap(kstat_ctl_t** pkc) {
  7    assert(pkc);
  8    if (*pkc) {
  9        kstat_close(*pkc);
 10    }
 11}
 12
 13static bool getUint32(kstat_t* ksp, const char* name, uint32_t* value) {
 14    kstat_named_t* stat = kstat_data_lookup(ksp, name);
 15    if (!stat) {
 16        return false;
 17    }
 18
 19    switch (stat->data_type) {
 20        case KSTAT_DATA_UINT32:
 21            *value = stat->value.ui32;
 22            return true;
 23        case KSTAT_DATA_INT32:
 24            if (stat->value.i32 >= 0) {
 25                *value = (uint32_t) stat->value.i32;
 26                return true;
 27            }
 28            break;
 29#ifdef _INT64_TYPE
 30        case KSTAT_DATA_UINT64:
 31            if (stat->value.ui64 <= UINT32_MAX) {
 32                *value = (uint32_t) stat->value.ui64;
 33                return true;
 34            }
 35            break;
 36        case KSTAT_DATA_INT64:
 37            if (stat->value.i64 >= 0 && (uint64_t) stat->value.i64 <= UINT32_MAX) {
 38                *value = (uint32_t) stat->value.i64;
 39                return true;
 40            }
 41            break;
 42#endif
 43        default:
 44            break;
 45    }
 46
 47    return false;
 48}
 49
 50const char* ffDetectBattery([[maybe_unused]] FFBatteryOptions* options, FFlist* results) {
 51    // https://github.com/illumos/illumos-gate/blob/master/usr/src/cmd/powertop/common/battery.c
 52    [[gnu::cleanup(kstatFreeWrap)]] kstat_ctl_t* kc = kstat_open();
 53    if (!kc) {
 54        FF_DEBUG("kstat_open() failed");
 55        return "kstat_open() failed";
 56    }
 57
 58    // illumos exposes battery information through either of these modules.
 59    const char* modules[] = { "battery", "acpi_drv" };
 60    kstat_t* bif = nullptr;
 61    kstat_t* bst = nullptr;
 62    for (size_t i = 0; i < ARRAY_SIZE(modules); ++i) {
 63        bif = kstat_lookup(kc, modules[i], 0, "battery BIF0");
 64        bst = kstat_lookup(kc, modules[i], 0, "battery BST0");
 65        if (bif && bst) {
 66            break;
 67        }
 68        bif = nullptr;
 69        bst = nullptr;
 70    }
 71
 72    if (!bif || !bst || kstat_read(kc, bif, nullptr) < 0 || kstat_read(kc, bst, nullptr) < 0) {
 73        return nullptr;
 74    }
 75
 76    uint32_t lastCapacity, remainingCapacity, rate, state;
 77    if (!getUint32(bif, "bif_last_cap", &lastCapacity) ||
 78        !getUint32(bst, "bst_rem_cap", &remainingCapacity) ||
 79        !getUint32(bst, "bst_state", &state)) {
 80        FF_DEBUG("kstat_data_lookup() failed for bif_last_cap, bst_rem_cap or bst_state");
 81        return "kstat_data_lookup() failed";
 82    }
 83
 84    if (lastCapacity == 0 || remainingCapacity == UINT32_MAX) {
 85        return nullptr;
 86    }
 87
 88    FFBatteryResult* battery = FF_LIST_ADD(FFBatteryResult, *results);
 89    ffStrbufInit(&battery->manufacturer);
 90    ffStrbufInit(&battery->manufactureDate);
 91    ffStrbufInit(&battery->modelName);
 92    ffStrbufInit(&battery->technology);
 93    ffStrbufInit(&battery->serial);
 94    battery->status = FF_BATTERY_STATUS_NONE;
 95    battery->capacity = (double) remainingCapacity * 100.0 / (double) lastCapacity;
 96    battery->temperature = FF_BATTERY_TEMP_UNSET;
 97    battery->cycleCount = 0;
 98    battery->timeRemaining = -1;
 99
100    // illumos BST state: bit 0 discharging, bit 1 charging, bit 2 critical.
101    if (state & (1u << 0)) {
102        battery->status |= FF_BATTERY_STATUS_DISCHARGING;
103    }
104    if (state & (1u << 1)) {
105        battery->status |= FF_BATTERY_STATUS_CHARGING;
106    }
107    if (state & (1u << 2)) {
108        battery->status |= FF_BATTERY_STATUS_CRITICAL;
109    }
110
111    if ((state & (1u << 0)) && getUint32(bst, "bst_rate", &rate) && rate > 0 && rate != UINT32_MAX) {
112        battery->timeRemaining = (int32_t) (((uint64_t) remainingCapacity * 3600) / rate);
113    }
114
115    return nullptr;
116}