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/acpi_support/acpi_ibm.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) 2004 Takanori Watanabe
    3  * Copyright (c) 2005 Markus Brueffer <markus@FreeBSD.org>
    4  * All rights reserved.
    5  *
    6  * Redistribution and use in source and binary forms, with or without
    7  * modification, are permitted provided that the following conditions
    8  * are met:
    9  * 1. Redistributions of source code must retain the above copyright
   10  *    notice, this list of conditions and the following disclaimer.
   11  * 2. Redistributions in binary form must reproduce the above copyright
   12  *    notice, this list of conditions and the following disclaimer in the
   13  *    documentation and/or other materials provided with the distribution.
   14  *
   15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   25  * SUCH DAMAGE.
   26  */
   27 
   28 #include <sys/cdefs.h>
   29 __FBSDID("$FreeBSD: releng/8.4/sys/dev/acpi_support/acpi_ibm.c 242915 2012-11-12 14:05:40Z bapt $");
   30 
   31 /*
   32  * Driver for extra ACPI-controlled gadgets found on IBM ThinkPad laptops.
   33  * Inspired by the ibm-acpi and tpb projects which implement these features
   34  * on Linux.
   35  *
   36  *   acpi-ibm: <http://ibm-acpi.sourceforge.net/>
   37  *        tpb: <http://www.nongnu.org/tpb/>
   38  */
   39 
   40 #include "opt_acpi.h"
   41 #include <sys/param.h>
   42 #include <sys/kernel.h>
   43 #include <sys/bus.h>
   44 #include <machine/cpufunc.h>
   45 
   46 #include <contrib/dev/acpica/include/acpi.h>
   47 #include <contrib/dev/acpica/include/accommon.h>
   48 
   49 #include "acpi_if.h"
   50 #include <sys/module.h>
   51 #include <dev/acpica/acpivar.h>
   52 #include <dev/led/led.h>
   53 #include <sys/sysctl.h>
   54 #include <isa/rtc.h>
   55 
   56 #define _COMPONENT      ACPI_OEM
   57 ACPI_MODULE_NAME("IBM")
   58 
   59 /* Internal methods */
   60 #define ACPI_IBM_METHOD_EVENTS          1
   61 #define ACPI_IBM_METHOD_EVENTMASK       2
   62 #define ACPI_IBM_METHOD_HOTKEY          3
   63 #define ACPI_IBM_METHOD_BRIGHTNESS      4
   64 #define ACPI_IBM_METHOD_VOLUME          5
   65 #define ACPI_IBM_METHOD_MUTE            6
   66 #define ACPI_IBM_METHOD_THINKLIGHT      7
   67 #define ACPI_IBM_METHOD_BLUETOOTH       8
   68 #define ACPI_IBM_METHOD_WLAN            9
   69 #define ACPI_IBM_METHOD_FANSPEED        10
   70 #define ACPI_IBM_METHOD_FANLEVEL        11
   71 #define ACPI_IBM_METHOD_FANSTATUS       12
   72 #define ACPI_IBM_METHOD_THERMAL         13
   73 
   74 /* Hotkeys/Buttons */
   75 #define IBM_RTC_HOTKEY1                 0x64
   76 #define   IBM_RTC_MASK_HOME             (1 << 0)
   77 #define   IBM_RTC_MASK_SEARCH           (1 << 1)
   78 #define   IBM_RTC_MASK_MAIL             (1 << 2)
   79 #define   IBM_RTC_MASK_WLAN             (1 << 5)
   80 #define IBM_RTC_HOTKEY2                 0x65
   81 #define   IBM_RTC_MASK_THINKPAD         (1 << 3)
   82 #define   IBM_RTC_MASK_ZOOM             (1 << 5)
   83 #define   IBM_RTC_MASK_VIDEO            (1 << 6)
   84 #define   IBM_RTC_MASK_HIBERNATE        (1 << 7)
   85 #define IBM_RTC_THINKLIGHT              0x66
   86 #define   IBM_RTC_MASK_THINKLIGHT       (1 << 4)
   87 #define IBM_RTC_SCREENEXPAND            0x67
   88 #define   IBM_RTC_MASK_SCREENEXPAND     (1 << 5)
   89 #define IBM_RTC_BRIGHTNESS              0x6c
   90 #define   IBM_RTC_MASK_BRIGHTNESS       (1 << 5)
   91 #define IBM_RTC_VOLUME                  0x6e
   92 #define   IBM_RTC_MASK_VOLUME           (1 << 7)
   93 
   94 /* Embedded Controller registers */
   95 #define IBM_EC_BRIGHTNESS               0x31
   96 #define   IBM_EC_MASK_BRI               0x7
   97 #define IBM_EC_VOLUME                   0x30
   98 #define   IBM_EC_MASK_VOL               0xf
   99 #define   IBM_EC_MASK_MUTE              (1 << 6)
  100 #define IBM_EC_FANSTATUS                0x2F
  101 #define   IBM_EC_MASK_FANLEVEL          0x3f
  102 #define   IBM_EC_MASK_FANDISENGAGED     (1 << 6)
  103 #define   IBM_EC_MASK_FANSTATUS         (1 << 7)
  104 #define IBM_EC_FANSPEED                 0x84
  105 
  106 /* CMOS Commands */
  107 #define IBM_CMOS_VOLUME_DOWN            0
  108 #define IBM_CMOS_VOLUME_UP              1
  109 #define IBM_CMOS_VOLUME_MUTE            2
  110 #define IBM_CMOS_BRIGHTNESS_UP          4
  111 #define IBM_CMOS_BRIGHTNESS_DOWN        5
  112 
  113 /* ACPI methods */
  114 #define IBM_NAME_KEYLIGHT               "KBLT"
  115 #define IBM_NAME_WLAN_BT_GET            "GBDC"
  116 #define IBM_NAME_WLAN_BT_SET            "SBDC"
  117 #define   IBM_NAME_MASK_BT              (1 << 1)
  118 #define   IBM_NAME_MASK_WLAN            (1 << 2)
  119 #define IBM_NAME_THERMAL_GET            "TMP7"
  120 #define IBM_NAME_THERMAL_UPDT           "UPDT"
  121 
  122 #define IBM_NAME_EVENTS_STATUS_GET      "DHKC"
  123 #define IBM_NAME_EVENTS_MASK_GET        "DHKN"
  124 #define IBM_NAME_EVENTS_STATUS_SET      "MHKC"
  125 #define IBM_NAME_EVENTS_MASK_SET        "MHKM"
  126 #define IBM_NAME_EVENTS_GET             "MHKP"
  127 #define IBM_NAME_EVENTS_AVAILMASK       "MHKA"
  128 
  129 #define ABS(x) (((x) < 0)? -(x) : (x))
  130 
  131 struct acpi_ibm_softc {
  132         device_t        dev;
  133         ACPI_HANDLE     handle;
  134 
  135         /* Embedded controller */
  136         device_t        ec_dev;
  137         ACPI_HANDLE     ec_handle;
  138 
  139         /* CMOS */
  140         ACPI_HANDLE     cmos_handle;
  141 
  142         /* Fan status */
  143         ACPI_HANDLE     fan_handle;
  144         int             fan_levels;
  145 
  146         /* Keylight commands and states */
  147         ACPI_HANDLE     light_handle;
  148         int             light_cmd_on;
  149         int             light_cmd_off;
  150         int             light_val;
  151         int             light_get_supported;
  152         int             light_set_supported;
  153 
  154         /* led(4) interface */
  155         struct cdev     *led_dev;
  156         int             led_busy;
  157         int             led_state;
  158 
  159         int             wlan_bt_flags;
  160         int             thermal_updt_supported;
  161 
  162         unsigned int    events_availmask;
  163         unsigned int    events_initialmask;
  164         int             events_mask_supported;
  165         int             events_enable;
  166 
  167         struct sysctl_ctx_list  *sysctl_ctx;
  168         struct sysctl_oid       *sysctl_tree;
  169 };
  170 
  171 static struct {
  172         char    *name;
  173         int     method;
  174         char    *description;
  175         int     access;
  176 } acpi_ibm_sysctls[] = {
  177         {
  178                 .name           = "events",
  179                 .method         = ACPI_IBM_METHOD_EVENTS,
  180                 .description    = "ACPI events enable",
  181                 .access         = CTLTYPE_INT | CTLFLAG_RW
  182         },
  183         {
  184                 .name           = "eventmask",
  185                 .method         = ACPI_IBM_METHOD_EVENTMASK,
  186                 .description    = "ACPI eventmask",
  187                 .access         = CTLTYPE_INT | CTLFLAG_RW
  188         },
  189         {
  190                 .name           = "hotkey",
  191                 .method         = ACPI_IBM_METHOD_HOTKEY,
  192                 .description    = "Key Status",
  193                 .access         = CTLTYPE_INT | CTLFLAG_RD
  194         },
  195         {
  196                 .name           = "lcd_brightness",
  197                 .method         = ACPI_IBM_METHOD_BRIGHTNESS,
  198                 .description    = "LCD Brightness",
  199                 .access         = CTLTYPE_INT | CTLFLAG_RW
  200         },
  201         {
  202                 .name           = "volume",
  203                 .method         = ACPI_IBM_METHOD_VOLUME,
  204                 .description    = "Volume",
  205                 .access         = CTLTYPE_INT | CTLFLAG_RW
  206         },
  207         {
  208                 .name           = "mute",
  209                 .method         = ACPI_IBM_METHOD_MUTE,
  210                 .description    = "Mute",
  211                 .access         = CTLTYPE_INT | CTLFLAG_RW
  212         },
  213         {
  214                 .name           = "thinklight",
  215                 .method         = ACPI_IBM_METHOD_THINKLIGHT,
  216                 .description    = "Thinklight enable",
  217                 .access         = CTLTYPE_INT | CTLFLAG_RW
  218         },
  219         {
  220                 .name           = "bluetooth",
  221                 .method         = ACPI_IBM_METHOD_BLUETOOTH,
  222                 .description    = "Bluetooth enable",
  223                 .access         = CTLTYPE_INT | CTLFLAG_RW
  224         },
  225         {
  226                 .name           = "wlan",
  227                 .method         = ACPI_IBM_METHOD_WLAN,
  228                 .description    = "WLAN enable",
  229                 .access         = CTLTYPE_INT | CTLFLAG_RD
  230         },
  231         {
  232                 .name           = "fan_speed",
  233                 .method         = ACPI_IBM_METHOD_FANSPEED,
  234                 .description    = "Fan speed",
  235                 .access         = CTLTYPE_INT | CTLFLAG_RD
  236         },
  237         {
  238                 .name           = "fan_level",
  239                 .method         = ACPI_IBM_METHOD_FANLEVEL,
  240                 .description    = "Fan level",
  241                 .access         = CTLTYPE_INT | CTLFLAG_RW
  242         },
  243         {
  244                 .name           = "fan",
  245                 .method         = ACPI_IBM_METHOD_FANSTATUS,
  246                 .description    = "Fan enable",
  247                 .access         = CTLTYPE_INT | CTLFLAG_RW
  248         },
  249 
  250         { NULL, 0, NULL, 0 }
  251 };
  252 
  253 ACPI_SERIAL_DECL(ibm, "ACPI IBM extras");
  254 
  255 static int      acpi_ibm_probe(device_t dev);
  256 static int      acpi_ibm_attach(device_t dev);
  257 static int      acpi_ibm_detach(device_t dev);
  258 static int      acpi_ibm_resume(device_t dev);
  259 
  260 static void     ibm_led(void *softc, int onoff);
  261 static void     ibm_led_task(struct acpi_ibm_softc *sc, int pending __unused);
  262 
  263 static int      acpi_ibm_sysctl(SYSCTL_HANDLER_ARGS);
  264 static int      acpi_ibm_sysctl_init(struct acpi_ibm_softc *sc, int method);
  265 static int      acpi_ibm_sysctl_get(struct acpi_ibm_softc *sc, int method);
  266 static int      acpi_ibm_sysctl_set(struct acpi_ibm_softc *sc, int method, int val);
  267 
  268 static int      acpi_ibm_eventmask_set(struct acpi_ibm_softc *sc, int val);
  269 static int      acpi_ibm_thermal_sysctl(SYSCTL_HANDLER_ARGS);
  270 static void     acpi_ibm_notify(ACPI_HANDLE h, UINT32 notify, void *context);
  271 
  272 static device_method_t acpi_ibm_methods[] = {
  273         /* Device interface */
  274         DEVMETHOD(device_probe, acpi_ibm_probe),
  275         DEVMETHOD(device_attach, acpi_ibm_attach),
  276         DEVMETHOD(device_detach, acpi_ibm_detach),
  277         DEVMETHOD(device_resume, acpi_ibm_resume),
  278 
  279         {0, 0}
  280 };
  281 
  282 static driver_t acpi_ibm_driver = {
  283         "acpi_ibm",
  284         acpi_ibm_methods,
  285         sizeof(struct acpi_ibm_softc),
  286 };
  287 
  288 static devclass_t acpi_ibm_devclass;
  289 
  290 DRIVER_MODULE(acpi_ibm, acpi, acpi_ibm_driver, acpi_ibm_devclass,
  291               0, 0);
  292 MODULE_DEPEND(acpi_ibm, acpi, 1, 1, 1);
  293 static char    *ibm_ids[] = {"IBM0068", "LEN0068", NULL};
  294 
  295 static void
  296 ibm_led(void *softc, int onoff)
  297 {
  298         struct acpi_ibm_softc* sc = (struct acpi_ibm_softc*) softc;
  299 
  300         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
  301 
  302         if (sc->led_busy)
  303                 return;
  304 
  305         sc->led_busy = 1;
  306         sc->led_state = onoff;
  307 
  308         AcpiOsExecute(OSL_NOTIFY_HANDLER, (void *)ibm_led_task, sc);
  309 }
  310 
  311 static void
  312 ibm_led_task(struct acpi_ibm_softc *sc, int pending __unused)
  313 {
  314         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
  315 
  316         ACPI_SERIAL_BEGIN(ibm);
  317         acpi_ibm_sysctl_set(sc, ACPI_IBM_METHOD_THINKLIGHT, sc->led_state);
  318         ACPI_SERIAL_END(ibm);
  319 
  320         sc->led_busy = 0;
  321 }
  322 
  323 static int
  324 acpi_ibm_probe(device_t dev)
  325 {
  326         if (acpi_disabled("ibm") ||
  327             ACPI_ID_PROBE(device_get_parent(dev), dev, ibm_ids) == NULL ||
  328             device_get_unit(dev) != 0)
  329                 return (ENXIO);
  330 
  331         device_set_desc(dev, "IBM ThinkPad ACPI Extras");
  332 
  333         return (0);
  334 }
  335 
  336 static int
  337 acpi_ibm_attach(device_t dev)
  338 {
  339         struct acpi_ibm_softc   *sc;
  340         devclass_t              ec_devclass;
  341 
  342         ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
  343 
  344         sc = device_get_softc(dev);
  345         sc->dev = dev;
  346         sc->handle = acpi_get_handle(dev);
  347 
  348         /* Look for the first embedded controller */
  349         if (!(ec_devclass = devclass_find ("acpi_ec"))) {
  350                 if (bootverbose)
  351                         device_printf(dev, "Couldn't find acpi_ec devclass\n");
  352                 return (EINVAL);
  353         }
  354         if (!(sc->ec_dev = devclass_get_device(ec_devclass, 0))) {
  355                 if (bootverbose)
  356                         device_printf(dev, "Couldn't find acpi_ec device\n");
  357                 return (EINVAL);
  358         }
  359         sc->ec_handle = acpi_get_handle(sc->ec_dev);
  360 
  361         /* Get the sysctl tree */
  362         sc->sysctl_ctx = device_get_sysctl_ctx(dev);
  363         sc->sysctl_tree = device_get_sysctl_tree(dev);
  364 
  365         /* Look for event mask and hook up the nodes */
  366         sc->events_mask_supported = ACPI_SUCCESS(acpi_GetInteger(sc->handle,
  367             IBM_NAME_EVENTS_MASK_GET, &sc->events_initialmask));
  368 
  369         if (sc->events_mask_supported) {
  370                 SYSCTL_ADD_INT(sc->sysctl_ctx,
  371                     SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
  372                     "initialmask", CTLFLAG_RD,
  373                     &sc->events_initialmask, 0, "Initial eventmask");
  374 
  375                 /* The availmask is the bitmask of supported events */
  376                 if (ACPI_FAILURE(acpi_GetInteger(sc->handle,
  377                     IBM_NAME_EVENTS_AVAILMASK, &sc->events_availmask)))
  378                         sc->events_availmask = 0xffffffff;
  379 
  380                 SYSCTL_ADD_INT(sc->sysctl_ctx,
  381                     SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
  382                     "availmask", CTLFLAG_RD,
  383                     &sc->events_availmask, 0, "Mask of supported events");
  384         }
  385 
  386         /* Hook up proc nodes */
  387         for (int i = 0; acpi_ibm_sysctls[i].name != NULL; i++) {
  388                 if (!acpi_ibm_sysctl_init(sc, acpi_ibm_sysctls[i].method))
  389                         continue;
  390 
  391                 SYSCTL_ADD_PROC(sc->sysctl_ctx,
  392                     SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
  393                     acpi_ibm_sysctls[i].name, acpi_ibm_sysctls[i].access,
  394                     sc, i, acpi_ibm_sysctl, "I",
  395                     acpi_ibm_sysctls[i].description);
  396         }
  397 
  398         /* Hook up thermal node */
  399         if (acpi_ibm_sysctl_init(sc, ACPI_IBM_METHOD_THERMAL)) {
  400                 SYSCTL_ADD_PROC(sc->sysctl_ctx,
  401                     SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
  402                     "thermal", CTLTYPE_STRING | CTLFLAG_RD,
  403                     sc, 0, acpi_ibm_thermal_sysctl, "I",
  404                     "Thermal zones");
  405         }
  406 
  407         /* Handle notifies */
  408         AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY,
  409             acpi_ibm_notify, dev);
  410 
  411         /* Hook up light to led(4) */
  412         if (sc->light_set_supported)
  413                 sc->led_dev = led_create_state(ibm_led, sc, "thinklight", sc->light_val);
  414 
  415         return (0);
  416 }
  417 
  418 static int
  419 acpi_ibm_detach(device_t dev)
  420 {
  421         ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
  422 
  423         struct acpi_ibm_softc *sc = device_get_softc(dev);
  424 
  425         /* Disable events and restore eventmask */
  426         ACPI_SERIAL_BEGIN(ibm);
  427         acpi_ibm_sysctl_set(sc, ACPI_IBM_METHOD_EVENTS, 0);
  428         acpi_ibm_sysctl_set(sc, ACPI_IBM_METHOD_EVENTMASK, sc->events_initialmask);
  429         ACPI_SERIAL_END(ibm);
  430 
  431         AcpiRemoveNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, acpi_ibm_notify);
  432 
  433         if (sc->led_dev != NULL)
  434                 led_destroy(sc->led_dev);
  435 
  436         return (0);
  437 }
  438 
  439 static int
  440 acpi_ibm_resume(device_t dev)
  441 {
  442         struct acpi_ibm_softc *sc = device_get_softc(dev);
  443 
  444         ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
  445 
  446         ACPI_SERIAL_BEGIN(ibm);
  447         for (int i = 0; acpi_ibm_sysctls[i].name != NULL; i++) {
  448                 int val;
  449 
  450                 if ((acpi_ibm_sysctls[i].access & CTLFLAG_RD) == 0) {
  451                         continue;
  452                 }
  453 
  454                 val = acpi_ibm_sysctl_get(sc, i);
  455 
  456                 if ((acpi_ibm_sysctls[i].access & CTLFLAG_WR) == 0) {
  457                         continue;
  458                 }
  459 
  460                 acpi_ibm_sysctl_set(sc, i, val);
  461         }
  462         ACPI_SERIAL_END(ibm);
  463 
  464         return (0);
  465 }
  466 
  467 static int
  468 acpi_ibm_eventmask_set(struct acpi_ibm_softc *sc, int val)
  469 {
  470         ACPI_OBJECT             arg[2];
  471         ACPI_OBJECT_LIST        args;
  472         ACPI_STATUS             status;
  473 
  474         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
  475         ACPI_SERIAL_ASSERT(ibm);
  476 
  477         args.Count = 2;
  478         args.Pointer = arg;
  479         arg[0].Type = ACPI_TYPE_INTEGER;
  480         arg[1].Type = ACPI_TYPE_INTEGER;
  481 
  482         for (int i = 0; i < 32; ++i) {
  483                 arg[0].Integer.Value = i+1;
  484                 arg[1].Integer.Value = (((1 << i) & val) != 0);
  485                 status = AcpiEvaluateObject(sc->handle,
  486                     IBM_NAME_EVENTS_MASK_SET, &args, NULL);
  487 
  488                 if (ACPI_FAILURE(status))
  489                         return (status);
  490         }
  491 
  492         return (0);
  493 }
  494 
  495 static int
  496 acpi_ibm_sysctl(SYSCTL_HANDLER_ARGS)
  497 {
  498         struct acpi_ibm_softc   *sc;
  499         int                     arg;
  500         int                     error = 0;
  501         int                     function;
  502         int                     method;
  503         
  504         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
  505 
  506         sc = (struct acpi_ibm_softc *)oidp->oid_arg1;
  507         function = oidp->oid_arg2;
  508         method = acpi_ibm_sysctls[function].method;
  509 
  510         ACPI_SERIAL_BEGIN(ibm);
  511         arg = acpi_ibm_sysctl_get(sc, method);
  512         error = sysctl_handle_int(oidp, &arg, 0, req);
  513 
  514         /* Sanity check */
  515         if (error != 0 || req->newptr == NULL)
  516                 goto out;
  517 
  518         /* Update */
  519         error = acpi_ibm_sysctl_set(sc, method, arg);
  520 
  521 out:
  522         ACPI_SERIAL_END(ibm);
  523         return (error);
  524 }
  525 
  526 static int
  527 acpi_ibm_sysctl_get(struct acpi_ibm_softc *sc, int method)
  528 {
  529         UINT64          val_ec;
  530         int             val = 0, key;
  531 
  532         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
  533         ACPI_SERIAL_ASSERT(ibm);
  534 
  535         switch (method) {
  536         case ACPI_IBM_METHOD_EVENTS:
  537                 acpi_GetInteger(sc->handle, IBM_NAME_EVENTS_STATUS_GET, &val);
  538                 break;
  539 
  540         case ACPI_IBM_METHOD_EVENTMASK:
  541                 if (sc->events_mask_supported)
  542                         acpi_GetInteger(sc->handle, IBM_NAME_EVENTS_MASK_GET, &val);
  543                 break;
  544 
  545         case ACPI_IBM_METHOD_HOTKEY:
  546                 /*
  547                  * Construct the hotkey as a bitmask as illustrated below.
  548                  * Note that whenever a key was pressed, the respecting bit
  549                  * toggles and nothing else changes.
  550                  * +--+--+-+-+-+-+-+-+-+-+-+-+
  551                  * |11|10|9|8|7|6|5|4|3|2|1|0|
  552                  * +--+--+-+-+-+-+-+-+-+-+-+-+
  553                  *   |  | | | | | | | | | | |
  554                  *   |  | | | | | | | | | | +- Home Button
  555                  *   |  | | | | | | | | | +--- Search Button
  556                  *   |  | | | | | | | | +----- Mail Button
  557                  *   |  | | | | | | | +------- Thinkpad Button
  558                  *   |  | | | | | | +--------- Zoom (Fn + Space)
  559                  *   |  | | | | | +----------- WLAN Button
  560                  *   |  | | | | +------------- Video Button
  561                  *   |  | | | +--------------- Hibernate Button
  562                  *   |  | | +----------------- Thinklight Button
  563                  *   |  | +------------------- Screen expand (Fn + F8)
  564                  *   |  +--------------------- Brightness
  565                  *   +------------------------ Volume/Mute
  566                  */
  567                 key = rtcin(IBM_RTC_HOTKEY1);
  568                 val = (IBM_RTC_MASK_HOME | IBM_RTC_MASK_SEARCH | IBM_RTC_MASK_MAIL | IBM_RTC_MASK_WLAN) & key;
  569                 key = rtcin(IBM_RTC_HOTKEY2);
  570                 val |= (IBM_RTC_MASK_THINKPAD | IBM_RTC_MASK_VIDEO | IBM_RTC_MASK_HIBERNATE) & key;
  571                 val |= (IBM_RTC_MASK_ZOOM & key) >> 1;
  572                 key = rtcin(IBM_RTC_THINKLIGHT);
  573                 val |= (IBM_RTC_MASK_THINKLIGHT & key) << 4;
  574                 key = rtcin(IBM_RTC_SCREENEXPAND);
  575                 val |= (IBM_RTC_MASK_THINKLIGHT & key) << 4;
  576                 key = rtcin(IBM_RTC_BRIGHTNESS);
  577                 val |= (IBM_RTC_MASK_BRIGHTNESS & key) << 5;
  578                 key = rtcin(IBM_RTC_VOLUME);
  579                 val |= (IBM_RTC_MASK_VOLUME & key) << 4;
  580                 break;
  581 
  582         case ACPI_IBM_METHOD_BRIGHTNESS:
  583                 ACPI_EC_READ(sc->ec_dev, IBM_EC_BRIGHTNESS, &val_ec, 1);
  584                 val = val_ec & IBM_EC_MASK_BRI;
  585                 break;
  586 
  587         case ACPI_IBM_METHOD_VOLUME:
  588                 ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1);
  589                 val = val_ec & IBM_EC_MASK_VOL;
  590                 break;
  591 
  592         case ACPI_IBM_METHOD_MUTE:
  593                 ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1);
  594                 val = ((val_ec & IBM_EC_MASK_MUTE) == IBM_EC_MASK_MUTE);
  595                 break;
  596 
  597         case ACPI_IBM_METHOD_THINKLIGHT:
  598                 if (sc->light_get_supported)
  599                         acpi_GetInteger(sc->ec_handle, IBM_NAME_KEYLIGHT, &val);
  600                 else
  601                         val = sc->light_val;
  602                 break;
  603 
  604         case ACPI_IBM_METHOD_BLUETOOTH:
  605                 acpi_GetInteger(sc->handle, IBM_NAME_WLAN_BT_GET, &val);
  606                 sc->wlan_bt_flags = val;
  607                 val = ((val & IBM_NAME_MASK_BT) != 0);
  608                 break;
  609 
  610         case ACPI_IBM_METHOD_WLAN:
  611                 acpi_GetInteger(sc->handle, IBM_NAME_WLAN_BT_GET, &val);
  612                 sc->wlan_bt_flags = val;
  613                 val = ((val & IBM_NAME_MASK_WLAN) != 0);
  614                 break;
  615 
  616         case ACPI_IBM_METHOD_FANSPEED:
  617                 if (sc->fan_handle) {
  618                         if(ACPI_FAILURE(acpi_GetInteger(sc->fan_handle, NULL, &val)))
  619                                 val = -1;
  620                 }
  621                 else {
  622                         ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSPEED, &val_ec, 2);
  623                         val = val_ec;
  624                 }
  625                 break;
  626 
  627         case ACPI_IBM_METHOD_FANLEVEL:
  628                 /*
  629                  * The IBM_EC_FANSTATUS register works as follows:
  630                  * Bit 0-5 indicate the level at which the fan operates. Only
  631                  *       values between 0 and 7 have an effect. Everything
  632                  *       above 7 is treated the same as level 7
  633                  * Bit 6 overrides the fan speed limit if set to 1
  634                  * Bit 7 indicates at which mode the fan operates:
  635                  *       manual (0) or automatic (1)
  636                  */
  637                 if (!sc->fan_handle) {
  638                         ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1);
  639                         val = val_ec & IBM_EC_MASK_FANLEVEL;
  640                 }
  641                 break;
  642 
  643         case ACPI_IBM_METHOD_FANSTATUS:
  644                 if (!sc->fan_handle) {
  645                         ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1);
  646                         val = (val_ec & IBM_EC_MASK_FANSTATUS) == IBM_EC_MASK_FANSTATUS;
  647                 }
  648                 else
  649                         val = -1;
  650                 break;
  651         }
  652 
  653         return (val);
  654 }
  655 
  656 static int
  657 acpi_ibm_sysctl_set(struct acpi_ibm_softc *sc, int method, int arg)
  658 {
  659         int                     val, step;
  660         UINT64                  val_ec;
  661         ACPI_OBJECT             Arg;
  662         ACPI_OBJECT_LIST        Args;
  663         ACPI_STATUS             status;
  664 
  665         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
  666         ACPI_SERIAL_ASSERT(ibm);
  667 
  668         switch (method) {
  669         case ACPI_IBM_METHOD_EVENTS:
  670                 if (arg < 0 || arg > 1)
  671                         return (EINVAL);
  672 
  673                 status = acpi_SetInteger(sc->handle, IBM_NAME_EVENTS_STATUS_SET, arg);
  674                 if (ACPI_FAILURE(status))
  675                         return (status);
  676                 if (sc->events_mask_supported)
  677                         return acpi_ibm_eventmask_set(sc, sc->events_availmask);
  678                 break;
  679 
  680         case ACPI_IBM_METHOD_EVENTMASK:
  681                 if (sc->events_mask_supported)
  682                         return acpi_ibm_eventmask_set(sc, arg);
  683                 break;
  684 
  685         case ACPI_IBM_METHOD_BRIGHTNESS:
  686                 if (arg < 0 || arg > 7)
  687                         return (EINVAL);
  688 
  689                 if (sc->cmos_handle) {
  690                         /* Read the current brightness */
  691                         status = ACPI_EC_READ(sc->ec_dev, IBM_EC_BRIGHTNESS, &val_ec, 1);
  692                         if (ACPI_FAILURE(status))
  693                                 return (status);
  694                         val = val_ec & IBM_EC_MASK_BRI;
  695 
  696                         Args.Count = 1;
  697                         Args.Pointer = &Arg;
  698                         Arg.Type = ACPI_TYPE_INTEGER;
  699                         Arg.Integer.Value = (arg > val) ? IBM_CMOS_BRIGHTNESS_UP : IBM_CMOS_BRIGHTNESS_DOWN;
  700 
  701                         step = (arg > val) ? 1 : -1;
  702                         for (int i = val; i != arg; i += step) {
  703                                 status = AcpiEvaluateObject(sc->cmos_handle, NULL, &Args, NULL);
  704                                 if (ACPI_FAILURE(status))
  705                                         break;
  706                         }
  707                 }
  708                 return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_BRIGHTNESS, arg, 1);
  709                 break;
  710 
  711         case ACPI_IBM_METHOD_VOLUME:
  712                 if (arg < 0 || arg > 14)
  713                         return (EINVAL);
  714 
  715                 status = ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1);
  716                 if (ACPI_FAILURE(status))
  717                         return (status);
  718 
  719                 if (sc->cmos_handle) {
  720                         val = val_ec & IBM_EC_MASK_VOL;
  721 
  722                         Args.Count = 1;
  723                         Args.Pointer = &Arg;
  724                         Arg.Type = ACPI_TYPE_INTEGER;
  725                         Arg.Integer.Value = (arg > val) ? IBM_CMOS_VOLUME_UP : IBM_CMOS_VOLUME_DOWN;
  726 
  727                         step = (arg > val) ? 1 : -1;
  728                         for (int i = val; i != arg; i += step) {
  729                                 status = AcpiEvaluateObject(sc->cmos_handle, NULL, &Args, NULL);
  730                                 if (ACPI_FAILURE(status))
  731                                         break;
  732                         }
  733                 }
  734                 return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_VOLUME, arg + (val_ec & (~IBM_EC_MASK_VOL)), 1);
  735                 break;
  736 
  737         case ACPI_IBM_METHOD_MUTE:
  738                 if (arg < 0 || arg > 1)
  739                         return (EINVAL);
  740 
  741                 status = ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1);
  742                 if (ACPI_FAILURE(status))
  743                         return (status);
  744 
  745                 if (sc->cmos_handle) {
  746                         Args.Count = 1;
  747                         Args.Pointer = &Arg;
  748                         Arg.Type = ACPI_TYPE_INTEGER;
  749                         Arg.Integer.Value = IBM_CMOS_VOLUME_MUTE;
  750 
  751                         status = AcpiEvaluateObject(sc->cmos_handle, NULL, &Args, NULL);
  752                         if (ACPI_FAILURE(status))
  753                                 break;
  754                 }
  755                 return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_VOLUME, (arg==1) ? val_ec | IBM_EC_MASK_MUTE : val_ec & (~IBM_EC_MASK_MUTE), 1);
  756                 break;
  757 
  758         case ACPI_IBM_METHOD_THINKLIGHT:
  759                 if (arg < 0 || arg > 1)
  760                         return (EINVAL);
  761 
  762                 if (sc->light_set_supported) {
  763                         Args.Count = 1;
  764                         Args.Pointer = &Arg;
  765                         Arg.Type = ACPI_TYPE_INTEGER;
  766                         Arg.Integer.Value = arg ? sc->light_cmd_on : sc->light_cmd_off;
  767 
  768                         status = AcpiEvaluateObject(sc->light_handle, NULL, &Args, NULL);
  769                         if (ACPI_SUCCESS(status))
  770                                 sc->light_val = arg;
  771                         return (status);
  772                 }
  773                 break;
  774 
  775         case ACPI_IBM_METHOD_BLUETOOTH:
  776                 if (arg < 0 || arg > 1)
  777                         return (EINVAL);
  778 
  779                 val = (arg == 1) ? sc->wlan_bt_flags | IBM_NAME_MASK_BT : sc->wlan_bt_flags & (~IBM_NAME_MASK_BT);
  780                 return acpi_SetInteger(sc->handle, IBM_NAME_WLAN_BT_SET, val);
  781                 break;
  782 
  783         case ACPI_IBM_METHOD_FANLEVEL:
  784                 if (arg < 0 || arg > 7)
  785                         return (EINVAL);
  786 
  787                 if (!sc->fan_handle) {
  788                         /* Read the current fanstatus */
  789                         ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1);
  790                         val = val_ec & (~IBM_EC_MASK_FANLEVEL);
  791 
  792                         return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_FANSTATUS, val | arg, 1);
  793                 }
  794                 break;
  795 
  796         case ACPI_IBM_METHOD_FANSTATUS:
  797                 if (arg < 0 || arg > 1)
  798                         return (EINVAL);
  799 
  800                 if (!sc->fan_handle) {
  801                         /* Read the current fanstatus */
  802                         ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1);
  803 
  804                         return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_FANSTATUS,
  805                                 (arg == 1) ? (val_ec | IBM_EC_MASK_FANSTATUS) : (val_ec & (~IBM_EC_MASK_FANSTATUS)), 1);
  806                 }
  807                 break;
  808         }
  809 
  810         return (0);
  811 }
  812 
  813 static int
  814 acpi_ibm_sysctl_init(struct acpi_ibm_softc *sc, int method)
  815 {
  816         int                     dummy;
  817         ACPI_OBJECT_TYPE        cmos_t;
  818         ACPI_HANDLE             ledb_handle;
  819 
  820         switch (method) {
  821         case ACPI_IBM_METHOD_EVENTS:
  822                 /* Events are disabled by default */
  823                 return (TRUE);
  824 
  825         case ACPI_IBM_METHOD_EVENTMASK:
  826                 return (sc->events_mask_supported);
  827 
  828         case ACPI_IBM_METHOD_HOTKEY:
  829         case ACPI_IBM_METHOD_BRIGHTNESS:
  830         case ACPI_IBM_METHOD_VOLUME:
  831         case ACPI_IBM_METHOD_MUTE:
  832                 /* EC is required here, which was aready checked before */
  833                 return (TRUE);
  834 
  835         case ACPI_IBM_METHOD_THINKLIGHT:
  836                 sc->cmos_handle = NULL;
  837                 sc->light_get_supported = ACPI_SUCCESS(acpi_GetInteger(
  838                     sc->ec_handle, IBM_NAME_KEYLIGHT, &sc->light_val));
  839 
  840                 if ((ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\UCMS", &sc->light_handle)) ||
  841                      ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\CMOS", &sc->light_handle)) ||
  842                      ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\CMS", &sc->light_handle))) &&
  843                      ACPI_SUCCESS(AcpiGetType(sc->light_handle, &cmos_t)) &&
  844                      cmos_t == ACPI_TYPE_METHOD) {
  845                         sc->light_cmd_on = 0x0c;
  846                         sc->light_cmd_off = 0x0d;
  847                         sc->cmos_handle = sc->light_handle;
  848                 }
  849                 else if (ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\LGHT", &sc->light_handle))) {
  850                         sc->light_cmd_on = 1;
  851                         sc->light_cmd_off = 0;
  852                 }
  853                 else
  854                         sc->light_handle = NULL;
  855 
  856                 sc->light_set_supported = (sc->light_handle &&
  857                     ACPI_FAILURE(AcpiGetHandle(sc->ec_handle, "LEDB", &ledb_handle)));
  858 
  859                 if (sc->light_get_supported)
  860                         return (TRUE);
  861 
  862                 if (sc->light_set_supported) {
  863                         sc->light_val = 0;
  864                         return (TRUE);
  865                 }
  866 
  867                 return (FALSE);
  868 
  869         case ACPI_IBM_METHOD_BLUETOOTH:
  870         case ACPI_IBM_METHOD_WLAN:
  871                 if (ACPI_SUCCESS(acpi_GetInteger(sc->handle, IBM_NAME_WLAN_BT_GET, &dummy)))
  872                         return (TRUE);
  873                 return (FALSE);
  874 
  875         case ACPI_IBM_METHOD_FANSPEED:
  876                 /* 
  877                  * Some models report the fan speed in levels from 0-7
  878                  * Newer models report it contiguously
  879                  */
  880                 sc->fan_levels =
  881                     (ACPI_SUCCESS(AcpiGetHandle(sc->handle, "GFAN", &sc->fan_handle)) ||
  882                      ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\FSPD", &sc->fan_handle)));
  883                 return (TRUE);
  884 
  885         case ACPI_IBM_METHOD_FANLEVEL:
  886         case ACPI_IBM_METHOD_FANSTATUS:
  887                 /* 
  888                  * Fan status is only supported on those models,
  889                  * which report fan RPM contiguously, not in levels
  890                  */
  891                 if (sc->fan_levels)
  892                         return (FALSE);
  893                 return (TRUE);
  894 
  895         case ACPI_IBM_METHOD_THERMAL:
  896                 if (ACPI_SUCCESS(acpi_GetInteger(sc->ec_handle, IBM_NAME_THERMAL_GET, &dummy))) {
  897                         sc->thermal_updt_supported = ACPI_SUCCESS(acpi_GetInteger(sc->ec_handle, IBM_NAME_THERMAL_UPDT, &dummy));
  898                         return (TRUE);
  899                 }
  900                 return (FALSE);
  901         }
  902         return (FALSE);
  903 }
  904 
  905 static int
  906 acpi_ibm_thermal_sysctl(SYSCTL_HANDLER_ARGS)
  907 {
  908         struct acpi_ibm_softc   *sc;
  909         int                     error = 0;
  910         char                    temp_cmd[] = "TMP0";
  911         int                     temp[8];
  912 
  913         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
  914 
  915         sc = (struct acpi_ibm_softc *)oidp->oid_arg1;
  916 
  917         ACPI_SERIAL_BEGIN(ibm);
  918 
  919         for (int i = 0; i < 8; ++i) {
  920                 temp_cmd[3] = '' + i;
  921                 
  922                 /* 
  923                  * The TMPx methods seem to return +/- 128 or 0
  924                  * when the respecting sensor is not available 
  925                  */
  926                 if (ACPI_FAILURE(acpi_GetInteger(sc->ec_handle, temp_cmd,
  927                     &temp[i])) || ABS(temp[i]) == 128 || temp[i] == 0)
  928                         temp[i] = -1;
  929                 else if (sc->thermal_updt_supported)
  930                         /* Temperature is reported in tenth of Kelvin */
  931                         temp[i] = (temp[i] - 2732 + 5) / 10;
  932         }
  933 
  934         error = sysctl_handle_opaque(oidp, &temp, 8*sizeof(int), req);
  935 
  936         ACPI_SERIAL_END(ibm);
  937         return (error);
  938 }
  939 
  940 static void
  941 acpi_ibm_notify(ACPI_HANDLE h, UINT32 notify, void *context)
  942 {
  943         int             event, arg, type;
  944         device_t        dev = context;
  945         struct acpi_ibm_softc *sc = device_get_softc(dev);
  946 
  947         ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, notify);
  948 
  949         if (notify != 0x80)
  950                 device_printf(dev, "Unknown notify\n");
  951 
  952         for (;;) {
  953                 acpi_GetInteger(acpi_get_handle(dev), IBM_NAME_EVENTS_GET, &event);
  954 
  955                 if (event == 0)
  956                         break;
  957 
  958 
  959                 type = (event >> 12) & 0xf;
  960                 arg = event & 0xfff;
  961                 switch (type) {
  962                 case 1:
  963                         if (!(sc->events_availmask & (1 << (arg - 1)))) {
  964                                 device_printf(dev, "Unknown key %d\n", arg);
  965                                 break;
  966                         }
  967 
  968                         /* Notify devd(8) */
  969                         acpi_UserNotify("IBM", h, (arg & 0xff));
  970                         break;
  971                 default:
  972                         break;
  973                 }
  974         }
  975 }

Cache object: a46caa629f03a127d6a8e4d9c3613542


[ 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.