root/drivers/power/supply/apm_power.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. __find_main_battery
  2. find_main_battery
  3. do_calculate_time
  4. calculate_time
  5. calculate_capacity
  6. apm_battery_apm_get_power_status
  7. apm_battery_init
  8. apm_battery_exit

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

/* [<][>][^][v][top][bottom][index][help] */