Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1767443AbXECVgN (ORCPT ); Thu, 3 May 2007 17:36:13 -0400 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S1767437AbXECVgM (ORCPT ); Thu, 3 May 2007 17:36:12 -0400 Received: from mail.z-net.ru ([89.113.80.10]:4211 "EHLO mail.z-net.ru" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1767430AbXECVgK (ORCPT ); Thu, 3 May 2007 17:36:10 -0400 Date: Fri, 4 May 2007 01:32:37 +0400 From: Anton Vorontsov To: Greg KH Cc: linux-kernel@vger.kernel.org, kernel-discuss@handhelds.org, David Woodhouse Subject: [PATCH 5/8] APM emulation driver for class batteries Message-ID: <20070503213237.GE20067@zarina> MIME-Version: 1.0 Content-Type: text/plain; charset=koi8-r Content-Disposition: inline User-Agent: Mutt/1.5.15 (2007-04-06) Sender: linux-kernel-owner@vger.kernel.org X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 8476 Lines: 296 Signed-off-by: Anton Vorontsov --- drivers/power/Kconfig | 7 ++ drivers/power/Makefile | 1 + drivers/power/apm_power.c | 249 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 257 insertions(+), 0 deletions(-) create mode 100644 drivers/power/apm_power.c diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index cc70644..791fa0c 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -21,4 +21,11 @@ config PDA_POWER one or two external power supplies (AC/USB) connected to main and backup batteries, and optional builtin charger. +config APM_POWER + tristate "APM emulation for class batteries" + depends on APM_EMULATION + help + Say Y here to enable support APM status emulation using + battery class devices. + endif # POWER_SUPPLY diff --git a/drivers/power/Makefile b/drivers/power/Makefile index 1808b69..a8fbc7e 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -15,3 +15,4 @@ endif obj-$(CONFIG_POWER_SUPPLY) += power_supply.o obj-$(CONFIG_PDA_POWER) += pda_power.o +obj-$(CONFIG_APM_POWER) += apm_power.o diff --git a/drivers/power/apm_power.c b/drivers/power/apm_power.c new file mode 100644 index 0000000..6a3a2f5 --- /dev/null +++ b/drivers/power/apm_power.c @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2007 Anton Vorontsov + * Copyright (c) 2007 Eugeny Boger + * + * Author: Eugeny Boger + * + * 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 +#include +#include + +#ifdef current +#undef current /* it expands to get_current() */ +#endif + +#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 struct power_supply *main_battery; + +static void find_main_battery(void) +{ + struct device *dev; + struct power_supply *bat, *batm; + union power_supply_propval full; + int max_charge = 0; + + main_battery = NULL; + batm = NULL; + list_for_each_entry(dev, &power_supply_class->devices, node) { + bat = dev_get_drvdata(dev); + /* If none of battery devices cantains 'use_for_apm' flag, + choice one with maximum design charge */ + if (!PSY_PROP(bat, CHARGE_FULL_DESIGN, &full)) { + if (full.intval > max_charge) { + batm = bat; + max_charge = full.intval; + } + } + + if (bat->use_for_apm) + main_battery = bat; + } + if (!main_battery) + main_battery = batm; + + return; +} + +static int calculate_time(int status) +{ + union power_supply_propval charge_full, charge_empty; + union power_supply_propval charge, current; + + if (MPSY_PROP(CHARGE_FULL, &charge_full)) { + /* if battery can't report this property, use design value */ + if (MPSY_PROP(CHARGE_FULL_DESIGN, &charge_full)) + return -1; + } + + if (MPSY_PROP(CHARGE_EMPTY, &charge_empty)) { + /* if battery can't report this property, use design value */ + if (MPSY_PROP(CHARGE_EMPTY_DESIGN, &charge_empty)) + charge_empty.intval = 0; + } + + if (MPSY_PROP(CHARGE_AVG, &charge)) { + /* if battery can't report average value, use momentary */ + if (MPSY_PROP(CHARGE_NOW, &charge)) + return -1; + } + + if (MPSY_PROP(CURRENT_AVG, ¤t)) { + /* if battery can't report average value, use momentary */ + if (MPSY_PROP(CURRENT_NOW, ¤t)) + return -1; + } + + if (status == POWER_SUPPLY_STATUS_CHARGING) + return ((charge.intval - charge_full.intval) * 60L) / + current.intval; + else + return -((charge.intval - charge_empty.intval) * 60L) / + current.intval; +} + +static int calculate_capacity(int using_charge) +{ + 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; + + if (using_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; + } + else { + 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; + } + + 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; + + down(&power_supply_class->sem); + find_main_battery(); + if (!main_battery) { + up(&power_supply_class->sem); + 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(0); + /* if failed try calculate using charge instead */ + if (info->battery_life == -1) + info->battery_life = calculate_capacity(1); + } + + /* 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)) { + if (MPSY_PROP(TIME_TO_FULL_NOW, &time_to_full)) + info->time = calculate_time(status.intval); + else + info->time = time_to_full.intval / 60; + } + } + else { + if (MPSY_PROP(TIME_TO_EMPTY_AVG, &time_to_empty)) { + if (MPSY_PROP(TIME_TO_EMPTY_NOW, &time_to_empty)) + info->time = calculate_time(status.intval); + else + info->time = time_to_empty.intval / 60; + } + } + + up(&power_supply_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 "); +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 majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/