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 }
1001