From: Aleksey Nemcev <Nemcev_Aleksey@xxxxxxxx> It is necessary for some Asus notebook models like FX705GE with the AURA RGB keyboard backlight On these models, 3 arguments must be passed to the WMI, or call fails: [ 4524.641086] ACPI Error: Field [IIA2] at bit offset/length 64/32 exceeds size of target Buffer (64 bits) (20180531/dsopcode-201) [ 4524.641105] No Local Variables are initialized for Method [WMNB] [ 4524.641108] Initialized Arguments for Method [WMNB]: (3 arguments defined for method invocation) [ 4524.641109] Arg0: 00000000ad8fc150 <Obj> Integer 0000000000000000 [ 4524.641119] Arg1: 0000000097c8ea2f <Obj> Integer 0000000054494E49 [ 4524.641127] Arg2: 00000000f3aa4ecd <Obj> Buffer(8) 00 00 00 00 00 00 00 00 Signed-off-by: Aleksey Nemcev <Nemcev_Aleksey@xxxxxxxx> --- drivers/platform/x86/asus-nb-wmi.c | 4 + drivers/platform/x86/asus-wmi.c | 216 ++++++++++++++++----- drivers/platform/x86/asus-wmi.h | 1 + include/linux/platform_data/x86/asus-wmi.h | 8 +- 4 files changed, 174 insertions(+), 55 deletions(-) diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c index 8d9e30dbb5af..4bed95357a32 100644 --- a/drivers/platform/x86/asus-nb-wmi.c +++ b/drivers/platform/x86/asus-nb-wmi.c @@ -110,6 +110,10 @@ static struct quirk_entry quirk_asus_forceals = { .wmi_force_als_set = true, }; +static struct quirk_entry quirk_asus_wmi_needs_3_args = { + .wmi_needs_3_args = true, +}; + static int dmi_matched(const struct dmi_system_id *dmi) { pr_info("Identified laptop model '%s'\n", dmi->ident); diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 9b18a184e0aa..56a407fa8944 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -87,6 +87,12 @@ struct bios_args { u32 arg1; } __packed; +struct bios_3args { + u32 arg0; + u32 arg1; + u32 arg2; +} __packed; + /* * Struct that's used for all methods called via AGFN. Naming is * identically to the AML code. @@ -110,6 +116,7 @@ struct fan_args { * <platform>/ - debugfs root directory * dev_id - current dev_id * ctrl_param - current ctrl_param + * ctrl_param2 - current ctrl_param2 (only with quirk_asus_wmi_needs_3_args) * method_id - current method_id * devs - call DEVS(dev_id, ctrl_param) and print result * dsts - call DSTS(dev_id) and print result @@ -120,6 +127,7 @@ struct asus_wmi_debug { u32 method_id; u32 dev_id; u32 ctrl_param; + u32 ctrl_param2; }; struct asus_rfkill { @@ -211,13 +219,10 @@ 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) +int asus_wmi_evaluate_method_common(u32 method_id, void *args, + size_t args_size, u32 *retval) { - struct bios_args args = { - .arg0 = arg0, - .arg1 = arg1, - }; - struct acpi_buffer input = { (acpi_size) sizeof(args), &args }; + struct acpi_buffer input = { (acpi_size) args_size, args }; struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; acpi_status status; union acpi_object *obj; @@ -247,9 +252,46 @@ int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval) return 0; } + +int asus_wmi_evaluate_method(struct asus_wmi *asus, u32 method_id, u32 arg0, + u32 arg1, u32 *retval) +{ + void *args_pointer = NULL; + size_t args_size = 0; + struct bios_args args; + struct bios_3args args3; + + if (asus->driver->quirks->wmi_needs_3_args) { + args3.arg0 = arg0; + args3.arg1 = arg1; + args3.arg2 = 0; + args_pointer = &args3; + args_size = sizeof(args3); + } else { + args.arg0 = arg0; + args.arg1 = arg1; + args_pointer = &args; + args_size = sizeof(args); + } + return asus_wmi_evaluate_method_common(method_id, args_pointer, + args_size, retval); +} EXPORT_SYMBOL_GPL(asus_wmi_evaluate_method); -static int asus_wmi_evaluate_method_agfn(const struct acpi_buffer args) +static int asus_wmi_evaluate_method_3args(u32 method_id, u32 arg0, u32 arg1, + u32 arg2, u32 *retval) +{ + struct bios_3args args = { + .arg0 = arg0, + .arg1 = arg1, + .arg2 = arg2, + }; + return asus_wmi_evaluate_method_common(method_id, &args, sizeof(args), + retval); +} + +static int asus_wmi_evaluate_method_agfn(struct asus_wmi *asus, + const struct acpi_buffer args) { struct acpi_buffer input; u64 phys_addr; @@ -267,7 +309,7 @@ static int asus_wmi_evaluate_method_agfn(const struct acpi_buffer args) phys_addr = virt_to_phys(input.pointer); memcpy(input.pointer, args.pointer, args.length); - status = asus_wmi_evaluate_method(ASUS_WMI_METHODID_AGFN, + status = asus_wmi_evaluate_method(asus, ASUS_WMI_METHODID_AGFN, phys_addr, 0, &retval); if (!status) memcpy(args.pointer, input.pointer, args.length); @@ -279,18 +321,27 @@ static int asus_wmi_evaluate_method_agfn(const struct acpi_buffer args) return retval; } -static int asus_wmi_get_devstate(struct asus_wmi *asus, u32 dev_id, u32 *retval) +static int asus_wmi_get_devstate(struct asus_wmi *asus, u32 dev_id, + u32 *retval) { - return asus_wmi_evaluate_method(asus->dsts_id, dev_id, 0, retval); + return asus_wmi_evaluate_method(asus, asus->dsts_id, dev_id, 0, + retval); } -static int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param, - u32 *retval) +static int asus_wmi_set_devstate(struct asus_wmi *asus, u32 dev_id, + u32 ctrl_param, u32 *retval) { - return asus_wmi_evaluate_method(ASUS_WMI_METHODID_DEVS, dev_id, + return asus_wmi_evaluate_method(asus, ASUS_WMI_METHODID_DEVS, dev_id, ctrl_param, retval); } +static int asus_wmi_set_devstate_3args(u32 dev_id, u32 ctrl_param, + u32 ctrl_param2, u32 *retval) +{ + return asus_wmi_evaluate_method_3args(ASUS_WMI_METHODID_DEVS, dev_id, + ctrl_param, ctrl_param2, retval); +} + /* Helper for special devices with magic return codes */ static int asus_wmi_get_devstate_bits(struct asus_wmi *asus, u32 dev_id, u32 mask) @@ -337,7 +388,8 @@ static void tpd_led_update(struct work_struct *work) asus = container_of(work, struct asus_wmi, tpd_led_work); ctrl_param = asus->tpd_led_wk; - asus_wmi_set_devstate(ASUS_WMI_DEVID_TOUCHPAD_LED, ctrl_param, NULL); + asus_wmi_set_devstate(asus, ASUS_WMI_DEVID_TOUCHPAD_LED, ctrl_param, + NULL); } static void tpd_led_set(struct led_classdev *led_cdev, @@ -376,7 +428,8 @@ static void kbd_led_update(struct asus_wmi *asus) if (asus->kbd_led_wk > 0) ctrl_param = 0x80 | (asus->kbd_led_wk & 0x7F); - asus_wmi_set_devstate(ASUS_WMI_DEVID_KBD_BACKLIGHT, ctrl_param, NULL); + asus_wmi_set_devstate(asus, ASUS_WMI_DEVID_KBD_BACKLIGHT, ctrl_param, + NULL); } static int kbd_led_read(struct asus_wmi *asus, int *level, int *env) @@ -479,7 +532,8 @@ static void wlan_led_update(struct work_struct *work) asus = container_of(work, struct asus_wmi, wlan_led_work); ctrl_param = asus->wlan_led_wk; - asus_wmi_set_devstate(ASUS_WMI_DEVID_WIRELESS_LED, ctrl_param, NULL); + asus_wmi_set_devstate(asus, ASUS_WMI_DEVID_WIRELESS_LED, ctrl_param, + NULL); } static void wlan_led_set(struct led_classdev *led_cdev, @@ -512,7 +566,7 @@ static void lightbar_led_update(struct work_struct *work) asus = container_of(work, struct asus_wmi, lightbar_led_work); ctrl_param = asus->lightbar_led_wk; - asus_wmi_set_devstate(ASUS_WMI_DEVID_LIGHTBAR, ctrl_param, NULL); + asus_wmi_set_devstate(asus, ASUS_WMI_DEVID_LIGHTBAR, ctrl_param, NULL); } static void lightbar_led_set(struct led_classdev *led_cdev, @@ -848,7 +902,7 @@ static int asus_rfkill_set(void *data, bool blocked) priv->asus->driver->wlan_ctrl_by_user) dev_id = ASUS_WMI_DEVID_WLAN_LED; - return asus_wmi_set_devstate(dev_id, ctrl_param, NULL); + return asus_wmi_set_devstate(priv->asus, dev_id, ctrl_param, NULL); } static void asus_rfkill_query(struct rfkill *rfkill, void *data) @@ -1082,9 +1136,9 @@ static void asus_wmi_set_xusb2pr(struct asus_wmi *asus) * Some devices dont support or have borcken get_als method * but still support set method. */ -static void asus_wmi_set_als(void) +static void asus_wmi_set_als(struct asus_wmi *asus) { - asus_wmi_set_devstate(ASUS_WMI_DEVID_ALS_ENABLE, 1, NULL); + asus_wmi_set_devstate(asus, ASUS_WMI_DEVID_ALS_ENABLE, 1, NULL); } /* @@ -1106,7 +1160,7 @@ static int asus_hwmon_agfn_fan_speed_read(struct asus_wmi *asus, int fan, if (fan != 1) return -EINVAL; - status = asus_wmi_evaluate_method_agfn(input); + status = asus_wmi_evaluate_method_agfn(asus, input); if (status || args.agfn.err) return -ENXIO; @@ -1134,7 +1188,7 @@ static int asus_hwmon_agfn_fan_speed_write(struct asus_wmi *asus, int fan, if (fan != 1 && fan != 0) return -EINVAL; - status = asus_wmi_evaluate_method_agfn(input); + status = asus_wmi_evaluate_method_agfn(asus, input); if (status || args.agfn.err) return -ENXIO; @@ -1504,7 +1558,7 @@ static int update_bl_status(struct backlight_device *bd) power = read_backlight_power(asus); if (power != -ENODEV && bd->props.power != power) { ctrl_param = !!(bd->props.power == FB_BLANK_UNBLANK); - err = asus_wmi_set_devstate(ASUS_WMI_DEVID_BACKLIGHT, + err = asus_wmi_set_devstate(asus, ASUS_WMI_DEVID_BACKLIGHT, ctrl_param, NULL); if (asus->driver->quirks->store_backlight_power) asus->driver->panel_power = bd->props.power; @@ -1520,7 +1574,7 @@ static int update_bl_status(struct backlight_device *bd) else ctrl_param = bd->props.brightness; - err = asus_wmi_set_devstate(ASUS_WMI_DEVID_BRIGHTNESS, + err = asus_wmi_set_devstate(asus, ASUS_WMI_DEVID_BRIGHTNESS, ctrl_param, NULL); return err; @@ -1625,7 +1679,7 @@ static void asus_wmi_fnlock_update(struct asus_wmi *asus) { int mode = asus->fnlock_locked; - asus_wmi_set_devstate(ASUS_WMI_DEVID_FNLOCK, mode, NULL); + asus_wmi_set_devstate(asus, ASUS_WMI_DEVID_FNLOCK, mode, NULL); } static void asus_wmi_notify(u32 value, void *context) @@ -1730,7 +1784,7 @@ static ssize_t store_sys_wmi(struct asus_wmi *asus, int devid, return value; rv = parse_arg(buf, count, &value); - err = asus_wmi_set_devstate(devid, value, &retval); + err = asus_wmi_set_devstate(asus, devid, value, &retval); if (err < 0) return err; @@ -1783,13 +1837,15 @@ static ssize_t cpufv_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int value, rv; + struct asus_wmi *asus = dev_get_drvdata(dev); if (!count || sscanf(buf, "%i", &value) != 1) return -EINVAL; if (value < 0 || value > 2) return -EINVAL; - rv = asus_wmi_evaluate_method(ASUS_WMI_METHODID_CFVS, value, 0, NULL); + rv = asus_wmi_evaluate_method(asus, ASUS_WMI_METHODID_CFVS, value, 0, + NULL); if (rv < 0) return rv; @@ -1856,11 +1912,12 @@ static int asus_wmi_platform_init(struct asus_wmi *asus) int rv; /* INIT enable hotkeys on some models */ - if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_INIT, 0, 0, &rv)) + if (!asus_wmi_evaluate_method(asus, ASUS_WMI_METHODID_INIT, 0, 0, &rv)) pr_info("Initialization: %#x\n", rv); /* We don't know yet what to do with this version... */ - if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_SPEC, 0, 0x9, &rv)) { + if (!asus_wmi_evaluate_method(asus, + ASUS_WMI_METHODID_SPEC, 0, 0x9, &rv)) { pr_info("BIOS WMI version: %d.%d\n", rv >> 16, rv & 0xFF); asus->spec = rv; } @@ -1871,7 +1928,8 @@ static int asus_wmi_platform_init(struct asus_wmi *asus) * bit signifies that the laptop is equipped with a Wi-Fi MiniPCI card. * The significance of others is yet to be found. */ - if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_SFUN, 0, 0, &rv)) { + if (!asus_wmi_evaluate_method(asus, + ASUS_WMI_METHODID_SFUN, 0, 0, &rv)) { pr_info("SFUN value: %#x\n", rv); asus->sfun = rv; } @@ -1883,7 +1941,7 @@ static int asus_wmi_platform_init(struct asus_wmi *asus) * or note, while on notebooks, they returns 0xFFFFFFFE on failure, * but once again, SPEC may probably be used for that kind of things. */ - if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS, 0, 0, NULL)) + if (!asus_wmi_evaluate_method(asus, ASUS_WMI_METHODID_DSTS, 0, 0, NULL)) asus->dsts_id = ASUS_WMI_METHODID_DSTS; else asus->dsts_id = ASUS_WMI_METHODID_DSTS2; @@ -1891,7 +1949,7 @@ static int asus_wmi_platform_init(struct asus_wmi *asus) /* CWAP allow to define the behavior of the Fn+F2 key, * this method doesn't seems to be present on Eee PCs */ if (asus->driver->quirks->wapf >= 0) - asus_wmi_set_devstate(ASUS_WMI_DEVID_CWAP, + asus_wmi_set_devstate(asus, ASUS_WMI_DEVID_CWAP, asus->driver->quirks->wapf, NULL); return asus_wmi_sysfs_init(asus->platform_device); @@ -1932,15 +1990,25 @@ static int show_devs(struct seq_file *m, void *data) struct asus_wmi *asus = m->private; int err; u32 retval = -1; - - err = asus_wmi_set_devstate(asus->debug.dev_id, asus->debug.ctrl_param, - &retval); + if (asus->driver->quirks->wmi_needs_3_args) + err = asus_wmi_set_devstate_3args(asus->debug.dev_id, + asus->debug.ctrl_param, + asus->debug.ctrl_param2, + &retval); + else + err = asus_wmi_set_devstate(asus, asus->debug.dev_id, + asus->debug.ctrl_param, &retval); if (err < 0) return err; - seq_printf(m, "DEVS(%#x, %#x) = %#x\n", asus->debug.dev_id, - asus->debug.ctrl_param, retval); + if (asus->driver->quirks->wmi_needs_3_args) + seq_printf(m, "DEVS(%#x, %#x, %#x) = %#x\n", asus->debug.dev_id, + asus->debug.ctrl_param, asus->debug.ctrl_param2, + retval); + else + seq_printf(m, "DEVS(%#x, %#x) = %#x\n", asus->debug.dev_id, + asus->debug.ctrl_param, retval); return 0; } @@ -1948,15 +2016,27 @@ static int show_devs(struct seq_file *m, void *data) static int show_call(struct seq_file *m, void *data) { struct asus_wmi *asus = m->private; - struct bios_args args = { - .arg0 = asus->debug.dev_id, - .arg1 = asus->debug.ctrl_param, - }; - struct acpi_buffer input = { (acpi_size) sizeof(args), &args }; + struct acpi_buffer input; struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; union acpi_object *obj; acpi_status status; + struct bios_args args; + struct bios_3args args3; + + if (asus->driver->quirks->wmi_needs_3_args) { + args3.arg0 = asus->debug.dev_id; + args3.arg1 = asus->debug.ctrl_param; + args3.arg2 = asus->debug.ctrl_param2; + input.length = (acpi_size) sizeof(args3); + input.pointer = &args3; + } else { + args.arg0 = asus->debug.dev_id; + args.arg1 = asus->debug.ctrl_param; + input.length = (acpi_size) sizeof(args); + input.pointer = &args; + } + status = wmi_evaluate_method(ASUS_WMI_MGMT_GUID, 0, asus->debug.method_id, &input, &output); @@ -1965,14 +2045,35 @@ static int show_call(struct seq_file *m, void *data) return -EIO; obj = (union acpi_object *)output.pointer; - if (obj && obj->type == ACPI_TYPE_INTEGER) - seq_printf(m, "%#x(%#x, %#x) = %#x\n", asus->debug.method_id, - asus->debug.dev_id, asus->debug.ctrl_param, - (u32) obj->integer.value); - else - seq_printf(m, "%#x(%#x, %#x) = t:%d\n", asus->debug.method_id, - asus->debug.dev_id, asus->debug.ctrl_param, - obj ? obj->type : -1); + if (asus->driver->quirks->wmi_needs_3_args) { + if (obj && obj->type == ACPI_TYPE_INTEGER) + seq_printf(m, "%#x(%#x, %#x, %#x) = %#x\n", + asus->debug.method_id, + asus->debug.dev_id, + asus->debug.ctrl_param, + asus->debug.ctrl_param2, + (u32) obj->integer.value); + else + seq_printf(m, "%#x(%#x, %#x, %#x) = t:%d\n", + asus->debug.method_id, + asus->debug.dev_id, + asus->debug.ctrl_param, + asus->debug.ctrl_param2, + obj ? obj->type : -1); + } else { + if (obj && obj->type == ACPI_TYPE_INTEGER) + seq_printf(m, "%#x(%#x, %#x) = %#x\n", + asus->debug.method_id, + asus->debug.dev_id, + asus->debug.ctrl_param, + (u32) obj->integer.value); + else + seq_printf(m, "%#x(%#x, %#x) = t:%d\n", + asus->debug.method_id, + asus->debug.dev_id, + asus->debug.ctrl_param, + obj ? obj->type : -1); + } kfree(obj); @@ -2031,6 +2132,14 @@ static int asus_wmi_debugfs_init(struct asus_wmi *asus) if (!dent) goto error_debugfs; + if (asus->driver->quirks->wmi_needs_3_args) { + dent = debugfs_create_x32("ctrl_param2", S_IRUGO | S_IWUSR, + asus->debug.root, + &asus->debug.ctrl_param2); + if (!dent) + goto error_debugfs; + } + for (i = 0; i < ARRAY_SIZE(asus_wmi_debug_files); i++) { struct asus_wmi_debugfs_node *node = &asus_wmi_debug_files[i]; @@ -2125,7 +2234,7 @@ static int asus_wmi_add(struct platform_device *pdev) } if (asus->driver->quirks->wmi_force_als_set) - asus_wmi_set_als(); + asus_wmi_set_als(asus); /* Some Asus desktop boards export an acpi-video backlight interface, stop this from showing up */ @@ -2147,7 +2256,8 @@ static int asus_wmi_add(struct platform_device *pdev) if (err && err != -ENODEV) goto fail_backlight; } else if (asus->driver->quirks->wmi_backlight_set_devstate) - err = asus_wmi_set_devstate(ASUS_WMI_DEVID_BACKLIGHT, 2, NULL); + err = asus_wmi_set_devstate(asus, ASUS_WMI_DEVID_BACKLIGHT, 2, + NULL); if (asus_wmi_has_fnlock_key(asus)) { asus->fnlock_locked = true; @@ -2220,7 +2330,7 @@ static int asus_hotk_thaw(struct device *device) * we should kick it ourselves in case hibernation is aborted. */ wlan = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_WLAN); - asus_wmi_set_devstate(ASUS_WMI_DEVID_WLAN, wlan, NULL); + asus_wmi_set_devstate(asus, ASUS_WMI_DEVID_WLAN, wlan, NULL); } return 0; diff --git a/drivers/platform/x86/asus-wmi.h b/drivers/platform/x86/asus-wmi.h index 4f31b68642a0..9568e42a6995 100644 --- a/drivers/platform/x86/asus-wmi.h +++ b/drivers/platform/x86/asus-wmi.h @@ -33,6 +33,7 @@ struct quirk_entry { bool wmi_backlight_native; bool wmi_backlight_set_devstate; bool wmi_force_als_set; + bool wmi_needs_3_args; int wapf; /* * For machines with AMD graphic chips, it will send out WMI event diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h index bfba245636a7..06dd9892533b 100644 --- a/include/linux/platform_data/x86/asus-wmi.h +++ b/include/linux/platform_data/x86/asus-wmi.h @@ -89,10 +89,14 @@ #define ASUS_WMI_DSTS_MAX_BRIGTH_MASK 0x0000FF00 #define ASUS_WMI_DSTS_LIGHTBAR_MASK 0x0000000F +struct asus_wmi; + #if IS_REACHABLE(CONFIG_ASUS_WMI) -int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval); +int asus_wmi_evaluate_method(struct asus_wmi *asus, u32 method_id, u32 arg0, + u32 arg1, u32 *retval); #else -static inline int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, +static inline int asus_wmi_evaluate_method(struct asus_wmi *asus, + u32 method_id, u32 arg0, u32 arg1, u32 *retval) { return -ENODEV; -- 2.20.1