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