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  * Copyright (c) 2020 Ali Abdallah <ali.abdallah@suse.com>
    6  *
    7  * Redistribution and use in source and binary forms, with or without
    8  * modification, are permitted provided that the following conditions
    9  * are met:
   10  * 1. Redistributions of source code must retain the above copyright
   11  *    notice, this list of conditions and the following disclaimer.
   12  * 2. Redistributions in binary form must reproduce the above copyright
   13  *    notice, this list of conditions and the following disclaimer in the
   14  *    documentation and/or other materials provided with the distribution.
   15  *
   16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   26  * SUCH DAMAGE.
   27  */
   28 
   29 #include <sys/cdefs.h>
   30 __FBSDID("$FreeBSD$");
   31 
   32 /*
   33  * Driver for extra ACPI-controlled gadgets found on ThinkPad laptops.
   34  * Inspired by the ibm-acpi and tpb projects which implement these features
   35  * on Linux.
   36  *
   37  *   acpi-ibm: <http://ibm-acpi.sourceforge.net/>
   38  *        tpb: <http://www.nongnu.org/tpb/>
   39  */
   40 
   41 #include "opt_acpi.h"
   42 #include <sys/param.h>
   43 #include <sys/systm.h>
   44 #include <sys/kernel.h>
   45 #include <sys/bus.h>
   46 #include <machine/cpufunc.h>
   47 
   48 #include <contrib/dev/acpica/include/acpi.h>
   49 #include <contrib/dev/acpica/include/accommon.h>
   50 
   51 #include "acpi_if.h"
   52 #include <sys/module.h>
   53 #include <dev/acpica/acpivar.h>
   54 #include <dev/led/led.h>
   55 #include <sys/power.h>
   56 #include <sys/sbuf.h>
   57 #include <sys/sysctl.h>
   58 #include <isa/rtc.h>
   59 
   60 #define _COMPONENT      ACPI_OEM
   61 ACPI_MODULE_NAME("IBM")
   62 
   63 /* Internal methods */
   64 #define ACPI_IBM_METHOD_EVENTS          1
   65 #define ACPI_IBM_METHOD_EVENTMASK       2
   66 #define ACPI_IBM_METHOD_HOTKEY          3
   67 #define ACPI_IBM_METHOD_BRIGHTNESS      4
   68 #define ACPI_IBM_METHOD_VOLUME          5
   69 #define ACPI_IBM_METHOD_MUTE            6
   70 #define ACPI_IBM_METHOD_THINKLIGHT      7
   71 #define ACPI_IBM_METHOD_BLUETOOTH       8
   72 #define ACPI_IBM_METHOD_WLAN            9
   73 #define ACPI_IBM_METHOD_FANSPEED        10
   74 #define ACPI_IBM_METHOD_FANLEVEL        11
   75 #define ACPI_IBM_METHOD_FANSTATUS       12
   76 #define ACPI_IBM_METHOD_THERMAL         13
   77 #define ACPI_IBM_METHOD_HANDLEREVENTS   14
   78 #define ACPI_IBM_METHOD_MIC_LED         15
   79 #define ACPI_IBM_METHOD_PRIVACYGUARD    16
   80 
   81 /* Hotkeys/Buttons */
   82 #define IBM_RTC_HOTKEY1                 0x64
   83 #define   IBM_RTC_MASK_HOME             (1 << 0)
   84 #define   IBM_RTC_MASK_SEARCH           (1 << 1)
   85 #define   IBM_RTC_MASK_MAIL             (1 << 2)
   86 #define   IBM_RTC_MASK_WLAN             (1 << 5)
   87 #define IBM_RTC_HOTKEY2                 0x65
   88 #define   IBM_RTC_MASK_THINKPAD         (1 << 3)
   89 #define   IBM_RTC_MASK_ZOOM             (1 << 5)
   90 #define   IBM_RTC_MASK_VIDEO            (1 << 6)
   91 #define   IBM_RTC_MASK_HIBERNATE        (1 << 7)
   92 #define IBM_RTC_THINKLIGHT              0x66
   93 #define   IBM_RTC_MASK_THINKLIGHT       (1 << 4)
   94 #define IBM_RTC_SCREENEXPAND            0x67
   95 #define   IBM_RTC_MASK_SCREENEXPAND     (1 << 5)
   96 #define IBM_RTC_BRIGHTNESS              0x6c
   97 #define   IBM_RTC_MASK_BRIGHTNESS       (1 << 5)
   98 #define IBM_RTC_VOLUME                  0x6e
   99 #define   IBM_RTC_MASK_VOLUME           (1 << 7)
  100 
  101 /* Embedded Controller registers */
  102 #define IBM_EC_BRIGHTNESS               0x31
  103 #define   IBM_EC_MASK_BRI               0x7
  104 #define IBM_EC_VOLUME                   0x30
  105 #define   IBM_EC_MASK_VOL               0xf
  106 #define   IBM_EC_MASK_MUTE              (1 << 6)
  107 #define IBM_EC_FANSTATUS                0x2F
  108 #define   IBM_EC_MASK_FANLEVEL          0x3f
  109 #define   IBM_EC_MASK_FANUNTHROTTLED    (1 << 6)
  110 #define   IBM_EC_MASK_FANSTATUS         (1 << 7)
  111 #define IBM_EC_FANSPEED                 0x84
  112 
  113 /* CMOS Commands */
  114 #define IBM_CMOS_VOLUME_DOWN            0
  115 #define IBM_CMOS_VOLUME_UP              1
  116 #define IBM_CMOS_VOLUME_MUTE            2
  117 #define IBM_CMOS_BRIGHTNESS_UP          4
  118 #define IBM_CMOS_BRIGHTNESS_DOWN        5
  119 
  120 /* ACPI methods */
  121 #define IBM_NAME_KEYLIGHT               "KBLT"
  122 #define IBM_NAME_WLAN_BT_GET            "GBDC"
  123 #define IBM_NAME_WLAN_BT_SET            "SBDC"
  124 #define   IBM_NAME_MASK_BT              (1 << 1)
  125 #define   IBM_NAME_MASK_WLAN            (1 << 2)
  126 #define IBM_NAME_THERMAL_GET            "TMP7"
  127 #define IBM_NAME_THERMAL_UPDT           "UPDT"
  128 #define IBM_NAME_PRIVACYGUARD_GET       "GSSS"
  129 #define IBM_NAME_PRIVACYGUARD_SET       "SSSS"
  130 
  131 #define IBM_NAME_EVENTS_STATUS_GET      "DHKC"
  132 #define IBM_NAME_EVENTS_MASK_GET        "DHKN"
  133 #define IBM_NAME_EVENTS_STATUS_SET      "MHKC"
  134 #define IBM_NAME_EVENTS_MASK_SET        "MHKM"
  135 #define IBM_NAME_EVENTS_GET             "MHKP"
  136 #define IBM_NAME_EVENTS_AVAILMASK       "MHKA"
  137 
  138 /* Event Code */
  139 #define IBM_EVENT_LCD_BACKLIGHT         0x03
  140 #define IBM_EVENT_SUSPEND_TO_RAM        0x04
  141 #define IBM_EVENT_BLUETOOTH             0x05
  142 #define IBM_EVENT_SCREEN_EXPAND         0x07
  143 #define IBM_EVENT_SUSPEND_TO_DISK       0x0c
  144 #define IBM_EVENT_BRIGHTNESS_UP         0x10
  145 #define IBM_EVENT_BRIGHTNESS_DOWN       0x11
  146 #define IBM_EVENT_THINKLIGHT            0x12
  147 #define IBM_EVENT_ZOOM                  0x14
  148 #define IBM_EVENT_VOLUME_UP             0x15
  149 #define IBM_EVENT_VOLUME_DOWN           0x16
  150 #define IBM_EVENT_MUTE                  0x17
  151 #define IBM_EVENT_ACCESS_IBM_BUTTON     0x18
  152 
  153 /* Device-specific register flags */
  154 #define IBM_FLAG_PRIVACYGUARD_DEVICE_PRESENT    0x10000
  155 #define IBM_FLAG_PRIVACYGUARD_ON        0x1
  156 
  157 #define ABS(x) (((x) < 0)? -(x) : (x))
  158 
  159 struct acpi_ibm_softc {
  160         device_t        dev;
  161         ACPI_HANDLE     handle;
  162 
  163         /* Embedded controller */
  164         device_t        ec_dev;
  165         ACPI_HANDLE     ec_handle;
  166 
  167         /* CMOS */
  168         ACPI_HANDLE     cmos_handle;
  169 
  170         /* Fan status */
  171         ACPI_HANDLE     fan_handle;
  172         int             fan_levels;
  173 
  174         /* Keylight commands and states */
  175         ACPI_HANDLE     light_handle;
  176         int             light_cmd_on;
  177         int             light_cmd_off;
  178         int             light_val;
  179         int             light_get_supported;
  180         int             light_set_supported;
  181 
  182         /* led(4) interface */
  183         struct cdev     *led_dev;
  184         int             led_busy;
  185         int             led_state;
  186 
  187         /* Mic led handle */
  188         ACPI_HANDLE     mic_led_handle;
  189         int             mic_led_state;
  190 
  191         int             wlan_bt_flags;
  192         int             thermal_updt_supported;
  193 
  194         unsigned int    events_availmask;
  195         unsigned int    events_initialmask;
  196         int             events_mask_supported;
  197         int             events_enable;
  198 
  199         unsigned int    handler_events;
  200 
  201         struct sysctl_ctx_list  *sysctl_ctx;
  202         struct sysctl_oid       *sysctl_tree;
  203 };
  204 
  205 static struct {
  206         char    *name;
  207         int     method;
  208         char    *description;
  209         int     flag_rdonly;
  210 } acpi_ibm_sysctls[] = {
  211         {
  212                 .name           = "events",
  213                 .method         = ACPI_IBM_METHOD_EVENTS,
  214                 .description    = "ACPI events enable",
  215         },
  216         {
  217                 .name           = "eventmask",
  218                 .method         = ACPI_IBM_METHOD_EVENTMASK,
  219                 .description    = "ACPI eventmask",
  220         },
  221         {
  222                 .name           = "hotkey",
  223                 .method         = ACPI_IBM_METHOD_HOTKEY,
  224                 .description    = "Key Status",
  225                 .flag_rdonly    = 1
  226         },
  227         {
  228                 .name           = "lcd_brightness",
  229                 .method         = ACPI_IBM_METHOD_BRIGHTNESS,
  230                 .description    = "LCD Brightness",
  231         },
  232         {
  233                 .name           = "volume",
  234                 .method         = ACPI_IBM_METHOD_VOLUME,
  235                 .description    = "Volume",
  236         },
  237         {
  238                 .name           = "mute",
  239                 .method         = ACPI_IBM_METHOD_MUTE,
  240                 .description    = "Mute",
  241         },
  242         {
  243                 .name           = "thinklight",
  244                 .method         = ACPI_IBM_METHOD_THINKLIGHT,
  245                 .description    = "Thinklight enable",
  246         },
  247         {
  248                 .name           = "bluetooth",
  249                 .method         = ACPI_IBM_METHOD_BLUETOOTH,
  250                 .description    = "Bluetooth enable",
  251         },
  252         {
  253                 .name           = "wlan",
  254                 .method         = ACPI_IBM_METHOD_WLAN,
  255                 .description    = "WLAN enable",
  256                 .flag_rdonly    = 1
  257         },
  258         {
  259                 .name           = "fan_speed",
  260                 .method         = ACPI_IBM_METHOD_FANSPEED,
  261                 .description    = "Fan speed",
  262                 .flag_rdonly    = 1
  263         },
  264         {
  265                 .name           = "fan_level",
  266                 .method         = ACPI_IBM_METHOD_FANLEVEL,
  267                 .description    = "Fan level, 0-7 (recommended max), "
  268                                   "8 (unthrottled, full-speed)",
  269         },
  270         {
  271                 .name           = "fan",
  272                 .method         = ACPI_IBM_METHOD_FANSTATUS,
  273                 .description    = "Fan enable",
  274         },
  275         {
  276                 .name           = "mic_led",
  277                 .method         = ACPI_IBM_METHOD_MIC_LED,
  278                 .description    = "Mic led",
  279         },
  280         {
  281                 .name           = "privacyguard",
  282                 .method         = ACPI_IBM_METHOD_PRIVACYGUARD,
  283                 .description    = "PrivacyGuard enable",
  284         },
  285         { NULL, 0, NULL, 0 }
  286 };
  287 
  288 /*
  289  * Per-model default list of event mask.
  290  */
  291 #define ACPI_IBM_HKEY_RFKILL_MASK               (1 << 4)
  292 #define ACPI_IBM_HKEY_DSWITCH_MASK              (1 << 6)
  293 #define ACPI_IBM_HKEY_BRIGHTNESS_UP_MASK        (1 << 15)
  294 #define ACPI_IBM_HKEY_BRIGHTNESS_DOWN_MASK      (1 << 16)
  295 #define ACPI_IBM_HKEY_SEARCH_MASK               (1 << 18)
  296 #define ACPI_IBM_HKEY_MICMUTE_MASK              (1 << 26)
  297 #define ACPI_IBM_HKEY_SETTINGS_MASK             (1 << 28)
  298 #define ACPI_IBM_HKEY_VIEWOPEN_MASK             (1 << 30)
  299 #define ACPI_IBM_HKEY_VIEWALL_MASK              (1 << 31)
  300 
  301 struct acpi_ibm_models {
  302         const char *maker;
  303         const char *product;
  304         uint32_t eventmask;
  305 } acpi_ibm_models[] = {
  306         { "LENOVO", "20BSCTO1WW",
  307           ACPI_IBM_HKEY_RFKILL_MASK |
  308           ACPI_IBM_HKEY_DSWITCH_MASK |
  309           ACPI_IBM_HKEY_BRIGHTNESS_UP_MASK |
  310           ACPI_IBM_HKEY_BRIGHTNESS_DOWN_MASK |
  311           ACPI_IBM_HKEY_SEARCH_MASK |
  312           ACPI_IBM_HKEY_MICMUTE_MASK |
  313           ACPI_IBM_HKEY_SETTINGS_MASK |
  314           ACPI_IBM_HKEY_VIEWOPEN_MASK |
  315           ACPI_IBM_HKEY_VIEWALL_MASK
  316         }
  317 };
  318 
  319 ACPI_SERIAL_DECL(ibm, "ThinkPad ACPI Extras");
  320 
  321 static int      acpi_ibm_probe(device_t dev);
  322 static int      acpi_ibm_attach(device_t dev);
  323 static int      acpi_ibm_detach(device_t dev);
  324 static int      acpi_ibm_resume(device_t dev);
  325 
  326 static void     ibm_led(void *softc, int onoff);
  327 static void     ibm_led_task(struct acpi_ibm_softc *sc, int pending __unused);
  328 
  329 static int      acpi_ibm_sysctl(SYSCTL_HANDLER_ARGS);
  330 static int      acpi_ibm_sysctl_init(struct acpi_ibm_softc *sc, int method);
  331 static int      acpi_ibm_sysctl_get(struct acpi_ibm_softc *sc, int method);
  332 static int      acpi_ibm_sysctl_set(struct acpi_ibm_softc *sc, int method, int val);
  333 
  334 static int      acpi_ibm_eventmask_set(struct acpi_ibm_softc *sc, int val);
  335 static int      acpi_ibm_thermal_sysctl(SYSCTL_HANDLER_ARGS);
  336 static int      acpi_ibm_handlerevents_sysctl(SYSCTL_HANDLER_ARGS);
  337 static void     acpi_ibm_notify(ACPI_HANDLE h, UINT32 notify, void *context);
  338 
  339 static int      acpi_ibm_brightness_set(struct acpi_ibm_softc *sc, int arg);
  340 static int      acpi_ibm_bluetooth_set(struct acpi_ibm_softc *sc, int arg);
  341 static int      acpi_ibm_thinklight_set(struct acpi_ibm_softc *sc, int arg);
  342 static int      acpi_ibm_volume_set(struct acpi_ibm_softc *sc, int arg);
  343 static int      acpi_ibm_mute_set(struct acpi_ibm_softc *sc, int arg);
  344 static int      acpi_ibm_privacyguard_get(struct acpi_ibm_softc *sc);
  345 static ACPI_STATUS      acpi_ibm_privacyguard_set(struct acpi_ibm_softc *sc, int arg);
  346 static ACPI_STATUS      acpi_ibm_privacyguard_acpi_call(struct acpi_ibm_softc *sc, bool write, int *arg);
  347 
  348 static int      acpi_status_to_errno(ACPI_STATUS status);
  349 
  350 static device_method_t acpi_ibm_methods[] = {
  351         /* Device interface */
  352         DEVMETHOD(device_probe, acpi_ibm_probe),
  353         DEVMETHOD(device_attach, acpi_ibm_attach),
  354         DEVMETHOD(device_detach, acpi_ibm_detach),
  355         DEVMETHOD(device_resume, acpi_ibm_resume),
  356 
  357         DEVMETHOD_END
  358 };
  359 
  360 static driver_t acpi_ibm_driver = {
  361         "acpi_ibm",
  362         acpi_ibm_methods,
  363         sizeof(struct acpi_ibm_softc),
  364 };
  365 
  366 DRIVER_MODULE(acpi_ibm, acpi, acpi_ibm_driver, 0, 0);
  367 MODULE_DEPEND(acpi_ibm, acpi, 1, 1, 1);
  368 static char    *ibm_ids[] = {"IBM0068", "LEN0068", "LEN0268", NULL};
  369 
  370 static int
  371 acpi_status_to_errno(ACPI_STATUS status)
  372 {
  373         switch (status) {
  374         case AE_OK:
  375                 return (0);
  376         case AE_BAD_PARAMETER:
  377                 return (EINVAL);
  378         default:
  379                 return (ENODEV);
  380         }
  381 }
  382 
  383 static void
  384 ibm_led(void *softc, int onoff)
  385 {
  386         struct acpi_ibm_softc *sc = softc;
  387 
  388         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
  389 
  390         if (sc->led_busy)
  391                 return;
  392 
  393         sc->led_busy = 1;
  394         sc->led_state = onoff;
  395 
  396         AcpiOsExecute(OSL_NOTIFY_HANDLER, (void *)ibm_led_task, sc);
  397 }
  398 
  399 static void
  400 ibm_led_task(struct acpi_ibm_softc *sc, int pending __unused)
  401 {
  402         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
  403 
  404         ACPI_SERIAL_BEGIN(ibm);
  405         acpi_ibm_sysctl_set(sc, ACPI_IBM_METHOD_THINKLIGHT, sc->led_state);
  406         ACPI_SERIAL_END(ibm);
  407 
  408         sc->led_busy = 0;
  409 }
  410 
  411 static int
  412 acpi_ibm_mic_led_set(struct acpi_ibm_softc *sc, int arg)
  413 {
  414         ACPI_OBJECT_LIST input;
  415         ACPI_OBJECT params[1];
  416         ACPI_STATUS status;
  417 
  418         if (arg < 0 || arg > 1)
  419                 return (EINVAL);
  420 
  421         if (sc->mic_led_handle) {
  422                 params[0].Type = ACPI_TYPE_INTEGER;
  423                 params[0].Integer.Value = 0;
  424                 /* mic led: 0 off, 2 on */
  425                 if (arg == 1)
  426                         params[0].Integer.Value = 2;
  427 
  428                 input.Pointer = params;
  429                 input.Count = 1;
  430 
  431                 status = AcpiEvaluateObject(sc->handle, "MMTS", &input, NULL);
  432                 if (ACPI_SUCCESS(status))
  433                         sc->mic_led_state = arg;
  434                 return (status);
  435         }
  436 
  437         return (0);
  438 }
  439 
  440 static int
  441 acpi_ibm_probe(device_t dev)
  442 {
  443         int rv;
  444 
  445         if (acpi_disabled("ibm") || device_get_unit(dev) != 0)
  446                 return (ENXIO);
  447         rv = ACPI_ID_PROBE(device_get_parent(dev), dev, ibm_ids, NULL);
  448 
  449         if (rv <= 0)
  450                 device_set_desc(dev, "ThinkPad ACPI Extras");
  451 
  452         return (rv);
  453 }
  454 
  455 static int
  456 acpi_ibm_attach(device_t dev)
  457 {
  458         int i;
  459         int hkey;
  460         struct acpi_ibm_softc *sc;
  461         char *maker, *product;
  462         ACPI_OBJECT_LIST input;
  463         ACPI_OBJECT params[1];
  464         ACPI_OBJECT out_obj;
  465         ACPI_BUFFER result;
  466         devclass_t ec_devclass;
  467 
  468         ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
  469 
  470         sc = device_get_softc(dev);
  471         sc->dev = dev;
  472         sc->handle = acpi_get_handle(dev);
  473 
  474         /* Look for the first embedded controller */
  475         if (!(ec_devclass = devclass_find ("acpi_ec"))) {
  476                 if (bootverbose)
  477                         device_printf(dev, "Couldn't find acpi_ec devclass\n");
  478                 return (EINVAL);
  479         }
  480         if (!(sc->ec_dev = devclass_get_device(ec_devclass, 0))) {
  481                 if (bootverbose)
  482                         device_printf(dev, "Couldn't find acpi_ec device\n");
  483                 return (EINVAL);
  484         }
  485         sc->ec_handle = acpi_get_handle(sc->ec_dev);
  486 
  487         /* Get the sysctl tree */
  488         sc->sysctl_ctx = device_get_sysctl_ctx(dev);
  489         sc->sysctl_tree = device_get_sysctl_tree(dev);
  490 
  491         /* Look for event mask and hook up the nodes */
  492         sc->events_mask_supported = ACPI_SUCCESS(acpi_GetInteger(sc->handle,
  493             IBM_NAME_EVENTS_MASK_GET, &sc->events_initialmask));
  494 
  495         if (sc->events_mask_supported) {
  496                 SYSCTL_ADD_UINT(sc->sysctl_ctx,
  497                     SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "initialmask",
  498                     CTLFLAG_RD, &sc->events_initialmask, 0,
  499                     "Initial eventmask");
  500 
  501                 if (ACPI_SUCCESS (acpi_GetInteger(sc->handle, "MHKV", &hkey))) {
  502                         device_printf(dev, "Firmware version is 0x%X\n", hkey);
  503                         switch (hkey >> 8) {
  504                         case 1:
  505                                 /* The availmask is the bitmask of supported events */
  506                                 if (ACPI_FAILURE(acpi_GetInteger(sc->handle,
  507                                     IBM_NAME_EVENTS_AVAILMASK, &sc->events_availmask)))
  508                                         sc->events_availmask = 0xffffffff;
  509                                 break;
  510 
  511                         case 2:
  512                                 result.Length = sizeof(out_obj);
  513                                 result.Pointer = &out_obj;
  514                                 params[0].Type = ACPI_TYPE_INTEGER;
  515                                 params[0].Integer.Value = 1;
  516                                 input.Pointer = params;
  517                                 input.Count = 1;
  518 
  519                                 sc->events_availmask = 0xffffffff;
  520 
  521                                 if (ACPI_SUCCESS(AcpiEvaluateObject (sc->handle,
  522                                     IBM_NAME_EVENTS_AVAILMASK, &input, &result)))
  523                                         sc->events_availmask = out_obj.Integer.Value;
  524                                 break;
  525                         default:
  526                                 device_printf(dev, "Unknown firmware version 0x%x\n", hkey);
  527                                 break;
  528                         }
  529                 } else
  530                         sc->events_availmask = 0xffffffff;
  531 
  532                 SYSCTL_ADD_UINT(sc->sysctl_ctx,
  533                                 SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
  534                                 "availmask", CTLFLAG_RD,
  535                                 &sc->events_availmask, 0, "Mask of supported events");
  536         }
  537 
  538         /* Hook up proc nodes */
  539         for (int i = 0; acpi_ibm_sysctls[i].name != NULL; i++) {
  540                 if (!acpi_ibm_sysctl_init(sc, acpi_ibm_sysctls[i].method))
  541                         continue;
  542 
  543                 if (acpi_ibm_sysctls[i].flag_rdonly != 0) {
  544                         SYSCTL_ADD_PROC(sc->sysctl_ctx,
  545                             SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
  546                             acpi_ibm_sysctls[i].name,
  547                             CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
  548                             sc, i, acpi_ibm_sysctl, "I",
  549                             acpi_ibm_sysctls[i].description);
  550                 } else {
  551                         SYSCTL_ADD_PROC(sc->sysctl_ctx,
  552                             SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
  553                             acpi_ibm_sysctls[i].name,
  554                             CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
  555                             sc, i, acpi_ibm_sysctl, "I",
  556                             acpi_ibm_sysctls[i].description);
  557                 }
  558         }
  559 
  560         /* Hook up thermal node */
  561         if (acpi_ibm_sysctl_init(sc, ACPI_IBM_METHOD_THERMAL)) {
  562                 SYSCTL_ADD_PROC(sc->sysctl_ctx,
  563                     SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "thermal",
  564                     CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
  565                     acpi_ibm_thermal_sysctl, "I", "Thermal zones");
  566         }
  567 
  568         /* Hook up handlerevents node */
  569         if (acpi_ibm_sysctl_init(sc, ACPI_IBM_METHOD_HANDLEREVENTS)) {
  570                 SYSCTL_ADD_PROC(sc->sysctl_ctx,
  571                     SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "handlerevents",
  572                     CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
  573                     acpi_ibm_handlerevents_sysctl, "I",
  574                     "devd(8) events handled by acpi_ibm");
  575         }
  576 
  577         /* Handle notifies */
  578         AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY,
  579             acpi_ibm_notify, dev);
  580 
  581         /* Hook up light to led(4) */
  582         if (sc->light_set_supported)
  583                 sc->led_dev = led_create_state(ibm_led, sc, "thinklight",
  584                     (sc->light_val ? 1 : 0));
  585 
  586         /* Enable per-model events. */
  587         maker = kern_getenv("smbios.system.maker");
  588         product = kern_getenv("smbios.system.product");
  589         if (maker == NULL || product == NULL)
  590                 goto nosmbios;
  591 
  592         for (i = 0; i < nitems(acpi_ibm_models); i++) {
  593                 if (strcmp(maker, acpi_ibm_models[i].maker) == 0 &&
  594                     strcmp(product, acpi_ibm_models[i].product) == 0) {
  595                         ACPI_SERIAL_BEGIN(ibm);
  596                         acpi_ibm_sysctl_set(sc, ACPI_IBM_METHOD_EVENTMASK,
  597                             acpi_ibm_models[i].eventmask);
  598                         ACPI_SERIAL_END(ibm);
  599                 }
  600         }
  601 
  602 nosmbios:
  603         freeenv(maker);
  604         freeenv(product);
  605 
  606         /* Enable events by default. */
  607         ACPI_SERIAL_BEGIN(ibm);
  608         acpi_ibm_sysctl_set(sc, ACPI_IBM_METHOD_EVENTS, 1);
  609         ACPI_SERIAL_END(ibm);
  610 
  611         return (0);
  612 }
  613 
  614 static int
  615 acpi_ibm_detach(device_t dev)
  616 {
  617         ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
  618 
  619         struct acpi_ibm_softc *sc = device_get_softc(dev);
  620 
  621         /* Disable events and restore eventmask */
  622         ACPI_SERIAL_BEGIN(ibm);
  623         acpi_ibm_sysctl_set(sc, ACPI_IBM_METHOD_EVENTS, 0);
  624         acpi_ibm_sysctl_set(sc, ACPI_IBM_METHOD_EVENTMASK, sc->events_initialmask);
  625         ACPI_SERIAL_END(ibm);
  626 
  627         AcpiRemoveNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, acpi_ibm_notify);
  628 
  629         if (sc->led_dev != NULL)
  630                 led_destroy(sc->led_dev);
  631 
  632         return (0);
  633 }
  634 
  635 static int
  636 acpi_ibm_resume(device_t dev)
  637 {
  638         struct acpi_ibm_softc *sc = device_get_softc(dev);
  639 
  640         ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
  641 
  642         ACPI_SERIAL_BEGIN(ibm);
  643         for (int i = 0; acpi_ibm_sysctls[i].name != NULL; i++) {
  644                 int val;
  645 
  646                 val = acpi_ibm_sysctl_get(sc, i);
  647 
  648                 if (acpi_ibm_sysctls[i].flag_rdonly != 0)
  649                         continue;
  650 
  651                 acpi_ibm_sysctl_set(sc, i, val);
  652         }
  653         ACPI_SERIAL_END(ibm);
  654 
  655         /* The mic led does not turn back on when sysctl_set is called in the above loop */
  656         acpi_ibm_mic_led_set(sc, sc->mic_led_state);
  657 
  658         return (0);
  659 }
  660 
  661 static int
  662 acpi_ibm_eventmask_set(struct acpi_ibm_softc *sc, int val)
  663 {
  664         ACPI_OBJECT             arg[2];
  665         ACPI_OBJECT_LIST        args;
  666         ACPI_STATUS             status;
  667 
  668         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
  669         ACPI_SERIAL_ASSERT(ibm);
  670 
  671         args.Count = 2;
  672         args.Pointer = arg;
  673         arg[0].Type = ACPI_TYPE_INTEGER;
  674         arg[1].Type = ACPI_TYPE_INTEGER;
  675 
  676         for (int i = 0; i < 32; ++i) {
  677                 arg[0].Integer.Value = i + 1;
  678                 arg[1].Integer.Value = (((1 << i) & val) != 0);
  679                 status = AcpiEvaluateObject(sc->handle,
  680                     IBM_NAME_EVENTS_MASK_SET, &args, NULL);
  681 
  682                 if (ACPI_FAILURE(status))
  683                         return (status);
  684         }
  685 
  686         return (0);
  687 }
  688 
  689 static int
  690 acpi_ibm_sysctl(SYSCTL_HANDLER_ARGS)
  691 {
  692         struct acpi_ibm_softc   *sc;
  693         int                     arg;
  694         int                     error = 0;
  695         int                     function;
  696         int                     method;
  697 
  698         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
  699 
  700         sc = (struct acpi_ibm_softc *)oidp->oid_arg1;
  701         function = oidp->oid_arg2;
  702         method = acpi_ibm_sysctls[function].method;
  703 
  704         ACPI_SERIAL_BEGIN(ibm);
  705         arg = acpi_ibm_sysctl_get(sc, method);
  706         error = sysctl_handle_int(oidp, &arg, 0, req);
  707 
  708         /* Sanity check */
  709         if (error != 0 || req->newptr == NULL)
  710                 goto out;
  711 
  712         /* Update */
  713         error = acpi_ibm_sysctl_set(sc, method, arg);
  714 
  715 out:
  716         ACPI_SERIAL_END(ibm);
  717         return (error);
  718 }
  719 
  720 static int
  721 acpi_ibm_sysctl_get(struct acpi_ibm_softc *sc, int method)
  722 {
  723         UINT64          val_ec;
  724         int             val = 0, key;
  725 
  726         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
  727         ACPI_SERIAL_ASSERT(ibm);
  728 
  729         switch (method) {
  730         case ACPI_IBM_METHOD_EVENTS:
  731                 acpi_GetInteger(sc->handle, IBM_NAME_EVENTS_STATUS_GET, &val);
  732                 break;
  733 
  734         case ACPI_IBM_METHOD_EVENTMASK:
  735                 if (sc->events_mask_supported)
  736                         acpi_GetInteger(sc->handle, IBM_NAME_EVENTS_MASK_GET, &val);
  737                 break;
  738 
  739         case ACPI_IBM_METHOD_HOTKEY:
  740                 /*
  741                  * Construct the hotkey as a bitmask as illustrated below.
  742                  * Note that whenever a key was pressed, the respecting bit
  743                  * toggles and nothing else changes.
  744                  * +--+--+-+-+-+-+-+-+-+-+-+-+
  745                  * |11|10|9|8|7|6|5|4|3|2|1|0|
  746                  * +--+--+-+-+-+-+-+-+-+-+-+-+
  747                  *   |  | | | | | | | | | | |
  748                  *   |  | | | | | | | | | | +- Home Button
  749                  *   |  | | | | | | | | | +--- Search Button
  750                  *   |  | | | | | | | | +----- Mail Button
  751                  *   |  | | | | | | | +------- Thinkpad Button
  752                  *   |  | | | | | | +--------- Zoom (Fn + Space)
  753                  *   |  | | | | | +----------- WLAN Button
  754                  *   |  | | | | +------------- Video Button
  755                  *   |  | | | +--------------- Hibernate Button
  756                  *   |  | | +----------------- Thinklight Button
  757                  *   |  | +------------------- Screen expand (Fn + F8)
  758                  *   |  +--------------------- Brightness
  759                  *   +------------------------ Volume/Mute
  760                  */
  761                 key = rtcin(IBM_RTC_HOTKEY1);
  762                 val = (IBM_RTC_MASK_HOME | IBM_RTC_MASK_SEARCH | IBM_RTC_MASK_MAIL | IBM_RTC_MASK_WLAN) & key;
  763                 key = rtcin(IBM_RTC_HOTKEY2);
  764                 val |= (IBM_RTC_MASK_THINKPAD | IBM_RTC_MASK_VIDEO | IBM_RTC_MASK_HIBERNATE) & key;
  765                 val |= (IBM_RTC_MASK_ZOOM & key) >> 1;
  766                 key = rtcin(IBM_RTC_THINKLIGHT);
  767                 val |= (IBM_RTC_MASK_THINKLIGHT & key) << 4;
  768                 key = rtcin(IBM_RTC_SCREENEXPAND);
  769                 val |= (IBM_RTC_MASK_THINKLIGHT & key) << 4;
  770                 key = rtcin(IBM_RTC_BRIGHTNESS);
  771                 val |= (IBM_RTC_MASK_BRIGHTNESS & key) << 5;
  772                 key = rtcin(IBM_RTC_VOLUME);
  773                 val |= (IBM_RTC_MASK_VOLUME & key) << 4;
  774                 break;
  775 
  776         case ACPI_IBM_METHOD_BRIGHTNESS:
  777                 ACPI_EC_READ(sc->ec_dev, IBM_EC_BRIGHTNESS, &val_ec, 1);
  778                 val = val_ec & IBM_EC_MASK_BRI;
  779                 break;
  780 
  781         case ACPI_IBM_METHOD_VOLUME:
  782                 ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1);
  783                 val = val_ec & IBM_EC_MASK_VOL;
  784                 break;
  785 
  786         case ACPI_IBM_METHOD_MUTE:
  787                 ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1);
  788                 val = ((val_ec & IBM_EC_MASK_MUTE) == IBM_EC_MASK_MUTE);
  789                 break;
  790 
  791         case ACPI_IBM_METHOD_THINKLIGHT:
  792                 if (sc->light_get_supported)
  793                         acpi_GetInteger(sc->ec_handle, IBM_NAME_KEYLIGHT, &val);
  794                 else
  795                         val = sc->light_val;
  796                 break;
  797 
  798         case ACPI_IBM_METHOD_BLUETOOTH:
  799                 acpi_GetInteger(sc->handle, IBM_NAME_WLAN_BT_GET, &val);
  800                 sc->wlan_bt_flags = val;
  801                 val = ((val & IBM_NAME_MASK_BT) != 0);
  802                 break;
  803 
  804         case ACPI_IBM_METHOD_WLAN:
  805                 acpi_GetInteger(sc->handle, IBM_NAME_WLAN_BT_GET, &val);
  806                 sc->wlan_bt_flags = val;
  807                 val = ((val & IBM_NAME_MASK_WLAN) != 0);
  808                 break;
  809 
  810         case ACPI_IBM_METHOD_FANSPEED:
  811                 if (sc->fan_handle) {
  812                         if(ACPI_FAILURE(acpi_GetInteger(sc->fan_handle, NULL, &val)))
  813                                 val = -1;
  814                 } else {
  815                         ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSPEED, &val_ec, 2);
  816                         val = val_ec;
  817                 }
  818                 break;
  819 
  820         case ACPI_IBM_METHOD_FANLEVEL:
  821                 /*
  822                  * The IBM_EC_FANSTATUS register works as follows:
  823                  * Bit 0-5 indicate the level at which the fan operates. Only
  824                  *       values between 0 and 7 have an effect. Everything
  825                  *       above 7 is treated the same as level 7
  826                  * Bit 6 overrides the fan speed limit if set to 1
  827                  * Bit 7 indicates at which mode the fan operates:
  828                  *       manual (0) or automatic (1)
  829                  */
  830                 if (!sc->fan_handle) {
  831                         ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1);
  832                         if (val_ec & IBM_EC_MASK_FANUNTHROTTLED)
  833                                 val = 8;
  834                         else
  835                                 val = val_ec & IBM_EC_MASK_FANLEVEL;
  836                 }
  837                 break;
  838 
  839         case ACPI_IBM_METHOD_FANSTATUS:
  840                 if (!sc->fan_handle) {
  841                         ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1);
  842                         val = (val_ec & IBM_EC_MASK_FANSTATUS) == IBM_EC_MASK_FANSTATUS;
  843                 } else
  844                         val = -1;
  845                 break;
  846 
  847         case ACPI_IBM_METHOD_MIC_LED:
  848                 if (sc->mic_led_handle)
  849                         return sc->mic_led_state;
  850                 else
  851                         val = -1;
  852                 break;
  853 
  854         case ACPI_IBM_METHOD_PRIVACYGUARD:
  855                 val = acpi_ibm_privacyguard_get(sc);
  856                 break;
  857         }
  858 
  859         return (val);
  860 }
  861 
  862 static int
  863 acpi_ibm_sysctl_set(struct acpi_ibm_softc *sc, int method, int arg)
  864 {
  865         int                     val;
  866         UINT64                  val_ec;
  867         ACPI_STATUS             status;
  868 
  869         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
  870         ACPI_SERIAL_ASSERT(ibm);
  871 
  872         switch (method) {
  873         case ACPI_IBM_METHOD_EVENTS:
  874                 if (arg < 0 || arg > 1)
  875                         return (EINVAL);
  876 
  877                 status = acpi_SetInteger(sc->handle, IBM_NAME_EVENTS_STATUS_SET, arg);
  878                 if (ACPI_FAILURE(status))
  879                         return (status);
  880                 if (sc->events_mask_supported)
  881                         return acpi_ibm_eventmask_set(sc, sc->events_availmask);
  882                 break;
  883 
  884         case ACPI_IBM_METHOD_EVENTMASK:
  885                 if (sc->events_mask_supported)
  886                         return acpi_ibm_eventmask_set(sc, arg);
  887                 break;
  888 
  889         case ACPI_IBM_METHOD_BRIGHTNESS:
  890                 return acpi_ibm_brightness_set(sc, arg);
  891                 break;
  892 
  893         case ACPI_IBM_METHOD_VOLUME:
  894                 return acpi_ibm_volume_set(sc, arg);
  895                 break;
  896 
  897         case ACPI_IBM_METHOD_MUTE:
  898                 return acpi_ibm_mute_set(sc, arg);
  899                 break;
  900 
  901         case ACPI_IBM_METHOD_MIC_LED:
  902                 return acpi_ibm_mic_led_set(sc, arg);
  903                 break;
  904 
  905         case ACPI_IBM_METHOD_THINKLIGHT:
  906                 return acpi_ibm_thinklight_set(sc, arg);
  907                 break;
  908 
  909         case ACPI_IBM_METHOD_BLUETOOTH:
  910                 return acpi_ibm_bluetooth_set(sc, arg);
  911                 break;
  912 
  913         case ACPI_IBM_METHOD_PRIVACYGUARD:
  914                 return (acpi_status_to_errno(acpi_ibm_privacyguard_set(sc, arg)));
  915                 break;
  916 
  917         case ACPI_IBM_METHOD_FANLEVEL:
  918                 if (arg < 0 || arg > 8)
  919                         return (EINVAL);
  920 
  921                 if (!sc->fan_handle) {
  922                         /* Read the current fan status. */
  923                         ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1);
  924                         val = val_ec & ~(IBM_EC_MASK_FANLEVEL |
  925                             IBM_EC_MASK_FANUNTHROTTLED);
  926 
  927                         if (arg == 8)
  928                                 /* Full speed, set the unthrottled bit. */
  929                                 val |= 7 | IBM_EC_MASK_FANUNTHROTTLED;
  930                         else
  931                                 val |= arg;
  932 
  933                         return (ACPI_EC_WRITE(sc->ec_dev, IBM_EC_FANSTATUS, val,
  934                             1));
  935                 }
  936                 break;
  937 
  938         case ACPI_IBM_METHOD_FANSTATUS:
  939                 if (arg < 0 || arg > 1)
  940                         return (EINVAL);
  941 
  942                 if (!sc->fan_handle) {
  943                         /* Read the current fanstatus */
  944                         ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1);
  945 
  946                         return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_FANSTATUS,
  947                                 (arg == 1) ? (val_ec | IBM_EC_MASK_FANSTATUS) : (val_ec & (~IBM_EC_MASK_FANSTATUS)), 1);
  948                 }
  949                 break;
  950         }
  951 
  952         return (0);
  953 }
  954 
  955 static int
  956 acpi_ibm_sysctl_init(struct acpi_ibm_softc *sc, int method)
  957 {
  958         int                     dummy;
  959         ACPI_OBJECT_TYPE        cmos_t;
  960         ACPI_HANDLE             ledb_handle;
  961 
  962         switch (method) {
  963         case ACPI_IBM_METHOD_EVENTS:
  964                 return (TRUE);
  965 
  966         case ACPI_IBM_METHOD_EVENTMASK:
  967                 return (sc->events_mask_supported);
  968 
  969         case ACPI_IBM_METHOD_HOTKEY:
  970         case ACPI_IBM_METHOD_BRIGHTNESS:
  971         case ACPI_IBM_METHOD_VOLUME:
  972         case ACPI_IBM_METHOD_MUTE:
  973                 /* EC is required here, which was already checked before */
  974                 return (TRUE);
  975 
  976         case ACPI_IBM_METHOD_MIC_LED:
  977                 if (ACPI_SUCCESS(AcpiGetHandle(sc->handle, "MMTS", &sc->mic_led_handle)))
  978                 {
  979                         /* Turn off mic led by default */
  980                         acpi_ibm_mic_led_set(sc, 0);
  981                         return (TRUE);
  982                 } else
  983                         sc->mic_led_handle = NULL;
  984                 return (FALSE);
  985 
  986         case ACPI_IBM_METHOD_THINKLIGHT:
  987                 sc->cmos_handle = NULL;
  988                 sc->light_get_supported = ACPI_SUCCESS(acpi_GetInteger(
  989                     sc->ec_handle, IBM_NAME_KEYLIGHT, &sc->light_val));
  990 
  991                 if ((ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\UCMS", &sc->light_handle)) ||
  992                      ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\CMOS", &sc->light_handle)) ||
  993                      ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\CMS", &sc->light_handle))) &&
  994                      ACPI_SUCCESS(AcpiGetType(sc->light_handle, &cmos_t)) &&
  995                      cmos_t == ACPI_TYPE_METHOD) {
  996                         sc->light_cmd_on = 0x0c;
  997                         sc->light_cmd_off = 0x0d;
  998                         sc->cmos_handle = sc->light_handle;
  999                 }
 1000                 else if (ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\LGHT", &sc->light_handle))) {
 1001                         sc->light_cmd_on = 1;
 1002                         sc->light_cmd_off = 0;
 1003                 } else
 1004                         sc->light_handle = NULL;
 1005 
 1006                 sc->light_set_supported = (sc->light_handle &&
 1007                     ACPI_FAILURE(AcpiGetHandle(sc->ec_handle, "LEDB", &ledb_handle)));
 1008 
 1009                 if (sc->light_get_supported)
 1010                         return (TRUE);
 1011 
 1012                 if (sc->light_set_supported) {
 1013                         sc->light_val = 0;
 1014                         return (TRUE);
 1015                 }
 1016 
 1017                 return (FALSE);
 1018 
 1019         case ACPI_IBM_METHOD_BLUETOOTH:
 1020         case ACPI_IBM_METHOD_WLAN:
 1021                 if (ACPI_SUCCESS(acpi_GetInteger(sc->handle, IBM_NAME_WLAN_BT_GET, &dummy)))
 1022                         return (TRUE);
 1023                 return (FALSE);
 1024 
 1025         case ACPI_IBM_METHOD_FANSPEED:
 1026                 /*
 1027                  * Some models report the fan speed in levels from 0-7
 1028                  * Newer models report it contiguously
 1029                  */
 1030                 sc->fan_levels =
 1031                     (ACPI_SUCCESS(AcpiGetHandle(sc->handle, "GFAN", &sc->fan_handle)) ||
 1032                      ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\FSPD", &sc->fan_handle)));
 1033                 return (TRUE);
 1034 
 1035         case ACPI_IBM_METHOD_FANLEVEL:
 1036         case ACPI_IBM_METHOD_FANSTATUS:
 1037                 /*
 1038                  * Fan status is only supported on those models,
 1039                  * which report fan RPM contiguously, not in levels
 1040                  */
 1041                 if (sc->fan_levels)
 1042                         return (FALSE);
 1043                 return (TRUE);
 1044 
 1045         case ACPI_IBM_METHOD_THERMAL:
 1046                 if (ACPI_SUCCESS(acpi_GetInteger(sc->ec_handle, IBM_NAME_THERMAL_GET, &dummy))) {
 1047                         sc->thermal_updt_supported = ACPI_SUCCESS(acpi_GetInteger(sc->ec_handle, IBM_NAME_THERMAL_UPDT, &dummy));
 1048                         return (TRUE);
 1049                 }
 1050                 return (FALSE);
 1051 
 1052         case ACPI_IBM_METHOD_HANDLEREVENTS:
 1053                 return (TRUE);
 1054 
 1055         case ACPI_IBM_METHOD_PRIVACYGUARD:
 1056                 return (acpi_ibm_privacyguard_get(sc) != -1);
 1057         }
 1058         return (FALSE);
 1059 }
 1060 
 1061 static int
 1062 acpi_ibm_thermal_sysctl(SYSCTL_HANDLER_ARGS)
 1063 {
 1064         struct acpi_ibm_softc   *sc;
 1065         int                     error = 0;
 1066         char                    temp_cmd[] = "TMP0";
 1067         int                     temp[8];
 1068 
 1069         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
 1070 
 1071         sc = (struct acpi_ibm_softc *)oidp->oid_arg1;
 1072 
 1073         ACPI_SERIAL_BEGIN(ibm);
 1074 
 1075         for (int i = 0; i < 8; ++i) {
 1076                 temp_cmd[3] = '' + i;
 1077 
 1078                 /*
 1079                  * The TMPx methods seem to return +/- 128 or 0
 1080                  * when the respecting sensor is not available
 1081                  */
 1082                 if (ACPI_FAILURE(acpi_GetInteger(sc->ec_handle, temp_cmd,
 1083                     &temp[i])) || ABS(temp[i]) == 128 || temp[i] == 0)
 1084                         temp[i] = -1;
 1085                 else if (sc->thermal_updt_supported)
 1086                         /* Temperature is reported in tenth of Kelvin */
 1087                         temp[i] = (temp[i] - 2731 + 5) / 10;
 1088         }
 1089 
 1090         error = sysctl_handle_opaque(oidp, &temp, 8*sizeof(int), req);
 1091 
 1092         ACPI_SERIAL_END(ibm);
 1093         return (error);
 1094 }
 1095 
 1096 static int
 1097 acpi_ibm_handlerevents_sysctl(SYSCTL_HANDLER_ARGS)
 1098 {
 1099         struct acpi_ibm_softc   *sc;
 1100         int                     error = 0;
 1101         struct sbuf             sb;
 1102         char                    *cp, *ep;
 1103         int                     l, val;
 1104         unsigned int            handler_events;
 1105         char                    temp[128];
 1106 
 1107         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
 1108 
 1109         sc = (struct acpi_ibm_softc *)oidp->oid_arg1;
 1110 
 1111         if (sbuf_new(&sb, NULL, 128, SBUF_AUTOEXTEND) == NULL)
 1112                 return (ENOMEM);
 1113 
 1114         ACPI_SERIAL_BEGIN(ibm);
 1115 
 1116         /* Get old values if this is a get request. */
 1117         if (req->newptr == NULL) {
 1118                 for (int i = 0; i < 8 * sizeof(sc->handler_events); i++)
 1119                         if (sc->handler_events & (1 << i))
 1120                                 sbuf_printf(&sb, "0x%02x ", i + 1);
 1121                 if (sbuf_len(&sb) == 0)
 1122                         sbuf_printf(&sb, "NONE");
 1123         }
 1124 
 1125         sbuf_trim(&sb);
 1126         sbuf_finish(&sb);
 1127         strlcpy(temp, sbuf_data(&sb), sizeof(temp));
 1128         sbuf_delete(&sb);
 1129 
 1130         error = sysctl_handle_string(oidp, temp, sizeof(temp), req);
 1131 
 1132         /* Check for error or no change */
 1133         if (error != 0 || req->newptr == NULL)
 1134                 goto out;
 1135 
 1136         /* If the user is setting a string, parse it. */
 1137         handler_events = 0;
 1138         cp = temp;
 1139         while (*cp) {
 1140                 if (isspace(*cp)) {
 1141                         cp++;
 1142                         continue;
 1143                 }
 1144 
 1145                 ep = cp;
 1146 
 1147                 while (*ep && !isspace(*ep))
 1148                         ep++;
 1149 
 1150                 l = ep - cp;
 1151                 if (l == 0)
 1152                         break;
 1153 
 1154                 if (strncmp(cp, "NONE", 4) == 0) {
 1155                         cp = ep;
 1156                         continue;
 1157                 }
 1158 
 1159                 if (l >= 3 && cp[0] == '' && (cp[1] == 'X' || cp[1] == 'x'))
 1160                         val = strtoul(cp, &ep, 16);
 1161                 else
 1162                         val = strtoul(cp, &ep, 10);
 1163 
 1164                 if (val == 0 || ep == cp || val >= 8 * sizeof(handler_events)) {
 1165                         cp[l] = '\0';
 1166                         device_printf(sc->dev, "invalid event code: %s\n", cp);
 1167                         error = EINVAL;
 1168                         goto out;
 1169                 }
 1170 
 1171                 handler_events |= 1 << (val - 1);
 1172 
 1173                 cp = ep;
 1174         }
 1175 
 1176         sc->handler_events = handler_events;
 1177 out:
 1178         ACPI_SERIAL_END(ibm);
 1179         return (error);
 1180 }
 1181 
 1182 static int
 1183 acpi_ibm_brightness_set(struct acpi_ibm_softc *sc, int arg)
 1184 {
 1185         int                     val, step;
 1186         UINT64                  val_ec;
 1187         ACPI_OBJECT             Arg;
 1188         ACPI_OBJECT_LIST        Args;
 1189         ACPI_STATUS             status;
 1190 
 1191         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
 1192         ACPI_SERIAL_ASSERT(ibm);
 1193 
 1194         if (arg < 0 || arg > 7)
 1195                 return (EINVAL);
 1196 
 1197         /* Read the current brightness */
 1198         status = ACPI_EC_READ(sc->ec_dev, IBM_EC_BRIGHTNESS, &val_ec, 1);
 1199         if (ACPI_FAILURE(status))
 1200                 return (status);
 1201 
 1202         if (sc->cmos_handle) {
 1203                 val = val_ec & IBM_EC_MASK_BRI;
 1204 
 1205                 Args.Count = 1;
 1206                 Args.Pointer = &Arg;
 1207                 Arg.Type = ACPI_TYPE_INTEGER;
 1208                 Arg.Integer.Value = (arg > val) ? IBM_CMOS_BRIGHTNESS_UP :
 1209                                                   IBM_CMOS_BRIGHTNESS_DOWN;
 1210 
 1211                 step = (arg > val) ? 1 : -1;
 1212                 for (int i = val; i != arg; i += step) {
 1213                         status = AcpiEvaluateObject(sc->cmos_handle, NULL,
 1214                                                     &Args, NULL);
 1215                         if (ACPI_FAILURE(status)) {
 1216                                 /* Record the last value */
 1217                                 if (i != val) {
 1218                                         ACPI_EC_WRITE(sc->ec_dev,
 1219                                             IBM_EC_BRIGHTNESS, i - step, 1);
 1220                                 }
 1221                                 return (status);
 1222                         }
 1223                 }
 1224         }
 1225 
 1226         return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_BRIGHTNESS, arg, 1);
 1227 }
 1228 
 1229 static int
 1230 acpi_ibm_bluetooth_set(struct acpi_ibm_softc *sc, int arg)
 1231 {
 1232         int                     val;
 1233 
 1234         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
 1235         ACPI_SERIAL_ASSERT(ibm);
 1236 
 1237         if (arg < 0 || arg > 1)
 1238                 return (EINVAL);
 1239 
 1240         val = (arg == 1) ? sc->wlan_bt_flags | IBM_NAME_MASK_BT :
 1241                            sc->wlan_bt_flags & (~IBM_NAME_MASK_BT);
 1242         return acpi_SetInteger(sc->handle, IBM_NAME_WLAN_BT_SET, val);
 1243 }
 1244 
 1245 static int
 1246 acpi_ibm_thinklight_set(struct acpi_ibm_softc *sc, int arg)
 1247 {
 1248         ACPI_OBJECT             Arg;
 1249         ACPI_OBJECT_LIST        Args;
 1250         ACPI_STATUS             status;
 1251 
 1252         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
 1253         ACPI_SERIAL_ASSERT(ibm);
 1254 
 1255         if (arg < 0 || arg > 1)
 1256                 return (EINVAL);
 1257 
 1258         if (sc->light_set_supported) {
 1259                 Args.Count = 1;
 1260                 Args.Pointer = &Arg;
 1261                 Arg.Type = ACPI_TYPE_INTEGER;
 1262                 Arg.Integer.Value = arg ? sc->light_cmd_on : sc->light_cmd_off;
 1263 
 1264                 status = AcpiEvaluateObject(sc->light_handle, NULL,
 1265                                             &Args, NULL);
 1266                 if (ACPI_SUCCESS(status))
 1267                         sc->light_val = arg;
 1268                 return (status);
 1269         }
 1270 
 1271         return (0);
 1272 }
 1273 
 1274 /*
 1275  * Helper function to make a get or set ACPI call to the PrivacyGuard handle.
 1276  * Only meant to be used internally by the get/set functions below.
 1277  */
 1278 static ACPI_STATUS
 1279 acpi_ibm_privacyguard_acpi_call(struct acpi_ibm_softc *sc, bool write, int *arg)
 1280 {
 1281         ACPI_OBJECT             Arg;
 1282         ACPI_OBJECT_LIST        Args;
 1283         ACPI_STATUS             status;
 1284         ACPI_OBJECT             out_obj;
 1285         ACPI_BUFFER             result;
 1286 
 1287         Arg.Type = ACPI_TYPE_INTEGER;
 1288         Arg.Integer.Value = (write ? *arg : 0);
 1289         Args.Count = 1;
 1290         Args.Pointer = &Arg;
 1291         result.Length = sizeof(out_obj);
 1292         result.Pointer = &out_obj;
 1293 
 1294         status = AcpiEvaluateObject(sc->handle,
 1295             (write ? IBM_NAME_PRIVACYGUARD_SET : IBM_NAME_PRIVACYGUARD_GET),
 1296             &Args, &result);
 1297         if (ACPI_SUCCESS(status) && !write)
 1298                 *arg = out_obj.Integer.Value;
 1299 
 1300         return (status);
 1301 }
 1302 
 1303 /*
 1304  * Returns -1 if the device is not present.
 1305  */
 1306 static int
 1307 acpi_ibm_privacyguard_get(struct acpi_ibm_softc *sc)
 1308 {
 1309         ACPI_STATUS status;
 1310         int val;
 1311 
 1312         status = acpi_ibm_privacyguard_acpi_call(sc, false, &val);
 1313         if (ACPI_SUCCESS(status) &&
 1314             (val & IBM_FLAG_PRIVACYGUARD_DEVICE_PRESENT))
 1315                 return (val & IBM_FLAG_PRIVACYGUARD_ON);
 1316 
 1317         return (-1);
 1318 }
 1319 
 1320 static ACPI_STATUS
 1321 acpi_ibm_privacyguard_set(struct acpi_ibm_softc *sc, int arg)
 1322 {
 1323         if (arg < 0 || arg > 1)
 1324                 return (AE_BAD_PARAMETER);
 1325 
 1326         return (acpi_ibm_privacyguard_acpi_call(sc, true, &arg));
 1327 }
 1328 
 1329 static int
 1330 acpi_ibm_volume_set(struct acpi_ibm_softc *sc, int arg)
 1331 {
 1332         int                     val, step;
 1333         UINT64                  val_ec;
 1334         ACPI_OBJECT             Arg;
 1335         ACPI_OBJECT_LIST        Args;
 1336         ACPI_STATUS             status;
 1337 
 1338         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
 1339         ACPI_SERIAL_ASSERT(ibm);
 1340 
 1341         if (arg < 0 || arg > 14)
 1342                 return (EINVAL);
 1343 
 1344         /* Read the current volume */
 1345         status = ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1);
 1346         if (ACPI_FAILURE(status))
 1347                 return (status);
 1348 
 1349         if (sc->cmos_handle) {
 1350                 val = val_ec & IBM_EC_MASK_VOL;
 1351 
 1352                 Args.Count = 1;
 1353                 Args.Pointer = &Arg;
 1354                 Arg.Type = ACPI_TYPE_INTEGER;
 1355                 Arg.Integer.Value = (arg > val) ? IBM_CMOS_VOLUME_UP :
 1356                                                   IBM_CMOS_VOLUME_DOWN;
 1357 
 1358                 step = (arg > val) ? 1 : -1;
 1359                 for (int i = val; i != arg; i += step) {
 1360                         status = AcpiEvaluateObject(sc->cmos_handle, NULL,
 1361                                                     &Args, NULL);
 1362                         if (ACPI_FAILURE(status)) {
 1363                                 /* Record the last value */
 1364                                 if (i != val) {
 1365                                         val_ec = i - step +
 1366                                                  (val_ec & (~IBM_EC_MASK_VOL));
 1367                                         ACPI_EC_WRITE(sc->ec_dev, IBM_EC_VOLUME,
 1368                                                       val_ec, 1);
 1369                                 }
 1370                                 return (status);
 1371                         }
 1372                 }
 1373         }
 1374 
 1375         val_ec = arg + (val_ec & (~IBM_EC_MASK_VOL));
 1376         return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_VOLUME, val_ec, 1);
 1377 }
 1378 
 1379 static int
 1380 acpi_ibm_mute_set(struct acpi_ibm_softc *sc, int arg)
 1381 {
 1382         UINT64                  val_ec;
 1383         ACPI_OBJECT             Arg;
 1384         ACPI_OBJECT_LIST        Args;
 1385         ACPI_STATUS             status;
 1386 
 1387         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
 1388         ACPI_SERIAL_ASSERT(ibm);
 1389 
 1390         if (arg < 0 || arg > 1)
 1391                 return (EINVAL);
 1392 
 1393         status = ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1);
 1394         if (ACPI_FAILURE(status))
 1395                 return (status);
 1396 
 1397         if (sc->cmos_handle) {
 1398                 Args.Count = 1;
 1399                 Args.Pointer = &Arg;
 1400                 Arg.Type = ACPI_TYPE_INTEGER;
 1401                 Arg.Integer.Value = IBM_CMOS_VOLUME_MUTE;
 1402 
 1403                 status = AcpiEvaluateObject(sc->cmos_handle, NULL, &Args, NULL);
 1404                 if (ACPI_FAILURE(status))
 1405                         return (status);
 1406         }
 1407 
 1408         val_ec = (arg == 1) ? val_ec | IBM_EC_MASK_MUTE :
 1409                               val_ec & (~IBM_EC_MASK_MUTE);
 1410         return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_VOLUME, val_ec, 1);
 1411 }
 1412 
 1413 static void
 1414 acpi_ibm_eventhandler(struct acpi_ibm_softc *sc, int arg)
 1415 {
 1416         int                     val;
 1417         UINT64                  val_ec;
 1418         ACPI_STATUS             status;
 1419 
 1420         ACPI_SERIAL_BEGIN(ibm);
 1421         switch (arg) {
 1422         case IBM_EVENT_SUSPEND_TO_RAM:
 1423                 power_pm_suspend(POWER_SLEEP_STATE_SUSPEND);
 1424                 break;
 1425 
 1426         case IBM_EVENT_BLUETOOTH:
 1427                 acpi_ibm_bluetooth_set(sc, (sc->wlan_bt_flags == 0));
 1428                 break;
 1429 
 1430         case IBM_EVENT_BRIGHTNESS_UP:
 1431         case IBM_EVENT_BRIGHTNESS_DOWN:
 1432                 /* Read the current brightness */
 1433                 status = ACPI_EC_READ(sc->ec_dev, IBM_EC_BRIGHTNESS,
 1434                                       &val_ec, 1);
 1435                 if (ACPI_FAILURE(status))
 1436                         return;
 1437 
 1438                 val = val_ec & IBM_EC_MASK_BRI;
 1439                 val = (arg == IBM_EVENT_BRIGHTNESS_UP) ? val + 1 : val - 1;
 1440                 acpi_ibm_brightness_set(sc, val);
 1441                 break;
 1442 
 1443         case IBM_EVENT_THINKLIGHT:
 1444                 acpi_ibm_thinklight_set(sc, (sc->light_val == 0));
 1445                 break;
 1446 
 1447         case IBM_EVENT_VOLUME_UP:
 1448         case IBM_EVENT_VOLUME_DOWN:
 1449                 /* Read the current volume */
 1450                 status = ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1);
 1451                 if (ACPI_FAILURE(status))
 1452                         return;
 1453 
 1454                 val = val_ec & IBM_EC_MASK_VOL;
 1455                 val = (arg == IBM_EVENT_VOLUME_UP) ? val + 1 : val - 1;
 1456                 acpi_ibm_volume_set(sc, val);
 1457                 break;
 1458 
 1459         case IBM_EVENT_MUTE:
 1460                 /* Read the current value */
 1461                 status = ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1);
 1462                 if (ACPI_FAILURE(status))
 1463                         return;
 1464 
 1465                 val = ((val_ec & IBM_EC_MASK_MUTE) == IBM_EC_MASK_MUTE);
 1466                 acpi_ibm_mute_set(sc, (val == 0));
 1467                 break;
 1468 
 1469         default:
 1470                 break;
 1471         }
 1472         ACPI_SERIAL_END(ibm);
 1473 }
 1474 
 1475 static void
 1476 acpi_ibm_notify(ACPI_HANDLE h, UINT32 notify, void *context)
 1477 {
 1478         int             event, arg, type;
 1479         device_t        dev = context;
 1480         struct acpi_ibm_softc *sc = device_get_softc(dev);
 1481 
 1482         ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, notify);
 1483 
 1484         if (notify != 0x80)
 1485                 device_printf(dev, "Unknown notify\n");
 1486 
 1487         for (;;) {
 1488                 acpi_GetInteger(acpi_get_handle(dev), IBM_NAME_EVENTS_GET, &event);
 1489                 if (event == 0)
 1490                         break;
 1491 
 1492                 type = (event >> 12) & 0xf;
 1493                 arg = event & 0xfff;
 1494                 switch (type) {
 1495                 case 1:
 1496                         if (!(sc->events_availmask & (1 << (arg - 1)))) {
 1497                                 device_printf(dev, "Unknown key %d\n", arg);
 1498                                 break;
 1499                         }
 1500 
 1501                         /* Execute event handler */
 1502                         if (sc->handler_events & (1 << (arg - 1)))
 1503                                 acpi_ibm_eventhandler(sc, (arg & 0xff));
 1504 
 1505                         /* Notify devd(8) */
 1506                         acpi_UserNotify("IBM", h, (arg & 0xff));
 1507                         break;
 1508                 default:
 1509                         break;
 1510                 }
 1511         }
 1512 }

Cache object: 832bd9f0413a9cfc7ba139767a3f00df


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