xref: /openbmc/linux/drivers/platform/x86/wmi.c (revision 25be44f6)
11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2b4f9fe12SLen Brown /*
3b4f9fe12SLen Brown  *  ACPI-WMI mapping driver
4b4f9fe12SLen Brown  *
5b4f9fe12SLen Brown  *  Copyright (C) 2007-2008 Carlos Corbacho <carlos@strangeworlds.co.uk>
6b4f9fe12SLen Brown  *
7b4f9fe12SLen Brown  *  GUID parsing code from ldm.c is:
8b4f9fe12SLen Brown  *   Copyright (C) 2001,2002 Richard Russon <ldm@flatcap.org>
9b4f9fe12SLen Brown  *   Copyright (c) 2001-2007 Anton Altaparmakov
10b4f9fe12SLen Brown  *   Copyright (C) 2001,2002 Jakob Kemi <jakob.kemi@telia.com>
11b4f9fe12SLen Brown  *
122c9c5664SDarren Hart (VMware)  *  WMI bus infrastructure by Andrew Lutomirski and Darren Hart:
132c9c5664SDarren Hart (VMware)  *    Copyright (C) 2015 Andrew Lutomirski
142c9c5664SDarren Hart (VMware)  *    Copyright (C) 2017 VMware, Inc. All Rights Reserved.
15b4f9fe12SLen Brown  */
16b4f9fe12SLen Brown 
178e07514dSDmitry Torokhov #define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
188e07514dSDmitry Torokhov 
19b4f9fe12SLen Brown #include <linux/acpi.h>
201c95ace7SBarnabás Pőcze #include <linux/bits.h>
21dea878d8SBarnabás Pőcze #include <linux/build_bug.h>
22b60ee4e0SMario Limonciello #include <linux/device.h>
23b60ee4e0SMario Limonciello #include <linux/init.h>
24b60ee4e0SMario Limonciello #include <linux/kernel.h>
25b60ee4e0SMario Limonciello #include <linux/list.h>
2644b6b766SMario Limonciello #include <linux/miscdevice.h>
277c52d551SPaul Gortmaker #include <linux/module.h>
289599ed91SAndy Lutomirski #include <linux/platform_device.h>
29b60ee4e0SMario Limonciello #include <linux/slab.h>
306133913aSBarnabás Pőcze #include <linux/sysfs.h>
31b60ee4e0SMario Limonciello #include <linux/types.h>
3244b6b766SMario Limonciello #include <linux/uaccess.h>
33538d7eb8SAndy Shevchenko #include <linux/uuid.h>
34b60ee4e0SMario Limonciello #include <linux/wmi.h>
35df23e2beSPeter Zijlstra #include <linux/fs.h>
3644b6b766SMario Limonciello #include <uapi/linux/wmi.h>
37b4f9fe12SLen Brown 
38b4f9fe12SLen Brown MODULE_AUTHOR("Carlos Corbacho");
39b4f9fe12SLen Brown MODULE_DESCRIPTION("ACPI-WMI Mapping Driver");
40b4f9fe12SLen Brown MODULE_LICENSE("GPL");
41b4f9fe12SLen Brown 
42762e1a2fSDmitry Torokhov static LIST_HEAD(wmi_block_list);
43b4f9fe12SLen Brown 
44b4f9fe12SLen Brown struct guid_block {
4567f472fdSBarnabás Pőcze 	guid_t guid;
46b4f9fe12SLen Brown 	union {
47b4f9fe12SLen Brown 		char object_id[2];
48b4f9fe12SLen Brown 		struct {
49b4f9fe12SLen Brown 			unsigned char notify_id;
50b4f9fe12SLen Brown 			unsigned char reserved;
51b4f9fe12SLen Brown 		};
52b4f9fe12SLen Brown 	};
53b4f9fe12SLen Brown 	u8 instance_count;
54b4f9fe12SLen Brown 	u8 flags;
55dea878d8SBarnabás Pőcze } __packed;
56dea878d8SBarnabás Pőcze static_assert(sizeof(typeof_member(struct guid_block, guid)) == 16);
57dea878d8SBarnabás Pőcze static_assert(sizeof(struct guid_block) == 20);
58dea878d8SBarnabás Pőcze static_assert(__alignof__(struct guid_block) == 1);
59b4f9fe12SLen Brown 
60b4f9fe12SLen Brown struct wmi_block {
61844af950SAndy Lutomirski 	struct wmi_device dev;
62b4f9fe12SLen Brown 	struct list_head list;
63b4f9fe12SLen Brown 	struct guid_block gblock;
6444b6b766SMario Limonciello 	struct miscdevice char_dev;
6544b6b766SMario Limonciello 	struct mutex char_mutex;
66b0e86302SAndy Lutomirski 	struct acpi_device *acpi_device;
67b4f9fe12SLen Brown 	wmi_notify_handler handler;
68b4f9fe12SLen Brown 	void *handler_data;
6944b6b766SMario Limonciello 	u64 req_buf_size;
70d4fc91adSAndy Lutomirski 
71fd70da6aSDarren Hart (VMware) 	bool read_takes_no_args;
72b4f9fe12SLen Brown };
73b4f9fe12SLen Brown 
74b4f9fe12SLen Brown 
75b4f9fe12SLen Brown /*
76b4f9fe12SLen Brown  * If the GUID data block is marked as expensive, we must enable and
77b4f9fe12SLen Brown  * explicitily disable data collection.
78b4f9fe12SLen Brown  */
791c95ace7SBarnabás Pőcze #define ACPI_WMI_EXPENSIVE   BIT(0)
801c95ace7SBarnabás Pőcze #define ACPI_WMI_METHOD      BIT(1)	/* GUID is a method */
811c95ace7SBarnabás Pőcze #define ACPI_WMI_STRING      BIT(2)	/* GUID takes & returns a string */
821c95ace7SBarnabás Pőcze #define ACPI_WMI_EVENT       BIT(3)	/* GUID is an event */
83b4f9fe12SLen Brown 
8490ab5ee9SRusty Russell static bool debug_event;
85fc3155b2SThomas Renninger module_param(debug_event, bool, 0444);
86fc3155b2SThomas Renninger MODULE_PARM_DESC(debug_event,
87fc3155b2SThomas Renninger 		 "Log WMI Events [0/1]");
88fc3155b2SThomas Renninger 
8990ab5ee9SRusty Russell static bool debug_dump_wdg;
90a929aae0SThomas Renninger module_param(debug_dump_wdg, bool, 0444);
91a929aae0SThomas Renninger MODULE_PARM_DESC(debug_dump_wdg,
92a929aae0SThomas Renninger 		 "Dump available WMI interfaces [0/1]");
93a929aae0SThomas Renninger 
949599ed91SAndy Lutomirski static int acpi_wmi_remove(struct platform_device *device);
959599ed91SAndy Lutomirski static int acpi_wmi_probe(struct platform_device *device);
96b4f9fe12SLen Brown 
97b4f9fe12SLen Brown static const struct acpi_device_id wmi_device_ids[] = {
98b4f9fe12SLen Brown 	{"PNP0C14", 0},
99b4f9fe12SLen Brown 	{"pnp0c14", 0},
1009bf9ca95SBarnabás Pőcze 	{ }
101b4f9fe12SLen Brown };
102b4f9fe12SLen Brown MODULE_DEVICE_TABLE(acpi, wmi_device_ids);
103b4f9fe12SLen Brown 
1049599ed91SAndy Lutomirski static struct platform_driver acpi_wmi_driver = {
1059599ed91SAndy Lutomirski 	.driver = {
106844af950SAndy Lutomirski 		.name = "acpi-wmi",
1079599ed91SAndy Lutomirski 		.acpi_match_table = wmi_device_ids,
108b4f9fe12SLen Brown 	},
1099599ed91SAndy Lutomirski 	.probe = acpi_wmi_probe,
1109599ed91SAndy Lutomirski 	.remove = acpi_wmi_remove,
111b4f9fe12SLen Brown };
112b4f9fe12SLen Brown 
113b4f9fe12SLen Brown /*
114b4f9fe12SLen Brown  * GUID parsing functions
115b4f9fe12SLen Brown  */
116b4f9fe12SLen Brown 
117b4f9fe12SLen Brown static bool find_guid(const char *guid_string, struct wmi_block **out)
118b4f9fe12SLen Brown {
119f9dffc14SAndy Shevchenko 	guid_t guid_input;
120b4f9fe12SLen Brown 	struct wmi_block *wblock;
121b4f9fe12SLen Brown 
122f9dffc14SAndy Shevchenko 	if (guid_parse(guid_string, &guid_input))
123538d7eb8SAndy Shevchenko 		return false;
124b4f9fe12SLen Brown 
125cedb3b2aSAndy Shevchenko 	list_for_each_entry(wblock, &wmi_block_list, list) {
1261ce69d2bSBarnabás Pőcze 		if (guid_equal(&wblock->gblock.guid, &guid_input)) {
127b4f9fe12SLen Brown 			if (out)
128b4f9fe12SLen Brown 				*out = wblock;
129097c27fcSJoe Perches 			return true;
130b4f9fe12SLen Brown 		}
131b4f9fe12SLen Brown 	}
132097c27fcSJoe Perches 	return false;
133b4f9fe12SLen Brown }
134b4f9fe12SLen Brown 
135a48e2338SMattias Jacobsson static const void *find_guid_context(struct wmi_block *wblock,
136a48e2338SMattias Jacobsson 				     struct wmi_driver *wdriver)
137a48e2338SMattias Jacobsson {
138a48e2338SMattias Jacobsson 	const struct wmi_device_id *id;
139a48e2338SMattias Jacobsson 
1406e0bc588SBarnabás Pőcze 	id = wdriver->id_table;
1416e0bc588SBarnabás Pőcze 	if (!id)
142a48e2338SMattias Jacobsson 		return NULL;
143a48e2338SMattias Jacobsson 
144a48e2338SMattias Jacobsson 	while (*id->guid_string) {
145f5431bf1SBarnabás Pőcze 		guid_t guid_input;
146f5431bf1SBarnabás Pőcze 
147f9dffc14SAndy Shevchenko 		if (guid_parse(id->guid_string, &guid_input))
148a48e2338SMattias Jacobsson 			continue;
14967f472fdSBarnabás Pőcze 		if (guid_equal(&wblock->gblock.guid, &guid_input))
150a48e2338SMattias Jacobsson 			return id->context;
151a48e2338SMattias Jacobsson 		id++;
152a48e2338SMattias Jacobsson 	}
153a48e2338SMattias Jacobsson 	return NULL;
154a48e2338SMattias Jacobsson }
155a48e2338SMattias Jacobsson 
156d4fc91adSAndy Lutomirski static int get_subobj_info(acpi_handle handle, const char *pathname,
157d4fc91adSAndy Lutomirski 			   struct acpi_device_info **info)
158d4fc91adSAndy Lutomirski {
159d4fc91adSAndy Lutomirski 	struct acpi_device_info *dummy_info, **info_ptr;
160d4fc91adSAndy Lutomirski 	acpi_handle subobj_handle;
161d4fc91adSAndy Lutomirski 	acpi_status status;
162d4fc91adSAndy Lutomirski 
163d4fc91adSAndy Lutomirski 	status = acpi_get_handle(handle, (char *)pathname, &subobj_handle);
164d4fc91adSAndy Lutomirski 	if (status == AE_NOT_FOUND)
165d4fc91adSAndy Lutomirski 		return -ENOENT;
166d4fc91adSAndy Lutomirski 	else if (ACPI_FAILURE(status))
167d4fc91adSAndy Lutomirski 		return -EIO;
168d4fc91adSAndy Lutomirski 
169d4fc91adSAndy Lutomirski 	info_ptr = info ? info : &dummy_info;
170d4fc91adSAndy Lutomirski 	status = acpi_get_object_info(subobj_handle, info_ptr);
171d4fc91adSAndy Lutomirski 	if (ACPI_FAILURE(status))
172d4fc91adSAndy Lutomirski 		return -EIO;
173d4fc91adSAndy Lutomirski 
174d4fc91adSAndy Lutomirski 	if (!info)
175d4fc91adSAndy Lutomirski 		kfree(dummy_info);
176d4fc91adSAndy Lutomirski 
177d4fc91adSAndy Lutomirski 	return 0;
178d4fc91adSAndy Lutomirski }
179d4fc91adSAndy Lutomirski 
180285dd01aSBarnabás Pőcze static acpi_status wmi_method_enable(struct wmi_block *wblock, bool enable)
181b4f9fe12SLen Brown {
18243aacf83SBarnabás Pőcze 	struct guid_block *block;
183b4f9fe12SLen Brown 	char method[5];
184b4f9fe12SLen Brown 	acpi_status status;
185b4f9fe12SLen Brown 	acpi_handle handle;
186b4f9fe12SLen Brown 
187b4f9fe12SLen Brown 	block = &wblock->gblock;
188b0e86302SAndy Lutomirski 	handle = wblock->acpi_device->handle;
189b4f9fe12SLen Brown 
190b4f9fe12SLen Brown 	snprintf(method, 5, "WE%02X", block->notify_id);
1918122ab66SZhang Rui 	status = acpi_execute_simple_method(handle, method, enable);
192736b48aaSBarnabás Pőcze 	if (status == AE_NOT_FOUND)
193b4f9fe12SLen Brown 		return AE_OK;
194736b48aaSBarnabás Pőcze 
195736b48aaSBarnabás Pőcze 	return status;
196b4f9fe12SLen Brown }
197b4f9fe12SLen Brown 
19857f2ce89SBarnabás Pőcze #define WMI_ACPI_METHOD_NAME_SIZE 5
19957f2ce89SBarnabás Pőcze 
20057f2ce89SBarnabás Pőcze static inline void get_acpi_method_name(const struct wmi_block *wblock,
20157f2ce89SBarnabás Pőcze 					const char method,
20257f2ce89SBarnabás Pőcze 					char buffer[static WMI_ACPI_METHOD_NAME_SIZE])
20357f2ce89SBarnabás Pőcze {
20457f2ce89SBarnabás Pőcze 	static_assert(ARRAY_SIZE(wblock->gblock.object_id) == 2);
20557f2ce89SBarnabás Pőcze 	static_assert(WMI_ACPI_METHOD_NAME_SIZE >= 5);
20657f2ce89SBarnabás Pőcze 
20757f2ce89SBarnabás Pőcze 	buffer[0] = 'W';
20857f2ce89SBarnabás Pőcze 	buffer[1] = method;
20957f2ce89SBarnabás Pőcze 	buffer[2] = wblock->gblock.object_id[0];
21057f2ce89SBarnabás Pőcze 	buffer[3] = wblock->gblock.object_id[1];
21157f2ce89SBarnabás Pőcze 	buffer[4] = '\0';
21257f2ce89SBarnabás Pőcze }
21357f2ce89SBarnabás Pőcze 
21451142a08SBarnabás Pőcze static inline acpi_object_type get_param_acpi_type(const struct wmi_block *wblock)
21551142a08SBarnabás Pőcze {
21651142a08SBarnabás Pőcze 	if (wblock->gblock.flags & ACPI_WMI_STRING)
21751142a08SBarnabás Pőcze 		return ACPI_TYPE_STRING;
21851142a08SBarnabás Pőcze 	else
21951142a08SBarnabás Pőcze 		return ACPI_TYPE_BUFFER;
22051142a08SBarnabás Pőcze }
22151142a08SBarnabás Pőcze 
222*25be44f6SBarnabás Pőcze static acpi_status get_event_data(const struct wmi_block *wblock, struct acpi_buffer *out)
223*25be44f6SBarnabás Pőcze {
224*25be44f6SBarnabás Pőcze 	union acpi_object param = {
225*25be44f6SBarnabás Pőcze 		.integer = {
226*25be44f6SBarnabás Pőcze 			.type = ACPI_TYPE_INTEGER,
227*25be44f6SBarnabás Pőcze 			.value = wblock->gblock.notify_id,
228*25be44f6SBarnabás Pőcze 		}
229*25be44f6SBarnabás Pőcze 	};
230*25be44f6SBarnabás Pőcze 	struct acpi_object_list input = {
231*25be44f6SBarnabás Pőcze 		.count = 1,
232*25be44f6SBarnabás Pőcze 		.pointer = &param,
233*25be44f6SBarnabás Pőcze 	};
234*25be44f6SBarnabás Pőcze 
235*25be44f6SBarnabás Pőcze 	return acpi_evaluate_object(wblock->acpi_device->handle, "_WED", &input, out);
236*25be44f6SBarnabás Pőcze }
237*25be44f6SBarnabás Pőcze 
238b4f9fe12SLen Brown /*
239b4f9fe12SLen Brown  * Exported WMI functions
240b4f9fe12SLen Brown  */
24144b6b766SMario Limonciello 
24244b6b766SMario Limonciello /**
24344b6b766SMario Limonciello  * set_required_buffer_size - Sets the buffer size needed for performing IOCTL
24444b6b766SMario Limonciello  * @wdev: A wmi bus device from a driver
2455a707af1SAndy Shevchenko  * @length: Required buffer size
24644b6b766SMario Limonciello  *
24744b6b766SMario Limonciello  * Allocates memory needed for buffer, stores the buffer size in that memory
24844b6b766SMario Limonciello  */
24944b6b766SMario Limonciello int set_required_buffer_size(struct wmi_device *wdev, u64 length)
25044b6b766SMario Limonciello {
25144b6b766SMario Limonciello 	struct wmi_block *wblock;
25244b6b766SMario Limonciello 
25344b6b766SMario Limonciello 	wblock = container_of(wdev, struct wmi_block, dev);
25444b6b766SMario Limonciello 	wblock->req_buf_size = length;
25544b6b766SMario Limonciello 
25644b6b766SMario Limonciello 	return 0;
25744b6b766SMario Limonciello }
25844b6b766SMario Limonciello EXPORT_SYMBOL_GPL(set_required_buffer_size);
25944b6b766SMario Limonciello 
260b4f9fe12SLen Brown /**
261b4f9fe12SLen Brown  * wmi_evaluate_method - Evaluate a WMI method
262b4f9fe12SLen Brown  * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
263b4f9fe12SLen Brown  * @instance: Instance index
264b4f9fe12SLen Brown  * @method_id: Method ID to call
2655a707af1SAndy Shevchenko  * @in: Buffer containing input for the method call
2665a707af1SAndy Shevchenko  * @out: Empty buffer to return the method results
267b4f9fe12SLen Brown  *
268b4f9fe12SLen Brown  * Call an ACPI-WMI method
269b4f9fe12SLen Brown  */
270bba08f35SBarnabás Pőcze acpi_status wmi_evaluate_method(const char *guid_string, u8 instance, u32 method_id,
271bba08f35SBarnabás Pőcze 				const struct acpi_buffer *in, struct acpi_buffer *out)
272b4f9fe12SLen Brown {
273722c856dSMario Limonciello 	struct wmi_block *wblock = NULL;
274722c856dSMario Limonciello 
275722c856dSMario Limonciello 	if (!find_guid(guid_string, &wblock))
276722c856dSMario Limonciello 		return AE_ERROR;
277722c856dSMario Limonciello 	return wmidev_evaluate_method(&wblock->dev, instance, method_id,
278722c856dSMario Limonciello 				      in, out);
279722c856dSMario Limonciello }
280722c856dSMario Limonciello EXPORT_SYMBOL_GPL(wmi_evaluate_method);
281722c856dSMario Limonciello 
282722c856dSMario Limonciello /**
283722c856dSMario Limonciello  * wmidev_evaluate_method - Evaluate a WMI method
284722c856dSMario Limonciello  * @wdev: A wmi bus device from a driver
285722c856dSMario Limonciello  * @instance: Instance index
286722c856dSMario Limonciello  * @method_id: Method ID to call
2875a707af1SAndy Shevchenko  * @in: Buffer containing input for the method call
2885a707af1SAndy Shevchenko  * @out: Empty buffer to return the method results
289722c856dSMario Limonciello  *
290722c856dSMario Limonciello  * Call an ACPI-WMI method
291722c856dSMario Limonciello  */
292bba08f35SBarnabás Pőcze acpi_status wmidev_evaluate_method(struct wmi_device *wdev, u8 instance, u32 method_id,
293bba08f35SBarnabás Pőcze 				   const struct acpi_buffer *in, struct acpi_buffer *out)
294722c856dSMario Limonciello {
29543aacf83SBarnabás Pőcze 	struct guid_block *block;
29643aacf83SBarnabás Pőcze 	struct wmi_block *wblock;
297b4f9fe12SLen Brown 	acpi_handle handle;
298b4f9fe12SLen Brown 	struct acpi_object_list input;
299b4f9fe12SLen Brown 	union acpi_object params[3];
30057f2ce89SBarnabás Pőcze 	char method[WMI_ACPI_METHOD_NAME_SIZE];
301b4f9fe12SLen Brown 
302722c856dSMario Limonciello 	wblock = container_of(wdev, struct wmi_block, dev);
303b4f9fe12SLen Brown 	block = &wblock->gblock;
304b0e86302SAndy Lutomirski 	handle = wblock->acpi_device->handle;
305b4f9fe12SLen Brown 
306b4f9fe12SLen Brown 	if (!(block->flags & ACPI_WMI_METHOD))
307b4f9fe12SLen Brown 		return AE_BAD_DATA;
308b4f9fe12SLen Brown 
3096afa1e2aSPali Rohár 	if (block->instance_count <= instance)
310b4f9fe12SLen Brown 		return AE_BAD_PARAMETER;
311b4f9fe12SLen Brown 
312b4f9fe12SLen Brown 	input.count = 2;
313b4f9fe12SLen Brown 	input.pointer = params;
314b4f9fe12SLen Brown 	params[0].type = ACPI_TYPE_INTEGER;
315b4f9fe12SLen Brown 	params[0].integer.value = instance;
316b4f9fe12SLen Brown 	params[1].type = ACPI_TYPE_INTEGER;
317b4f9fe12SLen Brown 	params[1].integer.value = method_id;
318b4f9fe12SLen Brown 
319b4f9fe12SLen Brown 	if (in) {
320b4f9fe12SLen Brown 		input.count = 3;
321b4f9fe12SLen Brown 
32251142a08SBarnabás Pőcze 		params[2].type = get_param_acpi_type(wblock);
323b4f9fe12SLen Brown 		params[2].buffer.length = in->length;
324b4f9fe12SLen Brown 		params[2].buffer.pointer = in->pointer;
325b4f9fe12SLen Brown 	}
326b4f9fe12SLen Brown 
32757f2ce89SBarnabás Pőcze 	get_acpi_method_name(wblock, 'M', method);
328b4f9fe12SLen Brown 
32921397cacSBarnabás Pőcze 	return acpi_evaluate_object(handle, method, &input, out);
330b4f9fe12SLen Brown }
331722c856dSMario Limonciello EXPORT_SYMBOL_GPL(wmidev_evaluate_method);
332b4f9fe12SLen Brown 
33356a37025SAndy Lutomirski static acpi_status __query_block(struct wmi_block *wblock, u8 instance,
334b4f9fe12SLen Brown 				 struct acpi_buffer *out)
335b4f9fe12SLen Brown {
33643aacf83SBarnabás Pőcze 	struct guid_block *block;
33754f14c27SZhang Rui 	acpi_handle handle;
338b4f9fe12SLen Brown 	acpi_status status, wc_status = AE_ERROR;
3398122ab66SZhang Rui 	struct acpi_object_list input;
3408122ab66SZhang Rui 	union acpi_object wq_params[1];
34157f2ce89SBarnabás Pőcze 	char wc_method[WMI_ACPI_METHOD_NAME_SIZE];
34257f2ce89SBarnabás Pőcze 	char method[WMI_ACPI_METHOD_NAME_SIZE];
343b4f9fe12SLen Brown 
34456a37025SAndy Lutomirski 	if (!out)
345b4f9fe12SLen Brown 		return AE_BAD_PARAMETER;
346b4f9fe12SLen Brown 
347b4f9fe12SLen Brown 	block = &wblock->gblock;
348b0e86302SAndy Lutomirski 	handle = wblock->acpi_device->handle;
349b4f9fe12SLen Brown 
3506afa1e2aSPali Rohár 	if (block->instance_count <= instance)
351b4f9fe12SLen Brown 		return AE_BAD_PARAMETER;
352b4f9fe12SLen Brown 
353b4f9fe12SLen Brown 	/* Check GUID is a data block */
354b4f9fe12SLen Brown 	if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD))
355b4f9fe12SLen Brown 		return AE_ERROR;
356b4f9fe12SLen Brown 
357b4f9fe12SLen Brown 	input.count = 1;
358b4f9fe12SLen Brown 	input.pointer = wq_params;
359b4f9fe12SLen Brown 	wq_params[0].type = ACPI_TYPE_INTEGER;
360b4f9fe12SLen Brown 	wq_params[0].integer.value = instance;
361b4f9fe12SLen Brown 
362d4fc91adSAndy Lutomirski 	if (instance == 0 && wblock->read_takes_no_args)
363d4fc91adSAndy Lutomirski 		input.count = 0;
364d4fc91adSAndy Lutomirski 
365b4f9fe12SLen Brown 	/*
366b4f9fe12SLen Brown 	 * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method first to
367b4f9fe12SLen Brown 	 * enable collection.
368b4f9fe12SLen Brown 	 */
369b4f9fe12SLen Brown 	if (block->flags & ACPI_WMI_EXPENSIVE) {
37057f2ce89SBarnabás Pőcze 		get_acpi_method_name(wblock, 'C', wc_method);
371b4f9fe12SLen Brown 
372b4f9fe12SLen Brown 		/*
373b4f9fe12SLen Brown 		 * Some GUIDs break the specification by declaring themselves
374b4f9fe12SLen Brown 		 * expensive, but have no corresponding WCxx method. So we
375b4f9fe12SLen Brown 		 * should not fail if this happens.
376b4f9fe12SLen Brown 		 */
377bad9da86SKelsey Skunberg 		wc_status = acpi_execute_simple_method(handle, wc_method, 1);
378b4f9fe12SLen Brown 	}
379b4f9fe12SLen Brown 
38057f2ce89SBarnabás Pőcze 	get_acpi_method_name(wblock, 'Q', method);
381b4f9fe12SLen Brown 	status = acpi_evaluate_object(handle, method, &input, out);
382b4f9fe12SLen Brown 
383b4f9fe12SLen Brown 	/*
384b4f9fe12SLen Brown 	 * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method, even if
385b4f9fe12SLen Brown 	 * the WQxx method failed - we should disable collection anyway.
386b4f9fe12SLen Brown 	 */
387b4f9fe12SLen Brown 	if ((block->flags & ACPI_WMI_EXPENSIVE) && ACPI_SUCCESS(wc_status)) {
3881975718cSBarnabás Pőcze 		/*
3891975718cSBarnabás Pőcze 		 * Ignore whether this WCxx call succeeds or not since
3901975718cSBarnabás Pőcze 		 * the previously executed WQxx method call might have
3911975718cSBarnabás Pőcze 		 * succeeded, and returning the failing status code
3921975718cSBarnabás Pőcze 		 * of this call would throw away the result of the WQxx
3931975718cSBarnabás Pőcze 		 * call, potentially leaking memory.
3941975718cSBarnabás Pőcze 		 */
3951975718cSBarnabás Pőcze 		acpi_execute_simple_method(handle, wc_method, 0);
396b4f9fe12SLen Brown 	}
397b4f9fe12SLen Brown 
398b4f9fe12SLen Brown 	return status;
399b4f9fe12SLen Brown }
40056a37025SAndy Lutomirski 
40156a37025SAndy Lutomirski /**
40256a37025SAndy Lutomirski  * wmi_query_block - Return contents of a WMI block (deprecated)
40356a37025SAndy Lutomirski  * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
40456a37025SAndy Lutomirski  * @instance: Instance index
4055a707af1SAndy Shevchenko  * @out: Empty buffer to return the contents of the data block to
40656a37025SAndy Lutomirski  *
40756a37025SAndy Lutomirski  * Return the contents of an ACPI-WMI data block to a buffer
40856a37025SAndy Lutomirski  */
40956a37025SAndy Lutomirski acpi_status wmi_query_block(const char *guid_string, u8 instance,
41056a37025SAndy Lutomirski 			    struct acpi_buffer *out)
41156a37025SAndy Lutomirski {
41256a37025SAndy Lutomirski 	struct wmi_block *wblock;
41356a37025SAndy Lutomirski 
41456a37025SAndy Lutomirski 	if (!guid_string)
41556a37025SAndy Lutomirski 		return AE_BAD_PARAMETER;
41656a37025SAndy Lutomirski 
41756a37025SAndy Lutomirski 	if (!find_guid(guid_string, &wblock))
41856a37025SAndy Lutomirski 		return AE_ERROR;
41956a37025SAndy Lutomirski 
42056a37025SAndy Lutomirski 	return __query_block(wblock, instance, out);
42156a37025SAndy Lutomirski }
422b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_query_block);
423b4f9fe12SLen Brown 
42456a37025SAndy Lutomirski union acpi_object *wmidev_block_query(struct wmi_device *wdev, u8 instance)
42556a37025SAndy Lutomirski {
42656a37025SAndy Lutomirski 	struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
42756a37025SAndy Lutomirski 	struct wmi_block *wblock = container_of(wdev, struct wmi_block, dev);
42856a37025SAndy Lutomirski 
42956a37025SAndy Lutomirski 	if (ACPI_FAILURE(__query_block(wblock, instance, &out)))
43056a37025SAndy Lutomirski 		return NULL;
43156a37025SAndy Lutomirski 
432c06a2fdeSBarnabás Pőcze 	return out.pointer;
43356a37025SAndy Lutomirski }
43456a37025SAndy Lutomirski EXPORT_SYMBOL_GPL(wmidev_block_query);
43556a37025SAndy Lutomirski 
436b4f9fe12SLen Brown /**
437b4f9fe12SLen Brown  * wmi_set_block - Write to a WMI block
438b4f9fe12SLen Brown  * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
439b4f9fe12SLen Brown  * @instance: Instance index
4405a707af1SAndy Shevchenko  * @in: Buffer containing new values for the data block
441b4f9fe12SLen Brown  *
442b4f9fe12SLen Brown  * Write the contents of the input buffer to an ACPI-WMI data block
443b4f9fe12SLen Brown  */
444b4f9fe12SLen Brown acpi_status wmi_set_block(const char *guid_string, u8 instance,
445b4f9fe12SLen Brown 			  const struct acpi_buffer *in)
446b4f9fe12SLen Brown {
447b4f9fe12SLen Brown 	struct wmi_block *wblock = NULL;
44843aacf83SBarnabás Pőcze 	struct guid_block *block;
449b4f9fe12SLen Brown 	acpi_handle handle;
450b4f9fe12SLen Brown 	struct acpi_object_list input;
451b4f9fe12SLen Brown 	union acpi_object params[2];
45257f2ce89SBarnabás Pőcze 	char method[WMI_ACPI_METHOD_NAME_SIZE];
453b4f9fe12SLen Brown 
454b4f9fe12SLen Brown 	if (!guid_string || !in)
455b4f9fe12SLen Brown 		return AE_BAD_DATA;
456b4f9fe12SLen Brown 
457b4f9fe12SLen Brown 	if (!find_guid(guid_string, &wblock))
458b4f9fe12SLen Brown 		return AE_ERROR;
459b4f9fe12SLen Brown 
460b4f9fe12SLen Brown 	block = &wblock->gblock;
461b0e86302SAndy Lutomirski 	handle = wblock->acpi_device->handle;
462b4f9fe12SLen Brown 
4636afa1e2aSPali Rohár 	if (block->instance_count <= instance)
464b4f9fe12SLen Brown 		return AE_BAD_PARAMETER;
465b4f9fe12SLen Brown 
466b4f9fe12SLen Brown 	/* Check GUID is a data block */
467b4f9fe12SLen Brown 	if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD))
468b4f9fe12SLen Brown 		return AE_ERROR;
469b4f9fe12SLen Brown 
470b4f9fe12SLen Brown 	input.count = 2;
471b4f9fe12SLen Brown 	input.pointer = params;
472b4f9fe12SLen Brown 	params[0].type = ACPI_TYPE_INTEGER;
473b4f9fe12SLen Brown 	params[0].integer.value = instance;
47451142a08SBarnabás Pőcze 	params[1].type = get_param_acpi_type(wblock);
475b4f9fe12SLen Brown 	params[1].buffer.length = in->length;
476b4f9fe12SLen Brown 	params[1].buffer.pointer = in->pointer;
477b4f9fe12SLen Brown 
47857f2ce89SBarnabás Pőcze 	get_acpi_method_name(wblock, 'S', method);
479b4f9fe12SLen Brown 
480b4f9fe12SLen Brown 	return acpi_evaluate_object(handle, method, &input, NULL);
481b4f9fe12SLen Brown }
482b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_set_block);
483b4f9fe12SLen Brown 
48437830662SDmitry Torokhov static void wmi_dump_wdg(const struct guid_block *g)
485a929aae0SThomas Renninger {
48667f472fdSBarnabás Pőcze 	pr_info("%pUL:\n", &g->guid);
487cd3921f8SPali Rohár 	if (g->flags & ACPI_WMI_EVENT)
488cd3921f8SPali Rohár 		pr_info("\tnotify_id: 0x%02X\n", g->notify_id);
489cd3921f8SPali Rohár 	else
490cd3921f8SPali Rohár 		pr_info("\tobject_id: %2pE\n", g->object_id);
4918e07514dSDmitry Torokhov 	pr_info("\tinstance_count: %d\n", g->instance_count);
4928e07514dSDmitry Torokhov 	pr_info("\tflags: %#x", g->flags);
493a929aae0SThomas Renninger 	if (g->flags) {
494a929aae0SThomas Renninger 		if (g->flags & ACPI_WMI_EXPENSIVE)
4958e07514dSDmitry Torokhov 			pr_cont(" ACPI_WMI_EXPENSIVE");
496a929aae0SThomas Renninger 		if (g->flags & ACPI_WMI_METHOD)
4978e07514dSDmitry Torokhov 			pr_cont(" ACPI_WMI_METHOD");
498a929aae0SThomas Renninger 		if (g->flags & ACPI_WMI_STRING)
4998e07514dSDmitry Torokhov 			pr_cont(" ACPI_WMI_STRING");
500a929aae0SThomas Renninger 		if (g->flags & ACPI_WMI_EVENT)
5018e07514dSDmitry Torokhov 			pr_cont(" ACPI_WMI_EVENT");
502a929aae0SThomas Renninger 	}
5038e07514dSDmitry Torokhov 	pr_cont("\n");
504a929aae0SThomas Renninger 
505a929aae0SThomas Renninger }
506a929aae0SThomas Renninger 
507fc3155b2SThomas Renninger static void wmi_notify_debug(u32 value, void *context)
508fc3155b2SThomas Renninger {
509fc3155b2SThomas Renninger 	struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
510fc3155b2SThomas Renninger 	union acpi_object *obj;
5111492616aSAxel Lin 	acpi_status status;
512fc3155b2SThomas Renninger 
5131492616aSAxel Lin 	status = wmi_get_event_data(value, &response);
5141492616aSAxel Lin 	if (status != AE_OK) {
5158e07514dSDmitry Torokhov 		pr_info("bad event status 0x%x\n", status);
5161492616aSAxel Lin 		return;
5171492616aSAxel Lin 	}
518fc3155b2SThomas Renninger 
519c06a2fdeSBarnabás Pőcze 	obj = response.pointer;
520fc3155b2SThomas Renninger 	if (!obj)
521fc3155b2SThomas Renninger 		return;
522fc3155b2SThomas Renninger 
5231c23ab91SBarnabás Pőcze 	pr_info("DEBUG: event 0x%02X ", value);
524fc3155b2SThomas Renninger 	switch (obj->type) {
525fc3155b2SThomas Renninger 	case ACPI_TYPE_BUFFER:
5261c23ab91SBarnabás Pőcze 		pr_cont("BUFFER_TYPE - length %u\n", obj->buffer.length);
527fc3155b2SThomas Renninger 		break;
528fc3155b2SThomas Renninger 	case ACPI_TYPE_STRING:
5298e07514dSDmitry Torokhov 		pr_cont("STRING_TYPE - %s\n", obj->string.pointer);
530fc3155b2SThomas Renninger 		break;
531fc3155b2SThomas Renninger 	case ACPI_TYPE_INTEGER:
5328e07514dSDmitry Torokhov 		pr_cont("INTEGER_TYPE - %llu\n", obj->integer.value);
533fc3155b2SThomas Renninger 		break;
534fc3155b2SThomas Renninger 	case ACPI_TYPE_PACKAGE:
5351c23ab91SBarnabás Pőcze 		pr_cont("PACKAGE_TYPE - %u elements\n", obj->package.count);
536fc3155b2SThomas Renninger 		break;
537fc3155b2SThomas Renninger 	default:
5388e07514dSDmitry Torokhov 		pr_cont("object type 0x%X\n", obj->type);
539fc3155b2SThomas Renninger 	}
5401492616aSAxel Lin 	kfree(obj);
541fc3155b2SThomas Renninger }
542fc3155b2SThomas Renninger 
543b4f9fe12SLen Brown /**
544b4f9fe12SLen Brown  * wmi_install_notify_handler - Register handler for WMI events
5455a707af1SAndy Shevchenko  * @guid: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
546b4f9fe12SLen Brown  * @handler: Function to handle notifications
547b4f9fe12SLen Brown  * @data: Data to be returned to handler when event is fired
548b4f9fe12SLen Brown  *
549b4f9fe12SLen Brown  * Register a handler for events sent to the ACPI-WMI mapper device.
550b4f9fe12SLen Brown  */
551b4f9fe12SLen Brown acpi_status wmi_install_notify_handler(const char *guid,
552bba08f35SBarnabás Pőcze 				       wmi_notify_handler handler,
553bba08f35SBarnabás Pőcze 				       void *data)
554b4f9fe12SLen Brown {
555b4f9fe12SLen Brown 	struct wmi_block *block;
55658f6425eSColin King 	acpi_status status = AE_NOT_EXIST;
557f9dffc14SAndy Shevchenko 	guid_t guid_input;
558b4f9fe12SLen Brown 
559b4f9fe12SLen Brown 	if (!guid || !handler)
560b4f9fe12SLen Brown 		return AE_BAD_PARAMETER;
561b4f9fe12SLen Brown 
562f9dffc14SAndy Shevchenko 	if (guid_parse(guid, &guid_input))
563538d7eb8SAndy Shevchenko 		return AE_BAD_PARAMETER;
564b4f9fe12SLen Brown 
565cedb3b2aSAndy Shevchenko 	list_for_each_entry(block, &wmi_block_list, list) {
56658f6425eSColin King 		acpi_status wmi_status;
56758f6425eSColin King 
56867f472fdSBarnabás Pőcze 		if (guid_equal(&block->gblock.guid, &guid_input)) {
56958f6425eSColin King 			if (block->handler &&
57058f6425eSColin King 			    block->handler != wmi_notify_debug)
571b4f9fe12SLen Brown 				return AE_ALREADY_ACQUIRED;
572b4f9fe12SLen Brown 
573b4f9fe12SLen Brown 			block->handler = handler;
574b4f9fe12SLen Brown 			block->handler_data = data;
575b4f9fe12SLen Brown 
576285dd01aSBarnabás Pőcze 			wmi_status = wmi_method_enable(block, true);
57758f6425eSColin King 			if ((wmi_status != AE_OK) ||
57858f6425eSColin King 			    ((wmi_status == AE_OK) && (status == AE_NOT_EXIST)))
57958f6425eSColin King 				status = wmi_status;
58058f6425eSColin King 		}
58158f6425eSColin King 	}
582b4f9fe12SLen Brown 
583b4f9fe12SLen Brown 	return status;
584b4f9fe12SLen Brown }
585b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_install_notify_handler);
586b4f9fe12SLen Brown 
587b4f9fe12SLen Brown /**
58807ce4cfdSBarnabás Pőcze  * wmi_remove_notify_handler - Unregister handler for WMI events
5895a707af1SAndy Shevchenko  * @guid: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
590b4f9fe12SLen Brown  *
591b4f9fe12SLen Brown  * Unregister handler for events sent to the ACPI-WMI mapper device.
592b4f9fe12SLen Brown  */
593b4f9fe12SLen Brown acpi_status wmi_remove_notify_handler(const char *guid)
594b4f9fe12SLen Brown {
595b4f9fe12SLen Brown 	struct wmi_block *block;
59658f6425eSColin King 	acpi_status status = AE_NOT_EXIST;
597f9dffc14SAndy Shevchenko 	guid_t guid_input;
598b4f9fe12SLen Brown 
599b4f9fe12SLen Brown 	if (!guid)
600b4f9fe12SLen Brown 		return AE_BAD_PARAMETER;
601b4f9fe12SLen Brown 
602f9dffc14SAndy Shevchenko 	if (guid_parse(guid, &guid_input))
603538d7eb8SAndy Shevchenko 		return AE_BAD_PARAMETER;
604b4f9fe12SLen Brown 
605cedb3b2aSAndy Shevchenko 	list_for_each_entry(block, &wmi_block_list, list) {
60658f6425eSColin King 		acpi_status wmi_status;
60758f6425eSColin King 
60867f472fdSBarnabás Pőcze 		if (guid_equal(&block->gblock.guid, &guid_input)) {
60958f6425eSColin King 			if (!block->handler ||
61058f6425eSColin King 			    block->handler == wmi_notify_debug)
611b4f9fe12SLen Brown 				return AE_NULL_ENTRY;
612b4f9fe12SLen Brown 
613fc3155b2SThomas Renninger 			if (debug_event) {
614fc3155b2SThomas Renninger 				block->handler = wmi_notify_debug;
61558f6425eSColin King 				status = AE_OK;
616fc3155b2SThomas Renninger 			} else {
617285dd01aSBarnabás Pőcze 				wmi_status = wmi_method_enable(block, false);
618b4f9fe12SLen Brown 				block->handler = NULL;
619b4f9fe12SLen Brown 				block->handler_data = NULL;
62058f6425eSColin King 				if ((wmi_status != AE_OK) ||
62158f6425eSColin King 				    ((wmi_status == AE_OK) &&
62258f6425eSColin King 				     (status == AE_NOT_EXIST)))
62358f6425eSColin King 					status = wmi_status;
624fc3155b2SThomas Renninger 			}
62558f6425eSColin King 		}
62658f6425eSColin King 	}
62758f6425eSColin King 
628b4f9fe12SLen Brown 	return status;
629b4f9fe12SLen Brown }
630b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_remove_notify_handler);
631b4f9fe12SLen Brown 
632b4f9fe12SLen Brown /**
633b4f9fe12SLen Brown  * wmi_get_event_data - Get WMI data associated with an event
634b4f9fe12SLen Brown  *
6353e9b988eSAnisse Astier  * @event: Event to find
6363e9b988eSAnisse Astier  * @out: Buffer to hold event data. out->pointer should be freed with kfree()
637b4f9fe12SLen Brown  *
638b4f9fe12SLen Brown  * Returns extra data associated with an event in WMI.
639b4f9fe12SLen Brown  */
640b4f9fe12SLen Brown acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out)
641b4f9fe12SLen Brown {
642b4f9fe12SLen Brown 	struct wmi_block *wblock;
643b4f9fe12SLen Brown 
644cedb3b2aSAndy Shevchenko 	list_for_each_entry(wblock, &wmi_block_list, list) {
645f5431bf1SBarnabás Pőcze 		struct guid_block *gblock = &wblock->gblock;
646b4f9fe12SLen Brown 
647*25be44f6SBarnabás Pőcze 		if ((gblock->flags & ACPI_WMI_EVENT) && gblock->notify_id == event)
648*25be44f6SBarnabás Pőcze 			return get_event_data(wblock, out);
649b4f9fe12SLen Brown 	}
650b4f9fe12SLen Brown 
651b4f9fe12SLen Brown 	return AE_NOT_FOUND;
652b4f9fe12SLen Brown }
653b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_get_event_data);
654b4f9fe12SLen Brown 
655b4f9fe12SLen Brown /**
656b4f9fe12SLen Brown  * wmi_has_guid - Check if a GUID is available
657b4f9fe12SLen Brown  * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
658b4f9fe12SLen Brown  *
659b4f9fe12SLen Brown  * Check if a given GUID is defined by _WDG
660b4f9fe12SLen Brown  */
661b4f9fe12SLen Brown bool wmi_has_guid(const char *guid_string)
662b4f9fe12SLen Brown {
663b4f9fe12SLen Brown 	return find_guid(guid_string, NULL);
664b4f9fe12SLen Brown }
665b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_has_guid);
666b4f9fe12SLen Brown 
667e7488e58SYurii Pavlovskyi /**
668e7488e58SYurii Pavlovskyi  * wmi_get_acpi_device_uid() - Get _UID name of ACPI device that defines GUID
669e7488e58SYurii Pavlovskyi  * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
670e7488e58SYurii Pavlovskyi  *
671e7488e58SYurii Pavlovskyi  * Find the _UID of ACPI device associated with this WMI GUID.
672e7488e58SYurii Pavlovskyi  *
673e7488e58SYurii Pavlovskyi  * Return: The ACPI _UID field value or NULL if the WMI GUID was not found
674e7488e58SYurii Pavlovskyi  */
675e7488e58SYurii Pavlovskyi char *wmi_get_acpi_device_uid(const char *guid_string)
676e7488e58SYurii Pavlovskyi {
677e7488e58SYurii Pavlovskyi 	struct wmi_block *wblock = NULL;
678e7488e58SYurii Pavlovskyi 
679e7488e58SYurii Pavlovskyi 	if (!find_guid(guid_string, &wblock))
680e7488e58SYurii Pavlovskyi 		return NULL;
681e7488e58SYurii Pavlovskyi 
682e7488e58SYurii Pavlovskyi 	return acpi_device_uid(wblock->acpi_device);
683e7488e58SYurii Pavlovskyi }
684e7488e58SYurii Pavlovskyi EXPORT_SYMBOL_GPL(wmi_get_acpi_device_uid);
685e7488e58SYurii Pavlovskyi 
686844af950SAndy Lutomirski static struct wmi_block *dev_to_wblock(struct device *dev)
687844af950SAndy Lutomirski {
688844af950SAndy Lutomirski 	return container_of(dev, struct wmi_block, dev.dev);
689844af950SAndy Lutomirski }
690844af950SAndy Lutomirski 
691844af950SAndy Lutomirski static struct wmi_device *dev_to_wdev(struct device *dev)
692844af950SAndy Lutomirski {
693844af950SAndy Lutomirski 	return container_of(dev, struct wmi_device, dev);
694844af950SAndy Lutomirski }
695844af950SAndy Lutomirski 
696e7b2e334SBarnabás Pőcze static inline struct wmi_driver *drv_to_wdrv(struct device_driver *drv)
697e7b2e334SBarnabás Pőcze {
698e7b2e334SBarnabás Pőcze 	return container_of(drv, struct wmi_driver, driver);
699e7b2e334SBarnabás Pőcze }
700e7b2e334SBarnabás Pőcze 
701b4f9fe12SLen Brown /*
7021caab3c1SMatthew Garrett  * sysfs interface
7031caab3c1SMatthew Garrett  */
704614ef432SDmitry Torokhov static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
7051caab3c1SMatthew Garrett 			     char *buf)
7061caab3c1SMatthew Garrett {
707844af950SAndy Lutomirski 	struct wmi_block *wblock = dev_to_wblock(dev);
7081caab3c1SMatthew Garrett 
7096133913aSBarnabás Pőcze 	return sysfs_emit(buf, "wmi:%pUL\n", &wblock->gblock.guid);
7101caab3c1SMatthew Garrett }
711e80b89a5SGreg Kroah-Hartman static DEVICE_ATTR_RO(modalias);
712614ef432SDmitry Torokhov 
713844af950SAndy Lutomirski static ssize_t guid_show(struct device *dev, struct device_attribute *attr,
714844af950SAndy Lutomirski 			 char *buf)
715844af950SAndy Lutomirski {
716844af950SAndy Lutomirski 	struct wmi_block *wblock = dev_to_wblock(dev);
717844af950SAndy Lutomirski 
7186133913aSBarnabás Pőcze 	return sysfs_emit(buf, "%pUL\n", &wblock->gblock.guid);
719844af950SAndy Lutomirski }
720844af950SAndy Lutomirski static DEVICE_ATTR_RO(guid);
721844af950SAndy Lutomirski 
722d79b1074SAndy Lutomirski static ssize_t instance_count_show(struct device *dev,
723d79b1074SAndy Lutomirski 				   struct device_attribute *attr, char *buf)
724d79b1074SAndy Lutomirski {
725d79b1074SAndy Lutomirski 	struct wmi_block *wblock = dev_to_wblock(dev);
726d79b1074SAndy Lutomirski 
7276133913aSBarnabás Pőcze 	return sysfs_emit(buf, "%d\n", (int)wblock->gblock.instance_count);
728d79b1074SAndy Lutomirski }
729d79b1074SAndy Lutomirski static DEVICE_ATTR_RO(instance_count);
730d79b1074SAndy Lutomirski 
731d79b1074SAndy Lutomirski static ssize_t expensive_show(struct device *dev,
732d79b1074SAndy Lutomirski 			      struct device_attribute *attr, char *buf)
733d79b1074SAndy Lutomirski {
734d79b1074SAndy Lutomirski 	struct wmi_block *wblock = dev_to_wblock(dev);
735d79b1074SAndy Lutomirski 
7366133913aSBarnabás Pőcze 	return sysfs_emit(buf, "%d\n",
737d79b1074SAndy Lutomirski 			  (wblock->gblock.flags & ACPI_WMI_EXPENSIVE) != 0);
738d79b1074SAndy Lutomirski }
739d79b1074SAndy Lutomirski static DEVICE_ATTR_RO(expensive);
740d79b1074SAndy Lutomirski 
741e80b89a5SGreg Kroah-Hartman static struct attribute *wmi_attrs[] = {
742e80b89a5SGreg Kroah-Hartman 	&dev_attr_modalias.attr,
743844af950SAndy Lutomirski 	&dev_attr_guid.attr,
744d79b1074SAndy Lutomirski 	&dev_attr_instance_count.attr,
745d79b1074SAndy Lutomirski 	&dev_attr_expensive.attr,
746cd3e3d29SBarnabás Pőcze 	NULL
747614ef432SDmitry Torokhov };
748e80b89a5SGreg Kroah-Hartman ATTRIBUTE_GROUPS(wmi);
7491caab3c1SMatthew Garrett 
750d79b1074SAndy Lutomirski static ssize_t notify_id_show(struct device *dev, struct device_attribute *attr,
751d79b1074SAndy Lutomirski 			      char *buf)
752d79b1074SAndy Lutomirski {
753d79b1074SAndy Lutomirski 	struct wmi_block *wblock = dev_to_wblock(dev);
754d79b1074SAndy Lutomirski 
7556133913aSBarnabás Pőcze 	return sysfs_emit(buf, "%02X\n", (unsigned int)wblock->gblock.notify_id);
756d79b1074SAndy Lutomirski }
757d79b1074SAndy Lutomirski static DEVICE_ATTR_RO(notify_id);
758d79b1074SAndy Lutomirski 
759d79b1074SAndy Lutomirski static struct attribute *wmi_event_attrs[] = {
760d79b1074SAndy Lutomirski 	&dev_attr_notify_id.attr,
761cd3e3d29SBarnabás Pőcze 	NULL
762d79b1074SAndy Lutomirski };
763d79b1074SAndy Lutomirski ATTRIBUTE_GROUPS(wmi_event);
764d79b1074SAndy Lutomirski 
765d79b1074SAndy Lutomirski static ssize_t object_id_show(struct device *dev, struct device_attribute *attr,
766d79b1074SAndy Lutomirski 			      char *buf)
767d79b1074SAndy Lutomirski {
768d79b1074SAndy Lutomirski 	struct wmi_block *wblock = dev_to_wblock(dev);
769d79b1074SAndy Lutomirski 
7706133913aSBarnabás Pőcze 	return sysfs_emit(buf, "%c%c\n", wblock->gblock.object_id[0],
771d79b1074SAndy Lutomirski 			  wblock->gblock.object_id[1]);
772d79b1074SAndy Lutomirski }
773d79b1074SAndy Lutomirski static DEVICE_ATTR_RO(object_id);
774d79b1074SAndy Lutomirski 
775fd70da6aSDarren Hart (VMware) static ssize_t setable_show(struct device *dev, struct device_attribute *attr,
776d4fc91adSAndy Lutomirski 			    char *buf)
777d4fc91adSAndy Lutomirski {
778d4fc91adSAndy Lutomirski 	struct wmi_device *wdev = dev_to_wdev(dev);
779d4fc91adSAndy Lutomirski 
7806133913aSBarnabás Pőcze 	return sysfs_emit(buf, "%d\n", (int)wdev->setable);
781d4fc91adSAndy Lutomirski }
782fd70da6aSDarren Hart (VMware) static DEVICE_ATTR_RO(setable);
783d4fc91adSAndy Lutomirski 
784d4fc91adSAndy Lutomirski static struct attribute *wmi_data_attrs[] = {
785d4fc91adSAndy Lutomirski 	&dev_attr_object_id.attr,
786fd70da6aSDarren Hart (VMware) 	&dev_attr_setable.attr,
787cd3e3d29SBarnabás Pőcze 	NULL
788d4fc91adSAndy Lutomirski };
789d4fc91adSAndy Lutomirski ATTRIBUTE_GROUPS(wmi_data);
790d4fc91adSAndy Lutomirski 
791d4fc91adSAndy Lutomirski static struct attribute *wmi_method_attrs[] = {
792d79b1074SAndy Lutomirski 	&dev_attr_object_id.attr,
793cd3e3d29SBarnabás Pőcze 	NULL
794d79b1074SAndy Lutomirski };
795d4fc91adSAndy Lutomirski ATTRIBUTE_GROUPS(wmi_method);
796d79b1074SAndy Lutomirski 
7971caab3c1SMatthew Garrett static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env)
7981caab3c1SMatthew Garrett {
799844af950SAndy Lutomirski 	struct wmi_block *wblock = dev_to_wblock(dev);
8001caab3c1SMatthew Garrett 
80167f472fdSBarnabás Pőcze 	if (add_uevent_var(env, "MODALIAS=wmi:%pUL", &wblock->gblock.guid))
8021caab3c1SMatthew Garrett 		return -ENOMEM;
8031caab3c1SMatthew Garrett 
80467f472fdSBarnabás Pőcze 	if (add_uevent_var(env, "WMI_GUID=%pUL", &wblock->gblock.guid))
8051caab3c1SMatthew Garrett 		return -ENOMEM;
8061caab3c1SMatthew Garrett 
8071caab3c1SMatthew Garrett 	return 0;
8081caab3c1SMatthew Garrett }
8091caab3c1SMatthew Garrett 
810844af950SAndy Lutomirski static void wmi_dev_release(struct device *dev)
8111caab3c1SMatthew Garrett {
812844af950SAndy Lutomirski 	struct wmi_block *wblock = dev_to_wblock(dev);
813c64eefd4SDmitry Torokhov 
814844af950SAndy Lutomirski 	kfree(wblock);
8151caab3c1SMatthew Garrett }
8161caab3c1SMatthew Garrett 
817844af950SAndy Lutomirski static int wmi_dev_match(struct device *dev, struct device_driver *driver)
818844af950SAndy Lutomirski {
819e7b2e334SBarnabás Pőcze 	struct wmi_driver *wmi_driver = drv_to_wdrv(driver);
820844af950SAndy Lutomirski 	struct wmi_block *wblock = dev_to_wblock(dev);
821844af950SAndy Lutomirski 	const struct wmi_device_id *id = wmi_driver->id_table;
822844af950SAndy Lutomirski 
823c355ec65SMattias Jacobsson 	if (id == NULL)
824c355ec65SMattias Jacobsson 		return 0;
825c355ec65SMattias Jacobsson 
826eacc95eaSMattias Jacobsson 	while (*id->guid_string) {
827f9dffc14SAndy Shevchenko 		guid_t driver_guid;
828844af950SAndy Lutomirski 
829f9dffc14SAndy Shevchenko 		if (WARN_ON(guid_parse(id->guid_string, &driver_guid)))
830844af950SAndy Lutomirski 			continue;
83167f472fdSBarnabás Pőcze 		if (guid_equal(&driver_guid, &wblock->gblock.guid))
832844af950SAndy Lutomirski 			return 1;
833844af950SAndy Lutomirski 
834844af950SAndy Lutomirski 		id++;
835844af950SAndy Lutomirski 	}
836844af950SAndy Lutomirski 
837844af950SAndy Lutomirski 	return 0;
838844af950SAndy Lutomirski }
83944b6b766SMario Limonciello static int wmi_char_open(struct inode *inode, struct file *filp)
84044b6b766SMario Limonciello {
84144b6b766SMario Limonciello 	const char *driver_name = filp->f_path.dentry->d_iname;
84243aacf83SBarnabás Pőcze 	struct wmi_block *wblock;
84343aacf83SBarnabás Pőcze 	struct wmi_block *next;
84444b6b766SMario Limonciello 
84544b6b766SMario Limonciello 	list_for_each_entry_safe(wblock, next, &wmi_block_list, list) {
84644b6b766SMario Limonciello 		if (!wblock->dev.dev.driver)
84744b6b766SMario Limonciello 			continue;
84844b6b766SMario Limonciello 		if (strcmp(driver_name, wblock->dev.dev.driver->name) == 0) {
84944b6b766SMario Limonciello 			filp->private_data = wblock;
85044b6b766SMario Limonciello 			break;
85144b6b766SMario Limonciello 		}
85244b6b766SMario Limonciello 	}
85344b6b766SMario Limonciello 
85444b6b766SMario Limonciello 	if (!filp->private_data)
85544b6b766SMario Limonciello 		return -ENODEV;
85644b6b766SMario Limonciello 
85744b6b766SMario Limonciello 	return nonseekable_open(inode, filp);
85844b6b766SMario Limonciello }
85944b6b766SMario Limonciello 
86044b6b766SMario Limonciello static ssize_t wmi_char_read(struct file *filp, char __user *buffer,
86144b6b766SMario Limonciello 			     size_t length, loff_t *offset)
86244b6b766SMario Limonciello {
86344b6b766SMario Limonciello 	struct wmi_block *wblock = filp->private_data;
86444b6b766SMario Limonciello 
86544b6b766SMario Limonciello 	return simple_read_from_buffer(buffer, length, offset,
86644b6b766SMario Limonciello 				       &wblock->req_buf_size,
86744b6b766SMario Limonciello 				       sizeof(wblock->req_buf_size));
86844b6b766SMario Limonciello }
86944b6b766SMario Limonciello 
87044b6b766SMario Limonciello static long wmi_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
87144b6b766SMario Limonciello {
87244b6b766SMario Limonciello 	struct wmi_ioctl_buffer __user *input =
87344b6b766SMario Limonciello 		(struct wmi_ioctl_buffer __user *) arg;
87444b6b766SMario Limonciello 	struct wmi_block *wblock = filp->private_data;
87543aacf83SBarnabás Pőcze 	struct wmi_ioctl_buffer *buf;
87643aacf83SBarnabás Pőcze 	struct wmi_driver *wdriver;
87744b6b766SMario Limonciello 	int ret;
87844b6b766SMario Limonciello 
87944b6b766SMario Limonciello 	if (_IOC_TYPE(cmd) != WMI_IOC)
88044b6b766SMario Limonciello 		return -ENOTTY;
88144b6b766SMario Limonciello 
88244b6b766SMario Limonciello 	/* make sure we're not calling a higher instance than exists*/
88344b6b766SMario Limonciello 	if (_IOC_NR(cmd) >= wblock->gblock.instance_count)
88444b6b766SMario Limonciello 		return -EINVAL;
88544b6b766SMario Limonciello 
88644b6b766SMario Limonciello 	mutex_lock(&wblock->char_mutex);
88744b6b766SMario Limonciello 	buf = wblock->handler_data;
88844b6b766SMario Limonciello 	if (get_user(buf->length, &input->length)) {
88944b6b766SMario Limonciello 		dev_dbg(&wblock->dev.dev, "Read length from user failed\n");
89044b6b766SMario Limonciello 		ret = -EFAULT;
89144b6b766SMario Limonciello 		goto out_ioctl;
89244b6b766SMario Limonciello 	}
89344b6b766SMario Limonciello 	/* if it's too small, abort */
89444b6b766SMario Limonciello 	if (buf->length < wblock->req_buf_size) {
89544b6b766SMario Limonciello 		dev_err(&wblock->dev.dev,
89644b6b766SMario Limonciello 			"Buffer %lld too small, need at least %lld\n",
89744b6b766SMario Limonciello 			buf->length, wblock->req_buf_size);
89844b6b766SMario Limonciello 		ret = -EINVAL;
89944b6b766SMario Limonciello 		goto out_ioctl;
90044b6b766SMario Limonciello 	}
90144b6b766SMario Limonciello 	/* if it's too big, warn, driver will only use what is needed */
90244b6b766SMario Limonciello 	if (buf->length > wblock->req_buf_size)
90344b6b766SMario Limonciello 		dev_warn(&wblock->dev.dev,
90444b6b766SMario Limonciello 			"Buffer %lld is bigger than required %lld\n",
90544b6b766SMario Limonciello 			buf->length, wblock->req_buf_size);
90644b6b766SMario Limonciello 
90744b6b766SMario Limonciello 	/* copy the structure from userspace */
90844b6b766SMario Limonciello 	if (copy_from_user(buf, input, wblock->req_buf_size)) {
90944b6b766SMario Limonciello 		dev_dbg(&wblock->dev.dev, "Copy %llu from user failed\n",
91044b6b766SMario Limonciello 			wblock->req_buf_size);
91144b6b766SMario Limonciello 		ret = -EFAULT;
91244b6b766SMario Limonciello 		goto out_ioctl;
91344b6b766SMario Limonciello 	}
91444b6b766SMario Limonciello 
91544b6b766SMario Limonciello 	/* let the driver do any filtering and do the call */
916e7b2e334SBarnabás Pőcze 	wdriver = drv_to_wdrv(wblock->dev.dev.driver);
9175e3e2297SMario Limonciello 	if (!try_module_get(wdriver->driver.owner)) {
9185e3e2297SMario Limonciello 		ret = -EBUSY;
9195e3e2297SMario Limonciello 		goto out_ioctl;
9205e3e2297SMario Limonciello 	}
92144b6b766SMario Limonciello 	ret = wdriver->filter_callback(&wblock->dev, cmd, buf);
92244b6b766SMario Limonciello 	module_put(wdriver->driver.owner);
92344b6b766SMario Limonciello 	if (ret)
92444b6b766SMario Limonciello 		goto out_ioctl;
92544b6b766SMario Limonciello 
92644b6b766SMario Limonciello 	/* return the result (only up to our internal buffer size) */
92744b6b766SMario Limonciello 	if (copy_to_user(input, buf, wblock->req_buf_size)) {
92844b6b766SMario Limonciello 		dev_dbg(&wblock->dev.dev, "Copy %llu to user failed\n",
92944b6b766SMario Limonciello 			wblock->req_buf_size);
93044b6b766SMario Limonciello 		ret = -EFAULT;
93144b6b766SMario Limonciello 	}
93244b6b766SMario Limonciello 
93344b6b766SMario Limonciello out_ioctl:
93444b6b766SMario Limonciello 	mutex_unlock(&wblock->char_mutex);
93544b6b766SMario Limonciello 	return ret;
93644b6b766SMario Limonciello }
93744b6b766SMario Limonciello 
93844b6b766SMario Limonciello static const struct file_operations wmi_fops = {
93944b6b766SMario Limonciello 	.owner		= THIS_MODULE,
94044b6b766SMario Limonciello 	.read		= wmi_char_read,
94144b6b766SMario Limonciello 	.open		= wmi_char_open,
94244b6b766SMario Limonciello 	.unlocked_ioctl	= wmi_ioctl,
9431832f2d8SArnd Bergmann 	.compat_ioctl	= compat_ptr_ioctl,
94444b6b766SMario Limonciello };
945844af950SAndy Lutomirski 
946844af950SAndy Lutomirski static int wmi_dev_probe(struct device *dev)
947844af950SAndy Lutomirski {
948844af950SAndy Lutomirski 	struct wmi_block *wblock = dev_to_wblock(dev);
949e7b2e334SBarnabás Pőcze 	struct wmi_driver *wdriver = drv_to_wdrv(dev->driver);
950844af950SAndy Lutomirski 	int ret = 0;
95144b6b766SMario Limonciello 	char *buf;
952844af950SAndy Lutomirski 
953285dd01aSBarnabás Pőcze 	if (ACPI_FAILURE(wmi_method_enable(wblock, true)))
954844af950SAndy Lutomirski 		dev_warn(dev, "failed to enable device -- probing anyway\n");
955844af950SAndy Lutomirski 
956844af950SAndy Lutomirski 	if (wdriver->probe) {
957440c4983SMattias Jacobsson 		ret = wdriver->probe(dev_to_wdev(dev),
958440c4983SMattias Jacobsson 				find_guid_context(wblock, wdriver));
95944b6b766SMario Limonciello 		if (ret != 0)
96044b6b766SMario Limonciello 			goto probe_failure;
961844af950SAndy Lutomirski 	}
962844af950SAndy Lutomirski 
96344b6b766SMario Limonciello 	/* driver wants a character device made */
96444b6b766SMario Limonciello 	if (wdriver->filter_callback) {
96544b6b766SMario Limonciello 		/* check that required buffer size declared by driver or MOF */
96644b6b766SMario Limonciello 		if (!wblock->req_buf_size) {
96744b6b766SMario Limonciello 			dev_err(&wblock->dev.dev,
96844b6b766SMario Limonciello 				"Required buffer size not set\n");
96944b6b766SMario Limonciello 			ret = -EINVAL;
97044b6b766SMario Limonciello 			goto probe_failure;
97144b6b766SMario Limonciello 		}
97244b6b766SMario Limonciello 
9736fb74107SKees Cook 		wblock->handler_data = kmalloc(wblock->req_buf_size,
9746fb74107SKees Cook 					       GFP_KERNEL);
97544b6b766SMario Limonciello 		if (!wblock->handler_data) {
97644b6b766SMario Limonciello 			ret = -ENOMEM;
97744b6b766SMario Limonciello 			goto probe_failure;
97844b6b766SMario Limonciello 		}
97944b6b766SMario Limonciello 
9807f166addSAndy Shevchenko 		buf = kasprintf(GFP_KERNEL, "wmi/%s", wdriver->driver.name);
98144b6b766SMario Limonciello 		if (!buf) {
98244b6b766SMario Limonciello 			ret = -ENOMEM;
98344b6b766SMario Limonciello 			goto probe_string_failure;
98444b6b766SMario Limonciello 		}
98544b6b766SMario Limonciello 		wblock->char_dev.minor = MISC_DYNAMIC_MINOR;
98644b6b766SMario Limonciello 		wblock->char_dev.name = buf;
98744b6b766SMario Limonciello 		wblock->char_dev.fops = &wmi_fops;
98844b6b766SMario Limonciello 		wblock->char_dev.mode = 0444;
98944b6b766SMario Limonciello 		ret = misc_register(&wblock->char_dev);
99044b6b766SMario Limonciello 		if (ret) {
991501f7e52SJoe Perches 			dev_warn(dev, "failed to register char dev: %d\n", ret);
99244b6b766SMario Limonciello 			ret = -ENOMEM;
99344b6b766SMario Limonciello 			goto probe_misc_failure;
99444b6b766SMario Limonciello 		}
99544b6b766SMario Limonciello 	}
99644b6b766SMario Limonciello 
99744b6b766SMario Limonciello 	return 0;
99844b6b766SMario Limonciello 
99944b6b766SMario Limonciello probe_misc_failure:
100044b6b766SMario Limonciello 	kfree(buf);
100144b6b766SMario Limonciello probe_string_failure:
100244b6b766SMario Limonciello 	kfree(wblock->handler_data);
100344b6b766SMario Limonciello probe_failure:
1004285dd01aSBarnabás Pőcze 	if (ACPI_FAILURE(wmi_method_enable(wblock, false)))
100544b6b766SMario Limonciello 		dev_warn(dev, "failed to disable device\n");
1006844af950SAndy Lutomirski 	return ret;
1007844af950SAndy Lutomirski }
1008844af950SAndy Lutomirski 
1009fc7a6209SUwe Kleine-König static void wmi_dev_remove(struct device *dev)
1010844af950SAndy Lutomirski {
1011844af950SAndy Lutomirski 	struct wmi_block *wblock = dev_to_wblock(dev);
1012e7b2e334SBarnabás Pőcze 	struct wmi_driver *wdriver = drv_to_wdrv(dev->driver);
1013844af950SAndy Lutomirski 
101444b6b766SMario Limonciello 	if (wdriver->filter_callback) {
101544b6b766SMario Limonciello 		misc_deregister(&wblock->char_dev);
101644b6b766SMario Limonciello 		kfree(wblock->char_dev.name);
10176fb74107SKees Cook 		kfree(wblock->handler_data);
101844b6b766SMario Limonciello 	}
101944b6b766SMario Limonciello 
1020844af950SAndy Lutomirski 	if (wdriver->remove)
10212b329f56SUwe Kleine-König 		wdriver->remove(dev_to_wdev(dev));
1022844af950SAndy Lutomirski 
1023285dd01aSBarnabás Pőcze 	if (ACPI_FAILURE(wmi_method_enable(wblock, false)))
1024844af950SAndy Lutomirski 		dev_warn(dev, "failed to disable device\n");
1025844af950SAndy Lutomirski }
1026844af950SAndy Lutomirski 
1027844af950SAndy Lutomirski static struct class wmi_bus_class = {
1028844af950SAndy Lutomirski 	.name = "wmi_bus",
10291caab3c1SMatthew Garrett };
10301caab3c1SMatthew Garrett 
1031844af950SAndy Lutomirski static struct bus_type wmi_bus_type = {
1032844af950SAndy Lutomirski 	.name = "wmi",
1033844af950SAndy Lutomirski 	.dev_groups = wmi_groups,
1034844af950SAndy Lutomirski 	.match = wmi_dev_match,
1035844af950SAndy Lutomirski 	.uevent = wmi_dev_uevent,
1036844af950SAndy Lutomirski 	.probe = wmi_dev_probe,
1037844af950SAndy Lutomirski 	.remove = wmi_dev_remove,
1038844af950SAndy Lutomirski };
1039844af950SAndy Lutomirski 
104069372c1dSBhumika Goyal static const struct device_type wmi_type_event = {
1041d79b1074SAndy Lutomirski 	.name = "event",
1042d79b1074SAndy Lutomirski 	.groups = wmi_event_groups,
1043d79b1074SAndy Lutomirski 	.release = wmi_dev_release,
1044d79b1074SAndy Lutomirski };
1045d79b1074SAndy Lutomirski 
104669372c1dSBhumika Goyal static const struct device_type wmi_type_method = {
1047d79b1074SAndy Lutomirski 	.name = "method",
1048d4fc91adSAndy Lutomirski 	.groups = wmi_method_groups,
1049d79b1074SAndy Lutomirski 	.release = wmi_dev_release,
1050d79b1074SAndy Lutomirski };
1051d79b1074SAndy Lutomirski 
105269372c1dSBhumika Goyal static const struct device_type wmi_type_data = {
1053d79b1074SAndy Lutomirski 	.name = "data",
1054d4fc91adSAndy Lutomirski 	.groups = wmi_data_groups,
1055d79b1074SAndy Lutomirski 	.release = wmi_dev_release,
1056d79b1074SAndy Lutomirski };
1057d79b1074SAndy Lutomirski 
1058fd70da6aSDarren Hart (VMware) static int wmi_create_device(struct device *wmi_bus_dev,
10597f5809bfSAndy Lutomirski 			     struct wmi_block *wblock,
10607f5809bfSAndy Lutomirski 			     struct acpi_device *device)
10611caab3c1SMatthew Garrett {
1062d4fc91adSAndy Lutomirski 	struct acpi_device_info *info;
106357f2ce89SBarnabás Pőcze 	char method[WMI_ACPI_METHOD_NAME_SIZE];
1064d4fc91adSAndy Lutomirski 	int result;
1065d4fc91adSAndy Lutomirski 
106684eacf7eSBarnabás Pőcze 	if (wblock->gblock.flags & ACPI_WMI_EVENT) {
1067fd70da6aSDarren Hart (VMware) 		wblock->dev.dev.type = &wmi_type_event;
1068fd70da6aSDarren Hart (VMware) 		goto out_init;
1069fd70da6aSDarren Hart (VMware) 	}
1070d4fc91adSAndy Lutomirski 
107184eacf7eSBarnabás Pőcze 	if (wblock->gblock.flags & ACPI_WMI_METHOD) {
1072fd70da6aSDarren Hart (VMware) 		wblock->dev.dev.type = &wmi_type_method;
107344b6b766SMario Limonciello 		mutex_init(&wblock->char_mutex);
1074fd70da6aSDarren Hart (VMware) 		goto out_init;
1075fd70da6aSDarren Hart (VMware) 	}
1076fd70da6aSDarren Hart (VMware) 
1077fd70da6aSDarren Hart (VMware) 	/*
1078fd70da6aSDarren Hart (VMware) 	 * Data Block Query Control Method (WQxx by convention) is
1079fd70da6aSDarren Hart (VMware) 	 * required per the WMI documentation. If it is not present,
1080fd70da6aSDarren Hart (VMware) 	 * we ignore this data block.
1081fd70da6aSDarren Hart (VMware) 	 */
108257f2ce89SBarnabás Pőcze 	get_acpi_method_name(wblock, 'Q', method);
1083d4fc91adSAndy Lutomirski 	result = get_subobj_info(device->handle, method, &info);
1084d4fc91adSAndy Lutomirski 
1085fd70da6aSDarren Hart (VMware) 	if (result) {
1086fd70da6aSDarren Hart (VMware) 		dev_warn(wmi_bus_dev,
1087501f7e52SJoe Perches 			 "%s data block query control method not found\n",
1088fd70da6aSDarren Hart (VMware) 			 method);
1089fd70da6aSDarren Hart (VMware) 		return result;
1090fd70da6aSDarren Hart (VMware) 	}
1091fd70da6aSDarren Hart (VMware) 
1092fd70da6aSDarren Hart (VMware) 	wblock->dev.dev.type = &wmi_type_data;
1093d4fc91adSAndy Lutomirski 
1094d4fc91adSAndy Lutomirski 	/*
1095d4fc91adSAndy Lutomirski 	 * The Microsoft documentation specifically states:
1096d4fc91adSAndy Lutomirski 	 *
1097d4fc91adSAndy Lutomirski 	 *   Data blocks registered with only a single instance
1098d4fc91adSAndy Lutomirski 	 *   can ignore the parameter.
1099d4fc91adSAndy Lutomirski 	 *
1100fd70da6aSDarren Hart (VMware) 	 * ACPICA will get mad at us if we call the method with the wrong number
1101fd70da6aSDarren Hart (VMware) 	 * of arguments, so check what our method expects.  (On some Dell
1102fd70da6aSDarren Hart (VMware) 	 * laptops, WQxx may not be a method at all.)
1103d4fc91adSAndy Lutomirski 	 */
1104fd70da6aSDarren Hart (VMware) 	if (info->type != ACPI_TYPE_METHOD || info->param_count == 0)
1105d4fc91adSAndy Lutomirski 		wblock->read_takes_no_args = true;
1106d4fc91adSAndy Lutomirski 
1107d4fc91adSAndy Lutomirski 	kfree(info);
1108d4fc91adSAndy Lutomirski 
110957f2ce89SBarnabás Pőcze 	get_acpi_method_name(wblock, 'S', method);
1110d4fc91adSAndy Lutomirski 	result = get_subobj_info(device->handle, method, NULL);
1111d4fc91adSAndy Lutomirski 
1112fd70da6aSDarren Hart (VMware) 	if (result == 0)
1113fd70da6aSDarren Hart (VMware) 		wblock->dev.setable = true;
1114d4fc91adSAndy Lutomirski 
1115fd70da6aSDarren Hart (VMware)  out_init:
1116fd70da6aSDarren Hart (VMware) 	wblock->dev.dev.bus = &wmi_bus_type;
1117fd70da6aSDarren Hart (VMware) 	wblock->dev.dev.parent = wmi_bus_dev;
1118fd70da6aSDarren Hart (VMware) 
111967f472fdSBarnabás Pőcze 	dev_set_name(&wblock->dev.dev, "%pUL", &wblock->gblock.guid);
1120c64eefd4SDmitry Torokhov 
11216ee50aaaSDarren Hart (VMware) 	device_initialize(&wblock->dev.dev);
1122fd70da6aSDarren Hart (VMware) 
1123fd70da6aSDarren Hart (VMware) 	return 0;
11241caab3c1SMatthew Garrett }
11251caab3c1SMatthew Garrett 
1126b0e86302SAndy Lutomirski static void wmi_free_devices(struct acpi_device *device)
11271caab3c1SMatthew Garrett {
1128c64eefd4SDmitry Torokhov 	struct wmi_block *wblock, *next;
11291caab3c1SMatthew Garrett 
11301caab3c1SMatthew Garrett 	/* Delete devices for all the GUIDs */
1131023b9565SDmitry Torokhov 	list_for_each_entry_safe(wblock, next, &wmi_block_list, list) {
1132b0e86302SAndy Lutomirski 		if (wblock->acpi_device == device) {
1133023b9565SDmitry Torokhov 			list_del(&wblock->list);
1134844af950SAndy Lutomirski 			device_unregister(&wblock->dev.dev);
1135023b9565SDmitry Torokhov 		}
11361caab3c1SMatthew Garrett 	}
1137b0e86302SAndy Lutomirski }
11381caab3c1SMatthew Garrett 
113967f472fdSBarnabás Pőcze static bool guid_already_parsed(struct acpi_device *device, const guid_t *guid)
1140d1f9e497SCarlos Corbacho {
1141d1f9e497SCarlos Corbacho 	struct wmi_block *wblock;
1142d1f9e497SCarlos Corbacho 
1143b0e86302SAndy Lutomirski 	list_for_each_entry(wblock, &wmi_block_list, list) {
114467f472fdSBarnabás Pőcze 		if (guid_equal(&wblock->gblock.guid, guid)) {
1145b0e86302SAndy Lutomirski 			/*
1146b0e86302SAndy Lutomirski 			 * Because we historically didn't track the relationship
1147b0e86302SAndy Lutomirski 			 * between GUIDs and ACPI nodes, we don't know whether
1148b0e86302SAndy Lutomirski 			 * we need to suppress GUIDs that are unique on a
1149b0e86302SAndy Lutomirski 			 * given node but duplicated across nodes.
1150b0e86302SAndy Lutomirski 			 */
1151b0e86302SAndy Lutomirski 			dev_warn(&device->dev, "duplicate WMI GUID %pUL (first instance was on %s)\n",
1152b0e86302SAndy Lutomirski 				 guid, dev_name(&wblock->acpi_device->dev));
1153d1f9e497SCarlos Corbacho 			return true;
1154b0e86302SAndy Lutomirski 		}
1155b0e86302SAndy Lutomirski 	}
1156c64eefd4SDmitry Torokhov 
1157d1f9e497SCarlos Corbacho 	return false;
1158d1f9e497SCarlos Corbacho }
1159d1f9e497SCarlos Corbacho 
11601caab3c1SMatthew Garrett /*
1161b4f9fe12SLen Brown  * Parse the _WDG method for the GUID data blocks
1162b4f9fe12SLen Brown  */
1163844af950SAndy Lutomirski static int parse_wdg(struct device *wmi_bus_dev, struct acpi_device *device)
1164b4f9fe12SLen Brown {
1165b4f9fe12SLen Brown 	struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
116637830662SDmitry Torokhov 	const struct guid_block *gblock;
11676ee50aaaSDarren Hart (VMware) 	struct wmi_block *wblock, *next;
11686ee50aaaSDarren Hart (VMware) 	union acpi_object *obj;
1169b4f9fe12SLen Brown 	acpi_status status;
11706ee50aaaSDarren Hart (VMware) 	int retval = 0;
1171b4f9fe12SLen Brown 	u32 i, total;
1172b4f9fe12SLen Brown 
11737f5809bfSAndy Lutomirski 	status = acpi_evaluate_object(device->handle, "_WDG", NULL, &out);
1174b4f9fe12SLen Brown 	if (ACPI_FAILURE(status))
1175c64eefd4SDmitry Torokhov 		return -ENXIO;
1176b4f9fe12SLen Brown 
1177c06a2fdeSBarnabás Pőcze 	obj = out.pointer;
11783d2c63ebSDmitry Torokhov 	if (!obj)
1179c64eefd4SDmitry Torokhov 		return -ENXIO;
1180b4f9fe12SLen Brown 
118164ed0ab8SDmitry Torokhov 	if (obj->type != ACPI_TYPE_BUFFER) {
1182c64eefd4SDmitry Torokhov 		retval = -ENXIO;
118364ed0ab8SDmitry Torokhov 		goto out_free_pointer;
118464ed0ab8SDmitry Torokhov 	}
1185b4f9fe12SLen Brown 
118637830662SDmitry Torokhov 	gblock = (const struct guid_block *)obj->buffer.pointer;
1187b4f9fe12SLen Brown 	total = obj->buffer.length / sizeof(struct guid_block);
1188b4f9fe12SLen Brown 
1189b4f9fe12SLen Brown 	for (i = 0; i < total; i++) {
1190a929aae0SThomas Renninger 		if (debug_dump_wdg)
1191a929aae0SThomas Renninger 			wmi_dump_wdg(&gblock[i]);
1192a929aae0SThomas Renninger 
1193a1c31bcdSAndy Lutomirski 		/*
1194a1c31bcdSAndy Lutomirski 		 * Some WMI devices, like those for nVidia hooks, have a
1195a1c31bcdSAndy Lutomirski 		 * duplicate GUID. It's not clear what we should do in this
1196a1c31bcdSAndy Lutomirski 		 * case yet, so for now, we'll just ignore the duplicate
1197a1c31bcdSAndy Lutomirski 		 * for device creation.
1198a1c31bcdSAndy Lutomirski 		 */
119967f472fdSBarnabás Pőcze 		if (guid_already_parsed(device, &gblock[i].guid))
1200a1c31bcdSAndy Lutomirski 			continue;
1201a1c31bcdSAndy Lutomirski 
12027410b8e6SBarnabás Pőcze 		wblock = kzalloc(sizeof(*wblock), GFP_KERNEL);
12036ee50aaaSDarren Hart (VMware) 		if (!wblock) {
12046ee50aaaSDarren Hart (VMware) 			retval = -ENOMEM;
12056ee50aaaSDarren Hart (VMware) 			break;
12066ee50aaaSDarren Hart (VMware) 		}
120758f6425eSColin King 
1208b0e86302SAndy Lutomirski 		wblock->acpi_device = device;
120958f6425eSColin King 		wblock->gblock = gblock[i];
121058f6425eSColin King 
121184eacf7eSBarnabás Pőcze 		retval = wmi_create_device(wmi_bus_dev, wblock, device);
1212fd70da6aSDarren Hart (VMware) 		if (retval) {
1213fd70da6aSDarren Hart (VMware) 			kfree(wblock);
1214fd70da6aSDarren Hart (VMware) 			continue;
1215fd70da6aSDarren Hart (VMware) 		}
121658f6425eSColin King 
121758f6425eSColin King 		list_add_tail(&wblock->list, &wmi_block_list);
1218b4f9fe12SLen Brown 
1219fc3155b2SThomas Renninger 		if (debug_event) {
1220fc3155b2SThomas Renninger 			wblock->handler = wmi_notify_debug;
1221285dd01aSBarnabás Pőcze 			wmi_method_enable(wblock, true);
1222fc3155b2SThomas Renninger 		}
1223b4f9fe12SLen Brown 	}
1224b4f9fe12SLen Brown 
12256ee50aaaSDarren Hart (VMware) 	/*
12266ee50aaaSDarren Hart (VMware) 	 * Now that all of the devices are created, add them to the
12276ee50aaaSDarren Hart (VMware) 	 * device tree and probe subdrivers.
12286ee50aaaSDarren Hart (VMware) 	 */
12296ee50aaaSDarren Hart (VMware) 	list_for_each_entry_safe(wblock, next, &wmi_block_list, list) {
12306ee50aaaSDarren Hart (VMware) 		if (wblock->acpi_device != device)
12316ee50aaaSDarren Hart (VMware) 			continue;
12326ee50aaaSDarren Hart (VMware) 
12336ee50aaaSDarren Hart (VMware) 		retval = device_add(&wblock->dev.dev);
12346ee50aaaSDarren Hart (VMware) 		if (retval) {
1235501f7e52SJoe Perches 			dev_err(wmi_bus_dev, "failed to register %pUL\n",
123667f472fdSBarnabás Pőcze 				&wblock->gblock.guid);
12376ee50aaaSDarren Hart (VMware) 			if (debug_event)
1238285dd01aSBarnabás Pőcze 				wmi_method_enable(wblock, false);
12396ee50aaaSDarren Hart (VMware) 			list_del(&wblock->list);
12406ee50aaaSDarren Hart (VMware) 			put_device(&wblock->dev.dev);
12416ee50aaaSDarren Hart (VMware) 		}
12426ee50aaaSDarren Hart (VMware) 	}
1243c64eefd4SDmitry Torokhov 
1244a5167c5bSAxel Lin out_free_pointer:
1245a5167c5bSAxel Lin 	kfree(out.pointer);
1246c64eefd4SDmitry Torokhov 	return retval;
1247b4f9fe12SLen Brown }
1248b4f9fe12SLen Brown 
1249b4f9fe12SLen Brown /*
1250b4f9fe12SLen Brown  * WMI can have EmbeddedControl access regions. In which case, we just want to
1251b4f9fe12SLen Brown  * hand these off to the EC driver.
1252b4f9fe12SLen Brown  */
1253b4f9fe12SLen Brown static acpi_status
1254b4f9fe12SLen Brown acpi_wmi_ec_space_handler(u32 function, acpi_physical_address address,
1255439913ffSLin Ming 			  u32 bits, u64 *value,
1256b4f9fe12SLen Brown 			  void *handler_context, void *region_context)
1257b4f9fe12SLen Brown {
1258b4f9fe12SLen Brown 	int result = 0, i = 0;
1259b4f9fe12SLen Brown 	u8 temp = 0;
1260b4f9fe12SLen Brown 
1261b4f9fe12SLen Brown 	if ((address > 0xFF) || !value)
1262b4f9fe12SLen Brown 		return AE_BAD_PARAMETER;
1263b4f9fe12SLen Brown 
1264b4f9fe12SLen Brown 	if (function != ACPI_READ && function != ACPI_WRITE)
1265b4f9fe12SLen Brown 		return AE_BAD_PARAMETER;
1266b4f9fe12SLen Brown 
1267b4f9fe12SLen Brown 	if (bits != 8)
1268b4f9fe12SLen Brown 		return AE_BAD_PARAMETER;
1269b4f9fe12SLen Brown 
1270b4f9fe12SLen Brown 	if (function == ACPI_READ) {
1271b4f9fe12SLen Brown 		result = ec_read(address, &temp);
1272439913ffSLin Ming 		(*value) |= ((u64)temp) << i;
1273b4f9fe12SLen Brown 	} else {
1274b4f9fe12SLen Brown 		temp = 0xff & ((*value) >> i);
1275b4f9fe12SLen Brown 		result = ec_write(address, temp);
1276b4f9fe12SLen Brown 	}
1277b4f9fe12SLen Brown 
1278b4f9fe12SLen Brown 	switch (result) {
1279b4f9fe12SLen Brown 	case -EINVAL:
1280b4f9fe12SLen Brown 		return AE_BAD_PARAMETER;
1281b4f9fe12SLen Brown 	case -ENODEV:
1282b4f9fe12SLen Brown 		return AE_NOT_FOUND;
1283b4f9fe12SLen Brown 	case -ETIME:
1284b4f9fe12SLen Brown 		return AE_TIME;
1285b4f9fe12SLen Brown 	default:
1286b4f9fe12SLen Brown 		return AE_OK;
1287b4f9fe12SLen Brown 	}
1288b4f9fe12SLen Brown }
1289b4f9fe12SLen Brown 
12901686f544SAndy Lutomirski static void acpi_wmi_notify_handler(acpi_handle handle, u32 event,
12911686f544SAndy Lutomirski 				    void *context)
1292b4f9fe12SLen Brown {
1293b4f9fe12SLen Brown 	struct wmi_block *wblock;
12941686f544SAndy Lutomirski 	bool found_it = false;
1295b4f9fe12SLen Brown 
1296cedb3b2aSAndy Shevchenko 	list_for_each_entry(wblock, &wmi_block_list, list) {
1297f5431bf1SBarnabás Pőcze 		struct guid_block *block = &wblock->gblock;
1298b4f9fe12SLen Brown 
12991686f544SAndy Lutomirski 		if (wblock->acpi_device->handle == handle &&
1300b0e86302SAndy Lutomirski 		    (block->flags & ACPI_WMI_EVENT) &&
13013ecace31SBarnabás Pőcze 		    (block->notify_id == event)) {
13021686f544SAndy Lutomirski 			found_it = true;
13031686f544SAndy Lutomirski 			break;
13041686f544SAndy Lutomirski 		}
13051686f544SAndy Lutomirski 	}
13061686f544SAndy Lutomirski 
13071686f544SAndy Lutomirski 	if (!found_it)
13081686f544SAndy Lutomirski 		return;
13091686f544SAndy Lutomirski 
13101686f544SAndy Lutomirski 	/* If a driver is bound, then notify the driver. */
13111686f544SAndy Lutomirski 	if (wblock->dev.dev.driver) {
1312e7b2e334SBarnabás Pőcze 		struct wmi_driver *driver = drv_to_wdrv(wblock->dev.dev.driver);
13131686f544SAndy Lutomirski 		struct acpi_buffer evdata = { ACPI_ALLOCATE_BUFFER, NULL };
13141686f544SAndy Lutomirski 		acpi_status status;
13151686f544SAndy Lutomirski 
1316*25be44f6SBarnabás Pőcze 		status = get_event_data(wblock, &evdata);
13171686f544SAndy Lutomirski 		if (ACPI_FAILURE(status)) {
1318*25be44f6SBarnabás Pőcze 			dev_warn(&wblock->dev.dev, "failed to get event data\n");
13191686f544SAndy Lutomirski 			return;
13201686f544SAndy Lutomirski 		}
13211686f544SAndy Lutomirski 
13221686f544SAndy Lutomirski 		if (driver->notify)
1323c06a2fdeSBarnabás Pőcze 			driver->notify(&wblock->dev, evdata.pointer);
13241686f544SAndy Lutomirski 
13251686f544SAndy Lutomirski 		kfree(evdata.pointer);
13261686f544SAndy Lutomirski 	} else if (wblock->handler) {
13271686f544SAndy Lutomirski 		/* Legacy handler */
1328b4f9fe12SLen Brown 		wblock->handler(event, wblock->handler_data);
13291686f544SAndy Lutomirski 	}
13301686f544SAndy Lutomirski 
13316701cc8fSAndy Shevchenko 	if (debug_event)
13321c23ab91SBarnabás Pőcze 		pr_info("DEBUG: GUID %pUL event 0x%02X\n", &wblock->gblock.guid, event);
1333b4f9fe12SLen Brown 
1334b4f9fe12SLen Brown 	acpi_bus_generate_netlink_event(
13351686f544SAndy Lutomirski 		wblock->acpi_device->pnp.device_class,
13361686f544SAndy Lutomirski 		dev_name(&wblock->dev.dev),
1337b4f9fe12SLen Brown 		event, 0);
1338b4f9fe12SLen Brown }
1339b4f9fe12SLen Brown 
13409599ed91SAndy Lutomirski static int acpi_wmi_remove(struct platform_device *device)
1341b4f9fe12SLen Brown {
13429599ed91SAndy Lutomirski 	struct acpi_device *acpi_device = ACPI_COMPANION(&device->dev);
13439599ed91SAndy Lutomirski 
13449599ed91SAndy Lutomirski 	acpi_remove_notify_handler(acpi_device->handle, ACPI_DEVICE_NOTIFY,
13451686f544SAndy Lutomirski 				   acpi_wmi_notify_handler);
13469599ed91SAndy Lutomirski 	acpi_remove_address_space_handler(acpi_device->handle,
1347b4f9fe12SLen Brown 				ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler);
13489599ed91SAndy Lutomirski 	wmi_free_devices(acpi_device);
1349c06a2fdeSBarnabás Pőcze 	device_unregister(dev_get_drvdata(&device->dev));
1350b4f9fe12SLen Brown 
1351b4f9fe12SLen Brown 	return 0;
1352b4f9fe12SLen Brown }
1353b4f9fe12SLen Brown 
13549599ed91SAndy Lutomirski static int acpi_wmi_probe(struct platform_device *device)
1355b4f9fe12SLen Brown {
13569599ed91SAndy Lutomirski 	struct acpi_device *acpi_device;
1357844af950SAndy Lutomirski 	struct device *wmi_bus_dev;
1358b4f9fe12SLen Brown 	acpi_status status;
1359c64eefd4SDmitry Torokhov 	int error;
1360b4f9fe12SLen Brown 
13619599ed91SAndy Lutomirski 	acpi_device = ACPI_COMPANION(&device->dev);
13629599ed91SAndy Lutomirski 	if (!acpi_device) {
13639599ed91SAndy Lutomirski 		dev_err(&device->dev, "ACPI companion is missing\n");
13649599ed91SAndy Lutomirski 		return -ENODEV;
13659599ed91SAndy Lutomirski 	}
13669599ed91SAndy Lutomirski 
13679599ed91SAndy Lutomirski 	status = acpi_install_address_space_handler(acpi_device->handle,
1368b4f9fe12SLen Brown 						    ACPI_ADR_SPACE_EC,
1369b4f9fe12SLen Brown 						    &acpi_wmi_ec_space_handler,
1370b4f9fe12SLen Brown 						    NULL, NULL);
13715212cd67SDmitry Torokhov 	if (ACPI_FAILURE(status)) {
137246492ee4SAndy Lutomirski 		dev_err(&device->dev, "Error installing EC region handler\n");
1373b4f9fe12SLen Brown 		return -ENODEV;
13745212cd67SDmitry Torokhov 	}
1375b4f9fe12SLen Brown 
13769599ed91SAndy Lutomirski 	status = acpi_install_notify_handler(acpi_device->handle,
13779599ed91SAndy Lutomirski 					     ACPI_DEVICE_NOTIFY,
13781686f544SAndy Lutomirski 					     acpi_wmi_notify_handler,
13791686f544SAndy Lutomirski 					     NULL);
13801686f544SAndy Lutomirski 	if (ACPI_FAILURE(status)) {
13811686f544SAndy Lutomirski 		dev_err(&device->dev, "Error installing notify handler\n");
13821686f544SAndy Lutomirski 		error = -ENODEV;
13831686f544SAndy Lutomirski 		goto err_remove_ec_handler;
13841686f544SAndy Lutomirski 	}
13851686f544SAndy Lutomirski 
1386844af950SAndy Lutomirski 	wmi_bus_dev = device_create(&wmi_bus_class, &device->dev, MKDEV(0, 0),
1387844af950SAndy Lutomirski 				    NULL, "wmi_bus-%s", dev_name(&device->dev));
1388844af950SAndy Lutomirski 	if (IS_ERR(wmi_bus_dev)) {
1389844af950SAndy Lutomirski 		error = PTR_ERR(wmi_bus_dev);
13901686f544SAndy Lutomirski 		goto err_remove_notify_handler;
1391844af950SAndy Lutomirski 	}
13929599ed91SAndy Lutomirski 	dev_set_drvdata(&device->dev, wmi_bus_dev);
1393844af950SAndy Lutomirski 
13949599ed91SAndy Lutomirski 	error = parse_wdg(wmi_bus_dev, acpi_device);
1395c64eefd4SDmitry Torokhov 	if (error) {
13968e07514dSDmitry Torokhov 		pr_err("Failed to parse WDG method\n");
1397844af950SAndy Lutomirski 		goto err_remove_busdev;
1398b4f9fe12SLen Brown 	}
1399b4f9fe12SLen Brown 
1400c64eefd4SDmitry Torokhov 	return 0;
140146492ee4SAndy Lutomirski 
1402844af950SAndy Lutomirski err_remove_busdev:
140356afb8d4SYongxin Liu 	device_unregister(wmi_bus_dev);
1404844af950SAndy Lutomirski 
14051686f544SAndy Lutomirski err_remove_notify_handler:
14069599ed91SAndy Lutomirski 	acpi_remove_notify_handler(acpi_device->handle, ACPI_DEVICE_NOTIFY,
14071686f544SAndy Lutomirski 				   acpi_wmi_notify_handler);
14081686f544SAndy Lutomirski 
14091686f544SAndy Lutomirski err_remove_ec_handler:
14109599ed91SAndy Lutomirski 	acpi_remove_address_space_handler(acpi_device->handle,
141146492ee4SAndy Lutomirski 					  ACPI_ADR_SPACE_EC,
141246492ee4SAndy Lutomirski 					  &acpi_wmi_ec_space_handler);
141346492ee4SAndy Lutomirski 
141446492ee4SAndy Lutomirski 	return error;
1415b4f9fe12SLen Brown }
1416b4f9fe12SLen Brown 
1417844af950SAndy Lutomirski int __must_check __wmi_driver_register(struct wmi_driver *driver,
1418844af950SAndy Lutomirski 				       struct module *owner)
1419844af950SAndy Lutomirski {
1420844af950SAndy Lutomirski 	driver->driver.owner = owner;
1421844af950SAndy Lutomirski 	driver->driver.bus = &wmi_bus_type;
1422844af950SAndy Lutomirski 
1423844af950SAndy Lutomirski 	return driver_register(&driver->driver);
1424844af950SAndy Lutomirski }
1425844af950SAndy Lutomirski EXPORT_SYMBOL(__wmi_driver_register);
1426844af950SAndy Lutomirski 
1427844af950SAndy Lutomirski void wmi_driver_unregister(struct wmi_driver *driver)
1428844af950SAndy Lutomirski {
1429844af950SAndy Lutomirski 	driver_unregister(&driver->driver);
1430844af950SAndy Lutomirski }
1431844af950SAndy Lutomirski EXPORT_SYMBOL(wmi_driver_unregister);
1432844af950SAndy Lutomirski 
1433b4f9fe12SLen Brown static int __init acpi_wmi_init(void)
1434b4f9fe12SLen Brown {
1435c64eefd4SDmitry Torokhov 	int error;
1436b4f9fe12SLen Brown 
1437b4f9fe12SLen Brown 	if (acpi_disabled)
1438b4f9fe12SLen Brown 		return -ENODEV;
1439b4f9fe12SLen Brown 
1440844af950SAndy Lutomirski 	error = class_register(&wmi_bus_class);
1441c64eefd4SDmitry Torokhov 	if (error)
1442c64eefd4SDmitry Torokhov 		return error;
1443b4f9fe12SLen Brown 
1444844af950SAndy Lutomirski 	error = bus_register(&wmi_bus_type);
1445844af950SAndy Lutomirski 	if (error)
1446844af950SAndy Lutomirski 		goto err_unreg_class;
1447844af950SAndy Lutomirski 
14489599ed91SAndy Lutomirski 	error = platform_driver_register(&acpi_wmi_driver);
1449c64eefd4SDmitry Torokhov 	if (error) {
1450c64eefd4SDmitry Torokhov 		pr_err("Error loading mapper\n");
1451844af950SAndy Lutomirski 		goto err_unreg_bus;
14521caab3c1SMatthew Garrett 	}
14531caab3c1SMatthew Garrett 
14548e07514dSDmitry Torokhov 	return 0;
1455844af950SAndy Lutomirski 
1456844af950SAndy Lutomirski err_unreg_bus:
1457844af950SAndy Lutomirski 	bus_unregister(&wmi_bus_type);
1458844af950SAndy Lutomirski 
145997277717SAlexey Khoroshilov err_unreg_class:
146097277717SAlexey Khoroshilov 	class_unregister(&wmi_bus_class);
146197277717SAlexey Khoroshilov 
1462844af950SAndy Lutomirski 	return error;
1463b4f9fe12SLen Brown }
1464b4f9fe12SLen Brown 
1465b4f9fe12SLen Brown static void __exit acpi_wmi_exit(void)
1466b4f9fe12SLen Brown {
14679599ed91SAndy Lutomirski 	platform_driver_unregister(&acpi_wmi_driver);
1468844af950SAndy Lutomirski 	bus_unregister(&wmi_bus_type);
1469303d1fccSMario Limonciello 	class_unregister(&wmi_bus_class);
1470b4f9fe12SLen Brown }
1471b4f9fe12SLen Brown 
147298b8e4e5SRafael J. Wysocki subsys_initcall_sync(acpi_wmi_init);
1473b4f9fe12SLen Brown module_exit(acpi_wmi_exit);
1474