]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
platform/x86: Add WMI driver for Redmibook keyboard
authorGladyshev Ilya <foxido@foxido.dev>
Wed, 20 Aug 2025 17:41:32 +0000 (20:41 +0300)
committerIlpo Järvinen <ilpo.jarvinen@linux.intel.com>
Thu, 28 Aug 2025 11:53:30 +0000 (14:53 +0300)
This driver implements support for various Fn keys (like Cut) and Xiaomi
specific AI button.

Reviewed-by: Armin Wolf <W_Armin@gmx.de>
Signed-off-by: Gladyshev Ilya <foxido@foxido.dev>
Link: https://lore.kernel.org/r/20250820174140.41410-1-foxido@foxido.dev
[ij: use BIT(8) instead of 0x00000100]
Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
MAINTAINERS
drivers/platform/x86/Kconfig
drivers/platform/x86/Makefile
drivers/platform/x86/redmi-wmi.c [new file with mode: 0644]

index fe168477caa45799dfe07de2f54de6d6a1ce0615..df1bb6be63f98a74c88cda17807bd2b17df1687e 100644 (file)
@@ -21253,6 +21253,12 @@ S:     Maintained
 T:     git https://github.com/pkshih/rtw.git
 F:     drivers/net/wireless/realtek/rtw89/
 
+REDMIBOOK WMI DRIVERS
+M:     Gladyshev Ilya <foxido@foxido.dev>
+L:     platform-driver-x86@vger.kernel.org
+S:     Maintained
+F:     drivers/platform/x86/redmi-wmi.c
+
 REDPINE WIRELESS DRIVER
 L:     linux-wireless@vger.kernel.org
 S:     Orphan
index 6d238e120dce78ce5462d442e612e5318b6f28a5..46e62feeda3c9ae0828bd49cac393944347b4147 100644 (file)
@@ -118,6 +118,18 @@ config XIAOMI_WMI
          To compile this driver as a module, choose M here: the module will
          be called xiaomi-wmi.
 
+config REDMI_WMI
+       tristate "Redmibook WMI key driver"
+       depends on ACPI_WMI
+       depends on INPUT
+       select INPUT_SPARSEKMAP
+       help
+         Say Y here if you want support for WMI-based hotkey events on
+         Xiaomi Redmibook devices.
+
+         To compile this driver as a module, choose M here: the module will
+         be called redmi-wmi.
+
 config GIGABYTE_WMI
        tristate "Gigabyte WMI temperature driver"
        depends on ACPI_WMI
index a0c5848513e38dfa02ebffc33c7b04c5483ad6a2..c7db2a88c11a1fcd1d170d35521466db4d50215b 100644 (file)
@@ -13,6 +13,7 @@ obj-$(CONFIG_HUAWEI_WMI)              += huawei-wmi.o
 obj-$(CONFIG_MXM_WMI)                  += mxm-wmi.o
 obj-$(CONFIG_NVIDIA_WMI_EC_BACKLIGHT)  += nvidia-wmi-ec-backlight.o
 obj-$(CONFIG_XIAOMI_WMI)               += xiaomi-wmi.o
+obj-$(CONFIG_REDMI_WMI)                        += redmi-wmi.o
 obj-$(CONFIG_GIGABYTE_WMI)             += gigabyte-wmi.o
 
 # Acer
diff --git a/drivers/platform/x86/redmi-wmi.c b/drivers/platform/x86/redmi-wmi.c
new file mode 100644 (file)
index 0000000..949236b
--- /dev/null
@@ -0,0 +1,130 @@
+// SPDX-License-Identifier: GPL-2.0
+/* WMI driver for Xiaomi Redmibooks */
+
+#include <linux/acpi.h>
+#include <linux/bits.h>
+#include <linux/device.h>
+#include <linux/input.h>
+#include <linux/input/sparse-keymap.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/unaligned.h>
+#include <linux/wmi.h>
+
+#include <uapi/linux/input-event-codes.h>
+
+#define WMI_REDMIBOOK_KEYBOARD_EVENT_GUID "46C93E13-EE9B-4262-8488-563BCA757FEF"
+
+#define AI_KEY_VALUE_MASK BIT(8)
+
+static const struct key_entry redmi_wmi_keymap[] = {
+       {KE_KEY, 0x00000201,    {KEY_SELECTIVE_SCREENSHOT}},
+       {KE_KEY, 0x00000301,    {KEY_ALL_APPLICATIONS}},
+       {KE_KEY, 0x00001b01,    {KEY_SETUP}},
+
+       /* AI button has code for each position */
+       {KE_KEY, 0x00011801,    {KEY_ASSISTANT}},
+       {KE_KEY, 0x00011901,    {KEY_ASSISTANT}},
+
+       /* Keyboard backlight */
+       {KE_IGNORE, 0x00000501, {}},
+       {KE_IGNORE, 0x00800501, {}},
+       {KE_IGNORE, 0x00050501, {}},
+       {KE_IGNORE, 0x000a0501, {}},
+
+       {KE_END}
+};
+
+struct redmi_wmi {
+       struct input_dev *input_dev;
+       /* Protects the key event sequence */
+       struct mutex key_lock;
+};
+
+static int redmi_wmi_probe(struct wmi_device *wdev, const void *context)
+{
+       struct redmi_wmi *data;
+       int err;
+
+       /* Init dev */
+       data = devm_kzalloc(&wdev->dev, sizeof(*data), GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+
+       dev_set_drvdata(&wdev->dev, data);
+
+       err = devm_mutex_init(&wdev->dev, &data->key_lock);
+       if (err)
+               return err;
+
+       data->input_dev = devm_input_allocate_device(&wdev->dev);
+       if (!data->input_dev)
+               return -ENOMEM;
+
+       data->input_dev->name = "Redmibook WMI keys";
+       data->input_dev->phys = "wmi/input0";
+
+       err = sparse_keymap_setup(data->input_dev, redmi_wmi_keymap, NULL);
+       if (err)
+               return err;
+
+       return input_register_device(data->input_dev);
+}
+
+static void redmi_wmi_notify(struct wmi_device *wdev, union acpi_object *obj)
+{
+       struct key_entry *entry;
+       struct redmi_wmi *data = dev_get_drvdata(&wdev->dev);
+       bool autorelease = true;
+       u32 payload;
+       int value = 1;
+
+       if (obj->type != ACPI_TYPE_BUFFER) {
+               dev_err(&wdev->dev, "Bad response type %u\n", obj->type);
+               return;
+       }
+
+       if (obj->buffer.length < 32) {
+               dev_err(&wdev->dev, "Invalid buffer length %u\n", obj->buffer.length);
+               return;
+       }
+
+       payload = get_unaligned_le32(obj->buffer.pointer);
+       entry = sparse_keymap_entry_from_scancode(data->input_dev, payload);
+
+       if (!entry) {
+               dev_dbg(&wdev->dev, "Unknown WMI event with payload %u", payload);
+               return;
+       }
+
+       /* AI key quirk */
+       if (entry->keycode == KEY_ASSISTANT) {
+               value = !(payload & AI_KEY_VALUE_MASK);
+               autorelease = false;
+       }
+
+       guard(mutex)(&data->key_lock);
+       sparse_keymap_report_entry(data->input_dev, entry, value, autorelease);
+}
+
+static const struct wmi_device_id redmi_wmi_id_table[] = {
+       { WMI_REDMIBOOK_KEYBOARD_EVENT_GUID, NULL },
+       { }
+};
+
+static struct wmi_driver redmi_wmi_driver = {
+       .driver = {
+               .name = "redmi-wmi",
+               .probe_type = PROBE_PREFER_ASYNCHRONOUS,
+       },
+       .id_table = redmi_wmi_id_table,
+       .probe = redmi_wmi_probe,
+       .notify = redmi_wmi_notify,
+       .no_singleton = true,
+};
+module_wmi_driver(redmi_wmi_driver);
+
+MODULE_DEVICE_TABLE(wmi, redmi_wmi_id_table);
+MODULE_AUTHOR("Gladyshev Ilya <foxido@foxido.dev>");
+MODULE_DESCRIPTION("Redmibook WMI driver");
+MODULE_LICENSE("GPL");