diff options
Diffstat (limited to 'drivers/misc/thinkpad_acpi.c')
-rw-r--r-- | drivers/misc/thinkpad_acpi.c | 505 |
1 files changed, 492 insertions, 13 deletions
diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c index e7ac1c8a5541..9ff9142ce063 100644 --- a/drivers/misc/thinkpad_acpi.c +++ b/drivers/misc/thinkpad_acpi.c | |||
@@ -22,7 +22,7 @@ | |||
22 | */ | 22 | */ |
23 | 23 | ||
24 | #define IBM_VERSION "0.17" | 24 | #define IBM_VERSION "0.17" |
25 | #define TPACPI_SYSFS_VERSION 0x020000 | 25 | #define TPACPI_SYSFS_VERSION 0x020101 |
26 | 26 | ||
27 | /* | 27 | /* |
28 | * Changelog: | 28 | * Changelog: |
@@ -773,6 +773,67 @@ static struct ibm_struct thinkpad_acpi_driver_data = { | |||
773 | * Hotkey subdriver | 773 | * Hotkey subdriver |
774 | */ | 774 | */ |
775 | 775 | ||
776 | enum { /* Keys available through NVRAM polling */ | ||
777 | TPACPI_HKEY_NVRAM_KNOWN_MASK = 0x00fb88c0U, | ||
778 | TPACPI_HKEY_NVRAM_GOOD_MASK = 0x00fb8000U, | ||
779 | }; | ||
780 | |||
781 | enum { /* Positions of some of the keys in hotkey masks */ | ||
782 | TP_ACPI_HKEY_DISPSWTCH_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNF7, | ||
783 | TP_ACPI_HKEY_DISPXPAND_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNF8, | ||
784 | TP_ACPI_HKEY_HIBERNATE_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNF12, | ||
785 | TP_ACPI_HKEY_BRGHTUP_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNHOME, | ||
786 | TP_ACPI_HKEY_BRGHTDWN_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNEND, | ||
787 | TP_ACPI_HKEY_THNKLGHT_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNPAGEUP, | ||
788 | TP_ACPI_HKEY_ZOOM_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNSPACE, | ||
789 | TP_ACPI_HKEY_VOLUP_MASK = 1 << TP_ACPI_HOTKEYSCAN_VOLUMEUP, | ||
790 | TP_ACPI_HKEY_VOLDWN_MASK = 1 << TP_ACPI_HOTKEYSCAN_VOLUMEDOWN, | ||
791 | TP_ACPI_HKEY_MUTE_MASK = 1 << TP_ACPI_HOTKEYSCAN_MUTE, | ||
792 | TP_ACPI_HKEY_THINKPAD_MASK = 1 << TP_ACPI_HOTKEYSCAN_THINKPAD, | ||
793 | }; | ||
794 | |||
795 | enum { /* NVRAM to ACPI HKEY group map */ | ||
796 | TP_NVRAM_HKEY_GROUP_HK2 = TP_ACPI_HKEY_THINKPAD_MASK | | ||
797 | TP_ACPI_HKEY_ZOOM_MASK | | ||
798 | TP_ACPI_HKEY_DISPSWTCH_MASK | | ||
799 | TP_ACPI_HKEY_HIBERNATE_MASK, | ||
800 | TP_NVRAM_HKEY_GROUP_BRIGHTNESS = TP_ACPI_HKEY_BRGHTUP_MASK | | ||
801 | TP_ACPI_HKEY_BRGHTDWN_MASK, | ||
802 | TP_NVRAM_HKEY_GROUP_VOLUME = TP_ACPI_HKEY_VOLUP_MASK | | ||
803 | TP_ACPI_HKEY_VOLDWN_MASK | | ||
804 | TP_ACPI_HKEY_MUTE_MASK, | ||
805 | }; | ||
806 | |||
807 | #ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL | ||
808 | struct tp_nvram_state { | ||
809 | u16 thinkpad_toggle:1; | ||
810 | u16 zoom_toggle:1; | ||
811 | u16 display_toggle:1; | ||
812 | u16 thinklight_toggle:1; | ||
813 | u16 hibernate_toggle:1; | ||
814 | u16 displayexp_toggle:1; | ||
815 | u16 display_state:1; | ||
816 | u16 brightness_toggle:1; | ||
817 | u16 volume_toggle:1; | ||
818 | u16 mute:1; | ||
819 | |||
820 | u8 brightness_level; | ||
821 | u8 volume_level; | ||
822 | }; | ||
823 | |||
824 | static struct task_struct *tpacpi_hotkey_task; | ||
825 | static u32 hotkey_source_mask; /* bit mask 0=ACPI,1=NVRAM */ | ||
826 | static int hotkey_poll_freq = 10; /* Hz */ | ||
827 | static struct mutex hotkey_thread_mutex; | ||
828 | static struct mutex hotkey_thread_data_mutex; | ||
829 | static unsigned int hotkey_config_change; | ||
830 | |||
831 | #else /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */ | ||
832 | |||
833 | #define hotkey_source_mask 0U | ||
834 | |||
835 | #endif /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */ | ||
836 | |||
776 | static int hotkey_orig_status; | 837 | static int hotkey_orig_status; |
777 | static u32 hotkey_orig_mask; | 838 | static u32 hotkey_orig_mask; |
778 | static u32 hotkey_all_mask; | 839 | static u32 hotkey_all_mask; |
@@ -783,6 +844,17 @@ static u16 *hotkey_keycode_map; | |||
783 | 844 | ||
784 | static struct attribute_set *hotkey_dev_attributes; | 845 | static struct attribute_set *hotkey_dev_attributes; |
785 | 846 | ||
847 | #ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL | ||
848 | #define HOTKEY_CONFIG_CRITICAL_START \ | ||
849 | mutex_lock(&hotkey_thread_data_mutex); \ | ||
850 | hotkey_config_change++; | ||
851 | #define HOTKEY_CONFIG_CRITICAL_END \ | ||
852 | mutex_unlock(&hotkey_thread_data_mutex); | ||
853 | #else | ||
854 | #define HOTKEY_CONFIG_CRITICAL_START | ||
855 | #define HOTKEY_CONFIG_CRITICAL_END | ||
856 | #endif /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */ | ||
857 | |||
786 | static int hotkey_get_wlsw(int *status) | 858 | static int hotkey_get_wlsw(int *status) |
787 | { | 859 | { |
788 | if (!acpi_evalf(hkey_handle, status, "WLSW", "d")) | 860 | if (!acpi_evalf(hkey_handle, status, "WLSW", "d")) |
@@ -795,10 +867,13 @@ static int hotkey_get_wlsw(int *status) | |||
795 | */ | 867 | */ |
796 | static int hotkey_mask_get(void) | 868 | static int hotkey_mask_get(void) |
797 | { | 869 | { |
870 | u32 m = 0; | ||
871 | |||
798 | if (tp_features.hotkey_mask) { | 872 | if (tp_features.hotkey_mask) { |
799 | if (!acpi_evalf(hkey_handle, &hotkey_mask, "DHKN", "d")) | 873 | if (!acpi_evalf(hkey_handle, &m, "DHKN", "d")) |
800 | return -EIO; | 874 | return -EIO; |
801 | } | 875 | } |
876 | hotkey_mask = m | (hotkey_source_mask & hotkey_mask); | ||
802 | 877 | ||
803 | return 0; | 878 | return 0; |
804 | } | 879 | } |
@@ -812,25 +887,50 @@ static int hotkey_mask_set(u32 mask) | |||
812 | int rc = 0; | 887 | int rc = 0; |
813 | 888 | ||
814 | if (tp_features.hotkey_mask) { | 889 | if (tp_features.hotkey_mask) { |
890 | HOTKEY_CONFIG_CRITICAL_START | ||
815 | for (i = 0; i < 32; i++) { | 891 | for (i = 0; i < 32; i++) { |
816 | u32 m = 1 << i; | 892 | u32 m = 1 << i; |
893 | /* enable in firmware mask only keys not in NVRAM | ||
894 | * mode, but enable the key in the cached hotkey_mask | ||
895 | * regardless of mode, or the key will end up | ||
896 | * disabled by hotkey_mask_get() */ | ||
817 | if (!acpi_evalf(hkey_handle, | 897 | if (!acpi_evalf(hkey_handle, |
818 | NULL, "MHKM", "vdd", i + 1, | 898 | NULL, "MHKM", "vdd", i + 1, |
819 | !!(mask & m))) { | 899 | !!((mask & ~hotkey_source_mask) & m))) { |
820 | rc = -EIO; | 900 | rc = -EIO; |
821 | break; | 901 | break; |
822 | } else { | 902 | } else { |
823 | hotkey_mask = (hotkey_mask & ~m) | (mask & m); | 903 | hotkey_mask = (hotkey_mask & ~m) | (mask & m); |
824 | } | 904 | } |
825 | } | 905 | } |
906 | HOTKEY_CONFIG_CRITICAL_END | ||
826 | 907 | ||
827 | /* hotkey_mask_get must be called unconditionally below */ | 908 | /* hotkey_mask_get must be called unconditionally below */ |
828 | if (!hotkey_mask_get() && !rc && hotkey_mask != mask) { | 909 | if (!hotkey_mask_get() && !rc && |
910 | (hotkey_mask & ~hotkey_source_mask) != | ||
911 | (mask & ~hotkey_source_mask)) { | ||
829 | printk(IBM_NOTICE | 912 | printk(IBM_NOTICE |
830 | "requested hot key mask 0x%08x, but " | 913 | "requested hot key mask 0x%08x, but " |
831 | "firmware forced it to 0x%08x\n", | 914 | "firmware forced it to 0x%08x\n", |
832 | mask, hotkey_mask); | 915 | mask, hotkey_mask); |
833 | } | 916 | } |
917 | } else { | ||
918 | #ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL | ||
919 | HOTKEY_CONFIG_CRITICAL_START | ||
920 | hotkey_mask = mask & hotkey_source_mask; | ||
921 | HOTKEY_CONFIG_CRITICAL_END | ||
922 | hotkey_mask_get(); | ||
923 | if (hotkey_mask != mask) { | ||
924 | printk(IBM_NOTICE | ||
925 | "requested hot key mask 0x%08x, " | ||
926 | "forced to 0x%08x (NVRAM poll mask is " | ||
927 | "0x%08x): no firmware mask support\n", | ||
928 | mask, hotkey_mask, hotkey_source_mask); | ||
929 | } | ||
930 | #else | ||
931 | hotkey_mask_get(); | ||
932 | rc = -ENXIO; | ||
933 | #endif /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */ | ||
834 | } | 934 | } |
835 | 935 | ||
836 | return rc; | 936 | return rc; |
@@ -892,6 +992,256 @@ static void tpacpi_input_send_key(unsigned int scancode) | |||
892 | } | 992 | } |
893 | } | 993 | } |
894 | 994 | ||
995 | #ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL | ||
996 | static struct tp_acpi_drv_struct ibm_hotkey_acpidriver; | ||
997 | |||
998 | static void tpacpi_hotkey_send_key(unsigned int scancode) | ||
999 | { | ||
1000 | tpacpi_input_send_key(scancode); | ||
1001 | if (hotkey_report_mode < 2) { | ||
1002 | acpi_bus_generate_proc_event(ibm_hotkey_acpidriver.device, | ||
1003 | 0x80, 0x1001 + scancode); | ||
1004 | } | ||
1005 | } | ||
1006 | |||
1007 | static void hotkey_read_nvram(struct tp_nvram_state *n, u32 m) | ||
1008 | { | ||
1009 | u8 d; | ||
1010 | |||
1011 | if (m & TP_NVRAM_HKEY_GROUP_HK2) { | ||
1012 | d = nvram_read_byte(TP_NVRAM_ADDR_HK2); | ||
1013 | n->thinkpad_toggle = !!(d & TP_NVRAM_MASK_HKT_THINKPAD); | ||
1014 | n->zoom_toggle = !!(d & TP_NVRAM_MASK_HKT_ZOOM); | ||
1015 | n->display_toggle = !!(d & TP_NVRAM_MASK_HKT_DISPLAY); | ||
1016 | n->hibernate_toggle = !!(d & TP_NVRAM_MASK_HKT_HIBERNATE); | ||
1017 | } | ||
1018 | if (m & TP_ACPI_HKEY_THNKLGHT_MASK) { | ||
1019 | d = nvram_read_byte(TP_NVRAM_ADDR_THINKLIGHT); | ||
1020 | n->thinklight_toggle = !!(d & TP_NVRAM_MASK_THINKLIGHT); | ||
1021 | } | ||
1022 | if (m & TP_ACPI_HKEY_DISPXPAND_MASK) { | ||
1023 | d = nvram_read_byte(TP_NVRAM_ADDR_VIDEO); | ||
1024 | n->displayexp_toggle = | ||
1025 | !!(d & TP_NVRAM_MASK_HKT_DISPEXPND); | ||
1026 | } | ||
1027 | if (m & TP_NVRAM_HKEY_GROUP_BRIGHTNESS) { | ||
1028 | d = nvram_read_byte(TP_NVRAM_ADDR_BRIGHTNESS); | ||
1029 | n->brightness_level = (d & TP_NVRAM_MASK_LEVEL_BRIGHTNESS) | ||
1030 | >> TP_NVRAM_POS_LEVEL_BRIGHTNESS; | ||
1031 | n->brightness_toggle = | ||
1032 | !!(d & TP_NVRAM_MASK_HKT_BRIGHTNESS); | ||
1033 | } | ||
1034 | if (m & TP_NVRAM_HKEY_GROUP_VOLUME) { | ||
1035 | d = nvram_read_byte(TP_NVRAM_ADDR_MIXER); | ||
1036 | n->volume_level = (d & TP_NVRAM_MASK_LEVEL_VOLUME) | ||
1037 | >> TP_NVRAM_POS_LEVEL_VOLUME; | ||
1038 | n->mute = !!(d & TP_NVRAM_MASK_MUTE); | ||
1039 | n->volume_toggle = !!(d & TP_NVRAM_MASK_HKT_VOLUME); | ||
1040 | } | ||
1041 | } | ||
1042 | |||
1043 | #define TPACPI_COMPARE_KEY(__scancode, __member) \ | ||
1044 | do { if ((mask & (1 << __scancode)) && oldn->__member != newn->__member) \ | ||
1045 | tpacpi_hotkey_send_key(__scancode); } while (0) | ||
1046 | |||
1047 | #define TPACPI_MAY_SEND_KEY(__scancode) \ | ||
1048 | do { if (mask & (1 << __scancode)) \ | ||
1049 | tpacpi_hotkey_send_key(__scancode); } while (0) | ||
1050 | |||
1051 | static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn, | ||
1052 | struct tp_nvram_state *newn, | ||
1053 | u32 mask) | ||
1054 | { | ||
1055 | TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_THINKPAD, thinkpad_toggle); | ||
1056 | TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNSPACE, zoom_toggle); | ||
1057 | TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNF7, display_toggle); | ||
1058 | TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNF12, hibernate_toggle); | ||
1059 | |||
1060 | TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNPAGEUP, thinklight_toggle); | ||
1061 | |||
1062 | TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNF8, displayexp_toggle); | ||
1063 | |||
1064 | /* handle volume */ | ||
1065 | if (oldn->volume_toggle != newn->volume_toggle) { | ||
1066 | if (oldn->mute != newn->mute) { | ||
1067 | TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_MUTE); | ||
1068 | } | ||
1069 | if (oldn->volume_level > newn->volume_level) { | ||
1070 | TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEDOWN); | ||
1071 | } else if (oldn->volume_level < newn->volume_level) { | ||
1072 | TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEUP); | ||
1073 | } else if (oldn->mute == newn->mute) { | ||
1074 | /* repeated key presses that didn't change state */ | ||
1075 | if (newn->mute) { | ||
1076 | TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_MUTE); | ||
1077 | } else if (newn->volume_level != 0) { | ||
1078 | TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEUP); | ||
1079 | } else { | ||
1080 | TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEDOWN); | ||
1081 | } | ||
1082 | } | ||
1083 | } | ||
1084 | |||
1085 | /* handle brightness */ | ||
1086 | if (oldn->brightness_toggle != newn->brightness_toggle) { | ||
1087 | if (oldn->brightness_level < newn->brightness_level) { | ||
1088 | TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNHOME); | ||
1089 | } else if (oldn->brightness_level > newn->brightness_level) { | ||
1090 | TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNEND); | ||
1091 | } else { | ||
1092 | /* repeated key presses that didn't change state */ | ||
1093 | if (newn->brightness_level != 0) { | ||
1094 | TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNHOME); | ||
1095 | } else { | ||
1096 | TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNEND); | ||
1097 | } | ||
1098 | } | ||
1099 | } | ||
1100 | } | ||
1101 | |||
1102 | #undef TPACPI_COMPARE_KEY | ||
1103 | #undef TPACPI_MAY_SEND_KEY | ||
1104 | |||
1105 | static int hotkey_kthread(void *data) | ||
1106 | { | ||
1107 | struct tp_nvram_state s[2]; | ||
1108 | u32 mask; | ||
1109 | unsigned int si, so; | ||
1110 | unsigned long t; | ||
1111 | unsigned int change_detector, must_reset; | ||
1112 | |||
1113 | mutex_lock(&hotkey_thread_mutex); | ||
1114 | |||
1115 | if (tpacpi_lifecycle == TPACPI_LIFE_EXITING) | ||
1116 | goto exit; | ||
1117 | |||
1118 | set_freezable(); | ||
1119 | |||
1120 | so = 0; | ||
1121 | si = 1; | ||
1122 | t = 0; | ||
1123 | |||
1124 | /* Initial state for compares */ | ||
1125 | mutex_lock(&hotkey_thread_data_mutex); | ||
1126 | change_detector = hotkey_config_change; | ||
1127 | mask = hotkey_source_mask & hotkey_mask; | ||
1128 | mutex_unlock(&hotkey_thread_data_mutex); | ||
1129 | hotkey_read_nvram(&s[so], mask); | ||
1130 | |||
1131 | while (!kthread_should_stop() && hotkey_poll_freq) { | ||
1132 | if (t == 0) | ||
1133 | t = 1000/hotkey_poll_freq; | ||
1134 | t = msleep_interruptible(t); | ||
1135 | if (unlikely(kthread_should_stop())) | ||
1136 | break; | ||
1137 | must_reset = try_to_freeze(); | ||
1138 | if (t > 0 && !must_reset) | ||
1139 | continue; | ||
1140 | |||
1141 | mutex_lock(&hotkey_thread_data_mutex); | ||
1142 | if (must_reset || hotkey_config_change != change_detector) { | ||
1143 | /* forget old state on thaw or config change */ | ||
1144 | si = so; | ||
1145 | t = 0; | ||
1146 | change_detector = hotkey_config_change; | ||
1147 | } | ||
1148 | mask = hotkey_source_mask & hotkey_mask; | ||
1149 | mutex_unlock(&hotkey_thread_data_mutex); | ||
1150 | |||
1151 | if (likely(mask)) { | ||
1152 | hotkey_read_nvram(&s[si], mask); | ||
1153 | if (likely(si != so)) { | ||
1154 | hotkey_compare_and_issue_event(&s[so], &s[si], | ||
1155 | mask); | ||
1156 | } | ||
1157 | } | ||
1158 | |||
1159 | so = si; | ||
1160 | si ^= 1; | ||
1161 | } | ||
1162 | |||
1163 | exit: | ||
1164 | mutex_unlock(&hotkey_thread_mutex); | ||
1165 | return 0; | ||
1166 | } | ||
1167 | |||
1168 | static void hotkey_poll_stop_sync(void) | ||
1169 | { | ||
1170 | if (tpacpi_hotkey_task) { | ||
1171 | if (frozen(tpacpi_hotkey_task) || | ||
1172 | freezing(tpacpi_hotkey_task)) | ||
1173 | thaw_process(tpacpi_hotkey_task); | ||
1174 | |||
1175 | kthread_stop(tpacpi_hotkey_task); | ||
1176 | tpacpi_hotkey_task = NULL; | ||
1177 | mutex_lock(&hotkey_thread_mutex); | ||
1178 | /* at this point, the thread did exit */ | ||
1179 | mutex_unlock(&hotkey_thread_mutex); | ||
1180 | } | ||
1181 | } | ||
1182 | |||
1183 | /* call with hotkey_mutex held */ | ||
1184 | static void hotkey_poll_setup(int may_warn) | ||
1185 | { | ||
1186 | if ((hotkey_source_mask & hotkey_mask) != 0 && | ||
1187 | hotkey_poll_freq > 0 && | ||
1188 | (tpacpi_inputdev->users > 0 || hotkey_report_mode < 2)) { | ||
1189 | if (!tpacpi_hotkey_task) { | ||
1190 | tpacpi_hotkey_task = kthread_run(hotkey_kthread, | ||
1191 | NULL, IBM_FILE "d"); | ||
1192 | if (IS_ERR(tpacpi_hotkey_task)) { | ||
1193 | tpacpi_hotkey_task = NULL; | ||
1194 | printk(IBM_ERR "could not create kernel thread " | ||
1195 | "for hotkey polling\n"); | ||
1196 | } | ||
1197 | } | ||
1198 | } else { | ||
1199 | hotkey_poll_stop_sync(); | ||
1200 | if (may_warn && | ||
1201 | hotkey_source_mask != 0 && hotkey_poll_freq == 0) { | ||
1202 | printk(IBM_NOTICE "hot keys 0x%08x require polling, " | ||
1203 | "which is currently disabled\n", | ||
1204 | hotkey_source_mask); | ||
1205 | } | ||
1206 | } | ||
1207 | } | ||
1208 | |||
1209 | static void hotkey_poll_setup_safe(int may_warn) | ||
1210 | { | ||
1211 | mutex_lock(&hotkey_mutex); | ||
1212 | hotkey_poll_setup(may_warn); | ||
1213 | mutex_unlock(&hotkey_mutex); | ||
1214 | } | ||
1215 | |||
1216 | static int hotkey_inputdev_open(struct input_dev *dev) | ||
1217 | { | ||
1218 | switch (tpacpi_lifecycle) { | ||
1219 | case TPACPI_LIFE_INIT: | ||
1220 | /* | ||
1221 | * hotkey_init will call hotkey_poll_setup_safe | ||
1222 | * at the appropriate moment | ||
1223 | */ | ||
1224 | return 0; | ||
1225 | case TPACPI_LIFE_EXITING: | ||
1226 | return -EBUSY; | ||
1227 | case TPACPI_LIFE_RUNNING: | ||
1228 | hotkey_poll_setup_safe(0); | ||
1229 | return 0; | ||
1230 | } | ||
1231 | |||
1232 | /* Should only happen if tpacpi_lifecycle is corrupt */ | ||
1233 | BUG(); | ||
1234 | return -EBUSY; | ||
1235 | } | ||
1236 | |||
1237 | static void hotkey_inputdev_close(struct input_dev *dev) | ||
1238 | { | ||
1239 | /* disable hotkey polling when possible */ | ||
1240 | if (tpacpi_lifecycle == TPACPI_LIFE_RUNNING) | ||
1241 | hotkey_poll_setup_safe(0); | ||
1242 | } | ||
1243 | #endif /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */ | ||
1244 | |||
895 | /* sysfs hotkey enable ------------------------------------------------- */ | 1245 | /* sysfs hotkey enable ------------------------------------------------- */ |
896 | static ssize_t hotkey_enable_show(struct device *dev, | 1246 | static ssize_t hotkey_enable_show(struct device *dev, |
897 | struct device_attribute *attr, | 1247 | struct device_attribute *attr, |
@@ -955,6 +1305,11 @@ static ssize_t hotkey_mask_store(struct device *dev, | |||
955 | return -ERESTARTSYS; | 1305 | return -ERESTARTSYS; |
956 | 1306 | ||
957 | res = hotkey_mask_set(t); | 1307 | res = hotkey_mask_set(t); |
1308 | |||
1309 | #ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL | ||
1310 | hotkey_poll_setup(1); | ||
1311 | #endif | ||
1312 | |||
958 | mutex_unlock(&hotkey_mutex); | 1313 | mutex_unlock(&hotkey_mutex); |
959 | 1314 | ||
960 | return (res) ? res : count; | 1315 | return (res) ? res : count; |
@@ -991,7 +1346,8 @@ static ssize_t hotkey_all_mask_show(struct device *dev, | |||
991 | struct device_attribute *attr, | 1346 | struct device_attribute *attr, |
992 | char *buf) | 1347 | char *buf) |
993 | { | 1348 | { |
994 | return snprintf(buf, PAGE_SIZE, "0x%08x\n", hotkey_all_mask); | 1349 | return snprintf(buf, PAGE_SIZE, "0x%08x\n", |
1350 | hotkey_all_mask | hotkey_source_mask); | ||
995 | } | 1351 | } |
996 | 1352 | ||
997 | static struct device_attribute dev_attr_hotkey_all_mask = | 1353 | static struct device_attribute dev_attr_hotkey_all_mask = |
@@ -1003,13 +1359,86 @@ static ssize_t hotkey_recommended_mask_show(struct device *dev, | |||
1003 | char *buf) | 1359 | char *buf) |
1004 | { | 1360 | { |
1005 | return snprintf(buf, PAGE_SIZE, "0x%08x\n", | 1361 | return snprintf(buf, PAGE_SIZE, "0x%08x\n", |
1006 | hotkey_all_mask & ~hotkey_reserved_mask); | 1362 | (hotkey_all_mask | hotkey_source_mask) |
1363 | & ~hotkey_reserved_mask); | ||
1007 | } | 1364 | } |
1008 | 1365 | ||
1009 | static struct device_attribute dev_attr_hotkey_recommended_mask = | 1366 | static struct device_attribute dev_attr_hotkey_recommended_mask = |
1010 | __ATTR(hotkey_recommended_mask, S_IRUGO, | 1367 | __ATTR(hotkey_recommended_mask, S_IRUGO, |
1011 | hotkey_recommended_mask_show, NULL); | 1368 | hotkey_recommended_mask_show, NULL); |
1012 | 1369 | ||
1370 | #ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL | ||
1371 | |||
1372 | /* sysfs hotkey hotkey_source_mask ------------------------------------- */ | ||
1373 | static ssize_t hotkey_source_mask_show(struct device *dev, | ||
1374 | struct device_attribute *attr, | ||
1375 | char *buf) | ||
1376 | { | ||
1377 | return snprintf(buf, PAGE_SIZE, "0x%08x\n", hotkey_source_mask); | ||
1378 | } | ||
1379 | |||
1380 | static ssize_t hotkey_source_mask_store(struct device *dev, | ||
1381 | struct device_attribute *attr, | ||
1382 | const char *buf, size_t count) | ||
1383 | { | ||
1384 | unsigned long t; | ||
1385 | |||
1386 | if (parse_strtoul(buf, 0xffffffffUL, &t) || | ||
1387 | ((t & ~TPACPI_HKEY_NVRAM_KNOWN_MASK) != 0)) | ||
1388 | return -EINVAL; | ||
1389 | |||
1390 | if (mutex_lock_interruptible(&hotkey_mutex)) | ||
1391 | return -ERESTARTSYS; | ||
1392 | |||
1393 | HOTKEY_CONFIG_CRITICAL_START | ||
1394 | hotkey_source_mask = t; | ||
1395 | HOTKEY_CONFIG_CRITICAL_END | ||
1396 | |||
1397 | hotkey_poll_setup(1); | ||
1398 | |||
1399 | mutex_unlock(&hotkey_mutex); | ||
1400 | |||
1401 | return count; | ||
1402 | } | ||
1403 | |||
1404 | static struct device_attribute dev_attr_hotkey_source_mask = | ||
1405 | __ATTR(hotkey_source_mask, S_IWUSR | S_IRUGO, | ||
1406 | hotkey_source_mask_show, hotkey_source_mask_store); | ||
1407 | |||
1408 | /* sysfs hotkey hotkey_poll_freq --------------------------------------- */ | ||
1409 | static ssize_t hotkey_poll_freq_show(struct device *dev, | ||
1410 | struct device_attribute *attr, | ||
1411 | char *buf) | ||
1412 | { | ||
1413 | return snprintf(buf, PAGE_SIZE, "%d\n", hotkey_poll_freq); | ||
1414 | } | ||
1415 | |||
1416 | static ssize_t hotkey_poll_freq_store(struct device *dev, | ||
1417 | struct device_attribute *attr, | ||
1418 | const char *buf, size_t count) | ||
1419 | { | ||
1420 | unsigned long t; | ||
1421 | |||
1422 | if (parse_strtoul(buf, 25, &t)) | ||
1423 | return -EINVAL; | ||
1424 | |||
1425 | if (mutex_lock_interruptible(&hotkey_mutex)) | ||
1426 | return -ERESTARTSYS; | ||
1427 | |||
1428 | hotkey_poll_freq = t; | ||
1429 | |||
1430 | hotkey_poll_setup(1); | ||
1431 | mutex_unlock(&hotkey_mutex); | ||
1432 | |||
1433 | return count; | ||
1434 | } | ||
1435 | |||
1436 | static struct device_attribute dev_attr_hotkey_poll_freq = | ||
1437 | __ATTR(hotkey_poll_freq, S_IWUSR | S_IRUGO, | ||
1438 | hotkey_poll_freq_show, hotkey_poll_freq_store); | ||
1439 | |||
1440 | #endif /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */ | ||
1441 | |||
1013 | /* sysfs hotkey radio_sw ----------------------------------------------- */ | 1442 | /* sysfs hotkey radio_sw ----------------------------------------------- */ |
1014 | static ssize_t hotkey_radio_sw_show(struct device *dev, | 1443 | static ssize_t hotkey_radio_sw_show(struct device *dev, |
1015 | struct device_attribute *attr, | 1444 | struct device_attribute *attr, |
@@ -1042,15 +1471,24 @@ static struct device_attribute dev_attr_hotkey_report_mode = | |||
1042 | 1471 | ||
1043 | static struct attribute *hotkey_attributes[] __initdata = { | 1472 | static struct attribute *hotkey_attributes[] __initdata = { |
1044 | &dev_attr_hotkey_enable.attr, | 1473 | &dev_attr_hotkey_enable.attr, |
1474 | &dev_attr_hotkey_bios_enabled.attr, | ||
1045 | &dev_attr_hotkey_report_mode.attr, | 1475 | &dev_attr_hotkey_report_mode.attr, |
1476 | #ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL | ||
1477 | &dev_attr_hotkey_mask.attr, | ||
1478 | &dev_attr_hotkey_all_mask.attr, | ||
1479 | &dev_attr_hotkey_recommended_mask.attr, | ||
1480 | &dev_attr_hotkey_source_mask.attr, | ||
1481 | &dev_attr_hotkey_poll_freq.attr, | ||
1482 | #endif | ||
1046 | }; | 1483 | }; |
1047 | 1484 | ||
1048 | static struct attribute *hotkey_mask_attributes[] __initdata = { | 1485 | static struct attribute *hotkey_mask_attributes[] __initdata = { |
1049 | &dev_attr_hotkey_mask.attr, | ||
1050 | &dev_attr_hotkey_bios_enabled.attr, | ||
1051 | &dev_attr_hotkey_bios_mask.attr, | 1486 | &dev_attr_hotkey_bios_mask.attr, |
1487 | #ifndef CONFIG_THINKPAD_ACPI_HOTKEY_POLL | ||
1488 | &dev_attr_hotkey_mask.attr, | ||
1052 | &dev_attr_hotkey_all_mask.attr, | 1489 | &dev_attr_hotkey_all_mask.attr, |
1053 | &dev_attr_hotkey_recommended_mask.attr, | 1490 | &dev_attr_hotkey_recommended_mask.attr, |
1491 | #endif | ||
1054 | }; | 1492 | }; |
1055 | 1493 | ||
1056 | static int __init hotkey_init(struct ibm_init_struct *iibm) | 1494 | static int __init hotkey_init(struct ibm_init_struct *iibm) |
@@ -1172,10 +1610,17 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
1172 | vdbg_printk(TPACPI_DBG_INIT, "initializing hotkey subdriver\n"); | 1610 | vdbg_printk(TPACPI_DBG_INIT, "initializing hotkey subdriver\n"); |
1173 | 1611 | ||
1174 | BUG_ON(!tpacpi_inputdev); | 1612 | BUG_ON(!tpacpi_inputdev); |
1613 | BUG_ON(tpacpi_inputdev->open != NULL || | ||
1614 | tpacpi_inputdev->close != NULL); | ||
1175 | 1615 | ||
1176 | IBM_ACPIHANDLE_INIT(hkey); | 1616 | IBM_ACPIHANDLE_INIT(hkey); |
1177 | mutex_init(&hotkey_mutex); | 1617 | mutex_init(&hotkey_mutex); |
1178 | 1618 | ||
1619 | #ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL | ||
1620 | mutex_init(&hotkey_thread_mutex); | ||
1621 | mutex_init(&hotkey_thread_data_mutex); | ||
1622 | #endif | ||
1623 | |||
1179 | /* hotkey not supported on 570 */ | 1624 | /* hotkey not supported on 570 */ |
1180 | tp_features.hotkey = hkey_handle != NULL; | 1625 | tp_features.hotkey = hkey_handle != NULL; |
1181 | 1626 | ||
@@ -1183,7 +1628,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
1183 | str_supported(tp_features.hotkey)); | 1628 | str_supported(tp_features.hotkey)); |
1184 | 1629 | ||
1185 | if (tp_features.hotkey) { | 1630 | if (tp_features.hotkey) { |
1186 | hotkey_dev_attributes = create_attr_set(8, NULL); | 1631 | hotkey_dev_attributes = create_attr_set(10, NULL); |
1187 | if (!hotkey_dev_attributes) | 1632 | if (!hotkey_dev_attributes) |
1188 | return -ENOMEM; | 1633 | return -ENOMEM; |
1189 | res = add_many_to_attr_set(hotkey_dev_attributes, | 1634 | res = add_many_to_attr_set(hotkey_dev_attributes, |
@@ -1205,7 +1650,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
1205 | /* | 1650 | /* |
1206 | * MHKV 0x100 in A31, R40, R40e, | 1651 | * MHKV 0x100 in A31, R40, R40e, |
1207 | * T4x, X31, and later | 1652 | * T4x, X31, and later |
1208 | * */ | 1653 | */ |
1209 | tp_features.hotkey_mask = 1; | 1654 | tp_features.hotkey_mask = 1; |
1210 | } | 1655 | } |
1211 | } | 1656 | } |
@@ -1224,6 +1669,8 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
1224 | } | 1669 | } |
1225 | } | 1670 | } |
1226 | 1671 | ||
1672 | /* hotkey_source_mask *must* be zero for | ||
1673 | * the first hotkey_mask_get */ | ||
1227 | res = hotkey_status_get(&hotkey_orig_status); | 1674 | res = hotkey_status_get(&hotkey_orig_status); |
1228 | if (!res && tp_features.hotkey_mask) { | 1675 | if (!res && tp_features.hotkey_mask) { |
1229 | res = hotkey_mask_get(); | 1676 | res = hotkey_mask_get(); |
@@ -1236,6 +1683,19 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
1236 | } | 1683 | } |
1237 | } | 1684 | } |
1238 | 1685 | ||
1686 | #ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL | ||
1687 | if (tp_features.hotkey_mask) { | ||
1688 | hotkey_source_mask = TPACPI_HKEY_NVRAM_GOOD_MASK | ||
1689 | & ~hotkey_all_mask; | ||
1690 | } else { | ||
1691 | hotkey_source_mask = TPACPI_HKEY_NVRAM_GOOD_MASK; | ||
1692 | } | ||
1693 | |||
1694 | vdbg_printk(TPACPI_DBG_INIT, | ||
1695 | "hotkey source mask 0x%08x, polling freq %d\n", | ||
1696 | hotkey_source_mask, hotkey_poll_freq); | ||
1697 | #endif | ||
1698 | |||
1239 | /* Not all thinkpads have a hardware radio switch */ | 1699 | /* Not all thinkpads have a hardware radio switch */ |
1240 | if (!res && acpi_evalf(hkey_handle, &status, "WLSW", "qd")) { | 1700 | if (!res && acpi_evalf(hkey_handle, &status, "WLSW", "qd")) { |
1241 | tp_features.hotkey_wlsw = 1; | 1701 | tp_features.hotkey_wlsw = 1; |
@@ -1300,15 +1760,23 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
1300 | res = hotkey_status_set(1); | 1760 | res = hotkey_status_set(1); |
1301 | if (res) | 1761 | if (res) |
1302 | return res; | 1762 | return res; |
1303 | res = hotkey_mask_set((hotkey_all_mask & ~hotkey_reserved_mask) | 1763 | res = hotkey_mask_set(((hotkey_all_mask | hotkey_source_mask) |
1764 | & ~hotkey_reserved_mask) | ||
1304 | | hotkey_orig_mask); | 1765 | | hotkey_orig_mask); |
1305 | if (res) | 1766 | if (res < 0 && res != -ENXIO) |
1306 | return res; | 1767 | return res; |
1307 | 1768 | ||
1308 | dbg_printk(TPACPI_DBG_INIT, | 1769 | dbg_printk(TPACPI_DBG_INIT, |
1309 | "legacy hot key reporting over procfs %s\n", | 1770 | "legacy hot key reporting over procfs %s\n", |
1310 | (hotkey_report_mode < 2) ? | 1771 | (hotkey_report_mode < 2) ? |
1311 | "enabled" : "disabled"); | 1772 | "enabled" : "disabled"); |
1773 | |||
1774 | #ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL | ||
1775 | tpacpi_inputdev->open = &hotkey_inputdev_open; | ||
1776 | tpacpi_inputdev->close = &hotkey_inputdev_close; | ||
1777 | |||
1778 | hotkey_poll_setup_safe(1); | ||
1779 | #endif | ||
1312 | } | 1780 | } |
1313 | 1781 | ||
1314 | return (tp_features.hotkey)? 0 : 1; | 1782 | return (tp_features.hotkey)? 0 : 1; |
@@ -1316,6 +1784,10 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
1316 | 1784 | ||
1317 | static void hotkey_exit(void) | 1785 | static void hotkey_exit(void) |
1318 | { | 1786 | { |
1787 | #ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL | ||
1788 | hotkey_poll_stop_sync(); | ||
1789 | #endif | ||
1790 | |||
1319 | if (tp_features.hotkey) { | 1791 | if (tp_features.hotkey) { |
1320 | dbg_printk(TPACPI_DBG_EXIT, "restoring original hot key mask\n"); | 1792 | dbg_printk(TPACPI_DBG_EXIT, "restoring original hot key mask\n"); |
1321 | /* no short-circuit boolean operator below! */ | 1793 | /* no short-circuit boolean operator below! */ |
@@ -1366,7 +1838,11 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event) | |||
1366 | scancode = hkey & 0xfff; | 1838 | scancode = hkey & 0xfff; |
1367 | if (scancode > 0 && scancode < 0x21) { | 1839 | if (scancode > 0 && scancode < 0x21) { |
1368 | scancode--; | 1840 | scancode--; |
1369 | tpacpi_input_send_key(scancode); | 1841 | if (!(hotkey_source_mask & (1 << scancode))) { |
1842 | tpacpi_input_send_key(scancode); | ||
1843 | } else { | ||
1844 | ignore_acpi_ev = 1; | ||
1845 | } | ||
1370 | } else { | 1846 | } else { |
1371 | printk(IBM_ERR | 1847 | printk(IBM_ERR |
1372 | "hotkey 0x%04x out of range for keyboard map\n", | 1848 | "hotkey 0x%04x out of range for keyboard map\n", |
@@ -1422,6 +1898,9 @@ static void hotkey_resume(void) | |||
1422 | if (hotkey_mask_get()) | 1898 | if (hotkey_mask_get()) |
1423 | printk(IBM_ERR "error while trying to read hot key mask from firmware\n"); | 1899 | printk(IBM_ERR "error while trying to read hot key mask from firmware\n"); |
1424 | tpacpi_input_send_radiosw(); | 1900 | tpacpi_input_send_radiosw(); |
1901 | #ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL | ||
1902 | hotkey_poll_setup_safe(0); | ||
1903 | #endif | ||
1425 | } | 1904 | } |
1426 | 1905 | ||
1427 | /* procfs -------------------------------------------------------------- */ | 1906 | /* procfs -------------------------------------------------------------- */ |