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> 40844af950SAndy Lutomirski #include <linux/wmi.h> 41538d7eb8SAndy Shevchenko #include <linux/uuid.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 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 { 64844af950SAndy Lutomirski struct wmi_device dev; 65b4f9fe12SLen Brown struct list_head list; 66b4f9fe12SLen Brown struct guid_block gblock; 67b0e86302SAndy Lutomirski struct acpi_device *acpi_device; 68b4f9fe12SLen Brown wmi_notify_handler handler; 69b4f9fe12SLen Brown void *handler_data; 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 = { 104844af950SAndy Lutomirski .name = "acpi-wmi", 105844af950SAndy Lutomirski .owner = THIS_MODULE, 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 static bool find_guid(const char *guid_string, struct wmi_block **out) 119b4f9fe12SLen Brown { 120538d7eb8SAndy Shevchenko uuid_le guid_input; 121b4f9fe12SLen Brown struct wmi_block *wblock; 122b4f9fe12SLen Brown struct guid_block *block; 123b4f9fe12SLen Brown struct list_head *p; 124b4f9fe12SLen Brown 125538d7eb8SAndy Shevchenko if (uuid_le_to_bin(guid_string, &guid_input)) 126538d7eb8SAndy Shevchenko return false; 127b4f9fe12SLen Brown 128762e1a2fSDmitry Torokhov list_for_each(p, &wmi_block_list) { 129b4f9fe12SLen Brown wblock = list_entry(p, struct wmi_block, list); 130b4f9fe12SLen Brown block = &wblock->gblock; 131b4f9fe12SLen Brown 132538d7eb8SAndy Shevchenko if (memcmp(block->guid, &guid_input, 16) == 0) { 133b4f9fe12SLen Brown if (out) 134b4f9fe12SLen Brown *out = wblock; 135097c27fcSJoe Perches return true; 136b4f9fe12SLen Brown } 137b4f9fe12SLen Brown } 138097c27fcSJoe Perches return false; 139b4f9fe12SLen Brown } 140b4f9fe12SLen Brown 141b4f9fe12SLen Brown static acpi_status wmi_method_enable(struct wmi_block *wblock, int enable) 142b4f9fe12SLen Brown { 143b4f9fe12SLen Brown struct guid_block *block = NULL; 144b4f9fe12SLen Brown char method[5]; 145b4f9fe12SLen Brown acpi_status status; 146b4f9fe12SLen Brown acpi_handle handle; 147b4f9fe12SLen Brown 148b4f9fe12SLen Brown block = &wblock->gblock; 149b0e86302SAndy Lutomirski handle = wblock->acpi_device->handle; 150b4f9fe12SLen Brown 151b4f9fe12SLen Brown snprintf(method, 5, "WE%02X", block->notify_id); 1528122ab66SZhang Rui status = acpi_execute_simple_method(handle, method, enable); 153b4f9fe12SLen Brown 154b4f9fe12SLen Brown if (status != AE_OK && status != AE_NOT_FOUND) 155b4f9fe12SLen Brown return status; 156b4f9fe12SLen Brown else 157b4f9fe12SLen Brown return AE_OK; 158b4f9fe12SLen Brown } 159b4f9fe12SLen Brown 160b4f9fe12SLen Brown /* 161b4f9fe12SLen Brown * Exported WMI functions 162b4f9fe12SLen Brown */ 163b4f9fe12SLen Brown /** 164b4f9fe12SLen Brown * wmi_evaluate_method - Evaluate a WMI method 165b4f9fe12SLen Brown * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba 166b4f9fe12SLen Brown * @instance: Instance index 167b4f9fe12SLen Brown * @method_id: Method ID to call 168b4f9fe12SLen Brown * &in: Buffer containing input for the method call 169b4f9fe12SLen Brown * &out: Empty buffer to return the method results 170b4f9fe12SLen Brown * 171b4f9fe12SLen Brown * Call an ACPI-WMI method 172b4f9fe12SLen Brown */ 173b4f9fe12SLen Brown acpi_status wmi_evaluate_method(const char *guid_string, u8 instance, 174b4f9fe12SLen Brown u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out) 175b4f9fe12SLen Brown { 176b4f9fe12SLen Brown struct guid_block *block = NULL; 177b4f9fe12SLen Brown struct wmi_block *wblock = NULL; 178b4f9fe12SLen Brown acpi_handle handle; 179b4f9fe12SLen Brown acpi_status status; 180b4f9fe12SLen Brown struct acpi_object_list input; 181b4f9fe12SLen Brown union acpi_object params[3]; 182f3d83e24SCostantino Leandro char method[5] = "WM"; 183b4f9fe12SLen Brown 184b4f9fe12SLen Brown if (!find_guid(guid_string, &wblock)) 185b4f9fe12SLen Brown return AE_ERROR; 186b4f9fe12SLen Brown 187b4f9fe12SLen Brown block = &wblock->gblock; 188b0e86302SAndy Lutomirski handle = wblock->acpi_device->handle; 189b4f9fe12SLen Brown 190b4f9fe12SLen Brown if (!(block->flags & ACPI_WMI_METHOD)) 191b4f9fe12SLen Brown return AE_BAD_DATA; 192b4f9fe12SLen Brown 193b4f9fe12SLen Brown if (block->instance_count < instance) 194b4f9fe12SLen Brown return AE_BAD_PARAMETER; 195b4f9fe12SLen Brown 196b4f9fe12SLen Brown input.count = 2; 197b4f9fe12SLen Brown input.pointer = params; 198b4f9fe12SLen Brown params[0].type = ACPI_TYPE_INTEGER; 199b4f9fe12SLen Brown params[0].integer.value = instance; 200b4f9fe12SLen Brown params[1].type = ACPI_TYPE_INTEGER; 201b4f9fe12SLen Brown params[1].integer.value = method_id; 202b4f9fe12SLen Brown 203b4f9fe12SLen Brown if (in) { 204b4f9fe12SLen Brown input.count = 3; 205b4f9fe12SLen Brown 206b4f9fe12SLen Brown if (block->flags & ACPI_WMI_STRING) { 207b4f9fe12SLen Brown params[2].type = ACPI_TYPE_STRING; 208b4f9fe12SLen Brown } else { 209b4f9fe12SLen Brown params[2].type = ACPI_TYPE_BUFFER; 210b4f9fe12SLen Brown } 211b4f9fe12SLen Brown params[2].buffer.length = in->length; 212b4f9fe12SLen Brown params[2].buffer.pointer = in->pointer; 213b4f9fe12SLen Brown } 214b4f9fe12SLen Brown 215b4f9fe12SLen Brown strncat(method, block->object_id, 2); 216b4f9fe12SLen Brown 217b4f9fe12SLen Brown status = acpi_evaluate_object(handle, method, &input, out); 218b4f9fe12SLen Brown 219b4f9fe12SLen Brown return status; 220b4f9fe12SLen Brown } 221b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_evaluate_method); 222b4f9fe12SLen Brown 223b4f9fe12SLen Brown /** 224b4f9fe12SLen Brown * wmi_query_block - Return contents of a WMI block 225b4f9fe12SLen Brown * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba 226b4f9fe12SLen Brown * @instance: Instance index 227b4f9fe12SLen Brown * &out: Empty buffer to return the contents of the data block to 228b4f9fe12SLen Brown * 229b4f9fe12SLen Brown * Return the contents of an ACPI-WMI data block to a buffer 230b4f9fe12SLen Brown */ 231b4f9fe12SLen Brown acpi_status wmi_query_block(const char *guid_string, u8 instance, 232b4f9fe12SLen Brown struct acpi_buffer *out) 233b4f9fe12SLen Brown { 234b4f9fe12SLen Brown struct guid_block *block = NULL; 235b4f9fe12SLen Brown struct wmi_block *wblock = NULL; 23654f14c27SZhang Rui acpi_handle handle; 237b4f9fe12SLen Brown acpi_status status, wc_status = AE_ERROR; 2388122ab66SZhang Rui struct acpi_object_list input; 2398122ab66SZhang Rui union acpi_object wq_params[1]; 240f3d83e24SCostantino Leandro char method[5]; 241f3d83e24SCostantino Leandro char wc_method[5] = "WC"; 242b4f9fe12SLen Brown 243b4f9fe12SLen Brown if (!guid_string || !out) 244b4f9fe12SLen Brown return AE_BAD_PARAMETER; 245b4f9fe12SLen Brown 246b4f9fe12SLen Brown if (!find_guid(guid_string, &wblock)) 247b4f9fe12SLen Brown return AE_ERROR; 248b4f9fe12SLen Brown 249b4f9fe12SLen Brown block = &wblock->gblock; 250b0e86302SAndy Lutomirski handle = wblock->acpi_device->handle; 251b4f9fe12SLen Brown 252b4f9fe12SLen Brown if (block->instance_count < instance) 253b4f9fe12SLen Brown return AE_BAD_PARAMETER; 254b4f9fe12SLen Brown 255b4f9fe12SLen Brown /* Check GUID is a data block */ 256b4f9fe12SLen Brown if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD)) 257b4f9fe12SLen Brown return AE_ERROR; 258b4f9fe12SLen Brown 259b4f9fe12SLen Brown input.count = 1; 260b4f9fe12SLen Brown input.pointer = wq_params; 261b4f9fe12SLen Brown wq_params[0].type = ACPI_TYPE_INTEGER; 262b4f9fe12SLen Brown wq_params[0].integer.value = instance; 263b4f9fe12SLen Brown 264b4f9fe12SLen Brown /* 265b4f9fe12SLen Brown * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method first to 266b4f9fe12SLen Brown * enable collection. 267b4f9fe12SLen Brown */ 268b4f9fe12SLen Brown if (block->flags & ACPI_WMI_EXPENSIVE) { 269b4f9fe12SLen Brown strncat(wc_method, block->object_id, 2); 270b4f9fe12SLen Brown 271b4f9fe12SLen Brown /* 272b4f9fe12SLen Brown * Some GUIDs break the specification by declaring themselves 273b4f9fe12SLen Brown * expensive, but have no corresponding WCxx method. So we 274b4f9fe12SLen Brown * should not fail if this happens. 275b4f9fe12SLen Brown */ 27654f14c27SZhang Rui if (acpi_has_method(handle, wc_method)) 2778122ab66SZhang Rui wc_status = acpi_execute_simple_method(handle, 2788122ab66SZhang Rui wc_method, 1); 279b4f9fe12SLen Brown } 280b4f9fe12SLen Brown 281b4f9fe12SLen Brown strcpy(method, "WQ"); 282b4f9fe12SLen Brown strncat(method, block->object_id, 2); 283b4f9fe12SLen Brown 284b4f9fe12SLen Brown status = acpi_evaluate_object(handle, method, &input, out); 285b4f9fe12SLen Brown 286b4f9fe12SLen Brown /* 287b4f9fe12SLen Brown * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method, even if 288b4f9fe12SLen Brown * the WQxx method failed - we should disable collection anyway. 289b4f9fe12SLen Brown */ 290b4f9fe12SLen Brown if ((block->flags & ACPI_WMI_EXPENSIVE) && ACPI_SUCCESS(wc_status)) { 2918122ab66SZhang Rui status = acpi_execute_simple_method(handle, wc_method, 0); 292b4f9fe12SLen Brown } 293b4f9fe12SLen Brown 294b4f9fe12SLen Brown return status; 295b4f9fe12SLen Brown } 296b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_query_block); 297b4f9fe12SLen Brown 298b4f9fe12SLen Brown /** 299b4f9fe12SLen Brown * wmi_set_block - Write to a WMI block 300b4f9fe12SLen Brown * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba 301b4f9fe12SLen Brown * @instance: Instance index 302b4f9fe12SLen Brown * &in: Buffer containing new values for the data block 303b4f9fe12SLen Brown * 304b4f9fe12SLen Brown * Write the contents of the input buffer to an ACPI-WMI data block 305b4f9fe12SLen Brown */ 306b4f9fe12SLen Brown acpi_status wmi_set_block(const char *guid_string, u8 instance, 307b4f9fe12SLen Brown const struct acpi_buffer *in) 308b4f9fe12SLen Brown { 309b4f9fe12SLen Brown struct guid_block *block = NULL; 310b4f9fe12SLen Brown struct wmi_block *wblock = NULL; 311b4f9fe12SLen Brown acpi_handle handle; 312b4f9fe12SLen Brown struct acpi_object_list input; 313b4f9fe12SLen Brown union acpi_object params[2]; 314f3d83e24SCostantino Leandro char method[5] = "WS"; 315b4f9fe12SLen Brown 316b4f9fe12SLen Brown if (!guid_string || !in) 317b4f9fe12SLen Brown return AE_BAD_DATA; 318b4f9fe12SLen Brown 319b4f9fe12SLen Brown if (!find_guid(guid_string, &wblock)) 320b4f9fe12SLen Brown return AE_ERROR; 321b4f9fe12SLen Brown 322b4f9fe12SLen Brown block = &wblock->gblock; 323b0e86302SAndy Lutomirski handle = wblock->acpi_device->handle; 324b4f9fe12SLen Brown 325b4f9fe12SLen Brown if (block->instance_count < instance) 326b4f9fe12SLen Brown return AE_BAD_PARAMETER; 327b4f9fe12SLen Brown 328b4f9fe12SLen Brown /* Check GUID is a data block */ 329b4f9fe12SLen Brown if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD)) 330b4f9fe12SLen Brown return AE_ERROR; 331b4f9fe12SLen Brown 332b4f9fe12SLen Brown input.count = 2; 333b4f9fe12SLen Brown input.pointer = params; 334b4f9fe12SLen Brown params[0].type = ACPI_TYPE_INTEGER; 335b4f9fe12SLen Brown params[0].integer.value = instance; 336b4f9fe12SLen Brown 337b4f9fe12SLen Brown if (block->flags & ACPI_WMI_STRING) { 338b4f9fe12SLen Brown params[1].type = ACPI_TYPE_STRING; 339b4f9fe12SLen Brown } else { 340b4f9fe12SLen Brown params[1].type = ACPI_TYPE_BUFFER; 341b4f9fe12SLen Brown } 342b4f9fe12SLen Brown params[1].buffer.length = in->length; 343b4f9fe12SLen Brown params[1].buffer.pointer = in->pointer; 344b4f9fe12SLen Brown 345b4f9fe12SLen Brown strncat(method, block->object_id, 2); 346b4f9fe12SLen Brown 347b4f9fe12SLen Brown return acpi_evaluate_object(handle, method, &input, NULL); 348b4f9fe12SLen Brown } 349b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_set_block); 350b4f9fe12SLen Brown 35137830662SDmitry Torokhov static void wmi_dump_wdg(const struct guid_block *g) 352a929aae0SThomas Renninger { 35385b4e4ebSRasmus Villemoes pr_info("%pUL:\n", g->guid); 3548e07514dSDmitry Torokhov pr_info("\tobject_id: %c%c\n", g->object_id[0], g->object_id[1]); 3558e07514dSDmitry Torokhov pr_info("\tnotify_id: %02X\n", g->notify_id); 3568e07514dSDmitry Torokhov pr_info("\treserved: %02X\n", g->reserved); 3578e07514dSDmitry Torokhov pr_info("\tinstance_count: %d\n", g->instance_count); 3588e07514dSDmitry Torokhov pr_info("\tflags: %#x", g->flags); 359a929aae0SThomas Renninger if (g->flags) { 360a929aae0SThomas Renninger if (g->flags & ACPI_WMI_EXPENSIVE) 3618e07514dSDmitry Torokhov pr_cont(" ACPI_WMI_EXPENSIVE"); 362a929aae0SThomas Renninger if (g->flags & ACPI_WMI_METHOD) 3638e07514dSDmitry Torokhov pr_cont(" ACPI_WMI_METHOD"); 364a929aae0SThomas Renninger if (g->flags & ACPI_WMI_STRING) 3658e07514dSDmitry Torokhov pr_cont(" ACPI_WMI_STRING"); 366a929aae0SThomas Renninger if (g->flags & ACPI_WMI_EVENT) 3678e07514dSDmitry Torokhov pr_cont(" ACPI_WMI_EVENT"); 368a929aae0SThomas Renninger } 3698e07514dSDmitry Torokhov pr_cont("\n"); 370a929aae0SThomas Renninger 371a929aae0SThomas Renninger } 372a929aae0SThomas Renninger 373fc3155b2SThomas Renninger static void wmi_notify_debug(u32 value, void *context) 374fc3155b2SThomas Renninger { 375fc3155b2SThomas Renninger struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; 376fc3155b2SThomas Renninger union acpi_object *obj; 3771492616aSAxel Lin acpi_status status; 378fc3155b2SThomas Renninger 3791492616aSAxel Lin status = wmi_get_event_data(value, &response); 3801492616aSAxel Lin if (status != AE_OK) { 3818e07514dSDmitry Torokhov pr_info("bad event status 0x%x\n", status); 3821492616aSAxel Lin return; 3831492616aSAxel Lin } 384fc3155b2SThomas Renninger 385fc3155b2SThomas Renninger obj = (union acpi_object *)response.pointer; 386fc3155b2SThomas Renninger 387fc3155b2SThomas Renninger if (!obj) 388fc3155b2SThomas Renninger return; 389fc3155b2SThomas Renninger 3908e07514dSDmitry Torokhov pr_info("DEBUG Event "); 391fc3155b2SThomas Renninger switch(obj->type) { 392fc3155b2SThomas Renninger case ACPI_TYPE_BUFFER: 3938e07514dSDmitry Torokhov pr_cont("BUFFER_TYPE - length %d\n", obj->buffer.length); 394fc3155b2SThomas Renninger break; 395fc3155b2SThomas Renninger case ACPI_TYPE_STRING: 3968e07514dSDmitry Torokhov pr_cont("STRING_TYPE - %s\n", obj->string.pointer); 397fc3155b2SThomas Renninger break; 398fc3155b2SThomas Renninger case ACPI_TYPE_INTEGER: 3998e07514dSDmitry Torokhov pr_cont("INTEGER_TYPE - %llu\n", obj->integer.value); 400fc3155b2SThomas Renninger break; 401fc3155b2SThomas Renninger case ACPI_TYPE_PACKAGE: 4028e07514dSDmitry Torokhov pr_cont("PACKAGE_TYPE - %d elements\n", obj->package.count); 403fc3155b2SThomas Renninger break; 404fc3155b2SThomas Renninger default: 4058e07514dSDmitry Torokhov pr_cont("object type 0x%X\n", obj->type); 406fc3155b2SThomas Renninger } 4071492616aSAxel Lin kfree(obj); 408fc3155b2SThomas Renninger } 409fc3155b2SThomas Renninger 410b4f9fe12SLen Brown /** 411b4f9fe12SLen Brown * wmi_install_notify_handler - Register handler for WMI events 412b4f9fe12SLen Brown * @handler: Function to handle notifications 413b4f9fe12SLen Brown * @data: Data to be returned to handler when event is fired 414b4f9fe12SLen Brown * 415b4f9fe12SLen Brown * Register a handler for events sent to the ACPI-WMI mapper device. 416b4f9fe12SLen Brown */ 417b4f9fe12SLen Brown acpi_status wmi_install_notify_handler(const char *guid, 418b4f9fe12SLen Brown wmi_notify_handler handler, void *data) 419b4f9fe12SLen Brown { 420b4f9fe12SLen Brown struct wmi_block *block; 42158f6425eSColin King acpi_status status = AE_NOT_EXIST; 422538d7eb8SAndy Shevchenko uuid_le guid_input; 42358f6425eSColin King struct list_head *p; 424b4f9fe12SLen Brown 425b4f9fe12SLen Brown if (!guid || !handler) 426b4f9fe12SLen Brown return AE_BAD_PARAMETER; 427b4f9fe12SLen Brown 428538d7eb8SAndy Shevchenko if (uuid_le_to_bin(guid, &guid_input)) 429538d7eb8SAndy Shevchenko return AE_BAD_PARAMETER; 430b4f9fe12SLen Brown 43158f6425eSColin King list_for_each(p, &wmi_block_list) { 43258f6425eSColin King acpi_status wmi_status; 43358f6425eSColin King block = list_entry(p, struct wmi_block, list); 43458f6425eSColin King 435538d7eb8SAndy Shevchenko if (memcmp(block->gblock.guid, &guid_input, 16) == 0) { 43658f6425eSColin King if (block->handler && 43758f6425eSColin King block->handler != wmi_notify_debug) 438b4f9fe12SLen Brown return AE_ALREADY_ACQUIRED; 439b4f9fe12SLen Brown 440b4f9fe12SLen Brown block->handler = handler; 441b4f9fe12SLen Brown block->handler_data = data; 442b4f9fe12SLen Brown 44358f6425eSColin King wmi_status = wmi_method_enable(block, 1); 44458f6425eSColin King if ((wmi_status != AE_OK) || 44558f6425eSColin King ((wmi_status == AE_OK) && (status == AE_NOT_EXIST))) 44658f6425eSColin King status = wmi_status; 44758f6425eSColin King } 44858f6425eSColin King } 449b4f9fe12SLen Brown 450b4f9fe12SLen Brown return status; 451b4f9fe12SLen Brown } 452b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_install_notify_handler); 453b4f9fe12SLen Brown 454b4f9fe12SLen Brown /** 455b4f9fe12SLen Brown * wmi_uninstall_notify_handler - Unregister handler for WMI events 456b4f9fe12SLen Brown * 457b4f9fe12SLen Brown * Unregister handler for events sent to the ACPI-WMI mapper device. 458b4f9fe12SLen Brown */ 459b4f9fe12SLen Brown acpi_status wmi_remove_notify_handler(const char *guid) 460b4f9fe12SLen Brown { 461b4f9fe12SLen Brown struct wmi_block *block; 46258f6425eSColin King acpi_status status = AE_NOT_EXIST; 463538d7eb8SAndy Shevchenko uuid_le guid_input; 46458f6425eSColin King struct list_head *p; 465b4f9fe12SLen Brown 466b4f9fe12SLen Brown if (!guid) 467b4f9fe12SLen Brown return AE_BAD_PARAMETER; 468b4f9fe12SLen Brown 469538d7eb8SAndy Shevchenko if (uuid_le_to_bin(guid, &guid_input)) 470538d7eb8SAndy Shevchenko return AE_BAD_PARAMETER; 471b4f9fe12SLen Brown 47258f6425eSColin King list_for_each(p, &wmi_block_list) { 47358f6425eSColin King acpi_status wmi_status; 47458f6425eSColin King block = list_entry(p, struct wmi_block, list); 47558f6425eSColin King 476538d7eb8SAndy Shevchenko if (memcmp(block->gblock.guid, &guid_input, 16) == 0) { 47758f6425eSColin King if (!block->handler || 47858f6425eSColin King block->handler == wmi_notify_debug) 479b4f9fe12SLen Brown return AE_NULL_ENTRY; 480b4f9fe12SLen Brown 481fc3155b2SThomas Renninger if (debug_event) { 482fc3155b2SThomas Renninger block->handler = wmi_notify_debug; 48358f6425eSColin King status = AE_OK; 484fc3155b2SThomas Renninger } else { 48558f6425eSColin King wmi_status = wmi_method_enable(block, 0); 486b4f9fe12SLen Brown block->handler = NULL; 487b4f9fe12SLen Brown block->handler_data = NULL; 48858f6425eSColin King if ((wmi_status != AE_OK) || 48958f6425eSColin King ((wmi_status == AE_OK) && 49058f6425eSColin King (status == AE_NOT_EXIST))) 49158f6425eSColin King status = wmi_status; 492fc3155b2SThomas Renninger } 49358f6425eSColin King } 49458f6425eSColin King } 49558f6425eSColin King 496b4f9fe12SLen Brown return status; 497b4f9fe12SLen Brown } 498b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_remove_notify_handler); 499b4f9fe12SLen Brown 500b4f9fe12SLen Brown /** 501b4f9fe12SLen Brown * wmi_get_event_data - Get WMI data associated with an event 502b4f9fe12SLen Brown * 5033e9b988eSAnisse Astier * @event: Event to find 5043e9b988eSAnisse Astier * @out: Buffer to hold event data. out->pointer should be freed with kfree() 505b4f9fe12SLen Brown * 506b4f9fe12SLen Brown * Returns extra data associated with an event in WMI. 507b4f9fe12SLen Brown */ 508b4f9fe12SLen Brown acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out) 509b4f9fe12SLen Brown { 510b4f9fe12SLen Brown struct acpi_object_list input; 511b4f9fe12SLen Brown union acpi_object params[1]; 512b4f9fe12SLen Brown struct guid_block *gblock; 513b4f9fe12SLen Brown struct wmi_block *wblock; 514b4f9fe12SLen Brown struct list_head *p; 515b4f9fe12SLen Brown 516b4f9fe12SLen Brown input.count = 1; 517b4f9fe12SLen Brown input.pointer = params; 518b4f9fe12SLen Brown params[0].type = ACPI_TYPE_INTEGER; 519b4f9fe12SLen Brown params[0].integer.value = event; 520b4f9fe12SLen Brown 521762e1a2fSDmitry Torokhov list_for_each(p, &wmi_block_list) { 522b4f9fe12SLen Brown wblock = list_entry(p, struct wmi_block, list); 523b4f9fe12SLen Brown gblock = &wblock->gblock; 524b4f9fe12SLen Brown 525b4f9fe12SLen Brown if ((gblock->flags & ACPI_WMI_EVENT) && 526b4f9fe12SLen Brown (gblock->notify_id == event)) 527b0e86302SAndy Lutomirski return acpi_evaluate_object(wblock->acpi_device->handle, 528b0e86302SAndy Lutomirski "_WED", &input, out); 529b4f9fe12SLen Brown } 530b4f9fe12SLen Brown 531b4f9fe12SLen Brown return AE_NOT_FOUND; 532b4f9fe12SLen Brown } 533b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_get_event_data); 534b4f9fe12SLen Brown 535b4f9fe12SLen Brown /** 536b4f9fe12SLen Brown * wmi_has_guid - Check if a GUID is available 537b4f9fe12SLen Brown * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba 538b4f9fe12SLen Brown * 539b4f9fe12SLen Brown * Check if a given GUID is defined by _WDG 540b4f9fe12SLen Brown */ 541b4f9fe12SLen Brown bool wmi_has_guid(const char *guid_string) 542b4f9fe12SLen Brown { 543b4f9fe12SLen Brown return find_guid(guid_string, NULL); 544b4f9fe12SLen Brown } 545b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_has_guid); 546b4f9fe12SLen Brown 547844af950SAndy Lutomirski static struct wmi_block *dev_to_wblock(struct device *dev) 548844af950SAndy Lutomirski { 549844af950SAndy Lutomirski return container_of(dev, struct wmi_block, dev.dev); 550844af950SAndy Lutomirski } 551844af950SAndy Lutomirski 552844af950SAndy Lutomirski static struct wmi_device *dev_to_wdev(struct device *dev) 553844af950SAndy Lutomirski { 554844af950SAndy Lutomirski return container_of(dev, struct wmi_device, dev); 555844af950SAndy Lutomirski } 556844af950SAndy Lutomirski 557b4f9fe12SLen Brown /* 5581caab3c1SMatthew Garrett * sysfs interface 5591caab3c1SMatthew Garrett */ 560614ef432SDmitry Torokhov static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, 5611caab3c1SMatthew Garrett char *buf) 5621caab3c1SMatthew Garrett { 563844af950SAndy Lutomirski struct wmi_block *wblock = dev_to_wblock(dev); 5641caab3c1SMatthew Garrett 56585b4e4ebSRasmus Villemoes return sprintf(buf, "wmi:%pUL\n", wblock->gblock.guid); 5661caab3c1SMatthew Garrett } 567e80b89a5SGreg Kroah-Hartman static DEVICE_ATTR_RO(modalias); 568614ef432SDmitry Torokhov 569844af950SAndy Lutomirski static ssize_t guid_show(struct device *dev, struct device_attribute *attr, 570844af950SAndy Lutomirski char *buf) 571844af950SAndy Lutomirski { 572844af950SAndy Lutomirski struct wmi_block *wblock = dev_to_wblock(dev); 573844af950SAndy Lutomirski 574844af950SAndy Lutomirski return sprintf(buf, "%pUL\n", wblock->gblock.guid); 575844af950SAndy Lutomirski } 576844af950SAndy Lutomirski static DEVICE_ATTR_RO(guid); 577844af950SAndy Lutomirski 578e80b89a5SGreg Kroah-Hartman static struct attribute *wmi_attrs[] = { 579e80b89a5SGreg Kroah-Hartman &dev_attr_modalias.attr, 580844af950SAndy Lutomirski &dev_attr_guid.attr, 581e80b89a5SGreg Kroah-Hartman NULL, 582614ef432SDmitry Torokhov }; 583e80b89a5SGreg Kroah-Hartman ATTRIBUTE_GROUPS(wmi); 5841caab3c1SMatthew Garrett 5851caab3c1SMatthew Garrett static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env) 5861caab3c1SMatthew Garrett { 587844af950SAndy Lutomirski struct wmi_block *wblock = dev_to_wblock(dev); 5881caab3c1SMatthew Garrett 589844af950SAndy Lutomirski if (add_uevent_var(env, "MODALIAS=wmi:%pUL", wblock->gblock.guid)) 5901caab3c1SMatthew Garrett return -ENOMEM; 5911caab3c1SMatthew Garrett 592844af950SAndy Lutomirski if (add_uevent_var(env, "WMI_GUID=%pUL", wblock->gblock.guid)) 5931caab3c1SMatthew Garrett return -ENOMEM; 5941caab3c1SMatthew Garrett 5951caab3c1SMatthew Garrett return 0; 5961caab3c1SMatthew Garrett } 5971caab3c1SMatthew Garrett 598844af950SAndy Lutomirski static void wmi_dev_release(struct device *dev) 5991caab3c1SMatthew Garrett { 600844af950SAndy Lutomirski struct wmi_block *wblock = dev_to_wblock(dev); 601c64eefd4SDmitry Torokhov 602844af950SAndy Lutomirski kfree(wblock); 6031caab3c1SMatthew Garrett } 6041caab3c1SMatthew Garrett 605844af950SAndy Lutomirski static int wmi_dev_match(struct device *dev, struct device_driver *driver) 606844af950SAndy Lutomirski { 607844af950SAndy Lutomirski struct wmi_driver *wmi_driver = 608844af950SAndy Lutomirski container_of(driver, struct wmi_driver, driver); 609844af950SAndy Lutomirski struct wmi_block *wblock = dev_to_wblock(dev); 610844af950SAndy Lutomirski const struct wmi_device_id *id = wmi_driver->id_table; 611844af950SAndy Lutomirski 612844af950SAndy Lutomirski while (id->guid_string) { 613844af950SAndy Lutomirski uuid_le driver_guid; 614844af950SAndy Lutomirski 615844af950SAndy Lutomirski if (WARN_ON(uuid_le_to_bin(id->guid_string, &driver_guid))) 616844af950SAndy Lutomirski continue; 617844af950SAndy Lutomirski if (!memcmp(&driver_guid, wblock->gblock.guid, 16)) 618844af950SAndy Lutomirski return 1; 619844af950SAndy Lutomirski 620844af950SAndy Lutomirski id++; 621844af950SAndy Lutomirski } 622844af950SAndy Lutomirski 623844af950SAndy Lutomirski return 0; 624844af950SAndy Lutomirski } 625844af950SAndy Lutomirski 626844af950SAndy Lutomirski static int wmi_dev_probe(struct device *dev) 627844af950SAndy Lutomirski { 628844af950SAndy Lutomirski struct wmi_block *wblock = dev_to_wblock(dev); 629844af950SAndy Lutomirski struct wmi_driver *wdriver = 630844af950SAndy Lutomirski container_of(dev->driver, struct wmi_driver, driver); 631844af950SAndy Lutomirski int ret = 0; 632844af950SAndy Lutomirski 633844af950SAndy Lutomirski if (ACPI_FAILURE(wmi_method_enable(wblock, 1))) 634844af950SAndy Lutomirski dev_warn(dev, "failed to enable device -- probing anyway\n"); 635844af950SAndy Lutomirski 636844af950SAndy Lutomirski if (wdriver->probe) { 637844af950SAndy Lutomirski ret = wdriver->probe(dev_to_wdev(dev)); 638844af950SAndy Lutomirski if (ret != 0 && ACPI_FAILURE(wmi_method_enable(wblock, 0))) 639844af950SAndy Lutomirski dev_warn(dev, "failed to disable device\n"); 640844af950SAndy Lutomirski } 641844af950SAndy Lutomirski 642844af950SAndy Lutomirski return ret; 643844af950SAndy Lutomirski } 644844af950SAndy Lutomirski 645844af950SAndy Lutomirski static int wmi_dev_remove(struct device *dev) 646844af950SAndy Lutomirski { 647844af950SAndy Lutomirski struct wmi_block *wblock = dev_to_wblock(dev); 648844af950SAndy Lutomirski struct wmi_driver *wdriver = 649844af950SAndy Lutomirski container_of(dev->driver, struct wmi_driver, driver); 650844af950SAndy Lutomirski int ret = 0; 651844af950SAndy Lutomirski 652844af950SAndy Lutomirski if (wdriver->remove) 653844af950SAndy Lutomirski ret = wdriver->remove(dev_to_wdev(dev)); 654844af950SAndy Lutomirski 655844af950SAndy Lutomirski if (ACPI_FAILURE(wmi_method_enable(wblock, 0))) 656844af950SAndy Lutomirski dev_warn(dev, "failed to disable device\n"); 657844af950SAndy Lutomirski 658844af950SAndy Lutomirski return ret; 659844af950SAndy Lutomirski } 660844af950SAndy Lutomirski 661844af950SAndy Lutomirski static struct class wmi_bus_class = { 662844af950SAndy Lutomirski .name = "wmi_bus", 6631caab3c1SMatthew Garrett }; 6641caab3c1SMatthew Garrett 665844af950SAndy Lutomirski static struct bus_type wmi_bus_type = { 666844af950SAndy Lutomirski .name = "wmi", 667844af950SAndy Lutomirski .dev_groups = wmi_groups, 668844af950SAndy Lutomirski .match = wmi_dev_match, 669844af950SAndy Lutomirski .uevent = wmi_dev_uevent, 670844af950SAndy Lutomirski .probe = wmi_dev_probe, 671844af950SAndy Lutomirski .remove = wmi_dev_remove, 672844af950SAndy Lutomirski }; 673844af950SAndy Lutomirski 674844af950SAndy Lutomirski static int wmi_create_device(struct device *wmi_bus_dev, 675844af950SAndy Lutomirski const struct guid_block *gblock, 6767f5809bfSAndy Lutomirski struct wmi_block *wblock, 6777f5809bfSAndy Lutomirski struct acpi_device *device) 6781caab3c1SMatthew Garrett { 679844af950SAndy Lutomirski wblock->dev.dev.bus = &wmi_bus_type; 680844af950SAndy Lutomirski wblock->dev.dev.parent = wmi_bus_dev; 6811caab3c1SMatthew Garrett 682844af950SAndy Lutomirski dev_set_name(&wblock->dev.dev, "%pUL", gblock->guid); 6831caab3c1SMatthew Garrett 684844af950SAndy Lutomirski wblock->dev.dev.release = wmi_dev_release; 685c64eefd4SDmitry Torokhov 686844af950SAndy Lutomirski return device_register(&wblock->dev.dev); 6871caab3c1SMatthew Garrett } 6881caab3c1SMatthew Garrett 689b0e86302SAndy Lutomirski static void wmi_free_devices(struct acpi_device *device) 6901caab3c1SMatthew Garrett { 691c64eefd4SDmitry Torokhov struct wmi_block *wblock, *next; 6921caab3c1SMatthew Garrett 6931caab3c1SMatthew Garrett /* Delete devices for all the GUIDs */ 694023b9565SDmitry Torokhov list_for_each_entry_safe(wblock, next, &wmi_block_list, list) { 695b0e86302SAndy Lutomirski if (wblock->acpi_device == device) { 696023b9565SDmitry Torokhov list_del(&wblock->list); 697844af950SAndy Lutomirski if (wblock->dev.dev.bus) 698844af950SAndy Lutomirski device_unregister(&wblock->dev.dev); 699023b9565SDmitry Torokhov else 700023b9565SDmitry Torokhov kfree(wblock); 701023b9565SDmitry Torokhov } 7021caab3c1SMatthew Garrett } 703b0e86302SAndy Lutomirski } 7041caab3c1SMatthew Garrett 705b0e86302SAndy Lutomirski static bool guid_already_parsed(struct acpi_device *device, 706b0e86302SAndy Lutomirski const u8 *guid) 707d1f9e497SCarlos Corbacho { 708d1f9e497SCarlos Corbacho struct wmi_block *wblock; 709d1f9e497SCarlos Corbacho 710b0e86302SAndy Lutomirski list_for_each_entry(wblock, &wmi_block_list, list) { 711b0e86302SAndy Lutomirski if (memcmp(wblock->gblock.guid, guid, 16) == 0) { 712b0e86302SAndy Lutomirski /* 713b0e86302SAndy Lutomirski * Because we historically didn't track the relationship 714b0e86302SAndy Lutomirski * between GUIDs and ACPI nodes, we don't know whether 715b0e86302SAndy Lutomirski * we need to suppress GUIDs that are unique on a 716b0e86302SAndy Lutomirski * given node but duplicated across nodes. 717b0e86302SAndy Lutomirski */ 718b0e86302SAndy Lutomirski dev_warn(&device->dev, "duplicate WMI GUID %pUL (first instance was on %s)\n", 719b0e86302SAndy Lutomirski guid, dev_name(&wblock->acpi_device->dev)); 720d1f9e497SCarlos Corbacho return true; 721b0e86302SAndy Lutomirski } 722b0e86302SAndy Lutomirski } 723c64eefd4SDmitry Torokhov 724d1f9e497SCarlos Corbacho return false; 725d1f9e497SCarlos Corbacho } 726d1f9e497SCarlos Corbacho 7271caab3c1SMatthew Garrett /* 728b4f9fe12SLen Brown * Parse the _WDG method for the GUID data blocks 729b4f9fe12SLen Brown */ 730844af950SAndy Lutomirski static int parse_wdg(struct device *wmi_bus_dev, struct acpi_device *device) 731b4f9fe12SLen Brown { 732b4f9fe12SLen Brown struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL}; 733b4f9fe12SLen Brown union acpi_object *obj; 73437830662SDmitry Torokhov const struct guid_block *gblock; 735b4f9fe12SLen Brown struct wmi_block *wblock; 736b4f9fe12SLen Brown acpi_status status; 737c64eefd4SDmitry Torokhov int retval; 738b4f9fe12SLen Brown u32 i, total; 739b4f9fe12SLen Brown 7407f5809bfSAndy Lutomirski status = acpi_evaluate_object(device->handle, "_WDG", NULL, &out); 741b4f9fe12SLen Brown if (ACPI_FAILURE(status)) 742c64eefd4SDmitry Torokhov return -ENXIO; 743b4f9fe12SLen Brown 744b4f9fe12SLen Brown obj = (union acpi_object *) out.pointer; 7453d2c63ebSDmitry Torokhov if (!obj) 746c64eefd4SDmitry Torokhov return -ENXIO; 747b4f9fe12SLen Brown 74864ed0ab8SDmitry Torokhov if (obj->type != ACPI_TYPE_BUFFER) { 749c64eefd4SDmitry Torokhov retval = -ENXIO; 75064ed0ab8SDmitry Torokhov goto out_free_pointer; 75164ed0ab8SDmitry Torokhov } 752b4f9fe12SLen Brown 75337830662SDmitry Torokhov gblock = (const struct guid_block *)obj->buffer.pointer; 754b4f9fe12SLen Brown total = obj->buffer.length / sizeof(struct guid_block); 755b4f9fe12SLen Brown 756b4f9fe12SLen Brown for (i = 0; i < total; i++) { 757a929aae0SThomas Renninger if (debug_dump_wdg) 758a929aae0SThomas Renninger wmi_dump_wdg(&gblock[i]); 759a929aae0SThomas Renninger 76058f6425eSColin King wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL); 76158f6425eSColin King if (!wblock) 7620a018a68SDan Carpenter return -ENOMEM; 76358f6425eSColin King 764b0e86302SAndy Lutomirski wblock->acpi_device = device; 76558f6425eSColin King wblock->gblock = gblock[i]; 76658f6425eSColin King 76758f6425eSColin King /* 76858f6425eSColin King Some WMI devices, like those for nVidia hooks, have a 76958f6425eSColin King duplicate GUID. It's not clear what we should do in this 77058f6425eSColin King case yet, so for now, we'll just ignore the duplicate 77158f6425eSColin King for device creation. 77258f6425eSColin King */ 773b0e86302SAndy Lutomirski if (!guid_already_parsed(device, gblock[i].guid)) { 774844af950SAndy Lutomirski retval = wmi_create_device(wmi_bus_dev, &gblock[i], 775844af950SAndy Lutomirski wblock, device); 77658f6425eSColin King if (retval) { 777b0e86302SAndy Lutomirski wmi_free_devices(device); 778e1e0dacbSDan Carpenter goto out_free_pointer; 779a5167c5bSAxel Lin } 78058f6425eSColin King } 78158f6425eSColin King 78258f6425eSColin King list_add_tail(&wblock->list, &wmi_block_list); 783b4f9fe12SLen Brown 784fc3155b2SThomas Renninger if (debug_event) { 785fc3155b2SThomas Renninger wblock->handler = wmi_notify_debug; 7862d5ab555SDmitry Torokhov wmi_method_enable(wblock, 1); 787fc3155b2SThomas Renninger } 788b4f9fe12SLen Brown } 789b4f9fe12SLen Brown 790c64eefd4SDmitry Torokhov retval = 0; 791c64eefd4SDmitry Torokhov 792a5167c5bSAxel Lin out_free_pointer: 793a5167c5bSAxel Lin kfree(out.pointer); 794b4f9fe12SLen Brown 795c64eefd4SDmitry Torokhov return retval; 796b4f9fe12SLen Brown } 797b4f9fe12SLen Brown 798b4f9fe12SLen Brown /* 799b4f9fe12SLen Brown * WMI can have EmbeddedControl access regions. In which case, we just want to 800b4f9fe12SLen Brown * hand these off to the EC driver. 801b4f9fe12SLen Brown */ 802b4f9fe12SLen Brown static acpi_status 803b4f9fe12SLen Brown acpi_wmi_ec_space_handler(u32 function, acpi_physical_address address, 804439913ffSLin Ming u32 bits, u64 *value, 805b4f9fe12SLen Brown void *handler_context, void *region_context) 806b4f9fe12SLen Brown { 807b4f9fe12SLen Brown int result = 0, i = 0; 808b4f9fe12SLen Brown u8 temp = 0; 809b4f9fe12SLen Brown 810b4f9fe12SLen Brown if ((address > 0xFF) || !value) 811b4f9fe12SLen Brown return AE_BAD_PARAMETER; 812b4f9fe12SLen Brown 813b4f9fe12SLen Brown if (function != ACPI_READ && function != ACPI_WRITE) 814b4f9fe12SLen Brown return AE_BAD_PARAMETER; 815b4f9fe12SLen Brown 816b4f9fe12SLen Brown if (bits != 8) 817b4f9fe12SLen Brown return AE_BAD_PARAMETER; 818b4f9fe12SLen Brown 819b4f9fe12SLen Brown if (function == ACPI_READ) { 820b4f9fe12SLen Brown result = ec_read(address, &temp); 821439913ffSLin Ming (*value) |= ((u64)temp) << i; 822b4f9fe12SLen Brown } else { 823b4f9fe12SLen Brown temp = 0xff & ((*value) >> i); 824b4f9fe12SLen Brown result = ec_write(address, temp); 825b4f9fe12SLen Brown } 826b4f9fe12SLen Brown 827b4f9fe12SLen Brown switch (result) { 828b4f9fe12SLen Brown case -EINVAL: 829b4f9fe12SLen Brown return AE_BAD_PARAMETER; 830b4f9fe12SLen Brown break; 831b4f9fe12SLen Brown case -ENODEV: 832b4f9fe12SLen Brown return AE_NOT_FOUND; 833b4f9fe12SLen Brown break; 834b4f9fe12SLen Brown case -ETIME: 835b4f9fe12SLen Brown return AE_TIME; 836b4f9fe12SLen Brown break; 837b4f9fe12SLen Brown default: 838b4f9fe12SLen Brown return AE_OK; 839b4f9fe12SLen Brown } 840b4f9fe12SLen Brown } 841b4f9fe12SLen Brown 842f61bb939SBjorn Helgaas static void acpi_wmi_notify(struct acpi_device *device, u32 event) 843b4f9fe12SLen Brown { 844b4f9fe12SLen Brown struct guid_block *block; 845b4f9fe12SLen Brown struct wmi_block *wblock; 846b4f9fe12SLen Brown struct list_head *p; 847b4f9fe12SLen Brown 848762e1a2fSDmitry Torokhov list_for_each(p, &wmi_block_list) { 849b4f9fe12SLen Brown wblock = list_entry(p, struct wmi_block, list); 850b4f9fe12SLen Brown block = &wblock->gblock; 851b4f9fe12SLen Brown 852b0e86302SAndy Lutomirski if (wblock->acpi_device == device && 853b0e86302SAndy Lutomirski (block->flags & ACPI_WMI_EVENT) && 854b4f9fe12SLen Brown (block->notify_id == event)) { 855b4f9fe12SLen Brown if (wblock->handler) 856b4f9fe12SLen Brown wblock->handler(event, wblock->handler_data); 8577715348cSThomas Renninger if (debug_event) { 85885b4e4ebSRasmus Villemoes pr_info("DEBUG Event GUID: %pUL\n", 85985b4e4ebSRasmus Villemoes wblock->gblock.guid); 8607715348cSThomas Renninger } 861b4f9fe12SLen Brown 862b4f9fe12SLen Brown acpi_bus_generate_netlink_event( 863b4f9fe12SLen Brown device->pnp.device_class, dev_name(&device->dev), 864b4f9fe12SLen Brown event, 0); 865b4f9fe12SLen Brown break; 866b4f9fe12SLen Brown } 867b4f9fe12SLen Brown } 868b4f9fe12SLen Brown } 869b4f9fe12SLen Brown 87051fac838SRafael J. Wysocki static int acpi_wmi_remove(struct acpi_device *device) 871b4f9fe12SLen Brown { 872b4f9fe12SLen Brown acpi_remove_address_space_handler(device->handle, 873b4f9fe12SLen Brown ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler); 874b0e86302SAndy Lutomirski wmi_free_devices(device); 875844af950SAndy Lutomirski device_unregister((struct device *)acpi_driver_data(device)); 876844af950SAndy Lutomirski device->driver_data = NULL; 877b4f9fe12SLen Brown 878b4f9fe12SLen Brown return 0; 879b4f9fe12SLen Brown } 880b4f9fe12SLen Brown 881925b1089SThomas Renninger static int acpi_wmi_add(struct acpi_device *device) 882b4f9fe12SLen Brown { 883844af950SAndy Lutomirski struct device *wmi_bus_dev; 884b4f9fe12SLen Brown acpi_status status; 885c64eefd4SDmitry Torokhov int error; 886b4f9fe12SLen Brown 887b4f9fe12SLen Brown status = acpi_install_address_space_handler(device->handle, 888b4f9fe12SLen Brown ACPI_ADR_SPACE_EC, 889b4f9fe12SLen Brown &acpi_wmi_ec_space_handler, 890b4f9fe12SLen Brown NULL, NULL); 8915212cd67SDmitry Torokhov if (ACPI_FAILURE(status)) { 89246492ee4SAndy Lutomirski dev_err(&device->dev, "Error installing EC region handler\n"); 893b4f9fe12SLen Brown return -ENODEV; 8945212cd67SDmitry Torokhov } 895b4f9fe12SLen Brown 896844af950SAndy Lutomirski wmi_bus_dev = device_create(&wmi_bus_class, &device->dev, MKDEV(0, 0), 897844af950SAndy Lutomirski NULL, "wmi_bus-%s", dev_name(&device->dev)); 898844af950SAndy Lutomirski if (IS_ERR(wmi_bus_dev)) { 899844af950SAndy Lutomirski error = PTR_ERR(wmi_bus_dev); 900844af950SAndy Lutomirski goto err_remove_handler; 901844af950SAndy Lutomirski } 902844af950SAndy Lutomirski device->driver_data = wmi_bus_dev; 903844af950SAndy Lutomirski 904844af950SAndy Lutomirski error = parse_wdg(wmi_bus_dev, device); 905c64eefd4SDmitry Torokhov if (error) { 9068e07514dSDmitry Torokhov pr_err("Failed to parse WDG method\n"); 907844af950SAndy Lutomirski goto err_remove_busdev; 908b4f9fe12SLen Brown } 909b4f9fe12SLen Brown 910c64eefd4SDmitry Torokhov return 0; 91146492ee4SAndy Lutomirski 912844af950SAndy Lutomirski err_remove_busdev: 913844af950SAndy Lutomirski device_unregister(wmi_bus_dev); 914844af950SAndy Lutomirski 91546492ee4SAndy Lutomirski err_remove_handler: 91646492ee4SAndy Lutomirski acpi_remove_address_space_handler(device->handle, 91746492ee4SAndy Lutomirski ACPI_ADR_SPACE_EC, 91846492ee4SAndy Lutomirski &acpi_wmi_ec_space_handler); 91946492ee4SAndy Lutomirski 92046492ee4SAndy Lutomirski return error; 921b4f9fe12SLen Brown } 922b4f9fe12SLen Brown 923844af950SAndy Lutomirski int __must_check __wmi_driver_register(struct wmi_driver *driver, 924844af950SAndy Lutomirski struct module *owner) 925844af950SAndy Lutomirski { 926844af950SAndy Lutomirski driver->driver.owner = owner; 927844af950SAndy Lutomirski driver->driver.bus = &wmi_bus_type; 928844af950SAndy Lutomirski 929844af950SAndy Lutomirski return driver_register(&driver->driver); 930844af950SAndy Lutomirski } 931844af950SAndy Lutomirski EXPORT_SYMBOL(__wmi_driver_register); 932844af950SAndy Lutomirski 933844af950SAndy Lutomirski void wmi_driver_unregister(struct wmi_driver *driver) 934844af950SAndy Lutomirski { 935844af950SAndy Lutomirski driver_unregister(&driver->driver); 936844af950SAndy Lutomirski } 937844af950SAndy Lutomirski EXPORT_SYMBOL(wmi_driver_unregister); 938844af950SAndy Lutomirski 939b4f9fe12SLen Brown static int __init acpi_wmi_init(void) 940b4f9fe12SLen Brown { 941c64eefd4SDmitry Torokhov int error; 942b4f9fe12SLen Brown 943b4f9fe12SLen Brown if (acpi_disabled) 944b4f9fe12SLen Brown return -ENODEV; 945b4f9fe12SLen Brown 946844af950SAndy Lutomirski error = class_register(&wmi_bus_class); 947c64eefd4SDmitry Torokhov if (error) 948c64eefd4SDmitry Torokhov return error; 949b4f9fe12SLen Brown 950844af950SAndy Lutomirski error = bus_register(&wmi_bus_type); 951844af950SAndy Lutomirski if (error) 952844af950SAndy Lutomirski goto err_unreg_class; 953844af950SAndy Lutomirski 954c64eefd4SDmitry Torokhov error = acpi_bus_register_driver(&acpi_wmi_driver); 955c64eefd4SDmitry Torokhov if (error) { 956c64eefd4SDmitry Torokhov pr_err("Error loading mapper\n"); 957844af950SAndy Lutomirski goto err_unreg_bus; 9581caab3c1SMatthew Garrett } 9591caab3c1SMatthew Garrett 9608e07514dSDmitry Torokhov return 0; 961844af950SAndy Lutomirski 962844af950SAndy Lutomirski err_unreg_class: 963844af950SAndy Lutomirski class_unregister(&wmi_bus_class); 964844af950SAndy Lutomirski 965844af950SAndy Lutomirski err_unreg_bus: 966844af950SAndy Lutomirski bus_unregister(&wmi_bus_type); 967844af950SAndy Lutomirski 968844af950SAndy Lutomirski return error; 969b4f9fe12SLen Brown } 970b4f9fe12SLen Brown 971b4f9fe12SLen Brown static void __exit acpi_wmi_exit(void) 972b4f9fe12SLen Brown { 973b4f9fe12SLen Brown acpi_bus_unregister_driver(&acpi_wmi_driver); 974844af950SAndy Lutomirski class_unregister(&wmi_bus_class); 975844af950SAndy Lutomirski bus_unregister(&wmi_bus_type); 976b4f9fe12SLen Brown } 977b4f9fe12SLen Brown 978b4f9fe12SLen Brown subsys_initcall(acpi_wmi_init); 979b4f9fe12SLen Brown module_exit(acpi_wmi_exit); 980