From: Steven Smith Date: Tue, 30 Jun 2009 11:55:48 +0000 (+0100) Subject: patch xen-acpi-wmi X-Git-Url: http://xenbits.xensource.com/gitweb?a=commitdiff_plain;h=f8d0736d3e6a71a54a23e2123bd8b60aae73605d;p=people%2Fssmith%2Fnc2-2.6.27.git patch xen-acpi-wmi --- diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 283f9083..2359480d 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -123,6 +123,12 @@ static struct acpi_ec { int acpi_ec_intr = 1; /* Default is interrupt mode */ +static u32 wmi_event_data_index = 0; +extern u8 in_query_wmi_event_data; +extern const u8 wmi_ec_max_data_size; +extern u32 wmi_ec_port_data_size; +extern u8 wmi_ec_port_data[32]; + /* -------------------------------------------------------------------------- Transaction Management -------------------------------------------------------------------------- */ @@ -377,6 +383,22 @@ static int acpi_ec_read(struct acpi_ec *ec, u8 address, u8 * data) result = acpi_ec_transaction(ec, &t, 0); *data = d; + + /* HACK ALERT + * Please refer to wmi.c for an explanation on why we added this hack. + */ + if ( in_query_wmi_event_data == TRUE ) { + if ( address == 0x2b ) { + wmi_event_data_index = 0; + memset(wmi_ec_port_data, 0, wmi_ec_max_data_size); + wmi_ec_port_data_size = *data; + } else if ( (address == 0x2c) && (wmi_event_data_index < wmi_ec_port_data_size) + && (wmi_event_data_index < wmi_ec_max_data_size) ) { + wmi_ec_port_data[wmi_event_data_index] = *data; + wmi_event_data_index++; + } + } + return result; } diff --git a/drivers/acpi/wmi.c b/drivers/acpi/wmi.c index cfe2c833..7765da45 100644 --- a/drivers/acpi/wmi.c +++ b/drivers/acpi/wmi.c @@ -1,13 +1,18 @@ /* * ACPI-WMI mapping driver * - * Copyright (C) 2007-2008 Carlos Corbacho + * Copyright (C) 2007-2008 Carlos Corbacho strangeworlds.co.uk> * - * GUID parsing code from ldm.c is: - * Copyright (C) 2001,2002 Richard Russon - * Copyright (c) 2001-2007 Anton Altaparmakov - * Copyright (C) 2001,2002 Jakob Kemi + * Modifications: + * Copyright (c) 2009 Kamala Narasimhan - Citrix Systems, Inc. * + * Following modifications where made to fit our usecase - + * a) Route WMI events to acpid. + * b) Remove exports not required for our usecase, remove notification + * installation/uninstallation code (as we now route events to acpid). + * c) Minor device id issue. + * d) Remove GUID parsing code as our usecase does not require supporting + * 36 char guid input. * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * This program is free software; you can redistribute it and/or modify @@ -32,6 +37,7 @@ #include #include #include +#include #include #include @@ -45,8 +51,6 @@ MODULE_LICENSE("GPL"); #undef PREFIX #define PREFIX "ACPI: WMI: " -static DEFINE_MUTEX(wmi_data_lock); - struct guid_block { char guid[16]; union { @@ -64,11 +68,25 @@ struct wmi_block { struct list_head list; struct guid_block gblock; acpi_handle handle; - wmi_notify_handler handler; - void *handler_data; }; static struct wmi_block wmi_blocks; +static DEFINE_MUTEX(wmi_mutex); + +/* + * YIKEES! HACK ALERT + * Due to buffer dereferencing bugs at firmware layer, some firmware(s) + * (Dell in specific) return all 0s for wmi event data when + * queried through _WED. To work around this firmware bug and provide + * appropriate event data to guest firmware, we will sniff the embedded + * controller port access to addresses 0x2b and 0x2c and slap that data + * at the beginning of _WED return buffer which would otherwise be all 0s. + */ +static u8 enable_wmi_event_data_hack = FALSE; +u8 in_query_wmi_event_data = FALSE; +const u8 wmi_ec_max_data_size = 32; +u32 wmi_ec_port_data_size = 0; +u8 wmi_ec_port_data[32]; /* * If the GUID data block is marked as expensive, we must enable and @@ -81,11 +99,10 @@ static struct wmi_block wmi_blocks; static int acpi_wmi_remove(struct acpi_device *device, int type); static int acpi_wmi_add(struct acpi_device *device); - static const struct acpi_device_id wmi_device_ids[] = { - {"PNP0C14", 0}, - {"pnp0c14", 0}, - {"", 0}, + {"PNP0C14", 0}, + {"pnp0c14", 0}, + {"", 0}, }; MODULE_DEVICE_TABLE(acpi, wmi_device_ids); @@ -99,116 +116,17 @@ static struct acpi_driver acpi_wmi_driver = { }, }; -/* - * GUID parsing functions - */ - -/** - * wmi_parse_hexbyte - Convert a ASCII hex number to a byte - * @src: Pointer to at least 2 characters to convert. - * - * Convert a two character ASCII hex string to a number. - * - * Return: 0-255 Success, the byte was parsed correctly - * -1 Error, an invalid character was supplied - */ -static int wmi_parse_hexbyte(const u8 *src) -{ - unsigned int x; /* For correct wrapping */ - int h; - - /* high part */ - x = src[0]; - if (x - '0' <= '9' - '0') { - h = x - '0'; - } else if (x - 'a' <= 'f' - 'a') { - h = x - 'a' + 10; - } else if (x - 'A' <= 'F' - 'A') { - h = x - 'A' + 10; - } else { - return -1; - } - h <<= 4; - - /* low part */ - x = src[1]; - if (x - '0' <= '9' - '0') - return h | (x - '0'); - if (x - 'a' <= 'f' - 'a') - return h | (x - 'a' + 10); - if (x - 'A' <= 'F' - 'A') - return h | (x - 'A' + 10); - return -1; -} - -/** - * wmi_swap_bytes - Rearrange GUID bytes to match GUID binary - * @src: Memory block holding binary GUID (16 bytes) - * @dest: Memory block to hold byte swapped binary GUID (16 bytes) - * - * Byte swap a binary GUID to match it's real GUID value - */ -static void wmi_swap_bytes(u8 *src, u8 *dest) -{ - int i; - - for (i = 0; i <= 3; i++) - memcpy(dest + i, src + (3 - i), 1); - - for (i = 0; i <= 1; i++) - memcpy(dest + 4 + i, src + (5 - i), 1); - - for (i = 0; i <= 1; i++) - memcpy(dest + 6 + i, src + (7 - i), 1); - - memcpy(dest + 8, src + 8, 8); -} - -/** - * wmi_parse_guid - Convert GUID from ASCII to binary - * @src: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba - * @dest: Memory block to hold binary GUID (16 bytes) - * - * N.B. The GUID need not be NULL terminated. - * - * Return: 'true' @dest contains binary GUID - * 'false' @dest contents are undefined - */ -static bool wmi_parse_guid(const u8 *src, u8 *dest) -{ - static const int size[] = { 4, 2, 2, 2, 6 }; - int i, j, v; - - if (src[8] != '-' || src[13] != '-' || - src[18] != '-' || src[23] != '-') - return false; - - for (j = 0; j < 5; j++, src++) { - for (i = 0; i < size[j]; i++, src += 2, *dest++ = v) { - v = wmi_parse_hexbyte(src); - if (v < 0) - return false; - } - } - - return true; -} - static bool find_guid(const char *guid_string, struct wmi_block **out) { - char tmp[16], guid_input[16]; struct wmi_block *wblock; struct guid_block *block; struct list_head *p; - wmi_parse_guid(guid_string, tmp); - wmi_swap_bytes(tmp, guid_input); - list_for_each(p, &wmi_blocks.list) { wblock = list_entry(p, struct wmi_block, list); block = &wblock->gblock; - if (memcmp(block->guid, guid_input, 16) == 0) { + if (memcmp(block->guid, guid_string, 16) == 0) { if (out) *out = wblock; return 1; @@ -217,12 +135,46 @@ static bool find_guid(const char *guid_string, struct wmi_block **out) return 0; } +static acpi_status wmi_enable_event_data_blocks(int enable) +{ + struct list_head *p; + struct guid_block *gblock; + struct wmi_block *wblock; + char method[5]; + struct acpi_object_list input; + union acpi_object params[1]; + acpi_status status; + + list_for_each(p, &wmi_blocks.list) { + wblock = list_entry(p, struct wmi_block, list); + gblock = &wblock->gblock; + + if (gblock->flags & ACPI_WMI_EVENT) { + input.count = 1; + input.pointer = params; + params[0].type = ACPI_TYPE_INTEGER; + params[0].integer.value = enable; + + snprintf(method, 5, "WE%02X", gblock->notify_id); + status = acpi_evaluate_object(wblock->handle, method, &input, NULL); + + if (status != AE_OK && status != AE_NOT_FOUND) { + printk(KERN_INFO PREFIX "Event block %s enable failed\n", method); + return status; + } else + return AE_OK; + } + } + + return AE_OK; /* if we don't have a wmi block (though odd), just return success */ +} + /* * Exported WMI functions */ /** * wmi_evaluate_method - Evaluate a WMI method - * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba + * @guid_string: 16 byte guid * @instance: Instance index * @method_id: Method ID to call * &in: Buffer containing input for the method call @@ -247,7 +199,7 @@ u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out) block = &wblock->gblock; handle = wblock->handle; - if (!(block->flags & ACPI_WMI_METHOD)) + if (!(block->flags & ACPI_WMI_METHOD)) return AE_BAD_DATA; if (block->instance_count < instance) @@ -282,7 +234,7 @@ EXPORT_SYMBOL_GPL(wmi_evaluate_method); /** * wmi_query_block - Return contents of a WMI block - * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba + * @guid_string: 16 byte guid * @instance: Instance index * &out: Empty buffer to return the contents of the data block to * @@ -338,10 +290,10 @@ struct acpi_buffer *out) * expensive, but have no corresponding WCxx method. So we * should not fail if this happens. */ - wc_status = acpi_get_handle(handle, wc_method, &wc_handle); - if (ACPI_SUCCESS(wc_status)) - wc_status = acpi_evaluate_object(handle, wc_method, - &wc_input, NULL); + wc_status = acpi_get_handle(handle, wc_method, &wc_handle); + if (ACPI_SUCCESS(wc_status)) + wc_status = acpi_evaluate_object(handle, wc_method, + &wc_input, NULL); } strcpy(method, "WQ"); @@ -365,7 +317,7 @@ EXPORT_SYMBOL_GPL(wmi_query_block); /** * wmi_set_block - Write to a WMI block - * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba + * @guid_string: 16 byte guid * @instance: Instance index * &in: Buffer containing new values for the data block * @@ -416,61 +368,6 @@ const struct acpi_buffer *in) } EXPORT_SYMBOL_GPL(wmi_set_block); -/** - * wmi_install_notify_handler - Register handler for WMI events - * @handler: Function to handle notifications - * @data: Data to be returned to handler when event is fired - * - * Register a handler for events sent to the ACPI-WMI mapper device. - */ -acpi_status wmi_install_notify_handler(const char *guid, -wmi_notify_handler handler, void *data) -{ - struct wmi_block *block; - - if (!guid || !handler) - return AE_BAD_PARAMETER; - - find_guid(guid, &block); - if (!block) - return AE_NOT_EXIST; - - if (block->handler) - return AE_ALREADY_ACQUIRED; - - block->handler = handler; - block->handler_data = data; - - return AE_OK; -} -EXPORT_SYMBOL_GPL(wmi_install_notify_handler); - -/** - * wmi_uninstall_notify_handler - Unregister handler for WMI events - * - * Unregister handler for events sent to the ACPI-WMI mapper device. - */ -acpi_status wmi_remove_notify_handler(const char *guid) -{ - struct wmi_block *block; - - if (!guid) - return AE_BAD_PARAMETER; - - find_guid(guid, &block); - if (!block) - return AE_NOT_EXIST; - - if (!block->handler) - return AE_NULL_ENTRY; - - block->handler = NULL; - block->handler_data = NULL; - - return AE_OK; -} -EXPORT_SYMBOL_GPL(wmi_remove_notify_handler); - /** * wmi_get_event_data - Get WMI data associated with an event * @@ -486,6 +383,8 @@ acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out) struct guid_block *gblock; struct wmi_block *wblock; struct list_head *p; + acpi_status status; + uint count; input.count = 1; input.pointer = params; @@ -497,27 +396,31 @@ acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out) gblock = &wblock->gblock; if ((gblock->flags & ACPI_WMI_EVENT) && - (gblock->notify_id == event)) - return acpi_evaluate_object(wblock->handle, "_WED", + (gblock->notify_id == event)) { + mutex_lock(&wmi_mutex); + if ( enable_wmi_event_data_hack == TRUE ) { + wmi_ec_port_data_size = 0; + memset(wmi_ec_port_data, 0, 32); + in_query_wmi_event_data = TRUE; + } + status = acpi_evaluate_object(wblock->handle, "_WED", &input, out); + if ( enable_wmi_event_data_hack == TRUE ) { + for ( count = 0; count < wmi_ec_port_data_size; count++) + ((char *)((union acpi_object *) + out->pointer)->buffer.pointer)[count] + = wmi_ec_port_data[count]; + in_query_wmi_event_data = FALSE; + } + mutex_unlock(&wmi_mutex); + return status; + } } return AE_NOT_FOUND; } EXPORT_SYMBOL_GPL(wmi_get_event_data); -/** - * wmi_has_guid - Check if a GUID is available - * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba - * - * Check if a given GUID is defined by _WDG - */ -bool wmi_has_guid(const char *guid_string) -{ - return find_guid(guid_string, NULL); -} -EXPORT_SYMBOL_GPL(wmi_has_guid); - /* * Parse the _WDG method for the GUID data blocks */ @@ -621,12 +524,7 @@ static void acpi_wmi_notify(acpi_handle handle, u32 event, void *data) if ((block->flags & ACPI_WMI_EVENT) && (block->notify_id == event)) { - if (wblock->handler) - wblock->handler(event, wblock->handler_data); - - acpi_bus_generate_netlink_event( - device->pnp.device_class, device->dev.bus_id, - event, 0); + acpi_bus_generate_proc_event(device, event, 0); break; } } @@ -640,6 +538,7 @@ static int acpi_wmi_remove(struct acpi_device *device, int type) acpi_remove_address_space_handler(device->handle, ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler); + wmi_enable_event_data_blocks(0); return 0; } @@ -659,26 +558,33 @@ static int __init acpi_wmi_add(struct acpi_device *device) ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler, NULL, NULL); - if (ACPI_FAILURE(status)) + if (ACPI_FAILURE(status)) { + printk(KERN_ERR PREFIX "Error installing EC region handler\n"); return -ENODEV; + } status = parse_wdg(device->handle); if (ACPI_FAILURE(status)) { - printk(KERN_ERR PREFIX "Error installing EC region handler\n"); + printk(KERN_ERR PREFIX "parse_wdg failed!\n"); return -ENODEV; } + /* No need to check and fail if wmi_enable_event_data_blocks should fail. + * wmi_enable_event_data_blocks will print an error message. + */ + wmi_enable_event_data_blocks(1); return result; } static int __init acpi_wmi_init(void) { acpi_status result; + char *dmi_sys_info; INIT_LIST_HEAD(&wmi_blocks.list); - if (acpi_disabled) - return -ENODEV; + if (acpi_disabled) + return -ENODEV; result = acpi_bus_register_driver(&acpi_wmi_driver); @@ -688,6 +594,13 @@ static int __init acpi_wmi_init(void) printk(KERN_INFO PREFIX "Mapper loaded\n"); } + dmi_sys_info = dmi_get_system_info(DMI_SYS_VENDOR); + if ( dmi_sys_info == NULL ) + return result; + + if ( strstr(dmi_sys_info, "Dell") != NULL ) + enable_wmi_event_data_hack = TRUE; + return result; } @@ -708,5 +621,5 @@ static void __exit acpi_wmi_exit(void) printk(KERN_INFO PREFIX "Mapper unloaded\n"); } -subsys_initcall(acpi_wmi_init); +module_init(acpi_wmi_init); module_exit(acpi_wmi_exit); diff --git a/drivers/xen/Kconfig b/drivers/xen/Kconfig index c672d059..a215f637 100644 --- a/drivers/xen/Kconfig +++ b/drivers/xen/Kconfig @@ -265,6 +265,13 @@ config XEN_NR_GUEST_DEVICES Specify the total number of virtual devices (i.e. both frontend and backend) that you want the kernel to be able to service. +config XEN_ACPI_WMI_WRAPPER + tristate "Xen ACPI WMI wrapper driver" + depends on ACPI_WMI + help + Facilitates OEM specific hotkey implementation within + guest space. + choice prompt "Xen version compatibility" default XEN_COMPAT_030002_AND_LATER diff --git a/drivers/xen/Makefile b/drivers/xen/Makefile index ab35228c..873e5a3e 100644 --- a/drivers/xen/Makefile +++ b/drivers/xen/Makefile @@ -30,3 +30,4 @@ obj-$(CONFIG_XEN_GRANT_DEV) += gntdev/ obj-$(CONFIG_XEN_NETDEV_ACCEL_SFC_UTIL) += sfc_netutil/ obj-$(CONFIG_XEN_NETDEV_ACCEL_SFC_FRONTEND) += sfc_netfront/ obj-$(CONFIG_XEN_NETDEV_ACCEL_SFC_BACKEND) += sfc_netback/ +obj-$(CONFIG_XEN_ACPI_WMI_WRAPPER) += acpi-wmi/ diff --git a/drivers/xen/acpi-wmi/Makefile b/drivers/xen/acpi-wmi/Makefile new file mode 100644 index 00000000..afe5d9d2 --- /dev/null +++ b/drivers/xen/acpi-wmi/Makefile @@ -0,0 +1,2 @@ + +obj-y := acpi-wmi.o diff --git a/drivers/xen/acpi-wmi/acpi-wmi.c b/drivers/xen/acpi-wmi/acpi-wmi.c new file mode 100644 index 00000000..90d1e3f4 --- /dev/null +++ b/drivers/xen/acpi-wmi/acpi-wmi.c @@ -0,0 +1,352 @@ +/****************************************************************************** + * 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 +#include +#include +#include +#include + +/* #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; + union acpi_object *acpi_obj = acpi_buf->pointer; + + if ( acpi_obj == NULL || acpi_obj->type != ACPI_TYPE_BUFFER ) + { + printk("XEN WMI: Unsupported output buffer data!\n"); + return ; + } + + printk("XEN WMI: Output buffer length is - %d\n", acpi_obj->buffer.length); + printk("XEN WMI: Buffer: "); + for (count=0; count < acpi_obj->buffer.length; count++) + printk("%d ", ((byte *)(acpi_obj->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"); + diff --git a/include/xen/public/acpi-wmi.h b/include/xen/public/acpi-wmi.h new file mode 100644 index 00000000..097639ba --- /dev/null +++ b/include/xen/public/acpi-wmi.h @@ -0,0 +1,94 @@ +/****************************************************************************** + * 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 */ +