Path: blob/main/sys/dev/acpi_support/acpi_asus_wmi.c
39534 views
/*-1* Copyright (c) 2012 Alexander Motin <[email protected]>2* All rights reserved.3*4* Redistribution and use in source and binary forms, with or without5* modification, are permitted provided that the following conditions6* are met:7* 1. Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer.9* 2. Redistributions in binary form must reproduce the above copyright10* notice, this list of conditions and the following disclaimer in the11* documentation and/or other materials provided with the distribution.12*13* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND14* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE15* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE16* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE17* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL18* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS19* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)20* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT21* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY22* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF23* SUCH DAMAGE.24*/2526#include <sys/cdefs.h>27#include "opt_acpi.h"28#include "opt_evdev.h"29#include <sys/param.h>30#include <sys/conf.h>31#include <sys/uio.h>32#include <sys/proc.h>33#include <sys/kernel.h>34#include <sys/bus.h>35#include <sys/sbuf.h>36#include <sys/module.h>37#include <sys/sysctl.h>3839#include <contrib/dev/acpica/include/acpi.h>40#include <contrib/dev/acpica/include/accommon.h>41#include <dev/acpica/acpivar.h>42#include "acpi_wmi_if.h"4344#include <dev/backlight/backlight.h>45#include "backlight_if.h"4647#ifdef EVDEV_SUPPORT48#include <dev/evdev/input.h>49#include <dev/evdev/evdev.h>50#define NO_KEY KEY_RESERVED51#endif5253#define _COMPONENT ACPI_OEM54ACPI_MODULE_NAME("ASUS-WMI")5556#define ACPI_ASUS_WMI_MGMT_GUID "97845ED0-4E6D-11DE-8A39-0800200C9A66"57#define ACPI_ASUS_WMI_EVENT_GUID "0B3CBB35-E3C2-45ED-91C2-4C5A6D195D1C"58#define ACPI_EEEPC_WMI_EVENT_GUID "ABBC0F72-8EA1-11D1-00A0-C90629100000"5960/* WMI Methods */61#define ASUS_WMI_METHODID_SPEC 0x4345505362#define ASUS_WMI_METHODID_SFUN 0x4E55465363#define ASUS_WMI_METHODID_DSTS 0x5354434464#define ASUS_WMI_METHODID_DSTS2 0x5354534465#define ASUS_WMI_METHODID_DEVS 0x5356454466#define ASUS_WMI_METHODID_INIT 0x54494E4967#define ASUS_WMI_METHODID_HKEY 0x59454B486869#define ASUS_WMI_UNSUPPORTED_METHOD 0xFFFFFFFE7071/* Wireless */72#define ASUS_WMI_DEVID_HW_SWITCH 0x0001000173#define ASUS_WMI_DEVID_WIRELESS_LED 0x0001000274#define ASUS_WMI_DEVID_CWAP 0x0001000375#define ASUS_WMI_DEVID_WLAN 0x0001001176#define ASUS_WMI_DEVID_BLUETOOTH 0x0001001377#define ASUS_WMI_DEVID_GPS 0x0001001578#define ASUS_WMI_DEVID_WIMAX 0x0001001779#define ASUS_WMI_DEVID_WWAN3G 0x0001001980#define ASUS_WMI_DEVID_UWB 0x000100218182/* LEDs */83#define ASUS_WMI_DEVID_LED1 0x0002001184#define ASUS_WMI_DEVID_LED2 0x0002001285#define ASUS_WMI_DEVID_LED3 0x0002001386#define ASUS_WMI_DEVID_LED4 0x0002001487#define ASUS_WMI_DEVID_LED5 0x0002001588#define ASUS_WMI_DEVID_LED6 0x000200168990/* Backlight and Brightness */91#define ASUS_WMI_DEVID_BACKLIGHT 0x0005001192#define ASUS_WMI_DEVID_BRIGHTNESS 0x0005001293#define ASUS_WMI_DEVID_KBD_BACKLIGHT 0x0005002194#define ASUS_WMI_DEVID_LIGHT_SENSOR 0x000500229596/* Misc */97#define ASUS_WMI_DEVID_CAMERA 0x0006001398#define ASUS_WMI_DEVID_CARDREADER 0x0008001399#define ASUS_WMI_DEVID_TOUCHPAD 0x00100011100#define ASUS_WMI_DEVID_TOUCHPAD_LED 0x00100012101#define ASUS_WMI_DEVID_TUF_RGB_MODE 0x00100056102#define ASUS_WMI_DEVID_THERMAL_CTRL 0x00110011103#define ASUS_WMI_DEVID_FAN_CTRL 0x00110012104#define ASUS_WMI_DEVID_PROCESSOR_STATE 0x00120012105#define ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY 0x00120075106107/* DSTS masks */108#define ASUS_WMI_DSTS_STATUS_BIT 0x00000001109#define ASUS_WMI_DSTS_UNKNOWN_BIT 0x00000002110#define ASUS_WMI_DSTS_PRESENCE_BIT 0x00010000111#define ASUS_WMI_DSTS_USER_BIT 0x00020000112#define ASUS_WMI_DSTS_BIOS_BIT 0x00040000113#define ASUS_WMI_DSTS_BRIGHTNESS_MASK 0x000000FF114#define ASUS_WMI_DSTS_MAX_BRIGTH_MASK 0x0000FF00115116/* Events */117#define ASUS_WMI_EVENT_QUEUE_SIZE 0x10118#define ASUS_WMI_EVENT_QUEUE_END 0x1119#define ASUS_WMI_EVENT_MASK 0xFFFF120#define ASUS_WMI_EVENT_VALUE_ATK 0xFF121122struct acpi_asus_wmi_softc {123device_t dev;124device_t wmi_dev;125const char *notify_guid;126struct sysctl_ctx_list *sysctl_ctx;127struct sysctl_oid *sysctl_tree;128int dsts_id;129int handle_keys;130bool event_queue;131struct cdev *kbd_bkl;132uint32_t kbd_bkl_level;133uint32_t tuf_rgb_mode;134uint32_t ttp_mode;135#ifdef EVDEV_SUPPORT136struct evdev_dev *evdev;137#endif138};139140static struct {141char *name;142int dev_id;143char *description;144int flag_rdonly;145} acpi_asus_wmi_sysctls[] = {146{147.name = "hw_switch",148.dev_id = ASUS_WMI_DEVID_HW_SWITCH,149.description = "hw_switch",150},151{152.name = "wireless_led",153.dev_id = ASUS_WMI_DEVID_WIRELESS_LED,154.description = "Wireless LED control",155},156{157.name = "cwap",158.dev_id = ASUS_WMI_DEVID_CWAP,159.description = "Alt+F2 function",160},161{162.name = "wlan",163.dev_id = ASUS_WMI_DEVID_WLAN,164.description = "WLAN power control",165},166{167.name = "bluetooth",168.dev_id = ASUS_WMI_DEVID_BLUETOOTH,169.description = "Bluetooth power control",170},171{172.name = "gps",173.dev_id = ASUS_WMI_DEVID_GPS,174.description = "GPS power control",175},176{177.name = "wimax",178.dev_id = ASUS_WMI_DEVID_WIMAX,179.description = "WiMAX power control",180},181{182.name = "wwan3g",183.dev_id = ASUS_WMI_DEVID_WWAN3G,184.description = "WWAN-3G power control",185},186{187.name = "uwb",188.dev_id = ASUS_WMI_DEVID_UWB,189.description = "UWB power control",190},191{192.name = "led1",193.dev_id = ASUS_WMI_DEVID_LED1,194.description = "LED1 control",195},196{197.name = "led2",198.dev_id = ASUS_WMI_DEVID_LED2,199.description = "LED2 control",200},201{202.name = "led3",203.dev_id = ASUS_WMI_DEVID_LED3,204.description = "LED3 control",205},206{207.name = "led4",208.dev_id = ASUS_WMI_DEVID_LED4,209.description = "LED4 control",210},211{212.name = "led5",213.dev_id = ASUS_WMI_DEVID_LED5,214.description = "LED5 control",215},216{217.name = "led6",218.dev_id = ASUS_WMI_DEVID_LED6,219.description = "LED6 control",220},221{222.name = "backlight",223.dev_id = ASUS_WMI_DEVID_BACKLIGHT,224.description = "LCD backlight on/off control",225},226{227.name = "brightness",228.dev_id = ASUS_WMI_DEVID_BRIGHTNESS,229.description = "LCD backlight brightness control",230},231{232.name = "kbd_backlight",233.dev_id = ASUS_WMI_DEVID_KBD_BACKLIGHT,234.description = "Keyboard backlight brightness control",235},236{237.name = "light_sensor",238.dev_id = ASUS_WMI_DEVID_LIGHT_SENSOR,239.description = "Ambient light sensor",240},241{242.name = "camera",243.dev_id = ASUS_WMI_DEVID_CAMERA,244.description = "Camera power control",245},246{247.name = "cardreader",248.dev_id = ASUS_WMI_DEVID_CARDREADER,249.description = "Cardreader power control",250},251{252.name = "touchpad",253.dev_id = ASUS_WMI_DEVID_TOUCHPAD,254.description = "Touchpad control",255},256{257.name = "touchpad_led",258.dev_id = ASUS_WMI_DEVID_TOUCHPAD_LED,259.description = "Touchpad LED control",260},261{262.name = "themperature",263.dev_id = ASUS_WMI_DEVID_THERMAL_CTRL,264.description = "Temperature (C)",265.flag_rdonly = 1266},267{268.name = "fan_speed",269.dev_id = ASUS_WMI_DEVID_FAN_CTRL,270.description = "Fan speed (0-3)",271.flag_rdonly = 1272},273{274.name = "processor_state",275.dev_id = ASUS_WMI_DEVID_PROCESSOR_STATE,276.flag_rdonly = 1277},278{279.name = "throttle_thermal_policy",280.dev_id = ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY,281.description = "Throttle Thermal Policy "282"(0 - default, 1 - overboost, 2 - silent)",283},284{ NULL, 0, NULL, 0 }285};286287#ifdef EVDEV_SUPPORT288static const struct {289UINT32 notify;290uint16_t key;291} acpi_asus_wmi_evdev_map[] = {292{ 0x20, KEY_BRIGHTNESSDOWN },293{ 0x2f, KEY_BRIGHTNESSUP },294{ 0x30, KEY_VOLUMEUP },295{ 0x31, KEY_VOLUMEDOWN },296{ 0x32, KEY_MUTE },297{ 0x35, KEY_SCREENLOCK },298{ 0x38, KEY_PROG3 }, /* Armoury Crate */299{ 0x40, KEY_PREVIOUSSONG },300{ 0x41, KEY_NEXTSONG },301{ 0x43, KEY_STOPCD }, /* Stop/Eject */302{ 0x45, KEY_PLAYPAUSE },303{ 0x4f, KEY_LEFTMETA }, /* Fn-locked "Windows" Key */304{ 0x4c, KEY_MEDIA }, /* WMP Key */305{ 0x50, KEY_EMAIL },306{ 0x51, KEY_WWW },307{ 0x55, KEY_CALC },308{ 0x57, NO_KEY }, /* Battery mode */309{ 0x58, NO_KEY }, /* AC mode */310{ 0x5C, KEY_F15 }, /* Power Gear key */311{ 0x5D, KEY_WLAN }, /* Wireless console Toggle */312{ 0x5E, KEY_WLAN }, /* Wireless console Enable */313{ 0x5F, KEY_WLAN }, /* Wireless console Disable */314{ 0x60, KEY_TOUCHPAD_ON },315{ 0x61, KEY_SWITCHVIDEOMODE }, /* SDSP LCD only */316{ 0x62, KEY_SWITCHVIDEOMODE }, /* SDSP CRT only */317{ 0x63, KEY_SWITCHVIDEOMODE }, /* SDSP LCD + CRT */318{ 0x64, KEY_SWITCHVIDEOMODE }, /* SDSP TV */319{ 0x65, KEY_SWITCHVIDEOMODE }, /* SDSP LCD + TV */320{ 0x66, KEY_SWITCHVIDEOMODE }, /* SDSP CRT + TV */321{ 0x67, KEY_SWITCHVIDEOMODE }, /* SDSP LCD + CRT + TV */322{ 0x6B, KEY_TOUCHPAD_TOGGLE },323{ 0x6E, NO_KEY }, /* Low Battery notification */324{ 0x71, KEY_F13 }, /* General-purpose button */325{ 0x79, NO_KEY }, /* Charger type dectection notification */326{ 0x7a, KEY_ALS_TOGGLE }, /* Ambient Light Sensor Toggle */327{ 0x7c, KEY_MICMUTE },328{ 0x7D, KEY_BLUETOOTH }, /* Bluetooth Enable */329{ 0x7E, KEY_BLUETOOTH }, /* Bluetooth Disable */330{ 0x82, KEY_CAMERA },331{ 0x86, KEY_PROG1 }, /* MyASUS Key */332{ 0x88, KEY_RFKILL }, /* Radio Toggle Key */333{ 0x8A, KEY_PROG1 }, /* Color enhancement mode */334{ 0x8C, KEY_SWITCHVIDEOMODE }, /* SDSP DVI only */335{ 0x8D, KEY_SWITCHVIDEOMODE }, /* SDSP LCD + DVI */336{ 0x8E, KEY_SWITCHVIDEOMODE }, /* SDSP CRT + DVI */337{ 0x8F, KEY_SWITCHVIDEOMODE }, /* SDSP TV + DVI */338{ 0x90, KEY_SWITCHVIDEOMODE }, /* SDSP LCD + CRT + DVI */339{ 0x91, KEY_SWITCHVIDEOMODE }, /* SDSP LCD + TV + DVI */340{ 0x92, KEY_SWITCHVIDEOMODE }, /* SDSP CRT + TV + DVI */341{ 0x93, KEY_SWITCHVIDEOMODE }, /* SDSP LCD + CRT + TV + DVI */342{ 0x95, KEY_MEDIA },343{ 0x99, KEY_PHONE }, /* Conflicts with fan mode switch */344{ 0xA0, KEY_SWITCHVIDEOMODE }, /* SDSP HDMI only */345{ 0xA1, KEY_SWITCHVIDEOMODE }, /* SDSP LCD + HDMI */346{ 0xA2, KEY_SWITCHVIDEOMODE }, /* SDSP CRT + HDMI */347{ 0xA3, KEY_SWITCHVIDEOMODE }, /* SDSP TV + HDMI */348{ 0xA4, KEY_SWITCHVIDEOMODE }, /* SDSP LCD + CRT + HDMI */349{ 0xA5, KEY_SWITCHVIDEOMODE }, /* SDSP LCD + TV + HDMI */350{ 0xA6, KEY_SWITCHVIDEOMODE }, /* SDSP CRT + TV + HDMI */351{ 0xA7, KEY_SWITCHVIDEOMODE }, /* SDSP LCD + CRT + TV + HDMI */352{ 0xAE, KEY_FN_F5 }, /* Fn+F5 fan mode on 2020+ */353{ 0xB3, KEY_PROG4 }, /* AURA */354{ 0xB5, KEY_CALC },355{ 0xC4, KEY_KBDILLUMUP },356{ 0xC5, KEY_KBDILLUMDOWN },357{ 0xC6, NO_KEY }, /* Ambient Light Sensor notification */358{ 0xFA, KEY_PROG2 }, /* Lid flip action */359{ 0xBD, KEY_PROG2 }, /* Lid flip action on ROG xflow laptops */360};361#endif362363ACPI_SERIAL_DECL(asus_wmi, "ASUS WMI device");364365static void acpi_asus_wmi_identify(driver_t *driver, device_t parent);366static int acpi_asus_wmi_probe(device_t dev);367static int acpi_asus_wmi_attach(device_t dev);368static int acpi_asus_wmi_detach(device_t dev);369static int acpi_asus_wmi_suspend(device_t dev);370static int acpi_asus_wmi_resume(device_t dev);371372static int acpi_asus_wmi_sysctl(SYSCTL_HANDLER_ARGS);373static int acpi_asus_wmi_sysctl_set(struct acpi_asus_wmi_softc *sc, int dev_id,374int arg, int oldarg);375static int acpi_asus_wmi_sysctl_get(struct acpi_asus_wmi_softc *sc, int dev_id);376static int acpi_asus_wmi_evaluate_method(device_t wmi_dev, int method,377UINT32 arg0, UINT32 arg1, UINT32 arg2, UINT32 *retval);378static int acpi_wpi_asus_get_devstate(struct acpi_asus_wmi_softc *sc,379UINT32 dev_id, UINT32 *retval);380static int acpi_wpi_asus_set_devstate(struct acpi_asus_wmi_softc *sc,381UINT32 dev_id, UINT32 ctrl_param, UINT32 *retval);382static int acpi_asus_wmi_get_event_code(device_t wmi_dev, UINT32 notify,383int *code);384static void acpi_asus_wmi_notify(ACPI_HANDLE h, UINT32 notify, void *context);385static int acpi_asus_wmi_backlight_update_status(device_t dev,386struct backlight_props *props);387static int acpi_asus_wmi_backlight_get_status(device_t dev,388struct backlight_props *props);389static int acpi_asus_wmi_backlight_get_info(device_t dev,390struct backlight_info *info);391392static device_method_t acpi_asus_wmi_methods[] = {393/* Device interface */394DEVMETHOD(device_identify, acpi_asus_wmi_identify),395DEVMETHOD(device_probe, acpi_asus_wmi_probe),396DEVMETHOD(device_attach, acpi_asus_wmi_attach),397DEVMETHOD(device_detach, acpi_asus_wmi_detach),398DEVMETHOD(device_suspend, acpi_asus_wmi_suspend),399DEVMETHOD(device_resume, acpi_asus_wmi_resume),400401/* Backlight interface */402DEVMETHOD(backlight_update_status, acpi_asus_wmi_backlight_update_status),403DEVMETHOD(backlight_get_status, acpi_asus_wmi_backlight_get_status),404DEVMETHOD(backlight_get_info, acpi_asus_wmi_backlight_get_info),405406DEVMETHOD_END407};408409static driver_t acpi_asus_wmi_driver = {410"acpi_asus_wmi",411acpi_asus_wmi_methods,412sizeof(struct acpi_asus_wmi_softc),413};414415DRIVER_MODULE(acpi_asus_wmi, acpi_wmi, acpi_asus_wmi_driver, 0, 0);416MODULE_DEPEND(acpi_asus_wmi, acpi_wmi, 1, 1, 1);417MODULE_DEPEND(acpi_asus_wmi, acpi, 1, 1, 1);418MODULE_DEPEND(acpi_asus_wmi, backlight, 1, 1, 1);419#ifdef EVDEV_SUPPORT420MODULE_DEPEND(acpi_asus_wmi, evdev, 1, 1, 1);421#endif422423static const uint32_t acpi_asus_wmi_backlight_levels[] = { 0, 33, 66, 100 };424425static inline uint32_t426devstate_to_kbd_bkl_level(UINT32 val)427{428return (acpi_asus_wmi_backlight_levels[val & 0x3]);429}430431static inline UINT32432kbd_bkl_level_to_devstate(uint32_t bkl)433{434UINT32 val;435int i;436437for (i = 0; i < nitems(acpi_asus_wmi_backlight_levels); i++) {438if (bkl < acpi_asus_wmi_backlight_levels[i])439break;440}441val = (i - 1) & 0x3;442if (val != 0)443val |= 0x80;444return(val);445}446447static void448acpi_asus_wmi_identify(driver_t *driver, device_t parent)449{450451/* Don't do anything if driver is disabled. */452if (acpi_disabled("asus_wmi"))453return;454455/* Add only a single device instance. */456if (device_find_child(parent, "acpi_asus_wmi", DEVICE_UNIT_ANY) != NULL)457return;458459/* Check management GUID to see whether system is compatible. */460if (!ACPI_WMI_PROVIDES_GUID_STRING(parent,461ACPI_ASUS_WMI_MGMT_GUID))462return;463464if (BUS_ADD_CHILD(parent, 0, "acpi_asus_wmi", DEVICE_UNIT_ANY) == NULL)465device_printf(parent, "add acpi_asus_wmi child failed\n");466}467468static int469acpi_asus_wmi_probe(device_t dev)470{471472if (!ACPI_WMI_PROVIDES_GUID_STRING(device_get_parent(dev),473ACPI_ASUS_WMI_MGMT_GUID))474return (EINVAL);475device_set_desc(dev, "ASUS WMI device");476return (0);477}478479static int480acpi_asus_wmi_attach(device_t dev)481{482struct acpi_asus_wmi_softc *sc;483UINT32 val;484int dev_id, i, code;485bool have_kbd_bkl = false;486487ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);488489sc = device_get_softc(dev);490sc->dev = dev;491sc->wmi_dev = device_get_parent(dev);492sc->handle_keys = 1;493494/* Check management GUID. */495if (!ACPI_WMI_PROVIDES_GUID_STRING(sc->wmi_dev,496ACPI_ASUS_WMI_MGMT_GUID)) {497device_printf(dev,498"WMI device does not provide the ASUS management GUID\n");499return (EINVAL);500}501502/* Find proper DSTS method. */503sc->dsts_id = ASUS_WMI_METHODID_DSTS;504next:505for (i = 0; acpi_asus_wmi_sysctls[i].name != NULL; ++i) {506dev_id = acpi_asus_wmi_sysctls[i].dev_id;507if (acpi_wpi_asus_get_devstate(sc, dev_id, &val))508continue;509break;510}511if (acpi_asus_wmi_sysctls[i].name == NULL) {512if (sc->dsts_id == ASUS_WMI_METHODID_DSTS) {513sc->dsts_id = ASUS_WMI_METHODID_DSTS2;514goto next;515} else {516device_printf(dev, "Can not detect DSTS method ID\n");517return (EINVAL);518}519}520521/* Find proper and attach to notufy GUID. */522if (ACPI_WMI_PROVIDES_GUID_STRING(sc->wmi_dev,523ACPI_ASUS_WMI_EVENT_GUID))524sc->notify_guid = ACPI_ASUS_WMI_EVENT_GUID;525else if (ACPI_WMI_PROVIDES_GUID_STRING(sc->wmi_dev,526ACPI_EEEPC_WMI_EVENT_GUID))527sc->notify_guid = ACPI_EEEPC_WMI_EVENT_GUID;528else529sc->notify_guid = NULL;530if (sc->notify_guid != NULL) {531if (ACPI_WMI_INSTALL_EVENT_HANDLER(sc->wmi_dev,532sc->notify_guid, acpi_asus_wmi_notify, dev))533sc->notify_guid = NULL;534}535if (sc->notify_guid == NULL)536device_printf(dev, "Could not install event handler!\n");537538/* Initialize. */539if (!acpi_asus_wmi_evaluate_method(sc->wmi_dev,540ASUS_WMI_METHODID_INIT, 0, 0, 0, &val) && bootverbose)541device_printf(dev, "Initialization: %#x\n", val);542if (!acpi_asus_wmi_evaluate_method(sc->wmi_dev,543ASUS_WMI_METHODID_SPEC, 0, 0x9, 0, &val) && bootverbose)544device_printf(dev, "WMI BIOS version: %d.%d\n",545val >> 16, val & 0xFF);546if (!acpi_asus_wmi_evaluate_method(sc->wmi_dev,547ASUS_WMI_METHODID_SFUN, 0, 0, 0, &val) && bootverbose)548device_printf(dev, "SFUN value: %#x\n", val);549550ACPI_SERIAL_BEGIN(asus_wmi);551552sc->sysctl_ctx = device_get_sysctl_ctx(dev);553sc->sysctl_tree = device_get_sysctl_tree(dev);554SYSCTL_ADD_INT(sc->sysctl_ctx,555SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,556"handle_keys", CTLFLAG_RW, &sc->handle_keys,5570, "Handle some hardware keys inside the driver");558for (i = 0; acpi_asus_wmi_sysctls[i].name != NULL; ++i) {559dev_id = acpi_asus_wmi_sysctls[i].dev_id;560if (acpi_wpi_asus_get_devstate(sc, dev_id, &val))561continue;562switch (dev_id) {563case ASUS_WMI_DEVID_THERMAL_CTRL:564case ASUS_WMI_DEVID_PROCESSOR_STATE:565case ASUS_WMI_DEVID_FAN_CTRL:566case ASUS_WMI_DEVID_BRIGHTNESS:567if (val == 0)568continue;569break;570case ASUS_WMI_DEVID_KBD_BACKLIGHT:571sc->kbd_bkl_level = devstate_to_kbd_bkl_level(val);572have_kbd_bkl = true;573/* FALLTHROUGH */574default:575if ((val & ASUS_WMI_DSTS_PRESENCE_BIT) == 0)576continue;577break;578}579580if (acpi_asus_wmi_sysctls[i].flag_rdonly != 0) {581SYSCTL_ADD_PROC(sc->sysctl_ctx,582SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,583acpi_asus_wmi_sysctls[i].name,584CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,585sc, i, acpi_asus_wmi_sysctl, "I",586acpi_asus_wmi_sysctls[i].description);587} else {588SYSCTL_ADD_PROC(sc->sysctl_ctx,589SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,590acpi_asus_wmi_sysctls[i].name,591CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,592sc, i, acpi_asus_wmi_sysctl, "I",593acpi_asus_wmi_sysctls[i].description);594}595}596ACPI_SERIAL_END(asus_wmi);597598/* Detect and flush event queue */599if (sc->dsts_id == ASUS_WMI_METHODID_DSTS2) {600for (i = 0; i <= ASUS_WMI_EVENT_QUEUE_SIZE; i++) {601if (acpi_asus_wmi_get_event_code(sc->wmi_dev,602ASUS_WMI_EVENT_VALUE_ATK, &code) != 0) {603device_printf(dev,604"Can not flush event queue\n");605break;606}607if (code == ASUS_WMI_EVENT_QUEUE_END ||608code == ASUS_WMI_EVENT_MASK) {609sc->event_queue = true;610break;611}612}613}614615#ifdef EVDEV_SUPPORT616if (sc->notify_guid != NULL) {617sc->evdev = evdev_alloc();618evdev_set_name(sc->evdev, device_get_desc(dev));619evdev_set_phys(sc->evdev, device_get_nameunit(dev));620evdev_set_id(sc->evdev, BUS_HOST, 0, 0, 1);621evdev_support_event(sc->evdev, EV_SYN);622evdev_support_event(sc->evdev, EV_KEY);623for (i = 0; i < nitems(acpi_asus_wmi_evdev_map); i++) {624if (acpi_asus_wmi_evdev_map[i].key != NO_KEY)625evdev_support_key(sc->evdev,626acpi_asus_wmi_evdev_map[i].key);627}628629if (evdev_register(sc->evdev) != 0) {630device_printf(dev, "Can not register evdev\n");631acpi_asus_wmi_detach(dev);632return (ENXIO);633}634}635#endif636637if (have_kbd_bkl) {638sc->kbd_bkl = backlight_register("acpi_asus_wmi", dev);639if (sc->kbd_bkl == NULL) {640device_printf(dev, "Can not register backlight\n");641acpi_asus_wmi_detach(dev);642return (ENXIO);643}644}645646return (0);647}648649static int650acpi_asus_wmi_detach(device_t dev)651{652struct acpi_asus_wmi_softc *sc = device_get_softc(dev);653654ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);655656if (sc->kbd_bkl != NULL)657backlight_destroy(sc->kbd_bkl);658659if (sc->notify_guid) {660ACPI_WMI_REMOVE_EVENT_HANDLER(dev, sc->notify_guid);661#ifdef EVDEV_SUPPORT662evdev_free(sc->evdev);663#endif664}665666return (0);667}668669static int670acpi_asus_wmi_suspend(device_t dev)671{672struct acpi_asus_wmi_softc *sc = device_get_softc(dev);673674if (sc->kbd_bkl != NULL) {675ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);676acpi_wpi_asus_set_devstate(sc,677ASUS_WMI_DEVID_KBD_BACKLIGHT, 0, NULL);678}679680return (0);681}682683static int684acpi_asus_wmi_resume(device_t dev)685{686struct acpi_asus_wmi_softc *sc = device_get_softc(dev);687688if (sc->kbd_bkl != NULL) {689ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);690acpi_wpi_asus_set_devstate(sc, ASUS_WMI_DEVID_KBD_BACKLIGHT,691kbd_bkl_level_to_devstate(sc->kbd_bkl_level), NULL);692}693694return (0);695}696697static int698acpi_asus_wmi_sysctl(SYSCTL_HANDLER_ARGS)699{700struct acpi_asus_wmi_softc *sc;701int arg;702int oldarg;703int error = 0;704int function;705int dev_id;706707ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);708709sc = (struct acpi_asus_wmi_softc *)oidp->oid_arg1;710function = oidp->oid_arg2;711dev_id = acpi_asus_wmi_sysctls[function].dev_id;712713ACPI_SERIAL_BEGIN(asus_wmi);714arg = acpi_asus_wmi_sysctl_get(sc, dev_id);715oldarg = arg;716error = sysctl_handle_int(oidp, &arg, 0, req);717if (!error && req->newptr != NULL)718error = acpi_asus_wmi_sysctl_set(sc, dev_id, arg, oldarg);719ACPI_SERIAL_END(asus_wmi);720721return (error);722}723724static int725acpi_asus_wmi_sysctl_get(struct acpi_asus_wmi_softc *sc, int dev_id)726{727UINT32 val = 0;728729ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);730ACPI_SERIAL_ASSERT(asus_wmi);731732switch(dev_id) {733case ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY:734return (sc->ttp_mode);735default:736break;737}738739acpi_wpi_asus_get_devstate(sc, dev_id, &val);740741switch(dev_id) {742case ASUS_WMI_DEVID_THERMAL_CTRL:743val = (val - 2731 + 5) / 10;744break;745case ASUS_WMI_DEVID_PROCESSOR_STATE:746case ASUS_WMI_DEVID_FAN_CTRL:747break;748case ASUS_WMI_DEVID_BRIGHTNESS:749val &= ASUS_WMI_DSTS_BRIGHTNESS_MASK;750break;751case ASUS_WMI_DEVID_KBD_BACKLIGHT:752val &= 0x3;753break;754default:755if (val & ASUS_WMI_DSTS_UNKNOWN_BIT)756val = -1;757else758val = !!(val & ASUS_WMI_DSTS_STATUS_BIT);759break;760}761762return (val);763}764765static int766acpi_asus_wmi_sysctl_set(struct acpi_asus_wmi_softc *sc, int dev_id, int arg, int oldarg)767{768ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);769ACPI_SERIAL_ASSERT(asus_wmi);770771switch(dev_id) {772case ASUS_WMI_DEVID_KBD_BACKLIGHT:773arg = min(0x3, arg);774if (arg != 0)775arg |= 0x80;776break;777case ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY:778arg = min(0x2, arg);779sc->ttp_mode = arg;780break;781}782783acpi_wpi_asus_set_devstate(sc, dev_id, arg, NULL);784785return (0);786}787788static __inline void789acpi_asus_wmi_free_buffer(ACPI_BUFFER* buf) {790if (buf && buf->Pointer) {791AcpiOsFree(buf->Pointer);792}793}794795static int796acpi_asus_wmi_get_event_code(device_t wmi_dev, UINT32 notify, int *code)797{798ACPI_BUFFER response = { ACPI_ALLOCATE_BUFFER, NULL };799ACPI_OBJECT *obj;800int error = 0;801802if (ACPI_FAILURE(ACPI_WMI_GET_EVENT_DATA(wmi_dev, notify, &response)))803return (EIO);804obj = (ACPI_OBJECT*) response.Pointer;805if (obj && obj->Type == ACPI_TYPE_INTEGER)806*code = obj->Integer.Value & ASUS_WMI_EVENT_MASK;807else808error = EINVAL;809acpi_asus_wmi_free_buffer(&response);810return (error);811}812813#ifdef EVDEV_SUPPORT814static void815acpi_asus_wmi_push_evdev_event(struct evdev_dev *evdev, UINT32 notify)816{817int i;818uint16_t key;819820for (i = 0; i < nitems(acpi_asus_wmi_evdev_map); i++) {821if (acpi_asus_wmi_evdev_map[i].notify == notify &&822acpi_asus_wmi_evdev_map[i].key != NO_KEY) {823key = acpi_asus_wmi_evdev_map[i].key;824evdev_push_key(evdev, key, 1);825evdev_sync(evdev);826evdev_push_key(evdev, key, 0);827evdev_sync(evdev);828break;829}830}831}832#endif833834static void835acpi_asus_wmi_handle_event(struct acpi_asus_wmi_softc *sc, int code)836{837UINT32 val;838839if (code != 0) {840acpi_UserNotify("ASUS", ACPI_ROOT_OBJECT,841code);842#ifdef EVDEV_SUPPORT843acpi_asus_wmi_push_evdev_event(sc->evdev, code);844#endif845}846if (code && sc->handle_keys) {847/* Keyboard backlight control. */848if (code == 0xc4 || code == 0xc5) {849acpi_wpi_asus_get_devstate(sc,850ASUS_WMI_DEVID_KBD_BACKLIGHT, &val);851val &= 0x3;852if (code == 0xc4) {853if (val < 0x3)854val++;855} else if (val > 0)856val--;857if (val != 0)858val |= 0x80;859acpi_wpi_asus_set_devstate(sc,860ASUS_WMI_DEVID_KBD_BACKLIGHT, val, NULL);861sc->kbd_bkl_level = devstate_to_kbd_bkl_level(val);862}863/* Touchpad control. */864if (code == 0x6b) {865acpi_wpi_asus_get_devstate(sc,866ASUS_WMI_DEVID_TOUCHPAD, &val);867val = !(val & 1);868acpi_wpi_asus_set_devstate(sc,869ASUS_WMI_DEVID_TOUCHPAD, val, NULL);870}871/* Throttle thermal policy control. */872if (code == 0xae) {873sc->ttp_mode++;874if (sc->ttp_mode > 2)875sc->ttp_mode = 0;876acpi_wpi_asus_set_devstate(sc,877ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY,878sc->ttp_mode, NULL);879}880/* TUF laptop RGB mode control. */881if (code == 0xb3) {882const uint32_t cmd = 0xb4; /* Save to BIOS */883const uint32_t r = 0xff, g = 0xff, b = 0xff;884const uint32_t speed = 0xeb; /* Medium */885if (sc->tuf_rgb_mode < 2)886sc->tuf_rgb_mode++;887else if (sc->tuf_rgb_mode == 2)888sc->tuf_rgb_mode = 10;889else sc->tuf_rgb_mode = 0;890acpi_asus_wmi_evaluate_method(sc->wmi_dev,891ASUS_WMI_METHODID_DEVS,892ASUS_WMI_DEVID_TUF_RGB_MODE,893cmd | (sc->tuf_rgb_mode << 8) | (r << 16) | (g << 24),894b | (speed << 8), NULL);895}896}897}898899static void900acpi_asus_wmi_notify(ACPI_HANDLE h, UINT32 notify, void *context)901{902device_t dev = context;903struct acpi_asus_wmi_softc *sc = device_get_softc(dev);904int code = 0, i = 1;905906ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, notify);907908if (sc->event_queue)909i += ASUS_WMI_EVENT_QUEUE_SIZE;910do {911if (acpi_asus_wmi_get_event_code(sc->wmi_dev, notify, &code)912!= 0) {913device_printf(dev, "Failed to get event code\n");914return;915}916if (code == ASUS_WMI_EVENT_QUEUE_END ||917code == ASUS_WMI_EVENT_MASK)918return;919acpi_asus_wmi_handle_event(sc, code);920if (notify != ASUS_WMI_EVENT_VALUE_ATK)921return;922} while (--i != 0);923if (sc->event_queue)924device_printf(dev, "Can not read event queue, "925"last code: 0x%x\n", code);926}927928static int929acpi_asus_wmi_evaluate_method(device_t wmi_dev, int method,930UINT32 arg0, UINT32 arg1, UINT32 arg2, UINT32 *retval)931{932UINT32 params[3] = { arg0, arg1, arg2 };933UINT32 result;934ACPI_OBJECT *obj;935ACPI_BUFFER in = { sizeof(params), ¶ms };936ACPI_BUFFER out = { ACPI_ALLOCATE_BUFFER, NULL };937938if (ACPI_FAILURE(ACPI_WMI_EVALUATE_CALL(wmi_dev,939ACPI_ASUS_WMI_MGMT_GUID, 1, method, &in, &out))) {940acpi_asus_wmi_free_buffer(&out);941return (-EINVAL);942}943obj = out.Pointer;944if (obj && obj->Type == ACPI_TYPE_INTEGER)945result = (UINT32) obj->Integer.Value;946else947result = 0;948acpi_asus_wmi_free_buffer(&out);949if (retval)950*retval = result;951return (result == ASUS_WMI_UNSUPPORTED_METHOD ? -ENODEV : 0);952}953954static int955acpi_wpi_asus_get_devstate(struct acpi_asus_wmi_softc *sc,956UINT32 dev_id, UINT32 *retval)957{958959return (acpi_asus_wmi_evaluate_method(sc->wmi_dev,960sc->dsts_id, dev_id, 0, 0, retval));961}962963static int964acpi_wpi_asus_set_devstate(struct acpi_asus_wmi_softc *sc,965UINT32 dev_id, UINT32 ctrl_param, UINT32 *retval)966{967968return (acpi_asus_wmi_evaluate_method(sc->wmi_dev,969ASUS_WMI_METHODID_DEVS, dev_id, ctrl_param, 0, retval));970}971972static int973acpi_asus_wmi_backlight_update_status(device_t dev, struct backlight_props974*props)975{976struct acpi_asus_wmi_softc *sc = device_get_softc(dev);977978acpi_wpi_asus_set_devstate(sc, ASUS_WMI_DEVID_KBD_BACKLIGHT,979kbd_bkl_level_to_devstate(props->brightness), NULL);980sc->kbd_bkl_level = props->brightness;981982return (0);983}984985static int986acpi_asus_wmi_backlight_get_status(device_t dev, struct backlight_props *props)987{988struct acpi_asus_wmi_softc *sc = device_get_softc(dev);989990props->brightness = sc->kbd_bkl_level;991props->nlevels = nitems(acpi_asus_wmi_backlight_levels);992memcpy(props->levels, acpi_asus_wmi_backlight_levels,993sizeof(acpi_asus_wmi_backlight_levels));994995return (0);996}997998static int999acpi_asus_wmi_backlight_get_info(device_t dev, struct backlight_info *info)1000{1001info->type = BACKLIGHT_TYPE_KEYBOARD;1002strlcpy(info->name, "ASUS Keyboard", BACKLIGHTMAXNAMELENGTH);10031004return (0);1005}100610071008