--- /dev/null
+/******************************************************************************
+ * drivers/xen/acpi-wmi/acpi-wmi.c
+ *
+ * Copyright (c) 2009 Kamala Narasimhan
+ * Copyright (c) 2009 Citrix Systems, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation; or, when distributed
+ * separately from the Linux kernel or incorporated into other
+ * software packages, subject to the following license:
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this source file (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/* Xen acpi-wmi implementation provides the interface required for userspace
+ * module (qemu) to communicate with acpi wmi wrapper kernel driver.
+ * Upon receiving request from qemu to call a WMI method or query or set WMI
+ * data, it communicates the request to kernel acpi wmi layer which then
+ * interacts with the base firmware to get the necessary information/execute
+ * relevant AML method etc. The result returned by the base firmware is then
+ * communicated back to the userspace module (qemu).
+ */
+
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/acpi.h>
+#include <asm/uaccess.h>
+#include <xen/public/acpi-wmi.h>
+
+/* #define XEN_WMI_DEBUG */
+
+static bool xen_wmi_misc_dev_registered = false;
+static int xen_wmi_ioctl(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+
+static struct file_operations xen_wmi_fops = {
+ .owner = THIS_MODULE,
+ .ioctl = xen_wmi_ioctl,
+};
+
+static struct miscdevice xen_wmi_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = XEN_WMI_DEVICE_NAME,
+ .fops = &xen_wmi_fops,
+};
+
+/*
+ * xen_wmi_copy_input_buffer
+ */
+int xen_wmi_copy_input_buffer(xen_wmi_buffer_t *user_buf, struct acpi_buffer *acpi_buf)
+{
+ if ( user_buf->length <= 0 )
+ return XEN_WMI_SUCCESS;
+
+ acpi_buf->pointer = kmalloc(user_buf->length, GFP_KERNEL);
+ if ( acpi_buf->pointer == NULL )
+ {
+ printk("XEN WMI: xen_wmi_copy_input_buffer - Buffer allocation failure\n");
+ return XEN_WMI_NOT_ENOUGH_MEMORY;
+ }
+
+ if ( copy_from_user(acpi_buf->pointer,
+ (char __user *)user_buf->pointer,
+ user_buf->length) )
+ {
+ printk("XEN WMI: Unable to copy input buffer argument\n");
+ kfree(acpi_buf->pointer);
+ return XEN_WMI_EFAULT;
+ }
+
+ acpi_buf->length = user_buf->length;
+ return XEN_WMI_SUCCESS;
+}
+
+/*
+ * xen_wmi_copy_output_buffer
+ */
+int xen_wmi_copy_output_buffer(struct acpi_buffer *acpi_buf, xen_wmi_buffer_t *user_buf)
+{
+ /* IMPORTANT NOTE: It is a little short sighted to assume that the return type
+ * will not be anything other than buffer type. A follow-up check-in will
+ * cover more types.
+ */
+
+ union acpi_object *acpi_obj = acpi_buf->pointer;
+
+ if ( acpi_obj == NULL )
+ {
+ printk("XEN WMI: Invalid acpi buffer!\n");
+ return XEN_WMI_EFAULT;
+ }
+
+ if ( acpi_obj->type != ACPI_TYPE_BUFFER )
+ {
+ printk("XEN WMI: Unsupported acpi return object type - %d\n", acpi_obj->type);
+ return XEN_WMI_UNSUPPORTED_TYPE;
+ }
+
+ if ( copy_to_user((char __user *) user_buf->copied_length, &acpi_obj->buffer.length, sizeof(size_t)) )
+ {
+ printk("XEN WMI: Invalid copied length user buffer!\n");
+ return XEN_WMI_INVALID_ARGUMENT;
+ }
+
+ if ( user_buf->length < acpi_obj->buffer.length )
+ {
+ printk("XEN WMI: Required buffer length is - %d\n", acpi_obj->buffer.length);
+ printk("XEN WMI: Passed in length is - %d\n", user_buf->length);
+ return XEN_WMI_BUFFER_TOO_SMALL;
+ }
+
+ if ( copy_to_user((char __user *) user_buf->pointer, acpi_obj->buffer.pointer, acpi_obj->buffer.length) )
+ {
+ printk("XEN WMI: Invalid user output buffer\n");
+ return XEN_WMI_NOT_ENOUGH_MEMORY;
+ }
+
+ return XEN_WMI_SUCCESS;
+}
+
+#ifdef XEN_WMI_DEBUG
+
+/*
+ * xen_wmi_print_buffer
+ */
+void xen_wmi_print_buffer(struct acpi_buffer *acpi_buf)
+{
+ int count;
+
+ printk("XEN WMI: Output buffer length is - %d\n", acpi_buf->buffer.length);
+ printk("XEN WMI: Buffer: ");
+ for (count=0; count < acpi_buf->buffer.length; count++)
+ printk("%d ", ((byte *)(acpi_buf->buffer.pointer))[count]);
+ printk("\n");
+}
+
+#endif /* XEN_WMI_DEBUG */
+
+/*
+ * xen_wmi_invoke_method
+ */
+int xen_wmi_invoke_method(xen_wmi_obj_invocation_data_t *obj_inv_data)
+{
+ int result;
+ struct acpi_buffer in_buf, *in_arg = NULL, out_buf = {ACPI_ALLOCATE_BUFFER, NULL};
+
+ result = xen_wmi_copy_input_buffer(&obj_inv_data->xen_wmi_arg.xen_wmi_method_arg.in_buf,
+ &in_buf);
+ if ( result != XEN_WMI_SUCCESS )
+ return result;
+
+ if ( in_buf.length > 0 )
+ in_arg = &in_buf;
+
+ result = wmi_evaluate_method(obj_inv_data->guid,
+ obj_inv_data->xen_wmi_arg.xen_wmi_method_arg.instance,
+ obj_inv_data->xen_wmi_arg.xen_wmi_method_arg.method_id,
+ in_arg, &out_buf);
+
+ if ( in_arg != NULL )
+ kfree(in_buf.pointer);
+
+ if ( out_buf.length > 0 && result == XEN_WMI_SUCCESS )
+ {
+#ifdef XEN_WMI_DEBUG
+ xen_wmi_print_buffer(&out_buf);
+#endif
+ result = xen_wmi_copy_output_buffer(&out_buf,
+ &obj_inv_data->xen_wmi_arg.xen_wmi_method_arg.out_buf);
+ kfree(out_buf.pointer);
+ }
+ else if ( result != XEN_WMI_SUCCESS )
+ printk("XEN WMI- Invoke WMI method failed with error - %d\n", result);
+
+ return result;
+}
+
+/*
+ * xen_wmi_query_object
+ */
+int xen_wmi_query_object(xen_wmi_obj_invocation_data_t *obj_inv_data)
+{
+ int result;
+ struct acpi_buffer out_buf = {ACPI_ALLOCATE_BUFFER, NULL};
+
+ result = wmi_query_block(obj_inv_data->guid,
+ obj_inv_data->xen_wmi_arg.xen_wmi_query_obj_arg.instance,
+ &out_buf);
+
+ if ( out_buf.length > 0 && result == XEN_WMI_SUCCESS )
+ {
+#ifdef XEN_WMI_DEBUG
+ xen_wmi_print_buffer(&out_buf);
+#endif
+ result = xen_wmi_copy_output_buffer(&out_buf,
+ &obj_inv_data->xen_wmi_arg.xen_wmi_query_obj_arg.out_buf);
+ kfree(out_buf.pointer);
+ }
+ else
+ printk("XEN WMI - Query WMI object failed with error - %d; output buffer length - %d\n",
+ result, out_buf.length);
+
+ return result;
+}
+
+/*
+ * xen_wmi_set_object
+ */
+int xen_wmi_set_object(xen_wmi_obj_invocation_data_t *obj_inv_data)
+{
+ int result;
+ struct acpi_buffer in_buf;
+
+ if ( obj_inv_data->xen_wmi_arg.xen_wmi_set_obj_arg.in_buf.length <= 0 )
+ return XEN_WMI_INVALID_ARGUMENT;
+
+ result = xen_wmi_copy_input_buffer(&obj_inv_data->xen_wmi_arg.xen_wmi_set_obj_arg.in_buf,
+ &in_buf);
+ if ( result != XEN_WMI_SUCCESS )
+ return result;
+
+ result = wmi_set_block(obj_inv_data->guid,
+ obj_inv_data->xen_wmi_arg.xen_wmi_set_obj_arg.instance,
+ &in_buf);
+ if ( result != XEN_WMI_SUCCESS )
+ printk("XEN WMI: Set object failed with error - %d\n", result);
+
+ kfree(in_buf.pointer);
+ return result;
+}
+
+/*
+ * xen_wmi_get_event_data
+ */
+int xen_wmi_get_event_data(xen_wmi_obj_invocation_data_t *obj_inv_data)
+{
+ int result;
+ struct acpi_buffer out_buf = {ACPI_ALLOCATE_BUFFER, NULL};
+
+ result = wmi_get_event_data(obj_inv_data->xen_wmi_arg.xen_wmi_event_data_arg.event_id,
+ &out_buf);
+
+ if ( out_buf.length > 0 && result == XEN_WMI_SUCCESS )
+ {
+#ifdef XEN_WMI_DEBUG
+ xen_wmi_print_buffer(&out_buf);
+#endif
+ result = xen_wmi_copy_output_buffer(&out_buf,
+ &obj_inv_data->xen_wmi_arg.xen_wmi_event_data_arg.out_buf);
+ kfree(out_buf.pointer);
+ }
+ else
+ printk("XEN WMI: Get event data failed with error - %d\n", result);
+
+ return result;
+}
+
+/*
+ * xen_wmi_ioctl
+ */
+static int xen_wmi_ioctl(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ xen_wmi_obj_invocation_data_t obj_inv_data;
+
+#ifdef XEN_WMI_DEBUG
+ printk("XEN WMI: In xen_wmi_ioctl - %d\n", cmd);
+#endif
+
+ memset(&obj_inv_data, 0, sizeof(obj_inv_data));
+ if ( copy_from_user(&obj_inv_data, (char __user *)arg, sizeof(obj_inv_data)) )
+ {
+ printk("XEN WMI: Invalid object invocation parameter\n");
+ return XEN_WMI_EFAULT;
+ }
+
+ switch ( cmd )
+ {
+ case XEN_WMI_IOCTL_CALL_METHOD:
+ return xen_wmi_invoke_method(&obj_inv_data);
+ case XEN_WMI_IOCTL_QUERY_OBJECT:
+ return xen_wmi_query_object(&obj_inv_data);
+ case XEN_WMI_IOCTL_SET_OBJECT:
+ return xen_wmi_set_object(&obj_inv_data);
+ case XEN_WMI_IOCTL_GET_EVENT_DATA:
+ return xen_wmi_get_event_data(&obj_inv_data);
+ }
+
+ return XEN_WMI_ENOIOCTLCMD;
+}
+
+/*
+ * xen_wmi_init
+ */
+static int __init xen_wmi_init(void)
+{
+ int ret;
+
+ ret = misc_register(&xen_wmi_misc);
+ if ( ret < 0 )
+ printk("XEN WMI: misc_register failed with error - %d\n", ret);
+ else
+ xen_wmi_misc_dev_registered = true;
+
+#ifdef XEN_WMI_DEBUG
+ printk("XEN WMI: xen-acpi-wmi misc_register succeeded!\n");
+#endif
+ return ret;
+}
+
+/*
+ * xen_wmi_exit
+ */
+static void xen_wmi_exit(void)
+{
+ int ret;
+
+ if ( xen_wmi_misc_dev_registered == false )
+ return;
+
+ if ( (ret = misc_deregister(&xen_wmi_misc)) < 0 )
+ printk("XEN WMI: misc_deregister failed with error - %d\n", ret);
+}
+
+module_init(xen_wmi_init);
+module_exit(xen_wmi_exit);
+MODULE_LICENSE("Dual BSD/GPL");
+
--- /dev/null
+/******************************************************************************
+ * acpi-wmi.h
+ *
+ * Interface to /proc/misc/xen-acpi-wmi
+ *
+ * Copyright (c) 2009 Kamala Narasimhan
+ * Copyright (c) 2009 Citrix Systems, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation; or, when distributed
+ * separately from the Linux kernel or incorporated into other
+ * software packages, subject to the following license:
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this source file (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+
+#ifndef _XEN_WMI_ACPI
+#define _XEN_WMI_ACPI
+
+/*
+ * Userspace Interface
+ */
+
+#define XEN_WMI_DEVICE_NAME "xen-acpi-wmi"
+#define XEN_WMI_GUID_SIZE 16
+
+#define XEN_WMI_SUCCESS 0
+#define XEN_WMI_UNSUPPORTED_TYPE -1
+#define XEN_WMI_BUFFER_TOO_SMALL -11
+#define XEN_WMI_NOT_ENOUGH_MEMORY -12
+#define XEN_WMI_EFAULT -14
+#define XEN_WMI_INVALID_ARGUMENT -22
+#define XEN_WMI_ENOIOCTLCMD -515
+
+#define XEN_WMI_IOCTL_CALL_METHOD 100
+#define XEN_WMI_IOCTL_QUERY_OBJECT 101
+#define XEN_WMI_IOCTL_SET_OBJECT 102
+#define XEN_WMI_IOCTL_GET_EVENT_DATA 103
+
+typedef unsigned char byte;
+
+typedef struct xen_wmi_buffer {
+ size_t length;
+ void *pointer;
+ size_t *copied_length;
+} xen_wmi_buffer_t;
+
+typedef struct xen_wmi_obj_invocation_data {
+ byte guid[XEN_WMI_GUID_SIZE];
+ union {
+ struct {
+ ushort instance;
+ uint method_id;
+ xen_wmi_buffer_t in_buf;
+ xen_wmi_buffer_t out_buf;
+ } xen_wmi_method_arg;
+
+ struct {
+ ushort instance;
+ xen_wmi_buffer_t out_buf;
+ } xen_wmi_query_obj_arg;
+
+ struct {
+ ushort instance;
+ xen_wmi_buffer_t in_buf;
+ } xen_wmi_set_obj_arg;
+
+ struct {
+ ushort event_id;
+ xen_wmi_buffer_t out_buf;
+ } xen_wmi_event_data_arg;
+ } xen_wmi_arg;
+} xen_wmi_obj_invocation_data_t;
+
+#endif /* _XEN_WMI_ACPI */
+