18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * ACPI-WMI mapping driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2007-2008 Carlos Corbacho <carlos@strangeworlds.co.uk> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * GUID parsing code from ldm.c is: 88c2ecf20Sopenharmony_ci * Copyright (C) 2001,2002 Richard Russon <ldm@flatcap.org> 98c2ecf20Sopenharmony_ci * Copyright (c) 2001-2007 Anton Altaparmakov 108c2ecf20Sopenharmony_ci * Copyright (C) 2001,2002 Jakob Kemi <jakob.kemi@telia.com> 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * WMI bus infrastructure by Andrew Lutomirski and Darren Hart: 138c2ecf20Sopenharmony_ci * Copyright (C) 2015 Andrew Lutomirski 148c2ecf20Sopenharmony_ci * Copyright (C) 2017 VMware, Inc. All Rights Reserved. 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <linux/acpi.h> 208c2ecf20Sopenharmony_ci#include <linux/device.h> 218c2ecf20Sopenharmony_ci#include <linux/init.h> 228c2ecf20Sopenharmony_ci#include <linux/kernel.h> 238c2ecf20Sopenharmony_ci#include <linux/list.h> 248c2ecf20Sopenharmony_ci#include <linux/miscdevice.h> 258c2ecf20Sopenharmony_ci#include <linux/module.h> 268c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 278c2ecf20Sopenharmony_ci#include <linux/slab.h> 288c2ecf20Sopenharmony_ci#include <linux/types.h> 298c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 308c2ecf20Sopenharmony_ci#include <linux/uuid.h> 318c2ecf20Sopenharmony_ci#include <linux/wmi.h> 328c2ecf20Sopenharmony_ci#include <linux/fs.h> 338c2ecf20Sopenharmony_ci#include <uapi/linux/wmi.h> 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ciACPI_MODULE_NAME("wmi"); 368c2ecf20Sopenharmony_ciMODULE_AUTHOR("Carlos Corbacho"); 378c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ACPI-WMI Mapping Driver"); 388c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic LIST_HEAD(wmi_block_list); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistruct guid_block { 438c2ecf20Sopenharmony_ci guid_t guid; 448c2ecf20Sopenharmony_ci union { 458c2ecf20Sopenharmony_ci char object_id[2]; 468c2ecf20Sopenharmony_ci struct { 478c2ecf20Sopenharmony_ci unsigned char notify_id; 488c2ecf20Sopenharmony_ci unsigned char reserved; 498c2ecf20Sopenharmony_ci }; 508c2ecf20Sopenharmony_ci }; 518c2ecf20Sopenharmony_ci u8 instance_count; 528c2ecf20Sopenharmony_ci u8 flags; 538c2ecf20Sopenharmony_ci}; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistruct wmi_block { 568c2ecf20Sopenharmony_ci struct wmi_device dev; 578c2ecf20Sopenharmony_ci struct list_head list; 588c2ecf20Sopenharmony_ci struct guid_block gblock; 598c2ecf20Sopenharmony_ci struct miscdevice char_dev; 608c2ecf20Sopenharmony_ci struct mutex char_mutex; 618c2ecf20Sopenharmony_ci struct acpi_device *acpi_device; 628c2ecf20Sopenharmony_ci wmi_notify_handler handler; 638c2ecf20Sopenharmony_ci void *handler_data; 648c2ecf20Sopenharmony_ci u64 req_buf_size; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci bool read_takes_no_args; 678c2ecf20Sopenharmony_ci}; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci/* 718c2ecf20Sopenharmony_ci * If the GUID data block is marked as expensive, we must enable and 728c2ecf20Sopenharmony_ci * explicitily disable data collection. 738c2ecf20Sopenharmony_ci */ 748c2ecf20Sopenharmony_ci#define ACPI_WMI_EXPENSIVE 0x1 758c2ecf20Sopenharmony_ci#define ACPI_WMI_METHOD 0x2 /* GUID is a method */ 768c2ecf20Sopenharmony_ci#define ACPI_WMI_STRING 0x4 /* GUID takes & returns a string */ 778c2ecf20Sopenharmony_ci#define ACPI_WMI_EVENT 0x8 /* GUID is an event */ 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic bool debug_event; 808c2ecf20Sopenharmony_cimodule_param(debug_event, bool, 0444); 818c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug_event, 828c2ecf20Sopenharmony_ci "Log WMI Events [0/1]"); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic bool debug_dump_wdg; 858c2ecf20Sopenharmony_cimodule_param(debug_dump_wdg, bool, 0444); 868c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug_dump_wdg, 878c2ecf20Sopenharmony_ci "Dump available WMI interfaces [0/1]"); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic int acpi_wmi_remove(struct platform_device *device); 908c2ecf20Sopenharmony_cistatic int acpi_wmi_probe(struct platform_device *device); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic const struct acpi_device_id wmi_device_ids[] = { 938c2ecf20Sopenharmony_ci {"PNP0C14", 0}, 948c2ecf20Sopenharmony_ci {"pnp0c14", 0}, 958c2ecf20Sopenharmony_ci {"", 0}, 968c2ecf20Sopenharmony_ci}; 978c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, wmi_device_ids); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic struct platform_driver acpi_wmi_driver = { 1008c2ecf20Sopenharmony_ci .driver = { 1018c2ecf20Sopenharmony_ci .name = "acpi-wmi", 1028c2ecf20Sopenharmony_ci .acpi_match_table = wmi_device_ids, 1038c2ecf20Sopenharmony_ci }, 1048c2ecf20Sopenharmony_ci .probe = acpi_wmi_probe, 1058c2ecf20Sopenharmony_ci .remove = acpi_wmi_remove, 1068c2ecf20Sopenharmony_ci}; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci/* 1098c2ecf20Sopenharmony_ci * GUID parsing functions 1108c2ecf20Sopenharmony_ci */ 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic bool find_guid(const char *guid_string, struct wmi_block **out) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci guid_t guid_input; 1158c2ecf20Sopenharmony_ci struct wmi_block *wblock; 1168c2ecf20Sopenharmony_ci struct guid_block *block; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci if (guid_parse(guid_string, &guid_input)) 1198c2ecf20Sopenharmony_ci return false; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci list_for_each_entry(wblock, &wmi_block_list, list) { 1228c2ecf20Sopenharmony_ci block = &wblock->gblock; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci if (guid_equal(&block->guid, &guid_input)) { 1258c2ecf20Sopenharmony_ci if (out) 1268c2ecf20Sopenharmony_ci *out = wblock; 1278c2ecf20Sopenharmony_ci return true; 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci } 1308c2ecf20Sopenharmony_ci return false; 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic bool guid_parse_and_compare(const char *string, const guid_t *guid) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci guid_t guid_input; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci if (guid_parse(string, &guid_input)) 1388c2ecf20Sopenharmony_ci return false; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci return guid_equal(&guid_input, guid); 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic const void *find_guid_context(struct wmi_block *wblock, 1448c2ecf20Sopenharmony_ci struct wmi_driver *wdriver) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci const struct wmi_device_id *id; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci if (wblock == NULL || wdriver == NULL) 1498c2ecf20Sopenharmony_ci return NULL; 1508c2ecf20Sopenharmony_ci if (wdriver->id_table == NULL) 1518c2ecf20Sopenharmony_ci return NULL; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci id = wdriver->id_table; 1548c2ecf20Sopenharmony_ci while (*id->guid_string) { 1558c2ecf20Sopenharmony_ci if (guid_parse_and_compare(id->guid_string, &wblock->gblock.guid)) 1568c2ecf20Sopenharmony_ci return id->context; 1578c2ecf20Sopenharmony_ci id++; 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci return NULL; 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cistatic int get_subobj_info(acpi_handle handle, const char *pathname, 1638c2ecf20Sopenharmony_ci struct acpi_device_info **info) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci struct acpi_device_info *dummy_info, **info_ptr; 1668c2ecf20Sopenharmony_ci acpi_handle subobj_handle; 1678c2ecf20Sopenharmony_ci acpi_status status; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci status = acpi_get_handle(handle, (char *)pathname, &subobj_handle); 1708c2ecf20Sopenharmony_ci if (status == AE_NOT_FOUND) 1718c2ecf20Sopenharmony_ci return -ENOENT; 1728c2ecf20Sopenharmony_ci else if (ACPI_FAILURE(status)) 1738c2ecf20Sopenharmony_ci return -EIO; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci info_ptr = info ? info : &dummy_info; 1768c2ecf20Sopenharmony_ci status = acpi_get_object_info(subobj_handle, info_ptr); 1778c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) 1788c2ecf20Sopenharmony_ci return -EIO; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci if (!info) 1818c2ecf20Sopenharmony_ci kfree(dummy_info); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci return 0; 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic acpi_status wmi_method_enable(struct wmi_block *wblock, int enable) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci struct guid_block *block; 1898c2ecf20Sopenharmony_ci char method[5]; 1908c2ecf20Sopenharmony_ci acpi_status status; 1918c2ecf20Sopenharmony_ci acpi_handle handle; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci block = &wblock->gblock; 1948c2ecf20Sopenharmony_ci handle = wblock->acpi_device->handle; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci snprintf(method, 5, "WE%02X", block->notify_id); 1978c2ecf20Sopenharmony_ci status = acpi_execute_simple_method(handle, method, enable); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci if (status != AE_OK && status != AE_NOT_FOUND) 2008c2ecf20Sopenharmony_ci return status; 2018c2ecf20Sopenharmony_ci else 2028c2ecf20Sopenharmony_ci return AE_OK; 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci/* 2068c2ecf20Sopenharmony_ci * Exported WMI functions 2078c2ecf20Sopenharmony_ci */ 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci/** 2108c2ecf20Sopenharmony_ci * set_required_buffer_size - Sets the buffer size needed for performing IOCTL 2118c2ecf20Sopenharmony_ci * @wdev: A wmi bus device from a driver 2128c2ecf20Sopenharmony_ci * @length: Required buffer size 2138c2ecf20Sopenharmony_ci * 2148c2ecf20Sopenharmony_ci * Allocates memory needed for buffer, stores the buffer size in that memory 2158c2ecf20Sopenharmony_ci */ 2168c2ecf20Sopenharmony_ciint set_required_buffer_size(struct wmi_device *wdev, u64 length) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci struct wmi_block *wblock; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci wblock = container_of(wdev, struct wmi_block, dev); 2218c2ecf20Sopenharmony_ci wblock->req_buf_size = length; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci return 0; 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(set_required_buffer_size); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci/** 2288c2ecf20Sopenharmony_ci * wmi_evaluate_method - Evaluate a WMI method 2298c2ecf20Sopenharmony_ci * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba 2308c2ecf20Sopenharmony_ci * @instance: Instance index 2318c2ecf20Sopenharmony_ci * @method_id: Method ID to call 2328c2ecf20Sopenharmony_ci * @in: Buffer containing input for the method call 2338c2ecf20Sopenharmony_ci * @out: Empty buffer to return the method results 2348c2ecf20Sopenharmony_ci * 2358c2ecf20Sopenharmony_ci * Call an ACPI-WMI method 2368c2ecf20Sopenharmony_ci */ 2378c2ecf20Sopenharmony_ciacpi_status wmi_evaluate_method(const char *guid_string, u8 instance, 2388c2ecf20Sopenharmony_ciu32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci struct wmi_block *wblock = NULL; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci if (!find_guid(guid_string, &wblock)) 2438c2ecf20Sopenharmony_ci return AE_ERROR; 2448c2ecf20Sopenharmony_ci return wmidev_evaluate_method(&wblock->dev, instance, method_id, 2458c2ecf20Sopenharmony_ci in, out); 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wmi_evaluate_method); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci/** 2508c2ecf20Sopenharmony_ci * wmidev_evaluate_method - Evaluate a WMI method 2518c2ecf20Sopenharmony_ci * @wdev: A wmi bus device from a driver 2528c2ecf20Sopenharmony_ci * @instance: Instance index 2538c2ecf20Sopenharmony_ci * @method_id: Method ID to call 2548c2ecf20Sopenharmony_ci * @in: Buffer containing input for the method call 2558c2ecf20Sopenharmony_ci * @out: Empty buffer to return the method results 2568c2ecf20Sopenharmony_ci * 2578c2ecf20Sopenharmony_ci * Call an ACPI-WMI method 2588c2ecf20Sopenharmony_ci */ 2598c2ecf20Sopenharmony_ciacpi_status wmidev_evaluate_method(struct wmi_device *wdev, u8 instance, 2608c2ecf20Sopenharmony_ci u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci struct guid_block *block; 2638c2ecf20Sopenharmony_ci struct wmi_block *wblock; 2648c2ecf20Sopenharmony_ci acpi_handle handle; 2658c2ecf20Sopenharmony_ci acpi_status status; 2668c2ecf20Sopenharmony_ci struct acpi_object_list input; 2678c2ecf20Sopenharmony_ci union acpi_object params[3]; 2688c2ecf20Sopenharmony_ci char method[5] = "WM"; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci wblock = container_of(wdev, struct wmi_block, dev); 2718c2ecf20Sopenharmony_ci block = &wblock->gblock; 2728c2ecf20Sopenharmony_ci handle = wblock->acpi_device->handle; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci if (!(block->flags & ACPI_WMI_METHOD)) 2758c2ecf20Sopenharmony_ci return AE_BAD_DATA; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci if (block->instance_count <= instance) 2788c2ecf20Sopenharmony_ci return AE_BAD_PARAMETER; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci input.count = 2; 2818c2ecf20Sopenharmony_ci input.pointer = params; 2828c2ecf20Sopenharmony_ci params[0].type = ACPI_TYPE_INTEGER; 2838c2ecf20Sopenharmony_ci params[0].integer.value = instance; 2848c2ecf20Sopenharmony_ci params[1].type = ACPI_TYPE_INTEGER; 2858c2ecf20Sopenharmony_ci params[1].integer.value = method_id; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci if (in) { 2888c2ecf20Sopenharmony_ci input.count = 3; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci if (block->flags & ACPI_WMI_STRING) { 2918c2ecf20Sopenharmony_ci params[2].type = ACPI_TYPE_STRING; 2928c2ecf20Sopenharmony_ci } else { 2938c2ecf20Sopenharmony_ci params[2].type = ACPI_TYPE_BUFFER; 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci params[2].buffer.length = in->length; 2968c2ecf20Sopenharmony_ci params[2].buffer.pointer = in->pointer; 2978c2ecf20Sopenharmony_ci } 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci strncat(method, block->object_id, 2); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci status = acpi_evaluate_object(handle, method, &input, out); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci return status; 3048c2ecf20Sopenharmony_ci} 3058c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wmidev_evaluate_method); 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_cistatic acpi_status __query_block(struct wmi_block *wblock, u8 instance, 3088c2ecf20Sopenharmony_ci struct acpi_buffer *out) 3098c2ecf20Sopenharmony_ci{ 3108c2ecf20Sopenharmony_ci struct guid_block *block; 3118c2ecf20Sopenharmony_ci acpi_handle handle; 3128c2ecf20Sopenharmony_ci acpi_status status, wc_status = AE_ERROR; 3138c2ecf20Sopenharmony_ci struct acpi_object_list input; 3148c2ecf20Sopenharmony_ci union acpi_object wq_params[1]; 3158c2ecf20Sopenharmony_ci char method[5]; 3168c2ecf20Sopenharmony_ci char wc_method[5] = "WC"; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci if (!out) 3198c2ecf20Sopenharmony_ci return AE_BAD_PARAMETER; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci block = &wblock->gblock; 3228c2ecf20Sopenharmony_ci handle = wblock->acpi_device->handle; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci if (block->instance_count <= instance) 3258c2ecf20Sopenharmony_ci return AE_BAD_PARAMETER; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci /* Check GUID is a data block */ 3288c2ecf20Sopenharmony_ci if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD)) 3298c2ecf20Sopenharmony_ci return AE_ERROR; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci input.count = 1; 3328c2ecf20Sopenharmony_ci input.pointer = wq_params; 3338c2ecf20Sopenharmony_ci wq_params[0].type = ACPI_TYPE_INTEGER; 3348c2ecf20Sopenharmony_ci wq_params[0].integer.value = instance; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci if (instance == 0 && wblock->read_takes_no_args) 3378c2ecf20Sopenharmony_ci input.count = 0; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci /* 3408c2ecf20Sopenharmony_ci * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method first to 3418c2ecf20Sopenharmony_ci * enable collection. 3428c2ecf20Sopenharmony_ci */ 3438c2ecf20Sopenharmony_ci if (block->flags & ACPI_WMI_EXPENSIVE) { 3448c2ecf20Sopenharmony_ci strncat(wc_method, block->object_id, 2); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci /* 3478c2ecf20Sopenharmony_ci * Some GUIDs break the specification by declaring themselves 3488c2ecf20Sopenharmony_ci * expensive, but have no corresponding WCxx method. So we 3498c2ecf20Sopenharmony_ci * should not fail if this happens. 3508c2ecf20Sopenharmony_ci */ 3518c2ecf20Sopenharmony_ci wc_status = acpi_execute_simple_method(handle, wc_method, 1); 3528c2ecf20Sopenharmony_ci } 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci strcpy(method, "WQ"); 3558c2ecf20Sopenharmony_ci strncat(method, block->object_id, 2); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci status = acpi_evaluate_object(handle, method, &input, out); 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci /* 3608c2ecf20Sopenharmony_ci * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method, even if 3618c2ecf20Sopenharmony_ci * the WQxx method failed - we should disable collection anyway. 3628c2ecf20Sopenharmony_ci */ 3638c2ecf20Sopenharmony_ci if ((block->flags & ACPI_WMI_EXPENSIVE) && ACPI_SUCCESS(wc_status)) { 3648c2ecf20Sopenharmony_ci /* 3658c2ecf20Sopenharmony_ci * Ignore whether this WCxx call succeeds or not since 3668c2ecf20Sopenharmony_ci * the previously executed WQxx method call might have 3678c2ecf20Sopenharmony_ci * succeeded, and returning the failing status code 3688c2ecf20Sopenharmony_ci * of this call would throw away the result of the WQxx 3698c2ecf20Sopenharmony_ci * call, potentially leaking memory. 3708c2ecf20Sopenharmony_ci */ 3718c2ecf20Sopenharmony_ci acpi_execute_simple_method(handle, wc_method, 0); 3728c2ecf20Sopenharmony_ci } 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci return status; 3758c2ecf20Sopenharmony_ci} 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci/** 3788c2ecf20Sopenharmony_ci * wmi_query_block - Return contents of a WMI block (deprecated) 3798c2ecf20Sopenharmony_ci * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba 3808c2ecf20Sopenharmony_ci * @instance: Instance index 3818c2ecf20Sopenharmony_ci * @out: Empty buffer to return the contents of the data block to 3828c2ecf20Sopenharmony_ci * 3838c2ecf20Sopenharmony_ci * Return the contents of an ACPI-WMI data block to a buffer 3848c2ecf20Sopenharmony_ci */ 3858c2ecf20Sopenharmony_ciacpi_status wmi_query_block(const char *guid_string, u8 instance, 3868c2ecf20Sopenharmony_ci struct acpi_buffer *out) 3878c2ecf20Sopenharmony_ci{ 3888c2ecf20Sopenharmony_ci struct wmi_block *wblock; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci if (!guid_string) 3918c2ecf20Sopenharmony_ci return AE_BAD_PARAMETER; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci if (!find_guid(guid_string, &wblock)) 3948c2ecf20Sopenharmony_ci return AE_ERROR; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci return __query_block(wblock, instance, out); 3978c2ecf20Sopenharmony_ci} 3988c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wmi_query_block); 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ciunion acpi_object *wmidev_block_query(struct wmi_device *wdev, u8 instance) 4018c2ecf20Sopenharmony_ci{ 4028c2ecf20Sopenharmony_ci struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL }; 4038c2ecf20Sopenharmony_ci struct wmi_block *wblock = container_of(wdev, struct wmi_block, dev); 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci if (ACPI_FAILURE(__query_block(wblock, instance, &out))) 4068c2ecf20Sopenharmony_ci return NULL; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci return (union acpi_object *)out.pointer; 4098c2ecf20Sopenharmony_ci} 4108c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wmidev_block_query); 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci/** 4138c2ecf20Sopenharmony_ci * wmi_set_block - Write to a WMI block 4148c2ecf20Sopenharmony_ci * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba 4158c2ecf20Sopenharmony_ci * @instance: Instance index 4168c2ecf20Sopenharmony_ci * @in: Buffer containing new values for the data block 4178c2ecf20Sopenharmony_ci * 4188c2ecf20Sopenharmony_ci * Write the contents of the input buffer to an ACPI-WMI data block 4198c2ecf20Sopenharmony_ci */ 4208c2ecf20Sopenharmony_ciacpi_status wmi_set_block(const char *guid_string, u8 instance, 4218c2ecf20Sopenharmony_ci const struct acpi_buffer *in) 4228c2ecf20Sopenharmony_ci{ 4238c2ecf20Sopenharmony_ci struct wmi_block *wblock = NULL; 4248c2ecf20Sopenharmony_ci struct guid_block *block; 4258c2ecf20Sopenharmony_ci acpi_handle handle; 4268c2ecf20Sopenharmony_ci struct acpi_object_list input; 4278c2ecf20Sopenharmony_ci union acpi_object params[2]; 4288c2ecf20Sopenharmony_ci char method[5] = "WS"; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci if (!guid_string || !in) 4318c2ecf20Sopenharmony_ci return AE_BAD_DATA; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci if (!find_guid(guid_string, &wblock)) 4348c2ecf20Sopenharmony_ci return AE_ERROR; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci block = &wblock->gblock; 4378c2ecf20Sopenharmony_ci handle = wblock->acpi_device->handle; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci if (block->instance_count <= instance) 4408c2ecf20Sopenharmony_ci return AE_BAD_PARAMETER; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci /* Check GUID is a data block */ 4438c2ecf20Sopenharmony_ci if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD)) 4448c2ecf20Sopenharmony_ci return AE_ERROR; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci input.count = 2; 4478c2ecf20Sopenharmony_ci input.pointer = params; 4488c2ecf20Sopenharmony_ci params[0].type = ACPI_TYPE_INTEGER; 4498c2ecf20Sopenharmony_ci params[0].integer.value = instance; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci if (block->flags & ACPI_WMI_STRING) { 4528c2ecf20Sopenharmony_ci params[1].type = ACPI_TYPE_STRING; 4538c2ecf20Sopenharmony_ci } else { 4548c2ecf20Sopenharmony_ci params[1].type = ACPI_TYPE_BUFFER; 4558c2ecf20Sopenharmony_ci } 4568c2ecf20Sopenharmony_ci params[1].buffer.length = in->length; 4578c2ecf20Sopenharmony_ci params[1].buffer.pointer = in->pointer; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci strncat(method, block->object_id, 2); 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci return acpi_evaluate_object(handle, method, &input, NULL); 4628c2ecf20Sopenharmony_ci} 4638c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wmi_set_block); 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_cistatic void wmi_dump_wdg(const struct guid_block *g) 4668c2ecf20Sopenharmony_ci{ 4678c2ecf20Sopenharmony_ci pr_info("%pUL:\n", &g->guid); 4688c2ecf20Sopenharmony_ci if (g->flags & ACPI_WMI_EVENT) 4698c2ecf20Sopenharmony_ci pr_info("\tnotify_id: 0x%02X\n", g->notify_id); 4708c2ecf20Sopenharmony_ci else 4718c2ecf20Sopenharmony_ci pr_info("\tobject_id: %2pE\n", g->object_id); 4728c2ecf20Sopenharmony_ci pr_info("\tinstance_count: %d\n", g->instance_count); 4738c2ecf20Sopenharmony_ci pr_info("\tflags: %#x", g->flags); 4748c2ecf20Sopenharmony_ci if (g->flags) { 4758c2ecf20Sopenharmony_ci if (g->flags & ACPI_WMI_EXPENSIVE) 4768c2ecf20Sopenharmony_ci pr_cont(" ACPI_WMI_EXPENSIVE"); 4778c2ecf20Sopenharmony_ci if (g->flags & ACPI_WMI_METHOD) 4788c2ecf20Sopenharmony_ci pr_cont(" ACPI_WMI_METHOD"); 4798c2ecf20Sopenharmony_ci if (g->flags & ACPI_WMI_STRING) 4808c2ecf20Sopenharmony_ci pr_cont(" ACPI_WMI_STRING"); 4818c2ecf20Sopenharmony_ci if (g->flags & ACPI_WMI_EVENT) 4828c2ecf20Sopenharmony_ci pr_cont(" ACPI_WMI_EVENT"); 4838c2ecf20Sopenharmony_ci } 4848c2ecf20Sopenharmony_ci pr_cont("\n"); 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci} 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_cistatic void wmi_notify_debug(u32 value, void *context) 4898c2ecf20Sopenharmony_ci{ 4908c2ecf20Sopenharmony_ci struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; 4918c2ecf20Sopenharmony_ci union acpi_object *obj; 4928c2ecf20Sopenharmony_ci acpi_status status; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci status = wmi_get_event_data(value, &response); 4958c2ecf20Sopenharmony_ci if (status != AE_OK) { 4968c2ecf20Sopenharmony_ci pr_info("bad event status 0x%x\n", status); 4978c2ecf20Sopenharmony_ci return; 4988c2ecf20Sopenharmony_ci } 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci obj = (union acpi_object *)response.pointer; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci if (!obj) 5038c2ecf20Sopenharmony_ci return; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci pr_info("DEBUG Event "); 5068c2ecf20Sopenharmony_ci switch(obj->type) { 5078c2ecf20Sopenharmony_ci case ACPI_TYPE_BUFFER: 5088c2ecf20Sopenharmony_ci pr_cont("BUFFER_TYPE - length %d\n", obj->buffer.length); 5098c2ecf20Sopenharmony_ci break; 5108c2ecf20Sopenharmony_ci case ACPI_TYPE_STRING: 5118c2ecf20Sopenharmony_ci pr_cont("STRING_TYPE - %s\n", obj->string.pointer); 5128c2ecf20Sopenharmony_ci break; 5138c2ecf20Sopenharmony_ci case ACPI_TYPE_INTEGER: 5148c2ecf20Sopenharmony_ci pr_cont("INTEGER_TYPE - %llu\n", obj->integer.value); 5158c2ecf20Sopenharmony_ci break; 5168c2ecf20Sopenharmony_ci case ACPI_TYPE_PACKAGE: 5178c2ecf20Sopenharmony_ci pr_cont("PACKAGE_TYPE - %d elements\n", obj->package.count); 5188c2ecf20Sopenharmony_ci break; 5198c2ecf20Sopenharmony_ci default: 5208c2ecf20Sopenharmony_ci pr_cont("object type 0x%X\n", obj->type); 5218c2ecf20Sopenharmony_ci } 5228c2ecf20Sopenharmony_ci kfree(obj); 5238c2ecf20Sopenharmony_ci} 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci/** 5268c2ecf20Sopenharmony_ci * wmi_install_notify_handler - Register handler for WMI events 5278c2ecf20Sopenharmony_ci * @guid: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba 5288c2ecf20Sopenharmony_ci * @handler: Function to handle notifications 5298c2ecf20Sopenharmony_ci * @data: Data to be returned to handler when event is fired 5308c2ecf20Sopenharmony_ci * 5318c2ecf20Sopenharmony_ci * Register a handler for events sent to the ACPI-WMI mapper device. 5328c2ecf20Sopenharmony_ci */ 5338c2ecf20Sopenharmony_ciacpi_status wmi_install_notify_handler(const char *guid, 5348c2ecf20Sopenharmony_ciwmi_notify_handler handler, void *data) 5358c2ecf20Sopenharmony_ci{ 5368c2ecf20Sopenharmony_ci struct wmi_block *block; 5378c2ecf20Sopenharmony_ci acpi_status status = AE_NOT_EXIST; 5388c2ecf20Sopenharmony_ci guid_t guid_input; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci if (!guid || !handler) 5418c2ecf20Sopenharmony_ci return AE_BAD_PARAMETER; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci if (guid_parse(guid, &guid_input)) 5448c2ecf20Sopenharmony_ci return AE_BAD_PARAMETER; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci list_for_each_entry(block, &wmi_block_list, list) { 5478c2ecf20Sopenharmony_ci acpi_status wmi_status; 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci if (guid_equal(&block->gblock.guid, &guid_input)) { 5508c2ecf20Sopenharmony_ci if (block->handler && 5518c2ecf20Sopenharmony_ci block->handler != wmi_notify_debug) 5528c2ecf20Sopenharmony_ci return AE_ALREADY_ACQUIRED; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci block->handler = handler; 5558c2ecf20Sopenharmony_ci block->handler_data = data; 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci wmi_status = wmi_method_enable(block, 1); 5588c2ecf20Sopenharmony_ci if ((wmi_status != AE_OK) || 5598c2ecf20Sopenharmony_ci ((wmi_status == AE_OK) && (status == AE_NOT_EXIST))) 5608c2ecf20Sopenharmony_ci status = wmi_status; 5618c2ecf20Sopenharmony_ci } 5628c2ecf20Sopenharmony_ci } 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci return status; 5658c2ecf20Sopenharmony_ci} 5668c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wmi_install_notify_handler); 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci/** 5698c2ecf20Sopenharmony_ci * wmi_uninstall_notify_handler - Unregister handler for WMI events 5708c2ecf20Sopenharmony_ci * @guid: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba 5718c2ecf20Sopenharmony_ci * 5728c2ecf20Sopenharmony_ci * Unregister handler for events sent to the ACPI-WMI mapper device. 5738c2ecf20Sopenharmony_ci */ 5748c2ecf20Sopenharmony_ciacpi_status wmi_remove_notify_handler(const char *guid) 5758c2ecf20Sopenharmony_ci{ 5768c2ecf20Sopenharmony_ci struct wmi_block *block; 5778c2ecf20Sopenharmony_ci acpi_status status = AE_NOT_EXIST; 5788c2ecf20Sopenharmony_ci guid_t guid_input; 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci if (!guid) 5818c2ecf20Sopenharmony_ci return AE_BAD_PARAMETER; 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci if (guid_parse(guid, &guid_input)) 5848c2ecf20Sopenharmony_ci return AE_BAD_PARAMETER; 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci list_for_each_entry(block, &wmi_block_list, list) { 5878c2ecf20Sopenharmony_ci acpi_status wmi_status; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci if (guid_equal(&block->gblock.guid, &guid_input)) { 5908c2ecf20Sopenharmony_ci if (!block->handler || 5918c2ecf20Sopenharmony_ci block->handler == wmi_notify_debug) 5928c2ecf20Sopenharmony_ci return AE_NULL_ENTRY; 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci if (debug_event) { 5958c2ecf20Sopenharmony_ci block->handler = wmi_notify_debug; 5968c2ecf20Sopenharmony_ci status = AE_OK; 5978c2ecf20Sopenharmony_ci } else { 5988c2ecf20Sopenharmony_ci wmi_status = wmi_method_enable(block, 0); 5998c2ecf20Sopenharmony_ci block->handler = NULL; 6008c2ecf20Sopenharmony_ci block->handler_data = NULL; 6018c2ecf20Sopenharmony_ci if ((wmi_status != AE_OK) || 6028c2ecf20Sopenharmony_ci ((wmi_status == AE_OK) && 6038c2ecf20Sopenharmony_ci (status == AE_NOT_EXIST))) 6048c2ecf20Sopenharmony_ci status = wmi_status; 6058c2ecf20Sopenharmony_ci } 6068c2ecf20Sopenharmony_ci } 6078c2ecf20Sopenharmony_ci } 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci return status; 6108c2ecf20Sopenharmony_ci} 6118c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wmi_remove_notify_handler); 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci/** 6148c2ecf20Sopenharmony_ci * wmi_get_event_data - Get WMI data associated with an event 6158c2ecf20Sopenharmony_ci * 6168c2ecf20Sopenharmony_ci * @event: Event to find 6178c2ecf20Sopenharmony_ci * @out: Buffer to hold event data. out->pointer should be freed with kfree() 6188c2ecf20Sopenharmony_ci * 6198c2ecf20Sopenharmony_ci * Returns extra data associated with an event in WMI. 6208c2ecf20Sopenharmony_ci */ 6218c2ecf20Sopenharmony_ciacpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out) 6228c2ecf20Sopenharmony_ci{ 6238c2ecf20Sopenharmony_ci struct acpi_object_list input; 6248c2ecf20Sopenharmony_ci union acpi_object params[1]; 6258c2ecf20Sopenharmony_ci struct wmi_block *wblock; 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci input.count = 1; 6288c2ecf20Sopenharmony_ci input.pointer = params; 6298c2ecf20Sopenharmony_ci params[0].type = ACPI_TYPE_INTEGER; 6308c2ecf20Sopenharmony_ci params[0].integer.value = event; 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci list_for_each_entry(wblock, &wmi_block_list, list) { 6338c2ecf20Sopenharmony_ci struct guid_block *gblock = &wblock->gblock; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci if ((gblock->flags & ACPI_WMI_EVENT) && 6368c2ecf20Sopenharmony_ci (gblock->notify_id == event)) 6378c2ecf20Sopenharmony_ci return acpi_evaluate_object(wblock->acpi_device->handle, 6388c2ecf20Sopenharmony_ci "_WED", &input, out); 6398c2ecf20Sopenharmony_ci } 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci return AE_NOT_FOUND; 6428c2ecf20Sopenharmony_ci} 6438c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wmi_get_event_data); 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci/** 6468c2ecf20Sopenharmony_ci * wmi_has_guid - Check if a GUID is available 6478c2ecf20Sopenharmony_ci * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba 6488c2ecf20Sopenharmony_ci * 6498c2ecf20Sopenharmony_ci * Check if a given GUID is defined by _WDG 6508c2ecf20Sopenharmony_ci */ 6518c2ecf20Sopenharmony_cibool wmi_has_guid(const char *guid_string) 6528c2ecf20Sopenharmony_ci{ 6538c2ecf20Sopenharmony_ci return find_guid(guid_string, NULL); 6548c2ecf20Sopenharmony_ci} 6558c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wmi_has_guid); 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci/** 6588c2ecf20Sopenharmony_ci * wmi_get_acpi_device_uid() - Get _UID name of ACPI device that defines GUID 6598c2ecf20Sopenharmony_ci * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba 6608c2ecf20Sopenharmony_ci * 6618c2ecf20Sopenharmony_ci * Find the _UID of ACPI device associated with this WMI GUID. 6628c2ecf20Sopenharmony_ci * 6638c2ecf20Sopenharmony_ci * Return: The ACPI _UID field value or NULL if the WMI GUID was not found 6648c2ecf20Sopenharmony_ci */ 6658c2ecf20Sopenharmony_cichar *wmi_get_acpi_device_uid(const char *guid_string) 6668c2ecf20Sopenharmony_ci{ 6678c2ecf20Sopenharmony_ci struct wmi_block *wblock = NULL; 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci if (!find_guid(guid_string, &wblock)) 6708c2ecf20Sopenharmony_ci return NULL; 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci return acpi_device_uid(wblock->acpi_device); 6738c2ecf20Sopenharmony_ci} 6748c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wmi_get_acpi_device_uid); 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_cistatic struct wmi_block *dev_to_wblock(struct device *dev) 6778c2ecf20Sopenharmony_ci{ 6788c2ecf20Sopenharmony_ci return container_of(dev, struct wmi_block, dev.dev); 6798c2ecf20Sopenharmony_ci} 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_cistatic struct wmi_device *dev_to_wdev(struct device *dev) 6828c2ecf20Sopenharmony_ci{ 6838c2ecf20Sopenharmony_ci return container_of(dev, struct wmi_device, dev); 6848c2ecf20Sopenharmony_ci} 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci/* 6878c2ecf20Sopenharmony_ci * sysfs interface 6888c2ecf20Sopenharmony_ci */ 6898c2ecf20Sopenharmony_cistatic ssize_t modalias_show(struct device *dev, struct device_attribute *attr, 6908c2ecf20Sopenharmony_ci char *buf) 6918c2ecf20Sopenharmony_ci{ 6928c2ecf20Sopenharmony_ci struct wmi_block *wblock = dev_to_wblock(dev); 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci return sprintf(buf, "wmi:%pUL\n", &wblock->gblock.guid); 6958c2ecf20Sopenharmony_ci} 6968c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(modalias); 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_cistatic ssize_t guid_show(struct device *dev, struct device_attribute *attr, 6998c2ecf20Sopenharmony_ci char *buf) 7008c2ecf20Sopenharmony_ci{ 7018c2ecf20Sopenharmony_ci struct wmi_block *wblock = dev_to_wblock(dev); 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci return sprintf(buf, "%pUL\n", &wblock->gblock.guid); 7048c2ecf20Sopenharmony_ci} 7058c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(guid); 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_cistatic ssize_t instance_count_show(struct device *dev, 7088c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 7098c2ecf20Sopenharmony_ci{ 7108c2ecf20Sopenharmony_ci struct wmi_block *wblock = dev_to_wblock(dev); 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", (int)wblock->gblock.instance_count); 7138c2ecf20Sopenharmony_ci} 7148c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(instance_count); 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_cistatic ssize_t expensive_show(struct device *dev, 7178c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 7188c2ecf20Sopenharmony_ci{ 7198c2ecf20Sopenharmony_ci struct wmi_block *wblock = dev_to_wblock(dev); 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", 7228c2ecf20Sopenharmony_ci (wblock->gblock.flags & ACPI_WMI_EXPENSIVE) != 0); 7238c2ecf20Sopenharmony_ci} 7248c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(expensive); 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_cistatic struct attribute *wmi_attrs[] = { 7278c2ecf20Sopenharmony_ci &dev_attr_modalias.attr, 7288c2ecf20Sopenharmony_ci &dev_attr_guid.attr, 7298c2ecf20Sopenharmony_ci &dev_attr_instance_count.attr, 7308c2ecf20Sopenharmony_ci &dev_attr_expensive.attr, 7318c2ecf20Sopenharmony_ci NULL, 7328c2ecf20Sopenharmony_ci}; 7338c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(wmi); 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_cistatic ssize_t notify_id_show(struct device *dev, struct device_attribute *attr, 7368c2ecf20Sopenharmony_ci char *buf) 7378c2ecf20Sopenharmony_ci{ 7388c2ecf20Sopenharmony_ci struct wmi_block *wblock = dev_to_wblock(dev); 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci return sprintf(buf, "%02X\n", (unsigned int)wblock->gblock.notify_id); 7418c2ecf20Sopenharmony_ci} 7428c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(notify_id); 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_cistatic struct attribute *wmi_event_attrs[] = { 7458c2ecf20Sopenharmony_ci &dev_attr_notify_id.attr, 7468c2ecf20Sopenharmony_ci NULL, 7478c2ecf20Sopenharmony_ci}; 7488c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(wmi_event); 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_cistatic ssize_t object_id_show(struct device *dev, struct device_attribute *attr, 7518c2ecf20Sopenharmony_ci char *buf) 7528c2ecf20Sopenharmony_ci{ 7538c2ecf20Sopenharmony_ci struct wmi_block *wblock = dev_to_wblock(dev); 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci return sprintf(buf, "%c%c\n", wblock->gblock.object_id[0], 7568c2ecf20Sopenharmony_ci wblock->gblock.object_id[1]); 7578c2ecf20Sopenharmony_ci} 7588c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(object_id); 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_cistatic ssize_t setable_show(struct device *dev, struct device_attribute *attr, 7618c2ecf20Sopenharmony_ci char *buf) 7628c2ecf20Sopenharmony_ci{ 7638c2ecf20Sopenharmony_ci struct wmi_device *wdev = dev_to_wdev(dev); 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", (int)wdev->setable); 7668c2ecf20Sopenharmony_ci} 7678c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(setable); 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_cistatic struct attribute *wmi_data_attrs[] = { 7708c2ecf20Sopenharmony_ci &dev_attr_object_id.attr, 7718c2ecf20Sopenharmony_ci &dev_attr_setable.attr, 7728c2ecf20Sopenharmony_ci NULL, 7738c2ecf20Sopenharmony_ci}; 7748c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(wmi_data); 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_cistatic struct attribute *wmi_method_attrs[] = { 7778c2ecf20Sopenharmony_ci &dev_attr_object_id.attr, 7788c2ecf20Sopenharmony_ci NULL, 7798c2ecf20Sopenharmony_ci}; 7808c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(wmi_method); 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_cistatic int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env) 7838c2ecf20Sopenharmony_ci{ 7848c2ecf20Sopenharmony_ci struct wmi_block *wblock = dev_to_wblock(dev); 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci if (add_uevent_var(env, "MODALIAS=wmi:%pUL", &wblock->gblock.guid)) 7878c2ecf20Sopenharmony_ci return -ENOMEM; 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci if (add_uevent_var(env, "WMI_GUID=%pUL", &wblock->gblock.guid)) 7908c2ecf20Sopenharmony_ci return -ENOMEM; 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci return 0; 7938c2ecf20Sopenharmony_ci} 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_cistatic void wmi_dev_release(struct device *dev) 7968c2ecf20Sopenharmony_ci{ 7978c2ecf20Sopenharmony_ci struct wmi_block *wblock = dev_to_wblock(dev); 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci kfree(wblock); 8008c2ecf20Sopenharmony_ci} 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_cistatic int wmi_dev_match(struct device *dev, struct device_driver *driver) 8038c2ecf20Sopenharmony_ci{ 8048c2ecf20Sopenharmony_ci struct wmi_driver *wmi_driver = 8058c2ecf20Sopenharmony_ci container_of(driver, struct wmi_driver, driver); 8068c2ecf20Sopenharmony_ci struct wmi_block *wblock = dev_to_wblock(dev); 8078c2ecf20Sopenharmony_ci const struct wmi_device_id *id = wmi_driver->id_table; 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci if (id == NULL) 8108c2ecf20Sopenharmony_ci return 0; 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci while (*id->guid_string) { 8138c2ecf20Sopenharmony_ci if (guid_parse_and_compare(id->guid_string, &wblock->gblock.guid)) 8148c2ecf20Sopenharmony_ci return 1; 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci id++; 8178c2ecf20Sopenharmony_ci } 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_ci return 0; 8208c2ecf20Sopenharmony_ci} 8218c2ecf20Sopenharmony_cistatic int wmi_char_open(struct inode *inode, struct file *filp) 8228c2ecf20Sopenharmony_ci{ 8238c2ecf20Sopenharmony_ci /* 8248c2ecf20Sopenharmony_ci * The miscdevice already stores a pointer to itself 8258c2ecf20Sopenharmony_ci * inside filp->private_data 8268c2ecf20Sopenharmony_ci */ 8278c2ecf20Sopenharmony_ci struct wmi_block *wblock = container_of(filp->private_data, struct wmi_block, char_dev); 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci filp->private_data = wblock; 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci return nonseekable_open(inode, filp); 8328c2ecf20Sopenharmony_ci} 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_cistatic ssize_t wmi_char_read(struct file *filp, char __user *buffer, 8358c2ecf20Sopenharmony_ci size_t length, loff_t *offset) 8368c2ecf20Sopenharmony_ci{ 8378c2ecf20Sopenharmony_ci struct wmi_block *wblock = filp->private_data; 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci return simple_read_from_buffer(buffer, length, offset, 8408c2ecf20Sopenharmony_ci &wblock->req_buf_size, 8418c2ecf20Sopenharmony_ci sizeof(wblock->req_buf_size)); 8428c2ecf20Sopenharmony_ci} 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_cistatic long wmi_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 8458c2ecf20Sopenharmony_ci{ 8468c2ecf20Sopenharmony_ci struct wmi_ioctl_buffer __user *input = 8478c2ecf20Sopenharmony_ci (struct wmi_ioctl_buffer __user *) arg; 8488c2ecf20Sopenharmony_ci struct wmi_block *wblock = filp->private_data; 8498c2ecf20Sopenharmony_ci struct wmi_ioctl_buffer *buf; 8508c2ecf20Sopenharmony_ci struct wmi_driver *wdriver; 8518c2ecf20Sopenharmony_ci int ret; 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci if (_IOC_TYPE(cmd) != WMI_IOC) 8548c2ecf20Sopenharmony_ci return -ENOTTY; 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci /* make sure we're not calling a higher instance than exists*/ 8578c2ecf20Sopenharmony_ci if (_IOC_NR(cmd) >= wblock->gblock.instance_count) 8588c2ecf20Sopenharmony_ci return -EINVAL; 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci mutex_lock(&wblock->char_mutex); 8618c2ecf20Sopenharmony_ci buf = wblock->handler_data; 8628c2ecf20Sopenharmony_ci if (get_user(buf->length, &input->length)) { 8638c2ecf20Sopenharmony_ci dev_dbg(&wblock->dev.dev, "Read length from user failed\n"); 8648c2ecf20Sopenharmony_ci ret = -EFAULT; 8658c2ecf20Sopenharmony_ci goto out_ioctl; 8668c2ecf20Sopenharmony_ci } 8678c2ecf20Sopenharmony_ci /* if it's too small, abort */ 8688c2ecf20Sopenharmony_ci if (buf->length < wblock->req_buf_size) { 8698c2ecf20Sopenharmony_ci dev_err(&wblock->dev.dev, 8708c2ecf20Sopenharmony_ci "Buffer %lld too small, need at least %lld\n", 8718c2ecf20Sopenharmony_ci buf->length, wblock->req_buf_size); 8728c2ecf20Sopenharmony_ci ret = -EINVAL; 8738c2ecf20Sopenharmony_ci goto out_ioctl; 8748c2ecf20Sopenharmony_ci } 8758c2ecf20Sopenharmony_ci /* if it's too big, warn, driver will only use what is needed */ 8768c2ecf20Sopenharmony_ci if (buf->length > wblock->req_buf_size) 8778c2ecf20Sopenharmony_ci dev_warn(&wblock->dev.dev, 8788c2ecf20Sopenharmony_ci "Buffer %lld is bigger than required %lld\n", 8798c2ecf20Sopenharmony_ci buf->length, wblock->req_buf_size); 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci /* copy the structure from userspace */ 8828c2ecf20Sopenharmony_ci if (copy_from_user(buf, input, wblock->req_buf_size)) { 8838c2ecf20Sopenharmony_ci dev_dbg(&wblock->dev.dev, "Copy %llu from user failed\n", 8848c2ecf20Sopenharmony_ci wblock->req_buf_size); 8858c2ecf20Sopenharmony_ci ret = -EFAULT; 8868c2ecf20Sopenharmony_ci goto out_ioctl; 8878c2ecf20Sopenharmony_ci } 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci /* let the driver do any filtering and do the call */ 8908c2ecf20Sopenharmony_ci wdriver = container_of(wblock->dev.dev.driver, 8918c2ecf20Sopenharmony_ci struct wmi_driver, driver); 8928c2ecf20Sopenharmony_ci if (!try_module_get(wdriver->driver.owner)) { 8938c2ecf20Sopenharmony_ci ret = -EBUSY; 8948c2ecf20Sopenharmony_ci goto out_ioctl; 8958c2ecf20Sopenharmony_ci } 8968c2ecf20Sopenharmony_ci ret = wdriver->filter_callback(&wblock->dev, cmd, buf); 8978c2ecf20Sopenharmony_ci module_put(wdriver->driver.owner); 8988c2ecf20Sopenharmony_ci if (ret) 8998c2ecf20Sopenharmony_ci goto out_ioctl; 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci /* return the result (only up to our internal buffer size) */ 9028c2ecf20Sopenharmony_ci if (copy_to_user(input, buf, wblock->req_buf_size)) { 9038c2ecf20Sopenharmony_ci dev_dbg(&wblock->dev.dev, "Copy %llu to user failed\n", 9048c2ecf20Sopenharmony_ci wblock->req_buf_size); 9058c2ecf20Sopenharmony_ci ret = -EFAULT; 9068c2ecf20Sopenharmony_ci } 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ciout_ioctl: 9098c2ecf20Sopenharmony_ci mutex_unlock(&wblock->char_mutex); 9108c2ecf20Sopenharmony_ci return ret; 9118c2ecf20Sopenharmony_ci} 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_cistatic const struct file_operations wmi_fops = { 9148c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 9158c2ecf20Sopenharmony_ci .read = wmi_char_read, 9168c2ecf20Sopenharmony_ci .open = wmi_char_open, 9178c2ecf20Sopenharmony_ci .unlocked_ioctl = wmi_ioctl, 9188c2ecf20Sopenharmony_ci .compat_ioctl = compat_ptr_ioctl, 9198c2ecf20Sopenharmony_ci}; 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_cistatic int wmi_dev_probe(struct device *dev) 9228c2ecf20Sopenharmony_ci{ 9238c2ecf20Sopenharmony_ci struct wmi_block *wblock = dev_to_wblock(dev); 9248c2ecf20Sopenharmony_ci struct wmi_driver *wdriver = 9258c2ecf20Sopenharmony_ci container_of(dev->driver, struct wmi_driver, driver); 9268c2ecf20Sopenharmony_ci int ret = 0; 9278c2ecf20Sopenharmony_ci char *buf; 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci if (ACPI_FAILURE(wmi_method_enable(wblock, 1))) 9308c2ecf20Sopenharmony_ci dev_warn(dev, "failed to enable device -- probing anyway\n"); 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci if (wdriver->probe) { 9338c2ecf20Sopenharmony_ci ret = wdriver->probe(dev_to_wdev(dev), 9348c2ecf20Sopenharmony_ci find_guid_context(wblock, wdriver)); 9358c2ecf20Sopenharmony_ci if (ret != 0) 9368c2ecf20Sopenharmony_ci goto probe_failure; 9378c2ecf20Sopenharmony_ci } 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci /* driver wants a character device made */ 9408c2ecf20Sopenharmony_ci if (wdriver->filter_callback) { 9418c2ecf20Sopenharmony_ci /* check that required buffer size declared by driver or MOF */ 9428c2ecf20Sopenharmony_ci if (!wblock->req_buf_size) { 9438c2ecf20Sopenharmony_ci dev_err(&wblock->dev.dev, 9448c2ecf20Sopenharmony_ci "Required buffer size not set\n"); 9458c2ecf20Sopenharmony_ci ret = -EINVAL; 9468c2ecf20Sopenharmony_ci goto probe_failure; 9478c2ecf20Sopenharmony_ci } 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci wblock->handler_data = kmalloc(wblock->req_buf_size, 9508c2ecf20Sopenharmony_ci GFP_KERNEL); 9518c2ecf20Sopenharmony_ci if (!wblock->handler_data) { 9528c2ecf20Sopenharmony_ci ret = -ENOMEM; 9538c2ecf20Sopenharmony_ci goto probe_failure; 9548c2ecf20Sopenharmony_ci } 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci buf = kasprintf(GFP_KERNEL, "wmi/%s", wdriver->driver.name); 9578c2ecf20Sopenharmony_ci if (!buf) { 9588c2ecf20Sopenharmony_ci ret = -ENOMEM; 9598c2ecf20Sopenharmony_ci goto probe_string_failure; 9608c2ecf20Sopenharmony_ci } 9618c2ecf20Sopenharmony_ci wblock->char_dev.minor = MISC_DYNAMIC_MINOR; 9628c2ecf20Sopenharmony_ci wblock->char_dev.name = buf; 9638c2ecf20Sopenharmony_ci wblock->char_dev.fops = &wmi_fops; 9648c2ecf20Sopenharmony_ci wblock->char_dev.mode = 0444; 9658c2ecf20Sopenharmony_ci ret = misc_register(&wblock->char_dev); 9668c2ecf20Sopenharmony_ci if (ret) { 9678c2ecf20Sopenharmony_ci dev_warn(dev, "failed to register char dev: %d\n", ret); 9688c2ecf20Sopenharmony_ci ret = -ENOMEM; 9698c2ecf20Sopenharmony_ci goto probe_misc_failure; 9708c2ecf20Sopenharmony_ci } 9718c2ecf20Sopenharmony_ci } 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci return 0; 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ciprobe_misc_failure: 9768c2ecf20Sopenharmony_ci kfree(buf); 9778c2ecf20Sopenharmony_ciprobe_string_failure: 9788c2ecf20Sopenharmony_ci kfree(wblock->handler_data); 9798c2ecf20Sopenharmony_ciprobe_failure: 9808c2ecf20Sopenharmony_ci if (ACPI_FAILURE(wmi_method_enable(wblock, 0))) 9818c2ecf20Sopenharmony_ci dev_warn(dev, "failed to disable device\n"); 9828c2ecf20Sopenharmony_ci return ret; 9838c2ecf20Sopenharmony_ci} 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_cistatic int wmi_dev_remove(struct device *dev) 9868c2ecf20Sopenharmony_ci{ 9878c2ecf20Sopenharmony_ci struct wmi_block *wblock = dev_to_wblock(dev); 9888c2ecf20Sopenharmony_ci struct wmi_driver *wdriver = 9898c2ecf20Sopenharmony_ci container_of(dev->driver, struct wmi_driver, driver); 9908c2ecf20Sopenharmony_ci int ret = 0; 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci if (wdriver->filter_callback) { 9938c2ecf20Sopenharmony_ci misc_deregister(&wblock->char_dev); 9948c2ecf20Sopenharmony_ci kfree(wblock->char_dev.name); 9958c2ecf20Sopenharmony_ci kfree(wblock->handler_data); 9968c2ecf20Sopenharmony_ci } 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci if (wdriver->remove) 9998c2ecf20Sopenharmony_ci ret = wdriver->remove(dev_to_wdev(dev)); 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci if (ACPI_FAILURE(wmi_method_enable(wblock, 0))) 10028c2ecf20Sopenharmony_ci dev_warn(dev, "failed to disable device\n"); 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci return ret; 10058c2ecf20Sopenharmony_ci} 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_cistatic struct class wmi_bus_class = { 10088c2ecf20Sopenharmony_ci .name = "wmi_bus", 10098c2ecf20Sopenharmony_ci}; 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_cistatic struct bus_type wmi_bus_type = { 10128c2ecf20Sopenharmony_ci .name = "wmi", 10138c2ecf20Sopenharmony_ci .dev_groups = wmi_groups, 10148c2ecf20Sopenharmony_ci .match = wmi_dev_match, 10158c2ecf20Sopenharmony_ci .uevent = wmi_dev_uevent, 10168c2ecf20Sopenharmony_ci .probe = wmi_dev_probe, 10178c2ecf20Sopenharmony_ci .remove = wmi_dev_remove, 10188c2ecf20Sopenharmony_ci}; 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_cistatic const struct device_type wmi_type_event = { 10218c2ecf20Sopenharmony_ci .name = "event", 10228c2ecf20Sopenharmony_ci .groups = wmi_event_groups, 10238c2ecf20Sopenharmony_ci .release = wmi_dev_release, 10248c2ecf20Sopenharmony_ci}; 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_cistatic const struct device_type wmi_type_method = { 10278c2ecf20Sopenharmony_ci .name = "method", 10288c2ecf20Sopenharmony_ci .groups = wmi_method_groups, 10298c2ecf20Sopenharmony_ci .release = wmi_dev_release, 10308c2ecf20Sopenharmony_ci}; 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_cistatic const struct device_type wmi_type_data = { 10338c2ecf20Sopenharmony_ci .name = "data", 10348c2ecf20Sopenharmony_ci .groups = wmi_data_groups, 10358c2ecf20Sopenharmony_ci .release = wmi_dev_release, 10368c2ecf20Sopenharmony_ci}; 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_cistatic int wmi_create_device(struct device *wmi_bus_dev, 10398c2ecf20Sopenharmony_ci struct wmi_block *wblock, 10408c2ecf20Sopenharmony_ci struct acpi_device *device) 10418c2ecf20Sopenharmony_ci{ 10428c2ecf20Sopenharmony_ci struct acpi_device_info *info; 10438c2ecf20Sopenharmony_ci char method[5]; 10448c2ecf20Sopenharmony_ci int result; 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci if (wblock->gblock.flags & ACPI_WMI_EVENT) { 10478c2ecf20Sopenharmony_ci wblock->dev.dev.type = &wmi_type_event; 10488c2ecf20Sopenharmony_ci goto out_init; 10498c2ecf20Sopenharmony_ci } 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci if (wblock->gblock.flags & ACPI_WMI_METHOD) { 10528c2ecf20Sopenharmony_ci wblock->dev.dev.type = &wmi_type_method; 10538c2ecf20Sopenharmony_ci mutex_init(&wblock->char_mutex); 10548c2ecf20Sopenharmony_ci goto out_init; 10558c2ecf20Sopenharmony_ci } 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci /* 10588c2ecf20Sopenharmony_ci * Data Block Query Control Method (WQxx by convention) is 10598c2ecf20Sopenharmony_ci * required per the WMI documentation. If it is not present, 10608c2ecf20Sopenharmony_ci * we ignore this data block. 10618c2ecf20Sopenharmony_ci */ 10628c2ecf20Sopenharmony_ci strcpy(method, "WQ"); 10638c2ecf20Sopenharmony_ci strncat(method, wblock->gblock.object_id, 2); 10648c2ecf20Sopenharmony_ci result = get_subobj_info(device->handle, method, &info); 10658c2ecf20Sopenharmony_ci 10668c2ecf20Sopenharmony_ci if (result) { 10678c2ecf20Sopenharmony_ci dev_warn(wmi_bus_dev, 10688c2ecf20Sopenharmony_ci "%s data block query control method not found\n", 10698c2ecf20Sopenharmony_ci method); 10708c2ecf20Sopenharmony_ci return result; 10718c2ecf20Sopenharmony_ci } 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci wblock->dev.dev.type = &wmi_type_data; 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci /* 10768c2ecf20Sopenharmony_ci * The Microsoft documentation specifically states: 10778c2ecf20Sopenharmony_ci * 10788c2ecf20Sopenharmony_ci * Data blocks registered with only a single instance 10798c2ecf20Sopenharmony_ci * can ignore the parameter. 10808c2ecf20Sopenharmony_ci * 10818c2ecf20Sopenharmony_ci * ACPICA will get mad at us if we call the method with the wrong number 10828c2ecf20Sopenharmony_ci * of arguments, so check what our method expects. (On some Dell 10838c2ecf20Sopenharmony_ci * laptops, WQxx may not be a method at all.) 10848c2ecf20Sopenharmony_ci */ 10858c2ecf20Sopenharmony_ci if (info->type != ACPI_TYPE_METHOD || info->param_count == 0) 10868c2ecf20Sopenharmony_ci wblock->read_takes_no_args = true; 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci kfree(info); 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_ci strcpy(method, "WS"); 10918c2ecf20Sopenharmony_ci strncat(method, wblock->gblock.object_id, 2); 10928c2ecf20Sopenharmony_ci result = get_subobj_info(device->handle, method, NULL); 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci if (result == 0) 10958c2ecf20Sopenharmony_ci wblock->dev.setable = true; 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci out_init: 10988c2ecf20Sopenharmony_ci wblock->dev.dev.bus = &wmi_bus_type; 10998c2ecf20Sopenharmony_ci wblock->dev.dev.parent = wmi_bus_dev; 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci dev_set_name(&wblock->dev.dev, "%pUL", &wblock->gblock.guid); 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci device_initialize(&wblock->dev.dev); 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci return 0; 11068c2ecf20Sopenharmony_ci} 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_cistatic void wmi_free_devices(struct acpi_device *device) 11098c2ecf20Sopenharmony_ci{ 11108c2ecf20Sopenharmony_ci struct wmi_block *wblock, *next; 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci /* Delete devices for all the GUIDs */ 11138c2ecf20Sopenharmony_ci list_for_each_entry_safe(wblock, next, &wmi_block_list, list) { 11148c2ecf20Sopenharmony_ci if (wblock->acpi_device == device) { 11158c2ecf20Sopenharmony_ci list_del(&wblock->list); 11168c2ecf20Sopenharmony_ci device_unregister(&wblock->dev.dev); 11178c2ecf20Sopenharmony_ci } 11188c2ecf20Sopenharmony_ci } 11198c2ecf20Sopenharmony_ci} 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_cistatic bool guid_already_parsed(struct acpi_device *device, const guid_t *guid) 11228c2ecf20Sopenharmony_ci{ 11238c2ecf20Sopenharmony_ci struct wmi_block *wblock; 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci list_for_each_entry(wblock, &wmi_block_list, list) { 11268c2ecf20Sopenharmony_ci if (guid_equal(&wblock->gblock.guid, guid)) { 11278c2ecf20Sopenharmony_ci /* 11288c2ecf20Sopenharmony_ci * Because we historically didn't track the relationship 11298c2ecf20Sopenharmony_ci * between GUIDs and ACPI nodes, we don't know whether 11308c2ecf20Sopenharmony_ci * we need to suppress GUIDs that are unique on a 11318c2ecf20Sopenharmony_ci * given node but duplicated across nodes. 11328c2ecf20Sopenharmony_ci */ 11338c2ecf20Sopenharmony_ci dev_warn(&device->dev, "duplicate WMI GUID %pUL (first instance was on %s)\n", 11348c2ecf20Sopenharmony_ci guid, dev_name(&wblock->acpi_device->dev)); 11358c2ecf20Sopenharmony_ci return true; 11368c2ecf20Sopenharmony_ci } 11378c2ecf20Sopenharmony_ci } 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_ci return false; 11408c2ecf20Sopenharmony_ci} 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci/* 11438c2ecf20Sopenharmony_ci * Parse the _WDG method for the GUID data blocks 11448c2ecf20Sopenharmony_ci */ 11458c2ecf20Sopenharmony_cistatic int parse_wdg(struct device *wmi_bus_dev, struct acpi_device *device) 11468c2ecf20Sopenharmony_ci{ 11478c2ecf20Sopenharmony_ci struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL}; 11488c2ecf20Sopenharmony_ci const struct guid_block *gblock; 11498c2ecf20Sopenharmony_ci struct wmi_block *wblock, *next; 11508c2ecf20Sopenharmony_ci union acpi_object *obj; 11518c2ecf20Sopenharmony_ci acpi_status status; 11528c2ecf20Sopenharmony_ci u32 i, total; 11538c2ecf20Sopenharmony_ci int retval; 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci status = acpi_evaluate_object(device->handle, "_WDG", NULL, &out); 11568c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) 11578c2ecf20Sopenharmony_ci return -ENXIO; 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ci obj = (union acpi_object *) out.pointer; 11608c2ecf20Sopenharmony_ci if (!obj) 11618c2ecf20Sopenharmony_ci return -ENXIO; 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_ci if (obj->type != ACPI_TYPE_BUFFER) { 11648c2ecf20Sopenharmony_ci kfree(obj); 11658c2ecf20Sopenharmony_ci return -ENXIO; 11668c2ecf20Sopenharmony_ci } 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_ci gblock = (const struct guid_block *)obj->buffer.pointer; 11698c2ecf20Sopenharmony_ci total = obj->buffer.length / sizeof(struct guid_block); 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci for (i = 0; i < total; i++) { 11728c2ecf20Sopenharmony_ci if (debug_dump_wdg) 11738c2ecf20Sopenharmony_ci wmi_dump_wdg(&gblock[i]); 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_ci /* 11768c2ecf20Sopenharmony_ci * Some WMI devices, like those for nVidia hooks, have a 11778c2ecf20Sopenharmony_ci * duplicate GUID. It's not clear what we should do in this 11788c2ecf20Sopenharmony_ci * case yet, so for now, we'll just ignore the duplicate 11798c2ecf20Sopenharmony_ci * for device creation. 11808c2ecf20Sopenharmony_ci */ 11818c2ecf20Sopenharmony_ci if (guid_already_parsed(device, &gblock[i].guid)) 11828c2ecf20Sopenharmony_ci continue; 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL); 11858c2ecf20Sopenharmony_ci if (!wblock) { 11868c2ecf20Sopenharmony_ci dev_err(wmi_bus_dev, "Failed to allocate %pUL\n", &gblock[i].guid); 11878c2ecf20Sopenharmony_ci continue; 11888c2ecf20Sopenharmony_ci } 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci wblock->acpi_device = device; 11918c2ecf20Sopenharmony_ci wblock->gblock = gblock[i]; 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci retval = wmi_create_device(wmi_bus_dev, wblock, device); 11948c2ecf20Sopenharmony_ci if (retval) { 11958c2ecf20Sopenharmony_ci kfree(wblock); 11968c2ecf20Sopenharmony_ci continue; 11978c2ecf20Sopenharmony_ci } 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_ci list_add_tail(&wblock->list, &wmi_block_list); 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_ci if (debug_event) { 12028c2ecf20Sopenharmony_ci wblock->handler = wmi_notify_debug; 12038c2ecf20Sopenharmony_ci wmi_method_enable(wblock, 1); 12048c2ecf20Sopenharmony_ci } 12058c2ecf20Sopenharmony_ci } 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_ci /* 12088c2ecf20Sopenharmony_ci * Now that all of the devices are created, add them to the 12098c2ecf20Sopenharmony_ci * device tree and probe subdrivers. 12108c2ecf20Sopenharmony_ci */ 12118c2ecf20Sopenharmony_ci list_for_each_entry_safe(wblock, next, &wmi_block_list, list) { 12128c2ecf20Sopenharmony_ci if (wblock->acpi_device != device) 12138c2ecf20Sopenharmony_ci continue; 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_ci retval = device_add(&wblock->dev.dev); 12168c2ecf20Sopenharmony_ci if (retval) { 12178c2ecf20Sopenharmony_ci dev_err(wmi_bus_dev, "failed to register %pUL\n", 12188c2ecf20Sopenharmony_ci &wblock->gblock.guid); 12198c2ecf20Sopenharmony_ci if (debug_event) 12208c2ecf20Sopenharmony_ci wmi_method_enable(wblock, 0); 12218c2ecf20Sopenharmony_ci list_del(&wblock->list); 12228c2ecf20Sopenharmony_ci put_device(&wblock->dev.dev); 12238c2ecf20Sopenharmony_ci } 12248c2ecf20Sopenharmony_ci } 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ci kfree(obj); 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci return 0; 12298c2ecf20Sopenharmony_ci} 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_ci/* 12328c2ecf20Sopenharmony_ci * WMI can have EmbeddedControl access regions. In which case, we just want to 12338c2ecf20Sopenharmony_ci * hand these off to the EC driver. 12348c2ecf20Sopenharmony_ci */ 12358c2ecf20Sopenharmony_cistatic acpi_status 12368c2ecf20Sopenharmony_ciacpi_wmi_ec_space_handler(u32 function, acpi_physical_address address, 12378c2ecf20Sopenharmony_ci u32 bits, u64 *value, 12388c2ecf20Sopenharmony_ci void *handler_context, void *region_context) 12398c2ecf20Sopenharmony_ci{ 12408c2ecf20Sopenharmony_ci int result = 0, i = 0; 12418c2ecf20Sopenharmony_ci u8 temp = 0; 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_ci if ((address > 0xFF) || !value) 12448c2ecf20Sopenharmony_ci return AE_BAD_PARAMETER; 12458c2ecf20Sopenharmony_ci 12468c2ecf20Sopenharmony_ci if (function != ACPI_READ && function != ACPI_WRITE) 12478c2ecf20Sopenharmony_ci return AE_BAD_PARAMETER; 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_ci if (bits != 8) 12508c2ecf20Sopenharmony_ci return AE_BAD_PARAMETER; 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ci if (function == ACPI_READ) { 12538c2ecf20Sopenharmony_ci result = ec_read(address, &temp); 12548c2ecf20Sopenharmony_ci (*value) |= ((u64)temp) << i; 12558c2ecf20Sopenharmony_ci } else { 12568c2ecf20Sopenharmony_ci temp = 0xff & ((*value) >> i); 12578c2ecf20Sopenharmony_ci result = ec_write(address, temp); 12588c2ecf20Sopenharmony_ci } 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_ci switch (result) { 12618c2ecf20Sopenharmony_ci case -EINVAL: 12628c2ecf20Sopenharmony_ci return AE_BAD_PARAMETER; 12638c2ecf20Sopenharmony_ci break; 12648c2ecf20Sopenharmony_ci case -ENODEV: 12658c2ecf20Sopenharmony_ci return AE_NOT_FOUND; 12668c2ecf20Sopenharmony_ci break; 12678c2ecf20Sopenharmony_ci case -ETIME: 12688c2ecf20Sopenharmony_ci return AE_TIME; 12698c2ecf20Sopenharmony_ci break; 12708c2ecf20Sopenharmony_ci default: 12718c2ecf20Sopenharmony_ci return AE_OK; 12728c2ecf20Sopenharmony_ci } 12738c2ecf20Sopenharmony_ci} 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_cistatic void acpi_wmi_notify_handler(acpi_handle handle, u32 event, 12768c2ecf20Sopenharmony_ci void *context) 12778c2ecf20Sopenharmony_ci{ 12788c2ecf20Sopenharmony_ci struct wmi_block *wblock; 12798c2ecf20Sopenharmony_ci bool found_it = false; 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_ci list_for_each_entry(wblock, &wmi_block_list, list) { 12828c2ecf20Sopenharmony_ci struct guid_block *block = &wblock->gblock; 12838c2ecf20Sopenharmony_ci 12848c2ecf20Sopenharmony_ci if (wblock->acpi_device->handle == handle && 12858c2ecf20Sopenharmony_ci (block->flags & ACPI_WMI_EVENT) && 12868c2ecf20Sopenharmony_ci (block->notify_id == event)) 12878c2ecf20Sopenharmony_ci { 12888c2ecf20Sopenharmony_ci found_it = true; 12898c2ecf20Sopenharmony_ci break; 12908c2ecf20Sopenharmony_ci } 12918c2ecf20Sopenharmony_ci } 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci if (!found_it) 12948c2ecf20Sopenharmony_ci return; 12958c2ecf20Sopenharmony_ci 12968c2ecf20Sopenharmony_ci /* If a driver is bound, then notify the driver. */ 12978c2ecf20Sopenharmony_ci if (wblock->dev.dev.driver) { 12988c2ecf20Sopenharmony_ci struct wmi_driver *driver; 12998c2ecf20Sopenharmony_ci struct acpi_object_list input; 13008c2ecf20Sopenharmony_ci union acpi_object params[1]; 13018c2ecf20Sopenharmony_ci struct acpi_buffer evdata = { ACPI_ALLOCATE_BUFFER, NULL }; 13028c2ecf20Sopenharmony_ci acpi_status status; 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_ci driver = container_of(wblock->dev.dev.driver, 13058c2ecf20Sopenharmony_ci struct wmi_driver, driver); 13068c2ecf20Sopenharmony_ci 13078c2ecf20Sopenharmony_ci input.count = 1; 13088c2ecf20Sopenharmony_ci input.pointer = params; 13098c2ecf20Sopenharmony_ci params[0].type = ACPI_TYPE_INTEGER; 13108c2ecf20Sopenharmony_ci params[0].integer.value = event; 13118c2ecf20Sopenharmony_ci 13128c2ecf20Sopenharmony_ci status = acpi_evaluate_object(wblock->acpi_device->handle, 13138c2ecf20Sopenharmony_ci "_WED", &input, &evdata); 13148c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) { 13158c2ecf20Sopenharmony_ci dev_warn(&wblock->dev.dev, 13168c2ecf20Sopenharmony_ci "failed to get event data\n"); 13178c2ecf20Sopenharmony_ci return; 13188c2ecf20Sopenharmony_ci } 13198c2ecf20Sopenharmony_ci 13208c2ecf20Sopenharmony_ci if (driver->notify) 13218c2ecf20Sopenharmony_ci driver->notify(&wblock->dev, 13228c2ecf20Sopenharmony_ci (union acpi_object *)evdata.pointer); 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_ci kfree(evdata.pointer); 13258c2ecf20Sopenharmony_ci } else if (wblock->handler) { 13268c2ecf20Sopenharmony_ci /* Legacy handler */ 13278c2ecf20Sopenharmony_ci wblock->handler(event, wblock->handler_data); 13288c2ecf20Sopenharmony_ci } 13298c2ecf20Sopenharmony_ci 13308c2ecf20Sopenharmony_ci if (debug_event) 13318c2ecf20Sopenharmony_ci pr_info("DEBUG Event GUID: %pUL\n", &wblock->gblock.guid); 13328c2ecf20Sopenharmony_ci 13338c2ecf20Sopenharmony_ci acpi_bus_generate_netlink_event( 13348c2ecf20Sopenharmony_ci wblock->acpi_device->pnp.device_class, 13358c2ecf20Sopenharmony_ci dev_name(&wblock->dev.dev), 13368c2ecf20Sopenharmony_ci event, 0); 13378c2ecf20Sopenharmony_ci 13388c2ecf20Sopenharmony_ci} 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_cistatic int acpi_wmi_remove(struct platform_device *device) 13418c2ecf20Sopenharmony_ci{ 13428c2ecf20Sopenharmony_ci struct acpi_device *acpi_device = ACPI_COMPANION(&device->dev); 13438c2ecf20Sopenharmony_ci 13448c2ecf20Sopenharmony_ci acpi_remove_notify_handler(acpi_device->handle, ACPI_DEVICE_NOTIFY, 13458c2ecf20Sopenharmony_ci acpi_wmi_notify_handler); 13468c2ecf20Sopenharmony_ci acpi_remove_address_space_handler(acpi_device->handle, 13478c2ecf20Sopenharmony_ci ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler); 13488c2ecf20Sopenharmony_ci wmi_free_devices(acpi_device); 13498c2ecf20Sopenharmony_ci device_destroy(&wmi_bus_class, MKDEV(0, 0)); 13508c2ecf20Sopenharmony_ci 13518c2ecf20Sopenharmony_ci return 0; 13528c2ecf20Sopenharmony_ci} 13538c2ecf20Sopenharmony_ci 13548c2ecf20Sopenharmony_cistatic int acpi_wmi_probe(struct platform_device *device) 13558c2ecf20Sopenharmony_ci{ 13568c2ecf20Sopenharmony_ci struct acpi_device *acpi_device; 13578c2ecf20Sopenharmony_ci struct device *wmi_bus_dev; 13588c2ecf20Sopenharmony_ci acpi_status status; 13598c2ecf20Sopenharmony_ci int error; 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_ci acpi_device = ACPI_COMPANION(&device->dev); 13628c2ecf20Sopenharmony_ci if (!acpi_device) { 13638c2ecf20Sopenharmony_ci dev_err(&device->dev, "ACPI companion is missing\n"); 13648c2ecf20Sopenharmony_ci return -ENODEV; 13658c2ecf20Sopenharmony_ci } 13668c2ecf20Sopenharmony_ci 13678c2ecf20Sopenharmony_ci status = acpi_install_address_space_handler(acpi_device->handle, 13688c2ecf20Sopenharmony_ci ACPI_ADR_SPACE_EC, 13698c2ecf20Sopenharmony_ci &acpi_wmi_ec_space_handler, 13708c2ecf20Sopenharmony_ci NULL, NULL); 13718c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) { 13728c2ecf20Sopenharmony_ci dev_err(&device->dev, "Error installing EC region handler\n"); 13738c2ecf20Sopenharmony_ci return -ENODEV; 13748c2ecf20Sopenharmony_ci } 13758c2ecf20Sopenharmony_ci 13768c2ecf20Sopenharmony_ci status = acpi_install_notify_handler(acpi_device->handle, 13778c2ecf20Sopenharmony_ci ACPI_DEVICE_NOTIFY, 13788c2ecf20Sopenharmony_ci acpi_wmi_notify_handler, 13798c2ecf20Sopenharmony_ci NULL); 13808c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) { 13818c2ecf20Sopenharmony_ci dev_err(&device->dev, "Error installing notify handler\n"); 13828c2ecf20Sopenharmony_ci error = -ENODEV; 13838c2ecf20Sopenharmony_ci goto err_remove_ec_handler; 13848c2ecf20Sopenharmony_ci } 13858c2ecf20Sopenharmony_ci 13868c2ecf20Sopenharmony_ci wmi_bus_dev = device_create(&wmi_bus_class, &device->dev, MKDEV(0, 0), 13878c2ecf20Sopenharmony_ci NULL, "wmi_bus-%s", dev_name(&device->dev)); 13888c2ecf20Sopenharmony_ci if (IS_ERR(wmi_bus_dev)) { 13898c2ecf20Sopenharmony_ci error = PTR_ERR(wmi_bus_dev); 13908c2ecf20Sopenharmony_ci goto err_remove_notify_handler; 13918c2ecf20Sopenharmony_ci } 13928c2ecf20Sopenharmony_ci dev_set_drvdata(&device->dev, wmi_bus_dev); 13938c2ecf20Sopenharmony_ci 13948c2ecf20Sopenharmony_ci error = parse_wdg(wmi_bus_dev, acpi_device); 13958c2ecf20Sopenharmony_ci if (error) { 13968c2ecf20Sopenharmony_ci pr_err("Failed to parse WDG method\n"); 13978c2ecf20Sopenharmony_ci goto err_remove_busdev; 13988c2ecf20Sopenharmony_ci } 13998c2ecf20Sopenharmony_ci 14008c2ecf20Sopenharmony_ci return 0; 14018c2ecf20Sopenharmony_ci 14028c2ecf20Sopenharmony_cierr_remove_busdev: 14038c2ecf20Sopenharmony_ci device_destroy(&wmi_bus_class, MKDEV(0, 0)); 14048c2ecf20Sopenharmony_ci 14058c2ecf20Sopenharmony_cierr_remove_notify_handler: 14068c2ecf20Sopenharmony_ci acpi_remove_notify_handler(acpi_device->handle, ACPI_DEVICE_NOTIFY, 14078c2ecf20Sopenharmony_ci acpi_wmi_notify_handler); 14088c2ecf20Sopenharmony_ci 14098c2ecf20Sopenharmony_cierr_remove_ec_handler: 14108c2ecf20Sopenharmony_ci acpi_remove_address_space_handler(acpi_device->handle, 14118c2ecf20Sopenharmony_ci ACPI_ADR_SPACE_EC, 14128c2ecf20Sopenharmony_ci &acpi_wmi_ec_space_handler); 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_ci return error; 14158c2ecf20Sopenharmony_ci} 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_ciint __must_check __wmi_driver_register(struct wmi_driver *driver, 14188c2ecf20Sopenharmony_ci struct module *owner) 14198c2ecf20Sopenharmony_ci{ 14208c2ecf20Sopenharmony_ci driver->driver.owner = owner; 14218c2ecf20Sopenharmony_ci driver->driver.bus = &wmi_bus_type; 14228c2ecf20Sopenharmony_ci 14238c2ecf20Sopenharmony_ci return driver_register(&driver->driver); 14248c2ecf20Sopenharmony_ci} 14258c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__wmi_driver_register); 14268c2ecf20Sopenharmony_ci 14278c2ecf20Sopenharmony_civoid wmi_driver_unregister(struct wmi_driver *driver) 14288c2ecf20Sopenharmony_ci{ 14298c2ecf20Sopenharmony_ci driver_unregister(&driver->driver); 14308c2ecf20Sopenharmony_ci} 14318c2ecf20Sopenharmony_ciEXPORT_SYMBOL(wmi_driver_unregister); 14328c2ecf20Sopenharmony_ci 14338c2ecf20Sopenharmony_cistatic int __init acpi_wmi_init(void) 14348c2ecf20Sopenharmony_ci{ 14358c2ecf20Sopenharmony_ci int error; 14368c2ecf20Sopenharmony_ci 14378c2ecf20Sopenharmony_ci if (acpi_disabled) 14388c2ecf20Sopenharmony_ci return -ENODEV; 14398c2ecf20Sopenharmony_ci 14408c2ecf20Sopenharmony_ci error = class_register(&wmi_bus_class); 14418c2ecf20Sopenharmony_ci if (error) 14428c2ecf20Sopenharmony_ci return error; 14438c2ecf20Sopenharmony_ci 14448c2ecf20Sopenharmony_ci error = bus_register(&wmi_bus_type); 14458c2ecf20Sopenharmony_ci if (error) 14468c2ecf20Sopenharmony_ci goto err_unreg_class; 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_ci error = platform_driver_register(&acpi_wmi_driver); 14498c2ecf20Sopenharmony_ci if (error) { 14508c2ecf20Sopenharmony_ci pr_err("Error loading mapper\n"); 14518c2ecf20Sopenharmony_ci goto err_unreg_bus; 14528c2ecf20Sopenharmony_ci } 14538c2ecf20Sopenharmony_ci 14548c2ecf20Sopenharmony_ci return 0; 14558c2ecf20Sopenharmony_ci 14568c2ecf20Sopenharmony_cierr_unreg_bus: 14578c2ecf20Sopenharmony_ci bus_unregister(&wmi_bus_type); 14588c2ecf20Sopenharmony_ci 14598c2ecf20Sopenharmony_cierr_unreg_class: 14608c2ecf20Sopenharmony_ci class_unregister(&wmi_bus_class); 14618c2ecf20Sopenharmony_ci 14628c2ecf20Sopenharmony_ci return error; 14638c2ecf20Sopenharmony_ci} 14648c2ecf20Sopenharmony_ci 14658c2ecf20Sopenharmony_cistatic void __exit acpi_wmi_exit(void) 14668c2ecf20Sopenharmony_ci{ 14678c2ecf20Sopenharmony_ci platform_driver_unregister(&acpi_wmi_driver); 14688c2ecf20Sopenharmony_ci bus_unregister(&wmi_bus_type); 14698c2ecf20Sopenharmony_ci class_unregister(&wmi_bus_class); 14708c2ecf20Sopenharmony_ci} 14718c2ecf20Sopenharmony_ci 14728c2ecf20Sopenharmony_cisubsys_initcall_sync(acpi_wmi_init); 14738c2ecf20Sopenharmony_cimodule_exit(acpi_wmi_exit); 1474