The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/acpica/acpi_cmbat.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (c) 2005 Nate Lawson
    3  * Copyright (c) 2000 Munehiro Matsuda
    4  * Copyright (c) 2000 Takanori Watanabe
    5  * Copyright (c) 2000 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
    6  * All rights reserved.
    7  *
    8  * Redistribution and use in source and binary forms, with or without
    9  * modification, are permitted provided that the following conditions
   10  * are met:
   11  * 1. Redistributions of source code must retain the above copyright
   12  *    notice, this list of conditions and the following disclaimer.
   13  * 2. Redistributions in binary form must reproduce the above copyright
   14  *    notice, this list of conditions and the following disclaimer in the
   15  *    documentation and/or other materials provided with the distribution.
   16  *
   17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   27  * SUCH DAMAGE.
   28  *
   29  * $FreeBSD: src/sys/dev/acpica/acpi_cmbat.c,v 1.46.8.1 2009/04/15 03:14:26 kensmith Exp $
   30  */
   31 
   32 #include "opt_acpi.h"
   33 #include <sys/param.h>
   34 #include <sys/kernel.h>
   35 #include <sys/module.h>
   36 #include <sys/bus.h>
   37 
   38 #include <sys/rman.h>
   39 
   40 #include "acpi.h"
   41 #include <dev/acpica/acpivar.h>
   42 #include <dev/acpica/acpiio.h>
   43 
   44 /* Number of times to retry initialization before giving up. */
   45 #define ACPI_CMBAT_RETRY_MAX    6
   46 
   47 /* Check the battery once a minute. */
   48 #define CMBAT_POLLRATE          (60 * hz)
   49 
   50 /* Hooks for the ACPI CA debugging infrastructure */
   51 #define _COMPONENT      ACPI_BATTERY
   52 ACPI_MODULE_NAME("BATTERY")
   53 
   54 #define ACPI_BATTERY_BST_CHANGE 0x80
   55 #define ACPI_BATTERY_BIF_CHANGE 0x81
   56 
   57 struct acpi_cmbat_softc {
   58     device_t        dev;
   59     int             flags;
   60 
   61     struct acpi_bif bif;
   62     struct acpi_bst bst;
   63     struct timespec bst_lastupdated;
   64 };
   65 
   66 ACPI_SERIAL_DECL(cmbat, "ACPI cmbat");
   67 
   68 static int              acpi_cmbat_probe(device_t dev);
   69 static int              acpi_cmbat_attach(device_t dev);
   70 static int              acpi_cmbat_detach(device_t dev);
   71 static int              acpi_cmbat_resume(device_t dev);
   72 static void             acpi_cmbat_notify_handler(ACPI_HANDLE h, UINT32 notify,
   73                             void *context);
   74 static int              acpi_cmbat_info_expired(struct timespec *lastupdated);
   75 static void             acpi_cmbat_info_updated(struct timespec *lastupdated);
   76 static void             acpi_cmbat_get_bst(void *arg);
   77 static void             acpi_cmbat_get_bif_task(void *arg);
   78 static void             acpi_cmbat_get_bif(void *arg);
   79 static int              acpi_cmbat_bst(device_t dev, struct acpi_bst *bstp);
   80 static int              acpi_cmbat_bif(device_t dev, struct acpi_bif *bifp);
   81 static void             acpi_cmbat_init_battery(void *arg);
   82 
   83 static device_method_t acpi_cmbat_methods[] = {
   84     /* Device interface */
   85     DEVMETHOD(device_probe,     acpi_cmbat_probe),
   86     DEVMETHOD(device_attach,    acpi_cmbat_attach),
   87     DEVMETHOD(device_detach,    acpi_cmbat_detach),
   88     DEVMETHOD(device_resume,    acpi_cmbat_resume),
   89 
   90     /* ACPI battery interface */
   91     DEVMETHOD(acpi_batt_get_info, acpi_cmbat_bif),
   92     DEVMETHOD(acpi_batt_get_status, acpi_cmbat_bst),
   93 
   94     DEVMETHOD_END
   95 };
   96 
   97 static driver_t acpi_cmbat_driver = {
   98     "battery",
   99     acpi_cmbat_methods,
  100     sizeof(struct acpi_cmbat_softc),
  101 };
  102 
  103 static devclass_t acpi_cmbat_devclass;
  104 DRIVER_MODULE(acpi_cmbat, acpi, acpi_cmbat_driver, acpi_cmbat_devclass, NULL, NULL);
  105 MODULE_DEPEND(acpi_cmbat, acpi, 1, 1, 1);
  106 
  107 static int
  108 acpi_cmbat_probe(device_t dev)
  109 {
  110     static char *cmbat_ids[] = { "PNP0C0A", NULL };
  111 
  112     if (acpi_disabled("cmbat") ||
  113         ACPI_ID_PROBE(device_get_parent(dev), dev, cmbat_ids) == NULL)
  114         return (ENXIO);
  115 
  116     device_set_desc(dev, "ACPI Control Method Battery");
  117     return (0);
  118 }
  119 
  120 static int
  121 acpi_cmbat_attach(device_t dev)
  122 {
  123     int         error;
  124     ACPI_HANDLE handle;
  125     struct acpi_cmbat_softc *sc;
  126 
  127     sc = device_get_softc(dev);
  128     handle = acpi_get_handle(dev);
  129     sc->dev = dev;
  130 
  131     ACPI_SERIAL_INIT(cmbat);
  132 
  133     timespecclear(&sc->bst_lastupdated);
  134 
  135     error = acpi_battery_register(dev);
  136     if (error != 0) {
  137         device_printf(dev, "registering battery failed\n");
  138         return (error);
  139     }
  140 
  141     /*
  142      * Install a system notify handler in addition to the device notify.
  143      * Toshiba notebook uses this alternate notify for its battery.
  144      */
  145     AcpiInstallNotifyHandler(handle, ACPI_ALL_NOTIFY,
  146         acpi_cmbat_notify_handler, dev);
  147 
  148     AcpiOsExecute(OSL_NOTIFY_HANDLER, acpi_cmbat_init_battery, dev);
  149 
  150     return (0);
  151 }
  152 
  153 static int
  154 acpi_cmbat_detach(device_t dev)
  155 {
  156     ACPI_HANDLE handle;
  157 
  158     handle = acpi_get_handle(dev);
  159     AcpiRemoveNotifyHandler(handle, ACPI_ALL_NOTIFY, acpi_cmbat_notify_handler);
  160     acpi_battery_remove(dev);
  161     return (0);
  162 }
  163 
  164 static int
  165 acpi_cmbat_resume(device_t dev)
  166 {
  167 
  168     AcpiOsExecute(OSL_NOTIFY_HANDLER, acpi_cmbat_init_battery, dev);
  169     return (0);
  170 }
  171 
  172 static void
  173 acpi_cmbat_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context)
  174 {
  175     struct acpi_cmbat_softc *sc;
  176     device_t dev;
  177 
  178     dev = (device_t)context;
  179     sc = device_get_softc(dev);
  180 
  181     switch (notify) {
  182     case ACPI_NOTIFY_DEVICE_CHECK:
  183     case ACPI_BATTERY_BST_CHANGE:
  184         /*
  185          * Clear the last updated time.  The next call to retrieve the
  186          * battery status will get the new value for us.
  187          */
  188         timespecclear(&sc->bst_lastupdated);
  189         break;
  190     case ACPI_NOTIFY_BUS_CHECK:
  191     case ACPI_BATTERY_BIF_CHANGE:
  192         /*
  193          * Queue a callback to get the current battery info from thread
  194          * context.  It's not safe to block in a notify handler.
  195          */
  196         AcpiOsExecute(OSL_NOTIFY_HANDLER, acpi_cmbat_get_bif_task, dev);
  197         break;
  198     }
  199 
  200     acpi_UserNotify("CMBAT", h, notify);
  201 }
  202 
  203 static int
  204 acpi_cmbat_info_expired(struct timespec *lastupdated)
  205 {
  206     struct timespec     curtime;
  207 
  208     ACPI_SERIAL_ASSERT(cmbat);
  209 
  210     if (lastupdated == NULL)
  211         return (TRUE);
  212     if (!timespecisset(lastupdated))
  213         return (TRUE);
  214 
  215     getnanotime(&curtime);
  216     timespecsub(&curtime, lastupdated);
  217     return (curtime.tv_sec < 0 ||
  218             curtime.tv_sec > acpi_battery_get_info_expire());
  219 }
  220 
  221 static void
  222 acpi_cmbat_info_updated(struct timespec *lastupdated)
  223 {
  224 
  225     ACPI_SERIAL_ASSERT(cmbat);
  226 
  227     if (lastupdated != NULL)
  228         getnanotime(lastupdated);
  229 }
  230 
  231 static void
  232 acpi_cmbat_get_bst(void *arg)
  233 {
  234     struct acpi_cmbat_softc *sc;
  235     ACPI_STATUS as;
  236     ACPI_OBJECT *res;
  237     ACPI_HANDLE h;
  238     ACPI_BUFFER bst_buffer;
  239     device_t dev;
  240 
  241     ACPI_SERIAL_ASSERT(cmbat);
  242 
  243     dev = arg;
  244     sc = device_get_softc(dev);
  245     h = acpi_get_handle(dev);
  246     bst_buffer.Pointer = NULL;
  247     bst_buffer.Length = ACPI_ALLOCATE_BUFFER;
  248 
  249     if (!acpi_cmbat_info_expired(&sc->bst_lastupdated))
  250         goto end;
  251 
  252     as = AcpiEvaluateObject(h, "_BST", NULL, &bst_buffer);
  253     if (ACPI_FAILURE(as)) {
  254         ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
  255                     "error fetching current battery status -- %s\n",
  256                     AcpiFormatException(as));
  257         goto end;
  258     }
  259 
  260     res = (ACPI_OBJECT *)bst_buffer.Pointer;
  261     if (!ACPI_PKG_VALID(res, 4)) {
  262         ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
  263                     "battery status corrupted\n");
  264         goto end;
  265     }
  266 
  267     if (acpi_PkgInt32(res, 0, &sc->bst.state) != 0)
  268         goto end;
  269     if (acpi_PkgInt32(res, 1, &sc->bst.rate) != 0)
  270         goto end;
  271     if (acpi_PkgInt32(res, 2, &sc->bst.cap) != 0)
  272         goto end;
  273     if (acpi_PkgInt32(res, 3, &sc->bst.volt) != 0)
  274         goto end;
  275     acpi_cmbat_info_updated(&sc->bst_lastupdated);
  276 
  277     /* XXX If all batteries are critical, perhaps we should suspend. */
  278     if (sc->bst.state & ACPI_BATT_STAT_CRITICAL) {
  279         if ((sc->flags & ACPI_BATT_STAT_CRITICAL) == 0) {
  280             sc->flags |= ACPI_BATT_STAT_CRITICAL;
  281             device_printf(dev, "critically low charge!\n");
  282         }
  283     } else
  284         sc->flags &= ~ACPI_BATT_STAT_CRITICAL;
  285 
  286 end:
  287     if (bst_buffer.Pointer != NULL)
  288         AcpiOsFree(bst_buffer.Pointer);
  289 }
  290 
  291 /* XXX There should be a cleaner way to do this locking. */
  292 static void
  293 acpi_cmbat_get_bif_task(void *arg)
  294 {
  295 
  296     ACPI_SERIAL_BEGIN(cmbat);
  297     acpi_cmbat_get_bif(arg);
  298     ACPI_SERIAL_END(cmbat);
  299 }
  300 
  301 static void
  302 acpi_cmbat_get_bif(void *arg)
  303 {
  304     struct acpi_cmbat_softc *sc;
  305     ACPI_STATUS as;
  306     ACPI_OBJECT *res;
  307     ACPI_HANDLE h;
  308     ACPI_BUFFER bif_buffer;
  309     device_t dev;
  310 
  311     ACPI_SERIAL_ASSERT(cmbat);
  312 
  313     dev = arg;
  314     sc = device_get_softc(dev);
  315     h = acpi_get_handle(dev);
  316     bif_buffer.Pointer = NULL;
  317     bif_buffer.Length = ACPI_ALLOCATE_BUFFER;
  318 
  319     as = AcpiEvaluateObject(h, "_BIF", NULL, &bif_buffer);
  320     if (ACPI_FAILURE(as)) {
  321         ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
  322                     "error fetching current battery info -- %s\n",
  323                     AcpiFormatException(as));
  324         goto end;
  325     }
  326 
  327     res = (ACPI_OBJECT *)bif_buffer.Pointer;
  328     if (!ACPI_PKG_VALID(res, 13)) {
  329         ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
  330                     "battery info corrupted\n");
  331         goto end;
  332     }
  333 
  334     if (acpi_PkgInt32(res, 0, &sc->bif.units) != 0)
  335         goto end;
  336     if (acpi_PkgInt32(res, 1, &sc->bif.dcap) != 0)
  337         goto end;
  338     if (acpi_PkgInt32(res, 2, &sc->bif.lfcap) != 0)
  339         goto end;
  340     if (acpi_PkgInt32(res, 3, &sc->bif.btech) != 0)
  341         goto end;
  342     if (acpi_PkgInt32(res, 4, &sc->bif.dvol) != 0)
  343         goto end;
  344     if (acpi_PkgInt32(res, 5, &sc->bif.wcap) != 0)
  345         goto end;
  346     if (acpi_PkgInt32(res, 6, &sc->bif.lcap) != 0)
  347         goto end;
  348     if (acpi_PkgInt32(res, 7, &sc->bif.gra1) != 0)
  349         goto end;
  350     if (acpi_PkgInt32(res, 8, &sc->bif.gra2) != 0)
  351         goto end;
  352     if (acpi_PkgStr(res,  9, sc->bif.model, ACPI_CMBAT_MAXSTRLEN) != 0)
  353         goto end;
  354     if (acpi_PkgStr(res, 10, sc->bif.serial, ACPI_CMBAT_MAXSTRLEN) != 0)
  355         goto end;
  356     if (acpi_PkgStr(res, 11, sc->bif.type, ACPI_CMBAT_MAXSTRLEN) != 0)
  357         goto end;
  358     if (acpi_PkgStr(res, 12, sc->bif.oeminfo, ACPI_CMBAT_MAXSTRLEN) != 0)
  359         goto end;
  360 
  361 end:
  362     if (bif_buffer.Pointer != NULL)
  363         AcpiOsFree(bif_buffer.Pointer);
  364 }
  365 
  366 static int
  367 acpi_cmbat_bif(device_t dev, struct acpi_bif *bifp)
  368 {
  369     struct acpi_cmbat_softc *sc;
  370 
  371     sc = device_get_softc(dev);
  372 
  373     /*
  374      * Just copy the data.  The only value that should change is the
  375      * last-full capacity, so we only update when we get a notify that says
  376      * the info has changed.  Many systems apparently take a long time to
  377      * process a _BIF call so we avoid it if possible.
  378      */
  379     ACPI_SERIAL_BEGIN(cmbat);
  380     bifp->units = sc->bif.units;
  381     bifp->dcap = sc->bif.dcap;
  382     bifp->lfcap = sc->bif.lfcap;
  383     bifp->btech = sc->bif.btech;
  384     bifp->dvol = sc->bif.dvol;
  385     bifp->wcap = sc->bif.wcap;
  386     bifp->lcap = sc->bif.lcap;
  387     bifp->gra1 = sc->bif.gra1;
  388     bifp->gra2 = sc->bif.gra2;
  389     strncpy(bifp->model, sc->bif.model, sizeof(sc->bif.model));
  390     strncpy(bifp->serial, sc->bif.serial, sizeof(sc->bif.serial));
  391     strncpy(bifp->type, sc->bif.type, sizeof(sc->bif.type));
  392     strncpy(bifp->oeminfo, sc->bif.oeminfo, sizeof(sc->bif.oeminfo));
  393     ACPI_SERIAL_END(cmbat);
  394 
  395     return (0);
  396 }
  397 
  398 static int
  399 acpi_cmbat_bst(device_t dev, struct acpi_bst *bstp)
  400 {
  401     struct acpi_cmbat_softc *sc;
  402 
  403     sc = device_get_softc(dev);
  404 
  405     ACPI_SERIAL_BEGIN(cmbat);
  406     if (acpi_BatteryIsPresent(dev)) {
  407         acpi_cmbat_get_bst(dev);
  408         bstp->state = sc->bst.state;
  409         bstp->rate = sc->bst.rate;
  410         bstp->cap = sc->bst.cap;
  411         bstp->volt = sc->bst.volt;
  412     } else
  413         bstp->state = ACPI_BATT_STAT_NOT_PRESENT;
  414     ACPI_SERIAL_END(cmbat);
  415 
  416     return (0);
  417 }
  418 
  419 static void
  420 acpi_cmbat_init_battery(void *arg)
  421 {
  422     struct acpi_cmbat_softc *sc;
  423     int         retry, valid;
  424     device_t    dev;
  425 
  426     dev = (device_t)arg;
  427     sc = device_get_softc(dev);
  428     ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
  429                 "battery initialization start\n");
  430 
  431     /*
  432      * Try repeatedly to get valid data from the battery.  Since the
  433      * embedded controller isn't always ready just after boot, we may have
  434      * to wait a while.
  435      */
  436     for (retry = 0; retry < ACPI_CMBAT_RETRY_MAX; retry++, AcpiOsSleep(10000)) {
  437         /* batteries on DOCK can be ejected w/ DOCK during retrying */
  438         if (!device_is_attached(dev))
  439             return;
  440 
  441         if (!acpi_BatteryIsPresent(dev))
  442             continue;
  443 
  444         /*
  445          * Only query the battery if this is the first try or the specific
  446          * type of info is still invalid.
  447          */
  448         ACPI_SERIAL_BEGIN(cmbat);
  449         if (retry == 0 || !acpi_battery_bst_valid(&sc->bst)) {
  450             timespecclear(&sc->bst_lastupdated);
  451             acpi_cmbat_get_bst(dev);
  452         }
  453         if (retry == 0 || !acpi_battery_bif_valid(&sc->bif))
  454             acpi_cmbat_get_bif(dev);
  455 
  456         valid = acpi_battery_bst_valid(&sc->bst) &&
  457             acpi_battery_bif_valid(&sc->bif);
  458         ACPI_SERIAL_END(cmbat);
  459 
  460         if (valid)
  461             break;
  462     }
  463 
  464     if (retry == ACPI_CMBAT_RETRY_MAX) {
  465         ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
  466                     "battery initialization failed, giving up\n");
  467     } else {
  468         ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
  469                     "battery initialization done, tried %d times\n", retry + 1);
  470     }
  471 }

Cache object: 7d6cc84b03ef1109cee8386ec6ce03d8


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.