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 760a1c31bcdSAndy Lutomirski /* 761a1c31bcdSAndy Lutomirski * Some WMI devices, like those for nVidia hooks, have a 762a1c31bcdSAndy Lutomirski * duplicate GUID. It's not clear what we should do in this 763a1c31bcdSAndy Lutomirski * case yet, so for now, we'll just ignore the duplicate 764a1c31bcdSAndy Lutomirski * for device creation. 765a1c31bcdSAndy Lutomirski */ 766a1c31bcdSAndy Lutomirski if (guid_already_parsed(device, gblock[i].guid)) 767a1c31bcdSAndy Lutomirski continue; 768a1c31bcdSAndy Lutomirski 76958f6425eSColin King wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL); 77058f6425eSColin King if (!wblock) 7710a018a68SDan Carpenter return -ENOMEM; 77258f6425eSColin King 773b0e86302SAndy Lutomirski wblock->acpi_device = device; 77458f6425eSColin King wblock->gblock = gblock[i]; 77558f6425eSColin King 776844af950SAndy Lutomirski retval = wmi_create_device(wmi_bus_dev, &gblock[i], 777844af950SAndy Lutomirski wblock, device); 77858f6425eSColin King if (retval) { 779a1c31bcdSAndy Lutomirski put_device(&wblock->dev.dev); 780b0e86302SAndy Lutomirski wmi_free_devices(device); 781e1e0dacbSDan Carpenter goto out_free_pointer; 782a5167c5bSAxel Lin } 78358f6425eSColin King 78458f6425eSColin King list_add_tail(&wblock->list, &wmi_block_list); 785b4f9fe12SLen Brown 786fc3155b2SThomas Renninger if (debug_event) { 787fc3155b2SThomas Renninger wblock->handler = wmi_notify_debug; 7882d5ab555SDmitry Torokhov wmi_method_enable(wblock, 1); 789fc3155b2SThomas Renninger } 790b4f9fe12SLen Brown } 791b4f9fe12SLen Brown 792c64eefd4SDmitry Torokhov retval = 0; 793c64eefd4SDmitry Torokhov 794a5167c5bSAxel Lin out_free_pointer: 795a5167c5bSAxel Lin kfree(out.pointer); 796b4f9fe12SLen Brown 797c64eefd4SDmitry Torokhov return retval; 798b4f9fe12SLen Brown } 799b4f9fe12SLen Brown 800b4f9fe12SLen Brown /* 801b4f9fe12SLen Brown * WMI can have EmbeddedControl access regions. In which case, we just want to 802b4f9fe12SLen Brown * hand these off to the EC driver. 803b4f9fe12SLen Brown */ 804b4f9fe12SLen Brown static acpi_status 805b4f9fe12SLen Brown acpi_wmi_ec_space_handler(u32 function, acpi_physical_address address, 806439913ffSLin Ming u32 bits, u64 *value, 807b4f9fe12SLen Brown void *handler_context, void *region_context) 808b4f9fe12SLen Brown { 809b4f9fe12SLen Brown int result = 0, i = 0; 810b4f9fe12SLen Brown u8 temp = 0; 811b4f9fe12SLen Brown 812b4f9fe12SLen Brown if ((address > 0xFF) || !value) 813b4f9fe12SLen Brown return AE_BAD_PARAMETER; 814b4f9fe12SLen Brown 815b4f9fe12SLen Brown if (function != ACPI_READ && function != ACPI_WRITE) 816b4f9fe12SLen Brown return AE_BAD_PARAMETER; 817b4f9fe12SLen Brown 818b4f9fe12SLen Brown if (bits != 8) 819b4f9fe12SLen Brown return AE_BAD_PARAMETER; 820b4f9fe12SLen Brown 821b4f9fe12SLen Brown if (function == ACPI_READ) { 822b4f9fe12SLen Brown result = ec_read(address, &temp); 823439913ffSLin Ming (*value) |= ((u64)temp) << i; 824b4f9fe12SLen Brown } else { 825b4f9fe12SLen Brown temp = 0xff & ((*value) >> i); 826b4f9fe12SLen Brown result = ec_write(address, temp); 827b4f9fe12SLen Brown } 828b4f9fe12SLen Brown 829b4f9fe12SLen Brown switch (result) { 830b4f9fe12SLen Brown case -EINVAL: 831b4f9fe12SLen Brown return AE_BAD_PARAMETER; 832b4f9fe12SLen Brown break; 833b4f9fe12SLen Brown case -ENODEV: 834b4f9fe12SLen Brown return AE_NOT_FOUND; 835b4f9fe12SLen Brown break; 836b4f9fe12SLen Brown case -ETIME: 837b4f9fe12SLen Brown return AE_TIME; 838b4f9fe12SLen Brown break; 839b4f9fe12SLen Brown default: 840b4f9fe12SLen Brown return AE_OK; 841b4f9fe12SLen Brown } 842b4f9fe12SLen Brown } 843b4f9fe12SLen Brown 844f61bb939SBjorn Helgaas static void acpi_wmi_notify(struct acpi_device *device, u32 event) 845b4f9fe12SLen Brown { 846b4f9fe12SLen Brown struct guid_block *block; 847b4f9fe12SLen Brown struct wmi_block *wblock; 848b4f9fe12SLen Brown struct list_head *p; 849b4f9fe12SLen Brown 850762e1a2fSDmitry Torokhov list_for_each(p, &wmi_block_list) { 851b4f9fe12SLen Brown wblock = list_entry(p, struct wmi_block, list); 852b4f9fe12SLen Brown block = &wblock->gblock; 853b4f9fe12SLen Brown 854b0e86302SAndy Lutomirski if (wblock->acpi_device == device && 855b0e86302SAndy Lutomirski (block->flags & ACPI_WMI_EVENT) && 856b4f9fe12SLen Brown (block->notify_id == event)) { 857b4f9fe12SLen Brown if (wblock->handler) 858b4f9fe12SLen Brown wblock->handler(event, wblock->handler_data); 8597715348cSThomas Renninger if (debug_event) { 86085b4e4ebSRasmus Villemoes pr_info("DEBUG Event GUID: %pUL\n", 86185b4e4ebSRasmus Villemoes wblock->gblock.guid); 8627715348cSThomas Renninger } 863b4f9fe12SLen Brown 864b4f9fe12SLen Brown acpi_bus_generate_netlink_event( 865b4f9fe12SLen Brown device->pnp.device_class, dev_name(&device->dev), 866b4f9fe12SLen Brown event, 0); 867b4f9fe12SLen Brown break; 868b4f9fe12SLen Brown } 869b4f9fe12SLen Brown } 870b4f9fe12SLen Brown } 871b4f9fe12SLen Brown 87251fac838SRafael J. Wysocki static int acpi_wmi_remove(struct acpi_device *device) 873b4f9fe12SLen Brown { 874b4f9fe12SLen Brown acpi_remove_address_space_handler(device->handle, 875b4f9fe12SLen Brown ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler); 876b0e86302SAndy Lutomirski wmi_free_devices(device); 877844af950SAndy Lutomirski device_unregister((struct device *)acpi_driver_data(device)); 878844af950SAndy Lutomirski device->driver_data = NULL; 879b4f9fe12SLen Brown 880b4f9fe12SLen Brown return 0; 881b4f9fe12SLen Brown } 882b4f9fe12SLen Brown 883925b1089SThomas Renninger static int acpi_wmi_add(struct acpi_device *device) 884b4f9fe12SLen Brown { 885844af950SAndy Lutomirski struct device *wmi_bus_dev; 886b4f9fe12SLen Brown acpi_status status; 887c64eefd4SDmitry Torokhov int error; 888b4f9fe12SLen Brown 889b4f9fe12SLen Brown status = acpi_install_address_space_handler(device->handle, 890b4f9fe12SLen Brown ACPI_ADR_SPACE_EC, 891b4f9fe12SLen Brown &acpi_wmi_ec_space_handler, 892b4f9fe12SLen Brown NULL, NULL); 8935212cd67SDmitry Torokhov if (ACPI_FAILURE(status)) { 89446492ee4SAndy Lutomirski dev_err(&device->dev, "Error installing EC region handler\n"); 895b4f9fe12SLen Brown return -ENODEV; 8965212cd67SDmitry Torokhov } 897b4f9fe12SLen Brown 898844af950SAndy Lutomirski wmi_bus_dev = device_create(&wmi_bus_class, &device->dev, MKDEV(0, 0), 899844af950SAndy Lutomirski NULL, "wmi_bus-%s", dev_name(&device->dev)); 900844af950SAndy Lutomirski if (IS_ERR(wmi_bus_dev)) { 901844af950SAndy Lutomirski error = PTR_ERR(wmi_bus_dev); 902844af950SAndy Lutomirski goto err_remove_handler; 903844af950SAndy Lutomirski } 904844af950SAndy Lutomirski device->driver_data = wmi_bus_dev; 905844af950SAndy Lutomirski 906844af950SAndy Lutomirski error = parse_wdg(wmi_bus_dev, device); 907c64eefd4SDmitry Torokhov if (error) { 9088e07514dSDmitry Torokhov pr_err("Failed to parse WDG method\n"); 909844af950SAndy Lutomirski goto err_remove_busdev; 910b4f9fe12SLen Brown } 911b4f9fe12SLen Brown 912c64eefd4SDmitry Torokhov return 0; 91346492ee4SAndy Lutomirski 914844af950SAndy Lutomirski err_remove_busdev: 915844af950SAndy Lutomirski device_unregister(wmi_bus_dev); 916844af950SAndy Lutomirski 91746492ee4SAndy Lutomirski err_remove_handler: 91846492ee4SAndy Lutomirski acpi_remove_address_space_handler(device->handle, 91946492ee4SAndy Lutomirski ACPI_ADR_SPACE_EC, 92046492ee4SAndy Lutomirski &acpi_wmi_ec_space_handler); 92146492ee4SAndy Lutomirski 92246492ee4SAndy Lutomirski return error; 923b4f9fe12SLen Brown } 924b4f9fe12SLen Brown 925844af950SAndy Lutomirski int __must_check __wmi_driver_register(struct wmi_driver *driver, 926844af950SAndy Lutomirski struct module *owner) 927844af950SAndy Lutomirski { 928844af950SAndy Lutomirski driver->driver.owner = owner; 929844af950SAndy Lutomirski driver->driver.bus = &wmi_bus_type; 930844af950SAndy Lutomirski 931844af950SAndy Lutomirski return driver_register(&driver->driver); 932844af950SAndy Lutomirski } 933844af950SAndy Lutomirski EXPORT_SYMBOL(__wmi_driver_register); 934844af950SAndy Lutomirski 935844af950SAndy Lutomirski void wmi_driver_unregister(struct wmi_driver *driver) 936844af950SAndy Lutomirski { 937844af950SAndy Lutomirski driver_unregister(&driver->driver); 938844af950SAndy Lutomirski } 939844af950SAndy Lutomirski EXPORT_SYMBOL(wmi_driver_unregister); 940844af950SAndy Lutomirski 941b4f9fe12SLen Brown static int __init acpi_wmi_init(void) 942b4f9fe12SLen Brown { 943c64eefd4SDmitry Torokhov int error; 944b4f9fe12SLen Brown 945b4f9fe12SLen Brown if (acpi_disabled) 946b4f9fe12SLen Brown return -ENODEV; 947b4f9fe12SLen Brown 948844af950SAndy Lutomirski error = class_register(&wmi_bus_class); 949c64eefd4SDmitry Torokhov if (error) 950c64eefd4SDmitry Torokhov return error; 951b4f9fe12SLen Brown 952844af950SAndy Lutomirski error = bus_register(&wmi_bus_type); 953844af950SAndy Lutomirski if (error) 954844af950SAndy Lutomirski goto err_unreg_class; 955844af950SAndy Lutomirski 956c64eefd4SDmitry Torokhov error = acpi_bus_register_driver(&acpi_wmi_driver); 957c64eefd4SDmitry Torokhov if (error) { 958c64eefd4SDmitry Torokhov pr_err("Error loading mapper\n"); 959844af950SAndy Lutomirski goto err_unreg_bus; 9601caab3c1SMatthew Garrett } 9611caab3c1SMatthew Garrett 9628e07514dSDmitry Torokhov return 0; 963844af950SAndy Lutomirski 964844af950SAndy Lutomirski err_unreg_class: 965844af950SAndy Lutomirski class_unregister(&wmi_bus_class); 966844af950SAndy Lutomirski 967844af950SAndy Lutomirski err_unreg_bus: 968844af950SAndy Lutomirski bus_unregister(&wmi_bus_type); 969844af950SAndy Lutomirski 970844af950SAndy Lutomirski return error; 971b4f9fe12SLen Brown } 972b4f9fe12SLen Brown 973b4f9fe12SLen Brown static void __exit acpi_wmi_exit(void) 974b4f9fe12SLen Brown { 975b4f9fe12SLen Brown acpi_bus_unregister_driver(&acpi_wmi_driver); 976844af950SAndy Lutomirski class_unregister(&wmi_bus_class); 977844af950SAndy Lutomirski bus_unregister(&wmi_bus_type); 978b4f9fe12SLen Brown } 979b4f9fe12SLen Brown 980b4f9fe12SLen Brown subsys_initcall(acpi_wmi_init); 981b4f9fe12SLen Brown module_exit(acpi_wmi_exit); 982