Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * Copyright © 2007 Anton Vorontsov <cbou@mail.ru>
0003  * Copyright © 2007 Eugeny Boger <eugenyboger@dgap.mipt.ru>
0004  *
0005  * Author: Eugeny Boger <eugenyboger@dgap.mipt.ru>
0006  *
0007  * Use consistent with the GNU GPL is permitted,
0008  * provided that this copyright notice is
0009  * preserved in its entirety in all copies and derived works.
0010  */
0011 
0012 #include <linux/module.h>
0013 #include <linux/device.h>
0014 #include <linux/power_supply.h>
0015 #include <linux/apm-emulation.h>
0016 
0017 
0018 #define PSY_PROP(psy, prop, val) (power_supply_get_property(psy, \
0019              POWER_SUPPLY_PROP_##prop, val))
0020 
0021 #define _MPSY_PROP(prop, val) (power_supply_get_property(main_battery, \
0022                              prop, val))
0023 
0024 #define MPSY_PROP(prop, val) _MPSY_PROP(POWER_SUPPLY_PROP_##prop, val)
0025 
0026 static DEFINE_MUTEX(apm_mutex);
0027 static struct power_supply *main_battery;
0028 
0029 enum apm_source {
0030     SOURCE_ENERGY,
0031     SOURCE_CHARGE,
0032     SOURCE_VOLTAGE,
0033 };
0034 
0035 struct find_bat_param {
0036     struct power_supply *main;
0037     struct power_supply *bat;
0038     struct power_supply *max_charge_bat;
0039     struct power_supply *max_energy_bat;
0040     union power_supply_propval full;
0041     int max_charge;
0042     int max_energy;
0043 };
0044 
0045 static int __find_main_battery(struct device *dev, void *data)
0046 {
0047     struct find_bat_param *bp = (struct find_bat_param *)data;
0048 
0049     bp->bat = dev_get_drvdata(dev);
0050 
0051     if (bp->bat->desc->use_for_apm) {
0052         /* nice, we explicitly asked to report this battery. */
0053         bp->main = bp->bat;
0054         return 1;
0055     }
0056 
0057     if (!PSY_PROP(bp->bat, CHARGE_FULL_DESIGN, &bp->full) ||
0058             !PSY_PROP(bp->bat, CHARGE_FULL, &bp->full)) {
0059         if (bp->full.intval > bp->max_charge) {
0060             bp->max_charge_bat = bp->bat;
0061             bp->max_charge = bp->full.intval;
0062         }
0063     } else if (!PSY_PROP(bp->bat, ENERGY_FULL_DESIGN, &bp->full) ||
0064             !PSY_PROP(bp->bat, ENERGY_FULL, &bp->full)) {
0065         if (bp->full.intval > bp->max_energy) {
0066             bp->max_energy_bat = bp->bat;
0067             bp->max_energy = bp->full.intval;
0068         }
0069     }
0070     return 0;
0071 }
0072 
0073 static void find_main_battery(void)
0074 {
0075     struct find_bat_param bp;
0076     int error;
0077 
0078     memset(&bp, 0, sizeof(struct find_bat_param));
0079     main_battery = NULL;
0080     bp.main = main_battery;
0081 
0082     error = class_for_each_device(power_supply_class, NULL, &bp,
0083                       __find_main_battery);
0084     if (error) {
0085         main_battery = bp.main;
0086         return;
0087     }
0088 
0089     if ((bp.max_energy_bat && bp.max_charge_bat) &&
0090             (bp.max_energy_bat != bp.max_charge_bat)) {
0091         /* try guess battery with more capacity */
0092         if (!PSY_PROP(bp.max_charge_bat, VOLTAGE_MAX_DESIGN,
0093                   &bp.full)) {
0094             if (bp.max_energy > bp.max_charge * bp.full.intval)
0095                 main_battery = bp.max_energy_bat;
0096             else
0097                 main_battery = bp.max_charge_bat;
0098         } else if (!PSY_PROP(bp.max_energy_bat, VOLTAGE_MAX_DESIGN,
0099                                   &bp.full)) {
0100             if (bp.max_charge > bp.max_energy / bp.full.intval)
0101                 main_battery = bp.max_charge_bat;
0102             else
0103                 main_battery = bp.max_energy_bat;
0104         } else {
0105             /* give up, choice any */
0106             main_battery = bp.max_energy_bat;
0107         }
0108     } else if (bp.max_charge_bat) {
0109         main_battery = bp.max_charge_bat;
0110     } else if (bp.max_energy_bat) {
0111         main_battery = bp.max_energy_bat;
0112     } else {
0113         /* give up, try the last if any */
0114         main_battery = bp.bat;
0115     }
0116 }
0117 
0118 static int do_calculate_time(int status, enum apm_source source)
0119 {
0120     union power_supply_propval full;
0121     union power_supply_propval empty;
0122     union power_supply_propval cur;
0123     union power_supply_propval I;
0124     enum power_supply_property full_prop;
0125     enum power_supply_property full_design_prop;
0126     enum power_supply_property empty_prop;
0127     enum power_supply_property empty_design_prop;
0128     enum power_supply_property cur_avg_prop;
0129     enum power_supply_property cur_now_prop;
0130 
0131     if (MPSY_PROP(CURRENT_AVG, &I)) {
0132         /* if battery can't report average value, use momentary */
0133         if (MPSY_PROP(CURRENT_NOW, &I))
0134             return -1;
0135     }
0136 
0137     if (!I.intval)
0138         return 0;
0139 
0140     switch (source) {
0141     case SOURCE_CHARGE:
0142         full_prop = POWER_SUPPLY_PROP_CHARGE_FULL;
0143         full_design_prop = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN;
0144         empty_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
0145         empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
0146         cur_avg_prop = POWER_SUPPLY_PROP_CHARGE_AVG;
0147         cur_now_prop = POWER_SUPPLY_PROP_CHARGE_NOW;
0148         break;
0149     case SOURCE_ENERGY:
0150         full_prop = POWER_SUPPLY_PROP_ENERGY_FULL;
0151         full_design_prop = POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN;
0152         empty_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY;
0153         empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
0154         cur_avg_prop = POWER_SUPPLY_PROP_ENERGY_AVG;
0155         cur_now_prop = POWER_SUPPLY_PROP_ENERGY_NOW;
0156         break;
0157     case SOURCE_VOLTAGE:
0158         full_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX;
0159         full_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN;
0160         empty_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN;
0161         empty_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN;
0162         cur_avg_prop = POWER_SUPPLY_PROP_VOLTAGE_AVG;
0163         cur_now_prop = POWER_SUPPLY_PROP_VOLTAGE_NOW;
0164         break;
0165     default:
0166         printk(KERN_ERR "Unsupported source: %d\n", source);
0167         return -1;
0168     }
0169 
0170     if (_MPSY_PROP(full_prop, &full)) {
0171         /* if battery can't report this property, use design value */
0172         if (_MPSY_PROP(full_design_prop, &full))
0173             return -1;
0174     }
0175 
0176     if (_MPSY_PROP(empty_prop, &empty)) {
0177         /* if battery can't report this property, use design value */
0178         if (_MPSY_PROP(empty_design_prop, &empty))
0179             empty.intval = 0;
0180     }
0181 
0182     if (_MPSY_PROP(cur_avg_prop, &cur)) {
0183         /* if battery can't report average value, use momentary */
0184         if (_MPSY_PROP(cur_now_prop, &cur))
0185             return -1;
0186     }
0187 
0188     if (status == POWER_SUPPLY_STATUS_CHARGING)
0189         return ((cur.intval - full.intval) * 60L) / I.intval;
0190     else
0191         return -((cur.intval - empty.intval) * 60L) / I.intval;
0192 }
0193 
0194 static int calculate_time(int status)
0195 {
0196     int time;
0197 
0198     time = do_calculate_time(status, SOURCE_ENERGY);
0199     if (time != -1)
0200         return time;
0201 
0202     time = do_calculate_time(status, SOURCE_CHARGE);
0203     if (time != -1)
0204         return time;
0205 
0206     time = do_calculate_time(status, SOURCE_VOLTAGE);
0207     if (time != -1)
0208         return time;
0209 
0210     return -1;
0211 }
0212 
0213 static int calculate_capacity(enum apm_source source)
0214 {
0215     enum power_supply_property full_prop, empty_prop;
0216     enum power_supply_property full_design_prop, empty_design_prop;
0217     enum power_supply_property now_prop, avg_prop;
0218     union power_supply_propval empty, full, cur;
0219     int ret;
0220 
0221     switch (source) {
0222     case SOURCE_CHARGE:
0223         full_prop = POWER_SUPPLY_PROP_CHARGE_FULL;
0224         empty_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
0225         full_design_prop = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN;
0226         empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN;
0227         now_prop = POWER_SUPPLY_PROP_CHARGE_NOW;
0228         avg_prop = POWER_SUPPLY_PROP_CHARGE_AVG;
0229         break;
0230     case SOURCE_ENERGY:
0231         full_prop = POWER_SUPPLY_PROP_ENERGY_FULL;
0232         empty_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY;
0233         full_design_prop = POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN;
0234         empty_design_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN;
0235         now_prop = POWER_SUPPLY_PROP_ENERGY_NOW;
0236         avg_prop = POWER_SUPPLY_PROP_ENERGY_AVG;
0237         break;
0238     case SOURCE_VOLTAGE:
0239         full_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX;
0240         empty_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN;
0241         full_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN;
0242         empty_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN;
0243         now_prop = POWER_SUPPLY_PROP_VOLTAGE_NOW;
0244         avg_prop = POWER_SUPPLY_PROP_VOLTAGE_AVG;
0245         break;
0246     default:
0247         printk(KERN_ERR "Unsupported source: %d\n", source);
0248         return -1;
0249     }
0250 
0251     if (_MPSY_PROP(full_prop, &full)) {
0252         /* if battery can't report this property, use design value */
0253         if (_MPSY_PROP(full_design_prop, &full))
0254             return -1;
0255     }
0256 
0257     if (_MPSY_PROP(avg_prop, &cur)) {
0258         /* if battery can't report average value, use momentary */
0259         if (_MPSY_PROP(now_prop, &cur))
0260             return -1;
0261     }
0262 
0263     if (_MPSY_PROP(empty_prop, &empty)) {
0264         /* if battery can't report this property, use design value */
0265         if (_MPSY_PROP(empty_design_prop, &empty))
0266             empty.intval = 0;
0267     }
0268 
0269     if (full.intval - empty.intval)
0270         ret =  ((cur.intval - empty.intval) * 100L) /
0271                (full.intval - empty.intval);
0272     else
0273         return -1;
0274 
0275     if (ret > 100)
0276         return 100;
0277     else if (ret < 0)
0278         return 0;
0279 
0280     return ret;
0281 }
0282 
0283 static void apm_battery_apm_get_power_status(struct apm_power_info *info)
0284 {
0285     union power_supply_propval status;
0286     union power_supply_propval capacity, time_to_full, time_to_empty;
0287 
0288     mutex_lock(&apm_mutex);
0289     find_main_battery();
0290     if (!main_battery) {
0291         mutex_unlock(&apm_mutex);
0292         return;
0293     }
0294 
0295     /* status */
0296 
0297     if (MPSY_PROP(STATUS, &status))
0298         status.intval = POWER_SUPPLY_STATUS_UNKNOWN;
0299 
0300     /* ac line status */
0301 
0302     if ((status.intval == POWER_SUPPLY_STATUS_CHARGING) ||
0303         (status.intval == POWER_SUPPLY_STATUS_NOT_CHARGING) ||
0304         (status.intval == POWER_SUPPLY_STATUS_FULL))
0305         info->ac_line_status = APM_AC_ONLINE;
0306     else
0307         info->ac_line_status = APM_AC_OFFLINE;
0308 
0309     /* battery life (i.e. capacity, in percents) */
0310 
0311     if (MPSY_PROP(CAPACITY, &capacity) == 0) {
0312         info->battery_life = capacity.intval;
0313     } else {
0314         /* try calculate using energy */
0315         info->battery_life = calculate_capacity(SOURCE_ENERGY);
0316         /* if failed try calculate using charge instead */
0317         if (info->battery_life == -1)
0318             info->battery_life = calculate_capacity(SOURCE_CHARGE);
0319         if (info->battery_life == -1)
0320             info->battery_life = calculate_capacity(SOURCE_VOLTAGE);
0321     }
0322 
0323     /* charging status */
0324 
0325     if (status.intval == POWER_SUPPLY_STATUS_CHARGING) {
0326         info->battery_status = APM_BATTERY_STATUS_CHARGING;
0327     } else {
0328         if (info->battery_life > 50)
0329             info->battery_status = APM_BATTERY_STATUS_HIGH;
0330         else if (info->battery_life > 5)
0331             info->battery_status = APM_BATTERY_STATUS_LOW;
0332         else
0333             info->battery_status = APM_BATTERY_STATUS_CRITICAL;
0334     }
0335     info->battery_flag = info->battery_status;
0336 
0337     /* time */
0338 
0339     info->units = APM_UNITS_MINS;
0340 
0341     if (status.intval == POWER_SUPPLY_STATUS_CHARGING) {
0342         if (!MPSY_PROP(TIME_TO_FULL_AVG, &time_to_full) ||
0343                 !MPSY_PROP(TIME_TO_FULL_NOW, &time_to_full))
0344             info->time = time_to_full.intval / 60;
0345         else
0346             info->time = calculate_time(status.intval);
0347     } else {
0348         if (!MPSY_PROP(TIME_TO_EMPTY_AVG, &time_to_empty) ||
0349                   !MPSY_PROP(TIME_TO_EMPTY_NOW, &time_to_empty))
0350             info->time = time_to_empty.intval / 60;
0351         else
0352             info->time = calculate_time(status.intval);
0353     }
0354 
0355     mutex_unlock(&apm_mutex);
0356 }
0357 
0358 static int __init apm_battery_init(void)
0359 {
0360     printk(KERN_INFO "APM Battery Driver\n");
0361 
0362     apm_get_power_status = apm_battery_apm_get_power_status;
0363     return 0;
0364 }
0365 
0366 static void __exit apm_battery_exit(void)
0367 {
0368     apm_get_power_status = NULL;
0369 }
0370 
0371 module_init(apm_battery_init);
0372 module_exit(apm_battery_exit);
0373 
0374 MODULE_AUTHOR("Eugeny Boger <eugenyboger@dgap.mipt.ru>");
0375 MODULE_DESCRIPTION("APM emulation driver for battery monitoring class");
0376 MODULE_LICENSE("GPL");