net: Compute protocol sequence numbers and fragment IDs using MD5.
[pandora-kernel.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 <acpi/acpi_drivers.h>
36 #include <linux/acpi.h>
37 #include <linux/string.h>
38 #include <linux/dmi.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_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         /* This is actually for all radios. Although physically a
68          * switch, the notification does not provide an indication of
69          * state and so it should be reported as a key */
70         { KE_KEY, 0xe008, { KEY_WLAN } },
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         { KE_IGNORE, 0xe02e, { KEY_VOLUMEDOWN } },
89         { KE_IGNORE, 0xe030, { KEY_VOLUMEUP } },
90         { KE_IGNORE, 0xe033, { KEY_KBDILLUMUP } },
91         { KE_IGNORE, 0xe034, { KEY_KBDILLUMDOWN } },
92         { KE_IGNORE, 0xe03a, { KEY_CAPSLOCK } },
93         { KE_IGNORE, 0xe045, { KEY_NUMLOCK } },
94         { KE_IGNORE, 0xe046, { KEY_SCROLLLOCK } },
95         { KE_END, 0 }
96 };
97
98 static bool dell_new_hk_type;
99
100 struct dell_bios_keymap_entry {
101         u16 scancode;
102         u16 keycode;
103 };
104
105 struct dell_bios_hotkey_table {
106         struct dmi_header header;
107         struct dell_bios_keymap_entry keymap[];
108
109 };
110
111 static const struct dell_bios_hotkey_table *dell_bios_hotkey_table;
112
113 static const u16 bios_to_linux_keycode[256] __initconst = {
114
115         KEY_MEDIA,      KEY_NEXTSONG,   KEY_PLAYPAUSE, KEY_PREVIOUSSONG,
116         KEY_STOPCD,     KEY_UNKNOWN,    KEY_UNKNOWN,    KEY_UNKNOWN,
117         KEY_WWW,        KEY_UNKNOWN,    KEY_VOLUMEDOWN, KEY_MUTE,
118         KEY_VOLUMEUP,   KEY_UNKNOWN,    KEY_BATTERY,    KEY_EJECTCD,
119         KEY_UNKNOWN,    KEY_SLEEP,      KEY_PROG1, KEY_BRIGHTNESSDOWN,
120         KEY_BRIGHTNESSUP,       KEY_UNKNOWN,    KEY_KBDILLUMTOGGLE,
121         KEY_UNKNOWN,    KEY_SWITCHVIDEOMODE,    KEY_UNKNOWN, KEY_UNKNOWN,
122         KEY_SWITCHVIDEOMODE,    KEY_UNKNOWN,    KEY_UNKNOWN, KEY_PROG2,
123         KEY_UNKNOWN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
124         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
125         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
126         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
127         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
128         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
129         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
130         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
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,
133         KEY_PROG3
134 };
135
136 static struct input_dev *dell_wmi_input_dev;
137
138 static void dell_wmi_notify(u32 value, void *context)
139 {
140         struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
141         union acpi_object *obj;
142         acpi_status status;
143
144         status = wmi_get_event_data(value, &response);
145         if (status != AE_OK) {
146                 pr_info("bad event status 0x%x\n", status);
147                 return;
148         }
149
150         obj = (union acpi_object *)response.pointer;
151
152         if (obj && obj->type == ACPI_TYPE_BUFFER) {
153                 const struct key_entry *key;
154                 int reported_key;
155                 u16 *buffer_entry = (u16 *)obj->buffer.pointer;
156
157                 if (dell_new_hk_type && (buffer_entry[1] != 0x10)) {
158                         pr_info("Received unknown WMI event (0x%x)\n",
159                                 buffer_entry[1]);
160                         kfree(obj);
161                         return;
162                 }
163
164                 if (dell_new_hk_type || buffer_entry[1] == 0x0)
165                         reported_key = (int)buffer_entry[2];
166                 else
167                         reported_key = (int)buffer_entry[1] & 0xffff;
168
169                 key = sparse_keymap_entry_from_scancode(dell_wmi_input_dev,
170                                                         reported_key);
171                 if (!key) {
172                         pr_info("Unknown key %x pressed\n", reported_key);
173                 } else if ((key->keycode == KEY_BRIGHTNESSUP ||
174                             key->keycode == KEY_BRIGHTNESSDOWN) && acpi_video) {
175                         /* Don't report brightness notifications that will also
176                          * come via ACPI */
177                         ;
178                 } else {
179                         sparse_keymap_report_entry(dell_wmi_input_dev, key,
180                                                    1, true);
181                 }
182         }
183         kfree(obj);
184 }
185
186 static const struct key_entry * __init dell_wmi_prepare_new_keymap(void)
187 {
188         int hotkey_num = (dell_bios_hotkey_table->header.length - 4) /
189                                 sizeof(struct dell_bios_keymap_entry);
190         struct key_entry *keymap;
191         int i;
192
193         keymap = kcalloc(hotkey_num + 1, sizeof(struct key_entry), GFP_KERNEL);
194         if (!keymap)
195                 return NULL;
196
197         for (i = 0; i < hotkey_num; i++) {
198                 const struct dell_bios_keymap_entry *bios_entry =
199                                         &dell_bios_hotkey_table->keymap[i];
200                 keymap[i].type = KE_KEY;
201                 keymap[i].code = bios_entry->scancode;
202                 keymap[i].keycode = bios_entry->keycode < 256 ?
203                                     bios_to_linux_keycode[bios_entry->keycode] :
204                                     KEY_RESERVED;
205         }
206
207         keymap[hotkey_num].type = KE_END;
208
209         return keymap;
210 }
211
212 static int __init dell_wmi_input_setup(void)
213 {
214         int err;
215
216         dell_wmi_input_dev = input_allocate_device();
217         if (!dell_wmi_input_dev)
218                 return -ENOMEM;
219
220         dell_wmi_input_dev->name = "Dell WMI hotkeys";
221         dell_wmi_input_dev->phys = "wmi/input0";
222         dell_wmi_input_dev->id.bustype = BUS_HOST;
223
224         if (dell_new_hk_type) {
225                 const struct key_entry *keymap = dell_wmi_prepare_new_keymap();
226                 if (!keymap) {
227                         err = -ENOMEM;
228                         goto err_free_dev;
229                 }
230
231                 err = sparse_keymap_setup(dell_wmi_input_dev, keymap, NULL);
232
233                 /*
234                  * Sparse keymap library makes a copy of keymap so we
235                  * don't need the original one that was allocated.
236                  */
237                 kfree(keymap);
238         } else {
239                 err = sparse_keymap_setup(dell_wmi_input_dev,
240                                           dell_wmi_legacy_keymap, NULL);
241         }
242         if (err)
243                 goto err_free_dev;
244
245         err = input_register_device(dell_wmi_input_dev);
246         if (err)
247                 goto err_free_keymap;
248
249         return 0;
250
251  err_free_keymap:
252         sparse_keymap_free(dell_wmi_input_dev);
253  err_free_dev:
254         input_free_device(dell_wmi_input_dev);
255         return err;
256 }
257
258 static void dell_wmi_input_destroy(void)
259 {
260         sparse_keymap_free(dell_wmi_input_dev);
261         input_unregister_device(dell_wmi_input_dev);
262 }
263
264 static void __init find_hk_type(const struct dmi_header *dm, void *dummy)
265 {
266         if (dm->type == 0xb2 && dm->length > 6) {
267                 dell_new_hk_type = true;
268                 dell_bios_hotkey_table =
269                         container_of(dm, struct dell_bios_hotkey_table, header);
270         }
271 }
272
273 static int __init dell_wmi_init(void)
274 {
275         int err;
276         acpi_status status;
277
278         if (!wmi_has_guid(DELL_EVENT_GUID)) {
279                 pr_warn("No known WMI GUID found\n");
280                 return -ENODEV;
281         }
282
283         dmi_walk(find_hk_type, NULL);
284         acpi_video = acpi_video_backlight_support();
285
286         err = dell_wmi_input_setup();
287         if (err)
288                 return err;
289
290         status = wmi_install_notify_handler(DELL_EVENT_GUID,
291                                          dell_wmi_notify, NULL);
292         if (ACPI_FAILURE(status)) {
293                 dell_wmi_input_destroy();
294                 pr_err("Unable to register notify handler - %d\n", status);
295                 return -ENODEV;
296         }
297
298         return 0;
299 }
300 module_init(dell_wmi_init);
301
302 static void __exit dell_wmi_exit(void)
303 {
304         wmi_remove_notify_handler(DELL_EVENT_GUID);
305         dell_wmi_input_destroy();
306 }
307 module_exit(dell_wmi_exit);