2 * wmi.c - WMI ACPI interface device
4 * Copyright (C) 2007 Carlos Corbacho <cathectic@gmail.com>
6 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or (at
11 * your option) any later version.
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
22 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
25 #include <linux/kernel.h>
26 #include <linux/module.h>
27 #include <linux/init.h>
28 #include <linux/types.h>
29 #include <acpi/acpi_drivers.h>
30 #include <linux/version.h>
34 #define ACPI_WMI_CLASS "wmi"
36 #define MY_LOGPREFIX "wmi: "
37 #define MY_ERR KERN_ERR MY_LOGPREFIX
38 #define MY_NOTICE KERN_NOTICE MY_LOGPREFIX
39 #define MY_INFO KERN_INFO MY_LOGPREFIX
41 #define DEBUG(level, message...) { \
43 printk(KERN_DEBUG MY_LOGPREFIX message);\
46 MODULE_AUTHOR("Carlos Corbacho");
47 MODULE_DESCRIPTION("ACPI WMI Interface Driver");
48 MODULE_LICENSE("GPL");
58 unsigned char notification_value
;
59 unsigned char reserved
;
68 struct guid_mapping
*pointer
;
72 static struct guid_list guids
;
73 static acpi_handle acpi_wmi_handle
;
76 module_param(debug
, int, 0664);
77 MODULE_PARM_DESC(debug
, "Debugging verbosity level (0=least 2=most)");
80 * Set this flag if the WCxx control method should be run to whenever the first
81 * data consumer is interested in collecting the data block and whenever the last data
82 * consumer is no longer interested.
84 #define WMIACPI_REGFLAG_EXPENSIVE 0x1
86 /* Set this flag if the GUID represents a set of WMI method calls and not a data block */
87 #define WMIACPI_REGFLAG_METHOD 0x2
90 * Set this flag if the data block is wholly composed of a string and should be
91 * translated from ASCIZ to UNICODE in returning queries and from UNICODE to ASCIZ
94 #define WMIACPI_REGFLAG_STRING 0x04
96 /* Set this flag if the guid maps to an event rather than a data block or method */
97 #define WMIACPI_REGFLAG_EVENT 0x08
100 * wmi_parse_hexbyte - Convert a ASCII hex number to a byte
101 * @src: Pointer to at least 2 characters to convert.
103 * Convert a two character ASCII hex string to a number.
105 * Return: 0-255 Success, the byte was parsed correctly
106 * -1 Error, an invalid character was supplied
108 static int wmi_parse_hexbyte (const u8
*src
)
110 unsigned int x
; /* For correct wrapping */
114 if ((x
= src
[0] - '0') <= '9'-'0') h
= x
;
115 else if ((x
= src
[0] - 'a') <= 'f'-'a') h
= x
+10;
116 else if ((x
= src
[0] - 'A') <= 'F'-'A') h
= x
+10;
121 if ((x
= src
[1] - '0') <= '9'-'0') return h
| x
;
122 if ((x
= src
[1] - 'a') <= 'f'-'a') return h
| (x
+10);
123 if ((x
= src
[1] - 'A') <= 'F'-'A') return h
| (x
+10);
128 * wmi_swap_bytes - Rearrange GUID bytes to match GUID binary
129 * @src: Memory block holding binary GUID (16 bytes)
130 * @dest: Memory block to hold byte swapped binary GUID (16 bytes)
132 * Byte swap a binary GUID to match it's real GUID value
134 static void wmi_swap_bytes(u8
*src
, u8
*dest
)
138 for (i
= 0; i
<= 3; i
++)
139 memcpy(dest
+ i
, src
+ (3 - i
), 1);
141 for (i
= 0; i
<= 1; i
++)
142 memcpy(dest
+ 4 + i
, src
+ (5 - i
), 1);
144 for (i
= 0; i
<= 1; i
++)
145 memcpy(dest
+ 6 + i
, src
+ (7 - i
), 1);
147 memcpy(dest
+ 8, src
+ 8, 8);
151 * wmi_parse_guid - Convert GUID from ASCII to binary
152 * @src: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
153 * @dest: Memory block to hold binary GUID (16 bytes)
155 * N.B. The GUID need not be NULL terminated.
157 * Return: 'true' @dest contains binary GUID
158 * 'false' @dest contents are undefined
160 static bool wmi_parse_guid (const u8
*src
, u8
*dest
)
162 static const int size
[] = { 4, 2, 2, 2, 6 };
165 if (src
[8] != '-' || src
[13] != '-' ||
166 src
[18] != '-' || src
[23] != '-')
169 for (j
= 0; j
< 5; j
++, src
++)
170 for (i
= 0; i
< size
[j
]; i
++, src
+=2, *dest
++ = v
)
171 if ((v
= wmi_parse_hexbyte (src
)) < 0)
177 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
178 const static struct acpi_device_id wmi_device_ids
[] = {
183 MODULE_DEVICE_TABLE(acpi
, wmi_device_ids
);
186 static acpi_status
wmi_evaluate_object(struct guid_mapping
*block
, u32 methodId
, const struct acpi_buffer
*in
, struct acpi_buffer
*out
)
188 struct acpi_object_list input
;
189 union acpi_object params
[3];
191 char method2
[4] = "";
193 if (block
->flags
& WMIACPI_REGFLAG_METHOD
) {
194 strcat(method
, "WM");
197 * We are now dealing with a data block - there are different
198 * methods for querying and setting these, so we must decide
199 * which one is to be called
202 strcat(method
, "WQ");
203 } else if (out
== NULL
) {
204 strcat(method
, "WS");
206 /* FIXME - problem, both in and out wanted for data block! */
210 if (block
->flags
& WMIACPI_REGFLAG_STRING
) {
211 /* Convert input from Unicode to ASCIIZ */
212 return AE_NOT_IMPLEMENTED
;
216 input
.pointer
= params
;
217 params
[0].type
= ACPI_TYPE_INTEGER
;
218 params
[0].integer
.value
= 0x01;
219 params
[1].type
= ACPI_TYPE_INTEGER
;
220 params
[1].integer
.value
= methodId
;
223 params
[2].type
= ACPI_TYPE_BUFFER
;
224 params
[2].buffer
.length
= in
->length
;
225 params
[2].buffer
.pointer
= in
->pointer
;
228 /* FIXME - arguments to pass? */
229 if (block
->flags
& WMIACPI_REGFLAG_EXPENSIVE
) {
231 strcat(method2, "WC");
232 strncat(method2, block->object_id, 2);
233 acpi_evaluate_object(acpi_wmi_handle, method2, &input, out);
235 return AE_NOT_IMPLEMENTED
;
238 strncat(method
, block
->object_id
, 2);
239 DEBUG(2, "Method to call is %s \n", method
);
241 return acpi_evaluate_object(acpi_wmi_handle
, method
, &input
, out
);
245 * Externally callable WMI functions
248 int wmi_evaluate_block(char* guid_string
, u32 methodId
, const struct acpi_buffer
*in
, struct acpi_buffer
*out
)
250 char tmp
[16], guid_input
[16];
251 struct guid_mapping
*block
;
254 DEBUG(2, "Passing GUID to parser\n");
255 wmi_parse_guid(guid_string
, tmp
);
256 wmi_swap_bytes(tmp
, guid_input
);
258 for (i
= 0; i
< guids
.total
; i
++) {
259 block
= guids
.pointer
+ i
;
261 if (memcmp(block
->guid
, guid_input
, 16) == 0) {
262 if (block
->flags
& WMIACPI_REGFLAG_EVENT
) {
263 DEBUG(2, "GUID is an event\n");
264 return AE_NOT_IMPLEMENTED
;
266 DEBUG(2, "GUID is not an event - evaluating\n");
267 return wmi_evaluate_object(block
, methodId
, in
, out
);
270 DEBUG(0, "Failed to match GUID\n");
274 DEBUG(2, "Unable to match GUID\n");
275 return AE_BAD_ADDRESS
;
278 EXPORT_SYMBOL(wmi_evaluate_block
);
281 * Parse the _WDG method for the data blocks
283 static int parse_wdg(void)
285 struct acpi_buffer out
= {ACPI_ALLOCATE_BUFFER
, NULL
};
286 union acpi_object
*obj
;
289 status
= acpi_evaluate_object(acpi_wmi_handle
, "_WDG", NULL
, &out
);
291 if (ACPI_FAILURE(status
)) {
292 DEBUG(2, "Problem with evaluating _WDG\n");
296 obj
= (union acpi_object
*) out
.pointer
;
298 if (obj
->type
!= ACPI_TYPE_BUFFER
) {
299 DEBUG(2, "Output is not a buffer\n");
303 DEBUG(2, "Size of _WDG output is %d bytes\n", obj
->buffer
.length
);
305 guids
.pointer
= (struct guid_mapping
*) obj
->buffer
.pointer
;
307 DEBUG(2, "GUID blocks = %lu \n", obj
->buffer
.length
/ sizeof(struct guid_mapping
));
308 guids
.total
= obj
->buffer
.length
/ sizeof(struct guid_mapping
);
312 static int acpi_wmi_add(struct acpi_device
*device
)
314 DEBUG(2, "acpi_wmi_add called\n");
315 acpi_wmi_handle
= device
->handle
;
321 static int acpi_wmi_remove(struct acpi_device
*device
, int type
)
326 static struct acpi_driver acpi_wmi_driver
= {
328 .class = ACPI_WMI_CLASS
,
329 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
330 .ids
= wmi_device_ids
,
336 .remove
= acpi_wmi_remove
,
340 static int __init
acpi_wmi_init(void)
347 result
= acpi_bus_register_driver(&acpi_wmi_driver
);
349 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
351 /* Try again with the alternate _HID */
352 acpi_wmi_driver
.ids
= "PNP0C14";
353 result
= acpi_bus_register_driver(&acpi_wmi_driver
);
361 DEBUG(0, "Succesfully loaded!\n");
366 static void __exit
acpi_wmi_exit(void)
368 acpi_bus_unregister_driver(&acpi_wmi_driver
);
369 DEBUG(0, "Unloaded!\n");
374 module_init(acpi_wmi_init
);
375 module_exit(acpi_wmi_exit
);