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] = '' + 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 }
Cache object: b5aeedb5b188ead09440e3b18f24ec9f
|