1*2912Sartem /*************************************************************************** 2*2912Sartem * CVSID: $Id$ 3*2912Sartem * 4*2912Sartem * utili_pm.c - Various Powermanagement related utilities 5*2912Sartem * 6*2912Sartem * Copyright (C) 2005 Richard Hughes <richard@hughsie.com> 7*2912Sartem * Copyright (C) 2005 Danny Kukawka <danny.kukawka@web.de> 8*2912Sartem * 9*2912Sartem * Licensed under the Academic Free License version 2.1 10*2912Sartem * 11*2912Sartem * This program is free software; you can redistribute it and/or modify 12*2912Sartem * it under the terms of the GNU General Public License as published by 13*2912Sartem * the Free Software Foundation; either version 2 of the License, or 14*2912Sartem * (at your option) any later version. 15*2912Sartem * 16*2912Sartem * This program is distributed in the hope that it will be useful, 17*2912Sartem * but WITHOUT ANY WARRANTY; without even the implied warranty of 18*2912Sartem * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19*2912Sartem * GNU General Public License for more details. 20*2912Sartem * 21*2912Sartem * You should have received a copy of the GNU General Public License 22*2912Sartem * along with this program; if not, write to the Free Software 23*2912Sartem * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 24*2912Sartem * 25*2912Sartem **************************************************************************/ 26*2912Sartem 27*2912Sartem #include <stdio.h> 28*2912Sartem #include <string.h> 29*2912Sartem #include <time.h> 30*2912Sartem #include <ctype.h> 31*2912Sartem #include <stdint.h> 32*2912Sartem 33*2912Sartem #include <glib.h> 34*2912Sartem 35*2912Sartem #include "logger.h" 36*2912Sartem 37*2912Sartem #include "util_pm.h" 38*2912Sartem 39*2912Sartem typedef struct { 40*2912Sartem int last_level; 41*2912Sartem int last_chargeRate; 42*2912Sartem time_t last_time; 43*2912Sartem } batteryInfo; 44*2912Sartem 45*2912Sartem GHashTable *saved_battery_info = NULL; 46*2912Sartem 47*2912Sartem /** Convert the hardware reported value into a few sane choices 48*2912Sartem * 49*2912Sartem * This is needed as ACPI does not specify the description text for a 50*2912Sartem * battery, and so we have to calculate it from the hardware output 51*2912Sartem * 52*2912Sartem * @param type The battery type recieved from the hardware 53*2912Sartem * @return The battery technology which is one of: 54*2912Sartem * unknown, lithium-ion or lead-acid 55*2912Sartem */ 56*2912Sartem const char * 57*2912Sartem util_get_battery_technology (const char *type) 58*2912Sartem { 59*2912Sartem if (type == NULL) { 60*2912Sartem return "unknown"; 61*2912Sartem } 62*2912Sartem /* every case combination of Li-Ion is commonly used.. */ 63*2912Sartem if (strcasecmp (type, "li-ion") == 0 || 64*2912Sartem strcasecmp (type, "lion") == 0) { 65*2912Sartem return "lithium-ion"; 66*2912Sartem } 67*2912Sartem if (strcasecmp (type, "pb") == 0 || 68*2912Sartem strcasecmp (type, "pbac") == 0) { 69*2912Sartem return "lead-acid"; 70*2912Sartem } 71*2912Sartem if (strcasecmp (type, "lip") == 0) { 72*2912Sartem return "lithium-polymer"; 73*2912Sartem } 74*2912Sartem if (strcasecmp (type, "nimh") == 0) { 75*2912Sartem return "nickel-metal-hydride"; 76*2912Sartem } 77*2912Sartem return "unknown"; 78*2912Sartem } 79*2912Sartem 80*2912Sartem /** Given all the required parameters, this function will return the percentage 81*2912Sartem * charge remaining. There are lots of checks here as ACPI is often broken. 82*2912Sartem * 83*2912Sartem * @param id Optional ID given to this battery. Unused at present. 84*2912Sartem * @param chargeLevel The current charge level of the battery (typically mWh) 85*2912Sartem * @param chargeLastFull The last "full" charge of the battery (typically mWh) 86*2912Sartem * @return Percentage, -1 if invalid 87*2912Sartem */ 88*2912Sartem int 89*2912Sartem util_compute_percentage_charge (const char *id, 90*2912Sartem int chargeLevel, 91*2912Sartem int chargeLastFull) 92*2912Sartem { 93*2912Sartem int percentage; 94*2912Sartem /* make sure we have chargelevel */ 95*2912Sartem if (chargeLevel <= 0) { 96*2912Sartem HAL_WARNING (("chargeLevel %i, returning -1!", chargeLevel)); 97*2912Sartem return -1; 98*2912Sartem } 99*2912Sartem /* make sure not division by zero */ 100*2912Sartem if (chargeLastFull > 0) 101*2912Sartem percentage = ((double) chargeLevel / (double) chargeLastFull) * 100; 102*2912Sartem else { 103*2912Sartem HAL_WARNING (("chargeLastFull %i, percentage returning -1!", chargeLastFull)); 104*2912Sartem return -1; 105*2912Sartem } 106*2912Sartem /* Some bios's will report this higher than 100, limit it here */ 107*2912Sartem if (percentage > 100) { 108*2912Sartem HAL_WARNING (("Percentage %i, returning 100!", percentage)); 109*2912Sartem return 100; 110*2912Sartem } 111*2912Sartem /* Something really isn't right if we get a negative... */ 112*2912Sartem if (percentage < 0) { 113*2912Sartem HAL_WARNING (("Percentage %i, returning -1!", percentage)); 114*2912Sartem return -1; 115*2912Sartem } 116*2912Sartem return percentage; 117*2912Sartem } 118*2912Sartem 119*2912Sartem /** Given all the required parameters, this function will return the number 120*2912Sartem * of seconds until the battery is charged (if charging) or the number 121*2912Sartem * of seconds until empty (if discharging) 122*2912Sartem * 123*2912Sartem * @param id Optional ID given to this battery. Unused at present. 124*2912Sartem * @param chargeRate The "rate" (typically mW) 125*2912Sartem * @param chargeLevel The current charge level of the battery (typically mWh) 126*2912Sartem * @param chargeLastFull The last "full" charge of the battery (typically mWh) 127*2912Sartem * @param isDischarging If battery is discharging 128*2912Sartem * @param isCharging If battery is charging 129*2912Sartem * @param guessChargeRate If ignore chargeRate and guess them. 130*2912Sartem * @return Number of seconds, or -1 if invalid 131*2912Sartem */ 132*2912Sartem int 133*2912Sartem util_compute_time_remaining (const char *id, 134*2912Sartem int chargeRate, 135*2912Sartem int chargeLevel, 136*2912Sartem int chargeLastFull, 137*2912Sartem gboolean isDischarging, 138*2912Sartem gboolean isCharging, 139*2912Sartem gboolean guessChargeRate) 140*2912Sartem { 141*2912Sartem int remaining_time = 0; 142*2912Sartem 143*2912Sartem /* should not get negative values */ 144*2912Sartem if (chargeRate < 0 || chargeLevel < 0 || chargeLastFull < 0) { 145*2912Sartem HAL_WARNING (("chargeRate, chargeLevel or chargeLastFull < 0, returning -1")); 146*2912Sartem return -1; 147*2912Sartem } 148*2912Sartem /* batteries cannot charge and discharge at the same time */ 149*2912Sartem if (isDischarging && isCharging) { 150*2912Sartem HAL_WARNING (("isDischarging & isCharging TRUE, returning -1")); 151*2912Sartem return -1; 152*2912Sartem } 153*2912Sartem /* 154*2912Sartem * Some laptops don't supply any rate info, but that's no reason for HAL not 155*2912Sartem * to. We use the current and previous chargeLevel to estimate the rate. 156*2912Sartem * The info is stored in a GHashTable because there could be more than one battery. 157*2912Sartem */ 158*2912Sartem if (chargeRate == 0 || guessChargeRate) { 159*2912Sartem batteryInfo *battery_info; 160*2912Sartem time_t cur_time = time(NULL); 161*2912Sartem 162*2912Sartem /* Initialize the save_battery_info GHashTable */ 163*2912Sartem if (!saved_battery_info) 164*2912Sartem saved_battery_info = g_hash_table_new(g_str_hash, g_str_equal); 165*2912Sartem 166*2912Sartem if ((battery_info = g_hash_table_lookup(saved_battery_info, id))) { 167*2912Sartem /* check this to prevent division by zero */ 168*2912Sartem if ((cur_time == battery_info->last_time) || (chargeLevel == battery_info->last_level)) { 169*2912Sartem /* if we can't calculate because nothing changed, use last 170*2912Sartem * chargeRate to prevent removing battery.remaining_time. 171*2912Sartem */ 172*2912Sartem chargeRate = battery_info->last_chargeRate; 173*2912Sartem } else { 174*2912Sartem chargeRate = ((chargeLevel - battery_info->last_level) * 60 * 60) / (cur_time - battery_info->last_time); 175*2912Sartem /* 176*2912Sartem * During discharging chargeRate would be negative, which would 177*2912Sartem * mess up the the calculation below, so we make sure it's always 178*2912Sartem * positive. 179*2912Sartem */ 180*2912Sartem chargeRate = (chargeRate > 0) ? chargeRate : -chargeRate; 181*2912Sartem 182*2912Sartem battery_info->last_level = chargeLevel; 183*2912Sartem battery_info->last_time = cur_time; 184*2912Sartem battery_info->last_chargeRate = chargeRate; 185*2912Sartem } 186*2912Sartem } else { 187*2912Sartem battery_info = g_new0(batteryInfo, 1); 188*2912Sartem g_hash_table_insert(saved_battery_info, (char*) id, battery_info); 189*2912Sartem 190*2912Sartem battery_info->last_level = chargeLevel; 191*2912Sartem battery_info->last_time = cur_time; 192*2912Sartem battery_info->last_chargeRate = 0; 193*2912Sartem return -1; 194*2912Sartem } 195*2912Sartem } 196*2912Sartem 197*2912Sartem if (chargeRate == 0) 198*2912Sartem return -1; 199*2912Sartem 200*2912Sartem if (isDischarging) { 201*2912Sartem remaining_time = ((double) chargeLevel / (double) chargeRate) * 60 * 60; 202*2912Sartem } else if (isCharging) { 203*2912Sartem /* 204*2912Sartem * Some ACPI BIOS's don't update chargeLastFull, 205*2912Sartem * so return 0 as we don't know how much more there is left 206*2912Sartem */ 207*2912Sartem if (chargeLevel > chargeLastFull ) { 208*2912Sartem HAL_WARNING (("chargeLevel > chargeLastFull, returning -1")); 209*2912Sartem return -1; 210*2912Sartem } 211*2912Sartem remaining_time = ((double) (chargeLastFull - chargeLevel) / (double) chargeRate) * 60 * 60; 212*2912Sartem } 213*2912Sartem 214*2912Sartem /* This shouldn't happen, but check for completeness */ 215*2912Sartem if (remaining_time < 0) { 216*2912Sartem HAL_WARNING (("remaining_time %i, returning -1", remaining_time)); 217*2912Sartem remaining_time = -1; 218*2912Sartem } 219*2912Sartem /* Battery life cannot be above 60 hours */ 220*2912Sartem else if (remaining_time > 60*60*60) { 221*2912Sartem HAL_WARNING (("remaining_time *very* high, returning -1")); 222*2912Sartem remaining_time = -1; 223*2912Sartem } 224*2912Sartem 225*2912Sartem return remaining_time; 226*2912Sartem } 227*2912Sartem 228