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        case SOURCE_VOLTAGE:
 237                full_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX;
 238                empty_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN;
 239                full_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN;
 240                empty_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN;
 241                now_prop = POWER_SUPPLY_PROP_VOLTAGE_NOW;
 242                avg_prop = POWER_SUPPLY_PROP_VOLTAGE_AVG;
 243                break;
 244        default:
 245                printk(KERN_ERR "Unsupported source: %d\n", source);
 246                return -1;
 247        }
 248
 249        if (_MPSY_PROP(full_prop, &full)) {
 250                /* if battery can't report this property, use design value */
 251                if (_MPSY_PROP(full_design_prop, &full))
 252                        return -1;
 253        }
 254
 255        if (_MPSY_PROP(avg_prop, &cur)) {
 256                /* if battery can't report average value, use momentary */
 257                if (_MPSY_PROP(now_prop, &cur))
 258                        return -1;
 259        }
 260
 261        if (_MPSY_PROP(empty_prop, &empty)) {
 262                /* if battery can't report this property, use design value */
 263                if (_MPSY_PROP(empty_design_prop, &empty))
 264                        empty.intval = 0;
 265        }
 266
 267        if (full.intval - empty.intval)
 268                ret =  ((cur.intval - empty.intval) * 100L) /
 269                       (full.intval - empty.intval);
 270        else
 271                return -1;
 272
 273        if (ret > 100)
 274                return 100;
 275        else if (ret < 0)
 276                return 0;
 277
 278        return ret;
 279}
 280
 281static void apm_battery_apm_get_power_status(struct apm_power_info *info)
 282{
 283        union power_supply_propval status;
 284        union power_supply_propval capacity, time_to_full, time_to_empty;
 285
 286        mutex_lock(&apm_mutex);
 287        find_main_battery();
 288        if (!main_battery) {
 289                mutex_unlock(&apm_mutex);
 290                return;
 291        }
 292
 293        /* status */
 294
 295        if (MPSY_PROP(STATUS, &status))
 296                status.intval = POWER_SUPPLY_STATUS_UNKNOWN;
 297
 298        /* ac line status */
 299
 300        if ((status.intval == POWER_SUPPLY_STATUS_CHARGING) ||
 301            (status.intval == POWER_SUPPLY_STATUS_NOT_CHARGING) ||
 302            (status.intval == POWER_SUPPLY_STATUS_FULL))
 303                info->ac_line_status = APM_AC_ONLINE;
 304        else
 305                info->ac_line_status = APM_AC_OFFLINE;
 306
 307        /* battery life (i.e. capacity, in percents) */
 308
 309        if (MPSY_PROP(CAPACITY, &capacity) == 0) {
 310                info->battery_life = capacity.intval;
 311        } else {
 312                /* try calculate using energy */
 313                info->battery_life = calculate_capacity(SOURCE_ENERGY);
 314                /* if failed try calculate using charge instead */
 315                if (info->battery_life == -1)
 316                        info->battery_life = calculate_capacity(SOURCE_CHARGE);
 317                if (info->battery_life == -1)
 318                        info->battery_life = calculate_capacity(SOURCE_VOLTAGE);
 319        }
 320
 321        /* charging status */
 322
 323        if (status.intval == POWER_SUPPLY_STATUS_CHARGING) {
 324                info->battery_status = APM_BATTERY_STATUS_CHARGING;
 325        } else {
 326                if (info->battery_life > 50)
 327                        info->battery_status = APM_BATTERY_STATUS_HIGH;
 328                else if (info->battery_life > 5)
 329                        info->battery_status = APM_BATTERY_STATUS_LOW;
 330                else
 331                        info->battery_status = APM_BATTERY_STATUS_CRITICAL;
 332        }
 333        info->battery_flag = info->battery_status;
 334
 335        /* time */
 336
 337        info->units = APM_UNITS_MINS;
 338
 339        if (status.intval == POWER_SUPPLY_STATUS_CHARGING) {
 340                if (!MPSY_PROP(TIME_TO_FULL_AVG, &time_to_full) ||
 341                                !MPSY_PROP(TIME_TO_FULL_NOW, &time_to_full))
 342                        info->time = time_to_full.intval / 60;
 343                else
 344                        info->time = calculate_time(status.intval);
 345        } else {
 346                if (!MPSY_PROP(TIME_TO_EMPTY_AVG, &time_to_empty) ||
 347                              !MPSY_PROP(TIME_TO_EMPTY_NOW, &time_to_empty))
 348                        info->time = time_to_empty.intval / 60;
 349                else
 350                        info->time = calculate_time(status.intval);
 351        }
 352
 353        mutex_unlock(&apm_mutex);
 354}
 355
 356static int __init apm_battery_init(void)
 357{
 358        printk(KERN_INFO "APM Battery Driver\n");
 359
 360        apm_get_power_status = apm_battery_apm_get_power_status;
 361        return 0;
 362}
 363
 364static void __exit apm_battery_exit(void)
 365{
 366        apm_get_power_status = NULL;
 367}
 368
 369module_init(apm_battery_init);
 370module_exit(apm_battery_exit);
 371
 372MODULE_AUTHOR("Eugeny Boger <eugenyboger@dgap.mipt.ru>");
 373MODULE_DESCRIPTION("APM emulation driver for battery monitoring class");
 374MODULE_LICENSE("GPL");
 375
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.