diff mbox

[08/14] wmi: Probe data objects for read and write capabilities

Message ID 6027f0563e82e3ca5265c6b29777636a9b8dc244.1448931782.git.luto@kernel.org (mailing list archive)
State RFC, archived
Headers show

Commit Message

Andy Lutomirski Dec. 1, 2015, 1:05 a.m. UTC
My laptop has one RW data object, one RO data object, and one
totally inaccessible data object.  Check for the existence of the
accessor methods and report in sysfs.

The docs also permit WQxx getters for single-instance objects to
take no parameters.  Probe for that as well to avoid ACPICA warnings
about mismatched signatures.

Signed-off-by: Andy Lutomirski <luto@kernel.org>
---
 drivers/platform/x86/wmi.c | 94 ++++++++++++++++++++++++++++++++++++++++++++--
 include/linux/wmi.h        |  6 +++
 2 files changed, 96 insertions(+), 4 deletions(-)

Comments

Michał Kępień Jan. 16, 2016, 1:11 p.m. UTC | #1
> My laptop has one RW data object, one RO data object, and one
> totally inaccessible data object.  Check for the existence of the
> accessor methods and report in sysfs.
> 
> The docs also permit WQxx getters for single-instance objects to
> take no parameters.  Probe for that as well to avoid ACPICA warnings
> about mismatched signatures.
> 
> Signed-off-by: Andy Lutomirski <luto@kernel.org>
> ---
>  drivers/platform/x86/wmi.c | 94 ++++++++++++++++++++++++++++++++++++++++++++--
>  include/linux/wmi.h        |  6 +++
>  2 files changed, 96 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c
> index 5571ae7354a1..49be61207c4a 100644
> --- a/drivers/platform/x86/wmi.c
> +++ b/drivers/platform/x86/wmi.c
> @@ -66,6 +66,8 @@ struct wmi_block {
>  	struct acpi_device *acpi_device;
>  	wmi_notify_handler handler;
>  	void *handler_data;
> +
> +	bool read_takes_no_args;	/* only defined if readable */
>  };
>  
>  
> @@ -216,6 +218,25 @@ static bool find_guid(const char *guid_string, struct wmi_block **out)
>  	return false;
>  }
>  
> +static int get_subobj_info(acpi_handle handle, const char *pathname,
> +			   struct acpi_device_info *info)
> +{
> +	acpi_handle subobj_handle;
> +	acpi_status status;
> +
> +	status = acpi_get_handle(handle, (char *)pathname, &subobj_handle);
> +	if (status == AE_NOT_FOUND)
> +		return -ENOENT;
> +	else if (ACPI_FAILURE(status))
> +		return -EIO;
> +
> +	status = acpi_get_object_info(subobj_handle, &info);

acpi_get_object_info() allocates the returned buffer itself.  The latter
should then subsequently be freed by the caller.  One solution is to
change get_subobj_info() so that it takes a struct acpi_device_info **
as an argument, which should then be passed to acpi_get_object_info().
See also below.

> +	if (ACPI_FAILURE(status))
> +		return -EIO;
> +
> +	return 0;
> +}
> +
>  static acpi_status wmi_method_enable(struct wmi_block *wblock, int enable)
>  {
>  	struct guid_block *block = NULL;
> @@ -339,6 +360,9 @@ struct acpi_buffer *out)
>  	wq_params[0].type = ACPI_TYPE_INTEGER;
>  	wq_params[0].integer.value = instance;
>  
> +	if (instance == 0 && wblock->read_takes_no_args)
> +		input.count = 0;
> +
>  	/*
>  	 * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method first to
>  	 * enable collection.
> @@ -706,11 +730,37 @@ static ssize_t object_id_show(struct device *dev, struct device_attribute *attr,
>  }
>  static DEVICE_ATTR_RO(object_id);
>  
> -static struct attribute *wmi_data_or_method_attrs[] = {
> +static ssize_t readable_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->readable);
> +}
> +static DEVICE_ATTR_RO(readable);
> +
> +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[] = {
>  	&dev_attr_object_id.attr,
> +	&dev_attr_readable.attr,
> +	&dev_attr_writeable.attr,
>  	NULL,
>  };
> -ATTRIBUTE_GROUPS(wmi_data_or_method);
> +ATTRIBUTE_GROUPS(wmi_data);
> +
> +static struct attribute *wmi_method_attrs[] = {
> +	&dev_attr_object_id.attr,
> +	NULL,
> +};
> +ATTRIBUTE_GROUPS(wmi_method);
>  
>  static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env)
>  {
> @@ -809,13 +859,13 @@ static struct device_type wmi_type_event = {
>  
>  static struct device_type wmi_type_method = {
>  	.name = "method",
> -	.groups = wmi_data_or_method_groups,
> +	.groups = wmi_method_groups,
>  	.release = wmi_dev_release,
>  };
>  
>  static struct device_type wmi_type_data = {
>  	.name = "data",
> -	.groups = wmi_data_or_method_groups,
> +	.groups = wmi_data_groups,
>  	.release = wmi_dev_release,
>  };
>  
> @@ -834,7 +884,43 @@ static int wmi_create_device(struct device *wmi_bus_dev,
>  	} else if (gblock->flags & ACPI_WMI_METHOD) {
>  		wblock->dev.dev.type = &wmi_type_method;
>  	} else {
> +		struct acpi_device_info info;

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, &info);
> +
> +		if (result == 0) {
> +			wblock->dev.writeable = true;

kfree(info)
Andy Lutomirski Jan. 16, 2016, 4:14 p.m. UTC | #2
On Sat, Jan 16, 2016 at 5:11 AM, Micha? K?pie? <kernel@kempniu.pl> wrote:
>> My laptop has one RW data object, one RO data object, and one
>> totally inaccessible data object.  Check for the existence of the
>> accessor methods and report in sysfs.
>>
>> The docs also permit WQxx getters for single-instance objects to
>> take no parameters.  Probe for that as well to avoid ACPICA warnings
>> about mismatched signatures.
>>
>> Signed-off-by: Andy Lutomirski <luto@kernel.org>
>> ---
>>  drivers/platform/x86/wmi.c | 94 ++++++++++++++++++++++++++++++++++++++++++++--
>>  include/linux/wmi.h        |  6 +++
>>  2 files changed, 96 insertions(+), 4 deletions(-)
>>
>> diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c
>> index 5571ae7354a1..49be61207c4a 100644
>> --- a/drivers/platform/x86/wmi.c
>> +++ b/drivers/platform/x86/wmi.c
>> @@ -66,6 +66,8 @@ struct wmi_block {
>>       struct acpi_device *acpi_device;
>>       wmi_notify_handler handler;
>>       void *handler_data;
>> +
>> +     bool read_takes_no_args;        /* only defined if readable */
>>  };
>>
>>
>> @@ -216,6 +218,25 @@ static bool find_guid(const char *guid_string, struct wmi_block **out)
>>       return false;
>>  }
>>
>> +static int get_subobj_info(acpi_handle handle, const char *pathname,
>> +                        struct acpi_device_info *info)
>> +{
>> +     acpi_handle subobj_handle;
>> +     acpi_status status;
>> +
>> +     status = acpi_get_handle(handle, (char *)pathname, &subobj_handle);
>> +     if (status == AE_NOT_FOUND)
>> +             return -ENOENT;
>> +     else if (ACPI_FAILURE(status))
>> +             return -EIO;
>> +
>> +     status = acpi_get_object_info(subobj_handle, &info);
>
> acpi_get_object_info() allocates the returned buffer itself.  The latter
> should then subsequently be freed by the caller.  One solution is to
> change get_subobj_info() so that it takes a struct acpi_device_info **
> as an argument, which should then be passed to acpi_get_object_info().
> See also below.

I have an updated version of this patch in my queue.  I'll try to send
it early next week.

--Andy
--
To unsubscribe from this list: send the line "unsubscribe linux-acpi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Michał Kępień Jan. 10, 2017, 5:56 a.m. UTC | #3
> On Sat, Jan 16, 2016 at 5:11 AM, Michał Kępień <kernel@kempniu.pl> wrote:
> >> My laptop has one RW data object, one RO data object, and one
> >> totally inaccessible data object.  Check for the existence of the
> >> accessor methods and report in sysfs.
> >>
> >> The docs also permit WQxx getters for single-instance objects to
> >> take no parameters.  Probe for that as well to avoid ACPICA warnings
> >> about mismatched signatures.
> >>
> >> Signed-off-by: Andy Lutomirski <luto@kernel.org>
> >> ---
> >>  drivers/platform/x86/wmi.c | 94 ++++++++++++++++++++++++++++++++++++++++++++--
> >>  include/linux/wmi.h        |  6 +++
> >>  2 files changed, 96 insertions(+), 4 deletions(-)
> >>
> >> diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c
> >> index 5571ae7354a1..49be61207c4a 100644
> >> --- a/drivers/platform/x86/wmi.c
> >> +++ b/drivers/platform/x86/wmi.c
> >> @@ -66,6 +66,8 @@ struct wmi_block {
> >>       struct acpi_device *acpi_device;
> >>       wmi_notify_handler handler;
> >>       void *handler_data;
> >> +
> >> +     bool read_takes_no_args;        /* only defined if readable */
> >>  };
> >>
> >>
> >> @@ -216,6 +218,25 @@ static bool find_guid(const char *guid_string, struct wmi_block **out)
> >>       return false;
> >>  }
> >>
> >> +static int get_subobj_info(acpi_handle handle, const char *pathname,
> >> +                        struct acpi_device_info *info)
> >> +{
> >> +     acpi_handle subobj_handle;
> >> +     acpi_status status;
> >> +
> >> +     status = acpi_get_handle(handle, (char *)pathname, &subobj_handle);
> >> +     if (status == AE_NOT_FOUND)
> >> +             return -ENOENT;
> >> +     else if (ACPI_FAILURE(status))
> >> +             return -EIO;
> >> +
> >> +     status = acpi_get_object_info(subobj_handle, &info);
> >
> > acpi_get_object_info() allocates the returned buffer itself.  The latter
> > should then subsequently be freed by the caller.  One solution is to
> > change get_subobj_info() so that it takes a struct acpi_device_info **
> > as an argument, which should then be passed to acpi_get_object_info().
> > See also below.
> 
> I have an updated version of this patch in my queue.  I'll try to send
> it early next week.

Hi Andy,

Are you planning any further work on this patch series (originally
posted in December 2015)?  It seems to be a valuable cleanup.
Andy Lutomirski Jan. 10, 2017, 10:05 a.m. UTC | #4
On Mon, Jan 9, 2017 at 9:56 PM, Michał Kępień <kernel@kempniu.pl> wrote:
>> On Sat, Jan 16, 2016 at 5:11 AM, Michał Kępień <kernel@kempniu.pl> wrote:
>> >> My laptop has one RW data object, one RO data object, and one
>> >> totally inaccessible data object.  Check for the existence of the
>> >> accessor methods and report in sysfs.
>> >>
>> >> The docs also permit WQxx getters for single-instance objects to
>> >> take no parameters.  Probe for that as well to avoid ACPICA warnings
>> >> about mismatched signatures.
>> >>
>> >> Signed-off-by: Andy Lutomirski <luto@kernel.org>
>> >> ---
>> >>  drivers/platform/x86/wmi.c | 94 ++++++++++++++++++++++++++++++++++++++++++++--
>> >>  include/linux/wmi.h        |  6 +++
>> >>  2 files changed, 96 insertions(+), 4 deletions(-)
>> >>
>> >> diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c
>> >> index 5571ae7354a1..49be61207c4a 100644
>> >> --- a/drivers/platform/x86/wmi.c
>> >> +++ b/drivers/platform/x86/wmi.c
>> >> @@ -66,6 +66,8 @@ struct wmi_block {
>> >>       struct acpi_device *acpi_device;
>> >>       wmi_notify_handler handler;
>> >>       void *handler_data;
>> >> +
>> >> +     bool read_takes_no_args;        /* only defined if readable */
>> >>  };
>> >>
>> >>
>> >> @@ -216,6 +218,25 @@ static bool find_guid(const char *guid_string, struct wmi_block **out)
>> >>       return false;
>> >>  }
>> >>
>> >> +static int get_subobj_info(acpi_handle handle, const char *pathname,
>> >> +                        struct acpi_device_info *info)
>> >> +{
>> >> +     acpi_handle subobj_handle;
>> >> +     acpi_status status;
>> >> +
>> >> +     status = acpi_get_handle(handle, (char *)pathname, &subobj_handle);
>> >> +     if (status == AE_NOT_FOUND)
>> >> +             return -ENOENT;
>> >> +     else if (ACPI_FAILURE(status))
>> >> +             return -EIO;
>> >> +
>> >> +     status = acpi_get_object_info(subobj_handle, &info);
>> >
>> > acpi_get_object_info() allocates the returned buffer itself.  The latter
>> > should then subsequently be freed by the caller.  One solution is to
>> > change get_subobj_info() so that it takes a struct acpi_device_info **
>> > as an argument, which should then be passed to acpi_get_object_info().
>> > See also below.
>>
>> I have an updated version of this patch in my queue.  I'll try to send
>> it early next week.
>
> Hi Andy,
>
> Are you planning any further work on this patch series (originally
> posted in December 2015)?  It seems to be a valuable cleanup.

I have a tree in which the patches are sitting and I've kept them
rebased, but I haven't had a chance to go through all the review
comments and send them out.  Want to do it?

--Andy
--
To unsubscribe from this list: send the line "unsubscribe linux-acpi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Michał Kępień Jan. 12, 2017, 5:01 a.m. UTC | #5
> On Mon, Jan 9, 2017 at 9:56 PM, Michał Kępień <kernel@kempniu.pl> wrote:
> >> On Sat, Jan 16, 2016 at 5:11 AM, Michał Kępień <kernel@kempniu.pl> wrote:
> >> >> My laptop has one RW data object, one RO data object, and one
> >> >> totally inaccessible data object.  Check for the existence of the
> >> >> accessor methods and report in sysfs.
> >> >>
> >> >> The docs also permit WQxx getters for single-instance objects to
> >> >> take no parameters.  Probe for that as well to avoid ACPICA warnings
> >> >> about mismatched signatures.
> >> >>
> >> >> Signed-off-by: Andy Lutomirski <luto@kernel.org>
> >> >> ---
> >> >>  drivers/platform/x86/wmi.c | 94 ++++++++++++++++++++++++++++++++++++++++++++--
> >> >>  include/linux/wmi.h        |  6 +++
> >> >>  2 files changed, 96 insertions(+), 4 deletions(-)
> >> >>
> >> >> diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c
> >> >> index 5571ae7354a1..49be61207c4a 100644
> >> >> --- a/drivers/platform/x86/wmi.c
> >> >> +++ b/drivers/platform/x86/wmi.c
> >> >> @@ -66,6 +66,8 @@ struct wmi_block {
> >> >>       struct acpi_device *acpi_device;
> >> >>       wmi_notify_handler handler;
> >> >>       void *handler_data;
> >> >> +
> >> >> +     bool read_takes_no_args;        /* only defined if readable */
> >> >>  };
> >> >>
> >> >>
> >> >> @@ -216,6 +218,25 @@ static bool find_guid(const char *guid_string, struct wmi_block **out)
> >> >>       return false;
> >> >>  }
> >> >>
> >> >> +static int get_subobj_info(acpi_handle handle, const char *pathname,
> >> >> +                        struct acpi_device_info *info)
> >> >> +{
> >> >> +     acpi_handle subobj_handle;
> >> >> +     acpi_status status;
> >> >> +
> >> >> +     status = acpi_get_handle(handle, (char *)pathname, &subobj_handle);
> >> >> +     if (status == AE_NOT_FOUND)
> >> >> +             return -ENOENT;
> >> >> +     else if (ACPI_FAILURE(status))
> >> >> +             return -EIO;
> >> >> +
> >> >> +     status = acpi_get_object_info(subobj_handle, &info);
> >> >
> >> > acpi_get_object_info() allocates the returned buffer itself.  The latter
> >> > should then subsequently be freed by the caller.  One solution is to
> >> > change get_subobj_info() so that it takes a struct acpi_device_info **
> >> > as an argument, which should then be passed to acpi_get_object_info().
> >> > See also below.
> >>
> >> I have an updated version of this patch in my queue.  I'll try to send
> >> it early next week.
> >
> > Hi Andy,
> >
> > Are you planning any further work on this patch series (originally
> > posted in December 2015)?  It seems to be a valuable cleanup.
> 
> I have a tree in which the patches are sitting and I've kept them
> rebased, but I haven't had a chance to go through all the review
> comments and send them out.  Want to do it?

If you want me to take the rebased patches, fix the issues I can find
and resubmit as v2 with your name in From: then sure, I can give it a
shot once I find the time (might not be soon, though, but I guess there
is no rush).  If you had something else on your mind, please elaborate.
Andy Lutomirski Jan. 13, 2017, 1:42 a.m. UTC | #6
On Wed, Jan 11, 2017 at 9:01 PM, Michał Kępień <kernel@kempniu.pl> wrote:
>> On Mon, Jan 9, 2017 at 9:56 PM, Michał Kępień <kernel@kempniu.pl> wrote:
>> >> On Sat, Jan 16, 2016 at 5:11 AM, Michał Kępień <kernel@kempniu.pl> wrote:
>> >> >> My laptop has one RW data object, one RO data object, and one
>> >> >> totally inaccessible data object.  Check for the existence of the
>> >> >> accessor methods and report in sysfs.
>> >> >>
>> >> >> The docs also permit WQxx getters for single-instance objects to
>> >> >> take no parameters.  Probe for that as well to avoid ACPICA warnings
>> >> >> about mismatched signatures.
>> >> >>
>> >> >> Signed-off-by: Andy Lutomirski <luto@kernel.org>
>> >> >> ---
>> >> >>  drivers/platform/x86/wmi.c | 94 ++++++++++++++++++++++++++++++++++++++++++++--
>> >> >>  include/linux/wmi.h        |  6 +++
>> >> >>  2 files changed, 96 insertions(+), 4 deletions(-)
>> >> >>
>> >> >> diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c
>> >> >> index 5571ae7354a1..49be61207c4a 100644
>> >> >> --- a/drivers/platform/x86/wmi.c
>> >> >> +++ b/drivers/platform/x86/wmi.c
>> >> >> @@ -66,6 +66,8 @@ struct wmi_block {
>> >> >>       struct acpi_device *acpi_device;
>> >> >>       wmi_notify_handler handler;
>> >> >>       void *handler_data;
>> >> >> +
>> >> >> +     bool read_takes_no_args;        /* only defined if readable */
>> >> >>  };
>> >> >>
>> >> >>
>> >> >> @@ -216,6 +218,25 @@ static bool find_guid(const char *guid_string, struct wmi_block **out)
>> >> >>       return false;
>> >> >>  }
>> >> >>
>> >> >> +static int get_subobj_info(acpi_handle handle, const char *pathname,
>> >> >> +                        struct acpi_device_info *info)
>> >> >> +{
>> >> >> +     acpi_handle subobj_handle;
>> >> >> +     acpi_status status;
>> >> >> +
>> >> >> +     status = acpi_get_handle(handle, (char *)pathname, &subobj_handle);
>> >> >> +     if (status == AE_NOT_FOUND)
>> >> >> +             return -ENOENT;
>> >> >> +     else if (ACPI_FAILURE(status))
>> >> >> +             return -EIO;
>> >> >> +
>> >> >> +     status = acpi_get_object_info(subobj_handle, &info);
>> >> >
>> >> > acpi_get_object_info() allocates the returned buffer itself.  The latter
>> >> > should then subsequently be freed by the caller.  One solution is to
>> >> > change get_subobj_info() so that it takes a struct acpi_device_info **
>> >> > as an argument, which should then be passed to acpi_get_object_info().
>> >> > See also below.
>> >>
>> >> I have an updated version of this patch in my queue.  I'll try to send
>> >> it early next week.
>> >
>> > Hi Andy,
>> >
>> > Are you planning any further work on this patch series (originally
>> > posted in December 2015)?  It seems to be a valuable cleanup.
>>
>> I have a tree in which the patches are sitting and I've kept them
>> rebased, but I haven't had a chance to go through all the review
>> comments and send them out.  Want to do it?
>
> If you want me to take the rebased patches, fix the issues I can find
> and resubmit as v2 with your name in From: then sure, I can give it a
> shot once I find the time (might not be soon, though, but I guess there
> is no rush).  If you had something else on your mind, please elaborate.
>

That would be fantastic.  Let me know if/when you get started.  In the
mean time, the rebased and slightly updated series is here:

https://git.kernel.org/cgit/linux/kernel/git/luto/linux.git/log/?h=platform/wmi

--Andy
--
To unsubscribe from this list: send the line "unsubscribe linux-acpi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c
index 5571ae7354a1..49be61207c4a 100644
--- a/drivers/platform/x86/wmi.c
+++ b/drivers/platform/x86/wmi.c
@@ -66,6 +66,8 @@  struct wmi_block {
 	struct acpi_device *acpi_device;
 	wmi_notify_handler handler;
 	void *handler_data;
+
+	bool read_takes_no_args;	/* only defined if readable */
 };
 
 
@@ -216,6 +218,25 @@  static bool find_guid(const char *guid_string, struct wmi_block **out)
 	return false;
 }
 
+static int get_subobj_info(acpi_handle handle, const char *pathname,
+			   struct acpi_device_info *info)
+{
+	acpi_handle subobj_handle;
+	acpi_status status;
+
+	status = acpi_get_handle(handle, (char *)pathname, &subobj_handle);
+	if (status == AE_NOT_FOUND)
+		return -ENOENT;
+	else if (ACPI_FAILURE(status))
+		return -EIO;
+
+	status = acpi_get_object_info(subobj_handle, &info);
+	if (ACPI_FAILURE(status))
+		return -EIO;
+
+	return 0;
+}
+
 static acpi_status wmi_method_enable(struct wmi_block *wblock, int enable)
 {
 	struct guid_block *block = NULL;
@@ -339,6 +360,9 @@  struct acpi_buffer *out)
 	wq_params[0].type = ACPI_TYPE_INTEGER;
 	wq_params[0].integer.value = instance;
 
+	if (instance == 0 && wblock->read_takes_no_args)
+		input.count = 0;
+
 	/*
 	 * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method first to
 	 * enable collection.
@@ -706,11 +730,37 @@  static ssize_t object_id_show(struct device *dev, struct device_attribute *attr,
 }
 static DEVICE_ATTR_RO(object_id);
 
-static struct attribute *wmi_data_or_method_attrs[] = {
+static ssize_t readable_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->readable);
+}
+static DEVICE_ATTR_RO(readable);
+
+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[] = {
 	&dev_attr_object_id.attr,
+	&dev_attr_readable.attr,
+	&dev_attr_writeable.attr,
 	NULL,
 };
-ATTRIBUTE_GROUPS(wmi_data_or_method);
+ATTRIBUTE_GROUPS(wmi_data);
+
+static struct attribute *wmi_method_attrs[] = {
+	&dev_attr_object_id.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(wmi_method);
 
 static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env)
 {
@@ -809,13 +859,13 @@  static struct device_type wmi_type_event = {
 
 static struct device_type wmi_type_method = {
 	.name = "method",
-	.groups = wmi_data_or_method_groups,
+	.groups = wmi_method_groups,
 	.release = wmi_dev_release,
 };
 
 static struct device_type wmi_type_data = {
 	.name = "data",
-	.groups = wmi_data_or_method_groups,
+	.groups = wmi_data_groups,
 	.release = wmi_dev_release,
 };
 
@@ -834,7 +884,43 @@  static int wmi_create_device(struct device *wmi_bus_dev,
 	} 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;
+		}
+
+		strcpy(method, "WS");
+		strncat(method, wblock->gblock.object_id, 2);
+		result = get_subobj_info(device->handle, method, &info);
+
+		if (result == 0) {
+			wblock->dev.writeable = true;
+		}
+
 	}
 
 	return device_register(&wblock->dev.dev);
diff --git a/include/linux/wmi.h b/include/linux/wmi.h
index 29ed34b4dae1..53095006821e 100644
--- a/include/linux/wmi.h
+++ b/include/linux/wmi.h
@@ -21,6 +21,12 @@ 
 
 struct wmi_device {
 	struct device dev;
+
+	/*
+	 * These are true for data objects that support reads and writes,
+	 * respectively.
+	 */
+	bool readable, writeable;
 };
 
 struct wmi_device_id {