Adding upstream version 0.9.0.
[debian/lxpanel.git] / plugins / batt / batt_sys.c
1 /*
2 * batt_sys.h
3 *
4 * Copyright 2009 Juergen Hötzel <juergen@archlinux.org>
5 * 2015 Henry Gebhardt <hsggebhardt@googlemail.com>
6 * 2015 Stanislav Kozina, Ersin <xersin@users.sf.net>
7 *
8 * Parts shameless stolen and glibified from acpi package
9 * Copyright (C) 2001 Grahame Bowland <grahame@angrygoats.net>
10 * (C) 2008-2009 Michael Meskes <meskes@debian.org>
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 * MA 02110-1301, USA.
26 */
27
28
29 #ifdef HAVE_CONFIG_H
30 # include <config.h>
31 #endif
32
33 #include "batt_sys.h"
34 #include <glib/gstdio.h>
35
36 /* shrug: get rid of this */
37 #include <stdlib.h>
38 #include <string.h>
39
40 battery* battery_new() {
41 static int battery_num = 1;
42 battery * b = g_new0 ( battery, 1 );
43 b->type_battery = TRUE;
44 //b->capacity_unit = "mAh";
45 b->energy_full = -1;
46 b->charge_full = -1;
47 b->voltage_now = -1;
48 b->energy_full_design = -1;
49 b->charge_full_design = -1;
50 b->energy_now = -1;
51 b->charge_now = -1;
52 b->current_now = -1;
53 b->power_now = -1;
54 b->state = NULL;
55 b->battery_num = battery_num;
56 b->seconds = -1;
57 b->percentage = -1;
58 //b->poststr = NULL;
59 battery_num++;
60 return b;
61 }
62
63
64 static gchar* parse_info_file(battery *b, char *sys_file)
65 {
66 char *buf = NULL;
67 GString *filename = g_string_new(ACPI_PATH_SYS_POWER_SUPPLY);
68
69 g_string_append_printf (filename, "/%s/%s", b->path, sys_file);
70
71 if (g_file_get_contents(filename->str, &buf, NULL, NULL) == TRUE) {
72 g_strstrip( buf );
73 }
74
75 g_string_free(filename, TRUE);
76
77 return buf;
78 }
79
80 /* get_gint_from_infofile():
81 * If the sys_file exists, then its value is converted to an int,
82 * divided by 1000, and returned.
83 * Failure is indicated by returning -1. */
84 static gint get_gint_from_infofile(battery *b, gchar *sys_file)
85 {
86 gchar *file_content = parse_info_file(b, sys_file);
87 gint value = -1;
88
89 if (file_content != NULL)
90 value = atoi(file_content) / 1000;
91 g_free(file_content);
92
93 return value;
94 }
95
96 static gchar* get_gchar_from_infofile(battery *b, gchar *sys_file)
97 {
98 return parse_info_file(b, sys_file);
99 }
100
101 #if 0 /* never used */
102 void battery_print(battery *b, int show_capacity)
103 {
104 if ( b->type_battery )
105 {
106 if (b->state) {
107
108 printf("%s %d: %s, %d%%", BATTERY_DESC, b->battery_num - 1, b->state, b->percentage);
109
110 if (b->seconds > 0) {
111 int hours = b->seconds / 3600;
112 int seconds = b->seconds - 3600 * hours;
113 int minutes = seconds / 60;
114 seconds -= 60 * minutes;
115 printf(", %02d:%02d:%02d%s", hours, minutes, seconds,
116 b->poststr);
117 } else if (b->poststr != NULL) {
118 printf(", %s", b->poststr);
119 }
120
121 printf("\n");
122
123 if (show_capacity && b->charge_full_design > 0) {
124 int percentage = -1;
125 int charge_full = -1;
126 if (b->charge_full <= 100) {
127 /* some broken systems just give a percentage here */
128 percentage = b->charge_full;
129 charge_full = percentage * b->charge_full_design / 100;
130 } else {
131 percentage = b->charge_full * 100 / b->charge_full_design;
132 charge_full = b->charge_full;
133 }
134 if (percentage > 100)
135 percentage = 100;
136
137 printf ("%s %d: design capacity %d %s, "
138 "last full capacity %d %s = %d%%\n",
139 BATTERY_DESC, b->battery_num - 1, b->charge_full_design,
140 b->capacity_unit, charge_full, b->capacity_unit,
141 percentage);
142 }
143 }
144 }
145 }
146 #endif
147
148 static gboolean battery_inserted(gchar* path)
149 {
150 if (path == NULL)
151 return FALSE;
152
153 GString *dirname = g_string_new(ACPI_PATH_SYS_POWER_SUPPLY);
154 GDir *dir;
155
156 g_string_append_printf (dirname, "/%s/", path);
157 dir = g_dir_open(dirname->str, 0, NULL);
158 if (dir)
159 g_dir_close(dir);
160 g_string_free(dirname, TRUE);
161
162 return dir ? TRUE : FALSE;
163 }
164
165
166 battery* battery_update(battery *b)
167 {
168 gchar *gctmp;
169
170 if (b == NULL)
171 return NULL;
172
173 if (!battery_inserted(b->path))
174 return NULL;
175
176 /* read from sysfs */
177 b->charge_now = get_gint_from_infofile(b, "charge_now");
178 b->energy_now = get_gint_from_infofile(b, "energy_now");
179
180 b->current_now = get_gint_from_infofile(b, "current_now");
181 b->power_now = get_gint_from_infofile(b, "power_now");
182 /* FIXME: Some battery drivers report -1000 when the discharge rate is
183 * unavailable. Others use negative values when discharging. Best we can do
184 * is to treat -1 as an error, and take the absolute value otherwise.
185 * Ideally the kernel would not export the sysfs file when the value is not
186 * available. */
187 if (b->current_now < -1)
188 b->current_now = - b->current_now;
189
190 b->charge_full = get_gint_from_infofile(b, "charge_full");
191 b->energy_full = get_gint_from_infofile(b, "energy_full");
192
193 b->charge_full_design = get_gint_from_infofile(b, "charge_full_design");
194 b->energy_full_design = get_gint_from_infofile(b, "energy_full_design");
195
196 b->voltage_now = get_gint_from_infofile(b, "voltage_now");
197
198 gctmp = get_gchar_from_infofile(b, "type");
199 b->type_battery = gctmp ? (strcasecmp(gctmp, "battery") == 0) : TRUE;
200 g_free(gctmp);
201
202 g_free(b->state);
203 b->state = get_gchar_from_infofile(b, "status");
204 if (!b->state)
205 b->state = get_gchar_from_infofile(b, "state");
206 if (!b->state) {
207 if (b->charge_now != -1 || b->energy_now != -1
208 || b->charge_full != -1 || b->energy_full != -1)
209 b->state = g_strdup("available");
210 else
211 b->state = g_strdup("unavailable");
212 }
213
214 #if 0 /* those conversions might be good for text prints but are pretty wrong for tooltip and calculations */
215 /* convert energy values (in mWh) to charge values (in mAh) if needed and possible */
216
217 if (b->energy_full != -1 && b->charge_full == -1) {
218 if (b->voltage_now != -1 && b->voltage_now != 0) {
219 b->charge_full = b->energy_full * 1000 / b->voltage_now;
220 } else {
221 b->charge_full = b->energy_full;
222 b->capacity_unit = "mWh";
223 }
224 }
225
226 if (b->energy_full_design != -1 && b->charge_full_design == -1) {
227 if (b->voltage_now != -1 && b->voltage_now != 0) {
228 b->charge_full_design = b->energy_full_design * 1000 / b->voltage_now;
229 } else {
230 b->charge_full_design = b->energy_full_design;
231 b->capacity_unit = "mWh";
232 }
233 }
234
235 if (b->energy_now != -1 && b->charge_now == -1) {
236 if (b->voltage_now != -1 && b->voltage_now != 0) {
237 b->charge_now = b->energy_now * 1000 / b->voltage_now;
238 if (b->current_now != -1)
239 b->current_now = b->current_now * 1000 / b->voltage_now;
240 } else {
241 b->charge_now = b->energy_now;
242 }
243 }
244
245 if (b->power_now < -1)
246 b->power_now = - b->power_now;
247 else if (b->power_now == -1 && b->voltage_now != -1 && b->current_now != -1)
248 b->power_now = b->voltage_now * b->current_now / 1000; // P = U*I
249 if (b->power_now != -1 && b->current_now == -1) {
250 if (b->voltage_now != -1 && b->voltage_now != 0)
251 b->current_now = b->power_now * 1000 / b->voltage_now;
252 }
253 #endif
254
255 if (b->charge_full < MIN_CAPACITY)
256 b->percentage = 0;
257 else {
258 int promille = (b->charge_now * 1000) / b->charge_full;
259 b->percentage = (promille + 5) / 10; /* round properly */
260 }
261 if (b->percentage > 100)
262 b->percentage = 100;
263
264
265 if (b->current_now == -1) {
266 //b->poststr = "rate information unavailable";
267 b->seconds = -1;
268 } else if (!strcasecmp(b->state, "charging")) {
269 if (b->current_now > MIN_PRESENT_RATE) {
270 b->seconds = 3600 * (b->charge_full - b->charge_now) / b->current_now;
271 //b->poststr = " until charged";
272 } else {
273 //b->poststr = "charging at zero rate - will never fully charge.";
274 b->seconds = -1;
275 }
276 } else if (!strcasecmp(b->state, "discharging")) {
277 if (b->current_now > MIN_PRESENT_RATE) {
278 b->seconds = 3600 * b->charge_now / b->current_now;
279 //b->poststr = " remaining";
280 } else {
281 //b->poststr = "discharging at zero rate - will never fully discharge.";
282 b->seconds = -1;
283 }
284 } else {
285 //b->poststr = NULL;
286 b->seconds = -1;
287 }
288
289 return b;
290 }
291
292
293 battery *battery_get(int battery_number) {
294 GError * error = NULL;
295 const gchar *entry;
296 gchar *batt_name = NULL;
297 gchar *batt_path = NULL;
298 GDir * dir = g_dir_open( ACPI_PATH_SYS_POWER_SUPPLY, 0, &error );
299 battery *b = NULL;
300 int i;
301
302 if ( dir == NULL )
303 {
304 g_warning( "NO ACPI/sysfs support in kernel: %s", error->message );
305 return NULL;
306 }
307
308 /* Try the expected path in sysfs first */
309 batt_name = g_strdup_printf(ACPI_BATTERY_DEVICE_NAME "%d", battery_number);
310 batt_path = g_strdup_printf(ACPI_PATH_SYS_POWER_SUPPLY "/%s", batt_name);
311 if (g_file_test(batt_path, G_FILE_TEST_IS_DIR) == TRUE) {
312 b = battery_new();
313 b->path = g_strdup( batt_name);
314 battery_update ( b );
315
316 if (!b->type_battery) {
317 g_warning( "Not a battery: %s", batt_path );
318 battery_free(b);
319 b = NULL;
320 }
321 }
322
323 g_free(batt_name);
324 g_free(batt_path);
325
326 if (b != NULL)
327 goto done;
328
329 /*
330 * We didn't find the expected path in sysfs.
331 * Walk the dir and blindly return n-th entry.
332 */
333 i = 0;
334 while ( ( entry = g_dir_read_name (dir) ) != NULL )
335 {
336 b = battery_new();
337 b->path = g_strdup( entry );
338 battery_update ( b );
339
340 /* We're looking for a battery with the selected ID */
341 if (b->type_battery == TRUE) {
342 if (i == battery_number)
343 break;
344 i++;
345 }
346 battery_free(b);
347 b = NULL;
348 }
349 if (b != NULL)
350 g_warning( "Battery entry " ACPI_BATTERY_DEVICE_NAME "%d not found, using %s",
351 battery_number, b->path);
352 else
353 g_warning( "Battery %d not found", battery_number );
354 done:
355 g_dir_close( dir );
356 return b;
357 }
358
359 void battery_free(battery* bat)
360 {
361 if (bat) {
362 g_free(bat->path);
363 g_free(bat->state);
364 g_free(bat);
365 }
366 }
367
368 gboolean battery_is_charging( battery *b )
369 {
370 if (!b->state)
371 return TRUE; // Same as "Unkown"
372 return ( strcasecmp( b->state, "Unknown" ) == 0
373 || strcasecmp( b->state, "Full" ) == 0
374 || strcasecmp( b->state, "Charging" ) == 0
375 || b->current_now == 0 ); /* bug sf.net, #720 */
376 }
377
378 gint battery_get_remaining( battery *b )
379 {
380 return b->seconds;
381 }
382
383
384 /* vim: set sw=4 et sts=4 : */