Message ID | 20181203185344.3521-3-ayman.bagabas@gmail.com (mailing list archive) |
---|---|
State | Changes Requested, archived |
Headers | show |
Series | Huawei laptops | expand |
On Mon, 03 Dec 2018 19:53:39 +0100, Ayman Bagabas wrote: > + if (code == 0x80) { > + acpi_status status; > + acpi_handle handle; > + unsigned long long result; > + union acpi_object args[1]; > + struct acpi_object_list arg_list = { > + .pointer = args, > + .count = ARRAY_SIZE(args), > + }; > + > + args[0].type = ACPI_TYPE_INTEGER; > + args[0].integer.value = 0; > + > + status = acpi_get_handle(NULL, "\\WMI0", &handle); > + if (ACPI_FAILURE(status)) { > + dev_err(&wdev->dev, "Unable to get ACPI handle\n"); > + return; > + } > + > + status = acpi_evaluate_integer(NULL, "WQ00", &arg_list, &result); I guess you need to pass handle here? In the earlier version, you passed \\WMI0.WQ00, so it worked with NULL handle. But now it's no longer so... Other than that, it looks OK to me. Reviewed-by: Takashi Iwai <tiwai@suse.de> But, you don't need to rush too much. Instead, better to test your own patches and make sure that they really work before submitting the final version. thanks, Takashi
On Mon, Dec 3, 2018 at 9:04 PM Takashi Iwai <tiwai@suse.de> wrote: > > On Mon, 03 Dec 2018 19:53:39 +0100, > Ayman Bagabas wrote: > > + if (code == 0x80) { > > + acpi_status status; > > + acpi_handle handle; > > + unsigned long long result; > > + union acpi_object args[1]; > > + struct acpi_object_list arg_list = { > > + .pointer = args, > > + .count = ARRAY_SIZE(args), > > + }; > > + > > + args[0].type = ACPI_TYPE_INTEGER; > > + args[0].integer.value = 0; > > + > > + status = acpi_get_handle(NULL, "\\WMI0", &handle); > > + if (ACPI_FAILURE(status)) { > > + dev_err(&wdev->dev, "Unable to get ACPI handle\n"); > > + return; > > + } > > + > > + status = acpi_evaluate_integer(NULL, "WQ00", &arg_list, &result); > > I guess you need to pass handle here? In the earlier version, you > passed \\WMI0.WQ00, so it worked with NULL handle. But now it's no > longer so... I think in this case we don't need to have a separate call to get handle and try to get integer directly. In either we will have an error case if method / namespace / etc is not found. > Other than that, it looks OK to me. > Reviewed-by: Takashi Iwai <tiwai@suse.de> > > But, you don't need to rush too much. Instead, better to test your > own patches and make sure that they really work before submitting the > final version. Yes, I will wait couple of days for v10 in hope it will be tested carefully.
On 12/3/18 10:53 AM, Ayman Bagabas wrote: > This driver adds support for missing hotkeys on some Huawei laptops. > Laptops such as the Matebook X have non functioning hotkeys. Whereas > newer laptops such as the Matebook X Pro come with working hotkeys out > of the box. > > Old laptops, such as the Matebook X, report hotkey events through ACPI > device "\WMI0". However, new laptops, such as the Matebook X Pro, > does not have this WMI device. > > All the hotkeys on the Matebook X Pro work fine > without this patch except (micmute, wlan, and huawei key). These keys > and the brightness keys report events to "\AMW0" ACPI device. One > problem is that brightness keys on the Matebook X Pro work without this > patch. This results in reporting two brightness key press > events one is captured by ACPI and another by this driver. > > A solution would be to check if such event came from the "\AMW0" WMI driver > then skip reporting event. Another solution would be to leave this to > user-space to handle. Which can be achieved by using "hwdb" tables and > remap those keys to "unknown". This solution seems more natural to me > because it leaves the decision to user-space. > > Signed-off-by: Ayman Bagabas <ayman.bagabas@gmail.com> > --- > drivers/platform/x86/Kconfig | 16 +++ > drivers/platform/x86/Makefile | 1 + > drivers/platform/x86/huawei-wmi.c | 226 ++++++++++++++++++++++++++++++ > 3 files changed, 243 insertions(+) > create mode 100644 drivers/platform/x86/huawei-wmi.c > > diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig > index 87f70e8f4dd0..27db3ce7a1e1 100644 > --- a/drivers/platform/x86/Kconfig > +++ b/drivers/platform/x86/Kconfig > @@ -1292,6 +1292,22 @@ config INTEL_ATOMISP2_PM > To compile this driver as a module, choose M here: the module > will be called intel_atomisp2_pm. > > +config HUAWEI_WMI > + tristate "Huawei WMI hotkeys driver" > + depends on ACPI_WMI > + depends on INPUT > + select INPUT_SPARSEKMAP > + select LEDS_CLASS but LEDS_CLASS is only available if NEW_LEDS is set/enabled, so it either depends on LEDS_CLASS... or it needs to select NEW_LEDS, which is what other drivers in this Kconfig file do, even though our b.d. has said a few times that any one driver should not "select" => enable an entire subsystem, so they all should be "depends on NEW_LEDS" IMHO. > + select LEDS_TRIGGERS > + select LEDS_TRIGGER_AUDIO > + help > + This driver provides support for Huawei WMI hotkeys. > + It enables the missing keys and adds support to the micmute > + LED found on some of these laptops. > + > + To compile this driver as a module, choose M here: the module > + will be called huawei-wmi. > + > endif # X86_PLATFORM_DEVICES > > config PMC_ATOM
On Mon, 2018-12-03 at 11:41 -0800, Randy Dunlap wrote: > On 12/3/18 10:53 AM, Ayman Bagabas wrote: > > This driver adds support for missing hotkeys on some Huawei > > laptops. > > Laptops such as the Matebook X have non functioning hotkeys. > > Whereas > > newer laptops such as the Matebook X Pro come with working hotkeys > > out > > of the box. > > > > Old laptops, such as the Matebook X, report hotkey events through > > ACPI > > device "\WMI0". However, new laptops, such as the Matebook X Pro, > > does not have this WMI device. > > > > All the hotkeys on the Matebook X Pro work fine > > without this patch except (micmute, wlan, and huawei key). These > > keys > > and the brightness keys report events to "\AMW0" ACPI device. One > > problem is that brightness keys on the Matebook X Pro work without > > this > > patch. This results in reporting two brightness key press > > events one is captured by ACPI and another by this driver. > > > > A solution would be to check if such event came from the "\AMW0" > > WMI driver > > then skip reporting event. Another solution would be to leave this > > to > > user-space to handle. Which can be achieved by using "hwdb" tables > > and > > remap those keys to "unknown". This solution seems more natural to > > me > > because it leaves the decision to user-space. > > > > Signed-off-by: Ayman Bagabas <ayman.bagabas@gmail.com> > > --- > > drivers/platform/x86/Kconfig | 16 +++ > > drivers/platform/x86/Makefile | 1 + > > drivers/platform/x86/huawei-wmi.c | 226 > > ++++++++++++++++++++++++++++++ > > 3 files changed, 243 insertions(+) > > create mode 100644 drivers/platform/x86/huawei-wmi.c > > > > diff --git a/drivers/platform/x86/Kconfig > > b/drivers/platform/x86/Kconfig > > index 87f70e8f4dd0..27db3ce7a1e1 100644 > > --- a/drivers/platform/x86/Kconfig > > +++ b/drivers/platform/x86/Kconfig > > @@ -1292,6 +1292,22 @@ config INTEL_ATOMISP2_PM > > To compile this driver as a module, choose M here: the module > > will be called intel_atomisp2_pm. > > > > +config HUAWEI_WMI > > + tristate "Huawei WMI hotkeys driver" > > + depends on ACPI_WMI > > + depends on INPUT > > + select INPUT_SPARSEKMAP > > + select LEDS_CLASS > > but LEDS_CLASS is only available if NEW_LEDS is set/enabled, so it > either > depends on LEDS_CLASS... or it needs to select NEW_LEDS, which is > what other > drivers in this Kconfig file do, even though our b.d. has said a few > times > that any one driver should not "select" => enable an entire > subsystem, > so they all should be "depends on NEW_LEDS" IMHO. Noted, I'll select NEW_LEDS just like other drivers in Kconfig. > > > + select LEDS_TRIGGERS > > + select LEDS_TRIGGER_AUDIO > > + help > > + This driver provides support for Huawei WMI hotkeys. > > + It enables the missing keys and adds support to the micmute > > + LED found on some of these laptops. > > + > > + To compile this driver as a module, choose M here: the module > > + will be called huawei-wmi. > > + > > endif # X86_PLATFORM_DEVICES > > > > config PMC_ATOM > >
On Mon, 2018-12-03 at 21:17 +0200, Andy Shevchenko wrote: > On Mon, Dec 3, 2018 at 9:04 PM Takashi Iwai <tiwai@suse.de> wrote: > > On Mon, 03 Dec 2018 19:53:39 +0100, > > Ayman Bagabas wrote: > > > + if (code == 0x80) { > > > + acpi_status status; > > > + acpi_handle handle; > > > + unsigned long long result; > > > + union acpi_object args[1]; > > > + struct acpi_object_list arg_list = { > > > + .pointer = args, > > > + .count = ARRAY_SIZE(args), > > > + }; > > > + > > > + args[0].type = ACPI_TYPE_INTEGER; > > > + args[0].integer.value = 0; > > > + > > > + status = acpi_get_handle(NULL, "\\WMI0", &handle); > > > + if (ACPI_FAILURE(status)) { > > > + dev_err(&wdev->dev, "Unable to get ACPI > > > handle\n"); > > > + return; > > > + } > > > + > > > + status = acpi_evaluate_integer(NULL, "WQ00", > > > &arg_list, &result); > > > > I guess you need to pass handle here? In the earlier version, you > > passed \\WMI0.WQ00, so it worked with NULL handle. But now it's no > > longer so... > > I think in this case we don't need to have a separate call to get > handle and try to get integer directly. In either we will have an > error case if method / namespace / etc is not found. Agree. > > > Other than that, it looks OK to me. > > Reviewed-by: Takashi Iwai <tiwai@suse.de> Thanks. > > > > But, you don't need to rush too much. Instead, better to test your > > own patches and make sure that they really work before submitting > > the > > final version. > > Yes, I will wait couple of days for v10 in hope it will be tested > carefully. You're right, I will test it more and make sure everything work as expected. >
On Fri, 2018-12-07 at 00:52 -0500, ayman.bagabas@gmail.com wrote: On Mon, 2018-12-03 at 21:17 +0200, Andy Shevchenko wrote: > On Mon, Dec 3, 2018 at 9:04 PM Takashi Iwai <tiwai@suse.de> wrote: > > On Mon, 03 Dec 2018 19:53:39 +0100, > > Ayman Bagabas wrote: > > > + if (code == 0x80) { > > > + acpi_status status; > > > + acpi_handle handle; > > > + unsigned long long result; > > > + union acpi_object args[1]; > > > + struct acpi_object_list arg_list = { > > > + .pointer = args, > > > + .count = ARRAY_SIZE(args), > > > + }; > > > + > > > + args[0].type = ACPI_TYPE_INTEGER; > > > + args[0].integer.value = 0; > > > + > > > + status = acpi_get_handle(NULL, "\\WMI0", &handle); > > > + if (ACPI_FAILURE(status)) { > > > + dev_err(&wdev->dev, "Unable to get ACPI > > > handle\n"); > > > + return; > > > + } > > > + > > > + status = acpi_evaluate_integer(NULL, "WQ00", > > > &arg_list, &result); > > > > I guess you need to pass handle here? In the earlier version, you > > passed \\WMI0.WQ00, so it worked with NULL handle. But now it's no > > longer so... > > I think in this case we don't need to have a separate call to get > handle and try to get integer directly. In either we will have an > error case if method / namespace / etc is not found. > > I was digging further into the DSDT table of the laptop that has WQ00 method and it turned out that this method is a ACPI_WMI_EXPENSIVE. It has its own GUID of "39142400-C6A3-40fa-BADB-8A2652834100". I believe wmi_query_block, which is deprecated BTW, can be used to access this method instead of acpi_evaluate. However, I don't have a machine to test it on. Any ideas?
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 87f70e8f4dd0..27db3ce7a1e1 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -1292,6 +1292,22 @@ config INTEL_ATOMISP2_PM To compile this driver as a module, choose M here: the module will be called intel_atomisp2_pm. +config HUAWEI_WMI + tristate "Huawei WMI hotkeys driver" + depends on ACPI_WMI + depends on INPUT + select INPUT_SPARSEKMAP + select LEDS_CLASS + select LEDS_TRIGGERS + select LEDS_TRIGGER_AUDIO + help + This driver provides support for Huawei WMI hotkeys. + It enables the missing keys and adds support to the micmute + LED found on some of these laptops. + + To compile this driver as a module, choose M here: the module + will be called huawei-wmi. + endif # X86_PLATFORM_DEVICES config PMC_ATOM diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 39ae94135406..d841c550e3cc 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -32,6 +32,7 @@ obj-$(CONFIG_ACERHDF) += acerhdf.o obj-$(CONFIG_HP_ACCEL) += hp_accel.o obj-$(CONFIG_HP_WIRELESS) += hp-wireless.o obj-$(CONFIG_HP_WMI) += hp-wmi.o +obj-$(CONFIG_HUAWEI_WMI) += huawei-wmi.o obj-$(CONFIG_AMILO_RFKILL) += amilo-rfkill.o obj-$(CONFIG_GPD_POCKET_FAN) += gpd-pocket-fan.o obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o diff --git a/drivers/platform/x86/huawei-wmi.c b/drivers/platform/x86/huawei-wmi.c new file mode 100644 index 000000000000..afa030fd1285 --- /dev/null +++ b/drivers/platform/x86/huawei-wmi.c @@ -0,0 +1,226 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Huawei WMI hotkeys + * + * Copyright (C) 2018 Ayman Bagabas <ayman.bagabas@gmail.com> + */ + +#include <linux/acpi.h> +#include <linux/input.h> +#include <linux/input/sparse-keymap.h> +#include <linux/leds.h> +#include <linux/module.h> +#include <linux/wmi.h> + +/* + * Huawei WMI Events GUIDs + */ +#define WMI0_EVENT_GUID "59142400-C6A3-40fa-BADB-8A2652834100" +#define AMW0_EVENT_GUID "ABBC0F5C-8EA1-11D1-A000-C90629100000" + +struct huawei_wmi_priv { + struct input_dev *idev; + struct led_classdev cdev; + acpi_handle handle; + char *acpi_method; +}; + +static const struct key_entry huawei_wmi_keymap[] = { + { KE_KEY, 0x281, { KEY_BRIGHTNESSDOWN } }, + { KE_KEY, 0x282, { KEY_BRIGHTNESSUP } }, + { KE_KEY, 0x284, { KEY_MUTE } }, + { KE_KEY, 0x285, { KEY_VOLUMEDOWN } }, + { KE_KEY, 0x286, { KEY_VOLUMEUP } }, + { KE_KEY, 0x287, { KEY_MICMUTE } }, + { KE_KEY, 0x289, { KEY_WLAN } }, + // Huawei |M| button + { KE_KEY, 0x28a, { KEY_CONFIG } }, + // Keyboard light + { KE_IGNORE, 0x293, { KEY_KBDILLUMTOGGLE } }, + { KE_IGNORE, 0x294, { KEY_KBDILLUMUP } }, + { KE_IGNORE, 0x295, { KEY_KBDILLUMUP } }, + { KE_END, 0 } +}; + +static int huawei_wmi_micmute_led_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct huawei_wmi_priv *priv = dev_get_drvdata(led_cdev->dev->parent); + acpi_status status; + union acpi_object args[3]; + struct acpi_object_list arg_list = { + .pointer = args, + .count = ARRAY_SIZE(args), + }; + + args[0].type = args[1].type = args[2].type = ACPI_TYPE_INTEGER; + args[1].integer.value = 0x04; + + if (strcmp(priv->acpi_method, "SPIN") == 0) { + args[0].integer.value = 0; + args[2].integer.value = brightness ? 1 : 0; + } else if (strcmp(priv->acpi_method, "WPIN") == 0) { + args[0].integer.value = 1; + args[2].integer.value = brightness ? 0 : 1; + } else { + return -EINVAL; + } + + status = acpi_evaluate_object(priv->handle, priv->acpi_method, &arg_list, NULL); + if (ACPI_FAILURE(status)) + return -ENXIO; + + return 0; +} + +static int huawei_wmi_leds_setup(struct wmi_device *wdev) +{ + struct huawei_wmi_priv *priv = dev_get_drvdata(&wdev->dev); + acpi_status status; + + // Skip registering LED subsystem if no ACPI method was found. + status = acpi_get_handle(NULL, "\\_SB.PCI0.LPCB.EC0", &priv->handle); + if (ACPI_FAILURE(status)) + return 0; + + if (acpi_has_method(priv->handle, "SPIN")) + priv->acpi_method = "SPIN"; + else if (acpi_has_method(priv->handle, "WPIN")) + priv->acpi_method = "WPIN"; + else + return 0; + + priv->cdev.name = "platform::micmute"; + priv->cdev.max_brightness = 1; + priv->cdev.brightness_set_blocking = huawei_wmi_micmute_led_set; + priv->cdev.default_trigger = "audio-micmute"; + priv->cdev.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE); + priv->cdev.dev = &wdev->dev; + priv->cdev.flags = LED_CORE_SUSPENDRESUME; + + return devm_led_classdev_register(&wdev->dev, &priv->cdev); +} + +static void huawei_wmi_process_key(struct wmi_device *wdev, int code) +{ + struct huawei_wmi_priv *priv = dev_get_drvdata(&wdev->dev); + const struct key_entry *key; + + /* + * WMI0 uses code 0x80 to indicate a hotkey event. + * The actual key is fetched from the method WQ00. + */ + if (code == 0x80) { + acpi_status status; + acpi_handle handle; + unsigned long long result; + union acpi_object args[1]; + struct acpi_object_list arg_list = { + .pointer = args, + .count = ARRAY_SIZE(args), + }; + + args[0].type = ACPI_TYPE_INTEGER; + args[0].integer.value = 0; + + status = acpi_get_handle(NULL, "\\WMI0", &handle); + if (ACPI_FAILURE(status)) { + dev_err(&wdev->dev, "Unable to get ACPI handle\n"); + return; + } + + status = acpi_evaluate_integer(NULL, "WQ00", &arg_list, &result); + if (ACPI_FAILURE(status)) { + dev_err(&wdev->dev, "Unable to evaluate ACPI method\n"); + return; + } + + code = result; + } + + key = sparse_keymap_entry_from_scancode(priv->idev, code); + if (!key) { + dev_info(&wdev->dev, "Unknown key pressed, code: 0x%04x\n", code); + return; + } + + sparse_keymap_report_entry(priv->idev, key, 1, true); +} + +static void huawei_wmi_notify(struct wmi_device *wdev, + union acpi_object *obj) +{ + if (obj->type == ACPI_TYPE_INTEGER) + huawei_wmi_process_key(wdev, obj->integer.value); + else + dev_info(&wdev->dev, "Bad response type %d\n", obj->type); +} + +static int huawei_wmi_input_setup(struct wmi_device *wdev) +{ + struct huawei_wmi_priv *priv = dev_get_drvdata(&wdev->dev); + int err; + + priv->idev = devm_input_allocate_device(&wdev->dev); + if (!priv->idev) + return -ENOMEM; + + priv->idev->name = "Huawei WMI hotkeys"; + priv->idev->phys = "wmi/input0"; + priv->idev->id.bustype = BUS_HOST; + priv->idev->dev.parent = &wdev->dev; + + err = sparse_keymap_setup(priv->idev, huawei_wmi_keymap, NULL); + if (err) + return err; + + err = input_register_device(priv->idev); + if (err) + return err; + + return 0; +} + +static int huawei_wmi_probe(struct wmi_device *wdev) +{ + struct huawei_wmi_priv *priv; + int err; + + priv = devm_kzalloc(&wdev->dev, sizeof(struct huawei_wmi_priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + dev_set_drvdata(&wdev->dev, priv); + + err = huawei_wmi_input_setup(wdev); + if (err) + return err; + + err = huawei_wmi_leds_setup(wdev); + if (err) + return err; + + return 0; +} + +static const struct wmi_device_id huawei_wmi_id_table[] = { + { .guid_string = WMI0_EVENT_GUID }, + { .guid_string = AMW0_EVENT_GUID }, + { } +}; + +static struct wmi_driver huawei_wmi_driver = { + .driver = { + .name = "huawei-wmi", + }, + .id_table = huawei_wmi_id_table, + .probe = huawei_wmi_probe, + .notify = huawei_wmi_notify, +}; + +module_wmi_driver(huawei_wmi_driver); + +MODULE_ALIAS("wmi:"WMI0_EVENT_GUID); +MODULE_ALIAS("wmi:"AMW0_EVENT_GUID); +MODULE_AUTHOR("Ayman Bagabas <ayman.bagabas@gmail.com>"); +MODULE_DESCRIPTION("Huawei WMI hotkeys"); +MODULE_LICENSE("GPL v2");
This driver adds support for missing hotkeys on some Huawei laptops. Laptops such as the Matebook X have non functioning hotkeys. Whereas newer laptops such as the Matebook X Pro come with working hotkeys out of the box. Old laptops, such as the Matebook X, report hotkey events through ACPI device "\WMI0". However, new laptops, such as the Matebook X Pro, does not have this WMI device. All the hotkeys on the Matebook X Pro work fine without this patch except (micmute, wlan, and huawei key). These keys and the brightness keys report events to "\AMW0" ACPI device. One problem is that brightness keys on the Matebook X Pro work without this patch. This results in reporting two brightness key press events one is captured by ACPI and another by this driver. A solution would be to check if such event came from the "\AMW0" WMI driver then skip reporting event. Another solution would be to leave this to user-space to handle. Which can be achieved by using "hwdb" tables and remap those keys to "unknown". This solution seems more natural to me because it leaves the decision to user-space. Signed-off-by: Ayman Bagabas <ayman.bagabas@gmail.com> --- drivers/platform/x86/Kconfig | 16 +++ drivers/platform/x86/Makefile | 1 + drivers/platform/x86/huawei-wmi.c | 226 ++++++++++++++++++++++++++++++ 3 files changed, 243 insertions(+) create mode 100644 drivers/platform/x86/huawei-wmi.c