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: head/sys/dev/acpi_support/acpi_ibm.c 246128 2013-01-30 18:01:20Z sbz $
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/sbuf.h>
47 #include <sys/sysctl.h>
48 #include <sys/lock.h>
49 #include <sys/thread2.h>
50 #include <machine/clock.h>
51 
52 #include "acpi.h"
53 #include "accommon.h"
54 #include "acpivar.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 #define ACPI_THINKPAD_METHOD_HANDLEREVENTS	14
74 
75 /* Hotkeys/Buttons */
76 #define THINKPAD_RTC_HOTKEY1			0x64
77 #define   THINKPAD_RTC_MASK_HOME		(1 << 0)
78 #define   THINKPAD_RTC_MASK_SEARCH		(1 << 1)
79 #define   THINKPAD_RTC_MASK_MAIL		(1 << 2)
80 #define   THINKPAD_RTC_MASK_WLAN		(1 << 5)
81 #define THINKPAD_RTC_HOTKEY2			0x65
82 #define   THINKPAD_RTC_MASK_THINKPAD		(1 << 3)
83 #define   THINKPAD_RTC_MASK_ZOOM		(1 << 5)
84 #define   THINKPAD_RTC_MASK_VIDEO		(1 << 6)
85 #define   THINKPAD_RTC_MASK_HIBERNATE		(1 << 7)
86 #define THINKPAD_RTC_THINKLIGHT			0x66
87 #define   THINKPAD_RTC_MASK_THINKLIGHT		(1 << 4)
88 #define THINKPAD_RTC_SCREENEXPAND		0x67
89 #define   THINKPAD_RTC_MASK_SCREENEXPAND	(1 << 5)
90 #define THINKPAD_RTC_BRIGHTNESS			0x6c
91 #define   THINKPAD_RTC_MASK_BRIGHTNESS		(1 << 5)
92 #define THINKPAD_RTC_VOLUME			0x6e
93 #define   THINKPAD_RTC_MASK_VOLUME		(1 << 7)
94 
95 /* Embedded Controller registers */
96 #define THINKPAD_EC_BRIGHTNESS			0x31
97 #define   THINKPAD_EC_MASK_BRI			0x7
98 #define THINKPAD_EC_VOLUME			0x30
99 #define   THINKPAD_EC_MASK_VOL			0xf
100 #define   THINKPAD_EC_MASK_MUTE			(1 << 6)
101 #define THINKPAD_EC_FANSTATUS			0x2F
102 #define   THINKPAD_EC_MASK_FANLEVEL		0x3f
103 #define   THINKPAD_EC_MASK_FANDISENGAGED	(1 << 6)
104 #define   THINKPAD_EC_MASK_FANSTATUS		(1 << 7)
105 #define THINKPAD_EC_FANSPEED			0x84
106 
107 /* CMOS Commands */
108 #define THINKPAD_CMOS_VOLUME_DOWN		0
109 #define THINKPAD_CMOS_VOLUME_UP			1
110 #define THINKPAD_CMOS_VOLUME_MUTE		2
111 #define THINKPAD_CMOS_BRIGHTNESS_UP		4
112 #define THINKPAD_CMOS_BRIGHTNESS_DOWN		5
113 
114 /* ACPI methods */
115 #define THINKPAD_NAME_KEYLIGHT			"KBLT"
116 #define THINKPAD_NAME_WLAN_BT_GET		"GBDC"
117 #define THINKPAD_NAME_WLAN_BT_SET		"SBDC"
118 #define   THINKPAD_NAME_MASK_BT			(1 << 1)
119 #define   THINKPAD_NAME_MASK_WLAN		(1 << 2)
120 #define THINKPAD_NAME_THERMAL_GET		"TMP7"
121 #define THINKPAD_NAME_THERMAL_UPDT		"UPDT"
122 
123 #define THINKPAD_NAME_EVENTS_STATUS_GET		"DHKC"
124 #define THINKPAD_NAME_EVENTS_MASK_GET		"DHKN"
125 #define THINKPAD_NAME_EVENTS_STATUS_SET		"MHKC"
126 #define THINKPAD_NAME_EVENTS_MASK_SET		"MHKM"
127 #define THINKPAD_NAME_EVENTS_GET		"MHKP"
128 #define THINKPAD_NAME_EVENTS_AVAILMASK		"MHKA"
129 
130 #define	THINKPAD_NUM_SENSORS			9
131 #define	THINKPAD_TEMP_SENSORS			8
132 
133 /* Event Code */
134 #define THINKPAD_EVENT_LCD_BACKLIGHT		0x03
135 #define THINKPAD_EVENT_SUSPEND_TO_RAM		0x04
136 #define THINKPAD_EVENT_BLUETOOTH		0x05
137 #define THINKPAD_EVENT_SCREEN_EXPAND		0x07
138 #define THINKPAD_EVENT_SUSPEND_TO_DISK		0x0c
139 #define THINKPAD_EVENT_BRIGHTNESS_UP		0x10
140 #define THINKPAD_EVENT_BRIGHTNESS_DOWN		0x11
141 #define THINKPAD_EVENT_THINKLIGHT		0x12
142 #define THINKPAD_EVENT_ZOOM			0x14
143 #define THINKPAD_EVENT_VOLUME_UP		0x15
144 #define THINKPAD_EVENT_VOLUME_DOWN		0x16
145 #define THINKPAD_EVENT_MUTE			0x17
146 #define THINKPAD_EVENT_ACCESS_THINKPAD_BUTTON	0x18
147 
148 #define ABS(x) (((x) < 0)? -(x) : (x))
149 
150 struct acpi_thinkpad_softc {
151 	device_t	dev;
152 	ACPI_HANDLE	handle;
153 
154 	/* Embedded controller */
155 	device_t	ec_dev;
156 	ACPI_HANDLE	ec_handle;
157 
158 	/* CMOS */
159 	ACPI_HANDLE	cmos_handle;
160 
161 	/* Fan status */
162 	ACPI_HANDLE	fan_handle;
163 	int		fan_levels;
164 
165 	/* Keylight commands and states */
166 	ACPI_HANDLE	light_handle;
167 	int		light_cmd_on;
168 	int		light_cmd_off;
169 	int		light_val;
170 	int		light_get_supported;
171 	int		light_set_supported;
172 
173 	/* led(4) interface */
174 	struct cdev	*led_dev;
175 	int		led_busy;
176 	int		led_state;
177 
178 	int		wlan_bt_flags;
179 	int		thermal_updt_supported;
180 
181 	unsigned int	events_availmask;
182 	unsigned int	events_initialmask;
183 	int		events_mask_supported;
184 	int		events_enable;
185 
186 	/* sensors(9) related */
187 	struct ksensordev sensordev;
188 	struct ksensor sensors[THINKPAD_NUM_SENSORS];
189 
190 	unsigned int	handler_events;
191 
192 	struct sysctl_ctx_list	 sysctl_ctx;
193 	struct sysctl_oid	*sysctl_tree;
194 };
195 
196 static struct {
197 	char	*name;
198 	int	method;
199 	char	*description;
200 	int	access;
201 } acpi_thinkpad_sysctls[] = {
202 	{
203 		.name		= "events",
204 		.method		= ACPI_THINKPAD_METHOD_EVENTS,
205 		.description	= "ACPI events enable",
206 		.access		= CTLTYPE_INT | CTLFLAG_RW
207 	},
208 	{
209 		.name		= "eventmask",
210 		.method		= ACPI_THINKPAD_METHOD_EVENTMASK,
211 		.description	= "ACPI eventmask",
212 		.access		= CTLTYPE_INT | CTLFLAG_RW
213 	},
214 	{
215 		.name		= "hotkey",
216 		.method		= ACPI_THINKPAD_METHOD_HOTKEY,
217 		.description	= "Key Status",
218 		.access		= CTLTYPE_INT | CTLFLAG_RD
219 	},
220 	{
221 		.name		= "lcd_brightness",
222 		.method		= ACPI_THINKPAD_METHOD_BRIGHTNESS,
223 		.description	= "LCD Brightness",
224 		.access		= CTLTYPE_INT | CTLFLAG_RW
225 	},
226 	{
227 		.name		= "volume",
228 		.method		= ACPI_THINKPAD_METHOD_VOLUME,
229 		.description	= "Volume",
230 		.access		= CTLTYPE_INT | CTLFLAG_RW
231 	},
232 	{
233 		.name		= "mute",
234 		.method		= ACPI_THINKPAD_METHOD_MUTE,
235 		.description	= "Mute",
236 		.access		= CTLTYPE_INT | CTLFLAG_RW
237 	},
238 	{
239 		.name		= "thinklight",
240 		.method		= ACPI_THINKPAD_METHOD_THINKLIGHT,
241 		.description	= "Thinklight enable",
242 		.access		= CTLTYPE_INT | CTLFLAG_RW
243 	},
244 	{
245 		.name		= "bluetooth",
246 		.method		= ACPI_THINKPAD_METHOD_BLUETOOTH,
247 		.description	= "Bluetooth enable",
248 		.access		= CTLTYPE_INT | CTLFLAG_RW
249 	},
250 	{
251 		.name		= "wlan",
252 		.method		= ACPI_THINKPAD_METHOD_WLAN,
253 		.description	= "WLAN enable",
254 		.access		= CTLTYPE_INT | CTLFLAG_RD
255 	},
256 	{
257 		.name		= "fan_speed",
258 		.method		= ACPI_THINKPAD_METHOD_FANSPEED,
259 		.description	= "Fan speed",
260 		.access		= CTLTYPE_INT | CTLFLAG_RD
261 	},
262 	{
263 		.name		= "fan_level",
264 		.method		= ACPI_THINKPAD_METHOD_FANLEVEL,
265 		.description	= "Fan level",
266 		.access		= CTLTYPE_INT | CTLFLAG_RW
267 	},
268 	{
269 		.name		= "fan",
270 		.method		= ACPI_THINKPAD_METHOD_FANSTATUS,
271 		.description	= "Fan enable",
272 		.access		= CTLTYPE_INT | CTLFLAG_RW
273 	},
274 
275 	{ NULL, 0, NULL, 0 }
276 };
277 
278 ACPI_SERIAL_DECL(thinkpad, "ACPI Thinkpad extras");
279 
280 static int	acpi_thinkpad_probe(device_t dev);
281 static int	acpi_thinkpad_attach(device_t dev);
282 static int	acpi_thinkpad_detach(device_t dev);
283 static int	acpi_thinkpad_resume(device_t dev);
284 
285 #if 0 /* XXX */
286 static void	thinkpad_led(void *softc, int onoff);
287 static void	thinkpad_led_task(struct acpi_thinkpad_softc *sc, int pending __unused);
288 #endif
289 
290 static int	acpi_thinkpad_sysctl(SYSCTL_HANDLER_ARGS);
291 static int	acpi_thinkpad_sysctl_init(struct acpi_thinkpad_softc *sc,
292 		int method);
293 static int	acpi_thinkpad_sysctl_get(struct acpi_thinkpad_softc *sc,
294 		int method);
295 static int	acpi_thinkpad_sysctl_set(struct acpi_thinkpad_softc *sc,
296 		int method, int val);
297 
298 static int	acpi_thinkpad_eventmask_set(struct acpi_thinkpad_softc *sc,
299 		int val);
300 static int	acpi_thinkpad_handlerevents_sysctl(SYSCTL_HANDLER_ARGS);
301 static void	acpi_thinkpad_notify(ACPI_HANDLE h, UINT32 notify,
302 		void *context);
303 static void	acpi_thinkpad_refresh(void *);
304 
305 static int	acpi_thinkpad_brightness_set(struct acpi_thinkpad_softc *sc, int arg);
306 static int	acpi_thinkpad_bluetooth_set(struct acpi_thinkpad_softc *sc, int arg);
307 static int	acpi_thinkpad_thinklight_set(struct acpi_thinkpad_softc *sc, int arg);
308 static int	acpi_thinkpad_volume_set(struct acpi_thinkpad_softc *sc, int arg);
309 static int	acpi_thinkpad_mute_set(struct acpi_thinkpad_softc *sc, int arg);
310 
311 static device_method_t acpi_thinkpad_methods[] = {
312 	/* Device interface */
313 	DEVMETHOD(device_probe, acpi_thinkpad_probe),
314 	DEVMETHOD(device_attach, acpi_thinkpad_attach),
315 	DEVMETHOD(device_detach, acpi_thinkpad_detach),
316 	DEVMETHOD(device_resume, acpi_thinkpad_resume),
317 
318 	DEVMETHOD_END
319 };
320 
321 static driver_t	acpi_thinkpad_driver = {
322 	"acpi_thinkpad",
323 	acpi_thinkpad_methods,
324 	sizeof(struct acpi_thinkpad_softc),
325 };
326 
327 static devclass_t acpi_thinkpad_devclass;
328 
329 DRIVER_MODULE(acpi_thinkpad, acpi, acpi_thinkpad_driver,
330     acpi_thinkpad_devclass, NULL, NULL);
331 MODULE_DEPEND(acpi_thinkpad, acpi, 1, 1, 1);
332 static char    *thinkpad_ids[] = {"IBM0068", "LEN0068", NULL};
333 
334 #if 0 /* XXX */
335 static void
336 thinkpad_led(void *softc, int onoff)
337 {
338 	struct acpi_thinkpad_softc* sc = (struct acpi_thinkpad_softc*) softc;
339 
340 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
341 
342 	if (sc->led_busy)
343 		return;
344 
345 	sc->led_busy = 1;
346 	sc->led_state = onoff;
347 
348 	AcpiOsExecute(OSL_NOTIFY_HANDLER, (void *)thinkpad_led_task, sc);
349 }
350 
351 static void
352 thinkpad_led_task(struct acpi_thinkpad_softc *sc, int pending __unused)
353 {
354 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
355 
356 	ACPI_SERIAL_BEGIN(thinkpad);
357 	acpi_thinkpad_sysctl_set(sc, ACPI_THINKPAD_METHOD_THINKLIGHT, sc->led_state);
358 	ACPI_SERIAL_END(thinkpad);
359 
360 	sc->led_busy = 0;
361 }
362 #endif
363 
364 static int
365 acpi_thinkpad_probe(device_t dev)
366 {
367 	if (acpi_disabled("thinkpad") ||
368 	    ACPI_ID_PROBE(device_get_parent(dev), dev, thinkpad_ids) == NULL ||
369 	    device_get_unit(dev) != 0)
370 		return (ENXIO);
371 
372 	device_set_desc(dev, "IBM/Lenovo ThinkPad ACPI Extras");
373 	return (0);
374 }
375 
376 static int
377 acpi_thinkpad_attach(device_t dev)
378 {
379 	struct acpi_thinkpad_softc	*sc;
380 	struct acpi_softc	*acpi_sc;
381 	devclass_t		ec_devclass;
382 	int			i;
383 
384 	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
385 
386 	sc = device_get_softc(dev);
387 	sc->dev = dev;
388 	sc->handle = acpi_get_handle(dev);
389 
390 	acpi_sc = acpi_device_get_parent_softc(dev);
391 
392 	/* Look for the first embedded controller */
393         if (!(ec_devclass = devclass_find ("acpi_ec"))) {
394 		if (bootverbose)
395 			device_printf(dev, "Couldn't find acpi_ec devclass\n");
396 		return (EINVAL);
397 	}
398         if (!(sc->ec_dev = devclass_get_device(ec_devclass, 0))) {
399 		if (bootverbose)
400 			device_printf(dev, "Couldn't find acpi_ec device\n");
401 		return (EINVAL);
402 	}
403 	sc->ec_handle = acpi_get_handle(sc->ec_dev);
404 
405 	sysctl_ctx_init(&sc->sysctl_ctx);
406 	sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
407 	    SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree), OID_AUTO,
408 	    "thinkpad", CTLFLAG_RD, 0, "");
409 
410 	/* Look for event mask and hook up the nodes */
411 	sc->events_mask_supported = ACPI_SUCCESS(acpi_GetInteger(sc->handle,
412 	    THINKPAD_NAME_EVENTS_MASK_GET, &sc->events_initialmask));
413 
414 	if (sc->events_mask_supported) {
415 		SYSCTL_ADD_UINT(&sc->sysctl_ctx,
416 		    SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
417 		    "initialmask", CTLFLAG_RD,
418 		    &sc->events_initialmask, 0, "Initial eventmask");
419 
420 		/* The availmask is the bitmask of supported events */
421 		if (ACPI_FAILURE(acpi_GetInteger(sc->handle,
422 		    THINKPAD_NAME_EVENTS_AVAILMASK, &sc->events_availmask)))
423 			sc->events_availmask = 0xffffffff;
424 
425 		SYSCTL_ADD_UINT(&sc->sysctl_ctx,
426 		    SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
427 		    "availmask", CTLFLAG_RD,
428 		    &sc->events_availmask, 0, "Mask of supported events");
429 	}
430 
431 	/* Hook up proc nodes */
432 	for (i = 0; acpi_thinkpad_sysctls[i].name != NULL; i++) {
433 		if (!acpi_thinkpad_sysctl_init(sc,
434 		    acpi_thinkpad_sysctls[i].method))
435 			continue;
436 
437 		SYSCTL_ADD_PROC(&sc->sysctl_ctx,
438 		    SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
439 		    acpi_thinkpad_sysctls[i].name,
440 		    acpi_thinkpad_sysctls[i].access,
441 		    sc, i, acpi_thinkpad_sysctl, "I",
442 		    acpi_thinkpad_sysctls[i].description);
443 	}
444 
445 	/* Hook up handlerevents node */
446 	if (acpi_thinkpad_sysctl_init(sc, ACPI_THINKPAD_METHOD_HANDLEREVENTS)) {
447 		SYSCTL_ADD_PROC(&sc->sysctl_ctx,
448 		    SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
449 		    "handlerevents", CTLTYPE_STRING | CTLFLAG_RW,
450 		    sc, 0, acpi_thinkpad_handlerevents_sysctl, "I",
451 		    "devd(8) events handled by acpi_thinkpad");
452 	}
453 
454 	/* Handle notifies */
455 	AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY,
456 	    acpi_thinkpad_notify, dev);
457 
458 	/* Attach sensors(9). */
459 	if (sensor_task_register(sc, acpi_thinkpad_refresh, 5)) {
460 		device_printf(sc->dev, "unable to register update task\n");
461 		return 1;
462 	}
463 
464 	strlcpy(sc->sensordev.xname, device_get_nameunit(sc->dev),
465 	    sizeof(sc->sensordev.xname));
466 
467 	for (i = 0; i < THINKPAD_TEMP_SENSORS; i++) {
468 		sc->sensors[i].type = SENSOR_TEMP;
469 		sensor_attach(&sc->sensordev, &sc->sensors[i]);
470 	}
471 
472 	sc->sensors[i].type = SENSOR_FANRPM;
473 	sensor_attach(&sc->sensordev, &sc->sensors[i]);
474 
475 	sensordev_install(&sc->sensordev);
476 
477 #if 0 /* XXX */
478 	/* Hook up light to led(4) */
479 	if (sc->light_set_supported)
480 		sc->led_dev = led_create_state(thinkpad_led, sc, "thinklight", sc->light_val);
481 #endif
482 
483 	return (0);
484 }
485 
486 static int
487 acpi_thinkpad_detach(device_t dev)
488 {
489 	int i;
490 
491 	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
492 
493 	struct acpi_thinkpad_softc *sc = device_get_softc(dev);
494 
495 	/* Disable events and restore eventmask */
496 	ACPI_SERIAL_BEGIN(thinkpad);
497 	acpi_thinkpad_sysctl_set(sc, ACPI_THINKPAD_METHOD_EVENTS, 0);
498 	acpi_thinkpad_sysctl_set(sc, ACPI_THINKPAD_METHOD_EVENTMASK,
499 	    sc->events_initialmask);
500 	ACPI_SERIAL_END(thinkpad);
501 
502 	AcpiRemoveNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY,
503 	    acpi_thinkpad_notify);
504 
505 	if (sc->sysctl_tree != NULL)
506 		sysctl_ctx_free(&sc->sysctl_ctx);
507 
508 	sensordev_deinstall(&sc->sensordev);
509 	for (i = 0; i < THINKPAD_NUM_SENSORS; i++)
510 		sensor_detach(&sc->sensordev, &sc->sensors[i]);
511 	sensor_task_unregister(sc);
512 
513 #if 0 /* XXX */
514 	if (sc->led_dev != NULL)
515 		led_destroy(sc->led_dev);
516 #endif
517 
518 	return (0);
519 }
520 
521 static int
522 acpi_thinkpad_resume(device_t dev)
523 {
524 	struct acpi_thinkpad_softc *sc = device_get_softc(dev);
525 
526 	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
527 
528 	ACPI_SERIAL_BEGIN(thinkpad);
529 	for (int i = 0; acpi_thinkpad_sysctls[i].name != NULL; i++) {
530 		int val;
531 
532 		if ((acpi_thinkpad_sysctls[i].access & CTLFLAG_RD) == 0) {
533 			continue;
534 		}
535 
536 		val = acpi_thinkpad_sysctl_get(sc, i);
537 
538 		if ((acpi_thinkpad_sysctls[i].access & CTLFLAG_WR) == 0) {
539 			continue;
540 		}
541 
542 		acpi_thinkpad_sysctl_set(sc, i, val);
543 	}
544 	ACPI_SERIAL_END(thinkpad);
545 
546 	return (0);
547 }
548 
549 static int
550 acpi_thinkpad_eventmask_set(struct acpi_thinkpad_softc *sc, int val)
551 {
552 	int i;
553 	ACPI_OBJECT		arg[2];
554 	ACPI_OBJECT_LIST	args;
555 	ACPI_STATUS		status;
556 
557 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
558 	ACPI_SERIAL_ASSERT(thinkpad);
559 
560 	args.Count = 2;
561 	args.Pointer = arg;
562 	arg[0].Type = ACPI_TYPE_INTEGER;
563 	arg[1].Type = ACPI_TYPE_INTEGER;
564 
565 	for (i = 0; i < 32; ++i) {
566 		arg[0].Integer.Value = i+1;
567 		arg[1].Integer.Value = (((1 << i) & val) != 0);
568 		status = AcpiEvaluateObject(sc->handle,
569 		    THINKPAD_NAME_EVENTS_MASK_SET, &args, NULL);
570 
571 		if (ACPI_FAILURE(status))
572 			return (status);
573 	}
574 
575 	return (0);
576 }
577 
578 static int
579 acpi_thinkpad_sysctl(SYSCTL_HANDLER_ARGS)
580 {
581 	struct acpi_thinkpad_softc	*sc;
582 	int			arg;
583 	int			error = 0;
584 	int			function;
585 	int			method;
586 
587 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
588 
589 	sc = (struct acpi_thinkpad_softc *)oidp->oid_arg1;
590 	function = oidp->oid_arg2;
591 	method = acpi_thinkpad_sysctls[function].method;
592 
593 	ACPI_SERIAL_BEGIN(thinkpad);
594 	arg = acpi_thinkpad_sysctl_get(sc, method);
595 	error = sysctl_handle_int(oidp, &arg, 0, req);
596 
597 	/* Sanity check */
598 	if (error != 0 || req->newptr == NULL)
599 		goto out;
600 
601 	/* Update */
602 	error = acpi_thinkpad_sysctl_set(sc, method, arg);
603 
604 out:
605 	ACPI_SERIAL_END(thinkpad);
606 	return (error);
607 }
608 
609 static int
610 acpi_thinkpad_sysctl_get(struct acpi_thinkpad_softc *sc, int method)
611 {
612 	UINT64		val_ec;
613 	int 		val = 0, key;
614 
615 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
616 	ACPI_SERIAL_ASSERT(thinkpad);
617 
618 	switch (method) {
619 	case ACPI_THINKPAD_METHOD_EVENTS:
620 		acpi_GetInteger(sc->handle, THINKPAD_NAME_EVENTS_STATUS_GET,
621 		    &val);
622 		break;
623 
624 	case ACPI_THINKPAD_METHOD_EVENTMASK:
625 		if (sc->events_mask_supported)
626 			acpi_GetInteger(sc->handle,
627 			    THINKPAD_NAME_EVENTS_MASK_GET, &val);
628 		break;
629 
630 	case ACPI_THINKPAD_METHOD_HOTKEY:
631 		/*
632 		 * Construct the hotkey as a bitmask as illustrated below.
633 		 * Note that whenever a key was pressed, the respecting bit
634 		 * toggles and nothing else changes.
635 		 * +--+--+-+-+-+-+-+-+-+-+-+-+
636 		 * |11|10|9|8|7|6|5|4|3|2|1|0|
637 		 * +--+--+-+-+-+-+-+-+-+-+-+-+
638 		 *   |  | | | | | | | | | | |
639 		 *   |  | | | | | | | | | | +- Home Button
640 		 *   |  | | | | | | | | | +--- Search Button
641 		 *   |  | | | | | | | | +----- Mail Button
642 		 *   |  | | | | | | | +------- Thinkpad Button
643 		 *   |  | | | | | | +--------- Zoom (Fn + Space)
644 		 *   |  | | | | | +----------- WLAN Button
645 		 *   |  | | | | +------------- Video Button
646 		 *   |  | | | +--------------- Hibernate Button
647 		 *   |  | | +----------------- Thinklight Button
648 		 *   |  | +------------------- Screen expand (Fn + F8)
649 		 *   |  +--------------------- Brightness
650 		 *   +------------------------ Volume/Mute
651 		 */
652 		key = rtcin(THINKPAD_RTC_HOTKEY1);
653 		val = (THINKPAD_RTC_MASK_HOME | THINKPAD_RTC_MASK_SEARCH |
654 		    THINKPAD_RTC_MASK_MAIL | THINKPAD_RTC_MASK_WLAN) & key;
655 		key = rtcin(THINKPAD_RTC_HOTKEY2);
656 		val |= (THINKPAD_RTC_MASK_THINKPAD | THINKPAD_RTC_MASK_VIDEO |
657 		    THINKPAD_RTC_MASK_HIBERNATE) & key;
658 		val |= (THINKPAD_RTC_MASK_ZOOM & key) >> 1;
659 		key = rtcin(THINKPAD_RTC_THINKLIGHT);
660 		val |= (THINKPAD_RTC_MASK_THINKLIGHT & key) << 4;
661 		key = rtcin(THINKPAD_RTC_SCREENEXPAND);
662 		val |= (THINKPAD_RTC_MASK_THINKLIGHT & key) << 4;
663 		key = rtcin(THINKPAD_RTC_BRIGHTNESS);
664 		val |= (THINKPAD_RTC_MASK_BRIGHTNESS & key) << 5;
665 		key = rtcin(THINKPAD_RTC_VOLUME);
666 		val |= (THINKPAD_RTC_MASK_VOLUME & key) << 4;
667 		break;
668 
669 	case ACPI_THINKPAD_METHOD_BRIGHTNESS:
670 		ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_BRIGHTNESS, &val_ec, 1);
671 		val = val_ec & THINKPAD_EC_MASK_BRI;
672 		break;
673 
674 	case ACPI_THINKPAD_METHOD_VOLUME:
675 		ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_VOLUME, &val_ec, 1);
676 		val = val_ec & THINKPAD_EC_MASK_VOL;
677 		break;
678 
679 	case ACPI_THINKPAD_METHOD_MUTE:
680 		ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_VOLUME, &val_ec, 1);
681 		val = ((val_ec & THINKPAD_EC_MASK_MUTE) ==
682 		    THINKPAD_EC_MASK_MUTE);
683 		break;
684 
685 	case ACPI_THINKPAD_METHOD_THINKLIGHT:
686 		if (sc->light_get_supported)
687 			acpi_GetInteger(sc->ec_handle, THINKPAD_NAME_KEYLIGHT,
688 			    &val);
689 		else
690 			val = sc->light_val;
691 		break;
692 
693 	case ACPI_THINKPAD_METHOD_BLUETOOTH:
694 		acpi_GetInteger(sc->handle, THINKPAD_NAME_WLAN_BT_GET, &val);
695 		sc->wlan_bt_flags = val;
696 		val = ((val & THINKPAD_NAME_MASK_BT) != 0);
697 		break;
698 
699 	case ACPI_THINKPAD_METHOD_WLAN:
700 		acpi_GetInteger(sc->handle, THINKPAD_NAME_WLAN_BT_GET, &val);
701 		sc->wlan_bt_flags = val;
702 		val = ((val & THINKPAD_NAME_MASK_WLAN) != 0);
703 		break;
704 
705 	case ACPI_THINKPAD_METHOD_FANSPEED:
706 		if (sc->fan_handle) {
707 			if (ACPI_FAILURE(acpi_GetInteger(sc->fan_handle,
708 			    NULL, &val)))
709 				val = -1;
710 		}
711 		else {
712 			ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_FANSPEED,
713 			    &val_ec, 2);
714 			val = val_ec;
715 		}
716 		break;
717 
718 	case ACPI_THINKPAD_METHOD_FANLEVEL:
719 		/*
720 		 * The THINKPAD_EC_FANSTATUS register works as follows:
721 		 * Bit 0-5 indicate the level at which the fan operates. Only
722 		 *       values between 0 and 7 have an effect. Everything
723 		 *       above 7 is treated the same as level 7
724 		 * Bit 6 overrides the fan speed limit if set to 1
725 		 * Bit 7 indicates at which mode the fan operates:
726 		 *       manual (0) or automatic (1)
727 		 */
728 		if (!sc->fan_handle) {
729 			ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_FANSTATUS,
730 			    &val_ec, 1);
731 			val = val_ec & THINKPAD_EC_MASK_FANLEVEL;
732 		}
733 		break;
734 
735 	case ACPI_THINKPAD_METHOD_FANSTATUS:
736 		if (!sc->fan_handle) {
737 			ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_FANSTATUS,
738 			    &val_ec, 1);
739 			val = (val_ec & THINKPAD_EC_MASK_FANSTATUS) ==
740 			    THINKPAD_EC_MASK_FANSTATUS;
741 		}
742 		else
743 			val = -1;
744 		break;
745 	}
746 
747 	return (val);
748 }
749 
750 static int
751 acpi_thinkpad_sysctl_set(struct acpi_thinkpad_softc *sc, int method, int arg)
752 {
753 	int			val;
754 	UINT64			val_ec;
755 	ACPI_STATUS		status;
756 
757 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
758 	ACPI_SERIAL_ASSERT(thinkpad);
759 
760 	switch (method) {
761 	case ACPI_THINKPAD_METHOD_EVENTS:
762 		if (arg < 0 || arg > 1)
763 			return (EINVAL);
764 
765 		status = acpi_SetInteger(sc->handle,
766 		    THINKPAD_NAME_EVENTS_STATUS_SET, arg);
767 		if (ACPI_FAILURE(status))
768 			return (status);
769 		if (sc->events_mask_supported)
770 			return acpi_thinkpad_eventmask_set(sc,
771 			    sc->events_availmask);
772 		break;
773 
774 	case ACPI_THINKPAD_METHOD_EVENTMASK:
775 		if (sc->events_mask_supported)
776 			return acpi_thinkpad_eventmask_set(sc, arg);
777 		break;
778 
779 	case ACPI_THINKPAD_METHOD_BRIGHTNESS:
780 		return acpi_thinkpad_brightness_set(sc, arg);
781 		break;
782 
783 	case ACPI_THINKPAD_METHOD_VOLUME:
784 		return acpi_thinkpad_volume_set(sc, arg);
785 		break;
786 
787 	case ACPI_THINKPAD_METHOD_MUTE:
788 		return acpi_thinkpad_mute_set(sc, arg);
789 		break;
790 
791 	case ACPI_THINKPAD_METHOD_THINKLIGHT:
792 		return acpi_thinkpad_thinklight_set(sc, arg);
793 		break;
794 
795 	case ACPI_THINKPAD_METHOD_BLUETOOTH:
796 		return acpi_thinkpad_bluetooth_set(sc, arg);
797 		break;
798 
799 	case ACPI_THINKPAD_METHOD_FANLEVEL:
800 		if (arg < 0 || arg > 7)
801 			return (EINVAL);
802 
803 		if (!sc->fan_handle) {
804 			/* Read the current fanstatus */
805 			ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_FANSTATUS,
806 			    &val_ec, 1);
807 			val = val_ec & (~THINKPAD_EC_MASK_FANLEVEL);
808 
809 			return ACPI_EC_WRITE(sc->ec_dev, THINKPAD_EC_FANSTATUS,
810 			    val | arg, 1);
811 		}
812 		break;
813 
814 	case ACPI_THINKPAD_METHOD_FANSTATUS:
815 		if (arg < 0 || arg > 1)
816 			return (EINVAL);
817 
818 		if (!sc->fan_handle) {
819 			/* Read the current fanstatus */
820 			ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_FANSTATUS,
821 			    &val_ec, 1);
822 
823 			return ACPI_EC_WRITE(sc->ec_dev, THINKPAD_EC_FANSTATUS,
824 			    (arg == 1) ? (val_ec | THINKPAD_EC_MASK_FANSTATUS) :
825 			    (val_ec & (~THINKPAD_EC_MASK_FANSTATUS)), 1);
826 		}
827 		break;
828 	}
829 
830 	return (0);
831 }
832 
833 static int
834 acpi_thinkpad_sysctl_init(struct acpi_thinkpad_softc *sc, int method)
835 {
836 	int 			dummy;
837 	ACPI_OBJECT_TYPE 	cmos_t;
838 	ACPI_HANDLE		ledb_handle;
839 
840 	switch (method) {
841 	case ACPI_THINKPAD_METHOD_EVENTS:
842 		/* Events are disabled by default */
843 		return (TRUE);
844 
845 	case ACPI_THINKPAD_METHOD_EVENTMASK:
846 		return (sc->events_mask_supported);
847 
848 	case ACPI_THINKPAD_METHOD_HOTKEY:
849 	case ACPI_THINKPAD_METHOD_BRIGHTNESS:
850 	case ACPI_THINKPAD_METHOD_VOLUME:
851 	case ACPI_THINKPAD_METHOD_MUTE:
852 		/* EC is required here, which was already checked before */
853 		return (TRUE);
854 
855 	case ACPI_THINKPAD_METHOD_THINKLIGHT:
856 		sc->cmos_handle = NULL;
857 		sc->light_get_supported = ACPI_SUCCESS(acpi_GetInteger(
858 		    sc->ec_handle, THINKPAD_NAME_KEYLIGHT, &sc->light_val));
859 
860 		if ((ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\UCMS",
861 		    &sc->light_handle)) ||
862 		    ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\CMOS",
863 		    &sc->light_handle)) ||
864 		    ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\CMS",
865 		    &sc->light_handle))) &&
866 		    ACPI_SUCCESS(AcpiGetType(sc->light_handle, &cmos_t)) &&
867 		    cmos_t == ACPI_TYPE_METHOD) {
868 			sc->light_cmd_on = 0x0c;
869 			sc->light_cmd_off = 0x0d;
870 			sc->cmos_handle = sc->light_handle;
871 		}
872 		else if (ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\LGHT",
873 		    &sc->light_handle))) {
874 			sc->light_cmd_on = 1;
875 			sc->light_cmd_off = 0;
876 		}
877 		else
878 			sc->light_handle = NULL;
879 
880 		sc->light_set_supported = (sc->light_handle &&
881 		    ACPI_FAILURE(AcpiGetHandle(sc->ec_handle, "LEDB",
882 		    &ledb_handle)));
883 
884 		if (sc->light_get_supported)
885 			return (TRUE);
886 
887 		if (sc->light_set_supported) {
888 			sc->light_val = 0;
889 			return (TRUE);
890 		}
891 
892 		return (FALSE);
893 
894 	case ACPI_THINKPAD_METHOD_BLUETOOTH:
895 	case ACPI_THINKPAD_METHOD_WLAN:
896 		if (ACPI_SUCCESS(acpi_GetInteger(sc->handle,
897 		    THINKPAD_NAME_WLAN_BT_GET, &dummy)))
898 			return (TRUE);
899 		return (FALSE);
900 
901 	case ACPI_THINKPAD_METHOD_FANSPEED:
902 		/*
903 		 * Some models report the fan speed in levels from 0-7
904 		 * Newer models report it contiguously
905 		 */
906 		sc->fan_levels = (ACPI_SUCCESS(AcpiGetHandle(sc->handle, "GFAN",
907 		    &sc->fan_handle)) ||
908 		    ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\FSPD",
909 		    &sc->fan_handle)));
910 		return (TRUE);
911 
912 	case ACPI_THINKPAD_METHOD_FANLEVEL:
913 	case ACPI_THINKPAD_METHOD_FANSTATUS:
914 		/*
915 		 * Fan status is only supported on those models,
916 		 * which report fan RPM contiguously, not in levels
917 		 */
918 		if (sc->fan_levels)
919 			return (FALSE);
920 		return (TRUE);
921 
922 	case ACPI_THINKPAD_METHOD_THERMAL:
923 		if (ACPI_SUCCESS(acpi_GetInteger(sc->ec_handle,
924 		    THINKPAD_NAME_THERMAL_GET, &dummy))) {
925 			sc->thermal_updt_supported =
926 			    ACPI_SUCCESS(acpi_GetInteger(sc->ec_handle,
927 			    THINKPAD_NAME_THERMAL_UPDT, &dummy));
928 			return (TRUE);
929 		}
930 		return (FALSE);
931 
932 	case ACPI_THINKPAD_METHOD_HANDLEREVENTS:
933 		return (TRUE);
934 	}
935 	return (FALSE);
936 }
937 
938 static int
939 acpi_thinkpad_handlerevents_sysctl(SYSCTL_HANDLER_ARGS)
940 {
941 	struct acpi_thinkpad_softc	*sc;
942 	int			error = 0;
943 	struct sbuf		sb;
944 	char			*cp, *ep;
945 	int			l, val;
946 	unsigned int		handler_events;
947 
948 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
949 
950 	sc = (struct acpi_thinkpad_softc *)oidp->oid_arg1;
951 
952 	if (sbuf_new(&sb, NULL, 128, SBUF_AUTOEXTEND) == NULL)
953 		return (ENOMEM);
954 
955 	ACPI_SERIAL_BEGIN(thinkpad);
956 
957 	/* Get old values if this is a get request. */
958 	if (req->newptr == NULL) {
959 		for (int i = 0; i < 8 * sizeof(sc->handler_events); i++)
960 			if (sc->handler_events & (1 << i))
961 				sbuf_printf(&sb, "0x%02x ", i + 1);
962 		if (sbuf_len(&sb) == 0)
963 			sbuf_printf(&sb, "NONE");
964 	}
965 
966 	sbuf_trim(&sb);
967 	sbuf_finish(&sb);
968 
969 	/* Copy out the old values to the user. */
970 	error = SYSCTL_OUT(req, sbuf_data(&sb), sbuf_len(&sb));
971 	sbuf_delete(&sb);
972 
973 	if (error != 0 || req->newptr == NULL)
974 		goto out;
975 
976 	/* If the user is setting a string, parse it. */
977 	handler_events = 0;
978 	cp = (char *)req->newptr;
979 	while (*cp) {
980 		if (isspace(*cp)) {
981 			cp++;
982 			continue;
983 		}
984 
985 		ep = cp;
986 
987 		while (*ep && !isspace(*ep))
988 			ep++;
989 
990 		l = ep - cp;
991 		if (l == 0)
992 			break;
993 
994 		if (strncmp(cp, "NONE", 4) == 0) {
995 			cp = ep;
996 			continue;
997 		}
998 
999 		if (l >= 3 && cp[0] == '0' && (cp[1] == 'X' || cp[1] == 'x'))
1000 			val = strtoul(cp, &ep, 16);
1001 		else
1002 			val = strtoul(cp, &ep, 10);
1003 
1004 		if (val == 0 || ep == cp || val >= 8 * sizeof(handler_events)) {
1005 			cp[l] = '\0';
1006 			device_printf(sc->dev, "invalid event code: %s\n", cp);
1007 			error = EINVAL;
1008 			goto out;
1009 		}
1010 
1011 		handler_events |= 1 << (val - 1);
1012 
1013 		cp = ep;
1014 	}
1015 
1016 	sc->handler_events = handler_events;
1017 out:
1018 	ACPI_SERIAL_END(thinkpad);
1019 	return (error);
1020 }
1021 
1022 static int
1023 acpi_thinkpad_brightness_set(struct acpi_thinkpad_softc *sc, int arg)
1024 {
1025 	int			val, step;
1026 	UINT64			val_ec;
1027 	ACPI_OBJECT		Arg;
1028 	ACPI_OBJECT_LIST	Args;
1029 	ACPI_STATUS		status;
1030 
1031 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
1032 	ACPI_SERIAL_ASSERT(thinkpad);
1033 
1034 	if (arg < 0 || arg > 7)
1035 		return (EINVAL);
1036 
1037 	/* Read the current brightness */
1038 	status = ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_BRIGHTNESS, &val_ec, 1);
1039 	if (ACPI_FAILURE(status))
1040 		return (status);
1041 
1042 	if (sc->cmos_handle) {
1043 		val = val_ec & THINKPAD_EC_MASK_BRI;
1044 
1045 		Args.Count = 1;
1046 		Args.Pointer = &Arg;
1047 		Arg.Type = ACPI_TYPE_INTEGER;
1048 		Arg.Integer.Value = (arg > val) ? THINKPAD_CMOS_BRIGHTNESS_UP :
1049 						  THINKPAD_CMOS_BRIGHTNESS_DOWN;
1050 
1051 		step = (arg > val) ? 1 : -1;
1052 		for (int i = val; i != arg; i += step) {
1053 			status = AcpiEvaluateObject(sc->cmos_handle, NULL,
1054 						    &Args, NULL);
1055 			if (ACPI_FAILURE(status)) {
1056 				/* Record the last value */
1057 				if (i != val) {
1058 					ACPI_EC_WRITE(sc->ec_dev,
1059 					    THINKPAD_EC_BRIGHTNESS, i - step, 1);
1060 				}
1061 				return (status);
1062 			}
1063 		}
1064 	}
1065 
1066 	return ACPI_EC_WRITE(sc->ec_dev, THINKPAD_EC_BRIGHTNESS, arg, 1);
1067 }
1068 
1069 static int
1070 acpi_thinkpad_bluetooth_set(struct acpi_thinkpad_softc *sc, int arg)
1071 {
1072 	int			val;
1073 
1074 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
1075 	ACPI_SERIAL_ASSERT(thinkpad);
1076 
1077 	if (arg < 0 || arg > 1)
1078 		return (EINVAL);
1079 
1080 	val = (arg == 1) ? sc->wlan_bt_flags | THINKPAD_NAME_MASK_BT :
1081 			   sc->wlan_bt_flags & (~THINKPAD_NAME_MASK_BT);
1082 	return acpi_SetInteger(sc->handle, THINKPAD_NAME_WLAN_BT_SET, val);
1083 }
1084 
1085 static int
1086 acpi_thinkpad_thinklight_set(struct acpi_thinkpad_softc *sc, int arg)
1087 {
1088 	ACPI_OBJECT		Arg;
1089 	ACPI_OBJECT_LIST	Args;
1090 	ACPI_STATUS		status;
1091 
1092 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
1093 	ACPI_SERIAL_ASSERT(thinkpad);
1094 
1095 	if (arg < 0 || arg > 1)
1096 		return (EINVAL);
1097 
1098 	if (sc->light_set_supported) {
1099 		Args.Count = 1;
1100 		Args.Pointer = &Arg;
1101 		Arg.Type = ACPI_TYPE_INTEGER;
1102 		Arg.Integer.Value = arg ? sc->light_cmd_on : sc->light_cmd_off;
1103 
1104 		status = AcpiEvaluateObject(sc->light_handle, NULL,
1105 					    &Args, NULL);
1106 		if (ACPI_SUCCESS(status))
1107 			sc->light_val = arg;
1108 		return (status);
1109 	}
1110 
1111 	return (0);
1112 }
1113 
1114 static int
1115 acpi_thinkpad_volume_set(struct acpi_thinkpad_softc *sc, int arg)
1116 {
1117 	int			val, step;
1118 	UINT64			val_ec;
1119 	ACPI_OBJECT		Arg;
1120 	ACPI_OBJECT_LIST	Args;
1121 	ACPI_STATUS		status;
1122 
1123 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
1124 	ACPI_SERIAL_ASSERT(thinkpad);
1125 
1126 	if (arg < 0 || arg > 14)
1127 		return (EINVAL);
1128 
1129 	/* Read the current volume */
1130 	status = ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_VOLUME, &val_ec, 1);
1131 	if (ACPI_FAILURE(status))
1132 		return (status);
1133 
1134 	if (sc->cmos_handle) {
1135 		val = val_ec & THINKPAD_EC_MASK_VOL;
1136 
1137 		Args.Count = 1;
1138 		Args.Pointer = &Arg;
1139 		Arg.Type = ACPI_TYPE_INTEGER;
1140 		Arg.Integer.Value = (arg > val) ? THINKPAD_CMOS_VOLUME_UP :
1141 						  THINKPAD_CMOS_VOLUME_DOWN;
1142 
1143 		step = (arg > val) ? 1 : -1;
1144 		for (int i = val; i != arg; i += step) {
1145 			status = AcpiEvaluateObject(sc->cmos_handle, NULL,
1146 						    &Args, NULL);
1147 			if (ACPI_FAILURE(status)) {
1148 				/* Record the last value */
1149 				if (i != val) {
1150 					val_ec = i - step +
1151 						 (val_ec & (~THINKPAD_EC_MASK_VOL));
1152 					ACPI_EC_WRITE(sc->ec_dev, THINKPAD_EC_VOLUME,
1153 						      val_ec, 1);
1154 				}
1155 				return (status);
1156 			}
1157 		}
1158 	}
1159 
1160 	val_ec = arg + (val_ec & (~THINKPAD_EC_MASK_VOL));
1161 	return ACPI_EC_WRITE(sc->ec_dev, THINKPAD_EC_VOLUME, val_ec, 1);
1162 }
1163 
1164 static int
1165 acpi_thinkpad_mute_set(struct acpi_thinkpad_softc *sc, int arg)
1166 {
1167 	UINT64			val_ec;
1168 	ACPI_OBJECT		Arg;
1169 	ACPI_OBJECT_LIST	Args;
1170 	ACPI_STATUS		status;
1171 
1172 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
1173 	ACPI_SERIAL_ASSERT(thinkpad);
1174 
1175 	if (arg < 0 || arg > 1)
1176 		return (EINVAL);
1177 
1178 	status = ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_VOLUME, &val_ec, 1);
1179 	if (ACPI_FAILURE(status))
1180 		return (status);
1181 
1182 	if (sc->cmos_handle) {
1183 		Args.Count = 1;
1184 		Args.Pointer = &Arg;
1185 		Arg.Type = ACPI_TYPE_INTEGER;
1186 		Arg.Integer.Value = THINKPAD_CMOS_VOLUME_MUTE;
1187 
1188 		status = AcpiEvaluateObject(sc->cmos_handle, NULL, &Args, NULL);
1189 		if (ACPI_FAILURE(status))
1190 			return (status);
1191 	}
1192 
1193 	val_ec = (arg == 1) ? val_ec | THINKPAD_EC_MASK_MUTE :
1194 			      val_ec & (~THINKPAD_EC_MASK_MUTE);
1195 	return ACPI_EC_WRITE(sc->ec_dev, THINKPAD_EC_VOLUME, val_ec, 1);
1196 }
1197 
1198 static void
1199 acpi_thinkpad_eventhandler(struct acpi_thinkpad_softc *sc, int arg)
1200 {
1201 	int			val;
1202 	UINT64			val_ec;
1203 	ACPI_STATUS		status;
1204 
1205 	ACPI_SERIAL_BEGIN(thinkpad);
1206 	switch (arg) {
1207 #if 0 /* XXX */
1208 	case THINKPAD_EVENT_SUSPEND_TO_RAM:
1209 		power_pm_suspend(POWER_SLEEP_STATE_SUSPEND);
1210 		break;
1211 #endif
1212 
1213 	case THINKPAD_EVENT_BLUETOOTH:
1214 		acpi_thinkpad_bluetooth_set(sc, (sc->wlan_bt_flags == 0));
1215 		break;
1216 
1217 	case THINKPAD_EVENT_BRIGHTNESS_UP:
1218 	case THINKPAD_EVENT_BRIGHTNESS_DOWN:
1219 		/* Read the current brightness */
1220 		status = ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_BRIGHTNESS,
1221 				      &val_ec, 1);
1222 		if (ACPI_FAILURE(status))
1223 			return;
1224 
1225 		val = val_ec & THINKPAD_EC_MASK_BRI;
1226 		val = (arg == THINKPAD_EVENT_BRIGHTNESS_UP) ? val + 1 : val - 1;
1227 		acpi_thinkpad_brightness_set(sc, val);
1228 		break;
1229 
1230 	case THINKPAD_EVENT_THINKLIGHT:
1231 		acpi_thinkpad_thinklight_set(sc, (sc->light_val == 0));
1232 		break;
1233 
1234 	case THINKPAD_EVENT_VOLUME_UP:
1235 	case THINKPAD_EVENT_VOLUME_DOWN:
1236 		/* Read the current volume */
1237 		status = ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_VOLUME, &val_ec, 1);
1238 		if (ACPI_FAILURE(status))
1239 			return;
1240 
1241 		val = val_ec & THINKPAD_EC_MASK_VOL;
1242 		val = (arg == THINKPAD_EVENT_VOLUME_UP) ? val + 1 : val - 1;
1243 		acpi_thinkpad_volume_set(sc, val);
1244 		break;
1245 
1246 	case THINKPAD_EVENT_MUTE:
1247 		/* Read the current value */
1248 		status = ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_VOLUME, &val_ec, 1);
1249 		if (ACPI_FAILURE(status))
1250 			return;
1251 
1252 		val = ((val_ec & THINKPAD_EC_MASK_MUTE) == THINKPAD_EC_MASK_MUTE);
1253 		acpi_thinkpad_mute_set(sc, (val == 0));
1254 		break;
1255 
1256 	default:
1257 		break;
1258 	}
1259 	ACPI_SERIAL_END(thinkpad);
1260 }
1261 
1262 static void
1263 acpi_thinkpad_notify(ACPI_HANDLE h, UINT32 notify, void *context)
1264 {
1265 	int		event, arg, type;
1266 	device_t	dev = context;
1267 	struct acpi_thinkpad_softc *sc = device_get_softc(dev);
1268 
1269 	ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, notify);
1270 
1271 	if (notify != 0x80)
1272 		device_printf(dev, "Unknown notify\n");
1273 
1274 	for (;;) {
1275 		acpi_GetInteger(acpi_get_handle(dev), THINKPAD_NAME_EVENTS_GET,
1276 		    &event);
1277 
1278 		if (event == 0)
1279 			break;
1280 
1281 		type = (event >> 12) & 0xf;
1282 		arg = event & 0xfff;
1283 		switch (type) {
1284 		case 1:
1285 			if (!(sc->events_availmask & (1 << (arg - 1)))) {
1286 				device_printf(dev, "Unknown key %d\n", arg);
1287 				break;
1288 			}
1289 
1290 			/* Execute event handler */
1291 			if (sc->handler_events & (1 << (arg - 1)))
1292 				acpi_thinkpad_eventhandler(sc, (arg & 0xff));
1293 
1294 			/* Notify devd(8) */
1295 			acpi_UserNotify("THINKPAD", h, (arg & 0xff));
1296 			break;
1297 		default:
1298 			break;
1299 		}
1300 	}
1301 }
1302 
1303 static void
1304 acpi_thinkpad_refresh(void *arg)
1305 {
1306 	struct acpi_thinkpad_softc *sc = (struct acpi_thinkpad_softc *)arg;
1307 	int i, data;
1308 
1309 	for (i = 0; i < THINKPAD_TEMP_SENSORS; i++) {
1310 		char temp_cmd[] = "TMP0";
1311 
1312 		temp_cmd[3] = '0' + i;
1313 		/*
1314 		 * The TMPx methods seem to return +/- 128 or 0
1315 		 * when the respecting sensor is not available
1316 		 */
1317 		if (ACPI_FAILURE(acpi_GetInteger(sc->ec_handle, temp_cmd,
1318 		    &data)) || ABS(data) == 128 || data == 0) {
1319 			sc->sensors[i].flags |= SENSOR_FINVALID;
1320 			continue;
1321 		}
1322 		if (sc->thermal_updt_supported)
1323 			/* Temperature is reported in tenth of Kelvin */
1324 			sc->sensors[i].value = data * 100000 - 50000;
1325 		else
1326 			sc->sensors[i].value = data * 1000000 + 273150000;
1327 		sc->sensors[i].flags &= ~SENSOR_FINVALID;
1328 	}
1329 
1330 	if (sc->fan_handle) {
1331 		if (ACPI_FAILURE(acpi_GetInteger(sc->fan_handle,
1332 		    NULL, &data)))
1333 			sc->sensors[i].flags |= SENSOR_FINVALID;
1334 		sc->sensors[i].value = data;
1335 		sc->sensors[i].flags &= ~SENSOR_FINVALID;
1336 	} else {
1337 		UINT64 speed;
1338 
1339 		ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_FANSPEED, &speed, 2);
1340 		sc->sensors[i].value = speed;
1341 		sc->sensors[i].flags &= ~SENSOR_FINVALID;
1342 	}
1343 }
1344