// SPDX-License-Identifier: GPL-2.0-or-later/* * ACPI-WMI mapping driver * * Copyright (C) 2007-2008 Carlos Corbacho <carlos@strangeworlds.co.uk> * * GUID parsing code from ldm.c is: * Copyright (C) 2001,2002 Richard Russon <ldm@flatcap.org> * Copyright (c) 2001-2007 Anton Altaparmakov * Copyright (C) 2001,2002 Jakob Kemi <jakob.kemi@telia.com> * * WMI bus infrastructure by Andrew Lutomirski and Darren Hart: * Copyright (C) 2015 Andrew Lutomirski * Copyright (C) 2017 VMware, Inc. All Rights Reserved. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/acpi.h>#include <linux/bits.h>#include <linux/build_bug.h>#include <linux/device.h>#include <linux/idr.h>#include <linux/init.h>#include <linux/kernel.h>#include <linux/limits.h>#include <linux/module.h>#include <linux/platform_device.h>#include <linux/rwsem.h>#include <linux/slab.h>#include <linux/sysfs.h>#include <linux/types.h>#include <linux/uuid.h>#include <linux/wmi.h>#include <linux/fs.h> #include "internal.h" MODULE_AUTHOR("Carlos Corbacho");MODULE_DESCRIPTION("ACPI-WMI Mapping Driver");MODULE_LICENSE("GPL"); struct guid_block { guid_t guid; union { char object_id[2]; struct { unsigned char notify_id; unsigned char reserved; }; }; u8 instance_count; u8 flags;} __packed;static_assert(sizeof(typeof_member(struct guid_block, guid)) == 16);static_assert(sizeof(struct guid_block) == 20);static_assert(__alignof__(struct guid_block) == 1); enum { /* wmi_block flags */ WMI_READ_TAKES_NO_ARGS, WMI_GUID_DUPLICATED, WMI_NO_EVENT_DATA,}; struct wmi_block { struct wmi_device dev; struct guid_block gblock; struct acpi_device *acpi_device; struct rw_semaphore notify_lock; /* Protects notify callback add/remove */ wmi_notify_handler handler; void *handler_data; bool driver_ready; unsigned long flags;}; struct wmi_guid_count_context { const guid_t *guid; int count;}; static DEFINE_IDA(wmi_ida); /* * If the GUID data block is marked as expensive, we must enable and * explicitily disable data collection. */#define ACPI_WMI_EXPENSIVE BIT(0)#define ACPI_WMI_METHOD BIT(1) /* GUID is a method */#define ACPI_WMI_STRING BIT(2) /* GUID takes & returns a string */#define ACPI_WMI_EVENT BIT(3) /* GUID is an event */ static const struct acpi_device_id wmi_device_ids[] = { {"PNP0C14", 0}, {"pnp0c14", 0}, { }};MODULE_DEVICE_TABLE(acpi, wmi_device_ids); #define dev_to_wblock(__dev) container_of_const(__dev, struct wmi_block, dev.dev) /* * GUID parsing functions */ static bool guid_parse_and_compare(const char *string, const guid_t *guid){ guid_t guid_input; if (guid_parse(string, &guid_input)) return false; return guid_equal(&guid_input, guid);} static const void *find_guid_context(struct wmi_block *wblock, struct wmi_driver *wdriver){ const struct wmi_device_id *id; id = wdriver->id_table; if (!id) return NULL; while (*id->guid_string) { if (guid_parse_and_compare(id->guid_string, &wblock->gblock.guid)) return id->context; id++; } return NULL;} #define WMI_ACPI_METHOD_NAME_SIZE 5 static inline void get_acpi_method_name(const struct wmi_block *wblock, const char method, char buffer[static WMI_ACPI_METHOD_NAME_SIZE]){ static_assert(ARRAY_SIZE(wblock->gblock.object_id) == 2); static_assert(WMI_ACPI_METHOD_NAME_SIZE >= 5); buffer[0] = 'W'; buffer[1] = method; buffer[2] = wblock->gblock.object_id[0]; buffer[3] = wblock->gblock.object_id[1]; buffer[4] = '\0';} static int wmidev_match_guid(struct device *dev, const void *data){ struct wmi_block *wblock = dev_to_wblock(dev); const guid_t *guid = data; /* Legacy GUID-based functions are restricted to only see * a single WMI device for each GUID. */ if (test_bit(WMI_GUID_DUPLICATED, &wblock->flags)) return 0; if (guid_equal(guid, &wblock->gblock.guid)) return 1; return 0;} static const struct bus_type wmi_bus_type; static const struct device_type wmi_type_event; static const struct device_type wmi_type_method; static int wmi_device_enable(struct wmi_device *wdev, bool enable){ struct wmi_block *wblock = container_of(wdev, struct wmi_block, dev); char method[WMI_ACPI_METHOD_NAME_SIZE]; acpi_handle handle; acpi_status status; if (wblock->dev.dev.type == &wmi_type_method) return 0; if (wblock->dev.dev.type == &wmi_type_event) { /* * Windows always enables/disables WMI events, even when they are * not marked as being expensive. We follow this behavior for * compatibility reasons. */ snprintf(method, sizeof(method), "WE%02X", wblock->gblock.notify_id); } else { if (!(wblock->gblock.flags & ACPI_WMI_EXPENSIVE)) return 0; get_acpi_method_name(wblock, 'C', method); } /* * Not all WMI devices marked as expensive actually implement the * necessary ACPI method. Ignore this missing ACPI method to match * the behaviour of the Windows driver. */ status = acpi_get_handle(wblock->acpi_device->handle, method, &handle); if (ACPI_FAILURE(status)) return 0; status = acpi_execute_simple_method(handle, NULL, enable); if (ACPI_FAILURE(status)) return -EIO; return 0;} static struct wmi_device *wmi_find_device_by_guid(const char *guid_string){ struct device *dev; guid_t guid; int ret; ret = guid_parse(guid_string, &guid); if (ret < 0) return ERR_PTR(ret); dev = bus_find_device(&wmi_bus_type, NULL, &guid, wmidev_match_guid); if (!dev) return ERR_PTR(-ENODEV); return to_wmi_device(dev);} static void wmi_device_put(struct wmi_device *wdev){ put_device(&wdev->dev);} /* * Exported WMI functions */ /** * wmi_instance_count - Get number of WMI object instances * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba * * Get the number of WMI object instances. * * Returns: Number of WMI object instances or negative error code. */int wmi_instance_count(const char *guid_string){ struct wmi_device *wdev; int ret; wdev = wmi_find_device_by_guid(guid_string); if (IS_ERR(wdev)) return PTR_ERR(wdev); ret = wmidev_instance_count(wdev); wmi_device_put(wdev); return ret;}EXPORT_SYMBOL_GPL(wmi_instance_count); /** * wmidev_instance_count - Get number of WMI object instances * @wdev: A wmi bus device from a driver * * Get the number of WMI object instances. * * Returns: Number of WMI object instances. */u8 wmidev_instance_count(struct wmi_device *wdev){ struct wmi_block *wblock = container_of(wdev, struct wmi_block, dev); return wblock->gblock.instance_count;}EXPORT_SYMBOL_GPL(wmidev_instance_count); /** * wmi_evaluate_method - Evaluate a WMI method (deprecated) * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba * @instance: Instance index * @method_id: Method ID to call * @in: Mandatory buffer containing input for the method call * @out: Empty buffer to return the method results * * Call an ACPI-WMI method, the caller must free @out. * * Return: acpi_status signaling success or error. */acpi_status wmi_evaluate_method(const char *guid_string, u8 instance, u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out){ struct wmi_device *wdev; acpi_status status; wdev = wmi_find_device_by_guid(guid_string); if (IS_ERR(wdev)) return AE_ERROR; status = wmidev_evaluate_method(wdev, instance, method_id, in, out); wmi_device_put(wdev); return status;}EXPORT_SYMBOL_GPL(wmi_evaluate_method); /** * wmidev_evaluate_method - Evaluate a WMI method (deprecated) * @wdev: A wmi bus device from a driver * @instance: Instance index * @method_id: Method ID to call * @in: Mandatory buffer containing input for the method call * @out: Empty buffer to return the method results * * Call an ACPI-WMI method, the caller must free @out. * * Return: acpi_status signaling success or error. */acpi_status wmidev_evaluate_method(struct wmi_device *wdev, u8 instance, u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out){ struct guid_block *block; struct wmi_block *wblock; acpi_handle handle; struct acpi_object_list input; union acpi_object params[3]; char method[WMI_ACPI_METHOD_NAME_SIZE]; wblock = container_of(wdev, struct wmi_block, dev); block = &wblock->gblock; handle = wblock->acpi_device->handle; if (!in) return AE_BAD_DATA; if (!(block->flags & ACPI_WMI_METHOD)) return AE_BAD_DATA; if (block->instance_count <= instance) return AE_BAD_PARAMETER; input.count = 3; input.pointer = params; params[0].type = ACPI_TYPE_INTEGER; params[0].integer.value = instance; params[1].type = ACPI_TYPE_INTEGER; params[1].integer.value = method_id; if (wblock->gblock.flags & ACPI_WMI_STRING) { params[2].type = ACPI_TYPE_STRING; params[2].string.length = in->length; params[2].string.pointer = in->pointer; } else { params[2].type = ACPI_TYPE_BUFFER; params[2].buffer.length = in->length; params[2].buffer.pointer = in->pointer; } get_acpi_method_name(wblock, 'M', method); return acpi_evaluate_object(handle, method, &input, out);}EXPORT_SYMBOL_GPL(wmidev_evaluate_method); /** * wmidev_invoke_method - Invoke a WMI method that returns values * @wdev: A wmi bus device from a driver * @instance: Instance index * @method_id: Method ID to call * @in: Mandatory WMI buffer containing input for the method call * @out: Mandatory WMI buffer to return the method results * @min_size: Minimum size of the method result data in bytes * * Invoke a WMI method that returns values, the caller must free the resulting * data inside @out using kfree(). Said data is guaranteed to be aligned on a * 8-byte boundary. Use wmidev_invoke_procedure() for WMI methods that * return no values. * * Return: 0 on success or negative error code on failure. */int wmidev_invoke_method(struct wmi_device *wdev, u8 instance, u32 method_id, const struct wmi_buffer *in, struct wmi_buffer *out, size_t min_size){ struct wmi_block *wblock = container_of(wdev, struct wmi_block, dev); struct acpi_buffer aout = { ACPI_ALLOCATE_BUFFER, NULL }; struct acpi_buffer ain; union acpi_object *obj; acpi_status status; int ret; if (wblock->gblock.flags & ACPI_WMI_STRING) { ret = wmi_marshal_string(in, &ain); if (ret < 0) return ret; } else { if (in->length > U32_MAX) return -E2BIG; ain.length = in->length; ain.pointer = in->data; } status = wmidev_evaluate_method(wdev, instance, method_id, &ain, &aout); if (wblock->gblock.flags & ACPI_WMI_STRING) kfree(ain.pointer); if (ACPI_FAILURE(status)) return -EIO; obj = aout.pointer; if (!obj) { if (min_size != 0) return -ENOMSG; out->length = 0; out->data = ZERO_SIZE_PTR; return 0; } ret = wmi_unmarshal_acpi_object(obj, out, min_size); kfree(obj); return ret;}EXPORT_SYMBOL_GPL(wmidev_invoke_method); /** * wmidev_invoke_procedure - Invoke a WMI method that does not return values * @wdev: A wmi bus device from a driver * @instance: Instance index * @method_id: Method ID to call * @in: Mandatory WMI buffer containing input for the method call * * Invoke a WMI method that does not return any values. Use wmidev_invoke_method() * for WMI methods that do return values. * * Return: 0 on success or negative error code on failure. */int wmidev_invoke_procedure(struct wmi_device *wdev, u8 instance, u32 method_id, const struct wmi_buffer *in){ struct wmi_block *wblock = container_of(wdev, struct wmi_block, dev); struct acpi_buffer ain; acpi_status status; int ret; if (wblock->gblock.flags & ACPI_WMI_STRING) { ret = wmi_marshal_string(in, &ain); if (ret < 0) return ret; } else { if (in->length > U32_MAX) return -E2BIG; ain.length = in->length; ain.pointer = in->data; } status = wmidev_evaluate_method(wdev, instance, method_id, &ain, NULL); if (wblock->gblock.flags & ACPI_WMI_STRING) kfree(ain.pointer); if (ACPI_FAILURE(status)) return -EIO; return 0;}EXPORT_SYMBOL_GPL(wmidev_invoke_procedure); static acpi_status __query_block(struct wmi_block *wblock, u8 instance, struct acpi_buffer *out){ struct guid_block *block; acpi_handle handle; struct acpi_object_list input; union acpi_object wq_params[1]; char method[WMI_ACPI_METHOD_NAME_SIZE]; if (!out) return AE_BAD_PARAMETER; block = &wblock->gblock; handle = wblock->acpi_device->handle; if (block->instance_count <= instance) return AE_BAD_PARAMETER; /* Check GUID is a data block */ if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD)) return AE_ERROR; input.count = 1; input.pointer = wq_params; wq_params[0].type = ACPI_TYPE_INTEGER; wq_params[0].integer.value = instance; Lines 1–500 of 1612. Load more
// SPDX-License-Identifier: GPL-2.0-or-later /* * ACPI-WMI mapping driver * * Copyright (C) 2007-2008 Carlos Corbacho <carlos@strangeworlds.co.uk> * * GUID parsing code from ldm.c is: * Copyright (C) 2001,2002 Richard Russon <ldm@flatcap.org> * Copyright (c) 2001-2007 Anton Altaparmakov * Copyright (C) 2001,2002 Jakob Kemi <jakob.kemi@telia.com> * * WMI bus infrastructure by Andrew Lutomirski and Darren Hart: * Copyright (C) 2015 Andrew Lutomirski * Copyright (C) 2017 VMware, Inc. All Rights Reserved. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/acpi.h> #include <linux/bits.h> #include <linux/build_bug.h> #include <linux/device.h> #include <linux/idr.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/limits.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/rwsem.h> #include <linux/slab.h> #include <linux/sysfs.h> #include <linux/types.h> #include <linux/uuid.h> #include <linux/wmi.h> #include <linux/fs.h> #include "internal.h" MODULE_AUTHOR("Carlos Corbacho"); MODULE_DESCRIPTION("ACPI-WMI Mapping Driver"); MODULE_LICENSE("GPL"); struct guid_block { guid_t guid; union { char object_id[2]; struct { unsigned char notify_id; unsigned char reserved; }; }; u8 instance_count; u8 flags; } __packed; static_assert(sizeof(typeof_member(struct guid_block, guid)) == 16); static_assert(sizeof(struct guid_block) == 20); static_assert(__alignof__(struct guid_block) == 1); enum { /* wmi_block flags */ WMI_READ_TAKES_NO_ARGS, WMI_GUID_DUPLICATED, WMI_NO_EVENT_DATA, }; struct wmi_block { struct wmi_device dev; struct guid_block gblock; struct acpi_device *acpi_device; struct rw_semaphore notify_lock; /* Protects notify callback add/remove */ wmi_notify_handler handler; void *handler_data; bool driver_ready; unsigned long flags; }; struct wmi_guid_count_context { const guid_t *guid; int count; }; static DEFINE_IDA(wmi_ida); /* * If the GUID data block is marked as expensive, we must enable and * explicitily disable data collection. */ #define ACPI_WMI_EXPENSIVE BIT(0) #define ACPI_WMI_METHOD BIT(1) /* GUID is a method */ #define ACPI_WMI_STRING BIT(2) /* GUID takes & returns a string */ #define ACPI_WMI_EVENT BIT(3) /* GUID is an event */ static const struct acpi_device_id wmi_device_ids[] = { {"PNP0C14", 0}, {"pnp0c14", 0}, { } }; MODULE_DEVICE_TABLE(acpi, wmi_device_ids); #define dev_to_wblock(__dev) container_of_const(__dev, struct wmi_block, dev.dev) /* * GUID parsing functions */ static bool guid_parse_and_compare(const char *string, const guid_t *guid) { guid_t guid_input; if (guid_parse(string, &guid_input)) return false; return guid_equal(&guid_input, guid); } static const void *find_guid_context(struct wmi_block *wblock, struct wmi_driver *wdriver) { const struct wmi_device_id *id; id = wdriver->id_table; if (!id) return NULL; while (*id->guid_string) { if (guid_parse_and_compare(id->guid_string, &wblock->gblock.guid)) return id->context; id++; } return NULL; } #define WMI_ACPI_METHOD_NAME_SIZE 5 static inline void get_acpi_method_name(const struct wmi_block *wblock, const char method, char buffer[static WMI_ACPI_METHOD_NAME_SIZE]) { static_assert(ARRAY_SIZE(wblock->gblock.object_id) == 2); static_assert(WMI_ACPI_METHOD_NAME_SIZE >= 5); buffer[0] = 'W'; buffer[1] = method; buffer[2] = wblock->gblock.object_id[0]; buffer[3] = wblock->gblock.object_id[1]; buffer[4] = '\0'; } static int wmidev_match_guid(struct device *dev, const void *data) { struct wmi_block *wblock = dev_to_wblock(dev); const guid_t *guid = data; /* Legacy GUID-based functions are restricted to only see * a single WMI device for each GUID. */ if (test_bit(WMI_GUID_DUPLICATED, &wblock->flags)) return 0; if (guid_equal(guid, &wblock->gblock.guid)) return 1; return 0; } static const struct bus_type wmi_bus_type; static const struct device_type wmi_type_event; static const struct device_type wmi_type_method; static int wmi_device_enable(struct wmi_device *wdev, bool enable) { struct wmi_block *wblock = container_of(wdev, struct wmi_block, dev); char method[WMI_ACPI_METHOD_NAME_SIZE]; acpi_handle handle; acpi_status status; if (wblock->dev.dev.type == &wmi_type_method) return 0; if (wblock->dev.dev.type == &wmi_type_event) { /* * Windows always enables/disables WMI events, even when they are * not marked as being expensive. We follow this behavior for * compatibility reasons. */ snprintf(method, sizeof(method), "WE%02X", wblock->gblock.notify_id); } else { if (!(wblock->gblock.flags & ACPI_WMI_EXPENSIVE)) return 0; get_acpi_method_name(wblock, 'C', method); } /* * Not all WMI devices marked as expensive actually implement the * necessary ACPI method. Ignore this missing ACPI method to match * the behaviour of the Windows driver. */ status = acpi_get_handle(wblock->acpi_device->handle, method, &handle); if (ACPI_FAILURE(status)) return 0; status = acpi_execute_simple_method(handle, NULL, enable); if (ACPI_FAILURE(status)) return -EIO; return 0; } static struct wmi_device *wmi_find_device_by_guid(const char *guid_string) { struct device *dev; guid_t guid; int ret; ret = guid_parse(guid_string, &guid); if (ret < 0) return ERR_PTR(ret); dev = bus_find_device(&wmi_bus_type, NULL, &guid, wmidev_match_guid); if (!dev) return ERR_PTR(-ENODEV); return to_wmi_device(dev); } static void wmi_device_put(struct wmi_device *wdev) { put_device(&wdev->dev); } /* * Exported WMI functions */ /** * wmi_instance_count - Get number of WMI object instances * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba * * Get the number of WMI object instances. * * Returns: Number of WMI object instances or negative error code. */ int wmi_instance_count(const char *guid_string) { struct wmi_device *wdev; int ret; wdev = wmi_find_device_by_guid(guid_string); if (IS_ERR(wdev)) return PTR_ERR(wdev); ret = wmidev_instance_count(wdev); wmi_device_put(wdev); return ret; } EXPORT_SYMBOL_GPL(wmi_instance_count); /** * wmidev_instance_count - Get number of WMI object instances * @wdev: A wmi bus device from a driver * * Get the number of WMI object instances. * * Returns: Number of WMI object instances. */ u8 wmidev_instance_count(struct wmi_device *wdev) { struct wmi_block *wblock = container_of(wdev, struct wmi_block, dev); return wblock->gblock.instance_count; } EXPORT_SYMBOL_GPL(wmidev_instance_count); /** * wmi_evaluate_method - Evaluate a WMI method (deprecated) * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba * @instance: Instance index * @method_id: Method ID to call * @in: Mandatory buffer containing input for the method call * @out: Empty buffer to return the method results * * Call an ACPI-WMI method, the caller must free @out. * * Return: acpi_status signaling success or error. */ acpi_status wmi_evaluate_method(const char *guid_string, u8 instance, u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out) { struct wmi_device *wdev; acpi_status status; wdev = wmi_find_device_by_guid(guid_string); if (IS_ERR(wdev)) return AE_ERROR; status = wmidev_evaluate_method(wdev, instance, method_id, in, out); wmi_device_put(wdev); return status; } EXPORT_SYMBOL_GPL(wmi_evaluate_method); /** * wmidev_evaluate_method - Evaluate a WMI method (deprecated) * @wdev: A wmi bus device from a driver * @instance: Instance index * @method_id: Method ID to call * @in: Mandatory buffer containing input for the method call * @out: Empty buffer to return the method results * * Call an ACPI-WMI method, the caller must free @out. * * Return: acpi_status signaling success or error. */ acpi_status wmidev_evaluate_method(struct wmi_device *wdev, u8 instance, u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out) { struct guid_block *block; struct wmi_block *wblock; acpi_handle handle; struct acpi_object_list input; union acpi_object params[3]; char method[WMI_ACPI_METHOD_NAME_SIZE]; wblock = container_of(wdev, struct wmi_block, dev); block = &wblock->gblock; handle = wblock->acpi_device->handle; if (!in) return AE_BAD_DATA; if (!(block->flags & ACPI_WMI_METHOD)) return AE_BAD_DATA; if (block->instance_count <= instance) return AE_BAD_PARAMETER; input.count = 3; input.pointer = params; params[0].type = ACPI_TYPE_INTEGER; params[0].integer.value = instance; params[1].type = ACPI_TYPE_INTEGER; params[1].integer.value = method_id; if (wblock->gblock.flags & ACPI_WMI_STRING) { params[2].type = ACPI_TYPE_STRING; params[2].string.length = in->length; params[2].string.pointer = in->pointer; } else { params[2].type = ACPI_TYPE_BUFFER; params[2].buffer.length = in->length; params[2].buffer.pointer = in->pointer; } get_acpi_method_name(wblock, 'M', method); return acpi_evaluate_object(handle, method, &input, out); } EXPORT_SYMBOL_GPL(wmidev_evaluate_method); /** * wmidev_invoke_method - Invoke a WMI method that returns values * @wdev: A wmi bus device from a driver * @instance: Instance index * @method_id: Method ID to call * @in: Mandatory WMI buffer containing input for the method call * @out: Mandatory WMI buffer to return the method results * @min_size: Minimum size of the method result data in bytes * * Invoke a WMI method that returns values, the caller must free the resulting * data inside @out using kfree(). Said data is guaranteed to be aligned on a * 8-byte boundary. Use wmidev_invoke_procedure() for WMI methods that * return no values. * * Return: 0 on success or negative error code on failure. */ int wmidev_invoke_method(struct wmi_device *wdev, u8 instance, u32 method_id, const struct wmi_buffer *in, struct wmi_buffer *out, size_t min_size) { struct wmi_block *wblock = container_of(wdev, struct wmi_block, dev); struct acpi_buffer aout = { ACPI_ALLOCATE_BUFFER, NULL }; struct acpi_buffer ain; union acpi_object *obj; acpi_status status; int ret; if (wblock->gblock.flags & ACPI_WMI_STRING) { ret = wmi_marshal_string(in, &ain); if (ret < 0) return ret; } else { if (in->length > U32_MAX) return -E2BIG; ain.length = in->length; ain.pointer = in->data; } status = wmidev_evaluate_method(wdev, instance, method_id, &ain, &aout); if (wblock->gblock.flags & ACPI_WMI_STRING) kfree(ain.pointer); if (ACPI_FAILURE(status)) return -EIO; obj = aout.pointer; if (!obj) { if (min_size != 0) return -ENOMSG; out->length = 0; out->data = ZERO_SIZE_PTR; return 0; } ret = wmi_unmarshal_acpi_object(obj, out, min_size); kfree(obj); return ret; } EXPORT_SYMBOL_GPL(wmidev_invoke_method); /** * wmidev_invoke_procedure - Invoke a WMI method that does not return values * @wdev: A wmi bus device from a driver * @instance: Instance index * @method_id: Method ID to call * @in: Mandatory WMI buffer containing input for the method call * * Invoke a WMI method that does not return any values. Use wmidev_invoke_method() * for WMI methods that do return values. * * Return: 0 on success or negative error code on failure. */ int wmidev_invoke_procedure(struct wmi_device *wdev, u8 instance, u32 method_id, const struct wmi_buffer *in) { struct wmi_block *wblock = container_of(wdev, struct wmi_block, dev); struct acpi_buffer ain; acpi_status status; int ret; if (wblock->gblock.flags & ACPI_WMI_STRING) { ret = wmi_marshal_string(in, &ain); if (ret < 0) return ret; } else { if (in->length > U32_MAX) return -E2BIG; ain.length = in->length; ain.pointer = in->data; } status = wmidev_evaluate_method(wdev, instance, method_id, &ain, NULL); if (wblock->gblock.flags & ACPI_WMI_STRING) kfree(ain.pointer); if (ACPI_FAILURE(status)) return -EIO; return 0; } EXPORT_SYMBOL_GPL(wmidev_invoke_procedure); static acpi_status __query_block(struct wmi_block *wblock, u8 instance, struct acpi_buffer *out) { struct guid_block *block; acpi_handle handle; struct acpi_object_list input; union acpi_object wq_params[1]; char method[WMI_ACPI_METHOD_NAME_SIZE]; if (!out) return AE_BAD_PARAMETER; block = &wblock->gblock; handle = wblock->acpi_device->handle; if (block->instance_count <= instance) return AE_BAD_PARAMETER; /* Check GUID is a data block */ if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD)) return AE_ERROR; input.count = 1; input.pointer = wq_params; wq_params[0].type = ACPI_TYPE_INTEGER; wq_params[0].integer.value = instance;