^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) * Copyright © 2007 Anton Vorontsov <cbou@mail.ru>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * Copyright © 2007 Eugeny Boger <eugenyboger@dgap.mipt.ru>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Author: Eugeny Boger <eugenyboger@dgap.mipt.ru>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) * Use consistent with the GNU GPL is permitted,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) * provided that this copyright notice is
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) * preserved in its entirety in all copies and derived works.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include <linux/device.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <linux/power_supply.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #include <linux/apm-emulation.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #define PSY_PROP(psy, prop, val) (power_supply_get_property(psy, \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) POWER_SUPPLY_PROP_##prop, val))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) #define _MPSY_PROP(prop, val) (power_supply_get_property(main_battery, \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) prop, val))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) #define MPSY_PROP(prop, val) _MPSY_PROP(POWER_SUPPLY_PROP_##prop, val)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) static DEFINE_MUTEX(apm_mutex);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) static struct power_supply *main_battery;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) enum apm_source {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) SOURCE_ENERGY,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) SOURCE_CHARGE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) SOURCE_VOLTAGE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) struct find_bat_param {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) struct power_supply *main;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) struct power_supply *bat;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) struct power_supply *max_charge_bat;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) struct power_supply *max_energy_bat;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) union power_supply_propval full;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) int max_charge;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) int max_energy;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) static int __find_main_battery(struct device *dev, void *data)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) struct find_bat_param *bp = (struct find_bat_param *)data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) bp->bat = dev_get_drvdata(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) if (bp->bat->desc->use_for_apm) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) /* nice, we explicitly asked to report this battery. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) bp->main = bp->bat;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) return 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) if (!PSY_PROP(bp->bat, CHARGE_FULL_DESIGN, &bp->full) ||
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) !PSY_PROP(bp->bat, CHARGE_FULL, &bp->full)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) if (bp->full.intval > bp->max_charge) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) bp->max_charge_bat = bp->bat;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) bp->max_charge = bp->full.intval;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) } else if (!PSY_PROP(bp->bat, ENERGY_FULL_DESIGN, &bp->full) ||
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) !PSY_PROP(bp->bat, ENERGY_FULL, &bp->full)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) if (bp->full.intval > bp->max_energy) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) bp->max_energy_bat = bp->bat;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) bp->max_energy = bp->full.intval;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) static void find_main_battery(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) struct find_bat_param bp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) int error;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) memset(&bp, 0, sizeof(struct find_bat_param));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) main_battery = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) bp.main = main_battery;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) error = class_for_each_device(power_supply_class, NULL, &bp,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) __find_main_battery);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) if (error) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) main_battery = bp.main;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) if ((bp.max_energy_bat && bp.max_charge_bat) &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) (bp.max_energy_bat != bp.max_charge_bat)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) /* try guess battery with more capacity */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) if (!PSY_PROP(bp.max_charge_bat, VOLTAGE_MAX_DESIGN,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) &bp.full)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) if (bp.max_energy > bp.max_charge * bp.full.intval)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) main_battery = bp.max_energy_bat;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) main_battery = bp.max_charge_bat;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) } else if (!PSY_PROP(bp.max_energy_bat, VOLTAGE_MAX_DESIGN,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) &bp.full)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) if (bp.max_charge > bp.max_energy / bp.full.intval)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) main_battery = bp.max_charge_bat;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) main_battery = bp.max_energy_bat;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) /* give up, choice any */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) main_battery = bp.max_energy_bat;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) } else if (bp.max_charge_bat) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) main_battery = bp.max_charge_bat;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) } else if (bp.max_energy_bat) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) main_battery = bp.max_energy_bat;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) /* give up, try the last if any */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) main_battery = bp.bat;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) static int do_calculate_time(int status, enum apm_source source)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) union power_supply_propval full;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) union power_supply_propval empty;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) union power_supply_propval cur;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) union power_supply_propval I;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) enum power_supply_property full_prop;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) enum power_supply_property full_design_prop;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) enum power_supply_property empty_prop;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) enum power_supply_property empty_design_prop;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) enum power_supply_property cur_avg_prop;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) enum power_supply_property cur_now_prop;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) if (MPSY_PROP(CURRENT_AVG, &I)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) /* if battery can't report average value, use momentary */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) if (MPSY_PROP(CURRENT_NOW, &I))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) return -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) if (!I.intval)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) switch (source) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) case SOURCE_CHARGE:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) full_prop = POWER_SUPPLY_PROP_CHARGE_FULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) full_design_prop = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) empty_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) cur_avg_prop = POWER_SUPPLY_PROP_CHARGE_AVG;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) cur_now_prop = POWER_SUPPLY_PROP_CHARGE_NOW;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) case SOURCE_ENERGY:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) full_prop = POWER_SUPPLY_PROP_ENERGY_FULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) full_design_prop = POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) empty_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) cur_avg_prop = POWER_SUPPLY_PROP_ENERGY_AVG;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) cur_now_prop = POWER_SUPPLY_PROP_ENERGY_NOW;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) case SOURCE_VOLTAGE:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) full_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) full_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) empty_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) empty_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) cur_avg_prop = POWER_SUPPLY_PROP_VOLTAGE_AVG;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) cur_now_prop = POWER_SUPPLY_PROP_VOLTAGE_NOW;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) default:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) printk(KERN_ERR "Unsupported source: %d\n", source);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) return -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) if (_MPSY_PROP(full_prop, &full)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) /* if battery can't report this property, use design value */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) if (_MPSY_PROP(full_design_prop, &full))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) return -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) if (_MPSY_PROP(empty_prop, &empty)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) /* if battery can't report this property, use design value */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) if (_MPSY_PROP(empty_design_prop, &empty))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) empty.intval = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) if (_MPSY_PROP(cur_avg_prop, &cur)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) /* if battery can't report average value, use momentary */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) if (_MPSY_PROP(cur_now_prop, &cur))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) return -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) if (status == POWER_SUPPLY_STATUS_CHARGING)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) return ((cur.intval - full.intval) * 60L) / I.intval;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) return -((cur.intval - empty.intval) * 60L) / I.intval;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) static int calculate_time(int status)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) int time;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) time = do_calculate_time(status, SOURCE_ENERGY);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) if (time != -1)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) return time;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) time = do_calculate_time(status, SOURCE_CHARGE);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) if (time != -1)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204) return time;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) time = do_calculate_time(status, SOURCE_VOLTAGE);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207) if (time != -1)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208) return time;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210) return -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213) static int calculate_capacity(enum apm_source source)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215) enum power_supply_property full_prop, empty_prop;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216) enum power_supply_property full_design_prop, empty_design_prop;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217) enum power_supply_property now_prop, avg_prop;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218) union power_supply_propval empty, full, cur;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221) switch (source) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222) case SOURCE_CHARGE:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223) full_prop = POWER_SUPPLY_PROP_CHARGE_FULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224) empty_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225) full_design_prop = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 226) empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 227) now_prop = POWER_SUPPLY_PROP_CHARGE_NOW;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 228) avg_prop = POWER_SUPPLY_PROP_CHARGE_AVG;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 229) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 230) case SOURCE_ENERGY:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 231) full_prop = POWER_SUPPLY_PROP_ENERGY_FULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 232) empty_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 233) full_design_prop = POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 234) empty_design_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 235) now_prop = POWER_SUPPLY_PROP_ENERGY_NOW;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 236) avg_prop = POWER_SUPPLY_PROP_ENERGY_AVG;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 237) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 238) case SOURCE_VOLTAGE:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 239) full_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 240) empty_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 241) full_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 242) empty_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 243) now_prop = POWER_SUPPLY_PROP_VOLTAGE_NOW;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 244) avg_prop = POWER_SUPPLY_PROP_VOLTAGE_AVG;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 245) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 246) default:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 247) printk(KERN_ERR "Unsupported source: %d\n", source);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 248) return -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 249) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 250)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 251) if (_MPSY_PROP(full_prop, &full)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 252) /* if battery can't report this property, use design value */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 253) if (_MPSY_PROP(full_design_prop, &full))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 254) return -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 255) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 256)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 257) if (_MPSY_PROP(avg_prop, &cur)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 258) /* if battery can't report average value, use momentary */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 259) if (_MPSY_PROP(now_prop, &cur))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 260) return -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 261) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 262)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 263) if (_MPSY_PROP(empty_prop, &empty)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 264) /* if battery can't report this property, use design value */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 265) if (_MPSY_PROP(empty_design_prop, &empty))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 266) empty.intval = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 267) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 268)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 269) if (full.intval - empty.intval)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 270) ret = ((cur.intval - empty.intval) * 100L) /
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 271) (full.intval - empty.intval);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 272) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 273) return -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 274)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 275) if (ret > 100)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 276) return 100;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 277) else if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 278) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 279)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 280) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 281) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 282)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 283) static void apm_battery_apm_get_power_status(struct apm_power_info *info)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 284) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 285) union power_supply_propval status;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 286) union power_supply_propval capacity, time_to_full, time_to_empty;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 287)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 288) mutex_lock(&apm_mutex);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 289) find_main_battery();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 290) if (!main_battery) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 291) mutex_unlock(&apm_mutex);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 292) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 293) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 294)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 295) /* status */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 296)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 297) if (MPSY_PROP(STATUS, &status))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 298) status.intval = POWER_SUPPLY_STATUS_UNKNOWN;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 299)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 300) /* ac line status */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 301)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 302) if ((status.intval == POWER_SUPPLY_STATUS_CHARGING) ||
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 303) (status.intval == POWER_SUPPLY_STATUS_NOT_CHARGING) ||
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 304) (status.intval == POWER_SUPPLY_STATUS_FULL))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 305) info->ac_line_status = APM_AC_ONLINE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 306) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 307) info->ac_line_status = APM_AC_OFFLINE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 308)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 309) /* battery life (i.e. capacity, in percents) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 310)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 311) if (MPSY_PROP(CAPACITY, &capacity) == 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 312) info->battery_life = capacity.intval;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 313) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 314) /* try calculate using energy */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 315) info->battery_life = calculate_capacity(SOURCE_ENERGY);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 316) /* if failed try calculate using charge instead */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 317) if (info->battery_life == -1)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 318) info->battery_life = calculate_capacity(SOURCE_CHARGE);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 319) if (info->battery_life == -1)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 320) info->battery_life = calculate_capacity(SOURCE_VOLTAGE);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 321) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 322)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 323) /* charging status */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 324)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 325) if (status.intval == POWER_SUPPLY_STATUS_CHARGING) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 326) info->battery_status = APM_BATTERY_STATUS_CHARGING;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 327) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 328) if (info->battery_life > 50)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 329) info->battery_status = APM_BATTERY_STATUS_HIGH;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 330) else if (info->battery_life > 5)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 331) info->battery_status = APM_BATTERY_STATUS_LOW;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 332) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 333) info->battery_status = APM_BATTERY_STATUS_CRITICAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 334) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 335) info->battery_flag = info->battery_status;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 336)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 337) /* time */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 338)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 339) info->units = APM_UNITS_MINS;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 340)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 341) if (status.intval == POWER_SUPPLY_STATUS_CHARGING) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 342) if (!MPSY_PROP(TIME_TO_FULL_AVG, &time_to_full) ||
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 343) !MPSY_PROP(TIME_TO_FULL_NOW, &time_to_full))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 344) info->time = time_to_full.intval / 60;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 345) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 346) info->time = calculate_time(status.intval);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 347) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 348) if (!MPSY_PROP(TIME_TO_EMPTY_AVG, &time_to_empty) ||
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 349) !MPSY_PROP(TIME_TO_EMPTY_NOW, &time_to_empty))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 350) info->time = time_to_empty.intval / 60;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 351) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 352) info->time = calculate_time(status.intval);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 353) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 354)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 355) mutex_unlock(&apm_mutex);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 356) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 357)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 358) static int __init apm_battery_init(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 359) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 360) printk(KERN_INFO "APM Battery Driver\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 361)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 362) apm_get_power_status = apm_battery_apm_get_power_status;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 363) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 364) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 365)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 366) static void __exit apm_battery_exit(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 367) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 368) apm_get_power_status = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 369) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 370)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 371) module_init(apm_battery_init);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 372) module_exit(apm_battery_exit);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 373)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 374) MODULE_AUTHOR("Eugeny Boger <eugenyboger@dgap.mipt.ru>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 375) MODULE_DESCRIPTION("APM emulation driver for battery monitoring class");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 376) MODULE_LICENSE("GPL");