aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/ath9k
diff options
context:
space:
mode:
authorJouni Malinen <jouni.malinen@atheros.com>2009-03-03 12:23:32 -0500
committerJohn W. Linville <linville@tuxdriver.com>2009-03-05 14:39:46 -0500
commit0e2dedf971f3feefd4d3d3d8cb5c57b1757f1101 (patch)
tree8ae3400d17331c000ac204ab85a970434f5e5233 /drivers/net/wireless/ath9k
parentf0ed85c6c7960b26666db013e02e748b56eef98a (diff)
ath9k: Add routines for switching between active virtual wiphys
ath9k_wiphy_select() can be used to select a virtual wiphy to be activated. Other virtual wiphys will be paused and once that is done, the operational channel is changed and the wiphys that are on the selected channel will be unpaused. Signed-off-by: Jouni Malinen <jouni.malinen@atheros.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/ath9k')
-rw-r--r--drivers/net/wireless/ath9k/ath9k.h14
-rw-r--r--drivers/net/wireless/ath9k/main.c27
-rw-r--r--drivers/net/wireless/ath9k/virtual.c147
3 files changed, 176 insertions, 12 deletions
diff --git a/drivers/net/wireless/ath9k/ath9k.h b/drivers/net/wireless/ath9k/ath9k.h
index 1153374f94fb..bc25075913ad 100644
--- a/drivers/net/wireless/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath9k/ath9k.h
@@ -563,6 +563,11 @@ struct ath_softc {
563 struct ath_wiphy **sec_wiphy; /* secondary wiphys (virtual radios); may 563 struct ath_wiphy **sec_wiphy; /* secondary wiphys (virtual radios); may
564 * have NULL entries */ 564 * have NULL entries */
565 int num_sec_wiphy; /* number of sec_wiphy pointers in the array */ 565 int num_sec_wiphy; /* number of sec_wiphy pointers in the array */
566 int chan_idx;
567 int chan_is_ht;
568 struct ath_wiphy *next_wiphy;
569 struct work_struct chan_work;
570
566 struct tasklet_struct intr_tq; 571 struct tasklet_struct intr_tq;
567 struct tasklet_struct bcon_tasklet; 572 struct tasklet_struct bcon_tasklet;
568 struct ath_hw *sc_ah; 573 struct ath_hw *sc_ah;
@@ -626,6 +631,8 @@ struct ath_wiphy {
626 ATH_WIPHY_PAUSING, 631 ATH_WIPHY_PAUSING,
627 ATH_WIPHY_PAUSED, 632 ATH_WIPHY_PAUSED,
628 } state; 633 } state;
634 int chan_idx;
635 int chan_is_ht;
629}; 636};
630 637
631int ath_reset(struct ath_softc *sc, bool retry_tx); 638int ath_reset(struct ath_softc *sc, bool retry_tx);
@@ -652,6 +659,11 @@ void ath_detach(struct ath_softc *sc);
652const char *ath_mac_bb_name(u32 mac_bb_version); 659const char *ath_mac_bb_name(u32 mac_bb_version);
653const char *ath_rf_name(u16 rf_version); 660const char *ath_rf_name(u16 rf_version);
654void ath_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw); 661void ath_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw);
662void ath9k_update_ichannel(struct ath_softc *sc, struct ieee80211_hw *hw,
663 struct ath9k_channel *ichan);
664void ath_update_chainmask(struct ath_softc *sc, int is_ht);
665int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
666 struct ath9k_channel *hchan);
655 667
656#ifdef CONFIG_PCI 668#ifdef CONFIG_PCI
657int ath_pci_init(void); 669int ath_pci_init(void);
@@ -694,5 +706,7 @@ int ath9k_wiphy_del(struct ath_wiphy *aphy);
694void ath9k_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb); 706void ath9k_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb);
695int ath9k_wiphy_pause(struct ath_wiphy *aphy); 707int ath9k_wiphy_pause(struct ath_wiphy *aphy);
696int ath9k_wiphy_unpause(struct ath_wiphy *aphy); 708int ath9k_wiphy_unpause(struct ath_wiphy *aphy);
709int ath9k_wiphy_select(struct ath_wiphy *aphy);
710void ath9k_wiphy_chan_work(struct work_struct *work);
697 711
698#endif /* ATH9K_H */ 712#endif /* ATH9K_H */
diff --git a/drivers/net/wireless/ath9k/main.c b/drivers/net/wireless/ath9k/main.c
index 7e44013ba6e7..44959010c547 100644
--- a/drivers/net/wireless/ath9k/main.c
+++ b/drivers/net/wireless/ath9k/main.c
@@ -236,11 +236,11 @@ static void ath_setup_rates(struct ath_softc *sc, enum ieee80211_band band)
236 * by reseting the chip. To accomplish this we must first cleanup any pending 236 * by reseting the chip. To accomplish this we must first cleanup any pending
237 * DMA, then restart stuff. 237 * DMA, then restart stuff.
238*/ 238*/
239static int ath_set_channel(struct ath_softc *sc, struct ath9k_channel *hchan) 239int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
240 struct ath9k_channel *hchan)
240{ 241{
241 struct ath_hw *ah = sc->sc_ah; 242 struct ath_hw *ah = sc->sc_ah;
242 bool fastcc = true, stopped; 243 bool fastcc = true, stopped;
243 struct ieee80211_hw *hw = sc->hw;
244 struct ieee80211_channel *channel = hw->conf.channel; 244 struct ieee80211_channel *channel = hw->conf.channel;
245 int r; 245 int r;
246 246
@@ -414,7 +414,7 @@ set_timer:
414 * the chainmask configuration, for bt coexistence, use 414 * the chainmask configuration, for bt coexistence, use
415 * the chainmask configuration even in legacy mode. 415 * the chainmask configuration even in legacy mode.
416 */ 416 */
417static void ath_update_chainmask(struct ath_softc *sc, int is_ht) 417void ath_update_chainmask(struct ath_softc *sc, int is_ht)
418{ 418{
419 sc->sc_flags |= SC_OP_CHAINMASK_UPDATE; 419 sc->sc_flags |= SC_OP_CHAINMASK_UPDATE;
420 if (is_ht || 420 if (is_ht ||
@@ -1324,6 +1324,7 @@ void ath_detach(struct ath_softc *sc)
1324 ath_deinit_rfkill(sc); 1324 ath_deinit_rfkill(sc);
1325#endif 1325#endif
1326 ath_deinit_leds(sc); 1326 ath_deinit_leds(sc);
1327 cancel_work_sync(&sc->chan_work);
1327 1328
1328 for (i = 0; i < sc->num_sec_wiphy; i++) { 1329 for (i = 0; i < sc->num_sec_wiphy; i++) {
1329 struct ath_wiphy *aphy = sc->sec_wiphy[i]; 1330 struct ath_wiphy *aphy = sc->sec_wiphy[i];
@@ -1669,6 +1670,8 @@ int ath_attach(u16 devid, struct ath_softc *sc)
1669 ath9k_reg_apply_radar_flags(hw->wiphy); 1670 ath9k_reg_apply_radar_flags(hw->wiphy);
1670 ath9k_reg_apply_world_flags(hw->wiphy, REGDOM_SET_BY_INIT); 1671 ath9k_reg_apply_world_flags(hw->wiphy, REGDOM_SET_BY_INIT);
1671 1672
1673 INIT_WORK(&sc->chan_work, ath9k_wiphy_chan_work);
1674
1672 error = ieee80211_register_hw(hw); 1675 error = ieee80211_register_hw(hw);
1673 1676
1674 if (!ath9k_is_world_regd(sc->sc_ah)) { 1677 if (!ath9k_is_world_regd(sc->sc_ah)) {
@@ -1917,10 +1920,9 @@ int ath_get_mac80211_qnum(u32 queue, struct ath_softc *sc)
1917 1920
1918/* XXX: Remove me once we don't depend on ath9k_channel for all 1921/* XXX: Remove me once we don't depend on ath9k_channel for all
1919 * this redundant data */ 1922 * this redundant data */
1920static void ath9k_update_ichannel(struct ath_softc *sc, 1923void ath9k_update_ichannel(struct ath_softc *sc, struct ieee80211_hw *hw,
1921 struct ath9k_channel *ichan) 1924 struct ath9k_channel *ichan)
1922{ 1925{
1923 struct ieee80211_hw *hw = sc->hw;
1924 struct ieee80211_channel *chan = hw->conf.channel; 1926 struct ieee80211_channel *chan = hw->conf.channel;
1925 struct ieee80211_conf *conf = &hw->conf; 1927 struct ieee80211_conf *conf = &hw->conf;
1926 1928
@@ -1967,8 +1969,9 @@ static int ath9k_start(struct ieee80211_hw *hw)
1967 1969
1968 pos = curchan->hw_value; 1970 pos = curchan->hw_value;
1969 1971
1972 sc->chan_idx = pos;
1970 init_channel = &sc->sc_ah->channels[pos]; 1973 init_channel = &sc->sc_ah->channels[pos];
1971 ath9k_update_ichannel(sc, init_channel); 1974 ath9k_update_ichannel(sc, hw, init_channel);
1972 1975
1973 /* Reset SERDES registers */ 1976 /* Reset SERDES registers */
1974 ath9k_hw_configpcipowersave(sc->sc_ah, 0); 1977 ath9k_hw_configpcipowersave(sc->sc_ah, 0);
@@ -2307,15 +2310,21 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
2307 struct ieee80211_channel *curchan = hw->conf.channel; 2310 struct ieee80211_channel *curchan = hw->conf.channel;
2308 int pos = curchan->hw_value; 2311 int pos = curchan->hw_value;
2309 2312
2313 aphy->chan_idx = pos;
2314 aphy->chan_is_ht = conf_is_ht(conf);
2315
2316 /* TODO: do not change operation channel immediately if there
2317 * are other virtual wiphys that use another channel */
2318
2310 DPRINTF(sc, ATH_DBG_CONFIG, "Set channel: %d MHz\n", 2319 DPRINTF(sc, ATH_DBG_CONFIG, "Set channel: %d MHz\n",
2311 curchan->center_freq); 2320 curchan->center_freq);
2312 2321
2313 /* XXX: remove me eventualy */ 2322 /* XXX: remove me eventualy */
2314 ath9k_update_ichannel(sc, &sc->sc_ah->channels[pos]); 2323 ath9k_update_ichannel(sc, hw, &sc->sc_ah->channels[pos]);
2315 2324
2316 ath_update_chainmask(sc, conf_is_ht(conf)); 2325 ath_update_chainmask(sc, conf_is_ht(conf));
2317 2326
2318 if (ath_set_channel(sc, &sc->sc_ah->channels[pos]) < 0) { 2327 if (ath_set_channel(sc, hw, &sc->sc_ah->channels[pos]) < 0) {
2319 DPRINTF(sc, ATH_DBG_FATAL, "Unable to set channel\n"); 2328 DPRINTF(sc, ATH_DBG_FATAL, "Unable to set channel\n");
2320 mutex_unlock(&sc->mutex); 2329 mutex_unlock(&sc->mutex);
2321 return -EINVAL; 2330 return -EINVAL;
diff --git a/drivers/net/wireless/ath9k/virtual.c b/drivers/net/wireless/ath9k/virtual.c
index a8bac97bd847..76ffdfa860ed 100644
--- a/drivers/net/wireless/ath9k/virtual.c
+++ b/drivers/net/wireless/ath9k/virtual.c
@@ -222,6 +222,81 @@ exit:
222 return -1; 222 return -1;
223} 223}
224 224
225static bool __ath9k_wiphy_pausing(struct ath_softc *sc)
226{
227 int i;
228 if (sc->pri_wiphy->state == ATH_WIPHY_PAUSING)
229 return true;
230 for (i = 0; i < sc->num_sec_wiphy; i++) {
231 if (sc->sec_wiphy[i] &&
232 sc->sec_wiphy[i]->state == ATH_WIPHY_PAUSING)
233 return true;
234 }
235 return false;
236}
237
238static bool ath9k_wiphy_pausing(struct ath_softc *sc)
239{
240 bool ret;
241 spin_lock_bh(&sc->wiphy_lock);
242 ret = __ath9k_wiphy_pausing(sc);
243 spin_unlock_bh(&sc->wiphy_lock);
244 return ret;
245}
246
247static int __ath9k_wiphy_unpause(struct ath_wiphy *aphy);
248
249/* caller must hold wiphy_lock */
250static void __ath9k_wiphy_unpause_ch(struct ath_wiphy *aphy)
251{
252 if (aphy == NULL)
253 return;
254 if (aphy->chan_idx != aphy->sc->chan_idx)
255 return; /* wiphy not on the selected channel */
256 __ath9k_wiphy_unpause(aphy);
257}
258
259static void ath9k_wiphy_unpause_channel(struct ath_softc *sc)
260{
261 int i;
262 spin_lock_bh(&sc->wiphy_lock);
263 __ath9k_wiphy_unpause_ch(sc->pri_wiphy);
264 for (i = 0; i < sc->num_sec_wiphy; i++)
265 __ath9k_wiphy_unpause_ch(sc->sec_wiphy[i]);
266 spin_unlock_bh(&sc->wiphy_lock);
267}
268
269void ath9k_wiphy_chan_work(struct work_struct *work)
270{
271 struct ath_softc *sc = container_of(work, struct ath_softc, chan_work);
272 struct ath_wiphy *aphy = sc->next_wiphy;
273
274 if (aphy == NULL)
275 return;
276
277 /*
278 * All pending interfaces paused; ready to change
279 * channels.
280 */
281
282 /* Change channels */
283 mutex_lock(&sc->mutex);
284 /* XXX: remove me eventually */
285 ath9k_update_ichannel(sc, aphy->hw,
286 &sc->sc_ah->channels[sc->chan_idx]);
287 ath_update_chainmask(sc, sc->chan_is_ht);
288 if (ath_set_channel(sc, aphy->hw,
289 &sc->sc_ah->channels[sc->chan_idx]) < 0) {
290 printk(KERN_DEBUG "ath9k: Failed to set channel for new "
291 "virtual wiphy\n");
292 mutex_unlock(&sc->mutex);
293 return;
294 }
295 mutex_unlock(&sc->mutex);
296
297 ath9k_wiphy_unpause_channel(sc);
298}
299
225/* 300/*
226 * ath9k version of ieee80211_tx_status() for TX frames that are generated 301 * ath9k version of ieee80211_tx_status() for TX frames that are generated
227 * internally in the driver. 302 * internally in the driver.
@@ -244,6 +319,14 @@ void ath9k_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
244 */ 319 */
245 } 320 }
246 aphy->state = ATH_WIPHY_PAUSED; 321 aphy->state = ATH_WIPHY_PAUSED;
322 if (!ath9k_wiphy_pausing(aphy->sc)) {
323 /*
324 * Drop from tasklet to work to allow mutex for channel
325 * change.
326 */
327 queue_work(aphy->sc->hw->workqueue,
328 &aphy->sc->chan_work);
329 }
247 } 330 }
248 331
249 kfree(tx_info_priv); 332 kfree(tx_info_priv);
@@ -252,6 +335,14 @@ void ath9k_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
252 dev_kfree_skb(skb); 335 dev_kfree_skb(skb);
253} 336}
254 337
338static void ath9k_mark_paused(struct ath_wiphy *aphy)
339{
340 struct ath_softc *sc = aphy->sc;
341 aphy->state = ATH_WIPHY_PAUSED;
342 if (!__ath9k_wiphy_pausing(sc))
343 queue_work(sc->hw->workqueue, &sc->chan_work);
344}
345
255static void ath9k_pause_iter(void *data, u8 *mac, struct ieee80211_vif *vif) 346static void ath9k_pause_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
256{ 347{
257 struct ath_wiphy *aphy = data; 348 struct ath_wiphy *aphy = data;
@@ -260,15 +351,19 @@ static void ath9k_pause_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
260 switch (vif->type) { 351 switch (vif->type) {
261 case NL80211_IFTYPE_STATION: 352 case NL80211_IFTYPE_STATION:
262 if (!vif->bss_conf.assoc) { 353 if (!vif->bss_conf.assoc) {
263 aphy->state = ATH_WIPHY_PAUSED; 354 ath9k_mark_paused(aphy);
264 break; 355 break;
265 } 356 }
266 /* TODO: could avoid this if already in PS mode */ 357 /* TODO: could avoid this if already in PS mode */
267 ath9k_send_nullfunc(aphy, vif, avp->bssid, 1); 358 if (ath9k_send_nullfunc(aphy, vif, avp->bssid, 1)) {
359 printk(KERN_DEBUG "%s: failed to send PS nullfunc\n",
360 __func__);
361 ath9k_mark_paused(aphy);
362 }
268 break; 363 break;
269 case NL80211_IFTYPE_AP: 364 case NL80211_IFTYPE_AP:
270 /* Beacon transmission is paused by aphy->state change */ 365 /* Beacon transmission is paused by aphy->state change */
271 aphy->state = ATH_WIPHY_PAUSED; 366 ath9k_mark_paused(aphy);
272 break; 367 break;
273 default: 368 default:
274 break; 369 break;
@@ -336,3 +431,49 @@ int ath9k_wiphy_unpause(struct ath_wiphy *aphy)
336 spin_unlock_bh(&aphy->sc->wiphy_lock); 431 spin_unlock_bh(&aphy->sc->wiphy_lock);
337 return ret; 432 return ret;
338} 433}
434
435/* caller must hold wiphy_lock */
436static void __ath9k_wiphy_pause_all(struct ath_softc *sc)
437{
438 int i;
439 if (sc->pri_wiphy->state == ATH_WIPHY_ACTIVE)
440 __ath9k_wiphy_pause(sc->pri_wiphy);
441 for (i = 0; i < sc->num_sec_wiphy; i++) {
442 if (sc->sec_wiphy[i] &&
443 sc->sec_wiphy[i]->state == ATH_WIPHY_ACTIVE)
444 __ath9k_wiphy_pause(sc->sec_wiphy[i]);
445 }
446}
447
448int ath9k_wiphy_select(struct ath_wiphy *aphy)
449{
450 struct ath_softc *sc = aphy->sc;
451 bool now;
452
453 spin_lock_bh(&sc->wiphy_lock);
454 if (__ath9k_wiphy_pausing(sc)) {
455 spin_unlock_bh(&sc->wiphy_lock);
456 return -EBUSY; /* previous select still in progress */
457 }
458
459 /* Store the new channel */
460 sc->chan_idx = aphy->chan_idx;
461 sc->chan_is_ht = aphy->chan_is_ht;
462 sc->next_wiphy = aphy;
463
464 __ath9k_wiphy_pause_all(sc);
465 now = !__ath9k_wiphy_pausing(aphy->sc);
466 spin_unlock_bh(&sc->wiphy_lock);
467
468 if (now) {
469 /* Ready to request channel change immediately */
470 queue_work(aphy->sc->hw->workqueue, &aphy->sc->chan_work);
471 }
472
473 /*
474 * wiphys will be unpaused in ath9k_tx_status() once channel has been
475 * changed if any wiphy needs time to become paused.
476 */
477
478 return 0;
479}