Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  *  WMI hotkeys support for Dell All-In-One series
0004  */
0005 
0006 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0007 
0008 #include <linux/kernel.h>
0009 #include <linux/module.h>
0010 #include <linux/init.h>
0011 #include <linux/types.h>
0012 #include <linux/input.h>
0013 #include <linux/input/sparse-keymap.h>
0014 #include <linux/acpi.h>
0015 #include <linux/string.h>
0016 
0017 MODULE_DESCRIPTION("WMI hotkeys driver for Dell All-In-One series");
0018 MODULE_LICENSE("GPL");
0019 
0020 #define EVENT_GUID1 "284A0E6B-380E-472A-921F-E52786257FB4"
0021 #define EVENT_GUID2 "02314822-307C-4F66-BF0E-48AEAEB26CC8"
0022 
0023 struct dell_wmi_event {
0024     u16 length;
0025     /* 0x000: A hot key pressed or an event occurred
0026      * 0x00F: A sequence of hot keys are pressed */
0027     u16 type;
0028     u16 event[];
0029 };
0030 
0031 static const char *dell_wmi_aio_guids[] = {
0032     EVENT_GUID1,
0033     EVENT_GUID2,
0034     NULL
0035 };
0036 
0037 MODULE_ALIAS("wmi:"EVENT_GUID1);
0038 MODULE_ALIAS("wmi:"EVENT_GUID2);
0039 
0040 static const struct key_entry dell_wmi_aio_keymap[] = {
0041     { KE_KEY, 0xc0, { KEY_VOLUMEUP } },
0042     { KE_KEY, 0xc1, { KEY_VOLUMEDOWN } },
0043     { KE_KEY, 0xe030, { KEY_VOLUMEUP } },
0044     { KE_KEY, 0xe02e, { KEY_VOLUMEDOWN } },
0045     { KE_KEY, 0xe020, { KEY_MUTE } },
0046     { KE_KEY, 0xe027, { KEY_DISPLAYTOGGLE } },
0047     { KE_KEY, 0xe006, { KEY_BRIGHTNESSUP } },
0048     { KE_KEY, 0xe005, { KEY_BRIGHTNESSDOWN } },
0049     { KE_KEY, 0xe00b, { KEY_SWITCHVIDEOMODE } },
0050     { KE_END, 0 }
0051 };
0052 
0053 static struct input_dev *dell_wmi_aio_input_dev;
0054 
0055 /*
0056  * The new WMI event data format will follow the dell_wmi_event structure
0057  * So, we will check if the buffer matches the format
0058  */
0059 static bool dell_wmi_aio_event_check(u8 *buffer, int length)
0060 {
0061     struct dell_wmi_event *event = (struct dell_wmi_event *)buffer;
0062 
0063     if (event == NULL || length < 6)
0064         return false;
0065 
0066     if ((event->type == 0 || event->type == 0xf) &&
0067             event->length >= 2)
0068         return true;
0069 
0070     return false;
0071 }
0072 
0073 static void dell_wmi_aio_notify(u32 value, void *context)
0074 {
0075     struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
0076     union acpi_object *obj;
0077     struct dell_wmi_event *event;
0078     acpi_status status;
0079 
0080     status = wmi_get_event_data(value, &response);
0081     if (status != AE_OK) {
0082         pr_info("bad event status 0x%x\n", status);
0083         return;
0084     }
0085 
0086     obj = (union acpi_object *)response.pointer;
0087     if (obj) {
0088         unsigned int scancode = 0;
0089 
0090         switch (obj->type) {
0091         case ACPI_TYPE_INTEGER:
0092             /* Most All-In-One correctly return integer scancode */
0093             scancode = obj->integer.value;
0094             sparse_keymap_report_event(dell_wmi_aio_input_dev,
0095                 scancode, 1, true);
0096             break;
0097         case ACPI_TYPE_BUFFER:
0098             if (dell_wmi_aio_event_check(obj->buffer.pointer,
0099                         obj->buffer.length)) {
0100                 event = (struct dell_wmi_event *)
0101                     obj->buffer.pointer;
0102                 scancode = event->event[0];
0103             } else {
0104                 /* Broken machines return the scancode in a
0105                    buffer */
0106                 if (obj->buffer.pointer &&
0107                         obj->buffer.length > 0)
0108                     scancode = obj->buffer.pointer[0];
0109             }
0110             if (scancode)
0111                 sparse_keymap_report_event(
0112                     dell_wmi_aio_input_dev,
0113                     scancode, 1, true);
0114             break;
0115         }
0116     }
0117     kfree(obj);
0118 }
0119 
0120 static int __init dell_wmi_aio_input_setup(void)
0121 {
0122     int err;
0123 
0124     dell_wmi_aio_input_dev = input_allocate_device();
0125 
0126     if (!dell_wmi_aio_input_dev)
0127         return -ENOMEM;
0128 
0129     dell_wmi_aio_input_dev->name = "Dell AIO WMI hotkeys";
0130     dell_wmi_aio_input_dev->phys = "wmi/input0";
0131     dell_wmi_aio_input_dev->id.bustype = BUS_HOST;
0132 
0133     err = sparse_keymap_setup(dell_wmi_aio_input_dev,
0134             dell_wmi_aio_keymap, NULL);
0135     if (err) {
0136         pr_err("Unable to setup input device keymap\n");
0137         goto err_free_dev;
0138     }
0139     err = input_register_device(dell_wmi_aio_input_dev);
0140     if (err) {
0141         pr_info("Unable to register input device\n");
0142         goto err_free_dev;
0143     }
0144     return 0;
0145 
0146 err_free_dev:
0147     input_free_device(dell_wmi_aio_input_dev);
0148     return err;
0149 }
0150 
0151 static const char *dell_wmi_aio_find(void)
0152 {
0153     int i;
0154 
0155     for (i = 0; dell_wmi_aio_guids[i] != NULL; i++)
0156         if (wmi_has_guid(dell_wmi_aio_guids[i]))
0157             return dell_wmi_aio_guids[i];
0158 
0159     return NULL;
0160 }
0161 
0162 static int __init dell_wmi_aio_init(void)
0163 {
0164     int err;
0165     const char *guid;
0166 
0167     guid = dell_wmi_aio_find();
0168     if (!guid) {
0169         pr_warn("No known WMI GUID found\n");
0170         return -ENXIO;
0171     }
0172 
0173     err = dell_wmi_aio_input_setup();
0174     if (err)
0175         return err;
0176 
0177     err = wmi_install_notify_handler(guid, dell_wmi_aio_notify, NULL);
0178     if (err) {
0179         pr_err("Unable to register notify handler - %d\n", err);
0180         input_unregister_device(dell_wmi_aio_input_dev);
0181         return err;
0182     }
0183 
0184     return 0;
0185 }
0186 
0187 static void __exit dell_wmi_aio_exit(void)
0188 {
0189     const char *guid;
0190 
0191     guid = dell_wmi_aio_find();
0192     wmi_remove_notify_handler(guid);
0193     input_unregister_device(dell_wmi_aio_input_dev);
0194 }
0195 
0196 module_init(dell_wmi_aio_init);
0197 module_exit(dell_wmi_aio_exit);