1b4f9fe12SLen Brown /* 2b4f9fe12SLen Brown * ACPI-WMI mapping driver 3b4f9fe12SLen Brown * 4b4f9fe12SLen Brown * Copyright (C) 2007-2008 Carlos Corbacho <carlos@strangeworlds.co.uk> 5b4f9fe12SLen Brown * 6b4f9fe12SLen Brown * GUID parsing code from ldm.c is: 7b4f9fe12SLen Brown * Copyright (C) 2001,2002 Richard Russon <ldm@flatcap.org> 8b4f9fe12SLen Brown * Copyright (c) 2001-2007 Anton Altaparmakov 9b4f9fe12SLen Brown * Copyright (C) 2001,2002 Jakob Kemi <jakob.kemi@telia.com> 10b4f9fe12SLen Brown * 11b4f9fe12SLen Brown * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 12b4f9fe12SLen Brown * 13b4f9fe12SLen Brown * This program is free software; you can redistribute it and/or modify 14b4f9fe12SLen Brown * it under the terms of the GNU General Public License as published by 15b4f9fe12SLen Brown * the Free Software Foundation; either version 2 of the License, or (at 16b4f9fe12SLen Brown * your option) any later version. 17b4f9fe12SLen Brown * 18b4f9fe12SLen Brown * This program is distributed in the hope that it will be useful, but 19b4f9fe12SLen Brown * WITHOUT ANY WARRANTY; without even the implied warranty of 20b4f9fe12SLen Brown * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 21b4f9fe12SLen Brown * General Public License for more details. 22b4f9fe12SLen Brown * 23b4f9fe12SLen Brown * You should have received a copy of the GNU General Public License along 24b4f9fe12SLen Brown * with this program; if not, write to the Free Software Foundation, Inc., 25b4f9fe12SLen Brown * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 26b4f9fe12SLen Brown * 27b4f9fe12SLen Brown * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 28b4f9fe12SLen Brown */ 29b4f9fe12SLen Brown 30b4f9fe12SLen Brown #include <linux/kernel.h> 31b4f9fe12SLen Brown #include <linux/init.h> 32b4f9fe12SLen Brown #include <linux/types.h> 33b4f9fe12SLen Brown #include <linux/list.h> 34b4f9fe12SLen Brown #include <linux/acpi.h> 35b4f9fe12SLen Brown #include <acpi/acpi_bus.h> 36b4f9fe12SLen Brown #include <acpi/acpi_drivers.h> 37b4f9fe12SLen Brown 38b4f9fe12SLen Brown ACPI_MODULE_NAME("wmi"); 39b4f9fe12SLen Brown MODULE_AUTHOR("Carlos Corbacho"); 40b4f9fe12SLen Brown MODULE_DESCRIPTION("ACPI-WMI Mapping Driver"); 41b4f9fe12SLen Brown MODULE_LICENSE("GPL"); 42b4f9fe12SLen Brown 43b4f9fe12SLen Brown #define ACPI_WMI_CLASS "wmi" 44b4f9fe12SLen Brown 45b4f9fe12SLen Brown #undef PREFIX 46b4f9fe12SLen Brown #define PREFIX "ACPI: WMI: " 47b4f9fe12SLen Brown 48b4f9fe12SLen Brown static DEFINE_MUTEX(wmi_data_lock); 49b4f9fe12SLen Brown 50b4f9fe12SLen Brown struct guid_block { 51b4f9fe12SLen Brown char guid[16]; 52b4f9fe12SLen Brown union { 53b4f9fe12SLen Brown char object_id[2]; 54b4f9fe12SLen Brown struct { 55b4f9fe12SLen Brown unsigned char notify_id; 56b4f9fe12SLen Brown unsigned char reserved; 57b4f9fe12SLen Brown }; 58b4f9fe12SLen Brown }; 59b4f9fe12SLen Brown u8 instance_count; 60b4f9fe12SLen Brown u8 flags; 61b4f9fe12SLen Brown }; 62b4f9fe12SLen Brown 63b4f9fe12SLen Brown struct wmi_block { 64b4f9fe12SLen Brown struct list_head list; 65b4f9fe12SLen Brown struct guid_block gblock; 66b4f9fe12SLen Brown acpi_handle handle; 67b4f9fe12SLen Brown wmi_notify_handler handler; 68b4f9fe12SLen Brown void *handler_data; 69b4f9fe12SLen Brown }; 70b4f9fe12SLen Brown 71b4f9fe12SLen Brown static struct wmi_block wmi_blocks; 72b4f9fe12SLen Brown 73b4f9fe12SLen Brown /* 74b4f9fe12SLen Brown * If the GUID data block is marked as expensive, we must enable and 75b4f9fe12SLen Brown * explicitily disable data collection. 76b4f9fe12SLen Brown */ 77b4f9fe12SLen Brown #define ACPI_WMI_EXPENSIVE 0x1 78b4f9fe12SLen Brown #define ACPI_WMI_METHOD 0x2 /* GUID is a method */ 79b4f9fe12SLen Brown #define ACPI_WMI_STRING 0x4 /* GUID takes & returns a string */ 80b4f9fe12SLen Brown #define ACPI_WMI_EVENT 0x8 /* GUID is an event */ 81b4f9fe12SLen Brown 82b4f9fe12SLen Brown static int acpi_wmi_remove(struct acpi_device *device, int type); 83b4f9fe12SLen Brown static int acpi_wmi_add(struct acpi_device *device); 84b4f9fe12SLen Brown 85b4f9fe12SLen Brown static const struct acpi_device_id wmi_device_ids[] = { 86b4f9fe12SLen Brown {"PNP0C14", 0}, 87b4f9fe12SLen Brown {"pnp0c14", 0}, 88b4f9fe12SLen Brown {"", 0}, 89b4f9fe12SLen Brown }; 90b4f9fe12SLen Brown MODULE_DEVICE_TABLE(acpi, wmi_device_ids); 91b4f9fe12SLen Brown 92b4f9fe12SLen Brown static struct acpi_driver acpi_wmi_driver = { 93b4f9fe12SLen Brown .name = "wmi", 94b4f9fe12SLen Brown .class = ACPI_WMI_CLASS, 95b4f9fe12SLen Brown .ids = wmi_device_ids, 96b4f9fe12SLen Brown .ops = { 97b4f9fe12SLen Brown .add = acpi_wmi_add, 98b4f9fe12SLen Brown .remove = acpi_wmi_remove, 99b4f9fe12SLen Brown }, 100b4f9fe12SLen Brown }; 101b4f9fe12SLen Brown 102b4f9fe12SLen Brown /* 103b4f9fe12SLen Brown * GUID parsing functions 104b4f9fe12SLen Brown */ 105b4f9fe12SLen Brown 106b4f9fe12SLen Brown /** 107b4f9fe12SLen Brown * wmi_parse_hexbyte - Convert a ASCII hex number to a byte 108b4f9fe12SLen Brown * @src: Pointer to at least 2 characters to convert. 109b4f9fe12SLen Brown * 110b4f9fe12SLen Brown * Convert a two character ASCII hex string to a number. 111b4f9fe12SLen Brown * 112b4f9fe12SLen Brown * Return: 0-255 Success, the byte was parsed correctly 113b4f9fe12SLen Brown * -1 Error, an invalid character was supplied 114b4f9fe12SLen Brown */ 115b4f9fe12SLen Brown static int wmi_parse_hexbyte(const u8 *src) 116b4f9fe12SLen Brown { 117b4f9fe12SLen Brown unsigned int x; /* For correct wrapping */ 118b4f9fe12SLen Brown int h; 119b4f9fe12SLen Brown 120b4f9fe12SLen Brown /* high part */ 121b4f9fe12SLen Brown x = src[0]; 122b4f9fe12SLen Brown if (x - '0' <= '9' - '0') { 123b4f9fe12SLen Brown h = x - '0'; 124b4f9fe12SLen Brown } else if (x - 'a' <= 'f' - 'a') { 125b4f9fe12SLen Brown h = x - 'a' + 10; 126b4f9fe12SLen Brown } else if (x - 'A' <= 'F' - 'A') { 127b4f9fe12SLen Brown h = x - 'A' + 10; 128b4f9fe12SLen Brown } else { 129b4f9fe12SLen Brown return -1; 130b4f9fe12SLen Brown } 131b4f9fe12SLen Brown h <<= 4; 132b4f9fe12SLen Brown 133b4f9fe12SLen Brown /* low part */ 134b4f9fe12SLen Brown x = src[1]; 135b4f9fe12SLen Brown if (x - '0' <= '9' - '0') 136b4f9fe12SLen Brown return h | (x - '0'); 137b4f9fe12SLen Brown if (x - 'a' <= 'f' - 'a') 138b4f9fe12SLen Brown return h | (x - 'a' + 10); 139b4f9fe12SLen Brown if (x - 'A' <= 'F' - 'A') 140b4f9fe12SLen Brown return h | (x - 'A' + 10); 141b4f9fe12SLen Brown return -1; 142b4f9fe12SLen Brown } 143b4f9fe12SLen Brown 144b4f9fe12SLen Brown /** 145b4f9fe12SLen Brown * wmi_swap_bytes - Rearrange GUID bytes to match GUID binary 146b4f9fe12SLen Brown * @src: Memory block holding binary GUID (16 bytes) 147b4f9fe12SLen Brown * @dest: Memory block to hold byte swapped binary GUID (16 bytes) 148b4f9fe12SLen Brown * 149b4f9fe12SLen Brown * Byte swap a binary GUID to match it's real GUID value 150b4f9fe12SLen Brown */ 151b4f9fe12SLen Brown static void wmi_swap_bytes(u8 *src, u8 *dest) 152b4f9fe12SLen Brown { 153b4f9fe12SLen Brown int i; 154b4f9fe12SLen Brown 155b4f9fe12SLen Brown for (i = 0; i <= 3; i++) 156b4f9fe12SLen Brown memcpy(dest + i, src + (3 - i), 1); 157b4f9fe12SLen Brown 158b4f9fe12SLen Brown for (i = 0; i <= 1; i++) 159b4f9fe12SLen Brown memcpy(dest + 4 + i, src + (5 - i), 1); 160b4f9fe12SLen Brown 161b4f9fe12SLen Brown for (i = 0; i <= 1; i++) 162b4f9fe12SLen Brown memcpy(dest + 6 + i, src + (7 - i), 1); 163b4f9fe12SLen Brown 164b4f9fe12SLen Brown memcpy(dest + 8, src + 8, 8); 165b4f9fe12SLen Brown } 166b4f9fe12SLen Brown 167b4f9fe12SLen Brown /** 168b4f9fe12SLen Brown * wmi_parse_guid - Convert GUID from ASCII to binary 169b4f9fe12SLen Brown * @src: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba 170b4f9fe12SLen Brown * @dest: Memory block to hold binary GUID (16 bytes) 171b4f9fe12SLen Brown * 172b4f9fe12SLen Brown * N.B. The GUID need not be NULL terminated. 173b4f9fe12SLen Brown * 174b4f9fe12SLen Brown * Return: 'true' @dest contains binary GUID 175b4f9fe12SLen Brown * 'false' @dest contents are undefined 176b4f9fe12SLen Brown */ 177b4f9fe12SLen Brown static bool wmi_parse_guid(const u8 *src, u8 *dest) 178b4f9fe12SLen Brown { 179b4f9fe12SLen Brown static const int size[] = { 4, 2, 2, 2, 6 }; 180b4f9fe12SLen Brown int i, j, v; 181b4f9fe12SLen Brown 182b4f9fe12SLen Brown if (src[8] != '-' || src[13] != '-' || 183b4f9fe12SLen Brown src[18] != '-' || src[23] != '-') 184b4f9fe12SLen Brown return false; 185b4f9fe12SLen Brown 186b4f9fe12SLen Brown for (j = 0; j < 5; j++, src++) { 187b4f9fe12SLen Brown for (i = 0; i < size[j]; i++, src += 2, *dest++ = v) { 188b4f9fe12SLen Brown v = wmi_parse_hexbyte(src); 189b4f9fe12SLen Brown if (v < 0) 190b4f9fe12SLen Brown return false; 191b4f9fe12SLen Brown } 192b4f9fe12SLen Brown } 193b4f9fe12SLen Brown 194b4f9fe12SLen Brown return true; 195b4f9fe12SLen Brown } 196b4f9fe12SLen Brown 197b4f9fe12SLen Brown static bool find_guid(const char *guid_string, struct wmi_block **out) 198b4f9fe12SLen Brown { 199b4f9fe12SLen Brown char tmp[16], guid_input[16]; 200b4f9fe12SLen Brown struct wmi_block *wblock; 201b4f9fe12SLen Brown struct guid_block *block; 202b4f9fe12SLen Brown struct list_head *p; 203b4f9fe12SLen Brown 204b4f9fe12SLen Brown wmi_parse_guid(guid_string, tmp); 205b4f9fe12SLen Brown wmi_swap_bytes(tmp, guid_input); 206b4f9fe12SLen Brown 207b4f9fe12SLen Brown list_for_each(p, &wmi_blocks.list) { 208b4f9fe12SLen Brown wblock = list_entry(p, struct wmi_block, list); 209b4f9fe12SLen Brown block = &wblock->gblock; 210b4f9fe12SLen Brown 211b4f9fe12SLen Brown if (memcmp(block->guid, guid_input, 16) == 0) { 212b4f9fe12SLen Brown if (out) 213b4f9fe12SLen Brown *out = wblock; 214b4f9fe12SLen Brown return 1; 215b4f9fe12SLen Brown } 216b4f9fe12SLen Brown } 217b4f9fe12SLen Brown return 0; 218b4f9fe12SLen Brown } 219b4f9fe12SLen Brown 220b4f9fe12SLen Brown static acpi_status wmi_method_enable(struct wmi_block *wblock, int enable) 221b4f9fe12SLen Brown { 222b4f9fe12SLen Brown struct guid_block *block = NULL; 223b4f9fe12SLen Brown char method[5]; 224b4f9fe12SLen Brown struct acpi_object_list input; 225b4f9fe12SLen Brown union acpi_object params[1]; 226b4f9fe12SLen Brown acpi_status status; 227b4f9fe12SLen Brown acpi_handle handle; 228b4f9fe12SLen Brown 229b4f9fe12SLen Brown block = &wblock->gblock; 230b4f9fe12SLen Brown handle = wblock->handle; 231b4f9fe12SLen Brown 232b4f9fe12SLen Brown if (!block) 233b4f9fe12SLen Brown return AE_NOT_EXIST; 234b4f9fe12SLen Brown 235b4f9fe12SLen Brown input.count = 1; 236b4f9fe12SLen Brown input.pointer = params; 237b4f9fe12SLen Brown params[0].type = ACPI_TYPE_INTEGER; 238b4f9fe12SLen Brown params[0].integer.value = enable; 239b4f9fe12SLen Brown 240b4f9fe12SLen Brown snprintf(method, 5, "WE%02X", block->notify_id); 241b4f9fe12SLen Brown status = acpi_evaluate_object(handle, method, &input, NULL); 242b4f9fe12SLen Brown 243b4f9fe12SLen Brown if (status != AE_OK && status != AE_NOT_FOUND) 244b4f9fe12SLen Brown return status; 245b4f9fe12SLen Brown else 246b4f9fe12SLen Brown return AE_OK; 247b4f9fe12SLen Brown } 248b4f9fe12SLen Brown 249b4f9fe12SLen Brown /* 250b4f9fe12SLen Brown * Exported WMI functions 251b4f9fe12SLen Brown */ 252b4f9fe12SLen Brown /** 253b4f9fe12SLen Brown * wmi_evaluate_method - Evaluate a WMI method 254b4f9fe12SLen Brown * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba 255b4f9fe12SLen Brown * @instance: Instance index 256b4f9fe12SLen Brown * @method_id: Method ID to call 257b4f9fe12SLen Brown * &in: Buffer containing input for the method call 258b4f9fe12SLen Brown * &out: Empty buffer to return the method results 259b4f9fe12SLen Brown * 260b4f9fe12SLen Brown * Call an ACPI-WMI method 261b4f9fe12SLen Brown */ 262b4f9fe12SLen Brown acpi_status wmi_evaluate_method(const char *guid_string, u8 instance, 263b4f9fe12SLen Brown u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out) 264b4f9fe12SLen Brown { 265b4f9fe12SLen Brown struct guid_block *block = NULL; 266b4f9fe12SLen Brown struct wmi_block *wblock = NULL; 267b4f9fe12SLen Brown acpi_handle handle; 268b4f9fe12SLen Brown acpi_status status; 269b4f9fe12SLen Brown struct acpi_object_list input; 270b4f9fe12SLen Brown union acpi_object params[3]; 271b4f9fe12SLen Brown char method[4] = "WM"; 272b4f9fe12SLen Brown 273b4f9fe12SLen Brown if (!find_guid(guid_string, &wblock)) 274b4f9fe12SLen Brown return AE_ERROR; 275b4f9fe12SLen Brown 276b4f9fe12SLen Brown block = &wblock->gblock; 277b4f9fe12SLen Brown handle = wblock->handle; 278b4f9fe12SLen Brown 279b4f9fe12SLen Brown if (!(block->flags & ACPI_WMI_METHOD)) 280b4f9fe12SLen Brown return AE_BAD_DATA; 281b4f9fe12SLen Brown 282b4f9fe12SLen Brown if (block->instance_count < instance) 283b4f9fe12SLen Brown return AE_BAD_PARAMETER; 284b4f9fe12SLen Brown 285b4f9fe12SLen Brown input.count = 2; 286b4f9fe12SLen Brown input.pointer = params; 287b4f9fe12SLen Brown params[0].type = ACPI_TYPE_INTEGER; 288b4f9fe12SLen Brown params[0].integer.value = instance; 289b4f9fe12SLen Brown params[1].type = ACPI_TYPE_INTEGER; 290b4f9fe12SLen Brown params[1].integer.value = method_id; 291b4f9fe12SLen Brown 292b4f9fe12SLen Brown if (in) { 293b4f9fe12SLen Brown input.count = 3; 294b4f9fe12SLen Brown 295b4f9fe12SLen Brown if (block->flags & ACPI_WMI_STRING) { 296b4f9fe12SLen Brown params[2].type = ACPI_TYPE_STRING; 297b4f9fe12SLen Brown } else { 298b4f9fe12SLen Brown params[2].type = ACPI_TYPE_BUFFER; 299b4f9fe12SLen Brown } 300b4f9fe12SLen Brown params[2].buffer.length = in->length; 301b4f9fe12SLen Brown params[2].buffer.pointer = in->pointer; 302b4f9fe12SLen Brown } 303b4f9fe12SLen Brown 304b4f9fe12SLen Brown strncat(method, block->object_id, 2); 305b4f9fe12SLen Brown 306b4f9fe12SLen Brown status = acpi_evaluate_object(handle, method, &input, out); 307b4f9fe12SLen Brown 308b4f9fe12SLen Brown return status; 309b4f9fe12SLen Brown } 310b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_evaluate_method); 311b4f9fe12SLen Brown 312b4f9fe12SLen Brown /** 313b4f9fe12SLen Brown * wmi_query_block - Return contents of a WMI block 314b4f9fe12SLen Brown * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba 315b4f9fe12SLen Brown * @instance: Instance index 316b4f9fe12SLen Brown * &out: Empty buffer to return the contents of the data block to 317b4f9fe12SLen Brown * 318b4f9fe12SLen Brown * Return the contents of an ACPI-WMI data block to a buffer 319b4f9fe12SLen Brown */ 320b4f9fe12SLen Brown acpi_status wmi_query_block(const char *guid_string, u8 instance, 321b4f9fe12SLen Brown struct acpi_buffer *out) 322b4f9fe12SLen Brown { 323b4f9fe12SLen Brown struct guid_block *block = NULL; 324b4f9fe12SLen Brown struct wmi_block *wblock = NULL; 325b4f9fe12SLen Brown acpi_handle handle, wc_handle; 326b4f9fe12SLen Brown acpi_status status, wc_status = AE_ERROR; 327b4f9fe12SLen Brown struct acpi_object_list input, wc_input; 328b4f9fe12SLen Brown union acpi_object wc_params[1], wq_params[1]; 329b4f9fe12SLen Brown char method[4]; 330b4f9fe12SLen Brown char wc_method[4] = "WC"; 331b4f9fe12SLen Brown 332b4f9fe12SLen Brown if (!guid_string || !out) 333b4f9fe12SLen Brown return AE_BAD_PARAMETER; 334b4f9fe12SLen Brown 335b4f9fe12SLen Brown if (!find_guid(guid_string, &wblock)) 336b4f9fe12SLen Brown return AE_ERROR; 337b4f9fe12SLen Brown 338b4f9fe12SLen Brown block = &wblock->gblock; 339b4f9fe12SLen Brown handle = wblock->handle; 340b4f9fe12SLen Brown 341b4f9fe12SLen Brown if (block->instance_count < instance) 342b4f9fe12SLen Brown return AE_BAD_PARAMETER; 343b4f9fe12SLen Brown 344b4f9fe12SLen Brown /* Check GUID is a data block */ 345b4f9fe12SLen Brown if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD)) 346b4f9fe12SLen Brown return AE_ERROR; 347b4f9fe12SLen Brown 348b4f9fe12SLen Brown input.count = 1; 349b4f9fe12SLen Brown input.pointer = wq_params; 350b4f9fe12SLen Brown wq_params[0].type = ACPI_TYPE_INTEGER; 351b4f9fe12SLen Brown wq_params[0].integer.value = instance; 352b4f9fe12SLen Brown 353b4f9fe12SLen Brown /* 354b4f9fe12SLen Brown * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method first to 355b4f9fe12SLen Brown * enable collection. 356b4f9fe12SLen Brown */ 357b4f9fe12SLen Brown if (block->flags & ACPI_WMI_EXPENSIVE) { 358b4f9fe12SLen Brown wc_input.count = 1; 359b4f9fe12SLen Brown wc_input.pointer = wc_params; 360b4f9fe12SLen Brown wc_params[0].type = ACPI_TYPE_INTEGER; 361b4f9fe12SLen Brown wc_params[0].integer.value = 1; 362b4f9fe12SLen Brown 363b4f9fe12SLen Brown strncat(wc_method, block->object_id, 2); 364b4f9fe12SLen Brown 365b4f9fe12SLen Brown /* 366b4f9fe12SLen Brown * Some GUIDs break the specification by declaring themselves 367b4f9fe12SLen Brown * expensive, but have no corresponding WCxx method. So we 368b4f9fe12SLen Brown * should not fail if this happens. 369b4f9fe12SLen Brown */ 370b4f9fe12SLen Brown wc_status = acpi_get_handle(handle, wc_method, &wc_handle); 371b4f9fe12SLen Brown if (ACPI_SUCCESS(wc_status)) 372b4f9fe12SLen Brown wc_status = acpi_evaluate_object(handle, wc_method, 373b4f9fe12SLen Brown &wc_input, NULL); 374b4f9fe12SLen Brown } 375b4f9fe12SLen Brown 376b4f9fe12SLen Brown strcpy(method, "WQ"); 377b4f9fe12SLen Brown strncat(method, block->object_id, 2); 378b4f9fe12SLen Brown 379b4f9fe12SLen Brown status = acpi_evaluate_object(handle, method, &input, out); 380b4f9fe12SLen Brown 381b4f9fe12SLen Brown /* 382b4f9fe12SLen Brown * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method, even if 383b4f9fe12SLen Brown * the WQxx method failed - we should disable collection anyway. 384b4f9fe12SLen Brown */ 385b4f9fe12SLen Brown if ((block->flags & ACPI_WMI_EXPENSIVE) && ACPI_SUCCESS(wc_status)) { 386b4f9fe12SLen Brown wc_params[0].integer.value = 0; 387b4f9fe12SLen Brown status = acpi_evaluate_object(handle, 388b4f9fe12SLen Brown wc_method, &wc_input, NULL); 389b4f9fe12SLen Brown } 390b4f9fe12SLen Brown 391b4f9fe12SLen Brown return status; 392b4f9fe12SLen Brown } 393b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_query_block); 394b4f9fe12SLen Brown 395b4f9fe12SLen Brown /** 396b4f9fe12SLen Brown * wmi_set_block - Write to a WMI block 397b4f9fe12SLen Brown * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba 398b4f9fe12SLen Brown * @instance: Instance index 399b4f9fe12SLen Brown * &in: Buffer containing new values for the data block 400b4f9fe12SLen Brown * 401b4f9fe12SLen Brown * Write the contents of the input buffer to an ACPI-WMI data block 402b4f9fe12SLen Brown */ 403b4f9fe12SLen Brown acpi_status wmi_set_block(const char *guid_string, u8 instance, 404b4f9fe12SLen Brown const struct acpi_buffer *in) 405b4f9fe12SLen Brown { 406b4f9fe12SLen Brown struct guid_block *block = NULL; 407b4f9fe12SLen Brown struct wmi_block *wblock = NULL; 408b4f9fe12SLen Brown acpi_handle handle; 409b4f9fe12SLen Brown struct acpi_object_list input; 410b4f9fe12SLen Brown union acpi_object params[2]; 411b4f9fe12SLen Brown char method[4] = "WS"; 412b4f9fe12SLen Brown 413b4f9fe12SLen Brown if (!guid_string || !in) 414b4f9fe12SLen Brown return AE_BAD_DATA; 415b4f9fe12SLen Brown 416b4f9fe12SLen Brown if (!find_guid(guid_string, &wblock)) 417b4f9fe12SLen Brown return AE_ERROR; 418b4f9fe12SLen Brown 419b4f9fe12SLen Brown block = &wblock->gblock; 420b4f9fe12SLen Brown handle = wblock->handle; 421b4f9fe12SLen Brown 422b4f9fe12SLen Brown if (block->instance_count < instance) 423b4f9fe12SLen Brown return AE_BAD_PARAMETER; 424b4f9fe12SLen Brown 425b4f9fe12SLen Brown /* Check GUID is a data block */ 426b4f9fe12SLen Brown if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD)) 427b4f9fe12SLen Brown return AE_ERROR; 428b4f9fe12SLen Brown 429b4f9fe12SLen Brown input.count = 2; 430b4f9fe12SLen Brown input.pointer = params; 431b4f9fe12SLen Brown params[0].type = ACPI_TYPE_INTEGER; 432b4f9fe12SLen Brown params[0].integer.value = instance; 433b4f9fe12SLen Brown 434b4f9fe12SLen Brown if (block->flags & ACPI_WMI_STRING) { 435b4f9fe12SLen Brown params[1].type = ACPI_TYPE_STRING; 436b4f9fe12SLen Brown } else { 437b4f9fe12SLen Brown params[1].type = ACPI_TYPE_BUFFER; 438b4f9fe12SLen Brown } 439b4f9fe12SLen Brown params[1].buffer.length = in->length; 440b4f9fe12SLen Brown params[1].buffer.pointer = in->pointer; 441b4f9fe12SLen Brown 442b4f9fe12SLen Brown strncat(method, block->object_id, 2); 443b4f9fe12SLen Brown 444b4f9fe12SLen Brown return acpi_evaluate_object(handle, method, &input, NULL); 445b4f9fe12SLen Brown } 446b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_set_block); 447b4f9fe12SLen Brown 448b4f9fe12SLen Brown /** 449b4f9fe12SLen Brown * wmi_install_notify_handler - Register handler for WMI events 450b4f9fe12SLen Brown * @handler: Function to handle notifications 451b4f9fe12SLen Brown * @data: Data to be returned to handler when event is fired 452b4f9fe12SLen Brown * 453b4f9fe12SLen Brown * Register a handler for events sent to the ACPI-WMI mapper device. 454b4f9fe12SLen Brown */ 455b4f9fe12SLen Brown acpi_status wmi_install_notify_handler(const char *guid, 456b4f9fe12SLen Brown wmi_notify_handler handler, void *data) 457b4f9fe12SLen Brown { 458b4f9fe12SLen Brown struct wmi_block *block; 459b4f9fe12SLen Brown acpi_status status; 460b4f9fe12SLen Brown 461b4f9fe12SLen Brown if (!guid || !handler) 462b4f9fe12SLen Brown return AE_BAD_PARAMETER; 463b4f9fe12SLen Brown 464b4f9fe12SLen Brown find_guid(guid, &block); 465b4f9fe12SLen Brown if (!block) 466b4f9fe12SLen Brown return AE_NOT_EXIST; 467b4f9fe12SLen Brown 468b4f9fe12SLen Brown if (block->handler) 469b4f9fe12SLen Brown return AE_ALREADY_ACQUIRED; 470b4f9fe12SLen Brown 471b4f9fe12SLen Brown block->handler = handler; 472b4f9fe12SLen Brown block->handler_data = data; 473b4f9fe12SLen Brown 474b4f9fe12SLen Brown status = wmi_method_enable(block, 1); 475b4f9fe12SLen Brown 476b4f9fe12SLen Brown return status; 477b4f9fe12SLen Brown } 478b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_install_notify_handler); 479b4f9fe12SLen Brown 480b4f9fe12SLen Brown /** 481b4f9fe12SLen Brown * wmi_uninstall_notify_handler - Unregister handler for WMI events 482b4f9fe12SLen Brown * 483b4f9fe12SLen Brown * Unregister handler for events sent to the ACPI-WMI mapper device. 484b4f9fe12SLen Brown */ 485b4f9fe12SLen Brown acpi_status wmi_remove_notify_handler(const char *guid) 486b4f9fe12SLen Brown { 487b4f9fe12SLen Brown struct wmi_block *block; 488b4f9fe12SLen Brown acpi_status status; 489b4f9fe12SLen Brown 490b4f9fe12SLen Brown if (!guid) 491b4f9fe12SLen Brown return AE_BAD_PARAMETER; 492b4f9fe12SLen Brown 493b4f9fe12SLen Brown find_guid(guid, &block); 494b4f9fe12SLen Brown if (!block) 495b4f9fe12SLen Brown return AE_NOT_EXIST; 496b4f9fe12SLen Brown 497b4f9fe12SLen Brown if (!block->handler) 498b4f9fe12SLen Brown return AE_NULL_ENTRY; 499b4f9fe12SLen Brown 500b4f9fe12SLen Brown status = wmi_method_enable(block, 0); 501b4f9fe12SLen Brown 502b4f9fe12SLen Brown block->handler = NULL; 503b4f9fe12SLen Brown block->handler_data = NULL; 504b4f9fe12SLen Brown 505b4f9fe12SLen Brown return status; 506b4f9fe12SLen Brown } 507b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_remove_notify_handler); 508b4f9fe12SLen Brown 509b4f9fe12SLen Brown /** 510b4f9fe12SLen Brown * wmi_get_event_data - Get WMI data associated with an event 511b4f9fe12SLen Brown * 512b4f9fe12SLen Brown * @event - Event to find 513b4f9fe12SLen Brown * &out - Buffer to hold event data 514b4f9fe12SLen Brown * 515b4f9fe12SLen Brown * Returns extra data associated with an event in WMI. 516b4f9fe12SLen Brown */ 517b4f9fe12SLen Brown acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out) 518b4f9fe12SLen Brown { 519b4f9fe12SLen Brown struct acpi_object_list input; 520b4f9fe12SLen Brown union acpi_object params[1]; 521b4f9fe12SLen Brown struct guid_block *gblock; 522b4f9fe12SLen Brown struct wmi_block *wblock; 523b4f9fe12SLen Brown struct list_head *p; 524b4f9fe12SLen Brown 525b4f9fe12SLen Brown input.count = 1; 526b4f9fe12SLen Brown input.pointer = params; 527b4f9fe12SLen Brown params[0].type = ACPI_TYPE_INTEGER; 528b4f9fe12SLen Brown params[0].integer.value = event; 529b4f9fe12SLen Brown 530b4f9fe12SLen Brown list_for_each(p, &wmi_blocks.list) { 531b4f9fe12SLen Brown wblock = list_entry(p, struct wmi_block, list); 532b4f9fe12SLen Brown gblock = &wblock->gblock; 533b4f9fe12SLen Brown 534b4f9fe12SLen Brown if ((gblock->flags & ACPI_WMI_EVENT) && 535b4f9fe12SLen Brown (gblock->notify_id == event)) 536b4f9fe12SLen Brown return acpi_evaluate_object(wblock->handle, "_WED", 537b4f9fe12SLen Brown &input, out); 538b4f9fe12SLen Brown } 539b4f9fe12SLen Brown 540b4f9fe12SLen Brown return AE_NOT_FOUND; 541b4f9fe12SLen Brown } 542b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_get_event_data); 543b4f9fe12SLen Brown 544b4f9fe12SLen Brown /** 545b4f9fe12SLen Brown * wmi_has_guid - Check if a GUID is available 546b4f9fe12SLen Brown * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba 547b4f9fe12SLen Brown * 548b4f9fe12SLen Brown * Check if a given GUID is defined by _WDG 549b4f9fe12SLen Brown */ 550b4f9fe12SLen Brown bool wmi_has_guid(const char *guid_string) 551b4f9fe12SLen Brown { 552b4f9fe12SLen Brown return find_guid(guid_string, NULL); 553b4f9fe12SLen Brown } 554b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_has_guid); 555b4f9fe12SLen Brown 556b4f9fe12SLen Brown /* 557b4f9fe12SLen Brown * Parse the _WDG method for the GUID data blocks 558b4f9fe12SLen Brown */ 559b4f9fe12SLen Brown static __init acpi_status parse_wdg(acpi_handle handle) 560b4f9fe12SLen Brown { 561b4f9fe12SLen Brown struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL}; 562b4f9fe12SLen Brown union acpi_object *obj; 563b4f9fe12SLen Brown struct guid_block *gblock; 564b4f9fe12SLen Brown struct wmi_block *wblock; 565b4f9fe12SLen Brown acpi_status status; 566b4f9fe12SLen Brown u32 i, total; 567b4f9fe12SLen Brown 568b4f9fe12SLen Brown status = acpi_evaluate_object(handle, "_WDG", NULL, &out); 569b4f9fe12SLen Brown 570b4f9fe12SLen Brown if (ACPI_FAILURE(status)) 571b4f9fe12SLen Brown return status; 572b4f9fe12SLen Brown 573b4f9fe12SLen Brown obj = (union acpi_object *) out.pointer; 574b4f9fe12SLen Brown 575b4f9fe12SLen Brown if (obj->type != ACPI_TYPE_BUFFER) 576b4f9fe12SLen Brown return AE_ERROR; 577b4f9fe12SLen Brown 578b4f9fe12SLen Brown total = obj->buffer.length / sizeof(struct guid_block); 579b4f9fe12SLen Brown 580b4f9fe12SLen Brown gblock = kzalloc(obj->buffer.length, GFP_KERNEL); 581b4f9fe12SLen Brown if (!gblock) 582b4f9fe12SLen Brown return AE_NO_MEMORY; 583b4f9fe12SLen Brown 584b4f9fe12SLen Brown memcpy(gblock, obj->buffer.pointer, obj->buffer.length); 585b4f9fe12SLen Brown 586b4f9fe12SLen Brown for (i = 0; i < total; i++) { 587b4f9fe12SLen Brown wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL); 588b4f9fe12SLen Brown if (!wblock) 589b4f9fe12SLen Brown return AE_NO_MEMORY; 590b4f9fe12SLen Brown 591b4f9fe12SLen Brown wblock->gblock = gblock[i]; 592b4f9fe12SLen Brown wblock->handle = handle; 593b4f9fe12SLen Brown list_add_tail(&wblock->list, &wmi_blocks.list); 594b4f9fe12SLen Brown } 595b4f9fe12SLen Brown 596b4f9fe12SLen Brown kfree(out.pointer); 597b4f9fe12SLen Brown kfree(gblock); 598b4f9fe12SLen Brown 599b4f9fe12SLen Brown return status; 600b4f9fe12SLen Brown } 601b4f9fe12SLen Brown 602b4f9fe12SLen Brown /* 603b4f9fe12SLen Brown * WMI can have EmbeddedControl access regions. In which case, we just want to 604b4f9fe12SLen Brown * hand these off to the EC driver. 605b4f9fe12SLen Brown */ 606b4f9fe12SLen Brown static acpi_status 607b4f9fe12SLen Brown acpi_wmi_ec_space_handler(u32 function, acpi_physical_address address, 608b4f9fe12SLen Brown u32 bits, acpi_integer * value, 609b4f9fe12SLen Brown void *handler_context, void *region_context) 610b4f9fe12SLen Brown { 611b4f9fe12SLen Brown int result = 0, i = 0; 612b4f9fe12SLen Brown u8 temp = 0; 613b4f9fe12SLen Brown 614b4f9fe12SLen Brown if ((address > 0xFF) || !value) 615b4f9fe12SLen Brown return AE_BAD_PARAMETER; 616b4f9fe12SLen Brown 617b4f9fe12SLen Brown if (function != ACPI_READ && function != ACPI_WRITE) 618b4f9fe12SLen Brown return AE_BAD_PARAMETER; 619b4f9fe12SLen Brown 620b4f9fe12SLen Brown if (bits != 8) 621b4f9fe12SLen Brown return AE_BAD_PARAMETER; 622b4f9fe12SLen Brown 623b4f9fe12SLen Brown if (function == ACPI_READ) { 624b4f9fe12SLen Brown result = ec_read(address, &temp); 625b4f9fe12SLen Brown (*value) |= ((acpi_integer)temp) << i; 626b4f9fe12SLen Brown } else { 627b4f9fe12SLen Brown temp = 0xff & ((*value) >> i); 628b4f9fe12SLen Brown result = ec_write(address, temp); 629b4f9fe12SLen Brown } 630b4f9fe12SLen Brown 631b4f9fe12SLen Brown switch (result) { 632b4f9fe12SLen Brown case -EINVAL: 633b4f9fe12SLen Brown return AE_BAD_PARAMETER; 634b4f9fe12SLen Brown break; 635b4f9fe12SLen Brown case -ENODEV: 636b4f9fe12SLen Brown return AE_NOT_FOUND; 637b4f9fe12SLen Brown break; 638b4f9fe12SLen Brown case -ETIME: 639b4f9fe12SLen Brown return AE_TIME; 640b4f9fe12SLen Brown break; 641b4f9fe12SLen Brown default: 642b4f9fe12SLen Brown return AE_OK; 643b4f9fe12SLen Brown } 644b4f9fe12SLen Brown } 645b4f9fe12SLen Brown 646b4f9fe12SLen Brown static void acpi_wmi_notify(acpi_handle handle, u32 event, void *data) 647b4f9fe12SLen Brown { 648b4f9fe12SLen Brown struct guid_block *block; 649b4f9fe12SLen Brown struct wmi_block *wblock; 650b4f9fe12SLen Brown struct list_head *p; 651b4f9fe12SLen Brown struct acpi_device *device = data; 652b4f9fe12SLen Brown 653b4f9fe12SLen Brown list_for_each(p, &wmi_blocks.list) { 654b4f9fe12SLen Brown wblock = list_entry(p, struct wmi_block, list); 655b4f9fe12SLen Brown block = &wblock->gblock; 656b4f9fe12SLen Brown 657b4f9fe12SLen Brown if ((block->flags & ACPI_WMI_EVENT) && 658b4f9fe12SLen Brown (block->notify_id == event)) { 659b4f9fe12SLen Brown if (wblock->handler) 660b4f9fe12SLen Brown wblock->handler(event, wblock->handler_data); 661b4f9fe12SLen Brown 662b4f9fe12SLen Brown acpi_bus_generate_netlink_event( 663b4f9fe12SLen Brown device->pnp.device_class, dev_name(&device->dev), 664b4f9fe12SLen Brown event, 0); 665b4f9fe12SLen Brown break; 666b4f9fe12SLen Brown } 667b4f9fe12SLen Brown } 668b4f9fe12SLen Brown } 669b4f9fe12SLen Brown 670b4f9fe12SLen Brown static int acpi_wmi_remove(struct acpi_device *device, int type) 671b4f9fe12SLen Brown { 672b4f9fe12SLen Brown acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY, 673b4f9fe12SLen Brown acpi_wmi_notify); 674b4f9fe12SLen Brown 675b4f9fe12SLen Brown acpi_remove_address_space_handler(device->handle, 676b4f9fe12SLen Brown ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler); 677b4f9fe12SLen Brown 678b4f9fe12SLen Brown return 0; 679b4f9fe12SLen Brown } 680b4f9fe12SLen Brown 681b4f9fe12SLen Brown static int __init acpi_wmi_add(struct acpi_device *device) 682b4f9fe12SLen Brown { 683b4f9fe12SLen Brown acpi_status status; 684b4f9fe12SLen Brown int result = 0; 685b4f9fe12SLen Brown 686b4f9fe12SLen Brown status = acpi_install_notify_handler(device->handle, ACPI_DEVICE_NOTIFY, 687b4f9fe12SLen Brown acpi_wmi_notify, device); 688b4f9fe12SLen Brown if (ACPI_FAILURE(status)) { 689b4f9fe12SLen Brown printk(KERN_ERR PREFIX "Error installing notify handler\n"); 690b4f9fe12SLen Brown return -ENODEV; 691b4f9fe12SLen Brown } 692b4f9fe12SLen Brown 693b4f9fe12SLen Brown status = acpi_install_address_space_handler(device->handle, 694b4f9fe12SLen Brown ACPI_ADR_SPACE_EC, 695b4f9fe12SLen Brown &acpi_wmi_ec_space_handler, 696b4f9fe12SLen Brown NULL, NULL); 697b4f9fe12SLen Brown if (ACPI_FAILURE(status)) 698b4f9fe12SLen Brown return -ENODEV; 699b4f9fe12SLen Brown 700b4f9fe12SLen Brown status = parse_wdg(device->handle); 701b4f9fe12SLen Brown if (ACPI_FAILURE(status)) { 702b4f9fe12SLen Brown printk(KERN_ERR PREFIX "Error installing EC region handler\n"); 703b4f9fe12SLen Brown return -ENODEV; 704b4f9fe12SLen Brown } 705b4f9fe12SLen Brown 706b4f9fe12SLen Brown return result; 707b4f9fe12SLen Brown } 708b4f9fe12SLen Brown 709b4f9fe12SLen Brown static int __init acpi_wmi_init(void) 710b4f9fe12SLen Brown { 711da511997SRoel Kluin int result; 712b4f9fe12SLen Brown 713b4f9fe12SLen Brown INIT_LIST_HEAD(&wmi_blocks.list); 714b4f9fe12SLen Brown 715b4f9fe12SLen Brown if (acpi_disabled) 716b4f9fe12SLen Brown return -ENODEV; 717b4f9fe12SLen Brown 718b4f9fe12SLen Brown result = acpi_bus_register_driver(&acpi_wmi_driver); 719b4f9fe12SLen Brown 720b4f9fe12SLen Brown if (result < 0) { 721b4f9fe12SLen Brown printk(KERN_INFO PREFIX "Error loading mapper\n"); 722b4f9fe12SLen Brown } else { 723b4f9fe12SLen Brown printk(KERN_INFO PREFIX "Mapper loaded\n"); 724b4f9fe12SLen Brown } 725b4f9fe12SLen Brown 726b4f9fe12SLen Brown return result; 727b4f9fe12SLen Brown } 728b4f9fe12SLen Brown 729b4f9fe12SLen Brown static void __exit acpi_wmi_exit(void) 730b4f9fe12SLen Brown { 731b4f9fe12SLen Brown struct list_head *p, *tmp; 732b4f9fe12SLen Brown struct wmi_block *wblock; 733b4f9fe12SLen Brown 734b4f9fe12SLen Brown acpi_bus_unregister_driver(&acpi_wmi_driver); 735b4f9fe12SLen Brown 736b4f9fe12SLen Brown list_for_each_safe(p, tmp, &wmi_blocks.list) { 737b4f9fe12SLen Brown wblock = list_entry(p, struct wmi_block, list); 738b4f9fe12SLen Brown 739b4f9fe12SLen Brown list_del(p); 740b4f9fe12SLen Brown kfree(wblock); 741b4f9fe12SLen Brown } 742b4f9fe12SLen Brown 743b4f9fe12SLen Brown printk(KERN_INFO PREFIX "Mapper unloaded\n"); 744b4f9fe12SLen Brown } 745b4f9fe12SLen Brown 746b4f9fe12SLen Brown subsys_initcall(acpi_wmi_init); 747b4f9fe12SLen Brown module_exit(acpi_wmi_exit); 748