diff options
author | Johannes Berg <johannes@sipsolutions.net> | 2009-06-02 07:01:37 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2009-06-03 14:06:13 -0400 |
commit | 19d337dff95cbf76edd3ad95c0cee2732c3e1ec5 (patch) | |
tree | 33326eeb09cb9664cc8427a5dc7cd2b08b5a57c3 /drivers/platform/x86/thinkpad_acpi.c | |
parent | 0f6399c4c525b518644a9b09f8d6fb125a418c4d (diff) |
rfkill: rewrite
This patch completely rewrites the rfkill core to address
the following deficiencies:
* all rfkill drivers need to implement polling where necessary
rather than having one central implementation
* updating the rfkill state cannot be done from arbitrary
contexts, forcing drivers to use schedule_work and requiring
lots of code
* rfkill drivers need to keep track of soft/hard blocked
internally -- the core should do this
* the rfkill API has many unexpected quirks, for example being
asymmetric wrt. alloc/free and register/unregister
* rfkill can call back into a driver from within a function the
driver called -- this is prone to deadlocks and generally
should be avoided
* rfkill-input pointlessly is a separate module
* drivers need to #ifdef rfkill functions (unless they want to
depend on or select RFKILL) -- rfkill should provide inlines
that do nothing if it isn't compiled in
* the rfkill structure is not opaque -- drivers need to initialise
it correctly (lots of sanity checking code required) -- instead
force drivers to pass the right variables to rfkill_alloc()
* the documentation is hard to read because it always assumes the
reader is completely clueless and contains way TOO MANY CAPS
* the rfkill code needlessly uses a lot of locks and atomic
operations in locked sections
* fix LED trigger to actually change the LED when the radio state
changes -- this wasn't done before
Tested-by: Alan Jenkins <alan-jenkins@tuffmail.co.uk>
Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br> [thinkpad]
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/platform/x86/thinkpad_acpi.c')
-rw-r--r-- | drivers/platform/x86/thinkpad_acpi.c | 873 |
1 files changed, 452 insertions, 421 deletions
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 912be65b6261..cfcafa4e9473 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c | |||
@@ -166,13 +166,6 @@ enum { | |||
166 | 166 | ||
167 | #define TPACPI_MAX_ACPI_ARGS 3 | 167 | #define TPACPI_MAX_ACPI_ARGS 3 |
168 | 168 | ||
169 | /* rfkill switches */ | ||
170 | enum { | ||
171 | TPACPI_RFK_BLUETOOTH_SW_ID = 0, | ||
172 | TPACPI_RFK_WWAN_SW_ID, | ||
173 | TPACPI_RFK_UWB_SW_ID, | ||
174 | }; | ||
175 | |||
176 | /* printk headers */ | 169 | /* printk headers */ |
177 | #define TPACPI_LOG TPACPI_FILE ": " | 170 | #define TPACPI_LOG TPACPI_FILE ": " |
178 | #define TPACPI_EMERG KERN_EMERG TPACPI_LOG | 171 | #define TPACPI_EMERG KERN_EMERG TPACPI_LOG |
@@ -1005,67 +998,237 @@ static int __init tpacpi_check_std_acpi_brightness_support(void) | |||
1005 | return 0; | 998 | return 0; |
1006 | } | 999 | } |
1007 | 1000 | ||
1008 | static int __init tpacpi_new_rfkill(const unsigned int id, | 1001 | static void printk_deprecated_attribute(const char * const what, |
1009 | struct rfkill **rfk, | 1002 | const char * const details) |
1003 | { | ||
1004 | tpacpi_log_usertask("deprecated sysfs attribute"); | ||
1005 | printk(TPACPI_WARN "WARNING: sysfs attribute %s is deprecated and " | ||
1006 | "will be removed. %s\n", | ||
1007 | what, details); | ||
1008 | } | ||
1009 | |||
1010 | /************************************************************************* | ||
1011 | * rfkill and radio control support helpers | ||
1012 | */ | ||
1013 | |||
1014 | /* | ||
1015 | * ThinkPad-ACPI firmware handling model: | ||
1016 | * | ||
1017 | * WLSW (master wireless switch) is event-driven, and is common to all | ||
1018 | * firmware-controlled radios. It cannot be controlled, just monitored, | ||
1019 | * as expected. It overrides all radio state in firmware | ||
1020 | * | ||
1021 | * The kernel, a masked-off hotkey, and WLSW can change the radio state | ||
1022 | * (TODO: verify how WLSW interacts with the returned radio state). | ||
1023 | * | ||
1024 | * The only time there are shadow radio state changes, is when | ||
1025 | * masked-off hotkeys are used. | ||
1026 | */ | ||
1027 | |||
1028 | /* | ||
1029 | * Internal driver API for radio state: | ||
1030 | * | ||
1031 | * int: < 0 = error, otherwise enum tpacpi_rfkill_state | ||
1032 | * bool: true means radio blocked (off) | ||
1033 | */ | ||
1034 | enum tpacpi_rfkill_state { | ||
1035 | TPACPI_RFK_RADIO_OFF = 0, | ||
1036 | TPACPI_RFK_RADIO_ON | ||
1037 | }; | ||
1038 | |||
1039 | /* rfkill switches */ | ||
1040 | enum tpacpi_rfk_id { | ||
1041 | TPACPI_RFK_BLUETOOTH_SW_ID = 0, | ||
1042 | TPACPI_RFK_WWAN_SW_ID, | ||
1043 | TPACPI_RFK_UWB_SW_ID, | ||
1044 | TPACPI_RFK_SW_MAX | ||
1045 | }; | ||
1046 | |||
1047 | static const char *tpacpi_rfkill_names[] = { | ||
1048 | [TPACPI_RFK_BLUETOOTH_SW_ID] = "bluetooth", | ||
1049 | [TPACPI_RFK_WWAN_SW_ID] = "wwan", | ||
1050 | [TPACPI_RFK_UWB_SW_ID] = "uwb", | ||
1051 | [TPACPI_RFK_SW_MAX] = NULL | ||
1052 | }; | ||
1053 | |||
1054 | /* ThinkPad-ACPI rfkill subdriver */ | ||
1055 | struct tpacpi_rfk { | ||
1056 | struct rfkill *rfkill; | ||
1057 | enum tpacpi_rfk_id id; | ||
1058 | const struct tpacpi_rfk_ops *ops; | ||
1059 | }; | ||
1060 | |||
1061 | struct tpacpi_rfk_ops { | ||
1062 | /* firmware interface */ | ||
1063 | int (*get_status)(void); | ||
1064 | int (*set_status)(const enum tpacpi_rfkill_state); | ||
1065 | }; | ||
1066 | |||
1067 | static struct tpacpi_rfk *tpacpi_rfkill_switches[TPACPI_RFK_SW_MAX]; | ||
1068 | |||
1069 | /* Query FW and update rfkill sw state for a given rfkill switch */ | ||
1070 | static int tpacpi_rfk_update_swstate(const struct tpacpi_rfk *tp_rfk) | ||
1071 | { | ||
1072 | int status; | ||
1073 | |||
1074 | if (!tp_rfk) | ||
1075 | return -ENODEV; | ||
1076 | |||
1077 | status = (tp_rfk->ops->get_status)(); | ||
1078 | if (status < 0) | ||
1079 | return status; | ||
1080 | |||
1081 | rfkill_set_sw_state(tp_rfk->rfkill, | ||
1082 | (status == TPACPI_RFK_RADIO_OFF)); | ||
1083 | |||
1084 | return status; | ||
1085 | } | ||
1086 | |||
1087 | /* Query FW and update rfkill sw state for all rfkill switches */ | ||
1088 | static void tpacpi_rfk_update_swstate_all(void) | ||
1089 | { | ||
1090 | unsigned int i; | ||
1091 | |||
1092 | for (i = 0; i < TPACPI_RFK_SW_MAX; i++) | ||
1093 | tpacpi_rfk_update_swstate(tpacpi_rfkill_switches[i]); | ||
1094 | } | ||
1095 | |||
1096 | /* | ||
1097 | * Sync the HW-blocking state of all rfkill switches, | ||
1098 | * do notice it causes the rfkill core to schedule uevents | ||
1099 | */ | ||
1100 | static void tpacpi_rfk_update_hwblock_state(bool blocked) | ||
1101 | { | ||
1102 | unsigned int i; | ||
1103 | struct tpacpi_rfk *tp_rfk; | ||
1104 | |||
1105 | for (i = 0; i < TPACPI_RFK_SW_MAX; i++) { | ||
1106 | tp_rfk = tpacpi_rfkill_switches[i]; | ||
1107 | if (tp_rfk) { | ||
1108 | if (rfkill_set_hw_state(tp_rfk->rfkill, | ||
1109 | blocked)) { | ||
1110 | /* ignore -- we track sw block */ | ||
1111 | } | ||
1112 | } | ||
1113 | } | ||
1114 | } | ||
1115 | |||
1116 | /* Call to get the WLSW state from the firmware */ | ||
1117 | static int hotkey_get_wlsw(void); | ||
1118 | |||
1119 | /* Call to query WLSW state and update all rfkill switches */ | ||
1120 | static bool tpacpi_rfk_check_hwblock_state(void) | ||
1121 | { | ||
1122 | int res = hotkey_get_wlsw(); | ||
1123 | int hw_blocked; | ||
1124 | |||
1125 | /* When unknown or unsupported, we have to assume it is unblocked */ | ||
1126 | if (res < 0) | ||
1127 | return false; | ||
1128 | |||
1129 | hw_blocked = (res == TPACPI_RFK_RADIO_OFF); | ||
1130 | tpacpi_rfk_update_hwblock_state(hw_blocked); | ||
1131 | |||
1132 | return hw_blocked; | ||
1133 | } | ||
1134 | |||
1135 | static int tpacpi_rfk_hook_set_block(void *data, bool blocked) | ||
1136 | { | ||
1137 | struct tpacpi_rfk *tp_rfk = data; | ||
1138 | int res; | ||
1139 | |||
1140 | dbg_printk(TPACPI_DBG_RFKILL, | ||
1141 | "request to change radio state to %s\n", | ||
1142 | blocked ? "blocked" : "unblocked"); | ||
1143 | |||
1144 | /* try to set radio state */ | ||
1145 | res = (tp_rfk->ops->set_status)(blocked ? | ||
1146 | TPACPI_RFK_RADIO_OFF : TPACPI_RFK_RADIO_ON); | ||
1147 | |||
1148 | /* and update the rfkill core with whatever the FW really did */ | ||
1149 | tpacpi_rfk_update_swstate(tp_rfk); | ||
1150 | |||
1151 | return (res < 0) ? res : 0; | ||
1152 | } | ||
1153 | |||
1154 | static const struct rfkill_ops tpacpi_rfk_rfkill_ops = { | ||
1155 | .set_block = tpacpi_rfk_hook_set_block, | ||
1156 | }; | ||
1157 | |||
1158 | static int __init tpacpi_new_rfkill(const enum tpacpi_rfk_id id, | ||
1159 | const struct tpacpi_rfk_ops *tp_rfkops, | ||
1010 | const enum rfkill_type rfktype, | 1160 | const enum rfkill_type rfktype, |
1011 | const char *name, | 1161 | const char *name, |
1012 | const bool set_default, | 1162 | const bool set_default) |
1013 | int (*toggle_radio)(void *, enum rfkill_state), | ||
1014 | int (*get_state)(void *, enum rfkill_state *)) | ||
1015 | { | 1163 | { |
1164 | struct tpacpi_rfk *atp_rfk; | ||
1016 | int res; | 1165 | int res; |
1017 | enum rfkill_state initial_state = RFKILL_STATE_SOFT_BLOCKED; | 1166 | bool initial_sw_state = false; |
1167 | int initial_sw_status; | ||
1018 | 1168 | ||
1019 | res = get_state(NULL, &initial_state); | 1169 | BUG_ON(id >= TPACPI_RFK_SW_MAX || tpacpi_rfkill_switches[id]); |
1020 | if (res < 0) { | 1170 | |
1171 | initial_sw_status = (tp_rfkops->get_status)(); | ||
1172 | if (initial_sw_status < 0) { | ||
1021 | printk(TPACPI_ERR | 1173 | printk(TPACPI_ERR |
1022 | "failed to read initial state for %s, error %d; " | 1174 | "failed to read initial state for %s, error %d; " |
1023 | "will turn radio off\n", name, res); | 1175 | "will turn radio off\n", name, initial_sw_status); |
1024 | } else if (set_default) { | 1176 | } else { |
1025 | /* try to set the initial state as the default for the rfkill | 1177 | initial_sw_state = (initial_sw_status == TPACPI_RFK_RADIO_OFF); |
1026 | * type, since we ask the firmware to preserve it across S5 in | 1178 | if (set_default) { |
1027 | * NVRAM */ | 1179 | /* try to set the initial state as the default for the |
1028 | if (rfkill_set_default(rfktype, | 1180 | * rfkill type, since we ask the firmware to preserve |
1029 | (initial_state == RFKILL_STATE_UNBLOCKED) ? | 1181 | * it across S5 in NVRAM */ |
1030 | RFKILL_STATE_UNBLOCKED : | 1182 | rfkill_set_global_sw_state(rfktype, initial_sw_state); |
1031 | RFKILL_STATE_SOFT_BLOCKED) == -EPERM) | 1183 | } |
1032 | vdbg_printk(TPACPI_DBG_RFKILL, | 1184 | } |
1033 | "Default state for %s cannot be changed\n", | 1185 | |
1034 | name); | 1186 | atp_rfk = kzalloc(sizeof(struct tpacpi_rfk), GFP_KERNEL); |
1035 | } | 1187 | if (atp_rfk) |
1036 | 1188 | atp_rfk->rfkill = rfkill_alloc(name, | |
1037 | *rfk = rfkill_allocate(&tpacpi_pdev->dev, rfktype); | 1189 | &tpacpi_pdev->dev, |
1038 | if (!*rfk) { | 1190 | rfktype, |
1191 | &tpacpi_rfk_rfkill_ops, | ||
1192 | atp_rfk); | ||
1193 | if (!atp_rfk || !atp_rfk->rfkill) { | ||
1039 | printk(TPACPI_ERR | 1194 | printk(TPACPI_ERR |
1040 | "failed to allocate memory for rfkill class\n"); | 1195 | "failed to allocate memory for rfkill class\n"); |
1196 | kfree(atp_rfk); | ||
1041 | return -ENOMEM; | 1197 | return -ENOMEM; |
1042 | } | 1198 | } |
1043 | 1199 | ||
1044 | (*rfk)->name = name; | 1200 | atp_rfk->id = id; |
1045 | (*rfk)->get_state = get_state; | 1201 | atp_rfk->ops = tp_rfkops; |
1046 | (*rfk)->toggle_radio = toggle_radio; | 1202 | |
1047 | (*rfk)->state = initial_state; | 1203 | rfkill_set_states(atp_rfk->rfkill, initial_sw_state, |
1204 | tpacpi_rfk_check_hwblock_state()); | ||
1048 | 1205 | ||
1049 | res = rfkill_register(*rfk); | 1206 | res = rfkill_register(atp_rfk->rfkill); |
1050 | if (res < 0) { | 1207 | if (res < 0) { |
1051 | printk(TPACPI_ERR | 1208 | printk(TPACPI_ERR |
1052 | "failed to register %s rfkill switch: %d\n", | 1209 | "failed to register %s rfkill switch: %d\n", |
1053 | name, res); | 1210 | name, res); |
1054 | rfkill_free(*rfk); | 1211 | rfkill_destroy(atp_rfk->rfkill); |
1055 | *rfk = NULL; | 1212 | kfree(atp_rfk); |
1056 | return res; | 1213 | return res; |
1057 | } | 1214 | } |
1058 | 1215 | ||
1216 | tpacpi_rfkill_switches[id] = atp_rfk; | ||
1059 | return 0; | 1217 | return 0; |
1060 | } | 1218 | } |
1061 | 1219 | ||
1062 | static void printk_deprecated_attribute(const char * const what, | 1220 | static void tpacpi_destroy_rfkill(const enum tpacpi_rfk_id id) |
1063 | const char * const details) | ||
1064 | { | 1221 | { |
1065 | tpacpi_log_usertask("deprecated sysfs attribute"); | 1222 | struct tpacpi_rfk *tp_rfk; |
1066 | printk(TPACPI_WARN "WARNING: sysfs attribute %s is deprecated and " | 1223 | |
1067 | "will be removed. %s\n", | 1224 | BUG_ON(id >= TPACPI_RFK_SW_MAX); |
1068 | what, details); | 1225 | |
1226 | tp_rfk = tpacpi_rfkill_switches[id]; | ||
1227 | if (tp_rfk) { | ||
1228 | rfkill_unregister(tp_rfk->rfkill); | ||
1229 | tpacpi_rfkill_switches[id] = NULL; | ||
1230 | kfree(tp_rfk); | ||
1231 | } | ||
1069 | } | 1232 | } |
1070 | 1233 | ||
1071 | static void printk_deprecated_rfkill_attribute(const char * const what) | 1234 | static void printk_deprecated_rfkill_attribute(const char * const what) |
@@ -1074,6 +1237,112 @@ static void printk_deprecated_rfkill_attribute(const char * const what) | |||
1074 | "Please switch to generic rfkill before year 2010"); | 1237 | "Please switch to generic rfkill before year 2010"); |
1075 | } | 1238 | } |
1076 | 1239 | ||
1240 | /* sysfs <radio> enable ------------------------------------------------ */ | ||
1241 | static ssize_t tpacpi_rfk_sysfs_enable_show(const enum tpacpi_rfk_id id, | ||
1242 | struct device_attribute *attr, | ||
1243 | char *buf) | ||
1244 | { | ||
1245 | int status; | ||
1246 | |||
1247 | printk_deprecated_rfkill_attribute(attr->attr.name); | ||
1248 | |||
1249 | /* This is in the ABI... */ | ||
1250 | if (tpacpi_rfk_check_hwblock_state()) { | ||
1251 | status = TPACPI_RFK_RADIO_OFF; | ||
1252 | } else { | ||
1253 | status = tpacpi_rfk_update_swstate(tpacpi_rfkill_switches[id]); | ||
1254 | if (status < 0) | ||
1255 | return status; | ||
1256 | } | ||
1257 | |||
1258 | return snprintf(buf, PAGE_SIZE, "%d\n", | ||
1259 | (status == TPACPI_RFK_RADIO_ON) ? 1 : 0); | ||
1260 | } | ||
1261 | |||
1262 | static ssize_t tpacpi_rfk_sysfs_enable_store(const enum tpacpi_rfk_id id, | ||
1263 | struct device_attribute *attr, | ||
1264 | const char *buf, size_t count) | ||
1265 | { | ||
1266 | unsigned long t; | ||
1267 | int res; | ||
1268 | |||
1269 | printk_deprecated_rfkill_attribute(attr->attr.name); | ||
1270 | |||
1271 | if (parse_strtoul(buf, 1, &t)) | ||
1272 | return -EINVAL; | ||
1273 | |||
1274 | tpacpi_disclose_usertask(attr->attr.name, "set to %ld\n", t); | ||
1275 | |||
1276 | /* This is in the ABI... */ | ||
1277 | if (tpacpi_rfk_check_hwblock_state() && !!t) | ||
1278 | return -EPERM; | ||
1279 | |||
1280 | res = tpacpi_rfkill_switches[id]->ops->set_status((!!t) ? | ||
1281 | TPACPI_RFK_RADIO_ON : TPACPI_RFK_RADIO_OFF); | ||
1282 | tpacpi_rfk_update_swstate(tpacpi_rfkill_switches[id]); | ||
1283 | |||
1284 | return (res < 0) ? res : count; | ||
1285 | } | ||
1286 | |||
1287 | /* procfs -------------------------------------------------------------- */ | ||
1288 | static int tpacpi_rfk_procfs_read(const enum tpacpi_rfk_id id, char *p) | ||
1289 | { | ||
1290 | int len = 0; | ||
1291 | |||
1292 | if (id >= TPACPI_RFK_SW_MAX) | ||
1293 | len += sprintf(p + len, "status:\t\tnot supported\n"); | ||
1294 | else { | ||
1295 | int status; | ||
1296 | |||
1297 | /* This is in the ABI... */ | ||
1298 | if (tpacpi_rfk_check_hwblock_state()) { | ||
1299 | status = TPACPI_RFK_RADIO_OFF; | ||
1300 | } else { | ||
1301 | status = tpacpi_rfk_update_swstate( | ||
1302 | tpacpi_rfkill_switches[id]); | ||
1303 | if (status < 0) | ||
1304 | return status; | ||
1305 | } | ||
1306 | |||
1307 | len += sprintf(p + len, "status:\t\t%s\n", | ||
1308 | (status == TPACPI_RFK_RADIO_ON) ? | ||
1309 | "enabled" : "disabled"); | ||
1310 | len += sprintf(p + len, "commands:\tenable, disable\n"); | ||
1311 | } | ||
1312 | |||
1313 | return len; | ||
1314 | } | ||
1315 | |||
1316 | static int tpacpi_rfk_procfs_write(const enum tpacpi_rfk_id id, char *buf) | ||
1317 | { | ||
1318 | char *cmd; | ||
1319 | int status = -1; | ||
1320 | int res = 0; | ||
1321 | |||
1322 | if (id >= TPACPI_RFK_SW_MAX) | ||
1323 | return -ENODEV; | ||
1324 | |||
1325 | while ((cmd = next_cmd(&buf))) { | ||
1326 | if (strlencmp(cmd, "enable") == 0) | ||
1327 | status = TPACPI_RFK_RADIO_ON; | ||
1328 | else if (strlencmp(cmd, "disable") == 0) | ||
1329 | status = TPACPI_RFK_RADIO_OFF; | ||
1330 | else | ||
1331 | return -EINVAL; | ||
1332 | } | ||
1333 | |||
1334 | if (status != -1) { | ||
1335 | tpacpi_disclose_usertask("procfs", "attempt to %s %s\n", | ||
1336 | (status == TPACPI_RFK_RADIO_ON) ? | ||
1337 | "enable" : "disable", | ||
1338 | tpacpi_rfkill_names[id]); | ||
1339 | res = (tpacpi_rfkill_switches[id]->ops->set_status)(status); | ||
1340 | tpacpi_rfk_update_swstate(tpacpi_rfkill_switches[id]); | ||
1341 | } | ||
1342 | |||
1343 | return res; | ||
1344 | } | ||
1345 | |||
1077 | /************************************************************************* | 1346 | /************************************************************************* |
1078 | * thinkpad-acpi driver attributes | 1347 | * thinkpad-acpi driver attributes |
1079 | */ | 1348 | */ |
@@ -1127,8 +1396,6 @@ static DRIVER_ATTR(version, S_IRUGO, | |||
1127 | 1396 | ||
1128 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | 1397 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES |
1129 | 1398 | ||
1130 | static void tpacpi_send_radiosw_update(void); | ||
1131 | |||
1132 | /* wlsw_emulstate ------------------------------------------------------ */ | 1399 | /* wlsw_emulstate ------------------------------------------------------ */ |
1133 | static ssize_t tpacpi_driver_wlsw_emulstate_show(struct device_driver *drv, | 1400 | static ssize_t tpacpi_driver_wlsw_emulstate_show(struct device_driver *drv, |
1134 | char *buf) | 1401 | char *buf) |
@@ -1144,11 +1411,10 @@ static ssize_t tpacpi_driver_wlsw_emulstate_store(struct device_driver *drv, | |||
1144 | if (parse_strtoul(buf, 1, &t)) | 1411 | if (parse_strtoul(buf, 1, &t)) |
1145 | return -EINVAL; | 1412 | return -EINVAL; |
1146 | 1413 | ||
1147 | if (tpacpi_wlsw_emulstate != t) { | 1414 | if (tpacpi_wlsw_emulstate != !!t) { |
1148 | tpacpi_wlsw_emulstate = !!t; | ||
1149 | tpacpi_send_radiosw_update(); | ||
1150 | } else | ||
1151 | tpacpi_wlsw_emulstate = !!t; | 1415 | tpacpi_wlsw_emulstate = !!t; |
1416 | tpacpi_rfk_update_hwblock_state(!t); /* negative logic */ | ||
1417 | } | ||
1152 | 1418 | ||
1153 | return count; | 1419 | return count; |
1154 | } | 1420 | } |
@@ -1463,17 +1729,23 @@ static struct attribute_set *hotkey_dev_attributes; | |||
1463 | /* HKEY.MHKG() return bits */ | 1729 | /* HKEY.MHKG() return bits */ |
1464 | #define TP_HOTKEY_TABLET_MASK (1 << 3) | 1730 | #define TP_HOTKEY_TABLET_MASK (1 << 3) |
1465 | 1731 | ||
1466 | static int hotkey_get_wlsw(int *status) | 1732 | static int hotkey_get_wlsw(void) |
1467 | { | 1733 | { |
1734 | int status; | ||
1735 | |||
1736 | if (!tp_features.hotkey_wlsw) | ||
1737 | return -ENODEV; | ||
1738 | |||
1468 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | 1739 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES |
1469 | if (dbg_wlswemul) { | 1740 | if (dbg_wlswemul) |
1470 | *status = !!tpacpi_wlsw_emulstate; | 1741 | return (tpacpi_wlsw_emulstate) ? |
1471 | return 0; | 1742 | TPACPI_RFK_RADIO_ON : TPACPI_RFK_RADIO_OFF; |
1472 | } | ||
1473 | #endif | 1743 | #endif |
1474 | if (!acpi_evalf(hkey_handle, status, "WLSW", "d")) | 1744 | |
1745 | if (!acpi_evalf(hkey_handle, &status, "WLSW", "d")) | ||
1475 | return -EIO; | 1746 | return -EIO; |
1476 | return 0; | 1747 | |
1748 | return (status) ? TPACPI_RFK_RADIO_ON : TPACPI_RFK_RADIO_OFF; | ||
1477 | } | 1749 | } |
1478 | 1750 | ||
1479 | static int hotkey_get_tablet_mode(int *status) | 1751 | static int hotkey_get_tablet_mode(int *status) |
@@ -2107,12 +2379,16 @@ static ssize_t hotkey_radio_sw_show(struct device *dev, | |||
2107 | struct device_attribute *attr, | 2379 | struct device_attribute *attr, |
2108 | char *buf) | 2380 | char *buf) |
2109 | { | 2381 | { |
2110 | int res, s; | 2382 | int res; |
2111 | res = hotkey_get_wlsw(&s); | 2383 | res = hotkey_get_wlsw(); |
2112 | if (res < 0) | 2384 | if (res < 0) |
2113 | return res; | 2385 | return res; |
2114 | 2386 | ||
2115 | return snprintf(buf, PAGE_SIZE, "%d\n", !!s); | 2387 | /* Opportunistic update */ |
2388 | tpacpi_rfk_update_hwblock_state((res == TPACPI_RFK_RADIO_OFF)); | ||
2389 | |||
2390 | return snprintf(buf, PAGE_SIZE, "%d\n", | ||
2391 | (res == TPACPI_RFK_RADIO_OFF) ? 0 : 1); | ||
2116 | } | 2392 | } |
2117 | 2393 | ||
2118 | static struct device_attribute dev_attr_hotkey_radio_sw = | 2394 | static struct device_attribute dev_attr_hotkey_radio_sw = |
@@ -2223,30 +2499,52 @@ static struct attribute *hotkey_mask_attributes[] __initdata = { | |||
2223 | &dev_attr_hotkey_wakeup_hotunplug_complete.attr, | 2499 | &dev_attr_hotkey_wakeup_hotunplug_complete.attr, |
2224 | }; | 2500 | }; |
2225 | 2501 | ||
2226 | static void bluetooth_update_rfk(void); | 2502 | /* |
2227 | static void wan_update_rfk(void); | 2503 | * Sync both the hw and sw blocking state of all switches |
2228 | static void uwb_update_rfk(void); | 2504 | */ |
2229 | static void tpacpi_send_radiosw_update(void) | 2505 | static void tpacpi_send_radiosw_update(void) |
2230 | { | 2506 | { |
2231 | int wlsw; | 2507 | int wlsw; |
2232 | 2508 | ||
2233 | /* Sync these BEFORE sending any rfkill events */ | 2509 | /* |
2234 | if (tp_features.bluetooth) | 2510 | * We must sync all rfkill controllers *before* issuing any |
2235 | bluetooth_update_rfk(); | 2511 | * rfkill input events, or we will race the rfkill core input |
2236 | if (tp_features.wan) | 2512 | * handler. |
2237 | wan_update_rfk(); | 2513 | * |
2238 | if (tp_features.uwb) | 2514 | * tpacpi_inputdev_send_mutex works as a syncronization point |
2239 | uwb_update_rfk(); | 2515 | * for the above. |
2516 | * | ||
2517 | * We optimize to avoid numerous calls to hotkey_get_wlsw. | ||
2518 | */ | ||
2240 | 2519 | ||
2241 | if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&wlsw)) { | 2520 | wlsw = hotkey_get_wlsw(); |
2521 | |||
2522 | /* Sync hw blocking state first if it is hw-blocked */ | ||
2523 | if (wlsw == TPACPI_RFK_RADIO_OFF) | ||
2524 | tpacpi_rfk_update_hwblock_state(true); | ||
2525 | |||
2526 | /* Sync sw blocking state */ | ||
2527 | tpacpi_rfk_update_swstate_all(); | ||
2528 | |||
2529 | /* Sync hw blocking state last if it is hw-unblocked */ | ||
2530 | if (wlsw == TPACPI_RFK_RADIO_ON) | ||
2531 | tpacpi_rfk_update_hwblock_state(false); | ||
2532 | |||
2533 | /* Issue rfkill input event for WLSW switch */ | ||
2534 | if (!(wlsw < 0)) { | ||
2242 | mutex_lock(&tpacpi_inputdev_send_mutex); | 2535 | mutex_lock(&tpacpi_inputdev_send_mutex); |
2243 | 2536 | ||
2244 | input_report_switch(tpacpi_inputdev, | 2537 | input_report_switch(tpacpi_inputdev, |
2245 | SW_RFKILL_ALL, !!wlsw); | 2538 | SW_RFKILL_ALL, (wlsw > 0)); |
2246 | input_sync(tpacpi_inputdev); | 2539 | input_sync(tpacpi_inputdev); |
2247 | 2540 | ||
2248 | mutex_unlock(&tpacpi_inputdev_send_mutex); | 2541 | mutex_unlock(&tpacpi_inputdev_send_mutex); |
2249 | } | 2542 | } |
2543 | |||
2544 | /* | ||
2545 | * this can be unconditional, as we will poll state again | ||
2546 | * if userspace uses the notify to read data | ||
2547 | */ | ||
2250 | hotkey_radio_sw_notify_change(); | 2548 | hotkey_radio_sw_notify_change(); |
2251 | } | 2549 | } |
2252 | 2550 | ||
@@ -3056,8 +3354,6 @@ enum { | |||
3056 | 3354 | ||
3057 | #define TPACPI_RFK_BLUETOOTH_SW_NAME "tpacpi_bluetooth_sw" | 3355 | #define TPACPI_RFK_BLUETOOTH_SW_NAME "tpacpi_bluetooth_sw" |
3058 | 3356 | ||
3059 | static struct rfkill *tpacpi_bluetooth_rfkill; | ||
3060 | |||
3061 | static void bluetooth_suspend(pm_message_t state) | 3357 | static void bluetooth_suspend(pm_message_t state) |
3062 | { | 3358 | { |
3063 | /* Try to make sure radio will resume powered off */ | 3359 | /* Try to make sure radio will resume powered off */ |
@@ -3067,83 +3363,47 @@ static void bluetooth_suspend(pm_message_t state) | |||
3067 | "bluetooth power down on resume request failed\n"); | 3363 | "bluetooth power down on resume request failed\n"); |
3068 | } | 3364 | } |
3069 | 3365 | ||
3070 | static int bluetooth_get_radiosw(void) | 3366 | static int bluetooth_get_status(void) |
3071 | { | 3367 | { |
3072 | int status; | 3368 | int status; |
3073 | 3369 | ||
3074 | if (!tp_features.bluetooth) | ||
3075 | return -ENODEV; | ||
3076 | |||
3077 | /* WLSW overrides bluetooth in firmware/hardware, reflect that */ | ||
3078 | if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status) | ||
3079 | return RFKILL_STATE_HARD_BLOCKED; | ||
3080 | |||
3081 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | 3370 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES |
3082 | if (dbg_bluetoothemul) | 3371 | if (dbg_bluetoothemul) |
3083 | return (tpacpi_bluetooth_emulstate) ? | 3372 | return (tpacpi_bluetooth_emulstate) ? |
3084 | RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED; | 3373 | TPACPI_RFK_RADIO_ON : TPACPI_RFK_RADIO_OFF; |
3085 | #endif | 3374 | #endif |
3086 | 3375 | ||
3087 | if (!acpi_evalf(hkey_handle, &status, "GBDC", "d")) | 3376 | if (!acpi_evalf(hkey_handle, &status, "GBDC", "d")) |
3088 | return -EIO; | 3377 | return -EIO; |
3089 | 3378 | ||
3090 | return ((status & TP_ACPI_BLUETOOTH_RADIOSSW) != 0) ? | 3379 | return ((status & TP_ACPI_BLUETOOTH_RADIOSSW) != 0) ? |
3091 | RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED; | 3380 | TPACPI_RFK_RADIO_ON : TPACPI_RFK_RADIO_OFF; |
3092 | } | 3381 | } |
3093 | 3382 | ||
3094 | static void bluetooth_update_rfk(void) | 3383 | static int bluetooth_set_status(enum tpacpi_rfkill_state state) |
3095 | { | 3384 | { |
3096 | int status; | 3385 | int status; |
3097 | 3386 | ||
3098 | if (!tpacpi_bluetooth_rfkill) | ||
3099 | return; | ||
3100 | |||
3101 | status = bluetooth_get_radiosw(); | ||
3102 | if (status < 0) | ||
3103 | return; | ||
3104 | rfkill_force_state(tpacpi_bluetooth_rfkill, status); | ||
3105 | |||
3106 | vdbg_printk(TPACPI_DBG_RFKILL, | 3387 | vdbg_printk(TPACPI_DBG_RFKILL, |
3107 | "forced rfkill state to %d\n", | 3388 | "will attempt to %s bluetooth\n", |
3108 | status); | 3389 | (state == TPACPI_RFK_RADIO_ON) ? "enable" : "disable"); |
3109 | } | ||
3110 | |||
3111 | static int bluetooth_set_radiosw(int radio_on, int update_rfk) | ||
3112 | { | ||
3113 | int status; | ||
3114 | |||
3115 | if (!tp_features.bluetooth) | ||
3116 | return -ENODEV; | ||
3117 | |||
3118 | /* WLSW overrides bluetooth in firmware/hardware, but there is no | ||
3119 | * reason to risk weird behaviour. */ | ||
3120 | if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status | ||
3121 | && radio_on) | ||
3122 | return -EPERM; | ||
3123 | |||
3124 | vdbg_printk(TPACPI_DBG_RFKILL, | ||
3125 | "will %s bluetooth\n", radio_on ? "enable" : "disable"); | ||
3126 | 3390 | ||
3127 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | 3391 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES |
3128 | if (dbg_bluetoothemul) { | 3392 | if (dbg_bluetoothemul) { |
3129 | tpacpi_bluetooth_emulstate = !!radio_on; | 3393 | tpacpi_bluetooth_emulstate = (state == TPACPI_RFK_RADIO_ON); |
3130 | if (update_rfk) | ||
3131 | bluetooth_update_rfk(); | ||
3132 | return 0; | 3394 | return 0; |
3133 | } | 3395 | } |
3134 | #endif | 3396 | #endif |
3135 | 3397 | ||
3136 | /* We make sure to keep TP_ACPI_BLUETOOTH_RESUMECTRL off */ | 3398 | /* We make sure to keep TP_ACPI_BLUETOOTH_RESUMECTRL off */ |
3137 | if (radio_on) | 3399 | if (state == TPACPI_RFK_RADIO_ON) |
3138 | status = TP_ACPI_BLUETOOTH_RADIOSSW; | 3400 | status = TP_ACPI_BLUETOOTH_RADIOSSW; |
3139 | else | 3401 | else |
3140 | status = 0; | 3402 | status = 0; |
3403 | |||
3141 | if (!acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status)) | 3404 | if (!acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status)) |
3142 | return -EIO; | 3405 | return -EIO; |
3143 | 3406 | ||
3144 | if (update_rfk) | ||
3145 | bluetooth_update_rfk(); | ||
3146 | |||
3147 | return 0; | 3407 | return 0; |
3148 | } | 3408 | } |
3149 | 3409 | ||
@@ -3152,35 +3412,16 @@ static ssize_t bluetooth_enable_show(struct device *dev, | |||
3152 | struct device_attribute *attr, | 3412 | struct device_attribute *attr, |
3153 | char *buf) | 3413 | char *buf) |
3154 | { | 3414 | { |
3155 | int status; | 3415 | return tpacpi_rfk_sysfs_enable_show(TPACPI_RFK_BLUETOOTH_SW_ID, |
3156 | 3416 | attr, buf); | |
3157 | printk_deprecated_rfkill_attribute("bluetooth_enable"); | ||
3158 | |||
3159 | status = bluetooth_get_radiosw(); | ||
3160 | if (status < 0) | ||
3161 | return status; | ||
3162 | |||
3163 | return snprintf(buf, PAGE_SIZE, "%d\n", | ||
3164 | (status == RFKILL_STATE_UNBLOCKED) ? 1 : 0); | ||
3165 | } | 3417 | } |
3166 | 3418 | ||
3167 | static ssize_t bluetooth_enable_store(struct device *dev, | 3419 | static ssize_t bluetooth_enable_store(struct device *dev, |
3168 | struct device_attribute *attr, | 3420 | struct device_attribute *attr, |
3169 | const char *buf, size_t count) | 3421 | const char *buf, size_t count) |
3170 | { | 3422 | { |
3171 | unsigned long t; | 3423 | return tpacpi_rfk_sysfs_enable_store(TPACPI_RFK_BLUETOOTH_SW_ID, |
3172 | int res; | 3424 | attr, buf, count); |
3173 | |||
3174 | printk_deprecated_rfkill_attribute("bluetooth_enable"); | ||
3175 | |||
3176 | if (parse_strtoul(buf, 1, &t)) | ||
3177 | return -EINVAL; | ||
3178 | |||
3179 | tpacpi_disclose_usertask("bluetooth_enable", "set to %ld\n", t); | ||
3180 | |||
3181 | res = bluetooth_set_radiosw(t, 1); | ||
3182 | |||
3183 | return (res) ? res : count; | ||
3184 | } | 3425 | } |
3185 | 3426 | ||
3186 | static struct device_attribute dev_attr_bluetooth_enable = | 3427 | static struct device_attribute dev_attr_bluetooth_enable = |
@@ -3198,23 +3439,10 @@ static const struct attribute_group bluetooth_attr_group = { | |||
3198 | .attrs = bluetooth_attributes, | 3439 | .attrs = bluetooth_attributes, |
3199 | }; | 3440 | }; |
3200 | 3441 | ||
3201 | static int tpacpi_bluetooth_rfk_get(void *data, enum rfkill_state *state) | 3442 | static const struct tpacpi_rfk_ops bluetooth_tprfk_ops = { |
3202 | { | 3443 | .get_status = bluetooth_get_status, |
3203 | int bts = bluetooth_get_radiosw(); | 3444 | .set_status = bluetooth_set_status, |
3204 | 3445 | }; | |
3205 | if (bts < 0) | ||
3206 | return bts; | ||
3207 | |||
3208 | *state = bts; | ||
3209 | return 0; | ||
3210 | } | ||
3211 | |||
3212 | static int tpacpi_bluetooth_rfk_set(void *data, enum rfkill_state state) | ||
3213 | { | ||
3214 | dbg_printk(TPACPI_DBG_RFKILL, | ||
3215 | "request to change radio state to %d\n", state); | ||
3216 | return bluetooth_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0); | ||
3217 | } | ||
3218 | 3446 | ||
3219 | static void bluetooth_shutdown(void) | 3447 | static void bluetooth_shutdown(void) |
3220 | { | 3448 | { |
@@ -3230,13 +3458,12 @@ static void bluetooth_shutdown(void) | |||
3230 | 3458 | ||
3231 | static void bluetooth_exit(void) | 3459 | static void bluetooth_exit(void) |
3232 | { | 3460 | { |
3233 | bluetooth_shutdown(); | ||
3234 | |||
3235 | if (tpacpi_bluetooth_rfkill) | ||
3236 | rfkill_unregister(tpacpi_bluetooth_rfkill); | ||
3237 | |||
3238 | sysfs_remove_group(&tpacpi_pdev->dev.kobj, | 3461 | sysfs_remove_group(&tpacpi_pdev->dev.kobj, |
3239 | &bluetooth_attr_group); | 3462 | &bluetooth_attr_group); |
3463 | |||
3464 | tpacpi_destroy_rfkill(TPACPI_RFK_BLUETOOTH_SW_ID); | ||
3465 | |||
3466 | bluetooth_shutdown(); | ||
3240 | } | 3467 | } |
3241 | 3468 | ||
3242 | static int __init bluetooth_init(struct ibm_init_struct *iibm) | 3469 | static int __init bluetooth_init(struct ibm_init_struct *iibm) |
@@ -3277,20 +3504,18 @@ static int __init bluetooth_init(struct ibm_init_struct *iibm) | |||
3277 | if (!tp_features.bluetooth) | 3504 | if (!tp_features.bluetooth) |
3278 | return 1; | 3505 | return 1; |
3279 | 3506 | ||
3280 | res = sysfs_create_group(&tpacpi_pdev->dev.kobj, | ||
3281 | &bluetooth_attr_group); | ||
3282 | if (res) | ||
3283 | return res; | ||
3284 | |||
3285 | res = tpacpi_new_rfkill(TPACPI_RFK_BLUETOOTH_SW_ID, | 3507 | res = tpacpi_new_rfkill(TPACPI_RFK_BLUETOOTH_SW_ID, |
3286 | &tpacpi_bluetooth_rfkill, | 3508 | &bluetooth_tprfk_ops, |
3287 | RFKILL_TYPE_BLUETOOTH, | 3509 | RFKILL_TYPE_BLUETOOTH, |
3288 | TPACPI_RFK_BLUETOOTH_SW_NAME, | 3510 | TPACPI_RFK_BLUETOOTH_SW_NAME, |
3289 | true, | 3511 | true); |
3290 | tpacpi_bluetooth_rfk_set, | 3512 | if (res) |
3291 | tpacpi_bluetooth_rfk_get); | 3513 | return res; |
3514 | |||
3515 | res = sysfs_create_group(&tpacpi_pdev->dev.kobj, | ||
3516 | &bluetooth_attr_group); | ||
3292 | if (res) { | 3517 | if (res) { |
3293 | bluetooth_exit(); | 3518 | tpacpi_destroy_rfkill(TPACPI_RFK_BLUETOOTH_SW_ID); |
3294 | return res; | 3519 | return res; |
3295 | } | 3520 | } |
3296 | 3521 | ||
@@ -3300,46 +3525,12 @@ static int __init bluetooth_init(struct ibm_init_struct *iibm) | |||
3300 | /* procfs -------------------------------------------------------------- */ | 3525 | /* procfs -------------------------------------------------------------- */ |
3301 | static int bluetooth_read(char *p) | 3526 | static int bluetooth_read(char *p) |
3302 | { | 3527 | { |
3303 | int len = 0; | 3528 | return tpacpi_rfk_procfs_read(TPACPI_RFK_BLUETOOTH_SW_ID, p); |
3304 | int status = bluetooth_get_radiosw(); | ||
3305 | |||
3306 | if (!tp_features.bluetooth) | ||
3307 | len += sprintf(p + len, "status:\t\tnot supported\n"); | ||
3308 | else { | ||
3309 | len += sprintf(p + len, "status:\t\t%s\n", | ||
3310 | (status == RFKILL_STATE_UNBLOCKED) ? | ||
3311 | "enabled" : "disabled"); | ||
3312 | len += sprintf(p + len, "commands:\tenable, disable\n"); | ||
3313 | } | ||
3314 | |||
3315 | return len; | ||
3316 | } | 3529 | } |
3317 | 3530 | ||
3318 | static int bluetooth_write(char *buf) | 3531 | static int bluetooth_write(char *buf) |
3319 | { | 3532 | { |
3320 | char *cmd; | 3533 | return tpacpi_rfk_procfs_write(TPACPI_RFK_BLUETOOTH_SW_ID, buf); |
3321 | int state = -1; | ||
3322 | |||
3323 | if (!tp_features.bluetooth) | ||
3324 | return -ENODEV; | ||
3325 | |||
3326 | while ((cmd = next_cmd(&buf))) { | ||
3327 | if (strlencmp(cmd, "enable") == 0) { | ||
3328 | state = 1; | ||
3329 | } else if (strlencmp(cmd, "disable") == 0) { | ||
3330 | state = 0; | ||
3331 | } else | ||
3332 | return -EINVAL; | ||
3333 | } | ||
3334 | |||
3335 | if (state != -1) { | ||
3336 | tpacpi_disclose_usertask("procfs bluetooth", | ||
3337 | "attempt to %s\n", | ||
3338 | state ? "enable" : "disable"); | ||
3339 | bluetooth_set_radiosw(state, 1); | ||
3340 | } | ||
3341 | |||
3342 | return 0; | ||
3343 | } | 3534 | } |
3344 | 3535 | ||
3345 | static struct ibm_struct bluetooth_driver_data = { | 3536 | static struct ibm_struct bluetooth_driver_data = { |
@@ -3365,8 +3556,6 @@ enum { | |||
3365 | 3556 | ||
3366 | #define TPACPI_RFK_WWAN_SW_NAME "tpacpi_wwan_sw" | 3557 | #define TPACPI_RFK_WWAN_SW_NAME "tpacpi_wwan_sw" |
3367 | 3558 | ||
3368 | static struct rfkill *tpacpi_wan_rfkill; | ||
3369 | |||
3370 | static void wan_suspend(pm_message_t state) | 3559 | static void wan_suspend(pm_message_t state) |
3371 | { | 3560 | { |
3372 | /* Try to make sure radio will resume powered off */ | 3561 | /* Try to make sure radio will resume powered off */ |
@@ -3376,83 +3565,47 @@ static void wan_suspend(pm_message_t state) | |||
3376 | "WWAN power down on resume request failed\n"); | 3565 | "WWAN power down on resume request failed\n"); |
3377 | } | 3566 | } |
3378 | 3567 | ||
3379 | static int wan_get_radiosw(void) | 3568 | static int wan_get_status(void) |
3380 | { | 3569 | { |
3381 | int status; | 3570 | int status; |
3382 | 3571 | ||
3383 | if (!tp_features.wan) | ||
3384 | return -ENODEV; | ||
3385 | |||
3386 | /* WLSW overrides WWAN in firmware/hardware, reflect that */ | ||
3387 | if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status) | ||
3388 | return RFKILL_STATE_HARD_BLOCKED; | ||
3389 | |||
3390 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | 3572 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES |
3391 | if (dbg_wwanemul) | 3573 | if (dbg_wwanemul) |
3392 | return (tpacpi_wwan_emulstate) ? | 3574 | return (tpacpi_wwan_emulstate) ? |
3393 | RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED; | 3575 | TPACPI_RFK_RADIO_ON : TPACPI_RFK_RADIO_OFF; |
3394 | #endif | 3576 | #endif |
3395 | 3577 | ||
3396 | if (!acpi_evalf(hkey_handle, &status, "GWAN", "d")) | 3578 | if (!acpi_evalf(hkey_handle, &status, "GWAN", "d")) |
3397 | return -EIO; | 3579 | return -EIO; |
3398 | 3580 | ||
3399 | return ((status & TP_ACPI_WANCARD_RADIOSSW) != 0) ? | 3581 | return ((status & TP_ACPI_WANCARD_RADIOSSW) != 0) ? |
3400 | RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED; | 3582 | TPACPI_RFK_RADIO_ON : TPACPI_RFK_RADIO_OFF; |
3401 | } | ||
3402 | |||
3403 | static void wan_update_rfk(void) | ||
3404 | { | ||
3405 | int status; | ||
3406 | |||
3407 | if (!tpacpi_wan_rfkill) | ||
3408 | return; | ||
3409 | |||
3410 | status = wan_get_radiosw(); | ||
3411 | if (status < 0) | ||
3412 | return; | ||
3413 | rfkill_force_state(tpacpi_wan_rfkill, status); | ||
3414 | |||
3415 | vdbg_printk(TPACPI_DBG_RFKILL, | ||
3416 | "forced rfkill state to %d\n", | ||
3417 | status); | ||
3418 | } | 3583 | } |
3419 | 3584 | ||
3420 | static int wan_set_radiosw(int radio_on, int update_rfk) | 3585 | static int wan_set_status(enum tpacpi_rfkill_state state) |
3421 | { | 3586 | { |
3422 | int status; | 3587 | int status; |
3423 | 3588 | ||
3424 | if (!tp_features.wan) | ||
3425 | return -ENODEV; | ||
3426 | |||
3427 | /* WLSW overrides bluetooth in firmware/hardware, but there is no | ||
3428 | * reason to risk weird behaviour. */ | ||
3429 | if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status | ||
3430 | && radio_on) | ||
3431 | return -EPERM; | ||
3432 | |||
3433 | vdbg_printk(TPACPI_DBG_RFKILL, | 3589 | vdbg_printk(TPACPI_DBG_RFKILL, |
3434 | "will %s WWAN\n", radio_on ? "enable" : "disable"); | 3590 | "will attempt to %s wwan\n", |
3591 | (state == TPACPI_RFK_RADIO_ON) ? "enable" : "disable"); | ||
3435 | 3592 | ||
3436 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | 3593 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES |
3437 | if (dbg_wwanemul) { | 3594 | if (dbg_wwanemul) { |
3438 | tpacpi_wwan_emulstate = !!radio_on; | 3595 | tpacpi_wwan_emulstate = (state == TPACPI_RFK_RADIO_ON); |
3439 | if (update_rfk) | ||
3440 | wan_update_rfk(); | ||
3441 | return 0; | 3596 | return 0; |
3442 | } | 3597 | } |
3443 | #endif | 3598 | #endif |
3444 | 3599 | ||
3445 | /* We make sure to keep TP_ACPI_WANCARD_RESUMECTRL off */ | 3600 | /* We make sure to keep TP_ACPI_WANCARD_RESUMECTRL off */ |
3446 | if (radio_on) | 3601 | if (state == TPACPI_RFK_RADIO_ON) |
3447 | status = TP_ACPI_WANCARD_RADIOSSW; | 3602 | status = TP_ACPI_WANCARD_RADIOSSW; |
3448 | else | 3603 | else |
3449 | status = 0; | 3604 | status = 0; |
3605 | |||
3450 | if (!acpi_evalf(hkey_handle, NULL, "SWAN", "vd", status)) | 3606 | if (!acpi_evalf(hkey_handle, NULL, "SWAN", "vd", status)) |
3451 | return -EIO; | 3607 | return -EIO; |
3452 | 3608 | ||
3453 | if (update_rfk) | ||
3454 | wan_update_rfk(); | ||
3455 | |||
3456 | return 0; | 3609 | return 0; |
3457 | } | 3610 | } |
3458 | 3611 | ||
@@ -3461,35 +3614,16 @@ static ssize_t wan_enable_show(struct device *dev, | |||
3461 | struct device_attribute *attr, | 3614 | struct device_attribute *attr, |
3462 | char *buf) | 3615 | char *buf) |
3463 | { | 3616 | { |
3464 | int status; | 3617 | return tpacpi_rfk_sysfs_enable_show(TPACPI_RFK_WWAN_SW_ID, |
3465 | 3618 | attr, buf); | |
3466 | printk_deprecated_rfkill_attribute("wwan_enable"); | ||
3467 | |||
3468 | status = wan_get_radiosw(); | ||
3469 | if (status < 0) | ||
3470 | return status; | ||
3471 | |||
3472 | return snprintf(buf, PAGE_SIZE, "%d\n", | ||
3473 | (status == RFKILL_STATE_UNBLOCKED) ? 1 : 0); | ||
3474 | } | 3619 | } |
3475 | 3620 | ||
3476 | static ssize_t wan_enable_store(struct device *dev, | 3621 | static ssize_t wan_enable_store(struct device *dev, |
3477 | struct device_attribute *attr, | 3622 | struct device_attribute *attr, |
3478 | const char *buf, size_t count) | 3623 | const char *buf, size_t count) |
3479 | { | 3624 | { |
3480 | unsigned long t; | 3625 | return tpacpi_rfk_sysfs_enable_store(TPACPI_RFK_WWAN_SW_ID, |
3481 | int res; | 3626 | attr, buf, count); |
3482 | |||
3483 | printk_deprecated_rfkill_attribute("wwan_enable"); | ||
3484 | |||
3485 | if (parse_strtoul(buf, 1, &t)) | ||
3486 | return -EINVAL; | ||
3487 | |||
3488 | tpacpi_disclose_usertask("wwan_enable", "set to %ld\n", t); | ||
3489 | |||
3490 | res = wan_set_radiosw(t, 1); | ||
3491 | |||
3492 | return (res) ? res : count; | ||
3493 | } | 3627 | } |
3494 | 3628 | ||
3495 | static struct device_attribute dev_attr_wan_enable = | 3629 | static struct device_attribute dev_attr_wan_enable = |
@@ -3507,23 +3641,10 @@ static const struct attribute_group wan_attr_group = { | |||
3507 | .attrs = wan_attributes, | 3641 | .attrs = wan_attributes, |
3508 | }; | 3642 | }; |
3509 | 3643 | ||
3510 | static int tpacpi_wan_rfk_get(void *data, enum rfkill_state *state) | 3644 | static const struct tpacpi_rfk_ops wan_tprfk_ops = { |
3511 | { | 3645 | .get_status = wan_get_status, |
3512 | int wans = wan_get_radiosw(); | 3646 | .set_status = wan_set_status, |
3513 | 3647 | }; | |
3514 | if (wans < 0) | ||
3515 | return wans; | ||
3516 | |||
3517 | *state = wans; | ||
3518 | return 0; | ||
3519 | } | ||
3520 | |||
3521 | static int tpacpi_wan_rfk_set(void *data, enum rfkill_state state) | ||
3522 | { | ||
3523 | dbg_printk(TPACPI_DBG_RFKILL, | ||
3524 | "request to change radio state to %d\n", state); | ||
3525 | return wan_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0); | ||
3526 | } | ||
3527 | 3648 | ||
3528 | static void wan_shutdown(void) | 3649 | static void wan_shutdown(void) |
3529 | { | 3650 | { |
@@ -3539,13 +3660,12 @@ static void wan_shutdown(void) | |||
3539 | 3660 | ||
3540 | static void wan_exit(void) | 3661 | static void wan_exit(void) |
3541 | { | 3662 | { |
3542 | wan_shutdown(); | ||
3543 | |||
3544 | if (tpacpi_wan_rfkill) | ||
3545 | rfkill_unregister(tpacpi_wan_rfkill); | ||
3546 | |||
3547 | sysfs_remove_group(&tpacpi_pdev->dev.kobj, | 3663 | sysfs_remove_group(&tpacpi_pdev->dev.kobj, |
3548 | &wan_attr_group); | 3664 | &wan_attr_group); |
3665 | |||
3666 | tpacpi_destroy_rfkill(TPACPI_RFK_WWAN_SW_ID); | ||
3667 | |||
3668 | wan_shutdown(); | ||
3549 | } | 3669 | } |
3550 | 3670 | ||
3551 | static int __init wan_init(struct ibm_init_struct *iibm) | 3671 | static int __init wan_init(struct ibm_init_struct *iibm) |
@@ -3584,20 +3704,19 @@ static int __init wan_init(struct ibm_init_struct *iibm) | |||
3584 | if (!tp_features.wan) | 3704 | if (!tp_features.wan) |
3585 | return 1; | 3705 | return 1; |
3586 | 3706 | ||
3587 | res = sysfs_create_group(&tpacpi_pdev->dev.kobj, | ||
3588 | &wan_attr_group); | ||
3589 | if (res) | ||
3590 | return res; | ||
3591 | |||
3592 | res = tpacpi_new_rfkill(TPACPI_RFK_WWAN_SW_ID, | 3707 | res = tpacpi_new_rfkill(TPACPI_RFK_WWAN_SW_ID, |
3593 | &tpacpi_wan_rfkill, | 3708 | &wan_tprfk_ops, |
3594 | RFKILL_TYPE_WWAN, | 3709 | RFKILL_TYPE_WWAN, |
3595 | TPACPI_RFK_WWAN_SW_NAME, | 3710 | TPACPI_RFK_WWAN_SW_NAME, |
3596 | true, | 3711 | true); |
3597 | tpacpi_wan_rfk_set, | 3712 | if (res) |
3598 | tpacpi_wan_rfk_get); | 3713 | return res; |
3714 | |||
3715 | res = sysfs_create_group(&tpacpi_pdev->dev.kobj, | ||
3716 | &wan_attr_group); | ||
3717 | |||
3599 | if (res) { | 3718 | if (res) { |
3600 | wan_exit(); | 3719 | tpacpi_destroy_rfkill(TPACPI_RFK_WWAN_SW_ID); |
3601 | return res; | 3720 | return res; |
3602 | } | 3721 | } |
3603 | 3722 | ||
@@ -3607,48 +3726,12 @@ static int __init wan_init(struct ibm_init_struct *iibm) | |||
3607 | /* procfs -------------------------------------------------------------- */ | 3726 | /* procfs -------------------------------------------------------------- */ |
3608 | static int wan_read(char *p) | 3727 | static int wan_read(char *p) |
3609 | { | 3728 | { |
3610 | int len = 0; | 3729 | return tpacpi_rfk_procfs_read(TPACPI_RFK_WWAN_SW_ID, p); |
3611 | int status = wan_get_radiosw(); | ||
3612 | |||
3613 | tpacpi_disclose_usertask("procfs wan", "read"); | ||
3614 | |||
3615 | if (!tp_features.wan) | ||
3616 | len += sprintf(p + len, "status:\t\tnot supported\n"); | ||
3617 | else { | ||
3618 | len += sprintf(p + len, "status:\t\t%s\n", | ||
3619 | (status == RFKILL_STATE_UNBLOCKED) ? | ||
3620 | "enabled" : "disabled"); | ||
3621 | len += sprintf(p + len, "commands:\tenable, disable\n"); | ||
3622 | } | ||
3623 | |||
3624 | return len; | ||
3625 | } | 3730 | } |
3626 | 3731 | ||
3627 | static int wan_write(char *buf) | 3732 | static int wan_write(char *buf) |
3628 | { | 3733 | { |
3629 | char *cmd; | 3734 | return tpacpi_rfk_procfs_write(TPACPI_RFK_WWAN_SW_ID, buf); |
3630 | int state = -1; | ||
3631 | |||
3632 | if (!tp_features.wan) | ||
3633 | return -ENODEV; | ||
3634 | |||
3635 | while ((cmd = next_cmd(&buf))) { | ||
3636 | if (strlencmp(cmd, "enable") == 0) { | ||
3637 | state = 1; | ||
3638 | } else if (strlencmp(cmd, "disable") == 0) { | ||
3639 | state = 0; | ||
3640 | } else | ||
3641 | return -EINVAL; | ||
3642 | } | ||
3643 | |||
3644 | if (state != -1) { | ||
3645 | tpacpi_disclose_usertask("procfs wan", | ||
3646 | "attempt to %s\n", | ||
3647 | state ? "enable" : "disable"); | ||
3648 | wan_set_radiosw(state, 1); | ||
3649 | } | ||
3650 | |||
3651 | return 0; | ||
3652 | } | 3735 | } |
3653 | 3736 | ||
3654 | static struct ibm_struct wan_driver_data = { | 3737 | static struct ibm_struct wan_driver_data = { |
@@ -3672,108 +3755,59 @@ enum { | |||
3672 | 3755 | ||
3673 | #define TPACPI_RFK_UWB_SW_NAME "tpacpi_uwb_sw" | 3756 | #define TPACPI_RFK_UWB_SW_NAME "tpacpi_uwb_sw" |
3674 | 3757 | ||
3675 | static struct rfkill *tpacpi_uwb_rfkill; | 3758 | static int uwb_get_status(void) |
3676 | |||
3677 | static int uwb_get_radiosw(void) | ||
3678 | { | 3759 | { |
3679 | int status; | 3760 | int status; |
3680 | 3761 | ||
3681 | if (!tp_features.uwb) | ||
3682 | return -ENODEV; | ||
3683 | |||
3684 | /* WLSW overrides UWB in firmware/hardware, reflect that */ | ||
3685 | if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status) | ||
3686 | return RFKILL_STATE_HARD_BLOCKED; | ||
3687 | |||
3688 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | 3762 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES |
3689 | if (dbg_uwbemul) | 3763 | if (dbg_uwbemul) |
3690 | return (tpacpi_uwb_emulstate) ? | 3764 | return (tpacpi_uwb_emulstate) ? |
3691 | RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED; | 3765 | TPACPI_RFK_RADIO_ON : TPACPI_RFK_RADIO_OFF; |
3692 | #endif | 3766 | #endif |
3693 | 3767 | ||
3694 | if (!acpi_evalf(hkey_handle, &status, "GUWB", "d")) | 3768 | if (!acpi_evalf(hkey_handle, &status, "GUWB", "d")) |
3695 | return -EIO; | 3769 | return -EIO; |
3696 | 3770 | ||
3697 | return ((status & TP_ACPI_UWB_RADIOSSW) != 0) ? | 3771 | return ((status & TP_ACPI_UWB_RADIOSSW) != 0) ? |
3698 | RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED; | 3772 | TPACPI_RFK_RADIO_ON : TPACPI_RFK_RADIO_OFF; |
3699 | } | 3773 | } |
3700 | 3774 | ||
3701 | static void uwb_update_rfk(void) | 3775 | static int uwb_set_status(enum tpacpi_rfkill_state state) |
3702 | { | 3776 | { |
3703 | int status; | 3777 | int status; |
3704 | 3778 | ||
3705 | if (!tpacpi_uwb_rfkill) | ||
3706 | return; | ||
3707 | |||
3708 | status = uwb_get_radiosw(); | ||
3709 | if (status < 0) | ||
3710 | return; | ||
3711 | rfkill_force_state(tpacpi_uwb_rfkill, status); | ||
3712 | |||
3713 | vdbg_printk(TPACPI_DBG_RFKILL, | 3779 | vdbg_printk(TPACPI_DBG_RFKILL, |
3714 | "forced rfkill state to %d\n", | 3780 | "will attempt to %s UWB\n", |
3715 | status); | 3781 | (state == TPACPI_RFK_RADIO_ON) ? "enable" : "disable"); |
3716 | } | ||
3717 | |||
3718 | static int uwb_set_radiosw(int radio_on, int update_rfk) | ||
3719 | { | ||
3720 | int status; | ||
3721 | |||
3722 | if (!tp_features.uwb) | ||
3723 | return -ENODEV; | ||
3724 | |||
3725 | /* WLSW overrides UWB in firmware/hardware, but there is no | ||
3726 | * reason to risk weird behaviour. */ | ||
3727 | if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status | ||
3728 | && radio_on) | ||
3729 | return -EPERM; | ||
3730 | |||
3731 | vdbg_printk(TPACPI_DBG_RFKILL, | ||
3732 | "will %s UWB\n", radio_on ? "enable" : "disable"); | ||
3733 | 3782 | ||
3734 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | 3783 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES |
3735 | if (dbg_uwbemul) { | 3784 | if (dbg_uwbemul) { |
3736 | tpacpi_uwb_emulstate = !!radio_on; | 3785 | tpacpi_uwb_emulstate = (state == TPACPI_RFK_RADIO_ON); |
3737 | if (update_rfk) | ||
3738 | uwb_update_rfk(); | ||
3739 | return 0; | 3786 | return 0; |
3740 | } | 3787 | } |
3741 | #endif | 3788 | #endif |
3742 | 3789 | ||
3743 | status = (radio_on) ? TP_ACPI_UWB_RADIOSSW : 0; | 3790 | if (state == TPACPI_RFK_RADIO_ON) |
3791 | status = TP_ACPI_UWB_RADIOSSW; | ||
3792 | else | ||
3793 | status = 0; | ||
3794 | |||
3744 | if (!acpi_evalf(hkey_handle, NULL, "SUWB", "vd", status)) | 3795 | if (!acpi_evalf(hkey_handle, NULL, "SUWB", "vd", status)) |
3745 | return -EIO; | 3796 | return -EIO; |
3746 | 3797 | ||
3747 | if (update_rfk) | ||
3748 | uwb_update_rfk(); | ||
3749 | |||
3750 | return 0; | 3798 | return 0; |
3751 | } | 3799 | } |
3752 | 3800 | ||
3753 | /* --------------------------------------------------------------------- */ | 3801 | /* --------------------------------------------------------------------- */ |
3754 | 3802 | ||
3755 | static int tpacpi_uwb_rfk_get(void *data, enum rfkill_state *state) | 3803 | static const struct tpacpi_rfk_ops uwb_tprfk_ops = { |
3756 | { | 3804 | .get_status = uwb_get_status, |
3757 | int uwbs = uwb_get_radiosw(); | 3805 | .set_status = uwb_set_status, |
3758 | 3806 | }; | |
3759 | if (uwbs < 0) | ||
3760 | return uwbs; | ||
3761 | |||
3762 | *state = uwbs; | ||
3763 | return 0; | ||
3764 | } | ||
3765 | |||
3766 | static int tpacpi_uwb_rfk_set(void *data, enum rfkill_state state) | ||
3767 | { | ||
3768 | dbg_printk(TPACPI_DBG_RFKILL, | ||
3769 | "request to change radio state to %d\n", state); | ||
3770 | return uwb_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0); | ||
3771 | } | ||
3772 | 3807 | ||
3773 | static void uwb_exit(void) | 3808 | static void uwb_exit(void) |
3774 | { | 3809 | { |
3775 | if (tpacpi_uwb_rfkill) | 3810 | tpacpi_destroy_rfkill(TPACPI_RFK_UWB_SW_ID); |
3776 | rfkill_unregister(tpacpi_uwb_rfkill); | ||
3777 | } | 3811 | } |
3778 | 3812 | ||
3779 | static int __init uwb_init(struct ibm_init_struct *iibm) | 3813 | static int __init uwb_init(struct ibm_init_struct *iibm) |
@@ -3813,13 +3847,10 @@ static int __init uwb_init(struct ibm_init_struct *iibm) | |||
3813 | return 1; | 3847 | return 1; |
3814 | 3848 | ||
3815 | res = tpacpi_new_rfkill(TPACPI_RFK_UWB_SW_ID, | 3849 | res = tpacpi_new_rfkill(TPACPI_RFK_UWB_SW_ID, |
3816 | &tpacpi_uwb_rfkill, | 3850 | &uwb_tprfk_ops, |
3817 | RFKILL_TYPE_UWB, | 3851 | RFKILL_TYPE_UWB, |
3818 | TPACPI_RFK_UWB_SW_NAME, | 3852 | TPACPI_RFK_UWB_SW_NAME, |
3819 | false, | 3853 | false); |
3820 | tpacpi_uwb_rfk_set, | ||
3821 | tpacpi_uwb_rfk_get); | ||
3822 | |||
3823 | return res; | 3854 | return res; |
3824 | } | 3855 | } |
3825 | 3856 | ||