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 41b4f9fe12SLen Brown ACPI_MODULE_NAME("wmi"); 42b4f9fe12SLen Brown MODULE_AUTHOR("Carlos Corbacho"); 43b4f9fe12SLen Brown MODULE_DESCRIPTION("ACPI-WMI Mapping Driver"); 44b4f9fe12SLen Brown MODULE_LICENSE("GPL"); 45b4f9fe12SLen Brown 46b4f9fe12SLen Brown #define ACPI_WMI_CLASS "wmi" 47b4f9fe12SLen Brown 48762e1a2fSDmitry Torokhov static LIST_HEAD(wmi_block_list); 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; 69c64eefd4SDmitry Torokhov struct device dev; 70b4f9fe12SLen Brown }; 71b4f9fe12SLen Brown 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 8290ab5ee9SRusty Russell static bool debug_event; 83fc3155b2SThomas Renninger module_param(debug_event, bool, 0444); 84fc3155b2SThomas Renninger MODULE_PARM_DESC(debug_event, 85fc3155b2SThomas Renninger "Log WMI Events [0/1]"); 86fc3155b2SThomas Renninger 8790ab5ee9SRusty Russell static bool debug_dump_wdg; 88a929aae0SThomas Renninger module_param(debug_dump_wdg, bool, 0444); 89a929aae0SThomas Renninger MODULE_PARM_DESC(debug_dump_wdg, 90a929aae0SThomas Renninger "Dump available WMI interfaces [0/1]"); 91a929aae0SThomas Renninger 9251fac838SRafael J. Wysocki static int acpi_wmi_remove(struct acpi_device *device); 93b4f9fe12SLen Brown static int acpi_wmi_add(struct acpi_device *device); 94f61bb939SBjorn Helgaas static void acpi_wmi_notify(struct acpi_device *device, u32 event); 95b4f9fe12SLen Brown 96b4f9fe12SLen Brown static const struct acpi_device_id wmi_device_ids[] = { 97b4f9fe12SLen Brown {"PNP0C14", 0}, 98b4f9fe12SLen Brown {"pnp0c14", 0}, 99b4f9fe12SLen Brown {"", 0}, 100b4f9fe12SLen Brown }; 101b4f9fe12SLen Brown MODULE_DEVICE_TABLE(acpi, wmi_device_ids); 102b4f9fe12SLen Brown 103b4f9fe12SLen Brown static struct acpi_driver acpi_wmi_driver = { 104b4f9fe12SLen Brown .name = "wmi", 105b4f9fe12SLen Brown .class = ACPI_WMI_CLASS, 106b4f9fe12SLen Brown .ids = wmi_device_ids, 107b4f9fe12SLen Brown .ops = { 108b4f9fe12SLen Brown .add = acpi_wmi_add, 109b4f9fe12SLen Brown .remove = acpi_wmi_remove, 110f61bb939SBjorn Helgaas .notify = acpi_wmi_notify, 111b4f9fe12SLen Brown }, 112b4f9fe12SLen Brown }; 113b4f9fe12SLen Brown 114b4f9fe12SLen Brown /* 115b4f9fe12SLen Brown * GUID parsing functions 116b4f9fe12SLen Brown */ 117b4f9fe12SLen Brown 118b4f9fe12SLen Brown /** 119b4f9fe12SLen Brown * wmi_parse_hexbyte - Convert a ASCII hex number to a byte 120b4f9fe12SLen Brown * @src: Pointer to at least 2 characters to convert. 121b4f9fe12SLen Brown * 122b4f9fe12SLen Brown * Convert a two character ASCII hex string to a number. 123b4f9fe12SLen Brown * 124b4f9fe12SLen Brown * Return: 0-255 Success, the byte was parsed correctly 125b4f9fe12SLen Brown * -1 Error, an invalid character was supplied 126b4f9fe12SLen Brown */ 127b4f9fe12SLen Brown static int wmi_parse_hexbyte(const u8 *src) 128b4f9fe12SLen Brown { 129b4f9fe12SLen Brown int h; 130392bd8b5SAndy Shevchenko int value; 131b4f9fe12SLen Brown 132b4f9fe12SLen Brown /* high part */ 133392bd8b5SAndy Shevchenko h = value = hex_to_bin(src[0]); 134392bd8b5SAndy Shevchenko if (value < 0) 135b4f9fe12SLen Brown return -1; 136b4f9fe12SLen Brown 137b4f9fe12SLen Brown /* low part */ 138392bd8b5SAndy Shevchenko value = hex_to_bin(src[1]); 139392bd8b5SAndy Shevchenko if (value >= 0) 140392bd8b5SAndy Shevchenko return (h << 4) | value; 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 207762e1a2fSDmitry Torokhov list_for_each(p, &wmi_block_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; 214097c27fcSJoe Perches return true; 215b4f9fe12SLen Brown } 216b4f9fe12SLen Brown } 217097c27fcSJoe Perches return false; 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 acpi_status status; 225b4f9fe12SLen Brown acpi_handle handle; 226b4f9fe12SLen Brown 227b4f9fe12SLen Brown block = &wblock->gblock; 228b4f9fe12SLen Brown handle = wblock->handle; 229b4f9fe12SLen Brown 230b4f9fe12SLen Brown snprintf(method, 5, "WE%02X", block->notify_id); 2318122ab66SZhang Rui status = acpi_execute_simple_method(handle, method, enable); 232b4f9fe12SLen Brown 233b4f9fe12SLen Brown if (status != AE_OK && status != AE_NOT_FOUND) 234b4f9fe12SLen Brown return status; 235b4f9fe12SLen Brown else 236b4f9fe12SLen Brown return AE_OK; 237b4f9fe12SLen Brown } 238b4f9fe12SLen Brown 239b4f9fe12SLen Brown /* 240b4f9fe12SLen Brown * Exported WMI functions 241b4f9fe12SLen Brown */ 242b4f9fe12SLen Brown /** 243b4f9fe12SLen Brown * wmi_evaluate_method - Evaluate a WMI method 244b4f9fe12SLen Brown * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba 245b4f9fe12SLen Brown * @instance: Instance index 246b4f9fe12SLen Brown * @method_id: Method ID to call 247b4f9fe12SLen Brown * &in: Buffer containing input for the method call 248b4f9fe12SLen Brown * &out: Empty buffer to return the method results 249b4f9fe12SLen Brown * 250b4f9fe12SLen Brown * Call an ACPI-WMI method 251b4f9fe12SLen Brown */ 252b4f9fe12SLen Brown acpi_status wmi_evaluate_method(const char *guid_string, u8 instance, 253b4f9fe12SLen Brown u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out) 254b4f9fe12SLen Brown { 255b4f9fe12SLen Brown struct guid_block *block = NULL; 256b4f9fe12SLen Brown struct wmi_block *wblock = NULL; 257b4f9fe12SLen Brown acpi_handle handle; 258b4f9fe12SLen Brown acpi_status status; 259b4f9fe12SLen Brown struct acpi_object_list input; 260b4f9fe12SLen Brown union acpi_object params[3]; 261f3d83e24SCostantino Leandro char method[5] = "WM"; 262b4f9fe12SLen Brown 263b4f9fe12SLen Brown if (!find_guid(guid_string, &wblock)) 264b4f9fe12SLen Brown return AE_ERROR; 265b4f9fe12SLen Brown 266b4f9fe12SLen Brown block = &wblock->gblock; 267b4f9fe12SLen Brown handle = wblock->handle; 268b4f9fe12SLen Brown 269b4f9fe12SLen Brown if (!(block->flags & ACPI_WMI_METHOD)) 270b4f9fe12SLen Brown return AE_BAD_DATA; 271b4f9fe12SLen Brown 272b4f9fe12SLen Brown if (block->instance_count < instance) 273b4f9fe12SLen Brown return AE_BAD_PARAMETER; 274b4f9fe12SLen Brown 275b4f9fe12SLen Brown input.count = 2; 276b4f9fe12SLen Brown input.pointer = params; 277b4f9fe12SLen Brown params[0].type = ACPI_TYPE_INTEGER; 278b4f9fe12SLen Brown params[0].integer.value = instance; 279b4f9fe12SLen Brown params[1].type = ACPI_TYPE_INTEGER; 280b4f9fe12SLen Brown params[1].integer.value = method_id; 281b4f9fe12SLen Brown 282b4f9fe12SLen Brown if (in) { 283b4f9fe12SLen Brown input.count = 3; 284b4f9fe12SLen Brown 285b4f9fe12SLen Brown if (block->flags & ACPI_WMI_STRING) { 286b4f9fe12SLen Brown params[2].type = ACPI_TYPE_STRING; 287b4f9fe12SLen Brown } else { 288b4f9fe12SLen Brown params[2].type = ACPI_TYPE_BUFFER; 289b4f9fe12SLen Brown } 290b4f9fe12SLen Brown params[2].buffer.length = in->length; 291b4f9fe12SLen Brown params[2].buffer.pointer = in->pointer; 292b4f9fe12SLen Brown } 293b4f9fe12SLen Brown 294b4f9fe12SLen Brown strncat(method, block->object_id, 2); 295b4f9fe12SLen Brown 296b4f9fe12SLen Brown status = acpi_evaluate_object(handle, method, &input, out); 297b4f9fe12SLen Brown 298b4f9fe12SLen Brown return status; 299b4f9fe12SLen Brown } 300b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_evaluate_method); 301b4f9fe12SLen Brown 302b4f9fe12SLen Brown /** 303b4f9fe12SLen Brown * wmi_query_block - Return contents of a WMI block 304b4f9fe12SLen Brown * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba 305b4f9fe12SLen Brown * @instance: Instance index 306b4f9fe12SLen Brown * &out: Empty buffer to return the contents of the data block to 307b4f9fe12SLen Brown * 308b4f9fe12SLen Brown * Return the contents of an ACPI-WMI data block to a buffer 309b4f9fe12SLen Brown */ 310b4f9fe12SLen Brown acpi_status wmi_query_block(const char *guid_string, u8 instance, 311b4f9fe12SLen Brown struct acpi_buffer *out) 312b4f9fe12SLen Brown { 313b4f9fe12SLen Brown struct guid_block *block = NULL; 314b4f9fe12SLen Brown struct wmi_block *wblock = NULL; 31554f14c27SZhang Rui acpi_handle handle; 316b4f9fe12SLen Brown acpi_status status, wc_status = AE_ERROR; 3178122ab66SZhang Rui struct acpi_object_list input; 3188122ab66SZhang Rui union acpi_object wq_params[1]; 319f3d83e24SCostantino Leandro char method[5]; 320f3d83e24SCostantino Leandro char wc_method[5] = "WC"; 321b4f9fe12SLen Brown 322b4f9fe12SLen Brown if (!guid_string || !out) 323b4f9fe12SLen Brown return AE_BAD_PARAMETER; 324b4f9fe12SLen Brown 325b4f9fe12SLen Brown if (!find_guid(guid_string, &wblock)) 326b4f9fe12SLen Brown return AE_ERROR; 327b4f9fe12SLen Brown 328b4f9fe12SLen Brown block = &wblock->gblock; 329b4f9fe12SLen Brown handle = wblock->handle; 330b4f9fe12SLen Brown 331b4f9fe12SLen Brown if (block->instance_count < instance) 332b4f9fe12SLen Brown return AE_BAD_PARAMETER; 333b4f9fe12SLen Brown 334b4f9fe12SLen Brown /* Check GUID is a data block */ 335b4f9fe12SLen Brown if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD)) 336b4f9fe12SLen Brown return AE_ERROR; 337b4f9fe12SLen Brown 338b4f9fe12SLen Brown input.count = 1; 339b4f9fe12SLen Brown input.pointer = wq_params; 340b4f9fe12SLen Brown wq_params[0].type = ACPI_TYPE_INTEGER; 341b4f9fe12SLen Brown wq_params[0].integer.value = instance; 342b4f9fe12SLen Brown 343b4f9fe12SLen Brown /* 344b4f9fe12SLen Brown * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method first to 345b4f9fe12SLen Brown * enable collection. 346b4f9fe12SLen Brown */ 347b4f9fe12SLen Brown if (block->flags & ACPI_WMI_EXPENSIVE) { 348b4f9fe12SLen Brown strncat(wc_method, block->object_id, 2); 349b4f9fe12SLen Brown 350b4f9fe12SLen Brown /* 351b4f9fe12SLen Brown * Some GUIDs break the specification by declaring themselves 352b4f9fe12SLen Brown * expensive, but have no corresponding WCxx method. So we 353b4f9fe12SLen Brown * should not fail if this happens. 354b4f9fe12SLen Brown */ 35554f14c27SZhang Rui if (acpi_has_method(handle, wc_method)) 3568122ab66SZhang Rui wc_status = acpi_execute_simple_method(handle, 3578122ab66SZhang Rui wc_method, 1); 358b4f9fe12SLen Brown } 359b4f9fe12SLen Brown 360b4f9fe12SLen Brown strcpy(method, "WQ"); 361b4f9fe12SLen Brown strncat(method, block->object_id, 2); 362b4f9fe12SLen Brown 363b4f9fe12SLen Brown status = acpi_evaluate_object(handle, method, &input, out); 364b4f9fe12SLen Brown 365b4f9fe12SLen Brown /* 366b4f9fe12SLen Brown * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method, even if 367b4f9fe12SLen Brown * the WQxx method failed - we should disable collection anyway. 368b4f9fe12SLen Brown */ 369b4f9fe12SLen Brown if ((block->flags & ACPI_WMI_EXPENSIVE) && ACPI_SUCCESS(wc_status)) { 3708122ab66SZhang Rui status = acpi_execute_simple_method(handle, wc_method, 0); 371b4f9fe12SLen Brown } 372b4f9fe12SLen Brown 373b4f9fe12SLen Brown return status; 374b4f9fe12SLen Brown } 375b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_query_block); 376b4f9fe12SLen Brown 377b4f9fe12SLen Brown /** 378b4f9fe12SLen Brown * wmi_set_block - Write to a WMI block 379b4f9fe12SLen Brown * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba 380b4f9fe12SLen Brown * @instance: Instance index 381b4f9fe12SLen Brown * &in: Buffer containing new values for the data block 382b4f9fe12SLen Brown * 383b4f9fe12SLen Brown * Write the contents of the input buffer to an ACPI-WMI data block 384b4f9fe12SLen Brown */ 385b4f9fe12SLen Brown acpi_status wmi_set_block(const char *guid_string, u8 instance, 386b4f9fe12SLen Brown const struct acpi_buffer *in) 387b4f9fe12SLen Brown { 388b4f9fe12SLen Brown struct guid_block *block = NULL; 389b4f9fe12SLen Brown struct wmi_block *wblock = NULL; 390b4f9fe12SLen Brown acpi_handle handle; 391b4f9fe12SLen Brown struct acpi_object_list input; 392b4f9fe12SLen Brown union acpi_object params[2]; 393f3d83e24SCostantino Leandro char method[5] = "WS"; 394b4f9fe12SLen Brown 395b4f9fe12SLen Brown if (!guid_string || !in) 396b4f9fe12SLen Brown return AE_BAD_DATA; 397b4f9fe12SLen Brown 398b4f9fe12SLen Brown if (!find_guid(guid_string, &wblock)) 399b4f9fe12SLen Brown return AE_ERROR; 400b4f9fe12SLen Brown 401b4f9fe12SLen Brown block = &wblock->gblock; 402b4f9fe12SLen Brown handle = wblock->handle; 403b4f9fe12SLen Brown 404b4f9fe12SLen Brown if (block->instance_count < instance) 405b4f9fe12SLen Brown return AE_BAD_PARAMETER; 406b4f9fe12SLen Brown 407b4f9fe12SLen Brown /* Check GUID is a data block */ 408b4f9fe12SLen Brown if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD)) 409b4f9fe12SLen Brown return AE_ERROR; 410b4f9fe12SLen Brown 411b4f9fe12SLen Brown input.count = 2; 412b4f9fe12SLen Brown input.pointer = params; 413b4f9fe12SLen Brown params[0].type = ACPI_TYPE_INTEGER; 414b4f9fe12SLen Brown params[0].integer.value = instance; 415b4f9fe12SLen Brown 416b4f9fe12SLen Brown if (block->flags & ACPI_WMI_STRING) { 417b4f9fe12SLen Brown params[1].type = ACPI_TYPE_STRING; 418b4f9fe12SLen Brown } else { 419b4f9fe12SLen Brown params[1].type = ACPI_TYPE_BUFFER; 420b4f9fe12SLen Brown } 421b4f9fe12SLen Brown params[1].buffer.length = in->length; 422b4f9fe12SLen Brown params[1].buffer.pointer = in->pointer; 423b4f9fe12SLen Brown 424b4f9fe12SLen Brown strncat(method, block->object_id, 2); 425b4f9fe12SLen Brown 426b4f9fe12SLen Brown return acpi_evaluate_object(handle, method, &input, NULL); 427b4f9fe12SLen Brown } 428b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_set_block); 429b4f9fe12SLen Brown 43037830662SDmitry Torokhov static void wmi_dump_wdg(const struct guid_block *g) 431a929aae0SThomas Renninger { 43285b4e4ebSRasmus Villemoes pr_info("%pUL:\n", g->guid); 4338e07514dSDmitry Torokhov pr_info("\tobject_id: %c%c\n", g->object_id[0], g->object_id[1]); 4348e07514dSDmitry Torokhov pr_info("\tnotify_id: %02X\n", g->notify_id); 4358e07514dSDmitry Torokhov pr_info("\treserved: %02X\n", g->reserved); 4368e07514dSDmitry Torokhov pr_info("\tinstance_count: %d\n", g->instance_count); 4378e07514dSDmitry Torokhov pr_info("\tflags: %#x", g->flags); 438a929aae0SThomas Renninger if (g->flags) { 439a929aae0SThomas Renninger if (g->flags & ACPI_WMI_EXPENSIVE) 4408e07514dSDmitry Torokhov pr_cont(" ACPI_WMI_EXPENSIVE"); 441a929aae0SThomas Renninger if (g->flags & ACPI_WMI_METHOD) 4428e07514dSDmitry Torokhov pr_cont(" ACPI_WMI_METHOD"); 443a929aae0SThomas Renninger if (g->flags & ACPI_WMI_STRING) 4448e07514dSDmitry Torokhov pr_cont(" ACPI_WMI_STRING"); 445a929aae0SThomas Renninger if (g->flags & ACPI_WMI_EVENT) 4468e07514dSDmitry Torokhov pr_cont(" ACPI_WMI_EVENT"); 447a929aae0SThomas Renninger } 4488e07514dSDmitry Torokhov pr_cont("\n"); 449a929aae0SThomas Renninger 450a929aae0SThomas Renninger } 451a929aae0SThomas Renninger 452fc3155b2SThomas Renninger static void wmi_notify_debug(u32 value, void *context) 453fc3155b2SThomas Renninger { 454fc3155b2SThomas Renninger struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; 455fc3155b2SThomas Renninger union acpi_object *obj; 4561492616aSAxel Lin acpi_status status; 457fc3155b2SThomas Renninger 4581492616aSAxel Lin status = wmi_get_event_data(value, &response); 4591492616aSAxel Lin if (status != AE_OK) { 4608e07514dSDmitry Torokhov pr_info("bad event status 0x%x\n", status); 4611492616aSAxel Lin return; 4621492616aSAxel Lin } 463fc3155b2SThomas Renninger 464fc3155b2SThomas Renninger obj = (union acpi_object *)response.pointer; 465fc3155b2SThomas Renninger 466fc3155b2SThomas Renninger if (!obj) 467fc3155b2SThomas Renninger return; 468fc3155b2SThomas Renninger 4698e07514dSDmitry Torokhov pr_info("DEBUG Event "); 470fc3155b2SThomas Renninger switch(obj->type) { 471fc3155b2SThomas Renninger case ACPI_TYPE_BUFFER: 4728e07514dSDmitry Torokhov pr_cont("BUFFER_TYPE - length %d\n", obj->buffer.length); 473fc3155b2SThomas Renninger break; 474fc3155b2SThomas Renninger case ACPI_TYPE_STRING: 4758e07514dSDmitry Torokhov pr_cont("STRING_TYPE - %s\n", obj->string.pointer); 476fc3155b2SThomas Renninger break; 477fc3155b2SThomas Renninger case ACPI_TYPE_INTEGER: 4788e07514dSDmitry Torokhov pr_cont("INTEGER_TYPE - %llu\n", obj->integer.value); 479fc3155b2SThomas Renninger break; 480fc3155b2SThomas Renninger case ACPI_TYPE_PACKAGE: 4818e07514dSDmitry Torokhov pr_cont("PACKAGE_TYPE - %d elements\n", obj->package.count); 482fc3155b2SThomas Renninger break; 483fc3155b2SThomas Renninger default: 4848e07514dSDmitry Torokhov pr_cont("object type 0x%X\n", obj->type); 485fc3155b2SThomas Renninger } 4861492616aSAxel Lin kfree(obj); 487fc3155b2SThomas Renninger } 488fc3155b2SThomas Renninger 489b4f9fe12SLen Brown /** 490b4f9fe12SLen Brown * wmi_install_notify_handler - Register handler for WMI events 491b4f9fe12SLen Brown * @handler: Function to handle notifications 492b4f9fe12SLen Brown * @data: Data to be returned to handler when event is fired 493b4f9fe12SLen Brown * 494b4f9fe12SLen Brown * Register a handler for events sent to the ACPI-WMI mapper device. 495b4f9fe12SLen Brown */ 496b4f9fe12SLen Brown acpi_status wmi_install_notify_handler(const char *guid, 497b4f9fe12SLen Brown wmi_notify_handler handler, void *data) 498b4f9fe12SLen Brown { 499b4f9fe12SLen Brown struct wmi_block *block; 50058f6425eSColin King acpi_status status = AE_NOT_EXIST; 50158f6425eSColin King char tmp[16], guid_input[16]; 50258f6425eSColin King struct list_head *p; 503b4f9fe12SLen Brown 504b4f9fe12SLen Brown if (!guid || !handler) 505b4f9fe12SLen Brown return AE_BAD_PARAMETER; 506b4f9fe12SLen Brown 50758f6425eSColin King wmi_parse_guid(guid, tmp); 50858f6425eSColin King wmi_swap_bytes(tmp, guid_input); 509b4f9fe12SLen Brown 51058f6425eSColin King list_for_each(p, &wmi_block_list) { 51158f6425eSColin King acpi_status wmi_status; 51258f6425eSColin King block = list_entry(p, struct wmi_block, list); 51358f6425eSColin King 51458f6425eSColin King if (memcmp(block->gblock.guid, guid_input, 16) == 0) { 51558f6425eSColin King if (block->handler && 51658f6425eSColin King block->handler != wmi_notify_debug) 517b4f9fe12SLen Brown return AE_ALREADY_ACQUIRED; 518b4f9fe12SLen Brown 519b4f9fe12SLen Brown block->handler = handler; 520b4f9fe12SLen Brown block->handler_data = data; 521b4f9fe12SLen Brown 52258f6425eSColin King wmi_status = wmi_method_enable(block, 1); 52358f6425eSColin King if ((wmi_status != AE_OK) || 52458f6425eSColin King ((wmi_status == AE_OK) && (status == AE_NOT_EXIST))) 52558f6425eSColin King status = wmi_status; 52658f6425eSColin King } 52758f6425eSColin King } 528b4f9fe12SLen Brown 529b4f9fe12SLen Brown return status; 530b4f9fe12SLen Brown } 531b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_install_notify_handler); 532b4f9fe12SLen Brown 533b4f9fe12SLen Brown /** 534b4f9fe12SLen Brown * wmi_uninstall_notify_handler - Unregister handler for WMI events 535b4f9fe12SLen Brown * 536b4f9fe12SLen Brown * Unregister handler for events sent to the ACPI-WMI mapper device. 537b4f9fe12SLen Brown */ 538b4f9fe12SLen Brown acpi_status wmi_remove_notify_handler(const char *guid) 539b4f9fe12SLen Brown { 540b4f9fe12SLen Brown struct wmi_block *block; 54158f6425eSColin King acpi_status status = AE_NOT_EXIST; 54258f6425eSColin King char tmp[16], guid_input[16]; 54358f6425eSColin King struct list_head *p; 544b4f9fe12SLen Brown 545b4f9fe12SLen Brown if (!guid) 546b4f9fe12SLen Brown return AE_BAD_PARAMETER; 547b4f9fe12SLen Brown 54858f6425eSColin King wmi_parse_guid(guid, tmp); 54958f6425eSColin King wmi_swap_bytes(tmp, guid_input); 550b4f9fe12SLen Brown 55158f6425eSColin King list_for_each(p, &wmi_block_list) { 55258f6425eSColin King acpi_status wmi_status; 55358f6425eSColin King block = list_entry(p, struct wmi_block, list); 55458f6425eSColin King 55558f6425eSColin King if (memcmp(block->gblock.guid, guid_input, 16) == 0) { 55658f6425eSColin King if (!block->handler || 55758f6425eSColin King block->handler == wmi_notify_debug) 558b4f9fe12SLen Brown return AE_NULL_ENTRY; 559b4f9fe12SLen Brown 560fc3155b2SThomas Renninger if (debug_event) { 561fc3155b2SThomas Renninger block->handler = wmi_notify_debug; 56258f6425eSColin King status = AE_OK; 563fc3155b2SThomas Renninger } else { 56458f6425eSColin King wmi_status = wmi_method_enable(block, 0); 565b4f9fe12SLen Brown block->handler = NULL; 566b4f9fe12SLen Brown block->handler_data = NULL; 56758f6425eSColin King if ((wmi_status != AE_OK) || 56858f6425eSColin King ((wmi_status == AE_OK) && 56958f6425eSColin King (status == AE_NOT_EXIST))) 57058f6425eSColin King status = wmi_status; 571fc3155b2SThomas Renninger } 57258f6425eSColin King } 57358f6425eSColin King } 57458f6425eSColin King 575b4f9fe12SLen Brown return status; 576b4f9fe12SLen Brown } 577b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_remove_notify_handler); 578b4f9fe12SLen Brown 579b4f9fe12SLen Brown /** 580b4f9fe12SLen Brown * wmi_get_event_data - Get WMI data associated with an event 581b4f9fe12SLen Brown * 5823e9b988eSAnisse Astier * @event: Event to find 5833e9b988eSAnisse Astier * @out: Buffer to hold event data. out->pointer should be freed with kfree() 584b4f9fe12SLen Brown * 585b4f9fe12SLen Brown * Returns extra data associated with an event in WMI. 586b4f9fe12SLen Brown */ 587b4f9fe12SLen Brown acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out) 588b4f9fe12SLen Brown { 589b4f9fe12SLen Brown struct acpi_object_list input; 590b4f9fe12SLen Brown union acpi_object params[1]; 591b4f9fe12SLen Brown struct guid_block *gblock; 592b4f9fe12SLen Brown struct wmi_block *wblock; 593b4f9fe12SLen Brown struct list_head *p; 594b4f9fe12SLen Brown 595b4f9fe12SLen Brown input.count = 1; 596b4f9fe12SLen Brown input.pointer = params; 597b4f9fe12SLen Brown params[0].type = ACPI_TYPE_INTEGER; 598b4f9fe12SLen Brown params[0].integer.value = event; 599b4f9fe12SLen Brown 600762e1a2fSDmitry Torokhov list_for_each(p, &wmi_block_list) { 601b4f9fe12SLen Brown wblock = list_entry(p, struct wmi_block, list); 602b4f9fe12SLen Brown gblock = &wblock->gblock; 603b4f9fe12SLen Brown 604b4f9fe12SLen Brown if ((gblock->flags & ACPI_WMI_EVENT) && 605b4f9fe12SLen Brown (gblock->notify_id == event)) 606b4f9fe12SLen Brown return acpi_evaluate_object(wblock->handle, "_WED", 607b4f9fe12SLen Brown &input, out); 608b4f9fe12SLen Brown } 609b4f9fe12SLen Brown 610b4f9fe12SLen Brown return AE_NOT_FOUND; 611b4f9fe12SLen Brown } 612b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_get_event_data); 613b4f9fe12SLen Brown 614b4f9fe12SLen Brown /** 615b4f9fe12SLen Brown * wmi_has_guid - Check if a GUID is available 616b4f9fe12SLen Brown * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba 617b4f9fe12SLen Brown * 618b4f9fe12SLen Brown * Check if a given GUID is defined by _WDG 619b4f9fe12SLen Brown */ 620b4f9fe12SLen Brown bool wmi_has_guid(const char *guid_string) 621b4f9fe12SLen Brown { 622b4f9fe12SLen Brown return find_guid(guid_string, NULL); 623b4f9fe12SLen Brown } 624b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_has_guid); 625b4f9fe12SLen Brown 626b4f9fe12SLen Brown /* 6271caab3c1SMatthew Garrett * sysfs interface 6281caab3c1SMatthew Garrett */ 629614ef432SDmitry Torokhov static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, 6301caab3c1SMatthew Garrett char *buf) 6311caab3c1SMatthew Garrett { 6321caab3c1SMatthew Garrett struct wmi_block *wblock; 6331caab3c1SMatthew Garrett 6341caab3c1SMatthew Garrett wblock = dev_get_drvdata(dev); 635a80e1053SPrarit Bhargava if (!wblock) { 636a80e1053SPrarit Bhargava strcat(buf, "\n"); 637a80e1053SPrarit Bhargava return strlen(buf); 638a80e1053SPrarit Bhargava } 6391caab3c1SMatthew Garrett 64085b4e4ebSRasmus Villemoes return sprintf(buf, "wmi:%pUL\n", wblock->gblock.guid); 6411caab3c1SMatthew Garrett } 642e80b89a5SGreg Kroah-Hartman static DEVICE_ATTR_RO(modalias); 643614ef432SDmitry Torokhov 644e80b89a5SGreg Kroah-Hartman static struct attribute *wmi_attrs[] = { 645e80b89a5SGreg Kroah-Hartman &dev_attr_modalias.attr, 646e80b89a5SGreg Kroah-Hartman NULL, 647614ef432SDmitry Torokhov }; 648e80b89a5SGreg Kroah-Hartman ATTRIBUTE_GROUPS(wmi); 6491caab3c1SMatthew Garrett 6501caab3c1SMatthew Garrett static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env) 6511caab3c1SMatthew Garrett { 6521caab3c1SMatthew Garrett char guid_string[37]; 6531caab3c1SMatthew Garrett 6541caab3c1SMatthew Garrett struct wmi_block *wblock; 6551caab3c1SMatthew Garrett 6561caab3c1SMatthew Garrett if (add_uevent_var(env, "MODALIAS=")) 6571caab3c1SMatthew Garrett return -ENOMEM; 6581caab3c1SMatthew Garrett 6591caab3c1SMatthew Garrett wblock = dev_get_drvdata(dev); 6601caab3c1SMatthew Garrett if (!wblock) 6611caab3c1SMatthew Garrett return -ENOMEM; 6621caab3c1SMatthew Garrett 66385b4e4ebSRasmus Villemoes sprintf(guid_string, "%pUL", wblock->gblock.guid); 6641caab3c1SMatthew Garrett 6651caab3c1SMatthew Garrett strcpy(&env->buf[env->buflen - 1], "wmi:"); 6661caab3c1SMatthew Garrett memcpy(&env->buf[env->buflen - 1 + 4], guid_string, 36); 6671caab3c1SMatthew Garrett env->buflen += 40; 6681caab3c1SMatthew Garrett 6691caab3c1SMatthew Garrett return 0; 6701caab3c1SMatthew Garrett } 6711caab3c1SMatthew Garrett 6721caab3c1SMatthew Garrett static void wmi_dev_free(struct device *dev) 6731caab3c1SMatthew Garrett { 674c64eefd4SDmitry Torokhov struct wmi_block *wmi_block = container_of(dev, struct wmi_block, dev); 675c64eefd4SDmitry Torokhov 676c64eefd4SDmitry Torokhov kfree(wmi_block); 6771caab3c1SMatthew Garrett } 6781caab3c1SMatthew Garrett 6791caab3c1SMatthew Garrett static struct class wmi_class = { 6801caab3c1SMatthew Garrett .name = "wmi", 6811caab3c1SMatthew Garrett .dev_release = wmi_dev_free, 6821caab3c1SMatthew Garrett .dev_uevent = wmi_dev_uevent, 683e80b89a5SGreg Kroah-Hartman .dev_groups = wmi_groups, 6841caab3c1SMatthew Garrett }; 6851caab3c1SMatthew Garrett 68658f6425eSColin King static int wmi_create_device(const struct guid_block *gblock, 68758f6425eSColin King struct wmi_block *wblock, acpi_handle handle) 6881caab3c1SMatthew Garrett { 689c64eefd4SDmitry Torokhov wblock->dev.class = &wmi_class; 6901caab3c1SMatthew Garrett 69185b4e4ebSRasmus Villemoes dev_set_name(&wblock->dev, "%pUL", gblock->guid); 6921caab3c1SMatthew Garrett 693c64eefd4SDmitry Torokhov dev_set_drvdata(&wblock->dev, wblock); 694c64eefd4SDmitry Torokhov 69558f6425eSColin King return device_register(&wblock->dev); 6961caab3c1SMatthew Garrett } 6971caab3c1SMatthew Garrett 698c64eefd4SDmitry Torokhov static void wmi_free_devices(void) 6991caab3c1SMatthew Garrett { 700c64eefd4SDmitry Torokhov struct wmi_block *wblock, *next; 7011caab3c1SMatthew Garrett 7021caab3c1SMatthew Garrett /* Delete devices for all the GUIDs */ 703023b9565SDmitry Torokhov list_for_each_entry_safe(wblock, next, &wmi_block_list, list) { 704023b9565SDmitry Torokhov list_del(&wblock->list); 70558f6425eSColin King if (wblock->dev.class) 706c64eefd4SDmitry Torokhov device_unregister(&wblock->dev); 707023b9565SDmitry Torokhov else 708023b9565SDmitry Torokhov kfree(wblock); 709023b9565SDmitry Torokhov } 7101caab3c1SMatthew Garrett } 7111caab3c1SMatthew Garrett 712d1f9e497SCarlos Corbacho static bool guid_already_parsed(const char *guid_string) 713d1f9e497SCarlos Corbacho { 714d1f9e497SCarlos Corbacho struct wmi_block *wblock; 715d1f9e497SCarlos Corbacho 716c64eefd4SDmitry Torokhov list_for_each_entry(wblock, &wmi_block_list, list) 7178b14d7b2SThadeu Lima de Souza Cascardo if (memcmp(wblock->gblock.guid, guid_string, 16) == 0) 718d1f9e497SCarlos Corbacho return true; 719c64eefd4SDmitry Torokhov 720d1f9e497SCarlos Corbacho return false; 721d1f9e497SCarlos Corbacho } 722d1f9e497SCarlos Corbacho 7231caab3c1SMatthew Garrett /* 724b4f9fe12SLen Brown * Parse the _WDG method for the GUID data blocks 725b4f9fe12SLen Brown */ 7260a018a68SDan Carpenter static int parse_wdg(acpi_handle handle) 727b4f9fe12SLen Brown { 728b4f9fe12SLen Brown struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL}; 729b4f9fe12SLen Brown union acpi_object *obj; 73037830662SDmitry Torokhov const struct guid_block *gblock; 731b4f9fe12SLen Brown struct wmi_block *wblock; 732b4f9fe12SLen Brown acpi_status status; 733c64eefd4SDmitry Torokhov int retval; 734b4f9fe12SLen Brown u32 i, total; 735b4f9fe12SLen Brown 736b4f9fe12SLen Brown status = acpi_evaluate_object(handle, "_WDG", NULL, &out); 737b4f9fe12SLen Brown if (ACPI_FAILURE(status)) 738c64eefd4SDmitry Torokhov return -ENXIO; 739b4f9fe12SLen Brown 740b4f9fe12SLen Brown obj = (union acpi_object *) out.pointer; 7413d2c63ebSDmitry Torokhov if (!obj) 742c64eefd4SDmitry Torokhov return -ENXIO; 743b4f9fe12SLen Brown 74464ed0ab8SDmitry Torokhov if (obj->type != ACPI_TYPE_BUFFER) { 745c64eefd4SDmitry Torokhov retval = -ENXIO; 74664ed0ab8SDmitry Torokhov goto out_free_pointer; 74764ed0ab8SDmitry Torokhov } 748b4f9fe12SLen Brown 74937830662SDmitry Torokhov gblock = (const struct guid_block *)obj->buffer.pointer; 750b4f9fe12SLen Brown total = obj->buffer.length / sizeof(struct guid_block); 751b4f9fe12SLen Brown 752b4f9fe12SLen Brown for (i = 0; i < total; i++) { 753a929aae0SThomas Renninger if (debug_dump_wdg) 754a929aae0SThomas Renninger wmi_dump_wdg(&gblock[i]); 755a929aae0SThomas Renninger 75658f6425eSColin King wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL); 75758f6425eSColin King if (!wblock) 7580a018a68SDan Carpenter return -ENOMEM; 75958f6425eSColin King 76058f6425eSColin King wblock->handle = handle; 76158f6425eSColin King wblock->gblock = gblock[i]; 76258f6425eSColin King 76358f6425eSColin King /* 76458f6425eSColin King Some WMI devices, like those for nVidia hooks, have a 76558f6425eSColin King duplicate GUID. It's not clear what we should do in this 76658f6425eSColin King case yet, so for now, we'll just ignore the duplicate 76758f6425eSColin King for device creation. 76858f6425eSColin King */ 76958f6425eSColin King if (!guid_already_parsed(gblock[i].guid)) { 77058f6425eSColin King retval = wmi_create_device(&gblock[i], wblock, handle); 77158f6425eSColin King if (retval) { 772c64eefd4SDmitry Torokhov wmi_free_devices(); 773e1e0dacbSDan Carpenter goto out_free_pointer; 774a5167c5bSAxel Lin } 77558f6425eSColin King } 77658f6425eSColin King 77758f6425eSColin King list_add_tail(&wblock->list, &wmi_block_list); 778b4f9fe12SLen Brown 779fc3155b2SThomas Renninger if (debug_event) { 780fc3155b2SThomas Renninger wblock->handler = wmi_notify_debug; 7812d5ab555SDmitry Torokhov wmi_method_enable(wblock, 1); 782fc3155b2SThomas Renninger } 783b4f9fe12SLen Brown } 784b4f9fe12SLen Brown 785c64eefd4SDmitry Torokhov retval = 0; 786c64eefd4SDmitry Torokhov 787a5167c5bSAxel Lin out_free_pointer: 788a5167c5bSAxel Lin kfree(out.pointer); 789b4f9fe12SLen Brown 790c64eefd4SDmitry Torokhov return retval; 791b4f9fe12SLen Brown } 792b4f9fe12SLen Brown 793b4f9fe12SLen Brown /* 794b4f9fe12SLen Brown * WMI can have EmbeddedControl access regions. In which case, we just want to 795b4f9fe12SLen Brown * hand these off to the EC driver. 796b4f9fe12SLen Brown */ 797b4f9fe12SLen Brown static acpi_status 798b4f9fe12SLen Brown acpi_wmi_ec_space_handler(u32 function, acpi_physical_address address, 799439913ffSLin Ming u32 bits, u64 *value, 800b4f9fe12SLen Brown void *handler_context, void *region_context) 801b4f9fe12SLen Brown { 802b4f9fe12SLen Brown int result = 0, i = 0; 803b4f9fe12SLen Brown u8 temp = 0; 804b4f9fe12SLen Brown 805b4f9fe12SLen Brown if ((address > 0xFF) || !value) 806b4f9fe12SLen Brown return AE_BAD_PARAMETER; 807b4f9fe12SLen Brown 808b4f9fe12SLen Brown if (function != ACPI_READ && function != ACPI_WRITE) 809b4f9fe12SLen Brown return AE_BAD_PARAMETER; 810b4f9fe12SLen Brown 811b4f9fe12SLen Brown if (bits != 8) 812b4f9fe12SLen Brown return AE_BAD_PARAMETER; 813b4f9fe12SLen Brown 814b4f9fe12SLen Brown if (function == ACPI_READ) { 815b4f9fe12SLen Brown result = ec_read(address, &temp); 816439913ffSLin Ming (*value) |= ((u64)temp) << i; 817b4f9fe12SLen Brown } else { 818b4f9fe12SLen Brown temp = 0xff & ((*value) >> i); 819b4f9fe12SLen Brown result = ec_write(address, temp); 820b4f9fe12SLen Brown } 821b4f9fe12SLen Brown 822b4f9fe12SLen Brown switch (result) { 823b4f9fe12SLen Brown case -EINVAL: 824b4f9fe12SLen Brown return AE_BAD_PARAMETER; 825b4f9fe12SLen Brown break; 826b4f9fe12SLen Brown case -ENODEV: 827b4f9fe12SLen Brown return AE_NOT_FOUND; 828b4f9fe12SLen Brown break; 829b4f9fe12SLen Brown case -ETIME: 830b4f9fe12SLen Brown return AE_TIME; 831b4f9fe12SLen Brown break; 832b4f9fe12SLen Brown default: 833b4f9fe12SLen Brown return AE_OK; 834b4f9fe12SLen Brown } 835b4f9fe12SLen Brown } 836b4f9fe12SLen Brown 837f61bb939SBjorn Helgaas static void acpi_wmi_notify(struct acpi_device *device, u32 event) 838b4f9fe12SLen Brown { 839b4f9fe12SLen Brown struct guid_block *block; 840b4f9fe12SLen Brown struct wmi_block *wblock; 841b4f9fe12SLen Brown struct list_head *p; 842b4f9fe12SLen Brown 843762e1a2fSDmitry Torokhov list_for_each(p, &wmi_block_list) { 844b4f9fe12SLen Brown wblock = list_entry(p, struct wmi_block, list); 845b4f9fe12SLen Brown block = &wblock->gblock; 846b4f9fe12SLen Brown 847b4f9fe12SLen Brown if ((block->flags & ACPI_WMI_EVENT) && 848b4f9fe12SLen Brown (block->notify_id == event)) { 849b4f9fe12SLen Brown if (wblock->handler) 850b4f9fe12SLen Brown wblock->handler(event, wblock->handler_data); 8517715348cSThomas Renninger if (debug_event) { 85285b4e4ebSRasmus Villemoes pr_info("DEBUG Event GUID: %pUL\n", 85385b4e4ebSRasmus Villemoes wblock->gblock.guid); 8547715348cSThomas Renninger } 855b4f9fe12SLen Brown 856b4f9fe12SLen Brown acpi_bus_generate_netlink_event( 857b4f9fe12SLen Brown device->pnp.device_class, dev_name(&device->dev), 858b4f9fe12SLen Brown event, 0); 859b4f9fe12SLen Brown break; 860b4f9fe12SLen Brown } 861b4f9fe12SLen Brown } 862b4f9fe12SLen Brown } 863b4f9fe12SLen Brown 86451fac838SRafael J. Wysocki static int acpi_wmi_remove(struct acpi_device *device) 865b4f9fe12SLen Brown { 866b4f9fe12SLen Brown acpi_remove_address_space_handler(device->handle, 867b4f9fe12SLen Brown ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler); 868c64eefd4SDmitry Torokhov wmi_free_devices(); 869b4f9fe12SLen Brown 870b4f9fe12SLen Brown return 0; 871b4f9fe12SLen Brown } 872b4f9fe12SLen Brown 873925b1089SThomas Renninger static int acpi_wmi_add(struct acpi_device *device) 874b4f9fe12SLen Brown { 875b4f9fe12SLen Brown acpi_status status; 876c64eefd4SDmitry Torokhov int error; 877b4f9fe12SLen Brown 878b4f9fe12SLen Brown status = acpi_install_address_space_handler(device->handle, 879b4f9fe12SLen Brown ACPI_ADR_SPACE_EC, 880b4f9fe12SLen Brown &acpi_wmi_ec_space_handler, 881b4f9fe12SLen Brown NULL, NULL); 8825212cd67SDmitry Torokhov if (ACPI_FAILURE(status)) { 8838e07514dSDmitry Torokhov pr_err("Error installing EC region handler\n"); 884b4f9fe12SLen Brown return -ENODEV; 8855212cd67SDmitry Torokhov } 886b4f9fe12SLen Brown 887c64eefd4SDmitry Torokhov error = parse_wdg(device->handle); 888c64eefd4SDmitry Torokhov if (error) { 8895212cd67SDmitry Torokhov acpi_remove_address_space_handler(device->handle, 8905212cd67SDmitry Torokhov ACPI_ADR_SPACE_EC, 8915212cd67SDmitry Torokhov &acpi_wmi_ec_space_handler); 8928e07514dSDmitry Torokhov pr_err("Failed to parse WDG method\n"); 893c64eefd4SDmitry Torokhov return error; 894b4f9fe12SLen Brown } 895b4f9fe12SLen Brown 896c64eefd4SDmitry Torokhov return 0; 897b4f9fe12SLen Brown } 898b4f9fe12SLen Brown 899b4f9fe12SLen Brown static int __init acpi_wmi_init(void) 900b4f9fe12SLen Brown { 901c64eefd4SDmitry Torokhov int error; 902b4f9fe12SLen Brown 903b4f9fe12SLen Brown if (acpi_disabled) 904b4f9fe12SLen Brown return -ENODEV; 905b4f9fe12SLen Brown 906c64eefd4SDmitry Torokhov error = class_register(&wmi_class); 907c64eefd4SDmitry Torokhov if (error) 908c64eefd4SDmitry Torokhov return error; 909b4f9fe12SLen Brown 910c64eefd4SDmitry Torokhov error = acpi_bus_register_driver(&acpi_wmi_driver); 911c64eefd4SDmitry Torokhov if (error) { 912c64eefd4SDmitry Torokhov pr_err("Error loading mapper\n"); 913c64eefd4SDmitry Torokhov class_unregister(&wmi_class); 914c64eefd4SDmitry Torokhov return error; 9151caab3c1SMatthew Garrett } 9161caab3c1SMatthew Garrett 9178e07514dSDmitry Torokhov pr_info("Mapper loaded\n"); 9188e07514dSDmitry Torokhov return 0; 919b4f9fe12SLen Brown } 920b4f9fe12SLen Brown 921b4f9fe12SLen Brown static void __exit acpi_wmi_exit(void) 922b4f9fe12SLen Brown { 923b4f9fe12SLen Brown acpi_bus_unregister_driver(&acpi_wmi_driver); 924c64eefd4SDmitry Torokhov class_unregister(&wmi_class); 925b4f9fe12SLen Brown 9268e07514dSDmitry Torokhov pr_info("Mapper unloaded\n"); 927b4f9fe12SLen Brown } 928b4f9fe12SLen Brown 929b4f9fe12SLen Brown subsys_initcall(acpi_wmi_init); 930b4f9fe12SLen Brown module_exit(acpi_wmi_exit); 931