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