diff options
| -rw-r--r-- | Documentation/laptops/thinkpad-acpi.txt | 25 | ||||
| -rw-r--r-- | drivers/platform/x86/Kconfig | 11 | ||||
| -rw-r--r-- | drivers/platform/x86/thinkpad_acpi.c | 910 |
3 files changed, 791 insertions, 155 deletions
diff --git a/Documentation/laptops/thinkpad-acpi.txt b/Documentation/laptops/thinkpad-acpi.txt index 898b4987bb80..41bc99fa1884 100644 --- a/Documentation/laptops/thinkpad-acpi.txt +++ b/Documentation/laptops/thinkpad-acpi.txt | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | ThinkPad ACPI Extras Driver | 1 | ThinkPad ACPI Extras Driver |
| 2 | 2 | ||
| 3 | Version 0.21 | 3 | Version 0.22 |
| 4 | May 29th, 2008 | 4 | November 23rd, 2008 |
| 5 | 5 | ||
| 6 | Borislav Deianov <borislav@users.sf.net> | 6 | Borislav Deianov <borislav@users.sf.net> |
| 7 | Henrique de Moraes Holschuh <hmh@hmh.eng.br> | 7 | Henrique de Moraes Holschuh <hmh@hmh.eng.br> |
| @@ -16,7 +16,8 @@ supported by the generic Linux ACPI drivers. | |||
| 16 | This driver used to be named ibm-acpi until kernel 2.6.21 and release | 16 | This driver used to be named ibm-acpi until kernel 2.6.21 and release |
| 17 | 0.13-20070314. It used to be in the drivers/acpi tree, but it was | 17 | 0.13-20070314. It used to be in the drivers/acpi tree, but it was |
| 18 | moved to the drivers/misc tree and renamed to thinkpad-acpi for kernel | 18 | moved to the drivers/misc tree and renamed to thinkpad-acpi for kernel |
| 19 | 2.6.22, and release 0.14. | 19 | 2.6.22, and release 0.14. It was moved to drivers/platform/x86 for |
| 20 | kernel 2.6.29 and release 0.22. | ||
| 20 | 21 | ||
| 21 | The driver is named "thinkpad-acpi". In some places, like module | 22 | The driver is named "thinkpad-acpi". In some places, like module |
| 22 | names, "thinkpad_acpi" is used because of userspace issues. | 23 | names, "thinkpad_acpi" is used because of userspace issues. |
| @@ -1412,6 +1413,24 @@ Sysfs notes: | |||
| 1412 | rfkill controller switch "tpacpi_wwan_sw": refer to | 1413 | rfkill controller switch "tpacpi_wwan_sw": refer to |
| 1413 | Documentation/rfkill.txt for details. | 1414 | Documentation/rfkill.txt for details. |
| 1414 | 1415 | ||
| 1416 | EXPERIMENTAL: UWB | ||
| 1417 | ----------------- | ||
| 1418 | |||
| 1419 | This feature is marked EXPERIMENTAL because it has not been extensively | ||
| 1420 | tested and validated in various ThinkPad models yet. The feature may not | ||
| 1421 | work as expected. USE WITH CAUTION! To use this feature, you need to supply | ||
| 1422 | the experimental=1 parameter when loading the module. | ||
| 1423 | |||
| 1424 | sysfs rfkill class: switch "tpacpi_uwb_sw" | ||
| 1425 | |||
| 1426 | This feature exports an rfkill controller for the UWB device, if one is | ||
| 1427 | present and enabled in the BIOS. | ||
| 1428 | |||
| 1429 | Sysfs notes: | ||
| 1430 | |||
| 1431 | rfkill controller switch "tpacpi_uwb_sw": refer to | ||
| 1432 | Documentation/rfkill.txt for details. | ||
| 1433 | |||
| 1415 | Multiple Commands, Module Parameters | 1434 | Multiple Commands, Module Parameters |
| 1416 | ------------------------------------ | 1435 | ------------------------------------ |
| 1417 | 1436 | ||
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index e65448e99b48..431772b8a1d0 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig | |||
| @@ -192,6 +192,17 @@ config THINKPAD_ACPI | |||
| 192 | 192 | ||
| 193 | If you have an IBM or Lenovo ThinkPad laptop, say Y or M here. | 193 | If you have an IBM or Lenovo ThinkPad laptop, say Y or M here. |
| 194 | 194 | ||
| 195 | config THINKPAD_ACPI_DEBUGFACILITIES | ||
| 196 | bool "Maintainer debug facilities" | ||
| 197 | depends on THINKPAD_ACPI | ||
| 198 | default n | ||
| 199 | ---help--- | ||
| 200 | Enables extra stuff in the thinkpad-acpi which is completely useless | ||
| 201 | for normal use. Read the driver source to find out what it does. | ||
| 202 | |||
| 203 | Say N here, unless you were told by a kernel maintainer to do | ||
| 204 | otherwise. | ||
| 205 | |||
| 195 | config THINKPAD_ACPI_DEBUG | 206 | config THINKPAD_ACPI_DEBUG |
| 196 | bool "Verbose debug mode" | 207 | bool "Verbose debug mode" |
| 197 | depends on THINKPAD_ACPI | 208 | depends on THINKPAD_ACPI |
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 3478453eba7a..bcbc05107ba8 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c | |||
| @@ -21,7 +21,7 @@ | |||
| 21 | * 02110-1301, USA. | 21 | * 02110-1301, USA. |
| 22 | */ | 22 | */ |
| 23 | 23 | ||
| 24 | #define TPACPI_VERSION "0.21" | 24 | #define TPACPI_VERSION "0.22" |
| 25 | #define TPACPI_SYSFS_VERSION 0x020200 | 25 | #define TPACPI_SYSFS_VERSION 0x020200 |
| 26 | 26 | ||
| 27 | /* | 27 | /* |
| @@ -122,6 +122,27 @@ enum { | |||
| 122 | #define TPACPI_HKEY_INPUT_PRODUCT 0x5054 /* "TP" */ | 122 | #define TPACPI_HKEY_INPUT_PRODUCT 0x5054 /* "TP" */ |
| 123 | #define TPACPI_HKEY_INPUT_VERSION 0x4101 | 123 | #define TPACPI_HKEY_INPUT_VERSION 0x4101 |
| 124 | 124 | ||
| 125 | /* ACPI \WGSV commands */ | ||
| 126 | enum { | ||
| 127 | TP_ACPI_WGSV_GET_STATE = 0x01, /* Get state information */ | ||
| 128 | TP_ACPI_WGSV_PWR_ON_ON_RESUME = 0x02, /* Resume WWAN powered on */ | ||
| 129 | TP_ACPI_WGSV_PWR_OFF_ON_RESUME = 0x03, /* Resume WWAN powered off */ | ||
| 130 | TP_ACPI_WGSV_SAVE_STATE = 0x04, /* Save state for S4/S5 */ | ||
| 131 | }; | ||
| 132 | |||
| 133 | /* TP_ACPI_WGSV_GET_STATE bits */ | ||
| 134 | enum { | ||
| 135 | TP_ACPI_WGSV_STATE_WWANEXIST = 0x0001, /* WWAN hw available */ | ||
| 136 | TP_ACPI_WGSV_STATE_WWANPWR = 0x0002, /* WWAN radio enabled */ | ||
| 137 | TP_ACPI_WGSV_STATE_WWANPWRRES = 0x0004, /* WWAN state at resume */ | ||
| 138 | TP_ACPI_WGSV_STATE_WWANBIOSOFF = 0x0008, /* WWAN disabled in BIOS */ | ||
| 139 | TP_ACPI_WGSV_STATE_BLTHEXIST = 0x0001, /* BLTH hw available */ | ||
| 140 | TP_ACPI_WGSV_STATE_BLTHPWR = 0x0002, /* BLTH radio enabled */ | ||
| 141 | TP_ACPI_WGSV_STATE_BLTHPWRRES = 0x0004, /* BLTH state at resume */ | ||
| 142 | TP_ACPI_WGSV_STATE_BLTHBIOSOFF = 0x0008, /* BLTH disabled in BIOS */ | ||
| 143 | TP_ACPI_WGSV_STATE_UWBEXIST = 0x0010, /* UWB hw available */ | ||
| 144 | TP_ACPI_WGSV_STATE_UWBPWR = 0x0020, /* UWB radio enabled */ | ||
| 145 | }; | ||
| 125 | 146 | ||
| 126 | /**************************************************************************** | 147 | /**************************************************************************** |
| 127 | * Main driver | 148 | * Main driver |
| @@ -148,14 +169,17 @@ enum { | |||
| 148 | enum { | 169 | enum { |
| 149 | TPACPI_RFK_BLUETOOTH_SW_ID = 0, | 170 | TPACPI_RFK_BLUETOOTH_SW_ID = 0, |
| 150 | TPACPI_RFK_WWAN_SW_ID, | 171 | TPACPI_RFK_WWAN_SW_ID, |
| 172 | TPACPI_RFK_UWB_SW_ID, | ||
| 151 | }; | 173 | }; |
| 152 | 174 | ||
| 153 | /* Debugging */ | 175 | /* Debugging */ |
| 154 | #define TPACPI_LOG TPACPI_FILE ": " | 176 | #define TPACPI_LOG TPACPI_FILE ": " |
| 155 | #define TPACPI_ERR KERN_ERR TPACPI_LOG | 177 | #define TPACPI_ALERT KERN_ALERT TPACPI_LOG |
| 156 | #define TPACPI_NOTICE KERN_NOTICE TPACPI_LOG | 178 | #define TPACPI_CRIT KERN_CRIT TPACPI_LOG |
| 157 | #define TPACPI_INFO KERN_INFO TPACPI_LOG | 179 | #define TPACPI_ERR KERN_ERR TPACPI_LOG |
| 158 | #define TPACPI_DEBUG KERN_DEBUG TPACPI_LOG | 180 | #define TPACPI_NOTICE KERN_NOTICE TPACPI_LOG |
| 181 | #define TPACPI_INFO KERN_INFO TPACPI_LOG | ||
| 182 | #define TPACPI_DEBUG KERN_DEBUG TPACPI_LOG | ||
| 159 | 183 | ||
| 160 | #define TPACPI_DBG_ALL 0xffff | 184 | #define TPACPI_DBG_ALL 0xffff |
| 161 | #define TPACPI_DBG_INIT 0x0001 | 185 | #define TPACPI_DBG_INIT 0x0001 |
| @@ -201,6 +225,7 @@ struct ibm_struct { | |||
| 201 | void (*exit) (void); | 225 | void (*exit) (void); |
| 202 | void (*resume) (void); | 226 | void (*resume) (void); |
| 203 | void (*suspend) (pm_message_t state); | 227 | void (*suspend) (pm_message_t state); |
| 228 | void (*shutdown) (void); | ||
| 204 | 229 | ||
| 205 | struct list_head all_drivers; | 230 | struct list_head all_drivers; |
| 206 | 231 | ||
| @@ -239,6 +264,7 @@ static struct { | |||
| 239 | u32 bright_16levels:1; | 264 | u32 bright_16levels:1; |
| 240 | u32 bright_acpimode:1; | 265 | u32 bright_acpimode:1; |
| 241 | u32 wan:1; | 266 | u32 wan:1; |
| 267 | u32 uwb:1; | ||
| 242 | u32 fan_ctrl_status_undef:1; | 268 | u32 fan_ctrl_status_undef:1; |
| 243 | u32 input_device_registered:1; | 269 | u32 input_device_registered:1; |
| 244 | u32 platform_drv_registered:1; | 270 | u32 platform_drv_registered:1; |
| @@ -288,6 +314,18 @@ struct tpacpi_led_classdev { | |||
| 288 | unsigned int led; | 314 | unsigned int led; |
| 289 | }; | 315 | }; |
| 290 | 316 | ||
| 317 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | ||
| 318 | static int dbg_wlswemul; | ||
| 319 | static int tpacpi_wlsw_emulstate; | ||
| 320 | static int dbg_bluetoothemul; | ||
| 321 | static int tpacpi_bluetooth_emulstate; | ||
| 322 | static int dbg_wwanemul; | ||
| 323 | static int tpacpi_wwan_emulstate; | ||
| 324 | static int dbg_uwbemul; | ||
| 325 | static int tpacpi_uwb_emulstate; | ||
| 326 | #endif | ||
| 327 | |||
| 328 | |||
| 291 | /**************************************************************************** | 329 | /**************************************************************************** |
| 292 | **************************************************************************** | 330 | **************************************************************************** |
| 293 | * | 331 | * |
| @@ -728,6 +766,18 @@ static int tpacpi_resume_handler(struct platform_device *pdev) | |||
| 728 | return 0; | 766 | return 0; |
| 729 | } | 767 | } |
| 730 | 768 | ||
| 769 | static void tpacpi_shutdown_handler(struct platform_device *pdev) | ||
| 770 | { | ||
| 771 | struct ibm_struct *ibm, *itmp; | ||
| 772 | |||
| 773 | list_for_each_entry_safe(ibm, itmp, | ||
| 774 | &tpacpi_all_drivers, | ||
| 775 | all_drivers) { | ||
| 776 | if (ibm->shutdown) | ||
| 777 | (ibm->shutdown)(); | ||
| 778 | } | ||
| 779 | } | ||
| 780 | |||
| 731 | static struct platform_driver tpacpi_pdriver = { | 781 | static struct platform_driver tpacpi_pdriver = { |
| 732 | .driver = { | 782 | .driver = { |
| 733 | .name = TPACPI_DRVR_NAME, | 783 | .name = TPACPI_DRVR_NAME, |
| @@ -735,6 +785,7 @@ static struct platform_driver tpacpi_pdriver = { | |||
| 735 | }, | 785 | }, |
| 736 | .suspend = tpacpi_suspend_handler, | 786 | .suspend = tpacpi_suspend_handler, |
| 737 | .resume = tpacpi_resume_handler, | 787 | .resume = tpacpi_resume_handler, |
| 788 | .shutdown = tpacpi_shutdown_handler, | ||
| 738 | }; | 789 | }; |
| 739 | 790 | ||
| 740 | static struct platform_driver tpacpi_hwmon_pdriver = { | 791 | static struct platform_driver tpacpi_hwmon_pdriver = { |
| @@ -922,11 +973,27 @@ static int __init tpacpi_new_rfkill(const unsigned int id, | |||
| 922 | struct rfkill **rfk, | 973 | struct rfkill **rfk, |
| 923 | const enum rfkill_type rfktype, | 974 | const enum rfkill_type rfktype, |
| 924 | const char *name, | 975 | const char *name, |
| 976 | const bool set_default, | ||
| 925 | int (*toggle_radio)(void *, enum rfkill_state), | 977 | int (*toggle_radio)(void *, enum rfkill_state), |
| 926 | int (*get_state)(void *, enum rfkill_state *)) | 978 | int (*get_state)(void *, enum rfkill_state *)) |
| 927 | { | 979 | { |
| 928 | int res; | 980 | int res; |
| 929 | enum rfkill_state initial_state; | 981 | enum rfkill_state initial_state = RFKILL_STATE_SOFT_BLOCKED; |
| 982 | |||
| 983 | res = get_state(NULL, &initial_state); | ||
| 984 | if (res < 0) { | ||
| 985 | printk(TPACPI_ERR | ||
| 986 | "failed to read initial state for %s, error %d; " | ||
| 987 | "will turn radio off\n", name, res); | ||
| 988 | } else if (set_default) { | ||
| 989 | /* try to set the initial state as the default for the rfkill | ||
| 990 | * type, since we ask the firmware to preserve it across S5 in | ||
| 991 | * NVRAM */ | ||
| 992 | rfkill_set_default(rfktype, | ||
| 993 | (initial_state == RFKILL_STATE_UNBLOCKED) ? | ||
| 994 | RFKILL_STATE_UNBLOCKED : | ||
| 995 | RFKILL_STATE_SOFT_BLOCKED); | ||
| 996 | } | ||
| 930 | 997 | ||
| 931 | *rfk = rfkill_allocate(&tpacpi_pdev->dev, rfktype); | 998 | *rfk = rfkill_allocate(&tpacpi_pdev->dev, rfktype); |
| 932 | if (!*rfk) { | 999 | if (!*rfk) { |
| @@ -938,9 +1005,7 @@ static int __init tpacpi_new_rfkill(const unsigned int id, | |||
| 938 | (*rfk)->name = name; | 1005 | (*rfk)->name = name; |
| 939 | (*rfk)->get_state = get_state; | 1006 | (*rfk)->get_state = get_state; |
| 940 | (*rfk)->toggle_radio = toggle_radio; | 1007 | (*rfk)->toggle_radio = toggle_radio; |
| 941 | 1008 | (*rfk)->state = initial_state; | |
| 942 | if (!get_state(NULL, &initial_state)) | ||
| 943 | (*rfk)->state = initial_state; | ||
| 944 | 1009 | ||
| 945 | res = rfkill_register(*rfk); | 1010 | res = rfkill_register(*rfk); |
| 946 | if (res < 0) { | 1011 | if (res < 0) { |
| @@ -1006,6 +1071,119 @@ static DRIVER_ATTR(version, S_IRUGO, | |||
| 1006 | 1071 | ||
| 1007 | /* --------------------------------------------------------------------- */ | 1072 | /* --------------------------------------------------------------------- */ |
| 1008 | 1073 | ||
| 1074 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | ||
| 1075 | |||
| 1076 | static void tpacpi_send_radiosw_update(void); | ||
| 1077 | |||
| 1078 | /* wlsw_emulstate ------------------------------------------------------ */ | ||
| 1079 | static ssize_t tpacpi_driver_wlsw_emulstate_show(struct device_driver *drv, | ||
| 1080 | char *buf) | ||
| 1081 | { | ||
| 1082 | return snprintf(buf, PAGE_SIZE, "%d\n", !!tpacpi_wlsw_emulstate); | ||
| 1083 | } | ||
| 1084 | |||
| 1085 | static ssize_t tpacpi_driver_wlsw_emulstate_store(struct device_driver *drv, | ||
| 1086 | const char *buf, size_t count) | ||
| 1087 | { | ||
| 1088 | unsigned long t; | ||
| 1089 | |||
| 1090 | if (parse_strtoul(buf, 1, &t)) | ||
| 1091 | return -EINVAL; | ||
| 1092 | |||
| 1093 | if (tpacpi_wlsw_emulstate != t) { | ||
| 1094 | tpacpi_wlsw_emulstate = !!t; | ||
| 1095 | tpacpi_send_radiosw_update(); | ||
| 1096 | } else | ||
| 1097 | tpacpi_wlsw_emulstate = !!t; | ||
| 1098 | |||
| 1099 | return count; | ||
| 1100 | } | ||
| 1101 | |||
| 1102 | static DRIVER_ATTR(wlsw_emulstate, S_IWUSR | S_IRUGO, | ||
| 1103 | tpacpi_driver_wlsw_emulstate_show, | ||
| 1104 | tpacpi_driver_wlsw_emulstate_store); | ||
| 1105 | |||
| 1106 | /* bluetooth_emulstate ------------------------------------------------- */ | ||
| 1107 | static ssize_t tpacpi_driver_bluetooth_emulstate_show( | ||
| 1108 | struct device_driver *drv, | ||
| 1109 | char *buf) | ||
| 1110 | { | ||
| 1111 | return snprintf(buf, PAGE_SIZE, "%d\n", !!tpacpi_bluetooth_emulstate); | ||
| 1112 | } | ||
| 1113 | |||
| 1114 | static ssize_t tpacpi_driver_bluetooth_emulstate_store( | ||
| 1115 | struct device_driver *drv, | ||
| 1116 | const char *buf, size_t count) | ||
| 1117 | { | ||
| 1118 | unsigned long t; | ||
| 1119 | |||
| 1120 | if (parse_strtoul(buf, 1, &t)) | ||
| 1121 | return -EINVAL; | ||
| 1122 | |||
| 1123 | tpacpi_bluetooth_emulstate = !!t; | ||
| 1124 | |||
| 1125 | return count; | ||
| 1126 | } | ||
| 1127 | |||
| 1128 | static DRIVER_ATTR(bluetooth_emulstate, S_IWUSR | S_IRUGO, | ||
| 1129 | tpacpi_driver_bluetooth_emulstate_show, | ||
| 1130 | tpacpi_driver_bluetooth_emulstate_store); | ||
| 1131 | |||
| 1132 | /* wwan_emulstate ------------------------------------------------- */ | ||
| 1133 | static ssize_t tpacpi_driver_wwan_emulstate_show( | ||
| 1134 | struct device_driver *drv, | ||
| 1135 | char *buf) | ||
| 1136 | { | ||
| 1137 | return snprintf(buf, PAGE_SIZE, "%d\n", !!tpacpi_wwan_emulstate); | ||
| 1138 | } | ||
| 1139 | |||
| 1140 | static ssize_t tpacpi_driver_wwan_emulstate_store( | ||
| 1141 | struct device_driver *drv, | ||
| 1142 | const char *buf, size_t count) | ||
| 1143 | { | ||
| 1144 | unsigned long t; | ||
| 1145 | |||
| 1146 | if (parse_strtoul(buf, 1, &t)) | ||
| 1147 | return -EINVAL; | ||
| 1148 | |||
| 1149 | tpacpi_wwan_emulstate = !!t; | ||
| 1150 | |||
| 1151 | return count; | ||
| 1152 | } | ||
| 1153 | |||
| 1154 | static DRIVER_ATTR(wwan_emulstate, S_IWUSR | S_IRUGO, | ||
| 1155 | tpacpi_driver_wwan_emulstate_show, | ||
| 1156 | tpacpi_driver_wwan_emulstate_store); | ||
| 1157 | |||
| 1158 | /* uwb_emulstate ------------------------------------------------- */ | ||
| 1159 | static ssize_t tpacpi_driver_uwb_emulstate_show( | ||
| 1160 | struct device_driver *drv, | ||
| 1161 | char *buf) | ||
| 1162 | { | ||
| 1163 | return snprintf(buf, PAGE_SIZE, "%d\n", !!tpacpi_uwb_emulstate); | ||
| 1164 | } | ||
| 1165 | |||
| 1166 | static ssize_t tpacpi_driver_uwb_emulstate_store( | ||
| 1167 | struct device_driver *drv, | ||
| 1168 | const char *buf, size_t count) | ||
| 1169 | { | ||
| 1170 | unsigned long t; | ||
| 1171 | |||
| 1172 | if (parse_strtoul(buf, 1, &t)) | ||
| 1173 | return -EINVAL; | ||
| 1174 | |||
| 1175 | tpacpi_uwb_emulstate = !!t; | ||
| 1176 | |||
| 1177 | return count; | ||
| 1178 | } | ||
| 1179 | |||
| 1180 | static DRIVER_ATTR(uwb_emulstate, S_IWUSR | S_IRUGO, | ||
| 1181 | tpacpi_driver_uwb_emulstate_show, | ||
| 1182 | tpacpi_driver_uwb_emulstate_store); | ||
| 1183 | #endif | ||
| 1184 | |||
| 1185 | /* --------------------------------------------------------------------- */ | ||
| 1186 | |||
| 1009 | static struct driver_attribute *tpacpi_driver_attributes[] = { | 1187 | static struct driver_attribute *tpacpi_driver_attributes[] = { |
| 1010 | &driver_attr_debug_level, &driver_attr_version, | 1188 | &driver_attr_debug_level, &driver_attr_version, |
| 1011 | &driver_attr_interface_version, | 1189 | &driver_attr_interface_version, |
| @@ -1022,6 +1200,17 @@ static int __init tpacpi_create_driver_attributes(struct device_driver *drv) | |||
| 1022 | i++; | 1200 | i++; |
| 1023 | } | 1201 | } |
| 1024 | 1202 | ||
| 1203 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | ||
| 1204 | if (!res && dbg_wlswemul) | ||
| 1205 | res = driver_create_file(drv, &driver_attr_wlsw_emulstate); | ||
| 1206 | if (!res && dbg_bluetoothemul) | ||
| 1207 | res = driver_create_file(drv, &driver_attr_bluetooth_emulstate); | ||
| 1208 | if (!res && dbg_wwanemul) | ||
| 1209 | res = driver_create_file(drv, &driver_attr_wwan_emulstate); | ||
| 1210 | if (!res && dbg_uwbemul) | ||
| 1211 | res = driver_create_file(drv, &driver_attr_uwb_emulstate); | ||
| 1212 | #endif | ||
| 1213 | |||
| 1025 | return res; | 1214 | return res; |
| 1026 | } | 1215 | } |
| 1027 | 1216 | ||
| @@ -1031,6 +1220,13 @@ static void tpacpi_remove_driver_attributes(struct device_driver *drv) | |||
| 1031 | 1220 | ||
| 1032 | for (i = 0; i < ARRAY_SIZE(tpacpi_driver_attributes); i++) | 1221 | for (i = 0; i < ARRAY_SIZE(tpacpi_driver_attributes); i++) |
| 1033 | driver_remove_file(drv, tpacpi_driver_attributes[i]); | 1222 | driver_remove_file(drv, tpacpi_driver_attributes[i]); |
| 1223 | |||
| 1224 | #ifdef THINKPAD_ACPI_DEBUGFACILITIES | ||
| 1225 | driver_remove_file(drv, &driver_attr_wlsw_emulstate); | ||
| 1226 | driver_remove_file(drv, &driver_attr_bluetooth_emulstate); | ||
| 1227 | driver_remove_file(drv, &driver_attr_wwan_emulstate); | ||
| 1228 | driver_remove_file(drv, &driver_attr_uwb_emulstate); | ||
| 1229 | #endif | ||
| 1034 | } | 1230 | } |
| 1035 | 1231 | ||
| 1036 | /**************************************************************************** | 1232 | /**************************************************************************** |
| @@ -1216,6 +1412,12 @@ static struct attribute_set *hotkey_dev_attributes; | |||
| 1216 | 1412 | ||
| 1217 | static int hotkey_get_wlsw(int *status) | 1413 | static int hotkey_get_wlsw(int *status) |
| 1218 | { | 1414 | { |
| 1415 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | ||
| 1416 | if (dbg_wlswemul) { | ||
| 1417 | *status = !!tpacpi_wlsw_emulstate; | ||
| 1418 | return 0; | ||
| 1419 | } | ||
| 1420 | #endif | ||
| 1219 | if (!acpi_evalf(hkey_handle, status, "WLSW", "d")) | 1421 | if (!acpi_evalf(hkey_handle, status, "WLSW", "d")) |
| 1220 | return -EIO; | 1422 | return -EIO; |
| 1221 | return 0; | 1423 | return 0; |
| @@ -1678,7 +1880,7 @@ static ssize_t hotkey_mask_show(struct device *dev, | |||
| 1678 | { | 1880 | { |
| 1679 | int res; | 1881 | int res; |
| 1680 | 1882 | ||
| 1681 | if (mutex_lock_interruptible(&hotkey_mutex)) | 1883 | if (mutex_lock_killable(&hotkey_mutex)) |
| 1682 | return -ERESTARTSYS; | 1884 | return -ERESTARTSYS; |
| 1683 | res = hotkey_mask_get(); | 1885 | res = hotkey_mask_get(); |
| 1684 | mutex_unlock(&hotkey_mutex); | 1886 | mutex_unlock(&hotkey_mutex); |
| @@ -1697,7 +1899,7 @@ static ssize_t hotkey_mask_store(struct device *dev, | |||
| 1697 | if (parse_strtoul(buf, 0xffffffffUL, &t)) | 1899 | if (parse_strtoul(buf, 0xffffffffUL, &t)) |
| 1698 | return -EINVAL; | 1900 | return -EINVAL; |
| 1699 | 1901 | ||
| 1700 | if (mutex_lock_interruptible(&hotkey_mutex)) | 1902 | if (mutex_lock_killable(&hotkey_mutex)) |
| 1701 | return -ERESTARTSYS; | 1903 | return -ERESTARTSYS; |
| 1702 | 1904 | ||
| 1703 | res = hotkey_mask_set(t); | 1905 | res = hotkey_mask_set(t); |
| @@ -1783,7 +1985,7 @@ static ssize_t hotkey_source_mask_store(struct device *dev, | |||
| 1783 | ((t & ~TPACPI_HKEY_NVRAM_KNOWN_MASK) != 0)) | 1985 | ((t & ~TPACPI_HKEY_NVRAM_KNOWN_MASK) != 0)) |
| 1784 | return -EINVAL; | 1986 | return -EINVAL; |
| 1785 | 1987 | ||
| 1786 | if (mutex_lock_interruptible(&hotkey_mutex)) | 1988 | if (mutex_lock_killable(&hotkey_mutex)) |
| 1787 | return -ERESTARTSYS; | 1989 | return -ERESTARTSYS; |
| 1788 | 1990 | ||
| 1789 | HOTKEY_CONFIG_CRITICAL_START | 1991 | HOTKEY_CONFIG_CRITICAL_START |
| @@ -1818,7 +2020,7 @@ static ssize_t hotkey_poll_freq_store(struct device *dev, | |||
| 1818 | if (parse_strtoul(buf, 25, &t)) | 2020 | if (parse_strtoul(buf, 25, &t)) |
| 1819 | return -EINVAL; | 2021 | return -EINVAL; |
| 1820 | 2022 | ||
| 1821 | if (mutex_lock_interruptible(&hotkey_mutex)) | 2023 | if (mutex_lock_killable(&hotkey_mutex)) |
| 1822 | return -ERESTARTSYS; | 2024 | return -ERESTARTSYS; |
| 1823 | 2025 | ||
| 1824 | hotkey_poll_freq = t; | 2026 | hotkey_poll_freq = t; |
| @@ -1958,6 +2160,7 @@ static struct attribute *hotkey_mask_attributes[] __initdata = { | |||
| 1958 | 2160 | ||
| 1959 | static void bluetooth_update_rfk(void); | 2161 | static void bluetooth_update_rfk(void); |
| 1960 | static void wan_update_rfk(void); | 2162 | static void wan_update_rfk(void); |
| 2163 | static void uwb_update_rfk(void); | ||
| 1961 | static void tpacpi_send_radiosw_update(void) | 2164 | static void tpacpi_send_radiosw_update(void) |
| 1962 | { | 2165 | { |
| 1963 | int wlsw; | 2166 | int wlsw; |
| @@ -1967,6 +2170,8 @@ static void tpacpi_send_radiosw_update(void) | |||
| 1967 | bluetooth_update_rfk(); | 2170 | bluetooth_update_rfk(); |
| 1968 | if (tp_features.wan) | 2171 | if (tp_features.wan) |
| 1969 | wan_update_rfk(); | 2172 | wan_update_rfk(); |
| 2173 | if (tp_features.uwb) | ||
| 2174 | uwb_update_rfk(); | ||
| 1970 | 2175 | ||
| 1971 | if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&wlsw)) { | 2176 | if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&wlsw)) { |
| 1972 | mutex_lock(&tpacpi_inputdev_send_mutex); | 2177 | mutex_lock(&tpacpi_inputdev_send_mutex); |
| @@ -2222,6 +2427,13 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
| 2222 | hotkey_source_mask, hotkey_poll_freq); | 2427 | hotkey_source_mask, hotkey_poll_freq); |
| 2223 | #endif | 2428 | #endif |
| 2224 | 2429 | ||
| 2430 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | ||
| 2431 | if (dbg_wlswemul) { | ||
| 2432 | tp_features.hotkey_wlsw = 1; | ||
| 2433 | printk(TPACPI_INFO | ||
| 2434 | "radio switch emulation enabled\n"); | ||
| 2435 | } else | ||
| 2436 | #endif | ||
| 2225 | /* Not all thinkpads have a hardware radio switch */ | 2437 | /* Not all thinkpads have a hardware radio switch */ |
| 2226 | if (acpi_evalf(hkey_handle, &status, "WLSW", "qd")) { | 2438 | if (acpi_evalf(hkey_handle, &status, "WLSW", "qd")) { |
| 2227 | tp_features.hotkey_wlsw = 1; | 2439 | tp_features.hotkey_wlsw = 1; |
| @@ -2361,13 +2573,154 @@ err_exit: | |||
| 2361 | return (res < 0)? res : 1; | 2573 | return (res < 0)? res : 1; |
| 2362 | } | 2574 | } |
| 2363 | 2575 | ||
| 2576 | static bool hotkey_notify_hotkey(const u32 hkey, | ||
| 2577 | bool *send_acpi_ev, | ||
| 2578 | bool *ignore_acpi_ev) | ||
| 2579 | { | ||
| 2580 | /* 0x1000-0x1FFF: key presses */ | ||
| 2581 | unsigned int scancode = hkey & 0xfff; | ||
| 2582 | *send_acpi_ev = true; | ||
| 2583 | *ignore_acpi_ev = false; | ||
| 2584 | |||
| 2585 | if (scancode > 0 && scancode < 0x21) { | ||
| 2586 | scancode--; | ||
| 2587 | if (!(hotkey_source_mask & (1 << scancode))) { | ||
| 2588 | tpacpi_input_send_key(scancode); | ||
| 2589 | *send_acpi_ev = false; | ||
| 2590 | } else { | ||
| 2591 | *ignore_acpi_ev = true; | ||
| 2592 | } | ||
| 2593 | return true; | ||
| 2594 | } | ||
| 2595 | return false; | ||
| 2596 | } | ||
| 2597 | |||
| 2598 | static bool hotkey_notify_wakeup(const u32 hkey, | ||
| 2599 | bool *send_acpi_ev, | ||
| 2600 | bool *ignore_acpi_ev) | ||
| 2601 | { | ||
| 2602 | /* 0x2000-0x2FFF: Wakeup reason */ | ||
| 2603 | *send_acpi_ev = true; | ||
| 2604 | *ignore_acpi_ev = false; | ||
| 2605 | |||
| 2606 | switch (hkey) { | ||
| 2607 | case 0x2304: /* suspend, undock */ | ||
| 2608 | case 0x2404: /* hibernation, undock */ | ||
| 2609 | hotkey_wakeup_reason = TP_ACPI_WAKEUP_UNDOCK; | ||
| 2610 | *ignore_acpi_ev = true; | ||
| 2611 | break; | ||
| 2612 | |||
| 2613 | case 0x2305: /* suspend, bay eject */ | ||
| 2614 | case 0x2405: /* hibernation, bay eject */ | ||
| 2615 | hotkey_wakeup_reason = TP_ACPI_WAKEUP_BAYEJ; | ||
| 2616 | *ignore_acpi_ev = true; | ||
| 2617 | break; | ||
| 2618 | |||
| 2619 | case 0x2313: /* Battery on critical low level (S3) */ | ||
| 2620 | case 0x2413: /* Battery on critical low level (S4) */ | ||
| 2621 | printk(TPACPI_ALERT | ||
| 2622 | "EMERGENCY WAKEUP: battery almost empty\n"); | ||
| 2623 | /* how to auto-heal: */ | ||
| 2624 | /* 2313: woke up from S3, go to S4/S5 */ | ||
| 2625 | /* 2413: woke up from S4, go to S5 */ | ||
| 2626 | break; | ||
| 2627 | |||
| 2628 | default: | ||
| 2629 | return false; | ||
| 2630 | } | ||
| 2631 | |||
| 2632 | if (hotkey_wakeup_reason != TP_ACPI_WAKEUP_NONE) { | ||
| 2633 | printk(TPACPI_INFO | ||
| 2634 | "woke up due to a hot-unplug " | ||
| 2635 | "request...\n"); | ||
| 2636 | hotkey_wakeup_reason_notify_change(); | ||
| 2637 | } | ||
| 2638 | return true; | ||
| 2639 | } | ||
| 2640 | |||
| 2641 | static bool hotkey_notify_usrevent(const u32 hkey, | ||
| 2642 | bool *send_acpi_ev, | ||
| 2643 | bool *ignore_acpi_ev) | ||
| 2644 | { | ||
| 2645 | /* 0x5000-0x5FFF: human interface helpers */ | ||
| 2646 | *send_acpi_ev = true; | ||
| 2647 | *ignore_acpi_ev = false; | ||
| 2648 | |||
| 2649 | switch (hkey) { | ||
| 2650 | case 0x5010: /* Lenovo new BIOS: brightness changed */ | ||
| 2651 | case 0x500b: /* X61t: tablet pen inserted into bay */ | ||
| 2652 | case 0x500c: /* X61t: tablet pen removed from bay */ | ||
| 2653 | return true; | ||
| 2654 | |||
| 2655 | case 0x5009: /* X41t-X61t: swivel up (tablet mode) */ | ||
| 2656 | case 0x500a: /* X41t-X61t: swivel down (normal mode) */ | ||
| 2657 | tpacpi_input_send_tabletsw(); | ||
| 2658 | hotkey_tablet_mode_notify_change(); | ||
| 2659 | *send_acpi_ev = false; | ||
| 2660 | return true; | ||
| 2661 | |||
| 2662 | case 0x5001: | ||
| 2663 | case 0x5002: | ||
| 2664 | /* LID switch events. Do not propagate */ | ||
| 2665 | *ignore_acpi_ev = true; | ||
| 2666 | return true; | ||
| 2667 | |||
| 2668 | default: | ||
| 2669 | return false; | ||
| 2670 | } | ||
| 2671 | } | ||
| 2672 | |||
| 2673 | static bool hotkey_notify_thermal(const u32 hkey, | ||
| 2674 | bool *send_acpi_ev, | ||
| 2675 | bool *ignore_acpi_ev) | ||
| 2676 | { | ||
| 2677 | /* 0x6000-0x6FFF: thermal alarms */ | ||
| 2678 | *send_acpi_ev = true; | ||
| 2679 | *ignore_acpi_ev = false; | ||
| 2680 | |||
| 2681 | switch (hkey) { | ||
| 2682 | case 0x6011: | ||
| 2683 | printk(TPACPI_CRIT | ||
| 2684 | "THERMAL ALARM: battery is too hot!\n"); | ||
| 2685 | /* recommended action: warn user through gui */ | ||
| 2686 | return true; | ||
| 2687 | case 0x6012: | ||
| 2688 | printk(TPACPI_ALERT | ||
| 2689 | "THERMAL EMERGENCY: battery is extremely hot!\n"); | ||
| 2690 | /* recommended action: immediate sleep/hibernate */ | ||
| 2691 | return true; | ||
| 2692 | case 0x6021: | ||
| 2693 | printk(TPACPI_CRIT | ||
| 2694 | "THERMAL ALARM: " | ||
| 2695 | "a sensor reports something is too hot!\n"); | ||
| 2696 | /* recommended action: warn user through gui, that */ | ||
| 2697 | /* some internal component is too hot */ | ||
| 2698 | return true; | ||
| 2699 | case 0x6022: | ||
| 2700 | printk(TPACPI_ALERT | ||
| 2701 | "THERMAL EMERGENCY: " | ||
| 2702 | "a sensor reports something is extremely hot!\n"); | ||
| 2703 | /* recommended action: immediate sleep/hibernate */ | ||
| 2704 | return true; | ||
| 2705 | case 0x6030: | ||
| 2706 | printk(TPACPI_INFO | ||
| 2707 | "EC reports that Thermal Table has changed\n"); | ||
| 2708 | /* recommended action: do nothing, we don't have | ||
| 2709 | * Lenovo ATM information */ | ||
| 2710 | return true; | ||
| 2711 | default: | ||
| 2712 | printk(TPACPI_ALERT | ||
| 2713 | "THERMAL ALERT: unknown thermal alarm received\n"); | ||
| 2714 | return false; | ||
| 2715 | } | ||
| 2716 | } | ||
| 2717 | |||
| 2364 | static void hotkey_notify(struct ibm_struct *ibm, u32 event) | 2718 | static void hotkey_notify(struct ibm_struct *ibm, u32 event) |
| 2365 | { | 2719 | { |
| 2366 | u32 hkey; | 2720 | u32 hkey; |
| 2367 | unsigned int scancode; | 2721 | bool send_acpi_ev; |
| 2368 | int send_acpi_ev; | 2722 | bool ignore_acpi_ev; |
| 2369 | int ignore_acpi_ev; | 2723 | bool known_ev; |
| 2370 | int unk_ev; | ||
| 2371 | 2724 | ||
| 2372 | if (event != 0x80) { | 2725 | if (event != 0x80) { |
| 2373 | printk(TPACPI_ERR | 2726 | printk(TPACPI_ERR |
| @@ -2375,7 +2728,7 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event) | |||
| 2375 | /* forward it to userspace, maybe it knows how to handle it */ | 2728 | /* forward it to userspace, maybe it knows how to handle it */ |
| 2376 | acpi_bus_generate_netlink_event( | 2729 | acpi_bus_generate_netlink_event( |
| 2377 | ibm->acpi->device->pnp.device_class, | 2730 | ibm->acpi->device->pnp.device_class, |
| 2378 | ibm->acpi->device->dev.bus_id, | 2731 | dev_name(&ibm->acpi->device->dev), |
| 2379 | event, 0); | 2732 | event, 0); |
| 2380 | return; | 2733 | return; |
| 2381 | } | 2734 | } |
| @@ -2391,107 +2744,72 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event) | |||
| 2391 | return; | 2744 | return; |
| 2392 | } | 2745 | } |
| 2393 | 2746 | ||
| 2394 | send_acpi_ev = 1; | 2747 | send_acpi_ev = true; |
| 2395 | ignore_acpi_ev = 0; | 2748 | ignore_acpi_ev = false; |
| 2396 | unk_ev = 0; | ||
| 2397 | 2749 | ||
| 2398 | switch (hkey >> 12) { | 2750 | switch (hkey >> 12) { |
| 2399 | case 1: | 2751 | case 1: |
| 2400 | /* 0x1000-0x1FFF: key presses */ | 2752 | /* 0x1000-0x1FFF: key presses */ |
| 2401 | scancode = hkey & 0xfff; | 2753 | known_ev = hotkey_notify_hotkey(hkey, &send_acpi_ev, |
| 2402 | if (scancode > 0 && scancode < 0x21) { | 2754 | &ignore_acpi_ev); |
| 2403 | scancode--; | ||
| 2404 | if (!(hotkey_source_mask & (1 << scancode))) { | ||
| 2405 | tpacpi_input_send_key(scancode); | ||
| 2406 | send_acpi_ev = 0; | ||
| 2407 | } else { | ||
| 2408 | ignore_acpi_ev = 1; | ||
| 2409 | } | ||
| 2410 | } else { | ||
| 2411 | unk_ev = 1; | ||
| 2412 | } | ||
| 2413 | break; | 2755 | break; |
| 2414 | case 2: | 2756 | case 2: |
| 2415 | /* Wakeup reason */ | 2757 | /* 0x2000-0x2FFF: Wakeup reason */ |
| 2416 | switch (hkey) { | 2758 | known_ev = hotkey_notify_wakeup(hkey, &send_acpi_ev, |
| 2417 | case 0x2304: /* suspend, undock */ | 2759 | &ignore_acpi_ev); |
| 2418 | case 0x2404: /* hibernation, undock */ | ||
| 2419 | hotkey_wakeup_reason = TP_ACPI_WAKEUP_UNDOCK; | ||
| 2420 | ignore_acpi_ev = 1; | ||
| 2421 | break; | ||
| 2422 | case 0x2305: /* suspend, bay eject */ | ||
| 2423 | case 0x2405: /* hibernation, bay eject */ | ||
| 2424 | hotkey_wakeup_reason = TP_ACPI_WAKEUP_BAYEJ; | ||
| 2425 | ignore_acpi_ev = 1; | ||
| 2426 | break; | ||
| 2427 | default: | ||
| 2428 | unk_ev = 1; | ||
| 2429 | } | ||
| 2430 | if (hotkey_wakeup_reason != TP_ACPI_WAKEUP_NONE) { | ||
| 2431 | printk(TPACPI_INFO | ||
| 2432 | "woke up due to a hot-unplug " | ||
| 2433 | "request...\n"); | ||
| 2434 | hotkey_wakeup_reason_notify_change(); | ||
| 2435 | } | ||
| 2436 | break; | 2760 | break; |
| 2437 | case 3: | 2761 | case 3: |
| 2438 | /* bay-related wakeups */ | 2762 | /* 0x3000-0x3FFF: bay-related wakeups */ |
| 2439 | if (hkey == 0x3003) { | 2763 | if (hkey == 0x3003) { |
| 2440 | hotkey_autosleep_ack = 1; | 2764 | hotkey_autosleep_ack = 1; |
| 2441 | printk(TPACPI_INFO | 2765 | printk(TPACPI_INFO |
| 2442 | "bay ejected\n"); | 2766 | "bay ejected\n"); |
| 2443 | hotkey_wakeup_hotunplug_complete_notify_change(); | 2767 | hotkey_wakeup_hotunplug_complete_notify_change(); |
| 2768 | known_ev = true; | ||
| 2444 | } else { | 2769 | } else { |
| 2445 | unk_ev = 1; | 2770 | known_ev = false; |
| 2446 | } | 2771 | } |
| 2447 | break; | 2772 | break; |
| 2448 | case 4: | 2773 | case 4: |
| 2449 | /* dock-related wakeups */ | 2774 | /* 0x4000-0x4FFF: dock-related wakeups */ |
| 2450 | if (hkey == 0x4003) { | 2775 | if (hkey == 0x4003) { |
| 2451 | hotkey_autosleep_ack = 1; | 2776 | hotkey_autosleep_ack = 1; |
| 2452 | printk(TPACPI_INFO | 2777 | printk(TPACPI_INFO |
| 2453 | "undocked\n"); | 2778 | "undocked\n"); |
| 2454 | hotkey_wakeup_hotunplug_complete_notify_change(); | 2779 | hotkey_wakeup_hotunplug_complete_notify_change(); |
| 2780 | known_ev = true; | ||
| 2455 | } else { | 2781 | } else { |
| 2456 | unk_ev = 1; | 2782 | known_ev = false; |
| 2457 | } | 2783 | } |
| 2458 | break; | 2784 | break; |
| 2459 | case 5: | 2785 | case 5: |
| 2460 | /* 0x5000-0x5FFF: human interface helpers */ | 2786 | /* 0x5000-0x5FFF: human interface helpers */ |
| 2461 | switch (hkey) { | 2787 | known_ev = hotkey_notify_usrevent(hkey, &send_acpi_ev, |
| 2462 | case 0x5010: /* Lenovo new BIOS: brightness changed */ | 2788 | &ignore_acpi_ev); |
| 2463 | case 0x500b: /* X61t: tablet pen inserted into bay */ | 2789 | break; |
| 2464 | case 0x500c: /* X61t: tablet pen removed from bay */ | 2790 | case 6: |
| 2465 | break; | 2791 | /* 0x6000-0x6FFF: thermal alarms */ |
| 2466 | case 0x5009: /* X41t-X61t: swivel up (tablet mode) */ | 2792 | known_ev = hotkey_notify_thermal(hkey, &send_acpi_ev, |
| 2467 | case 0x500a: /* X41t-X61t: swivel down (normal mode) */ | 2793 | &ignore_acpi_ev); |
| 2468 | tpacpi_input_send_tabletsw(); | ||
| 2469 | hotkey_tablet_mode_notify_change(); | ||
| 2470 | send_acpi_ev = 0; | ||
| 2471 | break; | ||
| 2472 | case 0x5001: | ||
| 2473 | case 0x5002: | ||
| 2474 | /* LID switch events. Do not propagate */ | ||
| 2475 | ignore_acpi_ev = 1; | ||
| 2476 | break; | ||
| 2477 | default: | ||
| 2478 | unk_ev = 1; | ||
| 2479 | } | ||
| 2480 | break; | 2794 | break; |
| 2481 | case 7: | 2795 | case 7: |
| 2482 | /* 0x7000-0x7FFF: misc */ | 2796 | /* 0x7000-0x7FFF: misc */ |
| 2483 | if (tp_features.hotkey_wlsw && hkey == 0x7000) { | 2797 | if (tp_features.hotkey_wlsw && hkey == 0x7000) { |
| 2484 | tpacpi_send_radiosw_update(); | 2798 | tpacpi_send_radiosw_update(); |
| 2485 | send_acpi_ev = 0; | 2799 | send_acpi_ev = 0; |
| 2800 | known_ev = true; | ||
| 2486 | break; | 2801 | break; |
| 2487 | } | 2802 | } |
| 2488 | /* fallthrough to default */ | 2803 | /* fallthrough to default */ |
| 2489 | default: | 2804 | default: |
| 2490 | unk_ev = 1; | 2805 | known_ev = false; |
| 2491 | } | 2806 | } |
| 2492 | if (unk_ev) { | 2807 | if (!known_ev) { |
| 2493 | printk(TPACPI_NOTICE | 2808 | printk(TPACPI_NOTICE |
| 2494 | "unhandled HKEY event 0x%04x\n", hkey); | 2809 | "unhandled HKEY event 0x%04x\n", hkey); |
| 2810 | printk(TPACPI_NOTICE | ||
| 2811 | "please report the conditions when this " | ||
| 2812 | "event happened to %s\n", TPACPI_MAIL); | ||
| 2495 | } | 2813 | } |
| 2496 | 2814 | ||
| 2497 | /* Legacy events */ | 2815 | /* Legacy events */ |
| @@ -2505,7 +2823,7 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event) | |||
| 2505 | if (!ignore_acpi_ev && send_acpi_ev) { | 2823 | if (!ignore_acpi_ev && send_acpi_ev) { |
| 2506 | acpi_bus_generate_netlink_event( | 2824 | acpi_bus_generate_netlink_event( |
| 2507 | ibm->acpi->device->pnp.device_class, | 2825 | ibm->acpi->device->pnp.device_class, |
| 2508 | ibm->acpi->device->dev.bus_id, | 2826 | dev_name(&ibm->acpi->device->dev), |
| 2509 | event, hkey); | 2827 | event, hkey); |
| 2510 | } | 2828 | } |
| 2511 | } | 2829 | } |
| @@ -2544,7 +2862,7 @@ static int hotkey_read(char *p) | |||
| 2544 | return len; | 2862 | return len; |
| 2545 | } | 2863 | } |
| 2546 | 2864 | ||
| 2547 | if (mutex_lock_interruptible(&hotkey_mutex)) | 2865 | if (mutex_lock_killable(&hotkey_mutex)) |
| 2548 | return -ERESTARTSYS; | 2866 | return -ERESTARTSYS; |
| 2549 | res = hotkey_status_get(&status); | 2867 | res = hotkey_status_get(&status); |
| 2550 | if (!res) | 2868 | if (!res) |
| @@ -2575,7 +2893,7 @@ static int hotkey_write(char *buf) | |||
| 2575 | if (!tp_features.hotkey) | 2893 | if (!tp_features.hotkey) |
| 2576 | return -ENODEV; | 2894 | return -ENODEV; |
| 2577 | 2895 | ||
| 2578 | if (mutex_lock_interruptible(&hotkey_mutex)) | 2896 | if (mutex_lock_killable(&hotkey_mutex)) |
| 2579 | return -ERESTARTSYS; | 2897 | return -ERESTARTSYS; |
| 2580 | 2898 | ||
| 2581 | status = -1; | 2899 | status = -1; |
| @@ -2640,11 +2958,28 @@ enum { | |||
| 2640 | /* ACPI GBDC/SBDC bits */ | 2958 | /* ACPI GBDC/SBDC bits */ |
| 2641 | TP_ACPI_BLUETOOTH_HWPRESENT = 0x01, /* Bluetooth hw available */ | 2959 | TP_ACPI_BLUETOOTH_HWPRESENT = 0x01, /* Bluetooth hw available */ |
| 2642 | TP_ACPI_BLUETOOTH_RADIOSSW = 0x02, /* Bluetooth radio enabled */ | 2960 | TP_ACPI_BLUETOOTH_RADIOSSW = 0x02, /* Bluetooth radio enabled */ |
| 2643 | TP_ACPI_BLUETOOTH_UNK = 0x04, /* unknown function */ | 2961 | TP_ACPI_BLUETOOTH_RESUMECTRL = 0x04, /* Bluetooth state at resume: |
| 2962 | off / last state */ | ||
| 2963 | }; | ||
| 2964 | |||
| 2965 | enum { | ||
| 2966 | /* ACPI \BLTH commands */ | ||
| 2967 | TP_ACPI_BLTH_GET_ULTRAPORT_ID = 0x00, /* Get Ultraport BT ID */ | ||
| 2968 | TP_ACPI_BLTH_GET_PWR_ON_RESUME = 0x01, /* Get power-on-resume state */ | ||
| 2969 | TP_ACPI_BLTH_PWR_ON_ON_RESUME = 0x02, /* Resume powered on */ | ||
| 2970 | TP_ACPI_BLTH_PWR_OFF_ON_RESUME = 0x03, /* Resume powered off */ | ||
| 2971 | TP_ACPI_BLTH_SAVE_STATE = 0x05, /* Save state for S4/S5 */ | ||
| 2644 | }; | 2972 | }; |
| 2645 | 2973 | ||
| 2646 | static struct rfkill *tpacpi_bluetooth_rfkill; | 2974 | static struct rfkill *tpacpi_bluetooth_rfkill; |
| 2647 | 2975 | ||
| 2976 | static void bluetooth_suspend(pm_message_t state) | ||
| 2977 | { | ||
| 2978 | /* Try to make sure radio will resume powered off */ | ||
| 2979 | acpi_evalf(NULL, NULL, "\\BLTH", "vd", | ||
| 2980 | TP_ACPI_BLTH_PWR_OFF_ON_RESUME); | ||
| 2981 | } | ||
| 2982 | |||
| 2648 | static int bluetooth_get_radiosw(void) | 2983 | static int bluetooth_get_radiosw(void) |
| 2649 | { | 2984 | { |
| 2650 | int status; | 2985 | int status; |
| @@ -2656,6 +2991,12 @@ static int bluetooth_get_radiosw(void) | |||
| 2656 | if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status) | 2991 | if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status) |
| 2657 | return RFKILL_STATE_HARD_BLOCKED; | 2992 | return RFKILL_STATE_HARD_BLOCKED; |
| 2658 | 2993 | ||
| 2994 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | ||
| 2995 | if (dbg_bluetoothemul) | ||
| 2996 | return (tpacpi_bluetooth_emulstate) ? | ||
| 2997 | RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED; | ||
| 2998 | #endif | ||
| 2999 | |||
| 2659 | if (!acpi_evalf(hkey_handle, &status, "GBDC", "d")) | 3000 | if (!acpi_evalf(hkey_handle, &status, "GBDC", "d")) |
| 2660 | return -EIO; | 3001 | return -EIO; |
| 2661 | 3002 | ||
| @@ -2689,12 +3030,20 @@ static int bluetooth_set_radiosw(int radio_on, int update_rfk) | |||
| 2689 | && radio_on) | 3030 | && radio_on) |
| 2690 | return -EPERM; | 3031 | return -EPERM; |
| 2691 | 3032 | ||
| 2692 | if (!acpi_evalf(hkey_handle, &status, "GBDC", "d")) | 3033 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES |
| 2693 | return -EIO; | 3034 | if (dbg_bluetoothemul) { |
| 3035 | tpacpi_bluetooth_emulstate = !!radio_on; | ||
| 3036 | if (update_rfk) | ||
| 3037 | bluetooth_update_rfk(); | ||
| 3038 | return 0; | ||
| 3039 | } | ||
| 3040 | #endif | ||
| 3041 | |||
| 3042 | /* We make sure to keep TP_ACPI_BLUETOOTH_RESUMECTRL off */ | ||
| 2694 | if (radio_on) | 3043 | if (radio_on) |
| 2695 | status |= TP_ACPI_BLUETOOTH_RADIOSSW; | 3044 | status = TP_ACPI_BLUETOOTH_RADIOSSW; |
| 2696 | else | 3045 | else |
| 2697 | status &= ~TP_ACPI_BLUETOOTH_RADIOSSW; | 3046 | status = 0; |
| 2698 | if (!acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status)) | 3047 | if (!acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status)) |
| 2699 | return -EIO; | 3048 | return -EIO; |
| 2700 | 3049 | ||
| @@ -2765,8 +3114,19 @@ static int tpacpi_bluetooth_rfk_set(void *data, enum rfkill_state state) | |||
| 2765 | return bluetooth_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0); | 3114 | return bluetooth_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0); |
| 2766 | } | 3115 | } |
| 2767 | 3116 | ||
| 3117 | static void bluetooth_shutdown(void) | ||
| 3118 | { | ||
| 3119 | /* Order firmware to save current state to NVRAM */ | ||
| 3120 | if (!acpi_evalf(NULL, NULL, "\\BLTH", "vd", | ||
| 3121 | TP_ACPI_BLTH_SAVE_STATE)) | ||
| 3122 | printk(TPACPI_NOTICE | ||
| 3123 | "failed to save bluetooth state to NVRAM\n"); | ||
| 3124 | } | ||
| 3125 | |||
| 2768 | static void bluetooth_exit(void) | 3126 | static void bluetooth_exit(void) |
| 2769 | { | 3127 | { |
| 3128 | bluetooth_shutdown(); | ||
| 3129 | |||
| 2770 | if (tpacpi_bluetooth_rfkill) | 3130 | if (tpacpi_bluetooth_rfkill) |
| 2771 | rfkill_unregister(tpacpi_bluetooth_rfkill); | 3131 | rfkill_unregister(tpacpi_bluetooth_rfkill); |
| 2772 | 3132 | ||
| @@ -2792,6 +3152,13 @@ static int __init bluetooth_init(struct ibm_init_struct *iibm) | |||
| 2792 | str_supported(tp_features.bluetooth), | 3152 | str_supported(tp_features.bluetooth), |
| 2793 | status); | 3153 | status); |
| 2794 | 3154 | ||
| 3155 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | ||
| 3156 | if (dbg_bluetoothemul) { | ||
| 3157 | tp_features.bluetooth = 1; | ||
| 3158 | printk(TPACPI_INFO | ||
| 3159 | "bluetooth switch emulation enabled\n"); | ||
| 3160 | } else | ||
| 3161 | #endif | ||
| 2795 | if (tp_features.bluetooth && | 3162 | if (tp_features.bluetooth && |
| 2796 | !(status & TP_ACPI_BLUETOOTH_HWPRESENT)) { | 3163 | !(status & TP_ACPI_BLUETOOTH_HWPRESENT)) { |
| 2797 | /* no bluetooth hardware present in system */ | 3164 | /* no bluetooth hardware present in system */ |
| @@ -2812,6 +3179,7 @@ static int __init bluetooth_init(struct ibm_init_struct *iibm) | |||
| 2812 | &tpacpi_bluetooth_rfkill, | 3179 | &tpacpi_bluetooth_rfkill, |
| 2813 | RFKILL_TYPE_BLUETOOTH, | 3180 | RFKILL_TYPE_BLUETOOTH, |
| 2814 | "tpacpi_bluetooth_sw", | 3181 | "tpacpi_bluetooth_sw", |
| 3182 | true, | ||
| 2815 | tpacpi_bluetooth_rfk_set, | 3183 | tpacpi_bluetooth_rfk_set, |
| 2816 | tpacpi_bluetooth_rfk_get); | 3184 | tpacpi_bluetooth_rfk_get); |
| 2817 | if (res) { | 3185 | if (res) { |
| @@ -2864,6 +3232,8 @@ static struct ibm_struct bluetooth_driver_data = { | |||
| 2864 | .read = bluetooth_read, | 3232 | .read = bluetooth_read, |
| 2865 | .write = bluetooth_write, | 3233 | .write = bluetooth_write, |
| 2866 | .exit = bluetooth_exit, | 3234 | .exit = bluetooth_exit, |
| 3235 | .suspend = bluetooth_suspend, | ||
| 3236 | .shutdown = bluetooth_shutdown, | ||
| 2867 | }; | 3237 | }; |
| 2868 | 3238 | ||
| 2869 | /************************************************************************* | 3239 | /************************************************************************* |
| @@ -2874,11 +3244,19 @@ enum { | |||
| 2874 | /* ACPI GWAN/SWAN bits */ | 3244 | /* ACPI GWAN/SWAN bits */ |
| 2875 | TP_ACPI_WANCARD_HWPRESENT = 0x01, /* Wan hw available */ | 3245 | TP_ACPI_WANCARD_HWPRESENT = 0x01, /* Wan hw available */ |
| 2876 | TP_ACPI_WANCARD_RADIOSSW = 0x02, /* Wan radio enabled */ | 3246 | TP_ACPI_WANCARD_RADIOSSW = 0x02, /* Wan radio enabled */ |
| 2877 | TP_ACPI_WANCARD_UNK = 0x04, /* unknown function */ | 3247 | TP_ACPI_WANCARD_RESUMECTRL = 0x04, /* Wan state at resume: |
| 3248 | off / last state */ | ||
| 2878 | }; | 3249 | }; |
| 2879 | 3250 | ||
| 2880 | static struct rfkill *tpacpi_wan_rfkill; | 3251 | static struct rfkill *tpacpi_wan_rfkill; |
| 2881 | 3252 | ||
| 3253 | static void wan_suspend(pm_message_t state) | ||
| 3254 | { | ||
| 3255 | /* Try to make sure radio will resume powered off */ | ||
| 3256 | acpi_evalf(NULL, NULL, "\\WGSV", "qvd", | ||
| 3257 | TP_ACPI_WGSV_PWR_OFF_ON_RESUME); | ||
| 3258 | } | ||
| 3259 | |||
| 2882 | static int wan_get_radiosw(void) | 3260 | static int wan_get_radiosw(void) |
| 2883 | { | 3261 | { |
| 2884 | int status; | 3262 | int status; |
| @@ -2890,6 +3268,12 @@ static int wan_get_radiosw(void) | |||
| 2890 | if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status) | 3268 | if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status) |
| 2891 | return RFKILL_STATE_HARD_BLOCKED; | 3269 | return RFKILL_STATE_HARD_BLOCKED; |
| 2892 | 3270 | ||
| 3271 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | ||
| 3272 | if (dbg_wwanemul) | ||
| 3273 | return (tpacpi_wwan_emulstate) ? | ||
| 3274 | RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED; | ||
| 3275 | #endif | ||
| 3276 | |||
| 2893 | if (!acpi_evalf(hkey_handle, &status, "GWAN", "d")) | 3277 | if (!acpi_evalf(hkey_handle, &status, "GWAN", "d")) |
| 2894 | return -EIO; | 3278 | return -EIO; |
| 2895 | 3279 | ||
| @@ -2923,12 +3307,20 @@ static int wan_set_radiosw(int radio_on, int update_rfk) | |||
| 2923 | && radio_on) | 3307 | && radio_on) |
| 2924 | return -EPERM; | 3308 | return -EPERM; |
| 2925 | 3309 | ||
| 2926 | if (!acpi_evalf(hkey_handle, &status, "GWAN", "d")) | 3310 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES |
| 2927 | return -EIO; | 3311 | if (dbg_wwanemul) { |
| 3312 | tpacpi_wwan_emulstate = !!radio_on; | ||
| 3313 | if (update_rfk) | ||
| 3314 | wan_update_rfk(); | ||
| 3315 | return 0; | ||
| 3316 | } | ||
| 3317 | #endif | ||
| 3318 | |||
| 3319 | /* We make sure to keep TP_ACPI_WANCARD_RESUMECTRL off */ | ||
| 2928 | if (radio_on) | 3320 | if (radio_on) |
| 2929 | status |= TP_ACPI_WANCARD_RADIOSSW; | 3321 | status = TP_ACPI_WANCARD_RADIOSSW; |
| 2930 | else | 3322 | else |
| 2931 | status &= ~TP_ACPI_WANCARD_RADIOSSW; | 3323 | status = 0; |
| 2932 | if (!acpi_evalf(hkey_handle, NULL, "SWAN", "vd", status)) | 3324 | if (!acpi_evalf(hkey_handle, NULL, "SWAN", "vd", status)) |
| 2933 | return -EIO; | 3325 | return -EIO; |
| 2934 | 3326 | ||
| @@ -2999,8 +3391,19 @@ static int tpacpi_wan_rfk_set(void *data, enum rfkill_state state) | |||
| 2999 | return wan_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0); | 3391 | return wan_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0); |
| 3000 | } | 3392 | } |
| 3001 | 3393 | ||
| 3394 | static void wan_shutdown(void) | ||
| 3395 | { | ||
| 3396 | /* Order firmware to save current state to NVRAM */ | ||
| 3397 | if (!acpi_evalf(NULL, NULL, "\\WGSV", "vd", | ||
| 3398 | TP_ACPI_WGSV_SAVE_STATE)) | ||
| 3399 | printk(TPACPI_NOTICE | ||
| 3400 | "failed to save WWAN state to NVRAM\n"); | ||
| 3401 | } | ||
| 3402 | |||
| 3002 | static void wan_exit(void) | 3403 | static void wan_exit(void) |
| 3003 | { | 3404 | { |
| 3405 | wan_shutdown(); | ||
| 3406 | |||
| 3004 | if (tpacpi_wan_rfkill) | 3407 | if (tpacpi_wan_rfkill) |
| 3005 | rfkill_unregister(tpacpi_wan_rfkill); | 3408 | rfkill_unregister(tpacpi_wan_rfkill); |
| 3006 | 3409 | ||
| @@ -3024,6 +3427,13 @@ static int __init wan_init(struct ibm_init_struct *iibm) | |||
| 3024 | str_supported(tp_features.wan), | 3427 | str_supported(tp_features.wan), |
| 3025 | status); | 3428 | status); |
| 3026 | 3429 | ||
| 3430 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | ||
| 3431 | if (dbg_wwanemul) { | ||
| 3432 | tp_features.wan = 1; | ||
| 3433 | printk(TPACPI_INFO | ||
| 3434 | "wwan switch emulation enabled\n"); | ||
| 3435 | } else | ||
| 3436 | #endif | ||
| 3027 | if (tp_features.wan && | 3437 | if (tp_features.wan && |
| 3028 | !(status & TP_ACPI_WANCARD_HWPRESENT)) { | 3438 | !(status & TP_ACPI_WANCARD_HWPRESENT)) { |
| 3029 | /* no wan hardware present in system */ | 3439 | /* no wan hardware present in system */ |
| @@ -3044,6 +3454,7 @@ static int __init wan_init(struct ibm_init_struct *iibm) | |||
| 3044 | &tpacpi_wan_rfkill, | 3454 | &tpacpi_wan_rfkill, |
| 3045 | RFKILL_TYPE_WWAN, | 3455 | RFKILL_TYPE_WWAN, |
| 3046 | "tpacpi_wwan_sw", | 3456 | "tpacpi_wwan_sw", |
| 3457 | true, | ||
| 3047 | tpacpi_wan_rfk_set, | 3458 | tpacpi_wan_rfk_set, |
| 3048 | tpacpi_wan_rfk_get); | 3459 | tpacpi_wan_rfk_get); |
| 3049 | if (res) { | 3460 | if (res) { |
| @@ -3096,6 +3507,164 @@ static struct ibm_struct wan_driver_data = { | |||
| 3096 | .read = wan_read, | 3507 | .read = wan_read, |
| 3097 | .write = wan_write, | 3508 | .write = wan_write, |
| 3098 | .exit = wan_exit, | 3509 | .exit = wan_exit, |
| 3510 | .suspend = wan_suspend, | ||
| 3511 | .shutdown = wan_shutdown, | ||
| 3512 | }; | ||
| 3513 | |||
| 3514 | /************************************************************************* | ||
| 3515 | * UWB subdriver | ||
| 3516 | */ | ||
| 3517 | |||
| 3518 | enum { | ||
| 3519 | /* ACPI GUWB/SUWB bits */ | ||
| 3520 | TP_ACPI_UWB_HWPRESENT = 0x01, /* UWB hw available */ | ||
| 3521 | TP_ACPI_UWB_RADIOSSW = 0x02, /* UWB radio enabled */ | ||
| 3522 | }; | ||
| 3523 | |||
| 3524 | static struct rfkill *tpacpi_uwb_rfkill; | ||
| 3525 | |||
| 3526 | static int uwb_get_radiosw(void) | ||
| 3527 | { | ||
| 3528 | int status; | ||
| 3529 | |||
| 3530 | if (!tp_features.uwb) | ||
| 3531 | return -ENODEV; | ||
| 3532 | |||
| 3533 | /* WLSW overrides UWB in firmware/hardware, reflect that */ | ||
| 3534 | if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status) | ||
| 3535 | return RFKILL_STATE_HARD_BLOCKED; | ||
| 3536 | |||
| 3537 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | ||
| 3538 | if (dbg_uwbemul) | ||
| 3539 | return (tpacpi_uwb_emulstate) ? | ||
| 3540 | RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED; | ||
| 3541 | #endif | ||
| 3542 | |||
| 3543 | if (!acpi_evalf(hkey_handle, &status, "GUWB", "d")) | ||
| 3544 | return -EIO; | ||
| 3545 | |||
| 3546 | return ((status & TP_ACPI_UWB_RADIOSSW) != 0) ? | ||
| 3547 | RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED; | ||
| 3548 | } | ||
| 3549 | |||
| 3550 | static void uwb_update_rfk(void) | ||
| 3551 | { | ||
| 3552 | int status; | ||
| 3553 | |||
| 3554 | if (!tpacpi_uwb_rfkill) | ||
| 3555 | return; | ||
| 3556 | |||
| 3557 | status = uwb_get_radiosw(); | ||
| 3558 | if (status < 0) | ||
| 3559 | return; | ||
| 3560 | rfkill_force_state(tpacpi_uwb_rfkill, status); | ||
| 3561 | } | ||
| 3562 | |||
| 3563 | static int uwb_set_radiosw(int radio_on, int update_rfk) | ||
| 3564 | { | ||
| 3565 | int status; | ||
| 3566 | |||
| 3567 | if (!tp_features.uwb) | ||
| 3568 | return -ENODEV; | ||
| 3569 | |||
| 3570 | /* WLSW overrides UWB in firmware/hardware, but there is no | ||
| 3571 | * reason to risk weird behaviour. */ | ||
| 3572 | if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status | ||
| 3573 | && radio_on) | ||
| 3574 | return -EPERM; | ||
| 3575 | |||
| 3576 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | ||
| 3577 | if (dbg_uwbemul) { | ||
| 3578 | tpacpi_uwb_emulstate = !!radio_on; | ||
| 3579 | if (update_rfk) | ||
| 3580 | uwb_update_rfk(); | ||
| 3581 | return 0; | ||
| 3582 | } | ||
| 3583 | #endif | ||
| 3584 | |||
| 3585 | status = (radio_on) ? TP_ACPI_UWB_RADIOSSW : 0; | ||
| 3586 | if (!acpi_evalf(hkey_handle, NULL, "SUWB", "vd", status)) | ||
| 3587 | return -EIO; | ||
| 3588 | |||
| 3589 | if (update_rfk) | ||
| 3590 | uwb_update_rfk(); | ||
| 3591 | |||
| 3592 | return 0; | ||
| 3593 | } | ||
| 3594 | |||
| 3595 | /* --------------------------------------------------------------------- */ | ||
| 3596 | |||
| 3597 | static int tpacpi_uwb_rfk_get(void *data, enum rfkill_state *state) | ||
| 3598 | { | ||
| 3599 | int uwbs = uwb_get_radiosw(); | ||
| 3600 | |||
| 3601 | if (uwbs < 0) | ||
| 3602 | return uwbs; | ||
| 3603 | |||
| 3604 | *state = uwbs; | ||
| 3605 | return 0; | ||
| 3606 | } | ||
| 3607 | |||
| 3608 | static int tpacpi_uwb_rfk_set(void *data, enum rfkill_state state) | ||
| 3609 | { | ||
| 3610 | return uwb_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0); | ||
| 3611 | } | ||
| 3612 | |||
| 3613 | static void uwb_exit(void) | ||
| 3614 | { | ||
| 3615 | if (tpacpi_uwb_rfkill) | ||
| 3616 | rfkill_unregister(tpacpi_uwb_rfkill); | ||
| 3617 | } | ||
| 3618 | |||
| 3619 | static int __init uwb_init(struct ibm_init_struct *iibm) | ||
| 3620 | { | ||
| 3621 | int res; | ||
| 3622 | int status = 0; | ||
| 3623 | |||
| 3624 | vdbg_printk(TPACPI_DBG_INIT, "initializing uwb subdriver\n"); | ||
| 3625 | |||
| 3626 | TPACPI_ACPIHANDLE_INIT(hkey); | ||
| 3627 | |||
| 3628 | tp_features.uwb = hkey_handle && | ||
| 3629 | acpi_evalf(hkey_handle, &status, "GUWB", "qd"); | ||
| 3630 | |||
| 3631 | vdbg_printk(TPACPI_DBG_INIT, "uwb is %s, status 0x%02x\n", | ||
| 3632 | str_supported(tp_features.uwb), | ||
| 3633 | status); | ||
| 3634 | |||
| 3635 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | ||
| 3636 | if (dbg_uwbemul) { | ||
| 3637 | tp_features.uwb = 1; | ||
| 3638 | printk(TPACPI_INFO | ||
| 3639 | "uwb switch emulation enabled\n"); | ||
| 3640 | } else | ||
| 3641 | #endif | ||
| 3642 | if (tp_features.uwb && | ||
| 3643 | !(status & TP_ACPI_UWB_HWPRESENT)) { | ||
| 3644 | /* no uwb hardware present in system */ | ||
| 3645 | tp_features.uwb = 0; | ||
| 3646 | dbg_printk(TPACPI_DBG_INIT, | ||
| 3647 | "uwb hardware not installed\n"); | ||
| 3648 | } | ||
| 3649 | |||
| 3650 | if (!tp_features.uwb) | ||
| 3651 | return 1; | ||
| 3652 | |||
| 3653 | res = tpacpi_new_rfkill(TPACPI_RFK_UWB_SW_ID, | ||
| 3654 | &tpacpi_uwb_rfkill, | ||
| 3655 | RFKILL_TYPE_UWB, | ||
| 3656 | "tpacpi_uwb_sw", | ||
| 3657 | false, | ||
| 3658 | tpacpi_uwb_rfk_set, | ||
| 3659 | tpacpi_uwb_rfk_get); | ||
| 3660 | |||
| 3661 | return res; | ||
| 3662 | } | ||
| 3663 | |||
| 3664 | static struct ibm_struct uwb_driver_data = { | ||
| 3665 | .name = "uwb", | ||
| 3666 | .exit = uwb_exit, | ||
| 3667 | .flags.experimental = 1, | ||
| 3099 | }; | 3668 | }; |
| 3100 | 3669 | ||
| 3101 | /************************************************************************* | 3670 | /************************************************************************* |
| @@ -3724,7 +4293,7 @@ static void dock_notify(struct ibm_struct *ibm, u32 event) | |||
| 3724 | } | 4293 | } |
| 3725 | acpi_bus_generate_proc_event(ibm->acpi->device, event, data); | 4294 | acpi_bus_generate_proc_event(ibm->acpi->device, event, data); |
| 3726 | acpi_bus_generate_netlink_event(ibm->acpi->device->pnp.device_class, | 4295 | acpi_bus_generate_netlink_event(ibm->acpi->device->pnp.device_class, |
| 3727 | ibm->acpi->device->dev.bus_id, | 4296 | dev_name(&ibm->acpi->device->dev), |
| 3728 | event, data); | 4297 | event, data); |
| 3729 | } | 4298 | } |
| 3730 | 4299 | ||
| @@ -3826,7 +4395,7 @@ static void bay_notify(struct ibm_struct *ibm, u32 event) | |||
| 3826 | { | 4395 | { |
| 3827 | acpi_bus_generate_proc_event(ibm->acpi->device, event, 0); | 4396 | acpi_bus_generate_proc_event(ibm->acpi->device, event, 0); |
| 3828 | acpi_bus_generate_netlink_event(ibm->acpi->device->pnp.device_class, | 4397 | acpi_bus_generate_netlink_event(ibm->acpi->device->pnp.device_class, |
| 3829 | ibm->acpi->device->dev.bus_id, | 4398 | dev_name(&ibm->acpi->device->dev), |
| 3830 | event, 0); | 4399 | event, 0); |
| 3831 | } | 4400 | } |
| 3832 | 4401 | ||
| @@ -4850,7 +5419,7 @@ static int brightness_set(int value) | |||
| 4850 | value < 0) | 5419 | value < 0) |
| 4851 | return -EINVAL; | 5420 | return -EINVAL; |
| 4852 | 5421 | ||
| 4853 | res = mutex_lock_interruptible(&brightness_mutex); | 5422 | res = mutex_lock_killable(&brightness_mutex); |
| 4854 | if (res < 0) | 5423 | if (res < 0) |
| 4855 | return res; | 5424 | return res; |
| 4856 | 5425 | ||
| @@ -5334,6 +5903,60 @@ TPACPI_HANDLE(sfan, ec, "SFAN", /* 570 */ | |||
| 5334 | ); /* all others */ | 5903 | ); /* all others */ |
| 5335 | 5904 | ||
| 5336 | /* | 5905 | /* |
| 5906 | * Unitialized HFSP quirk: ACPI DSDT and EC fail to initialize the | ||
| 5907 | * HFSP register at boot, so it contains 0x07 but the Thinkpad could | ||
| 5908 | * be in auto mode (0x80). | ||
| 5909 | * | ||
| 5910 | * This is corrected by any write to HFSP either by the driver, or | ||
| 5911 | * by the firmware. | ||
| 5912 | * | ||
| 5913 | * We assume 0x07 really means auto mode while this quirk is active, | ||
| 5914 | * as this is far more likely than the ThinkPad being in level 7, | ||
| 5915 | * which is only used by the firmware during thermal emergencies. | ||
| 5916 | */ | ||
| 5917 | |||
| 5918 | static void fan_quirk1_detect(void) | ||
| 5919 | { | ||
| 5920 | /* In some ThinkPads, neither the EC nor the ACPI | ||
| 5921 | * DSDT initialize the HFSP register, and it ends up | ||
| 5922 | * being initially set to 0x07 when it *could* be | ||
| 5923 | * either 0x07 or 0x80. | ||
| 5924 | * | ||
| 5925 | * Enable for TP-1Y (T43), TP-78 (R51e), | ||
| 5926 | * TP-76 (R52), TP-70 (T43, R52), which are known | ||
| 5927 | * to be buggy. */ | ||
| 5928 | if (fan_control_initial_status == 0x07) { | ||
| 5929 | switch (thinkpad_id.ec_model) { | ||
| 5930 | case 0x5931: /* TP-1Y */ | ||
| 5931 | case 0x3837: /* TP-78 */ | ||
| 5932 | case 0x3637: /* TP-76 */ | ||
| 5933 | case 0x3037: /* TP-70 */ | ||
| 5934 | printk(TPACPI_NOTICE | ||
| 5935 | "fan_init: initial fan status is unknown, " | ||
| 5936 | "assuming it is in auto mode\n"); | ||
| 5937 | tp_features.fan_ctrl_status_undef = 1; | ||
| 5938 | ;; | ||
| 5939 | } | ||
| 5940 | } | ||
| 5941 | } | ||
| 5942 | |||
| 5943 | static void fan_quirk1_handle(u8 *fan_status) | ||
| 5944 | { | ||
| 5945 | if (unlikely(tp_features.fan_ctrl_status_undef)) { | ||
| 5946 | if (*fan_status != fan_control_initial_status) { | ||
| 5947 | /* something changed the HFSP regisnter since | ||
| 5948 | * driver init time, so it is not undefined | ||
| 5949 | * anymore */ | ||
| 5950 | tp_features.fan_ctrl_status_undef = 0; | ||
| 5951 | } else { | ||
| 5952 | /* Return most likely status. In fact, it | ||
| 5953 | * might be the only possible status */ | ||
| 5954 | *fan_status = TP_EC_FAN_AUTO; | ||
| 5955 | } | ||
| 5956 | } | ||
| 5957 | } | ||
| 5958 | |||
| 5959 | /* | ||
| 5337 | * Call with fan_mutex held | 5960 | * Call with fan_mutex held |
| 5338 | */ | 5961 | */ |
| 5339 | static void fan_update_desired_level(u8 status) | 5962 | static void fan_update_desired_level(u8 status) |
| @@ -5371,8 +5994,10 @@ static int fan_get_status(u8 *status) | |||
| 5371 | if (unlikely(!acpi_ec_read(fan_status_offset, &s))) | 5994 | if (unlikely(!acpi_ec_read(fan_status_offset, &s))) |
| 5372 | return -EIO; | 5995 | return -EIO; |
| 5373 | 5996 | ||
| 5374 | if (likely(status)) | 5997 | if (likely(status)) { |
| 5375 | *status = s; | 5998 | *status = s; |
| 5999 | fan_quirk1_handle(status); | ||
| 6000 | } | ||
| 5376 | 6001 | ||
| 5377 | break; | 6002 | break; |
| 5378 | 6003 | ||
| @@ -5388,7 +6013,7 @@ static int fan_get_status_safe(u8 *status) | |||
| 5388 | int rc; | 6013 | int rc; |
| 5389 | u8 s; | 6014 | u8 s; |
| 5390 | 6015 | ||
| 5391 | if (mutex_lock_interruptible(&fan_mutex)) | 6016 | if (mutex_lock_killable(&fan_mutex)) |
| 5392 | return -ERESTARTSYS; | 6017 | return -ERESTARTSYS; |
| 5393 | rc = fan_get_status(&s); | 6018 | rc = fan_get_status(&s); |
| 5394 | if (!rc) | 6019 | if (!rc) |
| @@ -5471,7 +6096,7 @@ static int fan_set_level_safe(int level) | |||
| 5471 | if (!fan_control_allowed) | 6096 | if (!fan_control_allowed) |
| 5472 | return -EPERM; | 6097 | return -EPERM; |
| 5473 | 6098 | ||
| 5474 | if (mutex_lock_interruptible(&fan_mutex)) | 6099 | if (mutex_lock_killable(&fan_mutex)) |
| 5475 | return -ERESTARTSYS; | 6100 | return -ERESTARTSYS; |
| 5476 | 6101 | ||
| 5477 | if (level == TPACPI_FAN_LAST_LEVEL) | 6102 | if (level == TPACPI_FAN_LAST_LEVEL) |
| @@ -5493,7 +6118,7 @@ static int fan_set_enable(void) | |||
| 5493 | if (!fan_control_allowed) | 6118 | if (!fan_control_allowed) |
| 5494 | return -EPERM; | 6119 | return -EPERM; |
| 5495 | 6120 | ||
| 5496 | if (mutex_lock_interruptible(&fan_mutex)) | 6121 | if (mutex_lock_killable(&fan_mutex)) |
| 5497 | return -ERESTARTSYS; | 6122 | return -ERESTARTSYS; |
| 5498 | 6123 | ||
| 5499 | switch (fan_control_access_mode) { | 6124 | switch (fan_control_access_mode) { |
| @@ -5548,7 +6173,7 @@ static int fan_set_disable(void) | |||
| 5548 | if (!fan_control_allowed) | 6173 | if (!fan_control_allowed) |
| 5549 | return -EPERM; | 6174 | return -EPERM; |
| 5550 | 6175 | ||
| 5551 | if (mutex_lock_interruptible(&fan_mutex)) | 6176 | if (mutex_lock_killable(&fan_mutex)) |
| 5552 | return -ERESTARTSYS; | 6177 | return -ERESTARTSYS; |
| 5553 | 6178 | ||
| 5554 | rc = 0; | 6179 | rc = 0; |
| @@ -5586,7 +6211,7 @@ static int fan_set_speed(int speed) | |||
| 5586 | if (!fan_control_allowed) | 6211 | if (!fan_control_allowed) |
| 5587 | return -EPERM; | 6212 | return -EPERM; |
| 5588 | 6213 | ||
| 5589 | if (mutex_lock_interruptible(&fan_mutex)) | 6214 | if (mutex_lock_killable(&fan_mutex)) |
| 5590 | return -ERESTARTSYS; | 6215 | return -ERESTARTSYS; |
| 5591 | 6216 | ||
| 5592 | rc = 0; | 6217 | rc = 0; |
| @@ -5682,16 +6307,6 @@ static ssize_t fan_pwm1_enable_show(struct device *dev, | |||
| 5682 | if (res) | 6307 | if (res) |
| 5683 | return res; | 6308 | return res; |
| 5684 | 6309 | ||
| 5685 | if (unlikely(tp_features.fan_ctrl_status_undef)) { | ||
| 5686 | if (status != fan_control_initial_status) { | ||
| 5687 | tp_features.fan_ctrl_status_undef = 0; | ||
| 5688 | } else { | ||
| 5689 | /* Return most likely status. In fact, it | ||
| 5690 | * might be the only possible status */ | ||
| 5691 | status = TP_EC_FAN_AUTO; | ||
| 5692 | } | ||
| 5693 | } | ||
| 5694 | |||
| 5695 | if (status & TP_EC_FAN_FULLSPEED) { | 6310 | if (status & TP_EC_FAN_FULLSPEED) { |
| 5696 | mode = 0; | 6311 | mode = 0; |
| 5697 | } else if (status & TP_EC_FAN_AUTO) { | 6312 | } else if (status & TP_EC_FAN_AUTO) { |
| @@ -5756,14 +6371,6 @@ static ssize_t fan_pwm1_show(struct device *dev, | |||
| 5756 | if (res) | 6371 | if (res) |
| 5757 | return res; | 6372 | return res; |
| 5758 | 6373 | ||
| 5759 | if (unlikely(tp_features.fan_ctrl_status_undef)) { | ||
| 5760 | if (status != fan_control_initial_status) { | ||
| 5761 | tp_features.fan_ctrl_status_undef = 0; | ||
| 5762 | } else { | ||
| 5763 | status = TP_EC_FAN_AUTO; | ||
| 5764 | } | ||
| 5765 | } | ||
| 5766 | |||
| 5767 | if ((status & | 6374 | if ((status & |
| 5768 | (TP_EC_FAN_AUTO | TP_EC_FAN_FULLSPEED)) != 0) | 6375 | (TP_EC_FAN_AUTO | TP_EC_FAN_FULLSPEED)) != 0) |
| 5769 | status = fan_control_desired_level; | 6376 | status = fan_control_desired_level; |
| @@ -5788,7 +6395,7 @@ static ssize_t fan_pwm1_store(struct device *dev, | |||
| 5788 | /* scale down from 0-255 to 0-7 */ | 6395 | /* scale down from 0-255 to 0-7 */ |
| 5789 | newlevel = (s >> 5) & 0x07; | 6396 | newlevel = (s >> 5) & 0x07; |
| 5790 | 6397 | ||
| 5791 | if (mutex_lock_interruptible(&fan_mutex)) | 6398 | if (mutex_lock_killable(&fan_mutex)) |
| 5792 | return -ERESTARTSYS; | 6399 | return -ERESTARTSYS; |
| 5793 | 6400 | ||
| 5794 | rc = fan_get_status(&status); | 6401 | rc = fan_get_status(&status); |
| @@ -5895,29 +6502,7 @@ static int __init fan_init(struct ibm_init_struct *iibm) | |||
| 5895 | if (likely(acpi_ec_read(fan_status_offset, | 6502 | if (likely(acpi_ec_read(fan_status_offset, |
| 5896 | &fan_control_initial_status))) { | 6503 | &fan_control_initial_status))) { |
| 5897 | fan_status_access_mode = TPACPI_FAN_RD_TPEC; | 6504 | fan_status_access_mode = TPACPI_FAN_RD_TPEC; |
| 5898 | 6505 | fan_quirk1_detect(); | |
| 5899 | /* In some ThinkPads, neither the EC nor the ACPI | ||
| 5900 | * DSDT initialize the fan status, and it ends up | ||
| 5901 | * being set to 0x07 when it *could* be either | ||
| 5902 | * 0x07 or 0x80. | ||
| 5903 | * | ||
| 5904 | * Enable for TP-1Y (T43), TP-78 (R51e), | ||
| 5905 | * TP-76 (R52), TP-70 (T43, R52), which are known | ||
| 5906 | * to be buggy. */ | ||
| 5907 | if (fan_control_initial_status == 0x07) { | ||
| 5908 | switch (thinkpad_id.ec_model) { | ||
| 5909 | case 0x5931: /* TP-1Y */ | ||
| 5910 | case 0x3837: /* TP-78 */ | ||
| 5911 | case 0x3637: /* TP-76 */ | ||
| 5912 | case 0x3037: /* TP-70 */ | ||
| 5913 | printk(TPACPI_NOTICE | ||
| 5914 | "fan_init: initial fan status " | ||
| 5915 | "is unknown, assuming it is " | ||
| 5916 | "in auto mode\n"); | ||
| 5917 | tp_features.fan_ctrl_status_undef = 1; | ||
| 5918 | ;; | ||
| 5919 | } | ||
| 5920 | } | ||
| 5921 | } else { | 6506 | } else { |
| 5922 | printk(TPACPI_ERR | 6507 | printk(TPACPI_ERR |
| 5923 | "ThinkPad ACPI EC access misbehaving, " | 6508 | "ThinkPad ACPI EC access misbehaving, " |
| @@ -6106,15 +6691,6 @@ static int fan_read(char *p) | |||
| 6106 | if (rc < 0) | 6691 | if (rc < 0) |
| 6107 | return rc; | 6692 | return rc; |
| 6108 | 6693 | ||
| 6109 | if (unlikely(tp_features.fan_ctrl_status_undef)) { | ||
| 6110 | if (status != fan_control_initial_status) | ||
| 6111 | tp_features.fan_ctrl_status_undef = 0; | ||
| 6112 | else | ||
| 6113 | /* Return most likely status. In fact, it | ||
| 6114 | * might be the only possible status */ | ||
| 6115 | status = TP_EC_FAN_AUTO; | ||
| 6116 | } | ||
| 6117 | |||
| 6118 | len += sprintf(p + len, "status:\t\t%s\n", | 6694 | len += sprintf(p + len, "status:\t\t%s\n", |
| 6119 | (status != 0) ? "enabled" : "disabled"); | 6695 | (status != 0) ? "enabled" : "disabled"); |
| 6120 | 6696 | ||
| @@ -6563,6 +7139,10 @@ static struct ibm_init_struct ibms_init[] __initdata = { | |||
| 6563 | .init = wan_init, | 7139 | .init = wan_init, |
| 6564 | .data = &wan_driver_data, | 7140 | .data = &wan_driver_data, |
| 6565 | }, | 7141 | }, |
| 7142 | { | ||
| 7143 | .init = uwb_init, | ||
| 7144 | .data = &uwb_driver_data, | ||
| 7145 | }, | ||
| 6566 | #ifdef CONFIG_THINKPAD_ACPI_VIDEO | 7146 | #ifdef CONFIG_THINKPAD_ACPI_VIDEO |
| 6567 | { | 7147 | { |
| 6568 | .init = video_init, | 7148 | .init = video_init, |
| @@ -6701,6 +7281,32 @@ TPACPI_PARAM(brightness); | |||
| 6701 | TPACPI_PARAM(volume); | 7281 | TPACPI_PARAM(volume); |
| 6702 | TPACPI_PARAM(fan); | 7282 | TPACPI_PARAM(fan); |
| 6703 | 7283 | ||
| 7284 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | ||
| 7285 | module_param(dbg_wlswemul, uint, 0); | ||
| 7286 | MODULE_PARM_DESC(dbg_wlswemul, "Enables WLSW emulation"); | ||
| 7287 | module_param_named(wlsw_state, tpacpi_wlsw_emulstate, bool, 0); | ||
| 7288 | MODULE_PARM_DESC(wlsw_state, | ||
| 7289 | "Initial state of the emulated WLSW switch"); | ||
| 7290 | |||
| 7291 | module_param(dbg_bluetoothemul, uint, 0); | ||
| 7292 | MODULE_PARM_DESC(dbg_bluetoothemul, "Enables bluetooth switch emulation"); | ||
| 7293 | module_param_named(bluetooth_state, tpacpi_bluetooth_emulstate, bool, 0); | ||
| 7294 | MODULE_PARM_DESC(bluetooth_state, | ||
| 7295 | "Initial state of the emulated bluetooth switch"); | ||
| 7296 | |||
| 7297 | module_param(dbg_wwanemul, uint, 0); | ||
| 7298 | MODULE_PARM_DESC(dbg_wwanemul, "Enables WWAN switch emulation"); | ||
| 7299 | module_param_named(wwan_state, tpacpi_wwan_emulstate, bool, 0); | ||
| 7300 | MODULE_PARM_DESC(wwan_state, | ||
| 7301 | "Initial state of the emulated WWAN switch"); | ||
| 7302 | |||
| 7303 | module_param(dbg_uwbemul, uint, 0); | ||
| 7304 | MODULE_PARM_DESC(dbg_uwbemul, "Enables UWB switch emulation"); | ||
| 7305 | module_param_named(uwb_state, tpacpi_uwb_emulstate, bool, 0); | ||
| 7306 | MODULE_PARM_DESC(uwb_state, | ||
| 7307 | "Initial state of the emulated UWB switch"); | ||
| 7308 | #endif | ||
| 7309 | |||
| 6704 | static void thinkpad_acpi_module_exit(void) | 7310 | static void thinkpad_acpi_module_exit(void) |
| 6705 | { | 7311 | { |
| 6706 | struct ibm_struct *ibm, *itmp; | 7312 | struct ibm_struct *ibm, *itmp; |
