1
1
// SPDX-License-Identifier: GPL-2.0
2
2
/*
3
- * Huawei WMI hotkeys
3
+ * Huawei WMI laptop extras driver
4
4
*
5
5
* Copyright (C) 2018 Ayman Bagabas <[email protected] >
6
6
*/
10
10
#include <linux/input/sparse-keymap.h>
11
11
#include <linux/leds.h>
12
12
#include <linux/module.h>
13
+ #include <linux/platform_device.h>
13
14
#include <linux/wmi.h>
14
15
15
16
/*
16
17
* Huawei WMI GUIDs
17
18
*/
18
- #define WMI0_EVENT_GUID "59142400-C6A3-40fa-BADB-8A2652834100"
19
- #define AMW0_EVENT_GUID "ABBC0F5C-8EA1-11D1-A000-C90629100000"
19
+ #define HWMI_EVENT_GUID "ABBC0F5C-8EA1-11D1-A000-C90629100000"
20
20
21
+ /* Legacy GUIDs */
21
22
#define WMI0_EXPENSIVE_GUID "39142400-C6A3-40fa-BADB-8A2652834100"
23
+ #define WMI0_EVENT_GUID "59142400-C6A3-40fa-BADB-8A2652834100"
22
24
23
- struct huawei_wmi_priv {
24
- struct input_dev * idev ;
25
+ struct huawei_wmi {
26
+ struct input_dev * idev [ 2 ] ;
25
27
struct led_classdev cdev ;
28
+ struct platform_device * pdev ;
26
29
acpi_handle handle ;
27
30
char * acpi_method ;
28
31
};
29
32
33
+ struct huawei_wmi * huawei_wmi ;
34
+
30
35
static const struct key_entry huawei_wmi_keymap [] = {
31
36
{ KE_KEY , 0x281 , { KEY_BRIGHTNESSDOWN } },
32
37
{ KE_KEY , 0x282 , { KEY_BRIGHTNESSUP } },
@@ -37,7 +42,7 @@ static const struct key_entry huawei_wmi_keymap[] = {
37
42
{ KE_KEY , 0x289 , { KEY_WLAN } },
38
43
// Huawei |M| key
39
44
{ KE_KEY , 0x28a , { KEY_CONFIG } },
40
- // Keyboard backlight
45
+ // Keyboard backlit
41
46
{ KE_IGNORE , 0x293 , { KEY_KBDILLUMTOGGLE } },
42
47
{ KE_IGNORE , 0x294 , { KEY_KBDILLUMUP } },
43
48
{ KE_IGNORE , 0x295 , { KEY_KBDILLUMUP } },
@@ -47,7 +52,7 @@ static const struct key_entry huawei_wmi_keymap[] = {
47
52
static int huawei_wmi_micmute_led_set (struct led_classdev * led_cdev ,
48
53
enum led_brightness brightness )
49
54
{
50
- struct huawei_wmi_priv * priv = dev_get_drvdata (led_cdev -> dev -> parent );
55
+ struct huawei_wmi * huawei = dev_get_drvdata (led_cdev -> dev -> parent );
51
56
acpi_status status ;
52
57
union acpi_object args [3 ];
53
58
struct acpi_object_list arg_list = {
@@ -58,52 +63,53 @@ static int huawei_wmi_micmute_led_set(struct led_classdev *led_cdev,
58
63
args [0 ].type = args [1 ].type = args [2 ].type = ACPI_TYPE_INTEGER ;
59
64
args [1 ].integer .value = 0x04 ;
60
65
61
- if (strcmp (priv -> acpi_method , "SPIN" ) == 0 ) {
66
+ if (strcmp (huawei -> acpi_method , "SPIN" ) == 0 ) {
62
67
args [0 ].integer .value = 0 ;
63
68
args [2 ].integer .value = brightness ? 1 : 0 ;
64
- } else if (strcmp (priv -> acpi_method , "WPIN" ) == 0 ) {
69
+ } else if (strcmp (huawei -> acpi_method , "WPIN" ) == 0 ) {
65
70
args [0 ].integer .value = 1 ;
66
71
args [2 ].integer .value = brightness ? 0 : 1 ;
67
72
} else {
68
73
return - EINVAL ;
69
74
}
70
75
71
- status = acpi_evaluate_object (priv -> handle , priv -> acpi_method , & arg_list , NULL );
76
+ status = acpi_evaluate_object (huawei -> handle , huawei -> acpi_method , & arg_list , NULL );
72
77
if (ACPI_FAILURE (status ))
73
78
return - ENXIO ;
74
79
75
80
return 0 ;
76
81
}
77
82
78
- static int huawei_wmi_leds_setup (struct wmi_device * wdev )
83
+ static void huawei_wmi_leds_setup (struct device * dev )
79
84
{
80
- struct huawei_wmi_priv * priv = dev_get_drvdata (& wdev -> dev );
85
+ struct huawei_wmi * huawei = dev_get_drvdata (dev );
81
86
82
- priv -> handle = ec_get_handle ();
83
- if (!priv -> handle )
84
- return 0 ;
87
+ huawei -> handle = ec_get_handle ();
88
+ if (!huawei -> handle )
89
+ return ;
85
90
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" ;
91
+ if (acpi_has_method (huawei -> handle , "SPIN" ))
92
+ huawei -> acpi_method = "SPIN" ;
93
+ else if (acpi_has_method (huawei -> handle , "WPIN" ))
94
+ huawei -> acpi_method = "WPIN" ;
90
95
else
91
- return 0 ;
96
+ return ;
92
97
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 ;
98
+ huawei -> cdev .name = "platform::micmute" ;
99
+ huawei -> cdev .max_brightness = 1 ;
100
+ huawei -> cdev .brightness_set_blocking = & huawei_wmi_micmute_led_set ;
101
+ huawei -> cdev .default_trigger = "audio-micmute" ;
102
+ huawei -> cdev .brightness = ledtrig_audio_get (LED_AUDIO_MICMUTE );
103
+ huawei -> cdev .dev = dev ;
104
+ huawei -> cdev .flags = LED_CORE_SUSPENDRESUME ;
100
105
101
- return devm_led_classdev_register (& wdev -> dev , & priv -> cdev );
106
+ devm_led_classdev_register (dev , & huawei -> cdev );
102
107
}
103
108
104
- static void huawei_wmi_process_key (struct wmi_device * wdev , int code )
109
+ /* Input */
110
+
111
+ static void huawei_wmi_process_key (struct input_dev * idev , int code )
105
112
{
106
- struct huawei_wmi_priv * priv = dev_get_drvdata (& wdev -> dev );
107
113
const struct key_entry * key ;
108
114
109
115
/*
@@ -127,81 +133,155 @@ static void huawei_wmi_process_key(struct wmi_device *wdev, int code)
127
133
kfree (response .pointer );
128
134
}
129
135
130
- key = sparse_keymap_entry_from_scancode (priv -> idev , code );
136
+ key = sparse_keymap_entry_from_scancode (idev , code );
131
137
if (!key ) {
132
- dev_info (& wdev -> dev , "Unknown key pressed, code: 0x%04x\n" , code );
138
+ dev_info (& idev -> dev , "Unknown key pressed, code: 0x%04x\n" , code );
133
139
return ;
134
140
}
135
141
136
- sparse_keymap_report_entry (priv -> idev , key , 1 , true);
142
+ sparse_keymap_report_entry (idev , key , 1 , true);
137
143
}
138
144
139
- static void huawei_wmi_notify (struct wmi_device * wdev ,
140
- union acpi_object * obj )
145
+ static void huawei_wmi_input_notify (u32 value , void * context )
141
146
{
142
- if (obj -> type == ACPI_TYPE_INTEGER )
143
- huawei_wmi_process_key (wdev , obj -> integer .value );
147
+ struct input_dev * idev = (struct input_dev * )context ;
148
+ struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER , NULL };
149
+ union acpi_object * obj ;
150
+ acpi_status status ;
151
+
152
+ status = wmi_get_event_data (value , & response );
153
+ if (ACPI_FAILURE (status )) {
154
+ dev_err (& idev -> dev , "Unable to get event data\n" );
155
+ return ;
156
+ }
157
+
158
+ obj = (union acpi_object * )response .pointer ;
159
+ if (obj && obj -> type == ACPI_TYPE_INTEGER )
160
+ huawei_wmi_process_key (idev , obj -> integer .value );
144
161
else
145
- dev_info (& wdev -> dev , "Bad response type %d\n" , obj -> type );
162
+ dev_err (& idev -> dev , "Bad response type\n" );
163
+
164
+ kfree (response .pointer );
146
165
}
147
166
148
- static int huawei_wmi_input_setup (struct wmi_device * wdev )
167
+ static int huawei_wmi_input_setup (struct device * dev ,
168
+ const char * guid ,
169
+ struct input_dev * * idev )
149
170
{
150
- struct huawei_wmi_priv * priv = dev_get_drvdata (& wdev -> dev );
151
- int err ;
152
-
153
- priv -> idev = devm_input_allocate_device (& wdev -> dev );
154
- if (!priv -> idev )
171
+ * idev = devm_input_allocate_device (dev );
172
+ if (!* idev )
155
173
return - ENOMEM ;
156
174
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 ;
175
+ ( * idev ) -> name = "Huawei WMI hotkeys" ;
176
+ ( * idev ) -> phys = "wmi/input0" ;
177
+ ( * idev ) -> id .bustype = BUS_HOST ;
178
+ ( * idev ) -> dev .parent = dev ;
161
179
162
- err = sparse_keymap_setup (priv -> idev , huawei_wmi_keymap , NULL );
163
- if (err )
164
- return err ;
180
+ return sparse_keymap_setup (* idev , huawei_wmi_keymap , NULL ) ||
181
+ input_register_device (* idev ) ||
182
+ wmi_install_notify_handler (guid , huawei_wmi_input_notify ,
183
+ * idev );
184
+ }
165
185
166
- return input_register_device (priv -> idev );
186
+ static void huawei_wmi_input_exit (struct device * dev , const char * guid )
187
+ {
188
+ wmi_remove_notify_handler (guid );
167
189
}
168
190
169
- static int huawei_wmi_probe (struct wmi_device * wdev , const void * context )
191
+ /* Huawei driver */
192
+
193
+ static const struct wmi_device_id huawei_wmi_events_id_table [] = {
194
+ { .guid_string = WMI0_EVENT_GUID },
195
+ { }
196
+ };
197
+
198
+ static int huawei_wmi_probe (struct platform_device * pdev )
170
199
{
171
- struct huawei_wmi_priv * priv ;
200
+ const struct wmi_device_id * guid = huawei_wmi_events_id_table ;
172
201
int err ;
173
202
174
- priv = devm_kzalloc (& wdev -> dev , sizeof (struct huawei_wmi_priv ), GFP_KERNEL );
175
- if (!priv )
176
- return - ENOMEM ;
203
+ platform_set_drvdata (pdev , huawei_wmi );
204
+ huawei_wmi -> pdev = pdev ;
177
205
178
- dev_set_drvdata (& wdev -> dev , priv );
206
+ while (* guid -> guid_string ) {
207
+ struct input_dev * idev = * huawei_wmi -> idev ;
179
208
180
- err = huawei_wmi_input_setup (wdev );
181
- if (err )
182
- return err ;
209
+ if (wmi_has_guid (guid -> guid_string )) {
210
+ err = huawei_wmi_input_setup (& pdev -> dev , guid -> guid_string , & idev );
211
+ if (err ) {
212
+ dev_err (& pdev -> dev , "Failed to setup input on %s\n" , guid -> guid_string );
213
+ return err ;
214
+ }
215
+ }
183
216
184
- return huawei_wmi_leds_setup (wdev );
217
+ idev ++ ;
218
+ guid ++ ;
219
+ }
220
+
221
+ huawei_wmi_leds_setup (& pdev -> dev );
222
+ return 0 ;
185
223
}
186
224
187
- static const struct wmi_device_id huawei_wmi_id_table [] = {
188
- { .guid_string = WMI0_EVENT_GUID },
189
- { .guid_string = AMW0_EVENT_GUID },
190
- { }
191
- };
225
+ static int huawei_wmi_remove (struct platform_device * pdev )
226
+ {
227
+ const struct wmi_device_id * guid = huawei_wmi_events_id_table ;
228
+
229
+ while (* guid -> guid_string ) {
230
+ if (wmi_has_guid (guid -> guid_string ))
231
+ huawei_wmi_input_exit (& pdev -> dev , guid -> guid_string );
232
+
233
+ guid ++ ;
234
+ }
192
235
193
- static struct wmi_driver huawei_wmi_driver = {
236
+ return 0 ;
237
+ }
238
+
239
+ static struct platform_driver huawei_wmi_driver = {
194
240
.driver = {
195
241
.name = "huawei-wmi" ,
196
242
},
197
- .id_table = huawei_wmi_id_table ,
198
243
.probe = huawei_wmi_probe ,
199
- .notify = huawei_wmi_notify ,
244
+ .remove = huawei_wmi_remove ,
200
245
};
201
246
202
- module_wmi_driver (huawei_wmi_driver );
247
+ static __init int huawei_wmi_init (void )
248
+ {
249
+ struct platform_device * pdev ;
250
+ int err ;
251
+
252
+ huawei_wmi = kzalloc (sizeof (struct huawei_wmi ), GFP_KERNEL );
253
+ if (!huawei_wmi )
254
+ return - ENOMEM ;
255
+
256
+ err = platform_driver_register (& huawei_wmi_driver );
257
+ if (err )
258
+ goto pdrv_err ;
259
+
260
+ pdev = platform_device_register_simple ("huawei-wmi" , -1 , NULL , 0 );
261
+ if (IS_ERR (pdev )) {
262
+ err = PTR_ERR (pdev );
263
+ goto pdev_err ;
264
+ }
265
+
266
+ return 0 ;
267
+
268
+ pdev_err :
269
+ platform_driver_unregister (& huawei_wmi_driver );
270
+ pdrv_err :
271
+ kfree (huawei_wmi );
272
+ return err ;
273
+ }
274
+
275
+ static __exit void huawei_wmi_exit (void )
276
+ {
277
+ platform_device_unregister (huawei_wmi -> pdev );
278
+ platform_driver_unregister (& huawei_wmi_driver );
279
+ }
280
+
281
+ module_init (huawei_wmi_init );
282
+ module_exit (huawei_wmi_exit );
203
283
204
- MODULE_DEVICE_TABLE (wmi , huawei_wmi_id_table );
284
+ MODULE_DEVICE_TABLE (wmi , huawei_wmi_events_id_table );
205
285
MODULE_AUTHOR (
"Ayman Bagabas <[email protected] >" );
206
- MODULE_DESCRIPTION ("Huawei WMI hotkeys " );
286
+ MODULE_DESCRIPTION ("Huawei WMI laptop extras driver " );
207
287
MODULE_LICENSE ("GPL v2" );
0 commit comments