diff options
Diffstat (limited to 'drivers/net/wireless/ath9k')
-rw-r--r-- | drivers/net/wireless/ath9k/ath9k.h | 4 | ||||
-rw-r--r-- | drivers/net/wireless/ath9k/main.c | 4 | ||||
-rw-r--r-- | drivers/net/wireless/ath9k/virtual.c | 37 |
3 files changed, 43 insertions, 2 deletions
diff --git a/drivers/net/wireless/ath9k/ath9k.h b/drivers/net/wireless/ath9k/ath9k.h index 24373d395e49..4fc054e4354f 100644 --- a/drivers/net/wireless/ath9k/ath9k.h +++ b/drivers/net/wireless/ath9k/ath9k.h | |||
@@ -567,6 +567,8 @@ struct ath_softc { | |||
567 | int chan_is_ht; | 567 | int chan_is_ht; |
568 | struct ath_wiphy *next_wiphy; | 568 | struct ath_wiphy *next_wiphy; |
569 | struct work_struct chan_work; | 569 | struct work_struct chan_work; |
570 | int wiphy_select_failures; | ||
571 | unsigned long wiphy_select_first_fail; | ||
570 | 572 | ||
571 | struct tasklet_struct intr_tq; | 573 | struct tasklet_struct intr_tq; |
572 | struct tasklet_struct bcon_tasklet; | 574 | struct tasklet_struct bcon_tasklet; |
@@ -665,6 +667,8 @@ void ath9k_update_ichannel(struct ath_softc *sc, struct ieee80211_hw *hw, | |||
665 | void ath_update_chainmask(struct ath_softc *sc, int is_ht); | 667 | void ath_update_chainmask(struct ath_softc *sc, int is_ht); |
666 | int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw, | 668 | int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw, |
667 | struct ath9k_channel *hchan); | 669 | struct ath9k_channel *hchan); |
670 | void ath_radio_enable(struct ath_softc *sc); | ||
671 | void ath_radio_disable(struct ath_softc *sc); | ||
668 | 672 | ||
669 | #ifdef CONFIG_PCI | 673 | #ifdef CONFIG_PCI |
670 | int ath_pci_init(void); | 674 | int ath_pci_init(void); |
diff --git a/drivers/net/wireless/ath9k/main.c b/drivers/net/wireless/ath9k/main.c index 6d19a31934d5..bb6e1ddb4a57 100644 --- a/drivers/net/wireless/ath9k/main.c +++ b/drivers/net/wireless/ath9k/main.c | |||
@@ -1090,7 +1090,7 @@ fail: | |||
1090 | /* Rfkill */ | 1090 | /* Rfkill */ |
1091 | /*******************/ | 1091 | /*******************/ |
1092 | 1092 | ||
1093 | static void ath_radio_enable(struct ath_softc *sc) | 1093 | void ath_radio_enable(struct ath_softc *sc) |
1094 | { | 1094 | { |
1095 | struct ath_hw *ah = sc->sc_ah; | 1095 | struct ath_hw *ah = sc->sc_ah; |
1096 | struct ieee80211_channel *channel = sc->hw->conf.channel; | 1096 | struct ieee80211_channel *channel = sc->hw->conf.channel; |
@@ -1131,7 +1131,7 @@ static void ath_radio_enable(struct ath_softc *sc) | |||
1131 | ath9k_ps_restore(sc); | 1131 | ath9k_ps_restore(sc); |
1132 | } | 1132 | } |
1133 | 1133 | ||
1134 | static void ath_radio_disable(struct ath_softc *sc) | 1134 | void ath_radio_disable(struct ath_softc *sc) |
1135 | { | 1135 | { |
1136 | struct ath_hw *ah = sc->sc_ah; | 1136 | struct ath_hw *ah = sc->sc_ah; |
1137 | struct ieee80211_channel *channel = sc->hw->conf.channel; | 1137 | struct ieee80211_channel *channel = sc->hw->conf.channel; |
diff --git a/drivers/net/wireless/ath9k/virtual.c b/drivers/net/wireless/ath9k/virtual.c index 6122f48f25fb..913d2043d23e 100644 --- a/drivers/net/wireless/ath9k/virtual.c +++ b/drivers/net/wireless/ath9k/virtual.c | |||
@@ -432,6 +432,18 @@ int ath9k_wiphy_unpause(struct ath_wiphy *aphy) | |||
432 | return ret; | 432 | return ret; |
433 | } | 433 | } |
434 | 434 | ||
435 | static void __ath9k_wiphy_mark_all_paused(struct ath_softc *sc) | ||
436 | { | ||
437 | int i; | ||
438 | if (sc->pri_wiphy->state != ATH_WIPHY_INACTIVE) | ||
439 | sc->pri_wiphy->state = ATH_WIPHY_PAUSED; | ||
440 | for (i = 0; i < sc->num_sec_wiphy; i++) { | ||
441 | if (sc->sec_wiphy[i] && | ||
442 | sc->sec_wiphy[i]->state != ATH_WIPHY_INACTIVE) | ||
443 | sc->sec_wiphy[i]->state = ATH_WIPHY_PAUSED; | ||
444 | } | ||
445 | } | ||
446 | |||
435 | /* caller must hold wiphy_lock */ | 447 | /* caller must hold wiphy_lock */ |
436 | static void __ath9k_wiphy_pause_all(struct ath_softc *sc) | 448 | static void __ath9k_wiphy_pause_all(struct ath_softc *sc) |
437 | { | 449 | { |
@@ -452,9 +464,34 @@ int ath9k_wiphy_select(struct ath_wiphy *aphy) | |||
452 | 464 | ||
453 | spin_lock_bh(&sc->wiphy_lock); | 465 | spin_lock_bh(&sc->wiphy_lock); |
454 | if (__ath9k_wiphy_pausing(sc)) { | 466 | if (__ath9k_wiphy_pausing(sc)) { |
467 | if (sc->wiphy_select_failures == 0) | ||
468 | sc->wiphy_select_first_fail = jiffies; | ||
469 | sc->wiphy_select_failures++; | ||
470 | if (time_after(jiffies, sc->wiphy_select_first_fail + HZ / 2)) | ||
471 | { | ||
472 | printk(KERN_DEBUG "ath9k: Previous wiphy select timed " | ||
473 | "out; disable/enable hw to recover\n"); | ||
474 | __ath9k_wiphy_mark_all_paused(sc); | ||
475 | /* | ||
476 | * TODO: this workaround to fix hardware is unlikely to | ||
477 | * be specific to virtual wiphy changes. It can happen | ||
478 | * on normal channel change, too, and as such, this | ||
479 | * should really be made more generic. For example, | ||
480 | * tricker radio disable/enable on GTT interrupt burst | ||
481 | * (say, 10 GTT interrupts received without any TX | ||
482 | * frame being completed) | ||
483 | */ | ||
484 | spin_unlock_bh(&sc->wiphy_lock); | ||
485 | ath_radio_disable(sc); | ||
486 | ath_radio_enable(sc); | ||
487 | queue_work(aphy->sc->hw->workqueue, | ||
488 | &aphy->sc->chan_work); | ||
489 | return -EBUSY; /* previous select still in progress */ | ||
490 | } | ||
455 | spin_unlock_bh(&sc->wiphy_lock); | 491 | spin_unlock_bh(&sc->wiphy_lock); |
456 | return -EBUSY; /* previous select still in progress */ | 492 | return -EBUSY; /* previous select still in progress */ |
457 | } | 493 | } |
494 | sc->wiphy_select_failures = 0; | ||
458 | 495 | ||
459 | /* Store the new channel */ | 496 | /* Store the new channel */ |
460 | sc->chan_idx = aphy->chan_idx; | 497 | sc->chan_idx = aphy->chan_idx; |