$NetBSD: patch-ab,v 1.6 2003/11/10 23:53:32 mrg Exp $ --- xbattbar.c.orig 2001-02-02 16:25:29.000000000 +1100 +++ xbattbar.c 2003-11-10 17:50:08.000000000 +1100 @@ -27,6 +27,14 @@ #include #include + +#ifdef __NetBSD__ +#define ENVSYSUNITNAMES +#include +#include +#include +#endif /* __NetBSD__ */ + #include #include #include @@ -583,39 +591,174 @@ #define _PATH_APM_CTLDEV "/dev/apmctl" #define _PATH_APM_NORMAL "/dev/apm" +/* + * pre: fd contains a valid file descriptor of an envsys(4) supporting device + * && ns is the number of sensors + * && etds and ebis are arrays of sufficient size + * post: returns 0 and etds and ebis arrays are filled with sensor info + * or returns -1 on failure + */ +static int +fillsensors(int fd, envsys_tre_data_t *etds, envsys_basic_info_t *ebis, + size_t ns) +{ + int i; + + for (i = 0; i < ns; ++i) { + ebis[i].sensor = i; + if (ioctl(fd, ENVSYS_GTREINFO, &ebis[i]) == -1) { + warn("Can't get sensor info for sensor %d", i); + return 0; + } + + etds[i].sensor = i; + if (ioctl(fd, ENVSYS_GTREDATA, &etds[i]) == -1) { + warn("Can't get sensor data for sensor %d", i); + return 0; + } + } + return 1; +} + +/* + * pre: fd contains a valid file descriptor of an envsys(4) supporting device + * post: returns the number of valid sensors provided by the device + * or -1 on error + */ +static size_t +numsensors(int fd) +{ + int count = 0, valid = 1; + envsys_tre_data_t etd; + etd.sensor = 0; + + while (valid) { + if (ioctl(fd, ENVSYS_GTREDATA, &etd) == -1) + err(1, "Can't get sensor data"); + + valid = etd.validflags & ENVSYS_FVALID; + if (valid) + ++count; + + ++etd.sensor; + } + + return count; +} + +static envsys_tre_data_t *etds; +static envsys_basic_info_t *ebis; +static int *cetds; + +#if defined(_PATH_SYSMON) && __NetBSD_Version__ >= 106110000 +#define HAVE_NETBSD_ACPI +#endif + int first = 1; void battery_check(void) { int fd, r, p; struct apm_power_info info; - - if ((fd = open(_PATH_APM_NORMAL, O_RDONLY)) == -1) { - fprintf(stderr, "xbattbar: cannot open apm device\n"); - exit(1); + int acpi; + size_t ns; + size_t cc; + char *apmdev; + int i; + + acpi = 0; + apmdev = _PATH_APM_NORMAL; + if ((fd = open(apmdev, O_RDONLY)) == -1) { +#ifdef HAVE_NETBSD_ACPI + apmdev = _PATH_SYSMON; + fd = open(apmdev, O_RDONLY); + acpi = 1; +#endif } - - if (ioctl(fd, APM_IOC_GETPOWER, &info) != 0) { - fprintf(stderr, "xbattbar: ioctl APM_IOC_GETPOWER failed\n"); + if (fd < 0) { + fprintf(stderr, "xbattbar: cannot open %s device\n", apmdev); exit(1); } - close(fd); + if (acpi) { +#ifdef HAVE_NETBSD_ACPI + if ((ns = numsensors(fd)) == 0) { + fprintf(stderr, "xbattbar: no sensors found\n"); + exit(1); + } + if (first) { + cetds = (int *)malloc(ns * sizeof(int)); + etds = (envsys_tre_data_t *)malloc(ns * sizeof(envsys_tre_data_t)); + ebis = (envsys_basic_info_t *)malloc(ns * sizeof(envsys_basic_info_t)); + + if ((cetds == NULL) || (etds == NULL) || (ebis == NULL)) { + err(1, "Out of memory"); + } + } - ++elapsed_time; + fillsensors(fd, etds, ebis, ns); - /* get current remoain */ - if (info.battery_life > 100) { - /* some APM BIOSes return values slightly > 100 */ - r = 100; +#endif } else { - r = info.battery_life; + + memset(&info, 0, sizeof(info)); + if (ioctl(fd, APM_IOC_GETPOWER, &info) != 0) { + fprintf(stderr, "xbattbar: ioctl APM_IOC_GETPOWER failed\n"); + exit(1); + } } - /* get AC-line status */ - if (info.ac_state == APM_AC_ON) { - p = APM_AC_ON; + close(fd); + + ++elapsed_time; + + if (acpi) { +#ifdef HAVE_NETBSD_ACPI + int32_t rtot = 0, maxtot = 0; + p = APM_AC_ON; + for (i = 0 ; i < ns ; i++) { + if ((etds[i].validflags & ENVSYS_FCURVALID) == 0) + continue; + cc = strlen(ebis[i].desc); + if (strncmp(ebis[i].desc, "acpibat", 7) == 0 && + (strcmp(&ebis[i].desc[cc - 7], " charge") == 0 || + strcmp(&ebis[i].desc[cc - 7], " energy") == 0)) { + rtot += etds[i].cur.data_s; + maxtot += etds[i].max.data_s; + } + /* + * XXX: We should use acpiacad driver and look for + * " connected", but that's broken on some machines + * and we want this to work everywhere. With this + * we will occasionally catch a machine conditioning + * a battery while connected, while other machines take + * 10-15 seconds to switch from "charging" to + * "discharging" and vice versa, but this is the best + * compromise. + */ + if (ebis[i].units == ENVSYS_INDICATOR && + etds[i].cur.data_s && + strncmp(ebis[i].desc, "acpibat", 7) == 0 && + strcmp(&ebis[i].desc[cc - 11], "discharging") == 0) { + p = APM_AC_OFF; + } + } + r = (rtot * 100.0) / maxtot; +#endif } else { - p = APM_AC_OFF; + /* get current remain */ + if (info.battery_life > 100) { + /* some APM BIOSes return values slightly > 100 */ + r = 100; + } else { + r = info.battery_life; + } + + /* get AC-line status */ + if (info.ac_state == APM_AC_ON) { + p = APM_AC_ON; + } else { + p = APM_AC_OFF; + } } if (first || ac_line != p || battery_level != r) {