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_CONFIG}}, 24 {KE_KEY, 0x00011b01, {KEY_CONFIG}}, 25 {KE_KEY, 0x00010101, {KEY_SWITCHVIDEOMODE}}, 26 {KE_KEY, 0x00001a01, {KEY_REFRESH_RATE_TOGGLE}}, 27 28 /* AI button has code for each position */ 29 {KE_KEY, 0x00011801, {KEY_ASSISTANT}}, 30 {KE_KEY, 0x00011901, {KEY_ASSISTANT}}, 31 32 /* Keyboard backlight */ 33 {KE_IGNORE, 0x00000501, {}}, 34 {KE_IGNORE, 0x00800501, {}}, 35 {KE_IGNORE, 0x00050501, {}}, 36 {KE_IGNORE, 0x000a0501, {}}, 37 38 /* Xiaomi G Command Center */ 39 {KE_KEY, 0x00010a01, {KEY_VENDOR}}, 40 41 /* OEM preset power mode */ 42 {KE_IGNORE, 0x00011601, {}}, 43 {KE_IGNORE, 0x00021601, {}}, 44 {KE_IGNORE, 0x00031601, {}}, 45 {KE_IGNORE, 0x00041601, {}}, 46 47 /* Fn Lock state */ 48 {KE_IGNORE, 0x00000701, {}}, 49 {KE_IGNORE, 0x00010701, {}}, 50 51 /* Fn+`/1/2/3/4 */ 52 {KE_KEY, 0x00011101, {KEY_F13}}, 53 {KE_KEY, 0x00011201, {KEY_F14}}, 54 {KE_KEY, 0x00011301, {KEY_F15}}, 55 {KE_KEY, 0x00011401, {KEY_F16}}, 56 {KE_KEY, 0x00011501, {KEY_F17}}, 57 58 {KE_END} 59 }; 60 61 struct redmi_wmi { 62 struct input_dev *input_dev; 63 /* Protects the key event sequence */ 64 struct mutex key_lock; 65 }; 66 67 static int redmi_wmi_probe(struct wmi_device *wdev, const void *context) 68 { 69 struct redmi_wmi *data; 70 int err; 71 72 /* Init dev */ 73 data = devm_kzalloc(&wdev->dev, sizeof(*data), GFP_KERNEL); 74 if (!data) 75 return -ENOMEM; 76 77 dev_set_drvdata(&wdev->dev, data); 78 79 err = devm_mutex_init(&wdev->dev, &data->key_lock); 80 if (err) 81 return err; 82 83 data->input_dev = devm_input_allocate_device(&wdev->dev); 84 if (!data->input_dev) 85 return -ENOMEM; 86 87 data->input_dev->name = "Redmibook WMI keys"; 88 data->input_dev->phys = "wmi/input0"; 89 90 err = sparse_keymap_setup(data->input_dev, redmi_wmi_keymap, NULL); 91 if (err) 92 return err; 93 94 return input_register_device(data->input_dev); 95 } 96 97 static void redmi_wmi_notify(struct wmi_device *wdev, union acpi_object *obj) 98 { 99 struct key_entry *entry; 100 struct redmi_wmi *data = dev_get_drvdata(&wdev->dev); 101 bool autorelease = true; 102 u32 payload; 103 int value = 1; 104 105 if (obj->type != ACPI_TYPE_BUFFER) { 106 dev_err(&wdev->dev, "Bad response type %u\n", obj->type); 107 return; 108 } 109 110 if (obj->buffer.length < 32) { 111 dev_err(&wdev->dev, "Invalid buffer length %u\n", obj->buffer.length); 112 return; 113 } 114 115 payload = get_unaligned_le32(obj->buffer.pointer); 116 entry = sparse_keymap_entry_from_scancode(data->input_dev, payload); 117 118 if (!entry) { 119 dev_dbg(&wdev->dev, "Unknown WMI event with payload %u", payload); 120 return; 121 } 122 123 /* AI key quirk */ 124 if (entry->keycode == KEY_ASSISTANT) { 125 value = !(payload & AI_KEY_VALUE_MASK); 126 autorelease = false; 127 } 128 129 guard(mutex)(&data->key_lock); 130 sparse_keymap_report_entry(data->input_dev, entry, value, autorelease); 131 } 132 133 static const struct wmi_device_id redmi_wmi_id_table[] = { 134 { WMI_REDMIBOOK_KEYBOARD_EVENT_GUID, NULL }, 135 { } 136 }; 137 138 static struct wmi_driver redmi_wmi_driver = { 139 .driver = { 140 .name = "redmi-wmi", 141 .probe_type = PROBE_PREFER_ASYNCHRONOUS, 142 }, 143 .id_table = redmi_wmi_id_table, 144 .probe = redmi_wmi_probe, 145 .notify = redmi_wmi_notify, 146 .no_singleton = true, 147 }; 148 module_wmi_driver(redmi_wmi_driver); 149 150 MODULE_DEVICE_TABLE(wmi, redmi_wmi_id_table); 151 MODULE_AUTHOR("Gladyshev Ilya <foxido@foxido.dev>"); 152 MODULE_DESCRIPTION("Redmibook WMI driver"); 153 MODULE_LICENSE("GPL"); 154