diff options
Diffstat (limited to 'drivers/platform/x86/hp-wmi.c')
-rw-r--r-- | drivers/platform/x86/hp-wmi.c | 172 |
1 files changed, 51 insertions, 121 deletions
diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index c1741142a4cb..1dac659b5e0c 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c | |||
@@ -29,6 +29,7 @@ | |||
29 | #include <linux/slab.h> | 29 | #include <linux/slab.h> |
30 | #include <linux/types.h> | 30 | #include <linux/types.h> |
31 | #include <linux/input.h> | 31 | #include <linux/input.h> |
32 | #include <linux/input/sparse-keymap.h> | ||
32 | #include <linux/platform_device.h> | 33 | #include <linux/platform_device.h> |
33 | #include <linux/acpi.h> | 34 | #include <linux/acpi.h> |
34 | #include <linux/rfkill.h> | 35 | #include <linux/rfkill.h> |
@@ -88,24 +89,16 @@ struct bios_return { | |||
88 | u32 value; | 89 | u32 value; |
89 | }; | 90 | }; |
90 | 91 | ||
91 | struct key_entry { | 92 | static const struct key_entry hp_wmi_keymap[] = { |
92 | char type; /* See KE_* below */ | 93 | { KE_KEY, 0x02, { KEY_BRIGHTNESSUP } }, |
93 | u16 code; | 94 | { KE_KEY, 0x03, { KEY_BRIGHTNESSDOWN } }, |
94 | u16 keycode; | 95 | { KE_KEY, 0x20e6, { KEY_PROG1 } }, |
95 | }; | 96 | { KE_KEY, 0x20e8, { KEY_MEDIA } }, |
96 | 97 | { KE_KEY, 0x2142, { KEY_MEDIA } }, | |
97 | enum { KE_KEY, KE_END }; | 98 | { KE_KEY, 0x213b, { KEY_INFO } }, |
98 | 99 | { KE_KEY, 0x2169, { KEY_DIRECTION } }, | |
99 | static struct key_entry hp_wmi_keymap[] = { | 100 | { KE_KEY, 0x231b, { KEY_HELP } }, |
100 | {KE_KEY, 0x02, KEY_BRIGHTNESSUP}, | 101 | { KE_END, 0 } |
101 | {KE_KEY, 0x03, KEY_BRIGHTNESSDOWN}, | ||
102 | {KE_KEY, 0x20e6, KEY_PROG1}, | ||
103 | {KE_KEY, 0x20e8, KEY_MEDIA}, | ||
104 | {KE_KEY, 0x2142, KEY_MEDIA}, | ||
105 | {KE_KEY, 0x213b, KEY_INFO}, | ||
106 | {KE_KEY, 0x2169, KEY_DIRECTION}, | ||
107 | {KE_KEY, 0x231b, KEY_HELP}, | ||
108 | {KE_END, 0} | ||
109 | }; | 102 | }; |
110 | 103 | ||
111 | static struct input_dev *hp_wmi_input_dev; | 104 | static struct input_dev *hp_wmi_input_dev; |
@@ -347,64 +340,9 @@ static DEVICE_ATTR(als, S_IRUGO | S_IWUSR, show_als, set_als); | |||
347 | static DEVICE_ATTR(dock, S_IRUGO, show_dock, NULL); | 340 | static DEVICE_ATTR(dock, S_IRUGO, show_dock, NULL); |
348 | static DEVICE_ATTR(tablet, S_IRUGO, show_tablet, NULL); | 341 | static DEVICE_ATTR(tablet, S_IRUGO, show_tablet, NULL); |
349 | 342 | ||
350 | static struct key_entry *hp_wmi_get_entry_by_scancode(unsigned int code) | ||
351 | { | ||
352 | struct key_entry *key; | ||
353 | |||
354 | for (key = hp_wmi_keymap; key->type != KE_END; key++) | ||
355 | if (code == key->code) | ||
356 | return key; | ||
357 | |||
358 | return NULL; | ||
359 | } | ||
360 | |||
361 | static struct key_entry *hp_wmi_get_entry_by_keycode(unsigned int keycode) | ||
362 | { | ||
363 | struct key_entry *key; | ||
364 | |||
365 | for (key = hp_wmi_keymap; key->type != KE_END; key++) | ||
366 | if (key->type == KE_KEY && keycode == key->keycode) | ||
367 | return key; | ||
368 | |||
369 | return NULL; | ||
370 | } | ||
371 | |||
372 | static int hp_wmi_getkeycode(struct input_dev *dev, | ||
373 | unsigned int scancode, unsigned int *keycode) | ||
374 | { | ||
375 | struct key_entry *key = hp_wmi_get_entry_by_scancode(scancode); | ||
376 | |||
377 | if (key && key->type == KE_KEY) { | ||
378 | *keycode = key->keycode; | ||
379 | return 0; | ||
380 | } | ||
381 | |||
382 | return -EINVAL; | ||
383 | } | ||
384 | |||
385 | static int hp_wmi_setkeycode(struct input_dev *dev, | ||
386 | unsigned int scancode, unsigned int keycode) | ||
387 | { | ||
388 | struct key_entry *key; | ||
389 | unsigned int old_keycode; | ||
390 | |||
391 | key = hp_wmi_get_entry_by_scancode(scancode); | ||
392 | if (key && key->type == KE_KEY) { | ||
393 | old_keycode = key->keycode; | ||
394 | key->keycode = keycode; | ||
395 | set_bit(keycode, dev->keybit); | ||
396 | if (!hp_wmi_get_entry_by_keycode(old_keycode)) | ||
397 | clear_bit(old_keycode, dev->keybit); | ||
398 | return 0; | ||
399 | } | ||
400 | |||
401 | return -EINVAL; | ||
402 | } | ||
403 | |||
404 | static void hp_wmi_notify(u32 value, void *context) | 343 | static void hp_wmi_notify(u32 value, void *context) |
405 | { | 344 | { |
406 | struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; | 345 | struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; |
407 | static struct key_entry *key; | ||
408 | union acpi_object *obj; | 346 | union acpi_object *obj; |
409 | u32 event_id, event_data; | 347 | u32 event_id, event_data; |
410 | int key_code = 0, ret; | 348 | int key_code = 0, ret; |
@@ -465,19 +403,9 @@ static void hp_wmi_notify(u32 value, void *context) | |||
465 | sizeof(key_code)); | 403 | sizeof(key_code)); |
466 | if (ret) | 404 | if (ret) |
467 | break; | 405 | break; |
468 | key = hp_wmi_get_entry_by_scancode(key_code); | 406 | |
469 | if (key) { | 407 | if (!sparse_keymap_report_event(hp_wmi_input_dev, |
470 | switch (key->type) { | 408 | key_code, 1, true)) |
471 | case KE_KEY: | ||
472 | input_report_key(hp_wmi_input_dev, | ||
473 | key->keycode, 1); | ||
474 | input_sync(hp_wmi_input_dev); | ||
475 | input_report_key(hp_wmi_input_dev, | ||
476 | key->keycode, 0); | ||
477 | input_sync(hp_wmi_input_dev); | ||
478 | break; | ||
479 | } | ||
480 | } else | ||
481 | printk(KERN_INFO PREFIX "Unknown key code - 0x%x\n", | 409 | printk(KERN_INFO PREFIX "Unknown key code - 0x%x\n", |
482 | key_code); | 410 | key_code); |
483 | break; | 411 | break; |
@@ -510,7 +438,7 @@ static void hp_wmi_notify(u32 value, void *context) | |||
510 | 438 | ||
511 | static int __init hp_wmi_input_setup(void) | 439 | static int __init hp_wmi_input_setup(void) |
512 | { | 440 | { |
513 | struct key_entry *key; | 441 | acpi_status status; |
514 | int err; | 442 | int err; |
515 | 443 | ||
516 | hp_wmi_input_dev = input_allocate_device(); | 444 | hp_wmi_input_dev = input_allocate_device(); |
@@ -520,21 +448,14 @@ static int __init hp_wmi_input_setup(void) | |||
520 | hp_wmi_input_dev->name = "HP WMI hotkeys"; | 448 | hp_wmi_input_dev->name = "HP WMI hotkeys"; |
521 | hp_wmi_input_dev->phys = "wmi/input0"; | 449 | hp_wmi_input_dev->phys = "wmi/input0"; |
522 | hp_wmi_input_dev->id.bustype = BUS_HOST; | 450 | hp_wmi_input_dev->id.bustype = BUS_HOST; |
523 | hp_wmi_input_dev->getkeycode = hp_wmi_getkeycode; | ||
524 | hp_wmi_input_dev->setkeycode = hp_wmi_setkeycode; | ||
525 | |||
526 | for (key = hp_wmi_keymap; key->type != KE_END; key++) { | ||
527 | switch (key->type) { | ||
528 | case KE_KEY: | ||
529 | set_bit(EV_KEY, hp_wmi_input_dev->evbit); | ||
530 | set_bit(key->keycode, hp_wmi_input_dev->keybit); | ||
531 | break; | ||
532 | } | ||
533 | } | ||
534 | 451 | ||
535 | set_bit(EV_SW, hp_wmi_input_dev->evbit); | 452 | __set_bit(EV_SW, hp_wmi_input_dev->evbit); |
536 | set_bit(SW_DOCK, hp_wmi_input_dev->swbit); | 453 | __set_bit(SW_DOCK, hp_wmi_input_dev->swbit); |
537 | set_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit); | 454 | __set_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit); |
455 | |||
456 | err = sparse_keymap_setup(hp_wmi_input_dev, hp_wmi_keymap, NULL); | ||
457 | if (err) | ||
458 | goto err_free_dev; | ||
538 | 459 | ||
539 | /* Set initial hardware state */ | 460 | /* Set initial hardware state */ |
540 | input_report_switch(hp_wmi_input_dev, SW_DOCK, hp_wmi_dock_state()); | 461 | input_report_switch(hp_wmi_input_dev, SW_DOCK, hp_wmi_dock_state()); |
@@ -542,14 +463,32 @@ static int __init hp_wmi_input_setup(void) | |||
542 | hp_wmi_tablet_state()); | 463 | hp_wmi_tablet_state()); |
543 | input_sync(hp_wmi_input_dev); | 464 | input_sync(hp_wmi_input_dev); |
544 | 465 | ||
545 | err = input_register_device(hp_wmi_input_dev); | 466 | status = wmi_install_notify_handler(HPWMI_EVENT_GUID, hp_wmi_notify, NULL); |
546 | 467 | if (ACPI_FAILURE(status)) { | |
547 | if (err) { | 468 | err = -EIO; |
548 | input_free_device(hp_wmi_input_dev); | 469 | goto err_free_keymap; |
549 | return err; | ||
550 | } | 470 | } |
551 | 471 | ||
472 | err = input_register_device(hp_wmi_input_dev); | ||
473 | if (err) | ||
474 | goto err_uninstall_notifier; | ||
475 | |||
552 | return 0; | 476 | return 0; |
477 | |||
478 | err_uninstall_notifier: | ||
479 | wmi_remove_notify_handler(HPWMI_EVENT_GUID); | ||
480 | err_free_keymap: | ||
481 | sparse_keymap_free(hp_wmi_input_dev); | ||
482 | err_free_dev: | ||
483 | input_free_device(hp_wmi_input_dev); | ||
484 | return err; | ||
485 | } | ||
486 | |||
487 | static void hp_wmi_input_destroy(void) | ||
488 | { | ||
489 | wmi_remove_notify_handler(HPWMI_EVENT_GUID); | ||
490 | sparse_keymap_free(hp_wmi_input_dev); | ||
491 | input_unregister_device(hp_wmi_input_dev); | ||
553 | } | 492 | } |
554 | 493 | ||
555 | static void cleanup_sysfs(struct platform_device *device) | 494 | static void cleanup_sysfs(struct platform_device *device) |
@@ -704,15 +643,9 @@ static int __init hp_wmi_init(void) | |||
704 | int bios_capable = wmi_has_guid(HPWMI_BIOS_GUID); | 643 | int bios_capable = wmi_has_guid(HPWMI_BIOS_GUID); |
705 | 644 | ||
706 | if (event_capable) { | 645 | if (event_capable) { |
707 | err = wmi_install_notify_handler(HPWMI_EVENT_GUID, | ||
708 | hp_wmi_notify, NULL); | ||
709 | if (ACPI_FAILURE(err)) | ||
710 | return -EINVAL; | ||
711 | err = hp_wmi_input_setup(); | 646 | err = hp_wmi_input_setup(); |
712 | if (err) { | 647 | if (err) |
713 | wmi_remove_notify_handler(HPWMI_EVENT_GUID); | ||
714 | return err; | 648 | return err; |
715 | } | ||
716 | } | 649 | } |
717 | 650 | ||
718 | if (bios_capable) { | 651 | if (bios_capable) { |
@@ -739,20 +672,17 @@ err_device_add: | |||
739 | err_device_alloc: | 672 | err_device_alloc: |
740 | platform_driver_unregister(&hp_wmi_driver); | 673 | platform_driver_unregister(&hp_wmi_driver); |
741 | err_driver_reg: | 674 | err_driver_reg: |
742 | if (wmi_has_guid(HPWMI_EVENT_GUID)) { | 675 | if (event_capable) |
743 | input_unregister_device(hp_wmi_input_dev); | 676 | hp_wmi_input_destroy(); |
744 | wmi_remove_notify_handler(HPWMI_EVENT_GUID); | ||
745 | } | ||
746 | 677 | ||
747 | return err; | 678 | return err; |
748 | } | 679 | } |
749 | 680 | ||
750 | static void __exit hp_wmi_exit(void) | 681 | static void __exit hp_wmi_exit(void) |
751 | { | 682 | { |
752 | if (wmi_has_guid(HPWMI_EVENT_GUID)) { | 683 | if (wmi_has_guid(HPWMI_EVENT_GUID)) |
753 | wmi_remove_notify_handler(HPWMI_EVENT_GUID); | 684 | hp_wmi_input_destroy(); |
754 | input_unregister_device(hp_wmi_input_dev); | 685 | |
755 | } | ||
756 | if (hp_wmi_platform_dev) { | 686 | if (hp_wmi_platform_dev) { |
757 | platform_device_unregister(hp_wmi_platform_dev); | 687 | platform_device_unregister(hp_wmi_platform_dev); |
758 | platform_driver_unregister(&hp_wmi_driver); | 688 | platform_driver_unregister(&hp_wmi_driver); |