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 *
util_get_battery_technology(const char * type)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
util_compute_percentage_charge(const char * id,int chargeLevel,int chargeLastFull)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
util_compute_time_remaining(const char * id,int chargeRate,int chargeLevel,int chargeLastFull,gboolean isDischarging,gboolean isCharging,gboolean guessChargeRate)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