aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/acpi
diff options
context:
space:
mode:
authorCarlos Corbacho <carlos@strangeworlds.co.uk>2008-02-04 21:17:04 -0500
committerLen Brown <len.brown@intel.com>2008-02-05 15:06:40 -0500
commitbff431e49ff531a343fbb2b4426e313000844f32 (patch)
treedf18bae1d7c60e3ccc2d8e8b47445700fd942f50 /drivers/acpi
parent9ef9dc69d4167276c04590d67ee55de8380bc1ad (diff)
ACPI: WMI: Add ACPI-WMI mapping driver
The following is an implementation of the Windows Management Instrumentation (WMI) ACPI interface mapper (PNP0C14). What it does: Parses the _WDG method and exports functions to process WMI method calls, data block query/ set commands (both based on GUID) and does basic event handling. How: WMI presents an in kernel interface here (essentially, a minimal wrapper around ACPI) (const char *guid assume the 36 character ASCII representation of a GUID - e.g. 67C3371D-95A3-4C37-BB61-DD47B491DAAB) wmi_evaluate_method(const char *guid, u8 instance, u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out) wmi_query_block(const char *guid, u8 instance, struct acpi_buffer *out) wmi_set_block(const char *guid, u38 instance, const struct acpi_buffer *in) wmi_install_notify_handler(acpi_notify_handler handler); wmi_remove_notify_handler(void); wmi_get_event_data(u32 event, struct acpi_buffer *out) wmi_has_guid(const char guid*) wmi_has_guid() is a helper function to find if a GUID exists or not on the system (a quick and easy way for WMI dependant drivers to see if the the method/ block they want exists, since GUIDs are supposed to be unique). Event handling - allow a WMI based driver to register a notifier handler for each GUID with WMI. When a notification is sent to a GUID in WMI, the handler registered with WMI is then called (it is left to the caller to ask for the WMI event data associated with the GUID, if needed). What it won't do: Unicode - The MS article[1] calls for converting between ASCII and Unicode (or vice versa) if a GUID is marked as "string". This is left up to the calling driver. Handle a MOF[1] - the WMI mapper just exports methods, data and events to userspace. MOF handling is down to userspace. Userspace interface - this will be added later. [1] http://www.microsoft.com/whdc/system/pnppwr/wmi/wmi-acpi.mspx === ChangeLog == v1 (2007-10-02): * Initial release v2 (2007-10-05): * Cleaned up code - split up super "wmi_evaluate_block" -> each external symbol now handles its own ACPI calls, rather than handing off to a "super" method (and in turn, is a lot simpler to read) * Added a find_guid() symbol - return true if a given GUID exists on the system * wmi_* functions now return type acpi_status (since they are just fancy wrappers around acpi_evaluate_object()) * Removed extra debug code v3 (2007-10-27) * More code clean up - now passes checkpatch.pl * Change data block calls - ref MS spec, method ID is not required for them, so drop it from the function parameters. * Const'ify guid in the function call parameters. * Fix _WDG buffer handling - copy the data to our own private structure. * Change WMI from tristate to bool - otherwise the external functions are not exported in linux/acpi.h if you try to build WMI as a module. * Fix more flag comparisons. * Add a maintainers entry - since I wrote this, I should take the blame for it. v4 (2007-10-30) * Add missing brace from after fixing checkpatch errors. * Rewrote event handling - allow external drivers to register with WMI to handle WMI events * Clean up flags and sanitise flag handling v5 (2007-11-03) * Add sysfs interface for userspace. Export events over netlink again. * Remove module left overs, fully convert to built-in driver. * Tweak in-kernel API to use u8 for instance, since this is what the GUID blocks use (so instance cannot be greater than u8). * Export wmi_get_event_data() for in kernel WMI drivers. v6 (2007-11-07) * Split out userspace into a different patch v7 (2007-11-20) * Fix driver to handle multiple PNP0C14 devices - store all GUIDs using the kernel's built in list functions, and just keep adding to the list every time we handle a PNP0C14 devices - GUIDs will always be unique, and WMI callers do not know or care about different devices. * Change WMI event handler registration to use its' own event handling struct; we should not pass an acpi_handle down to any WMI based drivers - they should be able to function with only the calls provided in WMI. * Update my e-mail address v8 (2007-11-28) * Convert back to a module. * Update Kconfig to default to building as a module. * Remove an erroneous printk. * Simply comments for string flag (since we now leave the handling to the caller). v9 (2007-12-07) * Add back missing MODULE_DEVICE_TABLE for autoloading * Checkpatch fixes v10 (2007-12-12) * Workaround broken GUIDs declared expensive without a WCxx method. * Minor cleanups v11 (2007-12-17) * More fixing for broken GUIDs declared expensive without a WCxx method. * Add basic EmbeddedControl region handling. v12 (2007-12-18) * Changed EC region handling code, as per Alexey's comments. v13 (2007-12-27) * Changed event handling so that we can have one event handler registered per GUID, as per Matthew Garrett's suggestion. v14 (2008-01-12) * Remove ACPI debug statements v15 (2008-02-01) * Replace two remaining 'x == NULL' type tests with '!x' v16 (2008-02-05) * Change MAINTAINERS entry, as I am not, and never have been, paid to work on WMI * Remove 'default' line from Kconfig Signed-off-by: Carlos Corbacho <carlos@strangeworlds.co.uk> CC: Matthew Garrett <mjg59@srcf.ucam.org> CC: Alexey Starikovskiy <aystarik@gmail.com> Signed-off-by: Len Brown <len.brown@intel.com>
Diffstat (limited to 'drivers/acpi')
-rw-r--r--drivers/acpi/Kconfig10
-rw-r--r--drivers/acpi/Makefile1
-rw-r--r--drivers/acpi/wmi.c710
3 files changed, 721 insertions, 0 deletions
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index ccf6ea95f68c..f60cf6fea2bb 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -199,6 +199,16 @@ config ACPI_NUMA
199 depends on (X86 || IA64) 199 depends on (X86 || IA64)
200 default y if IA64_GENERIC || IA64_SGI_SN2 200 default y if IA64_GENERIC || IA64_SGI_SN2
201 201
202config ACPI_WMI
203 tristate "WMI (EXPERIMENTAL)"
204 depends on EXPERIMENTAL
205 help
206 This driver adds support for the ACPI-WMI mapper device (PNP0C14)
207 found on some systems.
208
209 NOTE: You will need another driver or userspace application on top of
210 this to actually use anything defined in the ACPI-WMI mapper.
211
202config ACPI_ASUS 212config ACPI_ASUS
203 tristate "ASUS/Medion Laptop Extras" 213 tristate "ASUS/Medion Laptop Extras"
204 depends on X86 214 depends on X86
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 456446f90077..f29812a86533 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -55,6 +55,7 @@ obj-$(CONFIG_ACPI_THERMAL) += thermal.o
55obj-$(CONFIG_ACPI_SYSTEM) += system.o event.o 55obj-$(CONFIG_ACPI_SYSTEM) += system.o event.o
56obj-$(CONFIG_ACPI_DEBUG) += debug.o 56obj-$(CONFIG_ACPI_DEBUG) += debug.o
57obj-$(CONFIG_ACPI_NUMA) += numa.o 57obj-$(CONFIG_ACPI_NUMA) += numa.o
58obj-$(CONFIG_ACPI_WMI) += wmi.o
58obj-$(CONFIG_ACPI_ASUS) += asus_acpi.o 59obj-$(CONFIG_ACPI_ASUS) += asus_acpi.o
59obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o 60obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o
60obj-$(CONFIG_ACPI_HOTPLUG_MEMORY) += acpi_memhotplug.o 61obj-$(CONFIG_ACPI_HOTPLUG_MEMORY) += acpi_memhotplug.o
diff --git a/drivers/acpi/wmi.c b/drivers/acpi/wmi.c
new file mode 100644
index 000000000000..36b84ab418dd
--- /dev/null
+++ b/drivers/acpi/wmi.c
@@ -0,0 +1,710 @@
1/*
2 * ACPI-WMI mapping driver
3 *
4 * Copyright (C) 2007-2008 Carlos Corbacho <carlos@strangeworlds.co.uk>
5 *
6 * GUID parsing code from ldm.c is:
7 * Copyright (C) 2001,2002 Richard Russon <ldm@flatcap.org>
8 * Copyright (c) 2001-2007 Anton Altaparmakov
9 * Copyright (C) 2001,2002 Jakob Kemi <jakob.kemi@telia.com>
10 *
11 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or (at
16 * your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful, but
19 * WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License along
24 * with this program; if not, write to the Free Software Foundation, Inc.,
25 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
26 *
27 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
28 */
29
30#include <linux/kernel.h>
31#include <linux/init.h>
32#include <linux/types.h>
33#include <linux/list.h>
34#include <linux/acpi.h>
35#include <acpi/acpi_bus.h>
36#include <acpi/acpi_drivers.h>
37
38ACPI_MODULE_NAME("wmi");
39MODULE_AUTHOR("Carlos Corbacho");
40MODULE_DESCRIPTION("ACPI-WMI Mapping Driver");
41MODULE_LICENSE("GPL");
42
43#define ACPI_WMI_CLASS "wmi"
44
45#undef PREFIX
46#define PREFIX "ACPI: WMI: "
47
48static DEFINE_MUTEX(wmi_data_lock);
49
50struct guid_block {
51 char guid[16];
52 union {
53 char object_id[2];
54 struct {
55 unsigned char notify_id;
56 unsigned char reserved;
57 };
58 };
59 u8 instance_count;
60 u8 flags;
61};
62
63struct wmi_block {
64 struct list_head list;
65 struct guid_block gblock;
66 acpi_handle handle;
67 wmi_notify_handler handler;
68 void *handler_data;
69};
70
71static struct wmi_block wmi_blocks;
72
73/*
74 * If the GUID data block is marked as expensive, we must enable and
75 * explicitily disable data collection.
76 */
77#define ACPI_WMI_EXPENSIVE 0x1
78#define ACPI_WMI_METHOD 0x2 /* GUID is a method */
79#define ACPI_WMI_STRING 0x4 /* GUID takes & returns a string */
80#define ACPI_WMI_EVENT 0x8 /* GUID is an event */
81
82static int acpi_wmi_remove(struct acpi_device *device, int type);
83static int acpi_wmi_add(struct acpi_device *device);
84
85static const struct acpi_device_id wmi_device_ids[] = {
86 {"PNP0C14", 0},
87 {"pnp0c14", 0},
88 {"", 0},
89};
90MODULE_DEVICE_TABLE(acpi, wmi_device_ids);
91
92static struct acpi_driver acpi_wmi_driver = {
93 .name = "wmi",
94 .class = ACPI_WMI_CLASS,
95 .ids = wmi_device_ids,
96 .ops = {
97 .add = acpi_wmi_add,
98 .remove = acpi_wmi_remove,
99 },
100};
101
102/*
103 * GUID parsing functions
104 */
105
106/**
107 * wmi_parse_hexbyte - Convert a ASCII hex number to a byte
108 * @src: Pointer to at least 2 characters to convert.
109 *
110 * Convert a two character ASCII hex string to a number.
111 *
112 * Return: 0-255 Success, the byte was parsed correctly
113 * -1 Error, an invalid character was supplied
114 */
115static int wmi_parse_hexbyte(const u8 *src)
116{
117 unsigned int x; /* For correct wrapping */
118 int h;
119
120 /* high part */
121 x = src[0];
122 if (x - '0' <= '9' - '0') {
123 h = x - '0';
124 } else if (x - 'a' <= 'f' - 'a') {
125 h = x - 'a' + 10;
126 } else if (x - 'A' <= 'F' - 'A') {
127 h = x - 'A' + 10;
128 } else {
129 return -1;
130 }
131 h <<= 4;
132
133 /* low part */
134 x = src[1];
135 if (x - '0' <= '9' - '0')
136 return h | (x - '0');
137 if (x - 'a' <= 'f' - 'a')
138 return h | (x - 'a' + 10);
139 if (x - 'A' <= 'F' - 'A')
140 return h | (x - 'A' + 10);
141 return -1;
142}
143
144/**
145 * wmi_swap_bytes - Rearrange GUID bytes to match GUID binary
146 * @src: Memory block holding binary GUID (16 bytes)
147 * @dest: Memory block to hold byte swapped binary GUID (16 bytes)
148 *
149 * Byte swap a binary GUID to match it's real GUID value
150 */
151static void wmi_swap_bytes(u8 *src, u8 *dest)
152{
153 int i;
154
155 for (i = 0; i <= 3; i++)
156 memcpy(dest + i, src + (3 - i), 1);
157
158 for (i = 0; i <= 1; i++)
159 memcpy(dest + 4 + i, src + (5 - i), 1);
160
161 for (i = 0; i <= 1; i++)
162 memcpy(dest + 6 + i, src + (7 - i), 1);
163
164 memcpy(dest + 8, src + 8, 8);
165}
166
167/**
168 * wmi_parse_guid - Convert GUID from ASCII to binary
169 * @src: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
170 * @dest: Memory block to hold binary GUID (16 bytes)
171 *
172 * N.B. The GUID need not be NULL terminated.
173 *
174 * Return: 'true' @dest contains binary GUID
175 * 'false' @dest contents are undefined
176 */
177static bool wmi_parse_guid(const u8 *src, u8 *dest)
178{
179 static const int size[] = { 4, 2, 2, 2, 6 };
180 int i, j, v;
181
182 if (src[8] != '-' || src[13] != '-' ||
183 src[18] != '-' || src[23] != '-')
184 return false;
185
186 for (j = 0; j < 5; j++, src++) {
187 for (i = 0; i < size[j]; i++, src += 2, *dest++ = v) {
188 v = wmi_parse_hexbyte(src);
189 if (v < 0)
190 return false;
191 }
192 }
193
194 return true;
195}
196
197static bool find_guid(const char *guid_string, struct wmi_block **out)
198{
199 char tmp[16], guid_input[16];
200 struct wmi_block *wblock;
201 struct guid_block *block;
202 struct list_head *p;
203
204 wmi_parse_guid(guid_string, tmp);
205 wmi_swap_bytes(tmp, guid_input);
206
207 list_for_each(p, &wmi_blocks.list) {
208 wblock = list_entry(p, struct wmi_block, list);
209 block = &wblock->gblock;
210
211 if (memcmp(block->guid, guid_input, 16) == 0) {
212 if (out)
213 *out = wblock;
214 return 1;
215 }
216 }
217 return 0;
218}
219
220/*
221 * Exported WMI functions
222 */
223/**
224 * wmi_evaluate_method - Evaluate a WMI method
225 * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
226 * @instance: Instance index
227 * @method_id: Method ID to call
228 * &in: Buffer containing input for the method call
229 * &out: Empty buffer to return the method results
230 *
231 * Call an ACPI-WMI method
232 */
233acpi_status wmi_evaluate_method(const char *guid_string, u8 instance,
234u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out)
235{
236 struct guid_block *block = NULL;
237 struct wmi_block *wblock = NULL;
238 acpi_handle handle;
239 acpi_status status;
240 struct acpi_object_list input;
241 union acpi_object params[3];
242 char method[4] = "WM";
243
244 if (!find_guid(guid_string, &wblock))
245 return AE_BAD_ADDRESS;
246
247 block = &wblock->gblock;
248 handle = wblock->handle;
249
250 if (!block->flags & ACPI_WMI_METHOD)
251 return AE_BAD_DATA;
252
253 if (block->instance_count < instance)
254 return AE_BAD_PARAMETER;
255
256 input.count = 2;
257 input.pointer = params;
258 params[0].type = ACPI_TYPE_INTEGER;
259 params[0].integer.value = instance;
260 params[1].type = ACPI_TYPE_INTEGER;
261 params[1].integer.value = method_id;
262
263 if (in) {
264 input.count = 3;
265
266 if (block->flags & ACPI_WMI_STRING) {
267 params[2].type = ACPI_TYPE_STRING;
268 } else {
269 params[2].type = ACPI_TYPE_BUFFER;
270 }
271 params[2].buffer.length = in->length;
272 params[2].buffer.pointer = in->pointer;
273 }
274
275 strncat(method, block->object_id, 2);
276
277 status = acpi_evaluate_object(handle, method, &input, out);
278
279 return status;
280}
281EXPORT_SYMBOL_GPL(wmi_evaluate_method);
282
283/**
284 * wmi_query_block - Return contents of a WMI block
285 * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
286 * @instance: Instance index
287 * &out: Empty buffer to return the contents of the data block to
288 *
289 * Return the contents of an ACPI-WMI data block to a buffer
290 */
291acpi_status wmi_query_block(const char *guid_string, u8 instance,
292struct acpi_buffer *out)
293{
294 struct guid_block *block = NULL;
295 struct wmi_block *wblock = NULL;
296 acpi_handle handle;
297 acpi_status status, wc_status = AE_ERROR;
298 struct acpi_object_list input, wc_input;
299 union acpi_object wc_params[1], wq_params[1];
300 char method[4];
301 char wc_method[4] = "WC";
302
303 if (!guid_string || !out)
304 return AE_BAD_PARAMETER;
305
306 if (!find_guid(guid_string, &wblock))
307 return AE_BAD_ADDRESS;
308
309 block = &wblock->gblock;
310 handle = wblock->handle;
311
312 if (block->instance_count < instance)
313 return AE_BAD_PARAMETER;
314
315 /* Check GUID is a data block */
316 if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD))
317 return AE_BAD_ADDRESS;
318
319 input.count = 1;
320 input.pointer = wq_params;
321 wq_params[0].type = ACPI_TYPE_INTEGER;
322 wq_params[0].integer.value = instance;
323
324 /*
325 * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method first to
326 * enable collection.
327 */
328 if (block->flags & ACPI_WMI_EXPENSIVE) {
329 wc_input.count = 1;
330 wc_input.pointer = wc_params;
331 wc_params[0].type = ACPI_TYPE_INTEGER;
332 wc_params[0].integer.value = 1;
333
334 strncat(wc_method, block->object_id, 2);
335
336 /*
337 * Some GUIDs break the specification by declaring themselves
338 * expensive, but have no corresponding WCxx method. So we
339 * should not fail if this happens.
340 */
341 wc_status = acpi_evaluate_object(handle, wc_method,
342 &wc_input, NULL);
343 }
344
345 strcpy(method, "WQ");
346 strncat(method, block->object_id, 2);
347
348 status = acpi_evaluate_object(handle, method, NULL, out);
349
350 /*
351 * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method, even if
352 * the WQxx method failed - we should disable collection anyway.
353 */
354 if ((block->flags & ACPI_WMI_EXPENSIVE) && wc_status) {
355 wc_params[0].integer.value = 0;
356 status = acpi_evaluate_object(handle,
357 wc_method, &wc_input, NULL);
358 }
359
360 return status;
361}
362EXPORT_SYMBOL_GPL(wmi_query_block);
363
364/**
365 * wmi_set_block - Write to a WMI block
366 * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
367 * @instance: Instance index
368 * &in: Buffer containing new values for the data block
369 *
370 * Write the contents of the input buffer to an ACPI-WMI data block
371 */
372acpi_status wmi_set_block(const char *guid_string, u8 instance,
373const struct acpi_buffer *in)
374{
375 struct guid_block *block = NULL;
376 struct wmi_block *wblock = NULL;
377 acpi_handle handle;
378 struct acpi_object_list input;
379 union acpi_object params[2];
380 char method[4] = "WS";
381
382 if (!guid_string || !in)
383 return AE_BAD_DATA;
384
385 if (!find_guid(guid_string, &wblock))
386 return AE_BAD_ADDRESS;
387
388 block = &wblock->gblock;
389 handle = wblock->handle;
390
391 if (block->instance_count < instance)
392 return AE_BAD_PARAMETER;
393
394 /* Check GUID is a data block */
395 if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD))
396 return AE_BAD_ADDRESS;
397
398 input.count = 2;
399 input.pointer = params;
400 params[0].type = ACPI_TYPE_INTEGER;
401 params[0].integer.value = instance;
402
403 if (block->flags & ACPI_WMI_STRING) {
404 params[1].type = ACPI_TYPE_STRING;
405 } else {
406 params[1].type = ACPI_TYPE_BUFFER;
407 }
408 params[1].buffer.length = in->length;
409 params[1].buffer.pointer = in->pointer;
410
411 strncat(method, block->object_id, 2);
412
413 return acpi_evaluate_object(handle, method, &input, NULL);
414}
415EXPORT_SYMBOL_GPL(wmi_set_block);
416
417/**
418 * wmi_install_notify_handler - Register handler for WMI events
419 * @handler: Function to handle notifications
420 * @data: Data to be returned to handler when event is fired
421 *
422 * Register a handler for events sent to the ACPI-WMI mapper device.
423 */
424acpi_status wmi_install_notify_handler(const char *guid,
425wmi_notify_handler handler, void *data)
426{
427 struct wmi_block *block;
428
429 if (!guid || !handler)
430 return AE_BAD_PARAMETER;
431
432 find_guid(guid, &block);
433 if (!block)
434 return AE_NOT_EXIST;
435
436 if (block->handler)
437 return AE_ALREADY_ACQUIRED;
438
439 block->handler = handler;
440 block->handler_data = data;
441
442 return AE_OK;
443}
444EXPORT_SYMBOL_GPL(wmi_install_notify_handler);
445
446/**
447 * wmi_uninstall_notify_handler - Unregister handler for WMI events
448 *
449 * Unregister handler for events sent to the ACPI-WMI mapper device.
450 */
451acpi_status wmi_remove_notify_handler(const char *guid)
452{
453 struct wmi_block *block;
454
455 if (!guid)
456 return AE_BAD_PARAMETER;
457
458 find_guid(guid, &block);
459 if (!block)
460 return AE_NOT_EXIST;
461
462 if (!block->handler)
463 return AE_NULL_ENTRY;
464
465 block->handler = NULL;
466 block->handler_data = NULL;
467
468 return AE_OK;
469}
470EXPORT_SYMBOL_GPL(wmi_remove_notify_handler);
471
472/**
473 * wmi_get_event_data - Get WMI data associated with an event
474 *
475 * @event - Event to find
476 * &out - Buffer to hold event data
477 *
478 * Returns extra data associated with an event in WMI.
479 */
480acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out)
481{
482 struct acpi_object_list input;
483 union acpi_object params[1];
484 struct guid_block *gblock;
485 struct wmi_block *wblock;
486 struct list_head *p;
487
488 input.count = 1;
489 input.pointer = params;
490 params[0].type = ACPI_TYPE_INTEGER;
491 params[0].integer.value = event;
492
493 list_for_each(p, &wmi_blocks.list) {
494 wblock = list_entry(p, struct wmi_block, list);
495 gblock = &wblock->gblock;
496
497 if ((gblock->flags & ACPI_WMI_EVENT) &&
498 (gblock->notify_id == event))
499 return acpi_evaluate_object(wblock->handle, "_WED",
500 &input, out);
501 }
502
503 return AE_NOT_FOUND;
504}
505EXPORT_SYMBOL_GPL(wmi_get_event_data);
506
507/**
508 * wmi_has_guid - Check if a GUID is available
509 * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
510 *
511 * Check if a given GUID is defined by _WDG
512 */
513bool wmi_has_guid(const char *guid_string)
514{
515 return find_guid(guid_string, NULL);
516}
517EXPORT_SYMBOL_GPL(wmi_has_guid);
518
519/*
520 * Parse the _WDG method for the GUID data blocks
521 */
522static __init acpi_status parse_wdg(acpi_handle handle)
523{
524 struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
525 union acpi_object *obj;
526 struct guid_block *gblock;
527 struct wmi_block *wblock;
528 acpi_status status;
529 u32 i, total;
530
531 status = acpi_evaluate_object(handle, "_WDG", NULL, &out);
532
533 if (ACPI_FAILURE(status))
534 return status;
535
536 obj = (union acpi_object *) out.pointer;
537
538 if (obj->type != ACPI_TYPE_BUFFER)
539 return AE_ERROR;
540
541 total = obj->buffer.length / sizeof(struct guid_block);
542
543 gblock = kzalloc(obj->buffer.length, GFP_KERNEL);
544 if (!gblock)
545 return AE_NO_MEMORY;
546
547 memcpy(gblock, obj->buffer.pointer, obj->buffer.length);
548
549 for (i = 0; i < total; i++) {
550 wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL);
551 if (!wblock)
552 return AE_NO_MEMORY;
553
554 wblock->gblock = gblock[i];
555 wblock->handle = handle;
556 list_add_tail(&wblock->list, &wmi_blocks.list);
557 }
558
559 kfree(out.pointer);
560 kfree(gblock);
561
562 return status;
563}
564
565/*
566 * WMI can have EmbeddedControl access regions. In which case, we just want to
567 * hand these off to the EC driver.
568 */
569static acpi_status
570acpi_wmi_ec_space_handler(u32 function, acpi_physical_address address,
571 u32 bits, acpi_integer * value,
572 void *handler_context, void *region_context)
573{
574 int result = 0, i = 0;
575 u8 temp = 0;
576
577 if ((address > 0xFF) || !value)
578 return AE_BAD_PARAMETER;
579
580 if (function != ACPI_READ && function != ACPI_WRITE)
581 return AE_BAD_PARAMETER;
582
583 if (bits != 8)
584 return AE_BAD_PARAMETER;
585
586 if (function == ACPI_READ) {
587 result = ec_read(address, &temp);
588 (*value) |= ((acpi_integer)temp) << i;
589 } else {
590 temp = 0xff & ((*value) >> i);
591 result = ec_write(address, temp);
592 }
593
594 switch (result) {
595 case -EINVAL:
596 return AE_BAD_PARAMETER;
597 break;
598 case -ENODEV:
599 return AE_NOT_FOUND;
600 break;
601 case -ETIME:
602 return AE_TIME;
603 break;
604 default:
605 return AE_OK;
606 }
607}
608
609static void acpi_wmi_notify(acpi_handle handle, u32 event, void *data)
610{
611 struct guid_block *block;
612 struct wmi_block *wblock;
613 struct list_head *p;
614 struct acpi_device *device = data;
615
616 list_for_each(p, &wmi_blocks.list) {
617 wblock = list_entry(p, struct wmi_block, list);
618 block = &wblock->gblock;
619
620 if ((block->flags & ACPI_WMI_EVENT) &&
621 (block->notify_id == event)) {
622 if (wblock->handler)
623 wblock->handler(event, wblock->handler_data);
624
625 acpi_bus_generate_netlink_event(
626 device->pnp.device_class, device->dev.bus_id,
627 event, 0);
628 break;
629 }
630 }
631}
632
633static int acpi_wmi_remove(struct acpi_device *device, int type)
634{
635 acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
636 acpi_wmi_notify);
637
638 acpi_remove_address_space_handler(device->handle,
639 ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler);
640
641 return 0;
642}
643
644static int __init acpi_wmi_add(struct acpi_device *device)
645{
646 acpi_status status;
647 int result = 0;
648
649 status = acpi_install_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
650 acpi_wmi_notify, device);
651 if (ACPI_FAILURE(status)) {
652 printk(KERN_ERR PREFIX "Error installing notify handler\n");
653 return -ENODEV;
654 }
655
656 status = acpi_install_address_space_handler(device->handle,
657 ACPI_ADR_SPACE_EC,
658 &acpi_wmi_ec_space_handler,
659 NULL, NULL);
660 if (ACPI_FAILURE(status))
661 return -ENODEV;
662
663 status = parse_wdg(device->handle);
664 if (ACPI_FAILURE(status)) {
665 printk(KERN_ERR PREFIX "Error installing EC region handler\n");
666 return -ENODEV;
667 }
668
669 return result;
670}
671
672static int __init acpi_wmi_init(void)
673{
674 acpi_status result;
675
676 if (acpi_disabled)
677 return -ENODEV;
678
679 INIT_LIST_HEAD(&wmi_blocks.list);
680
681 result = acpi_bus_register_driver(&acpi_wmi_driver);
682
683 if (result < 0) {
684 printk(KERN_INFO PREFIX "Error loading mapper\n");
685 } else {
686 printk(KERN_INFO PREFIX "Mapper loaded\n");
687 }
688
689 return result;
690}
691
692static void __exit acpi_wmi_exit(void)
693{
694 struct list_head *p, *tmp;
695 struct wmi_block *wblock;
696
697 acpi_bus_unregister_driver(&acpi_wmi_driver);
698
699 list_for_each_safe(p, tmp, &wmi_blocks.list) {
700 wblock = list_entry(p, struct wmi_block, list);
701
702 list_del(p);
703 kfree(wblock);
704 }
705
706 printk(KERN_INFO PREFIX "Mapper unloaded\n");
707}
708
709subsys_initcall(acpi_wmi_init);
710module_exit(acpi_wmi_exit);