diff options
Diffstat (limited to 'drivers/misc/thinkpad_acpi.c')
-rw-r--r-- | drivers/misc/thinkpad_acpi.c | 602 |
1 files changed, 526 insertions, 76 deletions
diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c index 95c0b96e83f2..f15a58f7403f 100644 --- a/drivers/misc/thinkpad_acpi.c +++ b/drivers/misc/thinkpad_acpi.c | |||
@@ -21,8 +21,8 @@ | |||
21 | * 02110-1301, USA. | 21 | * 02110-1301, USA. |
22 | */ | 22 | */ |
23 | 23 | ||
24 | #define IBM_VERSION "0.14" | 24 | #define IBM_VERSION "0.15" |
25 | #define TPACPI_SYSFS_VERSION 0x000100 | 25 | #define TPACPI_SYSFS_VERSION 0x010000 |
26 | 26 | ||
27 | /* | 27 | /* |
28 | * Changelog: | 28 | * Changelog: |
@@ -92,6 +92,29 @@ MODULE_LICENSE("GPL"); | |||
92 | /* Please remove this in year 2009 */ | 92 | /* Please remove this in year 2009 */ |
93 | MODULE_ALIAS("ibm_acpi"); | 93 | MODULE_ALIAS("ibm_acpi"); |
94 | 94 | ||
95 | /* | ||
96 | * DMI matching for module autoloading | ||
97 | * | ||
98 | * See http://thinkwiki.org/wiki/List_of_DMI_IDs | ||
99 | * See http://thinkwiki.org/wiki/BIOS_Upgrade_Downloads | ||
100 | * | ||
101 | * Only models listed in thinkwiki will be supported, so add yours | ||
102 | * if it is not there yet. | ||
103 | */ | ||
104 | #define IBM_BIOS_MODULE_ALIAS(__type) \ | ||
105 | MODULE_ALIAS("dmi:bvnIBM:bvr" __type "ET??WW") | ||
106 | |||
107 | /* Non-ancient thinkpads */ | ||
108 | MODULE_ALIAS("dmi:bvnIBM:*:svnIBM:*:pvrThinkPad*:rvnIBM:*"); | ||
109 | MODULE_ALIAS("dmi:bvnLENOVO:*:svnLENOVO:*:pvrThinkPad*:rvnLENOVO:*"); | ||
110 | |||
111 | /* Ancient thinkpad BIOSes have to be identified by | ||
112 | * BIOS type or model number, and there are far less | ||
113 | * BIOS types than model numbers... */ | ||
114 | IBM_BIOS_MODULE_ALIAS("I[B,D,H,I,M,N,O,T,W,V,Y,Z]"); | ||
115 | IBM_BIOS_MODULE_ALIAS("1[0,3,6,8,A-G,I,K,M-P,S,T]"); | ||
116 | IBM_BIOS_MODULE_ALIAS("K[U,X-Z]"); | ||
117 | |||
95 | #define __unused __attribute__ ((unused)) | 118 | #define __unused __attribute__ ((unused)) |
96 | 119 | ||
97 | /**************************************************************************** | 120 | /**************************************************************************** |
@@ -106,7 +129,7 @@ MODULE_ALIAS("ibm_acpi"); | |||
106 | * ACPI basic handles | 129 | * ACPI basic handles |
107 | */ | 130 | */ |
108 | 131 | ||
109 | static acpi_handle root_handle = NULL; | 132 | static acpi_handle root_handle; |
110 | 133 | ||
111 | #define IBM_HANDLE(object, parent, paths...) \ | 134 | #define IBM_HANDLE(object, parent, paths...) \ |
112 | static acpi_handle object##_handle; \ | 135 | static acpi_handle object##_handle; \ |
@@ -487,19 +510,36 @@ static char *next_cmd(char **cmds) | |||
487 | /**************************************************************************** | 510 | /**************************************************************************** |
488 | **************************************************************************** | 511 | **************************************************************************** |
489 | * | 512 | * |
490 | * Device model: hwmon and platform | 513 | * Device model: input, hwmon and platform |
491 | * | 514 | * |
492 | **************************************************************************** | 515 | **************************************************************************** |
493 | ****************************************************************************/ | 516 | ****************************************************************************/ |
494 | 517 | ||
495 | static struct platform_device *tpacpi_pdev = NULL; | 518 | static struct platform_device *tpacpi_pdev; |
496 | static struct class_device *tpacpi_hwmon = NULL; | 519 | static struct class_device *tpacpi_hwmon; |
520 | static struct input_dev *tpacpi_inputdev; | ||
521 | |||
522 | |||
523 | static int tpacpi_resume_handler(struct platform_device *pdev) | ||
524 | { | ||
525 | struct ibm_struct *ibm, *itmp; | ||
526 | |||
527 | list_for_each_entry_safe(ibm, itmp, | ||
528 | &tpacpi_all_drivers, | ||
529 | all_drivers) { | ||
530 | if (ibm->resume) | ||
531 | (ibm->resume)(); | ||
532 | } | ||
533 | |||
534 | return 0; | ||
535 | } | ||
497 | 536 | ||
498 | static struct platform_driver tpacpi_pdriver = { | 537 | static struct platform_driver tpacpi_pdriver = { |
499 | .driver = { | 538 | .driver = { |
500 | .name = IBM_DRVR_NAME, | 539 | .name = IBM_DRVR_NAME, |
501 | .owner = THIS_MODULE, | 540 | .owner = THIS_MODULE, |
502 | }, | 541 | }, |
542 | .resume = tpacpi_resume_handler, | ||
503 | }; | 543 | }; |
504 | 544 | ||
505 | 545 | ||
@@ -677,9 +717,19 @@ static int __init thinkpad_acpi_driver_init(struct ibm_init_struct *iibm) | |||
677 | printk(IBM_INFO "%s v%s\n", IBM_DESC, IBM_VERSION); | 717 | printk(IBM_INFO "%s v%s\n", IBM_DESC, IBM_VERSION); |
678 | printk(IBM_INFO "%s\n", IBM_URL); | 718 | printk(IBM_INFO "%s\n", IBM_URL); |
679 | 719 | ||
680 | if (ibm_thinkpad_ec_found) | 720 | printk(IBM_INFO "ThinkPad BIOS %s, EC %s\n", |
681 | printk(IBM_INFO "ThinkPad EC firmware %s\n", | 721 | (thinkpad_id.bios_version_str) ? |
682 | ibm_thinkpad_ec_found); | 722 | thinkpad_id.bios_version_str : "unknown", |
723 | (thinkpad_id.ec_version_str) ? | ||
724 | thinkpad_id.ec_version_str : "unknown"); | ||
725 | |||
726 | if (thinkpad_id.vendor && thinkpad_id.model_str) | ||
727 | printk(IBM_INFO "%s %s\n", | ||
728 | (thinkpad_id.vendor == PCI_VENDOR_ID_IBM) ? | ||
729 | "IBM" : ((thinkpad_id.vendor == | ||
730 | PCI_VENDOR_ID_LENOVO) ? | ||
731 | "Lenovo" : "Unknown vendor"), | ||
732 | thinkpad_id.model_str); | ||
683 | 733 | ||
684 | return 0; | 734 | return 0; |
685 | } | 735 | } |
@@ -704,16 +754,28 @@ static struct ibm_struct thinkpad_acpi_driver_data = { | |||
704 | */ | 754 | */ |
705 | 755 | ||
706 | static int hotkey_orig_status; | 756 | static int hotkey_orig_status; |
707 | static int hotkey_orig_mask; | 757 | static u32 hotkey_orig_mask; |
758 | static u32 hotkey_all_mask; | ||
759 | static u32 hotkey_reserved_mask; | ||
760 | |||
761 | static u16 *hotkey_keycode_map; | ||
708 | 762 | ||
709 | static struct attribute_set *hotkey_dev_attributes = NULL; | 763 | static struct attribute_set *hotkey_dev_attributes; |
764 | |||
765 | static int hotkey_get_wlsw(int *status) | ||
766 | { | ||
767 | if (!acpi_evalf(hkey_handle, status, "WLSW", "d")) | ||
768 | return -EIO; | ||
769 | return 0; | ||
770 | } | ||
710 | 771 | ||
711 | /* sysfs hotkey enable ------------------------------------------------- */ | 772 | /* sysfs hotkey enable ------------------------------------------------- */ |
712 | static ssize_t hotkey_enable_show(struct device *dev, | 773 | static ssize_t hotkey_enable_show(struct device *dev, |
713 | struct device_attribute *attr, | 774 | struct device_attribute *attr, |
714 | char *buf) | 775 | char *buf) |
715 | { | 776 | { |
716 | int res, status, mask; | 777 | int res, status; |
778 | u32 mask; | ||
717 | 779 | ||
718 | res = hotkey_get(&status, &mask); | 780 | res = hotkey_get(&status, &mask); |
719 | if (res) | 781 | if (res) |
@@ -727,7 +789,8 @@ static ssize_t hotkey_enable_store(struct device *dev, | |||
727 | const char *buf, size_t count) | 789 | const char *buf, size_t count) |
728 | { | 790 | { |
729 | unsigned long t; | 791 | unsigned long t; |
730 | int res, status, mask; | 792 | int res, status; |
793 | u32 mask; | ||
731 | 794 | ||
732 | if (parse_strtoul(buf, 1, &t)) | 795 | if (parse_strtoul(buf, 1, &t)) |
733 | return -EINVAL; | 796 | return -EINVAL; |
@@ -748,13 +811,14 @@ static ssize_t hotkey_mask_show(struct device *dev, | |||
748 | struct device_attribute *attr, | 811 | struct device_attribute *attr, |
749 | char *buf) | 812 | char *buf) |
750 | { | 813 | { |
751 | int res, status, mask; | 814 | int res, status; |
815 | u32 mask; | ||
752 | 816 | ||
753 | res = hotkey_get(&status, &mask); | 817 | res = hotkey_get(&status, &mask); |
754 | if (res) | 818 | if (res) |
755 | return res; | 819 | return res; |
756 | 820 | ||
757 | return snprintf(buf, PAGE_SIZE, "0x%04x\n", mask); | 821 | return snprintf(buf, PAGE_SIZE, "0x%08x\n", mask); |
758 | } | 822 | } |
759 | 823 | ||
760 | static ssize_t hotkey_mask_store(struct device *dev, | 824 | static ssize_t hotkey_mask_store(struct device *dev, |
@@ -762,9 +826,10 @@ static ssize_t hotkey_mask_store(struct device *dev, | |||
762 | const char *buf, size_t count) | 826 | const char *buf, size_t count) |
763 | { | 827 | { |
764 | unsigned long t; | 828 | unsigned long t; |
765 | int res, status, mask; | 829 | int res, status; |
830 | u32 mask; | ||
766 | 831 | ||
767 | if (parse_strtoul(buf, 0xffff, &t)) | 832 | if (parse_strtoul(buf, 0xffffffffUL, &t)) |
768 | return -EINVAL; | 833 | return -EINVAL; |
769 | 834 | ||
770 | res = hotkey_get(&status, &mask); | 835 | res = hotkey_get(&status, &mask); |
@@ -794,26 +859,123 @@ static ssize_t hotkey_bios_mask_show(struct device *dev, | |||
794 | struct device_attribute *attr, | 859 | struct device_attribute *attr, |
795 | char *buf) | 860 | char *buf) |
796 | { | 861 | { |
797 | return snprintf(buf, PAGE_SIZE, "0x%04x\n", hotkey_orig_mask); | 862 | return snprintf(buf, PAGE_SIZE, "0x%08x\n", hotkey_orig_mask); |
798 | } | 863 | } |
799 | 864 | ||
800 | static struct device_attribute dev_attr_hotkey_bios_mask = | 865 | static struct device_attribute dev_attr_hotkey_bios_mask = |
801 | __ATTR(hotkey_bios_mask, S_IRUGO, hotkey_bios_mask_show, NULL); | 866 | __ATTR(hotkey_bios_mask, S_IRUGO, hotkey_bios_mask_show, NULL); |
802 | 867 | ||
868 | /* sysfs hotkey all_mask ----------------------------------------------- */ | ||
869 | static ssize_t hotkey_all_mask_show(struct device *dev, | ||
870 | struct device_attribute *attr, | ||
871 | char *buf) | ||
872 | { | ||
873 | return snprintf(buf, PAGE_SIZE, "0x%08x\n", hotkey_all_mask); | ||
874 | } | ||
875 | |||
876 | static struct device_attribute dev_attr_hotkey_all_mask = | ||
877 | __ATTR(hotkey_all_mask, S_IRUGO, hotkey_all_mask_show, NULL); | ||
878 | |||
879 | /* sysfs hotkey recommended_mask --------------------------------------- */ | ||
880 | static ssize_t hotkey_recommended_mask_show(struct device *dev, | ||
881 | struct device_attribute *attr, | ||
882 | char *buf) | ||
883 | { | ||
884 | return snprintf(buf, PAGE_SIZE, "0x%08x\n", | ||
885 | hotkey_all_mask & ~hotkey_reserved_mask); | ||
886 | } | ||
887 | |||
888 | static struct device_attribute dev_attr_hotkey_recommended_mask = | ||
889 | __ATTR(hotkey_recommended_mask, S_IRUGO, | ||
890 | hotkey_recommended_mask_show, NULL); | ||
891 | |||
892 | /* sysfs hotkey radio_sw ----------------------------------------------- */ | ||
893 | static ssize_t hotkey_radio_sw_show(struct device *dev, | ||
894 | struct device_attribute *attr, | ||
895 | char *buf) | ||
896 | { | ||
897 | int res, s; | ||
898 | res = hotkey_get_wlsw(&s); | ||
899 | if (res < 0) | ||
900 | return res; | ||
901 | |||
902 | return snprintf(buf, PAGE_SIZE, "%d\n", !!s); | ||
903 | } | ||
904 | |||
905 | static struct device_attribute dev_attr_hotkey_radio_sw = | ||
906 | __ATTR(hotkey_radio_sw, S_IRUGO, hotkey_radio_sw_show, NULL); | ||
907 | |||
803 | /* --------------------------------------------------------------------- */ | 908 | /* --------------------------------------------------------------------- */ |
804 | 909 | ||
805 | static struct attribute *hotkey_mask_attributes[] = { | 910 | static struct attribute *hotkey_mask_attributes[] = { |
806 | &dev_attr_hotkey_mask.attr, | 911 | &dev_attr_hotkey_mask.attr, |
807 | &dev_attr_hotkey_bios_enabled.attr, | 912 | &dev_attr_hotkey_bios_enabled.attr, |
808 | &dev_attr_hotkey_bios_mask.attr, | 913 | &dev_attr_hotkey_bios_mask.attr, |
914 | &dev_attr_hotkey_all_mask.attr, | ||
915 | &dev_attr_hotkey_recommended_mask.attr, | ||
809 | }; | 916 | }; |
810 | 917 | ||
811 | static int __init hotkey_init(struct ibm_init_struct *iibm) | 918 | static int __init hotkey_init(struct ibm_init_struct *iibm) |
812 | { | 919 | { |
813 | int res; | 920 | |
921 | static u16 ibm_keycode_map[] __initdata = { | ||
922 | /* Scan Codes 0x00 to 0x0B: ACPI HKEY FN+F1..F12 */ | ||
923 | KEY_FN_F1, KEY_FN_F2, KEY_COFFEE, KEY_SLEEP, | ||
924 | KEY_WLAN, KEY_FN_F6, KEY_SWITCHVIDEOMODE, KEY_FN_F8, | ||
925 | KEY_FN_F9, KEY_FN_F10, KEY_FN_F11, KEY_SUSPEND, | ||
926 | /* Scan codes 0x0C to 0x0F: Other ACPI HKEY hot keys */ | ||
927 | KEY_UNKNOWN, /* 0x0C: FN+BACKSPACE */ | ||
928 | KEY_UNKNOWN, /* 0x0D: FN+INSERT */ | ||
929 | KEY_UNKNOWN, /* 0x0E: FN+DELETE */ | ||
930 | KEY_RESERVED, /* 0x0F: FN+HOME (brightness up) */ | ||
931 | /* Scan codes 0x10 to 0x1F: Extended ACPI HKEY hot keys */ | ||
932 | KEY_RESERVED, /* 0x10: FN+END (brightness down) */ | ||
933 | KEY_RESERVED, /* 0x11: FN+PGUP (thinklight toggle) */ | ||
934 | KEY_UNKNOWN, /* 0x12: FN+PGDOWN */ | ||
935 | KEY_ZOOM, /* 0x13: FN+SPACE (zoom) */ | ||
936 | KEY_RESERVED, /* 0x14: VOLUME UP */ | ||
937 | KEY_RESERVED, /* 0x15: VOLUME DOWN */ | ||
938 | KEY_RESERVED, /* 0x16: MUTE */ | ||
939 | KEY_VENDOR, /* 0x17: Thinkpad/AccessIBM/Lenovo */ | ||
940 | /* (assignments unknown, please report if found) */ | ||
941 | KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, | ||
942 | KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, | ||
943 | }; | ||
944 | static u16 lenovo_keycode_map[] __initdata = { | ||
945 | /* Scan Codes 0x00 to 0x0B: ACPI HKEY FN+F1..F12 */ | ||
946 | KEY_FN_F1, KEY_COFFEE, KEY_BATTERY, KEY_SLEEP, | ||
947 | KEY_WLAN, KEY_FN_F6, KEY_SWITCHVIDEOMODE, KEY_FN_F8, | ||
948 | KEY_FN_F9, KEY_FN_F10, KEY_FN_F11, KEY_SUSPEND, | ||
949 | /* Scan codes 0x0C to 0x0F: Other ACPI HKEY hot keys */ | ||
950 | KEY_UNKNOWN, /* 0x0C: FN+BACKSPACE */ | ||
951 | KEY_UNKNOWN, /* 0x0D: FN+INSERT */ | ||
952 | KEY_UNKNOWN, /* 0x0E: FN+DELETE */ | ||
953 | KEY_BRIGHTNESSUP, /* 0x0F: FN+HOME (brightness up) */ | ||
954 | /* Scan codes 0x10 to 0x1F: Extended ACPI HKEY hot keys */ | ||
955 | KEY_BRIGHTNESSDOWN, /* 0x10: FN+END (brightness down) */ | ||
956 | KEY_RESERVED, /* 0x11: FN+PGUP (thinklight toggle) */ | ||
957 | KEY_UNKNOWN, /* 0x12: FN+PGDOWN */ | ||
958 | KEY_ZOOM, /* 0x13: FN+SPACE (zoom) */ | ||
959 | KEY_RESERVED, /* 0x14: VOLUME UP */ | ||
960 | KEY_RESERVED, /* 0x15: VOLUME DOWN */ | ||
961 | KEY_RESERVED, /* 0x16: MUTE */ | ||
962 | KEY_VENDOR, /* 0x17: Thinkpad/AccessIBM/Lenovo */ | ||
963 | /* (assignments unknown, please report if found) */ | ||
964 | KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, | ||
965 | KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, | ||
966 | }; | ||
967 | |||
968 | #define TPACPI_HOTKEY_MAP_LEN ARRAY_SIZE(ibm_keycode_map) | ||
969 | #define TPACPI_HOTKEY_MAP_SIZE sizeof(ibm_keycode_map) | ||
970 | #define TPACPI_HOTKEY_MAP_TYPESIZE sizeof(ibm_keycode_map[0]) | ||
971 | |||
972 | int res, i; | ||
973 | int status; | ||
814 | 974 | ||
815 | vdbg_printk(TPACPI_DBG_INIT, "initializing hotkey subdriver\n"); | 975 | vdbg_printk(TPACPI_DBG_INIT, "initializing hotkey subdriver\n"); |
816 | 976 | ||
977 | BUG_ON(!tpacpi_inputdev); | ||
978 | |||
817 | IBM_ACPIHANDLE_INIT(hkey); | 979 | IBM_ACPIHANDLE_INIT(hkey); |
818 | mutex_init(&hotkey_mutex); | 980 | mutex_init(&hotkey_mutex); |
819 | 981 | ||
@@ -824,7 +986,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
824 | str_supported(tp_features.hotkey)); | 986 | str_supported(tp_features.hotkey)); |
825 | 987 | ||
826 | if (tp_features.hotkey) { | 988 | if (tp_features.hotkey) { |
827 | hotkey_dev_attributes = create_attr_set(4, NULL); | 989 | hotkey_dev_attributes = create_attr_set(7, NULL); |
828 | if (!hotkey_dev_attributes) | 990 | if (!hotkey_dev_attributes) |
829 | return -ENOMEM; | 991 | return -ENOMEM; |
830 | res = add_to_attr_set(hotkey_dev_attributes, | 992 | res = add_to_attr_set(hotkey_dev_attributes, |
@@ -840,19 +1002,92 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
840 | vdbg_printk(TPACPI_DBG_INIT, "hotkey masks are %s\n", | 1002 | vdbg_printk(TPACPI_DBG_INIT, "hotkey masks are %s\n", |
841 | str_supported(tp_features.hotkey_mask)); | 1003 | str_supported(tp_features.hotkey_mask)); |
842 | 1004 | ||
1005 | if (tp_features.hotkey_mask) { | ||
1006 | /* MHKA available in A31, R40, R40e, T4x, X31, and later */ | ||
1007 | if (!acpi_evalf(hkey_handle, &hotkey_all_mask, | ||
1008 | "MHKA", "qd")) | ||
1009 | hotkey_all_mask = 0x080cU; /* FN+F12, FN+F4, FN+F3 */ | ||
1010 | } | ||
1011 | |||
843 | res = hotkey_get(&hotkey_orig_status, &hotkey_orig_mask); | 1012 | res = hotkey_get(&hotkey_orig_status, &hotkey_orig_mask); |
844 | if (!res && tp_features.hotkey_mask) { | 1013 | if (!res && tp_features.hotkey_mask) { |
845 | res = add_many_to_attr_set(hotkey_dev_attributes, | 1014 | res = add_many_to_attr_set(hotkey_dev_attributes, |
846 | hotkey_mask_attributes, | 1015 | hotkey_mask_attributes, |
847 | ARRAY_SIZE(hotkey_mask_attributes)); | 1016 | ARRAY_SIZE(hotkey_mask_attributes)); |
848 | } | 1017 | } |
1018 | |||
1019 | /* Not all thinkpads have a hardware radio switch */ | ||
1020 | if (!res && acpi_evalf(hkey_handle, &status, "WLSW", "qd")) { | ||
1021 | tp_features.hotkey_wlsw = 1; | ||
1022 | printk(IBM_INFO | ||
1023 | "radio switch found; radios are %s\n", | ||
1024 | enabled(status, 0)); | ||
1025 | res = add_to_attr_set(hotkey_dev_attributes, | ||
1026 | &dev_attr_hotkey_radio_sw.attr); | ||
1027 | } | ||
1028 | |||
849 | if (!res) | 1029 | if (!res) |
850 | res = register_attr_set_with_sysfs( | 1030 | res = register_attr_set_with_sysfs( |
851 | hotkey_dev_attributes, | 1031 | hotkey_dev_attributes, |
852 | &tpacpi_pdev->dev.kobj); | 1032 | &tpacpi_pdev->dev.kobj); |
1033 | if (res) | ||
1034 | return res; | ||
1035 | |||
1036 | /* Set up key map */ | ||
1037 | |||
1038 | hotkey_keycode_map = kmalloc(TPACPI_HOTKEY_MAP_SIZE, | ||
1039 | GFP_KERNEL); | ||
1040 | if (!hotkey_keycode_map) { | ||
1041 | printk(IBM_ERR "failed to allocate memory for key map\n"); | ||
1042 | return -ENOMEM; | ||
1043 | } | ||
1044 | |||
1045 | if (thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO) { | ||
1046 | dbg_printk(TPACPI_DBG_INIT, | ||
1047 | "using Lenovo default hot key map\n"); | ||
1048 | memcpy(hotkey_keycode_map, &lenovo_keycode_map, | ||
1049 | TPACPI_HOTKEY_MAP_SIZE); | ||
1050 | } else { | ||
1051 | dbg_printk(TPACPI_DBG_INIT, | ||
1052 | "using IBM default hot key map\n"); | ||
1053 | memcpy(hotkey_keycode_map, &ibm_keycode_map, | ||
1054 | TPACPI_HOTKEY_MAP_SIZE); | ||
1055 | } | ||
853 | 1056 | ||
1057 | #ifndef CONFIG_THINKPAD_ACPI_INPUT_ENABLED | ||
1058 | for (i = 0; i < 12; i++) | ||
1059 | hotkey_keycode_map[i] = KEY_UNKNOWN; | ||
1060 | #endif /* ! CONFIG_THINKPAD_ACPI_INPUT_ENABLED */ | ||
1061 | |||
1062 | set_bit(EV_KEY, tpacpi_inputdev->evbit); | ||
1063 | set_bit(EV_MSC, tpacpi_inputdev->evbit); | ||
1064 | set_bit(MSC_SCAN, tpacpi_inputdev->mscbit); | ||
1065 | tpacpi_inputdev->keycodesize = TPACPI_HOTKEY_MAP_TYPESIZE; | ||
1066 | tpacpi_inputdev->keycodemax = TPACPI_HOTKEY_MAP_LEN; | ||
1067 | tpacpi_inputdev->keycode = hotkey_keycode_map; | ||
1068 | for (i = 0; i < TPACPI_HOTKEY_MAP_LEN; i++) { | ||
1069 | if (hotkey_keycode_map[i] != KEY_RESERVED) { | ||
1070 | set_bit(hotkey_keycode_map[i], | ||
1071 | tpacpi_inputdev->keybit); | ||
1072 | } else { | ||
1073 | if (i < sizeof(hotkey_reserved_mask)*8) | ||
1074 | hotkey_reserved_mask |= 1 << i; | ||
1075 | } | ||
1076 | } | ||
1077 | |||
1078 | if (tp_features.hotkey_wlsw) { | ||
1079 | set_bit(EV_SW, tpacpi_inputdev->evbit); | ||
1080 | set_bit(SW_RADIO, tpacpi_inputdev->swbit); | ||
1081 | } | ||
1082 | |||
1083 | #ifdef CONFIG_THINKPAD_ACPI_INPUT_ENABLED | ||
1084 | dbg_printk(TPACPI_DBG_INIT, | ||
1085 | "enabling hot key handling\n"); | ||
1086 | res = hotkey_set(1, (hotkey_all_mask & ~hotkey_reserved_mask) | ||
1087 | | hotkey_orig_mask); | ||
854 | if (res) | 1088 | if (res) |
855 | return res; | 1089 | return res; |
1090 | #endif /* CONFIG_THINKPAD_ACPI_INPUT_ENABLED */ | ||
856 | } | 1091 | } |
857 | 1092 | ||
858 | return (tp_features.hotkey)? 0 : 1; | 1093 | return (tp_features.hotkey)? 0 : 1; |
@@ -875,22 +1110,101 @@ static void hotkey_exit(void) | |||
875 | } | 1110 | } |
876 | } | 1111 | } |
877 | 1112 | ||
1113 | static void tpacpi_input_send_key(unsigned int scancode, | ||
1114 | unsigned int keycode) | ||
1115 | { | ||
1116 | if (keycode != KEY_RESERVED) { | ||
1117 | input_report_key(tpacpi_inputdev, keycode, 1); | ||
1118 | if (keycode == KEY_UNKNOWN) | ||
1119 | input_event(tpacpi_inputdev, EV_MSC, MSC_SCAN, | ||
1120 | scancode); | ||
1121 | input_sync(tpacpi_inputdev); | ||
1122 | |||
1123 | input_report_key(tpacpi_inputdev, keycode, 0); | ||
1124 | if (keycode == KEY_UNKNOWN) | ||
1125 | input_event(tpacpi_inputdev, EV_MSC, MSC_SCAN, | ||
1126 | scancode); | ||
1127 | input_sync(tpacpi_inputdev); | ||
1128 | } | ||
1129 | } | ||
1130 | |||
1131 | static void tpacpi_input_send_radiosw(void) | ||
1132 | { | ||
1133 | int wlsw; | ||
1134 | |||
1135 | if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&wlsw)) | ||
1136 | input_report_switch(tpacpi_inputdev, | ||
1137 | SW_RADIO, !!wlsw); | ||
1138 | } | ||
1139 | |||
878 | static void hotkey_notify(struct ibm_struct *ibm, u32 event) | 1140 | static void hotkey_notify(struct ibm_struct *ibm, u32 event) |
879 | { | 1141 | { |
880 | int hkey; | 1142 | u32 hkey; |
1143 | unsigned int keycode, scancode; | ||
1144 | int sendacpi = 1; | ||
1145 | |||
1146 | if (event == 0x80 && acpi_evalf(hkey_handle, &hkey, "MHKP", "d")) { | ||
1147 | if (tpacpi_inputdev->users > 0) { | ||
1148 | switch (hkey >> 12) { | ||
1149 | case 1: | ||
1150 | /* 0x1000-0x1FFF: key presses */ | ||
1151 | scancode = hkey & 0xfff; | ||
1152 | if (scancode > 0 && scancode < 0x21) { | ||
1153 | scancode--; | ||
1154 | keycode = hotkey_keycode_map[scancode]; | ||
1155 | tpacpi_input_send_key(scancode, keycode); | ||
1156 | sendacpi = (keycode == KEY_RESERVED | ||
1157 | || keycode == KEY_UNKNOWN); | ||
1158 | } else { | ||
1159 | printk(IBM_ERR | ||
1160 | "hotkey 0x%04x out of range for keyboard map\n", | ||
1161 | hkey); | ||
1162 | } | ||
1163 | break; | ||
1164 | case 5: | ||
1165 | /* 0x5000-0x5FFF: LID */ | ||
1166 | /* we don't handle it through this path, just | ||
1167 | * eat up known LID events */ | ||
1168 | if (hkey != 0x5001 && hkey != 0x5002) { | ||
1169 | printk(IBM_ERR | ||
1170 | "unknown LID-related hotkey event: 0x%04x\n", | ||
1171 | hkey); | ||
1172 | } | ||
1173 | break; | ||
1174 | case 7: | ||
1175 | /* 0x7000-0x7FFF: misc */ | ||
1176 | if (tp_features.hotkey_wlsw && hkey == 0x7000) { | ||
1177 | tpacpi_input_send_radiosw(); | ||
1178 | sendacpi = 0; | ||
1179 | break; | ||
1180 | } | ||
1181 | /* fallthrough to default */ | ||
1182 | default: | ||
1183 | /* case 2: dock-related */ | ||
1184 | /* 0x2305 - T43 waking up due to bay lever eject while aslept */ | ||
1185 | /* case 3: ultra-bay related. maybe bay in dock? */ | ||
1186 | /* 0x3003 - T43 after wake up by bay lever eject (0x2305) */ | ||
1187 | printk(IBM_NOTICE "unhandled hotkey event 0x%04x\n", hkey); | ||
1188 | } | ||
1189 | } | ||
881 | 1190 | ||
882 | if (acpi_evalf(hkey_handle, &hkey, "MHKP", "d")) | 1191 | if (sendacpi) |
883 | acpi_bus_generate_event(ibm->acpi->device, event, hkey); | 1192 | acpi_bus_generate_event(ibm->acpi->device, event, hkey); |
884 | else { | 1193 | } else { |
885 | printk(IBM_ERR "unknown hotkey event %d\n", event); | 1194 | printk(IBM_ERR "unknown hotkey notification event %d\n", event); |
886 | acpi_bus_generate_event(ibm->acpi->device, event, 0); | 1195 | acpi_bus_generate_event(ibm->acpi->device, event, 0); |
887 | } | 1196 | } |
888 | } | 1197 | } |
889 | 1198 | ||
1199 | static void hotkey_resume(void) | ||
1200 | { | ||
1201 | tpacpi_input_send_radiosw(); | ||
1202 | } | ||
1203 | |||
890 | /* | 1204 | /* |
891 | * Call with hotkey_mutex held | 1205 | * Call with hotkey_mutex held |
892 | */ | 1206 | */ |
893 | static int hotkey_get(int *status, int *mask) | 1207 | static int hotkey_get(int *status, u32 *mask) |
894 | { | 1208 | { |
895 | if (!acpi_evalf(hkey_handle, status, "DHKC", "d")) | 1209 | if (!acpi_evalf(hkey_handle, status, "DHKC", "d")) |
896 | return -EIO; | 1210 | return -EIO; |
@@ -905,7 +1219,7 @@ static int hotkey_get(int *status, int *mask) | |||
905 | /* | 1219 | /* |
906 | * Call with hotkey_mutex held | 1220 | * Call with hotkey_mutex held |
907 | */ | 1221 | */ |
908 | static int hotkey_set(int status, int mask) | 1222 | static int hotkey_set(int status, u32 mask) |
909 | { | 1223 | { |
910 | int i; | 1224 | int i; |
911 | 1225 | ||
@@ -926,7 +1240,8 @@ static int hotkey_set(int status, int mask) | |||
926 | /* procfs -------------------------------------------------------------- */ | 1240 | /* procfs -------------------------------------------------------------- */ |
927 | static int hotkey_read(char *p) | 1241 | static int hotkey_read(char *p) |
928 | { | 1242 | { |
929 | int res, status, mask; | 1243 | int res, status; |
1244 | u32 mask; | ||
930 | int len = 0; | 1245 | int len = 0; |
931 | 1246 | ||
932 | if (!tp_features.hotkey) { | 1247 | if (!tp_features.hotkey) { |
@@ -944,7 +1259,7 @@ static int hotkey_read(char *p) | |||
944 | 1259 | ||
945 | len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 0)); | 1260 | len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 0)); |
946 | if (tp_features.hotkey_mask) { | 1261 | if (tp_features.hotkey_mask) { |
947 | len += sprintf(p + len, "mask:\t\t0x%04x\n", mask); | 1262 | len += sprintf(p + len, "mask:\t\t0x%08x\n", mask); |
948 | len += sprintf(p + len, | 1263 | len += sprintf(p + len, |
949 | "commands:\tenable, disable, reset, <mask>\n"); | 1264 | "commands:\tenable, disable, reset, <mask>\n"); |
950 | } else { | 1265 | } else { |
@@ -957,7 +1272,8 @@ static int hotkey_read(char *p) | |||
957 | 1272 | ||
958 | static int hotkey_write(char *buf) | 1273 | static int hotkey_write(char *buf) |
959 | { | 1274 | { |
960 | int res, status, mask; | 1275 | int res, status; |
1276 | u32 mask; | ||
961 | char *cmd; | 1277 | char *cmd; |
962 | int do_cmd = 0; | 1278 | int do_cmd = 0; |
963 | 1279 | ||
@@ -1012,6 +1328,7 @@ static struct ibm_struct hotkey_driver_data = { | |||
1012 | .read = hotkey_read, | 1328 | .read = hotkey_read, |
1013 | .write = hotkey_write, | 1329 | .write = hotkey_write, |
1014 | .exit = hotkey_exit, | 1330 | .exit = hotkey_exit, |
1331 | .resume = hotkey_resume, | ||
1015 | .acpi = &ibm_hotkey_acpidriver, | 1332 | .acpi = &ibm_hotkey_acpidriver, |
1016 | }; | 1333 | }; |
1017 | 1334 | ||
@@ -1770,7 +2087,10 @@ static struct tp_acpi_drv_struct ibm_dock_acpidriver[2] = { | |||
1770 | .type = ACPI_SYSTEM_NOTIFY, | 2087 | .type = ACPI_SYSTEM_NOTIFY, |
1771 | }, | 2088 | }, |
1772 | { | 2089 | { |
1773 | .hid = IBM_PCI_HID, | 2090 | /* THIS ONE MUST NEVER BE USED FOR DRIVER AUTOLOADING. |
2091 | * We just use it to get notifications of dock hotplug | ||
2092 | * in very old thinkpads */ | ||
2093 | .hid = PCI_ROOT_HID_STRING, | ||
1774 | .notify = dock_notify, | 2094 | .notify = dock_notify, |
1775 | .handle = &pci_handle, | 2095 | .handle = &pci_handle, |
1776 | .type = ACPI_SYSTEM_NOTIFY, | 2096 | .type = ACPI_SYSTEM_NOTIFY, |
@@ -1829,7 +2149,7 @@ static int __init dock_init2(struct ibm_init_struct *iibm) | |||
1829 | static void dock_notify(struct ibm_struct *ibm, u32 event) | 2149 | static void dock_notify(struct ibm_struct *ibm, u32 event) |
1830 | { | 2150 | { |
1831 | int docked = dock_docked(); | 2151 | int docked = dock_docked(); |
1832 | int pci = ibm->acpi->hid && strstr(ibm->acpi->hid, IBM_PCI_HID); | 2152 | int pci = ibm->acpi->hid && strstr(ibm->acpi->hid, PCI_ROOT_HID_STRING); |
1833 | 2153 | ||
1834 | if (event == 1 && !pci) /* 570 */ | 2154 | if (event == 1 && !pci) /* 570 */ |
1835 | acpi_bus_generate_event(ibm->acpi->device, event, 1); /* button */ | 2155 | acpi_bus_generate_event(ibm->acpi->device, event, 1); /* button */ |
@@ -2389,7 +2709,7 @@ static int __init thermal_init(struct ibm_init_struct *iibm) | |||
2389 | 2709 | ||
2390 | acpi_tmp7 = acpi_evalf(ec_handle, NULL, "TMP7", "qv"); | 2710 | acpi_tmp7 = acpi_evalf(ec_handle, NULL, "TMP7", "qv"); |
2391 | 2711 | ||
2392 | if (ibm_thinkpad_ec_found && experimental) { | 2712 | if (thinkpad_id.ec_model) { |
2393 | /* | 2713 | /* |
2394 | * Direct EC access mode: sensors at registers | 2714 | * Direct EC access mode: sensors at registers |
2395 | * 0x78-0x7F, 0xC0-0xC7. Registers return 0x00 for | 2715 | * 0x78-0x7F, 0xC0-0xC7. Registers return 0x00 for |
@@ -2533,6 +2853,8 @@ static int thermal_get_sensor(int idx, s32 *value) | |||
2533 | snprintf(tmpi, sizeof(tmpi), "TMP%c", '0' + idx); | 2853 | snprintf(tmpi, sizeof(tmpi), "TMP%c", '0' + idx); |
2534 | if (!acpi_evalf(ec_handle, &t, tmpi, "d")) | 2854 | if (!acpi_evalf(ec_handle, &t, tmpi, "d")) |
2535 | return -EIO; | 2855 | return -EIO; |
2856 | if (t > 127 || t < -127) | ||
2857 | t = TP_EC_THERMAL_TMP_NA; | ||
2536 | *value = t * 1000; | 2858 | *value = t * 1000; |
2537 | return 0; | 2859 | return 0; |
2538 | } | 2860 | } |
@@ -2671,22 +2993,39 @@ static struct ibm_struct ecdump_driver_data = { | |||
2671 | * Backlight/brightness subdriver | 2993 | * Backlight/brightness subdriver |
2672 | */ | 2994 | */ |
2673 | 2995 | ||
2674 | static struct backlight_device *ibm_backlight_device = NULL; | 2996 | static struct backlight_device *ibm_backlight_device; |
2675 | 2997 | ||
2676 | static struct backlight_ops ibm_backlight_data = { | 2998 | static struct backlight_ops ibm_backlight_data = { |
2677 | .get_brightness = brightness_get, | 2999 | .get_brightness = brightness_get, |
2678 | .update_status = brightness_update_status, | 3000 | .update_status = brightness_update_status, |
2679 | }; | 3001 | }; |
2680 | 3002 | ||
3003 | static struct mutex brightness_mutex; | ||
3004 | |||
2681 | static int __init brightness_init(struct ibm_init_struct *iibm) | 3005 | static int __init brightness_init(struct ibm_init_struct *iibm) |
2682 | { | 3006 | { |
2683 | int b; | 3007 | int b; |
2684 | 3008 | ||
2685 | vdbg_printk(TPACPI_DBG_INIT, "initializing brightness subdriver\n"); | 3009 | vdbg_printk(TPACPI_DBG_INIT, "initializing brightness subdriver\n"); |
2686 | 3010 | ||
3011 | mutex_init(&brightness_mutex); | ||
3012 | |||
3013 | if (!brightness_mode) { | ||
3014 | if (thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO) | ||
3015 | brightness_mode = 2; | ||
3016 | else | ||
3017 | brightness_mode = 3; | ||
3018 | |||
3019 | dbg_printk(TPACPI_DBG_INIT, "selected brightness_mode=%d\n", | ||
3020 | brightness_mode); | ||
3021 | } | ||
3022 | |||
3023 | if (brightness_mode > 3) | ||
3024 | return -EINVAL; | ||
3025 | |||
2687 | b = brightness_get(NULL); | 3026 | b = brightness_get(NULL); |
2688 | if (b < 0) | 3027 | if (b < 0) |
2689 | return b; | 3028 | return 1; |
2690 | 3029 | ||
2691 | ibm_backlight_device = backlight_device_register( | 3030 | ibm_backlight_device = backlight_device_register( |
2692 | TPACPI_BACKLIGHT_DEV_NAME, NULL, NULL, | 3031 | TPACPI_BACKLIGHT_DEV_NAME, NULL, NULL, |
@@ -2722,34 +3061,79 @@ static int brightness_update_status(struct backlight_device *bd) | |||
2722 | bd->props.brightness : 0); | 3061 | bd->props.brightness : 0); |
2723 | } | 3062 | } |
2724 | 3063 | ||
3064 | /* | ||
3065 | * ThinkPads can read brightness from two places: EC 0x31, or | ||
3066 | * CMOS NVRAM byte 0x5E, bits 0-3. | ||
3067 | */ | ||
2725 | static int brightness_get(struct backlight_device *bd) | 3068 | static int brightness_get(struct backlight_device *bd) |
2726 | { | 3069 | { |
2727 | u8 level; | 3070 | u8 lec = 0, lcmos = 0, level = 0; |
2728 | if (!acpi_ec_read(brightness_offset, &level)) | ||
2729 | return -EIO; | ||
2730 | 3071 | ||
2731 | level &= 0x7; | 3072 | if (brightness_mode & 1) { |
3073 | if (!acpi_ec_read(brightness_offset, &lec)) | ||
3074 | return -EIO; | ||
3075 | lec &= 7; | ||
3076 | level = lec; | ||
3077 | }; | ||
3078 | if (brightness_mode & 2) { | ||
3079 | lcmos = (nvram_read_byte(TP_NVRAM_ADDR_BRIGHTNESS) | ||
3080 | & TP_NVRAM_MASK_LEVEL_BRIGHTNESS) | ||
3081 | >> TP_NVRAM_POS_LEVEL_BRIGHTNESS; | ||
3082 | level = lcmos; | ||
3083 | } | ||
3084 | |||
3085 | if (brightness_mode == 3 && lec != lcmos) { | ||
3086 | printk(IBM_ERR | ||
3087 | "CMOS NVRAM (%u) and EC (%u) do not agree " | ||
3088 | "on display brightness level\n", | ||
3089 | (unsigned int) lcmos, | ||
3090 | (unsigned int) lec); | ||
3091 | return -EIO; | ||
3092 | } | ||
2732 | 3093 | ||
2733 | return level; | 3094 | return level; |
2734 | } | 3095 | } |
2735 | 3096 | ||
2736 | static int brightness_set(int value) | 3097 | static int brightness_set(int value) |
2737 | { | 3098 | { |
2738 | int cmos_cmd, inc, i; | 3099 | int cmos_cmd, inc, i, res; |
2739 | int current_value = brightness_get(NULL); | 3100 | int current_value; |
3101 | |||
3102 | if (value > 7) | ||
3103 | return -EINVAL; | ||
2740 | 3104 | ||
2741 | value &= 7; | 3105 | res = mutex_lock_interruptible(&brightness_mutex); |
3106 | if (res < 0) | ||
3107 | return res; | ||
3108 | |||
3109 | current_value = brightness_get(NULL); | ||
3110 | if (current_value < 0) { | ||
3111 | res = current_value; | ||
3112 | goto errout; | ||
3113 | } | ||
2742 | 3114 | ||
2743 | cmos_cmd = value > current_value ? TP_CMOS_BRIGHTNESS_UP : TP_CMOS_BRIGHTNESS_DOWN; | 3115 | cmos_cmd = value > current_value ? |
3116 | TP_CMOS_BRIGHTNESS_UP : | ||
3117 | TP_CMOS_BRIGHTNESS_DOWN; | ||
2744 | inc = value > current_value ? 1 : -1; | 3118 | inc = value > current_value ? 1 : -1; |
3119 | |||
3120 | res = 0; | ||
2745 | for (i = current_value; i != value; i += inc) { | 3121 | for (i = current_value; i != value; i += inc) { |
2746 | if (issue_thinkpad_cmos_command(cmos_cmd)) | 3122 | if ((brightness_mode & 2) && |
2747 | return -EIO; | 3123 | issue_thinkpad_cmos_command(cmos_cmd)) { |
2748 | if (!acpi_ec_write(brightness_offset, i + inc)) | 3124 | res = -EIO; |
2749 | return -EIO; | 3125 | goto errout; |
3126 | } | ||
3127 | if ((brightness_mode & 1) && | ||
3128 | !acpi_ec_write(brightness_offset, i + inc)) { | ||
3129 | res = -EIO; | ||
3130 | goto errout;; | ||
3131 | } | ||
2750 | } | 3132 | } |
2751 | 3133 | ||
2752 | return 0; | 3134 | errout: |
3135 | mutex_unlock(&brightness_mutex); | ||
3136 | return res; | ||
2753 | } | 3137 | } |
2754 | 3138 | ||
2755 | static int brightness_read(char *p) | 3139 | static int brightness_read(char *p) |
@@ -3273,20 +3657,19 @@ static int __init fan_init(struct ibm_init_struct *iibm) | |||
3273 | * Enable for TP-1Y (T43), TP-78 (R51e), | 3657 | * Enable for TP-1Y (T43), TP-78 (R51e), |
3274 | * TP-76 (R52), TP-70 (T43, R52), which are known | 3658 | * TP-76 (R52), TP-70 (T43, R52), which are known |
3275 | * to be buggy. */ | 3659 | * to be buggy. */ |
3276 | if (fan_control_initial_status == 0x07 && | 3660 | if (fan_control_initial_status == 0x07) { |
3277 | ibm_thinkpad_ec_found && | 3661 | switch (thinkpad_id.ec_model) { |
3278 | ((ibm_thinkpad_ec_found[0] == '1' && | 3662 | case 0x5931: /* TP-1Y */ |
3279 | ibm_thinkpad_ec_found[1] == 'Y') || | 3663 | case 0x3837: /* TP-78 */ |
3280 | (ibm_thinkpad_ec_found[0] == '7' && | 3664 | case 0x3637: /* TP-76 */ |
3281 | (ibm_thinkpad_ec_found[1] == '6' || | 3665 | case 0x3037: /* TP-70 */ |
3282 | ibm_thinkpad_ec_found[1] == '8' || | 3666 | printk(IBM_NOTICE |
3283 | ibm_thinkpad_ec_found[1] == '0')) | 3667 | "fan_init: initial fan status is " |
3284 | )) { | 3668 | "unknown, assuming it is in auto " |
3285 | printk(IBM_NOTICE | 3669 | "mode\n"); |
3286 | "fan_init: initial fan status is " | 3670 | tp_features.fan_ctrl_status_undef = 1; |
3287 | "unknown, assuming it is in auto " | 3671 | ;; |
3288 | "mode\n"); | 3672 | } |
3289 | tp_features.fan_ctrl_status_undef = 1; | ||
3290 | } | 3673 | } |
3291 | } else { | 3674 | } else { |
3292 | printk(IBM_ERR | 3675 | printk(IBM_ERR |
@@ -3474,7 +3857,7 @@ static void fan_watchdog_fire(struct work_struct *ignored) | |||
3474 | 3857 | ||
3475 | static void fan_watchdog_reset(void) | 3858 | static void fan_watchdog_reset(void) |
3476 | { | 3859 | { |
3477 | static int fan_watchdog_active = 0; | 3860 | static int fan_watchdog_active; |
3478 | 3861 | ||
3479 | if (fan_control_access_mode == TPACPI_FAN_WR_NONE) | 3862 | if (fan_control_access_mode == TPACPI_FAN_WR_NONE) |
3480 | return; | 3863 | return; |
@@ -3877,7 +4260,7 @@ static struct ibm_struct fan_driver_data = { | |||
3877 | ****************************************************************************/ | 4260 | ****************************************************************************/ |
3878 | 4261 | ||
3879 | /* /proc support */ | 4262 | /* /proc support */ |
3880 | static struct proc_dir_entry *proc_dir = NULL; | 4263 | static struct proc_dir_entry *proc_dir; |
3881 | 4264 | ||
3882 | /* Subdriver registry */ | 4265 | /* Subdriver registry */ |
3883 | static LIST_HEAD(tpacpi_all_drivers); | 4266 | static LIST_HEAD(tpacpi_all_drivers); |
@@ -4020,13 +4403,30 @@ static void ibm_exit(struct ibm_struct *ibm) | |||
4020 | 4403 | ||
4021 | /* Probing */ | 4404 | /* Probing */ |
4022 | 4405 | ||
4023 | static char *ibm_thinkpad_ec_found = NULL; | 4406 | static void __init get_thinkpad_model_data(struct thinkpad_id_data *tp) |
4024 | |||
4025 | static char* __init check_dmi_for_ec(void) | ||
4026 | { | 4407 | { |
4027 | struct dmi_device *dev = NULL; | 4408 | struct dmi_device *dev = NULL; |
4028 | char ec_fw_string[18]; | 4409 | char ec_fw_string[18]; |
4029 | 4410 | ||
4411 | if (!tp) | ||
4412 | return; | ||
4413 | |||
4414 | memset(tp, 0, sizeof(*tp)); | ||
4415 | |||
4416 | if (dmi_name_in_vendors("IBM")) | ||
4417 | tp->vendor = PCI_VENDOR_ID_IBM; | ||
4418 | else if (dmi_name_in_vendors("LENOVO")) | ||
4419 | tp->vendor = PCI_VENDOR_ID_LENOVO; | ||
4420 | else | ||
4421 | return; | ||
4422 | |||
4423 | tp->bios_version_str = kstrdup(dmi_get_system_info(DMI_BIOS_VERSION), | ||
4424 | GFP_KERNEL); | ||
4425 | if (!tp->bios_version_str) | ||
4426 | return; | ||
4427 | tp->bios_model = tp->bios_version_str[0] | ||
4428 | | (tp->bios_version_str[1] << 8); | ||
4429 | |||
4030 | /* | 4430 | /* |
4031 | * ThinkPad T23 or newer, A31 or newer, R50e or newer, | 4431 | * ThinkPad T23 or newer, A31 or newer, R50e or newer, |
4032 | * X32 or newer, all Z series; Some models must have an | 4432 | * X32 or newer, all Z series; Some models must have an |
@@ -4040,10 +4440,20 @@ static char* __init check_dmi_for_ec(void) | |||
4040 | ec_fw_string) == 1) { | 4440 | ec_fw_string) == 1) { |
4041 | ec_fw_string[sizeof(ec_fw_string) - 1] = 0; | 4441 | ec_fw_string[sizeof(ec_fw_string) - 1] = 0; |
4042 | ec_fw_string[strcspn(ec_fw_string, " ]")] = 0; | 4442 | ec_fw_string[strcspn(ec_fw_string, " ]")] = 0; |
4043 | return kstrdup(ec_fw_string, GFP_KERNEL); | 4443 | |
4444 | tp->ec_version_str = kstrdup(ec_fw_string, GFP_KERNEL); | ||
4445 | tp->ec_model = ec_fw_string[0] | ||
4446 | | (ec_fw_string[1] << 8); | ||
4447 | break; | ||
4044 | } | 4448 | } |
4045 | } | 4449 | } |
4046 | return NULL; | 4450 | |
4451 | tp->model_str = kstrdup(dmi_get_system_info(DMI_PRODUCT_VERSION), | ||
4452 | GFP_KERNEL); | ||
4453 | if (strnicmp(tp->model_str, "ThinkPad", 8) != 0) { | ||
4454 | kfree(tp->model_str); | ||
4455 | tp->model_str = NULL; | ||
4456 | } | ||
4047 | } | 4457 | } |
4048 | 4458 | ||
4049 | static int __init probe_for_thinkpad(void) | 4459 | static int __init probe_for_thinkpad(void) |
@@ -4057,7 +4467,7 @@ static int __init probe_for_thinkpad(void) | |||
4057 | * Non-ancient models have better DMI tagging, but very old models | 4467 | * Non-ancient models have better DMI tagging, but very old models |
4058 | * don't. | 4468 | * don't. |
4059 | */ | 4469 | */ |
4060 | is_thinkpad = dmi_name_in_vendors("ThinkPad"); | 4470 | is_thinkpad = (thinkpad_id.model_str != NULL); |
4061 | 4471 | ||
4062 | /* ec is required because many other handles are relative to it */ | 4472 | /* ec is required because many other handles are relative to it */ |
4063 | IBM_ACPIHANDLE_INIT(ec); | 4473 | IBM_ACPIHANDLE_INIT(ec); |
@@ -4073,7 +4483,7 @@ static int __init probe_for_thinkpad(void) | |||
4073 | * false positives a damn great deal | 4483 | * false positives a damn great deal |
4074 | */ | 4484 | */ |
4075 | if (!is_thinkpad) | 4485 | if (!is_thinkpad) |
4076 | is_thinkpad = dmi_name_in_vendors("IBM"); | 4486 | is_thinkpad = (thinkpad_id.vendor == PCI_VENDOR_ID_IBM); |
4077 | 4487 | ||
4078 | if (!is_thinkpad && !force_load) | 4488 | if (!is_thinkpad && !force_load) |
4079 | return -ENODEV; | 4489 | return -ENODEV; |
@@ -4185,10 +4595,13 @@ static u32 dbg_level; | |||
4185 | module_param_named(debug, dbg_level, uint, 0); | 4595 | module_param_named(debug, dbg_level, uint, 0); |
4186 | 4596 | ||
4187 | static int force_load; | 4597 | static int force_load; |
4188 | module_param(force_load, int, 0); | 4598 | module_param(force_load, bool, 0); |
4189 | 4599 | ||
4190 | static int fan_control_allowed; | 4600 | static int fan_control_allowed; |
4191 | module_param_named(fan_control, fan_control_allowed, int, 0); | 4601 | module_param_named(fan_control, fan_control_allowed, bool, 0); |
4602 | |||
4603 | static int brightness_mode; | ||
4604 | module_param_named(brightness_mode, brightness_mode, int, 0); | ||
4192 | 4605 | ||
4193 | #define IBM_PARAM(feature) \ | 4606 | #define IBM_PARAM(feature) \ |
4194 | module_param_call(feature, set_ibm_param, NULL, NULL, 0) | 4607 | module_param_call(feature, set_ibm_param, NULL, NULL, 0) |
@@ -4216,12 +4629,16 @@ static int __init thinkpad_acpi_module_init(void) | |||
4216 | int ret, i; | 4629 | int ret, i; |
4217 | 4630 | ||
4218 | /* Driver-level probe */ | 4631 | /* Driver-level probe */ |
4632 | |||
4633 | get_thinkpad_model_data(&thinkpad_id); | ||
4219 | ret = probe_for_thinkpad(); | 4634 | ret = probe_for_thinkpad(); |
4220 | if (ret) | 4635 | if (ret) { |
4636 | thinkpad_acpi_module_exit(); | ||
4221 | return ret; | 4637 | return ret; |
4638 | } | ||
4222 | 4639 | ||
4223 | /* Driver initialization */ | 4640 | /* Driver initialization */ |
4224 | ibm_thinkpad_ec_found = check_dmi_for_ec(); | 4641 | |
4225 | IBM_ACPIHANDLE_INIT(ecrd); | 4642 | IBM_ACPIHANDLE_INIT(ecrd); |
4226 | IBM_ACPIHANDLE_INIT(ecwr); | 4643 | IBM_ACPIHANDLE_INIT(ecwr); |
4227 | 4644 | ||
@@ -4265,6 +4682,22 @@ static int __init thinkpad_acpi_module_init(void) | |||
4265 | thinkpad_acpi_module_exit(); | 4682 | thinkpad_acpi_module_exit(); |
4266 | return ret; | 4683 | return ret; |
4267 | } | 4684 | } |
4685 | tpacpi_inputdev = input_allocate_device(); | ||
4686 | if (!tpacpi_inputdev) { | ||
4687 | printk(IBM_ERR "unable to allocate input device\n"); | ||
4688 | thinkpad_acpi_module_exit(); | ||
4689 | return -ENOMEM; | ||
4690 | } else { | ||
4691 | /* Prepare input device, but don't register */ | ||
4692 | tpacpi_inputdev->name = "ThinkPad Extra Buttons"; | ||
4693 | tpacpi_inputdev->phys = IBM_DRVR_NAME "/input0"; | ||
4694 | tpacpi_inputdev->id.bustype = BUS_HOST; | ||
4695 | tpacpi_inputdev->id.vendor = (thinkpad_id.vendor) ? | ||
4696 | thinkpad_id.vendor : | ||
4697 | PCI_VENDOR_ID_IBM; | ||
4698 | tpacpi_inputdev->id.product = TPACPI_HKEY_INPUT_PRODUCT; | ||
4699 | tpacpi_inputdev->id.version = TPACPI_HKEY_INPUT_VERSION; | ||
4700 | } | ||
4268 | for (i = 0; i < ARRAY_SIZE(ibms_init); i++) { | 4701 | for (i = 0; i < ARRAY_SIZE(ibms_init); i++) { |
4269 | ret = ibm_init(&ibms_init[i]); | 4702 | ret = ibm_init(&ibms_init[i]); |
4270 | if (ret >= 0 && *ibms_init[i].param) | 4703 | if (ret >= 0 && *ibms_init[i].param) |
@@ -4274,6 +4707,14 @@ static int __init thinkpad_acpi_module_init(void) | |||
4274 | return ret; | 4707 | return ret; |
4275 | } | 4708 | } |
4276 | } | 4709 | } |
4710 | ret = input_register_device(tpacpi_inputdev); | ||
4711 | if (ret < 0) { | ||
4712 | printk(IBM_ERR "unable to register input device\n"); | ||
4713 | thinkpad_acpi_module_exit(); | ||
4714 | return ret; | ||
4715 | } else { | ||
4716 | tp_features.input_device_registered = 1; | ||
4717 | } | ||
4277 | 4718 | ||
4278 | return 0; | 4719 | return 0; |
4279 | } | 4720 | } |
@@ -4290,6 +4731,13 @@ static void thinkpad_acpi_module_exit(void) | |||
4290 | 4731 | ||
4291 | dbg_printk(TPACPI_DBG_INIT, "finished subdriver exit path...\n"); | 4732 | dbg_printk(TPACPI_DBG_INIT, "finished subdriver exit path...\n"); |
4292 | 4733 | ||
4734 | if (tpacpi_inputdev) { | ||
4735 | if (tp_features.input_device_registered) | ||
4736 | input_unregister_device(tpacpi_inputdev); | ||
4737 | else | ||
4738 | input_free_device(tpacpi_inputdev); | ||
4739 | } | ||
4740 | |||
4293 | if (tpacpi_hwmon) | 4741 | if (tpacpi_hwmon) |
4294 | hwmon_device_unregister(tpacpi_hwmon); | 4742 | hwmon_device_unregister(tpacpi_hwmon); |
4295 | 4743 | ||
@@ -4302,7 +4750,9 @@ static void thinkpad_acpi_module_exit(void) | |||
4302 | if (proc_dir) | 4750 | if (proc_dir) |
4303 | remove_proc_entry(IBM_PROC_DIR, acpi_root_dir); | 4751 | remove_proc_entry(IBM_PROC_DIR, acpi_root_dir); |
4304 | 4752 | ||
4305 | kfree(ibm_thinkpad_ec_found); | 4753 | kfree(thinkpad_id.bios_version_str); |
4754 | kfree(thinkpad_id.ec_version_str); | ||
4755 | kfree(thinkpad_id.model_str); | ||
4306 | } | 4756 | } |
4307 | 4757 | ||
4308 | module_init(thinkpad_acpi_module_init); | 4758 | module_init(thinkpad_acpi_module_init); |