diff options
Diffstat (limited to 'drivers/platform/x86/wmi.c')
-rw-r--r-- | drivers/platform/x86/wmi.c | 220 |
1 files changed, 210 insertions, 10 deletions
diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 177f8d767df4..39ec5b6c2e3a 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c | |||
@@ -30,8 +30,10 @@ | |||
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> |
36 | #include <linux/slab.h> | ||
35 | #include <acpi/acpi_bus.h> | 37 | #include <acpi/acpi_bus.h> |
36 | #include <acpi/acpi_drivers.h> | 38 | #include <acpi/acpi_drivers.h> |
37 | 39 | ||
@@ -65,6 +67,7 @@ struct wmi_block { | |||
65 | acpi_handle handle; | 67 | acpi_handle handle; |
66 | wmi_notify_handler handler; | 68 | wmi_notify_handler handler; |
67 | void *handler_data; | 69 | void *handler_data; |
70 | struct device *dev; | ||
68 | }; | 71 | }; |
69 | 72 | ||
70 | static struct wmi_block wmi_blocks; | 73 | static struct wmi_block wmi_blocks; |
@@ -195,6 +198,34 @@ static bool wmi_parse_guid(const u8 *src, u8 *dest) | |||
195 | return true; | 198 | return true; |
196 | } | 199 | } |
197 | 200 | ||
201 | /* | ||
202 | * Convert a raw GUID to the ACII string representation | ||
203 | */ | ||
204 | static int wmi_gtoa(const char *in, char *out) | ||
205 | { | ||
206 | int i; | ||
207 | |||
208 | for (i = 3; i >= 0; i--) | ||
209 | out += sprintf(out, "%02X", in[i] & 0xFF); | ||
210 | |||
211 | out += sprintf(out, "-"); | ||
212 | out += sprintf(out, "%02X", in[5] & 0xFF); | ||
213 | out += sprintf(out, "%02X", in[4] & 0xFF); | ||
214 | out += sprintf(out, "-"); | ||
215 | out += sprintf(out, "%02X", in[7] & 0xFF); | ||
216 | out += sprintf(out, "%02X", in[6] & 0xFF); | ||
217 | out += sprintf(out, "-"); | ||
218 | out += sprintf(out, "%02X", in[8] & 0xFF); | ||
219 | out += sprintf(out, "%02X", in[9] & 0xFF); | ||
220 | out += sprintf(out, "-"); | ||
221 | |||
222 | for (i = 10; i <= 15; i++) | ||
223 | out += sprintf(out, "%02X", in[i] & 0xFF); | ||
224 | |||
225 | out = '\0'; | ||
226 | return 0; | ||
227 | } | ||
228 | |||
198 | static bool find_guid(const char *guid_string, struct wmi_block **out) | 229 | static bool find_guid(const char *guid_string, struct wmi_block **out) |
199 | { | 230 | { |
200 | char tmp[16], guid_input[16]; | 231 | char tmp[16], guid_input[16]; |
@@ -462,8 +493,7 @@ wmi_notify_handler handler, void *data) | |||
462 | if (!guid || !handler) | 493 | if (!guid || !handler) |
463 | return AE_BAD_PARAMETER; | 494 | return AE_BAD_PARAMETER; |
464 | 495 | ||
465 | find_guid(guid, &block); | 496 | if (!find_guid(guid, &block)) |
466 | if (!block) | ||
467 | return AE_NOT_EXIST; | 497 | return AE_NOT_EXIST; |
468 | 498 | ||
469 | if (block->handler) | 499 | if (block->handler) |
@@ -491,8 +521,7 @@ acpi_status wmi_remove_notify_handler(const char *guid) | |||
491 | if (!guid) | 521 | if (!guid) |
492 | return AE_BAD_PARAMETER; | 522 | return AE_BAD_PARAMETER; |
493 | 523 | ||
494 | find_guid(guid, &block); | 524 | if (!find_guid(guid, &block)) |
495 | if (!block) | ||
496 | return AE_NOT_EXIST; | 525 | return AE_NOT_EXIST; |
497 | 526 | ||
498 | if (!block->handler) | 527 | if (!block->handler) |
@@ -510,8 +539,8 @@ EXPORT_SYMBOL_GPL(wmi_remove_notify_handler); | |||
510 | /** | 539 | /** |
511 | * wmi_get_event_data - Get WMI data associated with an event | 540 | * wmi_get_event_data - Get WMI data associated with an event |
512 | * | 541 | * |
513 | * @event - Event to find | 542 | * @event: Event to find |
514 | * &out - Buffer to hold event data | 543 | * @out: Buffer to hold event data. out->pointer should be freed with kfree() |
515 | * | 544 | * |
516 | * Returns extra data associated with an event in WMI. | 545 | * Returns extra data associated with an event in WMI. |
517 | */ | 546 | */ |
@@ -555,6 +584,154 @@ bool wmi_has_guid(const char *guid_string) | |||
555 | EXPORT_SYMBOL_GPL(wmi_has_guid); | 584 | EXPORT_SYMBOL_GPL(wmi_has_guid); |
556 | 585 | ||
557 | /* | 586 | /* |
587 | * sysfs interface | ||
588 | */ | ||
589 | static ssize_t show_modalias(struct device *dev, struct device_attribute *attr, | ||
590 | char *buf) | ||
591 | { | ||
592 | char guid_string[37]; | ||
593 | struct wmi_block *wblock; | ||
594 | |||
595 | wblock = dev_get_drvdata(dev); | ||
596 | if (!wblock) | ||
597 | return -ENOMEM; | ||
598 | |||
599 | wmi_gtoa(wblock->gblock.guid, guid_string); | ||
600 | |||
601 | return sprintf(buf, "wmi:%s\n", guid_string); | ||
602 | } | ||
603 | static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL); | ||
604 | |||
605 | static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env) | ||
606 | { | ||
607 | char guid_string[37]; | ||
608 | |||
609 | struct wmi_block *wblock; | ||
610 | |||
611 | if (add_uevent_var(env, "MODALIAS=")) | ||
612 | return -ENOMEM; | ||
613 | |||
614 | wblock = dev_get_drvdata(dev); | ||
615 | if (!wblock) | ||
616 | return -ENOMEM; | ||
617 | |||
618 | wmi_gtoa(wblock->gblock.guid, guid_string); | ||
619 | |||
620 | strcpy(&env->buf[env->buflen - 1], "wmi:"); | ||
621 | memcpy(&env->buf[env->buflen - 1 + 4], guid_string, 36); | ||
622 | env->buflen += 40; | ||
623 | |||
624 | return 0; | ||
625 | } | ||
626 | |||
627 | static void wmi_dev_free(struct device *dev) | ||
628 | { | ||
629 | kfree(dev); | ||
630 | } | ||
631 | |||
632 | static struct class wmi_class = { | ||
633 | .name = "wmi", | ||
634 | .dev_release = wmi_dev_free, | ||
635 | .dev_uevent = wmi_dev_uevent, | ||
636 | }; | ||
637 | |||
638 | static int wmi_create_devs(void) | ||
639 | { | ||
640 | int result; | ||
641 | char guid_string[37]; | ||
642 | struct guid_block *gblock; | ||
643 | struct wmi_block *wblock; | ||
644 | struct list_head *p; | ||
645 | struct device *guid_dev; | ||
646 | |||
647 | /* Create devices for all the GUIDs */ | ||
648 | list_for_each(p, &wmi_blocks.list) { | ||
649 | wblock = list_entry(p, struct wmi_block, list); | ||
650 | |||
651 | guid_dev = kzalloc(sizeof(struct device), GFP_KERNEL); | ||
652 | if (!guid_dev) | ||
653 | return -ENOMEM; | ||
654 | |||
655 | wblock->dev = guid_dev; | ||
656 | |||
657 | guid_dev->class = &wmi_class; | ||
658 | dev_set_drvdata(guid_dev, wblock); | ||
659 | |||
660 | gblock = &wblock->gblock; | ||
661 | |||
662 | wmi_gtoa(gblock->guid, guid_string); | ||
663 | dev_set_name(guid_dev, guid_string); | ||
664 | |||
665 | result = device_register(guid_dev); | ||
666 | if (result) | ||
667 | return result; | ||
668 | |||
669 | result = device_create_file(guid_dev, &dev_attr_modalias); | ||
670 | if (result) | ||
671 | return result; | ||
672 | } | ||
673 | |||
674 | return 0; | ||
675 | } | ||
676 | |||
677 | static void wmi_remove_devs(void) | ||
678 | { | ||
679 | struct guid_block *gblock; | ||
680 | struct wmi_block *wblock; | ||
681 | struct list_head *p; | ||
682 | struct device *guid_dev; | ||
683 | |||
684 | /* Delete devices for all the GUIDs */ | ||
685 | list_for_each(p, &wmi_blocks.list) { | ||
686 | wblock = list_entry(p, struct wmi_block, list); | ||
687 | |||
688 | guid_dev = wblock->dev; | ||
689 | gblock = &wblock->gblock; | ||
690 | |||
691 | device_remove_file(guid_dev, &dev_attr_modalias); | ||
692 | |||
693 | device_unregister(guid_dev); | ||
694 | } | ||
695 | } | ||
696 | |||
697 | static void wmi_class_exit(void) | ||
698 | { | ||
699 | wmi_remove_devs(); | ||
700 | class_unregister(&wmi_class); | ||
701 | } | ||
702 | |||
703 | static int wmi_class_init(void) | ||
704 | { | ||
705 | int ret; | ||
706 | |||
707 | ret = class_register(&wmi_class); | ||
708 | if (ret) | ||
709 | return ret; | ||
710 | |||
711 | ret = wmi_create_devs(); | ||
712 | if (ret) | ||
713 | wmi_class_exit(); | ||
714 | |||
715 | return ret; | ||
716 | } | ||
717 | |||
718 | static bool guid_already_parsed(const char *guid_string) | ||
719 | { | ||
720 | struct guid_block *gblock; | ||
721 | struct wmi_block *wblock; | ||
722 | struct list_head *p; | ||
723 | |||
724 | list_for_each(p, &wmi_blocks.list) { | ||
725 | wblock = list_entry(p, struct wmi_block, list); | ||
726 | gblock = &wblock->gblock; | ||
727 | |||
728 | if (strncmp(gblock->guid, guid_string, 16) == 0) | ||
729 | return true; | ||
730 | } | ||
731 | return false; | ||
732 | } | ||
733 | |||
734 | /* | ||
558 | * Parse the _WDG method for the GUID data blocks | 735 | * Parse the _WDG method for the GUID data blocks |
559 | */ | 736 | */ |
560 | static __init acpi_status parse_wdg(acpi_handle handle) | 737 | static __init acpi_status parse_wdg(acpi_handle handle) |
@@ -563,6 +740,7 @@ static __init acpi_status parse_wdg(acpi_handle handle) | |||
563 | union acpi_object *obj; | 740 | union acpi_object *obj; |
564 | struct guid_block *gblock; | 741 | struct guid_block *gblock; |
565 | struct wmi_block *wblock; | 742 | struct wmi_block *wblock; |
743 | char guid_string[37]; | ||
566 | acpi_status status; | 744 | acpi_status status; |
567 | u32 i, total; | 745 | u32 i, total; |
568 | 746 | ||
@@ -585,6 +763,19 @@ static __init acpi_status parse_wdg(acpi_handle handle) | |||
585 | memcpy(gblock, obj->buffer.pointer, obj->buffer.length); | 763 | memcpy(gblock, obj->buffer.pointer, obj->buffer.length); |
586 | 764 | ||
587 | for (i = 0; i < total; i++) { | 765 | for (i = 0; i < total; i++) { |
766 | /* | ||
767 | Some WMI devices, like those for nVidia hooks, have a | ||
768 | duplicate GUID. It's not clear what we should do in this | ||
769 | case yet, so for now, we'll just ignore the duplicate. | ||
770 | Anyone who wants to add support for that device can come | ||
771 | up with a better workaround for the mess then. | ||
772 | */ | ||
773 | if (guid_already_parsed(gblock[i].guid) == true) { | ||
774 | wmi_gtoa(gblock[i].guid, guid_string); | ||
775 | printk(KERN_INFO PREFIX "Skipping duplicate GUID %s\n", | ||
776 | guid_string); | ||
777 | continue; | ||
778 | } | ||
588 | wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL); | 779 | wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL); |
589 | if (!wblock) | 780 | if (!wblock) |
590 | return AE_NO_MEMORY; | 781 | return AE_NO_MEMORY; |
@@ -606,7 +797,7 @@ static __init acpi_status parse_wdg(acpi_handle handle) | |||
606 | */ | 797 | */ |
607 | static acpi_status | 798 | static acpi_status |
608 | acpi_wmi_ec_space_handler(u32 function, acpi_physical_address address, | 799 | acpi_wmi_ec_space_handler(u32 function, acpi_physical_address address, |
609 | u32 bits, acpi_integer * value, | 800 | u32 bits, u64 *value, |
610 | void *handler_context, void *region_context) | 801 | void *handler_context, void *region_context) |
611 | { | 802 | { |
612 | int result = 0, i = 0; | 803 | int result = 0, i = 0; |
@@ -623,7 +814,7 @@ acpi_wmi_ec_space_handler(u32 function, acpi_physical_address address, | |||
623 | 814 | ||
624 | if (function == ACPI_READ) { | 815 | if (function == ACPI_READ) { |
625 | result = ec_read(address, &temp); | 816 | result = ec_read(address, &temp); |
626 | (*value) |= ((acpi_integer)temp) << i; | 817 | (*value) |= ((u64)temp) << i; |
627 | } else { | 818 | } else { |
628 | temp = 0xff & ((*value) >> i); | 819 | temp = 0xff & ((*value) >> i); |
629 | result = ec_write(address, temp); | 820 | result = ec_write(address, temp); |
@@ -709,10 +900,17 @@ static int __init acpi_wmi_init(void) | |||
709 | 900 | ||
710 | if (result < 0) { | 901 | if (result < 0) { |
711 | printk(KERN_INFO PREFIX "Error loading mapper\n"); | 902 | printk(KERN_INFO PREFIX "Error loading mapper\n"); |
712 | } else { | 903 | return -ENODEV; |
713 | printk(KERN_INFO PREFIX "Mapper loaded\n"); | 904 | } |
905 | |||
906 | result = wmi_class_init(); | ||
907 | if (result) { | ||
908 | acpi_bus_unregister_driver(&acpi_wmi_driver); | ||
909 | return result; | ||
714 | } | 910 | } |
715 | 911 | ||
912 | printk(KERN_INFO PREFIX "Mapper loaded\n"); | ||
913 | |||
716 | return result; | 914 | return result; |
717 | } | 915 | } |
718 | 916 | ||
@@ -721,6 +919,8 @@ static void __exit acpi_wmi_exit(void) | |||
721 | struct list_head *p, *tmp; | 919 | struct list_head *p, *tmp; |
722 | struct wmi_block *wblock; | 920 | struct wmi_block *wblock; |
723 | 921 | ||
922 | wmi_class_exit(); | ||
923 | |||
724 | acpi_bus_unregister_driver(&acpi_wmi_driver); | 924 | acpi_bus_unregister_driver(&acpi_wmi_driver); |
725 | 925 | ||
726 | list_for_each_safe(p, tmp, &wmi_blocks.list) { | 926 | list_for_each_safe(p, tmp, &wmi_blocks.list) { |