forked from Minki/linux
51990e8254
For files that are actively using linux/device.h, make sure that they call it out. This will allow us to clean up some of the implicit uses of linux/device.h within include/* without introducing build regressions. Yes, this was created by "cheating" -- i.e. the headers were cleaned up, and then the fallout was found and fixed, and then the two commits were reordered. This ensures we don't introduce build regressions into the git history. Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com>
377 lines
10 KiB
C
377 lines
10 KiB
C
/*
|
|
* Copyright © 2007 Anton Vorontsov <cbou@mail.ru>
|
|
* Copyright © 2007 Eugeny Boger <eugenyboger@dgap.mipt.ru>
|
|
*
|
|
* Author: Eugeny Boger <eugenyboger@dgap.mipt.ru>
|
|
*
|
|
* Use consistent with the GNU GPL is permitted,
|
|
* provided that this copyright notice is
|
|
* preserved in its entirety in all copies and derived works.
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/device.h>
|
|
#include <linux/power_supply.h>
|
|
#include <linux/apm-emulation.h>
|
|
|
|
|
|
#define PSY_PROP(psy, prop, val) (psy->get_property(psy, \
|
|
POWER_SUPPLY_PROP_##prop, val))
|
|
|
|
#define _MPSY_PROP(prop, val) (main_battery->get_property(main_battery, \
|
|
prop, val))
|
|
|
|
#define MPSY_PROP(prop, val) _MPSY_PROP(POWER_SUPPLY_PROP_##prop, val)
|
|
|
|
static DEFINE_MUTEX(apm_mutex);
|
|
static struct power_supply *main_battery;
|
|
|
|
enum apm_source {
|
|
SOURCE_ENERGY,
|
|
SOURCE_CHARGE,
|
|
SOURCE_VOLTAGE,
|
|
};
|
|
|
|
struct find_bat_param {
|
|
struct power_supply *main;
|
|
struct power_supply *bat;
|
|
struct power_supply *max_charge_bat;
|
|
struct power_supply *max_energy_bat;
|
|
union power_supply_propval full;
|
|
int max_charge;
|
|
int max_energy;
|
|
};
|
|
|
|
static int __find_main_battery(struct device *dev, void *data)
|
|
{
|
|
struct find_bat_param *bp = (struct find_bat_param *)data;
|
|
|
|
bp->bat = dev_get_drvdata(dev);
|
|
|
|
if (bp->bat->use_for_apm) {
|
|
/* nice, we explicitly asked to report this battery. */
|
|
bp->main = bp->bat;
|
|
return 1;
|
|
}
|
|
|
|
if (!PSY_PROP(bp->bat, CHARGE_FULL_DESIGN, &bp->full) ||
|
|
!PSY_PROP(bp->bat, CHARGE_FULL, &bp->full)) {
|
|
if (bp->full.intval > bp->max_charge) {
|
|
bp->max_charge_bat = bp->bat;
|
|
bp->max_charge = bp->full.intval;
|
|
}
|
|
} else if (!PSY_PROP(bp->bat, ENERGY_FULL_DESIGN, &bp->full) ||
|
|
!PSY_PROP(bp->bat, ENERGY_FULL, &bp->full)) {
|
|
if (bp->full.intval > bp->max_energy) {
|
|
bp->max_energy_bat = bp->bat;
|
|
bp->max_energy = bp->full.intval;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void find_main_battery(void)
|
|
{
|
|
struct find_bat_param bp;
|
|
int error;
|
|
|
|
memset(&bp, 0, sizeof(struct find_bat_param));
|
|
main_battery = NULL;
|
|
bp.main = main_battery;
|
|
|
|
error = class_for_each_device(power_supply_class, NULL, &bp,
|
|
__find_main_battery);
|
|
if (error) {
|
|
main_battery = bp.main;
|
|
return;
|
|
}
|
|
|
|
if ((bp.max_energy_bat && bp.max_charge_bat) &&
|
|
(bp.max_energy_bat != bp.max_charge_bat)) {
|
|
/* try guess battery with more capacity */
|
|
if (!PSY_PROP(bp.max_charge_bat, VOLTAGE_MAX_DESIGN,
|
|
&bp.full)) {
|
|
if (bp.max_energy > bp.max_charge * bp.full.intval)
|
|
main_battery = bp.max_energy_bat;
|
|
else
|
|
main_battery = bp.max_charge_bat;
|
|
} else if (!PSY_PROP(bp.max_energy_bat, VOLTAGE_MAX_DESIGN,
|
|
&bp.full)) {
|
|
if (bp.max_charge > bp.max_energy / bp.full.intval)
|
|
main_battery = bp.max_charge_bat;
|
|
else
|
|
main_battery = bp.max_energy_bat;
|
|
} else {
|
|
/* give up, choice any */
|
|
main_battery = bp.max_energy_bat;
|
|
}
|
|
} else if (bp.max_charge_bat) {
|
|
main_battery = bp.max_charge_bat;
|
|
} else if (bp.max_energy_bat) {
|
|
main_battery = bp.max_energy_bat;
|
|
} else {
|
|
/* give up, try the last if any */
|
|
main_battery = bp.bat;
|
|
}
|
|
}
|
|
|
|
static int do_calculate_time(int status, enum apm_source source)
|
|
{
|
|
union power_supply_propval full;
|
|
union power_supply_propval empty;
|
|
union power_supply_propval cur;
|
|
union power_supply_propval I;
|
|
enum power_supply_property full_prop;
|
|
enum power_supply_property full_design_prop;
|
|
enum power_supply_property empty_prop;
|
|
enum power_supply_property empty_design_prop;
|
|
enum power_supply_property cur_avg_prop;
|
|
enum power_supply_property cur_now_prop;
|
|
|
|
if (MPSY_PROP(CURRENT_AVG, &I)) {
|
|
/* if battery can't report average value, use momentary */
|
|
if (MPSY_PROP(CURRENT_NOW, &I))
|
|
return -1;
|
|
}
|
|
|
|
if (!I.intval)
|
|
return 0;
|
|
|
|
switch (source) {
|
|
case SOURCE_CHARGE:
|
|
full_prop = POWER_SUPPLY_PROP_CHARGE_FULL;
|
|
full_design_prop = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN;
|
|
empty_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
|
|
empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
|
|
cur_avg_prop = POWER_SUPPLY_PROP_CHARGE_AVG;
|
|
cur_now_prop = POWER_SUPPLY_PROP_CHARGE_NOW;
|
|
break;
|
|
case SOURCE_ENERGY:
|
|
full_prop = POWER_SUPPLY_PROP_ENERGY_FULL;
|
|
full_design_prop = POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN;
|
|
empty_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY;
|
|
empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
|
|
cur_avg_prop = POWER_SUPPLY_PROP_ENERGY_AVG;
|
|
cur_now_prop = POWER_SUPPLY_PROP_ENERGY_NOW;
|
|
break;
|
|
case SOURCE_VOLTAGE:
|
|
full_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX;
|
|
full_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN;
|
|
empty_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN;
|
|
empty_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN;
|
|
cur_avg_prop = POWER_SUPPLY_PROP_VOLTAGE_AVG;
|
|
cur_now_prop = POWER_SUPPLY_PROP_VOLTAGE_NOW;
|
|
break;
|
|
default:
|
|
printk(KERN_ERR "Unsupported source: %d\n", source);
|
|
return -1;
|
|
}
|
|
|
|
if (_MPSY_PROP(full_prop, &full)) {
|
|
/* if battery can't report this property, use design value */
|
|
if (_MPSY_PROP(full_design_prop, &full))
|
|
return -1;
|
|
}
|
|
|
|
if (_MPSY_PROP(empty_prop, &empty)) {
|
|
/* if battery can't report this property, use design value */
|
|
if (_MPSY_PROP(empty_design_prop, &empty))
|
|
empty.intval = 0;
|
|
}
|
|
|
|
if (_MPSY_PROP(cur_avg_prop, &cur)) {
|
|
/* if battery can't report average value, use momentary */
|
|
if (_MPSY_PROP(cur_now_prop, &cur))
|
|
return -1;
|
|
}
|
|
|
|
if (status == POWER_SUPPLY_STATUS_CHARGING)
|
|
return ((cur.intval - full.intval) * 60L) / I.intval;
|
|
else
|
|
return -((cur.intval - empty.intval) * 60L) / I.intval;
|
|
}
|
|
|
|
static int calculate_time(int status)
|
|
{
|
|
int time;
|
|
|
|
time = do_calculate_time(status, SOURCE_ENERGY);
|
|
if (time != -1)
|
|
return time;
|
|
|
|
time = do_calculate_time(status, SOURCE_CHARGE);
|
|
if (time != -1)
|
|
return time;
|
|
|
|
time = do_calculate_time(status, SOURCE_VOLTAGE);
|
|
if (time != -1)
|
|
return time;
|
|
|
|
return -1;
|
|
}
|
|
|
|
static int calculate_capacity(enum apm_source source)
|
|
{
|
|
enum power_supply_property full_prop, empty_prop;
|
|
enum power_supply_property full_design_prop, empty_design_prop;
|
|
enum power_supply_property now_prop, avg_prop;
|
|
union power_supply_propval empty, full, cur;
|
|
int ret;
|
|
|
|
switch (source) {
|
|
case SOURCE_CHARGE:
|
|
full_prop = POWER_SUPPLY_PROP_CHARGE_FULL;
|
|
empty_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
|
|
full_design_prop = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN;
|
|
empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN;
|
|
now_prop = POWER_SUPPLY_PROP_CHARGE_NOW;
|
|
avg_prop = POWER_SUPPLY_PROP_CHARGE_AVG;
|
|
break;
|
|
case SOURCE_ENERGY:
|
|
full_prop = POWER_SUPPLY_PROP_ENERGY_FULL;
|
|
empty_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY;
|
|
full_design_prop = POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN;
|
|
empty_design_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN;
|
|
now_prop = POWER_SUPPLY_PROP_ENERGY_NOW;
|
|
avg_prop = POWER_SUPPLY_PROP_ENERGY_AVG;
|
|
break;
|
|
case SOURCE_VOLTAGE:
|
|
full_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX;
|
|
empty_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN;
|
|
full_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN;
|
|
empty_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN;
|
|
now_prop = POWER_SUPPLY_PROP_VOLTAGE_NOW;
|
|
avg_prop = POWER_SUPPLY_PROP_VOLTAGE_AVG;
|
|
break;
|
|
default:
|
|
printk(KERN_ERR "Unsupported source: %d\n", source);
|
|
return -1;
|
|
}
|
|
|
|
if (_MPSY_PROP(full_prop, &full)) {
|
|
/* if battery can't report this property, use design value */
|
|
if (_MPSY_PROP(full_design_prop, &full))
|
|
return -1;
|
|
}
|
|
|
|
if (_MPSY_PROP(avg_prop, &cur)) {
|
|
/* if battery can't report average value, use momentary */
|
|
if (_MPSY_PROP(now_prop, &cur))
|
|
return -1;
|
|
}
|
|
|
|
if (_MPSY_PROP(empty_prop, &empty)) {
|
|
/* if battery can't report this property, use design value */
|
|
if (_MPSY_PROP(empty_design_prop, &empty))
|
|
empty.intval = 0;
|
|
}
|
|
|
|
if (full.intval - empty.intval)
|
|
ret = ((cur.intval - empty.intval) * 100L) /
|
|
(full.intval - empty.intval);
|
|
else
|
|
return -1;
|
|
|
|
if (ret > 100)
|
|
return 100;
|
|
else if (ret < 0)
|
|
return 0;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void apm_battery_apm_get_power_status(struct apm_power_info *info)
|
|
{
|
|
union power_supply_propval status;
|
|
union power_supply_propval capacity, time_to_full, time_to_empty;
|
|
|
|
mutex_lock(&apm_mutex);
|
|
find_main_battery();
|
|
if (!main_battery) {
|
|
mutex_unlock(&apm_mutex);
|
|
return;
|
|
}
|
|
|
|
/* status */
|
|
|
|
if (MPSY_PROP(STATUS, &status))
|
|
status.intval = POWER_SUPPLY_STATUS_UNKNOWN;
|
|
|
|
/* ac line status */
|
|
|
|
if ((status.intval == POWER_SUPPLY_STATUS_CHARGING) ||
|
|
(status.intval == POWER_SUPPLY_STATUS_NOT_CHARGING) ||
|
|
(status.intval == POWER_SUPPLY_STATUS_FULL))
|
|
info->ac_line_status = APM_AC_ONLINE;
|
|
else
|
|
info->ac_line_status = APM_AC_OFFLINE;
|
|
|
|
/* battery life (i.e. capacity, in percents) */
|
|
|
|
if (MPSY_PROP(CAPACITY, &capacity) == 0) {
|
|
info->battery_life = capacity.intval;
|
|
} else {
|
|
/* try calculate using energy */
|
|
info->battery_life = calculate_capacity(SOURCE_ENERGY);
|
|
/* if failed try calculate using charge instead */
|
|
if (info->battery_life == -1)
|
|
info->battery_life = calculate_capacity(SOURCE_CHARGE);
|
|
if (info->battery_life == -1)
|
|
info->battery_life = calculate_capacity(SOURCE_VOLTAGE);
|
|
}
|
|
|
|
/* charging status */
|
|
|
|
if (status.intval == POWER_SUPPLY_STATUS_CHARGING) {
|
|
info->battery_status = APM_BATTERY_STATUS_CHARGING;
|
|
} else {
|
|
if (info->battery_life > 50)
|
|
info->battery_status = APM_BATTERY_STATUS_HIGH;
|
|
else if (info->battery_life > 5)
|
|
info->battery_status = APM_BATTERY_STATUS_LOW;
|
|
else
|
|
info->battery_status = APM_BATTERY_STATUS_CRITICAL;
|
|
}
|
|
info->battery_flag = info->battery_status;
|
|
|
|
/* time */
|
|
|
|
info->units = APM_UNITS_MINS;
|
|
|
|
if (status.intval == POWER_SUPPLY_STATUS_CHARGING) {
|
|
if (!MPSY_PROP(TIME_TO_FULL_AVG, &time_to_full) ||
|
|
!MPSY_PROP(TIME_TO_FULL_NOW, &time_to_full))
|
|
info->time = time_to_full.intval / 60;
|
|
else
|
|
info->time = calculate_time(status.intval);
|
|
} else {
|
|
if (!MPSY_PROP(TIME_TO_EMPTY_AVG, &time_to_empty) ||
|
|
!MPSY_PROP(TIME_TO_EMPTY_NOW, &time_to_empty))
|
|
info->time = time_to_empty.intval / 60;
|
|
else
|
|
info->time = calculate_time(status.intval);
|
|
}
|
|
|
|
mutex_unlock(&apm_mutex);
|
|
}
|
|
|
|
static int __init apm_battery_init(void)
|
|
{
|
|
printk(KERN_INFO "APM Battery Driver\n");
|
|
|
|
apm_get_power_status = apm_battery_apm_get_power_status;
|
|
return 0;
|
|
}
|
|
|
|
static void __exit apm_battery_exit(void)
|
|
{
|
|
apm_get_power_status = NULL;
|
|
}
|
|
|
|
module_init(apm_battery_init);
|
|
module_exit(apm_battery_exit);
|
|
|
|
MODULE_AUTHOR("Eugeny Boger <eugenyboger@dgap.mipt.ru>");
|
|
MODULE_DESCRIPTION("APM emulation driver for battery monitoring class");
|
|
MODULE_LICENSE("GPL");
|