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 input_dev
*idev
[2];
67 struct led_classdev cdev
;
70 struct mutex wmi_lock
;
73 static struct huawei_wmi
*huawei_wmi
;
75 static 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
} },
84 { KE_KEY
, 0x28a, { KEY_CONFIG
} },
86 { KE_IGNORE
, 0x293, { KEY_KBDILLUMTOGGLE
} },
87 { KE_IGNORE
, 0x294, { KEY_KBDILLUMUP
} },
88 { KE_IGNORE
, 0x295, { KEY_KBDILLUMUP
} },
92 static int battery_reset
= -1;
93 static int report_brightness
= -1;
95 module_param(battery_reset
, bint
, 0444);
96 MODULE_PARM_DESC(battery_reset
,
97 "Reset battery charge values to (0-0) before disabling it using (0-100)");
98 module_param(report_brightness
, bint
, 0444);
99 MODULE_PARM_DESC(report_brightness
,
100 "Report brightness keys.");
104 static int __init
dmi_matched(const struct dmi_system_id
*dmi
)
106 quirks
= dmi
->driver_data
;
110 static struct quirk_entry quirk_unknown
= {
113 static struct quirk_entry quirk_battery_reset
= {
114 .battery_reset
= true,
117 static struct quirk_entry quirk_matebook_x
= {
119 .report_brightness
= true,
122 static const struct dmi_system_id huawei_quirks
[] = {
124 .callback
= dmi_matched
,
125 .ident
= "Huawei MACH-WX9",
127 DMI_MATCH(DMI_SYS_VENDOR
, "HUAWEI"),
128 DMI_MATCH(DMI_PRODUCT_NAME
, "MACH-WX9"),
130 .driver_data
= &quirk_battery_reset
133 .callback
= dmi_matched
,
134 .ident
= "Huawei MateBook X",
136 DMI_MATCH(DMI_SYS_VENDOR
, "HUAWEI"),
137 DMI_MATCH(DMI_PRODUCT_NAME
, "HUAWEI MateBook X")
139 .driver_data
= &quirk_matebook_x
146 static int huawei_wmi_call(struct huawei_wmi
*huawei
,
147 struct acpi_buffer
*in
, struct acpi_buffer
*out
)
151 mutex_lock(&huawei
->wmi_lock
);
152 status
= wmi_evaluate_method(HWMI_METHOD_GUID
, 0, 1, in
, out
);
153 mutex_unlock(&huawei
->wmi_lock
);
154 if (ACPI_FAILURE(status
)) {
155 dev_err(huawei
->dev
, "Failed to evaluate wmi method\n");
162 /* HWMI takes a 64 bit input and returns either a package with 2 buffers, one of
163 * 4 bytes and the other of 256 bytes, or one buffer of size 0x104 (260) bytes.
164 * The first 4 bytes are ignored, we ignore the first 4 bytes buffer if we got a
165 * package, or skip the first 4 if a buffer of 0x104 is used. The first byte of
166 * the remaining 0x100 sized buffer has the return status of every call. In case
167 * the return status is non-zero, we return -ENODEV but still copy the returned
168 * buffer to the given buffer parameter (buf).
170 static int huawei_wmi_cmd(u64 arg
, u8
*buf
, size_t buflen
)
172 struct huawei_wmi
*huawei
= huawei_wmi
;
173 struct acpi_buffer out
= { ACPI_ALLOCATE_BUFFER
, NULL
};
174 struct acpi_buffer in
;
175 union acpi_object
*obj
;
179 in
.length
= sizeof(arg
);
182 /* Some models require calling HWMI twice to execute a command. We evaluate
183 * HWMI and if we get a non-zero return status we evaluate it again.
185 for (i
= 0; i
< 2; i
++) {
186 err
= huawei_wmi_call(huawei
, &in
, &out
);
197 /* Models that implement both "legacy" and HWMI tend to return a 0x104
198 * sized buffer instead of a package of 0x4 and 0x100 buffers.
200 case ACPI_TYPE_BUFFER
:
201 if (obj
->buffer
.length
== 0x104) {
202 // Skip the first 4 bytes.
203 obj
->buffer
.pointer
+= 4;
206 dev_err(huawei
->dev
, "Bad buffer length, got %d\n", obj
->buffer
.length
);
212 /* HWMI returns a package with 2 buffer elements, one of 4 bytes and the
213 * other is 256 bytes.
215 case ACPI_TYPE_PACKAGE
:
216 if (obj
->package
.count
!= 2) {
217 dev_err(huawei
->dev
, "Bad package count, got %d\n", obj
->package
.count
);
222 obj
= &obj
->package
.elements
[1];
223 if (obj
->type
!= ACPI_TYPE_BUFFER
) {
224 dev_err(huawei
->dev
, "Bad package element type, got %d\n", obj
->type
);
228 len
= obj
->buffer
.length
;
231 /* Shouldn't get here! */
233 dev_err(huawei
->dev
, "Unexpected obj type, got: %d\n", obj
->type
);
238 if (!*obj
->buffer
.pointer
)
242 err
= (*obj
->buffer
.pointer
) ? -ENODEV
: 0;
245 len
= min(buflen
, len
);
246 memcpy(buf
, obj
->buffer
.pointer
, len
);
256 static int huawei_wmi_micmute_led_set(struct led_classdev
*led_cdev
,
257 enum led_brightness brightness
)
259 /* This is a workaround until the "legacy" interface is implemented. */
260 if (quirks
&& quirks
->ec_micmute
) {
264 union acpi_object args
[3];
265 struct acpi_object_list arg_list
= {
267 .count
= ARRAY_SIZE(args
),
270 handle
= ec_get_handle();
274 args
[0].type
= args
[1].type
= args
[2].type
= ACPI_TYPE_INTEGER
;
275 args
[1].integer
.value
= 0x04;
277 if (acpi_has_method(handle
, "SPIN")) {
278 acpi_method
= "SPIN";
279 args
[0].integer
.value
= 0;
280 args
[2].integer
.value
= brightness
? 1 : 0;
281 } else if (acpi_has_method(handle
, "WPIN")) {
282 acpi_method
= "WPIN";
283 args
[0].integer
.value
= 1;
284 args
[2].integer
.value
= brightness
? 0 : 1;
289 status
= acpi_evaluate_object(handle
, acpi_method
, &arg_list
, NULL
);
290 if (ACPI_FAILURE(status
))
297 arg
.cmd
= MICMUTE_LED_SET
;
298 arg
.args
[2] = brightness
;
300 return huawei_wmi_cmd(arg
.cmd
, NULL
, 0);
304 static void huawei_wmi_leds_setup(struct device
*dev
)
306 struct huawei_wmi
*huawei
= dev_get_drvdata(dev
);
308 huawei
->cdev
.name
= "platform::micmute";
309 huawei
->cdev
.max_brightness
= 1;
310 huawei
->cdev
.brightness_set_blocking
= &huawei_wmi_micmute_led_set
;
311 huawei
->cdev
.default_trigger
= "audio-micmute";
312 huawei
->cdev
.brightness
= ledtrig_audio_get(LED_AUDIO_MICMUTE
);
313 huawei
->cdev
.dev
= dev
;
314 huawei
->cdev
.flags
= LED_CORE_SUSPENDRESUME
;
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
, 0x100);
330 /* Find the last two non-zero values. Return status is ignored. */
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 sprintf(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 sprintf(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 sprintf(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
)
473 device_create_file(&battery
->dev
, &dev_attr_charge_control_start_threshold
);
474 device_create_file(&battery
->dev
, &dev_attr_charge_control_end_threshold
);
479 static int huawei_wmi_battery_remove(struct power_supply
*battery
)
481 device_remove_file(&battery
->dev
, &dev_attr_charge_control_start_threshold
);
482 device_remove_file(&battery
->dev
, &dev_attr_charge_control_end_threshold
);
487 static struct acpi_battery_hook huawei_wmi_battery_hook
= {
488 .add_battery
= huawei_wmi_battery_add
,
489 .remove_battery
= huawei_wmi_battery_remove
,
490 .name
= "Huawei Battery Extension"
493 static void huawei_wmi_battery_setup(struct device
*dev
)
495 struct huawei_wmi
*huawei
= dev_get_drvdata(dev
);
497 huawei
->battery_available
= true;
498 if (huawei_wmi_battery_get(NULL
, NULL
)) {
499 huawei
->battery_available
= false;
503 battery_hook_register(&huawei_wmi_battery_hook
);
504 device_create_file(dev
, &dev_attr_charge_control_thresholds
);
507 static void huawei_wmi_battery_exit(struct device
*dev
)
509 struct huawei_wmi
*huawei
= dev_get_drvdata(dev
);
511 if (huawei
->battery_available
) {
512 battery_hook_unregister(&huawei_wmi_battery_hook
);
513 device_remove_file(dev
, &dev_attr_charge_control_thresholds
);
519 static int huawei_wmi_fn_lock_get(int *on
)
521 u8 ret
[0x100] = { 0 };
524 err
= huawei_wmi_cmd(FN_LOCK_GET
, ret
, 0x100);
528 /* Find the first non-zero value. Return status is ignored. */
532 *on
= ret
[i
] - 1; // -1 undefined, 0 off, 1 on.
533 } while (i
< 0xff && !ret
[i
++]);
538 static int huawei_wmi_fn_lock_set(int on
)
542 arg
.cmd
= FN_LOCK_SET
;
543 arg
.args
[2] = on
+ 1; // 0 undefined, 1 off, 2 on.
545 return huawei_wmi_cmd(arg
.cmd
, NULL
, 0);
548 static ssize_t
fn_lock_state_show(struct device
*dev
,
549 struct device_attribute
*attr
,
554 err
= huawei_wmi_fn_lock_get(&on
);
558 return sprintf(buf
, "%d\n", on
);
561 static ssize_t
fn_lock_state_store(struct device
*dev
,
562 struct device_attribute
*attr
,
563 const char *buf
, size_t size
)
567 if (kstrtoint(buf
, 10, &on
) ||
571 err
= huawei_wmi_fn_lock_set(on
);
578 static DEVICE_ATTR_RW(fn_lock_state
);
580 static void huawei_wmi_fn_lock_setup(struct device
*dev
)
582 struct huawei_wmi
*huawei
= dev_get_drvdata(dev
);
584 huawei
->fn_lock_available
= true;
585 if (huawei_wmi_fn_lock_get(NULL
)) {
586 huawei
->fn_lock_available
= false;
590 device_create_file(dev
, &dev_attr_fn_lock_state
);
593 static void huawei_wmi_fn_lock_exit(struct device
*dev
)
595 struct huawei_wmi
*huawei
= dev_get_drvdata(dev
);
597 if (huawei
->fn_lock_available
)
598 device_remove_file(dev
, &dev_attr_fn_lock_state
);
603 static void huawei_wmi_debugfs_call_dump(struct seq_file
*m
, void *data
,
604 union acpi_object
*obj
)
606 struct huawei_wmi
*huawei
= m
->private;
610 case ACPI_TYPE_INTEGER
:
611 seq_printf(m
, "0x%llx", obj
->integer
.value
);
613 case ACPI_TYPE_STRING
:
614 seq_printf(m
, "\"%.*s\"", obj
->string
.length
, obj
->string
.pointer
);
616 case ACPI_TYPE_BUFFER
:
618 for (i
= 0; i
< obj
->buffer
.length
; i
++) {
619 seq_printf(m
, "0x%02x", obj
->buffer
.pointer
[i
]);
620 if (i
< obj
->buffer
.length
- 1)
625 case ACPI_TYPE_PACKAGE
:
627 for (i
= 0; i
< obj
->package
.count
; i
++) {
628 huawei_wmi_debugfs_call_dump(m
, huawei
, &obj
->package
.elements
[i
]);
629 if (i
< obj
->package
.count
- 1)
635 dev_err(huawei
->dev
, "Unexpected obj type, got %d\n", obj
->type
);
640 static int huawei_wmi_debugfs_call_show(struct seq_file
*m
, void *data
)
642 struct huawei_wmi
*huawei
= m
->private;
643 struct acpi_buffer out
= { ACPI_ALLOCATE_BUFFER
, NULL
};
644 struct acpi_buffer in
;
645 union acpi_object
*obj
;
648 in
.length
= sizeof(u64
);
649 in
.pointer
= &huawei
->debug
.arg
;
651 err
= huawei_wmi_call(huawei
, &in
, &out
);
658 goto fail_debugfs_call
;
661 huawei_wmi_debugfs_call_dump(m
, huawei
, obj
);
668 DEFINE_SHOW_ATTRIBUTE(huawei_wmi_debugfs_call
);
670 static void huawei_wmi_debugfs_setup(struct device
*dev
)
672 struct huawei_wmi
*huawei
= dev_get_drvdata(dev
);
674 huawei
->debug
.root
= debugfs_create_dir("huawei-wmi", NULL
);
676 debugfs_create_x64("arg", 0644, huawei
->debug
.root
,
678 debugfs_create_file("call", 0400,
679 huawei
->debug
.root
, huawei
, &huawei_wmi_debugfs_call_fops
);
682 static void huawei_wmi_debugfs_exit(struct device
*dev
)
684 struct huawei_wmi
*huawei
= dev_get_drvdata(dev
);
686 debugfs_remove_recursive(huawei
->debug
.root
);
691 static void huawei_wmi_process_key(struct input_dev
*idev
, int code
)
693 const struct key_entry
*key
;
696 * WMI0 uses code 0x80 to indicate a hotkey event.
697 * The actual key is fetched from the method WQ00
698 * using WMI0_EXPENSIVE_GUID.
701 struct acpi_buffer response
= { ACPI_ALLOCATE_BUFFER
, NULL
};
702 union acpi_object
*obj
;
705 status
= wmi_query_block(WMI0_EXPENSIVE_GUID
, 0, &response
);
706 if (ACPI_FAILURE(status
))
709 obj
= (union acpi_object
*)response
.pointer
;
710 if (obj
&& obj
->type
== ACPI_TYPE_INTEGER
)
711 code
= obj
->integer
.value
;
713 kfree(response
.pointer
);
716 key
= sparse_keymap_entry_from_scancode(idev
, code
);
718 dev_info(&idev
->dev
, "Unknown key pressed, code: 0x%04x\n", code
);
722 if (quirks
&& !quirks
->report_brightness
&&
723 (key
->sw
.code
== KEY_BRIGHTNESSDOWN
||
724 key
->sw
.code
== KEY_BRIGHTNESSUP
))
727 sparse_keymap_report_entry(idev
, key
, 1, true);
730 static void huawei_wmi_input_notify(u32 value
, void *context
)
732 struct input_dev
*idev
= (struct input_dev
*)context
;
733 struct acpi_buffer response
= { ACPI_ALLOCATE_BUFFER
, NULL
};
734 union acpi_object
*obj
;
737 status
= wmi_get_event_data(value
, &response
);
738 if (ACPI_FAILURE(status
)) {
739 dev_err(&idev
->dev
, "Unable to get event data\n");
743 obj
= (union acpi_object
*)response
.pointer
;
744 if (obj
&& obj
->type
== ACPI_TYPE_INTEGER
)
745 huawei_wmi_process_key(idev
, obj
->integer
.value
);
747 dev_err(&idev
->dev
, "Bad response type\n");
749 kfree(response
.pointer
);
752 static int huawei_wmi_input_setup(struct device
*dev
,
754 struct input_dev
**idev
)
756 *idev
= devm_input_allocate_device(dev
);
760 (*idev
)->name
= "Huawei WMI hotkeys";
761 (*idev
)->phys
= "wmi/input0";
762 (*idev
)->id
.bustype
= BUS_HOST
;
763 (*idev
)->dev
.parent
= dev
;
765 return sparse_keymap_setup(*idev
, huawei_wmi_keymap
, NULL
) ||
766 input_register_device(*idev
) ||
767 wmi_install_notify_handler(guid
, huawei_wmi_input_notify
,
771 static void huawei_wmi_input_exit(struct device
*dev
, const char *guid
)
773 wmi_remove_notify_handler(guid
);
778 static const struct wmi_device_id huawei_wmi_events_id_table
[] = {
779 { .guid_string
= WMI0_EVENT_GUID
},
780 { .guid_string
= HWMI_EVENT_GUID
},
784 static int huawei_wmi_probe(struct platform_device
*pdev
)
786 const struct wmi_device_id
*guid
= huawei_wmi_events_id_table
;
789 platform_set_drvdata(pdev
, huawei_wmi
);
790 huawei_wmi
->dev
= &pdev
->dev
;
792 while (*guid
->guid_string
) {
793 struct input_dev
*idev
= *huawei_wmi
->idev
;
795 if (wmi_has_guid(guid
->guid_string
)) {
796 err
= huawei_wmi_input_setup(&pdev
->dev
, guid
->guid_string
, &idev
);
798 dev_err(&pdev
->dev
, "Failed to setup input on %s\n", guid
->guid_string
);
807 if (wmi_has_guid(HWMI_METHOD_GUID
)) {
808 mutex_init(&huawei_wmi
->wmi_lock
);
810 huawei_wmi_leds_setup(&pdev
->dev
);
811 huawei_wmi_fn_lock_setup(&pdev
->dev
);
812 huawei_wmi_battery_setup(&pdev
->dev
);
813 huawei_wmi_debugfs_setup(&pdev
->dev
);
819 static int huawei_wmi_remove(struct platform_device
*pdev
)
821 const struct wmi_device_id
*guid
= huawei_wmi_events_id_table
;
823 while (*guid
->guid_string
) {
824 if (wmi_has_guid(guid
->guid_string
))
825 huawei_wmi_input_exit(&pdev
->dev
, guid
->guid_string
);
830 if (wmi_has_guid(HWMI_METHOD_GUID
)) {
831 huawei_wmi_debugfs_exit(&pdev
->dev
);
832 huawei_wmi_battery_exit(&pdev
->dev
);
833 huawei_wmi_fn_lock_exit(&pdev
->dev
);
839 static struct platform_driver huawei_wmi_driver
= {
841 .name
= "huawei-wmi",
843 .probe
= huawei_wmi_probe
,
844 .remove
= huawei_wmi_remove
,
847 static __init
int huawei_wmi_init(void)
849 struct platform_device
*pdev
;
852 huawei_wmi
= kzalloc(sizeof(struct huawei_wmi
), GFP_KERNEL
);
856 quirks
= &quirk_unknown
;
857 dmi_check_system(huawei_quirks
);
858 if (battery_reset
!= -1)
859 quirks
->battery_reset
= battery_reset
;
860 if (report_brightness
!= -1)
861 quirks
->report_brightness
= report_brightness
;
863 err
= platform_driver_register(&huawei_wmi_driver
);
867 pdev
= platform_device_register_simple("huawei-wmi", -1, NULL
, 0);
876 platform_driver_unregister(&huawei_wmi_driver
);
882 static __exit
void huawei_wmi_exit(void)
884 struct platform_device
*pdev
= to_platform_device(huawei_wmi
->dev
);
886 platform_device_unregister(pdev
);
887 platform_driver_unregister(&huawei_wmi_driver
);
892 module_init(huawei_wmi_init
);
893 module_exit(huawei_wmi_exit
);
895 MODULE_ALIAS("wmi:"HWMI_METHOD_GUID
);
896 MODULE_DEVICE_TABLE(wmi
, huawei_wmi_events_id_table
);
897 MODULE_AUTHOR("Ayman Bagabas <ayman.bagabas@gmail.com>");
898 MODULE_DESCRIPTION("Huawei WMI laptop extras driver");
899 MODULE_LICENSE("GPL v2");