xref: /openbmc/linux/drivers/platform/x86/wmi.c (revision 85b4e4eb)
1b4f9fe12SLen Brown /*
2b4f9fe12SLen Brown  *  ACPI-WMI mapping driver
3b4f9fe12SLen Brown  *
4b4f9fe12SLen Brown  *  Copyright (C) 2007-2008 Carlos Corbacho <carlos@strangeworlds.co.uk>
5b4f9fe12SLen Brown  *
6b4f9fe12SLen Brown  *  GUID parsing code from ldm.c is:
7b4f9fe12SLen Brown  *   Copyright (C) 2001,2002 Richard Russon <ldm@flatcap.org>
8b4f9fe12SLen Brown  *   Copyright (c) 2001-2007 Anton Altaparmakov
9b4f9fe12SLen Brown  *   Copyright (C) 2001,2002 Jakob Kemi <jakob.kemi@telia.com>
10b4f9fe12SLen Brown  *
11b4f9fe12SLen Brown  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
12b4f9fe12SLen Brown  *
13b4f9fe12SLen Brown  *  This program is free software; you can redistribute it and/or modify
14b4f9fe12SLen Brown  *  it under the terms of the GNU General Public License as published by
15b4f9fe12SLen Brown  *  the Free Software Foundation; either version 2 of the License, or (at
16b4f9fe12SLen Brown  *  your option) any later version.
17b4f9fe12SLen Brown  *
18b4f9fe12SLen Brown  *  This program is distributed in the hope that it will be useful, but
19b4f9fe12SLen Brown  *  WITHOUT ANY WARRANTY; without even the implied warranty of
20b4f9fe12SLen Brown  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21b4f9fe12SLen Brown  *  General Public License for more details.
22b4f9fe12SLen Brown  *
23b4f9fe12SLen Brown  *  You should have received a copy of the GNU General Public License along
24b4f9fe12SLen Brown  *  with this program; if not, write to the Free Software Foundation, Inc.,
25b4f9fe12SLen Brown  *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
26b4f9fe12SLen Brown  *
27b4f9fe12SLen Brown  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
28b4f9fe12SLen Brown  */
29b4f9fe12SLen Brown 
308e07514dSDmitry Torokhov #define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
318e07514dSDmitry Torokhov 
32b4f9fe12SLen Brown #include <linux/kernel.h>
33b4f9fe12SLen Brown #include <linux/init.h>
34b4f9fe12SLen Brown #include <linux/types.h>
351caab3c1SMatthew Garrett #include <linux/device.h>
36b4f9fe12SLen Brown #include <linux/list.h>
37b4f9fe12SLen Brown #include <linux/acpi.h>
385a0e3ad6STejun Heo #include <linux/slab.h>
397c52d551SPaul Gortmaker #include <linux/module.h>
40b4f9fe12SLen Brown 
41b4f9fe12SLen Brown ACPI_MODULE_NAME("wmi");
42b4f9fe12SLen Brown MODULE_AUTHOR("Carlos Corbacho");
43b4f9fe12SLen Brown MODULE_DESCRIPTION("ACPI-WMI Mapping Driver");
44b4f9fe12SLen Brown MODULE_LICENSE("GPL");
45b4f9fe12SLen Brown 
46b4f9fe12SLen Brown #define ACPI_WMI_CLASS "wmi"
47b4f9fe12SLen Brown 
48762e1a2fSDmitry Torokhov static LIST_HEAD(wmi_block_list);
49b4f9fe12SLen Brown 
50b4f9fe12SLen Brown struct guid_block {
51b4f9fe12SLen Brown 	char guid[16];
52b4f9fe12SLen Brown 	union {
53b4f9fe12SLen Brown 		char object_id[2];
54b4f9fe12SLen Brown 		struct {
55b4f9fe12SLen Brown 			unsigned char notify_id;
56b4f9fe12SLen Brown 			unsigned char reserved;
57b4f9fe12SLen Brown 		};
58b4f9fe12SLen Brown 	};
59b4f9fe12SLen Brown 	u8 instance_count;
60b4f9fe12SLen Brown 	u8 flags;
61b4f9fe12SLen Brown };
62b4f9fe12SLen Brown 
63b4f9fe12SLen Brown struct wmi_block {
64b4f9fe12SLen Brown 	struct list_head list;
65b4f9fe12SLen Brown 	struct guid_block gblock;
66b4f9fe12SLen Brown 	acpi_handle handle;
67b4f9fe12SLen Brown 	wmi_notify_handler handler;
68b4f9fe12SLen Brown 	void *handler_data;
69c64eefd4SDmitry Torokhov 	struct device dev;
70b4f9fe12SLen Brown };
71b4f9fe12SLen Brown 
72b4f9fe12SLen Brown 
73b4f9fe12SLen Brown /*
74b4f9fe12SLen Brown  * If the GUID data block is marked as expensive, we must enable and
75b4f9fe12SLen Brown  * explicitily disable data collection.
76b4f9fe12SLen Brown  */
77b4f9fe12SLen Brown #define ACPI_WMI_EXPENSIVE   0x1
78b4f9fe12SLen Brown #define ACPI_WMI_METHOD      0x2	/* GUID is a method */
79b4f9fe12SLen Brown #define ACPI_WMI_STRING      0x4	/* GUID takes & returns a string */
80b4f9fe12SLen Brown #define ACPI_WMI_EVENT       0x8	/* GUID is an event */
81b4f9fe12SLen Brown 
8290ab5ee9SRusty Russell static bool debug_event;
83fc3155b2SThomas Renninger module_param(debug_event, bool, 0444);
84fc3155b2SThomas Renninger MODULE_PARM_DESC(debug_event,
85fc3155b2SThomas Renninger 		 "Log WMI Events [0/1]");
86fc3155b2SThomas Renninger 
8790ab5ee9SRusty Russell static bool debug_dump_wdg;
88a929aae0SThomas Renninger module_param(debug_dump_wdg, bool, 0444);
89a929aae0SThomas Renninger MODULE_PARM_DESC(debug_dump_wdg,
90a929aae0SThomas Renninger 		 "Dump available WMI interfaces [0/1]");
91a929aae0SThomas Renninger 
9251fac838SRafael J. Wysocki static int acpi_wmi_remove(struct acpi_device *device);
93b4f9fe12SLen Brown static int acpi_wmi_add(struct acpi_device *device);
94f61bb939SBjorn Helgaas static void acpi_wmi_notify(struct acpi_device *device, u32 event);
95b4f9fe12SLen Brown 
96b4f9fe12SLen Brown static const struct acpi_device_id wmi_device_ids[] = {
97b4f9fe12SLen Brown 	{"PNP0C14", 0},
98b4f9fe12SLen Brown 	{"pnp0c14", 0},
99b4f9fe12SLen Brown 	{"", 0},
100b4f9fe12SLen Brown };
101b4f9fe12SLen Brown MODULE_DEVICE_TABLE(acpi, wmi_device_ids);
102b4f9fe12SLen Brown 
103b4f9fe12SLen Brown static struct acpi_driver acpi_wmi_driver = {
104b4f9fe12SLen Brown 	.name = "wmi",
105b4f9fe12SLen Brown 	.class = ACPI_WMI_CLASS,
106b4f9fe12SLen Brown 	.ids = wmi_device_ids,
107b4f9fe12SLen Brown 	.ops = {
108b4f9fe12SLen Brown 		.add = acpi_wmi_add,
109b4f9fe12SLen Brown 		.remove = acpi_wmi_remove,
110f61bb939SBjorn Helgaas 		.notify = acpi_wmi_notify,
111b4f9fe12SLen Brown 	},
112b4f9fe12SLen Brown };
113b4f9fe12SLen Brown 
114b4f9fe12SLen Brown /*
115b4f9fe12SLen Brown  * GUID parsing functions
116b4f9fe12SLen Brown  */
117b4f9fe12SLen Brown 
118b4f9fe12SLen Brown /**
119b4f9fe12SLen Brown  * wmi_parse_hexbyte - Convert a ASCII hex number to a byte
120b4f9fe12SLen Brown  * @src:  Pointer to at least 2 characters to convert.
121b4f9fe12SLen Brown  *
122b4f9fe12SLen Brown  * Convert a two character ASCII hex string to a number.
123b4f9fe12SLen Brown  *
124b4f9fe12SLen Brown  * Return:  0-255  Success, the byte was parsed correctly
125b4f9fe12SLen Brown  *          -1     Error, an invalid character was supplied
126b4f9fe12SLen Brown  */
127b4f9fe12SLen Brown static int wmi_parse_hexbyte(const u8 *src)
128b4f9fe12SLen Brown {
129b4f9fe12SLen Brown 	int h;
130392bd8b5SAndy Shevchenko 	int value;
131b4f9fe12SLen Brown 
132b4f9fe12SLen Brown 	/* high part */
133392bd8b5SAndy Shevchenko 	h = value = hex_to_bin(src[0]);
134392bd8b5SAndy Shevchenko 	if (value < 0)
135b4f9fe12SLen Brown 		return -1;
136b4f9fe12SLen Brown 
137b4f9fe12SLen Brown 	/* low part */
138392bd8b5SAndy Shevchenko 	value = hex_to_bin(src[1]);
139392bd8b5SAndy Shevchenko 	if (value >= 0)
140392bd8b5SAndy Shevchenko 		return (h << 4) | value;
141b4f9fe12SLen Brown 	return -1;
142b4f9fe12SLen Brown }
143b4f9fe12SLen Brown 
144b4f9fe12SLen Brown /**
145b4f9fe12SLen Brown  * wmi_swap_bytes - Rearrange GUID bytes to match GUID binary
146b4f9fe12SLen Brown  * @src:   Memory block holding binary GUID (16 bytes)
147b4f9fe12SLen Brown  * @dest:  Memory block to hold byte swapped binary GUID (16 bytes)
148b4f9fe12SLen Brown  *
149b4f9fe12SLen Brown  * Byte swap a binary GUID to match it's real GUID value
150b4f9fe12SLen Brown  */
151b4f9fe12SLen Brown static void wmi_swap_bytes(u8 *src, u8 *dest)
152b4f9fe12SLen Brown {
153b4f9fe12SLen Brown 	int i;
154b4f9fe12SLen Brown 
155b4f9fe12SLen Brown 	for (i = 0; i <= 3; i++)
156b4f9fe12SLen Brown 		memcpy(dest + i, src + (3 - i), 1);
157b4f9fe12SLen Brown 
158b4f9fe12SLen Brown 	for (i = 0; i <= 1; i++)
159b4f9fe12SLen Brown 		memcpy(dest + 4 + i, src + (5 - i), 1);
160b4f9fe12SLen Brown 
161b4f9fe12SLen Brown 	for (i = 0; i <= 1; i++)
162b4f9fe12SLen Brown 		memcpy(dest + 6 + i, src + (7 - i), 1);
163b4f9fe12SLen Brown 
164b4f9fe12SLen Brown 	memcpy(dest + 8, src + 8, 8);
165b4f9fe12SLen Brown }
166b4f9fe12SLen Brown 
167b4f9fe12SLen Brown /**
168b4f9fe12SLen Brown  * wmi_parse_guid - Convert GUID from ASCII to binary
169b4f9fe12SLen Brown  * @src:   36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
170b4f9fe12SLen Brown  * @dest:  Memory block to hold binary GUID (16 bytes)
171b4f9fe12SLen Brown  *
172b4f9fe12SLen Brown  * N.B. The GUID need not be NULL terminated.
173b4f9fe12SLen Brown  *
174b4f9fe12SLen Brown  * Return:  'true'   @dest contains binary GUID
175b4f9fe12SLen Brown  *          'false'  @dest contents are undefined
176b4f9fe12SLen Brown  */
177b4f9fe12SLen Brown static bool wmi_parse_guid(const u8 *src, u8 *dest)
178b4f9fe12SLen Brown {
179b4f9fe12SLen Brown 	static const int size[] = { 4, 2, 2, 2, 6 };
180b4f9fe12SLen Brown 	int i, j, v;
181b4f9fe12SLen Brown 
182b4f9fe12SLen Brown 	if (src[8]  != '-' || src[13] != '-' ||
183b4f9fe12SLen Brown 		src[18] != '-' || src[23] != '-')
184b4f9fe12SLen Brown 		return false;
185b4f9fe12SLen Brown 
186b4f9fe12SLen Brown 	for (j = 0; j < 5; j++, src++) {
187b4f9fe12SLen Brown 		for (i = 0; i < size[j]; i++, src += 2, *dest++ = v) {
188b4f9fe12SLen Brown 			v = wmi_parse_hexbyte(src);
189b4f9fe12SLen Brown 			if (v < 0)
190b4f9fe12SLen Brown 				return false;
191b4f9fe12SLen Brown 		}
192b4f9fe12SLen Brown 	}
193b4f9fe12SLen Brown 
194b4f9fe12SLen Brown 	return true;
195b4f9fe12SLen Brown }
196b4f9fe12SLen Brown 
197b4f9fe12SLen Brown static bool find_guid(const char *guid_string, struct wmi_block **out)
198b4f9fe12SLen Brown {
199b4f9fe12SLen Brown 	char tmp[16], guid_input[16];
200b4f9fe12SLen Brown 	struct wmi_block *wblock;
201b4f9fe12SLen Brown 	struct guid_block *block;
202b4f9fe12SLen Brown 	struct list_head *p;
203b4f9fe12SLen Brown 
204b4f9fe12SLen Brown 	wmi_parse_guid(guid_string, tmp);
205b4f9fe12SLen Brown 	wmi_swap_bytes(tmp, guid_input);
206b4f9fe12SLen Brown 
207762e1a2fSDmitry Torokhov 	list_for_each(p, &wmi_block_list) {
208b4f9fe12SLen Brown 		wblock = list_entry(p, struct wmi_block, list);
209b4f9fe12SLen Brown 		block = &wblock->gblock;
210b4f9fe12SLen Brown 
211b4f9fe12SLen Brown 		if (memcmp(block->guid, guid_input, 16) == 0) {
212b4f9fe12SLen Brown 			if (out)
213b4f9fe12SLen Brown 				*out = wblock;
214097c27fcSJoe Perches 			return true;
215b4f9fe12SLen Brown 		}
216b4f9fe12SLen Brown 	}
217097c27fcSJoe Perches 	return false;
218b4f9fe12SLen Brown }
219b4f9fe12SLen Brown 
220b4f9fe12SLen Brown static acpi_status wmi_method_enable(struct wmi_block *wblock, int enable)
221b4f9fe12SLen Brown {
222b4f9fe12SLen Brown 	struct guid_block *block = NULL;
223b4f9fe12SLen Brown 	char method[5];
224b4f9fe12SLen Brown 	acpi_status status;
225b4f9fe12SLen Brown 	acpi_handle handle;
226b4f9fe12SLen Brown 
227b4f9fe12SLen Brown 	block = &wblock->gblock;
228b4f9fe12SLen Brown 	handle = wblock->handle;
229b4f9fe12SLen Brown 
230b4f9fe12SLen Brown 	snprintf(method, 5, "WE%02X", block->notify_id);
2318122ab66SZhang Rui 	status = acpi_execute_simple_method(handle, method, enable);
232b4f9fe12SLen Brown 
233b4f9fe12SLen Brown 	if (status != AE_OK && status != AE_NOT_FOUND)
234b4f9fe12SLen Brown 		return status;
235b4f9fe12SLen Brown 	else
236b4f9fe12SLen Brown 		return AE_OK;
237b4f9fe12SLen Brown }
238b4f9fe12SLen Brown 
239b4f9fe12SLen Brown /*
240b4f9fe12SLen Brown  * Exported WMI functions
241b4f9fe12SLen Brown  */
242b4f9fe12SLen Brown /**
243b4f9fe12SLen Brown  * wmi_evaluate_method - Evaluate a WMI method
244b4f9fe12SLen Brown  * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
245b4f9fe12SLen Brown  * @instance: Instance index
246b4f9fe12SLen Brown  * @method_id: Method ID to call
247b4f9fe12SLen Brown  * &in: Buffer containing input for the method call
248b4f9fe12SLen Brown  * &out: Empty buffer to return the method results
249b4f9fe12SLen Brown  *
250b4f9fe12SLen Brown  * Call an ACPI-WMI method
251b4f9fe12SLen Brown  */
252b4f9fe12SLen Brown acpi_status wmi_evaluate_method(const char *guid_string, u8 instance,
253b4f9fe12SLen Brown u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out)
254b4f9fe12SLen Brown {
255b4f9fe12SLen Brown 	struct guid_block *block = NULL;
256b4f9fe12SLen Brown 	struct wmi_block *wblock = NULL;
257b4f9fe12SLen Brown 	acpi_handle handle;
258b4f9fe12SLen Brown 	acpi_status status;
259b4f9fe12SLen Brown 	struct acpi_object_list input;
260b4f9fe12SLen Brown 	union acpi_object params[3];
261f3d83e24SCostantino Leandro 	char method[5] = "WM";
262b4f9fe12SLen Brown 
263b4f9fe12SLen Brown 	if (!find_guid(guid_string, &wblock))
264b4f9fe12SLen Brown 		return AE_ERROR;
265b4f9fe12SLen Brown 
266b4f9fe12SLen Brown 	block = &wblock->gblock;
267b4f9fe12SLen Brown 	handle = wblock->handle;
268b4f9fe12SLen Brown 
269b4f9fe12SLen Brown 	if (!(block->flags & ACPI_WMI_METHOD))
270b4f9fe12SLen Brown 		return AE_BAD_DATA;
271b4f9fe12SLen Brown 
272b4f9fe12SLen Brown 	if (block->instance_count < instance)
273b4f9fe12SLen Brown 		return AE_BAD_PARAMETER;
274b4f9fe12SLen Brown 
275b4f9fe12SLen Brown 	input.count = 2;
276b4f9fe12SLen Brown 	input.pointer = params;
277b4f9fe12SLen Brown 	params[0].type = ACPI_TYPE_INTEGER;
278b4f9fe12SLen Brown 	params[0].integer.value = instance;
279b4f9fe12SLen Brown 	params[1].type = ACPI_TYPE_INTEGER;
280b4f9fe12SLen Brown 	params[1].integer.value = method_id;
281b4f9fe12SLen Brown 
282b4f9fe12SLen Brown 	if (in) {
283b4f9fe12SLen Brown 		input.count = 3;
284b4f9fe12SLen Brown 
285b4f9fe12SLen Brown 		if (block->flags & ACPI_WMI_STRING) {
286b4f9fe12SLen Brown 			params[2].type = ACPI_TYPE_STRING;
287b4f9fe12SLen Brown 		} else {
288b4f9fe12SLen Brown 			params[2].type = ACPI_TYPE_BUFFER;
289b4f9fe12SLen Brown 		}
290b4f9fe12SLen Brown 		params[2].buffer.length = in->length;
291b4f9fe12SLen Brown 		params[2].buffer.pointer = in->pointer;
292b4f9fe12SLen Brown 	}
293b4f9fe12SLen Brown 
294b4f9fe12SLen Brown 	strncat(method, block->object_id, 2);
295b4f9fe12SLen Brown 
296b4f9fe12SLen Brown 	status = acpi_evaluate_object(handle, method, &input, out);
297b4f9fe12SLen Brown 
298b4f9fe12SLen Brown 	return status;
299b4f9fe12SLen Brown }
300b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_evaluate_method);
301b4f9fe12SLen Brown 
302b4f9fe12SLen Brown /**
303b4f9fe12SLen Brown  * wmi_query_block - Return contents of a WMI block
304b4f9fe12SLen Brown  * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
305b4f9fe12SLen Brown  * @instance: Instance index
306b4f9fe12SLen Brown  * &out: Empty buffer to return the contents of the data block to
307b4f9fe12SLen Brown  *
308b4f9fe12SLen Brown  * Return the contents of an ACPI-WMI data block to a buffer
309b4f9fe12SLen Brown  */
310b4f9fe12SLen Brown acpi_status wmi_query_block(const char *guid_string, u8 instance,
311b4f9fe12SLen Brown struct acpi_buffer *out)
312b4f9fe12SLen Brown {
313b4f9fe12SLen Brown 	struct guid_block *block = NULL;
314b4f9fe12SLen Brown 	struct wmi_block *wblock = NULL;
31554f14c27SZhang Rui 	acpi_handle handle;
316b4f9fe12SLen Brown 	acpi_status status, wc_status = AE_ERROR;
3178122ab66SZhang Rui 	struct acpi_object_list input;
3188122ab66SZhang Rui 	union acpi_object wq_params[1];
319f3d83e24SCostantino Leandro 	char method[5];
320f3d83e24SCostantino Leandro 	char wc_method[5] = "WC";
321b4f9fe12SLen Brown 
322b4f9fe12SLen Brown 	if (!guid_string || !out)
323b4f9fe12SLen Brown 		return AE_BAD_PARAMETER;
324b4f9fe12SLen Brown 
325b4f9fe12SLen Brown 	if (!find_guid(guid_string, &wblock))
326b4f9fe12SLen Brown 		return AE_ERROR;
327b4f9fe12SLen Brown 
328b4f9fe12SLen Brown 	block = &wblock->gblock;
329b4f9fe12SLen Brown 	handle = wblock->handle;
330b4f9fe12SLen Brown 
331b4f9fe12SLen Brown 	if (block->instance_count < instance)
332b4f9fe12SLen Brown 		return AE_BAD_PARAMETER;
333b4f9fe12SLen Brown 
334b4f9fe12SLen Brown 	/* Check GUID is a data block */
335b4f9fe12SLen Brown 	if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD))
336b4f9fe12SLen Brown 		return AE_ERROR;
337b4f9fe12SLen Brown 
338b4f9fe12SLen Brown 	input.count = 1;
339b4f9fe12SLen Brown 	input.pointer = wq_params;
340b4f9fe12SLen Brown 	wq_params[0].type = ACPI_TYPE_INTEGER;
341b4f9fe12SLen Brown 	wq_params[0].integer.value = instance;
342b4f9fe12SLen Brown 
343b4f9fe12SLen Brown 	/*
344b4f9fe12SLen Brown 	 * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method first to
345b4f9fe12SLen Brown 	 * enable collection.
346b4f9fe12SLen Brown 	 */
347b4f9fe12SLen Brown 	if (block->flags & ACPI_WMI_EXPENSIVE) {
348b4f9fe12SLen Brown 		strncat(wc_method, block->object_id, 2);
349b4f9fe12SLen Brown 
350b4f9fe12SLen Brown 		/*
351b4f9fe12SLen Brown 		 * Some GUIDs break the specification by declaring themselves
352b4f9fe12SLen Brown 		 * expensive, but have no corresponding WCxx method. So we
353b4f9fe12SLen Brown 		 * should not fail if this happens.
354b4f9fe12SLen Brown 		 */
35554f14c27SZhang Rui 		if (acpi_has_method(handle, wc_method))
3568122ab66SZhang Rui 			wc_status = acpi_execute_simple_method(handle,
3578122ab66SZhang Rui 								wc_method, 1);
358b4f9fe12SLen Brown 	}
359b4f9fe12SLen Brown 
360b4f9fe12SLen Brown 	strcpy(method, "WQ");
361b4f9fe12SLen Brown 	strncat(method, block->object_id, 2);
362b4f9fe12SLen Brown 
363b4f9fe12SLen Brown 	status = acpi_evaluate_object(handle, method, &input, out);
364b4f9fe12SLen Brown 
365b4f9fe12SLen Brown 	/*
366b4f9fe12SLen Brown 	 * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method, even if
367b4f9fe12SLen Brown 	 * the WQxx method failed - we should disable collection anyway.
368b4f9fe12SLen Brown 	 */
369b4f9fe12SLen Brown 	if ((block->flags & ACPI_WMI_EXPENSIVE) && ACPI_SUCCESS(wc_status)) {
3708122ab66SZhang Rui 		status = acpi_execute_simple_method(handle, wc_method, 0);
371b4f9fe12SLen Brown 	}
372b4f9fe12SLen Brown 
373b4f9fe12SLen Brown 	return status;
374b4f9fe12SLen Brown }
375b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_query_block);
376b4f9fe12SLen Brown 
377b4f9fe12SLen Brown /**
378b4f9fe12SLen Brown  * wmi_set_block - Write to a WMI block
379b4f9fe12SLen Brown  * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
380b4f9fe12SLen Brown  * @instance: Instance index
381b4f9fe12SLen Brown  * &in: Buffer containing new values for the data block
382b4f9fe12SLen Brown  *
383b4f9fe12SLen Brown  * Write the contents of the input buffer to an ACPI-WMI data block
384b4f9fe12SLen Brown  */
385b4f9fe12SLen Brown acpi_status wmi_set_block(const char *guid_string, u8 instance,
386b4f9fe12SLen Brown const struct acpi_buffer *in)
387b4f9fe12SLen Brown {
388b4f9fe12SLen Brown 	struct guid_block *block = NULL;
389b4f9fe12SLen Brown 	struct wmi_block *wblock = NULL;
390b4f9fe12SLen Brown 	acpi_handle handle;
391b4f9fe12SLen Brown 	struct acpi_object_list input;
392b4f9fe12SLen Brown 	union acpi_object params[2];
393f3d83e24SCostantino Leandro 	char method[5] = "WS";
394b4f9fe12SLen Brown 
395b4f9fe12SLen Brown 	if (!guid_string || !in)
396b4f9fe12SLen Brown 		return AE_BAD_DATA;
397b4f9fe12SLen Brown 
398b4f9fe12SLen Brown 	if (!find_guid(guid_string, &wblock))
399b4f9fe12SLen Brown 		return AE_ERROR;
400b4f9fe12SLen Brown 
401b4f9fe12SLen Brown 	block = &wblock->gblock;
402b4f9fe12SLen Brown 	handle = wblock->handle;
403b4f9fe12SLen Brown 
404b4f9fe12SLen Brown 	if (block->instance_count < instance)
405b4f9fe12SLen Brown 		return AE_BAD_PARAMETER;
406b4f9fe12SLen Brown 
407b4f9fe12SLen Brown 	/* Check GUID is a data block */
408b4f9fe12SLen Brown 	if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD))
409b4f9fe12SLen Brown 		return AE_ERROR;
410b4f9fe12SLen Brown 
411b4f9fe12SLen Brown 	input.count = 2;
412b4f9fe12SLen Brown 	input.pointer = params;
413b4f9fe12SLen Brown 	params[0].type = ACPI_TYPE_INTEGER;
414b4f9fe12SLen Brown 	params[0].integer.value = instance;
415b4f9fe12SLen Brown 
416b4f9fe12SLen Brown 	if (block->flags & ACPI_WMI_STRING) {
417b4f9fe12SLen Brown 		params[1].type = ACPI_TYPE_STRING;
418b4f9fe12SLen Brown 	} else {
419b4f9fe12SLen Brown 		params[1].type = ACPI_TYPE_BUFFER;
420b4f9fe12SLen Brown 	}
421b4f9fe12SLen Brown 	params[1].buffer.length = in->length;
422b4f9fe12SLen Brown 	params[1].buffer.pointer = in->pointer;
423b4f9fe12SLen Brown 
424b4f9fe12SLen Brown 	strncat(method, block->object_id, 2);
425b4f9fe12SLen Brown 
426b4f9fe12SLen Brown 	return acpi_evaluate_object(handle, method, &input, NULL);
427b4f9fe12SLen Brown }
428b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_set_block);
429b4f9fe12SLen Brown 
43037830662SDmitry Torokhov static void wmi_dump_wdg(const struct guid_block *g)
431a929aae0SThomas Renninger {
43285b4e4ebSRasmus Villemoes 	pr_info("%pUL:\n", g->guid);
4338e07514dSDmitry Torokhov 	pr_info("\tobject_id: %c%c\n", g->object_id[0], g->object_id[1]);
4348e07514dSDmitry Torokhov 	pr_info("\tnotify_id: %02X\n", g->notify_id);
4358e07514dSDmitry Torokhov 	pr_info("\treserved: %02X\n", g->reserved);
4368e07514dSDmitry Torokhov 	pr_info("\tinstance_count: %d\n", g->instance_count);
4378e07514dSDmitry Torokhov 	pr_info("\tflags: %#x", g->flags);
438a929aae0SThomas Renninger 	if (g->flags) {
439a929aae0SThomas Renninger 		if (g->flags & ACPI_WMI_EXPENSIVE)
4408e07514dSDmitry Torokhov 			pr_cont(" ACPI_WMI_EXPENSIVE");
441a929aae0SThomas Renninger 		if (g->flags & ACPI_WMI_METHOD)
4428e07514dSDmitry Torokhov 			pr_cont(" ACPI_WMI_METHOD");
443a929aae0SThomas Renninger 		if (g->flags & ACPI_WMI_STRING)
4448e07514dSDmitry Torokhov 			pr_cont(" ACPI_WMI_STRING");
445a929aae0SThomas Renninger 		if (g->flags & ACPI_WMI_EVENT)
4468e07514dSDmitry Torokhov 			pr_cont(" ACPI_WMI_EVENT");
447a929aae0SThomas Renninger 	}
4488e07514dSDmitry Torokhov 	pr_cont("\n");
449a929aae0SThomas Renninger 
450a929aae0SThomas Renninger }
451a929aae0SThomas Renninger 
452fc3155b2SThomas Renninger static void wmi_notify_debug(u32 value, void *context)
453fc3155b2SThomas Renninger {
454fc3155b2SThomas Renninger 	struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
455fc3155b2SThomas Renninger 	union acpi_object *obj;
4561492616aSAxel Lin 	acpi_status status;
457fc3155b2SThomas Renninger 
4581492616aSAxel Lin 	status = wmi_get_event_data(value, &response);
4591492616aSAxel Lin 	if (status != AE_OK) {
4608e07514dSDmitry Torokhov 		pr_info("bad event status 0x%x\n", status);
4611492616aSAxel Lin 		return;
4621492616aSAxel Lin 	}
463fc3155b2SThomas Renninger 
464fc3155b2SThomas Renninger 	obj = (union acpi_object *)response.pointer;
465fc3155b2SThomas Renninger 
466fc3155b2SThomas Renninger 	if (!obj)
467fc3155b2SThomas Renninger 		return;
468fc3155b2SThomas Renninger 
4698e07514dSDmitry Torokhov 	pr_info("DEBUG Event ");
470fc3155b2SThomas Renninger 	switch(obj->type) {
471fc3155b2SThomas Renninger 	case ACPI_TYPE_BUFFER:
4728e07514dSDmitry Torokhov 		pr_cont("BUFFER_TYPE - length %d\n", obj->buffer.length);
473fc3155b2SThomas Renninger 		break;
474fc3155b2SThomas Renninger 	case ACPI_TYPE_STRING:
4758e07514dSDmitry Torokhov 		pr_cont("STRING_TYPE - %s\n", obj->string.pointer);
476fc3155b2SThomas Renninger 		break;
477fc3155b2SThomas Renninger 	case ACPI_TYPE_INTEGER:
4788e07514dSDmitry Torokhov 		pr_cont("INTEGER_TYPE - %llu\n", obj->integer.value);
479fc3155b2SThomas Renninger 		break;
480fc3155b2SThomas Renninger 	case ACPI_TYPE_PACKAGE:
4818e07514dSDmitry Torokhov 		pr_cont("PACKAGE_TYPE - %d elements\n", obj->package.count);
482fc3155b2SThomas Renninger 		break;
483fc3155b2SThomas Renninger 	default:
4848e07514dSDmitry Torokhov 		pr_cont("object type 0x%X\n", obj->type);
485fc3155b2SThomas Renninger 	}
4861492616aSAxel Lin 	kfree(obj);
487fc3155b2SThomas Renninger }
488fc3155b2SThomas Renninger 
489b4f9fe12SLen Brown /**
490b4f9fe12SLen Brown  * wmi_install_notify_handler - Register handler for WMI events
491b4f9fe12SLen Brown  * @handler: Function to handle notifications
492b4f9fe12SLen Brown  * @data: Data to be returned to handler when event is fired
493b4f9fe12SLen Brown  *
494b4f9fe12SLen Brown  * Register a handler for events sent to the ACPI-WMI mapper device.
495b4f9fe12SLen Brown  */
496b4f9fe12SLen Brown acpi_status wmi_install_notify_handler(const char *guid,
497b4f9fe12SLen Brown wmi_notify_handler handler, void *data)
498b4f9fe12SLen Brown {
499b4f9fe12SLen Brown 	struct wmi_block *block;
50058f6425eSColin King 	acpi_status status = AE_NOT_EXIST;
50158f6425eSColin King 	char tmp[16], guid_input[16];
50258f6425eSColin King 	struct list_head *p;
503b4f9fe12SLen Brown 
504b4f9fe12SLen Brown 	if (!guid || !handler)
505b4f9fe12SLen Brown 		return AE_BAD_PARAMETER;
506b4f9fe12SLen Brown 
50758f6425eSColin King 	wmi_parse_guid(guid, tmp);
50858f6425eSColin King 	wmi_swap_bytes(tmp, guid_input);
509b4f9fe12SLen Brown 
51058f6425eSColin King 	list_for_each(p, &wmi_block_list) {
51158f6425eSColin King 		acpi_status wmi_status;
51258f6425eSColin King 		block = list_entry(p, struct wmi_block, list);
51358f6425eSColin King 
51458f6425eSColin King 		if (memcmp(block->gblock.guid, guid_input, 16) == 0) {
51558f6425eSColin King 			if (block->handler &&
51658f6425eSColin King 			    block->handler != wmi_notify_debug)
517b4f9fe12SLen Brown 				return AE_ALREADY_ACQUIRED;
518b4f9fe12SLen Brown 
519b4f9fe12SLen Brown 			block->handler = handler;
520b4f9fe12SLen Brown 			block->handler_data = data;
521b4f9fe12SLen Brown 
52258f6425eSColin King 			wmi_status = wmi_method_enable(block, 1);
52358f6425eSColin King 			if ((wmi_status != AE_OK) ||
52458f6425eSColin King 			    ((wmi_status == AE_OK) && (status == AE_NOT_EXIST)))
52558f6425eSColin King 				status = wmi_status;
52658f6425eSColin King 		}
52758f6425eSColin King 	}
528b4f9fe12SLen Brown 
529b4f9fe12SLen Brown 	return status;
530b4f9fe12SLen Brown }
531b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_install_notify_handler);
532b4f9fe12SLen Brown 
533b4f9fe12SLen Brown /**
534b4f9fe12SLen Brown  * wmi_uninstall_notify_handler - Unregister handler for WMI events
535b4f9fe12SLen Brown  *
536b4f9fe12SLen Brown  * Unregister handler for events sent to the ACPI-WMI mapper device.
537b4f9fe12SLen Brown  */
538b4f9fe12SLen Brown acpi_status wmi_remove_notify_handler(const char *guid)
539b4f9fe12SLen Brown {
540b4f9fe12SLen Brown 	struct wmi_block *block;
54158f6425eSColin King 	acpi_status status = AE_NOT_EXIST;
54258f6425eSColin King 	char tmp[16], guid_input[16];
54358f6425eSColin King 	struct list_head *p;
544b4f9fe12SLen Brown 
545b4f9fe12SLen Brown 	if (!guid)
546b4f9fe12SLen Brown 		return AE_BAD_PARAMETER;
547b4f9fe12SLen Brown 
54858f6425eSColin King 	wmi_parse_guid(guid, tmp);
54958f6425eSColin King 	wmi_swap_bytes(tmp, guid_input);
550b4f9fe12SLen Brown 
55158f6425eSColin King 	list_for_each(p, &wmi_block_list) {
55258f6425eSColin King 		acpi_status wmi_status;
55358f6425eSColin King 		block = list_entry(p, struct wmi_block, list);
55458f6425eSColin King 
55558f6425eSColin King 		if (memcmp(block->gblock.guid, guid_input, 16) == 0) {
55658f6425eSColin King 			if (!block->handler ||
55758f6425eSColin King 			    block->handler == wmi_notify_debug)
558b4f9fe12SLen Brown 				return AE_NULL_ENTRY;
559b4f9fe12SLen Brown 
560fc3155b2SThomas Renninger 			if (debug_event) {
561fc3155b2SThomas Renninger 				block->handler = wmi_notify_debug;
56258f6425eSColin King 				status = AE_OK;
563fc3155b2SThomas Renninger 			} else {
56458f6425eSColin King 				wmi_status = wmi_method_enable(block, 0);
565b4f9fe12SLen Brown 				block->handler = NULL;
566b4f9fe12SLen Brown 				block->handler_data = NULL;
56758f6425eSColin King 				if ((wmi_status != AE_OK) ||
56858f6425eSColin King 				    ((wmi_status == AE_OK) &&
56958f6425eSColin King 				     (status == AE_NOT_EXIST)))
57058f6425eSColin King 					status = wmi_status;
571fc3155b2SThomas Renninger 			}
57258f6425eSColin King 		}
57358f6425eSColin King 	}
57458f6425eSColin King 
575b4f9fe12SLen Brown 	return status;
576b4f9fe12SLen Brown }
577b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_remove_notify_handler);
578b4f9fe12SLen Brown 
579b4f9fe12SLen Brown /**
580b4f9fe12SLen Brown  * wmi_get_event_data - Get WMI data associated with an event
581b4f9fe12SLen Brown  *
5823e9b988eSAnisse Astier  * @event: Event to find
5833e9b988eSAnisse Astier  * @out: Buffer to hold event data. out->pointer should be freed with kfree()
584b4f9fe12SLen Brown  *
585b4f9fe12SLen Brown  * Returns extra data associated with an event in WMI.
586b4f9fe12SLen Brown  */
587b4f9fe12SLen Brown acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out)
588b4f9fe12SLen Brown {
589b4f9fe12SLen Brown 	struct acpi_object_list input;
590b4f9fe12SLen Brown 	union acpi_object params[1];
591b4f9fe12SLen Brown 	struct guid_block *gblock;
592b4f9fe12SLen Brown 	struct wmi_block *wblock;
593b4f9fe12SLen Brown 	struct list_head *p;
594b4f9fe12SLen Brown 
595b4f9fe12SLen Brown 	input.count = 1;
596b4f9fe12SLen Brown 	input.pointer = params;
597b4f9fe12SLen Brown 	params[0].type = ACPI_TYPE_INTEGER;
598b4f9fe12SLen Brown 	params[0].integer.value = event;
599b4f9fe12SLen Brown 
600762e1a2fSDmitry Torokhov 	list_for_each(p, &wmi_block_list) {
601b4f9fe12SLen Brown 		wblock = list_entry(p, struct wmi_block, list);
602b4f9fe12SLen Brown 		gblock = &wblock->gblock;
603b4f9fe12SLen Brown 
604b4f9fe12SLen Brown 		if ((gblock->flags & ACPI_WMI_EVENT) &&
605b4f9fe12SLen Brown 			(gblock->notify_id == event))
606b4f9fe12SLen Brown 			return acpi_evaluate_object(wblock->handle, "_WED",
607b4f9fe12SLen Brown 				&input, out);
608b4f9fe12SLen Brown 	}
609b4f9fe12SLen Brown 
610b4f9fe12SLen Brown 	return AE_NOT_FOUND;
611b4f9fe12SLen Brown }
612b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_get_event_data);
613b4f9fe12SLen Brown 
614b4f9fe12SLen Brown /**
615b4f9fe12SLen Brown  * wmi_has_guid - Check if a GUID is available
616b4f9fe12SLen Brown  * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
617b4f9fe12SLen Brown  *
618b4f9fe12SLen Brown  * Check if a given GUID is defined by _WDG
619b4f9fe12SLen Brown  */
620b4f9fe12SLen Brown bool wmi_has_guid(const char *guid_string)
621b4f9fe12SLen Brown {
622b4f9fe12SLen Brown 	return find_guid(guid_string, NULL);
623b4f9fe12SLen Brown }
624b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_has_guid);
625b4f9fe12SLen Brown 
626b4f9fe12SLen Brown /*
6271caab3c1SMatthew Garrett  * sysfs interface
6281caab3c1SMatthew Garrett  */
629614ef432SDmitry Torokhov static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
6301caab3c1SMatthew Garrett 			     char *buf)
6311caab3c1SMatthew Garrett {
6321caab3c1SMatthew Garrett 	struct wmi_block *wblock;
6331caab3c1SMatthew Garrett 
6341caab3c1SMatthew Garrett 	wblock = dev_get_drvdata(dev);
635a80e1053SPrarit Bhargava 	if (!wblock) {
636a80e1053SPrarit Bhargava 		strcat(buf, "\n");
637a80e1053SPrarit Bhargava 		return strlen(buf);
638a80e1053SPrarit Bhargava 	}
6391caab3c1SMatthew Garrett 
64085b4e4ebSRasmus Villemoes 	return sprintf(buf, "wmi:%pUL\n", wblock->gblock.guid);
6411caab3c1SMatthew Garrett }
642e80b89a5SGreg Kroah-Hartman static DEVICE_ATTR_RO(modalias);
643614ef432SDmitry Torokhov 
644e80b89a5SGreg Kroah-Hartman static struct attribute *wmi_attrs[] = {
645e80b89a5SGreg Kroah-Hartman 	&dev_attr_modalias.attr,
646e80b89a5SGreg Kroah-Hartman 	NULL,
647614ef432SDmitry Torokhov };
648e80b89a5SGreg Kroah-Hartman ATTRIBUTE_GROUPS(wmi);
6491caab3c1SMatthew Garrett 
6501caab3c1SMatthew Garrett static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env)
6511caab3c1SMatthew Garrett {
6521caab3c1SMatthew Garrett 	char guid_string[37];
6531caab3c1SMatthew Garrett 
6541caab3c1SMatthew Garrett 	struct wmi_block *wblock;
6551caab3c1SMatthew Garrett 
6561caab3c1SMatthew Garrett 	if (add_uevent_var(env, "MODALIAS="))
6571caab3c1SMatthew Garrett 		return -ENOMEM;
6581caab3c1SMatthew Garrett 
6591caab3c1SMatthew Garrett 	wblock = dev_get_drvdata(dev);
6601caab3c1SMatthew Garrett 	if (!wblock)
6611caab3c1SMatthew Garrett 		return -ENOMEM;
6621caab3c1SMatthew Garrett 
66385b4e4ebSRasmus Villemoes 	sprintf(guid_string, "%pUL", wblock->gblock.guid);
6641caab3c1SMatthew Garrett 
6651caab3c1SMatthew Garrett 	strcpy(&env->buf[env->buflen - 1], "wmi:");
6661caab3c1SMatthew Garrett 	memcpy(&env->buf[env->buflen - 1 + 4], guid_string, 36);
6671caab3c1SMatthew Garrett 	env->buflen += 40;
6681caab3c1SMatthew Garrett 
6691caab3c1SMatthew Garrett 	return 0;
6701caab3c1SMatthew Garrett }
6711caab3c1SMatthew Garrett 
6721caab3c1SMatthew Garrett static void wmi_dev_free(struct device *dev)
6731caab3c1SMatthew Garrett {
674c64eefd4SDmitry Torokhov 	struct wmi_block *wmi_block = container_of(dev, struct wmi_block, dev);
675c64eefd4SDmitry Torokhov 
676c64eefd4SDmitry Torokhov 	kfree(wmi_block);
6771caab3c1SMatthew Garrett }
6781caab3c1SMatthew Garrett 
6791caab3c1SMatthew Garrett static struct class wmi_class = {
6801caab3c1SMatthew Garrett 	.name = "wmi",
6811caab3c1SMatthew Garrett 	.dev_release = wmi_dev_free,
6821caab3c1SMatthew Garrett 	.dev_uevent = wmi_dev_uevent,
683e80b89a5SGreg Kroah-Hartman 	.dev_groups = wmi_groups,
6841caab3c1SMatthew Garrett };
6851caab3c1SMatthew Garrett 
68658f6425eSColin King static int wmi_create_device(const struct guid_block *gblock,
68758f6425eSColin King 			     struct wmi_block *wblock, acpi_handle handle)
6881caab3c1SMatthew Garrett {
689c64eefd4SDmitry Torokhov 	wblock->dev.class = &wmi_class;
6901caab3c1SMatthew Garrett 
69185b4e4ebSRasmus Villemoes 	dev_set_name(&wblock->dev, "%pUL", gblock->guid);
6921caab3c1SMatthew Garrett 
693c64eefd4SDmitry Torokhov 	dev_set_drvdata(&wblock->dev, wblock);
694c64eefd4SDmitry Torokhov 
69558f6425eSColin King 	return device_register(&wblock->dev);
6961caab3c1SMatthew Garrett }
6971caab3c1SMatthew Garrett 
698c64eefd4SDmitry Torokhov static void wmi_free_devices(void)
6991caab3c1SMatthew Garrett {
700c64eefd4SDmitry Torokhov 	struct wmi_block *wblock, *next;
7011caab3c1SMatthew Garrett 
7021caab3c1SMatthew Garrett 	/* Delete devices for all the GUIDs */
703023b9565SDmitry Torokhov 	list_for_each_entry_safe(wblock, next, &wmi_block_list, list) {
704023b9565SDmitry Torokhov 		list_del(&wblock->list);
70558f6425eSColin King 		if (wblock->dev.class)
706c64eefd4SDmitry Torokhov 			device_unregister(&wblock->dev);
707023b9565SDmitry Torokhov 		else
708023b9565SDmitry Torokhov 			kfree(wblock);
709023b9565SDmitry Torokhov 	}
7101caab3c1SMatthew Garrett }
7111caab3c1SMatthew Garrett 
712d1f9e497SCarlos Corbacho static bool guid_already_parsed(const char *guid_string)
713d1f9e497SCarlos Corbacho {
714d1f9e497SCarlos Corbacho 	struct wmi_block *wblock;
715d1f9e497SCarlos Corbacho 
716c64eefd4SDmitry Torokhov 	list_for_each_entry(wblock, &wmi_block_list, list)
7178b14d7b2SThadeu Lima de Souza Cascardo 		if (memcmp(wblock->gblock.guid, guid_string, 16) == 0)
718d1f9e497SCarlos Corbacho 			return true;
719c64eefd4SDmitry Torokhov 
720d1f9e497SCarlos Corbacho 	return false;
721d1f9e497SCarlos Corbacho }
722d1f9e497SCarlos Corbacho 
7231caab3c1SMatthew Garrett /*
724b4f9fe12SLen Brown  * Parse the _WDG method for the GUID data blocks
725b4f9fe12SLen Brown  */
7260a018a68SDan Carpenter static int parse_wdg(acpi_handle handle)
727b4f9fe12SLen Brown {
728b4f9fe12SLen Brown 	struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
729b4f9fe12SLen Brown 	union acpi_object *obj;
73037830662SDmitry Torokhov 	const struct guid_block *gblock;
731b4f9fe12SLen Brown 	struct wmi_block *wblock;
732b4f9fe12SLen Brown 	acpi_status status;
733c64eefd4SDmitry Torokhov 	int retval;
734b4f9fe12SLen Brown 	u32 i, total;
735b4f9fe12SLen Brown 
736b4f9fe12SLen Brown 	status = acpi_evaluate_object(handle, "_WDG", NULL, &out);
737b4f9fe12SLen Brown 	if (ACPI_FAILURE(status))
738c64eefd4SDmitry Torokhov 		return -ENXIO;
739b4f9fe12SLen Brown 
740b4f9fe12SLen Brown 	obj = (union acpi_object *) out.pointer;
7413d2c63ebSDmitry Torokhov 	if (!obj)
742c64eefd4SDmitry Torokhov 		return -ENXIO;
743b4f9fe12SLen Brown 
74464ed0ab8SDmitry Torokhov 	if (obj->type != ACPI_TYPE_BUFFER) {
745c64eefd4SDmitry Torokhov 		retval = -ENXIO;
74664ed0ab8SDmitry Torokhov 		goto out_free_pointer;
74764ed0ab8SDmitry Torokhov 	}
748b4f9fe12SLen Brown 
74937830662SDmitry Torokhov 	gblock = (const struct guid_block *)obj->buffer.pointer;
750b4f9fe12SLen Brown 	total = obj->buffer.length / sizeof(struct guid_block);
751b4f9fe12SLen Brown 
752b4f9fe12SLen Brown 	for (i = 0; i < total; i++) {
753a929aae0SThomas Renninger 		if (debug_dump_wdg)
754a929aae0SThomas Renninger 			wmi_dump_wdg(&gblock[i]);
755a929aae0SThomas Renninger 
75658f6425eSColin King 		wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL);
75758f6425eSColin King 		if (!wblock)
7580a018a68SDan Carpenter 			return -ENOMEM;
75958f6425eSColin King 
76058f6425eSColin King 		wblock->handle = handle;
76158f6425eSColin King 		wblock->gblock = gblock[i];
76258f6425eSColin King 
76358f6425eSColin King 		/*
76458f6425eSColin King 		  Some WMI devices, like those for nVidia hooks, have a
76558f6425eSColin King 		  duplicate GUID. It's not clear what we should do in this
76658f6425eSColin King 		  case yet, so for now, we'll just ignore the duplicate
76758f6425eSColin King 		  for device creation.
76858f6425eSColin King 		*/
76958f6425eSColin King 		if (!guid_already_parsed(gblock[i].guid)) {
77058f6425eSColin King 			retval = wmi_create_device(&gblock[i], wblock, handle);
77158f6425eSColin King 			if (retval) {
772c64eefd4SDmitry Torokhov 				wmi_free_devices();
773e1e0dacbSDan Carpenter 				goto out_free_pointer;
774a5167c5bSAxel Lin 			}
77558f6425eSColin King 		}
77658f6425eSColin King 
77758f6425eSColin King 		list_add_tail(&wblock->list, &wmi_block_list);
778b4f9fe12SLen Brown 
779fc3155b2SThomas Renninger 		if (debug_event) {
780fc3155b2SThomas Renninger 			wblock->handler = wmi_notify_debug;
7812d5ab555SDmitry Torokhov 			wmi_method_enable(wblock, 1);
782fc3155b2SThomas Renninger 		}
783b4f9fe12SLen Brown 	}
784b4f9fe12SLen Brown 
785c64eefd4SDmitry Torokhov 	retval = 0;
786c64eefd4SDmitry Torokhov 
787a5167c5bSAxel Lin out_free_pointer:
788a5167c5bSAxel Lin 	kfree(out.pointer);
789b4f9fe12SLen Brown 
790c64eefd4SDmitry Torokhov 	return retval;
791b4f9fe12SLen Brown }
792b4f9fe12SLen Brown 
793b4f9fe12SLen Brown /*
794b4f9fe12SLen Brown  * WMI can have EmbeddedControl access regions. In which case, we just want to
795b4f9fe12SLen Brown  * hand these off to the EC driver.
796b4f9fe12SLen Brown  */
797b4f9fe12SLen Brown static acpi_status
798b4f9fe12SLen Brown acpi_wmi_ec_space_handler(u32 function, acpi_physical_address address,
799439913ffSLin Ming 		      u32 bits, u64 *value,
800b4f9fe12SLen Brown 		      void *handler_context, void *region_context)
801b4f9fe12SLen Brown {
802b4f9fe12SLen Brown 	int result = 0, i = 0;
803b4f9fe12SLen Brown 	u8 temp = 0;
804b4f9fe12SLen Brown 
805b4f9fe12SLen Brown 	if ((address > 0xFF) || !value)
806b4f9fe12SLen Brown 		return AE_BAD_PARAMETER;
807b4f9fe12SLen Brown 
808b4f9fe12SLen Brown 	if (function != ACPI_READ && function != ACPI_WRITE)
809b4f9fe12SLen Brown 		return AE_BAD_PARAMETER;
810b4f9fe12SLen Brown 
811b4f9fe12SLen Brown 	if (bits != 8)
812b4f9fe12SLen Brown 		return AE_BAD_PARAMETER;
813b4f9fe12SLen Brown 
814b4f9fe12SLen Brown 	if (function == ACPI_READ) {
815b4f9fe12SLen Brown 		result = ec_read(address, &temp);
816439913ffSLin Ming 		(*value) |= ((u64)temp) << i;
817b4f9fe12SLen Brown 	} else {
818b4f9fe12SLen Brown 		temp = 0xff & ((*value) >> i);
819b4f9fe12SLen Brown 		result = ec_write(address, temp);
820b4f9fe12SLen Brown 	}
821b4f9fe12SLen Brown 
822b4f9fe12SLen Brown 	switch (result) {
823b4f9fe12SLen Brown 	case -EINVAL:
824b4f9fe12SLen Brown 		return AE_BAD_PARAMETER;
825b4f9fe12SLen Brown 		break;
826b4f9fe12SLen Brown 	case -ENODEV:
827b4f9fe12SLen Brown 		return AE_NOT_FOUND;
828b4f9fe12SLen Brown 		break;
829b4f9fe12SLen Brown 	case -ETIME:
830b4f9fe12SLen Brown 		return AE_TIME;
831b4f9fe12SLen Brown 		break;
832b4f9fe12SLen Brown 	default:
833b4f9fe12SLen Brown 		return AE_OK;
834b4f9fe12SLen Brown 	}
835b4f9fe12SLen Brown }
836b4f9fe12SLen Brown 
837f61bb939SBjorn Helgaas static void acpi_wmi_notify(struct acpi_device *device, u32 event)
838b4f9fe12SLen Brown {
839b4f9fe12SLen Brown 	struct guid_block *block;
840b4f9fe12SLen Brown 	struct wmi_block *wblock;
841b4f9fe12SLen Brown 	struct list_head *p;
842b4f9fe12SLen Brown 
843762e1a2fSDmitry Torokhov 	list_for_each(p, &wmi_block_list) {
844b4f9fe12SLen Brown 		wblock = list_entry(p, struct wmi_block, list);
845b4f9fe12SLen Brown 		block = &wblock->gblock;
846b4f9fe12SLen Brown 
847b4f9fe12SLen Brown 		if ((block->flags & ACPI_WMI_EVENT) &&
848b4f9fe12SLen Brown 			(block->notify_id == event)) {
849b4f9fe12SLen Brown 			if (wblock->handler)
850b4f9fe12SLen Brown 				wblock->handler(event, wblock->handler_data);
8517715348cSThomas Renninger 			if (debug_event) {
85285b4e4ebSRasmus Villemoes 				pr_info("DEBUG Event GUID: %pUL\n",
85385b4e4ebSRasmus Villemoes 					wblock->gblock.guid);
8547715348cSThomas Renninger 			}
855b4f9fe12SLen Brown 
856b4f9fe12SLen Brown 			acpi_bus_generate_netlink_event(
857b4f9fe12SLen Brown 				device->pnp.device_class, dev_name(&device->dev),
858b4f9fe12SLen Brown 				event, 0);
859b4f9fe12SLen Brown 			break;
860b4f9fe12SLen Brown 		}
861b4f9fe12SLen Brown 	}
862b4f9fe12SLen Brown }
863b4f9fe12SLen Brown 
86451fac838SRafael J. Wysocki static int acpi_wmi_remove(struct acpi_device *device)
865b4f9fe12SLen Brown {
866b4f9fe12SLen Brown 	acpi_remove_address_space_handler(device->handle,
867b4f9fe12SLen Brown 				ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler);
868c64eefd4SDmitry Torokhov 	wmi_free_devices();
869b4f9fe12SLen Brown 
870b4f9fe12SLen Brown 	return 0;
871b4f9fe12SLen Brown }
872b4f9fe12SLen Brown 
873925b1089SThomas Renninger static int acpi_wmi_add(struct acpi_device *device)
874b4f9fe12SLen Brown {
875b4f9fe12SLen Brown 	acpi_status status;
876c64eefd4SDmitry Torokhov 	int error;
877b4f9fe12SLen Brown 
878b4f9fe12SLen Brown 	status = acpi_install_address_space_handler(device->handle,
879b4f9fe12SLen Brown 						    ACPI_ADR_SPACE_EC,
880b4f9fe12SLen Brown 						    &acpi_wmi_ec_space_handler,
881b4f9fe12SLen Brown 						    NULL, NULL);
8825212cd67SDmitry Torokhov 	if (ACPI_FAILURE(status)) {
8838e07514dSDmitry Torokhov 		pr_err("Error installing EC region handler\n");
884b4f9fe12SLen Brown 		return -ENODEV;
8855212cd67SDmitry Torokhov 	}
886b4f9fe12SLen Brown 
887c64eefd4SDmitry Torokhov 	error = parse_wdg(device->handle);
888c64eefd4SDmitry Torokhov 	if (error) {
8895212cd67SDmitry Torokhov 		acpi_remove_address_space_handler(device->handle,
8905212cd67SDmitry Torokhov 						  ACPI_ADR_SPACE_EC,
8915212cd67SDmitry Torokhov 						  &acpi_wmi_ec_space_handler);
8928e07514dSDmitry Torokhov 		pr_err("Failed to parse WDG method\n");
893c64eefd4SDmitry Torokhov 		return error;
894b4f9fe12SLen Brown 	}
895b4f9fe12SLen Brown 
896c64eefd4SDmitry Torokhov 	return 0;
897b4f9fe12SLen Brown }
898b4f9fe12SLen Brown 
899b4f9fe12SLen Brown static int __init acpi_wmi_init(void)
900b4f9fe12SLen Brown {
901c64eefd4SDmitry Torokhov 	int error;
902b4f9fe12SLen Brown 
903b4f9fe12SLen Brown 	if (acpi_disabled)
904b4f9fe12SLen Brown 		return -ENODEV;
905b4f9fe12SLen Brown 
906c64eefd4SDmitry Torokhov 	error = class_register(&wmi_class);
907c64eefd4SDmitry Torokhov 	if (error)
908c64eefd4SDmitry Torokhov 		return error;
909b4f9fe12SLen Brown 
910c64eefd4SDmitry Torokhov 	error = acpi_bus_register_driver(&acpi_wmi_driver);
911c64eefd4SDmitry Torokhov 	if (error) {
912c64eefd4SDmitry Torokhov 		pr_err("Error loading mapper\n");
913c64eefd4SDmitry Torokhov 		class_unregister(&wmi_class);
914c64eefd4SDmitry Torokhov 		return error;
9151caab3c1SMatthew Garrett 	}
9161caab3c1SMatthew Garrett 
9178e07514dSDmitry Torokhov 	pr_info("Mapper loaded\n");
9188e07514dSDmitry Torokhov 	return 0;
919b4f9fe12SLen Brown }
920b4f9fe12SLen Brown 
921b4f9fe12SLen Brown static void __exit acpi_wmi_exit(void)
922b4f9fe12SLen Brown {
923b4f9fe12SLen Brown 	acpi_bus_unregister_driver(&acpi_wmi_driver);
924c64eefd4SDmitry Torokhov 	class_unregister(&wmi_class);
925b4f9fe12SLen Brown 
9268e07514dSDmitry Torokhov 	pr_info("Mapper unloaded\n");
927b4f9fe12SLen Brown }
928b4f9fe12SLen Brown 
929b4f9fe12SLen Brown subsys_initcall(acpi_wmi_init);
930b4f9fe12SLen Brown module_exit(acpi_wmi_exit);
931