ath9k: Add support for multiple secondary virtual wiphys
[linux-2.6.git] / drivers / net / wireless / ath9k / xmit.c
index a29b998..3c48fa5 100644 (file)
@@ -14,7 +14,7 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include "core.h"
+#include "ath9k.h"
 
 #define BITS_PER_BYTE           8
 #define OFDM_PLCP_BITS          22
@@ -151,7 +151,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));
-               list_cut_position(&bf_head, &tid->buf_q, &bf->bf_lastfrm->list);
+               list_move_tail(&bf->list, &bf_head);
                ath_tx_send_normal(sc, txq, tid, &bf_head);
        }
 
@@ -212,9 +212,9 @@ static void ath_tid_drain(struct ath_softc *sc, struct ath_txq *txq,
        for (;;) {
                if (list_empty(&tid->buf_q))
                        break;
-               bf = list_first_entry(&tid->buf_q, struct ath_buf, list);
 
-               list_cut_position(&bf_head, &tid->buf_q, &bf->bf_lastfrm->list);
+               bf = list_first_entry(&tid->buf_q, struct ath_buf, list);
+               list_move_tail(&bf->list, &bf_head);
 
                if (bf_isretried(bf))
                        ath_tx_update_baw(sc, tid, bf->bf_seqno);
@@ -241,53 +241,75 @@ static void ath_tx_set_retry(struct ath_softc *sc, struct ath_buf *bf)
        hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_RETRY);
 }
 
-static void ath_tx_complete_aggr_rifs(struct ath_softc *sc, struct ath_txq *txq,
-                                     struct ath_buf *bf, struct list_head *bf_q,
-                                     int txok)
+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)));
+       tbf = list_first_entry(&sc->tx.txbuf, struct ath_buf, list);
+       list_del(&tbf->list);
+       spin_unlock_bh(&sc->tx.txbuflock);
+
+       ATH_TXBUF_RESET(tbf);
+
+       tbf->bf_mpdu = bf->bf_mpdu;
+       tbf->bf_buf_addr = bf->bf_buf_addr;
+       *(tbf->bf_desc) = *(bf->bf_desc);
+       tbf->bf_state = bf->bf_state;
+       tbf->bf_dmacontext = bf->bf_dmacontext;
+
+       return tbf;
+}
+
+static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
+                                struct ath_buf *bf, struct list_head *bf_q,
+                                int txok)
 {
        struct ath_node *an = NULL;
        struct sk_buff *skb;
-       struct ieee80211_tx_info *tx_info;
+       struct ieee80211_sta *sta;
+       struct ieee80211_hdr *hdr;
        struct ath_atx_tid *tid = NULL;
-       struct ath_buf *bf_last = bf->bf_lastbf;
+       struct ath_buf *bf_next, *bf_last = bf->bf_lastbf;
        struct ath_desc *ds = bf_last->bf_desc;
-       struct ath_buf *bf_next, *bf_lastq = NULL;
        struct list_head bf_head, bf_pending;
        u16 seq_st = 0;
        u32 ba[WME_BA_BMP_SIZE >> 5];
        int isaggr, txfail, txpending, sendbar = 0, needreset = 0;
 
        skb = (struct sk_buff *)bf->bf_mpdu;
-       tx_info = IEEE80211_SKB_CB(skb);
+       hdr = (struct ieee80211_hdr *)skb->data;
 
-       if (tx_info->control.sta) {
-               an = (struct ath_node *)tx_info->control.sta->drv_priv;
-               tid = ATH_AN_2_TID(an, bf->bf_tidno);
+       rcu_read_lock();
+
+       sta = ieee80211_find_sta(sc->hw, hdr->addr1);
+       if (!sta) {
+               rcu_read_unlock();
+               return;
        }
 
+       an = (struct ath_node *)sta->drv_priv;
+       tid = ATH_AN_2_TID(an, bf->bf_tidno);
+
        isaggr = bf_isaggr(bf);
-       if (isaggr) {
-               if (txok) {
-                       if (ATH_DS_TX_BA(ds)) {
-                               seq_st = ATH_DS_BA_SEQ(ds);
-                               memcpy(ba, ATH_DS_BA_BITMAP(ds),
-                                      WME_BA_BMP_SIZE >> 3);
-                       } else {
-                               memset(ba, 0, WME_BA_BMP_SIZE >> 3);
+       memset(ba, 0, WME_BA_BMP_SIZE >> 3);
 
-                               /*
-                                * AR5416 can become deaf/mute when BA
-                                * issue happens. Chip needs to be reset.
-                                * But AP code may have sychronization issues
-                                * when perform internal reset in this routine.
-                                * Only enable reset in STA mode for now.
-                                */
-                               if (sc->sc_ah->ah_opmode ==
-                                           NL80211_IFTYPE_STATION)
-                                       needreset = 1;
-                       }
+       if (isaggr && txok) {
+               if (ATH_DS_TX_BA(ds)) {
+                       seq_st = ATH_DS_BA_SEQ(ds);
+                       memcpy(ba, ATH_DS_BA_BITMAP(ds),
+                              WME_BA_BMP_SIZE >> 3);
                } else {
-                       memset(ba, 0, WME_BA_BMP_SIZE >> 3);
+                       /*
+                        * AR5416 can become deaf/mute when BA
+                        * issue happens. Chip needs to be reset.
+                        * But AP code may have sychronization issues
+                        * when perform internal reset in this routine.
+                        * Only enable reset in STA mode for now.
+                        */
+                       if (sc->sc_ah->opmode == NL80211_IFTYPE_STATION)
+                               needreset = 1;
                }
        }
 
@@ -304,7 +326,6 @@ static void ath_tx_complete_aggr_rifs(struct ath_softc *sc, struct ath_txq *txq,
                } else if (!isaggr && txok) {
                        /* transmit completion */
                } else {
-
                        if (!(tid->state & AGGR_CLEANUP) &&
                            ds->ds_txstat.ts_flags != ATH9K_TX_SW_ABORTED) {
                                if (bf->bf_retries < ATH_MAX_SW_RETRIES) {
@@ -325,19 +346,10 @@ static void ath_tx_complete_aggr_rifs(struct ath_softc *sc, struct ath_txq *txq,
                }
 
                if (bf_next == NULL) {
-                       ASSERT(bf->bf_lastfrm == bf_last);
-                       if (!list_empty(bf_q)) {
-                               bf_lastq = list_entry(bf_q->prev,
-                                       struct ath_buf, list);
-                               list_cut_position(&bf_head,
-                                       bf_q, &bf_lastq->list);
-                       } else {
-                               INIT_LIST_HEAD(&bf_head);
-                       }
+                       INIT_LIST_HEAD(&bf_head);
                } else {
                        ASSERT(!list_empty(bf_q));
-                       list_cut_position(&bf_head,
-                               bf_q, &bf->bf_lastfrm->list);
+                       list_move_tail(&bf->list, &bf_head);
                }
 
                if (!txpending) {
@@ -351,53 +363,20 @@ static void ath_tx_complete_aggr_rifs(struct ath_softc *sc, struct ath_txq *txq,
 
                        ath_tx_complete_buf(sc, bf, &bf_head, !txfail, sendbar);
                } else {
-                       /*
-                        * retry the un-acked ones
-                        */
+                       /* retry the un-acked ones */
                        if (bf->bf_next == NULL &&
                            bf_last->bf_status & ATH_BUFSTATUS_STALE) {
                                struct ath_buf *tbf;
 
-                               /* allocate new descriptor */
-                               spin_lock_bh(&sc->tx.txbuflock);
-                               ASSERT(!list_empty((&sc->tx.txbuf)));
-                               tbf = list_first_entry(&sc->tx.txbuf,
-                                               struct ath_buf, list);
-                               list_del(&tbf->list);
-                               spin_unlock_bh(&sc->tx.txbuflock);
-
-                               ATH_TXBUF_RESET(tbf);
-
-                               /* copy descriptor content */
-                               tbf->bf_mpdu = bf_last->bf_mpdu;
-                               tbf->bf_buf_addr = bf_last->bf_buf_addr;
-                               *(tbf->bf_desc) = *(bf_last->bf_desc);
-
-                               /* link it to the frame */
-                               if (bf_lastq) {
-                                       bf_lastq->bf_desc->ds_link =
-                                               tbf->bf_daddr;
-                                       bf->bf_lastfrm = tbf;
-                                       ath9k_hw_cleartxdesc(sc->sc_ah,
-                                               bf->bf_lastfrm->bf_desc);
-                               } else {
-                                       tbf->bf_state = bf_last->bf_state;
-                                       tbf->bf_lastfrm = tbf;
-                                       ath9k_hw_cleartxdesc(sc->sc_ah,
-                                               tbf->bf_lastfrm->bf_desc);
-
-                                       /* copy the DMA context */
-                                       tbf->bf_dmacontext =
-                                               bf_last->bf_dmacontext;
-                               }
+                               tbf = ath_clone_txbuf(sc, bf_last);
+                               ath9k_hw_cleartxdesc(sc->sc_ah, tbf->bf_desc);
                                list_add_tail(&tbf->list, &bf_head);
                        } else {
                                /*
                                 * Clear descriptor status words for
                                 * software retry
                                 */
-                               ath9k_hw_cleartxdesc(sc->sc_ah,
-                                                    bf->bf_lastfrm->bf_desc);
+                               ath9k_hw_cleartxdesc(sc->sc_ah, bf->bf_desc);
                        }
 
                        /*
@@ -411,27 +390,19 @@ static void ath_tx_complete_aggr_rifs(struct ath_softc *sc, struct ath_txq *txq,
        }
 
        if (tid->state & AGGR_CLEANUP) {
-               /* check to see if we're done with cleaning the h/w queue */
-               spin_lock_bh(&txq->axq_lock);
-
                if (tid->baw_head == tid->baw_tail) {
                        tid->state &= ~AGGR_ADDBA_COMPLETE;
                        tid->addba_exchangeattempts = 0;
-                       spin_unlock_bh(&txq->axq_lock);
-
                        tid->state &= ~AGGR_CLEANUP;
 
                        /* send buffered frames as singles */
                        ath_tx_flush_tid(sc, tid);
-               } else
-                       spin_unlock_bh(&txq->axq_lock);
-
+               }
+               rcu_read_unlock();
                return;
        }
 
-       /*
-        * prepend un-acked frames to the beginning of the pending frame queue
-        */
+       /* prepend un-acked frames to the beginning of the pending frame queue */
        if (!list_empty(&bf_pending)) {
                spin_lock_bh(&txq->axq_lock);
                list_splice(&bf_pending, &tid->buf_q);
@@ -439,10 +410,10 @@ static void ath_tx_complete_aggr_rifs(struct ath_softc *sc, struct ath_txq *txq,
                spin_unlock_bh(&txq->axq_lock);
        }
 
+       rcu_read_unlock();
+
        if (needreset)
                ath_reset(sc, false);
-
-       return;
 }
 
 static u32 ath_lookup_rate(struct ath_softc *sc, struct ath_buf *bf,
@@ -453,15 +424,14 @@ static u32 ath_lookup_rate(struct ath_softc *sc, struct ath_buf *bf,
        struct ieee80211_tx_info *tx_info;
        struct ieee80211_tx_rate *rates;
        struct ath_tx_info_priv *tx_info_priv;
-       u32 max_4ms_framelen, frame_length;
+       u32 max_4ms_framelen, frmlen;
        u16 aggr_limit, legacy = 0, maxampdu;
        int i;
 
        skb = (struct sk_buff *)bf->bf_mpdu;
        tx_info = IEEE80211_SKB_CB(skb);
        rates = tx_info->control.rates;
-       tx_info_priv =
-               (struct ath_tx_info_priv *)tx_info->rate_driver_data[0];
+       tx_info_priv = (struct ath_tx_info_priv *)tx_info->rate_driver_data[0];
 
        /*
         * Find the lowest frame length among the rate series that will have a
@@ -477,9 +447,8 @@ static u32 ath_lookup_rate(struct ath_softc *sc, struct ath_buf *bf,
                                break;
                        }
 
-                       frame_length =
-                               rate_table->info[rates[i].idx].max_4ms_framelen;
-                       max_4ms_framelen = min(max_4ms_framelen, frame_length);
+                       frmlen = rate_table->info[rates[i].idx].max_4ms_framelen;
+                       max_4ms_framelen = min(max_4ms_framelen, frmlen);
                }
        }
 
@@ -491,8 +460,7 @@ 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);
+       aggr_limit = min(max_4ms_framelen, (u32)ATH_AMPDU_LIMIT_DEFAULT);
 
        /*
         * h/w can accept aggregates upto 16 bit lengths (65535).
@@ -507,9 +475,9 @@ static u32 ath_lookup_rate(struct ath_softc *sc, struct ath_buf *bf,
 }
 
 /*
- * returns the number of delimiters to be added to
+ * Returns the number of delimiters to be added to
  * meet the minimum required mpdudensity.
- * caller should make sure that the rate is  HT rate .
+ * 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)
@@ -566,9 +534,7 @@ static int ath_compute_num_delims(struct ath_softc *sc, struct ath_atx_tid *tid,
        nsymbits = bits_per_symbol[HT_RC_2_MCS(rc)][width];
        minlen = (nsymbols * nsymbits) / BITS_PER_BYTE;
 
-       /* Is frame shorter than required minimum length? */
        if (frmlen < minlen) {
-               /* Get the minimum number of delimiters required. */
                mindelim = (minlen - frmlen) / ATH_AGGR_DELIM_SZ;
                ndelim = max(mindelim, ndelim);
        }
@@ -577,30 +543,22 @@ 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_atx_tid *tid, struct list_head *bf_q,
-                    struct ath_buf **bf_last, struct aggr_rifs_param *param,
-                    int *prev_frames)
+                                            struct ath_atx_tid *tid,
+                                            struct list_head *bf_q)
 {
 #define PADBYTES(_len) ((4 - ((_len) % 4)) % 4)
-       struct ath_buf *bf, *tbf, *bf_first, *bf_prev = NULL;
-       struct list_head bf_head;
-       int rl = 0, nframes = 0, ndelim;
+       struct ath_buf *bf, *bf_first, *bf_prev = NULL;
+       int rl = 0, nframes = 0, ndelim, prev_al = 0;
        u16 aggr_limit = 0, al = 0, bpad = 0,
                al_delta, h_baw = tid->baw_size / 2;
        enum ATH_AGGR_STATUS status = ATH_AGGR_DONE;
-       int prev_al = 0;
-       INIT_LIST_HEAD(&bf_head);
-
-       BUG_ON(list_empty(&tid->buf_q));
 
        bf_first = list_first_entry(&tid->buf_q, struct ath_buf, list);
 
        do {
                bf = list_first_entry(&tid->buf_q, struct ath_buf, list);
 
-               /*
-                * do not step over block-ack window
-                */
+               /* do not step over block-ack window */
                if (!BAW_WITHIN(tid->seq_start, tid->baw_size, bf->bf_seqno)) {
                        status = ATH_AGGR_BAW_CLOSED;
                        break;
@@ -611,29 +569,23 @@ static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc,
                        rl = 1;
                }
 
-               /*
-                * do not exceed aggregation limit
-                */
+               /* do not exceed aggregation limit */
                al_delta = ATH_AGGR_DELIM_SZ + bf->bf_frmlen;
 
-               if (nframes && (aggr_limit <
-                       (al + bpad + al_delta + prev_al))) {
+               if (nframes &&
+                   (aggr_limit < (al + bpad + al_delta + prev_al))) {
                        status = ATH_AGGR_LIMITED;
                        break;
                }
 
-               /*
-                * do not exceed subframe limit
-                */
-               if ((nframes + *prev_frames) >=
-                   min((int)h_baw, ATH_AMPDU_SUBFRAME_DEFAULT)) {
+               /* do not exceed subframe limit */
+               if (nframes >= min((int)h_baw, ATH_AMPDU_SUBFRAME_DEFAULT)) {
                        status = ATH_AGGR_LIMITED;
                        break;
                }
+               nframes++;
 
-               /*
-                * add padding for previous frame to aggregation length
-                */
+               /* add padding for previous frame to aggregation length */
                al += bpad + al_delta;
 
                /*
@@ -641,44 +593,25 @@ static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc,
                 * density for this node.
                 */
                ndelim = ath_compute_num_delims(sc, tid, bf_first, bf->bf_frmlen);
-
                bpad = PADBYTES(al_delta) + (ndelim << 2);
 
                bf->bf_next = NULL;
-               bf->bf_lastfrm->bf_desc->ds_link = 0;
+               bf->bf_desc->ds_link = 0;
 
-               /*
-                * this packet is part of an aggregate
-                * - remove all descriptors belonging to this frame from
-                *   software queue
-                * - add it to block ack window
-                * - set up descriptors for aggregation
-                */
-               list_cut_position(&bf_head, &tid->buf_q, &bf->bf_lastfrm->list);
+               /* link buffers of this frame to the aggregate */
                ath_tx_addto_baw(sc, tid, bf);
-
-               list_for_each_entry(tbf, &bf_head, list) {
-                       ath9k_hw_set11n_aggr_middle(sc->sc_ah,
-                               tbf->bf_desc, ndelim);
-               }
-
-               /*
-                * link buffers of this frame to the aggregate
-                */
-               list_splice_tail_init(&bf_head, bf_q);
-               nframes++;
-
+               ath9k_hw_set11n_aggr_middle(sc->sc_ah, bf->bf_desc, ndelim);
+               list_move_tail(&bf->list, bf_q);
                if (bf_prev) {
                        bf_prev->bf_next = bf;
-                       bf_prev->bf_lastfrm->bf_desc->ds_link = bf->bf_daddr;
+                       bf_prev->bf_desc->ds_link = bf->bf_daddr;
                }
                bf_prev = bf;
-
        } while (!list_empty(&tid->buf_q));
 
        bf_first->bf_al = al;
        bf_first->bf_nframes = nframes;
-       *bf_last = bf_prev;
+
        return status;
 #undef PADBYTES
 }
@@ -686,11 +619,9 @@ static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc,
 static void ath_tx_sched_aggr(struct ath_softc *sc, struct ath_txq *txq,
                              struct ath_atx_tid *tid)
 {
-       struct ath_buf *bf, *tbf, *bf_last, *bf_lastaggr = NULL;
+       struct ath_buf *bf;
        enum ATH_AGGR_STATUS status;
        struct list_head bf_q;
-       struct aggr_rifs_param param = {0, 0, 0, 0, NULL};
-       int prev_frames = 0;
 
        do {
                if (list_empty(&tid->buf_q))
@@ -698,66 +629,36 @@ 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, &bf_lastaggr, &param,
-                                         &prev_frames);
+               status = ath_tx_form_aggr(sc, tid, &bf_q);
 
                /*
-                * no frames picked up to be aggregated; block-ack
-                * window is not open
+                * no frames picked up to be aggregated;
+                * block-ack window is not open.
                 */
                if (list_empty(&bf_q))
                        break;
 
                bf = list_first_entry(&bf_q, struct ath_buf, list);
-               bf_last = list_entry(bf_q.prev, struct ath_buf, list);
-               bf->bf_lastbf = bf_last;
+               bf->bf_lastbf = list_entry(bf_q.prev, struct ath_buf, list);
 
-               /*
-                * if only one frame, send as non-aggregate
-                */
+               /* if only one frame, send as non-aggregate */
                if (bf->bf_nframes == 1) {
-                       ASSERT(bf->bf_lastfrm == bf_last);
-
                        bf->bf_state.bf_type &= ~BUF_AGGR;
-                       /*
-                        * clear aggr bits for every descriptor
-                        * XXX TODO: is there a way to optimize it?
-                        */
-                       list_for_each_entry(tbf, &bf_q, list) {
-                               ath9k_hw_clr11n_aggr(sc->sc_ah, tbf->bf_desc);
-                       }
-
+                       ath9k_hw_clr11n_aggr(sc->sc_ah, bf->bf_desc);
                        ath_buf_set_rate(sc, bf);
                        ath_tx_txqaddbuf(sc, txq, &bf_q);
                        continue;
                }
 
-               /*
-                * setup first desc with rate and aggr info
-                */
+               /* setup first desc of aggregate */
                bf->bf_state.bf_type |= BUF_AGGR;
                ath_buf_set_rate(sc, bf);
                ath9k_hw_set11n_aggr_first(sc->sc_ah, bf->bf_desc, bf->bf_al);
 
-               /*
-                * anchor last frame of aggregate correctly
-                */
-               ASSERT(bf_lastaggr);
-               ASSERT(bf_lastaggr->bf_lastfrm == bf_last);
-               tbf = bf_lastaggr;
-               ath9k_hw_set11n_aggr_last(sc->sc_ah, tbf->bf_desc);
-
-               /* XXX: We don't enter into this loop, consider removing this */
-               while (!list_empty(&bf_q) && !list_is_last(&tbf->list, &bf_q)) {
-                       tbf = list_entry(tbf->list.next, struct ath_buf, list);
-                       ath9k_hw_set11n_aggr_last(sc->sc_ah, tbf->bf_desc);
-               }
+               /* anchor last desc of aggregate */
+               ath9k_hw_set11n_aggr_last(sc->sc_ah, bf->bf_lastbf->bf_desc);
 
                txq->axq_aggr_depth++;
-
-               /*
-                * Normal aggregate, queue to hardware
-                */
                ath_tx_txqaddbuf(sc, txq, &bf_q);
 
        } while (txq->axq_depth < ATH_AGGR_MIN_QDEPTH &&
@@ -776,6 +677,7 @@ int ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta,
                txtid = ATH_AN_2_TID(an, tid);
                txtid->state |= AGGR_ADDBA_PROGRESS;
                ath_tx_pause_tid(sc, txtid);
+               *ssn = txtid->seq_start;
        }
 
        return 0;
@@ -812,19 +714,17 @@ int ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid)
                         */
                        break;
                }
-               list_cut_position(&bf_head,
-                       &txtid->buf_q, &bf->bf_lastfrm->list);
+               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);
        }
+       spin_unlock_bh(&txq->axq_lock);
 
        if (txtid->baw_head != txtid->baw_tail) {
-               spin_unlock_bh(&txq->axq_lock);
                txtid->state |= AGGR_CLEANUP;
        } else {
                txtid->state &= ~AGGR_ADDBA_COMPLETE;
                txtid->addba_exchangeattempts = 0;
-               spin_unlock_bh(&txq->axq_lock);
                ath_tx_flush_tid(sc, txtid);
        }
 
@@ -872,24 +772,6 @@ bool ath_tx_aggr_check(struct ath_softc *sc, struct ath_node *an, u8 tidno)
 /* Queue Management */
 /********************/
 
-static u32 ath_txq_depth(struct ath_softc *sc, int qnum)
-{
-       return sc->tx.txq[qnum].axq_depth;
-}
-
-static void ath_get_beaconconfig(struct ath_softc *sc, int if_id,
-                                struct ath_beacon_config *conf)
-{
-       struct ieee80211_hw *hw = sc->hw;
-
-       /* fill in beacon config data */
-
-       conf->beacon_interval = hw->conf.beacon_int;
-       conf->listen_interval = 100;
-       conf->dtim_count = 1;
-       conf->bmiss_timeout = ATH_DEFAULT_BMISS_LIMIT * conf->listen_interval;
-}
-
 static void ath_txq_drain_pending_buffers(struct ath_softc *sc,
                                          struct ath_txq *txq)
 {
@@ -909,7 +791,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_hal *ah = sc->sc_ah;
+       struct ath_hw *ah = sc->sc_ah;
        struct ath9k_tx_queue_info qi;
        int qnum;
 
@@ -1026,7 +908,7 @@ struct ath_txq *ath_test_get_txq(struct ath_softc *sc, struct sk_buff *skb)
 int ath_txq_update(struct ath_softc *sc, int qnum,
                   struct ath9k_tx_queue_info *qinfo)
 {
-       struct ath_hal *ah = sc->sc_ah;
+       struct ath_hw *ah = sc->sc_ah;
        int error = 0;
        struct ath9k_tx_queue_info qi;
 
@@ -1064,20 +946,18 @@ int ath_cabq_update(struct ath_softc *sc)
 {
        struct ath9k_tx_queue_info qi;
        int qnum = sc->beacon.cabq->axq_qnum;
-       struct ath_beacon_config conf;
 
        ath9k_hw_get_txq_props(sc->sc_ah, qnum, &qi);
        /*
         * Ensure the readytime % is within the bounds.
         */
-       if (sc->sc_config.cabqReadytime < ATH9K_READY_TIME_LO_BOUND)
-               sc->sc_config.cabqReadytime = ATH9K_READY_TIME_LO_BOUND;
-       else if (sc->sc_config.cabqReadytime > ATH9K_READY_TIME_HI_BOUND)
-               sc->sc_config.cabqReadytime = ATH9K_READY_TIME_HI_BOUND;
-
-       ath_get_beaconconfig(sc, ATH_IF_ID_ANY, &conf);
-       qi.tqi_readyTime =
-               (conf.beacon_interval * sc->sc_config.cabqReadytime) / 100;
+       if (sc->config.cabqReadytime < ATH9K_READY_TIME_LO_BOUND)
+               sc->config.cabqReadytime = ATH9K_READY_TIME_LO_BOUND;
+       else if (sc->config.cabqReadytime > ATH9K_READY_TIME_HI_BOUND)
+               sc->config.cabqReadytime = ATH9K_READY_TIME_HI_BOUND;
+
+       qi.tqi_readyTime = (sc->hw->conf.beacon_int *
+                           sc->config.cabqReadytime) / 100;
        ath_txq_update(sc, qnum, &qi);
 
        return 0;
@@ -1130,7 +1010,7 @@ void ath_draintxq(struct ath_softc *sc, struct ath_txq *txq, bool retry_tx)
                spin_unlock_bh(&txq->axq_lock);
 
                if (bf_isampdu(bf))
-                       ath_tx_complete_aggr_rifs(sc, txq, bf, &bf_head, 0);
+                       ath_tx_complete_aggr(sc, txq, bf, &bf_head, 0);
                else
                        ath_tx_complete_buf(sc, bf, &bf_head, 0, 0);
        }
@@ -1147,7 +1027,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_hal *ah = sc->sc_ah;
+       struct ath_hw *ah = sc->sc_ah;
        struct ath_txq *txq;
        int i, npend = 0;
 
@@ -1172,7 +1052,7 @@ void ath_drain_all_txq(struct ath_softc *sc, bool retry_tx)
                DPRINTF(sc, ATH_DBG_XMIT, "Unable to stop TxDMA. Reset HAL!\n");
 
                spin_lock_bh(&sc->sc_resetlock);
-               r = ath9k_hw_reset(ah, sc->sc_ah->ah_curchan, true);
+               r = ath9k_hw_reset(ah, sc->sc_ah->curchan, true);
                if (r)
                        DPRINTF(sc, ATH_DBG_FATAL,
                                "Unable to reset hardware; reset status %u\n",
@@ -1265,7 +1145,7 @@ int ath_tx_setup(struct ath_softc *sc, int haltype)
 static void ath_tx_txqaddbuf(struct ath_softc *sc, struct ath_txq *txq,
                             struct list_head *head)
 {
-       struct ath_hal *ah = sc->sc_ah;
+       struct ath_hw *ah = sc->sc_ah;
        struct ath_buf *bf;
 
        /*
@@ -1326,8 +1206,6 @@ static void ath_tx_send_ampdu(struct ath_softc *sc, struct ath_atx_tid *tid,
 {
        struct ath_buf *bf;
 
-       BUG_ON(list_empty(bf_head));
-
        bf = list_first_entry(bf_head, struct ath_buf, list);
        bf->bf_state.bf_type |= BUF_AMPDU;
 
@@ -1345,7 +1223,7 @@ static void ath_tx_send_ampdu(struct ath_softc *sc, struct ath_atx_tid *tid,
                 * Add this frame to software queue for scheduling later
                 * for aggregation.
                 */
-               list_splice_tail_init(bf_head, &tid->buf_q);
+               list_move_tail(&bf->list, &tid->buf_q);
                ath_tx_queue_tid(txctl->txq, tid);
                return;
        }
@@ -1355,11 +1233,9 @@ static void ath_tx_send_ampdu(struct ath_softc *sc, struct ath_atx_tid *tid,
 
        /* Queue to h/w without aggregation */
        bf->bf_nframes = 1;
-       bf->bf_lastbf = bf->bf_lastfrm; /* one single frame */
+       bf->bf_lastbf = bf;
        ath_buf_set_rate(sc, bf);
        ath_tx_txqaddbuf(sc, txctl->txq, bf_head);
-
-       return;
 }
 
 static void ath_tx_send_normal(struct ath_softc *sc, struct ath_txq *txq,
@@ -1368,8 +1244,6 @@ static void ath_tx_send_normal(struct ath_softc *sc, struct ath_txq *txq,
 {
        struct ath_buf *bf;
 
-       BUG_ON(list_empty(bf_head));
-
        bf = list_first_entry(bf_head, struct ath_buf, list);
        bf->bf_state.bf_type &= ~BUF_AMPDU;
 
@@ -1377,7 +1251,7 @@ static void ath_tx_send_normal(struct ath_softc *sc, struct ath_txq *txq,
        INCR(tid->seq_start, IEEE80211_SEQ_MAX);
 
        bf->bf_nframes = 1;
-       bf->bf_lastbf = bf->bf_lastfrm;
+       bf->bf_lastbf = bf;
        ath_buf_set_rate(sc, bf);
        ath_tx_txqaddbuf(sc, txq, bf_head);
 }
@@ -1492,8 +1366,6 @@ static int setup_tx_flags(struct ath_softc *sc, struct sk_buff *skb,
 
        if (tx_info->flags & IEEE80211_TX_CTL_NO_ACK)
                flags |= ATH9K_TXDESC_NOACK;
-       if (tx_info->control.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS)
-               flags |= ATH9K_TXDESC_RTSENA;
 
        return flags;
 }
@@ -1539,143 +1411,98 @@ 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_hal *ah = sc->sc_ah;
-       struct ath_rate_table *rt;
-       struct ath_desc *ds = bf->bf_desc;
-       struct ath_desc *lastds = bf->bf_lastbf->bf_desc;
+       struct ath_rate_table *rt = sc->cur_rate_table;
        struct ath9k_11n_rate_series series[4];
        struct sk_buff *skb;
        struct ieee80211_tx_info *tx_info;
        struct ieee80211_tx_rate *rates;
        struct ieee80211_hdr *hdr;
-       struct ieee80211_hw *hw = sc->hw;
-       int i, flags, rtsctsena = 0, enable_g_protection = 0;
-       u32 ctsduration = 0;
-       u8 rix = 0, cix, ctsrate = 0;
-       __le16 fc;
+       int i, flags = 0;
+       u8 rix = 0, ctsrate = 0;
+       bool is_pspoll;
 
        memset(series, 0, sizeof(struct ath9k_11n_rate_series) * 4);
 
        skb = (struct sk_buff *)bf->bf_mpdu;
-       hdr = (struct ieee80211_hdr *)skb->data;
-       fc = hdr->frame_control;
        tx_info = IEEE80211_SKB_CB(skb);
        rates = tx_info->control.rates;
-
-       if (ieee80211_has_morefrags(fc) ||
-           (le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG)) {
-               rates[1].count = rates[2].count = rates[3].count = 0;
-               rates[1].idx = rates[2].idx = rates[3].idx = 0;
-               rates[0].count = ATH_TXMAXTRY;
-       }
-
-       /* get the cix for the lowest valid rix */
-       rt = sc->cur_rate_table;
-       for (i = 3; i >= 0; i--) {
-               if (rates[i].count && (rates[i].idx >= 0)) {
-                       rix = rates[i].idx;
-                       break;
-               }
-       }
-
-       flags = (bf->bf_flags & (ATH9K_TXDESC_RTSENA | ATH9K_TXDESC_CTSENA));
-       cix = rt->info[rix].ctrl_rate;
-
-       /* All protection frames are transmited at 2Mb/s for 802.11g,
-        * otherwise we transmit them at 1Mb/s */
-       if (hw->conf.channel->band == IEEE80211_BAND_2GHZ &&
-         !conf_is_ht(&hw->conf))
-               enable_g_protection = 1;
+       hdr = (struct ieee80211_hdr *)skb->data;
+       is_pspoll = ieee80211_is_pspoll(hdr->frame_control);
 
        /*
-        * If 802.11g protection is enabled, determine whether to use RTS/CTS or
-        * just CTS.  Note that this is only done for OFDM/HT unicast frames.
+        * We check if Short Preamble is needed for the CTS rate by
+        * checking the BSS's global flag.
+        * But for the rate series, IEEE80211_TX_RC_USE_SHORT_PREAMBLE is used.
         */
-       if (sc->sc_protmode != PROT_M_NONE && !(bf->bf_flags & ATH9K_TXDESC_NOACK)
-           && (rt->info[rix].phy == WLAN_RC_PHY_OFDM ||
-               WLAN_RC_PHY_HT(rt->info[rix].phy))) {
-               if (sc->sc_protmode == PROT_M_RTSCTS)
-                       flags = ATH9K_TXDESC_RTSENA;
-               else if (sc->sc_protmode == PROT_M_CTSONLY)
-                       flags = ATH9K_TXDESC_CTSENA;
-
-               cix = rt->info[enable_g_protection].ctrl_rate;
-               rtsctsena = 1;
-       }
+       if (sc->sc_flags & SC_OP_PREAMBLE_SHORT)
+               ctsrate = rt->info[tx_info->control.rts_cts_rate_idx].ratecode |
+                       rt->info[tx_info->control.rts_cts_rate_idx].short_preamble;
+       else
+               ctsrate = rt->info[tx_info->control.rts_cts_rate_idx].ratecode;
 
-       /* For 11n, the default behavior is to enable RTS for hw retried frames.
-        * We enable the global flag here and let rate series flags determine
-        * which rates will actually use RTS.
+       /*
+        * ATH9K_TXDESC_RTSENA and ATH9K_TXDESC_CTSENA are mutually exclusive.
+        * Check the first rate in the series to decide whether RTS/CTS
+        * or CTS-to-self has to be used.
         */
-       if ((ah->ah_caps.hw_caps & ATH9K_HW_CAP_HT) && bf_isdata(bf)) {
-               /* 802.11g protection not needed, use our default behavior */
-               if (!rtsctsena)
-                       flags = ATH9K_TXDESC_RTSENA;
-       }
+       if (rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT)
+               flags = ATH9K_TXDESC_CTSENA;
+       else if (rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS)
+               flags = ATH9K_TXDESC_RTSENA;
 
-       /* Set protection if aggregate protection on */
-       if (sc->sc_config.ath_aggr_prot &&
+       /* FIXME: Handle aggregation protection */
+       if (sc->config.ath_aggr_prot &&
            (!bf_isaggr(bf) || (bf_isaggr(bf) && bf->bf_al < 8192))) {
                flags = ATH9K_TXDESC_RTSENA;
-               cix = rt->info[enable_g_protection].ctrl_rate;
-               rtsctsena = 1;
        }
 
        /* For AR5416 - RTS cannot be followed by a frame larger than 8K */
-       if (bf_isaggr(bf) && (bf->bf_al > ah->ah_caps.rts_aggr_limit))
+       if (bf_isaggr(bf) && (bf->bf_al > sc->sc_ah->caps.rts_aggr_limit))
                flags &= ~(ATH9K_TXDESC_RTSENA);
 
-       /*
-        * CTS transmit rate is derived from the transmit rate by looking in the
-        * h/w rate table.  We must also factor in whether or not a short
-        * preamble is to be used. NB: cix is set above where RTS/CTS is enabled
-        */
-       ctsrate = rt->info[cix].ratecode |
-               (bf_isshpreamble(bf) ? rt->info[cix].short_preamble : 0);
-
        for (i = 0; i < 4; i++) {
                if (!rates[i].count || (rates[i].idx < 0))
                        continue;
 
                rix = rates[i].idx;
-
-               series[i].Rate = rt->info[rix].ratecode |
-                       (bf_isshpreamble(bf) ? rt->info[rix].short_preamble : 0);
-
                series[i].Tries = rates[i].count;
+               series[i].ChSel = sc->tx_chainmask;
 
-               series[i].RateFlags = (
-                       (rates[i].flags & IEEE80211_TX_RC_USE_RTS_CTS) ?
-                               ATH9K_RATESERIES_RTS_CTS : 0) |
-                       ((rates[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH) ?
-                               ATH9K_RATESERIES_2040 : 0) |
-                       ((rates[i].flags & IEEE80211_TX_RC_SHORT_GI) ?
-                               ATH9K_RATESERIES_HALFGI : 0);
+               if (rates[i].flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE)
+                       series[i].Rate = rt->info[rix].ratecode |
+                               rt->info[rix].short_preamble;
+               else
+                       series[i].Rate = rt->info[rix].ratecode;
+
+               if (rates[i].flags & IEEE80211_TX_RC_USE_RTS_CTS)
+                       series[i].RateFlags |= ATH9K_RATESERIES_RTS_CTS;
+               if (rates[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
+                       series[i].RateFlags |= ATH9K_RATESERIES_2040;
+               if (rates[i].flags & IEEE80211_TX_RC_SHORT_GI)
+                       series[i].RateFlags |= ATH9K_RATESERIES_HALFGI;
 
                series[i].PktDuration = ath_pkt_duration(sc, rix, bf,
                         (rates[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH) != 0,
                         (rates[i].flags & IEEE80211_TX_RC_SHORT_GI),
-                        bf_isshpreamble(bf));
-
-               series[i].ChSel = sc->sc_tx_chainmask;
-
-               if (rtsctsena)
-                       series[i].RateFlags |= ATH9K_RATESERIES_RTS_CTS;
+                        (rates[i].flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE));
        }
 
        /* set dur_update_en for l-sig computation except for PS-Poll frames */
-       ath9k_hw_set11n_ratescenario(ah, ds, lastds, !bf_ispspoll(bf),
-                                    ctsrate, ctsduration,
-                                    series, 4, flags);
+       ath9k_hw_set11n_ratescenario(sc->sc_ah, bf->bf_desc,
+                                    bf->bf_lastbf->bf_desc,
+                                    !is_pspoll, ctsrate,
+                                    0, series, 4, flags);
 
-       if (sc->sc_config.ath_aggr_prot && flags)
-               ath9k_hw_set11n_burstduration(ah, ds, 8192);
+       if (sc->config.ath_aggr_prot && flags)
+               ath9k_hw_set11n_burstduration(sc->sc_ah, bf->bf_desc, 8192);
 }
 
-static int ath_tx_setup_buffer(struct ath_softc *sc, struct ath_buf *bf,
+static int ath_tx_setup_buffer(struct ieee80211_hw *hw, struct ath_buf *bf,
                                struct sk_buff *skb,
                                struct ath_tx_control *txctl)
 {
+       struct ath_wiphy *aphy = hw->priv;
+       struct ath_softc *sc = aphy->sc;
        struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
        struct ath_tx_info_priv *tx_info_priv;
@@ -1686,6 +1513,7 @@ static int ath_tx_setup_buffer(struct ath_softc *sc, struct ath_buf *bf,
        if (unlikely(!tx_info_priv))
                return -ENOMEM;
        tx_info->rate_driver_data[0] = tx_info_priv;
+       tx_info_priv->aphy = aphy;
        hdrlen = ieee80211_get_hdrlen_from_skb(skb);
        fc = hdr->frame_control;
 
@@ -1693,27 +1521,13 @@ static int ath_tx_setup_buffer(struct ath_softc *sc, struct ath_buf *bf,
 
        bf->bf_frmlen = skb->len + FCS_LEN - (hdrlen & 3);
 
-       ieee80211_is_data(fc) ?
-               (bf->bf_state.bf_type |= BUF_DATA) :
-               (bf->bf_state.bf_type &= ~BUF_DATA);
-       ieee80211_is_back_req(fc) ?
-               (bf->bf_state.bf_type |= BUF_BAR) :
-               (bf->bf_state.bf_type &= ~BUF_BAR);
-       ieee80211_is_pspoll(fc) ?
-               (bf->bf_state.bf_type |= BUF_PSPOLL) :
-               (bf->bf_state.bf_type &= ~BUF_PSPOLL);
-       (sc->sc_flags & SC_OP_PREAMBLE_SHORT) ?
-               (bf->bf_state.bf_type |= BUF_SHORT_PREAMBLE) :
-               (bf->bf_state.bf_type &= ~BUF_SHORT_PREAMBLE);
-       (conf_is_ht(&sc->hw->conf) && !is_pae(skb) &&
-        (tx_info->flags & IEEE80211_TX_CTL_AMPDU)) ?
-               (bf->bf_state.bf_type |= BUF_HT) :
-               (bf->bf_state.bf_type &= ~BUF_HT);
+       if ((conf_is_ht(&sc->hw->conf) && !is_pae(skb) &&
+            (tx_info->flags & IEEE80211_TX_CTL_AMPDU)))
+               bf->bf_state.bf_type |= BUF_HT;
 
        bf->bf_flags = setup_tx_flags(sc, skb, txctl->txq);
 
        bf->bf_keytype = get_hw_crypto_keytype(skb);
-
        if (bf->bf_keytype != ATH9K_KEY_TYPE_CLEAR) {
                bf->bf_frmlen += tx_info->control.hw_key->icv_len;
                bf->bf_keyix = tx_info->control.hw_key->hw_key_idx;
@@ -1749,7 +1563,7 @@ static void ath_tx_start_dma(struct ath_softc *sc, struct ath_buf *bf,
        struct list_head bf_head;
        struct ath_desc *ds;
        struct ath_atx_tid *tid;
-       struct ath_hal *ah = sc->sc_ah;
+       struct ath_hw *ah = sc->sc_ah;
        int frm_type;
 
        frm_type = get_hw_packet_type(skb);
@@ -1770,8 +1584,6 @@ static void ath_tx_start_dma(struct ath_softc *sc, struct ath_buf *bf,
                            true,       /* last segment */
                            ds);        /* first descriptor */
 
-       bf->bf_lastfrm = bf;
-
        spin_lock_bh(&txctl->txq->axq_lock);
 
        if (bf_isht(bf) && (sc->sc_flags & SC_OP_TXAGGR) &&
@@ -1805,9 +1617,11 @@ static void ath_tx_start_dma(struct ath_softc *sc, struct ath_buf *bf,
 }
 
 /* Upon failure caller should free skb */
-int ath_tx_start(struct ath_softc *sc, struct sk_buff *skb,
+int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
                 struct ath_tx_control *txctl)
 {
+       struct ath_wiphy *aphy = hw->priv;
+       struct ath_softc *sc = aphy->sc;
        struct ath_buf *bf;
        int r;
 
@@ -1817,7 +1631,7 @@ int ath_tx_start(struct ath_softc *sc, struct sk_buff *skb,
                return -1;
        }
 
-       r = ath_tx_setup_buffer(sc, bf, skb, txctl);
+       r = ath_tx_setup_buffer(hw, bf, skb, txctl);
        if (unlikely(r)) {
                struct ath_txq *txq = txctl->txq;
 
@@ -1828,7 +1642,7 @@ int ath_tx_start(struct ath_softc *sc, struct sk_buff *skb,
                 * we will at least have to run TX completionon one buffer
                 * on the queue */
                spin_lock_bh(&txq->axq_lock);
-               if (ath_txq_depth(sc, txq->axq_qnum) > 1) {
+               if (sc->tx.txq[txq->axq_qnum].axq_depth > 1) {
                        ieee80211_stop_queue(sc->hw,
                                skb_get_queue_mapping(skb));
                        txq->stopped = 1;
@@ -1847,8 +1661,10 @@ int ath_tx_start(struct ath_softc *sc, struct sk_buff *skb,
        return 0;
 }
 
-void ath_tx_cabq(struct ath_softc *sc, struct sk_buff *skb)
+void ath_tx_cabq(struct ieee80211_hw *hw, struct sk_buff *skb)
 {
+       struct ath_wiphy *aphy = hw->priv;
+       struct ath_softc *sc = aphy->sc;
        int hdrlen, padsize;
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
        struct ath_tx_control txctl;
@@ -1885,7 +1701,7 @@ void ath_tx_cabq(struct ath_softc *sc, struct sk_buff *skb)
 
        DPRINTF(sc, ATH_DBG_XMIT, "transmitting CABQ packet, skb: %p\n", skb);
 
-       if (ath_tx_start(sc, skb, &txctl) != 0) {
+       if (ath_tx_start(hw, skb, &txctl) != 0) {
                DPRINTF(sc, ATH_DBG_XMIT, "CABQ TX failed\n");
                goto exit;
        }
@@ -1909,6 +1725,9 @@ static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb,
 
        DPRINTF(sc, ATH_DBG_XMIT, "TX complete: skb: %p\n", skb);
 
+       if (tx_info_priv)
+               hw = tx_info_priv->aphy->hw;
+
        if (tx_info->flags & IEEE80211_TX_CTL_NO_ACK ||
            tx_info->flags & IEEE80211_TX_STAT_TX_FILTERED) {
                kfree(tx_info_priv);
@@ -2012,6 +1831,7 @@ static int ath_tx_num_badfrms(struct ath_softc *sc, struct ath_buf *bf,
 static void ath_tx_rc_status(struct ath_buf *bf, struct ath_desc *ds, int nbad)
 {
        struct sk_buff *skb = (struct sk_buff *)bf->bf_mpdu;
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
        struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
        struct ath_tx_info_priv *tx_info_priv = ATH_TX_INFO_PRIV(tx_info);
 
@@ -2021,7 +1841,7 @@ static void ath_tx_rc_status(struct ath_buf *bf, struct ath_desc *ds, int nbad)
 
        if ((ds->ds_txstat.ts_status & ATH9K_TXERR_FILT) == 0 &&
            (bf->bf_flags & ATH9K_TXDESC_NOACK) == 0) {
-               if (bf_isdata(bf)) {
+               if (ieee80211_is_data(hdr->frame_control)) {
                        memcpy(&tx_info_priv->tx, &ds->ds_txstat,
                               sizeof(tx_info_priv->tx));
                        tx_info_priv->n_frames = bf->bf_nframes;
@@ -2031,9 +1851,25 @@ static void ath_tx_rc_status(struct ath_buf *bf, struct ath_desc *ds, int nbad)
        }
 }
 
+static void ath_wake_mac80211_queue(struct ath_softc *sc, struct ath_txq *txq)
+{
+       int qnum;
+
+       spin_lock_bh(&txq->axq_lock);
+       if (txq->stopped &&
+           sc->tx.txq[txq->axq_qnum].axq_depth <= (ATH_TXBUF - 20)) {
+               qnum = ath_get_mac80211_qnum(txq->axq_qnum, sc);
+               if (qnum != -1) {
+                       ieee80211_wake_queue(sc->hw, qnum);
+                       txq->stopped = 0;
+               }
+       }
+       spin_unlock_bh(&txq->axq_lock);
+}
+
 static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
 {
-       struct ath_hal *ah = sc->sc_ah;
+       struct ath_hw *ah = sc->sc_ah;
        struct ath_buf *bf, *lastbf, *bf_held = NULL;
        struct list_head bf_head;
        struct ath_desc *ds;
@@ -2066,16 +1902,23 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
                if (bf->bf_status & ATH_BUFSTATUS_STALE) {
                        bf_held = bf;
                        if (list_is_last(&bf_held->list, &txq->axq_q)) {
-                               /* FIXME:
+                               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_unlock_bh(&txq->axq_lock);
+                               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,
-                                       struct ath_buf, list);
+                                               struct ath_buf, list);
                        }
                }
 
@@ -2099,24 +1942,20 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
                 */
                lastbf->bf_status |= ATH_BUFSTATUS_STALE;
                INIT_LIST_HEAD(&bf_head);
-
                if (!list_is_singular(&lastbf->list))
                        list_cut_position(&bf_head,
                                &txq->axq_q, lastbf->list.prev);
 
                txq->axq_depth--;
-
                if (bf_isaggr(bf))
                        txq->axq_aggr_depth--;
 
                txok = (ds->ds_txstat.ts_status == 0);
-
                spin_unlock_bh(&txq->axq_lock);
 
                if (bf_held) {
-                       list_del(&bf_held->list);
                        spin_lock_bh(&sc->tx.txbuflock);
-                       list_add_tail(&bf_held->list, &sc->tx.txbuf);
+                       list_move_tail(&bf_held->list, &sc->tx.txbuf);
                        spin_unlock_bh(&sc->tx.txbuflock);
                }
 
@@ -2136,22 +1975,13 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
                ath_tx_rc_status(bf, ds, nbad);
 
                if (bf_isampdu(bf))
-                       ath_tx_complete_aggr_rifs(sc, txq, bf, &bf_head, txok);
+                       ath_tx_complete_aggr(sc, txq, bf, &bf_head, txok);
                else
                        ath_tx_complete_buf(sc, bf, &bf_head, txok, 0);
 
-               spin_lock_bh(&txq->axq_lock);
-               if (txq->stopped && ath_txq_depth(sc, txq->axq_qnum) <=
-                               (ATH_TXBUF - 20)) {
-                       int qnum;
-                       qnum = ath_get_mac80211_qnum(txq->axq_qnum, sc);
-                       if (qnum != -1) {
-                               ieee80211_wake_queue(sc->hw, qnum);
-                               txq->stopped = 0;
-                       }
-
-               }
+               ath_wake_mac80211_queue(sc, txq);
 
+               spin_lock_bh(&txq->axq_lock);
                if (sc->sc_flags & SC_OP_TXAGGR)
                        ath_txq_schedule(sc, txq);
                spin_unlock_bh(&txq->axq_lock);