diff options
Diffstat (limited to 'drivers/platform/x86/wmi.c')
-rw-r--r-- | drivers/platform/x86/wmi.c | 197 |
1 files changed, 154 insertions, 43 deletions
diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index faaa9a7c9c2e..f06b7c00339d 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c | |||
@@ -37,6 +37,7 @@ | |||
37 | #include <linux/acpi.h> | 37 | #include <linux/acpi.h> |
38 | #include <linux/slab.h> | 38 | #include <linux/slab.h> |
39 | #include <linux/module.h> | 39 | #include <linux/module.h> |
40 | #include <linux/wmi.h> | ||
40 | #include <linux/uuid.h> | 41 | #include <linux/uuid.h> |
41 | 42 | ||
42 | ACPI_MODULE_NAME("wmi"); | 43 | ACPI_MODULE_NAME("wmi"); |
@@ -44,8 +45,6 @@ MODULE_AUTHOR("Carlos Corbacho"); | |||
44 | MODULE_DESCRIPTION("ACPI-WMI Mapping Driver"); | 45 | MODULE_DESCRIPTION("ACPI-WMI Mapping Driver"); |
45 | MODULE_LICENSE("GPL"); | 46 | MODULE_LICENSE("GPL"); |
46 | 47 | ||
47 | #define ACPI_WMI_CLASS "wmi" | ||
48 | |||
49 | static LIST_HEAD(wmi_block_list); | 48 | static LIST_HEAD(wmi_block_list); |
50 | 49 | ||
51 | struct guid_block { | 50 | struct guid_block { |
@@ -62,12 +61,12 @@ struct guid_block { | |||
62 | }; | 61 | }; |
63 | 62 | ||
64 | struct wmi_block { | 63 | struct wmi_block { |
64 | struct wmi_device dev; | ||
65 | struct list_head list; | 65 | struct list_head list; |
66 | struct guid_block gblock; | 66 | struct guid_block gblock; |
67 | struct acpi_device *acpi_device; | 67 | struct acpi_device *acpi_device; |
68 | wmi_notify_handler handler; | 68 | wmi_notify_handler handler; |
69 | void *handler_data; | 69 | void *handler_data; |
70 | struct device dev; | ||
71 | }; | 70 | }; |
72 | 71 | ||
73 | 72 | ||
@@ -102,8 +101,8 @@ static const struct acpi_device_id wmi_device_ids[] = { | |||
102 | MODULE_DEVICE_TABLE(acpi, wmi_device_ids); | 101 | MODULE_DEVICE_TABLE(acpi, wmi_device_ids); |
103 | 102 | ||
104 | static struct acpi_driver acpi_wmi_driver = { | 103 | static struct acpi_driver acpi_wmi_driver = { |
105 | .name = "wmi", | 104 | .name = "acpi-wmi", |
106 | .class = ACPI_WMI_CLASS, | 105 | .owner = THIS_MODULE, |
107 | .ids = wmi_device_ids, | 106 | .ids = wmi_device_ids, |
108 | .ops = { | 107 | .ops = { |
109 | .add = acpi_wmi_add, | 108 | .add = acpi_wmi_add, |
@@ -545,77 +544,146 @@ bool wmi_has_guid(const char *guid_string) | |||
545 | } | 544 | } |
546 | EXPORT_SYMBOL_GPL(wmi_has_guid); | 545 | EXPORT_SYMBOL_GPL(wmi_has_guid); |
547 | 546 | ||
547 | static struct wmi_block *dev_to_wblock(struct device *dev) | ||
548 | { | ||
549 | return container_of(dev, struct wmi_block, dev.dev); | ||
550 | } | ||
551 | |||
552 | static struct wmi_device *dev_to_wdev(struct device *dev) | ||
553 | { | ||
554 | return container_of(dev, struct wmi_device, dev); | ||
555 | } | ||
556 | |||
548 | /* | 557 | /* |
549 | * sysfs interface | 558 | * sysfs interface |
550 | */ | 559 | */ |
551 | static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, | 560 | static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, |
552 | char *buf) | 561 | char *buf) |
553 | { | 562 | { |
554 | struct wmi_block *wblock; | 563 | struct wmi_block *wblock = dev_to_wblock(dev); |
555 | |||
556 | wblock = dev_get_drvdata(dev); | ||
557 | if (!wblock) { | ||
558 | strcat(buf, "\n"); | ||
559 | return strlen(buf); | ||
560 | } | ||
561 | 564 | ||
562 | return sprintf(buf, "wmi:%pUL\n", wblock->gblock.guid); | 565 | return sprintf(buf, "wmi:%pUL\n", wblock->gblock.guid); |
563 | } | 566 | } |
564 | static DEVICE_ATTR_RO(modalias); | 567 | static DEVICE_ATTR_RO(modalias); |
565 | 568 | ||
569 | static ssize_t guid_show(struct device *dev, struct device_attribute *attr, | ||
570 | char *buf) | ||
571 | { | ||
572 | struct wmi_block *wblock = dev_to_wblock(dev); | ||
573 | |||
574 | return sprintf(buf, "%pUL\n", wblock->gblock.guid); | ||
575 | } | ||
576 | static DEVICE_ATTR_RO(guid); | ||
577 | |||
566 | static struct attribute *wmi_attrs[] = { | 578 | static struct attribute *wmi_attrs[] = { |
567 | &dev_attr_modalias.attr, | 579 | &dev_attr_modalias.attr, |
580 | &dev_attr_guid.attr, | ||
568 | NULL, | 581 | NULL, |
569 | }; | 582 | }; |
570 | ATTRIBUTE_GROUPS(wmi); | 583 | ATTRIBUTE_GROUPS(wmi); |
571 | 584 | ||
572 | static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env) | 585 | static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env) |
573 | { | 586 | { |
574 | char guid_string[37]; | 587 | struct wmi_block *wblock = dev_to_wblock(dev); |
575 | |||
576 | struct wmi_block *wblock; | ||
577 | 588 | ||
578 | if (add_uevent_var(env, "MODALIAS=")) | 589 | if (add_uevent_var(env, "MODALIAS=wmi:%pUL", wblock->gblock.guid)) |
579 | return -ENOMEM; | 590 | return -ENOMEM; |
580 | 591 | ||
581 | wblock = dev_get_drvdata(dev); | 592 | if (add_uevent_var(env, "WMI_GUID=%pUL", wblock->gblock.guid)) |
582 | if (!wblock) | ||
583 | return -ENOMEM; | 593 | return -ENOMEM; |
584 | 594 | ||
585 | sprintf(guid_string, "%pUL", wblock->gblock.guid); | 595 | return 0; |
596 | } | ||
597 | |||
598 | static void wmi_dev_release(struct device *dev) | ||
599 | { | ||
600 | struct wmi_block *wblock = dev_to_wblock(dev); | ||
601 | |||
602 | kfree(wblock); | ||
603 | } | ||
604 | |||
605 | static int wmi_dev_match(struct device *dev, struct device_driver *driver) | ||
606 | { | ||
607 | struct wmi_driver *wmi_driver = | ||
608 | container_of(driver, struct wmi_driver, driver); | ||
609 | struct wmi_block *wblock = dev_to_wblock(dev); | ||
610 | const struct wmi_device_id *id = wmi_driver->id_table; | ||
586 | 611 | ||
587 | strcpy(&env->buf[env->buflen - 1], "wmi:"); | 612 | while (id->guid_string) { |
588 | memcpy(&env->buf[env->buflen - 1 + 4], guid_string, 36); | 613 | uuid_le driver_guid; |
589 | env->buflen += 40; | 614 | |
615 | if (WARN_ON(uuid_le_to_bin(id->guid_string, &driver_guid))) | ||
616 | continue; | ||
617 | if (!memcmp(&driver_guid, wblock->gblock.guid, 16)) | ||
618 | return 1; | ||
619 | |||
620 | id++; | ||
621 | } | ||
590 | 622 | ||
591 | return 0; | 623 | return 0; |
592 | } | 624 | } |
593 | 625 | ||
594 | static void wmi_dev_free(struct device *dev) | 626 | static int wmi_dev_probe(struct device *dev) |
595 | { | 627 | { |
596 | struct wmi_block *wmi_block = container_of(dev, struct wmi_block, dev); | 628 | struct wmi_block *wblock = dev_to_wblock(dev); |
629 | struct wmi_driver *wdriver = | ||
630 | container_of(dev->driver, struct wmi_driver, driver); | ||
631 | int ret = 0; | ||
632 | |||
633 | if (ACPI_FAILURE(wmi_method_enable(wblock, 1))) | ||
634 | dev_warn(dev, "failed to enable device -- probing anyway\n"); | ||
635 | |||
636 | if (wdriver->probe) { | ||
637 | ret = wdriver->probe(dev_to_wdev(dev)); | ||
638 | if (ret != 0 && ACPI_FAILURE(wmi_method_enable(wblock, 0))) | ||
639 | dev_warn(dev, "failed to disable device\n"); | ||
640 | } | ||
641 | |||
642 | return ret; | ||
643 | } | ||
644 | |||
645 | static int wmi_dev_remove(struct device *dev) | ||
646 | { | ||
647 | struct wmi_block *wblock = dev_to_wblock(dev); | ||
648 | struct wmi_driver *wdriver = | ||
649 | container_of(dev->driver, struct wmi_driver, driver); | ||
650 | int ret = 0; | ||
651 | |||
652 | if (wdriver->remove) | ||
653 | ret = wdriver->remove(dev_to_wdev(dev)); | ||
654 | |||
655 | if (ACPI_FAILURE(wmi_method_enable(wblock, 0))) | ||
656 | dev_warn(dev, "failed to disable device\n"); | ||
597 | 657 | ||
598 | kfree(wmi_block); | 658 | return ret; |
599 | } | 659 | } |
600 | 660 | ||
601 | static struct class wmi_class = { | 661 | static struct class wmi_bus_class = { |
662 | .name = "wmi_bus", | ||
663 | }; | ||
664 | |||
665 | static struct bus_type wmi_bus_type = { | ||
602 | .name = "wmi", | 666 | .name = "wmi", |
603 | .dev_release = wmi_dev_free, | ||
604 | .dev_uevent = wmi_dev_uevent, | ||
605 | .dev_groups = wmi_groups, | 667 | .dev_groups = wmi_groups, |
668 | .match = wmi_dev_match, | ||
669 | .uevent = wmi_dev_uevent, | ||
670 | .probe = wmi_dev_probe, | ||
671 | .remove = wmi_dev_remove, | ||
606 | }; | 672 | }; |
607 | 673 | ||
608 | static int wmi_create_device(const struct guid_block *gblock, | 674 | static int wmi_create_device(struct device *wmi_bus_dev, |
675 | const struct guid_block *gblock, | ||
609 | struct wmi_block *wblock, | 676 | struct wmi_block *wblock, |
610 | struct acpi_device *device) | 677 | struct acpi_device *device) |
611 | { | 678 | { |
612 | wblock->dev.class = &wmi_class; | 679 | wblock->dev.dev.bus = &wmi_bus_type; |
680 | wblock->dev.dev.parent = wmi_bus_dev; | ||
613 | 681 | ||
614 | dev_set_name(&wblock->dev, "%pUL", gblock->guid); | 682 | dev_set_name(&wblock->dev.dev, "%pUL", gblock->guid); |
615 | 683 | ||
616 | dev_set_drvdata(&wblock->dev, wblock); | 684 | wblock->dev.dev.release = wmi_dev_release; |
617 | 685 | ||
618 | return device_register(&wblock->dev); | 686 | return device_register(&wblock->dev.dev); |
619 | } | 687 | } |
620 | 688 | ||
621 | static void wmi_free_devices(struct acpi_device *device) | 689 | static void wmi_free_devices(struct acpi_device *device) |
@@ -626,8 +694,8 @@ static void wmi_free_devices(struct acpi_device *device) | |||
626 | list_for_each_entry_safe(wblock, next, &wmi_block_list, list) { | 694 | list_for_each_entry_safe(wblock, next, &wmi_block_list, list) { |
627 | if (wblock->acpi_device == device) { | 695 | if (wblock->acpi_device == device) { |
628 | list_del(&wblock->list); | 696 | list_del(&wblock->list); |
629 | if (wblock->dev.class) | 697 | if (wblock->dev.dev.bus) |
630 | device_unregister(&wblock->dev); | 698 | device_unregister(&wblock->dev.dev); |
631 | else | 699 | else |
632 | kfree(wblock); | 700 | kfree(wblock); |
633 | } | 701 | } |
@@ -659,7 +727,7 @@ static bool guid_already_parsed(struct acpi_device *device, | |||
659 | /* | 727 | /* |
660 | * Parse the _WDG method for the GUID data blocks | 728 | * Parse the _WDG method for the GUID data blocks |
661 | */ | 729 | */ |
662 | static int parse_wdg(struct acpi_device *device) | 730 | static int parse_wdg(struct device *wmi_bus_dev, struct acpi_device *device) |
663 | { | 731 | { |
664 | struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL}; | 732 | struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL}; |
665 | union acpi_object *obj; | 733 | union acpi_object *obj; |
@@ -703,7 +771,8 @@ static int parse_wdg(struct acpi_device *device) | |||
703 | for device creation. | 771 | for device creation. |
704 | */ | 772 | */ |
705 | if (!guid_already_parsed(device, gblock[i].guid)) { | 773 | if (!guid_already_parsed(device, gblock[i].guid)) { |
706 | retval = wmi_create_device(&gblock[i], wblock, device); | 774 | retval = wmi_create_device(wmi_bus_dev, &gblock[i], |
775 | wblock, device); | ||
707 | if (retval) { | 776 | if (retval) { |
708 | wmi_free_devices(device); | 777 | wmi_free_devices(device); |
709 | goto out_free_pointer; | 778 | goto out_free_pointer; |
@@ -803,12 +872,15 @@ static int acpi_wmi_remove(struct acpi_device *device) | |||
803 | acpi_remove_address_space_handler(device->handle, | 872 | acpi_remove_address_space_handler(device->handle, |
804 | ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler); | 873 | ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler); |
805 | wmi_free_devices(device); | 874 | wmi_free_devices(device); |
875 | device_unregister((struct device *)acpi_driver_data(device)); | ||
876 | device->driver_data = NULL; | ||
806 | 877 | ||
807 | return 0; | 878 | return 0; |
808 | } | 879 | } |
809 | 880 | ||
810 | static int acpi_wmi_add(struct acpi_device *device) | 881 | static int acpi_wmi_add(struct acpi_device *device) |
811 | { | 882 | { |
883 | struct device *wmi_bus_dev; | ||
812 | acpi_status status; | 884 | acpi_status status; |
813 | int error; | 885 | int error; |
814 | 886 | ||
@@ -821,14 +893,25 @@ static int acpi_wmi_add(struct acpi_device *device) | |||
821 | return -ENODEV; | 893 | return -ENODEV; |
822 | } | 894 | } |
823 | 895 | ||
824 | error = parse_wdg(device); | 896 | wmi_bus_dev = device_create(&wmi_bus_class, &device->dev, MKDEV(0, 0), |
897 | NULL, "wmi_bus-%s", dev_name(&device->dev)); | ||
898 | if (IS_ERR(wmi_bus_dev)) { | ||
899 | error = PTR_ERR(wmi_bus_dev); | ||
900 | goto err_remove_handler; | ||
901 | } | ||
902 | device->driver_data = wmi_bus_dev; | ||
903 | |||
904 | error = parse_wdg(wmi_bus_dev, device); | ||
825 | if (error) { | 905 | if (error) { |
826 | pr_err("Failed to parse WDG method\n"); | 906 | pr_err("Failed to parse WDG method\n"); |
827 | goto err_remove_handler; | 907 | goto err_remove_busdev; |
828 | } | 908 | } |
829 | 909 | ||
830 | return 0; | 910 | return 0; |
831 | 911 | ||
912 | err_remove_busdev: | ||
913 | device_unregister(wmi_bus_dev); | ||
914 | |||
832 | err_remove_handler: | 915 | err_remove_handler: |
833 | acpi_remove_address_space_handler(device->handle, | 916 | acpi_remove_address_space_handler(device->handle, |
834 | ACPI_ADR_SPACE_EC, | 917 | ACPI_ADR_SPACE_EC, |
@@ -837,6 +920,22 @@ err_remove_handler: | |||
837 | return error; | 920 | return error; |
838 | } | 921 | } |
839 | 922 | ||
923 | int __must_check __wmi_driver_register(struct wmi_driver *driver, | ||
924 | struct module *owner) | ||
925 | { | ||
926 | driver->driver.owner = owner; | ||
927 | driver->driver.bus = &wmi_bus_type; | ||
928 | |||
929 | return driver_register(&driver->driver); | ||
930 | } | ||
931 | EXPORT_SYMBOL(__wmi_driver_register); | ||
932 | |||
933 | void wmi_driver_unregister(struct wmi_driver *driver) | ||
934 | { | ||
935 | driver_unregister(&driver->driver); | ||
936 | } | ||
937 | EXPORT_SYMBOL(wmi_driver_unregister); | ||
938 | |||
840 | static int __init acpi_wmi_init(void) | 939 | static int __init acpi_wmi_init(void) |
841 | { | 940 | { |
842 | int error; | 941 | int error; |
@@ -844,24 +943,36 @@ static int __init acpi_wmi_init(void) | |||
844 | if (acpi_disabled) | 943 | if (acpi_disabled) |
845 | return -ENODEV; | 944 | return -ENODEV; |
846 | 945 | ||
847 | error = class_register(&wmi_class); | 946 | error = class_register(&wmi_bus_class); |
848 | if (error) | 947 | if (error) |
849 | return error; | 948 | return error; |
850 | 949 | ||
950 | error = bus_register(&wmi_bus_type); | ||
951 | if (error) | ||
952 | goto err_unreg_class; | ||
953 | |||
851 | error = acpi_bus_register_driver(&acpi_wmi_driver); | 954 | error = acpi_bus_register_driver(&acpi_wmi_driver); |
852 | if (error) { | 955 | if (error) { |
853 | pr_err("Error loading mapper\n"); | 956 | pr_err("Error loading mapper\n"); |
854 | class_unregister(&wmi_class); | 957 | goto err_unreg_bus; |
855 | return error; | ||
856 | } | 958 | } |
857 | 959 | ||
858 | return 0; | 960 | return 0; |
961 | |||
962 | err_unreg_class: | ||
963 | class_unregister(&wmi_bus_class); | ||
964 | |||
965 | err_unreg_bus: | ||
966 | bus_unregister(&wmi_bus_type); | ||
967 | |||
968 | return error; | ||
859 | } | 969 | } |
860 | 970 | ||
861 | static void __exit acpi_wmi_exit(void) | 971 | static void __exit acpi_wmi_exit(void) |
862 | { | 972 | { |
863 | acpi_bus_unregister_driver(&acpi_wmi_driver); | 973 | acpi_bus_unregister_driver(&acpi_wmi_driver); |
864 | class_unregister(&wmi_class); | 974 | class_unregister(&wmi_bus_class); |
975 | bus_unregister(&wmi_bus_type); | ||
865 | } | 976 | } |
866 | 977 | ||
867 | subsys_initcall(acpi_wmi_init); | 978 | subsys_initcall(acpi_wmi_init); |