wmacpi: Bump to wmacpi-ng version 0.99.
[dockapps.git] / wmacpi / libacpi.c
blobe0b536df548e5eed93a20ae89aa2ec2a7a99e0cd
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;
14 /* temp buffer */
15 char buf[512];
17 /* local proto */
18 int acpi_get_design_cap(int batt);
20 /* initialise the batteries */
21 int init_batteries(void)
23 DIR *battdir;
24 struct dirent *batt;
25 char *name, *tmp1, *tmp2;
26 char *names[MAXBATT];
27 int i, j;
29 /* now enumerate batteries */
30 batt_count = 0;
31 battdir = opendir("/proc/acpi/battery");
32 if (battdir == NULL) {
33 fprintf(stderr, "No batteries or ACPI not supported\n");
34 return 1;
36 while ((batt = readdir(battdir))) {
37 /* there's a serious problem with this code when there's
38 * more than one battery: the readdir won't return the
39 * entries in sorted order, so battery one won't
40 * necessarily be the first one returned. So, we need
41 * to sort them ourselves before adding them to the
42 * batteries array. */
43 name = batt->d_name;
45 /* skip . and .. */
46 if (!strncmp(".", name, 1) || !strncmp("..", name, 2))
47 continue;
49 names[batt_count] = strdup(name);
50 batt_count++;
52 closedir(battdir);
54 /* A nice quick insertion sort, ala CLR. */
55 for (i = 1; i < batt_count; i++) {
56 tmp1 = names[i];
57 j = i - 1;
58 while ((j >= 0) && ((strcmp(tmp1, names[j])) < 0)) {
59 tmp2 = names[j+1];
60 names[j+1] = names[j];
61 names[j] = tmp2;
65 for (i = 0; i < batt_count; i++) {
66 snprintf(batteries[i].name, MAX_NAME, "%s", names[i]);
67 snprintf(batteries[i].info_file, MAX_NAME,
68 "/proc/acpi/battery/%s/info", names[i]);
69 snprintf(batteries[i].state_file, MAX_NAME,
70 "/proc/acpi/battery/%s/state", names[i]);
71 eprint(0, "battery detected at %s\n", batteries[i].info_file);
72 fprintf(stderr, "found battery %s\n", names[i]);
75 /* tell user some info */
76 eprint(0, "%d batteries detected\n", batt_count);
77 fprintf(stderr, "libacpi: found %d batter%s\n", batt_count,
78 (batt_count == 1) ? "y" : "ies");
80 return 0;
83 /* the actual name of the subdirectory under ac_adapter may
84 * be anything, so we need to read the directory and use the
85 * name we find there. */
86 int init_ac_adapters(void)
88 DIR *acdir;
89 struct dirent *adapter;
90 adapter_t *ap = &globals->adapter;
91 char *name;
93 acdir = opendir("/proc/acpi/ac_adapter");
94 if (acdir == NULL) {
95 fprintf(stderr, "Unable to open /proc/acpi/ac_adapter -"
96 " are you sure this system supports ACPI?\n");
97 return 1;
99 name = NULL;
100 while ((adapter = readdir(acdir)) != NULL) {
101 name = adapter->d_name;
103 if (!strncmp(".", name, 1) || !strncmp("..", name, 2))
104 continue;
105 fprintf(stderr, "found adapter %s\n", name);
107 /* we /should/ only see one filename other than . and .. so
108 * we'll just use the last value name acquires . . . */
109 ap->name = strdup(name);
110 snprintf(ap->state_file, MAX_NAME, "/proc/acpi/ac_adapter/%s/state",
111 ap->name);
112 fprintf(stderr, "libacpi: found ac adapter %s\n", ap->name);
114 return 0;
117 /* see if we have ACPI support and check version */
118 int power_init(void)
120 FILE *acpi;
121 char buf[4096];
122 int acpi_ver = 0;
123 int retval;
125 if (!(acpi = fopen("/proc/acpi/info", "r"))) {
126 fprintf(stderr, "This system does not support ACPI\n");
127 return 1;
130 /* okay, now see if we got the right version */
131 fread(buf, 4096, 1, acpi);
132 acpi_ver = strtol(buf + 25, NULL, 10);
133 eprint(0, "ACPI version detected: %d\n", acpi_ver);
134 if (acpi_ver < 20020214) {
135 fprintf(stderr, "This version requires ACPI subsystem version 20020214\n");
136 fclose(acpi);
137 return 1;
139 /* yep, all good */
140 fclose(acpi);
142 if (!(retval = init_batteries()))
143 retval = init_ac_adapters();
145 return retval;
148 char *get_value(char *string)
150 char *retval;
151 int i;
153 if (string == NULL)
154 return NULL;
156 i = 0;
157 while (string[i] != ':') i++;
158 while (!isalnum(string[i])) i++;
159 retval = (string + i);
161 return retval;
164 power_state_t get_power_status(void)
166 FILE *file;
167 char buf[1024];
168 char *val;
169 adapter_t *ap = &globals->adapter;
171 if ((file = fopen(ap->state_file, "r")) == NULL) {
172 snprintf(buf, 1024, "Could not open state file %s", ap->state_file);
173 perror(buf);
174 return PS_ERR;
177 fgets(buf, 1024, file);
178 fclose(file);
179 val = get_value(buf);
180 if ((strncmp(val, "on-line", 7)) == 0)
181 return AC;
182 else
183 return BATT;
186 int get_battery_info(int batt_no)
188 FILE *file;
189 battery_t *info = &batteries[batt_no];
190 char buf[1024];
191 char *entry;
192 int buflen;
193 char *val;
195 if ((file = fopen(info->info_file, "r")) == NULL) {
196 /* this is cheating, but string concatenation should work . . . */
197 fprintf(stderr, "Could not open %s:", info->info_file );
198 perror(NULL);
199 return 0;
202 /* grab the contents of the file */
203 buflen = fread(buf, sizeof(buf), 1, file);
204 fclose(file);
206 /* check to see if battery is present */
207 entry = strstr(buf, "present:");
208 val = get_value(entry);
209 if ((strncmp(val, "yes", 3)) == 0) {
210 info->present = 1;
211 } else {
212 eprint(0, "Battery %s not present\n", info->name);
213 info->present = 0;
214 return 0;
217 /* get design capacity
218 * note that all these integer values can also contain the
219 * string 'unknown', so we need to check for this. */
220 entry = strstr(buf, "design capacity:");
221 val = get_value(entry);
222 if (val[0] == 'u')
223 info->design_cap = -1;
224 else
225 info->design_cap = strtoul(val, NULL, 10);
227 /* get last full capacity */
228 entry = strstr(buf, "last full capacity:");
229 val = get_value(entry);
230 if (val[0] == 'u')
231 info->last_full_cap = -1;
232 else
233 info->last_full_cap = strtoul(val, NULL, 10);
235 /* get design voltage */
236 entry = strstr(buf, "design voltage:");
237 val = get_value(entry);
238 if (val[0] == 'u')
239 info->design_voltage = -1;
240 else
241 info->design_voltage = strtoul(val, NULL, 10);
244 if ((file = fopen(info->state_file, "r")) == NULL) {
245 fprintf(stderr, "Could not open %s:", info->state_file );
246 perror(NULL);
247 return 0;
250 /* grab the file contents */
251 buflen = fread(buf, sizeof(buf), 1, file);
252 fclose(file);
254 /* check to see if battery is present */
255 entry = strstr(buf, "present:");
256 val = get_value(entry);
257 if ((strncmp(val, "yes", 3)) == 0) {
258 info->present = 1;
259 } else {
260 info->present = 0;
261 eprint(1, "Battery %s no longer present\n", info->name);
262 return 0;
265 /* get capacity state
266 * note that this has only two values (at least, in the 2.4.21-rc2
267 * source code) - ok and critical. */
268 entry = strstr(buf, "capacity state:");
269 val = get_value(entry);
270 if (val[0] == 'u')
271 info->capacity_state = CS_ERR;
272 else if ((strncmp(val, "ok", 2)) == 0)
273 info->capacity_state = OK;
274 else
275 info->capacity_state = CRITICAL;
277 /* get charging state */
278 entry = strstr(buf, "charging state:");
279 val = get_value(entry);
280 if (val[0] == 'u')
281 info->charge_state = CH_ERR;
282 else if ((strncmp(val, "discharging", 10)) == 0)
283 info->charge_state = DISCHARGE;
284 else
285 info->charge_state = CHARGE;
287 /* get current rate of burn
288 * note that if it's on AC, this will report 0 */
289 entry = strstr(buf, "present rate:");
290 val = get_value(entry);
291 if (val[0] == 'u') {
292 info->present_rate = -1;
293 } else {
294 int rate;
295 rate = strtoul(val, NULL, 10);
296 if (rate != 0)
297 info->present_rate = rate;
300 /* get remaining capacity */
301 entry = strstr(buf, "remaining capacity:");
302 val = get_value(entry);
303 if (val[0] == 'u')
304 info->remaining_cap = -1;
305 else
306 info->remaining_cap = strtoul(val, NULL, 10);
308 /* get current voltage */
309 entry = strstr(buf, "present voltage:");
310 val = get_value(entry);
311 if (val[0] == 'u')
312 info->present_voltage = -1;
313 else
314 info->present_voltage = strtoul(val, NULL, 10);
316 return 1;
320 * 2003-7-1.
321 * In order to make this code more convenient for things other than
322 * just plain old wmacpi-ng I'm breaking the basic functionality
323 * up into several chunks: collecting and collating info for a
324 * single battery, calculating the global info (such as rtime), and
325 * some stuff to provide a similar interface to now.
328 /* calculate the percentage remaining, using the values of
329 * remaining capacity and last full capacity, as outlined in
330 * the ACPI spec v2.0a, section 3.9.3. */
331 static int calc_remaining_percentage(int batt)
333 float rcap, lfcap;
334 battery_t *binfo;
335 int retval;
337 binfo = &batteries[batt];
339 rcap = (float)binfo->remaining_cap;
340 lfcap = (float)binfo->last_full_cap;
342 /* we use -1 to indicate that the value is unknown . . . */
343 if (rcap < 0) {
344 eprint(0, "unknown percentage value\n");
345 retval = -1;
346 } else {
347 if (lfcap <= 0)
348 lfcap = 1;
349 retval = (int)((rcap/lfcap) * 100.0);
350 eprint(0, "percent: %d\n", retval);
352 return retval;
355 /* calculate remaining time until the battery is charged.
356 * when charging, the battery state file reports the
357 * current being used to charge the battery. We can use
358 * this and the remaining capacity to work out how long
359 * until it reaches the last full capacity of the battery.
360 * XXX: make sure this is actually portable . . . */
361 static int calc_charge_time(int batt)
363 float rcap, lfcap;
364 battery_t *binfo;
365 int charge_time = 0;
367 binfo = &batteries[batt];
369 if (binfo->charge_state == CHARGE) {
370 if (binfo->present_rate == -1) {
371 eprint(0, "unknown present rate\n");
372 charge_time = -1;
373 } else {
374 lfcap = (float)binfo->last_full_cap;
375 rcap = (float)binfo->remaining_cap;
377 charge_time = (int)(((lfcap - rcap)/binfo->present_rate) * 60.0);
379 } else
380 if (binfo->charge_time)
381 charge_time = 0;
382 return charge_time;
385 void acquire_batt_info(int batt)
387 battery_t *binfo;
388 adapter_t *ap = &globals->adapter;
390 get_battery_info(batt);
392 binfo = &batteries[batt];
394 if (!binfo->present) {
395 binfo->percentage = 0;
396 binfo->valid = 0;
397 binfo->charge_time = 0;
398 globals->rtime = 0;
399 return;
402 binfo->percentage = calc_remaining_percentage(batt);
404 /* set the battery's capacity state, based (at present) on some
405 * guesstimated values: more than 75% == HIGH, 25% to 75% MED, and
406 * less than 25% is LOW. Less than globals->crit_level is CRIT. */
407 if (binfo->percentage == -1)
408 binfo->state = BS_ERR;
409 if (binfo->percentage < globals->crit_level)
410 binfo->state = CRIT;
411 else if (binfo->percentage > 75)
412 binfo->state = HIGH;
413 else if (binfo->percentage > 25)
414 binfo->state = MED;
415 else
416 binfo->state = LOW;
418 /* we need to /know/ that we've got a valid state for the
419 * globals->power value . . . .*/
420 ap->power = get_power_status();
422 if ((ap->power != AC) && (binfo->charge_state == DISCHARGE)) {
423 /* we're not on power, and not charging. So we might as well
424 * check if we're at a critical battery level, and calculate
425 * other interesting stuff . . . */
426 if (binfo->capacity_state == CRITICAL) {
427 eprint(1, "Received critical battery status");
428 ap->power = HARD_CRIT;
432 binfo->charge_time = calc_charge_time(batt);
434 /* and finally, we tell anyone who wants to use this information
435 * that it's now valid . . .*/
436 binfo->valid = 1;
439 void acquire_all_batt_info(void)
441 int i;
443 for(i = 0; i < batt_count; i++)
444 acquire_batt_info(i);
447 void acquire_global_info(void)
449 int i;
450 int rtime;
451 float rcap = 0;
452 float rate = 0;
453 battery_t *binfo;
454 adapter_t *ap = &globals->adapter;
455 static float rate_samples[SAMPLES];
456 static int j = 0;
457 static int sample_count = 0;
458 static int n = 0;
460 /* calculate the time remaining, using the battery's remaining
461 * capacity and the reported burn rate (3.9.3).
462 * For added accuracy, we average the value over the last
463 * SAMPLES number of calls, or for anything less than this we
464 * simply report the raw number. */
465 /* XXX: this needs to correctly handle the case where
466 * any of the values used is unknown (which we flag using
467 * -1). */
468 for (i = 0; i < batt_count; i++) {
469 binfo = &batteries[i];
470 if (binfo->present && binfo->valid) {
471 rcap += (float)binfo->remaining_cap;
472 rate += (float)binfo->present_rate;
475 rate_samples[j] = rate;
476 j++, sample_count++;
477 j = j % SAMPLES;
479 /* for the first SAMPLES number of calls we calculate the
480 * average based on sample_count, then we use SAMPLES to
481 * calculate the rolling average. */
483 /* when this fails, n should be equal to SAMPLES. */
484 if (sample_count < SAMPLES)
485 n++;
486 for (i = 0, rate = 0; i < n; i++) {
487 /* if any of our samples are invalid, we drop
488 * straight out, and flag our unknown values. */
489 if (rate_samples[i] < 0) {
490 rate = -1;
491 rtime = -1;
492 goto out;
494 rate += rate_samples[i];
496 rate = rate/(float)n;
498 if ((rcap < 1) || (rate < 1)) {
499 rtime = 0;
500 goto out;
502 if (rate <= 0)
503 rate = 1;
504 /* time remaining in minutes */
505 rtime = (int)((rcap/rate) * 60.0);
506 if(rtime <= 0)
507 rtime = 0;
508 out:
509 eprint(0, "time rem: %d\n", rtime);
510 globals->rtime = rtime;
512 /* get the power status.
513 * note that this is actually reported seperately from the
514 * battery info, under /proc/acpi/ac_adapter/AC/state */
515 ap->power = get_power_status();
518 void acquire_all_info(void)
520 acquire_all_batt_info();
521 acquire_global_info();