[PATCH v4 13/13] platform/x86: asus-wmi: Do not disable keyboard backlight on unloading
The keyboard backlight is automatically disabled when the module is unloaded as it is exposed as a ledclass device. Change this behavior to ignore setting brightness when the device is in unloading state. Signed-off-by: Yurii Pavlovskyi --- drivers/platform/x86/asus-wmi.c | 4 1 file changed, 4 insertions(+) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index feb8d72fc3c5..0c330d6a5871 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -471,6 +471,10 @@ static void do_kbd_led_set(struct led_classdev *led_cdev, int value) static void kbd_led_set(struct led_classdev *led_cdev, enum led_brightness value) { + /* Prevent disabling keyboard backlight on module unregister */ + if (led_cdev->flags & LED_UNREGISTERING) + return; + do_kbd_led_set(led_cdev, value); } -- 2.17.1
[PATCH v4 12/13] platform/x86: asus-wmi: Switch fan boost mode
The WMI exposes a write-only device ID where up to three fan modes can be switched on some laptops (TUF Gaming FX505GM). There is a hotkey combination Fn-F5 that does have a fan icon, which is designed to toggle between fan modes. The DSTS of the device ID returns information about the presence of this capability and the presence of each of the two additional fan modes as a bitmask (0x01 - overboost present, 0x02 - silent present) [1]. Add a SysFS entry that reads the last written value and updates value in WMI on write and a hotkey handler that toggles the modes taking into account their availability according to DSTS. Modes: * 0x00 - normal or balanced, * 0x01 - overboost, increased fan RPM, * 0x02 - silent, decreased fan RPM [1] Link: https://lkml.org/lkml/2019/4/12/110 Signed-off-by: Yurii Pavlovskyi Suggested-by: Daniel Drake --- .../ABI/testing/sysfs-platform-asus-wmi | 10 ++ drivers/platform/x86/asus-wmi.c | 151 +- include/linux/platform_data/x86/asus-wmi.h| 1 + 3 files changed, 154 insertions(+), 8 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-platform-asus-wmi b/Documentation/ABI/testing/sysfs-platform-asus-wmi index 019e1e29370e..87ae5cc983bf 100644 --- a/Documentation/ABI/testing/sysfs-platform-asus-wmi +++ b/Documentation/ABI/testing/sysfs-platform-asus-wmi @@ -36,3 +36,13 @@ KernelVersion: 3.5 Contact: "AceLan Kao" Description: Resume on lid open. 1 means on, 0 means off. + +What: /sys/devices/platform//fan_mode +Date: Apr 2019 +KernelVersion: 5.2 +Contact: "Yurii Pavlovskyi" +Description: + Fan boost mode: + * 0 - normal, + * 1 - overboost, + * 2 - silent diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index ffb4e2530ea4..feb8d72fc3c5 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -70,6 +70,7 @@ MODULE_LICENSE("GPL"); #define NOTIFY_KBD_BRTUP 0xc4 #define NOTIFY_KBD_BRTDWN 0xc5 #define NOTIFY_KBD_BRTTOGGLE 0xc7 +#define NOTIFY_KBD_FBM 0x99 #define ASUS_WMI_FNLOCK_BIOS_DISABLED BIT(0) @@ -80,6 +81,13 @@ MODULE_LICENSE("GPL"); #define ASUS_FAN_CTRL_MANUAL 1 #define ASUS_FAN_CTRL_AUTO 2 +#define ASUS_FAN_MODE_NORMAL 0 +#define ASUS_FAN_MODE_OVERBOOST1 +#define ASUS_FAN_MODE_OVERBOOST_MASK 0x01 +#define ASUS_FAN_MODE_SILENT 2 +#define ASUS_FAN_MODE_SILENT_MASK 0x02 +#define ASUS_FAN_MODES_MASK0x03 + #define USB_INTEL_XUSB2PR 0xD0 #define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31 @@ -187,6 +195,10 @@ struct asus_wmi { int asus_hwmon_num_fans; int asus_hwmon_pwm; + bool fan_mode_available; + u8 fan_mode_mask; + u8 fan_mode; + struct hotplug_slot hotplug_slot; struct mutex hotplug_lock; struct mutex wmi_lock; @@ -1483,6 +1495,116 @@ static int asus_wmi_fan_init(struct asus_wmi *asus) return 0; } +/* Fan mode ***/ + +static int fan_mode_check_present(struct asus_wmi *asus) +{ + u32 result; + int err; + + asus->fan_mode_available = false; + + err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_FAN_MODE, ); + if (err) { + if (err == -ENODEV) + return 0; + else + return err; + } + + if ((result & ASUS_WMI_DSTS_PRESENCE_BIT) && + (result & ASUS_FAN_MODES_MASK)) { + asus->fan_mode_available = true; + asus->fan_mode_mask = result & ASUS_FAN_MODES_MASK; + } + + return 0; +} + +static int fan_mode_write(struct asus_wmi *asus) +{ + int err; + u8 value; + u32 retval; + + value = asus->fan_mode; + + pr_info("Set fan mode: %u\n", value); + err = asus_wmi_set_devstate(ASUS_WMI_DEVID_FAN_MODE, value, ); + + if (err) { + pr_warn("Failed to set fan mode: %d\n", err); + return err; + } + + if (retval != 1) { + pr_warn("Failed to set fan mode (retval): 0x%x\n", retval); + return -EIO; + } + + return 0; +} + +static int fan_mode_switch_next(struct asus_wmi *asus) +{ + if (asus->fan_mode == ASUS_FAN_MODE_NORMAL) { + if (asus->fan_mode_mask & ASUS_FAN_MODE_OVERBOOST_MASK) + asus->fan_mode = ASUS_FAN_MODE_OVERBOOST; + else if (asus->fan_mode_mask & ASUS_FAN_MODE_SILENT_MASK) + asus->fan_mode = ASUS_FAN_MODE_SILENT; + } else if (asus->fan_mode == ASUS_F
[PATCH v4 11/13] platform/x86: asus-wmi: Enhance detection of thermal data
The obviously wrong value 1 for temperature device ID in this driver is returned by at least some devices, including TUF Gaming series laptops, instead of 0 as expected previously. Observable effect is that a temp1_input in hwmon reads temperature near absolute zero. Consider 0.1 K an erroneous value in addition to 0 K. Signed-off-by: Yurii Pavlovskyi --- drivers/platform/x86/asus-wmi.c | 7 +-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index bd9eb00f3a27..ffb4e2530ea4 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -1428,8 +1428,11 @@ static umode_t asus_hwmon_sysfs_is_visible(struct kobject *kobj, else ok = fan_attr <= asus->asus_hwmon_num_fans; } else if (dev_id == ASUS_WMI_DEVID_THERMAL_CTRL) { - /* If value is zero, something is clearly wrong */ - if (!value) + /* +* If the temperature value in deci-Kelvin is near the absolute +* zero temperature, something is clearly wrong +*/ + if (value == 0 || value == 1) ok = false; } else if (fan_attr <= asus->asus_hwmon_num_fans && fan_attr != -1) { ok = true; -- 2.17.1
[PATCH v4 10/13] platform/x86: asus-wmi: Organize code into sections
The driver has grown pretty big and will grow more, which makes it hard to navigate and understand. Add uniform comments to the code and ensure that it is sorted into logical sections. Signed-off-by: Yurii Pavlovskyi --- drivers/platform/x86/asus-wmi.c | 89 + 1 file changed, 47 insertions(+), 42 deletions(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 090a00af4017..bd9eb00f3a27 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -200,6 +200,8 @@ struct asus_wmi { struct asus_wmi_driver *driver; }; +/* Input **/ + static int asus_wmi_input_init(struct asus_wmi *asus) { int err; @@ -237,6 +239,8 @@ static void asus_wmi_input_exit(struct asus_wmi *asus) asus->inputdev = NULL; } +/* WMI / + static int asus_wmi_evaluate_method3(u32 method_id, u32 arg0, u32 arg1, u32 arg2, u32 *retval) { @@ -349,9 +353,8 @@ static int asus_wmi_get_devstate_simple(struct asus_wmi *asus, u32 dev_id) ASUS_WMI_DSTS_STATUS_BIT); } -/* - * LEDs - */ +/* LEDs ***/ + /* * These functions actually update the LED's, and are called from a * workqueue. By doing this as separate work rather than when the LED @@ -661,6 +664,7 @@ static int asus_wmi_led_init(struct asus_wmi *asus) return rv; } +/* RF */ /* * PCI hotplug (for wlan rfkill) @@ -1083,6 +1087,8 @@ static int asus_wmi_rfkill_init(struct asus_wmi *asus) return result; } +/* Quirks */ + static void asus_wmi_set_xusb2pr(struct asus_wmi *asus) { struct pci_dev *xhci_pdev; @@ -1115,9 +1121,8 @@ static void asus_wmi_set_als(void) asus_wmi_set_devstate(ASUS_WMI_DEVID_ALS_ENABLE, 1, NULL); } -/* - * Hwmon device - */ +/* Hwmon device ***/ + static int asus_hwmon_agfn_fan_speed_read(struct asus_wmi *asus, int fan, int *speed) { @@ -1456,9 +1461,27 @@ static int asus_wmi_hwmon_init(struct asus_wmi *asus) return 0; } -/* - * Backlight - */ +static int asus_wmi_fan_init(struct asus_wmi *asus) +{ + int status; + + asus->asus_hwmon_pwm = -1; + asus->asus_hwmon_num_fans = -1; + asus->asus_hwmon_fan_manual_mode = false; + + status = asus_hwmon_get_fan_number(asus, >asus_hwmon_num_fans); + if (status) { + asus->asus_hwmon_num_fans = 0; + pr_warn("Could not determine number of fans: %d\n", status); + return -ENXIO; + } + + pr_info("Number of fans: %d\n", asus->asus_hwmon_num_fans); + return 0; +} + +/* Backlight **/ + static int read_backlight_power(struct asus_wmi *asus) { int ret; @@ -1640,6 +1663,8 @@ static int is_display_toggle(int code) return 0; } +/* Fn-lock / + static bool asus_wmi_has_fnlock_key(struct asus_wmi *asus) { u32 result; @@ -1657,6 +1682,8 @@ static void asus_wmi_fnlock_update(struct asus_wmi *asus) asus_wmi_set_devstate(ASUS_WMI_DEVID_FNLOCK, mode, NULL); } +/* WMI events */ + static int asus_wmi_get_event_code(u32 value) { struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; @@ -1790,9 +1817,8 @@ static int asus_wmi_notify_queue_flush(struct asus_wmi *asus) return -EIO; } -/* - * Sys helpers - */ +/* Sysfs **/ + static int parse_arg(const char *buf, unsigned long count, int *val) { if (!count) @@ -1931,9 +1957,8 @@ static int asus_wmi_sysfs_init(struct platform_device *device) return sysfs_create_group(>dev.kobj, _attribute_group); } -/* - * Platform device - */ +/* Platform device / + static int asus_wmi_platform_init(struct asus_wmi *asus) { struct device *dev = >platform_device->dev; @@ -2017,9 +2042,8 @@ static void asus_wmi_platform_exit(struct asus_wmi *asus) asus_wmi_sysfs_exit(asus->platform_device); } -/* - * debugfs - */ +/* debugfs / + struct asus_wmi_debugfs_node { struct asus_wmi *asus; char *name; @@ -2166,28 +2190,8 @@ static int asus_wmi_debugfs_init(struct asus_wmi *asus)
[PATCH v4 09/13] platform/x86: asus-wmi: Refactor error handling
Remove exit label as it is only used once from the point in code where no cleanup is required and return can be called immediately. Signed-off-by: Yurii Pavlovskyi --- drivers/platform/x86/asus-wmi.c | 6 +- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 7bfac06abae4..090a00af4017 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -255,7 +255,7 @@ static int asus_wmi_evaluate_method3(u32 method_id, , ); if (ACPI_FAILURE(status)) - goto exit; + return -EIO; obj = (union acpi_object *)output.pointer; if (obj && obj->type == ACPI_TYPE_INTEGER) @@ -266,10 +266,6 @@ static int asus_wmi_evaluate_method3(u32 method_id, kfree(obj); -exit: - if (ACPI_FAILURE(status)) - return -EIO; - if (tmp == ASUS_WMI_UNSUPPORTED_METHOD) return -ENODEV; -- 2.17.1
[PATCH v4 08/13] platform/x86: asus-nb-wmi: Add microphone mute key code
The microphone mute key is missing from sparse keymap. It is present on FX505GM and possibly other laptops. Add the missing code. Also, comment on the fan mode switch key, which has the same code as the already used key. Signed-off-by: Yurii Pavlovskyi --- drivers/platform/x86/asus-nb-wmi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c index b6f2ff95c3ed..d2399ce0b3cd 100644 --- a/drivers/platform/x86/asus-nb-wmi.c +++ b/drivers/platform/x86/asus-nb-wmi.c @@ -468,6 +468,7 @@ static const struct key_entry asus_nb_wmi_keymap[] = { { KE_KEY, 0x6B, { KEY_TOUCHPAD_TOGGLE } }, { KE_IGNORE, 0x6E, }, /* Low Battery notification */ { KE_KEY, 0x7a, { KEY_ALS_TOGGLE } }, /* Ambient Light Sensor Toggle */ + { KE_KEY, 0x7c, { KEY_MICMUTE } }, { KE_KEY, 0x7D, { KEY_BLUETOOTH } }, /* Bluetooth Enable */ { KE_KEY, 0x7E, { KEY_BLUETOOTH } }, /* Bluetooth Disable */ { KE_KEY, 0x82, { KEY_CAMERA } }, @@ -482,7 +483,7 @@ static const struct key_entry asus_nb_wmi_keymap[] = { { KE_KEY, 0x92, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + TV + DVI */ { KE_KEY, 0x93, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + TV + DVI */ { KE_KEY, 0x95, { KEY_MEDIA } }, - { KE_KEY, 0x99, { KEY_PHONE } }, + { KE_KEY, 0x99, { KEY_PHONE } }, /* Conflicts with fan mode switch */ { KE_KEY, 0xA0, { KEY_SWITCHVIDEOMODE } }, /* SDSP HDMI only */ { KE_KEY, 0xA1, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + HDMI */ { KE_KEY, 0xA2, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + HDMI */ -- 2.17.1
[PATCH v4 07/13] platform/x86: asus-wmi: Support WMI event queue
Event codes are expected to be retrieved from a queue on at least some models. Specifically, very likely the ACPI WMI devices with _UID ATK are queued whereas those with ASUSWMI are not [1]. The WMI event codes are pushed into a circular buffer queue. After the INIT method is called, ACPI code is allowed to push events into this buffer. The INIT method cannot be reverted. If the module is unloaded and an event (such as hotkey press) gets emitted before inserting it back the events get processed delayed by one or if the queue overflows, additionally delayed by about 3 seconds. It might be considered a minor issue and no normal user would likely observe this (there is little reason unloading the driver), but it does significantly frustrate a developer who is unlucky enough to encounter this. Therefore, the fallback to unqueued behavior occurs whenever something unexpected happens. The fix flushes the old key codes out of the queue on load. After receiving event the queue is read until either .. or 1 is encountered. Also as noted in [1] it is checked whether notify code is equal to 0xFF before enabling queue processing in WMI notify handler. DSDT examples: FX505GM Device (ATKD) { .. Name (ATKQ, Package (0x10) { 0x, .. } Method (IANQ, 1, Serialized) { If ((AQNO >= 0x10)) { Local0 = 0x64 While ((Local0 && (AQNO >= 0x10))) { Local0-- Sleep (0x0A) } ... .. AQTI++ AQTI &= 0x0F ATKQ [AQTI] = Arg0 ... } Method (GANQ, 0, Serialized) { .. If (AQNO) { ... Local0 = DerefOf (ATKQ [AQHI]) AQHI++ AQHI &= 0x0F Return (Local0) } Return (One) } This code is almost identical to K54C, which does return Ones on empty queue. K54C: Method (GANQ, 0, Serialized) { If (AQNO) { ... Return (Local0) } Return (Ones) } [1] Link: https://lkml.org/lkml/2019/4/12/104 Signed-off-by: Yurii Pavlovskyi Suggested-by: Daniel Drake --- drivers/platform/x86/asus-wmi.c | 73 ++--- 1 file changed, 68 insertions(+), 5 deletions(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index ed7c7857012e..7bfac06abae4 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -84,6 +84,13 @@ MODULE_LICENSE("GPL"); #define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31 #define ASUS_ACPI_UID_ASUSWMI "ASUSWMI" +#define ASUS_ACPI_UID_ATK "ATK" + +#define WMI_EVENT_QUEUE_SIZE 0x10 +#define WMI_EVENT_QUEUE_END0x1 +#define WMI_EVENT_MASK 0x +/* The WMI hotkey event value is always the same. */ +#define WMI_EVENT_VALUE_ATK0xFF #define WMI_EVENT_MASK 0x @@ -150,6 +157,7 @@ struct asus_wmi { int dsts_id; int spec; int sfun; + bool wmi_event_queue; struct input_dev *inputdev; struct backlight_device *backlight_device; @@ -1738,14 +1746,52 @@ static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus) static void asus_wmi_notify(u32 value, void *context) { struct asus_wmi *asus = context; - int code = asus_wmi_get_event_code(value); + int code; + int i; - if (code < 0) { - pr_warn("Failed to get notify code: %d\n", code); - return; + for (i = 0; i < WMI_EVENT_QUEUE_SIZE + 1; i++) { + code = asus_wmi_get_event_code(value); + + if (code < 0) { + pr_warn("Failed to get notify code: %d\n", code); + return; + } + + if (code == WMI_EVENT_QUEUE_END || code == WMI_EVENT_MASK) + return; + + asus_wmi_handle_event_code(code, asus); + + /* +* Double check that queue is present: +* ATK (with queue) uses 0xff, ASUSWMI (without) 0xd2. +*/ + if (!asus->wmi_event_queue || value != WMI_EVENT_VALUE_ATK) + return; } - asus_wmi_handle_event_code(code, asus); + pr_warn("Failed to process event queue, last code: 0x%x\n", code); +} + +static int asus_wmi_notify_queue_flush(struct asus_wmi *asus) +{ + int code; + int i; + + for (i = 0; i < WMI_EVENT_QUEUE_SIZE + 1; i++) { + code = asus_wmi_get_event_code(WMI_EVENT_VALUE_ATK); + + if (code < 0) { + pr_warn("Failed to get event during flush: %d\n", code); + return code; + } + + if (code == WMI_EVENT_QUEUE_END |
[PATCH v4 06/13] platform/x86: asus-wmi: Refactor WMI event handling
Refactor WMI event handling into separate functions for getting the event code and handling the retrieved event code as a preparation for introduction of WMI event queue support. Signed-off-by: Yurii Pavlovskyi --- drivers/platform/x86/asus-wmi.c | 66 + 1 file changed, 42 insertions(+), 24 deletions(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 5bdb4ffdbee3..ed7c7857012e 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -85,6 +85,8 @@ MODULE_LICENSE("GPL"); #define ASUS_ACPI_UID_ASUSWMI "ASUSWMI" +#define WMI_EVENT_MASK 0x + static const char * const ashs_ids[] = { "ATK4001", "ATK4002", NULL }; static bool ashs_present(void) @@ -1651,83 +1653,99 @@ static void asus_wmi_fnlock_update(struct asus_wmi *asus) asus_wmi_set_devstate(ASUS_WMI_DEVID_FNLOCK, mode, NULL); } -static void asus_wmi_notify(u32 value, void *context) +static int asus_wmi_get_event_code(u32 value) { - struct asus_wmi *asus = context; struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; union acpi_object *obj; acpi_status status; int code; - int orig_code; - unsigned int key_value = 1; - bool autorelease = 1; status = wmi_get_event_data(value, ); - if (status != AE_OK) { - pr_err("bad event status 0x%x\n", status); - return; + if (ACPI_FAILURE(status)) { + pr_warn("Failed to get WMI notify code: %s\n", + acpi_format_exception(status)); + return -EIO; } obj = (union acpi_object *)response.pointer; - if (!obj || obj->type != ACPI_TYPE_INTEGER) - goto exit; + if (obj && obj->type == ACPI_TYPE_INTEGER) + code = (int)(obj->integer.value & WMI_EVENT_MASK); + else + code = -EIO; + + kfree(obj); + return code; +} + +static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus) +{ + int orig_code; + unsigned int key_value = 1; + bool autorelease = 1; - code = obj->integer.value; orig_code = code; if (asus->driver->key_filter) { asus->driver->key_filter(asus->driver, , _value, ); if (code == ASUS_WMI_KEY_IGNORE) - goto exit; + return; } if (code >= NOTIFY_BRNUP_MIN && code <= NOTIFY_BRNUP_MAX) code = ASUS_WMI_BRN_UP; - else if (code >= NOTIFY_BRNDOWN_MIN && -code <= NOTIFY_BRNDOWN_MAX) + else if (code >= NOTIFY_BRNDOWN_MIN && code <= NOTIFY_BRNDOWN_MAX) code = ASUS_WMI_BRN_DOWN; if (code == ASUS_WMI_BRN_DOWN || code == ASUS_WMI_BRN_UP) { if (acpi_video_get_backlight_type() == acpi_backlight_vendor) { asus_wmi_backlight_notify(asus, orig_code); - goto exit; + return; } } if (code == NOTIFY_KBD_BRTUP) { kbd_led_set_by_kbd(asus, asus->kbd_led_wk + 1); - goto exit; + return; } if (code == NOTIFY_KBD_BRTDWN) { kbd_led_set_by_kbd(asus, asus->kbd_led_wk - 1); - goto exit; + return; } if (code == NOTIFY_KBD_BRTTOGGLE) { if (asus->kbd_led_wk == asus->kbd_led.max_brightness) kbd_led_set_by_kbd(asus, 0); else kbd_led_set_by_kbd(asus, asus->kbd_led_wk + 1); - goto exit; + return; } if (code == NOTIFY_FNLOCK_TOGGLE) { asus->fnlock_locked = !asus->fnlock_locked; asus_wmi_fnlock_update(asus); - goto exit; + return; } - if (is_display_toggle(code) && - asus->driver->quirks->no_display_toggle) - goto exit; + if (is_display_toggle(code) && asus->driver->quirks->no_display_toggle) + return; if (!sparse_keymap_report_event(asus->inputdev, code, key_value, autorelease)) pr_info("Unknown key %x pressed\n", code); +} -exit: - kfree(obj); +static void asus_wmi_notify(u32 value, void *context) +{ + struct asus_wmi *asus = context; + int code = asus_wmi_get_event_code(value); + + if (code < 0) { + pr_warn("Failed to get notify code: %d\n", code); + return; + } + + asus_wmi_handle_event_code(code, asus); } /* -- 2.17.1
[PATCH v4 05/13] platform/x86: asus-wmi: Improve DSTS WMI method ID detection
The DSTS method detection mistakenly selects DCTS instead of DSTS if nothing is returned when the method ID is not defined in WMNB. As a result, the control of keyboard backlight is not functional for TUF Gaming series laptops. Implement detection based on _UID of the WMI device instead. There is evidence that DCTS is handled by ACPI WMI devices that have _UID ASUSWMI, whereas none of the devices without ASUSWMI respond to DCTS and DSTS is used instead [1]. DSDT examples: FX505GM (_UID ATK): Method (WMNB, 3, Serialized) { ... If ((Local0 == 0x53545344)) { ... Return (Zero) } ... // No return } K54C (_UID ATK): Method (WMNB, 3, Serialized) { ... If ((Local0 == 0x53545344)) { ... Return (0x02) } ... Return (0xFFFE) } [1] Link: https://lkml.org/lkml/2019/4/11/322 Signed-off-by: Yurii Pavlovskyi Suggested-by: Daniel Drake --- Depends on the previous patch in the series that adds method to wmi module. --- drivers/hid/hid-asus.c | 2 +- drivers/platform/x86/asus-wmi.c| 23 +++--- include/linux/platform_data/x86/asus-wmi.h | 4 ++-- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c index 336aeaed1159..1d01fe23ca0c 100644 --- a/drivers/hid/hid-asus.c +++ b/drivers/hid/hid-asus.c @@ -396,7 +396,7 @@ static bool asus_kbd_wmi_led_control_present(struct hid_device *hdev) if (!IS_ENABLED(CONFIG_ASUS_WMI)) return false; - ret = asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS2, + ret = asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS, ASUS_WMI_DEVID_KBD_BACKLIGHT, 0, ); hid_dbg(hdev, "WMI backlight check: rc %d value %x", ret, value); if (ret) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 6b35c1f00a3e..5bdb4ffdbee3 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -83,6 +83,8 @@ MODULE_LICENSE("GPL"); #define USB_INTEL_XUSB2PR 0xD0 #define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31 +#define ASUS_ACPI_UID_ASUSWMI "ASUSWMI" + static const char * const ashs_ids[] = { "ATK4001", "ATK4002", NULL }; static bool ashs_present(void) @@ -1874,6 +1876,8 @@ static int asus_wmi_sysfs_init(struct platform_device *device) */ static int asus_wmi_platform_init(struct asus_wmi *asus) { + struct device *dev = >platform_device->dev; + char *wmi_uid; int rv; /* INIT enable hotkeys on some models */ @@ -1903,11 +1907,24 @@ static int asus_wmi_platform_init(struct asus_wmi *asus) * Note, on most Eeepc, there is no way to check if a method exist * or note, while on notebooks, they returns 0xFFFE on failure, * but once again, SPEC may probably be used for that kind of things. +* +* Additionally at least TUF Gaming series laptops return nothing for +* unknown methods, so the detection in this way is not possible. +* +* There is strong indication that only ACPI WMI devices that have _UID +* equal to "ASUSWMI" use DCTS whereas those with "ATK" use DSTS. */ - if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS, 0, 0, NULL)) + wmi_uid = wmi_get_acpi_device_uid(ASUS_WMI_MGMT_GUID); + if (!wmi_uid) + return -ENODEV; + + if (!strcmp(wmi_uid, ASUS_ACPI_UID_ASUSWMI)) { + dev_info(dev, "Detected ASUSWMI, use DCTS\n"); + asus->dsts_id = ASUS_WMI_METHODID_DCTS; + } else { + dev_info(dev, "Detected %s, not ASUSWMI, use DSTS\n", wmi_uid); asus->dsts_id = ASUS_WMI_METHODID_DSTS; - else - asus->dsts_id = ASUS_WMI_METHODID_DSTS2; + } /* CWAP allow to define the behavior of the Fn+F2 key, * this method doesn't seems to be present on Eee PCs */ diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h index bfba245636a7..0668f76df921 100644 --- a/include/linux/platform_data/x86/asus-wmi.h +++ b/include/linux/platform_data/x86/asus-wmi.h @@ -18,8 +18,8 @@ #define ASUS_WMI_METHODID_GDSP 0x50534447 /* Get DiSPlay output */ #define ASUS_WMI_METHODID_DEVP 0x50564544 /* DEVice Policy */ #define ASUS_WMI_METHODID_OSVR 0x5256534F /* OS VeRsion */ -#define ASUS_WMI_METHODID_DSTS 0x53544344 /* Device STatuS */ -#define ASUS_WMI_METHODID_DSTS20x53545344 /* Device STatuS #2*/ +#define ASUS_WMI_METHODID_DCTS 0x53544344 /* Device status (DCTS) */ +#define ASUS_WMI_METHODID_DSTS 0x53545344 /* Device status (DSTS) */ #define ASUS_WMI_METHODID_BSTS 0x53545342 /* Bios STatuS ? */ #define ASUS_WMI_ME
[PATCH v4 04/13] platform/x86: wmi: Add function to get _UID of WMI device
Add a new function to acpi.h / wmi.c that returns _UID of the ACPI WMI device. For example, it returns "ATK" for the following declaration in DSDT: Device (ATKD) { Name (_HID, "PNP0C14" /* Windows Management Instrumentation Device */) // _HID: Hardware ID Name (_UID, "ATK") // _UID: Unique ID .. Generally, it is possible that multiple PNP0C14 ACPI devices are present in the system as mentioned in the commit message of commit bff431e49ff5 ("ACPI: WMI: Add ACPI-WMI mapping driver"). Therefore the _UID is returned for a specific ACPI device that declares the given GUID, to which it is also mapped by other methods of wmi module. Signed-off-by: Yurii Pavlovskyi --- drivers/platform/x86/wmi.c | 19 +++ include/linux/acpi.h | 1 + 2 files changed, 20 insertions(+) diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 7b26b6ccf1a0..b08ffb769cbe 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -635,6 +635,25 @@ bool wmi_has_guid(const char *guid_string) } EXPORT_SYMBOL_GPL(wmi_has_guid); +/** + * wmi_get_acpi_device_uid() - Get _UID name of ACPI device that defines GUID + * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba + * + * Find the _UID of ACPI device associated with this WMI GUID. + * + * Return: The ACPI _UID field value or NULL if the WMI GUID was not found + */ +char *wmi_get_acpi_device_uid(const char *guid_string) +{ + struct wmi_block *wblock = NULL; + + if (!find_guid(guid_string, )) + return NULL; + + return acpi_device_uid(wblock->acpi_device); +} +EXPORT_SYMBOL_GPL(wmi_get_acpi_device_uid); + static struct wmi_block *dev_to_wblock(struct device *dev) { return container_of(dev, struct wmi_block, dev.dev); diff --git a/include/linux/acpi.h b/include/linux/acpi.h index d5dcebd7aad3..d31c7fd66f97 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -376,6 +376,7 @@ extern acpi_status wmi_install_notify_handler(const char *guid, extern acpi_status wmi_remove_notify_handler(const char *guid); extern acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out); extern bool wmi_has_guid(const char *guid); +extern char *wmi_get_acpi_device_uid(const char *guid); #endif /* CONFIG_ACPI_WMI */ -- 2.17.1
[PATCH v4 03/13] platform/x86: asus-wmi: Increase input buffer size of WMI methods
The asus-nb-wmi driver is matched by WMI alias but fails to load on TUF Gaming series laptops producing multiple ACPI errors in the kernel log. The input buffer for WMI method invocation size is 2 dwords, whereas 3 are expected by this model. FX505GM: .. Method (WMNB, 3, Serialized) { P8XH (Zero, 0x11) CreateDWordField (Arg2, Zero, IIA0) CreateDWordField (Arg2, 0x04, IIA1) CreateDWordField (Arg2, 0x08, IIA2) Local0 = (Arg1 & 0x) ... Compare with older K54C: ... Method (WMNB, 3, NotSerialized) { CreateDWordField (Arg2, 0x00, IIA0) CreateDWordField (Arg2, 0x04, IIA1) Local0 = (Arg1 & 0x) ... Increase buffer size to 3 dwords. No negative consequences of this change are expected, as the input buffer size is not verified. The original function is replaced by a wrapper for a new method passing value 0 for the last parameter. The new function will be used to control RGB keyboard backlight. Signed-off-by: Yurii Pavlovskyi --- One of current kernel errors: ACPI BIOS Error (bug): AE_AML_BUFFER_LIMIT, Field [IIA2] at bit offset/ length 64/32 exceeds size of target Buffer (64 bits) (20190215/dsopcode-203) [ 4528.573948] No Local Variables are initialized for Method [WMNB] [ 4528.573949] Initialized Arguments for Method [WMNB]: (3 arguments defined for method invocation) [ 4528.573950] Arg0: bd1bea5a Integer [ 4528.573952] Arg1: d414dc53 Integer 4E464741 [ 4528.573954] Arg2: fcefea4b Buffer(8) F0 95 08 00 00 00 00 00 [ 4528.573959] ACPI Error: Aborting method \_SB.ATKD.WMNB due to previous error (AE_AML_BUFFER_LIMIT) (20190215/psparse-531) [ 4528.686425] asus-nb-wmi: probe of asus-nb-wmi failed with error -5 --- drivers/platform/x86/asus-wmi.c | 10 +- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 84d7fc6f941c..6b35c1f00a3e 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -98,6 +98,7 @@ static bool ashs_present(void) struct bios_args { u32 arg0; u32 arg1; + u32 arg2; /* At least TUF Gaming series uses 3 dword input buffer. */ } __packed; /* @@ -224,11 +225,13 @@ static void asus_wmi_input_exit(struct asus_wmi *asus) asus->inputdev = NULL; } -int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval) +static int asus_wmi_evaluate_method3(u32 method_id, + u32 arg0, u32 arg1, u32 arg2, u32 *retval) { struct bios_args args = { .arg0 = arg0, .arg1 = arg1, + .arg2 = arg2, }; struct acpi_buffer input = { (acpi_size) sizeof(args), }; struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; @@ -260,6 +263,11 @@ int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval) return 0; } + +int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval) +{ + return asus_wmi_evaluate_method3(method_id, arg0, arg1, 0, retval); +} EXPORT_SYMBOL_GPL(asus_wmi_evaluate_method); static int asus_wmi_evaluate_method_agfn(const struct acpi_buffer args) -- 2.17.1
[PATCH v4 02/13] platform/x86: asus-wmi: Fix preserving keyboard backlight intensity on load
The error code and return value are mixed up. The intensity is always set to 0 on load as kbd_led_read returns either 0 or negative value. To reproduce set backlight to maximum, reload driver and try to increase it using keyboard hotkey, the intensity will drop as a result. Correct the implementation. Signed-off-by: Yurii Pavlovskyi --- drivers/platform/x86/asus-wmi.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 62567766bdfb..84d7fc6f941c 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -595,8 +595,7 @@ static int asus_wmi_led_init(struct asus_wmi *asus) goto error; } - led_val = kbd_led_read(asus, NULL, NULL); - if (led_val >= 0) { + if (!kbd_led_read(asus, _val, NULL)) { asus->kbd_led_wk = led_val; asus->kbd_led.name = "asus::kbd_backlight"; asus->kbd_led.flags = LED_BRIGHT_HW_CHANGED; -- 2.17.1
[PATCH v4 01/13] platform/x86: asus-wmi: Fix hwmon device cleanup
The driver does not clean up the hwmon device on exit or error. To reproduce the bug, repeat rmmod, insmod to verify that device number /sys/devices/platform/asus-nb-wmi/hwmon/hwmon?? grows every time. Replace call for registering device with devm_* version that unregisters it automatically. Signed-off-by: Yurii Pavlovskyi --- drivers/platform/x86/asus-wmi.c | 7 --- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index f94691615881..62567766bdfb 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -1428,11 +1428,12 @@ __ATTRIBUTE_GROUPS(hwmon_attribute); static int asus_wmi_hwmon_init(struct asus_wmi *asus) { + struct device *dev = >platform_device->dev; struct device *hwmon; - hwmon = hwmon_device_register_with_groups(>platform_device->dev, - "asus", asus, - hwmon_attribute_groups); + hwmon = devm_hwmon_device_register_with_groups(dev, "asus", asus, + hwmon_attribute_groups); + if (IS_ERR(hwmon)) { pr_err("Could not register asus hwmon device\n"); return PTR_ERR(hwmon); -- 2.17.1
[PATCH v4 00/13] Support of ASUS TUF Gaming series laptops
Hi, this is the fourth version of the patch series. Changelog: v4: * Rebase on for-next branch * Extract local variable in patch 01 * Rename new method to "..._method3" and keep comma in struct declaration in patch 03 (NOTE: the arg2 does not fit on same line by 1 character) * Patch "Improve DSTS WMI method ID detection": - sort local variables - use dev_info - separate changes to wmi module in an own patch - rename method ID constants and fix comment capitalization * "Support WMI event queue": split into separate refactoring and new functionality patches, use dev_info as well * "Organize code into sections": split out error handling refactoring * "Enhance detection of thermal data": remove unreasonable refactoring and just change the currently used condition * "Control RGB keyboard backlight": removed, will be posted afterwards. I will follow on the status of the multicolor framework, it does look promising for this. * Mark URL references with "Link:" * Minor corrections to commit messages v3: * Use devm_* function in patch 01 * Detect DSTS/DCTS using _UID in patch 04 * Detect event queue by _UID as well in patch 05 * Rename poll function in patch 05 * Fix terminology in patches 09 and 10 * Correct commit messages v2: * Fix logging INTRODUCTION The support for this laptop series is currently non-existent, as the asus-nb-wmi driver (which is essentially configuration for asus-wmi) fails to load and multiple ACPI errors are logged in dmesg. This patch series adds pretty comprehensive support for these relatively new laptops, adds some code organization, and fixes a couple of bugs in the asus-wmi module. Thread for V1/V2: https://lkml.org/lkml/2019/4/10/973 Thread for V3: https://lkml.org/lkml/2019/4/19/178 Yurii Pavlovskyi (13): platform/x86: asus-wmi: Fix hwmon device cleanup platform/x86: asus-wmi: Fix preserving keyboard backlight intensity on load platform/x86: asus-wmi: Increase input buffer size of WMI methods platform/x86: wmi: Add function to get _UID of WMI device platform/x86: asus-wmi: Improve DSTS WMI method ID detection platform/x86: asus-wmi: Refactor WMI event handling platform/x86: asus-wmi: Support WMI event queue platform/x86: asus-nb-wmi: Add microphone mute key code platform/x86: asus-wmi: Refactor error handling platform/x86: asus-wmi: Organize code into sections platform/x86: asus-wmi: Enhance detection of thermal data platform/x86: asus-wmi: Switch fan boost mode platform/x86: asus-wmi: Do not disable keyboard backlight on unloading .../ABI/testing/sysfs-platform-asus-wmi | 10 + drivers/hid/hid-asus.c| 2 +- drivers/platform/x86/asus-nb-wmi.c| 3 +- drivers/platform/x86/asus-wmi.c | 427 ++ drivers/platform/x86/wmi.c| 19 + include/linux/acpi.h | 1 + include/linux/platform_data/x86/asus-wmi.h| 5 +- 7 files changed, 374 insertions(+), 93 deletions(-) -- 2.17.1
Re: [PATCH v3 09/11] platform/x86: asus-wmi: Control RGB keyboard backlight
First of all, thanks to Andy for all the review comments! I will implement all the ones that I didn't directly answer on as well and update this series shortly. Regarding this patch, On 08.05.19 19:12, Pavel Machek wrote: >> Shouldn't be the LED subsystem driver for this? > > Yes, please. We have common interface for LED drivers; this needs to > use it. That is indeed a better option and I did in fact considered this first and even did a test implementation. The discoveries were: 1. The WMI methods are write-only and only written all at once in a transaction manner (also invoking solely first RGB-interface method has no effect until some other keyboard backlight method is called). 2. In addition to RGB there are several control values, which switch effects, speed and enable or disable the backlight under specific conditions or switch whether it is set temporarily or permanently (not that these are critical functionalities, but for the sake of completeness). 3. The EC is really slow # time bash -c "echo 1 > /sys/devices/platform/faustus/kbbl_set" real0m0,691s user0m0,000s sys 0m0,691s (please ignore the sysfs-path there, it's essentially the same code running as in this patch). It is consistently same for both temporary and permanent configuration. Writing after every change would take about (6+)x of that. Not that it's that unbearable though as it is not likely to be done often. I was not quite happy with that implementation so I opted for writing sort of sysfs wrapper instead that would allow same sort of transactions as provided by BIOS. I agree that it's non-standard solution. If I understood correctly, the typical current RGB led_class devices from the Linux tree currently provide channels as separate LEDs. There are also blink / pattern options present, I guess one could misuse them for setting effects and speed. So one could make 3 devices for RGB + 3 for awake, sleep, boot modes + 1 for setting effect / speed. I'd guess the end solution might be also either something like combination of both approaches (RGB leds + separate sysfs interface) or some extension of the led_class device interface. Dropping support of the non-essential features for the sake of uniformity of ABI would also be an option to consider (exposing just three RGB LEDs with brightness only), not happy one though. In any case this looks like it might need some additional research, discussion, development, and a pair of iterations so I tend to separate this patch from the series and post it extra after the others are through to avoid dragging 10+ patches around. Any suggestions on how to do this properly would be appreciated. That's the best I could come up with at the moment. Thanks, Yurii
Re: [PATCH v3 08/11] platform/x86: asus-wmi: Enhance detection of thermal data
On 08.05.19 15:58, Andy Shevchenko wrote: > On Fri, Apr 19, 2019 at 1:12 PM Yurii Pavlovskyi > wrote: > >> - if (value == ASUS_WMI_UNSUPPORTED_METHOD || value & >> 0xFFF8 >> + if (value == ASUS_WMI_UNSUPPORTED_METHOD || (value & >> 0xFFF8) > > Seems like a bug fix and thus should be a separate commit predecessing > the series. The previous one should theoretically work as well, just thought that would help readability, will revert this. >> - else if (attr == _attr_temp1_input.attr) >> - dev_id = ASUS_WMI_DEVID_THERMAL_CTRL; > > I don't see how this change affects the user output or driver > behaviour. Why is it done? > >> - } else if (dev_id == ASUS_WMI_DEVID_THERMAL_CTRL) { > >> + } else if (attr == _attr_temp1_input.attr) { > > So, I don't see why you change this line. > Yes, looking at this patch now I'd guess the refactoring there is really misguided as it adds a lot more code than it removes, will drop it completely and just add a new condition to the current check instead in next version: - /* If value is zero, something is clearly wrong */ - if (!value) + if (!value || value == 1) Thanks, Yurii
Re: [PATCH v3 05/11] platform/x86: asus-wmi: Support WMI event queue
On 08.05.19 15:47, Andy Shevchenko wrote: > On Fri, Apr 19, 2019 at 1:10 PM Yurii Pavlovskyi > wrote: > > It's rather a big change. Can it be split to smaller pieces? > I will then split this into refactoring event code handling in separate methods and actual event queue support patches.
Re: [PATCH v3 04/11] platform/x86: asus-wmi: Improve DSTS WMI method ID detection
On 08.05.19 15:36, Andy Shevchenko wrote:> On Fri, Apr 19, 2019 at 1:08 PM Yurii Pavlovskyi > wrote: >> int rv; >> + char *wmi_uid; > > Keep them in reversed spruce order. What do you mean by that? Do you mean like this? + char *wmi_uid; int rv; int err; >> +#define ASUS_WMI_METHODID_DSTS 0x53544344 /* Device STatuS (DCTS) */ > > It's not clear from the description what 'C' stands for. > Is there any specification which describes the difference and actual > abbreviations? > Not that I know of. Daniel had written some research in the previous version thread regarding where it is used, but as I understand from current implementation the functional difference is not really there, as it serves the same purpose as DSTS, just for another hardware. On 09.05.19 08:08, Daniel Drake wrote: > For clarity I think the constants could be renamed as > ASUS_WMI_METHODID_DCTS and ASUS_WMI_METHODID_DSTS. > Agree, that'll be better. Thanks, Yurii
Re: [PATCH v3 08/11] platform/x86: asus-wmi: Enhance detection of thermal data
On 24.04.19 20:25, Pawnikar, Sumeet R wrote: >> +/* >> + * If the temperature value in deci-Kelvin is near the absolute >> + * zero temperature, something is clearly wrong. >> + */ >> +if (!value || value == 1) >> +return 0; > Do you still need to return 0 in case of wrong/failure case ? > Shouldn't you return error here ? Yes, I think 0 is right there, it's not an error condition in the check itself (such as IO or memory error). The function returns two values: check result in asus->asus_hwmon_thermal_available and error code. This is still a successful negative result. The 0 or 1 (as added in this patch) there merely indicates that the temperature measurement is not available this way. It is safe to assume that no device that does provide temperature will return 0.1 K. The temperature measurement is not critical for driver operation so the driver proceeds without it. One could return ENODEV when the check result is negative, but that would just move the actual check and assignment further to asus_wmi_fan_init. This is how it might look like in DSDT in this case: Method (TMPR, 0, NotSerialized) { Return (One) } .. If ((IIA0 == 0x00110011)) { Return ((TMPR () & 0x)) } This is indeed the right method ID but it's nothing there. Regards, Yurii
[PATCH v3 10/11] platform/x86: asus-wmi: Switch fan boost mode
The WMI exposes a write-only device ID where up to three fan modes modes can be switched on some laptops (TUF Gaming FX505GM). There is a hotkey combination Fn-F5 that does have a fan icon which is designed to toggle between fan modes. The DSTS of the device ID returns information about the presence of the device and the presence of each of the two additional fan modes as a bitmask (0x01 - overboost present, 0x02 - silent present) [1]. Add a SysFS entry that reads the last written value and updates value in WMI on write and a hotkey handler that toggles the modes taking into account their availability according to DSTS. Modes: * 0x00 - normal or balanced, * 0x01 - overboost, increased fan RPM, * 0x02 - silent, decreased fan RPM [1] https://lkml.org/lkml/2019/4/12/110 Signed-off-by: Yurii Pavlovskyi Suggested-by: Daniel Drake --- .../ABI/testing/sysfs-platform-asus-wmi | 10 ++ drivers/platform/x86/asus-wmi.c | 149 +- include/linux/platform_data/x86/asus-wmi.h| 1 + 3 files changed, 152 insertions(+), 8 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-platform-asus-wmi b/Documentation/ABI/testing/sysfs-platform-asus-wmi index 1cc54d5e3e10..6f396c4eabdc 100644 --- a/Documentation/ABI/testing/sysfs-platform-asus-wmi +++ b/Documentation/ABI/testing/sysfs-platform-asus-wmi @@ -97,3 +97,13 @@ Description: Write changed RGB keyboard backlight parameters: * 1 - permanently, * 2 - temporarily. + +What: /sys/devices/platform//fan_mode +Date: Apr 2019 +KernelVersion: 5.1 +Contact: "Yurii Pavlovskyi" +Description: + Fan boost mode: + * 0 - normal, + * 1 - overboost, + * 2 - silent diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 0a32079336d8..7974283b7b12 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -69,6 +69,7 @@ MODULE_LICENSE("GPL"); #define NOTIFY_KBD_BRTUP 0xc4 #define NOTIFY_KBD_BRTDWN 0xc5 #define NOTIFY_KBD_BRTTOGGLE 0xc7 +#define NOTIFY_KBD_FBM 0x99 #define ASUS_FAN_DESC "cpu_fan" #define ASUS_FAN_MFUN 0x13 @@ -77,6 +78,13 @@ MODULE_LICENSE("GPL"); #define ASUS_FAN_CTRL_MANUAL 1 #define ASUS_FAN_CTRL_AUTO 2 +#define ASUS_FAN_MODE_NORMAL 0 +#define ASUS_FAN_MODE_OVERBOOST1 +#define ASUS_FAN_MODE_OVERBOOST_MASK 0x01 +#define ASUS_FAN_MODE_SILENT 2 +#define ASUS_FAN_MODE_SILENT_MASK 0x02 +#define ASUS_FAN_MODES_MASK0x03 + #define USB_INTEL_XUSB2PR 0xD0 #define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31 @@ -198,6 +206,10 @@ struct asus_wmi { int asus_hwmon_num_fans; int asus_hwmon_pwm; + bool fan_mode_available; + u8 fan_mode_mask; + u8 fan_mode; + bool kbbl_rgb_available; struct asus_kbbl_rgb kbbl_rgb; @@ -1827,6 +1839,114 @@ static int asus_wmi_fan_init(struct asus_wmi *asus) return 0; } +/* Fan mode ***/ + +static int fan_mode_check_present(struct asus_wmi *asus) +{ + u32 result; + int err; + + asus->fan_mode_available = false; + + err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_FAN_MODE, ); + if (err) { + if (err == -ENODEV) + return 0; + else + return err; + } + + if ((result & ASUS_WMI_DSTS_PRESENCE_BIT) && + (result & ASUS_FAN_MODES_MASK)) { + asus->fan_mode_available = true; + asus->fan_mode_mask = result & ASUS_FAN_MODES_MASK; + } + + return 0; +} + +static int fan_mode_write(struct asus_wmi *asus) +{ + int err; + u8 value; + u32 retval; + + value = asus->fan_mode; + + pr_info("Set fan mode: %u\n", value); + err = asus_wmi_set_devstate(ASUS_WMI_DEVID_FAN_MODE, value, ); + + if (err) { + pr_warn("Failed to set fan mode: %d\n", err); + return err; + } + + if (retval != 1) { + pr_warn("Failed to set fan mode (retval): 0x%x\n", retval); + return -EIO; + } + + return 0; +} + +static int fan_mode_switch_next(struct asus_wmi *asus) +{ + if (asus->fan_mode == ASUS_FAN_MODE_NORMAL) { + if (asus->fan_mode_mask & ASUS_FAN_MODE_OVERBOOST_MASK) + asus->fan_mode = ASUS_FAN_MODE_OVERBOOST; + else if (asus->fan_mode_mask & ASUS_FAN_MODE_SILENT_MASK) + asus->fan_mode = ASUS_FAN_MODE_SILENT; +
[PATCH v3 08/11] platform/x86: asus-wmi: Enhance detection of thermal data
The obviously wrong value 1 for temperature device ID in this driver is returned by at least some devices, including TUF Gaming series laptops, instead of 0 as expected previously. Observable effect is that a temp1_input in hwmon reads temperature near absolute zero. * Consider 0.1 K an erroneous value in addition to 0 K. * Refactor detection of thermal input availability to a separate function. Signed-off-by: Yurii Pavlovskyi --- drivers/platform/x86/asus-wmi.c | 45 - 1 file changed, 38 insertions(+), 7 deletions(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index e69e55635afb..1b8272374660 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -178,6 +178,7 @@ struct asus_wmi { struct asus_rfkill gps; struct asus_rfkill uwb; + bool asus_hwmon_thermal_available; bool asus_hwmon_fan_manual_mode; int asus_hwmon_num_fans; int asus_hwmon_pwm; @@ -1375,6 +1376,32 @@ static struct attribute *hwmon_attributes[] = { NULL }; +static int asus_hwmon_check_thermal_available(struct asus_wmi *asus) +{ + u32 value = ASUS_WMI_UNSUPPORTED_METHOD; + int err; + + asus->asus_hwmon_thermal_available = false; + err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_THERMAL_CTRL, ); + + if (err < 0) { + if (err == -ENODEV) + return 0; + + return err; + } + + /* +* If the temperature value in deci-Kelvin is near the absolute +* zero temperature, something is clearly wrong. +*/ + if (!value || value == 1) + return 0; + + asus->asus_hwmon_thermal_available = true; + return 0; +} + static umode_t asus_hwmon_sysfs_is_visible(struct kobject *kobj, struct attribute *attr, int idx) { @@ -1388,8 +1415,6 @@ static umode_t asus_hwmon_sysfs_is_visible(struct kobject *kobj, if (attr == _attr_pwm1.attr) dev_id = ASUS_WMI_DEVID_FAN_CTRL; - else if (attr == _attr_temp1_input.attr) - dev_id = ASUS_WMI_DEVID_THERMAL_CTRL; if (attr == _attr_fan1_input.attr || attr == _attr_fan1_label.attr @@ -1414,15 +1439,13 @@ static umode_t asus_hwmon_sysfs_is_visible(struct kobject *kobj, * - reverved bits are non-zero * - sfun and presence bit are not set */ - if (value == ASUS_WMI_UNSUPPORTED_METHOD || value & 0xFFF8 + if (value == ASUS_WMI_UNSUPPORTED_METHOD || (value & 0xFFF8) || (!asus->sfun && !(value & ASUS_WMI_DSTS_PRESENCE_BIT))) ok = false; else ok = fan_attr <= asus->asus_hwmon_num_fans; - } else if (dev_id == ASUS_WMI_DEVID_THERMAL_CTRL) { - /* If value is zero, something is clearly wrong */ - if (!value) - ok = false; + } else if (attr == _attr_temp1_input.attr) { + ok = asus->asus_hwmon_thermal_available; } else if (fan_attr <= asus->asus_hwmon_num_fans && fan_attr != -1) { ok = true; } else { @@ -1469,6 +1492,14 @@ static int asus_wmi_fan_init(struct asus_wmi *asus) } pr_info("Number of fans: %d\n", asus->asus_hwmon_num_fans); + + status = asus_hwmon_check_thermal_available(asus); + if (status) { + pr_warn("Could not check if thermal available: %d\n", status); + return -ENXIO; + } + + pr_info("Thermal available: %d\n", asus->asus_hwmon_thermal_available); return 0; } -- 2.17.1
[PATCH v3 04/11] platform/x86: asus-wmi: Improve DSTS WMI method ID detection
The DSTS method detection mistakenly selects DCTS instead of DSTS if nothing is returned when the method ID is not defined in WMNB. As a result, the control of keyboard backlight is not functional for TUF Gaming series laptops. Implement another detection method instead. There is evidence that DCTS is handled by ACPI WMI devices that have _UID ASUSWMI, whereas none of the devices without ASUSWMI respond to DCTS and DSTS is used instead [1]. To check the _UID a new method is added to wmi.h / wmi.c. It returns _UID of the ACPI WMI device that declares WMI object with given GUID. Generally, it is possible that multiple PNP0C14 ACPI devices are present in the system as mentioned in the commit message of commit bff431e49ff5 ("ACPI: WMI: Add ACPI-WMI mapping driver"). Therefore the _UID is checked for given GUID that maps to a specific ACPI device, to which it is also mapped by other methods of wmi module. DSDT examples: FX505GM: Method (WMNB, 3, Serialized) { ... If ((Local0 == 0x53545344)) { ... Return (Zero) } ... // No return } K54C: Method (WMNB, 3, Serialized) { ... If ((Local0 == 0x53545344)) { ... Return (0x02) } ... Return (0xFFFE) } [1] https://lkml.org/lkml/2019/4/11/322 Signed-off-by: Yurii Pavlovskyi Suggested-by: Daniel Drake --- drivers/platform/x86/asus-wmi.c| 20 ++-- drivers/platform/x86/wmi.c | 19 +++ include/linux/acpi.h | 1 + include/linux/platform_data/x86/asus-wmi.h | 4 ++-- 4 files changed, 40 insertions(+), 4 deletions(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index ba04737ece0d..266d0eda5476 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -80,6 +80,8 @@ MODULE_LICENSE("GPL"); #define USB_INTEL_XUSB2PR 0xD0 #define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31 +#define ASUS_ACPI_UID_ASUSWMI "ASUSWMI" + static const char * const ashs_ids[] = { "ATK4001", "ATK4002", NULL }; static bool ashs_present(void) @@ -1847,6 +1849,7 @@ static int asus_wmi_sysfs_init(struct platform_device *device) static int asus_wmi_platform_init(struct asus_wmi *asus) { int rv; + char *wmi_uid; /* INIT enable hotkeys on some models */ if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_INIT, 0, 0, )) @@ -1875,11 +1878,24 @@ static int asus_wmi_platform_init(struct asus_wmi *asus) * Note, on most Eeepc, there is no way to check if a method exist * or note, while on notebooks, they returns 0xFFFE on failure, * but once again, SPEC may probably be used for that kind of things. +* +* Additionally at least TUF Gaming series laptops return nothing for +* unknown methods, so the detection in this way is not possible. +* +* There is strong indication that only ACPI WMI devices that have _UID +* equal to "ASUSWMI" use DCTS whereas those with "ATK" use DSTS. */ - if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS, 0, 0, NULL)) + wmi_uid = wmi_get_acpi_device_uid(ASUS_WMI_MGMT_GUID); + if (!wmi_uid) + return -ENODEV; + + if (!strcmp(wmi_uid, ASUS_ACPI_UID_ASUSWMI)) { + pr_info("Detected ASUSWMI, use DCTS\n"); asus->dsts_id = ASUS_WMI_METHODID_DSTS; - else + } else { + pr_info("Detected %s, not ASUSWMI, use DSTS\n", wmi_uid); asus->dsts_id = ASUS_WMI_METHODID_DSTS2; + } /* CWAP allow to define the behavior of the Fn+F2 key, * this method doesn't seems to be present on Eee PCs */ diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 7b26b6ccf1a0..b08ffb769cbe 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -635,6 +635,25 @@ bool wmi_has_guid(const char *guid_string) } EXPORT_SYMBOL_GPL(wmi_has_guid); +/** + * wmi_get_acpi_device_uid() - Get _UID name of ACPI device that defines GUID + * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba + * + * Find the _UID of ACPI device associated with this WMI GUID. + * + * Return: The ACPI _UID field value or NULL if the WMI GUID was not found + */ +char *wmi_get_acpi_device_uid(const char *guid_string) +{ + struct wmi_block *wblock = NULL; + + if (!find_guid(guid_string, )) + return NULL; + + return acpi_device_uid(wblock->acpi_device); +} +EXPORT_SYMBOL_GPL(wmi_get_acpi_device_uid); + static struct wmi_block *dev_to_wblock(struct device *dev) { return container_of(dev, struct wmi_block, dev.dev); diff --git a/include/linux/acpi.h b/include/linux/acpi.h index d5dcebd7aad3..d31c7fd66f97 100644 --- a/include/linux/acpi.h +++ b/include/linux/
[PATCH v3 06/11] platform/x86: asus-nb-wmi: Add microphone mute key code
The microphone mute key is missing from sparse keymap. It is present on FX505GM and possibly other laptops. Add the missing code. Also, comment on the fan mode switch key, which has the same code as the already used key. Signed-off-by: Yurii Pavlovskyi --- drivers/platform/x86/asus-nb-wmi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c index b6f2ff95c3ed..d2399ce0b3cd 100644 --- a/drivers/platform/x86/asus-nb-wmi.c +++ b/drivers/platform/x86/asus-nb-wmi.c @@ -468,6 +468,7 @@ static const struct key_entry asus_nb_wmi_keymap[] = { { KE_KEY, 0x6B, { KEY_TOUCHPAD_TOGGLE } }, { KE_IGNORE, 0x6E, }, /* Low Battery notification */ { KE_KEY, 0x7a, { KEY_ALS_TOGGLE } }, /* Ambient Light Sensor Toggle */ + { KE_KEY, 0x7c, { KEY_MICMUTE } }, { KE_KEY, 0x7D, { KEY_BLUETOOTH } }, /* Bluetooth Enable */ { KE_KEY, 0x7E, { KEY_BLUETOOTH } }, /* Bluetooth Disable */ { KE_KEY, 0x82, { KEY_CAMERA } }, @@ -482,7 +483,7 @@ static const struct key_entry asus_nb_wmi_keymap[] = { { KE_KEY, 0x92, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + TV + DVI */ { KE_KEY, 0x93, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + TV + DVI */ { KE_KEY, 0x95, { KEY_MEDIA } }, - { KE_KEY, 0x99, { KEY_PHONE } }, + { KE_KEY, 0x99, { KEY_PHONE } }, /* Conflicts with fan mode switch */ { KE_KEY, 0xA0, { KEY_SWITCHVIDEOMODE } }, /* SDSP HDMI only */ { KE_KEY, 0xA1, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + HDMI */ { KE_KEY, 0xA2, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + HDMI */ -- 2.17.1
[PATCH v3 02/11] platform/x86: asus-wmi: Fix preserving keyboard backlight intensity on load
The error code and return value are mixed up. The intensity is always set to 0 on load as kbd_led_read returns either 0 or negative value. To reproduce set backlight to maximum, reload driver and try to increase it using keyboard hotkey, the intensity will drop as a result. Correct the implementation. Signed-off-by: Yurii Pavlovskyi --- drivers/platform/x86/asus-wmi.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index d865eb95054c..731ffd382426 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -590,8 +590,7 @@ static int asus_wmi_led_init(struct asus_wmi *asus) goto error; } - led_val = kbd_led_read(asus, NULL, NULL); - if (led_val >= 0) { + if (!kbd_led_read(asus, _val, NULL)) { asus->kbd_led_wk = led_val; asus->kbd_led.name = "asus::kbd_backlight"; asus->kbd_led.flags = LED_BRIGHT_HW_CHANGED; -- 2.17.1
[PATCH v3 01/11] platform/x86: asus-wmi: Fix hwmon device cleanup
The driver does not clean up the hwmon device on exit or error. To reproduce the bug, repeat rmmod, insmod to verify that device number /sys/devices/platform/asus-nb-wmi/hwmon/hwmon?? grows every time. Replace call for registering device with devm_* version that unregisters it automatically. Signed-off-by: Yurii Pavlovskyi --- drivers/platform/x86/asus-wmi.c | 7 --- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index ee1fa93708ec..d865eb95054c 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -1425,9 +1425,10 @@ static int asus_wmi_hwmon_init(struct asus_wmi *asus) { struct device *hwmon; - hwmon = hwmon_device_register_with_groups(>platform_device->dev, - "asus", asus, - hwmon_attribute_groups); + hwmon = devm_hwmon_device_register_with_groups( + >platform_device->dev, "asus", asus, + hwmon_attribute_groups); + if (IS_ERR(hwmon)) { pr_err("Could not register asus hwmon device\n"); return PTR_ERR(hwmon); -- 2.17.1
[PATCH v3 11/11] platform/x86: asus-wmi: Do not disable keyboard backlight on unloading
The keyboard backlight is automatically disabled when the module is unloaded as it is exposed as a ledclass device. Change this behavior to ignore setting brightness when the device is in unloading state. Signed-off-by: Yurii Pavlovskyi --- drivers/platform/x86/asus-wmi.c | 4 1 file changed, 4 insertions(+) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 7974283b7b12..02c9639bf54f 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -483,6 +483,10 @@ static void do_kbd_led_set(struct led_classdev *led_cdev, int value) static void kbd_led_set(struct led_classdev *led_cdev, enum led_brightness value) { + /* Prevent disabling keyboard backlight on module unregister */ + if (led_cdev->flags & LED_UNREGISTERING) + return; + do_kbd_led_set(led_cdev, value); } -- 2.17.1
[PATCH v3 03/11] platform/x86: asus-wmi: Increase the input buffer size of WMI methods
The asus-nb-wmi driver is matched by WMI alias but fails to load on TUF Gaming series laptops producing multiple ACPI errors in the kernel log. The input buffer for WMI method invocation size is 2 dwords, whereas 3 are expected by this model. FX505GM: .. Method (WMNB, 3, Serialized) { P8XH (Zero, 0x11) CreateDWordField (Arg2, Zero, IIA0) CreateDWordField (Arg2, 0x04, IIA1) CreateDWordField (Arg2, 0x08, IIA2) Local0 = (Arg1 & 0x) ... Compare with older K54C: ... Method (WMNB, 3, NotSerialized) { CreateDWordField (Arg2, 0x00, IIA0) CreateDWordField (Arg2, 0x04, IIA1) Local0 = (Arg1 & 0x) ... Increase buffer size to 3 dwords. No negative consequences of this change are expected, as the input buffer size is not verified. The original function is replaced by a wrapper for a new method passing value 0 for the last parameter. The new function will be used to control RGB keyboard backlight. Signed-off-by: Yurii Pavlovskyi --- One of current kernel errors: ACPI BIOS Error (bug): AE_AML_BUFFER_LIMIT, Field [IIA2] at bit offset/ length 64/32 exceeds size of target Buffer (64 bits) (20190215/dsopcode-203) [ 4528.573948] No Local Variables are initialized for Method [WMNB] [ 4528.573949] Initialized Arguments for Method [WMNB]: (3 arguments defined for method invocation) [ 4528.573950] Arg0: bd1bea5a Integer [ 4528.573952] Arg1: d414dc53 Integer 4E464741 [ 4528.573954] Arg2: fcefea4b Buffer(8) F0 95 08 00 00 00 00 00 [ 4528.573959] ACPI Error: Aborting method \_SB.ATKD.WMNB due to previous error (AE_AML_BUFFER_LIMIT) (20190215/psparse-531) [ 4528.686425] asus-nb-wmi: probe of asus-nb-wmi failed with error -5 --- drivers/platform/x86/asus-wmi.c | 10 +- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 731ffd382426..ba04737ece0d 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -95,6 +95,7 @@ static bool ashs_present(void) struct bios_args { u32 arg0; u32 arg1; + u32 arg2; /* At least TUF Gaming series uses 3 dword input buffer. */ } __packed; /* @@ -219,11 +220,13 @@ static void asus_wmi_input_exit(struct asus_wmi *asus) asus->inputdev = NULL; } -int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval) +static int asus_wmi_evaluate_method_3dw(u32 method_id, u32 arg0, u32 arg1, + u32 arg2, u32 *retval) { struct bios_args args = { .arg0 = arg0, .arg1 = arg1, + .arg2 = arg2 }; struct acpi_buffer input = { (acpi_size) sizeof(args), }; struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; @@ -255,6 +258,11 @@ int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval) return 0; } + +int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval) +{ + return asus_wmi_evaluate_method_3dw(method_id, arg0, arg1, 0, retval); +} EXPORT_SYMBOL_GPL(asus_wmi_evaluate_method); static int asus_wmi_evaluate_method_agfn(const struct acpi_buffer args) -- 2.17.1
[PATCH v3 07/11] platform/x86: asus-wmi: Organize code into sections
The driver has grown pretty big and will grow more, which makes it hard to navigate and understand. Add uniform comments to the code and ensure that it is sorted into logical sections. Signed-off-by: Yurii Pavlovskyi --- drivers/platform/x86/asus-wmi.c | 94 - 1 file changed, 46 insertions(+), 48 deletions(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index f782dac4cbe7..e69e55635afb 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -193,6 +193,8 @@ struct asus_wmi { struct asus_wmi_driver *driver; }; +/* Input **/ + static int asus_wmi_input_init(struct asus_wmi *asus) { int err; @@ -230,6 +232,8 @@ static void asus_wmi_input_exit(struct asus_wmi *asus) asus->inputdev = NULL; } +/* WMI / + static int asus_wmi_evaluate_method_3dw(u32 method_id, u32 arg0, u32 arg1, u32 arg2, u32 *retval) { @@ -248,7 +252,7 @@ static int asus_wmi_evaluate_method_3dw(u32 method_id, u32 arg0, u32 arg1, , ); if (ACPI_FAILURE(status)) - goto exit; + return -EIO; obj = (union acpi_object *)output.pointer; if (obj && obj->type == ACPI_TYPE_INTEGER) @@ -259,10 +263,6 @@ static int asus_wmi_evaluate_method_3dw(u32 method_id, u32 arg0, u32 arg1, kfree(obj); -exit: - if (ACPI_FAILURE(status)) - return -EIO; - if (tmp == ASUS_WMI_UNSUPPORTED_METHOD) return -ENODEV; @@ -346,9 +346,8 @@ static int asus_wmi_get_devstate_simple(struct asus_wmi *asus, u32 dev_id) ASUS_WMI_DSTS_STATUS_BIT); } -/* - * LEDs - */ +/* LEDs ***/ + /* * These functions actually update the LED's, and are called from a * workqueue. By doing this as separate work rather than when the LED @@ -658,6 +657,7 @@ static int asus_wmi_led_init(struct asus_wmi *asus) return rv; } +/* RF */ /* * PCI hotplug (for wlan rfkill) @@ -1080,6 +1080,8 @@ static int asus_wmi_rfkill_init(struct asus_wmi *asus) return result; } +/* Quirks */ + static void asus_wmi_set_xusb2pr(struct asus_wmi *asus) { struct pci_dev *xhci_pdev; @@ -1112,9 +1114,8 @@ static void asus_wmi_set_als(void) asus_wmi_set_devstate(ASUS_WMI_DEVID_ALS_ENABLE, 1, NULL); } -/* - * Hwmon device - */ +/* Hwmon device ***/ + static int asus_hwmon_agfn_fan_speed_read(struct asus_wmi *asus, int fan, int *speed) { @@ -1390,7 +1391,6 @@ static umode_t asus_hwmon_sysfs_is_visible(struct kobject *kobj, else if (attr == _attr_temp1_input.attr) dev_id = ASUS_WMI_DEVID_THERMAL_CTRL; - if (attr == _attr_fan1_input.attr || attr == _attr_fan1_label.attr || attr == _attr_pwm1.attr @@ -1453,9 +1453,27 @@ static int asus_wmi_hwmon_init(struct asus_wmi *asus) return 0; } -/* - * Backlight - */ +static int asus_wmi_fan_init(struct asus_wmi *asus) +{ + int status; + + asus->asus_hwmon_pwm = -1; + asus->asus_hwmon_num_fans = -1; + asus->asus_hwmon_fan_manual_mode = false; + + status = asus_hwmon_get_fan_number(asus, >asus_hwmon_num_fans); + if (status) { + asus->asus_hwmon_num_fans = 0; + pr_warn("Could not determine number of fans: %d\n", status); + return -ENXIO; + } + + pr_info("Number of fans: %d\n", asus->asus_hwmon_num_fans); + return 0; +} + +/* Backlight **/ + static int read_backlight_power(struct asus_wmi *asus) { int ret; @@ -1637,6 +1655,8 @@ static int is_display_toggle(int code) return 0; } +/* WMI events */ + static int asus_wmi_get_next_event(u32 value) { struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; @@ -1762,9 +1782,8 @@ static int asus_wmi_notify_queue_flush(struct asus_wmi *asus) return -EIO; } -/* - * Sys helpers - */ +/* Sysfs **/ + static int parse_arg(const char *buf, unsigned long count, int *val) { if (!count) @@ -1903,9 +1922,8 @@ static int asus_wmi_sysfs_init(struct platform_device *device) return sysfs_create_group(>dev.kobj, _attribute_group)
[PATCH v3 05/11] platform/x86: asus-wmi: Support WMI event queue
Event codes are expected to be retrieved from a queue on at least some models. Specifically, very likely the ACPI WMI devices with _UID ATK are queued whereas those with ASUSWMI are not [1]. The WMI event codes are pushed into a circular buffer queue. After the INIT method is called, ACPI code is allowed to push events into this buffer. The INIT method cannot be reverted. If the module is unloaded and an event (such as hotkey press) gets emitted before inserting it back the events get processed delayed by one or if the queue overflows, additionally delayed by about 3 seconds. It might be considered a minor issue and no normal user would likely observe this (there is little reason unloading the driver), but it does significantly frustrate a developer who is unlucky enough to encounter this. Therefore, the fallback to unqueued behavior occurs whenever something unexpected happens. The fix flushes the old key codes out of the queue on load. After receiving event the queue is read until either .. or 1 is encountered. Also as noted in [1] it is checked whether notify code is equal to 0xFF before enabling queue processing in WMI notify handler. DSDT examples: FX505GM Device (ATKD) { .. Name (ATKQ, Package (0x10) { 0x, .. } Method (IANQ, 1, Serialized) { If ((AQNO >= 0x10)) { Local0 = 0x64 While ((Local0 && (AQNO >= 0x10))) { Local0-- Sleep (0x0A) } ... .. AQTI++ AQTI &= 0x0F ATKQ [AQTI] = Arg0 ... } Method (GANQ, 0, Serialized) { .. If (AQNO) { ... Local0 = DerefOf (ATKQ [AQHI]) AQHI++ AQHI &= 0x0F Return (Local0) } Return (One) } This code is almost identical to K54C, which does return Ones on empty queue. K54C: Method (GANQ, 0, Serialized) { If (AQNO) { ... Return (Local0) } Return (Ones) } [1] https://lkml.org/lkml/2019/4/12/104 Signed-off-by: Yurii Pavlovskyi Suggested-by: Daniel Drake --- drivers/platform/x86/asus-wmi.c | 136 +--- 1 file changed, 108 insertions(+), 28 deletions(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 266d0eda5476..f782dac4cbe7 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -81,6 +81,13 @@ MODULE_LICENSE("GPL"); #define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31 #define ASUS_ACPI_UID_ASUSWMI "ASUSWMI" +#define ASUS_ACPI_UID_ATK "ATK" + +#define WMI_EVENT_QUEUE_SIZE 0x10 +#define WMI_EVENT_QUEUE_END0x1 +#define WMI_EVENT_MASK 0x +/* The WMI hotkey event value is always the same. */ +#define WMI_EVENT_VALUE_ATK0xFF static const char * const ashs_ids[] = { "ATK4001", "ATK4002", NULL }; @@ -145,6 +152,7 @@ struct asus_wmi { int dsts_id; int spec; int sfun; + bool wmi_event_queue; struct input_dev *inputdev; struct backlight_device *backlight_device; @@ -1629,77 +1637,129 @@ static int is_display_toggle(int code) return 0; } -static void asus_wmi_notify(u32 value, void *context) +static int asus_wmi_get_next_event(u32 value) { - struct asus_wmi *asus = context; - struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; union acpi_object *obj; acpi_status status; - int code; - int orig_code; - unsigned int key_value = 1; - bool autorelease = 1; + int code = -EIO; - status = wmi_get_event_data(value, ); - if (status != AE_OK) { - pr_err("bad event status 0x%x\n", status); - return; + status = wmi_get_event_data(value, ); + if (ACPI_FAILURE(status)) { + pr_warn("Failed to get WMI event code: %s\n", + acpi_format_exception(status)); + return code; } - obj = (union acpi_object *)response.pointer; + obj = (union acpi_object *)output.pointer; - if (!obj || obj->type != ACPI_TYPE_INTEGER) - goto exit; + if (obj && obj->type == ACPI_TYPE_INTEGER) + code = (int)(obj->integer.value & WMI_EVENT_MASK); + + kfree(obj); + return code; +} + +static void asus_wmi_handle_notify_code(int code, struct asus_wmi *asus) +{ + int orig_code; + unsigned int key_value = 1; + bool autorelease = 1; - code = obj->integer.value; orig_code = code; if (asus->driver->key_filter) { asus->driver->key_filter(asus->driver,
[PATCH v3 00/11] asus-wmi: Support of ASUS TUF Gaming series laptops
Hi, this is the third version of the patch series. Changelog: v3: * Use devm_* function in patch 01 * Detect DSTS/DCTS using _UID in patch 04 * Detect event queue by _UID as well in patch 05 * Rename poll function in patch 05 * Fix terminology in patches 09 and 10 * Correct commit messages v2: * Fix logging INTRODUCTION The support for this laptop series is currently non-existent, as the asus-nb-wmi driver (which is essentially configuration for asus-wmi) fails to load and multiple ACPI errors are logged in dmesg. This patch series adds pretty comprehensive support for these relatively new laptops, adds some code organization, and fixes a couple of bugs in the asus-wmi module. Original message from V1/V2: https://lkml.org/lkml/2019/4/10/973 It is really long, so I will not copy it completely here, please refer to the original for notes on design decisions and existing minor issues (other than quirks, which should be hopefully solved now). Yurii Pavlovskyi (11): platform/x86: asus-wmi: Fix hwmon device cleanup platform/x86: asus-wmi: Fix preserving keyboard backlight intensity on load platform/x86: asus-wmi: Increase the input buffer size of WMI methods platform/x86: asus-wmi: Improve DSTS WMI method ID detection platform/x86: asus-wmi: Support WMI event queue platform/x86: asus-nb-wmi: Add microphone mute key code platform/x86: asus-wmi: Organize code into sections platform/x86: asus-wmi: Enhance detection of thermal data platform/x86: asus-wmi: Control RGB keyboard backlight platform/x86: asus-wmi: Switch fan boost mode platform/x86: asus-wmi: Do not disable keyboard backlight on unloading .../ABI/testing/sysfs-platform-asus-wmi | 71 ++ drivers/platform/x86/asus-nb-wmi.c| 3 +- drivers/platform/x86/asus-wmi.c | 797 +++--- drivers/platform/x86/wmi.c| 19 + include/linux/acpi.h | 1 + include/linux/platform_data/x86/asus-wmi.h| 7 +- 6 files changed, 797 insertions(+), 101 deletions(-) -- 2.17.1
[PATCH v3 09/11] platform/x86: asus-wmi: Control RGB keyboard backlight
The WMI exposes two methods for controlling RGB keyboard backlight, which allows controlling: * RGB components in range 00 - ff, * Switch between 4 effects, * Switch between 3 effect speed modes, * Separately enable the backlight on boot, in the awake state (after driver load), in sleep mode, and probably in something called shutdown mode (no observable effects of enabling it are known so far). The configuration should be written to several sysfs parameter buffers which are then written via WMI by writing either 1 or 2 to the "kbbl_set" parameter. When reading the buffers the last written value is returned. If the 2 is written to "kbbl_set", the parameters will be reset on reboot (temporary mode), 1 is permanent mode, parameters are retained. The calls use new 3-dword input buffer method call. The functionality is only enabled if corresponding DSTS methods return exact valid values. The following script demonstrates usage: echo Red [00 - ff] echo 33 > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_red echo Green [00 - ff] echo ff > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_green echo Blue [00 - ff] echo 0 > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_blue echo Mode: 0 - static color, 1 - breathing, 2 - color cycle, 3 - strobing echo 0 > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_mode echo Speed for modes 1 and 2: 0 - slow, 1 - medium, 2 - fast echo 0 > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_speed echo Enable: 02 - on boot, before module load, 08 - awake, 20 - sleep, echo 2a or ff to set all echo 2a > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_flags echo Save: 1 - permanently, 2 - temporarily, reset after reboot echo 1 > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_set Signed-off-by: Yurii Pavlovskyi --- .../ABI/testing/sysfs-platform-asus-wmi | 61 drivers/platform/x86/asus-wmi.c | 331 ++ include/linux/platform_data/x86/asus-wmi.h| 2 + 3 files changed, 394 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-platform-asus-wmi b/Documentation/ABI/testing/sysfs-platform-asus-wmi index 019e1e29370e..1cc54d5e3e10 100644 --- a/Documentation/ABI/testing/sysfs-platform-asus-wmi +++ b/Documentation/ABI/testing/sysfs-platform-asus-wmi @@ -36,3 +36,64 @@ KernelVersion: 3.5 Contact: "AceLan Kao" Description: Resume on lid open. 1 means on, 0 means off. + +What: /sys/devices/platform//kbbl/kbbl_red +Date: Apr 2019 +KernelVersion: 5.1 +Contact: "Yurii Pavlovskyi" +Description: + RGB keyboard backlight red component: 00 .. ff. + +What: /sys/devices/platform//kbbl/kbbl_green +Date: Apr 2019 +KernelVersion: 5.1 +Contact: "Yurii Pavlovskyi" +Description: + RGB keyboard backlight green component: 00 .. ff. + +What: /sys/devices/platform//kbbl/kbbl_blue +Date: Apr 2019 +KernelVersion: 5.1 +Contact: "Yurii Pavlovskyi" +Description: + RGB keyboard backlight blue component: 00 .. ff. + +What: /sys/devices/platform//kbbl/kbbl_mode +Date: Apr 2019 +KernelVersion: 5.1 +Contact: "Yurii Pavlovskyi" +Description: + RGB keyboard backlight mode: + * 0 - static color, + * 1 - breathing, + * 2 - color cycle, + * 3 - strobing. + +What: /sys/devices/platform//kbbl/kbbl_speed +Date: Apr 2019 +KernelVersion: 5.1 +Contact: "Yurii Pavlovskyi" +Description: + RGB keyboard backlight speed for modes 1 and 2: + * 0 - slow, + * 1 - medium, + * 2 - fast. + +What: /sys/devices/platform//kbbl/kbbl_flags +Date: Apr 2019 +KernelVersion: 5.1 +Contact: "Yurii Pavlovskyi" +Description: + RGB keyboard backlight enable flags (2a to enable everything), OR of: + * 02 - on boot (until module load), + * 08 - awake, + * 20 - sleep. + +What: /sys/devices/platform//kbbl/kbbl_set +Date: Apr 2019 +KernelVersion: 5.1 +Contact: "Yurii Pavlovskyi" +Description: + Write changed RGB keyboard backlight parameters: + * 1 - permanently, + * 2 - temporarily. diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 1b8272374660..0a32079336d8 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -148,6 +148,21 @@ struct asus_rfkill { u32 dev_id; }; +struct asus_kbbl_rgb { + u8 kbbl_red; + u8 kbbl_green; + u8 kbbl_blue; + u8 kbbl_mode; + u8 kbbl_speed; + + u8 kbbl_set_red; + u8 kbbl_set_green; + u8 kbbl_set_b
Re: [PATCH v2 10/11] platform/x86: asus-wmi: Switch fan boost mode
On 12.04.19 10:03, Daniel Drake wrote: > On Thu, Apr 11, 2019 at 1:47 PM Yurii Pavlovskyi > wrote: >> * 0x00 - is normal, >> * 0x01 - is obviously turbo by the amount of noise, might be useful to >> avoid CPU frequency throttling on high load, >> * 0x02 - the meaning is unknown at the time as modes are not named >> in the vendor documentation, but it does look like a quiet mode as CPU >> temperature does increase about 10 degrees on maximum load. > > I'm curious which vendor documentation you're working with here. That would be user manual for FX505 series, which is pretty minimalistic. It says it has a fan mode switch key and that's it. Following up on your comment, I've searched more and actually did found their names hidden in marketing web page on the website. You're right, they call them balanced, overboost and silent there. > From the spec, > 0 = normal > 1 = overboost > 2 = silent > Also you can use DSTS on the 0x00110018 device to check the exact > capabilities supported, which you should use to refine which modes can > be cycled through. > Bit 0 = overboost supported > Bit 1 = silent supported > > Thanks > Daniel > Thanks! I was guessing that the 3 in DSTS must've meant something. Appreciate your comments! Will definitely implement them. I'm going to post the v3 patch series approximately middle of next week. Best regards, Yurii
Re: [PATCH v2 05/11] platform/x86: asus-wmi: Support queued WMI event codes
On 12.04.19 09:48, Daniel Drake wrote: > On Thu, Apr 11, 2019 at 1:44 PM Yurii Pavlovskyi > wrote: >> Event codes are expected to be polled from a queue on at least some >> models. > > Maybe avoid the word "poll" since it suggests something else (at least to me). Ok, will change this in the code as well. >>> The fix flushes the old key codes out of the queue on load and after >> receiving event the queue is read until either .. or 1 is encountered. >> >> It might be considered a minor issue and no normal user would likely to >> observe this (there is little reason unloading the driver), but it does >> significantly frustrate a developer who is unlucky enough to encounter >> this. >> >> Introduce functionality for flushing and processing queued codes, which is >> enabled via quirk flag for ASUS7000. It might be considered if it is >> reasonable to enable it everywhere (might introduce regressions) or always >> try to flush the queue on module load and try to detect if this quirk is >> present in the future. >> >> This patch limits the effect to the specific hardware defined by ASUS7000 >> device that is used for driver detection by vendor driver of Fx505. The >> fallback is also implemented in case initial flush fails. > > Which vendor driver are you referring to here? The ASUS Keyboard hotkeys Driver V2.0.5 which is available to download for FX505 has this in his INF file: [ATKP.ntamd64] %DeviceDesc1% = NO_DRV64, ACPI\ASUS7000 But this driver is not generic. It is limited to very specific hardware, I'd guess gaming series (for instance K54C does not have this device). It was rather a way to avoid breaking anything. I think your suggestion below is much better. > > Researching more: > In our database of 144 Asus models, 142 of them have GANQ. > > Of those 142, 9 of them return One in the empty-queue case. The other > 133 match your FX505GM device exactly. So it seems valid to interpret > both 0x and 0x1 as queue-end markers. > > The 2 devices that do not have GANQ are not laptops. They also do not > have the _UID "ATK" WMI device, they only have "ASUSWMI" and their WMI > _WED method does not use a queue. > There are a few more units that have both ASUSWMI and ATK devices, and > the ASUSWMI device appears to never be queued. > Another observation is that the ASUSWMI device works with notify code > 0xD2, and the ATK device works with 0xFF. > > Nailing this down a bit further, I found a DSDT for EEEPC X101H: that > one only has ASUSWMI and it is also not queued. > > So I think you should make this queue behaviour applied more > generically, but either avoid it when the WMI device _UID is not "ATK" > (as discussed in the DCTS/DSTS thread), or avoid it when the notify > code is not 0x> > Thanks > Daniel Thanks a lot for your research, it's much appreciated! That seems to confirm that these two quirks are actually connected with ATK device. I guess it makes sense to combine the detection for both of them. Also to flush the queue we need to know the notify code beforehand, because it is checked in _WED so checking for ATK seems reasonable to me. Best regards, Yurii
Re: [PATCH 04/11] platform/x86: asus-wmi: Add quirk to force DSTS WMI method detection
Wow that is a great thing you done, thanks a lot for your time and your kind words! Your suggestion indeed sounds good judging by your results. Both of my devices have UID "ATK" (actually FX505 also has two other PNP0C14 devices with HIDs SampleDev and TestDev, but I think they are disabled by default). It looks like the driver is loaded already only if ASUS_WMI_MGMT_GUID is present, so I guess that could be used for wmi_driver_register. A little obstacle is that ACPI device pointer is stored in wmi_block structure, which is internal to the wmi.c. This means we would have to add a new method to wmi.h / wmi.c for getting the UID of WMI ACPI device. I guess it might be possible to somehow navigate the device tree back to the platform driver of WMI module, but it would definitely be more obscure, and searching for the device by HID again is not only slower but generally would require a guarantee that it's the same device. So adding a new function looks reasonable to me. It might also be useful to someone implementing similar things for other vendors in the future. I'm going to implement this in updated patch. Thanks, Yurii On 11.04.19 12:55, Daniel Drake wrote: > On Thu, Apr 11, 2019 at 4:28 AM Yurii Pavlovskyi > wrote: >> The DSTS method detection fails, as nothing is returned if method is not >> defined in WMNB. As a result the control of keyboard backlight is not >> functional for TUF Gaming series laptops (at the time the only >> functionality of the driver on this model implemented with WMI methods). >> >> Patch was tested on a newer TUF Gaming FX505GM and older K54C model. >> >> FX505GM: >> Method (WMNB, 3, Serialized) >> { ... >> If ((Local0 == 0x53545344)) >> { >> ... >> Return (Zero) >> } >> ... >> // No return >> } >> >> K54C: >> Method (WMNB, 3, Serialized) >> { ... >> If ((Local0 == 0x53545344)) >> { >> ... >> Return (0x02) >> } >> ... >> Return (0xFFFE) >> } >> >> The non-existing method ASUS_WMI_METHODID_DSTS=0x53544344 (actually it is >> DCTS in little endian ASCII) is selected in asus->dsts. >> >> One way to fix this would be to call both for every known device ID until >> some answers - this would increase module load time. >> >> Another option is to check some device that is known to exist on every >> model - none known at the time. >> >> Last option, which is implemented, is to check for presence of the >> ASUS7000 device in ACPI tree (it is a dummy device), which is the >> condition used for loading the vendor driver for this model. This might >> not fix every affected model ever produced, but it likely does not >> introduce any regressions. The patch introduces a quirk that is enabled >> when ASUS7000 is found. >> >> Scope (_SB) >> { >> Device (ATK) >> { >> Name (_HID, "ASUS7000") // _HID: Hardware ID >> } >> } > > Hmm, tricky! But about time we did something about the DSTS vs DCTS guessing. > > While we don't have definitive knowledge of the right thing to do > here, I think I have enough info available to solidify some > assumptions we'd otherwise be afraid to make, and then we can > implement a better approach here. > > In our database of 144 Asus DSDTs, 14 of them respond to DCTS: > > AS_D520MT > D425MC > D640SA > D320SF-K > D415MT > D830MT > G11DF > M32CD4-K > V221ID > V272UN_SKU3 > Z240IE > ZN220IC-K > ZN241IC > ZN270IE > > Of those 14, they all additionally respond to DSTS, except for D415MT > and AS_D520MT. > > In all 14 cases, the DCTS handling is done inside a device with _UID > ASUSWMI. None of the other 130 products have a device with that _UID. > > Furthermore, we recently received access to the ASUS spec, which > confirms that the Instance Name for EeePC is "ACPI\PNP0C14\ASUSWMI_0" > whereas the Instance Name for other notebooks is "ACPI\PNP0C14\ATK_0". > > The 12 devices that respond to both DCTS and DSTS, do it on separate > different devices, albeit with the same _WDG UUID. The one with _UID > ASUSWMI responds to DCTS, and the one with _UID ATK responds to DSTS. > > So that seems fairly convincing. My thinking is that we can check the > _UID of the underlying device, and use DCTS for ASUSWMI, DSTS > otherwise. To do that, I think you'd have to rework the driver probing > so that it happens through wmi_driver_register(). Then from the probe > function onwards you will get a wmi_device, and hopefully you can get > the _UID through that instance somehow. > > There's a separate issue of what happens on those 12 machines where > there are two WMI devs, with the same _WDG GUID. > drivers/platform/x86/wmi.c drops duplicates, so one of them is being > ignored in that case. We'd ideally find a way to ignore the ASUSWMI > node and go with ATK in that situation. But I think this can be > separated from your work here. > > Thanks for these patches and welcome to the world of kernel development! > > Daniel >
Re: [PATCH 01/11] platform/x86: asus-wmi: Fix hwmon device cleanup
Hello Daniel, thank you for the tip. I did not know about this function. It does indeed seem to make this complete patch redundant looking at 6e5f62b9e3651e61 hwmon: (lm90) Use devm_hwmon_device_register_with_groups I will surely implement it this way in the next version. Best regards, Yurii On 11.04.19 10:21, Daniel Drake wrote: > On Thu, Apr 11, 2019 at 4:21 AM Yurii Pavlovskyi > wrote: >> >> The asus-wmi driver does not clean up the hwmon device on exit or error. >> To reproduce the bug, repeat rmmod, insmod to verify that device number >> /sys/devices/platform/asus-nb-wmi/hwmon/hwmon?? grows every time. Add >> pointer to the device in module state and call cleanup on error. > > I wonder if this can be fixed more cleanly by using > devm_hwmon_device_register_with_groups() ? > > Thanks > Daniel >
[PATCH v2 11/11] platform/x86: asus-wmi: Do not disable keyboard backlight on unload
The keyboard backlight is disabled when module is unloaded as it is exposed as LED device. Change this behavior to ignore setting 0 brightness when the ledclass device is unloading. Signed-off-by: Yurii Pavlovskyi --- drivers/platform/x86/asus-wmi.c | 4 1 file changed, 4 insertions(+) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index f0e506feb924..f49992fa87b3 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -475,6 +475,10 @@ static void do_kbd_led_set(struct led_classdev *led_cdev, int value) static void kbd_led_set(struct led_classdev *led_cdev, enum led_brightness value) { + /* Prevent disabling keyboard backlight on module unregister */ + if (led_cdev->flags & LED_UNREGISTERING) + return; + do_kbd_led_set(led_cdev, value); } -- 2.17.1
[PATCH v2 09/11] platform/x86: asus-wmi: Control RGB keyboard backlight
The WMI exposes two methods for controlling RGB keyboard backlight which allow to control: * RGB components in range 00 - ff, * Switch between 4 effects, * Switch between 3 effect speed modes, * Separately enable the backlight on boot, in awake state (after driver load), in sleep mode, and probably in something called shutdown mode (no observable effects of enabling it are known so far). The configuration should be written to several sysfs parameter buffers which are then written via WMI by writing either 1 or 2 to the "kbbl_set" parameter. When reading the buffers the last written value is returned. If the 2 is written to "kbbl_set", the parameters will be reset on reboot (temporary mode), 1 is permanent mode, parameters are retained. The calls use new 3-dword input buffer method call. The functionality is only enabled if corresponding DSTS methods return exact valid values. The following script demonstrates usage: echo Red [00 - ff] echo 33 > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_red echo Green [00 - ff] echo ff > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_green echo Blue [00 - ff] echo 0 > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_blue echo Mode: 0 - static color, 1 - blink, 2 - rainbow, 3 - strobe echo 0 > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_mode echo Speed for modes 1 and 2: 0 - slow, 1 - medium, 2 - fast echo 0 > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_speed echo Enable: 02 - on boot, before module load, 08 - awake, 20 - sleep, echo 2a or ff to set all echo 2a > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_flags echo Save: 1 - permanently, 2 - temporarily, reset after reboot echo 1 > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_set Signed-off-by: Yurii Pavlovskyi --- .../ABI/testing/sysfs-platform-asus-wmi | 61 drivers/platform/x86/asus-wmi.c | 329 ++ include/linux/platform_data/x86/asus-wmi.h| 2 + 3 files changed, 392 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-platform-asus-wmi b/Documentation/ABI/testing/sysfs-platform-asus-wmi index 019e1e29370e..300a40519695 100644 --- a/Documentation/ABI/testing/sysfs-platform-asus-wmi +++ b/Documentation/ABI/testing/sysfs-platform-asus-wmi @@ -36,3 +36,64 @@ KernelVersion: 3.5 Contact: "AceLan Kao" Description: Resume on lid open. 1 means on, 0 means off. + +What: /sys/devices/platform//kbbl/kbbl_red +Date: Apr 2019 +KernelVersion: 5.1 +Contact: "Yurii Pavlovskyi" +Description: + RGB keyboard backlight red component: 00 .. ff. + +What: /sys/devices/platform//kbbl/kbbl_green +Date: Apr 2019 +KernelVersion: 5.1 +Contact: "Yurii Pavlovskyi" +Description: + RGB keyboard backlight green component: 00 .. ff. + +What: /sys/devices/platform//kbbl/kbbl_blue +Date: Apr 2019 +KernelVersion: 5.1 +Contact: "Yurii Pavlovskyi" +Description: + RGB keyboard backlight blue component: 00 .. ff. + +What: /sys/devices/platform//kbbl/kbbl_mode +Date: Apr 2019 +KernelVersion: 5.1 +Contact: "Yurii Pavlovskyi" +Description: + RGB keyboard backlight mode: + * 0 - static color, + * 1 - blink, + * 2 - rainbow, + * 3 - strobe. + +What: /sys/devices/platform//kbbl/kbbl_speed +Date: Apr 2019 +KernelVersion: 5.1 +Contact: "Yurii Pavlovskyi" +Description: + RGB keyboard backlight speed for modes 1 and 2: + * 0 - slow, + * 1 - medium, + * 2 - fast. + +What: /sys/devices/platform//kbbl/kbbl_flags +Date: Apr 2019 +KernelVersion: 5.1 +Contact: "Yurii Pavlovskyi" +Description: + RGB keyboard backlight enable flags (2a to enable everything), OR of: + * 02 - on boot (until module load), + * 08 - awake, + * 20 - sleep. + +What: /sys/devices/platform//kbbl/kbbl_set +Date: Apr 2019 +KernelVersion: 5.1 +Contact: "Yurii Pavlovskyi" +Description: + Write changed RGB keyboard backlight parameters: + * 1 - permanently, + * 2 - temporarily. diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index de0a8f61d4a1..b4fd200e8335 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -145,6 +145,21 @@ struct asus_rfkill { u32 dev_id; }; +struct asus_kbbl_rgb { + u8 kbbl_red; + u8 kbbl_green; + u8 kbbl_blue; + u8 kbbl_mode; + u8 kbbl_speed; + + u8 kbbl_set_red; + u8 kbbl_set_green; + u8 kbbl_set_blue; + u8 kbbl_set_m
[PATCH v2 10/11] platform/x86: asus-wmi: Switch fan boost mode
The WMI exposes a write-only device ID where three modes can be switched on some laptops (TUF Gaming FX505GM). There is a hotkey combination Fn-F5 that does have a fan icon which is designed to toggle between these 3 modes. Add a SysFS entry that reads the last written value and updates value in WMI on write and a hotkey handler that toggles the modes. The corresponding DEVS device handler does obviously take 3 possible argument values. Method (SFBM, 1, NotSerialized) { If ((Arg0 == Zero) { .. } If ((Arg0 == One)) { .. } If ((Arg0 == 0x02)) { .. } } ... // DEVS If ((IIA0 == 0x00110018)) { SFBM (IIA1) Return (One) } * 0x00 - is normal, * 0x01 - is obviously turbo by the amount of noise, might be useful to avoid CPU frequency throttling on high load, * 0x02 - the meaning is unknown at the time as modes are not named in the vendor documentation, but it does look like a quiet mode as CPU temperature does increase about 10 degrees on maximum load. Signed-off-by: Yurii Pavlovskyi --- .../ABI/testing/sysfs-platform-asus-wmi | 10 ++ drivers/platform/x86/asus-wmi.c | 119 -- include/linux/platform_data/x86/asus-wmi.h| 1 + 3 files changed, 117 insertions(+), 13 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-platform-asus-wmi b/Documentation/ABI/testing/sysfs-platform-asus-wmi index 300a40519695..2b3184e297a7 100644 --- a/Documentation/ABI/testing/sysfs-platform-asus-wmi +++ b/Documentation/ABI/testing/sysfs-platform-asus-wmi @@ -97,3 +97,13 @@ Description: Write changed RGB keyboard backlight parameters: * 1 - permanently, * 2 - temporarily. + +What: /sys/devices/platform//fan_mode +Date: Apr 2019 +KernelVersion: 5.1 +Contact: "Yurii Pavlovskyi" +Description: + Fan boost mode: + * 0 - normal, + * 1 - turbo, + * 2 - quiet? diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index b4fd200e8335..f0e506feb924 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -69,6 +69,7 @@ MODULE_LICENSE("GPL"); #define NOTIFY_KBD_BRTUP 0xc4 #define NOTIFY_KBD_BRTDWN 0xc5 #define NOTIFY_KBD_BRTTOGGLE 0xc7 +#define NOTIFY_KBD_FBM 0x99 #define ASUS_FAN_DESC "cpu_fan" #define ASUS_FAN_MFUN 0x13 @@ -77,6 +78,8 @@ MODULE_LICENSE("GPL"); #define ASUS_FAN_CTRL_MANUAL 1 #define ASUS_FAN_CTRL_AUTO 2 +#define ASUS_FAN_MODE_COUNT3 + #define USB_INTEL_XUSB2PR 0xD0 #define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31 @@ -196,6 +199,9 @@ struct asus_wmi { int asus_hwmon_num_fans; int asus_hwmon_pwm; + bool fan_mode_available; + u8 fan_mode; + bool kbbl_rgb_available; struct asus_kbbl_rgb kbbl_rgb; @@ -1832,6 +1838,87 @@ static int asus_wmi_fan_init(struct asus_wmi *asus) return 0; } +/* Fan mode ***/ + +static int fan_mode_check_present(struct asus_wmi *asus) +{ + u32 result; + int err; + + asus->fan_mode_available = false; + + err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_FAN_MODE, ); + if (err) { + if (err == -ENODEV) + return 0; + else + return err; + } + + if (result & ASUS_WMI_DSTS_PRESENCE_BIT) + asus->fan_mode_available = true; + + return 0; +} + +static int fan_mode_write(struct asus_wmi *asus) +{ + int err; + u8 value; + u32 retval; + + value = asus->fan_mode % ASUS_FAN_MODE_COUNT; + pr_info("Set fan mode: %u\n", value); + err = asus_wmi_set_devstate(ASUS_WMI_DEVID_FAN_MODE, value, ); + + if (err) { + pr_warn("Failed to set fan mode: %d\n", err); + return err; + } + + if (retval != 1) { + pr_warn("Failed to set fan mode (retval): 0x%x\n", retval); + return -EIO; + } + + return 0; +} + +static int fan_mode_switch_next(struct asus_wmi *asus) +{ + asus->fan_mode = (asus->fan_mode + 1) % ASUS_FAN_MODE_COUNT; + return fan_mode_write(asus); +} + +static ssize_t fan_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct asus_wmi *asus = dev_get_drvdata(dev); + + return show_u8(asus->fan_mode, buf); +} + +static ssize_t fan_mode_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int result; + u8 new_mode; + + struct asus_wmi *asus = dev_get_drvdata(dev); + + result = store_
[PATCH v2 08/11] platform/x86: asus-wmi: Enhance detection of thermal data
The obviously wrong value 1 for temperature device ID in this driver is returned by at least some devices, including TUF Gaming series laptops, instead of 0 as expected previously. Observable effect is that a temp1_input in hwmon reads temperature near absolute zero. * Consider 0.1 K as erroneous value in addition to 0 K. * Refactor detection of thermal input availability to a separate function. Signed-off-by: Yurii Pavlovskyi --- drivers/platform/x86/asus-wmi.c | 45 - 1 file changed, 38 insertions(+), 7 deletions(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index a98df005d6cb..de0a8f61d4a1 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -176,6 +176,7 @@ struct asus_wmi { struct asus_rfkill gps; struct asus_rfkill uwb; + bool asus_hwmon_thermal_available; bool asus_hwmon_fan_manual_mode; int asus_hwmon_num_fans; int asus_hwmon_pwm; @@ -1373,6 +1374,32 @@ static struct attribute *hwmon_attributes[] = { NULL }; +static int asus_hwmon_check_thermal_available(struct asus_wmi *asus) +{ + u32 value = ASUS_WMI_UNSUPPORTED_METHOD; + int err; + + asus->asus_hwmon_thermal_available = false; + err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_THERMAL_CTRL, ); + + if (err < 0) { + if (err == -ENODEV) + return 0; + + return err; + } + + /* +* If the temperature value in deci-Kelvin is near the absolute +* zero temperature, something is clearly wrong. +*/ + if (!value || value == 1) + return 0; + + asus->asus_hwmon_thermal_available = true; + return 0; +} + static umode_t asus_hwmon_sysfs_is_visible(struct kobject *kobj, struct attribute *attr, int idx) { @@ -1386,8 +1413,6 @@ static umode_t asus_hwmon_sysfs_is_visible(struct kobject *kobj, if (attr == _attr_pwm1.attr) dev_id = ASUS_WMI_DEVID_FAN_CTRL; - else if (attr == _attr_temp1_input.attr) - dev_id = ASUS_WMI_DEVID_THERMAL_CTRL; if (attr == _attr_fan1_input.attr || attr == _attr_fan1_label.attr @@ -1412,15 +1437,13 @@ static umode_t asus_hwmon_sysfs_is_visible(struct kobject *kobj, * - reverved bits are non-zero * - sfun and presence bit are not set */ - if (value == ASUS_WMI_UNSUPPORTED_METHOD || value & 0xFFF8 + if (value == ASUS_WMI_UNSUPPORTED_METHOD || (value & 0xFFF8) || (!asus->sfun && !(value & ASUS_WMI_DSTS_PRESENCE_BIT))) ok = false; else ok = fan_attr <= asus->asus_hwmon_num_fans; - } else if (dev_id == ASUS_WMI_DEVID_THERMAL_CTRL) { - /* If value is zero, something is clearly wrong */ - if (!value) - ok = false; + } else if (attr == _attr_temp1_input.attr) { + ok = asus->asus_hwmon_thermal_available; } else if (fan_attr <= asus->asus_hwmon_num_fans && fan_attr != -1) { ok = true; } else { @@ -1476,6 +1499,14 @@ static int asus_wmi_fan_init(struct asus_wmi *asus) } pr_info("Number of fans: %d\n", asus->asus_hwmon_num_fans); + + status = asus_hwmon_check_thermal_available(asus); + if (status) { + pr_warn("Could not check if thermal available: %d\n", status); + return -ENXIO; + } + + pr_info("Thermal available: %d\n", asus->asus_hwmon_thermal_available); return 0; } -- 2.17.1
[PATCH v2 06/11] platform/x86: asus-nb-wmi: Add microphone mute key code
The microphone mute key that is present on FX505GM laptop and possibly others is missing from sparse keymap. Add the missing code. Also comment on the fan mode switch key that has the same code as the already used key. Signed-off-by: Yurii Pavlovskyi --- drivers/platform/x86/asus-nb-wmi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c index 357d273ed336..39cf447198a9 100644 --- a/drivers/platform/x86/asus-nb-wmi.c +++ b/drivers/platform/x86/asus-nb-wmi.c @@ -474,6 +474,7 @@ static const struct key_entry asus_nb_wmi_keymap[] = { { KE_KEY, 0x6B, { KEY_TOUCHPAD_TOGGLE } }, { KE_IGNORE, 0x6E, }, /* Low Battery notification */ { KE_KEY, 0x7a, { KEY_ALS_TOGGLE } }, /* Ambient Light Sensor Toggle */ + { KE_KEY, 0x7c, { KEY_MICMUTE } }, { KE_KEY, 0x7D, { KEY_BLUETOOTH } }, /* Bluetooth Enable */ { KE_KEY, 0x7E, { KEY_BLUETOOTH } }, /* Bluetooth Disable */ { KE_KEY, 0x82, { KEY_CAMERA } }, @@ -488,7 +489,7 @@ static const struct key_entry asus_nb_wmi_keymap[] = { { KE_KEY, 0x92, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + TV + DVI */ { KE_KEY, 0x93, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + TV + DVI */ { KE_KEY, 0x95, { KEY_MEDIA } }, - { KE_KEY, 0x99, { KEY_PHONE } }, + { KE_KEY, 0x99, { KEY_PHONE } }, /* Conflicts with fan mode switch */ { KE_KEY, 0xA0, { KEY_SWITCHVIDEOMODE } }, /* SDSP HDMI only */ { KE_KEY, 0xA1, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + HDMI */ { KE_KEY, 0xA2, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + HDMI */ -- 2.17.1
[PATCH v2 07/11] platform/x86: asus-wmi: Organize code into sections
The driver has grown (and will more) pretty big which makes it hard to navigate and understand. Add uniform comments to the code and ensure that it is sorted into logical sections. Signed-off-by: Yurii Pavlovskyi --- drivers/platform/x86/asus-wmi.c | 94 - 1 file changed, 46 insertions(+), 48 deletions(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 5aa30f8a2a38..a98df005d6cb 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -191,6 +191,8 @@ struct asus_wmi { struct asus_wmi_driver *driver; }; +/* Input **/ + static int asus_wmi_input_init(struct asus_wmi *asus) { int err; @@ -228,6 +230,8 @@ static void asus_wmi_input_exit(struct asus_wmi *asus) asus->inputdev = NULL; } +/* WMI / + static int asus_wmi_evaluate_method_3dw(u32 method_id, u32 arg0, u32 arg1, u32 arg2, u32 *retval) { @@ -246,7 +250,7 @@ static int asus_wmi_evaluate_method_3dw(u32 method_id, u32 arg0, u32 arg1, , ); if (ACPI_FAILURE(status)) - goto exit; + return -EIO; obj = (union acpi_object *)output.pointer; if (obj && obj->type == ACPI_TYPE_INTEGER) @@ -257,10 +261,6 @@ static int asus_wmi_evaluate_method_3dw(u32 method_id, u32 arg0, u32 arg1, kfree(obj); -exit: - if (ACPI_FAILURE(status)) - return -EIO; - if (tmp == ASUS_WMI_UNSUPPORTED_METHOD) return -ENODEV; @@ -344,9 +344,8 @@ static int asus_wmi_get_devstate_simple(struct asus_wmi *asus, u32 dev_id) ASUS_WMI_DSTS_STATUS_BIT); } -/* - * LEDs - */ +/* LEDs ***/ + /* * These functions actually update the LED's, and are called from a * workqueue. By doing this as separate work rather than when the LED @@ -656,6 +655,7 @@ static int asus_wmi_led_init(struct asus_wmi *asus) return rv; } +/* RF */ /* * PCI hotplug (for wlan rfkill) @@ -1078,6 +1078,8 @@ static int asus_wmi_rfkill_init(struct asus_wmi *asus) return result; } +/* Quirks */ + static void asus_wmi_set_xusb2pr(struct asus_wmi *asus) { struct pci_dev *xhci_pdev; @@ -1110,9 +1112,8 @@ static void asus_wmi_set_als(void) asus_wmi_set_devstate(ASUS_WMI_DEVID_ALS_ENABLE, 1, NULL); } -/* - * Hwmon device - */ +/* Hwmon device ***/ + static int asus_hwmon_agfn_fan_speed_read(struct asus_wmi *asus, int fan, int *speed) { @@ -1388,7 +1389,6 @@ static umode_t asus_hwmon_sysfs_is_visible(struct kobject *kobj, else if (attr == _attr_temp1_input.attr) dev_id = ASUS_WMI_DEVID_THERMAL_CTRL; - if (attr == _attr_fan1_input.attr || attr == _attr_fan1_label.attr || attr == _attr_pwm1.attr @@ -1460,9 +1460,27 @@ static void asus_wmi_hwmon_exit(struct asus_wmi *asus) } } -/* - * Backlight - */ +static int asus_wmi_fan_init(struct asus_wmi *asus) +{ + int status; + + asus->asus_hwmon_pwm = -1; + asus->asus_hwmon_num_fans = -1; + asus->asus_hwmon_fan_manual_mode = false; + + status = asus_hwmon_get_fan_number(asus, >asus_hwmon_num_fans); + if (status) { + asus->asus_hwmon_num_fans = 0; + pr_warn("Could not determine number of fans: %d\n", status); + return -ENXIO; + } + + pr_info("Number of fans: %d\n", asus->asus_hwmon_num_fans); + return 0; +} + +/* Backlight **/ + static int read_backlight_power(struct asus_wmi *asus) { int ret; @@ -1644,6 +1662,8 @@ static int is_display_toggle(int code) return 0; } +/* WMI events */ + static int asus_poll_wmi_event(u32 value) { struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; @@ -1766,9 +1786,8 @@ static int asus_wmi_notify_queue_flush(struct asus_wmi *asus) return -EIO; } -/* - * Sys helpers - */ +/* Sysfs **/ + static int parse_arg(const char *buf, unsigned long count, int *val) { if (!count) @@ -1907,9 +1926,8 @@ static int asus_wmi_sysfs_init(struct platform_device *device) return sysfs_create_group(>dev.kobj, _attribute_group)
[PATCH v2 05/11] platform/x86: asus-wmi: Support queued WMI event codes
Event codes are expected to be polled from a queue on at least some models. The WMI event codes are pushed into queue based on circular buffer. After INIT method is called ACPI code is allowed to push events into this buffer the INIT method can not be reverted. If the module is unloaded and an event (such as hotkey press) gets emitted before inserting it back the events get processed delayed by one or, if the queue overflows, additionally delayed by about 3 seconds. Patch was tested on a newer TUF Gaming FX505GM and older K54C model. FX505GM Device (ATKD) { .. Name (ATKQ, Package (0x10) { 0x, .. } Method (IANQ, 1, Serialized) { If ((AQNO >= 0x10)) { Local0 = 0x64 While ((Local0 && (AQNO >= 0x10))) { Local0-- Sleep (0x0A) } ... .. AQTI++ AQTI &= 0x0F ATKQ [AQTI] = Arg0 ... } Method (GANQ, 0, Serialized) { .. If (AQNO) { ... Local0 = DerefOf (ATKQ [AQHI]) AQHI++ AQHI &= 0x0F Return (Local0) } Return (One) } This code is almost identical to K54C, which does return Ones on empty queue. K54C: Method (GANQ, 0, Serialized) { If (AQNO) { ... Return (Local0) } Return (Ones) } The fix flushes the old key codes out of the queue on load and after receiving event the queue is read until either .. or 1 is encountered. It might be considered a minor issue and no normal user would likely to observe this (there is little reason unloading the driver), but it does significantly frustrate a developer who is unlucky enough to encounter this. Introduce functionality for flushing and processing queued codes, which is enabled via quirk flag for ASUS7000. It might be considered if it is reasonable to enable it everywhere (might introduce regressions) or always try to flush the queue on module load and try to detect if this quirk is present in the future. This patch limits the effect to the specific hardware defined by ASUS7000 device that is used for driver detection by vendor driver of Fx505. The fallback is also implemented in case initial flush fails. Signed-off-by: Yurii Pavlovskyi --- drivers/platform/x86/asus-nb-wmi.c | 1 + drivers/platform/x86/asus-wmi.c| 122 ++--- drivers/platform/x86/asus-wmi.h| 2 + 3 files changed, 97 insertions(+), 28 deletions(-) diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c index cc5f0765a8d9..357d273ed336 100644 --- a/drivers/platform/x86/asus-nb-wmi.c +++ b/drivers/platform/x86/asus-nb-wmi.c @@ -438,6 +438,7 @@ static void asus_nb_wmi_quirks(struct asus_wmi_driver *driver) if (acpi_dev_found("ASUS7000")) { driver->quirks->force_dsts = true; + driver->quirks->wmi_event_queue = true; } } diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 80f3447734fc..5aa30f8a2a38 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -80,6 +80,12 @@ MODULE_LICENSE("GPL"); #define USB_INTEL_XUSB2PR 0xD0 #define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31 +#define WMI_EVENT_QUEUE_SIZE 0x10 +#define WMI_EVENT_QUEUE_END0x1 +#define WMI_EVENT_MASK 0x +/* The event value is always the same. */ +#define WMI_EVENT_VALUE0xFF + static const char * const ashs_ids[] = { "ATK4001", "ATK4002", NULL }; static bool ashs_present(void) @@ -143,6 +149,7 @@ struct asus_wmi { int dsts_id; int spec; int sfun; + bool wmi_event_queue; struct input_dev *inputdev; struct backlight_device *backlight_device; @@ -1637,77 +1644,126 @@ static int is_display_toggle(int code) return 0; } -static void asus_wmi_notify(u32 value, void *context) +static int asus_poll_wmi_event(u32 value) { - struct asus_wmi *asus = context; - struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; union acpi_object *obj; acpi_status status; - int code; - int orig_code; - unsigned int key_value = 1; - bool autorelease = 1; + int code = -EIO; - status = wmi_get_event_data(value, ); - if (status != AE_OK) { - pr_err("bad event status 0x%x\n", status); - return; + status = wmi_get_event_data(value, ); + if (ACPI_FAILURE(status)) { + pr_warn("Failed to get WMI event code: %s\n", + acpi_format_exception(status)); + return code; } -
[PATCH v2 04/11] platform/x86: asus-wmi: Add quirk to force DSTS WMI method detection
The DSTS method detection fails, as nothing is returned if method is not defined in WMNB. As a result the control of keyboard backlight is not functional for TUF Gaming series laptops (at the time the only functionality of the driver on this model implemented with WMI methods). Patch was tested on a newer TUF Gaming FX505GM and older K54C model. FX505GM: Method (WMNB, 3, Serialized) { ... If ((Local0 == 0x53545344)) { ... Return (Zero) } ... // No return } K54C: Method (WMNB, 3, Serialized) { ... If ((Local0 == 0x53545344)) { ... Return (0x02) } ... Return (0xFFFE) } The non-existing method ASUS_WMI_METHODID_DSTS=0x53544344 (actually it is DCTS in little endian ASCII) is selected in asus->dsts. One way to fix this would be to call both for every known device ID until some answers - this would increase module load time. Another option is to check some device that is known to exist on every model - none known at the time. Last option, which is implemented, is to check for presence of the ASUS7000 device in ACPI tree (it is a dummy device), which is the condition used for loading the vendor driver for this model. This might not fix every affected model ever produced, but it likely does not introduce any regressions. The patch introduces a quirk that is enabled when ASUS7000 is found. Scope (_SB) { Device (ATK) { Name (_HID, "ASUS7000") // _HID: Hardware ID } } Signed-off-by: Yurii Pavlovskyi --- drivers/platform/x86/asus-nb-wmi.c | 5 + drivers/platform/x86/asus-wmi.c| 14 -- drivers/platform/x86/asus-wmi.h| 5 + 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c index b6f2ff95c3ed..cc5f0765a8d9 100644 --- a/drivers/platform/x86/asus-nb-wmi.c +++ b/drivers/platform/x86/asus-nb-wmi.c @@ -28,6 +28,7 @@ #include #include #include +#include #include "asus-wmi.h" @@ -434,6 +435,10 @@ static void asus_nb_wmi_quirks(struct asus_wmi_driver *driver) } pr_info("Using i8042 filter function for receiving events\n"); } + + if (acpi_dev_found("ASUS7000")) { + driver->quirks->force_dsts = true; + } } static const struct key_entry asus_nb_wmi_keymap[] = { diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index cfccfc0b8c2f..80f3447734fc 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -1885,11 +1885,21 @@ static int asus_wmi_platform_init(struct asus_wmi *asus) * Note, on most Eeepc, there is no way to check if a method exist * or note, while on notebooks, they returns 0xFFFE on failure, * but once again, SPEC may probably be used for that kind of things. +* +* Additionally at least TUF Gaming series laptops return 0 for unknown +* methods, so the detection in this way is not possible and method must +* be forced. Likely the presence of ACPI device ASUS7000 indicates +* this. */ - if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS, 0, 0, NULL)) + if (asus->driver->quirks->force_dsts) { + pr_info("DSTS method forced\n"); + asus->dsts_id = ASUS_WMI_METHODID_DSTS2; + } else if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS, + 0, 0, NULL)) { asus->dsts_id = ASUS_WMI_METHODID_DSTS; - else + } else { asus->dsts_id = ASUS_WMI_METHODID_DSTS2; + } /* CWAP allow to define the behavior of the Fn+F2 key, * this method doesn't seems to be present on Eee PCs */ diff --git a/drivers/platform/x86/asus-wmi.h b/drivers/platform/x86/asus-wmi.h index 6c1311f4b04d..94056da02fde 100644 --- a/drivers/platform/x86/asus-wmi.h +++ b/drivers/platform/x86/asus-wmi.h @@ -54,6 +54,11 @@ struct quirk_entry { */ int no_display_toggle; u32 xusb2pr; + /** +* Force DSTS instead of DSCS and skip detection. Useful if WMNB +* returns nothing on unknown method call. +*/ + bool force_dsts; bool (*i8042_filter)(unsigned char data, unsigned char str, struct serio *serio); -- 2.17.1
Re: [PATCH 00/11] asus-wmi: Support of ASUS TUF Gaming series laptops
Hi, sorry, just realized, I've broken the logging. I will re-post patches 04 to 11 as replies to original ones, 1 to 3 were ok.
[PATCH 11/11] platform/x86: asus-wmi: Do not disable keyboard backlight on unload
The keyboard backlight is disabled when module is unloaded as it is exposed as LED device. Change this behavior to ignore setting 0 brightness when the ledclass device is unloading. Signed-off-by: Yurii Pavlovskyi --- drivers/platform/x86/asus-wmi.c | 4 1 file changed, 4 insertions(+) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 941c628945ac..a0ffdd99eae2 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -475,6 +475,10 @@ static void do_kbd_led_set(struct led_classdev *led_cdev, int value) static void kbd_led_set(struct led_classdev *led_cdev, enum led_brightness value) { + /* Prevent disabling keyboard backlight on module unregister */ + if (led_cdev->flags & LED_UNREGISTERING) + return; + do_kbd_led_set(led_cdev, value); } -- 2.17.1
[PATCH 10/11] platform/x86: asus-wmi: Switch fan boost mode
The WMI exposes a write-only device ID where three modes can be switched on some laptops (TUF Gaming FX505GM). There is a hotkey combination Fn-F5 that does have a fan icon which is designed to toggle between these 3 modes. Add a SysFS entry that reads the last written value and updates value in WMI on write and a hotkey handler that toggles the modes. The corresponding DEVS device handler does obviously take 3 possible argument values. Method (SFBM, 1, NotSerialized) { If ((Arg0 == Zero) { .. } If ((Arg0 == One)) { .. } If ((Arg0 == 0x02)) { .. } } ... // DEVS If ((IIA0 == 0x00110018)) { SFBM (IIA1) Return (One) } * 0x00 - is normal, * 0x01 - is obviously turbo by the amount of noise, might be useful to avoid CPU frequency throttling on high load, * 0x02 - the meaning is unknown at the time as modes are not named in the vendor documentation, but it does look like a quiet mode as CPU temperature does increase about 10 degrees on maximum load. Signed-off-by: Yurii Pavlovskyi --- .../ABI/testing/sysfs-platform-asus-wmi | 10 ++ drivers/platform/x86/asus-wmi.c | 119 -- include/linux/platform_data/x86/asus-wmi.h| 1 + 3 files changed, 117 insertions(+), 13 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-platform-asus-wmi b/Documentation/ABI/testing/sysfs-platform-asus-wmi index 300a40519695..2b3184e297a7 100644 --- a/Documentation/ABI/testing/sysfs-platform-asus-wmi +++ b/Documentation/ABI/testing/sysfs-platform-asus-wmi @@ -97,3 +97,13 @@ Description: Write changed RGB keyboard backlight parameters: * 1 - permanently, * 2 - temporarily. + +What: /sys/devices/platform//fan_mode +Date: Apr 2019 +KernelVersion: 5.1 +Contact: "Yurii Pavlovskyi" +Description: + Fan boost mode: + * 0 - normal, + * 1 - turbo, + * 2 - quiet? diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index f4323a57f22f..941c628945ac 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -69,6 +69,7 @@ MODULE_LICENSE("GPL"); #define NOTIFY_KBD_BRTUP 0xc4 #define NOTIFY_KBD_BRTDWN 0xc5 #define NOTIFY_KBD_BRTTOGGLE 0xc7 +#define NOTIFY_KBD_FBM 0x99 #define ASUS_FAN_DESC "cpu_fan" #define ASUS_FAN_MFUN 0x13 @@ -77,6 +78,8 @@ MODULE_LICENSE("GPL"); #define ASUS_FAN_CTRL_MANUAL 1 #define ASUS_FAN_CTRL_AUTO 2 +#define ASUS_FAN_MODE_COUNT3 + #define USB_INTEL_XUSB2PR 0xD0 #define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31 @@ -196,6 +199,9 @@ struct asus_wmi { int asus_hwmon_num_fans; int asus_hwmon_pwm; + bool fan_mode_available; + u8 fan_mode; + bool kbbl_rgb_available; struct asus_kbbl_rgb kbbl_rgb; @@ -1833,6 +1839,87 @@ static int asus_wmi_fan_init(struct asus_wmi *asus) return 0; } +/* Fan mode ***/ + +static int fan_mode_check_present(struct asus_wmi *asus) +{ + u32 result; + int err; + + asus->fan_mode_available = false; + + err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_FAN_MODE, ); + if (err) { + if (err == -ENODEV) + return 0; + else + return err; + } + + if (result & ASUS_WMI_DSTS_PRESENCE_BIT) + asus->fan_mode_available = true; + + return 0; +} + +static int fan_mode_write(struct asus_wmi *asus) +{ + int err; + u8 value; + u32 retval; + + value = asus->fan_mode % ASUS_FAN_MODE_COUNT; + pr_info(PR "Set fan mode: %u\n", value); + err = asus_wmi_set_devstate(ASUS_WMI_DEVID_FAN_MODE, value, ); + + if (err) { + pr_warn(PR "Failed to set fan mode: %d\n", err); + return err; + } + + if (retval != 1) { + pr_warn(PR "Failed to set fan mode (retval): 0x%x\n", retval); + return -EIO; + } + + return 0; +} + +static int fan_mode_switch_next(struct asus_wmi *asus) +{ + asus->fan_mode = (asus->fan_mode + 1) % ASUS_FAN_MODE_COUNT; + return fan_mode_write(asus); +} + +static ssize_t fan_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct asus_wmi *asus = dev_get_drvdata(dev); + + return show_u8(asus->fan_mode, buf); +} + +static ssize_t fan_mode_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int result; + u8 new_mode; + + struct asus_wmi *asus = dev_get_drvdata(dev); + +
[PATCH 09/11] platform/x86: asus-wmi: Control RGB keyboard backlight
The WMI exposes two methods for controlling RGB keyboard backlight which allow to control: * RGB components in range 00 - ff, * Switch between 4 effects, * Switch between 3 effect speed modes, * Separately enable the backlight on boot, in awake state (after driver load), in sleep mode, and probably in something called shutdown mode (no observable effects of enabling it are known so far). The configuration should be written to several sysfs parameter buffers which are then written via WMI by writing either 1 or 2 to the "kbbl_set" parameter. When reading the buffers the last written value is returned. If the 2 is written to "kbbl_set", the parameters will be reset on reboot (temporary mode), 1 is permanent mode, parameters are retained. The calls use new 3-dword input buffer method call. The functionality is only enabled if corresponding DSTS methods return exact valid values. The following script demonstrates usage: echo Red [00 - ff] echo 33 > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_red echo Green [00 - ff] echo ff > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_green echo Blue [00 - ff] echo 0 > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_blue echo Mode: 0 - static color, 1 - blink, 2 - rainbow, 3 - strobe echo 0 > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_mode echo Speed for modes 1 and 2: 0 - slow, 1 - medium, 2 - fast echo 0 > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_speed echo Enable: 02 - on boot, before module load, 08 - awake, 20 - sleep, echo 2a or ff to set all echo 2a > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_flags echo Save: 1 - permanently, 2 - temporarily, reset after reboot echo 1 > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_set Signed-off-by: Yurii Pavlovskyi --- .../ABI/testing/sysfs-platform-asus-wmi | 61 drivers/platform/x86/asus-wmi.c | 329 ++ include/linux/platform_data/x86/asus-wmi.h| 2 + 3 files changed, 392 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-platform-asus-wmi b/Documentation/ABI/testing/sysfs-platform-asus-wmi index 019e1e29370e..300a40519695 100644 --- a/Documentation/ABI/testing/sysfs-platform-asus-wmi +++ b/Documentation/ABI/testing/sysfs-platform-asus-wmi @@ -36,3 +36,64 @@ KernelVersion: 3.5 Contact: "AceLan Kao" Description: Resume on lid open. 1 means on, 0 means off. + +What: /sys/devices/platform//kbbl/kbbl_red +Date: Apr 2019 +KernelVersion: 5.1 +Contact: "Yurii Pavlovskyi" +Description: + RGB keyboard backlight red component: 00 .. ff. + +What: /sys/devices/platform//kbbl/kbbl_green +Date: Apr 2019 +KernelVersion: 5.1 +Contact: "Yurii Pavlovskyi" +Description: + RGB keyboard backlight green component: 00 .. ff. + +What: /sys/devices/platform//kbbl/kbbl_blue +Date: Apr 2019 +KernelVersion: 5.1 +Contact: "Yurii Pavlovskyi" +Description: + RGB keyboard backlight blue component: 00 .. ff. + +What: /sys/devices/platform//kbbl/kbbl_mode +Date: Apr 2019 +KernelVersion: 5.1 +Contact: "Yurii Pavlovskyi" +Description: + RGB keyboard backlight mode: + * 0 - static color, + * 1 - blink, + * 2 - rainbow, + * 3 - strobe. + +What: /sys/devices/platform//kbbl/kbbl_speed +Date: Apr 2019 +KernelVersion: 5.1 +Contact: "Yurii Pavlovskyi" +Description: + RGB keyboard backlight speed for modes 1 and 2: + * 0 - slow, + * 1 - medium, + * 2 - fast. + +What: /sys/devices/platform//kbbl/kbbl_flags +Date: Apr 2019 +KernelVersion: 5.1 +Contact: "Yurii Pavlovskyi" +Description: + RGB keyboard backlight enable flags (2a to enable everything), OR of: + * 02 - on boot (until module load), + * 08 - awake, + * 20 - sleep. + +What: /sys/devices/platform//kbbl/kbbl_set +Date: Apr 2019 +KernelVersion: 5.1 +Contact: "Yurii Pavlovskyi" +Description: + Write changed RGB keyboard backlight parameters: + * 1 - permanently, + * 2 - temporarily. diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 175ecd5b7c51..f4323a57f22f 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -145,6 +145,21 @@ struct asus_rfkill { u32 dev_id; }; +struct asus_kbbl_rgb { + u8 kbbl_red; + u8 kbbl_green; + u8 kbbl_blue; + u8 kbbl_mode; + u8 kbbl_speed; + + u8 kbbl_set_red; + u8 kbbl_set_green; + u8 kbbl_set_blue; + u8 kbbl_set_m
[PATCH 08/11] platform/x86: asus-wmi: Enhance detection of thermal data
The obviously wrong value 1 for temperature device ID in this driver is returned by at least some devices, including TUF Gaming series laptops, instead of 0 as expected previously. Observable effect is that a temp1_input in hwmon reads temperature near absolute zero. * Consider 0.1 K as erroneous value in addition to 0 K. * Refactor detection of thermal input availability to a separate function. Signed-off-by: Yurii Pavlovskyi --- drivers/platform/x86/asus-wmi.c | 46 - 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index b9a6dc224e08..175ecd5b7c51 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -176,6 +176,7 @@ struct asus_wmi { struct asus_rfkill gps; struct asus_rfkill uwb; + bool asus_hwmon_thermal_available; bool asus_hwmon_fan_manual_mode; int asus_hwmon_num_fans; int asus_hwmon_pwm; @@ -1373,6 +1374,32 @@ static struct attribute *hwmon_attributes[] = { NULL }; +static int asus_hwmon_check_thermal_available(struct asus_wmi *asus) +{ + u32 value = ASUS_WMI_UNSUPPORTED_METHOD; + int err; + + asus->asus_hwmon_thermal_available = false; + err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_THERMAL_CTRL, ); + + if (err < 0) { + if (err == -ENODEV) + return 0; + + return err; + } + + /* +* If the temperature value in deci-Kelvin is near the absolute +* zero temperature, something is clearly wrong. +*/ + if (!value || value == 1) + return 0; + + asus->asus_hwmon_thermal_available = true; + return 0; +} + static umode_t asus_hwmon_sysfs_is_visible(struct kobject *kobj, struct attribute *attr, int idx) { @@ -1386,8 +1413,6 @@ static umode_t asus_hwmon_sysfs_is_visible(struct kobject *kobj, if (attr == _attr_pwm1.attr) dev_id = ASUS_WMI_DEVID_FAN_CTRL; - else if (attr == _attr_temp1_input.attr) - dev_id = ASUS_WMI_DEVID_THERMAL_CTRL; if (attr == _attr_fan1_input.attr || attr == _attr_fan1_label.attr @@ -1412,15 +1437,13 @@ static umode_t asus_hwmon_sysfs_is_visible(struct kobject *kobj, * - reverved bits are non-zero * - sfun and presence bit are not set */ - if (value == ASUS_WMI_UNSUPPORTED_METHOD || value & 0xFFF8 + if (value == ASUS_WMI_UNSUPPORTED_METHOD || (value & 0xFFF8) || (!asus->sfun && !(value & ASUS_WMI_DSTS_PRESENCE_BIT))) ok = false; else ok = fan_attr <= asus->asus_hwmon_num_fans; - } else if (dev_id == ASUS_WMI_DEVID_THERMAL_CTRL) { - /* If value is zero, something is clearly wrong */ - if (!value) - ok = false; + } else if (attr == _attr_temp1_input.attr) { + ok = asus->asus_hwmon_thermal_available; } else if (fan_attr <= asus->asus_hwmon_num_fans && fan_attr != -1) { ok = true; } else { @@ -1476,6 +1499,15 @@ static int asus_wmi_fan_init(struct asus_wmi *asus) } pr_info("Number of fans: %d\n", asus->asus_hwmon_num_fans); + + status = asus_hwmon_check_thermal_available(asus); + if (status) { + pr_warn("Could not check if thermal available: %d\n", status); + return -ENXIO; + } + + pr_info(PR "Thermal available: %d\n", + asus->asus_hwmon_thermal_available); return 0; } -- 2.17.1
[PATCH 07/11] platform/x86: asus-wmi: Organize code into sections
The driver has grown (and will more) pretty big which makes it hard to navigate and understand. Add uniform comments to the code and ensure that it is sorted into logical sections. Signed-off-by: Yurii Pavlovskyi --- drivers/platform/x86/asus-wmi.c | 94 - 1 file changed, 46 insertions(+), 48 deletions(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index e0a710c64dea..b9a6dc224e08 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -191,6 +191,8 @@ struct asus_wmi { struct asus_wmi_driver *driver; }; +/* Input **/ + static int asus_wmi_input_init(struct asus_wmi *asus) { int err; @@ -228,6 +230,8 @@ static void asus_wmi_input_exit(struct asus_wmi *asus) asus->inputdev = NULL; } +/* WMI / + static int asus_wmi_evaluate_method_3dw(u32 method_id, u32 arg0, u32 arg1, u32 arg2, u32 *retval) { @@ -246,7 +250,7 @@ static int asus_wmi_evaluate_method_3dw(u32 method_id, u32 arg0, u32 arg1, , ); if (ACPI_FAILURE(status)) - goto exit; + return -EIO; obj = (union acpi_object *)output.pointer; if (obj && obj->type == ACPI_TYPE_INTEGER) @@ -257,10 +261,6 @@ static int asus_wmi_evaluate_method_3dw(u32 method_id, u32 arg0, u32 arg1, kfree(obj); -exit: - if (ACPI_FAILURE(status)) - return -EIO; - if (tmp == ASUS_WMI_UNSUPPORTED_METHOD) return -ENODEV; @@ -344,9 +344,8 @@ static int asus_wmi_get_devstate_simple(struct asus_wmi *asus, u32 dev_id) ASUS_WMI_DSTS_STATUS_BIT); } -/* - * LEDs - */ +/* LEDs ***/ + /* * These functions actually update the LED's, and are called from a * workqueue. By doing this as separate work rather than when the LED @@ -656,6 +655,7 @@ static int asus_wmi_led_init(struct asus_wmi *asus) return rv; } +/* RF */ /* * PCI hotplug (for wlan rfkill) @@ -1078,6 +1078,8 @@ static int asus_wmi_rfkill_init(struct asus_wmi *asus) return result; } +/* Quirks */ + static void asus_wmi_set_xusb2pr(struct asus_wmi *asus) { struct pci_dev *xhci_pdev; @@ -1110,9 +1112,8 @@ static void asus_wmi_set_als(void) asus_wmi_set_devstate(ASUS_WMI_DEVID_ALS_ENABLE, 1, NULL); } -/* - * Hwmon device - */ +/* Hwmon device ***/ + static int asus_hwmon_agfn_fan_speed_read(struct asus_wmi *asus, int fan, int *speed) { @@ -1388,7 +1389,6 @@ static umode_t asus_hwmon_sysfs_is_visible(struct kobject *kobj, else if (attr == _attr_temp1_input.attr) dev_id = ASUS_WMI_DEVID_THERMAL_CTRL; - if (attr == _attr_fan1_input.attr || attr == _attr_fan1_label.attr || attr == _attr_pwm1.attr @@ -1460,9 +1460,27 @@ static void asus_wmi_hwmon_exit(struct asus_wmi *asus) } } -/* - * Backlight - */ +static int asus_wmi_fan_init(struct asus_wmi *asus) +{ + int status; + + asus->asus_hwmon_pwm = -1; + asus->asus_hwmon_num_fans = -1; + asus->asus_hwmon_fan_manual_mode = false; + + status = asus_hwmon_get_fan_number(asus, >asus_hwmon_num_fans); + if (status) { + asus->asus_hwmon_num_fans = 0; + pr_warn("Could not determine number of fans: %d\n", status); + return -ENXIO; + } + + pr_info("Number of fans: %d\n", asus->asus_hwmon_num_fans); + return 0; +} + +/* Backlight **/ + static int read_backlight_power(struct asus_wmi *asus) { int ret; @@ -1644,6 +1662,8 @@ static int is_display_toggle(int code) return 0; } +/* WMI events */ + static int asus_poll_wmi_event(u32 value) { struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; @@ -1766,9 +1786,8 @@ static int asus_wmi_notify_queue_flush(struct asus_wmi *asus) return -EIO; } -/* - * Sys helpers - */ +/* Sysfs **/ + static int parse_arg(const char *buf, unsigned long count, int *val) { if (!count) @@ -1907,9 +1926,8 @@ static int asus_wmi_sysfs_init(struct platform_device *device) return sysfs_create_group(>dev.kobj, _attribute_group)
[PATCH 06/11] platform/x86: asus-nb-wmi: Add microphone mute key code
The microphone mute key that is present on FX505GM laptop and possibly others is missing from sparse keymap. Add the missing code. Also comment on the fan mode switch key that has the same code as the already used key. Signed-off-by: Yurii Pavlovskyi --- drivers/platform/x86/asus-nb-wmi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c index 357d273ed336..39cf447198a9 100644 --- a/drivers/platform/x86/asus-nb-wmi.c +++ b/drivers/platform/x86/asus-nb-wmi.c @@ -474,6 +474,7 @@ static const struct key_entry asus_nb_wmi_keymap[] = { { KE_KEY, 0x6B, { KEY_TOUCHPAD_TOGGLE } }, { KE_IGNORE, 0x6E, }, /* Low Battery notification */ { KE_KEY, 0x7a, { KEY_ALS_TOGGLE } }, /* Ambient Light Sensor Toggle */ + { KE_KEY, 0x7c, { KEY_MICMUTE } }, { KE_KEY, 0x7D, { KEY_BLUETOOTH } }, /* Bluetooth Enable */ { KE_KEY, 0x7E, { KEY_BLUETOOTH } }, /* Bluetooth Disable */ { KE_KEY, 0x82, { KEY_CAMERA } }, @@ -488,7 +489,7 @@ static const struct key_entry asus_nb_wmi_keymap[] = { { KE_KEY, 0x92, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + TV + DVI */ { KE_KEY, 0x93, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + TV + DVI */ { KE_KEY, 0x95, { KEY_MEDIA } }, - { KE_KEY, 0x99, { KEY_PHONE } }, + { KE_KEY, 0x99, { KEY_PHONE } }, /* Conflicts with fan mode switch */ { KE_KEY, 0xA0, { KEY_SWITCHVIDEOMODE } }, /* SDSP HDMI only */ { KE_KEY, 0xA1, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + HDMI */ { KE_KEY, 0xA2, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + HDMI */ -- 2.17.1
[PATCH 05/11] platform/x86: asus-wmi: Support queued WMI event codes
Event codes are expected to be polled from a queue on at least some models. The WMI event codes are pushed into queue based on circular buffer. After INIT method is called ACPI code is allowed to push events into this buffer the INIT method can not be reverted. If the module is unloaded and an event (such as hotkey press) gets emitted before inserting it back the events get processed delayed by one or, if the queue overflows, additionally delayed by about 3 seconds. Patch was tested on a newer TUF Gaming FX505GM and older K54C model. FX505GM Device (ATKD) { .. Name (ATKQ, Package (0x10) { 0x, .. } Method (IANQ, 1, Serialized) { If ((AQNO >= 0x10)) { Local0 = 0x64 While ((Local0 && (AQNO >= 0x10))) { Local0-- Sleep (0x0A) } ... .. AQTI++ AQTI &= 0x0F ATKQ [AQTI] = Arg0 ... } Method (GANQ, 0, Serialized) { .. If (AQNO) { ... Local0 = DerefOf (ATKQ [AQHI]) AQHI++ AQHI &= 0x0F Return (Local0) } Return (One) } This code is almost identical to K54C, which does return Ones on empty queue. K54C: Method (GANQ, 0, Serialized) { If (AQNO) { ... Return (Local0) } Return (Ones) } The fix flushes the old key codes out of the queue on load and after receiving event the queue is read until either .. or 1 is encountered. It might be considered a minor issue and no normal user would likely to observe this (there is little reason unloading the driver), but it does significantly frustrate a developer who is unlucky enough to encounter this. Introduce functionality for flushing and processing queued codes, which is enabled via quirk flag for ASUS7000. It might be considered if it is reasonable to enable it everywhere (might introduce regressions) or always try to flush the queue on module load and try to detect if this quirk is present in the future. This patch limits the effect to the specific hardware defined by ASUS7000 device that is used for driver detection by vendor driver of Fx505. The fallback is also implemented in case initial flush fails. Signed-off-by: Yurii Pavlovskyi --- drivers/platform/x86/asus-nb-wmi.c | 1 + drivers/platform/x86/asus-wmi.c| 122 ++--- drivers/platform/x86/asus-wmi.h| 2 + 3 files changed, 97 insertions(+), 28 deletions(-) diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c index cc5f0765a8d9..357d273ed336 100644 --- a/drivers/platform/x86/asus-nb-wmi.c +++ b/drivers/platform/x86/asus-nb-wmi.c @@ -438,6 +438,7 @@ static void asus_nb_wmi_quirks(struct asus_wmi_driver *driver) if (acpi_dev_found("ASUS7000")) { driver->quirks->force_dsts = true; + driver->quirks->wmi_event_queue = true; } } diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 58890d87d50c..e0a710c64dea 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -80,6 +80,12 @@ MODULE_LICENSE("GPL"); #define USB_INTEL_XUSB2PR 0xD0 #define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31 +#define WMI_EVENT_QUEUE_SIZE 0x10 +#define WMI_EVENT_QUEUE_END0x1 +#define WMI_EVENT_MASK 0x +/* The event value is always the same. */ +#define WMI_EVENT_VALUE0xFF + static const char * const ashs_ids[] = { "ATK4001", "ATK4002", NULL }; static bool ashs_present(void) @@ -143,6 +149,7 @@ struct asus_wmi { int dsts_id; int spec; int sfun; + bool wmi_event_queue; struct input_dev *inputdev; struct backlight_device *backlight_device; @@ -1637,77 +1644,126 @@ static int is_display_toggle(int code) return 0; } -static void asus_wmi_notify(u32 value, void *context) +static int asus_poll_wmi_event(u32 value) { - struct asus_wmi *asus = context; - struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; union acpi_object *obj; acpi_status status; - int code; - int orig_code; - unsigned int key_value = 1; - bool autorelease = 1; + int code = -EIO; - status = wmi_get_event_data(value, ); - if (status != AE_OK) { - pr_err("bad event status 0x%x\n", status); - return; + status = wmi_get_event_data(value, ); + if (ACPI_FAILURE(status)) { + pr_warn(PR "Failed to get WMI event code: %s\n", + acpi_format_exception(status)); + return code; } -
[PATCH 04/11] platform/x86: asus-wmi: Add quirk to force DSTS WMI method detection
The DSTS method detection fails, as nothing is returned if method is not defined in WMNB. As a result the control of keyboard backlight is not functional for TUF Gaming series laptops (at the time the only functionality of the driver on this model implemented with WMI methods). Patch was tested on a newer TUF Gaming FX505GM and older K54C model. FX505GM: Method (WMNB, 3, Serialized) { ... If ((Local0 == 0x53545344)) { ... Return (Zero) } ... // No return } K54C: Method (WMNB, 3, Serialized) { ... If ((Local0 == 0x53545344)) { ... Return (0x02) } ... Return (0xFFFE) } The non-existing method ASUS_WMI_METHODID_DSTS=0x53544344 (actually it is DCTS in little endian ASCII) is selected in asus->dsts. One way to fix this would be to call both for every known device ID until some answers - this would increase module load time. Another option is to check some device that is known to exist on every model - none known at the time. Last option, which is implemented, is to check for presence of the ASUS7000 device in ACPI tree (it is a dummy device), which is the condition used for loading the vendor driver for this model. This might not fix every affected model ever produced, but it likely does not introduce any regressions. The patch introduces a quirk that is enabled when ASUS7000 is found. Scope (_SB) { Device (ATK) { Name (_HID, "ASUS7000") // _HID: Hardware ID } } Signed-off-by: Yurii Pavlovskyi --- drivers/platform/x86/asus-nb-wmi.c | 5 + drivers/platform/x86/asus-wmi.c| 16 +--- drivers/platform/x86/asus-wmi.h| 5 + 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c index b6f2ff95c3ed..cc5f0765a8d9 100644 --- a/drivers/platform/x86/asus-nb-wmi.c +++ b/drivers/platform/x86/asus-nb-wmi.c @@ -28,6 +28,7 @@ #include #include #include +#include #include "asus-wmi.h" @@ -434,6 +435,10 @@ static void asus_nb_wmi_quirks(struct asus_wmi_driver *driver) } pr_info("Using i8042 filter function for receiving events\n"); } + + if (acpi_dev_found("ASUS7000")) { + driver->quirks->force_dsts = true; + } } static const struct key_entry asus_nb_wmi_keymap[] = { diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index cfccfc0b8c2f..58890d87d50c 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -24,7 +24,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#define PR KBUILD_MODNAME ": " #include #include @@ -1885,11 +1885,21 @@ static int asus_wmi_platform_init(struct asus_wmi *asus) * Note, on most Eeepc, there is no way to check if a method exist * or note, while on notebooks, they returns 0xFFFE on failure, * but once again, SPEC may probably be used for that kind of things. +* +* Additionally at least TUF Gaming series laptops return 0 for unknown +* methods, so the detection in this way is not possible and method must +* be forced. Likely the presence of ACPI device ASUS7000 indicates +* this. */ - if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS, 0, 0, NULL)) + if (asus->driver->quirks->force_dsts) { + pr_info(PR "DSTS method forced\n"); + asus->dsts_id = ASUS_WMI_METHODID_DSTS2; + } else if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS, + 0, 0, NULL)) { asus->dsts_id = ASUS_WMI_METHODID_DSTS; - else + } else { asus->dsts_id = ASUS_WMI_METHODID_DSTS2; + } /* CWAP allow to define the behavior of the Fn+F2 key, * this method doesn't seems to be present on Eee PCs */ diff --git a/drivers/platform/x86/asus-wmi.h b/drivers/platform/x86/asus-wmi.h index 6c1311f4b04d..94056da02fde 100644 --- a/drivers/platform/x86/asus-wmi.h +++ b/drivers/platform/x86/asus-wmi.h @@ -54,6 +54,11 @@ struct quirk_entry { */ int no_display_toggle; u32 xusb2pr; + /** +* Force DSTS instead of DSCS and skip detection. Useful if WMNB +* returns nothing on unknown method call. +*/ + bool force_dsts; bool (*i8042_filter)(unsigned char data, unsigned char str, struct serio *serio); -- 2.17.1
[PATCH 03/11] platform/x86: asus-wmi: Increase input buffer size of WMI methods
The asus-nb-wmi driver is matched by WMI alias but fails to load on TUF Gaming series laptops producing multiple ACPI errors in kernel log. Patch was tested on TUF Gaming FX505GM and older K54C model. The input buffer for WMI method invocation size is 2 dwords, whereas 3 are expected by this model. FX505GM: .. Method (WMNB, 3, Serialized) { P8XH (Zero, 0x11) CreateDWordField (Arg2, Zero, IIA0) CreateDWordField (Arg2, 0x04, IIA1) CreateDWordField (Arg2, 0x08, IIA2) Local0 = (Arg1 & 0x) ... Compare with older K54C: ... Method (WMNB, 3, NotSerialized) { CreateDWordField (Arg2, 0x00, IIA0) CreateDWordField (Arg2, 0x04, IIA1) Local0 = (Arg1 & 0x) ... Increase buffer size to 3 dwords. No negative consequences of this change are expected, as input buffer size is not verified. The original function is replaced by a wrapper for a new method passing value 0 for the last parameter. The new function will be used to control RGB keyboard backlight. Signed-off-by: Yurii Pavlovskyi --- One of current kernel errors: ACPI BIOS Error (bug): AE_AML_BUFFER_LIMIT, Field [IIA2] at bit offset/ length 64/32 exceeds size of target Buffer (64 bits) (20190215/dsopcode-203) [ 4528.573948] No Local Variables are initialized for Method [WMNB] [ 4528.573949] Initialized Arguments for Method [WMNB]: (3 arguments defined for method invocation) [ 4528.573950] Arg0: bd1bea5a Integer [ 4528.573952] Arg1: d414dc53 Integer 4E464741 [ 4528.573954] Arg2: fcefea4b Buffer(8) F0 95 08 00 00 00 00 00 [ 4528.573959] ACPI Error: Aborting method \_SB.ATKD.WMNB due to previous error (AE_AML_BUFFER_LIMIT) (20190215/psparse-531) [ 4528.686425] asus-nb-wmi: probe of asus-nb-wmi failed with error -5 --- drivers/platform/x86/asus-wmi.c | 10 +- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 0fbb947b07c4..cfccfc0b8c2f 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -95,6 +95,7 @@ static bool ashs_present(void) struct bios_args { u32 arg0; u32 arg1; + u32 arg2; /* At least TUF Gaming series uses 3 dword input buffer. */ } __packed; /* @@ -220,11 +221,13 @@ static void asus_wmi_input_exit(struct asus_wmi *asus) asus->inputdev = NULL; } -int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval) +static int asus_wmi_evaluate_method_3dw(u32 method_id, u32 arg0, u32 arg1, + u32 arg2, u32 *retval) { struct bios_args args = { .arg0 = arg0, .arg1 = arg1, + .arg2 = arg2 }; struct acpi_buffer input = { (acpi_size) sizeof(args), }; struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; @@ -256,6 +259,11 @@ int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval) return 0; } + +int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval) +{ + return asus_wmi_evaluate_method_3dw(method_id, arg0, arg1, 0, retval); +} EXPORT_SYMBOL_GPL(asus_wmi_evaluate_method); static int asus_wmi_evaluate_method_agfn(const struct acpi_buffer args) -- 2.17.1
[PATCH 02/11] platform/x86: asus-wmi: Fix preserving keyboard, backlight intensity on load
The error code and return value are mixed up. The intensity is always set to 0 on load as kbd_led_read returns either 0 or negative value. To reproduce set backlight to maximum, reload driver and try to increase it using keyboard hotkey, the intensity will drop as a result. Correct the implementation. Signed-off-by: Yurii Pavlovskyi --- drivers/platform/x86/asus-wmi.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 6b736a9375ef..0fbb947b07c4 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -591,8 +591,7 @@ static int asus_wmi_led_init(struct asus_wmi *asus) goto error; } - led_val = kbd_led_read(asus, NULL, NULL); - if (led_val >= 0) { + if (!kbd_led_read(asus, _val, NULL)) { asus->kbd_led_wk = led_val; asus->kbd_led.name = "asus::kbd_backlight"; asus->kbd_led.flags = LED_BRIGHT_HW_CHANGED; -- 2.17.1
[PATCH 01/11] platform/x86: asus-wmi: Fix hwmon device cleanup
The asus-wmi driver does not clean up the hwmon device on exit or error. To reproduce the bug, repeat rmmod, insmod to verify that device number /sys/devices/platform/asus-nb-wmi/hwmon/hwmon?? grows every time. Add pointer to the device in module state and call cleanup on error. Signed-off-by: Yurii Pavlovskyi --- drivers/platform/x86/asus-wmi.c | 14 +- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index ee1fa93708ec..6b736a9375ef 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -145,6 +145,7 @@ struct asus_wmi { struct input_dev *inputdev; struct backlight_device *backlight_device; + struct device *hwmon_device; struct platform_device *platform_device; struct led_classdev wlan_led; @@ -1432,9 +1433,19 @@ static int asus_wmi_hwmon_init(struct asus_wmi *asus) pr_err("Could not register asus hwmon device\n"); return PTR_ERR(hwmon); } + + asus->hwmon_device = hwmon; return 0; } +static void asus_wmi_hwmon_exit(struct asus_wmi *asus) +{ + if (asus->hwmon_device) { + asus_hwmon_fan_set_auto(asus); + hwmon_device_unregister(asus->hwmon_device); + } +} + /* * Backlight */ @@ -2157,6 +2168,7 @@ static int asus_wmi_add(struct platform_device *pdev) fail_rfkill: asus_wmi_led_exit(asus); fail_leds: + asus_wmi_hwmon_exit(asus); fail_hwmon: asus_wmi_input_exit(asus); fail_input: @@ -2178,7 +2190,7 @@ static int asus_wmi_remove(struct platform_device *device) asus_wmi_rfkill_exit(asus); asus_wmi_debugfs_exit(asus); asus_wmi_platform_exit(asus); - asus_hwmon_fan_set_auto(asus); + asus_wmi_hwmon_exit(asus); kfree(asus); return 0; -- 2.17.1
[PATCH 00/11] asus-wmi: Support of ASUS TUF Gaming series laptops
Hi, I'm new to kernel development, so first I would like to apologize in advance for any mistakes. The support for this laptop series is currently non-existent, as the asus-nb-wmi driver (which is essentially configuration for asus-wmi) fails to load and multiple ACPI errors are logged in dmesg. This patch series adds pretty comprehenisve support for these relatively new laptops, adds some code organization, and fixes couple of bugs in the asus-wmi module. I have FX565GM (FX505GM) model, but I would guess that the same will likely apply to the complete FX505 / FX705 series. I've also tested this on an older K54C model to ensure that it doesn't break support. Unfortunately I don't have capacity to test this on more devices. The patches 1 and 2 are pure bug fixes, but I can not measure relevance for stable. OVERALL DESIGN DECISIONS Besides this patch, I've written experimental separate driver [1] for this series to make it usable on my system as a DKMS module for 4.18 kernel for the time being. One might wonder if it is more reasonable to make a new independent module. The things to consider are that: asus-nb-wmi is currently loaded by the WMI GUID alias, whereas the original ASUS driver does check for the ASUS7000 device in ACPI. One should then choose appropriate base driver instead of asus-wmi when asus-nb-wmi is loaded, about third of the code gets duplicated in this case and the whole ends up ugly. Another question, does it make sense to embed RGB keyboard backlight support in kernel code? There was discussion [2] about exposing WMI to the userspace. The same would apply for the fan boost mode support. As I understand as of yet it is still preferrable to support hardware features in kernel. NOTE ON HWMON One open issue with the result is that hwmon device gets loaded anyway, but it does not do anything noticeable. The heavily reduced code for the MFUN is present in DSDT, but it either really does nothing or possibly call something unnoticeable via DMA. I've managed to detect that thermal sensor is not present, but the MFUN for read fan speed does return 0 and not an error. Taking this as condition for disabling hwmon might intermittently break some existing devices if the RPM is really 0 (no idea if that is really possible). One might ponder on the better way to detect the presence of manual fan control. NOTE ON QUIRKS I would speculate that the queue might be really present in many more devices, it just didn't get noticed. Anyway after this is merged one might consider if it is reasonable to enable it always and fallback if flush fails. The patch does enable the new quirks only for very new models. Regarding the DSTS force quirk, as I understand the underlying issue is a workaround for EEEPC devices and use of DSTS is more conventional. It might be reasonable to find a way to detect specific DSTS device ID that is present on EEEPC instead, apply same ACPI device detection approach or just duplicate the relevant method calls. I don't have access to such device or it's DSDT and can't evaluate any of these options myself. NOTE ON KEYBOARD BACKLIGHT When the keyboard backlight is set via 0x50021 DEVID the brightness drops slightly compared to brightness after boot. I did not found any way to revert this. The method does set some bit in EC RAM, but this address is not used anywhere else. Unfortunately I wiped original OS after two hours after unpacking.If someone can verify whether it is identical to behavior of the vendor driver it would be appreciated. NOTE ON UPOWER DAEMON If you're testing with GNOME, notice that UPower does hang pretty badly if the module is removed at runtime at least on my device. Stop it with 'systemctl stop upower' before removing the module and then restart it again. [1] https://github.com/hackbnw/faustus [2] https://lwn.net/Articles/725725/ Yurii Pavlovskyi (11): platform/x86: asus-wmi: Fix hwmon device cleanup platform/x86: asus-wmi: Fix preserving keyboard backlight intensity on load platform/x86: asus-wmi: Increase input buffer size of WMI methods platform/x86: asus-wmi: Add quirk to force DSTS WMI method detection platform/x86: asus-wmi: Support queued WMI event codes platform/x86: asus-nb-wmi: Add microphone mute key code platform/x86: asus-wmi: Organize code into sections platform/x86: asus-wmi: Enhance detection of thermal data platform/x86: asus-wmi: Control RGB keyboard backlight platform/x86: asus-wmi: Switch fan boost mode platform/x86: asus-wmi: Do not disable keyboard backlight on unload .../ABI/testing/sysfs-platform-asus-wmi | 71 ++ drivers/platform/x86/asus-nb-wmi.c| 9 +- drivers/platform/x86/asus-wmi.c | 755 +++--- drivers/platform/x86/asus-wmi.h | 7 + include/linux/platform_data/x86/asus-wmi.h| 3 + 5 files changed, 742 insertions(+), 103 deletions(-) -- 2.17.1