aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/net/wireless/ath/ath9k/ath9k.h1
-rw-r--r--drivers/net/wireless/ath/ath9k/main.c71
-rw-r--r--drivers/net/wireless/ath/ath9k/xmit.c27
3 files changed, 88 insertions, 11 deletions
diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index a224c56448de..f9f0389b92ab 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -189,6 +189,7 @@ struct ath_txq {
189 u32 axq_ampdu_depth; 189 u32 axq_ampdu_depth;
190 bool stopped; 190 bool stopped;
191 bool axq_tx_inprogress; 191 bool axq_tx_inprogress;
192 bool txq_flush_inprogress;
192 struct list_head axq_acq; 193 struct list_head axq_acq;
193 struct list_head txq_fifo[ATH_TXFIFO_DEPTH]; 194 struct list_head txq_fifo[ATH_TXFIFO_DEPTH];
194 struct list_head txq_fifo_pending; 195 struct list_head txq_fifo_pending;
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 1d2c7c3cc5ca..a71550049d84 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -15,6 +15,7 @@
15 */ 15 */
16 16
17#include <linux/nl80211.h> 17#include <linux/nl80211.h>
18#include <linux/delay.h>
18#include "ath9k.h" 19#include "ath9k.h"
19#include "btcoex.h" 20#include "btcoex.h"
20 21
@@ -53,6 +54,21 @@ static u8 parse_mpdudensity(u8 mpdudensity)
53 } 54 }
54} 55}
55 56
57static bool ath9k_has_pending_frames(struct ath_softc *sc, struct ath_txq *txq)
58{
59 bool pending = false;
60
61 spin_lock_bh(&txq->axq_lock);
62
63 if (txq->axq_depth || !list_empty(&txq->axq_acq))
64 pending = true;
65 else if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA)
66 pending = !list_empty(&txq->txq_fifo_pending);
67
68 spin_unlock_bh(&txq->axq_lock);
69 return pending;
70}
71
56bool ath9k_setpower(struct ath_softc *sc, enum ath9k_power_mode mode) 72bool ath9k_setpower(struct ath_softc *sc, enum ath9k_power_mode mode)
57{ 73{
58 unsigned long flags; 74 unsigned long flags;
@@ -2111,6 +2127,60 @@ static void ath9k_set_coverage_class(struct ieee80211_hw *hw, u8 coverage_class)
2111 mutex_unlock(&sc->mutex); 2127 mutex_unlock(&sc->mutex);
2112} 2128}
2113 2129
2130static void ath9k_flush(struct ieee80211_hw *hw, bool drop)
2131{
2132#define ATH_FLUSH_TIMEOUT 60 /* ms */
2133 struct ath_softc *sc = hw->priv;
2134 struct ath_txq *txq;
2135 struct ath_hw *ah = sc->sc_ah;
2136 struct ath_common *common = ath9k_hw_common(ah);
2137 int i, j, npend = 0;
2138
2139 mutex_lock(&sc->mutex);
2140
2141 cancel_delayed_work_sync(&sc->tx_complete_work);
2142
2143 for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
2144 if (!ATH_TXQ_SETUP(sc, i))
2145 continue;
2146 txq = &sc->tx.txq[i];
2147
2148 if (!drop) {
2149 for (j = 0; j < ATH_FLUSH_TIMEOUT; j++) {
2150 if (!ath9k_has_pending_frames(sc, txq))
2151 break;
2152 usleep_range(1000, 2000);
2153 }
2154 }
2155
2156 if (drop || ath9k_has_pending_frames(sc, txq)) {
2157 ath_dbg(common, ATH_DBG_QUEUE, "Drop frames from hw queue:%d\n",
2158 txq->axq_qnum);
2159 spin_lock_bh(&txq->axq_lock);
2160 txq->txq_flush_inprogress = true;
2161 spin_unlock_bh(&txq->axq_lock);
2162
2163 ath9k_ps_wakeup(sc);
2164 ath9k_hw_stoptxdma(ah, txq->axq_qnum);
2165 npend = ath9k_hw_numtxpending(ah, txq->axq_qnum);
2166 ath9k_ps_restore(sc);
2167 if (npend)
2168 break;
2169
2170 ath_draintxq(sc, txq, false);
2171 txq->txq_flush_inprogress = false;
2172 }
2173 }
2174
2175 if (npend) {
2176 ath_reset(sc, false);
2177 txq->txq_flush_inprogress = false;
2178 }
2179
2180 ieee80211_queue_delayed_work(hw, &sc->tx_complete_work, 0);
2181 mutex_unlock(&sc->mutex);
2182}
2183
2114struct ieee80211_ops ath9k_ops = { 2184struct ieee80211_ops ath9k_ops = {
2115 .tx = ath9k_tx, 2185 .tx = ath9k_tx,
2116 .start = ath9k_start, 2186 .start = ath9k_start,
@@ -2132,4 +2202,5 @@ struct ieee80211_ops ath9k_ops = {
2132 .get_survey = ath9k_get_survey, 2202 .get_survey = ath9k_get_survey,
2133 .rfkill_poll = ath9k_rfkill_poll_state, 2203 .rfkill_poll = ath9k_rfkill_poll_state,
2134 .set_coverage_class = ath9k_set_coverage_class, 2204 .set_coverage_class = ath9k_set_coverage_class,
2205 .flush = ath9k_flush,
2135}; 2206};
diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c
index bc614acb96de..e16136d61799 100644
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -2014,7 +2014,8 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
2014 spin_lock_bh(&txq->axq_lock); 2014 spin_lock_bh(&txq->axq_lock);
2015 if (list_empty(&txq->axq_q)) { 2015 if (list_empty(&txq->axq_q)) {
2016 txq->axq_link = NULL; 2016 txq->axq_link = NULL;
2017 if (sc->sc_flags & SC_OP_TXAGGR) 2017 if (sc->sc_flags & SC_OP_TXAGGR &&
2018 !txq->txq_flush_inprogress)
2018 ath_txq_schedule(sc, txq); 2019 ath_txq_schedule(sc, txq);
2019 spin_unlock_bh(&txq->axq_lock); 2020 spin_unlock_bh(&txq->axq_lock);
2020 break; 2021 break;
@@ -2071,6 +2072,7 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
2071 2072
2072 if (bf_is_ampdu_not_probing(bf)) 2073 if (bf_is_ampdu_not_probing(bf))
2073 txq->axq_ampdu_depth--; 2074 txq->axq_ampdu_depth--;
2075
2074 spin_unlock_bh(&txq->axq_lock); 2076 spin_unlock_bh(&txq->axq_lock);
2075 2077
2076 if (bf_held) 2078 if (bf_held)
@@ -2094,7 +2096,7 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
2094 2096
2095 spin_lock_bh(&txq->axq_lock); 2097 spin_lock_bh(&txq->axq_lock);
2096 2098
2097 if (sc->sc_flags & SC_OP_TXAGGR) 2099 if (sc->sc_flags & SC_OP_TXAGGR && !txq->txq_flush_inprogress)
2098 ath_txq_schedule(sc, txq); 2100 ath_txq_schedule(sc, txq);
2099 spin_unlock_bh(&txq->axq_lock); 2101 spin_unlock_bh(&txq->axq_lock);
2100 } 2102 }
@@ -2265,15 +2267,18 @@ void ath_tx_edma_tasklet(struct ath_softc *sc)
2265 2267
2266 spin_lock_bh(&txq->axq_lock); 2268 spin_lock_bh(&txq->axq_lock);
2267 2269
2268 if (!list_empty(&txq->txq_fifo_pending)) { 2270 if (!txq->txq_flush_inprogress) {
2269 INIT_LIST_HEAD(&bf_head); 2271 if (!list_empty(&txq->txq_fifo_pending)) {
2270 bf = list_first_entry(&txq->txq_fifo_pending, 2272 INIT_LIST_HEAD(&bf_head);
2271 struct ath_buf, list); 2273 bf = list_first_entry(&txq->txq_fifo_pending,
2272 list_cut_position(&bf_head, &txq->txq_fifo_pending, 2274 struct ath_buf, list);
2273 &bf->bf_lastbf->list); 2275 list_cut_position(&bf_head,
2274 ath_tx_txqaddbuf(sc, txq, &bf_head); 2276 &txq->txq_fifo_pending,
2275 } else if (sc->sc_flags & SC_OP_TXAGGR) 2277 &bf->bf_lastbf->list);
2276 ath_txq_schedule(sc, txq); 2278 ath_tx_txqaddbuf(sc, txq, &bf_head);
2279 } else if (sc->sc_flags & SC_OP_TXAGGR)
2280 ath_txq_schedule(sc, txq);
2281 }
2277 spin_unlock_bh(&txq->axq_lock); 2282 spin_unlock_bh(&txq->axq_lock);
2278 } 2283 }
2279} 2284}