cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

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);