/* $NetBSD: acpi_bat.c,v 1.64 2007/12/09 20:27:52 jmcneill Exp $ */ /*- * Copyright (c) 2003 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Charles M. Hannum of By Noon Software, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT 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. */ /* * Copyright 2001 Bill Sommerfeld. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed for the NetBSD Project by * Wasabi Systems, Inc. * 4. The name of Wasabi Systems, Inc. may not be used to endorse * or promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT 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. */ #if 0 #define ACPI_BAT_DEBUG #endif /* * ACPI Battery Driver. * * ACPI defines two different battery device interfaces: "Control * Method" batteries, in which AML methods are defined in order to get * battery status and set battery alarm thresholds, and a "Smart * Battery" device, which is an SMbus device accessed through the ACPI * Embedded Controller device. * * This driver is for the "Control Method"-style battery only. */ #include __KERNEL_RCSID(0, "$NetBSD: acpi_bat.c,v 1.64 2007/12/09 20:27:52 jmcneill Exp $"); #include #include #include /* for hz */ #include #include #include #include #include #include /* sensor indexes */ #define ACPIBAT_PRESENT 0 #define ACPIBAT_DCAPACITY 1 #define ACPIBAT_LFCCAPACITY 2 #define ACPIBAT_TECHNOLOGY 3 #define ACPIBAT_DVOLTAGE 4 #define ACPIBAT_WCAPACITY 5 #define ACPIBAT_LCAPACITY 6 #define ACPIBAT_VOLTAGE 7 #define ACPIBAT_CHARGERATE 8 #define ACPIBAT_DISCHARGERATE 9 #define ACPIBAT_CAPACITY 10 #define ACPIBAT_CHARGING 11 #define ACPIBAT_CHARGE_STATE 12 #define ACPIBAT_NSENSORS 13 /* number of sensors */ struct acpibat_softc { struct acpi_devnode *sc_node; /* our ACPI devnode */ int sc_flags; /* see below */ int sc_available; /* available information level */ struct sysmon_envsys *sc_sme; envsys_data_t sc_sensor[ACPIBAT_NSENSORS]; kmutex_t sc_mtx; struct timeval sc_lastupdate, sc_updateinterval; }; static const char * const bat_hid[] = { "PNP0C0A", NULL }; /* * These flags are used to examine the battery device data returned from * the ACPI interface, specifically the "battery status" */ #define ACPIBAT_PWRUNIT_MA 0x00000001 /* mA not mW */ /* * These flags are used to examine the battery charge/discharge/critical * state returned from a get-status command. */ #define ACPIBAT_ST_DISCHARGING 0x00000001 /* battery is discharging */ #define ACPIBAT_ST_CHARGING 0x00000002 /* battery is charging */ #define ACPIBAT_ST_CRITICAL 0x00000004 /* battery is critical */ /* * Flags for battery status from _STA return */ #define ACPIBAT_STA_PRESENT 0x00000010 /* battery present */ /* * These flags are used to set internal state in our softc. */ #define ABAT_F_VERBOSE 0x01 /* verbose events */ #define ABAT_F_PWRUNIT_MA 0x02 /* mA instead of mW */ #define ABAT_F_PRESENT 0x04 /* is the battery present? */ #define ABAT_SET(sc, f) (void)((sc)->sc_flags |= (f)) #define ABAT_CLEAR(sc, f) (void)((sc)->sc_flags &= ~(f)) #define ABAT_ISSET(sc, f) ((sc)->sc_flags & (f)) /* * Available info level */ #define ABAT_ALV_NONE 0 /* none is available */ #define ABAT_ALV_PRESENCE 1 /* presence info is available */ #define ABAT_ALV_INFO 2 /* battery info is available */ #define ABAT_ALV_STAT 3 /* battery status is available */ static int acpibat_match(device_t, struct cfdata *, void *); static void acpibat_attach(device_t, struct device *, void *); CFATTACH_DECL_NEW(acpibat, sizeof(struct acpibat_softc), acpibat_match, acpibat_attach, NULL, NULL); static void acpibat_clear_presence(struct acpibat_softc *); static void acpibat_clear_info(struct acpibat_softc *); static void acpibat_clear_stat(struct acpibat_softc *); static int acpibat_battery_present(device_t); static ACPI_STATUS acpibat_get_status(device_t); static ACPI_STATUS acpibat_get_info(device_t); static void acpibat_print_info(device_t); static void acpibat_print_stat(device_t); static void acpibat_update(void *); static void acpibat_init_envsys(device_t); static void acpibat_notify_handler(ACPI_HANDLE, UINT32, void *); static void acpibat_refresh(struct sysmon_envsys *, envsys_data_t *); /* * acpibat_match: * * Autoconfiguration `match' routine. */ static int acpibat_match(device_t parent, struct cfdata *match, void *aux) { struct acpi_attach_args *aa = aux; if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE) return 0; return acpi_match_hid(aa->aa_node->ad_devinfo, bat_hid); } /* * acpibat_attach: * * Autoconfiguration `attach' routine. */ static void acpibat_attach(device_t parent, device_t self, void *aux) { struct acpibat_softc *sc = device_private(self); struct acpi_attach_args *aa = aux; ACPI_STATUS rv; aprint_naive(": ACPI Battery (Control Method)\n"); aprint_normal(": ACPI Battery (Control Method)\n"); sc->sc_node = aa->aa_node; mutex_init(&sc->sc_mtx, MUTEX_DEFAULT, IPL_NONE); rv = AcpiInstallNotifyHandler(sc->sc_node->ad_handle, ACPI_ALL_NOTIFY, acpibat_notify_handler, self); if (ACPI_FAILURE(rv)) { aprint_error_dev(self, "unable to register DEVICE/SYSTEM NOTIFY handler: %s\n", AcpiFormatException(rv)); return; } #ifdef ACPI_BAT_DEBUG ABAT_SET(sc, ABAT_F_VERBOSE); #endif if (!pmf_device_register(self, NULL, NULL)) aprint_error_dev(self, "couldn't establish power handler\n"); acpibat_init_envsys(self); } /* * clear informations */ static void acpibat_clear_presence(struct acpibat_softc *sc) { acpibat_clear_info(sc); sc->sc_available = ABAT_ALV_NONE; ABAT_CLEAR(sc, ABAT_F_PRESENT); } static void acpibat_clear_info(struct acpibat_softc *sc) { acpibat_clear_stat(sc); if (sc->sc_available > ABAT_ALV_PRESENCE) sc->sc_available = ABAT_ALV_PRESENCE; sc->sc_sensor[ACPIBAT_DCAPACITY].state = ENVSYS_SINVALID; sc->sc_sensor[ACPIBAT_LFCCAPACITY].state = ENVSYS_SINVALID; sc->sc_sensor[ACPIBAT_CAPACITY].state = ENVSYS_SINVALID; sc->sc_sensor[ACPIBAT_TECHNOLOGY].state = ENVSYS_SINVALID; sc->sc_sensor[ACPIBAT_DVOLTAGE].state = ENVSYS_SINVALID; sc->sc_sensor[ACPIBAT_WCAPACITY].state = ENVSYS_SINVALID; sc->sc_sensor[ACPIBAT_LCAPACITY].state = ENVSYS_SINVALID; } static void acpibat_clear_stat(struct acpibat_softc *sc) { if (sc->sc_available > ABAT_ALV_INFO) sc->sc_available = ABAT_ALV_INFO; sc->sc_sensor[ACPIBAT_CHARGERATE].state = ENVSYS_SINVALID; sc->sc_sensor[ACPIBAT_DISCHARGERATE].state = ENVSYS_SINVALID; sc->sc_sensor[ACPIBAT_CAPACITY].state = ENVSYS_SINVALID; sc->sc_sensor[ACPIBAT_VOLTAGE].state = ENVSYS_SINVALID; sc->sc_sensor[ACPIBAT_CHARGING].state = ENVSYS_SINVALID; } /* * returns 0 for no battery, 1 for present, and -1 on error */ static int acpibat_battery_present(device_t dv) { struct acpibat_softc *sc = device_private(dv); uint32_t sta; ACPI_INTEGER val; ACPI_STATUS rv; rv = acpi_eval_integer(sc->sc_node->ad_handle, "_STA", &val); if (ACPI_FAILURE(rv)) { aprint_error_dev(dv, "failed to evaluate _STA: %s\n", AcpiFormatException(rv)); return -1; } sta = (uint32_t)val; mutex_enter(&sc->sc_mtx); sc->sc_available = ABAT_ALV_PRESENCE; if (sta & ACPIBAT_STA_PRESENT) { ABAT_SET(sc, ABAT_F_PRESENT); sc->sc_sensor[ACPIBAT_PRESENT].state = ENVSYS_SVALID; sc->sc_sensor[ACPIBAT_PRESENT].value_cur = 1; } else sc->sc_sensor[ACPIBAT_PRESENT].value_cur = 0; mutex_exit(&sc->sc_mtx); return (sta & ACPIBAT_STA_PRESENT) ? 1 : 0; } /* * acpibat_get_info * * Get, and possibly display, the battery info. */ static ACPI_STATUS acpibat_get_info(device_t dv) { struct acpibat_softc *sc = device_private(dv); ACPI_OBJECT *p1, *p2; ACPI_STATUS rv; ACPI_BUFFER buf; int capunit, rateunit; rv = acpi_eval_struct(sc->sc_node->ad_handle, "_BIF", &buf); if (ACPI_FAILURE(rv)) { aprint_error_dev(dv, "failed to evaluate _BIF: %s\n", AcpiFormatException(rv)); return rv; } p1 = (ACPI_OBJECT *)buf.Pointer; if (p1->Type != ACPI_TYPE_PACKAGE) { aprint_error_dev(dv, "expected PACKAGE, got %d\n", p1->Type); goto out; } if (p1->Package.Count < 13) { aprint_error_dev(dv, "expected 13 elements, got %d\n", p1->Package.Count); goto out; } p2 = p1->Package.Elements; mutex_enter(&sc->sc_mtx); if ((p2[0].Integer.Value & ACPIBAT_PWRUNIT_MA) != 0) { ABAT_SET(sc, ABAT_F_PWRUNIT_MA); capunit = ENVSYS_SAMPHOUR; rateunit = ENVSYS_SAMPS; } else { ABAT_CLEAR(sc, ABAT_F_PWRUNIT_MA); capunit = ENVSYS_SWATTHOUR; rateunit = ENVSYS_SWATTS; } sc->sc_sensor[ACPIBAT_DCAPACITY].units = capunit; sc->sc_sensor[ACPIBAT_LFCCAPACITY].units = capunit; sc->sc_sensor[ACPIBAT_WCAPACITY].units = capunit; sc->sc_sensor[ACPIBAT_LCAPACITY].units = capunit; sc->sc_sensor[ACPIBAT_CHARGERATE].units = rateunit; sc->sc_sensor[ACPIBAT_DISCHARGERATE].units = rateunit; sc->sc_sensor[ACPIBAT_CAPACITY].units = capunit; sc->sc_sensor[ACPIBAT_DCAPACITY].value_cur = p2[1].Integer.Value * 1000; sc->sc_sensor[ACPIBAT_DCAPACITY].state = ENVSYS_SVALID; sc->sc_sensor[ACPIBAT_LFCCAPACITY].value_cur = p2[2].Integer.Value * 1000; sc->sc_sensor[ACPIBAT_LFCCAPACITY].state = ENVSYS_SVALID; sc->sc_sensor[ACPIBAT_CAPACITY].value_max = p2[2].Integer.Value * 1000; sc->sc_sensor[ACPIBAT_TECHNOLOGY].value_cur = p2[3].Integer.Value; sc->sc_sensor[ACPIBAT_TECHNOLOGY].state = ENVSYS_SVALID; sc->sc_sensor[ACPIBAT_DVOLTAGE].value_cur = p2[4].Integer.Value * 1000; sc->sc_sensor[ACPIBAT_DVOLTAGE].state = ENVSYS_SVALID; sc->sc_sensor[ACPIBAT_WCAPACITY].value_cur = p2[5].Integer.Value * 1000; sc->sc_sensor[ACPIBAT_WCAPACITY].value_max = p2[2].Integer.Value * 1000; sc->sc_sensor[ACPIBAT_WCAPACITY].state = ENVSYS_SVALID; sc->sc_sensor[ACPIBAT_WCAPACITY].flags |= (ENVSYS_FPERCENT|ENVSYS_FVALID_MAX); sc->sc_sensor[ACPIBAT_LCAPACITY].value_cur = p2[6].Integer.Value * 1000; sc->sc_sensor[ACPIBAT_LCAPACITY].value_max = p2[2].Integer.Value * 1000; sc->sc_sensor[ACPIBAT_LCAPACITY].state = ENVSYS_SVALID; sc->sc_sensor[ACPIBAT_LCAPACITY].flags |= (ENVSYS_FPERCENT|ENVSYS_FVALID_MAX); sc->sc_available = ABAT_ALV_INFO; mutex_exit(&sc->sc_mtx); aprint_verbose_dev(dv, "battery info: %s, %s, %s", p2[12].String.Pointer, p2[11].String.Pointer, p2[9].String.Pointer); if (p2[10].String.Pointer) aprint_verbose(" %s", p2[10].String.Pointer); aprint_verbose("\n"); rv = AE_OK; out: AcpiOsFree(buf.Pointer); return rv; } /* * acpibat_get_status: * * Get, and possibly display, the current battery line status. */ static ACPI_STATUS acpibat_get_status(device_t dv) { struct acpibat_softc *sc = device_private(dv); int status, battrate; ACPI_OBJECT *p1, *p2; ACPI_STATUS rv; ACPI_BUFFER buf; rv = acpi_eval_struct(sc->sc_node->ad_handle, "_BST", &buf); if (ACPI_FAILURE(rv)) { aprint_error_dev(dv, "failed to evaluate _BST: %s\n", AcpiFormatException(rv)); return rv; } p1 = (ACPI_OBJECT *)buf.Pointer; if (p1->Type != ACPI_TYPE_PACKAGE) { aprint_error_dev(dv, "expected PACKAGE, got %d\n", p1->Type); rv = AE_ERROR; goto out; } if (p1->Package.Count < 4) { aprint_error_dev(dv, "expected 4 elts, got %d\n", p1->Package.Count); rv = AE_ERROR; goto out; } p2 = p1->Package.Elements; mutex_enter(&sc->sc_mtx); status = p2[0].Integer.Value; battrate = p2[1].Integer.Value; if (status & ACPIBAT_ST_CHARGING) { sc->sc_sensor[ACPIBAT_CHARGERATE].state = ENVSYS_SVALID; sc->sc_sensor[ACPIBAT_CHARGERATE].value_cur = battrate * 1000; sc->sc_sensor[ACPIBAT_DISCHARGERATE].state = ENVSYS_SINVALID; sc->sc_sensor[ACPIBAT_CHARGING].state = ENVSYS_SVALID; sc->sc_sensor[ACPIBAT_CHARGING].value_cur = 1; } else if (status & ACPIBAT_ST_DISCHARGING) { sc->sc_sensor[ACPIBAT_DISCHARGERATE].state = ENVSYS_SVALID; sc->sc_sensor[ACPIBAT_DISCHARGERATE].value_cur = battrate * 1000; sc->sc_sensor[ACPIBAT_CHARGERATE].state = ENVSYS_SINVALID; sc->sc_sensor[ACPIBAT_CHARGING].state = ENVSYS_SVALID; sc->sc_sensor[ACPIBAT_CHARGING].value_cur = 0; } else if (!(status & (ACPIBAT_ST_CHARGING|ACPIBAT_ST_DISCHARGING))) { sc->sc_sensor[ACPIBAT_CHARGING].state = ENVSYS_SVALID; sc->sc_sensor[ACPIBAT_CHARGING].value_cur = 0; sc->sc_sensor[ACPIBAT_CHARGERATE].state = ENVSYS_SINVALID; sc->sc_sensor[ACPIBAT_DISCHARGERATE].state = ENVSYS_SINVALID; } sc->sc_sensor[ACPIBAT_CHARGE_STATE].value_cur = ENVSYS_BATTERY_CAPACITY_NORMAL; sc->sc_sensor[ACPIBAT_CAPACITY].value_cur = p2[2].Integer.Value * 1000; sc->sc_sensor[ACPIBAT_CAPACITY].state = ENVSYS_SVALID; sc->sc_sensor[ACPIBAT_CAPACITY].flags |= (ENVSYS_FPERCENT|ENVSYS_FVALID_MAX); sc->sc_sensor[ACPIBAT_VOLTAGE].value_cur = p2[3].Integer.Value * 1000; sc->sc_sensor[ACPIBAT_VOLTAGE].state = ENVSYS_SVALID; if (sc->sc_sensor[ACPIBAT_CAPACITY].value_cur < sc->sc_sensor[ACPIBAT_WCAPACITY].value_cur) { sc->sc_sensor[ACPIBAT_CAPACITY].state = ENVSYS_SWARNUNDER; sc->sc_sensor[ACPIBAT_CHARGE_STATE].value_cur = ENVSYS_BATTERY_CAPACITY_WARNING; } if (sc->sc_sensor[ACPIBAT_CAPACITY].value_cur < sc->sc_sensor[ACPIBAT_LCAPACITY].value_cur) { sc->sc_sensor[ACPIBAT_CAPACITY].state = ENVSYS_SCRITUNDER; sc->sc_sensor[ACPIBAT_CHARGE_STATE].value_cur = ENVSYS_BATTERY_CAPACITY_LOW; } if (status & ACPIBAT_ST_CRITICAL) { sc->sc_sensor[ACPIBAT_CAPACITY].state = ENVSYS_SCRITICAL; sc->sc_sensor[ACPIBAT_CHARGE_STATE].value_cur = ENVSYS_BATTERY_CAPACITY_CRITICAL; } mutex_exit(&sc->sc_mtx); rv = AE_OK; out: AcpiOsFree(buf.Pointer); return rv; } #define SCALE(x) ((x)/1000000), (((x)%1000000)/1000) #define CAPUNITS(sc) (ABAT_ISSET((sc), ABAT_F_PWRUNIT_MA)?"Ah":"Wh") #define RATEUNITS(sc) (ABAT_ISSET((sc), ABAT_F_PWRUNIT_MA)?"A":"W") static void acpibat_print_info(device_t dv) { struct acpibat_softc *sc = device_private(dv); const char *tech; if (sc->sc_sensor[ACPIBAT_TECHNOLOGY].value_cur) tech = "secondary"; else tech = "primary"; aprint_debug_dev(dv, "%s battery, Design %d.%03d%s " "Last full %d.%03d%s Warn %d.%03d%s Low %d.%03d%s\n", tech, SCALE(sc->sc_sensor[ACPIBAT_DCAPACITY].value_cur), CAPUNITS(sc), SCALE(sc->sc_sensor[ACPIBAT_LFCCAPACITY].value_cur),CAPUNITS(sc), SCALE(sc->sc_sensor[ACPIBAT_WCAPACITY].value_cur), CAPUNITS(sc), SCALE(sc->sc_sensor[ACPIBAT_LCAPACITY].value_cur), CAPUNITS(sc)); } static void acpibat_print_stat(device_t dv) { struct acpibat_softc *sc = device_private(dv); const char *capstat, *chargestat; int percent, denom; int32_t value; percent = 0; if (sc->sc_sensor[ACPIBAT_CAPACITY].state == ENVSYS_SCRITUNDER) capstat = "CRITICAL UNDER "; else if (sc->sc_sensor[ACPIBAT_CAPACITY].state == ENVSYS_SCRITOVER) capstat = "CRITICAL OVER "; else capstat = ""; if (sc->sc_sensor[ACPIBAT_CHARGING].state != ENVSYS_SVALID) { chargestat = "idling"; value = 0; } else if (sc->sc_sensor[ACPIBAT_CHARGING].value_cur == 0) { chargestat = "discharging"; value = sc->sc_sensor[ACPIBAT_DISCHARGERATE].value_cur; } else { chargestat = "charging"; value = sc->sc_sensor[ACPIBAT_CHARGERATE].value_cur; } denom = sc->sc_sensor[ACPIBAT_LFCCAPACITY].value_cur / 100; if (denom > 0) percent = (sc->sc_sensor[ACPIBAT_CAPACITY].value_cur) / denom; aprint_debug_dev(dv, "%s%s: %d.%03dV cap %d.%03d%s (%d%%) " "rate %d.%03d%s\n", capstat, chargestat, SCALE(sc->sc_sensor[ACPIBAT_VOLTAGE].value_cur), SCALE(sc->sc_sensor[ACPIBAT_CAPACITY].value_cur), CAPUNITS(sc), percent, SCALE(value), RATEUNITS(sc)); } static void acpibat_update(void *arg) { device_t dv = arg; struct acpibat_softc *sc = device_private(dv); if (sc->sc_available < ABAT_ALV_INFO) { /* current information is invalid */ #if 0 /* * XXX: The driver sometimes unaware that the battery exist. * (i.e. just after the boot or resuming) * Thus, the driver should always check it here. */ if (sc->sc_available < ABAT_ALV_PRESENCE) #endif /* presence is invalid */ if (acpibat_battery_present(dv) < 0) { /* error */ aprint_debug_dev(dv, "cannot get battery presence.\n"); return; } if (ABAT_ISSET(sc, ABAT_F_PRESENT)) { /* the battery is present. */ if (ABAT_ISSET(sc, ABAT_F_VERBOSE)) aprint_debug_dev(dv, "battery is present.\n"); if (ACPI_FAILURE(acpibat_get_info(dv))) return; if (ABAT_ISSET(sc, ABAT_F_VERBOSE)) acpibat_print_info(dv); } else { /* the battery is not present. */ if (ABAT_ISSET(sc, ABAT_F_VERBOSE)) aprint_debug_dev(dv, "battery is not present.\n"); return; } } else { /* current information is valid */ if (!ABAT_ISSET(sc, ABAT_F_PRESENT)) { /* the battery is not present. */ return; } } if (ACPI_FAILURE(acpibat_get_status(dv))) return; if (ABAT_ISSET(sc, ABAT_F_VERBOSE)) acpibat_print_stat(dv); } /* * acpibat_notify_handler: * * Callback from ACPI interrupt handler to notify us of an event. */ static void acpibat_notify_handler(ACPI_HANDLE handle, UINT32 notify, void *context) { device_t dv = context; struct acpibat_softc *sc = device_private(dv); int rv; #ifdef ACPI_BAT_DEBUG aprint_debug_dev(dv, "received notify message: 0x%x\n", notify); #endif switch (notify) { case ACPI_NOTIFY_BusCheck: break; case ACPI_NOTIFY_DeviceCheck: case ACPI_NOTIFY_BatteryInformationChanged: mutex_enter(&sc->sc_mtx); acpibat_clear_presence(sc); mutex_exit(&sc->sc_mtx); rv = AcpiOsExecute(OSL_NOTIFY_HANDLER, acpibat_update, dv); if (ACPI_FAILURE(rv)) aprint_error_dev(dv, "unable to queue status check: %s\n", AcpiFormatException(rv)); break; case ACPI_NOTIFY_BatteryStatusChanged: mutex_enter(&sc->sc_mtx); acpibat_clear_stat(sc); mutex_exit(&sc->sc_mtx); rv = AcpiOsExecute(OSL_NOTIFY_HANDLER, acpibat_update, dv); if (ACPI_FAILURE(rv)) aprint_error_dev(dv, "unable to queue status check: %s\n", AcpiFormatException(rv)); break; default: aprint_error_dev(dv, "received unknown notify message: 0x%x\n", notify); } } static void acpibat_init_envsys(device_t dv) { struct acpibat_softc *sc = device_private(dv); int i, capunit, rateunit; if (sc->sc_flags & ABAT_F_PWRUNIT_MA) { capunit = ENVSYS_SAMPHOUR; rateunit = ENVSYS_SAMPS; } else { capunit = ENVSYS_SWATTHOUR; rateunit = ENVSYS_SWATTS; } #define INITDATA(index, unit, string) \ sc->sc_sensor[index].state = ENVSYS_SVALID; \ sc->sc_sensor[index].units = unit; \ strlcpy(sc->sc_sensor[index].desc, string, \ sizeof(sc->sc_sensor[index].desc)); INITDATA(ACPIBAT_PRESENT, ENVSYS_INDICATOR, "present"); INITDATA(ACPIBAT_DCAPACITY, capunit, "design cap"); INITDATA(ACPIBAT_LFCCAPACITY, capunit, "last full cap"); INITDATA(ACPIBAT_TECHNOLOGY, ENVSYS_INTEGER, "technology"); INITDATA(ACPIBAT_DVOLTAGE, ENVSYS_SVOLTS_DC, "design voltage"); INITDATA(ACPIBAT_WCAPACITY, capunit, "warn cap"); INITDATA(ACPIBAT_LCAPACITY, capunit, "low cap"); INITDATA(ACPIBAT_VOLTAGE, ENVSYS_SVOLTS_DC, "voltage"); INITDATA(ACPIBAT_CHARGERATE, rateunit, "charge rate"); INITDATA(ACPIBAT_DISCHARGERATE, rateunit, "discharge rate"); INITDATA(ACPIBAT_CAPACITY, capunit, "charge"); INITDATA(ACPIBAT_CHARGING, ENVSYS_BATTERY_CHARGE, "charging"); INITDATA(ACPIBAT_CHARGE_STATE, ENVSYS_BATTERY_CAPACITY, "charge state"); #undef INITDATA /* Enable monitoring for the charge state sensor */ sc->sc_sensor[ACPIBAT_CHARGE_STATE].monitor = true; sc->sc_sensor[ACPIBAT_CHARGE_STATE].flags |= ENVSYS_FMONSTCHANGED; /* Disable userland monitoring on these sensors */ sc->sc_sensor[ACPIBAT_VOLTAGE].flags = ENVSYS_FMONNOTSUPP; sc->sc_sensor[ACPIBAT_CHARGERATE].flags = ENVSYS_FMONNOTSUPP; sc->sc_sensor[ACPIBAT_DISCHARGERATE].flags = ENVSYS_FMONNOTSUPP; sc->sc_sensor[ACPIBAT_DCAPACITY].flags = ENVSYS_FMONNOTSUPP; sc->sc_sensor[ACPIBAT_LFCCAPACITY].flags = ENVSYS_FMONNOTSUPP; sc->sc_sensor[ACPIBAT_TECHNOLOGY].flags = ENVSYS_FMONNOTSUPP; sc->sc_sensor[ACPIBAT_DVOLTAGE].flags = ENVSYS_FMONNOTSUPP; sc->sc_sensor[ACPIBAT_WCAPACITY].flags = ENVSYS_FMONNOTSUPP; sc->sc_sensor[ACPIBAT_LCAPACITY].flags = ENVSYS_FMONNOTSUPP; sc->sc_sme = sysmon_envsys_create(); for (i = 0; i < ACPIBAT_NSENSORS; i++) { if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor[i])) { aprint_error_dev(dv, "unable to add sensor%d\n", i); sysmon_envsys_destroy(sc->sc_sme); return; } } sc->sc_sme->sme_name = device_xname(dv); sc->sc_sme->sme_cookie = dv; sc->sc_sme->sme_refresh = acpibat_refresh; sc->sc_sme->sme_class = SME_CLASS_BATTERY; sc->sc_updateinterval.tv_sec = 1; sc->sc_updateinterval.tv_usec = 0; if (sysmon_envsys_register(sc->sc_sme)) { aprint_error_dev(dv, "unable to register with sysmon\n"); sysmon_envsys_destroy(sc->sc_sme); } } static void acpibat_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) { device_t dv = sme->sme_cookie; struct acpibat_softc *sc = device_private(dv); if (ratecheck(&sc->sc_lastupdate, &sc->sc_updateinterval)) acpibat_update(dv); }