2 * ACPI-WMI mapping driver
4 * Copyright (C) 2007-2008 Carlos Corbacho <carlos@strangeworlds.co.uk>
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>
11 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
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.
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.
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.
27 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
30 #include <linux/kernel.h>
31 #include <linux/init.h>
32 #include <linux/types.h>
33 #include <linux/device.h>
34 #include <linux/list.h>
35 #include <linux/acpi.h>
36 #include <linux/slab.h>
37 #include <acpi/acpi_bus.h>
38 #include <acpi/acpi_drivers.h>
40 ACPI_MODULE_NAME("wmi");
41 MODULE_AUTHOR("Carlos Corbacho");
42 MODULE_DESCRIPTION("ACPI-WMI Mapping Driver");
43 MODULE_LICENSE("GPL");
45 #define ACPI_WMI_CLASS "wmi"
47 #define PREFIX "ACPI: WMI: "
49 static DEFINE_MUTEX(wmi_data_lock);
56 unsigned char notify_id;
57 unsigned char reserved;
65 struct list_head list;
66 struct guid_block gblock;
68 wmi_notify_handler handler;
73 static struct wmi_block wmi_blocks;
76 * If the GUID data block is marked as expensive, we must enable and
77 * explicitily disable data collection.
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 */
84 static int debug_event;
85 module_param(debug_event, bool, 0444);
86 MODULE_PARM_DESC(debug_event,
87 "Log WMI Events [0/1]");
89 static int acpi_wmi_remove(struct acpi_device *device, int type);
90 static int acpi_wmi_add(struct acpi_device *device);
91 static void acpi_wmi_notify(struct acpi_device *device, u32 event);
93 static const struct acpi_device_id wmi_device_ids[] = {
98 MODULE_DEVICE_TABLE(acpi, wmi_device_ids);
100 static struct acpi_driver acpi_wmi_driver = {
102 .class = ACPI_WMI_CLASS,
103 .ids = wmi_device_ids,
106 .remove = acpi_wmi_remove,
107 .notify = acpi_wmi_notify,
112 * GUID parsing functions
116 * wmi_parse_hexbyte - Convert a ASCII hex number to a byte
117 * @src: Pointer to at least 2 characters to convert.
119 * Convert a two character ASCII hex string to a number.
121 * Return: 0-255 Success, the byte was parsed correctly
122 * -1 Error, an invalid character was supplied
124 static int wmi_parse_hexbyte(const u8 *src)
126 unsigned int x; /* For correct wrapping */
131 if (x - '0' <= '9' - '0') {
133 } else if (x - 'a' <= 'f' - 'a') {
135 } else if (x - 'A' <= 'F' - 'A') {
144 if (x - '0' <= '9' - '0')
145 return h | (x - '0');
146 if (x - 'a' <= 'f' - 'a')
147 return h | (x - 'a' + 10);
148 if (x - 'A' <= 'F' - 'A')
149 return h | (x - 'A' + 10);
154 * wmi_swap_bytes - Rearrange GUID bytes to match GUID binary
155 * @src: Memory block holding binary GUID (16 bytes)
156 * @dest: Memory block to hold byte swapped binary GUID (16 bytes)
158 * Byte swap a binary GUID to match it's real GUID value
160 static void wmi_swap_bytes(u8 *src, u8 *dest)
164 for (i = 0; i <= 3; i++)
165 memcpy(dest + i, src + (3 - i), 1);
167 for (i = 0; i <= 1; i++)
168 memcpy(dest + 4 + i, src + (5 - i), 1);
170 for (i = 0; i <= 1; i++)
171 memcpy(dest + 6 + i, src + (7 - i), 1);
173 memcpy(dest + 8, src + 8, 8);
177 * wmi_parse_guid - Convert GUID from ASCII to binary
178 * @src: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
179 * @dest: Memory block to hold binary GUID (16 bytes)
181 * N.B. The GUID need not be NULL terminated.
183 * Return: 'true' @dest contains binary GUID
184 * 'false' @dest contents are undefined
186 static bool wmi_parse_guid(const u8 *src, u8 *dest)
188 static const int size[] = { 4, 2, 2, 2, 6 };
191 if (src[8] != '-' || src[13] != '-' ||
192 src[18] != '-' || src[23] != '-')
195 for (j = 0; j < 5; j++, src++) {
196 for (i = 0; i < size[j]; i++, src += 2, *dest++ = v) {
197 v = wmi_parse_hexbyte(src);
207 * Convert a raw GUID to the ACII string representation
209 static int wmi_gtoa(const char *in, char *out)
213 for (i = 3; i >= 0; i--)
214 out += sprintf(out, "%02X", in[i] & 0xFF);
216 out += sprintf(out, "-");
217 out += sprintf(out, "%02X", in[5] & 0xFF);
218 out += sprintf(out, "%02X", in[4] & 0xFF);
219 out += sprintf(out, "-");
220 out += sprintf(out, "%02X", in[7] & 0xFF);
221 out += sprintf(out, "%02X", in[6] & 0xFF);
222 out += sprintf(out, "-");
223 out += sprintf(out, "%02X", in[8] & 0xFF);
224 out += sprintf(out, "%02X", in[9] & 0xFF);
225 out += sprintf(out, "-");
227 for (i = 10; i <= 15; i++)
228 out += sprintf(out, "%02X", in[i] & 0xFF);
234 static bool find_guid(const char *guid_string, struct wmi_block **out)
236 char tmp[16], guid_input[16];
237 struct wmi_block *wblock;
238 struct guid_block *block;
241 wmi_parse_guid(guid_string, tmp);
242 wmi_swap_bytes(tmp, guid_input);
244 list_for_each(p, &wmi_blocks.list) {
245 wblock = list_entry(p, struct wmi_block, list);
246 block = &wblock->gblock;
248 if (memcmp(block->guid, guid_input, 16) == 0) {
257 static acpi_status wmi_method_enable(struct wmi_block *wblock, int enable)
259 struct guid_block *block = NULL;
261 struct acpi_object_list input;
262 union acpi_object params[1];
266 block = &wblock->gblock;
267 handle = wblock->handle;
273 input.pointer = params;
274 params[0].type = ACPI_TYPE_INTEGER;
275 params[0].integer.value = enable;
277 snprintf(method, 5, "WE%02X", block->notify_id);
278 status = acpi_evaluate_object(handle, method, &input, NULL);
280 if (status != AE_OK && status != AE_NOT_FOUND)
287 * Exported WMI functions
290 * wmi_evaluate_method - Evaluate a WMI method
291 * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
292 * @instance: Instance index
293 * @method_id: Method ID to call
294 * &in: Buffer containing input for the method call
295 * &out: Empty buffer to return the method results
297 * Call an ACPI-WMI method
299 acpi_status wmi_evaluate_method(const char *guid_string, u8 instance,
300 u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out)
302 struct guid_block *block = NULL;
303 struct wmi_block *wblock = NULL;
306 struct acpi_object_list input;
307 union acpi_object params[3];
308 char method[5] = "WM";
310 if (!find_guid(guid_string, &wblock))
313 block = &wblock->gblock;
314 handle = wblock->handle;
316 if (!(block->flags & ACPI_WMI_METHOD))
319 if (block->instance_count < instance)
320 return AE_BAD_PARAMETER;
323 input.pointer = params;
324 params[0].type = ACPI_TYPE_INTEGER;
325 params[0].integer.value = instance;
326 params[1].type = ACPI_TYPE_INTEGER;
327 params[1].integer.value = method_id;
332 if (block->flags & ACPI_WMI_STRING) {
333 params[2].type = ACPI_TYPE_STRING;
335 params[2].type = ACPI_TYPE_BUFFER;
337 params[2].buffer.length = in->length;
338 params[2].buffer.pointer = in->pointer;
341 strncat(method, block->object_id, 2);
343 status = acpi_evaluate_object(handle, method, &input, out);
347 EXPORT_SYMBOL_GPL(wmi_evaluate_method);
350 * wmi_query_block - Return contents of a WMI block
351 * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
352 * @instance: Instance index
353 * &out: Empty buffer to return the contents of the data block to
355 * Return the contents of an ACPI-WMI data block to a buffer
357 acpi_status wmi_query_block(const char *guid_string, u8 instance,
358 struct acpi_buffer *out)
360 struct guid_block *block = NULL;
361 struct wmi_block *wblock = NULL;
362 acpi_handle handle, wc_handle;
363 acpi_status status, wc_status = AE_ERROR;
364 struct acpi_object_list input, wc_input;
365 union acpi_object wc_params[1], wq_params[1];
367 char wc_method[5] = "WC";
369 if (!guid_string || !out)
370 return AE_BAD_PARAMETER;
372 if (!find_guid(guid_string, &wblock))
375 block = &wblock->gblock;
376 handle = wblock->handle;
378 if (block->instance_count < instance)
379 return AE_BAD_PARAMETER;
381 /* Check GUID is a data block */
382 if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD))
386 input.pointer = wq_params;
387 wq_params[0].type = ACPI_TYPE_INTEGER;
388 wq_params[0].integer.value = instance;
391 * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method first to
394 if (block->flags & ACPI_WMI_EXPENSIVE) {
396 wc_input.pointer = wc_params;
397 wc_params[0].type = ACPI_TYPE_INTEGER;
398 wc_params[0].integer.value = 1;
400 strncat(wc_method, block->object_id, 2);
403 * Some GUIDs break the specification by declaring themselves
404 * expensive, but have no corresponding WCxx method. So we
405 * should not fail if this happens.
407 wc_status = acpi_get_handle(handle, wc_method, &wc_handle);
408 if (ACPI_SUCCESS(wc_status))
409 wc_status = acpi_evaluate_object(handle, wc_method,
413 strcpy(method, "WQ");
414 strncat(method, block->object_id, 2);
416 status = acpi_evaluate_object(handle, method, &input, out);
419 * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method, even if
420 * the WQxx method failed - we should disable collection anyway.
422 if ((block->flags & ACPI_WMI_EXPENSIVE) && ACPI_SUCCESS(wc_status)) {
423 wc_params[0].integer.value = 0;
424 status = acpi_evaluate_object(handle,
425 wc_method, &wc_input, NULL);
430 EXPORT_SYMBOL_GPL(wmi_query_block);
433 * wmi_set_block - Write to a WMI block
434 * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
435 * @instance: Instance index
436 * &in: Buffer containing new values for the data block
438 * Write the contents of the input buffer to an ACPI-WMI data block
440 acpi_status wmi_set_block(const char *guid_string, u8 instance,
441 const struct acpi_buffer *in)
443 struct guid_block *block = NULL;
444 struct wmi_block *wblock = NULL;
446 struct acpi_object_list input;
447 union acpi_object params[2];
448 char method[5] = "WS";
450 if (!guid_string || !in)
453 if (!find_guid(guid_string, &wblock))
456 block = &wblock->gblock;
457 handle = wblock->handle;
459 if (block->instance_count < instance)
460 return AE_BAD_PARAMETER;
462 /* Check GUID is a data block */
463 if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD))
467 input.pointer = params;
468 params[0].type = ACPI_TYPE_INTEGER;
469 params[0].integer.value = instance;
471 if (block->flags & ACPI_WMI_STRING) {
472 params[1].type = ACPI_TYPE_STRING;
474 params[1].type = ACPI_TYPE_BUFFER;
476 params[1].buffer.length = in->length;
477 params[1].buffer.pointer = in->pointer;
479 strncat(method, block->object_id, 2);
481 return acpi_evaluate_object(handle, method, &input, NULL);
483 EXPORT_SYMBOL_GPL(wmi_set_block);
485 static void wmi_notify_debug(u32 value, void *context)
487 struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
488 union acpi_object *obj;
490 wmi_get_event_data(value, &response);
492 obj = (union acpi_object *)response.pointer;
497 printk(KERN_INFO PREFIX "DEBUG Event ");
499 case ACPI_TYPE_BUFFER:
500 printk("BUFFER_TYPE - length %d\n", obj->buffer.length);
502 case ACPI_TYPE_STRING:
503 printk("STRING_TYPE - %s\n", obj->string.pointer);
505 case ACPI_TYPE_INTEGER:
506 printk("INTEGER_TYPE - %llu\n", obj->integer.value);
508 case ACPI_TYPE_PACKAGE:
509 printk("PACKAGE_TYPE - %d elements\n", obj->package.count);
512 printk("object type 0x%X\n", obj->type);
517 * wmi_install_notify_handler - Register handler for WMI events
518 * @handler: Function to handle notifications
519 * @data: Data to be returned to handler when event is fired
521 * Register a handler for events sent to the ACPI-WMI mapper device.
523 acpi_status wmi_install_notify_handler(const char *guid,
524 wmi_notify_handler handler, void *data)
526 struct wmi_block *block;
529 if (!guid || !handler)
530 return AE_BAD_PARAMETER;
532 if (!find_guid(guid, &block))
535 if (block->handler && block->handler != wmi_notify_debug)
536 return AE_ALREADY_ACQUIRED;
538 block->handler = handler;
539 block->handler_data = data;
541 status = wmi_method_enable(block, 1);
545 EXPORT_SYMBOL_GPL(wmi_install_notify_handler);
548 * wmi_uninstall_notify_handler - Unregister handler for WMI events
550 * Unregister handler for events sent to the ACPI-WMI mapper device.
552 acpi_status wmi_remove_notify_handler(const char *guid)
554 struct wmi_block *block;
555 acpi_status status = AE_OK;
558 return AE_BAD_PARAMETER;
560 if (!find_guid(guid, &block))
563 if (!block->handler || block->handler == wmi_notify_debug)
564 return AE_NULL_ENTRY;
567 block->handler = wmi_notify_debug;
569 status = wmi_method_enable(block, 0);
570 block->handler = NULL;
571 block->handler_data = NULL;
575 EXPORT_SYMBOL_GPL(wmi_remove_notify_handler);
578 * wmi_get_event_data - Get WMI data associated with an event
580 * @event: Event to find
581 * @out: Buffer to hold event data. out->pointer should be freed with kfree()
583 * Returns extra data associated with an event in WMI.
585 acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out)
587 struct acpi_object_list input;
588 union acpi_object params[1];
589 struct guid_block *gblock;
590 struct wmi_block *wblock;
594 input.pointer = params;
595 params[0].type = ACPI_TYPE_INTEGER;
596 params[0].integer.value = event;
598 list_for_each(p, &wmi_blocks.list) {
599 wblock = list_entry(p, struct wmi_block, list);
600 gblock = &wblock->gblock;
602 if ((gblock->flags & ACPI_WMI_EVENT) &&
603 (gblock->notify_id == event))
604 return acpi_evaluate_object(wblock->handle, "_WED",
610 EXPORT_SYMBOL_GPL(wmi_get_event_data);
613 * wmi_has_guid - Check if a GUID is available
614 * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
616 * Check if a given GUID is defined by _WDG
618 bool wmi_has_guid(const char *guid_string)
620 return find_guid(guid_string, NULL);
622 EXPORT_SYMBOL_GPL(wmi_has_guid);
627 static ssize_t show_modalias(struct device *dev, struct device_attribute *attr,
630 char guid_string[37];
631 struct wmi_block *wblock;
633 wblock = dev_get_drvdata(dev);
637 wmi_gtoa(wblock->gblock.guid, guid_string);
639 return sprintf(buf, "wmi:%s\n", guid_string);
641 static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL);
643 static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env)
645 char guid_string[37];
647 struct wmi_block *wblock;
649 if (add_uevent_var(env, "MODALIAS="))
652 wblock = dev_get_drvdata(dev);
656 wmi_gtoa(wblock->gblock.guid, guid_string);
658 strcpy(&env->buf[env->buflen - 1], "wmi:");
659 memcpy(&env->buf[env->buflen - 1 + 4], guid_string, 36);
665 static void wmi_dev_free(struct device *dev)
670 static struct class wmi_class = {
672 .dev_release = wmi_dev_free,
673 .dev_uevent = wmi_dev_uevent,
676 static int wmi_create_devs(void)
679 char guid_string[37];
680 struct guid_block *gblock;
681 struct wmi_block *wblock;
683 struct device *guid_dev;
685 /* Create devices for all the GUIDs */
686 list_for_each(p, &wmi_blocks.list) {
687 wblock = list_entry(p, struct wmi_block, list);
689 guid_dev = kzalloc(sizeof(struct device), GFP_KERNEL);
693 wblock->dev = guid_dev;
695 guid_dev->class = &wmi_class;
696 dev_set_drvdata(guid_dev, wblock);
698 gblock = &wblock->gblock;
700 wmi_gtoa(gblock->guid, guid_string);
701 dev_set_name(guid_dev, guid_string);
703 result = device_register(guid_dev);
707 result = device_create_file(guid_dev, &dev_attr_modalias);
715 static void wmi_remove_devs(void)
717 struct guid_block *gblock;
718 struct wmi_block *wblock;
720 struct device *guid_dev;
722 /* Delete devices for all the GUIDs */
723 list_for_each(p, &wmi_blocks.list) {
724 wblock = list_entry(p, struct wmi_block, list);
726 guid_dev = wblock->dev;
727 gblock = &wblock->gblock;
729 device_remove_file(guid_dev, &dev_attr_modalias);
731 device_unregister(guid_dev);
735 static void wmi_class_exit(void)
738 class_unregister(&wmi_class);
741 static int wmi_class_init(void)
745 ret = class_register(&wmi_class);
749 ret = wmi_create_devs();
756 static bool guid_already_parsed(const char *guid_string)
758 struct guid_block *gblock;
759 struct wmi_block *wblock;
762 list_for_each(p, &wmi_blocks.list) {
763 wblock = list_entry(p, struct wmi_block, list);
764 gblock = &wblock->gblock;
766 if (strncmp(gblock->guid, guid_string, 16) == 0)
773 * Parse the _WDG method for the GUID data blocks
775 static __init acpi_status parse_wdg(acpi_handle handle)
777 struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
778 union acpi_object *obj;
779 struct guid_block *gblock;
780 struct wmi_block *wblock;
781 char guid_string[37];
785 status = acpi_evaluate_object(handle, "_WDG", NULL, &out);
787 if (ACPI_FAILURE(status))
790 obj = (union acpi_object *) out.pointer;
792 if (obj->type != ACPI_TYPE_BUFFER)
795 total = obj->buffer.length / sizeof(struct guid_block);
797 gblock = kmemdup(obj->buffer.pointer, obj->buffer.length, GFP_KERNEL);
801 for (i = 0; i < total; i++) {
803 Some WMI devices, like those for nVidia hooks, have a
804 duplicate GUID. It's not clear what we should do in this
805 case yet, so for now, we'll just ignore the duplicate.
806 Anyone who wants to add support for that device can come
807 up with a better workaround for the mess then.
809 if (guid_already_parsed(gblock[i].guid) == true) {
810 wmi_gtoa(gblock[i].guid, guid_string);
811 printk(KERN_INFO PREFIX "Skipping duplicate GUID %s\n",
815 wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL);
819 wblock->gblock = gblock[i];
820 wblock->handle = handle;
822 wblock->handler = wmi_notify_debug;
823 status = wmi_method_enable(wblock, 1);
825 list_add_tail(&wblock->list, &wmi_blocks.list);
835 * WMI can have EmbeddedControl access regions. In which case, we just want to
836 * hand these off to the EC driver.
839 acpi_wmi_ec_space_handler(u32 function, acpi_physical_address address,
840 u32 bits, u64 *value,
841 void *handler_context, void *region_context)
843 int result = 0, i = 0;
846 if ((address > 0xFF) || !value)
847 return AE_BAD_PARAMETER;
849 if (function != ACPI_READ && function != ACPI_WRITE)
850 return AE_BAD_PARAMETER;
853 return AE_BAD_PARAMETER;
855 if (function == ACPI_READ) {
856 result = ec_read(address, &temp);
857 (*value) |= ((u64)temp) << i;
859 temp = 0xff & ((*value) >> i);
860 result = ec_write(address, temp);
865 return AE_BAD_PARAMETER;
878 static void acpi_wmi_notify(struct acpi_device *device, u32 event)
880 struct guid_block *block;
881 struct wmi_block *wblock;
883 char guid_string[37];
885 list_for_each(p, &wmi_blocks.list) {
886 wblock = list_entry(p, struct wmi_block, list);
887 block = &wblock->gblock;
889 if ((block->flags & ACPI_WMI_EVENT) &&
890 (block->notify_id == event)) {
892 wblock->handler(event, wblock->handler_data);
894 wmi_gtoa(wblock->gblock.guid, guid_string);
895 printk(KERN_INFO PREFIX "DEBUG Event GUID:"
896 " %s\n", guid_string);
899 acpi_bus_generate_netlink_event(
900 device->pnp.device_class, dev_name(&device->dev),
907 static int acpi_wmi_remove(struct acpi_device *device, int type)
909 acpi_remove_address_space_handler(device->handle,
910 ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler);
915 static int __init acpi_wmi_add(struct acpi_device *device)
920 status = acpi_install_address_space_handler(device->handle,
922 &acpi_wmi_ec_space_handler,
924 if (ACPI_FAILURE(status))
927 status = parse_wdg(device->handle);
928 if (ACPI_FAILURE(status)) {
929 printk(KERN_ERR PREFIX "Error installing EC region handler\n");
936 static int __init acpi_wmi_init(void)
940 INIT_LIST_HEAD(&wmi_blocks.list);
945 result = acpi_bus_register_driver(&acpi_wmi_driver);
948 printk(KERN_INFO PREFIX "Error loading mapper\n");
952 result = wmi_class_init();
954 acpi_bus_unregister_driver(&acpi_wmi_driver);
958 printk(KERN_INFO PREFIX "Mapper loaded\n");
963 static void __exit acpi_wmi_exit(void)
965 struct list_head *p, *tmp;
966 struct wmi_block *wblock;
970 acpi_bus_unregister_driver(&acpi_wmi_driver);
972 list_for_each_safe(p, tmp, &wmi_blocks.list) {
973 wblock = list_entry(p, struct wmi_block, list);
979 printk(KERN_INFO PREFIX "Mapper unloaded\n");
982 subsys_initcall(acpi_wmi_init);
983 module_exit(acpi_wmi_exit);