aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/platform/x86/Kconfig1
-rw-r--r--drivers/platform/x86/msi-wmi.c134
2 files changed, 33 insertions, 102 deletions
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 98ec6bd9226e..1f82d6df96e1 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -369,6 +369,7 @@ config MSI_WMI
369 tristate "MSI WMI extras" 369 tristate "MSI WMI extras"
370 depends on ACPI_WMI 370 depends on ACPI_WMI
371 depends on INPUT 371 depends on INPUT
372 select INPUT_SPARSEKMAP
372 help 373 help
373 Say Y here if you want to support WMI-based hotkeys on MSI laptops. 374 Say Y here if you want to support WMI-based hotkeys on MSI laptops.
374 375
diff --git a/drivers/platform/x86/msi-wmi.c b/drivers/platform/x86/msi-wmi.c
index 2c2afc1828b8..e25b80c530f7 100644
--- a/drivers/platform/x86/msi-wmi.c
+++ b/drivers/platform/x86/msi-wmi.c
@@ -21,9 +21,9 @@
21 */ 21 */
22 22
23 23
24
25#include <linux/kernel.h> 24#include <linux/kernel.h>
26#include <linux/input.h> 25#include <linux/input.h>
26#include <linux/input/sparse-keymap.h>
27#include <linux/acpi.h> 27#include <linux/acpi.h>
28#include <linux/backlight.h> 28#include <linux/backlight.h>
29 29
@@ -52,26 +52,15 @@ MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-6638:*");
52 52
53#define dprintk(msg...) pr_debug(DRV_PFX msg) 53#define dprintk(msg...) pr_debug(DRV_PFX msg)
54 54
55struct key_entry { 55#define KEYCODE_BASE 0xD0
56 char type; /* See KE_* below */
57 u16 code;
58 u16 keycode;
59 ktime_t last_pressed;
60};
61
62/*
63 * KE_KEY the only used key type, but keep this, others might also
64 * show up in the future. Compare with hp-wmi.c
65 */
66enum { KE_KEY, KE_END };
67
68static struct key_entry msi_wmi_keymap[] = { 56static struct key_entry msi_wmi_keymap[] = {
69 { KE_KEY, 0xd0, KEY_BRIGHTNESSUP, {0, } }, 57 { KE_KEY, KEYCODE_BASE, {KEY_BRIGHTNESSUP} },
70 { KE_KEY, 0xd1, KEY_BRIGHTNESSDOWN, {0, } }, 58 { KE_KEY, KEYCODE_BASE + 1, {KEY_BRIGHTNESSDOWN} },
71 { KE_KEY, 0xd2, KEY_VOLUMEUP, {0, } }, 59 { KE_KEY, KEYCODE_BASE + 2, {KEY_VOLUMEUP} },
72 { KE_KEY, 0xd3, KEY_VOLUMEDOWN, {0, } }, 60 { KE_KEY, KEYCODE_BASE + 3, {KEY_VOLUMEDOWN} },
73 { KE_END, 0} 61 { KE_END, 0}
74}; 62};
63static ktime_t last_pressed[ARRAY_SIZE(msi_wmi_keymap) - 1];
75 64
76struct backlight_device *backlight; 65struct backlight_device *backlight;
77 66
@@ -158,61 +147,6 @@ static struct backlight_ops msi_backlight_ops = {
158 .update_status = bl_set_status, 147 .update_status = bl_set_status,
159}; 148};
160 149
161static struct key_entry *msi_wmi_get_entry_by_scancode(int code)
162{
163 struct key_entry *key;
164
165 for (key = msi_wmi_keymap; key->type != KE_END; key++)
166 if (code == key->code)
167 return key;
168
169 return NULL;
170}
171
172static struct key_entry *msi_wmi_get_entry_by_keycode(int keycode)
173{
174 struct key_entry *key;
175
176 for (key = msi_wmi_keymap; key->type != KE_END; key++)
177 if (key->type == KE_KEY && keycode == key->keycode)
178 return key;
179
180 return NULL;
181}
182
183static int msi_wmi_getkeycode(struct input_dev *dev, int scancode, int *keycode)
184{
185 struct key_entry *key = msi_wmi_get_entry_by_scancode(scancode);
186
187 if (key && key->type == KE_KEY) {
188 *keycode = key->keycode;
189 return 0;
190 }
191
192 return -EINVAL;
193}
194
195static int msi_wmi_setkeycode(struct input_dev *dev, int scancode, int keycode)
196{
197 struct key_entry *key;
198 int old_keycode;
199
200 if (keycode < 0 || keycode > KEY_MAX)
201 return -EINVAL;
202
203 key = msi_wmi_get_entry_by_scancode(scancode);
204 if (key && key->type == KE_KEY) {
205 old_keycode = key->keycode;
206 key->keycode = keycode;
207 set_bit(keycode, dev->keybit);
208 if (!msi_wmi_get_entry_by_keycode(old_keycode))
209 clear_bit(old_keycode, dev->keybit);
210 return 0;
211 }
212
213 return -EINVAL;
214}
215
216static void msi_wmi_notify(u32 value, void *context) 150static void msi_wmi_notify(u32 value, void *context)
217{ 151{
218 struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; 152 struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
@@ -227,21 +161,22 @@ static void msi_wmi_notify(u32 value, void *context)
227 if (obj && obj->type == ACPI_TYPE_INTEGER) { 161 if (obj && obj->type == ACPI_TYPE_INTEGER) {
228 int eventcode = obj->integer.value; 162 int eventcode = obj->integer.value;
229 dprintk("Eventcode: 0x%x\n", eventcode); 163 dprintk("Eventcode: 0x%x\n", eventcode);
230 key = msi_wmi_get_entry_by_scancode(eventcode); 164 key = sparse_keymap_entry_from_scancode(msi_wmi_input_dev,
165 eventcode);
231 if (key) { 166 if (key) {
167 ktime_t diff;
232 cur = ktime_get_real(); 168 cur = ktime_get_real();
169 diff = ktime_sub(cur, last_pressed[key->code -
170 KEYCODE_BASE]);
233 /* Ignore event if the same event happened in a 50 ms 171 /* Ignore event if the same event happened in a 50 ms
234 timeframe -> Key press may result in 10-20 GPEs */ 172 timeframe -> Key press may result in 10-20 GPEs */
235 if (ktime_to_us(ktime_sub(cur, key->last_pressed)) 173 if (ktime_to_us(diff) < 1000 * 50) {
236 < 1000 * 50) {
237 dprintk("Suppressed key event 0x%X - " 174 dprintk("Suppressed key event 0x%X - "
238 "Last press was %lld us ago\n", 175 "Last press was %lld us ago\n",
239 key->code, 176 key->code, ktime_to_us(diff));
240 ktime_to_us(ktime_sub(cur,
241 key->last_pressed)));
242 return; 177 return;
243 } 178 }
244 key->last_pressed = cur; 179 last_pressed[key->code - KEYCODE_BASE] = cur;
245 180
246 if (key->type == KE_KEY && 181 if (key->type == KE_KEY &&
247 /* Brightness is served via acpi video driver */ 182 /* Brightness is served via acpi video driver */
@@ -250,12 +185,8 @@ static void msi_wmi_notify(u32 value, void *context)
250 dprintk("Send key: 0x%X - " 185 dprintk("Send key: 0x%X - "
251 "Input layer keycode: %d\n", key->code, 186 "Input layer keycode: %d\n", key->code,
252 key->keycode); 187 key->keycode);
253 input_report_key(msi_wmi_input_dev, 188 sparse_keymap_report_entry(msi_wmi_input_dev,
254 key->keycode, 1); 189 key, 1, true);
255 input_sync(msi_wmi_input_dev);
256 input_report_key(msi_wmi_input_dev,
257 key->keycode, 0);
258 input_sync(msi_wmi_input_dev);
259 } 190 }
260 } else 191 } else
261 printk(KERN_INFO "Unknown key pressed - %x\n", 192 printk(KERN_INFO "Unknown key pressed - %x\n",
@@ -267,7 +198,6 @@ static void msi_wmi_notify(u32 value, void *context)
267 198
268static int __init msi_wmi_input_setup(void) 199static int __init msi_wmi_input_setup(void)
269{ 200{
270 struct key_entry *key;
271 int err; 201 int err;
272 202
273 msi_wmi_input_dev = input_allocate_device(); 203 msi_wmi_input_dev = input_allocate_device();
@@ -277,26 +207,25 @@ static int __init msi_wmi_input_setup(void)
277 msi_wmi_input_dev->name = "MSI WMI hotkeys"; 207 msi_wmi_input_dev->name = "MSI WMI hotkeys";
278 msi_wmi_input_dev->phys = "wmi/input0"; 208 msi_wmi_input_dev->phys = "wmi/input0";
279 msi_wmi_input_dev->id.bustype = BUS_HOST; 209 msi_wmi_input_dev->id.bustype = BUS_HOST;
280 msi_wmi_input_dev->getkeycode = msi_wmi_getkeycode; 210
281 msi_wmi_input_dev->setkeycode = msi_wmi_setkeycode; 211 err = sparse_keymap_setup(msi_wmi_input_dev, msi_wmi_keymap, NULL);
282 212 if (err)
283 for (key = msi_wmi_keymap; key->type != KE_END; key++) { 213 goto err_free_dev;
284 switch (key->type) {
285 case KE_KEY:
286 set_bit(EV_KEY, msi_wmi_input_dev->evbit);
287 set_bit(key->keycode, msi_wmi_input_dev->keybit);
288 break;
289 }
290 }
291 214
292 err = input_register_device(msi_wmi_input_dev); 215 err = input_register_device(msi_wmi_input_dev);
293 216
294 if (err) { 217 if (err)
295 input_free_device(msi_wmi_input_dev); 218 goto err_free_keymap;
296 return err; 219
297 } 220 memset(last_pressed, 0, sizeof(last_pressed));
298 221
299 return 0; 222 return 0;
223
224err_free_keymap:
225 sparse_keymap_free(msi_wmi_input_dev);
226err_free_dev:
227 input_free_device(msi_wmi_input_dev);
228 return err;
300} 229}
301 230
302static int __init msi_wmi_init(void) 231static int __init msi_wmi_init(void)
@@ -347,6 +276,7 @@ static void __exit msi_wmi_exit(void)
347{ 276{
348 if (wmi_has_guid(MSIWMI_EVENT_GUID)) { 277 if (wmi_has_guid(MSIWMI_EVENT_GUID)) {
349 wmi_remove_notify_handler(MSIWMI_EVENT_GUID); 278 wmi_remove_notify_handler(MSIWMI_EVENT_GUID);
279 sparse_keymap_free(msi_wmi_input_dev);
350 input_unregister_device(msi_wmi_input_dev); 280 input_unregister_device(msi_wmi_input_dev);
351 backlight_device_unregister(backlight); 281 backlight_device_unregister(backlight);
352 } 282 }