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 * 112c9c5664SDarren Hart (VMware) * WMI bus infrastructure by Andrew Lutomirski and Darren Hart: 122c9c5664SDarren Hart (VMware) * Copyright (C) 2015 Andrew Lutomirski 132c9c5664SDarren Hart (VMware) * Copyright (C) 2017 VMware, Inc. All Rights Reserved. 142c9c5664SDarren Hart (VMware) * 15b4f9fe12SLen Brown * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 16b4f9fe12SLen Brown * 17b4f9fe12SLen Brown * This program is free software; you can redistribute it and/or modify 18b4f9fe12SLen Brown * it under the terms of the GNU General Public License as published by 19b4f9fe12SLen Brown * the Free Software Foundation; either version 2 of the License, or (at 20b4f9fe12SLen Brown * your option) any later version. 21b4f9fe12SLen Brown * 22b4f9fe12SLen Brown * This program is distributed in the hope that it will be useful, but 23b4f9fe12SLen Brown * WITHOUT ANY WARRANTY; without even the implied warranty of 24b4f9fe12SLen Brown * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 25b4f9fe12SLen Brown * General Public License for more details. 26b4f9fe12SLen Brown * 27b4f9fe12SLen Brown * You should have received a copy of the GNU General Public License along 28b4f9fe12SLen Brown * with this program; if not, write to the Free Software Foundation, Inc., 29b4f9fe12SLen Brown * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 30b4f9fe12SLen Brown * 31b4f9fe12SLen Brown * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 32b4f9fe12SLen Brown */ 33b4f9fe12SLen Brown 348e07514dSDmitry Torokhov #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 358e07514dSDmitry Torokhov 36b4f9fe12SLen Brown #include <linux/acpi.h> 37b60ee4e0SMario Limonciello #include <linux/device.h> 38b60ee4e0SMario Limonciello #include <linux/init.h> 39b60ee4e0SMario Limonciello #include <linux/kernel.h> 40b60ee4e0SMario Limonciello #include <linux/list.h> 417c52d551SPaul Gortmaker #include <linux/module.h> 429599ed91SAndy Lutomirski #include <linux/platform_device.h> 43b60ee4e0SMario Limonciello #include <linux/slab.h> 44b60ee4e0SMario Limonciello #include <linux/types.h> 45538d7eb8SAndy Shevchenko #include <linux/uuid.h> 46b60ee4e0SMario Limonciello #include <linux/wmi.h> 47b4f9fe12SLen Brown 48b4f9fe12SLen Brown ACPI_MODULE_NAME("wmi"); 49b4f9fe12SLen Brown MODULE_AUTHOR("Carlos Corbacho"); 50b4f9fe12SLen Brown MODULE_DESCRIPTION("ACPI-WMI Mapping Driver"); 51b4f9fe12SLen Brown MODULE_LICENSE("GPL"); 52b4f9fe12SLen Brown 53762e1a2fSDmitry Torokhov static LIST_HEAD(wmi_block_list); 54b4f9fe12SLen Brown 55b4f9fe12SLen Brown struct guid_block { 56b4f9fe12SLen Brown char guid[16]; 57b4f9fe12SLen Brown union { 58b4f9fe12SLen Brown char object_id[2]; 59b4f9fe12SLen Brown struct { 60b4f9fe12SLen Brown unsigned char notify_id; 61b4f9fe12SLen Brown unsigned char reserved; 62b4f9fe12SLen Brown }; 63b4f9fe12SLen Brown }; 64b4f9fe12SLen Brown u8 instance_count; 65b4f9fe12SLen Brown u8 flags; 66b4f9fe12SLen Brown }; 67b4f9fe12SLen Brown 68b4f9fe12SLen Brown struct wmi_block { 69844af950SAndy Lutomirski struct wmi_device dev; 70b4f9fe12SLen Brown struct list_head list; 71b4f9fe12SLen Brown struct guid_block gblock; 72b0e86302SAndy Lutomirski struct acpi_device *acpi_device; 73b4f9fe12SLen Brown wmi_notify_handler handler; 74b4f9fe12SLen Brown void *handler_data; 75d4fc91adSAndy Lutomirski 76fd70da6aSDarren Hart (VMware) bool read_takes_no_args; 77b4f9fe12SLen Brown }; 78b4f9fe12SLen Brown 79b4f9fe12SLen Brown 80b4f9fe12SLen Brown /* 81b4f9fe12SLen Brown * If the GUID data block is marked as expensive, we must enable and 82b4f9fe12SLen Brown * explicitily disable data collection. 83b4f9fe12SLen Brown */ 84b4f9fe12SLen Brown #define ACPI_WMI_EXPENSIVE 0x1 85b4f9fe12SLen Brown #define ACPI_WMI_METHOD 0x2 /* GUID is a method */ 86b4f9fe12SLen Brown #define ACPI_WMI_STRING 0x4 /* GUID takes & returns a string */ 87b4f9fe12SLen Brown #define ACPI_WMI_EVENT 0x8 /* GUID is an event */ 88b4f9fe12SLen Brown 8990ab5ee9SRusty Russell static bool debug_event; 90fc3155b2SThomas Renninger module_param(debug_event, bool, 0444); 91fc3155b2SThomas Renninger MODULE_PARM_DESC(debug_event, 92fc3155b2SThomas Renninger "Log WMI Events [0/1]"); 93fc3155b2SThomas Renninger 9490ab5ee9SRusty Russell static bool debug_dump_wdg; 95a929aae0SThomas Renninger module_param(debug_dump_wdg, bool, 0444); 96a929aae0SThomas Renninger MODULE_PARM_DESC(debug_dump_wdg, 97a929aae0SThomas Renninger "Dump available WMI interfaces [0/1]"); 98a929aae0SThomas Renninger 999599ed91SAndy Lutomirski static int acpi_wmi_remove(struct platform_device *device); 1009599ed91SAndy Lutomirski static int acpi_wmi_probe(struct platform_device *device); 101b4f9fe12SLen Brown 102b4f9fe12SLen Brown static const struct acpi_device_id wmi_device_ids[] = { 103b4f9fe12SLen Brown {"PNP0C14", 0}, 104b4f9fe12SLen Brown {"pnp0c14", 0}, 105b4f9fe12SLen Brown {"", 0}, 106b4f9fe12SLen Brown }; 107b4f9fe12SLen Brown MODULE_DEVICE_TABLE(acpi, wmi_device_ids); 108b4f9fe12SLen Brown 1099599ed91SAndy Lutomirski static struct platform_driver acpi_wmi_driver = { 1109599ed91SAndy Lutomirski .driver = { 111844af950SAndy Lutomirski .name = "acpi-wmi", 1129599ed91SAndy Lutomirski .acpi_match_table = wmi_device_ids, 113b4f9fe12SLen Brown }, 1149599ed91SAndy Lutomirski .probe = acpi_wmi_probe, 1159599ed91SAndy Lutomirski .remove = acpi_wmi_remove, 116b4f9fe12SLen Brown }; 117b4f9fe12SLen Brown 118b4f9fe12SLen Brown /* 119b4f9fe12SLen Brown * GUID parsing functions 120b4f9fe12SLen Brown */ 121b4f9fe12SLen Brown 122b4f9fe12SLen Brown static bool find_guid(const char *guid_string, struct wmi_block **out) 123b4f9fe12SLen Brown { 124538d7eb8SAndy Shevchenko uuid_le guid_input; 125b4f9fe12SLen Brown struct wmi_block *wblock; 126b4f9fe12SLen Brown struct guid_block *block; 127b4f9fe12SLen Brown struct list_head *p; 128b4f9fe12SLen Brown 129538d7eb8SAndy Shevchenko if (uuid_le_to_bin(guid_string, &guid_input)) 130538d7eb8SAndy Shevchenko return false; 131b4f9fe12SLen Brown 132762e1a2fSDmitry Torokhov list_for_each(p, &wmi_block_list) { 133b4f9fe12SLen Brown wblock = list_entry(p, struct wmi_block, list); 134b4f9fe12SLen Brown block = &wblock->gblock; 135b4f9fe12SLen Brown 136538d7eb8SAndy Shevchenko if (memcmp(block->guid, &guid_input, 16) == 0) { 137b4f9fe12SLen Brown if (out) 138b4f9fe12SLen Brown *out = wblock; 139097c27fcSJoe Perches return true; 140b4f9fe12SLen Brown } 141b4f9fe12SLen Brown } 142097c27fcSJoe Perches return false; 143b4f9fe12SLen Brown } 144b4f9fe12SLen Brown 145d4fc91adSAndy Lutomirski static int get_subobj_info(acpi_handle handle, const char *pathname, 146d4fc91adSAndy Lutomirski struct acpi_device_info **info) 147d4fc91adSAndy Lutomirski { 148d4fc91adSAndy Lutomirski struct acpi_device_info *dummy_info, **info_ptr; 149d4fc91adSAndy Lutomirski acpi_handle subobj_handle; 150d4fc91adSAndy Lutomirski acpi_status status; 151d4fc91adSAndy Lutomirski 152d4fc91adSAndy Lutomirski status = acpi_get_handle(handle, (char *)pathname, &subobj_handle); 153d4fc91adSAndy Lutomirski if (status == AE_NOT_FOUND) 154d4fc91adSAndy Lutomirski return -ENOENT; 155d4fc91adSAndy Lutomirski else if (ACPI_FAILURE(status)) 156d4fc91adSAndy Lutomirski return -EIO; 157d4fc91adSAndy Lutomirski 158d4fc91adSAndy Lutomirski info_ptr = info ? info : &dummy_info; 159d4fc91adSAndy Lutomirski status = acpi_get_object_info(subobj_handle, info_ptr); 160d4fc91adSAndy Lutomirski if (ACPI_FAILURE(status)) 161d4fc91adSAndy Lutomirski return -EIO; 162d4fc91adSAndy Lutomirski 163d4fc91adSAndy Lutomirski if (!info) 164d4fc91adSAndy Lutomirski kfree(dummy_info); 165d4fc91adSAndy Lutomirski 166d4fc91adSAndy Lutomirski return 0; 167d4fc91adSAndy Lutomirski } 168d4fc91adSAndy Lutomirski 169b4f9fe12SLen Brown static acpi_status wmi_method_enable(struct wmi_block *wblock, int enable) 170b4f9fe12SLen Brown { 171b4f9fe12SLen Brown struct guid_block *block = NULL; 172b4f9fe12SLen Brown char method[5]; 173b4f9fe12SLen Brown acpi_status status; 174b4f9fe12SLen Brown acpi_handle handle; 175b4f9fe12SLen Brown 176b4f9fe12SLen Brown block = &wblock->gblock; 177b0e86302SAndy Lutomirski handle = wblock->acpi_device->handle; 178b4f9fe12SLen Brown 179b4f9fe12SLen Brown snprintf(method, 5, "WE%02X", block->notify_id); 1808122ab66SZhang Rui status = acpi_execute_simple_method(handle, method, enable); 181b4f9fe12SLen Brown 182b4f9fe12SLen Brown if (status != AE_OK && status != AE_NOT_FOUND) 183b4f9fe12SLen Brown return status; 184b4f9fe12SLen Brown else 185b4f9fe12SLen Brown return AE_OK; 186b4f9fe12SLen Brown } 187b4f9fe12SLen Brown 188b4f9fe12SLen Brown /* 189b4f9fe12SLen Brown * Exported WMI functions 190b4f9fe12SLen Brown */ 191b4f9fe12SLen Brown /** 192b4f9fe12SLen Brown * wmi_evaluate_method - Evaluate a WMI method 193b4f9fe12SLen Brown * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba 194b4f9fe12SLen Brown * @instance: Instance index 195b4f9fe12SLen Brown * @method_id: Method ID to call 196b4f9fe12SLen Brown * &in: Buffer containing input for the method call 197b4f9fe12SLen Brown * &out: Empty buffer to return the method results 198b4f9fe12SLen Brown * 199b4f9fe12SLen Brown * Call an ACPI-WMI method 200b4f9fe12SLen Brown */ 201b4f9fe12SLen Brown acpi_status wmi_evaluate_method(const char *guid_string, u8 instance, 202b4f9fe12SLen Brown u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out) 203b4f9fe12SLen Brown { 204722c856dSMario Limonciello struct wmi_block *wblock = NULL; 205722c856dSMario Limonciello 206722c856dSMario Limonciello if (!find_guid(guid_string, &wblock)) 207722c856dSMario Limonciello return AE_ERROR; 208722c856dSMario Limonciello return wmidev_evaluate_method(&wblock->dev, instance, method_id, 209722c856dSMario Limonciello in, out); 210722c856dSMario Limonciello } 211722c856dSMario Limonciello EXPORT_SYMBOL_GPL(wmi_evaluate_method); 212722c856dSMario Limonciello 213722c856dSMario Limonciello /** 214722c856dSMario Limonciello * wmidev_evaluate_method - Evaluate a WMI method 215722c856dSMario Limonciello * @wdev: A wmi bus device from a driver 216722c856dSMario Limonciello * @instance: Instance index 217722c856dSMario Limonciello * @method_id: Method ID to call 218722c856dSMario Limonciello * &in: Buffer containing input for the method call 219722c856dSMario Limonciello * &out: Empty buffer to return the method results 220722c856dSMario Limonciello * 221722c856dSMario Limonciello * Call an ACPI-WMI method 222722c856dSMario Limonciello */ 223722c856dSMario Limonciello acpi_status wmidev_evaluate_method(struct wmi_device *wdev, u8 instance, 224722c856dSMario Limonciello u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out) 225722c856dSMario Limonciello { 226b4f9fe12SLen Brown struct guid_block *block = NULL; 227b4f9fe12SLen Brown struct wmi_block *wblock = NULL; 228b4f9fe12SLen Brown acpi_handle handle; 229b4f9fe12SLen Brown acpi_status status; 230b4f9fe12SLen Brown struct acpi_object_list input; 231b4f9fe12SLen Brown union acpi_object params[3]; 232f3d83e24SCostantino Leandro char method[5] = "WM"; 233b4f9fe12SLen Brown 234722c856dSMario Limonciello wblock = container_of(wdev, struct wmi_block, dev); 235b4f9fe12SLen Brown block = &wblock->gblock; 236b0e86302SAndy Lutomirski handle = wblock->acpi_device->handle; 237b4f9fe12SLen Brown 238b4f9fe12SLen Brown if (!(block->flags & ACPI_WMI_METHOD)) 239b4f9fe12SLen Brown return AE_BAD_DATA; 240b4f9fe12SLen Brown 2416afa1e2aSPali Rohár if (block->instance_count <= instance) 242b4f9fe12SLen Brown return AE_BAD_PARAMETER; 243b4f9fe12SLen Brown 244b4f9fe12SLen Brown input.count = 2; 245b4f9fe12SLen Brown input.pointer = params; 246b4f9fe12SLen Brown params[0].type = ACPI_TYPE_INTEGER; 247b4f9fe12SLen Brown params[0].integer.value = instance; 248b4f9fe12SLen Brown params[1].type = ACPI_TYPE_INTEGER; 249b4f9fe12SLen Brown params[1].integer.value = method_id; 250b4f9fe12SLen Brown 251b4f9fe12SLen Brown if (in) { 252b4f9fe12SLen Brown input.count = 3; 253b4f9fe12SLen Brown 254b4f9fe12SLen Brown if (block->flags & ACPI_WMI_STRING) { 255b4f9fe12SLen Brown params[2].type = ACPI_TYPE_STRING; 256b4f9fe12SLen Brown } else { 257b4f9fe12SLen Brown params[2].type = ACPI_TYPE_BUFFER; 258b4f9fe12SLen Brown } 259b4f9fe12SLen Brown params[2].buffer.length = in->length; 260b4f9fe12SLen Brown params[2].buffer.pointer = in->pointer; 261b4f9fe12SLen Brown } 262b4f9fe12SLen Brown 263b4f9fe12SLen Brown strncat(method, block->object_id, 2); 264b4f9fe12SLen Brown 265b4f9fe12SLen Brown status = acpi_evaluate_object(handle, method, &input, out); 266b4f9fe12SLen Brown 267b4f9fe12SLen Brown return status; 268b4f9fe12SLen Brown } 269722c856dSMario Limonciello EXPORT_SYMBOL_GPL(wmidev_evaluate_method); 270b4f9fe12SLen Brown 27156a37025SAndy Lutomirski static acpi_status __query_block(struct wmi_block *wblock, u8 instance, 272b4f9fe12SLen Brown struct acpi_buffer *out) 273b4f9fe12SLen Brown { 274b4f9fe12SLen Brown struct guid_block *block = NULL; 27554f14c27SZhang Rui acpi_handle handle; 276b4f9fe12SLen Brown acpi_status status, wc_status = AE_ERROR; 2778122ab66SZhang Rui struct acpi_object_list input; 2788122ab66SZhang Rui union acpi_object wq_params[1]; 279f3d83e24SCostantino Leandro char method[5]; 280f3d83e24SCostantino Leandro char wc_method[5] = "WC"; 281b4f9fe12SLen Brown 28256a37025SAndy Lutomirski if (!out) 283b4f9fe12SLen Brown return AE_BAD_PARAMETER; 284b4f9fe12SLen Brown 285b4f9fe12SLen Brown block = &wblock->gblock; 286b0e86302SAndy Lutomirski handle = wblock->acpi_device->handle; 287b4f9fe12SLen Brown 2886afa1e2aSPali Rohár if (block->instance_count <= instance) 289b4f9fe12SLen Brown return AE_BAD_PARAMETER; 290b4f9fe12SLen Brown 291b4f9fe12SLen Brown /* Check GUID is a data block */ 292b4f9fe12SLen Brown if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD)) 293b4f9fe12SLen Brown return AE_ERROR; 294b4f9fe12SLen Brown 295b4f9fe12SLen Brown input.count = 1; 296b4f9fe12SLen Brown input.pointer = wq_params; 297b4f9fe12SLen Brown wq_params[0].type = ACPI_TYPE_INTEGER; 298b4f9fe12SLen Brown wq_params[0].integer.value = instance; 299b4f9fe12SLen Brown 300d4fc91adSAndy Lutomirski if (instance == 0 && wblock->read_takes_no_args) 301d4fc91adSAndy Lutomirski input.count = 0; 302d4fc91adSAndy Lutomirski 303b4f9fe12SLen Brown /* 304b4f9fe12SLen Brown * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method first to 305b4f9fe12SLen Brown * enable collection. 306b4f9fe12SLen Brown */ 307b4f9fe12SLen Brown if (block->flags & ACPI_WMI_EXPENSIVE) { 308b4f9fe12SLen Brown strncat(wc_method, block->object_id, 2); 309b4f9fe12SLen Brown 310b4f9fe12SLen Brown /* 311b4f9fe12SLen Brown * Some GUIDs break the specification by declaring themselves 312b4f9fe12SLen Brown * expensive, but have no corresponding WCxx method. So we 313b4f9fe12SLen Brown * should not fail if this happens. 314b4f9fe12SLen Brown */ 31554f14c27SZhang Rui if (acpi_has_method(handle, wc_method)) 3168122ab66SZhang Rui wc_status = acpi_execute_simple_method(handle, 3178122ab66SZhang Rui wc_method, 1); 318b4f9fe12SLen Brown } 319b4f9fe12SLen Brown 320b4f9fe12SLen Brown strcpy(method, "WQ"); 321b4f9fe12SLen Brown strncat(method, block->object_id, 2); 322b4f9fe12SLen Brown 323b4f9fe12SLen Brown status = acpi_evaluate_object(handle, method, &input, out); 324b4f9fe12SLen Brown 325b4f9fe12SLen Brown /* 326b4f9fe12SLen Brown * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method, even if 327b4f9fe12SLen Brown * the WQxx method failed - we should disable collection anyway. 328b4f9fe12SLen Brown */ 329b4f9fe12SLen Brown if ((block->flags & ACPI_WMI_EXPENSIVE) && ACPI_SUCCESS(wc_status)) { 3308122ab66SZhang Rui status = acpi_execute_simple_method(handle, wc_method, 0); 331b4f9fe12SLen Brown } 332b4f9fe12SLen Brown 333b4f9fe12SLen Brown return status; 334b4f9fe12SLen Brown } 33556a37025SAndy Lutomirski 33656a37025SAndy Lutomirski /** 33756a37025SAndy Lutomirski * wmi_query_block - Return contents of a WMI block (deprecated) 33856a37025SAndy Lutomirski * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba 33956a37025SAndy Lutomirski * @instance: Instance index 34056a37025SAndy Lutomirski * &out: Empty buffer to return the contents of the data block to 34156a37025SAndy Lutomirski * 34256a37025SAndy Lutomirski * Return the contents of an ACPI-WMI data block to a buffer 34356a37025SAndy Lutomirski */ 34456a37025SAndy Lutomirski acpi_status wmi_query_block(const char *guid_string, u8 instance, 34556a37025SAndy Lutomirski struct acpi_buffer *out) 34656a37025SAndy Lutomirski { 34756a37025SAndy Lutomirski struct wmi_block *wblock; 34856a37025SAndy Lutomirski 34956a37025SAndy Lutomirski if (!guid_string) 35056a37025SAndy Lutomirski return AE_BAD_PARAMETER; 35156a37025SAndy Lutomirski 35256a37025SAndy Lutomirski if (!find_guid(guid_string, &wblock)) 35356a37025SAndy Lutomirski return AE_ERROR; 35456a37025SAndy Lutomirski 35556a37025SAndy Lutomirski return __query_block(wblock, instance, out); 35656a37025SAndy Lutomirski } 357b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_query_block); 358b4f9fe12SLen Brown 35956a37025SAndy Lutomirski union acpi_object *wmidev_block_query(struct wmi_device *wdev, u8 instance) 36056a37025SAndy Lutomirski { 36156a37025SAndy Lutomirski struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL }; 36256a37025SAndy Lutomirski struct wmi_block *wblock = container_of(wdev, struct wmi_block, dev); 36356a37025SAndy Lutomirski 36456a37025SAndy Lutomirski if (ACPI_FAILURE(__query_block(wblock, instance, &out))) 36556a37025SAndy Lutomirski return NULL; 36656a37025SAndy Lutomirski 36756a37025SAndy Lutomirski return (union acpi_object *)out.pointer; 36856a37025SAndy Lutomirski } 36956a37025SAndy Lutomirski EXPORT_SYMBOL_GPL(wmidev_block_query); 37056a37025SAndy Lutomirski 371f6301986SAndy Lutomirski struct wmi_device *wmidev_get_other_guid(struct wmi_device *wdev, 372f6301986SAndy Lutomirski const char *guid_string) 373f6301986SAndy Lutomirski { 374f6301986SAndy Lutomirski struct wmi_block *this_wb = container_of(wdev, struct wmi_block, dev); 375f6301986SAndy Lutomirski struct wmi_block *other_wb; 376f6301986SAndy Lutomirski 377f6301986SAndy Lutomirski if (!find_guid(guid_string, &other_wb)) 378f6301986SAndy Lutomirski return NULL; 379f6301986SAndy Lutomirski 380f6301986SAndy Lutomirski if (other_wb->acpi_device != this_wb->acpi_device) 381f6301986SAndy Lutomirski return NULL; 382f6301986SAndy Lutomirski 383f6301986SAndy Lutomirski get_device(&other_wb->dev.dev); 384f6301986SAndy Lutomirski return &other_wb->dev; 385f6301986SAndy Lutomirski } 386f6301986SAndy Lutomirski EXPORT_SYMBOL_GPL(wmidev_get_other_guid); 387f6301986SAndy Lutomirski 388b4f9fe12SLen Brown /** 389b4f9fe12SLen Brown * wmi_set_block - Write to a WMI block 390b4f9fe12SLen Brown * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba 391b4f9fe12SLen Brown * @instance: Instance index 392b4f9fe12SLen Brown * &in: Buffer containing new values for the data block 393b4f9fe12SLen Brown * 394b4f9fe12SLen Brown * Write the contents of the input buffer to an ACPI-WMI data block 395b4f9fe12SLen Brown */ 396b4f9fe12SLen Brown acpi_status wmi_set_block(const char *guid_string, u8 instance, 397b4f9fe12SLen Brown const struct acpi_buffer *in) 398b4f9fe12SLen Brown { 399b4f9fe12SLen Brown struct guid_block *block = NULL; 400b4f9fe12SLen Brown struct wmi_block *wblock = NULL; 401b4f9fe12SLen Brown acpi_handle handle; 402b4f9fe12SLen Brown struct acpi_object_list input; 403b4f9fe12SLen Brown union acpi_object params[2]; 404f3d83e24SCostantino Leandro char method[5] = "WS"; 405b4f9fe12SLen Brown 406b4f9fe12SLen Brown if (!guid_string || !in) 407b4f9fe12SLen Brown return AE_BAD_DATA; 408b4f9fe12SLen Brown 409b4f9fe12SLen Brown if (!find_guid(guid_string, &wblock)) 410b4f9fe12SLen Brown return AE_ERROR; 411b4f9fe12SLen Brown 412b4f9fe12SLen Brown block = &wblock->gblock; 413b0e86302SAndy Lutomirski handle = wblock->acpi_device->handle; 414b4f9fe12SLen Brown 4156afa1e2aSPali Rohár if (block->instance_count <= instance) 416b4f9fe12SLen Brown return AE_BAD_PARAMETER; 417b4f9fe12SLen Brown 418b4f9fe12SLen Brown /* Check GUID is a data block */ 419b4f9fe12SLen Brown if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD)) 420b4f9fe12SLen Brown return AE_ERROR; 421b4f9fe12SLen Brown 422b4f9fe12SLen Brown input.count = 2; 423b4f9fe12SLen Brown input.pointer = params; 424b4f9fe12SLen Brown params[0].type = ACPI_TYPE_INTEGER; 425b4f9fe12SLen Brown params[0].integer.value = instance; 426b4f9fe12SLen Brown 427b4f9fe12SLen Brown if (block->flags & ACPI_WMI_STRING) { 428b4f9fe12SLen Brown params[1].type = ACPI_TYPE_STRING; 429b4f9fe12SLen Brown } else { 430b4f9fe12SLen Brown params[1].type = ACPI_TYPE_BUFFER; 431b4f9fe12SLen Brown } 432b4f9fe12SLen Brown params[1].buffer.length = in->length; 433b4f9fe12SLen Brown params[1].buffer.pointer = in->pointer; 434b4f9fe12SLen Brown 435b4f9fe12SLen Brown strncat(method, block->object_id, 2); 436b4f9fe12SLen Brown 437b4f9fe12SLen Brown return acpi_evaluate_object(handle, method, &input, NULL); 438b4f9fe12SLen Brown } 439b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_set_block); 440b4f9fe12SLen Brown 44137830662SDmitry Torokhov static void wmi_dump_wdg(const struct guid_block *g) 442a929aae0SThomas Renninger { 44385b4e4ebSRasmus Villemoes pr_info("%pUL:\n", g->guid); 444cd3921f8SPali Rohár if (g->flags & ACPI_WMI_EVENT) 445cd3921f8SPali Rohár pr_info("\tnotify_id: 0x%02X\n", g->notify_id); 446cd3921f8SPali Rohár else 447cd3921f8SPali Rohár pr_info("\tobject_id: %2pE\n", g->object_id); 4488e07514dSDmitry Torokhov pr_info("\tinstance_count: %d\n", g->instance_count); 4498e07514dSDmitry Torokhov pr_info("\tflags: %#x", g->flags); 450a929aae0SThomas Renninger if (g->flags) { 451a929aae0SThomas Renninger if (g->flags & ACPI_WMI_EXPENSIVE) 4528e07514dSDmitry Torokhov pr_cont(" ACPI_WMI_EXPENSIVE"); 453a929aae0SThomas Renninger if (g->flags & ACPI_WMI_METHOD) 4548e07514dSDmitry Torokhov pr_cont(" ACPI_WMI_METHOD"); 455a929aae0SThomas Renninger if (g->flags & ACPI_WMI_STRING) 4568e07514dSDmitry Torokhov pr_cont(" ACPI_WMI_STRING"); 457a929aae0SThomas Renninger if (g->flags & ACPI_WMI_EVENT) 4588e07514dSDmitry Torokhov pr_cont(" ACPI_WMI_EVENT"); 459a929aae0SThomas Renninger } 4608e07514dSDmitry Torokhov pr_cont("\n"); 461a929aae0SThomas Renninger 462a929aae0SThomas Renninger } 463a929aae0SThomas Renninger 464fc3155b2SThomas Renninger static void wmi_notify_debug(u32 value, void *context) 465fc3155b2SThomas Renninger { 466fc3155b2SThomas Renninger struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; 467fc3155b2SThomas Renninger union acpi_object *obj; 4681492616aSAxel Lin acpi_status status; 469fc3155b2SThomas Renninger 4701492616aSAxel Lin status = wmi_get_event_data(value, &response); 4711492616aSAxel Lin if (status != AE_OK) { 4728e07514dSDmitry Torokhov pr_info("bad event status 0x%x\n", status); 4731492616aSAxel Lin return; 4741492616aSAxel Lin } 475fc3155b2SThomas Renninger 476fc3155b2SThomas Renninger obj = (union acpi_object *)response.pointer; 477fc3155b2SThomas Renninger 478fc3155b2SThomas Renninger if (!obj) 479fc3155b2SThomas Renninger return; 480fc3155b2SThomas Renninger 4818e07514dSDmitry Torokhov pr_info("DEBUG Event "); 482fc3155b2SThomas Renninger switch(obj->type) { 483fc3155b2SThomas Renninger case ACPI_TYPE_BUFFER: 4848e07514dSDmitry Torokhov pr_cont("BUFFER_TYPE - length %d\n", obj->buffer.length); 485fc3155b2SThomas Renninger break; 486fc3155b2SThomas Renninger case ACPI_TYPE_STRING: 4878e07514dSDmitry Torokhov pr_cont("STRING_TYPE - %s\n", obj->string.pointer); 488fc3155b2SThomas Renninger break; 489fc3155b2SThomas Renninger case ACPI_TYPE_INTEGER: 4908e07514dSDmitry Torokhov pr_cont("INTEGER_TYPE - %llu\n", obj->integer.value); 491fc3155b2SThomas Renninger break; 492fc3155b2SThomas Renninger case ACPI_TYPE_PACKAGE: 4938e07514dSDmitry Torokhov pr_cont("PACKAGE_TYPE - %d elements\n", obj->package.count); 494fc3155b2SThomas Renninger break; 495fc3155b2SThomas Renninger default: 4968e07514dSDmitry Torokhov pr_cont("object type 0x%X\n", obj->type); 497fc3155b2SThomas Renninger } 4981492616aSAxel Lin kfree(obj); 499fc3155b2SThomas Renninger } 500fc3155b2SThomas Renninger 501b4f9fe12SLen Brown /** 502b4f9fe12SLen Brown * wmi_install_notify_handler - Register handler for WMI events 503b4f9fe12SLen Brown * @handler: Function to handle notifications 504b4f9fe12SLen Brown * @data: Data to be returned to handler when event is fired 505b4f9fe12SLen Brown * 506b4f9fe12SLen Brown * Register a handler for events sent to the ACPI-WMI mapper device. 507b4f9fe12SLen Brown */ 508b4f9fe12SLen Brown acpi_status wmi_install_notify_handler(const char *guid, 509b4f9fe12SLen Brown wmi_notify_handler handler, void *data) 510b4f9fe12SLen Brown { 511b4f9fe12SLen Brown struct wmi_block *block; 51258f6425eSColin King acpi_status status = AE_NOT_EXIST; 513538d7eb8SAndy Shevchenko uuid_le guid_input; 51458f6425eSColin King struct list_head *p; 515b4f9fe12SLen Brown 516b4f9fe12SLen Brown if (!guid || !handler) 517b4f9fe12SLen Brown return AE_BAD_PARAMETER; 518b4f9fe12SLen Brown 519538d7eb8SAndy Shevchenko if (uuid_le_to_bin(guid, &guid_input)) 520538d7eb8SAndy Shevchenko return AE_BAD_PARAMETER; 521b4f9fe12SLen Brown 52258f6425eSColin King list_for_each(p, &wmi_block_list) { 52358f6425eSColin King acpi_status wmi_status; 52458f6425eSColin King block = list_entry(p, struct wmi_block, list); 52558f6425eSColin King 526538d7eb8SAndy Shevchenko if (memcmp(block->gblock.guid, &guid_input, 16) == 0) { 52758f6425eSColin King if (block->handler && 52858f6425eSColin King block->handler != wmi_notify_debug) 529b4f9fe12SLen Brown return AE_ALREADY_ACQUIRED; 530b4f9fe12SLen Brown 531b4f9fe12SLen Brown block->handler = handler; 532b4f9fe12SLen Brown block->handler_data = data; 533b4f9fe12SLen Brown 53458f6425eSColin King wmi_status = wmi_method_enable(block, 1); 53558f6425eSColin King if ((wmi_status != AE_OK) || 53658f6425eSColin King ((wmi_status == AE_OK) && (status == AE_NOT_EXIST))) 53758f6425eSColin King status = wmi_status; 53858f6425eSColin King } 53958f6425eSColin King } 540b4f9fe12SLen Brown 541b4f9fe12SLen Brown return status; 542b4f9fe12SLen Brown } 543b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_install_notify_handler); 544b4f9fe12SLen Brown 545b4f9fe12SLen Brown /** 546b4f9fe12SLen Brown * wmi_uninstall_notify_handler - Unregister handler for WMI events 547b4f9fe12SLen Brown * 548b4f9fe12SLen Brown * Unregister handler for events sent to the ACPI-WMI mapper device. 549b4f9fe12SLen Brown */ 550b4f9fe12SLen Brown acpi_status wmi_remove_notify_handler(const char *guid) 551b4f9fe12SLen Brown { 552b4f9fe12SLen Brown struct wmi_block *block; 55358f6425eSColin King acpi_status status = AE_NOT_EXIST; 554538d7eb8SAndy Shevchenko uuid_le guid_input; 55558f6425eSColin King struct list_head *p; 556b4f9fe12SLen Brown 557b4f9fe12SLen Brown if (!guid) 558b4f9fe12SLen Brown return AE_BAD_PARAMETER; 559b4f9fe12SLen Brown 560538d7eb8SAndy Shevchenko if (uuid_le_to_bin(guid, &guid_input)) 561538d7eb8SAndy Shevchenko return AE_BAD_PARAMETER; 562b4f9fe12SLen Brown 56358f6425eSColin King list_for_each(p, &wmi_block_list) { 56458f6425eSColin King acpi_status wmi_status; 56558f6425eSColin King block = list_entry(p, struct wmi_block, list); 56658f6425eSColin King 567538d7eb8SAndy Shevchenko if (memcmp(block->gblock.guid, &guid_input, 16) == 0) { 56858f6425eSColin King if (!block->handler || 56958f6425eSColin King block->handler == wmi_notify_debug) 570b4f9fe12SLen Brown return AE_NULL_ENTRY; 571b4f9fe12SLen Brown 572fc3155b2SThomas Renninger if (debug_event) { 573fc3155b2SThomas Renninger block->handler = wmi_notify_debug; 57458f6425eSColin King status = AE_OK; 575fc3155b2SThomas Renninger } else { 57658f6425eSColin King wmi_status = wmi_method_enable(block, 0); 577b4f9fe12SLen Brown block->handler = NULL; 578b4f9fe12SLen Brown block->handler_data = NULL; 57958f6425eSColin King if ((wmi_status != AE_OK) || 58058f6425eSColin King ((wmi_status == AE_OK) && 58158f6425eSColin King (status == AE_NOT_EXIST))) 58258f6425eSColin King status = wmi_status; 583fc3155b2SThomas Renninger } 58458f6425eSColin King } 58558f6425eSColin King } 58658f6425eSColin King 587b4f9fe12SLen Brown return status; 588b4f9fe12SLen Brown } 589b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_remove_notify_handler); 590b4f9fe12SLen Brown 591b4f9fe12SLen Brown /** 592b4f9fe12SLen Brown * wmi_get_event_data - Get WMI data associated with an event 593b4f9fe12SLen Brown * 5943e9b988eSAnisse Astier * @event: Event to find 5953e9b988eSAnisse Astier * @out: Buffer to hold event data. out->pointer should be freed with kfree() 596b4f9fe12SLen Brown * 597b4f9fe12SLen Brown * Returns extra data associated with an event in WMI. 598b4f9fe12SLen Brown */ 599b4f9fe12SLen Brown acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out) 600b4f9fe12SLen Brown { 601b4f9fe12SLen Brown struct acpi_object_list input; 602b4f9fe12SLen Brown union acpi_object params[1]; 603b4f9fe12SLen Brown struct guid_block *gblock; 604b4f9fe12SLen Brown struct wmi_block *wblock; 605b4f9fe12SLen Brown struct list_head *p; 606b4f9fe12SLen Brown 607b4f9fe12SLen Brown input.count = 1; 608b4f9fe12SLen Brown input.pointer = params; 609b4f9fe12SLen Brown params[0].type = ACPI_TYPE_INTEGER; 610b4f9fe12SLen Brown params[0].integer.value = event; 611b4f9fe12SLen Brown 612762e1a2fSDmitry Torokhov list_for_each(p, &wmi_block_list) { 613b4f9fe12SLen Brown wblock = list_entry(p, struct wmi_block, list); 614b4f9fe12SLen Brown gblock = &wblock->gblock; 615b4f9fe12SLen Brown 616b4f9fe12SLen Brown if ((gblock->flags & ACPI_WMI_EVENT) && 617b4f9fe12SLen Brown (gblock->notify_id == event)) 618b0e86302SAndy Lutomirski return acpi_evaluate_object(wblock->acpi_device->handle, 619b0e86302SAndy Lutomirski "_WED", &input, out); 620b4f9fe12SLen Brown } 621b4f9fe12SLen Brown 622b4f9fe12SLen Brown return AE_NOT_FOUND; 623b4f9fe12SLen Brown } 624b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_get_event_data); 625b4f9fe12SLen Brown 626b4f9fe12SLen Brown /** 627b4f9fe12SLen Brown * wmi_has_guid - Check if a GUID is available 628b4f9fe12SLen Brown * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba 629b4f9fe12SLen Brown * 630b4f9fe12SLen Brown * Check if a given GUID is defined by _WDG 631b4f9fe12SLen Brown */ 632b4f9fe12SLen Brown bool wmi_has_guid(const char *guid_string) 633b4f9fe12SLen Brown { 634b4f9fe12SLen Brown return find_guid(guid_string, NULL); 635b4f9fe12SLen Brown } 636b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_has_guid); 637b4f9fe12SLen Brown 638844af950SAndy Lutomirski static struct wmi_block *dev_to_wblock(struct device *dev) 639844af950SAndy Lutomirski { 640844af950SAndy Lutomirski return container_of(dev, struct wmi_block, dev.dev); 641844af950SAndy Lutomirski } 642844af950SAndy Lutomirski 643844af950SAndy Lutomirski static struct wmi_device *dev_to_wdev(struct device *dev) 644844af950SAndy Lutomirski { 645844af950SAndy Lutomirski return container_of(dev, struct wmi_device, dev); 646844af950SAndy Lutomirski } 647844af950SAndy Lutomirski 648b4f9fe12SLen Brown /* 6491caab3c1SMatthew Garrett * sysfs interface 6501caab3c1SMatthew Garrett */ 651614ef432SDmitry Torokhov static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, 6521caab3c1SMatthew Garrett char *buf) 6531caab3c1SMatthew Garrett { 654844af950SAndy Lutomirski struct wmi_block *wblock = dev_to_wblock(dev); 6551caab3c1SMatthew Garrett 65685b4e4ebSRasmus Villemoes return sprintf(buf, "wmi:%pUL\n", wblock->gblock.guid); 6571caab3c1SMatthew Garrett } 658e80b89a5SGreg Kroah-Hartman static DEVICE_ATTR_RO(modalias); 659614ef432SDmitry Torokhov 660844af950SAndy Lutomirski static ssize_t guid_show(struct device *dev, struct device_attribute *attr, 661844af950SAndy Lutomirski char *buf) 662844af950SAndy Lutomirski { 663844af950SAndy Lutomirski struct wmi_block *wblock = dev_to_wblock(dev); 664844af950SAndy Lutomirski 665844af950SAndy Lutomirski return sprintf(buf, "%pUL\n", wblock->gblock.guid); 666844af950SAndy Lutomirski } 667844af950SAndy Lutomirski static DEVICE_ATTR_RO(guid); 668844af950SAndy Lutomirski 669d79b1074SAndy Lutomirski static ssize_t instance_count_show(struct device *dev, 670d79b1074SAndy Lutomirski struct device_attribute *attr, char *buf) 671d79b1074SAndy Lutomirski { 672d79b1074SAndy Lutomirski struct wmi_block *wblock = dev_to_wblock(dev); 673d79b1074SAndy Lutomirski 674d79b1074SAndy Lutomirski return sprintf(buf, "%d\n", (int)wblock->gblock.instance_count); 675d79b1074SAndy Lutomirski } 676d79b1074SAndy Lutomirski static DEVICE_ATTR_RO(instance_count); 677d79b1074SAndy Lutomirski 678d79b1074SAndy Lutomirski static ssize_t expensive_show(struct device *dev, 679d79b1074SAndy Lutomirski struct device_attribute *attr, char *buf) 680d79b1074SAndy Lutomirski { 681d79b1074SAndy Lutomirski struct wmi_block *wblock = dev_to_wblock(dev); 682d79b1074SAndy Lutomirski 683d79b1074SAndy Lutomirski return sprintf(buf, "%d\n", 684d79b1074SAndy Lutomirski (wblock->gblock.flags & ACPI_WMI_EXPENSIVE) != 0); 685d79b1074SAndy Lutomirski } 686d79b1074SAndy Lutomirski static DEVICE_ATTR_RO(expensive); 687d79b1074SAndy Lutomirski 688e80b89a5SGreg Kroah-Hartman static struct attribute *wmi_attrs[] = { 689e80b89a5SGreg Kroah-Hartman &dev_attr_modalias.attr, 690844af950SAndy Lutomirski &dev_attr_guid.attr, 691d79b1074SAndy Lutomirski &dev_attr_instance_count.attr, 692d79b1074SAndy Lutomirski &dev_attr_expensive.attr, 693e80b89a5SGreg Kroah-Hartman NULL, 694614ef432SDmitry Torokhov }; 695e80b89a5SGreg Kroah-Hartman ATTRIBUTE_GROUPS(wmi); 6961caab3c1SMatthew Garrett 697d79b1074SAndy Lutomirski static ssize_t notify_id_show(struct device *dev, struct device_attribute *attr, 698d79b1074SAndy Lutomirski char *buf) 699d79b1074SAndy Lutomirski { 700d79b1074SAndy Lutomirski struct wmi_block *wblock = dev_to_wblock(dev); 701d79b1074SAndy Lutomirski 702d79b1074SAndy Lutomirski return sprintf(buf, "%02X\n", (unsigned int)wblock->gblock.notify_id); 703d79b1074SAndy Lutomirski } 704d79b1074SAndy Lutomirski static DEVICE_ATTR_RO(notify_id); 705d79b1074SAndy Lutomirski 706d79b1074SAndy Lutomirski static struct attribute *wmi_event_attrs[] = { 707d79b1074SAndy Lutomirski &dev_attr_notify_id.attr, 708d79b1074SAndy Lutomirski NULL, 709d79b1074SAndy Lutomirski }; 710d79b1074SAndy Lutomirski ATTRIBUTE_GROUPS(wmi_event); 711d79b1074SAndy Lutomirski 712d79b1074SAndy Lutomirski static ssize_t object_id_show(struct device *dev, struct device_attribute *attr, 713d79b1074SAndy Lutomirski char *buf) 714d79b1074SAndy Lutomirski { 715d79b1074SAndy Lutomirski struct wmi_block *wblock = dev_to_wblock(dev); 716d79b1074SAndy Lutomirski 717d79b1074SAndy Lutomirski return sprintf(buf, "%c%c\n", wblock->gblock.object_id[0], 718d79b1074SAndy Lutomirski wblock->gblock.object_id[1]); 719d79b1074SAndy Lutomirski } 720d79b1074SAndy Lutomirski static DEVICE_ATTR_RO(object_id); 721d79b1074SAndy Lutomirski 722fd70da6aSDarren Hart (VMware) static ssize_t setable_show(struct device *dev, struct device_attribute *attr, 723d4fc91adSAndy Lutomirski char *buf) 724d4fc91adSAndy Lutomirski { 725d4fc91adSAndy Lutomirski struct wmi_device *wdev = dev_to_wdev(dev); 726d4fc91adSAndy Lutomirski 727fd70da6aSDarren Hart (VMware) return sprintf(buf, "%d\n", (int)wdev->setable); 728d4fc91adSAndy Lutomirski } 729fd70da6aSDarren Hart (VMware) static DEVICE_ATTR_RO(setable); 730d4fc91adSAndy Lutomirski 731d4fc91adSAndy Lutomirski static struct attribute *wmi_data_attrs[] = { 732d4fc91adSAndy Lutomirski &dev_attr_object_id.attr, 733fd70da6aSDarren Hart (VMware) &dev_attr_setable.attr, 734d4fc91adSAndy Lutomirski NULL, 735d4fc91adSAndy Lutomirski }; 736d4fc91adSAndy Lutomirski ATTRIBUTE_GROUPS(wmi_data); 737d4fc91adSAndy Lutomirski 738d4fc91adSAndy Lutomirski static struct attribute *wmi_method_attrs[] = { 739d79b1074SAndy Lutomirski &dev_attr_object_id.attr, 740d79b1074SAndy Lutomirski NULL, 741d79b1074SAndy Lutomirski }; 742d4fc91adSAndy Lutomirski ATTRIBUTE_GROUPS(wmi_method); 743d79b1074SAndy Lutomirski 7441caab3c1SMatthew Garrett static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env) 7451caab3c1SMatthew Garrett { 746844af950SAndy Lutomirski struct wmi_block *wblock = dev_to_wblock(dev); 7471caab3c1SMatthew Garrett 748844af950SAndy Lutomirski if (add_uevent_var(env, "MODALIAS=wmi:%pUL", wblock->gblock.guid)) 7491caab3c1SMatthew Garrett return -ENOMEM; 7501caab3c1SMatthew Garrett 751844af950SAndy Lutomirski if (add_uevent_var(env, "WMI_GUID=%pUL", wblock->gblock.guid)) 7521caab3c1SMatthew Garrett return -ENOMEM; 7531caab3c1SMatthew Garrett 7541caab3c1SMatthew Garrett return 0; 7551caab3c1SMatthew Garrett } 7561caab3c1SMatthew Garrett 757844af950SAndy Lutomirski static void wmi_dev_release(struct device *dev) 7581caab3c1SMatthew Garrett { 759844af950SAndy Lutomirski struct wmi_block *wblock = dev_to_wblock(dev); 760c64eefd4SDmitry Torokhov 761844af950SAndy Lutomirski kfree(wblock); 7621caab3c1SMatthew Garrett } 7631caab3c1SMatthew Garrett 764844af950SAndy Lutomirski static int wmi_dev_match(struct device *dev, struct device_driver *driver) 765844af950SAndy Lutomirski { 766844af950SAndy Lutomirski struct wmi_driver *wmi_driver = 767844af950SAndy Lutomirski container_of(driver, struct wmi_driver, driver); 768844af950SAndy Lutomirski struct wmi_block *wblock = dev_to_wblock(dev); 769844af950SAndy Lutomirski const struct wmi_device_id *id = wmi_driver->id_table; 770844af950SAndy Lutomirski 771844af950SAndy Lutomirski while (id->guid_string) { 772844af950SAndy Lutomirski uuid_le driver_guid; 773844af950SAndy Lutomirski 774844af950SAndy Lutomirski if (WARN_ON(uuid_le_to_bin(id->guid_string, &driver_guid))) 775844af950SAndy Lutomirski continue; 776844af950SAndy Lutomirski if (!memcmp(&driver_guid, wblock->gblock.guid, 16)) 777844af950SAndy Lutomirski return 1; 778844af950SAndy Lutomirski 779844af950SAndy Lutomirski id++; 780844af950SAndy Lutomirski } 781844af950SAndy Lutomirski 782844af950SAndy Lutomirski return 0; 783844af950SAndy Lutomirski } 784844af950SAndy Lutomirski 785844af950SAndy Lutomirski static int wmi_dev_probe(struct device *dev) 786844af950SAndy Lutomirski { 787844af950SAndy Lutomirski struct wmi_block *wblock = dev_to_wblock(dev); 788844af950SAndy Lutomirski struct wmi_driver *wdriver = 789844af950SAndy Lutomirski container_of(dev->driver, struct wmi_driver, driver); 790844af950SAndy Lutomirski int ret = 0; 791844af950SAndy Lutomirski 792844af950SAndy Lutomirski if (ACPI_FAILURE(wmi_method_enable(wblock, 1))) 793844af950SAndy Lutomirski dev_warn(dev, "failed to enable device -- probing anyway\n"); 794844af950SAndy Lutomirski 795844af950SAndy Lutomirski if (wdriver->probe) { 796844af950SAndy Lutomirski ret = wdriver->probe(dev_to_wdev(dev)); 797844af950SAndy Lutomirski if (ret != 0 && ACPI_FAILURE(wmi_method_enable(wblock, 0))) 798844af950SAndy Lutomirski dev_warn(dev, "failed to disable device\n"); 799844af950SAndy Lutomirski } 800844af950SAndy Lutomirski 801844af950SAndy Lutomirski return ret; 802844af950SAndy Lutomirski } 803844af950SAndy Lutomirski 804844af950SAndy Lutomirski static int wmi_dev_remove(struct device *dev) 805844af950SAndy Lutomirski { 806844af950SAndy Lutomirski struct wmi_block *wblock = dev_to_wblock(dev); 807844af950SAndy Lutomirski struct wmi_driver *wdriver = 808844af950SAndy Lutomirski container_of(dev->driver, struct wmi_driver, driver); 809844af950SAndy Lutomirski int ret = 0; 810844af950SAndy Lutomirski 811844af950SAndy Lutomirski if (wdriver->remove) 812844af950SAndy Lutomirski ret = wdriver->remove(dev_to_wdev(dev)); 813844af950SAndy Lutomirski 814844af950SAndy Lutomirski if (ACPI_FAILURE(wmi_method_enable(wblock, 0))) 815844af950SAndy Lutomirski dev_warn(dev, "failed to disable device\n"); 816844af950SAndy Lutomirski 817844af950SAndy Lutomirski return ret; 818844af950SAndy Lutomirski } 819844af950SAndy Lutomirski 820844af950SAndy Lutomirski static struct class wmi_bus_class = { 821844af950SAndy Lutomirski .name = "wmi_bus", 8221caab3c1SMatthew Garrett }; 8231caab3c1SMatthew Garrett 824844af950SAndy Lutomirski static struct bus_type wmi_bus_type = { 825844af950SAndy Lutomirski .name = "wmi", 826844af950SAndy Lutomirski .dev_groups = wmi_groups, 827844af950SAndy Lutomirski .match = wmi_dev_match, 828844af950SAndy Lutomirski .uevent = wmi_dev_uevent, 829844af950SAndy Lutomirski .probe = wmi_dev_probe, 830844af950SAndy Lutomirski .remove = wmi_dev_remove, 831844af950SAndy Lutomirski }; 832844af950SAndy Lutomirski 833d79b1074SAndy Lutomirski static struct device_type wmi_type_event = { 834d79b1074SAndy Lutomirski .name = "event", 835d79b1074SAndy Lutomirski .groups = wmi_event_groups, 836d79b1074SAndy Lutomirski .release = wmi_dev_release, 837d79b1074SAndy Lutomirski }; 838d79b1074SAndy Lutomirski 839d79b1074SAndy Lutomirski static struct device_type wmi_type_method = { 840d79b1074SAndy Lutomirski .name = "method", 841d4fc91adSAndy Lutomirski .groups = wmi_method_groups, 842d79b1074SAndy Lutomirski .release = wmi_dev_release, 843d79b1074SAndy Lutomirski }; 844d79b1074SAndy Lutomirski 845d79b1074SAndy Lutomirski static struct device_type wmi_type_data = { 846d79b1074SAndy Lutomirski .name = "data", 847d4fc91adSAndy Lutomirski .groups = wmi_data_groups, 848d79b1074SAndy Lutomirski .release = wmi_dev_release, 849d79b1074SAndy Lutomirski }; 850d79b1074SAndy Lutomirski 851fd70da6aSDarren Hart (VMware) static int wmi_create_device(struct device *wmi_bus_dev, 852844af950SAndy Lutomirski const struct guid_block *gblock, 8537f5809bfSAndy Lutomirski struct wmi_block *wblock, 8547f5809bfSAndy Lutomirski struct acpi_device *device) 8551caab3c1SMatthew Garrett { 856d4fc91adSAndy Lutomirski struct acpi_device_info *info; 857d4fc91adSAndy Lutomirski char method[5]; 858d4fc91adSAndy Lutomirski int result; 859d4fc91adSAndy Lutomirski 860fd70da6aSDarren Hart (VMware) if (gblock->flags & ACPI_WMI_EVENT) { 861fd70da6aSDarren Hart (VMware) wblock->dev.dev.type = &wmi_type_event; 862fd70da6aSDarren Hart (VMware) goto out_init; 863fd70da6aSDarren Hart (VMware) } 864d4fc91adSAndy Lutomirski 865fd70da6aSDarren Hart (VMware) if (gblock->flags & ACPI_WMI_METHOD) { 866fd70da6aSDarren Hart (VMware) wblock->dev.dev.type = &wmi_type_method; 867fd70da6aSDarren Hart (VMware) goto out_init; 868fd70da6aSDarren Hart (VMware) } 869fd70da6aSDarren Hart (VMware) 870fd70da6aSDarren Hart (VMware) /* 871fd70da6aSDarren Hart (VMware) * Data Block Query Control Method (WQxx by convention) is 872fd70da6aSDarren Hart (VMware) * required per the WMI documentation. If it is not present, 873fd70da6aSDarren Hart (VMware) * we ignore this data block. 874fd70da6aSDarren Hart (VMware) */ 875d4fc91adSAndy Lutomirski strcpy(method, "WQ"); 876d4fc91adSAndy Lutomirski strncat(method, wblock->gblock.object_id, 2); 877d4fc91adSAndy Lutomirski result = get_subobj_info(device->handle, method, &info); 878d4fc91adSAndy Lutomirski 879fd70da6aSDarren Hart (VMware) if (result) { 880fd70da6aSDarren Hart (VMware) dev_warn(wmi_bus_dev, 881fd70da6aSDarren Hart (VMware) "%s data block query control method not found", 882fd70da6aSDarren Hart (VMware) method); 883fd70da6aSDarren Hart (VMware) return result; 884fd70da6aSDarren Hart (VMware) } 885fd70da6aSDarren Hart (VMware) 886fd70da6aSDarren Hart (VMware) wblock->dev.dev.type = &wmi_type_data; 887d4fc91adSAndy Lutomirski 888d4fc91adSAndy Lutomirski /* 889d4fc91adSAndy Lutomirski * The Microsoft documentation specifically states: 890d4fc91adSAndy Lutomirski * 891d4fc91adSAndy Lutomirski * Data blocks registered with only a single instance 892d4fc91adSAndy Lutomirski * can ignore the parameter. 893d4fc91adSAndy Lutomirski * 894fd70da6aSDarren Hart (VMware) * ACPICA will get mad at us if we call the method with the wrong number 895fd70da6aSDarren Hart (VMware) * of arguments, so check what our method expects. (On some Dell 896fd70da6aSDarren Hart (VMware) * laptops, WQxx may not be a method at all.) 897d4fc91adSAndy Lutomirski */ 898fd70da6aSDarren Hart (VMware) if (info->type != ACPI_TYPE_METHOD || info->param_count == 0) 899d4fc91adSAndy Lutomirski wblock->read_takes_no_args = true; 900d4fc91adSAndy Lutomirski 901d4fc91adSAndy Lutomirski kfree(info); 902d4fc91adSAndy Lutomirski 903d4fc91adSAndy Lutomirski strcpy(method, "WS"); 904d4fc91adSAndy Lutomirski strncat(method, wblock->gblock.object_id, 2); 905d4fc91adSAndy Lutomirski result = get_subobj_info(device->handle, method, NULL); 906d4fc91adSAndy Lutomirski 907fd70da6aSDarren Hart (VMware) if (result == 0) 908fd70da6aSDarren Hart (VMware) wblock->dev.setable = true; 909d4fc91adSAndy Lutomirski 910fd70da6aSDarren Hart (VMware) out_init: 911fd70da6aSDarren Hart (VMware) wblock->dev.dev.bus = &wmi_bus_type; 912fd70da6aSDarren Hart (VMware) wblock->dev.dev.parent = wmi_bus_dev; 913fd70da6aSDarren Hart (VMware) 914fd70da6aSDarren Hart (VMware) dev_set_name(&wblock->dev.dev, "%pUL", gblock->guid); 915c64eefd4SDmitry Torokhov 9166ee50aaaSDarren Hart (VMware) device_initialize(&wblock->dev.dev); 917fd70da6aSDarren Hart (VMware) 918fd70da6aSDarren Hart (VMware) return 0; 9191caab3c1SMatthew Garrett } 9201caab3c1SMatthew Garrett 921b0e86302SAndy Lutomirski static void wmi_free_devices(struct acpi_device *device) 9221caab3c1SMatthew Garrett { 923c64eefd4SDmitry Torokhov struct wmi_block *wblock, *next; 9241caab3c1SMatthew Garrett 9251caab3c1SMatthew Garrett /* Delete devices for all the GUIDs */ 926023b9565SDmitry Torokhov list_for_each_entry_safe(wblock, next, &wmi_block_list, list) { 927b0e86302SAndy Lutomirski if (wblock->acpi_device == device) { 928023b9565SDmitry Torokhov list_del(&wblock->list); 929844af950SAndy Lutomirski device_unregister(&wblock->dev.dev); 930023b9565SDmitry Torokhov } 9311caab3c1SMatthew Garrett } 932b0e86302SAndy Lutomirski } 9331caab3c1SMatthew Garrett 934b0e86302SAndy Lutomirski static bool guid_already_parsed(struct acpi_device *device, 935b0e86302SAndy Lutomirski const u8 *guid) 936d1f9e497SCarlos Corbacho { 937d1f9e497SCarlos Corbacho struct wmi_block *wblock; 938d1f9e497SCarlos Corbacho 939b0e86302SAndy Lutomirski list_for_each_entry(wblock, &wmi_block_list, list) { 940b0e86302SAndy Lutomirski if (memcmp(wblock->gblock.guid, guid, 16) == 0) { 941b0e86302SAndy Lutomirski /* 942b0e86302SAndy Lutomirski * Because we historically didn't track the relationship 943b0e86302SAndy Lutomirski * between GUIDs and ACPI nodes, we don't know whether 944b0e86302SAndy Lutomirski * we need to suppress GUIDs that are unique on a 945b0e86302SAndy Lutomirski * given node but duplicated across nodes. 946b0e86302SAndy Lutomirski */ 947b0e86302SAndy Lutomirski dev_warn(&device->dev, "duplicate WMI GUID %pUL (first instance was on %s)\n", 948b0e86302SAndy Lutomirski guid, dev_name(&wblock->acpi_device->dev)); 949d1f9e497SCarlos Corbacho return true; 950b0e86302SAndy Lutomirski } 951b0e86302SAndy Lutomirski } 952c64eefd4SDmitry Torokhov 953d1f9e497SCarlos Corbacho return false; 954d1f9e497SCarlos Corbacho } 955d1f9e497SCarlos Corbacho 9561caab3c1SMatthew Garrett /* 957b4f9fe12SLen Brown * Parse the _WDG method for the GUID data blocks 958b4f9fe12SLen Brown */ 959844af950SAndy Lutomirski static int parse_wdg(struct device *wmi_bus_dev, struct acpi_device *device) 960b4f9fe12SLen Brown { 961b4f9fe12SLen Brown struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL}; 96237830662SDmitry Torokhov const struct guid_block *gblock; 9636ee50aaaSDarren Hart (VMware) struct wmi_block *wblock, *next; 9646ee50aaaSDarren Hart (VMware) union acpi_object *obj; 965b4f9fe12SLen Brown acpi_status status; 9666ee50aaaSDarren Hart (VMware) int retval = 0; 967b4f9fe12SLen Brown u32 i, total; 968b4f9fe12SLen Brown 9697f5809bfSAndy Lutomirski status = acpi_evaluate_object(device->handle, "_WDG", NULL, &out); 970b4f9fe12SLen Brown if (ACPI_FAILURE(status)) 971c64eefd4SDmitry Torokhov return -ENXIO; 972b4f9fe12SLen Brown 973b4f9fe12SLen Brown obj = (union acpi_object *) out.pointer; 9743d2c63ebSDmitry Torokhov if (!obj) 975c64eefd4SDmitry Torokhov return -ENXIO; 976b4f9fe12SLen Brown 97764ed0ab8SDmitry Torokhov if (obj->type != ACPI_TYPE_BUFFER) { 978c64eefd4SDmitry Torokhov retval = -ENXIO; 97964ed0ab8SDmitry Torokhov goto out_free_pointer; 98064ed0ab8SDmitry Torokhov } 981b4f9fe12SLen Brown 98237830662SDmitry Torokhov gblock = (const struct guid_block *)obj->buffer.pointer; 983b4f9fe12SLen Brown total = obj->buffer.length / sizeof(struct guid_block); 984b4f9fe12SLen Brown 985b4f9fe12SLen Brown for (i = 0; i < total; i++) { 986a929aae0SThomas Renninger if (debug_dump_wdg) 987a929aae0SThomas Renninger wmi_dump_wdg(&gblock[i]); 988a929aae0SThomas Renninger 989a1c31bcdSAndy Lutomirski /* 990a1c31bcdSAndy Lutomirski * Some WMI devices, like those for nVidia hooks, have a 991a1c31bcdSAndy Lutomirski * duplicate GUID. It's not clear what we should do in this 992a1c31bcdSAndy Lutomirski * case yet, so for now, we'll just ignore the duplicate 993a1c31bcdSAndy Lutomirski * for device creation. 994a1c31bcdSAndy Lutomirski */ 995a1c31bcdSAndy Lutomirski if (guid_already_parsed(device, gblock[i].guid)) 996a1c31bcdSAndy Lutomirski continue; 997a1c31bcdSAndy Lutomirski 99858f6425eSColin King wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL); 9996ee50aaaSDarren Hart (VMware) if (!wblock) { 10006ee50aaaSDarren Hart (VMware) retval = -ENOMEM; 10016ee50aaaSDarren Hart (VMware) break; 10026ee50aaaSDarren Hart (VMware) } 100358f6425eSColin King 1004b0e86302SAndy Lutomirski wblock->acpi_device = device; 100558f6425eSColin King wblock->gblock = gblock[i]; 100658f6425eSColin King 1007fd70da6aSDarren Hart (VMware) retval = wmi_create_device(wmi_bus_dev, &gblock[i], wblock, device); 1008fd70da6aSDarren Hart (VMware) if (retval) { 1009fd70da6aSDarren Hart (VMware) kfree(wblock); 1010fd70da6aSDarren Hart (VMware) continue; 1011fd70da6aSDarren Hart (VMware) } 101258f6425eSColin King 101358f6425eSColin King list_add_tail(&wblock->list, &wmi_block_list); 1014b4f9fe12SLen Brown 1015fc3155b2SThomas Renninger if (debug_event) { 1016fc3155b2SThomas Renninger wblock->handler = wmi_notify_debug; 10172d5ab555SDmitry Torokhov wmi_method_enable(wblock, 1); 1018fc3155b2SThomas Renninger } 1019b4f9fe12SLen Brown } 1020b4f9fe12SLen Brown 10216ee50aaaSDarren Hart (VMware) /* 10226ee50aaaSDarren Hart (VMware) * Now that all of the devices are created, add them to the 10236ee50aaaSDarren Hart (VMware) * device tree and probe subdrivers. 10246ee50aaaSDarren Hart (VMware) */ 10256ee50aaaSDarren Hart (VMware) list_for_each_entry_safe(wblock, next, &wmi_block_list, list) { 10266ee50aaaSDarren Hart (VMware) if (wblock->acpi_device != device) 10276ee50aaaSDarren Hart (VMware) continue; 10286ee50aaaSDarren Hart (VMware) 10296ee50aaaSDarren Hart (VMware) retval = device_add(&wblock->dev.dev); 10306ee50aaaSDarren Hart (VMware) if (retval) { 10316ee50aaaSDarren Hart (VMware) dev_err(wmi_bus_dev, "failed to register %pULL\n", 10326ee50aaaSDarren Hart (VMware) wblock->gblock.guid); 10336ee50aaaSDarren Hart (VMware) if (debug_event) 10346ee50aaaSDarren Hart (VMware) wmi_method_enable(wblock, 0); 10356ee50aaaSDarren Hart (VMware) list_del(&wblock->list); 10366ee50aaaSDarren Hart (VMware) put_device(&wblock->dev.dev); 10376ee50aaaSDarren Hart (VMware) } 10386ee50aaaSDarren Hart (VMware) } 1039c64eefd4SDmitry Torokhov 1040a5167c5bSAxel Lin out_free_pointer: 1041a5167c5bSAxel Lin kfree(out.pointer); 1042c64eefd4SDmitry Torokhov return retval; 1043b4f9fe12SLen Brown } 1044b4f9fe12SLen Brown 1045b4f9fe12SLen Brown /* 1046b4f9fe12SLen Brown * WMI can have EmbeddedControl access regions. In which case, we just want to 1047b4f9fe12SLen Brown * hand these off to the EC driver. 1048b4f9fe12SLen Brown */ 1049b4f9fe12SLen Brown static acpi_status 1050b4f9fe12SLen Brown acpi_wmi_ec_space_handler(u32 function, acpi_physical_address address, 1051439913ffSLin Ming u32 bits, u64 *value, 1052b4f9fe12SLen Brown void *handler_context, void *region_context) 1053b4f9fe12SLen Brown { 1054b4f9fe12SLen Brown int result = 0, i = 0; 1055b4f9fe12SLen Brown u8 temp = 0; 1056b4f9fe12SLen Brown 1057b4f9fe12SLen Brown if ((address > 0xFF) || !value) 1058b4f9fe12SLen Brown return AE_BAD_PARAMETER; 1059b4f9fe12SLen Brown 1060b4f9fe12SLen Brown if (function != ACPI_READ && function != ACPI_WRITE) 1061b4f9fe12SLen Brown return AE_BAD_PARAMETER; 1062b4f9fe12SLen Brown 1063b4f9fe12SLen Brown if (bits != 8) 1064b4f9fe12SLen Brown return AE_BAD_PARAMETER; 1065b4f9fe12SLen Brown 1066b4f9fe12SLen Brown if (function == ACPI_READ) { 1067b4f9fe12SLen Brown result = ec_read(address, &temp); 1068439913ffSLin Ming (*value) |= ((u64)temp) << i; 1069b4f9fe12SLen Brown } else { 1070b4f9fe12SLen Brown temp = 0xff & ((*value) >> i); 1071b4f9fe12SLen Brown result = ec_write(address, temp); 1072b4f9fe12SLen Brown } 1073b4f9fe12SLen Brown 1074b4f9fe12SLen Brown switch (result) { 1075b4f9fe12SLen Brown case -EINVAL: 1076b4f9fe12SLen Brown return AE_BAD_PARAMETER; 1077b4f9fe12SLen Brown break; 1078b4f9fe12SLen Brown case -ENODEV: 1079b4f9fe12SLen Brown return AE_NOT_FOUND; 1080b4f9fe12SLen Brown break; 1081b4f9fe12SLen Brown case -ETIME: 1082b4f9fe12SLen Brown return AE_TIME; 1083b4f9fe12SLen Brown break; 1084b4f9fe12SLen Brown default: 1085b4f9fe12SLen Brown return AE_OK; 1086b4f9fe12SLen Brown } 1087b4f9fe12SLen Brown } 1088b4f9fe12SLen Brown 10891686f544SAndy Lutomirski static void acpi_wmi_notify_handler(acpi_handle handle, u32 event, 10901686f544SAndy Lutomirski void *context) 1091b4f9fe12SLen Brown { 1092b4f9fe12SLen Brown struct guid_block *block; 1093b4f9fe12SLen Brown struct wmi_block *wblock; 1094b4f9fe12SLen Brown struct list_head *p; 10951686f544SAndy Lutomirski bool found_it = false; 1096b4f9fe12SLen Brown 1097762e1a2fSDmitry Torokhov list_for_each(p, &wmi_block_list) { 1098b4f9fe12SLen Brown wblock = list_entry(p, struct wmi_block, list); 1099b4f9fe12SLen Brown block = &wblock->gblock; 1100b4f9fe12SLen Brown 11011686f544SAndy Lutomirski if (wblock->acpi_device->handle == handle && 1102b0e86302SAndy Lutomirski (block->flags & ACPI_WMI_EVENT) && 11031686f544SAndy Lutomirski (block->notify_id == event)) 11041686f544SAndy Lutomirski { 11051686f544SAndy Lutomirski found_it = true; 11061686f544SAndy Lutomirski break; 11071686f544SAndy Lutomirski } 11081686f544SAndy Lutomirski } 11091686f544SAndy Lutomirski 11101686f544SAndy Lutomirski if (!found_it) 11111686f544SAndy Lutomirski return; 11121686f544SAndy Lutomirski 11131686f544SAndy Lutomirski /* If a driver is bound, then notify the driver. */ 11141686f544SAndy Lutomirski if (wblock->dev.dev.driver) { 11151686f544SAndy Lutomirski struct wmi_driver *driver; 11161686f544SAndy Lutomirski struct acpi_object_list input; 11171686f544SAndy Lutomirski union acpi_object params[1]; 11181686f544SAndy Lutomirski struct acpi_buffer evdata = { ACPI_ALLOCATE_BUFFER, NULL }; 11191686f544SAndy Lutomirski acpi_status status; 11201686f544SAndy Lutomirski 11211686f544SAndy Lutomirski driver = container_of(wblock->dev.dev.driver, 11221686f544SAndy Lutomirski struct wmi_driver, driver); 11231686f544SAndy Lutomirski 11241686f544SAndy Lutomirski input.count = 1; 11251686f544SAndy Lutomirski input.pointer = params; 11261686f544SAndy Lutomirski params[0].type = ACPI_TYPE_INTEGER; 11271686f544SAndy Lutomirski params[0].integer.value = event; 11281686f544SAndy Lutomirski 11291686f544SAndy Lutomirski status = acpi_evaluate_object(wblock->acpi_device->handle, 11301686f544SAndy Lutomirski "_WED", &input, &evdata); 11311686f544SAndy Lutomirski if (ACPI_FAILURE(status)) { 11321686f544SAndy Lutomirski dev_warn(&wblock->dev.dev, 11331686f544SAndy Lutomirski "failed to get event data\n"); 11341686f544SAndy Lutomirski return; 11351686f544SAndy Lutomirski } 11361686f544SAndy Lutomirski 11371686f544SAndy Lutomirski if (driver->notify) 11381686f544SAndy Lutomirski driver->notify(&wblock->dev, 11391686f544SAndy Lutomirski (union acpi_object *)evdata.pointer); 11401686f544SAndy Lutomirski 11411686f544SAndy Lutomirski kfree(evdata.pointer); 11421686f544SAndy Lutomirski } else if (wblock->handler) { 11431686f544SAndy Lutomirski /* Legacy handler */ 1144b4f9fe12SLen Brown wblock->handler(event, wblock->handler_data); 11451686f544SAndy Lutomirski } 11461686f544SAndy Lutomirski 11477715348cSThomas Renninger if (debug_event) { 114885b4e4ebSRasmus Villemoes pr_info("DEBUG Event GUID: %pUL\n", 114985b4e4ebSRasmus Villemoes wblock->gblock.guid); 11507715348cSThomas Renninger } 1151b4f9fe12SLen Brown 1152b4f9fe12SLen Brown acpi_bus_generate_netlink_event( 11531686f544SAndy Lutomirski wblock->acpi_device->pnp.device_class, 11541686f544SAndy Lutomirski dev_name(&wblock->dev.dev), 1155b4f9fe12SLen Brown event, 0); 11561686f544SAndy Lutomirski 1157b4f9fe12SLen Brown } 1158b4f9fe12SLen Brown 11599599ed91SAndy Lutomirski static int acpi_wmi_remove(struct platform_device *device) 1160b4f9fe12SLen Brown { 11619599ed91SAndy Lutomirski struct acpi_device *acpi_device = ACPI_COMPANION(&device->dev); 11629599ed91SAndy Lutomirski 11639599ed91SAndy Lutomirski acpi_remove_notify_handler(acpi_device->handle, ACPI_DEVICE_NOTIFY, 11641686f544SAndy Lutomirski acpi_wmi_notify_handler); 11659599ed91SAndy Lutomirski acpi_remove_address_space_handler(acpi_device->handle, 1166b4f9fe12SLen Brown ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler); 11679599ed91SAndy Lutomirski wmi_free_devices(acpi_device); 11687b11e898SMario Limonciello device_destroy(&wmi_bus_class, MKDEV(0, 0)); 1169b4f9fe12SLen Brown 1170b4f9fe12SLen Brown return 0; 1171b4f9fe12SLen Brown } 1172b4f9fe12SLen Brown 11739599ed91SAndy Lutomirski static int acpi_wmi_probe(struct platform_device *device) 1174b4f9fe12SLen Brown { 11759599ed91SAndy Lutomirski struct acpi_device *acpi_device; 1176844af950SAndy Lutomirski struct device *wmi_bus_dev; 1177b4f9fe12SLen Brown acpi_status status; 1178c64eefd4SDmitry Torokhov int error; 1179b4f9fe12SLen Brown 11809599ed91SAndy Lutomirski acpi_device = ACPI_COMPANION(&device->dev); 11819599ed91SAndy Lutomirski if (!acpi_device) { 11829599ed91SAndy Lutomirski dev_err(&device->dev, "ACPI companion is missing\n"); 11839599ed91SAndy Lutomirski return -ENODEV; 11849599ed91SAndy Lutomirski } 11859599ed91SAndy Lutomirski 11869599ed91SAndy Lutomirski status = acpi_install_address_space_handler(acpi_device->handle, 1187b4f9fe12SLen Brown ACPI_ADR_SPACE_EC, 1188b4f9fe12SLen Brown &acpi_wmi_ec_space_handler, 1189b4f9fe12SLen Brown NULL, NULL); 11905212cd67SDmitry Torokhov if (ACPI_FAILURE(status)) { 119146492ee4SAndy Lutomirski dev_err(&device->dev, "Error installing EC region handler\n"); 1192b4f9fe12SLen Brown return -ENODEV; 11935212cd67SDmitry Torokhov } 1194b4f9fe12SLen Brown 11959599ed91SAndy Lutomirski status = acpi_install_notify_handler(acpi_device->handle, 11969599ed91SAndy Lutomirski ACPI_DEVICE_NOTIFY, 11971686f544SAndy Lutomirski acpi_wmi_notify_handler, 11981686f544SAndy Lutomirski NULL); 11991686f544SAndy Lutomirski if (ACPI_FAILURE(status)) { 12001686f544SAndy Lutomirski dev_err(&device->dev, "Error installing notify handler\n"); 12011686f544SAndy Lutomirski error = -ENODEV; 12021686f544SAndy Lutomirski goto err_remove_ec_handler; 12031686f544SAndy Lutomirski } 12041686f544SAndy Lutomirski 1205844af950SAndy Lutomirski wmi_bus_dev = device_create(&wmi_bus_class, &device->dev, MKDEV(0, 0), 1206844af950SAndy Lutomirski NULL, "wmi_bus-%s", dev_name(&device->dev)); 1207844af950SAndy Lutomirski if (IS_ERR(wmi_bus_dev)) { 1208844af950SAndy Lutomirski error = PTR_ERR(wmi_bus_dev); 12091686f544SAndy Lutomirski goto err_remove_notify_handler; 1210844af950SAndy Lutomirski } 12119599ed91SAndy Lutomirski dev_set_drvdata(&device->dev, wmi_bus_dev); 1212844af950SAndy Lutomirski 12139599ed91SAndy Lutomirski error = parse_wdg(wmi_bus_dev, acpi_device); 1214c64eefd4SDmitry Torokhov if (error) { 12158e07514dSDmitry Torokhov pr_err("Failed to parse WDG method\n"); 1216844af950SAndy Lutomirski goto err_remove_busdev; 1217b4f9fe12SLen Brown } 1218b4f9fe12SLen Brown 1219c64eefd4SDmitry Torokhov return 0; 122046492ee4SAndy Lutomirski 1221844af950SAndy Lutomirski err_remove_busdev: 12227b11e898SMario Limonciello device_destroy(&wmi_bus_class, MKDEV(0, 0)); 1223844af950SAndy Lutomirski 12241686f544SAndy Lutomirski err_remove_notify_handler: 12259599ed91SAndy Lutomirski acpi_remove_notify_handler(acpi_device->handle, ACPI_DEVICE_NOTIFY, 12261686f544SAndy Lutomirski acpi_wmi_notify_handler); 12271686f544SAndy Lutomirski 12281686f544SAndy Lutomirski err_remove_ec_handler: 12299599ed91SAndy Lutomirski acpi_remove_address_space_handler(acpi_device->handle, 123046492ee4SAndy Lutomirski ACPI_ADR_SPACE_EC, 123146492ee4SAndy Lutomirski &acpi_wmi_ec_space_handler); 123246492ee4SAndy Lutomirski 123346492ee4SAndy Lutomirski return error; 1234b4f9fe12SLen Brown } 1235b4f9fe12SLen Brown 1236844af950SAndy Lutomirski int __must_check __wmi_driver_register(struct wmi_driver *driver, 1237844af950SAndy Lutomirski struct module *owner) 1238844af950SAndy Lutomirski { 1239844af950SAndy Lutomirski driver->driver.owner = owner; 1240844af950SAndy Lutomirski driver->driver.bus = &wmi_bus_type; 1241844af950SAndy Lutomirski 1242844af950SAndy Lutomirski return driver_register(&driver->driver); 1243844af950SAndy Lutomirski } 1244844af950SAndy Lutomirski EXPORT_SYMBOL(__wmi_driver_register); 1245844af950SAndy Lutomirski 1246844af950SAndy Lutomirski void wmi_driver_unregister(struct wmi_driver *driver) 1247844af950SAndy Lutomirski { 1248844af950SAndy Lutomirski driver_unregister(&driver->driver); 1249844af950SAndy Lutomirski } 1250844af950SAndy Lutomirski EXPORT_SYMBOL(wmi_driver_unregister); 1251844af950SAndy Lutomirski 1252b4f9fe12SLen Brown static int __init acpi_wmi_init(void) 1253b4f9fe12SLen Brown { 1254c64eefd4SDmitry Torokhov int error; 1255b4f9fe12SLen Brown 1256b4f9fe12SLen Brown if (acpi_disabled) 1257b4f9fe12SLen Brown return -ENODEV; 1258b4f9fe12SLen Brown 1259844af950SAndy Lutomirski error = class_register(&wmi_bus_class); 1260c64eefd4SDmitry Torokhov if (error) 1261c64eefd4SDmitry Torokhov return error; 1262b4f9fe12SLen Brown 1263844af950SAndy Lutomirski error = bus_register(&wmi_bus_type); 1264844af950SAndy Lutomirski if (error) 1265844af950SAndy Lutomirski goto err_unreg_class; 1266844af950SAndy Lutomirski 12679599ed91SAndy Lutomirski error = platform_driver_register(&acpi_wmi_driver); 1268c64eefd4SDmitry Torokhov if (error) { 1269c64eefd4SDmitry Torokhov pr_err("Error loading mapper\n"); 1270844af950SAndy Lutomirski goto err_unreg_bus; 12711caab3c1SMatthew Garrett } 12721caab3c1SMatthew Garrett 12738e07514dSDmitry Torokhov return 0; 1274844af950SAndy Lutomirski 1275844af950SAndy Lutomirski err_unreg_bus: 1276844af950SAndy Lutomirski bus_unregister(&wmi_bus_type); 1277844af950SAndy Lutomirski 127897277717SAlexey Khoroshilov err_unreg_class: 127997277717SAlexey Khoroshilov class_unregister(&wmi_bus_class); 128097277717SAlexey Khoroshilov 1281844af950SAndy Lutomirski return error; 1282b4f9fe12SLen Brown } 1283b4f9fe12SLen Brown 1284b4f9fe12SLen Brown static void __exit acpi_wmi_exit(void) 1285b4f9fe12SLen Brown { 12869599ed91SAndy Lutomirski platform_driver_unregister(&acpi_wmi_driver); 1287844af950SAndy Lutomirski bus_unregister(&wmi_bus_type); 1288303d1fccSMario Limonciello class_unregister(&wmi_bus_class); 1289b4f9fe12SLen Brown } 1290b4f9fe12SLen Brown 1291b4f9fe12SLen Brown subsys_initcall(acpi_wmi_init); 1292b4f9fe12SLen Brown module_exit(acpi_wmi_exit); 1293