diff options
Diffstat (limited to 'drivers/platform/x86/dell-wmi.c')
-rw-r--r-- | drivers/platform/x86/dell-wmi.c | 210 |
1 files changed, 210 insertions, 0 deletions
diff --git a/drivers/platform/x86/dell-wmi.c b/drivers/platform/x86/dell-wmi.c new file mode 100644 index 00000000000..2fab9416214 --- /dev/null +++ b/drivers/platform/x86/dell-wmi.c | |||
@@ -0,0 +1,210 @@ | |||
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 | #include <linux/kernel.h> | ||
27 | #include <linux/module.h> | ||
28 | #include <linux/init.h> | ||
29 | #include <linux/types.h> | ||
30 | #include <linux/input.h> | ||
31 | #include <acpi/acpi_drivers.h> | ||
32 | #include <linux/acpi.h> | ||
33 | #include <linux/string.h> | ||
34 | |||
35 | MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>"); | ||
36 | MODULE_DESCRIPTION("Dell laptop WMI hotkeys driver"); | ||
37 | MODULE_LICENSE("GPL"); | ||
38 | |||
39 | #define DELL_EVENT_GUID "9DBB5994-A997-11DA-B012-B622A1EF5492" | ||
40 | |||
41 | MODULE_ALIAS("wmi:"DELL_EVENT_GUID); | ||
42 | |||
43 | struct key_entry { | ||
44 | char type; /* See KE_* below */ | ||
45 | u16 code; | ||
46 | u16 keycode; | ||
47 | }; | ||
48 | |||
49 | enum { KE_KEY, KE_SW, KE_END }; | ||
50 | |||
51 | static struct key_entry dell_wmi_keymap[] = { | ||
52 | {KE_KEY, 0xe045, KEY_PROG1}, | ||
53 | {KE_END, 0} | ||
54 | }; | ||
55 | |||
56 | static struct input_dev *dell_wmi_input_dev; | ||
57 | |||
58 | static struct key_entry *dell_wmi_get_entry_by_scancode(int code) | ||
59 | { | ||
60 | struct key_entry *key; | ||
61 | |||
62 | for (key = dell_wmi_keymap; key->type != KE_END; key++) | ||
63 | if (code == key->code) | ||
64 | return key; | ||
65 | |||
66 | return NULL; | ||
67 | } | ||
68 | |||
69 | static struct key_entry *dell_wmi_get_entry_by_keycode(int keycode) | ||
70 | { | ||
71 | struct key_entry *key; | ||
72 | |||
73 | for (key = dell_wmi_keymap; key->type != KE_END; key++) | ||
74 | if (key->type == KE_KEY && keycode == key->keycode) | ||
75 | return key; | ||
76 | |||
77 | return NULL; | ||
78 | } | ||
79 | |||
80 | static int dell_wmi_getkeycode(struct input_dev *dev, int scancode, | ||
81 | int *keycode) | ||
82 | { | ||
83 | struct key_entry *key = dell_wmi_get_entry_by_scancode(scancode); | ||
84 | |||
85 | if (key && key->type == KE_KEY) { | ||
86 | *keycode = key->keycode; | ||
87 | return 0; | ||
88 | } | ||
89 | |||
90 | return -EINVAL; | ||
91 | } | ||
92 | |||
93 | static int dell_wmi_setkeycode(struct input_dev *dev, int scancode, int keycode) | ||
94 | { | ||
95 | struct key_entry *key; | ||
96 | int old_keycode; | ||
97 | |||
98 | if (keycode < 0 || keycode > KEY_MAX) | ||
99 | return -EINVAL; | ||
100 | |||
101 | key = dell_wmi_get_entry_by_scancode(scancode); | ||
102 | if (key && key->type == KE_KEY) { | ||
103 | old_keycode = key->keycode; | ||
104 | key->keycode = keycode; | ||
105 | set_bit(keycode, dev->keybit); | ||
106 | if (!dell_wmi_get_entry_by_keycode(old_keycode)) | ||
107 | clear_bit(old_keycode, dev->keybit); | ||
108 | return 0; | ||
109 | } | ||
110 | return -EINVAL; | ||
111 | } | ||
112 | |||
113 | static void dell_wmi_notify(u32 value, void *context) | ||
114 | { | ||
115 | struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
116 | static struct key_entry *key; | ||
117 | union acpi_object *obj; | ||
118 | |||
119 | wmi_get_event_data(value, &response); | ||
120 | |||
121 | obj = (union acpi_object *)response.pointer; | ||
122 | |||
123 | if (obj && obj->type == ACPI_TYPE_BUFFER) { | ||
124 | int *buffer = (int *)obj->buffer.pointer; | ||
125 | key = dell_wmi_get_entry_by_scancode(buffer[1]); | ||
126 | if (key) { | ||
127 | input_report_key(dell_wmi_input_dev, key->keycode, 1); | ||
128 | input_sync(dell_wmi_input_dev); | ||
129 | input_report_key(dell_wmi_input_dev, key->keycode, 0); | ||
130 | input_sync(dell_wmi_input_dev); | ||
131 | } else | ||
132 | printk(KERN_INFO "dell-wmi: Unknown key %x pressed\n", | ||
133 | buffer[1]); | ||
134 | } | ||
135 | } | ||
136 | |||
137 | static int __init dell_wmi_input_setup(void) | ||
138 | { | ||
139 | struct key_entry *key; | ||
140 | int err; | ||
141 | |||
142 | dell_wmi_input_dev = input_allocate_device(); | ||
143 | |||
144 | if (!dell_wmi_input_dev) | ||
145 | return -ENOMEM; | ||
146 | |||
147 | dell_wmi_input_dev->name = "Dell WMI hotkeys"; | ||
148 | dell_wmi_input_dev->phys = "wmi/input0"; | ||
149 | dell_wmi_input_dev->id.bustype = BUS_HOST; | ||
150 | dell_wmi_input_dev->getkeycode = dell_wmi_getkeycode; | ||
151 | dell_wmi_input_dev->setkeycode = dell_wmi_setkeycode; | ||
152 | |||
153 | for (key = dell_wmi_keymap; key->type != KE_END; key++) { | ||
154 | switch (key->type) { | ||
155 | case KE_KEY: | ||
156 | set_bit(EV_KEY, dell_wmi_input_dev->evbit); | ||
157 | set_bit(key->keycode, dell_wmi_input_dev->keybit); | ||
158 | break; | ||
159 | case KE_SW: | ||
160 | set_bit(EV_SW, dell_wmi_input_dev->evbit); | ||
161 | set_bit(key->keycode, dell_wmi_input_dev->swbit); | ||
162 | break; | ||
163 | } | ||
164 | } | ||
165 | |||
166 | err = input_register_device(dell_wmi_input_dev); | ||
167 | |||
168 | if (err) { | ||
169 | input_free_device(dell_wmi_input_dev); | ||
170 | return err; | ||
171 | } | ||
172 | |||
173 | return 0; | ||
174 | } | ||
175 | |||
176 | static int __init dell_wmi_init(void) | ||
177 | { | ||
178 | int err; | ||
179 | |||
180 | if (wmi_has_guid(DELL_EVENT_GUID)) { | ||
181 | err = dell_wmi_input_setup(); | ||
182 | |||
183 | if (err) | ||
184 | return err; | ||
185 | |||
186 | err = wmi_install_notify_handler(DELL_EVENT_GUID, | ||
187 | dell_wmi_notify, NULL); | ||
188 | if (err) { | ||
189 | input_unregister_device(dell_wmi_input_dev); | ||
190 | printk(KERN_ERR "dell-wmi: Unable to register" | ||
191 | " notify handler - %d\n", err); | ||
192 | return err; | ||
193 | } | ||
194 | |||
195 | } else | ||
196 | printk(KERN_WARNING "dell-wmi: No known WMI GUID found\n"); | ||
197 | |||
198 | return 0; | ||
199 | } | ||
200 | |||
201 | static void __exit dell_wmi_exit(void) | ||
202 | { | ||
203 | if (wmi_has_guid(DELL_EVENT_GUID)) { | ||
204 | wmi_remove_notify_handler(DELL_EVENT_GUID); | ||
205 | input_unregister_device(dell_wmi_input_dev); | ||
206 | } | ||
207 | } | ||
208 | |||
209 | module_init(dell_wmi_init); | ||
210 | module_exit(dell_wmi_exit); | ||