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