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
acpi_status_to_errno(ACPI_STATUS status)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
ibm_led(void * softc,int onoff)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
ibm_led_task(struct acpi_ibm_softc * sc,int pending __unused)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
acpi_ibm_mic_led_set(struct acpi_ibm_softc * sc,int arg)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
acpi_ibm_probe(device_t dev)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
acpi_ibm_attach(device_t dev)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
acpi_ibm_detach(device_t dev)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
acpi_ibm_resume(device_t dev)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
acpi_ibm_eventmask_set(struct acpi_ibm_softc * sc,int val)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
acpi_ibm_sysctl(SYSCTL_HANDLER_ARGS)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
acpi_ibm_sysctl_get(struct acpi_ibm_softc * sc,int method)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
acpi_ibm_sysctl_set(struct acpi_ibm_softc * sc,int method,int arg)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
acpi_ibm_sysctl_init(struct acpi_ibm_softc * sc,int method)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
acpi_ibm_thermal_sysctl(SYSCTL_HANDLER_ARGS)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
acpi_ibm_handlerevents_sysctl(SYSCTL_HANDLER_ARGS)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
acpi_ibm_brightness_set(struct acpi_ibm_softc * sc,int arg)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
acpi_ibm_bluetooth_set(struct acpi_ibm_softc * sc,int arg)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
acpi_ibm_thinklight_set(struct acpi_ibm_softc * sc,int arg)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
acpi_ibm_privacyguard_acpi_call(struct acpi_ibm_softc * sc,bool write,int * arg)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
acpi_ibm_privacyguard_get(struct acpi_ibm_softc * sc)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
acpi_ibm_privacyguard_set(struct acpi_ibm_softc * sc,int arg)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
acpi_ibm_volume_set(struct acpi_ibm_softc * sc,int arg)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
acpi_ibm_mute_set(struct acpi_ibm_softc * sc,int arg)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
acpi_ibm_eventhandler(struct acpi_ibm_softc * sc,int arg)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
acpi_ibm_notify(ACPI_HANDLE h,UINT32 notify,void * context)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