13 extern global_t
*globals
;
16 int acpi_get_design_cap(int batt
);
18 /* initialise the batteries */
19 int init_batteries(void)
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");
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
44 if (!strncmp(".", name
, 1) || !strncmp("..", name
, 2))
47 names
[globals
->battery_count
] = strdup(name
);
48 globals
->battery_count
++;
52 /* A nice quick insertion sort, ala CLR. */
56 for (i
= 1; i
< globals
->battery_count
; i
++) {
59 while ((j
>= 0) && ((strcmp(tmp1
, names
[j
])) < 0)) {
61 names
[j
+1] = names
[j
];
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");
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)
98 struct dirent
*adapter
;
99 adapter_t
*ap
= &globals
->adapter
;
102 acdir
= opendir("/proc/acpi/ac_adapter");
104 pfatal("Unable to open /proc/acpi/ac_adapter -"
105 " are you sure this system supports ACPI?\n");
109 while ((adapter
= readdir(acdir
)) != NULL
) {
110 name
= adapter
->d_name
;
112 if (!strncmp(".", name
, 1) || !strncmp("..", name
, 2))
114 pdebug("found adapter %s\n", name
);
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",
122 pinfo("libacpi: found ac adapter %s\n", ap
->name
);
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 */
142 if (!(acpi
= fopen("/proc/acpi/info", "r"))) {
143 pfatal("This system does not support ACPI\n");
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");
159 if (!(retval
= init_batteries()))
160 retval
= init_ac_adapters();
165 /* reinitialise everything, to deal with changing batteries or ac adapters */
166 int power_reinit(void)
171 if (!(acpi
= fopen("/proc/acpi/info", "r"))) {
172 pfatal("Could not reopen ACPI info file - does this system support ACPI?\n");
176 if (!(retval
= reinit_batteries()))
177 retval
= reinit_ac_adapters();
182 char *get_value(char *string
)
191 while (string
[i
] != ':') i
++;
192 while (!isalnum(string
[i
])) i
++;
193 retval
= (string
+ i
);
198 power_state_t
get_power_status(void)
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
);
211 fgets(buf
, 1024, file
);
213 val
= get_value(buf
);
214 if ((strncmp(val
, "on-line", 7)) == 0)
220 int get_battery_info(int batt_no
)
223 battery_t
*info
= &batteries
[batt_no
];
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
);
236 /* grab the contents of the file */
237 buflen
= fread(buf
, sizeof(buf
), 1, 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) {
246 pinfo("Battery %s not present\n", info
->name
);
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
);
257 info
->design_cap
= -1;
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
);
265 info
->last_full_cap
= -1;
267 info
->last_full_cap
= strtoul(val
, NULL
, 10);
269 /* get design voltage */
270 entry
= strstr(buf
, "design voltage:");
271 val
= get_value(entry
);
273 info
->design_voltage
= -1;
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
);
284 /* grab the file contents */
285 buflen
= fread(buf
, sizeof(buf
), 1, 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) {
295 perr("Battery %s no longer present\n", info
->name
);
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
);
305 info
->capacity_state
= CS_ERR
;
306 else if ((strncmp(val
, "ok", 2)) == 0)
307 info
->capacity_state
= OK
;
309 info
->capacity_state
= CRITICAL
;
311 /* get charging state */
312 entry
= strstr(buf
, "charging state:");
313 val
= get_value(entry
);
315 info
->charge_state
= CH_ERR
;
316 else if ((strncmp(val
, "discharging", 10)) == 0)
317 info
->charge_state
= DISCHARGE
;
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
);
326 info
->present_rate
= -1;
329 rate
= strtoul(val
, NULL
, 10);
331 info
->present_rate
= rate
;
334 /* get remaining capacity */
335 entry
= strstr(buf
, "remaining capacity:");
336 val
= get_value(entry
);
338 info
->remaining_cap
= -1;
340 info
->remaining_cap
= strtoul(val
, NULL
, 10);
342 /* get current voltage */
343 entry
= strstr(buf
, "present voltage:");
344 val
= get_value(entry
);
346 info
->present_voltage
= -1;
348 info
->present_voltage
= strtoul(val
, NULL
, 10);
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
)
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 . . . */
378 perr("unknown percentage value\n");
383 retval
= (int)((rcap
/lfcap
) * 100.0);
384 pdebug("percent: %d\n", 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
)
401 binfo
= &batteries
[batt
];
403 if (binfo
->charge_state
== CHARGE
) {
404 if (binfo
->present_rate
== -1) {
405 perr("unknown present rate\n");
408 lfcap
= (float)binfo
->last_full_cap
;
409 rcap
= (float)binfo
->remaining_cap
;
411 charge_time
= (int)(((lfcap
- rcap
)/binfo
->present_rate
) * 60.0);
414 if (binfo
->charge_time
)
419 void acquire_batt_info(int batt
)
422 adapter_t
*ap
= &globals
->adapter
;
424 get_battery_info(batt
);
426 binfo
= &batteries
[batt
];
428 if (!binfo
->present
) {
429 binfo
->percentage
= 0;
431 binfo
->charge_time
= 0;
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
)
445 else if (binfo
->percentage
> 75)
447 else if (binfo
->percentage
> 25)
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 . . .*/
473 void acquire_all_batt_info(void)
477 for(i
= 0; i
< globals
->battery_count
; i
++)
478 acquire_batt_info(i
);
481 void acquire_global_info(void)
488 adapter_t
*ap
= &globals
->adapter
;
489 static float rate_samples
[SAMPLES
];
491 static int sample_count
= 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
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
;
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
)
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) {
528 rate
+= rate_samples
[i
];
530 rate
= rate
/(float)n
;
532 if ((rcap
< 1) || (rate
< 1)) {
538 /* time remaining in minutes */
539 rtime
= (int)((rcap
/rate
) * 60.0);
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();