Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.30-rc5 374 lines 10 kB view raw
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");