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