aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/ath/ath9k
diff options
context:
space:
mode:
authorJohannes Berg <johannes@sipsolutions.net>2009-06-02 07:01:37 -0400
committerJohn W. Linville <linville@tuxdriver.com>2009-06-03 14:06:13 -0400
commit19d337dff95cbf76edd3ad95c0cee2732c3e1ec5 (patch)
tree33326eeb09cb9664cc8427a5dc7cd2b08b5a57c3 /drivers/net/wireless/ath/ath9k
parent0f6399c4c525b518644a9b09f8d6fb125a418c4d (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.h7
-rw-r--r--drivers/net/wireless/ath/ath9k/main.c115
-rw-r--r--drivers/net/wireless/ath/ath9k/pci.c15
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
466struct ath_rfkill { 463struct 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 */
1196static void ath_rfkill_poll(struct work_struct *work) 1196static 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 */ 1208static void ath_rfkill_poll_state(struct rfkill *rfkill, void *data)
1238static 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 */
1266static int ath_init_sw_rfkill(struct ath_softc *sc) 1220static 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 */
1286static void ath_deinit_rfkill(struct ath_softc *sc) 1242static 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
1298static int ath_start_rfkill_poll(struct ath_softc *sc) 1251static 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