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