diff options
Diffstat (limited to 'drivers/platform/x86/wmi.c')
| -rw-r--r-- | drivers/platform/x86/wmi.c | 212 |
1 files changed, 108 insertions, 104 deletions
diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 104b77c87ef..43d13295e63 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -36,8 +36,7 @@ #include <linux/list.h> #include <linux/acpi.h> #include <linux/slab.h> -#include <acpi/acpi_bus.h> -#include <acpi/acpi_drivers.h> +#include <linux/module.h> ACPI_MODULE_NAME("wmi"); MODULE_AUTHOR("Carlos Corbacho"); @@ -81,17 +80,17 @@ struct wmi_block { #define ACPI_WMI_STRING 0x4 /* GUID takes & returns a string */ #define ACPI_WMI_EVENT 0x8 /* GUID is an event */ -static int debug_event; +static bool debug_event; module_param(debug_event, bool, 0444); MODULE_PARM_DESC(debug_event, "Log WMI Events [0/1]"); -static int debug_dump_wdg; +static bool debug_dump_wdg; module_param(debug_dump_wdg, bool, 0444); MODULE_PARM_DESC(debug_dump_wdg, "Dump available WMI interfaces [0/1]"); -static int acpi_wmi_remove(struct acpi_device *device, int type); +static int acpi_wmi_remove(struct acpi_device *device); static int acpi_wmi_add(struct acpi_device *device); static void acpi_wmi_notify(struct acpi_device *device, u32 event); @@ -251,8 +250,6 @@ static acpi_status wmi_method_enable(struct wmi_block *wblock, int enable) { struct guid_block *block = NULL; char method[5]; - struct acpi_object_list input; - union acpi_object params[1]; acpi_status status; acpi_handle handle; @@ -262,13 +259,9 @@ static acpi_status wmi_method_enable(struct wmi_block *wblock, int enable) if (!block) return AE_NOT_EXIST; - input.count = 1; - input.pointer = params; - params[0].type = ACPI_TYPE_INTEGER; - params[0].integer.value = enable; snprintf(method, 5, "WE%02X", block->notify_id); - status = acpi_evaluate_object(handle, method, &input, NULL); + status = acpi_execute_simple_method(handle, method, enable); if (status != AE_OK && status != AE_NOT_FOUND) return status; @@ -352,10 +345,10 @@ struct acpi_buffer *out) { struct guid_block *block = NULL; struct wmi_block *wblock = NULL; - acpi_handle handle, wc_handle; + acpi_handle handle; acpi_status status, wc_status = AE_ERROR; - struct acpi_object_list input, wc_input; - union acpi_object wc_params[1], wq_params[1]; + struct acpi_object_list input; + union acpi_object wq_params[1]; char method[5]; char wc_method[5] = "WC"; @@ -385,11 +378,6 @@ struct acpi_buffer *out) * enable collection. */ if (block->flags & ACPI_WMI_EXPENSIVE) { - wc_input.count = 1; - wc_input.pointer = wc_params; - wc_params[0].type = ACPI_TYPE_INTEGER; - wc_params[0].integer.value = 1; - strncat(wc_method, block->object_id, 2); /* @@ -397,10 +385,9 @@ struct acpi_buffer *out) * expensive, but have no corresponding WCxx method. So we * should not fail if this happens. */ - wc_status = acpi_get_handle(handle, wc_method, &wc_handle); - if (ACPI_SUCCESS(wc_status)) - wc_status = acpi_evaluate_object(handle, wc_method, - &wc_input, NULL); + if (acpi_has_method(handle, wc_method)) + wc_status = acpi_execute_simple_method(handle, + wc_method, 1); } strcpy(method, "WQ"); @@ -413,9 +400,7 @@ struct acpi_buffer *out) * the WQxx method failed - we should disable collection anyway. */ if ((block->flags & ACPI_WMI_EXPENSIVE) && ACPI_SUCCESS(wc_status)) { - wc_params[0].integer.value = 0; - status = acpi_evaluate_object(handle, - wc_method, &wc_input, NULL); + status = acpi_execute_simple_method(handle, wc_method, 0); } return status; @@ -486,16 +471,16 @@ static void wmi_dump_wdg(const struct guid_block *g) pr_info("\tnotify_id: %02X\n", g->notify_id); pr_info("\treserved: %02X\n", g->reserved); pr_info("\tinstance_count: %d\n", g->instance_count); - pr_info("\tflags: %#x ", g->flags); + pr_info("\tflags: %#x", g->flags); if (g->flags) { if (g->flags & ACPI_WMI_EXPENSIVE) - pr_cont("ACPI_WMI_EXPENSIVE "); + pr_cont(" ACPI_WMI_EXPENSIVE"); if (g->flags & ACPI_WMI_METHOD) - pr_cont("ACPI_WMI_METHOD "); + pr_cont(" ACPI_WMI_METHOD"); if (g->flags & ACPI_WMI_STRING) - pr_cont("ACPI_WMI_STRING "); + pr_cont(" ACPI_WMI_STRING"); if (g->flags & ACPI_WMI_EVENT) - pr_cont("ACPI_WMI_EVENT "); + pr_cont(" ACPI_WMI_EVENT"); } pr_cont("\n"); @@ -549,21 +534,34 @@ acpi_status wmi_install_notify_handler(const char *guid, wmi_notify_handler handler, void *data) { struct wmi_block *block; - acpi_status status; + acpi_status status = AE_NOT_EXIST; + char tmp[16], guid_input[16]; + struct list_head *p; if (!guid || !handler) return AE_BAD_PARAMETER; - if (!find_guid(guid, &block)) - return AE_NOT_EXIST; + wmi_parse_guid(guid, tmp); + wmi_swap_bytes(tmp, guid_input); - if (block->handler && block->handler != wmi_notify_debug) - return AE_ALREADY_ACQUIRED; + list_for_each(p, &wmi_block_list) { + acpi_status wmi_status; + block = list_entry(p, struct wmi_block, list); - block->handler = handler; - block->handler_data = data; + if (memcmp(block->gblock.guid, guid_input, 16) == 0) { + if (block->handler && + block->handler != wmi_notify_debug) + return AE_ALREADY_ACQUIRED; - status = wmi_method_enable(block, 1); + block->handler = handler; + block->handler_data = data; + + wmi_status = wmi_method_enable(block, 1); + if ((wmi_status != AE_OK) || + ((wmi_status == AE_OK) && (status == AE_NOT_EXIST))) + status = wmi_status; + } + } return status; } @@ -577,24 +575,40 @@ EXPORT_SYMBOL_GPL(wmi_install_notify_handler); acpi_status wmi_remove_notify_handler(const char *guid) { struct wmi_block *block; - acpi_status status = AE_OK; + acpi_status status = AE_NOT_EXIST; + char tmp[16], guid_input[16]; + struct list_head *p; if (!guid) return AE_BAD_PARAMETER; - if (!find_guid(guid, &block)) - return AE_NOT_EXIST; + wmi_parse_guid(guid, tmp); + wmi_swap_bytes(tmp, guid_input); - if (!block->handler || block->handler == wmi_notify_debug) - return AE_NULL_ENTRY; + list_for_each(p, &wmi_block_list) { + acpi_status wmi_status; + block = list_entry(p, struct wmi_block, list); - if (debug_event) { - block->handler = wmi_notify_debug; - } else { - status = wmi_method_enable(block, 0); - block->handler = NULL; - block->handler_data = NULL; + if (memcmp(block->gblock.guid, guid_input, 16) == 0) { + if (!block->handler || + block->handler == wmi_notify_debug) + return AE_NULL_ENTRY; + + if (debug_event) { + block->handler = wmi_notify_debug; + status = AE_OK; + } else { + wmi_status = wmi_method_enable(block, 0); + block->handler = NULL; + block->handler_data = NULL; + if ((wmi_status != AE_OK) || + ((wmi_status == AE_OK) && + (status == AE_NOT_EXIST))) + status = wmi_status; + } + } } + return status; } EXPORT_SYMBOL_GPL(wmi_remove_notify_handler); @@ -656,18 +670,22 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, struct wmi_block *wblock; wblock = dev_get_drvdata(dev); - if (!wblock) - return -ENOMEM; + if (!wblock) { + strcat(buf, "\n"); + return strlen(buf); + } wmi_gtoa(wblock->gblock.guid, guid_string); return sprintf(buf, "wmi:%s\n", guid_string); } +static DEVICE_ATTR_RO(modalias); -static struct device_attribute wmi_dev_attrs[] = { - __ATTR_RO(modalias), - __ATTR_NULL +static struct attribute *wmi_attrs[] = { + &dev_attr_modalias.attr, + NULL, }; +ATTRIBUTE_GROUPS(wmi); static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env) { @@ -702,43 +720,22 @@ static struct class wmi_class = { .name = "wmi", .dev_release = wmi_dev_free, .dev_uevent = wmi_dev_uevent, - .dev_attrs = wmi_dev_attrs, + .dev_groups = wmi_groups, }; -static struct wmi_block *wmi_create_device(const struct guid_block *gblock, - acpi_handle handle) +static int wmi_create_device(const struct guid_block *gblock, + struct wmi_block *wblock, acpi_handle handle) { - struct wmi_block *wblock; - int error; char guid_string[37]; - wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL); - if (!wblock) { - error = -ENOMEM; - goto err_out; - } - - wblock->handle = handle; - wblock->gblock = *gblock; - wblock->dev.class = &wmi_class; wmi_gtoa(gblock->guid, guid_string); - dev_set_name(&wblock->dev, guid_string); + dev_set_name(&wblock->dev, "%s", guid_string); dev_set_drvdata(&wblock->dev, wblock); - error = device_register(&wblock->dev); - if (error) - goto err_free; - - list_add_tail(&wblock->list, &wmi_block_list); - return wblock; - -err_free: - kfree(wblock); -err_out: - return ERR_PTR(error); + return device_register(&wblock->dev); } static void wmi_free_devices(void) @@ -746,8 +743,13 @@ static void wmi_free_devices(void) struct wmi_block *wblock, *next; /* Delete devices for all the GUIDs */ - list_for_each_entry_safe(wblock, next, &wmi_block_list, list) - device_unregister(&wblock->dev); + list_for_each_entry_safe(wblock, next, &wmi_block_list, list) { + list_del(&wblock->list); + if (wblock->dev.class) + device_unregister(&wblock->dev); + else + kfree(wblock); + } } static bool guid_already_parsed(const char *guid_string) @@ -755,7 +757,7 @@ static bool guid_already_parsed(const char *guid_string) struct wmi_block *wblock; list_for_each_entry(wblock, &wmi_block_list, list) - if (strncmp(wblock->gblock.guid, guid_string, 16) == 0) + if (memcmp(wblock->gblock.guid, guid_string, 16) == 0) return true; return false; @@ -764,13 +766,12 @@ static bool guid_already_parsed(const char *guid_string) /* * Parse the _WDG method for the GUID data blocks */ -static acpi_status parse_wdg(acpi_handle handle) +static int parse_wdg(acpi_handle handle) { struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL}; union acpi_object *obj; const struct guid_block *gblock; struct wmi_block *wblock; - char guid_string[37]; acpi_status status; int retval; u32 i, total; @@ -792,28 +793,31 @@ static acpi_status parse_wdg(acpi_handle handle) total = obj->buffer.length / sizeof(struct guid_block); for (i = 0; i < total; i++) { + if (debug_dump_wdg) + wmi_dump_wdg(&gblock[i]); + + wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL); + if (!wblock) + return -ENOMEM; + + wblock->handle = handle; + wblock->gblock = gblock[i]; + /* Some WMI devices, like those for nVidia hooks, have a duplicate GUID. It's not clear what we should do in this - case yet, so for now, we'll just ignore the duplicate. - Anyone who wants to add support for that device can come - up with a better workaround for the mess then. + case yet, so for now, we'll just ignore the duplicate + for device creation. */ - if (guid_already_parsed(gblock[i].guid) == true) { - wmi_gtoa(gblock[i].guid, guid_string); - pr_info("Skipping duplicate GUID %s\n", guid_string); - continue; + if (!guid_already_parsed(gblock[i].guid)) { + retval = wmi_create_device(&gblock[i], wblock, handle); + if (retval) { + wmi_free_devices(); + goto out_free_pointer; + } } - if (debug_dump_wdg) - wmi_dump_wdg(&gblock[i]); - - wblock = wmi_create_device(&gblock[i], handle); - if (IS_ERR(wblock)) { - retval = PTR_ERR(wblock); - wmi_free_devices(); - break; - } + list_add_tail(&wblock->list, &wmi_block_list); if (debug_event) { wblock->handler = wmi_notify_debug; @@ -901,7 +905,7 @@ static void acpi_wmi_notify(struct acpi_device *device, u32 event) } } -static int acpi_wmi_remove(struct acpi_device *device, int type) +static int acpi_wmi_remove(struct acpi_device *device) { acpi_remove_address_space_handler(device->handle, ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler); |
