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 acpi_wmi_remove(struct acpi_device
*device
, int type
);
85 static int acpi_wmi_add(struct acpi_device
*device
);
86 static void acpi_wmi_notify(struct acpi_device
*device
, u32 event
);
88 static const struct acpi_device_id wmi_device_ids
[] = {
93 MODULE_DEVICE_TABLE(acpi
, wmi_device_ids
);
95 static struct acpi_driver acpi_wmi_driver
= {
97 .class = ACPI_WMI_CLASS
,
98 .ids
= wmi_device_ids
,
101 .remove
= acpi_wmi_remove
,
102 .notify
= acpi_wmi_notify
,
107 * GUID parsing functions
111 * wmi_parse_hexbyte - Convert a ASCII hex number to a byte
112 * @src: Pointer to at least 2 characters to convert.
114 * Convert a two character ASCII hex string to a number.
116 * Return: 0-255 Success, the byte was parsed correctly
117 * -1 Error, an invalid character was supplied
119 static int wmi_parse_hexbyte(const u8
*src
)
121 unsigned int x
; /* For correct wrapping */
126 if (x
- '0' <= '9' - '0') {
128 } else if (x
- 'a' <= 'f' - 'a') {
130 } else if (x
- 'A' <= 'F' - 'A') {
139 if (x
- '0' <= '9' - '0')
140 return h
| (x
- '0');
141 if (x
- 'a' <= 'f' - 'a')
142 return h
| (x
- 'a' + 10);
143 if (x
- 'A' <= 'F' - 'A')
144 return h
| (x
- 'A' + 10);
149 * wmi_swap_bytes - Rearrange GUID bytes to match GUID binary
150 * @src: Memory block holding binary GUID (16 bytes)
151 * @dest: Memory block to hold byte swapped binary GUID (16 bytes)
153 * Byte swap a binary GUID to match it's real GUID value
155 static void wmi_swap_bytes(u8
*src
, u8
*dest
)
159 for (i
= 0; i
<= 3; i
++)
160 memcpy(dest
+ i
, src
+ (3 - i
), 1);
162 for (i
= 0; i
<= 1; i
++)
163 memcpy(dest
+ 4 + i
, src
+ (5 - i
), 1);
165 for (i
= 0; i
<= 1; i
++)
166 memcpy(dest
+ 6 + i
, src
+ (7 - i
), 1);
168 memcpy(dest
+ 8, src
+ 8, 8);
172 * wmi_parse_guid - Convert GUID from ASCII to binary
173 * @src: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
174 * @dest: Memory block to hold binary GUID (16 bytes)
176 * N.B. The GUID need not be NULL terminated.
178 * Return: 'true' @dest contains binary GUID
179 * 'false' @dest contents are undefined
181 static bool wmi_parse_guid(const u8
*src
, u8
*dest
)
183 static const int size
[] = { 4, 2, 2, 2, 6 };
186 if (src
[8] != '-' || src
[13] != '-' ||
187 src
[18] != '-' || src
[23] != '-')
190 for (j
= 0; j
< 5; j
++, src
++) {
191 for (i
= 0; i
< size
[j
]; i
++, src
+= 2, *dest
++ = v
) {
192 v
= wmi_parse_hexbyte(src
);
202 * Convert a raw GUID to the ACII string representation
204 static int wmi_gtoa(const char *in
, char *out
)
208 for (i
= 3; i
>= 0; i
--)
209 out
+= sprintf(out
, "%02X", in
[i
] & 0xFF);
211 out
+= sprintf(out
, "-");
212 out
+= sprintf(out
, "%02X", in
[5] & 0xFF);
213 out
+= sprintf(out
, "%02X", in
[4] & 0xFF);
214 out
+= sprintf(out
, "-");
215 out
+= sprintf(out
, "%02X", in
[7] & 0xFF);
216 out
+= sprintf(out
, "%02X", in
[6] & 0xFF);
217 out
+= sprintf(out
, "-");
218 out
+= sprintf(out
, "%02X", in
[8] & 0xFF);
219 out
+= sprintf(out
, "%02X", in
[9] & 0xFF);
220 out
+= sprintf(out
, "-");
222 for (i
= 10; i
<= 15; i
++)
223 out
+= sprintf(out
, "%02X", in
[i
] & 0xFF);
229 static bool find_guid(const char *guid_string
, struct wmi_block
**out
)
231 char tmp
[16], guid_input
[16];
232 struct wmi_block
*wblock
;
233 struct guid_block
*block
;
236 wmi_parse_guid(guid_string
, tmp
);
237 wmi_swap_bytes(tmp
, guid_input
);
239 list_for_each(p
, &wmi_blocks
.list
) {
240 wblock
= list_entry(p
, struct wmi_block
, list
);
241 block
= &wblock
->gblock
;
243 if (memcmp(block
->guid
, guid_input
, 16) == 0) {
252 static acpi_status
wmi_method_enable(struct wmi_block
*wblock
, int enable
)
254 struct guid_block
*block
= NULL
;
256 struct acpi_object_list input
;
257 union acpi_object params
[1];
261 block
= &wblock
->gblock
;
262 handle
= wblock
->handle
;
268 input
.pointer
= params
;
269 params
[0].type
= ACPI_TYPE_INTEGER
;
270 params
[0].integer
.value
= enable
;
272 snprintf(method
, 5, "WE%02X", block
->notify_id
);
273 status
= acpi_evaluate_object(handle
, method
, &input
, NULL
);
275 if (status
!= AE_OK
&& status
!= AE_NOT_FOUND
)
282 * Exported WMI functions
285 * wmi_evaluate_method - Evaluate a WMI method
286 * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
287 * @instance: Instance index
288 * @method_id: Method ID to call
289 * &in: Buffer containing input for the method call
290 * &out: Empty buffer to return the method results
292 * Call an ACPI-WMI method
294 acpi_status
wmi_evaluate_method(const char *guid_string
, u8 instance
,
295 u32 method_id
, const struct acpi_buffer
*in
, struct acpi_buffer
*out
)
297 struct guid_block
*block
= NULL
;
298 struct wmi_block
*wblock
= NULL
;
301 struct acpi_object_list input
;
302 union acpi_object params
[3];
303 char method
[5] = "WM";
305 if (!find_guid(guid_string
, &wblock
))
308 block
= &wblock
->gblock
;
309 handle
= wblock
->handle
;
311 if (!(block
->flags
& ACPI_WMI_METHOD
))
314 if (block
->instance_count
< instance
)
315 return AE_BAD_PARAMETER
;
318 input
.pointer
= params
;
319 params
[0].type
= ACPI_TYPE_INTEGER
;
320 params
[0].integer
.value
= instance
;
321 params
[1].type
= ACPI_TYPE_INTEGER
;
322 params
[1].integer
.value
= method_id
;
327 if (block
->flags
& ACPI_WMI_STRING
) {
328 params
[2].type
= ACPI_TYPE_STRING
;
330 params
[2].type
= ACPI_TYPE_BUFFER
;
332 params
[2].buffer
.length
= in
->length
;
333 params
[2].buffer
.pointer
= in
->pointer
;
336 strncat(method
, block
->object_id
, 2);
338 status
= acpi_evaluate_object(handle
, method
, &input
, out
);
342 EXPORT_SYMBOL_GPL(wmi_evaluate_method
);
345 * wmi_query_block - Return contents of a WMI block
346 * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
347 * @instance: Instance index
348 * &out: Empty buffer to return the contents of the data block to
350 * Return the contents of an ACPI-WMI data block to a buffer
352 acpi_status
wmi_query_block(const char *guid_string
, u8 instance
,
353 struct acpi_buffer
*out
)
355 struct guid_block
*block
= NULL
;
356 struct wmi_block
*wblock
= NULL
;
357 acpi_handle handle
, wc_handle
;
358 acpi_status status
, wc_status
= AE_ERROR
;
359 struct acpi_object_list input
, wc_input
;
360 union acpi_object wc_params
[1], wq_params
[1];
362 char wc_method
[5] = "WC";
364 if (!guid_string
|| !out
)
365 return AE_BAD_PARAMETER
;
367 if (!find_guid(guid_string
, &wblock
))
370 block
= &wblock
->gblock
;
371 handle
= wblock
->handle
;
373 if (block
->instance_count
< instance
)
374 return AE_BAD_PARAMETER
;
376 /* Check GUID is a data block */
377 if (block
->flags
& (ACPI_WMI_EVENT
| ACPI_WMI_METHOD
))
381 input
.pointer
= wq_params
;
382 wq_params
[0].type
= ACPI_TYPE_INTEGER
;
383 wq_params
[0].integer
.value
= instance
;
386 * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method first to
389 if (block
->flags
& ACPI_WMI_EXPENSIVE
) {
391 wc_input
.pointer
= wc_params
;
392 wc_params
[0].type
= ACPI_TYPE_INTEGER
;
393 wc_params
[0].integer
.value
= 1;
395 strncat(wc_method
, block
->object_id
, 2);
398 * Some GUIDs break the specification by declaring themselves
399 * expensive, but have no corresponding WCxx method. So we
400 * should not fail if this happens.
402 wc_status
= acpi_get_handle(handle
, wc_method
, &wc_handle
);
403 if (ACPI_SUCCESS(wc_status
))
404 wc_status
= acpi_evaluate_object(handle
, wc_method
,
408 strcpy(method
, "WQ");
409 strncat(method
, block
->object_id
, 2);
411 status
= acpi_evaluate_object(handle
, method
, &input
, out
);
414 * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method, even if
415 * the WQxx method failed - we should disable collection anyway.
417 if ((block
->flags
& ACPI_WMI_EXPENSIVE
) && ACPI_SUCCESS(wc_status
)) {
418 wc_params
[0].integer
.value
= 0;
419 status
= acpi_evaluate_object(handle
,
420 wc_method
, &wc_input
, NULL
);
425 EXPORT_SYMBOL_GPL(wmi_query_block
);
428 * wmi_set_block - Write to a WMI block
429 * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
430 * @instance: Instance index
431 * &in: Buffer containing new values for the data block
433 * Write the contents of the input buffer to an ACPI-WMI data block
435 acpi_status
wmi_set_block(const char *guid_string
, u8 instance
,
436 const struct acpi_buffer
*in
)
438 struct guid_block
*block
= NULL
;
439 struct wmi_block
*wblock
= NULL
;
441 struct acpi_object_list input
;
442 union acpi_object params
[2];
443 char method
[5] = "WS";
445 if (!guid_string
|| !in
)
448 if (!find_guid(guid_string
, &wblock
))
451 block
= &wblock
->gblock
;
452 handle
= wblock
->handle
;
454 if (block
->instance_count
< instance
)
455 return AE_BAD_PARAMETER
;
457 /* Check GUID is a data block */
458 if (block
->flags
& (ACPI_WMI_EVENT
| ACPI_WMI_METHOD
))
462 input
.pointer
= params
;
463 params
[0].type
= ACPI_TYPE_INTEGER
;
464 params
[0].integer
.value
= instance
;
466 if (block
->flags
& ACPI_WMI_STRING
) {
467 params
[1].type
= ACPI_TYPE_STRING
;
469 params
[1].type
= ACPI_TYPE_BUFFER
;
471 params
[1].buffer
.length
= in
->length
;
472 params
[1].buffer
.pointer
= in
->pointer
;
474 strncat(method
, block
->object_id
, 2);
476 return acpi_evaluate_object(handle
, method
, &input
, NULL
);
478 EXPORT_SYMBOL_GPL(wmi_set_block
);
481 * wmi_install_notify_handler - Register handler for WMI events
482 * @handler: Function to handle notifications
483 * @data: Data to be returned to handler when event is fired
485 * Register a handler for events sent to the ACPI-WMI mapper device.
487 acpi_status
wmi_install_notify_handler(const char *guid
,
488 wmi_notify_handler handler
, void *data
)
490 struct wmi_block
*block
;
493 if (!guid
|| !handler
)
494 return AE_BAD_PARAMETER
;
496 if (!find_guid(guid
, &block
))
500 return AE_ALREADY_ACQUIRED
;
502 block
->handler
= handler
;
503 block
->handler_data
= data
;
505 status
= wmi_method_enable(block
, 1);
509 EXPORT_SYMBOL_GPL(wmi_install_notify_handler
);
512 * wmi_uninstall_notify_handler - Unregister handler for WMI events
514 * Unregister handler for events sent to the ACPI-WMI mapper device.
516 acpi_status
wmi_remove_notify_handler(const char *guid
)
518 struct wmi_block
*block
;
522 return AE_BAD_PARAMETER
;
524 if (!find_guid(guid
, &block
))
528 return AE_NULL_ENTRY
;
530 status
= wmi_method_enable(block
, 0);
532 block
->handler
= NULL
;
533 block
->handler_data
= NULL
;
537 EXPORT_SYMBOL_GPL(wmi_remove_notify_handler
);
540 * wmi_get_event_data - Get WMI data associated with an event
542 * @event: Event to find
543 * @out: Buffer to hold event data. out->pointer should be freed with kfree()
545 * Returns extra data associated with an event in WMI.
547 acpi_status
wmi_get_event_data(u32 event
, struct acpi_buffer
*out
)
549 struct acpi_object_list input
;
550 union acpi_object params
[1];
551 struct guid_block
*gblock
;
552 struct wmi_block
*wblock
;
556 input
.pointer
= params
;
557 params
[0].type
= ACPI_TYPE_INTEGER
;
558 params
[0].integer
.value
= event
;
560 list_for_each(p
, &wmi_blocks
.list
) {
561 wblock
= list_entry(p
, struct wmi_block
, list
);
562 gblock
= &wblock
->gblock
;
564 if ((gblock
->flags
& ACPI_WMI_EVENT
) &&
565 (gblock
->notify_id
== event
))
566 return acpi_evaluate_object(wblock
->handle
, "_WED",
572 EXPORT_SYMBOL_GPL(wmi_get_event_data
);
575 * wmi_has_guid - Check if a GUID is available
576 * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
578 * Check if a given GUID is defined by _WDG
580 bool wmi_has_guid(const char *guid_string
)
582 return find_guid(guid_string
, NULL
);
584 EXPORT_SYMBOL_GPL(wmi_has_guid
);
589 static ssize_t
show_modalias(struct device
*dev
, struct device_attribute
*attr
,
592 char guid_string
[37];
593 struct wmi_block
*wblock
;
595 wblock
= dev_get_drvdata(dev
);
599 wmi_gtoa(wblock
->gblock
.guid
, guid_string
);
601 return sprintf(buf
, "wmi:%s\n", guid_string
);
603 static DEVICE_ATTR(modalias
, S_IRUGO
, show_modalias
, NULL
);
605 static int wmi_dev_uevent(struct device
*dev
, struct kobj_uevent_env
*env
)
607 char guid_string
[37];
609 struct wmi_block
*wblock
;
611 if (add_uevent_var(env
, "MODALIAS="))
614 wblock
= dev_get_drvdata(dev
);
618 wmi_gtoa(wblock
->gblock
.guid
, guid_string
);
620 strcpy(&env
->buf
[env
->buflen
- 1], "wmi:");
621 memcpy(&env
->buf
[env
->buflen
- 1 + 4], guid_string
, 36);
627 static void wmi_dev_free(struct device
*dev
)
632 static struct class wmi_class
= {
634 .dev_release
= wmi_dev_free
,
635 .dev_uevent
= wmi_dev_uevent
,
638 static int wmi_create_devs(void)
641 char guid_string
[37];
642 struct guid_block
*gblock
;
643 struct wmi_block
*wblock
;
645 struct device
*guid_dev
;
647 /* Create devices for all the GUIDs */
648 list_for_each(p
, &wmi_blocks
.list
) {
649 wblock
= list_entry(p
, struct wmi_block
, list
);
651 guid_dev
= kzalloc(sizeof(struct device
), GFP_KERNEL
);
655 wblock
->dev
= guid_dev
;
657 guid_dev
->class = &wmi_class
;
658 dev_set_drvdata(guid_dev
, wblock
);
660 gblock
= &wblock
->gblock
;
662 wmi_gtoa(gblock
->guid
, guid_string
);
663 dev_set_name(guid_dev
, guid_string
);
665 result
= device_register(guid_dev
);
669 result
= device_create_file(guid_dev
, &dev_attr_modalias
);
677 static void wmi_remove_devs(void)
679 struct guid_block
*gblock
;
680 struct wmi_block
*wblock
;
682 struct device
*guid_dev
;
684 /* Delete devices for all the GUIDs */
685 list_for_each(p
, &wmi_blocks
.list
) {
686 wblock
= list_entry(p
, struct wmi_block
, list
);
688 guid_dev
= wblock
->dev
;
689 gblock
= &wblock
->gblock
;
691 device_remove_file(guid_dev
, &dev_attr_modalias
);
693 device_unregister(guid_dev
);
697 static void wmi_class_exit(void)
700 class_unregister(&wmi_class
);
703 static int wmi_class_init(void)
707 ret
= class_register(&wmi_class
);
711 ret
= wmi_create_devs();
718 static bool guid_already_parsed(const char *guid_string
)
720 struct guid_block
*gblock
;
721 struct wmi_block
*wblock
;
724 list_for_each(p
, &wmi_blocks
.list
) {
725 wblock
= list_entry(p
, struct wmi_block
, list
);
726 gblock
= &wblock
->gblock
;
728 if (strncmp(gblock
->guid
, guid_string
, 16) == 0)
735 * Parse the _WDG method for the GUID data blocks
737 static __init acpi_status
parse_wdg(acpi_handle handle
)
739 struct acpi_buffer out
= {ACPI_ALLOCATE_BUFFER
, NULL
};
740 union acpi_object
*obj
;
741 struct guid_block
*gblock
;
742 struct wmi_block
*wblock
;
743 char guid_string
[37];
747 status
= acpi_evaluate_object(handle
, "_WDG", NULL
, &out
);
749 if (ACPI_FAILURE(status
))
752 obj
= (union acpi_object
*) out
.pointer
;
754 if (obj
->type
!= ACPI_TYPE_BUFFER
)
757 total
= obj
->buffer
.length
/ sizeof(struct guid_block
);
759 gblock
= kzalloc(obj
->buffer
.length
, GFP_KERNEL
);
763 memcpy(gblock
, obj
->buffer
.pointer
, obj
->buffer
.length
);
765 for (i
= 0; i
< total
; i
++) {
767 Some WMI devices, like those for nVidia hooks, have a
768 duplicate GUID. It's not clear what we should do in this
769 case yet, so for now, we'll just ignore the duplicate.
770 Anyone who wants to add support for that device can come
771 up with a better workaround for the mess then.
773 if (guid_already_parsed(gblock
[i
].guid
) == true) {
774 wmi_gtoa(gblock
[i
].guid
, guid_string
);
775 printk(KERN_INFO PREFIX
"Skipping duplicate GUID %s\n",
779 wblock
= kzalloc(sizeof(struct wmi_block
), GFP_KERNEL
);
783 wblock
->gblock
= gblock
[i
];
784 wblock
->handle
= handle
;
785 list_add_tail(&wblock
->list
, &wmi_blocks
.list
);
795 * WMI can have EmbeddedControl access regions. In which case, we just want to
796 * hand these off to the EC driver.
799 acpi_wmi_ec_space_handler(u32 function
, acpi_physical_address address
,
800 u32 bits
, u64
*value
,
801 void *handler_context
, void *region_context
)
803 int result
= 0, i
= 0;
806 if ((address
> 0xFF) || !value
)
807 return AE_BAD_PARAMETER
;
809 if (function
!= ACPI_READ
&& function
!= ACPI_WRITE
)
810 return AE_BAD_PARAMETER
;
813 return AE_BAD_PARAMETER
;
815 if (function
== ACPI_READ
) {
816 result
= ec_read(address
, &temp
);
817 (*value
) |= ((u64
)temp
) << i
;
819 temp
= 0xff & ((*value
) >> i
);
820 result
= ec_write(address
, temp
);
825 return AE_BAD_PARAMETER
;
838 static void acpi_wmi_notify(struct acpi_device
*device
, u32 event
)
840 struct guid_block
*block
;
841 struct wmi_block
*wblock
;
844 list_for_each(p
, &wmi_blocks
.list
) {
845 wblock
= list_entry(p
, struct wmi_block
, list
);
846 block
= &wblock
->gblock
;
848 if ((block
->flags
& ACPI_WMI_EVENT
) &&
849 (block
->notify_id
== event
)) {
851 wblock
->handler(event
, wblock
->handler_data
);
853 acpi_bus_generate_netlink_event(
854 device
->pnp
.device_class
, dev_name(&device
->dev
),
861 static int acpi_wmi_remove(struct acpi_device
*device
, int type
)
863 acpi_remove_address_space_handler(device
->handle
,
864 ACPI_ADR_SPACE_EC
, &acpi_wmi_ec_space_handler
);
869 static int __init
acpi_wmi_add(struct acpi_device
*device
)
874 status
= acpi_install_address_space_handler(device
->handle
,
876 &acpi_wmi_ec_space_handler
,
878 if (ACPI_FAILURE(status
))
881 status
= parse_wdg(device
->handle
);
882 if (ACPI_FAILURE(status
)) {
883 printk(KERN_ERR PREFIX
"Error installing EC region handler\n");
890 static int __init
acpi_wmi_init(void)
894 INIT_LIST_HEAD(&wmi_blocks
.list
);
899 result
= acpi_bus_register_driver(&acpi_wmi_driver
);
902 printk(KERN_INFO PREFIX
"Error loading mapper\n");
906 result
= wmi_class_init();
908 acpi_bus_unregister_driver(&acpi_wmi_driver
);
912 printk(KERN_INFO PREFIX
"Mapper loaded\n");
917 static void __exit
acpi_wmi_exit(void)
919 struct list_head
*p
, *tmp
;
920 struct wmi_block
*wblock
;
924 acpi_bus_unregister_driver(&acpi_wmi_driver
);
926 list_for_each_safe(p
, tmp
, &wmi_blocks
.list
) {
927 wblock
= list_entry(p
, struct wmi_block
, list
);
933 printk(KERN_INFO PREFIX
"Mapper unloaded\n");
936 subsys_initcall(acpi_wmi_init
);
937 module_exit(acpi_wmi_exit
);