2 * A not-yet-general-purpose ACPI library, by Joey Hess <joey@kitenet.net>
15 #include <sys/types.h>
21 #define SYSFS_PATH "/sys/class/power_supply"
22 #define ACPI_MAXITEM 8
24 int acpi_batt_count
= 0;
25 /* Filenames of the battery info files for each system battery. */
26 char acpi_batt_info
[ACPI_MAXITEM
][128];
27 /* Filenames of the battery status files for each system battery. */
28 char acpi_batt_status
[ACPI_MAXITEM
][128];
29 /* Stores battery capacity, or 0 if the battery is absent. */
30 int acpi_batt_capacity
[ACPI_MAXITEM
];
32 int acpi_ac_count
= 0;
33 char acpi_ac_adapter_info
[ACPI_MAXITEM
][128];
34 char acpi_ac_adapter_status
[ACPI_MAXITEM
][128];
36 char *acpi_labels
[] = {
41 "POWER_SUPPLY_CAPACITY=",
42 "POWER_SUPPLY_??????_FULL_DESIGN=", /* CHARGE or ENERGY */
43 "POWER_SUPPLY_PRESENT=",
44 "POWER_SUPPLY_??????_NOW=",
45 "POWER_SUPPLY_CURRENT_NOW=",
46 "POWER_SUPPLY_STATUS=",
50 "POWER_SUPPLY_ONLINE=",
51 "POWER_SUPPLY_??????_FULL=",
56 int acpi_thermal_count
= 0;
57 char acpi_thermal_info
[ACPI_MAXITEM
][128];
58 char acpi_thermal_status
[ACPI_MAXITEM
][128];
61 /* Read in an entire ACPI proc file (well, the first 1024 bytes anyway), and
62 * return a statically allocated array containing it. */
63 inline char *get_acpi_file (const char *file
) {
66 static char buf
[1024];
67 fd
= open(file
, O_RDONLY
);
68 if (fd
== -1) return NULL
;
69 end
= read(fd
, buf
, sizeof(buf
));
75 int strmcmp(const char *s1
, const char *s2
)
77 for (; (*s1
== *s2
) || (*s2
== '?'); s1
++, s2
++) {
87 /* Given a buffer holding an acpi file, searches for the given key in it,
88 * and returns the numeric value. 0 is returned on failure. */
89 inline int scan_acpi_num (const char *buf
, const char *key
) {
94 ptr
= strchr(buf
, '\n');
95 if (!strmcmp(buf
, key
)) {
96 if ((ptr
= strchr(buf
, '='))) {
97 sscanf(ptr
+ 1, "%d", &ret
);
106 } while (buf
!= NULL
);
110 /* Given a buffer holding an acpi file, searches for the given key in it,
111 * and returns its value in a statically allocated string. */
112 inline char *scan_acpi_value (const char *buf
, const char *key
) {
114 static char ret
[256];
117 ptr
= strchr(buf
, '\n');
118 if (!strmcmp(buf
, key
)) {
119 if ((ptr
= strchr(buf
, '='))) {
120 if (sscanf(ptr
+ 1, "%255s", ret
) == 1) {
132 } while (buf
!= NULL
);
136 /* Read an ACPI proc file, pull out the requested piece of information, and
137 * return it (statically allocated string). Returns NULL on error, This is
138 * the slow, dumb way, fine for initialization or if only one value is needed
139 * from a file, slow if called many times. */
140 char *get_acpi_value (const char *file
, const char *key
) {
141 char *buf
= get_acpi_file(file
);
142 if (! buf
) return NULL
;
143 return scan_acpi_value(buf
, key
);
146 /* Returns the last full charge capacity of a battery.
148 int get_acpi_batt_capacity(int battery
) {
151 s
= get_acpi_value(acpi_batt_info
[battery
], acpi_labels
[label_last_full_capacity
]);
159 /* Comparison function for qsort. */
160 int _acpi_compare_strings (const void *a
, const void *b
) {
161 const char **pa
= (const char **)a
;
162 const char **pb
= (const char **)b
;
163 return strcasecmp((const char *)*pa
, (const char *)*pb
);
166 /* Find something (batteries, ac adpaters, etc), and set up a string array
167 * to hold the paths to info and status files of the things found.
168 * Returns the number of items found. */
169 int find_items (char *itemname
, char infoarray
[ACPI_MAXITEM
][128],
170 char statusarray
[ACPI_MAXITEM
][128]) {
175 char **devices
= malloc(ACPI_MAXITEM
* sizeof(char *));
179 sprintf(pathname
, SYSFS_PATH
);
181 dir
= opendir(pathname
);
184 while ((ent
= readdir(dir
))) {
188 if (!strcmp(".", ent
->d_name
) ||
189 !strcmp("..", ent
->d_name
))
192 snprintf(filename
, sizeof(filename
), SYSFS_PATH
"/%s/type", ent
->d_name
);
193 int fd
= open(filename
, O_RDONLY
);
195 int end
= read(fd
, buf
, sizeof(buf
));
198 if (strstr(buf
, itemname
) != buf
)
202 devices
[num_devices
]=strdup(ent
->d_name
);
204 if (num_devices
>= ACPI_MAXITEM
)
209 /* Sort, since readdir can return in any order. /sys/ does
210 * sometimes list BAT1 before BAT0. */
211 qsort(devices
, num_devices
, sizeof(char *), _acpi_compare_strings
);
213 for (i
= 0; i
< num_devices
; i
++) {
214 snprintf(infoarray
[i
], sizeof(infoarray
[i
]), SYSFS_PATH
"/%s/%s", devices
[i
],
215 acpi_labels
[label_info
]);
216 snprintf(statusarray
[i
], sizeof(statusarray
[i
]), SYSFS_PATH
"/%s/%s", devices
[i
],
217 acpi_labels
[label_status
]);
224 /* Find batteries, return the number, and set acpi_batt_count to it as well. */
225 int find_batteries(void) {
227 acpi_batt_count
= find_items(acpi_labels
[label_battery
], acpi_batt_info
, acpi_batt_status
);
228 for (i
= 0; i
< acpi_batt_count
; i
++)
229 acpi_batt_capacity
[i
] = get_acpi_batt_capacity(i
);
230 return acpi_batt_count
;
233 /* Find AC power adapters, return the number found, and set acpi_ac_count to it
235 int find_ac_adapters(void) {
236 acpi_ac_count
= find_items(acpi_labels
[label_ac_adapter
], acpi_ac_adapter_info
, acpi_ac_adapter_status
);
237 return acpi_ac_count
;
241 /* Find thermal information sources, return the number found, and set
242 * thermal_count to it as well. */
243 int find_thermal(void) {
244 acpi_thermal_count
= find_items(acpi_labels
[label_thermal
], acpi_thermal_info
, acpi_thermal_status
);
245 return acpi_thermal_count
;
249 /* Returns true if the system is on ac power. Call find_ac_adapters first. */
250 int on_ac_power (void) {
252 for (i
= 0; i
< acpi_ac_count
; i
++) {
253 char *online
=get_acpi_value(acpi_ac_adapter_info
[i
], acpi_labels
[label_ac_state
]);
254 if (online
&& atoi(online
))
262 /* See if we have ACPI support and check version. Also find batteries and
263 * ac power adapters. */
264 int acpi_supported (void) {
269 if (!(dir
= opendir(SYSFS_PATH
))) {
274 /* If kernel is 2.6.21 or newer, version is in
275 /sys/module/acpi/parameters/acpica_version */
277 version
= get_acpi_file("/sys/module/acpi/parameters/acpica_version");
278 if (version
== NULL
) {
282 if (num
< ACPI_VERSION
) {
283 fprintf(stderr
, "ACPI subsystem %s too is old, consider upgrading to %i.\n",
284 version
, ACPI_VERSION
);
298 /* Read ACPI info on a given power adapter and battery, and fill the passed
299 * apm_info struct. */
300 int acpi_read (int battery
, apm_info
*info
) {
303 if (acpi_batt_count
== 0) {
304 info
->battery_percentage
= 0;
305 info
->battery_time
= 0;
306 info
->battery_status
= BATTERY_STATUS_ABSENT
;
307 acpi_batt_capacity
[battery
] = 0;
308 /* Where else would the power come from, eh? ;-) */
309 info
->ac_line_status
= 1;
313 /* Internally it's zero indexed. */
316 buf
= get_acpi_file(acpi_batt_info
[battery
]);
318 fprintf(stderr
, "unable to read %s\n", acpi_batt_info
[battery
]);
323 info
->ac_line_status
= 0;
324 info
->battery_flags
= 0;
325 info
->using_minutes
= 1;
327 /* Work out if the battery is present, and what percentage of full
328 * it is and how much time is left. */
329 if (strcmp(scan_acpi_value(buf
, acpi_labels
[label_present
]), "1") == 0) {
330 int pcap
= scan_acpi_num(buf
, acpi_labels
[label_remaining_capacity
]);
331 int rate
= scan_acpi_num(buf
, acpi_labels
[label_present_rate
]);
333 /* time remaining = (current_capacity / discharge rate) */
334 info
->battery_time
= (float) pcap
/ (float) rate
* 60;
337 char *rate_s
= scan_acpi_value(buf
, acpi_labels
[label_present_rate
]);
339 /* Time remaining unknown. */
340 info
->battery_time
= 0;
343 /* a zero or unknown in the file; time
344 * unknown so use a negative one to
346 info
->battery_time
= -1;
350 state
= scan_acpi_value(buf
, acpi_labels
[label_charging_state
]);
352 if (state
[0] == 'D') { /* discharging */
353 info
->battery_status
= BATTERY_STATUS_CHARGING
;
354 /* Expensive ac power check used here
355 * because AC power might be on even if a
356 * battery is discharging in some cases. */
357 info
->ac_line_status
= on_ac_power();
359 else if (state
[0] == 'C' && state
[1] == 'h') { /* charging */
360 info
->battery_status
= BATTERY_STATUS_CHARGING
;
361 info
->ac_line_status
= 1;
362 info
->battery_flags
= info
->battery_flags
| BATTERY_FLAGS_CHARGING
;
364 info
->battery_time
= -1 * (float) (acpi_batt_capacity
[battery
] - pcap
) / (float) rate
* 60;
366 info
->battery_time
= 0;
367 if (abs(info
->battery_time
) < 0.5)
368 info
->battery_time
= 0;
370 else if (state
[0] == 'F') { /* full */
371 /* charged, on ac power */
372 info
->battery_status
= BATTERY_STATUS_HIGH
;
373 info
->ac_line_status
= 1;
375 else if (state
[0] == 'C') { /* not charging, so must be critical */
376 info
->battery_status
= BATTERY_STATUS_CRITICAL
;
377 /* Expensive ac power check used here
378 * because AC power might be on even if a
379 * battery is critical in some cases. */
380 info
->ac_line_status
= on_ac_power();
382 else if (state
[0] == 'U') { /* unknown */
383 info
->ac_line_status
= on_ac_power();
384 int current
= scan_acpi_num(buf
, acpi_labels
[label_present_rate
]);
385 if (info
->ac_line_status
) {
387 info
->battery_status
= BATTERY_STATUS_HIGH
;
389 info
->battery_status
= BATTERY_STATUS_CHARGING
;
392 info
->battery_status
= BATTERY_STATUS_CHARGING
;
396 fprintf(stderr
, "unknown battery state: %s\n", state
);
400 /* Battery state unknown. */
401 info
->battery_status
= BATTERY_STATUS_ABSENT
;
404 if (acpi_batt_capacity
[battery
] == 0) {
405 /* The battery was absent, and now is present.
406 * Well, it might be a different battery. So
407 * re-probe the battery. */
408 /* NOTE that this invalidates buf. No accesses of
409 * buf below this point! */
410 acpi_batt_capacity
[battery
] = get_acpi_batt_capacity(battery
);
412 else if (pcap
> acpi_batt_capacity
[battery
]) {
413 /* Battery is somehow charged to greater than max
414 * capacity. Rescan for a new max capacity. */
418 if (pcap
&& acpi_batt_capacity
[battery
]) {
419 info
->battery_percentage
= 100 * pcap
/ acpi_batt_capacity
[battery
];
420 if (info
->battery_percentage
> 100)
421 info
->battery_percentage
= 100;
424 info
->battery_percentage
= -1;
429 info
->battery_percentage
= 0;
430 info
->battery_time
= 0;
431 info
->battery_status
= BATTERY_STATUS_ABSENT
;
432 acpi_batt_capacity
[battery
] = 0;
433 if (acpi_batt_count
== 0) {
434 /* Where else would the power come from, eh? ;-) */
435 info
->ac_line_status
= 1;
438 /* Expensive ac power check. */
439 info
->ac_line_status
= on_ac_power();