diff options
author | Darren Hart (VMware) <dvhart@infradead.org> | 2017-05-19 22:28:36 -0400 |
---|---|---|
committer | Darren Hart (VMware) <dvhart@infradead.org> | 2017-06-06 13:15:20 -0400 |
commit | fd70da6a6267c91fbdda9c560f098cfd52fba00f (patch) | |
tree | 378c9c14573ce622164b455e5fb48c4a80b5cbea | |
parent | f63019861cd1192e546397b13f926876a93450fd (diff) |
platform/x86: wmi: Require query for data blocks, rename writable to setable
The Microsoft WMI documentation requires all data blocks to implement
the Query Control Method (WQxx). If we encounter a data block not
implementing this control method, issue a warning, and ignore the data
block. Remove the "readable" attribute as all data blocks must be
readable (query-able).
Be consistent with the language in the documentation, replace the
"writable" attribute with "setable".
Simplify (flatten) the control flow of wmi_create_device a bit while
we are updating it for the above changes.
Signed-off-by: Darren Hart (VMware) <dvhart@infradead.org>
Cc: Andy Lutomirski <luto@amacapital.net>
Cc: Mario Limonciello <mario_limonciello@dell.com>
Cc: Pali Rohár <pali.rohar@gmail.com>
Cc: linux-kernel@vger.kernel.org
Cc: platform-driver-x86@vger.kernel.org
Cc: linux-acpi@vger.kernel.org
Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
-rw-r--r-- | drivers/platform/x86/wmi.c | 117 | ||||
-rw-r--r-- | include/linux/wmi.h | 7 |
2 files changed, 63 insertions, 61 deletions
diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 250d7b2398b4..37f6651d1bf7 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c | |||
@@ -69,7 +69,7 @@ struct wmi_block { | |||
69 | wmi_notify_handler handler; | 69 | wmi_notify_handler handler; |
70 | void *handler_data; | 70 | void *handler_data; |
71 | 71 | ||
72 | bool read_takes_no_args; /* only defined if readable */ | 72 | bool read_takes_no_args; |
73 | }; | 73 | }; |
74 | 74 | ||
75 | 75 | ||
@@ -694,28 +694,18 @@ static ssize_t object_id_show(struct device *dev, struct device_attribute *attr, | |||
694 | } | 694 | } |
695 | static DEVICE_ATTR_RO(object_id); | 695 | static DEVICE_ATTR_RO(object_id); |
696 | 696 | ||
697 | static ssize_t readable_show(struct device *dev, struct device_attribute *attr, | 697 | static ssize_t setable_show(struct device *dev, struct device_attribute *attr, |
698 | char *buf) | 698 | char *buf) |
699 | { | 699 | { |
700 | struct wmi_device *wdev = dev_to_wdev(dev); | 700 | struct wmi_device *wdev = dev_to_wdev(dev); |
701 | 701 | ||
702 | return sprintf(buf, "%d\n", (int)wdev->readable); | 702 | return sprintf(buf, "%d\n", (int)wdev->setable); |
703 | } | 703 | } |
704 | static DEVICE_ATTR_RO(readable); | 704 | static DEVICE_ATTR_RO(setable); |
705 | |||
706 | static ssize_t writeable_show(struct device *dev, struct device_attribute *attr, | ||
707 | char *buf) | ||
708 | { | ||
709 | struct wmi_device *wdev = dev_to_wdev(dev); | ||
710 | |||
711 | return sprintf(buf, "%d\n", (int)wdev->writeable); | ||
712 | } | ||
713 | static DEVICE_ATTR_RO(writeable); | ||
714 | 705 | ||
715 | static struct attribute *wmi_data_attrs[] = { | 706 | static struct attribute *wmi_data_attrs[] = { |
716 | &dev_attr_object_id.attr, | 707 | &dev_attr_object_id.attr, |
717 | &dev_attr_readable.attr, | 708 | &dev_attr_setable.attr, |
718 | &dev_attr_writeable.attr, | ||
719 | NULL, | 709 | NULL, |
720 | }; | 710 | }; |
721 | ATTRIBUTE_GROUPS(wmi_data); | 711 | ATTRIBUTE_GROUPS(wmi_data); |
@@ -833,63 +823,74 @@ static struct device_type wmi_type_data = { | |||
833 | .release = wmi_dev_release, | 823 | .release = wmi_dev_release, |
834 | }; | 824 | }; |
835 | 825 | ||
836 | static void wmi_create_device(struct device *wmi_bus_dev, | 826 | static int wmi_create_device(struct device *wmi_bus_dev, |
837 | const struct guid_block *gblock, | 827 | const struct guid_block *gblock, |
838 | struct wmi_block *wblock, | 828 | struct wmi_block *wblock, |
839 | struct acpi_device *device) | 829 | struct acpi_device *device) |
840 | { | 830 | { |
841 | wblock->dev.dev.bus = &wmi_bus_type; | 831 | struct acpi_device_info *info; |
842 | wblock->dev.dev.parent = wmi_bus_dev; | 832 | char method[5]; |
843 | 833 | int result; | |
844 | dev_set_name(&wblock->dev.dev, "%pUL", gblock->guid); | ||
845 | 834 | ||
846 | if (gblock->flags & ACPI_WMI_EVENT) { | 835 | if (gblock->flags & ACPI_WMI_EVENT) { |
847 | wblock->dev.dev.type = &wmi_type_event; | 836 | wblock->dev.dev.type = &wmi_type_event; |
848 | } else if (gblock->flags & ACPI_WMI_METHOD) { | 837 | goto out_init; |
838 | } | ||
839 | |||
840 | if (gblock->flags & ACPI_WMI_METHOD) { | ||
849 | wblock->dev.dev.type = &wmi_type_method; | 841 | wblock->dev.dev.type = &wmi_type_method; |
850 | } else { | 842 | goto out_init; |
851 | struct acpi_device_info *info; | 843 | } |
852 | char method[5]; | ||
853 | int result; | ||
854 | 844 | ||
855 | wblock->dev.dev.type = &wmi_type_data; | 845 | /* |
846 | * Data Block Query Control Method (WQxx by convention) is | ||
847 | * required per the WMI documentation. If it is not present, | ||
848 | * we ignore this data block. | ||
849 | */ | ||
850 | strcpy(method, "WQ"); | ||
851 | strncat(method, wblock->gblock.object_id, 2); | ||
852 | result = get_subobj_info(device->handle, method, &info); | ||
853 | |||
854 | if (result) { | ||
855 | dev_warn(wmi_bus_dev, | ||
856 | "%s data block query control method not found", | ||
857 | method); | ||
858 | return result; | ||
859 | } | ||
856 | 860 | ||
857 | strcpy(method, "WQ"); | 861 | wblock->dev.dev.type = &wmi_type_data; |
858 | strncat(method, wblock->gblock.object_id, 2); | ||
859 | result = get_subobj_info(device->handle, method, &info); | ||
860 | 862 | ||
861 | if (result == 0) { | 863 | /* |
862 | wblock->dev.readable = true; | 864 | * The Microsoft documentation specifically states: |
865 | * | ||
866 | * Data blocks registered with only a single instance | ||
867 | * can ignore the parameter. | ||
868 | * | ||
869 | * ACPICA will get mad at us if we call the method with the wrong number | ||
870 | * of arguments, so check what our method expects. (On some Dell | ||
871 | * laptops, WQxx may not be a method at all.) | ||
872 | */ | ||
873 | if (info->type != ACPI_TYPE_METHOD || info->param_count == 0) | ||
874 | wblock->read_takes_no_args = true; | ||
863 | 875 | ||
864 | /* | 876 | kfree(info); |
865 | * The Microsoft documentation specifically states: | ||
866 | * | ||
867 | * Data blocks registered with only a single instance | ||
868 | * can ignore the parameter. | ||
869 | * | ||
870 | * ACPICA will get mad at us if we call the method | ||
871 | * with the wrong number of arguments, so check what | ||
872 | * our method expects. (On some Dell laptops, WQxx | ||
873 | * may not be a method at all.) | ||
874 | */ | ||
875 | if (info->type != ACPI_TYPE_METHOD || | ||
876 | info->param_count == 0) | ||
877 | wblock->read_takes_no_args = true; | ||
878 | 877 | ||
879 | kfree(info); | 878 | strcpy(method, "WS"); |
880 | } | 879 | strncat(method, wblock->gblock.object_id, 2); |
880 | result = get_subobj_info(device->handle, method, NULL); | ||
881 | 881 | ||
882 | strcpy(method, "WS"); | 882 | if (result == 0) |
883 | strncat(method, wblock->gblock.object_id, 2); | 883 | wblock->dev.setable = true; |
884 | result = get_subobj_info(device->handle, method, NULL); | ||
885 | 884 | ||
886 | if (result == 0) { | 885 | out_init: |
887 | wblock->dev.writeable = true; | 886 | wblock->dev.dev.bus = &wmi_bus_type; |
888 | } | 887 | wblock->dev.dev.parent = wmi_bus_dev; |
889 | 888 | ||
890 | } | 889 | dev_set_name(&wblock->dev.dev, "%pUL", gblock->guid); |
891 | 890 | ||
892 | device_initialize(&wblock->dev.dev); | 891 | device_initialize(&wblock->dev.dev); |
892 | |||
893 | return 0; | ||
893 | } | 894 | } |
894 | 895 | ||
895 | static void wmi_free_devices(struct acpi_device *device) | 896 | static void wmi_free_devices(struct acpi_device *device) |
@@ -978,7 +979,11 @@ static int parse_wdg(struct device *wmi_bus_dev, struct acpi_device *device) | |||
978 | wblock->acpi_device = device; | 979 | wblock->acpi_device = device; |
979 | wblock->gblock = gblock[i]; | 980 | wblock->gblock = gblock[i]; |
980 | 981 | ||
981 | wmi_create_device(wmi_bus_dev, &gblock[i], wblock, device); | 982 | retval = wmi_create_device(wmi_bus_dev, &gblock[i], wblock, device); |
983 | if (retval) { | ||
984 | kfree(wblock); | ||
985 | continue; | ||
986 | } | ||
982 | 987 | ||
983 | list_add_tail(&wblock->list, &wmi_block_list); | 988 | list_add_tail(&wblock->list, &wmi_block_list); |
984 | 989 | ||
diff --git a/include/linux/wmi.h b/include/linux/wmi.h index a283768afb7e..cd0d7734dc49 100644 --- a/include/linux/wmi.h +++ b/include/linux/wmi.h | |||
@@ -22,11 +22,8 @@ | |||
22 | struct wmi_device { | 22 | struct wmi_device { |
23 | struct device dev; | 23 | struct device dev; |
24 | 24 | ||
25 | /* | 25 | /* True for data blocks implementing the Set Control Method */ |
26 | * These are true for data objects that support reads and writes, | 26 | bool setable; |
27 | * respectively. | ||
28 | */ | ||
29 | bool readable, writeable; | ||
30 | }; | 27 | }; |
31 | 28 | ||
32 | /* Caller must kfree the result. */ | 29 | /* Caller must kfree the result. */ |