WMI
Subsystems (2)
cover WMI
ANYWR - A New Year WMI release
Len,
Can you please review at least the first two patches in this series, as I would really like to try and get them into 2.6.25.
-Carlos
===
Patch #1: (WMI - driver and in kernel interface) - No change
Len: For you to review, and to go upstream.
====
Patch #2: (acer-wmi)
Added more device autodetection. Also, fixed setting bluetooth and wireless on older AMW0 V2 laptops, and improved the error handling code on driver registration.
(I am currently working on rfkill support, but have been running into some strange problems with rfkill-input, so I'll release that as a future patch on top of this when it's done. Until then, though, this patch still works as-is).
Len: For you to review, and to go upstream.
====
Patch #3: (tc1100-wmi) - No Change
RFC only, needs actual testing on the hardware, and probably very broken. Waiting on Matthew Garrett to get some free time to test this.
====
Patch #4: (WMI sysfs interface) - No Change
RFC only (see patch #5 for reason). Adds interface under /sys/devices/virtual/wmi
Matthew: Do you want me to fold patch #5 into this?
====
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]).
[1] http://lkml.org/lkml/2007/12/4/30
---
Carlos Corbacho (5):
[WIP] ACPI: WMI: Limit size of device string to 19 characters [RFC] ACPI: WMI: Add sysfs userspace interface [RFC] tc1100-wmi: Add driver for HP Compaq TC1100 Tablets
acer-wmi: Add driver for newer Acer laptops
ACPI: WMI: Add ACPI-WMI mapping driver
MAINTAINERS | 7 drivers/acpi/Kconfig | 11 drivers/acpi/Makefile | 1 drivers/acpi/wmi.c | 1171 +++++++++++++++++++++++++++++++++++++++++++++ drivers/misc/Kconfig | 25 + drivers/misc/Makefile | 2 drivers/misc/acer-wmi.c | 1100 ++++++++++++++++++++++++++++++++++++++++++ drivers/misc/tc1100-wmi.c | 290 +++++++++++ include/linux/acpi.h | 20 + 9 files changed, 2627 insertions(+), 0 deletions(-) create mode 100644 drivers/acpi/wmi.c create mode 100644 drivers/misc/acer-wmi.c create mode 100644 drivers/misc/tc1100-wmi.c
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 for each GUID with WMI. When a notification is sent to a GUID in WMI, the handler registered with WMI is then called (it is left to the caller to ask for the WMI event data associated with the GUID, 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.
v12 (2007-12-18)
* Changed EC region handling code, as per Alexey's comments.
v13 (2007-12-27)
* Changed event handling so that we can have one event handler registered per GUID, as per Matthew Garrett's suggestion.
---
MAINTAINERS | 7 drivers/acpi/Kconfig | 11 + drivers/acpi/Makefile | 1 drivers/acpi/wmi.c | 712 +++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/acpi.h | 20 + 5 files changed, 751 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
@@ -199,6 +199,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 +712 -0
712 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 (const char *guid, +wmi_notify_handler handler,void *data); +extern acpi_status wmi_remove_notify_handler (const char *guid); +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 9 lines · On Saturday 12 January 2008 09:17:55 Len Brown wrote:
CC [M]
drivers/acpi/wmi.odrivers/acpi/wmi.c: In function ‘acpi_wmi_add’:drivers/acpi/wmi.c:1101: error: ‘_COMPONENT’ undeclared (first use in this function)drivers/acpi/wmi.c:1101: error: (Each undeclared identifier is reported only oncedrivers/acpi/wmi.c:1101: error: for each function it appears in.)
make[2]: *** [drivers/acpi/wmi.o] Error 1
make[1]: *** [drivers/acpi] Error 2
make: *** [drivers] Error 2
Ok - apparently WMI doesn't play nice if CONFIG_ACPI_DEBUG is enabled.
Fixed, new version is here:
---
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 for each GUID with WMI. When a notification is sent to a GUID in WMI, the handler registered with WMI is then called (it is left to the caller to ask for the WMI event data associated with the GUID, 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.
v12 (2007-12-18)
* Changed EC region handling code, as per Alexey's comments.
v13 (2007-12-27)
* Changed event handling so that we can have one event handler registered per GUID, as per Matthew Garrett's suggestion.
v14 (2008-01-12)
* Remove ACPI debug statements
---
MAINTAINERS | 7 drivers/acpi/Kconfig | 11 + drivers/acpi/Makefile | 1 drivers/acpi/wmi.c | 710 +++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/acpi.h | 20 + 5 files changed, 749 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
@@ -202,6 +202,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 +710 -0
710 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 (const char *guid, +wmi_notify_handler handler,void *data); +extern acpi_status wmi_remove_notify_handler (const char *guid); +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
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
v4:
* Simplified internal handling of WMID and AMW0 devices
* Add autodetection for bluetooth and maximum brightness on AMW0 V2 and WMID laptops.
v5:
* Add EC quirk for Medion MD 98000
* Add autodetection for AMW0, and mail LED on AMW0 and AMW0 V2.
* Improve error handling
* Fix AMW0 V2 bluetooth and wireless, by using both WMID and AMW0 methods to ensure that the correct value is always set.
---
drivers/misc/Kconfig | 16 + drivers/misc/Makefile | 1 drivers/misc/acer-wmi.c | 1100 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1117 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 +1100 -0
1,100 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>
@@ -66,6 +68,11 @@ struct wmi_block {acpi_handle handle;wmi_notify_handler handler;void *handler_data; +struct device dev; +u8 instance; +u32 method_id; +void *pointer; +acpi_size length; };static struct wmi_block wmi_blocks;
@@ -194,6 +201,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 ];
@@ -517,6 +551,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 the _WDG method for the GUID data blocks */ static __init acpi_status parse_wdg (acpi_handle handle)
@@ -673,6 +1122,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)
@@ -684,10 +1134,17 @@ static int __init acpi_wmi_init(void)if (result <0 ) { 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; }
@@ -696,6 +1153,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
@@ -864,7 +864,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)