diff options
author | Colin King <colin.king@canonical.com> | 2010-11-19 10:40:02 -0500 |
---|---|---|
committer | Matthew Garrett <mjg@redhat.com> | 2011-01-07 17:03:41 -0500 |
commit | 58f6425eb92f54943878b0b3f9c1e51f99c2cb72 (patch) | |
tree | aa4a5083adbf03561b9aee2d36129e0e7a5e2aa9 /drivers/platform | |
parent | 3098064d3b4a9bf9d2855b2a89599ad77695e324 (diff) |
WMI: Cater for multiple events with same GUID
WMI data blocks can contain WMI events with the same GUID but with
different notifiy_ids, for example volume up/down hotkeys.
This patch enables a single event handler to be registered and
unregistered against all events with same GUID but different
notify_ids. Since an event handler is passed the notify_id of
an event it can can differentiate between the different events.
The patch also ensures we only register and unregister a device per
unique GUID.
Signed-off-by: Colin Ian King <colin.king@canonical.com>
Signed-off-by: Matthew Garrett <mjg@redhat.com>
Diffstat (limited to 'drivers/platform')
-rw-r--r-- | drivers/platform/x86/wmi.c | 133 |
1 files changed, 72 insertions, 61 deletions
diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index aecd9a9b549f..b8e5383eab0c 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c | |||
@@ -549,21 +549,34 @@ acpi_status wmi_install_notify_handler(const char *guid, | |||
549 | wmi_notify_handler handler, void *data) | 549 | wmi_notify_handler handler, void *data) |
550 | { | 550 | { |
551 | struct wmi_block *block; | 551 | struct wmi_block *block; |
552 | acpi_status status; | 552 | acpi_status status = AE_NOT_EXIST; |
553 | char tmp[16], guid_input[16]; | ||
554 | struct list_head *p; | ||
553 | 555 | ||
554 | if (!guid || !handler) | 556 | if (!guid || !handler) |
555 | return AE_BAD_PARAMETER; | 557 | return AE_BAD_PARAMETER; |
556 | 558 | ||
557 | if (!find_guid(guid, &block)) | 559 | wmi_parse_guid(guid, tmp); |
558 | return AE_NOT_EXIST; | 560 | wmi_swap_bytes(tmp, guid_input); |
559 | 561 | ||
560 | if (block->handler && block->handler != wmi_notify_debug) | 562 | list_for_each(p, &wmi_block_list) { |
561 | return AE_ALREADY_ACQUIRED; | 563 | acpi_status wmi_status; |
564 | block = list_entry(p, struct wmi_block, list); | ||
562 | 565 | ||
563 | block->handler = handler; | 566 | if (memcmp(block->gblock.guid, guid_input, 16) == 0) { |
564 | block->handler_data = data; | 567 | if (block->handler && |
568 | block->handler != wmi_notify_debug) | ||
569 | return AE_ALREADY_ACQUIRED; | ||
565 | 570 | ||
566 | status = wmi_method_enable(block, 1); | 571 | block->handler = handler; |
572 | block->handler_data = data; | ||
573 | |||
574 | wmi_status = wmi_method_enable(block, 1); | ||
575 | if ((wmi_status != AE_OK) || | ||
576 | ((wmi_status == AE_OK) && (status == AE_NOT_EXIST))) | ||
577 | status = wmi_status; | ||
578 | } | ||
579 | } | ||
567 | 580 | ||
568 | return status; | 581 | return status; |
569 | } | 582 | } |
@@ -577,24 +590,40 @@ EXPORT_SYMBOL_GPL(wmi_install_notify_handler); | |||
577 | acpi_status wmi_remove_notify_handler(const char *guid) | 590 | acpi_status wmi_remove_notify_handler(const char *guid) |
578 | { | 591 | { |
579 | struct wmi_block *block; | 592 | struct wmi_block *block; |
580 | acpi_status status = AE_OK; | 593 | acpi_status status = AE_NOT_EXIST; |
594 | char tmp[16], guid_input[16]; | ||
595 | struct list_head *p; | ||
581 | 596 | ||
582 | if (!guid) | 597 | if (!guid) |
583 | return AE_BAD_PARAMETER; | 598 | return AE_BAD_PARAMETER; |
584 | 599 | ||
585 | if (!find_guid(guid, &block)) | 600 | wmi_parse_guid(guid, tmp); |
586 | return AE_NOT_EXIST; | 601 | wmi_swap_bytes(tmp, guid_input); |
587 | 602 | ||
588 | if (!block->handler || block->handler == wmi_notify_debug) | 603 | list_for_each(p, &wmi_block_list) { |
589 | return AE_NULL_ENTRY; | 604 | acpi_status wmi_status; |
605 | block = list_entry(p, struct wmi_block, list); | ||
590 | 606 | ||
591 | if (debug_event) { | 607 | if (memcmp(block->gblock.guid, guid_input, 16) == 0) { |
592 | block->handler = wmi_notify_debug; | 608 | if (!block->handler || |
593 | } else { | 609 | block->handler == wmi_notify_debug) |
594 | status = wmi_method_enable(block, 0); | 610 | return AE_NULL_ENTRY; |
595 | block->handler = NULL; | 611 | |
596 | block->handler_data = NULL; | 612 | if (debug_event) { |
613 | block->handler = wmi_notify_debug; | ||
614 | status = AE_OK; | ||
615 | } else { | ||
616 | wmi_status = wmi_method_enable(block, 0); | ||
617 | block->handler = NULL; | ||
618 | block->handler_data = NULL; | ||
619 | if ((wmi_status != AE_OK) || | ||
620 | ((wmi_status == AE_OK) && | ||
621 | (status == AE_NOT_EXIST))) | ||
622 | status = wmi_status; | ||
623 | } | ||
624 | } | ||
597 | } | 625 | } |
626 | |||
598 | return status; | 627 | return status; |
599 | } | 628 | } |
600 | EXPORT_SYMBOL_GPL(wmi_remove_notify_handler); | 629 | EXPORT_SYMBOL_GPL(wmi_remove_notify_handler); |
@@ -705,22 +734,11 @@ static struct class wmi_class = { | |||
705 | .dev_attrs = wmi_dev_attrs, | 734 | .dev_attrs = wmi_dev_attrs, |
706 | }; | 735 | }; |
707 | 736 | ||
708 | static struct wmi_block *wmi_create_device(const struct guid_block *gblock, | 737 | static int wmi_create_device(const struct guid_block *gblock, |
709 | acpi_handle handle) | 738 | struct wmi_block *wblock, acpi_handle handle) |
710 | { | 739 | { |
711 | struct wmi_block *wblock; | ||
712 | int error; | ||
713 | char guid_string[37]; | 740 | char guid_string[37]; |
714 | 741 | ||
715 | wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL); | ||
716 | if (!wblock) { | ||
717 | error = -ENOMEM; | ||
718 | goto err_out; | ||
719 | } | ||
720 | |||
721 | wblock->handle = handle; | ||
722 | wblock->gblock = *gblock; | ||
723 | |||
724 | wblock->dev.class = &wmi_class; | 742 | wblock->dev.class = &wmi_class; |
725 | 743 | ||
726 | wmi_gtoa(gblock->guid, guid_string); | 744 | wmi_gtoa(gblock->guid, guid_string); |
@@ -728,17 +746,7 @@ static struct wmi_block *wmi_create_device(const struct guid_block *gblock, | |||
728 | 746 | ||
729 | dev_set_drvdata(&wblock->dev, wblock); | 747 | dev_set_drvdata(&wblock->dev, wblock); |
730 | 748 | ||
731 | error = device_register(&wblock->dev); | 749 | return device_register(&wblock->dev); |
732 | if (error) | ||
733 | goto err_free; | ||
734 | |||
735 | list_add_tail(&wblock->list, &wmi_block_list); | ||
736 | return wblock; | ||
737 | |||
738 | err_free: | ||
739 | kfree(wblock); | ||
740 | err_out: | ||
741 | return ERR_PTR(error); | ||
742 | } | 750 | } |
743 | 751 | ||
744 | static void wmi_free_devices(void) | 752 | static void wmi_free_devices(void) |
@@ -747,7 +755,8 @@ static void wmi_free_devices(void) | |||
747 | 755 | ||
748 | /* Delete devices for all the GUIDs */ | 756 | /* Delete devices for all the GUIDs */ |
749 | list_for_each_entry_safe(wblock, next, &wmi_block_list, list) | 757 | list_for_each_entry_safe(wblock, next, &wmi_block_list, list) |
750 | device_unregister(&wblock->dev); | 758 | if (wblock->dev.class) |
759 | device_unregister(&wblock->dev); | ||
751 | } | 760 | } |
752 | 761 | ||
753 | static bool guid_already_parsed(const char *guid_string) | 762 | static bool guid_already_parsed(const char *guid_string) |
@@ -770,7 +779,6 @@ static acpi_status parse_wdg(acpi_handle handle) | |||
770 | union acpi_object *obj; | 779 | union acpi_object *obj; |
771 | const struct guid_block *gblock; | 780 | const struct guid_block *gblock; |
772 | struct wmi_block *wblock; | 781 | struct wmi_block *wblock; |
773 | char guid_string[37]; | ||
774 | acpi_status status; | 782 | acpi_status status; |
775 | int retval; | 783 | int retval; |
776 | u32 i, total; | 784 | u32 i, total; |
@@ -792,28 +800,31 @@ static acpi_status parse_wdg(acpi_handle handle) | |||
792 | total = obj->buffer.length / sizeof(struct guid_block); | 800 | total = obj->buffer.length / sizeof(struct guid_block); |
793 | 801 | ||
794 | for (i = 0; i < total; i++) { | 802 | for (i = 0; i < total; i++) { |
803 | if (debug_dump_wdg) | ||
804 | wmi_dump_wdg(&gblock[i]); | ||
805 | |||
806 | wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL); | ||
807 | if (!wblock) | ||
808 | return AE_NO_MEMORY; | ||
809 | |||
810 | wblock->handle = handle; | ||
811 | wblock->gblock = gblock[i]; | ||
812 | |||
795 | /* | 813 | /* |
796 | Some WMI devices, like those for nVidia hooks, have a | 814 | Some WMI devices, like those for nVidia hooks, have a |
797 | duplicate GUID. It's not clear what we should do in this | 815 | duplicate GUID. It's not clear what we should do in this |
798 | case yet, so for now, we'll just ignore the duplicate. | 816 | case yet, so for now, we'll just ignore the duplicate |
799 | Anyone who wants to add support for that device can come | 817 | for device creation. |
800 | up with a better workaround for the mess then. | ||
801 | */ | 818 | */ |
802 | if (guid_already_parsed(gblock[i].guid) == true) { | 819 | if (!guid_already_parsed(gblock[i].guid)) { |
803 | wmi_gtoa(gblock[i].guid, guid_string); | 820 | retval = wmi_create_device(&gblock[i], wblock, handle); |
804 | pr_info("Skipping duplicate GUID %s\n", guid_string); | 821 | if (retval) { |
805 | continue; | 822 | wmi_free_devices(); |
823 | break; | ||
824 | } | ||
806 | } | 825 | } |
807 | 826 | ||
808 | if (debug_dump_wdg) | 827 | list_add_tail(&wblock->list, &wmi_block_list); |
809 | wmi_dump_wdg(&gblock[i]); | ||
810 | |||
811 | wblock = wmi_create_device(&gblock[i], handle); | ||
812 | if (IS_ERR(wblock)) { | ||
813 | retval = PTR_ERR(wblock); | ||
814 | wmi_free_devices(); | ||
815 | break; | ||
816 | } | ||
817 | 828 | ||
818 | if (debug_event) { | 829 | if (debug_event) { |
819 | wblock->handler = wmi_notify_debug; | 830 | wblock->handler = wmi_notify_debug; |