aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/platform/x86
diff options
context:
space:
mode:
authorMatthew Garrett <mjg@redhat.com>2009-11-04 14:17:53 -0500
committerLen Brown <len.brown@intel.com>2009-11-05 12:29:37 -0500
commit1caab3c1a90be3aa4ec3599409d8fe044b077478 (patch)
tree1c41c27e2828d31b7ca838f958e9c6af364c69a1 /drivers/platform/x86
parentb419148e567728f6af0c3b01965c1cc141e3e13a (diff)
wmi: Add support for module autoloading
WMI provides interface-specific GUIDs that are exported from modules as modalises, but the core currently generates no events to trigger module loading. This patch adds support for registering devices for each WMI GUID and generating the appropriate uevent. Based heavily on a patch by Carlos Corbacho (<carlos@strangeworlds.co.uk>). Signed-off-by: Matthew Garrett <mjg@redhat.com> Tested-by: Carlos Corbacho <carlos@strangeworlds.co.uk> Acked-by: Carlos Corbacho <carlos@strangeworlds.co.uk> Signed-off-by: Len Brown <len.brown@intel.com>
Diffstat (limited to 'drivers/platform/x86')
-rw-r--r--drivers/platform/x86/wmi.c175
1 files changed, 173 insertions, 2 deletions
diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c
index 177f8d767df4..e425a868cd3a 100644
--- a/drivers/platform/x86/wmi.c
+++ b/drivers/platform/x86/wmi.c
@@ -30,6 +30,7 @@
30#include <linux/kernel.h> 30#include <linux/kernel.h>
31#include <linux/init.h> 31#include <linux/init.h>
32#include <linux/types.h> 32#include <linux/types.h>
33#include <linux/device.h>
33#include <linux/list.h> 34#include <linux/list.h>
34#include <linux/acpi.h> 35#include <linux/acpi.h>
35#include <acpi/acpi_bus.h> 36#include <acpi/acpi_bus.h>
@@ -65,6 +66,7 @@ struct wmi_block {
65 acpi_handle handle; 66 acpi_handle handle;
66 wmi_notify_handler handler; 67 wmi_notify_handler handler;
67 void *handler_data; 68 void *handler_data;
69 struct device *dev;
68}; 70};
69 71
70static struct wmi_block wmi_blocks; 72static struct wmi_block wmi_blocks;
@@ -195,6 +197,34 @@ static bool wmi_parse_guid(const u8 *src, u8 *dest)
195 return true; 197 return true;
196} 198}
197 199
200/*
201 * Convert a raw GUID to the ACII string representation
202 */
203static int wmi_gtoa(const char *in, char *out)
204{
205 int i;
206
207 for (i = 3; i >= 0; i--)
208 out += sprintf(out, "%02X", in[i] & 0xFF);
209
210 out += sprintf(out, "-");
211 out += sprintf(out, "%02X", in[5] & 0xFF);
212 out += sprintf(out, "%02X", in[4] & 0xFF);
213 out += sprintf(out, "-");
214 out += sprintf(out, "%02X", in[7] & 0xFF);
215 out += sprintf(out, "%02X", in[6] & 0xFF);
216 out += sprintf(out, "-");
217 out += sprintf(out, "%02X", in[8] & 0xFF);
218 out += sprintf(out, "%02X", in[9] & 0xFF);
219 out += sprintf(out, "-");
220
221 for (i = 10; i <= 15; i++)
222 out += sprintf(out, "%02X", in[i] & 0xFF);
223
224 out = '\0';
225 return 0;
226}
227
198static bool find_guid(const char *guid_string, struct wmi_block **out) 228static bool find_guid(const char *guid_string, struct wmi_block **out)
199{ 229{
200 char tmp[16], guid_input[16]; 230 char tmp[16], guid_input[16];
@@ -555,6 +585,138 @@ bool wmi_has_guid(const char *guid_string)
555EXPORT_SYMBOL_GPL(wmi_has_guid); 585EXPORT_SYMBOL_GPL(wmi_has_guid);
556 586
557/* 587/*
588 * sysfs interface
589 */
590static ssize_t show_modalias(struct device *dev, struct device_attribute *attr,
591 char *buf)
592{
593 char guid_string[37];
594 struct wmi_block *wblock;
595
596 wblock = dev_get_drvdata(dev);
597 if (!wblock)
598 return -ENOMEM;
599
600 wmi_gtoa(wblock->gblock.guid, guid_string);
601
602 return sprintf(buf, "wmi:%s\n", guid_string);
603}
604static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL);
605
606static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env)
607{
608 char guid_string[37];
609
610 struct wmi_block *wblock;
611
612 if (add_uevent_var(env, "MODALIAS="))
613 return -ENOMEM;
614
615 wblock = dev_get_drvdata(dev);
616 if (!wblock)
617 return -ENOMEM;
618
619 wmi_gtoa(wblock->gblock.guid, guid_string);
620
621 strcpy(&env->buf[env->buflen - 1], "wmi:");
622 memcpy(&env->buf[env->buflen - 1 + 4], guid_string, 36);
623 env->buflen += 40;
624
625 return 0;
626}
627
628static void wmi_dev_free(struct device *dev)
629{
630 kfree(dev);
631}
632
633static struct class wmi_class = {
634 .name = "wmi",
635 .dev_release = wmi_dev_free,
636 .dev_uevent = wmi_dev_uevent,
637};
638
639static int wmi_create_devs(void)
640{
641 int result;
642 char guid_string[37];
643 struct guid_block *gblock;
644 struct wmi_block *wblock;
645 struct list_head *p;
646 struct device *guid_dev;
647
648 /* Create devices for all the GUIDs */
649 list_for_each(p, &wmi_blocks.list) {
650 wblock = list_entry(p, struct wmi_block, list);
651
652 guid_dev = kzalloc(sizeof(struct device), GFP_KERNEL);
653 if (!guid_dev)
654 return -ENOMEM;
655
656 wblock->dev = guid_dev;
657
658 guid_dev->class = &wmi_class;
659 dev_set_drvdata(guid_dev, wblock);
660
661 gblock = &wblock->gblock;
662
663 wmi_gtoa(gblock->guid, guid_string);
664 dev_set_name(guid_dev, guid_string);
665
666 result = device_register(guid_dev);
667 if (result)
668 return result;
669
670 result = device_create_file(guid_dev, &dev_attr_modalias);
671 if (result)
672 return result;
673 }
674
675 return 0;
676}
677
678static void wmi_remove_devs(void)
679{
680 struct guid_block *gblock;
681 struct wmi_block *wblock;
682 struct list_head *p;
683 struct device *guid_dev;
684
685 /* Delete devices for all the GUIDs */
686 list_for_each(p, &wmi_blocks.list) {
687 wblock = list_entry(p, struct wmi_block, list);
688
689 guid_dev = wblock->dev;
690 gblock = &wblock->gblock;
691
692 device_remove_file(guid_dev, &dev_attr_modalias);
693
694 device_unregister(guid_dev);
695 }
696}
697
698static void wmi_class_exit(void)
699{
700 wmi_remove_devs();
701 class_unregister(&wmi_class);
702}
703
704static int wmi_class_init(void)
705{
706 int ret;
707
708 ret = class_register(&wmi_class);
709 if (ret)
710 return ret;
711
712 ret = wmi_create_devs();
713 if (ret)
714 wmi_class_exit();
715
716 return ret;
717}
718
719/*
558 * Parse the _WDG method for the GUID data blocks 720 * Parse the _WDG method for the GUID data blocks
559 */ 721 */
560static __init acpi_status parse_wdg(acpi_handle handle) 722static __init acpi_status parse_wdg(acpi_handle handle)
@@ -709,10 +871,17 @@ static int __init acpi_wmi_init(void)
709 871
710 if (result < 0) { 872 if (result < 0) {
711 printk(KERN_INFO PREFIX "Error loading mapper\n"); 873 printk(KERN_INFO PREFIX "Error loading mapper\n");
712 } else { 874 return -ENODEV;
713 printk(KERN_INFO PREFIX "Mapper loaded\n"); 875 }
876
877 result = wmi_class_init();
878 if (result) {
879 acpi_bus_unregister_driver(&acpi_wmi_driver);
880 return result;
714 } 881 }
715 882
883 printk(KERN_INFO PREFIX "Mapper loaded\n");
884
716 return result; 885 return result;
717} 886}
718 887
@@ -721,6 +890,8 @@ static void __exit acpi_wmi_exit(void)
721 struct list_head *p, *tmp; 890 struct list_head *p, *tmp;
722 struct wmi_block *wblock; 891 struct wmi_block *wblock;
723 892
893 wmi_class_exit();
894
724 acpi_bus_unregister_driver(&acpi_wmi_driver); 895 acpi_bus_unregister_driver(&acpi_wmi_driver);
725 896
726 list_for_each_safe(p, tmp, &wmi_blocks.list) { 897 list_for_each_safe(p, tmp, &wmi_blocks.list) {