From: Carlos Corbacho <[EMAIL PROTECTED]>
Userspace:
There is a userspace interface in /sys/firmware/acpi/wmi for WMI methods
and data.
/sys/firmware/acpi/wmi/
|
|-> <GUID>/
|-> type (method, data, event)
Method & data blocks
|-> <instance>/
|-> data (binary data file - write input data to file, read file
to execute method or retrieve data).
Method only
|-> method_id (write value of method id to execute)
Events - passed to userspace via netlink. However, the extra WMI data
associated with an event is exposed through sysfs.
|-> notification (ACPI event value)
|-> data (binary data file - WMI data associated with the event)
Signed-off-by: Carlos Corbacho <[EMAIL PROTECTED]>
---
drivers/acpi/wmi.c | 332 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 332 insertions(+), 0 deletions(-)
diff --git a/drivers/acpi/wmi.c b/drivers/acpi/wmi.c
index 88b326b..59c3d76 100644
--- a/drivers/acpi/wmi.c
+++ b/drivers/acpi/wmi.c
@@ -42,6 +42,7 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/types.h>
+#include <linux/sysfs.h>
#include <acpi/acpi_drivers.h>
#define ACPI_WMI_CLASS "wmi"
@@ -202,6 +203,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 guid_block **out)
{
char tmp[16], guid_input[16];
@@ -479,6 +507,308 @@ bool wmi_has_guid(const char *guid_string)
}
EXPORT_SYMBOL_GPL(wmi_has_guid);
+/*
+ * sysfs interface
+ */
+struct wmi_attribute {
+ struct attribute attr;
+ ssize_t (*show)(struct kobject *kobj, char *buf);
+ ssize_t (*store)(struct kobject *kobj, const char *buf, ssize_t count);
+};
+
+#define WMI_ATTR(_name, _mode, _show, _store) \
+struct wmi_attribute wmi_attr_##_name = __ATTR(_name, _mode, _show, _store);
+
+#define to_attr(a) container_of(a, struct wmi_attribute, attr)
+
+static ssize_t show(struct kobject *kobj, struct attribute *attr, char *buf)
+{
+ struct wmi_attribute *wmi_attr = to_attr(attr);
+ ssize_t ret = 0;
+
+ if (wmi_attr->show)
+ ret = wmi_attr->show(kobj, buf);
+ return ret;
+}
+
+static ssize_t store(struct kobject *kobj, struct attribute *attr, const
+ char *buf, size_t count)
+{
+ struct wmi_attribute *wmi_attr = to_attr(attr);
+ ssize_t ret = 0;
+
+ if (wmi_attr->store)
+ ret = wmi_attr->store(kobj, buf, count);
+ return ret;
+}
+
+static struct sysfs_ops wmi_sysfs_ops = {
+ .show = show,
+ .store = store,
+};
+
+static struct kobj_type ktype_wmi = {
+ .sysfs_ops = &wmi_sysfs_ops,
+};
+
+struct guid_kobjects {
+ struct kobject guid_kobj;
+ struct kobject *instance_kobjs;
+ struct kobject *params;
+ void *data;
+ size_t data_size;
+ u8 instances;
+ u8 method_id;
+};
+
+static struct kobject wmi_kobj;
+static struct guid_kobjects *wmi_guid_kobj;
+
+static ssize_t wmi_data_read(struct kobject *kobj, struct bin_attribute
+ *bin_attr, char *buf, loff_t offset, size_t count) {
+ u8 method_id;
+ int i;
+ u8 instance;
+ const char *guid;
+ struct guid_kobjects *gkobj;
+ struct acpi_buffer in;
+ struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
+ union acpi_object *obj;
+
+ guid = kobject_name(kobj->parent);
+
+ for (i = 0; i < guids.total; i++) {
+ gkobj = &wmi_guid_kobj[i];
+ if (memcmp(kobject_name(&gkobj->guid_kobj), guid, 36) == 0) {
+ instance = simple_strtoul(kobject_name(kobj), NULL, 10);
+
+ method_id = gkobj->method_id;
+
+ if (guids.pointer[i].flags & ACPI_WMI_METHOD) {
+ if (gkobj->data) {
+ in.pointer = gkobj->data;
+ in.length = gkobj->data_size;
+ wmi_evaluate_method(guid, instance,
+ method_id, &in, &out);
+ } else {
+ wmi_evaluate_method(guid, instance,
+ method_id, NULL, &out);
+ }
+ break;
+ } else {
+ wmi_query_block(guid, instance, &out);
+ break;
+ }
+ }
+ }
+
+ obj = (union acpi_object *) out.pointer;
+ buf = obj->buffer.pointer;
+
+ return 0;
+}
+
+static ssize_t wmi_data_write(struct kobject *kobj, struct bin_attribute
+ *bin_attr, char *buf, loff_t offset, size_t count){
+ int i;
+
+ for (i = 0; i < guids.total; i++) {
+ if (memcmp(kobject_name(&wmi_guid_kobj[i].guid_kobj),
+ kobject_name(kobj->parent), 36) == 0) {
+ kfree(wmi_guid_kobj[i].data);
+ wmi_guid_kobj[i].data = kzalloc(count, GFP_KERNEL);
+ memcpy(wmi_guid_kobj[i].data, buf, count);
+ wmi_guid_kobj[i].data_size = count;
+ return count;
+ }
+ }
+ return -EINVAL;
+}
+
+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) {
+ int i;
+ const char *guid;
+ struct guid_kobjects *gkobj;
+ struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
+ union acpi_object *obj;
+
+ guid = kobject_name(kobj->parent);
+
+ for (i = 0; i < guids.total; i++) {
+ gkobj = &wmi_guid_kobj[i];
+ if (memcmp(kobject_name(&gkobj->guid_kobj), guid, 36) == 0)
+ wmi_get_event_data(guids.pointer[i].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,
+};
+
+/* sysfs calls */
+static ssize_t
+show_guid_type(struct kobject *kobj, char *buf)
+{
+ struct guid_block *block;
+
+ find_guid(kobject_name(kobj), &block);
+
+ 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 WMI_ATTR(type, S_IRUGO, show_guid_type, NULL);
+
+static ssize_t show_guid_method_id(struct kobject *kobj, char *buf)
+{
+ int i;
+
+ for (i = 0; i < guids.total; i++) {
+ if (memcmp(kobject_name(&wmi_guid_kobj[i].guid_kobj),
+ kobject_name(kobj->parent), 36) == 0)
+ return sprintf(buf, "%d\n", wmi_guid_kobj[i].method_id);
+ }
+ return sprintf(buf, "Error\n");
+}
+
+static ssize_t set_guid_method_id(struct kobject *kobj, const char *buf,
+ ssize_t count)
+{
+ int i;
+ u8 method_id;
+
+ method_id = simple_strtoul(buf, NULL, 10);
+
+ for (i = 0; i < guids.total; i++) {
+ if (memcmp(kobject_name(&wmi_guid_kobj[i].guid_kobj),
+ kobject_name(kobj->parent), 36) == 0) {
+ wmi_guid_kobj[i].method_id = method_id;
+ return count;
+ }
+ }
+ return -EINVAL;
+}
+static WMI_ATTR(method_id, S_IWUGO | S_IRUGO, show_guid_method_id,
+ set_guid_method_id);
+
+static ssize_t show_event_notification(struct kobject *kobj, char *buf)
+{
+ int i;
+
+ for (i = 0; i < guids.total; i++) {
+ if (memcmp(kobject_name(&wmi_guid_kobj[i].guid_kobj),
+ kobject_name(kobj), 36) == 0)
+ return sprintf(buf, "%d\n",
+ guids.pointer[i].notify_id & 0xFF);
+ }
+ return sprintf(buf, "Error\n");
+}
+static WMI_ATTR(notification, S_IRUGO, show_event_notification, NULL);
+
+static int wmi_sysfs_init(void)
+{
+ int i, j, result;
+ u8 instances;
+ char guid_string[37];
+ char temp[4];
+ struct kobject *guid_kobj;
+ struct kobject *instance_kobjs;
+ struct guid_block *block;
+
+ wmi_guid_kobj = kzalloc(sizeof(struct guid_kobjects) * guids.total,
+ GFP_KERNEL);
+
+ wmi_kobj.parent = &acpi_subsys.kobj;
+ wmi_kobj.ktype = &ktype_wmi;
+ kobject_set_name(&wmi_kobj, "wmi");
+
+ result = kobject_register(&wmi_kobj);
+ if (result)
+ return result;
+
+ /* Create directories for all the GUIDs */
+ for (i = 0; i < guids.total; i++) {
+ guid_kobj = &wmi_guid_kobj[i].guid_kobj;
+ guid_kobj->parent = &wmi_kobj;
+ guid_kobj->ktype = &ktype_wmi;
+
+ block = guids.pointer + i;
+ wmi_gtoa(block->guid, guid_string);
+ kobject_set_name(guid_kobj, guid_string);
+
+ result = kobject_register(guid_kobj);
+ if (result)
+ return result;
+
+ result = sysfs_create_file(guid_kobj, &wmi_attr_type.attr);
+ if (result)
+ return result;
+
+ if (guids.pointer[i].flags & ACPI_WMI_EVENT) {
+ result = sysfs_create_file(guid_kobj,
+ &wmi_attr_notification.attr);
+ if (result)
+ return result;
+
+ result = sysfs_create_bin_file(guid_kobj,
+ &wmi_attr_event_data);
+ if (result)
+ return result;
+ break;
+ }
+
+ /* Create directories for all the instances */
+ instances = guids.pointer[i].instance_count;
+ wmi_guid_kobj[i].instances = instances;
+ wmi_guid_kobj[i].instance_kobjs = kzalloc(sizeof(struct kobject)
+ * instances, GFP_KERNEL);
+ instance_kobjs = wmi_guid_kobj[i].instance_kobjs;
+ for (j = 0; j < instances; j++) {
+ instance_kobjs[j].parent = guid_kobj;
+ instance_kobjs[j].ktype = &ktype_wmi;
+ sprintf(temp, "%d", j+1);
+ kobject_set_name(&instance_kobjs[j], temp);
+
+ result = kobject_register(&instance_kobjs[j]);
+ if (result)
+ return result;
+
+ /* Create the relevant files under each instance */
+ result = sysfs_create_bin_file(&instance_kobjs[j],
+ &wmi_attr_data);
+ if (result)
+ return result;
+
+ if (guids.pointer[i].flags & ACPI_WMI_METHOD) {
+ result = sysfs_create_file(&instance_kobjs[j],
+ &wmi_attr_method_id.attr);
+ if (result)
+ return result;
+ }
+ }
+ }
+ return 0;
+}
+
/**
* parse_wdg - Parse the _WDG method for the GUID data blocks
*/
@@ -551,6 +881,8 @@ static int __init acpi_wmi_add(struct acpi_device *device)
if (ACPI_FAILURE(status))
return -ENODEV;
+ result = wmi_sysfs_init();
+
return result;
}
--
1.5.3.4
-
To unsubscribe from this list: send the line "unsubscribe linux-acpi" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at http://vger.kernel.org/majordomo-info.html