blob: 6c15720be6a42d8c89ff1bc27f3cbfb2e68150a9 [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;
Axel Lin1492616a2010-06-28 09:30:45 +0800521 acpi_status status;
Thomas Renningerfc3155b2010-05-03 15:30:15 +0200522
Axel Lin1492616a2010-06-28 09:30:45 +0800523 status = wmi_get_event_data(value, &response);
524 if (status != AE_OK) {
525 printk(KERN_INFO "wmi: bad event status 0x%x\n", status);
526 return;
527 }
Thomas Renningerfc3155b2010-05-03 15:30:15 +0200528
529 obj = (union acpi_object *)response.pointer;
530
531 if (!obj)
532 return;
533
534 printk(KERN_INFO PREFIX "DEBUG Event ");
535 switch(obj->type) {
536 case ACPI_TYPE_BUFFER:
537 printk("BUFFER_TYPE - length %d\n", obj->buffer.length);
538 break;
539 case ACPI_TYPE_STRING:
540 printk("STRING_TYPE - %s\n", obj->string.pointer);
541 break;
542 case ACPI_TYPE_INTEGER:
543 printk("INTEGER_TYPE - %llu\n", obj->integer.value);
544 break;
545 case ACPI_TYPE_PACKAGE:
546 printk("PACKAGE_TYPE - %d elements\n", obj->package.count);
547 break;
548 default:
549 printk("object type 0x%X\n", obj->type);
550 }
Axel Lin1492616a2010-06-28 09:30:45 +0800551 kfree(obj);
Thomas Renningerfc3155b2010-05-03 15:30:15 +0200552}
553
Carlos Corbachobff431e2008-02-05 02:17:04 +0000554/**
555 * wmi_install_notify_handler - Register handler for WMI events
556 * @handler: Function to handle notifications
557 * @data: Data to be returned to handler when event is fired
558 *
559 * Register a handler for events sent to the ACPI-WMI mapper device.
560 */
561acpi_status wmi_install_notify_handler(const char *guid,
562wmi_notify_handler handler, void *data)
563{
564 struct wmi_block *block;
Matthew Garretta66bfa72008-10-08 21:40:32 +0100565 acpi_status status;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000566
567 if (!guid || !handler)
568 return AE_BAD_PARAMETER;
569
Paul Rollandc03b26a2009-12-30 01:07:40 -0500570 if (!find_guid(guid, &block))
Carlos Corbachobff431e2008-02-05 02:17:04 +0000571 return AE_NOT_EXIST;
572
Thomas Renningerfc3155b2010-05-03 15:30:15 +0200573 if (block->handler && block->handler != wmi_notify_debug)
Carlos Corbachobff431e2008-02-05 02:17:04 +0000574 return AE_ALREADY_ACQUIRED;
575
576 block->handler = handler;
577 block->handler_data = data;
578
Matthew Garretta66bfa72008-10-08 21:40:32 +0100579 status = wmi_method_enable(block, 1);
580
581 return status;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000582}
583EXPORT_SYMBOL_GPL(wmi_install_notify_handler);
584
585/**
586 * wmi_uninstall_notify_handler - Unregister handler for WMI events
587 *
588 * Unregister handler for events sent to the ACPI-WMI mapper device.
589 */
590acpi_status wmi_remove_notify_handler(const char *guid)
591{
592 struct wmi_block *block;
Thomas Renningerfc3155b2010-05-03 15:30:15 +0200593 acpi_status status = AE_OK;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000594
595 if (!guid)
596 return AE_BAD_PARAMETER;
597
Paul Rollandc03b26a2009-12-30 01:07:40 -0500598 if (!find_guid(guid, &block))
Carlos Corbachobff431e2008-02-05 02:17:04 +0000599 return AE_NOT_EXIST;
600
Thomas Renningerfc3155b2010-05-03 15:30:15 +0200601 if (!block->handler || block->handler == wmi_notify_debug)
Carlos Corbachobff431e2008-02-05 02:17:04 +0000602 return AE_NULL_ENTRY;
603
Thomas Renningerfc3155b2010-05-03 15:30:15 +0200604 if (debug_event) {
605 block->handler = wmi_notify_debug;
606 } else {
607 status = wmi_method_enable(block, 0);
608 block->handler = NULL;
609 block->handler_data = NULL;
610 }
Matthew Garretta66bfa72008-10-08 21:40:32 +0100611 return status;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000612}
613EXPORT_SYMBOL_GPL(wmi_remove_notify_handler);
614
615/**
616 * wmi_get_event_data - Get WMI data associated with an event
617 *
Anisse Astier3e9b9882009-12-04 10:10:09 +0100618 * @event: Event to find
619 * @out: Buffer to hold event data. out->pointer should be freed with kfree()
Carlos Corbachobff431e2008-02-05 02:17:04 +0000620 *
621 * Returns extra data associated with an event in WMI.
622 */
623acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out)
624{
625 struct acpi_object_list input;
626 union acpi_object params[1];
627 struct guid_block *gblock;
628 struct wmi_block *wblock;
629 struct list_head *p;
630
631 input.count = 1;
632 input.pointer = params;
633 params[0].type = ACPI_TYPE_INTEGER;
634 params[0].integer.value = event;
635
636 list_for_each(p, &wmi_blocks.list) {
637 wblock = list_entry(p, struct wmi_block, list);
638 gblock = &wblock->gblock;
639
640 if ((gblock->flags & ACPI_WMI_EVENT) &&
641 (gblock->notify_id == event))
642 return acpi_evaluate_object(wblock->handle, "_WED",
643 &input, out);
644 }
645
646 return AE_NOT_FOUND;
647}
648EXPORT_SYMBOL_GPL(wmi_get_event_data);
649
650/**
651 * wmi_has_guid - Check if a GUID is available
652 * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
653 *
654 * Check if a given GUID is defined by _WDG
655 */
656bool wmi_has_guid(const char *guid_string)
657{
658 return find_guid(guid_string, NULL);
659}
660EXPORT_SYMBOL_GPL(wmi_has_guid);
661
662/*
Matthew Garrett1caab3c2009-11-04 14:17:53 -0500663 * sysfs interface
664 */
665static ssize_t show_modalias(struct device *dev, struct device_attribute *attr,
666 char *buf)
667{
668 char guid_string[37];
669 struct wmi_block *wblock;
670
671 wblock = dev_get_drvdata(dev);
672 if (!wblock)
673 return -ENOMEM;
674
675 wmi_gtoa(wblock->gblock.guid, guid_string);
676
677 return sprintf(buf, "wmi:%s\n", guid_string);
678}
679static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL);
680
681static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env)
682{
683 char guid_string[37];
684
685 struct wmi_block *wblock;
686
687 if (add_uevent_var(env, "MODALIAS="))
688 return -ENOMEM;
689
690 wblock = dev_get_drvdata(dev);
691 if (!wblock)
692 return -ENOMEM;
693
694 wmi_gtoa(wblock->gblock.guid, guid_string);
695
696 strcpy(&env->buf[env->buflen - 1], "wmi:");
697 memcpy(&env->buf[env->buflen - 1 + 4], guid_string, 36);
698 env->buflen += 40;
699
700 return 0;
701}
702
703static void wmi_dev_free(struct device *dev)
704{
705 kfree(dev);
706}
707
708static struct class wmi_class = {
709 .name = "wmi",
710 .dev_release = wmi_dev_free,
711 .dev_uevent = wmi_dev_uevent,
712};
713
714static int wmi_create_devs(void)
715{
716 int result;
717 char guid_string[37];
718 struct guid_block *gblock;
719 struct wmi_block *wblock;
720 struct list_head *p;
721 struct device *guid_dev;
722
723 /* Create devices for all the GUIDs */
724 list_for_each(p, &wmi_blocks.list) {
725 wblock = list_entry(p, struct wmi_block, list);
726
727 guid_dev = kzalloc(sizeof(struct device), GFP_KERNEL);
728 if (!guid_dev)
729 return -ENOMEM;
730
731 wblock->dev = guid_dev;
732
733 guid_dev->class = &wmi_class;
734 dev_set_drvdata(guid_dev, wblock);
735
736 gblock = &wblock->gblock;
737
738 wmi_gtoa(gblock->guid, guid_string);
739 dev_set_name(guid_dev, guid_string);
740
741 result = device_register(guid_dev);
742 if (result)
743 return result;
744
745 result = device_create_file(guid_dev, &dev_attr_modalias);
746 if (result)
747 return result;
748 }
749
750 return 0;
751}
752
753static void wmi_remove_devs(void)
754{
755 struct guid_block *gblock;
756 struct wmi_block *wblock;
757 struct list_head *p;
758 struct device *guid_dev;
759
760 /* Delete devices for all the GUIDs */
761 list_for_each(p, &wmi_blocks.list) {
762 wblock = list_entry(p, struct wmi_block, list);
763
764 guid_dev = wblock->dev;
765 gblock = &wblock->gblock;
766
767 device_remove_file(guid_dev, &dev_attr_modalias);
768
769 device_unregister(guid_dev);
770 }
771}
772
773static void wmi_class_exit(void)
774{
775 wmi_remove_devs();
776 class_unregister(&wmi_class);
777}
778
779static int wmi_class_init(void)
780{
781 int ret;
782
783 ret = class_register(&wmi_class);
784 if (ret)
785 return ret;
786
787 ret = wmi_create_devs();
788 if (ret)
789 wmi_class_exit();
790
791 return ret;
792}
793
Carlos Corbachod1f9e492009-12-26 19:14:59 +0000794static bool guid_already_parsed(const char *guid_string)
795{
796 struct guid_block *gblock;
797 struct wmi_block *wblock;
798 struct list_head *p;
799
800 list_for_each(p, &wmi_blocks.list) {
801 wblock = list_entry(p, struct wmi_block, list);
802 gblock = &wblock->gblock;
803
804 if (strncmp(gblock->guid, guid_string, 16) == 0)
805 return true;
806 }
807 return false;
808}
809
Matthew Garrett1caab3c2009-11-04 14:17:53 -0500810/*
Carlos Corbachobff431e2008-02-05 02:17:04 +0000811 * Parse the _WDG method for the GUID data blocks
812 */
813static __init acpi_status parse_wdg(acpi_handle handle)
814{
815 struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
816 union acpi_object *obj;
817 struct guid_block *gblock;
818 struct wmi_block *wblock;
Carlos Corbachod1f9e492009-12-26 19:14:59 +0000819 char guid_string[37];
Carlos Corbachobff431e2008-02-05 02:17:04 +0000820 acpi_status status;
821 u32 i, total;
822
823 status = acpi_evaluate_object(handle, "_WDG", NULL, &out);
824
825 if (ACPI_FAILURE(status))
826 return status;
827
828 obj = (union acpi_object *) out.pointer;
829
830 if (obj->type != ACPI_TYPE_BUFFER)
831 return AE_ERROR;
832
833 total = obj->buffer.length / sizeof(struct guid_block);
834
Julia Lawall2c6719a2010-05-15 23:22:18 +0200835 gblock = kmemdup(obj->buffer.pointer, obj->buffer.length, GFP_KERNEL);
Axel Lina5167c52010-06-03 11:45:45 +0800836 if (!gblock) {
837 status = AE_NO_MEMORY;
838 goto out_free_pointer;
839 }
Carlos Corbachobff431e2008-02-05 02:17:04 +0000840
Carlos Corbachobff431e2008-02-05 02:17:04 +0000841 for (i = 0; i < total; i++) {
Carlos Corbachod1f9e492009-12-26 19:14:59 +0000842 /*
843 Some WMI devices, like those for nVidia hooks, have a
844 duplicate GUID. It's not clear what we should do in this
845 case yet, so for now, we'll just ignore the duplicate.
846 Anyone who wants to add support for that device can come
847 up with a better workaround for the mess then.
848 */
849 if (guid_already_parsed(gblock[i].guid) == true) {
850 wmi_gtoa(gblock[i].guid, guid_string);
851 printk(KERN_INFO PREFIX "Skipping duplicate GUID %s\n",
852 guid_string);
853 continue;
854 }
Thomas Renningera929aae2010-05-03 15:30:17 +0200855 if (debug_dump_wdg)
856 wmi_dump_wdg(&gblock[i]);
857
Carlos Corbachobff431e2008-02-05 02:17:04 +0000858 wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL);
Axel Lina5167c52010-06-03 11:45:45 +0800859 if (!wblock) {
860 status = AE_NO_MEMORY;
861 goto out_free_gblock;
862 }
Carlos Corbachobff431e2008-02-05 02:17:04 +0000863
864 wblock->gblock = gblock[i];
865 wblock->handle = handle;
Thomas Renningerfc3155b2010-05-03 15:30:15 +0200866 if (debug_event) {
867 wblock->handler = wmi_notify_debug;
868 status = wmi_method_enable(wblock, 1);
869 }
Carlos Corbachobff431e2008-02-05 02:17:04 +0000870 list_add_tail(&wblock->list, &wmi_blocks.list);
871 }
872
Axel Lina5167c52010-06-03 11:45:45 +0800873out_free_gblock:
Carlos Corbachobff431e2008-02-05 02:17:04 +0000874 kfree(gblock);
Axel Lina5167c52010-06-03 11:45:45 +0800875out_free_pointer:
876 kfree(out.pointer);
Carlos Corbachobff431e2008-02-05 02:17:04 +0000877
878 return status;
879}
880
881/*
882 * WMI can have EmbeddedControl access regions. In which case, we just want to
883 * hand these off to the EC driver.
884 */
885static acpi_status
886acpi_wmi_ec_space_handler(u32 function, acpi_physical_address address,
Lin Ming439913f2010-01-28 10:53:19 +0800887 u32 bits, u64 *value,
Carlos Corbachobff431e2008-02-05 02:17:04 +0000888 void *handler_context, void *region_context)
889{
890 int result = 0, i = 0;
891 u8 temp = 0;
892
893 if ((address > 0xFF) || !value)
894 return AE_BAD_PARAMETER;
895
896 if (function != ACPI_READ && function != ACPI_WRITE)
897 return AE_BAD_PARAMETER;
898
899 if (bits != 8)
900 return AE_BAD_PARAMETER;
901
902 if (function == ACPI_READ) {
903 result = ec_read(address, &temp);
Lin Ming439913f2010-01-28 10:53:19 +0800904 (*value) |= ((u64)temp) << i;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000905 } else {
906 temp = 0xff & ((*value) >> i);
907 result = ec_write(address, temp);
908 }
909
910 switch (result) {
911 case -EINVAL:
912 return AE_BAD_PARAMETER;
913 break;
914 case -ENODEV:
915 return AE_NOT_FOUND;
916 break;
917 case -ETIME:
918 return AE_TIME;
919 break;
920 default:
921 return AE_OK;
922 }
923}
924
Bjorn Helgaasf61bb932009-04-07 15:37:37 +0000925static void acpi_wmi_notify(struct acpi_device *device, u32 event)
Carlos Corbachobff431e2008-02-05 02:17:04 +0000926{
927 struct guid_block *block;
928 struct wmi_block *wblock;
929 struct list_head *p;
Thomas Renninger7715348c2010-05-03 15:30:16 +0200930 char guid_string[37];
Carlos Corbachobff431e2008-02-05 02:17:04 +0000931
932 list_for_each(p, &wmi_blocks.list) {
933 wblock = list_entry(p, struct wmi_block, list);
934 block = &wblock->gblock;
935
936 if ((block->flags & ACPI_WMI_EVENT) &&
937 (block->notify_id == event)) {
938 if (wblock->handler)
939 wblock->handler(event, wblock->handler_data);
Thomas Renninger7715348c2010-05-03 15:30:16 +0200940 if (debug_event) {
941 wmi_gtoa(wblock->gblock.guid, guid_string);
942 printk(KERN_INFO PREFIX "DEBUG Event GUID:"
943 " %s\n", guid_string);
944 }
Carlos Corbachobff431e2008-02-05 02:17:04 +0000945
946 acpi_bus_generate_netlink_event(
Kay Sievers07944692008-10-30 01:18:59 +0100947 device->pnp.device_class, dev_name(&device->dev),
Carlos Corbachobff431e2008-02-05 02:17:04 +0000948 event, 0);
949 break;
950 }
951 }
952}
953
954static int acpi_wmi_remove(struct acpi_device *device, int type)
955{
Carlos Corbachobff431e2008-02-05 02:17:04 +0000956 acpi_remove_address_space_handler(device->handle,
957 ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler);
958
959 return 0;
960}
961
962static int __init acpi_wmi_add(struct acpi_device *device)
963{
964 acpi_status status;
965 int result = 0;
966
Carlos Corbachobff431e2008-02-05 02:17:04 +0000967 status = acpi_install_address_space_handler(device->handle,
968 ACPI_ADR_SPACE_EC,
969 &acpi_wmi_ec_space_handler,
970 NULL, NULL);
971 if (ACPI_FAILURE(status))
972 return -ENODEV;
973
974 status = parse_wdg(device->handle);
975 if (ACPI_FAILURE(status)) {
976 printk(KERN_ERR PREFIX "Error installing EC region handler\n");
977 return -ENODEV;
978 }
979
980 return result;
981}
982
983static int __init acpi_wmi_init(void)
984{
Roel Kluinda511992009-03-04 11:55:30 -0800985 int result;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000986
Linus Torvalds96b5a462008-02-11 20:52:01 -0800987 INIT_LIST_HEAD(&wmi_blocks.list);
988
Carlos Corbachobff431e2008-02-05 02:17:04 +0000989 if (acpi_disabled)
990 return -ENODEV;
991
Carlos Corbachobff431e2008-02-05 02:17:04 +0000992 result = acpi_bus_register_driver(&acpi_wmi_driver);
993
994 if (result < 0) {
995 printk(KERN_INFO PREFIX "Error loading mapper\n");
Matthew Garrett1caab3c2009-11-04 14:17:53 -0500996 return -ENODEV;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000997 }
998
Matthew Garrett1caab3c2009-11-04 14:17:53 -0500999 result = wmi_class_init();
1000 if (result) {
1001 acpi_bus_unregister_driver(&acpi_wmi_driver);
1002 return result;
1003 }
1004
1005 printk(KERN_INFO PREFIX "Mapper loaded\n");
1006
Carlos Corbachobff431e2008-02-05 02:17:04 +00001007 return result;
1008}
1009
1010static void __exit acpi_wmi_exit(void)
1011{
1012 struct list_head *p, *tmp;
1013 struct wmi_block *wblock;
1014
Matthew Garrett1caab3c2009-11-04 14:17:53 -05001015 wmi_class_exit();
1016
Carlos Corbachobff431e2008-02-05 02:17:04 +00001017 acpi_bus_unregister_driver(&acpi_wmi_driver);
1018
1019 list_for_each_safe(p, tmp, &wmi_blocks.list) {
1020 wblock = list_entry(p, struct wmi_block, list);
1021
1022 list_del(p);
1023 kfree(wblock);
1024 }
1025
1026 printk(KERN_INFO PREFIX "Mapper unloaded\n");
1027}
1028
1029subsys_initcall(acpi_wmi_init);
1030module_exit(acpi_wmi_exit);