diff --git a/share/man/man4/acpi_asus_wmi.4 b/share/man/man4/acpi_asus_wmi.4 index 7d6aeaca948b..721b73833484 100644 --- a/share/man/man4/acpi_asus_wmi.4 +++ b/share/man/man4/acpi_asus_wmi.4 @@ -1,88 +1,96 @@ .\" .\" Copyright (c) 2012 Alexander Motin .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd July 2, 2012 +.Dd March 25, 2024 .Dt ACPI_ASUS_WMI 4 .Os .Sh NAME .Nm acpi_asus_wmi .Nd Asus Laptop WMI Extras .Sh SYNOPSIS To compile this driver into the kernel, place the following line in your kernel configuration file: .Bd -ragged -offset indent .Cd "device acpi_asus_wmi" .Ed .Pp Alternatively, to load the driver as a module at boot time, place the following line in .Xr loader.conf 5 : .Bd -literal -offset indent acpi_asus_wmi_load="YES" .Ed .Sh DESCRIPTION The .Nm driver provides support for the extra WMI-controlled gadgets, such as hotkeys and leds, found on Asus laptops. It allows one to use the .Xr sysctl 8 interface to manipulate the brightness of the LCD panel and keyboard backlight, power on/off different internal components, such as WiFi, Bluetooth, camera, cardreader, etc, read some sensors. Hotkey events are passed to .Xr devd 8 for easy handling in userspace with the default configuration in .Pa /etc/devd/asus.conf . Some hotkey events, such as keyboard backlight and touchpad control, are handled inside the driver. .Sh SYSCTL VARIABLES The following sysctls are currently implemented: .Bl -tag -width indent .It Va dev.acpi_asus_wmi.0.handle_keys Specifies whether driver should handle some hardware keys, such as keyboard backlight, internally. .El .Pp Number of other variables under the same sysctl branch are model-specific. .Pp Defaults for these variables can be set in .Xr sysctl.conf 5 , which is parsed at boot-time. +.Sh FILES +.Bl -tag -width "/dev/backlight/acpi_asus_wmi0" -compact +.It Pa /dev/backlight/acpi_asus_wmi0 +Keyboard +.Xr backlight 8 +device node. +.El .Sh SEE ALSO .Xr acpi 4 , .Xr acpi_asus 4 , .Xr acpi_video 4 , .Xr sysctl.conf 5 , +.Xr backlight 8 , .Xr devd 8 , .Xr sysctl 8 .Sh HISTORY The .Nm driver first appeared in .Fx 10.0 . .Sh AUTHORS .An Alexander Motin Aq Mt mav@FreeBSD.org diff --git a/sys/dev/acpi_support/acpi_asus_wmi.c b/sys/dev/acpi_support/acpi_asus_wmi.c index 80beee2fea7f..968de5fe5e87 100644 --- a/sys/dev/acpi_support/acpi_asus_wmi.c +++ b/sys/dev/acpi_support/acpi_asus_wmi.c @@ -1,769 +1,897 @@ /*- * Copyright (c) 2012 Alexander Motin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include "opt_acpi.h" #include "opt_evdev.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "acpi_wmi_if.h" +#include +#include "backlight_if.h" + #ifdef EVDEV_SUPPORT #include #include #define NO_KEY KEY_RESERVED #endif #define _COMPONENT ACPI_OEM ACPI_MODULE_NAME("ASUS-WMI") #define ACPI_ASUS_WMI_MGMT_GUID "97845ED0-4E6D-11DE-8A39-0800200C9A66" #define ACPI_ASUS_WMI_EVENT_GUID "0B3CBB35-E3C2-45ED-91C2-4C5A6D195D1C" #define ACPI_EEEPC_WMI_EVENT_GUID "ABBC0F72-8EA1-11D1-00A0-C90629100000" /* WMI Methods */ #define ASUS_WMI_METHODID_SPEC 0x43455053 #define ASUS_WMI_METHODID_SFUN 0x4E554653 #define ASUS_WMI_METHODID_DSTS 0x53544344 #define ASUS_WMI_METHODID_DSTS2 0x53545344 #define ASUS_WMI_METHODID_DEVS 0x53564544 #define ASUS_WMI_METHODID_INIT 0x54494E49 #define ASUS_WMI_METHODID_HKEY 0x59454B48 #define ASUS_WMI_UNSUPPORTED_METHOD 0xFFFFFFFE /* Wireless */ #define ASUS_WMI_DEVID_HW_SWITCH 0x00010001 #define ASUS_WMI_DEVID_WIRELESS_LED 0x00010002 #define ASUS_WMI_DEVID_CWAP 0x00010003 #define ASUS_WMI_DEVID_WLAN 0x00010011 #define ASUS_WMI_DEVID_BLUETOOTH 0x00010013 #define ASUS_WMI_DEVID_GPS 0x00010015 #define ASUS_WMI_DEVID_WIMAX 0x00010017 #define ASUS_WMI_DEVID_WWAN3G 0x00010019 #define ASUS_WMI_DEVID_UWB 0x00010021 /* LEDs */ #define ASUS_WMI_DEVID_LED1 0x00020011 #define ASUS_WMI_DEVID_LED2 0x00020012 #define ASUS_WMI_DEVID_LED3 0x00020013 #define ASUS_WMI_DEVID_LED4 0x00020014 #define ASUS_WMI_DEVID_LED5 0x00020015 #define ASUS_WMI_DEVID_LED6 0x00020016 /* Backlight and Brightness */ #define ASUS_WMI_DEVID_BACKLIGHT 0x00050011 #define ASUS_WMI_DEVID_BRIGHTNESS 0x00050012 #define ASUS_WMI_DEVID_KBD_BACKLIGHT 0x00050021 #define ASUS_WMI_DEVID_LIGHT_SENSOR 0x00050022 /* Misc */ #define ASUS_WMI_DEVID_CAMERA 0x00060013 #define ASUS_WMI_DEVID_CARDREADER 0x00080013 #define ASUS_WMI_DEVID_TOUCHPAD 0x00100011 #define ASUS_WMI_DEVID_TOUCHPAD_LED 0x00100012 #define ASUS_WMI_DEVID_THERMAL_CTRL 0x00110011 #define ASUS_WMI_DEVID_FAN_CTRL 0x00110012 #define ASUS_WMI_DEVID_PROCESSOR_STATE 0x00120012 /* DSTS masks */ #define ASUS_WMI_DSTS_STATUS_BIT 0x00000001 #define ASUS_WMI_DSTS_UNKNOWN_BIT 0x00000002 #define ASUS_WMI_DSTS_PRESENCE_BIT 0x00010000 #define ASUS_WMI_DSTS_USER_BIT 0x00020000 #define ASUS_WMI_DSTS_BIOS_BIT 0x00040000 #define ASUS_WMI_DSTS_BRIGHTNESS_MASK 0x000000FF #define ASUS_WMI_DSTS_MAX_BRIGTH_MASK 0x0000FF00 struct acpi_asus_wmi_softc { device_t dev; device_t wmi_dev; const char *notify_guid; struct sysctl_ctx_list *sysctl_ctx; struct sysctl_oid *sysctl_tree; int dsts_id; int handle_keys; + struct cdev *kbd_bkl; + uint32_t kbd_bkl_level; #ifdef EVDEV_SUPPORT struct evdev_dev *evdev; #endif }; static struct { char *name; int dev_id; char *description; int flag_rdonly; } acpi_asus_wmi_sysctls[] = { { .name = "hw_switch", .dev_id = ASUS_WMI_DEVID_HW_SWITCH, .description = "hw_switch", }, { .name = "wireless_led", .dev_id = ASUS_WMI_DEVID_WIRELESS_LED, .description = "Wireless LED control", }, { .name = "cwap", .dev_id = ASUS_WMI_DEVID_CWAP, .description = "Alt+F2 function", }, { .name = "wlan", .dev_id = ASUS_WMI_DEVID_WLAN, .description = "WLAN power control", }, { .name = "bluetooth", .dev_id = ASUS_WMI_DEVID_BLUETOOTH, .description = "Bluetooth power control", }, { .name = "gps", .dev_id = ASUS_WMI_DEVID_GPS, .description = "GPS power control", }, { .name = "wimax", .dev_id = ASUS_WMI_DEVID_WIMAX, .description = "WiMAX power control", }, { .name = "wwan3g", .dev_id = ASUS_WMI_DEVID_WWAN3G, .description = "WWAN-3G power control", }, { .name = "uwb", .dev_id = ASUS_WMI_DEVID_UWB, .description = "UWB power control", }, { .name = "led1", .dev_id = ASUS_WMI_DEVID_LED1, .description = "LED1 control", }, { .name = "led2", .dev_id = ASUS_WMI_DEVID_LED2, .description = "LED2 control", }, { .name = "led3", .dev_id = ASUS_WMI_DEVID_LED3, .description = "LED3 control", }, { .name = "led4", .dev_id = ASUS_WMI_DEVID_LED4, .description = "LED4 control", }, { .name = "led5", .dev_id = ASUS_WMI_DEVID_LED5, .description = "LED5 control", }, { .name = "led6", .dev_id = ASUS_WMI_DEVID_LED6, .description = "LED6 control", }, { .name = "backlight", .dev_id = ASUS_WMI_DEVID_BACKLIGHT, .description = "LCD backlight on/off control", }, { .name = "brightness", .dev_id = ASUS_WMI_DEVID_BRIGHTNESS, .description = "LCD backlight brightness control", }, { .name = "kbd_backlight", .dev_id = ASUS_WMI_DEVID_KBD_BACKLIGHT, .description = "Keyboard backlight brightness control", }, { .name = "light_sensor", .dev_id = ASUS_WMI_DEVID_LIGHT_SENSOR, .description = "Ambient light sensor", }, { .name = "camera", .dev_id = ASUS_WMI_DEVID_CAMERA, .description = "Camera power control", }, { .name = "cardreader", .dev_id = ASUS_WMI_DEVID_CARDREADER, .description = "Cardreader power control", }, { .name = "touchpad", .dev_id = ASUS_WMI_DEVID_TOUCHPAD, .description = "Touchpad control", }, { .name = "touchpad_led", .dev_id = ASUS_WMI_DEVID_TOUCHPAD_LED, .description = "Touchpad LED control", }, { .name = "themperature", .dev_id = ASUS_WMI_DEVID_THERMAL_CTRL, .description = "Temperature (C)", .flag_rdonly = 1 }, { .name = "fan_speed", .dev_id = ASUS_WMI_DEVID_FAN_CTRL, .description = "Fan speed (0-3)", .flag_rdonly = 1 }, { .name = "processor_state", .dev_id = ASUS_WMI_DEVID_PROCESSOR_STATE, .flag_rdonly = 1 }, { NULL, 0, NULL, 0 } }; #ifdef EVDEV_SUPPORT static const struct { UINT32 notify; uint16_t key; } acpi_asus_wmi_evdev_map[] = { { 0x20, KEY_BRIGHTNESSDOWN }, { 0x2f, KEY_BRIGHTNESSUP }, { 0x30, KEY_VOLUMEUP }, { 0x31, KEY_VOLUMEDOWN }, { 0x32, KEY_MUTE }, { 0x35, KEY_SCREENLOCK }, { 0x38, KEY_PROG3 }, /* Armoury Crate */ { 0x40, KEY_PREVIOUSSONG }, { 0x41, KEY_NEXTSONG }, { 0x43, KEY_STOPCD }, /* Stop/Eject */ { 0x45, KEY_PLAYPAUSE }, { 0x4f, KEY_LEFTMETA }, /* Fn-locked "Windows" Key */ { 0x4c, KEY_MEDIA }, /* WMP Key */ { 0x50, KEY_EMAIL }, { 0x51, KEY_WWW }, { 0x55, KEY_CALC }, { 0x57, NO_KEY }, /* Battery mode */ { 0x58, NO_KEY }, /* AC mode */ { 0x5C, KEY_F15 }, /* Power Gear key */ { 0x5D, KEY_WLAN }, /* Wireless console Toggle */ { 0x5E, KEY_WLAN }, /* Wireless console Enable */ { 0x5F, KEY_WLAN }, /* Wireless console Disable */ { 0x60, KEY_TOUCHPAD_ON }, { 0x61, KEY_SWITCHVIDEOMODE }, /* SDSP LCD only */ { 0x62, KEY_SWITCHVIDEOMODE }, /* SDSP CRT only */ { 0x63, KEY_SWITCHVIDEOMODE }, /* SDSP LCD + CRT */ { 0x64, KEY_SWITCHVIDEOMODE }, /* SDSP TV */ { 0x65, KEY_SWITCHVIDEOMODE }, /* SDSP LCD + TV */ { 0x66, KEY_SWITCHVIDEOMODE }, /* SDSP CRT + TV */ { 0x67, KEY_SWITCHVIDEOMODE }, /* SDSP LCD + CRT + TV */ { 0x6B, KEY_TOUCHPAD_TOGGLE }, { 0x6E, NO_KEY }, /* Low Battery notification */ { 0x71, KEY_F13 }, /* General-purpose button */ { 0x79, NO_KEY }, /* Charger type dectection notification */ { 0x7a, KEY_ALS_TOGGLE }, /* Ambient Light Sensor Toggle */ { 0x7c, KEY_MICMUTE }, { 0x7D, KEY_BLUETOOTH }, /* Bluetooth Enable */ { 0x7E, KEY_BLUETOOTH }, /* Bluetooth Disable */ { 0x82, KEY_CAMERA }, { 0x86, KEY_PROG1 }, /* MyASUS Key */ { 0x88, KEY_RFKILL }, /* Radio Toggle Key */ { 0x8A, KEY_PROG1 }, /* Color enhancement mode */ { 0x8C, KEY_SWITCHVIDEOMODE }, /* SDSP DVI only */ { 0x8D, KEY_SWITCHVIDEOMODE }, /* SDSP LCD + DVI */ { 0x8E, KEY_SWITCHVIDEOMODE }, /* SDSP CRT + DVI */ { 0x8F, KEY_SWITCHVIDEOMODE }, /* SDSP TV + DVI */ { 0x90, KEY_SWITCHVIDEOMODE }, /* SDSP LCD + CRT + DVI */ { 0x91, KEY_SWITCHVIDEOMODE }, /* SDSP LCD + TV + DVI */ { 0x92, KEY_SWITCHVIDEOMODE }, /* SDSP CRT + TV + DVI */ { 0x93, KEY_SWITCHVIDEOMODE }, /* SDSP LCD + CRT + TV + DVI */ { 0x95, KEY_MEDIA }, { 0x99, KEY_PHONE }, /* Conflicts with fan mode switch */ { 0xA0, KEY_SWITCHVIDEOMODE }, /* SDSP HDMI only */ { 0xA1, KEY_SWITCHVIDEOMODE }, /* SDSP LCD + HDMI */ { 0xA2, KEY_SWITCHVIDEOMODE }, /* SDSP CRT + HDMI */ { 0xA3, KEY_SWITCHVIDEOMODE }, /* SDSP TV + HDMI */ { 0xA4, KEY_SWITCHVIDEOMODE }, /* SDSP LCD + CRT + HDMI */ { 0xA5, KEY_SWITCHVIDEOMODE }, /* SDSP LCD + TV + HDMI */ { 0xA6, KEY_SWITCHVIDEOMODE }, /* SDSP CRT + TV + HDMI */ { 0xA7, KEY_SWITCHVIDEOMODE }, /* SDSP LCD + CRT + TV + HDMI */ { 0xAE, KEY_FN_F5 }, /* Fn+F5 fan mode on 2020+ */ { 0xB3, KEY_PROG4 }, /* AURA */ { 0xB5, KEY_CALC }, { 0xC4, KEY_KBDILLUMUP }, { 0xC5, KEY_KBDILLUMDOWN }, { 0xC6, NO_KEY }, /* Ambient Light Sensor notification */ { 0xFA, KEY_PROG2 }, /* Lid flip action */ { 0xBD, KEY_PROG2 }, /* Lid flip action on ROG xflow laptops */ }; #endif ACPI_SERIAL_DECL(asus_wmi, "ASUS WMI device"); static void acpi_asus_wmi_identify(driver_t *driver, device_t parent); static int acpi_asus_wmi_probe(device_t dev); static int acpi_asus_wmi_attach(device_t dev); static int acpi_asus_wmi_detach(device_t dev); +static int acpi_asus_wmi_suspend(device_t dev); +static int acpi_asus_wmi_resume(device_t dev); static int acpi_asus_wmi_sysctl(SYSCTL_HANDLER_ARGS); static int acpi_asus_wmi_sysctl_set(struct acpi_asus_wmi_softc *sc, int dev_id, int arg, int oldarg); static int acpi_asus_wmi_sysctl_get(struct acpi_asus_wmi_softc *sc, int dev_id); static int acpi_asus_wmi_evaluate_method(device_t wmi_dev, int method, UINT32 arg0, UINT32 arg1, UINT32 *retval); static int acpi_wpi_asus_get_devstate(struct acpi_asus_wmi_softc *sc, UINT32 dev_id, UINT32 *retval); static int acpi_wpi_asus_set_devstate(struct acpi_asus_wmi_softc *sc, UINT32 dev_id, UINT32 ctrl_param, UINT32 *retval); static void acpi_asus_wmi_notify(ACPI_HANDLE h, UINT32 notify, void *context); +static int acpi_asus_wmi_backlight_update_status(device_t dev, + struct backlight_props *props); +static int acpi_asus_wmi_backlight_get_status(device_t dev, + struct backlight_props *props); +static int acpi_asus_wmi_backlight_get_info(device_t dev, + struct backlight_info *info); static device_method_t acpi_asus_wmi_methods[] = { + /* Device interface */ DEVMETHOD(device_identify, acpi_asus_wmi_identify), DEVMETHOD(device_probe, acpi_asus_wmi_probe), DEVMETHOD(device_attach, acpi_asus_wmi_attach), DEVMETHOD(device_detach, acpi_asus_wmi_detach), + DEVMETHOD(device_suspend, acpi_asus_wmi_suspend), + DEVMETHOD(device_resume, acpi_asus_wmi_resume), + + /* Backlight interface */ + DEVMETHOD(backlight_update_status, acpi_asus_wmi_backlight_update_status), + DEVMETHOD(backlight_get_status, acpi_asus_wmi_backlight_get_status), + DEVMETHOD(backlight_get_info, acpi_asus_wmi_backlight_get_info), DEVMETHOD_END }; static driver_t acpi_asus_wmi_driver = { "acpi_asus_wmi", acpi_asus_wmi_methods, sizeof(struct acpi_asus_wmi_softc), }; DRIVER_MODULE(acpi_asus_wmi, acpi_wmi, acpi_asus_wmi_driver, 0, 0); MODULE_DEPEND(acpi_asus_wmi, acpi_wmi, 1, 1, 1); MODULE_DEPEND(acpi_asus_wmi, acpi, 1, 1, 1); +MODULE_DEPEND(acpi_asus_wmi, backlight, 1, 1, 1); #ifdef EVDEV_SUPPORT MODULE_DEPEND(acpi_asus_wmi, evdev, 1, 1, 1); #endif +static const uint32_t acpi_asus_wmi_backlight_levels[] = { 0, 33, 66, 100 }; + +static inline uint32_t +devstate_to_kbd_bkl_level(UINT32 val) +{ + return (acpi_asus_wmi_backlight_levels[val & 0x3]); +} + +static inline UINT32 +kbd_bkl_level_to_devstate(uint32_t bkl) +{ + UINT32 val; + int i; + + for (i = 0; i < nitems(acpi_asus_wmi_backlight_levels); i++) { + if (bkl < acpi_asus_wmi_backlight_levels[i]) + break; + } + val = (i - 1) & 0x3; + if (val != 0) + val |= 0x80; + return(val); +} + static void acpi_asus_wmi_identify(driver_t *driver, device_t parent) { /* Don't do anything if driver is disabled. */ if (acpi_disabled("asus_wmi")) return; /* Add only a single device instance. */ if (device_find_child(parent, "acpi_asus_wmi", -1) != NULL) return; /* Check management GUID to see whether system is compatible. */ if (!ACPI_WMI_PROVIDES_GUID_STRING(parent, ACPI_ASUS_WMI_MGMT_GUID)) return; if (BUS_ADD_CHILD(parent, 0, "acpi_asus_wmi", -1) == NULL) device_printf(parent, "add acpi_asus_wmi child failed\n"); } static int acpi_asus_wmi_probe(device_t dev) { if (!ACPI_WMI_PROVIDES_GUID_STRING(device_get_parent(dev), ACPI_ASUS_WMI_MGMT_GUID)) return (EINVAL); device_set_desc(dev, "ASUS WMI device"); return (0); } static int acpi_asus_wmi_attach(device_t dev) { struct acpi_asus_wmi_softc *sc; UINT32 val; int dev_id, i; + bool have_kbd_bkl = false; ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__); sc = device_get_softc(dev); sc->dev = dev; sc->wmi_dev = device_get_parent(dev); sc->handle_keys = 1; /* Check management GUID. */ if (!ACPI_WMI_PROVIDES_GUID_STRING(sc->wmi_dev, ACPI_ASUS_WMI_MGMT_GUID)) { device_printf(dev, "WMI device does not provide the ASUS management GUID\n"); return (EINVAL); } /* Find proper DSTS method. */ sc->dsts_id = ASUS_WMI_METHODID_DSTS; next: for (i = 0; acpi_asus_wmi_sysctls[i].name != NULL; ++i) { dev_id = acpi_asus_wmi_sysctls[i].dev_id; if (acpi_wpi_asus_get_devstate(sc, dev_id, &val)) continue; break; } if (acpi_asus_wmi_sysctls[i].name == NULL) { if (sc->dsts_id == ASUS_WMI_METHODID_DSTS) { sc->dsts_id = ASUS_WMI_METHODID_DSTS2; goto next; } else { device_printf(dev, "Can not detect DSTS method ID\n"); return (EINVAL); } } /* Find proper and attach to notufy GUID. */ if (ACPI_WMI_PROVIDES_GUID_STRING(sc->wmi_dev, ACPI_ASUS_WMI_EVENT_GUID)) sc->notify_guid = ACPI_ASUS_WMI_EVENT_GUID; else if (ACPI_WMI_PROVIDES_GUID_STRING(sc->wmi_dev, ACPI_EEEPC_WMI_EVENT_GUID)) sc->notify_guid = ACPI_EEEPC_WMI_EVENT_GUID; else sc->notify_guid = NULL; if (sc->notify_guid != NULL) { if (ACPI_WMI_INSTALL_EVENT_HANDLER(sc->wmi_dev, sc->notify_guid, acpi_asus_wmi_notify, dev)) sc->notify_guid = NULL; } if (sc->notify_guid == NULL) device_printf(dev, "Could not install event handler!\n"); /* Initialize. */ if (!acpi_asus_wmi_evaluate_method(sc->wmi_dev, ASUS_WMI_METHODID_INIT, 0, 0, &val) && bootverbose) device_printf(dev, "Initialization: %#x\n", val); if (!acpi_asus_wmi_evaluate_method(sc->wmi_dev, ASUS_WMI_METHODID_SPEC, 0, 0x9, &val) && bootverbose) device_printf(dev, "WMI BIOS version: %d.%d\n", val >> 16, val & 0xFF); if (!acpi_asus_wmi_evaluate_method(sc->wmi_dev, ASUS_WMI_METHODID_SFUN, 0, 0, &val) && bootverbose) device_printf(dev, "SFUN value: %#x\n", val); ACPI_SERIAL_BEGIN(asus_wmi); sc->sysctl_ctx = device_get_sysctl_ctx(dev); sc->sysctl_tree = device_get_sysctl_tree(dev); SYSCTL_ADD_INT(sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "handle_keys", CTLFLAG_RW, &sc->handle_keys, 0, "Handle some hardware keys inside the driver"); for (i = 0; acpi_asus_wmi_sysctls[i].name != NULL; ++i) { dev_id = acpi_asus_wmi_sysctls[i].dev_id; if (acpi_wpi_asus_get_devstate(sc, dev_id, &val)) continue; switch (dev_id) { case ASUS_WMI_DEVID_THERMAL_CTRL: case ASUS_WMI_DEVID_PROCESSOR_STATE: case ASUS_WMI_DEVID_FAN_CTRL: case ASUS_WMI_DEVID_BRIGHTNESS: if (val == 0) continue; break; + case ASUS_WMI_DEVID_KBD_BACKLIGHT: + sc->kbd_bkl_level = devstate_to_kbd_bkl_level(val); + have_kbd_bkl = true; + /* FALLTHROUGH */ default: if ((val & ASUS_WMI_DSTS_PRESENCE_BIT) == 0) continue; break; } if (acpi_asus_wmi_sysctls[i].flag_rdonly != 0) { SYSCTL_ADD_PROC(sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, acpi_asus_wmi_sysctls[i].name, CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, i, acpi_asus_wmi_sysctl, "I", acpi_asus_wmi_sysctls[i].description); } else { SYSCTL_ADD_PROC(sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, acpi_asus_wmi_sysctls[i].name, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, i, acpi_asus_wmi_sysctl, "I", acpi_asus_wmi_sysctls[i].description); } } ACPI_SERIAL_END(asus_wmi); #ifdef EVDEV_SUPPORT if (sc->notify_guid != NULL) { sc->evdev = evdev_alloc(); evdev_set_name(sc->evdev, device_get_desc(dev)); evdev_set_phys(sc->evdev, device_get_nameunit(dev)); evdev_set_id(sc->evdev, BUS_HOST, 0, 0, 1); evdev_support_event(sc->evdev, EV_SYN); evdev_support_event(sc->evdev, EV_KEY); for (i = 0; i < nitems(acpi_asus_wmi_evdev_map); i++) { if (acpi_asus_wmi_evdev_map[i].key != NO_KEY) evdev_support_key(sc->evdev, acpi_asus_wmi_evdev_map[i].key); } if (evdev_register(sc->evdev) != 0) { + device_printf(dev, "Can not register evdev\n"); acpi_asus_wmi_detach(dev); return (ENXIO); } } #endif + if (have_kbd_bkl) { + sc->kbd_bkl = backlight_register("acpi_asus_wmi", dev); + if (sc->kbd_bkl == NULL) { + device_printf(dev, "Can not register backlight\n"); + acpi_asus_wmi_detach(dev); + return (ENXIO); + } + } + return (0); } static int acpi_asus_wmi_detach(device_t dev) { struct acpi_asus_wmi_softc *sc = device_get_softc(dev); ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__); + if (sc->kbd_bkl != NULL) + backlight_destroy(sc->kbd_bkl); + if (sc->notify_guid) { ACPI_WMI_REMOVE_EVENT_HANDLER(dev, sc->notify_guid); #ifdef EVDEV_SUPPORT evdev_free(sc->evdev); #endif } return (0); } +static int +acpi_asus_wmi_suspend(device_t dev) +{ + struct acpi_asus_wmi_softc *sc = device_get_softc(dev); + + if (sc->kbd_bkl != NULL) { + ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__); + acpi_wpi_asus_set_devstate(sc, + ASUS_WMI_DEVID_KBD_BACKLIGHT, 0, NULL); + } + + return (0); +} + +static int +acpi_asus_wmi_resume(device_t dev) +{ + struct acpi_asus_wmi_softc *sc = device_get_softc(dev); + + if (sc->kbd_bkl != NULL) { + ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__); + acpi_wpi_asus_set_devstate(sc, ASUS_WMI_DEVID_KBD_BACKLIGHT, + kbd_bkl_level_to_devstate(sc->kbd_bkl_level), NULL); + } + + return (0); +} + static int acpi_asus_wmi_sysctl(SYSCTL_HANDLER_ARGS) { struct acpi_asus_wmi_softc *sc; int arg; int oldarg; int error = 0; int function; int dev_id; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); sc = (struct acpi_asus_wmi_softc *)oidp->oid_arg1; function = oidp->oid_arg2; dev_id = acpi_asus_wmi_sysctls[function].dev_id; ACPI_SERIAL_BEGIN(asus_wmi); arg = acpi_asus_wmi_sysctl_get(sc, dev_id); oldarg = arg; error = sysctl_handle_int(oidp, &arg, 0, req); if (!error && req->newptr != NULL) error = acpi_asus_wmi_sysctl_set(sc, dev_id, arg, oldarg); ACPI_SERIAL_END(asus_wmi); return (error); } static int acpi_asus_wmi_sysctl_get(struct acpi_asus_wmi_softc *sc, int dev_id) { UINT32 val = 0; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); ACPI_SERIAL_ASSERT(asus_wmi); acpi_wpi_asus_get_devstate(sc, dev_id, &val); switch(dev_id) { case ASUS_WMI_DEVID_THERMAL_CTRL: val = (val - 2731 + 5) / 10; break; case ASUS_WMI_DEVID_PROCESSOR_STATE: case ASUS_WMI_DEVID_FAN_CTRL: break; case ASUS_WMI_DEVID_BRIGHTNESS: val &= ASUS_WMI_DSTS_BRIGHTNESS_MASK; break; case ASUS_WMI_DEVID_KBD_BACKLIGHT: - val &= 0x7; + val &= 0x3; break; default: if (val & ASUS_WMI_DSTS_UNKNOWN_BIT) val = -1; else val = !!(val & ASUS_WMI_DSTS_STATUS_BIT); break; } return (val); } static int acpi_asus_wmi_sysctl_set(struct acpi_asus_wmi_softc *sc, int dev_id, int arg, int oldarg) { ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); ACPI_SERIAL_ASSERT(asus_wmi); switch(dev_id) { case ASUS_WMI_DEVID_KBD_BACKLIGHT: - arg = min(0x7, arg); + arg = min(0x3, arg); if (arg != 0) arg |= 0x80; break; } acpi_wpi_asus_set_devstate(sc, dev_id, arg, NULL); return (0); } static __inline void acpi_asus_wmi_free_buffer(ACPI_BUFFER* buf) { if (buf && buf->Pointer) { AcpiOsFree(buf->Pointer); } } #ifdef EVDEV_SUPPORT static void acpi_asus_wmi_push_evdev_event(struct evdev_dev *evdev, UINT32 notify) { int i; uint16_t key; for (i = 0; i < nitems(acpi_asus_wmi_evdev_map); i++) { if (acpi_asus_wmi_evdev_map[i].notify == notify && acpi_asus_wmi_evdev_map[i].key != NO_KEY) { key = acpi_asus_wmi_evdev_map[i].key; evdev_push_key(evdev, key, 1); evdev_sync(evdev); evdev_push_key(evdev, key, 0); evdev_sync(evdev); break; } } } #endif static void acpi_asus_wmi_notify(ACPI_HANDLE h, UINT32 notify, void *context) { device_t dev = context; ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, notify); UINT32 val; int code = 0; struct acpi_asus_wmi_softc *sc = device_get_softc(dev); ACPI_BUFFER response = { ACPI_ALLOCATE_BUFFER, NULL }; ACPI_OBJECT *obj; ACPI_WMI_GET_EVENT_DATA(sc->wmi_dev, notify, &response); obj = (ACPI_OBJECT*) response.Pointer; if (obj && obj->Type == ACPI_TYPE_INTEGER) { code = obj->Integer.Value; acpi_UserNotify("ASUS", ACPI_ROOT_OBJECT, code); #ifdef EVDEV_SUPPORT acpi_asus_wmi_push_evdev_event(sc->evdev, code); #endif } if (code && sc->handle_keys) { /* Keyboard backlight control. */ if (code == 0xc4 || code == 0xc5) { acpi_wpi_asus_get_devstate(sc, ASUS_WMI_DEVID_KBD_BACKLIGHT, &val); - val &= 0x7; + val &= 0x3; if (code == 0xc4) { - if (val < 0x7) + if (val < 0x3) val++; } else if (val > 0) val--; if (val != 0) val |= 0x80; acpi_wpi_asus_set_devstate(sc, ASUS_WMI_DEVID_KBD_BACKLIGHT, val, NULL); + sc->kbd_bkl_level = devstate_to_kbd_bkl_level(val); } /* Touchpad control. */ if (code == 0x6b) { acpi_wpi_asus_get_devstate(sc, ASUS_WMI_DEVID_TOUCHPAD, &val); val = !(val & 1); acpi_wpi_asus_set_devstate(sc, ASUS_WMI_DEVID_TOUCHPAD, val, NULL); } } acpi_asus_wmi_free_buffer(&response); } static int acpi_asus_wmi_evaluate_method(device_t wmi_dev, int method, UINT32 arg0, UINT32 arg1, UINT32 *retval) { UINT32 params[2] = { arg0, arg1 }; UINT32 result; ACPI_OBJECT *obj; ACPI_BUFFER in = { sizeof(params), ¶ms }; ACPI_BUFFER out = { ACPI_ALLOCATE_BUFFER, NULL }; if (ACPI_FAILURE(ACPI_WMI_EVALUATE_CALL(wmi_dev, ACPI_ASUS_WMI_MGMT_GUID, 1, method, &in, &out))) { acpi_asus_wmi_free_buffer(&out); return (-EINVAL); } obj = out.Pointer; if (obj && obj->Type == ACPI_TYPE_INTEGER) result = (UINT32) obj->Integer.Value; else result = 0; acpi_asus_wmi_free_buffer(&out); if (retval) *retval = result; return (result == ASUS_WMI_UNSUPPORTED_METHOD ? -ENODEV : 0); } static int acpi_wpi_asus_get_devstate(struct acpi_asus_wmi_softc *sc, UINT32 dev_id, UINT32 *retval) { return (acpi_asus_wmi_evaluate_method(sc->wmi_dev, sc->dsts_id, dev_id, 0, retval)); } static int acpi_wpi_asus_set_devstate(struct acpi_asus_wmi_softc *sc, UINT32 dev_id, UINT32 ctrl_param, UINT32 *retval) { return (acpi_asus_wmi_evaluate_method(sc->wmi_dev, ASUS_WMI_METHODID_DEVS, dev_id, ctrl_param, retval)); } + +static int +acpi_asus_wmi_backlight_update_status(device_t dev, struct backlight_props + *props) +{ + struct acpi_asus_wmi_softc *sc = device_get_softc(dev); + + acpi_wpi_asus_set_devstate(sc, ASUS_WMI_DEVID_KBD_BACKLIGHT, + kbd_bkl_level_to_devstate(props->brightness), NULL); + sc->kbd_bkl_level = props->brightness; + + return (0); +} + +static int +acpi_asus_wmi_backlight_get_status(device_t dev, struct backlight_props *props) +{ + struct acpi_asus_wmi_softc *sc = device_get_softc(dev); + + props->brightness = sc->kbd_bkl_level; + props->nlevels = nitems(acpi_asus_wmi_backlight_levels); + memcpy(props->levels, acpi_asus_wmi_backlight_levels, + sizeof(acpi_asus_wmi_backlight_levels)); + + return (0); +} + +static int +acpi_asus_wmi_backlight_get_info(device_t dev, struct backlight_info *info) +{ + info->type = BACKLIGHT_TYPE_KEYBOARD; + strlcpy(info->name, "ASUS Keyboard", BACKLIGHTMAXNAMELENGTH); + + return (0); +} diff --git a/sys/modules/acpi/acpi_asus_wmi/Makefile b/sys/modules/acpi/acpi_asus_wmi/Makefile index 552eef2ac479..3067bec099f5 100644 --- a/sys/modules/acpi/acpi_asus_wmi/Makefile +++ b/sys/modules/acpi/acpi_asus_wmi/Makefile @@ -1,8 +1,8 @@ .PATH: ${SRCTOP}/sys/dev/acpi_support KMOD= acpi_asus_wmi CFLAGS+=-I${SRCTOP}/sys/dev/acpi_support SRCS= acpi_asus_wmi.c opt_acpi.h acpi_if.h acpi_wmi_if.h device_if.h bus_if.h -SRCS+= opt_evdev.h +SRCS+= opt_evdev.h backlight_if.h .include