1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * WMI hotkeys support for Dell All-In-One series 4 */ 5 6 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 7 8 #include <linux/kernel.h> 9 #include <linux/module.h> 10 #include <linux/init.h> 11 #include <linux/types.h> 12 #include <linux/input.h> 13 #include <linux/input/sparse-keymap.h> 14 #include <linux/acpi.h> 15 #include <linux/string.h> 16 17 MODULE_DESCRIPTION("WMI hotkeys driver for Dell All-In-One series"); 18 MODULE_LICENSE("GPL"); 19 20 #define EVENT_GUID1 "284A0E6B-380E-472A-921F-E52786257FB4" 21 #define EVENT_GUID2 "02314822-307C-4F66-BF0E-48AEAEB26CC8" 22 23 struct dell_wmi_event { 24 u16 length; 25 /* 0x000: A hot key pressed or an event occurred 26 * 0x00F: A sequence of hot keys are pressed */ 27 u16 type; 28 u16 event[]; 29 }; 30 31 static const char *dell_wmi_aio_guids[] = { 32 EVENT_GUID1, 33 EVENT_GUID2, 34 NULL 35 }; 36 37 MODULE_ALIAS("wmi:"EVENT_GUID1); 38 MODULE_ALIAS("wmi:"EVENT_GUID2); 39 40 static const struct key_entry dell_wmi_aio_keymap[] = { 41 { KE_KEY, 0xc0, { KEY_VOLUMEUP } }, 42 { KE_KEY, 0xc1, { KEY_VOLUMEDOWN } }, 43 { KE_KEY, 0xe030, { KEY_VOLUMEUP } }, 44 { KE_KEY, 0xe02e, { KEY_VOLUMEDOWN } }, 45 { KE_KEY, 0xe020, { KEY_MUTE } }, 46 { KE_KEY, 0xe027, { KEY_DISPLAYTOGGLE } }, 47 { KE_KEY, 0xe006, { KEY_BRIGHTNESSUP } }, 48 { KE_KEY, 0xe005, { KEY_BRIGHTNESSDOWN } }, 49 { KE_KEY, 0xe00b, { KEY_SWITCHVIDEOMODE } }, 50 { KE_END, 0 } 51 }; 52 53 static struct input_dev *dell_wmi_aio_input_dev; 54 55 /* 56 * The new WMI event data format will follow the dell_wmi_event structure 57 * So, we will check if the buffer matches the format 58 */ 59 static bool dell_wmi_aio_event_check(u8 *buffer, int length) 60 { 61 struct dell_wmi_event *event = (struct dell_wmi_event *)buffer; 62 63 if (event == NULL || length < 6) 64 return false; 65 66 if ((event->type == 0 || event->type == 0xf) && 67 event->length >= 2) 68 return true; 69 70 return false; 71 } 72 73 static void dell_wmi_aio_notify(union acpi_object *obj, void *context) 74 { 75 struct dell_wmi_event *event; 76 77 if (obj) { 78 unsigned int scancode = 0; 79 80 switch (obj->type) { 81 case ACPI_TYPE_INTEGER: 82 /* Most All-In-One correctly return integer scancode */ 83 scancode = obj->integer.value; 84 sparse_keymap_report_event(dell_wmi_aio_input_dev, 85 scancode, 1, true); 86 break; 87 case ACPI_TYPE_BUFFER: 88 if (dell_wmi_aio_event_check(obj->buffer.pointer, 89 obj->buffer.length)) { 90 event = (struct dell_wmi_event *) 91 obj->buffer.pointer; 92 scancode = event->event[0]; 93 } else { 94 /* Broken machines return the scancode in a 95 buffer */ 96 if (obj->buffer.pointer && 97 obj->buffer.length > 0) 98 scancode = obj->buffer.pointer[0]; 99 } 100 if (scancode) 101 sparse_keymap_report_event( 102 dell_wmi_aio_input_dev, 103 scancode, 1, true); 104 break; 105 } 106 } 107 } 108 109 static int __init dell_wmi_aio_input_setup(void) 110 { 111 int err; 112 113 dell_wmi_aio_input_dev = input_allocate_device(); 114 115 if (!dell_wmi_aio_input_dev) 116 return -ENOMEM; 117 118 dell_wmi_aio_input_dev->name = "Dell AIO WMI hotkeys"; 119 dell_wmi_aio_input_dev->phys = "wmi/input0"; 120 dell_wmi_aio_input_dev->id.bustype = BUS_HOST; 121 122 err = sparse_keymap_setup(dell_wmi_aio_input_dev, 123 dell_wmi_aio_keymap, NULL); 124 if (err) { 125 pr_err("Unable to setup input device keymap\n"); 126 goto err_free_dev; 127 } 128 err = input_register_device(dell_wmi_aio_input_dev); 129 if (err) { 130 pr_info("Unable to register input device\n"); 131 goto err_free_dev; 132 } 133 return 0; 134 135 err_free_dev: 136 input_free_device(dell_wmi_aio_input_dev); 137 return err; 138 } 139 140 static const char *dell_wmi_aio_find(void) 141 { 142 int i; 143 144 for (i = 0; dell_wmi_aio_guids[i] != NULL; i++) 145 if (wmi_has_guid(dell_wmi_aio_guids[i])) 146 return dell_wmi_aio_guids[i]; 147 148 return NULL; 149 } 150 151 static int __init dell_wmi_aio_init(void) 152 { 153 int err; 154 const char *guid; 155 156 guid = dell_wmi_aio_find(); 157 if (!guid) { 158 pr_warn("No known WMI GUID found\n"); 159 return -ENXIO; 160 } 161 162 err = dell_wmi_aio_input_setup(); 163 if (err) 164 return err; 165 166 err = wmi_install_notify_handler(guid, dell_wmi_aio_notify, NULL); 167 if (err) { 168 pr_err("Unable to register notify handler - %d\n", err); 169 input_unregister_device(dell_wmi_aio_input_dev); 170 return err; 171 } 172 173 return 0; 174 } 175 176 static void __exit dell_wmi_aio_exit(void) 177 { 178 const char *guid; 179 180 guid = dell_wmi_aio_find(); 181 wmi_remove_notify_handler(guid); 182 input_unregister_device(dell_wmi_aio_input_dev); 183 } 184 185 module_init(dell_wmi_aio_init); 186 module_exit(dell_wmi_aio_exit); 187