aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorColin Ian King <colin.king@canonical.com>2011-02-17 13:44:09 -0500
committerMatthew Garrett <mjg@redhat.com>2011-03-28 06:05:22 -0400
commit820787fceb3e62c29a36423eb30e2f9f198547f7 (patch)
tree94342dca1dd89c5d2c8557e45618460d5a048dab
parent8eec8a1167b5912c19fec2cdad5b968dd0f8690d (diff)
Enable Dell All-In-One volume up/down keys
Enable volume up and down hotkeys on WMI events GUID 284A0E6B-380E-472A-921F-E52786257FB4 and GUID 02314822-307C-4F66-bf0E-48AEAEB26CC8. Also works around a firmware bug where the _WED method should return an integer containing the key code and in fact the method returns the key code in element zero of a buffer. BugLink: http://bugs.launchpad.net/bugs/701530 BugLink: http://bugs.launchpad.net/bugs/676997 Signed-off-by: Colin Ian King <colin.king@canonical.com> Signed-off-by: Matthew Garrett <mjg@redhat.com>
-rw-r--r--drivers/platform/x86/Kconfig13
-rw-r--r--drivers/platform/x86/Makefile1
-rw-r--r--drivers/platform/x86/dell-wmi-aio.c171
3 files changed, 185 insertions, 0 deletions
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 2822d8a8cdd2..f0ef757d9fde 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -101,6 +101,19 @@ config DELL_WMI
101 To compile this driver as a module, choose M here: the module will 101 To compile this driver as a module, choose M here: the module will
102 be called dell-wmi. 102 be called dell-wmi.
103 103
104config DELL_WMI_AIO
105 tristate "WMI Hotkeys for Dell All-In-One series"
106 depends on ACPI_WMI
107 depends on INPUT
108 select INPUT_SPARSEKMAP
109 ---help---
110 Say Y here if you want to support WMI-based hotkeys on Dell
111 All-In-One machines.
112
113 To compile this driver as a module, choose M here: the module will
114 be called dell-wmi.
115
116
104config FUJITSU_LAPTOP 117config FUJITSU_LAPTOP
105 tristate "Fujitsu Laptop Extras" 118 tristate "Fujitsu Laptop Extras"
106 depends on ACPI 119 depends on ACPI
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index 3e14b18403e0..46f3fd2c8856 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_ACPI_CMPC) += classmate-laptop.o
10obj-$(CONFIG_COMPAL_LAPTOP) += compal-laptop.o 10obj-$(CONFIG_COMPAL_LAPTOP) += compal-laptop.o
11obj-$(CONFIG_DELL_LAPTOP) += dell-laptop.o 11obj-$(CONFIG_DELL_LAPTOP) += dell-laptop.o
12obj-$(CONFIG_DELL_WMI) += dell-wmi.o 12obj-$(CONFIG_DELL_WMI) += dell-wmi.o
13obj-$(CONFIG_DELL_WMI_AIO) += dell-wmi-aio.o
13obj-$(CONFIG_ACER_WMI) += acer-wmi.o 14obj-$(CONFIG_ACER_WMI) += acer-wmi.o
14obj-$(CONFIG_ACERHDF) += acerhdf.o 15obj-$(CONFIG_ACERHDF) += acerhdf.o
15obj-$(CONFIG_HP_ACCEL) += hp_accel.o 16obj-$(CONFIG_HP_ACCEL) += hp_accel.o
diff --git a/drivers/platform/x86/dell-wmi-aio.c b/drivers/platform/x86/dell-wmi-aio.c
new file mode 100644
index 000000000000..0ed84573ae1f
--- /dev/null
+++ b/drivers/platform/x86/dell-wmi-aio.c
@@ -0,0 +1,171 @@
1/*
2 * WMI hotkeys support for Dell All-In-One series
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
19
20#include <linux/kernel.h>
21#include <linux/module.h>
22#include <linux/init.h>
23#include <linux/types.h>
24#include <linux/input.h>
25#include <linux/input/sparse-keymap.h>
26#include <acpi/acpi_drivers.h>
27#include <linux/acpi.h>
28#include <linux/string.h>
29
30MODULE_DESCRIPTION("WMI hotkeys driver for Dell All-In-One series");
31MODULE_LICENSE("GPL");
32
33#define EVENT_GUID1 "284A0E6B-380E-472A-921F-E52786257FB4"
34#define EVENT_GUID2 "02314822-307C-4F66-BF0E-48AEAEB26CC8"
35
36static const char *dell_wmi_aio_guids[] = {
37 EVENT_GUID1,
38 EVENT_GUID2,
39 NULL
40};
41
42MODULE_ALIAS("wmi:"EVENT_GUID1);
43MODULE_ALIAS("wmi:"EVENT_GUID2);
44
45static const struct key_entry dell_wmi_aio_keymap[] = {
46 { KE_KEY, 0xc0, { KEY_VOLUMEUP } },
47 { KE_KEY, 0xc1, { KEY_VOLUMEDOWN } },
48 { KE_END, 0 }
49};
50
51static struct input_dev *dell_wmi_aio_input_dev;
52
53static void dell_wmi_aio_notify(u32 value, void *context)
54{
55 struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
56 union acpi_object *obj;
57 acpi_status status;
58
59 status = wmi_get_event_data(value, &response);
60 if (status != AE_OK) {
61 pr_info("bad event status 0x%x\n", status);
62 return;
63 }
64
65 obj = (union acpi_object *)response.pointer;
66 if (obj) {
67 unsigned int scancode;
68
69 switch (obj->type) {
70 case ACPI_TYPE_INTEGER:
71 /* Most All-In-One correctly return integer scancode */
72 scancode = obj->integer.value;
73 sparse_keymap_report_event(dell_wmi_aio_input_dev,
74 scancode, 1, true);
75 break;
76 case ACPI_TYPE_BUFFER:
77 /* Broken machines return the scancode in a buffer */
78 if (obj->buffer.pointer && obj->buffer.length > 0) {
79 scancode = obj->buffer.pointer[0];
80 sparse_keymap_report_event(
81 dell_wmi_aio_input_dev,
82 scancode, 1, true);
83 }
84 break;
85 }
86 }
87 kfree(obj);
88}
89
90static int __init dell_wmi_aio_input_setup(void)
91{
92 int err;
93
94 dell_wmi_aio_input_dev = input_allocate_device();
95
96 if (!dell_wmi_aio_input_dev)
97 return -ENOMEM;
98
99 dell_wmi_aio_input_dev->name = "Dell AIO WMI hotkeys";
100 dell_wmi_aio_input_dev->phys = "wmi/input0";
101 dell_wmi_aio_input_dev->id.bustype = BUS_HOST;
102
103 err = sparse_keymap_setup(dell_wmi_aio_input_dev,
104 dell_wmi_aio_keymap, NULL);
105 if (err) {
106 pr_err("Unable to setup input device keymap\n");
107 goto err_free_dev;
108 }
109 err = input_register_device(dell_wmi_aio_input_dev);
110 if (err) {
111 pr_info("Unable to register input device\n");
112 goto err_free_keymap;
113 }
114 return 0;
115
116err_free_keymap:
117 sparse_keymap_free(dell_wmi_aio_input_dev);
118err_free_dev:
119 input_free_device(dell_wmi_aio_input_dev);
120 return err;
121}
122
123static const char *dell_wmi_aio_find(void)
124{
125 int i;
126
127 for (i = 0; dell_wmi_aio_guids[i] != NULL; i++)
128 if (wmi_has_guid(dell_wmi_aio_guids[i]))
129 return dell_wmi_aio_guids[i];
130
131 return NULL;
132}
133
134static int __init dell_wmi_aio_init(void)
135{
136 int err;
137 const char *guid;
138
139 guid = dell_wmi_aio_find();
140 if (!guid) {
141 pr_warning("No known WMI GUID found\n");
142 return -ENXIO;
143 }
144
145 err = dell_wmi_aio_input_setup();
146 if (err)
147 return err;
148
149 err = wmi_install_notify_handler(guid, dell_wmi_aio_notify, NULL);
150 if (err) {
151 pr_err("Unable to register notify handler - %d\n", err);
152 sparse_keymap_free(dell_wmi_aio_input_dev);
153 input_unregister_device(dell_wmi_aio_input_dev);
154 return err;
155 }
156
157 return 0;
158}
159
160static void __exit dell_wmi_aio_exit(void)
161{
162 const char *guid;
163
164 guid = dell_wmi_aio_find();
165 wmi_remove_notify_handler(guid);
166 sparse_keymap_free(dell_wmi_aio_input_dev);
167 input_unregister_device(dell_wmi_aio_input_dev);
168}
169
170module_init(dell_wmi_aio_init);
171module_exit(dell_wmi_aio_exit);