| Anton Vorontsov | 3788ec9 | 2007-05-04 00:43:24 +0400 | [diff] [blame] | 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 |  | 
| Dmitry Baryshkov | dffd28a | 2008-01-13 02:39:16 +0300 | [diff] [blame] | 16 |  | 
| Anton Vorontsov | 3788ec9 | 2007-05-04 00:43:24 +0400 | [diff] [blame] | 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 |  | 
| Dmitry Baryshkov | dffd28a | 2008-01-13 02:39:16 +0300 | [diff] [blame] | 25 | static DEFINE_MUTEX(apm_mutex); | 
| Anton Vorontsov | 3788ec9 | 2007-05-04 00:43:24 +0400 | [diff] [blame] | 26 | static struct power_supply *main_battery; | 
|  | 27 |  | 
| Dmitry Baryshkov | dffd28a | 2008-01-13 02:39:16 +0300 | [diff] [blame] | 28 | enum apm_source { | 
|  | 29 | SOURCE_ENERGY, | 
|  | 30 | SOURCE_CHARGE, | 
|  | 31 | SOURCE_VOLTAGE, | 
|  | 32 | }; | 
|  | 33 |  | 
| Dave Young | 443cad9 | 2008-01-22 13:58:22 +0800 | [diff] [blame] | 34 | struct 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; | 
| Anton Vorontsov | 3788ec9 | 2007-05-04 00:43:24 +0400 | [diff] [blame] | 39 | union power_supply_propval full; | 
| Dave Young | 443cad9 | 2008-01-22 13:58:22 +0800 | [diff] [blame] | 40 | int max_charge; | 
|  | 41 | int max_energy; | 
|  | 42 | }; | 
| Anton Vorontsov | 3788ec9 | 2007-05-04 00:43:24 +0400 | [diff] [blame] | 43 |  | 
| Dave Young | 443cad9 | 2008-01-22 13:58:22 +0800 | [diff] [blame] | 44 | static int __find_main_battery(struct device *dev, void *data) | 
|  | 45 | { | 
|  | 46 | struct find_bat_param *bp = (struct find_bat_param *)data; | 
| Anton Vorontsov | d385376 | 2007-10-05 01:05:00 +0400 | [diff] [blame] | 47 |  | 
| Dave Young | 443cad9 | 2008-01-22 13:58:22 +0800 | [diff] [blame] | 48 | bp->bat = dev_get_drvdata(dev); | 
| Anton Vorontsov | d385376 | 2007-10-05 01:05:00 +0400 | [diff] [blame] | 49 |  | 
| Dave Young | 443cad9 | 2008-01-22 13:58:22 +0800 | [diff] [blame] | 50 | if (bp->bat->use_for_apm) { | 
|  | 51 | /* nice, we explicitly asked to report this battery. */ | 
|  | 52 | bp->main = bp->bat; | 
|  | 53 | return 1; | 
| Anton Vorontsov | 3788ec9 | 2007-05-04 00:43:24 +0400 | [diff] [blame] | 54 | } | 
| Anton Vorontsov | d385376 | 2007-10-05 01:05:00 +0400 | [diff] [blame] | 55 |  | 
| Dave Young | 443cad9 | 2008-01-22 13:58:22 +0800 | [diff] [blame] | 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 |  | 
|  | 72 | static 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 |  | 
| Greg Kroah-Hartman | 93562b5 | 2008-05-22 17:21:08 -0400 | [diff] [blame] | 81 | error = class_for_each_device(power_supply_class, NULL, &bp, | 
| Dave Young | 443cad9 | 2008-01-22 13:58:22 +0800 | [diff] [blame] | 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)) { | 
| Anton Vorontsov | d385376 | 2007-10-05 01:05:00 +0400 | [diff] [blame] | 90 | /* try guess battery with more capacity */ | 
| Dave Young | 443cad9 | 2008-01-22 13:58:22 +0800 | [diff] [blame] | 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; | 
| Anton Vorontsov | d385376 | 2007-10-05 01:05:00 +0400 | [diff] [blame] | 95 | else | 
| Dave Young | 443cad9 | 2008-01-22 13:58:22 +0800 | [diff] [blame] | 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; | 
| Anton Vorontsov | d385376 | 2007-10-05 01:05:00 +0400 | [diff] [blame] | 101 | else | 
| Dave Young | 443cad9 | 2008-01-22 13:58:22 +0800 | [diff] [blame] | 102 | main_battery = bp.max_energy_bat; | 
| Anton Vorontsov | d385376 | 2007-10-05 01:05:00 +0400 | [diff] [blame] | 103 | } else { | 
|  | 104 | /* give up, choice any */ | 
| Dave Young | 443cad9 | 2008-01-22 13:58:22 +0800 | [diff] [blame] | 105 | main_battery = bp.max_energy_bat; | 
| Anton Vorontsov | d385376 | 2007-10-05 01:05:00 +0400 | [diff] [blame] | 106 | } | 
| Dave Young | 443cad9 | 2008-01-22 13:58:22 +0800 | [diff] [blame] | 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; | 
| Anton Vorontsov | d385376 | 2007-10-05 01:05:00 +0400 | [diff] [blame] | 111 | } else { | 
|  | 112 | /* give up, try the last if any */ | 
| Dave Young | 443cad9 | 2008-01-22 13:58:22 +0800 | [diff] [blame] | 113 | main_battery = bp.bat; | 
| Anton Vorontsov | d385376 | 2007-10-05 01:05:00 +0400 | [diff] [blame] | 114 | } | 
| Anton Vorontsov | 3788ec9 | 2007-05-04 00:43:24 +0400 | [diff] [blame] | 115 | } | 
|  | 116 |  | 
| Dmitry Baryshkov | dffd28a | 2008-01-13 02:39:16 +0300 | [diff] [blame] | 117 | static int do_calculate_time(int status, enum apm_source source) | 
| Anton Vorontsov | 3788ec9 | 2007-05-04 00:43:24 +0400 | [diff] [blame] | 118 | { | 
| Anton Vorontsov | 2a721df | 2007-10-05 01:05:00 +0400 | [diff] [blame] | 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; | 
| Anton Vorontsov | 3788ec9 | 2007-05-04 00:43:24 +0400 | [diff] [blame] | 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 |  | 
| Anton Vorontsov | e91926e | 2008-01-13 02:44:54 +0300 | [diff] [blame] | 136 | if (!I.intval) | 
|  | 137 | return 0; | 
|  | 138 |  | 
| Dmitry Baryshkov | dffd28a | 2008-01-13 02:39:16 +0300 | [diff] [blame] | 139 | switch (source) { | 
|  | 140 | case SOURCE_CHARGE: | 
| Anton Vorontsov | 2a721df | 2007-10-05 01:05:00 +0400 | [diff] [blame] | 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; | 
| Dmitry Baryshkov | dffd28a | 2008-01-13 02:39:16 +0300 | [diff] [blame] | 147 | break; | 
|  | 148 | case SOURCE_ENERGY: | 
| Anton Vorontsov | 2a721df | 2007-10-05 01:05:00 +0400 | [diff] [blame] | 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; | 
| Dmitry Baryshkov | dffd28a | 2008-01-13 02:39:16 +0300 | [diff] [blame] | 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; | 
| Anton Vorontsov | 2a721df | 2007-10-05 01:05:00 +0400 | [diff] [blame] | 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 |  | 
| Anton Vorontsov | 3788ec9 | 2007-05-04 00:43:24 +0400 | [diff] [blame] | 187 | if (status == POWER_SUPPLY_STATUS_CHARGING) | 
| Anton Vorontsov | 2a721df | 2007-10-05 01:05:00 +0400 | [diff] [blame] | 188 | return ((cur.intval - full.intval) * 60L) / I.intval; | 
| Anton Vorontsov | 3788ec9 | 2007-05-04 00:43:24 +0400 | [diff] [blame] | 189 | else | 
| Anton Vorontsov | 2a721df | 2007-10-05 01:05:00 +0400 | [diff] [blame] | 190 | return -((cur.intval - empty.intval) * 60L) / I.intval; | 
| Anton Vorontsov | 3788ec9 | 2007-05-04 00:43:24 +0400 | [diff] [blame] | 191 | } | 
|  | 192 |  | 
| Dmitry Baryshkov | dffd28a | 2008-01-13 02:39:16 +0300 | [diff] [blame] | 193 | static 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 |  | 
|  | 212 | static int calculate_capacity(enum apm_source source) | 
| Anton Vorontsov | 3788ec9 | 2007-05-04 00:43:24 +0400 | [diff] [blame] | 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 |  | 
| Dmitry Baryshkov | dffd28a | 2008-01-13 02:39:16 +0300 | [diff] [blame] | 220 | switch (source) { | 
|  | 221 | case SOURCE_CHARGE: | 
| Anton Vorontsov | 3788ec9 | 2007-05-04 00:43:24 +0400 | [diff] [blame] | 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; | 
| Dmitry Baryshkov | dffd28a | 2008-01-13 02:39:16 +0300 | [diff] [blame] | 228 | break; | 
|  | 229 | case SOURCE_ENERGY: | 
| Anton Vorontsov | 3788ec9 | 2007-05-04 00:43:24 +0400 | [diff] [blame] | 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; | 
| Dmitry Baryshkov | dffd28a | 2008-01-13 02:39:16 +0300 | [diff] [blame] | 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; | 
| Anton Vorontsov | 3788ec9 | 2007-05-04 00:43:24 +0400 | [diff] [blame] | 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 |  | 
|  | 281 | static 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 |  | 
| Dave Young | 443cad9 | 2008-01-22 13:58:22 +0800 | [diff] [blame] | 286 | mutex_lock(&apm_mutex); | 
| Anton Vorontsov | 3788ec9 | 2007-05-04 00:43:24 +0400 | [diff] [blame] | 287 | find_main_battery(); | 
|  | 288 | if (!main_battery) { | 
| Dave Young | 443cad9 | 2008-01-22 13:58:22 +0800 | [diff] [blame] | 289 | mutex_unlock(&apm_mutex); | 
| Anton Vorontsov | 3788ec9 | 2007-05-04 00:43:24 +0400 | [diff] [blame] | 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 */ | 
| Dmitry Baryshkov | dffd28a | 2008-01-13 02:39:16 +0300 | [diff] [blame] | 313 | info->battery_life = calculate_capacity(SOURCE_ENERGY); | 
| Anton Vorontsov | 3788ec9 | 2007-05-04 00:43:24 +0400 | [diff] [blame] | 314 | /* if failed try calculate using charge instead */ | 
|  | 315 | if (info->battery_life == -1) | 
| Dmitry Baryshkov | dffd28a | 2008-01-13 02:39:16 +0300 | [diff] [blame] | 316 | info->battery_life = calculate_capacity(SOURCE_CHARGE); | 
|  | 317 | if (info->battery_life == -1) | 
|  | 318 | info->battery_life = calculate_capacity(SOURCE_VOLTAGE); | 
| Anton Vorontsov | 3788ec9 | 2007-05-04 00:43:24 +0400 | [diff] [blame] | 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) { | 
| Anton Vorontsov | cd1ebcc | 2007-10-05 01:04:59 +0400 | [diff] [blame] | 340 | if (!MPSY_PROP(TIME_TO_FULL_AVG, &time_to_full) || | 
| Dmitry Baryshkov | dffd28a | 2008-01-13 02:39:16 +0300 | [diff] [blame] | 341 | !MPSY_PROP(TIME_TO_FULL_NOW, &time_to_full)) | 
| Anton Vorontsov | cd1ebcc | 2007-10-05 01:04:59 +0400 | [diff] [blame] | 342 | info->time = time_to_full.intval / 60; | 
| Dmitry Baryshkov | dffd28a | 2008-01-13 02:39:16 +0300 | [diff] [blame] | 343 | else | 
|  | 344 | info->time = calculate_time(status.intval); | 
| Anton Vorontsov | 3788ec9 | 2007-05-04 00:43:24 +0400 | [diff] [blame] | 345 | } else { | 
| Anton Vorontsov | cd1ebcc | 2007-10-05 01:04:59 +0400 | [diff] [blame] | 346 | if (!MPSY_PROP(TIME_TO_EMPTY_AVG, &time_to_empty) || | 
| Dmitry Baryshkov | dffd28a | 2008-01-13 02:39:16 +0300 | [diff] [blame] | 347 | !MPSY_PROP(TIME_TO_EMPTY_NOW, &time_to_empty)) | 
| Anton Vorontsov | cd1ebcc | 2007-10-05 01:04:59 +0400 | [diff] [blame] | 348 | info->time = time_to_empty.intval / 60; | 
| Dmitry Baryshkov | dffd28a | 2008-01-13 02:39:16 +0300 | [diff] [blame] | 349 | else | 
|  | 350 | info->time = calculate_time(status.intval); | 
| Anton Vorontsov | 3788ec9 | 2007-05-04 00:43:24 +0400 | [diff] [blame] | 351 | } | 
|  | 352 |  | 
| Dave Young | 443cad9 | 2008-01-22 13:58:22 +0800 | [diff] [blame] | 353 | mutex_unlock(&apm_mutex); | 
| Anton Vorontsov | 3788ec9 | 2007-05-04 00:43:24 +0400 | [diff] [blame] | 354 | } | 
|  | 355 |  | 
|  | 356 | static 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 |  | 
|  | 364 | static void __exit apm_battery_exit(void) | 
|  | 365 | { | 
|  | 366 | apm_get_power_status = NULL; | 
| Anton Vorontsov | 3788ec9 | 2007-05-04 00:43:24 +0400 | [diff] [blame] | 367 | } | 
|  | 368 |  | 
|  | 369 | module_init(apm_battery_init); | 
|  | 370 | module_exit(apm_battery_exit); | 
|  | 371 |  | 
|  | 372 | MODULE_AUTHOR("Eugeny Boger <eugenyboger@dgap.mipt.ru>"); | 
|  | 373 | MODULE_DESCRIPTION("APM emulation driver for battery monitoring class"); | 
|  | 374 | MODULE_LICENSE("GPL"); |