diff options
Diffstat (limited to 'drivers/platform')
-rw-r--r-- | drivers/platform/x86/dell-wmi.c | 129 |
1 files changed, 116 insertions, 13 deletions
diff --git a/drivers/platform/x86/dell-wmi.c b/drivers/platform/x86/dell-wmi.c index 0f900cc9fa7..67f3fe71c50 100644 --- a/drivers/platform/x86/dell-wmi.c +++ b/drivers/platform/x86/dell-wmi.c | |||
@@ -31,6 +31,7 @@ | |||
31 | #include <acpi/acpi_drivers.h> | 31 | #include <acpi/acpi_drivers.h> |
32 | #include <linux/acpi.h> | 32 | #include <linux/acpi.h> |
33 | #include <linux/string.h> | 33 | #include <linux/string.h> |
34 | #include <linux/dmi.h> | ||
34 | 35 | ||
35 | MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>"); | 36 | MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>"); |
36 | MODULE_DESCRIPTION("Dell laptop WMI hotkeys driver"); | 37 | MODULE_DESCRIPTION("Dell laptop WMI hotkeys driver"); |
@@ -38,6 +39,8 @@ MODULE_LICENSE("GPL"); | |||
38 | 39 | ||
39 | #define DELL_EVENT_GUID "9DBB5994-A997-11DA-B012-B622A1EF5492" | 40 | #define DELL_EVENT_GUID "9DBB5994-A997-11DA-B012-B622A1EF5492" |
40 | 41 | ||
42 | static int acpi_video; | ||
43 | |||
41 | MODULE_ALIAS("wmi:"DELL_EVENT_GUID); | 44 | MODULE_ALIAS("wmi:"DELL_EVENT_GUID); |
42 | 45 | ||
43 | struct key_entry { | 46 | struct key_entry { |
@@ -54,7 +57,7 @@ enum { KE_KEY, KE_SW, KE_IGNORE, KE_END }; | |||
54 | * via the keyboard controller so should not be sent again. | 57 | * via the keyboard controller so should not be sent again. |
55 | */ | 58 | */ |
56 | 59 | ||
57 | static struct key_entry dell_wmi_keymap[] = { | 60 | static struct key_entry dell_legacy_wmi_keymap[] = { |
58 | {KE_KEY, 0xe045, KEY_PROG1}, | 61 | {KE_KEY, 0xe045, KEY_PROG1}, |
59 | {KE_KEY, 0xe009, KEY_EJECTCD}, | 62 | {KE_KEY, 0xe009, KEY_EJECTCD}, |
60 | 63 | ||
@@ -72,7 +75,7 @@ static struct key_entry dell_wmi_keymap[] = { | |||
72 | 75 | ||
73 | /* The next device is at offset 6, the active devices are at | 76 | /* The next device is at offset 6, the active devices are at |
74 | offset 8 and the attached devices at offset 10 */ | 77 | offset 8 and the attached devices at offset 10 */ |
75 | {KE_KEY, 0xe00b, KEY_DISPLAYTOGGLE}, | 78 | {KE_KEY, 0xe00b, KEY_SWITCHVIDEOMODE}, |
76 | 79 | ||
77 | {KE_IGNORE, 0xe00c, KEY_KBDILLUMTOGGLE}, | 80 | {KE_IGNORE, 0xe00c, KEY_KBDILLUMTOGGLE}, |
78 | 81 | ||
@@ -96,6 +99,47 @@ static struct key_entry dell_wmi_keymap[] = { | |||
96 | {KE_END, 0} | 99 | {KE_END, 0} |
97 | }; | 100 | }; |
98 | 101 | ||
102 | static bool dell_new_hk_type; | ||
103 | |||
104 | struct dell_new_keymap_entry { | ||
105 | u16 scancode; | ||
106 | u16 keycode; | ||
107 | }; | ||
108 | |||
109 | struct dell_hotkey_table { | ||
110 | struct dmi_header header; | ||
111 | struct dell_new_keymap_entry keymap[]; | ||
112 | |||
113 | }; | ||
114 | |||
115 | static struct key_entry *dell_new_wmi_keymap; | ||
116 | |||
117 | static u16 bios_to_linux_keycode[256] = { | ||
118 | |||
119 | KEY_MEDIA, KEY_NEXTSONG, KEY_PLAYPAUSE, KEY_PREVIOUSSONG, | ||
120 | KEY_STOPCD, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, | ||
121 | KEY_WWW, KEY_UNKNOWN, KEY_VOLUMEDOWN, KEY_MUTE, | ||
122 | KEY_VOLUMEUP, KEY_UNKNOWN, KEY_BATTERY, KEY_EJECTCD, | ||
123 | KEY_UNKNOWN, KEY_SLEEP, KEY_PROG1, KEY_BRIGHTNESSDOWN, | ||
124 | KEY_BRIGHTNESSUP, KEY_UNKNOWN, KEY_KBDILLUMTOGGLE, | ||
125 | KEY_UNKNOWN, KEY_SWITCHVIDEOMODE, KEY_UNKNOWN, KEY_UNKNOWN, | ||
126 | KEY_SWITCHVIDEOMODE, KEY_UNKNOWN, KEY_UNKNOWN, KEY_PROG2, | ||
127 | KEY_UNKNOWN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
128 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
129 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
130 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
131 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
132 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
133 | 0, 0, 0, 0, 0, 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, | ||
137 | KEY_PROG3 | ||
138 | }; | ||
139 | |||
140 | |||
141 | static struct key_entry *dell_wmi_keymap = dell_legacy_wmi_keymap; | ||
142 | |||
99 | static struct input_dev *dell_wmi_input_dev; | 143 | static struct input_dev *dell_wmi_input_dev; |
100 | 144 | ||
101 | static struct key_entry *dell_wmi_get_entry_by_scancode(int code) | 145 | static struct key_entry *dell_wmi_get_entry_by_scancode(int code) |
@@ -164,24 +208,78 @@ static void dell_wmi_notify(u32 value, void *context) | |||
164 | obj = (union acpi_object *)response.pointer; | 208 | obj = (union acpi_object *)response.pointer; |
165 | 209 | ||
166 | if (obj && obj->type == ACPI_TYPE_BUFFER) { | 210 | if (obj && obj->type == ACPI_TYPE_BUFFER) { |
167 | int *buffer = (int *)obj->buffer.pointer; | 211 | int reported_key; |
168 | /* | 212 | u16 *buffer_entry = (u16 *)obj->buffer.pointer; |
169 | * The upper bytes of the event may contain | 213 | if (dell_new_hk_type && (buffer_entry[1] != 0x10)) { |
170 | * additional information, so mask them off for the | 214 | printk(KERN_INFO "dell-wmi: Received unknown WMI event" |
171 | * scancode lookup | 215 | " (0x%x)\n", buffer_entry[1]); |
172 | */ | 216 | return; |
173 | key = dell_wmi_get_entry_by_scancode(buffer[1] & 0xFFFF); | 217 | } |
174 | if (key) { | 218 | |
219 | if (dell_new_hk_type) | ||
220 | reported_key = (int)buffer_entry[2]; | ||
221 | else | ||
222 | reported_key = (int)buffer_entry[1] & 0xffff; | ||
223 | |||
224 | key = dell_wmi_get_entry_by_scancode(reported_key); | ||
225 | |||
226 | if (!key) { | ||
227 | printk(KERN_INFO "dell-wmi: Unknown key %x pressed\n", | ||
228 | reported_key); | ||
229 | } else if ((key->keycode == KEY_BRIGHTNESSUP || | ||
230 | key->keycode == KEY_BRIGHTNESSDOWN) && acpi_video) { | ||
231 | /* Don't report brightness notifications that will also | ||
232 | * come via ACPI */ | ||
233 | return; | ||
234 | } else { | ||
175 | input_report_key(dell_wmi_input_dev, key->keycode, 1); | 235 | input_report_key(dell_wmi_input_dev, key->keycode, 1); |
176 | input_sync(dell_wmi_input_dev); | 236 | input_sync(dell_wmi_input_dev); |
177 | input_report_key(dell_wmi_input_dev, key->keycode, 0); | 237 | input_report_key(dell_wmi_input_dev, key->keycode, 0); |
178 | input_sync(dell_wmi_input_dev); | 238 | input_sync(dell_wmi_input_dev); |
179 | } else if (buffer[1] & 0xFFFF) | 239 | } |
180 | printk(KERN_INFO "dell-wmi: Unknown key %x pressed\n", | ||
181 | buffer[1] & 0xFFFF); | ||
182 | } | 240 | } |
183 | } | 241 | } |
184 | 242 | ||
243 | |||
244 | static void setup_new_hk_map(const struct dmi_header *dm) | ||
245 | { | ||
246 | |||
247 | int i; | ||
248 | int hotkey_num = (dm->length-4)/sizeof(struct dell_new_keymap_entry); | ||
249 | struct dell_hotkey_table *table = | ||
250 | container_of(dm, struct dell_hotkey_table, header); | ||
251 | |||
252 | dell_new_wmi_keymap = kzalloc((hotkey_num+1) * | ||
253 | sizeof(struct key_entry), GFP_KERNEL); | ||
254 | |||
255 | for (i = 0; i < hotkey_num; i++) { | ||
256 | dell_new_wmi_keymap[i].type = KE_KEY; | ||
257 | dell_new_wmi_keymap[i].code = table->keymap[i].scancode; | ||
258 | dell_new_wmi_keymap[i].keycode = | ||
259 | (table->keymap[i].keycode > 255) ? 0 : | ||
260 | bios_to_linux_keycode[table->keymap[i].keycode]; | ||
261 | } | ||
262 | |||
263 | dell_new_wmi_keymap[i].type = KE_END; | ||
264 | dell_new_wmi_keymap[i].code = 0; | ||
265 | dell_new_wmi_keymap[i].keycode = 0; | ||
266 | |||
267 | dell_wmi_keymap = dell_new_wmi_keymap; | ||
268 | |||
269 | } | ||
270 | |||
271 | |||
272 | static void find_hk_type(const struct dmi_header *dm, void *dummy) | ||
273 | { | ||
274 | |||
275 | if ((dm->type == 0xb2) && (dm->length > 6)) { | ||
276 | dell_new_hk_type = true; | ||
277 | setup_new_hk_map(dm); | ||
278 | } | ||
279 | |||
280 | } | ||
281 | |||
282 | |||
185 | static int __init dell_wmi_input_setup(void) | 283 | static int __init dell_wmi_input_setup(void) |
186 | { | 284 | { |
187 | struct key_entry *key; | 285 | struct key_entry *key; |
@@ -226,6 +324,9 @@ static int __init dell_wmi_init(void) | |||
226 | int err; | 324 | int err; |
227 | 325 | ||
228 | if (wmi_has_guid(DELL_EVENT_GUID)) { | 326 | if (wmi_has_guid(DELL_EVENT_GUID)) { |
327 | |||
328 | dmi_walk(find_hk_type, NULL); | ||
329 | |||
229 | err = dell_wmi_input_setup(); | 330 | err = dell_wmi_input_setup(); |
230 | 331 | ||
231 | if (err) | 332 | if (err) |
@@ -240,6 +341,8 @@ static int __init dell_wmi_init(void) | |||
240 | return err; | 341 | return err; |
241 | } | 342 | } |
242 | 343 | ||
344 | acpi_video = acpi_video_backlight_support(); | ||
345 | |||
243 | } else | 346 | } else |
244 | printk(KERN_WARNING "dell-wmi: No known WMI GUID found\n"); | 347 | printk(KERN_WARNING "dell-wmi: No known WMI GUID found\n"); |
245 | 348 | ||