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/net/wireless/ath/ath9k | |
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/net/wireless/ath/ath9k')
-rw-r--r-- | drivers/net/wireless/ath/ath9k/ath9k.h | 7 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath9k/main.c | 115 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath9k/pci.c | 15 |
3 files changed, 30 insertions, 107 deletions
diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 796a3adffea0..515880aa2116 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h | |||
@@ -460,12 +460,9 @@ struct ath_led { | |||
460 | bool registered; | 460 | bool registered; |
461 | }; | 461 | }; |
462 | 462 | ||
463 | /* Rfkill */ | ||
464 | #define ATH_RFKILL_POLL_INTERVAL 2000 /* msecs */ | ||
465 | |||
466 | struct ath_rfkill { | 463 | struct ath_rfkill { |
467 | struct rfkill *rfkill; | 464 | struct rfkill *rfkill; |
468 | struct delayed_work rfkill_poll; | 465 | struct rfkill_ops ops; |
469 | char rfkill_name[32]; | 466 | char rfkill_name[32]; |
470 | }; | 467 | }; |
471 | 468 | ||
@@ -509,8 +506,6 @@ struct ath_rfkill { | |||
509 | #define SC_OP_RXFLUSH BIT(7) | 506 | #define SC_OP_RXFLUSH BIT(7) |
510 | #define SC_OP_LED_ASSOCIATED BIT(8) | 507 | #define SC_OP_LED_ASSOCIATED BIT(8) |
511 | #define SC_OP_RFKILL_REGISTERED BIT(9) | 508 | #define SC_OP_RFKILL_REGISTERED BIT(9) |
512 | #define SC_OP_RFKILL_SW_BLOCKED BIT(10) | ||
513 | #define SC_OP_RFKILL_HW_BLOCKED BIT(11) | ||
514 | #define SC_OP_WAIT_FOR_BEACON BIT(12) | 509 | #define SC_OP_WAIT_FOR_BEACON BIT(12) |
515 | #define SC_OP_LED_ON BIT(13) | 510 | #define SC_OP_LED_ON BIT(13) |
516 | #define SC_OP_SCANNING BIT(14) | 511 | #define SC_OP_SCANNING BIT(14) |
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 61da08a1648c..f7baa406918b 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c | |||
@@ -1192,120 +1192,69 @@ static bool ath_is_rfkill_set(struct ath_softc *sc) | |||
1192 | ah->rfkill_polarity; | 1192 | ah->rfkill_polarity; |
1193 | } | 1193 | } |
1194 | 1194 | ||
1195 | /* h/w rfkill poll function */ | 1195 | /* s/w rfkill handlers */ |
1196 | static void ath_rfkill_poll(struct work_struct *work) | 1196 | static int ath_rfkill_set_block(void *data, bool blocked) |
1197 | { | 1197 | { |
1198 | struct ath_softc *sc = container_of(work, struct ath_softc, | 1198 | struct ath_softc *sc = data; |
1199 | rf_kill.rfkill_poll.work); | ||
1200 | bool radio_on; | ||
1201 | |||
1202 | if (sc->sc_flags & SC_OP_INVALID) | ||
1203 | return; | ||
1204 | |||
1205 | radio_on = !ath_is_rfkill_set(sc); | ||
1206 | |||
1207 | /* | ||
1208 | * enable/disable radio only when there is a | ||
1209 | * state change in RF switch | ||
1210 | */ | ||
1211 | if (radio_on == !!(sc->sc_flags & SC_OP_RFKILL_HW_BLOCKED)) { | ||
1212 | enum rfkill_state state; | ||
1213 | |||
1214 | if (sc->sc_flags & SC_OP_RFKILL_SW_BLOCKED) { | ||
1215 | state = radio_on ? RFKILL_STATE_SOFT_BLOCKED | ||
1216 | : RFKILL_STATE_HARD_BLOCKED; | ||
1217 | } else if (radio_on) { | ||
1218 | ath_radio_enable(sc); | ||
1219 | state = RFKILL_STATE_UNBLOCKED; | ||
1220 | } else { | ||
1221 | ath_radio_disable(sc); | ||
1222 | state = RFKILL_STATE_HARD_BLOCKED; | ||
1223 | } | ||
1224 | |||
1225 | if (state == RFKILL_STATE_HARD_BLOCKED) | ||
1226 | sc->sc_flags |= SC_OP_RFKILL_HW_BLOCKED; | ||
1227 | else | ||
1228 | sc->sc_flags &= ~SC_OP_RFKILL_HW_BLOCKED; | ||
1229 | 1199 | ||
1230 | rfkill_force_state(sc->rf_kill.rfkill, state); | 1200 | if (blocked) |
1231 | } | 1201 | ath_radio_disable(sc); |
1202 | else | ||
1203 | ath_radio_enable(sc); | ||
1232 | 1204 | ||
1233 | queue_delayed_work(sc->hw->workqueue, &sc->rf_kill.rfkill_poll, | 1205 | return 0; |
1234 | msecs_to_jiffies(ATH_RFKILL_POLL_INTERVAL)); | ||
1235 | } | 1206 | } |
1236 | 1207 | ||
1237 | /* s/w rfkill handler */ | 1208 | static void ath_rfkill_poll_state(struct rfkill *rfkill, void *data) |
1238 | static int ath_sw_toggle_radio(void *data, enum rfkill_state state) | ||
1239 | { | 1209 | { |
1240 | struct ath_softc *sc = data; | 1210 | struct ath_softc *sc = data; |
1211 | bool blocked = !!ath_is_rfkill_set(sc); | ||
1241 | 1212 | ||
1242 | switch (state) { | 1213 | if (rfkill_set_hw_state(rfkill, blocked)) |
1243 | case RFKILL_STATE_SOFT_BLOCKED: | 1214 | ath_radio_disable(sc); |
1244 | if (!(sc->sc_flags & (SC_OP_RFKILL_HW_BLOCKED | | 1215 | else |
1245 | SC_OP_RFKILL_SW_BLOCKED))) | 1216 | ath_radio_enable(sc); |
1246 | ath_radio_disable(sc); | ||
1247 | sc->sc_flags |= SC_OP_RFKILL_SW_BLOCKED; | ||
1248 | return 0; | ||
1249 | case RFKILL_STATE_UNBLOCKED: | ||
1250 | if ((sc->sc_flags & SC_OP_RFKILL_SW_BLOCKED)) { | ||
1251 | sc->sc_flags &= ~SC_OP_RFKILL_SW_BLOCKED; | ||
1252 | if (sc->sc_flags & SC_OP_RFKILL_HW_BLOCKED) { | ||
1253 | DPRINTF(sc, ATH_DBG_FATAL, "Can't turn on the" | ||
1254 | "radio as it is disabled by h/w\n"); | ||
1255 | return -EPERM; | ||
1256 | } | ||
1257 | ath_radio_enable(sc); | ||
1258 | } | ||
1259 | return 0; | ||
1260 | default: | ||
1261 | return -EINVAL; | ||
1262 | } | ||
1263 | } | 1217 | } |
1264 | 1218 | ||
1265 | /* Init s/w rfkill */ | 1219 | /* Init s/w rfkill */ |
1266 | static int ath_init_sw_rfkill(struct ath_softc *sc) | 1220 | static int ath_init_sw_rfkill(struct ath_softc *sc) |
1267 | { | 1221 | { |
1268 | sc->rf_kill.rfkill = rfkill_allocate(wiphy_dev(sc->hw->wiphy), | 1222 | sc->rf_kill.ops.set_block = ath_rfkill_set_block; |
1269 | RFKILL_TYPE_WLAN); | 1223 | if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT) |
1224 | sc->rf_kill.ops.poll = ath_rfkill_poll_state; | ||
1225 | |||
1226 | snprintf(sc->rf_kill.rfkill_name, sizeof(sc->rf_kill.rfkill_name), | ||
1227 | "ath9k-%s::rfkill", wiphy_name(sc->hw->wiphy)); | ||
1228 | |||
1229 | sc->rf_kill.rfkill = rfkill_alloc(sc->rf_kill.rfkill_name, | ||
1230 | wiphy_dev(sc->hw->wiphy), | ||
1231 | RFKILL_TYPE_WLAN, | ||
1232 | &sc->rf_kill.ops, sc); | ||
1270 | if (!sc->rf_kill.rfkill) { | 1233 | if (!sc->rf_kill.rfkill) { |
1271 | DPRINTF(sc, ATH_DBG_FATAL, "Failed to allocate rfkill\n"); | 1234 | DPRINTF(sc, ATH_DBG_FATAL, "Failed to allocate rfkill\n"); |
1272 | return -ENOMEM; | 1235 | return -ENOMEM; |
1273 | } | 1236 | } |
1274 | 1237 | ||
1275 | snprintf(sc->rf_kill.rfkill_name, sizeof(sc->rf_kill.rfkill_name), | ||
1276 | "ath9k-%s::rfkill", wiphy_name(sc->hw->wiphy)); | ||
1277 | sc->rf_kill.rfkill->name = sc->rf_kill.rfkill_name; | ||
1278 | sc->rf_kill.rfkill->data = sc; | ||
1279 | sc->rf_kill.rfkill->toggle_radio = ath_sw_toggle_radio; | ||
1280 | sc->rf_kill.rfkill->state = RFKILL_STATE_UNBLOCKED; | ||
1281 | |||
1282 | return 0; | 1238 | return 0; |
1283 | } | 1239 | } |
1284 | 1240 | ||
1285 | /* Deinitialize rfkill */ | 1241 | /* Deinitialize rfkill */ |
1286 | static void ath_deinit_rfkill(struct ath_softc *sc) | 1242 | static void ath_deinit_rfkill(struct ath_softc *sc) |
1287 | { | 1243 | { |
1288 | if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT) | ||
1289 | cancel_delayed_work_sync(&sc->rf_kill.rfkill_poll); | ||
1290 | |||
1291 | if (sc->sc_flags & SC_OP_RFKILL_REGISTERED) { | 1244 | if (sc->sc_flags & SC_OP_RFKILL_REGISTERED) { |
1292 | rfkill_unregister(sc->rf_kill.rfkill); | 1245 | rfkill_unregister(sc->rf_kill.rfkill); |
1246 | rfkill_destroy(sc->rf_kill.rfkill); | ||
1293 | sc->sc_flags &= ~SC_OP_RFKILL_REGISTERED; | 1247 | sc->sc_flags &= ~SC_OP_RFKILL_REGISTERED; |
1294 | sc->rf_kill.rfkill = NULL; | ||
1295 | } | 1248 | } |
1296 | } | 1249 | } |
1297 | 1250 | ||
1298 | static int ath_start_rfkill_poll(struct ath_softc *sc) | 1251 | static int ath_start_rfkill_poll(struct ath_softc *sc) |
1299 | { | 1252 | { |
1300 | if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT) | ||
1301 | queue_delayed_work(sc->hw->workqueue, | ||
1302 | &sc->rf_kill.rfkill_poll, 0); | ||
1303 | |||
1304 | if (!(sc->sc_flags & SC_OP_RFKILL_REGISTERED)) { | 1253 | if (!(sc->sc_flags & SC_OP_RFKILL_REGISTERED)) { |
1305 | if (rfkill_register(sc->rf_kill.rfkill)) { | 1254 | if (rfkill_register(sc->rf_kill.rfkill)) { |
1306 | DPRINTF(sc, ATH_DBG_FATAL, | 1255 | DPRINTF(sc, ATH_DBG_FATAL, |
1307 | "Unable to register rfkill\n"); | 1256 | "Unable to register rfkill\n"); |
1308 | rfkill_free(sc->rf_kill.rfkill); | 1257 | rfkill_destroy(sc->rf_kill.rfkill); |
1309 | 1258 | ||
1310 | /* Deinitialize the device */ | 1259 | /* Deinitialize the device */ |
1311 | ath_cleanup(sc); | 1260 | ath_cleanup(sc); |
@@ -1678,10 +1627,6 @@ int ath_attach(u16 devid, struct ath_softc *sc) | |||
1678 | goto error_attach; | 1627 | goto error_attach; |
1679 | 1628 | ||
1680 | #if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE) | 1629 | #if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE) |
1681 | /* Initialze h/w Rfkill */ | ||
1682 | if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT) | ||
1683 | INIT_DELAYED_WORK(&sc->rf_kill.rfkill_poll, ath_rfkill_poll); | ||
1684 | |||
1685 | /* Initialize s/w rfkill */ | 1630 | /* Initialize s/w rfkill */ |
1686 | error = ath_init_sw_rfkill(sc); | 1631 | error = ath_init_sw_rfkill(sc); |
1687 | if (error) | 1632 | if (error) |
@@ -2214,10 +2159,8 @@ static void ath9k_stop(struct ieee80211_hw *hw) | |||
2214 | } else | 2159 | } else |
2215 | sc->rx.rxlink = NULL; | 2160 | sc->rx.rxlink = NULL; |
2216 | 2161 | ||
2217 | #if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE) | 2162 | rfkill_pause_polling(sc->rf_kill.rfkill); |
2218 | if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT) | 2163 | |
2219 | cancel_delayed_work_sync(&sc->rf_kill.rfkill_poll); | ||
2220 | #endif | ||
2221 | /* disable HAL and put h/w to sleep */ | 2164 | /* disable HAL and put h/w to sleep */ |
2222 | ath9k_hw_disable(sc->sc_ah); | 2165 | ath9k_hw_disable(sc->sc_ah); |
2223 | ath9k_hw_configpcipowersave(sc->sc_ah, 1); | 2166 | ath9k_hw_configpcipowersave(sc->sc_ah, 1); |
diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c index 168411d322a2..ccdf20a2e9be 100644 --- a/drivers/net/wireless/ath/ath9k/pci.c +++ b/drivers/net/wireless/ath/ath9k/pci.c | |||
@@ -227,11 +227,6 @@ static int ath_pci_suspend(struct pci_dev *pdev, pm_message_t state) | |||
227 | 227 | ||
228 | ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 1); | 228 | ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 1); |
229 | 229 | ||
230 | #if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE) | ||
231 | if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT) | ||
232 | cancel_delayed_work_sync(&sc->rf_kill.rfkill_poll); | ||
233 | #endif | ||
234 | |||
235 | pci_save_state(pdev); | 230 | pci_save_state(pdev); |
236 | pci_disable_device(pdev); | 231 | pci_disable_device(pdev); |
237 | pci_set_power_state(pdev, PCI_D3hot); | 232 | pci_set_power_state(pdev, PCI_D3hot); |
@@ -256,16 +251,6 @@ static int ath_pci_resume(struct pci_dev *pdev) | |||
256 | AR_GPIO_OUTPUT_MUX_AS_OUTPUT); | 251 | AR_GPIO_OUTPUT_MUX_AS_OUTPUT); |
257 | ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 1); | 252 | ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 1); |
258 | 253 | ||
259 | #if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE) | ||
260 | /* | ||
261 | * check the h/w rfkill state on resume | ||
262 | * and start the rfkill poll timer | ||
263 | */ | ||
264 | if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT) | ||
265 | queue_delayed_work(sc->hw->workqueue, | ||
266 | &sc->rf_kill.rfkill_poll, 0); | ||
267 | #endif | ||
268 | |||
269 | return 0; | 254 | return 0; |
270 | } | 255 | } |
271 | 256 | ||