diff --git a/hald/Makefile.am b/hald/Makefile.am index 7201b1a..7e369f6 100644 --- a/hald/Makefile.am +++ b/hald/Makefile.am @@ -41,6 +41,7 @@ hald_SOURCES = util.h util.c \ util_helper.h util_helper.c \ util_pm.h util_pm.c \ + util_battery.h util_battery.c \ hald_runner.h hald_runner.c \ device.h device.c \ device_info.h device_info.c \ diff --git a/hald/linux/acpi.c b/hald/linux/acpi.c index 2c59fbf..4dbecff 100644 --- a/hald/linux/acpi.c +++ b/hald/linux/acpi.c @@ -32,6 +32,7 @@ #include "../hald_dbus.h" #include "../logger.h" #include "../util.h" #include "../util_pm.h" +#include "../util_battery.h" #include "osspec_linux.h" @@ -77,27 +78,31 @@ ac_adapter_refresh_poll (HalDevice *d) hal_util_set_bool_elem_from_file (d, "ac_adapter.present", path, "state", "state", 0, "on-line", FALSE); } +/* architecture specific bits should try to populate: +- current +- last_full +- is_charging +- is_discharging +- rate +- voltage +- last_full + +TODO: don't read the key twice from haldb +TODO: only save the key if proc entry present + */ static void -battery_refresh_poll (HalDevice *d) +battery_refresh_acpi (HalDevice *d, const char *path) { - const char *path; - const char *reporting_unit; - int reporting_current; - int reporting_lastfull; - int reporting_rate; - int normalised_current; - int normalised_lastfull; - int normalised_rate; - int design_voltage; - int voltage; - int remaining_time; - int remaining_percentage; - gboolean charging; - gboolean discharging; + HalBattery *bat; - path = hal_device_property_get_string (d, "linux.acpi_path"); - if (path == NULL) + /* get battery object */ + bat = util_battery_get_data (d); + if (bat == NULL) { + HAL_WARNING (("Battery object not found")); return; + } + + /* get data from proc, add it to the battery.reporting.* keys and copy to HalBattery struct */ hal_util_set_bool_elem_from_file (d, "battery.rechargeable.is_charging", path, "state", "charging state", 0, "charging", TRUE); @@ -115,132 +120,42 @@ battery_refresh_poll (HalDevice *d) if (!hal_util_set_int_elem_from_file (d, "battery.reporting.rate", path, "state", "present rate", 0, 10, TRUE)) hal_device_property_set_int (d, "battery.reporting.rate", 0); + /* * we'll need this if we need to convert mAh to mWh, but we should * also update it here anyway as the value will have changed */ hal_util_set_int_elem_from_file (d, "battery.voltage.current", path, "state", "present voltage", 0, 10, TRUE); - /* get all the data we know */ - reporting_unit = hal_device_property_get_string (d, - "battery.reporting.unit"); - reporting_current = hal_device_property_get_int (d, + + /* get all the data we know (probably need to check for -1 also) */ + bat->current = hal_device_property_get_int (d, "battery.reporting.current"); - reporting_lastfull = hal_device_property_get_int (d, + bat->last_full = hal_device_property_get_int (d, "battery.reporting.last_full"); - reporting_rate = hal_device_property_get_int (d, + bat->rate = hal_device_property_get_int (d, "battery.reporting.rate"); + bat->voltage = hal_device_property_get_int (d, + "battery.voltage.current"); - /* - * ACPI gives out the special 'Ones' value for rate when it's unable - * to calculate the true rate. We should set the rate zero, and wait - * for the BIOS to stabilise. - * - * full details here: http://bugzilla.gnome.org/show_bug.cgi?id=348201 - */ - if (reporting_rate == 0xffff) { - reporting_rate = 0; - } - - /* - * We are converting the unknown units into mWh because of ACPI's nature - * of not having a standard "energy" unit. - * - * full details here: http://bugzilla.gnome.org/show_bug.cgi?id=309944 - */ - if (reporting_unit && strcmp (reporting_unit, "mAh") == 0) { - /* convert mAh to mWh by multiplying by voltage. due to the - * general wonkiness of ACPI implementations, this is a lot - * harder than it should have to be... - */ - - design_voltage = hal_device_property_get_int (d, "battery.voltage.design"); - voltage = hal_device_property_get_int (d, "battery.voltage.current"); - - /* Just in case we don't get design voltage information, then - * this will pretend that we have 1V. This degrades our - * ability to report accurate times on multi-battery systems - * but will always prevent negative charge levels and allow - * accurate reporting on single-battery systems. - */ - if (design_voltage <= 0) - design_voltage = 1000; /* mV */ - - /* If the current voltage is unknown, smaller than 50% of design voltage (fd.o #8593) - * or greater than design, then use design voltage. - */ - if (voltage < (design_voltage/2) || voltage > design_voltage) { - HAL_DEBUG (("Current voltage is unknown, smaller than 50% or greater than design")); - voltage = design_voltage; - } - - normalised_current = (reporting_current * voltage) / 1000; - normalised_lastfull = (reporting_lastfull * voltage) / 1000; - normalised_rate = (reporting_rate * voltage) / 1000; - } else { - /* - * handle as if mWh (which don't need conversion), which is the most common case. - */ - normalised_current = reporting_current; - normalised_lastfull = reporting_lastfull; - normalised_rate = reporting_rate; - } - - /* - * Set the normalised keys. - */ - if (normalised_current < 0) - normalised_current = 0; - if (normalised_lastfull < 0) - normalised_lastfull = 0; - if (normalised_rate < 0) - normalised_rate = 0; - - /* - * Some laptops report a rate even when not charging or discharging. - * If not charging and not discharging force rate to be zero. - * - * http://bugzilla.gnome.org/show_bug.cgi?id=323186 - */ - charging = hal_device_property_get_bool (d, "battery.rechargeable.is_charging"); - discharging = hal_device_property_get_bool (d, "battery.rechargeable.is_discharging"); + bat->is_charging = hal_device_property_get_bool (d, "battery.rechargeable.is_charging"); + bat->is_discharging = hal_device_property_get_bool (d, "battery.rechargeable.is_discharging"); +} - if (!charging && !discharging) - normalised_rate = 0; +static void +battery_refresh_poll (HalDevice *d) +{ + const char *path; - /* - * Some laptops report current charge much larger than - * full charge when at 100%. Clamp back down to 100%. - */ - if (normalised_current > normalised_lastfull) - normalised_current = normalised_lastfull; - - hal_device_property_set_int (d, "battery.charge_level.current", normalised_current); - hal_device_property_set_int (d, "battery.charge_level.last_full", normalised_lastfull); - hal_device_property_set_int (d, "battery.charge_level.rate", normalised_rate); - - remaining_time = util_compute_time_remaining ( - hal_device_get_udi (d), - normalised_rate, normalised_current, normalised_lastfull, - hal_device_property_get_bool (d, "battery.rechargeable.is_discharging"), - hal_device_property_get_bool (d, "battery.rechargeable.is_charging"), - hal_device_property_get_bool (d, "battery.remaining_time.calculate_per_time")); - remaining_percentage = util_compute_percentage_charge (hal_device_get_udi (d), normalised_current, normalised_lastfull); - /* - * Only set keys if no error (signified with negative return value) - * Scrict checking is needed to ensure that the values presented by HAL - * are 100% acurate. - */ + path = hal_device_property_get_string (d, "linux.acpi_path"); + if (path == NULL) + return; - if (remaining_time > 0) - hal_device_property_set_int (d, "battery.remaining_time", remaining_time); - else - hal_device_property_remove (d, "battery.remaining_time"); + /* do acpi specific bit */ + battery_refresh_acpi (d, path); - if (remaining_percentage > 0) - hal_device_property_set_int (d, "battery.charge_level.percentage", remaining_percentage); - else - hal_device_property_remove (d, "battery.charge_level.percentage"); + /* do generic bit */ + util_battery_update_values (d); } /** Recalculates the battery.reporting.last_full key as this may drift @@ -462,10 +377,10 @@ battery_refresh_add (HalDevice *d, const int reporting_low; int reporting_gran1; int reporting_gran2; - int voltage_design; - - const char *reporting_unit; const char *technology; + const char *strunit; + + HalBattery *bat; hal_util_set_string_elem_from_file (d, "battery.vendor", path, "info", "OEM info", 0, TRUE); @@ -488,15 +403,30 @@ battery_refresh_add (HalDevice *d, const } /* + * Create the HAL class data if it does not already exist + */ + bat = util_battery_get_data (d); + if (bat == NULL) { + bat = g_new (HalBattery, 1); + g_object_set_data ((gpointer) d, "battery-object", bat); + } + + /* get the common stuff and put into battery class data */ + battery_refresh_acpi (d, path); + + /* * we'll use the .reporting prefix as we don't know * if this data is energy (mWh) or unit enery (mAh) */ hal_util_set_string_elem_from_file (d, "battery.reporting.unit", path, "info", "design capacity", 1, TRUE); - hal_util_set_int_elem_from_file (d, "battery.reporting.last_full", path, - "info", "last full capacity", 0, 10, TRUE); + strunit = hal_device_property_get_string (d, + "battery.reporting.unit"); + util_battery_set_unit_from_string (bat, strunit); + hal_util_set_int_elem_from_file (d, "battery.reporting.design", path, "info", "design capacity", 0, 10, TRUE); + hal_util_set_int_elem_from_file (d, "battery.reporting.warning", path, "info", "design capacity warning", 0, 10, TRUE); hal_util_set_int_elem_from_file (d, "battery.reporting.low", path, @@ -518,31 +448,29 @@ battery_refresh_add (HalDevice *d, const * are not going to change. * We'll set the correct unit (or unknown) also. */ - reporting_unit = hal_device_property_get_string (d, "battery.reporting.unit"); reporting_design = hal_device_property_get_int (d, "battery.reporting.design"); reporting_warning = hal_device_property_get_int (d, "battery.reporting.warning"); reporting_low = hal_device_property_get_int (d, "battery.reporting.low"); reporting_gran1 = hal_device_property_get_int (d, "battery.reporting.granularity_1"); reporting_gran2 = hal_device_property_get_int (d, "battery.reporting.granularity_2"); - if (reporting_unit && strcmp (reporting_unit, "mAh") == 0) { - voltage_design = hal_device_property_get_int (d, "battery.voltage.design"); - - /* If design voltage is unknown, use 1V. */ - if (voltage_design <= 0) - voltage_design = 1000; /* mV */; + /* correct the voltage */ + bat->design_voltage = hal_device_property_get_int (d, "battery.voltage.design"); + util_battery_check_voltage (d, bat); + + if (bat->unit == HAL_BATTERY_TYPE_MAH) { - /* scale by factor battery.voltage.design */ + /* scale by voltage */ hal_device_property_set_int (d, "battery.charge_level.design", - (reporting_design * voltage_design) / 1000); + (reporting_design * bat->voltage) / 1000); hal_device_property_set_int (d, "battery.charge_level.warning", - (reporting_warning * voltage_design) / 1000); + (reporting_warning * bat->voltage) / 1000); hal_device_property_set_int (d, "battery.charge_level.low", - (reporting_low * voltage_design) / 1000); + (reporting_low * bat->voltage) / 1000); hal_device_property_set_int (d, "battery.charge_level.granularity_1", - (reporting_gran1 * voltage_design) / 1000); + (reporting_gran1 * bat->voltage) / 1000); hal_device_property_set_int (d, "battery.charge_level.granularity_2", - (reporting_gran2 * voltage_design) / 1000); + (reporting_gran2 * bat->voltage) / 1000); /* set unit */ hal_device_property_set_string (d, "battery.charge_level.unit", "mWh"); /* not mAh! */ @@ -558,7 +486,7 @@ battery_refresh_add (HalDevice *d, const hal_device_property_set_int (d, "battery.charge_level.granularity_2", reporting_gran2); /* set unit */ - if (reporting_unit && strcmp (reporting_unit, "mWh") == 0) { + if (bat->unit == HAL_BATTERY_TYPE_MWH) { hal_device_property_set_string (d, "battery.charge_level.unit", "mWh"); } else { /* set "Unknown ACPI Unit" unit so we can debug */ diff --git a/hald/util_pm.c b/hald/util_pm.c index 0bc62e8..e93554f 100644 --- a/hald/util_pm.c +++ b/hald/util_pm.c @@ -1,9 +1,9 @@ /*************************************************************************** * CVSID: $Id$ * - * utili_pm.c - Various Powermanagement related utilities + * util_pm.c - Various Powermanagement related utilities * - * Copyright (C) 2005 Richard Hughes + * Copyright (C) 2005-2006 Richard Hughes * Copyright (C) 2005 Danny Kukawka * * Licensed under the Academic Free License version 2.1 @@ -36,9 +36,10 @@ #include "logger.h" #include "util_pm.h" +/* depreciated; remove PMU and APM users */ typedef struct { int last_level; - int last_chargeRate; + int last_rate; time_t last_time; } batteryInfo; @@ -77,30 +78,32 @@ util_get_battery_technology (const char return "unknown"; } + +/* BELOW ARE DEPRECIATED */ /** Given all the required parameters, this function will return the percentage * charge remaining. There are lots of checks here as ACPI is often broken. * * @param id Optional ID given to this battery. Unused at present. - * @param chargeLevel The current charge level of the battery (typically mWh) - * @param chargeLastFull The last "full" charge of the battery (typically mWh) + * @param current The current charge level of the battery (typically mWh) + * @param last_full The last "full" charge of the battery (typically mWh) * @return Percentage, -1 if invalid */ int util_compute_percentage_charge (const char *id, - int chargeLevel, - int chargeLastFull) + int current, + int last_full) { int percentage; /* make sure we have chargelevel */ - if (chargeLevel <= 0) { - HAL_WARNING (("chargeLevel %i, returning -1!", chargeLevel)); + if (current <= 0) { + HAL_WARNING (("current %i, returning -1!", current)); return -1; } /* make sure not division by zero */ - if (chargeLastFull > 0) - percentage = ((double) chargeLevel / (double) chargeLastFull) * 100; + if (last_full > 0) + percentage = ((double) current / (double) last_full) * 100; else { - HAL_WARNING (("chargeLastFull %i, percentage returning -1!", chargeLastFull)); + HAL_WARNING (("last_full %i, percentage returning -1!", last_full)); return -1; } /* Some bios's will report this higher than 100, limit it here */ @@ -121,41 +124,41 @@ util_compute_percentage_charge (const ch * of seconds until empty (if discharging) * * @param id Optional ID given to this battery. Unused at present. - * @param chargeRate The "rate" (typically mW) - * @param chargeLevel The current charge level of the battery (typically mWh) - * @param chargeLastFull The last "full" charge of the battery (typically mWh) - * @param isDischarging If battery is discharging - * @param isCharging If battery is charging - * @param guessChargeRate If ignore chargeRate and guess them. + * @param rate The "rate" (typically mW) + * @param current The current charge level of the battery (typically mWh) + * @param last_full The last "full" charge of the battery (typically mWh) + * @param is_discharging If battery is discharging + * @param is_charging If battery is charging + * @param guessChargeRate If ignore rate and guess them. * @return Number of seconds, or -1 if invalid */ int util_compute_time_remaining (const char *id, - int chargeRate, - int chargeLevel, - int chargeLastFull, - gboolean isDischarging, - gboolean isCharging, + int rate, + int current, + int last_full, + gboolean is_discharging, + gboolean is_charging, gboolean guessChargeRate) { int remaining_time = 0; /* should not get negative values */ - if (chargeRate < 0 || chargeLevel < 0 || chargeLastFull < 0) { - HAL_WARNING (("chargeRate, chargeLevel or chargeLastFull < 0, returning -1")); + if (rate < 0 || current < 0 || last_full < 0) { + HAL_WARNING (("rate, current or last_full < 0, returning -1")); return -1; } /* batteries cannot charge and discharge at the same time */ - if (isDischarging && isCharging) { - HAL_WARNING (("isDischarging & isCharging TRUE, returning -1")); + if (is_discharging && is_charging) { + HAL_WARNING (("is_discharging & is_charging TRUE, returning -1")); return -1; } /* * Some laptops don't supply any rate info, but that's no reason for HAL not - * to. We use the current and previous chargeLevel to estimate the rate. + * to. We use the current and previous current to estimate the rate. * The info is stored in a GHashTable because there could be more than one battery. */ - if (chargeRate == 0 || guessChargeRate) { + if (rate == 0 || guessChargeRate || 1) { batteryInfo *battery_info; time_t cur_time = time(NULL); @@ -165,50 +168,52 @@ util_compute_time_remaining (const char if ((battery_info = g_hash_table_lookup(saved_battery_info, id))) { /* check this to prevent division by zero */ - if ((cur_time == battery_info->last_time) || (chargeLevel == battery_info->last_level)) { + if ((cur_time == battery_info->last_time) || (current == battery_info->last_level)) { /* if we can't calculate because nothing changed, use last - * chargeRate to prevent removing battery.remaining_time. + * rate to prevent removing battery.remaining_time. */ - chargeRate = battery_info->last_chargeRate; + rate = battery_info->last_rate; } else { - chargeRate = ((chargeLevel - battery_info->last_level) * 60 * 60) / (cur_time - battery_info->last_time); + rate = ((current - battery_info->last_level) * 60 * 60) / (cur_time - battery_info->last_time); /* - * During discharging chargeRate would be negative, which would + * During discharging rate would be negative, which would * mess up the the calculation below, so we make sure it's always * positive. */ - chargeRate = (chargeRate > 0) ? chargeRate : -chargeRate; + rate = (rate > 0) ? rate : -rate; - battery_info->last_level = chargeLevel; + battery_info->last_level = current; battery_info->last_time = cur_time; - battery_info->last_chargeRate = chargeRate; + battery_info->last_rate = rate; } } else { battery_info = g_new0(batteryInfo, 1); g_hash_table_insert(saved_battery_info, (char*) id, battery_info); - battery_info->last_level = chargeLevel; + battery_info->last_level = current; battery_info->last_time = cur_time; - battery_info->last_chargeRate = 0; + battery_info->last_rate = 0; return -1; } } - if (chargeRate == 0) + HAL_WARNING (("rate is %i", rate)); + + if (rate == 0) return -1; - if (isDischarging) { - remaining_time = ((double) chargeLevel / (double) chargeRate) * 60 * 60; - } else if (isCharging) { + if (is_discharging) { + remaining_time = ((double) current / (double) rate) * 60 * 60; + } else if (is_charging) { /* - * Some ACPI BIOS's don't update chargeLastFull, - * so return 0 as we don't know how much more there is left + * Some ACPI BIOS's don't update last_full, + * so return missing as we don't know how much more there is left */ - if (chargeLevel > chargeLastFull ) { - HAL_WARNING (("chargeLevel > chargeLastFull, returning -1")); + if (current > last_full ) { + HAL_WARNING (("current > last_full, returning -1")); return -1; } - remaining_time = ((double) (chargeLastFull - chargeLevel) / (double) chargeRate) * 60 * 60; + remaining_time = ((double) (last_full - current) / (double) rate) * 60 * 60; } /* This shouldn't happen, but check for completeness */ diff --git a/hald/util_pm.h b/hald/util_pm.h index 977531e..b17ec55 100644 --- a/hald/util_pm.h +++ b/hald/util_pm.h @@ -29,9 +29,9 @@ #define UTIL_PM_H const char *util_get_battery_technology (const char *type); +/* depreciated */ int util_compute_time_remaining (const char *id, int chargeRate, int chargeLevel, int chargeLastFull, gboolean isDischarging, gboolean isCharging, gboolean guessChargeRate); - int util_compute_percentage_charge (const char *id, int chargeLevel, int chargeLastFull); #endif /* UTIL__PM_H */ --- /dev/null 2006-10-16 22:06:18.000000000 +0100 +++ hald/util_battery.c 2006-11-06 01:38:29.000000000 +0000 @@ -0,0 +1,323 @@ +/*************************************************************************** + * + * util_battery.c - Battery class + * + * Copyright (C) 2005-2006 Richard Hughes + * Copyright (C) 2005 Danny Kukawka + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + **************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include + +#include "logger.h" +#include "device_info.h" +#include "util_battery.h" + +HalBattery * +util_battery_get_data (HalDevice *d) +{ + HalBattery *bat; + bat = g_object_get_data ((gpointer) d, "battery-object"); + return bat; +} + +void +util_battery_set_unit_from_string (HalBattery *bat, const char *unit) +{ + bat->unit = HAL_BATTERY_TYPE_UNKNOWN; + + /* if the hardware does not set a unit, assume mWh */ + if (unit == NULL) { + bat->unit = HAL_BATTERY_TYPE_MWH; + } else { + if (strcmp (unit, "mAh") == 0) { + bat->unit = HAL_BATTERY_TYPE_MAH; + } else if (strcmp (unit, "mWh") == 0) { + bat->unit = HAL_BATTERY_TYPE_MWH; + } + } +} + +void +util_battery_check_voltage (HalDevice *d, HalBattery *bat) +{ + /* Just in case we don't get design voltage information, then + * this will pretend that we have 10V. This degrades our + * ability to report accurate times on multi-battery systems + * but will always prevent negative charge levels and allow + * accurate reporting on single-battery systems. + */ + if (bat->design_voltage <= 0 || bat->design_voltage > 300000) { + HAL_DEBUG (("Design voltage is invalid or over 300V")); + bat->design_voltage = 10000; /* mV */ + } + + /* If the current voltage is unknown, smaller than 50% of design voltage (fd.o #8593) + * or greater than design, then use design voltage. + */ + if (bat->voltage < (bat->design_voltage / 2) || bat->voltage > bat->design_voltage) { + HAL_DEBUG (("Current voltage is unknown, smaller than 50%% or greater than design")); + bat->voltage = bat->design_voltage; + } + + HAL_DEBUG (("battery voltage\t%i", bat->voltage)); +} + +static void +util_battery_normalise_mwh (HalDevice *d, HalBattery *bat) +{ + /* We are converting the unknown units into mWh because of ACPI's nature + * of not having a standard "energy" unit. + * + * full details here: http://bugzilla.gnome.org/show_bug.cgi?id=309944 + */ + if (bat->unit == HAL_BATTERY_TYPE_MAH) { + /* convert mAh to mWh by multiplying by voltage. */ + bat->current = (bat->current * bat->voltage) / 1000; + bat->last_full = (bat->last_full * bat->voltage) / 1000; + bat->rate = (bat->rate * bat->voltage) / 1000; + } + HAL_DEBUG (("battery rate\t%i", bat->rate)); + HAL_DEBUG (("battery last_full\t%i", bat->last_full)); + HAL_DEBUG (("battery current\t%i", bat->current)); +} + +static void +util_battery_calculate_rate (HalDevice *d, HalBattery *bat) +{ + time_t cur_time; + gboolean use_fixup; + + /* + * ACPI gives out the special 'Ones' value for rate when it's unable + * to calculate the true rate. We should set the rate zero, and wait + * for the BIOS to stabilise. + * + * full details here: http://bugzilla.gnome.org/show_bug.cgi?id=348201 + */ + if (bat->rate == 0xffff || bat->rate == 0xffffff) { + bat->rate = -1; + goto out; + } + + /* + * Some laptops report a rate even when not charging or discharging. + * If not charging and not discharging force rate to be zero. + * + * http://bugzilla.gnome.org/show_bug.cgi?id=323186 + */ + if (!bat->is_charging && !bat->is_discharging) { + bat->rate = 0; + goto out; + } + + /* should not get negative values */ + if (bat->rate < 0 || bat->current < 0 || bat->last_full < 0) { + HAL_WARNING (("rate, current or last_full < 0, returning -1")); + bat->rate = -1; + goto out; + } + /* batteries cannot charge and discharge at the same time */ + if (bat->is_discharging && bat->is_charging) { + HAL_WARNING (("is_discharging & is_charging TRUE, returning 0")); + bat->rate = 0; + goto out; + } + + /* + * Some laptops don't supply any rate info, but that's no reason for HAL not + * to. We use the current and previous current to estimate the rate. + * The info is stored in a GHashTable because there could be more than one battery. + */ + use_fixup = hal_device_property_get_bool (d, "battery.remaining_time.calculate_per_time"); + if (TRUE || bat->rate == 0 || use_fixup) { + cur_time = time (NULL); + + /* check this to prevent division by zero */ + if ((cur_time == bat->last_time) || (bat->current == bat->last_level)) { + /* if we can't calculate because nothing changed, use last + * rate to prevent removing battery.remaining_time. + */ + bat->rate = bat->last_rate; + } else { + bat->rate = ((bat->current - bat->last_level) * 60 * 60) / (cur_time - bat->last_time); + /* + * During discharging rate would be negative, which would + * mess up the the calculation below, so we make sure it's always + * positive. + */ + if (bat->rate < 0) { + bat->rate = -bat->rate; + } + bat->last_level = bat->current; + bat->last_time = cur_time; + } + } + + /* don't even store the key if we can't calculate it */ + if (bat->rate == 0) { + bat->rate = -1; + goto out; + } +out: + HAL_DEBUG (("battery rate\t%i", bat->rate)); +} + +static void +util_battery_calculate_time_remaining (HalDevice *d, HalBattery *bat) +{ + if (bat->rate == 0) { + HAL_WARNING (("zero rate, setting -1")); + bat->remaining_time = -1; + goto out; + } + + if (bat->is_discharging) { + bat->remaining_time = ((double) bat->current / (double) bat->rate) * 60 * 60; + } else if (bat->is_charging) { + bat->remaining_time = ((double) (bat->last_full - bat->current) / (double) bat->rate) * 60 * 60; + } + + /* This shouldn't happen, but check for completeness */ + if (bat->remaining_time < 0) { + HAL_WARNING (("remaining_time %i, setting -1", bat->remaining_time)); + bat->remaining_time = -1; + goto out; + } + + /* Battery life cannot be above 60 hours */ + if (bat->remaining_time > 60 * 60 * 60) { + HAL_WARNING (("remaining_time *very* high, returning -1")); + bat->remaining_time = -1; + goto out; + } +out: + HAL_DEBUG (("battery time\t%i", bat->remaining_time)); +} + +static void +util_battery_check_charge (HalDevice *d, HalBattery *bat) +{ + /* + * Some laptops report current charge much larger than + * full charge when at 100%. Clamp back down to 100%. + */ + if (bat->current > bat->last_full) { + bat->current = bat->last_full; + } +} + +/* writes the HalBattery struct to the tree as battery.charge_level.* */ +static void +util_battery_commit (HalDevice *d, HalBattery *bat) +{ + /* + * Only set keys if no error (signified with negative return value) + * Scrict checking is needed to ensure that the values presented by HAL + * are 100% acurate. + */ + if (bat->current > 0) { + hal_device_property_set_int (d, "battery.charge_level.current", bat->current); + } else { + hal_device_property_remove (d, "battery.charge_level.current"); + } + if (bat->last_full > 0) { + hal_device_property_set_int (d, "battery.charge_level.last_full", bat->last_full); + } else { + hal_device_property_remove (d, "battery.charge_level.last_full"); + } + if (bat->rate > 0) { + hal_device_property_set_int (d, "battery.charge_level.rate", bat->rate); + } else { + hal_device_property_remove (d, "battery.charge_level.rate"); + } + if (bat->percentage > 0) { + hal_device_property_set_int (d, "battery.charge_level.percentage", bat->percentage); + } else { + hal_device_property_remove (d, "battery.charge_level.percentage"); + } + if (bat->percentage > 0) { + hal_device_property_set_int (d, "battery.remaining_time", bat->remaining_time); + } else { + hal_device_property_remove (d, "battery.remaining_time"); + } +} + +static void +util_battery_compute_percentage_charge (HalDevice *d, HalBattery *bat) +{ + int percentage; + /* make sure we have chargelevel */ + if (bat->current <= 0) { + HAL_WARNING (("current %i, set to -1!", bat->current)); + percentage = -1; + goto out; + } + /* make sure not division by zero */ + if (bat->last_full > 0) + percentage = ((double) bat->current / (double) bat->last_full) * 100; + else { + HAL_WARNING (("last_full %i, percentage set to -1!", bat->last_full)); + percentage = -1; + goto out; + } + /* Some bios's will report this higher than 100, limit it here */ + if (percentage > 100) { + HAL_WARNING (("Percentage %i, set to 100", percentage)); + percentage = 100; + goto out; + } + /* Something really isn't right if we get a negative... */ + if (percentage < 0) { + HAL_WARNING (("Percentage %i, set to -1!", percentage)); + percentage = -1; + goto out; + } +out: + bat->percentage = percentage; +} + +void +util_battery_update_values (HalDevice *d) +{ + HalBattery *bat; + + /* get battery object */ + bat = util_battery_get_data (d); + if (bat == NULL) { + HAL_WARNING (("Battery object not found")); + return; + } + + util_battery_check_voltage (d, bat); + util_battery_normalise_mwh (d, bat); + util_battery_check_charge (d, bat); + util_battery_compute_percentage_charge (d, bat); + util_battery_calculate_rate (d, bat); + util_battery_calculate_time_remaining (d, bat); + util_battery_commit (d, bat); +} --- /dev/null 2006-10-16 22:06:18.000000000 +0100 +++ hald/util_battery.h 2006-11-06 01:37:09.000000000 +0000 @@ -0,0 +1,61 @@ +/*************************************************************************** + * + * util_battery.c - Battery class + * + * Copyright (C) 2006 Richard Hughes + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + **************************************************************************/ + +#ifndef UTIL_BATTERY_H +#define UTIL_BATTERY_H + +#include "device.h" +#include "device_store.h" + +typedef enum { + HAL_BATTERY_TYPE_MWH, + HAL_BATTERY_TYPE_MAH, + HAL_BATTERY_TYPE_UNKNOWN +} HalBatteryType; + +typedef struct { + HalBatteryType unit; + const char *udi; + const char *path; + gboolean is_charging; + gboolean is_discharging; + int current; + int last_full; + int rate; + int design_voltage; + int voltage; + int remaining_time; + int percentage; + /* needed for rate calculations */ + int last_level; + int last_rate; + time_t last_time; +} HalBattery; + +HalBattery *util_battery_get_data (HalDevice *d); +void util_battery_update_values (HalDevice *d); +void util_battery_check_voltage (HalDevice *d, HalBattery *bat); +void util_battery_set_unit_from_string (HalBattery *bat, const char *unit); + +#endif /* UTIL_BATTERY_H */