diff options
| -rw-r--r-- | drivers/platform/x86/wmi.c | 175 |
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 | ||
| 70 | static struct wmi_block wmi_blocks; | 72 | static 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 | */ | ||
| 203 | static 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 | |||
| 198 | static bool find_guid(const char *guid_string, struct wmi_block **out) | 228 | static 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) | |||
| 555 | EXPORT_SYMBOL_GPL(wmi_has_guid); | 585 | EXPORT_SYMBOL_GPL(wmi_has_guid); |
| 556 | 586 | ||
| 557 | /* | 587 | /* |
| 588 | * sysfs interface | ||
| 589 | */ | ||
| 590 | static 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 | } | ||
| 604 | static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL); | ||
| 605 | |||
| 606 | static 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 | |||
| 628 | static void wmi_dev_free(struct device *dev) | ||
| 629 | { | ||
| 630 | kfree(dev); | ||
| 631 | } | ||
| 632 | |||
| 633 | static struct class wmi_class = { | ||
| 634 | .name = "wmi", | ||
| 635 | .dev_release = wmi_dev_free, | ||
| 636 | .dev_uevent = wmi_dev_uevent, | ||
| 637 | }; | ||
| 638 | |||
| 639 | static 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 | |||
| 678 | static 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 | |||
| 698 | static void wmi_class_exit(void) | ||
| 699 | { | ||
| 700 | wmi_remove_devs(); | ||
| 701 | class_unregister(&wmi_class); | ||
| 702 | } | ||
| 703 | |||
| 704 | static 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 | */ |
| 560 | static __init acpi_status parse_wdg(acpi_handle handle) | 722 | static __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) { |
