[PATCH 1/3] UBUNTU: SAUCE: Allow registration of handler to multiple WMI events with same GUID
Colin Ian King
colin.king at canonical.com
Wed Nov 24 18:53:06 UTC 2010
BugLink: http://bugs.launchpad.net/bugs/676997
WMI data blocks can contain WMI events with the same GUID but with
different notifiy_ids. This patch enables a single event handler
to be registered and unregistered against all events against with
the same GUID. The event handler is passed the notify_id of these
events and hence can differentiate between the differen events. The
patch also ensures we only register and unregister a device per
unique GUID.
The original registration implementation just matched on the first
event with the matching GUID and left the other events with out a
handler.
Signed-off-by: Eric Miao <eric.miao at canonical.com>
Signed-off-by: Colin Ian King <colin.king at canonical.com>
---
drivers/platform/x86/wmi.c | 125 +++++++++++++++++++++++--------------------
1 files changed, 67 insertions(+), 58 deletions(-)
diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c
index e4eaa14..3312c35 100644
--- a/drivers/platform/x86/wmi.c
+++ b/drivers/platform/x86/wmi.c
@@ -68,6 +68,7 @@ struct wmi_block {
wmi_notify_handler handler;
void *handler_data;
struct device *dev;
+ bool first_instance;
};
static struct wmi_block wmi_blocks;
@@ -241,13 +242,16 @@ static bool find_guid(const char *guid_string, struct wmi_block **out)
char tmp[16], guid_input[16];
struct wmi_block *wblock;
struct guid_block *block;
- struct list_head *p;
wmi_parse_guid(guid_string, tmp);
wmi_swap_bytes(tmp, guid_input);
- list_for_each(p, &wmi_blocks.list) {
- wblock = list_entry(p, struct wmi_block, list);
+ if ((out == NULL) || (*out == NULL))
+ wblock = list_entry(&wmi_blocks.list, struct wmi_block, list);
+ else
+ wblock = *out;
+
+ list_for_each_entry_continue(wblock, &wmi_blocks.list, list) {
block = &wblock->gblock;
if (memcmp(block->guid, guid_input, 16) == 0) {
@@ -555,22 +559,26 @@ static void wmi_notify_debug(u32 value, void *context)
acpi_status wmi_install_notify_handler(const char *guid,
wmi_notify_handler handler, void *data)
{
- struct wmi_block *block;
- acpi_status status;
+ struct wmi_block *block = NULL;
+ acpi_status status = AE_NOT_EXIST;
if (!guid || !handler)
return AE_BAD_PARAMETER;
- if (!find_guid(guid, &block))
- return AE_NOT_EXIST;
+ while (find_guid(guid, &block)) {
+ acpi_status wmi_status;
- if (block->handler && block->handler != wmi_notify_debug)
- return AE_ALREADY_ACQUIRED;
+ if (block->handler && block->handler != wmi_notify_debug)
+ return AE_ALREADY_ACQUIRED;
- block->handler = handler;
- block->handler_data = data;
+ block->handler = handler;
+ block->handler_data = data;
- status = wmi_method_enable(block, 1);
+ 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;
}
@@ -583,24 +591,29 @@ 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;
+ struct wmi_block *block = NULL;
+ acpi_status status = AE_NOT_EXIST;
if (!guid)
return AE_BAD_PARAMETER;
- if (!find_guid(guid, &block))
- return AE_NOT_EXIST;
+ while (find_guid(guid, &block)) {
+ acpi_status wmi_status;
- if (!block->handler || block->handler == wmi_notify_debug)
- return AE_NULL_ENTRY;
+ if (!block->handler || block->handler == wmi_notify_debug)
+ return AE_NULL_ENTRY;
- if (debug_event) {
- block->handler = wmi_notify_debug;
- } else {
- status = wmi_method_enable(block, 0);
- block->handler = NULL;
- block->handler_data = NULL;
+ 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;
}
@@ -717,28 +730,35 @@ static int wmi_create_devs(void)
/* Create devices for all the GUIDs */
list_for_each(p, &wmi_blocks.list) {
wblock = list_entry(p, struct wmi_block, list);
+ /*
+ * Only create device on first instance, as subsequent
+ * instances share the same GUID and we need to avoid
+ * creating multiple devices with the same GUID
+ */
+ if (wblock->first_instance) {
+ guid_dev = kzalloc(sizeof(struct device), GFP_KERNEL);
+ if (!guid_dev)
+ return -ENOMEM;
- guid_dev = kzalloc(sizeof(struct device), GFP_KERNEL);
- if (!guid_dev)
- return -ENOMEM;
-
- wblock->dev = guid_dev;
+ wblock->dev = guid_dev;
- guid_dev->class = &wmi_class;
- dev_set_drvdata(guid_dev, wblock);
+ guid_dev->class = &wmi_class;
+ dev_set_drvdata(guid_dev, wblock);
- gblock = &wblock->gblock;
+ gblock = &wblock->gblock;
- wmi_gtoa(gblock->guid, guid_string);
- dev_set_name(guid_dev, guid_string);
+ wmi_gtoa(gblock->guid, guid_string);
+ dev_set_name(guid_dev, guid_string);
- result = device_register(guid_dev);
- if (result)
- return result;
+ result = device_register(guid_dev);
+ if (result)
+ return result;
- result = device_create_file(guid_dev, &dev_attr_modalias);
- if (result)
- return result;
+ result = device_create_file(guid_dev,
+ &dev_attr_modalias);
+ if (result)
+ return result;
+ }
}
return 0;
@@ -755,12 +775,14 @@ static void wmi_remove_devs(void)
list_for_each(p, &wmi_blocks.list) {
wblock = list_entry(p, struct wmi_block, list);
- guid_dev = wblock->dev;
- gblock = &wblock->gblock;
+ if (wblock->first_instance) {
+ guid_dev = wblock->dev;
+ gblock = &wblock->gblock;
- device_remove_file(guid_dev, &dev_attr_modalias);
+ device_remove_file(guid_dev, &dev_attr_modalias);
- device_unregister(guid_dev);
+ device_unregister(guid_dev);
+ }
}
}
@@ -810,7 +832,6 @@ static __init acpi_status parse_wdg(acpi_handle handle)
union acpi_object *obj;
struct guid_block *gblock;
struct wmi_block *wblock;
- char guid_string[37];
acpi_status status;
u32 i, total;
@@ -831,19 +852,6 @@ static __init acpi_status parse_wdg(acpi_handle handle)
return AE_NO_MEMORY;
for (i = 0; i < total; 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.
- */
- if (guid_already_parsed(gblock[i].guid) == true) {
- wmi_gtoa(gblock[i].guid, guid_string);
- printk(KERN_INFO PREFIX "Skipping duplicate GUID %s\n",
- guid_string);
- continue;
- }
if (debug_dump_wdg)
wmi_dump_wdg(&gblock[i]);
@@ -851,6 +859,7 @@ static __init acpi_status parse_wdg(acpi_handle handle)
if (!wblock)
return AE_NO_MEMORY;
+ wblock->first_instance = !guid_already_parsed(gblock[i].guid);
wblock->gblock = gblock[i];
wblock->handle = handle;
if (debug_event) {
--
1.7.0.4
More information about the kernel-team
mailing list