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 308e07514dSDmitry Torokhov #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 318e07514dSDmitry Torokhov 32b4f9fe12SLen Brown #include <linux/kernel.h> 33b4f9fe12SLen Brown #include <linux/init.h> 34b4f9fe12SLen Brown #include <linux/types.h> 351caab3c1SMatthew Garrett #include <linux/device.h> 36b4f9fe12SLen Brown #include <linux/list.h> 37b4f9fe12SLen Brown #include <linux/acpi.h> 385a0e3ad6STejun Heo #include <linux/slab.h> 397c52d551SPaul Gortmaker #include <linux/module.h> 40b4f9fe12SLen Brown #include <acpi/acpi_bus.h> 41b4f9fe12SLen Brown #include <acpi/acpi_drivers.h> 42b4f9fe12SLen Brown 43b4f9fe12SLen Brown ACPI_MODULE_NAME("wmi"); 44b4f9fe12SLen Brown MODULE_AUTHOR("Carlos Corbacho"); 45b4f9fe12SLen Brown MODULE_DESCRIPTION("ACPI-WMI Mapping Driver"); 46b4f9fe12SLen Brown MODULE_LICENSE("GPL"); 47b4f9fe12SLen Brown 48b4f9fe12SLen Brown #define ACPI_WMI_CLASS "wmi" 49b4f9fe12SLen Brown 50b4f9fe12SLen Brown static DEFINE_MUTEX(wmi_data_lock); 51762e1a2fSDmitry Torokhov static LIST_HEAD(wmi_block_list); 52b4f9fe12SLen Brown 53b4f9fe12SLen Brown struct guid_block { 54b4f9fe12SLen Brown char guid[16]; 55b4f9fe12SLen Brown union { 56b4f9fe12SLen Brown char object_id[2]; 57b4f9fe12SLen Brown struct { 58b4f9fe12SLen Brown unsigned char notify_id; 59b4f9fe12SLen Brown unsigned char reserved; 60b4f9fe12SLen Brown }; 61b4f9fe12SLen Brown }; 62b4f9fe12SLen Brown u8 instance_count; 63b4f9fe12SLen Brown u8 flags; 64b4f9fe12SLen Brown }; 65b4f9fe12SLen Brown 66b4f9fe12SLen Brown struct wmi_block { 67b4f9fe12SLen Brown struct list_head list; 68b4f9fe12SLen Brown struct guid_block gblock; 69b4f9fe12SLen Brown acpi_handle handle; 70b4f9fe12SLen Brown wmi_notify_handler handler; 71b4f9fe12SLen Brown void *handler_data; 72c64eefd4SDmitry Torokhov struct device dev; 73b4f9fe12SLen Brown }; 74b4f9fe12SLen Brown 75b4f9fe12SLen Brown 76b4f9fe12SLen Brown /* 77b4f9fe12SLen Brown * If the GUID data block is marked as expensive, we must enable and 78b4f9fe12SLen Brown * explicitily disable data collection. 79b4f9fe12SLen Brown */ 80b4f9fe12SLen Brown #define ACPI_WMI_EXPENSIVE 0x1 81b4f9fe12SLen Brown #define ACPI_WMI_METHOD 0x2 /* GUID is a method */ 82b4f9fe12SLen Brown #define ACPI_WMI_STRING 0x4 /* GUID takes & returns a string */ 83b4f9fe12SLen Brown #define ACPI_WMI_EVENT 0x8 /* GUID is an event */ 84b4f9fe12SLen Brown 8590ab5ee9SRusty Russell static bool debug_event; 86fc3155b2SThomas Renninger module_param(debug_event, bool, 0444); 87fc3155b2SThomas Renninger MODULE_PARM_DESC(debug_event, 88fc3155b2SThomas Renninger "Log WMI Events [0/1]"); 89fc3155b2SThomas Renninger 9090ab5ee9SRusty Russell static bool debug_dump_wdg; 91a929aae0SThomas Renninger module_param(debug_dump_wdg, bool, 0444); 92a929aae0SThomas Renninger MODULE_PARM_DESC(debug_dump_wdg, 93a929aae0SThomas Renninger "Dump available WMI interfaces [0/1]"); 94a929aae0SThomas Renninger 9551fac838SRafael J. Wysocki static int acpi_wmi_remove(struct acpi_device *device); 96b4f9fe12SLen Brown static int acpi_wmi_add(struct acpi_device *device); 97f61bb939SBjorn Helgaas static void acpi_wmi_notify(struct acpi_device *device, u32 event); 98b4f9fe12SLen Brown 99b4f9fe12SLen Brown static const struct acpi_device_id wmi_device_ids[] = { 100b4f9fe12SLen Brown {"PNP0C14", 0}, 101b4f9fe12SLen Brown {"pnp0c14", 0}, 102b4f9fe12SLen Brown {"", 0}, 103b4f9fe12SLen Brown }; 104b4f9fe12SLen Brown MODULE_DEVICE_TABLE(acpi, wmi_device_ids); 105b4f9fe12SLen Brown 106b4f9fe12SLen Brown static struct acpi_driver acpi_wmi_driver = { 107b4f9fe12SLen Brown .name = "wmi", 108b4f9fe12SLen Brown .class = ACPI_WMI_CLASS, 109b4f9fe12SLen Brown .ids = wmi_device_ids, 110b4f9fe12SLen Brown .ops = { 111b4f9fe12SLen Brown .add = acpi_wmi_add, 112b4f9fe12SLen Brown .remove = acpi_wmi_remove, 113f61bb939SBjorn Helgaas .notify = acpi_wmi_notify, 114b4f9fe12SLen Brown }, 115b4f9fe12SLen Brown }; 116b4f9fe12SLen Brown 117b4f9fe12SLen Brown /* 118b4f9fe12SLen Brown * GUID parsing functions 119b4f9fe12SLen Brown */ 120b4f9fe12SLen Brown 121b4f9fe12SLen Brown /** 122b4f9fe12SLen Brown * wmi_parse_hexbyte - Convert a ASCII hex number to a byte 123b4f9fe12SLen Brown * @src: Pointer to at least 2 characters to convert. 124b4f9fe12SLen Brown * 125b4f9fe12SLen Brown * Convert a two character ASCII hex string to a number. 126b4f9fe12SLen Brown * 127b4f9fe12SLen Brown * Return: 0-255 Success, the byte was parsed correctly 128b4f9fe12SLen Brown * -1 Error, an invalid character was supplied 129b4f9fe12SLen Brown */ 130b4f9fe12SLen Brown static int wmi_parse_hexbyte(const u8 *src) 131b4f9fe12SLen Brown { 132b4f9fe12SLen Brown int h; 133392bd8b5SAndy Shevchenko int value; 134b4f9fe12SLen Brown 135b4f9fe12SLen Brown /* high part */ 136392bd8b5SAndy Shevchenko h = value = hex_to_bin(src[0]); 137392bd8b5SAndy Shevchenko if (value < 0) 138b4f9fe12SLen Brown return -1; 139b4f9fe12SLen Brown 140b4f9fe12SLen Brown /* low part */ 141392bd8b5SAndy Shevchenko value = hex_to_bin(src[1]); 142392bd8b5SAndy Shevchenko if (value >= 0) 143392bd8b5SAndy Shevchenko return (h << 4) | value; 144b4f9fe12SLen Brown return -1; 145b4f9fe12SLen Brown } 146b4f9fe12SLen Brown 147b4f9fe12SLen Brown /** 148b4f9fe12SLen Brown * wmi_swap_bytes - Rearrange GUID bytes to match GUID binary 149b4f9fe12SLen Brown * @src: Memory block holding binary GUID (16 bytes) 150b4f9fe12SLen Brown * @dest: Memory block to hold byte swapped binary GUID (16 bytes) 151b4f9fe12SLen Brown * 152b4f9fe12SLen Brown * Byte swap a binary GUID to match it's real GUID value 153b4f9fe12SLen Brown */ 154b4f9fe12SLen Brown static void wmi_swap_bytes(u8 *src, u8 *dest) 155b4f9fe12SLen Brown { 156b4f9fe12SLen Brown int i; 157b4f9fe12SLen Brown 158b4f9fe12SLen Brown for (i = 0; i <= 3; i++) 159b4f9fe12SLen Brown memcpy(dest + i, src + (3 - i), 1); 160b4f9fe12SLen Brown 161b4f9fe12SLen Brown for (i = 0; i <= 1; i++) 162b4f9fe12SLen Brown memcpy(dest + 4 + i, src + (5 - i), 1); 163b4f9fe12SLen Brown 164b4f9fe12SLen Brown for (i = 0; i <= 1; i++) 165b4f9fe12SLen Brown memcpy(dest + 6 + i, src + (7 - i), 1); 166b4f9fe12SLen Brown 167b4f9fe12SLen Brown memcpy(dest + 8, src + 8, 8); 168b4f9fe12SLen Brown } 169b4f9fe12SLen Brown 170b4f9fe12SLen Brown /** 171b4f9fe12SLen Brown * wmi_parse_guid - Convert GUID from ASCII to binary 172b4f9fe12SLen Brown * @src: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba 173b4f9fe12SLen Brown * @dest: Memory block to hold binary GUID (16 bytes) 174b4f9fe12SLen Brown * 175b4f9fe12SLen Brown * N.B. The GUID need not be NULL terminated. 176b4f9fe12SLen Brown * 177b4f9fe12SLen Brown * Return: 'true' @dest contains binary GUID 178b4f9fe12SLen Brown * 'false' @dest contents are undefined 179b4f9fe12SLen Brown */ 180b4f9fe12SLen Brown static bool wmi_parse_guid(const u8 *src, u8 *dest) 181b4f9fe12SLen Brown { 182b4f9fe12SLen Brown static const int size[] = { 4, 2, 2, 2, 6 }; 183b4f9fe12SLen Brown int i, j, v; 184b4f9fe12SLen Brown 185b4f9fe12SLen Brown if (src[8] != '-' || src[13] != '-' || 186b4f9fe12SLen Brown src[18] != '-' || src[23] != '-') 187b4f9fe12SLen Brown return false; 188b4f9fe12SLen Brown 189b4f9fe12SLen Brown for (j = 0; j < 5; j++, src++) { 190b4f9fe12SLen Brown for (i = 0; i < size[j]; i++, src += 2, *dest++ = v) { 191b4f9fe12SLen Brown v = wmi_parse_hexbyte(src); 192b4f9fe12SLen Brown if (v < 0) 193b4f9fe12SLen Brown return false; 194b4f9fe12SLen Brown } 195b4f9fe12SLen Brown } 196b4f9fe12SLen Brown 197b4f9fe12SLen Brown return true; 198b4f9fe12SLen Brown } 199b4f9fe12SLen Brown 2001caab3c1SMatthew Garrett /* 2011caab3c1SMatthew Garrett * Convert a raw GUID to the ACII string representation 2021caab3c1SMatthew Garrett */ 2031caab3c1SMatthew Garrett static int wmi_gtoa(const char *in, char *out) 2041caab3c1SMatthew Garrett { 2051caab3c1SMatthew Garrett int i; 2061caab3c1SMatthew Garrett 2071caab3c1SMatthew Garrett for (i = 3; i >= 0; i--) 2081caab3c1SMatthew Garrett out += sprintf(out, "%02X", in[i] & 0xFF); 2091caab3c1SMatthew Garrett 2101caab3c1SMatthew Garrett out += sprintf(out, "-"); 2111caab3c1SMatthew Garrett out += sprintf(out, "%02X", in[5] & 0xFF); 2121caab3c1SMatthew Garrett out += sprintf(out, "%02X", in[4] & 0xFF); 2131caab3c1SMatthew Garrett out += sprintf(out, "-"); 2141caab3c1SMatthew Garrett out += sprintf(out, "%02X", in[7] & 0xFF); 2151caab3c1SMatthew Garrett out += sprintf(out, "%02X", in[6] & 0xFF); 2161caab3c1SMatthew Garrett out += sprintf(out, "-"); 2171caab3c1SMatthew Garrett out += sprintf(out, "%02X", in[8] & 0xFF); 2181caab3c1SMatthew Garrett out += sprintf(out, "%02X", in[9] & 0xFF); 2191caab3c1SMatthew Garrett out += sprintf(out, "-"); 2201caab3c1SMatthew Garrett 2211caab3c1SMatthew Garrett for (i = 10; i <= 15; i++) 2221caab3c1SMatthew Garrett out += sprintf(out, "%02X", in[i] & 0xFF); 2231caab3c1SMatthew Garrett 2244e4304d7SDmitry Torokhov *out = '\0'; 2251caab3c1SMatthew Garrett return 0; 2261caab3c1SMatthew Garrett } 2271caab3c1SMatthew Garrett 228b4f9fe12SLen Brown static bool find_guid(const char *guid_string, struct wmi_block **out) 229b4f9fe12SLen Brown { 230b4f9fe12SLen Brown char tmp[16], guid_input[16]; 231b4f9fe12SLen Brown struct wmi_block *wblock; 232b4f9fe12SLen Brown struct guid_block *block; 233b4f9fe12SLen Brown struct list_head *p; 234b4f9fe12SLen Brown 235b4f9fe12SLen Brown wmi_parse_guid(guid_string, tmp); 236b4f9fe12SLen Brown wmi_swap_bytes(tmp, guid_input); 237b4f9fe12SLen Brown 238762e1a2fSDmitry Torokhov list_for_each(p, &wmi_block_list) { 239b4f9fe12SLen Brown wblock = list_entry(p, struct wmi_block, list); 240b4f9fe12SLen Brown block = &wblock->gblock; 241b4f9fe12SLen Brown 242b4f9fe12SLen Brown if (memcmp(block->guid, guid_input, 16) == 0) { 243b4f9fe12SLen Brown if (out) 244b4f9fe12SLen Brown *out = wblock; 245b4f9fe12SLen Brown return 1; 246b4f9fe12SLen Brown } 247b4f9fe12SLen Brown } 248b4f9fe12SLen Brown return 0; 249b4f9fe12SLen Brown } 250b4f9fe12SLen Brown 251b4f9fe12SLen Brown static acpi_status wmi_method_enable(struct wmi_block *wblock, int enable) 252b4f9fe12SLen Brown { 253b4f9fe12SLen Brown struct guid_block *block = NULL; 254b4f9fe12SLen Brown char method[5]; 255b4f9fe12SLen Brown acpi_status status; 256b4f9fe12SLen Brown acpi_handle handle; 257b4f9fe12SLen Brown 258b4f9fe12SLen Brown block = &wblock->gblock; 259b4f9fe12SLen Brown handle = wblock->handle; 260b4f9fe12SLen Brown 261b4f9fe12SLen Brown if (!block) 262b4f9fe12SLen Brown return AE_NOT_EXIST; 263b4f9fe12SLen Brown 264b4f9fe12SLen Brown 265b4f9fe12SLen Brown snprintf(method, 5, "WE%02X", block->notify_id); 2668122ab66SZhang Rui status = acpi_execute_simple_method(handle, method, enable); 267b4f9fe12SLen Brown 268b4f9fe12SLen Brown if (status != AE_OK && status != AE_NOT_FOUND) 269b4f9fe12SLen Brown return status; 270b4f9fe12SLen Brown else 271b4f9fe12SLen Brown return AE_OK; 272b4f9fe12SLen Brown } 273b4f9fe12SLen Brown 274b4f9fe12SLen Brown /* 275b4f9fe12SLen Brown * Exported WMI functions 276b4f9fe12SLen Brown */ 277b4f9fe12SLen Brown /** 278b4f9fe12SLen Brown * wmi_evaluate_method - Evaluate a WMI method 279b4f9fe12SLen Brown * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba 280b4f9fe12SLen Brown * @instance: Instance index 281b4f9fe12SLen Brown * @method_id: Method ID to call 282b4f9fe12SLen Brown * &in: Buffer containing input for the method call 283b4f9fe12SLen Brown * &out: Empty buffer to return the method results 284b4f9fe12SLen Brown * 285b4f9fe12SLen Brown * Call an ACPI-WMI method 286b4f9fe12SLen Brown */ 287b4f9fe12SLen Brown acpi_status wmi_evaluate_method(const char *guid_string, u8 instance, 288b4f9fe12SLen Brown u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out) 289b4f9fe12SLen Brown { 290b4f9fe12SLen Brown struct guid_block *block = NULL; 291b4f9fe12SLen Brown struct wmi_block *wblock = NULL; 292b4f9fe12SLen Brown acpi_handle handle; 293b4f9fe12SLen Brown acpi_status status; 294b4f9fe12SLen Brown struct acpi_object_list input; 295b4f9fe12SLen Brown union acpi_object params[3]; 296f3d83e24SCostantino Leandro char method[5] = "WM"; 297b4f9fe12SLen Brown 298b4f9fe12SLen Brown if (!find_guid(guid_string, &wblock)) 299b4f9fe12SLen Brown return AE_ERROR; 300b4f9fe12SLen Brown 301b4f9fe12SLen Brown block = &wblock->gblock; 302b4f9fe12SLen Brown handle = wblock->handle; 303b4f9fe12SLen Brown 304b4f9fe12SLen Brown if (!(block->flags & ACPI_WMI_METHOD)) 305b4f9fe12SLen Brown return AE_BAD_DATA; 306b4f9fe12SLen Brown 307b4f9fe12SLen Brown if (block->instance_count < instance) 308b4f9fe12SLen Brown return AE_BAD_PARAMETER; 309b4f9fe12SLen Brown 310b4f9fe12SLen Brown input.count = 2; 311b4f9fe12SLen Brown input.pointer = params; 312b4f9fe12SLen Brown params[0].type = ACPI_TYPE_INTEGER; 313b4f9fe12SLen Brown params[0].integer.value = instance; 314b4f9fe12SLen Brown params[1].type = ACPI_TYPE_INTEGER; 315b4f9fe12SLen Brown params[1].integer.value = method_id; 316b4f9fe12SLen Brown 317b4f9fe12SLen Brown if (in) { 318b4f9fe12SLen Brown input.count = 3; 319b4f9fe12SLen Brown 320b4f9fe12SLen Brown if (block->flags & ACPI_WMI_STRING) { 321b4f9fe12SLen Brown params[2].type = ACPI_TYPE_STRING; 322b4f9fe12SLen Brown } else { 323b4f9fe12SLen Brown params[2].type = ACPI_TYPE_BUFFER; 324b4f9fe12SLen Brown } 325b4f9fe12SLen Brown params[2].buffer.length = in->length; 326b4f9fe12SLen Brown params[2].buffer.pointer = in->pointer; 327b4f9fe12SLen Brown } 328b4f9fe12SLen Brown 329b4f9fe12SLen Brown strncat(method, block->object_id, 2); 330b4f9fe12SLen Brown 331b4f9fe12SLen Brown status = acpi_evaluate_object(handle, method, &input, out); 332b4f9fe12SLen Brown 333b4f9fe12SLen Brown return status; 334b4f9fe12SLen Brown } 335b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_evaluate_method); 336b4f9fe12SLen Brown 337b4f9fe12SLen Brown /** 338b4f9fe12SLen Brown * wmi_query_block - Return contents of a WMI block 339b4f9fe12SLen Brown * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba 340b4f9fe12SLen Brown * @instance: Instance index 341b4f9fe12SLen Brown * &out: Empty buffer to return the contents of the data block to 342b4f9fe12SLen Brown * 343b4f9fe12SLen Brown * Return the contents of an ACPI-WMI data block to a buffer 344b4f9fe12SLen Brown */ 345b4f9fe12SLen Brown acpi_status wmi_query_block(const char *guid_string, u8 instance, 346b4f9fe12SLen Brown struct acpi_buffer *out) 347b4f9fe12SLen Brown { 348b4f9fe12SLen Brown struct guid_block *block = NULL; 349b4f9fe12SLen Brown struct wmi_block *wblock = NULL; 35054f14c27SZhang Rui acpi_handle handle; 351b4f9fe12SLen Brown acpi_status status, wc_status = AE_ERROR; 3528122ab66SZhang Rui struct acpi_object_list input; 3538122ab66SZhang Rui union acpi_object wq_params[1]; 354f3d83e24SCostantino Leandro char method[5]; 355f3d83e24SCostantino Leandro char wc_method[5] = "WC"; 356b4f9fe12SLen Brown 357b4f9fe12SLen Brown if (!guid_string || !out) 358b4f9fe12SLen Brown return AE_BAD_PARAMETER; 359b4f9fe12SLen Brown 360b4f9fe12SLen Brown if (!find_guid(guid_string, &wblock)) 361b4f9fe12SLen Brown return AE_ERROR; 362b4f9fe12SLen Brown 363b4f9fe12SLen Brown block = &wblock->gblock; 364b4f9fe12SLen Brown handle = wblock->handle; 365b4f9fe12SLen Brown 366b4f9fe12SLen Brown if (block->instance_count < instance) 367b4f9fe12SLen Brown return AE_BAD_PARAMETER; 368b4f9fe12SLen Brown 369b4f9fe12SLen Brown /* Check GUID is a data block */ 370b4f9fe12SLen Brown if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD)) 371b4f9fe12SLen Brown return AE_ERROR; 372b4f9fe12SLen Brown 373b4f9fe12SLen Brown input.count = 1; 374b4f9fe12SLen Brown input.pointer = wq_params; 375b4f9fe12SLen Brown wq_params[0].type = ACPI_TYPE_INTEGER; 376b4f9fe12SLen Brown wq_params[0].integer.value = instance; 377b4f9fe12SLen Brown 378b4f9fe12SLen Brown /* 379b4f9fe12SLen Brown * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method first to 380b4f9fe12SLen Brown * enable collection. 381b4f9fe12SLen Brown */ 382b4f9fe12SLen Brown if (block->flags & ACPI_WMI_EXPENSIVE) { 383b4f9fe12SLen Brown strncat(wc_method, block->object_id, 2); 384b4f9fe12SLen Brown 385b4f9fe12SLen Brown /* 386b4f9fe12SLen Brown * Some GUIDs break the specification by declaring themselves 387b4f9fe12SLen Brown * expensive, but have no corresponding WCxx method. So we 388b4f9fe12SLen Brown * should not fail if this happens. 389b4f9fe12SLen Brown */ 39054f14c27SZhang Rui if (acpi_has_method(handle, wc_method)) 3918122ab66SZhang Rui wc_status = acpi_execute_simple_method(handle, 3928122ab66SZhang Rui wc_method, 1); 393b4f9fe12SLen Brown } 394b4f9fe12SLen Brown 395b4f9fe12SLen Brown strcpy(method, "WQ"); 396b4f9fe12SLen Brown strncat(method, block->object_id, 2); 397b4f9fe12SLen Brown 398b4f9fe12SLen Brown status = acpi_evaluate_object(handle, method, &input, out); 399b4f9fe12SLen Brown 400b4f9fe12SLen Brown /* 401b4f9fe12SLen Brown * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method, even if 402b4f9fe12SLen Brown * the WQxx method failed - we should disable collection anyway. 403b4f9fe12SLen Brown */ 404b4f9fe12SLen Brown if ((block->flags & ACPI_WMI_EXPENSIVE) && ACPI_SUCCESS(wc_status)) { 4058122ab66SZhang Rui status = acpi_execute_simple_method(handle, wc_method, 0); 406b4f9fe12SLen Brown } 407b4f9fe12SLen Brown 408b4f9fe12SLen Brown return status; 409b4f9fe12SLen Brown } 410b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_query_block); 411b4f9fe12SLen Brown 412b4f9fe12SLen Brown /** 413b4f9fe12SLen Brown * wmi_set_block - Write to a WMI block 414b4f9fe12SLen Brown * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba 415b4f9fe12SLen Brown * @instance: Instance index 416b4f9fe12SLen Brown * &in: Buffer containing new values for the data block 417b4f9fe12SLen Brown * 418b4f9fe12SLen Brown * Write the contents of the input buffer to an ACPI-WMI data block 419b4f9fe12SLen Brown */ 420b4f9fe12SLen Brown acpi_status wmi_set_block(const char *guid_string, u8 instance, 421b4f9fe12SLen Brown const struct acpi_buffer *in) 422b4f9fe12SLen Brown { 423b4f9fe12SLen Brown struct guid_block *block = NULL; 424b4f9fe12SLen Brown struct wmi_block *wblock = NULL; 425b4f9fe12SLen Brown acpi_handle handle; 426b4f9fe12SLen Brown struct acpi_object_list input; 427b4f9fe12SLen Brown union acpi_object params[2]; 428f3d83e24SCostantino Leandro char method[5] = "WS"; 429b4f9fe12SLen Brown 430b4f9fe12SLen Brown if (!guid_string || !in) 431b4f9fe12SLen Brown return AE_BAD_DATA; 432b4f9fe12SLen Brown 433b4f9fe12SLen Brown if (!find_guid(guid_string, &wblock)) 434b4f9fe12SLen Brown return AE_ERROR; 435b4f9fe12SLen Brown 436b4f9fe12SLen Brown block = &wblock->gblock; 437b4f9fe12SLen Brown handle = wblock->handle; 438b4f9fe12SLen Brown 439b4f9fe12SLen Brown if (block->instance_count < instance) 440b4f9fe12SLen Brown return AE_BAD_PARAMETER; 441b4f9fe12SLen Brown 442b4f9fe12SLen Brown /* Check GUID is a data block */ 443b4f9fe12SLen Brown if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD)) 444b4f9fe12SLen Brown return AE_ERROR; 445b4f9fe12SLen Brown 446b4f9fe12SLen Brown input.count = 2; 447b4f9fe12SLen Brown input.pointer = params; 448b4f9fe12SLen Brown params[0].type = ACPI_TYPE_INTEGER; 449b4f9fe12SLen Brown params[0].integer.value = instance; 450b4f9fe12SLen Brown 451b4f9fe12SLen Brown if (block->flags & ACPI_WMI_STRING) { 452b4f9fe12SLen Brown params[1].type = ACPI_TYPE_STRING; 453b4f9fe12SLen Brown } else { 454b4f9fe12SLen Brown params[1].type = ACPI_TYPE_BUFFER; 455b4f9fe12SLen Brown } 456b4f9fe12SLen Brown params[1].buffer.length = in->length; 457b4f9fe12SLen Brown params[1].buffer.pointer = in->pointer; 458b4f9fe12SLen Brown 459b4f9fe12SLen Brown strncat(method, block->object_id, 2); 460b4f9fe12SLen Brown 461b4f9fe12SLen Brown return acpi_evaluate_object(handle, method, &input, NULL); 462b4f9fe12SLen Brown } 463b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_set_block); 464b4f9fe12SLen Brown 46537830662SDmitry Torokhov static void wmi_dump_wdg(const struct guid_block *g) 466a929aae0SThomas Renninger { 467a929aae0SThomas Renninger char guid_string[37]; 468a929aae0SThomas Renninger 469a929aae0SThomas Renninger wmi_gtoa(g->guid, guid_string); 4708e07514dSDmitry Torokhov 4718e07514dSDmitry Torokhov pr_info("%s:\n", guid_string); 4728e07514dSDmitry Torokhov pr_info("\tobject_id: %c%c\n", g->object_id[0], g->object_id[1]); 4738e07514dSDmitry Torokhov pr_info("\tnotify_id: %02X\n", g->notify_id); 4748e07514dSDmitry Torokhov pr_info("\treserved: %02X\n", g->reserved); 4758e07514dSDmitry Torokhov pr_info("\tinstance_count: %d\n", g->instance_count); 4768e07514dSDmitry Torokhov pr_info("\tflags: %#x", g->flags); 477a929aae0SThomas Renninger if (g->flags) { 478a929aae0SThomas Renninger if (g->flags & ACPI_WMI_EXPENSIVE) 4798e07514dSDmitry Torokhov pr_cont(" ACPI_WMI_EXPENSIVE"); 480a929aae0SThomas Renninger if (g->flags & ACPI_WMI_METHOD) 4818e07514dSDmitry Torokhov pr_cont(" ACPI_WMI_METHOD"); 482a929aae0SThomas Renninger if (g->flags & ACPI_WMI_STRING) 4838e07514dSDmitry Torokhov pr_cont(" ACPI_WMI_STRING"); 484a929aae0SThomas Renninger if (g->flags & ACPI_WMI_EVENT) 4858e07514dSDmitry Torokhov pr_cont(" ACPI_WMI_EVENT"); 486a929aae0SThomas Renninger } 4878e07514dSDmitry Torokhov pr_cont("\n"); 488a929aae0SThomas Renninger 489a929aae0SThomas Renninger } 490a929aae0SThomas Renninger 491fc3155b2SThomas Renninger static void wmi_notify_debug(u32 value, void *context) 492fc3155b2SThomas Renninger { 493fc3155b2SThomas Renninger struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; 494fc3155b2SThomas Renninger union acpi_object *obj; 4951492616aSAxel Lin acpi_status status; 496fc3155b2SThomas Renninger 4971492616aSAxel Lin status = wmi_get_event_data(value, &response); 4981492616aSAxel Lin if (status != AE_OK) { 4998e07514dSDmitry Torokhov pr_info("bad event status 0x%x\n", status); 5001492616aSAxel Lin return; 5011492616aSAxel Lin } 502fc3155b2SThomas Renninger 503fc3155b2SThomas Renninger obj = (union acpi_object *)response.pointer; 504fc3155b2SThomas Renninger 505fc3155b2SThomas Renninger if (!obj) 506fc3155b2SThomas Renninger return; 507fc3155b2SThomas Renninger 5088e07514dSDmitry Torokhov pr_info("DEBUG Event "); 509fc3155b2SThomas Renninger switch(obj->type) { 510fc3155b2SThomas Renninger case ACPI_TYPE_BUFFER: 5118e07514dSDmitry Torokhov pr_cont("BUFFER_TYPE - length %d\n", obj->buffer.length); 512fc3155b2SThomas Renninger break; 513fc3155b2SThomas Renninger case ACPI_TYPE_STRING: 5148e07514dSDmitry Torokhov pr_cont("STRING_TYPE - %s\n", obj->string.pointer); 515fc3155b2SThomas Renninger break; 516fc3155b2SThomas Renninger case ACPI_TYPE_INTEGER: 5178e07514dSDmitry Torokhov pr_cont("INTEGER_TYPE - %llu\n", obj->integer.value); 518fc3155b2SThomas Renninger break; 519fc3155b2SThomas Renninger case ACPI_TYPE_PACKAGE: 5208e07514dSDmitry Torokhov pr_cont("PACKAGE_TYPE - %d elements\n", obj->package.count); 521fc3155b2SThomas Renninger break; 522fc3155b2SThomas Renninger default: 5238e07514dSDmitry Torokhov pr_cont("object type 0x%X\n", obj->type); 524fc3155b2SThomas Renninger } 5251492616aSAxel Lin kfree(obj); 526fc3155b2SThomas Renninger } 527fc3155b2SThomas Renninger 528b4f9fe12SLen Brown /** 529b4f9fe12SLen Brown * wmi_install_notify_handler - Register handler for WMI events 530b4f9fe12SLen Brown * @handler: Function to handle notifications 531b4f9fe12SLen Brown * @data: Data to be returned to handler when event is fired 532b4f9fe12SLen Brown * 533b4f9fe12SLen Brown * Register a handler for events sent to the ACPI-WMI mapper device. 534b4f9fe12SLen Brown */ 535b4f9fe12SLen Brown acpi_status wmi_install_notify_handler(const char *guid, 536b4f9fe12SLen Brown wmi_notify_handler handler, void *data) 537b4f9fe12SLen Brown { 538b4f9fe12SLen Brown struct wmi_block *block; 53958f6425eSColin King acpi_status status = AE_NOT_EXIST; 54058f6425eSColin King char tmp[16], guid_input[16]; 54158f6425eSColin King struct list_head *p; 542b4f9fe12SLen Brown 543b4f9fe12SLen Brown if (!guid || !handler) 544b4f9fe12SLen Brown return AE_BAD_PARAMETER; 545b4f9fe12SLen Brown 54658f6425eSColin King wmi_parse_guid(guid, tmp); 54758f6425eSColin King wmi_swap_bytes(tmp, guid_input); 548b4f9fe12SLen Brown 54958f6425eSColin King list_for_each(p, &wmi_block_list) { 55058f6425eSColin King acpi_status wmi_status; 55158f6425eSColin King block = list_entry(p, struct wmi_block, list); 55258f6425eSColin King 55358f6425eSColin King if (memcmp(block->gblock.guid, guid_input, 16) == 0) { 55458f6425eSColin King if (block->handler && 55558f6425eSColin King block->handler != wmi_notify_debug) 556b4f9fe12SLen Brown return AE_ALREADY_ACQUIRED; 557b4f9fe12SLen Brown 558b4f9fe12SLen Brown block->handler = handler; 559b4f9fe12SLen Brown block->handler_data = data; 560b4f9fe12SLen Brown 56158f6425eSColin King wmi_status = wmi_method_enable(block, 1); 56258f6425eSColin King if ((wmi_status != AE_OK) || 56358f6425eSColin King ((wmi_status == AE_OK) && (status == AE_NOT_EXIST))) 56458f6425eSColin King status = wmi_status; 56558f6425eSColin King } 56658f6425eSColin King } 567b4f9fe12SLen Brown 568b4f9fe12SLen Brown return status; 569b4f9fe12SLen Brown } 570b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_install_notify_handler); 571b4f9fe12SLen Brown 572b4f9fe12SLen Brown /** 573b4f9fe12SLen Brown * wmi_uninstall_notify_handler - Unregister handler for WMI events 574b4f9fe12SLen Brown * 575b4f9fe12SLen Brown * Unregister handler for events sent to the ACPI-WMI mapper device. 576b4f9fe12SLen Brown */ 577b4f9fe12SLen Brown acpi_status wmi_remove_notify_handler(const char *guid) 578b4f9fe12SLen Brown { 579b4f9fe12SLen Brown struct wmi_block *block; 58058f6425eSColin King acpi_status status = AE_NOT_EXIST; 58158f6425eSColin King char tmp[16], guid_input[16]; 58258f6425eSColin King struct list_head *p; 583b4f9fe12SLen Brown 584b4f9fe12SLen Brown if (!guid) 585b4f9fe12SLen Brown return AE_BAD_PARAMETER; 586b4f9fe12SLen Brown 58758f6425eSColin King wmi_parse_guid(guid, tmp); 58858f6425eSColin King wmi_swap_bytes(tmp, guid_input); 589b4f9fe12SLen Brown 59058f6425eSColin King list_for_each(p, &wmi_block_list) { 59158f6425eSColin King acpi_status wmi_status; 59258f6425eSColin King block = list_entry(p, struct wmi_block, list); 59358f6425eSColin King 59458f6425eSColin King if (memcmp(block->gblock.guid, guid_input, 16) == 0) { 59558f6425eSColin King if (!block->handler || 59658f6425eSColin King block->handler == wmi_notify_debug) 597b4f9fe12SLen Brown return AE_NULL_ENTRY; 598b4f9fe12SLen Brown 599fc3155b2SThomas Renninger if (debug_event) { 600fc3155b2SThomas Renninger block->handler = wmi_notify_debug; 60158f6425eSColin King status = AE_OK; 602fc3155b2SThomas Renninger } else { 60358f6425eSColin King wmi_status = wmi_method_enable(block, 0); 604b4f9fe12SLen Brown block->handler = NULL; 605b4f9fe12SLen Brown block->handler_data = NULL; 60658f6425eSColin King if ((wmi_status != AE_OK) || 60758f6425eSColin King ((wmi_status == AE_OK) && 60858f6425eSColin King (status == AE_NOT_EXIST))) 60958f6425eSColin King status = wmi_status; 610fc3155b2SThomas Renninger } 61158f6425eSColin King } 61258f6425eSColin King } 61358f6425eSColin King 614b4f9fe12SLen Brown return status; 615b4f9fe12SLen Brown } 616b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_remove_notify_handler); 617b4f9fe12SLen Brown 618b4f9fe12SLen Brown /** 619b4f9fe12SLen Brown * wmi_get_event_data - Get WMI data associated with an event 620b4f9fe12SLen Brown * 6213e9b988eSAnisse Astier * @event: Event to find 6223e9b988eSAnisse Astier * @out: Buffer to hold event data. out->pointer should be freed with kfree() 623b4f9fe12SLen Brown * 624b4f9fe12SLen Brown * Returns extra data associated with an event in WMI. 625b4f9fe12SLen Brown */ 626b4f9fe12SLen Brown acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out) 627b4f9fe12SLen Brown { 628b4f9fe12SLen Brown struct acpi_object_list input; 629b4f9fe12SLen Brown union acpi_object params[1]; 630b4f9fe12SLen Brown struct guid_block *gblock; 631b4f9fe12SLen Brown struct wmi_block *wblock; 632b4f9fe12SLen Brown struct list_head *p; 633b4f9fe12SLen Brown 634b4f9fe12SLen Brown input.count = 1; 635b4f9fe12SLen Brown input.pointer = params; 636b4f9fe12SLen Brown params[0].type = ACPI_TYPE_INTEGER; 637b4f9fe12SLen Brown params[0].integer.value = event; 638b4f9fe12SLen Brown 639762e1a2fSDmitry Torokhov list_for_each(p, &wmi_block_list) { 640b4f9fe12SLen Brown wblock = list_entry(p, struct wmi_block, list); 641b4f9fe12SLen Brown gblock = &wblock->gblock; 642b4f9fe12SLen Brown 643b4f9fe12SLen Brown if ((gblock->flags & ACPI_WMI_EVENT) && 644b4f9fe12SLen Brown (gblock->notify_id == event)) 645b4f9fe12SLen Brown return acpi_evaluate_object(wblock->handle, "_WED", 646b4f9fe12SLen Brown &input, out); 647b4f9fe12SLen Brown } 648b4f9fe12SLen Brown 649b4f9fe12SLen Brown return AE_NOT_FOUND; 650b4f9fe12SLen Brown } 651b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_get_event_data); 652b4f9fe12SLen Brown 653b4f9fe12SLen Brown /** 654b4f9fe12SLen Brown * wmi_has_guid - Check if a GUID is available 655b4f9fe12SLen Brown * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba 656b4f9fe12SLen Brown * 657b4f9fe12SLen Brown * Check if a given GUID is defined by _WDG 658b4f9fe12SLen Brown */ 659b4f9fe12SLen Brown bool wmi_has_guid(const char *guid_string) 660b4f9fe12SLen Brown { 661b4f9fe12SLen Brown return find_guid(guid_string, NULL); 662b4f9fe12SLen Brown } 663b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_has_guid); 664b4f9fe12SLen Brown 665b4f9fe12SLen Brown /* 6661caab3c1SMatthew Garrett * sysfs interface 6671caab3c1SMatthew Garrett */ 668614ef432SDmitry Torokhov static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, 6691caab3c1SMatthew Garrett char *buf) 6701caab3c1SMatthew Garrett { 6711caab3c1SMatthew Garrett char guid_string[37]; 6721caab3c1SMatthew Garrett struct wmi_block *wblock; 6731caab3c1SMatthew Garrett 6741caab3c1SMatthew Garrett wblock = dev_get_drvdata(dev); 675a80e1053SPrarit Bhargava if (!wblock) { 676a80e1053SPrarit Bhargava strcat(buf, "\n"); 677a80e1053SPrarit Bhargava return strlen(buf); 678a80e1053SPrarit Bhargava } 6791caab3c1SMatthew Garrett 6801caab3c1SMatthew Garrett wmi_gtoa(wblock->gblock.guid, guid_string); 6811caab3c1SMatthew Garrett 6821caab3c1SMatthew Garrett return sprintf(buf, "wmi:%s\n", guid_string); 6831caab3c1SMatthew Garrett } 684e80b89a5SGreg Kroah-Hartman static DEVICE_ATTR_RO(modalias); 685614ef432SDmitry Torokhov 686e80b89a5SGreg Kroah-Hartman static struct attribute *wmi_attrs[] = { 687e80b89a5SGreg Kroah-Hartman &dev_attr_modalias.attr, 688e80b89a5SGreg Kroah-Hartman NULL, 689614ef432SDmitry Torokhov }; 690e80b89a5SGreg Kroah-Hartman ATTRIBUTE_GROUPS(wmi); 6911caab3c1SMatthew Garrett 6921caab3c1SMatthew Garrett static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env) 6931caab3c1SMatthew Garrett { 6941caab3c1SMatthew Garrett char guid_string[37]; 6951caab3c1SMatthew Garrett 6961caab3c1SMatthew Garrett struct wmi_block *wblock; 6971caab3c1SMatthew Garrett 6981caab3c1SMatthew Garrett if (add_uevent_var(env, "MODALIAS=")) 6991caab3c1SMatthew Garrett return -ENOMEM; 7001caab3c1SMatthew Garrett 7011caab3c1SMatthew Garrett wblock = dev_get_drvdata(dev); 7021caab3c1SMatthew Garrett if (!wblock) 7031caab3c1SMatthew Garrett return -ENOMEM; 7041caab3c1SMatthew Garrett 7051caab3c1SMatthew Garrett wmi_gtoa(wblock->gblock.guid, guid_string); 7061caab3c1SMatthew Garrett 7071caab3c1SMatthew Garrett strcpy(&env->buf[env->buflen - 1], "wmi:"); 7081caab3c1SMatthew Garrett memcpy(&env->buf[env->buflen - 1 + 4], guid_string, 36); 7091caab3c1SMatthew Garrett env->buflen += 40; 7101caab3c1SMatthew Garrett 7111caab3c1SMatthew Garrett return 0; 7121caab3c1SMatthew Garrett } 7131caab3c1SMatthew Garrett 7141caab3c1SMatthew Garrett static void wmi_dev_free(struct device *dev) 7151caab3c1SMatthew Garrett { 716c64eefd4SDmitry Torokhov struct wmi_block *wmi_block = container_of(dev, struct wmi_block, dev); 717c64eefd4SDmitry Torokhov 718c64eefd4SDmitry Torokhov kfree(wmi_block); 7191caab3c1SMatthew Garrett } 7201caab3c1SMatthew Garrett 7211caab3c1SMatthew Garrett static struct class wmi_class = { 7221caab3c1SMatthew Garrett .name = "wmi", 7231caab3c1SMatthew Garrett .dev_release = wmi_dev_free, 7241caab3c1SMatthew Garrett .dev_uevent = wmi_dev_uevent, 725e80b89a5SGreg Kroah-Hartman .dev_groups = wmi_groups, 7261caab3c1SMatthew Garrett }; 7271caab3c1SMatthew Garrett 72858f6425eSColin King static int wmi_create_device(const struct guid_block *gblock, 72958f6425eSColin King struct wmi_block *wblock, acpi_handle handle) 7301caab3c1SMatthew Garrett { 731c64eefd4SDmitry Torokhov char guid_string[37]; 7321caab3c1SMatthew Garrett 733c64eefd4SDmitry Torokhov wblock->dev.class = &wmi_class; 7341caab3c1SMatthew Garrett 7351caab3c1SMatthew Garrett wmi_gtoa(gblock->guid, guid_string); 73602aa2a37SKees Cook dev_set_name(&wblock->dev, "%s", guid_string); 7371caab3c1SMatthew Garrett 738c64eefd4SDmitry Torokhov dev_set_drvdata(&wblock->dev, wblock); 739c64eefd4SDmitry Torokhov 74058f6425eSColin King return device_register(&wblock->dev); 7411caab3c1SMatthew Garrett } 7421caab3c1SMatthew Garrett 743c64eefd4SDmitry Torokhov static void wmi_free_devices(void) 7441caab3c1SMatthew Garrett { 745c64eefd4SDmitry Torokhov struct wmi_block *wblock, *next; 7461caab3c1SMatthew Garrett 7471caab3c1SMatthew Garrett /* Delete devices for all the GUIDs */ 748023b9565SDmitry Torokhov list_for_each_entry_safe(wblock, next, &wmi_block_list, list) { 749023b9565SDmitry Torokhov list_del(&wblock->list); 75058f6425eSColin King if (wblock->dev.class) 751c64eefd4SDmitry Torokhov device_unregister(&wblock->dev); 752023b9565SDmitry Torokhov else 753023b9565SDmitry Torokhov kfree(wblock); 754023b9565SDmitry Torokhov } 7551caab3c1SMatthew Garrett } 7561caab3c1SMatthew Garrett 757d1f9e497SCarlos Corbacho static bool guid_already_parsed(const char *guid_string) 758d1f9e497SCarlos Corbacho { 759d1f9e497SCarlos Corbacho struct wmi_block *wblock; 760d1f9e497SCarlos Corbacho 761c64eefd4SDmitry Torokhov list_for_each_entry(wblock, &wmi_block_list, list) 7628b14d7b2SThadeu Lima de Souza Cascardo if (memcmp(wblock->gblock.guid, guid_string, 16) == 0) 763d1f9e497SCarlos Corbacho return true; 764c64eefd4SDmitry Torokhov 765d1f9e497SCarlos Corbacho return false; 766d1f9e497SCarlos Corbacho } 767d1f9e497SCarlos Corbacho 7681caab3c1SMatthew Garrett /* 769b4f9fe12SLen Brown * Parse the _WDG method for the GUID data blocks 770b4f9fe12SLen Brown */ 7710a018a68SDan Carpenter static int parse_wdg(acpi_handle handle) 772b4f9fe12SLen Brown { 773b4f9fe12SLen Brown struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL}; 774b4f9fe12SLen Brown union acpi_object *obj; 77537830662SDmitry Torokhov const struct guid_block *gblock; 776b4f9fe12SLen Brown struct wmi_block *wblock; 777b4f9fe12SLen Brown acpi_status status; 778c64eefd4SDmitry Torokhov int retval; 779b4f9fe12SLen Brown u32 i, total; 780b4f9fe12SLen Brown 781b4f9fe12SLen Brown status = acpi_evaluate_object(handle, "_WDG", NULL, &out); 782b4f9fe12SLen Brown if (ACPI_FAILURE(status)) 783c64eefd4SDmitry Torokhov return -ENXIO; 784b4f9fe12SLen Brown 785b4f9fe12SLen Brown obj = (union acpi_object *) out.pointer; 7863d2c63ebSDmitry Torokhov if (!obj) 787c64eefd4SDmitry Torokhov return -ENXIO; 788b4f9fe12SLen Brown 78964ed0ab8SDmitry Torokhov if (obj->type != ACPI_TYPE_BUFFER) { 790c64eefd4SDmitry Torokhov retval = -ENXIO; 79164ed0ab8SDmitry Torokhov goto out_free_pointer; 79264ed0ab8SDmitry Torokhov } 793b4f9fe12SLen Brown 79437830662SDmitry Torokhov gblock = (const struct guid_block *)obj->buffer.pointer; 795b4f9fe12SLen Brown total = obj->buffer.length / sizeof(struct guid_block); 796b4f9fe12SLen Brown 797b4f9fe12SLen Brown for (i = 0; i < total; i++) { 798a929aae0SThomas Renninger if (debug_dump_wdg) 799a929aae0SThomas Renninger wmi_dump_wdg(&gblock[i]); 800a929aae0SThomas Renninger 80158f6425eSColin King wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL); 80258f6425eSColin King if (!wblock) 8030a018a68SDan Carpenter return -ENOMEM; 80458f6425eSColin King 80558f6425eSColin King wblock->handle = handle; 80658f6425eSColin King wblock->gblock = gblock[i]; 80758f6425eSColin King 80858f6425eSColin King /* 80958f6425eSColin King Some WMI devices, like those for nVidia hooks, have a 81058f6425eSColin King duplicate GUID. It's not clear what we should do in this 81158f6425eSColin King case yet, so for now, we'll just ignore the duplicate 81258f6425eSColin King for device creation. 81358f6425eSColin King */ 81458f6425eSColin King if (!guid_already_parsed(gblock[i].guid)) { 81558f6425eSColin King retval = wmi_create_device(&gblock[i], wblock, handle); 81658f6425eSColin King if (retval) { 817c64eefd4SDmitry Torokhov wmi_free_devices(); 818e1e0dacbSDan Carpenter goto out_free_pointer; 819a5167c5bSAxel Lin } 82058f6425eSColin King } 82158f6425eSColin King 82258f6425eSColin King list_add_tail(&wblock->list, &wmi_block_list); 823b4f9fe12SLen Brown 824fc3155b2SThomas Renninger if (debug_event) { 825fc3155b2SThomas Renninger wblock->handler = wmi_notify_debug; 8262d5ab555SDmitry Torokhov wmi_method_enable(wblock, 1); 827fc3155b2SThomas Renninger } 828b4f9fe12SLen Brown } 829b4f9fe12SLen Brown 830c64eefd4SDmitry Torokhov retval = 0; 831c64eefd4SDmitry Torokhov 832a5167c5bSAxel Lin out_free_pointer: 833a5167c5bSAxel Lin kfree(out.pointer); 834b4f9fe12SLen Brown 835c64eefd4SDmitry Torokhov return retval; 836b4f9fe12SLen Brown } 837b4f9fe12SLen Brown 838b4f9fe12SLen Brown /* 839b4f9fe12SLen Brown * WMI can have EmbeddedControl access regions. In which case, we just want to 840b4f9fe12SLen Brown * hand these off to the EC driver. 841b4f9fe12SLen Brown */ 842b4f9fe12SLen Brown static acpi_status 843b4f9fe12SLen Brown acpi_wmi_ec_space_handler(u32 function, acpi_physical_address address, 844439913ffSLin Ming u32 bits, u64 *value, 845b4f9fe12SLen Brown void *handler_context, void *region_context) 846b4f9fe12SLen Brown { 847b4f9fe12SLen Brown int result = 0, i = 0; 848b4f9fe12SLen Brown u8 temp = 0; 849b4f9fe12SLen Brown 850b4f9fe12SLen Brown if ((address > 0xFF) || !value) 851b4f9fe12SLen Brown return AE_BAD_PARAMETER; 852b4f9fe12SLen Brown 853b4f9fe12SLen Brown if (function != ACPI_READ && function != ACPI_WRITE) 854b4f9fe12SLen Brown return AE_BAD_PARAMETER; 855b4f9fe12SLen Brown 856b4f9fe12SLen Brown if (bits != 8) 857b4f9fe12SLen Brown return AE_BAD_PARAMETER; 858b4f9fe12SLen Brown 859b4f9fe12SLen Brown if (function == ACPI_READ) { 860b4f9fe12SLen Brown result = ec_read(address, &temp); 861439913ffSLin Ming (*value) |= ((u64)temp) << i; 862b4f9fe12SLen Brown } else { 863b4f9fe12SLen Brown temp = 0xff & ((*value) >> i); 864b4f9fe12SLen Brown result = ec_write(address, temp); 865b4f9fe12SLen Brown } 866b4f9fe12SLen Brown 867b4f9fe12SLen Brown switch (result) { 868b4f9fe12SLen Brown case -EINVAL: 869b4f9fe12SLen Brown return AE_BAD_PARAMETER; 870b4f9fe12SLen Brown break; 871b4f9fe12SLen Brown case -ENODEV: 872b4f9fe12SLen Brown return AE_NOT_FOUND; 873b4f9fe12SLen Brown break; 874b4f9fe12SLen Brown case -ETIME: 875b4f9fe12SLen Brown return AE_TIME; 876b4f9fe12SLen Brown break; 877b4f9fe12SLen Brown default: 878b4f9fe12SLen Brown return AE_OK; 879b4f9fe12SLen Brown } 880b4f9fe12SLen Brown } 881b4f9fe12SLen Brown 882f61bb939SBjorn Helgaas static void acpi_wmi_notify(struct acpi_device *device, u32 event) 883b4f9fe12SLen Brown { 884b4f9fe12SLen Brown struct guid_block *block; 885b4f9fe12SLen Brown struct wmi_block *wblock; 886b4f9fe12SLen Brown struct list_head *p; 8877715348cSThomas Renninger char guid_string[37]; 888b4f9fe12SLen Brown 889762e1a2fSDmitry Torokhov list_for_each(p, &wmi_block_list) { 890b4f9fe12SLen Brown wblock = list_entry(p, struct wmi_block, list); 891b4f9fe12SLen Brown block = &wblock->gblock; 892b4f9fe12SLen Brown 893b4f9fe12SLen Brown if ((block->flags & ACPI_WMI_EVENT) && 894b4f9fe12SLen Brown (block->notify_id == event)) { 895b4f9fe12SLen Brown if (wblock->handler) 896b4f9fe12SLen Brown wblock->handler(event, wblock->handler_data); 8977715348cSThomas Renninger if (debug_event) { 8987715348cSThomas Renninger wmi_gtoa(wblock->gblock.guid, guid_string); 8998e07514dSDmitry Torokhov pr_info("DEBUG Event GUID: %s\n", guid_string); 9007715348cSThomas Renninger } 901b4f9fe12SLen Brown 902b4f9fe12SLen Brown acpi_bus_generate_netlink_event( 903b4f9fe12SLen Brown device->pnp.device_class, dev_name(&device->dev), 904b4f9fe12SLen Brown event, 0); 905b4f9fe12SLen Brown break; 906b4f9fe12SLen Brown } 907b4f9fe12SLen Brown } 908b4f9fe12SLen Brown } 909b4f9fe12SLen Brown 91051fac838SRafael J. Wysocki static int acpi_wmi_remove(struct acpi_device *device) 911b4f9fe12SLen Brown { 912b4f9fe12SLen Brown acpi_remove_address_space_handler(device->handle, 913b4f9fe12SLen Brown ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler); 914c64eefd4SDmitry Torokhov wmi_free_devices(); 915b4f9fe12SLen Brown 916b4f9fe12SLen Brown return 0; 917b4f9fe12SLen Brown } 918b4f9fe12SLen Brown 919925b1089SThomas Renninger static int acpi_wmi_add(struct acpi_device *device) 920b4f9fe12SLen Brown { 921b4f9fe12SLen Brown acpi_status status; 922c64eefd4SDmitry Torokhov int error; 923b4f9fe12SLen Brown 924b4f9fe12SLen Brown status = acpi_install_address_space_handler(device->handle, 925b4f9fe12SLen Brown ACPI_ADR_SPACE_EC, 926b4f9fe12SLen Brown &acpi_wmi_ec_space_handler, 927b4f9fe12SLen Brown NULL, NULL); 9285212cd67SDmitry Torokhov if (ACPI_FAILURE(status)) { 9298e07514dSDmitry Torokhov pr_err("Error installing EC region handler\n"); 930b4f9fe12SLen Brown return -ENODEV; 9315212cd67SDmitry Torokhov } 932b4f9fe12SLen Brown 933c64eefd4SDmitry Torokhov error = parse_wdg(device->handle); 934c64eefd4SDmitry Torokhov if (error) { 9355212cd67SDmitry Torokhov acpi_remove_address_space_handler(device->handle, 9365212cd67SDmitry Torokhov ACPI_ADR_SPACE_EC, 9375212cd67SDmitry Torokhov &acpi_wmi_ec_space_handler); 9388e07514dSDmitry Torokhov pr_err("Failed to parse WDG method\n"); 939c64eefd4SDmitry Torokhov return error; 940b4f9fe12SLen Brown } 941b4f9fe12SLen Brown 942c64eefd4SDmitry Torokhov return 0; 943b4f9fe12SLen Brown } 944b4f9fe12SLen Brown 945b4f9fe12SLen Brown static int __init acpi_wmi_init(void) 946b4f9fe12SLen Brown { 947c64eefd4SDmitry Torokhov int error; 948b4f9fe12SLen Brown 949b4f9fe12SLen Brown if (acpi_disabled) 950b4f9fe12SLen Brown return -ENODEV; 951b4f9fe12SLen Brown 952c64eefd4SDmitry Torokhov error = class_register(&wmi_class); 953c64eefd4SDmitry Torokhov if (error) 954c64eefd4SDmitry Torokhov return error; 955b4f9fe12SLen Brown 956c64eefd4SDmitry Torokhov error = acpi_bus_register_driver(&acpi_wmi_driver); 957c64eefd4SDmitry Torokhov if (error) { 958c64eefd4SDmitry Torokhov pr_err("Error loading mapper\n"); 959c64eefd4SDmitry Torokhov class_unregister(&wmi_class); 960c64eefd4SDmitry Torokhov return error; 9611caab3c1SMatthew Garrett } 9621caab3c1SMatthew Garrett 9638e07514dSDmitry Torokhov pr_info("Mapper loaded\n"); 9648e07514dSDmitry Torokhov return 0; 965b4f9fe12SLen Brown } 966b4f9fe12SLen Brown 967b4f9fe12SLen Brown static void __exit acpi_wmi_exit(void) 968b4f9fe12SLen Brown { 969b4f9fe12SLen Brown acpi_bus_unregister_driver(&acpi_wmi_driver); 970c64eefd4SDmitry Torokhov class_unregister(&wmi_class); 971b4f9fe12SLen Brown 9728e07514dSDmitry Torokhov pr_info("Mapper unloaded\n"); 973b4f9fe12SLen Brown } 974b4f9fe12SLen Brown 975b4f9fe12SLen Brown subsys_initcall(acpi_wmi_init); 976b4f9fe12SLen Brown module_exit(acpi_wmi_exit); 977