File Index
Files and directories
Sign in
FILE

redmi-wmi.c

File preview with syntax highlighting.

linux-6.19.14/drivers/platform/x86/redmi-wmi.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/* WMI driver for Xiaomi Redmibooks */
  3
  4#include <linux/acpi.h>
  5#include <linux/bits.h>
  6#include <linux/device.h>
  7#include <linux/input.h>
  8#include <linux/input/sparse-keymap.h>
  9#include <linux/module.h>
 10#include <linux/mutex.h>
 11#include <linux/unaligned.h>
 12#include <linux/wmi.h>
 13
 14#include <uapi/linux/input-event-codes.h>
 15
 16#define WMI_REDMIBOOK_KEYBOARD_EVENT_GUID "46C93E13-EE9B-4262-8488-563BCA757FEF"
 17
 18#define AI_KEY_VALUE_MASK BIT(8)
 19
 20static const struct key_entry redmi_wmi_keymap[] = {
 21	{KE_KEY, 0x00000201,	{KEY_SELECTIVE_SCREENSHOT}},
 22	{KE_KEY, 0x00000301,	{KEY_ALL_APPLICATIONS}},
 23	{KE_KEY, 0x00001b01,	{KEY_SETUP}},
 24
 25	/* AI button has code for each position */
 26	{KE_KEY, 0x00011801,	{KEY_ASSISTANT}},
 27	{KE_KEY, 0x00011901,	{KEY_ASSISTANT}},
 28
 29	/* Keyboard backlight */
 30	{KE_IGNORE, 0x00000501, {}},
 31	{KE_IGNORE, 0x00800501, {}},
 32	{KE_IGNORE, 0x00050501, {}},
 33	{KE_IGNORE, 0x000a0501, {}},
 34
 35	{KE_END}
 36};
 37
 38struct redmi_wmi {
 39	struct input_dev *input_dev;
 40	/* Protects the key event sequence */
 41	struct mutex key_lock;
 42};
 43
 44static int redmi_wmi_probe(struct wmi_device *wdev, const void *context)
 45{
 46	struct redmi_wmi *data;
 47	int err;
 48
 49	/* Init dev */
 50	data = devm_kzalloc(&wdev->dev, sizeof(*data), GFP_KERNEL);
 51	if (!data)
 52		return -ENOMEM;
 53
 54	dev_set_drvdata(&wdev->dev, data);
 55
 56	err = devm_mutex_init(&wdev->dev, &data->key_lock);
 57	if (err)
 58		return err;
 59
 60	data->input_dev = devm_input_allocate_device(&wdev->dev);
 61	if (!data->input_dev)
 62		return -ENOMEM;
 63
 64	data->input_dev->name = "Redmibook WMI keys";
 65	data->input_dev->phys = "wmi/input0";
 66
 67	err = sparse_keymap_setup(data->input_dev, redmi_wmi_keymap, NULL);
 68	if (err)
 69		return err;
 70
 71	return input_register_device(data->input_dev);
 72}
 73
 74static void redmi_wmi_notify(struct wmi_device *wdev, union acpi_object *obj)
 75{
 76	struct key_entry *entry;
 77	struct redmi_wmi *data = dev_get_drvdata(&wdev->dev);
 78	bool autorelease = true;
 79	u32 payload;
 80	int value = 1;
 81
 82	if (obj->type != ACPI_TYPE_BUFFER) {
 83		dev_err(&wdev->dev, "Bad response type %u\n", obj->type);
 84		return;
 85	}
 86
 87	if (obj->buffer.length < 32) {
 88		dev_err(&wdev->dev, "Invalid buffer length %u\n", obj->buffer.length);
 89		return;
 90	}
 91
 92	payload = get_unaligned_le32(obj->buffer.pointer);
 93	entry = sparse_keymap_entry_from_scancode(data->input_dev, payload);
 94
 95	if (!entry) {
 96		dev_dbg(&wdev->dev, "Unknown WMI event with payload %u", payload);
 97		return;
 98	}
 99
100	/* AI key quirk */
101	if (entry->keycode == KEY_ASSISTANT) {
102		value = !(payload & AI_KEY_VALUE_MASK);
103		autorelease = false;
104	}
105
106	guard(mutex)(&data->key_lock);
107	sparse_keymap_report_entry(data->input_dev, entry, value, autorelease);
108}
109
110static const struct wmi_device_id redmi_wmi_id_table[] = {
111	{ WMI_REDMIBOOK_KEYBOARD_EVENT_GUID, NULL },
112	{ }
113};
114
115static struct wmi_driver redmi_wmi_driver = {
116	.driver = {
117		.name = "redmi-wmi",
118		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
119	},
120	.id_table = redmi_wmi_id_table,
121	.probe = redmi_wmi_probe,
122	.notify = redmi_wmi_notify,
123	.no_singleton = true,
124};
125module_wmi_driver(redmi_wmi_driver);
126
127MODULE_DEVICE_TABLE(wmi, redmi_wmi_id_table);
128MODULE_AUTHOR("Gladyshev Ilya <foxido@foxido.dev>");
129MODULE_DESCRIPTION("Redmibook WMI driver");
130MODULE_LICENSE("GPL");
~/ Read-only file preview.