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.9
  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);