root/dev/acpi/acpibat.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. acpibat_match
  2. acpibat_attach
  3. acpibat_monitor
  4. acpibat_refresh
  5. acpibat_getbif
  6. acpibat_getbst
  7. acpibat_notify

    1 /* $OpenBSD: acpibat.c,v 1.40 2007/03/20 15:17:21 mk Exp $ */
    2 /*
    3  * Copyright (c) 2005 Marco Peereboom <marco@openbsd.org>
    4  *
    5  * Permission to use, copy, modify, and distribute this software for any
    6  * purpose with or without fee is hereby granted, provided that the above
    7  * copyright notice and this permission notice appear in all copies.
    8  *
    9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
   11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
   12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
   13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
   14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
   15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   16  */
   17 
   18 #include <sys/param.h>
   19 #include <sys/proc.h>
   20 #include <sys/systm.h>
   21 #include <sys/device.h>
   22 #include <sys/malloc.h>
   23 #include <sys/sensors.h>
   24 
   25 #include <machine/bus.h>
   26 
   27 #include <dev/acpi/acpireg.h>
   28 #include <dev/acpi/acpivar.h>
   29 #include <dev/acpi/acpidev.h>
   30 #include <dev/acpi/amltypes.h>
   31 #include <dev/acpi/dsdt.h>
   32 
   33 int     acpibat_match(struct device *, void *, void *);
   34 void    acpibat_attach(struct device *, struct device *, void *);
   35 
   36 struct cfattach acpibat_ca = {
   37         sizeof(struct acpibat_softc), acpibat_match, acpibat_attach
   38 };
   39 
   40 struct cfdriver acpibat_cd = {
   41         NULL, "acpibat", DV_DULL
   42 };
   43 
   44 void    acpibat_monitor(struct acpibat_softc *);
   45 void    acpibat_refresh(void *);
   46 int     acpibat_getbif(struct acpibat_softc *);
   47 int     acpibat_getbst(struct acpibat_softc *);
   48 int     acpibat_notify(struct aml_node *, int, void *);
   49 
   50 int
   51 acpibat_match(struct device *parent, void *match, void *aux)
   52 {
   53         struct acpi_attach_args *aa = aux;
   54         struct cfdata           *cf = match;
   55 
   56         /* sanity */
   57         if (aa->aaa_name == NULL ||
   58             strcmp(aa->aaa_name, cf->cf_driver->cd_name) != 0 ||
   59             aa->aaa_table != NULL)
   60                 return (0);
   61 
   62         return (1);
   63 }
   64 
   65 void
   66 acpibat_attach(struct device *parent, struct device *self, void *aux)
   67 {
   68         struct acpibat_softc    *sc = (struct acpibat_softc *)self;
   69         struct acpi_attach_args *aa = aux;
   70         struct aml_value        res;
   71 
   72         sc->sc_acpi = (struct acpi_softc *)parent;
   73         sc->sc_devnode = aa->aaa_node->child;
   74 
   75         if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_STA", 0, NULL, &res)) {
   76                 dnprintf(10, "%s: no _STA\n", DEVNAME(sc));
   77                 return;
   78         }
   79 
   80         if ((sc->sc_bat_present = aml_val2int(&res) & STA_BATTERY) != 0) {
   81                 acpibat_getbif(sc);
   82                 acpibat_getbst(sc);
   83                 printf(": %s: model: %s serial: %s type: %s oem: %s\n",
   84                     sc->sc_devnode->parent->name,
   85                     sc->sc_bif.bif_model,
   86                     sc->sc_bif.bif_serial,
   87                     sc->sc_bif.bif_type,
   88                     sc->sc_bif.bif_oem);
   89         } else
   90                 printf(": %s: not present\n", sc->sc_devnode->parent->name);
   91 
   92         aml_freevalue(&res);
   93 
   94         /* create sensors */
   95         acpibat_monitor(sc);
   96 
   97         /* populate sensors */
   98         acpibat_refresh(sc);
   99 
  100         aml_register_notify(sc->sc_devnode->parent, aa->aaa_dev,
  101             acpibat_notify, sc, ACPIDEV_POLL);
  102 }
  103 
  104 void
  105 acpibat_monitor(struct acpibat_softc *sc)
  106 {
  107         int                     type;
  108 
  109         /* assume _BIF and _BST have been called */
  110         strlcpy(sc->sc_sensdev.xname, DEVNAME(sc),
  111             sizeof(sc->sc_sensdev.xname));
  112 
  113         type = sc->sc_bif.bif_power_unit ? SENSOR_AMPHOUR : SENSOR_WATTHOUR;
  114 
  115         strlcpy(sc->sc_sens[0].desc, "last full capacity",
  116             sizeof(sc->sc_sens[0].desc));
  117         sc->sc_sens[0].type = type;
  118         sensor_attach(&sc->sc_sensdev, &sc->sc_sens[0]);
  119         sc->sc_sens[0].value = sc->sc_bif.bif_last_capacity * 1000;
  120 
  121         strlcpy(sc->sc_sens[1].desc, "warning capacity",
  122             sizeof(sc->sc_sens[1].desc));
  123         sc->sc_sens[1].type = type;
  124         sensor_attach(&sc->sc_sensdev, &sc->sc_sens[1]);
  125         sc->sc_sens[1].value = sc->sc_bif.bif_warning * 1000;
  126 
  127         strlcpy(sc->sc_sens[2].desc, "low capacity",
  128             sizeof(sc->sc_sens[2].desc));
  129         sc->sc_sens[2].type = type;
  130         sensor_attach(&sc->sc_sensdev, &sc->sc_sens[2]);
  131         sc->sc_sens[2].value = sc->sc_bif.bif_low * 1000;
  132 
  133         strlcpy(sc->sc_sens[3].desc, "voltage", sizeof(sc->sc_sens[3].desc));
  134         sc->sc_sens[3].type = SENSOR_VOLTS_DC;
  135         sensor_attach(&sc->sc_sensdev, &sc->sc_sens[3]);
  136         sc->sc_sens[3].value = sc->sc_bif.bif_voltage * 1000;
  137 
  138         strlcpy(sc->sc_sens[4].desc, "battery unknown",
  139             sizeof(sc->sc_sens[4].desc));
  140         sc->sc_sens[4].type = SENSOR_INTEGER;
  141         sensor_attach(&sc->sc_sensdev, &sc->sc_sens[4]);
  142         sc->sc_sens[4].value = sc->sc_bst.bst_state;
  143 
  144         strlcpy(sc->sc_sens[5].desc, "rate", sizeof(sc->sc_sens[5].desc));
  145         sc->sc_sens[5].type = SENSOR_INTEGER;
  146         sensor_attach(&sc->sc_sensdev, &sc->sc_sens[5]);
  147         sc->sc_sens[5].value = sc->sc_bst.bst_rate;
  148 
  149         strlcpy(sc->sc_sens[6].desc, "remaining capacity",
  150             sizeof(sc->sc_sens[6].desc));
  151         sc->sc_sens[6].type = type;
  152         sensor_attach(&sc->sc_sensdev, &sc->sc_sens[6]);
  153         sc->sc_sens[6].value = sc->sc_bst.bst_capacity * 1000;
  154 
  155         strlcpy(sc->sc_sens[7].desc, "current voltage",
  156             sizeof(sc->sc_sens[7].desc));
  157         sc->sc_sens[7].type = SENSOR_VOLTS_DC;
  158         sensor_attach(&sc->sc_sensdev, &sc->sc_sens[7]);
  159         sc->sc_sens[7].value = sc->sc_bst.bst_voltage * 1000;
  160 
  161         sensordev_install(&sc->sc_sensdev);
  162 }
  163 
  164 void
  165 acpibat_refresh(void *arg)
  166 {
  167         struct acpibat_softc    *sc = arg;
  168         int                     i;
  169 
  170         dnprintf(30, "%s: %s: refresh\n", DEVNAME(sc),
  171             sc->sc_devnode->parent->name);
  172 
  173         if (!sc->sc_bat_present) {
  174                 for (i = 0; i < 8; i++) {
  175                         sc->sc_sens[i].value = 0;
  176                         sc->sc_sens[i].status = SENSOR_S_UNSPEC;
  177                         sc->sc_sens[i].flags = SENSOR_FINVALID;
  178                 }
  179                 /* override state */
  180                 strlcpy(sc->sc_sens[4].desc, "battery removed",
  181                     sizeof(sc->sc_sens[4].desc));
  182                 return;
  183         }
  184 
  185         /*
  186          * XXX don't really need _BIF but keep it here in case we
  187          * miss an insertion/removal event
  188          */
  189         acpibat_getbif(sc);
  190         acpibat_getbst(sc);
  191 
  192         /* _BIF values are static, sensor 0..3 */
  193         if (sc->sc_bif.bif_last_capacity == BIF_UNKNOWN) {
  194                 sc->sc_sens[0].value = 0;
  195                 sc->sc_sens[0].status = SENSOR_S_UNKNOWN;
  196                 sc->sc_sens[0].flags = SENSOR_FUNKNOWN;
  197         } else {
  198                 sc->sc_sens[0].value = sc->sc_bif.bif_last_capacity * 1000;
  199                 sc->sc_sens[0].status = SENSOR_S_UNSPEC;
  200                 sc->sc_sens[0].flags = 0;
  201         }
  202         sc->sc_sens[1].value = sc->sc_bif.bif_warning * 1000;
  203         sc->sc_sens[1].flags = 0;
  204         sc->sc_sens[2].value = sc->sc_bif.bif_low * 1000;
  205         sc->sc_sens[2].flags = 0;
  206         if (sc->sc_bif.bif_voltage == BIF_UNKNOWN) {
  207                 sc->sc_sens[3].value = 0;
  208                 sc->sc_sens[3].status = SENSOR_S_UNKNOWN;
  209                 sc->sc_sens[3].flags = SENSOR_FUNKNOWN;
  210         } else {
  211                 sc->sc_sens[3].value = sc->sc_bif.bif_voltage * 1000;
  212                 sc->sc_sens[3].status = SENSOR_S_UNSPEC;
  213                 sc->sc_sens[3].flags = 0;
  214         }
  215 
  216         /* _BST values are dynamic, sensor 4..7 */
  217         sc->sc_sens[4].status = SENSOR_S_OK;
  218         sc->sc_sens[4].flags = 0;
  219         if (sc->sc_bif.bif_last_capacity == BIF_UNKNOWN ||
  220             sc->sc_bst.bst_capacity == BST_UNKNOWN) { 
  221                 sc->sc_sens[4].status = SENSOR_S_UNKNOWN;
  222                 sc->sc_sens[4].flags = SENSOR_FUNKNOWN;
  223                 strlcpy(sc->sc_sens[4].desc, "battery unknown",
  224                     sizeof(sc->sc_sens[4].desc));
  225         } else if (sc->sc_bst.bst_capacity >= sc->sc_bif.bif_last_capacity)
  226                 strlcpy(sc->sc_sens[4].desc, "battery full",
  227                     sizeof(sc->sc_sens[4].desc));
  228         else if (sc->sc_bst.bst_state & BST_DISCHARGE)
  229                 strlcpy(sc->sc_sens[4].desc, "battery discharging",
  230                     sizeof(sc->sc_sens[4].desc));
  231         else if (sc->sc_bst.bst_state & BST_CHARGE)
  232                 strlcpy(sc->sc_sens[4].desc, "battery charging",
  233                     sizeof(sc->sc_sens[4].desc));
  234         else if (sc->sc_bst.bst_state & BST_CRITICAL) {
  235                 strlcpy(sc->sc_sens[4].desc, "battery critical",
  236                     sizeof(sc->sc_sens[4].desc));
  237                 sc->sc_sens[4].status = SENSOR_S_CRIT;
  238         } else
  239                 strlcpy(sc->sc_sens[4].desc, "battery idle",
  240                     sizeof(sc->sc_sens[4].desc));
  241         sc->sc_sens[4].value = sc->sc_bst.bst_state;
  242 
  243         if (sc->sc_bst.bst_rate == BST_UNKNOWN) {
  244                 sc->sc_sens[5].value = 0;
  245                 sc->sc_sens[5].status = SENSOR_S_UNKNOWN;
  246                 sc->sc_sens[5].flags = SENSOR_FUNKNOWN;
  247         } else {
  248                 sc->sc_sens[5].value = sc->sc_bst.bst_rate;
  249                 sc->sc_sens[5].status = SENSOR_S_UNSPEC;
  250                 sc->sc_sens[5].flags = 0;
  251         }
  252 
  253         if (sc->sc_bst.bst_capacity == BST_UNKNOWN) {
  254                 sc->sc_sens[6].value = 0;
  255                 sc->sc_sens[6].status = SENSOR_S_UNKNOWN;
  256                 sc->sc_sens[6].flags = SENSOR_FUNKNOWN;
  257         } else {
  258                 sc->sc_sens[6].value = sc->sc_bst.bst_capacity * 1000;
  259                 sc->sc_sens[6].flags = 0;
  260 
  261                 if (sc->sc_bst.bst_capacity < sc->sc_bif.bif_low)
  262                         /* XXX we should shutdown the system */
  263                         sc->sc_sens[6].status = SENSOR_S_CRIT;
  264                 else if (sc->sc_bst.bst_capacity < sc->sc_bif.bif_warning)
  265                         sc->sc_sens[6].status = SENSOR_S_WARN;
  266                 else
  267                         sc->sc_sens[6].status = SENSOR_S_OK;
  268         }
  269 
  270         if(sc->sc_bst.bst_voltage == BST_UNKNOWN) {
  271                 sc->sc_sens[7].value = 0;
  272                 sc->sc_sens[7].status = SENSOR_S_UNKNOWN;
  273                 sc->sc_sens[7].flags = SENSOR_FUNKNOWN;
  274         } else {
  275                 sc->sc_sens[7].value = sc->sc_bst.bst_voltage * 1000;
  276                 sc->sc_sens[7].status = SENSOR_S_UNSPEC;
  277                 sc->sc_sens[7].flags = 0;
  278         }
  279 }
  280 
  281 int
  282 acpibat_getbif(struct acpibat_softc *sc)
  283 {
  284         struct aml_value        res;
  285         int                     rv = EINVAL;
  286 
  287         if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_STA", 0, NULL, &res)) {
  288                 dnprintf(10, "%s: no _STA\n", DEVNAME(sc));
  289                 goto out;
  290         }
  291         aml_freevalue(&res);
  292 
  293         if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_BIF", 0, NULL, &res)) {
  294                 dnprintf(10, "%s: no _BIF\n", DEVNAME(sc));
  295                 goto out;
  296         }
  297 
  298         if (res.length != 13) {
  299                 dnprintf(10, "%s: invalid _BIF, battery info not saved\n",
  300                     DEVNAME(sc));
  301                 goto out;
  302         }
  303 
  304         memset(&sc->sc_bif, 0, sizeof sc->sc_bif);
  305         sc->sc_bif.bif_power_unit = aml_val2int(res.v_package[0]);
  306         sc->sc_bif.bif_capacity = aml_val2int(res.v_package[1]);
  307         sc->sc_bif.bif_last_capacity = aml_val2int(res.v_package[2]);
  308         sc->sc_bif.bif_technology = aml_val2int(res.v_package[3]);
  309         sc->sc_bif.bif_voltage = aml_val2int(res.v_package[4]);
  310         sc->sc_bif.bif_warning = aml_val2int(res.v_package[5]);
  311         sc->sc_bif.bif_low = aml_val2int(res.v_package[6]);
  312         sc->sc_bif.bif_cap_granu1 = aml_val2int(res.v_package[7]);
  313         sc->sc_bif.bif_cap_granu2 = aml_val2int(res.v_package[8]);
  314 
  315         strlcpy(sc->sc_bif.bif_model, aml_strval(res.v_package[9]),
  316                 sizeof(sc->sc_bif.bif_model));
  317         strlcpy(sc->sc_bif.bif_serial, aml_strval(res.v_package[10]),
  318                 sizeof(sc->sc_bif.bif_serial));
  319         strlcpy(sc->sc_bif.bif_type, aml_strval(res.v_package[11]),
  320                 sizeof(sc->sc_bif.bif_type));
  321         strlcpy(sc->sc_bif.bif_oem, aml_strval(res.v_package[12]),
  322                 sizeof(sc->sc_bif.bif_oem));
  323 
  324         dnprintf(60, "power_unit: %u capacity: %u last_cap: %u tech: %u "
  325             "volt: %u warn: %u low: %u gran1: %u gran2: %d model: %s "
  326             "serial: %s type: %s oem: %s\n",
  327             sc->sc_bif.bif_power_unit,
  328             sc->sc_bif.bif_capacity,
  329             sc->sc_bif.bif_last_capacity,
  330             sc->sc_bif.bif_technology,
  331             sc->sc_bif.bif_voltage,
  332             sc->sc_bif.bif_warning,
  333             sc->sc_bif.bif_low,
  334             sc->sc_bif.bif_cap_granu1,
  335             sc->sc_bif.bif_cap_granu2,
  336             sc->sc_bif.bif_model,
  337             sc->sc_bif.bif_serial,
  338             sc->sc_bif.bif_type,
  339             sc->sc_bif.bif_oem);
  340 
  341         rv = 0;
  342 out:
  343         aml_freevalue(&res);
  344         return (rv);
  345 }
  346 
  347 int
  348 acpibat_getbst(struct acpibat_softc *sc)
  349 {
  350         struct aml_value        res;
  351         int                     rv = EINVAL;
  352 
  353         if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_BST", 0, NULL, &res)) {
  354                 dnprintf(10, "%s: no _BST\n", DEVNAME(sc));
  355                 goto out;
  356         }
  357 
  358         if (res.length != 4) {
  359                 dnprintf(10, "%s: invalid _BST, battery status not saved\n",
  360                     DEVNAME(sc));
  361                 goto out;
  362         }
  363 
  364         sc->sc_bst.bst_state = aml_val2int(res.v_package[0]);
  365         sc->sc_bst.bst_rate = aml_val2int(res.v_package[1]);
  366         sc->sc_bst.bst_capacity = aml_val2int(res.v_package[2]);
  367         sc->sc_bst.bst_voltage = aml_val2int(res.v_package[3]);
  368 
  369         dnprintf(60, "state: %u rate: %u cap: %u volt: %u ",
  370             sc->sc_bst.bst_state,
  371             sc->sc_bst.bst_rate,
  372             sc->sc_bst.bst_capacity,
  373             sc->sc_bst.bst_voltage);
  374 
  375         rv = 0;
  376 out:
  377         aml_freevalue(&res);
  378         return (rv);
  379 }
  380 
  381 /* XXX it has been observed that some systems do not propagate battery
  382  * insertion events up to the driver.  What seems to happen is that DSDT
  383  * does receive an interrupt however the originator bit is not set.
  384  * This seems to happen when one inserts a 100% full battery.  Removal
  385  * of the power cord or insertion of a not 100% full battery breaks this
  386  * behavior and all events will then be sent upwards.  Currently there
  387  * is no known work-around for it.
  388  */
  389 
  390 int
  391 acpibat_notify(struct aml_node *node, int notify_type, void *arg)
  392 {
  393         struct acpibat_softc    *sc = arg;
  394 
  395         dnprintf(10, "acpibat_notify: %.2x %s\n", notify_type,
  396             sc->sc_devnode->parent->name);
  397 
  398         switch (notify_type) {
  399         case 0x80:      /* _BST changed */
  400                 if (!sc->sc_bat_present) {
  401                         printf("%s: %s: inserted\n", DEVNAME(sc),
  402                             sc->sc_devnode->parent->name);
  403                         sc->sc_bat_present = 1;
  404                 }
  405                 break;
  406         case 0x81:      /* _BIF changed */
  407                 /* XXX consider this a device removal */
  408                 if (sc->sc_bat_present) {
  409                         printf("%s: %s: removed\n", DEVNAME(sc),
  410                             sc->sc_devnode->parent->name);
  411                         sc->sc_bat_present = 0;
  412                 }
  413                 break;
  414         default:
  415                 break;
  416         }
  417 
  418         acpibat_refresh(sc);
  419 
  420         return (0);
  421 }

/* [<][>][^][v][top][bottom][index][help] */