blob: 63d0b65deffb569fdb1080629013bf440f02f669 [file] [log] [blame]
Carlos Corbachobff431e2008-02-05 02:17:04 +00001/*
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>
Matthew Garrett1caab3c2009-11-04 14:17:53 -050033#include <linux/device.h>
Carlos Corbachobff431e2008-02-05 02:17:04 +000034#include <linux/list.h>
35#include <linux/acpi.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090036#include <linux/slab.h>
Carlos Corbachobff431e2008-02-05 02:17:04 +000037#include <acpi/acpi_bus.h>
38#include <acpi/acpi_drivers.h>
39
40ACPI_MODULE_NAME("wmi");
41MODULE_AUTHOR("Carlos Corbacho");
42MODULE_DESCRIPTION("ACPI-WMI Mapping Driver");
43MODULE_LICENSE("GPL");
44
45#define ACPI_WMI_CLASS "wmi"
46
Carlos Corbachobff431e2008-02-05 02:17:04 +000047#define PREFIX "ACPI: WMI: "
48
49static DEFINE_MUTEX(wmi_data_lock);
50
51struct guid_block {
52 char guid[16];
53 union {
54 char object_id[2];
55 struct {
56 unsigned char notify_id;
57 unsigned char reserved;
58 };
59 };
60 u8 instance_count;
61 u8 flags;
62};
63
64struct wmi_block {
65 struct list_head list;
66 struct guid_block gblock;
67 acpi_handle handle;
68 wmi_notify_handler handler;
69 void *handler_data;
Matthew Garrett1caab3c2009-11-04 14:17:53 -050070 struct device *dev;
Carlos Corbachobff431e2008-02-05 02:17:04 +000071};
72
73static struct wmi_block wmi_blocks;
74
75/*
76 * If the GUID data block is marked as expensive, we must enable and
77 * explicitily disable data collection.
78 */
79#define ACPI_WMI_EXPENSIVE 0x1
80#define ACPI_WMI_METHOD 0x2 /* GUID is a method */
81#define ACPI_WMI_STRING 0x4 /* GUID takes & returns a string */
82#define ACPI_WMI_EVENT 0x8 /* GUID is an event */
83
Thomas Renningerfc3155b2010-05-03 15:30:15 +020084static int debug_event;
85module_param(debug_event, bool, 0444);
86MODULE_PARM_DESC(debug_event,
87 "Log WMI Events [0/1]");
88
Thomas Renningera929aae2010-05-03 15:30:17 +020089static int debug_dump_wdg;
90module_param(debug_dump_wdg, bool, 0444);
91MODULE_PARM_DESC(debug_dump_wdg,
92 "Dump available WMI interfaces [0/1]");
93
Carlos Corbachobff431e2008-02-05 02:17:04 +000094static int acpi_wmi_remove(struct acpi_device *device, int type);
95static int acpi_wmi_add(struct acpi_device *device);
Bjorn Helgaasf61bb932009-04-07 15:37:37 +000096static void acpi_wmi_notify(struct acpi_device *device, u32 event);
Carlos Corbachobff431e2008-02-05 02:17:04 +000097
98static const struct acpi_device_id wmi_device_ids[] = {
99 {"PNP0C14", 0},
100 {"pnp0c14", 0},
101 {"", 0},
102};
103MODULE_DEVICE_TABLE(acpi, wmi_device_ids);
104
105static struct acpi_driver acpi_wmi_driver = {
106 .name = "wmi",
107 .class = ACPI_WMI_CLASS,
108 .ids = wmi_device_ids,
109 .ops = {
110 .add = acpi_wmi_add,
111 .remove = acpi_wmi_remove,
Bjorn Helgaasf61bb932009-04-07 15:37:37 +0000112 .notify = acpi_wmi_notify,
Carlos Corbachobff431e2008-02-05 02:17:04 +0000113 },
114};
115
116/*
117 * GUID parsing functions
118 */
119
120/**
121 * wmi_parse_hexbyte - Convert a ASCII hex number to a byte
122 * @src: Pointer to at least 2 characters to convert.
123 *
124 * Convert a two character ASCII hex string to a number.
125 *
126 * Return: 0-255 Success, the byte was parsed correctly
127 * -1 Error, an invalid character was supplied
128 */
129static int wmi_parse_hexbyte(const u8 *src)
130{
Carlos Corbachobff431e2008-02-05 02:17:04 +0000131 int h;
Andy Shevchenko392bd8b2010-09-11 16:31:02 +0300132 int value;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000133
134 /* high part */
Andy Shevchenko392bd8b2010-09-11 16:31:02 +0300135 h = value = hex_to_bin(src[0]);
136 if (value < 0)
Carlos Corbachobff431e2008-02-05 02:17:04 +0000137 return -1;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000138
139 /* low part */
Andy Shevchenko392bd8b2010-09-11 16:31:02 +0300140 value = hex_to_bin(src[1]);
141 if (value >= 0)
142 return (h << 4) | value;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000143 return -1;
144}
145
146/**
147 * wmi_swap_bytes - Rearrange GUID bytes to match GUID binary
148 * @src: Memory block holding binary GUID (16 bytes)
149 * @dest: Memory block to hold byte swapped binary GUID (16 bytes)
150 *
151 * Byte swap a binary GUID to match it's real GUID value
152 */
153static void wmi_swap_bytes(u8 *src, u8 *dest)
154{
155 int i;
156
157 for (i = 0; i <= 3; i++)
158 memcpy(dest + i, src + (3 - i), 1);
159
160 for (i = 0; i <= 1; i++)
161 memcpy(dest + 4 + i, src + (5 - i), 1);
162
163 for (i = 0; i <= 1; i++)
164 memcpy(dest + 6 + i, src + (7 - i), 1);
165
166 memcpy(dest + 8, src + 8, 8);
167}
168
169/**
170 * wmi_parse_guid - Convert GUID from ASCII to binary
171 * @src: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
172 * @dest: Memory block to hold binary GUID (16 bytes)
173 *
174 * N.B. The GUID need not be NULL terminated.
175 *
176 * Return: 'true' @dest contains binary GUID
177 * 'false' @dest contents are undefined
178 */
179static bool wmi_parse_guid(const u8 *src, u8 *dest)
180{
181 static const int size[] = { 4, 2, 2, 2, 6 };
182 int i, j, v;
183
184 if (src[8] != '-' || src[13] != '-' ||
185 src[18] != '-' || src[23] != '-')
186 return false;
187
188 for (j = 0; j < 5; j++, src++) {
189 for (i = 0; i < size[j]; i++, src += 2, *dest++ = v) {
190 v = wmi_parse_hexbyte(src);
191 if (v < 0)
192 return false;
193 }
194 }
195
196 return true;
197}
198
Matthew Garrett1caab3c2009-11-04 14:17:53 -0500199/*
200 * Convert a raw GUID to the ACII string representation
201 */
202static int wmi_gtoa(const char *in, char *out)
203{
204 int i;
205
206 for (i = 3; i >= 0; i--)
207 out += sprintf(out, "%02X", in[i] & 0xFF);
208
209 out += sprintf(out, "-");
210 out += sprintf(out, "%02X", in[5] & 0xFF);
211 out += sprintf(out, "%02X", in[4] & 0xFF);
212 out += sprintf(out, "-");
213 out += sprintf(out, "%02X", in[7] & 0xFF);
214 out += sprintf(out, "%02X", in[6] & 0xFF);
215 out += sprintf(out, "-");
216 out += sprintf(out, "%02X", in[8] & 0xFF);
217 out += sprintf(out, "%02X", in[9] & 0xFF);
218 out += sprintf(out, "-");
219
220 for (i = 10; i <= 15; i++)
221 out += sprintf(out, "%02X", in[i] & 0xFF);
222
223 out = '\0';
224 return 0;
225}
226
Carlos Corbachobff431e2008-02-05 02:17:04 +0000227static bool find_guid(const char *guid_string, struct wmi_block **out)
228{
229 char tmp[16], guid_input[16];
230 struct wmi_block *wblock;
231 struct guid_block *block;
232 struct list_head *p;
233
234 wmi_parse_guid(guid_string, tmp);
235 wmi_swap_bytes(tmp, guid_input);
236
237 list_for_each(p, &wmi_blocks.list) {
238 wblock = list_entry(p, struct wmi_block, list);
239 block = &wblock->gblock;
240
241 if (memcmp(block->guid, guid_input, 16) == 0) {
242 if (out)
243 *out = wblock;
244 return 1;
245 }
246 }
247 return 0;
248}
249
Matthew Garretta66bfa72008-10-08 21:40:32 +0100250static acpi_status wmi_method_enable(struct wmi_block *wblock, int enable)
251{
252 struct guid_block *block = NULL;
253 char method[5];
254 struct acpi_object_list input;
255 union acpi_object params[1];
256 acpi_status status;
257 acpi_handle handle;
258
259 block = &wblock->gblock;
260 handle = wblock->handle;
261
262 if (!block)
263 return AE_NOT_EXIST;
264
265 input.count = 1;
266 input.pointer = params;
267 params[0].type = ACPI_TYPE_INTEGER;
268 params[0].integer.value = enable;
269
270 snprintf(method, 5, "WE%02X", block->notify_id);
271 status = acpi_evaluate_object(handle, method, &input, NULL);
272
273 if (status != AE_OK && status != AE_NOT_FOUND)
274 return status;
275 else
276 return AE_OK;
277}
278
Carlos Corbachobff431e2008-02-05 02:17:04 +0000279/*
280 * Exported WMI functions
281 */
282/**
283 * wmi_evaluate_method - Evaluate a WMI method
284 * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
285 * @instance: Instance index
286 * @method_id: Method ID to call
287 * &in: Buffer containing input for the method call
288 * &out: Empty buffer to return the method results
289 *
290 * Call an ACPI-WMI method
291 */
292acpi_status wmi_evaluate_method(const char *guid_string, u8 instance,
293u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out)
294{
295 struct guid_block *block = NULL;
296 struct wmi_block *wblock = NULL;
297 acpi_handle handle;
298 acpi_status status;
299 struct acpi_object_list input;
300 union acpi_object params[3];
Costantino Leandrof3d83e22009-08-26 14:29:28 -0700301 char method[5] = "WM";
Carlos Corbachobff431e2008-02-05 02:17:04 +0000302
303 if (!find_guid(guid_string, &wblock))
Lin Ming08237972008-08-08 11:57:11 +0800304 return AE_ERROR;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000305
306 block = &wblock->gblock;
307 handle = wblock->handle;
308
Al Viroe6bafba2008-02-13 04:03:25 +0000309 if (!(block->flags & ACPI_WMI_METHOD))
Carlos Corbachobff431e2008-02-05 02:17:04 +0000310 return AE_BAD_DATA;
311
312 if (block->instance_count < instance)
313 return AE_BAD_PARAMETER;
314
315 input.count = 2;
316 input.pointer = params;
317 params[0].type = ACPI_TYPE_INTEGER;
318 params[0].integer.value = instance;
319 params[1].type = ACPI_TYPE_INTEGER;
320 params[1].integer.value = method_id;
321
322 if (in) {
323 input.count = 3;
324
325 if (block->flags & ACPI_WMI_STRING) {
326 params[2].type = ACPI_TYPE_STRING;
327 } else {
328 params[2].type = ACPI_TYPE_BUFFER;
329 }
330 params[2].buffer.length = in->length;
331 params[2].buffer.pointer = in->pointer;
332 }
333
334 strncat(method, block->object_id, 2);
335
336 status = acpi_evaluate_object(handle, method, &input, out);
337
338 return status;
339}
340EXPORT_SYMBOL_GPL(wmi_evaluate_method);
341
342/**
343 * wmi_query_block - Return contents of a WMI block
344 * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
345 * @instance: Instance index
346 * &out: Empty buffer to return the contents of the data block to
347 *
348 * Return the contents of an ACPI-WMI data block to a buffer
349 */
350acpi_status wmi_query_block(const char *guid_string, u8 instance,
351struct acpi_buffer *out)
352{
353 struct guid_block *block = NULL;
354 struct wmi_block *wblock = NULL;
Carlos Corbachoa527f2d2008-02-24 13:34:34 +0000355 acpi_handle handle, wc_handle;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000356 acpi_status status, wc_status = AE_ERROR;
357 struct acpi_object_list input, wc_input;
358 union acpi_object wc_params[1], wq_params[1];
Costantino Leandrof3d83e22009-08-26 14:29:28 -0700359 char method[5];
360 char wc_method[5] = "WC";
Carlos Corbachobff431e2008-02-05 02:17:04 +0000361
362 if (!guid_string || !out)
363 return AE_BAD_PARAMETER;
364
365 if (!find_guid(guid_string, &wblock))
Lin Ming08237972008-08-08 11:57:11 +0800366 return AE_ERROR;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000367
368 block = &wblock->gblock;
369 handle = wblock->handle;
370
371 if (block->instance_count < instance)
372 return AE_BAD_PARAMETER;
373
374 /* Check GUID is a data block */
375 if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD))
Lin Ming08237972008-08-08 11:57:11 +0800376 return AE_ERROR;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000377
378 input.count = 1;
379 input.pointer = wq_params;
380 wq_params[0].type = ACPI_TYPE_INTEGER;
381 wq_params[0].integer.value = instance;
382
383 /*
384 * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method first to
385 * enable collection.
386 */
387 if (block->flags & ACPI_WMI_EXPENSIVE) {
388 wc_input.count = 1;
389 wc_input.pointer = wc_params;
390 wc_params[0].type = ACPI_TYPE_INTEGER;
391 wc_params[0].integer.value = 1;
392
393 strncat(wc_method, block->object_id, 2);
394
395 /*
396 * Some GUIDs break the specification by declaring themselves
397 * expensive, but have no corresponding WCxx method. So we
398 * should not fail if this happens.
399 */
Carlos Corbachoa527f2d2008-02-24 13:34:34 +0000400 wc_status = acpi_get_handle(handle, wc_method, &wc_handle);
401 if (ACPI_SUCCESS(wc_status))
402 wc_status = acpi_evaluate_object(handle, wc_method,
403 &wc_input, NULL);
Carlos Corbachobff431e2008-02-05 02:17:04 +0000404 }
405
406 strcpy(method, "WQ");
407 strncat(method, block->object_id, 2);
408
Carlos Corbachodab36ad2008-08-02 17:28:45 +0100409 status = acpi_evaluate_object(handle, method, &input, out);
Carlos Corbachobff431e2008-02-05 02:17:04 +0000410
411 /*
412 * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method, even if
413 * the WQxx method failed - we should disable collection anyway.
414 */
Carlos Corbachoa527f2d2008-02-24 13:34:34 +0000415 if ((block->flags & ACPI_WMI_EXPENSIVE) && ACPI_SUCCESS(wc_status)) {
Carlos Corbachobff431e2008-02-05 02:17:04 +0000416 wc_params[0].integer.value = 0;
417 status = acpi_evaluate_object(handle,
418 wc_method, &wc_input, NULL);
419 }
420
421 return status;
422}
423EXPORT_SYMBOL_GPL(wmi_query_block);
424
425/**
426 * wmi_set_block - Write to a WMI block
427 * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
428 * @instance: Instance index
429 * &in: Buffer containing new values for the data block
430 *
431 * Write the contents of the input buffer to an ACPI-WMI data block
432 */
433acpi_status wmi_set_block(const char *guid_string, u8 instance,
434const struct acpi_buffer *in)
435{
436 struct guid_block *block = NULL;
437 struct wmi_block *wblock = NULL;
438 acpi_handle handle;
439 struct acpi_object_list input;
440 union acpi_object params[2];
Costantino Leandrof3d83e22009-08-26 14:29:28 -0700441 char method[5] = "WS";
Carlos Corbachobff431e2008-02-05 02:17:04 +0000442
443 if (!guid_string || !in)
444 return AE_BAD_DATA;
445
446 if (!find_guid(guid_string, &wblock))
Lin Ming08237972008-08-08 11:57:11 +0800447 return AE_ERROR;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000448
449 block = &wblock->gblock;
450 handle = wblock->handle;
451
452 if (block->instance_count < instance)
453 return AE_BAD_PARAMETER;
454
455 /* Check GUID is a data block */
456 if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD))
Lin Ming08237972008-08-08 11:57:11 +0800457 return AE_ERROR;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000458
459 input.count = 2;
460 input.pointer = params;
461 params[0].type = ACPI_TYPE_INTEGER;
462 params[0].integer.value = instance;
463
464 if (block->flags & ACPI_WMI_STRING) {
465 params[1].type = ACPI_TYPE_STRING;
466 } else {
467 params[1].type = ACPI_TYPE_BUFFER;
468 }
469 params[1].buffer.length = in->length;
470 params[1].buffer.pointer = in->pointer;
471
472 strncat(method, block->object_id, 2);
473
474 return acpi_evaluate_object(handle, method, &input, NULL);
475}
476EXPORT_SYMBOL_GPL(wmi_set_block);
477
Thomas Renningera929aae2010-05-03 15:30:17 +0200478static void wmi_dump_wdg(struct guid_block *g)
479{
480 char guid_string[37];
481
482 wmi_gtoa(g->guid, guid_string);
483 printk(KERN_INFO PREFIX "%s:\n", guid_string);
484 printk(KERN_INFO PREFIX "\tobject_id: %c%c\n",
485 g->object_id[0], g->object_id[1]);
486 printk(KERN_INFO PREFIX "\tnotify_id: %02X\n", g->notify_id);
487 printk(KERN_INFO PREFIX "\treserved: %02X\n", g->reserved);
488 printk(KERN_INFO PREFIX "\tinstance_count: %d\n", g->instance_count);
489 printk(KERN_INFO PREFIX "\tflags: %#x", g->flags);
490 if (g->flags) {
491 printk(" ");
492 if (g->flags & ACPI_WMI_EXPENSIVE)
493 printk("ACPI_WMI_EXPENSIVE ");
494 if (g->flags & ACPI_WMI_METHOD)
495 printk("ACPI_WMI_METHOD ");
496 if (g->flags & ACPI_WMI_STRING)
497 printk("ACPI_WMI_STRING ");
498 if (g->flags & ACPI_WMI_EVENT)
499 printk("ACPI_WMI_EVENT ");
500 }
501 printk("\n");
502
503}
504
Thomas Renningerfc3155b2010-05-03 15:30:15 +0200505static void wmi_notify_debug(u32 value, void *context)
506{
507 struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
508 union acpi_object *obj;
Axel Lin14926162010-06-28 09:30:45 +0800509 acpi_status status;
Thomas Renningerfc3155b2010-05-03 15:30:15 +0200510
Axel Lin14926162010-06-28 09:30:45 +0800511 status = wmi_get_event_data(value, &response);
512 if (status != AE_OK) {
513 printk(KERN_INFO "wmi: bad event status 0x%x\n", status);
514 return;
515 }
Thomas Renningerfc3155b2010-05-03 15:30:15 +0200516
517 obj = (union acpi_object *)response.pointer;
518
519 if (!obj)
520 return;
521
522 printk(KERN_INFO PREFIX "DEBUG Event ");
523 switch(obj->type) {
524 case ACPI_TYPE_BUFFER:
525 printk("BUFFER_TYPE - length %d\n", obj->buffer.length);
526 break;
527 case ACPI_TYPE_STRING:
528 printk("STRING_TYPE - %s\n", obj->string.pointer);
529 break;
530 case ACPI_TYPE_INTEGER:
531 printk("INTEGER_TYPE - %llu\n", obj->integer.value);
532 break;
533 case ACPI_TYPE_PACKAGE:
534 printk("PACKAGE_TYPE - %d elements\n", obj->package.count);
535 break;
536 default:
537 printk("object type 0x%X\n", obj->type);
538 }
Axel Lin14926162010-06-28 09:30:45 +0800539 kfree(obj);
Thomas Renningerfc3155b2010-05-03 15:30:15 +0200540}
541
Carlos Corbachobff431e2008-02-05 02:17:04 +0000542/**
543 * wmi_install_notify_handler - Register handler for WMI events
544 * @handler: Function to handle notifications
545 * @data: Data to be returned to handler when event is fired
546 *
547 * Register a handler for events sent to the ACPI-WMI mapper device.
548 */
549acpi_status wmi_install_notify_handler(const char *guid,
550wmi_notify_handler handler, void *data)
551{
552 struct wmi_block *block;
Matthew Garretta66bfa72008-10-08 21:40:32 +0100553 acpi_status status;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000554
555 if (!guid || !handler)
556 return AE_BAD_PARAMETER;
557
Paul Rollandc03b26a2009-12-30 01:07:40 -0500558 if (!find_guid(guid, &block))
Carlos Corbachobff431e2008-02-05 02:17:04 +0000559 return AE_NOT_EXIST;
560
Thomas Renningerfc3155b2010-05-03 15:30:15 +0200561 if (block->handler && block->handler != wmi_notify_debug)
Carlos Corbachobff431e2008-02-05 02:17:04 +0000562 return AE_ALREADY_ACQUIRED;
563
564 block->handler = handler;
565 block->handler_data = data;
566
Matthew Garretta66bfa72008-10-08 21:40:32 +0100567 status = wmi_method_enable(block, 1);
568
569 return status;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000570}
571EXPORT_SYMBOL_GPL(wmi_install_notify_handler);
572
573/**
574 * wmi_uninstall_notify_handler - Unregister handler for WMI events
575 *
576 * Unregister handler for events sent to the ACPI-WMI mapper device.
577 */
578acpi_status wmi_remove_notify_handler(const char *guid)
579{
580 struct wmi_block *block;
Thomas Renningerfc3155b2010-05-03 15:30:15 +0200581 acpi_status status = AE_OK;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000582
583 if (!guid)
584 return AE_BAD_PARAMETER;
585
Paul Rollandc03b26a2009-12-30 01:07:40 -0500586 if (!find_guid(guid, &block))
Carlos Corbachobff431e2008-02-05 02:17:04 +0000587 return AE_NOT_EXIST;
588
Thomas Renningerfc3155b2010-05-03 15:30:15 +0200589 if (!block->handler || block->handler == wmi_notify_debug)
Carlos Corbachobff431e2008-02-05 02:17:04 +0000590 return AE_NULL_ENTRY;
591
Thomas Renningerfc3155b2010-05-03 15:30:15 +0200592 if (debug_event) {
593 block->handler = wmi_notify_debug;
594 } else {
595 status = wmi_method_enable(block, 0);
596 block->handler = NULL;
597 block->handler_data = NULL;
598 }
Matthew Garretta66bfa72008-10-08 21:40:32 +0100599 return status;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000600}
601EXPORT_SYMBOL_GPL(wmi_remove_notify_handler);
602
603/**
604 * wmi_get_event_data - Get WMI data associated with an event
605 *
Anisse Astier3e9b9882009-12-04 10:10:09 +0100606 * @event: Event to find
607 * @out: Buffer to hold event data. out->pointer should be freed with kfree()
Carlos Corbachobff431e2008-02-05 02:17:04 +0000608 *
609 * Returns extra data associated with an event in WMI.
610 */
611acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out)
612{
613 struct acpi_object_list input;
614 union acpi_object params[1];
615 struct guid_block *gblock;
616 struct wmi_block *wblock;
617 struct list_head *p;
618
619 input.count = 1;
620 input.pointer = params;
621 params[0].type = ACPI_TYPE_INTEGER;
622 params[0].integer.value = event;
623
624 list_for_each(p, &wmi_blocks.list) {
625 wblock = list_entry(p, struct wmi_block, list);
626 gblock = &wblock->gblock;
627
628 if ((gblock->flags & ACPI_WMI_EVENT) &&
629 (gblock->notify_id == event))
630 return acpi_evaluate_object(wblock->handle, "_WED",
631 &input, out);
632 }
633
634 return AE_NOT_FOUND;
635}
636EXPORT_SYMBOL_GPL(wmi_get_event_data);
637
638/**
639 * wmi_has_guid - Check if a GUID is available
640 * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
641 *
642 * Check if a given GUID is defined by _WDG
643 */
644bool wmi_has_guid(const char *guid_string)
645{
646 return find_guid(guid_string, NULL);
647}
648EXPORT_SYMBOL_GPL(wmi_has_guid);
649
650/*
Matthew Garrett1caab3c2009-11-04 14:17:53 -0500651 * sysfs interface
652 */
653static ssize_t show_modalias(struct device *dev, struct device_attribute *attr,
654 char *buf)
655{
656 char guid_string[37];
657 struct wmi_block *wblock;
658
659 wblock = dev_get_drvdata(dev);
660 if (!wblock)
661 return -ENOMEM;
662
663 wmi_gtoa(wblock->gblock.guid, guid_string);
664
665 return sprintf(buf, "wmi:%s\n", guid_string);
666}
667static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL);
668
669static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env)
670{
671 char guid_string[37];
672
673 struct wmi_block *wblock;
674
675 if (add_uevent_var(env, "MODALIAS="))
676 return -ENOMEM;
677
678 wblock = dev_get_drvdata(dev);
679 if (!wblock)
680 return -ENOMEM;
681
682 wmi_gtoa(wblock->gblock.guid, guid_string);
683
684 strcpy(&env->buf[env->buflen - 1], "wmi:");
685 memcpy(&env->buf[env->buflen - 1 + 4], guid_string, 36);
686 env->buflen += 40;
687
688 return 0;
689}
690
691static void wmi_dev_free(struct device *dev)
692{
693 kfree(dev);
694}
695
696static struct class wmi_class = {
697 .name = "wmi",
698 .dev_release = wmi_dev_free,
699 .dev_uevent = wmi_dev_uevent,
700};
701
702static int wmi_create_devs(void)
703{
704 int result;
705 char guid_string[37];
706 struct guid_block *gblock;
707 struct wmi_block *wblock;
708 struct list_head *p;
709 struct device *guid_dev;
710
711 /* Create devices for all the GUIDs */
712 list_for_each(p, &wmi_blocks.list) {
713 wblock = list_entry(p, struct wmi_block, list);
714
715 guid_dev = kzalloc(sizeof(struct device), GFP_KERNEL);
716 if (!guid_dev)
717 return -ENOMEM;
718
719 wblock->dev = guid_dev;
720
721 guid_dev->class = &wmi_class;
722 dev_set_drvdata(guid_dev, wblock);
723
724 gblock = &wblock->gblock;
725
726 wmi_gtoa(gblock->guid, guid_string);
727 dev_set_name(guid_dev, guid_string);
728
729 result = device_register(guid_dev);
730 if (result)
731 return result;
732
733 result = device_create_file(guid_dev, &dev_attr_modalias);
734 if (result)
735 return result;
736 }
737
738 return 0;
739}
740
741static void wmi_remove_devs(void)
742{
743 struct guid_block *gblock;
744 struct wmi_block *wblock;
745 struct list_head *p;
746 struct device *guid_dev;
747
748 /* Delete devices for all the GUIDs */
749 list_for_each(p, &wmi_blocks.list) {
750 wblock = list_entry(p, struct wmi_block, list);
751
752 guid_dev = wblock->dev;
753 gblock = &wblock->gblock;
754
755 device_remove_file(guid_dev, &dev_attr_modalias);
756
757 device_unregister(guid_dev);
758 }
759}
760
761static void wmi_class_exit(void)
762{
763 wmi_remove_devs();
764 class_unregister(&wmi_class);
765}
766
767static int wmi_class_init(void)
768{
769 int ret;
770
771 ret = class_register(&wmi_class);
772 if (ret)
773 return ret;
774
775 ret = wmi_create_devs();
776 if (ret)
777 wmi_class_exit();
778
779 return ret;
780}
781
Carlos Corbachod1f9e492009-12-26 19:14:59 +0000782static bool guid_already_parsed(const char *guid_string)
783{
784 struct guid_block *gblock;
785 struct wmi_block *wblock;
786 struct list_head *p;
787
788 list_for_each(p, &wmi_blocks.list) {
789 wblock = list_entry(p, struct wmi_block, list);
790 gblock = &wblock->gblock;
791
792 if (strncmp(gblock->guid, guid_string, 16) == 0)
793 return true;
794 }
795 return false;
796}
797
Matthew Garrett1caab3c2009-11-04 14:17:53 -0500798/*
Carlos Corbachobff431e2008-02-05 02:17:04 +0000799 * Parse the _WDG method for the GUID data blocks
800 */
Thomas Renninger925b1082010-07-16 13:11:36 +0200801static acpi_status parse_wdg(acpi_handle handle)
Carlos Corbachobff431e2008-02-05 02:17:04 +0000802{
803 struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
804 union acpi_object *obj;
805 struct guid_block *gblock;
806 struct wmi_block *wblock;
Carlos Corbachod1f9e492009-12-26 19:14:59 +0000807 char guid_string[37];
Carlos Corbachobff431e2008-02-05 02:17:04 +0000808 acpi_status status;
809 u32 i, total;
810
811 status = acpi_evaluate_object(handle, "_WDG", NULL, &out);
812
813 if (ACPI_FAILURE(status))
814 return status;
815
816 obj = (union acpi_object *) out.pointer;
817
818 if (obj->type != ACPI_TYPE_BUFFER)
819 return AE_ERROR;
820
821 total = obj->buffer.length / sizeof(struct guid_block);
822
Julia Lawall2c6719a2010-05-15 23:22:18 +0200823 gblock = kmemdup(obj->buffer.pointer, obj->buffer.length, GFP_KERNEL);
Axel Lina5167c52010-06-03 11:45:45 +0800824 if (!gblock) {
825 status = AE_NO_MEMORY;
826 goto out_free_pointer;
827 }
Carlos Corbachobff431e2008-02-05 02:17:04 +0000828
Carlos Corbachobff431e2008-02-05 02:17:04 +0000829 for (i = 0; i < total; i++) {
Carlos Corbachod1f9e492009-12-26 19:14:59 +0000830 /*
831 Some WMI devices, like those for nVidia hooks, have a
832 duplicate GUID. It's not clear what we should do in this
833 case yet, so for now, we'll just ignore the duplicate.
834 Anyone who wants to add support for that device can come
835 up with a better workaround for the mess then.
836 */
837 if (guid_already_parsed(gblock[i].guid) == true) {
838 wmi_gtoa(gblock[i].guid, guid_string);
839 printk(KERN_INFO PREFIX "Skipping duplicate GUID %s\n",
840 guid_string);
841 continue;
842 }
Thomas Renningera929aae2010-05-03 15:30:17 +0200843 if (debug_dump_wdg)
844 wmi_dump_wdg(&gblock[i]);
845
Carlos Corbachobff431e2008-02-05 02:17:04 +0000846 wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL);
Axel Lina5167c52010-06-03 11:45:45 +0800847 if (!wblock) {
848 status = AE_NO_MEMORY;
849 goto out_free_gblock;
850 }
Carlos Corbachobff431e2008-02-05 02:17:04 +0000851
852 wblock->gblock = gblock[i];
853 wblock->handle = handle;
Thomas Renningerfc3155b2010-05-03 15:30:15 +0200854 if (debug_event) {
855 wblock->handler = wmi_notify_debug;
856 status = wmi_method_enable(wblock, 1);
857 }
Carlos Corbachobff431e2008-02-05 02:17:04 +0000858 list_add_tail(&wblock->list, &wmi_blocks.list);
859 }
860
Axel Lina5167c52010-06-03 11:45:45 +0800861out_free_gblock:
Carlos Corbachobff431e2008-02-05 02:17:04 +0000862 kfree(gblock);
Axel Lina5167c52010-06-03 11:45:45 +0800863out_free_pointer:
864 kfree(out.pointer);
Carlos Corbachobff431e2008-02-05 02:17:04 +0000865
866 return status;
867}
868
869/*
870 * WMI can have EmbeddedControl access regions. In which case, we just want to
871 * hand these off to the EC driver.
872 */
873static acpi_status
874acpi_wmi_ec_space_handler(u32 function, acpi_physical_address address,
Lin Ming439913f2010-01-28 10:53:19 +0800875 u32 bits, u64 *value,
Carlos Corbachobff431e2008-02-05 02:17:04 +0000876 void *handler_context, void *region_context)
877{
878 int result = 0, i = 0;
879 u8 temp = 0;
880
881 if ((address > 0xFF) || !value)
882 return AE_BAD_PARAMETER;
883
884 if (function != ACPI_READ && function != ACPI_WRITE)
885 return AE_BAD_PARAMETER;
886
887 if (bits != 8)
888 return AE_BAD_PARAMETER;
889
890 if (function == ACPI_READ) {
891 result = ec_read(address, &temp);
Lin Ming439913f2010-01-28 10:53:19 +0800892 (*value) |= ((u64)temp) << i;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000893 } else {
894 temp = 0xff & ((*value) >> i);
895 result = ec_write(address, temp);
896 }
897
898 switch (result) {
899 case -EINVAL:
900 return AE_BAD_PARAMETER;
901 break;
902 case -ENODEV:
903 return AE_NOT_FOUND;
904 break;
905 case -ETIME:
906 return AE_TIME;
907 break;
908 default:
909 return AE_OK;
910 }
911}
912
Bjorn Helgaasf61bb932009-04-07 15:37:37 +0000913static void acpi_wmi_notify(struct acpi_device *device, u32 event)
Carlos Corbachobff431e2008-02-05 02:17:04 +0000914{
915 struct guid_block *block;
916 struct wmi_block *wblock;
917 struct list_head *p;
Thomas Renninger7715348c2010-05-03 15:30:16 +0200918 char guid_string[37];
Carlos Corbachobff431e2008-02-05 02:17:04 +0000919
920 list_for_each(p, &wmi_blocks.list) {
921 wblock = list_entry(p, struct wmi_block, list);
922 block = &wblock->gblock;
923
924 if ((block->flags & ACPI_WMI_EVENT) &&
925 (block->notify_id == event)) {
926 if (wblock->handler)
927 wblock->handler(event, wblock->handler_data);
Thomas Renninger7715348c2010-05-03 15:30:16 +0200928 if (debug_event) {
929 wmi_gtoa(wblock->gblock.guid, guid_string);
930 printk(KERN_INFO PREFIX "DEBUG Event GUID:"
931 " %s\n", guid_string);
932 }
Carlos Corbachobff431e2008-02-05 02:17:04 +0000933
934 acpi_bus_generate_netlink_event(
Kay Sievers07944692008-10-30 01:18:59 +0100935 device->pnp.device_class, dev_name(&device->dev),
Carlos Corbachobff431e2008-02-05 02:17:04 +0000936 event, 0);
937 break;
938 }
939 }
940}
941
942static int acpi_wmi_remove(struct acpi_device *device, int type)
943{
Carlos Corbachobff431e2008-02-05 02:17:04 +0000944 acpi_remove_address_space_handler(device->handle,
945 ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler);
946
947 return 0;
948}
949
Thomas Renninger925b1082010-07-16 13:11:36 +0200950static int acpi_wmi_add(struct acpi_device *device)
Carlos Corbachobff431e2008-02-05 02:17:04 +0000951{
952 acpi_status status;
953 int result = 0;
954
Carlos Corbachobff431e2008-02-05 02:17:04 +0000955 status = acpi_install_address_space_handler(device->handle,
956 ACPI_ADR_SPACE_EC,
957 &acpi_wmi_ec_space_handler,
958 NULL, NULL);
Dmitry Torokhov5212cd62010-08-26 00:14:42 -0700959 if (ACPI_FAILURE(status)) {
960 printk(KERN_ERR PREFIX "Error installing EC region handler\n");
Carlos Corbachobff431e2008-02-05 02:17:04 +0000961 return -ENODEV;
Dmitry Torokhov5212cd62010-08-26 00:14:42 -0700962 }
Carlos Corbachobff431e2008-02-05 02:17:04 +0000963
964 status = parse_wdg(device->handle);
965 if (ACPI_FAILURE(status)) {
Dmitry Torokhov5212cd62010-08-26 00:14:42 -0700966 acpi_remove_address_space_handler(device->handle,
967 ACPI_ADR_SPACE_EC,
968 &acpi_wmi_ec_space_handler);
969 printk(KERN_ERR PREFIX "Failed to parse WDG method\n");
Carlos Corbachobff431e2008-02-05 02:17:04 +0000970 return -ENODEV;
971 }
972
973 return result;
974}
975
976static int __init acpi_wmi_init(void)
977{
Roel Kluinda511992009-03-04 11:55:30 -0800978 int result;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000979
Linus Torvalds96b5a462008-02-11 20:52:01 -0800980 INIT_LIST_HEAD(&wmi_blocks.list);
981
Carlos Corbachobff431e2008-02-05 02:17:04 +0000982 if (acpi_disabled)
983 return -ENODEV;
984
Carlos Corbachobff431e2008-02-05 02:17:04 +0000985 result = acpi_bus_register_driver(&acpi_wmi_driver);
986
987 if (result < 0) {
988 printk(KERN_INFO PREFIX "Error loading mapper\n");
Matthew Garrett1caab3c2009-11-04 14:17:53 -0500989 return -ENODEV;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000990 }
991
Matthew Garrett1caab3c2009-11-04 14:17:53 -0500992 result = wmi_class_init();
993 if (result) {
994 acpi_bus_unregister_driver(&acpi_wmi_driver);
995 return result;
996 }
997
998 printk(KERN_INFO PREFIX "Mapper loaded\n");
999
Carlos Corbachobff431e2008-02-05 02:17:04 +00001000 return result;
1001}
1002
1003static void __exit acpi_wmi_exit(void)
1004{
1005 struct list_head *p, *tmp;
1006 struct wmi_block *wblock;
1007
Matthew Garrett1caab3c2009-11-04 14:17:53 -05001008 wmi_class_exit();
1009
Carlos Corbachobff431e2008-02-05 02:17:04 +00001010 acpi_bus_unregister_driver(&acpi_wmi_driver);
1011
1012 list_for_each_safe(p, tmp, &wmi_blocks.list) {
1013 wblock = list_entry(p, struct wmi_block, list);
1014
1015 list_del(p);
1016 kfree(wblock);
1017 }
1018
1019 printk(KERN_INFO PREFIX "Mapper unloaded\n");
1020}
1021
1022subsys_initcall(acpi_wmi_init);
1023module_exit(acpi_wmi_exit);