13 extern global_t
*globals
;
18 int acpi_get_design_cap(int batt
);
20 /* initialise the batteries */
21 int init_batteries(void)
25 char *name
, *tmp1
, *tmp2
;
29 /* now enumerate batteries */
31 battdir
= opendir("/proc/acpi/battery");
32 if (battdir
== NULL
) {
33 fprintf(stderr
, "No batteries or ACPI not supported\n");
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
46 if (!strncmp(".", name
, 1) || !strncmp("..", name
, 2))
49 names
[batt_count
] = strdup(name
);
54 /* A nice quick insertion sort, ala CLR. */
55 for (i
= 1; i
< batt_count
; i
++) {
58 while ((j
>= 0) && ((strcmp(tmp1
, names
[j
])) < 0)) {
60 names
[j
+1] = names
[j
];
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");
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)
89 struct dirent
*adapter
;
90 adapter_t
*ap
= &globals
->adapter
;
93 acdir
= opendir("/proc/acpi/ac_adapter");
95 fprintf(stderr
, "Unable to open /proc/acpi/ac_adapter -"
96 " are you sure this system supports ACPI?\n");
100 while ((adapter
= readdir(acdir
)) != NULL
) {
101 name
= adapter
->d_name
;
103 if (!strncmp(".", name
, 1) || !strncmp("..", name
, 2))
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",
112 fprintf(stderr
, "libacpi: found ac adapter %s\n", ap
->name
);
117 /* see if we have ACPI support and check version */
125 if (!(acpi
= fopen("/proc/acpi/info", "r"))) {
126 fprintf(stderr
, "This system does not support ACPI\n");
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");
142 if (!(retval
= init_batteries()))
143 retval
= init_ac_adapters();
148 char *get_value(char *string
)
157 while (string
[i
] != ':') i
++;
158 while (!isalnum(string
[i
])) i
++;
159 retval
= (string
+ i
);
164 power_state_t
get_power_status(void)
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
);
177 fgets(buf
, 1024, file
);
179 val
= get_value(buf
);
180 if ((strncmp(val
, "on-line", 7)) == 0)
186 int get_battery_info(int batt_no
)
189 battery_t
*info
= &batteries
[batt_no
];
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
);
202 /* grab the contents of the file */
203 buflen
= fread(buf
, sizeof(buf
), 1, 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) {
212 eprint(0, "Battery %s not present\n", info
->name
);
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
);
223 info
->design_cap
= -1;
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
);
231 info
->last_full_cap
= -1;
233 info
->last_full_cap
= strtoul(val
, NULL
, 10);
235 /* get design voltage */
236 entry
= strstr(buf
, "design voltage:");
237 val
= get_value(entry
);
239 info
->design_voltage
= -1;
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
);
250 /* grab the file contents */
251 buflen
= fread(buf
, sizeof(buf
), 1, 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) {
261 eprint(1, "Battery %s no longer present\n", info
->name
);
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
);
271 info
->capacity_state
= CS_ERR
;
272 else if ((strncmp(val
, "ok", 2)) == 0)
273 info
->capacity_state
= OK
;
275 info
->capacity_state
= CRITICAL
;
277 /* get charging state */
278 entry
= strstr(buf
, "charging state:");
279 val
= get_value(entry
);
281 info
->charge_state
= CH_ERR
;
282 else if ((strncmp(val
, "discharging", 10)) == 0)
283 info
->charge_state
= DISCHARGE
;
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
);
292 info
->present_rate
= -1;
295 rate
= strtoul(val
, NULL
, 10);
297 info
->present_rate
= rate
;
300 /* get remaining capacity */
301 entry
= strstr(buf
, "remaining capacity:");
302 val
= get_value(entry
);
304 info
->remaining_cap
= -1;
306 info
->remaining_cap
= strtoul(val
, NULL
, 10);
308 /* get current voltage */
309 entry
= strstr(buf
, "present voltage:");
310 val
= get_value(entry
);
312 info
->present_voltage
= -1;
314 info
->present_voltage
= strtoul(val
, NULL
, 10);
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
)
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 . . . */
344 eprint(0, "unknown percentage value\n");
349 retval
= (int)((rcap
/lfcap
) * 100.0);
350 eprint(0, "percent: %d\n", 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
)
367 binfo
= &batteries
[batt
];
369 if (binfo
->charge_state
== CHARGE
) {
370 if (binfo
->present_rate
== -1) {
371 eprint(0, "unknown present rate\n");
374 lfcap
= (float)binfo
->last_full_cap
;
375 rcap
= (float)binfo
->remaining_cap
;
377 charge_time
= (int)(((lfcap
- rcap
)/binfo
->present_rate
) * 60.0);
380 if (binfo
->charge_time
)
385 void acquire_batt_info(int batt
)
388 adapter_t
*ap
= &globals
->adapter
;
390 get_battery_info(batt
);
392 binfo
= &batteries
[batt
];
394 if (!binfo
->present
) {
395 binfo
->percentage
= 0;
397 binfo
->charge_time
= 0;
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
)
411 else if (binfo
->percentage
> 75)
413 else if (binfo
->percentage
> 25)
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 . . .*/
439 void acquire_all_batt_info(void)
443 for(i
= 0; i
< batt_count
; i
++)
444 acquire_batt_info(i
);
447 void acquire_global_info(void)
454 adapter_t
*ap
= &globals
->adapter
;
455 static float rate_samples
[SAMPLES
];
457 static int sample_count
= 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
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
;
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
)
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) {
494 rate
+= rate_samples
[i
];
496 rate
= rate
/(float)n
;
498 if ((rcap
< 1) || (rate
< 1)) {
504 /* time remaining in minutes */
505 rtime
= (int)((rcap
/rate
) * 60.0);
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();