Linux Audio

Check our new training course

Loading...
v3.1
 
  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);
v5.4
  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.rohar@gmail.com>
  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.rohar@gmail.com>");
 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	/* Mic mute */
259	{ KE_KEY, 0x150, { KEY_MICMUTE } },
260
261	/* Fn-lock */
262	{ KE_IGNORE, 0x151, { KEY_RESERVED } },
263
264	/* Change keyboard illumination */
265	{ KE_IGNORE, 0x152, { KEY_KBDILLUMTOGGLE } },
266
267	/*
268	 * Radio disable (notify only -- there is no model for which the
269	 * WMI event is supposed to trigger an action).
270	 */
271	{ KE_IGNORE, 0x153, { KEY_RFKILL } },
272
273	/* RGB keyboard backlight control */
274	{ KE_IGNORE, 0x154, { KEY_RESERVED } },
275
276	/*
277	 * Stealth mode toggle. This will "disable all lights and sounds".
278	 * The action is performed by the BIOS and EC; the WMI event is just
279	 * a notification. On the XPS 13 9350, this is Fn+F7, and there's
280	 * a BIOS setting to enable and disable the hotkey.
281	 */
282	{ KE_IGNORE, 0x155, { KEY_RESERVED } },
283
284	/* Rugged magnetic dock attach/detach events */
285	{ KE_IGNORE, 0x156, { KEY_RESERVED } },
286	{ KE_IGNORE, 0x157, { KEY_RESERVED } },
287
288	/* Rugged programmable (P1/P2/P3 keys) */
289	{ KE_KEY,    0x850, { KEY_PROG1 } },
290	{ KE_KEY,    0x851, { KEY_PROG2 } },
291	{ KE_KEY,    0x852, { KEY_PROG3 } },
292
293	/*
294	 * Radio disable (notify only -- there is no model for which the
295	 * WMI event is supposed to trigger an action).
296	 */
297	{ KE_IGNORE, 0xe008, { KEY_RFKILL } },
298
299	/* Fn-lock */
300	{ KE_IGNORE, 0xe035, { KEY_RESERVED } },
301};
302
303/*
304 * Keymap for WMI events of type 0x0011
305 */
306static const struct key_entry dell_wmi_keymap_type_0011[] = {
307	/* Battery unplugged */
308	{ KE_IGNORE, 0xfff0, { KEY_RESERVED } },
309
310	/* Battery inserted */
311	{ KE_IGNORE, 0xfff1, { KEY_RESERVED } },
312
313	/* Keyboard backlight level changed */
314	{ KE_IGNORE, KBD_LED_OFF_TOKEN,      { KEY_RESERVED } },
315	{ KE_IGNORE, KBD_LED_ON_TOKEN,       { KEY_RESERVED } },
316	{ KE_IGNORE, KBD_LED_AUTO_TOKEN,     { KEY_RESERVED } },
317	{ KE_IGNORE, KBD_LED_AUTO_25_TOKEN,  { KEY_RESERVED } },
318	{ KE_IGNORE, KBD_LED_AUTO_50_TOKEN,  { KEY_RESERVED } },
319	{ KE_IGNORE, KBD_LED_AUTO_75_TOKEN,  { KEY_RESERVED } },
320	{ KE_IGNORE, KBD_LED_AUTO_100_TOKEN, { KEY_RESERVED } },
321};
322
323static void dell_wmi_process_key(struct wmi_device *wdev, int type, int code)
324{
325	struct dell_wmi_priv *priv = dev_get_drvdata(&wdev->dev);
326	const struct key_entry *key;
327
328	key = sparse_keymap_entry_from_scancode(priv->input_dev,
329						(type << 16) | code);
330	if (!key) {
331		pr_info("Unknown key with type 0x%04x and code 0x%04x pressed\n",
332			type, code);
333		return;
334	}
335
336	pr_debug("Key with type 0x%04x and code 0x%04x pressed\n", type, code);
337
338	/* Don't report brightness notifications that will also come via ACPI */
339	if ((key->keycode == KEY_BRIGHTNESSUP ||
340	     key->keycode == KEY_BRIGHTNESSDOWN) &&
341	    acpi_video_handles_brightness_key_presses())
342		return;
343
344	if (type == 0x0000 && code == 0xe025 && !wmi_requires_smbios_request)
345		return;
346
347	if (key->keycode == KEY_KBDILLUMTOGGLE)
348		dell_laptop_call_notifier(
349			DELL_LAPTOP_KBD_BACKLIGHT_BRIGHTNESS_CHANGED, NULL);
350
351	sparse_keymap_report_entry(priv->input_dev, key, 1, true);
352}
353
354static void dell_wmi_notify(struct wmi_device *wdev,
355			    union acpi_object *obj)
356{
357	struct dell_wmi_priv *priv = dev_get_drvdata(&wdev->dev);
358	u16 *buffer_entry, *buffer_end;
359	acpi_size buffer_size;
360	int len, i;
361
362	if (obj->type != ACPI_TYPE_BUFFER) {
363		pr_warn("bad response type %x\n", obj->type);
364		return;
365	}
366
367	pr_debug("Received WMI event (%*ph)\n",
368		obj->buffer.length, obj->buffer.pointer);
369
370	buffer_entry = (u16 *)obj->buffer.pointer;
371	buffer_size = obj->buffer.length/2;
372	buffer_end = buffer_entry + buffer_size;
373
374	/*
375	 * BIOS/ACPI on devices with WMI interface version 0 does not clear
376	 * buffer before filling it. So next time when BIOS/ACPI send WMI event
377	 * which is smaller as previous then it contains garbage in buffer from
378	 * previous event.
379	 *
380	 * BIOS/ACPI on devices with WMI interface version 1 clears buffer and
381	 * sometimes send more events in buffer at one call.
382	 *
383	 * So to prevent reading garbage from buffer we will process only first
384	 * one event on devices with WMI interface version 0.
385	 */
386	if (priv->interface_version == 0 && buffer_entry < buffer_end)
387		if (buffer_end > buffer_entry + buffer_entry[0] + 1)
388			buffer_end = buffer_entry + buffer_entry[0] + 1;
389
390	while (buffer_entry < buffer_end) {
391
392		len = buffer_entry[0];
393		if (len == 0)
394			break;
395
396		len++;
397
398		if (buffer_entry + len > buffer_end) {
399			pr_warn("Invalid length of WMI event\n");
400			break;
401		}
402
403		pr_debug("Process buffer (%*ph)\n", len*2, buffer_entry);
 
 
 
404
405		switch (buffer_entry[1]) {
406		case 0x0000: /* One key pressed or event occurred */
407			if (len > 2)
408				dell_wmi_process_key(wdev, 0x0000,
409						     buffer_entry[2]);
410			/* Other entries could contain additional information */
411			break;
412		case 0x0010: /* Sequence of keys pressed */
413		case 0x0011: /* Sequence of events occurred */
414			for (i = 2; i < len; ++i)
415				dell_wmi_process_key(wdev, buffer_entry[1],
416						     buffer_entry[i]);
417			break;
418		default: /* Unknown event */
419			pr_info("Unknown WMI event type 0x%x\n",
420				(int)buffer_entry[1]);
421			break;
422		}
423
424		buffer_entry += len;
425
426	}
427
428}
429
430static bool have_scancode(u32 scancode, const struct key_entry *keymap, int len)
431{
 
 
 
432	int i;
433
434	for (i = 0; i < len; i++)
435		if (keymap[i].code == scancode)
436			return true;
437
438	return false;
439}
440
441static void handle_dmi_entry(const struct dmi_header *dm, void *opaque)
442{
443	struct dell_dmi_results *results = opaque;
444	struct dell_bios_hotkey_table *table;
445	int hotkey_num, i, pos = 0;
446	struct key_entry *keymap;
447
448	if (results->err || results->keymap)
449		return;		/* We already found the hotkey table. */
450
451	/* The Dell hotkey table is type 0xB2.  Scan until we find it. */
452	if (dm->type != 0xb2)
453		return;
454
455	table = container_of(dm, struct dell_bios_hotkey_table, header);
456
457	hotkey_num = (table->header.length -
458		      sizeof(struct dell_bios_hotkey_table)) /
459				sizeof(struct dell_bios_keymap_entry);
460	if (hotkey_num < 1) {
461		/*
462		 * Historically, dell-wmi would ignore a DMI entry of
463		 * fewer than 7 bytes.  Sizes between 4 and 8 bytes are
464		 * nonsensical (both the header and all entries are 4
465		 * bytes), so we approximate the old behavior by
466		 * ignoring tables with fewer than one entry.
467		 */
468		return;
469	}
470
471	keymap = kcalloc(hotkey_num, sizeof(struct key_entry), GFP_KERNEL);
472	if (!keymap) {
473		results->err = -ENOMEM;
474		return;
475	}
476
477	for (i = 0; i < hotkey_num; i++) {
478		const struct dell_bios_keymap_entry *bios_entry =
479					&table->keymap[i];
480
481		/* Uninitialized entries are 0 aka KEY_RESERVED. */
482		u16 keycode = (bios_entry->keycode <
483			       ARRAY_SIZE(bios_to_linux_keycode)) ?
484			bios_to_linux_keycode[bios_entry->keycode] :
485			KEY_RESERVED;
486
487		/*
488		 * Log if we find an entry in the DMI table that we don't
489		 * understand.  If this happens, we should figure out what
490		 * the entry means and add it to bios_to_linux_keycode.
491		 */
492		if (keycode == KEY_RESERVED) {
493			pr_info("firmware scancode 0x%x maps to unrecognized keycode 0x%x\n",
494				bios_entry->scancode, bios_entry->keycode);
495			continue;
496		}
497
498		if (keycode == KEY_KBDILLUMTOGGLE)
499			keymap[pos].type = KE_IGNORE;
500		else
501			keymap[pos].type = KE_KEY;
502		keymap[pos].code = bios_entry->scancode;
503		keymap[pos].keycode = keycode;
504
505		pos++;
506	}
507
508	results->keymap = keymap;
509	results->keymap_size = pos;
510}
511
512static int dell_wmi_input_setup(struct wmi_device *wdev)
513{
514	struct dell_wmi_priv *priv = dev_get_drvdata(&wdev->dev);
515	struct dell_dmi_results dmi_results = {};
516	struct key_entry *keymap;
517	int err, i, pos = 0;
518
519	priv->input_dev = input_allocate_device();
520	if (!priv->input_dev)
521		return -ENOMEM;
522
523	priv->input_dev->name = "Dell WMI hotkeys";
524	priv->input_dev->id.bustype = BUS_HOST;
525	priv->input_dev->dev.parent = &wdev->dev;
526
527	if (dmi_walk(handle_dmi_entry, &dmi_results)) {
528		/*
529		 * Historically, dell-wmi ignored dmi_walk errors.  A failure
530		 * is certainly surprising, but it probably just indicates
531		 * a very old laptop.
532		 */
533		pr_warn("no DMI; using the old-style hotkey interface\n");
534	}
535
536	if (dmi_results.err) {
537		err = dmi_results.err;
538		goto err_free_dev;
539	}
540
541	keymap = kcalloc(dmi_results.keymap_size +
542			 ARRAY_SIZE(dell_wmi_keymap_type_0000) +
543			 ARRAY_SIZE(dell_wmi_keymap_type_0010) +
544			 ARRAY_SIZE(dell_wmi_keymap_type_0011) +
545			 1,
546			 sizeof(struct key_entry), GFP_KERNEL);
547	if (!keymap) {
548		kfree(dmi_results.keymap);
549		err = -ENOMEM;
550		goto err_free_dev;
551	}
552
553	/* Append table with events of type 0x0010 which comes from DMI */
554	for (i = 0; i < dmi_results.keymap_size; i++) {
555		keymap[pos] = dmi_results.keymap[i];
556		keymap[pos].code |= (0x0010 << 16);
557		pos++;
558	}
559
560	kfree(dmi_results.keymap);
561
562	/* Append table with extra events of type 0x0010 which are not in DMI */
563	for (i = 0; i < ARRAY_SIZE(dell_wmi_keymap_type_0010); i++) {
564		const struct key_entry *entry = &dell_wmi_keymap_type_0010[i];
565
566		/*
567		 * Check if we've already found this scancode.  This takes
568		 * quadratic time, but it doesn't matter unless the list
569		 * of extra keys gets very long.
570		 */
571		if (dmi_results.keymap_size &&
572		    have_scancode(entry->code | (0x0010 << 16),
573				  keymap, dmi_results.keymap_size)
574		   )
575			continue;
576
577		keymap[pos] = *entry;
578		keymap[pos].code |= (0x0010 << 16);
579		pos++;
580	}
581
582	/* Append table with events of type 0x0011 */
583	for (i = 0; i < ARRAY_SIZE(dell_wmi_keymap_type_0011); i++) {
584		keymap[pos] = dell_wmi_keymap_type_0011[i];
585		keymap[pos].code |= (0x0011 << 16);
586		pos++;
587	}
588
589	/*
590	 * Now append also table with "legacy" events of type 0x0000. Some of
591	 * them are reported also on laptops which have scancodes in DMI.
592	 */
593	for (i = 0; i < ARRAY_SIZE(dell_wmi_keymap_type_0000); i++) {
594		keymap[pos] = dell_wmi_keymap_type_0000[i];
595		pos++;
596	}
597
598	keymap[pos].type = KE_END;
599
600	err = sparse_keymap_setup(priv->input_dev, keymap, NULL);
601	/*
602	 * Sparse keymap library makes a copy of keymap so we don't need the
603	 * original one that was allocated.
604	 */
605	kfree(keymap);
606	if (err)
607		goto err_free_dev;
608
609	err = input_register_device(priv->input_dev);
610	if (err)
611		goto err_free_dev;
612
613	return 0;
614
 
 
615 err_free_dev:
616	input_free_device(priv->input_dev);
617	return err;
618}
619
620static void dell_wmi_input_destroy(struct wmi_device *wdev)
621{
622	struct dell_wmi_priv *priv = dev_get_drvdata(&wdev->dev);
623
624	input_unregister_device(priv->input_dev);
625}
626
627/*
628 * According to Dell SMBIOS documentation:
629 *
630 * 17  3  Application Program Registration
631 *
632 *     cbArg1 Application ID 1 = 0x00010000
633 *     cbArg2 Application ID 2
634 *            QUICKSET/DCP = 0x51534554 "QSET"
635 *            ALS Driver   = 0x416c7353 "AlsS"
636 *            Latitude ON  = 0x4c6f6e52 "LonR"
637 *     cbArg3 Application version or revision number
638 *     cbArg4 0 = Unregister application
639 *            1 = Register application
640 *     cbRes1 Standard return codes (0, -1, -2)
641 */
642
643static int dell_wmi_events_set_enabled(bool enable)
644{
645	struct calling_interface_buffer *buffer;
646	int ret;
647
648	buffer = kzalloc(sizeof(struct calling_interface_buffer), GFP_KERNEL);
649	if (!buffer)
650		return -ENOMEM;
651	buffer->cmd_class = CLASS_INFO;
652	buffer->cmd_select = SELECT_APP_REGISTRATION;
653	buffer->input[0] = 0x10000;
654	buffer->input[1] = 0x51534554;
655	buffer->input[3] = enable;
656	ret = dell_smbios_call(buffer);
657	if (ret == 0)
658		ret = buffer->output[0];
659	kfree(buffer);
660
661	return dell_smbios_error(ret);
662}
663
664static int dell_wmi_probe(struct wmi_device *wdev, const void *context)
665{
666	struct dell_wmi_priv *priv;
667	int ret;
668
669	ret = dell_wmi_get_descriptor_valid();
670	if (ret)
671		return ret;
672
673	priv = devm_kzalloc(
674		&wdev->dev, sizeof(struct dell_wmi_priv), GFP_KERNEL);
675	if (!priv)
676		return -ENOMEM;
677	dev_set_drvdata(&wdev->dev, priv);
678
679	if (!dell_wmi_get_interface_version(&priv->interface_version))
680		return -EPROBE_DEFER;
681
682	return dell_wmi_input_setup(wdev);
683}
684
685static int dell_wmi_remove(struct wmi_device *wdev)
686{
687	dell_wmi_input_destroy(wdev);
688	return 0;
689}
690static const struct wmi_device_id dell_wmi_id_table[] = {
691	{ .guid_string = DELL_EVENT_GUID },
692	{ },
693};
694
695static struct wmi_driver dell_wmi_driver = {
696	.driver = {
697		.name = "dell-wmi",
698	},
699	.id_table = dell_wmi_id_table,
700	.probe = dell_wmi_probe,
701	.remove = dell_wmi_remove,
702	.notify = dell_wmi_notify,
703};
704
705static int __init dell_wmi_init(void)
706{
707	int err;
708
709	dmi_check_system(dell_wmi_smbios_list);
710
711	if (wmi_requires_smbios_request) {
712		err = dell_wmi_events_set_enabled(true);
713		if (err) {
714			pr_err("Failed to enable WMI events\n");
715			return err;
716		}
717	}
718
719	return wmi_driver_register(&dell_wmi_driver);
720}
721late_initcall(dell_wmi_init);
722
723static void __exit dell_wmi_exit(void)
724{
725	if (wmi_requires_smbios_request)
726		dell_wmi_events_set_enabled(false);
727
728	wmi_driver_unregister(&dell_wmi_driver);
729}
730module_exit(dell_wmi_exit);
731
732MODULE_DEVICE_TABLE(wmi, dell_wmi_id_table);