aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/ath9k/virtual.c
diff options
context:
space:
mode:
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}