wmacpi: Bump to version 1.99r5.
[dockapps.git] / wmacpi / libacpi.c
blobd6148f789b3a3f1c7754e8a260a4dfb92571cb35
1 #define _GNU_SOURCE
3 #include <stdio.h>
4 #include <string.h>
5 #include <ctype.h>
6 #include <stdlib.h>
7 #include <sys/types.h>
8 #include <dirent.h>
10 #include "libacpi.h"
12 extern char *state[];
13 extern global_t *globals;
15 /* local proto */
16 int acpi_get_design_cap(int batt);
18 /* initialise the batteries */
19 int init_batteries(void)
21 DIR *battdir;
22 struct dirent *batt;
23 char *name;
24 char *names[MAXBATT];
25 int i, j;
27 /* now enumerate batteries */
28 globals->battery_count = 0;
29 battdir = opendir("/proc/acpi/battery");
30 if (battdir == NULL) {
31 pfatal("No batteries or ACPI not supported\n");
32 return 1;
34 while ((batt = readdir(battdir))) {
35 /* there's a serious problem with this code when there's
36 * more than one battery: the readdir won't return the
37 * entries in sorted order, so battery one won't
38 * necessarily be the first one returned. So, we need
39 * to sort them ourselves before adding them to the
40 * batteries array. */
41 name = batt->d_name;
43 /* skip . and .. */
44 if (!strncmp(".", name, 1) || !strncmp("..", name, 2))
45 continue;
47 names[globals->battery_count] = strdup(name);
48 globals->battery_count++;
50 closedir(battdir);
52 /* A nice quick insertion sort, ala CLR. */
54 char *tmp1, *tmp2;
56 for (i = 1; i < globals->battery_count; i++) {
57 tmp1 = names[i];
58 j = i - 1;
59 while ((j >= 0) && ((strcmp(tmp1, names[j])) < 0)) {
60 tmp2 = names[j+1];
61 names[j+1] = names[j];
62 names[j] = tmp2;
67 for (i = 0; i < globals->battery_count; i++) {
68 snprintf(batteries[i].name, MAX_NAME, "%s", names[i]);
69 snprintf(batteries[i].info_file, MAX_NAME,
70 "/proc/acpi/battery/%s/info", names[i]);
71 snprintf(batteries[i].state_file, MAX_NAME,
72 "/proc/acpi/battery/%s/state", names[i]);
73 pdebug("battery detected at %s\n", batteries[i].info_file);
74 pinfo("found battery %s\n", names[i]);
77 /* tell user some info */
78 pdebug("%d batteries detected\n", globals->battery_count);
79 pinfo("libacpi: found %d batter%s\n", globals->battery_count,
80 (globals->battery_count == 1) ? "y" : "ies");
82 return 0;
85 /* a stub that just calls the current function */
86 int reinit_batteries(void)
88 pdebug("reinitialising batteries\n");
89 return init_batteries();
92 /* the actual name of the subdirectory under ac_adapter may
93 * be anything, so we need to read the directory and use the
94 * name we find there. */
95 int init_ac_adapters(void)
97 DIR *acdir;
98 struct dirent *adapter;
99 adapter_t *ap = &globals->adapter;
100 char *name;
102 acdir = opendir("/proc/acpi/ac_adapter");
103 if (acdir == NULL) {
104 pfatal("Unable to open /proc/acpi/ac_adapter -"
105 " are you sure this system supports ACPI?\n");
106 return 1;
108 name = NULL;
109 while ((adapter = readdir(acdir)) != NULL) {
110 name = adapter->d_name;
112 if (!strncmp(".", name, 1) || !strncmp("..", name, 2))
113 continue;
114 pdebug("found adapter %s\n", name);
116 closedir(acdir);
117 /* we /should/ only see one filename other than . and .. so
118 * we'll just use the last value name acquires . . . */
119 ap->name = strdup(name);
120 snprintf(ap->state_file, MAX_NAME, "/proc/acpi/ac_adapter/%s/state",
121 ap->name);
122 pinfo("libacpi: found ac adapter %s\n", ap->name);
124 return 0;
127 /* stub that does nothing but call the normal init function */
128 int reinit_ac_adapters(void)
130 pdebug("reinitialising ac adapters\n");
131 return init_ac_adapters();
134 /* see if we have ACPI support and check version */
135 int power_init(void)
137 FILE *acpi;
138 char buf[4096];
139 int acpi_ver = 0;
140 int retval;
142 if (!(acpi = fopen("/proc/acpi/info", "r"))) {
143 pfatal("This system does not support ACPI\n");
144 return 1;
147 /* okay, now see if we got the right version */
148 fread(buf, 4096, 1, acpi);
149 acpi_ver = strtol(buf + 25, NULL, 10);
150 pinfo("ACPI version detected: %d\n", acpi_ver);
151 if (acpi_ver < 20020214) {
152 pfatal("This version requires ACPI subsystem version 20020214\n");
153 fclose(acpi);
154 return 1;
156 /* yep, all good */
157 fclose(acpi);
159 if (!(retval = init_batteries()))
160 retval = init_ac_adapters();
162 return retval;
165 /* reinitialise everything, to deal with changing batteries or ac adapters */
166 int power_reinit(void)
168 FILE *acpi;
169 int retval;
171 if (!(acpi = fopen("/proc/acpi/info", "r"))) {
172 pfatal("Could not reopen ACPI info file - does this system support ACPI?\n");
173 return 1;
176 if (!(retval = reinit_batteries()))
177 retval = reinit_ac_adapters();
179 return retval;
182 char *get_value(char *string)
184 char *retval;
185 int i;
187 if (string == NULL)
188 return NULL;
190 i = 0;
191 while (string[i] != ':') i++;
192 while (!isalnum(string[i])) i++;
193 retval = (string + i);
195 return retval;
198 power_state_t get_power_status(void)
200 FILE *file;
201 char buf[1024];
202 char *val;
203 adapter_t *ap = &globals->adapter;
205 if ((file = fopen(ap->state_file, "r")) == NULL) {
206 snprintf(buf, 1024, "Could not open state file %s", ap->state_file);
207 perror(buf);
208 return PS_ERR;
211 fgets(buf, 1024, file);
212 fclose(file);
213 val = get_value(buf);
214 if ((strncmp(val, "on-line", 7)) == 0)
215 return AC;
216 else
217 return BATT;
220 int get_battery_info(int batt_no)
222 FILE *file;
223 battery_t *info = &batteries[batt_no];
224 char buf[1024];
225 char *entry;
226 int buflen;
227 char *val;
229 if ((file = fopen(info->info_file, "r")) == NULL) {
230 /* this is cheating, but string concatenation should work . . . */
231 pfatal("Could not open %s:", info->info_file );
232 perror(NULL);
233 return 0;
236 /* grab the contents of the file */
237 buflen = fread(buf, sizeof(buf), 1, file);
238 fclose(file);
240 /* check to see if battery is present */
241 entry = strstr(buf, "present:");
242 val = get_value(entry);
243 if ((strncmp(val, "yes", 3)) == 0) {
244 info->present = 1;
245 } else {
246 pinfo("Battery %s not present\n", info->name);
247 info->present = 0;
248 return 0;
251 /* get design capacity
252 * note that all these integer values can also contain the
253 * string 'unknown', so we need to check for this. */
254 entry = strstr(buf, "design capacity:");
255 val = get_value(entry);
256 if (val[0] == 'u')
257 info->design_cap = -1;
258 else
259 info->design_cap = strtoul(val, NULL, 10);
261 /* get last full capacity */
262 entry = strstr(buf, "last full capacity:");
263 val = get_value(entry);
264 if (val[0] == 'u')
265 info->last_full_cap = -1;
266 else
267 info->last_full_cap = strtoul(val, NULL, 10);
269 /* get design voltage */
270 entry = strstr(buf, "design voltage:");
271 val = get_value(entry);
272 if (val[0] == 'u')
273 info->design_voltage = -1;
274 else
275 info->design_voltage = strtoul(val, NULL, 10);
278 if ((file = fopen(info->state_file, "r")) == NULL) {
279 perr("Could not open %s:", info->state_file );
280 perror(NULL);
281 return 0;
284 /* grab the file contents */
285 buflen = fread(buf, sizeof(buf), 1, file);
286 fclose(file);
288 /* check to see if battery is present */
289 entry = strstr(buf, "present:");
290 val = get_value(entry);
291 if ((strncmp(val, "yes", 3)) == 0) {
292 info->present = 1;
293 } else {
294 info->present = 0;
295 perr("Battery %s no longer present\n", info->name);
296 return 0;
299 /* get capacity state
300 * note that this has only two values (at least, in the 2.4.21-rc2
301 * source code) - ok and critical. */
302 entry = strstr(buf, "capacity state:");
303 val = get_value(entry);
304 if (val[0] == 'u')
305 info->capacity_state = CS_ERR;
306 else if ((strncmp(val, "ok", 2)) == 0)
307 info->capacity_state = OK;
308 else
309 info->capacity_state = CRITICAL;
311 /* get charging state */
312 entry = strstr(buf, "charging state:");
313 val = get_value(entry);
314 if (val[0] == 'u')
315 info->charge_state = CH_ERR;
316 else if ((strncmp(val, "discharging", 10)) == 0)
317 info->charge_state = DISCHARGE;
318 else
319 info->charge_state = CHARGE;
321 /* get current rate of burn
322 * note that if it's on AC, this will report 0 */
323 entry = strstr(buf, "present rate:");
324 val = get_value(entry);
325 if (val[0] == 'u') {
326 info->present_rate = -1;
327 } else {
328 int rate;
329 rate = strtoul(val, NULL, 10);
330 if (rate != 0)
331 info->present_rate = rate;
334 /* get remaining capacity */
335 entry = strstr(buf, "remaining capacity:");
336 val = get_value(entry);
337 if (val[0] == 'u')
338 info->remaining_cap = -1;
339 else
340 info->remaining_cap = strtoul(val, NULL, 10);
342 /* get current voltage */
343 entry = strstr(buf, "present voltage:");
344 val = get_value(entry);
345 if (val[0] == 'u')
346 info->present_voltage = -1;
347 else
348 info->present_voltage = strtoul(val, NULL, 10);
350 return 1;
354 * 2003-7-1.
355 * In order to make this code more convenient for things other than
356 * just plain old wmacpi-ng I'm breaking the basic functionality
357 * up into several chunks: collecting and collating info for a
358 * single battery, calculating the global info (such as rtime), and
359 * some stuff to provide a similar interface to now.
362 /* calculate the percentage remaining, using the values of
363 * remaining capacity and last full capacity, as outlined in
364 * the ACPI spec v2.0a, section 3.9.3. */
365 static int calc_remaining_percentage(int batt)
367 float rcap, lfcap;
368 battery_t *binfo;
369 int retval;
371 binfo = &batteries[batt];
373 rcap = (float)binfo->remaining_cap;
374 lfcap = (float)binfo->last_full_cap;
376 /* we use -1 to indicate that the value is unknown . . . */
377 if (rcap < 0) {
378 perr("unknown percentage value\n");
379 retval = -1;
380 } else {
381 if (lfcap <= 0)
382 lfcap = 1;
383 retval = (int)((rcap/lfcap) * 100.0);
384 pdebug("percent: %d\n", retval);
386 return retval;
389 /* calculate remaining time until the battery is charged.
390 * when charging, the battery state file reports the
391 * current being used to charge the battery. We can use
392 * this and the remaining capacity to work out how long
393 * until it reaches the last full capacity of the battery.
394 * XXX: make sure this is actually portable . . . */
395 static int calc_charge_time(int batt)
397 float rcap, lfcap;
398 battery_t *binfo;
399 int charge_time = 0;
401 binfo = &batteries[batt];
403 if (binfo->charge_state == CHARGE) {
404 if (binfo->present_rate == -1) {
405 perr("unknown present rate\n");
406 charge_time = -1;
407 } else {
408 lfcap = (float)binfo->last_full_cap;
409 rcap = (float)binfo->remaining_cap;
411 charge_time = (int)(((lfcap - rcap)/binfo->present_rate) * 60.0);
413 } else
414 if (binfo->charge_time)
415 charge_time = 0;
416 return charge_time;
419 void acquire_batt_info(int batt)
421 battery_t *binfo;
422 adapter_t *ap = &globals->adapter;
424 get_battery_info(batt);
426 binfo = &batteries[batt];
428 if (!binfo->present) {
429 binfo->percentage = 0;
430 binfo->valid = 0;
431 binfo->charge_time = 0;
432 globals->rtime = 0;
433 return;
436 binfo->percentage = calc_remaining_percentage(batt);
438 /* set the battery's capacity state, based (at present) on some
439 * guesstimated values: more than 75% == HIGH, 25% to 75% MED, and
440 * less than 25% is LOW. Less than globals->crit_level is CRIT. */
441 if (binfo->percentage == -1)
442 binfo->state = BS_ERR;
443 if (binfo->percentage < globals->crit_level)
444 binfo->state = CRIT;
445 else if (binfo->percentage > 75)
446 binfo->state = HIGH;
447 else if (binfo->percentage > 25)
448 binfo->state = MED;
449 else
450 binfo->state = LOW;
452 /* we need to /know/ that we've got a valid state for the
453 * globals->power value . . . .*/
454 ap->power = get_power_status();
456 if ((ap->power != AC) && (binfo->charge_state == DISCHARGE)) {
457 /* we're not on power, and not charging. So we might as well
458 * check if we're at a critical battery level, and calculate
459 * other interesting stuff . . . */
460 if (binfo->capacity_state == CRITICAL) {
461 pinfo("Received critical battery status");
462 ap->power = HARD_CRIT;
466 binfo->charge_time = calc_charge_time(batt);
468 /* and finally, we tell anyone who wants to use this information
469 * that it's now valid . . .*/
470 binfo->valid = 1;
473 void acquire_all_batt_info(void)
475 int i;
477 for(i = 0; i < globals->battery_count; i++)
478 acquire_batt_info(i);
481 void acquire_global_info(void)
483 int i;
484 int rtime;
485 float rcap = 0;
486 float rate = 0;
487 battery_t *binfo;
488 adapter_t *ap = &globals->adapter;
489 static float rate_samples[SAMPLES];
490 static int j = 0;
491 static int sample_count = 0;
492 static int n = 0;
494 /* calculate the time remaining, using the battery's remaining
495 * capacity and the reported burn rate (3.9.3).
496 * For added accuracy, we average the value over the last
497 * SAMPLES number of calls, or for anything less than this we
498 * simply report the raw number. */
499 /* XXX: this needs to correctly handle the case where
500 * any of the values used is unknown (which we flag using
501 * -1). */
502 for (i = 0; i < globals->battery_count; i++) {
503 binfo = &batteries[i];
504 if (binfo->present && binfo->valid) {
505 rcap += (float)binfo->remaining_cap;
506 rate += (float)binfo->present_rate;
509 rate_samples[j] = rate;
510 j++, sample_count++;
511 j = j % SAMPLES;
513 /* for the first SAMPLES number of calls we calculate the
514 * average based on sample_count, then we use SAMPLES to
515 * calculate the rolling average. */
517 /* when this fails, n should be equal to SAMPLES. */
518 if (sample_count < SAMPLES)
519 n++;
520 for (i = 0, rate = 0; i < n; i++) {
521 /* if any of our samples are invalid, we drop
522 * straight out, and flag our unknown values. */
523 if (rate_samples[i] < 0) {
524 rate = -1;
525 rtime = -1;
526 goto out;
528 rate += rate_samples[i];
530 rate = rate/(float)n;
532 if ((rcap < 1) || (rate < 1)) {
533 rtime = 0;
534 goto out;
536 if (rate <= 0)
537 rate = 1;
538 /* time remaining in minutes */
539 rtime = (int)((rcap/rate) * 60.0);
540 if(rtime <= 0)
541 rtime = 0;
542 out:
543 pdebug("time rem: %d\n", rtime);
544 globals->rtime = rtime;
546 /* get the power status.
547 * note that this is actually reported seperately from the
548 * battery info, under /proc/acpi/ac_adapter/AC/state */
549 ap->power = get_power_status();
552 void acquire_all_info(void)
554 acquire_all_batt_info();
555 acquire_global_info();