Convert object_id to a union to also support notification value for events
[acer_acpi.git] / wmi.c
blobb1d2313bd2267f8f1fc8d80602f90b7d00be180b
1 /*
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>
32 #include "wmi.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...) { \
42 if (debug >= level) \
43 printk(KERN_DEBUG MY_LOGPREFIX message);\
46 MODULE_AUTHOR("Carlos Corbacho");
47 MODULE_DESCRIPTION("ACPI WMI Interface Driver");
48 MODULE_LICENSE("GPL");
50 struct guid_mapping
52 char guid[16];
53 union
55 char object_id[2];
56 struct
58 unsigned char notification_value;
59 unsigned char reserved;
62 u8 instance_count;
63 u8 flags;
66 struct guid_list
68 struct guid_mapping *pointer;
69 int total;
72 static struct guid_list guids;
73 static acpi_handle acpi_wmi_handle;
74 static int debug;
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
92 * when passing sets
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
99 /**
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 */
111 int h;
113 /* high part */
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;
117 else return -1;
118 h <<= 4;
120 /* low part */
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);
124 return -1;
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)
136 int i;
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 };
163 int i, j, v;
165 if (src[8] != '-' || src[13] != '-' ||
166 src[18] != '-' || src[23] != '-')
167 return false;
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)
172 return false;
174 return true;
177 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
178 const static struct acpi_device_id wmi_device_ids[] = {
179 {"PNP0C14", 0},
180 {"pnp0c14", 0},
181 {"", 0},
183 MODULE_DEVICE_TABLE(acpi, wmi_device_ids);
184 #endif
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];
190 char method[4] = "";
191 char method2[4] = "";
193 if (block->flags & WMIACPI_REGFLAG_METHOD) {
194 strcat(method, "WM");
195 } else {
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
201 if (in == NULL) {
202 strcat(method, "WQ");
203 } else if (out == NULL) {
204 strcat(method, "WS");
205 } else {
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;
215 input.count = 3;
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;
222 if (in != NULL) {
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;
252 int i;
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;
265 } else {
266 DEBUG(2, "GUID is not an event - evaluating\n");
267 return wmi_evaluate_object(block, methodId, in, out);
269 } else {
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;
287 acpi_status status;
289 status = acpi_evaluate_object(acpi_wmi_handle, "_WDG", NULL, &out);
291 if (ACPI_FAILURE(status)) {
292 DEBUG(2, "Problem with evaluating _WDG\n");
293 return status;
296 obj = (union acpi_object*) out.pointer;
298 if (obj->type != ACPI_TYPE_BUFFER) {
299 DEBUG(2, "Output is not a buffer\n");
300 return AE_ERROR;
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);
309 return AE_OK;
312 static int acpi_wmi_add(struct acpi_device *device)
314 DEBUG(2, "acpi_wmi_add called\n");
315 acpi_wmi_handle = device->handle;
316 parse_wdg();
318 return AE_OK;
321 static int acpi_wmi_remove(struct acpi_device *device, int type)
323 return AE_OK;
326 static struct acpi_driver acpi_wmi_driver = {
327 .name = "wmi",
328 .class = ACPI_WMI_CLASS,
329 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
330 .ids = wmi_device_ids,
331 #else
332 .ids = "pnp0c14",
333 #endif
334 .ops = {
335 .add = acpi_wmi_add,
336 .remove = acpi_wmi_remove,
340 static int __init acpi_wmi_init(void)
342 int result;
344 if (acpi_disabled)
345 return -ENODEV;
347 result = acpi_bus_register_driver(&acpi_wmi_driver);
349 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
350 if (result < 0) {
351 /* Try again with the alternate _HID */
352 acpi_wmi_driver.ids = "PNP0C14";
353 result = acpi_bus_register_driver(&acpi_wmi_driver);
355 #endif
357 if (result < 0) {
358 return -ENODEV;
361 DEBUG(0, "Succesfully loaded!\n");
363 return 0;
366 static void __exit acpi_wmi_exit(void)
368 acpi_bus_unregister_driver(&acpi_wmi_driver);
369 DEBUG(0, "Unloaded!\n");
371 return;
374 module_init(acpi_wmi_init);
375 module_exit(acpi_wmi_exit);