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