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