From 7a622d9869602cf5587cadf418f3eed48a39ab90 Mon Sep 17 00:00:00 2001 From: Philipp Jungkamp Date: Sat, 30 Jul 2022 15:21:20 +0200 Subject: [PATCH 4/9] Add IdeaPad WMI Fn Keys driver Create an input device for WMI events corresponding to some special keys on the 'Lenovo Yoga' line. This include the 3 keys to the right on the 'Lenovo Yoga9 14IAP7' and additionally the 'Lenovo Support' and 'Lenovo Favorites' (star with 'S' inside) in the fn key row. Signed-off-by: Philipp Jungkamp --- drivers/platform/x86/Kconfig | 13 ++ drivers/platform/x86/Makefile | 1 + drivers/platform/x86/ideapad-wmi-fn-keys.c | 151 +++++++++++++++++++++ 3 files changed, 165 insertions(+) create mode 100644 drivers/platform/x86/ideapad-wmi-fn-keys.c diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index bc4013e950ed..6ff757332aa0 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -140,6 +140,19 @@ config YOGABOOK_WMI To compile this driver as a module, choose M here: the module will be called lenovo-yogabook-wmi. +config IDEAPAD_WMI_FN_KEYS + tristate "Lenovo IdeaPad WMI Fn Keys" + depends on ACPI_WMI + depends on INPUT + select INPUT_SPARSEKMAP + help + Say Y here if you want to receive key presses from some lenovo + specific keys. (Star Key, Support Key, Virtual Background, + Dark Mode Toggle, ...) + + To compile this driver as a module, choose M here: the module will + be called ideapad-wmi-fn-keys. + config ACERHDF tristate "Acer Aspire One temperature and fan driver" depends on ACPI && THERMAL diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 4a59f47a46e2..5e9b678e48b9 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_PEAQ_WMI) += peaq-wmi.o obj-$(CONFIG_XIAOMI_WMI) += xiaomi-wmi.o obj-$(CONFIG_GIGABYTE_WMI) += gigabyte-wmi.o obj-$(CONFIG_YOGABOOK_WMI) += lenovo-yogabook-wmi.o +obj-$(CONFIG_IDEAPAD_WMI_FN_KEYS) += ideapad-wmi-fn-keys.o # Acer obj-$(CONFIG_ACERHDF) += acerhdf.o diff --git a/drivers/platform/x86/ideapad-wmi-fn-keys.c b/drivers/platform/x86/ideapad-wmi-fn-keys.c new file mode 100644 index 000000000000..04cac40bc044 --- /dev/null +++ b/drivers/platform/x86/ideapad-wmi-fn-keys.c @@ -0,0 +1,151 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * ideapad-wmi-fn-keys.c - Ideapad WMI fn keys driver + * + * Copyright (C) 2022 Philipp Jungkamp + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include + +#define IDEAPAD_FN_KEY_EVENT_GUID "8FC0DE0C-B4E4-43FD-B0F3-8871711C1294" + +struct ideapad_wmi_private { + struct wmi_device *wmi_device; + struct input_dev *input_dev; +}; + +static const struct key_entry ideapad_wmi_fn_key_keymap[] = { + /* FnLock (handled by the firmware) */ + { KE_IGNORE, 0x02 }, + /* Customizable Lenovo Hotkey ("star" with 'S' inside) */ + { KE_KEY, 0x01, { KEY_FAVORITES } }, + /* Dark mode toggle */ + { KE_KEY, 0x13, { KEY_PROG1 } }, + /* Sound profile switch */ + { KE_KEY, 0x12, { KEY_PROG2 } }, + /* Lenovo Virtual Background application */ + { KE_KEY, 0x28, { KEY_PROG3 } }, + /* Lenovo Support */ + { KE_KEY, 0x27, { KEY_HELP } }, + { KE_END }, +}; + +static int ideapad_wmi_input_init(struct ideapad_wmi_private *priv) +{ + struct input_dev *input_dev; + int err; + + input_dev = input_allocate_device(); + if (!input_dev) { + return -ENOMEM; + } + + input_dev->name = "Ideapad WMI Fn Keys"; + input_dev->phys = IDEAPAD_FN_KEY_EVENT_GUID "/input0"; + input_dev->id.bustype = BUS_HOST; + input_dev->dev.parent = &priv->wmi_device->dev; + + err = sparse_keymap_setup(input_dev, ideapad_wmi_fn_key_keymap, NULL); + if (err) { + dev_err(&priv->wmi_device->dev, + "Could not set up input device keymap: %d\n", err); + goto err_free_dev; + } + + err = input_register_device(input_dev); + if (err) { + dev_err(&priv->wmi_device->dev, + "Could not register input device: %d\n", err); + goto err_free_dev; + } + + priv->input_dev = input_dev; + return 0; + +err_free_dev: + input_free_device(input_dev); + return err; +} + +static void ideapad_wmi_input_exit(struct ideapad_wmi_private *priv) +{ + input_unregister_device(priv->input_dev); + priv->input_dev = NULL; +} + +static void ideapad_wmi_input_report(struct ideapad_wmi_private *priv, + unsigned int scancode) +{ + sparse_keymap_report_event(priv->input_dev, scancode, 1, true); +} + +static int ideapad_wmi_probe(struct wmi_device *wdev, const void *ctx) +{ + struct ideapad_wmi_private *priv; + int err; + + priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + dev_set_drvdata(&wdev->dev, priv); + + priv->wmi_device = wdev; + + err = ideapad_wmi_input_init(priv); + if (err) + return err; + + return 0; +} + +static void ideapad_wmi_remove(struct wmi_device *wdev) +{ + struct ideapad_wmi_private *priv = dev_get_drvdata(&wdev->dev); + + ideapad_wmi_input_exit(priv); +} + +static void ideapad_wmi_notify(struct wmi_device *wdev, union acpi_object *data) +{ + struct ideapad_wmi_private *priv = dev_get_drvdata(&wdev->dev); + + if(data->type != ACPI_TYPE_INTEGER) { + dev_warn(&priv->wmi_device->dev, + "WMI event data is not an integer\n"); + return; + } + + ideapad_wmi_input_report(priv, data->integer.value); +} + +static const struct wmi_device_id ideapad_wmi_id_table[] = { + { /* Special Keys on the Yoga 9 14IAP7 */ + .guid_string = IDEAPAD_FN_KEY_EVENT_GUID + }, + { } +}; + +static struct wmi_driver ideapad_wmi_driver = { + .driver = { + .name = "ideapad-wmi-fn-keys", + }, + .id_table = ideapad_wmi_id_table, + .probe = ideapad_wmi_probe, + .remove = ideapad_wmi_remove, + .notify = ideapad_wmi_notify, +}; + +module_wmi_driver(ideapad_wmi_driver); + +MODULE_DEVICE_TABLE(wmi, ideapad_wmi_id_table); +MODULE_AUTHOR("Philipp Jungkamp "); +MODULE_DESCRIPTION("Ideapad WMI fn keys driver"); +MODULE_LICENSE("GPL"); -- 2.37.1