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_thinkpad/acpi_thinkpad.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  * $FreeBSD: src/sys/dev/acpi_support/acpi_ibm.c,v 1.15 2007/10/25 17:30:18 jhb Exp $
   28  */
   29 
   30 /*
   31  * Driver for extra ACPI-controlled gadgets found on IBM and Lenovo ThinkPad
   32  * laptops. Inspired by the ibm-acpi and tpb projects which implement these
   33  * features on Linux.
   34  *
   35  *   acpi-ibm: <http://ibm-acpi.sourceforge.net/>
   36  *        tpb: <http://www.nongnu.org/tpb/>
   37  */
   38 
   39 #include "opt_acpi.h"
   40 #include <sys/param.h>
   41 #include <sys/kernel.h>
   42 #include <sys/bus.h>
   43 #include <machine/cpufunc.h>
   44 #include <sys/module.h>
   45 #include <sys/sensors.h>
   46 #include <sys/sysctl.h>
   47 #include <sys/lock.h>
   48 #include <sys/thread2.h>
   49 #include <machine/clock.h>
   50 
   51 #include "acpi.h"
   52 #include "accommon.h"
   53 #include "acpivar.h"
   54 #include "acpi_if.h"
   55 
   56 #define _COMPONENT      ACPI_OEM
   57 ACPI_MODULE_NAME("THINKPAD")
   58 
   59 /* Internal methods */
   60 #define ACPI_THINKPAD_METHOD_EVENTS             1
   61 #define ACPI_THINKPAD_METHOD_EVENTMASK          2
   62 #define ACPI_THINKPAD_METHOD_HOTKEY             3
   63 #define ACPI_THINKPAD_METHOD_BRIGHTNESS         4
   64 #define ACPI_THINKPAD_METHOD_VOLUME             5
   65 #define ACPI_THINKPAD_METHOD_MUTE               6
   66 #define ACPI_THINKPAD_METHOD_THINKLIGHT         7
   67 #define ACPI_THINKPAD_METHOD_BLUETOOTH          8
   68 #define ACPI_THINKPAD_METHOD_WLAN               9
   69 #define ACPI_THINKPAD_METHOD_FANSPEED           10
   70 #define ACPI_THINKPAD_METHOD_FANLEVEL           11
   71 #define ACPI_THINKPAD_METHOD_FANSTATUS          12
   72 #define ACPI_THINKPAD_METHOD_THERMAL            13
   73 
   74 /* Hotkeys/Buttons */
   75 #define THINKPAD_RTC_HOTKEY1                    0x64
   76 #define   THINKPAD_RTC_MASK_HOME                (1 << 0)
   77 #define   THINKPAD_RTC_MASK_SEARCH              (1 << 1)
   78 #define   THINKPAD_RTC_MASK_MAIL                (1 << 2)
   79 #define   THINKPAD_RTC_MASK_WLAN                (1 << 5)
   80 #define THINKPAD_RTC_HOTKEY2                    0x65
   81 #define   THINKPAD_RTC_MASK_THINKPAD            (1 << 3)
   82 #define   THINKPAD_RTC_MASK_ZOOM                (1 << 5)
   83 #define   THINKPAD_RTC_MASK_VIDEO               (1 << 6)
   84 #define   THINKPAD_RTC_MASK_HIBERNATE           (1 << 7)
   85 #define THINKPAD_RTC_THINKLIGHT                 0x66
   86 #define   THINKPAD_RTC_MASK_THINKLIGHT          (1 << 4)
   87 #define THINKPAD_RTC_SCREENEXPAND               0x67
   88 #define   THINKPAD_RTC_MASK_SCREENEXPAND        (1 << 5)
   89 #define THINKPAD_RTC_BRIGHTNESS                 0x6c
   90 #define   THINKPAD_RTC_MASK_BRIGHTNESS          (1 << 5)
   91 #define THINKPAD_RTC_VOLUME                     0x6e
   92 #define   THINKPAD_RTC_MASK_VOLUME              (1 << 7)
   93 
   94 /* Embedded Controller registers */
   95 #define THINKPAD_EC_BRIGHTNESS                  0x31
   96 #define   THINKPAD_EC_MASK_BRI                  0x7
   97 #define THINKPAD_EC_VOLUME                      0x30
   98 #define   THINKPAD_EC_MASK_VOL                  0xf
   99 #define   THINKPAD_EC_MASK_MUTE                 (1 << 6)
  100 #define THINKPAD_EC_FANSTATUS                   0x2F
  101 #define   THINKPAD_EC_MASK_FANLEVEL             0x3f
  102 #define   THINKPAD_EC_MASK_FANDISENGAGED        (1 << 6)
  103 #define   THINKPAD_EC_MASK_FANSTATUS            (1 << 7)
  104 #define THINKPAD_EC_FANSPEED                    0x84
  105 
  106 /* CMOS Commands */
  107 #define THINKPAD_CMOS_VOLUME_DOWN               0
  108 #define THINKPAD_CMOS_VOLUME_UP                 1
  109 #define THINKPAD_CMOS_VOLUME_MUTE               2
  110 #define THINKPAD_CMOS_BRIGHTNESS_UP             4
  111 #define THINKPAD_CMOS_BRIGHTNESS_DOWN           5
  112 
  113 /* ACPI methods */
  114 #define THINKPAD_NAME_KEYLIGHT                  "KBLT"
  115 #define THINKPAD_NAME_WLAN_BT_GET               "GBDC"
  116 #define THINKPAD_NAME_WLAN_BT_SET               "SBDC"
  117 #define   THINKPAD_NAME_MASK_BT                 (1 << 1)
  118 #define   THINKPAD_NAME_MASK_WLAN               (1 << 2)
  119 #define THINKPAD_NAME_THERMAL_GET               "TMP7"
  120 #define THINKPAD_NAME_THERMAL_UPDT              "UPDT"
  121 
  122 #define THINKPAD_NAME_EVENTS_STATUS_GET         "DHKC"
  123 #define THINKPAD_NAME_EVENTS_MASK_GET           "DHKN"
  124 #define THINKPAD_NAME_EVENTS_STATUS_SET         "MHKC"
  125 #define THINKPAD_NAME_EVENTS_MASK_SET           "MHKM"
  126 #define THINKPAD_NAME_EVENTS_GET                "MHKP"
  127 #define THINKPAD_NAME_EVENTS_AVAILMASK          "MHKA"
  128 
  129 #define THINKPAD_NUM_SENSORS                    9
  130 #define THINKPAD_TEMP_SENSORS                   8
  131 
  132 #define ABS(x) (((x) < 0)? -(x) : (x))
  133 
  134 struct acpi_thinkpad_softc {
  135         device_t        dev;
  136         ACPI_HANDLE     handle;
  137 
  138         /* Embedded controller */
  139         device_t        ec_dev;
  140         ACPI_HANDLE     ec_handle;
  141 
  142         /* CMOS */
  143         ACPI_HANDLE     cmos_handle;
  144 
  145         /* Fan status */
  146         ACPI_HANDLE     fan_handle;
  147         int             fan_levels;
  148 
  149         /* Keylight commands and states */
  150         ACPI_HANDLE     light_handle;
  151         int             light_cmd_on;
  152         int             light_cmd_off;
  153         int             light_val;
  154         int             light_get_supported;
  155         int             light_set_supported;
  156 
  157         /* led(4) interface */
  158         struct cdev     *led_dev;
  159         int             led_busy;
  160         int             led_state;
  161 
  162         int             wlan_bt_flags;
  163         int             thermal_updt_supported;
  164 
  165         unsigned int    events_availmask;
  166         unsigned int    events_initialmask;
  167         int             events_mask_supported;
  168         int             events_enable;
  169 
  170         /* sensors(9) related */
  171         struct ksensordev sensordev;
  172         struct ksensor sensors[THINKPAD_NUM_SENSORS];
  173 
  174         struct sysctl_ctx_list   sysctl_ctx;
  175         struct sysctl_oid       *sysctl_tree;
  176 };
  177 
  178 static struct {
  179         char    *name;
  180         int     method;
  181         char    *description;
  182         int     access;
  183 } acpi_thinkpad_sysctls[] = {
  184         {
  185                 .name           = "events",
  186                 .method         = ACPI_THINKPAD_METHOD_EVENTS,
  187                 .description    = "ACPI events enable",
  188                 .access         = CTLTYPE_INT | CTLFLAG_RW
  189         },
  190         {
  191                 .name           = "eventmask",
  192                 .method         = ACPI_THINKPAD_METHOD_EVENTMASK,
  193                 .description    = "ACPI eventmask",
  194                 .access         = CTLTYPE_INT | CTLFLAG_RW
  195         },
  196         {
  197                 .name           = "hotkey",
  198                 .method         = ACPI_THINKPAD_METHOD_HOTKEY,
  199                 .description    = "Key Status",
  200                 .access         = CTLTYPE_INT | CTLFLAG_RD
  201         },
  202         {
  203                 .name           = "lcd_brightness",
  204                 .method         = ACPI_THINKPAD_METHOD_BRIGHTNESS,
  205                 .description    = "LCD Brightness",
  206                 .access         = CTLTYPE_INT | CTLFLAG_RW
  207         },
  208         {
  209                 .name           = "volume",
  210                 .method         = ACPI_THINKPAD_METHOD_VOLUME,
  211                 .description    = "Volume",
  212                 .access         = CTLTYPE_INT | CTLFLAG_RW
  213         },
  214         {
  215                 .name           = "mute",
  216                 .method         = ACPI_THINKPAD_METHOD_MUTE,
  217                 .description    = "Mute",
  218                 .access         = CTLTYPE_INT | CTLFLAG_RW
  219         },
  220         {
  221                 .name           = "thinklight",
  222                 .method         = ACPI_THINKPAD_METHOD_THINKLIGHT,
  223                 .description    = "Thinklight enable",
  224                 .access         = CTLTYPE_INT | CTLFLAG_RW
  225         },
  226         {
  227                 .name           = "bluetooth",
  228                 .method         = ACPI_THINKPAD_METHOD_BLUETOOTH,
  229                 .description    = "Bluetooth enable",
  230                 .access         = CTLTYPE_INT | CTLFLAG_RW
  231         },
  232         {
  233                 .name           = "wlan",
  234                 .method         = ACPI_THINKPAD_METHOD_WLAN,
  235                 .description    = "WLAN enable",
  236                 .access         = CTLTYPE_INT | CTLFLAG_RD
  237         },
  238         {
  239                 .name           = "fan_level",
  240                 .method         = ACPI_THINKPAD_METHOD_FANLEVEL,
  241                 .description    = "Fan level",
  242                 .access         = CTLTYPE_INT | CTLFLAG_RW
  243         },
  244         {
  245                 .name           = "fan",
  246                 .method         = ACPI_THINKPAD_METHOD_FANSTATUS,
  247                 .description    = "Fan enable",
  248                 .access         = CTLTYPE_INT | CTLFLAG_RW
  249         },
  250 
  251         { NULL, 0, NULL, 0 }
  252 };
  253 
  254 static struct lock tplock;
  255 
  256 static int      acpi_thinkpad_probe(device_t dev);
  257 static int      acpi_thinkpad_attach(device_t dev);
  258 static int      acpi_thinkpad_detach(device_t dev);
  259 
  260 static int      acpi_thinkpad_sysctl(SYSCTL_HANDLER_ARGS);
  261 static int      acpi_thinkpad_sysctl_init(struct acpi_thinkpad_softc *sc,
  262                 int method);
  263 static int      acpi_thinkpad_sysctl_get(struct acpi_thinkpad_softc *sc,
  264                 int method);
  265 static int      acpi_thinkpad_sysctl_set(struct acpi_thinkpad_softc *sc,
  266                 int method, int val);
  267 
  268 static int      acpi_thinkpad_eventmask_set(struct acpi_thinkpad_softc *sc,
  269                 int val);
  270 static void     acpi_thinkpad_notify(ACPI_HANDLE h, UINT32 notify,
  271                 void *context);
  272 static void     acpi_thinkpad_refresh(void *);
  273 
  274 static device_method_t acpi_thinkpad_methods[] = {
  275         /* Device interface */
  276         DEVMETHOD(device_probe, acpi_thinkpad_probe),
  277         DEVMETHOD(device_attach, acpi_thinkpad_attach),
  278         DEVMETHOD(device_detach, acpi_thinkpad_detach),
  279         DEVMETHOD_END
  280 };
  281 
  282 static driver_t acpi_thinkpad_driver = {
  283         "acpi_thinkpad",
  284         acpi_thinkpad_methods,
  285         sizeof(struct acpi_thinkpad_softc),
  286 };
  287 
  288 static devclass_t acpi_thinkpad_devclass;
  289 
  290 DRIVER_MODULE(acpi_thinkpad, acpi, acpi_thinkpad_driver,
  291     acpi_thinkpad_devclass, 0, 0);
  292 MODULE_DEPEND(acpi_thinkpad, acpi, 1, 1, 1);
  293 static char    *thinkpad_ids[] = {"IBM0068", NULL};
  294 
  295 static int
  296 acpi_thinkpad_probe(device_t dev)
  297 {
  298         if (acpi_disabled("thinkpad") ||
  299             ACPI_ID_PROBE(device_get_parent(dev), dev, thinkpad_ids) == NULL ||
  300             device_get_unit(dev) != 0) 
  301                 return (ENXIO);
  302 
  303         device_set_desc(dev, "IBM/Lenovo ThinkPad ACPI Extras");
  304         return (0);
  305 }
  306 
  307 static int
  308 acpi_thinkpad_attach(device_t dev)
  309 {
  310         struct acpi_thinkpad_softc      *sc;
  311         struct acpi_softc       *acpi_sc;
  312         devclass_t              ec_devclass;
  313         int                     i;
  314 
  315         ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
  316 
  317         sc = device_get_softc(dev);
  318         sc->dev = dev;
  319         sc->handle = acpi_get_handle(dev);
  320 
  321         acpi_sc = acpi_device_get_parent_softc(dev);
  322 
  323         /* Look for the first embedded controller */
  324         if (!(ec_devclass = devclass_find ("acpi_ec"))) {
  325                 if (bootverbose)
  326                         device_printf(dev, "Couldn't find acpi_ec devclass\n");
  327                 return (EINVAL);
  328         }
  329         if (!(sc->ec_dev = devclass_get_device(ec_devclass, 0))) {
  330                 if (bootverbose)
  331                         device_printf(dev, "Couldn't find acpi_ec device\n");
  332                 return (EINVAL);
  333         }
  334         sc->ec_handle = acpi_get_handle(sc->ec_dev);
  335         
  336         lockinit(&tplock, "thinkpad", 0, 0);
  337         lockmgr(&tplock, LK_EXCLUSIVE);
  338 
  339         sysctl_ctx_init(&sc->sysctl_ctx);
  340         sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
  341             SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree), OID_AUTO,
  342             "thinkpad", CTLFLAG_RD, 0, "");
  343 
  344         /* Look for event mask and hook up the nodes */
  345         sc->events_mask_supported = ACPI_SUCCESS(acpi_GetInteger(sc->handle,
  346             THINKPAD_NAME_EVENTS_MASK_GET, &sc->events_initialmask));
  347 
  348         if (sc->events_mask_supported) {
  349                 SYSCTL_ADD_INT(&sc->sysctl_ctx,
  350                     SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
  351                     "initialmask", CTLFLAG_RD,
  352                     &sc->events_initialmask, 0, "Initial eventmask");
  353 
  354                 /* The availmask is the bitmask of supported events */
  355                 if (ACPI_FAILURE(acpi_GetInteger(sc->handle,
  356                     THINKPAD_NAME_EVENTS_AVAILMASK, &sc->events_availmask)))
  357                         sc->events_availmask = 0xffffffff;
  358 
  359                 SYSCTL_ADD_INT(&sc->sysctl_ctx,
  360                     SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
  361                     "availmask", CTLFLAG_RD,
  362                     &sc->events_availmask, 0, "Mask of supported events");
  363         }
  364 
  365         /* Hook up proc nodes */
  366         for (i = 0; acpi_thinkpad_sysctls[i].name != NULL; i++) {
  367                 if (!acpi_thinkpad_sysctl_init(sc,
  368                     acpi_thinkpad_sysctls[i].method))
  369                         continue;
  370 
  371                 SYSCTL_ADD_PROC(&sc->sysctl_ctx,
  372                     SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
  373                     acpi_thinkpad_sysctls[i].name,
  374                     acpi_thinkpad_sysctls[i].access,
  375                     sc, i, acpi_thinkpad_sysctl, "I",
  376                     acpi_thinkpad_sysctls[i].description);
  377         }
  378 
  379         lockmgr(&tplock, LK_RELEASE);
  380 
  381         /* Handle notifies */
  382         AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY,
  383             acpi_thinkpad_notify, dev);
  384 
  385         /* Attach sensors(9). */
  386         if (sensor_task_register(sc, acpi_thinkpad_refresh, 5)) {
  387                 device_printf(sc->dev, "unable to register update task\n");
  388                 return 1;
  389         }
  390 
  391         strlcpy(sc->sensordev.xname, device_get_nameunit(sc->dev),
  392             sizeof(sc->sensordev.xname));
  393 
  394         for (i = 0; i < THINKPAD_TEMP_SENSORS; i++) {
  395                 sc->sensors[i].type = SENSOR_TEMP;
  396                 sensor_attach(&sc->sensordev, &sc->sensors[i]);
  397         }
  398         
  399         sc->sensors[i].type = SENSOR_FANRPM;
  400         sensor_attach(&sc->sensordev, &sc->sensors[i]);
  401 
  402         sensordev_install(&sc->sensordev);
  403 
  404         return (0);
  405 }
  406 
  407 static int
  408 acpi_thinkpad_detach(device_t dev)
  409 {
  410         int i;
  411 
  412         ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
  413 
  414         struct acpi_thinkpad_softc *sc = device_get_softc(dev);
  415 
  416         /* Disable events and restore eventmask */
  417         lockmgr(&tplock, LK_EXCLUSIVE);
  418         acpi_thinkpad_sysctl_set(sc, ACPI_THINKPAD_METHOD_EVENTS, 0);
  419         acpi_thinkpad_sysctl_set(sc, ACPI_THINKPAD_METHOD_EVENTMASK,
  420             sc->events_initialmask);
  421         lockmgr(&tplock, LK_RELEASE);
  422 
  423         AcpiRemoveNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY,
  424             acpi_thinkpad_notify);
  425 
  426         if (sc->sysctl_tree != NULL)
  427                 sysctl_ctx_free(&sc->sysctl_ctx);
  428 
  429         sensordev_deinstall(&sc->sensordev);
  430         for (i = 0; i < THINKPAD_NUM_SENSORS; i++)
  431                 sensor_detach(&sc->sensordev, &sc->sensors[i]);
  432         sensor_task_unregister(sc);
  433 
  434         return (0);
  435 }
  436 
  437 static int
  438 acpi_thinkpad_eventmask_set(struct acpi_thinkpad_softc *sc, int val)
  439 {
  440         int i;
  441         ACPI_OBJECT             arg[2];
  442         ACPI_OBJECT_LIST        args;
  443         ACPI_STATUS             status;
  444 
  445         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
  446         KKASSERT(lockstatus(&tplock, curthread) != 0);
  447 
  448         args.Count = 2;
  449         args.Pointer = arg;
  450         arg[0].Type = ACPI_TYPE_INTEGER;
  451         arg[1].Type = ACPI_TYPE_INTEGER;
  452 
  453         for (i = 0; i < 32; ++i) {
  454                 arg[0].Integer.Value = i+1;
  455                 arg[1].Integer.Value = (((1 << i) & val) != 0);
  456                 status = AcpiEvaluateObject(sc->handle,
  457                     THINKPAD_NAME_EVENTS_MASK_SET, &args, NULL);
  458 
  459                 if (ACPI_FAILURE(status))
  460                         return (status);
  461         }
  462 
  463         return (0);
  464 }
  465 
  466 static int
  467 acpi_thinkpad_sysctl(SYSCTL_HANDLER_ARGS)
  468 {
  469         struct acpi_thinkpad_softc      *sc;
  470         int                     arg;
  471         int                     error = 0;
  472         int                     function;
  473         int                     method;
  474         
  475         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
  476 
  477         sc = (struct acpi_thinkpad_softc *)oidp->oid_arg1;
  478         function = oidp->oid_arg2;
  479         method = acpi_thinkpad_sysctls[function].method;
  480 
  481         lockmgr(&tplock, LK_EXCLUSIVE);
  482         arg = acpi_thinkpad_sysctl_get(sc, method);
  483         error = sysctl_handle_int(oidp, &arg, 0, req);
  484 
  485         /* Sanity check */
  486         if (error != 0 || req->newptr == NULL)
  487                 goto out;
  488 
  489         /* Update */
  490         error = acpi_thinkpad_sysctl_set(sc, method, arg);
  491 
  492 out:
  493         lockmgr(&tplock, LK_RELEASE);
  494         return (error);
  495 }
  496 
  497 static int
  498 acpi_thinkpad_sysctl_get(struct acpi_thinkpad_softc *sc, int method)
  499 {
  500         ACPI_INTEGER    val_ec;
  501         int             val = 0, key;
  502 
  503         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
  504         KKASSERT(lockstatus(&tplock, curthread) != 0);
  505 
  506         switch (method) {
  507         case ACPI_THINKPAD_METHOD_EVENTS:
  508                 acpi_GetInteger(sc->handle, THINKPAD_NAME_EVENTS_STATUS_GET,
  509                     &val);
  510                 break;
  511 
  512         case ACPI_THINKPAD_METHOD_EVENTMASK:
  513                 if (sc->events_mask_supported)
  514                         acpi_GetInteger(sc->handle,
  515                             THINKPAD_NAME_EVENTS_MASK_GET, &val);
  516                 break;
  517 
  518         case ACPI_THINKPAD_METHOD_HOTKEY:
  519                 /*
  520                  * Construct the hotkey as a bitmask as illustrated below.
  521                  * Note that whenever a key was pressed, the respecting bit
  522                  * toggles and nothing else changes.
  523                  * +--+--+-+-+-+-+-+-+-+-+-+-+
  524                  * |11|10|9|8|7|6|5|4|3|2|1|0|
  525                  * +--+--+-+-+-+-+-+-+-+-+-+-+
  526                  *   |  | | | | | | | | | | |
  527                  *   |  | | | | | | | | | | +- Home Button
  528                  *   |  | | | | | | | | | +--- Search Button
  529                  *   |  | | | | | | | | +----- Mail Button
  530                  *   |  | | | | | | | +------- Thinkpad Button
  531                  *   |  | | | | | | +--------- Zoom (Fn + Space)
  532                  *   |  | | | | | +----------- WLAN Button
  533                  *   |  | | | | +------------- Video Button
  534                  *   |  | | | +--------------- Hibernate Button
  535                  *   |  | | +----------------- Thinklight Button
  536                  *   |  | +------------------- Screen expand (Fn + F8)
  537                  *   |  +--------------------- Brightness
  538                  *   +------------------------ Volume/Mute
  539                  */
  540                 key = rtcin(THINKPAD_RTC_HOTKEY1);
  541                 val = (THINKPAD_RTC_MASK_HOME | THINKPAD_RTC_MASK_SEARCH |
  542                     THINKPAD_RTC_MASK_MAIL | THINKPAD_RTC_MASK_WLAN) & key;
  543                 key = rtcin(THINKPAD_RTC_HOTKEY2);
  544                 val |= (THINKPAD_RTC_MASK_THINKPAD | THINKPAD_RTC_MASK_VIDEO |
  545                     THINKPAD_RTC_MASK_HIBERNATE) & key;
  546                 val |= (THINKPAD_RTC_MASK_ZOOM & key) >> 1;
  547                 key = rtcin(THINKPAD_RTC_THINKLIGHT);
  548                 val |= (THINKPAD_RTC_MASK_THINKLIGHT & key) << 4;
  549                 key = rtcin(THINKPAD_RTC_SCREENEXPAND);
  550                 val |= (THINKPAD_RTC_MASK_THINKLIGHT & key) << 4;
  551                 key = rtcin(THINKPAD_RTC_BRIGHTNESS);
  552                 val |= (THINKPAD_RTC_MASK_BRIGHTNESS & key) << 5;
  553                 key = rtcin(THINKPAD_RTC_VOLUME);
  554                 val |= (THINKPAD_RTC_MASK_VOLUME & key) << 4;
  555                 break;
  556 
  557         case ACPI_THINKPAD_METHOD_BRIGHTNESS:
  558                 ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_BRIGHTNESS, &val_ec, 1);
  559                 val = val_ec & THINKPAD_EC_MASK_BRI;
  560                 break;
  561 
  562         case ACPI_THINKPAD_METHOD_VOLUME:
  563                 ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_VOLUME, &val_ec, 1);
  564                 val = val_ec & THINKPAD_EC_MASK_VOL;
  565                 break;
  566 
  567         case ACPI_THINKPAD_METHOD_MUTE:
  568                 ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_VOLUME, &val_ec, 1);
  569                 val = ((val_ec & THINKPAD_EC_MASK_MUTE) ==
  570                     THINKPAD_EC_MASK_MUTE);
  571                 break;
  572 
  573         case ACPI_THINKPAD_METHOD_THINKLIGHT:
  574                 if (sc->light_get_supported)
  575                         acpi_GetInteger(sc->ec_handle, THINKPAD_NAME_KEYLIGHT,
  576                             &val);
  577                 else
  578                         val = sc->light_val;
  579                 break;
  580 
  581         case ACPI_THINKPAD_METHOD_BLUETOOTH:
  582                 acpi_GetInteger(sc->handle, THINKPAD_NAME_WLAN_BT_GET, &val);
  583                 sc->wlan_bt_flags = val;
  584                 val = ((val & THINKPAD_NAME_MASK_BT) != 0);
  585                 break;
  586 
  587         case ACPI_THINKPAD_METHOD_WLAN:
  588                 acpi_GetInteger(sc->handle, THINKPAD_NAME_WLAN_BT_GET, &val);
  589                 sc->wlan_bt_flags = val;
  590                 val = ((val & THINKPAD_NAME_MASK_WLAN) != 0);
  591                 break;
  592 
  593         case ACPI_THINKPAD_METHOD_FANSPEED:
  594                 if (sc->fan_handle) {
  595                         if (ACPI_FAILURE(acpi_GetInteger(sc->fan_handle,
  596                             NULL, &val)))
  597                                 val = -1;
  598                 }
  599                 else {
  600                         ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_FANSPEED,
  601                             &val_ec, 2);
  602                         val = val_ec;
  603                 }
  604                 break;
  605 
  606         case ACPI_THINKPAD_METHOD_FANLEVEL:
  607                 /*
  608                  * The THINKPAD_EC_FANSTATUS register works as follows:
  609                  * Bit 0-5 indicate the level at which the fan operates. Only
  610                  *       values between 0 and 7 have an effect. Everything
  611                  *       above 7 is treated the same as level 7
  612                  * Bit 6 overrides the fan speed limit if set to 1
  613                  * Bit 7 indicates at which mode the fan operates:
  614                  *       manual (0) or automatic (1)
  615                  */
  616                 if (!sc->fan_handle) {
  617                         ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_FANSTATUS,
  618                             &val_ec, 1);
  619                         val = val_ec & THINKPAD_EC_MASK_FANLEVEL;
  620                 }
  621                 break;
  622 
  623         case ACPI_THINKPAD_METHOD_FANSTATUS:
  624                 if (!sc->fan_handle) {
  625                         ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_FANSTATUS,
  626                             &val_ec, 1);
  627                         val = (val_ec & THINKPAD_EC_MASK_FANSTATUS) ==
  628                             THINKPAD_EC_MASK_FANSTATUS;
  629                 }
  630                 else
  631                         val = -1;
  632                 break;
  633         }
  634 
  635         return (val);
  636 }
  637 
  638 static int
  639 acpi_thinkpad_sysctl_set(struct acpi_thinkpad_softc *sc, int method, int arg)
  640 {
  641         int                     val, step, i;
  642         ACPI_INTEGER            val_ec;
  643         ACPI_OBJECT             Arg;
  644         ACPI_OBJECT_LIST        Args;
  645         ACPI_STATUS             status;
  646 
  647         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
  648         KKASSERT(lockstatus(&tplock, curthread) != 0);
  649 
  650         switch (method) {
  651         case ACPI_THINKPAD_METHOD_EVENTS:
  652                 if (arg < 0 || arg > 1)
  653                         return (EINVAL);
  654 
  655                 status = acpi_SetInteger(sc->handle,
  656                     THINKPAD_NAME_EVENTS_STATUS_SET, arg);
  657                 if (ACPI_FAILURE(status))
  658                         return (status);
  659                 if (sc->events_mask_supported)
  660                         return acpi_thinkpad_eventmask_set(sc,
  661                             sc->events_availmask);
  662                 break;
  663 
  664         case ACPI_THINKPAD_METHOD_EVENTMASK:
  665                 if (sc->events_mask_supported)
  666                         return acpi_thinkpad_eventmask_set(sc, arg);
  667                 break;
  668 
  669         case ACPI_THINKPAD_METHOD_BRIGHTNESS:
  670                 if (arg < 0 || arg > 7)
  671                         return (EINVAL);
  672 
  673                 if (sc->cmos_handle) {
  674                         /* Read the current brightness */
  675                         status = ACPI_EC_READ(sc->ec_dev,
  676                             THINKPAD_EC_BRIGHTNESS, &val_ec, 1);
  677                         if (ACPI_FAILURE(status))
  678                                 return (status);
  679                         val = val_ec & THINKPAD_EC_MASK_BRI;
  680 
  681                         Args.Count = 1;
  682                         Args.Pointer = &Arg;
  683                         Arg.Type = ACPI_TYPE_INTEGER;
  684                         Arg.Integer.Value = (arg > val) ?
  685                             THINKPAD_CMOS_BRIGHTNESS_UP :
  686                             THINKPAD_CMOS_BRIGHTNESS_DOWN;
  687 
  688                         step = (arg > val) ? 1 : -1;
  689                         for (i = val; i != arg; i += step) {
  690                                 status = AcpiEvaluateObject(sc->cmos_handle,
  691                                     NULL, &Args, NULL);
  692                                 if (ACPI_FAILURE(status))
  693                                         break;
  694                         }
  695                 }
  696                 return ACPI_EC_WRITE(sc->ec_dev, THINKPAD_EC_BRIGHTNESS,
  697                     arg, 1);
  698                 break;
  699 
  700         case ACPI_THINKPAD_METHOD_VOLUME:
  701                 if (arg < 0 || arg > 14)
  702                         return (EINVAL);
  703 
  704                 status = ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_VOLUME,
  705                     &val_ec, 1);
  706                 if (ACPI_FAILURE(status))
  707                         return (status);
  708 
  709                 if (sc->cmos_handle) {
  710                         val = val_ec & THINKPAD_EC_MASK_VOL;
  711 
  712                         Args.Count = 1;
  713                         Args.Pointer = &Arg;
  714                         Arg.Type = ACPI_TYPE_INTEGER;
  715                         Arg.Integer.Value = (arg > val) ?
  716                             THINKPAD_CMOS_VOLUME_UP : THINKPAD_CMOS_VOLUME_DOWN;
  717 
  718                         step = (arg > val) ? 1 : -1;
  719                         for (i = val; i != arg; i += step) {
  720                                 status = AcpiEvaluateObject(sc->cmos_handle,
  721                                     NULL, &Args, NULL);
  722                                 if (ACPI_FAILURE(status))
  723                                         break;
  724                         }
  725                 }
  726                 return ACPI_EC_WRITE(sc->ec_dev, THINKPAD_EC_VOLUME, arg +
  727                     (val_ec & (~THINKPAD_EC_MASK_VOL)), 1);
  728                 break;
  729 
  730         case ACPI_THINKPAD_METHOD_MUTE:
  731                 if (arg < 0 || arg > 1)
  732                         return (EINVAL);
  733 
  734                 status = ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_VOLUME,
  735                     &val_ec, 1);
  736                 if (ACPI_FAILURE(status))
  737                         return (status);
  738 
  739                 if (sc->cmos_handle) {
  740                         val = val_ec & THINKPAD_EC_MASK_VOL;
  741 
  742                         Args.Count = 1;
  743                         Args.Pointer = &Arg;
  744                         Arg.Type = ACPI_TYPE_INTEGER;
  745                         Arg.Integer.Value = THINKPAD_CMOS_VOLUME_MUTE;
  746 
  747                         status = AcpiEvaluateObject(sc->cmos_handle, NULL,
  748                             &Args, NULL);
  749                         if (ACPI_FAILURE(status))
  750                                 break;
  751                 }
  752                 return ACPI_EC_WRITE(sc->ec_dev, THINKPAD_EC_VOLUME, (arg==1) ?
  753                    val_ec | THINKPAD_EC_MASK_MUTE :
  754                    val_ec & (~THINKPAD_EC_MASK_MUTE), 1);
  755                 break;
  756 
  757         case ACPI_THINKPAD_METHOD_THINKLIGHT:
  758                 if (arg < 0 || arg > 1)
  759                         return (EINVAL);
  760 
  761                 if (sc->light_set_supported) {
  762                         Args.Count = 1;
  763                         Args.Pointer = &Arg;
  764                         Arg.Type = ACPI_TYPE_INTEGER;
  765                         Arg.Integer.Value = arg ?
  766                             sc->light_cmd_on : sc->light_cmd_off;
  767 
  768                         status = AcpiEvaluateObject(sc->light_handle, NULL,
  769                             &Args, NULL);
  770                         if (ACPI_SUCCESS(status))
  771                                 sc->light_val = arg;
  772                         return (status);
  773                 }
  774                 break;
  775 
  776         case ACPI_THINKPAD_METHOD_BLUETOOTH:
  777                 if (arg < 0 || arg > 1)
  778                         return (EINVAL);
  779 
  780                 val = (arg == 1) ? sc->wlan_bt_flags |
  781                     THINKPAD_NAME_MASK_BT :
  782                     sc->wlan_bt_flags & (~THINKPAD_NAME_MASK_BT);
  783                 return acpi_SetInteger(sc->handle, THINKPAD_NAME_WLAN_BT_SET,
  784                     val);
  785                 break;
  786 
  787         case ACPI_THINKPAD_METHOD_FANLEVEL:
  788                 if (arg < 0 || arg > 7)
  789                         return (EINVAL);
  790 
  791                 if (!sc->fan_handle) {
  792                         /* Read the current fanstatus */
  793                         ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_FANSTATUS,
  794                             &val_ec, 1);
  795                         val = val_ec & (~THINKPAD_EC_MASK_FANLEVEL);
  796 
  797                         return ACPI_EC_WRITE(sc->ec_dev, THINKPAD_EC_FANSTATUS,
  798                             val | arg, 1);
  799                 }
  800                 break;
  801 
  802         case ACPI_THINKPAD_METHOD_FANSTATUS:
  803                 if (arg < 0 || arg > 1)
  804                         return (EINVAL);
  805 
  806                 if (!sc->fan_handle) {
  807                         /* Read the current fanstatus */
  808                         ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_FANSTATUS,
  809                             &val_ec, 1);
  810 
  811                         return ACPI_EC_WRITE(sc->ec_dev, THINKPAD_EC_FANSTATUS,
  812                             (arg == 1) ? (val_ec | THINKPAD_EC_MASK_FANSTATUS) :
  813                             (val_ec & (~THINKPAD_EC_MASK_FANSTATUS)), 1);
  814                 }
  815                 break;
  816         }
  817 
  818         return (0);
  819 }
  820 
  821 static int
  822 acpi_thinkpad_sysctl_init(struct acpi_thinkpad_softc *sc, int method)
  823 {
  824         int                     dummy;
  825         ACPI_OBJECT_TYPE        cmos_t;
  826         ACPI_HANDLE             ledb_handle;
  827 
  828         switch (method) {
  829         case ACPI_THINKPAD_METHOD_EVENTS:
  830                 /* Events are disabled by default */
  831                 return (TRUE);
  832 
  833         case ACPI_THINKPAD_METHOD_EVENTMASK:
  834                 return (sc->events_mask_supported);
  835 
  836         case ACPI_THINKPAD_METHOD_HOTKEY:
  837         case ACPI_THINKPAD_METHOD_BRIGHTNESS:
  838         case ACPI_THINKPAD_METHOD_VOLUME:
  839         case ACPI_THINKPAD_METHOD_MUTE:
  840                 /* EC is required here, which was already checked before */
  841                 return (TRUE);
  842 
  843         case ACPI_THINKPAD_METHOD_THINKLIGHT:
  844                 sc->cmos_handle = NULL;
  845                 sc->light_get_supported = ACPI_SUCCESS(acpi_GetInteger(
  846                     sc->ec_handle, THINKPAD_NAME_KEYLIGHT, &sc->light_val));
  847 
  848                 if ((ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\UCMS",
  849                     &sc->light_handle)) ||
  850                     ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\CMOS",
  851                     &sc->light_handle)) ||
  852                     ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\CMS",
  853                     &sc->light_handle))) &&
  854                     ACPI_SUCCESS(AcpiGetType(sc->light_handle, &cmos_t)) &&
  855                     cmos_t == ACPI_TYPE_METHOD) {
  856                         sc->light_cmd_on = 0x0c;
  857                         sc->light_cmd_off = 0x0d;
  858                         sc->cmos_handle = sc->light_handle;
  859                 }
  860                 else if (ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\LGHT",
  861                     &sc->light_handle))) {
  862                         sc->light_cmd_on = 1;
  863                         sc->light_cmd_off = 0;
  864                 }
  865                 else
  866                         sc->light_handle = NULL;
  867 
  868                 sc->light_set_supported = (sc->light_handle &&
  869                     ACPI_FAILURE(AcpiGetHandle(sc->ec_handle, "LEDB",
  870                     &ledb_handle)));
  871 
  872                 if (sc->light_get_supported)
  873                         return (TRUE);
  874 
  875                 if (sc->light_set_supported) {
  876                         sc->light_val = 0;
  877                         return (TRUE);
  878                 }
  879 
  880                 return (FALSE);
  881 
  882         case ACPI_THINKPAD_METHOD_BLUETOOTH:
  883         case ACPI_THINKPAD_METHOD_WLAN:
  884                 if (ACPI_SUCCESS(acpi_GetInteger(sc->handle,
  885                     THINKPAD_NAME_WLAN_BT_GET, &dummy)))
  886                         return (TRUE);
  887                 return (FALSE);
  888 
  889         case ACPI_THINKPAD_METHOD_FANSPEED:
  890                 /* 
  891                  * Some models report the fan speed in levels from 0-7
  892                  * Newer models report it contiguously
  893                  */
  894                 sc->fan_levels = (ACPI_SUCCESS(AcpiGetHandle(sc->handle, "GFAN",
  895                     &sc->fan_handle)) ||
  896                     ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\FSPD",
  897                     &sc->fan_handle)));
  898                 return (TRUE);
  899 
  900         case ACPI_THINKPAD_METHOD_FANLEVEL:
  901         case ACPI_THINKPAD_METHOD_FANSTATUS:
  902                 /* 
  903                  * Fan status is only supported on those models,
  904                  * which report fan RPM contiguously, not in levels
  905                  */
  906                 if (sc->fan_levels)
  907                         return (FALSE);
  908                 return (TRUE);
  909 
  910         case ACPI_THINKPAD_METHOD_THERMAL:
  911                 if (ACPI_SUCCESS(acpi_GetInteger(sc->ec_handle,
  912                     THINKPAD_NAME_THERMAL_GET, &dummy))) {
  913                         sc->thermal_updt_supported =
  914                             ACPI_SUCCESS(acpi_GetInteger(sc->ec_handle,
  915                             THINKPAD_NAME_THERMAL_UPDT, &dummy));
  916                         return (TRUE);
  917                 }
  918                 return (FALSE);
  919         }
  920         return (FALSE);
  921 }
  922 
  923 static void
  924 acpi_thinkpad_notify(ACPI_HANDLE h, UINT32 notify, void *context)
  925 {
  926         int             event, arg, type;
  927         device_t        dev = context;
  928         struct acpi_thinkpad_softc *sc = device_get_softc(dev);
  929 
  930         ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, notify);
  931 
  932         if (notify != 0x80)
  933                 device_printf(dev, "Unknown notify\n");
  934 
  935         for (;;) {
  936                 acpi_GetInteger(acpi_get_handle(dev), THINKPAD_NAME_EVENTS_GET,
  937                     &event);
  938 
  939                 if (event == 0)
  940                         break;
  941 
  942                 type = (event >> 12) & 0xf;
  943                 arg = event & 0xfff;
  944                 switch (type) {
  945                 case 1:
  946                         if (!(sc->events_availmask & (1 << (arg - 1)))) {
  947                                 device_printf(dev, "Unknown key %d\n", arg);
  948                                 break;
  949                         }
  950 
  951                         /* Notify devd(8) */
  952                         acpi_UserNotify("THINKPAD", h, (arg & 0xff));
  953                         break;
  954                 default:
  955                         break;
  956                 }
  957         }
  958 }
  959 
  960 static void
  961 acpi_thinkpad_refresh(void *arg)
  962 {
  963         struct acpi_thinkpad_softc *sc = (struct acpi_thinkpad_softc *)arg;
  964         int i, data;
  965 
  966         for (i = 0; i < THINKPAD_TEMP_SENSORS; i++) {
  967                 char temp_cmd[] = "TMP0";
  968 
  969                 temp_cmd[3] = '' + i;
  970                 /*
  971                  * The TMPx methods seem to return +/- 128 or 0
  972                  * when the respecting sensor is not available
  973                  */
  974                 if (ACPI_FAILURE(acpi_GetInteger(sc->ec_handle, temp_cmd,
  975                     &data)) || ABS(data) == 128 || data == 0) {
  976                         sc->sensors[i].flags |= SENSOR_FINVALID;
  977                         continue;
  978                 }
  979                 if (sc->thermal_updt_supported)
  980                         /* Temperature is reported in tenth of Kelvin */
  981                         sc->sensors[i].value = data * 100000 - 50000;
  982                 else
  983                         sc->sensors[i].value = data * 1000000 + 273150000;
  984                 sc->sensors[i].flags &= ~SENSOR_FINVALID;
  985         }
  986 
  987         if (sc->fan_handle) {
  988                 if (ACPI_FAILURE(acpi_GetInteger(sc->fan_handle,
  989                     NULL, &data)))
  990                         sc->sensors[i].flags |= SENSOR_FINVALID;
  991                 sc->sensors[i].value = data;
  992                 sc->sensors[i].flags &= ~SENSOR_FINVALID;
  993         } else {
  994                 ACPI_INTEGER speed;
  995 
  996                 ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_FANSPEED, &speed, 2);
  997                 sc->sensors[i].value = speed;
  998                 sc->sensors[i].flags &= ~SENSOR_FINVALID;
  999         }
 1000 }

Cache object: b5aeedb5b188ead09440e3b18f24ec9f


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