1/*
2 * Copyright 2009, Intel Corporation
3 * Copyright 2009, Sun Microsystems, Inc
4 *
5 * This file is part of PowerTOP
6 *
7 * This program file is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; version 2 of the License.
10 *
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program in a file named COPYING; if not, write to the
18 * Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301 USA
21 *
22 * Authors:
23 *	Arjan van de Ven <arjan@linux.intel.com>
24 *	Eric C Saxe <eric.saxe@sun.com>
25 *	Aubrey Li <aubrey.li@intel.com>
26 */
27
28/*
29 * GPL Disclaimer
30 *
31 * For the avoidance of doubt, except that if any license choice other
32 * than GPL or LGPL is available it will apply instead, Sun elects to
33 * use only the General Public License version 2 (GPLv2) at this time
34 * for any software where a choice of GPL license versions is made
35 * available with the language indicating that GPLv2 or any later
36 * version may be used, or where a choice of which version of the GPL
37 * is applied is otherwise unspecified.
38 */
39
40#include <string.h>
41#include <kstat.h>
42#include <errno.h>
43#include "powertop.h"
44
45#define	mW2W(value)	((value) / 1000)
46
47typedef struct battery_state {
48	uint32_t exist;
49	uint32_t power_unit;
50	uint32_t bst_state;
51	double present_rate;
52	double remain_cap;
53	double last_cap;
54} battery_state_t;
55
56static char		*kstat_batt_mod[3] = {NULL, "battery", "acpi_drv"};
57static uint_t		kstat_batt_idx;
58static battery_state_t	battery_state;
59
60static int		pt_battery_stat_snapshot(void);
61
62/*
63 * Checks if the kstat module for battery information is present and
64 * whether it's called 'battery' or 'acpi_drv'
65 */
66void
67pt_battery_mod_lookup(void)
68{
69	kstat_ctl_t *kc = kstat_open();
70
71	if (kstat_lookup(kc, kstat_batt_mod[1], 0, NULL))
72		kstat_batt_idx = 1;
73	else
74		if (kstat_lookup(kc, kstat_batt_mod[2], 0, NULL))
75			kstat_batt_idx = 2;
76		else
77			kstat_batt_idx = 0;
78
79	(void) kstat_close(kc);
80}
81
82void
83pt_battery_print(void)
84{
85	int err;
86
87	(void) memset(&battery_state, 0, sizeof (battery_state_t));
88
89	/*
90	 * The return value of pt_battery_stat_snapshot() can be used for
91	 * debug or to show/hide the acpi power line. We currently don't
92	 * make the distinction of a system that runs only on AC and one
93	 * that runs on battery but has no kstat battery info.
94	 *
95	 * We still display the estimate power usage for systems
96	 * running on AC with a fully charged battery because some
97	 * batteries may still consume power.
98	 *
99	 * If pt_battery_mod_lookup() didn't find a kstat battery module, don't
100	 * bother trying to take the snapshot
101	 */
102	if (kstat_batt_idx > 0) {
103		if ((err = pt_battery_stat_snapshot()) < 0)
104			pt_error("battery kstat not found (%d)\n", err);
105	}
106
107	pt_display_acpi_power(battery_state.exist, battery_state.present_rate,
108	    battery_state.remain_cap, battery_state.last_cap,
109	    battery_state.bst_state);
110}
111
112static int
113pt_battery_stat_snapshot(void)
114{
115	kstat_ctl_t	*kc;
116	kstat_t		*ksp;
117	kstat_named_t	*knp;
118
119	kc = kstat_open();
120
121	/*
122	 * power unit:
123	 * 	0 - Capacity information is reported in [mWh] and
124	 *	    charge/discharge rate information in [mW]
125	 *	1 - Capacity information is reported in [mAh] and
126	 *	    charge/discharge rate information in [mA].
127	 */
128	ksp = kstat_lookup(kc, kstat_batt_mod[kstat_batt_idx], 0,
129	    "battery BIF0");
130
131	if (ksp == NULL) {
132		(void) kstat_close(kc);
133		return (-1);
134	}
135
136	(void) kstat_read(kc, ksp, NULL);
137	knp = kstat_data_lookup(ksp, "bif_unit");
138
139	if (knp == NULL) {
140		(void) kstat_close(kc);
141		return (-1);
142	}
143
144	battery_state.power_unit = knp->value.ui32;
145
146	/*
147	 * Present rate:
148	 *	the power or current being supplied or accepted
149	 *	through the battery's terminal
150	 */
151	ksp = kstat_lookup(kc, kstat_batt_mod[kstat_batt_idx], 0,
152	    "battery BST0");
153
154	if (ksp == NULL) {
155		(void) kstat_close(kc);
156		return (-1);
157	}
158
159	(void) kstat_read(kc, ksp, NULL);
160	knp = kstat_data_lookup(ksp, "bst_rate");
161
162	if (knp == NULL) {
163		(void) kstat_close(kc);
164		return (-1);
165	}
166
167	if (knp->value.ui32 == 0xFFFFFFFF)
168		battery_state.present_rate = 0;
169	else {
170		battery_state.exist = 1;
171		battery_state.present_rate = mW2W((double)(knp->value.ui32));
172	}
173
174	/*
175	 * Last Full charge capacity:
176	 *	Predicted battery capacity when fully charged.
177	 */
178	ksp = kstat_lookup(kc, kstat_batt_mod[kstat_batt_idx], 0,
179	    "battery BIF0");
180
181	if (ksp == NULL) {
182		(void) kstat_close(kc);
183		return (-1);
184	}
185
186	(void) kstat_read(kc, ksp, NULL);
187	knp = kstat_data_lookup(ksp, "bif_last_cap");
188
189	if (knp == NULL) {
190		(void) kstat_close(kc);
191		return (-1);
192	}
193
194	battery_state.last_cap = mW2W((double)(knp->value.ui32));
195
196	/*
197	 * Remaining capacity:
198	 *	the estimated remaining battery capacity
199	 */
200	ksp = kstat_lookup(kc, kstat_batt_mod[kstat_batt_idx], 0,
201	    "battery BST0");
202
203	if (ksp == NULL) {
204		(void) kstat_close(kc);
205		return (-1);
206	}
207
208	(void) kstat_read(kc, ksp, NULL);
209	knp = kstat_data_lookup(ksp, "bst_rem_cap");
210
211	if (knp == NULL) {
212		(void) kstat_close(kc);
213		return (-1);
214	}
215
216	battery_state.remain_cap = mW2W((double)(knp->value.ui32));
217
218	/*
219	 * Battery State:
220	 *	Bit0 - 1 : discharging
221	 *	Bit1 - 1 : charging
222	 *	Bit2 - 1 : critical energy state
223	 */
224	ksp = kstat_lookup(kc, kstat_batt_mod[kstat_batt_idx], 0,
225	    "battery BST0");
226
227	if (ksp == NULL) {
228		(void) kstat_close(kc);
229		return (-1);
230	}
231
232	(void) kstat_read(kc, ksp, NULL);
233	knp = kstat_data_lookup(ksp, "bst_state");
234
235	if (knp == NULL) {
236		(void) kstat_close(kc);
237		return (-1);
238	}
239
240	battery_state.bst_state = knp->value.ui32;
241
242	(void) kstat_close(kc);
243
244	return (0);
245}
246