session->ampdu_len);
 }
 
-int
-brcms_c_sendampdu(struct ampdu_info *ampdu, struct brcms_txq_info *qi,
-             struct sk_buff **pdu, int prec)
-{
-       struct brcms_c_info *wlc;
-       struct sk_buff *p;
-       struct brcms_ampdu_session session;
-       int err = 0;
-       u8 tid;
-
-       uint count, fifo, seg_cnt = 0;
-       struct scb *scb;
-       struct scb_ampdu *scb_ampdu;
-       struct scb_ampdu_tid_ini *ini;
-       struct ieee80211_tx_info *tx_info;
-       u16 qlen;
-       struct wiphy *wiphy;
-
-       wlc = ampdu->wlc;
-       wiphy = wlc->wiphy;
-       p = *pdu;
-
-       tid = (u8) (p->priority);
-
-       scb = &wlc->pri_scb;
-       scb_ampdu = &scb->scb_ampdu;
-       ini = &scb_ampdu->ini[tid];
-
-       /* Let pressure continue to build ... */
-       qlen = pktq_plen(&qi->q, prec);
-       if (ini->tx_in_transit > 0 &&
-           qlen < min(scb_ampdu->max_pdu, ini->ba_wsize))
-               /* Collect multiple MPDU's to be sent in the next AMPDU */
-               return -EBUSY;
-
-       /* at this point we intend to transmit an AMPDU */
-       brcms_c_ampdu_reset_session(&session, wlc);
-
-       while (p) {
-               struct ieee80211_tx_rate *txrate;
-
-               tx_info = IEEE80211_SKB_CB(p);
-               txrate = tx_info->status.rates;
-
-               if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) {
-                       err = brcms_c_prep_pdu(wlc, p, &fifo);
-               } else {
-                       wiphy_err(wiphy, "%s: AMPDU flag is off!\n", __func__);
-                       *pdu = NULL;
-                       err = 0;
-                       break;
-               }
-
-               if (err) {
-                       if (err == -EBUSY) {
-                               wiphy_err(wiphy, "wl%d: sendampdu: "
-                                         "prep_xdu retry\n", wlc->pub->unit);
-                               *pdu = p;
-                               break;
-                       }
-
-                       /* error in the packet; reject it */
-                       wiphy_err(wiphy, "wl%d: sendampdu: prep_xdu "
-                                 "rejected\n", wlc->pub->unit);
-                       *pdu = NULL;
-                       break;
-               }
-
-               err = brcms_c_ampdu_add_frame(&session, p);
-               if (err == -ENOSPC) {
-                       /*
-                        * No space for this packet in the AMPDU.
-                        * Requeue packet and proceed;
-                        */
-                       *pdu = p;
-                       break;
-               } else if (err) {
-                       /* Unexpected error; reject packet */
-                       wiphy_err(wiphy, "wl%d: sendampdu: add_frame rejected",
-                                 wlc->pub->unit);
-                       *pdu = NULL;
-                       break;
-               }
-
-               seg_cnt += 1;
-
-               /*
-                * check to see if the next pkt is
-                * a candidate for aggregation
-                */
-               p = pktq_ppeek(&qi->q, prec);
-               if (p) {
-                       tx_info = IEEE80211_SKB_CB(p);
-                       if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) {
-                               /*
-                                * check if there are enough
-                                * descriptors available
-                                */
-                               if (*wlc->core->txavail[fifo] <= seg_cnt + 1) {
-                                       wiphy_err(wiphy, "%s: No fifo space "
-                                                 "!!\n", __func__);
-                                       p = NULL;
-                                       continue;
-                               }
-                               /* next packet fit for aggregation so dequeue */
-                               p = brcmu_pktq_pdeq(&qi->q, prec);
-                       } else {
-                               p = NULL;
-                       }
-               }
-       }                       /* end while(p) */
-
-       count = skb_queue_len(&session.skb_list);
-       ini->tx_in_transit += count;
-
-       if (count) {
-               /* patch up first and last txh's */
-               brcms_c_ampdu_finalize(&session);
-
-               while ((p = skb_dequeue(&session.skb_list)) != NULL)
-                       brcms_c_txfifo(wlc, fifo, p,
-                                      skb_queue_empty(&session.skb_list));
-       }
-       /* endif (count) */
-       return err;
-}
-
 static void
 brcms_c_ampdu_rate_status(struct brcms_c_info *wlc,
                          struct ieee80211_tx_info *tx_info,
                /* either retransmit or send bar if ack not recd */
                if (!ack_recd) {
                        if (retry && (ini->txretry[index] < (int)retry_limit)) {
+                               int ret;
                                ini->txretry[index]++;
                                ini->tx_in_transit--;
-                               brcms_c_txq_enq(wlc, scb, p);
+                               ret = brcms_c_txfifo(wlc, queue, p);
+                               /*
+                                * We shouldn't be out of space in the DMA
+                                * ring here since we're reinserting a frame
+                                * that was just pulled out.
+                                */
+                               WARN_ONCE(ret, "queue %d out of txds\n", queue);
                        } else {
                                /* Retry timeout */
                                ini->tx_in_transit--;
 
                p = dma_getnexttxp(wlc->hw->di[queue], DMA_RANGE_TRANSMITTED);
        }
-       brcms_c_send_q(wlc);
 
        /* update rate state */
        antselid = brcms_c_antsel_antsel2id(wlc->asi, mimoantsel);
-
-       brcms_c_txfifo_complete(wlc, queue);
 }
 
 void
                        p = dma_getnexttxp(wlc->hw->di[queue],
                                           DMA_RANGE_TRANSMITTED);
                }
-               brcms_c_txfifo_complete(wlc, queue);
        }
 }
 
        }
 }
 
-/*
- * callback function that helps flushing ampdu packets from a priority queue
- */
-static bool cb_del_ampdu_pkt(struct sk_buff *mpdu, void *arg_a)
-{
-       struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(mpdu);
-       struct cb_del_ampdu_pars *ampdu_pars =
-                                (struct cb_del_ampdu_pars *)arg_a;
-       bool rc;
-
-       rc = tx_info->flags & IEEE80211_TX_CTL_AMPDU ? true : false;
-       rc = rc && (tx_info->rate_driver_data[0] == NULL || ampdu_pars->sta == NULL ||
-                   tx_info->rate_driver_data[0] == ampdu_pars->sta);
-       rc = rc && ((u8)(mpdu->priority) == ampdu_pars->tid);
-       return rc;
-}
-
 /*
  * callback function that helps invalidating ampdu packets in a DMA queue
  */
 void brcms_c_ampdu_flush(struct brcms_c_info *wlc,
                     struct ieee80211_sta *sta, u16 tid)
 {
-       struct brcms_txq_info *qi = wlc->pkt_queue;
-       struct pktq *pq = &qi->q;
-       int prec;
-       struct cb_del_ampdu_pars ampdu_pars;
-
-       ampdu_pars.sta = sta;
-       ampdu_pars.tid = tid;
-       for (prec = 0; prec < pq->num_prec; prec++)
-               brcmu_pktq_pflush(pq, prec, true, cb_del_ampdu_pkt,
-                           (void *)&du_pars);
        brcms_c_inval_dma_pkts(wlc->hw, sta, dma_cb_fn_ampdu);
 }
 
 #include <linux/slab.h>
 #include <linux/delay.h>
 #include <linux/pci.h>
+#include <net/cfg80211.h>
+#include <net/mac80211.h>
 
 #include <brcmu_utils.h>
 #include <aiutils.h>
 #include "types.h"
+#include "main.h"
 #include "dma.h"
 #include "soc.h"
+#include "scb.h"
+#include "ampdu.h"
 
 /*
  * dma register field offset calculation
        struct bcma_device *core;
        struct device *dmadev;
 
+       /* session information for AMPDU */
+       struct brcms_ampdu_session ampdu_session;
+
        bool dma64;     /* this dma engine is operating in 64-bit mode */
        bool addrext;   /* this dma engine supports DmaExtendedAddrChanges */
 
        return dma64_alloc(di, direction);
 }
 
-struct dma_pub *dma_attach(char *name, struct si_pub *sih,
-                          struct bcma_device *core,
+struct dma_pub *dma_attach(char *name, struct brcms_c_info *wlc,
                           uint txregbase, uint rxregbase, uint ntxd, uint nrxd,
                           uint rxbufsize, int rxextheadroom,
                           uint nrxpost, uint rxoffset, uint *msg_level)
 {
+       struct si_pub *sih = wlc->hw->sih;
+       struct bcma_device *core = wlc->hw->d11core;
        struct dma_info *di;
        u8 rev = core->id.rev;
        uint size;
                }
        }
 
+       /* Initialize AMPDU session */
+       brcms_c_ampdu_reset_session(&di->ampdu_session, wlc);
+
        DMA_TRACE("ddoffsetlow 0x%x ddoffsethigh 0x%x dataoffsetlow 0x%x dataoffsethigh 0x%x addrext %d\n",
                  di->ddoffsetlow, di->ddoffsethigh,
                  di->dataoffsetlow, di->dataoffsethigh,
                 D64_RS0_CD_MASK));
 }
 
+static bool dma64_txidle(struct dma_info *di)
+{
+       if (di->ntxd == 0)
+               return true;
+
+       return ((bcma_read32(di->core,
+                            DMA64TXREGOFFS(di, status0)) & D64_XS0_CD_MASK) ==
+               (bcma_read32(di->core, DMA64TXREGOFFS(di, ptr)) &
+                D64_XS0_CD_MASK));
+}
+
 /*
  * post receive buffers
  *  return false is refill failed completely and ring is empty this will stall
        return status == D64_RS0_RS_DISABLED;
 }
 
-/* Update count of available tx descriptors based on current DMA state */
-static void dma_update_txavail(struct dma_info *di)
+static void dma_txenq(struct dma_info *di, struct sk_buff *p)
 {
-       /*
-        * Available space is number of descriptors less the number of
-        * active descriptors and the number of queued AMPDU frames.
-        */
-       di->dma.txavail = di->ntxd - ntxdactive(di, di->txin, di->txout) - 1;
-}
-
-
-/*
- * !! tx entry routine
- * WARNING: call must check the return value for error.
- *   the error(toss frames) could be fatal and cause many subsequent hard
- *   to debug problems
- */
-int dma_txfast(struct dma_pub *pub, struct sk_buff *p, bool commit)
-{
-       struct dma_info *di = (struct dma_info *)pub;
        unsigned char *data;
        uint len;
        u16 txout;
        u32 flags = 0;
        dma_addr_t pa;
 
-       DMA_TRACE("%s:\n", di->name);
-
        txout = di->txout;
 
+       if (WARN_ON(nexttxd(di, txout) == di->txin))
+               return;
+
        /*
         * obtain and initialize transmit descriptor entry.
         */
        data = p->data;
        len = p->len;
 
-       /* no use to transmit a zero length packet */
-       if (len == 0)
-               return 0;
-
-       /* return nonzero if out of tx descriptors */
-       if (nexttxd(di, txout) == di->txin)
-               goto outoftxd;
-
        /* get physical address of buffer start */
        pa = dma_map_single(di->dmadev, data, len, DMA_TO_DEVICE);
 
 
        /* bump the tx descriptor index */
        di->txout = txout;
+}
 
-       /* kick the chip */
-       if (commit)
-               bcma_write32(di->core, DMA64TXREGOFFS(di, ptr),
-                     di->xmtptrbase + I2B(txout, struct dma64desc));
+static void ampdu_finalize(struct dma_info *di)
+{
+       struct brcms_ampdu_session *session = &di->ampdu_session;
+       struct sk_buff *p;
+
+       if (WARN_ON(skb_queue_empty(&session->skb_list)))
+               return;
+
+       brcms_c_ampdu_finalize(session);
+
+       while (!skb_queue_empty(&session->skb_list)) {
+               p = skb_dequeue(&session->skb_list);
+               dma_txenq(di, p);
+       }
+
+       bcma_write32(di->core, DMA64TXREGOFFS(di, ptr),
+                    di->xmtptrbase + I2B(di->txout, struct dma64desc));
+       brcms_c_ampdu_reset_session(session, session->wlc);
+}
+
+static void prep_ampdu_frame(struct dma_info *di, struct sk_buff *p)
+{
+       struct brcms_ampdu_session *session = &di->ampdu_session;
+       int ret;
+
+       ret = brcms_c_ampdu_add_frame(session, p);
+       if (ret == -ENOSPC) {
+               /*
+                * AMPDU cannot accomodate this frame. Close out the in-
+                * progress AMPDU session and start a new one.
+                */
+               ampdu_finalize(di);
+               ret = brcms_c_ampdu_add_frame(session, p);
+       }
+
+       WARN_ON(ret);
+}
+
+/* Update count of available tx descriptors based on current DMA state */
+static void dma_update_txavail(struct dma_info *di)
+{
+       /*
+        * Available space is number of descriptors less the number of
+        * active descriptors and the number of queued AMPDU frames.
+        */
+       di->dma.txavail = di->ntxd - ntxdactive(di, di->txin, di->txout) -
+                         skb_queue_len(&di->ampdu_session.skb_list) - 1;
+}
+
+/*
+ * !! tx entry routine
+ * WARNING: call must check the return value for error.
+ *   the error(toss frames) could be fatal and cause many subsequent hard
+ *   to debug problems
+ */
+int dma_txfast(struct brcms_c_info *wlc, struct dma_pub *pub,
+              struct sk_buff *p)
+{
+       struct dma_info *di = (struct dma_info *)pub;
+       struct brcms_ampdu_session *session = &di->ampdu_session;
+       struct ieee80211_tx_info *tx_info;
+       bool is_ampdu;
+
+       DMA_TRACE("%s:\n", di->name);
+
+       /* no use to transmit a zero length packet */
+       if (p->len == 0)
+               return 0;
+
+       /* return nonzero if out of tx descriptors */
+       if (di->dma.txavail == 0 || nexttxd(di, di->txout) == di->txin)
+               goto outoftxd;
+
+       tx_info = IEEE80211_SKB_CB(p);
+       is_ampdu = tx_info->flags & IEEE80211_TX_CTL_AMPDU;
+       if (is_ampdu)
+               prep_ampdu_frame(di, p);
+       else
+               dma_txenq(di, p);
 
        /* tx flow control */
        dma_update_txavail(di);
 
+       /* kick the chip */
+       if (is_ampdu) {
+               /*
+                * Start sending data if we've got a full AMPDU, there's
+                * no more space in the DMA ring, or the ring isn't
+                * currently transmitting.
+                */
+               if (skb_queue_len(&session->skb_list) == session->max_ampdu_frames ||
+                   di->dma.txavail == 0 || dma64_txidle(di))
+                       ampdu_finalize(di);
+       } else {
+               bcma_write32(di->core, DMA64TXREGOFFS(di, ptr),
+                            di->xmtptrbase + I2B(di->txout, struct dma64desc));
+       }
+
        return 0;
 
  outoftxd:
        brcmu_pkt_buf_free_skb(p);
        di->dma.txavail = 0;
        di->dma.txnobuf++;
-       return -1;
+       return -ENOSPC;
+}
+
+void dma_txflush(struct dma_pub *pub)
+{
+       struct dma_info *di = (struct dma_info *)pub;
+       struct brcms_ampdu_session *session = &di->ampdu_session;
+
+       if (!skb_queue_empty(&session->skb_list))
+               ampdu_finalize(di);
+}
+
+int dma_txpending(struct dma_pub *pub)
+{
+       struct dma_info *di = (struct dma_info *)pub;
+       return ntxdactive(di, di->txin, di->txout);
+}
+
+/*
+ * If we have an active AMPDU session and are not transmitting,
+ * this function will force tx to start.
+ */
+void dma_kick_tx(struct dma_pub *pub)
+{
+       struct dma_info *di = (struct dma_info *)pub;
+       struct brcms_ampdu_session *session = &di->ampdu_session;
+
+       if (!skb_queue_empty(&session->skb_list) && dma64_txidle(di))
+               ampdu_finalize(di);
 }
 
 /*
 
 #include "ucode_loader.h"
 #include "main.h"
 #include "soc.h"
+#include "dma.h"
 
 /* watchdog timer, in unit of ms */
 #define TIMER_INTERVAL_WATCHDOG                1000
 /* Max # of entries in Rx FIFO based on 4kb page size */
 #define NRXD                           256
 
+/* Amount of headroom to leave in Tx FIFO */
+#define TX_HEADROOM                    4
+
 /* try to keep this # rbufs posted to the chip */
 #define NRXBUFPOST                     32
 
-/* data msg txq hiwat mark */
-#define BRCMS_DATAHIWAT                        50
-
 /* max # frames to process in brcms_c_recv() */
 #define RXBND                          8
 /* max # tx status to process in wlc_txstatus() */
        TX_AC_BK_FIFO
 };
 
+/* 802.1D Priority to precedence queue mapping */
+const u8 wlc_prio2prec_map[] = {
+       _BRCMS_PREC_BE,         /* 0 BE - Best-effort */
+       _BRCMS_PREC_BK,         /* 1 BK - Background */
+       _BRCMS_PREC_NONE,               /* 2 None = - */
+       _BRCMS_PREC_EE,         /* 3 EE - Excellent-effort */
+       _BRCMS_PREC_CL,         /* 4 CL - Controlled Load */
+       _BRCMS_PREC_VI,         /* 5 Vi - Video */
+       _BRCMS_PREC_VO,         /* 6 Vo - Voice */
+       _BRCMS_PREC_NC,         /* 7 NC - Network Control */
+};
+
 static const u16 xmtfifo_sz[][NFIFO] = {
        /* corerev 17: 5120, 49152, 49152, 5376, 4352, 1280 */
        {20, 192, 192, 21, 17, 5},
        [IEEE80211_AC_BK]       = TX_AC_BK_FIFO,
 };
 
+/* Mapping of tx fifos to ieee80211 AC numbers */
+static const u8 fifo_to_ac_mapping[IEEE80211_NUM_ACS] = {
+       [TX_AC_BK_FIFO] = IEEE80211_AC_BK,
+       [TX_AC_BE_FIFO] = IEEE80211_AC_BE,
+       [TX_AC_VI_FIFO] = IEEE80211_AC_VI,
+       [TX_AC_VO_FIFO] = IEEE80211_AC_VO,
+};
+
 static u8 brcms_ac_to_fifo(u8 ac)
 {
        if (ac >= ARRAY_SIZE(ac_to_fifo_mapping))
        return ac_to_fifo_mapping[ac];
 }
 
+static u8 brcms_fifo_to_ac(u8 fifo)
+{
+       if (fifo >= ARRAY_SIZE(fifo_to_ac_mapping))
+               return IEEE80211_AC_BE;
+       return fifo_to_ac_mapping[fifo];
+}
+
 /* Find basic rate for a given rate */
 static u8 brcms_basic_rate(struct brcms_c_info *wlc, u32 rspec)
 {
 }
 
 /* sum the individual fifo tx pending packet counts */
-static s16 brcms_txpktpendtot(struct brcms_c_info *wlc)
+static int brcms_txpktpendtot(struct brcms_c_info *wlc)
 {
-       return wlc->core->txpktpend[0] + wlc->core->txpktpend[1] +
-              wlc->core->txpktpend[2] + wlc->core->txpktpend[3];
+       int i;
+       int pending = 0;
+
+       for (i = 0; i < ARRAY_SIZE(wlc->hw->di); i++)
+               if (wlc->hw->di[i])
+                       pending += dma_txpending(wlc->hw->di[i]);
+       return pending;
 }
 
 static bool brcms_is_mband_unlocked(struct brcms_c_info *wlc)
 static bool
 brcms_c_dotxstatus(struct brcms_c_info *wlc, struct tx_status *txs)
 {
-       struct sk_buff *p;
-       uint queue;
+       struct sk_buff *p = NULL;
+       uint queue = NFIFO;
+       struct dma_pub *dma = NULL;
        struct d11txh *txh;
        struct scb *scb = NULL;
        bool free_pdu;
        struct ieee80211_tx_info *tx_info;
        struct ieee80211_tx_rate *txrate;
        int i;
+       bool fatal = true;
 
        /* discard intermediate indications for ucode with one legitimate case:
         *   e.g. if "useRTS" is set. ucode did a successful rts/cts exchange,
        if (!(txs->status & TX_STATUS_AMPDU)
            && (txs->status & TX_STATUS_INTERMEDIATE)) {
                BCMMSG(wlc->wiphy, "INTERMEDIATE but not AMPDU\n");
-               return false;
+               fatal = false;
+               goto out;
        }
 
        queue = txs->frameid & TXFID_QUEUE_MASK;
-       if (queue >= NFIFO) {
-               p = NULL;
-               goto fatal;
-       }
+       if (queue >= NFIFO)
+               goto out;
+
+       dma = wlc->hw->di[queue];
 
        p = dma_getnexttxp(wlc->hw->di[queue], DMA_RANGE_TRANSMITTED);
        if (p == NULL)
-               goto fatal;
+               goto out;
 
        txh = (struct d11txh *) (p->data);
        mcl = le16_to_cpu(txh->MacTxControlLow);
        }
 
        if (txs->frameid != le16_to_cpu(txh->TxFrameID))
-               goto fatal;
+               goto out;
        tx_info = IEEE80211_SKB_CB(p);
        h = (struct ieee80211_hdr *)((u8 *) (txh + 1) + D11_PHY_HDR_LEN);
 
 
        if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) {
                brcms_c_ampdu_dotxstatus(wlc->ampdu, scb, p, txs);
-               return false;
+               fatal = false;
+               goto out;
        }
 
        supr_status = txs->status & TX_STATUS_SUPR_MASK;
        totlen = p->len;
        free_pdu = true;
 
-       brcms_c_txfifo_complete(wlc, queue);
-
        if (lastframe) {
                /* remove PLCP & Broadcom tx descriptor header */
                skb_pull(p, D11_PHY_HDR_LEN);
                          "tx_status\n", __func__);
        }
 
-       return false;
+       fatal = false;
 
- fatal:
-       if (p)
+ out:
+       if (fatal && p)
                brcmu_pkt_buf_free_skb(p);
 
-       return true;
+       if (dma && queue < NFIFO) {
+               u16 ac_queue = brcms_fifo_to_ac(queue);
+               if (dma->txavail > TX_HEADROOM && queue < TX_BCMC_FIFO &&
+                   ieee80211_queue_stopped(wlc->pub->ieee_hw, ac_queue))
+                       ieee80211_wake_queue(wlc->pub->ieee_hw, ac_queue);
+               dma_kick_tx(dma);
+       }
 
+       return fatal;
 }
 
 /* process tx completion events in BMAC
        if (n >= max_tx_num)
                morepending = true;
 
-       if (!pktq_empty(&wlc->pkt_queue->q))
-               brcms_c_send_q(wlc);
-
        return morepending;
 }
 
                 * TX: TX_AC_BK_FIFO (TX AC Background data packets)
                 * RX: RX_FIFO (RX data packets)
                 */
-               wlc_hw->di[0] = dma_attach(name, wlc_hw->sih, wlc_hw->d11core,
+               wlc_hw->di[0] = dma_attach(name, wlc,
                                           (wme ? dmareg(DMA_TX, 0) : 0),
                                           dmareg(DMA_RX, 0),
                                           (wme ? NTXD : 0), NRXD,
                 *   (legacy) TX_DATA_FIFO (TX data packets)
                 * RX: UNUSED
                 */
-               wlc_hw->di[1] = dma_attach(name, wlc_hw->sih, wlc_hw->d11core,
+               wlc_hw->di[1] = dma_attach(name, wlc,
                                           dmareg(DMA_TX, 1), 0,
                                           NTXD, 0, 0, -1, 0, 0,
                                           &brcm_msg_level);
                 * TX: TX_AC_VI_FIFO (TX AC Video data packets)
                 * RX: UNUSED
                 */
-               wlc_hw->di[2] = dma_attach(name, wlc_hw->sih, wlc_hw->d11core,
+               wlc_hw->di[2] = dma_attach(name, wlc,
                                           dmareg(DMA_TX, 2), 0,
                                           NTXD, 0, 0, -1, 0, 0,
                                           &brcm_msg_level);
                 * TX: TX_AC_VO_FIFO (TX AC Voice data packets)
                 *   (legacy) TX_CTL_FIFO (TX control & mgmt packets)
                 */
-               wlc_hw->di[3] = dma_attach(name, wlc_hw->sih, wlc_hw->d11core,
+               wlc_hw->di[3] = dma_attach(name, wlc,
                                           dmareg(DMA_TX, 3),
                                           0, NTXD, 0, 0, -1,
                                           0, 0, &brcm_msg_level);
        uint i;
 
        /* free any posted tx packets */
-       for (i = 0; i < NFIFO; i++)
+       for (i = 0; i < NFIFO; i++) {
                if (wlc_hw->di[i]) {
                        dma_txreclaim(wlc_hw->di[i], DMA_RANGE_ALL);
-                       wlc->core->txpktpend[i] = 0;
-                       BCMMSG(wlc->wiphy, "pktpend fifo %d clrd\n", i);
+                       if (i < TX_BCMC_FIFO)
+                               ieee80211_wake_queue(wlc->pub->ieee_hw,
+                                                    brcms_fifo_to_ac(i));
                }
+       }
 
        /* free any posted rx packets */
        dma_rxreclaim(wlc_hw->di[RX_FIFO]);
        return 0;
 }
 
-/*
- * Initialize the base precedence map for dequeueing
- * from txq based on WME settings
- */
-static void brcms_c_tx_prec_map_init(struct brcms_c_info *wlc)
-{
-       wlc->tx_prec_map = BRCMS_PREC_BMP_ALL;
-}
-
 /* push sw hps and wake state through hardware */
 static void brcms_c_set_ps_ctrl(struct brcms_c_info *wlc)
 {
                bi->flags |= BRCMS_BSS_HT;
 }
 
-static struct brcms_txq_info *brcms_c_txq_alloc(struct brcms_c_info *wlc)
-{
-       struct brcms_txq_info *qi, *p;
-
-       qi = kzalloc(sizeof(struct brcms_txq_info), GFP_ATOMIC);
-       if (qi != NULL) {
-               /*
-                * Have enough room for control packets along with HI watermark
-                * Also, add room to txq for total psq packets if all the SCBs
-                * leave PS mode. The watermark for flowcontrol to OS packets
-                * will remain the same
-                */
-               brcmu_pktq_init(&qi->q, BRCMS_PREC_COUNT,
-                         2 * BRCMS_DATAHIWAT + PKTQ_LEN_DEFAULT);
-
-               /* add this queue to the the global list */
-               p = wlc->tx_queues;
-               if (p == NULL) {
-                       wlc->tx_queues = qi;
-               } else {
-                       while (p->next != NULL)
-                               p = p->next;
-                       p->next = qi;
-               }
-       }
-       return qi;
-}
-
-static void brcms_c_txq_free(struct brcms_c_info *wlc,
-                            struct brcms_txq_info *qi)
-{
-       struct brcms_txq_info *p;
-
-       if (qi == NULL)
-               return;
-
-       /* remove the queue from the linked list */
-       p = wlc->tx_queues;
-       if (p == qi)
-               wlc->tx_queues = p->next;
-       else {
-               while (p != NULL && p->next != qi)
-                       p = p->next;
-               if (p != NULL)
-                       p->next = p->next->next;
-       }
-
-       kfree(qi);
-}
-
 static void brcms_c_update_mimo_band_bwcap(struct brcms_c_info *wlc, u8 bwcap)
 {
        uint i;
 
        brcms_c_detach_module(wlc);
 
-
-       while (wlc->tx_queues != NULL)
-               brcms_c_txq_free(wlc, wlc->tx_queues);
-
        brcms_c_detach_mfree(wlc);
        return callbacks;
 }
        uint callbacks = 0;
        int i;
        bool dev_gone = false;
-       struct brcms_txq_info *qi;
 
        BCMMSG(wlc->wiphy, "wl%d\n", wlc->pub->unit);
 
 
        wlc_phy_mute_upd(wlc->band->pi, false, PHY_MUTE_ALL);
 
-       /* flush tx queues */
-       for (qi = wlc->tx_queues; qi != NULL; qi = qi->next)
-               brcmu_pktq_flush(&qi->q, true, NULL, NULL);
-
        callbacks += brcms_b_down_finish(wlc->hw);
 
        /* brcms_b_down_finish has done brcms_c_coredisable(). so clk is off */
        return 2 * brcms_b_read_shm(wlc_hw, table_ptr + (index * 2));
 }
 
-static bool
-brcms_c_prec_enq_head(struct brcms_c_info *wlc, struct pktq *q,
-                     struct sk_buff *pkt, int prec, bool head)
-{
-       struct sk_buff *p;
-       int eprec = -1;         /* precedence to evict from */
-
-       /* Determine precedence from which to evict packet, if any */
-       if (pktq_pfull(q, prec))
-               eprec = prec;
-       else if (pktq_full(q)) {
-               p = brcmu_pktq_peek_tail(q, &eprec);
-               if (eprec > prec) {
-                       wiphy_err(wlc->wiphy, "%s: Failing: eprec %d > prec %d"
-                                 "\n", __func__, eprec, prec);
-                       return false;
-               }
-       }
-
-       /* Evict if needed */
-       if (eprec >= 0) {
-               bool discard_oldest;
-
-               discard_oldest = ac_bitmap_tst(0, eprec);
-
-               /* Refuse newer packet unless configured to discard oldest */
-               if (eprec == prec && !discard_oldest) {
-                       wiphy_err(wlc->wiphy, "%s: No where to go, prec == %d"
-                                 "\n", __func__, prec);
-                       return false;
-               }
-
-               /* Evict packet according to discard policy */
-               p = discard_oldest ? brcmu_pktq_pdeq(q, eprec) :
-                       brcmu_pktq_pdeq_tail(q, eprec);
-               brcmu_pkt_buf_free_skb(p);
-       }
-
-       /* Enqueue */
-       if (head)
-               p = brcmu_pktq_penq_head(q, prec, pkt);
-       else
-               p = brcmu_pktq_penq(q, prec, pkt);
-
-       return true;
-}
-
-/*
- * Attempts to queue a packet onto a multiple-precedence queue,
- * if necessary evicting a lower precedence packet from the queue.
- *
- * 'prec' is the precedence number that has already been mapped
- * from the packet priority.
- *
- * Returns true if packet consumed (queued), false if not.
- */
-static bool brcms_c_prec_enq(struct brcms_c_info *wlc, struct pktq *q,
-                     struct sk_buff *pkt, int prec)
-{
-       return brcms_c_prec_enq_head(wlc, q, pkt, prec, false);
-}
-
-void brcms_c_txq_enq(struct brcms_c_info *wlc, struct scb *scb,
-                    struct sk_buff *sdu)
-{
-       struct brcms_txq_info *qi = wlc->pkt_queue;     /* Check me */
-       struct pktq *q = &qi->q;
-       uint prec;
-
-       prec = brcms_ac_to_fifo(skb_get_queue_mapping(sdu));
-       if (!brcms_c_prec_enq(wlc, q, sdu, prec)) {
-               /*
-                * we might hit this condtion in case
-                * packet flooding from mac80211 stack
-                */
-               brcmu_pkt_buf_free_skb(sdu);
-       }
-}
-
 /*
  * bcmc_fid_generate:
  * Generate frame ID for a BCMC packet.  The frag field is not used
        return 0;
 }
 
-void brcms_c_sendpkt_mac80211(struct brcms_c_info *wlc, struct sk_buff *sdu,
-                             struct ieee80211_hw *hw)
+static int brcms_c_tx(struct brcms_c_info *wlc, struct sk_buff *skb)
 {
-       uint fifo;
-       struct scb *scb = &wlc->pri_scb;
-
-       fifo = brcms_ac_to_fifo(skb_get_queue_mapping(sdu));
-       if (brcms_c_d11hdrs_mac80211(wlc, hw, sdu, scb, 0, 1, fifo, 0))
-               return;
-       brcms_c_txq_enq(wlc, scb, sdu);
-       brcms_c_send_q(wlc);
-}
-
-void brcms_c_send_q(struct brcms_c_info *wlc)
-{
-       struct sk_buff *pkt[DOT11_MAXNUMFRAGS];
-       int prec;
-       u16 prec_map;
-       int err = 0, i, count;
-       uint fifo;
-       struct brcms_txq_info *qi = wlc->pkt_queue;
-       struct pktq *q = &qi->q;
-       struct ieee80211_tx_info *tx_info;
+       struct dma_pub *dma;
+       int fifo, ret = -ENOSPC;
+       struct d11txh *txh;
+       u16 frameid = INVALIDFID;
 
-       prec_map = wlc->tx_prec_map;
+       fifo = brcms_ac_to_fifo(skb_get_queue_mapping(skb));
+       dma = wlc->hw->di[fifo];
+       txh = (struct d11txh *)(skb->data);
 
-       /* Send all the enq'd pkts that we can.
-        * Dequeue packets with precedence with empty HW fifo only
-        */
-       while (prec_map && (pkt[0] = brcmu_pktq_mdeq(q, prec_map, &prec))) {
-               tx_info = IEEE80211_SKB_CB(pkt[0]);
-               if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) {
-                       err = brcms_c_sendampdu(wlc->ampdu, qi, pkt, prec);
-               } else {
-                       count = 1;
-                       err = brcms_c_prep_pdu(wlc, pkt[0], &fifo);
-                       if (!err) {
-                               for (i = 0; i < count; i++)
-                                       brcms_c_txfifo(wlc, fifo, pkt[i], true);
-                       }
-               }
-
-               if (err == -EBUSY) {
-                       brcmu_pktq_penq_head(q, prec, pkt[0]);
-                       /*
-                        * If send failed due to any other reason than a
-                        * change in HW FIFO condition, quit. Otherwise,
-                        * read the new prec_map!
-                        */
-                       if (prec_map == wlc->tx_prec_map)
-                               break;
-                       prec_map = wlc->tx_prec_map;
-               }
+       if (dma->txavail == 0) {
+               /*
+                * We sometimes get a frame from mac80211 after stopping
+                * the queues. This only ever seems to be a single frame
+                * and is seems likely to be a race. TX_HEADROOM should
+                * ensure that we have enough space to handle these stray
+                * packets, so warn if there isn't. If we're out of space
+                * in the tx ring and the tx queue isn't stopped then
+                * we've really got a bug; warn loudly if that happens.
+                */
+               wiphy_warn(wlc->wiphy,
+                          "Received frame for tx with no space in DMA ring\n");
+               WARN_ON(!ieee80211_queue_stopped(wlc->pub->ieee_hw,
+                                                skb_get_queue_mapping(skb)));
+               return -ENOSPC;
        }
-}
-
-void
-brcms_c_txfifo(struct brcms_c_info *wlc, uint fifo, struct sk_buff *p,
-              bool commit)
-{
-       u16 frameid = INVALIDFID;
-       struct d11txh *txh;
-
-       txh = (struct d11txh *) (p->data);
 
        /* When a BC/MC frame is being committed to the BCMC fifo
         * via DMA (NOT PIO), update ucode or BSS info as appropriate.
        if (fifo == TX_BCMC_FIFO)
                frameid = le16_to_cpu(txh->TxFrameID);
 
-       /*
-        * Bump up pending count for if not using rpc. If rpc is
-        * used, this will be handled in brcms_b_txfifo()
-        */
-       if (commit) {
-               wlc->core->txpktpend[fifo] += 1;
-               BCMMSG(wlc->wiphy, "pktpend inc 1 to %d\n",
-                        wlc->core->txpktpend[fifo]);
-       }
-
        /* Commit BCMC sequence number in the SHM frame ID location */
        if (frameid != INVALIDFID) {
                /*
                brcms_b_write_shm(wlc->hw, M_BCMC_FID, frameid);
        }
 
-       if (dma_txfast(wlc->hw->di[fifo], p, commit) < 0)
+       ret = brcms_c_txfifo(wlc, fifo, skb);
+       /*
+        * The only reason for brcms_c_txfifo to fail is because
+        * there weren't any DMA descriptors, but we've already
+        * checked for that. So if it does fail yell loudly.
+        */
+       WARN_ON_ONCE(ret);
+
+       return ret;
+}
+
+void brcms_c_sendpkt_mac80211(struct brcms_c_info *wlc, struct sk_buff *sdu,
+                             struct ieee80211_hw *hw)
+{
+       uint fifo;
+       struct scb *scb = &wlc->pri_scb;
+
+       fifo = brcms_ac_to_fifo(skb_get_queue_mapping(sdu));
+       if (brcms_c_d11hdrs_mac80211(wlc, hw, sdu, scb, 0, 1, fifo, 0))
+               return;
+       if (brcms_c_tx(wlc, sdu))
+               dev_kfree_skb_any(sdu);
+}
+
+int
+brcms_c_txfifo(struct brcms_c_info *wlc, uint fifo, struct sk_buff *p)
+{
+       struct dma_pub *dma = wlc->hw->di[fifo];
+       int ret;
+       u16 queue;
+
+       ret = dma_txfast(wlc, dma, p);
+       if (ret < 0)
                wiphy_err(wlc->wiphy, "txfifo: fatal, toss frames !!!\n");
+
+       /*
+        * Stop queue if DMA ring is full. Reserve some free descriptors,
+        * as we sometimes receive a frame from mac80211 after the queues
+        * are stopped.
+        */
+       queue = skb_get_queue_mapping(p);
+       if (dma->txavail <= TX_HEADROOM && fifo < TX_BCMC_FIFO &&
+           !ieee80211_queue_stopped(wlc->pub->ieee_hw, queue))
+               ieee80211_stop_queue(wlc->pub->ieee_hw, queue);
+
+       return ret;
 }
 
 u32
        return rts_rspec;
 }
 
-void
-brcms_c_txfifo_complete(struct brcms_c_info *wlc, uint fifo)
-{
-       wlc->core->txpktpend[fifo] -= 1;
-       BCMMSG(wlc->wiphy, "pktpend dec 1 to %d\n",
-              wlc->core->txpktpend[fifo]);
-
-       /* There is more room; mark precedences related to this FIFO sendable */
-       wlc->tx_prec_map |= 1 << fifo;
-
-       /* figure out which bsscfg is being worked on... */
-}
-
 /* Update beacon listen interval in shared memory */
 static void brcms_c_bcn_li_upd(struct brcms_c_info *wlc)
 {
                brcms_c_bss_update_probe_resp(wlc, bsscfg, suspend);
 }
 
-/* prepares pdu for transmission. returns BCM error codes */
-int brcms_c_prep_pdu(struct brcms_c_info *wlc, struct sk_buff *pdu, uint *fifop)
-{
-       uint fifo;
-       struct d11txh *txh;
-       struct ieee80211_hdr *h;
-       struct scb *scb;
-
-       txh = (struct d11txh *) (pdu->data);
-       h = (struct ieee80211_hdr *)((u8 *) (txh + 1) + D11_PHY_HDR_LEN);
-
-       /* get the pkt queue info. This was put at brcms_c_sendctl or
-        * brcms_c_send for PDU */
-       fifo = le16_to_cpu(txh->TxFrameID) & TXFID_QUEUE_MASK;
-
-       scb = NULL;
-
-       *fifop = fifo;
-
-       /* return if insufficient dma resources */
-       if (*wlc->core->txavail[fifo] < MAX_DMA_SEGS) {
-               /* Mark precedences related to this FIFO, unsendable */
-               /* A fifo is full. Clear precedences related to that FIFO */
-               wlc->tx_prec_map &= ~(1 << fifo);
-               return -EBUSY;
-       }
-       return 0;
-}
-
 int brcms_b_xmtfifo_sz_get(struct brcms_hardware *wlc_hw, uint fifo,
                           uint *blocks)
 {
 void brcms_c_wait_for_tx_completion(struct brcms_c_info *wlc, bool drop)
 {
        int timeout = 20;
+       int i;
 
-       /* flush packet queue when requested */
-       if (drop)
-               brcmu_pktq_flush(&wlc->pkt_queue->q, false, NULL, NULL);
+       /* Kick DMA to send any pending AMPDU */
+       for (i = 0; i < ARRAY_SIZE(wlc->hw->di); i++)
+               if (wlc->hw->di[i])
+                       dma_txflush(wlc->hw->di[i]);
 
        /* wait for queue and DMA fifos to run dry */
-       while (!pktq_empty(&wlc->pkt_queue->q) || brcms_txpktpendtot(wlc) > 0) {
+       while (brcms_txpktpendtot(wlc) > 0) {
                brcms_msleep(wlc->wl, 1);
 
                if (--timeout == 0)
                brcms_rfkill_set_hw_state(wlc->wl);
        }
 
-       /* send any enq'd tx packets. Just makes sure to jump start tx */
-       if (!pktq_empty(&wlc->pkt_queue->q))
-               brcms_c_send_q(wlc);
-
        /* it isn't done and needs to be resched if macintstatus is non-zero */
        return wlc->macintstatus != 0;
 
        bcma_set16(core, D11REGOFFS(ifs_ctl), IFS_USEEDCF);
        brcms_c_edcf_setparams(wlc, false);
 
-       /* Init precedence maps for empty FIFOs */
-       brcms_c_tx_prec_map_init(wlc);
-
        /* read the ucode version if we have not yet done so */
        if (wlc->ucode_rev == 0) {
                wlc->ucode_rev =
         * Complete the wlc default state initializations..
         */
 
-       /* allocate our initial queue */
-       wlc->pkt_queue = brcms_c_txq_alloc(wlc);
-       if (wlc->pkt_queue == NULL) {
-               wiphy_err(wl->wiphy, "wl%d: %s: failed to malloc tx queue\n",
-                         unit, __func__);
-               err = 100;
-               goto fail;
-       }
-
        wlc->bsscfg->wlc = wlc;
 
        wlc->mimoft = FT_HT;