xref: /freebsd-src/sys/dev/acpi_support/acpi_ibm.c (revision c21f5751ef0932796676e55953461e0679020e28)
106064893STakanori Watanabe /*-
206064893STakanori Watanabe  * Copyright (c) 2004 Takanori Watanabe
3a4bfd638SMarkus Brueffer  * Copyright (c) 2005 Markus Brueffer <markus@FreeBSD.org>
406064893STakanori Watanabe  * All rights reserved.
59f763f00SMark Johnston  * Copyright (c) 2020 Ali Abdallah <ali.abdallah@suse.com>
606064893STakanori Watanabe  *
706064893STakanori Watanabe  * Redistribution and use in source and binary forms, with or without
806064893STakanori Watanabe  * modification, are permitted provided that the following conditions
906064893STakanori Watanabe  * are met:
1006064893STakanori Watanabe  * 1. Redistributions of source code must retain the above copyright
1106064893STakanori Watanabe  *    notice, this list of conditions and the following disclaimer.
1206064893STakanori Watanabe  * 2. Redistributions in binary form must reproduce the above copyright
1306064893STakanori Watanabe  *    notice, this list of conditions and the following disclaimer in the
1406064893STakanori Watanabe  *    documentation and/or other materials provided with the distribution.
1506064893STakanori Watanabe  *
1606064893STakanori Watanabe  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1706064893STakanori Watanabe  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1806064893STakanori Watanabe  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1906064893STakanori Watanabe  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2006064893STakanori Watanabe  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2106064893STakanori Watanabe  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2206064893STakanori Watanabe  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2306064893STakanori Watanabe  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2406064893STakanori Watanabe  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2506064893STakanori Watanabe  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2606064893STakanori Watanabe  * SUCH DAMAGE.
2706064893STakanori Watanabe  */
2806064893STakanori Watanabe 
29dad97feeSDavid E. O'Brien #include <sys/cdefs.h>
30a4bfd638SMarkus Brueffer /*
318c5cb4ceSEd Maste  * Driver for extra ACPI-controlled gadgets found on ThinkPad laptops.
32a4bfd638SMarkus Brueffer  * Inspired by the ibm-acpi and tpb projects which implement these features
33a4bfd638SMarkus Brueffer  * on Linux.
34a4bfd638SMarkus Brueffer  *
35a4bfd638SMarkus Brueffer  *   acpi-ibm: <http://ibm-acpi.sourceforge.net/>
36a4bfd638SMarkus Brueffer  *        tpb: <http://www.nongnu.org/tpb/>
37a4bfd638SMarkus Brueffer  */
38a4bfd638SMarkus Brueffer 
3906064893STakanori Watanabe #include "opt_acpi.h"
40*c21f5751SGleb Smirnoff #include "opt_evdev.h"
4106064893STakanori Watanabe #include <sys/param.h>
4278fd5381SRui Paulo #include <sys/systm.h>
4306064893STakanori Watanabe #include <sys/kernel.h>
4406064893STakanori Watanabe #include <sys/bus.h>
4506064893STakanori Watanabe #include <machine/cpufunc.h>
46129d3046SJung-uk Kim 
47129d3046SJung-uk Kim #include <contrib/dev/acpica/include/acpi.h>
48129d3046SJung-uk Kim #include <contrib/dev/acpica/include/accommon.h>
49129d3046SJung-uk Kim 
5006064893STakanori Watanabe #include "acpi_if.h"
5106064893STakanori Watanabe #include <sys/module.h>
5206064893STakanori Watanabe #include <dev/acpica/acpivar.h>
53a4bfd638SMarkus Brueffer #include <dev/led/led.h>
54ae2f080bSMitsuru IWASAKI #include <sys/power.h>
55ae2f080bSMitsuru IWASAKI #include <sys/sbuf.h>
5606064893STakanori Watanabe #include <sys/sysctl.h>
5736bff1ebSPoul-Henning Kamp #include <isa/rtc.h>
5806064893STakanori Watanabe 
59*c21f5751SGleb Smirnoff #ifdef EVDEV_SUPPORT
60*c21f5751SGleb Smirnoff #include <dev/evdev/input.h>
61*c21f5751SGleb Smirnoff #include <dev/evdev/evdev.h>
62*c21f5751SGleb Smirnoff #endif
63*c21f5751SGleb Smirnoff 
64276cd921SNate Lawson #define _COMPONENT	ACPI_OEM
6582d4da0fSScott Long ACPI_MODULE_NAME("IBM")
6682d4da0fSScott Long 
67a4bfd638SMarkus Brueffer /* Internal methods */
68a4bfd638SMarkus Brueffer #define ACPI_IBM_METHOD_EVENTS		1
69a4bfd638SMarkus Brueffer #define ACPI_IBM_METHOD_EVENTMASK	2
70a4bfd638SMarkus Brueffer #define ACPI_IBM_METHOD_HOTKEY		3
71a4bfd638SMarkus Brueffer #define ACPI_IBM_METHOD_BRIGHTNESS	4
72a4bfd638SMarkus Brueffer #define ACPI_IBM_METHOD_VOLUME		5
73a4bfd638SMarkus Brueffer #define ACPI_IBM_METHOD_MUTE		6
74a4bfd638SMarkus Brueffer #define ACPI_IBM_METHOD_THINKLIGHT	7
75a4bfd638SMarkus Brueffer #define ACPI_IBM_METHOD_BLUETOOTH	8
76a4bfd638SMarkus Brueffer #define ACPI_IBM_METHOD_WLAN		9
77a4bfd638SMarkus Brueffer #define ACPI_IBM_METHOD_FANSPEED	10
78f9312099SMarkus Brueffer #define ACPI_IBM_METHOD_FANLEVEL	11
79f9312099SMarkus Brueffer #define ACPI_IBM_METHOD_FANSTATUS	12
80f9312099SMarkus Brueffer #define ACPI_IBM_METHOD_THERMAL		13
81ae2f080bSMitsuru IWASAKI #define ACPI_IBM_METHOD_HANDLEREVENTS	14
829cd818a3SEitan Adler #define ACPI_IBM_METHOD_MIC_LED		15
8302aeba83SPhilip Paeps #define ACPI_IBM_METHOD_PRIVACYGUARD	16
8406064893STakanori Watanabe 
85a4bfd638SMarkus Brueffer /* Hotkeys/Buttons */
86a4bfd638SMarkus Brueffer #define IBM_RTC_HOTKEY1			0x64
87a4bfd638SMarkus Brueffer #define   IBM_RTC_MASK_HOME		(1 << 0)
88a4bfd638SMarkus Brueffer #define   IBM_RTC_MASK_SEARCH		(1 << 1)
89a4bfd638SMarkus Brueffer #define   IBM_RTC_MASK_MAIL		(1 << 2)
90a4bfd638SMarkus Brueffer #define   IBM_RTC_MASK_WLAN		(1 << 5)
91a4bfd638SMarkus Brueffer #define IBM_RTC_HOTKEY2			0x65
92a4bfd638SMarkus Brueffer #define   IBM_RTC_MASK_THINKPAD		(1 << 3)
93a4bfd638SMarkus Brueffer #define   IBM_RTC_MASK_ZOOM		(1 << 5)
94a4bfd638SMarkus Brueffer #define   IBM_RTC_MASK_VIDEO		(1 << 6)
95a4bfd638SMarkus Brueffer #define   IBM_RTC_MASK_HIBERNATE	(1 << 7)
96a4bfd638SMarkus Brueffer #define IBM_RTC_THINKLIGHT		0x66
97a4bfd638SMarkus Brueffer #define   IBM_RTC_MASK_THINKLIGHT	(1 << 4)
98a4bfd638SMarkus Brueffer #define IBM_RTC_SCREENEXPAND		0x67
99a4bfd638SMarkus Brueffer #define   IBM_RTC_MASK_SCREENEXPAND	(1 << 5)
100a4bfd638SMarkus Brueffer #define IBM_RTC_BRIGHTNESS		0x6c
101a4bfd638SMarkus Brueffer #define   IBM_RTC_MASK_BRIGHTNESS	(1 << 5)
102a4bfd638SMarkus Brueffer #define IBM_RTC_VOLUME			0x6e
103a4bfd638SMarkus Brueffer #define   IBM_RTC_MASK_VOLUME		(1 << 7)
104a4bfd638SMarkus Brueffer 
105a4bfd638SMarkus Brueffer /* Embedded Controller registers */
106a4bfd638SMarkus Brueffer #define IBM_EC_BRIGHTNESS		0x31
107a4bfd638SMarkus Brueffer #define   IBM_EC_MASK_BRI		0x7
108a4bfd638SMarkus Brueffer #define IBM_EC_VOLUME			0x30
109a4bfd638SMarkus Brueffer #define   IBM_EC_MASK_VOL		0xf
110a4bfd638SMarkus Brueffer #define   IBM_EC_MASK_MUTE		(1 << 6)
111a4bfd638SMarkus Brueffer #define IBM_EC_FANSTATUS		0x2F
112f9312099SMarkus Brueffer #define   IBM_EC_MASK_FANLEVEL		0x3f
113ddf18432SMark Johnston #define   IBM_EC_MASK_FANUNTHROTTLED	(1 << 6)
114a4bfd638SMarkus Brueffer #define   IBM_EC_MASK_FANSTATUS		(1 << 7)
115a4bfd638SMarkus Brueffer #define IBM_EC_FANSPEED			0x84
116a4bfd638SMarkus Brueffer 
117a4bfd638SMarkus Brueffer /* CMOS Commands */
118a4bfd638SMarkus Brueffer #define IBM_CMOS_VOLUME_DOWN		0
119a4bfd638SMarkus Brueffer #define IBM_CMOS_VOLUME_UP		1
120a4bfd638SMarkus Brueffer #define IBM_CMOS_VOLUME_MUTE		2
121a4bfd638SMarkus Brueffer #define IBM_CMOS_BRIGHTNESS_UP		4
122a4bfd638SMarkus Brueffer #define IBM_CMOS_BRIGHTNESS_DOWN	5
123a4bfd638SMarkus Brueffer 
124a4bfd638SMarkus Brueffer /* ACPI methods */
125a4bfd638SMarkus Brueffer #define IBM_NAME_KEYLIGHT		"KBLT"
126a4bfd638SMarkus Brueffer #define IBM_NAME_WLAN_BT_GET		"GBDC"
127a4bfd638SMarkus Brueffer #define IBM_NAME_WLAN_BT_SET		"SBDC"
128a4bfd638SMarkus Brueffer #define   IBM_NAME_MASK_BT		(1 << 1)
129a4bfd638SMarkus Brueffer #define   IBM_NAME_MASK_WLAN		(1 << 2)
130a4bfd638SMarkus Brueffer #define IBM_NAME_THERMAL_GET		"TMP7"
131a4bfd638SMarkus Brueffer #define IBM_NAME_THERMAL_UPDT		"UPDT"
13202aeba83SPhilip Paeps #define IBM_NAME_PRIVACYGUARD_GET	"GSSS"
13302aeba83SPhilip Paeps #define IBM_NAME_PRIVACYGUARD_SET	"SSSS"
134a4bfd638SMarkus Brueffer 
135a4bfd638SMarkus Brueffer #define IBM_NAME_EVENTS_STATUS_GET	"DHKC"
136a4bfd638SMarkus Brueffer #define IBM_NAME_EVENTS_MASK_GET	"DHKN"
137a4bfd638SMarkus Brueffer #define IBM_NAME_EVENTS_STATUS_SET	"MHKC"
138a4bfd638SMarkus Brueffer #define IBM_NAME_EVENTS_MASK_SET	"MHKM"
139a4bfd638SMarkus Brueffer #define IBM_NAME_EVENTS_GET		"MHKP"
140a4bfd638SMarkus Brueffer #define IBM_NAME_EVENTS_AVAILMASK	"MHKA"
141a4bfd638SMarkus Brueffer 
142ae2f080bSMitsuru IWASAKI /* Event Code */
143ae2f080bSMitsuru IWASAKI #define IBM_EVENT_LCD_BACKLIGHT		0x03
144ae2f080bSMitsuru IWASAKI #define IBM_EVENT_SUSPEND_TO_RAM	0x04
145ae2f080bSMitsuru IWASAKI #define IBM_EVENT_BLUETOOTH		0x05
146ae2f080bSMitsuru IWASAKI #define IBM_EVENT_SCREEN_EXPAND		0x07
147ae2f080bSMitsuru IWASAKI #define IBM_EVENT_SUSPEND_TO_DISK	0x0c
148ae2f080bSMitsuru IWASAKI #define IBM_EVENT_BRIGHTNESS_UP		0x10
149ae2f080bSMitsuru IWASAKI #define IBM_EVENT_BRIGHTNESS_DOWN	0x11
150ae2f080bSMitsuru IWASAKI #define IBM_EVENT_THINKLIGHT		0x12
151ae2f080bSMitsuru IWASAKI #define IBM_EVENT_ZOOM			0x14
152ae2f080bSMitsuru IWASAKI #define IBM_EVENT_VOLUME_UP		0x15
153ae2f080bSMitsuru IWASAKI #define IBM_EVENT_VOLUME_DOWN		0x16
154ae2f080bSMitsuru IWASAKI #define IBM_EVENT_MUTE			0x17
155ae2f080bSMitsuru IWASAKI #define IBM_EVENT_ACCESS_IBM_BUTTON	0x18
156ae2f080bSMitsuru IWASAKI 
15702aeba83SPhilip Paeps /* Device-specific register flags */
15802aeba83SPhilip Paeps #define IBM_FLAG_PRIVACYGUARD_DEVICE_PRESENT	0x10000
15902aeba83SPhilip Paeps #define IBM_FLAG_PRIVACYGUARD_ON	0x1
16002aeba83SPhilip Paeps 
161a4bfd638SMarkus Brueffer #define ABS(x) (((x) < 0)? -(x) : (x))
16206064893STakanori Watanabe 
16306064893STakanori Watanabe struct acpi_ibm_softc {
164a4bfd638SMarkus Brueffer 	device_t	dev;
165a4bfd638SMarkus Brueffer 	ACPI_HANDLE	handle;
166a4bfd638SMarkus Brueffer 
167a4bfd638SMarkus Brueffer 	/* Embedded controller */
168a4bfd638SMarkus Brueffer 	device_t	ec_dev;
169a4bfd638SMarkus Brueffer 	ACPI_HANDLE	ec_handle;
170a4bfd638SMarkus Brueffer 
171a4bfd638SMarkus Brueffer 	/* CMOS */
172a4bfd638SMarkus Brueffer 	ACPI_HANDLE	cmos_handle;
173a4bfd638SMarkus Brueffer 
174a4bfd638SMarkus Brueffer 	/* Fan status */
175a4bfd638SMarkus Brueffer 	ACPI_HANDLE	fan_handle;
176a4bfd638SMarkus Brueffer 	int		fan_levels;
177a4bfd638SMarkus Brueffer 
178a4bfd638SMarkus Brueffer 	/* Keylight commands and states */
179a4bfd638SMarkus Brueffer 	ACPI_HANDLE	light_handle;
180a4bfd638SMarkus Brueffer 	int		light_cmd_on;
181a4bfd638SMarkus Brueffer 	int		light_cmd_off;
182a4bfd638SMarkus Brueffer 	int		light_val;
183a4bfd638SMarkus Brueffer 	int		light_get_supported;
184a4bfd638SMarkus Brueffer 	int		light_set_supported;
1850873506fSMarkus Brueffer 
1860873506fSMarkus Brueffer 	/* led(4) interface */
187a4bfd638SMarkus Brueffer 	struct cdev	*led_dev;
1880873506fSMarkus Brueffer 	int		led_busy;
1890873506fSMarkus Brueffer 	int		led_state;
190a4bfd638SMarkus Brueffer 
1919cd818a3SEitan Adler 	/* Mic led handle */
1929cd818a3SEitan Adler 	ACPI_HANDLE	mic_led_handle;
1939cd818a3SEitan Adler 	int		mic_led_state;
1949cd818a3SEitan Adler 
195a4bfd638SMarkus Brueffer 	int		wlan_bt_flags;
196a4bfd638SMarkus Brueffer 	int		thermal_updt_supported;
197a4bfd638SMarkus Brueffer 
198a4bfd638SMarkus Brueffer 	unsigned int	events_availmask;
199a4bfd638SMarkus Brueffer 	unsigned int	events_initialmask;
200a4bfd638SMarkus Brueffer 	int		events_mask_supported;
201a4bfd638SMarkus Brueffer 	int		events_enable;
202a4bfd638SMarkus Brueffer 
203ae2f080bSMitsuru IWASAKI 	unsigned int	handler_events;
204ae2f080bSMitsuru IWASAKI 
205a4bfd638SMarkus Brueffer 	struct sysctl_ctx_list	*sysctl_ctx;
206a4bfd638SMarkus Brueffer 	struct sysctl_oid	*sysctl_tree;
207*c21f5751SGleb Smirnoff #ifdef EVDEV_SUPPORT
208*c21f5751SGleb Smirnoff 	struct evdev_dev	*evdev;
209*c21f5751SGleb Smirnoff #endif
21006064893STakanori Watanabe };
21106064893STakanori Watanabe 
212a4bfd638SMarkus Brueffer static struct {
213a4bfd638SMarkus Brueffer 	char	*name;
214a4bfd638SMarkus Brueffer 	int	method;
215a4bfd638SMarkus Brueffer 	char	*description;
216f0188618SHans Petter Selasky 	int	flag_rdonly;
217a4bfd638SMarkus Brueffer } acpi_ibm_sysctls[] = {
218a4bfd638SMarkus Brueffer 	{
219a4bfd638SMarkus Brueffer 		.name		= "events",
220a4bfd638SMarkus Brueffer 		.method		= ACPI_IBM_METHOD_EVENTS,
221a4bfd638SMarkus Brueffer 		.description	= "ACPI events enable",
222a4bfd638SMarkus Brueffer 	},
223a4bfd638SMarkus Brueffer 	{
224a4bfd638SMarkus Brueffer 		.name		= "eventmask",
225a4bfd638SMarkus Brueffer 		.method		= ACPI_IBM_METHOD_EVENTMASK,
226a4bfd638SMarkus Brueffer 		.description	= "ACPI eventmask",
227a4bfd638SMarkus Brueffer 	},
228a4bfd638SMarkus Brueffer 	{
229a4bfd638SMarkus Brueffer 		.name		= "hotkey",
230a4bfd638SMarkus Brueffer 		.method		= ACPI_IBM_METHOD_HOTKEY,
231a4bfd638SMarkus Brueffer 		.description	= "Key Status",
232f0188618SHans Petter Selasky 		.flag_rdonly	= 1
233a4bfd638SMarkus Brueffer 	},
234a4bfd638SMarkus Brueffer 	{
235a4bfd638SMarkus Brueffer 		.name		= "lcd_brightness",
236a4bfd638SMarkus Brueffer 		.method		= ACPI_IBM_METHOD_BRIGHTNESS,
237a4bfd638SMarkus Brueffer 		.description	= "LCD Brightness",
238a4bfd638SMarkus Brueffer 	},
239a4bfd638SMarkus Brueffer 	{
240a4bfd638SMarkus Brueffer 		.name		= "volume",
241a4bfd638SMarkus Brueffer 		.method		= ACPI_IBM_METHOD_VOLUME,
242a4bfd638SMarkus Brueffer 		.description	= "Volume",
243a4bfd638SMarkus Brueffer 	},
244a4bfd638SMarkus Brueffer 	{
245a4bfd638SMarkus Brueffer 		.name		= "mute",
246a4bfd638SMarkus Brueffer 		.method		= ACPI_IBM_METHOD_MUTE,
247a4bfd638SMarkus Brueffer 		.description	= "Mute",
248a4bfd638SMarkus Brueffer 	},
249a4bfd638SMarkus Brueffer 	{
250a4bfd638SMarkus Brueffer 		.name		= "thinklight",
251a4bfd638SMarkus Brueffer 		.method		= ACPI_IBM_METHOD_THINKLIGHT,
252a4bfd638SMarkus Brueffer 		.description	= "Thinklight enable",
253a4bfd638SMarkus Brueffer 	},
254a4bfd638SMarkus Brueffer 	{
255a4bfd638SMarkus Brueffer 		.name		= "bluetooth",
256a4bfd638SMarkus Brueffer 		.method		= ACPI_IBM_METHOD_BLUETOOTH,
257a4bfd638SMarkus Brueffer 		.description	= "Bluetooth enable",
258a4bfd638SMarkus Brueffer 	},
259a4bfd638SMarkus Brueffer 	{
260a4bfd638SMarkus Brueffer 		.name		= "wlan",
261a4bfd638SMarkus Brueffer 		.method		= ACPI_IBM_METHOD_WLAN,
262a4bfd638SMarkus Brueffer 		.description	= "WLAN enable",
263f0188618SHans Petter Selasky 		.flag_rdonly	= 1
264a4bfd638SMarkus Brueffer 	},
265a4bfd638SMarkus Brueffer 	{
266a4bfd638SMarkus Brueffer 		.name		= "fan_speed",
267a4bfd638SMarkus Brueffer 		.method		= ACPI_IBM_METHOD_FANSPEED,
268a4bfd638SMarkus Brueffer 		.description	= "Fan speed",
269f0188618SHans Petter Selasky 		.flag_rdonly	= 1
270a4bfd638SMarkus Brueffer 	},
271a4bfd638SMarkus Brueffer 	{
272f9312099SMarkus Brueffer 		.name		= "fan_level",
273f9312099SMarkus Brueffer 		.method		= ACPI_IBM_METHOD_FANLEVEL,
2749f763f00SMark Johnston 		.description	= "Fan level, 0-7 (recommended max), "
275ddf18432SMark Johnston 				  "8 (unthrottled, full-speed)",
276f9312099SMarkus Brueffer 	},
277f9312099SMarkus Brueffer 	{
278a4bfd638SMarkus Brueffer 		.name		= "fan",
279a4bfd638SMarkus Brueffer 		.method		= ACPI_IBM_METHOD_FANSTATUS,
280a4bfd638SMarkus Brueffer 		.description	= "Fan enable",
281a4bfd638SMarkus Brueffer 	},
2829cd818a3SEitan Adler 	{
2839cd818a3SEitan Adler 		.name		= "mic_led",
2849cd818a3SEitan Adler 		.method		= ACPI_IBM_METHOD_MIC_LED,
2859cd818a3SEitan Adler 		.description	= "Mic led",
2869cd818a3SEitan Adler 	},
28702aeba83SPhilip Paeps 	{
28802aeba83SPhilip Paeps 		.name		= "privacyguard",
28902aeba83SPhilip Paeps 		.method		= ACPI_IBM_METHOD_PRIVACYGUARD,
29002aeba83SPhilip Paeps 		.description	= "PrivacyGuard enable",
29102aeba83SPhilip Paeps 	},
292a4bfd638SMarkus Brueffer 	{ NULL, 0, NULL, 0 }
293a4bfd638SMarkus Brueffer };
294a4bfd638SMarkus Brueffer 
29578fd5381SRui Paulo /*
29678fd5381SRui Paulo  * Per-model default list of event mask.
29778fd5381SRui Paulo  */
29878fd5381SRui Paulo #define	ACPI_IBM_HKEY_RFKILL_MASK		(1 << 4)
29978fd5381SRui Paulo #define	ACPI_IBM_HKEY_DSWITCH_MASK		(1 << 6)
30078fd5381SRui Paulo #define	ACPI_IBM_HKEY_BRIGHTNESS_UP_MASK	(1 << 15)
30178fd5381SRui Paulo #define	ACPI_IBM_HKEY_BRIGHTNESS_DOWN_MASK	(1 << 16)
30278fd5381SRui Paulo #define	ACPI_IBM_HKEY_SEARCH_MASK		(1 << 18)
30378fd5381SRui Paulo #define	ACPI_IBM_HKEY_MICMUTE_MASK		(1 << 26)
30478fd5381SRui Paulo #define	ACPI_IBM_HKEY_SETTINGS_MASK		(1 << 28)
30578fd5381SRui Paulo #define	ACPI_IBM_HKEY_VIEWOPEN_MASK		(1 << 30)
30678fd5381SRui Paulo #define	ACPI_IBM_HKEY_VIEWALL_MASK		(1 << 31)
30778fd5381SRui Paulo 
30878fd5381SRui Paulo struct acpi_ibm_models {
30978fd5381SRui Paulo 	const char *maker;
31078fd5381SRui Paulo 	const char *product;
31178fd5381SRui Paulo 	uint32_t eventmask;
31278fd5381SRui Paulo } acpi_ibm_models[] = {
31378fd5381SRui Paulo 	{ "LENOVO", "20BSCTO1WW",
31478fd5381SRui Paulo 	  ACPI_IBM_HKEY_RFKILL_MASK |
31578fd5381SRui Paulo 	  ACPI_IBM_HKEY_DSWITCH_MASK |
31678fd5381SRui Paulo 	  ACPI_IBM_HKEY_BRIGHTNESS_UP_MASK |
31778fd5381SRui Paulo 	  ACPI_IBM_HKEY_BRIGHTNESS_DOWN_MASK |
31878fd5381SRui Paulo 	  ACPI_IBM_HKEY_SEARCH_MASK |
31978fd5381SRui Paulo 	  ACPI_IBM_HKEY_MICMUTE_MASK |
32078fd5381SRui Paulo 	  ACPI_IBM_HKEY_SETTINGS_MASK |
32178fd5381SRui Paulo 	  ACPI_IBM_HKEY_VIEWOPEN_MASK |
32278fd5381SRui Paulo 	  ACPI_IBM_HKEY_VIEWALL_MASK
32378fd5381SRui Paulo 	}
32478fd5381SRui Paulo };
32578fd5381SRui Paulo 
3268c5cb4ceSEd Maste ACPI_SERIAL_DECL(ibm, "ThinkPad ACPI Extras");
327a4bfd638SMarkus Brueffer 
32806064893STakanori Watanabe static int	acpi_ibm_probe(device_t dev);
32906064893STakanori Watanabe static int	acpi_ibm_attach(device_t dev);
33006064893STakanori Watanabe static int	acpi_ibm_detach(device_t dev);
331a50f74ccSMitsuru IWASAKI static int	acpi_ibm_resume(device_t dev);
332a4bfd638SMarkus Brueffer 
3330873506fSMarkus Brueffer static void	ibm_led(void *softc, int onoff);
3340873506fSMarkus Brueffer static void	ibm_led_task(struct acpi_ibm_softc *sc, int pending __unused);
3350873506fSMarkus Brueffer 
336a4bfd638SMarkus Brueffer static int	acpi_ibm_sysctl(SYSCTL_HANDLER_ARGS);
337a4bfd638SMarkus Brueffer static int	acpi_ibm_sysctl_init(struct acpi_ibm_softc *sc, int method);
338a4bfd638SMarkus Brueffer static int	acpi_ibm_sysctl_get(struct acpi_ibm_softc *sc, int method);
339a4bfd638SMarkus Brueffer static int	acpi_ibm_sysctl_set(struct acpi_ibm_softc *sc, int method, int val);
340a4bfd638SMarkus Brueffer 
341a4bfd638SMarkus Brueffer static int	acpi_ibm_eventmask_set(struct acpi_ibm_softc *sc, int val);
342a4bfd638SMarkus Brueffer static int	acpi_ibm_thermal_sysctl(SYSCTL_HANDLER_ARGS);
343ae2f080bSMitsuru IWASAKI static int	acpi_ibm_handlerevents_sysctl(SYSCTL_HANDLER_ARGS);
344a4bfd638SMarkus Brueffer static void	acpi_ibm_notify(ACPI_HANDLE h, UINT32 notify, void *context);
34506064893STakanori Watanabe 
346ae2f080bSMitsuru IWASAKI static int	acpi_ibm_brightness_set(struct acpi_ibm_softc *sc, int arg);
347ae2f080bSMitsuru IWASAKI static int	acpi_ibm_bluetooth_set(struct acpi_ibm_softc *sc, int arg);
348ae2f080bSMitsuru IWASAKI static int	acpi_ibm_thinklight_set(struct acpi_ibm_softc *sc, int arg);
349ae2f080bSMitsuru IWASAKI static int	acpi_ibm_volume_set(struct acpi_ibm_softc *sc, int arg);
350ae2f080bSMitsuru IWASAKI static int	acpi_ibm_mute_set(struct acpi_ibm_softc *sc, int arg);
35102aeba83SPhilip Paeps static int	acpi_ibm_privacyguard_get(struct acpi_ibm_softc *sc);
35202aeba83SPhilip Paeps static ACPI_STATUS	acpi_ibm_privacyguard_set(struct acpi_ibm_softc *sc, int arg);
35302aeba83SPhilip Paeps static ACPI_STATUS	acpi_ibm_privacyguard_acpi_call(struct acpi_ibm_softc *sc, bool write, int *arg);
35402aeba83SPhilip Paeps 
35502aeba83SPhilip Paeps static int	acpi_status_to_errno(ACPI_STATUS status);
356ae2f080bSMitsuru IWASAKI 
35706064893STakanori Watanabe static device_method_t acpi_ibm_methods[] = {
35806064893STakanori Watanabe 	/* Device interface */
35906064893STakanori Watanabe 	DEVMETHOD(device_probe, acpi_ibm_probe),
36006064893STakanori Watanabe 	DEVMETHOD(device_attach, acpi_ibm_attach),
36106064893STakanori Watanabe 	DEVMETHOD(device_detach, acpi_ibm_detach),
362a50f74ccSMitsuru IWASAKI 	DEVMETHOD(device_resume, acpi_ibm_resume),
36306064893STakanori Watanabe 
36461bfd867SSofian Brabez 	DEVMETHOD_END
36506064893STakanori Watanabe };
36606064893STakanori Watanabe 
36706064893STakanori Watanabe static driver_t	acpi_ibm_driver = {
36806064893STakanori Watanabe 	"acpi_ibm",
36906064893STakanori Watanabe 	acpi_ibm_methods,
37006064893STakanori Watanabe 	sizeof(struct acpi_ibm_softc),
37106064893STakanori Watanabe };
37206064893STakanori Watanabe 
37390161e72SJohn Baldwin DRIVER_MODULE(acpi_ibm, acpi, acpi_ibm_driver, 0, 0);
37406064893STakanori Watanabe MODULE_DEPEND(acpi_ibm, acpi, 1, 1, 1);
375*c21f5751SGleb Smirnoff #ifdef EVDEV_SUPPORT
376*c21f5751SGleb Smirnoff MODULE_DEPEND(acpi_ibm, evdev, 1, 1, 1);
377*c21f5751SGleb Smirnoff #endif
378cc1ac7fcSOleksandr Tymoshenko static char    *ibm_ids[] = {"IBM0068", "LEN0068", "LEN0268", NULL};
379a4bfd638SMarkus Brueffer 
38002aeba83SPhilip Paeps static int
38102aeba83SPhilip Paeps acpi_status_to_errno(ACPI_STATUS status)
38202aeba83SPhilip Paeps {
38302aeba83SPhilip Paeps 	switch (status) {
38402aeba83SPhilip Paeps 	case AE_OK:
38502aeba83SPhilip Paeps 		return (0);
38602aeba83SPhilip Paeps 	case AE_BAD_PARAMETER:
38702aeba83SPhilip Paeps 		return (EINVAL);
38802aeba83SPhilip Paeps 	default:
38902aeba83SPhilip Paeps 		return (ENODEV);
39002aeba83SPhilip Paeps 	}
39102aeba83SPhilip Paeps }
39202aeba83SPhilip Paeps 
393a4bfd638SMarkus Brueffer static void
394a4bfd638SMarkus Brueffer ibm_led(void *softc, int onoff)
395a4bfd638SMarkus Brueffer {
3962927ab03SEd Maste 	struct acpi_ibm_softc *sc = softc;
3970873506fSMarkus Brueffer 
3980873506fSMarkus Brueffer 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
3990873506fSMarkus Brueffer 
4000873506fSMarkus Brueffer 	if (sc->led_busy)
4010873506fSMarkus Brueffer 		return;
4020873506fSMarkus Brueffer 
4030873506fSMarkus Brueffer 	sc->led_busy = 1;
4040873506fSMarkus Brueffer 	sc->led_state = onoff;
4050873506fSMarkus Brueffer 
4062be4e471SJung-uk Kim 	AcpiOsExecute(OSL_NOTIFY_HANDLER, (void *)ibm_led_task, sc);
4070873506fSMarkus Brueffer }
4080873506fSMarkus Brueffer 
4090873506fSMarkus Brueffer static void
4100873506fSMarkus Brueffer ibm_led_task(struct acpi_ibm_softc *sc, int pending __unused)
4110873506fSMarkus Brueffer {
4120873506fSMarkus Brueffer 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
4130873506fSMarkus Brueffer 
414a4bfd638SMarkus Brueffer 	ACPI_SERIAL_BEGIN(ibm);
4150873506fSMarkus Brueffer 	acpi_ibm_sysctl_set(sc, ACPI_IBM_METHOD_THINKLIGHT, sc->led_state);
416a4bfd638SMarkus Brueffer 	ACPI_SERIAL_END(ibm);
4170873506fSMarkus Brueffer 
4180873506fSMarkus Brueffer 	sc->led_busy = 0;
419a4bfd638SMarkus Brueffer }
42006064893STakanori Watanabe 
42106064893STakanori Watanabe static int
4229cd818a3SEitan Adler acpi_ibm_mic_led_set(struct acpi_ibm_softc *sc, int arg)
4239cd818a3SEitan Adler {
4249cd818a3SEitan Adler 	ACPI_OBJECT_LIST input;
4259cd818a3SEitan Adler 	ACPI_OBJECT params[1];
4269cd818a3SEitan Adler 	ACPI_STATUS status;
4279cd818a3SEitan Adler 
4289cd818a3SEitan Adler 	if (arg < 0 || arg > 1)
4299cd818a3SEitan Adler 		return (EINVAL);
4309cd818a3SEitan Adler 
4319cd818a3SEitan Adler 	if (sc->mic_led_handle) {
4329cd818a3SEitan Adler 		params[0].Type = ACPI_TYPE_INTEGER;
4339cd818a3SEitan Adler 		params[0].Integer.Value = 0;
4349cd818a3SEitan Adler 		/* mic led: 0 off, 2 on */
4359cd818a3SEitan Adler 		if (arg == 1)
4369cd818a3SEitan Adler 			params[0].Integer.Value = 2;
4379cd818a3SEitan Adler 
4389cd818a3SEitan Adler 		input.Pointer = params;
4399cd818a3SEitan Adler 		input.Count = 1;
4409cd818a3SEitan Adler 
4419cd818a3SEitan Adler 		status = AcpiEvaluateObject(sc->handle, "MMTS", &input, NULL);
4429cd818a3SEitan Adler 		if (ACPI_SUCCESS(status))
4439cd818a3SEitan Adler 			sc->mic_led_state = arg;
4449cd818a3SEitan Adler 		return (status);
4459cd818a3SEitan Adler 	}
4469cd818a3SEitan Adler 
4479cd818a3SEitan Adler 	return (0);
4489cd818a3SEitan Adler }
4499cd818a3SEitan Adler 
4509cd818a3SEitan Adler static int
45106064893STakanori Watanabe acpi_ibm_probe(device_t dev)
45206064893STakanori Watanabe {
4535efca36fSTakanori Watanabe 	int rv;
4545efca36fSTakanori Watanabe 
45566671c14SEd Maste 	if (acpi_disabled("ibm") || device_get_unit(dev) != 0)
456a4bfd638SMarkus Brueffer 		return (ENXIO);
4575efca36fSTakanori Watanabe 	rv = ACPI_ID_PROBE(device_get_parent(dev), dev, ibm_ids, NULL);
45806064893STakanori Watanabe 
4595efca36fSTakanori Watanabe 	if (rv <= 0)
4608c5cb4ceSEd Maste 		device_set_desc(dev, "ThinkPad ACPI Extras");
46106064893STakanori Watanabe 
4625efca36fSTakanori Watanabe 	return (rv);
46306064893STakanori Watanabe }
46406064893STakanori Watanabe 
46506064893STakanori Watanabe static int
46606064893STakanori Watanabe acpi_ibm_attach(device_t dev)
46706064893STakanori Watanabe {
46878fd5381SRui Paulo 	int i;
469cc1ac7fcSOleksandr Tymoshenko 	int hkey;
47006064893STakanori Watanabe 	struct acpi_ibm_softc *sc;
47178fd5381SRui Paulo 	char *maker, *product;
472cc1ac7fcSOleksandr Tymoshenko 	ACPI_OBJECT_LIST input;
473cc1ac7fcSOleksandr Tymoshenko 	ACPI_OBJECT params[1];
474cc1ac7fcSOleksandr Tymoshenko 	ACPI_OBJECT out_obj;
475cc1ac7fcSOleksandr Tymoshenko 	ACPI_BUFFER result;
476a4bfd638SMarkus Brueffer 	devclass_t ec_devclass;
477a4bfd638SMarkus Brueffer 
47806064893STakanori Watanabe 	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
47906064893STakanori Watanabe 
48006064893STakanori Watanabe 	sc = device_get_softc(dev);
481a4bfd638SMarkus Brueffer 	sc->dev = dev;
482a4bfd638SMarkus Brueffer 	sc->handle = acpi_get_handle(dev);
483a4bfd638SMarkus Brueffer 
484a4bfd638SMarkus Brueffer 	/* Look for the first embedded controller */
485a4bfd638SMarkus Brueffer 	if (!(ec_devclass = devclass_find ("acpi_ec"))) {
486a4bfd638SMarkus Brueffer 		if (bootverbose)
487a4bfd638SMarkus Brueffer 			device_printf(dev, "Couldn't find acpi_ec devclass\n");
488a4bfd638SMarkus Brueffer 		return (EINVAL);
48906064893STakanori Watanabe 	}
490a4bfd638SMarkus Brueffer 	if (!(sc->ec_dev = devclass_get_device(ec_devclass, 0))) {
491a4bfd638SMarkus Brueffer 		if (bootverbose)
492a4bfd638SMarkus Brueffer 			device_printf(dev, "Couldn't find acpi_ec device\n");
493a4bfd638SMarkus Brueffer 		return (EINVAL);
49406064893STakanori Watanabe 	}
495a4bfd638SMarkus Brueffer 	sc->ec_handle = acpi_get_handle(sc->ec_dev);
496a4bfd638SMarkus Brueffer 
497*c21f5751SGleb Smirnoff #ifdef EVDEV_SUPPORT
498*c21f5751SGleb Smirnoff 	sc->evdev = evdev_alloc();
499*c21f5751SGleb Smirnoff 	evdev_set_name(sc->evdev, device_get_desc(dev));
500*c21f5751SGleb Smirnoff 	evdev_set_phys(sc->evdev, device_get_nameunit(dev));
501*c21f5751SGleb Smirnoff 	evdev_set_id(sc->evdev, BUS_HOST, 0, 0, 1);
502*c21f5751SGleb Smirnoff 	evdev_support_event(sc->evdev, EV_SYN);
503*c21f5751SGleb Smirnoff 	evdev_support_event(sc->evdev, EV_KEY);
504*c21f5751SGleb Smirnoff 	evdev_support_key(sc->evdev, KEY_BRIGHTNESSUP);
505*c21f5751SGleb Smirnoff 	evdev_support_key(sc->evdev, KEY_BRIGHTNESSDOWN);
506*c21f5751SGleb Smirnoff 
507*c21f5751SGleb Smirnoff 	if (evdev_register(sc->evdev) != 0)
508*c21f5751SGleb Smirnoff 		return (ENXIO);
509*c21f5751SGleb Smirnoff #endif
510*c21f5751SGleb Smirnoff 
511a4bfd638SMarkus Brueffer 	/* Get the sysctl tree */
512a4bfd638SMarkus Brueffer 	sc->sysctl_ctx = device_get_sysctl_ctx(dev);
513a4bfd638SMarkus Brueffer 	sc->sysctl_tree = device_get_sysctl_tree(dev);
514a4bfd638SMarkus Brueffer 
515a4bfd638SMarkus Brueffer 	/* Look for event mask and hook up the nodes */
516a4bfd638SMarkus Brueffer 	sc->events_mask_supported = ACPI_SUCCESS(acpi_GetInteger(sc->handle,
517a4bfd638SMarkus Brueffer 	    IBM_NAME_EVENTS_MASK_GET, &sc->events_initialmask));
518a4bfd638SMarkus Brueffer 
519a4bfd638SMarkus Brueffer 	if (sc->events_mask_supported) {
5206dc7dc9aSMatthew D Fleming 		SYSCTL_ADD_UINT(sc->sysctl_ctx,
52166671c14SEd Maste 		    SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "initialmask",
52266671c14SEd Maste 		    CTLFLAG_RD, &sc->events_initialmask, 0,
52366671c14SEd Maste 		    "Initial eventmask");
524a4bfd638SMarkus Brueffer 
525cc1ac7fcSOleksandr Tymoshenko 		if (ACPI_SUCCESS (acpi_GetInteger(sc->handle, "MHKV", &hkey))) {
526cc1ac7fcSOleksandr Tymoshenko 			device_printf(dev, "Firmware version is 0x%X\n", hkey);
52766671c14SEd Maste 			switch (hkey >> 8) {
528cc1ac7fcSOleksandr Tymoshenko 			case 1:
529a4bfd638SMarkus Brueffer 				/* The availmask is the bitmask of supported events */
530a4bfd638SMarkus Brueffer 				if (ACPI_FAILURE(acpi_GetInteger(sc->handle,
531a4bfd638SMarkus Brueffer 				    IBM_NAME_EVENTS_AVAILMASK, &sc->events_availmask)))
532a4bfd638SMarkus Brueffer 					sc->events_availmask = 0xffffffff;
533cc1ac7fcSOleksandr Tymoshenko 				break;
534cc1ac7fcSOleksandr Tymoshenko 
535cc1ac7fcSOleksandr Tymoshenko 			case 2:
536cc1ac7fcSOleksandr Tymoshenko 				result.Length = sizeof(out_obj);
537cc1ac7fcSOleksandr Tymoshenko 				result.Pointer = &out_obj;
538cc1ac7fcSOleksandr Tymoshenko 				params[0].Type = ACPI_TYPE_INTEGER;
539cc1ac7fcSOleksandr Tymoshenko 				params[0].Integer.Value = 1;
540cc1ac7fcSOleksandr Tymoshenko 				input.Pointer = params;
541cc1ac7fcSOleksandr Tymoshenko 				input.Count = 1;
542cc1ac7fcSOleksandr Tymoshenko 
543cc1ac7fcSOleksandr Tymoshenko 				sc->events_availmask = 0xffffffff;
544cc1ac7fcSOleksandr Tymoshenko 
545cc1ac7fcSOleksandr Tymoshenko 				if (ACPI_SUCCESS(AcpiEvaluateObject (sc->handle,
546cc1ac7fcSOleksandr Tymoshenko 				    IBM_NAME_EVENTS_AVAILMASK, &input, &result)))
547cc1ac7fcSOleksandr Tymoshenko 					sc->events_availmask = out_obj.Integer.Value;
548cc1ac7fcSOleksandr Tymoshenko 				break;
549cc1ac7fcSOleksandr Tymoshenko 			default:
550cc1ac7fcSOleksandr Tymoshenko 				device_printf(dev, "Unknown firmware version 0x%x\n", hkey);
551cc1ac7fcSOleksandr Tymoshenko 				break;
552cc1ac7fcSOleksandr Tymoshenko 			}
553cc1ac7fcSOleksandr Tymoshenko 		} else
554cc1ac7fcSOleksandr Tymoshenko 			sc->events_availmask = 0xffffffff;
555a4bfd638SMarkus Brueffer 
5566dc7dc9aSMatthew D Fleming 		SYSCTL_ADD_UINT(sc->sysctl_ctx,
557a4bfd638SMarkus Brueffer 				SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
558a4bfd638SMarkus Brueffer 				"availmask", CTLFLAG_RD,
559a4bfd638SMarkus Brueffer 				&sc->events_availmask, 0, "Mask of supported events");
560a4bfd638SMarkus Brueffer 	}
561a4bfd638SMarkus Brueffer 
562a4bfd638SMarkus Brueffer 	/* Hook up proc nodes */
563a4bfd638SMarkus Brueffer 	for (int i = 0; acpi_ibm_sysctls[i].name != NULL; i++) {
564a4bfd638SMarkus Brueffer 		if (!acpi_ibm_sysctl_init(sc, acpi_ibm_sysctls[i].method))
565a4bfd638SMarkus Brueffer 			continue;
566a4bfd638SMarkus Brueffer 
567f0188618SHans Petter Selasky 		if (acpi_ibm_sysctls[i].flag_rdonly != 0) {
568a4bfd638SMarkus Brueffer 			SYSCTL_ADD_PROC(sc->sysctl_ctx,
569a4bfd638SMarkus Brueffer 			    SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
5707029da5cSPawel Biernacki 			    acpi_ibm_sysctls[i].name,
5716237a1ccSAlexander Motin 			    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
572a4bfd638SMarkus Brueffer 			    sc, i, acpi_ibm_sysctl, "I",
573a4bfd638SMarkus Brueffer 			    acpi_ibm_sysctls[i].description);
574f0188618SHans Petter Selasky 		} else {
575f0188618SHans Petter Selasky 			SYSCTL_ADD_PROC(sc->sysctl_ctx,
576f0188618SHans Petter Selasky 			    SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
5777029da5cSPawel Biernacki 			    acpi_ibm_sysctls[i].name,
5786237a1ccSAlexander Motin 			    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
579f0188618SHans Petter Selasky 			    sc, i, acpi_ibm_sysctl, "I",
580f0188618SHans Petter Selasky 			    acpi_ibm_sysctls[i].description);
581f0188618SHans Petter Selasky 		}
582a4bfd638SMarkus Brueffer 	}
583a4bfd638SMarkus Brueffer 
584a4bfd638SMarkus Brueffer 	/* Hook up thermal node */
585a4bfd638SMarkus Brueffer 	if (acpi_ibm_sysctl_init(sc, ACPI_IBM_METHOD_THERMAL)) {
586a4bfd638SMarkus Brueffer 		SYSCTL_ADD_PROC(sc->sysctl_ctx,
58766671c14SEd Maste 		    SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "thermal",
5886237a1ccSAlexander Motin 		    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
5897029da5cSPawel Biernacki 		    acpi_ibm_thermal_sysctl, "I", "Thermal zones");
590a4bfd638SMarkus Brueffer 	}
591a4bfd638SMarkus Brueffer 
592ae2f080bSMitsuru IWASAKI 	/* Hook up handlerevents node */
593ae2f080bSMitsuru IWASAKI 	if (acpi_ibm_sysctl_init(sc, ACPI_IBM_METHOD_HANDLEREVENTS)) {
594ae2f080bSMitsuru IWASAKI 		SYSCTL_ADD_PROC(sc->sysctl_ctx,
59566671c14SEd Maste 		    SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "handlerevents",
5966237a1ccSAlexander Motin 		    CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
59766671c14SEd Maste 		    acpi_ibm_handlerevents_sysctl, "I",
598ae2f080bSMitsuru IWASAKI 		    "devd(8) events handled by acpi_ibm");
599ae2f080bSMitsuru IWASAKI 	}
600ae2f080bSMitsuru IWASAKI 
601a4bfd638SMarkus Brueffer 	/* Handle notifies */
602a4bfd638SMarkus Brueffer 	AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY,
603a4bfd638SMarkus Brueffer 	    acpi_ibm_notify, dev);
604a4bfd638SMarkus Brueffer 
605a4bfd638SMarkus Brueffer 	/* Hook up light to led(4) */
6064aea1563SMarkus Brueffer 	if (sc->light_set_supported)
6073a9ac403SGanbold Tsagaankhuu 		sc->led_dev = led_create_state(ibm_led, sc, "thinklight",
6083a9ac403SGanbold Tsagaankhuu 		    (sc->light_val ? 1 : 0));
609a4bfd638SMarkus Brueffer 
61078fd5381SRui Paulo 	/* Enable per-model events. */
61178fd5381SRui Paulo 	maker = kern_getenv("smbios.system.maker");
61278fd5381SRui Paulo 	product = kern_getenv("smbios.system.product");
61362692eb1SEnji Cooper 	if (maker == NULL || product == NULL)
614890cfe7eSAllan Jude 		goto nosmbios;
615890cfe7eSAllan Jude 
61678fd5381SRui Paulo 	for (i = 0; i < nitems(acpi_ibm_models); i++) {
61778fd5381SRui Paulo 		if (strcmp(maker, acpi_ibm_models[i].maker) == 0 &&
61878fd5381SRui Paulo 		    strcmp(product, acpi_ibm_models[i].product) == 0) {
61978fd5381SRui Paulo 			ACPI_SERIAL_BEGIN(ibm);
62078fd5381SRui Paulo 			acpi_ibm_sysctl_set(sc, ACPI_IBM_METHOD_EVENTMASK,
62178fd5381SRui Paulo 			    acpi_ibm_models[i].eventmask);
62278fd5381SRui Paulo 			ACPI_SERIAL_END(ibm);
62378fd5381SRui Paulo 		}
62478fd5381SRui Paulo 	}
625890cfe7eSAllan Jude 
626890cfe7eSAllan Jude nosmbios:
62778fd5381SRui Paulo 	freeenv(maker);
62878fd5381SRui Paulo 	freeenv(product);
62978fd5381SRui Paulo 
63078fd5381SRui Paulo 	/* Enable events by default. */
63178fd5381SRui Paulo 	ACPI_SERIAL_BEGIN(ibm);
63278fd5381SRui Paulo 	acpi_ibm_sysctl_set(sc, ACPI_IBM_METHOD_EVENTS, 1);
63378fd5381SRui Paulo 	ACPI_SERIAL_END(ibm);
63478fd5381SRui Paulo 
635a4bfd638SMarkus Brueffer 	return (0);
63606064893STakanori Watanabe }
63706064893STakanori Watanabe 
63806064893STakanori Watanabe static int
63906064893STakanori Watanabe acpi_ibm_detach(device_t dev)
64006064893STakanori Watanabe {
64182d4da0fSScott Long 	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
64282d4da0fSScott Long 
64306064893STakanori Watanabe 	struct acpi_ibm_softc *sc = device_get_softc(dev);
64406064893STakanori Watanabe 
645a4bfd638SMarkus Brueffer 	/* Disable events and restore eventmask */
646a4bfd638SMarkus Brueffer 	ACPI_SERIAL_BEGIN(ibm);
647a4bfd638SMarkus Brueffer 	acpi_ibm_sysctl_set(sc, ACPI_IBM_METHOD_EVENTS, 0);
648a4bfd638SMarkus Brueffer 	acpi_ibm_sysctl_set(sc, ACPI_IBM_METHOD_EVENTMASK, sc->events_initialmask);
649a4bfd638SMarkus Brueffer 	ACPI_SERIAL_END(ibm);
65006064893STakanori Watanabe 
651a4bfd638SMarkus Brueffer 	AcpiRemoveNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, acpi_ibm_notify);
652a4bfd638SMarkus Brueffer 
6534aea1563SMarkus Brueffer 	if (sc->led_dev != NULL)
654a4bfd638SMarkus Brueffer 		led_destroy(sc->led_dev);
6554aea1563SMarkus Brueffer 
656*c21f5751SGleb Smirnoff #ifdef EVDEV_SUPPORT
657*c21f5751SGleb Smirnoff 	evdev_free(sc->evdev);
658*c21f5751SGleb Smirnoff #endif
659*c21f5751SGleb Smirnoff 
66006064893STakanori Watanabe 	return (0);
66106064893STakanori Watanabe }
662a4bfd638SMarkus Brueffer 
663a4bfd638SMarkus Brueffer static int
664a50f74ccSMitsuru IWASAKI acpi_ibm_resume(device_t dev)
665a50f74ccSMitsuru IWASAKI {
666a50f74ccSMitsuru IWASAKI 	struct acpi_ibm_softc *sc = device_get_softc(dev);
667a50f74ccSMitsuru IWASAKI 
668a50f74ccSMitsuru IWASAKI 	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
669a50f74ccSMitsuru IWASAKI 
670a50f74ccSMitsuru IWASAKI 	ACPI_SERIAL_BEGIN(ibm);
671a50f74ccSMitsuru IWASAKI 	for (int i = 0; acpi_ibm_sysctls[i].name != NULL; i++) {
672a50f74ccSMitsuru IWASAKI 		int val;
673a50f74ccSMitsuru IWASAKI 
674a50f74ccSMitsuru IWASAKI 		val = acpi_ibm_sysctl_get(sc, i);
675a50f74ccSMitsuru IWASAKI 
676f0188618SHans Petter Selasky 		if (acpi_ibm_sysctls[i].flag_rdonly != 0)
677a50f74ccSMitsuru IWASAKI 			continue;
678a50f74ccSMitsuru IWASAKI 
679a50f74ccSMitsuru IWASAKI 		acpi_ibm_sysctl_set(sc, i, val);
680a50f74ccSMitsuru IWASAKI 	}
681a50f74ccSMitsuru IWASAKI 	ACPI_SERIAL_END(ibm);
682a50f74ccSMitsuru IWASAKI 
6839cd818a3SEitan Adler 	/* The mic led does not turn back on when sysctl_set is called in the above loop */
6849cd818a3SEitan Adler 	acpi_ibm_mic_led_set(sc, sc->mic_led_state);
6859cd818a3SEitan Adler 
686a50f74ccSMitsuru IWASAKI 	return (0);
687a50f74ccSMitsuru IWASAKI }
688a50f74ccSMitsuru IWASAKI 
689a50f74ccSMitsuru IWASAKI static int
690a4bfd638SMarkus Brueffer acpi_ibm_eventmask_set(struct acpi_ibm_softc *sc, int val)
69106064893STakanori Watanabe {
692a4bfd638SMarkus Brueffer 	ACPI_OBJECT		arg[2];
693a4bfd638SMarkus Brueffer 	ACPI_OBJECT_LIST	args;
694a4bfd638SMarkus Brueffer 	ACPI_STATUS		status;
695a4bfd638SMarkus Brueffer 
696a4bfd638SMarkus Brueffer 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
697a4bfd638SMarkus Brueffer 	ACPI_SERIAL_ASSERT(ibm);
698a4bfd638SMarkus Brueffer 
699a4bfd638SMarkus Brueffer 	args.Count = 2;
700a4bfd638SMarkus Brueffer 	args.Pointer = arg;
701a4bfd638SMarkus Brueffer 	arg[0].Type = ACPI_TYPE_INTEGER;
702a4bfd638SMarkus Brueffer 	arg[1].Type = ACPI_TYPE_INTEGER;
703a4bfd638SMarkus Brueffer 
704a4bfd638SMarkus Brueffer 	for (int i = 0; i < 32; ++i) {
705a4bfd638SMarkus Brueffer 		arg[0].Integer.Value = i + 1;
706a4bfd638SMarkus Brueffer 		arg[1].Integer.Value = (((1 << i) & val) != 0);
707a4bfd638SMarkus Brueffer 		status = AcpiEvaluateObject(sc->handle,
708a4bfd638SMarkus Brueffer 		    IBM_NAME_EVENTS_MASK_SET, &args, NULL);
709a4bfd638SMarkus Brueffer 
710a4bfd638SMarkus Brueffer 		if (ACPI_FAILURE(status))
711a4bfd638SMarkus Brueffer 			return (status);
712a4bfd638SMarkus Brueffer 	}
713a4bfd638SMarkus Brueffer 
714a4bfd638SMarkus Brueffer 	return (0);
715a4bfd638SMarkus Brueffer }
716a4bfd638SMarkus Brueffer 
717a4bfd638SMarkus Brueffer static int
718a4bfd638SMarkus Brueffer acpi_ibm_sysctl(SYSCTL_HANDLER_ARGS)
719a4bfd638SMarkus Brueffer {
720a4bfd638SMarkus Brueffer 	struct acpi_ibm_softc	*sc;
721a4bfd638SMarkus Brueffer 	int			arg;
722a4bfd638SMarkus Brueffer 	int			error = 0;
723a4bfd638SMarkus Brueffer 	int			function;
724a4bfd638SMarkus Brueffer 	int			method;
725a4bfd638SMarkus Brueffer 
726a4bfd638SMarkus Brueffer 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
727a4bfd638SMarkus Brueffer 
728a4bfd638SMarkus Brueffer 	sc = (struct acpi_ibm_softc *)oidp->oid_arg1;
729a4bfd638SMarkus Brueffer 	function = oidp->oid_arg2;
730a4bfd638SMarkus Brueffer 	method = acpi_ibm_sysctls[function].method;
731a4bfd638SMarkus Brueffer 
732a4bfd638SMarkus Brueffer 	ACPI_SERIAL_BEGIN(ibm);
733a4bfd638SMarkus Brueffer 	arg = acpi_ibm_sysctl_get(sc, method);
734a4bfd638SMarkus Brueffer 	error = sysctl_handle_int(oidp, &arg, 0, req);
735a4bfd638SMarkus Brueffer 
736a4bfd638SMarkus Brueffer 	/* Sanity check */
737a4bfd638SMarkus Brueffer 	if (error != 0 || req->newptr == NULL)
738a4bfd638SMarkus Brueffer 		goto out;
739a4bfd638SMarkus Brueffer 
740a4bfd638SMarkus Brueffer 	/* Update */
741a4bfd638SMarkus Brueffer 	error = acpi_ibm_sysctl_set(sc, method, arg);
742a4bfd638SMarkus Brueffer 
743a4bfd638SMarkus Brueffer out:
744a4bfd638SMarkus Brueffer 	ACPI_SERIAL_END(ibm);
745a4bfd638SMarkus Brueffer 	return (error);
746a4bfd638SMarkus Brueffer }
747a4bfd638SMarkus Brueffer 
748a4bfd638SMarkus Brueffer static int
749a4bfd638SMarkus Brueffer acpi_ibm_sysctl_get(struct acpi_ibm_softc *sc, int method)
750a4bfd638SMarkus Brueffer {
7519a179dd8SJung-uk Kim 	UINT64		val_ec;
752a4bfd638SMarkus Brueffer 	int 		val = 0, key;
753a4bfd638SMarkus Brueffer 
754a4bfd638SMarkus Brueffer 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
755a4bfd638SMarkus Brueffer 	ACPI_SERIAL_ASSERT(ibm);
756a4bfd638SMarkus Brueffer 
757a4bfd638SMarkus Brueffer 	switch (method) {
758a4bfd638SMarkus Brueffer 	case ACPI_IBM_METHOD_EVENTS:
759a4bfd638SMarkus Brueffer 		acpi_GetInteger(sc->handle, IBM_NAME_EVENTS_STATUS_GET, &val);
760a4bfd638SMarkus Brueffer 		break;
761a4bfd638SMarkus Brueffer 
762a4bfd638SMarkus Brueffer 	case ACPI_IBM_METHOD_EVENTMASK:
763a4bfd638SMarkus Brueffer 		if (sc->events_mask_supported)
764a4bfd638SMarkus Brueffer 			acpi_GetInteger(sc->handle, IBM_NAME_EVENTS_MASK_GET, &val);
765a4bfd638SMarkus Brueffer 		break;
766a4bfd638SMarkus Brueffer 
767a4bfd638SMarkus Brueffer 	case ACPI_IBM_METHOD_HOTKEY:
768a4bfd638SMarkus Brueffer 		/*
769a4bfd638SMarkus Brueffer 		 * Construct the hotkey as a bitmask as illustrated below.
770a4bfd638SMarkus Brueffer 		 * Note that whenever a key was pressed, the respecting bit
771a4bfd638SMarkus Brueffer 		 * toggles and nothing else changes.
772a4bfd638SMarkus Brueffer 		 * +--+--+-+-+-+-+-+-+-+-+-+-+
773a4bfd638SMarkus Brueffer 		 * |11|10|9|8|7|6|5|4|3|2|1|0|
774a4bfd638SMarkus Brueffer 		 * +--+--+-+-+-+-+-+-+-+-+-+-+
775a4bfd638SMarkus Brueffer 		 *   |  | | | | | | | | | | |
776a4bfd638SMarkus Brueffer 		 *   |  | | | | | | | | | | +- Home Button
777a4bfd638SMarkus Brueffer 		 *   |  | | | | | | | | | +--- Search Button
778a4bfd638SMarkus Brueffer 		 *   |  | | | | | | | | +----- Mail Button
779a4bfd638SMarkus Brueffer 		 *   |  | | | | | | | +------- Thinkpad Button
780a4bfd638SMarkus Brueffer 		 *   |  | | | | | | +--------- Zoom (Fn + Space)
781a4bfd638SMarkus Brueffer 		 *   |  | | | | | +----------- WLAN Button
782a4bfd638SMarkus Brueffer 		 *   |  | | | | +------------- Video Button
783a4bfd638SMarkus Brueffer 		 *   |  | | | +--------------- Hibernate Button
784a4bfd638SMarkus Brueffer 		 *   |  | | +----------------- Thinklight Button
785a4bfd638SMarkus Brueffer 		 *   |  | +------------------- Screen expand (Fn + F8)
786a4bfd638SMarkus Brueffer 		 *   |  +--------------------- Brightness
787a4bfd638SMarkus Brueffer 		 *   +------------------------ Volume/Mute
788a4bfd638SMarkus Brueffer 		 */
789a4bfd638SMarkus Brueffer 		key = rtcin(IBM_RTC_HOTKEY1);
790a4bfd638SMarkus Brueffer 		val = (IBM_RTC_MASK_HOME | IBM_RTC_MASK_SEARCH | IBM_RTC_MASK_MAIL | IBM_RTC_MASK_WLAN) & key;
791a4bfd638SMarkus Brueffer 		key = rtcin(IBM_RTC_HOTKEY2);
792a4bfd638SMarkus Brueffer 		val |= (IBM_RTC_MASK_THINKPAD | IBM_RTC_MASK_VIDEO | IBM_RTC_MASK_HIBERNATE) & key;
793a4bfd638SMarkus Brueffer 		val |= (IBM_RTC_MASK_ZOOM & key) >> 1;
794a4bfd638SMarkus Brueffer 		key = rtcin(IBM_RTC_THINKLIGHT);
795a4bfd638SMarkus Brueffer 		val |= (IBM_RTC_MASK_THINKLIGHT & key) << 4;
796a4bfd638SMarkus Brueffer 		key = rtcin(IBM_RTC_SCREENEXPAND);
797a4bfd638SMarkus Brueffer 		val |= (IBM_RTC_MASK_THINKLIGHT & key) << 4;
798a4bfd638SMarkus Brueffer 		key = rtcin(IBM_RTC_BRIGHTNESS);
799a4bfd638SMarkus Brueffer 		val |= (IBM_RTC_MASK_BRIGHTNESS & key) << 5;
800a4bfd638SMarkus Brueffer 		key = rtcin(IBM_RTC_VOLUME);
801a4bfd638SMarkus Brueffer 		val |= (IBM_RTC_MASK_VOLUME & key) << 4;
802a4bfd638SMarkus Brueffer 		break;
803a4bfd638SMarkus Brueffer 
804a4bfd638SMarkus Brueffer 	case ACPI_IBM_METHOD_BRIGHTNESS:
805a4bfd638SMarkus Brueffer 		ACPI_EC_READ(sc->ec_dev, IBM_EC_BRIGHTNESS, &val_ec, 1);
806a4bfd638SMarkus Brueffer 		val = val_ec & IBM_EC_MASK_BRI;
807a4bfd638SMarkus Brueffer 		break;
808a4bfd638SMarkus Brueffer 
809a4bfd638SMarkus Brueffer 	case ACPI_IBM_METHOD_VOLUME:
810a4bfd638SMarkus Brueffer 		ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1);
811a4bfd638SMarkus Brueffer 		val = val_ec & IBM_EC_MASK_VOL;
812a4bfd638SMarkus Brueffer 		break;
813a4bfd638SMarkus Brueffer 
814a4bfd638SMarkus Brueffer 	case ACPI_IBM_METHOD_MUTE:
815a4bfd638SMarkus Brueffer 		ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1);
816a4bfd638SMarkus Brueffer 		val = ((val_ec & IBM_EC_MASK_MUTE) == IBM_EC_MASK_MUTE);
817a4bfd638SMarkus Brueffer 		break;
818a4bfd638SMarkus Brueffer 
819a4bfd638SMarkus Brueffer 	case ACPI_IBM_METHOD_THINKLIGHT:
820a4bfd638SMarkus Brueffer 		if (sc->light_get_supported)
821a4bfd638SMarkus Brueffer 			acpi_GetInteger(sc->ec_handle, IBM_NAME_KEYLIGHT, &val);
822a4bfd638SMarkus Brueffer 		else
823a4bfd638SMarkus Brueffer 			val = sc->light_val;
824a4bfd638SMarkus Brueffer 		break;
825a4bfd638SMarkus Brueffer 
826a4bfd638SMarkus Brueffer 	case ACPI_IBM_METHOD_BLUETOOTH:
827a4bfd638SMarkus Brueffer 		acpi_GetInteger(sc->handle, IBM_NAME_WLAN_BT_GET, &val);
828a4bfd638SMarkus Brueffer 		sc->wlan_bt_flags = val;
829a4bfd638SMarkus Brueffer 		val = ((val & IBM_NAME_MASK_BT) != 0);
830a4bfd638SMarkus Brueffer 		break;
831a4bfd638SMarkus Brueffer 
832a4bfd638SMarkus Brueffer 	case ACPI_IBM_METHOD_WLAN:
833a4bfd638SMarkus Brueffer 		acpi_GetInteger(sc->handle, IBM_NAME_WLAN_BT_GET, &val);
834a4bfd638SMarkus Brueffer 		sc->wlan_bt_flags = val;
835a4bfd638SMarkus Brueffer 		val = ((val & IBM_NAME_MASK_WLAN) != 0);
836a4bfd638SMarkus Brueffer 		break;
837a4bfd638SMarkus Brueffer 
838a4bfd638SMarkus Brueffer 	case ACPI_IBM_METHOD_FANSPEED:
839a4bfd638SMarkus Brueffer 		if (sc->fan_handle) {
840a4bfd638SMarkus Brueffer 			if(ACPI_FAILURE(acpi_GetInteger(sc->fan_handle, NULL, &val)))
841a4bfd638SMarkus Brueffer 				val = -1;
84266671c14SEd Maste 		} else {
843a4bfd638SMarkus Brueffer 			ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSPEED, &val_ec, 2);
844a4bfd638SMarkus Brueffer 			val = val_ec;
845a4bfd638SMarkus Brueffer 		}
846a4bfd638SMarkus Brueffer 		break;
847a4bfd638SMarkus Brueffer 
848f9312099SMarkus Brueffer 	case ACPI_IBM_METHOD_FANLEVEL:
849f9312099SMarkus Brueffer 		/*
850f9312099SMarkus Brueffer 		 * The IBM_EC_FANSTATUS register works as follows:
851f9312099SMarkus Brueffer 		 * Bit 0-5 indicate the level at which the fan operates. Only
852f9312099SMarkus Brueffer 		 *       values between 0 and 7 have an effect. Everything
853f9312099SMarkus Brueffer 		 *       above 7 is treated the same as level 7
854f9312099SMarkus Brueffer 		 * Bit 6 overrides the fan speed limit if set to 1
855f9312099SMarkus Brueffer 		 * Bit 7 indicates at which mode the fan operates:
856f9312099SMarkus Brueffer 		 *       manual (0) or automatic (1)
857f9312099SMarkus Brueffer 		 */
858f9312099SMarkus Brueffer 		if (!sc->fan_handle) {
859f9312099SMarkus Brueffer 			ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1);
860ddf18432SMark Johnston 			if (val_ec & IBM_EC_MASK_FANUNTHROTTLED)
8619f763f00SMark Johnston 				val = 8;
8629f763f00SMark Johnston 			else
863f9312099SMarkus Brueffer 				val = val_ec & IBM_EC_MASK_FANLEVEL;
864f9312099SMarkus Brueffer 		}
865f9312099SMarkus Brueffer 		break;
866f9312099SMarkus Brueffer 
867a4bfd638SMarkus Brueffer 	case ACPI_IBM_METHOD_FANSTATUS:
868a4bfd638SMarkus Brueffer 		if (!sc->fan_handle) {
869a4bfd638SMarkus Brueffer 			ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1);
870a4bfd638SMarkus Brueffer 			val = (val_ec & IBM_EC_MASK_FANSTATUS) == IBM_EC_MASK_FANSTATUS;
87166671c14SEd Maste 		} else
872a4bfd638SMarkus Brueffer 			val = -1;
873a4bfd638SMarkus Brueffer 		break;
87466671c14SEd Maste 
8759cd818a3SEitan Adler 	case ACPI_IBM_METHOD_MIC_LED:
8769cd818a3SEitan Adler 		if (sc->mic_led_handle)
8779cd818a3SEitan Adler 			return sc->mic_led_state;
8789cd818a3SEitan Adler 		else
8799cd818a3SEitan Adler 			val = -1;
8809cd818a3SEitan Adler 		break;
88102aeba83SPhilip Paeps 
88202aeba83SPhilip Paeps 	case ACPI_IBM_METHOD_PRIVACYGUARD:
88302aeba83SPhilip Paeps 		val = acpi_ibm_privacyguard_get(sc);
88402aeba83SPhilip Paeps 		break;
885a4bfd638SMarkus Brueffer 	}
886a4bfd638SMarkus Brueffer 
887a4bfd638SMarkus Brueffer 	return (val);
888a4bfd638SMarkus Brueffer }
889a4bfd638SMarkus Brueffer 
890a4bfd638SMarkus Brueffer static int
891a4bfd638SMarkus Brueffer acpi_ibm_sysctl_set(struct acpi_ibm_softc *sc, int method, int arg)
892a4bfd638SMarkus Brueffer {
893ae2f080bSMitsuru IWASAKI 	int			val;
8949a179dd8SJung-uk Kim 	UINT64			val_ec;
895a4bfd638SMarkus Brueffer 	ACPI_STATUS		status;
896a4bfd638SMarkus Brueffer 
897a4bfd638SMarkus Brueffer 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
898a4bfd638SMarkus Brueffer 	ACPI_SERIAL_ASSERT(ibm);
899a4bfd638SMarkus Brueffer 
900a4bfd638SMarkus Brueffer 	switch (method) {
901a4bfd638SMarkus Brueffer 	case ACPI_IBM_METHOD_EVENTS:
902a4bfd638SMarkus Brueffer 		if (arg < 0 || arg > 1)
903a4bfd638SMarkus Brueffer 			return (EINVAL);
904a4bfd638SMarkus Brueffer 
905a4bfd638SMarkus Brueffer 		status = acpi_SetInteger(sc->handle, IBM_NAME_EVENTS_STATUS_SET, arg);
906a4bfd638SMarkus Brueffer 		if (ACPI_FAILURE(status))
907a4bfd638SMarkus Brueffer 			return (status);
908a4bfd638SMarkus Brueffer 		if (sc->events_mask_supported)
909a4bfd638SMarkus Brueffer 			return acpi_ibm_eventmask_set(sc, sc->events_availmask);
910a4bfd638SMarkus Brueffer 		break;
911a4bfd638SMarkus Brueffer 
912a4bfd638SMarkus Brueffer 	case ACPI_IBM_METHOD_EVENTMASK:
913a4bfd638SMarkus Brueffer 		if (sc->events_mask_supported)
914a4bfd638SMarkus Brueffer 			return acpi_ibm_eventmask_set(sc, arg);
915a4bfd638SMarkus Brueffer 		break;
916a4bfd638SMarkus Brueffer 
917a4bfd638SMarkus Brueffer 	case ACPI_IBM_METHOD_BRIGHTNESS:
918ae2f080bSMitsuru IWASAKI 		return acpi_ibm_brightness_set(sc, arg);
919a4bfd638SMarkus Brueffer 		break;
920a4bfd638SMarkus Brueffer 
921a4bfd638SMarkus Brueffer 	case ACPI_IBM_METHOD_VOLUME:
922ae2f080bSMitsuru IWASAKI 		return acpi_ibm_volume_set(sc, arg);
923a4bfd638SMarkus Brueffer 		break;
924a4bfd638SMarkus Brueffer 
925a4bfd638SMarkus Brueffer 	case ACPI_IBM_METHOD_MUTE:
926ae2f080bSMitsuru IWASAKI 		return acpi_ibm_mute_set(sc, arg);
927a4bfd638SMarkus Brueffer 		break;
928a4bfd638SMarkus Brueffer 
9299cd818a3SEitan Adler 	case ACPI_IBM_METHOD_MIC_LED:
9309cd818a3SEitan Adler 		return acpi_ibm_mic_led_set(sc, arg);
9319cd818a3SEitan Adler 		break;
9329cd818a3SEitan Adler 
933a4bfd638SMarkus Brueffer 	case ACPI_IBM_METHOD_THINKLIGHT:
934ae2f080bSMitsuru IWASAKI 		return acpi_ibm_thinklight_set(sc, arg);
935a4bfd638SMarkus Brueffer 		break;
936a4bfd638SMarkus Brueffer 
937a4bfd638SMarkus Brueffer 	case ACPI_IBM_METHOD_BLUETOOTH:
938ae2f080bSMitsuru IWASAKI 		return acpi_ibm_bluetooth_set(sc, arg);
939a4bfd638SMarkus Brueffer 		break;
940f9312099SMarkus Brueffer 
94102aeba83SPhilip Paeps 	case ACPI_IBM_METHOD_PRIVACYGUARD:
94202aeba83SPhilip Paeps 		return (acpi_status_to_errno(acpi_ibm_privacyguard_set(sc, arg)));
94302aeba83SPhilip Paeps 		break;
94402aeba83SPhilip Paeps 
945f9312099SMarkus Brueffer 	case ACPI_IBM_METHOD_FANLEVEL:
9469f763f00SMark Johnston 		if (arg < 0 || arg > 8)
947f9312099SMarkus Brueffer 			return (EINVAL);
948f9312099SMarkus Brueffer 
949f9312099SMarkus Brueffer 		if (!sc->fan_handle) {
9509f763f00SMark Johnston 			/* Read the current fan status. */
951f9312099SMarkus Brueffer 			ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1);
9529f763f00SMark Johnston 			val = val_ec & ~(IBM_EC_MASK_FANLEVEL |
953ddf18432SMark Johnston 			    IBM_EC_MASK_FANUNTHROTTLED);
954f9312099SMarkus Brueffer 
9559f763f00SMark Johnston 			if (arg == 8)
956ddf18432SMark Johnston 				/* Full speed, set the unthrottled bit. */
957ddf18432SMark Johnston 				val |= 7 | IBM_EC_MASK_FANUNTHROTTLED;
9589f763f00SMark Johnston 			else
9599f763f00SMark Johnston 				val |= arg;
9609f763f00SMark Johnston 
9619f763f00SMark Johnston 			return (ACPI_EC_WRITE(sc->ec_dev, IBM_EC_FANSTATUS, val,
9629f763f00SMark Johnston 			    1));
963f9312099SMarkus Brueffer 		}
964f9312099SMarkus Brueffer 		break;
965f9312099SMarkus Brueffer 
966f9312099SMarkus Brueffer 	case ACPI_IBM_METHOD_FANSTATUS:
967f9312099SMarkus Brueffer 		if (arg < 0 || arg > 1)
968f9312099SMarkus Brueffer 			return (EINVAL);
969f9312099SMarkus Brueffer 
970f9312099SMarkus Brueffer 		if (!sc->fan_handle) {
971f9312099SMarkus Brueffer 			/* Read the current fanstatus */
972f9312099SMarkus Brueffer 			ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1);
973f9312099SMarkus Brueffer 
974f9312099SMarkus Brueffer 			return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_FANSTATUS,
975f9312099SMarkus Brueffer 				(arg == 1) ? (val_ec | IBM_EC_MASK_FANSTATUS) : (val_ec & (~IBM_EC_MASK_FANSTATUS)), 1);
976f9312099SMarkus Brueffer 		}
977f9312099SMarkus Brueffer 		break;
978a4bfd638SMarkus Brueffer 	}
979a4bfd638SMarkus Brueffer 
980a4bfd638SMarkus Brueffer 	return (0);
981a4bfd638SMarkus Brueffer }
982a4bfd638SMarkus Brueffer 
983a4bfd638SMarkus Brueffer static int
984a4bfd638SMarkus Brueffer acpi_ibm_sysctl_init(struct acpi_ibm_softc *sc, int method)
985a4bfd638SMarkus Brueffer {
986a4bfd638SMarkus Brueffer 	int 			dummy;
987a4bfd638SMarkus Brueffer 	ACPI_OBJECT_TYPE 	cmos_t;
988a4bfd638SMarkus Brueffer 	ACPI_HANDLE		ledb_handle;
989a4bfd638SMarkus Brueffer 
990a4bfd638SMarkus Brueffer 	switch (method) {
991a4bfd638SMarkus Brueffer 	case ACPI_IBM_METHOD_EVENTS:
992a4bfd638SMarkus Brueffer 		return (TRUE);
993a4bfd638SMarkus Brueffer 
994a4bfd638SMarkus Brueffer 	case ACPI_IBM_METHOD_EVENTMASK:
995a4bfd638SMarkus Brueffer 		return (sc->events_mask_supported);
996a4bfd638SMarkus Brueffer 
997a4bfd638SMarkus Brueffer 	case ACPI_IBM_METHOD_HOTKEY:
998a4bfd638SMarkus Brueffer 	case ACPI_IBM_METHOD_BRIGHTNESS:
999a4bfd638SMarkus Brueffer 	case ACPI_IBM_METHOD_VOLUME:
1000a4bfd638SMarkus Brueffer 	case ACPI_IBM_METHOD_MUTE:
1001453130d9SPedro F. Giffuni 		/* EC is required here, which was already checked before */
1002a4bfd638SMarkus Brueffer 		return (TRUE);
1003a4bfd638SMarkus Brueffer 
10049cd818a3SEitan Adler 	case ACPI_IBM_METHOD_MIC_LED:
10059cd818a3SEitan Adler 		if (ACPI_SUCCESS(AcpiGetHandle(sc->handle, "MMTS", &sc->mic_led_handle)))
10069cd818a3SEitan Adler 		{
10079cd818a3SEitan Adler 			/* Turn off mic led by default */
10089cd818a3SEitan Adler 			acpi_ibm_mic_led_set(sc, 0);
10099cd818a3SEitan Adler 			return (TRUE);
101066671c14SEd Maste 		} else
10119cd818a3SEitan Adler 			sc->mic_led_handle = NULL;
10129cd818a3SEitan Adler 		return (FALSE);
10139cd818a3SEitan Adler 
1014a4bfd638SMarkus Brueffer 	case ACPI_IBM_METHOD_THINKLIGHT:
1015a4bfd638SMarkus Brueffer 		sc->cmos_handle = NULL;
10169cf74116SMarkus Brueffer 		sc->light_get_supported = ACPI_SUCCESS(acpi_GetInteger(
10179cf74116SMarkus Brueffer 		    sc->ec_handle, IBM_NAME_KEYLIGHT, &sc->light_val));
1018a4bfd638SMarkus Brueffer 
1019a4bfd638SMarkus Brueffer 		if ((ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\UCMS", &sc->light_handle)) ||
1020a4bfd638SMarkus Brueffer 		     ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\CMOS", &sc->light_handle)) ||
1021a4bfd638SMarkus Brueffer 		     ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\CMS", &sc->light_handle))) &&
1022a4bfd638SMarkus Brueffer 		     ACPI_SUCCESS(AcpiGetType(sc->light_handle, &cmos_t)) &&
1023a4bfd638SMarkus Brueffer 		     cmos_t == ACPI_TYPE_METHOD) {
1024a4bfd638SMarkus Brueffer 			sc->light_cmd_on = 0x0c;
1025a4bfd638SMarkus Brueffer 			sc->light_cmd_off = 0x0d;
1026a4bfd638SMarkus Brueffer 			sc->cmos_handle = sc->light_handle;
1027a4bfd638SMarkus Brueffer 		}
1028a4bfd638SMarkus Brueffer 		else if (ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\LGHT", &sc->light_handle))) {
1029a4bfd638SMarkus Brueffer 			sc->light_cmd_on = 1;
1030a4bfd638SMarkus Brueffer 			sc->light_cmd_off = 0;
103166671c14SEd Maste 		} else
1032a4bfd638SMarkus Brueffer 			sc->light_handle = NULL;
1033a4bfd638SMarkus Brueffer 
1034a4bfd638SMarkus Brueffer 		sc->light_set_supported = (sc->light_handle &&
1035a4bfd638SMarkus Brueffer 		    ACPI_FAILURE(AcpiGetHandle(sc->ec_handle, "LEDB", &ledb_handle)));
1036a4bfd638SMarkus Brueffer 
10379cf74116SMarkus Brueffer 		if (sc->light_get_supported)
10389cf74116SMarkus Brueffer 			return (TRUE);
10399cf74116SMarkus Brueffer 
10409cf74116SMarkus Brueffer 		if (sc->light_set_supported) {
1041a4bfd638SMarkus Brueffer 			sc->light_val = 0;
1042a4bfd638SMarkus Brueffer 			return (TRUE);
1043a4bfd638SMarkus Brueffer 		}
10449cf74116SMarkus Brueffer 
1045a4bfd638SMarkus Brueffer 		return (FALSE);
1046a4bfd638SMarkus Brueffer 
1047a4bfd638SMarkus Brueffer 	case ACPI_IBM_METHOD_BLUETOOTH:
1048a4bfd638SMarkus Brueffer 	case ACPI_IBM_METHOD_WLAN:
1049a4bfd638SMarkus Brueffer 		if (ACPI_SUCCESS(acpi_GetInteger(sc->handle, IBM_NAME_WLAN_BT_GET, &dummy)))
1050a4bfd638SMarkus Brueffer 			return (TRUE);
1051a4bfd638SMarkus Brueffer 		return (FALSE);
1052a4bfd638SMarkus Brueffer 
1053a4bfd638SMarkus Brueffer 	case ACPI_IBM_METHOD_FANSPEED:
1054a4bfd638SMarkus Brueffer 		/*
1055a4bfd638SMarkus Brueffer 		 * Some models report the fan speed in levels from 0-7
1056a4bfd638SMarkus Brueffer 		 * Newer models report it contiguously
1057a4bfd638SMarkus Brueffer 		 */
1058a4bfd638SMarkus Brueffer 		sc->fan_levels =
1059a4bfd638SMarkus Brueffer 		    (ACPI_SUCCESS(AcpiGetHandle(sc->handle, "GFAN", &sc->fan_handle)) ||
1060a4bfd638SMarkus Brueffer 		     ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\FSPD", &sc->fan_handle)));
1061a4bfd638SMarkus Brueffer 		return (TRUE);
1062a4bfd638SMarkus Brueffer 
1063f9312099SMarkus Brueffer 	case ACPI_IBM_METHOD_FANLEVEL:
1064a4bfd638SMarkus Brueffer 	case ACPI_IBM_METHOD_FANSTATUS:
1065a4bfd638SMarkus Brueffer 		/*
1066a4bfd638SMarkus Brueffer 		 * Fan status is only supported on those models,
1067a4bfd638SMarkus Brueffer 		 * which report fan RPM contiguously, not in levels
1068a4bfd638SMarkus Brueffer 		 */
1069a4bfd638SMarkus Brueffer 		if (sc->fan_levels)
1070a4bfd638SMarkus Brueffer 			return (FALSE);
1071a4bfd638SMarkus Brueffer 		return (TRUE);
1072a4bfd638SMarkus Brueffer 
1073a4bfd638SMarkus Brueffer 	case ACPI_IBM_METHOD_THERMAL:
1074a4bfd638SMarkus Brueffer 		if (ACPI_SUCCESS(acpi_GetInteger(sc->ec_handle, IBM_NAME_THERMAL_GET, &dummy))) {
1075a4bfd638SMarkus Brueffer 			sc->thermal_updt_supported = ACPI_SUCCESS(acpi_GetInteger(sc->ec_handle, IBM_NAME_THERMAL_UPDT, &dummy));
1076a4bfd638SMarkus Brueffer 			return (TRUE);
1077a4bfd638SMarkus Brueffer 		}
1078a4bfd638SMarkus Brueffer 		return (FALSE);
1079ae2f080bSMitsuru IWASAKI 
1080ae2f080bSMitsuru IWASAKI 	case ACPI_IBM_METHOD_HANDLEREVENTS:
1081ae2f080bSMitsuru IWASAKI 		return (TRUE);
108202aeba83SPhilip Paeps 
108302aeba83SPhilip Paeps 	case ACPI_IBM_METHOD_PRIVACYGUARD:
108402aeba83SPhilip Paeps 		return (acpi_ibm_privacyguard_get(sc) != -1);
1085a4bfd638SMarkus Brueffer 	}
1086a4bfd638SMarkus Brueffer 	return (FALSE);
1087a4bfd638SMarkus Brueffer }
1088a4bfd638SMarkus Brueffer 
1089a4bfd638SMarkus Brueffer static int
1090a4bfd638SMarkus Brueffer acpi_ibm_thermal_sysctl(SYSCTL_HANDLER_ARGS)
1091a4bfd638SMarkus Brueffer {
1092a4bfd638SMarkus Brueffer 	struct acpi_ibm_softc	*sc;
1093a4bfd638SMarkus Brueffer 	int			error = 0;
1094a4bfd638SMarkus Brueffer 	char			temp_cmd[] = "TMP0";
1095a4bfd638SMarkus Brueffer 	int			temp[8];
1096a4bfd638SMarkus Brueffer 
1097a4bfd638SMarkus Brueffer 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
1098a4bfd638SMarkus Brueffer 
1099a4bfd638SMarkus Brueffer 	sc = (struct acpi_ibm_softc *)oidp->oid_arg1;
1100a4bfd638SMarkus Brueffer 
1101a4bfd638SMarkus Brueffer 	ACPI_SERIAL_BEGIN(ibm);
1102a4bfd638SMarkus Brueffer 
1103a4bfd638SMarkus Brueffer 	for (int i = 0; i < 8; ++i) {
1104a4bfd638SMarkus Brueffer 		temp_cmd[3] = '0' + i;
1105a4bfd638SMarkus Brueffer 
1106a4bfd638SMarkus Brueffer 		/*
1107a4bfd638SMarkus Brueffer 		 * The TMPx methods seem to return +/- 128 or 0
1108a4bfd638SMarkus Brueffer 		 * when the respecting sensor is not available
1109a4bfd638SMarkus Brueffer 		 */
1110a4bfd638SMarkus Brueffer 		if (ACPI_FAILURE(acpi_GetInteger(sc->ec_handle, temp_cmd,
1111a4bfd638SMarkus Brueffer 		    &temp[i])) || ABS(temp[i]) == 128 || temp[i] == 0)
1112a4bfd638SMarkus Brueffer 			temp[i] = -1;
1113a4bfd638SMarkus Brueffer 		else if (sc->thermal_updt_supported)
1114a4bfd638SMarkus Brueffer 			/* Temperature is reported in tenth of Kelvin */
11159d6672e1SLuiz Otavio O Souza 			temp[i] = (temp[i] - 2731 + 5) / 10;
1116a4bfd638SMarkus Brueffer 	}
1117a4bfd638SMarkus Brueffer 
1118a4bfd638SMarkus Brueffer 	error = sysctl_handle_opaque(oidp, &temp, 8*sizeof(int), req);
1119a4bfd638SMarkus Brueffer 
1120a4bfd638SMarkus Brueffer 	ACPI_SERIAL_END(ibm);
1121a4bfd638SMarkus Brueffer 	return (error);
1122a4bfd638SMarkus Brueffer }
1123a4bfd638SMarkus Brueffer 
1124ae2f080bSMitsuru IWASAKI static int
1125ae2f080bSMitsuru IWASAKI acpi_ibm_handlerevents_sysctl(SYSCTL_HANDLER_ARGS)
1126ae2f080bSMitsuru IWASAKI {
1127ae2f080bSMitsuru IWASAKI 	struct acpi_ibm_softc	*sc;
1128ae2f080bSMitsuru IWASAKI 	int			error = 0;
1129ae2f080bSMitsuru IWASAKI 	struct sbuf		sb;
1130ae2f080bSMitsuru IWASAKI 	char			*cp, *ep;
1131ae2f080bSMitsuru IWASAKI 	int			l, val;
1132ae2f080bSMitsuru IWASAKI 	unsigned int		handler_events;
11330e1152fcSHans Petter Selasky 	char			temp[128];
1134ae2f080bSMitsuru IWASAKI 
1135ae2f080bSMitsuru IWASAKI 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
1136ae2f080bSMitsuru IWASAKI 
1137ae2f080bSMitsuru IWASAKI 	sc = (struct acpi_ibm_softc *)oidp->oid_arg1;
1138ae2f080bSMitsuru IWASAKI 
1139ae2f080bSMitsuru IWASAKI 	if (sbuf_new(&sb, NULL, 128, SBUF_AUTOEXTEND) == NULL)
1140ae2f080bSMitsuru IWASAKI 		return (ENOMEM);
1141ae2f080bSMitsuru IWASAKI 
1142ae2f080bSMitsuru IWASAKI 	ACPI_SERIAL_BEGIN(ibm);
1143ae2f080bSMitsuru IWASAKI 
1144ae2f080bSMitsuru IWASAKI 	/* Get old values if this is a get request. */
1145ae2f080bSMitsuru IWASAKI 	if (req->newptr == NULL) {
1146ae2f080bSMitsuru IWASAKI 		for (int i = 0; i < 8 * sizeof(sc->handler_events); i++)
1147ae2f080bSMitsuru IWASAKI 			if (sc->handler_events & (1 << i))
1148ae2f080bSMitsuru IWASAKI 				sbuf_printf(&sb, "0x%02x ", i + 1);
1149ae2f080bSMitsuru IWASAKI 		if (sbuf_len(&sb) == 0)
1150ae2f080bSMitsuru IWASAKI 			sbuf_printf(&sb, "NONE");
1151ae2f080bSMitsuru IWASAKI 	}
1152ae2f080bSMitsuru IWASAKI 
1153ae2f080bSMitsuru IWASAKI 	sbuf_trim(&sb);
1154ae2f080bSMitsuru IWASAKI 	sbuf_finish(&sb);
11550e1152fcSHans Petter Selasky 	strlcpy(temp, sbuf_data(&sb), sizeof(temp));
1156ae2f080bSMitsuru IWASAKI 	sbuf_delete(&sb);
1157ae2f080bSMitsuru IWASAKI 
11580e1152fcSHans Petter Selasky 	error = sysctl_handle_string(oidp, temp, sizeof(temp), req);
11590e1152fcSHans Petter Selasky 
11600e1152fcSHans Petter Selasky 	/* Check for error or no change */
1161ae2f080bSMitsuru IWASAKI 	if (error != 0 || req->newptr == NULL)
1162ae2f080bSMitsuru IWASAKI 		goto out;
1163ae2f080bSMitsuru IWASAKI 
1164ae2f080bSMitsuru IWASAKI 	/* If the user is setting a string, parse it. */
1165ae2f080bSMitsuru IWASAKI 	handler_events = 0;
11660e1152fcSHans Petter Selasky 	cp = temp;
1167ae2f080bSMitsuru IWASAKI 	while (*cp) {
1168ae2f080bSMitsuru IWASAKI 		if (isspace(*cp)) {
1169ae2f080bSMitsuru IWASAKI 			cp++;
1170ae2f080bSMitsuru IWASAKI 			continue;
1171ae2f080bSMitsuru IWASAKI 		}
1172ae2f080bSMitsuru IWASAKI 
1173ae2f080bSMitsuru IWASAKI 		ep = cp;
1174ae2f080bSMitsuru IWASAKI 
1175ae2f080bSMitsuru IWASAKI 		while (*ep && !isspace(*ep))
1176ae2f080bSMitsuru IWASAKI 			ep++;
1177ae2f080bSMitsuru IWASAKI 
1178ae2f080bSMitsuru IWASAKI 		l = ep - cp;
1179ae2f080bSMitsuru IWASAKI 		if (l == 0)
1180ae2f080bSMitsuru IWASAKI 			break;
1181ae2f080bSMitsuru IWASAKI 
1182ae2f080bSMitsuru IWASAKI 		if (strncmp(cp, "NONE", 4) == 0) {
1183ae2f080bSMitsuru IWASAKI 			cp = ep;
1184ae2f080bSMitsuru IWASAKI 			continue;
1185ae2f080bSMitsuru IWASAKI 		}
1186ae2f080bSMitsuru IWASAKI 
1187ae2f080bSMitsuru IWASAKI 		if (l >= 3 && cp[0] == '0' && (cp[1] == 'X' || cp[1] == 'x'))
1188ae2f080bSMitsuru IWASAKI 			val = strtoul(cp, &ep, 16);
1189ae2f080bSMitsuru IWASAKI 		else
1190ae2f080bSMitsuru IWASAKI 			val = strtoul(cp, &ep, 10);
1191ae2f080bSMitsuru IWASAKI 
1192ae2f080bSMitsuru IWASAKI 		if (val == 0 || ep == cp || val >= 8 * sizeof(handler_events)) {
1193ae2f080bSMitsuru IWASAKI 			cp[l] = '\0';
1194ae2f080bSMitsuru IWASAKI 			device_printf(sc->dev, "invalid event code: %s\n", cp);
1195ae2f080bSMitsuru IWASAKI 			error = EINVAL;
1196ae2f080bSMitsuru IWASAKI 			goto out;
1197ae2f080bSMitsuru IWASAKI 		}
1198ae2f080bSMitsuru IWASAKI 
1199ae2f080bSMitsuru IWASAKI 		handler_events |= 1 << (val - 1);
1200ae2f080bSMitsuru IWASAKI 
1201ae2f080bSMitsuru IWASAKI 		cp = ep;
1202ae2f080bSMitsuru IWASAKI 	}
1203ae2f080bSMitsuru IWASAKI 
1204ae2f080bSMitsuru IWASAKI 	sc->handler_events = handler_events;
1205ae2f080bSMitsuru IWASAKI out:
1206ae2f080bSMitsuru IWASAKI 	ACPI_SERIAL_END(ibm);
1207ae2f080bSMitsuru IWASAKI 	return (error);
1208ae2f080bSMitsuru IWASAKI }
1209ae2f080bSMitsuru IWASAKI 
1210ae2f080bSMitsuru IWASAKI static int
1211ae2f080bSMitsuru IWASAKI acpi_ibm_brightness_set(struct acpi_ibm_softc *sc, int arg)
1212ae2f080bSMitsuru IWASAKI {
1213ae2f080bSMitsuru IWASAKI 	int			val, step;
1214ae2f080bSMitsuru IWASAKI 	UINT64			val_ec;
1215ae2f080bSMitsuru IWASAKI 	ACPI_OBJECT		Arg;
1216ae2f080bSMitsuru IWASAKI 	ACPI_OBJECT_LIST	Args;
1217ae2f080bSMitsuru IWASAKI 	ACPI_STATUS		status;
1218ae2f080bSMitsuru IWASAKI 
1219ae2f080bSMitsuru IWASAKI 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
1220ae2f080bSMitsuru IWASAKI 	ACPI_SERIAL_ASSERT(ibm);
1221ae2f080bSMitsuru IWASAKI 
1222ae2f080bSMitsuru IWASAKI 	if (arg < 0 || arg > 7)
1223ae2f080bSMitsuru IWASAKI 		return (EINVAL);
1224ae2f080bSMitsuru IWASAKI 
1225ae2f080bSMitsuru IWASAKI 	/* Read the current brightness */
1226ae2f080bSMitsuru IWASAKI 	status = ACPI_EC_READ(sc->ec_dev, IBM_EC_BRIGHTNESS, &val_ec, 1);
1227ae2f080bSMitsuru IWASAKI 	if (ACPI_FAILURE(status))
1228ae2f080bSMitsuru IWASAKI 		return (status);
1229ae2f080bSMitsuru IWASAKI 
1230ae2f080bSMitsuru IWASAKI 	if (sc->cmos_handle) {
1231ae2f080bSMitsuru IWASAKI 		val = val_ec & IBM_EC_MASK_BRI;
1232ae2f080bSMitsuru IWASAKI 
1233ae2f080bSMitsuru IWASAKI 		Args.Count = 1;
1234ae2f080bSMitsuru IWASAKI 		Args.Pointer = &Arg;
1235ae2f080bSMitsuru IWASAKI 		Arg.Type = ACPI_TYPE_INTEGER;
1236ae2f080bSMitsuru IWASAKI 		Arg.Integer.Value = (arg > val) ? IBM_CMOS_BRIGHTNESS_UP :
1237ae2f080bSMitsuru IWASAKI 						  IBM_CMOS_BRIGHTNESS_DOWN;
1238ae2f080bSMitsuru IWASAKI 
1239ae2f080bSMitsuru IWASAKI 		step = (arg > val) ? 1 : -1;
1240ae2f080bSMitsuru IWASAKI 		for (int i = val; i != arg; i += step) {
1241ae2f080bSMitsuru IWASAKI 			status = AcpiEvaluateObject(sc->cmos_handle, NULL,
1242ae2f080bSMitsuru IWASAKI 						    &Args, NULL);
1243ae2f080bSMitsuru IWASAKI 			if (ACPI_FAILURE(status)) {
1244ae2f080bSMitsuru IWASAKI 				/* Record the last value */
1245ae2f080bSMitsuru IWASAKI 				if (i != val) {
1246ae2f080bSMitsuru IWASAKI 					ACPI_EC_WRITE(sc->ec_dev,
1247ae2f080bSMitsuru IWASAKI 					    IBM_EC_BRIGHTNESS, i - step, 1);
1248ae2f080bSMitsuru IWASAKI 				}
1249ae2f080bSMitsuru IWASAKI 				return (status);
1250ae2f080bSMitsuru IWASAKI 			}
1251ae2f080bSMitsuru IWASAKI 		}
1252ae2f080bSMitsuru IWASAKI 	}
1253ae2f080bSMitsuru IWASAKI 
1254ae2f080bSMitsuru IWASAKI 	return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_BRIGHTNESS, arg, 1);
1255ae2f080bSMitsuru IWASAKI }
1256ae2f080bSMitsuru IWASAKI 
1257ae2f080bSMitsuru IWASAKI static int
1258ae2f080bSMitsuru IWASAKI acpi_ibm_bluetooth_set(struct acpi_ibm_softc *sc, int arg)
1259ae2f080bSMitsuru IWASAKI {
1260ae2f080bSMitsuru IWASAKI 	int			val;
1261ae2f080bSMitsuru IWASAKI 
1262ae2f080bSMitsuru IWASAKI 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
1263ae2f080bSMitsuru IWASAKI 	ACPI_SERIAL_ASSERT(ibm);
1264ae2f080bSMitsuru IWASAKI 
1265ae2f080bSMitsuru IWASAKI 	if (arg < 0 || arg > 1)
1266ae2f080bSMitsuru IWASAKI 		return (EINVAL);
1267ae2f080bSMitsuru IWASAKI 
1268ae2f080bSMitsuru IWASAKI 	val = (arg == 1) ? sc->wlan_bt_flags | IBM_NAME_MASK_BT :
1269ae2f080bSMitsuru IWASAKI 			   sc->wlan_bt_flags & (~IBM_NAME_MASK_BT);
1270ae2f080bSMitsuru IWASAKI 	return acpi_SetInteger(sc->handle, IBM_NAME_WLAN_BT_SET, val);
1271ae2f080bSMitsuru IWASAKI }
1272ae2f080bSMitsuru IWASAKI 
1273ae2f080bSMitsuru IWASAKI static int
1274ae2f080bSMitsuru IWASAKI acpi_ibm_thinklight_set(struct acpi_ibm_softc *sc, int arg)
1275ae2f080bSMitsuru IWASAKI {
1276ae2f080bSMitsuru IWASAKI 	ACPI_OBJECT		Arg;
1277ae2f080bSMitsuru IWASAKI 	ACPI_OBJECT_LIST	Args;
1278ae2f080bSMitsuru IWASAKI 	ACPI_STATUS		status;
1279ae2f080bSMitsuru IWASAKI 
1280ae2f080bSMitsuru IWASAKI 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
1281ae2f080bSMitsuru IWASAKI 	ACPI_SERIAL_ASSERT(ibm);
1282ae2f080bSMitsuru IWASAKI 
1283ae2f080bSMitsuru IWASAKI 	if (arg < 0 || arg > 1)
1284ae2f080bSMitsuru IWASAKI 		return (EINVAL);
1285ae2f080bSMitsuru IWASAKI 
1286ae2f080bSMitsuru IWASAKI 	if (sc->light_set_supported) {
1287ae2f080bSMitsuru IWASAKI 		Args.Count = 1;
1288ae2f080bSMitsuru IWASAKI 		Args.Pointer = &Arg;
1289ae2f080bSMitsuru IWASAKI 		Arg.Type = ACPI_TYPE_INTEGER;
1290ae2f080bSMitsuru IWASAKI 		Arg.Integer.Value = arg ? sc->light_cmd_on : sc->light_cmd_off;
1291ae2f080bSMitsuru IWASAKI 
1292ae2f080bSMitsuru IWASAKI 		status = AcpiEvaluateObject(sc->light_handle, NULL,
1293ae2f080bSMitsuru IWASAKI 					    &Args, NULL);
1294ae2f080bSMitsuru IWASAKI 		if (ACPI_SUCCESS(status))
1295ae2f080bSMitsuru IWASAKI 			sc->light_val = arg;
1296ae2f080bSMitsuru IWASAKI 		return (status);
1297ae2f080bSMitsuru IWASAKI 	}
1298ae2f080bSMitsuru IWASAKI 
1299ae2f080bSMitsuru IWASAKI 	return (0);
1300ae2f080bSMitsuru IWASAKI }
1301ae2f080bSMitsuru IWASAKI 
130202aeba83SPhilip Paeps /*
130302aeba83SPhilip Paeps  * Helper function to make a get or set ACPI call to the PrivacyGuard handle.
130402aeba83SPhilip Paeps  * Only meant to be used internally by the get/set functions below.
130502aeba83SPhilip Paeps  */
130602aeba83SPhilip Paeps static ACPI_STATUS
130766671c14SEd Maste acpi_ibm_privacyguard_acpi_call(struct acpi_ibm_softc *sc, bool write, int *arg)
130866671c14SEd Maste {
130902aeba83SPhilip Paeps 	ACPI_OBJECT		Arg;
131002aeba83SPhilip Paeps 	ACPI_OBJECT_LIST	Args;
131102aeba83SPhilip Paeps 	ACPI_STATUS		status;
131202aeba83SPhilip Paeps 	ACPI_OBJECT		out_obj;
131302aeba83SPhilip Paeps 	ACPI_BUFFER		result;
131402aeba83SPhilip Paeps 
131502aeba83SPhilip Paeps 	Arg.Type = ACPI_TYPE_INTEGER;
131602aeba83SPhilip Paeps 	Arg.Integer.Value = (write ? *arg : 0);
131702aeba83SPhilip Paeps 	Args.Count = 1;
131802aeba83SPhilip Paeps 	Args.Pointer = &Arg;
131902aeba83SPhilip Paeps 	result.Length = sizeof(out_obj);
132002aeba83SPhilip Paeps 	result.Pointer = &out_obj;
132102aeba83SPhilip Paeps 
132202aeba83SPhilip Paeps 	status = AcpiEvaluateObject(sc->handle,
132302aeba83SPhilip Paeps 	    (write ? IBM_NAME_PRIVACYGUARD_SET : IBM_NAME_PRIVACYGUARD_GET),
132402aeba83SPhilip Paeps 	    &Args, &result);
132502aeba83SPhilip Paeps 	if (ACPI_SUCCESS(status) && !write)
132602aeba83SPhilip Paeps 		*arg = out_obj.Integer.Value;
132702aeba83SPhilip Paeps 
132802aeba83SPhilip Paeps 	return (status);
132902aeba83SPhilip Paeps }
133002aeba83SPhilip Paeps 
133102aeba83SPhilip Paeps /*
133202aeba83SPhilip Paeps  * Returns -1 if the device is not present.
133302aeba83SPhilip Paeps  */
133402aeba83SPhilip Paeps static int
133502aeba83SPhilip Paeps acpi_ibm_privacyguard_get(struct acpi_ibm_softc *sc)
133602aeba83SPhilip Paeps {
133702aeba83SPhilip Paeps 	ACPI_STATUS status;
133802aeba83SPhilip Paeps 	int val;
133902aeba83SPhilip Paeps 
134002aeba83SPhilip Paeps 	status = acpi_ibm_privacyguard_acpi_call(sc, false, &val);
134102aeba83SPhilip Paeps 	if (ACPI_SUCCESS(status) &&
134202aeba83SPhilip Paeps 	    (val & IBM_FLAG_PRIVACYGUARD_DEVICE_PRESENT))
134302aeba83SPhilip Paeps 		return (val & IBM_FLAG_PRIVACYGUARD_ON);
134402aeba83SPhilip Paeps 
134502aeba83SPhilip Paeps 	return (-1);
134602aeba83SPhilip Paeps }
134702aeba83SPhilip Paeps 
134802aeba83SPhilip Paeps static ACPI_STATUS
134902aeba83SPhilip Paeps acpi_ibm_privacyguard_set(struct acpi_ibm_softc *sc, int arg)
135002aeba83SPhilip Paeps {
135102aeba83SPhilip Paeps 	if (arg < 0 || arg > 1)
135202aeba83SPhilip Paeps 		return (AE_BAD_PARAMETER);
135302aeba83SPhilip Paeps 
135402aeba83SPhilip Paeps 	return (acpi_ibm_privacyguard_acpi_call(sc, true, &arg));
135502aeba83SPhilip Paeps }
135602aeba83SPhilip Paeps 
1357ae2f080bSMitsuru IWASAKI static int
1358ae2f080bSMitsuru IWASAKI acpi_ibm_volume_set(struct acpi_ibm_softc *sc, int arg)
1359ae2f080bSMitsuru IWASAKI {
1360ae2f080bSMitsuru IWASAKI 	int			val, step;
1361ae2f080bSMitsuru IWASAKI 	UINT64			val_ec;
1362ae2f080bSMitsuru IWASAKI 	ACPI_OBJECT		Arg;
1363ae2f080bSMitsuru IWASAKI 	ACPI_OBJECT_LIST	Args;
1364ae2f080bSMitsuru IWASAKI 	ACPI_STATUS		status;
1365ae2f080bSMitsuru IWASAKI 
1366ae2f080bSMitsuru IWASAKI 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
1367ae2f080bSMitsuru IWASAKI 	ACPI_SERIAL_ASSERT(ibm);
1368ae2f080bSMitsuru IWASAKI 
1369ae2f080bSMitsuru IWASAKI 	if (arg < 0 || arg > 14)
1370ae2f080bSMitsuru IWASAKI 		return (EINVAL);
1371ae2f080bSMitsuru IWASAKI 
1372ae2f080bSMitsuru IWASAKI 	/* Read the current volume */
1373ae2f080bSMitsuru IWASAKI 	status = ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1);
1374ae2f080bSMitsuru IWASAKI 	if (ACPI_FAILURE(status))
1375ae2f080bSMitsuru IWASAKI 		return (status);
1376ae2f080bSMitsuru IWASAKI 
1377ae2f080bSMitsuru IWASAKI 	if (sc->cmos_handle) {
1378ae2f080bSMitsuru IWASAKI 		val = val_ec & IBM_EC_MASK_VOL;
1379ae2f080bSMitsuru IWASAKI 
1380ae2f080bSMitsuru IWASAKI 		Args.Count = 1;
1381ae2f080bSMitsuru IWASAKI 		Args.Pointer = &Arg;
1382ae2f080bSMitsuru IWASAKI 		Arg.Type = ACPI_TYPE_INTEGER;
1383ae2f080bSMitsuru IWASAKI 		Arg.Integer.Value = (arg > val) ? IBM_CMOS_VOLUME_UP :
1384ae2f080bSMitsuru IWASAKI 						  IBM_CMOS_VOLUME_DOWN;
1385ae2f080bSMitsuru IWASAKI 
1386ae2f080bSMitsuru IWASAKI 		step = (arg > val) ? 1 : -1;
1387ae2f080bSMitsuru IWASAKI 		for (int i = val; i != arg; i += step) {
1388ae2f080bSMitsuru IWASAKI 			status = AcpiEvaluateObject(sc->cmos_handle, NULL,
1389ae2f080bSMitsuru IWASAKI 						    &Args, NULL);
1390ae2f080bSMitsuru IWASAKI 			if (ACPI_FAILURE(status)) {
1391ae2f080bSMitsuru IWASAKI 				/* Record the last value */
1392ae2f080bSMitsuru IWASAKI 				if (i != val) {
1393ae2f080bSMitsuru IWASAKI 					val_ec = i - step +
1394ae2f080bSMitsuru IWASAKI 						 (val_ec & (~IBM_EC_MASK_VOL));
1395ae2f080bSMitsuru IWASAKI 					ACPI_EC_WRITE(sc->ec_dev, IBM_EC_VOLUME,
1396ae2f080bSMitsuru IWASAKI 						      val_ec, 1);
1397ae2f080bSMitsuru IWASAKI 				}
1398ae2f080bSMitsuru IWASAKI 				return (status);
1399ae2f080bSMitsuru IWASAKI 			}
1400ae2f080bSMitsuru IWASAKI 		}
1401ae2f080bSMitsuru IWASAKI 	}
1402ae2f080bSMitsuru IWASAKI 
1403ae2f080bSMitsuru IWASAKI 	val_ec = arg + (val_ec & (~IBM_EC_MASK_VOL));
1404ae2f080bSMitsuru IWASAKI 	return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_VOLUME, val_ec, 1);
1405ae2f080bSMitsuru IWASAKI }
1406ae2f080bSMitsuru IWASAKI 
1407ae2f080bSMitsuru IWASAKI static int
1408ae2f080bSMitsuru IWASAKI acpi_ibm_mute_set(struct acpi_ibm_softc *sc, int arg)
1409ae2f080bSMitsuru IWASAKI {
1410ae2f080bSMitsuru IWASAKI 	UINT64			val_ec;
1411ae2f080bSMitsuru IWASAKI 	ACPI_OBJECT		Arg;
1412ae2f080bSMitsuru IWASAKI 	ACPI_OBJECT_LIST	Args;
1413ae2f080bSMitsuru IWASAKI 	ACPI_STATUS		status;
1414ae2f080bSMitsuru IWASAKI 
1415ae2f080bSMitsuru IWASAKI 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
1416ae2f080bSMitsuru IWASAKI 	ACPI_SERIAL_ASSERT(ibm);
1417ae2f080bSMitsuru IWASAKI 
1418ae2f080bSMitsuru IWASAKI 	if (arg < 0 || arg > 1)
1419ae2f080bSMitsuru IWASAKI 		return (EINVAL);
1420ae2f080bSMitsuru IWASAKI 
1421ae2f080bSMitsuru IWASAKI 	status = ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1);
1422ae2f080bSMitsuru IWASAKI 	if (ACPI_FAILURE(status))
1423ae2f080bSMitsuru IWASAKI 		return (status);
1424ae2f080bSMitsuru IWASAKI 
1425ae2f080bSMitsuru IWASAKI 	if (sc->cmos_handle) {
1426ae2f080bSMitsuru IWASAKI 		Args.Count = 1;
1427ae2f080bSMitsuru IWASAKI 		Args.Pointer = &Arg;
1428ae2f080bSMitsuru IWASAKI 		Arg.Type = ACPI_TYPE_INTEGER;
1429ae2f080bSMitsuru IWASAKI 		Arg.Integer.Value = IBM_CMOS_VOLUME_MUTE;
1430ae2f080bSMitsuru IWASAKI 
1431ae2f080bSMitsuru IWASAKI 		status = AcpiEvaluateObject(sc->cmos_handle, NULL, &Args, NULL);
1432ae2f080bSMitsuru IWASAKI 		if (ACPI_FAILURE(status))
1433ae2f080bSMitsuru IWASAKI 			return (status);
1434ae2f080bSMitsuru IWASAKI 	}
1435ae2f080bSMitsuru IWASAKI 
1436ae2f080bSMitsuru IWASAKI 	val_ec = (arg == 1) ? val_ec | IBM_EC_MASK_MUTE :
1437ae2f080bSMitsuru IWASAKI 			      val_ec & (~IBM_EC_MASK_MUTE);
1438ae2f080bSMitsuru IWASAKI 	return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_VOLUME, val_ec, 1);
1439ae2f080bSMitsuru IWASAKI }
1440ae2f080bSMitsuru IWASAKI 
1441ae2f080bSMitsuru IWASAKI static void
1442ae2f080bSMitsuru IWASAKI acpi_ibm_eventhandler(struct acpi_ibm_softc *sc, int arg)
1443ae2f080bSMitsuru IWASAKI {
1444ae2f080bSMitsuru IWASAKI 	int			val;
1445ae2f080bSMitsuru IWASAKI 	UINT64			val_ec;
1446ae2f080bSMitsuru IWASAKI 	ACPI_STATUS		status;
1447ae2f080bSMitsuru IWASAKI 
1448ae2f080bSMitsuru IWASAKI 	ACPI_SERIAL_BEGIN(ibm);
1449ae2f080bSMitsuru IWASAKI 	switch (arg) {
1450ae2f080bSMitsuru IWASAKI 	case IBM_EVENT_SUSPEND_TO_RAM:
1451ae2f080bSMitsuru IWASAKI 		power_pm_suspend(POWER_SLEEP_STATE_SUSPEND);
1452ae2f080bSMitsuru IWASAKI 		break;
1453ae2f080bSMitsuru IWASAKI 
1454ae2f080bSMitsuru IWASAKI 	case IBM_EVENT_BLUETOOTH:
1455ae2f080bSMitsuru IWASAKI 		acpi_ibm_bluetooth_set(sc, (sc->wlan_bt_flags == 0));
1456ae2f080bSMitsuru IWASAKI 		break;
1457ae2f080bSMitsuru IWASAKI 
1458ae2f080bSMitsuru IWASAKI 	case IBM_EVENT_BRIGHTNESS_UP:
1459ae2f080bSMitsuru IWASAKI 	case IBM_EVENT_BRIGHTNESS_DOWN:
1460ae2f080bSMitsuru IWASAKI 		/* Read the current brightness */
1461ae2f080bSMitsuru IWASAKI 		status = ACPI_EC_READ(sc->ec_dev, IBM_EC_BRIGHTNESS,
1462ae2f080bSMitsuru IWASAKI 				      &val_ec, 1);
1463ae2f080bSMitsuru IWASAKI 		if (ACPI_FAILURE(status))
1464ae2f080bSMitsuru IWASAKI 			return;
1465ae2f080bSMitsuru IWASAKI 
1466ae2f080bSMitsuru IWASAKI 		val = val_ec & IBM_EC_MASK_BRI;
1467ae2f080bSMitsuru IWASAKI 		val = (arg == IBM_EVENT_BRIGHTNESS_UP) ? val + 1 : val - 1;
1468ae2f080bSMitsuru IWASAKI 		acpi_ibm_brightness_set(sc, val);
1469ae2f080bSMitsuru IWASAKI 		break;
1470ae2f080bSMitsuru IWASAKI 
1471ae2f080bSMitsuru IWASAKI 	case IBM_EVENT_THINKLIGHT:
1472ae2f080bSMitsuru IWASAKI 		acpi_ibm_thinklight_set(sc, (sc->light_val == 0));
1473ae2f080bSMitsuru IWASAKI 		break;
1474ae2f080bSMitsuru IWASAKI 
1475ae2f080bSMitsuru IWASAKI 	case IBM_EVENT_VOLUME_UP:
1476ae2f080bSMitsuru IWASAKI 	case IBM_EVENT_VOLUME_DOWN:
1477ae2f080bSMitsuru IWASAKI 		/* Read the current volume */
1478ae2f080bSMitsuru IWASAKI 		status = ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1);
1479ae2f080bSMitsuru IWASAKI 		if (ACPI_FAILURE(status))
1480ae2f080bSMitsuru IWASAKI 			return;
1481ae2f080bSMitsuru IWASAKI 
1482ae2f080bSMitsuru IWASAKI 		val = val_ec & IBM_EC_MASK_VOL;
1483ae2f080bSMitsuru IWASAKI 		val = (arg == IBM_EVENT_VOLUME_UP) ? val + 1 : val - 1;
1484ae2f080bSMitsuru IWASAKI 		acpi_ibm_volume_set(sc, val);
1485ae2f080bSMitsuru IWASAKI 		break;
1486ae2f080bSMitsuru IWASAKI 
1487ae2f080bSMitsuru IWASAKI 	case IBM_EVENT_MUTE:
1488ae2f080bSMitsuru IWASAKI 		/* Read the current value */
1489ae2f080bSMitsuru IWASAKI 		status = ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1);
1490ae2f080bSMitsuru IWASAKI 		if (ACPI_FAILURE(status))
1491ae2f080bSMitsuru IWASAKI 			return;
1492ae2f080bSMitsuru IWASAKI 
1493ae2f080bSMitsuru IWASAKI 		val = ((val_ec & IBM_EC_MASK_MUTE) == IBM_EC_MASK_MUTE);
1494ae2f080bSMitsuru IWASAKI 		acpi_ibm_mute_set(sc, (val == 0));
1495ae2f080bSMitsuru IWASAKI 		break;
1496ae2f080bSMitsuru IWASAKI 
1497ae2f080bSMitsuru IWASAKI 	default:
1498ae2f080bSMitsuru IWASAKI 		break;
1499ae2f080bSMitsuru IWASAKI 	}
1500ae2f080bSMitsuru IWASAKI 	ACPI_SERIAL_END(ibm);
1501ae2f080bSMitsuru IWASAKI }
1502ae2f080bSMitsuru IWASAKI 
1503a4bfd638SMarkus Brueffer static void
1504a4bfd638SMarkus Brueffer acpi_ibm_notify(ACPI_HANDLE h, UINT32 notify, void *context)
1505a4bfd638SMarkus Brueffer {
1506a4bfd638SMarkus Brueffer 	int		event, arg, type;
150706064893STakanori Watanabe 	device_t	dev = context;
150806064893STakanori Watanabe 	struct acpi_ibm_softc *sc = device_get_softc(dev);
150906064893STakanori Watanabe 
1510a4bfd638SMarkus Brueffer 	ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, notify);
1511a4bfd638SMarkus Brueffer 
1512a4bfd638SMarkus Brueffer 	if (notify != 0x80)
1513b98a0ecaSTakanori Watanabe 		device_printf(dev, "Unknown notify\n");
1514a4bfd638SMarkus Brueffer 
151506064893STakanori Watanabe 	for (;;) {
1516a4bfd638SMarkus Brueffer 		acpi_GetInteger(acpi_get_handle(dev), IBM_NAME_EVENTS_GET, &event);
1517a4bfd638SMarkus Brueffer 		if (event == 0)
151806064893STakanori Watanabe 			break;
151906064893STakanori Watanabe 
1520a4bfd638SMarkus Brueffer 		type = (event >> 12) & 0xf;
1521a4bfd638SMarkus Brueffer 		arg = event & 0xfff;
152206064893STakanori Watanabe 		switch (type) {
152306064893STakanori Watanabe 		case 1:
1524a4bfd638SMarkus Brueffer 			if (!(sc->events_availmask & (1 << (arg - 1)))) {
1525b98a0ecaSTakanori Watanabe 				device_printf(dev, "Unknown key %d\n", arg);
152606064893STakanori Watanabe 				break;
152706064893STakanori Watanabe 			}
1528a4bfd638SMarkus Brueffer 
1529ae2f080bSMitsuru IWASAKI 			/* Execute event handler */
1530ae2f080bSMitsuru IWASAKI 			if (sc->handler_events & (1 << (arg - 1)))
1531ae2f080bSMitsuru IWASAKI 				acpi_ibm_eventhandler(sc, (arg & 0xff));
1532*c21f5751SGleb Smirnoff #ifdef EVDEV_SUPPORT
1533*c21f5751SGleb Smirnoff 			else if ((arg & 0xff) == IBM_EVENT_BRIGHTNESS_UP ||
1534*c21f5751SGleb Smirnoff 			    (arg & 0xff) == IBM_EVENT_BRIGHTNESS_DOWN) {
1535*c21f5751SGleb Smirnoff 				uint16_t key;
1536*c21f5751SGleb Smirnoff 
1537*c21f5751SGleb Smirnoff 				key = arg == IBM_EVENT_BRIGHTNESS_UP ?
1538*c21f5751SGleb Smirnoff 				    KEY_BRIGHTNESSUP : KEY_BRIGHTNESSDOWN;
1539*c21f5751SGleb Smirnoff 				evdev_push_key(sc->evdev, key, 1);
1540*c21f5751SGleb Smirnoff 				evdev_sync(sc->evdev);
1541*c21f5751SGleb Smirnoff 				evdev_push_key(sc->evdev, key, 0);
1542*c21f5751SGleb Smirnoff 				evdev_sync(sc->evdev);
1543*c21f5751SGleb Smirnoff 			}
1544*c21f5751SGleb Smirnoff #endif
1545ae2f080bSMitsuru IWASAKI 
1546a4bfd638SMarkus Brueffer 			/* Notify devd(8) */
154706064893STakanori Watanabe 			acpi_UserNotify("IBM", h, (arg & 0xff));
154806064893STakanori Watanabe 			break;
154906064893STakanori Watanabe 		default:
155006064893STakanori Watanabe 			break;
155106064893STakanori Watanabe 		}
155206064893STakanori Watanabe 	}
155306064893STakanori Watanabe }
1554