xref: /illumos-gate/usr/src/cmd/hal/hald/util_pm.c (revision 55fea89d)
118c2aff7Sartem /***************************************************************************
218c2aff7Sartem  * CVSID: $Id$
318c2aff7Sartem  *
418c2aff7Sartem  * utili_pm.c - Various Powermanagement related utilities
518c2aff7Sartem  *
618c2aff7Sartem  * Copyright (C) 2005 Richard Hughes <richard@hughsie.com>
718c2aff7Sartem  * Copyright (C) 2005 Danny Kukawka <danny.kukawka@web.de>
818c2aff7Sartem  *
918c2aff7Sartem  * Licensed under the Academic Free License version 2.1
1018c2aff7Sartem  *
1118c2aff7Sartem  * This program is free software; you can redistribute it and/or modify
1218c2aff7Sartem  * it under the terms of the GNU General Public License as published by
1318c2aff7Sartem  * the Free Software Foundation; either version 2 of the License, or
1418c2aff7Sartem  * (at your option) any later version.
1518c2aff7Sartem  *
1618c2aff7Sartem  * This program is distributed in the hope that it will be useful,
1718c2aff7Sartem  * but WITHOUT ANY WARRANTY; without even the implied warranty of
1818c2aff7Sartem  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1918c2aff7Sartem  * GNU General Public License for more details.
2018c2aff7Sartem  *
2118c2aff7Sartem  * You should have received a copy of the GNU General Public License
2218c2aff7Sartem  * along with this program; if not, write to the Free Software
2318c2aff7Sartem  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
2418c2aff7Sartem  *
2518c2aff7Sartem  **************************************************************************/
2618c2aff7Sartem 
2718c2aff7Sartem #include <stdio.h>
2818c2aff7Sartem #include <string.h>
2918c2aff7Sartem #include <time.h>
3018c2aff7Sartem #include <ctype.h>
3118c2aff7Sartem #include <stdint.h>
3218c2aff7Sartem 
3318c2aff7Sartem #include <glib.h>
3418c2aff7Sartem 
3518c2aff7Sartem #include "logger.h"
3618c2aff7Sartem 
3718c2aff7Sartem #include "util_pm.h"
3818c2aff7Sartem 
3918c2aff7Sartem typedef struct {
4018c2aff7Sartem 	int last_level;
4118c2aff7Sartem 	int last_chargeRate;
4218c2aff7Sartem 	time_t last_time;
4318c2aff7Sartem } batteryInfo;
4418c2aff7Sartem 
4518c2aff7Sartem GHashTable *saved_battery_info = NULL;
4618c2aff7Sartem 
4718c2aff7Sartem /** Convert the hardware reported value into a few sane choices
4818c2aff7Sartem  *
4918c2aff7Sartem  *  This is needed as ACPI does not specify the description text for a
5018c2aff7Sartem  *  battery, and so we have to calculate it from the hardware output
5118c2aff7Sartem  *
5218c2aff7Sartem  *  @param  type                The battery type recieved from the hardware
5318c2aff7Sartem  *  @return                     The battery technology which is one of:
5418c2aff7Sartem  *                              unknown, lithium-ion or lead-acid
5518c2aff7Sartem  */
5618c2aff7Sartem const char *
util_get_battery_technology(const char * type)5718c2aff7Sartem util_get_battery_technology (const char *type)
5818c2aff7Sartem {
5918c2aff7Sartem 	if (type == NULL) {
6018c2aff7Sartem 		return "unknown";
6118c2aff7Sartem 	}
6218c2aff7Sartem 	/* every case combination of Li-Ion is commonly used.. */
6318c2aff7Sartem 	if (strcasecmp (type, "li-ion") == 0 ||
6418c2aff7Sartem 	    strcasecmp (type, "lion") == 0) {
6518c2aff7Sartem 		return "lithium-ion";
6618c2aff7Sartem 	}
6718c2aff7Sartem 	if (strcasecmp (type, "pb") == 0 ||
6818c2aff7Sartem 	    strcasecmp (type, "pbac") == 0) {
6918c2aff7Sartem 		return "lead-acid";
7018c2aff7Sartem 	}
7118c2aff7Sartem 	if (strcasecmp (type, "lip") == 0) {
7218c2aff7Sartem 		return "lithium-polymer";
7318c2aff7Sartem 	}
7418c2aff7Sartem 	if (strcasecmp (type, "nimh") == 0) {
7518c2aff7Sartem 		return "nickel-metal-hydride";
7618c2aff7Sartem 	}
7718c2aff7Sartem 	return "unknown";
7818c2aff7Sartem }
7918c2aff7Sartem 
8018c2aff7Sartem /** Given all the required parameters, this function will return the percentage
8118c2aff7Sartem  *  charge remaining. There are lots of checks here as ACPI is often broken.
8218c2aff7Sartem  *
8318c2aff7Sartem  *  @param  id                  Optional ID given to this battery. Unused at present.
8418c2aff7Sartem  *  @param  chargeLevel         The current charge level of the battery (typically mWh)
8518c2aff7Sartem  *  @param  chargeLastFull      The last "full" charge of the battery (typically mWh)
8618c2aff7Sartem  *  @return                     Percentage, -1 if invalid
8718c2aff7Sartem  */
88*55fea89dSDan Cross int
util_compute_percentage_charge(const char * id,int chargeLevel,int chargeLastFull)8918c2aff7Sartem util_compute_percentage_charge (const char *id,
9018c2aff7Sartem 			     int chargeLevel,
9118c2aff7Sartem 			     int chargeLastFull)
9218c2aff7Sartem {
9318c2aff7Sartem 	int percentage;
9418c2aff7Sartem 	/* make sure we have chargelevel */
9518c2aff7Sartem 	if (chargeLevel <= 0) {
9618c2aff7Sartem 		HAL_WARNING (("chargeLevel %i, returning -1!", chargeLevel));
9718c2aff7Sartem 		return -1;
9818c2aff7Sartem 	}
9918c2aff7Sartem 	/* make sure not division by zero */
10018c2aff7Sartem 	if (chargeLastFull > 0)
10118c2aff7Sartem 		percentage = ((double) chargeLevel / (double) chargeLastFull) * 100;
10218c2aff7Sartem 	else {
10318c2aff7Sartem 		HAL_WARNING (("chargeLastFull %i, percentage returning -1!", chargeLastFull));
10418c2aff7Sartem 		return -1;
10518c2aff7Sartem 	}
10618c2aff7Sartem 	/* Some bios's will report this higher than 100, limit it here */
10718c2aff7Sartem 	if (percentage > 100) {
10818c2aff7Sartem 		HAL_WARNING (("Percentage %i, returning 100!", percentage));
10918c2aff7Sartem 		return 100;
11018c2aff7Sartem 	}
11118c2aff7Sartem 	/* Something really isn't right if we get a negative... */
11218c2aff7Sartem 	if (percentage < 0) {
11318c2aff7Sartem 		HAL_WARNING (("Percentage %i, returning -1!", percentage));
11418c2aff7Sartem 		return -1;
11518c2aff7Sartem 	}
11618c2aff7Sartem 	return percentage;
11718c2aff7Sartem }
11818c2aff7Sartem 
119*55fea89dSDan Cross /** Given all the required parameters, this function will return the number
12018c2aff7Sartem  *  of seconds until the battery is charged (if charging) or the number
12118c2aff7Sartem  *  of seconds until empty (if discharging)
12218c2aff7Sartem  *
12318c2aff7Sartem  *  @param  id                  Optional ID given to this battery. Unused at present.
12418c2aff7Sartem  *  @param  chargeRate          The "rate" (typically mW)
12518c2aff7Sartem  *  @param  chargeLevel         The current charge level of the battery (typically mWh)
12618c2aff7Sartem  *  @param  chargeLastFull      The last "full" charge of the battery (typically mWh)
12718c2aff7Sartem  *  @param  isDischarging       If battery is discharging
12818c2aff7Sartem  *  @param  isCharging          If battery is charging
12918c2aff7Sartem  *  @param  guessChargeRate     If ignore chargeRate and guess them.
13018c2aff7Sartem  *  @return                     Number of seconds, or -1 if invalid
13118c2aff7Sartem  */
132*55fea89dSDan Cross int
util_compute_time_remaining(const char * id,int chargeRate,int chargeLevel,int chargeLastFull,gboolean isDischarging,gboolean isCharging,gboolean guessChargeRate)13318c2aff7Sartem util_compute_time_remaining (const char *id,
13418c2aff7Sartem 			     int chargeRate,
13518c2aff7Sartem 			     int chargeLevel,
13618c2aff7Sartem 			     int chargeLastFull,
13718c2aff7Sartem 			     gboolean isDischarging,
13818c2aff7Sartem 			     gboolean isCharging,
13918c2aff7Sartem 			     gboolean guessChargeRate)
14018c2aff7Sartem {
14118c2aff7Sartem 	int remaining_time = 0;
14218c2aff7Sartem 
14318c2aff7Sartem 	/* should not get negative values */
14418c2aff7Sartem 	if (chargeRate < 0 || chargeLevel < 0 || chargeLastFull < 0) {
14518c2aff7Sartem 		HAL_WARNING (("chargeRate, chargeLevel or chargeLastFull < 0, returning -1"));
14618c2aff7Sartem 		return -1;
14718c2aff7Sartem 	}
14818c2aff7Sartem 	/* batteries cannot charge and discharge at the same time */
14918c2aff7Sartem 	if (isDischarging && isCharging) {
15018c2aff7Sartem 		HAL_WARNING (("isDischarging & isCharging TRUE, returning -1"));
15118c2aff7Sartem 		return -1;
15218c2aff7Sartem 	}
153*55fea89dSDan Cross 	/*
15418c2aff7Sartem 	 * Some laptops don't supply any rate info, but that's no reason for HAL not
15518c2aff7Sartem 	 * to. We use the current and previous chargeLevel to estimate the rate.
15618c2aff7Sartem 	 * The info is stored in a GHashTable because there could be more than one battery.
15718c2aff7Sartem 	 */
15818c2aff7Sartem 	if (chargeRate == 0 || guessChargeRate) {
15918c2aff7Sartem 		batteryInfo *battery_info;
16018c2aff7Sartem 		time_t cur_time = time(NULL);
16118c2aff7Sartem 
16218c2aff7Sartem 		/* Initialize the save_battery_info GHashTable */
163*55fea89dSDan Cross 		if (!saved_battery_info)
16418c2aff7Sartem 			saved_battery_info = g_hash_table_new(g_str_hash, g_str_equal);
16518c2aff7Sartem 
16618c2aff7Sartem 		if ((battery_info = g_hash_table_lookup(saved_battery_info, id))) {
16718c2aff7Sartem 			/* check this to prevent division by zero */
16818c2aff7Sartem 			if ((cur_time == battery_info->last_time) || (chargeLevel == battery_info->last_level)) {
169*55fea89dSDan Cross 				/* if we can't calculate because nothing changed, use last
17018c2aff7Sartem 				 * chargeRate to prevent removing battery.remaining_time.
17118c2aff7Sartem 				 */
17218c2aff7Sartem 				chargeRate = battery_info->last_chargeRate;
17318c2aff7Sartem 			} else {
17418c2aff7Sartem 				chargeRate = ((chargeLevel - battery_info->last_level) * 60 * 60) / (cur_time - battery_info->last_time);
17518c2aff7Sartem 				/*
17618c2aff7Sartem 				 * During discharging chargeRate would be negative, which would
17718c2aff7Sartem 				 * mess up the the calculation below, so we make sure it's always
17818c2aff7Sartem 				 * positive.
179*55fea89dSDan Cross 				 */
18018c2aff7Sartem 				chargeRate = (chargeRate > 0) ? chargeRate : -chargeRate;
181*55fea89dSDan Cross 
18218c2aff7Sartem 				battery_info->last_level = chargeLevel;
18318c2aff7Sartem 				battery_info->last_time = cur_time;
18418c2aff7Sartem 				battery_info->last_chargeRate = chargeRate;
18518c2aff7Sartem 			}
18618c2aff7Sartem 		} else {
18718c2aff7Sartem 			battery_info = g_new0(batteryInfo, 1);
18818c2aff7Sartem 			g_hash_table_insert(saved_battery_info, (char*) id, battery_info);
18918c2aff7Sartem 
19018c2aff7Sartem 			battery_info->last_level = chargeLevel;
19118c2aff7Sartem 			battery_info->last_time = cur_time;
19218c2aff7Sartem 			battery_info->last_chargeRate = 0;
19318c2aff7Sartem  			return -1;
19418c2aff7Sartem 		}
195*55fea89dSDan Cross 	}
19618c2aff7Sartem 
19718c2aff7Sartem 	if (chargeRate == 0)
19818c2aff7Sartem 		return -1;
19918c2aff7Sartem 
200*55fea89dSDan Cross 	if (isDischarging) {
20118c2aff7Sartem 		remaining_time = ((double) chargeLevel / (double) chargeRate) * 60 * 60;
20218c2aff7Sartem 	} else if (isCharging) {
203*55fea89dSDan Cross 		/*
204*55fea89dSDan Cross 		 * Some ACPI BIOS's don't update chargeLastFull,
20518c2aff7Sartem 		 * so return 0 as we don't know how much more there is left
20618c2aff7Sartem 		 */
20718c2aff7Sartem 		if (chargeLevel > chargeLastFull ) {
20818c2aff7Sartem 			HAL_WARNING (("chargeLevel > chargeLastFull, returning -1"));
20918c2aff7Sartem 			return -1;
21018c2aff7Sartem 		}
21118c2aff7Sartem 		remaining_time = ((double) (chargeLastFull - chargeLevel) / (double) chargeRate) * 60 * 60;
21218c2aff7Sartem 	}
213*55fea89dSDan Cross 
21418c2aff7Sartem 	/* This shouldn't happen, but check for completeness */
21518c2aff7Sartem 	if (remaining_time < 0) {
21618c2aff7Sartem 		HAL_WARNING (("remaining_time %i, returning -1", remaining_time));
21718c2aff7Sartem 		remaining_time = -1;
21818c2aff7Sartem 	}
21918c2aff7Sartem 	/* Battery life cannot be above 60 hours */
22018c2aff7Sartem 	else if (remaining_time > 60*60*60) {
22118c2aff7Sartem 		HAL_WARNING (("remaining_time *very* high, returning -1"));
22218c2aff7Sartem 		remaining_time = -1;
22318c2aff7Sartem 	}
22418c2aff7Sartem 
22518c2aff7Sartem 	return remaining_time;
22618c2aff7Sartem }
22718c2aff7Sartem 
228