162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * ACPI-WMI mapping driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2007-2008 Carlos Corbacho <carlos@strangeworlds.co.uk> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * GUID parsing code from ldm.c is: 862306a36Sopenharmony_ci * Copyright (C) 2001,2002 Richard Russon <ldm@flatcap.org> 962306a36Sopenharmony_ci * Copyright (c) 2001-2007 Anton Altaparmakov 1062306a36Sopenharmony_ci * Copyright (C) 2001,2002 Jakob Kemi <jakob.kemi@telia.com> 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * WMI bus infrastructure by Andrew Lutomirski and Darren Hart: 1362306a36Sopenharmony_ci * Copyright (C) 2015 Andrew Lutomirski 1462306a36Sopenharmony_ci * Copyright (C) 2017 VMware, Inc. All Rights Reserved. 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <linux/acpi.h> 2062306a36Sopenharmony_ci#include <linux/bits.h> 2162306a36Sopenharmony_ci#include <linux/build_bug.h> 2262306a36Sopenharmony_ci#include <linux/device.h> 2362306a36Sopenharmony_ci#include <linux/init.h> 2462306a36Sopenharmony_ci#include <linux/kernel.h> 2562306a36Sopenharmony_ci#include <linux/list.h> 2662306a36Sopenharmony_ci#include <linux/miscdevice.h> 2762306a36Sopenharmony_ci#include <linux/module.h> 2862306a36Sopenharmony_ci#include <linux/platform_device.h> 2962306a36Sopenharmony_ci#include <linux/slab.h> 3062306a36Sopenharmony_ci#include <linux/sysfs.h> 3162306a36Sopenharmony_ci#include <linux/types.h> 3262306a36Sopenharmony_ci#include <linux/uaccess.h> 3362306a36Sopenharmony_ci#include <linux/uuid.h> 3462306a36Sopenharmony_ci#include <linux/wmi.h> 3562306a36Sopenharmony_ci#include <linux/fs.h> 3662306a36Sopenharmony_ci#include <uapi/linux/wmi.h> 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ciMODULE_AUTHOR("Carlos Corbacho"); 3962306a36Sopenharmony_ciMODULE_DESCRIPTION("ACPI-WMI Mapping Driver"); 4062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic LIST_HEAD(wmi_block_list); 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistruct guid_block { 4562306a36Sopenharmony_ci guid_t guid; 4662306a36Sopenharmony_ci union { 4762306a36Sopenharmony_ci char object_id[2]; 4862306a36Sopenharmony_ci struct { 4962306a36Sopenharmony_ci unsigned char notify_id; 5062306a36Sopenharmony_ci unsigned char reserved; 5162306a36Sopenharmony_ci }; 5262306a36Sopenharmony_ci }; 5362306a36Sopenharmony_ci u8 instance_count; 5462306a36Sopenharmony_ci u8 flags; 5562306a36Sopenharmony_ci} __packed; 5662306a36Sopenharmony_cistatic_assert(sizeof(typeof_member(struct guid_block, guid)) == 16); 5762306a36Sopenharmony_cistatic_assert(sizeof(struct guid_block) == 20); 5862306a36Sopenharmony_cistatic_assert(__alignof__(struct guid_block) == 1); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cienum { /* wmi_block flags */ 6162306a36Sopenharmony_ci WMI_READ_TAKES_NO_ARGS, 6262306a36Sopenharmony_ci WMI_PROBED, 6362306a36Sopenharmony_ci}; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistruct wmi_block { 6662306a36Sopenharmony_ci struct wmi_device dev; 6762306a36Sopenharmony_ci struct list_head list; 6862306a36Sopenharmony_ci struct guid_block gblock; 6962306a36Sopenharmony_ci struct miscdevice char_dev; 7062306a36Sopenharmony_ci struct mutex char_mutex; 7162306a36Sopenharmony_ci struct acpi_device *acpi_device; 7262306a36Sopenharmony_ci wmi_notify_handler handler; 7362306a36Sopenharmony_ci void *handler_data; 7462306a36Sopenharmony_ci u64 req_buf_size; 7562306a36Sopenharmony_ci unsigned long flags; 7662306a36Sopenharmony_ci}; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci/* 8062306a36Sopenharmony_ci * If the GUID data block is marked as expensive, we must enable and 8162306a36Sopenharmony_ci * explicitily disable data collection. 8262306a36Sopenharmony_ci */ 8362306a36Sopenharmony_ci#define ACPI_WMI_EXPENSIVE BIT(0) 8462306a36Sopenharmony_ci#define ACPI_WMI_METHOD BIT(1) /* GUID is a method */ 8562306a36Sopenharmony_ci#define ACPI_WMI_STRING BIT(2) /* GUID takes & returns a string */ 8662306a36Sopenharmony_ci#define ACPI_WMI_EVENT BIT(3) /* GUID is an event */ 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic bool debug_event; 8962306a36Sopenharmony_cimodule_param(debug_event, bool, 0444); 9062306a36Sopenharmony_ciMODULE_PARM_DESC(debug_event, 9162306a36Sopenharmony_ci "Log WMI Events [0/1]"); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic bool debug_dump_wdg; 9462306a36Sopenharmony_cimodule_param(debug_dump_wdg, bool, 0444); 9562306a36Sopenharmony_ciMODULE_PARM_DESC(debug_dump_wdg, 9662306a36Sopenharmony_ci "Dump available WMI interfaces [0/1]"); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic const struct acpi_device_id wmi_device_ids[] = { 9962306a36Sopenharmony_ci {"PNP0C14", 0}, 10062306a36Sopenharmony_ci {"pnp0c14", 0}, 10162306a36Sopenharmony_ci { } 10262306a36Sopenharmony_ci}; 10362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, wmi_device_ids); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci/* allow duplicate GUIDs as these device drivers use struct wmi_driver */ 10662306a36Sopenharmony_cistatic const char * const allow_duplicates[] = { 10762306a36Sopenharmony_ci "05901221-D566-11D1-B2F0-00A0C9062910", /* wmi-bmof */ 10862306a36Sopenharmony_ci "8A42EA14-4F2A-FD45-6422-0087F7A7E608", /* dell-wmi-ddv */ 10962306a36Sopenharmony_ci NULL 11062306a36Sopenharmony_ci}; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci/* 11362306a36Sopenharmony_ci * GUID parsing functions 11462306a36Sopenharmony_ci */ 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic acpi_status find_guid(const char *guid_string, struct wmi_block **out) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci guid_t guid_input; 11962306a36Sopenharmony_ci struct wmi_block *wblock; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci if (!guid_string) 12262306a36Sopenharmony_ci return AE_BAD_PARAMETER; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci if (guid_parse(guid_string, &guid_input)) 12562306a36Sopenharmony_ci return AE_BAD_PARAMETER; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci list_for_each_entry(wblock, &wmi_block_list, list) { 12862306a36Sopenharmony_ci if (guid_equal(&wblock->gblock.guid, &guid_input)) { 12962306a36Sopenharmony_ci if (out) 13062306a36Sopenharmony_ci *out = wblock; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci return AE_OK; 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci return AE_NOT_FOUND; 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_cistatic bool guid_parse_and_compare(const char *string, const guid_t *guid) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci guid_t guid_input; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci if (guid_parse(string, &guid_input)) 14462306a36Sopenharmony_ci return false; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci return guid_equal(&guid_input, guid); 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistatic const void *find_guid_context(struct wmi_block *wblock, 15062306a36Sopenharmony_ci struct wmi_driver *wdriver) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci const struct wmi_device_id *id; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci id = wdriver->id_table; 15562306a36Sopenharmony_ci if (!id) 15662306a36Sopenharmony_ci return NULL; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci while (*id->guid_string) { 15962306a36Sopenharmony_ci if (guid_parse_and_compare(id->guid_string, &wblock->gblock.guid)) 16062306a36Sopenharmony_ci return id->context; 16162306a36Sopenharmony_ci id++; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci return NULL; 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic int get_subobj_info(acpi_handle handle, const char *pathname, 16762306a36Sopenharmony_ci struct acpi_device_info **info) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci struct acpi_device_info *dummy_info, **info_ptr; 17062306a36Sopenharmony_ci acpi_handle subobj_handle; 17162306a36Sopenharmony_ci acpi_status status; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci status = acpi_get_handle(handle, (char *)pathname, &subobj_handle); 17462306a36Sopenharmony_ci if (status == AE_NOT_FOUND) 17562306a36Sopenharmony_ci return -ENOENT; 17662306a36Sopenharmony_ci else if (ACPI_FAILURE(status)) 17762306a36Sopenharmony_ci return -EIO; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci info_ptr = info ? info : &dummy_info; 18062306a36Sopenharmony_ci status = acpi_get_object_info(subobj_handle, info_ptr); 18162306a36Sopenharmony_ci if (ACPI_FAILURE(status)) 18262306a36Sopenharmony_ci return -EIO; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci if (!info) 18562306a36Sopenharmony_ci kfree(dummy_info); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci return 0; 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic acpi_status wmi_method_enable(struct wmi_block *wblock, bool enable) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci struct guid_block *block; 19362306a36Sopenharmony_ci char method[5]; 19462306a36Sopenharmony_ci acpi_status status; 19562306a36Sopenharmony_ci acpi_handle handle; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci block = &wblock->gblock; 19862306a36Sopenharmony_ci handle = wblock->acpi_device->handle; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci snprintf(method, 5, "WE%02X", block->notify_id); 20162306a36Sopenharmony_ci status = acpi_execute_simple_method(handle, method, enable); 20262306a36Sopenharmony_ci if (status == AE_NOT_FOUND) 20362306a36Sopenharmony_ci return AE_OK; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci return status; 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci#define WMI_ACPI_METHOD_NAME_SIZE 5 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cistatic inline void get_acpi_method_name(const struct wmi_block *wblock, 21162306a36Sopenharmony_ci const char method, 21262306a36Sopenharmony_ci char buffer[static WMI_ACPI_METHOD_NAME_SIZE]) 21362306a36Sopenharmony_ci{ 21462306a36Sopenharmony_ci static_assert(ARRAY_SIZE(wblock->gblock.object_id) == 2); 21562306a36Sopenharmony_ci static_assert(WMI_ACPI_METHOD_NAME_SIZE >= 5); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci buffer[0] = 'W'; 21862306a36Sopenharmony_ci buffer[1] = method; 21962306a36Sopenharmony_ci buffer[2] = wblock->gblock.object_id[0]; 22062306a36Sopenharmony_ci buffer[3] = wblock->gblock.object_id[1]; 22162306a36Sopenharmony_ci buffer[4] = '\0'; 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_cistatic inline acpi_object_type get_param_acpi_type(const struct wmi_block *wblock) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci if (wblock->gblock.flags & ACPI_WMI_STRING) 22762306a36Sopenharmony_ci return ACPI_TYPE_STRING; 22862306a36Sopenharmony_ci else 22962306a36Sopenharmony_ci return ACPI_TYPE_BUFFER; 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistatic acpi_status get_event_data(const struct wmi_block *wblock, struct acpi_buffer *out) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci union acpi_object param = { 23562306a36Sopenharmony_ci .integer = { 23662306a36Sopenharmony_ci .type = ACPI_TYPE_INTEGER, 23762306a36Sopenharmony_ci .value = wblock->gblock.notify_id, 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci }; 24062306a36Sopenharmony_ci struct acpi_object_list input = { 24162306a36Sopenharmony_ci .count = 1, 24262306a36Sopenharmony_ci .pointer = ¶m, 24362306a36Sopenharmony_ci }; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci return acpi_evaluate_object(wblock->acpi_device->handle, "_WED", &input, out); 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci/* 24962306a36Sopenharmony_ci * Exported WMI functions 25062306a36Sopenharmony_ci */ 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci/** 25362306a36Sopenharmony_ci * set_required_buffer_size - Sets the buffer size needed for performing IOCTL 25462306a36Sopenharmony_ci * @wdev: A wmi bus device from a driver 25562306a36Sopenharmony_ci * @length: Required buffer size 25662306a36Sopenharmony_ci * 25762306a36Sopenharmony_ci * Allocates memory needed for buffer, stores the buffer size in that memory. 25862306a36Sopenharmony_ci * 25962306a36Sopenharmony_ci * Return: 0 on success or a negative error code for failure. 26062306a36Sopenharmony_ci */ 26162306a36Sopenharmony_ciint set_required_buffer_size(struct wmi_device *wdev, u64 length) 26262306a36Sopenharmony_ci{ 26362306a36Sopenharmony_ci struct wmi_block *wblock; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci wblock = container_of(wdev, struct wmi_block, dev); 26662306a36Sopenharmony_ci wblock->req_buf_size = length; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci return 0; 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(set_required_buffer_size); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci/** 27362306a36Sopenharmony_ci * wmi_instance_count - Get number of WMI object instances 27462306a36Sopenharmony_ci * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba 27562306a36Sopenharmony_ci * 27662306a36Sopenharmony_ci * Get the number of WMI object instances. 27762306a36Sopenharmony_ci * 27862306a36Sopenharmony_ci * Returns: Number of WMI object instances or negative error code. 27962306a36Sopenharmony_ci */ 28062306a36Sopenharmony_ciint wmi_instance_count(const char *guid_string) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci struct wmi_block *wblock; 28362306a36Sopenharmony_ci acpi_status status; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci status = find_guid(guid_string, &wblock); 28662306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 28762306a36Sopenharmony_ci if (status == AE_BAD_PARAMETER) 28862306a36Sopenharmony_ci return -EINVAL; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci return -ENODEV; 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci return wmidev_instance_count(&wblock->dev); 29462306a36Sopenharmony_ci} 29562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(wmi_instance_count); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci/** 29862306a36Sopenharmony_ci * wmidev_instance_count - Get number of WMI object instances 29962306a36Sopenharmony_ci * @wdev: A wmi bus device from a driver 30062306a36Sopenharmony_ci * 30162306a36Sopenharmony_ci * Get the number of WMI object instances. 30262306a36Sopenharmony_ci * 30362306a36Sopenharmony_ci * Returns: Number of WMI object instances. 30462306a36Sopenharmony_ci */ 30562306a36Sopenharmony_ciu8 wmidev_instance_count(struct wmi_device *wdev) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci struct wmi_block *wblock = container_of(wdev, struct wmi_block, dev); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci return wblock->gblock.instance_count; 31062306a36Sopenharmony_ci} 31162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(wmidev_instance_count); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci/** 31462306a36Sopenharmony_ci * wmi_evaluate_method - Evaluate a WMI method (deprecated) 31562306a36Sopenharmony_ci * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba 31662306a36Sopenharmony_ci * @instance: Instance index 31762306a36Sopenharmony_ci * @method_id: Method ID to call 31862306a36Sopenharmony_ci * @in: Buffer containing input for the method call 31962306a36Sopenharmony_ci * @out: Empty buffer to return the method results 32062306a36Sopenharmony_ci * 32162306a36Sopenharmony_ci * Call an ACPI-WMI method, the caller must free @out. 32262306a36Sopenharmony_ci * 32362306a36Sopenharmony_ci * Return: acpi_status signaling success or error. 32462306a36Sopenharmony_ci */ 32562306a36Sopenharmony_ciacpi_status wmi_evaluate_method(const char *guid_string, u8 instance, u32 method_id, 32662306a36Sopenharmony_ci const struct acpi_buffer *in, struct acpi_buffer *out) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci struct wmi_block *wblock = NULL; 32962306a36Sopenharmony_ci acpi_status status; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci status = find_guid(guid_string, &wblock); 33262306a36Sopenharmony_ci if (ACPI_FAILURE(status)) 33362306a36Sopenharmony_ci return status; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci return wmidev_evaluate_method(&wblock->dev, instance, method_id, 33662306a36Sopenharmony_ci in, out); 33762306a36Sopenharmony_ci} 33862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(wmi_evaluate_method); 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci/** 34162306a36Sopenharmony_ci * wmidev_evaluate_method - Evaluate a WMI method 34262306a36Sopenharmony_ci * @wdev: A wmi bus device from a driver 34362306a36Sopenharmony_ci * @instance: Instance index 34462306a36Sopenharmony_ci * @method_id: Method ID to call 34562306a36Sopenharmony_ci * @in: Buffer containing input for the method call 34662306a36Sopenharmony_ci * @out: Empty buffer to return the method results 34762306a36Sopenharmony_ci * 34862306a36Sopenharmony_ci * Call an ACPI-WMI method, the caller must free @out. 34962306a36Sopenharmony_ci * 35062306a36Sopenharmony_ci * Return: acpi_status signaling success or error. 35162306a36Sopenharmony_ci */ 35262306a36Sopenharmony_ciacpi_status wmidev_evaluate_method(struct wmi_device *wdev, u8 instance, u32 method_id, 35362306a36Sopenharmony_ci const struct acpi_buffer *in, struct acpi_buffer *out) 35462306a36Sopenharmony_ci{ 35562306a36Sopenharmony_ci struct guid_block *block; 35662306a36Sopenharmony_ci struct wmi_block *wblock; 35762306a36Sopenharmony_ci acpi_handle handle; 35862306a36Sopenharmony_ci struct acpi_object_list input; 35962306a36Sopenharmony_ci union acpi_object params[3]; 36062306a36Sopenharmony_ci char method[WMI_ACPI_METHOD_NAME_SIZE]; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci wblock = container_of(wdev, struct wmi_block, dev); 36362306a36Sopenharmony_ci block = &wblock->gblock; 36462306a36Sopenharmony_ci handle = wblock->acpi_device->handle; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci if (!(block->flags & ACPI_WMI_METHOD)) 36762306a36Sopenharmony_ci return AE_BAD_DATA; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci if (block->instance_count <= instance) 37062306a36Sopenharmony_ci return AE_BAD_PARAMETER; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci input.count = 2; 37362306a36Sopenharmony_ci input.pointer = params; 37462306a36Sopenharmony_ci params[0].type = ACPI_TYPE_INTEGER; 37562306a36Sopenharmony_ci params[0].integer.value = instance; 37662306a36Sopenharmony_ci params[1].type = ACPI_TYPE_INTEGER; 37762306a36Sopenharmony_ci params[1].integer.value = method_id; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci if (in) { 38062306a36Sopenharmony_ci input.count = 3; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci params[2].type = get_param_acpi_type(wblock); 38362306a36Sopenharmony_ci params[2].buffer.length = in->length; 38462306a36Sopenharmony_ci params[2].buffer.pointer = in->pointer; 38562306a36Sopenharmony_ci } 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci get_acpi_method_name(wblock, 'M', method); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci return acpi_evaluate_object(handle, method, &input, out); 39062306a36Sopenharmony_ci} 39162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(wmidev_evaluate_method); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_cistatic acpi_status __query_block(struct wmi_block *wblock, u8 instance, 39462306a36Sopenharmony_ci struct acpi_buffer *out) 39562306a36Sopenharmony_ci{ 39662306a36Sopenharmony_ci struct guid_block *block; 39762306a36Sopenharmony_ci acpi_handle handle; 39862306a36Sopenharmony_ci acpi_status status, wc_status = AE_ERROR; 39962306a36Sopenharmony_ci struct acpi_object_list input; 40062306a36Sopenharmony_ci union acpi_object wq_params[1]; 40162306a36Sopenharmony_ci char wc_method[WMI_ACPI_METHOD_NAME_SIZE]; 40262306a36Sopenharmony_ci char method[WMI_ACPI_METHOD_NAME_SIZE]; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci if (!out) 40562306a36Sopenharmony_ci return AE_BAD_PARAMETER; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci block = &wblock->gblock; 40862306a36Sopenharmony_ci handle = wblock->acpi_device->handle; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci if (block->instance_count <= instance) 41162306a36Sopenharmony_ci return AE_BAD_PARAMETER; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci /* Check GUID is a data block */ 41462306a36Sopenharmony_ci if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD)) 41562306a36Sopenharmony_ci return AE_ERROR; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci input.count = 1; 41862306a36Sopenharmony_ci input.pointer = wq_params; 41962306a36Sopenharmony_ci wq_params[0].type = ACPI_TYPE_INTEGER; 42062306a36Sopenharmony_ci wq_params[0].integer.value = instance; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci if (instance == 0 && test_bit(WMI_READ_TAKES_NO_ARGS, &wblock->flags)) 42362306a36Sopenharmony_ci input.count = 0; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci /* 42662306a36Sopenharmony_ci * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method first to 42762306a36Sopenharmony_ci * enable collection. 42862306a36Sopenharmony_ci */ 42962306a36Sopenharmony_ci if (block->flags & ACPI_WMI_EXPENSIVE) { 43062306a36Sopenharmony_ci get_acpi_method_name(wblock, 'C', wc_method); 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci /* 43362306a36Sopenharmony_ci * Some GUIDs break the specification by declaring themselves 43462306a36Sopenharmony_ci * expensive, but have no corresponding WCxx method. So we 43562306a36Sopenharmony_ci * should not fail if this happens. 43662306a36Sopenharmony_ci */ 43762306a36Sopenharmony_ci wc_status = acpi_execute_simple_method(handle, wc_method, 1); 43862306a36Sopenharmony_ci } 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci get_acpi_method_name(wblock, 'Q', method); 44162306a36Sopenharmony_ci status = acpi_evaluate_object(handle, method, &input, out); 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci /* 44462306a36Sopenharmony_ci * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method, even if 44562306a36Sopenharmony_ci * the WQxx method failed - we should disable collection anyway. 44662306a36Sopenharmony_ci */ 44762306a36Sopenharmony_ci if ((block->flags & ACPI_WMI_EXPENSIVE) && ACPI_SUCCESS(wc_status)) { 44862306a36Sopenharmony_ci /* 44962306a36Sopenharmony_ci * Ignore whether this WCxx call succeeds or not since 45062306a36Sopenharmony_ci * the previously executed WQxx method call might have 45162306a36Sopenharmony_ci * succeeded, and returning the failing status code 45262306a36Sopenharmony_ci * of this call would throw away the result of the WQxx 45362306a36Sopenharmony_ci * call, potentially leaking memory. 45462306a36Sopenharmony_ci */ 45562306a36Sopenharmony_ci acpi_execute_simple_method(handle, wc_method, 0); 45662306a36Sopenharmony_ci } 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci return status; 45962306a36Sopenharmony_ci} 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci/** 46262306a36Sopenharmony_ci * wmi_query_block - Return contents of a WMI block (deprecated) 46362306a36Sopenharmony_ci * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba 46462306a36Sopenharmony_ci * @instance: Instance index 46562306a36Sopenharmony_ci * @out: Empty buffer to return the contents of the data block to 46662306a36Sopenharmony_ci * 46762306a36Sopenharmony_ci * Query a ACPI-WMI block, the caller must free @out. 46862306a36Sopenharmony_ci * 46962306a36Sopenharmony_ci * Return: ACPI object containing the content of the WMI block. 47062306a36Sopenharmony_ci */ 47162306a36Sopenharmony_ciacpi_status wmi_query_block(const char *guid_string, u8 instance, 47262306a36Sopenharmony_ci struct acpi_buffer *out) 47362306a36Sopenharmony_ci{ 47462306a36Sopenharmony_ci struct wmi_block *wblock; 47562306a36Sopenharmony_ci acpi_status status; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci status = find_guid(guid_string, &wblock); 47862306a36Sopenharmony_ci if (ACPI_FAILURE(status)) 47962306a36Sopenharmony_ci return status; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci return __query_block(wblock, instance, out); 48262306a36Sopenharmony_ci} 48362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(wmi_query_block); 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci/** 48662306a36Sopenharmony_ci * wmidev_block_query - Return contents of a WMI block 48762306a36Sopenharmony_ci * @wdev: A wmi bus device from a driver 48862306a36Sopenharmony_ci * @instance: Instance index 48962306a36Sopenharmony_ci * 49062306a36Sopenharmony_ci * Query an ACPI-WMI block, the caller must free the result. 49162306a36Sopenharmony_ci * 49262306a36Sopenharmony_ci * Return: ACPI object containing the content of the WMI block. 49362306a36Sopenharmony_ci */ 49462306a36Sopenharmony_ciunion acpi_object *wmidev_block_query(struct wmi_device *wdev, u8 instance) 49562306a36Sopenharmony_ci{ 49662306a36Sopenharmony_ci struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL }; 49762306a36Sopenharmony_ci struct wmi_block *wblock = container_of(wdev, struct wmi_block, dev); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci if (ACPI_FAILURE(__query_block(wblock, instance, &out))) 50062306a36Sopenharmony_ci return NULL; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci return out.pointer; 50362306a36Sopenharmony_ci} 50462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(wmidev_block_query); 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci/** 50762306a36Sopenharmony_ci * wmi_set_block - Write to a WMI block (deprecated) 50862306a36Sopenharmony_ci * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba 50962306a36Sopenharmony_ci * @instance: Instance index 51062306a36Sopenharmony_ci * @in: Buffer containing new values for the data block 51162306a36Sopenharmony_ci * 51262306a36Sopenharmony_ci * Write the contents of the input buffer to an ACPI-WMI data block. 51362306a36Sopenharmony_ci * 51462306a36Sopenharmony_ci * Return: acpi_status signaling success or error. 51562306a36Sopenharmony_ci */ 51662306a36Sopenharmony_ciacpi_status wmi_set_block(const char *guid_string, u8 instance, 51762306a36Sopenharmony_ci const struct acpi_buffer *in) 51862306a36Sopenharmony_ci{ 51962306a36Sopenharmony_ci struct wmi_block *wblock = NULL; 52062306a36Sopenharmony_ci struct guid_block *block; 52162306a36Sopenharmony_ci acpi_handle handle; 52262306a36Sopenharmony_ci struct acpi_object_list input; 52362306a36Sopenharmony_ci union acpi_object params[2]; 52462306a36Sopenharmony_ci char method[WMI_ACPI_METHOD_NAME_SIZE]; 52562306a36Sopenharmony_ci acpi_status status; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci if (!in) 52862306a36Sopenharmony_ci return AE_BAD_DATA; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci status = find_guid(guid_string, &wblock); 53162306a36Sopenharmony_ci if (ACPI_FAILURE(status)) 53262306a36Sopenharmony_ci return status; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci block = &wblock->gblock; 53562306a36Sopenharmony_ci handle = wblock->acpi_device->handle; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci if (block->instance_count <= instance) 53862306a36Sopenharmony_ci return AE_BAD_PARAMETER; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci /* Check GUID is a data block */ 54162306a36Sopenharmony_ci if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD)) 54262306a36Sopenharmony_ci return AE_ERROR; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci input.count = 2; 54562306a36Sopenharmony_ci input.pointer = params; 54662306a36Sopenharmony_ci params[0].type = ACPI_TYPE_INTEGER; 54762306a36Sopenharmony_ci params[0].integer.value = instance; 54862306a36Sopenharmony_ci params[1].type = get_param_acpi_type(wblock); 54962306a36Sopenharmony_ci params[1].buffer.length = in->length; 55062306a36Sopenharmony_ci params[1].buffer.pointer = in->pointer; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci get_acpi_method_name(wblock, 'S', method); 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci return acpi_evaluate_object(handle, method, &input, NULL); 55562306a36Sopenharmony_ci} 55662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(wmi_set_block); 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_cistatic void wmi_dump_wdg(const struct guid_block *g) 55962306a36Sopenharmony_ci{ 56062306a36Sopenharmony_ci pr_info("%pUL:\n", &g->guid); 56162306a36Sopenharmony_ci if (g->flags & ACPI_WMI_EVENT) 56262306a36Sopenharmony_ci pr_info("\tnotify_id: 0x%02X\n", g->notify_id); 56362306a36Sopenharmony_ci else 56462306a36Sopenharmony_ci pr_info("\tobject_id: %2pE\n", g->object_id); 56562306a36Sopenharmony_ci pr_info("\tinstance_count: %d\n", g->instance_count); 56662306a36Sopenharmony_ci pr_info("\tflags: %#x", g->flags); 56762306a36Sopenharmony_ci if (g->flags) { 56862306a36Sopenharmony_ci if (g->flags & ACPI_WMI_EXPENSIVE) 56962306a36Sopenharmony_ci pr_cont(" ACPI_WMI_EXPENSIVE"); 57062306a36Sopenharmony_ci if (g->flags & ACPI_WMI_METHOD) 57162306a36Sopenharmony_ci pr_cont(" ACPI_WMI_METHOD"); 57262306a36Sopenharmony_ci if (g->flags & ACPI_WMI_STRING) 57362306a36Sopenharmony_ci pr_cont(" ACPI_WMI_STRING"); 57462306a36Sopenharmony_ci if (g->flags & ACPI_WMI_EVENT) 57562306a36Sopenharmony_ci pr_cont(" ACPI_WMI_EVENT"); 57662306a36Sopenharmony_ci } 57762306a36Sopenharmony_ci pr_cont("\n"); 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci} 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_cistatic void wmi_notify_debug(u32 value, void *context) 58262306a36Sopenharmony_ci{ 58362306a36Sopenharmony_ci struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; 58462306a36Sopenharmony_ci union acpi_object *obj; 58562306a36Sopenharmony_ci acpi_status status; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci status = wmi_get_event_data(value, &response); 58862306a36Sopenharmony_ci if (status != AE_OK) { 58962306a36Sopenharmony_ci pr_info("bad event status 0x%x\n", status); 59062306a36Sopenharmony_ci return; 59162306a36Sopenharmony_ci } 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci obj = response.pointer; 59462306a36Sopenharmony_ci if (!obj) 59562306a36Sopenharmony_ci return; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci pr_info("DEBUG: event 0x%02X ", value); 59862306a36Sopenharmony_ci switch (obj->type) { 59962306a36Sopenharmony_ci case ACPI_TYPE_BUFFER: 60062306a36Sopenharmony_ci pr_cont("BUFFER_TYPE - length %u\n", obj->buffer.length); 60162306a36Sopenharmony_ci break; 60262306a36Sopenharmony_ci case ACPI_TYPE_STRING: 60362306a36Sopenharmony_ci pr_cont("STRING_TYPE - %s\n", obj->string.pointer); 60462306a36Sopenharmony_ci break; 60562306a36Sopenharmony_ci case ACPI_TYPE_INTEGER: 60662306a36Sopenharmony_ci pr_cont("INTEGER_TYPE - %llu\n", obj->integer.value); 60762306a36Sopenharmony_ci break; 60862306a36Sopenharmony_ci case ACPI_TYPE_PACKAGE: 60962306a36Sopenharmony_ci pr_cont("PACKAGE_TYPE - %u elements\n", obj->package.count); 61062306a36Sopenharmony_ci break; 61162306a36Sopenharmony_ci default: 61262306a36Sopenharmony_ci pr_cont("object type 0x%X\n", obj->type); 61362306a36Sopenharmony_ci } 61462306a36Sopenharmony_ci kfree(obj); 61562306a36Sopenharmony_ci} 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci/** 61862306a36Sopenharmony_ci * wmi_install_notify_handler - Register handler for WMI events (deprecated) 61962306a36Sopenharmony_ci * @guid: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba 62062306a36Sopenharmony_ci * @handler: Function to handle notifications 62162306a36Sopenharmony_ci * @data: Data to be returned to handler when event is fired 62262306a36Sopenharmony_ci * 62362306a36Sopenharmony_ci * Register a handler for events sent to the ACPI-WMI mapper device. 62462306a36Sopenharmony_ci * 62562306a36Sopenharmony_ci * Return: acpi_status signaling success or error. 62662306a36Sopenharmony_ci */ 62762306a36Sopenharmony_ciacpi_status wmi_install_notify_handler(const char *guid, 62862306a36Sopenharmony_ci wmi_notify_handler handler, 62962306a36Sopenharmony_ci void *data) 63062306a36Sopenharmony_ci{ 63162306a36Sopenharmony_ci struct wmi_block *block; 63262306a36Sopenharmony_ci acpi_status status = AE_NOT_EXIST; 63362306a36Sopenharmony_ci guid_t guid_input; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci if (!guid || !handler) 63662306a36Sopenharmony_ci return AE_BAD_PARAMETER; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci if (guid_parse(guid, &guid_input)) 63962306a36Sopenharmony_ci return AE_BAD_PARAMETER; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci list_for_each_entry(block, &wmi_block_list, list) { 64262306a36Sopenharmony_ci acpi_status wmi_status; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci if (guid_equal(&block->gblock.guid, &guid_input)) { 64562306a36Sopenharmony_ci if (block->handler && 64662306a36Sopenharmony_ci block->handler != wmi_notify_debug) 64762306a36Sopenharmony_ci return AE_ALREADY_ACQUIRED; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci block->handler = handler; 65062306a36Sopenharmony_ci block->handler_data = data; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci wmi_status = wmi_method_enable(block, true); 65362306a36Sopenharmony_ci if ((wmi_status != AE_OK) || 65462306a36Sopenharmony_ci ((wmi_status == AE_OK) && (status == AE_NOT_EXIST))) 65562306a36Sopenharmony_ci status = wmi_status; 65662306a36Sopenharmony_ci } 65762306a36Sopenharmony_ci } 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci return status; 66062306a36Sopenharmony_ci} 66162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(wmi_install_notify_handler); 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci/** 66462306a36Sopenharmony_ci * wmi_remove_notify_handler - Unregister handler for WMI events (deprecated) 66562306a36Sopenharmony_ci * @guid: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba 66662306a36Sopenharmony_ci * 66762306a36Sopenharmony_ci * Unregister handler for events sent to the ACPI-WMI mapper device. 66862306a36Sopenharmony_ci * 66962306a36Sopenharmony_ci * Return: acpi_status signaling success or error. 67062306a36Sopenharmony_ci */ 67162306a36Sopenharmony_ciacpi_status wmi_remove_notify_handler(const char *guid) 67262306a36Sopenharmony_ci{ 67362306a36Sopenharmony_ci struct wmi_block *block; 67462306a36Sopenharmony_ci acpi_status status = AE_NOT_EXIST; 67562306a36Sopenharmony_ci guid_t guid_input; 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci if (!guid) 67862306a36Sopenharmony_ci return AE_BAD_PARAMETER; 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci if (guid_parse(guid, &guid_input)) 68162306a36Sopenharmony_ci return AE_BAD_PARAMETER; 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci list_for_each_entry(block, &wmi_block_list, list) { 68462306a36Sopenharmony_ci acpi_status wmi_status; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci if (guid_equal(&block->gblock.guid, &guid_input)) { 68762306a36Sopenharmony_ci if (!block->handler || 68862306a36Sopenharmony_ci block->handler == wmi_notify_debug) 68962306a36Sopenharmony_ci return AE_NULL_ENTRY; 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci if (debug_event) { 69262306a36Sopenharmony_ci block->handler = wmi_notify_debug; 69362306a36Sopenharmony_ci status = AE_OK; 69462306a36Sopenharmony_ci } else { 69562306a36Sopenharmony_ci wmi_status = wmi_method_enable(block, false); 69662306a36Sopenharmony_ci block->handler = NULL; 69762306a36Sopenharmony_ci block->handler_data = NULL; 69862306a36Sopenharmony_ci if ((wmi_status != AE_OK) || 69962306a36Sopenharmony_ci ((wmi_status == AE_OK) && 70062306a36Sopenharmony_ci (status == AE_NOT_EXIST))) 70162306a36Sopenharmony_ci status = wmi_status; 70262306a36Sopenharmony_ci } 70362306a36Sopenharmony_ci } 70462306a36Sopenharmony_ci } 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci return status; 70762306a36Sopenharmony_ci} 70862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(wmi_remove_notify_handler); 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci/** 71162306a36Sopenharmony_ci * wmi_get_event_data - Get WMI data associated with an event (deprecated) 71262306a36Sopenharmony_ci * 71362306a36Sopenharmony_ci * @event: Event to find 71462306a36Sopenharmony_ci * @out: Buffer to hold event data 71562306a36Sopenharmony_ci * 71662306a36Sopenharmony_ci * Get extra data associated with an WMI event, the caller needs to free @out. 71762306a36Sopenharmony_ci * 71862306a36Sopenharmony_ci * Return: acpi_status signaling success or error. 71962306a36Sopenharmony_ci */ 72062306a36Sopenharmony_ciacpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out) 72162306a36Sopenharmony_ci{ 72262306a36Sopenharmony_ci struct wmi_block *wblock; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci list_for_each_entry(wblock, &wmi_block_list, list) { 72562306a36Sopenharmony_ci struct guid_block *gblock = &wblock->gblock; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci if ((gblock->flags & ACPI_WMI_EVENT) && gblock->notify_id == event) 72862306a36Sopenharmony_ci return get_event_data(wblock, out); 72962306a36Sopenharmony_ci } 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci return AE_NOT_FOUND; 73262306a36Sopenharmony_ci} 73362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(wmi_get_event_data); 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci/** 73662306a36Sopenharmony_ci * wmi_has_guid - Check if a GUID is available 73762306a36Sopenharmony_ci * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba 73862306a36Sopenharmony_ci * 73962306a36Sopenharmony_ci * Check if a given GUID is defined by _WDG. 74062306a36Sopenharmony_ci * 74162306a36Sopenharmony_ci * Return: True if GUID is available, false otherwise. 74262306a36Sopenharmony_ci */ 74362306a36Sopenharmony_cibool wmi_has_guid(const char *guid_string) 74462306a36Sopenharmony_ci{ 74562306a36Sopenharmony_ci return ACPI_SUCCESS(find_guid(guid_string, NULL)); 74662306a36Sopenharmony_ci} 74762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(wmi_has_guid); 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci/** 75062306a36Sopenharmony_ci * wmi_get_acpi_device_uid() - Get _UID name of ACPI device that defines GUID (deprecated) 75162306a36Sopenharmony_ci * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba 75262306a36Sopenharmony_ci * 75362306a36Sopenharmony_ci * Find the _UID of ACPI device associated with this WMI GUID. 75462306a36Sopenharmony_ci * 75562306a36Sopenharmony_ci * Return: The ACPI _UID field value or NULL if the WMI GUID was not found. 75662306a36Sopenharmony_ci */ 75762306a36Sopenharmony_cichar *wmi_get_acpi_device_uid(const char *guid_string) 75862306a36Sopenharmony_ci{ 75962306a36Sopenharmony_ci struct wmi_block *wblock = NULL; 76062306a36Sopenharmony_ci acpi_status status; 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci status = find_guid(guid_string, &wblock); 76362306a36Sopenharmony_ci if (ACPI_FAILURE(status)) 76462306a36Sopenharmony_ci return NULL; 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci return acpi_device_uid(wblock->acpi_device); 76762306a36Sopenharmony_ci} 76862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(wmi_get_acpi_device_uid); 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci#define dev_to_wblock(__dev) container_of_const(__dev, struct wmi_block, dev.dev) 77162306a36Sopenharmony_ci#define dev_to_wdev(__dev) container_of_const(__dev, struct wmi_device, dev) 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_cistatic inline struct wmi_driver *drv_to_wdrv(struct device_driver *drv) 77462306a36Sopenharmony_ci{ 77562306a36Sopenharmony_ci return container_of(drv, struct wmi_driver, driver); 77662306a36Sopenharmony_ci} 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci/* 77962306a36Sopenharmony_ci * sysfs interface 78062306a36Sopenharmony_ci */ 78162306a36Sopenharmony_cistatic ssize_t modalias_show(struct device *dev, struct device_attribute *attr, 78262306a36Sopenharmony_ci char *buf) 78362306a36Sopenharmony_ci{ 78462306a36Sopenharmony_ci struct wmi_block *wblock = dev_to_wblock(dev); 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci return sysfs_emit(buf, "wmi:%pUL\n", &wblock->gblock.guid); 78762306a36Sopenharmony_ci} 78862306a36Sopenharmony_cistatic DEVICE_ATTR_RO(modalias); 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_cistatic ssize_t guid_show(struct device *dev, struct device_attribute *attr, 79162306a36Sopenharmony_ci char *buf) 79262306a36Sopenharmony_ci{ 79362306a36Sopenharmony_ci struct wmi_block *wblock = dev_to_wblock(dev); 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci return sysfs_emit(buf, "%pUL\n", &wblock->gblock.guid); 79662306a36Sopenharmony_ci} 79762306a36Sopenharmony_cistatic DEVICE_ATTR_RO(guid); 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_cistatic ssize_t instance_count_show(struct device *dev, 80062306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 80162306a36Sopenharmony_ci{ 80262306a36Sopenharmony_ci struct wmi_block *wblock = dev_to_wblock(dev); 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", (int)wblock->gblock.instance_count); 80562306a36Sopenharmony_ci} 80662306a36Sopenharmony_cistatic DEVICE_ATTR_RO(instance_count); 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_cistatic ssize_t expensive_show(struct device *dev, 80962306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 81062306a36Sopenharmony_ci{ 81162306a36Sopenharmony_ci struct wmi_block *wblock = dev_to_wblock(dev); 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", 81462306a36Sopenharmony_ci (wblock->gblock.flags & ACPI_WMI_EXPENSIVE) != 0); 81562306a36Sopenharmony_ci} 81662306a36Sopenharmony_cistatic DEVICE_ATTR_RO(expensive); 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_cistatic struct attribute *wmi_attrs[] = { 81962306a36Sopenharmony_ci &dev_attr_modalias.attr, 82062306a36Sopenharmony_ci &dev_attr_guid.attr, 82162306a36Sopenharmony_ci &dev_attr_instance_count.attr, 82262306a36Sopenharmony_ci &dev_attr_expensive.attr, 82362306a36Sopenharmony_ci NULL 82462306a36Sopenharmony_ci}; 82562306a36Sopenharmony_ciATTRIBUTE_GROUPS(wmi); 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_cistatic ssize_t notify_id_show(struct device *dev, struct device_attribute *attr, 82862306a36Sopenharmony_ci char *buf) 82962306a36Sopenharmony_ci{ 83062306a36Sopenharmony_ci struct wmi_block *wblock = dev_to_wblock(dev); 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci return sysfs_emit(buf, "%02X\n", (unsigned int)wblock->gblock.notify_id); 83362306a36Sopenharmony_ci} 83462306a36Sopenharmony_cistatic DEVICE_ATTR_RO(notify_id); 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_cistatic struct attribute *wmi_event_attrs[] = { 83762306a36Sopenharmony_ci &dev_attr_notify_id.attr, 83862306a36Sopenharmony_ci NULL 83962306a36Sopenharmony_ci}; 84062306a36Sopenharmony_ciATTRIBUTE_GROUPS(wmi_event); 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_cistatic ssize_t object_id_show(struct device *dev, struct device_attribute *attr, 84362306a36Sopenharmony_ci char *buf) 84462306a36Sopenharmony_ci{ 84562306a36Sopenharmony_ci struct wmi_block *wblock = dev_to_wblock(dev); 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci return sysfs_emit(buf, "%c%c\n", wblock->gblock.object_id[0], 84862306a36Sopenharmony_ci wblock->gblock.object_id[1]); 84962306a36Sopenharmony_ci} 85062306a36Sopenharmony_cistatic DEVICE_ATTR_RO(object_id); 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_cistatic ssize_t setable_show(struct device *dev, struct device_attribute *attr, 85362306a36Sopenharmony_ci char *buf) 85462306a36Sopenharmony_ci{ 85562306a36Sopenharmony_ci struct wmi_device *wdev = dev_to_wdev(dev); 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", (int)wdev->setable); 85862306a36Sopenharmony_ci} 85962306a36Sopenharmony_cistatic DEVICE_ATTR_RO(setable); 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_cistatic struct attribute *wmi_data_attrs[] = { 86262306a36Sopenharmony_ci &dev_attr_object_id.attr, 86362306a36Sopenharmony_ci &dev_attr_setable.attr, 86462306a36Sopenharmony_ci NULL 86562306a36Sopenharmony_ci}; 86662306a36Sopenharmony_ciATTRIBUTE_GROUPS(wmi_data); 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_cistatic struct attribute *wmi_method_attrs[] = { 86962306a36Sopenharmony_ci &dev_attr_object_id.attr, 87062306a36Sopenharmony_ci NULL 87162306a36Sopenharmony_ci}; 87262306a36Sopenharmony_ciATTRIBUTE_GROUPS(wmi_method); 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_cistatic int wmi_dev_uevent(const struct device *dev, struct kobj_uevent_env *env) 87562306a36Sopenharmony_ci{ 87662306a36Sopenharmony_ci const struct wmi_block *wblock = dev_to_wblock(dev); 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci if (add_uevent_var(env, "MODALIAS=wmi:%pUL", &wblock->gblock.guid)) 87962306a36Sopenharmony_ci return -ENOMEM; 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci if (add_uevent_var(env, "WMI_GUID=%pUL", &wblock->gblock.guid)) 88262306a36Sopenharmony_ci return -ENOMEM; 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci return 0; 88562306a36Sopenharmony_ci} 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_cistatic void wmi_dev_release(struct device *dev) 88862306a36Sopenharmony_ci{ 88962306a36Sopenharmony_ci struct wmi_block *wblock = dev_to_wblock(dev); 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci kfree(wblock); 89262306a36Sopenharmony_ci} 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_cistatic int wmi_dev_match(struct device *dev, struct device_driver *driver) 89562306a36Sopenharmony_ci{ 89662306a36Sopenharmony_ci struct wmi_driver *wmi_driver = drv_to_wdrv(driver); 89762306a36Sopenharmony_ci struct wmi_block *wblock = dev_to_wblock(dev); 89862306a36Sopenharmony_ci const struct wmi_device_id *id = wmi_driver->id_table; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci if (id == NULL) 90162306a36Sopenharmony_ci return 0; 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci while (*id->guid_string) { 90462306a36Sopenharmony_ci if (guid_parse_and_compare(id->guid_string, &wblock->gblock.guid)) 90562306a36Sopenharmony_ci return 1; 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci id++; 90862306a36Sopenharmony_ci } 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci return 0; 91162306a36Sopenharmony_ci} 91262306a36Sopenharmony_cistatic int wmi_char_open(struct inode *inode, struct file *filp) 91362306a36Sopenharmony_ci{ 91462306a36Sopenharmony_ci /* 91562306a36Sopenharmony_ci * The miscdevice already stores a pointer to itself 91662306a36Sopenharmony_ci * inside filp->private_data 91762306a36Sopenharmony_ci */ 91862306a36Sopenharmony_ci struct wmi_block *wblock = container_of(filp->private_data, struct wmi_block, char_dev); 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci filp->private_data = wblock; 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci return nonseekable_open(inode, filp); 92362306a36Sopenharmony_ci} 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_cistatic ssize_t wmi_char_read(struct file *filp, char __user *buffer, 92662306a36Sopenharmony_ci size_t length, loff_t *offset) 92762306a36Sopenharmony_ci{ 92862306a36Sopenharmony_ci struct wmi_block *wblock = filp->private_data; 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci return simple_read_from_buffer(buffer, length, offset, 93162306a36Sopenharmony_ci &wblock->req_buf_size, 93262306a36Sopenharmony_ci sizeof(wblock->req_buf_size)); 93362306a36Sopenharmony_ci} 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_cistatic long wmi_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 93662306a36Sopenharmony_ci{ 93762306a36Sopenharmony_ci struct wmi_ioctl_buffer __user *input = 93862306a36Sopenharmony_ci (struct wmi_ioctl_buffer __user *) arg; 93962306a36Sopenharmony_ci struct wmi_block *wblock = filp->private_data; 94062306a36Sopenharmony_ci struct wmi_ioctl_buffer *buf; 94162306a36Sopenharmony_ci struct wmi_driver *wdriver; 94262306a36Sopenharmony_ci int ret; 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci if (_IOC_TYPE(cmd) != WMI_IOC) 94562306a36Sopenharmony_ci return -ENOTTY; 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci /* make sure we're not calling a higher instance than exists*/ 94862306a36Sopenharmony_ci if (_IOC_NR(cmd) >= wblock->gblock.instance_count) 94962306a36Sopenharmony_ci return -EINVAL; 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci mutex_lock(&wblock->char_mutex); 95262306a36Sopenharmony_ci buf = wblock->handler_data; 95362306a36Sopenharmony_ci if (get_user(buf->length, &input->length)) { 95462306a36Sopenharmony_ci dev_dbg(&wblock->dev.dev, "Read length from user failed\n"); 95562306a36Sopenharmony_ci ret = -EFAULT; 95662306a36Sopenharmony_ci goto out_ioctl; 95762306a36Sopenharmony_ci } 95862306a36Sopenharmony_ci /* if it's too small, abort */ 95962306a36Sopenharmony_ci if (buf->length < wblock->req_buf_size) { 96062306a36Sopenharmony_ci dev_err(&wblock->dev.dev, 96162306a36Sopenharmony_ci "Buffer %lld too small, need at least %lld\n", 96262306a36Sopenharmony_ci buf->length, wblock->req_buf_size); 96362306a36Sopenharmony_ci ret = -EINVAL; 96462306a36Sopenharmony_ci goto out_ioctl; 96562306a36Sopenharmony_ci } 96662306a36Sopenharmony_ci /* if it's too big, warn, driver will only use what is needed */ 96762306a36Sopenharmony_ci if (buf->length > wblock->req_buf_size) 96862306a36Sopenharmony_ci dev_warn(&wblock->dev.dev, 96962306a36Sopenharmony_ci "Buffer %lld is bigger than required %lld\n", 97062306a36Sopenharmony_ci buf->length, wblock->req_buf_size); 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci /* copy the structure from userspace */ 97362306a36Sopenharmony_ci if (copy_from_user(buf, input, wblock->req_buf_size)) { 97462306a36Sopenharmony_ci dev_dbg(&wblock->dev.dev, "Copy %llu from user failed\n", 97562306a36Sopenharmony_ci wblock->req_buf_size); 97662306a36Sopenharmony_ci ret = -EFAULT; 97762306a36Sopenharmony_ci goto out_ioctl; 97862306a36Sopenharmony_ci } 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci /* let the driver do any filtering and do the call */ 98162306a36Sopenharmony_ci wdriver = drv_to_wdrv(wblock->dev.dev.driver); 98262306a36Sopenharmony_ci if (!try_module_get(wdriver->driver.owner)) { 98362306a36Sopenharmony_ci ret = -EBUSY; 98462306a36Sopenharmony_ci goto out_ioctl; 98562306a36Sopenharmony_ci } 98662306a36Sopenharmony_ci ret = wdriver->filter_callback(&wblock->dev, cmd, buf); 98762306a36Sopenharmony_ci module_put(wdriver->driver.owner); 98862306a36Sopenharmony_ci if (ret) 98962306a36Sopenharmony_ci goto out_ioctl; 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci /* return the result (only up to our internal buffer size) */ 99262306a36Sopenharmony_ci if (copy_to_user(input, buf, wblock->req_buf_size)) { 99362306a36Sopenharmony_ci dev_dbg(&wblock->dev.dev, "Copy %llu to user failed\n", 99462306a36Sopenharmony_ci wblock->req_buf_size); 99562306a36Sopenharmony_ci ret = -EFAULT; 99662306a36Sopenharmony_ci } 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ciout_ioctl: 99962306a36Sopenharmony_ci mutex_unlock(&wblock->char_mutex); 100062306a36Sopenharmony_ci return ret; 100162306a36Sopenharmony_ci} 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_cistatic const struct file_operations wmi_fops = { 100462306a36Sopenharmony_ci .owner = THIS_MODULE, 100562306a36Sopenharmony_ci .read = wmi_char_read, 100662306a36Sopenharmony_ci .open = wmi_char_open, 100762306a36Sopenharmony_ci .unlocked_ioctl = wmi_ioctl, 100862306a36Sopenharmony_ci .compat_ioctl = compat_ptr_ioctl, 100962306a36Sopenharmony_ci}; 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_cistatic int wmi_dev_probe(struct device *dev) 101262306a36Sopenharmony_ci{ 101362306a36Sopenharmony_ci struct wmi_block *wblock = dev_to_wblock(dev); 101462306a36Sopenharmony_ci struct wmi_driver *wdriver = drv_to_wdrv(dev->driver); 101562306a36Sopenharmony_ci int ret = 0; 101662306a36Sopenharmony_ci char *buf; 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci if (ACPI_FAILURE(wmi_method_enable(wblock, true))) 101962306a36Sopenharmony_ci dev_warn(dev, "failed to enable device -- probing anyway\n"); 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci if (wdriver->probe) { 102262306a36Sopenharmony_ci ret = wdriver->probe(dev_to_wdev(dev), 102362306a36Sopenharmony_ci find_guid_context(wblock, wdriver)); 102462306a36Sopenharmony_ci if (ret != 0) 102562306a36Sopenharmony_ci goto probe_failure; 102662306a36Sopenharmony_ci } 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci /* driver wants a character device made */ 102962306a36Sopenharmony_ci if (wdriver->filter_callback) { 103062306a36Sopenharmony_ci /* check that required buffer size declared by driver or MOF */ 103162306a36Sopenharmony_ci if (!wblock->req_buf_size) { 103262306a36Sopenharmony_ci dev_err(&wblock->dev.dev, 103362306a36Sopenharmony_ci "Required buffer size not set\n"); 103462306a36Sopenharmony_ci ret = -EINVAL; 103562306a36Sopenharmony_ci goto probe_failure; 103662306a36Sopenharmony_ci } 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci wblock->handler_data = kmalloc(wblock->req_buf_size, 103962306a36Sopenharmony_ci GFP_KERNEL); 104062306a36Sopenharmony_ci if (!wblock->handler_data) { 104162306a36Sopenharmony_ci ret = -ENOMEM; 104262306a36Sopenharmony_ci goto probe_failure; 104362306a36Sopenharmony_ci } 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ci buf = kasprintf(GFP_KERNEL, "wmi/%s", wdriver->driver.name); 104662306a36Sopenharmony_ci if (!buf) { 104762306a36Sopenharmony_ci ret = -ENOMEM; 104862306a36Sopenharmony_ci goto probe_string_failure; 104962306a36Sopenharmony_ci } 105062306a36Sopenharmony_ci wblock->char_dev.minor = MISC_DYNAMIC_MINOR; 105162306a36Sopenharmony_ci wblock->char_dev.name = buf; 105262306a36Sopenharmony_ci wblock->char_dev.fops = &wmi_fops; 105362306a36Sopenharmony_ci wblock->char_dev.mode = 0444; 105462306a36Sopenharmony_ci ret = misc_register(&wblock->char_dev); 105562306a36Sopenharmony_ci if (ret) { 105662306a36Sopenharmony_ci dev_warn(dev, "failed to register char dev: %d\n", ret); 105762306a36Sopenharmony_ci ret = -ENOMEM; 105862306a36Sopenharmony_ci goto probe_misc_failure; 105962306a36Sopenharmony_ci } 106062306a36Sopenharmony_ci } 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_ci set_bit(WMI_PROBED, &wblock->flags); 106362306a36Sopenharmony_ci return 0; 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ciprobe_misc_failure: 106662306a36Sopenharmony_ci kfree(buf); 106762306a36Sopenharmony_ciprobe_string_failure: 106862306a36Sopenharmony_ci kfree(wblock->handler_data); 106962306a36Sopenharmony_ciprobe_failure: 107062306a36Sopenharmony_ci if (ACPI_FAILURE(wmi_method_enable(wblock, false))) 107162306a36Sopenharmony_ci dev_warn(dev, "failed to disable device\n"); 107262306a36Sopenharmony_ci return ret; 107362306a36Sopenharmony_ci} 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_cistatic void wmi_dev_remove(struct device *dev) 107662306a36Sopenharmony_ci{ 107762306a36Sopenharmony_ci struct wmi_block *wblock = dev_to_wblock(dev); 107862306a36Sopenharmony_ci struct wmi_driver *wdriver = drv_to_wdrv(dev->driver); 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci clear_bit(WMI_PROBED, &wblock->flags); 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci if (wdriver->filter_callback) { 108362306a36Sopenharmony_ci misc_deregister(&wblock->char_dev); 108462306a36Sopenharmony_ci kfree(wblock->char_dev.name); 108562306a36Sopenharmony_ci kfree(wblock->handler_data); 108662306a36Sopenharmony_ci } 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci if (wdriver->remove) 108962306a36Sopenharmony_ci wdriver->remove(dev_to_wdev(dev)); 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci if (ACPI_FAILURE(wmi_method_enable(wblock, false))) 109262306a36Sopenharmony_ci dev_warn(dev, "failed to disable device\n"); 109362306a36Sopenharmony_ci} 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_cistatic struct class wmi_bus_class = { 109662306a36Sopenharmony_ci .name = "wmi_bus", 109762306a36Sopenharmony_ci}; 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_cistatic struct bus_type wmi_bus_type = { 110062306a36Sopenharmony_ci .name = "wmi", 110162306a36Sopenharmony_ci .dev_groups = wmi_groups, 110262306a36Sopenharmony_ci .match = wmi_dev_match, 110362306a36Sopenharmony_ci .uevent = wmi_dev_uevent, 110462306a36Sopenharmony_ci .probe = wmi_dev_probe, 110562306a36Sopenharmony_ci .remove = wmi_dev_remove, 110662306a36Sopenharmony_ci}; 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_cistatic const struct device_type wmi_type_event = { 110962306a36Sopenharmony_ci .name = "event", 111062306a36Sopenharmony_ci .groups = wmi_event_groups, 111162306a36Sopenharmony_ci .release = wmi_dev_release, 111262306a36Sopenharmony_ci}; 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_cistatic const struct device_type wmi_type_method = { 111562306a36Sopenharmony_ci .name = "method", 111662306a36Sopenharmony_ci .groups = wmi_method_groups, 111762306a36Sopenharmony_ci .release = wmi_dev_release, 111862306a36Sopenharmony_ci}; 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_cistatic const struct device_type wmi_type_data = { 112162306a36Sopenharmony_ci .name = "data", 112262306a36Sopenharmony_ci .groups = wmi_data_groups, 112362306a36Sopenharmony_ci .release = wmi_dev_release, 112462306a36Sopenharmony_ci}; 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci/* 112762306a36Sopenharmony_ci * _WDG is a static list that is only parsed at startup, 112862306a36Sopenharmony_ci * so it's safe to count entries without extra protection. 112962306a36Sopenharmony_ci */ 113062306a36Sopenharmony_cistatic int guid_count(const guid_t *guid) 113162306a36Sopenharmony_ci{ 113262306a36Sopenharmony_ci struct wmi_block *wblock; 113362306a36Sopenharmony_ci int count = 0; 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci list_for_each_entry(wblock, &wmi_block_list, list) { 113662306a36Sopenharmony_ci if (guid_equal(&wblock->gblock.guid, guid)) 113762306a36Sopenharmony_ci count++; 113862306a36Sopenharmony_ci } 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci return count; 114162306a36Sopenharmony_ci} 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_cistatic int wmi_create_device(struct device *wmi_bus_dev, 114462306a36Sopenharmony_ci struct wmi_block *wblock, 114562306a36Sopenharmony_ci struct acpi_device *device) 114662306a36Sopenharmony_ci{ 114762306a36Sopenharmony_ci struct acpi_device_info *info; 114862306a36Sopenharmony_ci char method[WMI_ACPI_METHOD_NAME_SIZE]; 114962306a36Sopenharmony_ci int result; 115062306a36Sopenharmony_ci uint count; 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci if (wblock->gblock.flags & ACPI_WMI_EVENT) { 115362306a36Sopenharmony_ci wblock->dev.dev.type = &wmi_type_event; 115462306a36Sopenharmony_ci goto out_init; 115562306a36Sopenharmony_ci } 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci if (wblock->gblock.flags & ACPI_WMI_METHOD) { 115862306a36Sopenharmony_ci wblock->dev.dev.type = &wmi_type_method; 115962306a36Sopenharmony_ci mutex_init(&wblock->char_mutex); 116062306a36Sopenharmony_ci goto out_init; 116162306a36Sopenharmony_ci } 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci /* 116462306a36Sopenharmony_ci * Data Block Query Control Method (WQxx by convention) is 116562306a36Sopenharmony_ci * required per the WMI documentation. If it is not present, 116662306a36Sopenharmony_ci * we ignore this data block. 116762306a36Sopenharmony_ci */ 116862306a36Sopenharmony_ci get_acpi_method_name(wblock, 'Q', method); 116962306a36Sopenharmony_ci result = get_subobj_info(device->handle, method, &info); 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_ci if (result) { 117262306a36Sopenharmony_ci dev_warn(wmi_bus_dev, 117362306a36Sopenharmony_ci "%s data block query control method not found\n", 117462306a36Sopenharmony_ci method); 117562306a36Sopenharmony_ci return result; 117662306a36Sopenharmony_ci } 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci wblock->dev.dev.type = &wmi_type_data; 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci /* 118162306a36Sopenharmony_ci * The Microsoft documentation specifically states: 118262306a36Sopenharmony_ci * 118362306a36Sopenharmony_ci * Data blocks registered with only a single instance 118462306a36Sopenharmony_ci * can ignore the parameter. 118562306a36Sopenharmony_ci * 118662306a36Sopenharmony_ci * ACPICA will get mad at us if we call the method with the wrong number 118762306a36Sopenharmony_ci * of arguments, so check what our method expects. (On some Dell 118862306a36Sopenharmony_ci * laptops, WQxx may not be a method at all.) 118962306a36Sopenharmony_ci */ 119062306a36Sopenharmony_ci if (info->type != ACPI_TYPE_METHOD || info->param_count == 0) 119162306a36Sopenharmony_ci set_bit(WMI_READ_TAKES_NO_ARGS, &wblock->flags); 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci kfree(info); 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci get_acpi_method_name(wblock, 'S', method); 119662306a36Sopenharmony_ci result = get_subobj_info(device->handle, method, NULL); 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_ci if (result == 0) 119962306a36Sopenharmony_ci wblock->dev.setable = true; 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_ci out_init: 120262306a36Sopenharmony_ci wblock->dev.dev.bus = &wmi_bus_type; 120362306a36Sopenharmony_ci wblock->dev.dev.parent = wmi_bus_dev; 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci count = guid_count(&wblock->gblock.guid); 120662306a36Sopenharmony_ci if (count) 120762306a36Sopenharmony_ci dev_set_name(&wblock->dev.dev, "%pUL-%d", &wblock->gblock.guid, count); 120862306a36Sopenharmony_ci else 120962306a36Sopenharmony_ci dev_set_name(&wblock->dev.dev, "%pUL", &wblock->gblock.guid); 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci device_initialize(&wblock->dev.dev); 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci return 0; 121462306a36Sopenharmony_ci} 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_cistatic void wmi_free_devices(struct acpi_device *device) 121762306a36Sopenharmony_ci{ 121862306a36Sopenharmony_ci struct wmi_block *wblock, *next; 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci /* Delete devices for all the GUIDs */ 122162306a36Sopenharmony_ci list_for_each_entry_safe(wblock, next, &wmi_block_list, list) { 122262306a36Sopenharmony_ci if (wblock->acpi_device == device) { 122362306a36Sopenharmony_ci list_del(&wblock->list); 122462306a36Sopenharmony_ci device_unregister(&wblock->dev.dev); 122562306a36Sopenharmony_ci } 122662306a36Sopenharmony_ci } 122762306a36Sopenharmony_ci} 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_cistatic bool guid_already_parsed_for_legacy(struct acpi_device *device, const guid_t *guid) 123062306a36Sopenharmony_ci{ 123162306a36Sopenharmony_ci struct wmi_block *wblock; 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_ci list_for_each_entry(wblock, &wmi_block_list, list) { 123462306a36Sopenharmony_ci /* skip warning and register if we know the driver will use struct wmi_driver */ 123562306a36Sopenharmony_ci for (int i = 0; allow_duplicates[i] != NULL; i++) { 123662306a36Sopenharmony_ci if (guid_parse_and_compare(allow_duplicates[i], guid)) 123762306a36Sopenharmony_ci return false; 123862306a36Sopenharmony_ci } 123962306a36Sopenharmony_ci if (guid_equal(&wblock->gblock.guid, guid)) { 124062306a36Sopenharmony_ci /* 124162306a36Sopenharmony_ci * Because we historically didn't track the relationship 124262306a36Sopenharmony_ci * between GUIDs and ACPI nodes, we don't know whether 124362306a36Sopenharmony_ci * we need to suppress GUIDs that are unique on a 124462306a36Sopenharmony_ci * given node but duplicated across nodes. 124562306a36Sopenharmony_ci */ 124662306a36Sopenharmony_ci dev_warn(&device->dev, "duplicate WMI GUID %pUL (first instance was on %s)\n", 124762306a36Sopenharmony_ci guid, dev_name(&wblock->acpi_device->dev)); 124862306a36Sopenharmony_ci return true; 124962306a36Sopenharmony_ci } 125062306a36Sopenharmony_ci } 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_ci return false; 125362306a36Sopenharmony_ci} 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_ci/* 125662306a36Sopenharmony_ci * Parse the _WDG method for the GUID data blocks 125762306a36Sopenharmony_ci */ 125862306a36Sopenharmony_cistatic int parse_wdg(struct device *wmi_bus_dev, struct acpi_device *device) 125962306a36Sopenharmony_ci{ 126062306a36Sopenharmony_ci struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL}; 126162306a36Sopenharmony_ci const struct guid_block *gblock; 126262306a36Sopenharmony_ci struct wmi_block *wblock, *next; 126362306a36Sopenharmony_ci union acpi_object *obj; 126462306a36Sopenharmony_ci acpi_status status; 126562306a36Sopenharmony_ci u32 i, total; 126662306a36Sopenharmony_ci int retval; 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_ci status = acpi_evaluate_object(device->handle, "_WDG", NULL, &out); 126962306a36Sopenharmony_ci if (ACPI_FAILURE(status)) 127062306a36Sopenharmony_ci return -ENXIO; 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_ci obj = out.pointer; 127362306a36Sopenharmony_ci if (!obj) 127462306a36Sopenharmony_ci return -ENXIO; 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_ci if (obj->type != ACPI_TYPE_BUFFER) { 127762306a36Sopenharmony_ci kfree(obj); 127862306a36Sopenharmony_ci return -ENXIO; 127962306a36Sopenharmony_ci } 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_ci gblock = (const struct guid_block *)obj->buffer.pointer; 128262306a36Sopenharmony_ci total = obj->buffer.length / sizeof(struct guid_block); 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci for (i = 0; i < total; i++) { 128562306a36Sopenharmony_ci if (debug_dump_wdg) 128662306a36Sopenharmony_ci wmi_dump_wdg(&gblock[i]); 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_ci if (!gblock[i].instance_count) { 128962306a36Sopenharmony_ci dev_info(wmi_bus_dev, FW_INFO "%pUL has zero instances\n", &gblock[i].guid); 129062306a36Sopenharmony_ci continue; 129162306a36Sopenharmony_ci } 129262306a36Sopenharmony_ci 129362306a36Sopenharmony_ci if (guid_already_parsed_for_legacy(device, &gblock[i].guid)) 129462306a36Sopenharmony_ci continue; 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_ci wblock = kzalloc(sizeof(*wblock), GFP_KERNEL); 129762306a36Sopenharmony_ci if (!wblock) { 129862306a36Sopenharmony_ci dev_err(wmi_bus_dev, "Failed to allocate %pUL\n", &gblock[i].guid); 129962306a36Sopenharmony_ci continue; 130062306a36Sopenharmony_ci } 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_ci wblock->acpi_device = device; 130362306a36Sopenharmony_ci wblock->gblock = gblock[i]; 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_ci retval = wmi_create_device(wmi_bus_dev, wblock, device); 130662306a36Sopenharmony_ci if (retval) { 130762306a36Sopenharmony_ci kfree(wblock); 130862306a36Sopenharmony_ci continue; 130962306a36Sopenharmony_ci } 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_ci list_add_tail(&wblock->list, &wmi_block_list); 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_ci if (debug_event) { 131462306a36Sopenharmony_ci wblock->handler = wmi_notify_debug; 131562306a36Sopenharmony_ci wmi_method_enable(wblock, true); 131662306a36Sopenharmony_ci } 131762306a36Sopenharmony_ci } 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_ci /* 132062306a36Sopenharmony_ci * Now that all of the devices are created, add them to the 132162306a36Sopenharmony_ci * device tree and probe subdrivers. 132262306a36Sopenharmony_ci */ 132362306a36Sopenharmony_ci list_for_each_entry_safe(wblock, next, &wmi_block_list, list) { 132462306a36Sopenharmony_ci if (wblock->acpi_device != device) 132562306a36Sopenharmony_ci continue; 132662306a36Sopenharmony_ci 132762306a36Sopenharmony_ci retval = device_add(&wblock->dev.dev); 132862306a36Sopenharmony_ci if (retval) { 132962306a36Sopenharmony_ci dev_err(wmi_bus_dev, "failed to register %pUL\n", 133062306a36Sopenharmony_ci &wblock->gblock.guid); 133162306a36Sopenharmony_ci if (debug_event) 133262306a36Sopenharmony_ci wmi_method_enable(wblock, false); 133362306a36Sopenharmony_ci list_del(&wblock->list); 133462306a36Sopenharmony_ci put_device(&wblock->dev.dev); 133562306a36Sopenharmony_ci } 133662306a36Sopenharmony_ci } 133762306a36Sopenharmony_ci 133862306a36Sopenharmony_ci kfree(obj); 133962306a36Sopenharmony_ci 134062306a36Sopenharmony_ci return 0; 134162306a36Sopenharmony_ci} 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_ci/* 134462306a36Sopenharmony_ci * WMI can have EmbeddedControl access regions. In which case, we just want to 134562306a36Sopenharmony_ci * hand these off to the EC driver. 134662306a36Sopenharmony_ci */ 134762306a36Sopenharmony_cistatic acpi_status 134862306a36Sopenharmony_ciacpi_wmi_ec_space_handler(u32 function, acpi_physical_address address, 134962306a36Sopenharmony_ci u32 bits, u64 *value, 135062306a36Sopenharmony_ci void *handler_context, void *region_context) 135162306a36Sopenharmony_ci{ 135262306a36Sopenharmony_ci int result = 0, i = 0; 135362306a36Sopenharmony_ci u8 temp = 0; 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci if ((address > 0xFF) || !value) 135662306a36Sopenharmony_ci return AE_BAD_PARAMETER; 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ci if (function != ACPI_READ && function != ACPI_WRITE) 135962306a36Sopenharmony_ci return AE_BAD_PARAMETER; 136062306a36Sopenharmony_ci 136162306a36Sopenharmony_ci if (bits != 8) 136262306a36Sopenharmony_ci return AE_BAD_PARAMETER; 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_ci if (function == ACPI_READ) { 136562306a36Sopenharmony_ci result = ec_read(address, &temp); 136662306a36Sopenharmony_ci (*value) |= ((u64)temp) << i; 136762306a36Sopenharmony_ci } else { 136862306a36Sopenharmony_ci temp = 0xff & ((*value) >> i); 136962306a36Sopenharmony_ci result = ec_write(address, temp); 137062306a36Sopenharmony_ci } 137162306a36Sopenharmony_ci 137262306a36Sopenharmony_ci switch (result) { 137362306a36Sopenharmony_ci case -EINVAL: 137462306a36Sopenharmony_ci return AE_BAD_PARAMETER; 137562306a36Sopenharmony_ci case -ENODEV: 137662306a36Sopenharmony_ci return AE_NOT_FOUND; 137762306a36Sopenharmony_ci case -ETIME: 137862306a36Sopenharmony_ci return AE_TIME; 137962306a36Sopenharmony_ci default: 138062306a36Sopenharmony_ci return AE_OK; 138162306a36Sopenharmony_ci } 138262306a36Sopenharmony_ci} 138362306a36Sopenharmony_ci 138462306a36Sopenharmony_cistatic void acpi_wmi_notify_handler(acpi_handle handle, u32 event, 138562306a36Sopenharmony_ci void *context) 138662306a36Sopenharmony_ci{ 138762306a36Sopenharmony_ci struct wmi_block *wblock = NULL, *iter; 138862306a36Sopenharmony_ci 138962306a36Sopenharmony_ci list_for_each_entry(iter, &wmi_block_list, list) { 139062306a36Sopenharmony_ci struct guid_block *block = &iter->gblock; 139162306a36Sopenharmony_ci 139262306a36Sopenharmony_ci if (iter->acpi_device->handle == handle && 139362306a36Sopenharmony_ci (block->flags & ACPI_WMI_EVENT) && 139462306a36Sopenharmony_ci (block->notify_id == event)) { 139562306a36Sopenharmony_ci wblock = iter; 139662306a36Sopenharmony_ci break; 139762306a36Sopenharmony_ci } 139862306a36Sopenharmony_ci } 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_ci if (!wblock) 140162306a36Sopenharmony_ci return; 140262306a36Sopenharmony_ci 140362306a36Sopenharmony_ci /* If a driver is bound, then notify the driver. */ 140462306a36Sopenharmony_ci if (test_bit(WMI_PROBED, &wblock->flags) && wblock->dev.dev.driver) { 140562306a36Sopenharmony_ci struct wmi_driver *driver = drv_to_wdrv(wblock->dev.dev.driver); 140662306a36Sopenharmony_ci struct acpi_buffer evdata = { ACPI_ALLOCATE_BUFFER, NULL }; 140762306a36Sopenharmony_ci acpi_status status; 140862306a36Sopenharmony_ci 140962306a36Sopenharmony_ci if (!driver->no_notify_data) { 141062306a36Sopenharmony_ci status = get_event_data(wblock, &evdata); 141162306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 141262306a36Sopenharmony_ci dev_warn(&wblock->dev.dev, "failed to get event data\n"); 141362306a36Sopenharmony_ci return; 141462306a36Sopenharmony_ci } 141562306a36Sopenharmony_ci } 141662306a36Sopenharmony_ci 141762306a36Sopenharmony_ci if (driver->notify) 141862306a36Sopenharmony_ci driver->notify(&wblock->dev, evdata.pointer); 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_ci kfree(evdata.pointer); 142162306a36Sopenharmony_ci } else if (wblock->handler) { 142262306a36Sopenharmony_ci /* Legacy handler */ 142362306a36Sopenharmony_ci wblock->handler(event, wblock->handler_data); 142462306a36Sopenharmony_ci } 142562306a36Sopenharmony_ci 142662306a36Sopenharmony_ci if (debug_event) 142762306a36Sopenharmony_ci pr_info("DEBUG: GUID %pUL event 0x%02X\n", &wblock->gblock.guid, event); 142862306a36Sopenharmony_ci 142962306a36Sopenharmony_ci acpi_bus_generate_netlink_event( 143062306a36Sopenharmony_ci wblock->acpi_device->pnp.device_class, 143162306a36Sopenharmony_ci dev_name(&wblock->dev.dev), 143262306a36Sopenharmony_ci event, 0); 143362306a36Sopenharmony_ci} 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_cistatic void acpi_wmi_remove(struct platform_device *device) 143662306a36Sopenharmony_ci{ 143762306a36Sopenharmony_ci struct acpi_device *acpi_device = ACPI_COMPANION(&device->dev); 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_ci acpi_remove_notify_handler(acpi_device->handle, ACPI_ALL_NOTIFY, 144062306a36Sopenharmony_ci acpi_wmi_notify_handler); 144162306a36Sopenharmony_ci acpi_remove_address_space_handler(acpi_device->handle, 144262306a36Sopenharmony_ci ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler); 144362306a36Sopenharmony_ci wmi_free_devices(acpi_device); 144462306a36Sopenharmony_ci device_unregister(dev_get_drvdata(&device->dev)); 144562306a36Sopenharmony_ci} 144662306a36Sopenharmony_ci 144762306a36Sopenharmony_cistatic int acpi_wmi_probe(struct platform_device *device) 144862306a36Sopenharmony_ci{ 144962306a36Sopenharmony_ci struct acpi_device *acpi_device; 145062306a36Sopenharmony_ci struct device *wmi_bus_dev; 145162306a36Sopenharmony_ci acpi_status status; 145262306a36Sopenharmony_ci int error; 145362306a36Sopenharmony_ci 145462306a36Sopenharmony_ci acpi_device = ACPI_COMPANION(&device->dev); 145562306a36Sopenharmony_ci if (!acpi_device) { 145662306a36Sopenharmony_ci dev_err(&device->dev, "ACPI companion is missing\n"); 145762306a36Sopenharmony_ci return -ENODEV; 145862306a36Sopenharmony_ci } 145962306a36Sopenharmony_ci 146062306a36Sopenharmony_ci status = acpi_install_address_space_handler(acpi_device->handle, 146162306a36Sopenharmony_ci ACPI_ADR_SPACE_EC, 146262306a36Sopenharmony_ci &acpi_wmi_ec_space_handler, 146362306a36Sopenharmony_ci NULL, NULL); 146462306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 146562306a36Sopenharmony_ci dev_err(&device->dev, "Error installing EC region handler\n"); 146662306a36Sopenharmony_ci return -ENODEV; 146762306a36Sopenharmony_ci } 146862306a36Sopenharmony_ci 146962306a36Sopenharmony_ci status = acpi_install_notify_handler(acpi_device->handle, 147062306a36Sopenharmony_ci ACPI_ALL_NOTIFY, 147162306a36Sopenharmony_ci acpi_wmi_notify_handler, 147262306a36Sopenharmony_ci NULL); 147362306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 147462306a36Sopenharmony_ci dev_err(&device->dev, "Error installing notify handler\n"); 147562306a36Sopenharmony_ci error = -ENODEV; 147662306a36Sopenharmony_ci goto err_remove_ec_handler; 147762306a36Sopenharmony_ci } 147862306a36Sopenharmony_ci 147962306a36Sopenharmony_ci wmi_bus_dev = device_create(&wmi_bus_class, &device->dev, MKDEV(0, 0), 148062306a36Sopenharmony_ci NULL, "wmi_bus-%s", dev_name(&device->dev)); 148162306a36Sopenharmony_ci if (IS_ERR(wmi_bus_dev)) { 148262306a36Sopenharmony_ci error = PTR_ERR(wmi_bus_dev); 148362306a36Sopenharmony_ci goto err_remove_notify_handler; 148462306a36Sopenharmony_ci } 148562306a36Sopenharmony_ci dev_set_drvdata(&device->dev, wmi_bus_dev); 148662306a36Sopenharmony_ci 148762306a36Sopenharmony_ci error = parse_wdg(wmi_bus_dev, acpi_device); 148862306a36Sopenharmony_ci if (error) { 148962306a36Sopenharmony_ci pr_err("Failed to parse WDG method\n"); 149062306a36Sopenharmony_ci goto err_remove_busdev; 149162306a36Sopenharmony_ci } 149262306a36Sopenharmony_ci 149362306a36Sopenharmony_ci return 0; 149462306a36Sopenharmony_ci 149562306a36Sopenharmony_cierr_remove_busdev: 149662306a36Sopenharmony_ci device_unregister(wmi_bus_dev); 149762306a36Sopenharmony_ci 149862306a36Sopenharmony_cierr_remove_notify_handler: 149962306a36Sopenharmony_ci acpi_remove_notify_handler(acpi_device->handle, ACPI_ALL_NOTIFY, 150062306a36Sopenharmony_ci acpi_wmi_notify_handler); 150162306a36Sopenharmony_ci 150262306a36Sopenharmony_cierr_remove_ec_handler: 150362306a36Sopenharmony_ci acpi_remove_address_space_handler(acpi_device->handle, 150462306a36Sopenharmony_ci ACPI_ADR_SPACE_EC, 150562306a36Sopenharmony_ci &acpi_wmi_ec_space_handler); 150662306a36Sopenharmony_ci 150762306a36Sopenharmony_ci return error; 150862306a36Sopenharmony_ci} 150962306a36Sopenharmony_ci 151062306a36Sopenharmony_ciint __must_check __wmi_driver_register(struct wmi_driver *driver, 151162306a36Sopenharmony_ci struct module *owner) 151262306a36Sopenharmony_ci{ 151362306a36Sopenharmony_ci driver->driver.owner = owner; 151462306a36Sopenharmony_ci driver->driver.bus = &wmi_bus_type; 151562306a36Sopenharmony_ci 151662306a36Sopenharmony_ci return driver_register(&driver->driver); 151762306a36Sopenharmony_ci} 151862306a36Sopenharmony_ciEXPORT_SYMBOL(__wmi_driver_register); 151962306a36Sopenharmony_ci 152062306a36Sopenharmony_ci/** 152162306a36Sopenharmony_ci * wmi_driver_unregister() - Unregister a WMI driver 152262306a36Sopenharmony_ci * @driver: WMI driver to unregister 152362306a36Sopenharmony_ci * 152462306a36Sopenharmony_ci * Unregisters a WMI driver from the WMI bus. 152562306a36Sopenharmony_ci */ 152662306a36Sopenharmony_civoid wmi_driver_unregister(struct wmi_driver *driver) 152762306a36Sopenharmony_ci{ 152862306a36Sopenharmony_ci driver_unregister(&driver->driver); 152962306a36Sopenharmony_ci} 153062306a36Sopenharmony_ciEXPORT_SYMBOL(wmi_driver_unregister); 153162306a36Sopenharmony_ci 153262306a36Sopenharmony_cistatic struct platform_driver acpi_wmi_driver = { 153362306a36Sopenharmony_ci .driver = { 153462306a36Sopenharmony_ci .name = "acpi-wmi", 153562306a36Sopenharmony_ci .acpi_match_table = wmi_device_ids, 153662306a36Sopenharmony_ci }, 153762306a36Sopenharmony_ci .probe = acpi_wmi_probe, 153862306a36Sopenharmony_ci .remove_new = acpi_wmi_remove, 153962306a36Sopenharmony_ci}; 154062306a36Sopenharmony_ci 154162306a36Sopenharmony_cistatic int __init acpi_wmi_init(void) 154262306a36Sopenharmony_ci{ 154362306a36Sopenharmony_ci int error; 154462306a36Sopenharmony_ci 154562306a36Sopenharmony_ci if (acpi_disabled) 154662306a36Sopenharmony_ci return -ENODEV; 154762306a36Sopenharmony_ci 154862306a36Sopenharmony_ci error = class_register(&wmi_bus_class); 154962306a36Sopenharmony_ci if (error) 155062306a36Sopenharmony_ci return error; 155162306a36Sopenharmony_ci 155262306a36Sopenharmony_ci error = bus_register(&wmi_bus_type); 155362306a36Sopenharmony_ci if (error) 155462306a36Sopenharmony_ci goto err_unreg_class; 155562306a36Sopenharmony_ci 155662306a36Sopenharmony_ci error = platform_driver_register(&acpi_wmi_driver); 155762306a36Sopenharmony_ci if (error) { 155862306a36Sopenharmony_ci pr_err("Error loading mapper\n"); 155962306a36Sopenharmony_ci goto err_unreg_bus; 156062306a36Sopenharmony_ci } 156162306a36Sopenharmony_ci 156262306a36Sopenharmony_ci return 0; 156362306a36Sopenharmony_ci 156462306a36Sopenharmony_cierr_unreg_bus: 156562306a36Sopenharmony_ci bus_unregister(&wmi_bus_type); 156662306a36Sopenharmony_ci 156762306a36Sopenharmony_cierr_unreg_class: 156862306a36Sopenharmony_ci class_unregister(&wmi_bus_class); 156962306a36Sopenharmony_ci 157062306a36Sopenharmony_ci return error; 157162306a36Sopenharmony_ci} 157262306a36Sopenharmony_ci 157362306a36Sopenharmony_cistatic void __exit acpi_wmi_exit(void) 157462306a36Sopenharmony_ci{ 157562306a36Sopenharmony_ci platform_driver_unregister(&acpi_wmi_driver); 157662306a36Sopenharmony_ci bus_unregister(&wmi_bus_type); 157762306a36Sopenharmony_ci class_unregister(&wmi_bus_class); 157862306a36Sopenharmony_ci} 157962306a36Sopenharmony_ci 158062306a36Sopenharmony_cisubsys_initcall_sync(acpi_wmi_init); 158162306a36Sopenharmony_cimodule_exit(acpi_wmi_exit); 1582