aboutsummaryrefslogtreecommitdiff
path: root/drivers/platform/x86/wmi.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/platform/x86/wmi.c')
-rw-r--r--drivers/platform/x86/wmi.c212
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);