aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/platform/x86/wmi.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/platform/x86/wmi.c')
-rw-r--r--drivers/platform/x86/wmi.c220
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
70static struct wmi_block wmi_blocks; 73static 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 */
204static 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
198static bool find_guid(const char *guid_string, struct wmi_block **out) 229static 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)
555EXPORT_SYMBOL_GPL(wmi_has_guid); 584EXPORT_SYMBOL_GPL(wmi_has_guid);
556 585
557/* 586/*
587 * sysfs interface
588 */
589static 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}
603static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL);
604
605static 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
627static void wmi_dev_free(struct device *dev)
628{
629 kfree(dev);
630}
631
632static struct class wmi_class = {
633 .name = "wmi",
634 .dev_release = wmi_dev_free,
635 .dev_uevent = wmi_dev_uevent,
636};
637
638static 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
677static 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
697static void wmi_class_exit(void)
698{
699 wmi_remove_devs();
700 class_unregister(&wmi_class);
701}
702
703static 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
718static 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 */
560static __init acpi_status parse_wdg(acpi_handle handle) 737static __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 */
607static acpi_status 798static acpi_status
608acpi_wmi_ec_space_handler(u32 function, acpi_physical_address address, 799acpi_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) {