blob: dd30b2aa7a29bb675bfca3b251640b4a2d2d943e [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
Dmitry Torokhov8e075142010-08-26 00:15:19 -070030#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
31
Carlos Corbachobff431e2008-02-05 02:17:04 +000032#include <linux/kernel.h>
33#include <linux/init.h>
34#include <linux/types.h>
Matthew Garrett1caab3c2009-11-04 14:17:53 -050035#include <linux/device.h>
Carlos Corbachobff431e2008-02-05 02:17:04 +000036#include <linux/list.h>
37#include <linux/acpi.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090038#include <linux/slab.h>
Carlos Corbachobff431e2008-02-05 02:17:04 +000039#include <acpi/acpi_bus.h>
40#include <acpi/acpi_drivers.h>
41
42ACPI_MODULE_NAME("wmi");
43MODULE_AUTHOR("Carlos Corbacho");
44MODULE_DESCRIPTION("ACPI-WMI Mapping Driver");
45MODULE_LICENSE("GPL");
46
47#define ACPI_WMI_CLASS "wmi"
48
Carlos Corbachobff431e2008-02-05 02:17:04 +000049static DEFINE_MUTEX(wmi_data_lock);
Dmitry Torokhov762e1a22010-08-26 00:15:14 -070050static LIST_HEAD(wmi_block_list);
Carlos Corbachobff431e2008-02-05 02:17:04 +000051
52struct guid_block {
53 char guid[16];
54 union {
55 char object_id[2];
56 struct {
57 unsigned char notify_id;
58 unsigned char reserved;
59 };
60 };
61 u8 instance_count;
62 u8 flags;
63};
64
65struct wmi_block {
66 struct list_head list;
67 struct guid_block gblock;
68 acpi_handle handle;
69 wmi_notify_handler handler;
70 void *handler_data;
Matthew Garrett1caab3c2009-11-04 14:17:53 -050071 struct device *dev;
Carlos Corbachobff431e2008-02-05 02:17:04 +000072};
73
Carlos Corbachobff431e2008-02-05 02:17:04 +000074
75/*
76 * If the GUID data block is marked as expensive, we must enable and
77 * explicitily disable data collection.
78 */
79#define ACPI_WMI_EXPENSIVE 0x1
80#define ACPI_WMI_METHOD 0x2 /* GUID is a method */
81#define ACPI_WMI_STRING 0x4 /* GUID takes & returns a string */
82#define ACPI_WMI_EVENT 0x8 /* GUID is an event */
83
Thomas Renningerfc3155b2010-05-03 15:30:15 +020084static int debug_event;
85module_param(debug_event, bool, 0444);
86MODULE_PARM_DESC(debug_event,
87 "Log WMI Events [0/1]");
88
Thomas Renningera929aae2010-05-03 15:30:17 +020089static int debug_dump_wdg;
90module_param(debug_dump_wdg, bool, 0444);
91MODULE_PARM_DESC(debug_dump_wdg,
92 "Dump available WMI interfaces [0/1]");
93
Carlos Corbachobff431e2008-02-05 02:17:04 +000094static int acpi_wmi_remove(struct acpi_device *device, int type);
95static int acpi_wmi_add(struct acpi_device *device);
Bjorn Helgaasf61bb932009-04-07 15:37:37 +000096static void acpi_wmi_notify(struct acpi_device *device, u32 event);
Carlos Corbachobff431e2008-02-05 02:17:04 +000097
98static const struct acpi_device_id wmi_device_ids[] = {
99 {"PNP0C14", 0},
100 {"pnp0c14", 0},
101 {"", 0},
102};
103MODULE_DEVICE_TABLE(acpi, wmi_device_ids);
104
105static struct acpi_driver acpi_wmi_driver = {
106 .name = "wmi",
107 .class = ACPI_WMI_CLASS,
108 .ids = wmi_device_ids,
109 .ops = {
110 .add = acpi_wmi_add,
111 .remove = acpi_wmi_remove,
Bjorn Helgaasf61bb932009-04-07 15:37:37 +0000112 .notify = acpi_wmi_notify,
Carlos Corbachobff431e2008-02-05 02:17:04 +0000113 },
114};
115
116/*
117 * GUID parsing functions
118 */
119
120/**
121 * wmi_parse_hexbyte - Convert a ASCII hex number to a byte
122 * @src: Pointer to at least 2 characters to convert.
123 *
124 * Convert a two character ASCII hex string to a number.
125 *
126 * Return: 0-255 Success, the byte was parsed correctly
127 * -1 Error, an invalid character was supplied
128 */
129static int wmi_parse_hexbyte(const u8 *src)
130{
Carlos Corbachobff431e2008-02-05 02:17:04 +0000131 int h;
Andy Shevchenko392bd8b2010-09-11 16:31:02 +0300132 int value;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000133
134 /* high part */
Andy Shevchenko392bd8b2010-09-11 16:31:02 +0300135 h = value = hex_to_bin(src[0]);
136 if (value < 0)
Carlos Corbachobff431e2008-02-05 02:17:04 +0000137 return -1;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000138
139 /* low part */
Andy Shevchenko392bd8b2010-09-11 16:31:02 +0300140 value = hex_to_bin(src[1]);
141 if (value >= 0)
142 return (h << 4) | value;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000143 return -1;
144}
145
146/**
147 * wmi_swap_bytes - Rearrange GUID bytes to match GUID binary
148 * @src: Memory block holding binary GUID (16 bytes)
149 * @dest: Memory block to hold byte swapped binary GUID (16 bytes)
150 *
151 * Byte swap a binary GUID to match it's real GUID value
152 */
153static void wmi_swap_bytes(u8 *src, u8 *dest)
154{
155 int i;
156
157 for (i = 0; i <= 3; i++)
158 memcpy(dest + i, src + (3 - i), 1);
159
160 for (i = 0; i <= 1; i++)
161 memcpy(dest + 4 + i, src + (5 - i), 1);
162
163 for (i = 0; i <= 1; i++)
164 memcpy(dest + 6 + i, src + (7 - i), 1);
165
166 memcpy(dest + 8, src + 8, 8);
167}
168
169/**
170 * wmi_parse_guid - Convert GUID from ASCII to binary
171 * @src: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
172 * @dest: Memory block to hold binary GUID (16 bytes)
173 *
174 * N.B. The GUID need not be NULL terminated.
175 *
176 * Return: 'true' @dest contains binary GUID
177 * 'false' @dest contents are undefined
178 */
179static bool wmi_parse_guid(const u8 *src, u8 *dest)
180{
181 static const int size[] = { 4, 2, 2, 2, 6 };
182 int i, j, v;
183
184 if (src[8] != '-' || src[13] != '-' ||
185 src[18] != '-' || src[23] != '-')
186 return false;
187
188 for (j = 0; j < 5; j++, src++) {
189 for (i = 0; i < size[j]; i++, src += 2, *dest++ = v) {
190 v = wmi_parse_hexbyte(src);
191 if (v < 0)
192 return false;
193 }
194 }
195
196 return true;
197}
198
Matthew Garrett1caab3c2009-11-04 14:17:53 -0500199/*
200 * Convert a raw GUID to the ACII string representation
201 */
202static int wmi_gtoa(const char *in, char *out)
203{
204 int i;
205
206 for (i = 3; i >= 0; i--)
207 out += sprintf(out, "%02X", in[i] & 0xFF);
208
209 out += sprintf(out, "-");
210 out += sprintf(out, "%02X", in[5] & 0xFF);
211 out += sprintf(out, "%02X", in[4] & 0xFF);
212 out += sprintf(out, "-");
213 out += sprintf(out, "%02X", in[7] & 0xFF);
214 out += sprintf(out, "%02X", in[6] & 0xFF);
215 out += sprintf(out, "-");
216 out += sprintf(out, "%02X", in[8] & 0xFF);
217 out += sprintf(out, "%02X", in[9] & 0xFF);
218 out += sprintf(out, "-");
219
220 for (i = 10; i <= 15; i++)
221 out += sprintf(out, "%02X", in[i] & 0xFF);
222
Dmitry Torokhov4e4304d2010-08-26 00:14:53 -0700223 *out = '\0';
Matthew Garrett1caab3c2009-11-04 14:17:53 -0500224 return 0;
225}
226
Carlos Corbachobff431e2008-02-05 02:17:04 +0000227static bool find_guid(const char *guid_string, struct wmi_block **out)
228{
229 char tmp[16], guid_input[16];
230 struct wmi_block *wblock;
231 struct guid_block *block;
232 struct list_head *p;
233
234 wmi_parse_guid(guid_string, tmp);
235 wmi_swap_bytes(tmp, guid_input);
236
Dmitry Torokhov762e1a22010-08-26 00:15:14 -0700237 list_for_each(p, &wmi_block_list) {
Carlos Corbachobff431e2008-02-05 02:17:04 +0000238 wblock = list_entry(p, struct wmi_block, list);
239 block = &wblock->gblock;
240
241 if (memcmp(block->guid, guid_input, 16) == 0) {
242 if (out)
243 *out = wblock;
244 return 1;
245 }
246 }
247 return 0;
248}
249
Matthew Garretta66bfa72008-10-08 21:40:32 +0100250static acpi_status wmi_method_enable(struct wmi_block *wblock, int enable)
251{
252 struct guid_block *block = NULL;
253 char method[5];
254 struct acpi_object_list input;
255 union acpi_object params[1];
256 acpi_status status;
257 acpi_handle handle;
258
259 block = &wblock->gblock;
260 handle = wblock->handle;
261
262 if (!block)
263 return AE_NOT_EXIST;
264
265 input.count = 1;
266 input.pointer = params;
267 params[0].type = ACPI_TYPE_INTEGER;
268 params[0].integer.value = enable;
269
270 snprintf(method, 5, "WE%02X", block->notify_id);
271 status = acpi_evaluate_object(handle, method, &input, NULL);
272
273 if (status != AE_OK && status != AE_NOT_FOUND)
274 return status;
275 else
276 return AE_OK;
277}
278
Carlos Corbachobff431e2008-02-05 02:17:04 +0000279/*
280 * Exported WMI functions
281 */
282/**
283 * wmi_evaluate_method - Evaluate a WMI method
284 * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
285 * @instance: Instance index
286 * @method_id: Method ID to call
287 * &in: Buffer containing input for the method call
288 * &out: Empty buffer to return the method results
289 *
290 * Call an ACPI-WMI method
291 */
292acpi_status wmi_evaluate_method(const char *guid_string, u8 instance,
293u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out)
294{
295 struct guid_block *block = NULL;
296 struct wmi_block *wblock = NULL;
297 acpi_handle handle;
298 acpi_status status;
299 struct acpi_object_list input;
300 union acpi_object params[3];
Costantino Leandrof3d83e22009-08-26 14:29:28 -0700301 char method[5] = "WM";
Carlos Corbachobff431e2008-02-05 02:17:04 +0000302
303 if (!find_guid(guid_string, &wblock))
Lin Ming08237972008-08-08 11:57:11 +0800304 return AE_ERROR;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000305
306 block = &wblock->gblock;
307 handle = wblock->handle;
308
Al Viroe6bafba2008-02-13 04:03:25 +0000309 if (!(block->flags & ACPI_WMI_METHOD))
Carlos Corbachobff431e2008-02-05 02:17:04 +0000310 return AE_BAD_DATA;
311
312 if (block->instance_count < instance)
313 return AE_BAD_PARAMETER;
314
315 input.count = 2;
316 input.pointer = params;
317 params[0].type = ACPI_TYPE_INTEGER;
318 params[0].integer.value = instance;
319 params[1].type = ACPI_TYPE_INTEGER;
320 params[1].integer.value = method_id;
321
322 if (in) {
323 input.count = 3;
324
325 if (block->flags & ACPI_WMI_STRING) {
326 params[2].type = ACPI_TYPE_STRING;
327 } else {
328 params[2].type = ACPI_TYPE_BUFFER;
329 }
330 params[2].buffer.length = in->length;
331 params[2].buffer.pointer = in->pointer;
332 }
333
334 strncat(method, block->object_id, 2);
335
336 status = acpi_evaluate_object(handle, method, &input, out);
337
338 return status;
339}
340EXPORT_SYMBOL_GPL(wmi_evaluate_method);
341
342/**
343 * wmi_query_block - Return contents of a WMI block
344 * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
345 * @instance: Instance index
346 * &out: Empty buffer to return the contents of the data block to
347 *
348 * Return the contents of an ACPI-WMI data block to a buffer
349 */
350acpi_status wmi_query_block(const char *guid_string, u8 instance,
351struct acpi_buffer *out)
352{
353 struct guid_block *block = NULL;
354 struct wmi_block *wblock = NULL;
Carlos Corbachoa527f2d2008-02-24 13:34:34 +0000355 acpi_handle handle, wc_handle;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000356 acpi_status status, wc_status = AE_ERROR;
357 struct acpi_object_list input, wc_input;
358 union acpi_object wc_params[1], wq_params[1];
Costantino Leandrof3d83e22009-08-26 14:29:28 -0700359 char method[5];
360 char wc_method[5] = "WC";
Carlos Corbachobff431e2008-02-05 02:17:04 +0000361
362 if (!guid_string || !out)
363 return AE_BAD_PARAMETER;
364
365 if (!find_guid(guid_string, &wblock))
Lin Ming08237972008-08-08 11:57:11 +0800366 return AE_ERROR;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000367
368 block = &wblock->gblock;
369 handle = wblock->handle;
370
371 if (block->instance_count < instance)
372 return AE_BAD_PARAMETER;
373
374 /* Check GUID is a data block */
375 if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD))
Lin Ming08237972008-08-08 11:57:11 +0800376 return AE_ERROR;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000377
378 input.count = 1;
379 input.pointer = wq_params;
380 wq_params[0].type = ACPI_TYPE_INTEGER;
381 wq_params[0].integer.value = instance;
382
383 /*
384 * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method first to
385 * enable collection.
386 */
387 if (block->flags & ACPI_WMI_EXPENSIVE) {
388 wc_input.count = 1;
389 wc_input.pointer = wc_params;
390 wc_params[0].type = ACPI_TYPE_INTEGER;
391 wc_params[0].integer.value = 1;
392
393 strncat(wc_method, block->object_id, 2);
394
395 /*
396 * Some GUIDs break the specification by declaring themselves
397 * expensive, but have no corresponding WCxx method. So we
398 * should not fail if this happens.
399 */
Carlos Corbachoa527f2d2008-02-24 13:34:34 +0000400 wc_status = acpi_get_handle(handle, wc_method, &wc_handle);
401 if (ACPI_SUCCESS(wc_status))
402 wc_status = acpi_evaluate_object(handle, wc_method,
403 &wc_input, NULL);
Carlos Corbachobff431e2008-02-05 02:17:04 +0000404 }
405
406 strcpy(method, "WQ");
407 strncat(method, block->object_id, 2);
408
Carlos Corbachodab36ad2008-08-02 17:28:45 +0100409 status = acpi_evaluate_object(handle, method, &input, out);
Carlos Corbachobff431e2008-02-05 02:17:04 +0000410
411 /*
412 * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method, even if
413 * the WQxx method failed - we should disable collection anyway.
414 */
Carlos Corbachoa527f2d2008-02-24 13:34:34 +0000415 if ((block->flags & ACPI_WMI_EXPENSIVE) && ACPI_SUCCESS(wc_status)) {
Carlos Corbachobff431e2008-02-05 02:17:04 +0000416 wc_params[0].integer.value = 0;
417 status = acpi_evaluate_object(handle,
418 wc_method, &wc_input, NULL);
419 }
420
421 return status;
422}
423EXPORT_SYMBOL_GPL(wmi_query_block);
424
425/**
426 * wmi_set_block - Write to a WMI block
427 * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
428 * @instance: Instance index
429 * &in: Buffer containing new values for the data block
430 *
431 * Write the contents of the input buffer to an ACPI-WMI data block
432 */
433acpi_status wmi_set_block(const char *guid_string, u8 instance,
434const struct acpi_buffer *in)
435{
436 struct guid_block *block = NULL;
437 struct wmi_block *wblock = NULL;
438 acpi_handle handle;
439 struct acpi_object_list input;
440 union acpi_object params[2];
Costantino Leandrof3d83e22009-08-26 14:29:28 -0700441 char method[5] = "WS";
Carlos Corbachobff431e2008-02-05 02:17:04 +0000442
443 if (!guid_string || !in)
444 return AE_BAD_DATA;
445
446 if (!find_guid(guid_string, &wblock))
Lin Ming08237972008-08-08 11:57:11 +0800447 return AE_ERROR;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000448
449 block = &wblock->gblock;
450 handle = wblock->handle;
451
452 if (block->instance_count < instance)
453 return AE_BAD_PARAMETER;
454
455 /* Check GUID is a data block */
456 if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD))
Lin Ming08237972008-08-08 11:57:11 +0800457 return AE_ERROR;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000458
459 input.count = 2;
460 input.pointer = params;
461 params[0].type = ACPI_TYPE_INTEGER;
462 params[0].integer.value = instance;
463
464 if (block->flags & ACPI_WMI_STRING) {
465 params[1].type = ACPI_TYPE_STRING;
466 } else {
467 params[1].type = ACPI_TYPE_BUFFER;
468 }
469 params[1].buffer.length = in->length;
470 params[1].buffer.pointer = in->pointer;
471
472 strncat(method, block->object_id, 2);
473
474 return acpi_evaluate_object(handle, method, &input, NULL);
475}
476EXPORT_SYMBOL_GPL(wmi_set_block);
477
Dmitry Torokhov37830662010-08-26 00:15:09 -0700478static void wmi_dump_wdg(const struct guid_block *g)
Thomas Renningera929aae2010-05-03 15:30:17 +0200479{
480 char guid_string[37];
481
482 wmi_gtoa(g->guid, guid_string);
Dmitry Torokhov8e075142010-08-26 00:15:19 -0700483
484 pr_info("%s:\n", guid_string);
485 pr_info("\tobject_id: %c%c\n", g->object_id[0], g->object_id[1]);
486 pr_info("\tnotify_id: %02X\n", g->notify_id);
487 pr_info("\treserved: %02X\n", g->reserved);
488 pr_info("\tinstance_count: %d\n", g->instance_count);
489 pr_info("\tflags: %#x ", g->flags);
Thomas Renningera929aae2010-05-03 15:30:17 +0200490 if (g->flags) {
Thomas Renningera929aae2010-05-03 15:30:17 +0200491 if (g->flags & ACPI_WMI_EXPENSIVE)
Dmitry Torokhov8e075142010-08-26 00:15:19 -0700492 pr_cont("ACPI_WMI_EXPENSIVE ");
Thomas Renningera929aae2010-05-03 15:30:17 +0200493 if (g->flags & ACPI_WMI_METHOD)
Dmitry Torokhov8e075142010-08-26 00:15:19 -0700494 pr_cont("ACPI_WMI_METHOD ");
Thomas Renningera929aae2010-05-03 15:30:17 +0200495 if (g->flags & ACPI_WMI_STRING)
Dmitry Torokhov8e075142010-08-26 00:15:19 -0700496 pr_cont("ACPI_WMI_STRING ");
Thomas Renningera929aae2010-05-03 15:30:17 +0200497 if (g->flags & ACPI_WMI_EVENT)
Dmitry Torokhov8e075142010-08-26 00:15:19 -0700498 pr_cont("ACPI_WMI_EVENT ");
Thomas Renningera929aae2010-05-03 15:30:17 +0200499 }
Dmitry Torokhov8e075142010-08-26 00:15:19 -0700500 pr_cont("\n");
Thomas Renningera929aae2010-05-03 15:30:17 +0200501
502}
503
Thomas Renningerfc3155b2010-05-03 15:30:15 +0200504static void wmi_notify_debug(u32 value, void *context)
505{
506 struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
507 union acpi_object *obj;
Axel Lin14926162010-06-28 09:30:45 +0800508 acpi_status status;
Thomas Renningerfc3155b2010-05-03 15:30:15 +0200509
Axel Lin14926162010-06-28 09:30:45 +0800510 status = wmi_get_event_data(value, &response);
511 if (status != AE_OK) {
Dmitry Torokhov8e075142010-08-26 00:15:19 -0700512 pr_info("bad event status 0x%x\n", status);
Axel Lin14926162010-06-28 09:30:45 +0800513 return;
514 }
Thomas Renningerfc3155b2010-05-03 15:30:15 +0200515
516 obj = (union acpi_object *)response.pointer;
517
518 if (!obj)
519 return;
520
Dmitry Torokhov8e075142010-08-26 00:15:19 -0700521 pr_info("DEBUG Event ");
Thomas Renningerfc3155b2010-05-03 15:30:15 +0200522 switch(obj->type) {
523 case ACPI_TYPE_BUFFER:
Dmitry Torokhov8e075142010-08-26 00:15:19 -0700524 pr_cont("BUFFER_TYPE - length %d\n", obj->buffer.length);
Thomas Renningerfc3155b2010-05-03 15:30:15 +0200525 break;
526 case ACPI_TYPE_STRING:
Dmitry Torokhov8e075142010-08-26 00:15:19 -0700527 pr_cont("STRING_TYPE - %s\n", obj->string.pointer);
Thomas Renningerfc3155b2010-05-03 15:30:15 +0200528 break;
529 case ACPI_TYPE_INTEGER:
Dmitry Torokhov8e075142010-08-26 00:15:19 -0700530 pr_cont("INTEGER_TYPE - %llu\n", obj->integer.value);
Thomas Renningerfc3155b2010-05-03 15:30:15 +0200531 break;
532 case ACPI_TYPE_PACKAGE:
Dmitry Torokhov8e075142010-08-26 00:15:19 -0700533 pr_cont("PACKAGE_TYPE - %d elements\n", obj->package.count);
Thomas Renningerfc3155b2010-05-03 15:30:15 +0200534 break;
535 default:
Dmitry Torokhov8e075142010-08-26 00:15:19 -0700536 pr_cont("object type 0x%X\n", obj->type);
Thomas Renningerfc3155b2010-05-03 15:30:15 +0200537 }
Axel Lin14926162010-06-28 09:30:45 +0800538 kfree(obj);
Thomas Renningerfc3155b2010-05-03 15:30:15 +0200539}
540
Carlos Corbachobff431e2008-02-05 02:17:04 +0000541/**
542 * wmi_install_notify_handler - Register handler for WMI events
543 * @handler: Function to handle notifications
544 * @data: Data to be returned to handler when event is fired
545 *
546 * Register a handler for events sent to the ACPI-WMI mapper device.
547 */
548acpi_status wmi_install_notify_handler(const char *guid,
549wmi_notify_handler handler, void *data)
550{
551 struct wmi_block *block;
Matthew Garretta66bfa72008-10-08 21:40:32 +0100552 acpi_status status;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000553
554 if (!guid || !handler)
555 return AE_BAD_PARAMETER;
556
Paul Rollandc03b26a2009-12-30 01:07:40 -0500557 if (!find_guid(guid, &block))
Carlos Corbachobff431e2008-02-05 02:17:04 +0000558 return AE_NOT_EXIST;
559
Thomas Renningerfc3155b2010-05-03 15:30:15 +0200560 if (block->handler && block->handler != wmi_notify_debug)
Carlos Corbachobff431e2008-02-05 02:17:04 +0000561 return AE_ALREADY_ACQUIRED;
562
563 block->handler = handler;
564 block->handler_data = data;
565
Matthew Garretta66bfa72008-10-08 21:40:32 +0100566 status = wmi_method_enable(block, 1);
567
568 return status;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000569}
570EXPORT_SYMBOL_GPL(wmi_install_notify_handler);
571
572/**
573 * wmi_uninstall_notify_handler - Unregister handler for WMI events
574 *
575 * Unregister handler for events sent to the ACPI-WMI mapper device.
576 */
577acpi_status wmi_remove_notify_handler(const char *guid)
578{
579 struct wmi_block *block;
Thomas Renningerfc3155b2010-05-03 15:30:15 +0200580 acpi_status status = AE_OK;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000581
582 if (!guid)
583 return AE_BAD_PARAMETER;
584
Paul Rollandc03b26a2009-12-30 01:07:40 -0500585 if (!find_guid(guid, &block))
Carlos Corbachobff431e2008-02-05 02:17:04 +0000586 return AE_NOT_EXIST;
587
Thomas Renningerfc3155b2010-05-03 15:30:15 +0200588 if (!block->handler || block->handler == wmi_notify_debug)
Carlos Corbachobff431e2008-02-05 02:17:04 +0000589 return AE_NULL_ENTRY;
590
Thomas Renningerfc3155b2010-05-03 15:30:15 +0200591 if (debug_event) {
592 block->handler = wmi_notify_debug;
593 } else {
594 status = wmi_method_enable(block, 0);
595 block->handler = NULL;
596 block->handler_data = NULL;
597 }
Matthew Garretta66bfa72008-10-08 21:40:32 +0100598 return status;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000599}
600EXPORT_SYMBOL_GPL(wmi_remove_notify_handler);
601
602/**
603 * wmi_get_event_data - Get WMI data associated with an event
604 *
Anisse Astier3e9b9882009-12-04 10:10:09 +0100605 * @event: Event to find
606 * @out: Buffer to hold event data. out->pointer should be freed with kfree()
Carlos Corbachobff431e2008-02-05 02:17:04 +0000607 *
608 * Returns extra data associated with an event in WMI.
609 */
610acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out)
611{
612 struct acpi_object_list input;
613 union acpi_object params[1];
614 struct guid_block *gblock;
615 struct wmi_block *wblock;
616 struct list_head *p;
617
618 input.count = 1;
619 input.pointer = params;
620 params[0].type = ACPI_TYPE_INTEGER;
621 params[0].integer.value = event;
622
Dmitry Torokhov762e1a22010-08-26 00:15:14 -0700623 list_for_each(p, &wmi_block_list) {
Carlos Corbachobff431e2008-02-05 02:17:04 +0000624 wblock = list_entry(p, struct wmi_block, list);
625 gblock = &wblock->gblock;
626
627 if ((gblock->flags & ACPI_WMI_EVENT) &&
628 (gblock->notify_id == event))
629 return acpi_evaluate_object(wblock->handle, "_WED",
630 &input, out);
631 }
632
633 return AE_NOT_FOUND;
634}
635EXPORT_SYMBOL_GPL(wmi_get_event_data);
636
637/**
638 * wmi_has_guid - Check if a GUID is available
639 * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
640 *
641 * Check if a given GUID is defined by _WDG
642 */
643bool wmi_has_guid(const char *guid_string)
644{
645 return find_guid(guid_string, NULL);
646}
647EXPORT_SYMBOL_GPL(wmi_has_guid);
648
649/*
Matthew Garrett1caab3c2009-11-04 14:17:53 -0500650 * sysfs interface
651 */
652static ssize_t show_modalias(struct device *dev, struct device_attribute *attr,
653 char *buf)
654{
655 char guid_string[37];
656 struct wmi_block *wblock;
657
658 wblock = dev_get_drvdata(dev);
659 if (!wblock)
660 return -ENOMEM;
661
662 wmi_gtoa(wblock->gblock.guid, guid_string);
663
664 return sprintf(buf, "wmi:%s\n", guid_string);
665}
666static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL);
667
668static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env)
669{
670 char guid_string[37];
671
672 struct wmi_block *wblock;
673
674 if (add_uevent_var(env, "MODALIAS="))
675 return -ENOMEM;
676
677 wblock = dev_get_drvdata(dev);
678 if (!wblock)
679 return -ENOMEM;
680
681 wmi_gtoa(wblock->gblock.guid, guid_string);
682
683 strcpy(&env->buf[env->buflen - 1], "wmi:");
684 memcpy(&env->buf[env->buflen - 1 + 4], guid_string, 36);
685 env->buflen += 40;
686
687 return 0;
688}
689
690static void wmi_dev_free(struct device *dev)
691{
692 kfree(dev);
693}
694
695static struct class wmi_class = {
696 .name = "wmi",
697 .dev_release = wmi_dev_free,
698 .dev_uevent = wmi_dev_uevent,
699};
700
701static int wmi_create_devs(void)
702{
703 int result;
704 char guid_string[37];
705 struct guid_block *gblock;
706 struct wmi_block *wblock;
707 struct list_head *p;
708 struct device *guid_dev;
709
710 /* Create devices for all the GUIDs */
Dmitry Torokhov762e1a22010-08-26 00:15:14 -0700711 list_for_each(p, &wmi_block_list) {
Matthew Garrett1caab3c2009-11-04 14:17:53 -0500712 wblock = list_entry(p, struct wmi_block, list);
713
714 guid_dev = kzalloc(sizeof(struct device), GFP_KERNEL);
715 if (!guid_dev)
716 return -ENOMEM;
717
718 wblock->dev = guid_dev;
719
720 guid_dev->class = &wmi_class;
721 dev_set_drvdata(guid_dev, wblock);
722
723 gblock = &wblock->gblock;
724
725 wmi_gtoa(gblock->guid, guid_string);
726 dev_set_name(guid_dev, guid_string);
727
728 result = device_register(guid_dev);
729 if (result)
730 return result;
731
732 result = device_create_file(guid_dev, &dev_attr_modalias);
733 if (result)
734 return result;
735 }
736
737 return 0;
738}
739
740static void wmi_remove_devs(void)
741{
742 struct guid_block *gblock;
743 struct wmi_block *wblock;
744 struct list_head *p;
745 struct device *guid_dev;
746
747 /* Delete devices for all the GUIDs */
Dmitry Torokhov762e1a22010-08-26 00:15:14 -0700748 list_for_each(p, &wmi_block_list) {
Matthew Garrett1caab3c2009-11-04 14:17:53 -0500749 wblock = list_entry(p, struct wmi_block, list);
750
751 guid_dev = wblock->dev;
752 gblock = &wblock->gblock;
753
754 device_remove_file(guid_dev, &dev_attr_modalias);
755
756 device_unregister(guid_dev);
757 }
758}
759
760static void wmi_class_exit(void)
761{
762 wmi_remove_devs();
763 class_unregister(&wmi_class);
764}
765
766static int wmi_class_init(void)
767{
768 int ret;
769
770 ret = class_register(&wmi_class);
771 if (ret)
772 return ret;
773
774 ret = wmi_create_devs();
775 if (ret)
776 wmi_class_exit();
777
778 return ret;
779}
780
Carlos Corbachod1f9e492009-12-26 19:14:59 +0000781static bool guid_already_parsed(const char *guid_string)
782{
783 struct guid_block *gblock;
784 struct wmi_block *wblock;
785 struct list_head *p;
786
Dmitry Torokhov762e1a22010-08-26 00:15:14 -0700787 list_for_each(p, &wmi_block_list) {
Carlos Corbachod1f9e492009-12-26 19:14:59 +0000788 wblock = list_entry(p, struct wmi_block, list);
789 gblock = &wblock->gblock;
790
791 if (strncmp(gblock->guid, guid_string, 16) == 0)
792 return true;
793 }
794 return false;
795}
796
Dmitry Torokhov2d5ab552010-08-26 00:14:48 -0700797static void free_wmi_blocks(void)
798{
799 struct wmi_block *wblock, *next;
800
Dmitry Torokhov762e1a22010-08-26 00:15:14 -0700801 list_for_each_entry_safe(wblock, next, &wmi_block_list, list) {
Dmitry Torokhov2d5ab552010-08-26 00:14:48 -0700802 list_del(&wblock->list);
803 kfree(wblock);
804 }
805}
806
Matthew Garrett1caab3c2009-11-04 14:17:53 -0500807/*
Carlos Corbachobff431e2008-02-05 02:17:04 +0000808 * Parse the _WDG method for the GUID data blocks
809 */
Thomas Renninger925b1082010-07-16 13:11:36 +0200810static acpi_status parse_wdg(acpi_handle handle)
Carlos Corbachobff431e2008-02-05 02:17:04 +0000811{
812 struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
813 union acpi_object *obj;
Dmitry Torokhov37830662010-08-26 00:15:09 -0700814 const struct guid_block *gblock;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000815 struct wmi_block *wblock;
Carlos Corbachod1f9e492009-12-26 19:14:59 +0000816 char guid_string[37];
Carlos Corbachobff431e2008-02-05 02:17:04 +0000817 acpi_status status;
818 u32 i, total;
819
820 status = acpi_evaluate_object(handle, "_WDG", NULL, &out);
821
822 if (ACPI_FAILURE(status))
823 return status;
824
825 obj = (union acpi_object *) out.pointer;
Dmitry Torokhov3d2c63e2010-08-26 00:15:03 -0700826 if (!obj)
827 return AE_ERROR;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000828
Dmitry Torokhov64ed0ab2010-08-26 00:14:58 -0700829 if (obj->type != ACPI_TYPE_BUFFER) {
830 status = AE_ERROR;
831 goto out_free_pointer;
832 }
Carlos Corbachobff431e2008-02-05 02:17:04 +0000833
Dmitry Torokhov37830662010-08-26 00:15:09 -0700834 gblock = (const struct guid_block *)obj->buffer.pointer;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000835 total = obj->buffer.length / sizeof(struct guid_block);
836
Carlos Corbachobff431e2008-02-05 02:17:04 +0000837 for (i = 0; i < total; i++) {
Carlos Corbachod1f9e492009-12-26 19:14:59 +0000838 /*
839 Some WMI devices, like those for nVidia hooks, have a
840 duplicate GUID. It's not clear what we should do in this
841 case yet, so for now, we'll just ignore the duplicate.
842 Anyone who wants to add support for that device can come
843 up with a better workaround for the mess then.
844 */
845 if (guid_already_parsed(gblock[i].guid) == true) {
846 wmi_gtoa(gblock[i].guid, guid_string);
Dmitry Torokhov8e075142010-08-26 00:15:19 -0700847 pr_info("Skipping duplicate GUID %s\n", guid_string);
Carlos Corbachod1f9e492009-12-26 19:14:59 +0000848 continue;
849 }
Thomas Renningera929aae2010-05-03 15:30:17 +0200850 if (debug_dump_wdg)
851 wmi_dump_wdg(&gblock[i]);
852
Carlos Corbachobff431e2008-02-05 02:17:04 +0000853 wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL);
Axel Lina5167c52010-06-03 11:45:45 +0800854 if (!wblock) {
855 status = AE_NO_MEMORY;
Dmitry Torokhov37830662010-08-26 00:15:09 -0700856 goto out_free_pointer;
Axel Lina5167c52010-06-03 11:45:45 +0800857 }
Carlos Corbachobff431e2008-02-05 02:17:04 +0000858
859 wblock->gblock = gblock[i];
860 wblock->handle = handle;
Thomas Renningerfc3155b2010-05-03 15:30:15 +0200861 if (debug_event) {
862 wblock->handler = wmi_notify_debug;
Dmitry Torokhov2d5ab552010-08-26 00:14:48 -0700863 wmi_method_enable(wblock, 1);
Thomas Renningerfc3155b2010-05-03 15:30:15 +0200864 }
Dmitry Torokhov762e1a22010-08-26 00:15:14 -0700865 list_add_tail(&wblock->list, &wmi_block_list);
Carlos Corbachobff431e2008-02-05 02:17:04 +0000866 }
867
Axel Lina5167c52010-06-03 11:45:45 +0800868out_free_pointer:
869 kfree(out.pointer);
Carlos Corbachobff431e2008-02-05 02:17:04 +0000870
Dmitry Torokhov2d5ab552010-08-26 00:14:48 -0700871 if (ACPI_FAILURE(status))
872 free_wmi_blocks();
873
Carlos Corbachobff431e2008-02-05 02:17:04 +0000874 return status;
875}
876
877/*
878 * WMI can have EmbeddedControl access regions. In which case, we just want to
879 * hand these off to the EC driver.
880 */
881static acpi_status
882acpi_wmi_ec_space_handler(u32 function, acpi_physical_address address,
Lin Ming439913f2010-01-28 10:53:19 +0800883 u32 bits, u64 *value,
Carlos Corbachobff431e2008-02-05 02:17:04 +0000884 void *handler_context, void *region_context)
885{
886 int result = 0, i = 0;
887 u8 temp = 0;
888
889 if ((address > 0xFF) || !value)
890 return AE_BAD_PARAMETER;
891
892 if (function != ACPI_READ && function != ACPI_WRITE)
893 return AE_BAD_PARAMETER;
894
895 if (bits != 8)
896 return AE_BAD_PARAMETER;
897
898 if (function == ACPI_READ) {
899 result = ec_read(address, &temp);
Lin Ming439913f2010-01-28 10:53:19 +0800900 (*value) |= ((u64)temp) << i;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000901 } else {
902 temp = 0xff & ((*value) >> i);
903 result = ec_write(address, temp);
904 }
905
906 switch (result) {
907 case -EINVAL:
908 return AE_BAD_PARAMETER;
909 break;
910 case -ENODEV:
911 return AE_NOT_FOUND;
912 break;
913 case -ETIME:
914 return AE_TIME;
915 break;
916 default:
917 return AE_OK;
918 }
919}
920
Bjorn Helgaasf61bb932009-04-07 15:37:37 +0000921static void acpi_wmi_notify(struct acpi_device *device, u32 event)
Carlos Corbachobff431e2008-02-05 02:17:04 +0000922{
923 struct guid_block *block;
924 struct wmi_block *wblock;
925 struct list_head *p;
Thomas Renninger77153482010-05-03 15:30:16 +0200926 char guid_string[37];
Carlos Corbachobff431e2008-02-05 02:17:04 +0000927
Dmitry Torokhov762e1a22010-08-26 00:15:14 -0700928 list_for_each(p, &wmi_block_list) {
Carlos Corbachobff431e2008-02-05 02:17:04 +0000929 wblock = list_entry(p, struct wmi_block, list);
930 block = &wblock->gblock;
931
932 if ((block->flags & ACPI_WMI_EVENT) &&
933 (block->notify_id == event)) {
934 if (wblock->handler)
935 wblock->handler(event, wblock->handler_data);
Thomas Renninger77153482010-05-03 15:30:16 +0200936 if (debug_event) {
937 wmi_gtoa(wblock->gblock.guid, guid_string);
Dmitry Torokhov8e075142010-08-26 00:15:19 -0700938 pr_info("DEBUG Event GUID: %s\n", guid_string);
Thomas Renninger77153482010-05-03 15:30:16 +0200939 }
Carlos Corbachobff431e2008-02-05 02:17:04 +0000940
941 acpi_bus_generate_netlink_event(
Kay Sievers07944692008-10-30 01:18:59 +0100942 device->pnp.device_class, dev_name(&device->dev),
Carlos Corbachobff431e2008-02-05 02:17:04 +0000943 event, 0);
944 break;
945 }
946 }
947}
948
949static int acpi_wmi_remove(struct acpi_device *device, int type)
950{
Carlos Corbachobff431e2008-02-05 02:17:04 +0000951 acpi_remove_address_space_handler(device->handle,
952 ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler);
953
954 return 0;
955}
956
Thomas Renninger925b1082010-07-16 13:11:36 +0200957static int acpi_wmi_add(struct acpi_device *device)
Carlos Corbachobff431e2008-02-05 02:17:04 +0000958{
959 acpi_status status;
960 int result = 0;
961
Carlos Corbachobff431e2008-02-05 02:17:04 +0000962 status = acpi_install_address_space_handler(device->handle,
963 ACPI_ADR_SPACE_EC,
964 &acpi_wmi_ec_space_handler,
965 NULL, NULL);
Dmitry Torokhov5212cd62010-08-26 00:14:42 -0700966 if (ACPI_FAILURE(status)) {
Dmitry Torokhov8e075142010-08-26 00:15:19 -0700967 pr_err("Error installing EC region handler\n");
Carlos Corbachobff431e2008-02-05 02:17:04 +0000968 return -ENODEV;
Dmitry Torokhov5212cd62010-08-26 00:14:42 -0700969 }
Carlos Corbachobff431e2008-02-05 02:17:04 +0000970
971 status = parse_wdg(device->handle);
972 if (ACPI_FAILURE(status)) {
Dmitry Torokhov5212cd62010-08-26 00:14:42 -0700973 acpi_remove_address_space_handler(device->handle,
974 ACPI_ADR_SPACE_EC,
975 &acpi_wmi_ec_space_handler);
Dmitry Torokhov8e075142010-08-26 00:15:19 -0700976 pr_err("Failed to parse WDG method\n");
Carlos Corbachobff431e2008-02-05 02:17:04 +0000977 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
987 if (acpi_disabled)
988 return -ENODEV;
989
Carlos Corbachobff431e2008-02-05 02:17:04 +0000990 result = acpi_bus_register_driver(&acpi_wmi_driver);
Carlos Corbachobff431e2008-02-05 02:17:04 +0000991 if (result < 0) {
Dmitry Torokhov8e075142010-08-26 00:15:19 -0700992 pr_err("Error loading mapper\n");
Matthew Garrett1caab3c2009-11-04 14:17:53 -0500993 return -ENODEV;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000994 }
995
Matthew Garrett1caab3c2009-11-04 14:17:53 -0500996 result = wmi_class_init();
997 if (result) {
998 acpi_bus_unregister_driver(&acpi_wmi_driver);
999 return result;
1000 }
1001
Dmitry Torokhov8e075142010-08-26 00:15:19 -07001002 pr_info("Mapper loaded\n");
Matthew Garrett1caab3c2009-11-04 14:17:53 -05001003
Dmitry Torokhov8e075142010-08-26 00:15:19 -07001004 return 0;
Carlos Corbachobff431e2008-02-05 02:17:04 +00001005}
1006
1007static void __exit acpi_wmi_exit(void)
1008{
Matthew Garrett1caab3c2009-11-04 14:17:53 -05001009 wmi_class_exit();
Carlos Corbachobff431e2008-02-05 02:17:04 +00001010 acpi_bus_unregister_driver(&acpi_wmi_driver);
Dmitry Torokhov2d5ab552010-08-26 00:14:48 -07001011 free_wmi_blocks();
Carlos Corbachobff431e2008-02-05 02:17:04 +00001012
Dmitry Torokhov8e075142010-08-26 00:15:19 -07001013 pr_info("Mapper unloaded\n");
Carlos Corbachobff431e2008-02-05 02:17:04 +00001014}
1015
1016subsys_initcall(acpi_wmi_init);
1017module_exit(acpi_wmi_exit);