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 20 static 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 38 struct redmi_wmi { 39 struct input_dev *input_dev; 40 /* Protects the key event sequence */ 41 struct mutex key_lock; 42 }; 43 44 static 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 74 static 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 110 static const struct wmi_device_id redmi_wmi_id_table[] = { 111 { WMI_REDMIBOOK_KEYBOARD_EVENT_GUID, NULL }, 112 { } 113 }; 114 115 static 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 }; 125 module_wmi_driver(redmi_wmi_driver); 126 127 MODULE_DEVICE_TABLE(wmi, redmi_wmi_id_table); 128 MODULE_AUTHOR("Gladyshev Ilya <foxido@foxido.dev>"); 129 MODULE_DESCRIPTION("Redmibook WMI driver"); 130 MODULE_LICENSE("GPL"); 131