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