diff options
Diffstat (limited to 'drivers/net/wireless/ath/ath9k/xmit.c')
-rw-r--r-- | drivers/net/wireless/ath/ath9k/xmit.c | 116 |
1 files changed, 59 insertions, 57 deletions
diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 4dda14e36227..8f00c6c13979 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c | |||
@@ -61,6 +61,8 @@ static int ath_tx_num_badfrms(struct ath_softc *sc, struct ath_buf *bf, | |||
61 | struct ath_tx_status *ts, int txok); | 61 | struct ath_tx_status *ts, int txok); |
62 | static void ath_tx_rc_status(struct ath_buf *bf, struct ath_tx_status *ts, | 62 | static void ath_tx_rc_status(struct ath_buf *bf, struct ath_tx_status *ts, |
63 | int nbad, int txok, bool update_rc); | 63 | int nbad, int txok, bool update_rc); |
64 | static void ath_tx_update_baw(struct ath_softc *sc, struct ath_atx_tid *tid, | ||
65 | int seqno); | ||
64 | 66 | ||
65 | enum { | 67 | enum { |
66 | MCS_HT20, | 68 | MCS_HT20, |
@@ -143,18 +145,23 @@ static void ath_tx_flush_tid(struct ath_softc *sc, struct ath_atx_tid *tid) | |||
143 | struct ath_txq *txq = &sc->tx.txq[tid->ac->qnum]; | 145 | struct ath_txq *txq = &sc->tx.txq[tid->ac->qnum]; |
144 | struct ath_buf *bf; | 146 | struct ath_buf *bf; |
145 | struct list_head bf_head; | 147 | struct list_head bf_head; |
146 | INIT_LIST_HEAD(&bf_head); | 148 | struct ath_tx_status ts; |
147 | 149 | ||
148 | WARN_ON(!tid->paused); | 150 | INIT_LIST_HEAD(&bf_head); |
149 | 151 | ||
152 | memset(&ts, 0, sizeof(ts)); | ||
150 | spin_lock_bh(&txq->axq_lock); | 153 | spin_lock_bh(&txq->axq_lock); |
151 | tid->paused = false; | ||
152 | 154 | ||
153 | while (!list_empty(&tid->buf_q)) { | 155 | while (!list_empty(&tid->buf_q)) { |
154 | bf = list_first_entry(&tid->buf_q, struct ath_buf, list); | 156 | bf = list_first_entry(&tid->buf_q, struct ath_buf, list); |
155 | BUG_ON(bf_isretried(bf)); | ||
156 | list_move_tail(&bf->list, &bf_head); | 157 | list_move_tail(&bf->list, &bf_head); |
157 | ath_tx_send_ht_normal(sc, txq, tid, &bf_head); | 158 | |
159 | if (bf_isretried(bf)) { | ||
160 | ath_tx_update_baw(sc, tid, bf->bf_seqno); | ||
161 | ath_tx_complete_buf(sc, bf, txq, &bf_head, &ts, 0, 0); | ||
162 | } else { | ||
163 | ath_tx_send_ht_normal(sc, txq, tid, &bf_head); | ||
164 | } | ||
158 | } | 165 | } |
159 | 166 | ||
160 | spin_unlock_bh(&txq->axq_lock); | 167 | spin_unlock_bh(&txq->axq_lock); |
@@ -312,6 +319,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, | |||
312 | int isaggr, txfail, txpending, sendbar = 0, needreset = 0, nbad = 0; | 319 | int isaggr, txfail, txpending, sendbar = 0, needreset = 0, nbad = 0; |
313 | bool rc_update = true; | 320 | bool rc_update = true; |
314 | struct ieee80211_tx_rate rates[4]; | 321 | struct ieee80211_tx_rate rates[4]; |
322 | int nframes; | ||
315 | 323 | ||
316 | skb = bf->bf_mpdu; | 324 | skb = bf->bf_mpdu; |
317 | hdr = (struct ieee80211_hdr *)skb->data; | 325 | hdr = (struct ieee80211_hdr *)skb->data; |
@@ -320,6 +328,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, | |||
320 | hw = bf->aphy->hw; | 328 | hw = bf->aphy->hw; |
321 | 329 | ||
322 | memcpy(rates, tx_info->control.rates, sizeof(rates)); | 330 | memcpy(rates, tx_info->control.rates, sizeof(rates)); |
331 | nframes = bf->bf_nframes; | ||
323 | 332 | ||
324 | rcu_read_lock(); | 333 | rcu_read_lock(); |
325 | 334 | ||
@@ -337,7 +346,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, | |||
337 | !bf->bf_stale || bf_next != NULL) | 346 | !bf->bf_stale || bf_next != NULL) |
338 | list_move_tail(&bf->list, &bf_head); | 347 | list_move_tail(&bf->list, &bf_head); |
339 | 348 | ||
340 | ath_tx_rc_status(bf, ts, 0, 0, false); | 349 | ath_tx_rc_status(bf, ts, 1, 0, false); |
341 | ath_tx_complete_buf(sc, bf, txq, &bf_head, ts, | 350 | ath_tx_complete_buf(sc, bf, txq, &bf_head, ts, |
342 | 0, 0); | 351 | 0, 0); |
343 | 352 | ||
@@ -431,7 +440,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, | |||
431 | list_move_tail(&bf->list, &bf_head); | 440 | list_move_tail(&bf->list, &bf_head); |
432 | } | 441 | } |
433 | 442 | ||
434 | if (!txpending) { | 443 | if (!txpending || (tid->state & AGGR_CLEANUP)) { |
435 | /* | 444 | /* |
436 | * complete the acked-ones/xretried ones; update | 445 | * complete the acked-ones/xretried ones; update |
437 | * block-ack window | 446 | * block-ack window |
@@ -442,6 +451,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, | |||
442 | 451 | ||
443 | if (rc_update && (acked_cnt == 1 || txfail_cnt == 1)) { | 452 | if (rc_update && (acked_cnt == 1 || txfail_cnt == 1)) { |
444 | memcpy(tx_info->control.rates, rates, sizeof(rates)); | 453 | memcpy(tx_info->control.rates, rates, sizeof(rates)); |
454 | bf->bf_nframes = nframes; | ||
445 | ath_tx_rc_status(bf, ts, nbad, txok, true); | 455 | ath_tx_rc_status(bf, ts, nbad, txok, true); |
446 | rc_update = false; | 456 | rc_update = false; |
447 | } else { | 457 | } else { |
@@ -510,15 +520,12 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, | |||
510 | } | 520 | } |
511 | 521 | ||
512 | if (tid->state & AGGR_CLEANUP) { | 522 | if (tid->state & AGGR_CLEANUP) { |
523 | ath_tx_flush_tid(sc, tid); | ||
524 | |||
513 | if (tid->baw_head == tid->baw_tail) { | 525 | if (tid->baw_head == tid->baw_tail) { |
514 | tid->state &= ~AGGR_ADDBA_COMPLETE; | 526 | tid->state &= ~AGGR_ADDBA_COMPLETE; |
515 | tid->state &= ~AGGR_CLEANUP; | 527 | tid->state &= ~AGGR_CLEANUP; |
516 | |||
517 | /* send buffered frames as singles */ | ||
518 | ath_tx_flush_tid(sc, tid); | ||
519 | } | 528 | } |
520 | rcu_read_unlock(); | ||
521 | return; | ||
522 | } | 529 | } |
523 | 530 | ||
524 | rcu_read_unlock(); | 531 | rcu_read_unlock(); |
@@ -785,17 +792,23 @@ static void ath_tx_sched_aggr(struct ath_softc *sc, struct ath_txq *txq, | |||
785 | status != ATH_AGGR_BAW_CLOSED); | 792 | status != ATH_AGGR_BAW_CLOSED); |
786 | } | 793 | } |
787 | 794 | ||
788 | void ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta, | 795 | int ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta, |
789 | u16 tid, u16 *ssn) | 796 | u16 tid, u16 *ssn) |
790 | { | 797 | { |
791 | struct ath_atx_tid *txtid; | 798 | struct ath_atx_tid *txtid; |
792 | struct ath_node *an; | 799 | struct ath_node *an; |
793 | 800 | ||
794 | an = (struct ath_node *)sta->drv_priv; | 801 | an = (struct ath_node *)sta->drv_priv; |
795 | txtid = ATH_AN_2_TID(an, tid); | 802 | txtid = ATH_AN_2_TID(an, tid); |
803 | |||
804 | if (txtid->state & (AGGR_CLEANUP | AGGR_ADDBA_COMPLETE)) | ||
805 | return -EAGAIN; | ||
806 | |||
796 | txtid->state |= AGGR_ADDBA_PROGRESS; | 807 | txtid->state |= AGGR_ADDBA_PROGRESS; |
797 | txtid->paused = true; | 808 | txtid->paused = true; |
798 | *ssn = txtid->seq_start; | 809 | *ssn = txtid->seq_start; |
810 | |||
811 | return 0; | ||
799 | } | 812 | } |
800 | 813 | ||
801 | void ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid) | 814 | void ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid) |
@@ -803,12 +816,6 @@ void ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid) | |||
803 | struct ath_node *an = (struct ath_node *)sta->drv_priv; | 816 | struct ath_node *an = (struct ath_node *)sta->drv_priv; |
804 | struct ath_atx_tid *txtid = ATH_AN_2_TID(an, tid); | 817 | struct ath_atx_tid *txtid = ATH_AN_2_TID(an, tid); |
805 | struct ath_txq *txq = &sc->tx.txq[txtid->ac->qnum]; | 818 | struct ath_txq *txq = &sc->tx.txq[txtid->ac->qnum]; |
806 | struct ath_tx_status ts; | ||
807 | struct ath_buf *bf; | ||
808 | struct list_head bf_head; | ||
809 | |||
810 | memset(&ts, 0, sizeof(ts)); | ||
811 | INIT_LIST_HEAD(&bf_head); | ||
812 | 819 | ||
813 | if (txtid->state & AGGR_CLEANUP) | 820 | if (txtid->state & AGGR_CLEANUP) |
814 | return; | 821 | return; |
@@ -818,31 +825,22 @@ void ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid) | |||
818 | return; | 825 | return; |
819 | } | 826 | } |
820 | 827 | ||
821 | /* drop all software retried frames and mark this TID */ | ||
822 | spin_lock_bh(&txq->axq_lock); | 828 | spin_lock_bh(&txq->axq_lock); |
823 | txtid->paused = true; | 829 | txtid->paused = true; |
824 | while (!list_empty(&txtid->buf_q)) { | ||
825 | bf = list_first_entry(&txtid->buf_q, struct ath_buf, list); | ||
826 | if (!bf_isretried(bf)) { | ||
827 | /* | ||
828 | * NB: it's based on the assumption that | ||
829 | * software retried frame will always stay | ||
830 | * at the head of software queue. | ||
831 | */ | ||
832 | break; | ||
833 | } | ||
834 | list_move_tail(&bf->list, &bf_head); | ||
835 | ath_tx_update_baw(sc, txtid, bf->bf_seqno); | ||
836 | ath_tx_complete_buf(sc, bf, txq, &bf_head, &ts, 0, 0); | ||
837 | } | ||
838 | spin_unlock_bh(&txq->axq_lock); | ||
839 | 830 | ||
840 | if (txtid->baw_head != txtid->baw_tail) { | 831 | /* |
832 | * If frames are still being transmitted for this TID, they will be | ||
833 | * cleaned up during tx completion. To prevent race conditions, this | ||
834 | * TID can only be reused after all in-progress subframes have been | ||
835 | * completed. | ||
836 | */ | ||
837 | if (txtid->baw_head != txtid->baw_tail) | ||
841 | txtid->state |= AGGR_CLEANUP; | 838 | txtid->state |= AGGR_CLEANUP; |
842 | } else { | 839 | else |
843 | txtid->state &= ~AGGR_ADDBA_COMPLETE; | 840 | txtid->state &= ~AGGR_ADDBA_COMPLETE; |
844 | ath_tx_flush_tid(sc, txtid); | 841 | spin_unlock_bh(&txq->axq_lock); |
845 | } | 842 | |
843 | ath_tx_flush_tid(sc, txtid); | ||
846 | } | 844 | } |
847 | 845 | ||
848 | void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid) | 846 | void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid) |
@@ -1103,15 +1101,6 @@ void ath_draintxq(struct ath_softc *sc, struct ath_txq *txq, bool retry_tx) | |||
1103 | txq->axq_tx_inprogress = false; | 1101 | txq->axq_tx_inprogress = false; |
1104 | spin_unlock_bh(&txq->axq_lock); | 1102 | spin_unlock_bh(&txq->axq_lock); |
1105 | 1103 | ||
1106 | /* flush any pending frames if aggregation is enabled */ | ||
1107 | if (sc->sc_flags & SC_OP_TXAGGR) { | ||
1108 | if (!retry_tx) { | ||
1109 | spin_lock_bh(&txq->axq_lock); | ||
1110 | ath_txq_drain_pending_buffers(sc, txq); | ||
1111 | spin_unlock_bh(&txq->axq_lock); | ||
1112 | } | ||
1113 | } | ||
1114 | |||
1115 | if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) { | 1104 | if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) { |
1116 | spin_lock_bh(&txq->axq_lock); | 1105 | spin_lock_bh(&txq->axq_lock); |
1117 | while (!list_empty(&txq->txq_fifo_pending)) { | 1106 | while (!list_empty(&txq->txq_fifo_pending)) { |
@@ -1132,6 +1121,15 @@ void ath_draintxq(struct ath_softc *sc, struct ath_txq *txq, bool retry_tx) | |||
1132 | } | 1121 | } |
1133 | spin_unlock_bh(&txq->axq_lock); | 1122 | spin_unlock_bh(&txq->axq_lock); |
1134 | } | 1123 | } |
1124 | |||
1125 | /* flush any pending frames if aggregation is enabled */ | ||
1126 | if (sc->sc_flags & SC_OP_TXAGGR) { | ||
1127 | if (!retry_tx) { | ||
1128 | spin_lock_bh(&txq->axq_lock); | ||
1129 | ath_txq_drain_pending_buffers(sc, txq); | ||
1130 | spin_unlock_bh(&txq->axq_lock); | ||
1131 | } | ||
1132 | } | ||
1135 | } | 1133 | } |
1136 | 1134 | ||
1137 | void ath_drain_all_txq(struct ath_softc *sc, bool retry_tx) | 1135 | void ath_drain_all_txq(struct ath_softc *sc, bool retry_tx) |
@@ -1162,13 +1160,13 @@ void ath_drain_all_txq(struct ath_softc *sc, bool retry_tx) | |||
1162 | ath_print(common, ATH_DBG_FATAL, | 1160 | ath_print(common, ATH_DBG_FATAL, |
1163 | "Failed to stop TX DMA. Resetting hardware!\n"); | 1161 | "Failed to stop TX DMA. Resetting hardware!\n"); |
1164 | 1162 | ||
1165 | spin_lock_bh(&sc->sc_resetlock); | 1163 | spin_lock_bh(&sc->sc_pcu_lock); |
1166 | r = ath9k_hw_reset(ah, sc->sc_ah->curchan, ah->caldata, false); | 1164 | r = ath9k_hw_reset(ah, sc->sc_ah->curchan, ah->caldata, false); |
1167 | if (r) | 1165 | if (r) |
1168 | ath_print(common, ATH_DBG_FATAL, | 1166 | ath_print(common, ATH_DBG_FATAL, |
1169 | "Unable to reset hardware; reset status %d\n", | 1167 | "Unable to reset hardware; reset status %d\n", |
1170 | r); | 1168 | r); |
1171 | spin_unlock_bh(&sc->sc_resetlock); | 1169 | spin_unlock_bh(&sc->sc_pcu_lock); |
1172 | } | 1170 | } |
1173 | 1171 | ||
1174 | for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) { | 1172 | for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) { |
@@ -2024,9 +2022,15 @@ static void ath_tx_rc_status(struct ath_buf *bf, struct ath_tx_status *ts, | |||
2024 | 2022 | ||
2025 | if (ts->ts_status & ATH9K_TXERR_FILT) | 2023 | if (ts->ts_status & ATH9K_TXERR_FILT) |
2026 | tx_info->flags |= IEEE80211_TX_STAT_TX_FILTERED; | 2024 | tx_info->flags |= IEEE80211_TX_STAT_TX_FILTERED; |
2027 | if ((tx_info->flags & IEEE80211_TX_CTL_AMPDU) && update_rc) | 2025 | if ((tx_info->flags & IEEE80211_TX_CTL_AMPDU) && update_rc) { |
2028 | tx_info->flags |= IEEE80211_TX_STAT_AMPDU; | 2026 | tx_info->flags |= IEEE80211_TX_STAT_AMPDU; |
2029 | 2027 | ||
2028 | BUG_ON(nbad > bf->bf_nframes); | ||
2029 | |||
2030 | tx_info->status.ampdu_len = bf->bf_nframes; | ||
2031 | tx_info->status.ampdu_ack_len = bf->bf_nframes - nbad; | ||
2032 | } | ||
2033 | |||
2030 | if ((ts->ts_status & ATH9K_TXERR_FILT) == 0 && | 2034 | if ((ts->ts_status & ATH9K_TXERR_FILT) == 0 && |
2031 | (bf->bf_flags & ATH9K_TXDESC_NOACK) == 0 && update_rc) { | 2035 | (bf->bf_flags & ATH9K_TXDESC_NOACK) == 0 && update_rc) { |
2032 | if (ieee80211_is_data(hdr->frame_control)) { | 2036 | if (ieee80211_is_data(hdr->frame_control)) { |
@@ -2036,8 +2040,6 @@ static void ath_tx_rc_status(struct ath_buf *bf, struct ath_tx_status *ts, | |||
2036 | if ((ts->ts_status & ATH9K_TXERR_XRETRY) || | 2040 | if ((ts->ts_status & ATH9K_TXERR_XRETRY) || |
2037 | (ts->ts_status & ATH9K_TXERR_FIFO)) | 2041 | (ts->ts_status & ATH9K_TXERR_FIFO)) |
2038 | tx_info->pad[0] |= ATH_TX_INFO_XRETRY; | 2042 | tx_info->pad[0] |= ATH_TX_INFO_XRETRY; |
2039 | tx_info->status.ampdu_len = bf->bf_nframes; | ||
2040 | tx_info->status.ampdu_ack_len = bf->bf_nframes - nbad; | ||
2041 | } | 2043 | } |
2042 | } | 2044 | } |
2043 | 2045 | ||
@@ -2159,7 +2161,7 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq) | |||
2159 | */ | 2161 | */ |
2160 | if (ts.ts_status & ATH9K_TXERR_XRETRY) | 2162 | if (ts.ts_status & ATH9K_TXERR_XRETRY) |
2161 | bf->bf_state.bf_type |= BUF_XRETRY; | 2163 | bf->bf_state.bf_type |= BUF_XRETRY; |
2162 | ath_tx_rc_status(bf, &ts, 0, txok, true); | 2164 | ath_tx_rc_status(bf, &ts, txok ? 0 : 1, txok, true); |
2163 | } | 2165 | } |
2164 | 2166 | ||
2165 | if (bf_isampdu(bf)) | 2167 | if (bf_isampdu(bf)) |
@@ -2204,7 +2206,7 @@ static void ath_tx_complete_poll_work(struct work_struct *work) | |||
2204 | ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_RESET, | 2206 | ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_RESET, |
2205 | "tx hung, resetting the chip\n"); | 2207 | "tx hung, resetting the chip\n"); |
2206 | ath9k_ps_wakeup(sc); | 2208 | ath9k_ps_wakeup(sc); |
2207 | ath_reset(sc, false); | 2209 | ath_reset(sc, true); |
2208 | ath9k_ps_restore(sc); | 2210 | ath9k_ps_restore(sc); |
2209 | } | 2211 | } |
2210 | 2212 | ||
@@ -2288,7 +2290,7 @@ void ath_tx_edma_tasklet(struct ath_softc *sc) | |||
2288 | if (!bf_isampdu(bf)) { | 2290 | if (!bf_isampdu(bf)) { |
2289 | if (txs.ts_status & ATH9K_TXERR_XRETRY) | 2291 | if (txs.ts_status & ATH9K_TXERR_XRETRY) |
2290 | bf->bf_state.bf_type |= BUF_XRETRY; | 2292 | bf->bf_state.bf_type |= BUF_XRETRY; |
2291 | ath_tx_rc_status(bf, &txs, 0, txok, true); | 2293 | ath_tx_rc_status(bf, &txs, txok ? 0 : 1, txok, true); |
2292 | } | 2294 | } |
2293 | 2295 | ||
2294 | if (bf_isampdu(bf)) | 2296 | if (bf_isampdu(bf)) |