ath9k: remove driver ASSERT, just use BUG_ON()
[safe/jmp/linux-2.6] / drivers / net / wireless / ath / ath9k / xmit.c
index b61a071..a8620b1 100644 (file)
@@ -59,6 +59,7 @@ static void ath_tx_send_ht_normal(struct ath_softc *sc, struct ath_txq *txq,
                                  struct ath_atx_tid *tid,
                                  struct list_head *bf_head);
 static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf,
+                               struct ath_txq *txq,
                                struct list_head *bf_q,
                                int txok, int sendbar);
 static void ath_tx_txqaddbuf(struct ath_softc *sc, struct ath_txq *txq,
@@ -73,18 +74,6 @@ static void ath_tx_rc_status(struct ath_buf *bf, struct ath_desc *ds,
 /* Aggregation logic */
 /*********************/
 
-static int ath_aggr_query(struct ath_softc *sc, struct ath_node *an, u8 tidno)
-{
-       struct ath_atx_tid *tid;
-       tid = ATH_AN_2_TID(an, tidno);
-
-       if (tid->state & AGGR_ADDBA_COMPLETE ||
-           tid->state & AGGR_ADDBA_PROGRESS)
-               return 1;
-       else
-               return 0;
-}
-
 static void ath_tx_queue_tid(struct ath_txq *txq, struct ath_atx_tid *tid)
 {
        struct ath_atx_ac *ac = tid->ac;
@@ -118,7 +107,7 @@ static void ath_tx_resume_tid(struct ath_softc *sc, struct ath_atx_tid *tid)
 {
        struct ath_txq *txq = &sc->tx.txq[tid->ac->qnum];
 
-       ASSERT(tid->paused > 0);
+       BUG_ON(tid->paused <= 0);
        spin_lock_bh(&txq->axq_lock);
 
        tid->paused--;
@@ -142,7 +131,7 @@ static void ath_tx_flush_tid(struct ath_softc *sc, struct ath_atx_tid *tid)
        struct list_head bf_head;
        INIT_LIST_HEAD(&bf_head);
 
-       ASSERT(tid->paused > 0);
+       BUG_ON(tid->paused <= 0);
        spin_lock_bh(&txq->axq_lock);
 
        tid->paused--;
@@ -154,7 +143,7 @@ static void ath_tx_flush_tid(struct ath_softc *sc, struct ath_atx_tid *tid)
 
        while (!list_empty(&tid->buf_q)) {
                bf = list_first_entry(&tid->buf_q, struct ath_buf, list);
-               ASSERT(!bf_isretried(bf));
+               BUG_ON(bf_isretried(bf));
                list_move_tail(&bf->list, &bf_head);
                ath_tx_send_ht_normal(sc, txq, tid, &bf_head);
        }
@@ -189,7 +178,7 @@ static void ath_tx_addto_baw(struct ath_softc *sc, struct ath_atx_tid *tid,
        index  = ATH_BA_INDEX(tid->seq_start, bf->bf_seqno);
        cindex = (tid->baw_head + index) & (ATH_TID_MAX_BUFS - 1);
 
-       ASSERT(tid->tx_buf[cindex] == NULL);
+       BUG_ON(tid->tx_buf[cindex] != NULL);
        tid->tx_buf[cindex] = bf;
 
        if (index >= ((tid->baw_tail - tid->baw_head) &
@@ -224,7 +213,7 @@ static void ath_tid_drain(struct ath_softc *sc, struct ath_txq *txq,
                        ath_tx_update_baw(sc, tid, bf->bf_seqno);
 
                spin_unlock(&txq->axq_lock);
-               ath_tx_complete_buf(sc, bf, &bf_head, 0, 0);
+               ath_tx_complete_buf(sc, bf, txq, &bf_head, 0, 0);
                spin_lock(&txq->axq_lock);
        }
 
@@ -232,13 +221,15 @@ static void ath_tid_drain(struct ath_softc *sc, struct ath_txq *txq,
        tid->baw_tail = tid->baw_head;
 }
 
-static void ath_tx_set_retry(struct ath_softc *sc, struct ath_buf *bf)
+static void ath_tx_set_retry(struct ath_softc *sc, struct ath_txq *txq,
+                            struct ath_buf *bf)
 {
        struct sk_buff *skb;
        struct ieee80211_hdr *hdr;
 
        bf->bf_state.bf_type |= BUF_RETRY;
        bf->bf_retries++;
+       TX_STAT_INC(txq->axq_qnum, a_retries);
 
        skb = bf->bf_mpdu;
        hdr = (struct ieee80211_hdr *)skb->data;
@@ -250,7 +241,10 @@ static struct ath_buf* ath_clone_txbuf(struct ath_softc *sc, struct ath_buf *bf)
        struct ath_buf *tbf;
 
        spin_lock_bh(&sc->tx.txbuflock);
-       ASSERT(!list_empty((&sc->tx.txbuf)));
+       if (WARN_ON(list_empty(&sc->tx.txbuf))) {
+               spin_unlock_bh(&sc->tx.txbuflock);
+               return NULL;
+       }
        tbf = list_first_entry(&sc->tx.txbuf, struct ath_buf, list);
        list_del(&tbf->list);
        spin_unlock_bh(&sc->tx.txbuflock);
@@ -337,7 +331,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
                        if (!(tid->state & AGGR_CLEANUP) &&
                            ds->ds_txstat.ts_flags != ATH9K_TX_SW_ABORTED) {
                                if (bf->bf_retries < ATH_MAX_SW_RETRIES) {
-                                       ath_tx_set_retry(sc, bf);
+                                       ath_tx_set_retry(sc, txq, bf);
                                        txpending = 1;
                                } else {
                                        bf->bf_state.bf_type |= BUF_XRETRY;
@@ -355,9 +349,16 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
                }
 
                if (bf_next == NULL) {
-                       INIT_LIST_HEAD(&bf_head);
+                       /*
+                        * Make sure the last desc is reclaimed if it
+                        * not a holding desc.
+                        */
+                       if (!bf_last->bf_stale)
+                               list_move_tail(&bf->list, &bf_head);
+                       else
+                               INIT_LIST_HEAD(&bf_head);
                } else {
-                       ASSERT(!list_empty(bf_q));
+                       BUG_ON(list_empty(bf_q));
                        list_move_tail(&bf->list, &bf_head);
                }
 
@@ -377,13 +378,31 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
                                ath_tx_rc_status(bf, ds, nbad, txok, false);
                        }
 
-                       ath_tx_complete_buf(sc, bf, &bf_head, !txfail, sendbar);
+                       ath_tx_complete_buf(sc, bf, txq, &bf_head, !txfail, sendbar);
                } else {
                        /* retry the un-acked ones */
                        if (bf->bf_next == NULL && bf_last->bf_stale) {
                                struct ath_buf *tbf;
 
                                tbf = ath_clone_txbuf(sc, bf_last);
+                               /*
+                                * Update tx baw and complete the frame with
+                                * failed status if we run out of tx buf
+                                */
+                               if (!tbf) {
+                                       spin_lock_bh(&txq->axq_lock);
+                                       ath_tx_update_baw(sc, tid,
+                                                         bf->bf_seqno);
+                                       spin_unlock_bh(&txq->axq_lock);
+
+                                       bf->bf_state.bf_type |= BUF_XRETRY;
+                                       ath_tx_rc_status(bf, ds, nbad,
+                                                        0, false);
+                                       ath_tx_complete_buf(sc, bf, txq,
+                                                           &bf_head, 0, 0);
+                                       break;
+                               }
+
                                ath9k_hw_cleartxdesc(sc->sc_ah, tbf->bf_desc);
                                list_add_tail(&tbf->list, &bf_head);
                        } else {
@@ -407,7 +426,6 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
        if (tid->state & AGGR_CLEANUP) {
                if (tid->baw_head == tid->baw_tail) {
                        tid->state &= ~AGGR_ADDBA_COMPLETE;
-                       tid->addba_exchangeattempts = 0;
                        tid->state &= ~AGGR_CLEANUP;
 
                        /* send buffered frames as singles */
@@ -440,7 +458,7 @@ static u32 ath_lookup_rate(struct ath_softc *sc, struct ath_buf *bf,
        struct ieee80211_tx_rate *rates;
        struct ath_tx_info_priv *tx_info_priv;
        u32 max_4ms_framelen, frmlen;
-       u16 aggr_limit, legacy = 0, maxampdu;
+       u16 aggr_limit, legacy = 0;
        int i;
 
        skb = bf->bf_mpdu;
@@ -475,16 +493,20 @@ static u32 ath_lookup_rate(struct ath_softc *sc, struct ath_buf *bf,
        if (tx_info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE || legacy)
                return 0;
 
-       aggr_limit = min(max_4ms_framelen, (u32)ATH_AMPDU_LIMIT_DEFAULT);
+       if (sc->sc_flags & SC_OP_BT_PRIORITY_DETECTED)
+               aggr_limit = min((max_4ms_framelen * 3) / 8,
+                                (u32)ATH_AMPDU_LIMIT_MAX);
+       else
+               aggr_limit = min(max_4ms_framelen,
+                                (u32)ATH_AMPDU_LIMIT_MAX);
 
        /*
         * h/w can accept aggregates upto 16 bit lengths (65535).
         * The IE, however can hold upto 65536, which shows up here
         * as zero. Ignore 65536 since we  are constrained by hw.
         */
-       maxampdu = tid->an->maxampdu;
-       if (maxampdu)
-               aggr_limit = min(aggr_limit, maxampdu);
+       if (tid->an->maxampdu)
+               aggr_limit = min(aggr_limit, tid->an->maxampdu);
 
        return aggr_limit;
 }
@@ -492,7 +514,6 @@ static u32 ath_lookup_rate(struct ath_softc *sc, struct ath_buf *bf,
 /*
  * Returns the number of delimiters to be added to
  * meet the minimum required mpdudensity.
- * caller should make sure that the rate is HT rate .
  */
 static int ath_compute_num_delims(struct ath_softc *sc, struct ath_atx_tid *tid,
                                  struct ath_buf *bf, u16 frmlen)
@@ -500,7 +521,7 @@ static int ath_compute_num_delims(struct ath_softc *sc, struct ath_atx_tid *tid,
        const struct ath_rate_table *rt = sc->cur_rate_table;
        struct sk_buff *skb = bf->bf_mpdu;
        struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
-       u32 nsymbits, nsymbols, mpdudensity;
+       u32 nsymbits, nsymbols;
        u16 minlen;
        u8 rc, flags, rix;
        int width, half_gi, ndelim, mindelim;
@@ -522,14 +543,12 @@ static int ath_compute_num_delims(struct ath_softc *sc, struct ath_atx_tid *tid,
         * on highest rate in rate series (i.e. first rate) to determine
         * required minimum length for subframe. Take into account
         * whether high rate is 20 or 40Mhz and half or full GI.
-        */
-       mpdudensity = tid->an->mpdudensity;
-
-       /*
+        *
         * If there is no mpdu density restriction, no further calculation
         * is needed.
         */
-       if (mpdudensity == 0)
+
+       if (tid->an->mpdudensity == 0)
                return ndelim;
 
        rix = tx_info->control.rates[0].idx;
@@ -539,9 +558,9 @@ static int ath_compute_num_delims(struct ath_softc *sc, struct ath_atx_tid *tid,
        half_gi = (flags & IEEE80211_TX_RC_SHORT_GI) ? 1 : 0;
 
        if (half_gi)
-               nsymbols = NUM_SYMBOLS_PER_USEC_HALFGI(mpdudensity);
+               nsymbols = NUM_SYMBOLS_PER_USEC_HALFGI(tid->an->mpdudensity);
        else
-               nsymbols = NUM_SYMBOLS_PER_USEC(mpdudensity);
+               nsymbols = NUM_SYMBOLS_PER_USEC(tid->an->mpdudensity);
 
        if (nsymbols == 0)
                nsymbols = 1;
@@ -558,6 +577,7 @@ static int ath_compute_num_delims(struct ath_softc *sc, struct ath_atx_tid *tid,
 }
 
 static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc,
+                                            struct ath_txq *txq,
                                             struct ath_atx_tid *tid,
                                             struct list_head *bf_q)
 {
@@ -622,6 +642,7 @@ static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc,
                        bf_prev->bf_desc->ds_link = bf->bf_daddr;
                }
                bf_prev = bf;
+
        } while (!list_empty(&tid->buf_q));
 
        bf_first->bf_al = al;
@@ -644,7 +665,7 @@ static void ath_tx_sched_aggr(struct ath_softc *sc, struct ath_txq *txq,
 
                INIT_LIST_HEAD(&bf_q);
 
-               status = ath_tx_form_aggr(sc, tid, &bf_q);
+               status = ath_tx_form_aggr(sc, txq, tid, &bf_q);
 
                /*
                 * no frames picked up to be aggregated;
@@ -675,30 +696,26 @@ static void ath_tx_sched_aggr(struct ath_softc *sc, struct ath_txq *txq,
 
                txq->axq_aggr_depth++;
                ath_tx_txqaddbuf(sc, txq, &bf_q);
+               TX_STAT_INC(txq->axq_qnum, a_aggr);
 
        } while (txq->axq_depth < ATH_AGGR_MIN_QDEPTH &&
                 status != ATH_AGGR_BAW_CLOSED);
 }
 
-int ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta,
-                     u16 tid, u16 *ssn)
+void ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta,
+                      u16 tid, u16 *ssn)
 {
        struct ath_atx_tid *txtid;
        struct ath_node *an;
 
        an = (struct ath_node *)sta->drv_priv;
-
-       if (sc->sc_flags & SC_OP_TXAGGR) {
-               txtid = ATH_AN_2_TID(an, tid);
-               txtid->state |= AGGR_ADDBA_PROGRESS;
-               ath_tx_pause_tid(sc, txtid);
-               *ssn = txtid->seq_start;
-       }
-
-       return 0;
+       txtid = ATH_AN_2_TID(an, tid);
+       txtid->state |= AGGR_ADDBA_PROGRESS;
+       ath_tx_pause_tid(sc, txtid);
+       *ssn = txtid->seq_start;
 }
 
-int ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid)
+void ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid)
 {
        struct ath_node *an = (struct ath_node *)sta->drv_priv;
        struct ath_atx_tid *txtid = ATH_AN_2_TID(an, tid);
@@ -708,12 +725,11 @@ int ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid)
        INIT_LIST_HEAD(&bf_head);
 
        if (txtid->state & AGGR_CLEANUP)
-               return 0;
+               return;
 
        if (!(txtid->state & AGGR_ADDBA_COMPLETE)) {
                txtid->state &= ~AGGR_ADDBA_PROGRESS;
-               txtid->addba_exchangeattempts = 0;
-               return 0;
+               return;
        }
 
        ath_tx_pause_tid(sc, txtid);
@@ -732,7 +748,7 @@ int ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid)
                }
                list_move_tail(&bf->list, &bf_head);
                ath_tx_update_baw(sc, txtid, bf->bf_seqno);
-               ath_tx_complete_buf(sc, bf, &bf_head, 0, 0);
+               ath_tx_complete_buf(sc, bf, txq, &bf_head, 0, 0);
        }
        spin_unlock_bh(&txq->axq_lock);
 
@@ -740,11 +756,8 @@ int ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid)
                txtid->state |= AGGR_CLEANUP;
        } else {
                txtid->state &= ~AGGR_ADDBA_COMPLETE;
-               txtid->addba_exchangeattempts = 0;
                ath_tx_flush_tid(sc, txtid);
        }
-
-       return 0;
 }
 
 void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid)
@@ -773,14 +786,8 @@ bool ath_tx_aggr_check(struct ath_softc *sc, struct ath_node *an, u8 tidno)
 
        txtid = ATH_AN_2_TID(an, tidno);
 
-       if (!(txtid->state & AGGR_ADDBA_COMPLETE)) {
-               if (!(txtid->state & AGGR_ADDBA_PROGRESS) &&
-                   (txtid->addba_exchangeattempts < ADDBA_EXCHANGE_ATTEMPTS)) {
-                       txtid->addba_exchangeattempts++;
+       if (!(txtid->state & (AGGR_ADDBA_COMPLETE | AGGR_ADDBA_PROGRESS)))
                        return true;
-               }
-       }
-
        return false;
 }
 
@@ -808,6 +815,7 @@ static void ath_txq_drain_pending_buffers(struct ath_softc *sc,
 struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype, int subtype)
 {
        struct ath_hw *ah = sc->sc_ah;
+       struct ath_common *common = ath9k_hw_common(ah);
        struct ath9k_tx_queue_info qi;
        int qnum;
 
@@ -847,9 +855,9 @@ struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype, int subtype)
                return NULL;
        }
        if (qnum >= ARRAY_SIZE(sc->tx.txq)) {
-               DPRINTF(sc, ATH_DBG_FATAL,
-                       "qnum %u out of range, max %u!\n",
-                       qnum, (unsigned int)ARRAY_SIZE(sc->tx.txq));
+               ath_print(common, ATH_DBG_FATAL,
+                         "qnum %u out of range, max %u!\n",
+                         qnum, (unsigned int)ARRAY_SIZE(sc->tx.txq));
                ath9k_hw_releasetxqueue(ah, qnum);
                return NULL;
        }
@@ -863,23 +871,23 @@ struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype, int subtype)
                spin_lock_init(&txq->axq_lock);
                txq->axq_depth = 0;
                txq->axq_aggr_depth = 0;
-               txq->axq_totalqueued = 0;
                txq->axq_linkbuf = NULL;
+               txq->axq_tx_inprogress = false;
                sc->tx.txqsetup |= 1<<qnum;
        }
        return &sc->tx.txq[qnum];
 }
 
-static int ath_tx_get_qnum(struct ath_softc *sc, int qtype, int haltype)
+int ath_tx_get_qnum(struct ath_softc *sc, int qtype, int haltype)
 {
        int qnum;
 
        switch (qtype) {
        case ATH9K_TX_QUEUE_DATA:
                if (haltype >= ARRAY_SIZE(sc->tx.hwq_map)) {
-                       DPRINTF(sc, ATH_DBG_FATAL,
-                               "HAL AC %u out of range, max %zu!\n",
-                               haltype, ARRAY_SIZE(sc->tx.hwq_map));
+                       ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_FATAL,
+                                 "HAL AC %u out of range, max %zu!\n",
+                                 haltype, ARRAY_SIZE(sc->tx.hwq_map));
                        return -1;
                }
                qnum = sc->tx.hwq_map[haltype];
@@ -907,9 +915,9 @@ struct ath_txq *ath_test_get_txq(struct ath_softc *sc, struct sk_buff *skb)
        spin_lock_bh(&txq->axq_lock);
 
        if (txq->axq_depth >= (ATH_TXBUF - 20)) {
-               DPRINTF(sc, ATH_DBG_XMIT,
-                       "TX queue: %d is full, depth: %d\n",
-                       qnum, txq->axq_depth);
+               ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_XMIT,
+                         "TX queue: %d is full, depth: %d\n",
+                         qnum, txq->axq_depth);
                ieee80211_stop_queue(sc->hw, skb_get_queue_mapping(skb));
                txq->stopped = 1;
                spin_unlock_bh(&txq->axq_lock);
@@ -938,7 +946,7 @@ int ath_txq_update(struct ath_softc *sc, int qnum,
                return 0;
        }
 
-       ASSERT(sc->tx.txq[qnum].axq_qnum == qnum);
+       BUG_ON(sc->tx.txq[qnum].axq_qnum != qnum);
 
        ath9k_hw_get_txq_props(ah, qnum, &qi);
        qi.tqi_aifs = qinfo->tqi_aifs;
@@ -948,8 +956,8 @@ int ath_txq_update(struct ath_softc *sc, int qnum,
        qi.tqi_readyTime = qinfo->tqi_readyTime;
 
        if (!ath9k_hw_set_txq_props(ah, qnum, &qi)) {
-               DPRINTF(sc, ATH_DBG_FATAL,
-                       "Unable to update hardware queue %u!\n", qnum);
+               ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_FATAL,
+                         "Unable to update hardware queue %u!\n", qnum);
                error = -EIO;
        } else {
                ath9k_hw_resettxqueue(ah, qnum);
@@ -1028,9 +1036,13 @@ void ath_draintxq(struct ath_softc *sc, struct ath_txq *txq, bool retry_tx)
                if (bf_isampdu(bf))
                        ath_tx_complete_aggr(sc, txq, bf, &bf_head, 0);
                else
-                       ath_tx_complete_buf(sc, bf, &bf_head, 0, 0);
+                       ath_tx_complete_buf(sc, bf, txq, &bf_head, 0, 0);
        }
 
+       spin_lock_bh(&txq->axq_lock);
+       txq->axq_tx_inprogress = false;
+       spin_unlock_bh(&txq->axq_lock);
+
        /* flush any pending frames if aggregation is enabled */
        if (sc->sc_flags & SC_OP_TXAGGR) {
                if (!retry_tx) {
@@ -1044,6 +1056,7 @@ void ath_draintxq(struct ath_softc *sc, struct ath_txq *txq, bool retry_tx)
 void ath_drain_all_txq(struct ath_softc *sc, bool retry_tx)
 {
        struct ath_hw *ah = sc->sc_ah;
+       struct ath_common *common = ath9k_hw_common(sc->sc_ah);
        struct ath_txq *txq;
        int i, npend = 0;
 
@@ -1065,14 +1078,15 @@ void ath_drain_all_txq(struct ath_softc *sc, bool retry_tx)
        if (npend) {
                int r;
 
-               DPRINTF(sc, ATH_DBG_XMIT, "Unable to stop TxDMA. Reset HAL!\n");
+               ath_print(common, ATH_DBG_XMIT,
+                         "Unable to stop TxDMA. Reset HAL!\n");
 
                spin_lock_bh(&sc->sc_resetlock);
                r = ath9k_hw_reset(ah, sc->sc_ah->curchan, true);
                if (r)
-                       DPRINTF(sc, ATH_DBG_FATAL,
-                               "Unable to reset hardware; reset status %d\n",
-                               r);
+                       ath_print(common, ATH_DBG_FATAL,
+                                 "Unable to reset hardware; reset status %d\n",
+                                 r);
                spin_unlock_bh(&sc->sc_resetlock);
        }
 
@@ -1111,8 +1125,7 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq)
                if (tid->paused)
                        continue;
 
-               if ((txq->axq_depth % 2) == 0)
-                       ath_tx_sched_aggr(sc, txq, tid);
+               ath_tx_sched_aggr(sc, txq, tid);
 
                /*
                 * add tid to round-robin queue if more frames
@@ -1137,8 +1150,8 @@ int ath_tx_setup(struct ath_softc *sc, int haltype)
        struct ath_txq *txq;
 
        if (haltype >= ARRAY_SIZE(sc->tx.hwq_map)) {
-               DPRINTF(sc, ATH_DBG_FATAL,
-                       "HAL AC %u out of range, max %zu!\n",
+               ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_FATAL,
+                         "HAL AC %u out of range, max %zu!\n",
                         haltype, ARRAY_SIZE(sc->tx.hwq_map));
                return 0;
        }
@@ -1162,6 +1175,7 @@ static void ath_tx_txqaddbuf(struct ath_softc *sc, struct ath_txq *txq,
                             struct list_head *head)
 {
        struct ath_hw *ah = sc->sc_ah;
+       struct ath_common *common = ath9k_hw_common(ah);
        struct ath_buf *bf;
 
        /*
@@ -1176,22 +1190,21 @@ static void ath_tx_txqaddbuf(struct ath_softc *sc, struct ath_txq *txq,
 
        list_splice_tail_init(head, &txq->axq_q);
        txq->axq_depth++;
-       txq->axq_totalqueued++;
        txq->axq_linkbuf = list_entry(txq->axq_q.prev, struct ath_buf, list);
 
-       DPRINTF(sc, ATH_DBG_QUEUE,
-               "qnum: %d, txq depth: %d\n", txq->axq_qnum, txq->axq_depth);
+       ath_print(common, ATH_DBG_QUEUE,
+                 "qnum: %d, txq depth: %d\n", txq->axq_qnum, txq->axq_depth);
 
        if (txq->axq_link == NULL) {
                ath9k_hw_puttxbuf(ah, txq->axq_qnum, bf->bf_daddr);
-               DPRINTF(sc, ATH_DBG_XMIT,
-                       "TXDP[%u] = %llx (%p)\n",
-                       txq->axq_qnum, ito64(bf->bf_daddr), bf->bf_desc);
+               ath_print(common, ATH_DBG_XMIT,
+                         "TXDP[%u] = %llx (%p)\n",
+                         txq->axq_qnum, ito64(bf->bf_daddr), bf->bf_desc);
        } else {
                *txq->axq_link = bf->bf_daddr;
-               DPRINTF(sc, ATH_DBG_XMIT, "link[%u] (%p)=%llx (%p)\n",
-                       txq->axq_qnum, txq->axq_link,
-                       ito64(bf->bf_daddr), bf->bf_desc);
+               ath_print(common, ATH_DBG_XMIT, "link[%u] (%p)=%llx (%p)\n",
+                         txq->axq_qnum, txq->axq_link,
+                         ito64(bf->bf_daddr), bf->bf_desc);
        }
        txq->axq_link = &(bf->bf_lastbf->bf_desc->ds_link);
        ath9k_hw_txstart(ah, txq->axq_qnum);
@@ -1224,6 +1237,7 @@ static void ath_tx_send_ampdu(struct ath_softc *sc, struct ath_atx_tid *tid,
 
        bf = list_first_entry(bf_head, struct ath_buf, list);
        bf->bf_state.bf_type |= BUF_AMPDU;
+       TX_STAT_INC(txctl->txq->axq_qnum, a_queued);
 
        /*
         * Do not queue to h/w when any of the following conditions is true:
@@ -1270,6 +1284,7 @@ static void ath_tx_send_ht_normal(struct ath_softc *sc, struct ath_txq *txq,
        bf->bf_lastbf = bf;
        ath_buf_set_rate(sc, bf);
        ath_tx_txqaddbuf(sc, txq, bf_head);
+       TX_STAT_INC(txq->axq_qnum, queued);
 }
 
 static void ath_tx_send_normal(struct ath_softc *sc, struct ath_txq *txq,
@@ -1283,6 +1298,7 @@ static void ath_tx_send_normal(struct ath_softc *sc, struct ath_txq *txq,
        bf->bf_nframes = 1;
        ath_buf_set_rate(sc, bf);
        ath_tx_txqaddbuf(sc, txq, bf_head);
+       TX_STAT_INC(txq->axq_qnum, queued);
 }
 
 static enum ath9k_pkt_type get_hw_packet_type(struct sk_buff *skb)
@@ -1440,6 +1456,7 @@ static u32 ath_pkt_duration(struct ath_softc *sc, u8 rix, struct ath_buf *bf,
 
 static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf)
 {
+       struct ath_common *common = ath9k_hw_common(sc->sc_ah);
        const struct ath_rate_table *rt = sc->cur_rate_table;
        struct ath9k_11n_rate_series series[4];
        struct sk_buff *skb;
@@ -1495,7 +1512,7 @@ static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf)
 
                rix = rates[i].idx;
                series[i].Tries = rates[i].count;
-               series[i].ChSel = sc->tx_chainmask;
+               series[i].ChSel = common->tx_chainmask;
 
                if (rates[i].flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE)
                        series[i].Rate = rt->info[rix].ratecode |
@@ -1575,7 +1592,8 @@ static int ath_tx_setup_buffer(struct ieee80211_hw *hw, struct ath_buf *bf,
                bf->bf_mpdu = NULL;
                kfree(tx_info_priv);
                tx_info->rate_driver_data[0] = NULL;
-               DPRINTF(sc, ATH_DBG_FATAL, "dma_mapping_error() on TX\n");
+               ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_FATAL,
+                         "dma_mapping_error() on TX\n");
                return -ENOMEM;
        }
 
@@ -1629,7 +1647,7 @@ static void ath_tx_start_dma(struct ath_softc *sc, struct ath_buf *bf,
                        goto tx_done;
                }
 
-               if (ath_aggr_query(sc, an, bf->bf_tidno)) {
+               if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) {
                        /*
                         * Try aggregation if it's a unicast data frame
                         * and the destination is HT capable.
@@ -1657,12 +1675,13 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
 {
        struct ath_wiphy *aphy = hw->priv;
        struct ath_softc *sc = aphy->sc;
+       struct ath_common *common = ath9k_hw_common(sc->sc_ah);
        struct ath_buf *bf;
        int r;
 
        bf = ath_tx_get_buffer(sc);
        if (!bf) {
-               DPRINTF(sc, ATH_DBG_XMIT, "TX buffers are full\n");
+               ath_print(common, ATH_DBG_XMIT, "TX buffers are full\n");
                return -1;
        }
 
@@ -1670,7 +1689,7 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
        if (unlikely(r)) {
                struct ath_txq *txq = txctl->txq;
 
-               DPRINTF(sc, ATH_DBG_FATAL, "TX mem alloc failure\n");
+               ath_print(common, ATH_DBG_FATAL, "TX mem alloc failure\n");
 
                /* upon ath_tx_processq() this TX queue will be resumed, we
                 * guarantee this will happen by knowing beforehand that
@@ -1700,6 +1719,7 @@ void ath_tx_cabq(struct ieee80211_hw *hw, struct sk_buff *skb)
 {
        struct ath_wiphy *aphy = hw->priv;
        struct ath_softc *sc = aphy->sc;
+       struct ath_common *common = ath9k_hw_common(sc->sc_ah);
        int hdrlen, padsize;
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
        struct ath_tx_control txctl;
@@ -1724,7 +1744,8 @@ void ath_tx_cabq(struct ieee80211_hw *hw, struct sk_buff *skb)
        if (hdrlen & 3) {
                padsize = hdrlen % 4;
                if (skb_headroom(skb) < padsize) {
-                       DPRINTF(sc, ATH_DBG_XMIT, "TX CABQ padding failed\n");
+                       ath_print(common, ATH_DBG_XMIT,
+                                 "TX CABQ padding failed\n");
                        dev_kfree_skb_any(skb);
                        return;
                }
@@ -1734,10 +1755,11 @@ void ath_tx_cabq(struct ieee80211_hw *hw, struct sk_buff *skb)
 
        txctl.txq = sc->beacon.cabq;
 
-       DPRINTF(sc, ATH_DBG_XMIT, "transmitting CABQ packet, skb: %p\n", skb);
+       ath_print(common, ATH_DBG_XMIT,
+                 "transmitting CABQ packet, skb: %p\n", skb);
 
        if (ath_tx_start(hw, skb, &txctl) != 0) {
-               DPRINTF(sc, ATH_DBG_XMIT, "CABQ TX failed\n");
+               ath_print(common, ATH_DBG_XMIT, "CABQ TX failed\n");
                goto exit;
        }
 
@@ -1756,10 +1778,11 @@ static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb,
        struct ieee80211_hw *hw = sc->hw;
        struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
        struct ath_tx_info_priv *tx_info_priv = ATH_TX_INFO_PRIV(tx_info);
+       struct ath_common *common = ath9k_hw_common(sc->sc_ah);
        int hdrlen, padsize;
        int frame_type = ATH9K_NOT_INTERNAL;
 
-       DPRINTF(sc, ATH_DBG_XMIT, "TX complete: skb: %p\n", skb);
+       ath_print(common, ATH_DBG_XMIT, "TX complete: skb: %p\n", skb);
 
        if (tx_info_priv) {
                hw = tx_info_priv->aphy->hw;
@@ -1793,8 +1816,9 @@ static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb,
 
        if (sc->sc_flags & SC_OP_WAIT_FOR_TX_ACK) {
                sc->sc_flags &= ~SC_OP_WAIT_FOR_TX_ACK;
-               DPRINTF(sc, ATH_DBG_PS, "Going back to sleep after having "
-                       "received TX status (0x%x)\n",
+               ath_print(common, ATH_DBG_PS,
+                         "Going back to sleep after having "
+                         "received TX status (0x%x)\n",
                        sc->sc_flags & (SC_OP_WAIT_FOR_BEACON |
                                        SC_OP_WAIT_FOR_CAB |
                                        SC_OP_WAIT_FOR_PSPOLL_DATA |
@@ -1808,6 +1832,7 @@ static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb,
 }
 
 static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf,
+                               struct ath_txq *txq,
                                struct list_head *bf_q,
                                int txok, int sendbar)
 {
@@ -1815,7 +1840,6 @@ static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf,
        unsigned long flags;
        int tx_flags = 0;
 
-
        if (sendbar)
                tx_flags = ATH_TX_BAR;
 
@@ -1828,6 +1852,7 @@ static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf,
 
        dma_unmap_single(sc->dev, bf->bf_dmacontext, skb->len, DMA_TO_DEVICE);
        ath_tx_complete(sc, skb, tx_flags);
+       ath_debug_stat_tx(sc, txq, bf);
 
        /*
         * Return the list of ath_buf of this mpdu to free queue
@@ -1923,15 +1948,16 @@ static void ath_wake_mac80211_queue(struct ath_softc *sc, struct ath_txq *txq)
 static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
 {
        struct ath_hw *ah = sc->sc_ah;
+       struct ath_common *common = ath9k_hw_common(ah);
        struct ath_buf *bf, *lastbf, *bf_held = NULL;
        struct list_head bf_head;
        struct ath_desc *ds;
        int txok;
        int status;
 
-       DPRINTF(sc, ATH_DBG_QUEUE, "tx queue %d (%x), link %p\n",
-               txq->axq_qnum, ath9k_hw_gettxbuf(sc->sc_ah, txq->axq_qnum),
-               txq->axq_link);
+       ath_print(common, ATH_DBG_QUEUE, "tx queue %d (%x), link %p\n",
+                 txq->axq_qnum, ath9k_hw_gettxbuf(sc->sc_ah, txq->axq_qnum),
+                 txq->axq_link);
 
        for (;;) {
                spin_lock_bh(&txq->axq_lock);
@@ -1955,19 +1981,7 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
                if (bf->bf_stale) {
                        bf_held = bf;
                        if (list_is_last(&bf_held->list, &txq->axq_q)) {
-                               txq->axq_link = NULL;
-                               txq->axq_linkbuf = NULL;
                                spin_unlock_bh(&txq->axq_lock);
-
-                               /*
-                                * The holding descriptor is the last
-                                * descriptor in queue. It's safe to remove
-                                * the last holding descriptor in BH context.
-                                */
-                               spin_lock_bh(&sc->tx.txbuflock);
-                               list_move_tail(&bf_held->list, &sc->tx.txbuf);
-                               spin_unlock_bh(&sc->tx.txbuflock);
-
                                break;
                        } else {
                                bf = list_entry(bf_held->list.next,
@@ -2004,6 +2018,7 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
                        txq->axq_aggr_depth--;
 
                txok = (ds->ds_txstat.ts_status == 0);
+               txq->axq_tx_inprogress = false;
                spin_unlock_bh(&txq->axq_lock);
 
                if (bf_held) {
@@ -2026,7 +2041,7 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
                if (bf_isampdu(bf))
                        ath_tx_complete_aggr(sc, txq, bf, &bf_head, txok);
                else
-                       ath_tx_complete_buf(sc, bf, &bf_head, txok, 0);
+                       ath_tx_complete_buf(sc, bf, txq, &bf_head, txok, 0);
 
                ath_wake_mac80211_queue(sc, txq);
 
@@ -2037,6 +2052,41 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
        }
 }
 
+static void ath_tx_complete_poll_work(struct work_struct *work)
+{
+       struct ath_softc *sc = container_of(work, struct ath_softc,
+                       tx_complete_work.work);
+       struct ath_txq *txq;
+       int i;
+       bool needreset = false;
+
+       for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++)
+               if (ATH_TXQ_SETUP(sc, i)) {
+                       txq = &sc->tx.txq[i];
+                       spin_lock_bh(&txq->axq_lock);
+                       if (txq->axq_depth) {
+                               if (txq->axq_tx_inprogress) {
+                                       needreset = true;
+                                       spin_unlock_bh(&txq->axq_lock);
+                                       break;
+                               } else {
+                                       txq->axq_tx_inprogress = true;
+                               }
+                       }
+                       spin_unlock_bh(&txq->axq_lock);
+               }
+
+       if (needreset) {
+               ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_RESET,
+                         "tx hung, resetting the chip\n");
+               ath_reset(sc, false);
+       }
+
+       ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work,
+                       msecs_to_jiffies(ATH_TX_COMPLETE_POLL_INT));
+}
+
+
 
 void ath_tx_tasklet(struct ath_softc *sc)
 {
@@ -2057,6 +2107,7 @@ void ath_tx_tasklet(struct ath_softc *sc)
 
 int ath_tx_init(struct ath_softc *sc, int nbufs)
 {
+       struct ath_common *common = ath9k_hw_common(sc->sc_ah);
        int error = 0;
 
        spin_lock_init(&sc->tx.txbuflock);
@@ -2064,19 +2115,21 @@ int ath_tx_init(struct ath_softc *sc, int nbufs)
        error = ath_descdma_setup(sc, &sc->tx.txdma, &sc->tx.txbuf,
                                  "tx", nbufs, 1);
        if (error != 0) {
-               DPRINTF(sc, ATH_DBG_FATAL,
-                       "Failed to allocate tx descriptors: %d\n", error);
+               ath_print(common, ATH_DBG_FATAL,
+                         "Failed to allocate tx descriptors: %d\n", error);
                goto err;
        }
 
        error = ath_descdma_setup(sc, &sc->beacon.bdma, &sc->beacon.bbuf,
                                  "beacon", ATH_BCBUF, 1);
        if (error != 0) {
-               DPRINTF(sc, ATH_DBG_FATAL,
-                       "Failed to allocate beacon descriptors: %d\n", error);
+               ath_print(common, ATH_DBG_FATAL,
+                         "Failed to allocate beacon descriptors: %d\n", error);
                goto err;
        }
 
+       INIT_DELAYED_WORK(&sc->tx_complete_work, ath_tx_complete_poll_work);
+
 err:
        if (error != 0)
                ath_tx_cleanup(sc);
@@ -2115,7 +2168,6 @@ void ath_tx_node_init(struct ath_softc *sc, struct ath_node *an)
                tid->ac = &an->ac[acno];
                tid->state &= ~AGGR_ADDBA_COMPLETE;
                tid->state &= ~AGGR_ADDBA_PROGRESS;
-               tid->addba_exchangeattempts = 0;
        }
 
        for (acno = 0, ac = &an->ac[acno];
@@ -2172,7 +2224,6 @@ void ath_tx_node_cleanup(struct ath_softc *sc, struct ath_node *an)
                                        tid->sched = false;
                                        ath_tid_drain(sc, txq, tid);
                                        tid->state &= ~AGGR_ADDBA_COMPLETE;
-                                       tid->addba_exchangeattempts = 0;
                                        tid->state &= ~AGGR_CLEANUP;
                                }
                        }