Update acpi_battery(4) related code to the latest one from FreeBSD HEAD.
authorHasso Tepper <hasso@dragonflybsd.org>
Mon, 29 Sep 2008 06:59:45 +0000 (06:59 +0000)
committerHasso Tepper <hasso@dragonflybsd.org>
Mon, 29 Sep 2008 06:59:45 +0000 (06:59 +0000)
Obtained-from: FreeBSD

sys/dev/acpica5/acpi_battery.c
sys/dev/acpica5/acpi_cmbat.c
sys/dev/acpica5/acpi_if.m
sys/dev/acpica5/acpiio.h
sys/dev/acpica5/acpivar.h
sys/kern/subr_bus.c
sys/platform/pc32/acpica5/acpi_machdep.c
sys/sys/bus.h
usr.sbin/acpi/acpiconf/acpiconf.c

index a2651cd..c436065 100644 (file)
@@ -1,4 +1,5 @@
 /*-
+ * Copyright (c) 2005 Nate Lawson
  * Copyright (c) 2000 Mitsuru IWASAKI <iwasaki@jp.freebsd.org>
  * All rights reserved.
  *
@@ -23,8 +24,8 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * $FreeBSD: src/sys/dev/acpica/acpi_battery.c,v 1.8 2003/08/11 15:34:43 njl Exp $
- * $DragonFly: src/sys/dev/acpica5/acpi_battery.c,v 1.3 2006/09/05 00:55:36 dillon Exp $
+ * $FreeBSD: src/sys/dev/acpica/acpi_battery.c,v 1.26 2007/11/20 18:35:36 jkim Exp $
+ * $DragonFly: src/sys/dev/acpica5/acpi_battery.c,v 1.4 2008/09/29 06:59:45 hasso Exp $
  */
 
 #include "opt_acpi.h"
 #include <sys/bus.h>
 #include <sys/ioccom.h>
 #include <sys/sysctl.h>
+#include <sys/thread2.h>
 
 #include "acpi.h"
 #include <dev/acpica5/acpivar.h>
 #include <dev/acpica5/acpiio.h>
 
-MALLOC_DEFINE(M_ACPIBATT, "acpibatt", "ACPI generic battery data");
+/* Default seconds before re-sampling the battery state. */
+#define        ACPI_BATTERY_INFO_EXPIRE        5
 
-struct acpi_batteries {
-    TAILQ_ENTRY(acpi_batteries)        link;
-    struct acpi_battdesc       battdesc;
-};
+static int     acpi_batteries_initted;
+static int     acpi_battery_info_expire = ACPI_BATTERY_INFO_EXPIRE;
+static struct  acpi_battinfo   acpi_battery_battinfo;
+static struct  sysctl_ctx_list acpi_battery_sysctl_ctx;
+static struct  sysctl_oid      *acpi_battery_sysctl_tree;
 
-static TAILQ_HEAD(,acpi_batteries) acpi_batteries;
-static int                     acpi_batteries_initted = 0;
-static int                     acpi_batteries_units = 0;
-static int                     acpi_battery_info_expire = 5;
-static struct acpi_battinfo    acpi_battery_battinfo;
+static void acpi_reset_battinfo(struct acpi_battinfo *info);
+static void acpi_battery_clean_str(char *str, int len);
+static device_t acpi_battery_find_dev(u_int logical_unit);
+static int acpi_battery_ioctl(u_long cmd, caddr_t addr, void *arg);
+static int acpi_battery_sysctl(SYSCTL_HANDLER_ARGS);
+static int acpi_battery_units_sysctl(SYSCTL_HANDLER_ARGS);
+static int acpi_battery_init(void);
 
 int
-acpi_battery_get_units(void)
+acpi_battery_register(device_t dev)
 {
-    return (acpi_batteries_units);
+    int error;
+
+    error = 0;
+    crit_enter();
+    if (!acpi_batteries_initted)
+       error = acpi_battery_init();
+    crit_exit();
+    return (error);
 }
 
 int
-acpi_battery_get_battdesc(int logical_unit, struct acpi_battdesc *battdesc)
+acpi_battery_remove(device_t dev)
 {
-    struct acpi_batteries *bp;
-    int i;
+    return (0);
+}
 
-    if (logical_unit < 0 || logical_unit >= acpi_batteries_units)
-       return (ENXIO);
+int
+acpi_battery_get_units(void)
+{
+    devclass_t batt_dc;
 
-    i = 0;
-    TAILQ_FOREACH(bp, &acpi_batteries, link) {
-       if (logical_unit == i) {
-           battdesc->type = bp->battdesc.type;
-           battdesc->phys_unit = bp->battdesc.phys_unit;
-           return (0);
-       }
-       i++;
-    }
+    batt_dc = devclass_find("battery");
+    if (batt_dc == NULL)
+       return (0);
+    return (devclass_get_count(batt_dc));
+}
 
-    return (ENXIO);
+int
+acpi_battery_get_info_expire(void)
+{
+    return (acpi_battery_info_expire);
 }
 
+/* Check _BST results for validity. */
 int
-acpi_battery_get_battinfo(int unit, struct acpi_battinfo *battinfo)
+acpi_battery_bst_valid(struct acpi_bst *bst)
 {
-    struct acpi_battdesc battdesc;
-    int error;
+    return (bst->state < ACPI_BATT_STAT_MAX && bst->cap != ACPI_BATT_UNKNOWN &&
+       bst->volt != ACPI_BATT_UNKNOWN);
+}
 
-    error = 0;
-    if (unit == -1) {
-       error = acpi_cmbat_get_battinfo(-1, battinfo);
-       goto out;
-    } else {
-       error = acpi_battery_get_battdesc(unit, &battdesc);
-       if (error != 0)
-           goto out;
-
-       switch (battdesc.type) {
-       case ACPI_BATT_TYPE_CMBAT:
-           error = acpi_cmbat_get_battinfo(battdesc.phys_unit, battinfo);
-           break;
-       default:
-           error = ENXIO;
-           break;
+/* Check _BIF results for validity. */
+int
+acpi_battery_bif_valid(struct acpi_bif *bif)
+{
+    return (bif->lfcap != 0);
+}
+
+/* Get info about one or all batteries. */
+int
+acpi_battery_get_battinfo(device_t dev, struct acpi_battinfo *battinfo)
+{
+    int        batt_stat, devcount, dev_idx, error, i;
+    int total_cap, total_min, valid_rate, valid_units;
+    devclass_t batt_dc;
+    device_t batt_dev;
+    struct acpi_bst *bst;
+    struct acpi_bif *bif;
+    struct acpi_battinfo *bi;
+
+    /*
+     * Get the battery devclass and max unit for battery devices.  If there
+     * are none or error, return immediately.
+     */
+    batt_dc = devclass_find("battery");
+    if (batt_dc == NULL)
+       return (ENXIO);
+    devcount = devclass_get_maxunit(batt_dc);
+    if (devcount == 0)
+       return (ENXIO);
+
+    /*
+     * Allocate storage for all _BST data, their derived battinfo data,
+     * and the current battery's _BIF data.
+     */
+    bst = kmalloc(devcount * sizeof(*bst), M_TEMP, M_WAITOK | M_ZERO);
+    bi = kmalloc(devcount * sizeof(*bi), M_TEMP, M_WAITOK | M_ZERO);
+    bif = kmalloc(sizeof(*bif), M_TEMP, M_WAITOK | M_ZERO);
+
+    /*
+     * Pass 1:  for each battery that is present and valid, get its status,
+     * calculate percent capacity remaining, and sum all the current
+     * discharge rates.
+     */
+    dev_idx = -1;
+    batt_stat = valid_rate = valid_units = 0;
+    for (i = 0; i < devcount; i++) {
+       /* Default info for every battery is "not present". */
+       acpi_reset_battinfo(&bi[i]);
+
+       /*
+        * Find the device.  Since devcount is in terms of max units, this
+        * may be a sparse array so skip devices that aren't present.
+        */
+       batt_dev = devclass_get_device(batt_dc, i);
+       if (batt_dev == NULL)
+           continue;
+
+       /* If examining a specific battery and this is it, record its index. */
+       if (dev != NULL && dev == batt_dev)
+           dev_idx = i;
+
+       /*
+        * Be sure we can get various info from the battery.  Note that
+        * acpi_BatteryIsPresent() is not enough because smart batteries only
+        * return that the device is present.
+        */
+       if (!acpi_BatteryIsPresent(batt_dev) ||
+           ACPI_BATT_GET_STATUS(batt_dev, &bst[i]) != 0 ||
+           ACPI_BATT_GET_INFO(batt_dev, bif) != 0)
+           continue;
+
+       /* If a battery is not installed, we sometimes get strange values. */
+       if (!acpi_battery_bst_valid(&bst[i]) ||
+           !acpi_battery_bif_valid(bif))
+           continue;
+
+       /*
+        * Record current state.  If both charging and discharging are set,
+        * ignore the charging flag.
+        */
+       valid_units++;
+       if ((bst[i].state & ACPI_BATT_STAT_DISCHARG) != 0)
+           bst[i].state &= ~ACPI_BATT_STAT_CHARGING;
+       batt_stat |= bst[i].state;
+       bi[i].state = bst[i].state;
+
+       /*
+        * If the battery info is in terms of mA, convert to mW by
+        * multiplying by the design voltage.  If the design voltage
+        * is 0 (due to some error reading the battery), skip this
+        * conversion.
+        */
+       if (bif->units == ACPI_BIF_UNITS_MA && bif->dvol != 0) {
+           bst[i].rate = (bst[i].rate * bif->dvol) / 1000;
+           bst[i].cap = (bst[i].cap * bif->dvol) / 1000;
+           bif->lfcap = (bif->lfcap * bif->dvol) / 1000;
        }
+
+       /* Calculate percent capacity remaining. */
+       bi[i].cap = (100 * bst[i].cap) / bif->lfcap;
+
+       /*
+        * Some laptops report the "design-capacity" instead of the
+        * "real-capacity" when the battery is fully charged.  That breaks
+        * the above arithmetic as it needs to be 100% maximum.
+        */
+       if (bi[i].cap > 100)
+           bi[i].cap = 100;
+
+       /*
+        * On systems with more than one battery, they may get used
+        * sequentially, thus bst.rate may only signify the one currently
+        * in use.  For the remaining batteries, bst.rate will be zero,
+        * which makes it impossible to calculate the total remaining time.
+        * Therefore, we sum the bst.rate for batteries in the discharging
+        * state and use the sum to calculate the total remaining time.
+        */
+       if (bst[i].rate != ACPI_BATT_UNKNOWN &&
+           (bst[i].state & ACPI_BATT_STAT_DISCHARG) != 0)
+           valid_rate += bst[i].rate;
     }
 
+    /* If the caller asked for a device but we didn't find it, error. */
+    if (dev != NULL && dev_idx == -1) {
+       error = ENXIO;
+       goto out;
+    }
+
+    /* Pass 2:  calculate capacity and remaining time for all batteries. */
+    total_cap = total_min = 0;
+    for (i = 0; i < devcount; i++) {
+       /*
+        * If any batteries are discharging, use the sum of the bst.rate
+        * values.  Otherwise, we are on AC power, and there is infinite
+        * time remaining for this battery until we go offline.
+        */
+       if (valid_rate > 0)
+           bi[i].min = (60 * bst[i].cap) / valid_rate;
+       else
+           bi[i].min = 0;
+       total_min += bi[i].min;
+
+       /* If this battery is not present, don't use its capacity. */
+       if (bi[i].cap != -1)
+           total_cap += bi[i].cap;
+    }
+
+    /*
+     * Return total battery percent and time remaining.  If there are
+     * no valid batteries, report values as unknown.
+     */
+    if (valid_units > 0) {
+       if (dev == NULL) {
+           battinfo->cap = total_cap / valid_units;
+           battinfo->min = total_min;
+           battinfo->state = batt_stat;
+           battinfo->rate = valid_rate;
+       } else {
+           battinfo->cap = bi[dev_idx].cap;
+           battinfo->min = bi[dev_idx].min;
+           battinfo->state = bi[dev_idx].state;
+           battinfo->rate = bst[dev_idx].rate;
+       }
+
+       /*
+        * If the queried battery has no discharge rate or is charging,
+        * report that we don't know the remaining time.
+        */
+       if (valid_rate == 0 || (battinfo->state & ACPI_BATT_STAT_CHARGING))
+           battinfo->min = -1;
+    } else
+       acpi_reset_battinfo(battinfo);
+
+    error = 0;
+
 out:
+    if (bi)
+       kfree(bi, M_TEMP);
+    if (bif)
+       kfree(bif, M_TEMP);
+    if (bst)
+       kfree(bst, M_TEMP);
     return (error);
 }
 
-int
-acpi_battery_get_info_expire(void)
+static void
+acpi_reset_battinfo(struct acpi_battinfo *info)
 {
-    return (acpi_battery_info_expire);
+    info->cap = -1;
+    info->min = -1;
+    info->state = ACPI_BATT_STAT_NOT_PRESENT;
+    info->rate = -1;
+}
+
+/* Make string printable, removing invalid chars. */
+static void
+acpi_battery_clean_str(char *str, int len)
+{
+    int i;
+
+    for (i = 0; i < len && *str != '\0'; i++, str++) {
+       if (!isprint(*str))
+           *str = '?';
+    }
+
+    /* NUL-terminate the string if we reached the end. */
+    if (i == len)
+       *str = '\0';
+}
+
+/*
+ * The battery interface deals with devices and methods but userland
+ * expects a logical unit number.  Convert a logical unit to a device_t.
+ */
+static device_t
+acpi_battery_find_dev(u_int logical_unit)
+{
+    int found_unit, i, maxunit;
+    device_t dev;
+    devclass_t batt_dc;
+
+    dev = NULL;
+    found_unit = 0;
+    batt_dc = devclass_find("battery");
+    maxunit = devclass_get_maxunit(batt_dc);
+    for (i = 0; i < maxunit; i++) {
+       dev = devclass_get_device(batt_dc, i);
+       if (dev == NULL)
+           continue;
+       if (logical_unit == found_unit)
+           break;
+       found_unit++;
+       dev = NULL;
+    }
+
+    return (dev);
 }
 
 static int
 acpi_battery_ioctl(u_long cmd, caddr_t addr, void *arg)
 {
     union acpi_battery_ioctl_arg *ioctl_arg;
-    int        error, logical_unit;
+    int error, unit;
+    device_t dev;
 
-    ioctl_arg = (union acpi_battery_ioctl_arg *)addr;
-    error = 0;
+    /* For commands that use the ioctl_arg struct, validate it first. */
+    error = ENXIO;
+    unit = 0;
+    dev = NULL;
+    ioctl_arg = NULL;
+    if (IOCPARM_LEN(cmd) == sizeof(*ioctl_arg)) {
+       ioctl_arg = (union acpi_battery_ioctl_arg *)addr;
+       unit = ioctl_arg->unit;
+       if (unit != ACPI_BATTERY_ALL_UNITS)
+           dev = acpi_battery_find_dev(unit);
+    }
 
     /*
      * No security check required: information retrieval only.  If
@@ -131,18 +366,42 @@ acpi_battery_ioctl(u_long cmd, caddr_t addr, void *arg)
     switch (cmd) {
     case ACPIIO_BATT_GET_UNITS:
        *(int *)addr = acpi_battery_get_units();
-       break;
-    case ACPIIO_BATT_GET_BATTDESC:
-       logical_unit = ioctl_arg->unit;
-       error = acpi_battery_get_battdesc(logical_unit, &ioctl_arg->battdesc);
+       error = 0;
        break;
     case ACPIIO_BATT_GET_BATTINFO:
-       logical_unit = ioctl_arg->unit;
-       error = acpi_battery_get_battinfo(logical_unit, &ioctl_arg->battinfo);
+       if (dev != NULL || unit == ACPI_BATTERY_ALL_UNITS) {
+           bzero(&ioctl_arg->battinfo, sizeof(ioctl_arg->battinfo));
+           error = acpi_battery_get_battinfo(dev, &ioctl_arg->battinfo);
+       }
+       break;
+    case ACPIIO_BATT_GET_BIF:
+       if (dev != NULL) {
+           bzero(&ioctl_arg->bif, sizeof(ioctl_arg->bif));
+           error = ACPI_BATT_GET_INFO(dev, &ioctl_arg->bif);
+
+           /*
+            * Remove invalid characters.  Perhaps this should be done
+            * within a convenience function so all callers get the
+            * benefit.
+            */
+           acpi_battery_clean_str(ioctl_arg->bif.model,
+               sizeof(ioctl_arg->bif.model));
+           acpi_battery_clean_str(ioctl_arg->bif.serial,
+               sizeof(ioctl_arg->bif.serial));
+           acpi_battery_clean_str(ioctl_arg->bif.type,
+               sizeof(ioctl_arg->bif.type));
+           acpi_battery_clean_str(ioctl_arg->bif.oeminfo,
+               sizeof(ioctl_arg->bif.oeminfo));
+       }
+       break;
+    case ACPIIO_BATT_GET_BST:
+       if (dev != NULL) {
+           bzero(&ioctl_arg->bst, sizeof(ioctl_arg->bst));
+           error = ACPI_BATT_GET_STATUS(dev, &ioctl_arg->bst);
+       }
        break;
     default:
        error = EINVAL;
-       break;
     }
 
     return (error);
@@ -151,14 +410,24 @@ acpi_battery_ioctl(u_long cmd, caddr_t addr, void *arg)
 static int
 acpi_battery_sysctl(SYSCTL_HANDLER_ARGS)
 {
-    int        val, error;
+    int val, error;
 
-    acpi_battery_get_battinfo(-1, &acpi_battery_battinfo);
+    acpi_battery_get_battinfo(NULL, &acpi_battery_battinfo);
     val = *(u_int *)oidp->oid_arg1;
     error = sysctl_handle_int(oidp, &val, 0, req);
     return (error);
 }
 
+static int
+acpi_battery_units_sysctl(SYSCTL_HANDLER_ARGS)
+{
+    int count, error;
+
+    count = acpi_battery_get_units();
+    error = sysctl_handle_int(oidp, &count, 0, req);
+    return (error);
+}
+
 static int
 acpi_battery_init(void)
 {
@@ -166,76 +435,64 @@ acpi_battery_init(void)
     device_t            dev;
     int                         error;
 
+    error = ENXIO;
     dev = devclass_get_device(devclass_find("acpi"), 0);
     if (dev == NULL)
-       return (ENXIO);
+       goto out;
     sc = device_get_softc(dev);
-    if (sc == NULL)
-       return (ENXIO);
-
-    error = 0;
-    TAILQ_INIT(&acpi_batteries);
-    acpi_batteries_initted = 1;
 
     error = acpi_register_ioctl(ACPIIO_BATT_GET_UNITS, acpi_battery_ioctl,
-                               NULL);
+       NULL);
     if (error != 0)
-       return (error);
-    error = acpi_register_ioctl(ACPIIO_BATT_GET_BATTDESC, acpi_battery_ioctl,
-                               NULL);
-    if (error != 0)
-       return (error);
+       goto out;
     error = acpi_register_ioctl(ACPIIO_BATT_GET_BATTINFO, acpi_battery_ioctl,
-                               NULL);
+       NULL);
+    if (error != 0)
+       goto out;
+    error = acpi_register_ioctl(ACPIIO_BATT_GET_BIF, acpi_battery_ioctl, NULL);
+    if (error != 0)
+       goto out;
+    error = acpi_register_ioctl(ACPIIO_BATT_GET_BST, acpi_battery_ioctl, NULL);
     if (error != 0)
-       return (error);
-
-    sysctl_ctx_init(&sc->acpi_battery_sysctl_ctx);
-    sc->acpi_battery_sysctl_tree = SYSCTL_ADD_NODE(&sc->acpi_battery_sysctl_ctx,
-                                  SYSCTL_CHILDREN(sc->acpi_sysctl_tree),
-                                  OID_AUTO, "battery", CTLFLAG_RD, 0, "");
-    SYSCTL_ADD_PROC(&sc->acpi_battery_sysctl_ctx,
-       SYSCTL_CHILDREN(sc->acpi_battery_sysctl_tree),
+       goto out;
+
+    sysctl_ctx_init(&acpi_battery_sysctl_ctx);
+    acpi_battery_sysctl_tree = SYSCTL_ADD_NODE(&acpi_battery_sysctl_ctx,
+       SYSCTL_CHILDREN(sc->acpi_sysctl_tree), OID_AUTO, "battery", CTLFLAG_RD,
+       0, "battery status and info");
+    SYSCTL_ADD_PROC(&acpi_battery_sysctl_ctx,
+       SYSCTL_CHILDREN(acpi_battery_sysctl_tree),
        OID_AUTO, "life", CTLTYPE_INT | CTLFLAG_RD,
-       &acpi_battery_battinfo.cap, 0, acpi_battery_sysctl, "I", "");
-    SYSCTL_ADD_PROC(&sc->acpi_battery_sysctl_ctx,
-       SYSCTL_CHILDREN(sc->acpi_battery_sysctl_tree),
+       &acpi_battery_battinfo.cap, 0, acpi_battery_sysctl, "I",
+       "percent capacity remaining");
+    SYSCTL_ADD_PROC(&acpi_battery_sysctl_ctx,
+       SYSCTL_CHILDREN(acpi_battery_sysctl_tree),
        OID_AUTO, "time", CTLTYPE_INT | CTLFLAG_RD,
-       &acpi_battery_battinfo.min, 0, acpi_battery_sysctl, "I", "");
-    SYSCTL_ADD_PROC(&sc->acpi_battery_sysctl_ctx,
-       SYSCTL_CHILDREN(sc->acpi_battery_sysctl_tree),
+       &acpi_battery_battinfo.min, 0, acpi_battery_sysctl, "I",
+       "remaining time in minutes");
+    SYSCTL_ADD_PROC(&acpi_battery_sysctl_ctx,
+       SYSCTL_CHILDREN(acpi_battery_sysctl_tree),
        OID_AUTO, "state", CTLTYPE_INT | CTLFLAG_RD,
-       &acpi_battery_battinfo.state, 0, acpi_battery_sysctl, "I", "");
-    SYSCTL_ADD_INT(&sc->acpi_battery_sysctl_ctx,
-       SYSCTL_CHILDREN(sc->acpi_battery_sysctl_tree),
-       OID_AUTO, "units", CTLFLAG_RD, &acpi_batteries_units, 0, "");
-    SYSCTL_ADD_INT(&sc->acpi_battery_sysctl_ctx,
-       SYSCTL_CHILDREN(sc->acpi_battery_sysctl_tree),
-       OID_AUTO, "info_expire", CTLFLAG_RD | CTLFLAG_RW,
-       &acpi_battery_info_expire, 0, "");
+       &acpi_battery_battinfo.state, 0, acpi_battery_sysctl, "I",
+       "current status flags");
+    SYSCTL_ADD_PROC(&acpi_battery_sysctl_ctx,
+       SYSCTL_CHILDREN(acpi_battery_sysctl_tree),
+       OID_AUTO, "units", CTLTYPE_INT | CTLFLAG_RD,
+       NULL, 0, acpi_battery_units_sysctl, "I", "number of batteries");
+    SYSCTL_ADD_INT(&acpi_battery_sysctl_ctx,
+       SYSCTL_CHILDREN(acpi_battery_sysctl_tree),
+       OID_AUTO, "info_expire", CTLFLAG_RW,
+       &acpi_battery_info_expire, 0,
+       "time in seconds until info is refreshed");
 
-    return (error);
-}
+    acpi_batteries_initted = TRUE;
 
-int
-acpi_battery_register(int type, int phys_unit)
-{
-    struct acpi_batteries *bp;
-    int error;
-
-    error = 0;
-    bp = kmalloc(sizeof(*bp), M_ACPIBATT, M_INTWAIT);
-    bp->battdesc.type = type;
-    bp->battdesc.phys_unit = phys_unit;
-    if (acpi_batteries_initted == 0) {
-       if ((error = acpi_battery_init()) != 0) {
-           kfree(bp, M_ACPIBATT);
-           return (error);
-       }
+out:
+    if (error != 0) {
+       acpi_deregister_ioctl(ACPIIO_BATT_GET_UNITS, acpi_battery_ioctl);
+       acpi_deregister_ioctl(ACPIIO_BATT_GET_BATTINFO, acpi_battery_ioctl);
+       acpi_deregister_ioctl(ACPIIO_BATT_GET_BIF, acpi_battery_ioctl);
+       acpi_deregister_ioctl(ACPIIO_BATT_GET_BST, acpi_battery_ioctl);
     }
-               
-    TAILQ_INSERT_TAIL(&acpi_batteries, bp, link);
-    acpi_batteries_units++;
-
-    return (0);
+    return (error);
 }
index 186a8c8..7a9e971 100644 (file)
@@ -1,4 +1,5 @@
 /*-
+ * Copyright (c) 2005 Nate Lawson
  * Copyright (c) 2000 Munehiro Matsuda
  * Copyright (c) 2000 Takanori Watanabe
  * Copyright (c) 2000 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
@@ -25,8 +26,8 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * $FreeBSD: src/sys/dev/acpica/acpi_cmbat.c,v 1.30 2004/06/13 22:52:30 njl Exp $
- * $DragonFly: src/sys/dev/acpica5/acpi_cmbat.c,v 1.11 2007/10/23 03:04:48 y0netan1 Exp $
+ * $FreeBSD: src/sys/dev/acpica/acpi_cmbat.c,v 1.46 2007/03/22 18:16:40 jkim Exp $
+ * $DragonFly: src/sys/dev/acpica5/acpi_cmbat.c,v 1.12 2008/09/29 06:59:45 hasso Exp $
  */
 
 #include "opt_acpi.h"
@@ -37,6 +38,7 @@
 #include <sys/ioccom.h>
 #include <sys/rman.h>
 #include <sys/malloc.h>
+#include <sys/thread2.h>
 
 #include "acpi.h"
 #include <dev/acpica5/acpivar.h>
@@ -48,62 +50,55 @@ MALLOC_DEFINE(M_ACPICMBAT, "acpicmbat", "ACPI control method battery data");
 #define ACPI_CMBAT_RETRY_MAX   6
 
 /* Check the battery once a minute. */
-#define        CMBAT_POLLRATE  (60 * hz)
+#define        CMBAT_POLLRATE          (60 * hz)
 
 /* Hooks for the ACPI CA debugging infrastructure */
 #define        _COMPONENT      ACPI_BATTERY
 ACPI_MODULE_NAME("BATTERY")
 
-#define        ACPI_BATTERY_BST_CHANGE 0x80
-#define        ACPI_BATTERY_BIF_CHANGE 0x81
+#define        ACPI_BATTERY_BST_CHANGE 0x80
+#define        ACPI_BATTERY_BIF_CHANGE 0x81
 
 struct acpi_cmbat_softc {
     device_t       dev;
+    int                    flags;
 
     struct acpi_bif bif;
     struct acpi_bst bst;
-    struct timespec bif_lastupdated;
     struct timespec bst_lastupdated;
-    int                    bif_updating;
-    int                    bst_updating;
-
-    int                    present;
-    int                    cap;
-    int                    min;
-    int                    full_charge_time;
-    int                    initializing;
 };
 
-static struct timespec acpi_cmbat_info_lastupdated;
-
-/* XXX: devclass_get_maxunit() don't give us the current allocated units. */
-static int             acpi_cmbat_units = 0;
-
-static int             acpi_cmbat_info_expired(struct timespec *);
-static void            acpi_cmbat_info_updated(struct timespec *);
-static void            acpi_cmbat_get_bst(void *);
-static void            acpi_cmbat_get_bif(void *);
-static void            acpi_cmbat_notify_handler(ACPI_HANDLE, UINT32, void *);
-static int             acpi_cmbat_probe(device_t);
-static int             acpi_cmbat_attach(device_t);
-static int             acpi_cmbat_resume(device_t);
-static int             acpi_cmbat_ioctl(u_long, caddr_t, void *);
-static int             acpi_cmbat_is_bst_valid(struct acpi_bst*);
-static int             acpi_cmbat_is_bif_valid(struct acpi_bif*);
-static int             acpi_cmbat_get_total_battinfo(struct acpi_battinfo *);
-static void            acpi_cmbat_init_battery(void *);
+static int             acpi_cmbat_probe(device_t dev);
+static int             acpi_cmbat_attach(device_t dev);
+static int             acpi_cmbat_detach(device_t dev);
+static int             acpi_cmbat_resume(device_t dev);
+static void            acpi_cmbat_notify_handler(ACPI_HANDLE h, UINT32 notify,
+                           void *context);
+static int             acpi_cmbat_info_expired(struct timespec *lastupdated);
+static void            acpi_cmbat_info_updated(struct timespec *lastupdated);
+static void            acpi_cmbat_get_bst(void *arg);
+static void            acpi_cmbat_get_bif_task(void *arg);
+static void            acpi_cmbat_get_bif(void *arg);
+static int             acpi_cmbat_bst(device_t dev, struct acpi_bst *bstp);
+static int             acpi_cmbat_bif(device_t dev, struct acpi_bif *bifp);
+static void            acpi_cmbat_init_battery(void *arg);
 
 static device_method_t acpi_cmbat_methods[] = {
     /* Device interface */
     DEVMETHOD(device_probe,    acpi_cmbat_probe),
     DEVMETHOD(device_attach,   acpi_cmbat_attach),
+    DEVMETHOD(device_detach,   acpi_cmbat_detach),
     DEVMETHOD(device_resume,   acpi_cmbat_resume),
 
+    /* ACPI battery interface */
+    DEVMETHOD(acpi_batt_get_info, acpi_cmbat_bif),
+    DEVMETHOD(acpi_batt_get_status, acpi_cmbat_bst),
+
     {0, 0}
 };
 
 static driver_t acpi_cmbat_driver = {
-    "acpi_cmbat",
+    "battery",
     acpi_cmbat_methods,
     sizeof(struct acpi_cmbat_softc),
 };
@@ -112,15 +107,109 @@ static devclass_t acpi_cmbat_devclass;
 DRIVER_MODULE(acpi_cmbat, acpi, acpi_cmbat_driver, acpi_cmbat_devclass, 0, 0);
 MODULE_DEPEND(acpi_cmbat, acpi, 1, 1, 1);
 
+static int
+acpi_cmbat_probe(device_t dev)
+{
+    static char *cmbat_ids[] = { "PNP0C0A", NULL };
+
+    if (acpi_disabled("cmbat") ||
+       ACPI_ID_PROBE(device_get_parent(dev), dev, cmbat_ids) == NULL)
+       return (ENXIO);
+
+    device_set_desc(dev, "ACPI Control Method Battery");
+    return (0);
+}
+
+static int
+acpi_cmbat_attach(device_t dev)
+{
+    int                error;
+    ACPI_HANDLE        handle;
+    struct acpi_cmbat_softc *sc;
+
+    sc = device_get_softc(dev);
+    handle = acpi_get_handle(dev);
+    sc->dev = dev;
+
+    timespecclear(&sc->bst_lastupdated);
+
+    error = acpi_battery_register(dev);
+    if (error != 0) {
+       device_printf(dev, "registering battery failed\n");
+       return (error);
+    }
+
+    /*
+     * Install a system notify handler in addition to the device notify.
+     * Toshiba notebook uses this alternate notify for its battery.
+     */
+    AcpiInstallNotifyHandler(handle, ACPI_ALL_NOTIFY,
+       acpi_cmbat_notify_handler, dev);
+
+    AcpiOsExecute(OSL_NOTIFY_HANDLER, acpi_cmbat_init_battery, dev);
+
+    return (0);
+}
+
+static int
+acpi_cmbat_detach(device_t dev)
+{
+    ACPI_HANDLE        handle;
+
+    handle = acpi_get_handle(dev);
+    AcpiRemoveNotifyHandler(handle, ACPI_ALL_NOTIFY, acpi_cmbat_notify_handler);
+    acpi_battery_remove(dev);
+    return (0);
+}
+
+static int
+acpi_cmbat_resume(device_t dev)
+{
+
+    AcpiOsExecute(OSL_NOTIFY_HANDLER, acpi_cmbat_init_battery, dev);
+    return (0);
+}
+
+static void
+acpi_cmbat_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context)
+{
+    struct acpi_cmbat_softc *sc;
+    device_t dev;
+
+    dev = (device_t)context;
+    sc = device_get_softc(dev);
+
+    switch (notify) {
+    case ACPI_NOTIFY_DEVICE_CHECK:
+    case ACPI_BATTERY_BST_CHANGE:
+       /*
+        * Clear the last updated time.  The next call to retrieve the
+        * battery status will get the new value for us.
+        */
+       timespecclear(&sc->bst_lastupdated);
+       break;
+    case ACPI_NOTIFY_BUS_CHECK:
+    case ACPI_BATTERY_BIF_CHANGE:
+       /*
+        * Queue a callback to get the current battery info from thread
+        * context.  It's not safe to block in a notify handler.
+        */
+       AcpiOsExecute(OSL_NOTIFY_HANDLER, acpi_cmbat_get_bif_task, dev);
+       break;
+    }
+
+    acpi_UserNotify("CMBAT", h, notify);
+}
+
 static int
 acpi_cmbat_info_expired(struct timespec *lastupdated)
 {
     struct timespec    curtime;
 
     if (lastupdated == NULL)
-       return (1);
+       return (TRUE);
     if (!timespecisset(lastupdated))
-       return (1);
+       return (TRUE);
 
     getnanotime(&curtime);
     timespecsub(&curtime, lastupdated);
@@ -128,7 +217,6 @@ acpi_cmbat_info_expired(struct timespec *lastupdated)
            curtime.tv_sec > acpi_battery_get_info_expire());
 }
 
-
 static void
 acpi_cmbat_info_updated(struct timespec *lastupdated)
 {
@@ -137,27 +225,24 @@ acpi_cmbat_info_updated(struct timespec *lastupdated)
 }
 
 static void
-acpi_cmbat_get_bst(void *context)
+acpi_cmbat_get_bst(void *arg)
 {
-    device_t   dev;
     struct acpi_cmbat_softc *sc;
     ACPI_STATUS        as;
     ACPI_OBJECT        *res;
     ACPI_HANDLE        h;
     ACPI_BUFFER        bst_buffer;
+    device_t dev;
 
-    dev = context;
+    dev = arg;
     sc = device_get_softc(dev);
     h = acpi_get_handle(dev);
+    bst_buffer.Pointer = NULL;
+    bst_buffer.Length = ACPI_ALLOCATE_BUFFER;
 
     if (!acpi_cmbat_info_expired(&sc->bst_lastupdated))
-       return;
-    if (sc->bst_updating)
-       return;
-    sc->bst_updating = 1;
+       goto end;
 
-    bst_buffer.Pointer = NULL;
-    bst_buffer.Length = ACPI_ALLOCATE_BUFFER;
     as = AcpiEvaluateObject(h, "_BST", NULL, &bst_buffer);
     if (ACPI_FAILURE(as)) {
        ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
@@ -183,34 +268,45 @@ acpi_cmbat_get_bst(void *context)
        goto end;
     acpi_cmbat_info_updated(&sc->bst_lastupdated);
 
+    /* XXX If all batteries are critical, perhaps we should suspend. */
+    if (sc->bst.state & ACPI_BATT_STAT_CRITICAL) {
+       if ((sc->flags & ACPI_BATT_STAT_CRITICAL) == 0) {
+           sc->flags |= ACPI_BATT_STAT_CRITICAL;
+           device_printf(dev, "critically low charge!\n");
+       }
+    } else
+       sc->flags &= ~ACPI_BATT_STAT_CRITICAL;
+
 end:
     if (bst_buffer.Pointer != NULL)
        AcpiOsFree(bst_buffer.Pointer);
-    sc->bst_updating = 0;
 }
 
+/* XXX There should be a cleaner way to do this locking. */
 static void
-acpi_cmbat_get_bif(void *context)
+acpi_cmbat_get_bif_task(void *arg)
+{
+    crit_enter();
+    acpi_cmbat_get_bif(arg);
+    crit_exit();
+}
+
+static void
+acpi_cmbat_get_bif(void *arg)
 {
-    device_t   dev;
     struct acpi_cmbat_softc *sc;
     ACPI_STATUS        as;
     ACPI_OBJECT        *res;
     ACPI_HANDLE        h;
     ACPI_BUFFER        bif_buffer;
+    device_t dev;
 
-    dev = context;
+    dev = arg;
     sc = device_get_softc(dev);
     h = acpi_get_handle(dev);
-
-    if (!acpi_cmbat_info_expired(&sc->bif_lastupdated))
-       return;
-    if (sc->bif_updating)
-       return;
-    sc->bif_updating = 1;
-
     bif_buffer.Pointer = NULL;
     bif_buffer.Length = ACPI_ALLOCATE_BUFFER;
+
     as = AcpiEvaluateObject(h, "_BIF", NULL, &bif_buffer);
     if (ACPI_FAILURE(as)) {
        ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
@@ -226,23 +322,23 @@ acpi_cmbat_get_bif(void *context)
        goto end;
     }
 
-    if (acpi_PkgInt32(res,  0, &sc->bif.units) != 0)
+    if (acpi_PkgInt32(res, 0, &sc->bif.units) != 0)
        goto end;
-    if (acpi_PkgInt32(res,  1, &sc->bif.dcap) != 0)
+    if (acpi_PkgInt32(res, 1, &sc->bif.dcap) != 0)
        goto end;
-    if (acpi_PkgInt32(res,  2, &sc->bif.lfcap) != 0)
+    if (acpi_PkgInt32(res, 2, &sc->bif.lfcap) != 0)
        goto end;
-    if (acpi_PkgInt32(res,  3, &sc->bif.btech) != 0)
+    if (acpi_PkgInt32(res, 3, &sc->bif.btech) != 0)
        goto end;
-    if (acpi_PkgInt32(res,  4, &sc->bif.dvol) != 0)
+    if (acpi_PkgInt32(res, 4, &sc->bif.dvol) != 0)
        goto end;
-    if (acpi_PkgInt32(res,  5, &sc->bif.wcap) != 0)
+    if (acpi_PkgInt32(res, 5, &sc->bif.wcap) != 0)
        goto end;
-    if (acpi_PkgInt32(res,  6, &sc->bif.lcap) != 0)
+    if (acpi_PkgInt32(res, 6, &sc->bif.lcap) != 0)
        goto end;
-    if (acpi_PkgInt32(res,  7, &sc->bif.gra1) != 0)
+    if (acpi_PkgInt32(res, 7, &sc->bif.gra1) != 0)
        goto end;
-    if (acpi_PkgInt32(res,  8, &sc->bif.gra2) != 0)
+    if (acpi_PkgInt32(res, 8, &sc->bif.gra2) != 0)
        goto end;
     if (acpi_PkgStr(res,  9, sc->bif.model, ACPI_CMBAT_MAXSTRLEN) != 0)
        goto end;
@@ -252,354 +348,110 @@ acpi_cmbat_get_bif(void *context)
        goto end;
     if (acpi_PkgStr(res, 12, sc->bif.oeminfo, ACPI_CMBAT_MAXSTRLEN) != 0)
        goto end;
-    acpi_cmbat_info_updated(&sc->bif_lastupdated);
 
 end:
     if (bif_buffer.Pointer != NULL)
        AcpiOsFree(bif_buffer.Pointer);
-    sc->bif_updating = 0;
-}
-
-static void
-acpi_cmbat_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context)
-{
-    device_t dev;
-    struct acpi_cmbat_softc    *sc;
-
-    dev = (device_t)context;
-    if ((sc = device_get_softc(dev)) == NULL)
-       return;
-
-    acpi_UserNotify("CMBAT", h, notify);
-
-    switch (notify) {
-    case ACPI_NOTIFY_DEVICE_CHECK:
-    case ACPI_BATTERY_BST_CHANGE:
-       timespecclear(&sc->bst_lastupdated);
-       break;
-    case ACPI_NOTIFY_BUS_CHECK:
-    case ACPI_BATTERY_BIF_CHANGE:
-       timespecclear(&sc->bif_lastupdated);
-       AcpiOsExecute(OSL_NOTIFY_HANDLER, acpi_cmbat_get_bif, dev);
-       break;
-    default:
-       break;
-    }
 }
 
 static int
-acpi_cmbat_probe(device_t dev)
+acpi_cmbat_bif(device_t dev, struct acpi_bif *bifp)
 {
-    if (acpi_get_type(dev) == ACPI_TYPE_DEVICE && !acpi_disabled("cmbat")
-       && acpi_MatchHid(acpi_get_handle(dev), "PNP0C0A")) {
-
-       device_set_desc(dev, "Control Method Battery");
-       return (0);
-    }
-    return (ENXIO);
-}
-
-static int
-acpi_cmbat_attach(device_t dev)
-{
-    int                error;
-    ACPI_HANDLE        handle;
     struct acpi_cmbat_softc *sc;
 
-    if ((sc = device_get_softc(dev)) == NULL)
-       return (ENXIO);
-
-    handle = acpi_get_handle(dev);
+    sc = device_get_softc(dev);
 
     /*
-     * Install a system notify handler in addition to the device notify.
-     * Toshiba notebook uses this alternate notify for its battery.
+     * Just copy the data.  The only value that should change is the
+     * last-full capacity, so we only update when we get a notify that says
+     * the info has changed.  Many systems apparently take a long time to
+     * process a _BIF call so we avoid it if possible.
      */
-    AcpiInstallNotifyHandler(handle, ACPI_SYSTEM_NOTIFY,
-                            acpi_cmbat_notify_handler, dev);
-    AcpiInstallNotifyHandler(handle, ACPI_DEVICE_NOTIFY,
-                            acpi_cmbat_notify_handler, dev);
-
-    sc->bif_updating = sc->bst_updating = 0;
-    sc->dev = dev;
-
-    timespecclear(&sc->bif_lastupdated);
-    timespecclear(&sc->bst_lastupdated);
-
-    if (acpi_cmbat_units == 0) {
-       error = acpi_register_ioctl(ACPIIO_CMBAT_GET_BIF,
-                                   acpi_cmbat_ioctl, NULL);
-       if (error != 0)
-           return (error);
-       error = acpi_register_ioctl(ACPIIO_CMBAT_GET_BST,
-                                   acpi_cmbat_ioctl, NULL);
-       if (error != 0)
-               return (error);
-    }
-
-    error = acpi_battery_register(ACPI_BATT_TYPE_CMBAT, acpi_cmbat_units);
-    if (error != 0)
-       return (error);
-
-    acpi_cmbat_units++;
-    timespecclear(&acpi_cmbat_info_lastupdated);
-    sc->initializing = 0;
-    AcpiOsExecute(OSL_NOTIFY_HANDLER, acpi_cmbat_init_battery, dev);
+    crit_enter();
+    bifp->units = sc->bif.units;
+    bifp->dcap = sc->bif.dcap;
+    bifp->lfcap = sc->bif.lfcap;
+    bifp->btech = sc->bif.btech;
+    bifp->dvol = sc->bif.dvol;
+    bifp->wcap = sc->bif.wcap;
+    bifp->lcap = sc->bif.lcap;
+    bifp->gra1 = sc->bif.gra1;
+    bifp->gra2 = sc->bif.gra2;
+    strncpy(bifp->model, sc->bif.model, sizeof(sc->bif.model));
+    strncpy(bifp->serial, sc->bif.serial, sizeof(sc->bif.serial));
+    strncpy(bifp->type, sc->bif.type, sizeof(sc->bif.type));
+    strncpy(bifp->oeminfo, sc->bif.oeminfo, sizeof(sc->bif.oeminfo));
+    crit_exit();
 
     return (0);
 }
 
 static int
-acpi_cmbat_resume(device_t dev)
+acpi_cmbat_bst(device_t dev, struct acpi_bst *bstp)
 {
-    AcpiOsExecute(OSL_NOTIFY_HANDLER, acpi_cmbat_init_battery, dev);
-    return (0);
-}
-
-static int
-acpi_cmbat_ioctl(u_long cmd, caddr_t addr, void *arg)
-{
-    device_t   dev;
-    union acpi_battery_ioctl_arg *ioctl_arg;
     struct acpi_cmbat_softc *sc;
-    struct acpi_bif    *bifp;
-    struct acpi_bst    *bstp;
 
-    ioctl_arg = (union acpi_battery_ioctl_arg *)addr;
-    dev = devclass_get_device(acpi_cmbat_devclass, ioctl_arg->unit);
-    if (dev == NULL)
-       return (ENXIO);
     sc = device_get_softc(dev);
-    if (sc == NULL)
-       return (ENXIO);
 
-    /*
-     * No security check required: information retrieval only.  If
-     * new functions are added here, a check might be required.
-     */
-    switch (cmd) {
-    case ACPIIO_CMBAT_GET_BIF:
-       acpi_cmbat_get_bif(dev);
-       bifp = &ioctl_arg->bif;
-       bifp->units = sc->bif.units;
-       bifp->dcap = sc->bif.dcap;
-       bifp->lfcap = sc->bif.lfcap;
-       bifp->btech = sc->bif.btech;
-       bifp->dvol = sc->bif.dvol;
-       bifp->wcap = sc->bif.wcap;
-       bifp->lcap = sc->bif.lcap;
-       bifp->gra1 = sc->bif.gra1;
-       bifp->gra2 = sc->bif.gra2;
-       strncpy(bifp->model, sc->bif.model, sizeof(sc->bif.model));
-       strncpy(bifp->serial, sc->bif.serial, sizeof(sc->bif.serial));
-       strncpy(bifp->type, sc->bif.type, sizeof(sc->bif.type));
-       strncpy(bifp->oeminfo, sc->bif.oeminfo, sizeof(sc->bif.oeminfo));
-       break;
-    case ACPIIO_CMBAT_GET_BST:
-       bstp = &ioctl_arg->bst;
-       if (acpi_BatteryIsPresent(dev)) {
-           acpi_cmbat_get_bst(dev);
-           bstp->state = sc->bst.state;
-           bstp->rate = sc->bst.rate;
-           bstp->cap = sc->bst.cap;
-           bstp->volt = sc->bst.volt;
-       } else {
-           bstp->state = ACPI_BATT_STAT_NOT_PRESENT;
-       }
-       break;
-    default:
-       break;
-    }
+    crit_enter();
+    if (acpi_BatteryIsPresent(dev)) {
+       acpi_cmbat_get_bst(dev);
+       bstp->state = sc->bst.state;
+       bstp->rate = sc->bst.rate;
+       bstp->cap = sc->bst.cap;
+       bstp->volt = sc->bst.volt;
+    } else
+       bstp->state = ACPI_BATT_STAT_NOT_PRESENT;
+    crit_exit();
 
     return (0);
 }
 
-static int
-acpi_cmbat_is_bst_valid(struct acpi_bst *bst)
-{
-    if (bst->state >= ACPI_BATT_STAT_MAX || bst->cap == 0xffffffff ||
-       bst->volt == 0xffffffff)
-
-       return (0);
-    else
-       return (1);
-}
-
-static int
-acpi_cmbat_is_bif_valid(struct acpi_bif *bif)
-{
-    if (bif->lfcap == 0)
-       return (0);
-    else
-       return (1);
-}
-
-static int
-acpi_cmbat_get_total_battinfo(struct acpi_battinfo *battinfo)
-{
-    int                i;
-    int                error;
-    int                batt_stat;
-    int                valid_rate, valid_units;
-    int                cap, min;
-    int                total_cap, total_min, total_full;
-    device_t   dev;
-    struct acpi_cmbat_softc *sc;
-    static int bat_units = 0;
-    static struct acpi_cmbat_softc **bat = NULL;
-
-    cap = min = -1;
-    batt_stat = ACPI_BATT_STAT_NOT_PRESENT;
-    error = 0;
-
-    /* Allocate array of softc pointers */
-    if (bat_units != acpi_cmbat_units) {
-       if (bat != NULL) {
-           kfree(bat, M_ACPICMBAT);
-           bat = NULL;
-       }
-       bat_units = 0;
-    }
-    if (bat == NULL) {
-       bat_units = acpi_cmbat_units;
-       bat = kmalloc(sizeof(struct acpi_cmbat_softc *) * bat_units,
-                    M_ACPICMBAT, M_INTWAIT);
-
-       /* Collect softc pointers */
-       for (i = 0; i < acpi_cmbat_units; i++) {
-           if ((dev = devclass_get_device(acpi_cmbat_devclass, i)) == NULL) {
-               error = ENXIO;
-               goto out;
-           }
-           if ((sc = device_get_softc(dev)) == NULL) {
-               error = ENXIO;
-               goto out;
-           }
-           bat[i] = sc;
-       }
-    }
-
-    /* Get battery status, valid rate and valid units */
-    batt_stat = valid_rate = valid_units = 0;
-    for (i = 0; i < acpi_cmbat_units; i++) {
-       bat[i]->present = acpi_BatteryIsPresent(bat[i]->dev);
-       if (!bat[i]->present)
-           continue;
-
-       acpi_cmbat_get_bst(bat[i]->dev);
-
-       /* If battery not installed, we get strange values */
-       if (!acpi_cmbat_is_bst_valid(&(bat[i]->bst)) ||
-           !acpi_cmbat_is_bif_valid(&(bat[i]->bif))) {
-
-           bat[i]->present = 0;
-           continue;
-       }
-
-       valid_units++;
-       bat[i]->cap = 100 * bat[i]->bst.cap / bat[i]->bif.lfcap;
-       batt_stat |= bat[i]->bst.state;
-
-       if (bat[i]->bst.rate > 0) {
-           /*
-            * XXX Hack to calculate total battery time.
-            * Systems with 2 or more battries, they may get used
-            * one by one, thus bst.rate is set only to the one
-            * in use. For remaining batteries bst.rate = 0, which
-            * makes it impossible to calculate remaining time.
-            * Some other systems may need sum of bst.rate in
-            * dis-charging state.
-            * There for we sum up the bst.rate that is valid
-            * (in dis-charging state), and use the sum to
-            * calcutate remaining batteries' time.
-            */
-           if (bat[i]->bst.state & ACPI_BATT_STAT_DISCHARG)
-               valid_rate += bat[i]->bst.rate;
-       }
-    }
-
-    /* Calculate total battery capacity and time */
-    total_cap = total_min = total_full = 0;
-    for (i = 0; i < acpi_cmbat_units; i++) {
-       if (!bat[i]->present)
-           continue;
-
-       if (valid_rate > 0) {
-           /* Use the sum of bst.rate */
-           bat[i]->min = 60 * bat[i]->bst.cap / valid_rate;
-       } else if (bat[i]->full_charge_time > 0) {
-           bat[i]->min = (bat[i]->full_charge_time * bat[i]->cap) / 100;
-       } else {
-           /* Couldn't find valid rate and full battery time */
-           bat[i]->min = 0;
-       }
-       total_min += bat[i]->min;
-       total_cap += bat[i]->cap;
-       total_full += bat[i]->full_charge_time;
-    }
-
-    /* Battery life */
-    if (valid_units == 0) {
-       cap = -1;
-       batt_stat = ACPI_BATT_STAT_NOT_PRESENT;
-    } else {
-       cap = total_cap / valid_units;
-    }
-
-    /* Battery time */
-    if (valid_units == 0) {
-       min = -1;
-    } else if (valid_rate == 0 || (batt_stat & ACPI_BATT_STAT_CHARGING)) {
-       if (total_full == 0)
-           min = -1;
-       else
-           min = (total_full * cap) / 100;
-    } else {
-       min = total_min;
-    }
-    acpi_cmbat_info_updated(&acpi_cmbat_info_lastupdated);
-
-out:
-    battinfo->cap = cap;
-    battinfo->min = min;
-    battinfo->state = batt_stat;
-
-    return (error);
-}
-
 static void
 acpi_cmbat_init_battery(void *arg)
 {
-    int                retry;
-    device_t   dev = (device_t)arg;
-    struct acpi_cmbat_softc *sc = device_get_softc(dev);
-
-    if (sc->initializing)
-       return;
+    struct acpi_cmbat_softc *sc;
+    int                retry, valid;
+    device_t   dev;
 
-    sc->initializing = 1;
+    dev = (device_t)arg;
+    sc = device_get_softc(dev);
     ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
                "battery initialization start\n");
 
-    for (retry = 0; retry < ACPI_CMBAT_RETRY_MAX; retry++, AcpiOsSleep(10)) {
-       sc->present = acpi_BatteryIsPresent(dev);
-       if (!sc->present)
+    /*
+     * Try repeatedly to get valid data from the battery.  Since the
+     * embedded controller isn't always ready just after boot, we may have
+     * to wait a while.
+     */
+    for (retry = 0; retry < ACPI_CMBAT_RETRY_MAX; retry++, AcpiOsSleep(10000)) {
+       /* batteries on DOCK can be ejected w/ DOCK during retrying */
+       if (!device_is_attached(dev))
+           return;
+
+       if (!acpi_BatteryIsPresent(dev))
            continue;
 
-       timespecclear(&sc->bst_lastupdated);
-       timespecclear(&sc->bif_lastupdated);
+       /*
+        * Only query the battery if this is the first try or the specific
+        * type of info is still invalid.
+        */
+       crit_enter();
+       if (retry == 0 || !acpi_battery_bst_valid(&sc->bst)) {
+           timespecclear(&sc->bst_lastupdated);
+           acpi_cmbat_get_bst(dev);
+       }
+       if (retry == 0 || !acpi_battery_bif_valid(&sc->bif))
+           acpi_cmbat_get_bif(dev);
 
-       acpi_cmbat_get_bst(dev);
-       if (!acpi_cmbat_is_bst_valid(&sc->bst))
-           continue;
+       valid = acpi_battery_bst_valid(&sc->bst) &&
+           acpi_battery_bif_valid(&sc->bif);
+       crit_exit();
 
-       acpi_cmbat_get_bif(dev);
-       if (!acpi_cmbat_is_bif_valid(&sc->bif))
-           continue;
-       break;
+       if (valid)
+           break;
     }
 
-    sc->initializing = 0;
     if (retry == ACPI_CMBAT_RETRY_MAX) {
        ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
                    "battery initialization failed, giving up\n");
@@ -608,51 +460,3 @@ acpi_cmbat_init_battery(void *arg)
                    "battery initialization done, tried %d times\n", retry + 1);
     }
 }
-
-/*
- * Public interfaces.
- */
-int
-acpi_cmbat_get_battinfo(int unit, struct acpi_battinfo *battinfo)
-{
-    int                error;
-    device_t   dev;
-    struct acpi_cmbat_softc *sc;
-
-    if (unit == -1)
-       return (acpi_cmbat_get_total_battinfo(battinfo));
-
-    if (acpi_cmbat_info_expired(&acpi_cmbat_info_lastupdated)) {
-       error = acpi_cmbat_get_total_battinfo(battinfo);
-       if (error)
-           goto out;
-    }
-
-    error = 0;
-    if (unit >= acpi_cmbat_units) {
-       error = ENXIO;
-       goto out;
-    }
-
-    if ((dev = devclass_get_device(acpi_cmbat_devclass, unit)) == NULL) {
-       error = ENXIO;
-       goto out;
-    }
-    if ((sc = device_get_softc(dev)) == NULL) {
-       error = ENXIO;
-       goto out;
-    }
-
-    if (!sc->present) {
-       battinfo->cap = -1;
-       battinfo->min = -1;
-       battinfo->state = ACPI_BATT_STAT_NOT_PRESENT;
-    } else {
-       battinfo->cap = sc->cap;
-       battinfo->min = sc->min;
-       battinfo->state = sc->bst.state;
-    }
-
-out:
-    return (error);
-}
index ab8fc0f..9eedf3f 100644 (file)
@@ -23,7 +23,7 @@
 # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 # SUCH DAMAGE.
 #
-# $DragonFly: src/sys/dev/acpica5/acpi_if.m,v 1.2 2008/09/05 10:28:35 hasso Exp $
+# $DragonFly: src/sys/dev/acpica5/acpi_if.m,v 1.3 2008/09/29 06:59:45 hasso Exp $
 #
 
 #include <sys/bus.h>
 
 INTERFACE acpi;
 
+#
+# Callback function for each child handle traversed in acpi_scan_children().
+#
+# ACPI_HANDLE h:  current child device being considered
+#
+# device_t *dev:  pointer to the child's original device_t or NULL if there
+#   was none.  The callback should store a new device in *dev if it has
+#   created one.  The method implementation will automatically clean up the
+#   previous device and properly associate the current ACPI_HANDLE with it.
+#
+# level:  current level being scanned
+#
+# void *arg:  argument passed in original call to acpi_scan_children()
+#
+# Returns:  AE_OK if the scan should continue, otherwise an error
+#
+HEADER {
+       typedef ACPI_STATUS (*acpi_scan_cb_t)(ACPI_HANDLE h, device_t *dev,
+           int level, void *arg);
+
+       struct acpi_bif;
+       struct acpi_bst;
+};
+
 #
 # Default implementation for acpi_id_probe().
 #
@@ -103,3 +127,26 @@ METHOD int ec_write {
        ACPI_INTEGER    val;
        int             width;
 };
+
+#
+# Get battery information (_BIF format)
+#
+# device_t dev:  Battery device
+# struct acpi_bif *bif:  Pointer to storage for _BIF results
+#
+METHOD int batt_get_info {
+       device_t        dev;
+       struct acpi_bif *bif;
+};
+
+#
+# Get battery status (_BST format)
+#
+# device_t dev:  Battery device
+# struct acpi_bst *bst:  Pointer to storage for _BST results
+#
+METHOD int batt_get_status {
+       device_t        dev;
+       struct acpi_bst *bst;
+};
+
index a73df69..d9b7cc5 100644 (file)
@@ -25,7 +25,7 @@
  * SUCH DAMAGE.
  *
  *     $FreeBSD: src/sys/dev/acpica/acpiio.h,v 1.9 2003/09/13 20:13:01 njl Exp $
- *     $DragonFly: src/sys/dev/acpica5/acpiio.h,v 1.1 2004/02/21 06:48:08 dillon Exp $
+ *     $DragonFly: src/sys/dev/acpica5/acpiio.h,v 1.2 2008/09/29 06:59:45 hasso Exp $
  */
 
 /*
 #define ACPIIO_DISABLE         _IO('P', 2)
 #define ACPIIO_SETSLPSTATE     _IOW('P', 3, int)
 
-struct acpi_battdesc {
-    int         type;                          /* battery type: e.g. CMBAT */
-    int         phys_unit;                     /* physical unit of devclass */
-};
-
-#define ACPI_BATT_TYPE_CMBAT           0x0000
-#define ACPI_BATT_TYPE_SMBAT           0x0001
-
 struct acpi_battinfo {
-    int         cap;                           /* percent */
-    int         min;                           /* remianing time */
-    int         state;                         /* battery state */
+    int  cap;                          /* percent */
+    int  min;                          /* remaining time (in minutes) */
+    int  state;                                /* battery state */
+    int  rate;                         /* emptying rate */
 };
 
 #define ACPI_CMBAT_MAXSTRLEN 32
 struct acpi_bif {
-    u_int32_t units;                   /* 0 for mWh, 1 for mAh */
-    u_int32_t dcap;                    /* Design Capacity */
-    u_int32_t lfcap;                   /* Last Full capacity */
-    u_int32_t btech;                   /* Battery Technology */
-    u_int32_t dvol;                    /* Design voltage (mV) */
-    u_int32_t wcap;                    /* WARN capacity */
-    u_int32_t lcap;                    /* Low capacity */
-    u_int32_t gra1;                    /* Granulity 1(Warn to Low) */
-    u_int32_t gra2;                    /* Granulity 2(Full to Warn) */
+    uint32_t units;                    /* Units (mW or mA). */
+#define ACPI_BIF_UNITS_MW       0      /* Capacity in mWh, rate in mW. */
+#define ACPI_BIF_UNITS_MA       1      /* Capacity in mAh, rate in mA. */
+    uint32_t dcap;                     /* Design Capacity */
+    uint32_t lfcap;                    /* Last Full capacity */
+    uint32_t btech;                    /* Battery Technology */
+    uint32_t dvol;                     /* Design voltage (mV) */
+    uint32_t wcap;                     /* WARN capacity */
+    uint32_t lcap;                     /* Low capacity */
+    uint32_t gra1;                     /* Granularity 1 (Warn to Low) */
+    uint32_t gra2;                     /* Granularity 2 (Full to Warn) */
     char model[ACPI_CMBAT_MAXSTRLEN];  /* model identifier */
     char serial[ACPI_CMBAT_MAXSTRLEN]; /* Serial number */
     char type[ACPI_CMBAT_MAXSTRLEN];   /* Type */
@@ -67,10 +62,10 @@ struct acpi_bif {
 };
 
 struct acpi_bst {
-    u_int32_t state;                   /* Battery State */
-    u_int32_t rate;                    /* Present Rate */
-    u_int32_t cap;                     /* Remaining Capacity */
-    u_int32_t volt;                    /* Present Voltage */
+    uint32_t state;                    /* Battery State */
+    uint32_t rate;                     /* Present Rate */
+    uint32_t cap;                      /* Remaining Capacity */
+    uint32_t volt;                     /* Present Voltage */
 };
 
 #define ACPI_BATT_STAT_DISCHARG                0x0001
@@ -80,25 +75,28 @@ struct acpi_bst {
 #define ACPI_BATT_STAT_MAX             0x0007
 
 union acpi_battery_ioctl_arg {
-    int         unit;                  /* argument: logical unit (-1 = overall) */
+    int                         unit;  /* Device unit or ACPI_BATTERY_ALL_UNITS. */
 
-    struct acpi_battdesc       battdesc; 
-    struct acpi_battinfo       battinfo; 
+    struct acpi_battinfo battinfo;
 
-    struct acpi_bif    bif;    /* for acpi_cmbat */
-    struct acpi_bst    bst;    /* for acpi_cmbat */
+    struct acpi_bif     bif;
+    struct acpi_bst     bst;
 };
 
-/* Common battery ioctl */
-#define ACPIIO_BATT_GET_UNITS    _IOR('B', 0x01, int)
-#define ACPIIO_BATT_GET_TYPE     _IOR('B', 0x02, union acpi_battery_ioctl_arg)
+#define ACPI_BATTERY_ALL_UNITS  (-1)
+#define ACPI_BATT_UNKNOWN       0xffffffff /* _BST or _BIF value unknown. */
+
+/* Common battery ioctls */
+#define ACPIIO_BATT_GET_UNITS     _IOR('B', 0x01, int)
 #define ACPIIO_BATT_GET_BATTINFO _IOWR('B', 0x03, union acpi_battery_ioctl_arg)
-#define ACPIIO_BATT_GET_BATTDESC _IOWR('B', 0x04, union acpi_battery_ioctl_arg)
+#define ACPIIO_BATT_GET_BIF      _IOWR('B', 0x10, union acpi_battery_ioctl_arg)
+#define ACPIIO_BATT_GET_BST      _IOWR('B', 0x11, union acpi_battery_ioctl_arg)
 
-/* Cotrol Method battery ioctl */
-#define ACPIIO_CMBAT_GET_BIF    _IOWR('B', 0x10, union acpi_battery_ioctl_arg)
-#define ACPIIO_CMBAT_GET_BST    _IOWR('B', 0x11, union acpi_battery_ioctl_arg)
+/* Control Method battery ioctls (deprecated) */
+#define ACPIIO_CMBAT_GET_BIF     ACPIIO_BATT_GET_BIF
+#define ACPIIO_CMBAT_GET_BST     ACPIIO_BATT_GET_BST
 
+/* Get AC adapter status. */
 #define ACPIIO_ACAD_GET_STATUS   _IOR('A', 1, int)
 
 #ifdef _KERNEL
index a21e797..120afd1 100644 (file)
@@ -26,7 +26,7 @@
  * SUCH DAMAGE.
  *
  * $FreeBSD: src/sys/dev/acpica/acpivar.h,v 1.71 2004/06/13 22:52:30 njl Exp $
- * $DragonFly: src/sys/dev/acpica5/acpivar.h,v 1.15 2008/09/05 10:28:35 hasso Exp $
+ * $DragonFly: src/sys/dev/acpica5/acpivar.h,v 1.16 2008/09/29 06:59:45 hasso Exp $
  */
 
 #include "acpi_if.h"
@@ -344,15 +344,15 @@ int               acpi_sleep_machdep(struct acpi_softc *sc, int state);
 
 /* Battery Abstraction. */
 struct acpi_battinfo;
-struct acpi_battdesc;
 
-int            acpi_battery_register(int, int);
-int            acpi_battery_get_battinfo(int, struct acpi_battinfo *);
+int            acpi_battery_register(device_t dev);
+int            acpi_battery_remove(device_t dev);
 int            acpi_battery_get_units(void);
 int            acpi_battery_get_info_expire(void);
-int            acpi_battery_get_battdesc(int, struct acpi_battdesc *);
-
-int            acpi_cmbat_get_battinfo(int, struct acpi_battinfo *);
+int            acpi_battery_bst_valid(struct acpi_bst *bst);
+int            acpi_battery_bif_valid(struct acpi_bif *bif);
+int            acpi_battery_get_battinfo(device_t dev,
+                   struct acpi_battinfo *info);
 
 /* Embedded controller. */
 void           acpi_ec_ecdt_probe(device_t);
index 4bda885..0cfe755 100644 (file)
@@ -24,7 +24,7 @@
  * SUCH DAMAGE.
  *
  * $FreeBSD: src/sys/kern/subr_bus.c,v 1.54.2.9 2002/10/10 15:13:32 jhb Exp $
- * $DragonFly: src/sys/kern/subr_bus.c,v 1.43 2008/09/05 10:28:35 hasso Exp $
+ * $DragonFly: src/sys/kern/subr_bus.c,v 1.44 2008/09/29 06:59:45 hasso Exp $
  */
 
 #include "opt_bus.h"
@@ -398,6 +398,23 @@ devclass_get_drivers(devclass_t dc, driver_t ***listp, int *countp)
         return (0);
 }
 
+/**
+ * @brief Get the number of devices in a devclass
+ *
+ * @param dc           the devclass to examine
+ */
+int
+devclass_get_count(devclass_t dc)
+{
+       int count, i;
+
+       count = 0;
+       for (i = 0; i < dc->maxunit; i++)
+               if (dc->devices[i])
+                       count++;
+       return (count);
+}
+
 int
 devclass_get_maxunit(devclass_t dc)
 {
index 7d181d7..dee90ae 100644 (file)
@@ -24,7 +24,7 @@
  * SUCH DAMAGE.
  *
  * $FreeBSD: src/sys/i386/acpica/acpi_machdep.c,v 1.20 2004/05/05 19:51:15 njl Exp $
- * $DragonFly: src/sys/platform/pc32/acpica5/acpi_machdep.c,v 1.13 2007/01/17 17:31:19 y0netan1 Exp $
+ * $DragonFly: src/sys/platform/pc32/acpica5/acpi_machdep.c,v 1.14 2008/09/29 06:59:45 hasso Exp $
  */
 
 #include <sys/param.h>
@@ -143,7 +143,7 @@ acpi_capm_get_info(apm_info_t aip)
        else
                aip->ai_acline = acline;        /* on/off */
 
-       if (acpi_battery_get_battinfo(-1, &batt)) {
+       if (acpi_battery_get_battinfo(NULL, &batt)) {
                aip->ai_batt_stat = 0xff;       /* unknown */
                aip->ai_batt_life = 0xff;       /* unknown */
                aip->ai_batt_time = -1;         /* unknown */
@@ -161,8 +161,8 @@ acpi_capm_get_info(apm_info_t aip)
 static int
 acpi_capm_get_pwstatus(apm_pwstatus_t app)
 {
-       int     batt_unit;
-       int     acline;
+       device_t dev;
+       int     acline, unit, error;
        struct  acpi_battinfo batt;
 
        if (app->ap_device != PMDV_ALLDEV &&
@@ -170,11 +170,16 @@ acpi_capm_get_pwstatus(apm_pwstatus_t app)
                return (1);
 
        if (app->ap_device == PMDV_ALLDEV)
-               batt_unit = -1;                 /* all units */
-       else
-               batt_unit = app->ap_device - PMDV_BATT0;
-
-       if (acpi_battery_get_battinfo(batt_unit, &batt))
+               error = acpi_battery_get_battinfo(NULL, &batt);
+       else {
+               unit = app->ap_device - PMDV_BATT0;
+               dev = devclass_get_device(devclass_find("battery"), unit);
+               if (dev != NULL)
+                       error = acpi_battery_get_battinfo(dev, &batt);
+               else
+                       error = ENXIO;
+       }
+       if (error)
                return (1);
 
        app->ap_batt_stat = acpi_capm_convert_battstate(&batt);
index 38402c2..5e7369c 100644 (file)
@@ -24,7 +24,7 @@
  * SUCH DAMAGE.
  *
  * $FreeBSD: src/sys/sys/bus.h,v 1.30.2.5 2004/03/17 17:54:25 njl Exp $
- * $DragonFly: src/sys/sys/bus.h,v 1.28 2008/09/05 10:28:35 hasso Exp $
+ * $DragonFly: src/sys/sys/bus.h,v 1.29 2008/09/29 06:59:45 hasso Exp $
  */
 
 #ifndef _SYS_BUS_H_
@@ -353,6 +353,7 @@ device_t    devclass_get_device(devclass_t dc, int unit);
 void   *devclass_get_softc(devclass_t dc, int unit);
 int    devclass_get_devices(devclass_t dc, device_t **listp, int *countp);
 int    devclass_get_drivers(devclass_t dc, driver_t ***listp, int *countp);
+int    devclass_get_count(devclass_t dc);
 int    devclass_get_maxunit(devclass_t dc);
 void   devclass_set_parent(devclass_t dc, devclass_t pdc);
 devclass_t     devclass_get_parent(devclass_t dc);
index 6539911..600dac6 100644 (file)
@@ -25,7 +25,7 @@
  *
  *     $Id: acpiconf.c,v 1.5 2000/08/08 14:12:19 iwasaki Exp $
  *     $FreeBSD: src/usr.sbin/acpi/acpiconf/acpiconf.c,v 1.14 2004/03/05 02:48:22 takawata Exp $
- *     $DragonFly: src/usr.sbin/acpi/acpiconf/acpiconf.c,v 1.2 2005/12/05 01:04:00 swildner Exp $
+ *     $DragonFly: src/usr.sbin/acpi/acpiconf/acpiconf.c,v 1.3 2008/09/29 06:59:45 hasso Exp $
  */
 
 #include <sys/param.h>
 
 static int     acpifd;
 
-static int
+static void
 acpi_init(void)
 {
        acpifd = open(ACPIDEV, O_RDWR);
-       if (acpifd == -1){
+       if (acpifd == -1)
                acpifd = open(ACPIDEV, O_RDONLY);
-       }
-       if (acpifd == -1){
+       if (acpifd == -1)
                err(EX_OSFILE, ACPIDEV);
-       }
 }
 
 static int
@@ -99,38 +97,100 @@ acpi_sleep(int sleep_type)
        return (0);
 }
 
+/* should be a acpi define, but doesn't appear to be */
+#define UNKNOWN_CAP 0xffffffff
+#define UNKNOWN_VOLTAGE 0xffffffff
+
 static int
 acpi_battinfo(int num)
 {
        union acpi_battery_ioctl_arg battio;
        const char *pwr_units;
+       int hours, min;
 
        if (num < 0 || num > 64)
                err(EX_USAGE, "invalid battery %d", num);
 
+       /* Print battery design information. */
        battio.unit = num;
-       if (ioctl(acpifd, ACPIIO_CMBAT_GET_BIF, &battio) == -1)
+       if (ioctl(acpifd, ACPIIO_BATT_GET_BIF, &battio) == -1)
                err(EX_IOERR, "get battery info (%d) failed", num);
-       printf("Battery %d information\n", num);
        if (battio.bif.units == 0)
-               pwr_units = "mWh";
+               pwr_units = "mW";
        else
-               pwr_units = "mAh";
+               pwr_units = "mA";
 
-       printf("Design capacity:\t%d %s\n", battio.bif.dcap, pwr_units);
-       printf("Last full capacity:\t%d %s\n", battio.bif.lfcap, pwr_units);
+       if (battio.bif.dcap == UNKNOWN_CAP)
+               printf("Design capacity:\tunknown\n");
+       else
+               printf("Design capacity:\t%d %sh\n", battio.bif.dcap,
+                   pwr_units);
+       if (battio.bif.lfcap == UNKNOWN_CAP)
+               printf("Last full capacity:\tunknown\n");
+       else
+               printf("Last full capacity:\t%d %sh\n", battio.bif.lfcap,
+                   pwr_units);
        printf("Technology:\t\t%s\n", battio.bif.btech == 0 ?
            "primary (non-rechargeable)" : "secondary (rechargeable)");
-       printf("Design voltage:\t\t%d mV\n", battio.bif.dvol);
-       printf("Capacity (warn):\t%d %s\n", battio.bif.wcap, pwr_units);
-       printf("Capacity (low):\t\t%d %s\n", battio.bif.lcap, pwr_units);
-       printf("Low/warn granularity:\t%d %s\n", battio.bif.gra1, pwr_units);
-       printf("Warn/full granularity:\t%d %s\n", battio.bif.gra2, pwr_units);
+       if (battio.bif.dvol == UNKNOWN_CAP)
+               printf("Design voltage:\t\tunknown\n");
+       else
+               printf("Design voltage:\t\t%d mV\n", battio.bif.dvol);
+       printf("Capacity (warn):\t%d %sh\n", battio.bif.wcap, pwr_units);
+       printf("Capacity (low):\t\t%d %sh\n", battio.bif.lcap, pwr_units);
+       printf("Low/warn granularity:\t%d %sh\n", battio.bif.gra1, pwr_units);
+       printf("Warn/full granularity:\t%d %sh\n", battio.bif.gra2, pwr_units);
        printf("Model number:\t\t%s\n", battio.bif.model);
        printf("Serial number:\t\t%s\n", battio.bif.serial);
        printf("Type:\t\t\t%s\n", battio.bif.type);
        printf("OEM info:\t\t%s\n", battio.bif.oeminfo);
 
+       /* Print current battery state information. */
+       battio.unit = num;
+       if (ioctl(acpifd, ACPIIO_BATT_GET_BATTINFO, &battio) == -1)
+               err(EX_IOERR, "get battery user info (%d) failed", num);
+       if (battio.battinfo.state != ACPI_BATT_STAT_NOT_PRESENT) {
+               printf("State:\t\t\t");
+               if (battio.battinfo.state == 0)
+                       printf("high ");
+               if (battio.battinfo.state & ACPI_BATT_STAT_CRITICAL)
+                       printf("critical ");
+               if (battio.battinfo.state & ACPI_BATT_STAT_DISCHARG)
+                       printf("discharging ");
+               if (battio.battinfo.state & ACPI_BATT_STAT_CHARGING)
+                       printf("charging ");
+               printf("\n");
+               if (battio.battinfo.cap == -1)
+                       printf("Remaining capacity:\tunknown\n");
+               else
+                       printf("Remaining capacity:\t%d%%\n",
+                           battio.battinfo.cap);
+               if (battio.battinfo.min == -1)
+                       printf("Remaining time:\t\tunknown\n");
+               else {
+                       hours = battio.battinfo.min / 60;
+                       min = battio.battinfo.min % 60;
+                       printf("Remaining time:\t\t%d:%02d\n", hours, min);
+               }
+               if (battio.battinfo.rate == -1)
+                       printf("Present rate:\t\tunknown\n");
+               else
+                       printf("Present rate:\t\t%d %s\n",
+                           battio.battinfo.rate, pwr_units);
+       } else
+               printf("State:\t\t\tnot present\n");
+
+       /* Print battery voltage information. */
+       battio.unit = num;
+       if (ioctl(acpifd, ACPIIO_BATT_GET_BST, &battio) == -1)
+               err(EX_IOERR, "get battery status (%d) failed", num);
+       if (battio.bst.state != ACPI_BATT_STAT_NOT_PRESENT) {
+               if (battio.bst.volt == UNKNOWN_VOLTAGE)
+                       printf("Voltage:\t\tunknown\n");
+               else
+                       printf("Voltage:\t\t%d mV\n", battio.bst.volt);
+       }
+
        return (0);
 }