11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 2b4f9fe12SLen Brown /* 3b4f9fe12SLen Brown * ACPI-WMI mapping driver 4b4f9fe12SLen Brown * 5b4f9fe12SLen Brown * Copyright (C) 2007-2008 Carlos Corbacho <carlos@strangeworlds.co.uk> 6b4f9fe12SLen Brown * 7b4f9fe12SLen Brown * GUID parsing code from ldm.c is: 8b4f9fe12SLen Brown * Copyright (C) 2001,2002 Richard Russon <ldm@flatcap.org> 9b4f9fe12SLen Brown * Copyright (c) 2001-2007 Anton Altaparmakov 10b4f9fe12SLen Brown * Copyright (C) 2001,2002 Jakob Kemi <jakob.kemi@telia.com> 11b4f9fe12SLen Brown * 122c9c5664SDarren Hart (VMware) * WMI bus infrastructure by Andrew Lutomirski and Darren Hart: 132c9c5664SDarren Hart (VMware) * Copyright (C) 2015 Andrew Lutomirski 142c9c5664SDarren Hart (VMware) * Copyright (C) 2017 VMware, Inc. All Rights Reserved. 15b4f9fe12SLen Brown */ 16b4f9fe12SLen Brown 178e07514dSDmitry Torokhov #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 188e07514dSDmitry Torokhov 19b4f9fe12SLen Brown #include <linux/acpi.h> 201c95ace7SBarnabás Pőcze #include <linux/bits.h> 21dea878d8SBarnabás Pőcze #include <linux/build_bug.h> 22b60ee4e0SMario Limonciello #include <linux/device.h> 23b60ee4e0SMario Limonciello #include <linux/init.h> 24b60ee4e0SMario Limonciello #include <linux/kernel.h> 25b60ee4e0SMario Limonciello #include <linux/list.h> 2644b6b766SMario Limonciello #include <linux/miscdevice.h> 277c52d551SPaul Gortmaker #include <linux/module.h> 289599ed91SAndy Lutomirski #include <linux/platform_device.h> 29b60ee4e0SMario Limonciello #include <linux/slab.h> 306133913aSBarnabás Pőcze #include <linux/sysfs.h> 31b60ee4e0SMario Limonciello #include <linux/types.h> 3244b6b766SMario Limonciello #include <linux/uaccess.h> 33538d7eb8SAndy Shevchenko #include <linux/uuid.h> 34b60ee4e0SMario Limonciello #include <linux/wmi.h> 35df23e2beSPeter Zijlstra #include <linux/fs.h> 3644b6b766SMario Limonciello #include <uapi/linux/wmi.h> 37b4f9fe12SLen Brown 38b4f9fe12SLen Brown MODULE_AUTHOR("Carlos Corbacho"); 39b4f9fe12SLen Brown MODULE_DESCRIPTION("ACPI-WMI Mapping Driver"); 40b4f9fe12SLen Brown MODULE_LICENSE("GPL"); 41b4f9fe12SLen Brown 42762e1a2fSDmitry Torokhov static LIST_HEAD(wmi_block_list); 43b4f9fe12SLen Brown 44b4f9fe12SLen Brown struct guid_block { 4567f472fdSBarnabás Pőcze guid_t guid; 46b4f9fe12SLen Brown union { 47b4f9fe12SLen Brown char object_id[2]; 48b4f9fe12SLen Brown struct { 49b4f9fe12SLen Brown unsigned char notify_id; 50b4f9fe12SLen Brown unsigned char reserved; 51b4f9fe12SLen Brown }; 52b4f9fe12SLen Brown }; 53b4f9fe12SLen Brown u8 instance_count; 54b4f9fe12SLen Brown u8 flags; 55dea878d8SBarnabás Pőcze } __packed; 56dea878d8SBarnabás Pőcze static_assert(sizeof(typeof_member(struct guid_block, guid)) == 16); 57dea878d8SBarnabás Pőcze static_assert(sizeof(struct guid_block) == 20); 58dea878d8SBarnabás Pőcze static_assert(__alignof__(struct guid_block) == 1); 59b4f9fe12SLen Brown 60a90b38c5SHans de Goede enum { /* wmi_block flags */ 61a90b38c5SHans de Goede WMI_READ_TAKES_NO_ARGS, 6299188786SHans de Goede WMI_PROBED, 63a90b38c5SHans de Goede }; 64a90b38c5SHans de Goede 65b4f9fe12SLen Brown struct wmi_block { 66844af950SAndy Lutomirski struct wmi_device dev; 67b4f9fe12SLen Brown struct list_head list; 68b4f9fe12SLen Brown struct guid_block gblock; 6944b6b766SMario Limonciello struct miscdevice char_dev; 7044b6b766SMario Limonciello struct mutex char_mutex; 71b0e86302SAndy Lutomirski struct acpi_device *acpi_device; 72b4f9fe12SLen Brown wmi_notify_handler handler; 73b4f9fe12SLen Brown void *handler_data; 7444b6b766SMario Limonciello u64 req_buf_size; 75a90b38c5SHans de Goede unsigned long flags; 76b4f9fe12SLen Brown }; 77b4f9fe12SLen Brown 78b4f9fe12SLen Brown 79b4f9fe12SLen Brown /* 80b4f9fe12SLen Brown * If the GUID data block is marked as expensive, we must enable and 81b4f9fe12SLen Brown * explicitily disable data collection. 82b4f9fe12SLen Brown */ 831c95ace7SBarnabás Pőcze #define ACPI_WMI_EXPENSIVE BIT(0) 841c95ace7SBarnabás Pőcze #define ACPI_WMI_METHOD BIT(1) /* GUID is a method */ 851c95ace7SBarnabás Pőcze #define ACPI_WMI_STRING BIT(2) /* GUID takes & returns a string */ 861c95ace7SBarnabás Pőcze #define ACPI_WMI_EVENT BIT(3) /* GUID is an event */ 87b4f9fe12SLen Brown 8890ab5ee9SRusty Russell static bool debug_event; 89fc3155b2SThomas Renninger module_param(debug_event, bool, 0444); 90fc3155b2SThomas Renninger MODULE_PARM_DESC(debug_event, 91fc3155b2SThomas Renninger "Log WMI Events [0/1]"); 92fc3155b2SThomas Renninger 9390ab5ee9SRusty Russell static bool debug_dump_wdg; 94a929aae0SThomas Renninger module_param(debug_dump_wdg, bool, 0444); 95a929aae0SThomas Renninger MODULE_PARM_DESC(debug_dump_wdg, 96a929aae0SThomas Renninger "Dump available WMI interfaces [0/1]"); 97a929aae0SThomas Renninger 98b4f9fe12SLen Brown static const struct acpi_device_id wmi_device_ids[] = { 99b4f9fe12SLen Brown {"PNP0C14", 0}, 100b4f9fe12SLen Brown {"pnp0c14", 0}, 1019bf9ca95SBarnabás Pőcze { } 102b4f9fe12SLen Brown }; 103b4f9fe12SLen Brown MODULE_DEVICE_TABLE(acpi, wmi_device_ids); 104b4f9fe12SLen Brown 105134038b0SMario Limonciello /* allow duplicate GUIDs as these device drivers use struct wmi_driver */ 106134038b0SMario Limonciello static const char * const allow_duplicates[] = { 107134038b0SMario Limonciello "05901221-D566-11D1-B2F0-00A0C9062910", /* wmi-bmof */ 108a77272c1SArmin Wolf "8A42EA14-4F2A-FD45-6422-0087F7A7E608", /* dell-wmi-ddv */ 109134038b0SMario Limonciello NULL 110134038b0SMario Limonciello }; 111134038b0SMario Limonciello 112b4f9fe12SLen Brown /* 113b4f9fe12SLen Brown * GUID parsing functions 114b4f9fe12SLen Brown */ 115b4f9fe12SLen Brown 116b0179b80SBarnabás Pőcze static acpi_status find_guid(const char *guid_string, struct wmi_block **out) 117b4f9fe12SLen Brown { 118f9dffc14SAndy Shevchenko guid_t guid_input; 119b4f9fe12SLen Brown struct wmi_block *wblock; 120b4f9fe12SLen Brown 121b0179b80SBarnabás Pőcze if (!guid_string) 122b0179b80SBarnabás Pőcze return AE_BAD_PARAMETER; 123b0179b80SBarnabás Pőcze 124f9dffc14SAndy Shevchenko if (guid_parse(guid_string, &guid_input)) 125b0179b80SBarnabás Pőcze return AE_BAD_PARAMETER; 126b4f9fe12SLen Brown 127cedb3b2aSAndy Shevchenko list_for_each_entry(wblock, &wmi_block_list, list) { 1281ce69d2bSBarnabás Pőcze if (guid_equal(&wblock->gblock.guid, &guid_input)) { 129b4f9fe12SLen Brown if (out) 130b4f9fe12SLen Brown *out = wblock; 131b0179b80SBarnabás Pőcze 132b0179b80SBarnabás Pőcze return AE_OK; 133b4f9fe12SLen Brown } 134b4f9fe12SLen Brown } 135b0179b80SBarnabás Pőcze 136b0179b80SBarnabás Pőcze return AE_NOT_FOUND; 137b4f9fe12SLen Brown } 138b4f9fe12SLen Brown 139028e6e20SAndy Shevchenko static bool guid_parse_and_compare(const char *string, const guid_t *guid) 140028e6e20SAndy Shevchenko { 141028e6e20SAndy Shevchenko guid_t guid_input; 142028e6e20SAndy Shevchenko 143028e6e20SAndy Shevchenko if (guid_parse(string, &guid_input)) 144028e6e20SAndy Shevchenko return false; 145028e6e20SAndy Shevchenko 146028e6e20SAndy Shevchenko return guid_equal(&guid_input, guid); 147028e6e20SAndy Shevchenko } 148028e6e20SAndy Shevchenko 149a48e2338SMattias Jacobsson static const void *find_guid_context(struct wmi_block *wblock, 150a48e2338SMattias Jacobsson struct wmi_driver *wdriver) 151a48e2338SMattias Jacobsson { 152a48e2338SMattias Jacobsson const struct wmi_device_id *id; 153a48e2338SMattias Jacobsson 1546e0bc588SBarnabás Pőcze id = wdriver->id_table; 1556e0bc588SBarnabás Pőcze if (!id) 156a48e2338SMattias Jacobsson return NULL; 157a48e2338SMattias Jacobsson 158a48e2338SMattias Jacobsson while (*id->guid_string) { 159028e6e20SAndy Shevchenko if (guid_parse_and_compare(id->guid_string, &wblock->gblock.guid)) 160a48e2338SMattias Jacobsson return id->context; 161a48e2338SMattias Jacobsson id++; 162a48e2338SMattias Jacobsson } 163a48e2338SMattias Jacobsson return NULL; 164a48e2338SMattias Jacobsson } 165a48e2338SMattias Jacobsson 166d4fc91adSAndy Lutomirski static int get_subobj_info(acpi_handle handle, const char *pathname, 167d4fc91adSAndy Lutomirski struct acpi_device_info **info) 168d4fc91adSAndy Lutomirski { 169d4fc91adSAndy Lutomirski struct acpi_device_info *dummy_info, **info_ptr; 170d4fc91adSAndy Lutomirski acpi_handle subobj_handle; 171d4fc91adSAndy Lutomirski acpi_status status; 172d4fc91adSAndy Lutomirski 173d4fc91adSAndy Lutomirski status = acpi_get_handle(handle, (char *)pathname, &subobj_handle); 174d4fc91adSAndy Lutomirski if (status == AE_NOT_FOUND) 175d4fc91adSAndy Lutomirski return -ENOENT; 176d4fc91adSAndy Lutomirski else if (ACPI_FAILURE(status)) 177d4fc91adSAndy Lutomirski return -EIO; 178d4fc91adSAndy Lutomirski 179d4fc91adSAndy Lutomirski info_ptr = info ? info : &dummy_info; 180d4fc91adSAndy Lutomirski status = acpi_get_object_info(subobj_handle, info_ptr); 181d4fc91adSAndy Lutomirski if (ACPI_FAILURE(status)) 182d4fc91adSAndy Lutomirski return -EIO; 183d4fc91adSAndy Lutomirski 184d4fc91adSAndy Lutomirski if (!info) 185d4fc91adSAndy Lutomirski kfree(dummy_info); 186d4fc91adSAndy Lutomirski 187d4fc91adSAndy Lutomirski return 0; 188d4fc91adSAndy Lutomirski } 189d4fc91adSAndy Lutomirski 190285dd01aSBarnabás Pőcze static acpi_status wmi_method_enable(struct wmi_block *wblock, bool enable) 191b4f9fe12SLen Brown { 19243aacf83SBarnabás Pőcze struct guid_block *block; 193b4f9fe12SLen Brown char method[5]; 194b4f9fe12SLen Brown acpi_status status; 195b4f9fe12SLen Brown acpi_handle handle; 196b4f9fe12SLen Brown 197b4f9fe12SLen Brown block = &wblock->gblock; 198b0e86302SAndy Lutomirski handle = wblock->acpi_device->handle; 199b4f9fe12SLen Brown 200b4f9fe12SLen Brown snprintf(method, 5, "WE%02X", block->notify_id); 2018122ab66SZhang Rui status = acpi_execute_simple_method(handle, method, enable); 202736b48aaSBarnabás Pőcze if (status == AE_NOT_FOUND) 203b4f9fe12SLen Brown return AE_OK; 204736b48aaSBarnabás Pőcze 205736b48aaSBarnabás Pőcze return status; 206b4f9fe12SLen Brown } 207b4f9fe12SLen Brown 20857f2ce89SBarnabás Pőcze #define WMI_ACPI_METHOD_NAME_SIZE 5 20957f2ce89SBarnabás Pőcze 21057f2ce89SBarnabás Pőcze static inline void get_acpi_method_name(const struct wmi_block *wblock, 21157f2ce89SBarnabás Pőcze const char method, 21257f2ce89SBarnabás Pőcze char buffer[static WMI_ACPI_METHOD_NAME_SIZE]) 21357f2ce89SBarnabás Pőcze { 21457f2ce89SBarnabás Pőcze static_assert(ARRAY_SIZE(wblock->gblock.object_id) == 2); 21557f2ce89SBarnabás Pőcze static_assert(WMI_ACPI_METHOD_NAME_SIZE >= 5); 21657f2ce89SBarnabás Pőcze 21757f2ce89SBarnabás Pőcze buffer[0] = 'W'; 21857f2ce89SBarnabás Pőcze buffer[1] = method; 21957f2ce89SBarnabás Pőcze buffer[2] = wblock->gblock.object_id[0]; 22057f2ce89SBarnabás Pőcze buffer[3] = wblock->gblock.object_id[1]; 22157f2ce89SBarnabás Pőcze buffer[4] = '\0'; 22257f2ce89SBarnabás Pőcze } 22357f2ce89SBarnabás Pőcze 22451142a08SBarnabás Pőcze static inline acpi_object_type get_param_acpi_type(const struct wmi_block *wblock) 22551142a08SBarnabás Pőcze { 22651142a08SBarnabás Pőcze if (wblock->gblock.flags & ACPI_WMI_STRING) 22751142a08SBarnabás Pőcze return ACPI_TYPE_STRING; 22851142a08SBarnabás Pőcze else 22951142a08SBarnabás Pőcze return ACPI_TYPE_BUFFER; 23051142a08SBarnabás Pőcze } 23151142a08SBarnabás Pőcze 23225be44f6SBarnabás Pőcze static acpi_status get_event_data(const struct wmi_block *wblock, struct acpi_buffer *out) 23325be44f6SBarnabás Pőcze { 23425be44f6SBarnabás Pőcze union acpi_object param = { 23525be44f6SBarnabás Pőcze .integer = { 23625be44f6SBarnabás Pőcze .type = ACPI_TYPE_INTEGER, 23725be44f6SBarnabás Pőcze .value = wblock->gblock.notify_id, 23825be44f6SBarnabás Pőcze } 23925be44f6SBarnabás Pőcze }; 24025be44f6SBarnabás Pőcze struct acpi_object_list input = { 24125be44f6SBarnabás Pőcze .count = 1, 24225be44f6SBarnabás Pőcze .pointer = ¶m, 24325be44f6SBarnabás Pőcze }; 24425be44f6SBarnabás Pőcze 24525be44f6SBarnabás Pőcze return acpi_evaluate_object(wblock->acpi_device->handle, "_WED", &input, out); 24625be44f6SBarnabás Pőcze } 24725be44f6SBarnabás Pőcze 248b4f9fe12SLen Brown /* 249b4f9fe12SLen Brown * Exported WMI functions 250b4f9fe12SLen Brown */ 25144b6b766SMario Limonciello 25244b6b766SMario Limonciello /** 25344b6b766SMario Limonciello * set_required_buffer_size - Sets the buffer size needed for performing IOCTL 25444b6b766SMario Limonciello * @wdev: A wmi bus device from a driver 2555a707af1SAndy Shevchenko * @length: Required buffer size 25644b6b766SMario Limonciello * 257b4cc9795SArmin Wolf * Allocates memory needed for buffer, stores the buffer size in that memory. 258b4cc9795SArmin Wolf * 259b4cc9795SArmin Wolf * Return: 0 on success or a negative error code for failure. 26044b6b766SMario Limonciello */ 26144b6b766SMario Limonciello int set_required_buffer_size(struct wmi_device *wdev, u64 length) 26244b6b766SMario Limonciello { 26344b6b766SMario Limonciello struct wmi_block *wblock; 26444b6b766SMario Limonciello 26544b6b766SMario Limonciello wblock = container_of(wdev, struct wmi_block, dev); 26644b6b766SMario Limonciello wblock->req_buf_size = length; 26744b6b766SMario Limonciello 26844b6b766SMario Limonciello return 0; 26944b6b766SMario Limonciello } 27044b6b766SMario Limonciello EXPORT_SYMBOL_GPL(set_required_buffer_size); 27144b6b766SMario Limonciello 272b4f9fe12SLen Brown /** 2732a2b13aeSArmin Wolf * wmi_instance_count - Get number of WMI object instances 2742a2b13aeSArmin Wolf * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba 2752a2b13aeSArmin Wolf * 2762a2b13aeSArmin Wolf * Get the number of WMI object instances. 2772a2b13aeSArmin Wolf * 2782a2b13aeSArmin Wolf * Returns: Number of WMI object instances or negative error code. 2792a2b13aeSArmin Wolf */ 2802a2b13aeSArmin Wolf int wmi_instance_count(const char *guid_string) 2812a2b13aeSArmin Wolf { 2822a2b13aeSArmin Wolf struct wmi_block *wblock; 2832a2b13aeSArmin Wolf acpi_status status; 2842a2b13aeSArmin Wolf 2852a2b13aeSArmin Wolf status = find_guid(guid_string, &wblock); 2862a2b13aeSArmin Wolf if (ACPI_FAILURE(status)) { 2872a2b13aeSArmin Wolf if (status == AE_BAD_PARAMETER) 2882a2b13aeSArmin Wolf return -EINVAL; 2892a2b13aeSArmin Wolf 2902a2b13aeSArmin Wolf return -ENODEV; 2912a2b13aeSArmin Wolf } 2922a2b13aeSArmin Wolf 2932a2b13aeSArmin Wolf return wmidev_instance_count(&wblock->dev); 2942a2b13aeSArmin Wolf } 2952a2b13aeSArmin Wolf EXPORT_SYMBOL_GPL(wmi_instance_count); 2962a2b13aeSArmin Wolf 2972a2b13aeSArmin Wolf /** 2982a2b13aeSArmin Wolf * wmidev_instance_count - Get number of WMI object instances 2992a2b13aeSArmin Wolf * @wdev: A wmi bus device from a driver 3002a2b13aeSArmin Wolf * 3012a2b13aeSArmin Wolf * Get the number of WMI object instances. 3022a2b13aeSArmin Wolf * 3032a2b13aeSArmin Wolf * Returns: Number of WMI object instances. 3042a2b13aeSArmin Wolf */ 3052a2b13aeSArmin Wolf u8 wmidev_instance_count(struct wmi_device *wdev) 3062a2b13aeSArmin Wolf { 3072a2b13aeSArmin Wolf struct wmi_block *wblock = container_of(wdev, struct wmi_block, dev); 3082a2b13aeSArmin Wolf 3092a2b13aeSArmin Wolf return wblock->gblock.instance_count; 3102a2b13aeSArmin Wolf } 3112a2b13aeSArmin Wolf EXPORT_SYMBOL_GPL(wmidev_instance_count); 3122a2b13aeSArmin Wolf 3132a2b13aeSArmin Wolf /** 314d54bd4bcSArmin Wolf * wmi_evaluate_method - Evaluate a WMI method (deprecated) 315b4f9fe12SLen Brown * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba 316b4f9fe12SLen Brown * @instance: Instance index 317b4f9fe12SLen Brown * @method_id: Method ID to call 3185a707af1SAndy Shevchenko * @in: Buffer containing input for the method call 3195a707af1SAndy Shevchenko * @out: Empty buffer to return the method results 320b4f9fe12SLen Brown * 321b4cc9795SArmin Wolf * Call an ACPI-WMI method, the caller must free @out. 322b4cc9795SArmin Wolf * 323b4cc9795SArmin Wolf * Return: acpi_status signaling success or error. 324b4f9fe12SLen Brown */ 325bba08f35SBarnabás Pőcze acpi_status wmi_evaluate_method(const char *guid_string, u8 instance, u32 method_id, 326bba08f35SBarnabás Pőcze const struct acpi_buffer *in, struct acpi_buffer *out) 327b4f9fe12SLen Brown { 328722c856dSMario Limonciello struct wmi_block *wblock = NULL; 329b0179b80SBarnabás Pőcze acpi_status status; 330722c856dSMario Limonciello 331b0179b80SBarnabás Pőcze status = find_guid(guid_string, &wblock); 332b0179b80SBarnabás Pőcze if (ACPI_FAILURE(status)) 333b0179b80SBarnabás Pőcze return status; 334b0179b80SBarnabás Pőcze 335722c856dSMario Limonciello return wmidev_evaluate_method(&wblock->dev, instance, method_id, 336722c856dSMario Limonciello in, out); 337722c856dSMario Limonciello } 338722c856dSMario Limonciello EXPORT_SYMBOL_GPL(wmi_evaluate_method); 339722c856dSMario Limonciello 340722c856dSMario Limonciello /** 341722c856dSMario Limonciello * wmidev_evaluate_method - Evaluate a WMI method 342722c856dSMario Limonciello * @wdev: A wmi bus device from a driver 343722c856dSMario Limonciello * @instance: Instance index 344722c856dSMario Limonciello * @method_id: Method ID to call 3455a707af1SAndy Shevchenko * @in: Buffer containing input for the method call 3465a707af1SAndy Shevchenko * @out: Empty buffer to return the method results 347722c856dSMario Limonciello * 348b4cc9795SArmin Wolf * Call an ACPI-WMI method, the caller must free @out. 349b4cc9795SArmin Wolf * 350b4cc9795SArmin Wolf * Return: acpi_status signaling success or error. 351722c856dSMario Limonciello */ 352bba08f35SBarnabás Pőcze acpi_status wmidev_evaluate_method(struct wmi_device *wdev, u8 instance, u32 method_id, 353bba08f35SBarnabás Pőcze const struct acpi_buffer *in, struct acpi_buffer *out) 354722c856dSMario Limonciello { 35543aacf83SBarnabás Pőcze struct guid_block *block; 35643aacf83SBarnabás Pőcze struct wmi_block *wblock; 357b4f9fe12SLen Brown acpi_handle handle; 358b4f9fe12SLen Brown struct acpi_object_list input; 359b4f9fe12SLen Brown union acpi_object params[3]; 36057f2ce89SBarnabás Pőcze char method[WMI_ACPI_METHOD_NAME_SIZE]; 361b4f9fe12SLen Brown 362722c856dSMario Limonciello wblock = container_of(wdev, struct wmi_block, dev); 363b4f9fe12SLen Brown block = &wblock->gblock; 364b0e86302SAndy Lutomirski handle = wblock->acpi_device->handle; 365b4f9fe12SLen Brown 366b4f9fe12SLen Brown if (!(block->flags & ACPI_WMI_METHOD)) 367b4f9fe12SLen Brown return AE_BAD_DATA; 368b4f9fe12SLen Brown 3696afa1e2aSPali Rohár if (block->instance_count <= instance) 370b4f9fe12SLen Brown return AE_BAD_PARAMETER; 371b4f9fe12SLen Brown 372b4f9fe12SLen Brown input.count = 2; 373b4f9fe12SLen Brown input.pointer = params; 374b4f9fe12SLen Brown params[0].type = ACPI_TYPE_INTEGER; 375b4f9fe12SLen Brown params[0].integer.value = instance; 376b4f9fe12SLen Brown params[1].type = ACPI_TYPE_INTEGER; 377b4f9fe12SLen Brown params[1].integer.value = method_id; 378b4f9fe12SLen Brown 379b4f9fe12SLen Brown if (in) { 380b4f9fe12SLen Brown input.count = 3; 381b4f9fe12SLen Brown 38251142a08SBarnabás Pőcze params[2].type = get_param_acpi_type(wblock); 383b4f9fe12SLen Brown params[2].buffer.length = in->length; 384b4f9fe12SLen Brown params[2].buffer.pointer = in->pointer; 385b4f9fe12SLen Brown } 386b4f9fe12SLen Brown 38757f2ce89SBarnabás Pőcze get_acpi_method_name(wblock, 'M', method); 388b4f9fe12SLen Brown 38921397cacSBarnabás Pőcze return acpi_evaluate_object(handle, method, &input, out); 390b4f9fe12SLen Brown } 391722c856dSMario Limonciello EXPORT_SYMBOL_GPL(wmidev_evaluate_method); 392b4f9fe12SLen Brown 39356a37025SAndy Lutomirski static acpi_status __query_block(struct wmi_block *wblock, u8 instance, 394b4f9fe12SLen Brown struct acpi_buffer *out) 395b4f9fe12SLen Brown { 39643aacf83SBarnabás Pőcze struct guid_block *block; 39754f14c27SZhang Rui acpi_handle handle; 398b4f9fe12SLen Brown acpi_status status, wc_status = AE_ERROR; 3998122ab66SZhang Rui struct acpi_object_list input; 4008122ab66SZhang Rui union acpi_object wq_params[1]; 40157f2ce89SBarnabás Pőcze char wc_method[WMI_ACPI_METHOD_NAME_SIZE]; 40257f2ce89SBarnabás Pőcze char method[WMI_ACPI_METHOD_NAME_SIZE]; 403b4f9fe12SLen Brown 40456a37025SAndy Lutomirski if (!out) 405b4f9fe12SLen Brown return AE_BAD_PARAMETER; 406b4f9fe12SLen Brown 407b4f9fe12SLen Brown block = &wblock->gblock; 408b0e86302SAndy Lutomirski handle = wblock->acpi_device->handle; 409b4f9fe12SLen Brown 4106afa1e2aSPali Rohár if (block->instance_count <= instance) 411b4f9fe12SLen Brown return AE_BAD_PARAMETER; 412b4f9fe12SLen Brown 413b4f9fe12SLen Brown /* Check GUID is a data block */ 414b4f9fe12SLen Brown if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD)) 415b4f9fe12SLen Brown return AE_ERROR; 416b4f9fe12SLen Brown 417b4f9fe12SLen Brown input.count = 1; 418b4f9fe12SLen Brown input.pointer = wq_params; 419b4f9fe12SLen Brown wq_params[0].type = ACPI_TYPE_INTEGER; 420b4f9fe12SLen Brown wq_params[0].integer.value = instance; 421b4f9fe12SLen Brown 422a90b38c5SHans de Goede if (instance == 0 && test_bit(WMI_READ_TAKES_NO_ARGS, &wblock->flags)) 423d4fc91adSAndy Lutomirski input.count = 0; 424d4fc91adSAndy Lutomirski 425b4f9fe12SLen Brown /* 426b4f9fe12SLen Brown * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method first to 427b4f9fe12SLen Brown * enable collection. 428b4f9fe12SLen Brown */ 429b4f9fe12SLen Brown if (block->flags & ACPI_WMI_EXPENSIVE) { 43057f2ce89SBarnabás Pőcze get_acpi_method_name(wblock, 'C', wc_method); 431b4f9fe12SLen Brown 432b4f9fe12SLen Brown /* 433b4f9fe12SLen Brown * Some GUIDs break the specification by declaring themselves 434b4f9fe12SLen Brown * expensive, but have no corresponding WCxx method. So we 435b4f9fe12SLen Brown * should not fail if this happens. 436b4f9fe12SLen Brown */ 437bad9da86SKelsey Skunberg wc_status = acpi_execute_simple_method(handle, wc_method, 1); 438b4f9fe12SLen Brown } 439b4f9fe12SLen Brown 44057f2ce89SBarnabás Pőcze get_acpi_method_name(wblock, 'Q', method); 441b4f9fe12SLen Brown status = acpi_evaluate_object(handle, method, &input, out); 442b4f9fe12SLen Brown 443b4f9fe12SLen Brown /* 444b4f9fe12SLen Brown * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method, even if 445b4f9fe12SLen Brown * the WQxx method failed - we should disable collection anyway. 446b4f9fe12SLen Brown */ 447b4f9fe12SLen Brown if ((block->flags & ACPI_WMI_EXPENSIVE) && ACPI_SUCCESS(wc_status)) { 4481975718cSBarnabás Pőcze /* 4491975718cSBarnabás Pőcze * Ignore whether this WCxx call succeeds or not since 4501975718cSBarnabás Pőcze * the previously executed WQxx method call might have 4511975718cSBarnabás Pőcze * succeeded, and returning the failing status code 4521975718cSBarnabás Pőcze * of this call would throw away the result of the WQxx 4531975718cSBarnabás Pőcze * call, potentially leaking memory. 4541975718cSBarnabás Pőcze */ 4551975718cSBarnabás Pőcze acpi_execute_simple_method(handle, wc_method, 0); 456b4f9fe12SLen Brown } 457b4f9fe12SLen Brown 458b4f9fe12SLen Brown return status; 459b4f9fe12SLen Brown } 46056a37025SAndy Lutomirski 46156a37025SAndy Lutomirski /** 46256a37025SAndy Lutomirski * wmi_query_block - Return contents of a WMI block (deprecated) 46356a37025SAndy Lutomirski * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba 46456a37025SAndy Lutomirski * @instance: Instance index 4655a707af1SAndy Shevchenko * @out: Empty buffer to return the contents of the data block to 46656a37025SAndy Lutomirski * 467b4cc9795SArmin Wolf * Query a ACPI-WMI block, the caller must free @out. 468b4cc9795SArmin Wolf * 469b4cc9795SArmin Wolf * Return: ACPI object containing the content of the WMI block. 47056a37025SAndy Lutomirski */ 47156a37025SAndy Lutomirski acpi_status wmi_query_block(const char *guid_string, u8 instance, 47256a37025SAndy Lutomirski struct acpi_buffer *out) 47356a37025SAndy Lutomirski { 47456a37025SAndy Lutomirski struct wmi_block *wblock; 475b0179b80SBarnabás Pőcze acpi_status status; 47656a37025SAndy Lutomirski 477b0179b80SBarnabás Pőcze status = find_guid(guid_string, &wblock); 478b0179b80SBarnabás Pőcze if (ACPI_FAILURE(status)) 479b0179b80SBarnabás Pőcze return status; 48056a37025SAndy Lutomirski 48156a37025SAndy Lutomirski return __query_block(wblock, instance, out); 48256a37025SAndy Lutomirski } 483b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_query_block); 484b4f9fe12SLen Brown 485b4cc9795SArmin Wolf /** 486b4cc9795SArmin Wolf * wmidev_block_query - Return contents of a WMI block 487b4cc9795SArmin Wolf * @wdev: A wmi bus device from a driver 488b4cc9795SArmin Wolf * @instance: Instance index 489b4cc9795SArmin Wolf * 490b4cc9795SArmin Wolf * Query an ACPI-WMI block, the caller must free the result. 491b4cc9795SArmin Wolf * 492b4cc9795SArmin Wolf * Return: ACPI object containing the content of the WMI block. 493b4cc9795SArmin Wolf */ 49456a37025SAndy Lutomirski union acpi_object *wmidev_block_query(struct wmi_device *wdev, u8 instance) 49556a37025SAndy Lutomirski { 49656a37025SAndy Lutomirski struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL }; 49756a37025SAndy Lutomirski struct wmi_block *wblock = container_of(wdev, struct wmi_block, dev); 49856a37025SAndy Lutomirski 49956a37025SAndy Lutomirski if (ACPI_FAILURE(__query_block(wblock, instance, &out))) 50056a37025SAndy Lutomirski return NULL; 50156a37025SAndy Lutomirski 502c06a2fdeSBarnabás Pőcze return out.pointer; 50356a37025SAndy Lutomirski } 50456a37025SAndy Lutomirski EXPORT_SYMBOL_GPL(wmidev_block_query); 50556a37025SAndy Lutomirski 506b4f9fe12SLen Brown /** 507d54bd4bcSArmin Wolf * wmi_set_block - Write to a WMI block (deprecated) 508b4f9fe12SLen Brown * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba 509b4f9fe12SLen Brown * @instance: Instance index 5105a707af1SAndy Shevchenko * @in: Buffer containing new values for the data block 511b4f9fe12SLen Brown * 512b4cc9795SArmin Wolf * Write the contents of the input buffer to an ACPI-WMI data block. 513b4cc9795SArmin Wolf * 514b4cc9795SArmin Wolf * Return: acpi_status signaling success or error. 515b4f9fe12SLen Brown */ 516b4f9fe12SLen Brown acpi_status wmi_set_block(const char *guid_string, u8 instance, 517b4f9fe12SLen Brown const struct acpi_buffer *in) 518b4f9fe12SLen Brown { 519b4f9fe12SLen Brown struct wmi_block *wblock = NULL; 52043aacf83SBarnabás Pőcze struct guid_block *block; 521b4f9fe12SLen Brown acpi_handle handle; 522b4f9fe12SLen Brown struct acpi_object_list input; 523b4f9fe12SLen Brown union acpi_object params[2]; 52457f2ce89SBarnabás Pőcze char method[WMI_ACPI_METHOD_NAME_SIZE]; 525b0179b80SBarnabás Pőcze acpi_status status; 526b4f9fe12SLen Brown 527b0179b80SBarnabás Pőcze if (!in) 528b4f9fe12SLen Brown return AE_BAD_DATA; 529b4f9fe12SLen Brown 530b0179b80SBarnabás Pőcze status = find_guid(guid_string, &wblock); 531b0179b80SBarnabás Pőcze if (ACPI_FAILURE(status)) 532b0179b80SBarnabás Pőcze return status; 533b4f9fe12SLen Brown 534b4f9fe12SLen Brown block = &wblock->gblock; 535b0e86302SAndy Lutomirski handle = wblock->acpi_device->handle; 536b4f9fe12SLen Brown 5376afa1e2aSPali Rohár if (block->instance_count <= instance) 538b4f9fe12SLen Brown return AE_BAD_PARAMETER; 539b4f9fe12SLen Brown 540b4f9fe12SLen Brown /* Check GUID is a data block */ 541b4f9fe12SLen Brown if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD)) 542b4f9fe12SLen Brown return AE_ERROR; 543b4f9fe12SLen Brown 544b4f9fe12SLen Brown input.count = 2; 545b4f9fe12SLen Brown input.pointer = params; 546b4f9fe12SLen Brown params[0].type = ACPI_TYPE_INTEGER; 547b4f9fe12SLen Brown params[0].integer.value = instance; 54851142a08SBarnabás Pőcze params[1].type = get_param_acpi_type(wblock); 549b4f9fe12SLen Brown params[1].buffer.length = in->length; 550b4f9fe12SLen Brown params[1].buffer.pointer = in->pointer; 551b4f9fe12SLen Brown 55257f2ce89SBarnabás Pőcze get_acpi_method_name(wblock, 'S', method); 553b4f9fe12SLen Brown 554b4f9fe12SLen Brown return acpi_evaluate_object(handle, method, &input, NULL); 555b4f9fe12SLen Brown } 556b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_set_block); 557b4f9fe12SLen Brown 55837830662SDmitry Torokhov static void wmi_dump_wdg(const struct guid_block *g) 559a929aae0SThomas Renninger { 56067f472fdSBarnabás Pőcze pr_info("%pUL:\n", &g->guid); 561cd3921f8SPali Rohár if (g->flags & ACPI_WMI_EVENT) 562cd3921f8SPali Rohár pr_info("\tnotify_id: 0x%02X\n", g->notify_id); 563cd3921f8SPali Rohár else 564cd3921f8SPali Rohár pr_info("\tobject_id: %2pE\n", g->object_id); 5658e07514dSDmitry Torokhov pr_info("\tinstance_count: %d\n", g->instance_count); 5668e07514dSDmitry Torokhov pr_info("\tflags: %#x", g->flags); 567a929aae0SThomas Renninger if (g->flags) { 568a929aae0SThomas Renninger if (g->flags & ACPI_WMI_EXPENSIVE) 5698e07514dSDmitry Torokhov pr_cont(" ACPI_WMI_EXPENSIVE"); 570a929aae0SThomas Renninger if (g->flags & ACPI_WMI_METHOD) 5718e07514dSDmitry Torokhov pr_cont(" ACPI_WMI_METHOD"); 572a929aae0SThomas Renninger if (g->flags & ACPI_WMI_STRING) 5738e07514dSDmitry Torokhov pr_cont(" ACPI_WMI_STRING"); 574a929aae0SThomas Renninger if (g->flags & ACPI_WMI_EVENT) 5758e07514dSDmitry Torokhov pr_cont(" ACPI_WMI_EVENT"); 576a929aae0SThomas Renninger } 5778e07514dSDmitry Torokhov pr_cont("\n"); 578a929aae0SThomas Renninger 579a929aae0SThomas Renninger } 580a929aae0SThomas Renninger 581fc3155b2SThomas Renninger static void wmi_notify_debug(u32 value, void *context) 582fc3155b2SThomas Renninger { 583fc3155b2SThomas Renninger struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; 584fc3155b2SThomas Renninger union acpi_object *obj; 5851492616aSAxel Lin acpi_status status; 586fc3155b2SThomas Renninger 5871492616aSAxel Lin status = wmi_get_event_data(value, &response); 5881492616aSAxel Lin if (status != AE_OK) { 5898e07514dSDmitry Torokhov pr_info("bad event status 0x%x\n", status); 5901492616aSAxel Lin return; 5911492616aSAxel Lin } 592fc3155b2SThomas Renninger 593c06a2fdeSBarnabás Pőcze obj = response.pointer; 594fc3155b2SThomas Renninger if (!obj) 595fc3155b2SThomas Renninger return; 596fc3155b2SThomas Renninger 5971c23ab91SBarnabás Pőcze pr_info("DEBUG: event 0x%02X ", value); 598fc3155b2SThomas Renninger switch (obj->type) { 599fc3155b2SThomas Renninger case ACPI_TYPE_BUFFER: 6001c23ab91SBarnabás Pőcze pr_cont("BUFFER_TYPE - length %u\n", obj->buffer.length); 601fc3155b2SThomas Renninger break; 602fc3155b2SThomas Renninger case ACPI_TYPE_STRING: 6038e07514dSDmitry Torokhov pr_cont("STRING_TYPE - %s\n", obj->string.pointer); 604fc3155b2SThomas Renninger break; 605fc3155b2SThomas Renninger case ACPI_TYPE_INTEGER: 6068e07514dSDmitry Torokhov pr_cont("INTEGER_TYPE - %llu\n", obj->integer.value); 607fc3155b2SThomas Renninger break; 608fc3155b2SThomas Renninger case ACPI_TYPE_PACKAGE: 6091c23ab91SBarnabás Pőcze pr_cont("PACKAGE_TYPE - %u elements\n", obj->package.count); 610fc3155b2SThomas Renninger break; 611fc3155b2SThomas Renninger default: 6128e07514dSDmitry Torokhov pr_cont("object type 0x%X\n", obj->type); 613fc3155b2SThomas Renninger } 6141492616aSAxel Lin kfree(obj); 615fc3155b2SThomas Renninger } 616fc3155b2SThomas Renninger 617b4f9fe12SLen Brown /** 618d54bd4bcSArmin Wolf * wmi_install_notify_handler - Register handler for WMI events (deprecated) 6195a707af1SAndy Shevchenko * @guid: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba 620b4f9fe12SLen Brown * @handler: Function to handle notifications 621b4f9fe12SLen Brown * @data: Data to be returned to handler when event is fired 622b4f9fe12SLen Brown * 623b4f9fe12SLen Brown * Register a handler for events sent to the ACPI-WMI mapper device. 624b4cc9795SArmin Wolf * 625b4cc9795SArmin Wolf * Return: acpi_status signaling success or error. 626b4f9fe12SLen Brown */ 627b4f9fe12SLen Brown acpi_status wmi_install_notify_handler(const char *guid, 628bba08f35SBarnabás Pőcze wmi_notify_handler handler, 629bba08f35SBarnabás Pőcze void *data) 630b4f9fe12SLen Brown { 631b4f9fe12SLen Brown struct wmi_block *block; 63258f6425eSColin King acpi_status status = AE_NOT_EXIST; 633f9dffc14SAndy Shevchenko guid_t guid_input; 634b4f9fe12SLen Brown 635b4f9fe12SLen Brown if (!guid || !handler) 636b4f9fe12SLen Brown return AE_BAD_PARAMETER; 637b4f9fe12SLen Brown 638f9dffc14SAndy Shevchenko if (guid_parse(guid, &guid_input)) 639538d7eb8SAndy Shevchenko return AE_BAD_PARAMETER; 640b4f9fe12SLen Brown 641cedb3b2aSAndy Shevchenko list_for_each_entry(block, &wmi_block_list, list) { 64258f6425eSColin King acpi_status wmi_status; 64358f6425eSColin King 64467f472fdSBarnabás Pőcze if (guid_equal(&block->gblock.guid, &guid_input)) { 64558f6425eSColin King if (block->handler && 64658f6425eSColin King block->handler != wmi_notify_debug) 647b4f9fe12SLen Brown return AE_ALREADY_ACQUIRED; 648b4f9fe12SLen Brown 649b4f9fe12SLen Brown block->handler = handler; 650b4f9fe12SLen Brown block->handler_data = data; 651b4f9fe12SLen Brown 652285dd01aSBarnabás Pőcze wmi_status = wmi_method_enable(block, true); 65358f6425eSColin King if ((wmi_status != AE_OK) || 65458f6425eSColin King ((wmi_status == AE_OK) && (status == AE_NOT_EXIST))) 65558f6425eSColin King status = wmi_status; 65658f6425eSColin King } 65758f6425eSColin King } 658b4f9fe12SLen Brown 659b4f9fe12SLen Brown return status; 660b4f9fe12SLen Brown } 661b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_install_notify_handler); 662b4f9fe12SLen Brown 663b4f9fe12SLen Brown /** 664d54bd4bcSArmin Wolf * wmi_remove_notify_handler - Unregister handler for WMI events (deprecated) 6655a707af1SAndy Shevchenko * @guid: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba 666b4f9fe12SLen Brown * 667b4f9fe12SLen Brown * Unregister handler for events sent to the ACPI-WMI mapper device. 668b4cc9795SArmin Wolf * 669b4cc9795SArmin Wolf * Return: acpi_status signaling success or error. 670b4f9fe12SLen Brown */ 671b4f9fe12SLen Brown acpi_status wmi_remove_notify_handler(const char *guid) 672b4f9fe12SLen Brown { 673b4f9fe12SLen Brown struct wmi_block *block; 67458f6425eSColin King acpi_status status = AE_NOT_EXIST; 675f9dffc14SAndy Shevchenko guid_t guid_input; 676b4f9fe12SLen Brown 677b4f9fe12SLen Brown if (!guid) 678b4f9fe12SLen Brown return AE_BAD_PARAMETER; 679b4f9fe12SLen Brown 680f9dffc14SAndy Shevchenko if (guid_parse(guid, &guid_input)) 681538d7eb8SAndy Shevchenko return AE_BAD_PARAMETER; 682b4f9fe12SLen Brown 683cedb3b2aSAndy Shevchenko list_for_each_entry(block, &wmi_block_list, list) { 68458f6425eSColin King acpi_status wmi_status; 68558f6425eSColin King 68667f472fdSBarnabás Pőcze if (guid_equal(&block->gblock.guid, &guid_input)) { 68758f6425eSColin King if (!block->handler || 68858f6425eSColin King block->handler == wmi_notify_debug) 689b4f9fe12SLen Brown return AE_NULL_ENTRY; 690b4f9fe12SLen Brown 691fc3155b2SThomas Renninger if (debug_event) { 692fc3155b2SThomas Renninger block->handler = wmi_notify_debug; 69358f6425eSColin King status = AE_OK; 694fc3155b2SThomas Renninger } else { 695285dd01aSBarnabás Pőcze wmi_status = wmi_method_enable(block, false); 696b4f9fe12SLen Brown block->handler = NULL; 697b4f9fe12SLen Brown block->handler_data = NULL; 69858f6425eSColin King if ((wmi_status != AE_OK) || 69958f6425eSColin King ((wmi_status == AE_OK) && 70058f6425eSColin King (status == AE_NOT_EXIST))) 70158f6425eSColin King status = wmi_status; 702fc3155b2SThomas Renninger } 70358f6425eSColin King } 70458f6425eSColin King } 70558f6425eSColin King 706b4f9fe12SLen Brown return status; 707b4f9fe12SLen Brown } 708b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_remove_notify_handler); 709b4f9fe12SLen Brown 710b4f9fe12SLen Brown /** 711d54bd4bcSArmin Wolf * wmi_get_event_data - Get WMI data associated with an event (deprecated) 712b4f9fe12SLen Brown * 7133e9b988eSAnisse Astier * @event: Event to find 714b4cc9795SArmin Wolf * @out: Buffer to hold event data 715b4f9fe12SLen Brown * 716b4cc9795SArmin Wolf * Get extra data associated with an WMI event, the caller needs to free @out. 717b4cc9795SArmin Wolf * 718b4cc9795SArmin Wolf * Return: acpi_status signaling success or error. 719b4f9fe12SLen Brown */ 720b4f9fe12SLen Brown acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out) 721b4f9fe12SLen Brown { 722b4f9fe12SLen Brown struct wmi_block *wblock; 723b4f9fe12SLen Brown 724cedb3b2aSAndy Shevchenko list_for_each_entry(wblock, &wmi_block_list, list) { 725f5431bf1SBarnabás Pőcze struct guid_block *gblock = &wblock->gblock; 726b4f9fe12SLen Brown 72725be44f6SBarnabás Pőcze if ((gblock->flags & ACPI_WMI_EVENT) && gblock->notify_id == event) 72825be44f6SBarnabás Pőcze return get_event_data(wblock, out); 729b4f9fe12SLen Brown } 730b4f9fe12SLen Brown 731b4f9fe12SLen Brown return AE_NOT_FOUND; 732b4f9fe12SLen Brown } 733b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_get_event_data); 734b4f9fe12SLen Brown 735b4f9fe12SLen Brown /** 736b4f9fe12SLen Brown * wmi_has_guid - Check if a GUID is available 737b4f9fe12SLen Brown * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba 738b4f9fe12SLen Brown * 739b4cc9795SArmin Wolf * Check if a given GUID is defined by _WDG. 740b4cc9795SArmin Wolf * 741b4cc9795SArmin Wolf * Return: True if GUID is available, false otherwise. 742b4f9fe12SLen Brown */ 743b4f9fe12SLen Brown bool wmi_has_guid(const char *guid_string) 744b4f9fe12SLen Brown { 745b0179b80SBarnabás Pőcze return ACPI_SUCCESS(find_guid(guid_string, NULL)); 746b4f9fe12SLen Brown } 747b4f9fe12SLen Brown EXPORT_SYMBOL_GPL(wmi_has_guid); 748b4f9fe12SLen Brown 749e7488e58SYurii Pavlovskyi /** 750d54bd4bcSArmin Wolf * wmi_get_acpi_device_uid() - Get _UID name of ACPI device that defines GUID (deprecated) 751e7488e58SYurii Pavlovskyi * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba 752e7488e58SYurii Pavlovskyi * 753e7488e58SYurii Pavlovskyi * Find the _UID of ACPI device associated with this WMI GUID. 754e7488e58SYurii Pavlovskyi * 755b4cc9795SArmin Wolf * Return: The ACPI _UID field value or NULL if the WMI GUID was not found. 756e7488e58SYurii Pavlovskyi */ 757e7488e58SYurii Pavlovskyi char *wmi_get_acpi_device_uid(const char *guid_string) 758e7488e58SYurii Pavlovskyi { 759e7488e58SYurii Pavlovskyi struct wmi_block *wblock = NULL; 760b0179b80SBarnabás Pőcze acpi_status status; 761e7488e58SYurii Pavlovskyi 762b0179b80SBarnabás Pőcze status = find_guid(guid_string, &wblock); 763b0179b80SBarnabás Pőcze if (ACPI_FAILURE(status)) 764e7488e58SYurii Pavlovskyi return NULL; 765e7488e58SYurii Pavlovskyi 766e7488e58SYurii Pavlovskyi return acpi_device_uid(wblock->acpi_device); 767e7488e58SYurii Pavlovskyi } 768e7488e58SYurii Pavlovskyi EXPORT_SYMBOL_GPL(wmi_get_acpi_device_uid); 769e7488e58SYurii Pavlovskyi 7702f89e23bSGreg Kroah-Hartman #define dev_to_wblock(__dev) container_of_const(__dev, struct wmi_block, dev.dev) 7712f89e23bSGreg Kroah-Hartman #define dev_to_wdev(__dev) container_of_const(__dev, struct wmi_device, dev) 772844af950SAndy Lutomirski 773e7b2e334SBarnabás Pőcze static inline struct wmi_driver *drv_to_wdrv(struct device_driver *drv) 774e7b2e334SBarnabás Pőcze { 775e7b2e334SBarnabás Pőcze return container_of(drv, struct wmi_driver, driver); 776e7b2e334SBarnabás Pőcze } 777e7b2e334SBarnabás Pőcze 778b4f9fe12SLen Brown /* 7791caab3c1SMatthew Garrett * sysfs interface 7801caab3c1SMatthew Garrett */ 781614ef432SDmitry Torokhov static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, 7821caab3c1SMatthew Garrett char *buf) 7831caab3c1SMatthew Garrett { 784844af950SAndy Lutomirski struct wmi_block *wblock = dev_to_wblock(dev); 7851caab3c1SMatthew Garrett 7866133913aSBarnabás Pőcze return sysfs_emit(buf, "wmi:%pUL\n", &wblock->gblock.guid); 7871caab3c1SMatthew Garrett } 788e80b89a5SGreg Kroah-Hartman static DEVICE_ATTR_RO(modalias); 789614ef432SDmitry Torokhov 790844af950SAndy Lutomirski static ssize_t guid_show(struct device *dev, struct device_attribute *attr, 791844af950SAndy Lutomirski char *buf) 792844af950SAndy Lutomirski { 793844af950SAndy Lutomirski struct wmi_block *wblock = dev_to_wblock(dev); 794844af950SAndy Lutomirski 7956133913aSBarnabás Pőcze return sysfs_emit(buf, "%pUL\n", &wblock->gblock.guid); 796844af950SAndy Lutomirski } 797844af950SAndy Lutomirski static DEVICE_ATTR_RO(guid); 798844af950SAndy Lutomirski 799d79b1074SAndy Lutomirski static ssize_t instance_count_show(struct device *dev, 800d79b1074SAndy Lutomirski struct device_attribute *attr, char *buf) 801d79b1074SAndy Lutomirski { 802d79b1074SAndy Lutomirski struct wmi_block *wblock = dev_to_wblock(dev); 803d79b1074SAndy Lutomirski 8046133913aSBarnabás Pőcze return sysfs_emit(buf, "%d\n", (int)wblock->gblock.instance_count); 805d79b1074SAndy Lutomirski } 806d79b1074SAndy Lutomirski static DEVICE_ATTR_RO(instance_count); 807d79b1074SAndy Lutomirski 808d79b1074SAndy Lutomirski static ssize_t expensive_show(struct device *dev, 809d79b1074SAndy Lutomirski struct device_attribute *attr, char *buf) 810d79b1074SAndy Lutomirski { 811d79b1074SAndy Lutomirski struct wmi_block *wblock = dev_to_wblock(dev); 812d79b1074SAndy Lutomirski 8136133913aSBarnabás Pőcze return sysfs_emit(buf, "%d\n", 814d79b1074SAndy Lutomirski (wblock->gblock.flags & ACPI_WMI_EXPENSIVE) != 0); 815d79b1074SAndy Lutomirski } 816d79b1074SAndy Lutomirski static DEVICE_ATTR_RO(expensive); 817d79b1074SAndy Lutomirski 818e80b89a5SGreg Kroah-Hartman static struct attribute *wmi_attrs[] = { 819e80b89a5SGreg Kroah-Hartman &dev_attr_modalias.attr, 820844af950SAndy Lutomirski &dev_attr_guid.attr, 821d79b1074SAndy Lutomirski &dev_attr_instance_count.attr, 822d79b1074SAndy Lutomirski &dev_attr_expensive.attr, 823cd3e3d29SBarnabás Pőcze NULL 824614ef432SDmitry Torokhov }; 825e80b89a5SGreg Kroah-Hartman ATTRIBUTE_GROUPS(wmi); 8261caab3c1SMatthew Garrett 827d79b1074SAndy Lutomirski static ssize_t notify_id_show(struct device *dev, struct device_attribute *attr, 828d79b1074SAndy Lutomirski char *buf) 829d79b1074SAndy Lutomirski { 830d79b1074SAndy Lutomirski struct wmi_block *wblock = dev_to_wblock(dev); 831d79b1074SAndy Lutomirski 8326133913aSBarnabás Pőcze return sysfs_emit(buf, "%02X\n", (unsigned int)wblock->gblock.notify_id); 833d79b1074SAndy Lutomirski } 834d79b1074SAndy Lutomirski static DEVICE_ATTR_RO(notify_id); 835d79b1074SAndy Lutomirski 836d79b1074SAndy Lutomirski static struct attribute *wmi_event_attrs[] = { 837d79b1074SAndy Lutomirski &dev_attr_notify_id.attr, 838cd3e3d29SBarnabás Pőcze NULL 839d79b1074SAndy Lutomirski }; 840d79b1074SAndy Lutomirski ATTRIBUTE_GROUPS(wmi_event); 841d79b1074SAndy Lutomirski 842d79b1074SAndy Lutomirski static ssize_t object_id_show(struct device *dev, struct device_attribute *attr, 843d79b1074SAndy Lutomirski char *buf) 844d79b1074SAndy Lutomirski { 845d79b1074SAndy Lutomirski struct wmi_block *wblock = dev_to_wblock(dev); 846d79b1074SAndy Lutomirski 8476133913aSBarnabás Pőcze return sysfs_emit(buf, "%c%c\n", wblock->gblock.object_id[0], 848d79b1074SAndy Lutomirski wblock->gblock.object_id[1]); 849d79b1074SAndy Lutomirski } 850d79b1074SAndy Lutomirski static DEVICE_ATTR_RO(object_id); 851d79b1074SAndy Lutomirski 852fd70da6aSDarren Hart (VMware) static ssize_t setable_show(struct device *dev, struct device_attribute *attr, 853d4fc91adSAndy Lutomirski char *buf) 854d4fc91adSAndy Lutomirski { 855d4fc91adSAndy Lutomirski struct wmi_device *wdev = dev_to_wdev(dev); 856d4fc91adSAndy Lutomirski 8576133913aSBarnabás Pőcze return sysfs_emit(buf, "%d\n", (int)wdev->setable); 858d4fc91adSAndy Lutomirski } 859fd70da6aSDarren Hart (VMware) static DEVICE_ATTR_RO(setable); 860d4fc91adSAndy Lutomirski 861d4fc91adSAndy Lutomirski static struct attribute *wmi_data_attrs[] = { 862d4fc91adSAndy Lutomirski &dev_attr_object_id.attr, 863fd70da6aSDarren Hart (VMware) &dev_attr_setable.attr, 864cd3e3d29SBarnabás Pőcze NULL 865d4fc91adSAndy Lutomirski }; 866d4fc91adSAndy Lutomirski ATTRIBUTE_GROUPS(wmi_data); 867d4fc91adSAndy Lutomirski 868d4fc91adSAndy Lutomirski static struct attribute *wmi_method_attrs[] = { 869d79b1074SAndy Lutomirski &dev_attr_object_id.attr, 870cd3e3d29SBarnabás Pőcze NULL 871d79b1074SAndy Lutomirski }; 872d4fc91adSAndy Lutomirski ATTRIBUTE_GROUPS(wmi_method); 873d79b1074SAndy Lutomirski 8742a81ada3SGreg Kroah-Hartman static int wmi_dev_uevent(const struct device *dev, struct kobj_uevent_env *env) 8751caab3c1SMatthew Garrett { 8762a81ada3SGreg Kroah-Hartman const struct wmi_block *wblock = dev_to_wblock(dev); 8771caab3c1SMatthew Garrett 87867f472fdSBarnabás Pőcze if (add_uevent_var(env, "MODALIAS=wmi:%pUL", &wblock->gblock.guid)) 8791caab3c1SMatthew Garrett return -ENOMEM; 8801caab3c1SMatthew Garrett 88167f472fdSBarnabás Pőcze if (add_uevent_var(env, "WMI_GUID=%pUL", &wblock->gblock.guid)) 8821caab3c1SMatthew Garrett return -ENOMEM; 8831caab3c1SMatthew Garrett 8841caab3c1SMatthew Garrett return 0; 8851caab3c1SMatthew Garrett } 8861caab3c1SMatthew Garrett 887844af950SAndy Lutomirski static void wmi_dev_release(struct device *dev) 8881caab3c1SMatthew Garrett { 889844af950SAndy Lutomirski struct wmi_block *wblock = dev_to_wblock(dev); 890c64eefd4SDmitry Torokhov 891844af950SAndy Lutomirski kfree(wblock); 8921caab3c1SMatthew Garrett } 8931caab3c1SMatthew Garrett 894844af950SAndy Lutomirski static int wmi_dev_match(struct device *dev, struct device_driver *driver) 895844af950SAndy Lutomirski { 896e7b2e334SBarnabás Pőcze struct wmi_driver *wmi_driver = drv_to_wdrv(driver); 897844af950SAndy Lutomirski struct wmi_block *wblock = dev_to_wblock(dev); 898844af950SAndy Lutomirski const struct wmi_device_id *id = wmi_driver->id_table; 899844af950SAndy Lutomirski 900c355ec65SMattias Jacobsson if (id == NULL) 901c355ec65SMattias Jacobsson return 0; 902c355ec65SMattias Jacobsson 903eacc95eaSMattias Jacobsson while (*id->guid_string) { 904028e6e20SAndy Shevchenko if (guid_parse_and_compare(id->guid_string, &wblock->gblock.guid)) 905844af950SAndy Lutomirski return 1; 906844af950SAndy Lutomirski 907844af950SAndy Lutomirski id++; 908844af950SAndy Lutomirski } 909844af950SAndy Lutomirski 910844af950SAndy Lutomirski return 0; 911844af950SAndy Lutomirski } 91244b6b766SMario Limonciello static int wmi_char_open(struct inode *inode, struct file *filp) 91344b6b766SMario Limonciello { 914*fb7b06b5SArmin Wolf /* 915*fb7b06b5SArmin Wolf * The miscdevice already stores a pointer to itself 916*fb7b06b5SArmin Wolf * inside filp->private_data 917*fb7b06b5SArmin Wolf */ 918*fb7b06b5SArmin Wolf struct wmi_block *wblock = container_of(filp->private_data, struct wmi_block, char_dev); 91944b6b766SMario Limonciello 92044b6b766SMario Limonciello filp->private_data = wblock; 92144b6b766SMario Limonciello 92244b6b766SMario Limonciello return nonseekable_open(inode, filp); 92344b6b766SMario Limonciello } 92444b6b766SMario Limonciello 92544b6b766SMario Limonciello static ssize_t wmi_char_read(struct file *filp, char __user *buffer, 92644b6b766SMario Limonciello size_t length, loff_t *offset) 92744b6b766SMario Limonciello { 92844b6b766SMario Limonciello struct wmi_block *wblock = filp->private_data; 92944b6b766SMario Limonciello 93044b6b766SMario Limonciello return simple_read_from_buffer(buffer, length, offset, 93144b6b766SMario Limonciello &wblock->req_buf_size, 93244b6b766SMario Limonciello sizeof(wblock->req_buf_size)); 93344b6b766SMario Limonciello } 93444b6b766SMario Limonciello 93544b6b766SMario Limonciello static long wmi_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 93644b6b766SMario Limonciello { 93744b6b766SMario Limonciello struct wmi_ioctl_buffer __user *input = 93844b6b766SMario Limonciello (struct wmi_ioctl_buffer __user *) arg; 93944b6b766SMario Limonciello struct wmi_block *wblock = filp->private_data; 94043aacf83SBarnabás Pőcze struct wmi_ioctl_buffer *buf; 94143aacf83SBarnabás Pőcze struct wmi_driver *wdriver; 94244b6b766SMario Limonciello int ret; 94344b6b766SMario Limonciello 94444b6b766SMario Limonciello if (_IOC_TYPE(cmd) != WMI_IOC) 94544b6b766SMario Limonciello return -ENOTTY; 94644b6b766SMario Limonciello 94744b6b766SMario Limonciello /* make sure we're not calling a higher instance than exists*/ 94844b6b766SMario Limonciello if (_IOC_NR(cmd) >= wblock->gblock.instance_count) 94944b6b766SMario Limonciello return -EINVAL; 95044b6b766SMario Limonciello 95144b6b766SMario Limonciello mutex_lock(&wblock->char_mutex); 95244b6b766SMario Limonciello buf = wblock->handler_data; 95344b6b766SMario Limonciello if (get_user(buf->length, &input->length)) { 95444b6b766SMario Limonciello dev_dbg(&wblock->dev.dev, "Read length from user failed\n"); 95544b6b766SMario Limonciello ret = -EFAULT; 95644b6b766SMario Limonciello goto out_ioctl; 95744b6b766SMario Limonciello } 95844b6b766SMario Limonciello /* if it's too small, abort */ 95944b6b766SMario Limonciello if (buf->length < wblock->req_buf_size) { 96044b6b766SMario Limonciello dev_err(&wblock->dev.dev, 96144b6b766SMario Limonciello "Buffer %lld too small, need at least %lld\n", 96244b6b766SMario Limonciello buf->length, wblock->req_buf_size); 96344b6b766SMario Limonciello ret = -EINVAL; 96444b6b766SMario Limonciello goto out_ioctl; 96544b6b766SMario Limonciello } 96644b6b766SMario Limonciello /* if it's too big, warn, driver will only use what is needed */ 96744b6b766SMario Limonciello if (buf->length > wblock->req_buf_size) 96844b6b766SMario Limonciello dev_warn(&wblock->dev.dev, 96944b6b766SMario Limonciello "Buffer %lld is bigger than required %lld\n", 97044b6b766SMario Limonciello buf->length, wblock->req_buf_size); 97144b6b766SMario Limonciello 97244b6b766SMario Limonciello /* copy the structure from userspace */ 97344b6b766SMario Limonciello if (copy_from_user(buf, input, wblock->req_buf_size)) { 97444b6b766SMario Limonciello dev_dbg(&wblock->dev.dev, "Copy %llu from user failed\n", 97544b6b766SMario Limonciello wblock->req_buf_size); 97644b6b766SMario Limonciello ret = -EFAULT; 97744b6b766SMario Limonciello goto out_ioctl; 97844b6b766SMario Limonciello } 97944b6b766SMario Limonciello 98044b6b766SMario Limonciello /* let the driver do any filtering and do the call */ 981e7b2e334SBarnabás Pőcze wdriver = drv_to_wdrv(wblock->dev.dev.driver); 9825e3e2297SMario Limonciello if (!try_module_get(wdriver->driver.owner)) { 9835e3e2297SMario Limonciello ret = -EBUSY; 9845e3e2297SMario Limonciello goto out_ioctl; 9855e3e2297SMario Limonciello } 98644b6b766SMario Limonciello ret = wdriver->filter_callback(&wblock->dev, cmd, buf); 98744b6b766SMario Limonciello module_put(wdriver->driver.owner); 98844b6b766SMario Limonciello if (ret) 98944b6b766SMario Limonciello goto out_ioctl; 99044b6b766SMario Limonciello 99144b6b766SMario Limonciello /* return the result (only up to our internal buffer size) */ 99244b6b766SMario Limonciello if (copy_to_user(input, buf, wblock->req_buf_size)) { 99344b6b766SMario Limonciello dev_dbg(&wblock->dev.dev, "Copy %llu to user failed\n", 99444b6b766SMario Limonciello wblock->req_buf_size); 99544b6b766SMario Limonciello ret = -EFAULT; 99644b6b766SMario Limonciello } 99744b6b766SMario Limonciello 99844b6b766SMario Limonciello out_ioctl: 99944b6b766SMario Limonciello mutex_unlock(&wblock->char_mutex); 100044b6b766SMario Limonciello return ret; 100144b6b766SMario Limonciello } 100244b6b766SMario Limonciello 100344b6b766SMario Limonciello static const struct file_operations wmi_fops = { 100444b6b766SMario Limonciello .owner = THIS_MODULE, 100544b6b766SMario Limonciello .read = wmi_char_read, 100644b6b766SMario Limonciello .open = wmi_char_open, 100744b6b766SMario Limonciello .unlocked_ioctl = wmi_ioctl, 10081832f2d8SArnd Bergmann .compat_ioctl = compat_ptr_ioctl, 100944b6b766SMario Limonciello }; 1010844af950SAndy Lutomirski 1011844af950SAndy Lutomirski static int wmi_dev_probe(struct device *dev) 1012844af950SAndy Lutomirski { 1013844af950SAndy Lutomirski struct wmi_block *wblock = dev_to_wblock(dev); 1014e7b2e334SBarnabás Pőcze struct wmi_driver *wdriver = drv_to_wdrv(dev->driver); 1015844af950SAndy Lutomirski int ret = 0; 101644b6b766SMario Limonciello char *buf; 1017844af950SAndy Lutomirski 1018285dd01aSBarnabás Pőcze if (ACPI_FAILURE(wmi_method_enable(wblock, true))) 1019844af950SAndy Lutomirski dev_warn(dev, "failed to enable device -- probing anyway\n"); 1020844af950SAndy Lutomirski 1021844af950SAndy Lutomirski if (wdriver->probe) { 1022440c4983SMattias Jacobsson ret = wdriver->probe(dev_to_wdev(dev), 1023440c4983SMattias Jacobsson find_guid_context(wblock, wdriver)); 102444b6b766SMario Limonciello if (ret != 0) 102544b6b766SMario Limonciello goto probe_failure; 1026844af950SAndy Lutomirski } 1027844af950SAndy Lutomirski 102844b6b766SMario Limonciello /* driver wants a character device made */ 102944b6b766SMario Limonciello if (wdriver->filter_callback) { 103044b6b766SMario Limonciello /* check that required buffer size declared by driver or MOF */ 103144b6b766SMario Limonciello if (!wblock->req_buf_size) { 103244b6b766SMario Limonciello dev_err(&wblock->dev.dev, 103344b6b766SMario Limonciello "Required buffer size not set\n"); 103444b6b766SMario Limonciello ret = -EINVAL; 103544b6b766SMario Limonciello goto probe_failure; 103644b6b766SMario Limonciello } 103744b6b766SMario Limonciello 10386fb74107SKees Cook wblock->handler_data = kmalloc(wblock->req_buf_size, 10396fb74107SKees Cook GFP_KERNEL); 104044b6b766SMario Limonciello if (!wblock->handler_data) { 104144b6b766SMario Limonciello ret = -ENOMEM; 104244b6b766SMario Limonciello goto probe_failure; 104344b6b766SMario Limonciello } 104444b6b766SMario Limonciello 10457f166addSAndy Shevchenko buf = kasprintf(GFP_KERNEL, "wmi/%s", wdriver->driver.name); 104644b6b766SMario Limonciello if (!buf) { 104744b6b766SMario Limonciello ret = -ENOMEM; 104844b6b766SMario Limonciello goto probe_string_failure; 104944b6b766SMario Limonciello } 105044b6b766SMario Limonciello wblock->char_dev.minor = MISC_DYNAMIC_MINOR; 105144b6b766SMario Limonciello wblock->char_dev.name = buf; 105244b6b766SMario Limonciello wblock->char_dev.fops = &wmi_fops; 105344b6b766SMario Limonciello wblock->char_dev.mode = 0444; 105444b6b766SMario Limonciello ret = misc_register(&wblock->char_dev); 105544b6b766SMario Limonciello if (ret) { 1056501f7e52SJoe Perches dev_warn(dev, "failed to register char dev: %d\n", ret); 105744b6b766SMario Limonciello ret = -ENOMEM; 105844b6b766SMario Limonciello goto probe_misc_failure; 105944b6b766SMario Limonciello } 106044b6b766SMario Limonciello } 106144b6b766SMario Limonciello 106299188786SHans de Goede set_bit(WMI_PROBED, &wblock->flags); 106344b6b766SMario Limonciello return 0; 106444b6b766SMario Limonciello 106544b6b766SMario Limonciello probe_misc_failure: 106644b6b766SMario Limonciello kfree(buf); 106744b6b766SMario Limonciello probe_string_failure: 106844b6b766SMario Limonciello kfree(wblock->handler_data); 106944b6b766SMario Limonciello probe_failure: 1070285dd01aSBarnabás Pőcze if (ACPI_FAILURE(wmi_method_enable(wblock, false))) 107144b6b766SMario Limonciello dev_warn(dev, "failed to disable device\n"); 1072844af950SAndy Lutomirski return ret; 1073844af950SAndy Lutomirski } 1074844af950SAndy Lutomirski 1075fc7a6209SUwe Kleine-König static void wmi_dev_remove(struct device *dev) 1076844af950SAndy Lutomirski { 1077844af950SAndy Lutomirski struct wmi_block *wblock = dev_to_wblock(dev); 1078e7b2e334SBarnabás Pőcze struct wmi_driver *wdriver = drv_to_wdrv(dev->driver); 1079844af950SAndy Lutomirski 108099188786SHans de Goede clear_bit(WMI_PROBED, &wblock->flags); 108199188786SHans de Goede 108244b6b766SMario Limonciello if (wdriver->filter_callback) { 108344b6b766SMario Limonciello misc_deregister(&wblock->char_dev); 108444b6b766SMario Limonciello kfree(wblock->char_dev.name); 10856fb74107SKees Cook kfree(wblock->handler_data); 108644b6b766SMario Limonciello } 108744b6b766SMario Limonciello 1088844af950SAndy Lutomirski if (wdriver->remove) 10892b329f56SUwe Kleine-König wdriver->remove(dev_to_wdev(dev)); 1090844af950SAndy Lutomirski 1091285dd01aSBarnabás Pőcze if (ACPI_FAILURE(wmi_method_enable(wblock, false))) 1092844af950SAndy Lutomirski dev_warn(dev, "failed to disable device\n"); 1093844af950SAndy Lutomirski } 1094844af950SAndy Lutomirski 1095844af950SAndy Lutomirski static struct class wmi_bus_class = { 1096844af950SAndy Lutomirski .name = "wmi_bus", 10971caab3c1SMatthew Garrett }; 10981caab3c1SMatthew Garrett 1099844af950SAndy Lutomirski static struct bus_type wmi_bus_type = { 1100844af950SAndy Lutomirski .name = "wmi", 1101844af950SAndy Lutomirski .dev_groups = wmi_groups, 1102844af950SAndy Lutomirski .match = wmi_dev_match, 1103844af950SAndy Lutomirski .uevent = wmi_dev_uevent, 1104844af950SAndy Lutomirski .probe = wmi_dev_probe, 1105844af950SAndy Lutomirski .remove = wmi_dev_remove, 1106844af950SAndy Lutomirski }; 1107844af950SAndy Lutomirski 110869372c1dSBhumika Goyal static const struct device_type wmi_type_event = { 1109d79b1074SAndy Lutomirski .name = "event", 1110d79b1074SAndy Lutomirski .groups = wmi_event_groups, 1111d79b1074SAndy Lutomirski .release = wmi_dev_release, 1112d79b1074SAndy Lutomirski }; 1113d79b1074SAndy Lutomirski 111469372c1dSBhumika Goyal static const struct device_type wmi_type_method = { 1115d79b1074SAndy Lutomirski .name = "method", 1116d4fc91adSAndy Lutomirski .groups = wmi_method_groups, 1117d79b1074SAndy Lutomirski .release = wmi_dev_release, 1118d79b1074SAndy Lutomirski }; 1119d79b1074SAndy Lutomirski 112069372c1dSBhumika Goyal static const struct device_type wmi_type_data = { 1121d79b1074SAndy Lutomirski .name = "data", 1122d4fc91adSAndy Lutomirski .groups = wmi_data_groups, 1123d79b1074SAndy Lutomirski .release = wmi_dev_release, 1124d79b1074SAndy Lutomirski }; 1125d79b1074SAndy Lutomirski 1126134038b0SMario Limonciello /* 1127134038b0SMario Limonciello * _WDG is a static list that is only parsed at startup, 1128134038b0SMario Limonciello * so it's safe to count entries without extra protection. 1129134038b0SMario Limonciello */ 1130134038b0SMario Limonciello static int guid_count(const guid_t *guid) 1131134038b0SMario Limonciello { 1132134038b0SMario Limonciello struct wmi_block *wblock; 1133134038b0SMario Limonciello int count = 0; 1134134038b0SMario Limonciello 1135134038b0SMario Limonciello list_for_each_entry(wblock, &wmi_block_list, list) { 1136134038b0SMario Limonciello if (guid_equal(&wblock->gblock.guid, guid)) 1137134038b0SMario Limonciello count++; 1138134038b0SMario Limonciello } 1139134038b0SMario Limonciello 1140134038b0SMario Limonciello return count; 1141134038b0SMario Limonciello } 1142134038b0SMario Limonciello 1143fd70da6aSDarren Hart (VMware) static int wmi_create_device(struct device *wmi_bus_dev, 11447f5809bfSAndy Lutomirski struct wmi_block *wblock, 11457f5809bfSAndy Lutomirski struct acpi_device *device) 11461caab3c1SMatthew Garrett { 1147d4fc91adSAndy Lutomirski struct acpi_device_info *info; 114857f2ce89SBarnabás Pőcze char method[WMI_ACPI_METHOD_NAME_SIZE]; 1149d4fc91adSAndy Lutomirski int result; 1150134038b0SMario Limonciello uint count; 1151d4fc91adSAndy Lutomirski 115284eacf7eSBarnabás Pőcze if (wblock->gblock.flags & ACPI_WMI_EVENT) { 1153fd70da6aSDarren Hart (VMware) wblock->dev.dev.type = &wmi_type_event; 1154fd70da6aSDarren Hart (VMware) goto out_init; 1155fd70da6aSDarren Hart (VMware) } 1156d4fc91adSAndy Lutomirski 115784eacf7eSBarnabás Pőcze if (wblock->gblock.flags & ACPI_WMI_METHOD) { 1158fd70da6aSDarren Hart (VMware) wblock->dev.dev.type = &wmi_type_method; 115944b6b766SMario Limonciello mutex_init(&wblock->char_mutex); 1160fd70da6aSDarren Hart (VMware) goto out_init; 1161fd70da6aSDarren Hart (VMware) } 1162fd70da6aSDarren Hart (VMware) 1163fd70da6aSDarren Hart (VMware) /* 1164fd70da6aSDarren Hart (VMware) * Data Block Query Control Method (WQxx by convention) is 1165fd70da6aSDarren Hart (VMware) * required per the WMI documentation. If it is not present, 1166fd70da6aSDarren Hart (VMware) * we ignore this data block. 1167fd70da6aSDarren Hart (VMware) */ 116857f2ce89SBarnabás Pőcze get_acpi_method_name(wblock, 'Q', method); 1169d4fc91adSAndy Lutomirski result = get_subobj_info(device->handle, method, &info); 1170d4fc91adSAndy Lutomirski 1171fd70da6aSDarren Hart (VMware) if (result) { 1172fd70da6aSDarren Hart (VMware) dev_warn(wmi_bus_dev, 1173501f7e52SJoe Perches "%s data block query control method not found\n", 1174fd70da6aSDarren Hart (VMware) method); 1175fd70da6aSDarren Hart (VMware) return result; 1176fd70da6aSDarren Hart (VMware) } 1177fd70da6aSDarren Hart (VMware) 1178fd70da6aSDarren Hart (VMware) wblock->dev.dev.type = &wmi_type_data; 1179d4fc91adSAndy Lutomirski 1180d4fc91adSAndy Lutomirski /* 1181d4fc91adSAndy Lutomirski * The Microsoft documentation specifically states: 1182d4fc91adSAndy Lutomirski * 1183d4fc91adSAndy Lutomirski * Data blocks registered with only a single instance 1184d4fc91adSAndy Lutomirski * can ignore the parameter. 1185d4fc91adSAndy Lutomirski * 1186fd70da6aSDarren Hart (VMware) * ACPICA will get mad at us if we call the method with the wrong number 1187fd70da6aSDarren Hart (VMware) * of arguments, so check what our method expects. (On some Dell 1188fd70da6aSDarren Hart (VMware) * laptops, WQxx may not be a method at all.) 1189d4fc91adSAndy Lutomirski */ 1190fd70da6aSDarren Hart (VMware) if (info->type != ACPI_TYPE_METHOD || info->param_count == 0) 1191a90b38c5SHans de Goede set_bit(WMI_READ_TAKES_NO_ARGS, &wblock->flags); 1192d4fc91adSAndy Lutomirski 1193d4fc91adSAndy Lutomirski kfree(info); 1194d4fc91adSAndy Lutomirski 119557f2ce89SBarnabás Pőcze get_acpi_method_name(wblock, 'S', method); 1196d4fc91adSAndy Lutomirski result = get_subobj_info(device->handle, method, NULL); 1197d4fc91adSAndy Lutomirski 1198fd70da6aSDarren Hart (VMware) if (result == 0) 1199fd70da6aSDarren Hart (VMware) wblock->dev.setable = true; 1200d4fc91adSAndy Lutomirski 1201fd70da6aSDarren Hart (VMware) out_init: 1202fd70da6aSDarren Hart (VMware) wblock->dev.dev.bus = &wmi_bus_type; 1203fd70da6aSDarren Hart (VMware) wblock->dev.dev.parent = wmi_bus_dev; 1204fd70da6aSDarren Hart (VMware) 1205134038b0SMario Limonciello count = guid_count(&wblock->gblock.guid); 1206134038b0SMario Limonciello if (count) 1207134038b0SMario Limonciello dev_set_name(&wblock->dev.dev, "%pUL-%d", &wblock->gblock.guid, count); 1208134038b0SMario Limonciello else 120967f472fdSBarnabás Pőcze dev_set_name(&wblock->dev.dev, "%pUL", &wblock->gblock.guid); 1210c64eefd4SDmitry Torokhov 12116ee50aaaSDarren Hart (VMware) device_initialize(&wblock->dev.dev); 1212fd70da6aSDarren Hart (VMware) 1213fd70da6aSDarren Hart (VMware) return 0; 12141caab3c1SMatthew Garrett } 12151caab3c1SMatthew Garrett 1216b0e86302SAndy Lutomirski static void wmi_free_devices(struct acpi_device *device) 12171caab3c1SMatthew Garrett { 1218c64eefd4SDmitry Torokhov struct wmi_block *wblock, *next; 12191caab3c1SMatthew Garrett 12201caab3c1SMatthew Garrett /* Delete devices for all the GUIDs */ 1221023b9565SDmitry Torokhov list_for_each_entry_safe(wblock, next, &wmi_block_list, list) { 1222b0e86302SAndy Lutomirski if (wblock->acpi_device == device) { 1223023b9565SDmitry Torokhov list_del(&wblock->list); 1224844af950SAndy Lutomirski device_unregister(&wblock->dev.dev); 1225023b9565SDmitry Torokhov } 12261caab3c1SMatthew Garrett } 1227b0e86302SAndy Lutomirski } 12281caab3c1SMatthew Garrett 1229134038b0SMario Limonciello static bool guid_already_parsed_for_legacy(struct acpi_device *device, const guid_t *guid) 1230d1f9e497SCarlos Corbacho { 1231d1f9e497SCarlos Corbacho struct wmi_block *wblock; 1232d1f9e497SCarlos Corbacho 1233b0e86302SAndy Lutomirski list_for_each_entry(wblock, &wmi_block_list, list) { 1234134038b0SMario Limonciello /* skip warning and register if we know the driver will use struct wmi_driver */ 1235134038b0SMario Limonciello for (int i = 0; allow_duplicates[i] != NULL; i++) { 12366bf06f14SAndy Shevchenko if (guid_parse_and_compare(allow_duplicates[i], guid)) 1237134038b0SMario Limonciello return false; 1238134038b0SMario Limonciello } 123967f472fdSBarnabás Pőcze if (guid_equal(&wblock->gblock.guid, guid)) { 1240b0e86302SAndy Lutomirski /* 1241b0e86302SAndy Lutomirski * Because we historically didn't track the relationship 1242b0e86302SAndy Lutomirski * between GUIDs and ACPI nodes, we don't know whether 1243b0e86302SAndy Lutomirski * we need to suppress GUIDs that are unique on a 1244b0e86302SAndy Lutomirski * given node but duplicated across nodes. 1245b0e86302SAndy Lutomirski */ 1246b0e86302SAndy Lutomirski dev_warn(&device->dev, "duplicate WMI GUID %pUL (first instance was on %s)\n", 1247b0e86302SAndy Lutomirski guid, dev_name(&wblock->acpi_device->dev)); 1248d1f9e497SCarlos Corbacho return true; 1249b0e86302SAndy Lutomirski } 1250b0e86302SAndy Lutomirski } 1251c64eefd4SDmitry Torokhov 1252d1f9e497SCarlos Corbacho return false; 1253d1f9e497SCarlos Corbacho } 1254d1f9e497SCarlos Corbacho 12551caab3c1SMatthew Garrett /* 1256b4f9fe12SLen Brown * Parse the _WDG method for the GUID data blocks 1257b4f9fe12SLen Brown */ 1258844af950SAndy Lutomirski static int parse_wdg(struct device *wmi_bus_dev, struct acpi_device *device) 1259b4f9fe12SLen Brown { 1260b4f9fe12SLen Brown struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL}; 126137830662SDmitry Torokhov const struct guid_block *gblock; 12626ee50aaaSDarren Hart (VMware) struct wmi_block *wblock, *next; 12636ee50aaaSDarren Hart (VMware) union acpi_object *obj; 1264b4f9fe12SLen Brown acpi_status status; 1265b4f9fe12SLen Brown u32 i, total; 1266328d6725SArmin Wolf int retval; 1267b4f9fe12SLen Brown 12687f5809bfSAndy Lutomirski status = acpi_evaluate_object(device->handle, "_WDG", NULL, &out); 1269b4f9fe12SLen Brown if (ACPI_FAILURE(status)) 1270c64eefd4SDmitry Torokhov return -ENXIO; 1271b4f9fe12SLen Brown 1272c06a2fdeSBarnabás Pőcze obj = out.pointer; 12733d2c63ebSDmitry Torokhov if (!obj) 1274c64eefd4SDmitry Torokhov return -ENXIO; 1275b4f9fe12SLen Brown 127664ed0ab8SDmitry Torokhov if (obj->type != ACPI_TYPE_BUFFER) { 1277328d6725SArmin Wolf kfree(obj); 1278328d6725SArmin Wolf return -ENXIO; 127964ed0ab8SDmitry Torokhov } 1280b4f9fe12SLen Brown 128137830662SDmitry Torokhov gblock = (const struct guid_block *)obj->buffer.pointer; 1282b4f9fe12SLen Brown total = obj->buffer.length / sizeof(struct guid_block); 1283b4f9fe12SLen Brown 1284b4f9fe12SLen Brown for (i = 0; i < total; i++) { 1285a929aae0SThomas Renninger if (debug_dump_wdg) 1286a929aae0SThomas Renninger wmi_dump_wdg(&gblock[i]); 1287a929aae0SThomas Renninger 1288134038b0SMario Limonciello if (guid_already_parsed_for_legacy(device, &gblock[i].guid)) 1289a1c31bcdSAndy Lutomirski continue; 1290a1c31bcdSAndy Lutomirski 12917410b8e6SBarnabás Pőcze wblock = kzalloc(sizeof(*wblock), GFP_KERNEL); 12926ee50aaaSDarren Hart (VMware) if (!wblock) { 1293328d6725SArmin Wolf dev_err(wmi_bus_dev, "Failed to allocate %pUL\n", &gblock[i].guid); 1294328d6725SArmin Wolf continue; 12956ee50aaaSDarren Hart (VMware) } 129658f6425eSColin King 1297b0e86302SAndy Lutomirski wblock->acpi_device = device; 129858f6425eSColin King wblock->gblock = gblock[i]; 129958f6425eSColin King 130084eacf7eSBarnabás Pőcze retval = wmi_create_device(wmi_bus_dev, wblock, device); 1301fd70da6aSDarren Hart (VMware) if (retval) { 1302fd70da6aSDarren Hart (VMware) kfree(wblock); 1303fd70da6aSDarren Hart (VMware) continue; 1304fd70da6aSDarren Hart (VMware) } 130558f6425eSColin King 130658f6425eSColin King list_add_tail(&wblock->list, &wmi_block_list); 1307b4f9fe12SLen Brown 1308fc3155b2SThomas Renninger if (debug_event) { 1309fc3155b2SThomas Renninger wblock->handler = wmi_notify_debug; 1310285dd01aSBarnabás Pőcze wmi_method_enable(wblock, true); 1311fc3155b2SThomas Renninger } 1312b4f9fe12SLen Brown } 1313b4f9fe12SLen Brown 13146ee50aaaSDarren Hart (VMware) /* 13156ee50aaaSDarren Hart (VMware) * Now that all of the devices are created, add them to the 13166ee50aaaSDarren Hart (VMware) * device tree and probe subdrivers. 13176ee50aaaSDarren Hart (VMware) */ 13186ee50aaaSDarren Hart (VMware) list_for_each_entry_safe(wblock, next, &wmi_block_list, list) { 13196ee50aaaSDarren Hart (VMware) if (wblock->acpi_device != device) 13206ee50aaaSDarren Hart (VMware) continue; 13216ee50aaaSDarren Hart (VMware) 13226ee50aaaSDarren Hart (VMware) retval = device_add(&wblock->dev.dev); 13236ee50aaaSDarren Hart (VMware) if (retval) { 1324501f7e52SJoe Perches dev_err(wmi_bus_dev, "failed to register %pUL\n", 132567f472fdSBarnabás Pőcze &wblock->gblock.guid); 13266ee50aaaSDarren Hart (VMware) if (debug_event) 1327285dd01aSBarnabás Pőcze wmi_method_enable(wblock, false); 13286ee50aaaSDarren Hart (VMware) list_del(&wblock->list); 13296ee50aaaSDarren Hart (VMware) put_device(&wblock->dev.dev); 13306ee50aaaSDarren Hart (VMware) } 13316ee50aaaSDarren Hart (VMware) } 1332c64eefd4SDmitry Torokhov 1333328d6725SArmin Wolf kfree(obj); 1334328d6725SArmin Wolf 1335328d6725SArmin Wolf return 0; 1336b4f9fe12SLen Brown } 1337b4f9fe12SLen Brown 1338b4f9fe12SLen Brown /* 1339b4f9fe12SLen Brown * WMI can have EmbeddedControl access regions. In which case, we just want to 1340b4f9fe12SLen Brown * hand these off to the EC driver. 1341b4f9fe12SLen Brown */ 1342b4f9fe12SLen Brown static acpi_status 1343b4f9fe12SLen Brown acpi_wmi_ec_space_handler(u32 function, acpi_physical_address address, 1344439913ffSLin Ming u32 bits, u64 *value, 1345b4f9fe12SLen Brown void *handler_context, void *region_context) 1346b4f9fe12SLen Brown { 1347b4f9fe12SLen Brown int result = 0, i = 0; 1348b4f9fe12SLen Brown u8 temp = 0; 1349b4f9fe12SLen Brown 1350b4f9fe12SLen Brown if ((address > 0xFF) || !value) 1351b4f9fe12SLen Brown return AE_BAD_PARAMETER; 1352b4f9fe12SLen Brown 1353b4f9fe12SLen Brown if (function != ACPI_READ && function != ACPI_WRITE) 1354b4f9fe12SLen Brown return AE_BAD_PARAMETER; 1355b4f9fe12SLen Brown 1356b4f9fe12SLen Brown if (bits != 8) 1357b4f9fe12SLen Brown return AE_BAD_PARAMETER; 1358b4f9fe12SLen Brown 1359b4f9fe12SLen Brown if (function == ACPI_READ) { 1360b4f9fe12SLen Brown result = ec_read(address, &temp); 1361439913ffSLin Ming (*value) |= ((u64)temp) << i; 1362b4f9fe12SLen Brown } else { 1363b4f9fe12SLen Brown temp = 0xff & ((*value) >> i); 1364b4f9fe12SLen Brown result = ec_write(address, temp); 1365b4f9fe12SLen Brown } 1366b4f9fe12SLen Brown 1367b4f9fe12SLen Brown switch (result) { 1368b4f9fe12SLen Brown case -EINVAL: 1369b4f9fe12SLen Brown return AE_BAD_PARAMETER; 1370b4f9fe12SLen Brown case -ENODEV: 1371b4f9fe12SLen Brown return AE_NOT_FOUND; 1372b4f9fe12SLen Brown case -ETIME: 1373b4f9fe12SLen Brown return AE_TIME; 1374b4f9fe12SLen Brown default: 1375b4f9fe12SLen Brown return AE_OK; 1376b4f9fe12SLen Brown } 1377b4f9fe12SLen Brown } 1378b4f9fe12SLen Brown 13791686f544SAndy Lutomirski static void acpi_wmi_notify_handler(acpi_handle handle, u32 event, 13801686f544SAndy Lutomirski void *context) 1381b4f9fe12SLen Brown { 1382264e8de2SJakob Koschel struct wmi_block *wblock = NULL, *iter; 1383b4f9fe12SLen Brown 1384264e8de2SJakob Koschel list_for_each_entry(iter, &wmi_block_list, list) { 1385264e8de2SJakob Koschel struct guid_block *block = &iter->gblock; 1386b4f9fe12SLen Brown 1387264e8de2SJakob Koschel if (iter->acpi_device->handle == handle && 1388b0e86302SAndy Lutomirski (block->flags & ACPI_WMI_EVENT) && 13893ecace31SBarnabás Pőcze (block->notify_id == event)) { 1390264e8de2SJakob Koschel wblock = iter; 13911686f544SAndy Lutomirski break; 13921686f544SAndy Lutomirski } 13931686f544SAndy Lutomirski } 13941686f544SAndy Lutomirski 1395264e8de2SJakob Koschel if (!wblock) 13961686f544SAndy Lutomirski return; 13971686f544SAndy Lutomirski 13981686f544SAndy Lutomirski /* If a driver is bound, then notify the driver. */ 139999188786SHans de Goede if (test_bit(WMI_PROBED, &wblock->flags) && wblock->dev.dev.driver) { 1400e7b2e334SBarnabás Pőcze struct wmi_driver *driver = drv_to_wdrv(wblock->dev.dev.driver); 14011686f544SAndy Lutomirski struct acpi_buffer evdata = { ACPI_ALLOCATE_BUFFER, NULL }; 14021686f544SAndy Lutomirski acpi_status status; 14031686f544SAndy Lutomirski 14048c33915dSHans de Goede if (!driver->no_notify_data) { 140525be44f6SBarnabás Pőcze status = get_event_data(wblock, &evdata); 14061686f544SAndy Lutomirski if (ACPI_FAILURE(status)) { 140725be44f6SBarnabás Pőcze dev_warn(&wblock->dev.dev, "failed to get event data\n"); 14081686f544SAndy Lutomirski return; 14091686f544SAndy Lutomirski } 14108c33915dSHans de Goede } 14111686f544SAndy Lutomirski 14121686f544SAndy Lutomirski if (driver->notify) 1413c06a2fdeSBarnabás Pőcze driver->notify(&wblock->dev, evdata.pointer); 14141686f544SAndy Lutomirski 14151686f544SAndy Lutomirski kfree(evdata.pointer); 14161686f544SAndy Lutomirski } else if (wblock->handler) { 14171686f544SAndy Lutomirski /* Legacy handler */ 1418b4f9fe12SLen Brown wblock->handler(event, wblock->handler_data); 14191686f544SAndy Lutomirski } 14201686f544SAndy Lutomirski 14216701cc8fSAndy Shevchenko if (debug_event) 14221c23ab91SBarnabás Pőcze pr_info("DEBUG: GUID %pUL event 0x%02X\n", &wblock->gblock.guid, event); 1423b4f9fe12SLen Brown 1424b4f9fe12SLen Brown acpi_bus_generate_netlink_event( 14251686f544SAndy Lutomirski wblock->acpi_device->pnp.device_class, 14261686f544SAndy Lutomirski dev_name(&wblock->dev.dev), 1427b4f9fe12SLen Brown event, 0); 1428b4f9fe12SLen Brown } 1429b4f9fe12SLen Brown 14300f16136bSUwe Kleine-König static void acpi_wmi_remove(struct platform_device *device) 1431b4f9fe12SLen Brown { 14329599ed91SAndy Lutomirski struct acpi_device *acpi_device = ACPI_COMPANION(&device->dev); 14339599ed91SAndy Lutomirski 1434b8d4d350SMikalai Ramanovich acpi_remove_notify_handler(acpi_device->handle, ACPI_ALL_NOTIFY, 14351686f544SAndy Lutomirski acpi_wmi_notify_handler); 14369599ed91SAndy Lutomirski acpi_remove_address_space_handler(acpi_device->handle, 1437b4f9fe12SLen Brown ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler); 14389599ed91SAndy Lutomirski wmi_free_devices(acpi_device); 1439c06a2fdeSBarnabás Pőcze device_unregister(dev_get_drvdata(&device->dev)); 1440b4f9fe12SLen Brown } 1441b4f9fe12SLen Brown 14429599ed91SAndy Lutomirski static int acpi_wmi_probe(struct platform_device *device) 1443b4f9fe12SLen Brown { 14449599ed91SAndy Lutomirski struct acpi_device *acpi_device; 1445844af950SAndy Lutomirski struct device *wmi_bus_dev; 1446b4f9fe12SLen Brown acpi_status status; 1447c64eefd4SDmitry Torokhov int error; 1448b4f9fe12SLen Brown 14499599ed91SAndy Lutomirski acpi_device = ACPI_COMPANION(&device->dev); 14509599ed91SAndy Lutomirski if (!acpi_device) { 14519599ed91SAndy Lutomirski dev_err(&device->dev, "ACPI companion is missing\n"); 14529599ed91SAndy Lutomirski return -ENODEV; 14539599ed91SAndy Lutomirski } 14549599ed91SAndy Lutomirski 14559599ed91SAndy Lutomirski status = acpi_install_address_space_handler(acpi_device->handle, 1456b4f9fe12SLen Brown ACPI_ADR_SPACE_EC, 1457b4f9fe12SLen Brown &acpi_wmi_ec_space_handler, 1458b4f9fe12SLen Brown NULL, NULL); 14595212cd67SDmitry Torokhov if (ACPI_FAILURE(status)) { 146046492ee4SAndy Lutomirski dev_err(&device->dev, "Error installing EC region handler\n"); 1461b4f9fe12SLen Brown return -ENODEV; 14625212cd67SDmitry Torokhov } 1463b4f9fe12SLen Brown 14649599ed91SAndy Lutomirski status = acpi_install_notify_handler(acpi_device->handle, 1465b8d4d350SMikalai Ramanovich ACPI_ALL_NOTIFY, 14661686f544SAndy Lutomirski acpi_wmi_notify_handler, 14671686f544SAndy Lutomirski NULL); 14681686f544SAndy Lutomirski if (ACPI_FAILURE(status)) { 14691686f544SAndy Lutomirski dev_err(&device->dev, "Error installing notify handler\n"); 14701686f544SAndy Lutomirski error = -ENODEV; 14711686f544SAndy Lutomirski goto err_remove_ec_handler; 14721686f544SAndy Lutomirski } 14731686f544SAndy Lutomirski 1474844af950SAndy Lutomirski wmi_bus_dev = device_create(&wmi_bus_class, &device->dev, MKDEV(0, 0), 1475844af950SAndy Lutomirski NULL, "wmi_bus-%s", dev_name(&device->dev)); 1476844af950SAndy Lutomirski if (IS_ERR(wmi_bus_dev)) { 1477844af950SAndy Lutomirski error = PTR_ERR(wmi_bus_dev); 14781686f544SAndy Lutomirski goto err_remove_notify_handler; 1479844af950SAndy Lutomirski } 14809599ed91SAndy Lutomirski dev_set_drvdata(&device->dev, wmi_bus_dev); 1481844af950SAndy Lutomirski 14829599ed91SAndy Lutomirski error = parse_wdg(wmi_bus_dev, acpi_device); 1483c64eefd4SDmitry Torokhov if (error) { 14848e07514dSDmitry Torokhov pr_err("Failed to parse WDG method\n"); 1485844af950SAndy Lutomirski goto err_remove_busdev; 1486b4f9fe12SLen Brown } 1487b4f9fe12SLen Brown 1488c64eefd4SDmitry Torokhov return 0; 148946492ee4SAndy Lutomirski 1490844af950SAndy Lutomirski err_remove_busdev: 149156afb8d4SYongxin Liu device_unregister(wmi_bus_dev); 1492844af950SAndy Lutomirski 14931686f544SAndy Lutomirski err_remove_notify_handler: 1494b8d4d350SMikalai Ramanovich acpi_remove_notify_handler(acpi_device->handle, ACPI_ALL_NOTIFY, 14951686f544SAndy Lutomirski acpi_wmi_notify_handler); 14961686f544SAndy Lutomirski 14971686f544SAndy Lutomirski err_remove_ec_handler: 14989599ed91SAndy Lutomirski acpi_remove_address_space_handler(acpi_device->handle, 149946492ee4SAndy Lutomirski ACPI_ADR_SPACE_EC, 150046492ee4SAndy Lutomirski &acpi_wmi_ec_space_handler); 150146492ee4SAndy Lutomirski 150246492ee4SAndy Lutomirski return error; 1503b4f9fe12SLen Brown } 1504b4f9fe12SLen Brown 1505844af950SAndy Lutomirski int __must_check __wmi_driver_register(struct wmi_driver *driver, 1506844af950SAndy Lutomirski struct module *owner) 1507844af950SAndy Lutomirski { 1508844af950SAndy Lutomirski driver->driver.owner = owner; 1509844af950SAndy Lutomirski driver->driver.bus = &wmi_bus_type; 1510844af950SAndy Lutomirski 1511844af950SAndy Lutomirski return driver_register(&driver->driver); 1512844af950SAndy Lutomirski } 1513844af950SAndy Lutomirski EXPORT_SYMBOL(__wmi_driver_register); 1514844af950SAndy Lutomirski 1515b4cc9795SArmin Wolf /** 1516b4cc9795SArmin Wolf * wmi_driver_unregister() - Unregister a WMI driver 1517b4cc9795SArmin Wolf * @driver: WMI driver to unregister 1518b4cc9795SArmin Wolf * 1519b4cc9795SArmin Wolf * Unregisters a WMI driver from the WMI bus. 1520b4cc9795SArmin Wolf */ 1521844af950SAndy Lutomirski void wmi_driver_unregister(struct wmi_driver *driver) 1522844af950SAndy Lutomirski { 1523844af950SAndy Lutomirski driver_unregister(&driver->driver); 1524844af950SAndy Lutomirski } 1525844af950SAndy Lutomirski EXPORT_SYMBOL(wmi_driver_unregister); 1526844af950SAndy Lutomirski 1527c710765aSUwe Kleine-König static struct platform_driver acpi_wmi_driver = { 1528c710765aSUwe Kleine-König .driver = { 1529c710765aSUwe Kleine-König .name = "acpi-wmi", 1530c710765aSUwe Kleine-König .acpi_match_table = wmi_device_ids, 1531c710765aSUwe Kleine-König }, 1532c710765aSUwe Kleine-König .probe = acpi_wmi_probe, 15330f16136bSUwe Kleine-König .remove_new = acpi_wmi_remove, 1534c710765aSUwe Kleine-König }; 1535c710765aSUwe Kleine-König 1536b4f9fe12SLen Brown static int __init acpi_wmi_init(void) 1537b4f9fe12SLen Brown { 1538c64eefd4SDmitry Torokhov int error; 1539b4f9fe12SLen Brown 1540b4f9fe12SLen Brown if (acpi_disabled) 1541b4f9fe12SLen Brown return -ENODEV; 1542b4f9fe12SLen Brown 1543844af950SAndy Lutomirski error = class_register(&wmi_bus_class); 1544c64eefd4SDmitry Torokhov if (error) 1545c64eefd4SDmitry Torokhov return error; 1546b4f9fe12SLen Brown 1547844af950SAndy Lutomirski error = bus_register(&wmi_bus_type); 1548844af950SAndy Lutomirski if (error) 1549844af950SAndy Lutomirski goto err_unreg_class; 1550844af950SAndy Lutomirski 15519599ed91SAndy Lutomirski error = platform_driver_register(&acpi_wmi_driver); 1552c64eefd4SDmitry Torokhov if (error) { 1553c64eefd4SDmitry Torokhov pr_err("Error loading mapper\n"); 1554844af950SAndy Lutomirski goto err_unreg_bus; 15551caab3c1SMatthew Garrett } 15561caab3c1SMatthew Garrett 15578e07514dSDmitry Torokhov return 0; 1558844af950SAndy Lutomirski 1559844af950SAndy Lutomirski err_unreg_bus: 1560844af950SAndy Lutomirski bus_unregister(&wmi_bus_type); 1561844af950SAndy Lutomirski 156297277717SAlexey Khoroshilov err_unreg_class: 156397277717SAlexey Khoroshilov class_unregister(&wmi_bus_class); 156497277717SAlexey Khoroshilov 1565844af950SAndy Lutomirski return error; 1566b4f9fe12SLen Brown } 1567b4f9fe12SLen Brown 1568b4f9fe12SLen Brown static void __exit acpi_wmi_exit(void) 1569b4f9fe12SLen Brown { 15709599ed91SAndy Lutomirski platform_driver_unregister(&acpi_wmi_driver); 1571844af950SAndy Lutomirski bus_unregister(&wmi_bus_type); 1572303d1fccSMario Limonciello class_unregister(&wmi_bus_class); 1573b4f9fe12SLen Brown } 1574b4f9fe12SLen Brown 157598b8e4e5SRafael J. Wysocki subsys_initcall_sync(acpi_wmi_init); 1576b4f9fe12SLen Brown module_exit(acpi_wmi_exit); 1577