aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless
diff options
context:
space:
mode:
authorVasanthakumar Thiagarajan <vasanth@atheros.com>2011-02-19 04:13:42 -0500
committerJohn W. Linville <linville@tuxdriver.com>2011-02-21 15:39:56 -0500
commit69081624c7b2138b137738e307cb67e2dafd6e9b (patch)
tree9aff5e8a5f4140efc2578f2f8616db0796a9aa62 /drivers/net/wireless
parent0aec516ce4cfd44f48b3ae0c54bc2f1eab007173 (diff)
ath9k: Implement op_flush()
When op_flush() is called with no drop (drop=false), the driver tries to tx as many frames as possible in about 100ms on every hw queue. During this time period frames from sw queue are also scheduled on to respective hw queue. Signed-off-by: Vasanthakumar Thiagarajan <vasanth@atheros.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless')
-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}