From 1cb806cc9e5fbdc3e914ad946522d64a8237ff6a Mon Sep 17 00:00:00 2001 From: njl Date: Sat, 23 Jul 2005 19:36:00 +0000 Subject: [PATCH] Rewrite the acpi_battery interface to allow for other battery types (i.e., smart battery) and fix various bugs found during the cleanup. API changes: * kernel access: Access to individual batteries is now via devclass_find("battery"). Introduce new methods ACPI_BATT_GET_STATUS (for _BST-formatted data) and ACPI_BATT_GET_INFO (for _BIF-formatted data). The helper function acpi_battery_get_battinfo() now takes a device_t instead of a unit # argument. If dev is NULL, this signifies all batteries. * ioctl access: The ACPIIO_BATT_GET_TYPE and ACPIIO_BATT_GET_BATTDESC ioctls have been removed. Since there is now no need for a mapping between "virtual" unit and physical unit, usermode programs can just specify the unit directly and skip the old translation steps. In fact, acpiconf(8) was actually already doing this and virtual unit was the same as physical unit in all cases since there was previously only one battery type (acpi_cmbat). Additionally, we now map the ACPIIO_BATT_GET_BIF and ACPIIO_BATT_GET_BST ioctls for all batteries, if they provide the associated methods. * apm compatibility device/ioctls: no change * sysctl: no change Since most third-party applications use the apm(4) compat interface, there should be very few affected applications (if any). Reviewed by: bruno MFC after: 5 days --- sys/dev/acpica/acpi_battery.c | 396 ++++++++++++++++--------- sys/dev/acpica/acpi_cmbat.c | 517 ++++++++++----------------------- sys/dev/acpica/acpi_if.m | 25 ++ sys/dev/acpica/acpiio.h | 44 ++- sys/dev/acpica/acpivar.h | 16 +- sys/i386/acpica/acpi_machdep.c | 21 +- 6 files changed, 474 insertions(+), 545 deletions(-) diff --git a/sys/dev/acpica/acpi_battery.c b/sys/dev/acpica/acpi_battery.c index 6857e9195aa0..06fd60d63405 100644 --- a/sys/dev/acpica/acpi_battery.c +++ b/sys/dev/acpica/acpi_battery.c @@ -1,4 +1,5 @@ /*- + * Copyright (c) 2005 Nate Lawson * Copyright (c) 2000 Mitsuru IWASAKI * All rights reserved. * @@ -39,96 +40,248 @@ __FBSDID("$FreeBSD$"); #include #include -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; -static int acpi_batteries_units; -static int acpi_battery_info_expire = 5; -static struct acpi_battinfo acpi_battery_battinfo; ACPI_SERIAL_DECL(battery, "ACPI generic battery"); -int -acpi_battery_get_info_expire(void) -{ - return (acpi_battery_info_expire); -} +static void acpi_reset_battinfo(struct acpi_battinfo *info); +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; -int -acpi_battery_get_battdesc(int unit, struct acpi_battdesc *battdesc) -{ - struct acpi_batteries *bp; - int error, i; - - error = ENXIO; + error = 0; ACPI_SERIAL_BEGIN(battery); - if (unit < 0 || unit >= acpi_batteries_units) - goto out; - - i = 0; - TAILQ_FOREACH(bp, &acpi_batteries, link) { - if (unit == i) { - battdesc->type = bp->battdesc.type; - battdesc->phys_unit = bp->battdesc.phys_unit; - error = 0; - break; - } - i++; - } - -out: + if (!acpi_batteries_initted) + error = acpi_battery_init(); ACPI_SERIAL_END(battery); return (error); } int -acpi_battery_get_battinfo(int unit, struct acpi_battinfo *battinfo) +acpi_battery_remove(device_t dev) { - struct acpi_battdesc battdesc; - int error; - 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) + return (0); +} + +int +acpi_battery_get_units(void) +{ + devclass_t batt_dc; + + batt_dc = devclass_find("battery"); + if (batt_dc == NULL) + return (0); + return (devclass_get_count(batt_dc)); +} + +int +acpi_battery_get_info_expire(void) +{ + + return (acpi_battery_info_expire); +} + +/* Check _BST results for validity. */ +int +acpi_battery_bst_valid(struct acpi_bst *bst) +{ + if (bst->state >= ACPI_BATT_STAT_MAX || bst->cap == 0xffffffff || + bst->volt == 0xffffffff) + return (FALSE); + else + return (TRUE); +} + +/* Check _BIF results for validity. */ +int +acpi_battery_bif_valid(struct acpi_bif *bif) +{ + if (bif->lfcap == 0) + return (FALSE); + else + return (TRUE); +} + +/* 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 number of devices. If there are none + * or error, return immediately. + */ + batt_dc = devclass_find("battery"); + if (batt_dc == NULL) + return (ENXIO); + devcount = devclass_get_count(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 = malloc(devcount * sizeof(*bst), M_TEMP, M_WAITOK); + bi = malloc(devcount * sizeof(*bi), M_TEMP, M_WAITOK); + bif = malloc(sizeof(*bif), M_TEMP, M_WAITOK); + + /* + * 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++) { + /* Find the device. If it disappeared, the user can try again. */ + batt_dev = devclass_get_device(batt_dc, i); + if (batt_dev == NULL) { + error = ENOMEM; goto out; - - switch (battdesc.type) { - case ACPI_BATT_TYPE_CMBAT: - error = acpi_cmbat_get_battinfo(battdesc.phys_unit, battinfo); - break; - default: - error = ENXIO; - break; } + + /* Default info for every battery is "not present". */ + acpi_reset_battinfo(&bi[i]); + + /* 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. */ + 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 state and calculate percent capacity remaining. */ + valid_units++; + batt_stat |= bst[i].state; + bi[i].state = bst[i].state; + 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 > 0 && (bst[i].state & ACPI_BATT_STAT_DISCHARG)) + valid_rate += bst[i].rate; } + /* If the caller asked for a device but we didn't find it, error. */ + if (dev != NULL && dev_idx < 0) { + 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; + 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; + } + } else + acpi_reset_battinfo(battinfo); + + error = 0; + out: + if (bi) + free(bi, M_TEMP); + if (bif) + free(bif, M_TEMP); + if (bst) + free(bst, M_TEMP); return (error); } +static void +acpi_reset_battinfo(struct acpi_battinfo *info) +{ + info->cap = -1; + info->min = -1; + info->state = ACPI_BATT_STAT_NOT_PRESENT; + info->rate = -1; +} + static int acpi_battery_ioctl(u_long cmd, caddr_t addr, void *arg) { union acpi_battery_ioctl_arg *ioctl_arg; int error, unit; + device_t dev; + error = ENXIO; ioctl_arg = (union acpi_battery_ioctl_arg *)addr; - error = 0; + unit = ioctl_arg->unit; + if (unit != ACPI_BATTERY_ALL_UNITS) + dev = devclass_get_device(devclass_find("battery"), unit); + else + dev = NULL; /* * No security check required: information retrieval only. If @@ -138,17 +291,20 @@ acpi_battery_ioctl(u_long cmd, caddr_t addr, void *arg) case ACPIIO_BATT_GET_UNITS: *(int *)addr = acpi_battery_get_units(); break; - case ACPIIO_BATT_GET_BATTDESC: - unit = ioctl_arg->unit; - error = acpi_battery_get_battdesc(unit, &ioctl_arg->battdesc); - break; case ACPIIO_BATT_GET_BATTINFO: - unit = ioctl_arg->unit; - error = acpi_battery_get_battinfo(unit, &ioctl_arg->battinfo); + if (dev != NULL || unit == ACPI_BATTERY_ALL_UNITS) + error = acpi_battery_get_battinfo(dev, &ioctl_arg->battinfo); + break; + case ACPIIO_BATT_GET_BIF: + if (dev != NULL) + error = ACPI_BATT_GET_INFO(dev, &ioctl_arg->bif); + break; + case ACPIIO_BATT_GET_BST: + if (dev != NULL) + error = ACPI_BATT_GET_STATUS(dev, &ioctl_arg->bst); break; default: error = EINVAL; - break; } return (error); @@ -159,12 +315,22 @@ acpi_battery_sysctl(SYSCTL_HANDLER_ARGS) { 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) { @@ -180,98 +346,54 @@ acpi_battery_init(void) goto out; sc = device_get_softc(dev); - TAILQ_INIT(&acpi_batteries); - - /* XXX We should back out registered ioctls on error. */ error = acpi_register_ioctl(ACPIIO_BATT_GET_UNITS, acpi_battery_ioctl, NULL); if (error != 0) goto out; - error = acpi_register_ioctl(ACPIIO_BATT_GET_BATTDESC, acpi_battery_ioctl, - NULL); - if (error != 0) - goto out; error = acpi_register_ioctl(ACPIIO_BATT_GET_BATTINFO, acpi_battery_ioctl, 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) + goto out; - sysctl_ctx_init(&sc->acpi_battery_sysctl_ctx); - sc->acpi_battery_sysctl_tree = SYSCTL_ADD_NODE(&sc->acpi_battery_sysctl_ctx, + 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, ""); - SYSCTL_ADD_PROC(&sc->acpi_battery_sysctl_ctx, - SYSCTL_CHILDREN(sc->acpi_battery_sysctl_tree), + 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), + 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), + 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), + 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", ""); + SYSCTL_ADD_INT(&acpi_battery_sysctl_ctx, + SYSCTL_CHILDREN(acpi_battery_sysctl_tree), OID_AUTO, "info_expire", CTLFLAG_RD | CTLFLAG_RW, &acpi_battery_info_expire, 0, ""); acpi_batteries_initted = TRUE; 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); + } return (error); } - -int -acpi_battery_register(int type, int phys_unit) -{ - struct acpi_batteries *bp; - int error; - - error = 0; - bp = malloc(sizeof(*bp), M_ACPIBATT, M_NOWAIT); - if (bp == NULL) - return (ENOMEM); - - ACPI_SERIAL_BEGIN(battery); - if (!acpi_batteries_initted && (error = acpi_battery_init()) != 0) { - printf("acpi_battery_register failed for unit %d\n", phys_unit); - goto out; - } - bp->battdesc.type = type; - bp->battdesc.phys_unit = phys_unit; - TAILQ_INSERT_TAIL(&acpi_batteries, bp, link); - acpi_batteries_units++; - -out: - ACPI_SERIAL_END(battery); - if (error) - free(bp, M_ACPIBATT); - return (error); -} - -int -acpi_battery_remove(int type, int phys_unit) -{ - struct acpi_batteries *bp, *tmp; - int ret; - - ret = ENOENT; - ACPI_SERIAL_BEGIN(battery); - TAILQ_FOREACH_SAFE(bp, &acpi_batteries, link, tmp) { - if (bp->battdesc.type == type && bp->battdesc.phys_unit == phys_unit) { - TAILQ_REMOVE(&acpi_batteries, bp, link); - acpi_batteries_units--; - ret = 0; - break; - } - } - ACPI_SERIAL_END(battery); - if (ret == 0) - free(bp, M_ACPIBATT); - return (ret); -} diff --git a/sys/dev/acpica/acpi_cmbat.c b/sys/dev/acpica/acpi_cmbat.c index 54e498225060..e60ea17a3909 100644 --- a/sys/dev/acpica/acpi_cmbat.c +++ b/sys/dev/acpica/acpi_cmbat.c @@ -1,4 +1,5 @@ /*- + * Copyright (c) 2005 Nate Lawson * Copyright (c) 2000 Munehiro Matsuda * Copyright (c) 2000 Takanori Watanabe * Copyright (c) 2000 Mitsuru IWASAKI @@ -24,10 +25,11 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ +#include +__FBSDID("$FreeBSD$"); + #include "opt_acpi.h" #include #include @@ -60,41 +62,29 @@ ACPI_MODULE_NAME("BATTERY") 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 flags; - int present; - int cap; - int min; - int full_charge_time; - int initializing; - int phys_unit; }; -static struct timespec acpi_cmbat_info_lastupdated; ACPI_SERIAL_DECL(cmbat, "ACPI cmbat"); -/* 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_detach(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(device_t dev); +static void acpi_cmbat_get_bif(device_t dev); +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 */ @@ -103,11 +93,15 @@ static device_method_t acpi_cmbat_methods[] = { 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), }; @@ -116,6 +110,96 @@ 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->bif_lastupdated); + 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); + + AcpiOsQueueForExecution(OSD_PRIORITY_LO, acpi_cmbat_init_battery, dev); + + return (0); +} + +static int +acpi_cmbat_detach(device_t dev) +{ + + acpi_battery_remove(dev); + return (0); +} + +static int +acpi_cmbat_resume(device_t dev) +{ + + AcpiOsQueueForExecution(OSD_PRIORITY_LO, 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); + + /* + * Clear the appropriate last updated time. The next call to retrieve + * the battery status will get the new value for us. We don't need to + * acquire a lock since we are only clearing the time stamp and since + * calling _BST/_BIF can trigger a notify, we could deadlock also. + */ + 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); + break; + } + + acpi_UserNotify("CMBAT", h, notify); +} + static int acpi_cmbat_info_expired(struct timespec *lastupdated) { @@ -145,9 +229,8 @@ acpi_cmbat_info_updated(struct timespec *lastupdated) } static void -acpi_cmbat_get_bst(void *context) +acpi_cmbat_get_bst(device_t dev) { - device_t dev; struct acpi_cmbat_softc *sc; ACPI_STATUS as; ACPI_OBJECT *res; @@ -156,7 +239,6 @@ acpi_cmbat_get_bst(void *context) ACPI_SERIAL_ASSERT(cmbat); - dev = context; sc = device_get_softc(dev); h = acpi_get_handle(dev); bst_buffer.Pointer = NULL; @@ -205,9 +287,8 @@ acpi_cmbat_get_bst(void *context) } static void -acpi_cmbat_get_bif(void *context) +acpi_cmbat_get_bif(device_t dev) { - device_t dev; struct acpi_cmbat_softc *sc; ACPI_STATUS as; ACPI_OBJECT *res; @@ -216,7 +297,6 @@ acpi_cmbat_get_bif(void *context) ACPI_SERIAL_ASSERT(cmbat); - dev = context; sc = device_get_softc(dev); h = acpi_get_handle(dev); bif_buffer.Pointer = NULL; @@ -273,319 +353,59 @@ acpi_cmbat_get_bif(void *context) AcpiOsFree(bif_buffer.Pointer); } -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; - sc = device_get_softc(dev); - - acpi_UserNotify("CMBAT", h, notify); - - /* - * Clear the appropriate last updated time. The next call to retrieve - * the battery status will get the new value for us. We don't need to - * acquire a lock since we are only clearing the time stamp and since - * calling _BST/_BIF can trigger a notify, we could deadlock also. - */ - 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); - break; - default: - break; - } -} - 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, "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; - - /* - * 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); - - ACPI_SERIAL_BEGIN(cmbat); - 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) { - device_printf(dev, "register bif ioctl failed\n"); - return (error); - } - error = acpi_register_ioctl(ACPIIO_CMBAT_GET_BST, - acpi_cmbat_ioctl, NULL); - if (error != 0) { - device_printf(dev, "register bst ioctl failed\n"); - return (error); - } - } - - sc->phys_unit = acpi_cmbat_units; - error = acpi_battery_register(ACPI_BATT_TYPE_CMBAT, sc->phys_unit); - if (error != 0) { - device_printf(dev, "registering battery %d failed\n", sc->phys_unit); - return (error); - } - acpi_cmbat_units++; - timespecclear(&acpi_cmbat_info_lastupdated); - ACPI_SERIAL_END(cmbat); - - AcpiOsQueueForExecution(OSD_PRIORITY_LO, acpi_cmbat_init_battery, dev); - - return (0); -} - -static int -acpi_cmbat_detach(device_t dev) +acpi_cmbat_bif(device_t dev, struct acpi_bif *bifp) { struct acpi_cmbat_softc *sc; sc = device_get_softc(dev); + ACPI_SERIAL_BEGIN(cmbat); - acpi_battery_remove(ACPI_BATT_TYPE_CMBAT, sc->phys_unit); - acpi_cmbat_units--; - ACPI_SERIAL_END(cmbat); - return (0); -} - -static int -acpi_cmbat_resume(device_t dev) -{ - AcpiOsQueueForExecution(OSD_PRIORITY_LO, 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); - - /* - * No security check required: information retrieval only. If - * new functions are added here, a check might be required. - */ - ACPI_SERIAL_BEGIN(cmbat); - 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; - } + acpi_cmbat_get_bif(dev); + 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)); ACPI_SERIAL_END(cmbat); return (0); } static int -acpi_cmbat_is_bst_valid(struct acpi_bst *bst) +acpi_cmbat_bst(device_t dev, struct acpi_bst *bstp) { - if (bst->state >= ACPI_BATT_STAT_MAX || bst->cap == 0xffffffff || - bst->volt == 0xffffffff) - return (FALSE); - else - return (TRUE); -} - -static int -acpi_cmbat_is_bif_valid(struct acpi_bif *bif) -{ - if (bif->lfcap == 0) - return (FALSE); - else - return (TRUE); -} - -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; struct acpi_cmbat_softc *sc; - ACPI_SERIAL_ASSERT(cmbat); + sc = device_get_softc(dev); - cap = min = -1; - batt_stat = ACPI_BATT_STAT_NOT_PRESENT; - error = 0; - - /* Get battery status, valid rate and valid units */ - batt_stat = valid_rate = valid_units = 0; - for (i = 0; i < acpi_cmbat_units; i++) { - sc = devclass_get_softc(acpi_cmbat_devclass, i); - if (sc == NULL) - continue; - sc->present = acpi_BatteryIsPresent(sc->dev); - if (!sc->present) - continue; - acpi_cmbat_get_bst(sc->dev); - - /* If battery not installed, we get strange values */ - if (!acpi_cmbat_is_bst_valid(&sc->bst) || - !acpi_cmbat_is_bif_valid(&sc->bif)) { - sc->present = FALSE; - continue; - } - - valid_units++; - sc->cap = 100 * sc->bst.cap / sc->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 (sc->cap > 100) - sc->cap = 100; - - batt_stat |= sc->bst.state; - - /* - * XXX Hack to calculate total battery time. - * - * On systems with more than one battery, they may get used - * sequentially, thus bst.rate may only signify the one in use. - * For the remaining batteries, bst.rate will be zero, which - * makes it impossible to calculate the remaining time. Some - * other systems may need the sum of all the bst.rate values - * when discharging. Therefore, we sum the bst.rate for valid - * batteries (ones in the discharging state) and use the sum - * to calculate the total remaining time. - */ - if (sc->bst.rate > 0) { - if (sc->bst.state & ACPI_BATT_STAT_DISCHARG) - valid_rate += sc->bst.rate; - } - } - - /* Calculate total battery capacity and time */ - total_cap = total_min = total_full = 0; - for (i = 0; i < acpi_cmbat_units; i++) { - sc = devclass_get_softc(acpi_cmbat_devclass, i); - if (!sc->present) - continue; - - /* - * If any batteries are discharging, use the sum of the bst.rate - * values. Otherwise, use the full charge time to estimate - * remaining time. If neither are available, assume no charge. - */ - if (valid_rate > 0) - sc->min = 60 * sc->bst.cap / valid_rate; - else if (sc->full_charge_time > 0) - sc->min = (sc->full_charge_time * sc->cap) / 100; - else - sc->min = 0; - total_min += sc->min; - total_cap += sc->cap; - total_full += sc->full_charge_time; - } - - /* Battery life */ - if (valid_units == 0) { - cap = -1; - batt_stat = ACPI_BATT_STAT_NOT_PRESENT; + ACPI_SERIAL_BEGIN(cmbat); + 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 - cap = total_cap / valid_units; + bstp->state = ACPI_BATT_STAT_NOT_PRESENT; + ACPI_SERIAL_END(cmbat); - /* 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); - - battinfo->cap = cap; - battinfo->min = min; - battinfo->state = batt_stat; - - return (error); + return (0); } static void acpi_cmbat_init_battery(void *arg) { struct acpi_cmbat_softc *sc; - int retry; + int retry, valid; device_t dev; dev = (device_t)arg; @@ -593,9 +413,13 @@ acpi_cmbat_init_battery(void *arg) ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev), "battery initialization start\n"); + /* + * 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)) { - sc->present = acpi_BatteryIsPresent(dev); - if (!sc->present) + if (!acpi_BatteryIsPresent(dev)) continue; ACPI_SERIAL_BEGIN(cmbat); @@ -603,10 +427,11 @@ acpi_cmbat_init_battery(void *arg) timespecclear(&sc->bif_lastupdated); acpi_cmbat_get_bst(dev); acpi_cmbat_get_bif(dev); + valid = acpi_battery_bst_valid(&sc->bst) && + acpi_battery_bif_valid(&sc->bif); ACPI_SERIAL_END(cmbat); - if (acpi_cmbat_is_bst_valid(&sc->bst) && - acpi_cmbat_is_bif_valid(&sc->bif)) + if (valid) break; } @@ -618,39 +443,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; - struct acpi_cmbat_softc *sc; - - ACPI_SERIAL_BEGIN(cmbat); - error = acpi_cmbat_get_total_battinfo(battinfo); - if (unit == -1 || error) - goto out; - - error = ENXIO; - if (unit >= acpi_cmbat_units) - goto out; - if ((sc = devclass_get_softc(acpi_cmbat_devclass, unit)) == NULL) - 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; - } - error = 0; - -out: - ACPI_SERIAL_END(cmbat); - return (error); -} diff --git a/sys/dev/acpica/acpi_if.m b/sys/dev/acpica/acpi_if.m index 8c7706c3ceb4..51c479cc24a5 100644 --- a/sys/dev/acpica/acpi_if.m +++ b/sys/dev/acpica/acpi_if.m @@ -51,6 +51,9 @@ INTERFACE acpi; HEADER { typedef ACPI_STATUS (*acpi_scan_cb_t)(ACPI_HANDLE h, device_t *dev, int level, void *arg); + + struct acpi_bif; + struct acpi_bst; }; # @@ -197,3 +200,25 @@ 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; +}; diff --git a/sys/dev/acpica/acpiio.h b/sys/dev/acpica/acpiio.h index 42894f29d477..aa18c385bdf4 100644 --- a/sys/dev/acpica/acpiio.h +++ b/sys/dev/acpica/acpiio.h @@ -35,31 +35,24 @@ */ #define ACPIIO_SETSLPSTATE _IOW('P', 3, int) -struct acpi_battdesc { - int type; /* battery type */ - 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; /* 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; /* Granularity 1 (Warn to Low) */ - u_int32_t gra2; /* Granularity 2 (Full to Warn) */ + uint32_t units; /* 0 for mWh, 1 for mAh */ + 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 +60,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,20 +73,19 @@ 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; struct acpi_bst bst; }; +#define ACPI_BATTERY_ALL_UNITS (-1) + /* Common battery ioctls */ #define ACPIIO_BATT_GET_UNITS _IOR('B', 0x01, int) -#define ACPIIO_BATT_GET_TYPE _IOR('B', 0x02, union acpi_battery_ioctl_arg) #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) diff --git a/sys/dev/acpica/acpivar.h b/sys/dev/acpica/acpivar.h index e8431e9730e2..9c0547d24daf 100644 --- a/sys/dev/acpica/acpivar.h +++ b/sys/dev/acpica/acpivar.h @@ -74,9 +74,6 @@ struct acpi_softc { bus_dmamap_t acpi_wakemap; vm_offset_t acpi_wakeaddr; vm_paddr_t acpi_wakephys; - - struct sysctl_ctx_list acpi_battery_sysctl_ctx; - struct sysctl_oid *acpi_battery_sysctl_tree; }; struct acpi_device { @@ -393,16 +390,15 @@ int acpi_machdep_quirks(int *quirks); /* Battery Abstraction. */ struct acpi_battinfo; -struct acpi_battdesc; -int acpi_battery_register(int, int); -int acpi_battery_remove(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); diff --git a/sys/i386/acpica/acpi_machdep.c b/sys/i386/acpica/acpi_machdep.c index f98a6fb80f0d..8b2efd0ba4ca 100644 --- a/sys/i386/acpica/acpi_machdep.c +++ b/sys/i386/acpica/acpi_machdep.c @@ -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) != 0) { aip->ai_batt_stat = APM_UNKNOWN; aip->ai_batt_life = APM_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);