Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/trivial
[cascardo/linux.git] / 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 MODULE_ALIAS("wmi:"DELL_EVENT_GUID);
47
48 /*
49  * Certain keys are flagged as KE_IGNORE. All of these are either
50  * notifications (rather than requests for change) or are also sent
51  * via the keyboard controller so should not be sent again.
52  */
53
54 static const struct key_entry dell_wmi_legacy_keymap[] __initconst = {
55         { KE_IGNORE, 0x003a, { KEY_CAPSLOCK } },
56
57         { KE_KEY, 0xe045, { KEY_PROG1 } },
58         { KE_KEY, 0xe009, { KEY_EJECTCD } },
59
60         /* These also contain the brightness level at offset 6 */
61         { KE_KEY, 0xe006, { KEY_BRIGHTNESSUP } },
62         { KE_KEY, 0xe005, { KEY_BRIGHTNESSDOWN } },
63
64         /* Battery health status button */
65         { KE_KEY, 0xe007, { KEY_BATTERY } },
66
67         /* Radio devices state change */
68         { KE_IGNORE, 0xe008, { KEY_RFKILL } },
69
70         /* The next device is at offset 6, the active devices are at
71            offset 8 and the attached devices at offset 10 */
72         { KE_KEY, 0xe00b, { KEY_SWITCHVIDEOMODE } },
73
74         { KE_IGNORE, 0xe00c, { KEY_KBDILLUMTOGGLE } },
75
76         /* BIOS error detected */
77         { KE_IGNORE, 0xe00d, { KEY_RESERVED } },
78
79         /* Wifi Catcher */
80         { KE_KEY, 0xe011, {KEY_PROG2 } },
81
82         /* Ambient light sensor toggle */
83         { KE_IGNORE, 0xe013, { KEY_RESERVED } },
84
85         { KE_IGNORE, 0xe020, { KEY_MUTE } },
86
87         /* Shortcut and audio panel keys */
88         { KE_IGNORE, 0xe025, { KEY_RESERVED } },
89         { KE_IGNORE, 0xe026, { KEY_RESERVED } },
90
91         { KE_IGNORE, 0xe02e, { KEY_VOLUMEDOWN } },
92         { KE_IGNORE, 0xe030, { KEY_VOLUMEUP } },
93         { KE_IGNORE, 0xe033, { KEY_KBDILLUMUP } },
94         { KE_IGNORE, 0xe034, { KEY_KBDILLUMDOWN } },
95         { KE_IGNORE, 0xe03a, { KEY_CAPSLOCK } },
96         { KE_IGNORE, 0xe045, { KEY_NUMLOCK } },
97         { KE_IGNORE, 0xe046, { KEY_SCROLLLOCK } },
98         { KE_IGNORE, 0xe0f7, { KEY_MUTE } },
99         { KE_IGNORE, 0xe0f8, { KEY_VOLUMEDOWN } },
100         { KE_IGNORE, 0xe0f9, { KEY_VOLUMEUP } },
101         { KE_END, 0 }
102 };
103
104 static bool dell_new_hk_type;
105
106 struct dell_bios_keymap_entry {
107         u16 scancode;
108         u16 keycode;
109 };
110
111 struct dell_bios_hotkey_table {
112         struct dmi_header header;
113         struct dell_bios_keymap_entry keymap[];
114
115 };
116
117 static const struct dell_bios_hotkey_table *dell_bios_hotkey_table;
118
119 static const u16 bios_to_linux_keycode[256] __initconst = {
120
121         KEY_MEDIA,      KEY_NEXTSONG,   KEY_PLAYPAUSE, KEY_PREVIOUSSONG,
122         KEY_STOPCD,     KEY_UNKNOWN,    KEY_UNKNOWN,    KEY_UNKNOWN,
123         KEY_WWW,        KEY_UNKNOWN,    KEY_VOLUMEDOWN, KEY_MUTE,
124         KEY_VOLUMEUP,   KEY_UNKNOWN,    KEY_BATTERY,    KEY_EJECTCD,
125         KEY_UNKNOWN,    KEY_SLEEP,      KEY_PROG1, KEY_BRIGHTNESSDOWN,
126         KEY_BRIGHTNESSUP,       KEY_UNKNOWN,    KEY_KBDILLUMTOGGLE,
127         KEY_UNKNOWN,    KEY_SWITCHVIDEOMODE,    KEY_UNKNOWN, KEY_UNKNOWN,
128         KEY_SWITCHVIDEOMODE,    KEY_UNKNOWN,    KEY_UNKNOWN, KEY_PROG2,
129         KEY_UNKNOWN,    KEY_UNKNOWN,    KEY_UNKNOWN,    KEY_UNKNOWN,
130         KEY_UNKNOWN,    KEY_UNKNOWN,    KEY_UNKNOWN,    KEY_MICMUTE,
131         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
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, KEY_PROG3
141 };
142
143 static struct input_dev *dell_wmi_input_dev;
144
145 static void dell_wmi_process_key(int reported_key)
146 {
147         const struct key_entry *key;
148
149         key = sparse_keymap_entry_from_scancode(dell_wmi_input_dev,
150                                                 reported_key);
151         if (!key) {
152                 pr_info("Unknown key %x pressed\n", reported_key);
153                 return;
154         }
155
156         pr_debug("Key %x pressed\n", reported_key);
157
158         /* Don't report brightness notifications that will also come via ACPI */
159         if ((key->keycode == KEY_BRIGHTNESSUP ||
160              key->keycode == KEY_BRIGHTNESSDOWN) &&
161             acpi_video_handles_brightness_key_presses())
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
401         err = dell_wmi_input_setup();
402         if (err)
403                 return err;
404
405         status = wmi_install_notify_handler(DELL_EVENT_GUID,
406                                          dell_wmi_notify, NULL);
407         if (ACPI_FAILURE(status)) {
408                 dell_wmi_input_destroy();
409                 pr_err("Unable to register notify handler - %d\n", status);
410                 return -ENODEV;
411         }
412
413         return 0;
414 }
415 module_init(dell_wmi_init);
416
417 static void __exit dell_wmi_exit(void)
418 {
419         wmi_remove_notify_handler(DELL_EVENT_GUID);
420         dell_wmi_input_destroy();
421 }
422 module_exit(dell_wmi_exit);