18c2aff776a775d34a4c9893a4c72e0434d68e36artem/***************************************************************************
18c2aff776a775d34a4c9893a4c72e0434d68e36artem * CVSID: $Id$
18c2aff776a775d34a4c9893a4c72e0434d68e36artem *
18c2aff776a775d34a4c9893a4c72e0434d68e36artem * utili_pm.c - Various Powermanagement related utilities
18c2aff776a775d34a4c9893a4c72e0434d68e36artem *
18c2aff776a775d34a4c9893a4c72e0434d68e36artem * Copyright (C) 2005 Richard Hughes <richard@hughsie.com>
18c2aff776a775d34a4c9893a4c72e0434d68e36artem * Copyright (C) 2005 Danny Kukawka <danny.kukawka@web.de>
18c2aff776a775d34a4c9893a4c72e0434d68e36artem *
18c2aff776a775d34a4c9893a4c72e0434d68e36artem * Licensed under the Academic Free License version 2.1
18c2aff776a775d34a4c9893a4c72e0434d68e36artem *
18c2aff776a775d34a4c9893a4c72e0434d68e36artem * This program is free software; you can redistribute it and/or modify
18c2aff776a775d34a4c9893a4c72e0434d68e36artem * it under the terms of the GNU General Public License as published by
18c2aff776a775d34a4c9893a4c72e0434d68e36artem * the Free Software Foundation; either version 2 of the License, or
18c2aff776a775d34a4c9893a4c72e0434d68e36artem * (at your option) any later version.
18c2aff776a775d34a4c9893a4c72e0434d68e36artem *
18c2aff776a775d34a4c9893a4c72e0434d68e36artem * This program is distributed in the hope that it will be useful,
18c2aff776a775d34a4c9893a4c72e0434d68e36artem * but WITHOUT ANY WARRANTY; without even the implied warranty of
18c2aff776a775d34a4c9893a4c72e0434d68e36artem * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18c2aff776a775d34a4c9893a4c72e0434d68e36artem * GNU General Public License for more details.
18c2aff776a775d34a4c9893a4c72e0434d68e36artem *
18c2aff776a775d34a4c9893a4c72e0434d68e36artem * You should have received a copy of the GNU General Public License
18c2aff776a775d34a4c9893a4c72e0434d68e36artem * along with this program; if not, write to the Free Software
18c2aff776a775d34a4c9893a4c72e0434d68e36artem * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18c2aff776a775d34a4c9893a4c72e0434d68e36artem *
18c2aff776a775d34a4c9893a4c72e0434d68e36artem **************************************************************************/
18c2aff776a775d34a4c9893a4c72e0434d68e36artem
18c2aff776a775d34a4c9893a4c72e0434d68e36artem#include <stdio.h>
18c2aff776a775d34a4c9893a4c72e0434d68e36artem#include <string.h>
18c2aff776a775d34a4c9893a4c72e0434d68e36artem#include <time.h>
18c2aff776a775d34a4c9893a4c72e0434d68e36artem#include <ctype.h>
18c2aff776a775d34a4c9893a4c72e0434d68e36artem#include <stdint.h>
18c2aff776a775d34a4c9893a4c72e0434d68e36artem
18c2aff776a775d34a4c9893a4c72e0434d68e36artem#include <glib.h>
18c2aff776a775d34a4c9893a4c72e0434d68e36artem
18c2aff776a775d34a4c9893a4c72e0434d68e36artem#include "logger.h"
18c2aff776a775d34a4c9893a4c72e0434d68e36artem
18c2aff776a775d34a4c9893a4c72e0434d68e36artem#include "util_pm.h"
18c2aff776a775d34a4c9893a4c72e0434d68e36artem
18c2aff776a775d34a4c9893a4c72e0434d68e36artemtypedef struct {
18c2aff776a775d34a4c9893a4c72e0434d68e36artem int last_level;
18c2aff776a775d34a4c9893a4c72e0434d68e36artem int last_chargeRate;
18c2aff776a775d34a4c9893a4c72e0434d68e36artem time_t last_time;
18c2aff776a775d34a4c9893a4c72e0434d68e36artem} batteryInfo;
18c2aff776a775d34a4c9893a4c72e0434d68e36artem
18c2aff776a775d34a4c9893a4c72e0434d68e36artemGHashTable *saved_battery_info = NULL;
18c2aff776a775d34a4c9893a4c72e0434d68e36artem
18c2aff776a775d34a4c9893a4c72e0434d68e36artem/** Convert the hardware reported value into a few sane choices
18c2aff776a775d34a4c9893a4c72e0434d68e36artem *
18c2aff776a775d34a4c9893a4c72e0434d68e36artem * This is needed as ACPI does not specify the description text for a
18c2aff776a775d34a4c9893a4c72e0434d68e36artem * battery, and so we have to calculate it from the hardware output
18c2aff776a775d34a4c9893a4c72e0434d68e36artem *
18c2aff776a775d34a4c9893a4c72e0434d68e36artem * @param type The battery type recieved from the hardware
18c2aff776a775d34a4c9893a4c72e0434d68e36artem * @return The battery technology which is one of:
18c2aff776a775d34a4c9893a4c72e0434d68e36artem * unknown, lithium-ion or lead-acid
18c2aff776a775d34a4c9893a4c72e0434d68e36artem */
18c2aff776a775d34a4c9893a4c72e0434d68e36artemconst char *
18c2aff776a775d34a4c9893a4c72e0434d68e36artemutil_get_battery_technology (const char *type)
18c2aff776a775d34a4c9893a4c72e0434d68e36artem{
18c2aff776a775d34a4c9893a4c72e0434d68e36artem if (type == NULL) {
18c2aff776a775d34a4c9893a4c72e0434d68e36artem return "unknown";
18c2aff776a775d34a4c9893a4c72e0434d68e36artem }
18c2aff776a775d34a4c9893a4c72e0434d68e36artem /* every case combination of Li-Ion is commonly used.. */
18c2aff776a775d34a4c9893a4c72e0434d68e36artem if (strcasecmp (type, "li-ion") == 0 ||
18c2aff776a775d34a4c9893a4c72e0434d68e36artem strcasecmp (type, "lion") == 0) {
18c2aff776a775d34a4c9893a4c72e0434d68e36artem return "lithium-ion";
18c2aff776a775d34a4c9893a4c72e0434d68e36artem }
18c2aff776a775d34a4c9893a4c72e0434d68e36artem if (strcasecmp (type, "pb") == 0 ||
18c2aff776a775d34a4c9893a4c72e0434d68e36artem strcasecmp (type, "pbac") == 0) {
18c2aff776a775d34a4c9893a4c72e0434d68e36artem return "lead-acid";
18c2aff776a775d34a4c9893a4c72e0434d68e36artem }
18c2aff776a775d34a4c9893a4c72e0434d68e36artem if (strcasecmp (type, "lip") == 0) {
18c2aff776a775d34a4c9893a4c72e0434d68e36artem return "lithium-polymer";
18c2aff776a775d34a4c9893a4c72e0434d68e36artem }
18c2aff776a775d34a4c9893a4c72e0434d68e36artem if (strcasecmp (type, "nimh") == 0) {
18c2aff776a775d34a4c9893a4c72e0434d68e36artem return "nickel-metal-hydride";
18c2aff776a775d34a4c9893a4c72e0434d68e36artem }
18c2aff776a775d34a4c9893a4c72e0434d68e36artem return "unknown";
18c2aff776a775d34a4c9893a4c72e0434d68e36artem}
18c2aff776a775d34a4c9893a4c72e0434d68e36artem
18c2aff776a775d34a4c9893a4c72e0434d68e36artem/** Given all the required parameters, this function will return the percentage
18c2aff776a775d34a4c9893a4c72e0434d68e36artem * charge remaining. There are lots of checks here as ACPI is often broken.
18c2aff776a775d34a4c9893a4c72e0434d68e36artem *
18c2aff776a775d34a4c9893a4c72e0434d68e36artem * @param id Optional ID given to this battery. Unused at present.
18c2aff776a775d34a4c9893a4c72e0434d68e36artem * @param chargeLevel The current charge level of the battery (typically mWh)
18c2aff776a775d34a4c9893a4c72e0434d68e36artem * @param chargeLastFull The last "full" charge of the battery (typically mWh)
18c2aff776a775d34a4c9893a4c72e0434d68e36artem * @return Percentage, -1 if invalid
18c2aff776a775d34a4c9893a4c72e0434d68e36artem */
18c2aff776a775d34a4c9893a4c72e0434d68e36artemint
18c2aff776a775d34a4c9893a4c72e0434d68e36artemutil_compute_percentage_charge (const char *id,
18c2aff776a775d34a4c9893a4c72e0434d68e36artem int chargeLevel,
18c2aff776a775d34a4c9893a4c72e0434d68e36artem int chargeLastFull)
18c2aff776a775d34a4c9893a4c72e0434d68e36artem{
18c2aff776a775d34a4c9893a4c72e0434d68e36artem int percentage;
18c2aff776a775d34a4c9893a4c72e0434d68e36artem /* make sure we have chargelevel */
18c2aff776a775d34a4c9893a4c72e0434d68e36artem if (chargeLevel <= 0) {
18c2aff776a775d34a4c9893a4c72e0434d68e36artem HAL_WARNING (("chargeLevel %i, returning -1!", chargeLevel));
18c2aff776a775d34a4c9893a4c72e0434d68e36artem return -1;
18c2aff776a775d34a4c9893a4c72e0434d68e36artem }
18c2aff776a775d34a4c9893a4c72e0434d68e36artem /* make sure not division by zero */
18c2aff776a775d34a4c9893a4c72e0434d68e36artem if (chargeLastFull > 0)
18c2aff776a775d34a4c9893a4c72e0434d68e36artem percentage = ((double) chargeLevel / (double) chargeLastFull) * 100;
18c2aff776a775d34a4c9893a4c72e0434d68e36artem else {
18c2aff776a775d34a4c9893a4c72e0434d68e36artem HAL_WARNING (("chargeLastFull %i, percentage returning -1!", chargeLastFull));
18c2aff776a775d34a4c9893a4c72e0434d68e36artem return -1;
18c2aff776a775d34a4c9893a4c72e0434d68e36artem }
18c2aff776a775d34a4c9893a4c72e0434d68e36artem /* Some bios's will report this higher than 100, limit it here */
18c2aff776a775d34a4c9893a4c72e0434d68e36artem if (percentage > 100) {
18c2aff776a775d34a4c9893a4c72e0434d68e36artem HAL_WARNING (("Percentage %i, returning 100!", percentage));
18c2aff776a775d34a4c9893a4c72e0434d68e36artem return 100;
18c2aff776a775d34a4c9893a4c72e0434d68e36artem }
18c2aff776a775d34a4c9893a4c72e0434d68e36artem /* Something really isn't right if we get a negative... */
18c2aff776a775d34a4c9893a4c72e0434d68e36artem if (percentage < 0) {
18c2aff776a775d34a4c9893a4c72e0434d68e36artem HAL_WARNING (("Percentage %i, returning -1!", percentage));
18c2aff776a775d34a4c9893a4c72e0434d68e36artem return -1;
18c2aff776a775d34a4c9893a4c72e0434d68e36artem }
18c2aff776a775d34a4c9893a4c72e0434d68e36artem return percentage;
18c2aff776a775d34a4c9893a4c72e0434d68e36artem}
18c2aff776a775d34a4c9893a4c72e0434d68e36artem
18c2aff776a775d34a4c9893a4c72e0434d68e36artem/** Given all the required parameters, this function will return the number
18c2aff776a775d34a4c9893a4c72e0434d68e36artem * of seconds until the battery is charged (if charging) or the number
18c2aff776a775d34a4c9893a4c72e0434d68e36artem * of seconds until empty (if discharging)
18c2aff776a775d34a4c9893a4c72e0434d68e36artem *
18c2aff776a775d34a4c9893a4c72e0434d68e36artem * @param id Optional ID given to this battery. Unused at present.
18c2aff776a775d34a4c9893a4c72e0434d68e36artem * @param chargeRate The "rate" (typically mW)
18c2aff776a775d34a4c9893a4c72e0434d68e36artem * @param chargeLevel The current charge level of the battery (typically mWh)
18c2aff776a775d34a4c9893a4c72e0434d68e36artem * @param chargeLastFull The last "full" charge of the battery (typically mWh)
18c2aff776a775d34a4c9893a4c72e0434d68e36artem * @param isDischarging If battery is discharging
18c2aff776a775d34a4c9893a4c72e0434d68e36artem * @param isCharging If battery is charging
18c2aff776a775d34a4c9893a4c72e0434d68e36artem * @param guessChargeRate If ignore chargeRate and guess them.
18c2aff776a775d34a4c9893a4c72e0434d68e36artem * @return Number of seconds, or -1 if invalid
18c2aff776a775d34a4c9893a4c72e0434d68e36artem */
18c2aff776a775d34a4c9893a4c72e0434d68e36artemint
18c2aff776a775d34a4c9893a4c72e0434d68e36artemutil_compute_time_remaining (const char *id,
18c2aff776a775d34a4c9893a4c72e0434d68e36artem int chargeRate,
18c2aff776a775d34a4c9893a4c72e0434d68e36artem int chargeLevel,
18c2aff776a775d34a4c9893a4c72e0434d68e36artem int chargeLastFull,
18c2aff776a775d34a4c9893a4c72e0434d68e36artem gboolean isDischarging,
18c2aff776a775d34a4c9893a4c72e0434d68e36artem gboolean isCharging,
18c2aff776a775d34a4c9893a4c72e0434d68e36artem gboolean guessChargeRate)
18c2aff776a775d34a4c9893a4c72e0434d68e36artem{
18c2aff776a775d34a4c9893a4c72e0434d68e36artem int remaining_time = 0;
18c2aff776a775d34a4c9893a4c72e0434d68e36artem
18c2aff776a775d34a4c9893a4c72e0434d68e36artem /* should not get negative values */
18c2aff776a775d34a4c9893a4c72e0434d68e36artem if (chargeRate < 0 || chargeLevel < 0 || chargeLastFull < 0) {
18c2aff776a775d34a4c9893a4c72e0434d68e36artem HAL_WARNING (("chargeRate, chargeLevel or chargeLastFull < 0, returning -1"));
18c2aff776a775d34a4c9893a4c72e0434d68e36artem return -1;
18c2aff776a775d34a4c9893a4c72e0434d68e36artem }
18c2aff776a775d34a4c9893a4c72e0434d68e36artem /* batteries cannot charge and discharge at the same time */
18c2aff776a775d34a4c9893a4c72e0434d68e36artem if (isDischarging && isCharging) {
18c2aff776a775d34a4c9893a4c72e0434d68e36artem HAL_WARNING (("isDischarging & isCharging TRUE, returning -1"));
18c2aff776a775d34a4c9893a4c72e0434d68e36artem return -1;
18c2aff776a775d34a4c9893a4c72e0434d68e36artem }
18c2aff776a775d34a4c9893a4c72e0434d68e36artem /*
18c2aff776a775d34a4c9893a4c72e0434d68e36artem * Some laptops don't supply any rate info, but that's no reason for HAL not
18c2aff776a775d34a4c9893a4c72e0434d68e36artem * to. We use the current and previous chargeLevel to estimate the rate.
18c2aff776a775d34a4c9893a4c72e0434d68e36artem * The info is stored in a GHashTable because there could be more than one battery.
18c2aff776a775d34a4c9893a4c72e0434d68e36artem */
18c2aff776a775d34a4c9893a4c72e0434d68e36artem if (chargeRate == 0 || guessChargeRate) {
18c2aff776a775d34a4c9893a4c72e0434d68e36artem batteryInfo *battery_info;
18c2aff776a775d34a4c9893a4c72e0434d68e36artem time_t cur_time = time(NULL);
18c2aff776a775d34a4c9893a4c72e0434d68e36artem
18c2aff776a775d34a4c9893a4c72e0434d68e36artem /* Initialize the save_battery_info GHashTable */
18c2aff776a775d34a4c9893a4c72e0434d68e36artem if (!saved_battery_info)
18c2aff776a775d34a4c9893a4c72e0434d68e36artem saved_battery_info = g_hash_table_new(g_str_hash, g_str_equal);
18c2aff776a775d34a4c9893a4c72e0434d68e36artem
18c2aff776a775d34a4c9893a4c72e0434d68e36artem if ((battery_info = g_hash_table_lookup(saved_battery_info, id))) {
18c2aff776a775d34a4c9893a4c72e0434d68e36artem /* check this to prevent division by zero */
18c2aff776a775d34a4c9893a4c72e0434d68e36artem if ((cur_time == battery_info->last_time) || (chargeLevel == battery_info->last_level)) {
18c2aff776a775d34a4c9893a4c72e0434d68e36artem /* if we can't calculate because nothing changed, use last
18c2aff776a775d34a4c9893a4c72e0434d68e36artem * chargeRate to prevent removing battery.remaining_time.
18c2aff776a775d34a4c9893a4c72e0434d68e36artem */
18c2aff776a775d34a4c9893a4c72e0434d68e36artem chargeRate = battery_info->last_chargeRate;
18c2aff776a775d34a4c9893a4c72e0434d68e36artem } else {
18c2aff776a775d34a4c9893a4c72e0434d68e36artem chargeRate = ((chargeLevel - battery_info->last_level) * 60 * 60) / (cur_time - battery_info->last_time);
18c2aff776a775d34a4c9893a4c72e0434d68e36artem /*
18c2aff776a775d34a4c9893a4c72e0434d68e36artem * During discharging chargeRate would be negative, which would
18c2aff776a775d34a4c9893a4c72e0434d68e36artem * mess up the the calculation below, so we make sure it's always
18c2aff776a775d34a4c9893a4c72e0434d68e36artem * positive.
18c2aff776a775d34a4c9893a4c72e0434d68e36artem */
18c2aff776a775d34a4c9893a4c72e0434d68e36artem chargeRate = (chargeRate > 0) ? chargeRate : -chargeRate;
18c2aff776a775d34a4c9893a4c72e0434d68e36artem
18c2aff776a775d34a4c9893a4c72e0434d68e36artem battery_info->last_level = chargeLevel;
18c2aff776a775d34a4c9893a4c72e0434d68e36artem battery_info->last_time = cur_time;
18c2aff776a775d34a4c9893a4c72e0434d68e36artem battery_info->last_chargeRate = chargeRate;
18c2aff776a775d34a4c9893a4c72e0434d68e36artem }
18c2aff776a775d34a4c9893a4c72e0434d68e36artem } else {
18c2aff776a775d34a4c9893a4c72e0434d68e36artem battery_info = g_new0(batteryInfo, 1);
18c2aff776a775d34a4c9893a4c72e0434d68e36artem g_hash_table_insert(saved_battery_info, (char*) id, battery_info);
18c2aff776a775d34a4c9893a4c72e0434d68e36artem
18c2aff776a775d34a4c9893a4c72e0434d68e36artem battery_info->last_level = chargeLevel;
18c2aff776a775d34a4c9893a4c72e0434d68e36artem battery_info->last_time = cur_time;
18c2aff776a775d34a4c9893a4c72e0434d68e36artem battery_info->last_chargeRate = 0;
18c2aff776a775d34a4c9893a4c72e0434d68e36artem return -1;
18c2aff776a775d34a4c9893a4c72e0434d68e36artem }
18c2aff776a775d34a4c9893a4c72e0434d68e36artem }
18c2aff776a775d34a4c9893a4c72e0434d68e36artem
18c2aff776a775d34a4c9893a4c72e0434d68e36artem if (chargeRate == 0)
18c2aff776a775d34a4c9893a4c72e0434d68e36artem return -1;
18c2aff776a775d34a4c9893a4c72e0434d68e36artem
18c2aff776a775d34a4c9893a4c72e0434d68e36artem if (isDischarging) {
18c2aff776a775d34a4c9893a4c72e0434d68e36artem remaining_time = ((double) chargeLevel / (double) chargeRate) * 60 * 60;
18c2aff776a775d34a4c9893a4c72e0434d68e36artem } else if (isCharging) {
18c2aff776a775d34a4c9893a4c72e0434d68e36artem /*
18c2aff776a775d34a4c9893a4c72e0434d68e36artem * Some ACPI BIOS's don't update chargeLastFull,
18c2aff776a775d34a4c9893a4c72e0434d68e36artem * so return 0 as we don't know how much more there is left
18c2aff776a775d34a4c9893a4c72e0434d68e36artem */
18c2aff776a775d34a4c9893a4c72e0434d68e36artem if (chargeLevel > chargeLastFull ) {
18c2aff776a775d34a4c9893a4c72e0434d68e36artem HAL_WARNING (("chargeLevel > chargeLastFull, returning -1"));
18c2aff776a775d34a4c9893a4c72e0434d68e36artem return -1;
18c2aff776a775d34a4c9893a4c72e0434d68e36artem }
18c2aff776a775d34a4c9893a4c72e0434d68e36artem remaining_time = ((double) (chargeLastFull - chargeLevel) / (double) chargeRate) * 60 * 60;
18c2aff776a775d34a4c9893a4c72e0434d68e36artem }
18c2aff776a775d34a4c9893a4c72e0434d68e36artem
18c2aff776a775d34a4c9893a4c72e0434d68e36artem /* This shouldn't happen, but check for completeness */
18c2aff776a775d34a4c9893a4c72e0434d68e36artem if (remaining_time < 0) {
18c2aff776a775d34a4c9893a4c72e0434d68e36artem HAL_WARNING (("remaining_time %i, returning -1", remaining_time));
18c2aff776a775d34a4c9893a4c72e0434d68e36artem remaining_time = -1;
18c2aff776a775d34a4c9893a4c72e0434d68e36artem }
18c2aff776a775d34a4c9893a4c72e0434d68e36artem /* Battery life cannot be above 60 hours */
18c2aff776a775d34a4c9893a4c72e0434d68e36artem else if (remaining_time > 60*60*60) {
18c2aff776a775d34a4c9893a4c72e0434d68e36artem HAL_WARNING (("remaining_time *very* high, returning -1"));
18c2aff776a775d34a4c9893a4c72e0434d68e36artem remaining_time = -1;
18c2aff776a775d34a4c9893a4c72e0434d68e36artem }
18c2aff776a775d34a4c9893a4c72e0434d68e36artem
18c2aff776a775d34a4c9893a4c72e0434d68e36artem return remaining_time;
18c2aff776a775d34a4c9893a4c72e0434d68e36artem}
18c2aff776a775d34a4c9893a4c72e0434d68e36artem