blob: b104302fea0a1e9dbc4a91f82290a185ad244e00 [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>
36#include <acpi/acpi_bus.h>
37#include <acpi/acpi_drivers.h>
38
39ACPI_MODULE_NAME("wmi");
40MODULE_AUTHOR("Carlos Corbacho");
41MODULE_DESCRIPTION("ACPI-WMI Mapping Driver");
42MODULE_LICENSE("GPL");
43
44#define ACPI_WMI_CLASS "wmi"
45
Carlos Corbachobff431e2008-02-05 02:17:04 +000046#define PREFIX "ACPI: WMI: "
47
48static DEFINE_MUTEX(wmi_data_lock);
49
50struct guid_block {
51 char guid[16];
52 union {
53 char object_id[2];
54 struct {
55 unsigned char notify_id;
56 unsigned char reserved;
57 };
58 };
59 u8 instance_count;
60 u8 flags;
61};
62
63struct wmi_block {
64 struct list_head list;
65 struct guid_block gblock;
66 acpi_handle handle;
67 wmi_notify_handler handler;
68 void *handler_data;
Matthew Garrett1caab3c2009-11-04 14:17:53 -050069 struct device *dev;
Carlos Corbachobff431e2008-02-05 02:17:04 +000070};
71
72static struct wmi_block wmi_blocks;
73
74/*
75 * If the GUID data block is marked as expensive, we must enable and
76 * explicitily disable data collection.
77 */
78#define ACPI_WMI_EXPENSIVE 0x1
79#define ACPI_WMI_METHOD 0x2 /* GUID is a method */
80#define ACPI_WMI_STRING 0x4 /* GUID takes & returns a string */
81#define ACPI_WMI_EVENT 0x8 /* GUID is an event */
82
83static int acpi_wmi_remove(struct acpi_device *device, int type);
84static int acpi_wmi_add(struct acpi_device *device);
Bjorn Helgaasf61bb932009-04-07 15:37:37 +000085static void acpi_wmi_notify(struct acpi_device *device, u32 event);
Carlos Corbachobff431e2008-02-05 02:17:04 +000086
87static const struct acpi_device_id wmi_device_ids[] = {
88 {"PNP0C14", 0},
89 {"pnp0c14", 0},
90 {"", 0},
91};
92MODULE_DEVICE_TABLE(acpi, wmi_device_ids);
93
94static struct acpi_driver acpi_wmi_driver = {
95 .name = "wmi",
96 .class = ACPI_WMI_CLASS,
97 .ids = wmi_device_ids,
98 .ops = {
99 .add = acpi_wmi_add,
100 .remove = acpi_wmi_remove,
Bjorn Helgaasf61bb932009-04-07 15:37:37 +0000101 .notify = acpi_wmi_notify,
Carlos Corbachobff431e2008-02-05 02:17:04 +0000102 },
103};
104
105/*
106 * GUID parsing functions
107 */
108
109/**
110 * wmi_parse_hexbyte - Convert a ASCII hex number to a byte
111 * @src: Pointer to at least 2 characters to convert.
112 *
113 * Convert a two character ASCII hex string to a number.
114 *
115 * Return: 0-255 Success, the byte was parsed correctly
116 * -1 Error, an invalid character was supplied
117 */
118static int wmi_parse_hexbyte(const u8 *src)
119{
120 unsigned int x; /* For correct wrapping */
121 int h;
122
123 /* high part */
124 x = src[0];
125 if (x - '0' <= '9' - '0') {
126 h = x - '0';
127 } else if (x - 'a' <= 'f' - 'a') {
128 h = x - 'a' + 10;
129 } else if (x - 'A' <= 'F' - 'A') {
130 h = x - 'A' + 10;
131 } else {
132 return -1;
133 }
134 h <<= 4;
135
136 /* low part */
137 x = src[1];
138 if (x - '0' <= '9' - '0')
139 return h | (x - '0');
140 if (x - 'a' <= 'f' - 'a')
141 return h | (x - 'a' + 10);
142 if (x - 'A' <= 'F' - 'A')
143 return h | (x - 'A' + 10);
144 return -1;
145}
146
147/**
148 * wmi_swap_bytes - Rearrange GUID bytes to match GUID binary
149 * @src: Memory block holding binary GUID (16 bytes)
150 * @dest: Memory block to hold byte swapped binary GUID (16 bytes)
151 *
152 * Byte swap a binary GUID to match it's real GUID value
153 */
154static void wmi_swap_bytes(u8 *src, u8 *dest)
155{
156 int i;
157
158 for (i = 0; i <= 3; i++)
159 memcpy(dest + i, src + (3 - i), 1);
160
161 for (i = 0; i <= 1; i++)
162 memcpy(dest + 4 + i, src + (5 - i), 1);
163
164 for (i = 0; i <= 1; i++)
165 memcpy(dest + 6 + i, src + (7 - i), 1);
166
167 memcpy(dest + 8, src + 8, 8);
168}
169
170/**
171 * wmi_parse_guid - Convert GUID from ASCII to binary
172 * @src: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
173 * @dest: Memory block to hold binary GUID (16 bytes)
174 *
175 * N.B. The GUID need not be NULL terminated.
176 *
177 * Return: 'true' @dest contains binary GUID
178 * 'false' @dest contents are undefined
179 */
180static bool wmi_parse_guid(const u8 *src, u8 *dest)
181{
182 static const int size[] = { 4, 2, 2, 2, 6 };
183 int i, j, v;
184
185 if (src[8] != '-' || src[13] != '-' ||
186 src[18] != '-' || src[23] != '-')
187 return false;
188
189 for (j = 0; j < 5; j++, src++) {
190 for (i = 0; i < size[j]; i++, src += 2, *dest++ = v) {
191 v = wmi_parse_hexbyte(src);
192 if (v < 0)
193 return false;
194 }
195 }
196
197 return true;
198}
199
Matthew Garrett1caab3c2009-11-04 14:17:53 -0500200/*
201 * Convert a raw GUID to the ACII string representation
202 */
203static int wmi_gtoa(const char *in, char *out)
204{
205 int i;
206
207 for (i = 3; i >= 0; i--)
208 out += sprintf(out, "%02X", in[i] & 0xFF);
209
210 out += sprintf(out, "-");
211 out += sprintf(out, "%02X", in[5] & 0xFF);
212 out += sprintf(out, "%02X", in[4] & 0xFF);
213 out += sprintf(out, "-");
214 out += sprintf(out, "%02X", in[7] & 0xFF);
215 out += sprintf(out, "%02X", in[6] & 0xFF);
216 out += sprintf(out, "-");
217 out += sprintf(out, "%02X", in[8] & 0xFF);
218 out += sprintf(out, "%02X", in[9] & 0xFF);
219 out += sprintf(out, "-");
220
221 for (i = 10; i <= 15; i++)
222 out += sprintf(out, "%02X", in[i] & 0xFF);
223
224 out = '\0';
225 return 0;
226}
227
Carlos Corbachobff431e2008-02-05 02:17:04 +0000228static bool find_guid(const char *guid_string, struct wmi_block **out)
229{
230 char tmp[16], guid_input[16];
231 struct wmi_block *wblock;
232 struct guid_block *block;
233 struct list_head *p;
234
235 wmi_parse_guid(guid_string, tmp);
236 wmi_swap_bytes(tmp, guid_input);
237
238 list_for_each(p, &wmi_blocks.list) {
239 wblock = list_entry(p, struct wmi_block, list);
240 block = &wblock->gblock;
241
242 if (memcmp(block->guid, guid_input, 16) == 0) {
243 if (out)
244 *out = wblock;
245 return 1;
246 }
247 }
248 return 0;
249}
250
Matthew Garretta66bfa72008-10-08 21:40:32 +0100251static acpi_status wmi_method_enable(struct wmi_block *wblock, int enable)
252{
253 struct guid_block *block = NULL;
254 char method[5];
255 struct acpi_object_list input;
256 union acpi_object params[1];
257 acpi_status status;
258 acpi_handle handle;
259
260 block = &wblock->gblock;
261 handle = wblock->handle;
262
263 if (!block)
264 return AE_NOT_EXIST;
265
266 input.count = 1;
267 input.pointer = params;
268 params[0].type = ACPI_TYPE_INTEGER;
269 params[0].integer.value = enable;
270
271 snprintf(method, 5, "WE%02X", block->notify_id);
272 status = acpi_evaluate_object(handle, method, &input, NULL);
273
274 if (status != AE_OK && status != AE_NOT_FOUND)
275 return status;
276 else
277 return AE_OK;
278}
279
Carlos Corbachobff431e2008-02-05 02:17:04 +0000280/*
281 * Exported WMI functions
282 */
283/**
284 * wmi_evaluate_method - Evaluate a WMI method
285 * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
286 * @instance: Instance index
287 * @method_id: Method ID to call
288 * &in: Buffer containing input for the method call
289 * &out: Empty buffer to return the method results
290 *
291 * Call an ACPI-WMI method
292 */
293acpi_status wmi_evaluate_method(const char *guid_string, u8 instance,
294u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out)
295{
296 struct guid_block *block = NULL;
297 struct wmi_block *wblock = NULL;
298 acpi_handle handle;
299 acpi_status status;
300 struct acpi_object_list input;
301 union acpi_object params[3];
Costantino Leandrof3d83e22009-08-26 14:29:28 -0700302 char method[5] = "WM";
Carlos Corbachobff431e2008-02-05 02:17:04 +0000303
304 if (!find_guid(guid_string, &wblock))
Lin Ming08237972008-08-08 11:57:11 +0800305 return AE_ERROR;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000306
307 block = &wblock->gblock;
308 handle = wblock->handle;
309
Al Viroe6bafba2008-02-13 04:03:25 +0000310 if (!(block->flags & ACPI_WMI_METHOD))
Carlos Corbachobff431e2008-02-05 02:17:04 +0000311 return AE_BAD_DATA;
312
313 if (block->instance_count < instance)
314 return AE_BAD_PARAMETER;
315
316 input.count = 2;
317 input.pointer = params;
318 params[0].type = ACPI_TYPE_INTEGER;
319 params[0].integer.value = instance;
320 params[1].type = ACPI_TYPE_INTEGER;
321 params[1].integer.value = method_id;
322
323 if (in) {
324 input.count = 3;
325
326 if (block->flags & ACPI_WMI_STRING) {
327 params[2].type = ACPI_TYPE_STRING;
328 } else {
329 params[2].type = ACPI_TYPE_BUFFER;
330 }
331 params[2].buffer.length = in->length;
332 params[2].buffer.pointer = in->pointer;
333 }
334
335 strncat(method, block->object_id, 2);
336
337 status = acpi_evaluate_object(handle, method, &input, out);
338
339 return status;
340}
341EXPORT_SYMBOL_GPL(wmi_evaluate_method);
342
343/**
344 * wmi_query_block - Return contents of a WMI block
345 * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
346 * @instance: Instance index
347 * &out: Empty buffer to return the contents of the data block to
348 *
349 * Return the contents of an ACPI-WMI data block to a buffer
350 */
351acpi_status wmi_query_block(const char *guid_string, u8 instance,
352struct acpi_buffer *out)
353{
354 struct guid_block *block = NULL;
355 struct wmi_block *wblock = NULL;
Carlos Corbachoa527f2d2008-02-24 13:34:34 +0000356 acpi_handle handle, wc_handle;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000357 acpi_status status, wc_status = AE_ERROR;
358 struct acpi_object_list input, wc_input;
359 union acpi_object wc_params[1], wq_params[1];
Costantino Leandrof3d83e22009-08-26 14:29:28 -0700360 char method[5];
361 char wc_method[5] = "WC";
Carlos Corbachobff431e2008-02-05 02:17:04 +0000362
363 if (!guid_string || !out)
364 return AE_BAD_PARAMETER;
365
366 if (!find_guid(guid_string, &wblock))
Lin Ming08237972008-08-08 11:57:11 +0800367 return AE_ERROR;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000368
369 block = &wblock->gblock;
370 handle = wblock->handle;
371
372 if (block->instance_count < instance)
373 return AE_BAD_PARAMETER;
374
375 /* Check GUID is a data block */
376 if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD))
Lin Ming08237972008-08-08 11:57:11 +0800377 return AE_ERROR;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000378
379 input.count = 1;
380 input.pointer = wq_params;
381 wq_params[0].type = ACPI_TYPE_INTEGER;
382 wq_params[0].integer.value = instance;
383
384 /*
385 * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method first to
386 * enable collection.
387 */
388 if (block->flags & ACPI_WMI_EXPENSIVE) {
389 wc_input.count = 1;
390 wc_input.pointer = wc_params;
391 wc_params[0].type = ACPI_TYPE_INTEGER;
392 wc_params[0].integer.value = 1;
393
394 strncat(wc_method, block->object_id, 2);
395
396 /*
397 * Some GUIDs break the specification by declaring themselves
398 * expensive, but have no corresponding WCxx method. So we
399 * should not fail if this happens.
400 */
Carlos Corbachoa527f2d2008-02-24 13:34:34 +0000401 wc_status = acpi_get_handle(handle, wc_method, &wc_handle);
402 if (ACPI_SUCCESS(wc_status))
403 wc_status = acpi_evaluate_object(handle, wc_method,
404 &wc_input, NULL);
Carlos Corbachobff431e2008-02-05 02:17:04 +0000405 }
406
407 strcpy(method, "WQ");
408 strncat(method, block->object_id, 2);
409
Carlos Corbachodab36ad2008-08-02 17:28:45 +0100410 status = acpi_evaluate_object(handle, method, &input, out);
Carlos Corbachobff431e2008-02-05 02:17:04 +0000411
412 /*
413 * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method, even if
414 * the WQxx method failed - we should disable collection anyway.
415 */
Carlos Corbachoa527f2d2008-02-24 13:34:34 +0000416 if ((block->flags & ACPI_WMI_EXPENSIVE) && ACPI_SUCCESS(wc_status)) {
Carlos Corbachobff431e2008-02-05 02:17:04 +0000417 wc_params[0].integer.value = 0;
418 status = acpi_evaluate_object(handle,
419 wc_method, &wc_input, NULL);
420 }
421
422 return status;
423}
424EXPORT_SYMBOL_GPL(wmi_query_block);
425
426/**
427 * wmi_set_block - Write to a WMI block
428 * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
429 * @instance: Instance index
430 * &in: Buffer containing new values for the data block
431 *
432 * Write the contents of the input buffer to an ACPI-WMI data block
433 */
434acpi_status wmi_set_block(const char *guid_string, u8 instance,
435const struct acpi_buffer *in)
436{
437 struct guid_block *block = NULL;
438 struct wmi_block *wblock = NULL;
439 acpi_handle handle;
440 struct acpi_object_list input;
441 union acpi_object params[2];
Costantino Leandrof3d83e22009-08-26 14:29:28 -0700442 char method[5] = "WS";
Carlos Corbachobff431e2008-02-05 02:17:04 +0000443
444 if (!guid_string || !in)
445 return AE_BAD_DATA;
446
447 if (!find_guid(guid_string, &wblock))
Lin Ming08237972008-08-08 11:57:11 +0800448 return AE_ERROR;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000449
450 block = &wblock->gblock;
451 handle = wblock->handle;
452
453 if (block->instance_count < instance)
454 return AE_BAD_PARAMETER;
455
456 /* Check GUID is a data block */
457 if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD))
Lin Ming08237972008-08-08 11:57:11 +0800458 return AE_ERROR;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000459
460 input.count = 2;
461 input.pointer = params;
462 params[0].type = ACPI_TYPE_INTEGER;
463 params[0].integer.value = instance;
464
465 if (block->flags & ACPI_WMI_STRING) {
466 params[1].type = ACPI_TYPE_STRING;
467 } else {
468 params[1].type = ACPI_TYPE_BUFFER;
469 }
470 params[1].buffer.length = in->length;
471 params[1].buffer.pointer = in->pointer;
472
473 strncat(method, block->object_id, 2);
474
475 return acpi_evaluate_object(handle, method, &input, NULL);
476}
477EXPORT_SYMBOL_GPL(wmi_set_block);
478
479/**
480 * wmi_install_notify_handler - Register handler for WMI events
481 * @handler: Function to handle notifications
482 * @data: Data to be returned to handler when event is fired
483 *
484 * Register a handler for events sent to the ACPI-WMI mapper device.
485 */
486acpi_status wmi_install_notify_handler(const char *guid,
487wmi_notify_handler handler, void *data)
488{
489 struct wmi_block *block;
Matthew Garretta66bfa72008-10-08 21:40:32 +0100490 acpi_status status;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000491
492 if (!guid || !handler)
493 return AE_BAD_PARAMETER;
494
Paul Rollandc03b26a2009-12-30 01:07:40 -0500495 if (!find_guid(guid, &block))
Carlos Corbachobff431e2008-02-05 02:17:04 +0000496 return AE_NOT_EXIST;
497
498 if (block->handler)
499 return AE_ALREADY_ACQUIRED;
500
501 block->handler = handler;
502 block->handler_data = data;
503
Matthew Garretta66bfa72008-10-08 21:40:32 +0100504 status = wmi_method_enable(block, 1);
505
506 return status;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000507}
508EXPORT_SYMBOL_GPL(wmi_install_notify_handler);
509
510/**
511 * wmi_uninstall_notify_handler - Unregister handler for WMI events
512 *
513 * Unregister handler for events sent to the ACPI-WMI mapper device.
514 */
515acpi_status wmi_remove_notify_handler(const char *guid)
516{
517 struct wmi_block *block;
Matthew Garretta66bfa72008-10-08 21:40:32 +0100518 acpi_status status;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000519
520 if (!guid)
521 return AE_BAD_PARAMETER;
522
Paul Rollandc03b26a2009-12-30 01:07:40 -0500523 if (!find_guid(guid, &block))
Carlos Corbachobff431e2008-02-05 02:17:04 +0000524 return AE_NOT_EXIST;
525
526 if (!block->handler)
527 return AE_NULL_ENTRY;
528
Matthew Garretta66bfa72008-10-08 21:40:32 +0100529 status = wmi_method_enable(block, 0);
530
Carlos Corbachobff431e2008-02-05 02:17:04 +0000531 block->handler = NULL;
532 block->handler_data = NULL;
533
Matthew Garretta66bfa72008-10-08 21:40:32 +0100534 return status;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000535}
536EXPORT_SYMBOL_GPL(wmi_remove_notify_handler);
537
538/**
539 * wmi_get_event_data - Get WMI data associated with an event
540 *
Anisse Astier3e9b9882009-12-04 10:10:09 +0100541 * @event: Event to find
542 * @out: Buffer to hold event data. out->pointer should be freed with kfree()
Carlos Corbachobff431e2008-02-05 02:17:04 +0000543 *
544 * Returns extra data associated with an event in WMI.
545 */
546acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out)
547{
548 struct acpi_object_list input;
549 union acpi_object params[1];
550 struct guid_block *gblock;
551 struct wmi_block *wblock;
552 struct list_head *p;
553
554 input.count = 1;
555 input.pointer = params;
556 params[0].type = ACPI_TYPE_INTEGER;
557 params[0].integer.value = event;
558
559 list_for_each(p, &wmi_blocks.list) {
560 wblock = list_entry(p, struct wmi_block, list);
561 gblock = &wblock->gblock;
562
563 if ((gblock->flags & ACPI_WMI_EVENT) &&
564 (gblock->notify_id == event))
565 return acpi_evaluate_object(wblock->handle, "_WED",
566 &input, out);
567 }
568
569 return AE_NOT_FOUND;
570}
571EXPORT_SYMBOL_GPL(wmi_get_event_data);
572
573/**
574 * wmi_has_guid - Check if a GUID is available
575 * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
576 *
577 * Check if a given GUID is defined by _WDG
578 */
579bool wmi_has_guid(const char *guid_string)
580{
581 return find_guid(guid_string, NULL);
582}
583EXPORT_SYMBOL_GPL(wmi_has_guid);
584
585/*
Matthew Garrett1caab3c2009-11-04 14:17:53 -0500586 * sysfs interface
587 */
588static ssize_t show_modalias(struct device *dev, struct device_attribute *attr,
589 char *buf)
590{
591 char guid_string[37];
592 struct wmi_block *wblock;
593
594 wblock = dev_get_drvdata(dev);
595 if (!wblock)
596 return -ENOMEM;
597
598 wmi_gtoa(wblock->gblock.guid, guid_string);
599
600 return sprintf(buf, "wmi:%s\n", guid_string);
601}
602static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL);
603
604static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env)
605{
606 char guid_string[37];
607
608 struct wmi_block *wblock;
609
610 if (add_uevent_var(env, "MODALIAS="))
611 return -ENOMEM;
612
613 wblock = dev_get_drvdata(dev);
614 if (!wblock)
615 return -ENOMEM;
616
617 wmi_gtoa(wblock->gblock.guid, guid_string);
618
619 strcpy(&env->buf[env->buflen - 1], "wmi:");
620 memcpy(&env->buf[env->buflen - 1 + 4], guid_string, 36);
621 env->buflen += 40;
622
623 return 0;
624}
625
626static void wmi_dev_free(struct device *dev)
627{
628 kfree(dev);
629}
630
631static struct class wmi_class = {
632 .name = "wmi",
633 .dev_release = wmi_dev_free,
634 .dev_uevent = wmi_dev_uevent,
635};
636
637static int wmi_create_devs(void)
638{
639 int result;
640 char guid_string[37];
641 struct guid_block *gblock;
642 struct wmi_block *wblock;
643 struct list_head *p;
644 struct device *guid_dev;
645
646 /* Create devices for all the GUIDs */
647 list_for_each(p, &wmi_blocks.list) {
648 wblock = list_entry(p, struct wmi_block, list);
649
650 guid_dev = kzalloc(sizeof(struct device), GFP_KERNEL);
651 if (!guid_dev)
652 return -ENOMEM;
653
654 wblock->dev = guid_dev;
655
656 guid_dev->class = &wmi_class;
657 dev_set_drvdata(guid_dev, wblock);
658
659 gblock = &wblock->gblock;
660
661 wmi_gtoa(gblock->guid, guid_string);
662 dev_set_name(guid_dev, guid_string);
663
664 result = device_register(guid_dev);
665 if (result)
666 return result;
667
668 result = device_create_file(guid_dev, &dev_attr_modalias);
669 if (result)
670 return result;
671 }
672
673 return 0;
674}
675
676static void wmi_remove_devs(void)
677{
678 struct guid_block *gblock;
679 struct wmi_block *wblock;
680 struct list_head *p;
681 struct device *guid_dev;
682
683 /* Delete devices for all the GUIDs */
684 list_for_each(p, &wmi_blocks.list) {
685 wblock = list_entry(p, struct wmi_block, list);
686
687 guid_dev = wblock->dev;
688 gblock = &wblock->gblock;
689
690 device_remove_file(guid_dev, &dev_attr_modalias);
691
692 device_unregister(guid_dev);
693 }
694}
695
696static void wmi_class_exit(void)
697{
698 wmi_remove_devs();
699 class_unregister(&wmi_class);
700}
701
702static int wmi_class_init(void)
703{
704 int ret;
705
706 ret = class_register(&wmi_class);
707 if (ret)
708 return ret;
709
710 ret = wmi_create_devs();
711 if (ret)
712 wmi_class_exit();
713
714 return ret;
715}
716
Carlos Corbachod1f9e492009-12-26 19:14:59 +0000717static bool guid_already_parsed(const char *guid_string)
718{
719 struct guid_block *gblock;
720 struct wmi_block *wblock;
721 struct list_head *p;
722
723 list_for_each(p, &wmi_blocks.list) {
724 wblock = list_entry(p, struct wmi_block, list);
725 gblock = &wblock->gblock;
726
727 if (strncmp(gblock->guid, guid_string, 16) == 0)
728 return true;
729 }
730 return false;
731}
732
Matthew Garrett1caab3c2009-11-04 14:17:53 -0500733/*
Carlos Corbachobff431e2008-02-05 02:17:04 +0000734 * Parse the _WDG method for the GUID data blocks
735 */
736static __init acpi_status parse_wdg(acpi_handle handle)
737{
738 struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
739 union acpi_object *obj;
740 struct guid_block *gblock;
741 struct wmi_block *wblock;
Carlos Corbachod1f9e492009-12-26 19:14:59 +0000742 char guid_string[37];
Carlos Corbachobff431e2008-02-05 02:17:04 +0000743 acpi_status status;
744 u32 i, total;
745
746 status = acpi_evaluate_object(handle, "_WDG", NULL, &out);
747
748 if (ACPI_FAILURE(status))
749 return status;
750
751 obj = (union acpi_object *) out.pointer;
752
753 if (obj->type != ACPI_TYPE_BUFFER)
754 return AE_ERROR;
755
756 total = obj->buffer.length / sizeof(struct guid_block);
757
758 gblock = kzalloc(obj->buffer.length, GFP_KERNEL);
759 if (!gblock)
760 return AE_NO_MEMORY;
761
762 memcpy(gblock, obj->buffer.pointer, obj->buffer.length);
763
764 for (i = 0; i < total; i++) {
Carlos Corbachod1f9e492009-12-26 19:14:59 +0000765 /*
766 Some WMI devices, like those for nVidia hooks, have a
767 duplicate GUID. It's not clear what we should do in this
768 case yet, so for now, we'll just ignore the duplicate.
769 Anyone who wants to add support for that device can come
770 up with a better workaround for the mess then.
771 */
772 if (guid_already_parsed(gblock[i].guid) == true) {
773 wmi_gtoa(gblock[i].guid, guid_string);
774 printk(KERN_INFO PREFIX "Skipping duplicate GUID %s\n",
775 guid_string);
776 continue;
777 }
Carlos Corbachobff431e2008-02-05 02:17:04 +0000778 wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL);
779 if (!wblock)
780 return AE_NO_MEMORY;
781
782 wblock->gblock = gblock[i];
783 wblock->handle = handle;
784 list_add_tail(&wblock->list, &wmi_blocks.list);
785 }
786
787 kfree(out.pointer);
788 kfree(gblock);
789
790 return status;
791}
792
793/*
794 * WMI can have EmbeddedControl access regions. In which case, we just want to
795 * hand these off to the EC driver.
796 */
797static acpi_status
798acpi_wmi_ec_space_handler(u32 function, acpi_physical_address address,
799 u32 bits, acpi_integer * value,
800 void *handler_context, void *region_context)
801{
802 int result = 0, i = 0;
803 u8 temp = 0;
804
805 if ((address > 0xFF) || !value)
806 return AE_BAD_PARAMETER;
807
808 if (function != ACPI_READ && function != ACPI_WRITE)
809 return AE_BAD_PARAMETER;
810
811 if (bits != 8)
812 return AE_BAD_PARAMETER;
813
814 if (function == ACPI_READ) {
815 result = ec_read(address, &temp);
816 (*value) |= ((acpi_integer)temp) << i;
817 } else {
818 temp = 0xff & ((*value) >> i);
819 result = ec_write(address, temp);
820 }
821
822 switch (result) {
823 case -EINVAL:
824 return AE_BAD_PARAMETER;
825 break;
826 case -ENODEV:
827 return AE_NOT_FOUND;
828 break;
829 case -ETIME:
830 return AE_TIME;
831 break;
832 default:
833 return AE_OK;
834 }
835}
836
Bjorn Helgaasf61bb932009-04-07 15:37:37 +0000837static void acpi_wmi_notify(struct acpi_device *device, u32 event)
Carlos Corbachobff431e2008-02-05 02:17:04 +0000838{
839 struct guid_block *block;
840 struct wmi_block *wblock;
841 struct list_head *p;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000842
843 list_for_each(p, &wmi_blocks.list) {
844 wblock = list_entry(p, struct wmi_block, list);
845 block = &wblock->gblock;
846
847 if ((block->flags & ACPI_WMI_EVENT) &&
848 (block->notify_id == event)) {
849 if (wblock->handler)
850 wblock->handler(event, wblock->handler_data);
851
852 acpi_bus_generate_netlink_event(
Kay Sievers07944692008-10-30 01:18:59 +0100853 device->pnp.device_class, dev_name(&device->dev),
Carlos Corbachobff431e2008-02-05 02:17:04 +0000854 event, 0);
855 break;
856 }
857 }
858}
859
860static int acpi_wmi_remove(struct acpi_device *device, int type)
861{
Carlos Corbachobff431e2008-02-05 02:17:04 +0000862 acpi_remove_address_space_handler(device->handle,
863 ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler);
864
865 return 0;
866}
867
868static int __init acpi_wmi_add(struct acpi_device *device)
869{
870 acpi_status status;
871 int result = 0;
872
Carlos Corbachobff431e2008-02-05 02:17:04 +0000873 status = acpi_install_address_space_handler(device->handle,
874 ACPI_ADR_SPACE_EC,
875 &acpi_wmi_ec_space_handler,
876 NULL, NULL);
877 if (ACPI_FAILURE(status))
878 return -ENODEV;
879
880 status = parse_wdg(device->handle);
881 if (ACPI_FAILURE(status)) {
882 printk(KERN_ERR PREFIX "Error installing EC region handler\n");
883 return -ENODEV;
884 }
885
886 return result;
887}
888
889static int __init acpi_wmi_init(void)
890{
Roel Kluinda511992009-03-04 11:55:30 -0800891 int result;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000892
Linus Torvalds96b5a462008-02-11 20:52:01 -0800893 INIT_LIST_HEAD(&wmi_blocks.list);
894
Carlos Corbachobff431e2008-02-05 02:17:04 +0000895 if (acpi_disabled)
896 return -ENODEV;
897
Carlos Corbachobff431e2008-02-05 02:17:04 +0000898 result = acpi_bus_register_driver(&acpi_wmi_driver);
899
900 if (result < 0) {
901 printk(KERN_INFO PREFIX "Error loading mapper\n");
Matthew Garrett1caab3c2009-11-04 14:17:53 -0500902 return -ENODEV;
Carlos Corbachobff431e2008-02-05 02:17:04 +0000903 }
904
Matthew Garrett1caab3c2009-11-04 14:17:53 -0500905 result = wmi_class_init();
906 if (result) {
907 acpi_bus_unregister_driver(&acpi_wmi_driver);
908 return result;
909 }
910
911 printk(KERN_INFO PREFIX "Mapper loaded\n");
912
Carlos Corbachobff431e2008-02-05 02:17:04 +0000913 return result;
914}
915
916static void __exit acpi_wmi_exit(void)
917{
918 struct list_head *p, *tmp;
919 struct wmi_block *wblock;
920
Matthew Garrett1caab3c2009-11-04 14:17:53 -0500921 wmi_class_exit();
922
Carlos Corbachobff431e2008-02-05 02:17:04 +0000923 acpi_bus_unregister_driver(&acpi_wmi_driver);
924
925 list_for_each_safe(p, tmp, &wmi_blocks.list) {
926 wblock = list_entry(p, struct wmi_block, list);
927
928 list_del(p);
929 kfree(wblock);
930 }
931
932 printk(KERN_INFO PREFIX "Mapper unloaded\n");
933}
934
935subsys_initcall(acpi_wmi_init);
936module_exit(acpi_wmi_exit);