blob: 582b5cdd3f4368b8fb9a1f5827be388dffc20e94 [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{
131 unsigned int x; /* For correct wrapping */
132 int h;
133
134 /* high part */
135 x = src[0];
136 if (x - '0' <= '9' - '0') {
137 h = x - '0';
138 } else if (x - 'a' <= 'f' - 'a') {
139 h = x - 'a' + 10;
140 } else if (x - 'A' <= 'F' - 'A') {
141 h = x - 'A' + 10;
142 } else {
143 return -1;
144 }
145 h <<= 4;
146
147 /* low part */
148 x = src[1];
149 if (x - '0' <= '9' - '0')
150 return h | (x - '0');
151 if (x - 'a' <= 'f' - 'a')
152 return h | (x - 'a' + 10);
153 if (x - 'A' <= 'F' - 'A')
154 return h | (x - 'A' + 10);
155 return -1;
156}
157
158/**
159 * wmi_swap_bytes - Rearrange GUID bytes to match GUID binary
160 * @src: Memory block holding binary GUID (16 bytes)
161 * @dest: Memory block to hold byte swapped binary GUID (16 bytes)
162 *
163 * Byte swap a binary GUID to match it's real GUID value
164 */
165static void wmi_swap_bytes(u8 *src, u8 *dest)
166{
167 int i;
168
169 for (i = 0; i <= 3; i++)
170 memcpy(dest + i, src + (3 - i), 1);
171
172 for (i = 0; i <= 1; i++)
173 memcpy(dest + 4 + i, src + (5 - i), 1);
174
175 for (i = 0; i <= 1; i++)
176 memcpy(dest + 6 + i, src + (7 - i), 1);
177
178 memcpy(dest + 8, src + 8, 8);
179}
180
181/**
182 * wmi_parse_guid - Convert GUID from ASCII to binary
183 * @src: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
184 * @dest: Memory block to hold binary GUID (16 bytes)
185 *
186 * N.B. The GUID need not be NULL terminated.
187 *
188 * Return: 'true' @dest contains binary GUID
189 * 'false' @dest contents are undefined
190 */
191static bool wmi_parse_guid(const u8 *src, u8 *dest)
192{
193 static const int size[] = { 4, 2, 2, 2, 6 };
194 int i, j, v;
195
196 if (src[8] != '-' || src[13] != '-' ||
197 src[18] != '-' || src[23] != '-')
198 return false;
199
200 for (j = 0; j < 5; j++, src++) {
201 for (i = 0; i < size[j]; i++, src += 2, *dest++ = v) {
202 v = wmi_parse_hexbyte(src);
203 if (v < 0)
204 return false;
205 }
206 }
207
208 return true;
209}
210
Matthew Garrett1caab3c2009-11-04 14:17:53 -0500211/*
212 * Convert a raw GUID to the ACII string representation
213 */
214static int wmi_gtoa(const char *in, char *out)
215{
216 int i;
217
218 for (i = 3; i >= 0; i--)
219 out += sprintf(out, "%02X", in[i] & 0xFF);
220
221 out += sprintf(out, "-");
222 out += sprintf(out, "%02X", in[5] & 0xFF);
223 out += sprintf(out, "%02X", in[4] & 0xFF);
224 out += sprintf(out, "-");
225 out += sprintf(out, "%02X", in[7] & 0xFF);
226 out += sprintf(out, "%02X", in[6] & 0xFF);
227 out += sprintf(out, "-");
228 out += sprintf(out, "%02X", in[8] & 0xFF);
229 out += sprintf(out, "%02X", in[9] & 0xFF);
230 out += sprintf(out, "-");
231
232 for (i = 10; i <= 15; i++)
233 out += sprintf(out, "%02X", in[i] & 0xFF);
234
235 out = '\0';
236 return 0;
237}
238
Carlos Corbachobff431e2008-02-05 02:17:04 +0000239static bool find_guid(const char *guid_string, struct wmi_block **out)
240{
241 char tmp[16], guid_input[16];
242 struct wmi_block *wblock;
243 struct guid_block *block;
244 struct list_head *p;
245
246 wmi_parse_guid(guid_string, tmp);
247 wmi_swap_bytes(tmp, guid_input);
248
249 list_for_each(p, &wmi_blocks.list) {
250 wblock = list_entry(p, struct wmi_block, list);
251 block = &wblock->gblock;
252
253 if (memcmp(block->guid, guid_input, 16) == 0) {
254 if (out)
255 *out = wblock;
256 return 1;
257 }
258 }
259 return 0;
260}
261
Matthew Garretta66bfa72008-10-08 21:40:32 +0100262static acpi_status wmi_method_enable(struct wmi_block *wblock, int enable)
263{
264 struct guid_block *block = NULL;
265 char method[5];
266 struct acpi_object_list input;
267 union acpi_object params[1];
268 acpi_status status;
269 acpi_handle handle;
270
271 block = &wblock->gblock;
272 handle = wblock->handle;
273
274 if (!block)
275 return AE_NOT_EXIST;
276
277 input.count = 1;
278 input.pointer = params;
279 params[0].type = ACPI_TYPE_INTEGER;
280 params[0].integer.value = enable;
281
282 snprintf(method, 5, "WE%02X", block->notify_id);
283 status = acpi_evaluate_object(handle, method, &input, NULL);
284
285 if (status != AE_OK && status != AE_NOT_FOUND)
286 return status;
287 else
288 return AE_OK;
289}
290
Carlos Corbachobff431e2008-02-05 02:17:04 +0000291/*
292 * Exported WMI functions
293 */
294/**
295 * wmi_evaluate_method - Evaluate a WMI method
296 * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
297 * @instance: Instance index
298 * @method_id: Method ID to call
299 * &in: Buffer containing input for the method call
300 * &out: Empty buffer to return the method results
301 *
302 * Call an ACPI-WMI method
303 */
304acpi_status wmi_evaluate_method(const char *guid_string, u8 instance,
305u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out)
306{
307 struct guid_block *block = NULL;
308 struct wmi_block *wblock = NULL;
309 acpi_handle handle;
310 acpi_status status;
311 struct acpi_object_list input;
312 union acpi_object params[3];
Costantino Leandrof3d83e22009-08-26 14:29:28 -0700313 char method[5] = "WM";
Carlos Corbachobff431e2008-02-05 02:17:04 +0000314
315 if (!find_guid(guid_string, &wblock))
Lin Ming08237972008-08-08 11:57:11 +0800316 return AE_ERROR;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000317
318 block = &wblock->gblock;
319 handle = wblock->handle;
320
Al Viroe6bafba2008-02-13 04:03:25 +0000321 if (!(block->flags & ACPI_WMI_METHOD))
Carlos Corbachobff431e2008-02-05 02:17:04 +0000322 return AE_BAD_DATA;
323
324 if (block->instance_count < instance)
325 return AE_BAD_PARAMETER;
326
327 input.count = 2;
328 input.pointer = params;
329 params[0].type = ACPI_TYPE_INTEGER;
330 params[0].integer.value = instance;
331 params[1].type = ACPI_TYPE_INTEGER;
332 params[1].integer.value = method_id;
333
334 if (in) {
335 input.count = 3;
336
337 if (block->flags & ACPI_WMI_STRING) {
338 params[2].type = ACPI_TYPE_STRING;
339 } else {
340 params[2].type = ACPI_TYPE_BUFFER;
341 }
342 params[2].buffer.length = in->length;
343 params[2].buffer.pointer = in->pointer;
344 }
345
346 strncat(method, block->object_id, 2);
347
348 status = acpi_evaluate_object(handle, method, &input, out);
349
350 return status;
351}
352EXPORT_SYMBOL_GPL(wmi_evaluate_method);
353
354/**
355 * wmi_query_block - Return contents of a WMI block
356 * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
357 * @instance: Instance index
358 * &out: Empty buffer to return the contents of the data block to
359 *
360 * Return the contents of an ACPI-WMI data block to a buffer
361 */
362acpi_status wmi_query_block(const char *guid_string, u8 instance,
363struct acpi_buffer *out)
364{
365 struct guid_block *block = NULL;
366 struct wmi_block *wblock = NULL;
Carlos Corbachoa527f2d2008-02-24 13:34:34 +0000367 acpi_handle handle, wc_handle;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000368 acpi_status status, wc_status = AE_ERROR;
369 struct acpi_object_list input, wc_input;
370 union acpi_object wc_params[1], wq_params[1];
Costantino Leandrof3d83e22009-08-26 14:29:28 -0700371 char method[5];
372 char wc_method[5] = "WC";
Carlos Corbachobff431e2008-02-05 02:17:04 +0000373
374 if (!guid_string || !out)
375 return AE_BAD_PARAMETER;
376
377 if (!find_guid(guid_string, &wblock))
Lin Ming08237972008-08-08 11:57:11 +0800378 return AE_ERROR;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000379
380 block = &wblock->gblock;
381 handle = wblock->handle;
382
383 if (block->instance_count < instance)
384 return AE_BAD_PARAMETER;
385
386 /* Check GUID is a data block */
387 if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD))
Lin Ming08237972008-08-08 11:57:11 +0800388 return AE_ERROR;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000389
390 input.count = 1;
391 input.pointer = wq_params;
392 wq_params[0].type = ACPI_TYPE_INTEGER;
393 wq_params[0].integer.value = instance;
394
395 /*
396 * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method first to
397 * enable collection.
398 */
399 if (block->flags & ACPI_WMI_EXPENSIVE) {
400 wc_input.count = 1;
401 wc_input.pointer = wc_params;
402 wc_params[0].type = ACPI_TYPE_INTEGER;
403 wc_params[0].integer.value = 1;
404
405 strncat(wc_method, block->object_id, 2);
406
407 /*
408 * Some GUIDs break the specification by declaring themselves
409 * expensive, but have no corresponding WCxx method. So we
410 * should not fail if this happens.
411 */
Carlos Corbachoa527f2d2008-02-24 13:34:34 +0000412 wc_status = acpi_get_handle(handle, wc_method, &wc_handle);
413 if (ACPI_SUCCESS(wc_status))
414 wc_status = acpi_evaluate_object(handle, wc_method,
415 &wc_input, NULL);
Carlos Corbachobff431e2008-02-05 02:17:04 +0000416 }
417
418 strcpy(method, "WQ");
419 strncat(method, block->object_id, 2);
420
Carlos Corbachodab36ad2008-08-02 17:28:45 +0100421 status = acpi_evaluate_object(handle, method, &input, out);
Carlos Corbachobff431e2008-02-05 02:17:04 +0000422
423 /*
424 * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method, even if
425 * the WQxx method failed - we should disable collection anyway.
426 */
Carlos Corbachoa527f2d2008-02-24 13:34:34 +0000427 if ((block->flags & ACPI_WMI_EXPENSIVE) && ACPI_SUCCESS(wc_status)) {
Carlos Corbachobff431e2008-02-05 02:17:04 +0000428 wc_params[0].integer.value = 0;
429 status = acpi_evaluate_object(handle,
430 wc_method, &wc_input, NULL);
431 }
432
433 return status;
434}
435EXPORT_SYMBOL_GPL(wmi_query_block);
436
437/**
438 * wmi_set_block - Write to a WMI block
439 * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
440 * @instance: Instance index
441 * &in: Buffer containing new values for the data block
442 *
443 * Write the contents of the input buffer to an ACPI-WMI data block
444 */
445acpi_status wmi_set_block(const char *guid_string, u8 instance,
446const struct acpi_buffer *in)
447{
448 struct guid_block *block = NULL;
449 struct wmi_block *wblock = NULL;
450 acpi_handle handle;
451 struct acpi_object_list input;
452 union acpi_object params[2];
Costantino Leandrof3d83e22009-08-26 14:29:28 -0700453 char method[5] = "WS";
Carlos Corbachobff431e2008-02-05 02:17:04 +0000454
455 if (!guid_string || !in)
456 return AE_BAD_DATA;
457
458 if (!find_guid(guid_string, &wblock))
Lin Ming08237972008-08-08 11:57:11 +0800459 return AE_ERROR;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000460
461 block = &wblock->gblock;
462 handle = wblock->handle;
463
464 if (block->instance_count < instance)
465 return AE_BAD_PARAMETER;
466
467 /* Check GUID is a data block */
468 if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD))
Lin Ming08237972008-08-08 11:57:11 +0800469 return AE_ERROR;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000470
471 input.count = 2;
472 input.pointer = params;
473 params[0].type = ACPI_TYPE_INTEGER;
474 params[0].integer.value = instance;
475
476 if (block->flags & ACPI_WMI_STRING) {
477 params[1].type = ACPI_TYPE_STRING;
478 } else {
479 params[1].type = ACPI_TYPE_BUFFER;
480 }
481 params[1].buffer.length = in->length;
482 params[1].buffer.pointer = in->pointer;
483
484 strncat(method, block->object_id, 2);
485
486 return acpi_evaluate_object(handle, method, &input, NULL);
487}
488EXPORT_SYMBOL_GPL(wmi_set_block);
489
Thomas Renningera929aae2010-05-03 15:30:17 +0200490static void wmi_dump_wdg(struct guid_block *g)
491{
492 char guid_string[37];
493
494 wmi_gtoa(g->guid, guid_string);
495 printk(KERN_INFO PREFIX "%s:\n", guid_string);
496 printk(KERN_INFO PREFIX "\tobject_id: %c%c\n",
497 g->object_id[0], g->object_id[1]);
498 printk(KERN_INFO PREFIX "\tnotify_id: %02X\n", g->notify_id);
499 printk(KERN_INFO PREFIX "\treserved: %02X\n", g->reserved);
500 printk(KERN_INFO PREFIX "\tinstance_count: %d\n", g->instance_count);
501 printk(KERN_INFO PREFIX "\tflags: %#x", g->flags);
502 if (g->flags) {
503 printk(" ");
504 if (g->flags & ACPI_WMI_EXPENSIVE)
505 printk("ACPI_WMI_EXPENSIVE ");
506 if (g->flags & ACPI_WMI_METHOD)
507 printk("ACPI_WMI_METHOD ");
508 if (g->flags & ACPI_WMI_STRING)
509 printk("ACPI_WMI_STRING ");
510 if (g->flags & ACPI_WMI_EVENT)
511 printk("ACPI_WMI_EVENT ");
512 }
513 printk("\n");
514
515}
516
Thomas Renningerfc3155b2010-05-03 15:30:15 +0200517static void wmi_notify_debug(u32 value, void *context)
518{
519 struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
520 union acpi_object *obj;
521
522 wmi_get_event_data(value, &response);
523
524 obj = (union acpi_object *)response.pointer;
525
526 if (!obj)
527 return;
528
529 printk(KERN_INFO PREFIX "DEBUG Event ");
530 switch(obj->type) {
531 case ACPI_TYPE_BUFFER:
532 printk("BUFFER_TYPE - length %d\n", obj->buffer.length);
533 break;
534 case ACPI_TYPE_STRING:
535 printk("STRING_TYPE - %s\n", obj->string.pointer);
536 break;
537 case ACPI_TYPE_INTEGER:
538 printk("INTEGER_TYPE - %llu\n", obj->integer.value);
539 break;
540 case ACPI_TYPE_PACKAGE:
541 printk("PACKAGE_TYPE - %d elements\n", obj->package.count);
542 break;
543 default:
544 printk("object type 0x%X\n", obj->type);
545 }
546}
547
Carlos Corbachobff431e2008-02-05 02:17:04 +0000548/**
549 * wmi_install_notify_handler - Register handler for WMI events
550 * @handler: Function to handle notifications
551 * @data: Data to be returned to handler when event is fired
552 *
553 * Register a handler for events sent to the ACPI-WMI mapper device.
554 */
555acpi_status wmi_install_notify_handler(const char *guid,
556wmi_notify_handler handler, void *data)
557{
558 struct wmi_block *block;
Matthew Garretta66bfa72008-10-08 21:40:32 +0100559 acpi_status status;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000560
561 if (!guid || !handler)
562 return AE_BAD_PARAMETER;
563
Paul Rollandc03b26a2009-12-30 01:07:40 -0500564 if (!find_guid(guid, &block))
Carlos Corbachobff431e2008-02-05 02:17:04 +0000565 return AE_NOT_EXIST;
566
Thomas Renningerfc3155b2010-05-03 15:30:15 +0200567 if (block->handler && block->handler != wmi_notify_debug)
Carlos Corbachobff431e2008-02-05 02:17:04 +0000568 return AE_ALREADY_ACQUIRED;
569
570 block->handler = handler;
571 block->handler_data = data;
572
Matthew Garretta66bfa72008-10-08 21:40:32 +0100573 status = wmi_method_enable(block, 1);
574
575 return status;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000576}
577EXPORT_SYMBOL_GPL(wmi_install_notify_handler);
578
579/**
580 * wmi_uninstall_notify_handler - Unregister handler for WMI events
581 *
582 * Unregister handler for events sent to the ACPI-WMI mapper device.
583 */
584acpi_status wmi_remove_notify_handler(const char *guid)
585{
586 struct wmi_block *block;
Thomas Renningerfc3155b2010-05-03 15:30:15 +0200587 acpi_status status = AE_OK;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000588
589 if (!guid)
590 return AE_BAD_PARAMETER;
591
Paul Rollandc03b26a2009-12-30 01:07:40 -0500592 if (!find_guid(guid, &block))
Carlos Corbachobff431e2008-02-05 02:17:04 +0000593 return AE_NOT_EXIST;
594
Thomas Renningerfc3155b2010-05-03 15:30:15 +0200595 if (!block->handler || block->handler == wmi_notify_debug)
Carlos Corbachobff431e2008-02-05 02:17:04 +0000596 return AE_NULL_ENTRY;
597
Thomas Renningerfc3155b2010-05-03 15:30:15 +0200598 if (debug_event) {
599 block->handler = wmi_notify_debug;
600 } else {
601 status = wmi_method_enable(block, 0);
602 block->handler = NULL;
603 block->handler_data = NULL;
604 }
Matthew Garretta66bfa72008-10-08 21:40:32 +0100605 return status;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000606}
607EXPORT_SYMBOL_GPL(wmi_remove_notify_handler);
608
609/**
610 * wmi_get_event_data - Get WMI data associated with an event
611 *
Anisse Astier3e9b9882009-12-04 10:10:09 +0100612 * @event: Event to find
613 * @out: Buffer to hold event data. out->pointer should be freed with kfree()
Carlos Corbachobff431e2008-02-05 02:17:04 +0000614 *
615 * Returns extra data associated with an event in WMI.
616 */
617acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out)
618{
619 struct acpi_object_list input;
620 union acpi_object params[1];
621 struct guid_block *gblock;
622 struct wmi_block *wblock;
623 struct list_head *p;
624
625 input.count = 1;
626 input.pointer = params;
627 params[0].type = ACPI_TYPE_INTEGER;
628 params[0].integer.value = event;
629
630 list_for_each(p, &wmi_blocks.list) {
631 wblock = list_entry(p, struct wmi_block, list);
632 gblock = &wblock->gblock;
633
634 if ((gblock->flags & ACPI_WMI_EVENT) &&
635 (gblock->notify_id == event))
636 return acpi_evaluate_object(wblock->handle, "_WED",
637 &input, out);
638 }
639
640 return AE_NOT_FOUND;
641}
642EXPORT_SYMBOL_GPL(wmi_get_event_data);
643
644/**
645 * wmi_has_guid - Check if a GUID is available
646 * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
647 *
648 * Check if a given GUID is defined by _WDG
649 */
650bool wmi_has_guid(const char *guid_string)
651{
652 return find_guid(guid_string, NULL);
653}
654EXPORT_SYMBOL_GPL(wmi_has_guid);
655
656/*
Matthew Garrett1caab3c2009-11-04 14:17:53 -0500657 * sysfs interface
658 */
659static ssize_t show_modalias(struct device *dev, struct device_attribute *attr,
660 char *buf)
661{
662 char guid_string[37];
663 struct wmi_block *wblock;
664
665 wblock = dev_get_drvdata(dev);
666 if (!wblock)
667 return -ENOMEM;
668
669 wmi_gtoa(wblock->gblock.guid, guid_string);
670
671 return sprintf(buf, "wmi:%s\n", guid_string);
672}
673static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL);
674
675static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env)
676{
677 char guid_string[37];
678
679 struct wmi_block *wblock;
680
681 if (add_uevent_var(env, "MODALIAS="))
682 return -ENOMEM;
683
684 wblock = dev_get_drvdata(dev);
685 if (!wblock)
686 return -ENOMEM;
687
688 wmi_gtoa(wblock->gblock.guid, guid_string);
689
690 strcpy(&env->buf[env->buflen - 1], "wmi:");
691 memcpy(&env->buf[env->buflen - 1 + 4], guid_string, 36);
692 env->buflen += 40;
693
694 return 0;
695}
696
697static void wmi_dev_free(struct device *dev)
698{
699 kfree(dev);
700}
701
702static struct class wmi_class = {
703 .name = "wmi",
704 .dev_release = wmi_dev_free,
705 .dev_uevent = wmi_dev_uevent,
706};
707
708static int wmi_create_devs(void)
709{
710 int result;
711 char guid_string[37];
712 struct guid_block *gblock;
713 struct wmi_block *wblock;
714 struct list_head *p;
715 struct device *guid_dev;
716
717 /* Create devices for all the GUIDs */
718 list_for_each(p, &wmi_blocks.list) {
719 wblock = list_entry(p, struct wmi_block, list);
720
721 guid_dev = kzalloc(sizeof(struct device), GFP_KERNEL);
722 if (!guid_dev)
723 return -ENOMEM;
724
725 wblock->dev = guid_dev;
726
727 guid_dev->class = &wmi_class;
728 dev_set_drvdata(guid_dev, wblock);
729
730 gblock = &wblock->gblock;
731
732 wmi_gtoa(gblock->guid, guid_string);
733 dev_set_name(guid_dev, guid_string);
734
735 result = device_register(guid_dev);
736 if (result)
737 return result;
738
739 result = device_create_file(guid_dev, &dev_attr_modalias);
740 if (result)
741 return result;
742 }
743
744 return 0;
745}
746
747static void wmi_remove_devs(void)
748{
749 struct guid_block *gblock;
750 struct wmi_block *wblock;
751 struct list_head *p;
752 struct device *guid_dev;
753
754 /* Delete devices for all the GUIDs */
755 list_for_each(p, &wmi_blocks.list) {
756 wblock = list_entry(p, struct wmi_block, list);
757
758 guid_dev = wblock->dev;
759 gblock = &wblock->gblock;
760
761 device_remove_file(guid_dev, &dev_attr_modalias);
762
763 device_unregister(guid_dev);
764 }
765}
766
767static void wmi_class_exit(void)
768{
769 wmi_remove_devs();
770 class_unregister(&wmi_class);
771}
772
773static int wmi_class_init(void)
774{
775 int ret;
776
777 ret = class_register(&wmi_class);
778 if (ret)
779 return ret;
780
781 ret = wmi_create_devs();
782 if (ret)
783 wmi_class_exit();
784
785 return ret;
786}
787
Carlos Corbachod1f9e492009-12-26 19:14:59 +0000788static bool guid_already_parsed(const char *guid_string)
789{
790 struct guid_block *gblock;
791 struct wmi_block *wblock;
792 struct list_head *p;
793
794 list_for_each(p, &wmi_blocks.list) {
795 wblock = list_entry(p, struct wmi_block, list);
796 gblock = &wblock->gblock;
797
798 if (strncmp(gblock->guid, guid_string, 16) == 0)
799 return true;
800 }
801 return false;
802}
803
Matthew Garrett1caab3c2009-11-04 14:17:53 -0500804/*
Carlos Corbachobff431e2008-02-05 02:17:04 +0000805 * Parse the _WDG method for the GUID data blocks
806 */
807static __init acpi_status parse_wdg(acpi_handle handle)
808{
809 struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
810 union acpi_object *obj;
811 struct guid_block *gblock;
812 struct wmi_block *wblock;
Carlos Corbachod1f9e492009-12-26 19:14:59 +0000813 char guid_string[37];
Carlos Corbachobff431e2008-02-05 02:17:04 +0000814 acpi_status status;
815 u32 i, total;
816
817 status = acpi_evaluate_object(handle, "_WDG", NULL, &out);
818
819 if (ACPI_FAILURE(status))
820 return status;
821
822 obj = (union acpi_object *) out.pointer;
823
824 if (obj->type != ACPI_TYPE_BUFFER)
825 return AE_ERROR;
826
827 total = obj->buffer.length / sizeof(struct guid_block);
828
Julia Lawall2c6719a2010-05-15 23:22:18 +0200829 gblock = kmemdup(obj->buffer.pointer, obj->buffer.length, GFP_KERNEL);
Axel Lina5167c52010-06-03 11:45:45 +0800830 if (!gblock) {
831 status = AE_NO_MEMORY;
832 goto out_free_pointer;
833 }
Carlos Corbachobff431e2008-02-05 02:17:04 +0000834
Carlos Corbachobff431e2008-02-05 02:17:04 +0000835 for (i = 0; i < total; i++) {
Carlos Corbachod1f9e492009-12-26 19:14:59 +0000836 /*
837 Some WMI devices, like those for nVidia hooks, have a
838 duplicate GUID. It's not clear what we should do in this
839 case yet, so for now, we'll just ignore the duplicate.
840 Anyone who wants to add support for that device can come
841 up with a better workaround for the mess then.
842 */
843 if (guid_already_parsed(gblock[i].guid) == true) {
844 wmi_gtoa(gblock[i].guid, guid_string);
845 printk(KERN_INFO PREFIX "Skipping duplicate GUID %s\n",
846 guid_string);
847 continue;
848 }
Thomas Renningera929aae2010-05-03 15:30:17 +0200849 if (debug_dump_wdg)
850 wmi_dump_wdg(&gblock[i]);
851
Carlos Corbachobff431e2008-02-05 02:17:04 +0000852 wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL);
Axel Lina5167c52010-06-03 11:45:45 +0800853 if (!wblock) {
854 status = AE_NO_MEMORY;
855 goto out_free_gblock;
856 }
Carlos Corbachobff431e2008-02-05 02:17:04 +0000857
858 wblock->gblock = gblock[i];
859 wblock->handle = handle;
Thomas Renningerfc3155b2010-05-03 15:30:15 +0200860 if (debug_event) {
861 wblock->handler = wmi_notify_debug;
862 status = wmi_method_enable(wblock, 1);
863 }
Carlos Corbachobff431e2008-02-05 02:17:04 +0000864 list_add_tail(&wblock->list, &wmi_blocks.list);
865 }
866
Axel Lina5167c52010-06-03 11:45:45 +0800867out_free_gblock:
Carlos Corbachobff431e2008-02-05 02:17:04 +0000868 kfree(gblock);
Axel Lina5167c52010-06-03 11:45:45 +0800869out_free_pointer:
870 kfree(out.pointer);
Carlos Corbachobff431e2008-02-05 02:17:04 +0000871
872 return status;
873}
874
875/*
876 * WMI can have EmbeddedControl access regions. In which case, we just want to
877 * hand these off to the EC driver.
878 */
879static acpi_status
880acpi_wmi_ec_space_handler(u32 function, acpi_physical_address address,
Lin Ming439913f2010-01-28 10:53:19 +0800881 u32 bits, u64 *value,
Carlos Corbachobff431e2008-02-05 02:17:04 +0000882 void *handler_context, void *region_context)
883{
884 int result = 0, i = 0;
885 u8 temp = 0;
886
887 if ((address > 0xFF) || !value)
888 return AE_BAD_PARAMETER;
889
890 if (function != ACPI_READ && function != ACPI_WRITE)
891 return AE_BAD_PARAMETER;
892
893 if (bits != 8)
894 return AE_BAD_PARAMETER;
895
896 if (function == ACPI_READ) {
897 result = ec_read(address, &temp);
Lin Ming439913f2010-01-28 10:53:19 +0800898 (*value) |= ((u64)temp) << i;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000899 } else {
900 temp = 0xff & ((*value) >> i);
901 result = ec_write(address, temp);
902 }
903
904 switch (result) {
905 case -EINVAL:
906 return AE_BAD_PARAMETER;
907 break;
908 case -ENODEV:
909 return AE_NOT_FOUND;
910 break;
911 case -ETIME:
912 return AE_TIME;
913 break;
914 default:
915 return AE_OK;
916 }
917}
918
Bjorn Helgaasf61bb932009-04-07 15:37:37 +0000919static void acpi_wmi_notify(struct acpi_device *device, u32 event)
Carlos Corbachobff431e2008-02-05 02:17:04 +0000920{
921 struct guid_block *block;
922 struct wmi_block *wblock;
923 struct list_head *p;
Thomas Renninger7715348c2010-05-03 15:30:16 +0200924 char guid_string[37];
Carlos Corbachobff431e2008-02-05 02:17:04 +0000925
926 list_for_each(p, &wmi_blocks.list) {
927 wblock = list_entry(p, struct wmi_block, list);
928 block = &wblock->gblock;
929
930 if ((block->flags & ACPI_WMI_EVENT) &&
931 (block->notify_id == event)) {
932 if (wblock->handler)
933 wblock->handler(event, wblock->handler_data);
Thomas Renninger7715348c2010-05-03 15:30:16 +0200934 if (debug_event) {
935 wmi_gtoa(wblock->gblock.guid, guid_string);
936 printk(KERN_INFO PREFIX "DEBUG Event GUID:"
937 " %s\n", guid_string);
938 }
Carlos Corbachobff431e2008-02-05 02:17:04 +0000939
940 acpi_bus_generate_netlink_event(
Kay Sievers07944692008-10-30 01:18:59 +0100941 device->pnp.device_class, dev_name(&device->dev),
Carlos Corbachobff431e2008-02-05 02:17:04 +0000942 event, 0);
943 break;
944 }
945 }
946}
947
948static int acpi_wmi_remove(struct acpi_device *device, int type)
949{
Carlos Corbachobff431e2008-02-05 02:17:04 +0000950 acpi_remove_address_space_handler(device->handle,
951 ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler);
952
953 return 0;
954}
955
956static int __init acpi_wmi_add(struct acpi_device *device)
957{
958 acpi_status status;
959 int result = 0;
960
Carlos Corbachobff431e2008-02-05 02:17:04 +0000961 status = acpi_install_address_space_handler(device->handle,
962 ACPI_ADR_SPACE_EC,
963 &acpi_wmi_ec_space_handler,
964 NULL, NULL);
965 if (ACPI_FAILURE(status))
966 return -ENODEV;
967
968 status = parse_wdg(device->handle);
969 if (ACPI_FAILURE(status)) {
970 printk(KERN_ERR PREFIX "Error installing EC region handler\n");
971 return -ENODEV;
972 }
973
974 return result;
975}
976
977static int __init acpi_wmi_init(void)
978{
Roel Kluinda511992009-03-04 11:55:30 -0800979 int result;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000980
Linus Torvalds96b5a462008-02-11 20:52:01 -0800981 INIT_LIST_HEAD(&wmi_blocks.list);
982
Carlos Corbachobff431e2008-02-05 02:17:04 +0000983 if (acpi_disabled)
984 return -ENODEV;
985
Carlos Corbachobff431e2008-02-05 02:17:04 +0000986 result = acpi_bus_register_driver(&acpi_wmi_driver);
987
988 if (result < 0) {
989 printk(KERN_INFO PREFIX "Error loading mapper\n");
Matthew Garrett1caab3c2009-11-04 14:17:53 -0500990 return -ENODEV;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000991 }
992
Matthew Garrett1caab3c2009-11-04 14:17:53 -0500993 result = wmi_class_init();
994 if (result) {
995 acpi_bus_unregister_driver(&acpi_wmi_driver);
996 return result;
997 }
998
999 printk(KERN_INFO PREFIX "Mapper loaded\n");
1000
Carlos Corbachobff431e2008-02-05 02:17:04 +00001001 return result;
1002}
1003
1004static void __exit acpi_wmi_exit(void)
1005{
1006 struct list_head *p, *tmp;
1007 struct wmi_block *wblock;
1008
Matthew Garrett1caab3c2009-11-04 14:17:53 -05001009 wmi_class_exit();
1010
Carlos Corbachobff431e2008-02-05 02:17:04 +00001011 acpi_bus_unregister_driver(&acpi_wmi_driver);
1012
1013 list_for_each_safe(p, tmp, &wmi_blocks.list) {
1014 wblock = list_entry(p, struct wmi_block, list);
1015
1016 list_del(p);
1017 kfree(wblock);
1018 }
1019
1020 printk(KERN_INFO PREFIX "Mapper unloaded\n");
1021}
1022
1023subsys_initcall(acpi_wmi_init);
1024module_exit(acpi_wmi_exit);