]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blame - drivers/platform/x86/wmi.c
platform/x86: wmi: Clean up acpi_wmi_add
[mirror_ubuntu-jammy-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>
7c52d551 39#include <linux/module.h>
538d7eb8 40#include <linux/uuid.h>
bff431e4
CC
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
762e1a2f 49static LIST_HEAD(wmi_block_list);
bff431e4
CC
50
51struct guid_block {
52 char guid[16];
53 union {
54 char object_id[2];
55 struct {
56 unsigned char notify_id;
57 unsigned char reserved;
58 };
59 };
60 u8 instance_count;
61 u8 flags;
62};
63
64struct wmi_block {
65 struct list_head list;
66 struct guid_block gblock;
67 acpi_handle handle;
68 wmi_notify_handler handler;
69 void *handler_data;
c64eefd4 70 struct device dev;
bff431e4
CC
71};
72
bff431e4
CC
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
90ab5ee9 83static bool debug_event;
fc3155b2
TR
84module_param(debug_event, bool, 0444);
85MODULE_PARM_DESC(debug_event,
86 "Log WMI Events [0/1]");
87
90ab5ee9 88static bool debug_dump_wdg;
a929aae0
TR
89module_param(debug_dump_wdg, bool, 0444);
90MODULE_PARM_DESC(debug_dump_wdg,
91 "Dump available WMI interfaces [0/1]");
92
51fac838 93static int acpi_wmi_remove(struct acpi_device *device);
bff431e4 94static int acpi_wmi_add(struct acpi_device *device);
f61bb939 95static void acpi_wmi_notify(struct acpi_device *device, u32 event);
bff431e4
CC
96
97static const struct acpi_device_id wmi_device_ids[] = {
98 {"PNP0C14", 0},
99 {"pnp0c14", 0},
100 {"", 0},
101};
102MODULE_DEVICE_TABLE(acpi, wmi_device_ids);
103
104static struct acpi_driver acpi_wmi_driver = {
105 .name = "wmi",
106 .class = ACPI_WMI_CLASS,
107 .ids = wmi_device_ids,
108 .ops = {
109 .add = acpi_wmi_add,
110 .remove = acpi_wmi_remove,
f61bb939 111 .notify = acpi_wmi_notify,
c64eefd4 112 },
bff431e4
CC
113};
114
115/*
116 * GUID parsing functions
117 */
118
bff431e4
CC
119static bool find_guid(const char *guid_string, struct wmi_block **out)
120{
538d7eb8 121 uuid_le guid_input;
bff431e4
CC
122 struct wmi_block *wblock;
123 struct guid_block *block;
124 struct list_head *p;
125
538d7eb8
AS
126 if (uuid_le_to_bin(guid_string, &guid_input))
127 return false;
bff431e4 128
762e1a2f 129 list_for_each(p, &wmi_block_list) {
bff431e4
CC
130 wblock = list_entry(p, struct wmi_block, list);
131 block = &wblock->gblock;
132
538d7eb8 133 if (memcmp(block->guid, &guid_input, 16) == 0) {
bff431e4
CC
134 if (out)
135 *out = wblock;
097c27fc 136 return true;
bff431e4
CC
137 }
138 }
097c27fc 139 return false;
bff431e4
CC
140}
141
a66bfa7a
MG
142static acpi_status wmi_method_enable(struct wmi_block *wblock, int enable)
143{
144 struct guid_block *block = NULL;
145 char method[5];
a66bfa7a
MG
146 acpi_status status;
147 acpi_handle handle;
148
149 block = &wblock->gblock;
150 handle = wblock->handle;
151
a66bfa7a 152 snprintf(method, 5, "WE%02X", block->notify_id);
8122ab66 153 status = acpi_execute_simple_method(handle, method, enable);
a66bfa7a
MG
154
155 if (status != AE_OK && status != AE_NOT_FOUND)
156 return status;
157 else
158 return AE_OK;
159}
160
bff431e4
CC
161/*
162 * Exported WMI functions
163 */
164/**
165 * wmi_evaluate_method - Evaluate a WMI method
166 * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
167 * @instance: Instance index
168 * @method_id: Method ID to call
169 * &in: Buffer containing input for the method call
170 * &out: Empty buffer to return the method results
171 *
172 * Call an ACPI-WMI method
173 */
174acpi_status wmi_evaluate_method(const char *guid_string, u8 instance,
175u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out)
176{
177 struct guid_block *block = NULL;
178 struct wmi_block *wblock = NULL;
179 acpi_handle handle;
180 acpi_status status;
181 struct acpi_object_list input;
182 union acpi_object params[3];
f3d83e24 183 char method[5] = "WM";
bff431e4
CC
184
185 if (!find_guid(guid_string, &wblock))
08237974 186 return AE_ERROR;
bff431e4
CC
187
188 block = &wblock->gblock;
189 handle = wblock->handle;
190
e6bafba5 191 if (!(block->flags & ACPI_WMI_METHOD))
bff431e4
CC
192 return AE_BAD_DATA;
193
194 if (block->instance_count < instance)
195 return AE_BAD_PARAMETER;
196
197 input.count = 2;
198 input.pointer = params;
199 params[0].type = ACPI_TYPE_INTEGER;
200 params[0].integer.value = instance;
201 params[1].type = ACPI_TYPE_INTEGER;
202 params[1].integer.value = method_id;
203
204 if (in) {
205 input.count = 3;
206
207 if (block->flags & ACPI_WMI_STRING) {
208 params[2].type = ACPI_TYPE_STRING;
209 } else {
210 params[2].type = ACPI_TYPE_BUFFER;
211 }
212 params[2].buffer.length = in->length;
213 params[2].buffer.pointer = in->pointer;
214 }
215
216 strncat(method, block->object_id, 2);
217
218 status = acpi_evaluate_object(handle, method, &input, out);
219
220 return status;
221}
222EXPORT_SYMBOL_GPL(wmi_evaluate_method);
223
224/**
225 * wmi_query_block - Return contents of a WMI block
226 * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
227 * @instance: Instance index
228 * &out: Empty buffer to return the contents of the data block to
229 *
230 * Return the contents of an ACPI-WMI data block to a buffer
231 */
232acpi_status wmi_query_block(const char *guid_string, u8 instance,
233struct acpi_buffer *out)
234{
235 struct guid_block *block = NULL;
236 struct wmi_block *wblock = NULL;
54f14c27 237 acpi_handle handle;
bff431e4 238 acpi_status status, wc_status = AE_ERROR;
8122ab66
ZR
239 struct acpi_object_list input;
240 union acpi_object wq_params[1];
f3d83e24
CL
241 char method[5];
242 char wc_method[5] = "WC";
bff431e4
CC
243
244 if (!guid_string || !out)
245 return AE_BAD_PARAMETER;
246
247 if (!find_guid(guid_string, &wblock))
08237974 248 return AE_ERROR;
bff431e4
CC
249
250 block = &wblock->gblock;
251 handle = wblock->handle;
252
253 if (block->instance_count < instance)
254 return AE_BAD_PARAMETER;
255
256 /* Check GUID is a data block */
257 if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD))
08237974 258 return AE_ERROR;
bff431e4
CC
259
260 input.count = 1;
261 input.pointer = wq_params;
262 wq_params[0].type = ACPI_TYPE_INTEGER;
263 wq_params[0].integer.value = instance;
264
265 /*
266 * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method first to
267 * enable collection.
268 */
269 if (block->flags & ACPI_WMI_EXPENSIVE) {
bff431e4
CC
270 strncat(wc_method, block->object_id, 2);
271
272 /*
273 * Some GUIDs break the specification by declaring themselves
274 * expensive, but have no corresponding WCxx method. So we
275 * should not fail if this happens.
276 */
54f14c27 277 if (acpi_has_method(handle, wc_method))
8122ab66
ZR
278 wc_status = acpi_execute_simple_method(handle,
279 wc_method, 1);
bff431e4
CC
280 }
281
282 strcpy(method, "WQ");
283 strncat(method, block->object_id, 2);
284
dab36ad8 285 status = acpi_evaluate_object(handle, method, &input, out);
bff431e4
CC
286
287 /*
288 * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method, even if
289 * the WQxx method failed - we should disable collection anyway.
290 */
a527f2d7 291 if ((block->flags & ACPI_WMI_EXPENSIVE) && ACPI_SUCCESS(wc_status)) {
8122ab66 292 status = acpi_execute_simple_method(handle, wc_method, 0);
bff431e4
CC
293 }
294
295 return status;
296}
297EXPORT_SYMBOL_GPL(wmi_query_block);
298
299/**
300 * wmi_set_block - Write to a WMI block
301 * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
302 * @instance: Instance index
303 * &in: Buffer containing new values for the data block
304 *
305 * Write the contents of the input buffer to an ACPI-WMI data block
306 */
307acpi_status wmi_set_block(const char *guid_string, u8 instance,
308const struct acpi_buffer *in)
309{
310 struct guid_block *block = NULL;
311 struct wmi_block *wblock = NULL;
312 acpi_handle handle;
313 struct acpi_object_list input;
314 union acpi_object params[2];
f3d83e24 315 char method[5] = "WS";
bff431e4
CC
316
317 if (!guid_string || !in)
318 return AE_BAD_DATA;
319
320 if (!find_guid(guid_string, &wblock))
08237974 321 return AE_ERROR;
bff431e4
CC
322
323 block = &wblock->gblock;
324 handle = wblock->handle;
325
326 if (block->instance_count < instance)
327 return AE_BAD_PARAMETER;
328
329 /* Check GUID is a data block */
330 if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD))
08237974 331 return AE_ERROR;
bff431e4
CC
332
333 input.count = 2;
334 input.pointer = params;
335 params[0].type = ACPI_TYPE_INTEGER;
336 params[0].integer.value = instance;
337
338 if (block->flags & ACPI_WMI_STRING) {
339 params[1].type = ACPI_TYPE_STRING;
340 } else {
341 params[1].type = ACPI_TYPE_BUFFER;
342 }
343 params[1].buffer.length = in->length;
344 params[1].buffer.pointer = in->pointer;
345
346 strncat(method, block->object_id, 2);
347
348 return acpi_evaluate_object(handle, method, &input, NULL);
349}
350EXPORT_SYMBOL_GPL(wmi_set_block);
351
37830662 352static void wmi_dump_wdg(const struct guid_block *g)
a929aae0 353{
85b4e4eb 354 pr_info("%pUL:\n", g->guid);
8e07514d
DT
355 pr_info("\tobject_id: %c%c\n", g->object_id[0], g->object_id[1]);
356 pr_info("\tnotify_id: %02X\n", g->notify_id);
357 pr_info("\treserved: %02X\n", g->reserved);
358 pr_info("\tinstance_count: %d\n", g->instance_count);
dd8e908e 359 pr_info("\tflags: %#x", g->flags);
a929aae0 360 if (g->flags) {
a929aae0 361 if (g->flags & ACPI_WMI_EXPENSIVE)
dd8e908e 362 pr_cont(" ACPI_WMI_EXPENSIVE");
a929aae0 363 if (g->flags & ACPI_WMI_METHOD)
dd8e908e 364 pr_cont(" ACPI_WMI_METHOD");
a929aae0 365 if (g->flags & ACPI_WMI_STRING)
dd8e908e 366 pr_cont(" ACPI_WMI_STRING");
a929aae0 367 if (g->flags & ACPI_WMI_EVENT)
dd8e908e 368 pr_cont(" ACPI_WMI_EVENT");
a929aae0 369 }
8e07514d 370 pr_cont("\n");
a929aae0
TR
371
372}
373
fc3155b2
TR
374static void wmi_notify_debug(u32 value, void *context)
375{
376 struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
377 union acpi_object *obj;
1492616a 378 acpi_status status;
fc3155b2 379
1492616a
AL
380 status = wmi_get_event_data(value, &response);
381 if (status != AE_OK) {
8e07514d 382 pr_info("bad event status 0x%x\n", status);
1492616a
AL
383 return;
384 }
fc3155b2
TR
385
386 obj = (union acpi_object *)response.pointer;
387
388 if (!obj)
389 return;
390
8e07514d 391 pr_info("DEBUG Event ");
fc3155b2
TR
392 switch(obj->type) {
393 case ACPI_TYPE_BUFFER:
8e07514d 394 pr_cont("BUFFER_TYPE - length %d\n", obj->buffer.length);
fc3155b2
TR
395 break;
396 case ACPI_TYPE_STRING:
8e07514d 397 pr_cont("STRING_TYPE - %s\n", obj->string.pointer);
fc3155b2
TR
398 break;
399 case ACPI_TYPE_INTEGER:
8e07514d 400 pr_cont("INTEGER_TYPE - %llu\n", obj->integer.value);
fc3155b2
TR
401 break;
402 case ACPI_TYPE_PACKAGE:
8e07514d 403 pr_cont("PACKAGE_TYPE - %d elements\n", obj->package.count);
fc3155b2
TR
404 break;
405 default:
8e07514d 406 pr_cont("object type 0x%X\n", obj->type);
fc3155b2 407 }
1492616a 408 kfree(obj);
fc3155b2
TR
409}
410
bff431e4
CC
411/**
412 * wmi_install_notify_handler - Register handler for WMI events
413 * @handler: Function to handle notifications
414 * @data: Data to be returned to handler when event is fired
415 *
416 * Register a handler for events sent to the ACPI-WMI mapper device.
417 */
418acpi_status wmi_install_notify_handler(const char *guid,
419wmi_notify_handler handler, void *data)
420{
421 struct wmi_block *block;
58f6425e 422 acpi_status status = AE_NOT_EXIST;
538d7eb8 423 uuid_le guid_input;
58f6425e 424 struct list_head *p;
bff431e4
CC
425
426 if (!guid || !handler)
427 return AE_BAD_PARAMETER;
428
538d7eb8
AS
429 if (uuid_le_to_bin(guid, &guid_input))
430 return AE_BAD_PARAMETER;
bff431e4 431
58f6425e
CK
432 list_for_each(p, &wmi_block_list) {
433 acpi_status wmi_status;
434 block = list_entry(p, struct wmi_block, list);
435
538d7eb8 436 if (memcmp(block->gblock.guid, &guid_input, 16) == 0) {
58f6425e
CK
437 if (block->handler &&
438 block->handler != wmi_notify_debug)
439 return AE_ALREADY_ACQUIRED;
bff431e4 440
58f6425e
CK
441 block->handler = handler;
442 block->handler_data = data;
bff431e4 443
58f6425e
CK
444 wmi_status = wmi_method_enable(block, 1);
445 if ((wmi_status != AE_OK) ||
446 ((wmi_status == AE_OK) && (status == AE_NOT_EXIST)))
447 status = wmi_status;
448 }
449 }
a66bfa7a
MG
450
451 return status;
bff431e4
CC
452}
453EXPORT_SYMBOL_GPL(wmi_install_notify_handler);
454
455/**
456 * wmi_uninstall_notify_handler - Unregister handler for WMI events
457 *
458 * Unregister handler for events sent to the ACPI-WMI mapper device.
459 */
460acpi_status wmi_remove_notify_handler(const char *guid)
461{
462 struct wmi_block *block;
58f6425e 463 acpi_status status = AE_NOT_EXIST;
538d7eb8 464 uuid_le guid_input;
58f6425e 465 struct list_head *p;
bff431e4
CC
466
467 if (!guid)
468 return AE_BAD_PARAMETER;
469
538d7eb8
AS
470 if (uuid_le_to_bin(guid, &guid_input))
471 return AE_BAD_PARAMETER;
bff431e4 472
58f6425e
CK
473 list_for_each(p, &wmi_block_list) {
474 acpi_status wmi_status;
475 block = list_entry(p, struct wmi_block, list);
476
538d7eb8 477 if (memcmp(block->gblock.guid, &guid_input, 16) == 0) {
58f6425e
CK
478 if (!block->handler ||
479 block->handler == wmi_notify_debug)
480 return AE_NULL_ENTRY;
481
482 if (debug_event) {
483 block->handler = wmi_notify_debug;
484 status = AE_OK;
485 } else {
486 wmi_status = wmi_method_enable(block, 0);
487 block->handler = NULL;
488 block->handler_data = NULL;
489 if ((wmi_status != AE_OK) ||
490 ((wmi_status == AE_OK) &&
491 (status == AE_NOT_EXIST)))
492 status = wmi_status;
493 }
494 }
fc3155b2 495 }
58f6425e 496
a66bfa7a 497 return status;
bff431e4
CC
498}
499EXPORT_SYMBOL_GPL(wmi_remove_notify_handler);
500
501/**
502 * wmi_get_event_data - Get WMI data associated with an event
503 *
3e9b988e
AA
504 * @event: Event to find
505 * @out: Buffer to hold event data. out->pointer should be freed with kfree()
bff431e4
CC
506 *
507 * Returns extra data associated with an event in WMI.
508 */
509acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out)
510{
511 struct acpi_object_list input;
512 union acpi_object params[1];
513 struct guid_block *gblock;
514 struct wmi_block *wblock;
515 struct list_head *p;
516
517 input.count = 1;
518 input.pointer = params;
519 params[0].type = ACPI_TYPE_INTEGER;
520 params[0].integer.value = event;
521
762e1a2f 522 list_for_each(p, &wmi_block_list) {
bff431e4
CC
523 wblock = list_entry(p, struct wmi_block, list);
524 gblock = &wblock->gblock;
525
526 if ((gblock->flags & ACPI_WMI_EVENT) &&
527 (gblock->notify_id == event))
528 return acpi_evaluate_object(wblock->handle, "_WED",
529 &input, out);
530 }
531
532 return AE_NOT_FOUND;
533}
534EXPORT_SYMBOL_GPL(wmi_get_event_data);
535
536/**
537 * wmi_has_guid - Check if a GUID is available
538 * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
539 *
540 * Check if a given GUID is defined by _WDG
541 */
542bool wmi_has_guid(const char *guid_string)
543{
544 return find_guid(guid_string, NULL);
545}
546EXPORT_SYMBOL_GPL(wmi_has_guid);
547
1caab3c1
MG
548/*
549 * sysfs interface
550 */
614ef432 551static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
1caab3c1
MG
552 char *buf)
553{
1caab3c1
MG
554 struct wmi_block *wblock;
555
556 wblock = dev_get_drvdata(dev);
a80e1053
PB
557 if (!wblock) {
558 strcat(buf, "\n");
559 return strlen(buf);
560 }
1caab3c1 561
85b4e4eb 562 return sprintf(buf, "wmi:%pUL\n", wblock->gblock.guid);
1caab3c1 563}
e80b89a5 564static DEVICE_ATTR_RO(modalias);
614ef432 565
e80b89a5
GKH
566static struct attribute *wmi_attrs[] = {
567 &dev_attr_modalias.attr,
568 NULL,
614ef432 569};
e80b89a5 570ATTRIBUTE_GROUPS(wmi);
1caab3c1
MG
571
572static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env)
573{
574 char guid_string[37];
575
576 struct wmi_block *wblock;
577
578 if (add_uevent_var(env, "MODALIAS="))
579 return -ENOMEM;
580
581 wblock = dev_get_drvdata(dev);
582 if (!wblock)
583 return -ENOMEM;
584
85b4e4eb 585 sprintf(guid_string, "%pUL", wblock->gblock.guid);
1caab3c1
MG
586
587 strcpy(&env->buf[env->buflen - 1], "wmi:");
588 memcpy(&env->buf[env->buflen - 1 + 4], guid_string, 36);
589 env->buflen += 40;
590
591 return 0;
592}
593
594static void wmi_dev_free(struct device *dev)
595{
c64eefd4
DT
596 struct wmi_block *wmi_block = container_of(dev, struct wmi_block, dev);
597
598 kfree(wmi_block);
1caab3c1
MG
599}
600
601static struct class wmi_class = {
602 .name = "wmi",
603 .dev_release = wmi_dev_free,
604 .dev_uevent = wmi_dev_uevent,
e80b89a5 605 .dev_groups = wmi_groups,
1caab3c1
MG
606};
607
58f6425e 608static int wmi_create_device(const struct guid_block *gblock,
7f5809bf
AL
609 struct wmi_block *wblock,
610 struct acpi_device *device)
1caab3c1 611{
c64eefd4 612 wblock->dev.class = &wmi_class;
1caab3c1 613
85b4e4eb 614 dev_set_name(&wblock->dev, "%pUL", gblock->guid);
1caab3c1 615
c64eefd4 616 dev_set_drvdata(&wblock->dev, wblock);
1caab3c1 617
58f6425e 618 return device_register(&wblock->dev);
1caab3c1
MG
619}
620
c64eefd4 621static void wmi_free_devices(void)
1caab3c1 622{
c64eefd4 623 struct wmi_block *wblock, *next;
1caab3c1
MG
624
625 /* Delete devices for all the GUIDs */
023b9565
DT
626 list_for_each_entry_safe(wblock, next, &wmi_block_list, list) {
627 list_del(&wblock->list);
58f6425e
CK
628 if (wblock->dev.class)
629 device_unregister(&wblock->dev);
023b9565
DT
630 else
631 kfree(wblock);
632 }
1caab3c1
MG
633}
634
d1f9e497
CC
635static bool guid_already_parsed(const char *guid_string)
636{
d1f9e497 637 struct wmi_block *wblock;
d1f9e497 638
c64eefd4 639 list_for_each_entry(wblock, &wmi_block_list, list)
8b14d7b2 640 if (memcmp(wblock->gblock.guid, guid_string, 16) == 0)
d1f9e497 641 return true;
d1f9e497 642
c64eefd4 643 return false;
2d5ab555
DT
644}
645
bff431e4
CC
646/*
647 * Parse the _WDG method for the GUID data blocks
648 */
7f5809bf 649static int parse_wdg(struct acpi_device *device)
bff431e4
CC
650{
651 struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
652 union acpi_object *obj;
37830662 653 const struct guid_block *gblock;
bff431e4
CC
654 struct wmi_block *wblock;
655 acpi_status status;
c64eefd4 656 int retval;
bff431e4
CC
657 u32 i, total;
658
7f5809bf 659 status = acpi_evaluate_object(device->handle, "_WDG", NULL, &out);
bff431e4 660 if (ACPI_FAILURE(status))
c64eefd4 661 return -ENXIO;
bff431e4
CC
662
663 obj = (union acpi_object *) out.pointer;
3d2c63eb 664 if (!obj)
c64eefd4 665 return -ENXIO;
bff431e4 666
64ed0ab8 667 if (obj->type != ACPI_TYPE_BUFFER) {
c64eefd4 668 retval = -ENXIO;
64ed0ab8
DT
669 goto out_free_pointer;
670 }
bff431e4 671
37830662 672 gblock = (const struct guid_block *)obj->buffer.pointer;
bff431e4
CC
673 total = obj->buffer.length / sizeof(struct guid_block);
674
bff431e4 675 for (i = 0; i < total; i++) {
58f6425e
CK
676 if (debug_dump_wdg)
677 wmi_dump_wdg(&gblock[i]);
678
679 wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL);
680 if (!wblock)
0a018a68 681 return -ENOMEM;
58f6425e 682
7f5809bf 683 wblock->handle = device->handle;
58f6425e
CK
684 wblock->gblock = gblock[i];
685
d1f9e497
CC
686 /*
687 Some WMI devices, like those for nVidia hooks, have a
688 duplicate GUID. It's not clear what we should do in this
58f6425e
CK
689 case yet, so for now, we'll just ignore the duplicate
690 for device creation.
d1f9e497 691 */
58f6425e 692 if (!guid_already_parsed(gblock[i].guid)) {
7f5809bf 693 retval = wmi_create_device(&gblock[i], wblock, device);
58f6425e
CK
694 if (retval) {
695 wmi_free_devices();
e1e0dacb 696 goto out_free_pointer;
58f6425e 697 }
d1f9e497 698 }
c64eefd4 699
58f6425e 700 list_add_tail(&wblock->list, &wmi_block_list);
bff431e4 701
fc3155b2
TR
702 if (debug_event) {
703 wblock->handler = wmi_notify_debug;
2d5ab555 704 wmi_method_enable(wblock, 1);
fc3155b2 705 }
bff431e4
CC
706 }
707
c64eefd4
DT
708 retval = 0;
709
a5167c5b
AL
710out_free_pointer:
711 kfree(out.pointer);
bff431e4 712
c64eefd4 713 return retval;
bff431e4
CC
714}
715
716/*
717 * WMI can have EmbeddedControl access regions. In which case, we just want to
718 * hand these off to the EC driver.
719 */
720static acpi_status
721acpi_wmi_ec_space_handler(u32 function, acpi_physical_address address,
439913ff 722 u32 bits, u64 *value,
bff431e4
CC
723 void *handler_context, void *region_context)
724{
725 int result = 0, i = 0;
726 u8 temp = 0;
727
728 if ((address > 0xFF) || !value)
729 return AE_BAD_PARAMETER;
730
731 if (function != ACPI_READ && function != ACPI_WRITE)
732 return AE_BAD_PARAMETER;
733
734 if (bits != 8)
735 return AE_BAD_PARAMETER;
736
737 if (function == ACPI_READ) {
738 result = ec_read(address, &temp);
439913ff 739 (*value) |= ((u64)temp) << i;
bff431e4
CC
740 } else {
741 temp = 0xff & ((*value) >> i);
742 result = ec_write(address, temp);
743 }
744
745 switch (result) {
746 case -EINVAL:
747 return AE_BAD_PARAMETER;
748 break;
749 case -ENODEV:
750 return AE_NOT_FOUND;
751 break;
752 case -ETIME:
753 return AE_TIME;
754 break;
755 default:
756 return AE_OK;
757 }
758}
759
f61bb939 760static void acpi_wmi_notify(struct acpi_device *device, u32 event)
bff431e4
CC
761{
762 struct guid_block *block;
763 struct wmi_block *wblock;
764 struct list_head *p;
bff431e4 765
762e1a2f 766 list_for_each(p, &wmi_block_list) {
bff431e4
CC
767 wblock = list_entry(p, struct wmi_block, list);
768 block = &wblock->gblock;
769
770 if ((block->flags & ACPI_WMI_EVENT) &&
771 (block->notify_id == event)) {
772 if (wblock->handler)
773 wblock->handler(event, wblock->handler_data);
7715348c 774 if (debug_event) {
85b4e4eb
RV
775 pr_info("DEBUG Event GUID: %pUL\n",
776 wblock->gblock.guid);
7715348c 777 }
bff431e4
CC
778
779 acpi_bus_generate_netlink_event(
0794469d 780 device->pnp.device_class, dev_name(&device->dev),
bff431e4
CC
781 event, 0);
782 break;
783 }
784 }
785}
786
51fac838 787static int acpi_wmi_remove(struct acpi_device *device)
bff431e4 788{
bff431e4
CC
789 acpi_remove_address_space_handler(device->handle,
790 ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler);
c64eefd4 791 wmi_free_devices();
bff431e4
CC
792
793 return 0;
794}
795
925b1089 796static int acpi_wmi_add(struct acpi_device *device)
bff431e4
CC
797{
798 acpi_status status;
c64eefd4 799 int error;
bff431e4 800
bff431e4
CC
801 status = acpi_install_address_space_handler(device->handle,
802 ACPI_ADR_SPACE_EC,
803 &acpi_wmi_ec_space_handler,
804 NULL, NULL);
5212cd67 805 if (ACPI_FAILURE(status)) {
46492ee4 806 dev_err(&device->dev, "Error installing EC region handler\n");
bff431e4 807 return -ENODEV;
5212cd67 808 }
bff431e4 809
7f5809bf 810 error = parse_wdg(device);
c64eefd4 811 if (error) {
8e07514d 812 pr_err("Failed to parse WDG method\n");
46492ee4 813 goto err_remove_handler;
bff431e4
CC
814 }
815
c64eefd4 816 return 0;
46492ee4
AL
817
818err_remove_handler:
819 acpi_remove_address_space_handler(device->handle,
820 ACPI_ADR_SPACE_EC,
821 &acpi_wmi_ec_space_handler);
822
823 return error;
bff431e4
CC
824}
825
826static int __init acpi_wmi_init(void)
827{
c64eefd4 828 int error;
bff431e4
CC
829
830 if (acpi_disabled)
831 return -ENODEV;
832
c64eefd4
DT
833 error = class_register(&wmi_class);
834 if (error)
835 return error;
1caab3c1 836
c64eefd4
DT
837 error = acpi_bus_register_driver(&acpi_wmi_driver);
838 if (error) {
839 pr_err("Error loading mapper\n");
840 class_unregister(&wmi_class);
841 return error;
bff431e4
CC
842 }
843
8e07514d 844 return 0;
bff431e4
CC
845}
846
847static void __exit acpi_wmi_exit(void)
848{
bff431e4 849 acpi_bus_unregister_driver(&acpi_wmi_driver);
c64eefd4 850 class_unregister(&wmi_class);
bff431e4
CC
851}
852
853subsys_initcall(acpi_wmi_init);
854module_exit(acpi_wmi_exit);