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