These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / platform / x86 / dell-wmi.c
1 /*
2  * Dell WMI hotkeys
3  *
4  * Copyright (C) 2008 Red Hat <mjg@redhat.com>
5  *
6  * Portions based on wistron_btns.c:
7  * Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz>
8  * Copyright (C) 2005 Bernhard Rosenkraenzer <bero@arklinux.org>
9  * Copyright (C) 2005 Dmitry Torokhov <dtor@mail.ru>
10  *
11  *  This program is free software; you can redistribute it and/or modify
12  *  it under the terms of the GNU General Public License as published by
13  *  the Free Software Foundation; either version 2 of the License, or
14  *  (at your option) any later version.
15  *
16  *  This program is distributed in the hope that it will be useful,
17  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
18  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  *  GNU General Public License for more details.
20  *
21  *  You should have received a copy of the GNU General Public License
22  *  along with this program; if not, write to the Free Software
23  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24  */
25
26 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
27
28 #include <linux/kernel.h>
29 #include <linux/module.h>
30 #include <linux/init.h>
31 #include <linux/slab.h>
32 #include <linux/types.h>
33 #include <linux/input.h>
34 #include <linux/input/sparse-keymap.h>
35 #include <linux/acpi.h>
36 #include <linux/string.h>
37 #include <linux/dmi.h>
38 #include <acpi/video.h>
39
40 MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
41 MODULE_DESCRIPTION("Dell laptop WMI hotkeys driver");
42 MODULE_LICENSE("GPL");
43
44 #define DELL_EVENT_GUID "9DBB5994-A997-11DA-B012-B622A1EF5492"
45
46 static int acpi_video;
47
48 MODULE_ALIAS("wmi:"DELL_EVENT_GUID);
49
50 /*
51  * Certain keys are flagged as KE_IGNORE. All of these are either
52  * notifications (rather than requests for change) or are also sent
53  * via the keyboard controller so should not be sent again.
54  */
55
56 static const struct key_entry dell_wmi_legacy_keymap[] __initconst = {
57         { KE_IGNORE, 0x003a, { KEY_CAPSLOCK } },
58
59         { KE_KEY, 0xe045, { KEY_PROG1 } },
60         { KE_KEY, 0xe009, { KEY_EJECTCD } },
61
62         /* These also contain the brightness level at offset 6 */
63         { KE_KEY, 0xe006, { KEY_BRIGHTNESSUP } },
64         { KE_KEY, 0xe005, { KEY_BRIGHTNESSDOWN } },
65
66         /* Battery health status button */
67         { KE_KEY, 0xe007, { KEY_BATTERY } },
68
69         /* Radio devices state change */
70         { KE_IGNORE, 0xe008, { KEY_RFKILL } },
71
72         /* The next device is at offset 6, the active devices are at
73            offset 8 and the attached devices at offset 10 */
74         { KE_KEY, 0xe00b, { KEY_SWITCHVIDEOMODE } },
75
76         { KE_IGNORE, 0xe00c, { KEY_KBDILLUMTOGGLE } },
77
78         /* BIOS error detected */
79         { KE_IGNORE, 0xe00d, { KEY_RESERVED } },
80
81         /* Wifi Catcher */
82         { KE_KEY, 0xe011, {KEY_PROG2 } },
83
84         /* Ambient light sensor toggle */
85         { KE_IGNORE, 0xe013, { KEY_RESERVED } },
86
87         { KE_IGNORE, 0xe020, { KEY_MUTE } },
88
89         /* Shortcut and audio panel keys */
90         { KE_IGNORE, 0xe025, { KEY_RESERVED } },
91         { KE_IGNORE, 0xe026, { KEY_RESERVED } },
92
93         { KE_IGNORE, 0xe02e, { KEY_VOLUMEDOWN } },
94         { KE_IGNORE, 0xe030, { KEY_VOLUMEUP } },
95         { KE_IGNORE, 0xe033, { KEY_KBDILLUMUP } },
96         { KE_IGNORE, 0xe034, { KEY_KBDILLUMDOWN } },
97         { KE_IGNORE, 0xe03a, { KEY_CAPSLOCK } },
98         { KE_IGNORE, 0xe045, { KEY_NUMLOCK } },
99         { KE_IGNORE, 0xe046, { KEY_SCROLLLOCK } },
100         { KE_IGNORE, 0xe0f7, { KEY_MUTE } },
101         { KE_IGNORE, 0xe0f8, { KEY_VOLUMEDOWN } },
102         { KE_IGNORE, 0xe0f9, { KEY_VOLUMEUP } },
103         { KE_END, 0 }
104 };
105
106 static bool dell_new_hk_type;
107
108 struct dell_bios_keymap_entry {
109         u16 scancode;
110         u16 keycode;
111 };
112
113 struct dell_bios_hotkey_table {
114         struct dmi_header header;
115         struct dell_bios_keymap_entry keymap[];
116
117 };
118
119 static const struct dell_bios_hotkey_table *dell_bios_hotkey_table;
120
121 static const u16 bios_to_linux_keycode[256] __initconst = {
122
123         KEY_MEDIA,      KEY_NEXTSONG,   KEY_PLAYPAUSE, KEY_PREVIOUSSONG,
124         KEY_STOPCD,     KEY_UNKNOWN,    KEY_UNKNOWN,    KEY_UNKNOWN,
125         KEY_WWW,        KEY_UNKNOWN,    KEY_VOLUMEDOWN, KEY_MUTE,
126         KEY_VOLUMEUP,   KEY_UNKNOWN,    KEY_BATTERY,    KEY_EJECTCD,
127         KEY_UNKNOWN,    KEY_SLEEP,      KEY_PROG1, KEY_BRIGHTNESSDOWN,
128         KEY_BRIGHTNESSUP,       KEY_UNKNOWN,    KEY_KBDILLUMTOGGLE,
129         KEY_UNKNOWN,    KEY_SWITCHVIDEOMODE,    KEY_UNKNOWN, KEY_UNKNOWN,
130         KEY_SWITCHVIDEOMODE,    KEY_UNKNOWN,    KEY_UNKNOWN, KEY_PROG2,
131         KEY_UNKNOWN,    KEY_UNKNOWN,    KEY_UNKNOWN,    KEY_UNKNOWN,
132         KEY_UNKNOWN,    KEY_UNKNOWN,    KEY_UNKNOWN,    KEY_MICMUTE,
133         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
134         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
135         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
136         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
137         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
138         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
139         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
140         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
141         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
142         0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_PROG3
143 };
144
145 static struct input_dev *dell_wmi_input_dev;
146
147 static void dell_wmi_process_key(int reported_key)
148 {
149         const struct key_entry *key;
150
151         key = sparse_keymap_entry_from_scancode(dell_wmi_input_dev,
152                                                 reported_key);
153         if (!key) {
154                 pr_info("Unknown key %x pressed\n", reported_key);
155                 return;
156         }
157
158         pr_debug("Key %x pressed\n", reported_key);
159
160         /* Don't report brightness notifications that will also come via ACPI */
161         if ((key->keycode == KEY_BRIGHTNESSUP ||
162              key->keycode == KEY_BRIGHTNESSDOWN) && acpi_video)
163                 return;
164
165         sparse_keymap_report_entry(dell_wmi_input_dev, key, 1, true);
166 }
167
168 static void dell_wmi_notify(u32 value, void *context)
169 {
170         struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
171         union acpi_object *obj;
172         acpi_status status;
173         acpi_size buffer_size;
174         u16 *buffer_entry, *buffer_end;
175         int len, i;
176
177         status = wmi_get_event_data(value, &response);
178         if (status != AE_OK) {
179                 pr_warn("bad event status 0x%x\n", status);
180                 return;
181         }
182
183         obj = (union acpi_object *)response.pointer;
184         if (!obj) {
185                 pr_warn("no response\n");
186                 return;
187         }
188
189         if (obj->type != ACPI_TYPE_BUFFER) {
190                 pr_warn("bad response type %x\n", obj->type);
191                 kfree(obj);
192                 return;
193         }
194
195         pr_debug("Received WMI event (%*ph)\n",
196                 obj->buffer.length, obj->buffer.pointer);
197
198         buffer_entry = (u16 *)obj->buffer.pointer;
199         buffer_size = obj->buffer.length/2;
200
201         if (!dell_new_hk_type) {
202                 if (buffer_size >= 3 && buffer_entry[1] == 0x0)
203                         dell_wmi_process_key(buffer_entry[2]);
204                 else if (buffer_size >= 2)
205                         dell_wmi_process_key(buffer_entry[1]);
206                 else
207                         pr_info("Received unknown WMI event\n");
208                 kfree(obj);
209                 return;
210         }
211
212         buffer_end = buffer_entry + buffer_size;
213
214         while (buffer_entry < buffer_end) {
215
216                 len = buffer_entry[0];
217                 if (len == 0)
218                         break;
219
220                 len++;
221
222                 if (buffer_entry + len > buffer_end) {
223                         pr_warn("Invalid length of WMI event\n");
224                         break;
225                 }
226
227                 pr_debug("Process buffer (%*ph)\n", len*2, buffer_entry);
228
229                 switch (buffer_entry[1]) {
230                 case 0x00:
231                         for (i = 2; i < len; ++i) {
232                                 switch (buffer_entry[i]) {
233                                 case 0xe043:
234                                         /* NIC Link is Up */
235                                         pr_debug("NIC Link is Up\n");
236                                         break;
237                                 case 0xe044:
238                                         /* NIC Link is Down */
239                                         pr_debug("NIC Link is Down\n");
240                                         break;
241                                 case 0xe045:
242                                         /* Unknown event but defined in DSDT */
243                                 default:
244                                         /* Unknown event */
245                                         pr_info("Unknown WMI event type 0x00: "
246                                                 "0x%x\n", (int)buffer_entry[i]);
247                                         break;
248                                 }
249                         }
250                         break;
251                 case 0x10:
252                         /* Keys pressed */
253                         for (i = 2; i < len; ++i)
254                                 dell_wmi_process_key(buffer_entry[i]);
255                         break;
256                 case 0x11:
257                         for (i = 2; i < len; ++i) {
258                                 switch (buffer_entry[i]) {
259                                 case 0xfff0:
260                                         /* Battery unplugged */
261                                         pr_debug("Battery unplugged\n");
262                                         break;
263                                 case 0xfff1:
264                                         /* Battery inserted */
265                                         pr_debug("Battery inserted\n");
266                                         break;
267                                 case 0x01e1:
268                                 case 0x02ea:
269                                 case 0x02eb:
270                                 case 0x02ec:
271                                 case 0x02f6:
272                                         /* Keyboard backlight level changed */
273                                         pr_debug("Keyboard backlight level "
274                                                  "changed\n");
275                                         break;
276                                 default:
277                                         /* Unknown event */
278                                         pr_info("Unknown WMI event type 0x11: "
279                                                 "0x%x\n", (int)buffer_entry[i]);
280                                         break;
281                                 }
282                         }
283                         break;
284                 default:
285                         /* Unknown event */
286                         pr_info("Unknown WMI event type 0x%x\n",
287                                 (int)buffer_entry[1]);
288                         break;
289                 }
290
291                 buffer_entry += len;
292
293         }
294
295         kfree(obj);
296 }
297
298 static const struct key_entry * __init dell_wmi_prepare_new_keymap(void)
299 {
300         int hotkey_num = (dell_bios_hotkey_table->header.length - 4) /
301                                 sizeof(struct dell_bios_keymap_entry);
302         struct key_entry *keymap;
303         int i;
304
305         keymap = kcalloc(hotkey_num + 1, sizeof(struct key_entry), GFP_KERNEL);
306         if (!keymap)
307                 return NULL;
308
309         for (i = 0; i < hotkey_num; i++) {
310                 const struct dell_bios_keymap_entry *bios_entry =
311                                         &dell_bios_hotkey_table->keymap[i];
312                 u16 keycode = bios_entry->keycode < 256 ?
313                                     bios_to_linux_keycode[bios_entry->keycode] :
314                                     KEY_RESERVED;
315
316                 if (keycode == KEY_KBDILLUMTOGGLE)
317                         keymap[i].type = KE_IGNORE;
318                 else
319                         keymap[i].type = KE_KEY;
320                 keymap[i].code = bios_entry->scancode;
321                 keymap[i].keycode = keycode;
322         }
323
324         keymap[hotkey_num].type = KE_END;
325
326         return keymap;
327 }
328
329 static int __init dell_wmi_input_setup(void)
330 {
331         int err;
332
333         dell_wmi_input_dev = input_allocate_device();
334         if (!dell_wmi_input_dev)
335                 return -ENOMEM;
336
337         dell_wmi_input_dev->name = "Dell WMI hotkeys";
338         dell_wmi_input_dev->phys = "wmi/input0";
339         dell_wmi_input_dev->id.bustype = BUS_HOST;
340
341         if (dell_new_hk_type) {
342                 const struct key_entry *keymap = dell_wmi_prepare_new_keymap();
343                 if (!keymap) {
344                         err = -ENOMEM;
345                         goto err_free_dev;
346                 }
347
348                 err = sparse_keymap_setup(dell_wmi_input_dev, keymap, NULL);
349
350                 /*
351                  * Sparse keymap library makes a copy of keymap so we
352                  * don't need the original one that was allocated.
353                  */
354                 kfree(keymap);
355         } else {
356                 err = sparse_keymap_setup(dell_wmi_input_dev,
357                                           dell_wmi_legacy_keymap, NULL);
358         }
359         if (err)
360                 goto err_free_dev;
361
362         err = input_register_device(dell_wmi_input_dev);
363         if (err)
364                 goto err_free_keymap;
365
366         return 0;
367
368  err_free_keymap:
369         sparse_keymap_free(dell_wmi_input_dev);
370  err_free_dev:
371         input_free_device(dell_wmi_input_dev);
372         return err;
373 }
374
375 static void dell_wmi_input_destroy(void)
376 {
377         sparse_keymap_free(dell_wmi_input_dev);
378         input_unregister_device(dell_wmi_input_dev);
379 }
380
381 static void __init find_hk_type(const struct dmi_header *dm, void *dummy)
382 {
383         if (dm->type == 0xb2 && dm->length > 6) {
384                 dell_new_hk_type = true;
385                 dell_bios_hotkey_table =
386                         container_of(dm, struct dell_bios_hotkey_table, header);
387         }
388 }
389
390 static int __init dell_wmi_init(void)
391 {
392         int err;
393         acpi_status status;
394
395         if (!wmi_has_guid(DELL_EVENT_GUID)) {
396                 pr_warn("No known WMI GUID found\n");
397                 return -ENODEV;
398         }
399
400         dmi_walk(find_hk_type, NULL);
401         acpi_video = acpi_video_get_backlight_type() != acpi_backlight_vendor;
402
403         err = dell_wmi_input_setup();
404         if (err)
405                 return err;
406
407         status = wmi_install_notify_handler(DELL_EVENT_GUID,
408                                          dell_wmi_notify, NULL);
409         if (ACPI_FAILURE(status)) {
410                 dell_wmi_input_destroy();
411                 pr_err("Unable to register notify handler - %d\n", status);
412                 return -ENODEV;
413         }
414
415         return 0;
416 }
417 module_init(dell_wmi_init);
418
419 static void __exit dell_wmi_exit(void)
420 {
421         wmi_remove_notify_handler(DELL_EVENT_GUID);
422         dell_wmi_input_destroy();
423 }
424 module_exit(dell_wmi_exit);