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);