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