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