1612 lines · 39.4 KB · C
RawDownload
// 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.