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