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>
This commit is contained in:
Darren Hart (VMware) 2017-05-19 19:28:36 -07:00
parent f63019861c
commit fd70da6a62
2 changed files with 71 additions and 69 deletions

View File

@ -69,7 +69,7 @@ struct wmi_block {
wmi_notify_handler handler; wmi_notify_handler handler;
void *handler_data; void *handler_data;
bool read_takes_no_args; /* only defined if readable */ bool read_takes_no_args;
}; };
@ -694,28 +694,18 @@ static ssize_t object_id_show(struct device *dev, struct device_attribute *attr,
} }
static DEVICE_ATTR_RO(object_id); static DEVICE_ATTR_RO(object_id);
static ssize_t readable_show(struct device *dev, struct device_attribute *attr, static ssize_t setable_show(struct device *dev, struct device_attribute *attr,
char *buf) char *buf)
{ {
struct wmi_device *wdev = dev_to_wdev(dev); struct wmi_device *wdev = dev_to_wdev(dev);
return sprintf(buf, "%d\n", (int)wdev->readable); return sprintf(buf, "%d\n", (int)wdev->setable);
} }
static DEVICE_ATTR_RO(readable); static DEVICE_ATTR_RO(setable);
static ssize_t writeable_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct wmi_device *wdev = dev_to_wdev(dev);
return sprintf(buf, "%d\n", (int)wdev->writeable);
}
static DEVICE_ATTR_RO(writeable);
static struct attribute *wmi_data_attrs[] = { static struct attribute *wmi_data_attrs[] = {
&dev_attr_object_id.attr, &dev_attr_object_id.attr,
&dev_attr_readable.attr, &dev_attr_setable.attr,
&dev_attr_writeable.attr,
NULL, NULL,
}; };
ATTRIBUTE_GROUPS(wmi_data); ATTRIBUTE_GROUPS(wmi_data);
@ -833,63 +823,74 @@ static struct device_type wmi_type_data = {
.release = wmi_dev_release, .release = wmi_dev_release,
}; };
static void wmi_create_device(struct device *wmi_bus_dev, static int wmi_create_device(struct device *wmi_bus_dev,
const struct guid_block *gblock, const struct guid_block *gblock,
struct wmi_block *wblock, struct wmi_block *wblock,
struct acpi_device *device) struct acpi_device *device)
{ {
struct acpi_device_info *info;
char method[5];
int result;
if (gblock->flags & ACPI_WMI_EVENT) {
wblock->dev.dev.type = &wmi_type_event;
goto out_init;
}
if (gblock->flags & ACPI_WMI_METHOD) {
wblock->dev.dev.type = &wmi_type_method;
goto out_init;
}
/*
* Data Block Query Control Method (WQxx by convention) is
* required per the WMI documentation. If it is not present,
* we ignore this data block.
*/
strcpy(method, "WQ");
strncat(method, wblock->gblock.object_id, 2);
result = get_subobj_info(device->handle, method, &info);
if (result) {
dev_warn(wmi_bus_dev,
"%s data block query control method not found",
method);
return result;
}
wblock->dev.dev.type = &wmi_type_data;
/*
* The Microsoft documentation specifically states:
*
* Data blocks registered with only a single instance
* can ignore the parameter.
*
* ACPICA will get mad at us if we call the method with the wrong number
* of arguments, so check what our method expects. (On some Dell
* laptops, WQxx may not be a method at all.)
*/
if (info->type != ACPI_TYPE_METHOD || info->param_count == 0)
wblock->read_takes_no_args = true;
kfree(info);
strcpy(method, "WS");
strncat(method, wblock->gblock.object_id, 2);
result = get_subobj_info(device->handle, method, NULL);
if (result == 0)
wblock->dev.setable = true;
out_init:
wblock->dev.dev.bus = &wmi_bus_type; wblock->dev.dev.bus = &wmi_bus_type;
wblock->dev.dev.parent = wmi_bus_dev; wblock->dev.dev.parent = wmi_bus_dev;
dev_set_name(&wblock->dev.dev, "%pUL", gblock->guid); dev_set_name(&wblock->dev.dev, "%pUL", gblock->guid);
if (gblock->flags & ACPI_WMI_EVENT) {
wblock->dev.dev.type = &wmi_type_event;
} else if (gblock->flags & ACPI_WMI_METHOD) {
wblock->dev.dev.type = &wmi_type_method;
} else {
struct acpi_device_info *info;
char method[5];
int result;
wblock->dev.dev.type = &wmi_type_data;
strcpy(method, "WQ");
strncat(method, wblock->gblock.object_id, 2);
result = get_subobj_info(device->handle, method, &info);
if (result == 0) {
wblock->dev.readable = true;
/*
* The Microsoft documentation specifically states:
*
* Data blocks registered with only a single instance
* can ignore the parameter.
*
* ACPICA will get mad at us if we call the method
* with the wrong number of arguments, so check what
* our method expects. (On some Dell laptops, WQxx
* may not be a method at all.)
*/
if (info->type != ACPI_TYPE_METHOD ||
info->param_count == 0)
wblock->read_takes_no_args = true;
kfree(info);
}
strcpy(method, "WS");
strncat(method, wblock->gblock.object_id, 2);
result = get_subobj_info(device->handle, method, NULL);
if (result == 0) {
wblock->dev.writeable = true;
}
}
device_initialize(&wblock->dev.dev); device_initialize(&wblock->dev.dev);
return 0;
} }
static void wmi_free_devices(struct acpi_device *device) 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)
wblock->acpi_device = device; wblock->acpi_device = device;
wblock->gblock = gblock[i]; wblock->gblock = gblock[i];
wmi_create_device(wmi_bus_dev, &gblock[i], wblock, device); retval = wmi_create_device(wmi_bus_dev, &gblock[i], wblock, device);
if (retval) {
kfree(wblock);
continue;
}
list_add_tail(&wblock->list, &wmi_block_list); list_add_tail(&wblock->list, &wmi_block_list);

View File

@ -22,11 +22,8 @@
struct wmi_device { struct wmi_device {
struct device dev; struct device dev;
/* /* True for data blocks implementing the Set Control Method */
* These are true for data objects that support reads and writes, bool setable;
* respectively.
*/
bool readable, writeable;
}; };
/* Caller must kfree the result. */ /* Caller must kfree the result. */