Skip to contentSkip to conversation

WMI

PATCH In review Carlos Corbacho 1 participant 7 messages Dec 27, 2007 – Jan 6, 2008

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

cover WMI

ACSWR - A Christmas Sale WMI release

Patch #1: (WMI - driver and in kernel interface)

Added per GUID event handling callbacks, as per Matthew's suggestion.

Len:
For review, and to go upstream.

(Bar the EC stuff, this code has also been inflic^^^^tested by acer_acpi users with my 0.10 releases, so I'm confident on Acer hardware at least that it won't break horribly).

Patch #2: (acer-wmi)

Add device detection for bluetooth and maximum brightness (removes more hard coded EC quirks). Also simplified some of the code.

Len:
For review, and to go upstream.

Patch #3: (tc1100-wmi) - No Change

RFC only, needs actual testing on the hardware, and probably very broken. Waiting on Matthew Garrett to get some free time to test this.

Patch #4: (WMI sysfs interface) - No Change

RFC only (see patch #5 for reason). Adds interface under /sys/devices/virtual/wmi

Matthew - Do you want me to fold patch #5 into this?

Patch #5: (WMI sysfs workaround) - No change

Temporary hack, needed to get patch #4 working, due to a limitation on bus_id length (Kay Sievers is apparently working on this, ref Greg KH[1]).

-Carlos

[1] http://lkml.org/lkml/2007/12/4/30

#1 lore raw

PATCH 1/5 ACPI: WMI: Add ACPI-WMI mapping driver

The following is an implementation of the Windows Management Instrumentation (WMI) ACPI interface mapper (PNP0C14).

What it does:

Parses the _WDG method and exports functions to process WMI method calls, data block query/ set commands (both based on GUID) and does basic event handling.

How: WMI presents an in kernel interface here (essentially, a minimal wrapper around ACPI)

(const char *guid assume the 36 character ASCII representation of a GUID - e.g. 67C3371D-95A3-4C37-BB61-DD47B491DAAB)

wmi_evaluate_method(const char *guid, u8 instance, u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out)

wmi_query_block(const char *guid, u8 instance,
struct acpi_buffer *out)

wmi_set_block(const char *guid, u38 instance,
const struct acpi_buffer *in)

wmi_install_notify_handler(acpi_notify_handler handler);

wmi_remove_notify_handler(void);

wmi_get_event_data(u32 event, struct acpi_buffer *out)

wmi_has_guid(const char guid*)

wmi_has_guid() is a helper function to find if a GUID exists or not on the system (a quick and easy way for WMI dependant drivers to see if the the method/ block they want exists, since GUIDs are supposed to be unique).

Event handling - allow a WMI based driver to register a notifier handler for each GUID with WMI. When a notification is sent to a GUID in WMI, the handler registered with WMI is then called (it is left to the caller to ask for the WMI event data associated with the GUID, if needed).

What it won't do:

Unicode - The MS article[1] calls for converting between ASCII and Unicode (or vice versa) if a GUID is marked as "string". This is left up to the calling driver.

Handle a MOF[1] - the WMI mapper just exports methods, data and events to userspace. MOF handling is down to userspace.

Userspace interface - this will be added later.

[1] http://www.microsoft.com/whdc/system/pnppwr/wmi/wmi-acpi.mspx

===
ChangeLog
==

v1 (2007-10-02):

* Initial release

v2 (2007-10-05):

* Cleaned up code - split up super "wmi_evaluate_block" -> each external symbol now handles its own ACPI calls, rather than handing off to a "super" method (and in turn, is a lot simpler to read)
* Added a find_guid() symbol - return true if a given GUID exists on the system
* wmi_* functions now return type acpi_status (since they are just fancy wrappers around acpi_evaluate_object())
* Removed extra debug code

v3 (2007-10-27)

* More code clean up - now passes checkpatch.pl
* Change data block calls - ref MS spec, method ID is not required for them, so drop it from the function parameters.
* Const'ify guid in the function call parameters.
* Fix _WDG buffer handling - copy the data to our own private structure.
* Change WMI from tristate to bool - otherwise the external functions are not exported in linux/acpi.h if you try to build WMI as a module.
* Fix more flag comparisons.
* Add a maintainers entry - since I wrote this, I should take the blame for it.

v4 (2007-10-30)

* Add missing brace from after fixing checkpatch errors.
* Rewrote event handling - allow external drivers to register with WMI to handle WMI events
* Clean up flags and sanitise flag handling

v5 (2007-11-03)

* Add sysfs interface for userspace. Export events over netlink again.
* Remove module left overs, fully convert to built-in driver.
* Tweak in-kernel API to use u8 for instance, since this is what the GUID blocks use (so instance cannot be greater than u8).
* Export wmi_get_event_data() for in kernel WMI drivers.

v6 (2007-11-07)

* Split out userspace into a different patch

v7 (2007-11-20)

* Fix driver to handle multiple PNP0C14 devices - store all GUIDs using the kernel's built in list functions, and just keep adding to the list every time we handle a PNP0C14 devices - GUIDs will always be unique, and WMI callers do not know or care about different devices.
* Change WMI event handler registration to use its' own event handling struct; we should not pass an acpi_handle down to any WMI based drivers
- they should be able to function with only the calls provided in WMI.
* Update my e-mail address

v8 (2007-11-28)

* Convert back to a module.
* Update Kconfig to default to building as a module.
* Remove an erroneous printk.
* Simply comments for string flag (since we now leave the handling to the caller).

v9 (2007-12-07)

* Add back missing MODULE_DEVICE_TABLE for autoloading
* Checkpatch fixes

v10 (2007-12-12)

* Workaround broken GUIDs declared expensive without a WCxx method.
* Minor cleanups

v11 (2007-12-17)

* More fixing for broken GUIDs declared expensive without a WCxx method.
* Add basic EmbeddedControl region handling.

v12 (2007-12-18)

* Changed EC region handling code, as per Alexey's comments.

v13 (2007-12-27)

* Changed event handling so that we can have one event handler registered per GUID, as per Matthew Garrett's suggestion.

CC Len Brown <[email protected]>
CC Matthew Garrett <[email protected]>
CC Alexey Starikovskiy <[email protected]>

---

 MAINTAINERS           |    7 
 drivers/acpi/Kconfig  |   11 +
 drivers/acpi/Makefile |    1 
 drivers/acpi/wmi.c    |  712 +++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/acpi.h  |   20 +
 5 files changed, 751 insertions(+), 0 deletions(-)
 create mode 100644 drivers/acpi/wmi.c
MAINTAINERS +7 -0
index 9507b42..68101ad 100644
@@ -259,6 +259,13 @@ L:	[email protected]
 W:	http://acpi.sourceforge.net/
 S:	Supported
 
+ACPI WMI DRIVER
+P:      Carlos Corbacho
+M:      [email protected]
+L:      [email protected]
+W:      http://www.lesswatts.org/projects/acpi/
+S:      Supported
+
 ADM1025 HARDWARE MONITOR DRIVER
 P:	Jean Delvare
 M:	[email protected]
drivers/acpi/Kconfig +11 -0
index b9f923e..5fe266c 100644
@@ -195,6 +195,17 @@ config ACPI_NUMA
 	depends on (X86 || IA64)
 	default y if IA64_GENERIC || IA64_SGI_SN2
 
+config ACPI_WMI
+	tristate "WMI (EXPERIMENTAL)"
+	depends on EXPERIMENTAL
+	default m
+	help
+	  This driver adds support for the ACPI-WMI mapper device (PNP0C14)
+	  found on some systems.
+
+	  NOTE: You will need another driver or userspace application on top of
+	  this to actually use anything defined in the ACPI-WMI mapper.
+
 config ACPI_ASUS
         tristate "ASUS/Medion Laptop Extras"
 	depends on X86
drivers/acpi/Makefile +1 -0
index 456446f..f29812a 100644
@@ -55,6 +55,7 @@ obj-$(CONFIG_ACPI_THERMAL)	+= thermal.o
 obj-$(CONFIG_ACPI_SYSTEM)	+= system.o event.o
 obj-$(CONFIG_ACPI_DEBUG)	+= debug.o
 obj-$(CONFIG_ACPI_NUMA)		+= numa.o
+obj-$(CONFIG_ACPI_WMI)		+= wmi.o
 obj-$(CONFIG_ACPI_ASUS)		+= asus_acpi.o
 obj-$(CONFIG_ACPI_TOSHIBA)	+= toshiba_acpi.o
 obj-$(CONFIG_ACPI_HOTPLUG_MEMORY)	+= acpi_memhotplug.o
drivers/acpi/wmi.c new file +712 -0

712 lines not on this page · raw message

include/linux/acpi.h +20 -0
index 4497964..aeffe08 100644
@@ -176,6 +176,26 @@ extern int ec_transaction(u8 command,
 
 #endif /*CONFIG_ACPI_EC*/
 
+#if defined(CONFIG_ACPI_WMI) || defined(CONFIG_ACPI_WMI_MODULE)
+
+typedef void (*wmi_notify_handler) (u32 value, void *context);
+
+extern acpi_status wmi_evaluate_method(const char *guid, u8 instance,
+					u32 method_id,
+					const struct acpi_buffer *in,
+					struct acpi_buffer *out);
+extern acpi_status wmi_query_block(const char *guid, u8 instance,
+					struct acpi_buffer *out);
+extern acpi_status wmi_set_block(const char *guid, u8 instance,
+					const struct acpi_buffer *in);
+extern acpi_status wmi_install_notify_handler(const char *guid,
+					wmi_notify_handler handler, void *data);
+extern acpi_status wmi_remove_notify_handler(const char *guid);
+extern acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out);
+extern bool wmi_has_guid(const char *guid);
+
+#endif	/* CONFIG_ACPI_WMI */
+
 extern int acpi_blacklisted(void);
 extern void acpi_bios_year(char *s);
 
#2 lore raw

PATCH 2/5 acer-wmi: Add driver for newer Acer laptops

In reply to Carlos Corbacho

This is a driver for newer Acer (and Wistron) laptops. It adds wireless radio and bluetooth control, and on some laptops, exposes the mail LED and LCD backlight.

v1:

* Initial release

v2:

* Replace left over ACPI references with WMI
* Add GUID based autoloading (depends on future work to WMI)
* Add DMI based autoloading (backup solution until WMI sysfs/ class work is available)
* Checkpatch fixes

v3:

* Add new EC quirks for Aspire 3100 & 5100, and Extensa 5220

v4:

* Simplified internal handling of WMID and AMW0 devices
* Add autodetection for bluetooth and maximum brightness on AMW0 V2 and WMID laptops.

CC Len Brown <[email protected]>
CC Matthew Garrett <[email protected]>

---

 drivers/misc/Kconfig    |   16 +
 drivers/misc/Makefile   |    1 
 drivers/misc/acer-wmi.c |  965 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 982 insertions(+), 0 deletions(-)
 create mode 100644 drivers/misc/acer-wmi.c
drivers/misc/Kconfig +16 -0
index b1f9a40..d72acbf 100644
@@ -92,6 +92,22 @@ config TIFM_7XX1
           To compile this driver as a module, choose M here: the module will
 	  be called tifm_7xx1.
 
+config ACER_WMI
+        tristate "Acer Laptop WMI-ACPI Extras (EXPERIMENTAL)"
+	depends on X86
+	depends on EXPERIMENTAL
+	depends on ACPI
+	depends on ACPI_WMI
+	depends on LEDS_CLASS
+	depends on BACKLIGHT_CLASS_DEVICE
+	---help---
+	  This is a driver for newer Acer (and Wistron) laptops. It adds
+	  wireless radio and bluetooth control, and on some laptops,
+	  exposes the mail LED and LCD backlight.
+
+	  If you have an ACPI-WMI compatible Acer/ Wistron laptop, say Y or M
+	  here.
+
 config ASUS_LAPTOP
         tristate "Asus Laptop Extras (EXPERIMENTAL)"
         depends on X86
drivers/misc/Makefile +1 -0
index 87f2685..3da1491 100644
@@ -6,6 +6,7 @@ obj- := misc.o	# Dummy rule to force built-in.o to be made
 obj-$(CONFIG_IBM_ASM)		+= ibmasm/
 obj-$(CONFIG_HDPU_FEATURES)	+= hdpuftrs/
 obj-$(CONFIG_MSI_LAPTOP)     += msi-laptop.o
+obj-$(CONFIG_ACER_WMI)     += acer-wmi.o
 obj-$(CONFIG_ASUS_LAPTOP)     += asus-laptop.o
 obj-$(CONFIG_ATMEL_SSC)		+= atmel-ssc.o
 obj-$(CONFIG_LKDTM)		+= lkdtm.o
drivers/misc/acer-wmi.c new file +965 -0

965 lines not on this page · raw message

#3 lore raw

RFC PATCH 3/5 tc1100-wmi: Add driver for HP Compaq TC1100 Tablets

In reply to Carlos Corbacho

This is based on the 2004 out-of-tree work of Jamey Hicks, to add support via WMI for controlling the jog dial and wireless on these tablets.

v1:

Original release

v2:

As per Joshua Wise's comments, change bluetooth to jogdial (an error from the original driver).

CC Len Brown <[email protected]>
CC Matthew Garrett <[email protected]>
CC Jamey Hicks <[email protected]>
CC Joshua Wise <[email protected]>

---

 drivers/misc/Kconfig      |    9 +
 drivers/misc/Makefile     |    1 
 drivers/misc/tc1100-wmi.c |  290 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 300 insertions(+), 0 deletions(-)
 create mode 100644 drivers/misc/tc1100-wmi.c
drivers/misc/Kconfig +9 -0
index d72acbf..205fd6a 100644
@@ -142,6 +142,15 @@ config FUJITSU_LAPTOP
 
 	  If you have a Fujitsu laptop, say Y or M here.
 
+config TC1100_WMI
+	tristate "HP Compaq TC1100 Tablet WMI Extras"
+	depends on X86 && !X86_64
+	depends on ACPI
+	depends on ACPI_WMI
+	---help---
+	  This is a driver for the WMI extensions (wireless and bluetooth power
+	  control) of the HP Compaq TC1100 tablet.
+
 config MSI_LAPTOP
         tristate "MSI Laptop Extras"
         depends on X86
drivers/misc/Makefile +1 -0
index 3da1491..51196c0 100644
@@ -9,6 +9,7 @@ obj-$(CONFIG_MSI_LAPTOP)     += msi-laptop.o
 obj-$(CONFIG_ACER_WMI)     += acer-wmi.o
 obj-$(CONFIG_ASUS_LAPTOP)     += asus-laptop.o
 obj-$(CONFIG_ATMEL_SSC)		+= atmel-ssc.o
+obj-$(CONFIG_TC1100_WMI)	+= tc1100-wmi.o
 obj-$(CONFIG_LKDTM)		+= lkdtm.o
 obj-$(CONFIG_TIFM_CORE)       	+= tifm_core.o
 obj-$(CONFIG_TIFM_7XX1)       	+= tifm_7xx1.o
drivers/misc/tc1100-wmi.c new file +290 -0

290 lines not on this page · raw message

#4 lore raw

RFC PATCH 4/5 ACPI: WMI: Add sysfs userspace interface

In reply to Carlos Corbacho

Create a 'wmi' class, and populate it with a virtual device for each GUID. This also allows us to autoload WMI drivers based on GUID
via MODULE_ALIAS.

Under each GUID are a set of files that can be read and written to from userspace (these will be fully documented in a later patch).

v1 (2007-11-03)

* Initial release

v2 (2007-11-07)

* Split out into a separate patch

v3 (2007-11-20)

* Convert kobject storage to using kernel list structures.
* Change instance handling - store input data on a per instance basis, rather than a per GUID.
* Add locking to methods - method_id (write) and data (read & write) should all be mutually exclusive - we do not want the input data to change when we are trying to execute a method.
* Change method calling semantics: when input is written to 'data', store it, but don't execute the method until 'data' is read. This is due to the fact that it is perfectly acceptable to have a WMI method that does not take any input (and it is easier to trigger an execute on reading the file, and not return anything if there is nothing to return, rather than trying to write values that aren't required, or may cause ACPI evaluation to fail on the method).
* Do not try and convert String's from ASCII to Unicode - instead, only handle ASCII (as per ACPI), and leave it up to userspace to convert to/ from whatever encoding they wish to use.

v4 (2007-11-28)

* Add code to remove sysfs files on module removal

v5 (2007-12-08)

* Convert to a class device, instead of trying to manually create and manipulate kobject's - this allows us to add module autoloading for WMI based drivers, and will (hopefully) be less likely to break with the latest round of kobject changes.
* Convert GUIDs to use a 'flat' structure - add two new files:
instance and instance_count, and use these to provide the instance to call for a given GUID (while still communicating the maximum number of instances available - for now, we will always respect instance_count when setting instance - so any instance > instance_count will be discarded).

v6 (2007-12-12)

* Add string file - this reports back if the GUID has the string flag set.

CC Len Brown <[email protected]>
CC Matthew Garrett <[email protected]>

---

 drivers/acpi/wmi.c |  463 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 461 insertions(+), 2 deletions(-)
drivers/acpi/wmi.c +461 -2
index 0e443af..52015ba 100644
@@ -30,6 +30,8 @@
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/types.h>
+#include <linux/device.h>
+#include <linux/mutex.h>
 #include <linux/list.h>
 #include <linux/acpi.h>
 #include <acpi/acpi_bus.h>
@@ -66,6 +68,11 @@ struct wmi_block {
 	acpi_handle handle;
 	wmi_notify_handler handler;
 	void *handler_data;
+	struct device dev;
+	u8 instance;
+	u32 method_id;
+	void *pointer;
+	acpi_size length;
 };
 
 static struct wmi_block wmi_blocks;
@@ -194,6 +201,33 @@ static bool wmi_parse_guid(const u8 *src, u8 *dest)
 	return true;
 }
 
+/*
+ * Convert a raw GUID to the ACII string representation
+ */
+static int wmi_gtoa(const char *in, char *out)
+{
+	int i;
+
+	for (i = 3; i >= 0; i--)
+		out += sprintf(out, "%02X", in[i] & 0xFF);
+
+	out += sprintf(out, "-");
+	out += sprintf(out, "%02X", in[5] & 0xFF);
+	out += sprintf(out, "%02X", in[4] & 0xFF);
+	out += sprintf(out, "-");
+	out += sprintf(out, "%02X", in[7] & 0xFF);
+	out += sprintf(out, "%02X", in[6] & 0xFF);
+	out += sprintf(out, "-");
+	out += sprintf(out, "%02X", in[8] & 0xFF);
+	out += sprintf(out, "%02X", in[9] & 0xFF);
+	out += sprintf(out, "-");
+
+	for (i = 10; i <= 15; i++)
+		out += sprintf(out, "%02X", in[i] & 0xFF);
+
+	return 0;
+}
+
 static bool find_guid(const char *guid_string, struct wmi_block **out)
 {
 	char tmp[16], guid_input[16];
@@ -517,6 +551,421 @@ bool wmi_has_guid(const char *guid_string)
 EXPORT_SYMBOL_GPL(wmi_has_guid);
 
 /*
+ * sysfs interface
+ */
+static ssize_t wmi_data_read(struct kobject *kobj, struct bin_attribute
+			*bin_attr, char *buf, loff_t offset, size_t count) {
+	u8 instance;
+	const char *guid;
+	struct acpi_buffer in;
+	struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
+	acpi_status status;
+	union acpi_object *obj;
+	struct wmi_block *block;
+	struct device *dev;
+
+	dev = container_of(kobj, struct device, kobj);
+	if (!dev)
+		return -ENODEV;
+
+	block = container_of(dev, struct wmi_block, dev);
+	if (!block)
+		return -EINVAL;
+
+	guid = block->gblock.guid;
+	if (!guid)
+		return -EINVAL;
+
+	instance = block->instance;
+
+	if (block->gblock.flags & ACPI_WMI_METHOD) {
+		mutex_lock(&wmi_data_lock);
+		if (block->pointer) {
+			in.pointer = block->pointer;
+			in.length = block->length;
+			status = wmi_evaluate_method(guid, instance,
+				block->method_id, &in, &out);
+		} else {
+			status = wmi_evaluate_method(guid, instance,
+				block->method_id, NULL, &out);
+		}
+		mutex_unlock(&wmi_data_lock);
+	} else {
+		status = wmi_query_block(guid, instance, &out);
+	}
+
+	obj = (union acpi_object *) out.pointer;
+
+	switch (obj->type) {
+	case (ACPI_TYPE_STRING):
+	case (ACPI_TYPE_BUFFER):
+		buf = obj->buffer.pointer;
+		break;
+	default:
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static ssize_t wmi_data_write(struct kobject *kobj, struct bin_attribute
+			*bin_attr, char *buf, loff_t offset, size_t count){
+	struct wmi_block *block;
+	struct acpi_buffer in;
+	acpi_status status;
+	struct device *dev;
+	char guid_string[37];
+
+	dev = container_of(kobj, struct device, kobj);
+	if (!dev)
+		return -ENODEV;
+
+	block = container_of(dev, struct wmi_block, dev);
+	if (!block)
+		return -EINVAL;
+
+	if (count == 0)
+		return count;
+
+	if (block->gblock.flags & ACPI_WMI_METHOD) {
+		block->pointer = kzalloc(count, GFP_KERNEL);
+		if (!block->pointer)
+			return count;
+
+		memcpy(block->pointer, buf, count);
+		block->length = count;
+	} else {
+		in.pointer = buf;
+		in.length = count;
+		wmi_gtoa(block->gblock.guid, guid_string);
+		status = wmi_set_block(guid_string, block->instance, &in);
+	}
+
+	return count;
+}
+
+static struct bin_attribute wmi_attr_data = {
+	.attr = {
+		.name = "data",
+		.mode = 0600
+	},
+	.size = 0,
+	.read = wmi_data_read,
+	.write = wmi_data_write,
+};
+
+static ssize_t wmi_event_data_read(struct kobject *kobj, struct bin_attribute
+			*bin_attr, char *buf, loff_t offset, size_t count) {
+	struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
+	union acpi_object *obj;
+	struct wmi_block *block;
+	struct device *dev;
+
+	dev = container_of(kobj, struct device, kobj);
+	if (!dev)
+		return -ENODEV;
+
+	block = container_of(dev, struct wmi_block, dev);
+	if (!block)
+		return -EINVAL;
+
+	wmi_get_event_data(block->gblock.notify_id, &out);
+
+	obj = (union acpi_object *) out.pointer;
+	buf = obj->buffer.pointer;
+
+	return 0;
+}
+
+static struct bin_attribute wmi_attr_event_data = {
+	.attr = {
+		.name = "data",
+		.mode = 0400
+	},
+	.size = 0,
+	.read = wmi_event_data_read,
+};
+
+static ssize_t show_type(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct guid_block *block;
+	struct wmi_block *wblock;
+
+	wblock = container_of(dev, struct wmi_block, dev);
+	if (!wblock)
+		return sprintf(buf, "Error\n");
+
+	block = &wblock->gblock;
+
+	if (block->flags & ACPI_WMI_METHOD) {
+		return sprintf(buf, "method\n");
+	} else if (block->flags & ACPI_WMI_EVENT) {
+		return sprintf(buf, "event\n");
+	} else {
+		return sprintf(buf, "data\n");
+	}
+}
+static DEVICE_ATTR(type, S_IRUGO, show_type, NULL);
+
+static ssize_t show_instance_count(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct wmi_block *block;
+
+	block = container_of(dev, struct wmi_block, dev);
+	if (!block)
+		return sprintf(buf, "Error\n");
+
+	return sprintf(buf, "%d\n", block->gblock.instance_count);
+}
+static DEVICE_ATTR(instance_count, S_IRUGO, show_instance_count, NULL);
+
+static ssize_t show_instance(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct wmi_block *block;
+
+	block = container_of(dev, struct wmi_block, dev);
+	if (!block)
+		return sprintf(buf, "Error\n");
+
+	return sprintf(buf, "%d\n", block->instance);
+}
+
+static ssize_t set_instance(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct wmi_block *block;
+	u8 instance;
+
+	instance = simple_strtoul(buf, NULL, 10);
+
+	block = container_of(dev, struct wmi_block, dev);
+	if (!block)
+		return -EINVAL;
+
+	if (instance <= block->gblock.instance_count) {
+		block->instance = instance;
+	} else {
+		return -EINVAL;
+	}
+
+	return count;
+}
+static DEVICE_ATTR(instance, S_IWUGO | S_IRUGO, show_instance, set_instance);
+
+static ssize_t show_method_id(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct wmi_block *block;
+
+	block = container_of(dev, struct wmi_block, dev);
+	if (!block)
+		return sprintf(buf, "Error\n");
+
+	return sprintf(buf, "%d\n", block->method_id);
+}
+
+static ssize_t set_method_id(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct wmi_block *block;
+	u8 method_id;
+
+	method_id = simple_strtoul(buf, NULL, 10);
+
+	mutex_lock(&wmi_data_lock);
+
+	block = container_of(dev, struct wmi_block, dev);
+	if (!block) {
+		mutex_unlock(&wmi_data_lock);
+		return -EINVAL;
+	}
+
+	block->method_id = method_id;
+	mutex_unlock(&wmi_data_lock);
+
+	return count;
+}
+static DEVICE_ATTR(method_id, S_IWUGO | S_IRUGO, show_method_id,
+	set_method_id);
+
+static ssize_t show_event_notification(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct wmi_block *block;
+	struct guid_block *gblock;
+
+	block = container_of(dev, struct wmi_block, dev);
+	gblock = &block->gblock;
+
+	return sprintf(buf, "%d\n", gblock->notify_id & 0xFF);
+}
+static DEVICE_ATTR(notification, S_IRUGO, show_event_notification, NULL);
+
+static ssize_t show_string(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct wmi_block *block;
+	struct guid_block *gblock;
+
+	block = container_of(dev, struct wmi_block, dev);
+	gblock = &block->gblock;
+
+	return sprintf(buf, "%d\n", (gblock->flags & ACPI_WMI_STRING) >> 2);
+}
+static DEVICE_ATTR(string, S_IRUGO, show_string, NULL);
+
+static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+	char guid_string[37];
+
+	struct wmi_block *wblock;
+
+	if (add_uevent_var(env, "MODALIAS="))
+		return -ENOMEM;
+
+	wblock = container_of(dev, struct wmi_block, dev);
+	if (!wblock)
+		return -ENOMEM;
+
+	wmi_gtoa(wblock->gblock.guid, guid_string);
+
+	strcpy(&env->buf[env->buflen - 1], "wmi:");
+	memcpy(&env->buf[env->buflen - 1 + 4], guid_string, 36);
+	env->buflen += 40;
+
+	return 0;
+}
+
+static struct class wmi_class = {
+	.name = "wmi",
+	.dev_release = (void(*)(struct device *)) kfree,
+	.dev_uevent = wmi_dev_uevent,
+};
+
+static int wmi_create_devs(void)
+{
+	int result;
+	char guid_string[37];
+	struct guid_block *gblock;
+	struct wmi_block *wblock;
+	struct list_head *p;
+	struct device *guid_dev;
+
+	/* Create devices for all the GUIDs */
+	list_for_each(p, &wmi_blocks.list) {
+		wblock = list_entry(p, struct wmi_block, list);
+
+		guid_dev = &wblock->dev;
+		guid_dev->class = &wmi_class;
+
+		gblock = &wblock->gblock;
+
+		wmi_gtoa(gblock->guid, guid_string);
+		memcpy(guid_dev->bus_id, guid_string, 36);
+
+		result = device_register(guid_dev);
+		if (result)
+			return result;
+
+		result = device_create_file(guid_dev, &dev_attr_type);
+		if (result)
+			return result;
+
+		result = device_create_file(guid_dev, &dev_attr_string);
+		if (result)
+			return result;
+
+		if (gblock->flags & ACPI_WMI_EVENT) {
+			result = device_create_file(guid_dev,
+				&dev_attr_notification);
+			if (result)
+				return result;
+
+			result = device_create_bin_file(guid_dev,
+				&wmi_attr_event_data);
+			if (result)
+				return result;
+			break;
+		}
+
+		result = device_create_file(guid_dev, &dev_attr_instance_count);
+		if (result)
+			return result;
+
+		result = device_create_file(guid_dev, &dev_attr_instance);
+		if (result)
+			return result;
+
+		result = device_create_bin_file(guid_dev, &wmi_attr_data);
+		if (result)
+			return result;
+
+		if (gblock->flags & ACPI_WMI_METHOD) {
+			result = device_create_file(guid_dev,
+				&dev_attr_method_id);
+			if (result)
+				return result;
+		}
+	}
+
+	return 0;
+}
+
+static void wmi_remove_devs(void)
+{
+	struct guid_block *gblock;
+	struct wmi_block *wblock;
+	struct list_head *p;
+	struct device *guid_dev;
+
+	/* Delete devices for all the GUIDs */
+	list_for_each(p, &wmi_blocks.list) {
+		wblock = list_entry(p, struct wmi_block, list);
+
+		guid_dev = &wblock->dev;
+		gblock = &wblock->gblock;
+
+		device_remove_file(guid_dev, &dev_attr_type);
+		device_remove_file(guid_dev, &dev_attr_string);
+
+		if (gblock->flags & ACPI_WMI_EVENT) {
+			device_remove_file(guid_dev, &dev_attr_notification);
+			device_remove_bin_file(guid_dev, &wmi_attr_event_data);
+		} else {
+			device_remove_file(guid_dev, &dev_attr_instance_count);
+			device_remove_file(guid_dev, &dev_attr_instance);
+			device_remove_bin_file(guid_dev, &wmi_attr_data);
+
+			if (gblock->flags & ACPI_WMI_METHOD) {
+				device_remove_file(guid_dev,
+					&dev_attr_method_id);
+			}
+		}
+		device_unregister(guid_dev);
+	}
+}
+
+static int wmi_class_init(void)
+{
+	int ret;
+
+	ret = class_register(&wmi_class);
+	if (ret)
+		return ret;
+
+	return wmi_create_devs();
+}
+
+static void wmi_class_exit(void)
+{
+	wmi_remove_devs();
+	class_unregister(&wmi_class);
+}
+
+/*
  * Parse the _WDG method for the GUID data blocks
  */
 static __init acpi_status parse_wdg(acpi_handle handle)
@@ -673,6 +1122,7 @@ static int __init acpi_wmi_add(struct acpi_device *device)
 
 static int __init acpi_wmi_init(void)
 {
+	int ret;
 	acpi_status result;
 
 	if (acpi_disabled)
@@ -684,10 +1134,17 @@ static int __init acpi_wmi_init(void)
 
 	if (result < 0) {
 		printk(KERN_INFO PREFIX "Error loading mapper\n");
-	} else {
-		printk(KERN_INFO PREFIX "Mapper loaded\n");
+		return -ENODEV;
+	}
+
+	ret = wmi_class_init();
+	if (ret) {
+		acpi_bus_unregister_driver(&acpi_wmi_driver);
+		return ret;
 	}
 
+	printk(KERN_INFO PREFIX "Mapper loaded\n");
+
 	return result;
 }
 
@@ -696,6 +1153,8 @@ static void __exit acpi_wmi_exit(void)
 	struct list_head *p, *tmp;
 	struct wmi_block *wblock;
 
+	wmi_class_exit();
+
 	acpi_bus_unregister_driver(&acpi_wmi_driver);
 
 	list_for_each_safe(p, tmp, &wmi_blocks.list) {
#5 lore raw

PATCH 5/5 ACPI: WMI: Limit size of device string to 19 characters

In reply to Carlos Corbacho

Until Kay Sievers bus_id variable length code goes in, this is a temporary workaround.

CC Len Brown <[email protected]>
CC Matthew Garrett <[email protected]>

---

 drivers/acpi/wmi.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)
drivers/acpi/wmi.c +1 -1
index 52015ba..b922fd1 100644
@@ -864,7 +864,7 @@ static int wmi_create_devs(void)
 		gblock = &wblock->gblock;
 
 		wmi_gtoa(gblock->guid, guid_string);
-		memcpy(guid_dev->bus_id, guid_string, 36);
+		memcpy(guid_dev->bus_id, guid_string, 19);
 
 		result = device_register(guid_dev);
 		if (result)
#6 lore raw
In reply to Carlos Corbacho
Quoted 11 lines · On Thursday 27 December 2007 15:38:04 Carlos Corbacho wrote:

Patch #1: (WMI - driver and in kernel interface)

Added per GUID event handling callbacks, as per Matthew's suggestion.

Len:
For review, and to go upstream.

(Bar the EC stuff, this code has also been inflic^^^^tested by acer_acpi users with my 0.10 releases, so I'm confident on Acer hardware at least that it won't break horribly).

Quoted 7 lines

Patch #2: (acer-wmi)

Add device detection for bluetooth and maximum brightness (removes more hard coded EC quirks). Also simplified some of the code.

Len:
For review, and to go upstream.

Len,

Could you please bump up reviewing patches #1 and #2 on your to-do list?

-Carlos

Signature · 3 lines
E-Mail: [email protected]
Web: strangeworlds.co.uk
GPG Key ID: 0x23EE722D