]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/acpi_support/acpi_ibm.c
MFV r366990: less v563.
[FreeBSD/FreeBSD.git] / sys / dev / acpi_support / acpi_ibm.c
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 static devclass_t acpi_ibm_devclass;
367
368 DRIVER_MODULE(acpi_ibm, acpi, acpi_ibm_driver, acpi_ibm_devclass, 0, 0);
369 MODULE_DEPEND(acpi_ibm, acpi, 1, 1, 1);
370 static char    *ibm_ids[] = {"IBM0068", "LEN0068", "LEN0268", NULL};
371
372 static int
373 acpi_status_to_errno(ACPI_STATUS status)
374 {
375         switch (status) {
376         case AE_OK:
377                 return (0);
378         case AE_BAD_PARAMETER:
379                 return (EINVAL);
380         default:
381                 return (ENODEV);
382         }
383 }
384
385 static void
386 ibm_led(void *softc, int onoff)
387 {
388         struct acpi_ibm_softc *sc = softc;
389
390         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
391
392         if (sc->led_busy)
393                 return;
394
395         sc->led_busy = 1;
396         sc->led_state = onoff;
397
398         AcpiOsExecute(OSL_NOTIFY_HANDLER, (void *)ibm_led_task, sc);
399 }
400
401 static void
402 ibm_led_task(struct acpi_ibm_softc *sc, int pending __unused)
403 {
404         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
405
406         ACPI_SERIAL_BEGIN(ibm);
407         acpi_ibm_sysctl_set(sc, ACPI_IBM_METHOD_THINKLIGHT, sc->led_state);
408         ACPI_SERIAL_END(ibm);
409
410         sc->led_busy = 0;
411 }
412
413 static int
414 acpi_ibm_mic_led_set(struct acpi_ibm_softc *sc, int arg)
415 {
416         ACPI_OBJECT_LIST input;
417         ACPI_OBJECT params[1];
418         ACPI_STATUS status;
419
420         if (arg < 0 || arg > 1)
421                 return (EINVAL);
422
423         if (sc->mic_led_handle) {
424                 params[0].Type = ACPI_TYPE_INTEGER;
425                 params[0].Integer.Value = 0;
426                 /* mic led: 0 off, 2 on */
427                 if (arg == 1)
428                         params[0].Integer.Value = 2;
429
430                 input.Pointer = params;
431                 input.Count = 1;
432
433                 status = AcpiEvaluateObject(sc->handle, "MMTS", &input, NULL);
434                 if (ACPI_SUCCESS(status))
435                         sc->mic_led_state = arg;
436                 return (status);
437         }
438
439         return (0);
440 }
441
442 static int
443 acpi_ibm_probe(device_t dev)
444 {
445         int rv;
446
447         if (acpi_disabled("ibm") || device_get_unit(dev) != 0)
448                 return (ENXIO);
449         rv = ACPI_ID_PROBE(device_get_parent(dev), dev, ibm_ids, NULL);
450
451         if (rv <= 0)
452                 device_set_desc(dev, "ThinkPad ACPI Extras");
453
454         return (rv);
455 }
456
457 static int
458 acpi_ibm_attach(device_t dev)
459 {
460         int i;
461         int hkey;
462         struct acpi_ibm_softc *sc;
463         char *maker, *product;
464         ACPI_OBJECT_LIST input;
465         ACPI_OBJECT params[1];
466         ACPI_OBJECT out_obj;
467         ACPI_BUFFER result;
468         devclass_t ec_devclass;
469
470         ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
471
472         sc = device_get_softc(dev);
473         sc->dev = dev;
474         sc->handle = acpi_get_handle(dev);
475
476         /* Look for the first embedded controller */
477         if (!(ec_devclass = devclass_find ("acpi_ec"))) {
478                 if (bootverbose)
479                         device_printf(dev, "Couldn't find acpi_ec devclass\n");
480                 return (EINVAL);
481         }
482         if (!(sc->ec_dev = devclass_get_device(ec_devclass, 0))) {
483                 if (bootverbose)
484                         device_printf(dev, "Couldn't find acpi_ec device\n");
485                 return (EINVAL);
486         }
487         sc->ec_handle = acpi_get_handle(sc->ec_dev);
488
489         /* Get the sysctl tree */
490         sc->sysctl_ctx = device_get_sysctl_ctx(dev);
491         sc->sysctl_tree = device_get_sysctl_tree(dev);
492
493         /* Look for event mask and hook up the nodes */
494         sc->events_mask_supported = ACPI_SUCCESS(acpi_GetInteger(sc->handle,
495             IBM_NAME_EVENTS_MASK_GET, &sc->events_initialmask));
496
497         if (sc->events_mask_supported) {
498                 SYSCTL_ADD_UINT(sc->sysctl_ctx,
499                     SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "initialmask",
500                     CTLFLAG_RD, &sc->events_initialmask, 0,
501                     "Initial eventmask");
502
503                 if (ACPI_SUCCESS (acpi_GetInteger(sc->handle, "MHKV", &hkey))) {
504                         device_printf(dev, "Firmware version is 0x%X\n", hkey);
505                         switch (hkey >> 8) {
506                         case 1:
507                                 /* The availmask is the bitmask of supported events */
508                                 if (ACPI_FAILURE(acpi_GetInteger(sc->handle,
509                                     IBM_NAME_EVENTS_AVAILMASK, &sc->events_availmask)))
510                                         sc->events_availmask = 0xffffffff;
511                                 break;
512
513                         case 2:
514                                 result.Length = sizeof(out_obj);
515                                 result.Pointer = &out_obj;
516                                 params[0].Type = ACPI_TYPE_INTEGER;
517                                 params[0].Integer.Value = 1;
518                                 input.Pointer = params;
519                                 input.Count = 1;
520
521                                 sc->events_availmask = 0xffffffff;
522
523                                 if (ACPI_SUCCESS(AcpiEvaluateObject (sc->handle,
524                                     IBM_NAME_EVENTS_AVAILMASK, &input, &result)))
525                                         sc->events_availmask = out_obj.Integer.Value;
526                                 break;
527                         default:
528                                 device_printf(dev, "Unknown firmware version 0x%x\n", hkey);
529                                 break;
530                         }
531                 } else
532                         sc->events_availmask = 0xffffffff;
533
534                 SYSCTL_ADD_UINT(sc->sysctl_ctx,
535                                 SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
536                                 "availmask", CTLFLAG_RD,
537                                 &sc->events_availmask, 0, "Mask of supported events");
538         }
539
540         /* Hook up proc nodes */
541         for (int i = 0; acpi_ibm_sysctls[i].name != NULL; i++) {
542                 if (!acpi_ibm_sysctl_init(sc, acpi_ibm_sysctls[i].method))
543                         continue;
544
545                 if (acpi_ibm_sysctls[i].flag_rdonly != 0) {
546                         SYSCTL_ADD_PROC(sc->sysctl_ctx,
547                             SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
548                             acpi_ibm_sysctls[i].name,
549                             CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT,
550                             sc, i, acpi_ibm_sysctl, "I",
551                             acpi_ibm_sysctls[i].description);
552                 } else {
553                         SYSCTL_ADD_PROC(sc->sysctl_ctx,
554                             SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
555                             acpi_ibm_sysctls[i].name,
556                             CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
557                             sc, i, acpi_ibm_sysctl, "I",
558                             acpi_ibm_sysctls[i].description);
559                 }
560         }
561
562         /* Hook up thermal node */
563         if (acpi_ibm_sysctl_init(sc, ACPI_IBM_METHOD_THERMAL)) {
564                 SYSCTL_ADD_PROC(sc->sysctl_ctx,
565                     SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "thermal",
566                     CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, sc, 0,
567                     acpi_ibm_thermal_sysctl, "I", "Thermal zones");
568         }
569
570         /* Hook up handlerevents node */
571         if (acpi_ibm_sysctl_init(sc, ACPI_IBM_METHOD_HANDLEREVENTS)) {
572                 SYSCTL_ADD_PROC(sc->sysctl_ctx,
573                     SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "handlerevents",
574                     CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0,
575                     acpi_ibm_handlerevents_sysctl, "I",
576                     "devd(8) events handled by acpi_ibm");
577         }
578
579         /* Handle notifies */
580         AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY,
581             acpi_ibm_notify, dev);
582
583         /* Hook up light to led(4) */
584         if (sc->light_set_supported)
585                 sc->led_dev = led_create_state(ibm_led, sc, "thinklight",
586                     (sc->light_val ? 1 : 0));
587
588         /* Enable per-model events. */
589         maker = kern_getenv("smbios.system.maker");
590         product = kern_getenv("smbios.system.product");
591         if (maker == NULL || product == NULL)
592                 goto nosmbios;
593
594         for (i = 0; i < nitems(acpi_ibm_models); i++) {
595                 if (strcmp(maker, acpi_ibm_models[i].maker) == 0 &&
596                     strcmp(product, acpi_ibm_models[i].product) == 0) {
597                         ACPI_SERIAL_BEGIN(ibm);
598                         acpi_ibm_sysctl_set(sc, ACPI_IBM_METHOD_EVENTMASK,
599                             acpi_ibm_models[i].eventmask);
600                         ACPI_SERIAL_END(ibm);
601                 }
602         }
603
604 nosmbios:
605         freeenv(maker);
606         freeenv(product);
607
608         /* Enable events by default. */
609         ACPI_SERIAL_BEGIN(ibm);
610         acpi_ibm_sysctl_set(sc, ACPI_IBM_METHOD_EVENTS, 1);
611         ACPI_SERIAL_END(ibm);
612
613         return (0);
614 }
615
616 static int
617 acpi_ibm_detach(device_t dev)
618 {
619         ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
620
621         struct acpi_ibm_softc *sc = device_get_softc(dev);
622
623         /* Disable events and restore eventmask */
624         ACPI_SERIAL_BEGIN(ibm);
625         acpi_ibm_sysctl_set(sc, ACPI_IBM_METHOD_EVENTS, 0);
626         acpi_ibm_sysctl_set(sc, ACPI_IBM_METHOD_EVENTMASK, sc->events_initialmask);
627         ACPI_SERIAL_END(ibm);
628
629         AcpiRemoveNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, acpi_ibm_notify);
630
631         if (sc->led_dev != NULL)
632                 led_destroy(sc->led_dev);
633
634         return (0);
635 }
636
637 static int
638 acpi_ibm_resume(device_t dev)
639 {
640         struct acpi_ibm_softc *sc = device_get_softc(dev);
641
642         ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
643
644         ACPI_SERIAL_BEGIN(ibm);
645         for (int i = 0; acpi_ibm_sysctls[i].name != NULL; i++) {
646                 int val;
647
648                 val = acpi_ibm_sysctl_get(sc, i);
649
650                 if (acpi_ibm_sysctls[i].flag_rdonly != 0)
651                         continue;
652
653                 acpi_ibm_sysctl_set(sc, i, val);
654         }
655         ACPI_SERIAL_END(ibm);
656
657         /* The mic led does not turn back on when sysctl_set is called in the above loop */
658         acpi_ibm_mic_led_set(sc, sc->mic_led_state);
659
660         return (0);
661 }
662
663 static int
664 acpi_ibm_eventmask_set(struct acpi_ibm_softc *sc, int val)
665 {
666         ACPI_OBJECT             arg[2];
667         ACPI_OBJECT_LIST        args;
668         ACPI_STATUS             status;
669
670         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
671         ACPI_SERIAL_ASSERT(ibm);
672
673         args.Count = 2;
674         args.Pointer = arg;
675         arg[0].Type = ACPI_TYPE_INTEGER;
676         arg[1].Type = ACPI_TYPE_INTEGER;
677
678         for (int i = 0; i < 32; ++i) {
679                 arg[0].Integer.Value = i + 1;
680                 arg[1].Integer.Value = (((1 << i) & val) != 0);
681                 status = AcpiEvaluateObject(sc->handle,
682                     IBM_NAME_EVENTS_MASK_SET, &args, NULL);
683
684                 if (ACPI_FAILURE(status))
685                         return (status);
686         }
687
688         return (0);
689 }
690
691 static int
692 acpi_ibm_sysctl(SYSCTL_HANDLER_ARGS)
693 {
694         struct acpi_ibm_softc   *sc;
695         int                     arg;
696         int                     error = 0;
697         int                     function;
698         int                     method;
699
700         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
701
702         sc = (struct acpi_ibm_softc *)oidp->oid_arg1;
703         function = oidp->oid_arg2;
704         method = acpi_ibm_sysctls[function].method;
705
706         ACPI_SERIAL_BEGIN(ibm);
707         arg = acpi_ibm_sysctl_get(sc, method);
708         error = sysctl_handle_int(oidp, &arg, 0, req);
709
710         /* Sanity check */
711         if (error != 0 || req->newptr == NULL)
712                 goto out;
713
714         /* Update */
715         error = acpi_ibm_sysctl_set(sc, method, arg);
716
717 out:
718         ACPI_SERIAL_END(ibm);
719         return (error);
720 }
721
722 static int
723 acpi_ibm_sysctl_get(struct acpi_ibm_softc *sc, int method)
724 {
725         UINT64          val_ec;
726         int             val = 0, key;
727
728         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
729         ACPI_SERIAL_ASSERT(ibm);
730
731         switch (method) {
732         case ACPI_IBM_METHOD_EVENTS:
733                 acpi_GetInteger(sc->handle, IBM_NAME_EVENTS_STATUS_GET, &val);
734                 break;
735
736         case ACPI_IBM_METHOD_EVENTMASK:
737                 if (sc->events_mask_supported)
738                         acpi_GetInteger(sc->handle, IBM_NAME_EVENTS_MASK_GET, &val);
739                 break;
740
741         case ACPI_IBM_METHOD_HOTKEY:
742                 /*
743                  * Construct the hotkey as a bitmask as illustrated below.
744                  * Note that whenever a key was pressed, the respecting bit
745                  * toggles and nothing else changes.
746                  * +--+--+-+-+-+-+-+-+-+-+-+-+
747                  * |11|10|9|8|7|6|5|4|3|2|1|0|
748                  * +--+--+-+-+-+-+-+-+-+-+-+-+
749                  *   |  | | | | | | | | | | |
750                  *   |  | | | | | | | | | | +- Home Button
751                  *   |  | | | | | | | | | +--- Search Button
752                  *   |  | | | | | | | | +----- Mail Button
753                  *   |  | | | | | | | +------- Thinkpad Button
754                  *   |  | | | | | | +--------- Zoom (Fn + Space)
755                  *   |  | | | | | +----------- WLAN Button
756                  *   |  | | | | +------------- Video Button
757                  *   |  | | | +--------------- Hibernate Button
758                  *   |  | | +----------------- Thinklight Button
759                  *   |  | +------------------- Screen expand (Fn + F8)
760                  *   |  +--------------------- Brightness
761                  *   +------------------------ Volume/Mute
762                  */
763                 key = rtcin(IBM_RTC_HOTKEY1);
764                 val = (IBM_RTC_MASK_HOME | IBM_RTC_MASK_SEARCH | IBM_RTC_MASK_MAIL | IBM_RTC_MASK_WLAN) & key;
765                 key = rtcin(IBM_RTC_HOTKEY2);
766                 val |= (IBM_RTC_MASK_THINKPAD | IBM_RTC_MASK_VIDEO | IBM_RTC_MASK_HIBERNATE) & key;
767                 val |= (IBM_RTC_MASK_ZOOM & key) >> 1;
768                 key = rtcin(IBM_RTC_THINKLIGHT);
769                 val |= (IBM_RTC_MASK_THINKLIGHT & key) << 4;
770                 key = rtcin(IBM_RTC_SCREENEXPAND);
771                 val |= (IBM_RTC_MASK_THINKLIGHT & key) << 4;
772                 key = rtcin(IBM_RTC_BRIGHTNESS);
773                 val |= (IBM_RTC_MASK_BRIGHTNESS & key) << 5;
774                 key = rtcin(IBM_RTC_VOLUME);
775                 val |= (IBM_RTC_MASK_VOLUME & key) << 4;
776                 break;
777
778         case ACPI_IBM_METHOD_BRIGHTNESS:
779                 ACPI_EC_READ(sc->ec_dev, IBM_EC_BRIGHTNESS, &val_ec, 1);
780                 val = val_ec & IBM_EC_MASK_BRI;
781                 break;
782
783         case ACPI_IBM_METHOD_VOLUME:
784                 ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1);
785                 val = val_ec & IBM_EC_MASK_VOL;
786                 break;
787
788         case ACPI_IBM_METHOD_MUTE:
789                 ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1);
790                 val = ((val_ec & IBM_EC_MASK_MUTE) == IBM_EC_MASK_MUTE);
791                 break;
792
793         case ACPI_IBM_METHOD_THINKLIGHT:
794                 if (sc->light_get_supported)
795                         acpi_GetInteger(sc->ec_handle, IBM_NAME_KEYLIGHT, &val);
796                 else
797                         val = sc->light_val;
798                 break;
799
800         case ACPI_IBM_METHOD_BLUETOOTH:
801                 acpi_GetInteger(sc->handle, IBM_NAME_WLAN_BT_GET, &val);
802                 sc->wlan_bt_flags = val;
803                 val = ((val & IBM_NAME_MASK_BT) != 0);
804                 break;
805
806         case ACPI_IBM_METHOD_WLAN:
807                 acpi_GetInteger(sc->handle, IBM_NAME_WLAN_BT_GET, &val);
808                 sc->wlan_bt_flags = val;
809                 val = ((val & IBM_NAME_MASK_WLAN) != 0);
810                 break;
811
812         case ACPI_IBM_METHOD_FANSPEED:
813                 if (sc->fan_handle) {
814                         if(ACPI_FAILURE(acpi_GetInteger(sc->fan_handle, NULL, &val)))
815                                 val = -1;
816                 } else {
817                         ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSPEED, &val_ec, 2);
818                         val = val_ec;
819                 }
820                 break;
821
822         case ACPI_IBM_METHOD_FANLEVEL:
823                 /*
824                  * The IBM_EC_FANSTATUS register works as follows:
825                  * Bit 0-5 indicate the level at which the fan operates. Only
826                  *       values between 0 and 7 have an effect. Everything
827                  *       above 7 is treated the same as level 7
828                  * Bit 6 overrides the fan speed limit if set to 1
829                  * Bit 7 indicates at which mode the fan operates:
830                  *       manual (0) or automatic (1)
831                  */
832                 if (!sc->fan_handle) {
833                         ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1);
834                         if (val_ec & IBM_EC_MASK_FANUNTHROTTLED)
835                                 val = 8;
836                         else
837                                 val = val_ec & IBM_EC_MASK_FANLEVEL;
838                 }
839                 break;
840
841         case ACPI_IBM_METHOD_FANSTATUS:
842                 if (!sc->fan_handle) {
843                         ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1);
844                         val = (val_ec & IBM_EC_MASK_FANSTATUS) == IBM_EC_MASK_FANSTATUS;
845                 } else
846                         val = -1;
847                 break;
848
849         case ACPI_IBM_METHOD_MIC_LED:
850                 if (sc->mic_led_handle)
851                         return sc->mic_led_state;
852                 else
853                         val = -1;
854                 break;
855
856         case ACPI_IBM_METHOD_PRIVACYGUARD:
857                 val = acpi_ibm_privacyguard_get(sc);
858                 break;
859         }
860
861         return (val);
862 }
863
864 static int
865 acpi_ibm_sysctl_set(struct acpi_ibm_softc *sc, int method, int arg)
866 {
867         int                     val;
868         UINT64                  val_ec;
869         ACPI_STATUS             status;
870
871         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
872         ACPI_SERIAL_ASSERT(ibm);
873
874         switch (method) {
875         case ACPI_IBM_METHOD_EVENTS:
876                 if (arg < 0 || arg > 1)
877                         return (EINVAL);
878
879                 status = acpi_SetInteger(sc->handle, IBM_NAME_EVENTS_STATUS_SET, arg);
880                 if (ACPI_FAILURE(status))
881                         return (status);
882                 if (sc->events_mask_supported)
883                         return acpi_ibm_eventmask_set(sc, sc->events_availmask);
884                 break;
885
886         case ACPI_IBM_METHOD_EVENTMASK:
887                 if (sc->events_mask_supported)
888                         return acpi_ibm_eventmask_set(sc, arg);
889                 break;
890
891         case ACPI_IBM_METHOD_BRIGHTNESS:
892                 return acpi_ibm_brightness_set(sc, arg);
893                 break;
894
895         case ACPI_IBM_METHOD_VOLUME:
896                 return acpi_ibm_volume_set(sc, arg);
897                 break;
898
899         case ACPI_IBM_METHOD_MUTE:
900                 return acpi_ibm_mute_set(sc, arg);
901                 break;
902
903         case ACPI_IBM_METHOD_MIC_LED:
904                 return acpi_ibm_mic_led_set(sc, arg);
905                 break;
906
907         case ACPI_IBM_METHOD_THINKLIGHT:
908                 return acpi_ibm_thinklight_set(sc, arg);
909                 break;
910
911         case ACPI_IBM_METHOD_BLUETOOTH:
912                 return acpi_ibm_bluetooth_set(sc, arg);
913                 break;
914
915         case ACPI_IBM_METHOD_PRIVACYGUARD:
916                 return (acpi_status_to_errno(acpi_ibm_privacyguard_set(sc, arg)));
917                 break;
918
919         case ACPI_IBM_METHOD_FANLEVEL:
920                 if (arg < 0 || arg > 8)
921                         return (EINVAL);
922
923                 if (!sc->fan_handle) {
924                         /* Read the current fan status. */
925                         ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1);
926                         val = val_ec & ~(IBM_EC_MASK_FANLEVEL |
927                             IBM_EC_MASK_FANUNTHROTTLED);
928
929                         if (arg == 8)
930                                 /* Full speed, set the unthrottled bit. */
931                                 val |= 7 | IBM_EC_MASK_FANUNTHROTTLED;
932                         else
933                                 val |= arg;
934
935                         return (ACPI_EC_WRITE(sc->ec_dev, IBM_EC_FANSTATUS, val,
936                             1));
937                 }
938                 break;
939
940         case ACPI_IBM_METHOD_FANSTATUS:
941                 if (arg < 0 || arg > 1)
942                         return (EINVAL);
943
944                 if (!sc->fan_handle) {
945                         /* Read the current fanstatus */
946                         ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1);
947
948                         return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_FANSTATUS,
949                                 (arg == 1) ? (val_ec | IBM_EC_MASK_FANSTATUS) : (val_ec & (~IBM_EC_MASK_FANSTATUS)), 1);
950                 }
951                 break;
952         }
953
954         return (0);
955 }
956
957 static int
958 acpi_ibm_sysctl_init(struct acpi_ibm_softc *sc, int method)
959 {
960         int                     dummy;
961         ACPI_OBJECT_TYPE        cmos_t;
962         ACPI_HANDLE             ledb_handle;
963
964         switch (method) {
965         case ACPI_IBM_METHOD_EVENTS:
966                 return (TRUE);
967
968         case ACPI_IBM_METHOD_EVENTMASK:
969                 return (sc->events_mask_supported);
970
971         case ACPI_IBM_METHOD_HOTKEY:
972         case ACPI_IBM_METHOD_BRIGHTNESS:
973         case ACPI_IBM_METHOD_VOLUME:
974         case ACPI_IBM_METHOD_MUTE:
975                 /* EC is required here, which was already checked before */
976                 return (TRUE);
977
978         case ACPI_IBM_METHOD_MIC_LED:
979                 if (ACPI_SUCCESS(AcpiGetHandle(sc->handle, "MMTS", &sc->mic_led_handle)))
980                 {
981                         /* Turn off mic led by default */
982                         acpi_ibm_mic_led_set(sc, 0);
983                         return (TRUE);
984                 } else
985                         sc->mic_led_handle = NULL;
986                 return (FALSE);
987
988         case ACPI_IBM_METHOD_THINKLIGHT:
989                 sc->cmos_handle = NULL;
990                 sc->light_get_supported = ACPI_SUCCESS(acpi_GetInteger(
991                     sc->ec_handle, IBM_NAME_KEYLIGHT, &sc->light_val));
992
993                 if ((ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\UCMS", &sc->light_handle)) ||
994                      ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\CMOS", &sc->light_handle)) ||
995                      ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\CMS", &sc->light_handle))) &&
996                      ACPI_SUCCESS(AcpiGetType(sc->light_handle, &cmos_t)) &&
997                      cmos_t == ACPI_TYPE_METHOD) {
998                         sc->light_cmd_on = 0x0c;
999                         sc->light_cmd_off = 0x0d;
1000                         sc->cmos_handle = sc->light_handle;
1001                 }
1002                 else if (ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\LGHT", &sc->light_handle))) {
1003                         sc->light_cmd_on = 1;
1004                         sc->light_cmd_off = 0;
1005                 } else
1006                         sc->light_handle = NULL;
1007
1008                 sc->light_set_supported = (sc->light_handle &&
1009                     ACPI_FAILURE(AcpiGetHandle(sc->ec_handle, "LEDB", &ledb_handle)));
1010
1011                 if (sc->light_get_supported)
1012                         return (TRUE);
1013
1014                 if (sc->light_set_supported) {
1015                         sc->light_val = 0;
1016                         return (TRUE);
1017                 }
1018
1019                 return (FALSE);
1020
1021         case ACPI_IBM_METHOD_BLUETOOTH:
1022         case ACPI_IBM_METHOD_WLAN:
1023                 if (ACPI_SUCCESS(acpi_GetInteger(sc->handle, IBM_NAME_WLAN_BT_GET, &dummy)))
1024                         return (TRUE);
1025                 return (FALSE);
1026
1027         case ACPI_IBM_METHOD_FANSPEED:
1028                 /*
1029                  * Some models report the fan speed in levels from 0-7
1030                  * Newer models report it contiguously
1031                  */
1032                 sc->fan_levels =
1033                     (ACPI_SUCCESS(AcpiGetHandle(sc->handle, "GFAN", &sc->fan_handle)) ||
1034                      ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\FSPD", &sc->fan_handle)));
1035                 return (TRUE);
1036
1037         case ACPI_IBM_METHOD_FANLEVEL:
1038         case ACPI_IBM_METHOD_FANSTATUS:
1039                 /*
1040                  * Fan status is only supported on those models,
1041                  * which report fan RPM contiguously, not in levels
1042                  */
1043                 if (sc->fan_levels)
1044                         return (FALSE);
1045                 return (TRUE);
1046
1047         case ACPI_IBM_METHOD_THERMAL:
1048                 if (ACPI_SUCCESS(acpi_GetInteger(sc->ec_handle, IBM_NAME_THERMAL_GET, &dummy))) {
1049                         sc->thermal_updt_supported = ACPI_SUCCESS(acpi_GetInteger(sc->ec_handle, IBM_NAME_THERMAL_UPDT, &dummy));
1050                         return (TRUE);
1051                 }
1052                 return (FALSE);
1053
1054         case ACPI_IBM_METHOD_HANDLEREVENTS:
1055                 return (TRUE);
1056
1057         case ACPI_IBM_METHOD_PRIVACYGUARD:
1058                 return (acpi_ibm_privacyguard_get(sc) != -1);
1059         }
1060         return (FALSE);
1061 }
1062
1063 static int
1064 acpi_ibm_thermal_sysctl(SYSCTL_HANDLER_ARGS)
1065 {
1066         struct acpi_ibm_softc   *sc;
1067         int                     error = 0;
1068         char                    temp_cmd[] = "TMP0";
1069         int                     temp[8];
1070
1071         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
1072
1073         sc = (struct acpi_ibm_softc *)oidp->oid_arg1;
1074
1075         ACPI_SERIAL_BEGIN(ibm);
1076
1077         for (int i = 0; i < 8; ++i) {
1078                 temp_cmd[3] = '0' + i;
1079
1080                 /*
1081                  * The TMPx methods seem to return +/- 128 or 0
1082                  * when the respecting sensor is not available
1083                  */
1084                 if (ACPI_FAILURE(acpi_GetInteger(sc->ec_handle, temp_cmd,
1085                     &temp[i])) || ABS(temp[i]) == 128 || temp[i] == 0)
1086                         temp[i] = -1;
1087                 else if (sc->thermal_updt_supported)
1088                         /* Temperature is reported in tenth of Kelvin */
1089                         temp[i] = (temp[i] - 2731 + 5) / 10;
1090         }
1091
1092         error = sysctl_handle_opaque(oidp, &temp, 8*sizeof(int), req);
1093
1094         ACPI_SERIAL_END(ibm);
1095         return (error);
1096 }
1097
1098 static int
1099 acpi_ibm_handlerevents_sysctl(SYSCTL_HANDLER_ARGS)
1100 {
1101         struct acpi_ibm_softc   *sc;
1102         int                     error = 0;
1103         struct sbuf             sb;
1104         char                    *cp, *ep;
1105         int                     l, val;
1106         unsigned int            handler_events;
1107         char                    temp[128];
1108
1109         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
1110
1111         sc = (struct acpi_ibm_softc *)oidp->oid_arg1;
1112
1113         if (sbuf_new(&sb, NULL, 128, SBUF_AUTOEXTEND) == NULL)
1114                 return (ENOMEM);
1115
1116         ACPI_SERIAL_BEGIN(ibm);
1117
1118         /* Get old values if this is a get request. */
1119         if (req->newptr == NULL) {
1120                 for (int i = 0; i < 8 * sizeof(sc->handler_events); i++)
1121                         if (sc->handler_events & (1 << i))
1122                                 sbuf_printf(&sb, "0x%02x ", i + 1);
1123                 if (sbuf_len(&sb) == 0)
1124                         sbuf_printf(&sb, "NONE");
1125         }
1126
1127         sbuf_trim(&sb);
1128         sbuf_finish(&sb);
1129         strlcpy(temp, sbuf_data(&sb), sizeof(temp));
1130         sbuf_delete(&sb);
1131
1132         error = sysctl_handle_string(oidp, temp, sizeof(temp), req);
1133
1134         /* Check for error or no change */
1135         if (error != 0 || req->newptr == NULL)
1136                 goto out;
1137
1138         /* If the user is setting a string, parse it. */
1139         handler_events = 0;
1140         cp = temp;
1141         while (*cp) {
1142                 if (isspace(*cp)) {
1143                         cp++;
1144                         continue;
1145                 }
1146
1147                 ep = cp;
1148
1149                 while (*ep && !isspace(*ep))
1150                         ep++;
1151
1152                 l = ep - cp;
1153                 if (l == 0)
1154                         break;
1155
1156                 if (strncmp(cp, "NONE", 4) == 0) {
1157                         cp = ep;
1158                         continue;
1159                 }
1160
1161                 if (l >= 3 && cp[0] == '0' && (cp[1] == 'X' || cp[1] == 'x'))
1162                         val = strtoul(cp, &ep, 16);
1163                 else
1164                         val = strtoul(cp, &ep, 10);
1165
1166                 if (val == 0 || ep == cp || val >= 8 * sizeof(handler_events)) {
1167                         cp[l] = '\0';
1168                         device_printf(sc->dev, "invalid event code: %s\n", cp);
1169                         error = EINVAL;
1170                         goto out;
1171                 }
1172
1173                 handler_events |= 1 << (val - 1);
1174
1175                 cp = ep;
1176         }
1177
1178         sc->handler_events = handler_events;
1179 out:
1180         ACPI_SERIAL_END(ibm);
1181         return (error);
1182 }
1183
1184 static int
1185 acpi_ibm_brightness_set(struct acpi_ibm_softc *sc, int arg)
1186 {
1187         int                     val, step;
1188         UINT64                  val_ec;
1189         ACPI_OBJECT             Arg;
1190         ACPI_OBJECT_LIST        Args;
1191         ACPI_STATUS             status;
1192
1193         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
1194         ACPI_SERIAL_ASSERT(ibm);
1195
1196         if (arg < 0 || arg > 7)
1197                 return (EINVAL);
1198
1199         /* Read the current brightness */
1200         status = ACPI_EC_READ(sc->ec_dev, IBM_EC_BRIGHTNESS, &val_ec, 1);
1201         if (ACPI_FAILURE(status))
1202                 return (status);
1203
1204         if (sc->cmos_handle) {
1205                 val = val_ec & IBM_EC_MASK_BRI;
1206
1207                 Args.Count = 1;
1208                 Args.Pointer = &Arg;
1209                 Arg.Type = ACPI_TYPE_INTEGER;
1210                 Arg.Integer.Value = (arg > val) ? IBM_CMOS_BRIGHTNESS_UP :
1211                                                   IBM_CMOS_BRIGHTNESS_DOWN;
1212
1213                 step = (arg > val) ? 1 : -1;
1214                 for (int i = val; i != arg; i += step) {
1215                         status = AcpiEvaluateObject(sc->cmos_handle, NULL,
1216                                                     &Args, NULL);
1217                         if (ACPI_FAILURE(status)) {
1218                                 /* Record the last value */
1219                                 if (i != val) {
1220                                         ACPI_EC_WRITE(sc->ec_dev,
1221                                             IBM_EC_BRIGHTNESS, i - step, 1);
1222                                 }
1223                                 return (status);
1224                         }
1225                 }
1226         }
1227
1228         return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_BRIGHTNESS, arg, 1);
1229 }
1230
1231 static int
1232 acpi_ibm_bluetooth_set(struct acpi_ibm_softc *sc, int arg)
1233 {
1234         int                     val;
1235
1236         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
1237         ACPI_SERIAL_ASSERT(ibm);
1238
1239         if (arg < 0 || arg > 1)
1240                 return (EINVAL);
1241
1242         val = (arg == 1) ? sc->wlan_bt_flags | IBM_NAME_MASK_BT :
1243                            sc->wlan_bt_flags & (~IBM_NAME_MASK_BT);
1244         return acpi_SetInteger(sc->handle, IBM_NAME_WLAN_BT_SET, val);
1245 }
1246
1247 static int
1248 acpi_ibm_thinklight_set(struct acpi_ibm_softc *sc, int arg)
1249 {
1250         ACPI_OBJECT             Arg;
1251         ACPI_OBJECT_LIST        Args;
1252         ACPI_STATUS             status;
1253
1254         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
1255         ACPI_SERIAL_ASSERT(ibm);
1256
1257         if (arg < 0 || arg > 1)
1258                 return (EINVAL);
1259
1260         if (sc->light_set_supported) {
1261                 Args.Count = 1;
1262                 Args.Pointer = &Arg;
1263                 Arg.Type = ACPI_TYPE_INTEGER;
1264                 Arg.Integer.Value = arg ? sc->light_cmd_on : sc->light_cmd_off;
1265
1266                 status = AcpiEvaluateObject(sc->light_handle, NULL,
1267                                             &Args, NULL);
1268                 if (ACPI_SUCCESS(status))
1269                         sc->light_val = arg;
1270                 return (status);
1271         }
1272
1273         return (0);
1274 }
1275
1276 /*
1277  * Helper function to make a get or set ACPI call to the PrivacyGuard handle.
1278  * Only meant to be used internally by the get/set functions below.
1279  */
1280 static ACPI_STATUS
1281 acpi_ibm_privacyguard_acpi_call(struct acpi_ibm_softc *sc, bool write, int *arg)
1282 {
1283         ACPI_OBJECT             Arg;
1284         ACPI_OBJECT_LIST        Args;
1285         ACPI_STATUS             status;
1286         ACPI_OBJECT             out_obj;
1287         ACPI_BUFFER             result;
1288
1289         Arg.Type = ACPI_TYPE_INTEGER;
1290         Arg.Integer.Value = (write ? *arg : 0);
1291         Args.Count = 1;
1292         Args.Pointer = &Arg;
1293         result.Length = sizeof(out_obj);
1294         result.Pointer = &out_obj;
1295
1296         status = AcpiEvaluateObject(sc->handle,
1297             (write ? IBM_NAME_PRIVACYGUARD_SET : IBM_NAME_PRIVACYGUARD_GET),
1298             &Args, &result);
1299         if (ACPI_SUCCESS(status) && !write)
1300                 *arg = out_obj.Integer.Value;
1301
1302         return (status);
1303 }
1304
1305 /*
1306  * Returns -1 if the device is not present.
1307  */
1308 static int
1309 acpi_ibm_privacyguard_get(struct acpi_ibm_softc *sc)
1310 {
1311         ACPI_STATUS status;
1312         int val;
1313
1314         status = acpi_ibm_privacyguard_acpi_call(sc, false, &val);
1315         if (ACPI_SUCCESS(status) &&
1316             (val & IBM_FLAG_PRIVACYGUARD_DEVICE_PRESENT))
1317                 return (val & IBM_FLAG_PRIVACYGUARD_ON);
1318
1319         return (-1);
1320 }
1321
1322 static ACPI_STATUS
1323 acpi_ibm_privacyguard_set(struct acpi_ibm_softc *sc, int arg)
1324 {
1325         if (arg < 0 || arg > 1)
1326                 return (AE_BAD_PARAMETER);
1327
1328         return (acpi_ibm_privacyguard_acpi_call(sc, true, &arg));
1329 }
1330
1331 static int
1332 acpi_ibm_volume_set(struct acpi_ibm_softc *sc, int arg)
1333 {
1334         int                     val, step;
1335         UINT64                  val_ec;
1336         ACPI_OBJECT             Arg;
1337         ACPI_OBJECT_LIST        Args;
1338         ACPI_STATUS             status;
1339
1340         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
1341         ACPI_SERIAL_ASSERT(ibm);
1342
1343         if (arg < 0 || arg > 14)
1344                 return (EINVAL);
1345
1346         /* Read the current volume */
1347         status = ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1);
1348         if (ACPI_FAILURE(status))
1349                 return (status);
1350
1351         if (sc->cmos_handle) {
1352                 val = val_ec & IBM_EC_MASK_VOL;
1353
1354                 Args.Count = 1;
1355                 Args.Pointer = &Arg;
1356                 Arg.Type = ACPI_TYPE_INTEGER;
1357                 Arg.Integer.Value = (arg > val) ? IBM_CMOS_VOLUME_UP :
1358                                                   IBM_CMOS_VOLUME_DOWN;
1359
1360                 step = (arg > val) ? 1 : -1;
1361                 for (int i = val; i != arg; i += step) {
1362                         status = AcpiEvaluateObject(sc->cmos_handle, NULL,
1363                                                     &Args, NULL);
1364                         if (ACPI_FAILURE(status)) {
1365                                 /* Record the last value */
1366                                 if (i != val) {
1367                                         val_ec = i - step +
1368                                                  (val_ec & (~IBM_EC_MASK_VOL));
1369                                         ACPI_EC_WRITE(sc->ec_dev, IBM_EC_VOLUME,
1370                                                       val_ec, 1);
1371                                 }
1372                                 return (status);
1373                         }
1374                 }
1375         }
1376
1377         val_ec = arg + (val_ec & (~IBM_EC_MASK_VOL));
1378         return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_VOLUME, val_ec, 1);
1379 }
1380
1381 static int
1382 acpi_ibm_mute_set(struct acpi_ibm_softc *sc, int arg)
1383 {
1384         UINT64                  val_ec;
1385         ACPI_OBJECT             Arg;
1386         ACPI_OBJECT_LIST        Args;
1387         ACPI_STATUS             status;
1388
1389         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
1390         ACPI_SERIAL_ASSERT(ibm);
1391
1392         if (arg < 0 || arg > 1)
1393                 return (EINVAL);
1394
1395         status = ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1);
1396         if (ACPI_FAILURE(status))
1397                 return (status);
1398
1399         if (sc->cmos_handle) {
1400                 Args.Count = 1;
1401                 Args.Pointer = &Arg;
1402                 Arg.Type = ACPI_TYPE_INTEGER;
1403                 Arg.Integer.Value = IBM_CMOS_VOLUME_MUTE;
1404
1405                 status = AcpiEvaluateObject(sc->cmos_handle, NULL, &Args, NULL);
1406                 if (ACPI_FAILURE(status))
1407                         return (status);
1408         }
1409
1410         val_ec = (arg == 1) ? val_ec | IBM_EC_MASK_MUTE :
1411                               val_ec & (~IBM_EC_MASK_MUTE);
1412         return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_VOLUME, val_ec, 1);
1413 }
1414
1415 static void
1416 acpi_ibm_eventhandler(struct acpi_ibm_softc *sc, int arg)
1417 {
1418         int                     val;
1419         UINT64                  val_ec;
1420         ACPI_STATUS             status;
1421
1422         ACPI_SERIAL_BEGIN(ibm);
1423         switch (arg) {
1424         case IBM_EVENT_SUSPEND_TO_RAM:
1425                 power_pm_suspend(POWER_SLEEP_STATE_SUSPEND);
1426                 break;
1427
1428         case IBM_EVENT_BLUETOOTH:
1429                 acpi_ibm_bluetooth_set(sc, (sc->wlan_bt_flags == 0));
1430                 break;
1431
1432         case IBM_EVENT_BRIGHTNESS_UP:
1433         case IBM_EVENT_BRIGHTNESS_DOWN:
1434                 /* Read the current brightness */
1435                 status = ACPI_EC_READ(sc->ec_dev, IBM_EC_BRIGHTNESS,
1436                                       &val_ec, 1);
1437                 if (ACPI_FAILURE(status))
1438                         return;
1439
1440                 val = val_ec & IBM_EC_MASK_BRI;
1441                 val = (arg == IBM_EVENT_BRIGHTNESS_UP) ? val + 1 : val - 1;
1442                 acpi_ibm_brightness_set(sc, val);
1443                 break;
1444
1445         case IBM_EVENT_THINKLIGHT:
1446                 acpi_ibm_thinklight_set(sc, (sc->light_val == 0));
1447                 break;
1448
1449         case IBM_EVENT_VOLUME_UP:
1450         case IBM_EVENT_VOLUME_DOWN:
1451                 /* Read the current volume */
1452                 status = ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1);
1453                 if (ACPI_FAILURE(status))
1454                         return;
1455
1456                 val = val_ec & IBM_EC_MASK_VOL;
1457                 val = (arg == IBM_EVENT_VOLUME_UP) ? val + 1 : val - 1;
1458                 acpi_ibm_volume_set(sc, val);
1459                 break;
1460
1461         case IBM_EVENT_MUTE:
1462                 /* Read the current value */
1463                 status = ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1);
1464                 if (ACPI_FAILURE(status))
1465                         return;
1466
1467                 val = ((val_ec & IBM_EC_MASK_MUTE) == IBM_EC_MASK_MUTE);
1468                 acpi_ibm_mute_set(sc, (val == 0));
1469                 break;
1470
1471         default:
1472                 break;
1473         }
1474         ACPI_SERIAL_END(ibm);
1475 }
1476
1477 static void
1478 acpi_ibm_notify(ACPI_HANDLE h, UINT32 notify, void *context)
1479 {
1480         int             event, arg, type;
1481         device_t        dev = context;
1482         struct acpi_ibm_softc *sc = device_get_softc(dev);
1483
1484         ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, notify);
1485
1486         if (notify != 0x80)
1487                 device_printf(dev, "Unknown notify\n");
1488
1489         for (;;) {
1490                 acpi_GetInteger(acpi_get_handle(dev), IBM_NAME_EVENTS_GET, &event);
1491                 if (event == 0)
1492                         break;
1493
1494                 type = (event >> 12) & 0xf;
1495                 arg = event & 0xfff;
1496                 switch (type) {
1497                 case 1:
1498                         if (!(sc->events_availmask & (1 << (arg - 1)))) {
1499                                 device_printf(dev, "Unknown key %d\n", arg);
1500                                 break;
1501                         }
1502
1503                         /* Execute event handler */
1504                         if (sc->handler_events & (1 << (arg - 1)))
1505                                 acpi_ibm_eventhandler(sc, (arg & 0xff));
1506
1507                         /* Notify devd(8) */
1508                         acpi_UserNotify("IBM", h, (arg & 0xff));
1509                         break;
1510                 default:
1511                         break;
1512                 }
1513         }
1514 }