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