#include <sys/cdefs.h>
#include "opt_inet.h"
#include "opt_ath.h"
#include "opt_wlan.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/sysctl.h>
#include <sys/mbuf.h>
#include <sys/malloc.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/kernel.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/errno.h>
#include <sys/callout.h>
#include <sys/bus.h>
#include <sys/endian.h>
#include <sys/kthread.h>
#include <sys/taskqueue.h>
#include <sys/priv.h>
#include <sys/ktr.h>
#include <machine/bus.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/if_dl.h>
#include <net/if_media.h>
#include <net/if_types.h>
#include <net/if_arp.h>
#include <net/ethernet.h>
#include <net/if_llc.h>
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_regdomain.h>
#ifdef IEEE80211_SUPPORT_SUPERG
#include <net80211/ieee80211_superg.h>
#endif
#ifdef IEEE80211_SUPPORT_TDMA
#include <net80211/ieee80211_tdma.h>
#endif
#include <net80211/ieee80211_ht.h>
#include <net/bpf.h>
#ifdef INET
#include <netinet/in.h>
#include <netinet/if_ether.h>
#endif
#include <dev/ath/if_athvar.h>
#include <dev/ath/ath_hal/ah_devid.h>
#include <dev/ath/ath_hal/ah_diagcodes.h>
#include <dev/ath/if_ath_debug.h>
#ifdef ATH_TX99_DIAG
#include <dev/ath/ath_tx99/ath_tx99.h>
#endif
#include <dev/ath/if_ath_misc.h>
#include <dev/ath/if_ath_tx.h>
#include <dev/ath/if_ath_tx_ht.h>
#ifdef ATH_DEBUG_ALQ
#include <dev/ath/if_ath_alq.h>
#endif
#define SWMAX_RETRIES 10
#define ATH_NONQOS_TID_AC WME_AC_VO
#if 0
static int ath_tx_node_is_asleep(struct ath_softc *sc, struct ath_node *an);
#endif
static int ath_tx_ampdu_pending(struct ath_softc *sc, struct ath_node *an,
int tid);
static int ath_tx_ampdu_running(struct ath_softc *sc, struct ath_node *an,
int tid);
static ieee80211_seq ath_tx_tid_seqno_assign(struct ath_softc *sc,
struct ieee80211_node *ni, struct ath_buf *bf, struct mbuf *m0);
static int ath_tx_action_frame_override_queue(struct ath_softc *sc,
struct ieee80211_node *ni, struct mbuf *m0, int *tid);
static struct ath_buf *
ath_tx_retry_clone(struct ath_softc *sc, struct ath_node *an,
struct ath_tid *tid, struct ath_buf *bf);
#ifdef ATH_DEBUG_ALQ
void
ath_tx_alq_post(struct ath_softc *sc, struct ath_buf *bf_first)
{
struct ath_buf *bf;
int i, n;
const char *ds;
bf = bf_first;
while (bf != NULL) {
if (bf->bf_nseg == 0)
break;
n = ((bf->bf_nseg - 1) / sc->sc_tx_nmaps) + 1;
for (i = 0, ds = (const char *) bf->bf_desc;
i < n;
i++, ds += sc->sc_tx_desclen) {
if_ath_alq_post(&sc->sc_alq,
ATH_ALQ_EDMA_TXDESC,
sc->sc_tx_desclen,
ds);
}
bf = bf->bf_next;
}
}
#endif
static inline int
ath_tx_is_11n(struct ath_softc *sc)
{
return ((sc->sc_ah->ah_magic == 0x20065416) ||
(sc->sc_ah->ah_magic == 0x19741014));
}
static int
ath_tx_gettid(struct ath_softc *sc, const struct mbuf *m0)
{
const struct ieee80211_frame *wh;
wh = mtod(m0, const struct ieee80211_frame *);
if (! IEEE80211_QOS_HAS_SEQ(wh))
return (WME_AC_TO_TID(M_WME_GETAC(m0)));
return (ieee80211_gettid(wh));
}
static void
ath_tx_set_retry(struct ath_softc *sc, struct ath_buf *bf)
{
struct ieee80211_frame *wh;
wh = mtod(bf->bf_m, struct ieee80211_frame *);
if (bf->bf_state.bfs_isretried == 0) {
wh->i_fc[1] |= IEEE80211_FC1_RETRY;
bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap,
BUS_DMASYNC_PREWRITE);
}
bf->bf_state.bfs_isretried = 1;
bf->bf_state.bfs_retries ++;
}
static int
ath_tx_getac(struct ath_softc *sc, const struct mbuf *m0)
{
const struct ieee80211_frame *wh;
wh = mtod(m0, const struct ieee80211_frame *);
if (IEEE80211_QOS_HAS_SEQ(wh))
return TID_TO_WME_AC(ieee80211_gettid(wh));
return (M_WME_GETAC(m0));
}
void
ath_txfrag_cleanup(struct ath_softc *sc,
ath_bufhead *frags, struct ieee80211_node *ni)
{
struct ath_buf *bf, *next;
ATH_TXBUF_LOCK_ASSERT(sc);
TAILQ_FOREACH_SAFE(bf, frags, bf_list, next) {
TAILQ_REMOVE(frags, bf, bf_list);
ath_returnbuf_head(sc, bf);
ieee80211_node_decref(ni);
}
}
int
ath_txfrag_setup(struct ath_softc *sc, ath_bufhead *frags,
struct mbuf *m0, struct ieee80211_node *ni)
{
struct mbuf *m;
struct ath_buf *bf;
ATH_TXBUF_LOCK(sc);
for (m = m0->m_nextpkt; m != NULL; m = m->m_nextpkt) {
bf = _ath_getbuf_locked(sc, ATH_BUFTYPE_NORMAL);
if (bf == NULL) {
DPRINTF(sc, ATH_DEBUG_XMIT, "%s: no buffer?\n",
__func__);
ath_txfrag_cleanup(sc, frags, ni);
break;
}
(void) ieee80211_ref_node(ni);
TAILQ_INSERT_TAIL(frags, bf, bf_list);
}
ATH_TXBUF_UNLOCK(sc);
return !TAILQ_EMPTY(frags);
}
static int
ath_tx_dmasetup(struct ath_softc *sc, struct ath_buf *bf, struct mbuf *m0)
{
struct mbuf *m;
int error;
error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, bf->bf_dmamap, m0,
bf->bf_segs, &bf->bf_nseg,
BUS_DMA_NOWAIT);
if (error == EFBIG) {
bf->bf_nseg = ATH_MAX_SCATTER + 1;
} else if (error != 0) {
sc->sc_stats.ast_tx_busdma++;
ieee80211_free_mbuf(m0);
return error;
}
if (bf->bf_nseg > ATH_MAX_SCATTER) {
sc->sc_stats.ast_tx_linear++;
m = m_collapse(m0, M_NOWAIT, ATH_MAX_SCATTER);
if (m == NULL) {
ieee80211_free_mbuf(m0);
sc->sc_stats.ast_tx_nombuf++;
return ENOMEM;
}
m0 = m;
error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, bf->bf_dmamap, m0,
bf->bf_segs, &bf->bf_nseg,
BUS_DMA_NOWAIT);
if (error != 0) {
sc->sc_stats.ast_tx_busdma++;
ieee80211_free_mbuf(m0);
return error;
}
KASSERT(bf->bf_nseg <= ATH_MAX_SCATTER,
("too many segments after defrag; nseg %u", bf->bf_nseg));
} else if (bf->bf_nseg == 0) {
sc->sc_stats.ast_tx_nodata++;
ieee80211_free_mbuf(m0);
return EIO;
}
DPRINTF(sc, ATH_DEBUG_XMIT, "%s: m %p len %u\n",
__func__, m0, m0->m_pkthdr.len);
bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREWRITE);
bf->bf_m = m0;
return 0;
}
static void
ath_tx_chaindesclist(struct ath_softc *sc, struct ath_desc *ds0,
struct ath_buf *bf, bool is_aggr, int is_first_subframe,
int is_last_subframe)
{
struct ath_hal *ah = sc->sc_ah;
char *ds;
int i, bp, dsp;
HAL_DMA_ADDR bufAddrList[4];
uint32_t segLenList[4];
int numTxMaps = 1;
int isFirstDesc = 1;
struct ath_descdma *dd = &sc->sc_txdma;
numTxMaps = sc->sc_tx_nmaps;
ds = (char *) bf->bf_desc;
bp = dsp = 0;
bzero(bufAddrList, sizeof(bufAddrList));
bzero(segLenList, sizeof(segLenList));
for (i = 0; i < bf->bf_nseg; i++) {
bufAddrList[bp] = bf->bf_segs[i].ds_addr;
segLenList[bp] = bf->bf_segs[i].ds_len;
bp++;
if ((i != bf->bf_nseg - 1) && (bp < numTxMaps))
continue;
bp = 0;
if (i == bf->bf_nseg - 1)
ath_hal_settxdesclink(ah, (struct ath_desc *) ds, 0);
else
ath_hal_settxdesclink(ah, (struct ath_desc *) ds,
bf->bf_daddr + dd->dd_descsize * (dsp + 1));
ath_hal_filltxdesc(ah, (struct ath_desc *) ds
, bufAddrList
, segLenList
, bf->bf_descid
, bf->bf_state.bfs_tx_queue
, isFirstDesc
, i == bf->bf_nseg - 1
, (struct ath_desc *) ds0
);
if (ath_tx_is_11n(sc))
ath_hal_clr11n_aggr(sc->sc_ah, (struct ath_desc *) ds);
if (is_last_subframe) {
ath_hal_set11n_aggr_last(sc->sc_ah,
(struct ath_desc *) ds);
} else if (is_aggr) {
ath_hal_set11n_aggr_middle(sc->sc_ah,
(struct ath_desc *) ds,
bf->bf_state.bfs_ndelim);
}
isFirstDesc = 0;
bf->bf_lastds = (struct ath_desc *) ds;
ds += sc->sc_tx_desclen;
dsp++;
bzero(bufAddrList, sizeof(bufAddrList));
bzero(segLenList, sizeof(segLenList));
}
bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREWRITE);
}
static void
ath_tx_set_ratectrl(struct ath_softc *sc, struct ieee80211_node *ni,
struct ath_buf *bf)
{
struct ath_rc_series *rc = bf->bf_state.bfs_rc;
if (! bf->bf_state.bfs_ismrr)
rc[1].tries = rc[2].tries = rc[3].tries = 0;
#if 0
else if (bf->bf_state.bfs_txflags & HAL_TXDESC_NOACK) {
rc[1].tries = rc[2].tries = rc[3].tries = 0;
rc[0].tries = 1;
}
#endif
if (ath_tx_is_11n(sc)) {
ath_buf_set_rate(sc, ni, bf);
} else {
ath_hal_setupxtxdesc(sc->sc_ah, bf->bf_desc
, rc[1].ratecode, rc[1].tries
, rc[2].ratecode, rc[2].tries
, rc[3].ratecode, rc[3].tries
);
}
}
static void
ath_tx_setds_11n(struct ath_softc *sc, struct ath_buf *bf_first)
{
struct ath_buf *bf, *bf_prev = NULL;
struct ath_desc *ds0 = bf_first->bf_desc;
DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, "%s: nframes=%d, al=%d\n",
__func__, bf_first->bf_state.bfs_nframes,
bf_first->bf_state.bfs_al);
bf = bf_first;
if (bf->bf_state.bfs_txrate0 == 0)
DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, "%s: bf=%p, txrate0=%d\n",
__func__, bf, 0);
if (bf->bf_state.bfs_rc[0].ratecode == 0)
DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, "%s: bf=%p, rix0=%d\n",
__func__, bf, 0);
while (bf != NULL) {
DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR,
"%s: bf=%p, nseg=%d, pktlen=%d, seqno=%d\n",
__func__, bf, bf->bf_nseg, bf->bf_state.bfs_pktlen,
SEQNO(bf->bf_state.bfs_seqno));
ath_hal_setuptxdesc(sc->sc_ah, bf->bf_desc
, bf->bf_state.bfs_pktlen
, bf->bf_state.bfs_hdrlen
, bf->bf_state.bfs_atype
, bf->bf_state.bfs_txpower
, bf->bf_state.bfs_txrate0
, bf->bf_state.bfs_try0
, bf->bf_state.bfs_keyix
, bf->bf_state.bfs_txantenna
, bf->bf_state.bfs_txflags | HAL_TXDESC_INTREQ
, bf->bf_state.bfs_ctsrate
, bf->bf_state.bfs_ctsduration
);
if (bf == bf_first) {
ath_tx_set_ratectrl(sc, bf->bf_node, bf);
}
ath_tx_chaindesclist(sc, ds0, bf,
1,
!! (bf == bf_first),
!! (bf->bf_next == NULL)
);
if (bf == bf_first) {
ath_hal_set11n_aggr_first(sc->sc_ah,
ds0,
bf->bf_state.bfs_al,
bf->bf_state.bfs_ndelim);
}
if (bf_prev != NULL)
ath_hal_settxdesclink(sc->sc_ah, bf_prev->bf_lastds,
bf->bf_daddr);
bf_prev = bf;
bf = bf->bf_next;
}
bf_first->bf_lastds = bf_prev->bf_lastds;
bf_first->bf_last = bf_prev;
ath_hal_setuplasttxdesc(sc->sc_ah, bf_prev->bf_lastds, ds0);
DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, "%s: end\n", __func__);
}
static void
ath_tx_handoff_mcast(struct ath_softc *sc, struct ath_txq *txq,
struct ath_buf *bf)
{
ATH_TX_LOCK_ASSERT(sc);
KASSERT((bf->bf_flags & ATH_BUF_BUSY) == 0,
("%s: busy status 0x%x", __func__, bf->bf_flags));
if (bf->bf_state.bfs_tx_queue != sc->sc_cabq->axq_qnum) {
DPRINTF(sc, ATH_DEBUG_XMIT,
"%s: bf=%p, bfs_tx_queue=%d, axq_qnum=%d\n",
__func__, bf, bf->bf_state.bfs_tx_queue,
txq->axq_qnum);
}
ATH_TXQ_LOCK(txq);
if (ATH_TXQ_LAST(txq, axq_q_s) != NULL) {
struct ath_buf *bf_last = ATH_TXQ_LAST(txq, axq_q_s);
struct ieee80211_frame *wh;
wh = mtod(bf_last->bf_m, struct ieee80211_frame *);
wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA;
bus_dmamap_sync(sc->sc_dmat, bf_last->bf_dmamap,
BUS_DMASYNC_PREWRITE);
ath_hal_settxdesclink(sc->sc_ah,
bf_last->bf_lastds,
bf->bf_daddr);
}
ATH_TXQ_INSERT_TAIL(txq, bf, bf_list);
ATH_TXQ_UNLOCK(txq);
}
static void
ath_tx_handoff_hw(struct ath_softc *sc, struct ath_txq *txq,
struct ath_buf *bf)
{
struct ath_hal *ah = sc->sc_ah;
struct ath_buf *bf_first;
ATH_TX_LOCK_ASSERT(sc);
KASSERT((bf->bf_flags & ATH_BUF_BUSY) == 0,
("%s: busy status 0x%x", __func__, bf->bf_flags));
KASSERT(txq->axq_qnum != ATH_TXQ_SWQ,
("ath_tx_handoff_hw called for mcast queue"));
if (sc->sc_txproc_cnt == 0 && sc->sc_txstart_cnt == 0) {
device_printf(sc->sc_dev,
"%s: TX dispatch without holding txcount/txstart refcnt!\n",
__func__);
}
ATH_TXQ_LOCK(txq);
ATH_TXQ_INSERT_TAIL(txq, bf, bf_list);
ATH_KTR(sc, ATH_KTR_TX, 3,
"ath_tx_handoff: non-tdma: txq=%u, add bf=%p "
"depth=%d",
txq->axq_qnum,
bf,
txq->axq_depth);
if (txq->axq_link != NULL) {
*txq->axq_link = bf->bf_daddr;
DPRINTF(sc, ATH_DEBUG_XMIT,
"%s: link[%u](%p)=%p (%p) depth %d\n", __func__,
txq->axq_qnum, txq->axq_link,
(caddr_t)bf->bf_daddr, bf->bf_desc,
txq->axq_depth);
ATH_KTR(sc, ATH_KTR_TX, 5,
"ath_tx_handoff: non-tdma: link[%u](%p)=%p (%p) "
"lastds=%d",
txq->axq_qnum, txq->axq_link,
(caddr_t)bf->bf_daddr, bf->bf_desc,
bf->bf_lastds);
}
if (! (txq->axq_flags & ATH_TXQ_PUTRUNNING)) {
bf_first = TAILQ_FIRST(&txq->axq_q);
txq->axq_flags |= ATH_TXQ_PUTRUNNING;
ath_hal_puttxbuf(ah, txq->axq_qnum, bf_first->bf_daddr);
DPRINTF(sc, ATH_DEBUG_XMIT,
"%s: TXDP[%u] = %p (%p) depth %d\n",
__func__, txq->axq_qnum,
(caddr_t)bf_first->bf_daddr, bf_first->bf_desc,
txq->axq_depth);
ATH_KTR(sc, ATH_KTR_TX, 5,
"ath_tx_handoff: TXDP[%u] = %p (%p) "
"lastds=%p depth %d",
txq->axq_qnum,
(caddr_t)bf_first->bf_daddr, bf_first->bf_desc,
bf_first->bf_lastds,
txq->axq_depth);
}
if (bf->bf_state.bfs_tx_queue != txq->axq_qnum) {
DPRINTF(sc, ATH_DEBUG_XMIT,
"%s: bf=%p, bfs_tx_queue=%d, axq_qnum=%d\n",
__func__, bf, bf->bf_state.bfs_tx_queue,
txq->axq_qnum);
}
if (bf->bf_state.bfs_aggr)
txq->axq_aggr_depth++;
ath_hal_gettxdesclinkptr(ah, bf->bf_lastds, &txq->axq_link);
ath_hal_txstart(ah, txq->axq_qnum);
ATH_TXQ_UNLOCK(txq);
ATH_KTR(sc, ATH_KTR_TX, 1,
"ath_tx_handoff: txq=%u, txstart", txq->axq_qnum);
}
static void
ath_legacy_tx_dma_restart(struct ath_softc *sc, struct ath_txq *txq)
{
struct ath_buf *bf, *bf_last;
ATH_TXQ_LOCK_ASSERT(txq);
bf = TAILQ_FIRST(&txq->axq_q);
bf_last = ATH_TXQ_LAST(txq, axq_q_s);
if (bf == NULL)
return;
DPRINTF(sc, ATH_DEBUG_RESET,
"%s: Q%d: bf=%p, bf_last=%p, daddr=0x%08x\n",
__func__,
txq->axq_qnum,
bf,
bf_last,
(uint32_t) bf->bf_daddr);
#ifdef ATH_DEBUG
if (sc->sc_debug & ATH_DEBUG_RESET)
ath_tx_dump(sc, txq);
#endif
KASSERT((!(txq->axq_flags & ATH_TXQ_PUTRUNNING)),
("%s: Q%d: called with PUTRUNNING=1\n",
__func__,
txq->axq_qnum));
ath_hal_puttxbuf(sc->sc_ah, txq->axq_qnum, bf->bf_daddr);
txq->axq_flags |= ATH_TXQ_PUTRUNNING;
ath_hal_gettxdesclinkptr(sc->sc_ah, bf_last->bf_lastds,
&txq->axq_link);
ath_hal_txstart(sc->sc_ah, txq->axq_qnum);
}
static void
ath_legacy_xmit_handoff(struct ath_softc *sc, struct ath_txq *txq,
struct ath_buf *bf)
{
ATH_TX_LOCK_ASSERT(sc);
#ifdef ATH_DEBUG_ALQ
if (if_ath_alq_checkdebug(&sc->sc_alq, ATH_ALQ_EDMA_TXDESC))
ath_tx_alq_post(sc, bf);
#endif
if (txq->axq_qnum == ATH_TXQ_SWQ)
ath_tx_handoff_mcast(sc, txq, bf);
else
ath_tx_handoff_hw(sc, txq, bf);
}
static int
ath_tx_tag_crypto(struct ath_softc *sc, struct ieee80211_node *ni,
struct mbuf *m0, int iswep, int isfrag, int *hdrlen, int *pktlen,
int *keyix)
{
DPRINTF(sc, ATH_DEBUG_XMIT,
"%s: hdrlen=%d, pktlen=%d, isfrag=%d, iswep=%d, m0=%p\n",
__func__,
*hdrlen,
*pktlen,
isfrag,
iswep,
m0);
if (iswep) {
const struct ieee80211_cipher *cip;
struct ieee80211_key *k;
k = ieee80211_crypto_encap(ni, m0);
if (k == NULL) {
return (0);
}
cip = k->wk_cipher;
(*hdrlen) += cip->ic_header;
(*pktlen) += cip->ic_header + cip->ic_trailer;
if ((k->wk_flags & IEEE80211_KEY_SWMIC) == 0 && !isfrag)
(*pktlen) += cip->ic_miclen;
(*keyix) = k->wk_keyix;
} else if (ni->ni_ucastkey.wk_cipher == &ieee80211_cipher_none) {
(*keyix) = ni->ni_ucastkey.wk_keyix;
if ((*keyix) == IEEE80211_KEYIX_NONE)
(*keyix) = HAL_TXKEYIX_INVALID;
} else
(*keyix) = HAL_TXKEYIX_INVALID;
return (1);
}
static void
ath_tx_calc_protection(struct ath_softc *sc, struct ath_buf *bf)
{
struct ieee80211_frame *wh;
uint8_t rix;
uint16_t flags;
int shortPreamble;
const HAL_RATE_TABLE *rt = sc->sc_currates;
struct ieee80211com *ic = &sc->sc_ic;
flags = bf->bf_state.bfs_txflags;
rix = bf->bf_state.bfs_rc[0].rix;
shortPreamble = bf->bf_state.bfs_shpream;
wh = mtod(bf->bf_m, struct ieee80211_frame *);
if (bf->bf_flags & ATH_BUF_TOA_PROBE) {
flags &= ~(HAL_TXDESC_CTSENA | HAL_TXDESC_RTSENA);
bf->bf_state.bfs_doprot = 0;
goto finish;
}
if ((ic->ic_flags & IEEE80211_F_USEPROT) &&
rt->info[rix].phy == IEEE80211_T_OFDM &&
(flags & HAL_TXDESC_NOACK) == 0) {
bf->bf_state.bfs_doprot = 1;
if (ic->ic_protmode == IEEE80211_PROT_RTSCTS) {
flags |= HAL_TXDESC_RTSENA;
} else if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) {
flags |= HAL_TXDESC_CTSENA;
}
sc->sc_stats.ast_tx_protect++;
}
if ((ic->ic_htprotmode == IEEE80211_PROT_RTSCTS) &&
rt->info[rix].phy == IEEE80211_T_HT &&
(flags & HAL_TXDESC_NOACK) == 0) {
flags |= HAL_TXDESC_RTSENA;
sc->sc_stats.ast_tx_htprotect++;
}
finish:
bf->bf_state.bfs_txflags = flags;
}
static void
ath_tx_calc_duration(struct ath_softc *sc, struct ath_buf *bf)
{
struct ieee80211_frame *wh;
uint8_t rix;
uint16_t flags;
int shortPreamble;
struct ath_hal *ah = sc->sc_ah;
const HAL_RATE_TABLE *rt = sc->sc_currates;
int isfrag = bf->bf_m->m_flags & M_FRAG;
flags = bf->bf_state.bfs_txflags;
rix = bf->bf_state.bfs_rc[0].rix;
shortPreamble = bf->bf_state.bfs_shpream;
wh = mtod(bf->bf_m, struct ieee80211_frame *);
if ((flags & HAL_TXDESC_NOACK) == 0 && !IEEE80211_IS_CTL(wh)) {
u_int16_t dur;
if (shortPreamble)
dur = rt->info[rix].spAckDuration;
else
dur = rt->info[rix].lpAckDuration;
if (wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG) {
dur += dur;
dur += ath_hal_computetxtime(ah,
rt,
bf->bf_nextfraglen,
rix, shortPreamble,
AH_TRUE);
}
if (isfrag) {
bf->bf_state.bfs_ismrr = 0;
bf->bf_state.bfs_try0 = ATH_TXMGTTRY;
}
*(u_int16_t *)wh->i_dur = htole16(dur);
}
}
static uint8_t
ath_tx_get_rtscts_rate(struct ath_hal *ah, const HAL_RATE_TABLE *rt,
int cix, int shortPreamble)
{
uint8_t ctsrate;
KASSERT(cix != 0xff, ("cix not setup"));
ctsrate = rt->info[cix].rateCode;
if (shortPreamble)
ctsrate |= rt->info[cix].shortPreamble;
return (ctsrate);
}
static int
ath_tx_calc_ctsduration(struct ath_hal *ah, int rix, int cix,
int shortPreamble, int pktlen, const HAL_RATE_TABLE *rt,
int flags)
{
int ctsduration = 0;
if (rt->info[cix].phy == IEEE80211_T_HT) {
printf("%s: HT rate where it shouldn't be (0x%x)\n",
__func__, rt->info[cix].rateCode);
return (-1);
}
if (shortPreamble) {
if (flags & HAL_TXDESC_RTSENA)
ctsduration += rt->info[cix].spAckDuration;
ctsduration += ath_hal_computetxtime(ah,
rt, pktlen, rix, AH_TRUE, AH_TRUE);
if ((flags & HAL_TXDESC_NOACK) == 0)
ctsduration += rt->info[rix].spAckDuration;
} else {
if (flags & HAL_TXDESC_RTSENA)
ctsduration += rt->info[cix].lpAckDuration;
ctsduration += ath_hal_computetxtime(ah,
rt, pktlen, rix, AH_FALSE, AH_TRUE);
if ((flags & HAL_TXDESC_NOACK) == 0)
ctsduration += rt->info[rix].lpAckDuration;
}
return (ctsduration);
}
static void
ath_tx_set_rtscts(struct ath_softc *sc, struct ath_buf *bf)
{
uint16_t ctsduration = 0;
uint8_t ctsrate = 0;
uint8_t rix = bf->bf_state.bfs_rc[0].rix;
uint8_t cix = 0;
const HAL_RATE_TABLE *rt = sc->sc_currates;
if ((bf->bf_state.bfs_txflags &
(HAL_TXDESC_RTSENA | HAL_TXDESC_CTSENA)) == 0) {
bf->bf_state.bfs_ctsrate = 0;
bf->bf_state.bfs_ctsduration = 0;
return;
}
if (bf->bf_state.bfs_doprot)
rix = sc->sc_protrix;
else
rix = bf->bf_state.bfs_rc[0].rix;
if (bf->bf_state.bfs_ctsrate0 != 0)
cix = ath_tx_findrix(sc, bf->bf_state.bfs_ctsrate0);
else
cix = rt->info[rix].controlRate;
ctsrate = ath_tx_get_rtscts_rate(sc->sc_ah, rt, cix,
bf->bf_state.bfs_shpream);
if (! ath_tx_is_11n(sc))
ctsduration = ath_tx_calc_ctsduration(sc->sc_ah, rix, cix,
bf->bf_state.bfs_shpream, bf->bf_state.bfs_pktlen,
rt, bf->bf_state.bfs_txflags);
bf->bf_state.bfs_ctsrate = ctsrate;
bf->bf_state.bfs_ctsduration = ctsduration;
if (!sc->sc_mrrprot) {
bf->bf_state.bfs_ismrr = 0;
bf->bf_state.bfs_try0 =
bf->bf_state.bfs_rc[0].tries = ATH_TXMGTTRY;
}
}
static void
ath_tx_setds(struct ath_softc *sc, struct ath_buf *bf)
{
struct ath_desc *ds = bf->bf_desc;
struct ath_hal *ah = sc->sc_ah;
if (bf->bf_state.bfs_txrate0 == 0)
DPRINTF(sc, ATH_DEBUG_XMIT,
"%s: bf=%p, txrate0=%d\n", __func__, bf, 0);
ath_hal_setuptxdesc(ah, ds
, bf->bf_state.bfs_pktlen
, bf->bf_state.bfs_hdrlen
, bf->bf_state.bfs_atype
, bf->bf_state.bfs_txpower
, bf->bf_state.bfs_txrate0
, bf->bf_state.bfs_try0
, bf->bf_state.bfs_keyix
, bf->bf_state.bfs_txantenna
, bf->bf_state.bfs_txflags
, bf->bf_state.bfs_ctsrate
, bf->bf_state.bfs_ctsduration
);
bf->bf_lastds = ds;
bf->bf_last = bf;
ath_tx_set_ratectrl(sc, bf->bf_node, bf);
ath_tx_chaindesclist(sc, ds, bf, 0, 0, 0);
}
static void
ath_tx_do_ratelookup(struct ath_softc *sc, struct ath_buf *bf, int tid,
int pktlen, int is_aggr)
{
uint8_t rate, rix;
int try0;
int maxdur;
int maxpktlen;
if (! bf->bf_state.bfs_doratelookup)
return;
bzero(bf->bf_state.bfs_rc, sizeof(bf->bf_state.bfs_rc));
ATH_NODE_LOCK(ATH_NODE(bf->bf_node));
ath_rate_findrate(sc, ATH_NODE(bf->bf_node), bf->bf_state.bfs_shpream,
pktlen, tid, is_aggr, &rix, &try0, &rate, &maxdur, &maxpktlen);
bf->bf_state.bfs_rc[0].rix = rix;
bf->bf_state.bfs_rc[0].ratecode = rate;
bf->bf_state.bfs_rc[0].tries = try0;
if (bf->bf_state.bfs_ismrr && try0 != ATH_TXMAXTRY)
ath_rate_getxtxrates(sc, ATH_NODE(bf->bf_node), rix,
is_aggr, bf->bf_state.bfs_rc);
ATH_NODE_UNLOCK(ATH_NODE(bf->bf_node));
sc->sc_txrix = rix;
sc->sc_lastdatarix = rix;
bf->bf_state.bfs_try0 = try0;
bf->bf_state.bfs_txrate0 = rate;
bf->bf_state.bfs_rc_maxpktlen = maxpktlen;
}
static void
ath_tx_update_clrdmask(struct ath_softc *sc, struct ath_tid *tid,
struct ath_buf *bf)
{
struct ath_node *an = ATH_NODE(bf->bf_node);
ATH_TX_LOCK_ASSERT(sc);
if (an->clrdmask == 1) {
bf->bf_state.bfs_txflags |= HAL_TXDESC_CLRDMASK;
an->clrdmask = 0;
}
}
static int
ath_tx_should_swq_frame(struct ath_softc *sc, struct ath_node *an,
struct mbuf *m0, int *queue_to_head)
{
struct ieee80211_node *ni = &an->an_node;
struct ieee80211_frame *wh;
uint8_t type, subtype;
wh = mtod(m0, struct ieee80211_frame *);
type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
(*queue_to_head) = 0;
if ((ATH_NODE(ni)->an_is_powersave == 0)
&& type == IEEE80211_FC0_TYPE_CTL &&
subtype == IEEE80211_FC0_SUBTYPE_BAR) {
DPRINTF(sc, ATH_DEBUG_SW_TX,
"%s: BAR: TX'ing direct\n", __func__);
return (0);
} else if ((ATH_NODE(ni)->an_is_powersave == 1)
&& type == IEEE80211_FC0_TYPE_CTL &&
subtype == IEEE80211_FC0_SUBTYPE_BAR) {
DPRINTF(sc, ATH_DEBUG_SW_TX,
"%s: swq: TX'ing\n", __func__);
(*queue_to_head) = 1;
return (1);
} else if ((ATH_NODE(ni)->an_is_powersave == 1)
&& (type == IEEE80211_FC0_TYPE_MGT ||
type == IEEE80211_FC0_TYPE_CTL)) {
DPRINTF(sc, ATH_DEBUG_XMIT,
"%s: %6D: Node is asleep; sending mgmt "
"(type=%d, subtype=%d)\n",
__func__, ni->ni_macaddr, ":", type, subtype);
return (0);
} else {
return (1);
}
}
static void
ath_tx_xmit_normal(struct ath_softc *sc, struct ath_txq *txq,
struct ath_buf *bf)
{
struct ath_node *an = ATH_NODE(bf->bf_node);
struct ath_tid *tid = &an->an_tid[bf->bf_state.bfs_tid];
ATH_TX_LOCK_ASSERT(sc);
bf->bf_state.bfs_txflags |= HAL_TXDESC_CLRDMASK;
ath_tx_do_ratelookup(sc, bf, tid->tid, bf->bf_state.bfs_pktlen, false);
ath_tx_calc_duration(sc, bf);
ath_tx_calc_protection(sc, bf);
ath_tx_set_rtscts(sc, bf);
ath_tx_rate_fill_rcflags(sc, bf);
ath_tx_setds(sc, bf);
tid->hwq_depth++;
bf->bf_comp = ath_tx_normal_comp;
ath_tx_handoff(sc, txq, bf);
}
static int
ath_tx_normal_setup(struct ath_softc *sc, struct ieee80211_node *ni,
struct ath_buf *bf, struct mbuf *m0, struct ath_txq *txq)
{
struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = &sc->sc_ic;
int error, iswep, ismcast, isfrag, ismrr;
int keyix, hdrlen, pktlen, try0 = 0;
u_int8_t rix = 0, txrate = 0;
struct ath_desc *ds;
struct ieee80211_frame *wh;
u_int subtype, flags;
HAL_PKT_TYPE atype;
const HAL_RATE_TABLE *rt;
HAL_BOOL shortPreamble;
struct ath_node *an;
u_int pri;
ATH_TX_LOCK_ASSERT(sc);
wh = mtod(m0, struct ieee80211_frame *);
iswep = wh->i_fc[1] & IEEE80211_FC1_PROTECTED;
ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1);
isfrag = m0->m_flags & M_FRAG;
hdrlen = ieee80211_anyhdrsize(wh);
pktlen = m0->m_pkthdr.len - (hdrlen & 3);
if (! ath_tx_tag_crypto(sc, ni, m0, iswep, isfrag, &hdrlen,
&pktlen, &keyix)) {
ieee80211_free_mbuf(m0);
return EIO;
}
wh = mtod(m0, struct ieee80211_frame *);
pktlen += IEEE80211_CRC_LEN;
error = ath_tx_dmasetup(sc, bf, m0);
if (error != 0)
return error;
KASSERT((ni != NULL), ("%s: ni=NULL!", __func__));
bf->bf_node = ni;
m0 = bf->bf_m;
wh = mtod(m0, struct ieee80211_frame *);
ds = bf->bf_desc;
rt = sc->sc_currates;
KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode));
if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
(ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)) {
shortPreamble = AH_TRUE;
sc->sc_stats.ast_tx_shortpre++;
} else {
shortPreamble = AH_FALSE;
}
an = ATH_NODE(ni);
flags = 0;
ismrr = 0;
pri = ath_tx_getac(sc, m0);
switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) {
case IEEE80211_FC0_TYPE_MGT:
subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
if (subtype == IEEE80211_FC0_SUBTYPE_BEACON)
atype = HAL_PKT_TYPE_BEACON;
else if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP)
atype = HAL_PKT_TYPE_PROBE_RESP;
else if (subtype == IEEE80211_FC0_SUBTYPE_ATIM)
atype = HAL_PKT_TYPE_ATIM;
else
atype = HAL_PKT_TYPE_NORMAL;
rix = an->an_mgmtrix;
txrate = rt->info[rix].rateCode;
if (shortPreamble)
txrate |= rt->info[rix].shortPreamble;
try0 = ATH_TXMGTTRY;
flags |= HAL_TXDESC_INTREQ;
break;
case IEEE80211_FC0_TYPE_CTL:
atype = HAL_PKT_TYPE_PSPOLL;
rix = an->an_mgmtrix;
txrate = rt->info[rix].rateCode;
if (shortPreamble)
txrate |= rt->info[rix].shortPreamble;
try0 = ATH_TXMGTTRY;
flags |= HAL_TXDESC_INTREQ;
break;
case IEEE80211_FC0_TYPE_DATA:
atype = HAL_PKT_TYPE_NORMAL;
if (ismcast) {
rix = an->an_mcastrix;
txrate = rt->info[rix].rateCode;
if (shortPreamble)
txrate |= rt->info[rix].shortPreamble;
try0 = 1;
} else if (m0->m_flags & M_EAPOL) {
rix = an->an_mgmtrix;
txrate = rt->info[rix].rateCode;
if (shortPreamble)
txrate |= rt->info[rix].shortPreamble;
try0 = ATH_TXMAXTRY;
} else {
ismrr = 1;
bf->bf_state.bfs_doratelookup = 1;
}
if (ieee80211_wme_vap_ac_is_noack(vap, pri))
flags |= HAL_TXDESC_NOACK;
break;
default:
device_printf(sc->sc_dev, "bogus frame type 0x%x (%s)\n",
wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK, __func__);
ieee80211_free_mbuf(m0);
return EIO;
}
#if 0
if (txq != sc->sc_ac2q[pri]) {
DPRINTF(sc, ATH_DEBUG_XMIT,
"%s: txq=%p (%d), pri=%d, pri txq=%p (%d)\n",
__func__,
txq,
txq->axq_qnum,
pri,
sc->sc_ac2q[pri],
sc->sc_ac2q[pri]->axq_qnum);
}
#endif
if (ismcast) {
flags |= HAL_TXDESC_NOACK;
} else if (pktlen > vap->iv_rtsthreshold &&
(ni->ni_ath_flags & IEEE80211_NODE_FF) == 0) {
flags |= HAL_TXDESC_RTSENA;
sc->sc_stats.ast_tx_rts++;
}
if (flags & HAL_TXDESC_NOACK)
sc->sc_stats.ast_tx_noack++;
#ifdef IEEE80211_SUPPORT_TDMA
if (sc->sc_tdma && (flags & HAL_TXDESC_NOACK) == 0) {
DPRINTF(sc, ATH_DEBUG_TDMA,
"%s: discard frame, ACK required w/ TDMA\n", __func__);
sc->sc_stats.ast_tdma_ack++;
ieee80211_free_mbuf(m0);
return EIO;
}
#endif
if (ieee80211_get_toa_params(m0, NULL)) {
device_printf(sc->sc_dev,
"%s: setting TX positioning bit\n", __func__);
flags |= HAL_TXDESC_POS;
flags &= ~(HAL_TXDESC_RTSENA | HAL_TXDESC_CTSENA);
bf->bf_flags |= ATH_BUF_TOA_PROBE;
}
#if 0
flags |= HAL_TXDESC_HWTS;
#endif
if (flags & HAL_TXDESC_INTREQ) {
txq->axq_intrcnt = 0;
} else if (++txq->axq_intrcnt >= sc->sc_txintrperiod) {
flags |= HAL_TXDESC_INTREQ;
txq->axq_intrcnt = 0;
}
m0->m_nextpkt = NULL;
if (IFF_DUMPPKTS(sc, ATH_DEBUG_XMIT))
ieee80211_dump_pkt(ic, mtod(m0, const uint8_t *), m0->m_len,
sc->sc_hwmap[rix].ieeerate, -1);
if (ieee80211_radiotap_active_vap(vap)) {
sc->sc_tx_th.wt_flags = sc->sc_hwmap[rix].txflags;
if (iswep)
sc->sc_tx_th.wt_flags |= IEEE80211_RADIOTAP_F_WEP;
if (isfrag)
sc->sc_tx_th.wt_flags |= IEEE80211_RADIOTAP_F_FRAG;
sc->sc_tx_th.wt_rate = sc->sc_hwmap[rix].ieeerate;
sc->sc_tx_th.wt_txpower = ieee80211_get_node_txpower(ni);
sc->sc_tx_th.wt_antenna = sc->sc_txantenna;
ieee80211_radiotap_tx(vap, m0);
}
bzero(&bf->bf_state.bfs_rc, sizeof(bf->bf_state.bfs_rc));
bf->bf_state.bfs_rc[0].rix = rix;
bf->bf_state.bfs_rc[0].tries = try0;
bf->bf_state.bfs_rc[0].ratecode = txrate;
bf->bf_state.bfs_pktlen = pktlen;
bf->bf_state.bfs_hdrlen = hdrlen;
bf->bf_state.bfs_atype = atype;
bf->bf_state.bfs_txpower = ieee80211_get_node_txpower(ni);
bf->bf_state.bfs_txrate0 = txrate;
bf->bf_state.bfs_try0 = try0;
bf->bf_state.bfs_keyix = keyix;
bf->bf_state.bfs_txantenna = sc->sc_txantenna;
bf->bf_state.bfs_txflags = flags;
bf->bf_state.bfs_shpream = shortPreamble;
bf->bf_state.bfs_ctsrate0 = 0;
bf->bf_state.bfs_ctsrate = 0;
bf->bf_state.bfs_ctsduration = 0;
bf->bf_state.bfs_ismrr = ismrr;
return 0;
}
int
ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni,
struct ath_buf *bf, struct mbuf *m0)
{
struct ieee80211vap *vap = ni->ni_vap;
struct ath_vap *avp = ATH_VAP(vap);
int r = 0;
u_int pri;
int tid;
struct ath_txq *txq;
int ismcast;
const struct ieee80211_frame *wh;
int is_ampdu, is_ampdu_tx, is_ampdu_pending;
ieee80211_seq seqno;
uint8_t type, subtype;
int queue_to_head;
ATH_TX_LOCK_ASSERT(sc);
pri = ath_tx_getac(sc, m0);
tid = ath_tx_gettid(sc, m0);
txq = sc->sc_ac2q[pri];
wh = mtod(m0, struct ieee80211_frame *);
ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1);
type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
if (IEEE80211_IS_MULTICAST(wh->i_addr1)) {
if (sc->sc_cabq->axq_depth + sc->sc_cabq->fifo.axq_depth
> sc->sc_txq_mcastq_maxdepth) {
sc->sc_stats.ast_tx_mcastq_overflow++;
m_freem(m0);
return (ENOBUFS);
}
}
if (type == IEEE80211_FC0_TYPE_DATA &&
ATH_NODE(ni)->an_is_powersave &&
ATH_NODE(ni)->an_swq_depth >
sc->sc_txq_node_psq_maxdepth) {
sc->sc_stats.ast_tx_node_psq_overflow++;
m_freem(m0);
return (ENOBUFS);
}
is_ampdu_tx = ath_tx_ampdu_running(sc, ATH_NODE(ni), tid);
is_ampdu_pending = ath_tx_ampdu_pending(sc, ATH_NODE(ni), tid);
is_ampdu = is_ampdu_tx | is_ampdu_pending;
DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: tid=%d, ac=%d, is_ampdu=%d\n",
__func__, tid, pri, is_ampdu);
bf->bf_state.bfs_tid = tid;
bf->bf_state.bfs_tx_queue = txq->axq_qnum;
bf->bf_state.bfs_pri = pri;
#if 1
if (sc->sc_cabq_enable && ismcast && (vap->iv_ps_sta || avp->av_mcastq.axq_depth)) {
txq = &avp->av_mcastq;
bf->bf_state.bfs_tx_queue = sc->sc_cabq->axq_qnum;
}
#endif
bf->bf_state.bfs_dobaw = 0;
if (is_ampdu_tx && (! IEEE80211_IS_MULTICAST(wh->i_addr1))) {
seqno = ath_tx_tid_seqno_assign(sc, ni, bf, m0);
if (IEEE80211_QOS_HAS_SEQ(wh) &&
(! IEEE80211_IS_MULTICAST(wh->i_addr1)) &&
(! IEEE80211_IS_QOS_NULL(wh))) {
bf->bf_state.bfs_dobaw = 1;
}
}
bf->bf_state.bfs_seqno = M_SEQNO_GET(m0) << IEEE80211_SEQ_SEQ_SHIFT;
if (is_ampdu_pending)
DPRINTF(sc, ATH_DEBUG_SW_TX,
"%s: tid %d: ampdu pending, seqno %d\n",
__func__, tid, M_SEQNO_GET(m0));
r = ath_tx_normal_setup(sc, ni, bf, m0, txq);
if (r != 0)
goto done;
m0 = bf->bf_m;
#if 1
if (txq == &avp->av_mcastq) {
DPRINTF(sc, ATH_DEBUG_SW_TX,
"%s: bf=%p: mcastq: TX'ing\n", __func__, bf);
bf->bf_state.bfs_txflags |= HAL_TXDESC_CLRDMASK;
ath_tx_xmit_normal(sc, txq, bf);
} else if (ath_tx_should_swq_frame(sc, ATH_NODE(ni), m0,
&queue_to_head)) {
ath_tx_swq(sc, ni, txq, queue_to_head, bf);
} else {
bf->bf_state.bfs_txflags |= HAL_TXDESC_CLRDMASK;
ath_tx_xmit_normal(sc, txq, bf);
}
#else
bf->bf_state.bfs_txflags |= HAL_TXDESC_CLRDMASK;
ath_tx_leak_count_update(sc, tid, bf);
ath_tx_xmit_normal(sc, txq, bf);
#endif
done:
return 0;
}
static int
ath_tx_raw_start(struct ath_softc *sc, struct ieee80211_node *ni,
struct ath_buf *bf, struct mbuf *m0,
const struct ieee80211_bpf_params *params)
{
struct ieee80211com *ic = &sc->sc_ic;
struct ieee80211vap *vap = ni->ni_vap;
int error, ismcast, ismrr;
int keyix, hdrlen, pktlen, try0, txantenna;
u_int8_t rix, txrate;
struct ieee80211_frame *wh;
u_int flags;
HAL_PKT_TYPE atype;
const HAL_RATE_TABLE *rt;
struct ath_desc *ds;
u_int pri;
int o_tid = -1;
int do_override;
uint8_t type, subtype;
int queue_to_head;
struct ath_node *an = ATH_NODE(ni);
ATH_TX_LOCK_ASSERT(sc);
wh = mtod(m0, struct ieee80211_frame *);
ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1);
hdrlen = ieee80211_anyhdrsize(wh);
pktlen = m0->m_pkthdr.len - (hdrlen & 3) + IEEE80211_CRC_LEN;
type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
ATH_KTR(sc, ATH_KTR_TX, 2,
"ath_tx_raw_start: ni=%p, bf=%p, raw", ni, bf);
DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: ismcast=%d\n",
__func__, ismcast);
pri = params->ibp_pri & 3;
if (! IEEE80211_QOS_HAS_SEQ(wh))
pri = ath_tx_getac(sc, m0);
do_override = ath_tx_action_frame_override_queue(sc, ni, m0, &o_tid);
if (do_override) {
#if 1
DPRINTF(sc, ATH_DEBUG_XMIT,
"%s: overriding tid %d pri %d -> %d\n",
__func__, o_tid, pri, TID_TO_WME_AC(o_tid));
#endif
pri = TID_TO_WME_AC(o_tid);
}
if (! ath_tx_tag_crypto(sc, ni,
m0, params->ibp_flags & IEEE80211_BPF_CRYPTO, 0,
&hdrlen, &pktlen, &keyix)) {
ieee80211_free_mbuf(m0);
return EIO;
}
wh = mtod(m0, struct ieee80211_frame *);
bf->bf_state.bfs_dobaw = 0;
error = ath_tx_dmasetup(sc, bf, m0);
if (error != 0)
return error;
m0 = bf->bf_m;
wh = mtod(m0, struct ieee80211_frame *);
KASSERT((ni != NULL), ("%s: ni=NULL!", __func__));
bf->bf_node = ni;
flags = HAL_TXDESC_CLRDMASK;
flags |= HAL_TXDESC_INTREQ;
if (params->ibp_flags & IEEE80211_BPF_RTS)
flags |= HAL_TXDESC_RTSENA;
else if (params->ibp_flags & IEEE80211_BPF_CTS) {
bf->bf_state.bfs_doprot = 1;
flags |= HAL_TXDESC_CTSENA;
}
if ((params->ibp_flags & IEEE80211_BPF_NOACK) || ismcast)
flags |= HAL_TXDESC_NOACK;
rt = sc->sc_currates;
KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode));
rix = ath_tx_findrix(sc, params->ibp_rate0);
try0 = params->ibp_try0;
if (m0->m_flags & M_EAPOL) {
rix = an->an_mgmtrix;
try0 = ATH_TXMAXTRY;
}
if (ieee80211_get_toa_params(m0, NULL)) {
device_printf(sc->sc_dev,
"%s: setting TX positioning bit\n", __func__);
flags |= HAL_TXDESC_POS;
flags &= ~(HAL_TXDESC_RTSENA | HAL_TXDESC_CTSENA);
bf->bf_flags |= ATH_BUF_TOA_PROBE;
}
txrate = rt->info[rix].rateCode;
if (params->ibp_flags & IEEE80211_BPF_SHORTPRE)
txrate |= rt->info[rix].shortPreamble;
sc->sc_txrix = rix;
ismrr = (params->ibp_try1 != 0);
txantenna = params->ibp_pri >> 2;
if (txantenna == 0)
txantenna = sc->sc_txantenna;
if (flags & (HAL_TXDESC_RTSENA|HAL_TXDESC_CTSENA))
bf->bf_state.bfs_ctsrate0 = params->ibp_ctsrate;
atype = HAL_PKT_TYPE_PSPOLL;
if (IFF_DUMPPKTS(sc, ATH_DEBUG_XMIT))
ieee80211_dump_pkt(ic, mtod(m0, caddr_t), m0->m_len,
sc->sc_hwmap[rix].ieeerate, -1);
if (ieee80211_radiotap_active_vap(vap)) {
sc->sc_tx_th.wt_flags = sc->sc_hwmap[rix].txflags;
if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED)
sc->sc_tx_th.wt_flags |= IEEE80211_RADIOTAP_F_WEP;
if (m0->m_flags & M_FRAG)
sc->sc_tx_th.wt_flags |= IEEE80211_RADIOTAP_F_FRAG;
sc->sc_tx_th.wt_rate = sc->sc_hwmap[rix].ieeerate;
sc->sc_tx_th.wt_txpower = MIN(params->ibp_power,
ieee80211_get_node_txpower(ni));
sc->sc_tx_th.wt_antenna = sc->sc_txantenna;
ieee80211_radiotap_tx(vap, m0);
}
ds = bf->bf_desc;
bf->bf_state.bfs_pktlen = pktlen;
bf->bf_state.bfs_hdrlen = hdrlen;
bf->bf_state.bfs_atype = atype;
bf->bf_state.bfs_txpower = MIN(params->ibp_power,
ieee80211_get_node_txpower(ni));
bf->bf_state.bfs_txrate0 = txrate;
bf->bf_state.bfs_try0 = try0;
bf->bf_state.bfs_keyix = keyix;
bf->bf_state.bfs_txantenna = txantenna;
bf->bf_state.bfs_txflags = flags;
bf->bf_state.bfs_shpream =
!! (params->ibp_flags & IEEE80211_BPF_SHORTPRE);
bf->bf_state.bfs_tid = WME_AC_TO_TID(pri);
bf->bf_state.bfs_tx_queue = sc->sc_ac2q[pri]->axq_qnum;
bf->bf_state.bfs_pri = pri;
bf->bf_state.bfs_ctsrate = 0;
bf->bf_state.bfs_ctsduration = 0;
bf->bf_state.bfs_ismrr = ismrr;
bzero(&bf->bf_state.bfs_rc, sizeof(bf->bf_state.bfs_rc));
bf->bf_state.bfs_rc[0].rix = rix;
bf->bf_state.bfs_rc[0].tries = try0;
bf->bf_state.bfs_rc[0].ratecode = txrate;
if (ismrr) {
int rix;
rix = ath_tx_findrix(sc, params->ibp_rate1);
bf->bf_state.bfs_rc[1].rix = rix;
bf->bf_state.bfs_rc[1].tries = params->ibp_try1;
rix = ath_tx_findrix(sc, params->ibp_rate2);
bf->bf_state.bfs_rc[2].rix = rix;
bf->bf_state.bfs_rc[2].tries = params->ibp_try2;
rix = ath_tx_findrix(sc, params->ibp_rate3);
bf->bf_state.bfs_rc[3].rix = rix;
bf->bf_state.bfs_rc[3].tries = params->ibp_try3;
}
ath_tx_rate_fill_rcflags(sc, bf);
DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: dooverride=%d\n",
__func__, do_override);
#if 1
if (do_override) {
bf->bf_state.bfs_txflags |= HAL_TXDESC_CLRDMASK;
ath_tx_xmit_normal(sc, sc->sc_ac2q[pri], bf);
} else if (ath_tx_should_swq_frame(sc, ATH_NODE(ni), m0,
&queue_to_head)) {
ath_tx_swq(sc, ni, sc->sc_ac2q[pri], queue_to_head, bf);
} else {
bf->bf_state.bfs_txflags |= HAL_TXDESC_CLRDMASK;
ath_tx_xmit_normal(sc, sc->sc_ac2q[pri], bf);
}
#else
bf->bf_state.bfs_txflags |= HAL_TXDESC_CLRDMASK;
ath_tx_leak_count_update(sc, tid, bf);
ath_tx_xmit_normal(sc, sc->sc_ac2q[pri], bf);
#endif
return 0;
}
int
ath_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
const struct ieee80211_bpf_params *params)
{
struct ieee80211com *ic = ni->ni_ic;
struct ath_softc *sc = ic->ic_softc;
struct ath_buf *bf;
struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *);
int error = 0;
ATH_PCU_LOCK(sc);
if (sc->sc_inreset_cnt > 0) {
DPRINTF(sc, ATH_DEBUG_XMIT,
"%s: sc_inreset_cnt > 0; bailing\n", __func__);
error = EIO;
ATH_PCU_UNLOCK(sc);
goto badbad;
}
sc->sc_txstart_cnt++;
ATH_PCU_UNLOCK(sc);
ATH_LOCK(sc);
ath_power_set_power_state(sc, HAL_PM_AWAKE);
ATH_UNLOCK(sc);
ATH_TX_LOCK(sc);
if (!sc->sc_running || sc->sc_invalid) {
DPRINTF(sc, ATH_DEBUG_XMIT, "%s: discard frame, r/i: %d/%d",
__func__, sc->sc_running, sc->sc_invalid);
m_freem(m);
error = ENETDOWN;
goto bad;
}
if (IEEE80211_IS_MULTICAST(wh->i_addr1)) {
if (sc->sc_cabq->axq_depth + sc->sc_cabq->fifo.axq_depth
> sc->sc_txq_mcastq_maxdepth) {
sc->sc_stats.ast_tx_mcastq_overflow++;
error = ENOBUFS;
}
if (error != 0) {
m_freem(m);
goto bad;
}
}
bf = ath_getbuf(sc, ATH_BUFTYPE_MGMT);
if (bf == NULL) {
sc->sc_stats.ast_tx_nobuf++;
m_freem(m);
error = ENOBUFS;
goto bad;
}
ATH_KTR(sc, ATH_KTR_TX, 3, "ath_raw_xmit: m=%p, params=%p, bf=%p\n",
m, params, bf);
if (params == NULL) {
if (ath_tx_start(sc, ni, bf, m)) {
error = EIO;
goto bad2;
}
} else {
if (ath_tx_raw_start(sc, ni, bf, m, params)) {
error = EIO;
goto bad2;
}
}
sc->sc_wd_timer = 5;
sc->sc_stats.ast_tx_raw++;
ath_tx_update_tim(sc, ni, 1);
ATH_TX_UNLOCK(sc);
ATH_PCU_LOCK(sc);
sc->sc_txstart_cnt--;
ATH_PCU_UNLOCK(sc);
ATH_LOCK(sc);
ath_power_restore_power_state(sc);
ATH_UNLOCK(sc);
return 0;
bad2:
ATH_KTR(sc, ATH_KTR_TX, 3, "ath_raw_xmit: bad2: m=%p, params=%p, "
"bf=%p",
m,
params,
bf);
ATH_TXBUF_LOCK(sc);
ath_returnbuf_head(sc, bf);
ATH_TXBUF_UNLOCK(sc);
bad:
ATH_TX_UNLOCK(sc);
ATH_PCU_LOCK(sc);
sc->sc_txstart_cnt--;
ATH_PCU_UNLOCK(sc);
ATH_LOCK(sc);
ath_power_restore_power_state(sc);
ATH_UNLOCK(sc);
badbad:
ATH_KTR(sc, ATH_KTR_TX, 2, "ath_raw_xmit: bad0: m=%p, params=%p",
m, params);
sc->sc_stats.ast_tx_raw_fail++;
return error;
}
static int
ath_tx_action_frame_override_queue(struct ath_softc *sc,
struct ieee80211_node *ni,
struct mbuf *m0, int *tid)
{
struct ieee80211_frame *wh = mtod(m0, struct ieee80211_frame *);
struct ieee80211_action_ba_addbarequest *ia;
uint8_t *frm;
uint16_t baparamset;
if (! IEEE80211_IS_MGMT_ACTION(wh))
return 0;
#if 0
if (! ieee80211_parse_action(ni, m))
return 0;
#endif
frm = (u_int8_t *)&wh[1];
ia = (struct ieee80211_action_ba_addbarequest *) frm;
if (ia->rq_header.ia_category != IEEE80211_ACTION_CAT_BA)
return 0;
if (ia->rq_header.ia_action != IEEE80211_ACTION_BA_ADDBA_REQUEST)
return 0;
baparamset = le16toh(ia->rq_baparamset);
*tid = (int) _IEEE80211_MASKSHIFT(baparamset, IEEE80211_BAPS_TID);
return 1;
}
void
ath_tx_addto_baw(struct ath_softc *sc, struct ath_node *an,
struct ath_tid *tid, struct ath_buf *bf)
{
int index, cindex;
struct ieee80211_tx_ampdu *tap;
ATH_TX_LOCK_ASSERT(sc);
if (bf->bf_state.bfs_isretried)
return;
tap = ath_tx_get_tx_tid(an, tid->tid);
if (! bf->bf_state.bfs_dobaw) {
DPRINTF(sc, ATH_DEBUG_SW_TX_BAW,
"%s: dobaw=0, seqno=%d, window %d:%d\n",
__func__, SEQNO(bf->bf_state.bfs_seqno),
tap->txa_start, tap->txa_wnd);
}
if (bf->bf_state.bfs_addedbaw)
DPRINTF(sc, ATH_DEBUG_SW_TX_BAW,
"%s: re-added? tid=%d, seqno %d; window %d:%d; "
"baw head=%d tail=%d\n",
__func__, tid->tid, SEQNO(bf->bf_state.bfs_seqno),
tap->txa_start, tap->txa_wnd, tid->baw_head,
tid->baw_tail);
if (! BAW_WITHIN(tap->txa_start, tap->txa_wnd,
SEQNO(bf->bf_state.bfs_seqno))) {
DPRINTF(sc, ATH_DEBUG_SW_TX_BAW,
"%s: bf=%p: outside of BAW?? tid=%d, seqno %d; window %d:%d; "
"baw head=%d tail=%d\n",
__func__, bf, tid->tid, SEQNO(bf->bf_state.bfs_seqno),
tap->txa_start, tap->txa_wnd, tid->baw_head,
tid->baw_tail);
}
index = ATH_BA_INDEX(tap->txa_start, SEQNO(bf->bf_state.bfs_seqno));
cindex = (tid->baw_head + index) & (ATH_TID_MAX_BUFS - 1);
DPRINTF(sc, ATH_DEBUG_SW_TX_BAW,
"%s: tid=%d, seqno %d; window %d:%d; index=%d cindex=%d "
"baw head=%d tail=%d\n",
__func__, tid->tid, SEQNO(bf->bf_state.bfs_seqno),
tap->txa_start, tap->txa_wnd, index, cindex, tid->baw_head,
tid->baw_tail);
#if 0
assert(tid->tx_buf[cindex] == NULL);
#endif
if (tid->tx_buf[cindex] != NULL) {
DPRINTF(sc, ATH_DEBUG_SW_TX_BAW,
"%s: ba packet dup (index=%d, cindex=%d, "
"head=%d, tail=%d)\n",
__func__, index, cindex, tid->baw_head, tid->baw_tail);
DPRINTF(sc, ATH_DEBUG_SW_TX_BAW,
"%s: BA bf: %p; seqno=%d ; new bf: %p; seqno=%d\n",
__func__,
tid->tx_buf[cindex],
SEQNO(tid->tx_buf[cindex]->bf_state.bfs_seqno),
bf,
SEQNO(bf->bf_state.bfs_seqno)
);
}
tid->tx_buf[cindex] = bf;
if (index >= ((tid->baw_tail - tid->baw_head) &
(ATH_TID_MAX_BUFS - 1))) {
tid->baw_tail = cindex;
INCR(tid->baw_tail, ATH_TID_MAX_BUFS);
}
}
static void
ath_tx_switch_baw_buf(struct ath_softc *sc, struct ath_node *an,
struct ath_tid *tid, struct ath_buf *old_bf, struct ath_buf *new_bf)
{
int index, cindex;
struct ieee80211_tx_ampdu *tap;
int seqno = SEQNO(old_bf->bf_state.bfs_seqno);
ATH_TX_LOCK_ASSERT(sc);
tap = ath_tx_get_tx_tid(an, tid->tid);
index = ATH_BA_INDEX(tap->txa_start, seqno);
cindex = (tid->baw_head + index) & (ATH_TID_MAX_BUFS - 1);
if (old_bf->bf_state.bfs_seqno != new_bf->bf_state.bfs_seqno) {
DPRINTF(sc, ATH_DEBUG_SW_TX_BAW,
"%s: retransmitted buffer"
" has mismatching seqno's, BA session may hang.\n",
__func__);
DPRINTF(sc, ATH_DEBUG_SW_TX_BAW,
"%s: old seqno=%d, new_seqno=%d\n", __func__,
old_bf->bf_state.bfs_seqno, new_bf->bf_state.bfs_seqno);
}
if (tid->tx_buf[cindex] != old_bf) {
DPRINTF(sc, ATH_DEBUG_SW_TX_BAW,
"%s: ath_buf pointer incorrect; "
" has m BA session may hang.\n", __func__);
DPRINTF(sc, ATH_DEBUG_SW_TX_BAW,
"%s: old bf=%p, new bf=%p\n", __func__, old_bf, new_bf);
}
tid->tx_buf[cindex] = new_bf;
}
static void
ath_tx_update_baw(struct ath_softc *sc, struct ath_node *an,
struct ath_tid *tid, const struct ath_buf *bf)
{
int index, cindex;
struct ieee80211_tx_ampdu *tap;
int seqno = SEQNO(bf->bf_state.bfs_seqno);
ATH_TX_LOCK_ASSERT(sc);
tap = ath_tx_get_tx_tid(an, tid->tid);
index = ATH_BA_INDEX(tap->txa_start, seqno);
cindex = (tid->baw_head + index) & (ATH_TID_MAX_BUFS - 1);
DPRINTF(sc, ATH_DEBUG_SW_TX_BAW,
"%s: tid=%d, baw=%d:%d, seqno=%d, index=%d, cindex=%d, "
"baw head=%d, tail=%d\n",
__func__, tid->tid, tap->txa_start, tap->txa_wnd, seqno, index,
cindex, tid->baw_head, tid->baw_tail);
if (tid->tx_buf[cindex] != bf) {
DPRINTF(sc, ATH_DEBUG_SW_TX_BAW,
"%s: comp bf=%p, seq=%d; slot bf=%p, seqno=%d\n",
__func__, bf, SEQNO(bf->bf_state.bfs_seqno),
tid->tx_buf[cindex],
(tid->tx_buf[cindex] != NULL) ?
SEQNO(tid->tx_buf[cindex]->bf_state.bfs_seqno) : -1);
}
tid->tx_buf[cindex] = NULL;
while (tid->baw_head != tid->baw_tail &&
!tid->tx_buf[tid->baw_head]) {
INCR(tap->txa_start, IEEE80211_SEQ_RANGE);
INCR(tid->baw_head, ATH_TID_MAX_BUFS);
}
DPRINTF(sc, ATH_DEBUG_SW_TX_BAW,
"%s: tid=%d: baw is now %d:%d, baw head=%d\n",
__func__, tid->tid, tap->txa_start, tap->txa_wnd, tid->baw_head);
}
static void
ath_tx_leak_count_update(struct ath_softc *sc, struct ath_tid *tid,
struct ath_buf *bf)
{
struct ieee80211_frame *wh;
ATH_TX_LOCK_ASSERT(sc);
if (tid->an->an_leak_count > 0) {
wh = mtod(bf->bf_m, struct ieee80211_frame *);
if ((tid->an->an_stack_psq > 0)
|| (tid->an->an_swq_depth > 0))
wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA;
else
wh->i_fc[1] &= ~IEEE80211_FC1_MORE_DATA;
DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE,
"%s: %6D: leak count = %d, psq=%d, swq=%d, MORE=%d\n",
__func__,
tid->an->an_node.ni_macaddr,
":",
tid->an->an_leak_count,
tid->an->an_stack_psq,
tid->an->an_swq_depth,
!! (wh->i_fc[1] & IEEE80211_FC1_MORE_DATA));
bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap,
BUS_DMASYNC_PREWRITE);
tid->an->an_leak_count --;
}
}
static int
ath_tx_tid_can_tx_or_sched(struct ath_softc *sc, struct ath_tid *tid)
{
ATH_TX_LOCK_ASSERT(sc);
if (tid->an->an_leak_count > 0) {
return (1);
}
if (tid->paused)
return (0);
return (1);
}
void
ath_tx_tid_sched(struct ath_softc *sc, struct ath_tid *tid)
{
struct ath_txq *txq = sc->sc_ac2q[tid->ac];
ATH_TX_LOCK_ASSERT(sc);
if (! ath_tx_tid_can_tx_or_sched(sc, tid))
return;
if (tid->sched)
return;
tid->sched = 1;
#if 0
if (tid->an->an_leak_count) {
TAILQ_INSERT_HEAD(&txq->axq_tidq, tid, axq_qelem);
} else {
TAILQ_INSERT_TAIL(&txq->axq_tidq, tid, axq_qelem);
}
#endif
TAILQ_INSERT_TAIL(&txq->axq_tidq, tid, axq_qelem);
}
static void
ath_tx_tid_unsched(struct ath_softc *sc, struct ath_tid *tid)
{
struct ath_txq *txq = sc->sc_ac2q[tid->ac];
ATH_TX_LOCK_ASSERT(sc);
if (tid->sched == 0)
return;
tid->sched = 0;
TAILQ_REMOVE(&txq->axq_tidq, tid, axq_qelem);
}
static ieee80211_seq
ath_tx_tid_seqno_assign(struct ath_softc *sc, struct ieee80211_node *ni,
struct ath_buf *bf, struct mbuf *m0)
{
struct ieee80211_frame *wh;
int tid;
ieee80211_seq seqno;
uint8_t subtype;
wh = mtod(m0, struct ieee80211_frame *);
tid = ieee80211_gettid(wh);
DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: tid=%d, qos has seq=%d\n",
__func__, tid, IEEE80211_QOS_HAS_SEQ(wh));
if (! IEEE80211_QOS_HAS_SEQ(wh))
return -1;
ATH_TX_LOCK_ASSERT(sc);
subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
if (IEEE80211_IS_QOS_NULL(wh)) {
seqno = ni->ni_txseqs[IEEE80211_NONQOS_TID];
INCR(ni->ni_txseqs[IEEE80211_NONQOS_TID], IEEE80211_SEQ_RANGE);
} else if (IEEE80211_IS_MULTICAST(wh->i_addr1)) {
seqno = ni->ni_txseqs[IEEE80211_NONQOS_TID];
INCR(ni->ni_txseqs[IEEE80211_NONQOS_TID], IEEE80211_SEQ_RANGE);
} else {
seqno = ni->ni_txseqs[tid];
INCR(ni->ni_txseqs[tid], IEEE80211_SEQ_RANGE);
}
*(uint16_t *)&wh->i_seq[0] = htole16(seqno << IEEE80211_SEQ_SEQ_SHIFT);
M_SEQNO_SET(m0, seqno);
DPRINTF(sc, ATH_DEBUG_SW_TX,
"%s: -> subtype=0x%x, tid=%d, seqno=%d\n",
__func__, subtype, tid, seqno);
return seqno;
}
static void
ath_tx_xmit_aggr(struct ath_softc *sc, struct ath_node *an,
struct ath_txq *txq, struct ath_buf *bf)
{
struct ath_tid *tid = &an->an_tid[bf->bf_state.bfs_tid];
struct ieee80211_tx_ampdu *tap;
ATH_TX_LOCK_ASSERT(sc);
tap = ath_tx_get_tx_tid(an, tid->tid);
if (! ath_tx_tid_can_tx_or_sched(sc, tid)) {
ATH_TID_INSERT_HEAD(tid, bf, bf_list);
return;
}
if (bf->bf_state.bfs_dobaw &&
(! BAW_WITHIN(tap->txa_start, tap->txa_wnd,
SEQNO(bf->bf_state.bfs_seqno)))) {
ATH_TID_INSERT_HEAD(tid, bf, bf_list);
ath_tx_tid_sched(sc, tid);
return;
}
if (bf->bf_state.bfs_aggr != 0 || bf->bf_state.bfs_nframes > 1) {
DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR,
"%s: bfs_aggr=%d, bfs_nframes=%d\n", __func__,
bf->bf_state.bfs_aggr, bf->bf_state.bfs_nframes);
bf->bf_state.bfs_aggr = 0;
bf->bf_state.bfs_nframes = 1;
}
ath_tx_update_clrdmask(sc, tid, bf);
ath_tx_do_ratelookup(sc, bf, tid->tid, bf->bf_state.bfs_pktlen,
false);
ath_tx_calc_duration(sc, bf);
ath_tx_calc_protection(sc, bf);
ath_tx_set_rtscts(sc, bf);
ath_tx_rate_fill_rcflags(sc, bf);
ath_tx_setds(sc, bf);
sc->sc_aggr_stats.aggr_low_hwq_single_pkt++;
tid->hwq_depth++;
if (bf->bf_state.bfs_dobaw) {
ath_tx_addto_baw(sc, an, tid, bf);
bf->bf_state.bfs_addedbaw = 1;
}
bf->bf_comp = ath_tx_aggr_comp;
ath_tx_leak_count_update(sc, tid, bf);
ath_tx_handoff(sc, txq, bf);
}
void
ath_tx_swq(struct ath_softc *sc, struct ieee80211_node *ni,
struct ath_txq *txq, int queue_to_head, struct ath_buf *bf)
{
struct ath_node *an = ATH_NODE(ni);
struct ieee80211_frame *wh;
struct ath_tid *atid;
int pri, tid;
struct mbuf *m0 = bf->bf_m;
ATH_TX_LOCK_ASSERT(sc);
wh = mtod(m0, struct ieee80211_frame *);
pri = ath_tx_getac(sc, m0);
tid = ath_tx_gettid(sc, m0);
atid = &an->an_tid[tid];
DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: bf=%p, pri=%d, tid=%d, qos=%d\n",
__func__, bf, pri, tid, IEEE80211_QOS_HAS_SEQ(wh));
bf->bf_state.bfs_tid = tid;
bf->bf_state.bfs_tx_queue = txq->axq_qnum;
bf->bf_state.bfs_pri = pri;
if (! ath_tx_tid_can_tx_or_sched(sc, atid)) {
DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: paused\n", __func__);
if (queue_to_head)
ATH_TID_INSERT_HEAD(atid, bf, bf_list);
else
ATH_TID_INSERT_TAIL(atid, bf, bf_list);
} else if (ath_tx_ampdu_pending(sc, an, tid)) {
DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: pending\n", __func__);
ATH_TID_INSERT_TAIL(atid, bf, bf_list);
} else if (ath_tx_ampdu_running(sc, an, tid)) {
ATH_TID_INSERT_TAIL(atid, bf, bf_list);
if (txq->axq_depth + txq->fifo.axq_depth == 0) {
bf = ATH_TID_FIRST(atid);
ATH_TID_REMOVE(atid, bf, bf_list);
bf->bf_state.bfs_aggr = 0;
bf->bf_state.bfs_nframes = 1;
ath_tx_xmit_aggr(sc, an, txq, bf);
DPRINTF(sc, ATH_DEBUG_SW_TX,
"%s: xmit_aggr\n",
__func__);
} else {
DPRINTF(sc, ATH_DEBUG_SW_TX,
"%s: ampdu; swq'ing\n",
__func__);
ath_tx_tid_sched(sc, atid);
}
} else if ((txq->axq_depth + txq->fifo.axq_depth < sc->sc_hwq_limit_nonaggr) &&
(txq->axq_aggr_depth < sc->sc_hwq_limit_aggr)) {
DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: xmit_normal\n", __func__);
ath_tx_update_clrdmask(sc, atid, bf);
ath_tx_leak_count_update(sc, atid, bf);
ath_tx_xmit_normal(sc, txq, bf);
} else {
DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: swq'ing\n", __func__);
ATH_TID_INSERT_TAIL(atid, bf, bf_list);
ath_tx_tid_sched(sc, atid);
}
}
static void
ath_tx_set_clrdmask(struct ath_softc *sc, struct ath_node *an)
{
int i;
ATH_TX_LOCK_ASSERT(sc);
for (i = 0; i < IEEE80211_TID_SIZE; i++) {
if (an->an_tid[i].isfiltered == 1)
return;
}
an->clrdmask = 1;
}
void
ath_tx_tid_init(struct ath_softc *sc, struct ath_node *an)
{
int i, j;
struct ath_tid *atid;
for (i = 0; i < IEEE80211_TID_SIZE; i++) {
atid = &an->an_tid[i];
bzero(atid, sizeof(*atid));
TAILQ_INIT(&atid->tid_q);
TAILQ_INIT(&atid->filtq.tid_q);
atid->tid = i;
atid->an = an;
for (j = 0; j < ATH_TID_MAX_BUFS; j++)
atid->tx_buf[j] = NULL;
atid->baw_head = atid->baw_tail = 0;
atid->paused = 0;
atid->sched = 0;
atid->hwq_depth = 0;
atid->cleanup_inprogress = 0;
if (i == IEEE80211_NONQOS_TID)
atid->ac = ATH_NONQOS_TID_AC;
else
atid->ac = TID_TO_WME_AC(i);
}
an->clrdmask = 1;
}
static void
ath_tx_tid_pause(struct ath_softc *sc, struct ath_tid *tid)
{
ATH_TX_LOCK_ASSERT(sc);
tid->paused++;
DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL, "%s: [%6D]: tid=%d, paused = %d\n",
__func__,
tid->an->an_node.ni_macaddr, ":",
tid->tid,
tid->paused);
}
static void
ath_tx_tid_resume(struct ath_softc *sc, struct ath_tid *tid)
{
ATH_TX_LOCK_ASSERT(sc);
if (tid->paused == 0) {
device_printf(sc->sc_dev,
"%s: [%6D]: tid=%d, paused=0?\n",
__func__,
tid->an->an_node.ni_macaddr, ":",
tid->tid);
} else {
tid->paused--;
}
DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL,
"%s: [%6D]: tid=%d, unpaused = %d\n",
__func__,
tid->an->an_node.ni_macaddr, ":",
tid->tid,
tid->paused);
if (tid->paused)
return;
ath_tx_set_clrdmask(sc, tid->an);
if (tid->axq_depth == 0)
return;
if (tid->isfiltered == 1) {
DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL, "%s: filtered?!\n",
__func__);
return;
}
ath_tx_tid_sched(sc, tid);
ath_tx_swq_kick(sc);
}
static void
ath_tx_tid_filt_addbuf(struct ath_softc *sc, struct ath_tid *tid,
struct ath_buf *bf)
{
ATH_TX_LOCK_ASSERT(sc);
if (!tid->isfiltered)
DPRINTF(sc, ATH_DEBUG_SW_TX_FILT, "%s: not filtered?!\n",
__func__);
DPRINTF(sc, ATH_DEBUG_SW_TX_FILT, "%s: bf=%p\n", __func__, bf);
ath_tx_set_retry(sc, bf);
sc->sc_stats.ast_tx_swfiltered++;
ATH_TID_FILT_INSERT_TAIL(tid, bf, bf_list);
}
static void
ath_tx_tid_filt_comp_buf(struct ath_softc *sc, struct ath_tid *tid,
struct ath_buf *bf)
{
ATH_TX_LOCK_ASSERT(sc);
if (! tid->isfiltered) {
DPRINTF(sc, ATH_DEBUG_SW_TX_FILT, "%s: tid=%d; filter transition\n",
__func__, tid->tid);
tid->isfiltered = 1;
ath_tx_tid_pause(sc, tid);
}
ath_tx_tid_filt_addbuf(sc, tid, bf);
}
static void
ath_tx_tid_filt_comp_complete(struct ath_softc *sc, struct ath_tid *tid)
{
struct ath_buf *bf;
int do_resume = 0;
ATH_TX_LOCK_ASSERT(sc);
if (tid->hwq_depth != 0)
return;
DPRINTF(sc, ATH_DEBUG_SW_TX_FILT, "%s: tid=%d, hwq=0, transition back\n",
__func__, tid->tid);
if (tid->isfiltered == 1) {
tid->isfiltered = 0;
do_resume = 1;
}
ath_tx_set_clrdmask(sc, tid->an);
while ((bf = ATH_TID_FILT_LAST(tid, ath_bufhead_s)) != NULL) {
ATH_TID_FILT_REMOVE(tid, bf, bf_list);
ATH_TID_INSERT_HEAD(tid, bf, bf_list);
}
if (do_resume)
ath_tx_tid_resume(sc, tid);
}
static int
ath_tx_tid_filt_comp_single(struct ath_softc *sc, struct ath_tid *tid,
struct ath_buf *bf)
{
struct ath_buf *nbf;
int retval;
ATH_TX_LOCK_ASSERT(sc);
if (bf->bf_state.bfs_retries > SWMAX_RETRIES) {
sc->sc_stats.ast_tx_swretrymax++;
DPRINTF(sc, ATH_DEBUG_SW_TX_FILT,
"%s: bf=%p, seqno=%d, exceeded retries\n",
__func__,
bf,
SEQNO(bf->bf_state.bfs_seqno));
retval = 1;
goto finish;
}
if (bf->bf_flags & ATH_BUF_BUSY) {
nbf = ath_tx_retry_clone(sc, tid->an, tid, bf);
DPRINTF(sc, ATH_DEBUG_SW_TX_FILT,
"%s: busy buffer clone: %p -> %p\n",
__func__, bf, nbf);
} else {
nbf = bf;
}
if (nbf == NULL) {
DPRINTF(sc, ATH_DEBUG_SW_TX_FILT,
"%s: busy buffer couldn't be cloned (%p)!\n",
__func__, bf);
retval = 1;
} else {
ath_tx_tid_filt_comp_buf(sc, tid, nbf);
retval = 0;
}
finish:
ath_tx_tid_filt_comp_complete(sc, tid);
return (retval);
}
static void
ath_tx_tid_filt_comp_aggr(struct ath_softc *sc, struct ath_tid *tid,
struct ath_buf *bf_first, ath_bufhead *bf_q)
{
struct ath_buf *bf, *bf_next, *nbf;
ATH_TX_LOCK_ASSERT(sc);
bf = bf_first;
while (bf) {
bf_next = bf->bf_next;
bf->bf_next = NULL;
if (bf->bf_state.bfs_retries > SWMAX_RETRIES) {
sc->sc_stats.ast_tx_swretrymax++;
DPRINTF(sc, ATH_DEBUG_SW_TX_FILT,
"%s: tid=%d, bf=%p, seqno=%d, exceeded retries\n",
__func__,
tid->tid,
bf,
SEQNO(bf->bf_state.bfs_seqno));
TAILQ_INSERT_TAIL(bf_q, bf, bf_list);
goto next;
}
if (bf->bf_flags & ATH_BUF_BUSY) {
nbf = ath_tx_retry_clone(sc, tid->an, tid, bf);
DPRINTF(sc, ATH_DEBUG_SW_TX_FILT,
"%s: tid=%d, busy buffer cloned: %p -> %p, seqno=%d\n",
__func__, tid->tid, bf, nbf, SEQNO(bf->bf_state.bfs_seqno));
} else {
nbf = bf;
}
if (nbf == NULL) {
DPRINTF(sc, ATH_DEBUG_SW_TX_FILT,
"%s: tid=%d, buffer couldn't be cloned! (%p) seqno=%d\n",
__func__, tid->tid, bf, SEQNO(bf->bf_state.bfs_seqno));
TAILQ_INSERT_TAIL(bf_q, bf, bf_list);
} else {
ath_tx_tid_filt_comp_buf(sc, tid, nbf);
}
next:
bf = bf_next;
}
ath_tx_tid_filt_comp_complete(sc, tid);
}
static void
ath_tx_tid_bar_suspend(struct ath_softc *sc, struct ath_tid *tid)
{
ATH_TX_LOCK_ASSERT(sc);
DPRINTF(sc, ATH_DEBUG_SW_TX_BAR,
"%s: tid=%d, bar_wait=%d, bar_tx=%d, called\n",
__func__,
tid->tid,
tid->bar_wait,
tid->bar_tx);
if (tid->bar_tx) {
DPRINTF(sc, ATH_DEBUG_SW_TX_BAR,
"%s: bar_tx is 1?!\n", __func__);
}
if (tid->bar_wait)
return;
tid->bar_wait = 1;
ath_tx_tid_pause(sc, tid);
}
static void
ath_tx_tid_bar_unsuspend(struct ath_softc *sc, struct ath_tid *tid)
{
ATH_TX_LOCK_ASSERT(sc);
DPRINTF(sc, ATH_DEBUG_SW_TX_BAR,
"%s: %6D: TID=%d, called\n",
__func__,
tid->an->an_node.ni_macaddr,
":",
tid->tid);
if (tid->bar_tx == 0 || tid->bar_wait == 0) {
DPRINTF(sc, ATH_DEBUG_SW_TX_BAR,
"%s: %6D: TID=%d, bar_tx=%d, bar_wait=%d: ?\n",
__func__, tid->an->an_node.ni_macaddr, ":",
tid->tid, tid->bar_tx, tid->bar_wait);
}
tid->bar_tx = tid->bar_wait = 0;
ath_tx_tid_resume(sc, tid);
}
static int
ath_tx_tid_bar_tx_ready(struct ath_softc *sc, struct ath_tid *tid)
{
ATH_TX_LOCK_ASSERT(sc);
if (tid->bar_wait == 0 || tid->hwq_depth > 0)
return (0);
DPRINTF(sc, ATH_DEBUG_SW_TX_BAR,
"%s: %6D: TID=%d, bar ready\n",
__func__,
tid->an->an_node.ni_macaddr,
":",
tid->tid);
return (1);
}
static void
ath_tx_tid_bar_tx(struct ath_softc *sc, struct ath_tid *tid)
{
struct ieee80211_tx_ampdu *tap;
ATH_TX_LOCK_ASSERT(sc);
DPRINTF(sc, ATH_DEBUG_SW_TX_BAR,
"%s: %6D: TID=%d, called\n",
__func__,
tid->an->an_node.ni_macaddr,
":",
tid->tid);
tap = ath_tx_get_tx_tid(tid->an, tid->tid);
if (tid->bar_wait == 0 || tid->bar_tx == 1) {
DPRINTF(sc, ATH_DEBUG_SW_TX_BAR,
"%s: %6D: TID=%d, bar_tx=%d, bar_wait=%d: ?\n",
__func__, tid->an->an_node.ni_macaddr, ":",
tid->tid, tid->bar_tx, tid->bar_wait);
return;
}
if (tid->hwq_depth > 0) {
DPRINTF(sc, ATH_DEBUG_SW_TX_BAR,
"%s: %6D: TID=%d, hwq_depth=%d, waiting\n",
__func__,
tid->an->an_node.ni_macaddr,
":",
tid->tid,
tid->hwq_depth);
return;
}
tid->bar_tx = 1;
ath_tx_set_clrdmask(sc, tid->an);
DPRINTF(sc, ATH_DEBUG_SW_TX_BAR,
"%s: %6D: TID=%d, new BAW left edge=%d\n",
__func__,
tid->an->an_node.ni_macaddr,
":",
tid->tid,
tap->txa_start);
ATH_TX_UNLOCK(sc);
if (ieee80211_send_bar(&tid->an->an_node, tap, tap->txa_start) == 0) {
ATH_TX_LOCK(sc);
return;
}
ATH_TX_LOCK(sc);
DPRINTF(sc, ATH_DEBUG_SW_TX_BAR,
"%s: %6D: TID=%d, failed to TX BAR, continue!\n",
__func__, tid->an->an_node.ni_macaddr, ":",
tid->tid);
ath_tx_tid_bar_unsuspend(sc, tid);
}
static void
ath_tx_tid_drain_pkt(struct ath_softc *sc, struct ath_node *an,
struct ath_tid *tid, ath_bufhead *bf_cq, struct ath_buf *bf)
{
ATH_TX_LOCK_ASSERT(sc);
if (ath_tx_ampdu_running(sc, an, tid->tid) &&
bf->bf_state.bfs_dobaw) {
if (bf->bf_state.bfs_retries > 0) {
ath_tx_update_baw(sc, an, tid, bf);
bf->bf_state.bfs_dobaw = 0;
}
#if 0
if (! bf->bf_state.bfs_addedbaw)
DPRINTF(sc, ATH_DEBUG_SW_TX_BAW
"%s: wasn't added: seqno %d\n",
__func__, SEQNO(bf->bf_state.bfs_seqno));
#endif
}
bf->bf_next = NULL;
TAILQ_INSERT_TAIL(bf_cq, bf, bf_list);
}
static void
ath_tx_tid_drain_print(struct ath_softc *sc, struct ath_node *an,
const char *pfx, struct ath_tid *tid, struct ath_buf *bf)
{
struct ieee80211_node *ni = &an->an_node;
struct ath_txq *txq;
struct ieee80211_tx_ampdu *tap;
txq = sc->sc_ac2q[tid->ac];
tap = ath_tx_get_tx_tid(an, tid->tid);
DPRINTF(sc, ATH_DEBUG_SW_TX | ATH_DEBUG_RESET,
"%s: %s: %6D: bf=%p: addbaw=%d, dobaw=%d, "
"seqno=%d, retry=%d\n",
__func__,
pfx,
ni->ni_macaddr,
":",
bf,
bf->bf_state.bfs_addedbaw,
bf->bf_state.bfs_dobaw,
SEQNO(bf->bf_state.bfs_seqno),
bf->bf_state.bfs_retries);
DPRINTF(sc, ATH_DEBUG_SW_TX | ATH_DEBUG_RESET,
"%s: %s: %6D: bf=%p: txq[%d] axq_depth=%d, axq_aggr_depth=%d\n",
__func__,
pfx,
ni->ni_macaddr,
":",
bf,
txq->axq_qnum,
txq->axq_depth,
txq->axq_aggr_depth);
DPRINTF(sc, ATH_DEBUG_SW_TX | ATH_DEBUG_RESET,
"%s: %s: %6D: bf=%p: tid txq_depth=%d hwq_depth=%d, bar_wait=%d, "
"isfiltered=%d\n",
__func__,
pfx,
ni->ni_macaddr,
":",
bf,
tid->axq_depth,
tid->hwq_depth,
tid->bar_wait,
tid->isfiltered);
DPRINTF(sc, ATH_DEBUG_SW_TX | ATH_DEBUG_RESET,
"%s: %s: %6D: tid %d: "
"sched=%d, paused=%d, "
"incomp=%d, baw_head=%d, "
"baw_tail=%d txa_start=%d, ni_txseqs=%d\n",
__func__,
pfx,
ni->ni_macaddr,
":",
tid->tid,
tid->sched, tid->paused,
tid->incomp, tid->baw_head,
tid->baw_tail, tap == NULL ? -1 : tap->txa_start,
ni->ni_txseqs[tid->tid]);
if (IFF_DUMPPKTS(sc, ATH_DEBUG_XMIT))
ieee80211_dump_pkt(ni->ni_ic,
mtod(bf->bf_m, const uint8_t *),
bf->bf_m->m_len, 0, -1);
}
static void
ath_tx_tid_drain(struct ath_softc *sc, struct ath_node *an,
struct ath_tid *tid, ath_bufhead *bf_cq)
{
struct ath_buf *bf;
struct ieee80211_tx_ampdu *tap;
struct ieee80211_node *ni = &an->an_node;
int t;
tap = ath_tx_get_tx_tid(an, tid->tid);
ATH_TX_LOCK_ASSERT(sc);
t = 0;
for (;;) {
bf = ATH_TID_FIRST(tid);
if (bf == NULL) {
break;
}
if (t == 0) {
ath_tx_tid_drain_print(sc, an, "norm", tid, bf);
}
ATH_TID_REMOVE(tid, bf, bf_list);
ath_tx_tid_drain_pkt(sc, an, tid, bf_cq, bf);
}
t = 0;
for (;;) {
bf = ATH_TID_FILT_FIRST(tid);
if (bf == NULL)
break;
if (t == 0) {
ath_tx_tid_drain_print(sc, an, "filt", tid, bf);
}
ATH_TID_FILT_REMOVE(tid, bf, bf_list);
ath_tx_tid_drain_pkt(sc, an, tid, bf_cq, bf);
}
ath_tx_set_clrdmask(sc, tid->an);
if (tap) {
#if 1
DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL,
"%s: %6D: node %p: TID %d: sliding BAW left edge to %d\n",
__func__,
ni->ni_macaddr,
":",
an,
tid->tid,
tap->txa_start);
#endif
ni->ni_txseqs[tid->tid] = tap->txa_start;
tid->baw_tail = tid->baw_head;
}
}
static void
ath_tx_tid_reset(struct ath_softc *sc, struct ath_tid *tid)
{
#if 0
tid->bar_wait = tid->bar_tx = tid->isfiltered = 0;
tid->paused = tid->sched = tid->addba_tx_pending = 0;
tid->incomp = tid->cleanup_inprogress = 0;
#endif
if (tid->bar_wait) {
if (tid->paused > 0) {
tid->paused --;
}
}
if (tid->isfiltered) {
if (tid->paused > 0) {
tid->paused --;
}
}
tid->bar_wait = 0;
tid->bar_tx = 0;
tid->isfiltered = 0;
tid->sched = 0;
tid->addba_tx_pending = 0;
}
void
ath_tx_node_flush(struct ath_softc *sc, struct ath_node *an)
{
int tid;
ath_bufhead bf_cq;
struct ath_buf *bf;
TAILQ_INIT(&bf_cq);
ATH_KTR(sc, ATH_KTR_NODE, 1, "ath_tx_node_flush: flush node; ni=%p",
&an->an_node);
ATH_TX_LOCK(sc);
DPRINTF(sc, ATH_DEBUG_NODE,
"%s: %6D: flush; is_powersave=%d, stack_psq=%d, tim=%d, "
"swq_depth=%d, clrdmask=%d, leak_count=%d\n",
__func__,
an->an_node.ni_macaddr,
":",
an->an_is_powersave,
an->an_stack_psq,
an->an_tim_set,
an->an_swq_depth,
an->clrdmask,
an->an_leak_count);
for (tid = 0; tid < IEEE80211_TID_SIZE; tid++) {
struct ath_tid *atid = &an->an_tid[tid];
ath_tx_tid_drain(sc, an, atid, &bf_cq);
ath_tx_tid_unsched(sc, atid);
ath_tx_tid_reset(sc, atid);
}
an->an_leak_count = 0;
ATH_TX_UNLOCK(sc);
while ((bf = TAILQ_FIRST(&bf_cq)) != NULL) {
TAILQ_REMOVE(&bf_cq, bf, bf_list);
ath_tx_default_comp(sc, bf, 0);
}
}
void
ath_tx_txq_drain(struct ath_softc *sc, struct ath_txq *txq)
{
struct ath_tid *tid;
ath_bufhead bf_cq;
struct ath_buf *bf;
TAILQ_INIT(&bf_cq);
ATH_TX_LOCK(sc);
while (! TAILQ_EMPTY(&txq->axq_tidq)) {
tid = TAILQ_FIRST(&txq->axq_tidq);
ath_tx_tid_drain(sc, tid->an, tid, &bf_cq);
ath_tx_tid_unsched(sc, tid);
}
ATH_TX_UNLOCK(sc);
while ((bf = TAILQ_FIRST(&bf_cq)) != NULL) {
TAILQ_REMOVE(&bf_cq, bf, bf_list);
ath_tx_default_comp(sc, bf, 0);
}
}
void
ath_tx_normal_comp(struct ath_softc *sc, struct ath_buf *bf, int fail)
{
struct ieee80211_node *ni = bf->bf_node;
struct ath_node *an = ATH_NODE(ni);
int tid = bf->bf_state.bfs_tid;
struct ath_tid *atid = &an->an_tid[tid];
struct ath_tx_status *ts = &bf->bf_status.ds_txstat;
ATH_TX_LOCK(sc);
DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: bf=%p: fail=%d, hwq_depth now %d\n",
__func__, bf, fail, atid->hwq_depth - 1);
atid->hwq_depth--;
#if 0
if ((ts->ts_status & HAL_TXERR_FILT) ||
(ts->ts_status != 0 && atid->isfiltered)) {
DPRINTF(sc, ATH_DEBUG_SW_TX,
"%s: isfiltered=%d, ts_status=%d: huh?\n",
__func__,
atid->isfiltered,
ts->ts_status);
ath_tx_tid_filt_comp_buf(sc, atid, bf);
}
#endif
if (atid->isfiltered)
DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: filtered?!\n", __func__);
if (atid->hwq_depth < 0)
DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: hwq_depth < 0: %d\n",
__func__, atid->hwq_depth);
if (atid->cleanup_inprogress) {
atid->incomp--;
if (atid->incomp == 0) {
DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL,
"%s: TID %d: cleaned up! resume!\n",
__func__, tid);
atid->cleanup_inprogress = 0;
ath_tx_tid_resume(sc, atid);
}
}
if (atid->isfiltered)
ath_tx_tid_filt_comp_complete(sc, atid);
ATH_TX_UNLOCK(sc);
if (fail == 0 && ((bf->bf_state.bfs_txflags & HAL_TXDESC_NOACK) == 0))
ath_tx_update_ratectrl(sc, ni, bf->bf_state.bfs_rc,
ts,
bf->bf_state.bfs_pktlen,
bf->bf_state.bfs_pktlen,
1, (ts->ts_status == 0) ? 0 : 1);
ath_tx_default_comp(sc, bf, fail);
}
static void
ath_tx_comp_cleanup_unaggr(struct ath_softc *sc, struct ath_buf *bf)
{
struct ieee80211_node *ni = bf->bf_node;
struct ath_node *an = ATH_NODE(ni);
int tid = bf->bf_state.bfs_tid;
struct ath_tid *atid = &an->an_tid[tid];
DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL, "%s: TID %d: incomp=%d\n",
__func__, tid, atid->incomp);
ATH_TX_LOCK(sc);
atid->incomp--;
if (bf->bf_state.bfs_dobaw) {
ath_tx_update_baw(sc, an, atid, bf);
if (!bf->bf_state.bfs_addedbaw)
DPRINTF(sc, ATH_DEBUG_SW_TX,
"%s: wasn't added: seqno %d\n",
__func__, SEQNO(bf->bf_state.bfs_seqno));
}
if (atid->incomp == 0) {
DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL,
"%s: TID %d: cleaned up! resume!\n",
__func__, tid);
atid->cleanup_inprogress = 0;
ath_tx_tid_resume(sc, atid);
}
ATH_TX_UNLOCK(sc);
ath_tx_default_comp(sc, bf, 0);
}
static void
ath_tx_tid_cleanup_frame(struct ath_softc *sc, struct ath_node *an,
int tid, struct ath_buf *bf_head, ath_bufhead *bf_cq)
{
struct ath_tid *atid = &an->an_tid[tid];
struct ath_buf *bf, *bf_next;
ATH_TX_LOCK_ASSERT(sc);
ATH_TID_REMOVE(atid, bf_head, bf_list);
bf = bf_head;
while (bf != NULL) {
bf_next = bf->bf_next;
if (bf->bf_state.bfs_addedbaw) {
ath_tx_update_baw(sc, an, atid, bf);
bf->bf_state.bfs_dobaw = 0;
}
bf->bf_comp = ath_tx_normal_comp;
bf->bf_next = NULL;
TAILQ_INSERT_TAIL(bf_cq, bf, bf_list);
bf = bf_next;
}
}
static void
ath_tx_tid_cleanup(struct ath_softc *sc, struct ath_node *an, int tid,
ath_bufhead *bf_cq)
{
struct ath_tid *atid = &an->an_tid[tid];
struct ath_buf *bf, *bf_next;
ATH_TX_LOCK_ASSERT(sc);
DPRINTF(sc, ATH_DEBUG_SW_TX_BAW,
"%s: TID %d: called; inprogress=%d\n", __func__, tid,
atid->cleanup_inprogress);
while ((bf = ATH_TID_FILT_LAST(atid, ath_bufhead_s)) != NULL) {
ATH_TID_FILT_REMOVE(atid, bf, bf_list);
ATH_TID_INSERT_HEAD(atid, bf, bf_list);
}
bf = ATH_TID_FIRST(atid);
while (bf) {
bf_next = TAILQ_NEXT(bf, bf_list);
ath_tx_tid_cleanup_frame(sc, an, tid, bf, bf_cq);
bf = bf_next;
}
if (atid->hwq_depth > 0) {
atid->incomp = atid->hwq_depth;
atid->cleanup_inprogress = 1;
}
if (atid->cleanup_inprogress)
DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL,
"%s: TID %d: cleanup needed: %d packets\n",
__func__, tid, atid->incomp);
}
static struct ath_buf *
ath_tx_retry_clone(struct ath_softc *sc, struct ath_node *an,
struct ath_tid *tid, struct ath_buf *bf)
{
struct ath_buf *nbf;
int error;
nbf = ath_buf_clone(sc, bf);
#if 0
DPRINTF(sc, ATH_DEBUG_XMIT, "%s: ATH_BUF_BUSY; cloning\n",
__func__);
#endif
if (nbf == NULL) {
DPRINTF(sc, ATH_DEBUG_XMIT,
"%s: failed to clone a busy buffer\n",
__func__);
return NULL;
}
error = ath_tx_dmasetup(sc, nbf, nbf->bf_m);
if (error != 0) {
DPRINTF(sc, ATH_DEBUG_XMIT,
"%s: failed to setup dma for clone\n",
__func__);
ATH_TXBUF_LOCK(sc);
ath_returnbuf_head(sc, nbf);
ATH_TXBUF_UNLOCK(sc);
return NULL;
}
if (bf->bf_state.bfs_dobaw)
ath_tx_switch_baw_buf(sc, an, tid, bf, nbf);
ath_freebuf(sc, bf);
return nbf;
}
static void
ath_tx_aggr_retry_unaggr(struct ath_softc *sc, struct ath_buf *bf)
{
struct ieee80211_node *ni = bf->bf_node;
struct ath_node *an = ATH_NODE(ni);
int tid = bf->bf_state.bfs_tid;
struct ath_tid *atid = &an->an_tid[tid];
struct ieee80211_tx_ampdu *tap;
ATH_TX_LOCK(sc);
tap = ath_tx_get_tx_tid(an, tid);
if ((bf->bf_state.bfs_retries < SWMAX_RETRIES) &&
(bf->bf_flags & ATH_BUF_BUSY)) {
struct ath_buf *nbf;
nbf = ath_tx_retry_clone(sc, an, atid, bf);
if (nbf)
bf = nbf;
else
bf->bf_state.bfs_retries = SWMAX_RETRIES + 1;
}
if (bf->bf_state.bfs_retries >= SWMAX_RETRIES) {
DPRINTF(sc, ATH_DEBUG_SW_TX_RETRIES,
"%s: exceeded retries; seqno %d\n",
__func__, SEQNO(bf->bf_state.bfs_seqno));
sc->sc_stats.ast_tx_swretrymax++;
if (bf->bf_state.bfs_dobaw) {
ath_tx_update_baw(sc, an, atid, bf);
if (! bf->bf_state.bfs_addedbaw)
DPRINTF(sc, ATH_DEBUG_SW_TX_BAW,
"%s: wasn't added: seqno %d\n",
__func__, SEQNO(bf->bf_state.bfs_seqno));
}
bf->bf_state.bfs_dobaw = 0;
ath_tx_tid_bar_suspend(sc, atid);
if (ath_tx_tid_bar_tx_ready(sc, atid))
ath_tx_tid_bar_tx(sc, atid);
ATH_TX_UNLOCK(sc);
ath_tx_default_comp(sc, bf, 0);
return;
}
ath_tx_set_retry(sc, bf);
sc->sc_stats.ast_tx_swretries++;
ATH_TID_INSERT_HEAD(atid, bf, bf_list);
ath_tx_tid_sched(sc, atid);
if (ath_tx_tid_bar_tx_ready(sc, atid))
ath_tx_tid_bar_tx(sc, atid);
ATH_TX_UNLOCK(sc);
}
static int
ath_tx_retry_subframe(struct ath_softc *sc, struct ath_buf *bf,
ath_bufhead *bf_q)
{
struct ieee80211_node *ni = bf->bf_node;
struct ath_node *an = ATH_NODE(ni);
int tid = bf->bf_state.bfs_tid;
struct ath_tid *atid = &an->an_tid[tid];
ATH_TX_LOCK_ASSERT(sc);
ath_hal_clr11n_aggr(sc->sc_ah, bf->bf_desc);
ath_hal_set11nburstduration(sc->sc_ah, bf->bf_desc, 0);
if ((bf->bf_state.bfs_retries < SWMAX_RETRIES) &&
(bf->bf_flags & ATH_BUF_BUSY)) {
struct ath_buf *nbf;
nbf = ath_tx_retry_clone(sc, an, atid, bf);
if (nbf)
bf = nbf;
else
bf->bf_state.bfs_retries = SWMAX_RETRIES + 1;
}
if (bf->bf_state.bfs_retries >= SWMAX_RETRIES) {
sc->sc_stats.ast_tx_swretrymax++;
DPRINTF(sc, ATH_DEBUG_SW_TX_RETRIES,
"%s: max retries: seqno %d\n",
__func__, SEQNO(bf->bf_state.bfs_seqno));
ath_tx_update_baw(sc, an, atid, bf);
if (!bf->bf_state.bfs_addedbaw)
DPRINTF(sc, ATH_DEBUG_SW_TX_BAW,
"%s: wasn't added: seqno %d\n",
__func__, SEQNO(bf->bf_state.bfs_seqno));
bf->bf_state.bfs_dobaw = 0;
return 1;
}
ath_tx_set_retry(sc, bf);
sc->sc_stats.ast_tx_swretries++;
bf->bf_next = NULL;
bf->bf_state.bfs_aggr = 0;
bf->bf_state.bfs_ndelim = 0;
bf->bf_state.bfs_nframes = 1;
TAILQ_INSERT_TAIL(bf_q, bf, bf_list);
return 0;
}
static void
ath_tx_comp_aggr_error(struct ath_softc *sc, struct ath_buf *bf_first,
struct ath_tid *tid)
{
struct ieee80211_node *ni = bf_first->bf_node;
struct ath_node *an = ATH_NODE(ni);
struct ath_buf *bf_next, *bf;
ath_bufhead bf_q;
int drops = 0;
struct ieee80211_tx_ampdu *tap;
ath_bufhead bf_cq;
TAILQ_INIT(&bf_q);
TAILQ_INIT(&bf_cq);
ath_tx_update_ratectrl(sc, ni, bf_first->bf_state.bfs_rc,
&bf_first->bf_status.ds_txstat,
bf_first->bf_state.bfs_al,
bf_first->bf_state.bfs_rc_maxpktlen,
bf_first->bf_state.bfs_nframes, bf_first->bf_state.bfs_nframes);
ATH_TX_LOCK(sc);
tap = ath_tx_get_tx_tid(an, tid->tid);
sc->sc_stats.ast_tx_aggr_failall++;
bf = bf_first;
while (bf) {
bf_next = bf->bf_next;
bf->bf_next = NULL;
sc->sc_stats.ast_tx_aggr_fail++;
if (ath_tx_retry_subframe(sc, bf, &bf_q)) {
drops++;
bf->bf_next = NULL;
TAILQ_INSERT_TAIL(&bf_cq, bf, bf_list);
}
bf = bf_next;
}
while ((bf = TAILQ_LAST(&bf_q, ath_bufhead_s)) != NULL) {
TAILQ_REMOVE(&bf_q, bf, bf_list);
ATH_TID_INSERT_HEAD(tid, bf, bf_list);
}
ath_tx_tid_sched(sc, tid);
if (drops) {
ath_tx_tid_bar_suspend(sc, tid);
}
if (ath_tx_tid_bar_tx_ready(sc, tid))
ath_tx_tid_bar_tx(sc, tid);
ATH_TX_UNLOCK(sc);
while ((bf = TAILQ_FIRST(&bf_cq)) != NULL) {
TAILQ_REMOVE(&bf_cq, bf, bf_list);
ath_tx_default_comp(sc, bf, 0);
}
}
static void
ath_tx_comp_cleanup_aggr(struct ath_softc *sc, struct ath_buf *bf_first)
{
struct ath_buf *bf, *bf_next;
struct ieee80211_node *ni = bf_first->bf_node;
struct ath_node *an = ATH_NODE(ni);
int tid = bf_first->bf_state.bfs_tid;
struct ath_tid *atid = &an->an_tid[tid];
ATH_TX_LOCK(sc);
atid->incomp--;
bf = bf_first;
while (bf) {
if (bf->bf_state.bfs_dobaw) {
ath_tx_update_baw(sc, an, atid, bf);
if (!bf->bf_state.bfs_addedbaw)
DPRINTF(sc, ATH_DEBUG_SW_TX,
"%s: wasn't added: seqno %d\n",
__func__, SEQNO(bf->bf_state.bfs_seqno));
}
bf = bf->bf_next;
}
if (atid->incomp == 0) {
DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL,
"%s: TID %d: cleaned up! resume!\n",
__func__, tid);
atid->cleanup_inprogress = 0;
ath_tx_tid_resume(sc, atid);
}
if (ath_tx_tid_bar_tx_ready(sc, atid))
ath_tx_tid_bar_tx(sc, atid);
ATH_TX_UNLOCK(sc);
bf = bf_first;
while (bf) {
bf_next = bf->bf_next;
bf->bf_next = NULL;
ath_tx_default_comp(sc, bf, 1);
bf = bf_next;
}
}
static void
ath_tx_aggr_comp_aggr(struct ath_softc *sc, struct ath_buf *bf_first,
int fail)
{
struct ieee80211_node *ni = bf_first->bf_node;
struct ath_node *an = ATH_NODE(ni);
int tid = bf_first->bf_state.bfs_tid;
struct ath_tid *atid = &an->an_tid[tid];
struct ath_tx_status ts;
struct ieee80211_tx_ampdu *tap;
ath_bufhead bf_q;
ath_bufhead bf_cq;
int seq_st, tx_ok;
int hasba, isaggr;
uint32_t ba[2];
struct ath_buf *bf, *bf_next;
int ba_index;
int drops = 0;
int nframes = 0, nbad = 0, nf;
int pktlen;
int agglen, rc_agglen;
struct ath_rc_series rc[ATH_RC_NUM];
int txseq;
DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, "%s: called; hwq_depth=%d\n",
__func__, atid->hwq_depth);
ts = bf_first->bf_status.ds_txstat;
agglen = bf_first->bf_state.bfs_al;
rc_agglen = bf_first->bf_state.bfs_rc_maxpktlen;
TAILQ_INIT(&bf_q);
TAILQ_INIT(&bf_cq);
ATH_TX_LOCK(sc);
atid->hwq_depth--;
if (atid->hwq_depth < 0)
DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, "%s: hwq_depth < 0: %d\n",
__func__, atid->hwq_depth);
if (atid->isfiltered)
ath_tx_tid_filt_comp_complete(sc, atid);
if (atid->cleanup_inprogress) {
if (atid->isfiltered)
DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR,
"%s: isfiltered=1, normal_comp?\n",
__func__);
ATH_TX_UNLOCK(sc);
ath_tx_comp_cleanup_aggr(sc, bf_first);
return;
}
if ((ts.ts_status & HAL_TXERR_FILT) ||
(ts.ts_status != 0 && atid->isfiltered)) {
if (fail != 0)
DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR,
"%s: isfiltered=1, fail=%d\n", __func__, fail);
ath_tx_tid_filt_comp_aggr(sc, atid, bf_first, &bf_cq);
TAILQ_FOREACH_SAFE(bf, &bf_cq, bf_list, bf_next) {
if (bf->bf_state.bfs_addedbaw)
drops++;
if (bf->bf_state.bfs_dobaw) {
ath_tx_update_baw(sc, an, atid, bf);
if (!bf->bf_state.bfs_addedbaw)
DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR,
"%s: wasn't added: seqno %d\n",
__func__,
SEQNO(bf->bf_state.bfs_seqno));
}
bf->bf_state.bfs_dobaw = 0;
}
if (drops)
ath_tx_tid_bar_suspend(sc, atid);
goto finish_send_bar;
}
pktlen = bf_first->bf_state.bfs_pktlen;
#if 0
if (ts.ts_status & HAL_TXERR_XRETRY) {
#endif
if (ts.ts_status != 0) {
ATH_TX_UNLOCK(sc);
ath_tx_comp_aggr_error(sc, bf_first, atid);
return;
}
tap = ath_tx_get_tx_tid(an, tid);
seq_st = ts.ts_seqnum;
hasba = !! (ts.ts_flags & HAL_TX_BA);
tx_ok = (ts.ts_status == 0);
isaggr = bf_first->bf_state.bfs_aggr;
ba[0] = ts.ts_ba_low;
ba[1] = ts.ts_ba_high;
memcpy(rc, bf_first->bf_state.bfs_rc, sizeof(rc));
DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR,
"%s: txa_start=%d, tx_ok=%d, status=%.8x, flags=%.8x, "
"isaggr=%d, seq_st=%d, hasba=%d, ba=%.8x, %.8x\n",
__func__, tap->txa_start, tx_ok, ts.ts_status, ts.ts_flags,
isaggr, seq_st, hasba, ba[0], ba[1]);
#if 0
if (tid != ts.ts_tid) {
DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, "%s: tid %d != hw tid %d\n",
__func__, tid, ts.ts_tid);
tx_ok = 0;
}
#endif
if (isaggr && tx_ok && (! hasba)) {
device_printf(sc->sc_dev,
"%s: AR5416 bug: hasba=%d; txok=%d, isaggr=%d, "
"seq_st=%d\n",
__func__, hasba, tx_ok, isaggr, seq_st);
taskqueue_enqueue(sc->sc_tq, &sc->sc_fataltask);
ba[0] = 0;
ba[1] = 0;
seq_st = 0;
#ifdef ATH_DEBUG
ath_printtxbuf(sc, bf_first,
sc->sc_ac2q[atid->ac]->axq_qnum, 0, 0);
#endif
}
bf = bf_first;
nf = bf_first->bf_state.bfs_nframes;
bf_first = NULL;
while (bf) {
nframes++;
ba_index = ATH_BA_INDEX(seq_st,
SEQNO(bf->bf_state.bfs_seqno));
bf_next = bf->bf_next;
bf->bf_next = NULL;
DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR,
"%s: checking bf=%p seqno=%d; ack=%d\n",
__func__, bf, SEQNO(bf->bf_state.bfs_seqno),
ATH_BA_ISSET(ba, ba_index));
if (tx_ok && ATH_BA_ISSET(ba, ba_index)) {
sc->sc_stats.ast_tx_aggr_ok++;
ath_tx_update_baw(sc, an, atid, bf);
bf->bf_state.bfs_dobaw = 0;
if (!bf->bf_state.bfs_addedbaw)
DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR,
"%s: wasn't added: seqno %d\n",
__func__, SEQNO(bf->bf_state.bfs_seqno));
bf->bf_next = NULL;
TAILQ_INSERT_TAIL(&bf_cq, bf, bf_list);
} else {
sc->sc_stats.ast_tx_aggr_fail++;
if (ath_tx_retry_subframe(sc, bf, &bf_q)) {
drops++;
bf->bf_next = NULL;
TAILQ_INSERT_TAIL(&bf_cq, bf, bf_list);
}
nbad++;
}
bf = bf_next;
}
txseq = tap->txa_start;
ATH_TX_UNLOCK(sc);
if (nframes != nf)
DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR,
"%s: num frames seen=%d; bf nframes=%d\n",
__func__, nframes, nf);
if (fail == 0) {
ath_tx_update_ratectrl(sc, ni, rc, &ts, agglen, rc_agglen,
nframes, nbad);
}
if (drops) {
ATH_TX_LOCK(sc);
ath_tx_tid_bar_suspend(sc, atid);
ATH_TX_UNLOCK(sc);
}
DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR,
"%s: txa_start now %d\n", __func__, tap->txa_start);
ATH_TX_LOCK(sc);
while ((bf = TAILQ_LAST(&bf_q, ath_bufhead_s)) != NULL) {
TAILQ_REMOVE(&bf_q, bf, bf_list);
ATH_TID_INSERT_HEAD(atid, bf, bf_list);
}
ath_tx_tid_sched(sc, atid);
if (atid->isfiltered)
ath_tx_tid_filt_comp_complete(sc, atid);
finish_send_bar:
if (ath_tx_tid_bar_tx_ready(sc, atid))
ath_tx_tid_bar_tx(sc, atid);
ATH_TX_UNLOCK(sc);
while ((bf = TAILQ_FIRST(&bf_cq)) != NULL) {
TAILQ_REMOVE(&bf_cq, bf, bf_list);
ath_tx_default_comp(sc, bf, 0);
}
}
static void
ath_tx_aggr_comp_unaggr(struct ath_softc *sc, struct ath_buf *bf, int fail)
{
struct ieee80211_node *ni = bf->bf_node;
struct ath_node *an = ATH_NODE(ni);
int tid = bf->bf_state.bfs_tid;
struct ath_tid *atid = &an->an_tid[tid];
struct ath_tx_status ts;
int drops = 0;
ts = bf->bf_status.ds_txstat;
if (fail == 0 && ((bf->bf_state.bfs_txflags & HAL_TXDESC_NOACK) == 0))
ath_tx_update_ratectrl(sc, ni, bf->bf_state.bfs_rc,
&bf->bf_status.ds_txstat,
bf->bf_state.bfs_pktlen,
bf->bf_state.bfs_pktlen,
1, (ts.ts_status == 0) ? 0 : 1);
ATH_TX_LOCK(sc);
if (tid == IEEE80211_NONQOS_TID)
DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: TID=16!\n", __func__);
DPRINTF(sc, ATH_DEBUG_SW_TX,
"%s: bf=%p: tid=%d, hwq_depth=%d, seqno=%d\n",
__func__, bf, bf->bf_state.bfs_tid, atid->hwq_depth,
SEQNO(bf->bf_state.bfs_seqno));
atid->hwq_depth--;
if (atid->hwq_depth < 0)
DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: hwq_depth < 0: %d\n",
__func__, atid->hwq_depth);
if (atid->isfiltered)
ath_tx_tid_filt_comp_complete(sc, atid);
if (atid->cleanup_inprogress) {
if (atid->isfiltered)
DPRINTF(sc, ATH_DEBUG_SW_TX,
"%s: isfiltered=1, normal_comp?\n",
__func__);
ATH_TX_UNLOCK(sc);
DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: cleanup_unaggr\n",
__func__);
ath_tx_comp_cleanup_unaggr(sc, bf);
return;
}
if ((ts.ts_status & HAL_TXERR_FILT) ||
(ts.ts_status != 0 && atid->isfiltered)) {
int freeframe;
if (fail != 0)
DPRINTF(sc, ATH_DEBUG_SW_TX,
"%s: isfiltered=1, fail=%d\n",
__func__, fail);
freeframe = ath_tx_tid_filt_comp_single(sc, atid, bf);
if (freeframe) {
if (bf->bf_state.bfs_addedbaw)
drops++;
if (bf->bf_state.bfs_dobaw) {
ath_tx_update_baw(sc, an, atid, bf);
if (!bf->bf_state.bfs_addedbaw)
DPRINTF(sc, ATH_DEBUG_SW_TX,
"%s: wasn't added: seqno %d\n",
__func__, SEQNO(bf->bf_state.bfs_seqno));
}
bf->bf_state.bfs_dobaw = 0;
}
if (freeframe && drops)
ath_tx_tid_bar_suspend(sc, atid);
if (ath_tx_tid_bar_tx_ready(sc, atid))
ath_tx_tid_bar_tx(sc, atid);
ATH_TX_UNLOCK(sc);
if (freeframe)
ath_tx_default_comp(sc, bf, fail);
return;
}
#if 0
if (fail == 0 && ts->ts_status & HAL_TXERR_XRETRY) {
#endif
if (fail == 0 && ts.ts_status != 0) {
ATH_TX_UNLOCK(sc);
DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: retry_unaggr\n",
__func__);
ath_tx_aggr_retry_unaggr(sc, bf);
return;
}
DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: TID=%d, seqno %d\n",
__func__, tid, SEQNO(bf->bf_state.bfs_seqno));
if (bf->bf_state.bfs_dobaw) {
ath_tx_update_baw(sc, an, atid, bf);
bf->bf_state.bfs_dobaw = 0;
if (!bf->bf_state.bfs_addedbaw)
DPRINTF(sc, ATH_DEBUG_SW_TX,
"%s: wasn't added: seqno %d\n",
__func__, SEQNO(bf->bf_state.bfs_seqno));
}
if (atid->isfiltered)
ath_tx_tid_filt_comp_complete(sc, atid);
if (ath_tx_tid_bar_tx_ready(sc, atid))
ath_tx_tid_bar_tx(sc, atid);
ATH_TX_UNLOCK(sc);
ath_tx_default_comp(sc, bf, fail);
}
void
ath_tx_aggr_comp(struct ath_softc *sc, struct ath_buf *bf, int fail)
{
if (bf->bf_state.bfs_aggr)
ath_tx_aggr_comp_aggr(sc, bf, fail);
else
ath_tx_aggr_comp_unaggr(sc, bf, fail);
}
static int
ath_tx_tid_swq_depth_bytes(struct ath_softc *sc, struct ath_node *an,
struct ath_tid *tid)
{
struct ath_buf *bf;
struct ieee80211_tx_ampdu *tap;
int nbytes = 0;
ATH_TX_LOCK_ASSERT(sc);
tap = ath_tx_get_tx_tid(an, tid->tid);
TAILQ_FOREACH(bf, &tid->tid_q, bf_list) {
if (tap != NULL && (! BAW_WITHIN(tap->txa_start, tap->txa_wnd,
SEQNO(bf->bf_state.bfs_seqno)))) {
break;
}
if (! bf->bf_state.bfs_dobaw) {
break;
}
nbytes += bf->bf_state.bfs_pktlen;
if (nbytes >= ATH_AGGR_MAXSIZE)
break;
if (an->an_leak_count) {
break;
}
}
return MIN(nbytes, ATH_AGGR_MAXSIZE);
}
void
ath_tx_tid_hw_queue_aggr(struct ath_softc *sc, struct ath_node *an,
struct ath_tid *tid)
{
struct ath_buf *bf;
struct ath_txq *txq = sc->sc_ac2q[tid->ac];
struct ieee80211_tx_ampdu *tap;
ATH_AGGR_STATUS status;
ath_bufhead bf_q;
int swq_pktbytes;
DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: tid=%d\n", __func__, tid->tid);
ATH_TX_LOCK_ASSERT(sc);
tap = ath_tx_get_tx_tid(an, tid->tid);
if (tid->tid == IEEE80211_NONQOS_TID)
DPRINTF(sc, ATH_DEBUG_SW_TX,
"%s: called for TID=NONQOS_TID?\n", __func__);
for (;;) {
status = ATH_AGGR_DONE;
if (! ath_tx_tid_can_tx_or_sched(sc, tid))
break;
bf = ATH_TID_FIRST(tid);
if (bf == NULL) {
break;
}
if (! bf->bf_state.bfs_dobaw) {
DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR,
"%s: non-baw packet\n",
__func__);
ATH_TID_REMOVE(tid, bf, bf_list);
if (bf->bf_state.bfs_nframes > 1)
DPRINTF(sc, ATH_DEBUG_SW_TX,
"%s: aggr=%d, nframes=%d\n",
__func__,
bf->bf_state.bfs_aggr,
bf->bf_state.bfs_nframes);
bf->bf_state.bfs_aggr = 0;
bf->bf_state.bfs_nframes = 1;
ath_tx_update_clrdmask(sc, tid, bf);
ath_tx_do_ratelookup(sc, bf, tid->tid,
bf->bf_state.bfs_pktlen, false);
ath_tx_calc_duration(sc, bf);
ath_tx_calc_protection(sc, bf);
ath_tx_set_rtscts(sc, bf);
ath_tx_rate_fill_rcflags(sc, bf);
ath_tx_setds(sc, bf);
ath_hal_clr11n_aggr(sc->sc_ah, bf->bf_desc);
sc->sc_aggr_stats.aggr_nonbaw_pkt++;
goto queuepkt;
}
TAILQ_INIT(&bf_q);
swq_pktbytes = ath_tx_tid_swq_depth_bytes(sc, an, tid);
ath_tx_do_ratelookup(sc, bf, tid->tid, swq_pktbytes, true);
ath_tx_calc_duration(sc, bf);
ath_tx_calc_protection(sc, bf);
ath_tx_set_rtscts(sc, bf);
ath_tx_rate_fill_rcflags(sc, bf);
status = ath_tx_form_aggr(sc, an, tid, &bf_q);
DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR,
"%s: ath_tx_form_aggr() status=%d\n", __func__, status);
if (TAILQ_EMPTY(&bf_q))
break;
bf = TAILQ_FIRST(&bf_q);
if (status == ATH_AGGR_8K_LIMITED)
sc->sc_aggr_stats.aggr_rts_aggr_limited++;
if (bf->bf_state.bfs_nframes == 1) {
DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR,
"%s: single-frame aggregate\n", __func__);
ath_tx_update_clrdmask(sc, tid, bf);
bf->bf_state.bfs_aggr = 0;
bf->bf_state.bfs_ndelim = 0;
ath_tx_setds(sc, bf);
ath_hal_clr11n_aggr(sc->sc_ah, bf->bf_desc);
if (status == ATH_AGGR_BAW_CLOSED)
sc->sc_aggr_stats.aggr_baw_closed_single_pkt++;
else
sc->sc_aggr_stats.aggr_single_pkt++;
} else {
DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR,
"%s: multi-frame aggregate: %d frames, "
"length %d\n",
__func__, bf->bf_state.bfs_nframes,
bf->bf_state.bfs_al);
bf->bf_state.bfs_aggr = 1;
sc->sc_aggr_stats.aggr_pkts[bf->bf_state.bfs_nframes]++;
sc->sc_aggr_stats.aggr_aggr_pkt++;
ath_tx_update_clrdmask(sc, tid, bf);
ath_tx_calc_duration(sc, bf);
ath_tx_calc_protection(sc, bf);
ath_tx_set_rtscts(sc, bf);
ath_tx_setds_11n(sc, bf);
}
queuepkt:
bf->bf_comp = ath_tx_aggr_comp;
if (bf->bf_state.bfs_tid == IEEE80211_NONQOS_TID)
DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: TID=16?\n", __func__);
ath_tx_leak_count_update(sc, tid, bf);
ath_tx_handoff(sc, txq, bf);
tid->hwq_depth++;
if (txq->axq_aggr_depth >= sc->sc_hwq_limit_aggr ||
(status == ATH_AGGR_BAW_CLOSED ||
status == ATH_AGGR_LEAK_CLOSED))
break;
}
}
void
ath_tx_tid_hw_queue_norm(struct ath_softc *sc, struct ath_node *an,
struct ath_tid *tid)
{
struct ath_buf *bf;
struct ath_txq *txq = sc->sc_ac2q[tid->ac];
DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: node %p: TID %d: called\n",
__func__, an, tid->tid);
ATH_TX_LOCK_ASSERT(sc);
if (ath_tx_ampdu_pending(sc, an, tid->tid))
DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: tid=%d, ampdu pending?\n",
__func__, tid->tid);
if (ath_tx_ampdu_running(sc, an, tid->tid))
DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: tid=%d, ampdu running?\n",
__func__, tid->tid);
for (;;) {
if (! ath_tx_tid_can_tx_or_sched(sc, tid))
break;
bf = ATH_TID_FIRST(tid);
if (bf == NULL) {
break;
}
ATH_TID_REMOVE(tid, bf, bf_list);
if (tid->tid != bf->bf_state.bfs_tid) {
DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: bfs_tid %d !="
" tid %d\n", __func__, bf->bf_state.bfs_tid,
tid->tid);
}
bf->bf_comp = ath_tx_normal_comp;
bf->bf_state.bfs_txflags |= HAL_TXDESC_CLRDMASK;
ath_tx_update_clrdmask(sc, tid, bf);
ath_tx_do_ratelookup(sc, bf, tid->tid,
bf->bf_state.bfs_pktlen, false);
ath_tx_calc_duration(sc, bf);
ath_tx_calc_protection(sc, bf);
ath_tx_set_rtscts(sc, bf);
ath_tx_rate_fill_rcflags(sc, bf);
ath_tx_setds(sc, bf);
ath_tx_leak_count_update(sc, tid, bf);
tid->hwq_depth++;
ath_tx_handoff(sc, txq, bf);
}
}
void
ath_txq_sched(struct ath_softc *sc, struct ath_txq *txq)
{
struct ath_tid *tid, *next, *last;
ATH_TX_LOCK_ASSERT(sc);
if (txq->axq_aggr_depth >= sc->sc_hwq_limit_aggr) {
sc->sc_aggr_stats.aggr_sched_nopkt++;
return;
}
if (txq->axq_depth + txq->fifo.axq_depth >= sc->sc_hwq_limit_nonaggr) {
sc->sc_aggr_stats.aggr_sched_nopkt++;
return;
}
last = TAILQ_LAST(&txq->axq_tidq, axq_t_s);
TAILQ_FOREACH_SAFE(tid, &txq->axq_tidq, axq_qelem, next) {
DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: tid=%d, paused=%d\n",
__func__, tid->tid, tid->paused);
ath_tx_tid_unsched(sc, tid);
if (! ath_tx_tid_can_tx_or_sched(sc, tid)) {
goto loop_done;
}
if (ath_tx_ampdu_running(sc, tid->an, tid->tid))
ath_tx_tid_hw_queue_aggr(sc, tid->an, tid);
else
ath_tx_tid_hw_queue_norm(sc, tid->an, tid);
if (tid->axq_depth != 0)
ath_tx_tid_sched(sc, tid);
if (txq->axq_aggr_depth + txq->fifo.axq_depth >= sc->sc_hwq_limit_aggr) {
break;
}
if (txq->axq_depth >= sc->sc_hwq_limit_nonaggr) {
break;
}
loop_done:
if (tid == last)
break;
}
}
struct ieee80211_tx_ampdu *
ath_tx_get_tx_tid(struct ath_node *an, int tid)
{
struct ieee80211_node *ni = &an->an_node;
struct ieee80211_tx_ampdu *tap;
if (tid == IEEE80211_NONQOS_TID)
return NULL;
tap = &ni->ni_tx_ampdu[tid];
return tap;
}
static int
ath_tx_ampdu_running(struct ath_softc *sc, struct ath_node *an, int tid)
{
struct ieee80211_tx_ampdu *tap;
if (tid == IEEE80211_NONQOS_TID)
return 0;
tap = ath_tx_get_tx_tid(an, tid);
if (tap == NULL)
return 0;
return !! (tap->txa_flags & IEEE80211_AGGR_RUNNING);
}
static int
ath_tx_ampdu_pending(struct ath_softc *sc, struct ath_node *an, int tid)
{
struct ieee80211_tx_ampdu *tap;
if (tid == IEEE80211_NONQOS_TID)
return 0;
tap = ath_tx_get_tx_tid(an, tid);
if (tap == NULL)
return 0;
return !! (tap->txa_flags & IEEE80211_AGGR_XCHGPEND);
}
int
ath_addba_request(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap,
int dialogtoken, int baparamset, int batimeout)
{
struct ath_softc *sc = ni->ni_ic->ic_softc;
int tid = tap->txa_tid;
struct ath_node *an = ATH_NODE(ni);
struct ath_tid *atid = &an->an_tid[tid];
ATH_TX_LOCK(sc);
if (atid->addba_tx_pending == 0) {
ath_tx_tid_pause(sc, atid);
atid->addba_tx_pending = 1;
}
ATH_TX_UNLOCK(sc);
DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL,
"%s: %6D: called; dialogtoken=%d, baparamset=%d, batimeout=%d\n",
__func__,
ni->ni_macaddr,
":",
dialogtoken, baparamset, batimeout);
DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL,
"%s: txa_start=%d, ni_txseqs=%d\n",
__func__, tap->txa_start, ni->ni_txseqs[tid]);
return sc->sc_addba_request(ni, tap, dialogtoken, baparamset,
batimeout);
}
int
ath_addba_response(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap,
int status, int code, int batimeout)
{
struct ath_softc *sc = ni->ni_ic->ic_softc;
int tid = tap->txa_tid;
struct ath_node *an = ATH_NODE(ni);
struct ath_tid *atid = &an->an_tid[tid];
int r;
DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL,
"%s: %6D: called; status=%d, code=%d, batimeout=%d\n", __func__,
ni->ni_macaddr,
":",
status, code, batimeout);
DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL,
"%s: txa_start=%d, ni_txseqs=%d\n",
__func__, tap->txa_start, ni->ni_txseqs[tid]);
r = sc->sc_addba_response(ni, tap, status, code, batimeout);
ATH_TX_LOCK(sc);
atid->addba_tx_pending = 0;
tap->txa_start = ni->ni_txseqs[tid];
ath_tx_tid_resume(sc, atid);
ATH_TX_UNLOCK(sc);
return r;
}
void
ath_addba_stop(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap)
{
struct ath_softc *sc = ni->ni_ic->ic_softc;
int tid = tap->txa_tid;
struct ath_node *an = ATH_NODE(ni);
struct ath_tid *atid = &an->an_tid[tid];
ath_bufhead bf_cq;
struct ath_buf *bf;
DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL, "%s: %6D: called\n",
__func__,
ni->ni_macaddr,
":");
ATH_TX_LOCK(sc);
ath_tx_tid_pause(sc, atid);
if (atid->bar_wait) {
atid->bar_tx = 1;
ath_tx_tid_bar_unsuspend(sc, atid);
}
ATH_TX_UNLOCK(sc);
sc->sc_addba_stop(ni, tap);
TAILQ_INIT(&bf_cq);
ATH_TX_LOCK(sc);
if (atid->cleanup_inprogress) {
ath_tx_tid_resume(sc, atid);
} else {
ath_tx_tid_cleanup(sc, an, tid, &bf_cq);
if (! atid->cleanup_inprogress)
ath_tx_tid_resume(sc, atid);
}
ATH_TX_UNLOCK(sc);
while ((bf = TAILQ_FIRST(&bf_cq)) != NULL) {
TAILQ_REMOVE(&bf_cq, bf, bf_list);
ath_tx_default_comp(sc, bf, 1);
}
}
void
ath_tx_node_reassoc(struct ath_softc *sc, struct ath_node *an)
{
struct ath_tid *tid;
int i;
ath_bufhead bf_cq;
struct ath_buf *bf;
TAILQ_INIT(&bf_cq);
ATH_TX_UNLOCK_ASSERT(sc);
ATH_TX_LOCK(sc);
for (i = 0; i < IEEE80211_TID_SIZE; i++) {
tid = &an->an_tid[i];
if (tid->hwq_depth == 0)
continue;
DPRINTF(sc, ATH_DEBUG_NODE,
"%s: %6D: TID %d: cleaning up TID\n",
__func__,
an->an_node.ni_macaddr,
":",
i);
if (! tid->cleanup_inprogress) {
ath_tx_tid_pause(sc, tid);
ath_tx_tid_cleanup(sc, an, i, &bf_cq);
if (! tid->cleanup_inprogress)
ath_tx_tid_resume(sc, tid);
}
}
ATH_TX_UNLOCK(sc);
while ((bf = TAILQ_FIRST(&bf_cq)) != NULL) {
TAILQ_REMOVE(&bf_cq, bf, bf_list);
ath_tx_default_comp(sc, bf, 1);
}
}
void
ath_bar_response(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap,
int status)
{
struct ath_softc *sc = ni->ni_ic->ic_softc;
int tid = tap->txa_tid;
struct ath_node *an = ATH_NODE(ni);
struct ath_tid *atid = &an->an_tid[tid];
int attempts = tap->txa_attempts;
int old_txa_start;
DPRINTF(sc, ATH_DEBUG_SW_TX_BAR,
"%s: %6D: called; txa_tid=%d, atid->tid=%d, status=%d, attempts=%d, txa_start=%d, txa_seqpending=%d\n",
__func__,
ni->ni_macaddr,
":",
tap->txa_tid,
atid->tid,
status,
attempts,
tap->txa_start,
tap->txa_seqpending);
ATH_TX_LOCK(sc);
old_txa_start = tap->txa_start;
sc->sc_bar_response(ni, tap, status);
if (tap->txa_start != old_txa_start) {
device_printf(sc->sc_dev, "%s: tid=%d; txa_start=%d, old=%d, adjusting\n",
__func__,
tid,
tap->txa_start,
old_txa_start);
}
tap->txa_start = old_txa_start;
ATH_TX_UNLOCK(sc);
if (status == 0 || attempts == 50) {
ATH_TX_LOCK(sc);
if (atid->bar_tx == 0 || atid->bar_wait == 0)
DPRINTF(sc, ATH_DEBUG_SW_TX_BAR,
"%s: huh? bar_tx=%d, bar_wait=%d\n",
__func__,
atid->bar_tx, atid->bar_wait);
else
ath_tx_tid_bar_unsuspend(sc, atid);
ATH_TX_UNLOCK(sc);
}
}
void
ath_addba_response_timeout(struct ieee80211_node *ni,
struct ieee80211_tx_ampdu *tap)
{
struct ath_softc *sc = ni->ni_ic->ic_softc;
int tid = tap->txa_tid;
struct ath_node *an = ATH_NODE(ni);
struct ath_tid *atid = &an->an_tid[tid];
DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL,
"%s: %6D: TID=%d, called; resuming\n",
__func__,
ni->ni_macaddr,
":",
tid);
ATH_TX_LOCK(sc);
atid->addba_tx_pending = 0;
ATH_TX_UNLOCK(sc);
sc->sc_addba_response_timeout(ni, tap);
ATH_TX_LOCK(sc);
ath_tx_tid_resume(sc, atid);
ATH_TX_UNLOCK(sc);
}
int
ath_tx_node_is_asleep(struct ath_softc *sc, struct ath_node *an)
{
ATH_TX_LOCK_ASSERT(sc);
return (an->an_is_powersave);
}
void
ath_tx_node_sleep(struct ath_softc *sc, struct ath_node *an)
{
struct ath_tid *atid;
struct ath_txq *txq;
int tid;
ATH_TX_UNLOCK_ASSERT(sc);
ATH_TX_LOCK(sc);
if (an->an_is_powersave) {
DPRINTF(sc, ATH_DEBUG_XMIT,
"%s: %6D: node was already asleep!\n",
__func__, an->an_node.ni_macaddr, ":");
ATH_TX_UNLOCK(sc);
return;
}
for (tid = 0; tid < IEEE80211_TID_SIZE; tid++) {
atid = &an->an_tid[tid];
txq = sc->sc_ac2q[atid->ac];
ath_tx_tid_pause(sc, atid);
}
an->an_is_powersave = 1;
ATH_TX_UNLOCK(sc);
}
void
ath_tx_node_wakeup(struct ath_softc *sc, struct ath_node *an)
{
struct ath_tid *atid;
struct ath_txq *txq;
int tid;
ATH_TX_UNLOCK_ASSERT(sc);
ATH_TX_LOCK(sc);
if (an->an_is_powersave == 0) {
ATH_TX_UNLOCK(sc);
DPRINTF(sc, ATH_DEBUG_XMIT,
"%s: an=%p: node was already awake\n",
__func__, an);
return;
}
an->an_is_powersave = 0;
an->an_leak_count = 0;
for (tid = 0; tid < IEEE80211_TID_SIZE; tid++) {
atid = &an->an_tid[tid];
txq = sc->sc_ac2q[atid->ac];
ath_tx_tid_resume(sc, atid);
}
ATH_TX_UNLOCK(sc);
}
static int
ath_legacy_dma_txsetup(struct ath_softc *sc)
{
return (0);
}
static int
ath_legacy_dma_txteardown(struct ath_softc *sc)
{
return (0);
}
void
ath_xmit_setup_legacy(struct ath_softc *sc)
{
sc->sc_tx_desclen = sizeof(struct ath_desc);
sc->sc_tx_statuslen = sizeof(struct ath_desc);
sc->sc_tx_nmaps = 1;
sc->sc_tx.xmit_setup = ath_legacy_dma_txsetup;
sc->sc_tx.xmit_teardown = ath_legacy_dma_txteardown;
sc->sc_tx.xmit_attach_comp_func = ath_legacy_attach_comp_func;
sc->sc_tx.xmit_dma_restart = ath_legacy_tx_dma_restart;
sc->sc_tx.xmit_handoff = ath_legacy_xmit_handoff;
sc->sc_tx.xmit_drain = ath_legacy_tx_drain;
}