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