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> 409599ed91SAndy Lutomirski #include <linux/platform_device.h> 41844af950SAndy Lutomirski #include <linux/wmi.h> 42538d7eb8SAndy Shevchenko #include <linux/uuid.h> 43b4f9fe12SLen Brown 44b4f9fe12SLen Brown ACPI_MODULE_NAME("wmi"); 45b4f9fe12SLen Brown MODULE_AUTHOR("Carlos Corbacho"); 46b4f9fe12SLen Brown MODULE_DESCRIPTION("ACPI-WMI Mapping Driver"); 47b4f9fe12SLen Brown MODULE_LICENSE("GPL"); 48b4f9fe12SLen Brown 49762e1a2fSDmitry Torokhov static LIST_HEAD(wmi_block_list); 50b4f9fe12SLen Brown 51b4f9fe12SLen Brown struct guid_block { 52b4f9fe12SLen Brown char guid[16]; 53b4f9fe12SLen Brown union { 54b4f9fe12SLen Brown char object_id[2]; 55b4f9fe12SLen Brown struct { 56b4f9fe12SLen Brown unsigned char notify_id; 57b4f9fe12SLen Brown unsigned char reserved; 58b4f9fe12SLen Brown }; 59b4f9fe12SLen Brown }; 60b4f9fe12SLen Brown u8 instance_count; 61b4f9fe12SLen Brown u8 flags; 62b4f9fe12SLen Brown }; 63b4f9fe12SLen Brown 64b4f9fe12SLen Brown struct wmi_block { 65844af950SAndy Lutomirski struct wmi_device dev; 66b4f9fe12SLen Brown struct list_head list; 67b4f9fe12SLen Brown struct guid_block gblock; 68b0e86302SAndy Lutomirski struct acpi_device *acpi_device; 69b4f9fe12SLen Brown wmi_notify_handler handler; 70b4f9fe12SLen Brown void *handler_data; 71d4fc91adSAndy Lutomirski 72d4fc91adSAndy Lutomirski bool read_takes_no_args; /* only defined if readable */ 73b4f9fe12SLen Brown }; 74b4f9fe12SLen Brown 75b4f9fe12SLen Brown 76b4f9fe12SLen Brown /* 77b4f9fe12SLen Brown * If the GUID data block is marked as expensive, we must enable and 78b4f9fe12SLen Brown * explicitily disable data collection. 79b4f9fe12SLen Brown */ 80b4f9fe12SLen Brown #define ACPI_WMI_EXPENSIVE 0x1 81b4f9fe12SLen Brown #define ACPI_WMI_METHOD 0x2 /* GUID is a method */ 82b4f9fe12SLen Brown #define ACPI_WMI_STRING 0x4 /* GUID takes & returns a string */ 83b4f9fe12SLen Brown #define ACPI_WMI_EVENT 0x8 /* GUID is an event */ 84b4f9fe12SLen Brown 8590ab5ee9SRusty Russell static bool debug_event; 86fc3155b2SThomas Renninger module_param(debug_event, bool, 0444); 87fc3155b2SThomas Renninger MODULE_PARM_DESC(debug_event, 88fc3155b2SThomas Renninger "Log WMI Events [0/1]"); 89fc3155b2SThomas Renninger 9090ab5ee9SRusty Russell static bool debug_dump_wdg; 91a929aae0SThomas Renninger module_param(debug_dump_wdg, bool, 0444); 92a929aae0SThomas Renninger MODULE_PARM_DESC(debug_dump_wdg, 93a929aae0SThomas Renninger "Dump available WMI interfaces [0/1]"); 94a929aae0SThomas Renninger 959599ed91SAndy Lutomirski static int acpi_wmi_remove(struct platform_device *device); 969599ed91SAndy Lutomirski static int acpi_wmi_probe(struct platform_device *device); 97b4f9fe12SLen Brown 98b4f9fe12SLen Brown static const struct acpi_device_id wmi_device_ids[] = { 99b4f9fe12SLen Brown {"PNP0C14", 0}, 100b4f9fe12SLen Brown {"pnp0c14", 0}, 101b4f9fe12SLen Brown {"", 0}, 102b4f9fe12SLen Brown }; 103b4f9fe12SLen Brown MODULE_DEVICE_TABLE(acpi, wmi_device_ids); 104b4f9fe12SLen Brown 1059599ed91SAndy Lutomirski static struct platform_driver acpi_wmi_driver = { 1069599ed91SAndy Lutomirski .driver = { 107844af950SAndy Lutomirski .name = "acpi-wmi", 1089599ed91SAndy Lutomirski .acpi_match_table = wmi_device_ids, 109b4f9fe12SLen Brown }, 1109599ed91SAndy Lutomirski .probe = acpi_wmi_probe, 1119599ed91SAndy Lutomirski .remove = acpi_wmi_remove, 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 141d4fc91adSAndy Lutomirski static int get_subobj_info(acpi_handle handle, const char *pathname, 142d4fc91adSAndy Lutomirski struct acpi_device_info **info) 143d4fc91adSAndy Lutomirski { 144d4fc91adSAndy Lutomirski struct acpi_device_info *dummy_info, **info_ptr; 145d4fc91adSAndy Lutomirski acpi_handle subobj_handle; 146d4fc91adSAndy Lutomirski acpi_status status; 147d4fc91adSAndy Lutomirski 148d4fc91adSAndy Lutomirski status = acpi_get_handle(handle, (char *)pathname, &subobj_handle); 149d4fc91adSAndy Lutomirski if (status == AE_NOT_FOUND) 150d4fc91adSAndy Lutomirski return -ENOENT; 151d4fc91adSAndy Lutomirski else if (ACPI_FAILURE(status)) 152d4fc91adSAndy Lutomirski return -EIO; 153d4fc91adSAndy Lutomirski 154d4fc91adSAndy Lutomirski info_ptr = info ? info : &dummy_info; 155d4fc91adSAndy Lutomirski status = acpi_get_object_info(subobj_handle, info_ptr); 156d4fc91adSAndy Lutomirski if (ACPI_FAILURE(status)) 157d4fc91adSAndy Lutomirski return -EIO; 158d4fc91adSAndy Lutomirski 159d4fc91adSAndy Lutomirski if (!info) 160d4fc91adSAndy Lutomirski kfree(dummy_info); 161d4fc91adSAndy Lutomirski 162d4fc91adSAndy Lutomirski return 0; 163d4fc91adSAndy Lutomirski } 164d4fc91adSAndy Lutomirski 165b4f9fe12SLen Brown static acpi_status wmi_method_enable(struct wmi_block *wblock, int enable) 166b4f9fe12SLen Brown { 167b4f9fe12SLen Brown struct guid_block *block = NULL; 168b4f9fe12SLen Brown char method[5]; 169b4f9fe12SLen Brown acpi_status status; 170b4f9fe12SLen Brown acpi_handle handle; 171b4f9fe12SLen Brown 172b4f9fe12SLen Brown block = &wblock->gblock; 173b0e86302SAndy Lutomirski handle = wblock->acpi_device->handle; 174b4f9fe12SLen Brown 175b4f9fe12SLen Brown snprintf(method, 5, "WE%02X", block->notify_id); 1768122ab66SZhang Rui status = acpi_execute_simple_method(handle, method, enable); 177b4f9fe12SLen Brown 178b4f9fe12SLen Brown if (status != AE_OK && status != AE_NOT_FOUND) 179b4f9fe12SLen Brown return status; 180b4f9fe12SLen Brown else 181b4f9fe12SLen Brown return AE_OK; 182b4f9fe12SLen Brown } 183b4f9fe12SLen Brown 184b4f9fe12SLen Brown /* 185b4f9fe12SLen Brown * Exported WMI functions 186b4f9fe12SLen Brown */ 187b4f9fe12SLen Brown /** 188b4f9fe12SLen Brown * wmi_evaluate_method - Evaluate a WMI method 189b4f9fe12SLen Brown * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba 190b4f9fe12SLen Brown * @instance: Instance index 191b4f9fe12SLen Brown * @method_id: Method ID to call 192b4f9fe12SLen Brown * &in: Buffer containing input for the method call 193b4f9fe12SLen Brown * &out: Empty buffer to return the method results 194b4f9fe12SLen Brown * 195b4f9fe12SLen Brown * Call an ACPI-WMI method 196b4f9fe12SLen Brown */ 197b4f9fe12SLen Brown acpi_status wmi_evaluate_method(const char *guid_string, u8 instance, 198b4f9fe12SLen Brown u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out) 199b4f9fe12SLen Brown { 200b4f9fe12SLen Brown struct guid_block *block = NULL; 201b4f9fe12SLen Brown struct wmi_block *wblock = NULL; 202b4f9fe12SLen Brown acpi_handle handle; 203b4f9fe12SLen Brown acpi_status status; 204b4f9fe12SLen Brown struct acpi_object_list input; 205b4f9fe12SLen Brown union acpi_object params[3]; 206f3d83e24SCostantino Leandro char method[5] = "WM"; 207b4f9fe12SLen Brown 208b4f9fe12SLen Brown if (!find_guid(guid_string, &wblock)) 209b4f9fe12SLen Brown return AE_ERROR; 210b4f9fe12SLen Brown 211b4f9fe12SLen Brown block = &wblock->gblock; 212b0e86302SAndy Lutomirski handle = wblock->acpi_device->handle; 213b4f9fe12SLen Brown 214b4f9fe12SLen Brown if (!(block->flags & ACPI_WMI_METHOD)) 215b4f9fe12SLen Brown return AE_BAD_DATA; 216b4f9fe12SLen Brown 217b4f9fe12SLen Brown if (block->instance_count < instance) 218b4f9fe12SLen Brown return AE_BAD_PARAMETER; 219b4f9fe12SLen Brown 220b4f9fe12SLen Brown input.count = 2; 221b4f9fe12SLen Brown input.pointer = params; 222b4f9fe12SLen Brown params[0].type = ACPI_TYPE_INTEGER; 223b4f9fe12SLen Brown params[0].integer.value = instance; 224b4f9fe12SLen Brown params[1].type = ACPI_TYPE_INTEGER; 225b4f9fe12SLen Brown params[1].integer.value = method_id; 226b4f9fe12SLen Brown 227b4f9fe12SLen Brown if (in) { 228b4f9fe12SLen Brown input.count = 3; 229b4f9fe12SLen Brown 230b4f9fe12SLen Brown if (block->flags & ACPI_WMI_STRING) { 231b4f9fe12SLen Brown params[2].type = ACPI_TYPE_STRING; 232b4f9fe12SLen Brown } else { 233b4f9fe12SLen Brown params[2].type = ACPI_TYPE_BUFFER; 234b4f9fe12SLen Brown } 235b4f9fe12SLen Brown params[2].buffer.length = in->length; 236b4f9fe12SLen Brown params[2].buffer.pointer = in->pointer; 237b4f9fe12SLen Brown } 238b4f9fe12SLen Brown 239b4f9fe12SLen Brown strncat(method, block->object_id, 2); 240b4f9fe12SLen Brown 241b4f9fe12SLen Brown status = acpi_evaluate_object(handle, method, &input, out); 242b4f9fe12SLen Brown 243b4f9fe12SLen Brown return status; 244b4f9fe12SLen Brown } 245b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_evaluate_method); 246b4f9fe12SLen Brown 24756a37025SAndy Lutomirski static acpi_status __query_block(struct wmi_block *wblock, u8 instance, 248b4f9fe12SLen Brown struct acpi_buffer *out) 249b4f9fe12SLen Brown { 250b4f9fe12SLen Brown struct guid_block *block = NULL; 25154f14c27SZhang Rui acpi_handle handle; 252b4f9fe12SLen Brown acpi_status status, wc_status = AE_ERROR; 2538122ab66SZhang Rui struct acpi_object_list input; 2548122ab66SZhang Rui union acpi_object wq_params[1]; 255f3d83e24SCostantino Leandro char method[5]; 256f3d83e24SCostantino Leandro char wc_method[5] = "WC"; 257b4f9fe12SLen Brown 25856a37025SAndy Lutomirski if (!out) 259b4f9fe12SLen Brown return AE_BAD_PARAMETER; 260b4f9fe12SLen Brown 261b4f9fe12SLen Brown block = &wblock->gblock; 262b0e86302SAndy Lutomirski handle = wblock->acpi_device->handle; 263b4f9fe12SLen Brown 264b4f9fe12SLen Brown if (block->instance_count < instance) 265b4f9fe12SLen Brown return AE_BAD_PARAMETER; 266b4f9fe12SLen Brown 267b4f9fe12SLen Brown /* Check GUID is a data block */ 268b4f9fe12SLen Brown if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD)) 269b4f9fe12SLen Brown return AE_ERROR; 270b4f9fe12SLen Brown 271b4f9fe12SLen Brown input.count = 1; 272b4f9fe12SLen Brown input.pointer = wq_params; 273b4f9fe12SLen Brown wq_params[0].type = ACPI_TYPE_INTEGER; 274b4f9fe12SLen Brown wq_params[0].integer.value = instance; 275b4f9fe12SLen Brown 276d4fc91adSAndy Lutomirski if (instance == 0 && wblock->read_takes_no_args) 277d4fc91adSAndy Lutomirski input.count = 0; 278d4fc91adSAndy Lutomirski 279b4f9fe12SLen Brown /* 280b4f9fe12SLen Brown * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method first to 281b4f9fe12SLen Brown * enable collection. 282b4f9fe12SLen Brown */ 283b4f9fe12SLen Brown if (block->flags & ACPI_WMI_EXPENSIVE) { 284b4f9fe12SLen Brown strncat(wc_method, block->object_id, 2); 285b4f9fe12SLen Brown 286b4f9fe12SLen Brown /* 287b4f9fe12SLen Brown * Some GUIDs break the specification by declaring themselves 288b4f9fe12SLen Brown * expensive, but have no corresponding WCxx method. So we 289b4f9fe12SLen Brown * should not fail if this happens. 290b4f9fe12SLen Brown */ 29154f14c27SZhang Rui if (acpi_has_method(handle, wc_method)) 2928122ab66SZhang Rui wc_status = acpi_execute_simple_method(handle, 2938122ab66SZhang Rui wc_method, 1); 294b4f9fe12SLen Brown } 295b4f9fe12SLen Brown 296b4f9fe12SLen Brown strcpy(method, "WQ"); 297b4f9fe12SLen Brown strncat(method, block->object_id, 2); 298b4f9fe12SLen Brown 299b4f9fe12SLen Brown status = acpi_evaluate_object(handle, method, &input, out); 300b4f9fe12SLen Brown 301b4f9fe12SLen Brown /* 302b4f9fe12SLen Brown * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method, even if 303b4f9fe12SLen Brown * the WQxx method failed - we should disable collection anyway. 304b4f9fe12SLen Brown */ 305b4f9fe12SLen Brown if ((block->flags & ACPI_WMI_EXPENSIVE) && ACPI_SUCCESS(wc_status)) { 3068122ab66SZhang Rui status = acpi_execute_simple_method(handle, wc_method, 0); 307b4f9fe12SLen Brown } 308b4f9fe12SLen Brown 309b4f9fe12SLen Brown return status; 310b4f9fe12SLen Brown } 31156a37025SAndy Lutomirski 31256a37025SAndy Lutomirski /** 31356a37025SAndy Lutomirski * wmi_query_block - Return contents of a WMI block (deprecated) 31456a37025SAndy Lutomirski * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba 31556a37025SAndy Lutomirski * @instance: Instance index 31656a37025SAndy Lutomirski * &out: Empty buffer to return the contents of the data block to 31756a37025SAndy Lutomirski * 31856a37025SAndy Lutomirski * Return the contents of an ACPI-WMI data block to a buffer 31956a37025SAndy Lutomirski */ 32056a37025SAndy Lutomirski acpi_status wmi_query_block(const char *guid_string, u8 instance, 32156a37025SAndy Lutomirski struct acpi_buffer *out) 32256a37025SAndy Lutomirski { 32356a37025SAndy Lutomirski struct wmi_block *wblock; 32456a37025SAndy Lutomirski 32556a37025SAndy Lutomirski if (!guid_string) 32656a37025SAndy Lutomirski return AE_BAD_PARAMETER; 32756a37025SAndy Lutomirski 32856a37025SAndy Lutomirski if (!find_guid(guid_string, &wblock)) 32956a37025SAndy Lutomirski return AE_ERROR; 33056a37025SAndy Lutomirski 33156a37025SAndy Lutomirski return __query_block(wblock, instance, out); 33256a37025SAndy Lutomirski } 333b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_query_block); 334b4f9fe12SLen Brown 33556a37025SAndy Lutomirski union acpi_object *wmidev_block_query(struct wmi_device *wdev, u8 instance) 33656a37025SAndy Lutomirski { 33756a37025SAndy Lutomirski struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL }; 33856a37025SAndy Lutomirski struct wmi_block *wblock = container_of(wdev, struct wmi_block, dev); 33956a37025SAndy Lutomirski 34056a37025SAndy Lutomirski if (ACPI_FAILURE(__query_block(wblock, instance, &out))) 34156a37025SAndy Lutomirski return NULL; 34256a37025SAndy Lutomirski 34356a37025SAndy Lutomirski return (union acpi_object *)out.pointer; 34456a37025SAndy Lutomirski } 34556a37025SAndy Lutomirski EXPORT_SYMBOL_GPL(wmidev_block_query); 34656a37025SAndy Lutomirski 347b4f9fe12SLen Brown /** 348b4f9fe12SLen Brown * wmi_set_block - Write to a WMI block 349b4f9fe12SLen Brown * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba 350b4f9fe12SLen Brown * @instance: Instance index 351b4f9fe12SLen Brown * &in: Buffer containing new values for the data block 352b4f9fe12SLen Brown * 353b4f9fe12SLen Brown * Write the contents of the input buffer to an ACPI-WMI data block 354b4f9fe12SLen Brown */ 355b4f9fe12SLen Brown acpi_status wmi_set_block(const char *guid_string, u8 instance, 356b4f9fe12SLen Brown const struct acpi_buffer *in) 357b4f9fe12SLen Brown { 358b4f9fe12SLen Brown struct guid_block *block = NULL; 359b4f9fe12SLen Brown struct wmi_block *wblock = NULL; 360b4f9fe12SLen Brown acpi_handle handle; 361b4f9fe12SLen Brown struct acpi_object_list input; 362b4f9fe12SLen Brown union acpi_object params[2]; 363f3d83e24SCostantino Leandro char method[5] = "WS"; 364b4f9fe12SLen Brown 365b4f9fe12SLen Brown if (!guid_string || !in) 366b4f9fe12SLen Brown return AE_BAD_DATA; 367b4f9fe12SLen Brown 368b4f9fe12SLen Brown if (!find_guid(guid_string, &wblock)) 369b4f9fe12SLen Brown return AE_ERROR; 370b4f9fe12SLen Brown 371b4f9fe12SLen Brown block = &wblock->gblock; 372b0e86302SAndy Lutomirski handle = wblock->acpi_device->handle; 373b4f9fe12SLen Brown 374b4f9fe12SLen Brown if (block->instance_count < instance) 375b4f9fe12SLen Brown return AE_BAD_PARAMETER; 376b4f9fe12SLen Brown 377b4f9fe12SLen Brown /* Check GUID is a data block */ 378b4f9fe12SLen Brown if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD)) 379b4f9fe12SLen Brown return AE_ERROR; 380b4f9fe12SLen Brown 381b4f9fe12SLen Brown input.count = 2; 382b4f9fe12SLen Brown input.pointer = params; 383b4f9fe12SLen Brown params[0].type = ACPI_TYPE_INTEGER; 384b4f9fe12SLen Brown params[0].integer.value = instance; 385b4f9fe12SLen Brown 386b4f9fe12SLen Brown if (block->flags & ACPI_WMI_STRING) { 387b4f9fe12SLen Brown params[1].type = ACPI_TYPE_STRING; 388b4f9fe12SLen Brown } else { 389b4f9fe12SLen Brown params[1].type = ACPI_TYPE_BUFFER; 390b4f9fe12SLen Brown } 391b4f9fe12SLen Brown params[1].buffer.length = in->length; 392b4f9fe12SLen Brown params[1].buffer.pointer = in->pointer; 393b4f9fe12SLen Brown 394b4f9fe12SLen Brown strncat(method, block->object_id, 2); 395b4f9fe12SLen Brown 396b4f9fe12SLen Brown return acpi_evaluate_object(handle, method, &input, NULL); 397b4f9fe12SLen Brown } 398b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_set_block); 399b4f9fe12SLen Brown 40037830662SDmitry Torokhov static void wmi_dump_wdg(const struct guid_block *g) 401a929aae0SThomas Renninger { 40285b4e4ebSRasmus Villemoes pr_info("%pUL:\n", g->guid); 4038e07514dSDmitry Torokhov pr_info("\tobject_id: %c%c\n", g->object_id[0], g->object_id[1]); 4048e07514dSDmitry Torokhov pr_info("\tnotify_id: %02X\n", g->notify_id); 4058e07514dSDmitry Torokhov pr_info("\treserved: %02X\n", g->reserved); 4068e07514dSDmitry Torokhov pr_info("\tinstance_count: %d\n", g->instance_count); 4078e07514dSDmitry Torokhov pr_info("\tflags: %#x", g->flags); 408a929aae0SThomas Renninger if (g->flags) { 409a929aae0SThomas Renninger if (g->flags & ACPI_WMI_EXPENSIVE) 4108e07514dSDmitry Torokhov pr_cont(" ACPI_WMI_EXPENSIVE"); 411a929aae0SThomas Renninger if (g->flags & ACPI_WMI_METHOD) 4128e07514dSDmitry Torokhov pr_cont(" ACPI_WMI_METHOD"); 413a929aae0SThomas Renninger if (g->flags & ACPI_WMI_STRING) 4148e07514dSDmitry Torokhov pr_cont(" ACPI_WMI_STRING"); 415a929aae0SThomas Renninger if (g->flags & ACPI_WMI_EVENT) 4168e07514dSDmitry Torokhov pr_cont(" ACPI_WMI_EVENT"); 417a929aae0SThomas Renninger } 4188e07514dSDmitry Torokhov pr_cont("\n"); 419a929aae0SThomas Renninger 420a929aae0SThomas Renninger } 421a929aae0SThomas Renninger 422fc3155b2SThomas Renninger static void wmi_notify_debug(u32 value, void *context) 423fc3155b2SThomas Renninger { 424fc3155b2SThomas Renninger struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; 425fc3155b2SThomas Renninger union acpi_object *obj; 4261492616aSAxel Lin acpi_status status; 427fc3155b2SThomas Renninger 4281492616aSAxel Lin status = wmi_get_event_data(value, &response); 4291492616aSAxel Lin if (status != AE_OK) { 4308e07514dSDmitry Torokhov pr_info("bad event status 0x%x\n", status); 4311492616aSAxel Lin return; 4321492616aSAxel Lin } 433fc3155b2SThomas Renninger 434fc3155b2SThomas Renninger obj = (union acpi_object *)response.pointer; 435fc3155b2SThomas Renninger 436fc3155b2SThomas Renninger if (!obj) 437fc3155b2SThomas Renninger return; 438fc3155b2SThomas Renninger 4398e07514dSDmitry Torokhov pr_info("DEBUG Event "); 440fc3155b2SThomas Renninger switch(obj->type) { 441fc3155b2SThomas Renninger case ACPI_TYPE_BUFFER: 4428e07514dSDmitry Torokhov pr_cont("BUFFER_TYPE - length %d\n", obj->buffer.length); 443fc3155b2SThomas Renninger break; 444fc3155b2SThomas Renninger case ACPI_TYPE_STRING: 4458e07514dSDmitry Torokhov pr_cont("STRING_TYPE - %s\n", obj->string.pointer); 446fc3155b2SThomas Renninger break; 447fc3155b2SThomas Renninger case ACPI_TYPE_INTEGER: 4488e07514dSDmitry Torokhov pr_cont("INTEGER_TYPE - %llu\n", obj->integer.value); 449fc3155b2SThomas Renninger break; 450fc3155b2SThomas Renninger case ACPI_TYPE_PACKAGE: 4518e07514dSDmitry Torokhov pr_cont("PACKAGE_TYPE - %d elements\n", obj->package.count); 452fc3155b2SThomas Renninger break; 453fc3155b2SThomas Renninger default: 4548e07514dSDmitry Torokhov pr_cont("object type 0x%X\n", obj->type); 455fc3155b2SThomas Renninger } 4561492616aSAxel Lin kfree(obj); 457fc3155b2SThomas Renninger } 458fc3155b2SThomas Renninger 459b4f9fe12SLen Brown /** 460b4f9fe12SLen Brown * wmi_install_notify_handler - Register handler for WMI events 461b4f9fe12SLen Brown * @handler: Function to handle notifications 462b4f9fe12SLen Brown * @data: Data to be returned to handler when event is fired 463b4f9fe12SLen Brown * 464b4f9fe12SLen Brown * Register a handler for events sent to the ACPI-WMI mapper device. 465b4f9fe12SLen Brown */ 466b4f9fe12SLen Brown acpi_status wmi_install_notify_handler(const char *guid, 467b4f9fe12SLen Brown wmi_notify_handler handler, void *data) 468b4f9fe12SLen Brown { 469b4f9fe12SLen Brown struct wmi_block *block; 47058f6425eSColin King acpi_status status = AE_NOT_EXIST; 471538d7eb8SAndy Shevchenko uuid_le guid_input; 47258f6425eSColin King struct list_head *p; 473b4f9fe12SLen Brown 474b4f9fe12SLen Brown if (!guid || !handler) 475b4f9fe12SLen Brown return AE_BAD_PARAMETER; 476b4f9fe12SLen Brown 477538d7eb8SAndy Shevchenko if (uuid_le_to_bin(guid, &guid_input)) 478538d7eb8SAndy Shevchenko return AE_BAD_PARAMETER; 479b4f9fe12SLen Brown 48058f6425eSColin King list_for_each(p, &wmi_block_list) { 48158f6425eSColin King acpi_status wmi_status; 48258f6425eSColin King block = list_entry(p, struct wmi_block, list); 48358f6425eSColin King 484538d7eb8SAndy Shevchenko if (memcmp(block->gblock.guid, &guid_input, 16) == 0) { 48558f6425eSColin King if (block->handler && 48658f6425eSColin King block->handler != wmi_notify_debug) 487b4f9fe12SLen Brown return AE_ALREADY_ACQUIRED; 488b4f9fe12SLen Brown 489b4f9fe12SLen Brown block->handler = handler; 490b4f9fe12SLen Brown block->handler_data = data; 491b4f9fe12SLen Brown 49258f6425eSColin King wmi_status = wmi_method_enable(block, 1); 49358f6425eSColin King if ((wmi_status != AE_OK) || 49458f6425eSColin King ((wmi_status == AE_OK) && (status == AE_NOT_EXIST))) 49558f6425eSColin King status = wmi_status; 49658f6425eSColin King } 49758f6425eSColin King } 498b4f9fe12SLen Brown 499b4f9fe12SLen Brown return status; 500b4f9fe12SLen Brown } 501b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_install_notify_handler); 502b4f9fe12SLen Brown 503b4f9fe12SLen Brown /** 504b4f9fe12SLen Brown * wmi_uninstall_notify_handler - Unregister handler for WMI events 505b4f9fe12SLen Brown * 506b4f9fe12SLen Brown * Unregister handler for events sent to the ACPI-WMI mapper device. 507b4f9fe12SLen Brown */ 508b4f9fe12SLen Brown acpi_status wmi_remove_notify_handler(const char *guid) 509b4f9fe12SLen Brown { 510b4f9fe12SLen Brown struct wmi_block *block; 51158f6425eSColin King acpi_status status = AE_NOT_EXIST; 512538d7eb8SAndy Shevchenko uuid_le guid_input; 51358f6425eSColin King struct list_head *p; 514b4f9fe12SLen Brown 515b4f9fe12SLen Brown if (!guid) 516b4f9fe12SLen Brown return AE_BAD_PARAMETER; 517b4f9fe12SLen Brown 518538d7eb8SAndy Shevchenko if (uuid_le_to_bin(guid, &guid_input)) 519538d7eb8SAndy Shevchenko return AE_BAD_PARAMETER; 520b4f9fe12SLen Brown 52158f6425eSColin King list_for_each(p, &wmi_block_list) { 52258f6425eSColin King acpi_status wmi_status; 52358f6425eSColin King block = list_entry(p, struct wmi_block, list); 52458f6425eSColin King 525538d7eb8SAndy Shevchenko if (memcmp(block->gblock.guid, &guid_input, 16) == 0) { 52658f6425eSColin King if (!block->handler || 52758f6425eSColin King block->handler == wmi_notify_debug) 528b4f9fe12SLen Brown return AE_NULL_ENTRY; 529b4f9fe12SLen Brown 530fc3155b2SThomas Renninger if (debug_event) { 531fc3155b2SThomas Renninger block->handler = wmi_notify_debug; 53258f6425eSColin King status = AE_OK; 533fc3155b2SThomas Renninger } else { 53458f6425eSColin King wmi_status = wmi_method_enable(block, 0); 535b4f9fe12SLen Brown block->handler = NULL; 536b4f9fe12SLen Brown block->handler_data = NULL; 53758f6425eSColin King if ((wmi_status != AE_OK) || 53858f6425eSColin King ((wmi_status == AE_OK) && 53958f6425eSColin King (status == AE_NOT_EXIST))) 54058f6425eSColin King status = wmi_status; 541fc3155b2SThomas Renninger } 54258f6425eSColin King } 54358f6425eSColin King } 54458f6425eSColin King 545b4f9fe12SLen Brown return status; 546b4f9fe12SLen Brown } 547b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_remove_notify_handler); 548b4f9fe12SLen Brown 549b4f9fe12SLen Brown /** 550b4f9fe12SLen Brown * wmi_get_event_data - Get WMI data associated with an event 551b4f9fe12SLen Brown * 5523e9b988eSAnisse Astier * @event: Event to find 5533e9b988eSAnisse Astier * @out: Buffer to hold event data. out->pointer should be freed with kfree() 554b4f9fe12SLen Brown * 555b4f9fe12SLen Brown * Returns extra data associated with an event in WMI. 556b4f9fe12SLen Brown */ 557b4f9fe12SLen Brown acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out) 558b4f9fe12SLen Brown { 559b4f9fe12SLen Brown struct acpi_object_list input; 560b4f9fe12SLen Brown union acpi_object params[1]; 561b4f9fe12SLen Brown struct guid_block *gblock; 562b4f9fe12SLen Brown struct wmi_block *wblock; 563b4f9fe12SLen Brown struct list_head *p; 564b4f9fe12SLen Brown 565b4f9fe12SLen Brown input.count = 1; 566b4f9fe12SLen Brown input.pointer = params; 567b4f9fe12SLen Brown params[0].type = ACPI_TYPE_INTEGER; 568b4f9fe12SLen Brown params[0].integer.value = event; 569b4f9fe12SLen Brown 570762e1a2fSDmitry Torokhov list_for_each(p, &wmi_block_list) { 571b4f9fe12SLen Brown wblock = list_entry(p, struct wmi_block, list); 572b4f9fe12SLen Brown gblock = &wblock->gblock; 573b4f9fe12SLen Brown 574b4f9fe12SLen Brown if ((gblock->flags & ACPI_WMI_EVENT) && 575b4f9fe12SLen Brown (gblock->notify_id == event)) 576b0e86302SAndy Lutomirski return acpi_evaluate_object(wblock->acpi_device->handle, 577b0e86302SAndy Lutomirski "_WED", &input, out); 578b4f9fe12SLen Brown } 579b4f9fe12SLen Brown 580b4f9fe12SLen Brown return AE_NOT_FOUND; 581b4f9fe12SLen Brown } 582b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_get_event_data); 583b4f9fe12SLen Brown 584b4f9fe12SLen Brown /** 585b4f9fe12SLen Brown * wmi_has_guid - Check if a GUID is available 586b4f9fe12SLen Brown * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba 587b4f9fe12SLen Brown * 588b4f9fe12SLen Brown * Check if a given GUID is defined by _WDG 589b4f9fe12SLen Brown */ 590b4f9fe12SLen Brown bool wmi_has_guid(const char *guid_string) 591b4f9fe12SLen Brown { 592b4f9fe12SLen Brown return find_guid(guid_string, NULL); 593b4f9fe12SLen Brown } 594b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_has_guid); 595b4f9fe12SLen Brown 596844af950SAndy Lutomirski static struct wmi_block *dev_to_wblock(struct device *dev) 597844af950SAndy Lutomirski { 598844af950SAndy Lutomirski return container_of(dev, struct wmi_block, dev.dev); 599844af950SAndy Lutomirski } 600844af950SAndy Lutomirski 601844af950SAndy Lutomirski static struct wmi_device *dev_to_wdev(struct device *dev) 602844af950SAndy Lutomirski { 603844af950SAndy Lutomirski return container_of(dev, struct wmi_device, dev); 604844af950SAndy Lutomirski } 605844af950SAndy Lutomirski 606b4f9fe12SLen Brown /* 6071caab3c1SMatthew Garrett * sysfs interface 6081caab3c1SMatthew Garrett */ 609614ef432SDmitry Torokhov static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, 6101caab3c1SMatthew Garrett char *buf) 6111caab3c1SMatthew Garrett { 612844af950SAndy Lutomirski struct wmi_block *wblock = dev_to_wblock(dev); 6131caab3c1SMatthew Garrett 61485b4e4ebSRasmus Villemoes return sprintf(buf, "wmi:%pUL\n", wblock->gblock.guid); 6151caab3c1SMatthew Garrett } 616e80b89a5SGreg Kroah-Hartman static DEVICE_ATTR_RO(modalias); 617614ef432SDmitry Torokhov 618844af950SAndy Lutomirski static ssize_t guid_show(struct device *dev, struct device_attribute *attr, 619844af950SAndy Lutomirski char *buf) 620844af950SAndy Lutomirski { 621844af950SAndy Lutomirski struct wmi_block *wblock = dev_to_wblock(dev); 622844af950SAndy Lutomirski 623844af950SAndy Lutomirski return sprintf(buf, "%pUL\n", wblock->gblock.guid); 624844af950SAndy Lutomirski } 625844af950SAndy Lutomirski static DEVICE_ATTR_RO(guid); 626844af950SAndy Lutomirski 627d79b1074SAndy Lutomirski static ssize_t instance_count_show(struct device *dev, 628d79b1074SAndy Lutomirski struct device_attribute *attr, char *buf) 629d79b1074SAndy Lutomirski { 630d79b1074SAndy Lutomirski struct wmi_block *wblock = dev_to_wblock(dev); 631d79b1074SAndy Lutomirski 632d79b1074SAndy Lutomirski return sprintf(buf, "%d\n", (int)wblock->gblock.instance_count); 633d79b1074SAndy Lutomirski } 634d79b1074SAndy Lutomirski static DEVICE_ATTR_RO(instance_count); 635d79b1074SAndy Lutomirski 636d79b1074SAndy Lutomirski static ssize_t expensive_show(struct device *dev, 637d79b1074SAndy Lutomirski struct device_attribute *attr, char *buf) 638d79b1074SAndy Lutomirski { 639d79b1074SAndy Lutomirski struct wmi_block *wblock = dev_to_wblock(dev); 640d79b1074SAndy Lutomirski 641d79b1074SAndy Lutomirski return sprintf(buf, "%d\n", 642d79b1074SAndy Lutomirski (wblock->gblock.flags & ACPI_WMI_EXPENSIVE) != 0); 643d79b1074SAndy Lutomirski } 644d79b1074SAndy Lutomirski static DEVICE_ATTR_RO(expensive); 645d79b1074SAndy Lutomirski 646e80b89a5SGreg Kroah-Hartman static struct attribute *wmi_attrs[] = { 647e80b89a5SGreg Kroah-Hartman &dev_attr_modalias.attr, 648844af950SAndy Lutomirski &dev_attr_guid.attr, 649d79b1074SAndy Lutomirski &dev_attr_instance_count.attr, 650d79b1074SAndy Lutomirski &dev_attr_expensive.attr, 651e80b89a5SGreg Kroah-Hartman NULL, 652614ef432SDmitry Torokhov }; 653e80b89a5SGreg Kroah-Hartman ATTRIBUTE_GROUPS(wmi); 6541caab3c1SMatthew Garrett 655d79b1074SAndy Lutomirski static ssize_t notify_id_show(struct device *dev, struct device_attribute *attr, 656d79b1074SAndy Lutomirski char *buf) 657d79b1074SAndy Lutomirski { 658d79b1074SAndy Lutomirski struct wmi_block *wblock = dev_to_wblock(dev); 659d79b1074SAndy Lutomirski 660d79b1074SAndy Lutomirski return sprintf(buf, "%02X\n", (unsigned int)wblock->gblock.notify_id); 661d79b1074SAndy Lutomirski } 662d79b1074SAndy Lutomirski static DEVICE_ATTR_RO(notify_id); 663d79b1074SAndy Lutomirski 664d79b1074SAndy Lutomirski static struct attribute *wmi_event_attrs[] = { 665d79b1074SAndy Lutomirski &dev_attr_notify_id.attr, 666d79b1074SAndy Lutomirski NULL, 667d79b1074SAndy Lutomirski }; 668d79b1074SAndy Lutomirski ATTRIBUTE_GROUPS(wmi_event); 669d79b1074SAndy Lutomirski 670d79b1074SAndy Lutomirski static ssize_t object_id_show(struct device *dev, struct device_attribute *attr, 671d79b1074SAndy Lutomirski char *buf) 672d79b1074SAndy Lutomirski { 673d79b1074SAndy Lutomirski struct wmi_block *wblock = dev_to_wblock(dev); 674d79b1074SAndy Lutomirski 675d79b1074SAndy Lutomirski return sprintf(buf, "%c%c\n", wblock->gblock.object_id[0], 676d79b1074SAndy Lutomirski wblock->gblock.object_id[1]); 677d79b1074SAndy Lutomirski } 678d79b1074SAndy Lutomirski static DEVICE_ATTR_RO(object_id); 679d79b1074SAndy Lutomirski 680d4fc91adSAndy Lutomirski static ssize_t readable_show(struct device *dev, struct device_attribute *attr, 681d4fc91adSAndy Lutomirski char *buf) 682d4fc91adSAndy Lutomirski { 683d4fc91adSAndy Lutomirski struct wmi_device *wdev = dev_to_wdev(dev); 684d4fc91adSAndy Lutomirski 685d4fc91adSAndy Lutomirski return sprintf(buf, "%d\n", (int)wdev->readable); 686d4fc91adSAndy Lutomirski } 687d4fc91adSAndy Lutomirski static DEVICE_ATTR_RO(readable); 688d4fc91adSAndy Lutomirski 689d4fc91adSAndy Lutomirski static ssize_t writeable_show(struct device *dev, struct device_attribute *attr, 690d4fc91adSAndy Lutomirski char *buf) 691d4fc91adSAndy Lutomirski { 692d4fc91adSAndy Lutomirski struct wmi_device *wdev = dev_to_wdev(dev); 693d4fc91adSAndy Lutomirski 694d4fc91adSAndy Lutomirski return sprintf(buf, "%d\n", (int)wdev->writeable); 695d4fc91adSAndy Lutomirski } 696d4fc91adSAndy Lutomirski static DEVICE_ATTR_RO(writeable); 697d4fc91adSAndy Lutomirski 698d4fc91adSAndy Lutomirski static struct attribute *wmi_data_attrs[] = { 699d4fc91adSAndy Lutomirski &dev_attr_object_id.attr, 700d4fc91adSAndy Lutomirski &dev_attr_readable.attr, 701d4fc91adSAndy Lutomirski &dev_attr_writeable.attr, 702d4fc91adSAndy Lutomirski NULL, 703d4fc91adSAndy Lutomirski }; 704d4fc91adSAndy Lutomirski ATTRIBUTE_GROUPS(wmi_data); 705d4fc91adSAndy Lutomirski 706d4fc91adSAndy Lutomirski static struct attribute *wmi_method_attrs[] = { 707d79b1074SAndy Lutomirski &dev_attr_object_id.attr, 708d79b1074SAndy Lutomirski NULL, 709d79b1074SAndy Lutomirski }; 710d4fc91adSAndy Lutomirski ATTRIBUTE_GROUPS(wmi_method); 711d79b1074SAndy Lutomirski 7121caab3c1SMatthew Garrett static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env) 7131caab3c1SMatthew Garrett { 714844af950SAndy Lutomirski struct wmi_block *wblock = dev_to_wblock(dev); 7151caab3c1SMatthew Garrett 716844af950SAndy Lutomirski if (add_uevent_var(env, "MODALIAS=wmi:%pUL", wblock->gblock.guid)) 7171caab3c1SMatthew Garrett return -ENOMEM; 7181caab3c1SMatthew Garrett 719844af950SAndy Lutomirski if (add_uevent_var(env, "WMI_GUID=%pUL", wblock->gblock.guid)) 7201caab3c1SMatthew Garrett return -ENOMEM; 7211caab3c1SMatthew Garrett 7221caab3c1SMatthew Garrett return 0; 7231caab3c1SMatthew Garrett } 7241caab3c1SMatthew Garrett 725844af950SAndy Lutomirski static void wmi_dev_release(struct device *dev) 7261caab3c1SMatthew Garrett { 727844af950SAndy Lutomirski struct wmi_block *wblock = dev_to_wblock(dev); 728c64eefd4SDmitry Torokhov 729844af950SAndy Lutomirski kfree(wblock); 7301caab3c1SMatthew Garrett } 7311caab3c1SMatthew Garrett 732844af950SAndy Lutomirski static int wmi_dev_match(struct device *dev, struct device_driver *driver) 733844af950SAndy Lutomirski { 734844af950SAndy Lutomirski struct wmi_driver *wmi_driver = 735844af950SAndy Lutomirski container_of(driver, struct wmi_driver, driver); 736844af950SAndy Lutomirski struct wmi_block *wblock = dev_to_wblock(dev); 737844af950SAndy Lutomirski const struct wmi_device_id *id = wmi_driver->id_table; 738844af950SAndy Lutomirski 739844af950SAndy Lutomirski while (id->guid_string) { 740844af950SAndy Lutomirski uuid_le driver_guid; 741844af950SAndy Lutomirski 742844af950SAndy Lutomirski if (WARN_ON(uuid_le_to_bin(id->guid_string, &driver_guid))) 743844af950SAndy Lutomirski continue; 744844af950SAndy Lutomirski if (!memcmp(&driver_guid, wblock->gblock.guid, 16)) 745844af950SAndy Lutomirski return 1; 746844af950SAndy Lutomirski 747844af950SAndy Lutomirski id++; 748844af950SAndy Lutomirski } 749844af950SAndy Lutomirski 750844af950SAndy Lutomirski return 0; 751844af950SAndy Lutomirski } 752844af950SAndy Lutomirski 753844af950SAndy Lutomirski static int wmi_dev_probe(struct device *dev) 754844af950SAndy Lutomirski { 755844af950SAndy Lutomirski struct wmi_block *wblock = dev_to_wblock(dev); 756844af950SAndy Lutomirski struct wmi_driver *wdriver = 757844af950SAndy Lutomirski container_of(dev->driver, struct wmi_driver, driver); 758844af950SAndy Lutomirski int ret = 0; 759844af950SAndy Lutomirski 760844af950SAndy Lutomirski if (ACPI_FAILURE(wmi_method_enable(wblock, 1))) 761844af950SAndy Lutomirski dev_warn(dev, "failed to enable device -- probing anyway\n"); 762844af950SAndy Lutomirski 763844af950SAndy Lutomirski if (wdriver->probe) { 764844af950SAndy Lutomirski ret = wdriver->probe(dev_to_wdev(dev)); 765844af950SAndy Lutomirski if (ret != 0 && ACPI_FAILURE(wmi_method_enable(wblock, 0))) 766844af950SAndy Lutomirski dev_warn(dev, "failed to disable device\n"); 767844af950SAndy Lutomirski } 768844af950SAndy Lutomirski 769844af950SAndy Lutomirski return ret; 770844af950SAndy Lutomirski } 771844af950SAndy Lutomirski 772844af950SAndy Lutomirski static int wmi_dev_remove(struct device *dev) 773844af950SAndy Lutomirski { 774844af950SAndy Lutomirski struct wmi_block *wblock = dev_to_wblock(dev); 775844af950SAndy Lutomirski struct wmi_driver *wdriver = 776844af950SAndy Lutomirski container_of(dev->driver, struct wmi_driver, driver); 777844af950SAndy Lutomirski int ret = 0; 778844af950SAndy Lutomirski 779844af950SAndy Lutomirski if (wdriver->remove) 780844af950SAndy Lutomirski ret = wdriver->remove(dev_to_wdev(dev)); 781844af950SAndy Lutomirski 782844af950SAndy Lutomirski if (ACPI_FAILURE(wmi_method_enable(wblock, 0))) 783844af950SAndy Lutomirski dev_warn(dev, "failed to disable device\n"); 784844af950SAndy Lutomirski 785844af950SAndy Lutomirski return ret; 786844af950SAndy Lutomirski } 787844af950SAndy Lutomirski 788844af950SAndy Lutomirski static struct class wmi_bus_class = { 789844af950SAndy Lutomirski .name = "wmi_bus", 7901caab3c1SMatthew Garrett }; 7911caab3c1SMatthew Garrett 792844af950SAndy Lutomirski static struct bus_type wmi_bus_type = { 793844af950SAndy Lutomirski .name = "wmi", 794844af950SAndy Lutomirski .dev_groups = wmi_groups, 795844af950SAndy Lutomirski .match = wmi_dev_match, 796844af950SAndy Lutomirski .uevent = wmi_dev_uevent, 797844af950SAndy Lutomirski .probe = wmi_dev_probe, 798844af950SAndy Lutomirski .remove = wmi_dev_remove, 799844af950SAndy Lutomirski }; 800844af950SAndy Lutomirski 801d79b1074SAndy Lutomirski static struct device_type wmi_type_event = { 802d79b1074SAndy Lutomirski .name = "event", 803d79b1074SAndy Lutomirski .groups = wmi_event_groups, 804d79b1074SAndy Lutomirski .release = wmi_dev_release, 805d79b1074SAndy Lutomirski }; 806d79b1074SAndy Lutomirski 807d79b1074SAndy Lutomirski static struct device_type wmi_type_method = { 808d79b1074SAndy Lutomirski .name = "method", 809d4fc91adSAndy Lutomirski .groups = wmi_method_groups, 810d79b1074SAndy Lutomirski .release = wmi_dev_release, 811d79b1074SAndy Lutomirski }; 812d79b1074SAndy Lutomirski 813d79b1074SAndy Lutomirski static struct device_type wmi_type_data = { 814d79b1074SAndy Lutomirski .name = "data", 815d4fc91adSAndy Lutomirski .groups = wmi_data_groups, 816d79b1074SAndy Lutomirski .release = wmi_dev_release, 817d79b1074SAndy Lutomirski }; 818d79b1074SAndy Lutomirski 8196ee50aaaSDarren Hart (VMware) static void wmi_create_device(struct device *wmi_bus_dev, 820844af950SAndy Lutomirski const struct guid_block *gblock, 8217f5809bfSAndy Lutomirski struct wmi_block *wblock, 8227f5809bfSAndy Lutomirski struct acpi_device *device) 8231caab3c1SMatthew Garrett { 824844af950SAndy Lutomirski wblock->dev.dev.bus = &wmi_bus_type; 825844af950SAndy Lutomirski wblock->dev.dev.parent = wmi_bus_dev; 8261caab3c1SMatthew Garrett 827844af950SAndy Lutomirski dev_set_name(&wblock->dev.dev, "%pUL", gblock->guid); 8281caab3c1SMatthew Garrett 829d79b1074SAndy Lutomirski if (gblock->flags & ACPI_WMI_EVENT) { 830d79b1074SAndy Lutomirski wblock->dev.dev.type = &wmi_type_event; 831d79b1074SAndy Lutomirski } else if (gblock->flags & ACPI_WMI_METHOD) { 832d79b1074SAndy Lutomirski wblock->dev.dev.type = &wmi_type_method; 833d79b1074SAndy Lutomirski } else { 834d4fc91adSAndy Lutomirski struct acpi_device_info *info; 835d4fc91adSAndy Lutomirski char method[5]; 836d4fc91adSAndy Lutomirski int result; 837d4fc91adSAndy Lutomirski 838d79b1074SAndy Lutomirski wblock->dev.dev.type = &wmi_type_data; 839d4fc91adSAndy Lutomirski 840d4fc91adSAndy Lutomirski strcpy(method, "WQ"); 841d4fc91adSAndy Lutomirski strncat(method, wblock->gblock.object_id, 2); 842d4fc91adSAndy Lutomirski result = get_subobj_info(device->handle, method, &info); 843d4fc91adSAndy Lutomirski 844d4fc91adSAndy Lutomirski if (result == 0) { 845d4fc91adSAndy Lutomirski wblock->dev.readable = true; 846d4fc91adSAndy Lutomirski 847d4fc91adSAndy Lutomirski /* 848d4fc91adSAndy Lutomirski * The Microsoft documentation specifically states: 849d4fc91adSAndy Lutomirski * 850d4fc91adSAndy Lutomirski * Data blocks registered with only a single instance 851d4fc91adSAndy Lutomirski * can ignore the parameter. 852d4fc91adSAndy Lutomirski * 853d4fc91adSAndy Lutomirski * ACPICA will get mad at us if we call the method 854d4fc91adSAndy Lutomirski * with the wrong number of arguments, so check what 855d4fc91adSAndy Lutomirski * our method expects. (On some Dell laptops, WQxx 856d4fc91adSAndy Lutomirski * may not be a method at all.) 857d4fc91adSAndy Lutomirski */ 858d4fc91adSAndy Lutomirski if (info->type != ACPI_TYPE_METHOD || 859d4fc91adSAndy Lutomirski info->param_count == 0) 860d4fc91adSAndy Lutomirski wblock->read_takes_no_args = true; 861d4fc91adSAndy Lutomirski 862d4fc91adSAndy Lutomirski kfree(info); 863d4fc91adSAndy Lutomirski } 864d4fc91adSAndy Lutomirski 865d4fc91adSAndy Lutomirski strcpy(method, "WS"); 866d4fc91adSAndy Lutomirski strncat(method, wblock->gblock.object_id, 2); 867d4fc91adSAndy Lutomirski result = get_subobj_info(device->handle, method, NULL); 868d4fc91adSAndy Lutomirski 869d4fc91adSAndy Lutomirski if (result == 0) { 870d4fc91adSAndy Lutomirski wblock->dev.writeable = true; 871d4fc91adSAndy Lutomirski } 872d4fc91adSAndy Lutomirski 873d79b1074SAndy Lutomirski } 874c64eefd4SDmitry Torokhov 8756ee50aaaSDarren Hart (VMware) device_initialize(&wblock->dev.dev); 8761caab3c1SMatthew Garrett } 8771caab3c1SMatthew Garrett 878b0e86302SAndy Lutomirski static void wmi_free_devices(struct acpi_device *device) 8791caab3c1SMatthew Garrett { 880c64eefd4SDmitry Torokhov struct wmi_block *wblock, *next; 8811caab3c1SMatthew Garrett 8821caab3c1SMatthew Garrett /* Delete devices for all the GUIDs */ 883023b9565SDmitry Torokhov list_for_each_entry_safe(wblock, next, &wmi_block_list, list) { 884b0e86302SAndy Lutomirski if (wblock->acpi_device == device) { 885023b9565SDmitry Torokhov list_del(&wblock->list); 886844af950SAndy Lutomirski device_unregister(&wblock->dev.dev); 887023b9565SDmitry Torokhov } 8881caab3c1SMatthew Garrett } 889b0e86302SAndy Lutomirski } 8901caab3c1SMatthew Garrett 891b0e86302SAndy Lutomirski static bool guid_already_parsed(struct acpi_device *device, 892b0e86302SAndy Lutomirski const u8 *guid) 893d1f9e497SCarlos Corbacho { 894d1f9e497SCarlos Corbacho struct wmi_block *wblock; 895d1f9e497SCarlos Corbacho 896b0e86302SAndy Lutomirski list_for_each_entry(wblock, &wmi_block_list, list) { 897b0e86302SAndy Lutomirski if (memcmp(wblock->gblock.guid, guid, 16) == 0) { 898b0e86302SAndy Lutomirski /* 899b0e86302SAndy Lutomirski * Because we historically didn't track the relationship 900b0e86302SAndy Lutomirski * between GUIDs and ACPI nodes, we don't know whether 901b0e86302SAndy Lutomirski * we need to suppress GUIDs that are unique on a 902b0e86302SAndy Lutomirski * given node but duplicated across nodes. 903b0e86302SAndy Lutomirski */ 904b0e86302SAndy Lutomirski dev_warn(&device->dev, "duplicate WMI GUID %pUL (first instance was on %s)\n", 905b0e86302SAndy Lutomirski guid, dev_name(&wblock->acpi_device->dev)); 906d1f9e497SCarlos Corbacho return true; 907b0e86302SAndy Lutomirski } 908b0e86302SAndy Lutomirski } 909c64eefd4SDmitry Torokhov 910d1f9e497SCarlos Corbacho return false; 911d1f9e497SCarlos Corbacho } 912d1f9e497SCarlos Corbacho 9131caab3c1SMatthew Garrett /* 914b4f9fe12SLen Brown * Parse the _WDG method for the GUID data blocks 915b4f9fe12SLen Brown */ 916844af950SAndy Lutomirski static int parse_wdg(struct device *wmi_bus_dev, struct acpi_device *device) 917b4f9fe12SLen Brown { 918b4f9fe12SLen Brown struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL}; 91937830662SDmitry Torokhov const struct guid_block *gblock; 9206ee50aaaSDarren Hart (VMware) struct wmi_block *wblock, *next; 9216ee50aaaSDarren Hart (VMware) union acpi_object *obj; 922b4f9fe12SLen Brown acpi_status status; 9236ee50aaaSDarren Hart (VMware) int retval = 0; 924b4f9fe12SLen Brown u32 i, total; 925b4f9fe12SLen Brown 9267f5809bfSAndy Lutomirski status = acpi_evaluate_object(device->handle, "_WDG", NULL, &out); 927b4f9fe12SLen Brown if (ACPI_FAILURE(status)) 928c64eefd4SDmitry Torokhov return -ENXIO; 929b4f9fe12SLen Brown 930b4f9fe12SLen Brown obj = (union acpi_object *) out.pointer; 9313d2c63ebSDmitry Torokhov if (!obj) 932c64eefd4SDmitry Torokhov return -ENXIO; 933b4f9fe12SLen Brown 93464ed0ab8SDmitry Torokhov if (obj->type != ACPI_TYPE_BUFFER) { 935c64eefd4SDmitry Torokhov retval = -ENXIO; 93664ed0ab8SDmitry Torokhov goto out_free_pointer; 93764ed0ab8SDmitry Torokhov } 938b4f9fe12SLen Brown 93937830662SDmitry Torokhov gblock = (const struct guid_block *)obj->buffer.pointer; 940b4f9fe12SLen Brown total = obj->buffer.length / sizeof(struct guid_block); 941b4f9fe12SLen Brown 942b4f9fe12SLen Brown for (i = 0; i < total; i++) { 943a929aae0SThomas Renninger if (debug_dump_wdg) 944a929aae0SThomas Renninger wmi_dump_wdg(&gblock[i]); 945a929aae0SThomas Renninger 946a1c31bcdSAndy Lutomirski /* 947a1c31bcdSAndy Lutomirski * Some WMI devices, like those for nVidia hooks, have a 948a1c31bcdSAndy Lutomirski * duplicate GUID. It's not clear what we should do in this 949a1c31bcdSAndy Lutomirski * case yet, so for now, we'll just ignore the duplicate 950a1c31bcdSAndy Lutomirski * for device creation. 951a1c31bcdSAndy Lutomirski */ 952a1c31bcdSAndy Lutomirski if (guid_already_parsed(device, gblock[i].guid)) 953a1c31bcdSAndy Lutomirski continue; 954a1c31bcdSAndy Lutomirski 95558f6425eSColin King wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL); 9566ee50aaaSDarren Hart (VMware) if (!wblock) { 9576ee50aaaSDarren Hart (VMware) retval = -ENOMEM; 9586ee50aaaSDarren Hart (VMware) break; 9596ee50aaaSDarren Hart (VMware) } 96058f6425eSColin King 961b0e86302SAndy Lutomirski wblock->acpi_device = device; 96258f6425eSColin King wblock->gblock = gblock[i]; 96358f6425eSColin King 9646ee50aaaSDarren Hart (VMware) wmi_create_device(wmi_bus_dev, &gblock[i], wblock, device); 96558f6425eSColin King 96658f6425eSColin King list_add_tail(&wblock->list, &wmi_block_list); 967b4f9fe12SLen Brown 968fc3155b2SThomas Renninger if (debug_event) { 969fc3155b2SThomas Renninger wblock->handler = wmi_notify_debug; 9702d5ab555SDmitry Torokhov wmi_method_enable(wblock, 1); 971fc3155b2SThomas Renninger } 972b4f9fe12SLen Brown } 973b4f9fe12SLen Brown 9746ee50aaaSDarren Hart (VMware) /* 9756ee50aaaSDarren Hart (VMware) * Now that all of the devices are created, add them to the 9766ee50aaaSDarren Hart (VMware) * device tree and probe subdrivers. 9776ee50aaaSDarren Hart (VMware) */ 9786ee50aaaSDarren Hart (VMware) list_for_each_entry_safe(wblock, next, &wmi_block_list, list) { 9796ee50aaaSDarren Hart (VMware) if (wblock->acpi_device != device) 9806ee50aaaSDarren Hart (VMware) continue; 9816ee50aaaSDarren Hart (VMware) 9826ee50aaaSDarren Hart (VMware) retval = device_add(&wblock->dev.dev); 9836ee50aaaSDarren Hart (VMware) if (retval) { 9846ee50aaaSDarren Hart (VMware) dev_err(wmi_bus_dev, "failed to register %pULL\n", 9856ee50aaaSDarren Hart (VMware) wblock->gblock.guid); 9866ee50aaaSDarren Hart (VMware) if (debug_event) 9876ee50aaaSDarren Hart (VMware) wmi_method_enable(wblock, 0); 9886ee50aaaSDarren Hart (VMware) list_del(&wblock->list); 9896ee50aaaSDarren Hart (VMware) put_device(&wblock->dev.dev); 9906ee50aaaSDarren Hart (VMware) } 9916ee50aaaSDarren Hart (VMware) } 992c64eefd4SDmitry Torokhov 993a5167c5bSAxel Lin out_free_pointer: 994a5167c5bSAxel Lin kfree(out.pointer); 995c64eefd4SDmitry Torokhov return retval; 996b4f9fe12SLen Brown } 997b4f9fe12SLen Brown 998b4f9fe12SLen Brown /* 999b4f9fe12SLen Brown * WMI can have EmbeddedControl access regions. In which case, we just want to 1000b4f9fe12SLen Brown * hand these off to the EC driver. 1001b4f9fe12SLen Brown */ 1002b4f9fe12SLen Brown static acpi_status 1003b4f9fe12SLen Brown acpi_wmi_ec_space_handler(u32 function, acpi_physical_address address, 1004439913ffSLin Ming u32 bits, u64 *value, 1005b4f9fe12SLen Brown void *handler_context, void *region_context) 1006b4f9fe12SLen Brown { 1007b4f9fe12SLen Brown int result = 0, i = 0; 1008b4f9fe12SLen Brown u8 temp = 0; 1009b4f9fe12SLen Brown 1010b4f9fe12SLen Brown if ((address > 0xFF) || !value) 1011b4f9fe12SLen Brown return AE_BAD_PARAMETER; 1012b4f9fe12SLen Brown 1013b4f9fe12SLen Brown if (function != ACPI_READ && function != ACPI_WRITE) 1014b4f9fe12SLen Brown return AE_BAD_PARAMETER; 1015b4f9fe12SLen Brown 1016b4f9fe12SLen Brown if (bits != 8) 1017b4f9fe12SLen Brown return AE_BAD_PARAMETER; 1018b4f9fe12SLen Brown 1019b4f9fe12SLen Brown if (function == ACPI_READ) { 1020b4f9fe12SLen Brown result = ec_read(address, &temp); 1021439913ffSLin Ming (*value) |= ((u64)temp) << i; 1022b4f9fe12SLen Brown } else { 1023b4f9fe12SLen Brown temp = 0xff & ((*value) >> i); 1024b4f9fe12SLen Brown result = ec_write(address, temp); 1025b4f9fe12SLen Brown } 1026b4f9fe12SLen Brown 1027b4f9fe12SLen Brown switch (result) { 1028b4f9fe12SLen Brown case -EINVAL: 1029b4f9fe12SLen Brown return AE_BAD_PARAMETER; 1030b4f9fe12SLen Brown break; 1031b4f9fe12SLen Brown case -ENODEV: 1032b4f9fe12SLen Brown return AE_NOT_FOUND; 1033b4f9fe12SLen Brown break; 1034b4f9fe12SLen Brown case -ETIME: 1035b4f9fe12SLen Brown return AE_TIME; 1036b4f9fe12SLen Brown break; 1037b4f9fe12SLen Brown default: 1038b4f9fe12SLen Brown return AE_OK; 1039b4f9fe12SLen Brown } 1040b4f9fe12SLen Brown } 1041b4f9fe12SLen Brown 10421686f544SAndy Lutomirski static void acpi_wmi_notify_handler(acpi_handle handle, u32 event, 10431686f544SAndy Lutomirski void *context) 1044b4f9fe12SLen Brown { 1045b4f9fe12SLen Brown struct guid_block *block; 1046b4f9fe12SLen Brown struct wmi_block *wblock; 1047b4f9fe12SLen Brown struct list_head *p; 10481686f544SAndy Lutomirski bool found_it = false; 1049b4f9fe12SLen Brown 1050762e1a2fSDmitry Torokhov list_for_each(p, &wmi_block_list) { 1051b4f9fe12SLen Brown wblock = list_entry(p, struct wmi_block, list); 1052b4f9fe12SLen Brown block = &wblock->gblock; 1053b4f9fe12SLen Brown 10541686f544SAndy Lutomirski if (wblock->acpi_device->handle == handle && 1055b0e86302SAndy Lutomirski (block->flags & ACPI_WMI_EVENT) && 10561686f544SAndy Lutomirski (block->notify_id == event)) 10571686f544SAndy Lutomirski { 10581686f544SAndy Lutomirski found_it = true; 10591686f544SAndy Lutomirski break; 10601686f544SAndy Lutomirski } 10611686f544SAndy Lutomirski } 10621686f544SAndy Lutomirski 10631686f544SAndy Lutomirski if (!found_it) 10641686f544SAndy Lutomirski return; 10651686f544SAndy Lutomirski 10661686f544SAndy Lutomirski /* If a driver is bound, then notify the driver. */ 10671686f544SAndy Lutomirski if (wblock->dev.dev.driver) { 10681686f544SAndy Lutomirski struct wmi_driver *driver; 10691686f544SAndy Lutomirski struct acpi_object_list input; 10701686f544SAndy Lutomirski union acpi_object params[1]; 10711686f544SAndy Lutomirski struct acpi_buffer evdata = { ACPI_ALLOCATE_BUFFER, NULL }; 10721686f544SAndy Lutomirski acpi_status status; 10731686f544SAndy Lutomirski 10741686f544SAndy Lutomirski driver = container_of(wblock->dev.dev.driver, 10751686f544SAndy Lutomirski struct wmi_driver, driver); 10761686f544SAndy Lutomirski 10771686f544SAndy Lutomirski input.count = 1; 10781686f544SAndy Lutomirski input.pointer = params; 10791686f544SAndy Lutomirski params[0].type = ACPI_TYPE_INTEGER; 10801686f544SAndy Lutomirski params[0].integer.value = event; 10811686f544SAndy Lutomirski 10821686f544SAndy Lutomirski status = acpi_evaluate_object(wblock->acpi_device->handle, 10831686f544SAndy Lutomirski "_WED", &input, &evdata); 10841686f544SAndy Lutomirski if (ACPI_FAILURE(status)) { 10851686f544SAndy Lutomirski dev_warn(&wblock->dev.dev, 10861686f544SAndy Lutomirski "failed to get event data\n"); 10871686f544SAndy Lutomirski return; 10881686f544SAndy Lutomirski } 10891686f544SAndy Lutomirski 10901686f544SAndy Lutomirski if (driver->notify) 10911686f544SAndy Lutomirski driver->notify(&wblock->dev, 10921686f544SAndy Lutomirski (union acpi_object *)evdata.pointer); 10931686f544SAndy Lutomirski 10941686f544SAndy Lutomirski kfree(evdata.pointer); 10951686f544SAndy Lutomirski } else if (wblock->handler) { 10961686f544SAndy Lutomirski /* Legacy handler */ 1097b4f9fe12SLen Brown wblock->handler(event, wblock->handler_data); 10981686f544SAndy Lutomirski } 10991686f544SAndy Lutomirski 11007715348cSThomas Renninger if (debug_event) { 110185b4e4ebSRasmus Villemoes pr_info("DEBUG Event GUID: %pUL\n", 110285b4e4ebSRasmus Villemoes wblock->gblock.guid); 11037715348cSThomas Renninger } 1104b4f9fe12SLen Brown 1105b4f9fe12SLen Brown acpi_bus_generate_netlink_event( 11061686f544SAndy Lutomirski wblock->acpi_device->pnp.device_class, 11071686f544SAndy Lutomirski dev_name(&wblock->dev.dev), 1108b4f9fe12SLen Brown event, 0); 11091686f544SAndy Lutomirski 1110b4f9fe12SLen Brown } 1111b4f9fe12SLen Brown 11129599ed91SAndy Lutomirski static int acpi_wmi_remove(struct platform_device *device) 1113b4f9fe12SLen Brown { 11149599ed91SAndy Lutomirski struct acpi_device *acpi_device = ACPI_COMPANION(&device->dev); 11159599ed91SAndy Lutomirski 11169599ed91SAndy Lutomirski acpi_remove_notify_handler(acpi_device->handle, ACPI_DEVICE_NOTIFY, 11171686f544SAndy Lutomirski acpi_wmi_notify_handler); 11189599ed91SAndy Lutomirski acpi_remove_address_space_handler(acpi_device->handle, 1119b4f9fe12SLen Brown ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler); 11209599ed91SAndy Lutomirski wmi_free_devices(acpi_device); 11219599ed91SAndy Lutomirski device_unregister((struct device *)dev_get_drvdata(&device->dev)); 1122b4f9fe12SLen Brown 1123b4f9fe12SLen Brown return 0; 1124b4f9fe12SLen Brown } 1125b4f9fe12SLen Brown 11269599ed91SAndy Lutomirski static int acpi_wmi_probe(struct platform_device *device) 1127b4f9fe12SLen Brown { 11289599ed91SAndy Lutomirski struct acpi_device *acpi_device; 1129844af950SAndy Lutomirski struct device *wmi_bus_dev; 1130b4f9fe12SLen Brown acpi_status status; 1131c64eefd4SDmitry Torokhov int error; 1132b4f9fe12SLen Brown 11339599ed91SAndy Lutomirski acpi_device = ACPI_COMPANION(&device->dev); 11349599ed91SAndy Lutomirski if (!acpi_device) { 11359599ed91SAndy Lutomirski dev_err(&device->dev, "ACPI companion is missing\n"); 11369599ed91SAndy Lutomirski return -ENODEV; 11379599ed91SAndy Lutomirski } 11389599ed91SAndy Lutomirski 11399599ed91SAndy Lutomirski status = acpi_install_address_space_handler(acpi_device->handle, 1140b4f9fe12SLen Brown ACPI_ADR_SPACE_EC, 1141b4f9fe12SLen Brown &acpi_wmi_ec_space_handler, 1142b4f9fe12SLen Brown NULL, NULL); 11435212cd67SDmitry Torokhov if (ACPI_FAILURE(status)) { 114446492ee4SAndy Lutomirski dev_err(&device->dev, "Error installing EC region handler\n"); 1145b4f9fe12SLen Brown return -ENODEV; 11465212cd67SDmitry Torokhov } 1147b4f9fe12SLen Brown 11489599ed91SAndy Lutomirski status = acpi_install_notify_handler(acpi_device->handle, 11499599ed91SAndy Lutomirski ACPI_DEVICE_NOTIFY, 11501686f544SAndy Lutomirski acpi_wmi_notify_handler, 11511686f544SAndy Lutomirski NULL); 11521686f544SAndy Lutomirski if (ACPI_FAILURE(status)) { 11531686f544SAndy Lutomirski dev_err(&device->dev, "Error installing notify handler\n"); 11541686f544SAndy Lutomirski error = -ENODEV; 11551686f544SAndy Lutomirski goto err_remove_ec_handler; 11561686f544SAndy Lutomirski } 11571686f544SAndy Lutomirski 1158844af950SAndy Lutomirski wmi_bus_dev = device_create(&wmi_bus_class, &device->dev, MKDEV(0, 0), 1159844af950SAndy Lutomirski NULL, "wmi_bus-%s", dev_name(&device->dev)); 1160844af950SAndy Lutomirski if (IS_ERR(wmi_bus_dev)) { 1161844af950SAndy Lutomirski error = PTR_ERR(wmi_bus_dev); 11621686f544SAndy Lutomirski goto err_remove_notify_handler; 1163844af950SAndy Lutomirski } 11649599ed91SAndy Lutomirski dev_set_drvdata(&device->dev, wmi_bus_dev); 1165844af950SAndy Lutomirski 11669599ed91SAndy Lutomirski error = parse_wdg(wmi_bus_dev, acpi_device); 1167c64eefd4SDmitry Torokhov if (error) { 11688e07514dSDmitry Torokhov pr_err("Failed to parse WDG method\n"); 1169844af950SAndy Lutomirski goto err_remove_busdev; 1170b4f9fe12SLen Brown } 1171b4f9fe12SLen Brown 1172c64eefd4SDmitry Torokhov return 0; 117346492ee4SAndy Lutomirski 1174844af950SAndy Lutomirski err_remove_busdev: 1175844af950SAndy Lutomirski device_unregister(wmi_bus_dev); 1176844af950SAndy Lutomirski 11771686f544SAndy Lutomirski err_remove_notify_handler: 11789599ed91SAndy Lutomirski acpi_remove_notify_handler(acpi_device->handle, ACPI_DEVICE_NOTIFY, 11791686f544SAndy Lutomirski acpi_wmi_notify_handler); 11801686f544SAndy Lutomirski 11811686f544SAndy Lutomirski err_remove_ec_handler: 11829599ed91SAndy Lutomirski acpi_remove_address_space_handler(acpi_device->handle, 118346492ee4SAndy Lutomirski ACPI_ADR_SPACE_EC, 118446492ee4SAndy Lutomirski &acpi_wmi_ec_space_handler); 118546492ee4SAndy Lutomirski 118646492ee4SAndy Lutomirski return error; 1187b4f9fe12SLen Brown } 1188b4f9fe12SLen Brown 1189844af950SAndy Lutomirski int __must_check __wmi_driver_register(struct wmi_driver *driver, 1190844af950SAndy Lutomirski struct module *owner) 1191844af950SAndy Lutomirski { 1192844af950SAndy Lutomirski driver->driver.owner = owner; 1193844af950SAndy Lutomirski driver->driver.bus = &wmi_bus_type; 1194844af950SAndy Lutomirski 1195844af950SAndy Lutomirski return driver_register(&driver->driver); 1196844af950SAndy Lutomirski } 1197844af950SAndy Lutomirski EXPORT_SYMBOL(__wmi_driver_register); 1198844af950SAndy Lutomirski 1199844af950SAndy Lutomirski void wmi_driver_unregister(struct wmi_driver *driver) 1200844af950SAndy Lutomirski { 1201844af950SAndy Lutomirski driver_unregister(&driver->driver); 1202844af950SAndy Lutomirski } 1203844af950SAndy Lutomirski EXPORT_SYMBOL(wmi_driver_unregister); 1204844af950SAndy Lutomirski 1205b4f9fe12SLen Brown static int __init acpi_wmi_init(void) 1206b4f9fe12SLen Brown { 1207c64eefd4SDmitry Torokhov int error; 1208b4f9fe12SLen Brown 1209b4f9fe12SLen Brown if (acpi_disabled) 1210b4f9fe12SLen Brown return -ENODEV; 1211b4f9fe12SLen Brown 1212844af950SAndy Lutomirski error = class_register(&wmi_bus_class); 1213c64eefd4SDmitry Torokhov if (error) 1214c64eefd4SDmitry Torokhov return error; 1215b4f9fe12SLen Brown 1216844af950SAndy Lutomirski error = bus_register(&wmi_bus_type); 1217844af950SAndy Lutomirski if (error) 1218844af950SAndy Lutomirski goto err_unreg_class; 1219844af950SAndy Lutomirski 12209599ed91SAndy Lutomirski error = platform_driver_register(&acpi_wmi_driver); 1221c64eefd4SDmitry Torokhov if (error) { 1222c64eefd4SDmitry Torokhov pr_err("Error loading mapper\n"); 1223844af950SAndy Lutomirski goto err_unreg_bus; 12241caab3c1SMatthew Garrett } 12251caab3c1SMatthew Garrett 12268e07514dSDmitry Torokhov return 0; 1227844af950SAndy Lutomirski 1228844af950SAndy Lutomirski err_unreg_class: 1229844af950SAndy Lutomirski class_unregister(&wmi_bus_class); 1230844af950SAndy Lutomirski 1231844af950SAndy Lutomirski err_unreg_bus: 1232844af950SAndy Lutomirski bus_unregister(&wmi_bus_type); 1233844af950SAndy Lutomirski 1234844af950SAndy Lutomirski return error; 1235b4f9fe12SLen Brown } 1236b4f9fe12SLen Brown 1237b4f9fe12SLen Brown static void __exit acpi_wmi_exit(void) 1238b4f9fe12SLen Brown { 12399599ed91SAndy Lutomirski platform_driver_unregister(&acpi_wmi_driver); 1240844af950SAndy Lutomirski class_unregister(&wmi_bus_class); 1241844af950SAndy Lutomirski bus_unregister(&wmi_bus_type); 1242b4f9fe12SLen Brown } 1243b4f9fe12SLen Brown 1244b4f9fe12SLen Brown subsys_initcall(acpi_wmi_init); 1245b4f9fe12SLen Brown module_exit(acpi_wmi_exit); 1246