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