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