xref: /onnv-gate/usr/src/cmd/hal/hald/util_pm.c (revision 2912:85ea316d9c18)
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 *
util_get_battery_technology(const char * type)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
util_compute_percentage_charge(const char * id,int chargeLevel,int chargeLastFull)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
util_compute_time_remaining(const char * id,int chargeRate,int chargeLevel,int chargeLastFull,gboolean isDischarging,gboolean isCharging,gboolean guessChargeRate)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