]> git.proxmox.com Git - mirror_ubuntu-focal-kernel.git/blame - drivers/platform/x86/wmi.c
WMI: make use of class device's attributres
[mirror_ubuntu-focal-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
8e07514d
DT
30#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
31
bff431e4
CC
32#include <linux/kernel.h>
33#include <linux/init.h>
34#include <linux/types.h>
1caab3c1 35#include <linux/device.h>
bff431e4
CC
36#include <linux/list.h>
37#include <linux/acpi.h>
5a0e3ad6 38#include <linux/slab.h>
bff431e4
CC
39#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
bff431e4 49static DEFINE_MUTEX(wmi_data_lock);
762e1a2f 50static LIST_HEAD(wmi_block_list);
bff431e4
CC
51
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;
1caab3c1 71 struct device *dev;
bff431e4
CC
72};
73
bff431e4
CC
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
a929aae0
TR
89static 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
bff431e4
CC
94static int acpi_wmi_remove(struct acpi_device *device, int type);
95static int acpi_wmi_add(struct acpi_device *device);
f61bb939 96static void acpi_wmi_notify(struct acpi_device *device, u32 event);
bff431e4
CC
97
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,
f61bb939 112 .notify = acpi_wmi_notify,
bff431e4
CC
113 },
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{
bff431e4 131 int h;
392bd8b5 132 int value;
bff431e4
CC
133
134 /* high part */
392bd8b5
AS
135 h = value = hex_to_bin(src[0]);
136 if (value < 0)
bff431e4 137 return -1;
bff431e4
CC
138
139 /* low part */
392bd8b5
AS
140 value = hex_to_bin(src[1]);
141 if (value >= 0)
142 return (h << 4) | value;
bff431e4
CC
143 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
1caab3c1
MG
199/*
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
4e4304d7 223 *out = '\0';
1caab3c1
MG
224 return 0;
225}
226
bff431e4
CC
227static 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
762e1a2f 237 list_for_each(p, &wmi_block_list) {
bff431e4
CC
238 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
a66bfa7a
MG
250static 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
bff431e4
CC
279/*
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];
f3d83e24 301 char method[5] = "WM";
bff431e4
CC
302
303 if (!find_guid(guid_string, &wblock))
08237974 304 return AE_ERROR;
bff431e4
CC
305
306 block = &wblock->gblock;
307 handle = wblock->handle;
308
e6bafba5 309 if (!(block->flags & ACPI_WMI_METHOD))
bff431e4
CC
310 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;
a527f2d7 355 acpi_handle handle, wc_handle;
bff431e4
CC
356 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];
f3d83e24
CL
359 char method[5];
360 char wc_method[5] = "WC";
bff431e4
CC
361
362 if (!guid_string || !out)
363 return AE_BAD_PARAMETER;
364
365 if (!find_guid(guid_string, &wblock))
08237974 366 return AE_ERROR;
bff431e4
CC
367
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))
08237974 376 return AE_ERROR;
bff431e4
CC
377
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 */
a527f2d7
CC
400 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);
bff431e4
CC
404 }
405
406 strcpy(method, "WQ");
407 strncat(method, block->object_id, 2);
408
dab36ad8 409 status = acpi_evaluate_object(handle, method, &input, out);
bff431e4
CC
410
411 /*
412 * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method, even if
413 * the WQxx method failed - we should disable collection anyway.
414 */
a527f2d7 415 if ((block->flags & ACPI_WMI_EXPENSIVE) && ACPI_SUCCESS(wc_status)) {
bff431e4
CC
416 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];
f3d83e24 441 char method[5] = "WS";
bff431e4
CC
442
443 if (!guid_string || !in)
444 return AE_BAD_DATA;
445
446 if (!find_guid(guid_string, &wblock))
08237974 447 return AE_ERROR;
bff431e4
CC
448
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))
08237974 457 return AE_ERROR;
bff431e4
CC
458
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
37830662 478static void wmi_dump_wdg(const struct guid_block *g)
a929aae0
TR
479{
480 char guid_string[37];
481
482 wmi_gtoa(g->guid, guid_string);
8e07514d
DT
483
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);
a929aae0 490 if (g->flags) {
a929aae0 491 if (g->flags & ACPI_WMI_EXPENSIVE)
8e07514d 492 pr_cont("ACPI_WMI_EXPENSIVE ");
a929aae0 493 if (g->flags & ACPI_WMI_METHOD)
8e07514d 494 pr_cont("ACPI_WMI_METHOD ");
a929aae0 495 if (g->flags & ACPI_WMI_STRING)
8e07514d 496 pr_cont("ACPI_WMI_STRING ");
a929aae0 497 if (g->flags & ACPI_WMI_EVENT)
8e07514d 498 pr_cont("ACPI_WMI_EVENT ");
a929aae0 499 }
8e07514d 500 pr_cont("\n");
a929aae0
TR
501
502}
503
fc3155b2
TR
504static void wmi_notify_debug(u32 value, void *context)
505{
506 struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
507 union acpi_object *obj;
1492616a 508 acpi_status status;
fc3155b2 509
1492616a
AL
510 status = wmi_get_event_data(value, &response);
511 if (status != AE_OK) {
8e07514d 512 pr_info("bad event status 0x%x\n", status);
1492616a
AL
513 return;
514 }
fc3155b2
TR
515
516 obj = (union acpi_object *)response.pointer;
517
518 if (!obj)
519 return;
520
8e07514d 521 pr_info("DEBUG Event ");
fc3155b2
TR
522 switch(obj->type) {
523 case ACPI_TYPE_BUFFER:
8e07514d 524 pr_cont("BUFFER_TYPE - length %d\n", obj->buffer.length);
fc3155b2
TR
525 break;
526 case ACPI_TYPE_STRING:
8e07514d 527 pr_cont("STRING_TYPE - %s\n", obj->string.pointer);
fc3155b2
TR
528 break;
529 case ACPI_TYPE_INTEGER:
8e07514d 530 pr_cont("INTEGER_TYPE - %llu\n", obj->integer.value);
fc3155b2
TR
531 break;
532 case ACPI_TYPE_PACKAGE:
8e07514d 533 pr_cont("PACKAGE_TYPE - %d elements\n", obj->package.count);
fc3155b2
TR
534 break;
535 default:
8e07514d 536 pr_cont("object type 0x%X\n", obj->type);
fc3155b2 537 }
1492616a 538 kfree(obj);
fc3155b2
TR
539}
540
bff431e4
CC
541/**
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;
a66bfa7a 552 acpi_status status;
bff431e4
CC
553
554 if (!guid || !handler)
555 return AE_BAD_PARAMETER;
556
c03b26a5 557 if (!find_guid(guid, &block))
bff431e4
CC
558 return AE_NOT_EXIST;
559
fc3155b2 560 if (block->handler && block->handler != wmi_notify_debug)
bff431e4
CC
561 return AE_ALREADY_ACQUIRED;
562
563 block->handler = handler;
564 block->handler_data = data;
565
a66bfa7a
MG
566 status = wmi_method_enable(block, 1);
567
568 return status;
bff431e4
CC
569}
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;
fc3155b2 580 acpi_status status = AE_OK;
bff431e4
CC
581
582 if (!guid)
583 return AE_BAD_PARAMETER;
584
c03b26a5 585 if (!find_guid(guid, &block))
bff431e4
CC
586 return AE_NOT_EXIST;
587
fc3155b2 588 if (!block->handler || block->handler == wmi_notify_debug)
bff431e4
CC
589 return AE_NULL_ENTRY;
590
fc3155b2
TR
591 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 }
a66bfa7a 598 return status;
bff431e4
CC
599}
600EXPORT_SYMBOL_GPL(wmi_remove_notify_handler);
601
602/**
603 * wmi_get_event_data - Get WMI data associated with an event
604 *
3e9b988e
AA
605 * @event: Event to find
606 * @out: Buffer to hold event data. out->pointer should be freed with kfree()
bff431e4
CC
607 *
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
762e1a2f 623 list_for_each(p, &wmi_block_list) {
bff431e4
CC
624 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
1caab3c1
MG
649/*
650 * sysfs interface
651 */
614ef432 652static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
1caab3c1
MG
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}
614ef432
DT
666
667static struct device_attribute wmi_dev_attrs[] = {
668 __ATTR_RO(modalias),
669 __ATTR_NULL
670};
1caab3c1
MG
671
672static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env)
673{
674 char guid_string[37];
675
676 struct wmi_block *wblock;
677
678 if (add_uevent_var(env, "MODALIAS="))
679 return -ENOMEM;
680
681 wblock = dev_get_drvdata(dev);
682 if (!wblock)
683 return -ENOMEM;
684
685 wmi_gtoa(wblock->gblock.guid, guid_string);
686
687 strcpy(&env->buf[env->buflen - 1], "wmi:");
688 memcpy(&env->buf[env->buflen - 1 + 4], guid_string, 36);
689 env->buflen += 40;
690
691 return 0;
692}
693
694static void wmi_dev_free(struct device *dev)
695{
696 kfree(dev);
697}
698
699static struct class wmi_class = {
700 .name = "wmi",
701 .dev_release = wmi_dev_free,
702 .dev_uevent = wmi_dev_uevent,
614ef432 703 .dev_attrs = wmi_dev_attrs,
1caab3c1
MG
704};
705
706static int wmi_create_devs(void)
707{
708 int result;
709 char guid_string[37];
710 struct guid_block *gblock;
711 struct wmi_block *wblock;
712 struct list_head *p;
713 struct device *guid_dev;
714
715 /* Create devices for all the GUIDs */
762e1a2f 716 list_for_each(p, &wmi_block_list) {
1caab3c1
MG
717 wblock = list_entry(p, struct wmi_block, list);
718
719 guid_dev = kzalloc(sizeof(struct device), GFP_KERNEL);
720 if (!guid_dev)
721 return -ENOMEM;
722
723 wblock->dev = guid_dev;
724
725 guid_dev->class = &wmi_class;
726 dev_set_drvdata(guid_dev, wblock);
727
728 gblock = &wblock->gblock;
729
730 wmi_gtoa(gblock->guid, guid_string);
731 dev_set_name(guid_dev, guid_string);
732
733 result = device_register(guid_dev);
734 if (result)
735 return result;
1caab3c1
MG
736 }
737
738 return 0;
739}
740
741static void wmi_remove_devs(void)
742{
743 struct guid_block *gblock;
744 struct wmi_block *wblock;
745 struct list_head *p;
746 struct device *guid_dev;
747
748 /* Delete devices for all the GUIDs */
762e1a2f 749 list_for_each(p, &wmi_block_list) {
1caab3c1
MG
750 wblock = list_entry(p, struct wmi_block, list);
751
752 guid_dev = wblock->dev;
753 gblock = &wblock->gblock;
754
1caab3c1
MG
755 device_unregister(guid_dev);
756 }
757}
758
759static void wmi_class_exit(void)
760{
761 wmi_remove_devs();
762 class_unregister(&wmi_class);
763}
764
765static int wmi_class_init(void)
766{
767 int ret;
768
769 ret = class_register(&wmi_class);
770 if (ret)
771 return ret;
772
773 ret = wmi_create_devs();
774 if (ret)
775 wmi_class_exit();
776
777 return ret;
778}
779
d1f9e497
CC
780static bool guid_already_parsed(const char *guid_string)
781{
782 struct guid_block *gblock;
783 struct wmi_block *wblock;
784 struct list_head *p;
785
762e1a2f 786 list_for_each(p, &wmi_block_list) {
d1f9e497
CC
787 wblock = list_entry(p, struct wmi_block, list);
788 gblock = &wblock->gblock;
789
790 if (strncmp(gblock->guid, guid_string, 16) == 0)
791 return true;
792 }
793 return false;
794}
795
2d5ab555
DT
796static void free_wmi_blocks(void)
797{
798 struct wmi_block *wblock, *next;
799
762e1a2f 800 list_for_each_entry_safe(wblock, next, &wmi_block_list, list) {
2d5ab555
DT
801 list_del(&wblock->list);
802 kfree(wblock);
803 }
804}
805
bff431e4
CC
806/*
807 * Parse the _WDG method for the GUID data blocks
808 */
925b1089 809static acpi_status parse_wdg(acpi_handle handle)
bff431e4
CC
810{
811 struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
812 union acpi_object *obj;
37830662 813 const struct guid_block *gblock;
bff431e4 814 struct wmi_block *wblock;
d1f9e497 815 char guid_string[37];
bff431e4
CC
816 acpi_status status;
817 u32 i, total;
818
819 status = acpi_evaluate_object(handle, "_WDG", NULL, &out);
820
821 if (ACPI_FAILURE(status))
822 return status;
823
824 obj = (union acpi_object *) out.pointer;
3d2c63eb
DT
825 if (!obj)
826 return AE_ERROR;
bff431e4 827
64ed0ab8
DT
828 if (obj->type != ACPI_TYPE_BUFFER) {
829 status = AE_ERROR;
830 goto out_free_pointer;
831 }
bff431e4 832
37830662 833 gblock = (const struct guid_block *)obj->buffer.pointer;
bff431e4
CC
834 total = obj->buffer.length / sizeof(struct guid_block);
835
bff431e4 836 for (i = 0; i < total; i++) {
d1f9e497
CC
837 /*
838 Some WMI devices, like those for nVidia hooks, have a
839 duplicate GUID. It's not clear what we should do in this
840 case yet, so for now, we'll just ignore the duplicate.
841 Anyone who wants to add support for that device can come
842 up with a better workaround for the mess then.
843 */
844 if (guid_already_parsed(gblock[i].guid) == true) {
845 wmi_gtoa(gblock[i].guid, guid_string);
8e07514d 846 pr_info("Skipping duplicate GUID %s\n", guid_string);
d1f9e497
CC
847 continue;
848 }
a929aae0
TR
849 if (debug_dump_wdg)
850 wmi_dump_wdg(&gblock[i]);
851
bff431e4 852 wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL);
a5167c5b
AL
853 if (!wblock) {
854 status = AE_NO_MEMORY;
37830662 855 goto out_free_pointer;
a5167c5b 856 }
bff431e4
CC
857
858 wblock->gblock = gblock[i];
859 wblock->handle = handle;
fc3155b2
TR
860 if (debug_event) {
861 wblock->handler = wmi_notify_debug;
2d5ab555 862 wmi_method_enable(wblock, 1);
fc3155b2 863 }
762e1a2f 864 list_add_tail(&wblock->list, &wmi_block_list);
bff431e4
CC
865 }
866
a5167c5b
AL
867out_free_pointer:
868 kfree(out.pointer);
bff431e4 869
2d5ab555
DT
870 if (ACPI_FAILURE(status))
871 free_wmi_blocks();
872
bff431e4
CC
873 return status;
874}
875
876/*
877 * WMI can have EmbeddedControl access regions. In which case, we just want to
878 * hand these off to the EC driver.
879 */
880static acpi_status
881acpi_wmi_ec_space_handler(u32 function, acpi_physical_address address,
439913ff 882 u32 bits, u64 *value,
bff431e4
CC
883 void *handler_context, void *region_context)
884{
885 int result = 0, i = 0;
886 u8 temp = 0;
887
888 if ((address > 0xFF) || !value)
889 return AE_BAD_PARAMETER;
890
891 if (function != ACPI_READ && function != ACPI_WRITE)
892 return AE_BAD_PARAMETER;
893
894 if (bits != 8)
895 return AE_BAD_PARAMETER;
896
897 if (function == ACPI_READ) {
898 result = ec_read(address, &temp);
439913ff 899 (*value) |= ((u64)temp) << i;
bff431e4
CC
900 } else {
901 temp = 0xff & ((*value) >> i);
902 result = ec_write(address, temp);
903 }
904
905 switch (result) {
906 case -EINVAL:
907 return AE_BAD_PARAMETER;
908 break;
909 case -ENODEV:
910 return AE_NOT_FOUND;
911 break;
912 case -ETIME:
913 return AE_TIME;
914 break;
915 default:
916 return AE_OK;
917 }
918}
919
f61bb939 920static void acpi_wmi_notify(struct acpi_device *device, u32 event)
bff431e4
CC
921{
922 struct guid_block *block;
923 struct wmi_block *wblock;
924 struct list_head *p;
7715348c 925 char guid_string[37];
bff431e4 926
762e1a2f 927 list_for_each(p, &wmi_block_list) {
bff431e4
CC
928 wblock = list_entry(p, struct wmi_block, list);
929 block = &wblock->gblock;
930
931 if ((block->flags & ACPI_WMI_EVENT) &&
932 (block->notify_id == event)) {
933 if (wblock->handler)
934 wblock->handler(event, wblock->handler_data);
7715348c
TR
935 if (debug_event) {
936 wmi_gtoa(wblock->gblock.guid, guid_string);
8e07514d 937 pr_info("DEBUG Event GUID: %s\n", guid_string);
7715348c 938 }
bff431e4
CC
939
940 acpi_bus_generate_netlink_event(
0794469d 941 device->pnp.device_class, dev_name(&device->dev),
bff431e4
CC
942 event, 0);
943 break;
944 }
945 }
946}
947
948static int acpi_wmi_remove(struct acpi_device *device, int type)
949{
bff431e4
CC
950 acpi_remove_address_space_handler(device->handle,
951 ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler);
952
953 return 0;
954}
955
925b1089 956static int acpi_wmi_add(struct acpi_device *device)
bff431e4
CC
957{
958 acpi_status status;
959 int result = 0;
960
bff431e4
CC
961 status = acpi_install_address_space_handler(device->handle,
962 ACPI_ADR_SPACE_EC,
963 &acpi_wmi_ec_space_handler,
964 NULL, NULL);
5212cd67 965 if (ACPI_FAILURE(status)) {
8e07514d 966 pr_err("Error installing EC region handler\n");
bff431e4 967 return -ENODEV;
5212cd67 968 }
bff431e4
CC
969
970 status = parse_wdg(device->handle);
971 if (ACPI_FAILURE(status)) {
5212cd67
DT
972 acpi_remove_address_space_handler(device->handle,
973 ACPI_ADR_SPACE_EC,
974 &acpi_wmi_ec_space_handler);
8e07514d 975 pr_err("Failed to parse WDG method\n");
bff431e4
CC
976 return -ENODEV;
977 }
978
979 return result;
980}
981
982static int __init acpi_wmi_init(void)
983{
da511997 984 int result;
bff431e4
CC
985
986 if (acpi_disabled)
987 return -ENODEV;
988
bff431e4 989 result = acpi_bus_register_driver(&acpi_wmi_driver);
bff431e4 990 if (result < 0) {
8e07514d 991 pr_err("Error loading mapper\n");
1caab3c1
MG
992 return -ENODEV;
993 }
994
995 result = wmi_class_init();
996 if (result) {
997 acpi_bus_unregister_driver(&acpi_wmi_driver);
998 return result;
bff431e4
CC
999 }
1000
8e07514d 1001 pr_info("Mapper loaded\n");
1caab3c1 1002
8e07514d 1003 return 0;
bff431e4
CC
1004}
1005
1006static void __exit acpi_wmi_exit(void)
1007{
1caab3c1 1008 wmi_class_exit();
bff431e4 1009 acpi_bus_unregister_driver(&acpi_wmi_driver);
2d5ab555 1010 free_wmi_blocks();
bff431e4 1011
8e07514d 1012 pr_info("Mapper unloaded\n");
bff431e4
CC
1013}
1014
1015subsys_initcall(acpi_wmi_init);
1016module_exit(acpi_wmi_exit);