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