Linux Audio

Check our new training course

Loading...
v4.6
  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 * Certain keys are flagged as KE_IGNORE. All of these are either
 84 * notifications (rather than requests for change) or are also sent
 85 * via the keyboard controller so should not be sent again.
 86 */
 87
 88static const struct key_entry dell_wmi_legacy_keymap[] __initconst = {
 89	{ KE_IGNORE, 0x003a, { KEY_CAPSLOCK } },
 90
 91	{ KE_KEY, 0xe045, { KEY_PROG1 } },
 92	{ KE_KEY, 0xe009, { KEY_EJECTCD } },
 93
 94	/* These also contain the brightness level at offset 6 */
 95	{ KE_KEY, 0xe006, { KEY_BRIGHTNESSUP } },
 96	{ KE_KEY, 0xe005, { KEY_BRIGHTNESSDOWN } },
 97
 98	/* Battery health status button */
 99	{ KE_KEY, 0xe007, { KEY_BATTERY } },
100
101	/* Radio devices state change */
102	{ KE_IGNORE, 0xe008, { KEY_RFKILL } },
 
 
103
104	/* The next device is at offset 6, the active devices are at
105	   offset 8 and the attached devices at offset 10 */
106	{ KE_KEY, 0xe00b, { KEY_SWITCHVIDEOMODE } },
107
108	{ KE_IGNORE, 0xe00c, { KEY_KBDILLUMTOGGLE } },
109
110	/* BIOS error detected */
111	{ KE_IGNORE, 0xe00d, { KEY_RESERVED } },
112
113	/* Wifi Catcher */
114	{ KE_KEY, 0xe011, {KEY_PROG2 } },
115
116	/* Ambient light sensor toggle */
117	{ KE_IGNORE, 0xe013, { KEY_RESERVED } },
118
119	{ KE_IGNORE, 0xe020, { KEY_MUTE } },
120
121	/* Dell Instant Launch key */
122	{ KE_KEY, 0xe025, { KEY_PROG4 } },
123	{ KE_KEY, 0xe029, { KEY_PROG4 } },
124
125	/* Audio panel key */
126	{ KE_IGNORE, 0xe026, { KEY_RESERVED } },
127
128	{ KE_IGNORE, 0xe02e, { KEY_VOLUMEDOWN } },
129	{ KE_IGNORE, 0xe030, { KEY_VOLUMEUP } },
130	{ KE_IGNORE, 0xe033, { KEY_KBDILLUMUP } },
131	{ KE_IGNORE, 0xe034, { KEY_KBDILLUMDOWN } },
132	{ KE_IGNORE, 0xe03a, { KEY_CAPSLOCK } },
133	{ KE_IGNORE, 0xe045, { KEY_NUMLOCK } },
134	{ KE_IGNORE, 0xe046, { KEY_SCROLLLOCK } },
135	{ KE_IGNORE, 0xe0f7, { KEY_MUTE } },
136	{ KE_IGNORE, 0xe0f8, { KEY_VOLUMEDOWN } },
137	{ KE_IGNORE, 0xe0f9, { KEY_VOLUMEUP } },
138	{ KE_END, 0 }
139};
140
141static bool dell_new_hk_type;
142
143struct dell_bios_keymap_entry {
144	u16 scancode;
145	u16 keycode;
146};
147
148struct dell_bios_hotkey_table {
149	struct dmi_header header;
150	struct dell_bios_keymap_entry keymap[];
151
152};
153
154struct dell_dmi_results {
155	int err;
156	struct key_entry *keymap;
157};
158
159/* Uninitialized entries here are KEY_RESERVED == 0. */
160static const u16 bios_to_linux_keycode[256] __initconst = {
161	[0]	= KEY_MEDIA,
162	[1]	= KEY_NEXTSONG,
163	[2]	= KEY_PLAYPAUSE,
164	[3]	= KEY_PREVIOUSSONG,
165	[4]	= KEY_STOPCD,
166	[5]	= KEY_UNKNOWN,
167	[6]	= KEY_UNKNOWN,
168	[7]	= KEY_UNKNOWN,
169	[8]	= KEY_WWW,
170	[9]	= KEY_UNKNOWN,
171	[10]	= KEY_VOLUMEDOWN,
172	[11]	= KEY_MUTE,
173	[12]	= KEY_VOLUMEUP,
174	[13]	= KEY_UNKNOWN,
175	[14]	= KEY_BATTERY,
176	[15]	= KEY_EJECTCD,
177	[16]	= KEY_UNKNOWN,
178	[17]	= KEY_SLEEP,
179	[18]	= KEY_PROG1,
180	[19]	= KEY_BRIGHTNESSDOWN,
181	[20]	= KEY_BRIGHTNESSUP,
182	[21]	= KEY_UNKNOWN,
183	[22]	= KEY_KBDILLUMTOGGLE,
184	[23]	= KEY_UNKNOWN,
185	[24]	= KEY_SWITCHVIDEOMODE,
186	[25]	= KEY_UNKNOWN,
187	[26]	= KEY_UNKNOWN,
188	[27]	= KEY_SWITCHVIDEOMODE,
189	[28]	= KEY_UNKNOWN,
190	[29]	= KEY_UNKNOWN,
191	[30]	= KEY_PROG2,
192	[31]	= KEY_UNKNOWN,
193	[32]	= KEY_UNKNOWN,
194	[33]	= KEY_UNKNOWN,
195	[34]	= KEY_UNKNOWN,
196	[35]	= KEY_UNKNOWN,
197	[36]	= KEY_UNKNOWN,
198	[37]	= KEY_UNKNOWN,
199	[38]	= KEY_MICMUTE,
200	[255]	= KEY_PROG3,
201};
202
203/*
204 * These are applied if the 0xB2 DMI hotkey table is present and doesn't
205 * override them.
206 */
207static const struct key_entry dell_wmi_extra_keymap[] __initconst = {
208	/* Fn-lock */
209	{ KE_IGNORE, 0x151, { KEY_RESERVED } },
210
211	/* Change keyboard illumination */
212	{ KE_IGNORE, 0x152, { KEY_KBDILLUMTOGGLE } },
213
214	/*
215	 * Radio disable (notify only -- there is no model for which the
216	 * WMI event is supposed to trigger an action).
217	 */
218	{ KE_IGNORE, 0x153, { KEY_RFKILL } },
219
220	/* RGB keyboard backlight control */
221	{ KE_IGNORE, 0x154, { KEY_RESERVED } },
222
223	/* Stealth mode toggle */
224	{ KE_IGNORE, 0x155, { KEY_RESERVED } },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
225};
226
227static struct input_dev *dell_wmi_input_dev;
228
229static void dell_wmi_process_key(int reported_key)
230{
231	const struct key_entry *key;
232
233	key = sparse_keymap_entry_from_scancode(dell_wmi_input_dev,
234						reported_key);
235	if (!key) {
236		pr_info("Unknown key with scancode 0x%x pressed\n",
237			reported_key);
238		return;
239	}
240
241	pr_debug("Key %x pressed\n", reported_key);
242
243	/* Don't report brightness notifications that will also come via ACPI */
244	if ((key->keycode == KEY_BRIGHTNESSUP ||
245	     key->keycode == KEY_BRIGHTNESSDOWN) &&
246	    acpi_video_handles_brightness_key_presses())
247		return;
248
249	if (reported_key == 0xe025 && !wmi_requires_smbios_request)
250		return;
251
252	sparse_keymap_report_entry(dell_wmi_input_dev, key, 1, true);
253}
254
255static void dell_wmi_notify(u32 value, void *context)
256{
257	struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
258	union acpi_object *obj;
259	acpi_status status;
260	acpi_size buffer_size;
261	u16 *buffer_entry, *buffer_end;
262	int len, i;
263
264	status = wmi_get_event_data(value, &response);
265	if (status != AE_OK) {
266		pr_warn("bad event status 0x%x\n", status);
267		return;
268	}
269
270	obj = (union acpi_object *)response.pointer;
271	if (!obj) {
272		pr_warn("no response\n");
273		return;
274	}
275
276	if (obj->type != ACPI_TYPE_BUFFER) {
277		pr_warn("bad response type %x\n", obj->type);
278		kfree(obj);
279		return;
280	}
281
282	pr_debug("Received WMI event (%*ph)\n",
283		obj->buffer.length, obj->buffer.pointer);
284
285	buffer_entry = (u16 *)obj->buffer.pointer;
286	buffer_size = obj->buffer.length/2;
287
288	if (!dell_new_hk_type) {
289		if (buffer_size >= 3 && buffer_entry[1] == 0x0)
290			dell_wmi_process_key(buffer_entry[2]);
291		else if (buffer_size >= 2)
292			dell_wmi_process_key(buffer_entry[1]);
293		else
294			pr_info("Received unknown WMI event\n");
295		kfree(obj);
296		return;
297	}
298
299	buffer_end = buffer_entry + buffer_size;
300
301	/*
302	 * BIOS/ACPI on devices with WMI interface version 0 does not clear
303	 * buffer before filling it. So next time when BIOS/ACPI send WMI event
304	 * which is smaller as previous then it contains garbage in buffer from
305	 * previous event.
306	 *
307	 * BIOS/ACPI on devices with WMI interface version 1 clears buffer and
308	 * sometimes send more events in buffer at one call.
309	 *
310	 * So to prevent reading garbage from buffer we will process only first
311	 * one event on devices with WMI interface version 0.
312	 */
313	if (dell_wmi_interface_version == 0 && buffer_entry < buffer_end)
314		if (buffer_end > buffer_entry + buffer_entry[0] + 1)
315			buffer_end = buffer_entry + buffer_entry[0] + 1;
316
317	while (buffer_entry < buffer_end) {
318
319		len = buffer_entry[0];
320		if (len == 0)
321			break;
322
323		len++;
324
325		if (buffer_entry + len > buffer_end) {
326			pr_warn("Invalid length of WMI event\n");
327			break;
328		}
329
330		pr_debug("Process buffer (%*ph)\n", len*2, buffer_entry);
 
 
 
331
332		switch (buffer_entry[1]) {
333		case 0x00:
334			for (i = 2; i < len; ++i) {
335				switch (buffer_entry[i]) {
336				case 0xe043:
337					/* NIC Link is Up */
338					pr_debug("NIC Link is Up\n");
339					break;
340				case 0xe044:
341					/* NIC Link is Down */
342					pr_debug("NIC Link is Down\n");
343					break;
344				case 0xe045:
345					/* Unknown event but defined in DSDT */
346				default:
347					/* Unknown event */
348					pr_info("Unknown WMI event type 0x00: "
349						"0x%x\n", (int)buffer_entry[i]);
350					break;
351				}
352			}
353			break;
354		case 0x10:
355			/* Keys pressed */
356			for (i = 2; i < len; ++i)
357				dell_wmi_process_key(buffer_entry[i]);
358			break;
359		case 0x11:
360			for (i = 2; i < len; ++i) {
361				switch (buffer_entry[i]) {
362				case 0xfff0:
363					/* Battery unplugged */
364					pr_debug("Battery unplugged\n");
365					break;
366				case 0xfff1:
367					/* Battery inserted */
368					pr_debug("Battery inserted\n");
369					break;
370				case 0x01e1:
371				case 0x02ea:
372				case 0x02eb:
373				case 0x02ec:
374				case 0x02f6:
375					/* Keyboard backlight level changed */
376					pr_debug("Keyboard backlight level "
377						 "changed\n");
378					break;
379				default:
380					/* Unknown event */
381					pr_info("Unknown WMI event type 0x11: "
382						"0x%x\n", (int)buffer_entry[i]);
383					break;
384				}
385			}
386			break;
387		default:
388			/* Unknown event */
389			pr_info("Unknown WMI event type 0x%x\n",
390				(int)buffer_entry[1]);
391			break;
392		}
393
394		buffer_entry += len;
395
396	}
397
398	kfree(obj);
399}
400
401static bool have_scancode(u32 scancode, const struct key_entry *keymap, int len)
402{
403	int i;
404
405	for (i = 0; i < len; i++)
406		if (keymap[i].code == scancode)
407			return true;
408
409	return false;
410}
411
412static void __init handle_dmi_entry(const struct dmi_header *dm,
413
414				    void *opaque)
415
416{
417	struct dell_dmi_results *results = opaque;
418	struct dell_bios_hotkey_table *table;
419	int hotkey_num, i, pos = 0;
420	struct key_entry *keymap;
421	int num_bios_keys;
422
423	if (results->err || results->keymap)
424		return;		/* We already found the hotkey table. */
425
426	if (dm->type != 0xb2)
427		return;
428
429	table = container_of(dm, struct dell_bios_hotkey_table, header);
430
431	hotkey_num = (table->header.length -
432		      sizeof(struct dell_bios_hotkey_table)) /
433				sizeof(struct dell_bios_keymap_entry);
434	if (hotkey_num < 1) {
435		/*
436		 * Historically, dell-wmi would ignore a DMI entry of
437		 * fewer than 7 bytes.  Sizes between 4 and 8 bytes are
438		 * nonsensical (both the header and all entries are 4
439		 * bytes), so we approximate the old behavior by
440		 * ignoring tables with fewer than one entry.
441		 */
442		return;
443	}
444
445	keymap = kcalloc(hotkey_num + ARRAY_SIZE(dell_wmi_extra_keymap) + 1,
446			 sizeof(struct key_entry), GFP_KERNEL);
447	if (!keymap) {
448		results->err = -ENOMEM;
449		return;
450	}
451
452	for (i = 0; i < hotkey_num; i++) {
453		const struct dell_bios_keymap_entry *bios_entry =
454					&table->keymap[i];
455
456		/* Uninitialized entries are 0 aka KEY_RESERVED. */
457		u16 keycode = (bios_entry->keycode <
458			       ARRAY_SIZE(bios_to_linux_keycode)) ?
459			bios_to_linux_keycode[bios_entry->keycode] :
460			KEY_RESERVED;
461
462		/*
463		 * Log if we find an entry in the DMI table that we don't
464		 * understand.  If this happens, we should figure out what
465		 * the entry means and add it to bios_to_linux_keycode.
466		 */
467		if (keycode == KEY_RESERVED) {
468			pr_info("firmware scancode 0x%x maps to unrecognized keycode 0x%x\n",
469				bios_entry->scancode, bios_entry->keycode);
470			continue;
471		}
472
473		if (keycode == KEY_KBDILLUMTOGGLE)
474			keymap[pos].type = KE_IGNORE;
475		else
476			keymap[pos].type = KE_KEY;
477		keymap[pos].code = bios_entry->scancode;
478		keymap[pos].keycode = keycode;
479
480		pos++;
481	}
482
483	num_bios_keys = pos;
484
485	for (i = 0; i < ARRAY_SIZE(dell_wmi_extra_keymap); i++) {
486		const struct key_entry *entry = &dell_wmi_extra_keymap[i];
487
488		/*
489		 * Check if we've already found this scancode.  This takes
490		 * quadratic time, but it doesn't matter unless the list
491		 * of extra keys gets very long.
492		 */
493		if (!have_scancode(entry->code, keymap, num_bios_keys)) {
494			keymap[pos] = *entry;
495			pos++;
496		}
497	}
498
499	keymap[pos].type = KE_END;
500
501	results->keymap = keymap;
502}
503
504static int __init dell_wmi_input_setup(void)
505{
506	struct dell_dmi_results dmi_results = {};
507	int err;
508
509	dell_wmi_input_dev = input_allocate_device();
510	if (!dell_wmi_input_dev)
511		return -ENOMEM;
512
513	dell_wmi_input_dev->name = "Dell WMI hotkeys";
514	dell_wmi_input_dev->phys = "wmi/input0";
515	dell_wmi_input_dev->id.bustype = BUS_HOST;
516
517	if (dmi_walk(handle_dmi_entry, &dmi_results)) {
518		/*
519		 * Historically, dell-wmi ignored dmi_walk errors.  A failure
520		 * is certainly surprising, but it probably just indicates
521		 * a very old laptop.
522		 */
523		pr_warn("no DMI; using the old-style hotkey interface\n");
524	}
525
526	if (dmi_results.err) {
527		err = dmi_results.err;
528		goto err_free_dev;
529	}
530
531	if (dmi_results.keymap) {
532		dell_new_hk_type = true;
533
534		err = sparse_keymap_setup(dell_wmi_input_dev,
535					  dmi_results.keymap, NULL);
536
537		/*
538		 * Sparse keymap library makes a copy of keymap so we
539		 * don't need the original one that was allocated.
540		 */
541		kfree(dmi_results.keymap);
542	} else {
543		err = sparse_keymap_setup(dell_wmi_input_dev,
544					  dell_wmi_legacy_keymap, NULL);
545	}
546	if (err)
547		goto err_free_dev;
548
549	err = input_register_device(dell_wmi_input_dev);
550	if (err)
551		goto err_free_keymap;
552
553	return 0;
554
555 err_free_keymap:
556	sparse_keymap_free(dell_wmi_input_dev);
557 err_free_dev:
558	input_free_device(dell_wmi_input_dev);
559	return err;
560}
561
562static void dell_wmi_input_destroy(void)
563{
564	sparse_keymap_free(dell_wmi_input_dev);
565	input_unregister_device(dell_wmi_input_dev);
566}
567
568/*
569 * Descriptor buffer is 128 byte long and contains:
570 *
571 *       Name             Offset  Length  Value
572 * Vendor Signature          0       4    "DELL"
573 * Object Signature          4       4    " WMI"
574 * WMI Interface Version     8       4    <version>
575 * WMI buffer length        12       4    4096
576 */
577static int __init dell_wmi_check_descriptor_buffer(void)
578{
579	struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
580	union acpi_object *obj;
581	acpi_status status;
582	u32 *buffer;
583
584	status = wmi_query_block(DELL_DESCRIPTOR_GUID, 0, &out);
585	if (ACPI_FAILURE(status)) {
586		pr_err("Cannot read Dell descriptor buffer - %d\n", status);
587		return status;
588	}
589
590	obj = (union acpi_object *)out.pointer;
591	if (!obj) {
592		pr_err("Dell descriptor buffer is empty\n");
593		return -EINVAL;
594	}
595
596	if (obj->type != ACPI_TYPE_BUFFER) {
597		pr_err("Cannot read Dell descriptor buffer\n");
598		kfree(obj);
599		return -EINVAL;
600	}
601
602	if (obj->buffer.length != 128) {
603		pr_err("Dell descriptor buffer has invalid length (%d)\n",
604			obj->buffer.length);
605		if (obj->buffer.length < 16) {
606			kfree(obj);
607			return -EINVAL;
608		}
609	}
610
611	buffer = (u32 *)obj->buffer.pointer;
612
613	if (buffer[0] != 0x4C4C4544 && buffer[1] != 0x494D5720)
614		pr_warn("Dell descriptor buffer has invalid signature (%*ph)\n",
615			8, buffer);
616
617	if (buffer[2] != 0 && buffer[2] != 1)
618		pr_warn("Dell descriptor buffer has unknown version (%d)\n",
619			buffer[2]);
620
621	if (buffer[3] != 4096)
622		pr_warn("Dell descriptor buffer has invalid buffer length (%d)\n",
623			buffer[3]);
624
625	dell_wmi_interface_version = buffer[2];
626
627	pr_info("Detected Dell WMI interface version %u\n",
628		dell_wmi_interface_version);
629
630	kfree(obj);
631	return 0;
632}
633
634/*
635 * According to Dell SMBIOS documentation:
636 *
637 * 17  3  Application Program Registration
638 *
639 *     cbArg1 Application ID 1 = 0x00010000
640 *     cbArg2 Application ID 2
641 *            QUICKSET/DCP = 0x51534554 "QSET"
642 *            ALS Driver   = 0x416c7353 "AlsS"
643 *            Latitude ON  = 0x4c6f6e52 "LonR"
644 *     cbArg3 Application version or revision number
645 *     cbArg4 0 = Unregister application
646 *            1 = Register application
647 *     cbRes1 Standard return codes (0, -1, -2)
648 */
649
650static int dell_wmi_events_set_enabled(bool enable)
651{
652	struct calling_interface_buffer *buffer;
653	int ret;
654
655	buffer = dell_smbios_get_buffer();
656	buffer->input[0] = 0x10000;
657	buffer->input[1] = 0x51534554;
658	buffer->input[3] = enable;
659	dell_smbios_send_request(17, 3);
660	ret = buffer->output[0];
661	dell_smbios_release_buffer();
662
663	return dell_smbios_error(ret);
664}
665
666static int __init dell_wmi_init(void)
667{
668	int err;
669	acpi_status status;
670
671	if (!wmi_has_guid(DELL_EVENT_GUID) ||
672	    !wmi_has_guid(DELL_DESCRIPTOR_GUID)) {
673		pr_warn("Dell WMI GUID were not found\n");
674		return -ENODEV;
675	}
676
677	err = dell_wmi_check_descriptor_buffer();
678	if (err)
679		return err;
680
681	err = dell_wmi_input_setup();
682	if (err)
683		return err;
684
685	status = wmi_install_notify_handler(DELL_EVENT_GUID,
686					 dell_wmi_notify, NULL);
687	if (ACPI_FAILURE(status)) {
688		dell_wmi_input_destroy();
689		pr_err("Unable to register notify handler - %d\n", status);
690		return -ENODEV;
691	}
692
693	dmi_check_system(dell_wmi_smbios_list);
694
695	if (wmi_requires_smbios_request) {
696		err = dell_wmi_events_set_enabled(true);
697		if (err) {
698			pr_err("Failed to enable WMI events\n");
699			wmi_remove_notify_handler(DELL_EVENT_GUID);
700			dell_wmi_input_destroy();
701			return err;
702		}
703	}
704
705	return 0;
706}
707module_init(dell_wmi_init);
708
709static void __exit dell_wmi_exit(void)
710{
711	if (wmi_requires_smbios_request)
712		dell_wmi_events_set_enabled(false);
713	wmi_remove_notify_handler(DELL_EVENT_GUID);
714	dell_wmi_input_destroy();
715}
716module_exit(dell_wmi_exit);
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);