1 // SPDX-License-Identifier: GPL-2.0
5 * Copyright (C) 2018 Ayman Bagabas <ayman.bagabas@gmail.com>
8 #include <linux/acpi.h>
9 #include <linux/input.h>
10 #include <linux/input/sparse-keymap.h>
11 #include <linux/leds.h>
12 #include <linux/module.h>
13 #include <linux/wmi.h>
18 #define WMI0_EVENT_GUID "59142400-C6A3-40fa-BADB-8A2652834100"
19 #define AMW0_EVENT_GUID "ABBC0F5C-8EA1-11D1-A000-C90629100000"
21 #define WMI0_EXPENSIVE_GUID "39142400-C6A3-40fa-BADB-8A2652834100"
23 struct huawei_wmi_priv
{
24 struct input_dev
*idev
;
25 struct led_classdev cdev
;
30 static const struct key_entry huawei_wmi_keymap
[] = {
31 { KE_KEY
, 0x281, { KEY_BRIGHTNESSDOWN
} },
32 { KE_KEY
, 0x282, { KEY_BRIGHTNESSUP
} },
33 { KE_KEY
, 0x284, { KEY_MUTE
} },
34 { KE_KEY
, 0x285, { KEY_VOLUMEDOWN
} },
35 { KE_KEY
, 0x286, { KEY_VOLUMEUP
} },
36 { KE_KEY
, 0x287, { KEY_MICMUTE
} },
37 { KE_KEY
, 0x289, { KEY_WLAN
} },
39 { KE_KEY
, 0x28a, { KEY_CONFIG
} },
41 { KE_IGNORE
, 0x293, { KEY_KBDILLUMTOGGLE
} },
42 { KE_IGNORE
, 0x294, { KEY_KBDILLUMUP
} },
43 { KE_IGNORE
, 0x295, { KEY_KBDILLUMUP
} },
47 static int huawei_wmi_micmute_led_set(struct led_classdev
*led_cdev
,
48 enum led_brightness brightness
)
50 struct huawei_wmi_priv
*priv
= dev_get_drvdata(led_cdev
->dev
->parent
);
52 union acpi_object args
[3];
53 struct acpi_object_list arg_list
= {
55 .count
= ARRAY_SIZE(args
),
58 args
[0].type
= args
[1].type
= args
[2].type
= ACPI_TYPE_INTEGER
;
59 args
[1].integer
.value
= 0x04;
61 if (strcmp(priv
->acpi_method
, "SPIN") == 0) {
62 args
[0].integer
.value
= 0;
63 args
[2].integer
.value
= brightness
? 1 : 0;
64 } else if (strcmp(priv
->acpi_method
, "WPIN") == 0) {
65 args
[0].integer
.value
= 1;
66 args
[2].integer
.value
= brightness
? 0 : 1;
71 status
= acpi_evaluate_object(priv
->handle
, priv
->acpi_method
, &arg_list
, NULL
);
72 if (ACPI_FAILURE(status
))
78 static int huawei_wmi_leds_setup(struct wmi_device
*wdev
)
80 struct huawei_wmi_priv
*priv
= dev_get_drvdata(&wdev
->dev
);
82 priv
->handle
= ec_get_handle();
86 if (acpi_has_method(priv
->handle
, "SPIN"))
87 priv
->acpi_method
= "SPIN";
88 else if (acpi_has_method(priv
->handle
, "WPIN"))
89 priv
->acpi_method
= "WPIN";
93 priv
->cdev
.name
= "platform::micmute";
94 priv
->cdev
.max_brightness
= 1;
95 priv
->cdev
.brightness_set_blocking
= huawei_wmi_micmute_led_set
;
96 priv
->cdev
.default_trigger
= "audio-micmute";
97 priv
->cdev
.brightness
= ledtrig_audio_get(LED_AUDIO_MICMUTE
);
98 priv
->cdev
.dev
= &wdev
->dev
;
99 priv
->cdev
.flags
= LED_CORE_SUSPENDRESUME
;
101 return devm_led_classdev_register(&wdev
->dev
, &priv
->cdev
);
104 static void huawei_wmi_process_key(struct wmi_device
*wdev
, int code
)
106 struct huawei_wmi_priv
*priv
= dev_get_drvdata(&wdev
->dev
);
107 const struct key_entry
*key
;
110 * WMI0 uses code 0x80 to indicate a hotkey event.
111 * The actual key is fetched from the method WQ00
112 * using WMI0_EXPENSIVE_GUID.
115 struct acpi_buffer response
= { ACPI_ALLOCATE_BUFFER
, NULL
};
116 union acpi_object
*obj
;
119 status
= wmi_query_block(WMI0_EXPENSIVE_GUID
, 0, &response
);
120 if (ACPI_FAILURE(status
))
123 obj
= (union acpi_object
*)response
.pointer
;
124 if (obj
&& obj
->type
== ACPI_TYPE_INTEGER
)
125 code
= obj
->integer
.value
;
127 kfree(response
.pointer
);
130 key
= sparse_keymap_entry_from_scancode(priv
->idev
, code
);
132 dev_info(&wdev
->dev
, "Unknown key pressed, code: 0x%04x\n", code
);
136 sparse_keymap_report_entry(priv
->idev
, key
, 1, true);
139 static void huawei_wmi_notify(struct wmi_device
*wdev
,
140 union acpi_object
*obj
)
142 if (obj
->type
== ACPI_TYPE_INTEGER
)
143 huawei_wmi_process_key(wdev
, obj
->integer
.value
);
145 dev_info(&wdev
->dev
, "Bad response type %d\n", obj
->type
);
148 static int huawei_wmi_input_setup(struct wmi_device
*wdev
)
150 struct huawei_wmi_priv
*priv
= dev_get_drvdata(&wdev
->dev
);
153 priv
->idev
= devm_input_allocate_device(&wdev
->dev
);
157 priv
->idev
->name
= "Huawei WMI hotkeys";
158 priv
->idev
->phys
= "wmi/input0";
159 priv
->idev
->id
.bustype
= BUS_HOST
;
160 priv
->idev
->dev
.parent
= &wdev
->dev
;
162 err
= sparse_keymap_setup(priv
->idev
, huawei_wmi_keymap
, NULL
);
166 return input_register_device(priv
->idev
);
169 static int huawei_wmi_probe(struct wmi_device
*wdev
)
171 struct huawei_wmi_priv
*priv
;
174 priv
= devm_kzalloc(&wdev
->dev
, sizeof(struct huawei_wmi_priv
), GFP_KERNEL
);
178 dev_set_drvdata(&wdev
->dev
, priv
);
180 err
= huawei_wmi_input_setup(wdev
);
184 return huawei_wmi_leds_setup(wdev
);
187 static const struct wmi_device_id huawei_wmi_id_table
[] = {
188 { .guid_string
= WMI0_EVENT_GUID
},
189 { .guid_string
= AMW0_EVENT_GUID
},
193 static struct wmi_driver huawei_wmi_driver
= {
195 .name
= "huawei-wmi",
197 .id_table
= huawei_wmi_id_table
,
198 .probe
= huawei_wmi_probe
,
199 .notify
= huawei_wmi_notify
,
202 module_wmi_driver(huawei_wmi_driver
);
204 MODULE_DEVICE_TABLE(wmi
, huawei_wmi_id_table
);
205 MODULE_AUTHOR("Ayman Bagabas <ayman.bagabas@gmail.com>");
206 MODULE_DESCRIPTION("Huawei WMI hotkeys");
207 MODULE_LICENSE("GPL v2");