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