dell-wmi-aio.c (4588B)
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 17MODULE_DESCRIPTION("WMI hotkeys driver for Dell All-In-One series"); 18MODULE_LICENSE("GPL"); 19 20#define EVENT_GUID1 "284A0E6B-380E-472A-921F-E52786257FB4" 21#define EVENT_GUID2 "02314822-307C-4F66-BF0E-48AEAEB26CC8" 22 23struct 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 31static const char *dell_wmi_aio_guids[] = { 32 EVENT_GUID1, 33 EVENT_GUID2, 34 NULL 35}; 36 37MODULE_ALIAS("wmi:"EVENT_GUID1); 38MODULE_ALIAS("wmi:"EVENT_GUID2); 39 40static 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 53static 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 */ 59static 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 73static void dell_wmi_aio_notify(u32 value, void *context) 74{ 75 struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; 76 union acpi_object *obj; 77 struct dell_wmi_event *event; 78 acpi_status status; 79 80 status = wmi_get_event_data(value, &response); 81 if (status != AE_OK) { 82 pr_info("bad event status 0x%x\n", status); 83 return; 84 } 85 86 obj = (union acpi_object *)response.pointer; 87 if (obj) { 88 unsigned int scancode = 0; 89 90 switch (obj->type) { 91 case ACPI_TYPE_INTEGER: 92 /* Most All-In-One correctly return integer scancode */ 93 scancode = obj->integer.value; 94 sparse_keymap_report_event(dell_wmi_aio_input_dev, 95 scancode, 1, true); 96 break; 97 case ACPI_TYPE_BUFFER: 98 if (dell_wmi_aio_event_check(obj->buffer.pointer, 99 obj->buffer.length)) { 100 event = (struct dell_wmi_event *) 101 obj->buffer.pointer; 102 scancode = event->event[0]; 103 } else { 104 /* Broken machines return the scancode in a 105 buffer */ 106 if (obj->buffer.pointer && 107 obj->buffer.length > 0) 108 scancode = obj->buffer.pointer[0]; 109 } 110 if (scancode) 111 sparse_keymap_report_event( 112 dell_wmi_aio_input_dev, 113 scancode, 1, true); 114 break; 115 } 116 } 117 kfree(obj); 118} 119 120static int __init dell_wmi_aio_input_setup(void) 121{ 122 int err; 123 124 dell_wmi_aio_input_dev = input_allocate_device(); 125 126 if (!dell_wmi_aio_input_dev) 127 return -ENOMEM; 128 129 dell_wmi_aio_input_dev->name = "Dell AIO WMI hotkeys"; 130 dell_wmi_aio_input_dev->phys = "wmi/input0"; 131 dell_wmi_aio_input_dev->id.bustype = BUS_HOST; 132 133 err = sparse_keymap_setup(dell_wmi_aio_input_dev, 134 dell_wmi_aio_keymap, NULL); 135 if (err) { 136 pr_err("Unable to setup input device keymap\n"); 137 goto err_free_dev; 138 } 139 err = input_register_device(dell_wmi_aio_input_dev); 140 if (err) { 141 pr_info("Unable to register input device\n"); 142 goto err_free_dev; 143 } 144 return 0; 145 146err_free_dev: 147 input_free_device(dell_wmi_aio_input_dev); 148 return err; 149} 150 151static const char *dell_wmi_aio_find(void) 152{ 153 int i; 154 155 for (i = 0; dell_wmi_aio_guids[i] != NULL; i++) 156 if (wmi_has_guid(dell_wmi_aio_guids[i])) 157 return dell_wmi_aio_guids[i]; 158 159 return NULL; 160} 161 162static int __init dell_wmi_aio_init(void) 163{ 164 int err; 165 const char *guid; 166 167 guid = dell_wmi_aio_find(); 168 if (!guid) { 169 pr_warn("No known WMI GUID found\n"); 170 return -ENXIO; 171 } 172 173 err = dell_wmi_aio_input_setup(); 174 if (err) 175 return err; 176 177 err = wmi_install_notify_handler(guid, dell_wmi_aio_notify, NULL); 178 if (err) { 179 pr_err("Unable to register notify handler - %d\n", err); 180 input_unregister_device(dell_wmi_aio_input_dev); 181 return err; 182 } 183 184 return 0; 185} 186 187static void __exit dell_wmi_aio_exit(void) 188{ 189 const char *guid; 190 191 guid = dell_wmi_aio_find(); 192 wmi_remove_notify_handler(guid); 193 input_unregister_device(dell_wmi_aio_input_dev); 194} 195 196module_init(dell_wmi_aio_init); 197module_exit(dell_wmi_aio_exit);