Skip to contentSkip to conversation

ACPI: wmi: Initial attempt at WMI ACPI support

RFC PATCH Superseded Carlos Corbacho 1 participant 1 message Oct 2, 2007 ACPI

v1 of 2 Superseded Latest is v2 →

lore
Share & tools
lore
#0 lore raw

RFC PATCH 1/1 ACPI: wmi: Initial attempt at WMI ACPI support

The following is my own basic implementation of the Windows Management Instrumentation (WMI) ACPI interface.

What it does:

Parses the _WDG method and exports functions to process WMI method calls (tested with my own hacked up version of acer_acpi), and basic query/ set commands (WQxx and WSxx _not_ marked as "expensive") based on GUID.

i.e.

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

wmi_query_block(char *guid, u32 instance, u32 method_id, struct acpi_buffer *out)

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

Where guid is the text encoding of the guid, e.g.:

"67C3371D-95A3-4C37-BB61-DD47B491DAAB"

What it might be able to do:

Handle reading data block GUIDs marked as "expensive" (e.g. calling WCxx with the correct arguments, before and after querying the block in question). My DSDT does not have any, so this is untested.

What it can't do:

Events - I've added a notify handler, but when we get a Notify() call to PNP0C14, what should happen next?

(The MS article[1] says something about "firing a WMI event", in conjunction with the data from the _WED method - would the driver be expected to send an event to proc/ netlink?).

This would also need to handle events marked as "expensive"

Unicode - The MS article calls for converting between ASCII and Unicode (or vice versa) if a GUID is marked as "string".

What it doesn't do:

Try to enforce any kind of MOF 'types' on the data - it just takes and returns acpi_buffer - interpretation is left to the end user.

What I would like to add:

A function that would return true if a given GUID exists on the system (for acer_acpi, this would make it much easier to distinguish between the two different WMI ACPI interfaces we support, rather than having to use wmi_evaluate_method(), which is overkill).

e.g.

bool wmi_guid_exists(char *guid)

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

---
 drivers/acpi/Kconfig  |   10 +
 drivers/acpi/Makefile |    1 +
 drivers/acpi/wmi.c    |  502 
+++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/acpi.h  |   11 +
 4 files changed, 524 insertions(+), 0 deletions(-)
 create mode 100644 drivers/acpi/wmi.c
drivers/acpi/Kconfig +10 -0
index 9685b75..b2074c3 100644
@@ -179,6 +179,16 @@ config ACPI_NUMA
 	depends on (X86 || IA64)
 	default y if IA64_GENERIC || IA64_SGI_SN2
 
+config ACPI_WMI
+	tristate "WMI ACPI Interface (EXPERIMENTAL)"
+	depends on EXPERIMENTAL
+	help
+	  This driver adds support for the WMI ACPI device (PNP0C14) found on
+	  some systems.
+
+	  NOTE: You will need another driver on top of this to actually use
+	  anything defined in the WMI ACPI device.
+
 config ACPI_ASUS
         tristate "ASUS/Medion Laptop Extras"
 	depends on X86
drivers/acpi/Makefile +1 -0
index 54e3ab0..9e9aa39 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 +192 -0

192 lines not on this page · raw message

instance,
+ u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out)
+{
+ acpi_status status;
+ struct acpi_object_list input, wc_input;
+ union acpi_object wm_params[3], wc_params[1], wq_params[1],
+ ws_params[2];
+ char method[4] = "";
+ char wc_method[4] = "";
+
+ if (block->flags & WMIACPI_FLAG_METHOD) {
+ strcat(method, "WM");
+ input.count = 3;
+ input.pointer = wm_params;
+ wm_params[0].type = ACPI_TYPE_INTEGER;
+ wm_params[0].integer.value = instance;
+ wm_params[1].type = ACPI_TYPE_INTEGER;
+ wm_params[1].integer.value = method_id;
+
+ if (in != NULL) {
+ wm_params[2].type = ACPI_TYPE_BUFFER;
+ wm_params[2].buffer.length = in->length;
+ wm_params[2].buffer.pointer = in->pointer;
+ }
+ } else {
+ /* Data block */
+ if (in == NULL) {
+ /* Query/ collection */
+
+ strcat(method, "WQ");
+ input.count = 1;
+ input.pointer = wq_params;
+ wq_params[0].type = ACPI_TYPE_INTEGER;
+ wq_params[0].integer.value = instance;
+
+ /*
+ * If WMIACPI_FLAG_EXPENSIVE, call the relevant WCxx
+ * method first to enable collection
+ */
+ /* FIXME - should INTEGER be STRING? */
+ if (block->flags & WMIACPI_FLAG_EXPENSIVE) {
+ wc_input.count = 1;
+ wc_input.pointer = wc_params;
+ wc_params[0].type = ACPI_TYPE_INTEGER;
+ wc_params[0].integer.value = instance;
+
+ strcat(wc_method, "WC");
+ strncat(wc_method, block->object_id, 2);
+
+ status = acpi_evaluate_object(acpi_wmi_handle,
+ wc_method, &wc_input, NULL);
+ }
+ } else if (out == NULL) {
+ /* Set */
+
+ strcat(method, "WS");
+ input.count = 2;
+ input.pointer = ws_params;
+ ws_params[0].type = ACPI_TYPE_INTEGER;
+ ws_params[0].integer.value = instance;
+ ws_params[1].type = ACPI_TYPE_BUFFER;
+ ws_params[1].buffer.length = in->length;
+ ws_params[1].buffer.pointer = in->pointer;
+
+ if (block->flags & WMIACPI_FLAG_STRING) {
+ /* Convert input from Unicode to ASCIIZ */
+ return AE_NOT_IMPLEMENTED;
+ }
+ }
+ }
+
+ strncat(method, block->object_id, 2);
+ DEBUG(2, "Object to call is %s \n", method);
+
+ status = acpi_evaluate_object(acpi_wmi_handle, method, &input, out);
+
+ if (in == NULL) {
+ /* If WMIACPI_FLAG_EXPENSIVE, call the relevant WCxx method */
+ if (block->flags & WMIACPI_FLAG_EXPENSIVE) {
+ wc_params[0].integer.value = 0x0;
+
+ status = acpi_evaluate_object(acpi_wmi_handle,
+ wc_method, &wc_input, NULL);
+ }
+
+ if (block->flags & WMIACPI_FLAG_STRING) {
+ /* Convert output from ASCIIZ to Unicode */
+ return AE_NOT_IMPLEMENTED;
+ }
+ }
+
+ return status;
+}
+
+static acpi_status wmi_evaluate_block(char *guid_string, u32 instance,
+ u32 method_id, const struct acpi_buffer *in,
+ struct acpi_buffer *out, bool query)
+{
+ char tmp[16], guid_input[16];
+ struct guid_mapping *block;
+ int i;
+
+ DEBUG(2, "Passing GUID to parser\n");
+ wmi_parse_guid(guid_string, tmp);
+ wmi_swap_bytes(tmp, guid_input);
+
+ for (i = 0; i < guids.total; i++) {
+ block = guids.pointer + i;
+
+ if (memcmp(block->guid, guid_input, 16) == 0) {
+ if (!(block->flags & WMIACPI_FLAG_EVENT) &&
+ !(block->flags & WMIACPI_FLAG_METHOD)) {
+ DEBUG(2, "GUID is a data block\n");
+ if (query) {
+ return wmi_evaluate_object(block,
+ instance, method_id, NULL, out);
+ } else {
+ return wmi_evaluate_object(block,
+ instance, method_id, in, NULL);
+ }
+ } else {
+ DEBUG(2, "GUID is not a data block\n");
+ return AE_BAD_DATA;
+ }
+ } else {
+ DEBUG(0, "Failed to match GUID\n");
+ }
+ }
+
+ DEBUG(2, "Unable to match GUID\n");
+ return AE_BAD_ADDRESS;
+}
+
+/*
+ * Externally callable WMI functions
+ */
+int wmi_evaluate_method(char *guid_string, u32 instance, u32 method_id,
+ const struct acpi_buffer *in, struct acpi_buffer *out)
+{
+ char tmp[16], guid_input[16];
+ struct guid_mapping *block;
+ int i;
+
+ DEBUG(2, "Passing GUID to parser\n");
+ wmi_parse_guid(guid_string, tmp);
+ wmi_swap_bytes(tmp, guid_input);
+
+ for (i = 0; i < guids.total; i++) {
+ block = guids.pointer + i;
+
+ if (memcmp(block->guid, guid_input, 16) == 0) {
+ if (block->flags & WMIACPI_FLAG_METHOD) {
+ DEBUG(2, "GUID is a method\n");
+ return wmi_evaluate_object(block, instance,
+ method_id, in, out);
+ } else {
+ DEBUG(2, "GUID is not a method\n");
+ return AE_BAD_DATA;
+ }
+ }
+ }
+
+ DEBUG(2, "Unable to match GUID\n");
+ return AE_BAD_ADDRESS;
+}
+EXPORT_SYMBOL(wmi_evaluate_method);
+
+int wmi_query_block(char *guid_string, u32 instance, u32 method_id,
+ struct acpi_buffer *out)
+{
+ return wmi_evaluate_block(guid_string, instance,
+ method_id, NULL, out, 1);
+}
+EXPORT_SYMBOL(wmi_query_block);
+
+int wmi_set_block(char *guid_string, u32 instance, u32 method_id,
+ const struct acpi_buffer *in)
+{
+ return wmi_evaluate_block(guid_string, instance,
+ method_id, in, NULL, 0);
+}
+EXPORT_SYMBOL(wmi_set_block);
+
+/*
+ * Parse the _WDG method for the data blocks
+ */
+static int parse_wdg(void)
+{
+ struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
+ union acpi_object *obj;
+ acpi_status status;
+
+ status = acpi_evaluate_object(acpi_wmi_handle, "_WDG", NULL, &out);
+
+ if (ACPI_FAILURE(status)) {
+ DEBUG(2, "Problem with evaluating _WDG\n");
+ return status;
+ }
+
+ obj = (union *acpi_object) out.pointer;
+
+ if (obj->type != ACPI_TYPE_BUFFER) {
+ DEBUG(2, "Output is not a buffer\n");
+ return AE_ERROR;
+ }
+
+ DEBUG(2, "Size of _WDG output is %d bytes\n", obj->buffer.length);
+
+ guids.pointer = (struct *guid_mapping) obj->buffer.pointer;
+
+ DEBUG(2, "GUID blocks = %lu \n", obj->buffer.length /
+ sizeof(struct guid_mapping));
+ guids.total = obj->buffer.length / sizeof(struct guid_mapping);
+ return AE_OK;
+}
+
+static void acpi_wmi_notify(acpi_handle handle, u32 event, void *data) +{
+ int i;
+ struct guid_mapping *block;
+ struct acpi_device *device = NULL;
+
+ DEBUG(1, "Notifier triggered\n");
+
+ for (i = 0; i < guids.total; i++) {
+ block = guids.pointer + i;
+
+ /* FIXME - the last comparison may be broken */
+ if (block->flags & WMIACPI_FLAG_EVENT
+ && block->notification_value == event)
+ /*
+ * What now? Should we generate an event? And how do we
+ * pass the results of _WED?
+ */
+ }
+}
+
+static int acpi_wmi_add(struct acpi_device *device)
+{
+ acpi_status status;
+
+ DEBUG(2, "acpi_wmi_add called\n");
+ acpi_wmi_handle = device->handle;
+ parse_wdg();
+
+ status = acpi_install_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
+ acpi_wmi_notify, device);
+
+ if (ACPI_FAILURE(status)) {
+ DEBUG(1, "Error installing notify handler\n");
+ /* FIXME - Is this critical enough to justify bailing out? */
+ } else {
+ DEBUG(1, "Notify handler installed\n");
+ }
+
+ return AE_OK;
+}
+
+static int acpi_wmi_remove(struct acpi_device *device, int type) +{
+ acpi_status status;
+
+ status = acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
+ acpi_wmi_notify);
+
+ if (ACPI_FAILURE(status)) {
+ DEBUG(1, "Error removing notification handler\n");
+ } else {
+ DEBUG(1, "Notification handler installed\n");
+ }
+
+ return AE_OK;
+}
+
+static struct acpi_driver acpi_wmi_driver = {
+ .name = "wmi",
+ .class = ACPI_WMI_CLASS,
+ .ids = wmi_device_ids,
+ .ops = {
+ .add = acpi_wmi_add,
+ .remove = acpi_wmi_remove,
+ },
+};
+
+static int __init acpi_wmi_init(void)
+{
+ int result;
+
+ if (acpi_disabled)
+ return -ENODEV;
+
+ result = acpi_bus_register_driver(&acpi_wmi_driver);
+
+ if (result < 0)
+ return -ENODEV;
+
+ DEBUG(1, "WMI succesfully loaded\n");
+
+ return 0;
+}
+
+static void __exit acpi_wmi_exit(void)
+{
+ acpi_bus_unregister_driver(&acpi_wmi_driver);
+ DEBUG(1, "WMI unloaded\n");
+
+ return;
+}
+
+module_init(acpi_wmi_init);
+module_exit(acpi_wmi_exit);

include/linux/acpi.h +11 -0
index bf5e000..1017c26 100644
@@ -186,6 +186,17 @@ extern int ec_transaction(u8 command,
 
 #endif /*CONFIG_ACPI_EC*/
 
+#ifdef CONFIG_ACPI_WMI
+
+extern int wmi_evaluate_method(char *guid, u32 instance, u32 methodId,
+	const struct acpi_buffer *in, struct acpi_buffer *out);
+extern int wmi_query_block(char *guid, u32 instance, u32 methodId,
+	struct acpi_buffer *out);
+extern int wmi_set_block(char *guid, u32 instance, u32 methodId,
+	const struct acpi_buffer *in);
+
+#endif	/* CONFIG_ACPI_WMI */
+
 extern int acpi_blacklisted(void);
 extern void acpi_bios_year(char *s);
 
Signature · 1 line
1.5.2.2