Loading...
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
40MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
41MODULE_DESCRIPTION("Dell laptop WMI hotkeys driver");
42MODULE_LICENSE("GPL");
43
44#define DELL_EVENT_GUID "9DBB5994-A997-11DA-B012-B622A1EF5492"
45
46static int acpi_video;
47
48MODULE_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
56static 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 /* This is actually for all radios. Although physically a
70 * switch, the notification does not provide an indication of
71 * state and so it should be reported as a key */
72 { KE_KEY, 0xe008, { KEY_WLAN } },
73
74 /* The next device is at offset 6, the active devices are at
75 offset 8 and the attached devices at offset 10 */
76 { KE_KEY, 0xe00b, { KEY_SWITCHVIDEOMODE } },
77
78 { KE_IGNORE, 0xe00c, { KEY_KBDILLUMTOGGLE } },
79
80 /* BIOS error detected */
81 { KE_IGNORE, 0xe00d, { KEY_RESERVED } },
82
83 /* Wifi Catcher */
84 { KE_KEY, 0xe011, {KEY_PROG2 } },
85
86 /* Ambient light sensor toggle */
87 { KE_IGNORE, 0xe013, { KEY_RESERVED } },
88
89 { KE_IGNORE, 0xe020, { KEY_MUTE } },
90
91 /* Shortcut and audio panel keys */
92 { KE_IGNORE, 0xe025, { KEY_RESERVED } },
93 { KE_IGNORE, 0xe026, { KEY_RESERVED } },
94
95 { KE_IGNORE, 0xe02e, { KEY_VOLUMEDOWN } },
96 { KE_IGNORE, 0xe030, { KEY_VOLUMEUP } },
97 { KE_IGNORE, 0xe033, { KEY_KBDILLUMUP } },
98 { KE_IGNORE, 0xe034, { KEY_KBDILLUMDOWN } },
99 { KE_IGNORE, 0xe03a, { KEY_CAPSLOCK } },
100 { KE_IGNORE, 0xe045, { KEY_NUMLOCK } },
101 { KE_IGNORE, 0xe046, { KEY_SCROLLLOCK } },
102 { KE_IGNORE, 0xe0f7, { KEY_MUTE } },
103 { KE_IGNORE, 0xe0f8, { KEY_VOLUMEDOWN } },
104 { KE_IGNORE, 0xe0f9, { KEY_VOLUMEUP } },
105 { KE_END, 0 }
106};
107
108static bool dell_new_hk_type;
109
110struct dell_bios_keymap_entry {
111 u16 scancode;
112 u16 keycode;
113};
114
115struct dell_bios_hotkey_table {
116 struct dmi_header header;
117 struct dell_bios_keymap_entry keymap[];
118
119};
120
121static const struct dell_bios_hotkey_table *dell_bios_hotkey_table;
122
123static const u16 bios_to_linux_keycode[256] __initconst = {
124
125 KEY_MEDIA, KEY_NEXTSONG, KEY_PLAYPAUSE, KEY_PREVIOUSSONG,
126 KEY_STOPCD, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
127 KEY_WWW, KEY_UNKNOWN, KEY_VOLUMEDOWN, KEY_MUTE,
128 KEY_VOLUMEUP, KEY_UNKNOWN, KEY_BATTERY, KEY_EJECTCD,
129 KEY_UNKNOWN, KEY_SLEEP, KEY_PROG1, KEY_BRIGHTNESSDOWN,
130 KEY_BRIGHTNESSUP, KEY_UNKNOWN, KEY_KBDILLUMTOGGLE,
131 KEY_UNKNOWN, KEY_SWITCHVIDEOMODE, KEY_UNKNOWN, KEY_UNKNOWN,
132 KEY_SWITCHVIDEOMODE, KEY_UNKNOWN, KEY_UNKNOWN, KEY_PROG2,
133 KEY_UNKNOWN, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
143 KEY_PROG3
144};
145
146static struct input_dev *dell_wmi_input_dev;
147
148static void dell_wmi_notify(u32 value, void *context)
149{
150 struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
151 union acpi_object *obj;
152 acpi_status status;
153
154 status = wmi_get_event_data(value, &response);
155 if (status != AE_OK) {
156 pr_info("bad event status 0x%x\n", status);
157 return;
158 }
159
160 obj = (union acpi_object *)response.pointer;
161
162 if (obj && obj->type == ACPI_TYPE_BUFFER) {
163 const struct key_entry *key;
164 int reported_key;
165 u16 *buffer_entry = (u16 *)obj->buffer.pointer;
166
167 if (dell_new_hk_type && (buffer_entry[1] != 0x10)) {
168 pr_info("Received unknown WMI event (0x%x)\n",
169 buffer_entry[1]);
170 kfree(obj);
171 return;
172 }
173
174 if (dell_new_hk_type || buffer_entry[1] == 0x0)
175 reported_key = (int)buffer_entry[2];
176 else
177 reported_key = (int)buffer_entry[1] & 0xffff;
178
179 key = sparse_keymap_entry_from_scancode(dell_wmi_input_dev,
180 reported_key);
181 if (!key) {
182 pr_info("Unknown key %x pressed\n", reported_key);
183 } else if ((key->keycode == KEY_BRIGHTNESSUP ||
184 key->keycode == KEY_BRIGHTNESSDOWN) && acpi_video) {
185 /* Don't report brightness notifications that will also
186 * come via ACPI */
187 ;
188 } else {
189 sparse_keymap_report_entry(dell_wmi_input_dev, key,
190 1, true);
191 }
192 }
193 kfree(obj);
194}
195
196static const struct key_entry * __init dell_wmi_prepare_new_keymap(void)
197{
198 int hotkey_num = (dell_bios_hotkey_table->header.length - 4) /
199 sizeof(struct dell_bios_keymap_entry);
200 struct key_entry *keymap;
201 int i;
202
203 keymap = kcalloc(hotkey_num + 1, sizeof(struct key_entry), GFP_KERNEL);
204 if (!keymap)
205 return NULL;
206
207 for (i = 0; i < hotkey_num; i++) {
208 const struct dell_bios_keymap_entry *bios_entry =
209 &dell_bios_hotkey_table->keymap[i];
210 keymap[i].type = KE_KEY;
211 keymap[i].code = bios_entry->scancode;
212 keymap[i].keycode = bios_entry->keycode < 256 ?
213 bios_to_linux_keycode[bios_entry->keycode] :
214 KEY_RESERVED;
215 }
216
217 keymap[hotkey_num].type = KE_END;
218
219 return keymap;
220}
221
222static int __init dell_wmi_input_setup(void)
223{
224 int err;
225
226 dell_wmi_input_dev = input_allocate_device();
227 if (!dell_wmi_input_dev)
228 return -ENOMEM;
229
230 dell_wmi_input_dev->name = "Dell WMI hotkeys";
231 dell_wmi_input_dev->phys = "wmi/input0";
232 dell_wmi_input_dev->id.bustype = BUS_HOST;
233
234 if (dell_new_hk_type) {
235 const struct key_entry *keymap = dell_wmi_prepare_new_keymap();
236 if (!keymap) {
237 err = -ENOMEM;
238 goto err_free_dev;
239 }
240
241 err = sparse_keymap_setup(dell_wmi_input_dev, keymap, NULL);
242
243 /*
244 * Sparse keymap library makes a copy of keymap so we
245 * don't need the original one that was allocated.
246 */
247 kfree(keymap);
248 } else {
249 err = sparse_keymap_setup(dell_wmi_input_dev,
250 dell_wmi_legacy_keymap, NULL);
251 }
252 if (err)
253 goto err_free_dev;
254
255 err = input_register_device(dell_wmi_input_dev);
256 if (err)
257 goto err_free_keymap;
258
259 return 0;
260
261 err_free_keymap:
262 sparse_keymap_free(dell_wmi_input_dev);
263 err_free_dev:
264 input_free_device(dell_wmi_input_dev);
265 return err;
266}
267
268static void dell_wmi_input_destroy(void)
269{
270 sparse_keymap_free(dell_wmi_input_dev);
271 input_unregister_device(dell_wmi_input_dev);
272}
273
274static void __init find_hk_type(const struct dmi_header *dm, void *dummy)
275{
276 if (dm->type == 0xb2 && dm->length > 6) {
277 dell_new_hk_type = true;
278 dell_bios_hotkey_table =
279 container_of(dm, struct dell_bios_hotkey_table, header);
280 }
281}
282
283static int __init dell_wmi_init(void)
284{
285 int err;
286 acpi_status status;
287
288 if (!wmi_has_guid(DELL_EVENT_GUID)) {
289 pr_warn("No known WMI GUID found\n");
290 return -ENODEV;
291 }
292
293 dmi_walk(find_hk_type, NULL);
294 acpi_video = acpi_video_backlight_support();
295
296 err = dell_wmi_input_setup();
297 if (err)
298 return err;
299
300 status = wmi_install_notify_handler(DELL_EVENT_GUID,
301 dell_wmi_notify, NULL);
302 if (ACPI_FAILURE(status)) {
303 dell_wmi_input_destroy();
304 pr_err("Unable to register notify handler - %d\n", status);
305 return -ENODEV;
306 }
307
308 return 0;
309}
310module_init(dell_wmi_init);
311
312static void __exit dell_wmi_exit(void)
313{
314 wmi_remove_notify_handler(DELL_EVENT_GUID);
315 dell_wmi_input_destroy();
316}
317module_exit(dell_wmi_exit);
1/*
2 * Dell WMI hotkeys
3 *
4 * Copyright (C) 2008 Red Hat <mjg@redhat.com>
5 * Copyright (C) 2014-2015 Pali Rohár <pali.rohar@gmail.com>
6 *
7 * Portions based on wistron_btns.c:
8 * Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz>
9 * Copyright (C) 2005 Bernhard Rosenkraenzer <bero@arklinux.org>
10 * Copyright (C) 2005 Dmitry Torokhov <dtor@mail.ru>
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 */
26
27#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
28
29#include <linux/kernel.h>
30#include <linux/module.h>
31#include <linux/init.h>
32#include <linux/slab.h>
33#include <linux/types.h>
34#include <linux/input.h>
35#include <linux/input/sparse-keymap.h>
36#include <linux/acpi.h>
37#include <linux/string.h>
38#include <linux/dmi.h>
39#include <acpi/video.h>
40#include "dell-smbios.h"
41
42MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
43MODULE_AUTHOR("Pali Rohár <pali.rohar@gmail.com>");
44MODULE_DESCRIPTION("Dell laptop WMI hotkeys driver");
45MODULE_LICENSE("GPL");
46
47#define DELL_EVENT_GUID "9DBB5994-A997-11DA-B012-B622A1EF5492"
48#define DELL_DESCRIPTOR_GUID "8D9DDCBC-A997-11DA-B012-B622A1EF5492"
49
50static u32 dell_wmi_interface_version;
51static bool wmi_requires_smbios_request;
52
53MODULE_ALIAS("wmi:"DELL_EVENT_GUID);
54MODULE_ALIAS("wmi:"DELL_DESCRIPTOR_GUID);
55
56static int __init dmi_matched(const struct dmi_system_id *dmi)
57{
58 wmi_requires_smbios_request = 1;
59 return 1;
60}
61
62static const struct dmi_system_id dell_wmi_smbios_list[] __initconst = {
63 {
64 .callback = dmi_matched,
65 .ident = "Dell Inspiron M5110",
66 .matches = {
67 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
68 DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron M5110"),
69 },
70 },
71 {
72 .callback = dmi_matched,
73 .ident = "Dell Vostro V131",
74 .matches = {
75 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
76 DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V131"),
77 },
78 },
79 { }
80};
81
82/*
83 * Keymap for WMI events of type 0x0000
84 *
85 * Certain keys are flagged as KE_IGNORE. All of these are either
86 * notifications (rather than requests for change) or are also sent
87 * via the keyboard controller so should not be sent again.
88 */
89static const struct key_entry dell_wmi_keymap_type_0000[] __initconst = {
90 { KE_IGNORE, 0x003a, { KEY_CAPSLOCK } },
91
92 /* Key code is followed by brightness level */
93 { KE_KEY, 0xe005, { KEY_BRIGHTNESSDOWN } },
94 { KE_KEY, 0xe006, { KEY_BRIGHTNESSUP } },
95
96 /* Battery health status button */
97 { KE_KEY, 0xe007, { KEY_BATTERY } },
98
99 /* Radio devices state change, key code is followed by other values */
100 { KE_IGNORE, 0xe008, { KEY_RFKILL } },
101
102 { KE_KEY, 0xe009, { KEY_EJECTCD } },
103
104 /* Key code is followed by: next, active and attached devices */
105 { KE_KEY, 0xe00b, { KEY_SWITCHVIDEOMODE } },
106
107 /* Key code is followed by keyboard illumination level */
108 { KE_IGNORE, 0xe00c, { KEY_KBDILLUMTOGGLE } },
109
110 /* BIOS error detected */
111 { KE_IGNORE, 0xe00d, { KEY_RESERVED } },
112
113 /* Battery was removed or inserted */
114 { KE_IGNORE, 0xe00e, { KEY_RESERVED } },
115
116 /* Wifi Catcher */
117 { KE_KEY, 0xe011, { KEY_WLAN } },
118
119 /* Ambient light sensor toggle */
120 { KE_IGNORE, 0xe013, { KEY_RESERVED } },
121
122 { KE_IGNORE, 0xe020, { KEY_MUTE } },
123
124 /* Unknown, defined in ACPI DSDT */
125 /* { KE_IGNORE, 0xe023, { KEY_RESERVED } }, */
126
127 /* Untested, Dell Instant Launch key on Inspiron 7520 */
128 /* { KE_IGNORE, 0xe024, { KEY_RESERVED } }, */
129
130 /* Dell Instant Launch key */
131 { KE_KEY, 0xe025, { KEY_PROG4 } },
132
133 /* Audio panel key */
134 { KE_IGNORE, 0xe026, { KEY_RESERVED } },
135
136 /* LCD Display On/Off Control key */
137 { KE_KEY, 0xe027, { KEY_DISPLAYTOGGLE } },
138
139 /* Untested, Multimedia key on Dell Vostro 3560 */
140 /* { KE_IGNORE, 0xe028, { KEY_RESERVED } }, */
141
142 /* Dell Instant Launch key */
143 { KE_KEY, 0xe029, { KEY_PROG4 } },
144
145 /* Untested, Windows Mobility Center button on Inspiron 7520 */
146 /* { KE_IGNORE, 0xe02a, { KEY_RESERVED } }, */
147
148 /* Unknown, defined in ACPI DSDT */
149 /* { KE_IGNORE, 0xe02b, { KEY_RESERVED } }, */
150
151 /* Untested, Dell Audio With Preset Switch button on Inspiron 7520 */
152 /* { KE_IGNORE, 0xe02c, { KEY_RESERVED } }, */
153
154 { KE_IGNORE, 0xe02e, { KEY_VOLUMEDOWN } },
155 { KE_IGNORE, 0xe030, { KEY_VOLUMEUP } },
156 { KE_IGNORE, 0xe033, { KEY_KBDILLUMUP } },
157 { KE_IGNORE, 0xe034, { KEY_KBDILLUMDOWN } },
158 { KE_IGNORE, 0xe03a, { KEY_CAPSLOCK } },
159
160 /* NIC Link is Up */
161 { KE_IGNORE, 0xe043, { KEY_RESERVED } },
162
163 /* NIC Link is Down */
164 { KE_IGNORE, 0xe044, { KEY_RESERVED } },
165
166 /*
167 * This entry is very suspicious!
168 * Originally Matthew Garrett created this dell-wmi driver specially for
169 * "button with a picture of a battery" which has event code 0xe045.
170 * Later Mario Limonciello from Dell told us that event code 0xe045 is
171 * reported by Num Lock and should be ignored because key is send also
172 * by keyboard controller.
173 * So for now we will ignore this event to prevent potential double
174 * Num Lock key press.
175 */
176 { KE_IGNORE, 0xe045, { KEY_NUMLOCK } },
177
178 /* Scroll lock and also going to tablet mode on portable devices */
179 { KE_IGNORE, 0xe046, { KEY_SCROLLLOCK } },
180
181 /* Untested, going from tablet mode on portable devices */
182 /* { KE_IGNORE, 0xe047, { KEY_RESERVED } }, */
183
184 /* Dell Support Center key */
185 { KE_IGNORE, 0xe06e, { KEY_RESERVED } },
186
187 { KE_IGNORE, 0xe0f7, { KEY_MUTE } },
188 { KE_IGNORE, 0xe0f8, { KEY_VOLUMEDOWN } },
189 { KE_IGNORE, 0xe0f9, { KEY_VOLUMEUP } },
190};
191
192struct dell_bios_keymap_entry {
193 u16 scancode;
194 u16 keycode;
195};
196
197struct dell_bios_hotkey_table {
198 struct dmi_header header;
199 struct dell_bios_keymap_entry keymap[];
200
201};
202
203struct dell_dmi_results {
204 int err;
205 int keymap_size;
206 struct key_entry *keymap;
207};
208
209/* Uninitialized entries here are KEY_RESERVED == 0. */
210static const u16 bios_to_linux_keycode[256] __initconst = {
211 [0] = KEY_MEDIA,
212 [1] = KEY_NEXTSONG,
213 [2] = KEY_PLAYPAUSE,
214 [3] = KEY_PREVIOUSSONG,
215 [4] = KEY_STOPCD,
216 [5] = KEY_UNKNOWN,
217 [6] = KEY_UNKNOWN,
218 [7] = KEY_UNKNOWN,
219 [8] = KEY_WWW,
220 [9] = KEY_UNKNOWN,
221 [10] = KEY_VOLUMEDOWN,
222 [11] = KEY_MUTE,
223 [12] = KEY_VOLUMEUP,
224 [13] = KEY_UNKNOWN,
225 [14] = KEY_BATTERY,
226 [15] = KEY_EJECTCD,
227 [16] = KEY_UNKNOWN,
228 [17] = KEY_SLEEP,
229 [18] = KEY_PROG1,
230 [19] = KEY_BRIGHTNESSDOWN,
231 [20] = KEY_BRIGHTNESSUP,
232 [21] = KEY_UNKNOWN,
233 [22] = KEY_KBDILLUMTOGGLE,
234 [23] = KEY_UNKNOWN,
235 [24] = KEY_SWITCHVIDEOMODE,
236 [25] = KEY_UNKNOWN,
237 [26] = KEY_UNKNOWN,
238 [27] = KEY_SWITCHVIDEOMODE,
239 [28] = KEY_UNKNOWN,
240 [29] = KEY_UNKNOWN,
241 [30] = KEY_PROG2,
242 [31] = KEY_UNKNOWN,
243 [32] = KEY_UNKNOWN,
244 [33] = KEY_UNKNOWN,
245 [34] = KEY_UNKNOWN,
246 [35] = KEY_UNKNOWN,
247 [36] = KEY_UNKNOWN,
248 [37] = KEY_UNKNOWN,
249 [38] = KEY_MICMUTE,
250 [255] = KEY_PROG3,
251};
252
253/*
254 * Keymap for WMI events of type 0x0010
255 *
256 * These are applied if the 0xB2 DMI hotkey table is present and doesn't
257 * override them.
258 */
259static const struct key_entry dell_wmi_keymap_type_0010[] __initconst = {
260 /* Fn-lock */
261 { KE_IGNORE, 0x151, { KEY_RESERVED } },
262
263 /* Change keyboard illumination */
264 { KE_IGNORE, 0x152, { KEY_KBDILLUMTOGGLE } },
265
266 /*
267 * Radio disable (notify only -- there is no model for which the
268 * WMI event is supposed to trigger an action).
269 */
270 { KE_IGNORE, 0x153, { KEY_RFKILL } },
271
272 /* RGB keyboard backlight control */
273 { KE_IGNORE, 0x154, { KEY_RESERVED } },
274
275 /* Stealth mode toggle */
276 { KE_IGNORE, 0x155, { KEY_RESERVED } },
277
278 /* Rugged magnetic dock attach/detach events */
279 { KE_IGNORE, 0x156, { KEY_RESERVED } },
280 { KE_IGNORE, 0x157, { KEY_RESERVED } },
281
282 /* Rugged programmable (P1/P2/P3 keys) */
283 { KE_KEY, 0x850, { KEY_PROG1 } },
284 { KE_KEY, 0x851, { KEY_PROG2 } },
285 { KE_KEY, 0x852, { KEY_PROG3 } },
286
287};
288
289/*
290 * Keymap for WMI events of type 0x0011
291 */
292static const struct key_entry dell_wmi_keymap_type_0011[] __initconst = {
293 /* Battery unplugged */
294 { KE_IGNORE, 0xfff0, { KEY_RESERVED } },
295
296 /* Battery inserted */
297 { KE_IGNORE, 0xfff1, { KEY_RESERVED } },
298
299 /* Keyboard backlight level changed */
300 { KE_IGNORE, 0x01e1, { KEY_RESERVED } },
301 { KE_IGNORE, 0x02ea, { KEY_RESERVED } },
302 { KE_IGNORE, 0x02eb, { KEY_RESERVED } },
303 { KE_IGNORE, 0x02ec, { KEY_RESERVED } },
304 { KE_IGNORE, 0x02f6, { KEY_RESERVED } },
305};
306
307static struct input_dev *dell_wmi_input_dev;
308
309static void dell_wmi_process_key(int type, int code)
310{
311 const struct key_entry *key;
312
313 key = sparse_keymap_entry_from_scancode(dell_wmi_input_dev,
314 (type << 16) | code);
315 if (!key) {
316 pr_info("Unknown key with type 0x%04x and code 0x%04x pressed\n",
317 type, code);
318 return;
319 }
320
321 pr_debug("Key with type 0x%04x and code 0x%04x pressed\n", type, code);
322
323 /* Don't report brightness notifications that will also come via ACPI */
324 if ((key->keycode == KEY_BRIGHTNESSUP ||
325 key->keycode == KEY_BRIGHTNESSDOWN) &&
326 acpi_video_handles_brightness_key_presses())
327 return;
328
329 if (type == 0x0000 && code == 0xe025 && !wmi_requires_smbios_request)
330 return;
331
332 sparse_keymap_report_entry(dell_wmi_input_dev, key, 1, true);
333}
334
335static void dell_wmi_notify(u32 value, void *context)
336{
337 struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
338 union acpi_object *obj;
339 acpi_status status;
340 acpi_size buffer_size;
341 u16 *buffer_entry, *buffer_end;
342 int len, i;
343
344 status = wmi_get_event_data(value, &response);
345 if (status != AE_OK) {
346 pr_warn("bad event status 0x%x\n", status);
347 return;
348 }
349
350 obj = (union acpi_object *)response.pointer;
351 if (!obj) {
352 pr_warn("no response\n");
353 return;
354 }
355
356 if (obj->type != ACPI_TYPE_BUFFER) {
357 pr_warn("bad response type %x\n", obj->type);
358 kfree(obj);
359 return;
360 }
361
362 pr_debug("Received WMI event (%*ph)\n",
363 obj->buffer.length, obj->buffer.pointer);
364
365 buffer_entry = (u16 *)obj->buffer.pointer;
366 buffer_size = obj->buffer.length/2;
367 buffer_end = buffer_entry + buffer_size;
368
369 /*
370 * BIOS/ACPI on devices with WMI interface version 0 does not clear
371 * buffer before filling it. So next time when BIOS/ACPI send WMI event
372 * which is smaller as previous then it contains garbage in buffer from
373 * previous event.
374 *
375 * BIOS/ACPI on devices with WMI interface version 1 clears buffer and
376 * sometimes send more events in buffer at one call.
377 *
378 * So to prevent reading garbage from buffer we will process only first
379 * one event on devices with WMI interface version 0.
380 */
381 if (dell_wmi_interface_version == 0 && buffer_entry < buffer_end)
382 if (buffer_end > buffer_entry + buffer_entry[0] + 1)
383 buffer_end = buffer_entry + buffer_entry[0] + 1;
384
385 while (buffer_entry < buffer_end) {
386
387 len = buffer_entry[0];
388 if (len == 0)
389 break;
390
391 len++;
392
393 if (buffer_entry + len > buffer_end) {
394 pr_warn("Invalid length of WMI event\n");
395 break;
396 }
397
398 pr_debug("Process buffer (%*ph)\n", len*2, buffer_entry);
399
400 switch (buffer_entry[1]) {
401 case 0x0000: /* One key pressed or event occurred */
402 if (len > 2)
403 dell_wmi_process_key(0x0000, buffer_entry[2]);
404 /* Other entries could contain additional information */
405 break;
406 case 0x0010: /* Sequence of keys pressed */
407 case 0x0011: /* Sequence of events occurred */
408 for (i = 2; i < len; ++i)
409 dell_wmi_process_key(buffer_entry[1],
410 buffer_entry[i]);
411 break;
412 default: /* Unknown event */
413 pr_info("Unknown WMI event type 0x%x\n",
414 (int)buffer_entry[1]);
415 break;
416 }
417
418 buffer_entry += len;
419
420 }
421
422 kfree(obj);
423}
424
425static bool have_scancode(u32 scancode, const struct key_entry *keymap, int len)
426{
427 int i;
428
429 for (i = 0; i < len; i++)
430 if (keymap[i].code == scancode)
431 return true;
432
433 return false;
434}
435
436static void __init handle_dmi_entry(const struct dmi_header *dm,
437 void *opaque)
438
439{
440 struct dell_dmi_results *results = opaque;
441 struct dell_bios_hotkey_table *table;
442 int hotkey_num, i, pos = 0;
443 struct key_entry *keymap;
444
445 if (results->err || results->keymap)
446 return; /* We already found the hotkey table. */
447
448 if (dm->type != 0xb2)
449 return;
450
451 table = container_of(dm, struct dell_bios_hotkey_table, header);
452
453 hotkey_num = (table->header.length -
454 sizeof(struct dell_bios_hotkey_table)) /
455 sizeof(struct dell_bios_keymap_entry);
456 if (hotkey_num < 1) {
457 /*
458 * Historically, dell-wmi would ignore a DMI entry of
459 * fewer than 7 bytes. Sizes between 4 and 8 bytes are
460 * nonsensical (both the header and all entries are 4
461 * bytes), so we approximate the old behavior by
462 * ignoring tables with fewer than one entry.
463 */
464 return;
465 }
466
467 keymap = kcalloc(hotkey_num, sizeof(struct key_entry), GFP_KERNEL);
468 if (!keymap) {
469 results->err = -ENOMEM;
470 return;
471 }
472
473 for (i = 0; i < hotkey_num; i++) {
474 const struct dell_bios_keymap_entry *bios_entry =
475 &table->keymap[i];
476
477 /* Uninitialized entries are 0 aka KEY_RESERVED. */
478 u16 keycode = (bios_entry->keycode <
479 ARRAY_SIZE(bios_to_linux_keycode)) ?
480 bios_to_linux_keycode[bios_entry->keycode] :
481 KEY_RESERVED;
482
483 /*
484 * Log if we find an entry in the DMI table that we don't
485 * understand. If this happens, we should figure out what
486 * the entry means and add it to bios_to_linux_keycode.
487 */
488 if (keycode == KEY_RESERVED) {
489 pr_info("firmware scancode 0x%x maps to unrecognized keycode 0x%x\n",
490 bios_entry->scancode, bios_entry->keycode);
491 continue;
492 }
493
494 if (keycode == KEY_KBDILLUMTOGGLE)
495 keymap[pos].type = KE_IGNORE;
496 else
497 keymap[pos].type = KE_KEY;
498 keymap[pos].code = bios_entry->scancode;
499 keymap[pos].keycode = keycode;
500
501 pos++;
502 }
503
504 results->keymap = keymap;
505 results->keymap_size = pos;
506}
507
508static int __init dell_wmi_input_setup(void)
509{
510 struct dell_dmi_results dmi_results = {};
511 struct key_entry *keymap;
512 int err, i, pos = 0;
513
514 dell_wmi_input_dev = input_allocate_device();
515 if (!dell_wmi_input_dev)
516 return -ENOMEM;
517
518 dell_wmi_input_dev->name = "Dell WMI hotkeys";
519 dell_wmi_input_dev->phys = "wmi/input0";
520 dell_wmi_input_dev->id.bustype = BUS_HOST;
521
522 if (dmi_walk(handle_dmi_entry, &dmi_results)) {
523 /*
524 * Historically, dell-wmi ignored dmi_walk errors. A failure
525 * is certainly surprising, but it probably just indicates
526 * a very old laptop.
527 */
528 pr_warn("no DMI; using the old-style hotkey interface\n");
529 }
530
531 if (dmi_results.err) {
532 err = dmi_results.err;
533 goto err_free_dev;
534 }
535
536 keymap = kcalloc(dmi_results.keymap_size +
537 ARRAY_SIZE(dell_wmi_keymap_type_0000) +
538 ARRAY_SIZE(dell_wmi_keymap_type_0010) +
539 ARRAY_SIZE(dell_wmi_keymap_type_0011) +
540 1,
541 sizeof(struct key_entry), GFP_KERNEL);
542 if (!keymap) {
543 kfree(dmi_results.keymap);
544 err = -ENOMEM;
545 goto err_free_dev;
546 }
547
548 /* Append table with events of type 0x0010 which comes from DMI */
549 for (i = 0; i < dmi_results.keymap_size; i++) {
550 keymap[pos] = dmi_results.keymap[i];
551 keymap[pos].code |= (0x0010 << 16);
552 pos++;
553 }
554
555 kfree(dmi_results.keymap);
556
557 /* Append table with extra events of type 0x0010 which are not in DMI */
558 for (i = 0; i < ARRAY_SIZE(dell_wmi_keymap_type_0010); i++) {
559 const struct key_entry *entry = &dell_wmi_keymap_type_0010[i];
560
561 /*
562 * Check if we've already found this scancode. This takes
563 * quadratic time, but it doesn't matter unless the list
564 * of extra keys gets very long.
565 */
566 if (dmi_results.keymap_size &&
567 have_scancode(entry->code | (0x0010 << 16),
568 keymap, dmi_results.keymap_size)
569 )
570 continue;
571
572 keymap[pos] = *entry;
573 keymap[pos].code |= (0x0010 << 16);
574 pos++;
575 }
576
577 /* Append table with events of type 0x0011 */
578 for (i = 0; i < ARRAY_SIZE(dell_wmi_keymap_type_0011); i++) {
579 keymap[pos] = dell_wmi_keymap_type_0011[i];
580 keymap[pos].code |= (0x0011 << 16);
581 pos++;
582 }
583
584 /*
585 * Now append also table with "legacy" events of type 0x0000. Some of
586 * them are reported also on laptops which have scancodes in DMI.
587 */
588 for (i = 0; i < ARRAY_SIZE(dell_wmi_keymap_type_0000); i++) {
589 keymap[pos] = dell_wmi_keymap_type_0000[i];
590 pos++;
591 }
592
593 keymap[pos].type = KE_END;
594
595 err = sparse_keymap_setup(dell_wmi_input_dev, keymap, NULL);
596 /*
597 * Sparse keymap library makes a copy of keymap so we don't need the
598 * original one that was allocated.
599 */
600 kfree(keymap);
601 if (err)
602 goto err_free_dev;
603
604 err = input_register_device(dell_wmi_input_dev);
605 if (err)
606 goto err_free_keymap;
607
608 return 0;
609
610 err_free_keymap:
611 sparse_keymap_free(dell_wmi_input_dev);
612 err_free_dev:
613 input_free_device(dell_wmi_input_dev);
614 return err;
615}
616
617static void dell_wmi_input_destroy(void)
618{
619 sparse_keymap_free(dell_wmi_input_dev);
620 input_unregister_device(dell_wmi_input_dev);
621}
622
623/*
624 * Descriptor buffer is 128 byte long and contains:
625 *
626 * Name Offset Length Value
627 * Vendor Signature 0 4 "DELL"
628 * Object Signature 4 4 " WMI"
629 * WMI Interface Version 8 4 <version>
630 * WMI buffer length 12 4 4096
631 */
632static int __init dell_wmi_check_descriptor_buffer(void)
633{
634 struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
635 union acpi_object *obj;
636 acpi_status status;
637 u32 *buffer;
638
639 status = wmi_query_block(DELL_DESCRIPTOR_GUID, 0, &out);
640 if (ACPI_FAILURE(status)) {
641 pr_err("Cannot read Dell descriptor buffer - %d\n", status);
642 return status;
643 }
644
645 obj = (union acpi_object *)out.pointer;
646 if (!obj) {
647 pr_err("Dell descriptor buffer is empty\n");
648 return -EINVAL;
649 }
650
651 if (obj->type != ACPI_TYPE_BUFFER) {
652 pr_err("Cannot read Dell descriptor buffer\n");
653 kfree(obj);
654 return -EINVAL;
655 }
656
657 if (obj->buffer.length != 128) {
658 pr_err("Dell descriptor buffer has invalid length (%d)\n",
659 obj->buffer.length);
660 if (obj->buffer.length < 16) {
661 kfree(obj);
662 return -EINVAL;
663 }
664 }
665
666 buffer = (u32 *)obj->buffer.pointer;
667
668 if (buffer[0] != 0x4C4C4544 && buffer[1] != 0x494D5720)
669 pr_warn("Dell descriptor buffer has invalid signature (%*ph)\n",
670 8, buffer);
671
672 if (buffer[2] != 0 && buffer[2] != 1)
673 pr_warn("Dell descriptor buffer has unknown version (%d)\n",
674 buffer[2]);
675
676 if (buffer[3] != 4096)
677 pr_warn("Dell descriptor buffer has invalid buffer length (%d)\n",
678 buffer[3]);
679
680 dell_wmi_interface_version = buffer[2];
681
682 pr_info("Detected Dell WMI interface version %u\n",
683 dell_wmi_interface_version);
684
685 kfree(obj);
686 return 0;
687}
688
689/*
690 * According to Dell SMBIOS documentation:
691 *
692 * 17 3 Application Program Registration
693 *
694 * cbArg1 Application ID 1 = 0x00010000
695 * cbArg2 Application ID 2
696 * QUICKSET/DCP = 0x51534554 "QSET"
697 * ALS Driver = 0x416c7353 "AlsS"
698 * Latitude ON = 0x4c6f6e52 "LonR"
699 * cbArg3 Application version or revision number
700 * cbArg4 0 = Unregister application
701 * 1 = Register application
702 * cbRes1 Standard return codes (0, -1, -2)
703 */
704
705static int dell_wmi_events_set_enabled(bool enable)
706{
707 struct calling_interface_buffer *buffer;
708 int ret;
709
710 buffer = dell_smbios_get_buffer();
711 buffer->input[0] = 0x10000;
712 buffer->input[1] = 0x51534554;
713 buffer->input[3] = enable;
714 dell_smbios_send_request(17, 3);
715 ret = buffer->output[0];
716 dell_smbios_release_buffer();
717
718 return dell_smbios_error(ret);
719}
720
721static int __init dell_wmi_init(void)
722{
723 int err;
724 acpi_status status;
725
726 if (!wmi_has_guid(DELL_EVENT_GUID) ||
727 !wmi_has_guid(DELL_DESCRIPTOR_GUID)) {
728 pr_warn("Dell WMI GUID were not found\n");
729 return -ENODEV;
730 }
731
732 err = dell_wmi_check_descriptor_buffer();
733 if (err)
734 return err;
735
736 err = dell_wmi_input_setup();
737 if (err)
738 return err;
739
740 status = wmi_install_notify_handler(DELL_EVENT_GUID,
741 dell_wmi_notify, NULL);
742 if (ACPI_FAILURE(status)) {
743 dell_wmi_input_destroy();
744 pr_err("Unable to register notify handler - %d\n", status);
745 return -ENODEV;
746 }
747
748 dmi_check_system(dell_wmi_smbios_list);
749
750 if (wmi_requires_smbios_request) {
751 err = dell_wmi_events_set_enabled(true);
752 if (err) {
753 pr_err("Failed to enable WMI events\n");
754 wmi_remove_notify_handler(DELL_EVENT_GUID);
755 dell_wmi_input_destroy();
756 return err;
757 }
758 }
759
760 return 0;
761}
762module_init(dell_wmi_init);
763
764static void __exit dell_wmi_exit(void)
765{
766 if (wmi_requires_smbios_request)
767 dell_wmi_events_set_enabled(false);
768 wmi_remove_notify_handler(DELL_EVENT_GUID);
769 dell_wmi_input_destroy();
770}
771module_exit(dell_wmi_exit);