blob: e820f4cddb618e0dd46eca245538fcfd17cf2bca [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
Carlos Corbachobff431e2008-02-05 02:17:04 +000089static int acpi_wmi_remove(struct acpi_device *device, int type);
90static int acpi_wmi_add(struct acpi_device *device);
Bjorn Helgaasf61bb932009-04-07 15:37:37 +000091static void acpi_wmi_notify(struct acpi_device *device, u32 event);
Carlos Corbachobff431e2008-02-05 02:17:04 +000092
93static const struct acpi_device_id wmi_device_ids[] = {
94 {"PNP0C14", 0},
95 {"pnp0c14", 0},
96 {"", 0},
97};
98MODULE_DEVICE_TABLE(acpi, wmi_device_ids);
99
100static struct acpi_driver acpi_wmi_driver = {
101 .name = "wmi",
102 .class = ACPI_WMI_CLASS,
103 .ids = wmi_device_ids,
104 .ops = {
105 .add = acpi_wmi_add,
106 .remove = acpi_wmi_remove,
Bjorn Helgaasf61bb932009-04-07 15:37:37 +0000107 .notify = acpi_wmi_notify,
Carlos Corbachobff431e2008-02-05 02:17:04 +0000108 },
109};
110
111/*
112 * GUID parsing functions
113 */
114
115/**
116 * wmi_parse_hexbyte - Convert a ASCII hex number to a byte
117 * @src: Pointer to at least 2 characters to convert.
118 *
119 * Convert a two character ASCII hex string to a number.
120 *
121 * Return: 0-255 Success, the byte was parsed correctly
122 * -1 Error, an invalid character was supplied
123 */
124static int wmi_parse_hexbyte(const u8 *src)
125{
126 unsigned int x; /* For correct wrapping */
127 int h;
128
129 /* high part */
130 x = src[0];
131 if (x - '0' <= '9' - '0') {
132 h = x - '0';
133 } else if (x - 'a' <= 'f' - 'a') {
134 h = x - 'a' + 10;
135 } else if (x - 'A' <= 'F' - 'A') {
136 h = x - 'A' + 10;
137 } else {
138 return -1;
139 }
140 h <<= 4;
141
142 /* low part */
143 x = src[1];
144 if (x - '0' <= '9' - '0')
145 return h | (x - '0');
146 if (x - 'a' <= 'f' - 'a')
147 return h | (x - 'a' + 10);
148 if (x - 'A' <= 'F' - 'A')
149 return h | (x - 'A' + 10);
150 return -1;
151}
152
153/**
154 * wmi_swap_bytes - Rearrange GUID bytes to match GUID binary
155 * @src: Memory block holding binary GUID (16 bytes)
156 * @dest: Memory block to hold byte swapped binary GUID (16 bytes)
157 *
158 * Byte swap a binary GUID to match it's real GUID value
159 */
160static void wmi_swap_bytes(u8 *src, u8 *dest)
161{
162 int i;
163
164 for (i = 0; i <= 3; i++)
165 memcpy(dest + i, src + (3 - i), 1);
166
167 for (i = 0; i <= 1; i++)
168 memcpy(dest + 4 + i, src + (5 - i), 1);
169
170 for (i = 0; i <= 1; i++)
171 memcpy(dest + 6 + i, src + (7 - i), 1);
172
173 memcpy(dest + 8, src + 8, 8);
174}
175
176/**
177 * wmi_parse_guid - Convert GUID from ASCII to binary
178 * @src: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
179 * @dest: Memory block to hold binary GUID (16 bytes)
180 *
181 * N.B. The GUID need not be NULL terminated.
182 *
183 * Return: 'true' @dest contains binary GUID
184 * 'false' @dest contents are undefined
185 */
186static bool wmi_parse_guid(const u8 *src, u8 *dest)
187{
188 static const int size[] = { 4, 2, 2, 2, 6 };
189 int i, j, v;
190
191 if (src[8] != '-' || src[13] != '-' ||
192 src[18] != '-' || src[23] != '-')
193 return false;
194
195 for (j = 0; j < 5; j++, src++) {
196 for (i = 0; i < size[j]; i++, src += 2, *dest++ = v) {
197 v = wmi_parse_hexbyte(src);
198 if (v < 0)
199 return false;
200 }
201 }
202
203 return true;
204}
205
Matthew Garrett1caab3c2009-11-04 14:17:53 -0500206/*
207 * Convert a raw GUID to the ACII string representation
208 */
209static int wmi_gtoa(const char *in, char *out)
210{
211 int i;
212
213 for (i = 3; i >= 0; i--)
214 out += sprintf(out, "%02X", in[i] & 0xFF);
215
216 out += sprintf(out, "-");
217 out += sprintf(out, "%02X", in[5] & 0xFF);
218 out += sprintf(out, "%02X", in[4] & 0xFF);
219 out += sprintf(out, "-");
220 out += sprintf(out, "%02X", in[7] & 0xFF);
221 out += sprintf(out, "%02X", in[6] & 0xFF);
222 out += sprintf(out, "-");
223 out += sprintf(out, "%02X", in[8] & 0xFF);
224 out += sprintf(out, "%02X", in[9] & 0xFF);
225 out += sprintf(out, "-");
226
227 for (i = 10; i <= 15; i++)
228 out += sprintf(out, "%02X", in[i] & 0xFF);
229
230 out = '\0';
231 return 0;
232}
233
Carlos Corbachobff431e2008-02-05 02:17:04 +0000234static bool find_guid(const char *guid_string, struct wmi_block **out)
235{
236 char tmp[16], guid_input[16];
237 struct wmi_block *wblock;
238 struct guid_block *block;
239 struct list_head *p;
240
241 wmi_parse_guid(guid_string, tmp);
242 wmi_swap_bytes(tmp, guid_input);
243
244 list_for_each(p, &wmi_blocks.list) {
245 wblock = list_entry(p, struct wmi_block, list);
246 block = &wblock->gblock;
247
248 if (memcmp(block->guid, guid_input, 16) == 0) {
249 if (out)
250 *out = wblock;
251 return 1;
252 }
253 }
254 return 0;
255}
256
Matthew Garretta66bfa72008-10-08 21:40:32 +0100257static acpi_status wmi_method_enable(struct wmi_block *wblock, int enable)
258{
259 struct guid_block *block = NULL;
260 char method[5];
261 struct acpi_object_list input;
262 union acpi_object params[1];
263 acpi_status status;
264 acpi_handle handle;
265
266 block = &wblock->gblock;
267 handle = wblock->handle;
268
269 if (!block)
270 return AE_NOT_EXIST;
271
272 input.count = 1;
273 input.pointer = params;
274 params[0].type = ACPI_TYPE_INTEGER;
275 params[0].integer.value = enable;
276
277 snprintf(method, 5, "WE%02X", block->notify_id);
278 status = acpi_evaluate_object(handle, method, &input, NULL);
279
280 if (status != AE_OK && status != AE_NOT_FOUND)
281 return status;
282 else
283 return AE_OK;
284}
285
Carlos Corbachobff431e2008-02-05 02:17:04 +0000286/*
287 * Exported WMI functions
288 */
289/**
290 * wmi_evaluate_method - Evaluate a WMI method
291 * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
292 * @instance: Instance index
293 * @method_id: Method ID to call
294 * &in: Buffer containing input for the method call
295 * &out: Empty buffer to return the method results
296 *
297 * Call an ACPI-WMI method
298 */
299acpi_status wmi_evaluate_method(const char *guid_string, u8 instance,
300u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out)
301{
302 struct guid_block *block = NULL;
303 struct wmi_block *wblock = NULL;
304 acpi_handle handle;
305 acpi_status status;
306 struct acpi_object_list input;
307 union acpi_object params[3];
Costantino Leandrof3d83e22009-08-26 14:29:28 -0700308 char method[5] = "WM";
Carlos Corbachobff431e2008-02-05 02:17:04 +0000309
310 if (!find_guid(guid_string, &wblock))
Lin Ming08237972008-08-08 11:57:11 +0800311 return AE_ERROR;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000312
313 block = &wblock->gblock;
314 handle = wblock->handle;
315
Al Viroe6bafba2008-02-13 04:03:25 +0000316 if (!(block->flags & ACPI_WMI_METHOD))
Carlos Corbachobff431e2008-02-05 02:17:04 +0000317 return AE_BAD_DATA;
318
319 if (block->instance_count < instance)
320 return AE_BAD_PARAMETER;
321
322 input.count = 2;
323 input.pointer = params;
324 params[0].type = ACPI_TYPE_INTEGER;
325 params[0].integer.value = instance;
326 params[1].type = ACPI_TYPE_INTEGER;
327 params[1].integer.value = method_id;
328
329 if (in) {
330 input.count = 3;
331
332 if (block->flags & ACPI_WMI_STRING) {
333 params[2].type = ACPI_TYPE_STRING;
334 } else {
335 params[2].type = ACPI_TYPE_BUFFER;
336 }
337 params[2].buffer.length = in->length;
338 params[2].buffer.pointer = in->pointer;
339 }
340
341 strncat(method, block->object_id, 2);
342
343 status = acpi_evaluate_object(handle, method, &input, out);
344
345 return status;
346}
347EXPORT_SYMBOL_GPL(wmi_evaluate_method);
348
349/**
350 * wmi_query_block - Return contents of a WMI block
351 * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
352 * @instance: Instance index
353 * &out: Empty buffer to return the contents of the data block to
354 *
355 * Return the contents of an ACPI-WMI data block to a buffer
356 */
357acpi_status wmi_query_block(const char *guid_string, u8 instance,
358struct acpi_buffer *out)
359{
360 struct guid_block *block = NULL;
361 struct wmi_block *wblock = NULL;
Carlos Corbachoa527f2d2008-02-24 13:34:34 +0000362 acpi_handle handle, wc_handle;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000363 acpi_status status, wc_status = AE_ERROR;
364 struct acpi_object_list input, wc_input;
365 union acpi_object wc_params[1], wq_params[1];
Costantino Leandrof3d83e22009-08-26 14:29:28 -0700366 char method[5];
367 char wc_method[5] = "WC";
Carlos Corbachobff431e2008-02-05 02:17:04 +0000368
369 if (!guid_string || !out)
370 return AE_BAD_PARAMETER;
371
372 if (!find_guid(guid_string, &wblock))
Lin Ming08237972008-08-08 11:57:11 +0800373 return AE_ERROR;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000374
375 block = &wblock->gblock;
376 handle = wblock->handle;
377
378 if (block->instance_count < instance)
379 return AE_BAD_PARAMETER;
380
381 /* Check GUID is a data block */
382 if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD))
Lin Ming08237972008-08-08 11:57:11 +0800383 return AE_ERROR;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000384
385 input.count = 1;
386 input.pointer = wq_params;
387 wq_params[0].type = ACPI_TYPE_INTEGER;
388 wq_params[0].integer.value = instance;
389
390 /*
391 * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method first to
392 * enable collection.
393 */
394 if (block->flags & ACPI_WMI_EXPENSIVE) {
395 wc_input.count = 1;
396 wc_input.pointer = wc_params;
397 wc_params[0].type = ACPI_TYPE_INTEGER;
398 wc_params[0].integer.value = 1;
399
400 strncat(wc_method, block->object_id, 2);
401
402 /*
403 * Some GUIDs break the specification by declaring themselves
404 * expensive, but have no corresponding WCxx method. So we
405 * should not fail if this happens.
406 */
Carlos Corbachoa527f2d2008-02-24 13:34:34 +0000407 wc_status = acpi_get_handle(handle, wc_method, &wc_handle);
408 if (ACPI_SUCCESS(wc_status))
409 wc_status = acpi_evaluate_object(handle, wc_method,
410 &wc_input, NULL);
Carlos Corbachobff431e2008-02-05 02:17:04 +0000411 }
412
413 strcpy(method, "WQ");
414 strncat(method, block->object_id, 2);
415
Carlos Corbachodab36ad2008-08-02 17:28:45 +0100416 status = acpi_evaluate_object(handle, method, &input, out);
Carlos Corbachobff431e2008-02-05 02:17:04 +0000417
418 /*
419 * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method, even if
420 * the WQxx method failed - we should disable collection anyway.
421 */
Carlos Corbachoa527f2d2008-02-24 13:34:34 +0000422 if ((block->flags & ACPI_WMI_EXPENSIVE) && ACPI_SUCCESS(wc_status)) {
Carlos Corbachobff431e2008-02-05 02:17:04 +0000423 wc_params[0].integer.value = 0;
424 status = acpi_evaluate_object(handle,
425 wc_method, &wc_input, NULL);
426 }
427
428 return status;
429}
430EXPORT_SYMBOL_GPL(wmi_query_block);
431
432/**
433 * wmi_set_block - Write to a WMI block
434 * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
435 * @instance: Instance index
436 * &in: Buffer containing new values for the data block
437 *
438 * Write the contents of the input buffer to an ACPI-WMI data block
439 */
440acpi_status wmi_set_block(const char *guid_string, u8 instance,
441const struct acpi_buffer *in)
442{
443 struct guid_block *block = NULL;
444 struct wmi_block *wblock = NULL;
445 acpi_handle handle;
446 struct acpi_object_list input;
447 union acpi_object params[2];
Costantino Leandrof3d83e22009-08-26 14:29:28 -0700448 char method[5] = "WS";
Carlos Corbachobff431e2008-02-05 02:17:04 +0000449
450 if (!guid_string || !in)
451 return AE_BAD_DATA;
452
453 if (!find_guid(guid_string, &wblock))
Lin Ming08237972008-08-08 11:57:11 +0800454 return AE_ERROR;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000455
456 block = &wblock->gblock;
457 handle = wblock->handle;
458
459 if (block->instance_count < instance)
460 return AE_BAD_PARAMETER;
461
462 /* Check GUID is a data block */
463 if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD))
Lin Ming08237972008-08-08 11:57:11 +0800464 return AE_ERROR;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000465
466 input.count = 2;
467 input.pointer = params;
468 params[0].type = ACPI_TYPE_INTEGER;
469 params[0].integer.value = instance;
470
471 if (block->flags & ACPI_WMI_STRING) {
472 params[1].type = ACPI_TYPE_STRING;
473 } else {
474 params[1].type = ACPI_TYPE_BUFFER;
475 }
476 params[1].buffer.length = in->length;
477 params[1].buffer.pointer = in->pointer;
478
479 strncat(method, block->object_id, 2);
480
481 return acpi_evaluate_object(handle, method, &input, NULL);
482}
483EXPORT_SYMBOL_GPL(wmi_set_block);
484
Thomas Renningerfc3155b2010-05-03 15:30:15 +0200485static void wmi_notify_debug(u32 value, void *context)
486{
487 struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
488 union acpi_object *obj;
489
490 wmi_get_event_data(value, &response);
491
492 obj = (union acpi_object *)response.pointer;
493
494 if (!obj)
495 return;
496
497 printk(KERN_INFO PREFIX "DEBUG Event ");
498 switch(obj->type) {
499 case ACPI_TYPE_BUFFER:
500 printk("BUFFER_TYPE - length %d\n", obj->buffer.length);
501 break;
502 case ACPI_TYPE_STRING:
503 printk("STRING_TYPE - %s\n", obj->string.pointer);
504 break;
505 case ACPI_TYPE_INTEGER:
506 printk("INTEGER_TYPE - %llu\n", obj->integer.value);
507 break;
508 case ACPI_TYPE_PACKAGE:
509 printk("PACKAGE_TYPE - %d elements\n", obj->package.count);
510 break;
511 default:
512 printk("object type 0x%X\n", obj->type);
513 }
514}
515
Carlos Corbachobff431e2008-02-05 02:17:04 +0000516/**
517 * wmi_install_notify_handler - Register handler for WMI events
518 * @handler: Function to handle notifications
519 * @data: Data to be returned to handler when event is fired
520 *
521 * Register a handler for events sent to the ACPI-WMI mapper device.
522 */
523acpi_status wmi_install_notify_handler(const char *guid,
524wmi_notify_handler handler, void *data)
525{
526 struct wmi_block *block;
Matthew Garretta66bfa72008-10-08 21:40:32 +0100527 acpi_status status;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000528
529 if (!guid || !handler)
530 return AE_BAD_PARAMETER;
531
Paul Rollandc03b26a2009-12-30 01:07:40 -0500532 if (!find_guid(guid, &block))
Carlos Corbachobff431e2008-02-05 02:17:04 +0000533 return AE_NOT_EXIST;
534
Thomas Renningerfc3155b2010-05-03 15:30:15 +0200535 if (block->handler && block->handler != wmi_notify_debug)
Carlos Corbachobff431e2008-02-05 02:17:04 +0000536 return AE_ALREADY_ACQUIRED;
537
538 block->handler = handler;
539 block->handler_data = data;
540
Matthew Garretta66bfa72008-10-08 21:40:32 +0100541 status = wmi_method_enable(block, 1);
542
543 return status;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000544}
545EXPORT_SYMBOL_GPL(wmi_install_notify_handler);
546
547/**
548 * wmi_uninstall_notify_handler - Unregister handler for WMI events
549 *
550 * Unregister handler for events sent to the ACPI-WMI mapper device.
551 */
552acpi_status wmi_remove_notify_handler(const char *guid)
553{
554 struct wmi_block *block;
Thomas Renningerfc3155b2010-05-03 15:30:15 +0200555 acpi_status status = AE_OK;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000556
557 if (!guid)
558 return AE_BAD_PARAMETER;
559
Paul Rollandc03b26a2009-12-30 01:07:40 -0500560 if (!find_guid(guid, &block))
Carlos Corbachobff431e2008-02-05 02:17:04 +0000561 return AE_NOT_EXIST;
562
Thomas Renningerfc3155b2010-05-03 15:30:15 +0200563 if (!block->handler || block->handler == wmi_notify_debug)
Carlos Corbachobff431e2008-02-05 02:17:04 +0000564 return AE_NULL_ENTRY;
565
Thomas Renningerfc3155b2010-05-03 15:30:15 +0200566 if (debug_event) {
567 block->handler = wmi_notify_debug;
568 } else {
569 status = wmi_method_enable(block, 0);
570 block->handler = NULL;
571 block->handler_data = NULL;
572 }
Matthew Garretta66bfa72008-10-08 21:40:32 +0100573 return status;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000574}
575EXPORT_SYMBOL_GPL(wmi_remove_notify_handler);
576
577/**
578 * wmi_get_event_data - Get WMI data associated with an event
579 *
Anisse Astier3e9b9882009-12-04 10:10:09 +0100580 * @event: Event to find
581 * @out: Buffer to hold event data. out->pointer should be freed with kfree()
Carlos Corbachobff431e2008-02-05 02:17:04 +0000582 *
583 * Returns extra data associated with an event in WMI.
584 */
585acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out)
586{
587 struct acpi_object_list input;
588 union acpi_object params[1];
589 struct guid_block *gblock;
590 struct wmi_block *wblock;
591 struct list_head *p;
592
593 input.count = 1;
594 input.pointer = params;
595 params[0].type = ACPI_TYPE_INTEGER;
596 params[0].integer.value = event;
597
598 list_for_each(p, &wmi_blocks.list) {
599 wblock = list_entry(p, struct wmi_block, list);
600 gblock = &wblock->gblock;
601
602 if ((gblock->flags & ACPI_WMI_EVENT) &&
603 (gblock->notify_id == event))
604 return acpi_evaluate_object(wblock->handle, "_WED",
605 &input, out);
606 }
607
608 return AE_NOT_FOUND;
609}
610EXPORT_SYMBOL_GPL(wmi_get_event_data);
611
612/**
613 * wmi_has_guid - Check if a GUID is available
614 * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
615 *
616 * Check if a given GUID is defined by _WDG
617 */
618bool wmi_has_guid(const char *guid_string)
619{
620 return find_guid(guid_string, NULL);
621}
622EXPORT_SYMBOL_GPL(wmi_has_guid);
623
624/*
Matthew Garrett1caab3c2009-11-04 14:17:53 -0500625 * sysfs interface
626 */
627static ssize_t show_modalias(struct device *dev, struct device_attribute *attr,
628 char *buf)
629{
630 char guid_string[37];
631 struct wmi_block *wblock;
632
633 wblock = dev_get_drvdata(dev);
634 if (!wblock)
635 return -ENOMEM;
636
637 wmi_gtoa(wblock->gblock.guid, guid_string);
638
639 return sprintf(buf, "wmi:%s\n", guid_string);
640}
641static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL);
642
643static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env)
644{
645 char guid_string[37];
646
647 struct wmi_block *wblock;
648
649 if (add_uevent_var(env, "MODALIAS="))
650 return -ENOMEM;
651
652 wblock = dev_get_drvdata(dev);
653 if (!wblock)
654 return -ENOMEM;
655
656 wmi_gtoa(wblock->gblock.guid, guid_string);
657
658 strcpy(&env->buf[env->buflen - 1], "wmi:");
659 memcpy(&env->buf[env->buflen - 1 + 4], guid_string, 36);
660 env->buflen += 40;
661
662 return 0;
663}
664
665static void wmi_dev_free(struct device *dev)
666{
667 kfree(dev);
668}
669
670static struct class wmi_class = {
671 .name = "wmi",
672 .dev_release = wmi_dev_free,
673 .dev_uevent = wmi_dev_uevent,
674};
675
676static int wmi_create_devs(void)
677{
678 int result;
679 char guid_string[37];
680 struct guid_block *gblock;
681 struct wmi_block *wblock;
682 struct list_head *p;
683 struct device *guid_dev;
684
685 /* Create devices for all the GUIDs */
686 list_for_each(p, &wmi_blocks.list) {
687 wblock = list_entry(p, struct wmi_block, list);
688
689 guid_dev = kzalloc(sizeof(struct device), GFP_KERNEL);
690 if (!guid_dev)
691 return -ENOMEM;
692
693 wblock->dev = guid_dev;
694
695 guid_dev->class = &wmi_class;
696 dev_set_drvdata(guid_dev, wblock);
697
698 gblock = &wblock->gblock;
699
700 wmi_gtoa(gblock->guid, guid_string);
701 dev_set_name(guid_dev, guid_string);
702
703 result = device_register(guid_dev);
704 if (result)
705 return result;
706
707 result = device_create_file(guid_dev, &dev_attr_modalias);
708 if (result)
709 return result;
710 }
711
712 return 0;
713}
714
715static void wmi_remove_devs(void)
716{
717 struct guid_block *gblock;
718 struct wmi_block *wblock;
719 struct list_head *p;
720 struct device *guid_dev;
721
722 /* Delete devices for all the GUIDs */
723 list_for_each(p, &wmi_blocks.list) {
724 wblock = list_entry(p, struct wmi_block, list);
725
726 guid_dev = wblock->dev;
727 gblock = &wblock->gblock;
728
729 device_remove_file(guid_dev, &dev_attr_modalias);
730
731 device_unregister(guid_dev);
732 }
733}
734
735static void wmi_class_exit(void)
736{
737 wmi_remove_devs();
738 class_unregister(&wmi_class);
739}
740
741static int wmi_class_init(void)
742{
743 int ret;
744
745 ret = class_register(&wmi_class);
746 if (ret)
747 return ret;
748
749 ret = wmi_create_devs();
750 if (ret)
751 wmi_class_exit();
752
753 return ret;
754}
755
Carlos Corbachod1f9e492009-12-26 19:14:59 +0000756static bool guid_already_parsed(const char *guid_string)
757{
758 struct guid_block *gblock;
759 struct wmi_block *wblock;
760 struct list_head *p;
761
762 list_for_each(p, &wmi_blocks.list) {
763 wblock = list_entry(p, struct wmi_block, list);
764 gblock = &wblock->gblock;
765
766 if (strncmp(gblock->guid, guid_string, 16) == 0)
767 return true;
768 }
769 return false;
770}
771
Matthew Garrett1caab3c2009-11-04 14:17:53 -0500772/*
Carlos Corbachobff431e2008-02-05 02:17:04 +0000773 * Parse the _WDG method for the GUID data blocks
774 */
775static __init acpi_status parse_wdg(acpi_handle handle)
776{
777 struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
778 union acpi_object *obj;
779 struct guid_block *gblock;
780 struct wmi_block *wblock;
Carlos Corbachod1f9e492009-12-26 19:14:59 +0000781 char guid_string[37];
Carlos Corbachobff431e2008-02-05 02:17:04 +0000782 acpi_status status;
783 u32 i, total;
784
785 status = acpi_evaluate_object(handle, "_WDG", NULL, &out);
786
787 if (ACPI_FAILURE(status))
788 return status;
789
790 obj = (union acpi_object *) out.pointer;
791
792 if (obj->type != ACPI_TYPE_BUFFER)
793 return AE_ERROR;
794
795 total = obj->buffer.length / sizeof(struct guid_block);
796
Julia Lawall2c6719a2010-05-15 23:22:18 +0200797 gblock = kmemdup(obj->buffer.pointer, obj->buffer.length, GFP_KERNEL);
Carlos Corbachobff431e2008-02-05 02:17:04 +0000798 if (!gblock)
799 return AE_NO_MEMORY;
800
Carlos Corbachobff431e2008-02-05 02:17:04 +0000801 for (i = 0; i < total; i++) {
Carlos Corbachod1f9e492009-12-26 19:14:59 +0000802 /*
803 Some WMI devices, like those for nVidia hooks, have a
804 duplicate GUID. It's not clear what we should do in this
805 case yet, so for now, we'll just ignore the duplicate.
806 Anyone who wants to add support for that device can come
807 up with a better workaround for the mess then.
808 */
809 if (guid_already_parsed(gblock[i].guid) == true) {
810 wmi_gtoa(gblock[i].guid, guid_string);
811 printk(KERN_INFO PREFIX "Skipping duplicate GUID %s\n",
812 guid_string);
813 continue;
814 }
Carlos Corbachobff431e2008-02-05 02:17:04 +0000815 wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL);
816 if (!wblock)
817 return AE_NO_MEMORY;
818
819 wblock->gblock = gblock[i];
820 wblock->handle = handle;
Thomas Renningerfc3155b2010-05-03 15:30:15 +0200821 if (debug_event) {
822 wblock->handler = wmi_notify_debug;
823 status = wmi_method_enable(wblock, 1);
824 }
Carlos Corbachobff431e2008-02-05 02:17:04 +0000825 list_add_tail(&wblock->list, &wmi_blocks.list);
826 }
827
828 kfree(out.pointer);
829 kfree(gblock);
830
831 return status;
832}
833
834/*
835 * WMI can have EmbeddedControl access regions. In which case, we just want to
836 * hand these off to the EC driver.
837 */
838static acpi_status
839acpi_wmi_ec_space_handler(u32 function, acpi_physical_address address,
Lin Ming439913f2010-01-28 10:53:19 +0800840 u32 bits, u64 *value,
Carlos Corbachobff431e2008-02-05 02:17:04 +0000841 void *handler_context, void *region_context)
842{
843 int result = 0, i = 0;
844 u8 temp = 0;
845
846 if ((address > 0xFF) || !value)
847 return AE_BAD_PARAMETER;
848
849 if (function != ACPI_READ && function != ACPI_WRITE)
850 return AE_BAD_PARAMETER;
851
852 if (bits != 8)
853 return AE_BAD_PARAMETER;
854
855 if (function == ACPI_READ) {
856 result = ec_read(address, &temp);
Lin Ming439913f2010-01-28 10:53:19 +0800857 (*value) |= ((u64)temp) << i;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000858 } else {
859 temp = 0xff & ((*value) >> i);
860 result = ec_write(address, temp);
861 }
862
863 switch (result) {
864 case -EINVAL:
865 return AE_BAD_PARAMETER;
866 break;
867 case -ENODEV:
868 return AE_NOT_FOUND;
869 break;
870 case -ETIME:
871 return AE_TIME;
872 break;
873 default:
874 return AE_OK;
875 }
876}
877
Bjorn Helgaasf61bb932009-04-07 15:37:37 +0000878static void acpi_wmi_notify(struct acpi_device *device, u32 event)
Carlos Corbachobff431e2008-02-05 02:17:04 +0000879{
880 struct guid_block *block;
881 struct wmi_block *wblock;
882 struct list_head *p;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000883
884 list_for_each(p, &wmi_blocks.list) {
885 wblock = list_entry(p, struct wmi_block, list);
886 block = &wblock->gblock;
887
888 if ((block->flags & ACPI_WMI_EVENT) &&
889 (block->notify_id == event)) {
890 if (wblock->handler)
891 wblock->handler(event, wblock->handler_data);
892
893 acpi_bus_generate_netlink_event(
Kay Sievers07944692008-10-30 01:18:59 +0100894 device->pnp.device_class, dev_name(&device->dev),
Carlos Corbachobff431e2008-02-05 02:17:04 +0000895 event, 0);
896 break;
897 }
898 }
899}
900
901static int acpi_wmi_remove(struct acpi_device *device, int type)
902{
Carlos Corbachobff431e2008-02-05 02:17:04 +0000903 acpi_remove_address_space_handler(device->handle,
904 ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler);
905
906 return 0;
907}
908
909static int __init acpi_wmi_add(struct acpi_device *device)
910{
911 acpi_status status;
912 int result = 0;
913
Carlos Corbachobff431e2008-02-05 02:17:04 +0000914 status = acpi_install_address_space_handler(device->handle,
915 ACPI_ADR_SPACE_EC,
916 &acpi_wmi_ec_space_handler,
917 NULL, NULL);
918 if (ACPI_FAILURE(status))
919 return -ENODEV;
920
921 status = parse_wdg(device->handle);
922 if (ACPI_FAILURE(status)) {
923 printk(KERN_ERR PREFIX "Error installing EC region handler\n");
924 return -ENODEV;
925 }
926
927 return result;
928}
929
930static int __init acpi_wmi_init(void)
931{
Roel Kluinda511992009-03-04 11:55:30 -0800932 int result;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000933
Linus Torvalds96b5a462008-02-11 20:52:01 -0800934 INIT_LIST_HEAD(&wmi_blocks.list);
935
Carlos Corbachobff431e2008-02-05 02:17:04 +0000936 if (acpi_disabled)
937 return -ENODEV;
938
Carlos Corbachobff431e2008-02-05 02:17:04 +0000939 result = acpi_bus_register_driver(&acpi_wmi_driver);
940
941 if (result < 0) {
942 printk(KERN_INFO PREFIX "Error loading mapper\n");
Matthew Garrett1caab3c2009-11-04 14:17:53 -0500943 return -ENODEV;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000944 }
945
Matthew Garrett1caab3c2009-11-04 14:17:53 -0500946 result = wmi_class_init();
947 if (result) {
948 acpi_bus_unregister_driver(&acpi_wmi_driver);
949 return result;
950 }
951
952 printk(KERN_INFO PREFIX "Mapper loaded\n");
953
Carlos Corbachobff431e2008-02-05 02:17:04 +0000954 return result;
955}
956
957static void __exit acpi_wmi_exit(void)
958{
959 struct list_head *p, *tmp;
960 struct wmi_block *wblock;
961
Matthew Garrett1caab3c2009-11-04 14:17:53 -0500962 wmi_class_exit();
963
Carlos Corbachobff431e2008-02-05 02:17:04 +0000964 acpi_bus_unregister_driver(&acpi_wmi_driver);
965
966 list_for_each_safe(p, tmp, &wmi_blocks.list) {
967 wblock = list_entry(p, struct wmi_block, list);
968
969 list_del(p);
970 kfree(wblock);
971 }
972
973 printk(KERN_INFO PREFIX "Mapper unloaded\n");
974}
975
976subsys_initcall(acpi_wmi_init);
977module_exit(acpi_wmi_exit);