aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/ath9k/virtual.c
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/virtual.c
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/virtual.c')
-rw-r--r--drivers/net/wireless/ath9k/virtual.c147
1 files changed, 144 insertions, 3 deletions
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}