diff options
Diffstat (limited to 'drivers/net/wireless/ath9k/virtual.c')
-rw-r--r-- | drivers/net/wireless/ath9k/virtual.c | 147 |
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 | ||
225 | static 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 | |||
238 | static 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 | |||
247 | static int __ath9k_wiphy_unpause(struct ath_wiphy *aphy); | ||
248 | |||
249 | /* caller must hold wiphy_lock */ | ||
250 | static 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 | |||
259 | static 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 | |||
269 | void 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 | ||
338 | static 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 | |||
255 | static void ath9k_pause_iter(void *data, u8 *mac, struct ieee80211_vif *vif) | 346 | static 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 */ | ||
436 | static 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 | |||
448 | int 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 | } | ||