Commit | Line | Data |
---|---|---|
1a59d1b8 | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
bff431e4 CC |
2 | /* |
3 | * ACPI-WMI mapping driver | |
4 | * | |
5 | * Copyright (C) 2007-2008 Carlos Corbacho <carlos@strangeworlds.co.uk> | |
6 | * | |
7 | * GUID parsing code from ldm.c is: | |
8 | * Copyright (C) 2001,2002 Richard Russon <ldm@flatcap.org> | |
9 | * Copyright (c) 2001-2007 Anton Altaparmakov | |
10 | * Copyright (C) 2001,2002 Jakob Kemi <jakob.kemi@telia.com> | |
11 | * | |
2c9c5664 DHV |
12 | * WMI bus infrastructure by Andrew Lutomirski and Darren Hart: |
13 | * Copyright (C) 2015 Andrew Lutomirski | |
14 | * Copyright (C) 2017 VMware, Inc. All Rights Reserved. | |
bff431e4 CC |
15 | */ |
16 | ||
8e07514d DT |
17 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
18 | ||
b60ee4e0 | 19 | #include <linux/acpi.h> |
1caab3c1 | 20 | #include <linux/device.h> |
b60ee4e0 ML |
21 | #include <linux/init.h> |
22 | #include <linux/kernel.h> | |
bff431e4 | 23 | #include <linux/list.h> |
44b6b766 | 24 | #include <linux/miscdevice.h> |
7c52d551 | 25 | #include <linux/module.h> |
9599ed91 | 26 | #include <linux/platform_device.h> |
b60ee4e0 ML |
27 | #include <linux/slab.h> |
28 | #include <linux/types.h> | |
44b6b766 | 29 | #include <linux/uaccess.h> |
538d7eb8 | 30 | #include <linux/uuid.h> |
b60ee4e0 | 31 | #include <linux/wmi.h> |
44b6b766 | 32 | #include <uapi/linux/wmi.h> |
bff431e4 CC |
33 | |
34 | ACPI_MODULE_NAME("wmi"); | |
35 | MODULE_AUTHOR("Carlos Corbacho"); | |
36 | MODULE_DESCRIPTION("ACPI-WMI Mapping Driver"); | |
37 | MODULE_LICENSE("GPL"); | |
38 | ||
762e1a2f | 39 | static LIST_HEAD(wmi_block_list); |
bff431e4 CC |
40 | |
41 | struct guid_block { | |
42 | char guid[16]; | |
43 | union { | |
44 | char object_id[2]; | |
45 | struct { | |
46 | unsigned char notify_id; | |
47 | unsigned char reserved; | |
48 | }; | |
49 | }; | |
50 | u8 instance_count; | |
51 | u8 flags; | |
52 | }; | |
53 | ||
54 | struct wmi_block { | |
844af950 | 55 | struct wmi_device dev; |
bff431e4 CC |
56 | struct list_head list; |
57 | struct guid_block gblock; | |
44b6b766 ML |
58 | struct miscdevice char_dev; |
59 | struct mutex char_mutex; | |
b0e86302 | 60 | struct acpi_device *acpi_device; |
bff431e4 CC |
61 | wmi_notify_handler handler; |
62 | void *handler_data; | |
44b6b766 | 63 | u64 req_buf_size; |
d4fc91ad | 64 | |
fd70da6a | 65 | bool read_takes_no_args; |
bff431e4 CC |
66 | }; |
67 | ||
bff431e4 CC |
68 | |
69 | /* | |
70 | * If the GUID data block is marked as expensive, we must enable and | |
71 | * explicitily disable data collection. | |
72 | */ | |
73 | #define ACPI_WMI_EXPENSIVE 0x1 | |
74 | #define ACPI_WMI_METHOD 0x2 /* GUID is a method */ | |
75 | #define ACPI_WMI_STRING 0x4 /* GUID takes & returns a string */ | |
76 | #define ACPI_WMI_EVENT 0x8 /* GUID is an event */ | |
77 | ||
90ab5ee9 | 78 | static bool debug_event; |
fc3155b2 TR |
79 | module_param(debug_event, bool, 0444); |
80 | MODULE_PARM_DESC(debug_event, | |
81 | "Log WMI Events [0/1]"); | |
82 | ||
90ab5ee9 | 83 | static bool debug_dump_wdg; |
a929aae0 TR |
84 | module_param(debug_dump_wdg, bool, 0444); |
85 | MODULE_PARM_DESC(debug_dump_wdg, | |
86 | "Dump available WMI interfaces [0/1]"); | |
87 | ||
9599ed91 AL |
88 | static int acpi_wmi_remove(struct platform_device *device); |
89 | static int acpi_wmi_probe(struct platform_device *device); | |
bff431e4 CC |
90 | |
91 | static const struct acpi_device_id wmi_device_ids[] = { | |
92 | {"PNP0C14", 0}, | |
93 | {"pnp0c14", 0}, | |
94 | {"", 0}, | |
95 | }; | |
96 | MODULE_DEVICE_TABLE(acpi, wmi_device_ids); | |
97 | ||
9599ed91 AL |
98 | static struct platform_driver acpi_wmi_driver = { |
99 | .driver = { | |
100 | .name = "acpi-wmi", | |
101 | .acpi_match_table = wmi_device_ids, | |
c64eefd4 | 102 | }, |
9599ed91 AL |
103 | .probe = acpi_wmi_probe, |
104 | .remove = acpi_wmi_remove, | |
bff431e4 CC |
105 | }; |
106 | ||
107 | /* | |
108 | * GUID parsing functions | |
109 | */ | |
110 | ||
bff431e4 CC |
111 | static bool find_guid(const char *guid_string, struct wmi_block **out) |
112 | { | |
538d7eb8 | 113 | uuid_le guid_input; |
bff431e4 CC |
114 | struct wmi_block *wblock; |
115 | struct guid_block *block; | |
bff431e4 | 116 | |
538d7eb8 AS |
117 | if (uuid_le_to_bin(guid_string, &guid_input)) |
118 | return false; | |
bff431e4 | 119 | |
cedb3b2a | 120 | list_for_each_entry(wblock, &wmi_block_list, list) { |
bff431e4 CC |
121 | block = &wblock->gblock; |
122 | ||
538d7eb8 | 123 | if (memcmp(block->guid, &guid_input, 16) == 0) { |
bff431e4 CC |
124 | if (out) |
125 | *out = wblock; | |
097c27fc | 126 | return true; |
bff431e4 CC |
127 | } |
128 | } | |
097c27fc | 129 | return false; |
bff431e4 CC |
130 | } |
131 | ||
d4fc91ad AL |
132 | static int get_subobj_info(acpi_handle handle, const char *pathname, |
133 | struct acpi_device_info **info) | |
134 | { | |
135 | struct acpi_device_info *dummy_info, **info_ptr; | |
136 | acpi_handle subobj_handle; | |
137 | acpi_status status; | |
138 | ||
139 | status = acpi_get_handle(handle, (char *)pathname, &subobj_handle); | |
140 | if (status == AE_NOT_FOUND) | |
141 | return -ENOENT; | |
142 | else if (ACPI_FAILURE(status)) | |
143 | return -EIO; | |
144 | ||
145 | info_ptr = info ? info : &dummy_info; | |
146 | status = acpi_get_object_info(subobj_handle, info_ptr); | |
147 | if (ACPI_FAILURE(status)) | |
148 | return -EIO; | |
149 | ||
150 | if (!info) | |
151 | kfree(dummy_info); | |
152 | ||
153 | return 0; | |
154 | } | |
155 | ||
a66bfa7a MG |
156 | static acpi_status wmi_method_enable(struct wmi_block *wblock, int enable) |
157 | { | |
158 | struct guid_block *block = NULL; | |
159 | char method[5]; | |
a66bfa7a MG |
160 | acpi_status status; |
161 | acpi_handle handle; | |
162 | ||
163 | block = &wblock->gblock; | |
b0e86302 | 164 | handle = wblock->acpi_device->handle; |
a66bfa7a | 165 | |
a66bfa7a | 166 | snprintf(method, 5, "WE%02X", block->notify_id); |
8122ab66 | 167 | status = acpi_execute_simple_method(handle, method, enable); |
a66bfa7a MG |
168 | |
169 | if (status != AE_OK && status != AE_NOT_FOUND) | |
170 | return status; | |
171 | else | |
172 | return AE_OK; | |
173 | } | |
174 | ||
bff431e4 CC |
175 | /* |
176 | * Exported WMI functions | |
177 | */ | |
44b6b766 ML |
178 | |
179 | /** | |
180 | * set_required_buffer_size - Sets the buffer size needed for performing IOCTL | |
181 | * @wdev: A wmi bus device from a driver | |
182 | * @instance: Instance index | |
183 | * | |
184 | * Allocates memory needed for buffer, stores the buffer size in that memory | |
185 | */ | |
186 | int set_required_buffer_size(struct wmi_device *wdev, u64 length) | |
187 | { | |
188 | struct wmi_block *wblock; | |
189 | ||
190 | wblock = container_of(wdev, struct wmi_block, dev); | |
191 | wblock->req_buf_size = length; | |
192 | ||
193 | return 0; | |
194 | } | |
195 | EXPORT_SYMBOL_GPL(set_required_buffer_size); | |
196 | ||
bff431e4 CC |
197 | /** |
198 | * wmi_evaluate_method - Evaluate a WMI method | |
199 | * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba | |
200 | * @instance: Instance index | |
201 | * @method_id: Method ID to call | |
202 | * &in: Buffer containing input for the method call | |
203 | * &out: Empty buffer to return the method results | |
204 | * | |
205 | * Call an ACPI-WMI method | |
206 | */ | |
207 | acpi_status wmi_evaluate_method(const char *guid_string, u8 instance, | |
208 | u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out) | |
722c856d ML |
209 | { |
210 | struct wmi_block *wblock = NULL; | |
211 | ||
212 | if (!find_guid(guid_string, &wblock)) | |
213 | return AE_ERROR; | |
214 | return wmidev_evaluate_method(&wblock->dev, instance, method_id, | |
215 | in, out); | |
216 | } | |
217 | EXPORT_SYMBOL_GPL(wmi_evaluate_method); | |
218 | ||
219 | /** | |
220 | * wmidev_evaluate_method - Evaluate a WMI method | |
221 | * @wdev: A wmi bus device from a driver | |
222 | * @instance: Instance index | |
223 | * @method_id: Method ID to call | |
224 | * &in: Buffer containing input for the method call | |
225 | * &out: Empty buffer to return the method results | |
226 | * | |
227 | * Call an ACPI-WMI method | |
228 | */ | |
229 | acpi_status wmidev_evaluate_method(struct wmi_device *wdev, u8 instance, | |
230 | u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out) | |
bff431e4 CC |
231 | { |
232 | struct guid_block *block = NULL; | |
233 | struct wmi_block *wblock = NULL; | |
234 | acpi_handle handle; | |
235 | acpi_status status; | |
236 | struct acpi_object_list input; | |
237 | union acpi_object params[3]; | |
f3d83e24 | 238 | char method[5] = "WM"; |
bff431e4 | 239 | |
722c856d | 240 | wblock = container_of(wdev, struct wmi_block, dev); |
bff431e4 | 241 | block = &wblock->gblock; |
b0e86302 | 242 | handle = wblock->acpi_device->handle; |
bff431e4 | 243 | |
e6bafba5 | 244 | if (!(block->flags & ACPI_WMI_METHOD)) |
bff431e4 CC |
245 | return AE_BAD_DATA; |
246 | ||
6afa1e2a | 247 | if (block->instance_count <= instance) |
bff431e4 CC |
248 | return AE_BAD_PARAMETER; |
249 | ||
250 | input.count = 2; | |
251 | input.pointer = params; | |
252 | params[0].type = ACPI_TYPE_INTEGER; | |
253 | params[0].integer.value = instance; | |
254 | params[1].type = ACPI_TYPE_INTEGER; | |
255 | params[1].integer.value = method_id; | |
256 | ||
257 | if (in) { | |
258 | input.count = 3; | |
259 | ||
260 | if (block->flags & ACPI_WMI_STRING) { | |
261 | params[2].type = ACPI_TYPE_STRING; | |
262 | } else { | |
263 | params[2].type = ACPI_TYPE_BUFFER; | |
264 | } | |
265 | params[2].buffer.length = in->length; | |
266 | params[2].buffer.pointer = in->pointer; | |
267 | } | |
268 | ||
269 | strncat(method, block->object_id, 2); | |
270 | ||
271 | status = acpi_evaluate_object(handle, method, &input, out); | |
272 | ||
273 | return status; | |
274 | } | |
722c856d | 275 | EXPORT_SYMBOL_GPL(wmidev_evaluate_method); |
bff431e4 | 276 | |
56a37025 AL |
277 | static acpi_status __query_block(struct wmi_block *wblock, u8 instance, |
278 | struct acpi_buffer *out) | |
bff431e4 CC |
279 | { |
280 | struct guid_block *block = NULL; | |
54f14c27 | 281 | acpi_handle handle; |
bff431e4 | 282 | acpi_status status, wc_status = AE_ERROR; |
8122ab66 ZR |
283 | struct acpi_object_list input; |
284 | union acpi_object wq_params[1]; | |
f3d83e24 CL |
285 | char method[5]; |
286 | char wc_method[5] = "WC"; | |
bff431e4 | 287 | |
56a37025 | 288 | if (!out) |
bff431e4 CC |
289 | return AE_BAD_PARAMETER; |
290 | ||
bff431e4 | 291 | block = &wblock->gblock; |
b0e86302 | 292 | handle = wblock->acpi_device->handle; |
bff431e4 | 293 | |
6afa1e2a | 294 | if (block->instance_count <= instance) |
bff431e4 CC |
295 | return AE_BAD_PARAMETER; |
296 | ||
297 | /* Check GUID is a data block */ | |
298 | if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD)) | |
08237974 | 299 | return AE_ERROR; |
bff431e4 CC |
300 | |
301 | input.count = 1; | |
302 | input.pointer = wq_params; | |
303 | wq_params[0].type = ACPI_TYPE_INTEGER; | |
304 | wq_params[0].integer.value = instance; | |
305 | ||
d4fc91ad AL |
306 | if (instance == 0 && wblock->read_takes_no_args) |
307 | input.count = 0; | |
308 | ||
bff431e4 CC |
309 | /* |
310 | * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method first to | |
311 | * enable collection. | |
312 | */ | |
313 | if (block->flags & ACPI_WMI_EXPENSIVE) { | |
bff431e4 CC |
314 | strncat(wc_method, block->object_id, 2); |
315 | ||
316 | /* | |
317 | * Some GUIDs break the specification by declaring themselves | |
318 | * expensive, but have no corresponding WCxx method. So we | |
319 | * should not fail if this happens. | |
320 | */ | |
54f14c27 | 321 | if (acpi_has_method(handle, wc_method)) |
8122ab66 ZR |
322 | wc_status = acpi_execute_simple_method(handle, |
323 | wc_method, 1); | |
bff431e4 CC |
324 | } |
325 | ||
326 | strcpy(method, "WQ"); | |
327 | strncat(method, block->object_id, 2); | |
328 | ||
dab36ad8 | 329 | status = acpi_evaluate_object(handle, method, &input, out); |
bff431e4 CC |
330 | |
331 | /* | |
332 | * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method, even if | |
333 | * the WQxx method failed - we should disable collection anyway. | |
334 | */ | |
a527f2d7 | 335 | if ((block->flags & ACPI_WMI_EXPENSIVE) && ACPI_SUCCESS(wc_status)) { |
8122ab66 | 336 | status = acpi_execute_simple_method(handle, wc_method, 0); |
bff431e4 CC |
337 | } |
338 | ||
339 | return status; | |
340 | } | |
56a37025 AL |
341 | |
342 | /** | |
343 | * wmi_query_block - Return contents of a WMI block (deprecated) | |
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 | */ | |
350 | acpi_status wmi_query_block(const char *guid_string, u8 instance, | |
351 | struct acpi_buffer *out) | |
352 | { | |
353 | struct wmi_block *wblock; | |
354 | ||
355 | if (!guid_string) | |
356 | return AE_BAD_PARAMETER; | |
357 | ||
358 | if (!find_guid(guid_string, &wblock)) | |
359 | return AE_ERROR; | |
360 | ||
361 | return __query_block(wblock, instance, out); | |
362 | } | |
bff431e4 CC |
363 | EXPORT_SYMBOL_GPL(wmi_query_block); |
364 | ||
56a37025 AL |
365 | union acpi_object *wmidev_block_query(struct wmi_device *wdev, u8 instance) |
366 | { | |
367 | struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL }; | |
368 | struct wmi_block *wblock = container_of(wdev, struct wmi_block, dev); | |
369 | ||
370 | if (ACPI_FAILURE(__query_block(wblock, instance, &out))) | |
371 | return NULL; | |
372 | ||
373 | return (union acpi_object *)out.pointer; | |
374 | } | |
375 | EXPORT_SYMBOL_GPL(wmidev_block_query); | |
376 | ||
bff431e4 CC |
377 | /** |
378 | * wmi_set_block - Write to a WMI block | |
379 | * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba | |
380 | * @instance: Instance index | |
381 | * &in: Buffer containing new values for the data block | |
382 | * | |
383 | * Write the contents of the input buffer to an ACPI-WMI data block | |
384 | */ | |
385 | acpi_status wmi_set_block(const char *guid_string, u8 instance, | |
56a37025 | 386 | const struct acpi_buffer *in) |
bff431e4 CC |
387 | { |
388 | struct guid_block *block = NULL; | |
389 | struct wmi_block *wblock = NULL; | |
390 | acpi_handle handle; | |
391 | struct acpi_object_list input; | |
392 | union acpi_object params[2]; | |
f3d83e24 | 393 | char method[5] = "WS"; |
bff431e4 CC |
394 | |
395 | if (!guid_string || !in) | |
396 | return AE_BAD_DATA; | |
397 | ||
398 | if (!find_guid(guid_string, &wblock)) | |
08237974 | 399 | return AE_ERROR; |
bff431e4 CC |
400 | |
401 | block = &wblock->gblock; | |
b0e86302 | 402 | handle = wblock->acpi_device->handle; |
bff431e4 | 403 | |
6afa1e2a | 404 | if (block->instance_count <= instance) |
bff431e4 CC |
405 | return AE_BAD_PARAMETER; |
406 | ||
407 | /* Check GUID is a data block */ | |
408 | if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD)) | |
08237974 | 409 | return AE_ERROR; |
bff431e4 CC |
410 | |
411 | input.count = 2; | |
412 | input.pointer = params; | |
413 | params[0].type = ACPI_TYPE_INTEGER; | |
414 | params[0].integer.value = instance; | |
415 | ||
416 | if (block->flags & ACPI_WMI_STRING) { | |
417 | params[1].type = ACPI_TYPE_STRING; | |
418 | } else { | |
419 | params[1].type = ACPI_TYPE_BUFFER; | |
420 | } | |
421 | params[1].buffer.length = in->length; | |
422 | params[1].buffer.pointer = in->pointer; | |
423 | ||
424 | strncat(method, block->object_id, 2); | |
425 | ||
426 | return acpi_evaluate_object(handle, method, &input, NULL); | |
427 | } | |
428 | EXPORT_SYMBOL_GPL(wmi_set_block); | |
429 | ||
37830662 | 430 | static void wmi_dump_wdg(const struct guid_block *g) |
a929aae0 | 431 | { |
85b4e4eb | 432 | pr_info("%pUL:\n", g->guid); |
cd3921f8 PR |
433 | if (g->flags & ACPI_WMI_EVENT) |
434 | pr_info("\tnotify_id: 0x%02X\n", g->notify_id); | |
435 | else | |
436 | pr_info("\tobject_id: %2pE\n", g->object_id); | |
8e07514d | 437 | pr_info("\tinstance_count: %d\n", g->instance_count); |
dd8e908e | 438 | pr_info("\tflags: %#x", g->flags); |
a929aae0 | 439 | if (g->flags) { |
a929aae0 | 440 | if (g->flags & ACPI_WMI_EXPENSIVE) |
dd8e908e | 441 | pr_cont(" ACPI_WMI_EXPENSIVE"); |
a929aae0 | 442 | if (g->flags & ACPI_WMI_METHOD) |
dd8e908e | 443 | pr_cont(" ACPI_WMI_METHOD"); |
a929aae0 | 444 | if (g->flags & ACPI_WMI_STRING) |
dd8e908e | 445 | pr_cont(" ACPI_WMI_STRING"); |
a929aae0 | 446 | if (g->flags & ACPI_WMI_EVENT) |
dd8e908e | 447 | pr_cont(" ACPI_WMI_EVENT"); |
a929aae0 | 448 | } |
8e07514d | 449 | pr_cont("\n"); |
a929aae0 TR |
450 | |
451 | } | |
452 | ||
fc3155b2 TR |
453 | static void wmi_notify_debug(u32 value, void *context) |
454 | { | |
455 | struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; | |
456 | union acpi_object *obj; | |
1492616a | 457 | acpi_status status; |
fc3155b2 | 458 | |
1492616a AL |
459 | status = wmi_get_event_data(value, &response); |
460 | if (status != AE_OK) { | |
8e07514d | 461 | pr_info("bad event status 0x%x\n", status); |
1492616a AL |
462 | return; |
463 | } | |
fc3155b2 TR |
464 | |
465 | obj = (union acpi_object *)response.pointer; | |
466 | ||
467 | if (!obj) | |
468 | return; | |
469 | ||
8e07514d | 470 | pr_info("DEBUG Event "); |
fc3155b2 TR |
471 | switch(obj->type) { |
472 | case ACPI_TYPE_BUFFER: | |
8e07514d | 473 | pr_cont("BUFFER_TYPE - length %d\n", obj->buffer.length); |
fc3155b2 TR |
474 | break; |
475 | case ACPI_TYPE_STRING: | |
8e07514d | 476 | pr_cont("STRING_TYPE - %s\n", obj->string.pointer); |
fc3155b2 TR |
477 | break; |
478 | case ACPI_TYPE_INTEGER: | |
8e07514d | 479 | pr_cont("INTEGER_TYPE - %llu\n", obj->integer.value); |
fc3155b2 TR |
480 | break; |
481 | case ACPI_TYPE_PACKAGE: | |
8e07514d | 482 | pr_cont("PACKAGE_TYPE - %d elements\n", obj->package.count); |
fc3155b2 TR |
483 | break; |
484 | default: | |
8e07514d | 485 | pr_cont("object type 0x%X\n", obj->type); |
fc3155b2 | 486 | } |
1492616a | 487 | kfree(obj); |
fc3155b2 TR |
488 | } |
489 | ||
bff431e4 CC |
490 | /** |
491 | * wmi_install_notify_handler - Register handler for WMI events | |
492 | * @handler: Function to handle notifications | |
493 | * @data: Data to be returned to handler when event is fired | |
494 | * | |
495 | * Register a handler for events sent to the ACPI-WMI mapper device. | |
496 | */ | |
497 | acpi_status wmi_install_notify_handler(const char *guid, | |
498 | wmi_notify_handler handler, void *data) | |
499 | { | |
500 | struct wmi_block *block; | |
58f6425e | 501 | acpi_status status = AE_NOT_EXIST; |
538d7eb8 | 502 | uuid_le guid_input; |
bff431e4 CC |
503 | |
504 | if (!guid || !handler) | |
505 | return AE_BAD_PARAMETER; | |
506 | ||
538d7eb8 AS |
507 | if (uuid_le_to_bin(guid, &guid_input)) |
508 | return AE_BAD_PARAMETER; | |
bff431e4 | 509 | |
cedb3b2a | 510 | list_for_each_entry(block, &wmi_block_list, list) { |
58f6425e | 511 | acpi_status wmi_status; |
58f6425e | 512 | |
538d7eb8 | 513 | if (memcmp(block->gblock.guid, &guid_input, 16) == 0) { |
58f6425e CK |
514 | if (block->handler && |
515 | block->handler != wmi_notify_debug) | |
516 | return AE_ALREADY_ACQUIRED; | |
bff431e4 | 517 | |
58f6425e CK |
518 | block->handler = handler; |
519 | block->handler_data = data; | |
bff431e4 | 520 | |
58f6425e CK |
521 | wmi_status = wmi_method_enable(block, 1); |
522 | if ((wmi_status != AE_OK) || | |
523 | ((wmi_status == AE_OK) && (status == AE_NOT_EXIST))) | |
524 | status = wmi_status; | |
525 | } | |
526 | } | |
a66bfa7a MG |
527 | |
528 | return status; | |
bff431e4 CC |
529 | } |
530 | EXPORT_SYMBOL_GPL(wmi_install_notify_handler); | |
531 | ||
532 | /** | |
533 | * wmi_uninstall_notify_handler - Unregister handler for WMI events | |
534 | * | |
535 | * Unregister handler for events sent to the ACPI-WMI mapper device. | |
536 | */ | |
537 | acpi_status wmi_remove_notify_handler(const char *guid) | |
538 | { | |
539 | struct wmi_block *block; | |
58f6425e | 540 | acpi_status status = AE_NOT_EXIST; |
538d7eb8 | 541 | uuid_le guid_input; |
bff431e4 CC |
542 | |
543 | if (!guid) | |
544 | return AE_BAD_PARAMETER; | |
545 | ||
538d7eb8 AS |
546 | if (uuid_le_to_bin(guid, &guid_input)) |
547 | return AE_BAD_PARAMETER; | |
bff431e4 | 548 | |
cedb3b2a | 549 | list_for_each_entry(block, &wmi_block_list, list) { |
58f6425e | 550 | acpi_status wmi_status; |
58f6425e | 551 | |
538d7eb8 | 552 | if (memcmp(block->gblock.guid, &guid_input, 16) == 0) { |
58f6425e CK |
553 | if (!block->handler || |
554 | block->handler == wmi_notify_debug) | |
555 | return AE_NULL_ENTRY; | |
556 | ||
557 | if (debug_event) { | |
558 | block->handler = wmi_notify_debug; | |
559 | status = AE_OK; | |
560 | } else { | |
561 | wmi_status = wmi_method_enable(block, 0); | |
562 | block->handler = NULL; | |
563 | block->handler_data = NULL; | |
564 | if ((wmi_status != AE_OK) || | |
565 | ((wmi_status == AE_OK) && | |
566 | (status == AE_NOT_EXIST))) | |
567 | status = wmi_status; | |
568 | } | |
569 | } | |
fc3155b2 | 570 | } |
58f6425e | 571 | |
a66bfa7a | 572 | return status; |
bff431e4 CC |
573 | } |
574 | EXPORT_SYMBOL_GPL(wmi_remove_notify_handler); | |
575 | ||
576 | /** | |
577 | * wmi_get_event_data - Get WMI data associated with an event | |
578 | * | |
3e9b988e AA |
579 | * @event: Event to find |
580 | * @out: Buffer to hold event data. out->pointer should be freed with kfree() | |
bff431e4 CC |
581 | * |
582 | * Returns extra data associated with an event in WMI. | |
583 | */ | |
584 | acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out) | |
585 | { | |
586 | struct acpi_object_list input; | |
587 | union acpi_object params[1]; | |
588 | struct guid_block *gblock; | |
589 | struct wmi_block *wblock; | |
bff431e4 CC |
590 | |
591 | input.count = 1; | |
592 | input.pointer = params; | |
593 | params[0].type = ACPI_TYPE_INTEGER; | |
594 | params[0].integer.value = event; | |
595 | ||
cedb3b2a | 596 | list_for_each_entry(wblock, &wmi_block_list, list) { |
bff431e4 CC |
597 | gblock = &wblock->gblock; |
598 | ||
599 | if ((gblock->flags & ACPI_WMI_EVENT) && | |
600 | (gblock->notify_id == event)) | |
b0e86302 AL |
601 | return acpi_evaluate_object(wblock->acpi_device->handle, |
602 | "_WED", &input, out); | |
bff431e4 CC |
603 | } |
604 | ||
605 | return AE_NOT_FOUND; | |
606 | } | |
607 | EXPORT_SYMBOL_GPL(wmi_get_event_data); | |
608 | ||
609 | /** | |
610 | * wmi_has_guid - Check if a GUID is available | |
611 | * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba | |
612 | * | |
613 | * Check if a given GUID is defined by _WDG | |
614 | */ | |
615 | bool wmi_has_guid(const char *guid_string) | |
616 | { | |
617 | return find_guid(guid_string, NULL); | |
618 | } | |
619 | EXPORT_SYMBOL_GPL(wmi_has_guid); | |
620 | ||
844af950 AL |
621 | static struct wmi_block *dev_to_wblock(struct device *dev) |
622 | { | |
623 | return container_of(dev, struct wmi_block, dev.dev); | |
624 | } | |
625 | ||
626 | static struct wmi_device *dev_to_wdev(struct device *dev) | |
627 | { | |
628 | return container_of(dev, struct wmi_device, dev); | |
629 | } | |
630 | ||
1caab3c1 MG |
631 | /* |
632 | * sysfs interface | |
633 | */ | |
614ef432 | 634 | static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, |
1caab3c1 MG |
635 | char *buf) |
636 | { | |
844af950 | 637 | struct wmi_block *wblock = dev_to_wblock(dev); |
1caab3c1 | 638 | |
85b4e4eb | 639 | return sprintf(buf, "wmi:%pUL\n", wblock->gblock.guid); |
1caab3c1 | 640 | } |
e80b89a5 | 641 | static DEVICE_ATTR_RO(modalias); |
614ef432 | 642 | |
844af950 AL |
643 | static ssize_t guid_show(struct device *dev, struct device_attribute *attr, |
644 | char *buf) | |
645 | { | |
646 | struct wmi_block *wblock = dev_to_wblock(dev); | |
647 | ||
648 | return sprintf(buf, "%pUL\n", wblock->gblock.guid); | |
649 | } | |
650 | static DEVICE_ATTR_RO(guid); | |
651 | ||
d79b1074 AL |
652 | static ssize_t instance_count_show(struct device *dev, |
653 | struct device_attribute *attr, char *buf) | |
654 | { | |
655 | struct wmi_block *wblock = dev_to_wblock(dev); | |
656 | ||
657 | return sprintf(buf, "%d\n", (int)wblock->gblock.instance_count); | |
658 | } | |
659 | static DEVICE_ATTR_RO(instance_count); | |
660 | ||
661 | static ssize_t expensive_show(struct device *dev, | |
662 | struct device_attribute *attr, char *buf) | |
663 | { | |
664 | struct wmi_block *wblock = dev_to_wblock(dev); | |
665 | ||
666 | return sprintf(buf, "%d\n", | |
667 | (wblock->gblock.flags & ACPI_WMI_EXPENSIVE) != 0); | |
668 | } | |
669 | static DEVICE_ATTR_RO(expensive); | |
670 | ||
e80b89a5 GKH |
671 | static struct attribute *wmi_attrs[] = { |
672 | &dev_attr_modalias.attr, | |
844af950 | 673 | &dev_attr_guid.attr, |
d79b1074 AL |
674 | &dev_attr_instance_count.attr, |
675 | &dev_attr_expensive.attr, | |
e80b89a5 | 676 | NULL, |
614ef432 | 677 | }; |
e80b89a5 | 678 | ATTRIBUTE_GROUPS(wmi); |
1caab3c1 | 679 | |
d79b1074 AL |
680 | static ssize_t notify_id_show(struct device *dev, struct device_attribute *attr, |
681 | char *buf) | |
682 | { | |
683 | struct wmi_block *wblock = dev_to_wblock(dev); | |
684 | ||
685 | return sprintf(buf, "%02X\n", (unsigned int)wblock->gblock.notify_id); | |
686 | } | |
687 | static DEVICE_ATTR_RO(notify_id); | |
688 | ||
689 | static struct attribute *wmi_event_attrs[] = { | |
690 | &dev_attr_notify_id.attr, | |
691 | NULL, | |
692 | }; | |
693 | ATTRIBUTE_GROUPS(wmi_event); | |
694 | ||
695 | static ssize_t object_id_show(struct device *dev, struct device_attribute *attr, | |
696 | char *buf) | |
697 | { | |
698 | struct wmi_block *wblock = dev_to_wblock(dev); | |
699 | ||
700 | return sprintf(buf, "%c%c\n", wblock->gblock.object_id[0], | |
701 | wblock->gblock.object_id[1]); | |
702 | } | |
703 | static DEVICE_ATTR_RO(object_id); | |
704 | ||
fd70da6a DHV |
705 | static ssize_t setable_show(struct device *dev, struct device_attribute *attr, |
706 | char *buf) | |
d4fc91ad AL |
707 | { |
708 | struct wmi_device *wdev = dev_to_wdev(dev); | |
709 | ||
fd70da6a | 710 | return sprintf(buf, "%d\n", (int)wdev->setable); |
d4fc91ad | 711 | } |
fd70da6a | 712 | static DEVICE_ATTR_RO(setable); |
d4fc91ad AL |
713 | |
714 | static struct attribute *wmi_data_attrs[] = { | |
715 | &dev_attr_object_id.attr, | |
fd70da6a | 716 | &dev_attr_setable.attr, |
d4fc91ad AL |
717 | NULL, |
718 | }; | |
719 | ATTRIBUTE_GROUPS(wmi_data); | |
720 | ||
721 | static struct attribute *wmi_method_attrs[] = { | |
d79b1074 AL |
722 | &dev_attr_object_id.attr, |
723 | NULL, | |
724 | }; | |
d4fc91ad | 725 | ATTRIBUTE_GROUPS(wmi_method); |
d79b1074 | 726 | |
1caab3c1 MG |
727 | static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env) |
728 | { | |
844af950 | 729 | struct wmi_block *wblock = dev_to_wblock(dev); |
1caab3c1 | 730 | |
844af950 | 731 | if (add_uevent_var(env, "MODALIAS=wmi:%pUL", wblock->gblock.guid)) |
1caab3c1 MG |
732 | return -ENOMEM; |
733 | ||
844af950 | 734 | if (add_uevent_var(env, "WMI_GUID=%pUL", wblock->gblock.guid)) |
1caab3c1 MG |
735 | return -ENOMEM; |
736 | ||
844af950 AL |
737 | return 0; |
738 | } | |
739 | ||
740 | static void wmi_dev_release(struct device *dev) | |
741 | { | |
742 | struct wmi_block *wblock = dev_to_wblock(dev); | |
743 | ||
744 | kfree(wblock); | |
745 | } | |
746 | ||
747 | static int wmi_dev_match(struct device *dev, struct device_driver *driver) | |
748 | { | |
749 | struct wmi_driver *wmi_driver = | |
750 | container_of(driver, struct wmi_driver, driver); | |
751 | struct wmi_block *wblock = dev_to_wblock(dev); | |
752 | const struct wmi_device_id *id = wmi_driver->id_table; | |
1caab3c1 | 753 | |
c355ec65 MJ |
754 | if (id == NULL) |
755 | return 0; | |
756 | ||
eacc95ea | 757 | while (*id->guid_string) { |
844af950 AL |
758 | uuid_le driver_guid; |
759 | ||
760 | if (WARN_ON(uuid_le_to_bin(id->guid_string, &driver_guid))) | |
761 | continue; | |
762 | if (!memcmp(&driver_guid, wblock->gblock.guid, 16)) | |
763 | return 1; | |
764 | ||
765 | id++; | |
766 | } | |
1caab3c1 MG |
767 | |
768 | return 0; | |
769 | } | |
44b6b766 ML |
770 | static int wmi_char_open(struct inode *inode, struct file *filp) |
771 | { | |
772 | const char *driver_name = filp->f_path.dentry->d_iname; | |
773 | struct wmi_block *wblock = NULL; | |
774 | struct wmi_block *next = NULL; | |
775 | ||
776 | list_for_each_entry_safe(wblock, next, &wmi_block_list, list) { | |
777 | if (!wblock->dev.dev.driver) | |
778 | continue; | |
779 | if (strcmp(driver_name, wblock->dev.dev.driver->name) == 0) { | |
780 | filp->private_data = wblock; | |
781 | break; | |
782 | } | |
783 | } | |
784 | ||
785 | if (!filp->private_data) | |
786 | return -ENODEV; | |
787 | ||
788 | return nonseekable_open(inode, filp); | |
789 | } | |
790 | ||
791 | static ssize_t wmi_char_read(struct file *filp, char __user *buffer, | |
792 | size_t length, loff_t *offset) | |
793 | { | |
794 | struct wmi_block *wblock = filp->private_data; | |
795 | ||
796 | return simple_read_from_buffer(buffer, length, offset, | |
797 | &wblock->req_buf_size, | |
798 | sizeof(wblock->req_buf_size)); | |
799 | } | |
800 | ||
801 | static long wmi_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) | |
802 | { | |
803 | struct wmi_ioctl_buffer __user *input = | |
804 | (struct wmi_ioctl_buffer __user *) arg; | |
805 | struct wmi_block *wblock = filp->private_data; | |
806 | struct wmi_ioctl_buffer *buf = NULL; | |
807 | struct wmi_driver *wdriver = NULL; | |
808 | int ret; | |
809 | ||
810 | if (_IOC_TYPE(cmd) != WMI_IOC) | |
811 | return -ENOTTY; | |
812 | ||
813 | /* make sure we're not calling a higher instance than exists*/ | |
814 | if (_IOC_NR(cmd) >= wblock->gblock.instance_count) | |
815 | return -EINVAL; | |
816 | ||
817 | mutex_lock(&wblock->char_mutex); | |
818 | buf = wblock->handler_data; | |
819 | if (get_user(buf->length, &input->length)) { | |
820 | dev_dbg(&wblock->dev.dev, "Read length from user failed\n"); | |
821 | ret = -EFAULT; | |
822 | goto out_ioctl; | |
823 | } | |
824 | /* if it's too small, abort */ | |
825 | if (buf->length < wblock->req_buf_size) { | |
826 | dev_err(&wblock->dev.dev, | |
827 | "Buffer %lld too small, need at least %lld\n", | |
828 | buf->length, wblock->req_buf_size); | |
829 | ret = -EINVAL; | |
830 | goto out_ioctl; | |
831 | } | |
832 | /* if it's too big, warn, driver will only use what is needed */ | |
833 | if (buf->length > wblock->req_buf_size) | |
834 | dev_warn(&wblock->dev.dev, | |
835 | "Buffer %lld is bigger than required %lld\n", | |
836 | buf->length, wblock->req_buf_size); | |
837 | ||
838 | /* copy the structure from userspace */ | |
839 | if (copy_from_user(buf, input, wblock->req_buf_size)) { | |
840 | dev_dbg(&wblock->dev.dev, "Copy %llu from user failed\n", | |
841 | wblock->req_buf_size); | |
842 | ret = -EFAULT; | |
843 | goto out_ioctl; | |
844 | } | |
845 | ||
846 | /* let the driver do any filtering and do the call */ | |
847 | wdriver = container_of(wblock->dev.dev.driver, | |
848 | struct wmi_driver, driver); | |
5e3e2297 ML |
849 | if (!try_module_get(wdriver->driver.owner)) { |
850 | ret = -EBUSY; | |
851 | goto out_ioctl; | |
852 | } | |
44b6b766 ML |
853 | ret = wdriver->filter_callback(&wblock->dev, cmd, buf); |
854 | module_put(wdriver->driver.owner); | |
855 | if (ret) | |
856 | goto out_ioctl; | |
857 | ||
858 | /* return the result (only up to our internal buffer size) */ | |
859 | if (copy_to_user(input, buf, wblock->req_buf_size)) { | |
860 | dev_dbg(&wblock->dev.dev, "Copy %llu to user failed\n", | |
861 | wblock->req_buf_size); | |
862 | ret = -EFAULT; | |
863 | } | |
864 | ||
865 | out_ioctl: | |
866 | mutex_unlock(&wblock->char_mutex); | |
867 | return ret; | |
868 | } | |
869 | ||
870 | static const struct file_operations wmi_fops = { | |
871 | .owner = THIS_MODULE, | |
872 | .read = wmi_char_read, | |
873 | .open = wmi_char_open, | |
874 | .unlocked_ioctl = wmi_ioctl, | |
875 | .compat_ioctl = wmi_ioctl, | |
876 | }; | |
1caab3c1 | 877 | |
844af950 | 878 | static int wmi_dev_probe(struct device *dev) |
1caab3c1 | 879 | { |
844af950 AL |
880 | struct wmi_block *wblock = dev_to_wblock(dev); |
881 | struct wmi_driver *wdriver = | |
882 | container_of(dev->driver, struct wmi_driver, driver); | |
883 | int ret = 0; | |
44b6b766 | 884 | char *buf; |
844af950 AL |
885 | |
886 | if (ACPI_FAILURE(wmi_method_enable(wblock, 1))) | |
887 | dev_warn(dev, "failed to enable device -- probing anyway\n"); | |
888 | ||
889 | if (wdriver->probe) { | |
890 | ret = wdriver->probe(dev_to_wdev(dev)); | |
44b6b766 ML |
891 | if (ret != 0) |
892 | goto probe_failure; | |
893 | } | |
894 | ||
895 | /* driver wants a character device made */ | |
896 | if (wdriver->filter_callback) { | |
897 | /* check that required buffer size declared by driver or MOF */ | |
898 | if (!wblock->req_buf_size) { | |
899 | dev_err(&wblock->dev.dev, | |
900 | "Required buffer size not set\n"); | |
901 | ret = -EINVAL; | |
902 | goto probe_failure; | |
903 | } | |
904 | ||
6fb74107 KC |
905 | wblock->handler_data = kmalloc(wblock->req_buf_size, |
906 | GFP_KERNEL); | |
44b6b766 ML |
907 | if (!wblock->handler_data) { |
908 | ret = -ENOMEM; | |
909 | goto probe_failure; | |
910 | } | |
911 | ||
7f166add | 912 | buf = kasprintf(GFP_KERNEL, "wmi/%s", wdriver->driver.name); |
44b6b766 ML |
913 | if (!buf) { |
914 | ret = -ENOMEM; | |
915 | goto probe_string_failure; | |
916 | } | |
44b6b766 ML |
917 | wblock->char_dev.minor = MISC_DYNAMIC_MINOR; |
918 | wblock->char_dev.name = buf; | |
919 | wblock->char_dev.fops = &wmi_fops; | |
920 | wblock->char_dev.mode = 0444; | |
921 | ret = misc_register(&wblock->char_dev); | |
922 | if (ret) { | |
501f7e52 | 923 | dev_warn(dev, "failed to register char dev: %d\n", ret); |
44b6b766 ML |
924 | ret = -ENOMEM; |
925 | goto probe_misc_failure; | |
926 | } | |
844af950 AL |
927 | } |
928 | ||
44b6b766 ML |
929 | return 0; |
930 | ||
931 | probe_misc_failure: | |
932 | kfree(buf); | |
933 | probe_string_failure: | |
934 | kfree(wblock->handler_data); | |
935 | probe_failure: | |
936 | if (ACPI_FAILURE(wmi_method_enable(wblock, 0))) | |
937 | dev_warn(dev, "failed to disable device\n"); | |
844af950 AL |
938 | return ret; |
939 | } | |
940 | ||
941 | static int wmi_dev_remove(struct device *dev) | |
942 | { | |
943 | struct wmi_block *wblock = dev_to_wblock(dev); | |
944 | struct wmi_driver *wdriver = | |
945 | container_of(dev->driver, struct wmi_driver, driver); | |
946 | int ret = 0; | |
947 | ||
44b6b766 ML |
948 | if (wdriver->filter_callback) { |
949 | misc_deregister(&wblock->char_dev); | |
950 | kfree(wblock->char_dev.name); | |
6fb74107 | 951 | kfree(wblock->handler_data); |
44b6b766 ML |
952 | } |
953 | ||
844af950 AL |
954 | if (wdriver->remove) |
955 | ret = wdriver->remove(dev_to_wdev(dev)); | |
956 | ||
957 | if (ACPI_FAILURE(wmi_method_enable(wblock, 0))) | |
958 | dev_warn(dev, "failed to disable device\n"); | |
c64eefd4 | 959 | |
844af950 | 960 | return ret; |
1caab3c1 MG |
961 | } |
962 | ||
844af950 AL |
963 | static struct class wmi_bus_class = { |
964 | .name = "wmi_bus", | |
965 | }; | |
966 | ||
967 | static struct bus_type wmi_bus_type = { | |
1caab3c1 | 968 | .name = "wmi", |
e80b89a5 | 969 | .dev_groups = wmi_groups, |
844af950 AL |
970 | .match = wmi_dev_match, |
971 | .uevent = wmi_dev_uevent, | |
972 | .probe = wmi_dev_probe, | |
973 | .remove = wmi_dev_remove, | |
1caab3c1 MG |
974 | }; |
975 | ||
69372c1d | 976 | static const struct device_type wmi_type_event = { |
d79b1074 AL |
977 | .name = "event", |
978 | .groups = wmi_event_groups, | |
979 | .release = wmi_dev_release, | |
980 | }; | |
981 | ||
69372c1d | 982 | static const struct device_type wmi_type_method = { |
d79b1074 | 983 | .name = "method", |
d4fc91ad | 984 | .groups = wmi_method_groups, |
d79b1074 AL |
985 | .release = wmi_dev_release, |
986 | }; | |
987 | ||
69372c1d | 988 | static const struct device_type wmi_type_data = { |
d79b1074 | 989 | .name = "data", |
d4fc91ad | 990 | .groups = wmi_data_groups, |
d79b1074 AL |
991 | .release = wmi_dev_release, |
992 | }; | |
993 | ||
fd70da6a | 994 | static int wmi_create_device(struct device *wmi_bus_dev, |
844af950 | 995 | const struct guid_block *gblock, |
7f5809bf AL |
996 | struct wmi_block *wblock, |
997 | struct acpi_device *device) | |
1caab3c1 | 998 | { |
fd70da6a DHV |
999 | struct acpi_device_info *info; |
1000 | char method[5]; | |
1001 | int result; | |
1caab3c1 | 1002 | |
d79b1074 AL |
1003 | if (gblock->flags & ACPI_WMI_EVENT) { |
1004 | wblock->dev.dev.type = &wmi_type_event; | |
fd70da6a DHV |
1005 | goto out_init; |
1006 | } | |
1007 | ||
1008 | if (gblock->flags & ACPI_WMI_METHOD) { | |
d79b1074 | 1009 | wblock->dev.dev.type = &wmi_type_method; |
44b6b766 | 1010 | mutex_init(&wblock->char_mutex); |
fd70da6a DHV |
1011 | goto out_init; |
1012 | } | |
d4fc91ad | 1013 | |
fd70da6a DHV |
1014 | /* |
1015 | * Data Block Query Control Method (WQxx by convention) is | |
1016 | * required per the WMI documentation. If it is not present, | |
1017 | * we ignore this data block. | |
1018 | */ | |
1019 | strcpy(method, "WQ"); | |
1020 | strncat(method, wblock->gblock.object_id, 2); | |
1021 | result = get_subobj_info(device->handle, method, &info); | |
1022 | ||
1023 | if (result) { | |
1024 | dev_warn(wmi_bus_dev, | |
501f7e52 | 1025 | "%s data block query control method not found\n", |
fd70da6a DHV |
1026 | method); |
1027 | return result; | |
1028 | } | |
d4fc91ad | 1029 | |
fd70da6a | 1030 | wblock->dev.dev.type = &wmi_type_data; |
d4fc91ad | 1031 | |
fd70da6a DHV |
1032 | /* |
1033 | * The Microsoft documentation specifically states: | |
1034 | * | |
1035 | * Data blocks registered with only a single instance | |
1036 | * can ignore the parameter. | |
1037 | * | |
1038 | * ACPICA will get mad at us if we call the method with the wrong number | |
1039 | * of arguments, so check what our method expects. (On some Dell | |
1040 | * laptops, WQxx may not be a method at all.) | |
1041 | */ | |
1042 | if (info->type != ACPI_TYPE_METHOD || info->param_count == 0) | |
1043 | wblock->read_takes_no_args = true; | |
d4fc91ad | 1044 | |
fd70da6a | 1045 | kfree(info); |
d4fc91ad | 1046 | |
fd70da6a DHV |
1047 | strcpy(method, "WS"); |
1048 | strncat(method, wblock->gblock.object_id, 2); | |
1049 | result = get_subobj_info(device->handle, method, NULL); | |
d4fc91ad | 1050 | |
fd70da6a DHV |
1051 | if (result == 0) |
1052 | wblock->dev.setable = true; | |
d4fc91ad | 1053 | |
fd70da6a DHV |
1054 | out_init: |
1055 | wblock->dev.dev.bus = &wmi_bus_type; | |
1056 | wblock->dev.dev.parent = wmi_bus_dev; | |
d4fc91ad | 1057 | |
fd70da6a | 1058 | dev_set_name(&wblock->dev.dev, "%pUL", gblock->guid); |
1caab3c1 | 1059 | |
6ee50aaa | 1060 | device_initialize(&wblock->dev.dev); |
fd70da6a DHV |
1061 | |
1062 | return 0; | |
1caab3c1 MG |
1063 | } |
1064 | ||
b0e86302 | 1065 | static void wmi_free_devices(struct acpi_device *device) |
1caab3c1 | 1066 | { |
c64eefd4 | 1067 | struct wmi_block *wblock, *next; |
1caab3c1 MG |
1068 | |
1069 | /* Delete devices for all the GUIDs */ | |
023b9565 | 1070 | list_for_each_entry_safe(wblock, next, &wmi_block_list, list) { |
b0e86302 AL |
1071 | if (wblock->acpi_device == device) { |
1072 | list_del(&wblock->list); | |
6ee50aaa | 1073 | device_unregister(&wblock->dev.dev); |
b0e86302 | 1074 | } |
023b9565 | 1075 | } |
1caab3c1 MG |
1076 | } |
1077 | ||
b0e86302 AL |
1078 | static bool guid_already_parsed(struct acpi_device *device, |
1079 | const u8 *guid) | |
d1f9e497 | 1080 | { |
d1f9e497 | 1081 | struct wmi_block *wblock; |
d1f9e497 | 1082 | |
b0e86302 AL |
1083 | list_for_each_entry(wblock, &wmi_block_list, list) { |
1084 | if (memcmp(wblock->gblock.guid, guid, 16) == 0) { | |
1085 | /* | |
1086 | * Because we historically didn't track the relationship | |
1087 | * between GUIDs and ACPI nodes, we don't know whether | |
1088 | * we need to suppress GUIDs that are unique on a | |
1089 | * given node but duplicated across nodes. | |
1090 | */ | |
1091 | dev_warn(&device->dev, "duplicate WMI GUID %pUL (first instance was on %s)\n", | |
1092 | guid, dev_name(&wblock->acpi_device->dev)); | |
d1f9e497 | 1093 | return true; |
b0e86302 AL |
1094 | } |
1095 | } | |
d1f9e497 | 1096 | |
c64eefd4 | 1097 | return false; |
2d5ab555 DT |
1098 | } |
1099 | ||
bff431e4 CC |
1100 | /* |
1101 | * Parse the _WDG method for the GUID data blocks | |
1102 | */ | |
844af950 | 1103 | static int parse_wdg(struct device *wmi_bus_dev, struct acpi_device *device) |
bff431e4 CC |
1104 | { |
1105 | struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL}; | |
37830662 | 1106 | const struct guid_block *gblock; |
6ee50aaa DHV |
1107 | struct wmi_block *wblock, *next; |
1108 | union acpi_object *obj; | |
bff431e4 | 1109 | acpi_status status; |
6ee50aaa | 1110 | int retval = 0; |
bff431e4 CC |
1111 | u32 i, total; |
1112 | ||
7f5809bf | 1113 | status = acpi_evaluate_object(device->handle, "_WDG", NULL, &out); |
bff431e4 | 1114 | if (ACPI_FAILURE(status)) |
c64eefd4 | 1115 | return -ENXIO; |
bff431e4 CC |
1116 | |
1117 | obj = (union acpi_object *) out.pointer; | |
3d2c63eb | 1118 | if (!obj) |
c64eefd4 | 1119 | return -ENXIO; |
bff431e4 | 1120 | |
64ed0ab8 | 1121 | if (obj->type != ACPI_TYPE_BUFFER) { |
c64eefd4 | 1122 | retval = -ENXIO; |
64ed0ab8 DT |
1123 | goto out_free_pointer; |
1124 | } | |
bff431e4 | 1125 | |
37830662 | 1126 | gblock = (const struct guid_block *)obj->buffer.pointer; |
bff431e4 CC |
1127 | total = obj->buffer.length / sizeof(struct guid_block); |
1128 | ||
bff431e4 | 1129 | for (i = 0; i < total; i++) { |
58f6425e CK |
1130 | if (debug_dump_wdg) |
1131 | wmi_dump_wdg(&gblock[i]); | |
1132 | ||
a1c31bcd AL |
1133 | /* |
1134 | * Some WMI devices, like those for nVidia hooks, have a | |
1135 | * duplicate GUID. It's not clear what we should do in this | |
1136 | * case yet, so for now, we'll just ignore the duplicate | |
1137 | * for device creation. | |
1138 | */ | |
1139 | if (guid_already_parsed(device, gblock[i].guid)) | |
1140 | continue; | |
1141 | ||
58f6425e | 1142 | wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL); |
6ee50aaa DHV |
1143 | if (!wblock) { |
1144 | retval = -ENOMEM; | |
1145 | break; | |
1146 | } | |
58f6425e | 1147 | |
b0e86302 | 1148 | wblock->acpi_device = device; |
58f6425e CK |
1149 | wblock->gblock = gblock[i]; |
1150 | ||
fd70da6a DHV |
1151 | retval = wmi_create_device(wmi_bus_dev, &gblock[i], wblock, device); |
1152 | if (retval) { | |
1153 | kfree(wblock); | |
1154 | continue; | |
1155 | } | |
c64eefd4 | 1156 | |
58f6425e | 1157 | list_add_tail(&wblock->list, &wmi_block_list); |
bff431e4 | 1158 | |
fc3155b2 TR |
1159 | if (debug_event) { |
1160 | wblock->handler = wmi_notify_debug; | |
2d5ab555 | 1161 | wmi_method_enable(wblock, 1); |
fc3155b2 | 1162 | } |
bff431e4 CC |
1163 | } |
1164 | ||
6ee50aaa DHV |
1165 | /* |
1166 | * Now that all of the devices are created, add them to the | |
1167 | * device tree and probe subdrivers. | |
1168 | */ | |
1169 | list_for_each_entry_safe(wblock, next, &wmi_block_list, list) { | |
1170 | if (wblock->acpi_device != device) | |
1171 | continue; | |
1172 | ||
1173 | retval = device_add(&wblock->dev.dev); | |
1174 | if (retval) { | |
501f7e52 | 1175 | dev_err(wmi_bus_dev, "failed to register %pUL\n", |
6ee50aaa DHV |
1176 | wblock->gblock.guid); |
1177 | if (debug_event) | |
1178 | wmi_method_enable(wblock, 0); | |
1179 | list_del(&wblock->list); | |
1180 | put_device(&wblock->dev.dev); | |
1181 | } | |
1182 | } | |
c64eefd4 | 1183 | |
a5167c5b AL |
1184 | out_free_pointer: |
1185 | kfree(out.pointer); | |
c64eefd4 | 1186 | return retval; |
bff431e4 CC |
1187 | } |
1188 | ||
1189 | /* | |
1190 | * WMI can have EmbeddedControl access regions. In which case, we just want to | |
1191 | * hand these off to the EC driver. | |
1192 | */ | |
1193 | static acpi_status | |
1194 | acpi_wmi_ec_space_handler(u32 function, acpi_physical_address address, | |
439913ff | 1195 | u32 bits, u64 *value, |
bff431e4 CC |
1196 | void *handler_context, void *region_context) |
1197 | { | |
1198 | int result = 0, i = 0; | |
1199 | u8 temp = 0; | |
1200 | ||
1201 | if ((address > 0xFF) || !value) | |
1202 | return AE_BAD_PARAMETER; | |
1203 | ||
1204 | if (function != ACPI_READ && function != ACPI_WRITE) | |
1205 | return AE_BAD_PARAMETER; | |
1206 | ||
1207 | if (bits != 8) | |
1208 | return AE_BAD_PARAMETER; | |
1209 | ||
1210 | if (function == ACPI_READ) { | |
1211 | result = ec_read(address, &temp); | |
439913ff | 1212 | (*value) |= ((u64)temp) << i; |
bff431e4 CC |
1213 | } else { |
1214 | temp = 0xff & ((*value) >> i); | |
1215 | result = ec_write(address, temp); | |
1216 | } | |
1217 | ||
1218 | switch (result) { | |
1219 | case -EINVAL: | |
1220 | return AE_BAD_PARAMETER; | |
1221 | break; | |
1222 | case -ENODEV: | |
1223 | return AE_NOT_FOUND; | |
1224 | break; | |
1225 | case -ETIME: | |
1226 | return AE_TIME; | |
1227 | break; | |
1228 | default: | |
1229 | return AE_OK; | |
1230 | } | |
1231 | } | |
1232 | ||
1686f544 AL |
1233 | static void acpi_wmi_notify_handler(acpi_handle handle, u32 event, |
1234 | void *context) | |
bff431e4 CC |
1235 | { |
1236 | struct guid_block *block; | |
1237 | struct wmi_block *wblock; | |
1686f544 | 1238 | bool found_it = false; |
bff431e4 | 1239 | |
cedb3b2a | 1240 | list_for_each_entry(wblock, &wmi_block_list, list) { |
bff431e4 CC |
1241 | block = &wblock->gblock; |
1242 | ||
1686f544 | 1243 | if (wblock->acpi_device->handle == handle && |
b0e86302 | 1244 | (block->flags & ACPI_WMI_EVENT) && |
1686f544 AL |
1245 | (block->notify_id == event)) |
1246 | { | |
1247 | found_it = true; | |
bff431e4 CC |
1248 | break; |
1249 | } | |
1250 | } | |
1686f544 AL |
1251 | |
1252 | if (!found_it) | |
1253 | return; | |
1254 | ||
1255 | /* If a driver is bound, then notify the driver. */ | |
1256 | if (wblock->dev.dev.driver) { | |
1257 | struct wmi_driver *driver; | |
1258 | struct acpi_object_list input; | |
1259 | union acpi_object params[1]; | |
1260 | struct acpi_buffer evdata = { ACPI_ALLOCATE_BUFFER, NULL }; | |
1261 | acpi_status status; | |
1262 | ||
1263 | driver = container_of(wblock->dev.dev.driver, | |
1264 | struct wmi_driver, driver); | |
1265 | ||
1266 | input.count = 1; | |
1267 | input.pointer = params; | |
1268 | params[0].type = ACPI_TYPE_INTEGER; | |
1269 | params[0].integer.value = event; | |
1270 | ||
1271 | status = acpi_evaluate_object(wblock->acpi_device->handle, | |
1272 | "_WED", &input, &evdata); | |
1273 | if (ACPI_FAILURE(status)) { | |
1274 | dev_warn(&wblock->dev.dev, | |
1275 | "failed to get event data\n"); | |
1276 | return; | |
1277 | } | |
1278 | ||
1279 | if (driver->notify) | |
1280 | driver->notify(&wblock->dev, | |
1281 | (union acpi_object *)evdata.pointer); | |
1282 | ||
1283 | kfree(evdata.pointer); | |
1284 | } else if (wblock->handler) { | |
1285 | /* Legacy handler */ | |
1286 | wblock->handler(event, wblock->handler_data); | |
1287 | } | |
1288 | ||
1289 | if (debug_event) { | |
1290 | pr_info("DEBUG Event GUID: %pUL\n", | |
1291 | wblock->gblock.guid); | |
1292 | } | |
1293 | ||
1294 | acpi_bus_generate_netlink_event( | |
1295 | wblock->acpi_device->pnp.device_class, | |
1296 | dev_name(&wblock->dev.dev), | |
1297 | event, 0); | |
1298 | ||
bff431e4 CC |
1299 | } |
1300 | ||
9599ed91 | 1301 | static int acpi_wmi_remove(struct platform_device *device) |
bff431e4 | 1302 | { |
9599ed91 AL |
1303 | struct acpi_device *acpi_device = ACPI_COMPANION(&device->dev); |
1304 | ||
1305 | acpi_remove_notify_handler(acpi_device->handle, ACPI_DEVICE_NOTIFY, | |
1686f544 | 1306 | acpi_wmi_notify_handler); |
9599ed91 | 1307 | acpi_remove_address_space_handler(acpi_device->handle, |
bff431e4 | 1308 | ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler); |
9599ed91 | 1309 | wmi_free_devices(acpi_device); |
7b11e898 | 1310 | device_destroy(&wmi_bus_class, MKDEV(0, 0)); |
bff431e4 CC |
1311 | |
1312 | return 0; | |
1313 | } | |
1314 | ||
9599ed91 | 1315 | static int acpi_wmi_probe(struct platform_device *device) |
bff431e4 | 1316 | { |
9599ed91 | 1317 | struct acpi_device *acpi_device; |
844af950 | 1318 | struct device *wmi_bus_dev; |
bff431e4 | 1319 | acpi_status status; |
c64eefd4 | 1320 | int error; |
bff431e4 | 1321 | |
9599ed91 AL |
1322 | acpi_device = ACPI_COMPANION(&device->dev); |
1323 | if (!acpi_device) { | |
1324 | dev_err(&device->dev, "ACPI companion is missing\n"); | |
1325 | return -ENODEV; | |
1326 | } | |
1327 | ||
1328 | status = acpi_install_address_space_handler(acpi_device->handle, | |
bff431e4 CC |
1329 | ACPI_ADR_SPACE_EC, |
1330 | &acpi_wmi_ec_space_handler, | |
1331 | NULL, NULL); | |
5212cd67 | 1332 | if (ACPI_FAILURE(status)) { |
46492ee4 | 1333 | dev_err(&device->dev, "Error installing EC region handler\n"); |
bff431e4 | 1334 | return -ENODEV; |
5212cd67 | 1335 | } |
bff431e4 | 1336 | |
9599ed91 AL |
1337 | status = acpi_install_notify_handler(acpi_device->handle, |
1338 | ACPI_DEVICE_NOTIFY, | |
1686f544 AL |
1339 | acpi_wmi_notify_handler, |
1340 | NULL); | |
1341 | if (ACPI_FAILURE(status)) { | |
1342 | dev_err(&device->dev, "Error installing notify handler\n"); | |
1343 | error = -ENODEV; | |
1344 | goto err_remove_ec_handler; | |
1345 | } | |
1346 | ||
844af950 AL |
1347 | wmi_bus_dev = device_create(&wmi_bus_class, &device->dev, MKDEV(0, 0), |
1348 | NULL, "wmi_bus-%s", dev_name(&device->dev)); | |
1349 | if (IS_ERR(wmi_bus_dev)) { | |
1350 | error = PTR_ERR(wmi_bus_dev); | |
1686f544 | 1351 | goto err_remove_notify_handler; |
844af950 | 1352 | } |
9599ed91 | 1353 | dev_set_drvdata(&device->dev, wmi_bus_dev); |
844af950 | 1354 | |
9599ed91 | 1355 | error = parse_wdg(wmi_bus_dev, acpi_device); |
c64eefd4 | 1356 | if (error) { |
8e07514d | 1357 | pr_err("Failed to parse WDG method\n"); |
844af950 | 1358 | goto err_remove_busdev; |
bff431e4 CC |
1359 | } |
1360 | ||
c64eefd4 | 1361 | return 0; |
46492ee4 | 1362 | |
844af950 | 1363 | err_remove_busdev: |
7b11e898 | 1364 | device_destroy(&wmi_bus_class, MKDEV(0, 0)); |
844af950 | 1365 | |
1686f544 | 1366 | err_remove_notify_handler: |
9599ed91 | 1367 | acpi_remove_notify_handler(acpi_device->handle, ACPI_DEVICE_NOTIFY, |
1686f544 AL |
1368 | acpi_wmi_notify_handler); |
1369 | ||
1370 | err_remove_ec_handler: | |
9599ed91 | 1371 | acpi_remove_address_space_handler(acpi_device->handle, |
46492ee4 AL |
1372 | ACPI_ADR_SPACE_EC, |
1373 | &acpi_wmi_ec_space_handler); | |
1374 | ||
1375 | return error; | |
bff431e4 CC |
1376 | } |
1377 | ||
844af950 AL |
1378 | int __must_check __wmi_driver_register(struct wmi_driver *driver, |
1379 | struct module *owner) | |
1380 | { | |
1381 | driver->driver.owner = owner; | |
1382 | driver->driver.bus = &wmi_bus_type; | |
1383 | ||
1384 | return driver_register(&driver->driver); | |
1385 | } | |
1386 | EXPORT_SYMBOL(__wmi_driver_register); | |
1387 | ||
1388 | void wmi_driver_unregister(struct wmi_driver *driver) | |
1389 | { | |
1390 | driver_unregister(&driver->driver); | |
1391 | } | |
1392 | EXPORT_SYMBOL(wmi_driver_unregister); | |
1393 | ||
bff431e4 CC |
1394 | static int __init acpi_wmi_init(void) |
1395 | { | |
c64eefd4 | 1396 | int error; |
bff431e4 CC |
1397 | |
1398 | if (acpi_disabled) | |
1399 | return -ENODEV; | |
1400 | ||
844af950 | 1401 | error = class_register(&wmi_bus_class); |
c64eefd4 DT |
1402 | if (error) |
1403 | return error; | |
1caab3c1 | 1404 | |
844af950 AL |
1405 | error = bus_register(&wmi_bus_type); |
1406 | if (error) | |
1407 | goto err_unreg_class; | |
1408 | ||
9599ed91 | 1409 | error = platform_driver_register(&acpi_wmi_driver); |
c64eefd4 DT |
1410 | if (error) { |
1411 | pr_err("Error loading mapper\n"); | |
844af950 | 1412 | goto err_unreg_bus; |
bff431e4 CC |
1413 | } |
1414 | ||
8e07514d | 1415 | return 0; |
844af950 | 1416 | |
844af950 AL |
1417 | err_unreg_bus: |
1418 | bus_unregister(&wmi_bus_type); | |
1419 | ||
97277717 AK |
1420 | err_unreg_class: |
1421 | class_unregister(&wmi_bus_class); | |
1422 | ||
844af950 | 1423 | return error; |
bff431e4 CC |
1424 | } |
1425 | ||
1426 | static void __exit acpi_wmi_exit(void) | |
1427 | { | |
9599ed91 | 1428 | platform_driver_unregister(&acpi_wmi_driver); |
844af950 | 1429 | bus_unregister(&wmi_bus_type); |
303d1fcc | 1430 | class_unregister(&wmi_bus_class); |
bff431e4 CC |
1431 | } |
1432 | ||
98b8e4e5 | 1433 | subsys_initcall_sync(acpi_wmi_init); |
bff431e4 | 1434 | module_exit(acpi_wmi_exit); |