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