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