xref: /openbmc/linux/drivers/platform/x86/wmi.c (revision 925b1089)
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 
84fc3155b2SThomas Renninger static int 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 
89a929aae0SThomas Renninger static int 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 
94b4f9fe12SLen Brown static int acpi_wmi_remove(struct acpi_device *device, int type);
95b4f9fe12SLen Brown static int acpi_wmi_add(struct acpi_device *device);
96f61bb939SBjorn Helgaas static void acpi_wmi_notify(struct acpi_device *device, u32 event);
97b4f9fe12SLen Brown 
98b4f9fe12SLen Brown static const struct acpi_device_id wmi_device_ids[] = {
99b4f9fe12SLen Brown 	{"PNP0C14", 0},
100b4f9fe12SLen Brown 	{"pnp0c14", 0},
101b4f9fe12SLen Brown 	{"", 0},
102b4f9fe12SLen Brown };
103b4f9fe12SLen Brown MODULE_DEVICE_TABLE(acpi, wmi_device_ids);
104b4f9fe12SLen Brown 
105b4f9fe12SLen Brown static struct acpi_driver acpi_wmi_driver = {
106b4f9fe12SLen Brown 	.name = "wmi",
107b4f9fe12SLen Brown 	.class = ACPI_WMI_CLASS,
108b4f9fe12SLen Brown 	.ids = wmi_device_ids,
109b4f9fe12SLen Brown 	.ops = {
110b4f9fe12SLen Brown 		.add = acpi_wmi_add,
111b4f9fe12SLen Brown 		.remove = acpi_wmi_remove,
112f61bb939SBjorn Helgaas 		.notify = acpi_wmi_notify,
113b4f9fe12SLen Brown 		},
114b4f9fe12SLen Brown };
115b4f9fe12SLen Brown 
116b4f9fe12SLen Brown /*
117b4f9fe12SLen Brown  * GUID parsing functions
118b4f9fe12SLen Brown  */
119b4f9fe12SLen Brown 
120b4f9fe12SLen Brown /**
121b4f9fe12SLen Brown  * wmi_parse_hexbyte - Convert a ASCII hex number to a byte
122b4f9fe12SLen Brown  * @src:  Pointer to at least 2 characters to convert.
123b4f9fe12SLen Brown  *
124b4f9fe12SLen Brown  * Convert a two character ASCII hex string to a number.
125b4f9fe12SLen Brown  *
126b4f9fe12SLen Brown  * Return:  0-255  Success, the byte was parsed correctly
127b4f9fe12SLen Brown  *          -1     Error, an invalid character was supplied
128b4f9fe12SLen Brown  */
129b4f9fe12SLen Brown static int wmi_parse_hexbyte(const u8 *src)
130b4f9fe12SLen Brown {
131b4f9fe12SLen Brown 	unsigned int x; /* For correct wrapping */
132b4f9fe12SLen Brown 	int h;
133b4f9fe12SLen Brown 
134b4f9fe12SLen Brown 	/* high part */
135b4f9fe12SLen Brown 	x = src[0];
136b4f9fe12SLen Brown 	if (x - '0' <= '9' - '0') {
137b4f9fe12SLen Brown 		h = x - '0';
138b4f9fe12SLen Brown 	} else if (x - 'a' <= 'f' - 'a') {
139b4f9fe12SLen Brown 		h = x - 'a' + 10;
140b4f9fe12SLen Brown 	} else if (x - 'A' <= 'F' - 'A') {
141b4f9fe12SLen Brown 		h = x - 'A' + 10;
142b4f9fe12SLen Brown 	} else {
143b4f9fe12SLen Brown 		return -1;
144b4f9fe12SLen Brown 	}
145b4f9fe12SLen Brown 	h <<= 4;
146b4f9fe12SLen Brown 
147b4f9fe12SLen Brown 	/* low part */
148b4f9fe12SLen Brown 	x = src[1];
149b4f9fe12SLen Brown 	if (x - '0' <= '9' - '0')
150b4f9fe12SLen Brown 		return h | (x - '0');
151b4f9fe12SLen Brown 	if (x - 'a' <= 'f' - 'a')
152b4f9fe12SLen Brown 		return h | (x - 'a' + 10);
153b4f9fe12SLen Brown 	if (x - 'A' <= 'F' - 'A')
154b4f9fe12SLen Brown 		return h | (x - 'A' + 10);
155b4f9fe12SLen Brown 	return -1;
156b4f9fe12SLen Brown }
157b4f9fe12SLen Brown 
158b4f9fe12SLen Brown /**
159b4f9fe12SLen Brown  * wmi_swap_bytes - Rearrange GUID bytes to match GUID binary
160b4f9fe12SLen Brown  * @src:   Memory block holding binary GUID (16 bytes)
161b4f9fe12SLen Brown  * @dest:  Memory block to hold byte swapped binary GUID (16 bytes)
162b4f9fe12SLen Brown  *
163b4f9fe12SLen Brown  * Byte swap a binary GUID to match it's real GUID value
164b4f9fe12SLen Brown  */
165b4f9fe12SLen Brown static void wmi_swap_bytes(u8 *src, u8 *dest)
166b4f9fe12SLen Brown {
167b4f9fe12SLen Brown 	int i;
168b4f9fe12SLen Brown 
169b4f9fe12SLen Brown 	for (i = 0; i <= 3; i++)
170b4f9fe12SLen Brown 		memcpy(dest + i, src + (3 - i), 1);
171b4f9fe12SLen Brown 
172b4f9fe12SLen Brown 	for (i = 0; i <= 1; i++)
173b4f9fe12SLen Brown 		memcpy(dest + 4 + i, src + (5 - i), 1);
174b4f9fe12SLen Brown 
175b4f9fe12SLen Brown 	for (i = 0; i <= 1; i++)
176b4f9fe12SLen Brown 		memcpy(dest + 6 + i, src + (7 - i), 1);
177b4f9fe12SLen Brown 
178b4f9fe12SLen Brown 	memcpy(dest + 8, src + 8, 8);
179b4f9fe12SLen Brown }
180b4f9fe12SLen Brown 
181b4f9fe12SLen Brown /**
182b4f9fe12SLen Brown  * wmi_parse_guid - Convert GUID from ASCII to binary
183b4f9fe12SLen Brown  * @src:   36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
184b4f9fe12SLen Brown  * @dest:  Memory block to hold binary GUID (16 bytes)
185b4f9fe12SLen Brown  *
186b4f9fe12SLen Brown  * N.B. The GUID need not be NULL terminated.
187b4f9fe12SLen Brown  *
188b4f9fe12SLen Brown  * Return:  'true'   @dest contains binary GUID
189b4f9fe12SLen Brown  *          'false'  @dest contents are undefined
190b4f9fe12SLen Brown  */
191b4f9fe12SLen Brown static bool wmi_parse_guid(const u8 *src, u8 *dest)
192b4f9fe12SLen Brown {
193b4f9fe12SLen Brown 	static const int size[] = { 4, 2, 2, 2, 6 };
194b4f9fe12SLen Brown 	int i, j, v;
195b4f9fe12SLen Brown 
196b4f9fe12SLen Brown 	if (src[8]  != '-' || src[13] != '-' ||
197b4f9fe12SLen Brown 		src[18] != '-' || src[23] != '-')
198b4f9fe12SLen Brown 		return false;
199b4f9fe12SLen Brown 
200b4f9fe12SLen Brown 	for (j = 0; j < 5; j++, src++) {
201b4f9fe12SLen Brown 		for (i = 0; i < size[j]; i++, src += 2, *dest++ = v) {
202b4f9fe12SLen Brown 			v = wmi_parse_hexbyte(src);
203b4f9fe12SLen Brown 			if (v < 0)
204b4f9fe12SLen Brown 				return false;
205b4f9fe12SLen Brown 		}
206b4f9fe12SLen Brown 	}
207b4f9fe12SLen Brown 
208b4f9fe12SLen Brown 	return true;
209b4f9fe12SLen Brown }
210b4f9fe12SLen Brown 
2111caab3c1SMatthew Garrett /*
2121caab3c1SMatthew Garrett  * Convert a raw GUID to the ACII string representation
2131caab3c1SMatthew Garrett  */
2141caab3c1SMatthew Garrett static int wmi_gtoa(const char *in, char *out)
2151caab3c1SMatthew Garrett {
2161caab3c1SMatthew Garrett 	int i;
2171caab3c1SMatthew Garrett 
2181caab3c1SMatthew Garrett 	for (i = 3; i >= 0; i--)
2191caab3c1SMatthew Garrett 		out += sprintf(out, "%02X", in[i] & 0xFF);
2201caab3c1SMatthew Garrett 
2211caab3c1SMatthew Garrett 	out += sprintf(out, "-");
2221caab3c1SMatthew Garrett 	out += sprintf(out, "%02X", in[5] & 0xFF);
2231caab3c1SMatthew Garrett 	out += sprintf(out, "%02X", in[4] & 0xFF);
2241caab3c1SMatthew Garrett 	out += sprintf(out, "-");
2251caab3c1SMatthew Garrett 	out += sprintf(out, "%02X", in[7] & 0xFF);
2261caab3c1SMatthew Garrett 	out += sprintf(out, "%02X", in[6] & 0xFF);
2271caab3c1SMatthew Garrett 	out += sprintf(out, "-");
2281caab3c1SMatthew Garrett 	out += sprintf(out, "%02X", in[8] & 0xFF);
2291caab3c1SMatthew Garrett 	out += sprintf(out, "%02X", in[9] & 0xFF);
2301caab3c1SMatthew Garrett 	out += sprintf(out, "-");
2311caab3c1SMatthew Garrett 
2321caab3c1SMatthew Garrett 	for (i = 10; i <= 15; i++)
2331caab3c1SMatthew Garrett 		out += sprintf(out, "%02X", in[i] & 0xFF);
2341caab3c1SMatthew Garrett 
2351caab3c1SMatthew Garrett 	out = '\0';
2361caab3c1SMatthew Garrett 	return 0;
2371caab3c1SMatthew Garrett }
2381caab3c1SMatthew Garrett 
239b4f9fe12SLen Brown static bool find_guid(const char *guid_string, struct wmi_block **out)
240b4f9fe12SLen Brown {
241b4f9fe12SLen Brown 	char tmp[16], guid_input[16];
242b4f9fe12SLen Brown 	struct wmi_block *wblock;
243b4f9fe12SLen Brown 	struct guid_block *block;
244b4f9fe12SLen Brown 	struct list_head *p;
245b4f9fe12SLen Brown 
246b4f9fe12SLen Brown 	wmi_parse_guid(guid_string, tmp);
247b4f9fe12SLen Brown 	wmi_swap_bytes(tmp, guid_input);
248b4f9fe12SLen Brown 
249b4f9fe12SLen Brown 	list_for_each(p, &wmi_blocks.list) {
250b4f9fe12SLen Brown 		wblock = list_entry(p, struct wmi_block, list);
251b4f9fe12SLen Brown 		block = &wblock->gblock;
252b4f9fe12SLen Brown 
253b4f9fe12SLen Brown 		if (memcmp(block->guid, guid_input, 16) == 0) {
254b4f9fe12SLen Brown 			if (out)
255b4f9fe12SLen Brown 				*out = wblock;
256b4f9fe12SLen Brown 			return 1;
257b4f9fe12SLen Brown 		}
258b4f9fe12SLen Brown 	}
259b4f9fe12SLen Brown 	return 0;
260b4f9fe12SLen Brown }
261b4f9fe12SLen Brown 
262b4f9fe12SLen Brown static acpi_status wmi_method_enable(struct wmi_block *wblock, int enable)
263b4f9fe12SLen Brown {
264b4f9fe12SLen Brown 	struct guid_block *block = NULL;
265b4f9fe12SLen Brown 	char method[5];
266b4f9fe12SLen Brown 	struct acpi_object_list input;
267b4f9fe12SLen Brown 	union acpi_object params[1];
268b4f9fe12SLen Brown 	acpi_status status;
269b4f9fe12SLen Brown 	acpi_handle handle;
270b4f9fe12SLen Brown 
271b4f9fe12SLen Brown 	block = &wblock->gblock;
272b4f9fe12SLen Brown 	handle = wblock->handle;
273b4f9fe12SLen Brown 
274b4f9fe12SLen Brown 	if (!block)
275b4f9fe12SLen Brown 		return AE_NOT_EXIST;
276b4f9fe12SLen Brown 
277b4f9fe12SLen Brown 	input.count = 1;
278b4f9fe12SLen Brown 	input.pointer = params;
279b4f9fe12SLen Brown 	params[0].type = ACPI_TYPE_INTEGER;
280b4f9fe12SLen Brown 	params[0].integer.value = enable;
281b4f9fe12SLen Brown 
282b4f9fe12SLen Brown 	snprintf(method, 5, "WE%02X", block->notify_id);
283b4f9fe12SLen Brown 	status = acpi_evaluate_object(handle, method, &input, NULL);
284b4f9fe12SLen Brown 
285b4f9fe12SLen Brown 	if (status != AE_OK && status != AE_NOT_FOUND)
286b4f9fe12SLen Brown 		return status;
287b4f9fe12SLen Brown 	else
288b4f9fe12SLen Brown 		return AE_OK;
289b4f9fe12SLen Brown }
290b4f9fe12SLen Brown 
291b4f9fe12SLen Brown /*
292b4f9fe12SLen Brown  * Exported WMI functions
293b4f9fe12SLen Brown  */
294b4f9fe12SLen Brown /**
295b4f9fe12SLen Brown  * wmi_evaluate_method - Evaluate a WMI method
296b4f9fe12SLen Brown  * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
297b4f9fe12SLen Brown  * @instance: Instance index
298b4f9fe12SLen Brown  * @method_id: Method ID to call
299b4f9fe12SLen Brown  * &in: Buffer containing input for the method call
300b4f9fe12SLen Brown  * &out: Empty buffer to return the method results
301b4f9fe12SLen Brown  *
302b4f9fe12SLen Brown  * Call an ACPI-WMI method
303b4f9fe12SLen Brown  */
304b4f9fe12SLen Brown acpi_status wmi_evaluate_method(const char *guid_string, u8 instance,
305b4f9fe12SLen Brown u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out)
306b4f9fe12SLen Brown {
307b4f9fe12SLen Brown 	struct guid_block *block = NULL;
308b4f9fe12SLen Brown 	struct wmi_block *wblock = NULL;
309b4f9fe12SLen Brown 	acpi_handle handle;
310b4f9fe12SLen Brown 	acpi_status status;
311b4f9fe12SLen Brown 	struct acpi_object_list input;
312b4f9fe12SLen Brown 	union acpi_object params[3];
313f3d83e24SCostantino Leandro 	char method[5] = "WM";
314b4f9fe12SLen Brown 
315b4f9fe12SLen Brown 	if (!find_guid(guid_string, &wblock))
316b4f9fe12SLen Brown 		return AE_ERROR;
317b4f9fe12SLen Brown 
318b4f9fe12SLen Brown 	block = &wblock->gblock;
319b4f9fe12SLen Brown 	handle = wblock->handle;
320b4f9fe12SLen Brown 
321b4f9fe12SLen Brown 	if (!(block->flags & ACPI_WMI_METHOD))
322b4f9fe12SLen Brown 		return AE_BAD_DATA;
323b4f9fe12SLen Brown 
324b4f9fe12SLen Brown 	if (block->instance_count < instance)
325b4f9fe12SLen Brown 		return AE_BAD_PARAMETER;
326b4f9fe12SLen Brown 
327b4f9fe12SLen Brown 	input.count = 2;
328b4f9fe12SLen Brown 	input.pointer = params;
329b4f9fe12SLen Brown 	params[0].type = ACPI_TYPE_INTEGER;
330b4f9fe12SLen Brown 	params[0].integer.value = instance;
331b4f9fe12SLen Brown 	params[1].type = ACPI_TYPE_INTEGER;
332b4f9fe12SLen Brown 	params[1].integer.value = method_id;
333b4f9fe12SLen Brown 
334b4f9fe12SLen Brown 	if (in) {
335b4f9fe12SLen Brown 		input.count = 3;
336b4f9fe12SLen Brown 
337b4f9fe12SLen Brown 		if (block->flags & ACPI_WMI_STRING) {
338b4f9fe12SLen Brown 			params[2].type = ACPI_TYPE_STRING;
339b4f9fe12SLen Brown 		} else {
340b4f9fe12SLen Brown 			params[2].type = ACPI_TYPE_BUFFER;
341b4f9fe12SLen Brown 		}
342b4f9fe12SLen Brown 		params[2].buffer.length = in->length;
343b4f9fe12SLen Brown 		params[2].buffer.pointer = in->pointer;
344b4f9fe12SLen Brown 	}
345b4f9fe12SLen Brown 
346b4f9fe12SLen Brown 	strncat(method, block->object_id, 2);
347b4f9fe12SLen Brown 
348b4f9fe12SLen Brown 	status = acpi_evaluate_object(handle, method, &input, out);
349b4f9fe12SLen Brown 
350b4f9fe12SLen Brown 	return status;
351b4f9fe12SLen Brown }
352b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_evaluate_method);
353b4f9fe12SLen Brown 
354b4f9fe12SLen Brown /**
355b4f9fe12SLen Brown  * wmi_query_block - Return contents of a WMI block
356b4f9fe12SLen Brown  * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
357b4f9fe12SLen Brown  * @instance: Instance index
358b4f9fe12SLen Brown  * &out: Empty buffer to return the contents of the data block to
359b4f9fe12SLen Brown  *
360b4f9fe12SLen Brown  * Return the contents of an ACPI-WMI data block to a buffer
361b4f9fe12SLen Brown  */
362b4f9fe12SLen Brown acpi_status wmi_query_block(const char *guid_string, u8 instance,
363b4f9fe12SLen Brown struct acpi_buffer *out)
364b4f9fe12SLen Brown {
365b4f9fe12SLen Brown 	struct guid_block *block = NULL;
366b4f9fe12SLen Brown 	struct wmi_block *wblock = NULL;
367b4f9fe12SLen Brown 	acpi_handle handle, wc_handle;
368b4f9fe12SLen Brown 	acpi_status status, wc_status = AE_ERROR;
369b4f9fe12SLen Brown 	struct acpi_object_list input, wc_input;
370b4f9fe12SLen Brown 	union acpi_object wc_params[1], wq_params[1];
371f3d83e24SCostantino Leandro 	char method[5];
372f3d83e24SCostantino Leandro 	char wc_method[5] = "WC";
373b4f9fe12SLen Brown 
374b4f9fe12SLen Brown 	if (!guid_string || !out)
375b4f9fe12SLen Brown 		return AE_BAD_PARAMETER;
376b4f9fe12SLen Brown 
377b4f9fe12SLen Brown 	if (!find_guid(guid_string, &wblock))
378b4f9fe12SLen Brown 		return AE_ERROR;
379b4f9fe12SLen Brown 
380b4f9fe12SLen Brown 	block = &wblock->gblock;
381b4f9fe12SLen Brown 	handle = wblock->handle;
382b4f9fe12SLen Brown 
383b4f9fe12SLen Brown 	if (block->instance_count < instance)
384b4f9fe12SLen Brown 		return AE_BAD_PARAMETER;
385b4f9fe12SLen Brown 
386b4f9fe12SLen Brown 	/* Check GUID is a data block */
387b4f9fe12SLen Brown 	if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD))
388b4f9fe12SLen Brown 		return AE_ERROR;
389b4f9fe12SLen Brown 
390b4f9fe12SLen Brown 	input.count = 1;
391b4f9fe12SLen Brown 	input.pointer = wq_params;
392b4f9fe12SLen Brown 	wq_params[0].type = ACPI_TYPE_INTEGER;
393b4f9fe12SLen Brown 	wq_params[0].integer.value = instance;
394b4f9fe12SLen Brown 
395b4f9fe12SLen Brown 	/*
396b4f9fe12SLen Brown 	 * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method first to
397b4f9fe12SLen Brown 	 * enable collection.
398b4f9fe12SLen Brown 	 */
399b4f9fe12SLen Brown 	if (block->flags & ACPI_WMI_EXPENSIVE) {
400b4f9fe12SLen Brown 		wc_input.count = 1;
401b4f9fe12SLen Brown 		wc_input.pointer = wc_params;
402b4f9fe12SLen Brown 		wc_params[0].type = ACPI_TYPE_INTEGER;
403b4f9fe12SLen Brown 		wc_params[0].integer.value = 1;
404b4f9fe12SLen Brown 
405b4f9fe12SLen Brown 		strncat(wc_method, block->object_id, 2);
406b4f9fe12SLen Brown 
407b4f9fe12SLen Brown 		/*
408b4f9fe12SLen Brown 		 * Some GUIDs break the specification by declaring themselves
409b4f9fe12SLen Brown 		 * expensive, but have no corresponding WCxx method. So we
410b4f9fe12SLen Brown 		 * should not fail if this happens.
411b4f9fe12SLen Brown 		 */
412b4f9fe12SLen Brown 		wc_status = acpi_get_handle(handle, wc_method, &wc_handle);
413b4f9fe12SLen Brown 		if (ACPI_SUCCESS(wc_status))
414b4f9fe12SLen Brown 			wc_status = acpi_evaluate_object(handle, wc_method,
415b4f9fe12SLen Brown 				&wc_input, NULL);
416b4f9fe12SLen Brown 	}
417b4f9fe12SLen Brown 
418b4f9fe12SLen Brown 	strcpy(method, "WQ");
419b4f9fe12SLen Brown 	strncat(method, block->object_id, 2);
420b4f9fe12SLen Brown 
421b4f9fe12SLen Brown 	status = acpi_evaluate_object(handle, method, &input, out);
422b4f9fe12SLen Brown 
423b4f9fe12SLen Brown 	/*
424b4f9fe12SLen Brown 	 * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method, even if
425b4f9fe12SLen Brown 	 * the WQxx method failed - we should disable collection anyway.
426b4f9fe12SLen Brown 	 */
427b4f9fe12SLen Brown 	if ((block->flags & ACPI_WMI_EXPENSIVE) && ACPI_SUCCESS(wc_status)) {
428b4f9fe12SLen Brown 		wc_params[0].integer.value = 0;
429b4f9fe12SLen Brown 		status = acpi_evaluate_object(handle,
430b4f9fe12SLen Brown 		wc_method, &wc_input, NULL);
431b4f9fe12SLen Brown 	}
432b4f9fe12SLen Brown 
433b4f9fe12SLen Brown 	return status;
434b4f9fe12SLen Brown }
435b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_query_block);
436b4f9fe12SLen Brown 
437b4f9fe12SLen Brown /**
438b4f9fe12SLen Brown  * wmi_set_block - Write to a WMI block
439b4f9fe12SLen Brown  * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
440b4f9fe12SLen Brown  * @instance: Instance index
441b4f9fe12SLen Brown  * &in: Buffer containing new values for the data block
442b4f9fe12SLen Brown  *
443b4f9fe12SLen Brown  * Write the contents of the input buffer to an ACPI-WMI data block
444b4f9fe12SLen Brown  */
445b4f9fe12SLen Brown acpi_status wmi_set_block(const char *guid_string, u8 instance,
446b4f9fe12SLen Brown const struct acpi_buffer *in)
447b4f9fe12SLen Brown {
448b4f9fe12SLen Brown 	struct guid_block *block = NULL;
449b4f9fe12SLen Brown 	struct wmi_block *wblock = NULL;
450b4f9fe12SLen Brown 	acpi_handle handle;
451b4f9fe12SLen Brown 	struct acpi_object_list input;
452b4f9fe12SLen Brown 	union acpi_object params[2];
453f3d83e24SCostantino Leandro 	char method[5] = "WS";
454b4f9fe12SLen Brown 
455b4f9fe12SLen Brown 	if (!guid_string || !in)
456b4f9fe12SLen Brown 		return AE_BAD_DATA;
457b4f9fe12SLen Brown 
458b4f9fe12SLen Brown 	if (!find_guid(guid_string, &wblock))
459b4f9fe12SLen Brown 		return AE_ERROR;
460b4f9fe12SLen Brown 
461b4f9fe12SLen Brown 	block = &wblock->gblock;
462b4f9fe12SLen Brown 	handle = wblock->handle;
463b4f9fe12SLen Brown 
464b4f9fe12SLen Brown 	if (block->instance_count < instance)
465b4f9fe12SLen Brown 		return AE_BAD_PARAMETER;
466b4f9fe12SLen Brown 
467b4f9fe12SLen Brown 	/* Check GUID is a data block */
468b4f9fe12SLen Brown 	if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD))
469b4f9fe12SLen Brown 		return AE_ERROR;
470b4f9fe12SLen Brown 
471b4f9fe12SLen Brown 	input.count = 2;
472b4f9fe12SLen Brown 	input.pointer = params;
473b4f9fe12SLen Brown 	params[0].type = ACPI_TYPE_INTEGER;
474b4f9fe12SLen Brown 	params[0].integer.value = instance;
475b4f9fe12SLen Brown 
476b4f9fe12SLen Brown 	if (block->flags & ACPI_WMI_STRING) {
477b4f9fe12SLen Brown 		params[1].type = ACPI_TYPE_STRING;
478b4f9fe12SLen Brown 	} else {
479b4f9fe12SLen Brown 		params[1].type = ACPI_TYPE_BUFFER;
480b4f9fe12SLen Brown 	}
481b4f9fe12SLen Brown 	params[1].buffer.length = in->length;
482b4f9fe12SLen Brown 	params[1].buffer.pointer = in->pointer;
483b4f9fe12SLen Brown 
484b4f9fe12SLen Brown 	strncat(method, block->object_id, 2);
485b4f9fe12SLen Brown 
486b4f9fe12SLen Brown 	return acpi_evaluate_object(handle, method, &input, NULL);
487b4f9fe12SLen Brown }
488b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_set_block);
489b4f9fe12SLen Brown 
490a929aae0SThomas Renninger static void wmi_dump_wdg(struct guid_block *g)
491a929aae0SThomas Renninger {
492a929aae0SThomas Renninger 	char guid_string[37];
493a929aae0SThomas Renninger 
494a929aae0SThomas Renninger 	wmi_gtoa(g->guid, guid_string);
495a929aae0SThomas Renninger 	printk(KERN_INFO PREFIX "%s:\n", guid_string);
496a929aae0SThomas Renninger 	printk(KERN_INFO PREFIX "\tobject_id: %c%c\n",
497a929aae0SThomas Renninger 	       g->object_id[0], g->object_id[1]);
498a929aae0SThomas Renninger 	printk(KERN_INFO PREFIX "\tnotify_id: %02X\n", g->notify_id);
499a929aae0SThomas Renninger 	printk(KERN_INFO PREFIX "\treserved: %02X\n", g->reserved);
500a929aae0SThomas Renninger 	printk(KERN_INFO PREFIX "\tinstance_count: %d\n", g->instance_count);
501a929aae0SThomas Renninger 	printk(KERN_INFO PREFIX "\tflags: %#x", g->flags);
502a929aae0SThomas Renninger 	if (g->flags) {
503a929aae0SThomas Renninger 		printk(" ");
504a929aae0SThomas Renninger 		if (g->flags & ACPI_WMI_EXPENSIVE)
505a929aae0SThomas Renninger 			printk("ACPI_WMI_EXPENSIVE ");
506a929aae0SThomas Renninger 		if (g->flags & ACPI_WMI_METHOD)
507a929aae0SThomas Renninger 			printk("ACPI_WMI_METHOD ");
508a929aae0SThomas Renninger 		if (g->flags & ACPI_WMI_STRING)
509a929aae0SThomas Renninger 			printk("ACPI_WMI_STRING ");
510a929aae0SThomas Renninger 		if (g->flags & ACPI_WMI_EVENT)
511a929aae0SThomas Renninger 			printk("ACPI_WMI_EVENT ");
512a929aae0SThomas Renninger 	}
513a929aae0SThomas Renninger 	printk("\n");
514a929aae0SThomas Renninger 
515a929aae0SThomas Renninger }
516a929aae0SThomas Renninger 
517fc3155b2SThomas Renninger static void wmi_notify_debug(u32 value, void *context)
518fc3155b2SThomas Renninger {
519fc3155b2SThomas Renninger 	struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
520fc3155b2SThomas Renninger 	union acpi_object *obj;
5211492616aSAxel Lin 	acpi_status status;
522fc3155b2SThomas Renninger 
5231492616aSAxel Lin 	status = wmi_get_event_data(value, &response);
5241492616aSAxel Lin 	if (status != AE_OK) {
5251492616aSAxel Lin 		printk(KERN_INFO "wmi: bad event status 0x%x\n", status);
5261492616aSAxel Lin 		return;
5271492616aSAxel Lin 	}
528fc3155b2SThomas Renninger 
529fc3155b2SThomas Renninger 	obj = (union acpi_object *)response.pointer;
530fc3155b2SThomas Renninger 
531fc3155b2SThomas Renninger 	if (!obj)
532fc3155b2SThomas Renninger 		return;
533fc3155b2SThomas Renninger 
534fc3155b2SThomas Renninger 	printk(KERN_INFO PREFIX "DEBUG Event ");
535fc3155b2SThomas Renninger 	switch(obj->type) {
536fc3155b2SThomas Renninger 	case ACPI_TYPE_BUFFER:
537fc3155b2SThomas Renninger 		printk("BUFFER_TYPE - length %d\n", obj->buffer.length);
538fc3155b2SThomas Renninger 		break;
539fc3155b2SThomas Renninger 	case ACPI_TYPE_STRING:
540fc3155b2SThomas Renninger 		printk("STRING_TYPE - %s\n", obj->string.pointer);
541fc3155b2SThomas Renninger 		break;
542fc3155b2SThomas Renninger 	case ACPI_TYPE_INTEGER:
543fc3155b2SThomas Renninger 		printk("INTEGER_TYPE - %llu\n", obj->integer.value);
544fc3155b2SThomas Renninger 		break;
545fc3155b2SThomas Renninger 	case ACPI_TYPE_PACKAGE:
546fc3155b2SThomas Renninger 		printk("PACKAGE_TYPE - %d elements\n", obj->package.count);
547fc3155b2SThomas Renninger 		break;
548fc3155b2SThomas Renninger 	default:
549fc3155b2SThomas Renninger 		printk("object type 0x%X\n", obj->type);
550fc3155b2SThomas Renninger 	}
5511492616aSAxel Lin 	kfree(obj);
552fc3155b2SThomas Renninger }
553fc3155b2SThomas Renninger 
554b4f9fe12SLen Brown /**
555b4f9fe12SLen Brown  * wmi_install_notify_handler - Register handler for WMI events
556b4f9fe12SLen Brown  * @handler: Function to handle notifications
557b4f9fe12SLen Brown  * @data: Data to be returned to handler when event is fired
558b4f9fe12SLen Brown  *
559b4f9fe12SLen Brown  * Register a handler for events sent to the ACPI-WMI mapper device.
560b4f9fe12SLen Brown  */
561b4f9fe12SLen Brown acpi_status wmi_install_notify_handler(const char *guid,
562b4f9fe12SLen Brown wmi_notify_handler handler, void *data)
563b4f9fe12SLen Brown {
564b4f9fe12SLen Brown 	struct wmi_block *block;
565b4f9fe12SLen Brown 	acpi_status status;
566b4f9fe12SLen Brown 
567b4f9fe12SLen Brown 	if (!guid || !handler)
568b4f9fe12SLen Brown 		return AE_BAD_PARAMETER;
569b4f9fe12SLen Brown 
570c03b26a5SPaul Rolland 	if (!find_guid(guid, &block))
571b4f9fe12SLen Brown 		return AE_NOT_EXIST;
572b4f9fe12SLen Brown 
573fc3155b2SThomas Renninger 	if (block->handler && block->handler != wmi_notify_debug)
574b4f9fe12SLen Brown 		return AE_ALREADY_ACQUIRED;
575b4f9fe12SLen Brown 
576b4f9fe12SLen Brown 	block->handler = handler;
577b4f9fe12SLen Brown 	block->handler_data = data;
578b4f9fe12SLen Brown 
579b4f9fe12SLen Brown 	status = wmi_method_enable(block, 1);
580b4f9fe12SLen Brown 
581b4f9fe12SLen Brown 	return status;
582b4f9fe12SLen Brown }
583b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_install_notify_handler);
584b4f9fe12SLen Brown 
585b4f9fe12SLen Brown /**
586b4f9fe12SLen Brown  * wmi_uninstall_notify_handler - Unregister handler for WMI events
587b4f9fe12SLen Brown  *
588b4f9fe12SLen Brown  * Unregister handler for events sent to the ACPI-WMI mapper device.
589b4f9fe12SLen Brown  */
590b4f9fe12SLen Brown acpi_status wmi_remove_notify_handler(const char *guid)
591b4f9fe12SLen Brown {
592b4f9fe12SLen Brown 	struct wmi_block *block;
593fc3155b2SThomas Renninger 	acpi_status status = AE_OK;
594b4f9fe12SLen Brown 
595b4f9fe12SLen Brown 	if (!guid)
596b4f9fe12SLen Brown 		return AE_BAD_PARAMETER;
597b4f9fe12SLen Brown 
598c03b26a5SPaul Rolland 	if (!find_guid(guid, &block))
599b4f9fe12SLen Brown 		return AE_NOT_EXIST;
600b4f9fe12SLen Brown 
601fc3155b2SThomas Renninger 	if (!block->handler || block->handler == wmi_notify_debug)
602b4f9fe12SLen Brown 		return AE_NULL_ENTRY;
603b4f9fe12SLen Brown 
604fc3155b2SThomas Renninger 	if (debug_event) {
605fc3155b2SThomas Renninger 		block->handler = wmi_notify_debug;
606fc3155b2SThomas Renninger 	} else {
607b4f9fe12SLen Brown 		status = wmi_method_enable(block, 0);
608b4f9fe12SLen Brown 		block->handler = NULL;
609b4f9fe12SLen Brown 		block->handler_data = NULL;
610fc3155b2SThomas Renninger 	}
611b4f9fe12SLen Brown 	return status;
612b4f9fe12SLen Brown }
613b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_remove_notify_handler);
614b4f9fe12SLen Brown 
615b4f9fe12SLen Brown /**
616b4f9fe12SLen Brown  * wmi_get_event_data - Get WMI data associated with an event
617b4f9fe12SLen Brown  *
6183e9b988eSAnisse Astier  * @event: Event to find
6193e9b988eSAnisse Astier  * @out: Buffer to hold event data. out->pointer should be freed with kfree()
620b4f9fe12SLen Brown  *
621b4f9fe12SLen Brown  * Returns extra data associated with an event in WMI.
622b4f9fe12SLen Brown  */
623b4f9fe12SLen Brown acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out)
624b4f9fe12SLen Brown {
625b4f9fe12SLen Brown 	struct acpi_object_list input;
626b4f9fe12SLen Brown 	union acpi_object params[1];
627b4f9fe12SLen Brown 	struct guid_block *gblock;
628b4f9fe12SLen Brown 	struct wmi_block *wblock;
629b4f9fe12SLen Brown 	struct list_head *p;
630b4f9fe12SLen Brown 
631b4f9fe12SLen Brown 	input.count = 1;
632b4f9fe12SLen Brown 	input.pointer = params;
633b4f9fe12SLen Brown 	params[0].type = ACPI_TYPE_INTEGER;
634b4f9fe12SLen Brown 	params[0].integer.value = event;
635b4f9fe12SLen Brown 
636b4f9fe12SLen Brown 	list_for_each(p, &wmi_blocks.list) {
637b4f9fe12SLen Brown 		wblock = list_entry(p, struct wmi_block, list);
638b4f9fe12SLen Brown 		gblock = &wblock->gblock;
639b4f9fe12SLen Brown 
640b4f9fe12SLen Brown 		if ((gblock->flags & ACPI_WMI_EVENT) &&
641b4f9fe12SLen Brown 			(gblock->notify_id == event))
642b4f9fe12SLen Brown 			return acpi_evaluate_object(wblock->handle, "_WED",
643b4f9fe12SLen Brown 				&input, out);
644b4f9fe12SLen Brown 	}
645b4f9fe12SLen Brown 
646b4f9fe12SLen Brown 	return AE_NOT_FOUND;
647b4f9fe12SLen Brown }
648b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_get_event_data);
649b4f9fe12SLen Brown 
650b4f9fe12SLen Brown /**
651b4f9fe12SLen Brown  * wmi_has_guid - Check if a GUID is available
652b4f9fe12SLen Brown  * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
653b4f9fe12SLen Brown  *
654b4f9fe12SLen Brown  * Check if a given GUID is defined by _WDG
655b4f9fe12SLen Brown  */
656b4f9fe12SLen Brown bool wmi_has_guid(const char *guid_string)
657b4f9fe12SLen Brown {
658b4f9fe12SLen Brown 	return find_guid(guid_string, NULL);
659b4f9fe12SLen Brown }
660b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_has_guid);
661b4f9fe12SLen Brown 
662b4f9fe12SLen Brown /*
6631caab3c1SMatthew Garrett  * sysfs interface
6641caab3c1SMatthew Garrett  */
6651caab3c1SMatthew Garrett static ssize_t show_modalias(struct device *dev, struct device_attribute *attr,
6661caab3c1SMatthew Garrett 			     char *buf)
6671caab3c1SMatthew Garrett {
6681caab3c1SMatthew Garrett 	char guid_string[37];
6691caab3c1SMatthew Garrett 	struct wmi_block *wblock;
6701caab3c1SMatthew Garrett 
6711caab3c1SMatthew Garrett 	wblock = dev_get_drvdata(dev);
6721caab3c1SMatthew Garrett 	if (!wblock)
6731caab3c1SMatthew Garrett 		return -ENOMEM;
6741caab3c1SMatthew Garrett 
6751caab3c1SMatthew Garrett 	wmi_gtoa(wblock->gblock.guid, guid_string);
6761caab3c1SMatthew Garrett 
6771caab3c1SMatthew Garrett 	return sprintf(buf, "wmi:%s\n", guid_string);
6781caab3c1SMatthew Garrett }
6791caab3c1SMatthew Garrett static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL);
6801caab3c1SMatthew Garrett 
6811caab3c1SMatthew Garrett static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env)
6821caab3c1SMatthew Garrett {
6831caab3c1SMatthew Garrett 	char guid_string[37];
6841caab3c1SMatthew Garrett 
6851caab3c1SMatthew Garrett 	struct wmi_block *wblock;
6861caab3c1SMatthew Garrett 
6871caab3c1SMatthew Garrett 	if (add_uevent_var(env, "MODALIAS="))
6881caab3c1SMatthew Garrett 		return -ENOMEM;
6891caab3c1SMatthew Garrett 
6901caab3c1SMatthew Garrett 	wblock = dev_get_drvdata(dev);
6911caab3c1SMatthew Garrett 	if (!wblock)
6921caab3c1SMatthew Garrett 		return -ENOMEM;
6931caab3c1SMatthew Garrett 
6941caab3c1SMatthew Garrett 	wmi_gtoa(wblock->gblock.guid, guid_string);
6951caab3c1SMatthew Garrett 
6961caab3c1SMatthew Garrett 	strcpy(&env->buf[env->buflen - 1], "wmi:");
6971caab3c1SMatthew Garrett 	memcpy(&env->buf[env->buflen - 1 + 4], guid_string, 36);
6981caab3c1SMatthew Garrett 	env->buflen += 40;
6991caab3c1SMatthew Garrett 
7001caab3c1SMatthew Garrett 	return 0;
7011caab3c1SMatthew Garrett }
7021caab3c1SMatthew Garrett 
7031caab3c1SMatthew Garrett static void wmi_dev_free(struct device *dev)
7041caab3c1SMatthew Garrett {
7051caab3c1SMatthew Garrett 	kfree(dev);
7061caab3c1SMatthew Garrett }
7071caab3c1SMatthew Garrett 
7081caab3c1SMatthew Garrett static struct class wmi_class = {
7091caab3c1SMatthew Garrett 	.name = "wmi",
7101caab3c1SMatthew Garrett 	.dev_release = wmi_dev_free,
7111caab3c1SMatthew Garrett 	.dev_uevent = wmi_dev_uevent,
7121caab3c1SMatthew Garrett };
7131caab3c1SMatthew Garrett 
7141caab3c1SMatthew Garrett static int wmi_create_devs(void)
7151caab3c1SMatthew Garrett {
7161caab3c1SMatthew Garrett 	int result;
7171caab3c1SMatthew Garrett 	char guid_string[37];
7181caab3c1SMatthew Garrett 	struct guid_block *gblock;
7191caab3c1SMatthew Garrett 	struct wmi_block *wblock;
7201caab3c1SMatthew Garrett 	struct list_head *p;
7211caab3c1SMatthew Garrett 	struct device *guid_dev;
7221caab3c1SMatthew Garrett 
7231caab3c1SMatthew Garrett 	/* Create devices for all the GUIDs */
7241caab3c1SMatthew Garrett 	list_for_each(p, &wmi_blocks.list) {
7251caab3c1SMatthew Garrett 		wblock = list_entry(p, struct wmi_block, list);
7261caab3c1SMatthew Garrett 
7271caab3c1SMatthew Garrett 		guid_dev = kzalloc(sizeof(struct device), GFP_KERNEL);
7281caab3c1SMatthew Garrett 		if (!guid_dev)
7291caab3c1SMatthew Garrett 			return -ENOMEM;
7301caab3c1SMatthew Garrett 
7311caab3c1SMatthew Garrett 		wblock->dev = guid_dev;
7321caab3c1SMatthew Garrett 
7331caab3c1SMatthew Garrett 		guid_dev->class = &wmi_class;
7341caab3c1SMatthew Garrett 		dev_set_drvdata(guid_dev, wblock);
7351caab3c1SMatthew Garrett 
7361caab3c1SMatthew Garrett 		gblock = &wblock->gblock;
7371caab3c1SMatthew Garrett 
7381caab3c1SMatthew Garrett 		wmi_gtoa(gblock->guid, guid_string);
7391caab3c1SMatthew Garrett 		dev_set_name(guid_dev, guid_string);
7401caab3c1SMatthew Garrett 
7411caab3c1SMatthew Garrett 		result = device_register(guid_dev);
7421caab3c1SMatthew Garrett 		if (result)
7431caab3c1SMatthew Garrett 			return result;
7441caab3c1SMatthew Garrett 
7451caab3c1SMatthew Garrett 		result = device_create_file(guid_dev, &dev_attr_modalias);
7461caab3c1SMatthew Garrett 		if (result)
7471caab3c1SMatthew Garrett 			return result;
7481caab3c1SMatthew Garrett 	}
7491caab3c1SMatthew Garrett 
7501caab3c1SMatthew Garrett 	return 0;
7511caab3c1SMatthew Garrett }
7521caab3c1SMatthew Garrett 
7531caab3c1SMatthew Garrett static void wmi_remove_devs(void)
7541caab3c1SMatthew Garrett {
7551caab3c1SMatthew Garrett 	struct guid_block *gblock;
7561caab3c1SMatthew Garrett 	struct wmi_block *wblock;
7571caab3c1SMatthew Garrett 	struct list_head *p;
7581caab3c1SMatthew Garrett 	struct device *guid_dev;
7591caab3c1SMatthew Garrett 
7601caab3c1SMatthew Garrett 	/* Delete devices for all the GUIDs */
7611caab3c1SMatthew Garrett 	list_for_each(p, &wmi_blocks.list) {
7621caab3c1SMatthew Garrett 		wblock = list_entry(p, struct wmi_block, list);
7631caab3c1SMatthew Garrett 
7641caab3c1SMatthew Garrett 		guid_dev = wblock->dev;
7651caab3c1SMatthew Garrett 		gblock = &wblock->gblock;
7661caab3c1SMatthew Garrett 
7671caab3c1SMatthew Garrett 		device_remove_file(guid_dev, &dev_attr_modalias);
7681caab3c1SMatthew Garrett 
7691caab3c1SMatthew Garrett 		device_unregister(guid_dev);
7701caab3c1SMatthew Garrett 	}
7711caab3c1SMatthew Garrett }
7721caab3c1SMatthew Garrett 
7731caab3c1SMatthew Garrett static void wmi_class_exit(void)
7741caab3c1SMatthew Garrett {
7751caab3c1SMatthew Garrett 	wmi_remove_devs();
7761caab3c1SMatthew Garrett 	class_unregister(&wmi_class);
7771caab3c1SMatthew Garrett }
7781caab3c1SMatthew Garrett 
7791caab3c1SMatthew Garrett static int wmi_class_init(void)
7801caab3c1SMatthew Garrett {
7811caab3c1SMatthew Garrett 	int ret;
7821caab3c1SMatthew Garrett 
7831caab3c1SMatthew Garrett 	ret = class_register(&wmi_class);
7841caab3c1SMatthew Garrett 	if (ret)
7851caab3c1SMatthew Garrett 		return ret;
7861caab3c1SMatthew Garrett 
7871caab3c1SMatthew Garrett 	ret = wmi_create_devs();
7881caab3c1SMatthew Garrett 	if (ret)
7891caab3c1SMatthew Garrett 		wmi_class_exit();
7901caab3c1SMatthew Garrett 
7911caab3c1SMatthew Garrett 	return ret;
7921caab3c1SMatthew Garrett }
7931caab3c1SMatthew Garrett 
794d1f9e497SCarlos Corbacho static bool guid_already_parsed(const char *guid_string)
795d1f9e497SCarlos Corbacho {
796d1f9e497SCarlos Corbacho 	struct guid_block *gblock;
797d1f9e497SCarlos Corbacho 	struct wmi_block *wblock;
798d1f9e497SCarlos Corbacho 	struct list_head *p;
799d1f9e497SCarlos Corbacho 
800d1f9e497SCarlos Corbacho 	list_for_each(p, &wmi_blocks.list) {
801d1f9e497SCarlos Corbacho 		wblock = list_entry(p, struct wmi_block, list);
802d1f9e497SCarlos Corbacho 		gblock = &wblock->gblock;
803d1f9e497SCarlos Corbacho 
804d1f9e497SCarlos Corbacho 		if (strncmp(gblock->guid, guid_string, 16) == 0)
805d1f9e497SCarlos Corbacho 			return true;
806d1f9e497SCarlos Corbacho 	}
807d1f9e497SCarlos Corbacho 	return false;
808d1f9e497SCarlos Corbacho }
809d1f9e497SCarlos Corbacho 
8101caab3c1SMatthew Garrett /*
811b4f9fe12SLen Brown  * Parse the _WDG method for the GUID data blocks
812b4f9fe12SLen Brown  */
813925b1089SThomas Renninger static acpi_status parse_wdg(acpi_handle handle)
814b4f9fe12SLen Brown {
815b4f9fe12SLen Brown 	struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
816b4f9fe12SLen Brown 	union acpi_object *obj;
817b4f9fe12SLen Brown 	struct guid_block *gblock;
818b4f9fe12SLen Brown 	struct wmi_block *wblock;
819d1f9e497SCarlos Corbacho 	char guid_string[37];
820b4f9fe12SLen Brown 	acpi_status status;
821b4f9fe12SLen Brown 	u32 i, total;
822b4f9fe12SLen Brown 
823b4f9fe12SLen Brown 	status = acpi_evaluate_object(handle, "_WDG", NULL, &out);
824b4f9fe12SLen Brown 
825b4f9fe12SLen Brown 	if (ACPI_FAILURE(status))
826b4f9fe12SLen Brown 		return status;
827b4f9fe12SLen Brown 
828b4f9fe12SLen Brown 	obj = (union acpi_object *) out.pointer;
829b4f9fe12SLen Brown 
830b4f9fe12SLen Brown 	if (obj->type != ACPI_TYPE_BUFFER)
831b4f9fe12SLen Brown 		return AE_ERROR;
832b4f9fe12SLen Brown 
833b4f9fe12SLen Brown 	total = obj->buffer.length / sizeof(struct guid_block);
834b4f9fe12SLen Brown 
8352c6719a3SJulia Lawall 	gblock = kmemdup(obj->buffer.pointer, obj->buffer.length, GFP_KERNEL);
836a5167c5bSAxel Lin 	if (!gblock) {
837a5167c5bSAxel Lin 		status = AE_NO_MEMORY;
838a5167c5bSAxel Lin 		goto out_free_pointer;
839a5167c5bSAxel Lin 	}
840b4f9fe12SLen Brown 
841b4f9fe12SLen Brown 	for (i = 0; i < total; i++) {
842d1f9e497SCarlos Corbacho 		/*
843d1f9e497SCarlos Corbacho 		  Some WMI devices, like those for nVidia hooks, have a
844d1f9e497SCarlos Corbacho 		  duplicate GUID. It's not clear what we should do in this
845d1f9e497SCarlos Corbacho 		  case yet, so for now, we'll just ignore the duplicate.
846d1f9e497SCarlos Corbacho 		  Anyone who wants to add support for that device can come
847d1f9e497SCarlos Corbacho 		  up with a better workaround for the mess then.
848d1f9e497SCarlos Corbacho 		*/
849d1f9e497SCarlos Corbacho 		if (guid_already_parsed(gblock[i].guid) == true) {
850d1f9e497SCarlos Corbacho 			wmi_gtoa(gblock[i].guid, guid_string);
851d1f9e497SCarlos Corbacho 			printk(KERN_INFO PREFIX "Skipping duplicate GUID %s\n",
852d1f9e497SCarlos Corbacho 				guid_string);
853d1f9e497SCarlos Corbacho 			continue;
854d1f9e497SCarlos Corbacho 		}
855a929aae0SThomas Renninger 		if (debug_dump_wdg)
856a929aae0SThomas Renninger 			wmi_dump_wdg(&gblock[i]);
857a929aae0SThomas Renninger 
858b4f9fe12SLen Brown 		wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL);
859a5167c5bSAxel Lin 		if (!wblock) {
860a5167c5bSAxel Lin 			status = AE_NO_MEMORY;
861a5167c5bSAxel Lin 			goto out_free_gblock;
862a5167c5bSAxel Lin 		}
863b4f9fe12SLen Brown 
864b4f9fe12SLen Brown 		wblock->gblock = gblock[i];
865b4f9fe12SLen Brown 		wblock->handle = handle;
866fc3155b2SThomas Renninger 		if (debug_event) {
867fc3155b2SThomas Renninger 			wblock->handler = wmi_notify_debug;
868fc3155b2SThomas Renninger 			status = wmi_method_enable(wblock, 1);
869fc3155b2SThomas Renninger 		}
870b4f9fe12SLen Brown 		list_add_tail(&wblock->list, &wmi_blocks.list);
871b4f9fe12SLen Brown 	}
872b4f9fe12SLen Brown 
873a5167c5bSAxel Lin out_free_gblock:
874b4f9fe12SLen Brown 	kfree(gblock);
875a5167c5bSAxel Lin out_free_pointer:
876a5167c5bSAxel Lin 	kfree(out.pointer);
877b4f9fe12SLen Brown 
878b4f9fe12SLen Brown 	return status;
879b4f9fe12SLen Brown }
880b4f9fe12SLen Brown 
881b4f9fe12SLen Brown /*
882b4f9fe12SLen Brown  * WMI can have EmbeddedControl access regions. In which case, we just want to
883b4f9fe12SLen Brown  * hand these off to the EC driver.
884b4f9fe12SLen Brown  */
885b4f9fe12SLen Brown static acpi_status
886b4f9fe12SLen Brown acpi_wmi_ec_space_handler(u32 function, acpi_physical_address address,
887439913ffSLin Ming 		      u32 bits, u64 *value,
888b4f9fe12SLen Brown 		      void *handler_context, void *region_context)
889b4f9fe12SLen Brown {
890b4f9fe12SLen Brown 	int result = 0, i = 0;
891b4f9fe12SLen Brown 	u8 temp = 0;
892b4f9fe12SLen Brown 
893b4f9fe12SLen Brown 	if ((address > 0xFF) || !value)
894b4f9fe12SLen Brown 		return AE_BAD_PARAMETER;
895b4f9fe12SLen Brown 
896b4f9fe12SLen Brown 	if (function != ACPI_READ && function != ACPI_WRITE)
897b4f9fe12SLen Brown 		return AE_BAD_PARAMETER;
898b4f9fe12SLen Brown 
899b4f9fe12SLen Brown 	if (bits != 8)
900b4f9fe12SLen Brown 		return AE_BAD_PARAMETER;
901b4f9fe12SLen Brown 
902b4f9fe12SLen Brown 	if (function == ACPI_READ) {
903b4f9fe12SLen Brown 		result = ec_read(address, &temp);
904439913ffSLin Ming 		(*value) |= ((u64)temp) << i;
905b4f9fe12SLen Brown 	} else {
906b4f9fe12SLen Brown 		temp = 0xff & ((*value) >> i);
907b4f9fe12SLen Brown 		result = ec_write(address, temp);
908b4f9fe12SLen Brown 	}
909b4f9fe12SLen Brown 
910b4f9fe12SLen Brown 	switch (result) {
911b4f9fe12SLen Brown 	case -EINVAL:
912b4f9fe12SLen Brown 		return AE_BAD_PARAMETER;
913b4f9fe12SLen Brown 		break;
914b4f9fe12SLen Brown 	case -ENODEV:
915b4f9fe12SLen Brown 		return AE_NOT_FOUND;
916b4f9fe12SLen Brown 		break;
917b4f9fe12SLen Brown 	case -ETIME:
918b4f9fe12SLen Brown 		return AE_TIME;
919b4f9fe12SLen Brown 		break;
920b4f9fe12SLen Brown 	default:
921b4f9fe12SLen Brown 		return AE_OK;
922b4f9fe12SLen Brown 	}
923b4f9fe12SLen Brown }
924b4f9fe12SLen Brown 
925f61bb939SBjorn Helgaas static void acpi_wmi_notify(struct acpi_device *device, u32 event)
926b4f9fe12SLen Brown {
927b4f9fe12SLen Brown 	struct guid_block *block;
928b4f9fe12SLen Brown 	struct wmi_block *wblock;
929b4f9fe12SLen Brown 	struct list_head *p;
9307715348cSThomas Renninger 	char guid_string[37];
931b4f9fe12SLen Brown 
932b4f9fe12SLen Brown 	list_for_each(p, &wmi_blocks.list) {
933b4f9fe12SLen Brown 		wblock = list_entry(p, struct wmi_block, list);
934b4f9fe12SLen Brown 		block = &wblock->gblock;
935b4f9fe12SLen Brown 
936b4f9fe12SLen Brown 		if ((block->flags & ACPI_WMI_EVENT) &&
937b4f9fe12SLen Brown 			(block->notify_id == event)) {
938b4f9fe12SLen Brown 			if (wblock->handler)
939b4f9fe12SLen Brown 				wblock->handler(event, wblock->handler_data);
9407715348cSThomas Renninger 			if (debug_event) {
9417715348cSThomas Renninger 				wmi_gtoa(wblock->gblock.guid, guid_string);
9427715348cSThomas Renninger 				printk(KERN_INFO PREFIX "DEBUG Event GUID:"
9437715348cSThomas Renninger 				       " %s\n", guid_string);
9447715348cSThomas Renninger 			}
945b4f9fe12SLen Brown 
946b4f9fe12SLen Brown 			acpi_bus_generate_netlink_event(
947b4f9fe12SLen Brown 				device->pnp.device_class, dev_name(&device->dev),
948b4f9fe12SLen Brown 				event, 0);
949b4f9fe12SLen Brown 			break;
950b4f9fe12SLen Brown 		}
951b4f9fe12SLen Brown 	}
952b4f9fe12SLen Brown }
953b4f9fe12SLen Brown 
954b4f9fe12SLen Brown static int acpi_wmi_remove(struct acpi_device *device, int type)
955b4f9fe12SLen Brown {
956b4f9fe12SLen Brown 	acpi_remove_address_space_handler(device->handle,
957b4f9fe12SLen Brown 				ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler);
958b4f9fe12SLen Brown 
959b4f9fe12SLen Brown 	return 0;
960b4f9fe12SLen Brown }
961b4f9fe12SLen Brown 
962925b1089SThomas Renninger static int acpi_wmi_add(struct acpi_device *device)
963b4f9fe12SLen Brown {
964b4f9fe12SLen Brown 	acpi_status status;
965b4f9fe12SLen Brown 	int result = 0;
966b4f9fe12SLen Brown 
967b4f9fe12SLen Brown 	status = acpi_install_address_space_handler(device->handle,
968b4f9fe12SLen Brown 						    ACPI_ADR_SPACE_EC,
969b4f9fe12SLen Brown 						    &acpi_wmi_ec_space_handler,
970b4f9fe12SLen Brown 						    NULL, NULL);
971b4f9fe12SLen Brown 	if (ACPI_FAILURE(status))
972b4f9fe12SLen Brown 		return -ENODEV;
973b4f9fe12SLen Brown 
974b4f9fe12SLen Brown 	status = parse_wdg(device->handle);
975b4f9fe12SLen Brown 	if (ACPI_FAILURE(status)) {
976b4f9fe12SLen Brown 		printk(KERN_ERR PREFIX "Error installing EC region handler\n");
977b4f9fe12SLen Brown 		return -ENODEV;
978b4f9fe12SLen Brown 	}
979b4f9fe12SLen Brown 
980b4f9fe12SLen Brown 	return result;
981b4f9fe12SLen Brown }
982b4f9fe12SLen Brown 
983b4f9fe12SLen Brown static int __init acpi_wmi_init(void)
984b4f9fe12SLen Brown {
985da511997SRoel Kluin 	int result;
986b4f9fe12SLen Brown 
987b4f9fe12SLen Brown 	INIT_LIST_HEAD(&wmi_blocks.list);
988b4f9fe12SLen Brown 
989b4f9fe12SLen Brown 	if (acpi_disabled)
990b4f9fe12SLen Brown 		return -ENODEV;
991b4f9fe12SLen Brown 
992b4f9fe12SLen Brown 	result = acpi_bus_register_driver(&acpi_wmi_driver);
993b4f9fe12SLen Brown 
994b4f9fe12SLen Brown 	if (result < 0) {
995b4f9fe12SLen Brown 		printk(KERN_INFO PREFIX "Error loading mapper\n");
9961caab3c1SMatthew Garrett 		return -ENODEV;
997b4f9fe12SLen Brown 	}
998b4f9fe12SLen Brown 
9991caab3c1SMatthew Garrett 	result = wmi_class_init();
10001caab3c1SMatthew Garrett 	if (result) {
10011caab3c1SMatthew Garrett 		acpi_bus_unregister_driver(&acpi_wmi_driver);
10021caab3c1SMatthew Garrett 		return result;
10031caab3c1SMatthew Garrett 	}
10041caab3c1SMatthew Garrett 
10051caab3c1SMatthew Garrett 	printk(KERN_INFO PREFIX "Mapper loaded\n");
10061caab3c1SMatthew Garrett 
1007b4f9fe12SLen Brown 	return result;
1008b4f9fe12SLen Brown }
1009b4f9fe12SLen Brown 
1010b4f9fe12SLen Brown static void __exit acpi_wmi_exit(void)
1011b4f9fe12SLen Brown {
1012b4f9fe12SLen Brown 	struct list_head *p, *tmp;
1013b4f9fe12SLen Brown 	struct wmi_block *wblock;
1014b4f9fe12SLen Brown 
10151caab3c1SMatthew Garrett 	wmi_class_exit();
10161caab3c1SMatthew Garrett 
1017b4f9fe12SLen Brown 	acpi_bus_unregister_driver(&acpi_wmi_driver);
1018b4f9fe12SLen Brown 
1019b4f9fe12SLen Brown 	list_for_each_safe(p, tmp, &wmi_blocks.list) {
1020b4f9fe12SLen Brown 		wblock = list_entry(p, struct wmi_block, list);
1021b4f9fe12SLen Brown 
1022b4f9fe12SLen Brown 		list_del(p);
1023b4f9fe12SLen Brown 		kfree(wblock);
1024b4f9fe12SLen Brown 	}
1025b4f9fe12SLen Brown 
1026b4f9fe12SLen Brown 	printk(KERN_INFO PREFIX "Mapper unloaded\n");
1027b4f9fe12SLen Brown }
1028b4f9fe12SLen Brown 
1029b4f9fe12SLen Brown subsys_initcall(acpi_wmi_init);
1030b4f9fe12SLen Brown module_exit(acpi_wmi_exit);
1031