16 battery_t batteries
[MAXBATT
];
19 #define PROC_DATA_SOURCE 0
20 #define SYSFS_DATA_SOURCE 1
21 static int data_source
;
24 int acpi_get_design_cap(int batt
);
27 cmpstr (const void *va
, const void *vb
)
29 const char *a
= *(const char **) va
;
30 const char *b
= *(const char **) vb
;
35 static int read_sysfs_file(char *node
, char *prop
, char *buf
, size_t buflen
)
41 ret
= snprintf(tmp
, sizeof(tmp
), "/sys/class/power_supply/%s/%s", node
, prop
);
42 if (ret
>= (int)sizeof(tmp
)) {
43 perr("Path too long for %s/%s\n", node
, prop
);
49 perr("Could not open %s/%s\n", node
, prop
);
53 ret
= fread(buf
, 1, buflen
- 1, fp
);
58 perr("Could not read %s/%s\n", node
, prop
);
67 /* initialise the batteries */
68 static int sysfs_init_batteries(global_t
*globals
)
77 /* now enumerate batteries */
78 globals
->battery_count
= 0;
79 battdir
= opendir("/sys/class/power_supply");
80 if (battdir
== NULL
) {
81 pfatal("No batteries or ACPI not supported\n");
84 while ((batt
= readdir(battdir
))) {
85 /* there's a serious problem with this code when there's
86 * more than one battery: the readdir won't return the
87 * entries in sorted order, so battery one won't
88 * necessarily be the first one returned. So, we need
89 * to sort them ourselves before adding them to the
93 /* skip ., .. and dotfiles */
97 if (read_sysfs_file(name
, "type", ps_type
, sizeof(ps_type
)) < 0)
100 if (strncmp("Battery", ps_type
, 7) != 0)
103 names
[globals
->battery_count
] = strdup(name
);
104 globals
->battery_count
++;
108 qsort(names
, globals
->battery_count
, sizeof *names
, cmpstr
);
110 for (i
= 0; i
< globals
->battery_count
; i
++) {
111 snprintf(batteries
[i
].name
, MAX_NAME
, "%s", names
[i
]);
112 pdebug("battery detected at /sys/class/power_supply/%s\n", batteries
[i
].name
);
113 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 qsort(names
, globals
->battery_count
, sizeof *names
, cmpstr
);
172 for (i
= 0; i
< globals
->battery_count
; i
++) {
173 snprintf(batteries
[i
].name
, MAX_NAME
, "%s", names
[i
]);
174 snprintf(batteries
[i
].info_file
, MAX_NAME
,
175 "/proc/acpi/battery/%s/info", names
[i
]);
176 snprintf(batteries
[i
].state_file
, MAX_NAME
,
177 "/proc/acpi/battery/%s/state", names
[i
]);
178 pdebug("battery detected at %s\n", batteries
[i
].info_file
);
179 pinfo("found battery %s\n", names
[i
]);
183 /* tell user some info */
184 pdebug("%d batteries detected\n", globals
->battery_count
);
185 pinfo("libacpi: found %d batter%s\n", globals
->battery_count
,
186 (globals
->battery_count
== 1) ? "y" : "ies");
191 int init_batteries(global_t
*globals
)
193 if (data_source
== SYSFS_DATA_SOURCE
)
194 return sysfs_init_batteries(globals
);
196 return procfs_init_batteries(globals
);
199 /* a stub that just calls the current function */
200 int reinit_batteries(global_t
*globals
)
202 pdebug("reinitialising batteries\n");
203 return init_batteries(globals
);
206 /* the actual name of the subdirectory under power_supply may
207 * be anything, so we need to read the directory and use the
208 * name we find there. */
209 static int sysfs_init_ac_adapters(global_t
*globals
)
212 struct dirent
*adapter
;
213 adapter_t
*ap
= &globals
->adapter
;
217 acdir
= opendir("/sys/class/power_supply");
219 pfatal("Unable to open /sys/class/power_supply -"
220 " are you sure this system supports ACPI?\n");
224 while ((adapter
= readdir(acdir
)) != NULL
) {
226 if (adapter
->d_name
[0] == '.') {
230 if (read_sysfs_file(adapter
->d_name
, "type", ps_type
, sizeof(ps_type
)) < 0) {
234 if (strncmp("Mains", ps_type
, 5) == 0) {
235 pdebug("found adapter %s\n", adapter
->d_name
);
236 name
= strdup(adapter
->d_name
);
243 perr("No AC adapter found !\n");
247 /* we'll just use the first adapter we find ... */
249 pinfo("libacpi: found ac adapter %s\n", ap
->name
);
254 /* the actual name of the subdirectory under ac_adapter may
255 * be anything, so we need to read the directory and use the
256 * name we find there. */
257 static int procfs_init_ac_adapters(global_t
*globals
)
260 struct dirent
*adapter
;
261 adapter_t
*ap
= &globals
->adapter
;
264 acdir
= opendir("/proc/acpi/ac_adapter");
266 pfatal("Unable to open /proc/acpi/ac_adapter -"
267 " are you sure this system supports ACPI?\n");
271 while ((adapter
= readdir(acdir
)) != NULL
) {
272 name
= adapter
->d_name
;
274 if (!strncmp(".", name
, 1) || !strncmp("..", name
, 2))
276 pdebug("found adapter %s\n", name
);
278 ap
->name
= strdup(name
);
280 /* we /should/ only see one filename other than . and .. so
281 * we'll just use the last value name acquires . . . */
282 snprintf(ap
->state_file
, MAX_NAME
, "/proc/acpi/ac_adapter/%s/state",
284 pinfo("libacpi: found ac adapter %s\n", ap
->name
);
289 int init_ac_adapters(global_t
*globals
)
291 if (data_source
== SYSFS_DATA_SOURCE
)
292 return sysfs_init_ac_adapters(globals
);
294 return procfs_init_ac_adapters(globals
);
297 /* stub that does nothing but call the normal init function */
298 int reinit_ac_adapters(global_t
*globals
)
300 pdebug("reinitialising ac adapters\n");
301 return init_ac_adapters(globals
);
304 /* see if we have ACPI support and check version */
305 int power_init(global_t
*globals
)
312 unsigned int version_offset
= 0;
314 if (!(acpi
= fopen("/sys/module/acpi/parameters/acpica_version", "r"))) {
315 if (!(acpi
= fopen("/proc/acpi/info", "r"))) {
316 pfatal("This system does not support ACPI\n");
323 /* okay, now see if we got the right version */
324 buflen
= fread(buf
, 4096, 1, acpi
);
325 if (buflen
!= 4096 && ferror(acpi
)) {
326 pfatal("Could not read file\n");
329 acpi_ver
= strtol(buf
+ version_offset
, NULL
, 10);
330 pinfo("ACPI version detected: %d\n", acpi_ver
);
331 if (acpi_ver
< 20020214) {
332 pfatal("This version requires ACPI subsystem version 20020214\n");
339 /* determine data source */
340 if (access("/sys/class/power_supply", R_OK
| X_OK
) == 0) {
341 data_source
= SYSFS_DATA_SOURCE
;
342 pinfo("Selecting sysfs as the data source\n");
344 data_source
= PROC_DATA_SOURCE
;
345 pinfo("Selecting procfs as the data source\n");
348 if (!(retval
= init_batteries(globals
)))
349 retval
= init_ac_adapters(globals
);
354 /* reinitialise everything, to deal with changing batteries or ac adapters */
355 int power_reinit(global_t
*globals
)
360 if (!(acpi
= fopen("/sys/module/acpi/parameters/acpica_version", "r"))) {
361 if (!(acpi
= fopen("/proc/acpi/info", "r"))) {
362 pfatal("Could not reopen ACPI info file - does this system support ACPI?\n");
367 if (!(retval
= reinit_batteries(globals
)))
368 retval
= reinit_ac_adapters(globals
);
374 static char *get_value(char *string
)
383 while (string
[i
] != ':') i
++;
384 while (!isalnum(string
[i
])) i
++;
385 retval
= (string
+ i
);
390 static int check_error(char *buf
)
392 if(strstr(buf
, "ERROR") != NULL
)
397 static power_state_t
sysfs_get_power_status(global_t
*globals
)
400 adapter_t
*ap
= &globals
->adapter
;
402 if (read_sysfs_file(ap
->name
, "online", online
, sizeof(online
)) < 0)
411 static power_state_t
procfs_get_power_status(global_t
*globals
)
416 adapter_t
*ap
= &globals
->adapter
;
418 if ((file
= fopen(ap
->state_file
, "r")) == NULL
) {
419 snprintf(buf
, 1024, "Could not open state file %s", ap
->state_file
);
424 if (!fgets(buf
, 1024, file
)) {
425 pfatal("Could not read file\n");
429 val
= get_value(buf
);
430 if ((strncmp(val
, "on-line", 7)) == 0)
436 power_state_t
get_power_status(global_t
*globals
)
438 if (data_source
== SYSFS_DATA_SOURCE
)
439 return sysfs_get_power_status(globals
);
441 return procfs_get_power_status(globals
);
444 static int sysfs_get_battery_info(global_t
*globals
, int batt_no
)
446 battery_t
*info
= &batteries
[batt_no
];
450 /* check to see if battery is present */
451 ret
= read_sysfs_file(info
->name
, "present", buf
, sizeof(buf
));
453 /* interestingly, when the battery is not present, the whole
454 * /sys/class/power_supply/BATn directory does not exist.
455 * Yes, this is broken.
460 /* reinit batteries, this one went away and it's very
461 possible there just isn't any other one */
462 reinit_batteries(globals
);
467 info
->present
= (*buf
== '1');
468 if (!info
->present
) {
469 pinfo("Battery %s not present\n", info
->name
);
473 /* get design capacity
474 * note that all these integer values can also contain the
475 * string 'unknown', so we need to check for this. */
476 if (info
->sysfs_capa_mode
== SYSFS_CAPA_ENERGY
) {
477 if (read_sysfs_file(info
->name
, "energy_full_design", buf
, sizeof(buf
)) < 0)
478 info
->design_cap
= -1;
480 info
->design_cap
= strtoul(buf
, NULL
, 10) / 1000;
482 /* get last full capacity */
483 if (read_sysfs_file(info
->name
, "energy_full", buf
, sizeof(buf
)) < 0)
484 info
->last_full_cap
= -1;
486 info
->last_full_cap
= strtoul(buf
, NULL
, 10) / 1000;
487 } else if (info
->sysfs_capa_mode
== SYSFS_CAPA_CHARGE
) {
488 /* get design capacity */
489 if (read_sysfs_file(info
->name
, "charge_full_design", buf
, sizeof(buf
)) < 0)
490 info
->design_cap
= -1;
492 info
->design_cap
= strtoul(buf
, NULL
, 10) / 1000;
494 /* get last full capacity */
495 if (read_sysfs_file(info
->name
, "charge_full", buf
, sizeof(buf
)) < 0)
496 info
->last_full_cap
= -1;
498 info
->last_full_cap
= strtoul(buf
, NULL
, 10) / 1000;
499 } else if (info
->sysfs_capa_mode
!= SYSFS_CAPA_PERCENT
) {
500 info
->design_cap
= -1;
501 info
->last_full_cap
= -1;
505 /* get design voltage */
506 if (read_sysfs_file(info
->name
, "voltage_min_design", buf
, sizeof(buf
)) < 0)
507 info
->design_voltage
= -1;
509 info
->design_voltage
= strtoul(buf
, NULL
, 10) / 1000;
511 /* get charging state */
512 if (read_sysfs_file(info
->name
, "status", buf
, sizeof(buf
)) < 0) {
513 info
->charge_state
= CH_ERR
;
515 if (strncmp(buf
, "Unknown", 7) == 0)
516 info
->charge_state
= CH_ERR
;
517 else if (strncmp(buf
, "Discharging", 11) == 0)
518 info
->charge_state
= DISCHARGE
;
519 else if (strncmp(buf
, "Charging", 8) == 0)
520 info
->charge_state
= CHARGE
;
521 else if (strncmp(buf
, "Not charging", 12) == 0)
522 info
->charge_state
= NO_CHARGE
;
523 else if (strncmp(buf
, "Full", 4) == 0)
524 info
->charge_state
= FULL
; /* DISCHARGE ? as per old comment ... */
527 /* get current rate of burn
528 * note that if it's on AC, this will report 0 */
529 if (read_sysfs_file(info
->name
, "current_now", buf
, sizeof(buf
)) < 0)
530 info
->present_rate
= -1;
533 rate
= strtoul(buf
, NULL
, 10) / 1000;
534 info
->present_rate
= (rate
!= 0) ? rate
: info
->present_rate
;
537 if (info
->sysfs_capa_mode
== SYSFS_CAPA_ENERGY
) {
538 /* get remaining capacity */
539 if (read_sysfs_file(info
->name
, "energy_now", buf
, sizeof(buf
)) < 0)
540 info
->remaining_cap
= -1;
542 info
->remaining_cap
= strtoul(buf
, NULL
, 10) / 1000;
544 } else if (info
->sysfs_capa_mode
== SYSFS_CAPA_CHARGE
) {
545 /* get remaining capacity */
546 if (read_sysfs_file(info
->name
, "charge_now", buf
, sizeof(buf
)) < 0)
547 info
->remaining_cap
= -1;
549 info
->remaining_cap
= strtoul(buf
, NULL
, 10) / 1000;
550 } else if (info
->sysfs_capa_mode
== SYSFS_CAPA_PERCENT
) {
551 /* get remaining capacity */
552 if (read_sysfs_file(info
->name
, "capacity", buf
, sizeof(buf
)) < 0)
553 info
->remaining_cap
= -1;
555 info
->remaining_cap
= strtoul(buf
, NULL
, 10) / 1000;
557 info
->remaining_cap
= -1;
560 /* get current voltage */
561 if (read_sysfs_file(info
->name
, "voltage_now", buf
, sizeof(buf
)) < 0)
562 info
->present_voltage
= -1;
564 info
->present_voltage
= strtoul(buf
, NULL
, 10) / 1000;
569 static int procfs_get_battery_info(global_t
*globals
, int batt_no
)
572 battery_t
*info
= &batteries
[batt_no
];
578 globals
= globals
; /* silencing a warning */
580 if ((file
= fopen(info
->info_file
, "r")) == NULL
) {
581 /* this is cheating, but string concatenation should work . . . */
582 pfatal("Could not open %s:", info
->info_file
);
587 /* grab the contents of the file */
588 buflen
= fread(buf
, sizeof(buf
), 1, file
);
591 /* check to see if there were any errors reported in the file */
592 if(check_error(buf
)) {
593 pinfo("Error reported in file %s - discarding data\n",
598 /* check to see if battery is present */
599 entry
= strstr(buf
, "present:");
600 val
= get_value(entry
);
601 if ((strncmp(val
, "yes", 3)) == 0) {
604 pinfo("Battery %s not present\n", info
->name
);
609 /* get design capacity
610 * note that all these integer values can also contain the
611 * string 'unknown', so we need to check for this. */
612 entry
= strstr(buf
, "design capacity:");
613 val
= get_value(entry
);
615 info
->design_cap
= -1;
617 info
->design_cap
= strtoul(val
, NULL
, 10);
619 /* get last full capacity */
620 entry
= strstr(buf
, "last full capacity:");
621 val
= get_value(entry
);
623 info
->last_full_cap
= -1;
625 info
->last_full_cap
= strtoul(val
, NULL
, 10);
627 /* get design voltage */
628 entry
= strstr(buf
, "design voltage:");
629 val
= get_value(entry
);
631 info
->design_voltage
= -1;
633 info
->design_voltage
= strtoul(val
, NULL
, 10);
636 if ((file
= fopen(info
->state_file
, "r")) == NULL
) {
637 perr("Could not open %s:", info
->state_file
);
642 /* grab the file contents */
643 memset(buf
, 0, sizeof(buf
));
644 buflen
= fread(buf
, sizeof(buf
), 1, file
);
645 if (buflen
!= sizeof(buf
) && ferror(file
)) {
646 pfatal("Could not read file\n");
651 /* check to see if there were any errors reported in the file */
652 if(check_error(buf
)) {
653 pinfo("Error reported in file %s - discarding data\n",
657 /* check to see if battery is present */
658 entry
= strstr(buf
, "present:");
659 val
= get_value(entry
);
660 if ((strncmp(val
, "yes", 3)) == 0) {
664 perr("Battery %s no longer present\n", info
->name
);
668 /* get charging state */
669 entry
= strstr(buf
, "charging state:");
670 val
= get_value(entry
);
672 info
->charge_state
= CH_ERR
;
673 else if ((strncmp(val
, "discharging", 10)) == 0)
674 info
->charge_state
= DISCHARGE
;
675 else if ((strncmp(val
, "charged", 7)) == 0)
676 /* this is a workaround for machines that report
677 * their charge state as 'charged', rather than
678 * what my laptop does, which is go straight to
679 * 'discharging'. dunno which matches the standard */
680 info
->charge_state
= DISCHARGE
;
682 info
->charge_state
= CHARGE
;
684 /* get current rate of burn
685 * note that if it's on AC, this will report 0 */
686 entry
= strstr(buf
, "present rate:");
687 val
= get_value(entry
);
689 info
->present_rate
= -1;
692 rate
= strtoul(val
, NULL
, 10);
694 info
->present_rate
= rate
;
697 /* get remaining capacity */
698 entry
= strstr(buf
, "remaining capacity:");
699 val
= get_value(entry
);
701 info
->remaining_cap
= -1;
703 info
->remaining_cap
= strtoul(val
, NULL
, 10);
705 /* get current voltage */
706 entry
= strstr(buf
, "present voltage:");
707 val
= get_value(entry
);
709 info
->present_voltage
= -1;
711 info
->present_voltage
= strtoul(val
, NULL
, 10);
716 int get_battery_info(global_t
*globals
, int batt_no
)
718 if (data_source
== SYSFS_DATA_SOURCE
)
719 return sysfs_get_battery_info(globals
, batt_no
);
721 return procfs_get_battery_info(globals
, batt_no
);
726 * In order to make this code more convenient for things other than
727 * just plain old wmacpi-ng I'm breaking the basic functionality
728 * up into several chunks: collecting and collating info for a
729 * single battery, calculating the global info (such as rtime), and
730 * some stuff to provide a similar interface to now.
733 /* calculate the percentage remaining, using the values of
734 * remaining capacity and last full capacity, as outlined in
735 * the ACPI spec v2.0a, section 3.9.3. */
736 static int calc_remaining_percentage(int batt
)
742 binfo
= &batteries
[batt
];
744 rcap
= (float)binfo
->remaining_cap
;
745 lfcap
= (float)binfo
->last_full_cap
;
747 /* we use -1 to indicate that the value is unknown . . . */
749 perr("unknown percentage value\n");
754 retval
= (int)((rcap
/lfcap
) * 100.0);
755 pdebug("percent: %d\n", retval
);
760 /* check to see if we've been getting bad data from the batteries - if
761 * we get more than some limit we switch to using the remaining capacity
762 * for the calculations. */
763 static enum rtime_mode
check_rt_mode(global_t
*globals
)
769 /* if we were told what to do, we should keep doing it */
770 if(globals
->rt_forced
)
771 return globals
->rt_mode
;
773 for(i
= 0; i
< MAXBATT
; i
++) {
774 binfo
= &batteries
[i
];
775 if(binfo
->present
&& globals
->adapter
.power
== BATT
) {
776 if(binfo
->present_rate
<= 0) {
777 pdebug("Bad report from %s\n", binfo
->name
);
782 for(i
= 0; i
< MAXBATT
; i
++) {
783 binfo
= &batteries
[i
];
784 if(binfo
->bad_count
> bad_limit
) {
785 if(globals
->rt_mode
!= RT_CAP
)
786 pinfo("More than %d bad reports from %s; "
787 "Switching to remaining capacity mode\n",
788 bad_limit
, binfo
->name
);
795 /* calculate remaining time until the battery is charged.
796 * when charging, the battery state file reports the
797 * current being used to charge the battery. We can use
798 * this and the remaining capacity to work out how long
799 * until it reaches the last full capacity of the battery.
800 * XXX: make sure this is actually portable . . . */
801 static int calc_charge_time_rate(int batt
)
807 binfo
= &batteries
[batt
];
809 if (binfo
->charge_state
== CHARGE
) {
810 if (binfo
->present_rate
== -1) {
811 perr("unknown present rate\n");
814 lfcap
= (float)binfo
->last_full_cap
;
815 rcap
= (float)binfo
->remaining_cap
;
817 charge_time
= (int)(((lfcap
- rcap
)/binfo
->present_rate
) * 60.0);
820 if (binfo
->charge_time
)
825 /* we need to calculate the present rate the same way we do in rt_cap
826 * mode, and then use that to estimate charge time. This will
827 * necessarily be even less accurate than it is for remaining time, but
828 * it's just as neessary . . . */
829 static int calc_charge_time_cap(int batt
)
831 static float cap_samples
[CAP_SAMPLES
];
832 static int time_samples
[CAP_SAMPLES
];
833 static int sample_count
= 0;
834 static int current
= 0;
840 battery_t
*binfo
= &batteries
[batt
];
842 cap_samples
[current
] = (float) binfo
->remaining_cap
;
843 time_samples
[current
] = time(NULL
);
845 if (sample_count
== 0) {
846 /* we can't do much if we don't have any data . . . */
848 } else if (sample_count
< CAP_SAMPLES
) {
849 /* if we have less than SAMPLES samples so far, we use the first
850 * sample and the current one */
851 cdiff
= cap_samples
[current
] - cap_samples
[0];
852 tdiff
= time_samples
[current
] - time_samples
[0];
853 current_rate
= cdiff
/tdiff
;
855 /* if we have more than SAMPLES samples, we use the oldest
856 * current one, which at this point is current + 1. This will
857 * wrap the same way that current will wrap, but one cycle
859 cdiff
= cap_samples
[current
] - cap_samples
[old
];
860 tdiff
= time_samples
[current
] - time_samples
[old
];
861 current_rate
= cdiff
/(float)tdiff
;
863 if (current_rate
== 0)
866 float cap_left
= (float)(binfo
->last_full_cap
- binfo
->remaining_cap
);
867 rtime
= (int)(cap_left
/(current_rate
* 60.0));
869 sample_count
++, current
++, old
++;
870 if (current
>= CAP_SAMPLES
)
872 if (old
>= CAP_SAMPLES
)
875 pdebug("cap charge time rem: %d\n", rtime
);
879 static int calc_charge_time(global_t
*globals
, int batt
)
883 globals
->rt_mode
= check_rt_mode(globals
);
885 switch(globals
->rt_mode
) {
887 ctime
= calc_charge_time_rate(batt
);
890 ctime
= calc_charge_time_cap(batt
);
896 void acquire_batt_info(global_t
*globals
, int batt
)
899 adapter_t
*ap
= &globals
->adapter
;
901 get_battery_info(globals
, batt
);
903 binfo
= &batteries
[batt
];
905 if (!binfo
->present
) {
906 binfo
->percentage
= 0;
908 binfo
->charge_time
= 0;
913 binfo
->percentage
= calc_remaining_percentage(batt
);
915 /* set the battery's capacity state, based (at present) on some
916 * guesstimated values: more than 75% == HIGH, 25% to 75% MED, and
917 * less than 25% is LOW. Less than globals->crit_level is CRIT. */
918 if (binfo
->percentage
== -1)
919 binfo
->state
= BS_ERR
;
920 if (binfo
->percentage
< globals
->crit_level
)
922 else if (binfo
->percentage
> 75)
924 else if (binfo
->percentage
> 25)
929 /* we need to /know/ that we've got a valid state for the
930 * globals->power value . . . .*/
931 ap
->power
= get_power_status(globals
);
933 binfo
->charge_time
= calc_charge_time(globals
, batt
);
935 /* and finally, we tell anyone who wants to use this information
936 * that it's now valid . . .*/
940 void acquire_all_batt_info(global_t
*globals
)
944 for(i
= 0; i
< globals
->battery_count
; i
++)
945 acquire_batt_info(globals
, i
);
949 * One of the feature requests I've had is for some way to deal with
950 * batteries that are too dumb or too b0rken to report a present rate
951 * value. The way to do this, obviously, is to record the time that
952 * samples were taken and use that information to calculate the rate
953 * at which the battery is draining/charging. This still won't help
954 * systems where the battery doesn't even report the remaining
955 * capacity, but without the present rate or the remaining capacity, I
956 * don't think there's /anything/ we can do to work around it.
958 * So, what we need to do is provide a way to use a different method
959 * to calculate the time remaining. What seems most sensible is to
960 * split out the code to calculate it into a seperate function, and
961 * then provide multiple implementations . . .
965 * the default implementation - if present rate and remaining capacity
966 * are both reported correctly, we use them.
968 int calc_time_remaining_rate(global_t
*globals
)
975 static float rate_samples
[SAMPLES
];
976 static int sample_count
= 0;
980 /* calculate the time remaining, using the battery's remaining
981 * capacity and the reported burn rate (3.9.3).
982 * For added accuracy, we average the value over the last
983 * SAMPLES number of calls, or for anything less than this we
984 * simply report the raw number. */
985 /* XXX: this needs to correctly handle the case where
986 * any of the values used is unknown (which we flag using
988 for (i
= 0; i
< globals
->battery_count
; i
++) {
989 binfo
= &batteries
[i
];
990 if (binfo
->present
&& binfo
->valid
) {
991 rcap
+= (float)binfo
->remaining_cap
;
992 rate
+= (float)binfo
->present_rate
;
995 rate_samples
[j
] = rate
;
1000 /* for the first SAMPLES number of calls we calculate the
1001 * average based on sample_count, then we use SAMPLES to
1002 * calculate the rolling average. */
1004 /* when this fails, n should be equal to SAMPLES. */
1005 if (sample_count
< SAMPLES
)
1007 for (i
= 0, rate
= 0; i
< n
; i
++) {
1008 /* if any of our samples are invalid, we drop
1009 * straight out, and flag our unknown values. */
1010 if (rate_samples
[i
] < 0) {
1015 rate
+= rate_samples
[i
];
1017 rate
= rate
/(float)n
;
1019 if ((rcap
< 1) || (rate
< 1)) {
1025 /* time remaining in minutes */
1026 rtime
= (int)((rcap
/rate
) * 60.0);
1030 pdebug("discharge time rem: %d\n", rtime
);
1035 * the alternative implementation - record the time at which each
1036 * sample was taken, and then use the difference between the latest
1037 * sample and the one SAMPLES ago to calculate the difference over
1038 * that time, and from there the rate of change of capacity.
1040 * XXX: this code sucks, but largely because batteries aren't exactly
1041 * precision instruments - mine only report with about 70mAH
1042 * resolution, so they don't report any changes until the difference
1043 * is 70mAH. This means that calculating the current rate from the
1044 * remaining capacity is very choppy . . .
1046 * To fix this, we should calculate an average over some number of
1047 * samples at the old end of the set - this would smooth out the
1050 int calc_time_remaining_cap(global_t
*globals
)
1052 static float cap_samples
[CAP_SAMPLES
];
1053 static int time_samples
[CAP_SAMPLES
];
1054 static int sample_count
= 0;
1055 static int current
= 0;
1065 for (i
= 0; i
< globals
->battery_count
; i
++) {
1066 binfo
= &batteries
[i
];
1067 if (binfo
->present
&& binfo
->valid
)
1068 cap
+= binfo
->remaining_cap
;
1070 cap_samples
[current
] = cap
;
1071 time_samples
[current
] = time(NULL
);
1073 if (sample_count
== 0) {
1074 /* we can't do much if we don't have any data . . . */
1076 } else if (sample_count
< CAP_SAMPLES
) {
1077 /* if we have less than SAMPLES samples so far, we use the first
1078 * sample and the current one */
1079 cdiff
= cap_samples
[0] - cap_samples
[current
];
1080 tdiff
= time_samples
[current
] - time_samples
[0];
1081 current_rate
= cdiff
/tdiff
;
1083 /* if we have more than SAMPLES samples, we use the oldest
1084 * current one, which at this point is current + 1. This will
1085 * wrap the same way that current will wrap, but one cycle
1087 cdiff
= cap_samples
[old
] - cap_samples
[current
];
1088 tdiff
= time_samples
[current
] - time_samples
[old
];
1089 current_rate
= cdiff
/tdiff
;
1091 if (current_rate
== 0)
1094 rtime
= (int)(cap_samples
[current
]/(current_rate
* 60.0));
1096 sample_count
++, current
++, old
++;
1097 if (current
>= CAP_SAMPLES
)
1099 if (old
>= CAP_SAMPLES
)
1102 pdebug("cap discharge time rem: %d\n", rtime
);
1106 void acquire_global_info(global_t
*globals
)
1108 adapter_t
*ap
= &globals
->adapter
;
1110 globals
->rt_mode
= check_rt_mode(globals
);
1112 switch(globals
->rt_mode
) {
1114 globals
->rtime
= calc_time_remaining_rate(globals
);
1117 globals
->rtime
= calc_time_remaining_cap(globals
);
1121 /* get the power status.
1122 * note that this is actually reported seperately from the
1123 * battery info, under /proc/acpi/ac_adapter/AC/state */
1124 ap
->power
= get_power_status(globals
);
1127 void acquire_all_info(global_t
*globals
)
1129 acquire_all_batt_info(globals
);
1130 acquire_global_info(globals
);