2019-05-27 16:39:08

by Mattias Jacobsson

[permalink] [raw]
Subject: [PATCH 0/3] platform/x86: wmi: add Xiaomi WMI key driver

This patchset adds a WMI key driver for Xiaomi notebooks. To be able to
do this while using the struct wmi_driver a context field has been added
to struct wmi_device_id.

[PATCH 1/3]: Add field to struct wmi_device_id and add a helper function
[PATCH 2/3]: Add new parameter to the probe function
[PATCH 3/3]: Add new driver

Mattias Jacobsson (3):
platform/x86: wmi: add context pointer field to struct wmi_device_id
platform/x86: wmi: add context argument to the probe function
platform/x86: wmi: add Xiaomi WMI key driver

drivers/platform/x86/Kconfig | 10 +++
drivers/platform/x86/Makefile | 1 +
drivers/platform/x86/dell-smbios-wmi.c | 2 +-
drivers/platform/x86/dell-wmi-descriptor.c | 3 +-
drivers/platform/x86/dell-wmi.c | 2 +-
drivers/platform/x86/huawei-wmi.c | 2 +-
drivers/platform/x86/intel-wmi-thunderbolt.c | 3 +-
drivers/platform/x86/wmi-bmof.c | 2 +-
drivers/platform/x86/wmi.c | 25 +++++-
drivers/platform/x86/xiaomi-wmi.c | 94 ++++++++++++++++++++
include/linux/mod_devicetable.h | 1 +
include/linux/wmi.h | 2 +-
12 files changed, 139 insertions(+), 8 deletions(-)
create mode 100644 drivers/platform/x86/xiaomi-wmi.c

--
2.21.0


2019-05-27 17:07:34

by Mattias Jacobsson

[permalink] [raw]
Subject: [PATCH 3/3] platform/x86: wmi: add Xiaomi WMI key driver

Some function keys on the built in keyboard on Xiaomi's notebooks does
not produce any key events when pressed in combination with the function
key. Some of these keys do report that they are being pressed via WMI
events.

This driver reports key events for Fn+F7 and double tap on Fn.

Other WMI events that are reported by the hardware but not utilized by
this driver are Caps Lock(which already work) and Fn lock/unlock.

Signed-off-by: Mattias Jacobsson <[email protected]>
---
drivers/platform/x86/Kconfig | 10 ++++
drivers/platform/x86/Makefile | 1 +
drivers/platform/x86/xiaomi-wmi.c | 94 +++++++++++++++++++++++++++++++
3 files changed, 105 insertions(+)
create mode 100644 drivers/platform/x86/xiaomi-wmi.c

diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 5d5cc6111081..257a99134b64 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -781,6 +781,16 @@ config INTEL_WMI_THUNDERBOLT
To compile this driver as a module, choose M here: the module will
be called intel-wmi-thunderbolt.

+config XIAOMI_WMI
+ tristate "Xiaomi WMI key driver"
+ depends on ACPI_WMI
+ depends on INPUT
+ help
+ Say Y here if you want to support WMI-based keys on Xiaomi notebooks.
+
+ To compile this driver as a module, choose M here: the module will
+ be called xiaomi-wmi.
+
config MSI_WMI
tristate "MSI WMI extras"
depends on ACPI_WMI
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index 87b0069bd781..f64445d69f99 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -51,6 +51,7 @@ obj-$(CONFIG_SURFACE3_WMI) += surface3-wmi.o
obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o
obj-$(CONFIG_WMI_BMOF) += wmi-bmof.o
obj-$(CONFIG_INTEL_WMI_THUNDERBOLT) += intel-wmi-thunderbolt.o
+obj-$(CONFIG_XIAOMI_WMI) += xiaomi-wmi.o

# toshiba_acpi must link after wmi to ensure that wmi devices are found
# before toshiba_acpi initializes
diff --git a/drivers/platform/x86/xiaomi-wmi.c b/drivers/platform/x86/xiaomi-wmi.c
new file mode 100644
index 000000000000..4ff9df5eb88f
--- /dev/null
+++ b/drivers/platform/x86/xiaomi-wmi.c
@@ -0,0 +1,94 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * WMI driver for Xiaomi Laptops
+ *
+ */
+
+#include <linux/acpi.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/wmi.h>
+#include <uapi/linux/input-event-codes.h>
+
+#define XIAOMI_KEY_FN_ESC_0 "A2095CCE-0491-44E7-BA27-F8ED8F88AA86"
+#define XIAOMI_KEY_FN_ESC_1 "7BBE8E39-B486-473D-BA13-66F75C5805CD"
+#define XIAOMI_KEY_FN_FN "409B028D-F06B-4C7C-8BBB-EE133A6BD87E"
+#define XIAOMI_KEY_CAPSLOCK "83FE7607-053A-4644-822A-21532C621FC7"
+#define XIAOMI_KEY_FN_F7 "76E9027C-95D0-4180-8692-DA6747DD1C2D"
+
+#define XIAOMI_DEVICE(guid, key) \
+ .guid_string = (guid), \
+ .context = &(const unsigned int){key}
+
+struct xiaomi_wmi {
+ struct input_dev *input_dev;
+ unsigned int key_code;
+};
+
+int xiaomi_wmi_probe(struct wmi_device *wdev, const void *context)
+{
+ struct xiaomi_wmi *data;
+
+ if (wdev == NULL || context == NULL)
+ return -EINVAL;
+
+ data = devm_kzalloc(&wdev->dev, sizeof(struct xiaomi_wmi), GFP_KERNEL);
+ if (data == NULL)
+ return -ENOMEM;
+ dev_set_drvdata(&wdev->dev, data);
+
+ data->input_dev = devm_input_allocate_device(&wdev->dev);
+ if (data->input_dev == NULL)
+ return -ENOMEM;
+ data->input_dev->name = "Xiaomi WMI keys";
+ data->input_dev->phys = "wmi/input0";
+
+ data->key_code = *((const unsigned int *)context);
+ set_bit(EV_KEY, data->input_dev->evbit);
+ set_bit(data->key_code, data->input_dev->keybit);
+
+ return input_register_device(data->input_dev);
+}
+
+void xiaomi_wmi_notify(struct wmi_device *wdev, union acpi_object *_)
+{
+ struct xiaomi_wmi *data;
+
+ if (wdev == NULL)
+ return;
+
+ data = dev_get_drvdata(&wdev->dev);
+ if (data == NULL)
+ return;
+
+ input_report_key(data->input_dev, data->key_code, 1);
+ input_sync(data->input_dev);
+ input_report_key(data->input_dev, data->key_code, 0);
+ input_sync(data->input_dev);
+}
+
+static const struct wmi_device_id xiaomi_wmi_id_table[] = {
+ // { XIAOMI_DEVICE(XIAOMI_KEY_FN_ESC_0, KEY_FN_ESC) },
+ // { XIAOMI_DEVICE(XIAOMI_KEY_FN_ESC_1, KEY_FN_ESC) },
+ { XIAOMI_DEVICE(XIAOMI_KEY_FN_FN, KEY_PROG1) },
+ // { XIAOMI_DEVICE(XIAOMI_KEY_CAPSLOCK, KEY_CAPSLOCK) },
+ { XIAOMI_DEVICE(XIAOMI_KEY_FN_F7, KEY_CUT) },
+
+ /* Terminating entry */
+ { }
+};
+
+static struct wmi_driver xiaomi_wmi_driver = {
+ .driver = {
+ .name = "xiaomi-wmi",
+ },
+ .id_table = xiaomi_wmi_id_table,
+ .probe = xiaomi_wmi_probe,
+ .notify = xiaomi_wmi_notify,
+};
+module_wmi_driver(xiaomi_wmi_driver);
+
+MODULE_DEVICE_TABLE(wmi, xiaomi_wmi_id_table);
+MODULE_AUTHOR("Mattias Jacobsson");
+MODULE_DESCRIPTION("Xiaomi WMI driver");
+MODULE_LICENSE("GPL v2");
--
2.21.0

2019-06-29 13:17:47

by Andy Shevchenko

[permalink] [raw]
Subject: Re: [PATCH 3/3] platform/x86: wmi: add Xiaomi WMI key driver

On Mon, May 27, 2019 at 7:22 PM Mattias Jacobsson <[email protected]> wrote:
>
> Some function keys on the built in keyboard on Xiaomi's notebooks does
> not produce any key events when pressed in combination with the function
> key. Some of these keys do report that they are being pressed via WMI
> events.
>
> This driver reports key events for Fn+F7 and double tap on Fn.
>
> Other WMI events that are reported by the hardware but not utilized by
> this driver are Caps Lock(which already work) and Fn lock/unlock.
>

Pushed to my review and testing queue, thanks!

> Signed-off-by: Mattias Jacobsson <[email protected]>
> ---
> drivers/platform/x86/Kconfig | 10 ++++
> drivers/platform/x86/Makefile | 1 +
> drivers/platform/x86/xiaomi-wmi.c | 94 +++++++++++++++++++++++++++++++
> 3 files changed, 105 insertions(+)
> create mode 100644 drivers/platform/x86/xiaomi-wmi.c
>
> diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
> index 5d5cc6111081..257a99134b64 100644
> --- a/drivers/platform/x86/Kconfig
> +++ b/drivers/platform/x86/Kconfig
> @@ -781,6 +781,16 @@ config INTEL_WMI_THUNDERBOLT
> To compile this driver as a module, choose M here: the module will
> be called intel-wmi-thunderbolt.
>
> +config XIAOMI_WMI
> + tristate "Xiaomi WMI key driver"
> + depends on ACPI_WMI
> + depends on INPUT
> + help
> + Say Y here if you want to support WMI-based keys on Xiaomi notebooks.
> +
> + To compile this driver as a module, choose M here: the module will
> + be called xiaomi-wmi.
> +
> config MSI_WMI
> tristate "MSI WMI extras"
> depends on ACPI_WMI
> diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
> index 87b0069bd781..f64445d69f99 100644
> --- a/drivers/platform/x86/Makefile
> +++ b/drivers/platform/x86/Makefile
> @@ -51,6 +51,7 @@ obj-$(CONFIG_SURFACE3_WMI) += surface3-wmi.o
> obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o
> obj-$(CONFIG_WMI_BMOF) += wmi-bmof.o
> obj-$(CONFIG_INTEL_WMI_THUNDERBOLT) += intel-wmi-thunderbolt.o
> +obj-$(CONFIG_XIAOMI_WMI) += xiaomi-wmi.o
>
> # toshiba_acpi must link after wmi to ensure that wmi devices are found
> # before toshiba_acpi initializes
> diff --git a/drivers/platform/x86/xiaomi-wmi.c b/drivers/platform/x86/xiaomi-wmi.c
> new file mode 100644
> index 000000000000..4ff9df5eb88f
> --- /dev/null
> +++ b/drivers/platform/x86/xiaomi-wmi.c
> @@ -0,0 +1,94 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * WMI driver for Xiaomi Laptops
> + *
> + */
> +
> +#include <linux/acpi.h>
> +#include <linux/input.h>
> +#include <linux/module.h>
> +#include <linux/wmi.h>
> +#include <uapi/linux/input-event-codes.h>
> +
> +#define XIAOMI_KEY_FN_ESC_0 "A2095CCE-0491-44E7-BA27-F8ED8F88AA86"
> +#define XIAOMI_KEY_FN_ESC_1 "7BBE8E39-B486-473D-BA13-66F75C5805CD"
> +#define XIAOMI_KEY_FN_FN "409B028D-F06B-4C7C-8BBB-EE133A6BD87E"
> +#define XIAOMI_KEY_CAPSLOCK "83FE7607-053A-4644-822A-21532C621FC7"
> +#define XIAOMI_KEY_FN_F7 "76E9027C-95D0-4180-8692-DA6747DD1C2D"
> +
> +#define XIAOMI_DEVICE(guid, key) \
> + .guid_string = (guid), \
> + .context = &(const unsigned int){key}
> +
> +struct xiaomi_wmi {
> + struct input_dev *input_dev;
> + unsigned int key_code;
> +};
> +
> +int xiaomi_wmi_probe(struct wmi_device *wdev, const void *context)
> +{
> + struct xiaomi_wmi *data;
> +
> + if (wdev == NULL || context == NULL)
> + return -EINVAL;
> +
> + data = devm_kzalloc(&wdev->dev, sizeof(struct xiaomi_wmi), GFP_KERNEL);
> + if (data == NULL)
> + return -ENOMEM;
> + dev_set_drvdata(&wdev->dev, data);
> +
> + data->input_dev = devm_input_allocate_device(&wdev->dev);
> + if (data->input_dev == NULL)
> + return -ENOMEM;
> + data->input_dev->name = "Xiaomi WMI keys";
> + data->input_dev->phys = "wmi/input0";
> +
> + data->key_code = *((const unsigned int *)context);
> + set_bit(EV_KEY, data->input_dev->evbit);
> + set_bit(data->key_code, data->input_dev->keybit);
> +
> + return input_register_device(data->input_dev);
> +}
> +
> +void xiaomi_wmi_notify(struct wmi_device *wdev, union acpi_object *_)
> +{
> + struct xiaomi_wmi *data;
> +
> + if (wdev == NULL)
> + return;
> +
> + data = dev_get_drvdata(&wdev->dev);
> + if (data == NULL)
> + return;
> +
> + input_report_key(data->input_dev, data->key_code, 1);
> + input_sync(data->input_dev);
> + input_report_key(data->input_dev, data->key_code, 0);
> + input_sync(data->input_dev);
> +}
> +
> +static const struct wmi_device_id xiaomi_wmi_id_table[] = {
> + // { XIAOMI_DEVICE(XIAOMI_KEY_FN_ESC_0, KEY_FN_ESC) },
> + // { XIAOMI_DEVICE(XIAOMI_KEY_FN_ESC_1, KEY_FN_ESC) },
> + { XIAOMI_DEVICE(XIAOMI_KEY_FN_FN, KEY_PROG1) },
> + // { XIAOMI_DEVICE(XIAOMI_KEY_CAPSLOCK, KEY_CAPSLOCK) },
> + { XIAOMI_DEVICE(XIAOMI_KEY_FN_F7, KEY_CUT) },
> +
> + /* Terminating entry */
> + { }
> +};
> +
> +static struct wmi_driver xiaomi_wmi_driver = {
> + .driver = {
> + .name = "xiaomi-wmi",
> + },
> + .id_table = xiaomi_wmi_id_table,
> + .probe = xiaomi_wmi_probe,
> + .notify = xiaomi_wmi_notify,
> +};
> +module_wmi_driver(xiaomi_wmi_driver);
> +
> +MODULE_DEVICE_TABLE(wmi, xiaomi_wmi_id_table);
> +MODULE_AUTHOR("Mattias Jacobsson");
> +MODULE_DESCRIPTION("Xiaomi WMI driver");
> +MODULE_LICENSE("GPL v2");
> --
> 2.21.0
>


--
With Best Regards,
Andy Shevchenko