1 // SPDX-License-Identifier: GPL-2.0
3 * Huawei WMI laptop extras driver
5 * Copyright (C) 2018 Ayman Bagabas <ayman.bagabas@gmail.com>
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>
26 #define HWMI_METHOD_GUID "ABBC0F5B-8EA1-11D1-A000-C90629100000"
27 #define HWMI_EVENT_GUID "ABBC0F5C-8EA1-11D1-A000-C90629100000"
30 #define WMI0_EXPENSIVE_GUID "39142400-C6A3-40fa-BADB-8A2652834100"
31 #define WMI0_EVENT_GUID "59142400-C6A3-40fa-BADB-8A2652834100"
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 */
51 bool report_brightness
;
54 static struct quirk_entry
*quirks
;
56 struct huawei_wmi_debug
{
62 bool battery_available
;
63 bool fn_lock_available
;
65 struct huawei_wmi_debug debug
;
66 struct led_classdev cdev
;
69 struct mutex wmi_lock
;
72 static struct huawei_wmi
*huawei_wmi
;
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
} },
83 { KE_KEY
, 0x28a, { KEY_CONFIG
} },
85 { KE_IGNORE
, 0x293, { KEY_KBDILLUMTOGGLE
} },
86 { KE_IGNORE
, 0x294, { KEY_KBDILLUMUP
} },
87 { KE_IGNORE
, 0x295, { KEY_KBDILLUMUP
} },
88 // Ignore Ambient Light Sensoring
89 { KE_KEY
, 0x2c1, { KEY_RESERVED
} },
93 static int battery_reset
= -1;
94 static int report_brightness
= -1;
96 module_param(battery_reset
, bint
, 0444);
97 MODULE_PARM_DESC(battery_reset
,
98 "Reset battery charge values to (0-0) before disabling it using (0-100)");
99 module_param(report_brightness
, bint
, 0444);
100 MODULE_PARM_DESC(report_brightness
,
101 "Report brightness keys.");
105 static int __init
dmi_matched(const struct dmi_system_id
*dmi
)
107 quirks
= dmi
->driver_data
;
111 static struct quirk_entry quirk_unknown
= {
114 static struct quirk_entry quirk_battery_reset
= {
115 .battery_reset
= true,
118 static struct quirk_entry quirk_matebook_x
= {
120 .report_brightness
= true,
123 static const struct dmi_system_id huawei_quirks
[] = {
125 .callback
= dmi_matched
,
126 .ident
= "Huawei MACH-WX9",
128 DMI_MATCH(DMI_SYS_VENDOR
, "HUAWEI"),
129 DMI_MATCH(DMI_PRODUCT_NAME
, "MACH-WX9"),
131 .driver_data
= &quirk_battery_reset
134 .callback
= dmi_matched
,
135 .ident
= "Huawei MateBook X",
137 DMI_MATCH(DMI_SYS_VENDOR
, "HUAWEI"),
138 DMI_MATCH(DMI_PRODUCT_NAME
, "HUAWEI MateBook X")
140 .driver_data
= &quirk_matebook_x
147 static int huawei_wmi_call(struct huawei_wmi
*huawei
,
148 struct acpi_buffer
*in
, struct acpi_buffer
*out
)
152 mutex_lock(&huawei
->wmi_lock
);
153 status
= wmi_evaluate_method(HWMI_METHOD_GUID
, 0, 1, in
, out
);
154 mutex_unlock(&huawei
->wmi_lock
);
155 if (ACPI_FAILURE(status
)) {
156 dev_err(huawei
->dev
, "Failed to evaluate wmi method\n");
163 /* HWMI takes a 64 bit input and returns either a package with 2 buffers, one of
164 * 4 bytes and the other of 256 bytes, or one buffer of size 0x104 (260) bytes.
165 * The first 4 bytes are ignored, we ignore the first 4 bytes buffer if we got a
166 * package, or skip the first 4 if a buffer of 0x104 is used. The first byte of
167 * the remaining 0x100 sized buffer has the return status of every call. In case
168 * the return status is non-zero, we return -ENODEV but still copy the returned
169 * buffer to the given buffer parameter (buf).
171 static int huawei_wmi_cmd(u64 arg
, u8
*buf
, size_t buflen
)
173 struct huawei_wmi
*huawei
= huawei_wmi
;
174 struct acpi_buffer out
= { ACPI_ALLOCATE_BUFFER
, NULL
};
175 struct acpi_buffer in
;
176 union acpi_object
*obj
;
180 in
.length
= sizeof(arg
);
183 /* Some models require calling HWMI twice to execute a command. We evaluate
184 * HWMI and if we get a non-zero return status we evaluate it again.
186 for (i
= 0; i
< 2; i
++) {
187 err
= huawei_wmi_call(huawei
, &in
, &out
);
198 /* Models that implement both "legacy" and HWMI tend to return a 0x104
199 * sized buffer instead of a package of 0x4 and 0x100 buffers.
201 case ACPI_TYPE_BUFFER
:
202 if (obj
->buffer
.length
== 0x104) {
203 // Skip the first 4 bytes.
204 obj
->buffer
.pointer
+= 4;
207 dev_err(huawei
->dev
, "Bad buffer length, got %d\n", obj
->buffer
.length
);
213 /* HWMI returns a package with 2 buffer elements, one of 4 bytes and the
214 * other is 256 bytes.
216 case ACPI_TYPE_PACKAGE
:
217 if (obj
->package
.count
!= 2) {
218 dev_err(huawei
->dev
, "Bad package count, got %d\n", obj
->package
.count
);
223 obj
= &obj
->package
.elements
[1];
224 if (obj
->type
!= ACPI_TYPE_BUFFER
) {
225 dev_err(huawei
->dev
, "Bad package element type, got %d\n", obj
->type
);
229 len
= obj
->buffer
.length
;
232 /* Shouldn't get here! */
234 dev_err(huawei
->dev
, "Unexpected obj type, got: %d\n", obj
->type
);
239 if (!*obj
->buffer
.pointer
)
243 err
= (*obj
->buffer
.pointer
) ? -ENODEV
: 0;
246 len
= min(buflen
, len
);
247 memcpy(buf
, obj
->buffer
.pointer
, len
);
257 static int huawei_wmi_micmute_led_set(struct led_classdev
*led_cdev
,
258 enum led_brightness brightness
)
260 /* This is a workaround until the "legacy" interface is implemented. */
261 if (quirks
&& quirks
->ec_micmute
) {
265 union acpi_object args
[3];
266 struct acpi_object_list arg_list
= {
268 .count
= ARRAY_SIZE(args
),
271 handle
= ec_get_handle();
275 args
[0].type
= args
[1].type
= args
[2].type
= ACPI_TYPE_INTEGER
;
276 args
[1].integer
.value
= 0x04;
278 if (acpi_has_method(handle
, "SPIN")) {
279 acpi_method
= "SPIN";
280 args
[0].integer
.value
= 0;
281 args
[2].integer
.value
= brightness
? 1 : 0;
282 } else if (acpi_has_method(handle
, "WPIN")) {
283 acpi_method
= "WPIN";
284 args
[0].integer
.value
= 1;
285 args
[2].integer
.value
= brightness
? 0 : 1;
290 status
= acpi_evaluate_object(handle
, acpi_method
, &arg_list
, NULL
);
291 if (ACPI_FAILURE(status
))
298 arg
.cmd
= MICMUTE_LED_SET
;
299 arg
.args
[2] = brightness
;
301 return huawei_wmi_cmd(arg
.cmd
, NULL
, 0);
305 static void huawei_wmi_leds_setup(struct device
*dev
)
307 struct huawei_wmi
*huawei
= dev_get_drvdata(dev
);
309 huawei
->cdev
.name
= "platform::micmute";
310 huawei
->cdev
.max_brightness
= 1;
311 huawei
->cdev
.brightness_set_blocking
= &huawei_wmi_micmute_led_set
;
312 huawei
->cdev
.default_trigger
= "audio-micmute";
313 huawei
->cdev
.dev
= dev
;
314 huawei
->cdev
.flags
= LED_CORE_SUSPENDRESUME
;
316 devm_led_classdev_register(dev
, &huawei
->cdev
);
319 /* Battery protection */
321 static int huawei_wmi_battery_get(int *start
, int *end
)
326 err
= huawei_wmi_cmd(BATTERY_THRESH_GET
, ret
, sizeof(ret
));
330 /* Find the last two non-zero values. Return status is ignored. */
331 i
= ARRAY_SIZE(ret
) - 1;
337 } while (i
> 2 && !ret
[i
--]);
342 static int huawei_wmi_battery_set(int start
, int end
)
347 if (start
< 0 || end
< 0 || start
> 100 || end
> 100)
350 arg
.cmd
= BATTERY_THRESH_SET
;
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.
359 if (quirks
&& quirks
->battery_reset
&& start
== 0 && end
== 100) {
360 err
= huawei_wmi_battery_set(0, 0);
367 err
= huawei_wmi_cmd(arg
.cmd
, NULL
, 0);
372 static ssize_t
charge_control_start_threshold_show(struct device
*dev
,
373 struct device_attribute
*attr
,
378 err
= huawei_wmi_battery_get(&start
, NULL
);
382 return sysfs_emit(buf
, "%d\n", start
);
385 static ssize_t
charge_control_end_threshold_show(struct device
*dev
,
386 struct device_attribute
*attr
,
391 err
= huawei_wmi_battery_get(NULL
, &end
);
395 return sysfs_emit(buf
, "%d\n", end
);
398 static ssize_t
charge_control_thresholds_show(struct device
*dev
,
399 struct device_attribute
*attr
,
404 err
= huawei_wmi_battery_get(&start
, &end
);
408 return sysfs_emit(buf
, "%d %d\n", start
, end
);
411 static ssize_t
charge_control_start_threshold_store(struct device
*dev
,
412 struct device_attribute
*attr
,
413 const char *buf
, size_t size
)
417 err
= huawei_wmi_battery_get(NULL
, &end
);
421 if (sscanf(buf
, "%d", &start
) != 1)
424 err
= huawei_wmi_battery_set(start
, end
);
431 static ssize_t
charge_control_end_threshold_store(struct device
*dev
,
432 struct device_attribute
*attr
,
433 const char *buf
, size_t size
)
437 err
= huawei_wmi_battery_get(&start
, NULL
);
441 if (sscanf(buf
, "%d", &end
) != 1)
444 err
= huawei_wmi_battery_set(start
, end
);
451 static ssize_t
charge_control_thresholds_store(struct device
*dev
,
452 struct device_attribute
*attr
,
453 const char *buf
, size_t size
)
457 if (sscanf(buf
, "%d %d", &start
, &end
) != 2)
460 err
= huawei_wmi_battery_set(start
, end
);
467 static DEVICE_ATTR_RW(charge_control_start_threshold
);
468 static DEVICE_ATTR_RW(charge_control_end_threshold
);
469 static DEVICE_ATTR_RW(charge_control_thresholds
);
471 static int huawei_wmi_battery_add(struct power_supply
*battery
, struct acpi_battery_hook
*hook
)
475 err
= device_create_file(&battery
->dev
, &dev_attr_charge_control_start_threshold
);
479 err
= device_create_file(&battery
->dev
, &dev_attr_charge_control_end_threshold
);
481 device_remove_file(&battery
->dev
, &dev_attr_charge_control_start_threshold
);
486 static int huawei_wmi_battery_remove(struct power_supply
*battery
, struct acpi_battery_hook
*hook
)
488 device_remove_file(&battery
->dev
, &dev_attr_charge_control_start_threshold
);
489 device_remove_file(&battery
->dev
, &dev_attr_charge_control_end_threshold
);
494 static 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"
500 static void huawei_wmi_battery_setup(struct device
*dev
)
502 struct huawei_wmi
*huawei
= dev_get_drvdata(dev
);
504 huawei
->battery_available
= true;
505 if (huawei_wmi_battery_get(NULL
, NULL
)) {
506 huawei
->battery_available
= false;
510 battery_hook_register(&huawei_wmi_battery_hook
);
511 device_create_file(dev
, &dev_attr_charge_control_thresholds
);
514 static void huawei_wmi_battery_exit(struct device
*dev
)
516 struct huawei_wmi
*huawei
= dev_get_drvdata(dev
);
518 if (huawei
->battery_available
) {
519 battery_hook_unregister(&huawei_wmi_battery_hook
);
520 device_remove_file(dev
, &dev_attr_charge_control_thresholds
);
526 static int huawei_wmi_fn_lock_get(int *on
)
528 u8 ret
[0x100] = { 0 };
531 err
= huawei_wmi_cmd(FN_LOCK_GET
, ret
, 0x100);
535 /* Find the first non-zero value. Return status is ignored. */
539 *on
= ret
[i
] - 1; // -1 undefined, 0 off, 1 on.
540 } while (i
< 0xff && !ret
[i
++]);
545 static int huawei_wmi_fn_lock_set(int on
)
549 arg
.cmd
= FN_LOCK_SET
;
550 arg
.args
[2] = on
+ 1; // 0 undefined, 1 off, 2 on.
552 return huawei_wmi_cmd(arg
.cmd
, NULL
, 0);
555 static ssize_t
fn_lock_state_show(struct device
*dev
,
556 struct device_attribute
*attr
,
561 err
= huawei_wmi_fn_lock_get(&on
);
565 return sysfs_emit(buf
, "%d\n", on
);
568 static ssize_t
fn_lock_state_store(struct device
*dev
,
569 struct device_attribute
*attr
,
570 const char *buf
, size_t size
)
574 if (kstrtoint(buf
, 10, &on
) ||
578 err
= huawei_wmi_fn_lock_set(on
);
585 static DEVICE_ATTR_RW(fn_lock_state
);
587 static void huawei_wmi_fn_lock_setup(struct device
*dev
)
589 struct huawei_wmi
*huawei
= dev_get_drvdata(dev
);
591 huawei
->fn_lock_available
= true;
592 if (huawei_wmi_fn_lock_get(NULL
)) {
593 huawei
->fn_lock_available
= false;
597 device_create_file(dev
, &dev_attr_fn_lock_state
);
600 static void huawei_wmi_fn_lock_exit(struct device
*dev
)
602 struct huawei_wmi
*huawei
= dev_get_drvdata(dev
);
604 if (huawei
->fn_lock_available
)
605 device_remove_file(dev
, &dev_attr_fn_lock_state
);
610 static void huawei_wmi_debugfs_call_dump(struct seq_file
*m
, void *data
,
611 union acpi_object
*obj
)
613 struct huawei_wmi
*huawei
= m
->private;
617 case ACPI_TYPE_INTEGER
:
618 seq_printf(m
, "0x%llx", obj
->integer
.value
);
620 case ACPI_TYPE_STRING
:
621 seq_printf(m
, "\"%.*s\"", obj
->string
.length
, obj
->string
.pointer
);
623 case ACPI_TYPE_BUFFER
:
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)
632 case ACPI_TYPE_PACKAGE
:
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)
642 dev_err(huawei
->dev
, "Unexpected obj type, got %d\n", obj
->type
);
647 static int huawei_wmi_debugfs_call_show(struct seq_file
*m
, void *data
)
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
;
655 in
.length
= sizeof(u64
);
656 in
.pointer
= &huawei
->debug
.arg
;
658 err
= huawei_wmi_call(huawei
, &in
, &out
);
665 goto fail_debugfs_call
;
668 huawei_wmi_debugfs_call_dump(m
, huawei
, obj
);
675 DEFINE_SHOW_ATTRIBUTE(huawei_wmi_debugfs_call
);
677 static void huawei_wmi_debugfs_setup(struct device
*dev
)
679 struct huawei_wmi
*huawei
= dev_get_drvdata(dev
);
681 huawei
->debug
.root
= debugfs_create_dir("huawei-wmi", NULL
);
683 debugfs_create_x64("arg", 0644, huawei
->debug
.root
,
685 debugfs_create_file("call", 0400,
686 huawei
->debug
.root
, huawei
, &huawei_wmi_debugfs_call_fops
);
689 static void huawei_wmi_debugfs_exit(struct device
*dev
)
691 struct huawei_wmi
*huawei
= dev_get_drvdata(dev
);
693 debugfs_remove_recursive(huawei
->debug
.root
);
698 static void huawei_wmi_process_key(struct input_dev
*idev
, int code
)
700 const struct key_entry
*key
;
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.
708 struct acpi_buffer response
= { ACPI_ALLOCATE_BUFFER
, NULL
};
709 union acpi_object
*obj
;
712 status
= wmi_query_block(WMI0_EXPENSIVE_GUID
, 0, &response
);
713 if (ACPI_FAILURE(status
))
716 obj
= (union acpi_object
*)response
.pointer
;
717 if (obj
&& obj
->type
== ACPI_TYPE_INTEGER
)
718 code
= obj
->integer
.value
;
720 kfree(response
.pointer
);
723 key
= sparse_keymap_entry_from_scancode(idev
, code
);
725 dev_info(&idev
->dev
, "Unknown key pressed, code: 0x%04x\n", code
);
729 if (quirks
&& !quirks
->report_brightness
&&
730 (key
->sw
.code
== KEY_BRIGHTNESSDOWN
||
731 key
->sw
.code
== KEY_BRIGHTNESSUP
))
734 sparse_keymap_report_entry(idev
, key
, 1, true);
737 static void huawei_wmi_input_notify(union acpi_object
*obj
, void *context
)
739 struct input_dev
*idev
= (struct input_dev
*)context
;
741 if (obj
&& obj
->type
== ACPI_TYPE_INTEGER
)
742 huawei_wmi_process_key(idev
, obj
->integer
.value
);
744 dev_err(&idev
->dev
, "Bad response type\n");
747 static int huawei_wmi_input_setup(struct device
*dev
, const char *guid
)
749 struct input_dev
*idev
;
753 idev
= devm_input_allocate_device(dev
);
757 idev
->name
= "Huawei WMI hotkeys";
758 idev
->phys
= "wmi/input0";
759 idev
->id
.bustype
= BUS_HOST
;
760 idev
->dev
.parent
= dev
;
762 err
= sparse_keymap_setup(idev
, huawei_wmi_keymap
, NULL
);
766 err
= input_register_device(idev
);
770 status
= wmi_install_notify_handler(guid
, huawei_wmi_input_notify
, idev
);
771 if (ACPI_FAILURE(status
))
777 static void huawei_wmi_input_exit(struct device
*dev
, const char *guid
)
779 wmi_remove_notify_handler(guid
);
784 static const struct wmi_device_id huawei_wmi_events_id_table
[] = {
785 { .guid_string
= WMI0_EVENT_GUID
},
786 { .guid_string
= HWMI_EVENT_GUID
},
790 static int huawei_wmi_probe(struct platform_device
*pdev
)
792 const struct wmi_device_id
*guid
= huawei_wmi_events_id_table
;
795 platform_set_drvdata(pdev
, huawei_wmi
);
796 huawei_wmi
->dev
= &pdev
->dev
;
798 while (*guid
->guid_string
) {
799 if (wmi_has_guid(guid
->guid_string
)) {
800 err
= huawei_wmi_input_setup(&pdev
->dev
, guid
->guid_string
);
802 dev_err(&pdev
->dev
, "Failed to setup input on %s\n", guid
->guid_string
);
810 if (wmi_has_guid(HWMI_METHOD_GUID
)) {
811 mutex_init(&huawei_wmi
->wmi_lock
);
813 huawei_wmi_leds_setup(&pdev
->dev
);
814 huawei_wmi_fn_lock_setup(&pdev
->dev
);
815 huawei_wmi_battery_setup(&pdev
->dev
);
816 huawei_wmi_debugfs_setup(&pdev
->dev
);
822 static void huawei_wmi_remove(struct platform_device
*pdev
)
824 const struct wmi_device_id
*guid
= huawei_wmi_events_id_table
;
826 while (*guid
->guid_string
) {
827 if (wmi_has_guid(guid
->guid_string
))
828 huawei_wmi_input_exit(&pdev
->dev
, guid
->guid_string
);
833 if (wmi_has_guid(HWMI_METHOD_GUID
)) {
834 huawei_wmi_debugfs_exit(&pdev
->dev
);
835 huawei_wmi_battery_exit(&pdev
->dev
);
836 huawei_wmi_fn_lock_exit(&pdev
->dev
);
840 static struct platform_driver huawei_wmi_driver
= {
842 .name
= "huawei-wmi",
844 .probe
= huawei_wmi_probe
,
845 .remove
= huawei_wmi_remove
,
848 static __init
int huawei_wmi_init(void)
850 struct platform_device
*pdev
;
853 huawei_wmi
= kzalloc(sizeof(struct huawei_wmi
), GFP_KERNEL
);
857 quirks
= &quirk_unknown
;
858 dmi_check_system(huawei_quirks
);
859 if (battery_reset
!= -1)
860 quirks
->battery_reset
= battery_reset
;
861 if (report_brightness
!= -1)
862 quirks
->report_brightness
= report_brightness
;
864 err
= platform_driver_register(&huawei_wmi_driver
);
868 pdev
= platform_device_register_simple("huawei-wmi", PLATFORM_DEVID_NONE
, NULL
, 0);
877 platform_driver_unregister(&huawei_wmi_driver
);
883 static __exit
void huawei_wmi_exit(void)
885 struct platform_device
*pdev
= to_platform_device(huawei_wmi
->dev
);
887 platform_device_unregister(pdev
);
888 platform_driver_unregister(&huawei_wmi_driver
);
893 module_init(huawei_wmi_init
);
894 module_exit(huawei_wmi_exit
);
896 MODULE_ALIAS("wmi:"HWMI_METHOD_GUID
);
897 MODULE_DEVICE_TABLE(wmi
, huawei_wmi_events_id_table
);
898 MODULE_AUTHOR("Ayman Bagabas <ayman.bagabas@gmail.com>");
899 MODULE_DESCRIPTION("Huawei WMI laptop extras driver");
900 MODULE_LICENSE("GPL v2");