xref: /openbmc/linux/drivers/platform/x86/wmi.c (revision 2c6719a3)
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 
30b4f9fe12SLen Brown #include <linux/kernel.h>
31b4f9fe12SLen Brown #include <linux/init.h>
32b4f9fe12SLen Brown #include <linux/types.h>
331caab3c1SMatthew Garrett #include <linux/device.h>
34b4f9fe12SLen Brown #include <linux/list.h>
35b4f9fe12SLen Brown #include <linux/acpi.h>
365a0e3ad6STejun Heo #include <linux/slab.h>
37b4f9fe12SLen Brown #include <acpi/acpi_bus.h>
38b4f9fe12SLen Brown #include <acpi/acpi_drivers.h>
39b4f9fe12SLen Brown 
40b4f9fe12SLen Brown ACPI_MODULE_NAME("wmi");
41b4f9fe12SLen Brown MODULE_AUTHOR("Carlos Corbacho");
42b4f9fe12SLen Brown MODULE_DESCRIPTION("ACPI-WMI Mapping Driver");
43b4f9fe12SLen Brown MODULE_LICENSE("GPL");
44b4f9fe12SLen Brown 
45b4f9fe12SLen Brown #define ACPI_WMI_CLASS "wmi"
46b4f9fe12SLen Brown 
47b4f9fe12SLen Brown #define PREFIX "ACPI: WMI: "
48b4f9fe12SLen Brown 
49b4f9fe12SLen Brown static DEFINE_MUTEX(wmi_data_lock);
50b4f9fe12SLen Brown 
51b4f9fe12SLen Brown struct guid_block {
52b4f9fe12SLen Brown 	char guid[16];
53b4f9fe12SLen Brown 	union {
54b4f9fe12SLen Brown 		char object_id[2];
55b4f9fe12SLen Brown 		struct {
56b4f9fe12SLen Brown 			unsigned char notify_id;
57b4f9fe12SLen Brown 			unsigned char reserved;
58b4f9fe12SLen Brown 		};
59b4f9fe12SLen Brown 	};
60b4f9fe12SLen Brown 	u8 instance_count;
61b4f9fe12SLen Brown 	u8 flags;
62b4f9fe12SLen Brown };
63b4f9fe12SLen Brown 
64b4f9fe12SLen Brown struct wmi_block {
65b4f9fe12SLen Brown 	struct list_head list;
66b4f9fe12SLen Brown 	struct guid_block gblock;
67b4f9fe12SLen Brown 	acpi_handle handle;
68b4f9fe12SLen Brown 	wmi_notify_handler handler;
69b4f9fe12SLen Brown 	void *handler_data;
701caab3c1SMatthew Garrett 	struct device *dev;
71b4f9fe12SLen Brown };
72b4f9fe12SLen Brown 
73b4f9fe12SLen Brown static struct wmi_block wmi_blocks;
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  */
79b4f9fe12SLen Brown #define ACPI_WMI_EXPENSIVE   0x1
80b4f9fe12SLen Brown #define ACPI_WMI_METHOD      0x2	/* GUID is a method */
81b4f9fe12SLen Brown #define ACPI_WMI_STRING      0x4	/* GUID takes & returns a string */
82b4f9fe12SLen Brown #define ACPI_WMI_EVENT       0x8	/* GUID is an event */
83b4f9fe12SLen Brown 
84b4f9fe12SLen Brown static int acpi_wmi_remove(struct acpi_device *device, int type);
85b4f9fe12SLen Brown static int acpi_wmi_add(struct acpi_device *device);
86f61bb939SBjorn Helgaas static void acpi_wmi_notify(struct acpi_device *device, u32 event);
87b4f9fe12SLen Brown 
88b4f9fe12SLen Brown static const struct acpi_device_id wmi_device_ids[] = {
89b4f9fe12SLen Brown 	{"PNP0C14", 0},
90b4f9fe12SLen Brown 	{"pnp0c14", 0},
91b4f9fe12SLen Brown 	{"", 0},
92b4f9fe12SLen Brown };
93b4f9fe12SLen Brown MODULE_DEVICE_TABLE(acpi, wmi_device_ids);
94b4f9fe12SLen Brown 
95b4f9fe12SLen Brown static struct acpi_driver acpi_wmi_driver = {
96b4f9fe12SLen Brown 	.name = "wmi",
97b4f9fe12SLen Brown 	.class = ACPI_WMI_CLASS,
98b4f9fe12SLen Brown 	.ids = wmi_device_ids,
99b4f9fe12SLen Brown 	.ops = {
100b4f9fe12SLen Brown 		.add = acpi_wmi_add,
101b4f9fe12SLen Brown 		.remove = acpi_wmi_remove,
102f61bb939SBjorn Helgaas 		.notify = acpi_wmi_notify,
103b4f9fe12SLen Brown 		},
104b4f9fe12SLen Brown };
105b4f9fe12SLen Brown 
106b4f9fe12SLen Brown /*
107b4f9fe12SLen Brown  * GUID parsing functions
108b4f9fe12SLen Brown  */
109b4f9fe12SLen Brown 
110b4f9fe12SLen Brown /**
111b4f9fe12SLen Brown  * wmi_parse_hexbyte - Convert a ASCII hex number to a byte
112b4f9fe12SLen Brown  * @src:  Pointer to at least 2 characters to convert.
113b4f9fe12SLen Brown  *
114b4f9fe12SLen Brown  * Convert a two character ASCII hex string to a number.
115b4f9fe12SLen Brown  *
116b4f9fe12SLen Brown  * Return:  0-255  Success, the byte was parsed correctly
117b4f9fe12SLen Brown  *          -1     Error, an invalid character was supplied
118b4f9fe12SLen Brown  */
119b4f9fe12SLen Brown static int wmi_parse_hexbyte(const u8 *src)
120b4f9fe12SLen Brown {
121b4f9fe12SLen Brown 	unsigned int x; /* For correct wrapping */
122b4f9fe12SLen Brown 	int h;
123b4f9fe12SLen Brown 
124b4f9fe12SLen Brown 	/* high part */
125b4f9fe12SLen Brown 	x = src[0];
126b4f9fe12SLen Brown 	if (x - '0' <= '9' - '0') {
127b4f9fe12SLen Brown 		h = x - '0';
128b4f9fe12SLen Brown 	} else if (x - 'a' <= 'f' - 'a') {
129b4f9fe12SLen Brown 		h = x - 'a' + 10;
130b4f9fe12SLen Brown 	} else if (x - 'A' <= 'F' - 'A') {
131b4f9fe12SLen Brown 		h = x - 'A' + 10;
132b4f9fe12SLen Brown 	} else {
133b4f9fe12SLen Brown 		return -1;
134b4f9fe12SLen Brown 	}
135b4f9fe12SLen Brown 	h <<= 4;
136b4f9fe12SLen Brown 
137b4f9fe12SLen Brown 	/* low part */
138b4f9fe12SLen Brown 	x = src[1];
139b4f9fe12SLen Brown 	if (x - '0' <= '9' - '0')
140b4f9fe12SLen Brown 		return h | (x - '0');
141b4f9fe12SLen Brown 	if (x - 'a' <= 'f' - 'a')
142b4f9fe12SLen Brown 		return h | (x - 'a' + 10);
143b4f9fe12SLen Brown 	if (x - 'A' <= 'F' - 'A')
144b4f9fe12SLen Brown 		return h | (x - 'A' + 10);
145b4f9fe12SLen Brown 	return -1;
146b4f9fe12SLen Brown }
147b4f9fe12SLen Brown 
148b4f9fe12SLen Brown /**
149b4f9fe12SLen Brown  * wmi_swap_bytes - Rearrange GUID bytes to match GUID binary
150b4f9fe12SLen Brown  * @src:   Memory block holding binary GUID (16 bytes)
151b4f9fe12SLen Brown  * @dest:  Memory block to hold byte swapped binary GUID (16 bytes)
152b4f9fe12SLen Brown  *
153b4f9fe12SLen Brown  * Byte swap a binary GUID to match it's real GUID value
154b4f9fe12SLen Brown  */
155b4f9fe12SLen Brown static void wmi_swap_bytes(u8 *src, u8 *dest)
156b4f9fe12SLen Brown {
157b4f9fe12SLen Brown 	int i;
158b4f9fe12SLen Brown 
159b4f9fe12SLen Brown 	for (i = 0; i <= 3; i++)
160b4f9fe12SLen Brown 		memcpy(dest + i, src + (3 - i), 1);
161b4f9fe12SLen Brown 
162b4f9fe12SLen Brown 	for (i = 0; i <= 1; i++)
163b4f9fe12SLen Brown 		memcpy(dest + 4 + i, src + (5 - i), 1);
164b4f9fe12SLen Brown 
165b4f9fe12SLen Brown 	for (i = 0; i <= 1; i++)
166b4f9fe12SLen Brown 		memcpy(dest + 6 + i, src + (7 - i), 1);
167b4f9fe12SLen Brown 
168b4f9fe12SLen Brown 	memcpy(dest + 8, src + 8, 8);
169b4f9fe12SLen Brown }
170b4f9fe12SLen Brown 
171b4f9fe12SLen Brown /**
172b4f9fe12SLen Brown  * wmi_parse_guid - Convert GUID from ASCII to binary
173b4f9fe12SLen Brown  * @src:   36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
174b4f9fe12SLen Brown  * @dest:  Memory block to hold binary GUID (16 bytes)
175b4f9fe12SLen Brown  *
176b4f9fe12SLen Brown  * N.B. The GUID need not be NULL terminated.
177b4f9fe12SLen Brown  *
178b4f9fe12SLen Brown  * Return:  'true'   @dest contains binary GUID
179b4f9fe12SLen Brown  *          'false'  @dest contents are undefined
180b4f9fe12SLen Brown  */
181b4f9fe12SLen Brown static bool wmi_parse_guid(const u8 *src, u8 *dest)
182b4f9fe12SLen Brown {
183b4f9fe12SLen Brown 	static const int size[] = { 4, 2, 2, 2, 6 };
184b4f9fe12SLen Brown 	int i, j, v;
185b4f9fe12SLen Brown 
186b4f9fe12SLen Brown 	if (src[8]  != '-' || src[13] != '-' ||
187b4f9fe12SLen Brown 		src[18] != '-' || src[23] != '-')
188b4f9fe12SLen Brown 		return false;
189b4f9fe12SLen Brown 
190b4f9fe12SLen Brown 	for (j = 0; j < 5; j++, src++) {
191b4f9fe12SLen Brown 		for (i = 0; i < size[j]; i++, src += 2, *dest++ = v) {
192b4f9fe12SLen Brown 			v = wmi_parse_hexbyte(src);
193b4f9fe12SLen Brown 			if (v < 0)
194b4f9fe12SLen Brown 				return false;
195b4f9fe12SLen Brown 		}
196b4f9fe12SLen Brown 	}
197b4f9fe12SLen Brown 
198b4f9fe12SLen Brown 	return true;
199b4f9fe12SLen Brown }
200b4f9fe12SLen Brown 
2011caab3c1SMatthew Garrett /*
2021caab3c1SMatthew Garrett  * Convert a raw GUID to the ACII string representation
2031caab3c1SMatthew Garrett  */
2041caab3c1SMatthew Garrett static int wmi_gtoa(const char *in, char *out)
2051caab3c1SMatthew Garrett {
2061caab3c1SMatthew Garrett 	int i;
2071caab3c1SMatthew Garrett 
2081caab3c1SMatthew Garrett 	for (i = 3; i >= 0; i--)
2091caab3c1SMatthew Garrett 		out += sprintf(out, "%02X", in[i] & 0xFF);
2101caab3c1SMatthew Garrett 
2111caab3c1SMatthew Garrett 	out += sprintf(out, "-");
2121caab3c1SMatthew Garrett 	out += sprintf(out, "%02X", in[5] & 0xFF);
2131caab3c1SMatthew Garrett 	out += sprintf(out, "%02X", in[4] & 0xFF);
2141caab3c1SMatthew Garrett 	out += sprintf(out, "-");
2151caab3c1SMatthew Garrett 	out += sprintf(out, "%02X", in[7] & 0xFF);
2161caab3c1SMatthew Garrett 	out += sprintf(out, "%02X", in[6] & 0xFF);
2171caab3c1SMatthew Garrett 	out += sprintf(out, "-");
2181caab3c1SMatthew Garrett 	out += sprintf(out, "%02X", in[8] & 0xFF);
2191caab3c1SMatthew Garrett 	out += sprintf(out, "%02X", in[9] & 0xFF);
2201caab3c1SMatthew Garrett 	out += sprintf(out, "-");
2211caab3c1SMatthew Garrett 
2221caab3c1SMatthew Garrett 	for (i = 10; i <= 15; i++)
2231caab3c1SMatthew Garrett 		out += sprintf(out, "%02X", in[i] & 0xFF);
2241caab3c1SMatthew Garrett 
2251caab3c1SMatthew Garrett 	out = '\0';
2261caab3c1SMatthew Garrett 	return 0;
2271caab3c1SMatthew Garrett }
2281caab3c1SMatthew Garrett 
229b4f9fe12SLen Brown static bool find_guid(const char *guid_string, struct wmi_block **out)
230b4f9fe12SLen Brown {
231b4f9fe12SLen Brown 	char tmp[16], guid_input[16];
232b4f9fe12SLen Brown 	struct wmi_block *wblock;
233b4f9fe12SLen Brown 	struct guid_block *block;
234b4f9fe12SLen Brown 	struct list_head *p;
235b4f9fe12SLen Brown 
236b4f9fe12SLen Brown 	wmi_parse_guid(guid_string, tmp);
237b4f9fe12SLen Brown 	wmi_swap_bytes(tmp, guid_input);
238b4f9fe12SLen Brown 
239b4f9fe12SLen Brown 	list_for_each(p, &wmi_blocks.list) {
240b4f9fe12SLen Brown 		wblock = list_entry(p, struct wmi_block, list);
241b4f9fe12SLen Brown 		block = &wblock->gblock;
242b4f9fe12SLen Brown 
243b4f9fe12SLen Brown 		if (memcmp(block->guid, guid_input, 16) == 0) {
244b4f9fe12SLen Brown 			if (out)
245b4f9fe12SLen Brown 				*out = wblock;
246b4f9fe12SLen Brown 			return 1;
247b4f9fe12SLen Brown 		}
248b4f9fe12SLen Brown 	}
249b4f9fe12SLen Brown 	return 0;
250b4f9fe12SLen Brown }
251b4f9fe12SLen Brown 
252b4f9fe12SLen Brown static acpi_status wmi_method_enable(struct wmi_block *wblock, int enable)
253b4f9fe12SLen Brown {
254b4f9fe12SLen Brown 	struct guid_block *block = NULL;
255b4f9fe12SLen Brown 	char method[5];
256b4f9fe12SLen Brown 	struct acpi_object_list input;
257b4f9fe12SLen Brown 	union acpi_object params[1];
258b4f9fe12SLen Brown 	acpi_status status;
259b4f9fe12SLen Brown 	acpi_handle handle;
260b4f9fe12SLen Brown 
261b4f9fe12SLen Brown 	block = &wblock->gblock;
262b4f9fe12SLen Brown 	handle = wblock->handle;
263b4f9fe12SLen Brown 
264b4f9fe12SLen Brown 	if (!block)
265b4f9fe12SLen Brown 		return AE_NOT_EXIST;
266b4f9fe12SLen Brown 
267b4f9fe12SLen Brown 	input.count = 1;
268b4f9fe12SLen Brown 	input.pointer = params;
269b4f9fe12SLen Brown 	params[0].type = ACPI_TYPE_INTEGER;
270b4f9fe12SLen Brown 	params[0].integer.value = enable;
271b4f9fe12SLen Brown 
272b4f9fe12SLen Brown 	snprintf(method, 5, "WE%02X", block->notify_id);
273b4f9fe12SLen Brown 	status = acpi_evaluate_object(handle, method, &input, NULL);
274b4f9fe12SLen Brown 
275b4f9fe12SLen Brown 	if (status != AE_OK && status != AE_NOT_FOUND)
276b4f9fe12SLen Brown 		return status;
277b4f9fe12SLen Brown 	else
278b4f9fe12SLen Brown 		return AE_OK;
279b4f9fe12SLen Brown }
280b4f9fe12SLen Brown 
281b4f9fe12SLen Brown /*
282b4f9fe12SLen Brown  * Exported WMI functions
283b4f9fe12SLen Brown  */
284b4f9fe12SLen Brown /**
285b4f9fe12SLen Brown  * wmi_evaluate_method - Evaluate a WMI method
286b4f9fe12SLen Brown  * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
287b4f9fe12SLen Brown  * @instance: Instance index
288b4f9fe12SLen Brown  * @method_id: Method ID to call
289b4f9fe12SLen Brown  * &in: Buffer containing input for the method call
290b4f9fe12SLen Brown  * &out: Empty buffer to return the method results
291b4f9fe12SLen Brown  *
292b4f9fe12SLen Brown  * Call an ACPI-WMI method
293b4f9fe12SLen Brown  */
294b4f9fe12SLen Brown acpi_status wmi_evaluate_method(const char *guid_string, u8 instance,
295b4f9fe12SLen Brown u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out)
296b4f9fe12SLen Brown {
297b4f9fe12SLen Brown 	struct guid_block *block = NULL;
298b4f9fe12SLen Brown 	struct wmi_block *wblock = NULL;
299b4f9fe12SLen Brown 	acpi_handle handle;
300b4f9fe12SLen Brown 	acpi_status status;
301b4f9fe12SLen Brown 	struct acpi_object_list input;
302b4f9fe12SLen Brown 	union acpi_object params[3];
303f3d83e24SCostantino Leandro 	char method[5] = "WM";
304b4f9fe12SLen Brown 
305b4f9fe12SLen Brown 	if (!find_guid(guid_string, &wblock))
306b4f9fe12SLen Brown 		return AE_ERROR;
307b4f9fe12SLen Brown 
308b4f9fe12SLen Brown 	block = &wblock->gblock;
309b4f9fe12SLen Brown 	handle = wblock->handle;
310b4f9fe12SLen Brown 
311b4f9fe12SLen Brown 	if (!(block->flags & ACPI_WMI_METHOD))
312b4f9fe12SLen Brown 		return AE_BAD_DATA;
313b4f9fe12SLen Brown 
314b4f9fe12SLen Brown 	if (block->instance_count < instance)
315b4f9fe12SLen Brown 		return AE_BAD_PARAMETER;
316b4f9fe12SLen Brown 
317b4f9fe12SLen Brown 	input.count = 2;
318b4f9fe12SLen Brown 	input.pointer = params;
319b4f9fe12SLen Brown 	params[0].type = ACPI_TYPE_INTEGER;
320b4f9fe12SLen Brown 	params[0].integer.value = instance;
321b4f9fe12SLen Brown 	params[1].type = ACPI_TYPE_INTEGER;
322b4f9fe12SLen Brown 	params[1].integer.value = method_id;
323b4f9fe12SLen Brown 
324b4f9fe12SLen Brown 	if (in) {
325b4f9fe12SLen Brown 		input.count = 3;
326b4f9fe12SLen Brown 
327b4f9fe12SLen Brown 		if (block->flags & ACPI_WMI_STRING) {
328b4f9fe12SLen Brown 			params[2].type = ACPI_TYPE_STRING;
329b4f9fe12SLen Brown 		} else {
330b4f9fe12SLen Brown 			params[2].type = ACPI_TYPE_BUFFER;
331b4f9fe12SLen Brown 		}
332b4f9fe12SLen Brown 		params[2].buffer.length = in->length;
333b4f9fe12SLen Brown 		params[2].buffer.pointer = in->pointer;
334b4f9fe12SLen Brown 	}
335b4f9fe12SLen Brown 
336b4f9fe12SLen Brown 	strncat(method, block->object_id, 2);
337b4f9fe12SLen Brown 
338b4f9fe12SLen Brown 	status = acpi_evaluate_object(handle, method, &input, out);
339b4f9fe12SLen Brown 
340b4f9fe12SLen Brown 	return status;
341b4f9fe12SLen Brown }
342b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_evaluate_method);
343b4f9fe12SLen Brown 
344b4f9fe12SLen Brown /**
345b4f9fe12SLen Brown  * wmi_query_block - Return contents of a WMI block
346b4f9fe12SLen Brown  * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
347b4f9fe12SLen Brown  * @instance: Instance index
348b4f9fe12SLen Brown  * &out: Empty buffer to return the contents of the data block to
349b4f9fe12SLen Brown  *
350b4f9fe12SLen Brown  * Return the contents of an ACPI-WMI data block to a buffer
351b4f9fe12SLen Brown  */
352b4f9fe12SLen Brown acpi_status wmi_query_block(const char *guid_string, u8 instance,
353b4f9fe12SLen Brown struct acpi_buffer *out)
354b4f9fe12SLen Brown {
355b4f9fe12SLen Brown 	struct guid_block *block = NULL;
356b4f9fe12SLen Brown 	struct wmi_block *wblock = NULL;
357b4f9fe12SLen Brown 	acpi_handle handle, wc_handle;
358b4f9fe12SLen Brown 	acpi_status status, wc_status = AE_ERROR;
359b4f9fe12SLen Brown 	struct acpi_object_list input, wc_input;
360b4f9fe12SLen Brown 	union acpi_object wc_params[1], wq_params[1];
361f3d83e24SCostantino Leandro 	char method[5];
362f3d83e24SCostantino Leandro 	char wc_method[5] = "WC";
363b4f9fe12SLen Brown 
364b4f9fe12SLen Brown 	if (!guid_string || !out)
365b4f9fe12SLen Brown 		return AE_BAD_PARAMETER;
366b4f9fe12SLen Brown 
367b4f9fe12SLen Brown 	if (!find_guid(guid_string, &wblock))
368b4f9fe12SLen Brown 		return AE_ERROR;
369b4f9fe12SLen Brown 
370b4f9fe12SLen Brown 	block = &wblock->gblock;
371b4f9fe12SLen Brown 	handle = wblock->handle;
372b4f9fe12SLen Brown 
373b4f9fe12SLen Brown 	if (block->instance_count < instance)
374b4f9fe12SLen Brown 		return AE_BAD_PARAMETER;
375b4f9fe12SLen Brown 
376b4f9fe12SLen Brown 	/* Check GUID is a data block */
377b4f9fe12SLen Brown 	if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD))
378b4f9fe12SLen Brown 		return AE_ERROR;
379b4f9fe12SLen Brown 
380b4f9fe12SLen Brown 	input.count = 1;
381b4f9fe12SLen Brown 	input.pointer = wq_params;
382b4f9fe12SLen Brown 	wq_params[0].type = ACPI_TYPE_INTEGER;
383b4f9fe12SLen Brown 	wq_params[0].integer.value = instance;
384b4f9fe12SLen Brown 
385b4f9fe12SLen Brown 	/*
386b4f9fe12SLen Brown 	 * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method first to
387b4f9fe12SLen Brown 	 * enable collection.
388b4f9fe12SLen Brown 	 */
389b4f9fe12SLen Brown 	if (block->flags & ACPI_WMI_EXPENSIVE) {
390b4f9fe12SLen Brown 		wc_input.count = 1;
391b4f9fe12SLen Brown 		wc_input.pointer = wc_params;
392b4f9fe12SLen Brown 		wc_params[0].type = ACPI_TYPE_INTEGER;
393b4f9fe12SLen Brown 		wc_params[0].integer.value = 1;
394b4f9fe12SLen Brown 
395b4f9fe12SLen Brown 		strncat(wc_method, block->object_id, 2);
396b4f9fe12SLen Brown 
397b4f9fe12SLen Brown 		/*
398b4f9fe12SLen Brown 		 * Some GUIDs break the specification by declaring themselves
399b4f9fe12SLen Brown 		 * expensive, but have no corresponding WCxx method. So we
400b4f9fe12SLen Brown 		 * should not fail if this happens.
401b4f9fe12SLen Brown 		 */
402b4f9fe12SLen Brown 		wc_status = acpi_get_handle(handle, wc_method, &wc_handle);
403b4f9fe12SLen Brown 		if (ACPI_SUCCESS(wc_status))
404b4f9fe12SLen Brown 			wc_status = acpi_evaluate_object(handle, wc_method,
405b4f9fe12SLen Brown 				&wc_input, NULL);
406b4f9fe12SLen Brown 	}
407b4f9fe12SLen Brown 
408b4f9fe12SLen Brown 	strcpy(method, "WQ");
409b4f9fe12SLen Brown 	strncat(method, block->object_id, 2);
410b4f9fe12SLen Brown 
411b4f9fe12SLen Brown 	status = acpi_evaluate_object(handle, method, &input, out);
412b4f9fe12SLen Brown 
413b4f9fe12SLen Brown 	/*
414b4f9fe12SLen Brown 	 * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method, even if
415b4f9fe12SLen Brown 	 * the WQxx method failed - we should disable collection anyway.
416b4f9fe12SLen Brown 	 */
417b4f9fe12SLen Brown 	if ((block->flags & ACPI_WMI_EXPENSIVE) && ACPI_SUCCESS(wc_status)) {
418b4f9fe12SLen Brown 		wc_params[0].integer.value = 0;
419b4f9fe12SLen Brown 		status = acpi_evaluate_object(handle,
420b4f9fe12SLen Brown 		wc_method, &wc_input, NULL);
421b4f9fe12SLen Brown 	}
422b4f9fe12SLen Brown 
423b4f9fe12SLen Brown 	return status;
424b4f9fe12SLen Brown }
425b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_query_block);
426b4f9fe12SLen Brown 
427b4f9fe12SLen Brown /**
428b4f9fe12SLen Brown  * wmi_set_block - Write to a WMI block
429b4f9fe12SLen Brown  * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
430b4f9fe12SLen Brown  * @instance: Instance index
431b4f9fe12SLen Brown  * &in: Buffer containing new values for the data block
432b4f9fe12SLen Brown  *
433b4f9fe12SLen Brown  * Write the contents of the input buffer to an ACPI-WMI data block
434b4f9fe12SLen Brown  */
435b4f9fe12SLen Brown acpi_status wmi_set_block(const char *guid_string, u8 instance,
436b4f9fe12SLen Brown const struct acpi_buffer *in)
437b4f9fe12SLen Brown {
438b4f9fe12SLen Brown 	struct guid_block *block = NULL;
439b4f9fe12SLen Brown 	struct wmi_block *wblock = NULL;
440b4f9fe12SLen Brown 	acpi_handle handle;
441b4f9fe12SLen Brown 	struct acpi_object_list input;
442b4f9fe12SLen Brown 	union acpi_object params[2];
443f3d83e24SCostantino Leandro 	char method[5] = "WS";
444b4f9fe12SLen Brown 
445b4f9fe12SLen Brown 	if (!guid_string || !in)
446b4f9fe12SLen Brown 		return AE_BAD_DATA;
447b4f9fe12SLen Brown 
448b4f9fe12SLen Brown 	if (!find_guid(guid_string, &wblock))
449b4f9fe12SLen Brown 		return AE_ERROR;
450b4f9fe12SLen Brown 
451b4f9fe12SLen Brown 	block = &wblock->gblock;
452b4f9fe12SLen Brown 	handle = wblock->handle;
453b4f9fe12SLen Brown 
454b4f9fe12SLen Brown 	if (block->instance_count < instance)
455b4f9fe12SLen Brown 		return AE_BAD_PARAMETER;
456b4f9fe12SLen Brown 
457b4f9fe12SLen Brown 	/* Check GUID is a data block */
458b4f9fe12SLen Brown 	if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD))
459b4f9fe12SLen Brown 		return AE_ERROR;
460b4f9fe12SLen Brown 
461b4f9fe12SLen Brown 	input.count = 2;
462b4f9fe12SLen Brown 	input.pointer = params;
463b4f9fe12SLen Brown 	params[0].type = ACPI_TYPE_INTEGER;
464b4f9fe12SLen Brown 	params[0].integer.value = instance;
465b4f9fe12SLen Brown 
466b4f9fe12SLen Brown 	if (block->flags & ACPI_WMI_STRING) {
467b4f9fe12SLen Brown 		params[1].type = ACPI_TYPE_STRING;
468b4f9fe12SLen Brown 	} else {
469b4f9fe12SLen Brown 		params[1].type = ACPI_TYPE_BUFFER;
470b4f9fe12SLen Brown 	}
471b4f9fe12SLen Brown 	params[1].buffer.length = in->length;
472b4f9fe12SLen Brown 	params[1].buffer.pointer = in->pointer;
473b4f9fe12SLen Brown 
474b4f9fe12SLen Brown 	strncat(method, block->object_id, 2);
475b4f9fe12SLen Brown 
476b4f9fe12SLen Brown 	return acpi_evaluate_object(handle, method, &input, NULL);
477b4f9fe12SLen Brown }
478b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_set_block);
479b4f9fe12SLen Brown 
480b4f9fe12SLen Brown /**
481b4f9fe12SLen Brown  * wmi_install_notify_handler - Register handler for WMI events
482b4f9fe12SLen Brown  * @handler: Function to handle notifications
483b4f9fe12SLen Brown  * @data: Data to be returned to handler when event is fired
484b4f9fe12SLen Brown  *
485b4f9fe12SLen Brown  * Register a handler for events sent to the ACPI-WMI mapper device.
486b4f9fe12SLen Brown  */
487b4f9fe12SLen Brown acpi_status wmi_install_notify_handler(const char *guid,
488b4f9fe12SLen Brown wmi_notify_handler handler, void *data)
489b4f9fe12SLen Brown {
490b4f9fe12SLen Brown 	struct wmi_block *block;
491b4f9fe12SLen Brown 	acpi_status status;
492b4f9fe12SLen Brown 
493b4f9fe12SLen Brown 	if (!guid || !handler)
494b4f9fe12SLen Brown 		return AE_BAD_PARAMETER;
495b4f9fe12SLen Brown 
496c03b26a5SPaul Rolland 	if (!find_guid(guid, &block))
497b4f9fe12SLen Brown 		return AE_NOT_EXIST;
498b4f9fe12SLen Brown 
499b4f9fe12SLen Brown 	if (block->handler)
500b4f9fe12SLen Brown 		return AE_ALREADY_ACQUIRED;
501b4f9fe12SLen Brown 
502b4f9fe12SLen Brown 	block->handler = handler;
503b4f9fe12SLen Brown 	block->handler_data = data;
504b4f9fe12SLen Brown 
505b4f9fe12SLen Brown 	status = wmi_method_enable(block, 1);
506b4f9fe12SLen Brown 
507b4f9fe12SLen Brown 	return status;
508b4f9fe12SLen Brown }
509b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_install_notify_handler);
510b4f9fe12SLen Brown 
511b4f9fe12SLen Brown /**
512b4f9fe12SLen Brown  * wmi_uninstall_notify_handler - Unregister handler for WMI events
513b4f9fe12SLen Brown  *
514b4f9fe12SLen Brown  * Unregister handler for events sent to the ACPI-WMI mapper device.
515b4f9fe12SLen Brown  */
516b4f9fe12SLen Brown acpi_status wmi_remove_notify_handler(const char *guid)
517b4f9fe12SLen Brown {
518b4f9fe12SLen Brown 	struct wmi_block *block;
519b4f9fe12SLen Brown 	acpi_status status;
520b4f9fe12SLen Brown 
521b4f9fe12SLen Brown 	if (!guid)
522b4f9fe12SLen Brown 		return AE_BAD_PARAMETER;
523b4f9fe12SLen Brown 
524c03b26a5SPaul Rolland 	if (!find_guid(guid, &block))
525b4f9fe12SLen Brown 		return AE_NOT_EXIST;
526b4f9fe12SLen Brown 
527b4f9fe12SLen Brown 	if (!block->handler)
528b4f9fe12SLen Brown 		return AE_NULL_ENTRY;
529b4f9fe12SLen Brown 
530b4f9fe12SLen Brown 	status = wmi_method_enable(block, 0);
531b4f9fe12SLen Brown 
532b4f9fe12SLen Brown 	block->handler = NULL;
533b4f9fe12SLen Brown 	block->handler_data = NULL;
534b4f9fe12SLen Brown 
535b4f9fe12SLen Brown 	return status;
536b4f9fe12SLen Brown }
537b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_remove_notify_handler);
538b4f9fe12SLen Brown 
539b4f9fe12SLen Brown /**
540b4f9fe12SLen Brown  * wmi_get_event_data - Get WMI data associated with an event
541b4f9fe12SLen Brown  *
5423e9b988eSAnisse Astier  * @event: Event to find
5433e9b988eSAnisse Astier  * @out: Buffer to hold event data. out->pointer should be freed with kfree()
544b4f9fe12SLen Brown  *
545b4f9fe12SLen Brown  * Returns extra data associated with an event in WMI.
546b4f9fe12SLen Brown  */
547b4f9fe12SLen Brown acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out)
548b4f9fe12SLen Brown {
549b4f9fe12SLen Brown 	struct acpi_object_list input;
550b4f9fe12SLen Brown 	union acpi_object params[1];
551b4f9fe12SLen Brown 	struct guid_block *gblock;
552b4f9fe12SLen Brown 	struct wmi_block *wblock;
553b4f9fe12SLen Brown 	struct list_head *p;
554b4f9fe12SLen Brown 
555b4f9fe12SLen Brown 	input.count = 1;
556b4f9fe12SLen Brown 	input.pointer = params;
557b4f9fe12SLen Brown 	params[0].type = ACPI_TYPE_INTEGER;
558b4f9fe12SLen Brown 	params[0].integer.value = event;
559b4f9fe12SLen Brown 
560b4f9fe12SLen Brown 	list_for_each(p, &wmi_blocks.list) {
561b4f9fe12SLen Brown 		wblock = list_entry(p, struct wmi_block, list);
562b4f9fe12SLen Brown 		gblock = &wblock->gblock;
563b4f9fe12SLen Brown 
564b4f9fe12SLen Brown 		if ((gblock->flags & ACPI_WMI_EVENT) &&
565b4f9fe12SLen Brown 			(gblock->notify_id == event))
566b4f9fe12SLen Brown 			return acpi_evaluate_object(wblock->handle, "_WED",
567b4f9fe12SLen Brown 				&input, out);
568b4f9fe12SLen Brown 	}
569b4f9fe12SLen Brown 
570b4f9fe12SLen Brown 	return AE_NOT_FOUND;
571b4f9fe12SLen Brown }
572b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_get_event_data);
573b4f9fe12SLen Brown 
574b4f9fe12SLen Brown /**
575b4f9fe12SLen Brown  * wmi_has_guid - Check if a GUID is available
576b4f9fe12SLen Brown  * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
577b4f9fe12SLen Brown  *
578b4f9fe12SLen Brown  * Check if a given GUID is defined by _WDG
579b4f9fe12SLen Brown  */
580b4f9fe12SLen Brown bool wmi_has_guid(const char *guid_string)
581b4f9fe12SLen Brown {
582b4f9fe12SLen Brown 	return find_guid(guid_string, NULL);
583b4f9fe12SLen Brown }
584b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_has_guid);
585b4f9fe12SLen Brown 
586b4f9fe12SLen Brown /*
5871caab3c1SMatthew Garrett  * sysfs interface
5881caab3c1SMatthew Garrett  */
5891caab3c1SMatthew Garrett static ssize_t show_modalias(struct device *dev, struct device_attribute *attr,
5901caab3c1SMatthew Garrett 			     char *buf)
5911caab3c1SMatthew Garrett {
5921caab3c1SMatthew Garrett 	char guid_string[37];
5931caab3c1SMatthew Garrett 	struct wmi_block *wblock;
5941caab3c1SMatthew Garrett 
5951caab3c1SMatthew Garrett 	wblock = dev_get_drvdata(dev);
5961caab3c1SMatthew Garrett 	if (!wblock)
5971caab3c1SMatthew Garrett 		return -ENOMEM;
5981caab3c1SMatthew Garrett 
5991caab3c1SMatthew Garrett 	wmi_gtoa(wblock->gblock.guid, guid_string);
6001caab3c1SMatthew Garrett 
6011caab3c1SMatthew Garrett 	return sprintf(buf, "wmi:%s\n", guid_string);
6021caab3c1SMatthew Garrett }
6031caab3c1SMatthew Garrett static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL);
6041caab3c1SMatthew Garrett 
6051caab3c1SMatthew Garrett static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env)
6061caab3c1SMatthew Garrett {
6071caab3c1SMatthew Garrett 	char guid_string[37];
6081caab3c1SMatthew Garrett 
6091caab3c1SMatthew Garrett 	struct wmi_block *wblock;
6101caab3c1SMatthew Garrett 
6111caab3c1SMatthew Garrett 	if (add_uevent_var(env, "MODALIAS="))
6121caab3c1SMatthew Garrett 		return -ENOMEM;
6131caab3c1SMatthew Garrett 
6141caab3c1SMatthew Garrett 	wblock = dev_get_drvdata(dev);
6151caab3c1SMatthew Garrett 	if (!wblock)
6161caab3c1SMatthew Garrett 		return -ENOMEM;
6171caab3c1SMatthew Garrett 
6181caab3c1SMatthew Garrett 	wmi_gtoa(wblock->gblock.guid, guid_string);
6191caab3c1SMatthew Garrett 
6201caab3c1SMatthew Garrett 	strcpy(&env->buf[env->buflen - 1], "wmi:");
6211caab3c1SMatthew Garrett 	memcpy(&env->buf[env->buflen - 1 + 4], guid_string, 36);
6221caab3c1SMatthew Garrett 	env->buflen += 40;
6231caab3c1SMatthew Garrett 
6241caab3c1SMatthew Garrett 	return 0;
6251caab3c1SMatthew Garrett }
6261caab3c1SMatthew Garrett 
6271caab3c1SMatthew Garrett static void wmi_dev_free(struct device *dev)
6281caab3c1SMatthew Garrett {
6291caab3c1SMatthew Garrett 	kfree(dev);
6301caab3c1SMatthew Garrett }
6311caab3c1SMatthew Garrett 
6321caab3c1SMatthew Garrett static struct class wmi_class = {
6331caab3c1SMatthew Garrett 	.name = "wmi",
6341caab3c1SMatthew Garrett 	.dev_release = wmi_dev_free,
6351caab3c1SMatthew Garrett 	.dev_uevent = wmi_dev_uevent,
6361caab3c1SMatthew Garrett };
6371caab3c1SMatthew Garrett 
6381caab3c1SMatthew Garrett static int wmi_create_devs(void)
6391caab3c1SMatthew Garrett {
6401caab3c1SMatthew Garrett 	int result;
6411caab3c1SMatthew Garrett 	char guid_string[37];
6421caab3c1SMatthew Garrett 	struct guid_block *gblock;
6431caab3c1SMatthew Garrett 	struct wmi_block *wblock;
6441caab3c1SMatthew Garrett 	struct list_head *p;
6451caab3c1SMatthew Garrett 	struct device *guid_dev;
6461caab3c1SMatthew Garrett 
6471caab3c1SMatthew Garrett 	/* Create devices for all the GUIDs */
6481caab3c1SMatthew Garrett 	list_for_each(p, &wmi_blocks.list) {
6491caab3c1SMatthew Garrett 		wblock = list_entry(p, struct wmi_block, list);
6501caab3c1SMatthew Garrett 
6511caab3c1SMatthew Garrett 		guid_dev = kzalloc(sizeof(struct device), GFP_KERNEL);
6521caab3c1SMatthew Garrett 		if (!guid_dev)
6531caab3c1SMatthew Garrett 			return -ENOMEM;
6541caab3c1SMatthew Garrett 
6551caab3c1SMatthew Garrett 		wblock->dev = guid_dev;
6561caab3c1SMatthew Garrett 
6571caab3c1SMatthew Garrett 		guid_dev->class = &wmi_class;
6581caab3c1SMatthew Garrett 		dev_set_drvdata(guid_dev, wblock);
6591caab3c1SMatthew Garrett 
6601caab3c1SMatthew Garrett 		gblock = &wblock->gblock;
6611caab3c1SMatthew Garrett 
6621caab3c1SMatthew Garrett 		wmi_gtoa(gblock->guid, guid_string);
6631caab3c1SMatthew Garrett 		dev_set_name(guid_dev, guid_string);
6641caab3c1SMatthew Garrett 
6651caab3c1SMatthew Garrett 		result = device_register(guid_dev);
6661caab3c1SMatthew Garrett 		if (result)
6671caab3c1SMatthew Garrett 			return result;
6681caab3c1SMatthew Garrett 
6691caab3c1SMatthew Garrett 		result = device_create_file(guid_dev, &dev_attr_modalias);
6701caab3c1SMatthew Garrett 		if (result)
6711caab3c1SMatthew Garrett 			return result;
6721caab3c1SMatthew Garrett 	}
6731caab3c1SMatthew Garrett 
6741caab3c1SMatthew Garrett 	return 0;
6751caab3c1SMatthew Garrett }
6761caab3c1SMatthew Garrett 
6771caab3c1SMatthew Garrett static void wmi_remove_devs(void)
6781caab3c1SMatthew Garrett {
6791caab3c1SMatthew Garrett 	struct guid_block *gblock;
6801caab3c1SMatthew Garrett 	struct wmi_block *wblock;
6811caab3c1SMatthew Garrett 	struct list_head *p;
6821caab3c1SMatthew Garrett 	struct device *guid_dev;
6831caab3c1SMatthew Garrett 
6841caab3c1SMatthew Garrett 	/* Delete devices for all the GUIDs */
6851caab3c1SMatthew Garrett 	list_for_each(p, &wmi_blocks.list) {
6861caab3c1SMatthew Garrett 		wblock = list_entry(p, struct wmi_block, list);
6871caab3c1SMatthew Garrett 
6881caab3c1SMatthew Garrett 		guid_dev = wblock->dev;
6891caab3c1SMatthew Garrett 		gblock = &wblock->gblock;
6901caab3c1SMatthew Garrett 
6911caab3c1SMatthew Garrett 		device_remove_file(guid_dev, &dev_attr_modalias);
6921caab3c1SMatthew Garrett 
6931caab3c1SMatthew Garrett 		device_unregister(guid_dev);
6941caab3c1SMatthew Garrett 	}
6951caab3c1SMatthew Garrett }
6961caab3c1SMatthew Garrett 
6971caab3c1SMatthew Garrett static void wmi_class_exit(void)
6981caab3c1SMatthew Garrett {
6991caab3c1SMatthew Garrett 	wmi_remove_devs();
7001caab3c1SMatthew Garrett 	class_unregister(&wmi_class);
7011caab3c1SMatthew Garrett }
7021caab3c1SMatthew Garrett 
7031caab3c1SMatthew Garrett static int wmi_class_init(void)
7041caab3c1SMatthew Garrett {
7051caab3c1SMatthew Garrett 	int ret;
7061caab3c1SMatthew Garrett 
7071caab3c1SMatthew Garrett 	ret = class_register(&wmi_class);
7081caab3c1SMatthew Garrett 	if (ret)
7091caab3c1SMatthew Garrett 		return ret;
7101caab3c1SMatthew Garrett 
7111caab3c1SMatthew Garrett 	ret = wmi_create_devs();
7121caab3c1SMatthew Garrett 	if (ret)
7131caab3c1SMatthew Garrett 		wmi_class_exit();
7141caab3c1SMatthew Garrett 
7151caab3c1SMatthew Garrett 	return ret;
7161caab3c1SMatthew Garrett }
7171caab3c1SMatthew Garrett 
718d1f9e497SCarlos Corbacho static bool guid_already_parsed(const char *guid_string)
719d1f9e497SCarlos Corbacho {
720d1f9e497SCarlos Corbacho 	struct guid_block *gblock;
721d1f9e497SCarlos Corbacho 	struct wmi_block *wblock;
722d1f9e497SCarlos Corbacho 	struct list_head *p;
723d1f9e497SCarlos Corbacho 
724d1f9e497SCarlos Corbacho 	list_for_each(p, &wmi_blocks.list) {
725d1f9e497SCarlos Corbacho 		wblock = list_entry(p, struct wmi_block, list);
726d1f9e497SCarlos Corbacho 		gblock = &wblock->gblock;
727d1f9e497SCarlos Corbacho 
728d1f9e497SCarlos Corbacho 		if (strncmp(gblock->guid, guid_string, 16) == 0)
729d1f9e497SCarlos Corbacho 			return true;
730d1f9e497SCarlos Corbacho 	}
731d1f9e497SCarlos Corbacho 	return false;
732d1f9e497SCarlos Corbacho }
733d1f9e497SCarlos Corbacho 
7341caab3c1SMatthew Garrett /*
735b4f9fe12SLen Brown  * Parse the _WDG method for the GUID data blocks
736b4f9fe12SLen Brown  */
737b4f9fe12SLen Brown static __init acpi_status parse_wdg(acpi_handle handle)
738b4f9fe12SLen Brown {
739b4f9fe12SLen Brown 	struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
740b4f9fe12SLen Brown 	union acpi_object *obj;
741b4f9fe12SLen Brown 	struct guid_block *gblock;
742b4f9fe12SLen Brown 	struct wmi_block *wblock;
743d1f9e497SCarlos Corbacho 	char guid_string[37];
744b4f9fe12SLen Brown 	acpi_status status;
745b4f9fe12SLen Brown 	u32 i, total;
746b4f9fe12SLen Brown 
747b4f9fe12SLen Brown 	status = acpi_evaluate_object(handle, "_WDG", NULL, &out);
748b4f9fe12SLen Brown 
749b4f9fe12SLen Brown 	if (ACPI_FAILURE(status))
750b4f9fe12SLen Brown 		return status;
751b4f9fe12SLen Brown 
752b4f9fe12SLen Brown 	obj = (union acpi_object *) out.pointer;
753b4f9fe12SLen Brown 
754b4f9fe12SLen Brown 	if (obj->type != ACPI_TYPE_BUFFER)
755b4f9fe12SLen Brown 		return AE_ERROR;
756b4f9fe12SLen Brown 
757b4f9fe12SLen Brown 	total = obj->buffer.length / sizeof(struct guid_block);
758b4f9fe12SLen Brown 
7592c6719a3SJulia Lawall 	gblock = kmemdup(obj->buffer.pointer, obj->buffer.length, GFP_KERNEL);
760b4f9fe12SLen Brown 	if (!gblock)
761b4f9fe12SLen Brown 		return AE_NO_MEMORY;
762b4f9fe12SLen Brown 
763b4f9fe12SLen Brown 	for (i = 0; i < total; i++) {
764d1f9e497SCarlos Corbacho 		/*
765d1f9e497SCarlos Corbacho 		  Some WMI devices, like those for nVidia hooks, have a
766d1f9e497SCarlos Corbacho 		  duplicate GUID. It's not clear what we should do in this
767d1f9e497SCarlos Corbacho 		  case yet, so for now, we'll just ignore the duplicate.
768d1f9e497SCarlos Corbacho 		  Anyone who wants to add support for that device can come
769d1f9e497SCarlos Corbacho 		  up with a better workaround for the mess then.
770d1f9e497SCarlos Corbacho 		*/
771d1f9e497SCarlos Corbacho 		if (guid_already_parsed(gblock[i].guid) == true) {
772d1f9e497SCarlos Corbacho 			wmi_gtoa(gblock[i].guid, guid_string);
773d1f9e497SCarlos Corbacho 			printk(KERN_INFO PREFIX "Skipping duplicate GUID %s\n",
774d1f9e497SCarlos Corbacho 				guid_string);
775d1f9e497SCarlos Corbacho 			continue;
776d1f9e497SCarlos Corbacho 		}
777b4f9fe12SLen Brown 		wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL);
778b4f9fe12SLen Brown 		if (!wblock)
779b4f9fe12SLen Brown 			return AE_NO_MEMORY;
780b4f9fe12SLen Brown 
781b4f9fe12SLen Brown 		wblock->gblock = gblock[i];
782b4f9fe12SLen Brown 		wblock->handle = handle;
783b4f9fe12SLen Brown 		list_add_tail(&wblock->list, &wmi_blocks.list);
784b4f9fe12SLen Brown 	}
785b4f9fe12SLen Brown 
786b4f9fe12SLen Brown 	kfree(out.pointer);
787b4f9fe12SLen Brown 	kfree(gblock);
788b4f9fe12SLen Brown 
789b4f9fe12SLen Brown 	return status;
790b4f9fe12SLen Brown }
791b4f9fe12SLen Brown 
792b4f9fe12SLen Brown /*
793b4f9fe12SLen Brown  * WMI can have EmbeddedControl access regions. In which case, we just want to
794b4f9fe12SLen Brown  * hand these off to the EC driver.
795b4f9fe12SLen Brown  */
796b4f9fe12SLen Brown static acpi_status
797b4f9fe12SLen Brown acpi_wmi_ec_space_handler(u32 function, acpi_physical_address address,
798439913ffSLin Ming 		      u32 bits, u64 *value,
799b4f9fe12SLen Brown 		      void *handler_context, void *region_context)
800b4f9fe12SLen Brown {
801b4f9fe12SLen Brown 	int result = 0, i = 0;
802b4f9fe12SLen Brown 	u8 temp = 0;
803b4f9fe12SLen Brown 
804b4f9fe12SLen Brown 	if ((address > 0xFF) || !value)
805b4f9fe12SLen Brown 		return AE_BAD_PARAMETER;
806b4f9fe12SLen Brown 
807b4f9fe12SLen Brown 	if (function != ACPI_READ && function != ACPI_WRITE)
808b4f9fe12SLen Brown 		return AE_BAD_PARAMETER;
809b4f9fe12SLen Brown 
810b4f9fe12SLen Brown 	if (bits != 8)
811b4f9fe12SLen Brown 		return AE_BAD_PARAMETER;
812b4f9fe12SLen Brown 
813b4f9fe12SLen Brown 	if (function == ACPI_READ) {
814b4f9fe12SLen Brown 		result = ec_read(address, &temp);
815439913ffSLin Ming 		(*value) |= ((u64)temp) << i;
816b4f9fe12SLen Brown 	} else {
817b4f9fe12SLen Brown 		temp = 0xff & ((*value) >> i);
818b4f9fe12SLen Brown 		result = ec_write(address, temp);
819b4f9fe12SLen Brown 	}
820b4f9fe12SLen Brown 
821b4f9fe12SLen Brown 	switch (result) {
822b4f9fe12SLen Brown 	case -EINVAL:
823b4f9fe12SLen Brown 		return AE_BAD_PARAMETER;
824b4f9fe12SLen Brown 		break;
825b4f9fe12SLen Brown 	case -ENODEV:
826b4f9fe12SLen Brown 		return AE_NOT_FOUND;
827b4f9fe12SLen Brown 		break;
828b4f9fe12SLen Brown 	case -ETIME:
829b4f9fe12SLen Brown 		return AE_TIME;
830b4f9fe12SLen Brown 		break;
831b4f9fe12SLen Brown 	default:
832b4f9fe12SLen Brown 		return AE_OK;
833b4f9fe12SLen Brown 	}
834b4f9fe12SLen Brown }
835b4f9fe12SLen Brown 
836f61bb939SBjorn Helgaas static void acpi_wmi_notify(struct acpi_device *device, u32 event)
837b4f9fe12SLen Brown {
838b4f9fe12SLen Brown 	struct guid_block *block;
839b4f9fe12SLen Brown 	struct wmi_block *wblock;
840b4f9fe12SLen Brown 	struct list_head *p;
841b4f9fe12SLen Brown 
842b4f9fe12SLen Brown 	list_for_each(p, &wmi_blocks.list) {
843b4f9fe12SLen Brown 		wblock = list_entry(p, struct wmi_block, list);
844b4f9fe12SLen Brown 		block = &wblock->gblock;
845b4f9fe12SLen Brown 
846b4f9fe12SLen Brown 		if ((block->flags & ACPI_WMI_EVENT) &&
847b4f9fe12SLen Brown 			(block->notify_id == event)) {
848b4f9fe12SLen Brown 			if (wblock->handler)
849b4f9fe12SLen Brown 				wblock->handler(event, wblock->handler_data);
850b4f9fe12SLen Brown 
851b4f9fe12SLen Brown 			acpi_bus_generate_netlink_event(
852b4f9fe12SLen Brown 				device->pnp.device_class, dev_name(&device->dev),
853b4f9fe12SLen Brown 				event, 0);
854b4f9fe12SLen Brown 			break;
855b4f9fe12SLen Brown 		}
856b4f9fe12SLen Brown 	}
857b4f9fe12SLen Brown }
858b4f9fe12SLen Brown 
859b4f9fe12SLen Brown static int acpi_wmi_remove(struct acpi_device *device, int type)
860b4f9fe12SLen Brown {
861b4f9fe12SLen Brown 	acpi_remove_address_space_handler(device->handle,
862b4f9fe12SLen Brown 				ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler);
863b4f9fe12SLen Brown 
864b4f9fe12SLen Brown 	return 0;
865b4f9fe12SLen Brown }
866b4f9fe12SLen Brown 
867b4f9fe12SLen Brown static int __init acpi_wmi_add(struct acpi_device *device)
868b4f9fe12SLen Brown {
869b4f9fe12SLen Brown 	acpi_status status;
870b4f9fe12SLen Brown 	int result = 0;
871b4f9fe12SLen Brown 
872b4f9fe12SLen Brown 	status = acpi_install_address_space_handler(device->handle,
873b4f9fe12SLen Brown 						    ACPI_ADR_SPACE_EC,
874b4f9fe12SLen Brown 						    &acpi_wmi_ec_space_handler,
875b4f9fe12SLen Brown 						    NULL, NULL);
876b4f9fe12SLen Brown 	if (ACPI_FAILURE(status))
877b4f9fe12SLen Brown 		return -ENODEV;
878b4f9fe12SLen Brown 
879b4f9fe12SLen Brown 	status = parse_wdg(device->handle);
880b4f9fe12SLen Brown 	if (ACPI_FAILURE(status)) {
881b4f9fe12SLen Brown 		printk(KERN_ERR PREFIX "Error installing EC region handler\n");
882b4f9fe12SLen Brown 		return -ENODEV;
883b4f9fe12SLen Brown 	}
884b4f9fe12SLen Brown 
885b4f9fe12SLen Brown 	return result;
886b4f9fe12SLen Brown }
887b4f9fe12SLen Brown 
888b4f9fe12SLen Brown static int __init acpi_wmi_init(void)
889b4f9fe12SLen Brown {
890da511997SRoel Kluin 	int result;
891b4f9fe12SLen Brown 
892b4f9fe12SLen Brown 	INIT_LIST_HEAD(&wmi_blocks.list);
893b4f9fe12SLen Brown 
894b4f9fe12SLen Brown 	if (acpi_disabled)
895b4f9fe12SLen Brown 		return -ENODEV;
896b4f9fe12SLen Brown 
897b4f9fe12SLen Brown 	result = acpi_bus_register_driver(&acpi_wmi_driver);
898b4f9fe12SLen Brown 
899b4f9fe12SLen Brown 	if (result < 0) {
900b4f9fe12SLen Brown 		printk(KERN_INFO PREFIX "Error loading mapper\n");
9011caab3c1SMatthew Garrett 		return -ENODEV;
902b4f9fe12SLen Brown 	}
903b4f9fe12SLen Brown 
9041caab3c1SMatthew Garrett 	result = wmi_class_init();
9051caab3c1SMatthew Garrett 	if (result) {
9061caab3c1SMatthew Garrett 		acpi_bus_unregister_driver(&acpi_wmi_driver);
9071caab3c1SMatthew Garrett 		return result;
9081caab3c1SMatthew Garrett 	}
9091caab3c1SMatthew Garrett 
9101caab3c1SMatthew Garrett 	printk(KERN_INFO PREFIX "Mapper loaded\n");
9111caab3c1SMatthew Garrett 
912b4f9fe12SLen Brown 	return result;
913b4f9fe12SLen Brown }
914b4f9fe12SLen Brown 
915b4f9fe12SLen Brown static void __exit acpi_wmi_exit(void)
916b4f9fe12SLen Brown {
917b4f9fe12SLen Brown 	struct list_head *p, *tmp;
918b4f9fe12SLen Brown 	struct wmi_block *wblock;
919b4f9fe12SLen Brown 
9201caab3c1SMatthew Garrett 	wmi_class_exit();
9211caab3c1SMatthew Garrett 
922b4f9fe12SLen Brown 	acpi_bus_unregister_driver(&acpi_wmi_driver);
923b4f9fe12SLen Brown 
924b4f9fe12SLen Brown 	list_for_each_safe(p, tmp, &wmi_blocks.list) {
925b4f9fe12SLen Brown 		wblock = list_entry(p, struct wmi_block, list);
926b4f9fe12SLen Brown 
927b4f9fe12SLen Brown 		list_del(p);
928b4f9fe12SLen Brown 		kfree(wblock);
929b4f9fe12SLen Brown 	}
930b4f9fe12SLen Brown 
931b4f9fe12SLen Brown 	printk(KERN_INFO PREFIX "Mapper unloaded\n");
932b4f9fe12SLen Brown }
933b4f9fe12SLen Brown 
934b4f9fe12SLen Brown subsys_initcall(acpi_wmi_init);
935b4f9fe12SLen Brown module_exit(acpi_wmi_exit);
936