162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  ACPI-WMI mapping driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Copyright (C) 2007-2008 Carlos Corbacho <carlos@strangeworlds.co.uk>
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci *  GUID parsing code from ldm.c is:
862306a36Sopenharmony_ci *   Copyright (C) 2001,2002 Richard Russon <ldm@flatcap.org>
962306a36Sopenharmony_ci *   Copyright (c) 2001-2007 Anton Altaparmakov
1062306a36Sopenharmony_ci *   Copyright (C) 2001,2002 Jakob Kemi <jakob.kemi@telia.com>
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci *  WMI bus infrastructure by Andrew Lutomirski and Darren Hart:
1362306a36Sopenharmony_ci *    Copyright (C) 2015 Andrew Lutomirski
1462306a36Sopenharmony_ci *    Copyright (C) 2017 VMware, Inc. All Rights Reserved.
1562306a36Sopenharmony_ci */
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include <linux/acpi.h>
2062306a36Sopenharmony_ci#include <linux/bits.h>
2162306a36Sopenharmony_ci#include <linux/build_bug.h>
2262306a36Sopenharmony_ci#include <linux/device.h>
2362306a36Sopenharmony_ci#include <linux/init.h>
2462306a36Sopenharmony_ci#include <linux/kernel.h>
2562306a36Sopenharmony_ci#include <linux/list.h>
2662306a36Sopenharmony_ci#include <linux/miscdevice.h>
2762306a36Sopenharmony_ci#include <linux/module.h>
2862306a36Sopenharmony_ci#include <linux/platform_device.h>
2962306a36Sopenharmony_ci#include <linux/slab.h>
3062306a36Sopenharmony_ci#include <linux/sysfs.h>
3162306a36Sopenharmony_ci#include <linux/types.h>
3262306a36Sopenharmony_ci#include <linux/uaccess.h>
3362306a36Sopenharmony_ci#include <linux/uuid.h>
3462306a36Sopenharmony_ci#include <linux/wmi.h>
3562306a36Sopenharmony_ci#include <linux/fs.h>
3662306a36Sopenharmony_ci#include <uapi/linux/wmi.h>
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ciMODULE_AUTHOR("Carlos Corbacho");
3962306a36Sopenharmony_ciMODULE_DESCRIPTION("ACPI-WMI Mapping Driver");
4062306a36Sopenharmony_ciMODULE_LICENSE("GPL");
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic LIST_HEAD(wmi_block_list);
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistruct guid_block {
4562306a36Sopenharmony_ci	guid_t guid;
4662306a36Sopenharmony_ci	union {
4762306a36Sopenharmony_ci		char object_id[2];
4862306a36Sopenharmony_ci		struct {
4962306a36Sopenharmony_ci			unsigned char notify_id;
5062306a36Sopenharmony_ci			unsigned char reserved;
5162306a36Sopenharmony_ci		};
5262306a36Sopenharmony_ci	};
5362306a36Sopenharmony_ci	u8 instance_count;
5462306a36Sopenharmony_ci	u8 flags;
5562306a36Sopenharmony_ci} __packed;
5662306a36Sopenharmony_cistatic_assert(sizeof(typeof_member(struct guid_block, guid)) == 16);
5762306a36Sopenharmony_cistatic_assert(sizeof(struct guid_block) == 20);
5862306a36Sopenharmony_cistatic_assert(__alignof__(struct guid_block) == 1);
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_cienum {	/* wmi_block flags */
6162306a36Sopenharmony_ci	WMI_READ_TAKES_NO_ARGS,
6262306a36Sopenharmony_ci	WMI_PROBED,
6362306a36Sopenharmony_ci};
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistruct wmi_block {
6662306a36Sopenharmony_ci	struct wmi_device dev;
6762306a36Sopenharmony_ci	struct list_head list;
6862306a36Sopenharmony_ci	struct guid_block gblock;
6962306a36Sopenharmony_ci	struct miscdevice char_dev;
7062306a36Sopenharmony_ci	struct mutex char_mutex;
7162306a36Sopenharmony_ci	struct acpi_device *acpi_device;
7262306a36Sopenharmony_ci	wmi_notify_handler handler;
7362306a36Sopenharmony_ci	void *handler_data;
7462306a36Sopenharmony_ci	u64 req_buf_size;
7562306a36Sopenharmony_ci	unsigned long flags;
7662306a36Sopenharmony_ci};
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci/*
8062306a36Sopenharmony_ci * If the GUID data block is marked as expensive, we must enable and
8162306a36Sopenharmony_ci * explicitily disable data collection.
8262306a36Sopenharmony_ci */
8362306a36Sopenharmony_ci#define ACPI_WMI_EXPENSIVE   BIT(0)
8462306a36Sopenharmony_ci#define ACPI_WMI_METHOD      BIT(1)	/* GUID is a method */
8562306a36Sopenharmony_ci#define ACPI_WMI_STRING      BIT(2)	/* GUID takes & returns a string */
8662306a36Sopenharmony_ci#define ACPI_WMI_EVENT       BIT(3)	/* GUID is an event */
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic bool debug_event;
8962306a36Sopenharmony_cimodule_param(debug_event, bool, 0444);
9062306a36Sopenharmony_ciMODULE_PARM_DESC(debug_event,
9162306a36Sopenharmony_ci		 "Log WMI Events [0/1]");
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistatic bool debug_dump_wdg;
9462306a36Sopenharmony_cimodule_param(debug_dump_wdg, bool, 0444);
9562306a36Sopenharmony_ciMODULE_PARM_DESC(debug_dump_wdg,
9662306a36Sopenharmony_ci		 "Dump available WMI interfaces [0/1]");
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic const struct acpi_device_id wmi_device_ids[] = {
9962306a36Sopenharmony_ci	{"PNP0C14", 0},
10062306a36Sopenharmony_ci	{"pnp0c14", 0},
10162306a36Sopenharmony_ci	{ }
10262306a36Sopenharmony_ci};
10362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, wmi_device_ids);
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci/* allow duplicate GUIDs as these device drivers use struct wmi_driver */
10662306a36Sopenharmony_cistatic const char * const allow_duplicates[] = {
10762306a36Sopenharmony_ci	"05901221-D566-11D1-B2F0-00A0C9062910",	/* wmi-bmof */
10862306a36Sopenharmony_ci	"8A42EA14-4F2A-FD45-6422-0087F7A7E608",	/* dell-wmi-ddv */
10962306a36Sopenharmony_ci	NULL
11062306a36Sopenharmony_ci};
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci/*
11362306a36Sopenharmony_ci * GUID parsing functions
11462306a36Sopenharmony_ci */
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistatic acpi_status find_guid(const char *guid_string, struct wmi_block **out)
11762306a36Sopenharmony_ci{
11862306a36Sopenharmony_ci	guid_t guid_input;
11962306a36Sopenharmony_ci	struct wmi_block *wblock;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	if (!guid_string)
12262306a36Sopenharmony_ci		return AE_BAD_PARAMETER;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	if (guid_parse(guid_string, &guid_input))
12562306a36Sopenharmony_ci		return AE_BAD_PARAMETER;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	list_for_each_entry(wblock, &wmi_block_list, list) {
12862306a36Sopenharmony_ci		if (guid_equal(&wblock->gblock.guid, &guid_input)) {
12962306a36Sopenharmony_ci			if (out)
13062306a36Sopenharmony_ci				*out = wblock;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci			return AE_OK;
13362306a36Sopenharmony_ci		}
13462306a36Sopenharmony_ci	}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	return AE_NOT_FOUND;
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_cistatic bool guid_parse_and_compare(const char *string, const guid_t *guid)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	guid_t guid_input;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	if (guid_parse(string, &guid_input))
14462306a36Sopenharmony_ci		return false;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	return guid_equal(&guid_input, guid);
14762306a36Sopenharmony_ci}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_cistatic const void *find_guid_context(struct wmi_block *wblock,
15062306a36Sopenharmony_ci				     struct wmi_driver *wdriver)
15162306a36Sopenharmony_ci{
15262306a36Sopenharmony_ci	const struct wmi_device_id *id;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	id = wdriver->id_table;
15562306a36Sopenharmony_ci	if (!id)
15662306a36Sopenharmony_ci		return NULL;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	while (*id->guid_string) {
15962306a36Sopenharmony_ci		if (guid_parse_and_compare(id->guid_string, &wblock->gblock.guid))
16062306a36Sopenharmony_ci			return id->context;
16162306a36Sopenharmony_ci		id++;
16262306a36Sopenharmony_ci	}
16362306a36Sopenharmony_ci	return NULL;
16462306a36Sopenharmony_ci}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_cistatic int get_subobj_info(acpi_handle handle, const char *pathname,
16762306a36Sopenharmony_ci			   struct acpi_device_info **info)
16862306a36Sopenharmony_ci{
16962306a36Sopenharmony_ci	struct acpi_device_info *dummy_info, **info_ptr;
17062306a36Sopenharmony_ci	acpi_handle subobj_handle;
17162306a36Sopenharmony_ci	acpi_status status;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	status = acpi_get_handle(handle, (char *)pathname, &subobj_handle);
17462306a36Sopenharmony_ci	if (status == AE_NOT_FOUND)
17562306a36Sopenharmony_ci		return -ENOENT;
17662306a36Sopenharmony_ci	else if (ACPI_FAILURE(status))
17762306a36Sopenharmony_ci		return -EIO;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	info_ptr = info ? info : &dummy_info;
18062306a36Sopenharmony_ci	status = acpi_get_object_info(subobj_handle, info_ptr);
18162306a36Sopenharmony_ci	if (ACPI_FAILURE(status))
18262306a36Sopenharmony_ci		return -EIO;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	if (!info)
18562306a36Sopenharmony_ci		kfree(dummy_info);
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	return 0;
18862306a36Sopenharmony_ci}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_cistatic acpi_status wmi_method_enable(struct wmi_block *wblock, bool enable)
19162306a36Sopenharmony_ci{
19262306a36Sopenharmony_ci	struct guid_block *block;
19362306a36Sopenharmony_ci	char method[5];
19462306a36Sopenharmony_ci	acpi_status status;
19562306a36Sopenharmony_ci	acpi_handle handle;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	block = &wblock->gblock;
19862306a36Sopenharmony_ci	handle = wblock->acpi_device->handle;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	snprintf(method, 5, "WE%02X", block->notify_id);
20162306a36Sopenharmony_ci	status = acpi_execute_simple_method(handle, method, enable);
20262306a36Sopenharmony_ci	if (status == AE_NOT_FOUND)
20362306a36Sopenharmony_ci		return AE_OK;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	return status;
20662306a36Sopenharmony_ci}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci#define WMI_ACPI_METHOD_NAME_SIZE 5
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_cistatic inline void get_acpi_method_name(const struct wmi_block *wblock,
21162306a36Sopenharmony_ci					const char method,
21262306a36Sopenharmony_ci					char buffer[static WMI_ACPI_METHOD_NAME_SIZE])
21362306a36Sopenharmony_ci{
21462306a36Sopenharmony_ci	static_assert(ARRAY_SIZE(wblock->gblock.object_id) == 2);
21562306a36Sopenharmony_ci	static_assert(WMI_ACPI_METHOD_NAME_SIZE >= 5);
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	buffer[0] = 'W';
21862306a36Sopenharmony_ci	buffer[1] = method;
21962306a36Sopenharmony_ci	buffer[2] = wblock->gblock.object_id[0];
22062306a36Sopenharmony_ci	buffer[3] = wblock->gblock.object_id[1];
22162306a36Sopenharmony_ci	buffer[4] = '\0';
22262306a36Sopenharmony_ci}
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_cistatic inline acpi_object_type get_param_acpi_type(const struct wmi_block *wblock)
22562306a36Sopenharmony_ci{
22662306a36Sopenharmony_ci	if (wblock->gblock.flags & ACPI_WMI_STRING)
22762306a36Sopenharmony_ci		return ACPI_TYPE_STRING;
22862306a36Sopenharmony_ci	else
22962306a36Sopenharmony_ci		return ACPI_TYPE_BUFFER;
23062306a36Sopenharmony_ci}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_cistatic acpi_status get_event_data(const struct wmi_block *wblock, struct acpi_buffer *out)
23362306a36Sopenharmony_ci{
23462306a36Sopenharmony_ci	union acpi_object param = {
23562306a36Sopenharmony_ci		.integer = {
23662306a36Sopenharmony_ci			.type = ACPI_TYPE_INTEGER,
23762306a36Sopenharmony_ci			.value = wblock->gblock.notify_id,
23862306a36Sopenharmony_ci		}
23962306a36Sopenharmony_ci	};
24062306a36Sopenharmony_ci	struct acpi_object_list input = {
24162306a36Sopenharmony_ci		.count = 1,
24262306a36Sopenharmony_ci		.pointer = &param,
24362306a36Sopenharmony_ci	};
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	return acpi_evaluate_object(wblock->acpi_device->handle, "_WED", &input, out);
24662306a36Sopenharmony_ci}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci/*
24962306a36Sopenharmony_ci * Exported WMI functions
25062306a36Sopenharmony_ci */
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci/**
25362306a36Sopenharmony_ci * set_required_buffer_size - Sets the buffer size needed for performing IOCTL
25462306a36Sopenharmony_ci * @wdev: A wmi bus device from a driver
25562306a36Sopenharmony_ci * @length: Required buffer size
25662306a36Sopenharmony_ci *
25762306a36Sopenharmony_ci * Allocates memory needed for buffer, stores the buffer size in that memory.
25862306a36Sopenharmony_ci *
25962306a36Sopenharmony_ci * Return: 0 on success or a negative error code for failure.
26062306a36Sopenharmony_ci */
26162306a36Sopenharmony_ciint set_required_buffer_size(struct wmi_device *wdev, u64 length)
26262306a36Sopenharmony_ci{
26362306a36Sopenharmony_ci	struct wmi_block *wblock;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	wblock = container_of(wdev, struct wmi_block, dev);
26662306a36Sopenharmony_ci	wblock->req_buf_size = length;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	return 0;
26962306a36Sopenharmony_ci}
27062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(set_required_buffer_size);
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci/**
27362306a36Sopenharmony_ci * wmi_instance_count - Get number of WMI object instances
27462306a36Sopenharmony_ci * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
27562306a36Sopenharmony_ci *
27662306a36Sopenharmony_ci * Get the number of WMI object instances.
27762306a36Sopenharmony_ci *
27862306a36Sopenharmony_ci * Returns: Number of WMI object instances or negative error code.
27962306a36Sopenharmony_ci */
28062306a36Sopenharmony_ciint wmi_instance_count(const char *guid_string)
28162306a36Sopenharmony_ci{
28262306a36Sopenharmony_ci	struct wmi_block *wblock;
28362306a36Sopenharmony_ci	acpi_status status;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	status = find_guid(guid_string, &wblock);
28662306a36Sopenharmony_ci	if (ACPI_FAILURE(status)) {
28762306a36Sopenharmony_ci		if (status == AE_BAD_PARAMETER)
28862306a36Sopenharmony_ci			return -EINVAL;
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci		return -ENODEV;
29162306a36Sopenharmony_ci	}
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	return wmidev_instance_count(&wblock->dev);
29462306a36Sopenharmony_ci}
29562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(wmi_instance_count);
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci/**
29862306a36Sopenharmony_ci * wmidev_instance_count - Get number of WMI object instances
29962306a36Sopenharmony_ci * @wdev: A wmi bus device from a driver
30062306a36Sopenharmony_ci *
30162306a36Sopenharmony_ci * Get the number of WMI object instances.
30262306a36Sopenharmony_ci *
30362306a36Sopenharmony_ci * Returns: Number of WMI object instances.
30462306a36Sopenharmony_ci */
30562306a36Sopenharmony_ciu8 wmidev_instance_count(struct wmi_device *wdev)
30662306a36Sopenharmony_ci{
30762306a36Sopenharmony_ci	struct wmi_block *wblock = container_of(wdev, struct wmi_block, dev);
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	return wblock->gblock.instance_count;
31062306a36Sopenharmony_ci}
31162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(wmidev_instance_count);
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci/**
31462306a36Sopenharmony_ci * wmi_evaluate_method - Evaluate a WMI method (deprecated)
31562306a36Sopenharmony_ci * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
31662306a36Sopenharmony_ci * @instance: Instance index
31762306a36Sopenharmony_ci * @method_id: Method ID to call
31862306a36Sopenharmony_ci * @in: Buffer containing input for the method call
31962306a36Sopenharmony_ci * @out: Empty buffer to return the method results
32062306a36Sopenharmony_ci *
32162306a36Sopenharmony_ci * Call an ACPI-WMI method, the caller must free @out.
32262306a36Sopenharmony_ci *
32362306a36Sopenharmony_ci * Return: acpi_status signaling success or error.
32462306a36Sopenharmony_ci */
32562306a36Sopenharmony_ciacpi_status wmi_evaluate_method(const char *guid_string, u8 instance, u32 method_id,
32662306a36Sopenharmony_ci				const struct acpi_buffer *in, struct acpi_buffer *out)
32762306a36Sopenharmony_ci{
32862306a36Sopenharmony_ci	struct wmi_block *wblock = NULL;
32962306a36Sopenharmony_ci	acpi_status status;
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	status = find_guid(guid_string, &wblock);
33262306a36Sopenharmony_ci	if (ACPI_FAILURE(status))
33362306a36Sopenharmony_ci		return status;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	return wmidev_evaluate_method(&wblock->dev, instance, method_id,
33662306a36Sopenharmony_ci				      in, out);
33762306a36Sopenharmony_ci}
33862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(wmi_evaluate_method);
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci/**
34162306a36Sopenharmony_ci * wmidev_evaluate_method - Evaluate a WMI method
34262306a36Sopenharmony_ci * @wdev: A wmi bus device from a driver
34362306a36Sopenharmony_ci * @instance: Instance index
34462306a36Sopenharmony_ci * @method_id: Method ID to call
34562306a36Sopenharmony_ci * @in: Buffer containing input for the method call
34662306a36Sopenharmony_ci * @out: Empty buffer to return the method results
34762306a36Sopenharmony_ci *
34862306a36Sopenharmony_ci * Call an ACPI-WMI method, the caller must free @out.
34962306a36Sopenharmony_ci *
35062306a36Sopenharmony_ci * Return: acpi_status signaling success or error.
35162306a36Sopenharmony_ci */
35262306a36Sopenharmony_ciacpi_status wmidev_evaluate_method(struct wmi_device *wdev, u8 instance, u32 method_id,
35362306a36Sopenharmony_ci				   const struct acpi_buffer *in, struct acpi_buffer *out)
35462306a36Sopenharmony_ci{
35562306a36Sopenharmony_ci	struct guid_block *block;
35662306a36Sopenharmony_ci	struct wmi_block *wblock;
35762306a36Sopenharmony_ci	acpi_handle handle;
35862306a36Sopenharmony_ci	struct acpi_object_list input;
35962306a36Sopenharmony_ci	union acpi_object params[3];
36062306a36Sopenharmony_ci	char method[WMI_ACPI_METHOD_NAME_SIZE];
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	wblock = container_of(wdev, struct wmi_block, dev);
36362306a36Sopenharmony_ci	block = &wblock->gblock;
36462306a36Sopenharmony_ci	handle = wblock->acpi_device->handle;
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	if (!(block->flags & ACPI_WMI_METHOD))
36762306a36Sopenharmony_ci		return AE_BAD_DATA;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	if (block->instance_count <= instance)
37062306a36Sopenharmony_ci		return AE_BAD_PARAMETER;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	input.count = 2;
37362306a36Sopenharmony_ci	input.pointer = params;
37462306a36Sopenharmony_ci	params[0].type = ACPI_TYPE_INTEGER;
37562306a36Sopenharmony_ci	params[0].integer.value = instance;
37662306a36Sopenharmony_ci	params[1].type = ACPI_TYPE_INTEGER;
37762306a36Sopenharmony_ci	params[1].integer.value = method_id;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	if (in) {
38062306a36Sopenharmony_ci		input.count = 3;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci		params[2].type = get_param_acpi_type(wblock);
38362306a36Sopenharmony_ci		params[2].buffer.length = in->length;
38462306a36Sopenharmony_ci		params[2].buffer.pointer = in->pointer;
38562306a36Sopenharmony_ci	}
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	get_acpi_method_name(wblock, 'M', method);
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	return acpi_evaluate_object(handle, method, &input, out);
39062306a36Sopenharmony_ci}
39162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(wmidev_evaluate_method);
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_cistatic acpi_status __query_block(struct wmi_block *wblock, u8 instance,
39462306a36Sopenharmony_ci				 struct acpi_buffer *out)
39562306a36Sopenharmony_ci{
39662306a36Sopenharmony_ci	struct guid_block *block;
39762306a36Sopenharmony_ci	acpi_handle handle;
39862306a36Sopenharmony_ci	acpi_status status, wc_status = AE_ERROR;
39962306a36Sopenharmony_ci	struct acpi_object_list input;
40062306a36Sopenharmony_ci	union acpi_object wq_params[1];
40162306a36Sopenharmony_ci	char wc_method[WMI_ACPI_METHOD_NAME_SIZE];
40262306a36Sopenharmony_ci	char method[WMI_ACPI_METHOD_NAME_SIZE];
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	if (!out)
40562306a36Sopenharmony_ci		return AE_BAD_PARAMETER;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	block = &wblock->gblock;
40862306a36Sopenharmony_ci	handle = wblock->acpi_device->handle;
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	if (block->instance_count <= instance)
41162306a36Sopenharmony_ci		return AE_BAD_PARAMETER;
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	/* Check GUID is a data block */
41462306a36Sopenharmony_ci	if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD))
41562306a36Sopenharmony_ci		return AE_ERROR;
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	input.count = 1;
41862306a36Sopenharmony_ci	input.pointer = wq_params;
41962306a36Sopenharmony_ci	wq_params[0].type = ACPI_TYPE_INTEGER;
42062306a36Sopenharmony_ci	wq_params[0].integer.value = instance;
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	if (instance == 0 && test_bit(WMI_READ_TAKES_NO_ARGS, &wblock->flags))
42362306a36Sopenharmony_ci		input.count = 0;
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	/*
42662306a36Sopenharmony_ci	 * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method first to
42762306a36Sopenharmony_ci	 * enable collection.
42862306a36Sopenharmony_ci	 */
42962306a36Sopenharmony_ci	if (block->flags & ACPI_WMI_EXPENSIVE) {
43062306a36Sopenharmony_ci		get_acpi_method_name(wblock, 'C', wc_method);
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci		/*
43362306a36Sopenharmony_ci		 * Some GUIDs break the specification by declaring themselves
43462306a36Sopenharmony_ci		 * expensive, but have no corresponding WCxx method. So we
43562306a36Sopenharmony_ci		 * should not fail if this happens.
43662306a36Sopenharmony_ci		 */
43762306a36Sopenharmony_ci		wc_status = acpi_execute_simple_method(handle, wc_method, 1);
43862306a36Sopenharmony_ci	}
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	get_acpi_method_name(wblock, 'Q', method);
44162306a36Sopenharmony_ci	status = acpi_evaluate_object(handle, method, &input, out);
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	/*
44462306a36Sopenharmony_ci	 * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method, even if
44562306a36Sopenharmony_ci	 * the WQxx method failed - we should disable collection anyway.
44662306a36Sopenharmony_ci	 */
44762306a36Sopenharmony_ci	if ((block->flags & ACPI_WMI_EXPENSIVE) && ACPI_SUCCESS(wc_status)) {
44862306a36Sopenharmony_ci		/*
44962306a36Sopenharmony_ci		 * Ignore whether this WCxx call succeeds or not since
45062306a36Sopenharmony_ci		 * the previously executed WQxx method call might have
45162306a36Sopenharmony_ci		 * succeeded, and returning the failing status code
45262306a36Sopenharmony_ci		 * of this call would throw away the result of the WQxx
45362306a36Sopenharmony_ci		 * call, potentially leaking memory.
45462306a36Sopenharmony_ci		 */
45562306a36Sopenharmony_ci		acpi_execute_simple_method(handle, wc_method, 0);
45662306a36Sopenharmony_ci	}
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	return status;
45962306a36Sopenharmony_ci}
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci/**
46262306a36Sopenharmony_ci * wmi_query_block - Return contents of a WMI block (deprecated)
46362306a36Sopenharmony_ci * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
46462306a36Sopenharmony_ci * @instance: Instance index
46562306a36Sopenharmony_ci * @out: Empty buffer to return the contents of the data block to
46662306a36Sopenharmony_ci *
46762306a36Sopenharmony_ci * Query a ACPI-WMI block, the caller must free @out.
46862306a36Sopenharmony_ci *
46962306a36Sopenharmony_ci * Return: ACPI object containing the content of the WMI block.
47062306a36Sopenharmony_ci */
47162306a36Sopenharmony_ciacpi_status wmi_query_block(const char *guid_string, u8 instance,
47262306a36Sopenharmony_ci			    struct acpi_buffer *out)
47362306a36Sopenharmony_ci{
47462306a36Sopenharmony_ci	struct wmi_block *wblock;
47562306a36Sopenharmony_ci	acpi_status status;
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	status = find_guid(guid_string, &wblock);
47862306a36Sopenharmony_ci	if (ACPI_FAILURE(status))
47962306a36Sopenharmony_ci		return status;
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	return __query_block(wblock, instance, out);
48262306a36Sopenharmony_ci}
48362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(wmi_query_block);
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci/**
48662306a36Sopenharmony_ci * wmidev_block_query - Return contents of a WMI block
48762306a36Sopenharmony_ci * @wdev: A wmi bus device from a driver
48862306a36Sopenharmony_ci * @instance: Instance index
48962306a36Sopenharmony_ci *
49062306a36Sopenharmony_ci * Query an ACPI-WMI block, the caller must free the result.
49162306a36Sopenharmony_ci *
49262306a36Sopenharmony_ci * Return: ACPI object containing the content of the WMI block.
49362306a36Sopenharmony_ci */
49462306a36Sopenharmony_ciunion acpi_object *wmidev_block_query(struct wmi_device *wdev, u8 instance)
49562306a36Sopenharmony_ci{
49662306a36Sopenharmony_ci	struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
49762306a36Sopenharmony_ci	struct wmi_block *wblock = container_of(wdev, struct wmi_block, dev);
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	if (ACPI_FAILURE(__query_block(wblock, instance, &out)))
50062306a36Sopenharmony_ci		return NULL;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	return out.pointer;
50362306a36Sopenharmony_ci}
50462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(wmidev_block_query);
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci/**
50762306a36Sopenharmony_ci * wmi_set_block - Write to a WMI block (deprecated)
50862306a36Sopenharmony_ci * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
50962306a36Sopenharmony_ci * @instance: Instance index
51062306a36Sopenharmony_ci * @in: Buffer containing new values for the data block
51162306a36Sopenharmony_ci *
51262306a36Sopenharmony_ci * Write the contents of the input buffer to an ACPI-WMI data block.
51362306a36Sopenharmony_ci *
51462306a36Sopenharmony_ci * Return: acpi_status signaling success or error.
51562306a36Sopenharmony_ci */
51662306a36Sopenharmony_ciacpi_status wmi_set_block(const char *guid_string, u8 instance,
51762306a36Sopenharmony_ci			  const struct acpi_buffer *in)
51862306a36Sopenharmony_ci{
51962306a36Sopenharmony_ci	struct wmi_block *wblock = NULL;
52062306a36Sopenharmony_ci	struct guid_block *block;
52162306a36Sopenharmony_ci	acpi_handle handle;
52262306a36Sopenharmony_ci	struct acpi_object_list input;
52362306a36Sopenharmony_ci	union acpi_object params[2];
52462306a36Sopenharmony_ci	char method[WMI_ACPI_METHOD_NAME_SIZE];
52562306a36Sopenharmony_ci	acpi_status status;
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	if (!in)
52862306a36Sopenharmony_ci		return AE_BAD_DATA;
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	status = find_guid(guid_string, &wblock);
53162306a36Sopenharmony_ci	if (ACPI_FAILURE(status))
53262306a36Sopenharmony_ci		return status;
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	block = &wblock->gblock;
53562306a36Sopenharmony_ci	handle = wblock->acpi_device->handle;
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	if (block->instance_count <= instance)
53862306a36Sopenharmony_ci		return AE_BAD_PARAMETER;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	/* Check GUID is a data block */
54162306a36Sopenharmony_ci	if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD))
54262306a36Sopenharmony_ci		return AE_ERROR;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	input.count = 2;
54562306a36Sopenharmony_ci	input.pointer = params;
54662306a36Sopenharmony_ci	params[0].type = ACPI_TYPE_INTEGER;
54762306a36Sopenharmony_ci	params[0].integer.value = instance;
54862306a36Sopenharmony_ci	params[1].type = get_param_acpi_type(wblock);
54962306a36Sopenharmony_ci	params[1].buffer.length = in->length;
55062306a36Sopenharmony_ci	params[1].buffer.pointer = in->pointer;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	get_acpi_method_name(wblock, 'S', method);
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	return acpi_evaluate_object(handle, method, &input, NULL);
55562306a36Sopenharmony_ci}
55662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(wmi_set_block);
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_cistatic void wmi_dump_wdg(const struct guid_block *g)
55962306a36Sopenharmony_ci{
56062306a36Sopenharmony_ci	pr_info("%pUL:\n", &g->guid);
56162306a36Sopenharmony_ci	if (g->flags & ACPI_WMI_EVENT)
56262306a36Sopenharmony_ci		pr_info("\tnotify_id: 0x%02X\n", g->notify_id);
56362306a36Sopenharmony_ci	else
56462306a36Sopenharmony_ci		pr_info("\tobject_id: %2pE\n", g->object_id);
56562306a36Sopenharmony_ci	pr_info("\tinstance_count: %d\n", g->instance_count);
56662306a36Sopenharmony_ci	pr_info("\tflags: %#x", g->flags);
56762306a36Sopenharmony_ci	if (g->flags) {
56862306a36Sopenharmony_ci		if (g->flags & ACPI_WMI_EXPENSIVE)
56962306a36Sopenharmony_ci			pr_cont(" ACPI_WMI_EXPENSIVE");
57062306a36Sopenharmony_ci		if (g->flags & ACPI_WMI_METHOD)
57162306a36Sopenharmony_ci			pr_cont(" ACPI_WMI_METHOD");
57262306a36Sopenharmony_ci		if (g->flags & ACPI_WMI_STRING)
57362306a36Sopenharmony_ci			pr_cont(" ACPI_WMI_STRING");
57462306a36Sopenharmony_ci		if (g->flags & ACPI_WMI_EVENT)
57562306a36Sopenharmony_ci			pr_cont(" ACPI_WMI_EVENT");
57662306a36Sopenharmony_ci	}
57762306a36Sopenharmony_ci	pr_cont("\n");
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci}
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_cistatic void wmi_notify_debug(u32 value, void *context)
58262306a36Sopenharmony_ci{
58362306a36Sopenharmony_ci	struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
58462306a36Sopenharmony_ci	union acpi_object *obj;
58562306a36Sopenharmony_ci	acpi_status status;
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	status = wmi_get_event_data(value, &response);
58862306a36Sopenharmony_ci	if (status != AE_OK) {
58962306a36Sopenharmony_ci		pr_info("bad event status 0x%x\n", status);
59062306a36Sopenharmony_ci		return;
59162306a36Sopenharmony_ci	}
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	obj = response.pointer;
59462306a36Sopenharmony_ci	if (!obj)
59562306a36Sopenharmony_ci		return;
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	pr_info("DEBUG: event 0x%02X ", value);
59862306a36Sopenharmony_ci	switch (obj->type) {
59962306a36Sopenharmony_ci	case ACPI_TYPE_BUFFER:
60062306a36Sopenharmony_ci		pr_cont("BUFFER_TYPE - length %u\n", obj->buffer.length);
60162306a36Sopenharmony_ci		break;
60262306a36Sopenharmony_ci	case ACPI_TYPE_STRING:
60362306a36Sopenharmony_ci		pr_cont("STRING_TYPE - %s\n", obj->string.pointer);
60462306a36Sopenharmony_ci		break;
60562306a36Sopenharmony_ci	case ACPI_TYPE_INTEGER:
60662306a36Sopenharmony_ci		pr_cont("INTEGER_TYPE - %llu\n", obj->integer.value);
60762306a36Sopenharmony_ci		break;
60862306a36Sopenharmony_ci	case ACPI_TYPE_PACKAGE:
60962306a36Sopenharmony_ci		pr_cont("PACKAGE_TYPE - %u elements\n", obj->package.count);
61062306a36Sopenharmony_ci		break;
61162306a36Sopenharmony_ci	default:
61262306a36Sopenharmony_ci		pr_cont("object type 0x%X\n", obj->type);
61362306a36Sopenharmony_ci	}
61462306a36Sopenharmony_ci	kfree(obj);
61562306a36Sopenharmony_ci}
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci/**
61862306a36Sopenharmony_ci * wmi_install_notify_handler - Register handler for WMI events (deprecated)
61962306a36Sopenharmony_ci * @guid: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
62062306a36Sopenharmony_ci * @handler: Function to handle notifications
62162306a36Sopenharmony_ci * @data: Data to be returned to handler when event is fired
62262306a36Sopenharmony_ci *
62362306a36Sopenharmony_ci * Register a handler for events sent to the ACPI-WMI mapper device.
62462306a36Sopenharmony_ci *
62562306a36Sopenharmony_ci * Return: acpi_status signaling success or error.
62662306a36Sopenharmony_ci */
62762306a36Sopenharmony_ciacpi_status wmi_install_notify_handler(const char *guid,
62862306a36Sopenharmony_ci				       wmi_notify_handler handler,
62962306a36Sopenharmony_ci				       void *data)
63062306a36Sopenharmony_ci{
63162306a36Sopenharmony_ci	struct wmi_block *block;
63262306a36Sopenharmony_ci	acpi_status status = AE_NOT_EXIST;
63362306a36Sopenharmony_ci	guid_t guid_input;
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	if (!guid || !handler)
63662306a36Sopenharmony_ci		return AE_BAD_PARAMETER;
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	if (guid_parse(guid, &guid_input))
63962306a36Sopenharmony_ci		return AE_BAD_PARAMETER;
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	list_for_each_entry(block, &wmi_block_list, list) {
64262306a36Sopenharmony_ci		acpi_status wmi_status;
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci		if (guid_equal(&block->gblock.guid, &guid_input)) {
64562306a36Sopenharmony_ci			if (block->handler &&
64662306a36Sopenharmony_ci			    block->handler != wmi_notify_debug)
64762306a36Sopenharmony_ci				return AE_ALREADY_ACQUIRED;
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci			block->handler = handler;
65062306a36Sopenharmony_ci			block->handler_data = data;
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci			wmi_status = wmi_method_enable(block, true);
65362306a36Sopenharmony_ci			if ((wmi_status != AE_OK) ||
65462306a36Sopenharmony_ci			    ((wmi_status == AE_OK) && (status == AE_NOT_EXIST)))
65562306a36Sopenharmony_ci				status = wmi_status;
65662306a36Sopenharmony_ci		}
65762306a36Sopenharmony_ci	}
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	return status;
66062306a36Sopenharmony_ci}
66162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(wmi_install_notify_handler);
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci/**
66462306a36Sopenharmony_ci * wmi_remove_notify_handler - Unregister handler for WMI events (deprecated)
66562306a36Sopenharmony_ci * @guid: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
66662306a36Sopenharmony_ci *
66762306a36Sopenharmony_ci * Unregister handler for events sent to the ACPI-WMI mapper device.
66862306a36Sopenharmony_ci *
66962306a36Sopenharmony_ci * Return: acpi_status signaling success or error.
67062306a36Sopenharmony_ci */
67162306a36Sopenharmony_ciacpi_status wmi_remove_notify_handler(const char *guid)
67262306a36Sopenharmony_ci{
67362306a36Sopenharmony_ci	struct wmi_block *block;
67462306a36Sopenharmony_ci	acpi_status status = AE_NOT_EXIST;
67562306a36Sopenharmony_ci	guid_t guid_input;
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	if (!guid)
67862306a36Sopenharmony_ci		return AE_BAD_PARAMETER;
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	if (guid_parse(guid, &guid_input))
68162306a36Sopenharmony_ci		return AE_BAD_PARAMETER;
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci	list_for_each_entry(block, &wmi_block_list, list) {
68462306a36Sopenharmony_ci		acpi_status wmi_status;
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci		if (guid_equal(&block->gblock.guid, &guid_input)) {
68762306a36Sopenharmony_ci			if (!block->handler ||
68862306a36Sopenharmony_ci			    block->handler == wmi_notify_debug)
68962306a36Sopenharmony_ci				return AE_NULL_ENTRY;
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci			if (debug_event) {
69262306a36Sopenharmony_ci				block->handler = wmi_notify_debug;
69362306a36Sopenharmony_ci				status = AE_OK;
69462306a36Sopenharmony_ci			} else {
69562306a36Sopenharmony_ci				wmi_status = wmi_method_enable(block, false);
69662306a36Sopenharmony_ci				block->handler = NULL;
69762306a36Sopenharmony_ci				block->handler_data = NULL;
69862306a36Sopenharmony_ci				if ((wmi_status != AE_OK) ||
69962306a36Sopenharmony_ci				    ((wmi_status == AE_OK) &&
70062306a36Sopenharmony_ci				     (status == AE_NOT_EXIST)))
70162306a36Sopenharmony_ci					status = wmi_status;
70262306a36Sopenharmony_ci			}
70362306a36Sopenharmony_ci		}
70462306a36Sopenharmony_ci	}
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	return status;
70762306a36Sopenharmony_ci}
70862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(wmi_remove_notify_handler);
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci/**
71162306a36Sopenharmony_ci * wmi_get_event_data - Get WMI data associated with an event (deprecated)
71262306a36Sopenharmony_ci *
71362306a36Sopenharmony_ci * @event: Event to find
71462306a36Sopenharmony_ci * @out: Buffer to hold event data
71562306a36Sopenharmony_ci *
71662306a36Sopenharmony_ci * Get extra data associated with an WMI event, the caller needs to free @out.
71762306a36Sopenharmony_ci *
71862306a36Sopenharmony_ci * Return: acpi_status signaling success or error.
71962306a36Sopenharmony_ci */
72062306a36Sopenharmony_ciacpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out)
72162306a36Sopenharmony_ci{
72262306a36Sopenharmony_ci	struct wmi_block *wblock;
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	list_for_each_entry(wblock, &wmi_block_list, list) {
72562306a36Sopenharmony_ci		struct guid_block *gblock = &wblock->gblock;
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci		if ((gblock->flags & ACPI_WMI_EVENT) && gblock->notify_id == event)
72862306a36Sopenharmony_ci			return get_event_data(wblock, out);
72962306a36Sopenharmony_ci	}
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	return AE_NOT_FOUND;
73262306a36Sopenharmony_ci}
73362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(wmi_get_event_data);
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci/**
73662306a36Sopenharmony_ci * wmi_has_guid - Check if a GUID is available
73762306a36Sopenharmony_ci * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
73862306a36Sopenharmony_ci *
73962306a36Sopenharmony_ci * Check if a given GUID is defined by _WDG.
74062306a36Sopenharmony_ci *
74162306a36Sopenharmony_ci * Return: True if GUID is available, false otherwise.
74262306a36Sopenharmony_ci */
74362306a36Sopenharmony_cibool wmi_has_guid(const char *guid_string)
74462306a36Sopenharmony_ci{
74562306a36Sopenharmony_ci	return ACPI_SUCCESS(find_guid(guid_string, NULL));
74662306a36Sopenharmony_ci}
74762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(wmi_has_guid);
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci/**
75062306a36Sopenharmony_ci * wmi_get_acpi_device_uid() - Get _UID name of ACPI device that defines GUID (deprecated)
75162306a36Sopenharmony_ci * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
75262306a36Sopenharmony_ci *
75362306a36Sopenharmony_ci * Find the _UID of ACPI device associated with this WMI GUID.
75462306a36Sopenharmony_ci *
75562306a36Sopenharmony_ci * Return: The ACPI _UID field value or NULL if the WMI GUID was not found.
75662306a36Sopenharmony_ci */
75762306a36Sopenharmony_cichar *wmi_get_acpi_device_uid(const char *guid_string)
75862306a36Sopenharmony_ci{
75962306a36Sopenharmony_ci	struct wmi_block *wblock = NULL;
76062306a36Sopenharmony_ci	acpi_status status;
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	status = find_guid(guid_string, &wblock);
76362306a36Sopenharmony_ci	if (ACPI_FAILURE(status))
76462306a36Sopenharmony_ci		return NULL;
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	return acpi_device_uid(wblock->acpi_device);
76762306a36Sopenharmony_ci}
76862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(wmi_get_acpi_device_uid);
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci#define dev_to_wblock(__dev)	container_of_const(__dev, struct wmi_block, dev.dev)
77162306a36Sopenharmony_ci#define dev_to_wdev(__dev)	container_of_const(__dev, struct wmi_device, dev)
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_cistatic inline struct wmi_driver *drv_to_wdrv(struct device_driver *drv)
77462306a36Sopenharmony_ci{
77562306a36Sopenharmony_ci	return container_of(drv, struct wmi_driver, driver);
77662306a36Sopenharmony_ci}
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci/*
77962306a36Sopenharmony_ci * sysfs interface
78062306a36Sopenharmony_ci */
78162306a36Sopenharmony_cistatic ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
78262306a36Sopenharmony_ci			     char *buf)
78362306a36Sopenharmony_ci{
78462306a36Sopenharmony_ci	struct wmi_block *wblock = dev_to_wblock(dev);
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci	return sysfs_emit(buf, "wmi:%pUL\n", &wblock->gblock.guid);
78762306a36Sopenharmony_ci}
78862306a36Sopenharmony_cistatic DEVICE_ATTR_RO(modalias);
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_cistatic ssize_t guid_show(struct device *dev, struct device_attribute *attr,
79162306a36Sopenharmony_ci			 char *buf)
79262306a36Sopenharmony_ci{
79362306a36Sopenharmony_ci	struct wmi_block *wblock = dev_to_wblock(dev);
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci	return sysfs_emit(buf, "%pUL\n", &wblock->gblock.guid);
79662306a36Sopenharmony_ci}
79762306a36Sopenharmony_cistatic DEVICE_ATTR_RO(guid);
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_cistatic ssize_t instance_count_show(struct device *dev,
80062306a36Sopenharmony_ci				   struct device_attribute *attr, char *buf)
80162306a36Sopenharmony_ci{
80262306a36Sopenharmony_ci	struct wmi_block *wblock = dev_to_wblock(dev);
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	return sysfs_emit(buf, "%d\n", (int)wblock->gblock.instance_count);
80562306a36Sopenharmony_ci}
80662306a36Sopenharmony_cistatic DEVICE_ATTR_RO(instance_count);
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_cistatic ssize_t expensive_show(struct device *dev,
80962306a36Sopenharmony_ci			      struct device_attribute *attr, char *buf)
81062306a36Sopenharmony_ci{
81162306a36Sopenharmony_ci	struct wmi_block *wblock = dev_to_wblock(dev);
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	return sysfs_emit(buf, "%d\n",
81462306a36Sopenharmony_ci			  (wblock->gblock.flags & ACPI_WMI_EXPENSIVE) != 0);
81562306a36Sopenharmony_ci}
81662306a36Sopenharmony_cistatic DEVICE_ATTR_RO(expensive);
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_cistatic struct attribute *wmi_attrs[] = {
81962306a36Sopenharmony_ci	&dev_attr_modalias.attr,
82062306a36Sopenharmony_ci	&dev_attr_guid.attr,
82162306a36Sopenharmony_ci	&dev_attr_instance_count.attr,
82262306a36Sopenharmony_ci	&dev_attr_expensive.attr,
82362306a36Sopenharmony_ci	NULL
82462306a36Sopenharmony_ci};
82562306a36Sopenharmony_ciATTRIBUTE_GROUPS(wmi);
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_cistatic ssize_t notify_id_show(struct device *dev, struct device_attribute *attr,
82862306a36Sopenharmony_ci			      char *buf)
82962306a36Sopenharmony_ci{
83062306a36Sopenharmony_ci	struct wmi_block *wblock = dev_to_wblock(dev);
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	return sysfs_emit(buf, "%02X\n", (unsigned int)wblock->gblock.notify_id);
83362306a36Sopenharmony_ci}
83462306a36Sopenharmony_cistatic DEVICE_ATTR_RO(notify_id);
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_cistatic struct attribute *wmi_event_attrs[] = {
83762306a36Sopenharmony_ci	&dev_attr_notify_id.attr,
83862306a36Sopenharmony_ci	NULL
83962306a36Sopenharmony_ci};
84062306a36Sopenharmony_ciATTRIBUTE_GROUPS(wmi_event);
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_cistatic ssize_t object_id_show(struct device *dev, struct device_attribute *attr,
84362306a36Sopenharmony_ci			      char *buf)
84462306a36Sopenharmony_ci{
84562306a36Sopenharmony_ci	struct wmi_block *wblock = dev_to_wblock(dev);
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci	return sysfs_emit(buf, "%c%c\n", wblock->gblock.object_id[0],
84862306a36Sopenharmony_ci			  wblock->gblock.object_id[1]);
84962306a36Sopenharmony_ci}
85062306a36Sopenharmony_cistatic DEVICE_ATTR_RO(object_id);
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_cistatic ssize_t setable_show(struct device *dev, struct device_attribute *attr,
85362306a36Sopenharmony_ci			    char *buf)
85462306a36Sopenharmony_ci{
85562306a36Sopenharmony_ci	struct wmi_device *wdev = dev_to_wdev(dev);
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci	return sysfs_emit(buf, "%d\n", (int)wdev->setable);
85862306a36Sopenharmony_ci}
85962306a36Sopenharmony_cistatic DEVICE_ATTR_RO(setable);
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_cistatic struct attribute *wmi_data_attrs[] = {
86262306a36Sopenharmony_ci	&dev_attr_object_id.attr,
86362306a36Sopenharmony_ci	&dev_attr_setable.attr,
86462306a36Sopenharmony_ci	NULL
86562306a36Sopenharmony_ci};
86662306a36Sopenharmony_ciATTRIBUTE_GROUPS(wmi_data);
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_cistatic struct attribute *wmi_method_attrs[] = {
86962306a36Sopenharmony_ci	&dev_attr_object_id.attr,
87062306a36Sopenharmony_ci	NULL
87162306a36Sopenharmony_ci};
87262306a36Sopenharmony_ciATTRIBUTE_GROUPS(wmi_method);
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_cistatic int wmi_dev_uevent(const struct device *dev, struct kobj_uevent_env *env)
87562306a36Sopenharmony_ci{
87662306a36Sopenharmony_ci	const struct wmi_block *wblock = dev_to_wblock(dev);
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	if (add_uevent_var(env, "MODALIAS=wmi:%pUL", &wblock->gblock.guid))
87962306a36Sopenharmony_ci		return -ENOMEM;
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci	if (add_uevent_var(env, "WMI_GUID=%pUL", &wblock->gblock.guid))
88262306a36Sopenharmony_ci		return -ENOMEM;
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci	return 0;
88562306a36Sopenharmony_ci}
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_cistatic void wmi_dev_release(struct device *dev)
88862306a36Sopenharmony_ci{
88962306a36Sopenharmony_ci	struct wmi_block *wblock = dev_to_wblock(dev);
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci	kfree(wblock);
89262306a36Sopenharmony_ci}
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_cistatic int wmi_dev_match(struct device *dev, struct device_driver *driver)
89562306a36Sopenharmony_ci{
89662306a36Sopenharmony_ci	struct wmi_driver *wmi_driver = drv_to_wdrv(driver);
89762306a36Sopenharmony_ci	struct wmi_block *wblock = dev_to_wblock(dev);
89862306a36Sopenharmony_ci	const struct wmi_device_id *id = wmi_driver->id_table;
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	if (id == NULL)
90162306a36Sopenharmony_ci		return 0;
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci	while (*id->guid_string) {
90462306a36Sopenharmony_ci		if (guid_parse_and_compare(id->guid_string, &wblock->gblock.guid))
90562306a36Sopenharmony_ci			return 1;
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci		id++;
90862306a36Sopenharmony_ci	}
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	return 0;
91162306a36Sopenharmony_ci}
91262306a36Sopenharmony_cistatic int wmi_char_open(struct inode *inode, struct file *filp)
91362306a36Sopenharmony_ci{
91462306a36Sopenharmony_ci	/*
91562306a36Sopenharmony_ci	 * The miscdevice already stores a pointer to itself
91662306a36Sopenharmony_ci	 * inside filp->private_data
91762306a36Sopenharmony_ci	 */
91862306a36Sopenharmony_ci	struct wmi_block *wblock = container_of(filp->private_data, struct wmi_block, char_dev);
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci	filp->private_data = wblock;
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci	return nonseekable_open(inode, filp);
92362306a36Sopenharmony_ci}
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_cistatic ssize_t wmi_char_read(struct file *filp, char __user *buffer,
92662306a36Sopenharmony_ci			     size_t length, loff_t *offset)
92762306a36Sopenharmony_ci{
92862306a36Sopenharmony_ci	struct wmi_block *wblock = filp->private_data;
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	return simple_read_from_buffer(buffer, length, offset,
93162306a36Sopenharmony_ci				       &wblock->req_buf_size,
93262306a36Sopenharmony_ci				       sizeof(wblock->req_buf_size));
93362306a36Sopenharmony_ci}
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_cistatic long wmi_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
93662306a36Sopenharmony_ci{
93762306a36Sopenharmony_ci	struct wmi_ioctl_buffer __user *input =
93862306a36Sopenharmony_ci		(struct wmi_ioctl_buffer __user *) arg;
93962306a36Sopenharmony_ci	struct wmi_block *wblock = filp->private_data;
94062306a36Sopenharmony_ci	struct wmi_ioctl_buffer *buf;
94162306a36Sopenharmony_ci	struct wmi_driver *wdriver;
94262306a36Sopenharmony_ci	int ret;
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_ci	if (_IOC_TYPE(cmd) != WMI_IOC)
94562306a36Sopenharmony_ci		return -ENOTTY;
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci	/* make sure we're not calling a higher instance than exists*/
94862306a36Sopenharmony_ci	if (_IOC_NR(cmd) >= wblock->gblock.instance_count)
94962306a36Sopenharmony_ci		return -EINVAL;
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci	mutex_lock(&wblock->char_mutex);
95262306a36Sopenharmony_ci	buf = wblock->handler_data;
95362306a36Sopenharmony_ci	if (get_user(buf->length, &input->length)) {
95462306a36Sopenharmony_ci		dev_dbg(&wblock->dev.dev, "Read length from user failed\n");
95562306a36Sopenharmony_ci		ret = -EFAULT;
95662306a36Sopenharmony_ci		goto out_ioctl;
95762306a36Sopenharmony_ci	}
95862306a36Sopenharmony_ci	/* if it's too small, abort */
95962306a36Sopenharmony_ci	if (buf->length < wblock->req_buf_size) {
96062306a36Sopenharmony_ci		dev_err(&wblock->dev.dev,
96162306a36Sopenharmony_ci			"Buffer %lld too small, need at least %lld\n",
96262306a36Sopenharmony_ci			buf->length, wblock->req_buf_size);
96362306a36Sopenharmony_ci		ret = -EINVAL;
96462306a36Sopenharmony_ci		goto out_ioctl;
96562306a36Sopenharmony_ci	}
96662306a36Sopenharmony_ci	/* if it's too big, warn, driver will only use what is needed */
96762306a36Sopenharmony_ci	if (buf->length > wblock->req_buf_size)
96862306a36Sopenharmony_ci		dev_warn(&wblock->dev.dev,
96962306a36Sopenharmony_ci			"Buffer %lld is bigger than required %lld\n",
97062306a36Sopenharmony_ci			buf->length, wblock->req_buf_size);
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci	/* copy the structure from userspace */
97362306a36Sopenharmony_ci	if (copy_from_user(buf, input, wblock->req_buf_size)) {
97462306a36Sopenharmony_ci		dev_dbg(&wblock->dev.dev, "Copy %llu from user failed\n",
97562306a36Sopenharmony_ci			wblock->req_buf_size);
97662306a36Sopenharmony_ci		ret = -EFAULT;
97762306a36Sopenharmony_ci		goto out_ioctl;
97862306a36Sopenharmony_ci	}
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci	/* let the driver do any filtering and do the call */
98162306a36Sopenharmony_ci	wdriver = drv_to_wdrv(wblock->dev.dev.driver);
98262306a36Sopenharmony_ci	if (!try_module_get(wdriver->driver.owner)) {
98362306a36Sopenharmony_ci		ret = -EBUSY;
98462306a36Sopenharmony_ci		goto out_ioctl;
98562306a36Sopenharmony_ci	}
98662306a36Sopenharmony_ci	ret = wdriver->filter_callback(&wblock->dev, cmd, buf);
98762306a36Sopenharmony_ci	module_put(wdriver->driver.owner);
98862306a36Sopenharmony_ci	if (ret)
98962306a36Sopenharmony_ci		goto out_ioctl;
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci	/* return the result (only up to our internal buffer size) */
99262306a36Sopenharmony_ci	if (copy_to_user(input, buf, wblock->req_buf_size)) {
99362306a36Sopenharmony_ci		dev_dbg(&wblock->dev.dev, "Copy %llu to user failed\n",
99462306a36Sopenharmony_ci			wblock->req_buf_size);
99562306a36Sopenharmony_ci		ret = -EFAULT;
99662306a36Sopenharmony_ci	}
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ciout_ioctl:
99962306a36Sopenharmony_ci	mutex_unlock(&wblock->char_mutex);
100062306a36Sopenharmony_ci	return ret;
100162306a36Sopenharmony_ci}
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_cistatic const struct file_operations wmi_fops = {
100462306a36Sopenharmony_ci	.owner		= THIS_MODULE,
100562306a36Sopenharmony_ci	.read		= wmi_char_read,
100662306a36Sopenharmony_ci	.open		= wmi_char_open,
100762306a36Sopenharmony_ci	.unlocked_ioctl	= wmi_ioctl,
100862306a36Sopenharmony_ci	.compat_ioctl	= compat_ptr_ioctl,
100962306a36Sopenharmony_ci};
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_cistatic int wmi_dev_probe(struct device *dev)
101262306a36Sopenharmony_ci{
101362306a36Sopenharmony_ci	struct wmi_block *wblock = dev_to_wblock(dev);
101462306a36Sopenharmony_ci	struct wmi_driver *wdriver = drv_to_wdrv(dev->driver);
101562306a36Sopenharmony_ci	int ret = 0;
101662306a36Sopenharmony_ci	char *buf;
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci	if (ACPI_FAILURE(wmi_method_enable(wblock, true)))
101962306a36Sopenharmony_ci		dev_warn(dev, "failed to enable device -- probing anyway\n");
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci	if (wdriver->probe) {
102262306a36Sopenharmony_ci		ret = wdriver->probe(dev_to_wdev(dev),
102362306a36Sopenharmony_ci				find_guid_context(wblock, wdriver));
102462306a36Sopenharmony_ci		if (ret != 0)
102562306a36Sopenharmony_ci			goto probe_failure;
102662306a36Sopenharmony_ci	}
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	/* driver wants a character device made */
102962306a36Sopenharmony_ci	if (wdriver->filter_callback) {
103062306a36Sopenharmony_ci		/* check that required buffer size declared by driver or MOF */
103162306a36Sopenharmony_ci		if (!wblock->req_buf_size) {
103262306a36Sopenharmony_ci			dev_err(&wblock->dev.dev,
103362306a36Sopenharmony_ci				"Required buffer size not set\n");
103462306a36Sopenharmony_ci			ret = -EINVAL;
103562306a36Sopenharmony_ci			goto probe_failure;
103662306a36Sopenharmony_ci		}
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci		wblock->handler_data = kmalloc(wblock->req_buf_size,
103962306a36Sopenharmony_ci					       GFP_KERNEL);
104062306a36Sopenharmony_ci		if (!wblock->handler_data) {
104162306a36Sopenharmony_ci			ret = -ENOMEM;
104262306a36Sopenharmony_ci			goto probe_failure;
104362306a36Sopenharmony_ci		}
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_ci		buf = kasprintf(GFP_KERNEL, "wmi/%s", wdriver->driver.name);
104662306a36Sopenharmony_ci		if (!buf) {
104762306a36Sopenharmony_ci			ret = -ENOMEM;
104862306a36Sopenharmony_ci			goto probe_string_failure;
104962306a36Sopenharmony_ci		}
105062306a36Sopenharmony_ci		wblock->char_dev.minor = MISC_DYNAMIC_MINOR;
105162306a36Sopenharmony_ci		wblock->char_dev.name = buf;
105262306a36Sopenharmony_ci		wblock->char_dev.fops = &wmi_fops;
105362306a36Sopenharmony_ci		wblock->char_dev.mode = 0444;
105462306a36Sopenharmony_ci		ret = misc_register(&wblock->char_dev);
105562306a36Sopenharmony_ci		if (ret) {
105662306a36Sopenharmony_ci			dev_warn(dev, "failed to register char dev: %d\n", ret);
105762306a36Sopenharmony_ci			ret = -ENOMEM;
105862306a36Sopenharmony_ci			goto probe_misc_failure;
105962306a36Sopenharmony_ci		}
106062306a36Sopenharmony_ci	}
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_ci	set_bit(WMI_PROBED, &wblock->flags);
106362306a36Sopenharmony_ci	return 0;
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_ciprobe_misc_failure:
106662306a36Sopenharmony_ci	kfree(buf);
106762306a36Sopenharmony_ciprobe_string_failure:
106862306a36Sopenharmony_ci	kfree(wblock->handler_data);
106962306a36Sopenharmony_ciprobe_failure:
107062306a36Sopenharmony_ci	if (ACPI_FAILURE(wmi_method_enable(wblock, false)))
107162306a36Sopenharmony_ci		dev_warn(dev, "failed to disable device\n");
107262306a36Sopenharmony_ci	return ret;
107362306a36Sopenharmony_ci}
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_cistatic void wmi_dev_remove(struct device *dev)
107662306a36Sopenharmony_ci{
107762306a36Sopenharmony_ci	struct wmi_block *wblock = dev_to_wblock(dev);
107862306a36Sopenharmony_ci	struct wmi_driver *wdriver = drv_to_wdrv(dev->driver);
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ci	clear_bit(WMI_PROBED, &wblock->flags);
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_ci	if (wdriver->filter_callback) {
108362306a36Sopenharmony_ci		misc_deregister(&wblock->char_dev);
108462306a36Sopenharmony_ci		kfree(wblock->char_dev.name);
108562306a36Sopenharmony_ci		kfree(wblock->handler_data);
108662306a36Sopenharmony_ci	}
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	if (wdriver->remove)
108962306a36Sopenharmony_ci		wdriver->remove(dev_to_wdev(dev));
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci	if (ACPI_FAILURE(wmi_method_enable(wblock, false)))
109262306a36Sopenharmony_ci		dev_warn(dev, "failed to disable device\n");
109362306a36Sopenharmony_ci}
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_cistatic struct class wmi_bus_class = {
109662306a36Sopenharmony_ci	.name = "wmi_bus",
109762306a36Sopenharmony_ci};
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_cistatic struct bus_type wmi_bus_type = {
110062306a36Sopenharmony_ci	.name = "wmi",
110162306a36Sopenharmony_ci	.dev_groups = wmi_groups,
110262306a36Sopenharmony_ci	.match = wmi_dev_match,
110362306a36Sopenharmony_ci	.uevent = wmi_dev_uevent,
110462306a36Sopenharmony_ci	.probe = wmi_dev_probe,
110562306a36Sopenharmony_ci	.remove = wmi_dev_remove,
110662306a36Sopenharmony_ci};
110762306a36Sopenharmony_ci
110862306a36Sopenharmony_cistatic const struct device_type wmi_type_event = {
110962306a36Sopenharmony_ci	.name = "event",
111062306a36Sopenharmony_ci	.groups = wmi_event_groups,
111162306a36Sopenharmony_ci	.release = wmi_dev_release,
111262306a36Sopenharmony_ci};
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_cistatic const struct device_type wmi_type_method = {
111562306a36Sopenharmony_ci	.name = "method",
111662306a36Sopenharmony_ci	.groups = wmi_method_groups,
111762306a36Sopenharmony_ci	.release = wmi_dev_release,
111862306a36Sopenharmony_ci};
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_cistatic const struct device_type wmi_type_data = {
112162306a36Sopenharmony_ci	.name = "data",
112262306a36Sopenharmony_ci	.groups = wmi_data_groups,
112362306a36Sopenharmony_ci	.release = wmi_dev_release,
112462306a36Sopenharmony_ci};
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci/*
112762306a36Sopenharmony_ci * _WDG is a static list that is only parsed at startup,
112862306a36Sopenharmony_ci * so it's safe to count entries without extra protection.
112962306a36Sopenharmony_ci */
113062306a36Sopenharmony_cistatic int guid_count(const guid_t *guid)
113162306a36Sopenharmony_ci{
113262306a36Sopenharmony_ci	struct wmi_block *wblock;
113362306a36Sopenharmony_ci	int count = 0;
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci	list_for_each_entry(wblock, &wmi_block_list, list) {
113662306a36Sopenharmony_ci		if (guid_equal(&wblock->gblock.guid, guid))
113762306a36Sopenharmony_ci			count++;
113862306a36Sopenharmony_ci	}
113962306a36Sopenharmony_ci
114062306a36Sopenharmony_ci	return count;
114162306a36Sopenharmony_ci}
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_cistatic int wmi_create_device(struct device *wmi_bus_dev,
114462306a36Sopenharmony_ci			     struct wmi_block *wblock,
114562306a36Sopenharmony_ci			     struct acpi_device *device)
114662306a36Sopenharmony_ci{
114762306a36Sopenharmony_ci	struct acpi_device_info *info;
114862306a36Sopenharmony_ci	char method[WMI_ACPI_METHOD_NAME_SIZE];
114962306a36Sopenharmony_ci	int result;
115062306a36Sopenharmony_ci	uint count;
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_ci	if (wblock->gblock.flags & ACPI_WMI_EVENT) {
115362306a36Sopenharmony_ci		wblock->dev.dev.type = &wmi_type_event;
115462306a36Sopenharmony_ci		goto out_init;
115562306a36Sopenharmony_ci	}
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci	if (wblock->gblock.flags & ACPI_WMI_METHOD) {
115862306a36Sopenharmony_ci		wblock->dev.dev.type = &wmi_type_method;
115962306a36Sopenharmony_ci		mutex_init(&wblock->char_mutex);
116062306a36Sopenharmony_ci		goto out_init;
116162306a36Sopenharmony_ci	}
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci	/*
116462306a36Sopenharmony_ci	 * Data Block Query Control Method (WQxx by convention) is
116562306a36Sopenharmony_ci	 * required per the WMI documentation. If it is not present,
116662306a36Sopenharmony_ci	 * we ignore this data block.
116762306a36Sopenharmony_ci	 */
116862306a36Sopenharmony_ci	get_acpi_method_name(wblock, 'Q', method);
116962306a36Sopenharmony_ci	result = get_subobj_info(device->handle, method, &info);
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_ci	if (result) {
117262306a36Sopenharmony_ci		dev_warn(wmi_bus_dev,
117362306a36Sopenharmony_ci			 "%s data block query control method not found\n",
117462306a36Sopenharmony_ci			 method);
117562306a36Sopenharmony_ci		return result;
117662306a36Sopenharmony_ci	}
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ci	wblock->dev.dev.type = &wmi_type_data;
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_ci	/*
118162306a36Sopenharmony_ci	 * The Microsoft documentation specifically states:
118262306a36Sopenharmony_ci	 *
118362306a36Sopenharmony_ci	 *   Data blocks registered with only a single instance
118462306a36Sopenharmony_ci	 *   can ignore the parameter.
118562306a36Sopenharmony_ci	 *
118662306a36Sopenharmony_ci	 * ACPICA will get mad at us if we call the method with the wrong number
118762306a36Sopenharmony_ci	 * of arguments, so check what our method expects.  (On some Dell
118862306a36Sopenharmony_ci	 * laptops, WQxx may not be a method at all.)
118962306a36Sopenharmony_ci	 */
119062306a36Sopenharmony_ci	if (info->type != ACPI_TYPE_METHOD || info->param_count == 0)
119162306a36Sopenharmony_ci		set_bit(WMI_READ_TAKES_NO_ARGS, &wblock->flags);
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci	kfree(info);
119462306a36Sopenharmony_ci
119562306a36Sopenharmony_ci	get_acpi_method_name(wblock, 'S', method);
119662306a36Sopenharmony_ci	result = get_subobj_info(device->handle, method, NULL);
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_ci	if (result == 0)
119962306a36Sopenharmony_ci		wblock->dev.setable = true;
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci out_init:
120262306a36Sopenharmony_ci	wblock->dev.dev.bus = &wmi_bus_type;
120362306a36Sopenharmony_ci	wblock->dev.dev.parent = wmi_bus_dev;
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_ci	count = guid_count(&wblock->gblock.guid);
120662306a36Sopenharmony_ci	if (count)
120762306a36Sopenharmony_ci		dev_set_name(&wblock->dev.dev, "%pUL-%d", &wblock->gblock.guid, count);
120862306a36Sopenharmony_ci	else
120962306a36Sopenharmony_ci		dev_set_name(&wblock->dev.dev, "%pUL", &wblock->gblock.guid);
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci	device_initialize(&wblock->dev.dev);
121262306a36Sopenharmony_ci
121362306a36Sopenharmony_ci	return 0;
121462306a36Sopenharmony_ci}
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_cistatic void wmi_free_devices(struct acpi_device *device)
121762306a36Sopenharmony_ci{
121862306a36Sopenharmony_ci	struct wmi_block *wblock, *next;
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci	/* Delete devices for all the GUIDs */
122162306a36Sopenharmony_ci	list_for_each_entry_safe(wblock, next, &wmi_block_list, list) {
122262306a36Sopenharmony_ci		if (wblock->acpi_device == device) {
122362306a36Sopenharmony_ci			list_del(&wblock->list);
122462306a36Sopenharmony_ci			device_unregister(&wblock->dev.dev);
122562306a36Sopenharmony_ci		}
122662306a36Sopenharmony_ci	}
122762306a36Sopenharmony_ci}
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_cistatic bool guid_already_parsed_for_legacy(struct acpi_device *device, const guid_t *guid)
123062306a36Sopenharmony_ci{
123162306a36Sopenharmony_ci	struct wmi_block *wblock;
123262306a36Sopenharmony_ci
123362306a36Sopenharmony_ci	list_for_each_entry(wblock, &wmi_block_list, list) {
123462306a36Sopenharmony_ci		/* skip warning and register if we know the driver will use struct wmi_driver */
123562306a36Sopenharmony_ci		for (int i = 0; allow_duplicates[i] != NULL; i++) {
123662306a36Sopenharmony_ci			if (guid_parse_and_compare(allow_duplicates[i], guid))
123762306a36Sopenharmony_ci				return false;
123862306a36Sopenharmony_ci		}
123962306a36Sopenharmony_ci		if (guid_equal(&wblock->gblock.guid, guid)) {
124062306a36Sopenharmony_ci			/*
124162306a36Sopenharmony_ci			 * Because we historically didn't track the relationship
124262306a36Sopenharmony_ci			 * between GUIDs and ACPI nodes, we don't know whether
124362306a36Sopenharmony_ci			 * we need to suppress GUIDs that are unique on a
124462306a36Sopenharmony_ci			 * given node but duplicated across nodes.
124562306a36Sopenharmony_ci			 */
124662306a36Sopenharmony_ci			dev_warn(&device->dev, "duplicate WMI GUID %pUL (first instance was on %s)\n",
124762306a36Sopenharmony_ci				 guid, dev_name(&wblock->acpi_device->dev));
124862306a36Sopenharmony_ci			return true;
124962306a36Sopenharmony_ci		}
125062306a36Sopenharmony_ci	}
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_ci	return false;
125362306a36Sopenharmony_ci}
125462306a36Sopenharmony_ci
125562306a36Sopenharmony_ci/*
125662306a36Sopenharmony_ci * Parse the _WDG method for the GUID data blocks
125762306a36Sopenharmony_ci */
125862306a36Sopenharmony_cistatic int parse_wdg(struct device *wmi_bus_dev, struct acpi_device *device)
125962306a36Sopenharmony_ci{
126062306a36Sopenharmony_ci	struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
126162306a36Sopenharmony_ci	const struct guid_block *gblock;
126262306a36Sopenharmony_ci	struct wmi_block *wblock, *next;
126362306a36Sopenharmony_ci	union acpi_object *obj;
126462306a36Sopenharmony_ci	acpi_status status;
126562306a36Sopenharmony_ci	u32 i, total;
126662306a36Sopenharmony_ci	int retval;
126762306a36Sopenharmony_ci
126862306a36Sopenharmony_ci	status = acpi_evaluate_object(device->handle, "_WDG", NULL, &out);
126962306a36Sopenharmony_ci	if (ACPI_FAILURE(status))
127062306a36Sopenharmony_ci		return -ENXIO;
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_ci	obj = out.pointer;
127362306a36Sopenharmony_ci	if (!obj)
127462306a36Sopenharmony_ci		return -ENXIO;
127562306a36Sopenharmony_ci
127662306a36Sopenharmony_ci	if (obj->type != ACPI_TYPE_BUFFER) {
127762306a36Sopenharmony_ci		kfree(obj);
127862306a36Sopenharmony_ci		return -ENXIO;
127962306a36Sopenharmony_ci	}
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_ci	gblock = (const struct guid_block *)obj->buffer.pointer;
128262306a36Sopenharmony_ci	total = obj->buffer.length / sizeof(struct guid_block);
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci	for (i = 0; i < total; i++) {
128562306a36Sopenharmony_ci		if (debug_dump_wdg)
128662306a36Sopenharmony_ci			wmi_dump_wdg(&gblock[i]);
128762306a36Sopenharmony_ci
128862306a36Sopenharmony_ci		if (!gblock[i].instance_count) {
128962306a36Sopenharmony_ci			dev_info(wmi_bus_dev, FW_INFO "%pUL has zero instances\n", &gblock[i].guid);
129062306a36Sopenharmony_ci			continue;
129162306a36Sopenharmony_ci		}
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_ci		if (guid_already_parsed_for_legacy(device, &gblock[i].guid))
129462306a36Sopenharmony_ci			continue;
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_ci		wblock = kzalloc(sizeof(*wblock), GFP_KERNEL);
129762306a36Sopenharmony_ci		if (!wblock) {
129862306a36Sopenharmony_ci			dev_err(wmi_bus_dev, "Failed to allocate %pUL\n", &gblock[i].guid);
129962306a36Sopenharmony_ci			continue;
130062306a36Sopenharmony_ci		}
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_ci		wblock->acpi_device = device;
130362306a36Sopenharmony_ci		wblock->gblock = gblock[i];
130462306a36Sopenharmony_ci
130562306a36Sopenharmony_ci		retval = wmi_create_device(wmi_bus_dev, wblock, device);
130662306a36Sopenharmony_ci		if (retval) {
130762306a36Sopenharmony_ci			kfree(wblock);
130862306a36Sopenharmony_ci			continue;
130962306a36Sopenharmony_ci		}
131062306a36Sopenharmony_ci
131162306a36Sopenharmony_ci		list_add_tail(&wblock->list, &wmi_block_list);
131262306a36Sopenharmony_ci
131362306a36Sopenharmony_ci		if (debug_event) {
131462306a36Sopenharmony_ci			wblock->handler = wmi_notify_debug;
131562306a36Sopenharmony_ci			wmi_method_enable(wblock, true);
131662306a36Sopenharmony_ci		}
131762306a36Sopenharmony_ci	}
131862306a36Sopenharmony_ci
131962306a36Sopenharmony_ci	/*
132062306a36Sopenharmony_ci	 * Now that all of the devices are created, add them to the
132162306a36Sopenharmony_ci	 * device tree and probe subdrivers.
132262306a36Sopenharmony_ci	 */
132362306a36Sopenharmony_ci	list_for_each_entry_safe(wblock, next, &wmi_block_list, list) {
132462306a36Sopenharmony_ci		if (wblock->acpi_device != device)
132562306a36Sopenharmony_ci			continue;
132662306a36Sopenharmony_ci
132762306a36Sopenharmony_ci		retval = device_add(&wblock->dev.dev);
132862306a36Sopenharmony_ci		if (retval) {
132962306a36Sopenharmony_ci			dev_err(wmi_bus_dev, "failed to register %pUL\n",
133062306a36Sopenharmony_ci				&wblock->gblock.guid);
133162306a36Sopenharmony_ci			if (debug_event)
133262306a36Sopenharmony_ci				wmi_method_enable(wblock, false);
133362306a36Sopenharmony_ci			list_del(&wblock->list);
133462306a36Sopenharmony_ci			put_device(&wblock->dev.dev);
133562306a36Sopenharmony_ci		}
133662306a36Sopenharmony_ci	}
133762306a36Sopenharmony_ci
133862306a36Sopenharmony_ci	kfree(obj);
133962306a36Sopenharmony_ci
134062306a36Sopenharmony_ci	return 0;
134162306a36Sopenharmony_ci}
134262306a36Sopenharmony_ci
134362306a36Sopenharmony_ci/*
134462306a36Sopenharmony_ci * WMI can have EmbeddedControl access regions. In which case, we just want to
134562306a36Sopenharmony_ci * hand these off to the EC driver.
134662306a36Sopenharmony_ci */
134762306a36Sopenharmony_cistatic acpi_status
134862306a36Sopenharmony_ciacpi_wmi_ec_space_handler(u32 function, acpi_physical_address address,
134962306a36Sopenharmony_ci			  u32 bits, u64 *value,
135062306a36Sopenharmony_ci			  void *handler_context, void *region_context)
135162306a36Sopenharmony_ci{
135262306a36Sopenharmony_ci	int result = 0, i = 0;
135362306a36Sopenharmony_ci	u8 temp = 0;
135462306a36Sopenharmony_ci
135562306a36Sopenharmony_ci	if ((address > 0xFF) || !value)
135662306a36Sopenharmony_ci		return AE_BAD_PARAMETER;
135762306a36Sopenharmony_ci
135862306a36Sopenharmony_ci	if (function != ACPI_READ && function != ACPI_WRITE)
135962306a36Sopenharmony_ci		return AE_BAD_PARAMETER;
136062306a36Sopenharmony_ci
136162306a36Sopenharmony_ci	if (bits != 8)
136262306a36Sopenharmony_ci		return AE_BAD_PARAMETER;
136362306a36Sopenharmony_ci
136462306a36Sopenharmony_ci	if (function == ACPI_READ) {
136562306a36Sopenharmony_ci		result = ec_read(address, &temp);
136662306a36Sopenharmony_ci		(*value) |= ((u64)temp) << i;
136762306a36Sopenharmony_ci	} else {
136862306a36Sopenharmony_ci		temp = 0xff & ((*value) >> i);
136962306a36Sopenharmony_ci		result = ec_write(address, temp);
137062306a36Sopenharmony_ci	}
137162306a36Sopenharmony_ci
137262306a36Sopenharmony_ci	switch (result) {
137362306a36Sopenharmony_ci	case -EINVAL:
137462306a36Sopenharmony_ci		return AE_BAD_PARAMETER;
137562306a36Sopenharmony_ci	case -ENODEV:
137662306a36Sopenharmony_ci		return AE_NOT_FOUND;
137762306a36Sopenharmony_ci	case -ETIME:
137862306a36Sopenharmony_ci		return AE_TIME;
137962306a36Sopenharmony_ci	default:
138062306a36Sopenharmony_ci		return AE_OK;
138162306a36Sopenharmony_ci	}
138262306a36Sopenharmony_ci}
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_cistatic void acpi_wmi_notify_handler(acpi_handle handle, u32 event,
138562306a36Sopenharmony_ci				    void *context)
138662306a36Sopenharmony_ci{
138762306a36Sopenharmony_ci	struct wmi_block *wblock = NULL, *iter;
138862306a36Sopenharmony_ci
138962306a36Sopenharmony_ci	list_for_each_entry(iter, &wmi_block_list, list) {
139062306a36Sopenharmony_ci		struct guid_block *block = &iter->gblock;
139162306a36Sopenharmony_ci
139262306a36Sopenharmony_ci		if (iter->acpi_device->handle == handle &&
139362306a36Sopenharmony_ci		    (block->flags & ACPI_WMI_EVENT) &&
139462306a36Sopenharmony_ci		    (block->notify_id == event)) {
139562306a36Sopenharmony_ci			wblock = iter;
139662306a36Sopenharmony_ci			break;
139762306a36Sopenharmony_ci		}
139862306a36Sopenharmony_ci	}
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_ci	if (!wblock)
140162306a36Sopenharmony_ci		return;
140262306a36Sopenharmony_ci
140362306a36Sopenharmony_ci	/* If a driver is bound, then notify the driver. */
140462306a36Sopenharmony_ci	if (test_bit(WMI_PROBED, &wblock->flags) && wblock->dev.dev.driver) {
140562306a36Sopenharmony_ci		struct wmi_driver *driver = drv_to_wdrv(wblock->dev.dev.driver);
140662306a36Sopenharmony_ci		struct acpi_buffer evdata = { ACPI_ALLOCATE_BUFFER, NULL };
140762306a36Sopenharmony_ci		acpi_status status;
140862306a36Sopenharmony_ci
140962306a36Sopenharmony_ci		if (!driver->no_notify_data) {
141062306a36Sopenharmony_ci			status = get_event_data(wblock, &evdata);
141162306a36Sopenharmony_ci			if (ACPI_FAILURE(status)) {
141262306a36Sopenharmony_ci				dev_warn(&wblock->dev.dev, "failed to get event data\n");
141362306a36Sopenharmony_ci				return;
141462306a36Sopenharmony_ci			}
141562306a36Sopenharmony_ci		}
141662306a36Sopenharmony_ci
141762306a36Sopenharmony_ci		if (driver->notify)
141862306a36Sopenharmony_ci			driver->notify(&wblock->dev, evdata.pointer);
141962306a36Sopenharmony_ci
142062306a36Sopenharmony_ci		kfree(evdata.pointer);
142162306a36Sopenharmony_ci	} else if (wblock->handler) {
142262306a36Sopenharmony_ci		/* Legacy handler */
142362306a36Sopenharmony_ci		wblock->handler(event, wblock->handler_data);
142462306a36Sopenharmony_ci	}
142562306a36Sopenharmony_ci
142662306a36Sopenharmony_ci	if (debug_event)
142762306a36Sopenharmony_ci		pr_info("DEBUG: GUID %pUL event 0x%02X\n", &wblock->gblock.guid, event);
142862306a36Sopenharmony_ci
142962306a36Sopenharmony_ci	acpi_bus_generate_netlink_event(
143062306a36Sopenharmony_ci		wblock->acpi_device->pnp.device_class,
143162306a36Sopenharmony_ci		dev_name(&wblock->dev.dev),
143262306a36Sopenharmony_ci		event, 0);
143362306a36Sopenharmony_ci}
143462306a36Sopenharmony_ci
143562306a36Sopenharmony_cistatic void acpi_wmi_remove(struct platform_device *device)
143662306a36Sopenharmony_ci{
143762306a36Sopenharmony_ci	struct acpi_device *acpi_device = ACPI_COMPANION(&device->dev);
143862306a36Sopenharmony_ci
143962306a36Sopenharmony_ci	acpi_remove_notify_handler(acpi_device->handle, ACPI_ALL_NOTIFY,
144062306a36Sopenharmony_ci				   acpi_wmi_notify_handler);
144162306a36Sopenharmony_ci	acpi_remove_address_space_handler(acpi_device->handle,
144262306a36Sopenharmony_ci				ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler);
144362306a36Sopenharmony_ci	wmi_free_devices(acpi_device);
144462306a36Sopenharmony_ci	device_unregister(dev_get_drvdata(&device->dev));
144562306a36Sopenharmony_ci}
144662306a36Sopenharmony_ci
144762306a36Sopenharmony_cistatic int acpi_wmi_probe(struct platform_device *device)
144862306a36Sopenharmony_ci{
144962306a36Sopenharmony_ci	struct acpi_device *acpi_device;
145062306a36Sopenharmony_ci	struct device *wmi_bus_dev;
145162306a36Sopenharmony_ci	acpi_status status;
145262306a36Sopenharmony_ci	int error;
145362306a36Sopenharmony_ci
145462306a36Sopenharmony_ci	acpi_device = ACPI_COMPANION(&device->dev);
145562306a36Sopenharmony_ci	if (!acpi_device) {
145662306a36Sopenharmony_ci		dev_err(&device->dev, "ACPI companion is missing\n");
145762306a36Sopenharmony_ci		return -ENODEV;
145862306a36Sopenharmony_ci	}
145962306a36Sopenharmony_ci
146062306a36Sopenharmony_ci	status = acpi_install_address_space_handler(acpi_device->handle,
146162306a36Sopenharmony_ci						    ACPI_ADR_SPACE_EC,
146262306a36Sopenharmony_ci						    &acpi_wmi_ec_space_handler,
146362306a36Sopenharmony_ci						    NULL, NULL);
146462306a36Sopenharmony_ci	if (ACPI_FAILURE(status)) {
146562306a36Sopenharmony_ci		dev_err(&device->dev, "Error installing EC region handler\n");
146662306a36Sopenharmony_ci		return -ENODEV;
146762306a36Sopenharmony_ci	}
146862306a36Sopenharmony_ci
146962306a36Sopenharmony_ci	status = acpi_install_notify_handler(acpi_device->handle,
147062306a36Sopenharmony_ci					     ACPI_ALL_NOTIFY,
147162306a36Sopenharmony_ci					     acpi_wmi_notify_handler,
147262306a36Sopenharmony_ci					     NULL);
147362306a36Sopenharmony_ci	if (ACPI_FAILURE(status)) {
147462306a36Sopenharmony_ci		dev_err(&device->dev, "Error installing notify handler\n");
147562306a36Sopenharmony_ci		error = -ENODEV;
147662306a36Sopenharmony_ci		goto err_remove_ec_handler;
147762306a36Sopenharmony_ci	}
147862306a36Sopenharmony_ci
147962306a36Sopenharmony_ci	wmi_bus_dev = device_create(&wmi_bus_class, &device->dev, MKDEV(0, 0),
148062306a36Sopenharmony_ci				    NULL, "wmi_bus-%s", dev_name(&device->dev));
148162306a36Sopenharmony_ci	if (IS_ERR(wmi_bus_dev)) {
148262306a36Sopenharmony_ci		error = PTR_ERR(wmi_bus_dev);
148362306a36Sopenharmony_ci		goto err_remove_notify_handler;
148462306a36Sopenharmony_ci	}
148562306a36Sopenharmony_ci	dev_set_drvdata(&device->dev, wmi_bus_dev);
148662306a36Sopenharmony_ci
148762306a36Sopenharmony_ci	error = parse_wdg(wmi_bus_dev, acpi_device);
148862306a36Sopenharmony_ci	if (error) {
148962306a36Sopenharmony_ci		pr_err("Failed to parse WDG method\n");
149062306a36Sopenharmony_ci		goto err_remove_busdev;
149162306a36Sopenharmony_ci	}
149262306a36Sopenharmony_ci
149362306a36Sopenharmony_ci	return 0;
149462306a36Sopenharmony_ci
149562306a36Sopenharmony_cierr_remove_busdev:
149662306a36Sopenharmony_ci	device_unregister(wmi_bus_dev);
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_cierr_remove_notify_handler:
149962306a36Sopenharmony_ci	acpi_remove_notify_handler(acpi_device->handle, ACPI_ALL_NOTIFY,
150062306a36Sopenharmony_ci				   acpi_wmi_notify_handler);
150162306a36Sopenharmony_ci
150262306a36Sopenharmony_cierr_remove_ec_handler:
150362306a36Sopenharmony_ci	acpi_remove_address_space_handler(acpi_device->handle,
150462306a36Sopenharmony_ci					  ACPI_ADR_SPACE_EC,
150562306a36Sopenharmony_ci					  &acpi_wmi_ec_space_handler);
150662306a36Sopenharmony_ci
150762306a36Sopenharmony_ci	return error;
150862306a36Sopenharmony_ci}
150962306a36Sopenharmony_ci
151062306a36Sopenharmony_ciint __must_check __wmi_driver_register(struct wmi_driver *driver,
151162306a36Sopenharmony_ci				       struct module *owner)
151262306a36Sopenharmony_ci{
151362306a36Sopenharmony_ci	driver->driver.owner = owner;
151462306a36Sopenharmony_ci	driver->driver.bus = &wmi_bus_type;
151562306a36Sopenharmony_ci
151662306a36Sopenharmony_ci	return driver_register(&driver->driver);
151762306a36Sopenharmony_ci}
151862306a36Sopenharmony_ciEXPORT_SYMBOL(__wmi_driver_register);
151962306a36Sopenharmony_ci
152062306a36Sopenharmony_ci/**
152162306a36Sopenharmony_ci * wmi_driver_unregister() - Unregister a WMI driver
152262306a36Sopenharmony_ci * @driver: WMI driver to unregister
152362306a36Sopenharmony_ci *
152462306a36Sopenharmony_ci * Unregisters a WMI driver from the WMI bus.
152562306a36Sopenharmony_ci */
152662306a36Sopenharmony_civoid wmi_driver_unregister(struct wmi_driver *driver)
152762306a36Sopenharmony_ci{
152862306a36Sopenharmony_ci	driver_unregister(&driver->driver);
152962306a36Sopenharmony_ci}
153062306a36Sopenharmony_ciEXPORT_SYMBOL(wmi_driver_unregister);
153162306a36Sopenharmony_ci
153262306a36Sopenharmony_cistatic struct platform_driver acpi_wmi_driver = {
153362306a36Sopenharmony_ci	.driver = {
153462306a36Sopenharmony_ci		.name = "acpi-wmi",
153562306a36Sopenharmony_ci		.acpi_match_table = wmi_device_ids,
153662306a36Sopenharmony_ci	},
153762306a36Sopenharmony_ci	.probe = acpi_wmi_probe,
153862306a36Sopenharmony_ci	.remove_new = acpi_wmi_remove,
153962306a36Sopenharmony_ci};
154062306a36Sopenharmony_ci
154162306a36Sopenharmony_cistatic int __init acpi_wmi_init(void)
154262306a36Sopenharmony_ci{
154362306a36Sopenharmony_ci	int error;
154462306a36Sopenharmony_ci
154562306a36Sopenharmony_ci	if (acpi_disabled)
154662306a36Sopenharmony_ci		return -ENODEV;
154762306a36Sopenharmony_ci
154862306a36Sopenharmony_ci	error = class_register(&wmi_bus_class);
154962306a36Sopenharmony_ci	if (error)
155062306a36Sopenharmony_ci		return error;
155162306a36Sopenharmony_ci
155262306a36Sopenharmony_ci	error = bus_register(&wmi_bus_type);
155362306a36Sopenharmony_ci	if (error)
155462306a36Sopenharmony_ci		goto err_unreg_class;
155562306a36Sopenharmony_ci
155662306a36Sopenharmony_ci	error = platform_driver_register(&acpi_wmi_driver);
155762306a36Sopenharmony_ci	if (error) {
155862306a36Sopenharmony_ci		pr_err("Error loading mapper\n");
155962306a36Sopenharmony_ci		goto err_unreg_bus;
156062306a36Sopenharmony_ci	}
156162306a36Sopenharmony_ci
156262306a36Sopenharmony_ci	return 0;
156362306a36Sopenharmony_ci
156462306a36Sopenharmony_cierr_unreg_bus:
156562306a36Sopenharmony_ci	bus_unregister(&wmi_bus_type);
156662306a36Sopenharmony_ci
156762306a36Sopenharmony_cierr_unreg_class:
156862306a36Sopenharmony_ci	class_unregister(&wmi_bus_class);
156962306a36Sopenharmony_ci
157062306a36Sopenharmony_ci	return error;
157162306a36Sopenharmony_ci}
157262306a36Sopenharmony_ci
157362306a36Sopenharmony_cistatic void __exit acpi_wmi_exit(void)
157462306a36Sopenharmony_ci{
157562306a36Sopenharmony_ci	platform_driver_unregister(&acpi_wmi_driver);
157662306a36Sopenharmony_ci	bus_unregister(&wmi_bus_type);
157762306a36Sopenharmony_ci	class_unregister(&wmi_bus_class);
157862306a36Sopenharmony_ci}
157962306a36Sopenharmony_ci
158062306a36Sopenharmony_cisubsys_initcall_sync(acpi_wmi_init);
158162306a36Sopenharmony_cimodule_exit(acpi_wmi_exit);
1582