[PATCH 1/3] UBUNTU: SAUCE: Allow registration of handler to multiple WMI events with same GUID

Colin King colin.king at canonical.com
Fri Nov 19 19:16:45 UTC 2010


From: Colin Ian King <colin.king at canonical.com>

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 |  131 ++++++++++++++++++++++++++------------------
 1 files changed, 78 insertions(+), 53 deletions(-)

diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c
index e4eaa14..70526ca 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;
@@ -556,21 +557,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);
+
+	list_for_each(p, &wmi_blocks.list) {
+		acpi_status wmi_status;
+		block = list_entry(p, struct wmi_block, list);
 
-	if (block->handler && block->handler != wmi_notify_debug)
-		return AE_ALREADY_ACQUIRED;
+		if (memcmp(block->gblock.guid, guid_input, 16) == 0) {
+			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;
 }
@@ -584,23 +598,38 @@ 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);
+
+	list_for_each(p, &wmi_blocks.list) {
+		acpi_status wmi_status;
+		block = list_entry(p, struct wmi_block, list);
 
-	if (!block->handler || block->handler == wmi_notify_debug)
-		return AE_NULL_ENTRY;
+		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;
-	} 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 +746,34 @@ 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 +790,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);
+		}
 	}
 }
 
@@ -831,19 +868,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 +875,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