WMI
Subsystems (2)
cover WMI
Another WMI release.
Patch #1: (WMI - initial driver and in kernel interface)
Big change here is putting in a basic EC region handler - which is mostly a shameless rip off of the EC driver's acpi_ec_space_handler, with the exception that WMI doesn't need to care about the EC device, and just calls ec_{read,write} instead.
Therefore, CC'ing Alexey for the EC bit.
For review, and hopefully to go up to 2.6.25
Patch #2: (acer-wmi) - No Change
For review, and (rather tentatively here) 2.6.25 material?
Patch #3: (tc1100-wmi)
RFC only, needs actual testing on the hardware, and probably very broken. Waiting on Matthew Garrett to get some free time to test this.
Bluetooth renamed to jogdial, as per comments from Joshua Wise.
Patch #4: (WMI sysfs interface) - No Change
RFC only (see patch #5 for reason). Adds interface under /sys/devices/virtual/wmi
Changes: Added 'string' file to each GUID, which reports whether the GUID has the 'string' flag set (userspace needs to know for converting from its' format to ASCII and back).
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]). This should _NOT_ be applied to any upstream kernel.
-Carlos
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 with WMI. When a notification is sent to WMI, the handler registered with WMI is then called (it is left to the caller to ask for the WMI event data, 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.
---
MAINTAINERS | 7 drivers/acpi/Kconfig | 11 + drivers/acpi/Makefile | 1 drivers/acpi/wmi.c | 693 +++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/acpi.h | 20 + 5 files changed, 732 insertions(+), 0 deletions(-) create mode 100644 drivers/acpi/wmi.c
MAINTAINERS +7 -0
@@ -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
@@ -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
@@ -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 +693 -0
693 lines not on this page · raw message
include/linux/acpi.h +20 -0
@@ -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 (wmi_notify_handler handler, +void *data); +extern acpi_status wmi_remove_notify_handler (void ); +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);
Re: [PATCH 1/5] ACPI: WMI: Add ACPI-WMI mapping driver
Quoted 318 lines · Carlos Corbacho
The following is an implementation of the Windows Management Instrumentation (WMI) ACPI interface mapper (PNP0C14).
What it does:
Parses the
_WDGmethod 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 structacpi_buffer*in, structacpi_buffer*out)wmi_query_block(const char *guid, u8 instance,
structacpi_buffer*out)wmi_set_block(const char *guid, u38 instance,
const structacpi_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 with WMI. When a notification is sent to WMI, the handler registered with WMI is then called (it is left to the caller to ask for the WMI event data, 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 afind_guid()symbol - return true if a given GUID exists on the system
* wmi_* functions now return typeacpi_status(since they are just fancy wrappers aroundacpi_evaluate_object())
* Removed extra debug codev3 (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_WDGbuffer handling - copy the data to our own private structure.
* Change WMI from tristate to bool - otherwise the external functions are not exported inlinux/acpi.hif 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 handlingv5 (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).
* Exportwmi_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 anacpi_handledown to any WMI based drivers
- they should be able to function with only the calls provided in WMI.
* Update my e-mail addressv8 (2007-11-28)
* Convert back to a module.
* UpdateKconfigto 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_TABLEfor autoloading
* Checkpatch fixesv10 (2007-12-12)
* Workaround broken GUIDs declared expensive without a WCxx method.
* Minor cleanupsv11 (2007-12-17)
* More fixing for broken GUIDs declared expensive without a WCxx method.
* Add basic EmbeddedControl region handling.Signed-off-by Carlos Corbacho <[email protected]>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 | 693 +++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/acpi.h | 20 + 5 files changed, 732 insertions(+), 0 deletions(-) create mode 100644 drivers/acpi/wmi.cMAINTAINERS +7
-0@@ -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@@ -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 X86drivers/acpi/Makefile +1
-0@@ -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.odrivers/acpi/wmi.c new file +105
-0@@ -0,0 +1,693 @@ +/* +* ACPI-WMI mapping driver +* +* Copyright (C) 2007 Carlos Corbacho <[email protected]> +* +* GUID parsing code from ldm.c is: +* Copyright (C) 2001,2002 Richard Russon <[email protected]> +* Copyright (c) 2001-2007 Anton Altaparmakov +* Copyright (C) 2001,2002 Jakob Kemi <[email protected]> +* +* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License along +* with this program; if not, write to the Free Software Foundation, Inc., +* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +* +* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +*/ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/list.h> +#include <linux/acpi.h> +#include <acpi/acpi_bus.h> +#include <acpi/acpi_drivers.h> + +ACPI_MODULE_NAME ("wmi" ); +MODULE_AUTHOR ("Carlos Corbacho" ); +MODULE_DESCRIPTION ("ACPI-WMI Mapping Driver" ); +MODULE_LICENSE ("GPL" ); + +#define ACPI_WMI_CLASS "wmi" + +#undef PREFIX +#define PREFIX "ACPI: WMI: " + +static DEFINE_MUTEX (wmi_data_lock); + +struct guid_block { +char guid[16 ]; +union { +char object_id[2 ]; +struct { +unsigned char notify_id; +unsigned char reserved; + }; + }; +u8 instance_count; +u8 flags; +}; + +struct wmi_block { +struct list_head list; +struct guid_block gblock; +acpi_handle handle; +}; + +static struct wmi_block wmi_blocks; + +static wmi_notify_handler wmi_external_handler; +static void *wmi_external_data; + +/* +* If the GUID data block is marked as expensive, we must enable and +* explicitily disable data collection. +*/ +#define ACPI_WMI_EXPENSIVE 0x1 +#define ACPI_WMI_METHOD 0x2/* GUID is a method */ +#define ACPI_WMI_STRING 0x4/* GUID takes & returns a string */ +#define ACPI_WMI_EVENT 0x8/* GUID is an event */ + +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 }, +}; +MODULE_DEVICE_TABLE (acpi, wmi_device_ids); + +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, + }, +}; + +/* +* GUID parsing functions +*/
drivers/acpi/wmi.c line 115
Quoted 10 lines
drivers/acpi/wmi.c new file +10
-0+ +/** +* 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 +*/
Why don't use sscanf("%h")?
Quoted 441 lines
drivers/acpi/wmi.c new file +440
-0+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 (out) + *out = wblock; +return 1 ; + } + } +return 0 ; +} + +/* +* Exported WMI functions +*/ +/** +* wmi_evaluate_method - Evaluate a WMI method +* @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba +* @instance: Instance index +* @method_id: Method ID to call +* &in: Buffer containing input for the method call +* &out: Empty buffer to return the method results +* +* Convert a WMI method call to an ACPI one, and return the results +*/ +acpi_status wmi_evaluate_method (const char *guid_string,u8 instance, +u32 method_id,const struct acpi_buffer *in,struct acpi_buffer *out) +{ +struct guid_block *block =NULL ; +struct wmi_block *wblock =NULL ; +acpi_handle handle; +acpi_status status; +struct acpi_object_list input; +union acpi_object params[3 ]; +char method[4 ] ="WM" ; + +if (!find_guid (guid_string, &wblock)) +return AE_BAD_ADDRESS ; + + block = &wblock->gblock; + handle = wblock->handle; + +if (!block->flags &ACPI_WMI_METHOD ) +return AE_BAD_DATA ; + +if (block->instance_count < instance) +return AE_BAD_PARAMETER ; + + input.count =2 ; + input.pointer = params; + params[0 ].type =ACPI_TYPE_INTEGER ; + params[0 ].integer.value = instance; + params[1 ].type =ACPI_TYPE_INTEGER ; + params[1 ].integer.value = method_id; + +if (in) { + input.count =3 ; + +if (block->flags &ACPI_WMI_STRING ) { + params[2 ].type =ACPI_TYPE_STRING ; + }else { + params[2 ].type =ACPI_TYPE_BUFFER ; + } + params[2 ].buffer.length = in->length; + params[2 ].buffer.pointer = in->pointer; + } + +strncat (method, block->object_id,2 ); + + status =acpi_evaluate_object (handle, method, &input, out); + +return status; +} +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 +* @instance: Instance index +* &out: Empty buffer to return the contents of the data block to +* +* Return the contents of a data block to a buffer +*/ +acpi_status wmi_query_block (const char *guid_string,u8 instance, +struct acpi_buffer *out) +{ +struct guid_block *block =NULL ; +struct wmi_block *wblock =NULL ; +acpi_handle handle; +acpi_status status, wc_status =AE_ERROR ; +struct acpi_object_list input, wc_input; +union acpi_object wc_params[1 ], wq_params[1 ]; +char method[4 ] ="WQ" ; +char wc_method[4 ] ="WC" ; + +if (guid_string ==NULL || out ==NULL ) +return AE_BAD_PARAMETER ; + +if (!find_guid (guid_string, &wblock)) +return AE_BAD_ADDRESS ; + + block = &wblock->gblock; + handle = wblock->handle; + +if (block->instance_count < instance) +return AE_BAD_PARAMETER ; + +/* Check GUID is a data block */ +if (block->flags & (ACPI_WMI_EVENT |ACPI_WMI_METHOD )) +return AE_BAD_ADDRESS ; + + input.count =1 ; + input.pointer = wq_params; + wq_params[0 ].type =ACPI_TYPE_INTEGER ; + wq_params[0 ].integer.value = instance; + +/* +* If ACPI_WMI_EXPENSIVE, call the relevant WCxx method first to +* enable collection +*/ +if (block->flags &ACPI_WMI_EXPENSIVE ) { + wc_input.count =1 ; + wc_input.pointer = wc_params; + wc_params[0 ].type =ACPI_TYPE_INTEGER ; + wc_params[0 ].integer.value =1 ; + +strncat (wc_method, block->object_id,2 ); + +/* +* Some GUIDs break the specification by declaring themselves +* expensive, but have no corresponding WCxx method. So we +* should not fail if this happens. +*/ + wc_status =acpi_evaluate_object (handle, wc_method, + &wc_input,NULL ); + } + +strncat (method, block->object_id,2 ); + + status =acpi_evaluate_object (handle, method,NULL , out); + +/* +* If ACPI_WMI_EXPENSIVE, call the relevant WCxx method, even if +* the WQxx method failed - we should disable collection anyway +*/ +if ((block->flags &ACPI_WMI_EXPENSIVE ) && wc_status) { + wc_params[0 ].integer.value =0 ; + status =acpi_evaluate_object (handle, + wc_method, &wc_input,NULL ); + } + +return status; +} +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 +* @instance: Instance index +* &in: Buffer containing new values for the data block +* +* Write the contents of the input buffer to ACPI +*/ +acpi_status wmi_set_block (const char *guid_string,u8 instance, +const struct acpi_buffer *in) +{ +struct guid_block *block =NULL ; +struct wmi_block *wblock =NULL ; +acpi_handle handle; +struct acpi_object_list input; +union acpi_object params[2 ]; +char method[4 ] ="WS" ; + +if (!guid_string || !in) +return AE_BAD_DATA ; + +if (!find_guid (guid_string, &wblock)) +return AE_BAD_ADDRESS ; + + block = &wblock->gblock; + handle = wblock->handle; + +if (block->instance_count < instance) +return AE_BAD_PARAMETER ; + +/* Check GUID is a data block */ +if (block->flags & (ACPI_WMI_EVENT |ACPI_WMI_METHOD )) +return AE_BAD_ADDRESS ; + + input.count =2 ; + input.pointer = params; + params[0 ].type =ACPI_TYPE_INTEGER ; + params[0 ].integer.value = instance; + +if (block->flags &ACPI_WMI_STRING ) { + params[1 ].type =ACPI_TYPE_STRING ; + }else { + params[1 ].type =ACPI_TYPE_BUFFER ; + } + params[1 ].buffer.length = in->length; + params[1 ].buffer.pointer = in->pointer; + +strncat (method, block->object_id,2 ); + +return acpi_evaluate_object (handle, method, &input,NULL ); +} +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 WMI-ACPI mapper device. +*/ +acpi_status wmi_install_notify_handler (wmi_notify_handler handler,void *data) +{ +if (!handler) +return AE_BAD_PARAMETER ; + +if (!wmi_external_handler) +return AE_ALREADY_ACQUIRED ; + + wmi_external_handler = handler; + wmi_external_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 WMI-ACPI mapper device. +*/ +acpi_status wmi_remove_notify_handler (void ) +{ +if (wmi_external_handler) { + wmi_external_handler =NULL ; + wmi_external_data =NULL ; +return AE_OK ; + } +return AE_ERROR ; +} +EXPORT_SYMBOL_GPL (wmi_remove_notify_handler); + +/** +* wmi_get_event_data - Get WMI data associated with an event +* +* @event - Event to find +* &out - Buffer to hold event data +* +* Returns extra data associated with an event in WMI. +*/ +acpi_status wmi_get_event_data (u32 event,struct acpi_buffer *out) +{ +struct acpi_object_list input; +union acpi_object params[1 ]; +struct guid_block *gblock; +struct wmi_block *wblock; +struct list_head *p; + + input.count =1 ; + input.pointer = params; + params[0 ].type =ACPI_TYPE_INTEGER ; + params[0 ].integer.value = event; + +list_for_each (p, &wmi_blocks.list) { + wblock = list_entry(p, struct wmi_block, list); + gblock = &wblock->gblock; + +if ((gblock->flags &ACPI_WMI_EVENT ) && + (gblock->notify_id == event)) +return acpi_evaluate_object (wblock->handle,"_WED" , + &input, out); + } + +return AE_NOT_FOUND ; +} +EXPORT_SYMBOL (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_wdg - Parse the _WDG method for the GUID data blocks +*/ +static __init acpi_status parse_wdg (acpi_handle handle) +{ +struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER ,NULL }; +union acpi_object *obj; +struct guid_block *gblock; +struct wmi_block *wblock; +acpi_status status; +u32 i, total; + + status =acpi_evaluate_object (handle,"_WDG" ,NULL , &out); + +if (ACPI_FAILURE (status)) +return status; + + obj = (union acpi_object *) out.pointer; + +if (obj->type !=ACPI_TYPE_BUFFER ) +return AE_ERROR ; + + total = obj->buffer.length /sizeof (struct guid_block ); + + gblock =kzalloc (obj->buffer.length,GFP_KERNEL ); +if (!gblock) +return AE_NO_MEMORY ; + +memcpy (gblock, obj->buffer.pointer, obj->buffer.length); + +for (i =0 ; i < total; i++) { + wblock =kzalloc (sizeof (struct wmi_block ),GFP_KERNEL ); +if (!wblock) +return AE_NO_MEMORY ; + + wblock->gblock = gblock[i]; + wblock->handle = handle; +list_add_tail (&wblock->list, &wmi_blocks.list); + } + +kfree (out.pointer); +kfree (gblock); + +return status; +} + +/* +* WMI can have EmbeddedControl access regions. In which case, we just want to +* hand these off to the EC driver. +*/ +static acpi_status +acpi_wmi_ec_space_handler (u32 function,acpi_physical_address address, +u32 bits,acpi_integer *value, +void *handler_context,void *region_context)
drivers/acpi/wmi.c line 565
Quoted 10 lines
drivers/acpi/wmi.c new file +10
-0+{ +int result =0 , i =0 ; +u8 temp =0 ; + +if ((address >0xFF ) || !value) +return AE_BAD_PARAMETER ; + +if (function !=ACPI_READ && function !=ACPI_WRITE ) +return AE_BAD_PARAMETER ; +
You probably don't need to handle more than 1 byte here -- even in EC it is legacy...
Quoted 165 lines
drivers/acpi/wmi.c new file +128
-0+while (bits - i >0 ) { +if (function ==ACPI_READ ) { + result =ec_read (address, &temp); + (*value) |= ((acpi_integer )temp) << i; + }else { + temp =0xff & ((*value) >> i); + result =ec_write (address, temp); + } + i +=8 ; + ++address; + } + +switch (result) { +case -EINVAL : +return AE_BAD_PARAMETER ; +break ; +case -ENODEV : +return AE_NOT_FOUND ; +break ; +case -ETIME : +return AE_TIME ; +break ; +default : +return AE_OK ; + } +} + +static void acpi_wmi_notify (acpi_handle handle,u32 event,void *data) +{ +struct guid_block *block; +struct wmi_block *wblock; +struct list_head *p; +struct acpi_device *device = data; + +list_for_each (p, &wmi_blocks.list) { + wblock = list_entry(p, struct wmi_block, list); + block = &wblock->gblock; + +if ((block->flags &ACPI_WMI_EVENT ) && + block->notify_id == event) { +if (wmi_external_handler) +wmi_external_handler (event, wmi_external_data); + +acpi_bus_generate_netlink_event ( + device->pnp.device_class, device->dev.bus_id, + event,0 ); +break ; + } + } +} + +static int acpi_wmi_remove (struct acpi_device *device,int type) +{ +acpi_remove_notify_handler (device->handle,ACPI_DEVICE_NOTIFY , + acpi_wmi_notify); + +acpi_remove_address_space_handler (device->handle, +ACPI_ADR_SPACE_EC , &acpi_wmi_ec_space_handler); + +return 0 ; +} + +static int __init acpi_wmi_add (struct acpi_device *device) +{ +acpi_status status; +int result =0 ; + + status =acpi_install_notify_handler (device->handle,ACPI_DEVICE_NOTIFY , + acpi_wmi_notify, device); +if (ACPI_FAILURE (status)) { +ACPI_DEBUG_PRINT ((ACPI_DB_ERROR , +"Error installing notify handler\n" )); +return -ENODEV ; + } + + status =acpi_install_address_space_handler (device->handle, +ACPI_ADR_SPACE_EC , + &acpi_wmi_ec_space_handler, +NULL ,NULL ); +if (ACPI_FAILURE (status)) +return -ENODEV ; + + status =parse_wdg (device->handle); +if (ACPI_FAILURE (status)) +return -ENODEV ; + +return result; +} + +static int __init acpi_wmi_init (void ) +{ +acpi_status result; + +if (acpi_disabled) +return -ENODEV ; + +INIT_LIST_HEAD (&wmi_blocks.list); + + result =acpi_bus_register_driver (&acpi_wmi_driver); + +if (ACPI_FAILURE (result)) { + printk(KERN_INFO PREFIX "Error loading mapper\n"); + }else { + printk(KERN_INFO PREFIX "Mapper loaded\n"); + } + +return result; +} + +static void __exit acpi_wmi_exit(void) +{ +struct list_head *p, *tmp; +struct wmi_block *wblock; + +acpi_bus_unregister_driver (&acpi_wmi_driver); + +list_for_each_safe (p, tmp, &wmi_blocks.list) { + wblock = list_entry(p, struct wmi_block, list); + +list_del (p); +kfree (wblock); + } + + printk(KERN_INFO PREFIX "Mapper unloaded\n"); +} + +subsys_initcall (acpi_wmi_init); +module_exit (acpi_wmi_exit);include/linux/acpi.h +20
-1@@ -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 (wmi_notify_handler handler, +void *data); +extern acpi_status wmi_remove_notify_handler (void ); +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); -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
Re: [PATCH 1/5] ACPI: WMI: Add ACPI-WMI mapping driver
Quoted 13 lines · On Monday 17 December 2007 09:40:19 Alexey Starikovskiy wrote:
Quoted 13 lines
+/*
+ * WMI can have EmbeddedControl access regions. In which case, we just want to + * hand these off to the EC driver.
+ */drivers/acpi/wmi.c new file +9
-0+static acpi_status +acpi_wmi_ec_space_handler (u32 function,acpi_physical_address address, +u32 bits,acpi_integer *value, +void *handler_context,void *region_context) +{ +int result =0 , i =0 ; +u8 temp =0 ; + +if ((address >0xFF ) || !value)
Quoted 8 lines
drivers/acpi/wmi.c line 565
Quoted 5 lines
drivers/acpi/wmi.c new file +5
-0+return AE_BAD_PARAMETER ; + +if (function !=ACPI_READ && function !=ACPI_WRITE ) +return AE_BAD_PARAMETER ; +You probably don't need to handle more than 1 byte here -- even in EC it is legacy...
So would putting back in something like:
if (bits != 8)
return AE_BAD_PARAMETER;
Be the right solution? (acpi_strict test is removed compared to EC, since it's no longer exported, and WMI can be built as a module)
-Carlos
Signature · 3 lines
E-Mail: [email protected] Web: strangeworlds.co.uk GPG Key ID: 0x23EE722D
Re: [PATCH 1/5] ACPI: WMI: Add ACPI-WMI mapping driver
Quoted 25 lines · Carlos Corbacho
Quoted 13 lines · On Monday 17 December 2007 09:40:19 Alexey Starikovskiy wrote:
Quoted 13 lines
+/*
+ * WMI can have EmbeddedControl access regions. In which case, we just want to + * hand these off to the EC driver.
+ */drivers/acpi/wmi.c new file +9
-0+static acpi_status +acpi_wmi_ec_space_handler (u32 function,acpi_physical_address address, +u32 bits,acpi_integer *value, +void *handler_context,void *region_context) +{ +int result =0 , i =0 ; +u8 temp =0 ; + +if ((address >0xFF ) || !value)Quoted 9 lines
drivers/acpi/wmi.c line 565
Quoted 6 lines
drivers/acpi/wmi.c new file +5
-0+return AE_BAD_PARAMETER ; + +if (function !=ACPI_READ && function !=ACPI_WRITE ) +return AE_BAD_PARAMETER ; +You probably don't need to handle more than 1 byte here -- even in EC it is legacy...
Quoted 9 lines
So would putting back in something like:
if (bits != 8)
return AE_BAD_PARAMETER;Be the right solution? (
acpi_stricttest is removed compared to EC, since it's no longer exported, and WMI can be built as a module)-Carlos
Right
PATCH 2/5 acer-wmi: Add driver for newer Acer laptops
In reply to Carlos CorbachoThis 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
---
drivers/misc/Kconfig | 16 + drivers/misc/Makefile | 1 drivers/misc/acer-wmi.c | 1009 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1026 insertions(+), 0 deletions(-) create mode 100644 drivers/misc/acer-wmi.c
drivers/misc/Kconfig +16 -0
@@ -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
@@ -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 +1009 -0
1,009 lines not on this page · raw message
RFC PATCH 3/5 tc1100-wmi: Add driver for HP Compaq TC1100 Tablets
In reply to Carlos CorbachoThis 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).
---
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
@@ -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
@@ -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
RFC PATCH 4/5 ACPI: WMI: Add sysfs userspace interface
In reply to Carlos CorbachoCreate 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.
---
drivers/acpi/wmi.c | 463 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 461 insertions(+), 2 deletions(-)
drivers/acpi/wmi.c +461 -2
@@ -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>
@@ -64,6 +66,11 @@ struct wmi_block {struct list_head list;struct guid_block gblock;acpi_handle handle; +struct device dev; +u8 instance; +u32 method_id; +void *pointer; +acpi_size length; };static struct wmi_block wmi_blocks;
@@ -195,6 +202,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 ];
@@ -499,6 +533,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_wdg - Parse the _WDG method for the GUID data blocks */
@@ -654,6 +1103,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)
@@ -665,10 +1115,17 @@ static int __init acpi_wmi_init(void)if (ACPI_FAILURE (result)) { 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; }
@@ -677,6 +1134,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) {
PATCH 5/5 ACPI: WMI: Limit size of device string to 19 characters
In reply to Carlos CorbachoUntil Kay Sievers bus_id variable length code goes in, this is a temporary workaround.
---
drivers/acpi/wmi.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
drivers/acpi/wmi.c +1 -1
@@ -847,7 +847,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)