It's still tristate, but now on module removal it will set apm get status function to NULL, to not cause oops if two APM modules loaded/unloaded in special order.
Fixing APM API itself is another story. Signed-off-by: Anton Vorontsov <[EMAIL PROTECTED]> --- drivers/battery/Kconfig | 7 ++ drivers/battery/Makefile | 1 + drivers/battery/apm_power.c | 228 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 236 insertions(+), 0 deletions(-) create mode 100644 drivers/battery/apm_power.c diff --git a/drivers/battery/Kconfig b/drivers/battery/Kconfig index 0c14ae0..5e2dac8 100644 --- a/drivers/battery/Kconfig +++ b/drivers/battery/Kconfig @@ -15,4 +15,11 @@ config BATTERY_DS2760 help Say Y here to enable support for batteries with ds2760 chip. +config APM_POWER + tristate "APM emulation" + depends on BATTERY && APM_EMULATION + help + Say Y here to enable support APM status emulation using + battery class devices. + endmenu diff --git a/drivers/battery/Makefile b/drivers/battery/Makefile index 9902513..cea5807 100644 --- a/drivers/battery/Makefile +++ b/drivers/battery/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_BATTERY) += battery.o obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o +obj-$(CONFIG_APM_POWER) += apm_power.o diff --git a/drivers/battery/apm_power.c b/drivers/battery/apm_power.c new file mode 100644 index 0000000..e3bb4bd --- /dev/null +++ b/drivers/battery/apm_power.c @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2007 Anton Vorontsov <[EMAIL PROTECTED]> + * Copyright (c) 2007 Eugeny Boger <[EMAIL PROTECTED]> + * + * Author: Eugeny Boger <[EMAIL PROTECTED]> + * + * 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/battery.h> +#include <linux/apm-emulation.h> + +#ifdef current +#undef current /* it expands to get_current() */ +#endif + +#define BATTERY_PROP(bat, prop) ({ \ + void *value = bat->get_property(bat, BATTERY_PROP_##prop); \ + value ? *(int*)value : 0; \ +}) + +#define MBATTERY_PROP(prop) main_battery->get_property(main_battery, \ + BATTERY_PROP_##prop) + +static struct battery *main_battery; + +static void find_main_battery(void) +{ + struct device *dev; + struct battery *bat, *batm; + int max_charge = 0; + + main_battery = NULL; + batm = NULL; + list_for_each_entry(dev, &battery_class->devices, node) { + bat = dev_get_drvdata(dev); + /* If none of battery devices cantains 'main_battery' flag, + choice one with maximum design charge */ + if (BATTERY_PROP(bat, CHARGE_FULL_DESIGN) > max_charge) { + batm = bat; + max_charge = BATTERY_PROP(bat, CHARGE_FULL_DESIGN); + } + + if (bat->main_battery) + main_battery = bat; + } + if (!main_battery) + main_battery = batm; + + return; +} + +static int calculate_time(int status) +{ + int *charge_full; + int *charge_empty; + int *charge; + int *current; + + charge_full = MBATTERY_PROP(CHARGE_FULL); + /* if battery can't report this property, use design value */ + if (!charge_full) + charge_full = MBATTERY_PROP(CHARGE_FULL_DESIGN); + + charge_empty = MBATTERY_PROP(CHARGE_EMPTY); + /* if battery can't report this property, use design value */ + if (!charge_empty) + charge_empty = MBATTERY_PROP(CHARGE_EMPTY_DESIGN); + + charge = MBATTERY_PROP(CHARGE_AVG); + /* if battery can't report average value, use momentary */ + if (!charge) + charge = MBATTERY_PROP(CHARGE_NOW); + + current = MBATTERY_PROP(CURRENT_AVG); + /* if battery can't report average value, use momentary */ + if (!current) + current = MBATTERY_PROP(CURRENT_NOW); + + /* no luck */ + if (!current || !charge || !charge_empty) + return -1; + + if (status == BATTERY_STATUS_CHARGING) + return ((*charge - *charge_full) * 60L) / *current; + else + return -((*charge - *charge_empty) * 60L) / *current; +} + +/* TODO: teach this function to calculate battery life using energy */ +static int calculate_capacity(void) +{ + int ret; + int *charge_empty; + int *charge_full; + int *charge; + + charge_full = MBATTERY_PROP(CHARGE_FULL); + /* if battery can't report this property, use design value */ + if (!charge_full) + charge_full = MBATTERY_PROP(CHARGE_FULL_DESIGN); + + charge_empty = MBATTERY_PROP(CHARGE_EMPTY); + /* if battery can't report this property, use design value */ + if (!charge_empty) + charge_empty = MBATTERY_PROP(CHARGE_EMPTY_DESIGN); + + charge = MBATTERY_PROP(CHARGE_AVG); + /* if battery can't report average value, use momentary */ + if (!charge) + charge = MBATTERY_PROP(CHARGE_NOW); + + /* no luck */ + if (!charge_full || !charge_empty || !charge) + return -1; + + if (*charge_full - *charge_empty) + ret = ((*charge - *charge_empty) * 100L) / + (*charge_full - *charge_empty); + else + return -1; + + if (ret > 100) + return 100; + else if (ret < 0) + return 0; + else + return ret; +} + +static void apm_battery_apm_get_power_status(struct apm_power_info *info) +{ + int status; + int *capacity; + int *time_to_full; + int *time_to_empty; + + down(&battery_class->sem); + find_main_battery(); + if (!main_battery) { + up(&battery_class->sem); + return; + } + + /* status */ + + if (MBATTERY_PROP(STATUS)) + status = *(int *)MBATTERY_PROP(STATUS); + else + status = BATTERY_STATUS_UNKNOWN; + + /* ac line status */ + + if ((status == BATTERY_STATUS_CHARGING) || + (status == BATTERY_STATUS_NOT_CHARGING) || + (status == BATTERY_STATUS_FULL)) + info->ac_line_status = APM_AC_ONLINE; + else + info->ac_line_status = APM_AC_OFFLINE; + + /* battery life (i.e. capacity, in percents) */ + + capacity = MBATTERY_PROP(CAPACITY); + if (capacity) + info->battery_life = *capacity; + else + info->battery_life = calculate_capacity(); + + /* charging status */ + + if (status == BATTERY_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; + + time_to_empty = MBATTERY_PROP(TIME_TO_EMPTY_AVG); + if (!time_to_empty) + time_to_empty = MBATTERY_PROP(TIME_TO_EMPTY_NOW); + + time_to_full = MBATTERY_PROP(TIME_TO_FULL_AVG); + if (!time_to_full) + time_to_full = MBATTERY_PROP(TIME_TO_FULL_NOW); + + if (status == BATTERY_STATUS_CHARGING && time_to_full) + info->time = *time_to_full / 60; + else if (status != BATTERY_STATUS_CHARGING && time_to_empty) + info->time = *time_to_empty / 60; + else + info->time = calculate_time(status); + + up(&battery_class->sem); + return; +} + +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; + return; +} + +module_init(apm_battery_init); +module_exit(apm_battery_exit); + +MODULE_AUTHOR("Eugeny Boger <[EMAIL PROTECTED]>"); +MODULE_DESCRIPTION("APM emulation driver for battery monitoring class"); +MODULE_LICENSE("GPL"); -- 1.5.1.1-dirty - To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [EMAIL PROTECTED] More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/