16 #define PROC_DATA_SOURCE 0
17 #define SYSFS_DATA_SOURCE 1
18 static int data_source
;
21 int acpi_get_design_cap(int batt
);
23 static int read_sysfs_file(char *node
, char *prop
, char *buf
, size_t buflen
)
29 ret
= snprintf(tmp
, sizeof(tmp
), "/sys/class/power_supply/%s/%s", node
, prop
);
30 if (ret
>= (int)sizeof(tmp
)) {
31 perr("Path too long for %s/%s\n", node
, prop
);
37 perr("Could not open %s/%s\n", node
, prop
);
41 ret
= fread(buf
, 1, buflen
- 1, fp
);
46 perr("Could not read %s/%s\n", node
, prop
);
55 /* initialise the batteries */
56 static int sysfs_init_batteries(global_t
*globals
)
65 /* now enumerate batteries */
66 globals
->battery_count
= 0;
67 battdir
= opendir("/sys/class/power_supply");
68 if (battdir
== NULL
) {
69 pfatal("No batteries or ACPI not supported\n");
72 while ((batt
= readdir(battdir
))) {
73 /* there's a serious problem with this code when there's
74 * more than one battery: the readdir won't return the
75 * entries in sorted order, so battery one won't
76 * necessarily be the first one returned. So, we need
77 * to sort them ourselves before adding them to the
81 /* skip ., .. and dotfiles */
85 if (read_sysfs_file(name
, "type", ps_type
, sizeof(ps_type
)) < 0)
88 if (strncmp("Battery", ps_type
, 7) != 0)
91 names
[globals
->battery_count
] = strdup(name
);
92 globals
->battery_count
++;
96 /* A nice quick insertion sort, ala CLR. */
100 for (i
= 1; i
< globals
->battery_count
; i
++) {
103 while ((j
>= 0) && ((strcmp(tmp1
, names
[j
])) < 0)) {
105 names
[j
+1] = names
[j
];
111 for (i
= 0; i
< globals
->battery_count
; i
++) {
112 snprintf(batteries
[i
].name
, MAX_NAME
, "%s", names
[i
]);
113 pdebug("battery detected at /sys/class/power_supply/%s\n", batteries
[i
].name
);
114 pinfo("found battery %s\n", names
[i
]);
116 if (read_sysfs_file(batteries
[i
].name
, "energy_now", ps_type
, sizeof(ps_type
)) == 0)
117 batteries
[i
].sysfs_capa_mode
= SYSFS_CAPA_ENERGY
;
118 else if (read_sysfs_file(batteries
[i
].name
, "charge_now", ps_type
, sizeof(ps_type
)) == 0)
119 batteries
[i
].sysfs_capa_mode
= SYSFS_CAPA_CHARGE
;
120 else if (read_sysfs_file(batteries
[i
].name
, "capacity", ps_type
, sizeof(ps_type
)) == 0) {
121 batteries
[i
].sysfs_capa_mode
= SYSFS_CAPA_PERCENT
;
122 batteries
[i
].design_cap
= 100;
123 batteries
[i
].last_full_cap
= 100;
125 batteries
[i
].sysfs_capa_mode
= SYSFS_CAPA_ERR
;
128 /* tell user some info */
129 pdebug("%d batteries detected\n", globals
->battery_count
);
130 pinfo("libacpi: found %d batter%s\n", globals
->battery_count
,
131 (globals
->battery_count
== 1) ? "y" : "ies");
136 /* initialise the batteries */
137 static int procfs_init_batteries(global_t
*globals
)
142 char *names
[MAXBATT
];
145 /* now enumerate batteries */
146 globals
->battery_count
= 0;
147 battdir
= opendir("/proc/acpi/battery");
148 if (battdir
== NULL
) {
149 pfatal("No batteries or ACPI not supported\n");
152 while ((batt
= readdir(battdir
))) {
153 /* there's a serious problem with this code when there's
154 * more than one battery: the readdir won't return the
155 * entries in sorted order, so battery one won't
156 * necessarily be the first one returned. So, we need
157 * to sort them ourselves before adding them to the
158 * batteries array. */
162 if (!strncmp(".", name
, 1) || !strncmp("..", name
, 2))
165 names
[globals
->battery_count
] = strdup(name
);
166 globals
->battery_count
++;
170 /* A nice quick insertion sort, ala CLR. */
174 for (i
= 1; i
< globals
->battery_count
; i
++) {
177 while ((j
>= 0) && ((strcmp(tmp1
, names
[j
])) < 0)) {
179 names
[j
+1] = names
[j
];
185 for (i
= 0; i
< globals
->battery_count
; i
++) {
186 snprintf(batteries
[i
].name
, MAX_NAME
, "%s", names
[i
]);
187 snprintf(batteries
[i
].info_file
, MAX_NAME
,
188 "/proc/acpi/battery/%s/info", names
[i
]);
189 snprintf(batteries
[i
].state_file
, MAX_NAME
,
190 "/proc/acpi/battery/%s/state", names
[i
]);
191 pdebug("battery detected at %s\n", batteries
[i
].info_file
);
192 pinfo("found battery %s\n", names
[i
]);
195 /* tell user some info */
196 pdebug("%d batteries detected\n", globals
->battery_count
);
197 pinfo("libacpi: found %d batter%s\n", globals
->battery_count
,
198 (globals
->battery_count
== 1) ? "y" : "ies");
203 int init_batteries(global_t
*globals
)
205 if (data_source
== SYSFS_DATA_SOURCE
)
206 return sysfs_init_batteries(globals
);
208 return procfs_init_batteries(globals
);
211 /* a stub that just calls the current function */
212 int reinit_batteries(global_t
*globals
)
214 pdebug("reinitialising batteries\n");
215 return init_batteries(globals
);
218 /* the actual name of the subdirectory under power_supply may
219 * be anything, so we need to read the directory and use the
220 * name we find there. */
221 static int sysfs_init_ac_adapters(global_t
*globals
)
224 struct dirent
*adapter
;
225 adapter_t
*ap
= &globals
->adapter
;
229 acdir
= opendir("/sys/class/power_supply");
231 pfatal("Unable to open /sys/class/power_supply -"
232 " are you sure this system supports ACPI?\n");
236 while ((adapter
= readdir(acdir
)) != NULL
) {
237 name
= adapter
->d_name
;
239 if (name
[0] == '.') {
244 if (read_sysfs_file(name
, "type", ps_type
, sizeof(ps_type
)) < 0) {
249 if (strncmp("Mains", ps_type
, 5) == 0) {
250 pdebug("found adapter %s\n", name
);
259 perr("No AC adapter found !\n");
263 /* we'll just use the first adapter we find ... */
264 ap
->name
= strdup(name
);
265 pinfo("libacpi: found ac adapter %s\n", ap
->name
);
270 /* the actual name of the subdirectory under ac_adapter may
271 * be anything, so we need to read the directory and use the
272 * name we find there. */
273 static int procfs_init_ac_adapters(global_t
*globals
)
276 struct dirent
*adapter
;
277 adapter_t
*ap
= &globals
->adapter
;
280 acdir
= opendir("/proc/acpi/ac_adapter");
282 pfatal("Unable to open /proc/acpi/ac_adapter -"
283 " are you sure this system supports ACPI?\n");
287 while ((adapter
= readdir(acdir
)) != NULL
) {
288 name
= adapter
->d_name
;
290 if (!strncmp(".", name
, 1) || !strncmp("..", name
, 2))
292 pdebug("found adapter %s\n", name
);
295 /* we /should/ only see one filename other than . and .. so
296 * we'll just use the last value name acquires . . . */
297 ap
->name
= strdup(name
);
298 snprintf(ap
->state_file
, MAX_NAME
, "/proc/acpi/ac_adapter/%s/state",
300 pinfo("libacpi: found ac adapter %s\n", ap
->name
);
305 int init_ac_adapters(global_t
*globals
)
307 if (data_source
== SYSFS_DATA_SOURCE
)
308 return sysfs_init_ac_adapters(globals
);
310 return procfs_init_ac_adapters(globals
);
313 /* stub that does nothing but call the normal init function */
314 int reinit_ac_adapters(global_t
*globals
)
316 pdebug("reinitialising ac adapters\n");
317 return init_ac_adapters(globals
);
320 /* see if we have ACPI support and check version */
321 int power_init(global_t
*globals
)
328 unsigned int version_offset
= 0;
330 if (!(acpi
= fopen("/sys/module/acpi/parameters/acpica_version", "r"))) {
331 if (!(acpi
= fopen("/proc/acpi/info", "r"))) {
332 pfatal("This system does not support ACPI\n");
339 /* okay, now see if we got the right version */
340 buflen
= fread(buf
, 4096, 1, acpi
);
341 if (buflen
!= 4096 && ferror(acpi
)) {
342 pfatal("Could not read file\n");
345 acpi_ver
= strtol(buf
+ version_offset
, NULL
, 10);
346 pinfo("ACPI version detected: %d\n", acpi_ver
);
347 if (acpi_ver
< 20020214) {
348 pfatal("This version requires ACPI subsystem version 20020214\n");
355 /* determine data source */
356 if (access("/sys/class/power_supply", R_OK
| X_OK
) == 0) {
357 data_source
= SYSFS_DATA_SOURCE
;
358 pinfo("Selecting sysfs as the data source\n");
360 data_source
= PROC_DATA_SOURCE
;
361 pinfo("Selecting procfs as the data source\n");
364 if (!(retval
= init_batteries(globals
)))
365 retval
= init_ac_adapters(globals
);
370 /* reinitialise everything, to deal with changing batteries or ac adapters */
371 int power_reinit(global_t
*globals
)
376 if (!(acpi
= fopen("/sys/module/acpi/parameters/acpica_version", "r"))) {
377 if (!(acpi
= fopen("/proc/acpi/info", "r"))) {
378 pfatal("Could not reopen ACPI info file - does this system support ACPI?\n");
383 if (!(retval
= reinit_batteries(globals
)))
384 retval
= reinit_ac_adapters(globals
);
389 static char *get_value(char *string
)
398 while (string
[i
] != ':') i
++;
399 while (!isalnum(string
[i
])) i
++;
400 retval
= (string
+ i
);
405 static int check_error(char *buf
)
407 if(strstr(buf
, "ERROR") != NULL
)
412 static power_state_t
sysfs_get_power_status(global_t
*globals
)
415 adapter_t
*ap
= &globals
->adapter
;
417 if (read_sysfs_file(ap
->name
, "online", online
, sizeof(online
)) < 0)
426 static power_state_t
procfs_get_power_status(global_t
*globals
)
431 adapter_t
*ap
= &globals
->adapter
;
433 if ((file
= fopen(ap
->state_file
, "r")) == NULL
) {
434 snprintf(buf
, 1024, "Could not open state file %s", ap
->state_file
);
439 if (!fgets(buf
, 1024, file
)) {
440 pfatal("Could not read file\n");
444 val
= get_value(buf
);
445 if ((strncmp(val
, "on-line", 7)) == 0)
451 power_state_t
get_power_status(global_t
*globals
)
453 if (data_source
== SYSFS_DATA_SOURCE
)
454 return sysfs_get_power_status(globals
);
456 return procfs_get_power_status(globals
);
459 static int sysfs_get_battery_info(global_t
*globals
, int batt_no
)
461 battery_t
*info
= &batteries
[batt_no
];
465 /* check to see if battery is present */
466 ret
= read_sysfs_file(info
->name
, "present", buf
, sizeof(buf
));
468 /* interestingly, when the battery is not present, the whole
469 * /sys/class/power_supply/BATn directory does not exist.
470 * Yes, this is broken.
475 /* reinit batteries, this one went away and it's very
476 possible there just isn't any other one */
477 reinit_batteries(globals
);
482 info
->present
= (*buf
== '1');
483 if (!info
->present
) {
484 pinfo("Battery %s not present\n", info
->name
);
488 /* get design capacity
489 * note that all these integer values can also contain the
490 * string 'unknown', so we need to check for this. */
491 if (info
->sysfs_capa_mode
== SYSFS_CAPA_ENERGY
) {
492 if (read_sysfs_file(info
->name
, "energy_full_design", buf
, sizeof(buf
)) < 0)
493 info
->design_cap
= -1;
495 info
->design_cap
= strtoul(buf
, NULL
, 10) / 1000;
497 /* get last full capacity */
498 if (read_sysfs_file(info
->name
, "energy_full", buf
, sizeof(buf
)) < 0)
499 info
->last_full_cap
= -1;
501 info
->last_full_cap
= strtoul(buf
, NULL
, 10) / 1000;
502 } else if (info
->sysfs_capa_mode
== SYSFS_CAPA_CHARGE
) {
503 /* get design capacity */
504 if (read_sysfs_file(info
->name
, "charge_full_design", buf
, sizeof(buf
)) < 0)
505 info
->design_cap
= -1;
507 info
->design_cap
= strtoul(buf
, NULL
, 10) / 1000;
509 /* get last full capacity */
510 if (read_sysfs_file(info
->name
, "charge_full", buf
, sizeof(buf
)) < 0)
511 info
->last_full_cap
= -1;
513 info
->last_full_cap
= strtoul(buf
, NULL
, 10) / 1000;
514 } else if (info
->sysfs_capa_mode
!= SYSFS_CAPA_PERCENT
) {
515 info
->design_cap
= -1;
516 info
->last_full_cap
= -1;
520 /* get design voltage */
521 if (read_sysfs_file(info
->name
, "voltage_min_design", buf
, sizeof(buf
)) < 0)
522 info
->design_voltage
= -1;
524 info
->design_voltage
= strtoul(buf
, NULL
, 10) / 1000;
526 /* get charging state */
527 if (read_sysfs_file(info
->name
, "status", buf
, sizeof(buf
)) < 0) {
528 info
->charge_state
= CH_ERR
;
530 if (strncmp(buf
, "Unknown", 7) == 0)
531 info
->charge_state
= CH_ERR
;
532 else if (strncmp(buf
, "Discharging", 11) == 0)
533 info
->charge_state
= DISCHARGE
;
534 else if (strncmp(buf
, "Charging", 8) == 0)
535 info
->charge_state
= CHARGE
;
536 else if (strncmp(buf
, "Not charging", 12) == 0)
537 info
->charge_state
= NO_CHARGE
;
538 else if (strncmp(buf
, "Full", 4) == 0)
539 info
->charge_state
= FULL
; /* DISCHARGE ? as per old comment ... */
542 /* get current rate of burn
543 * note that if it's on AC, this will report 0 */
544 if (read_sysfs_file(info
->name
, "current_now", buf
, sizeof(buf
)) < 0)
545 info
->present_rate
= -1;
548 rate
= strtoul(buf
, NULL
, 10) / 1000;
549 info
->present_rate
= (rate
!= 0) ? rate
: info
->present_rate
;
552 if (info
->sysfs_capa_mode
== SYSFS_CAPA_ENERGY
) {
553 /* get remaining capacity */
554 if (read_sysfs_file(info
->name
, "energy_now", buf
, sizeof(buf
)) < 0)
555 info
->remaining_cap
= -1;
557 info
->remaining_cap
= strtoul(buf
, NULL
, 10) / 1000;
559 } else if (info
->sysfs_capa_mode
== SYSFS_CAPA_CHARGE
) {
560 /* get remaining capacity */
561 if (read_sysfs_file(info
->name
, "charge_now", buf
, sizeof(buf
)) < 0)
562 info
->remaining_cap
= -1;
564 info
->remaining_cap
= strtoul(buf
, NULL
, 10) / 1000;
565 } else if (info
->sysfs_capa_mode
== SYSFS_CAPA_PERCENT
) {
566 /* get remaining capacity */
567 if (read_sysfs_file(info
->name
, "capacity", buf
, sizeof(buf
)) < 0)
568 info
->remaining_cap
= -1;
570 info
->remaining_cap
= strtoul(buf
, NULL
, 10) / 1000;
572 info
->remaining_cap
= -1;
575 /* get current voltage */
576 if (read_sysfs_file(info
->name
, "voltage_now", buf
, sizeof(buf
)) < 0)
577 info
->present_voltage
= -1;
579 info
->present_voltage
= strtoul(buf
, NULL
, 10) / 1000;
584 static int procfs_get_battery_info(global_t
*globals
, int batt_no
)
587 battery_t
*info
= &batteries
[batt_no
];
593 globals
= globals
; /* silencing a warning */
595 if ((file
= fopen(info
->info_file
, "r")) == NULL
) {
596 /* this is cheating, but string concatenation should work . . . */
597 pfatal("Could not open %s:", info
->info_file
);
602 /* grab the contents of the file */
603 buflen
= fread(buf
, sizeof(buf
), 1, file
);
606 /* check to see if there were any errors reported in the file */
607 if(check_error(buf
)) {
608 pinfo("Error reported in file %s - discarding data\n",
613 /* check to see if battery is present */
614 entry
= strstr(buf
, "present:");
615 val
= get_value(entry
);
616 if ((strncmp(val
, "yes", 3)) == 0) {
619 pinfo("Battery %s not present\n", info
->name
);
624 /* get design capacity
625 * note that all these integer values can also contain the
626 * string 'unknown', so we need to check for this. */
627 entry
= strstr(buf
, "design capacity:");
628 val
= get_value(entry
);
630 info
->design_cap
= -1;
632 info
->design_cap
= strtoul(val
, NULL
, 10);
634 /* get last full capacity */
635 entry
= strstr(buf
, "last full capacity:");
636 val
= get_value(entry
);
638 info
->last_full_cap
= -1;
640 info
->last_full_cap
= strtoul(val
, NULL
, 10);
642 /* get design voltage */
643 entry
= strstr(buf
, "design voltage:");
644 val
= get_value(entry
);
646 info
->design_voltage
= -1;
648 info
->design_voltage
= strtoul(val
, NULL
, 10);
651 if ((file
= fopen(info
->state_file
, "r")) == NULL
) {
652 perr("Could not open %s:", info
->state_file
);
657 /* grab the file contents */
658 memset(buf
, 0, sizeof(buf
));
659 buflen
= fread(buf
, sizeof(buf
), 1, file
);
660 if (buflen
!= sizeof(buf
) && ferror(file
)) {
661 pfatal("Could not read file\n");
666 /* check to see if there were any errors reported in the file */
667 if(check_error(buf
)) {
668 pinfo("Error reported in file %s - discarding data\n",
672 /* check to see if battery is present */
673 entry
= strstr(buf
, "present:");
674 val
= get_value(entry
);
675 if ((strncmp(val
, "yes", 3)) == 0) {
679 perr("Battery %s no longer present\n", info
->name
);
683 /* get charging state */
684 entry
= strstr(buf
, "charging state:");
685 val
= get_value(entry
);
687 info
->charge_state
= CH_ERR
;
688 else if ((strncmp(val
, "discharging", 10)) == 0)
689 info
->charge_state
= DISCHARGE
;
690 else if ((strncmp(val
, "charged", 7)) == 0)
691 /* this is a workaround for machines that report
692 * their charge state as 'charged', rather than
693 * what my laptop does, which is go straight to
694 * 'discharging'. dunno which matches the standard */
695 info
->charge_state
= DISCHARGE
;
697 info
->charge_state
= CHARGE
;
699 /* get current rate of burn
700 * note that if it's on AC, this will report 0 */
701 entry
= strstr(buf
, "present rate:");
702 val
= get_value(entry
);
704 info
->present_rate
= -1;
707 rate
= strtoul(val
, NULL
, 10);
709 info
->present_rate
= rate
;
712 /* get remaining capacity */
713 entry
= strstr(buf
, "remaining capacity:");
714 val
= get_value(entry
);
716 info
->remaining_cap
= -1;
718 info
->remaining_cap
= strtoul(val
, NULL
, 10);
720 /* get current voltage */
721 entry
= strstr(buf
, "present voltage:");
722 val
= get_value(entry
);
724 info
->present_voltage
= -1;
726 info
->present_voltage
= strtoul(val
, NULL
, 10);
731 int get_battery_info(global_t
*globals
, int batt_no
)
733 if (data_source
== SYSFS_DATA_SOURCE
)
734 return sysfs_get_battery_info(globals
, batt_no
);
736 return procfs_get_battery_info(globals
, batt_no
);
741 * In order to make this code more convenient for things other than
742 * just plain old wmacpi-ng I'm breaking the basic functionality
743 * up into several chunks: collecting and collating info for a
744 * single battery, calculating the global info (such as rtime), and
745 * some stuff to provide a similar interface to now.
748 /* calculate the percentage remaining, using the values of
749 * remaining capacity and last full capacity, as outlined in
750 * the ACPI spec v2.0a, section 3.9.3. */
751 static int calc_remaining_percentage(int batt
)
757 binfo
= &batteries
[batt
];
759 rcap
= (float)binfo
->remaining_cap
;
760 lfcap
= (float)binfo
->last_full_cap
;
762 /* we use -1 to indicate that the value is unknown . . . */
764 perr("unknown percentage value\n");
769 retval
= (int)((rcap
/lfcap
) * 100.0);
770 pdebug("percent: %d\n", retval
);
775 /* check to see if we've been getting bad data from the batteries - if
776 * we get more than some limit we switch to using the remaining capacity
777 * for the calculations. */
778 static enum rtime_mode
check_rt_mode(global_t
*globals
)
784 /* if we were told what to do, we should keep doing it */
785 if(globals
->rt_forced
)
786 return globals
->rt_mode
;
788 for(i
= 0; i
< MAXBATT
; i
++) {
789 binfo
= &batteries
[i
];
790 if(binfo
->present
&& globals
->adapter
.power
== BATT
) {
791 if(binfo
->present_rate
<= 0) {
792 pdebug("Bad report from %s\n", binfo
->name
);
797 for(i
= 0; i
< MAXBATT
; i
++) {
798 binfo
= &batteries
[i
];
799 if(binfo
->bad_count
> bad_limit
) {
800 if(globals
->rt_mode
!= RT_CAP
)
801 pinfo("More than %d bad reports from %s; "
802 "Switching to remaining capacity mode\n",
803 bad_limit
, binfo
->name
);
810 /* calculate remaining time until the battery is charged.
811 * when charging, the battery state file reports the
812 * current being used to charge the battery. We can use
813 * this and the remaining capacity to work out how long
814 * until it reaches the last full capacity of the battery.
815 * XXX: make sure this is actually portable . . . */
816 static int calc_charge_time_rate(int batt
)
822 binfo
= &batteries
[batt
];
824 if (binfo
->charge_state
== CHARGE
) {
825 if (binfo
->present_rate
== -1) {
826 perr("unknown present rate\n");
829 lfcap
= (float)binfo
->last_full_cap
;
830 rcap
= (float)binfo
->remaining_cap
;
832 charge_time
= (int)(((lfcap
- rcap
)/binfo
->present_rate
) * 60.0);
835 if (binfo
->charge_time
)
840 /* we need to calculate the present rate the same way we do in rt_cap
841 * mode, and then use that to estimate charge time. This will
842 * necessarily be even less accurate than it is for remaining time, but
843 * it's just as neessary . . . */
844 static int calc_charge_time_cap(int batt
)
846 static float cap_samples
[CAP_SAMPLES
];
847 static int time_samples
[CAP_SAMPLES
];
848 static int sample_count
= 0;
849 static int current
= 0;
855 battery_t
*binfo
= &batteries
[batt
];
857 cap_samples
[current
] = (float) binfo
->remaining_cap
;
858 time_samples
[current
] = time(NULL
);
860 if (sample_count
== 0) {
861 /* we can't do much if we don't have any data . . . */
863 } else if (sample_count
< CAP_SAMPLES
) {
864 /* if we have less than SAMPLES samples so far, we use the first
865 * sample and the current one */
866 cdiff
= cap_samples
[current
] - cap_samples
[0];
867 tdiff
= time_samples
[current
] - time_samples
[0];
868 current_rate
= cdiff
/tdiff
;
870 /* if we have more than SAMPLES samples, we use the oldest
871 * current one, which at this point is current + 1. This will
872 * wrap the same way that current will wrap, but one cycle
874 cdiff
= cap_samples
[current
] - cap_samples
[old
];
875 tdiff
= time_samples
[current
] - time_samples
[old
];
876 current_rate
= cdiff
/(float)tdiff
;
878 if (current_rate
== 0)
881 float cap_left
= (float)(binfo
->last_full_cap
- binfo
->remaining_cap
);
882 rtime
= (int)(cap_left
/(current_rate
* 60.0));
884 sample_count
++, current
++, old
++;
885 if (current
>= CAP_SAMPLES
)
887 if (old
>= CAP_SAMPLES
)
890 pdebug("cap charge time rem: %d\n", rtime
);
894 static int calc_charge_time(global_t
*globals
, int batt
)
898 globals
->rt_mode
= check_rt_mode(globals
);
900 switch(globals
->rt_mode
) {
902 ctime
= calc_charge_time_rate(batt
);
905 ctime
= calc_charge_time_cap(batt
);
911 void acquire_batt_info(global_t
*globals
, int batt
)
914 adapter_t
*ap
= &globals
->adapter
;
916 get_battery_info(globals
, batt
);
918 binfo
= &batteries
[batt
];
920 if (!binfo
->present
) {
921 binfo
->percentage
= 0;
923 binfo
->charge_time
= 0;
928 binfo
->percentage
= calc_remaining_percentage(batt
);
930 /* set the battery's capacity state, based (at present) on some
931 * guesstimated values: more than 75% == HIGH, 25% to 75% MED, and
932 * less than 25% is LOW. Less than globals->crit_level is CRIT. */
933 if (binfo
->percentage
== -1)
934 binfo
->state
= BS_ERR
;
935 if (binfo
->percentage
< globals
->crit_level
)
937 else if (binfo
->percentage
> 75)
939 else if (binfo
->percentage
> 25)
944 /* we need to /know/ that we've got a valid state for the
945 * globals->power value . . . .*/
946 ap
->power
= get_power_status(globals
);
948 binfo
->charge_time
= calc_charge_time(globals
, batt
);
950 /* and finally, we tell anyone who wants to use this information
951 * that it's now valid . . .*/
955 void acquire_all_batt_info(global_t
*globals
)
959 for(i
= 0; i
< globals
->battery_count
; i
++)
960 acquire_batt_info(globals
, i
);
964 * One of the feature requests I've had is for some way to deal with
965 * batteries that are too dumb or too b0rken to report a present rate
966 * value. The way to do this, obviously, is to record the time that
967 * samples were taken and use that information to calculate the rate
968 * at which the battery is draining/charging. This still won't help
969 * systems where the battery doesn't even report the remaining
970 * capacity, but without the present rate or the remaining capacity, I
971 * don't think there's /anything/ we can do to work around it.
973 * So, what we need to do is provide a way to use a different method
974 * to calculate the time remaining. What seems most sensible is to
975 * split out the code to calculate it into a seperate function, and
976 * then provide multiple implementations . . .
980 * the default implementation - if present rate and remaining capacity
981 * are both reported correctly, we use them.
983 int calc_time_remaining_rate(global_t
*globals
)
990 static float rate_samples
[SAMPLES
];
991 static int sample_count
= 0;
995 /* calculate the time remaining, using the battery's remaining
996 * capacity and the reported burn rate (3.9.3).
997 * For added accuracy, we average the value over the last
998 * SAMPLES number of calls, or for anything less than this we
999 * simply report the raw number. */
1000 /* XXX: this needs to correctly handle the case where
1001 * any of the values used is unknown (which we flag using
1003 for (i
= 0; i
< globals
->battery_count
; i
++) {
1004 binfo
= &batteries
[i
];
1005 if (binfo
->present
&& binfo
->valid
) {
1006 rcap
+= (float)binfo
->remaining_cap
;
1007 rate
+= (float)binfo
->present_rate
;
1010 rate_samples
[j
] = rate
;
1011 j
++, sample_count
++;
1015 /* for the first SAMPLES number of calls we calculate the
1016 * average based on sample_count, then we use SAMPLES to
1017 * calculate the rolling average. */
1019 /* when this fails, n should be equal to SAMPLES. */
1020 if (sample_count
< SAMPLES
)
1022 for (i
= 0, rate
= 0; i
< n
; i
++) {
1023 /* if any of our samples are invalid, we drop
1024 * straight out, and flag our unknown values. */
1025 if (rate_samples
[i
] < 0) {
1030 rate
+= rate_samples
[i
];
1032 rate
= rate
/(float)n
;
1034 if ((rcap
< 1) || (rate
< 1)) {
1040 /* time remaining in minutes */
1041 rtime
= (int)((rcap
/rate
) * 60.0);
1045 pdebug("discharge time rem: %d\n", rtime
);
1050 * the alternative implementation - record the time at which each
1051 * sample was taken, and then use the difference between the latest
1052 * sample and the one SAMPLES ago to calculate the difference over
1053 * that time, and from there the rate of change of capacity.
1055 * XXX: this code sucks, but largely because batteries aren't exactly
1056 * precision instruments - mine only report with about 70mAH
1057 * resolution, so they don't report any changes until the difference
1058 * is 70mAH. This means that calculating the current rate from the
1059 * remaining capacity is very choppy . . .
1061 * To fix this, we should calculate an average over some number of
1062 * samples at the old end of the set - this would smooth out the
1065 int calc_time_remaining_cap(global_t
*globals
)
1067 static float cap_samples
[CAP_SAMPLES
];
1068 static int time_samples
[CAP_SAMPLES
];
1069 static int sample_count
= 0;
1070 static int current
= 0;
1080 for (i
= 0; i
< globals
->battery_count
; i
++) {
1081 binfo
= &batteries
[i
];
1082 if (binfo
->present
&& binfo
->valid
)
1083 cap
+= binfo
->remaining_cap
;
1085 cap_samples
[current
] = cap
;
1086 time_samples
[current
] = time(NULL
);
1088 if (sample_count
== 0) {
1089 /* we can't do much if we don't have any data . . . */
1091 } else if (sample_count
< CAP_SAMPLES
) {
1092 /* if we have less than SAMPLES samples so far, we use the first
1093 * sample and the current one */
1094 cdiff
= cap_samples
[0] - cap_samples
[current
];
1095 tdiff
= time_samples
[current
] - time_samples
[0];
1096 current_rate
= cdiff
/tdiff
;
1098 /* if we have more than SAMPLES samples, we use the oldest
1099 * current one, which at this point is current + 1. This will
1100 * wrap the same way that current will wrap, but one cycle
1102 cdiff
= cap_samples
[old
] - cap_samples
[current
];
1103 tdiff
= time_samples
[current
] - time_samples
[old
];
1104 current_rate
= cdiff
/tdiff
;
1106 if (current_rate
== 0)
1109 rtime
= (int)(cap_samples
[current
]/(current_rate
* 60.0));
1111 sample_count
++, current
++, old
++;
1112 if (current
>= CAP_SAMPLES
)
1114 if (old
>= CAP_SAMPLES
)
1117 pdebug("cap discharge time rem: %d\n", rtime
);
1121 void acquire_global_info(global_t
*globals
)
1123 adapter_t
*ap
= &globals
->adapter
;
1125 globals
->rt_mode
= check_rt_mode(globals
);
1127 switch(globals
->rt_mode
) {
1129 globals
->rtime
= calc_time_remaining_rate(globals
);
1132 globals
->rtime
= calc_time_remaining_cap(globals
);
1136 /* get the power status.
1137 * note that this is actually reported seperately from the
1138 * battery info, under /proc/acpi/ac_adapter/AC/state */
1139 ap
->power
= get_power_status(globals
);
1142 void acquire_all_info(global_t
*globals
)
1144 acquire_all_batt_info(globals
);
1145 acquire_global_info(globals
);