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

huawei-wmi.c (20908B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 *  Huawei WMI laptop extras driver
      4 *
      5 *  Copyright (C) 2018	      Ayman Bagabas <ayman.bagabas@gmail.com>
      6 */
      7
      8#include <linux/acpi.h>
      9#include <linux/debugfs.h>
     10#include <linux/delay.h>
     11#include <linux/dmi.h>
     12#include <linux/input.h>
     13#include <linux/input/sparse-keymap.h>
     14#include <linux/leds.h>
     15#include <linux/module.h>
     16#include <linux/mutex.h>
     17#include <linux/platform_device.h>
     18#include <linux/power_supply.h>
     19#include <linux/sysfs.h>
     20#include <linux/wmi.h>
     21#include <acpi/battery.h>
     22
     23/*
     24 * Huawei WMI GUIDs
     25 */
     26#define HWMI_METHOD_GUID "ABBC0F5B-8EA1-11D1-A000-C90629100000"
     27#define HWMI_EVENT_GUID "ABBC0F5C-8EA1-11D1-A000-C90629100000"
     28
     29/* Legacy GUIDs */
     30#define WMI0_EXPENSIVE_GUID "39142400-C6A3-40fa-BADB-8A2652834100"
     31#define WMI0_EVENT_GUID "59142400-C6A3-40fa-BADB-8A2652834100"
     32
     33/* HWMI commands */
     34
     35enum {
     36	BATTERY_THRESH_GET		= 0x00001103, /* \GBTT */
     37	BATTERY_THRESH_SET		= 0x00001003, /* \SBTT */
     38	FN_LOCK_GET			= 0x00000604, /* \GFRS */
     39	FN_LOCK_SET			= 0x00000704, /* \SFRS */
     40	MICMUTE_LED_SET			= 0x00000b04, /* \SMLS */
     41};
     42
     43union hwmi_arg {
     44	u64 cmd;
     45	u8 args[8];
     46};
     47
     48struct quirk_entry {
     49	bool battery_reset;
     50	bool ec_micmute;
     51	bool report_brightness;
     52};
     53
     54static struct quirk_entry *quirks;
     55
     56struct huawei_wmi_debug {
     57	struct dentry *root;
     58	u64 arg;
     59};
     60
     61struct huawei_wmi {
     62	bool battery_available;
     63	bool fn_lock_available;
     64
     65	struct huawei_wmi_debug debug;
     66	struct input_dev *idev[2];
     67	struct led_classdev cdev;
     68	struct device *dev;
     69
     70	struct mutex wmi_lock;
     71};
     72
     73static struct huawei_wmi *huawei_wmi;
     74
     75static const struct key_entry huawei_wmi_keymap[] = {
     76	{ KE_KEY,    0x281, { KEY_BRIGHTNESSDOWN } },
     77	{ KE_KEY,    0x282, { KEY_BRIGHTNESSUP } },
     78	{ KE_KEY,    0x284, { KEY_MUTE } },
     79	{ KE_KEY,    0x285, { KEY_VOLUMEDOWN } },
     80	{ KE_KEY,    0x286, { KEY_VOLUMEUP } },
     81	{ KE_KEY,    0x287, { KEY_MICMUTE } },
     82	{ KE_KEY,    0x289, { KEY_WLAN } },
     83	// Huawei |M| key
     84	{ KE_KEY,    0x28a, { KEY_CONFIG } },
     85	// Keyboard backlit
     86	{ KE_IGNORE, 0x293, { KEY_KBDILLUMTOGGLE } },
     87	{ KE_IGNORE, 0x294, { KEY_KBDILLUMUP } },
     88	{ KE_IGNORE, 0x295, { KEY_KBDILLUMUP } },
     89	{ KE_END,	 0 }
     90};
     91
     92static int battery_reset = -1;
     93static int report_brightness = -1;
     94
     95module_param(battery_reset, bint, 0444);
     96MODULE_PARM_DESC(battery_reset,
     97		"Reset battery charge values to (0-0) before disabling it using (0-100)");
     98module_param(report_brightness, bint, 0444);
     99MODULE_PARM_DESC(report_brightness,
    100		"Report brightness keys.");
    101
    102/* Quirks */
    103
    104static int __init dmi_matched(const struct dmi_system_id *dmi)
    105{
    106	quirks = dmi->driver_data;
    107	return 1;
    108}
    109
    110static struct quirk_entry quirk_unknown = {
    111};
    112
    113static struct quirk_entry quirk_battery_reset = {
    114	.battery_reset = true,
    115};
    116
    117static struct quirk_entry quirk_matebook_x = {
    118	.ec_micmute = true,
    119	.report_brightness = true,
    120};
    121
    122static const struct dmi_system_id huawei_quirks[] = {
    123	{
    124		.callback = dmi_matched,
    125		.ident = "Huawei MACH-WX9",
    126		.matches = {
    127			DMI_MATCH(DMI_SYS_VENDOR, "HUAWEI"),
    128			DMI_MATCH(DMI_PRODUCT_NAME, "MACH-WX9"),
    129		},
    130		.driver_data = &quirk_battery_reset
    131	},
    132	{
    133		.callback = dmi_matched,
    134		.ident = "Huawei MateBook X",
    135		.matches = {
    136			DMI_MATCH(DMI_SYS_VENDOR, "HUAWEI"),
    137			DMI_MATCH(DMI_PRODUCT_NAME, "HUAWEI MateBook X")
    138		},
    139		.driver_data = &quirk_matebook_x
    140	},
    141	{  }
    142};
    143
    144/* Utils */
    145
    146static int huawei_wmi_call(struct huawei_wmi *huawei,
    147			   struct acpi_buffer *in, struct acpi_buffer *out)
    148{
    149	acpi_status status;
    150
    151	mutex_lock(&huawei->wmi_lock);
    152	status = wmi_evaluate_method(HWMI_METHOD_GUID, 0, 1, in, out);
    153	mutex_unlock(&huawei->wmi_lock);
    154	if (ACPI_FAILURE(status)) {
    155		dev_err(huawei->dev, "Failed to evaluate wmi method\n");
    156		return -ENODEV;
    157	}
    158
    159	return 0;
    160}
    161
    162/* HWMI takes a 64 bit input and returns either a package with 2 buffers, one of
    163 * 4 bytes and the other of 256 bytes, or one buffer of size 0x104 (260) bytes.
    164 * The first 4 bytes are ignored, we ignore the first 4 bytes buffer if we got a
    165 * package, or skip the first 4 if a buffer of 0x104 is used. The first byte of
    166 * the remaining 0x100 sized buffer has the return status of every call. In case
    167 * the return status is non-zero, we return -ENODEV but still copy the returned
    168 * buffer to the given buffer parameter (buf).
    169 */
    170static int huawei_wmi_cmd(u64 arg, u8 *buf, size_t buflen)
    171{
    172	struct huawei_wmi *huawei = huawei_wmi;
    173	struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
    174	struct acpi_buffer in;
    175	union acpi_object *obj;
    176	size_t len;
    177	int err, i;
    178
    179	in.length = sizeof(arg);
    180	in.pointer = &arg;
    181
    182	/* Some models require calling HWMI twice to execute a command. We evaluate
    183	 * HWMI and if we get a non-zero return status we evaluate it again.
    184	 */
    185	for (i = 0; i < 2; i++) {
    186		err = huawei_wmi_call(huawei, &in, &out);
    187		if (err)
    188			goto fail_cmd;
    189
    190		obj = out.pointer;
    191		if (!obj) {
    192			err = -EIO;
    193			goto fail_cmd;
    194		}
    195
    196		switch (obj->type) {
    197		/* Models that implement both "legacy" and HWMI tend to return a 0x104
    198		 * sized buffer instead of a package of 0x4 and 0x100 buffers.
    199		 */
    200		case ACPI_TYPE_BUFFER:
    201			if (obj->buffer.length == 0x104) {
    202				// Skip the first 4 bytes.
    203				obj->buffer.pointer += 4;
    204				len = 0x100;
    205			} else {
    206				dev_err(huawei->dev, "Bad buffer length, got %d\n", obj->buffer.length);
    207				err = -EIO;
    208				goto fail_cmd;
    209			}
    210
    211			break;
    212		/* HWMI returns a package with 2 buffer elements, one of 4 bytes and the
    213		 * other is 256 bytes.
    214		 */
    215		case ACPI_TYPE_PACKAGE:
    216			if (obj->package.count != 2) {
    217				dev_err(huawei->dev, "Bad package count, got %d\n", obj->package.count);
    218				err = -EIO;
    219				goto fail_cmd;
    220			}
    221
    222			obj = &obj->package.elements[1];
    223			if (obj->type != ACPI_TYPE_BUFFER) {
    224				dev_err(huawei->dev, "Bad package element type, got %d\n", obj->type);
    225				err = -EIO;
    226				goto fail_cmd;
    227			}
    228			len = obj->buffer.length;
    229
    230			break;
    231		/* Shouldn't get here! */
    232		default:
    233			dev_err(huawei->dev, "Unexpected obj type, got: %d\n", obj->type);
    234			err = -EIO;
    235			goto fail_cmd;
    236		}
    237
    238		if (!*obj->buffer.pointer)
    239			break;
    240	}
    241
    242	err = (*obj->buffer.pointer) ? -ENODEV : 0;
    243
    244	if (buf) {
    245		len = min(buflen, len);
    246		memcpy(buf, obj->buffer.pointer, len);
    247	}
    248
    249fail_cmd:
    250	kfree(out.pointer);
    251	return err;
    252}
    253
    254/* LEDs */
    255
    256static int huawei_wmi_micmute_led_set(struct led_classdev *led_cdev,
    257		enum led_brightness brightness)
    258{
    259	/* This is a workaround until the "legacy" interface is implemented. */
    260	if (quirks && quirks->ec_micmute) {
    261		char *acpi_method;
    262		acpi_handle handle;
    263		acpi_status status;
    264		union acpi_object args[3];
    265		struct acpi_object_list arg_list = {
    266			.pointer = args,
    267			.count = ARRAY_SIZE(args),
    268		};
    269
    270		handle = ec_get_handle();
    271		if (!handle)
    272			return -ENODEV;
    273
    274		args[0].type = args[1].type = args[2].type = ACPI_TYPE_INTEGER;
    275		args[1].integer.value = 0x04;
    276
    277		if (acpi_has_method(handle, "SPIN")) {
    278			acpi_method = "SPIN";
    279			args[0].integer.value = 0;
    280			args[2].integer.value = brightness ? 1 : 0;
    281		} else if (acpi_has_method(handle, "WPIN")) {
    282			acpi_method = "WPIN";
    283			args[0].integer.value = 1;
    284			args[2].integer.value = brightness ? 0 : 1;
    285		} else {
    286			return -ENODEV;
    287		}
    288
    289		status = acpi_evaluate_object(handle, acpi_method, &arg_list, NULL);
    290		if (ACPI_FAILURE(status))
    291			return -ENODEV;
    292
    293		return 0;
    294	} else {
    295		union hwmi_arg arg;
    296
    297		arg.cmd = MICMUTE_LED_SET;
    298		arg.args[2] = brightness;
    299
    300		return huawei_wmi_cmd(arg.cmd, NULL, 0);
    301	}
    302}
    303
    304static void huawei_wmi_leds_setup(struct device *dev)
    305{
    306	struct huawei_wmi *huawei = dev_get_drvdata(dev);
    307
    308	huawei->cdev.name = "platform::micmute";
    309	huawei->cdev.max_brightness = 1;
    310	huawei->cdev.brightness_set_blocking = &huawei_wmi_micmute_led_set;
    311	huawei->cdev.default_trigger = "audio-micmute";
    312	huawei->cdev.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE);
    313	huawei->cdev.dev = dev;
    314	huawei->cdev.flags = LED_CORE_SUSPENDRESUME;
    315
    316	devm_led_classdev_register(dev, &huawei->cdev);
    317}
    318
    319/* Battery protection */
    320
    321static int huawei_wmi_battery_get(int *start, int *end)
    322{
    323	u8 ret[0x100];
    324	int err, i;
    325
    326	err = huawei_wmi_cmd(BATTERY_THRESH_GET, ret, 0x100);
    327	if (err)
    328		return err;
    329
    330	/* Find the last two non-zero values. Return status is ignored. */
    331	i = 0xff;
    332	do {
    333		if (start)
    334			*start = ret[i-1];
    335		if (end)
    336			*end = ret[i];
    337	} while (i > 2 && !ret[i--]);
    338
    339	return 0;
    340}
    341
    342static int huawei_wmi_battery_set(int start, int end)
    343{
    344	union hwmi_arg arg;
    345	int err;
    346
    347	if (start < 0 || end < 0 || start > 100 || end > 100)
    348		return -EINVAL;
    349
    350	arg.cmd = BATTERY_THRESH_SET;
    351	arg.args[2] = start;
    352	arg.args[3] = end;
    353
    354	/* This is an edge case were some models turn battery protection
    355	 * off without changing their thresholds values. We clear the
    356	 * values before turning off protection. Sometimes we need a sleep delay to
    357	 * make sure these values make their way to EC memory.
    358	 */
    359	if (quirks && quirks->battery_reset && start == 0 && end == 100) {
    360		err = huawei_wmi_battery_set(0, 0);
    361		if (err)
    362			return err;
    363
    364		msleep(1000);
    365	}
    366
    367	err = huawei_wmi_cmd(arg.cmd, NULL, 0);
    368
    369	return err;
    370}
    371
    372static ssize_t charge_control_start_threshold_show(struct device *dev,
    373		struct device_attribute *attr,
    374		char *buf)
    375{
    376	int err, start;
    377
    378	err = huawei_wmi_battery_get(&start, NULL);
    379	if (err)
    380		return err;
    381
    382	return sprintf(buf, "%d\n", start);
    383}
    384
    385static ssize_t charge_control_end_threshold_show(struct device *dev,
    386		struct device_attribute *attr,
    387		char *buf)
    388{
    389	int err, end;
    390
    391	err = huawei_wmi_battery_get(NULL, &end);
    392	if (err)
    393		return err;
    394
    395	return sprintf(buf, "%d\n", end);
    396}
    397
    398static ssize_t charge_control_thresholds_show(struct device *dev,
    399		struct device_attribute *attr,
    400		char *buf)
    401{
    402	int err, start, end;
    403
    404	err = huawei_wmi_battery_get(&start, &end);
    405	if (err)
    406		return err;
    407
    408	return sprintf(buf, "%d %d\n", start, end);
    409}
    410
    411static ssize_t charge_control_start_threshold_store(struct device *dev,
    412		struct device_attribute *attr,
    413		const char *buf, size_t size)
    414{
    415	int err, start, end;
    416
    417	err = huawei_wmi_battery_get(NULL, &end);
    418	if (err)
    419		return err;
    420
    421	if (sscanf(buf, "%d", &start) != 1)
    422		return -EINVAL;
    423
    424	err = huawei_wmi_battery_set(start, end);
    425	if (err)
    426		return err;
    427
    428	return size;
    429}
    430
    431static ssize_t charge_control_end_threshold_store(struct device *dev,
    432		struct device_attribute *attr,
    433		const char *buf, size_t size)
    434{
    435	int err, start, end;
    436
    437	err = huawei_wmi_battery_get(&start, NULL);
    438	if (err)
    439		return err;
    440
    441	if (sscanf(buf, "%d", &end) != 1)
    442		return -EINVAL;
    443
    444	err = huawei_wmi_battery_set(start, end);
    445	if (err)
    446		return err;
    447
    448	return size;
    449}
    450
    451static ssize_t charge_control_thresholds_store(struct device *dev,
    452		struct device_attribute *attr,
    453		const char *buf, size_t size)
    454{
    455	int err, start, end;
    456
    457	if (sscanf(buf, "%d %d", &start, &end) != 2)
    458		return -EINVAL;
    459
    460	err = huawei_wmi_battery_set(start, end);
    461	if (err)
    462		return err;
    463
    464	return size;
    465}
    466
    467static DEVICE_ATTR_RW(charge_control_start_threshold);
    468static DEVICE_ATTR_RW(charge_control_end_threshold);
    469static DEVICE_ATTR_RW(charge_control_thresholds);
    470
    471static int huawei_wmi_battery_add(struct power_supply *battery)
    472{
    473	int err = 0;
    474
    475	err = device_create_file(&battery->dev, &dev_attr_charge_control_start_threshold);
    476	if (err)
    477		return err;
    478
    479	err = device_create_file(&battery->dev, &dev_attr_charge_control_end_threshold);
    480	if (err)
    481		device_remove_file(&battery->dev, &dev_attr_charge_control_start_threshold);
    482
    483	return err;
    484}
    485
    486static int huawei_wmi_battery_remove(struct power_supply *battery)
    487{
    488	device_remove_file(&battery->dev, &dev_attr_charge_control_start_threshold);
    489	device_remove_file(&battery->dev, &dev_attr_charge_control_end_threshold);
    490
    491	return 0;
    492}
    493
    494static struct acpi_battery_hook huawei_wmi_battery_hook = {
    495	.add_battery = huawei_wmi_battery_add,
    496	.remove_battery = huawei_wmi_battery_remove,
    497	.name = "Huawei Battery Extension"
    498};
    499
    500static void huawei_wmi_battery_setup(struct device *dev)
    501{
    502	struct huawei_wmi *huawei = dev_get_drvdata(dev);
    503
    504	huawei->battery_available = true;
    505	if (huawei_wmi_battery_get(NULL, NULL)) {
    506		huawei->battery_available = false;
    507		return;
    508	}
    509
    510	battery_hook_register(&huawei_wmi_battery_hook);
    511	device_create_file(dev, &dev_attr_charge_control_thresholds);
    512}
    513
    514static void huawei_wmi_battery_exit(struct device *dev)
    515{
    516	struct huawei_wmi *huawei = dev_get_drvdata(dev);
    517
    518	if (huawei->battery_available) {
    519		battery_hook_unregister(&huawei_wmi_battery_hook);
    520		device_remove_file(dev, &dev_attr_charge_control_thresholds);
    521	}
    522}
    523
    524/* Fn lock */
    525
    526static int huawei_wmi_fn_lock_get(int *on)
    527{
    528	u8 ret[0x100] = { 0 };
    529	int err, i;
    530
    531	err = huawei_wmi_cmd(FN_LOCK_GET, ret, 0x100);
    532	if (err)
    533		return err;
    534
    535	/* Find the first non-zero value. Return status is ignored. */
    536	i = 1;
    537	do {
    538		if (on)
    539			*on = ret[i] - 1; // -1 undefined, 0 off, 1 on.
    540	} while (i < 0xff && !ret[i++]);
    541
    542	return 0;
    543}
    544
    545static int huawei_wmi_fn_lock_set(int on)
    546{
    547	union hwmi_arg arg;
    548
    549	arg.cmd = FN_LOCK_SET;
    550	arg.args[2] = on + 1; // 0 undefined, 1 off, 2 on.
    551
    552	return huawei_wmi_cmd(arg.cmd, NULL, 0);
    553}
    554
    555static ssize_t fn_lock_state_show(struct device *dev,
    556		struct device_attribute *attr,
    557		char *buf)
    558{
    559	int err, on;
    560
    561	err = huawei_wmi_fn_lock_get(&on);
    562	if (err)
    563		return err;
    564
    565	return sprintf(buf, "%d\n", on);
    566}
    567
    568static ssize_t fn_lock_state_store(struct device *dev,
    569		struct device_attribute *attr,
    570		const char *buf, size_t size)
    571{
    572	int on, err;
    573
    574	if (kstrtoint(buf, 10, &on) ||
    575			on < 0 || on > 1)
    576		return -EINVAL;
    577
    578	err = huawei_wmi_fn_lock_set(on);
    579	if (err)
    580		return err;
    581
    582	return size;
    583}
    584
    585static DEVICE_ATTR_RW(fn_lock_state);
    586
    587static void huawei_wmi_fn_lock_setup(struct device *dev)
    588{
    589	struct huawei_wmi *huawei = dev_get_drvdata(dev);
    590
    591	huawei->fn_lock_available = true;
    592	if (huawei_wmi_fn_lock_get(NULL)) {
    593		huawei->fn_lock_available = false;
    594		return;
    595	}
    596
    597	device_create_file(dev, &dev_attr_fn_lock_state);
    598}
    599
    600static void huawei_wmi_fn_lock_exit(struct device *dev)
    601{
    602	struct huawei_wmi *huawei = dev_get_drvdata(dev);
    603
    604	if (huawei->fn_lock_available)
    605		device_remove_file(dev, &dev_attr_fn_lock_state);
    606}
    607
    608/* debugfs */
    609
    610static void huawei_wmi_debugfs_call_dump(struct seq_file *m, void *data,
    611		union acpi_object *obj)
    612{
    613	struct huawei_wmi *huawei = m->private;
    614	int i;
    615
    616	switch (obj->type) {
    617	case ACPI_TYPE_INTEGER:
    618		seq_printf(m, "0x%llx", obj->integer.value);
    619		break;
    620	case ACPI_TYPE_STRING:
    621		seq_printf(m, "\"%.*s\"", obj->string.length, obj->string.pointer);
    622		break;
    623	case ACPI_TYPE_BUFFER:
    624		seq_puts(m, "{");
    625		for (i = 0; i < obj->buffer.length; i++) {
    626			seq_printf(m, "0x%02x", obj->buffer.pointer[i]);
    627			if (i < obj->buffer.length - 1)
    628				seq_puts(m, ",");
    629		}
    630		seq_puts(m, "}");
    631		break;
    632	case ACPI_TYPE_PACKAGE:
    633		seq_puts(m, "[");
    634		for (i = 0; i < obj->package.count; i++) {
    635			huawei_wmi_debugfs_call_dump(m, huawei, &obj->package.elements[i]);
    636			if (i < obj->package.count - 1)
    637				seq_puts(m, ",");
    638		}
    639		seq_puts(m, "]");
    640		break;
    641	default:
    642		dev_err(huawei->dev, "Unexpected obj type, got %d\n", obj->type);
    643		return;
    644	}
    645}
    646
    647static int huawei_wmi_debugfs_call_show(struct seq_file *m, void *data)
    648{
    649	struct huawei_wmi *huawei = m->private;
    650	struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
    651	struct acpi_buffer in;
    652	union acpi_object *obj;
    653	int err;
    654
    655	in.length = sizeof(u64);
    656	in.pointer = &huawei->debug.arg;
    657
    658	err = huawei_wmi_call(huawei, &in, &out);
    659	if (err)
    660		return err;
    661
    662	obj = out.pointer;
    663	if (!obj) {
    664		err = -EIO;
    665		goto fail_debugfs_call;
    666	}
    667
    668	huawei_wmi_debugfs_call_dump(m, huawei, obj);
    669
    670fail_debugfs_call:
    671	kfree(out.pointer);
    672	return err;
    673}
    674
    675DEFINE_SHOW_ATTRIBUTE(huawei_wmi_debugfs_call);
    676
    677static void huawei_wmi_debugfs_setup(struct device *dev)
    678{
    679	struct huawei_wmi *huawei = dev_get_drvdata(dev);
    680
    681	huawei->debug.root = debugfs_create_dir("huawei-wmi", NULL);
    682
    683	debugfs_create_x64("arg", 0644, huawei->debug.root,
    684		&huawei->debug.arg);
    685	debugfs_create_file("call", 0400,
    686		huawei->debug.root, huawei, &huawei_wmi_debugfs_call_fops);
    687}
    688
    689static void huawei_wmi_debugfs_exit(struct device *dev)
    690{
    691	struct huawei_wmi *huawei = dev_get_drvdata(dev);
    692
    693	debugfs_remove_recursive(huawei->debug.root);
    694}
    695
    696/* Input */
    697
    698static void huawei_wmi_process_key(struct input_dev *idev, int code)
    699{
    700	const struct key_entry *key;
    701
    702	/*
    703	 * WMI0 uses code 0x80 to indicate a hotkey event.
    704	 * The actual key is fetched from the method WQ00
    705	 * using WMI0_EXPENSIVE_GUID.
    706	 */
    707	if (code == 0x80) {
    708		struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
    709		union acpi_object *obj;
    710		acpi_status status;
    711
    712		status = wmi_query_block(WMI0_EXPENSIVE_GUID, 0, &response);
    713		if (ACPI_FAILURE(status))
    714			return;
    715
    716		obj = (union acpi_object *)response.pointer;
    717		if (obj && obj->type == ACPI_TYPE_INTEGER)
    718			code = obj->integer.value;
    719
    720		kfree(response.pointer);
    721	}
    722
    723	key = sparse_keymap_entry_from_scancode(idev, code);
    724	if (!key) {
    725		dev_info(&idev->dev, "Unknown key pressed, code: 0x%04x\n", code);
    726		return;
    727	}
    728
    729	if (quirks && !quirks->report_brightness &&
    730			(key->sw.code == KEY_BRIGHTNESSDOWN ||
    731			key->sw.code == KEY_BRIGHTNESSUP))
    732		return;
    733
    734	sparse_keymap_report_entry(idev, key, 1, true);
    735}
    736
    737static void huawei_wmi_input_notify(u32 value, void *context)
    738{
    739	struct input_dev *idev = (struct input_dev *)context;
    740	struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
    741	union acpi_object *obj;
    742	acpi_status status;
    743
    744	status = wmi_get_event_data(value, &response);
    745	if (ACPI_FAILURE(status)) {
    746		dev_err(&idev->dev, "Unable to get event data\n");
    747		return;
    748	}
    749
    750	obj = (union acpi_object *)response.pointer;
    751	if (obj && obj->type == ACPI_TYPE_INTEGER)
    752		huawei_wmi_process_key(idev, obj->integer.value);
    753	else
    754		dev_err(&idev->dev, "Bad response type\n");
    755
    756	kfree(response.pointer);
    757}
    758
    759static int huawei_wmi_input_setup(struct device *dev,
    760		const char *guid,
    761		struct input_dev **idev)
    762{
    763	*idev = devm_input_allocate_device(dev);
    764	if (!*idev)
    765		return -ENOMEM;
    766
    767	(*idev)->name = "Huawei WMI hotkeys";
    768	(*idev)->phys = "wmi/input0";
    769	(*idev)->id.bustype = BUS_HOST;
    770	(*idev)->dev.parent = dev;
    771
    772	return sparse_keymap_setup(*idev, huawei_wmi_keymap, NULL) ||
    773		input_register_device(*idev) ||
    774		wmi_install_notify_handler(guid, huawei_wmi_input_notify,
    775				*idev);
    776}
    777
    778static void huawei_wmi_input_exit(struct device *dev, const char *guid)
    779{
    780	wmi_remove_notify_handler(guid);
    781}
    782
    783/* Huawei driver */
    784
    785static const struct wmi_device_id huawei_wmi_events_id_table[] = {
    786	{ .guid_string = WMI0_EVENT_GUID },
    787	{ .guid_string = HWMI_EVENT_GUID },
    788	{  }
    789};
    790
    791static int huawei_wmi_probe(struct platform_device *pdev)
    792{
    793	const struct wmi_device_id *guid = huawei_wmi_events_id_table;
    794	int err;
    795
    796	platform_set_drvdata(pdev, huawei_wmi);
    797	huawei_wmi->dev = &pdev->dev;
    798
    799	while (*guid->guid_string) {
    800		struct input_dev *idev = *huawei_wmi->idev;
    801
    802		if (wmi_has_guid(guid->guid_string)) {
    803			err = huawei_wmi_input_setup(&pdev->dev, guid->guid_string, &idev);
    804			if (err) {
    805				dev_err(&pdev->dev, "Failed to setup input on %s\n", guid->guid_string);
    806				return err;
    807			}
    808		}
    809
    810		idev++;
    811		guid++;
    812	}
    813
    814	if (wmi_has_guid(HWMI_METHOD_GUID)) {
    815		mutex_init(&huawei_wmi->wmi_lock);
    816
    817		huawei_wmi_leds_setup(&pdev->dev);
    818		huawei_wmi_fn_lock_setup(&pdev->dev);
    819		huawei_wmi_battery_setup(&pdev->dev);
    820		huawei_wmi_debugfs_setup(&pdev->dev);
    821	}
    822
    823	return 0;
    824}
    825
    826static int huawei_wmi_remove(struct platform_device *pdev)
    827{
    828	const struct wmi_device_id *guid = huawei_wmi_events_id_table;
    829
    830	while (*guid->guid_string) {
    831		if (wmi_has_guid(guid->guid_string))
    832			huawei_wmi_input_exit(&pdev->dev, guid->guid_string);
    833
    834		guid++;
    835	}
    836
    837	if (wmi_has_guid(HWMI_METHOD_GUID)) {
    838		huawei_wmi_debugfs_exit(&pdev->dev);
    839		huawei_wmi_battery_exit(&pdev->dev);
    840		huawei_wmi_fn_lock_exit(&pdev->dev);
    841	}
    842
    843	return 0;
    844}
    845
    846static struct platform_driver huawei_wmi_driver = {
    847	.driver = {
    848		.name = "huawei-wmi",
    849	},
    850	.probe = huawei_wmi_probe,
    851	.remove = huawei_wmi_remove,
    852};
    853
    854static __init int huawei_wmi_init(void)
    855{
    856	struct platform_device *pdev;
    857	int err;
    858
    859	huawei_wmi = kzalloc(sizeof(struct huawei_wmi), GFP_KERNEL);
    860	if (!huawei_wmi)
    861		return -ENOMEM;
    862
    863	quirks = &quirk_unknown;
    864	dmi_check_system(huawei_quirks);
    865	if (battery_reset != -1)
    866		quirks->battery_reset = battery_reset;
    867	if (report_brightness != -1)
    868		quirks->report_brightness = report_brightness;
    869
    870	err = platform_driver_register(&huawei_wmi_driver);
    871	if (err)
    872		goto pdrv_err;
    873
    874	pdev = platform_device_register_simple("huawei-wmi", -1, NULL, 0);
    875	if (IS_ERR(pdev)) {
    876		err = PTR_ERR(pdev);
    877		goto pdev_err;
    878	}
    879
    880	return 0;
    881
    882pdev_err:
    883	platform_driver_unregister(&huawei_wmi_driver);
    884pdrv_err:
    885	kfree(huawei_wmi);
    886	return err;
    887}
    888
    889static __exit void huawei_wmi_exit(void)
    890{
    891	struct platform_device *pdev = to_platform_device(huawei_wmi->dev);
    892
    893	platform_device_unregister(pdev);
    894	platform_driver_unregister(&huawei_wmi_driver);
    895
    896	kfree(huawei_wmi);
    897}
    898
    899module_init(huawei_wmi_init);
    900module_exit(huawei_wmi_exit);
    901
    902MODULE_ALIAS("wmi:"HWMI_METHOD_GUID);
    903MODULE_DEVICE_TABLE(wmi, huawei_wmi_events_id_table);
    904MODULE_AUTHOR("Ayman Bagabas <ayman.bagabas@gmail.com>");
    905MODULE_DESCRIPTION("Huawei WMI laptop extras driver");
    906MODULE_LICENSE("GPL v2");