]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blame - drivers/platform/x86/wmi.c
X86 platform wmi: Introduce debug param to log all WMI events
[mirror_ubuntu-zesty-kernel.git] / drivers / platform / x86 / wmi.c
CommitLineData
bff431e4
CC
1/*
2 * ACPI-WMI mapping driver
3 *
4 * Copyright (C) 2007-2008 Carlos Corbacho <carlos@strangeworlds.co.uk>
5 *
6 * GUID parsing code from ldm.c is:
7 * Copyright (C) 2001,2002 Richard Russon <ldm@flatcap.org>
8 * Copyright (c) 2001-2007 Anton Altaparmakov
9 * Copyright (C) 2001,2002 Jakob Kemi <jakob.kemi@telia.com>
10 *
11 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or (at
16 * your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful, but
19 * WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License along
24 * with this program; if not, write to the Free Software Foundation, Inc.,
25 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
26 *
27 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
28 */
29
30#include <linux/kernel.h>
31#include <linux/init.h>
32#include <linux/types.h>
1caab3c1 33#include <linux/device.h>
bff431e4
CC
34#include <linux/list.h>
35#include <linux/acpi.h>
5a0e3ad6 36#include <linux/slab.h>
bff431e4
CC
37#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
bff431e4
CC
47#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;
1caab3c1 70 struct device *dev;
bff431e4
CC
71};
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
fc3155b2
TR
84static int debug_event;
85module_param(debug_event, bool, 0444);
86MODULE_PARM_DESC(debug_event,
87 "Log WMI Events [0/1]");
88
bff431e4
CC
89static int acpi_wmi_remove(struct acpi_device *device, int type);
90static int acpi_wmi_add(struct acpi_device *device);
f61bb939 91static void acpi_wmi_notify(struct acpi_device *device, u32 event);
bff431e4
CC
92
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,
f61bb939 107 .notify = acpi_wmi_notify,
bff431e4
CC
108 },
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
1caab3c1
MG
206/*
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
bff431e4
CC
234static 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
a66bfa7a
MG
257static 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
bff431e4
CC
286/*
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];
f3d83e24 308 char method[5] = "WM";
bff431e4
CC
309
310 if (!find_guid(guid_string, &wblock))
08237974 311 return AE_ERROR;
bff431e4
CC
312
313 block = &wblock->gblock;
314 handle = wblock->handle;
315
e6bafba5 316 if (!(block->flags & ACPI_WMI_METHOD))
bff431e4
CC
317 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;
a527f2d7 362 acpi_handle handle, wc_handle;
bff431e4
CC
363 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];
f3d83e24
CL
366 char method[5];
367 char wc_method[5] = "WC";
bff431e4
CC
368
369 if (!guid_string || !out)
370 return AE_BAD_PARAMETER;
371
372 if (!find_guid(guid_string, &wblock))
08237974 373 return AE_ERROR;
bff431e4
CC
374
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))
08237974 383 return AE_ERROR;
bff431e4
CC
384
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 */
a527f2d7
CC
407 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);
bff431e4
CC
411 }
412
413 strcpy(method, "WQ");
414 strncat(method, block->object_id, 2);
415
dab36ad8 416 status = acpi_evaluate_object(handle, method, &input, out);
bff431e4
CC
417
418 /*
419 * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method, even if
420 * the WQxx method failed - we should disable collection anyway.
421 */
a527f2d7 422 if ((block->flags & ACPI_WMI_EXPENSIVE) && ACPI_SUCCESS(wc_status)) {
bff431e4
CC
423 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];
f3d83e24 448 char method[5] = "WS";
bff431e4
CC
449
450 if (!guid_string || !in)
451 return AE_BAD_DATA;
452
453 if (!find_guid(guid_string, &wblock))
08237974 454 return AE_ERROR;
bff431e4
CC
455
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))
08237974 464 return AE_ERROR;
bff431e4
CC
465
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
fc3155b2
TR
485static 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
bff431e4
CC
516/**
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;
a66bfa7a 527 acpi_status status;
bff431e4
CC
528
529 if (!guid || !handler)
530 return AE_BAD_PARAMETER;
531
c03b26a5 532 if (!find_guid(guid, &block))
bff431e4
CC
533 return AE_NOT_EXIST;
534
fc3155b2 535 if (block->handler && block->handler != wmi_notify_debug)
bff431e4
CC
536 return AE_ALREADY_ACQUIRED;
537
538 block->handler = handler;
539 block->handler_data = data;
540
a66bfa7a
MG
541 status = wmi_method_enable(block, 1);
542
543 return status;
bff431e4
CC
544}
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;
fc3155b2 555 acpi_status status = AE_OK;
bff431e4
CC
556
557 if (!guid)
558 return AE_BAD_PARAMETER;
559
c03b26a5 560 if (!find_guid(guid, &block))
bff431e4
CC
561 return AE_NOT_EXIST;
562
fc3155b2 563 if (!block->handler || block->handler == wmi_notify_debug)
bff431e4
CC
564 return AE_NULL_ENTRY;
565
fc3155b2
TR
566 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 }
a66bfa7a 573 return status;
bff431e4
CC
574}
575EXPORT_SYMBOL_GPL(wmi_remove_notify_handler);
576
577/**
578 * wmi_get_event_data - Get WMI data associated with an event
579 *
3e9b988e
AA
580 * @event: Event to find
581 * @out: Buffer to hold event data. out->pointer should be freed with kfree()
bff431e4
CC
582 *
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
1caab3c1
MG
624/*
625 * 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
d1f9e497
CC
756static 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
bff431e4
CC
772/*
773 * 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;
d1f9e497 781 char guid_string[37];
bff431e4
CC
782 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
2c6719a3 797 gblock = kmemdup(obj->buffer.pointer, obj->buffer.length, GFP_KERNEL);
bff431e4
CC
798 if (!gblock)
799 return AE_NO_MEMORY;
800
bff431e4 801 for (i = 0; i < total; i++) {
d1f9e497
CC
802 /*
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 }
bff431e4
CC
815 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;
fc3155b2
TR
821 if (debug_event) {
822 wblock->handler = wmi_notify_debug;
823 status = wmi_method_enable(wblock, 1);
824 }
bff431e4
CC
825 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,
439913ff 840 u32 bits, u64 *value,
bff431e4
CC
841 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);
439913ff 857 (*value) |= ((u64)temp) << i;
bff431e4
CC
858 } 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
f61bb939 878static void acpi_wmi_notify(struct acpi_device *device, u32 event)
bff431e4
CC
879{
880 struct guid_block *block;
881 struct wmi_block *wblock;
882 struct list_head *p;
bff431e4
CC
883
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(
0794469d 894 device->pnp.device_class, dev_name(&device->dev),
bff431e4
CC
895 event, 0);
896 break;
897 }
898 }
899}
900
901static int acpi_wmi_remove(struct acpi_device *device, int type)
902{
bff431e4
CC
903 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
bff431e4
CC
914 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{
da511997 932 int result;
bff431e4 933
96b5a46e
LT
934 INIT_LIST_HEAD(&wmi_blocks.list);
935
bff431e4
CC
936 if (acpi_disabled)
937 return -ENODEV;
938
bff431e4
CC
939 result = acpi_bus_register_driver(&acpi_wmi_driver);
940
941 if (result < 0) {
942 printk(KERN_INFO PREFIX "Error loading mapper\n");
1caab3c1
MG
943 return -ENODEV;
944 }
945
946 result = wmi_class_init();
947 if (result) {
948 acpi_bus_unregister_driver(&acpi_wmi_driver);
949 return result;
bff431e4
CC
950 }
951
1caab3c1
MG
952 printk(KERN_INFO PREFIX "Mapper loaded\n");
953
bff431e4
CC
954 return result;
955}
956
957static void __exit acpi_wmi_exit(void)
958{
959 struct list_head *p, *tmp;
960 struct wmi_block *wblock;
961
1caab3c1
MG
962 wmi_class_exit();
963
bff431e4
CC
964 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);