summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoradrian <adrian@FreeBSD.org>2011-01-29 11:35:23 +0000
committeradrian <adrian@FreeBSD.org>2011-01-29 11:35:23 +0000
commit05a9c90aff7fc24d4ff0c691901e9a44607ab93d (patch)
tree0855be70ab386f365706ad46e7bd48b820580f7c
parentcc338a54f711e91735e9c9640940ef576495186c (diff)
downloadFreeBSD-src-05a9c90aff7fc24d4ff0c691901e9a44607ab93d.zip
FreeBSD-src-05a9c90aff7fc24d4ff0c691901e9a44607ab93d.tar.gz
Migrate the TX path code out of if_ath and into a separate source file.
There's two reasons for this: * the raw and non-raw TX path shares a lot of duplicate code which should be refactored; * the 11n-ready chip TX path needs a little reworking.
-rw-r--r--sys/conf/files2
-rw-r--r--sys/dev/ath/if_ath.c957
-rw-r--r--sys/dev/ath/if_ath_misc.h56
-rw-r--r--sys/dev/ath/if_ath_tx.c1029
-rw-r--r--sys/dev/ath/if_ath_tx.h44
-rw-r--r--sys/modules/ath/Makefile2
6 files changed, 1137 insertions, 953 deletions
diff --git a/sys/conf/files b/sys/conf/files
index 3a20e2a..c4077eb 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -568,6 +568,8 @@ dev/ath/if_ath.c optional ath \
compile-with "${NORMAL_C} -I$S/dev/ath"
dev/ath/if_ath_debug.c optional ath \
compile-with "${NORMAL_C} -I$S/dev/ath"
+dev/ath/if_ath_tx.c optional ath \
+ compile-with "${NORMAL_C} -I$S/dev/ath"
dev/ath/if_ath_pci.c optional ath pci \
compile-with "${NORMAL_C} -I$S/dev/ath"
dev/ath/ah_osdep.c optional ath \
diff --git a/sys/dev/ath/if_ath.c b/sys/dev/ath/if_ath.c
index fdda16d..43ad7ce 100644
--- a/sys/dev/ath/if_ath.c
+++ b/sys/dev/ath/if_ath.c
@@ -90,6 +90,8 @@ __FBSDID("$FreeBSD$");
#include <dev/ath/ath_hal/ah_diagcodes.h>
#include <dev/ath/if_ath_debug.h>
+#include <dev/ath/if_ath_misc.h>
+#include <dev/ath/if_ath_tx.h>
#ifdef ATH_TX99_DIAG
#include <dev/ath/ath_tx99/ath_tx99.h>
@@ -111,15 +113,6 @@ __FBSDID("$FreeBSD$");
*/
CTASSERT(ATH_BCBUF <= 8);
-/* unaligned little endian access */
-#define LE_READ_2(p) \
- ((u_int16_t) \
- ((((u_int8_t *)(p))[0] ) | (((u_int8_t *)(p))[1] << 8)))
-#define LE_READ_4(p) \
- ((u_int32_t) \
- ((((u_int8_t *)(p))[0] ) | (((u_int8_t *)(p))[1] << 8) | \
- (((u_int8_t *)(p))[2] << 16) | (((u_int8_t *)(p))[3] << 24)))
-
static struct ieee80211vap *ath_vap_create(struct ieee80211com *,
const char name[IFNAMSIZ], int unit, int opmode,
int flags, const uint8_t bssid[IEEE80211_ADDR_LEN],
@@ -184,9 +177,6 @@ static int ath_tx_setup(struct ath_softc *, int, int);
static int ath_wme_update(struct ieee80211com *);
static void ath_tx_cleanupq(struct ath_softc *, struct ath_txq *);
static void ath_tx_cleanup(struct ath_softc *);
-static void ath_freetx(struct mbuf *);
-static int ath_tx_start(struct ath_softc *, struct ieee80211_node *,
- struct ath_buf *, struct mbuf *);
static void ath_tx_proc_q0(void *, int);
static void ath_tx_proc_q0123(void *, int);
static void ath_tx_proc(void *, int);
@@ -215,8 +205,6 @@ static int ath_rate_setup(struct ath_softc *, u_int mode);
static void ath_setcurmode(struct ath_softc *, enum ieee80211_phymode);
static void ath_sysctlattach(struct ath_softc *);
-static int ath_raw_xmit(struct ieee80211_node *,
- struct mbuf *, const struct ieee80211_bpf_params *);
static void ath_announce(struct ath_softc *);
static void ath_sysctl_stats_attach(struct ath_softc *sc);
@@ -1670,7 +1658,7 @@ ath_reset_vap(struct ieee80211vap *vap, u_long cmd)
return ath_reset(ifp);
}
-static struct ath_buf *
+struct ath_buf *
_ath_getbuf_locked(struct ath_softc *sc)
{
struct ath_buf *bf;
@@ -1690,7 +1678,7 @@ _ath_getbuf_locked(struct ath_softc *sc)
return bf;
}
-static struct ath_buf *
+struct ath_buf *
ath_getbuf(struct ath_softc *sc)
{
struct ath_buf *bf;
@@ -1708,54 +1696,6 @@ ath_getbuf(struct ath_softc *sc)
return bf;
}
-/*
- * Cleanup driver resources when we run out of buffers
- * while processing fragments; return the tx buffers
- * allocated and drop node references.
- */
-static 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);
-
- STAILQ_FOREACH_SAFE(bf, frags, bf_list, next) {
- /* NB: bf assumed clean */
- STAILQ_REMOVE_HEAD(frags, bf_list);
- STAILQ_INSERT_HEAD(&sc->sc_txbuf, bf, bf_list);
- ieee80211_node_decref(ni);
- }
-}
-
-/*
- * Setup xmit of a fragmented frame. Allocate a buffer
- * for each frag and bump the node reference count to
- * reflect the held reference to be setup by ath_tx_start.
- */
-static 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);
- if (bf == NULL) { /* out of buffers, cleanup */
- ath_txfrag_cleanup(sc, frags, ni);
- break;
- }
- ieee80211_node_incref(ni);
- STAILQ_INSERT_TAIL(frags, bf, bf_list);
- }
- ATH_TXBUF_UNLOCK(sc);
-
- return !STAILQ_EMPTY(frags);
-}
-
static void
ath_start(struct ifnet *ifp)
{
@@ -4227,7 +4167,7 @@ ath_tx_cleanup(struct ath_softc *sc)
* Return h/w rate index for an IEEE rate (w/o basic rate bit)
* using the current rates in sc_rixmap.
*/
-static __inline int
+int
ath_tx_findrix(const struct ath_softc *sc, uint8_t rate)
{
int rix = sc->sc_rixmap[rate];
@@ -4236,623 +4176,6 @@ ath_tx_findrix(const struct ath_softc *sc, uint8_t rate)
}
/*
- * Reclaim mbuf resources. For fragmented frames we
- * need to claim each frag chained with m_nextpkt.
- */
-static void
-ath_freetx(struct mbuf *m)
-{
- struct mbuf *next;
-
- do {
- next = m->m_nextpkt;
- m->m_nextpkt = NULL;
- m_freem(m);
- } while ((m = next) != NULL);
-}
-
-static int
-ath_tx_dmasetup(struct ath_softc *sc, struct ath_buf *bf, struct mbuf *m0)
-{
- struct mbuf *m;
- int error;
-
- /*
- * Load the DMA map so any coalescing is done. This
- * also calculates the number of descriptors we need.
- */
- 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) {
- /* XXX packet requires too many descriptors */
- bf->bf_nseg = ATH_TXDESC+1;
- } else if (error != 0) {
- sc->sc_stats.ast_tx_busdma++;
- ath_freetx(m0);
- return error;
- }
- /*
- * Discard null packets and check for packets that
- * require too many TX descriptors. We try to convert
- * the latter to a cluster.
- */
- if (bf->bf_nseg > ATH_TXDESC) { /* too many desc's, linearize */
- sc->sc_stats.ast_tx_linear++;
- m = m_collapse(m0, M_DONTWAIT, ATH_TXDESC);
- if (m == NULL) {
- ath_freetx(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++;
- ath_freetx(m0);
- return error;
- }
- KASSERT(bf->bf_nseg <= ATH_TXDESC,
- ("too many segments after defrag; nseg %u", bf->bf_nseg));
- } else if (bf->bf_nseg == 0) { /* null packet, discard */
- sc->sc_stats.ast_tx_nodata++;
- ath_freetx(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_handoff(struct ath_softc *sc, struct ath_txq *txq, struct ath_buf *bf)
-{
- struct ath_hal *ah = sc->sc_ah;
- struct ath_desc *ds, *ds0;
- int i;
-
- /*
- * Fillin the remainder of the descriptor info.
- */
- ds0 = ds = bf->bf_desc;
- for (i = 0; i < bf->bf_nseg; i++, ds++) {
- ds->ds_data = bf->bf_segs[i].ds_addr;
- if (i == bf->bf_nseg - 1)
- ds->ds_link = 0;
- else
- ds->ds_link = bf->bf_daddr + sizeof(*ds) * (i + 1);
- ath_hal_filltxdesc(ah, ds
- , bf->bf_segs[i].ds_len /* segment length */
- , i == 0 /* first segment */
- , i == bf->bf_nseg - 1 /* last segment */
- , ds0 /* first descriptor */
- );
- DPRINTF(sc, ATH_DEBUG_XMIT,
- "%s: %d: %08x %08x %08x %08x %08x %08x\n",
- __func__, i, ds->ds_link, ds->ds_data,
- ds->ds_ctl0, ds->ds_ctl1, ds->ds_hw[0], ds->ds_hw[1]);
- }
- /*
- * Insert the frame on the outbound list and pass it on
- * to the hardware. Multicast frames buffered for power
- * save stations and transmit from the CAB queue are stored
- * on a s/w only queue and loaded on to the CAB queue in
- * the SWBA handler since frames only go out on DTIM and
- * to avoid possible races.
- */
- ATH_TXQ_LOCK(txq);
- KASSERT((bf->bf_flags & ATH_BUF_BUSY) == 0,
- ("busy status 0x%x", bf->bf_flags));
- if (txq->axq_qnum != ATH_TXQ_SWQ) {
-#ifdef IEEE80211_SUPPORT_TDMA
- int qbusy;
-
- ATH_TXQ_INSERT_TAIL(txq, bf, bf_list);
- qbusy = ath_hal_txqenabled(ah, txq->axq_qnum);
- if (txq->axq_link == NULL) {
- /*
- * Be careful writing the address to TXDP. If
- * the tx q is enabled then this write will be
- * ignored. Normally this is not an issue but
- * when tdma is in use and the q is beacon gated
- * this race can occur. If the q is busy then
- * defer the work to later--either when another
- * packet comes along or when we prepare a beacon
- * frame at SWBA.
- */
- if (!qbusy) {
- ath_hal_puttxbuf(ah, txq->axq_qnum, bf->bf_daddr);
- txq->axq_flags &= ~ATH_TXQ_PUTPENDING;
- DPRINTF(sc, ATH_DEBUG_XMIT,
- "%s: TXDP[%u] = %p (%p) depth %d\n",
- __func__, txq->axq_qnum,
- (caddr_t)bf->bf_daddr, bf->bf_desc,
- txq->axq_depth);
- } else {
- txq->axq_flags |= ATH_TXQ_PUTPENDING;
- DPRINTF(sc, ATH_DEBUG_TDMA | ATH_DEBUG_XMIT,
- "%s: Q%u busy, defer enable\n", __func__,
- txq->axq_qnum);
- }
- } else {
- *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);
- if ((txq->axq_flags & ATH_TXQ_PUTPENDING) && !qbusy) {
- /*
- * The q was busy when we previously tried
- * to write the address of the first buffer
- * in the chain. Since it's not busy now
- * handle this chore. We are certain the
- * buffer at the front is the right one since
- * axq_link is NULL only when the buffer list
- * is/was empty.
- */
- ath_hal_puttxbuf(ah, txq->axq_qnum,
- STAILQ_FIRST(&txq->axq_q)->bf_daddr);
- txq->axq_flags &= ~ATH_TXQ_PUTPENDING;
- DPRINTF(sc, ATH_DEBUG_TDMA | ATH_DEBUG_XMIT,
- "%s: Q%u restarted\n", __func__,
- txq->axq_qnum);
- }
- }
-#else
- ATH_TXQ_INSERT_TAIL(txq, bf, bf_list);
- if (txq->axq_link == NULL) {
- ath_hal_puttxbuf(ah, txq->axq_qnum, bf->bf_daddr);
- DPRINTF(sc, ATH_DEBUG_XMIT,
- "%s: TXDP[%u] = %p (%p) depth %d\n",
- __func__, txq->axq_qnum,
- (caddr_t)bf->bf_daddr, bf->bf_desc,
- txq->axq_depth);
- } else {
- *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);
- }
-#endif /* IEEE80211_SUPPORT_TDMA */
- txq->axq_link = &bf->bf_desc[bf->bf_nseg - 1].ds_link;
- ath_hal_txstart(ah, txq->axq_qnum);
- } else {
- if (txq->axq_link != NULL) {
- struct ath_buf *last = ATH_TXQ_LAST(txq);
- struct ieee80211_frame *wh;
-
- /* mark previous frame */
- wh = mtod(last->bf_m, struct ieee80211_frame *);
- wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA;
- bus_dmamap_sync(sc->sc_dmat, last->bf_dmamap,
- BUS_DMASYNC_PREWRITE);
-
- /* link descriptor */
- *txq->axq_link = bf->bf_daddr;
- }
- ATH_TXQ_INSERT_TAIL(txq, bf, bf_list);
- txq->axq_link = &bf->bf_desc[bf->bf_nseg - 1].ds_link;
- }
- ATH_TXQ_UNLOCK(txq);
-}
-
-static 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);
- struct ath_hal *ah = sc->sc_ah;
- struct ifnet *ifp = sc->sc_ifp;
- struct ieee80211com *ic = ifp->if_l2com;
- const struct chanAccParams *cap = &ic->ic_wme.wme_chanParams;
- int error, iswep, ismcast, isfrag, ismrr;
- int keyix, hdrlen, pktlen, try0;
- u_int8_t rix, txrate, ctsrate;
- u_int8_t cix = 0xff; /* NB: silence compiler */
- struct ath_desc *ds;
- struct ath_txq *txq;
- struct ieee80211_frame *wh;
- u_int subtype, flags, ctsduration;
- HAL_PKT_TYPE atype;
- const HAL_RATE_TABLE *rt;
- HAL_BOOL shortPreamble;
- struct ath_node *an;
- u_int pri;
-
- wh = mtod(m0, struct ieee80211_frame *);
- iswep = wh->i_fc[1] & IEEE80211_FC1_WEP;
- ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1);
- isfrag = m0->m_flags & M_FRAG;
- hdrlen = ieee80211_anyhdrsize(wh);
- /*
- * Packet length must not include any
- * pad bytes; deduct them here.
- */
- pktlen = m0->m_pkthdr.len - (hdrlen & 3);
-
- if (iswep) {
- const struct ieee80211_cipher *cip;
- struct ieee80211_key *k;
-
- /*
- * Construct the 802.11 header+trailer for an encrypted
- * frame. The only reason this can fail is because of an
- * unknown or unsupported cipher/key type.
- */
- k = ieee80211_crypto_encap(ni, m0);
- if (k == NULL) {
- /*
- * This can happen when the key is yanked after the
- * frame was queued. Just discard the frame; the
- * 802.11 layer counts failures and provides
- * debugging/diagnostics.
- */
- ath_freetx(m0);
- return EIO;
- }
- /*
- * Adjust the packet + header lengths for the crypto
- * additions and calculate the h/w key index. When
- * a s/w mic is done the frame will have had any mic
- * added to it prior to entry so m0->m_pkthdr.len will
- * account for it. Otherwise we need to add it to the
- * packet length.
- */
- cip = k->wk_cipher;
- hdrlen += cip->ic_header;
- pktlen += cip->ic_header + cip->ic_trailer;
- /* NB: frags always have any TKIP MIC done in s/w */
- if ((k->wk_flags & IEEE80211_KEY_SWMIC) == 0 && !isfrag)
- pktlen += cip->ic_miclen;
- keyix = k->wk_keyix;
-
- /* packet header may have moved, reset our local pointer */
- wh = mtod(m0, struct ieee80211_frame *);
- } else if (ni->ni_ucastkey.wk_cipher == &ieee80211_cipher_none) {
- /*
- * Use station key cache slot, if assigned.
- */
- keyix = ni->ni_ucastkey.wk_keyix;
- if (keyix == IEEE80211_KEYIX_NONE)
- keyix = HAL_TXKEYIX_INVALID;
- } else
- keyix = HAL_TXKEYIX_INVALID;
-
- pktlen += IEEE80211_CRC_LEN;
-
- /*
- * Load the DMA map so any coalescing is done. This
- * also calculates the number of descriptors we need.
- */
- error = ath_tx_dmasetup(sc, bf, m0);
- if (error != 0)
- return error;
- bf->bf_node = ni; /* NB: held reference */
- m0 = bf->bf_m; /* NB: may have changed */
- wh = mtod(m0, struct ieee80211_frame *);
-
- /* setup descriptors */
- ds = bf->bf_desc;
- rt = sc->sc_currates;
- KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode));
-
- /*
- * NB: the 802.11 layer marks whether or not we should
- * use short preamble based on the current mode and
- * negotiated parameters.
- */
- 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 = HAL_TXDESC_CLRDMASK; /* XXX needed for crypto errs */
- ismrr = 0; /* default no multi-rate retry*/
- pri = M_WME_GETAC(m0); /* honor classification */
- /* XXX use txparams instead of fixed values */
- /*
- * Calculate Atheros packet type from IEEE80211 packet header,
- * setup for rate calculations, and select h/w transmit queue.
- */
- 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; /* XXX */
- rix = an->an_mgmtrix;
- txrate = rt->info[rix].rateCode;
- if (shortPreamble)
- txrate |= rt->info[rix].shortPreamble;
- try0 = ATH_TXMGTTRY;
- flags |= HAL_TXDESC_INTREQ; /* force interrupt */
- break;
- case IEEE80211_FC0_TYPE_CTL:
- atype = HAL_PKT_TYPE_PSPOLL; /* stop setting of duration */
- rix = an->an_mgmtrix;
- txrate = rt->info[rix].rateCode;
- if (shortPreamble)
- txrate |= rt->info[rix].shortPreamble;
- try0 = ATH_TXMGTTRY;
- flags |= HAL_TXDESC_INTREQ; /* force interrupt */
- break;
- case IEEE80211_FC0_TYPE_DATA:
- atype = HAL_PKT_TYPE_NORMAL; /* default */
- /*
- * Data frames: multicast frames go out at a fixed rate,
- * EAPOL frames use the mgmt frame rate; otherwise consult
- * the rate control module for the rate to use.
- */
- 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) {
- /* XXX? maybe always use long preamble? */
- rix = an->an_mgmtrix;
- txrate = rt->info[rix].rateCode;
- if (shortPreamble)
- txrate |= rt->info[rix].shortPreamble;
- try0 = ATH_TXMAXTRY; /* XXX?too many? */
- } else {
- ath_rate_findrate(sc, an, shortPreamble, pktlen,
- &rix, &try0, &txrate);
- sc->sc_txrix = rix; /* for LED blinking */
- sc->sc_lastdatarix = rix; /* for fast frames */
- if (try0 != ATH_TXMAXTRY)
- ismrr = 1;
- }
- if (cap->cap_wmeParams[pri].wmep_noackPolicy)
- flags |= HAL_TXDESC_NOACK;
- break;
- default:
- if_printf(ifp, "bogus frame type 0x%x (%s)\n",
- wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK, __func__);
- /* XXX statistic */
- ath_freetx(m0);
- return EIO;
- }
- txq = sc->sc_ac2q[pri];
-
- /*
- * When servicing one or more stations in power-save mode
- * (or) if there is some mcast data waiting on the mcast
- * queue (to prevent out of order delivery) multicast
- * frames must be buffered until after the beacon.
- */
- if (ismcast && (vap->iv_ps_sta || avp->av_mcastq.axq_depth))
- txq = &avp->av_mcastq;
-
- /*
- * Calculate miscellaneous flags.
- */
- if (ismcast) {
- flags |= HAL_TXDESC_NOACK; /* no ack on broad/multicast */
- } else if (pktlen > vap->iv_rtsthreshold &&
- (ni->ni_ath_flags & IEEE80211_NODE_FF) == 0) {
- flags |= HAL_TXDESC_RTSENA; /* RTS based on frame length */
- cix = rt->info[rix].controlRate;
- sc->sc_stats.ast_tx_rts++;
- }
- if (flags & HAL_TXDESC_NOACK) /* NB: avoid double counting */
- 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++;
- ath_freetx(m0);
- return EIO;
- }
-#endif
-
- /*
- * If 802.11g protection is enabled, determine whether
- * to use RTS/CTS or just CTS. Note that this is only
- * done for OFDM unicast frames.
- */
- if ((ic->ic_flags & IEEE80211_F_USEPROT) &&
- rt->info[rix].phy == IEEE80211_T_OFDM &&
- (flags & HAL_TXDESC_NOACK) == 0) {
- /* XXX fragments must use CCK rates w/ protection */
- if (ic->ic_protmode == IEEE80211_PROT_RTSCTS)
- flags |= HAL_TXDESC_RTSENA;
- else if (ic->ic_protmode == IEEE80211_PROT_CTSONLY)
- flags |= HAL_TXDESC_CTSENA;
- if (isfrag) {
- /*
- * For frags it would be desirable to use the
- * highest CCK rate for RTS/CTS. But stations
- * farther away may detect it at a lower CCK rate
- * so use the configured protection rate instead
- * (for now).
- */
- cix = rt->info[sc->sc_protrix].controlRate;
- } else
- cix = rt->info[sc->sc_protrix].controlRate;
- sc->sc_stats.ast_tx_protect++;
- }
-
- /*
- * Calculate duration. This logically belongs in the 802.11
- * layer but it lacks sufficient information to calculate it.
- */
- if ((flags & HAL_TXDESC_NOACK) == 0 &&
- (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_CTL) {
- 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; /* additional SIFS+ACK */
- KASSERT(m0->m_nextpkt != NULL, ("no fragment"));
- /*
- * Include the size of next fragment so NAV is
- * updated properly. The last fragment uses only
- * the ACK duration
- */
- dur += ath_hal_computetxtime(ah, rt,
- m0->m_nextpkt->m_pkthdr.len,
- rix, shortPreamble);
- }
- if (isfrag) {
- /*
- * Force hardware to use computed duration for next
- * fragment by disabling multi-rate retry which updates
- * duration based on the multi-rate duration table.
- */
- ismrr = 0;
- try0 = ATH_TXMGTTRY; /* XXX? */
- }
- *(u_int16_t *)wh->i_dur = htole16(dur);
- }
-
- /*
- * Calculate RTS/CTS rate and duration if needed.
- */
- ctsduration = 0;
- if (flags & (HAL_TXDESC_RTSENA|HAL_TXDESC_CTSENA)) {
- /*
- * CTS transmit rate is derived from the transmit rate
- * by looking in the h/w rate table. We must also factor
- * in whether or not a short preamble is to be used.
- */
- /* NB: cix is set above where RTS/CTS is enabled */
- KASSERT(cix != 0xff, ("cix not setup"));
- ctsrate = rt->info[cix].rateCode;
- /*
- * Compute the transmit duration based on the frame
- * size and the size of an ACK frame. We call into the
- * HAL to do the computation since it depends on the
- * characteristics of the actual PHY being used.
- *
- * NB: CTS is assumed the same size as an ACK so we can
- * use the precalculated ACK durations.
- */
- if (shortPreamble) {
- ctsrate |= rt->info[cix].shortPreamble;
- if (flags & HAL_TXDESC_RTSENA) /* SIFS + CTS */
- ctsduration += rt->info[cix].spAckDuration;
- ctsduration += ath_hal_computetxtime(ah,
- rt, pktlen, rix, AH_TRUE);
- if ((flags & HAL_TXDESC_NOACK) == 0) /* SIFS + ACK */
- ctsduration += rt->info[rix].spAckDuration;
- } else {
- if (flags & HAL_TXDESC_RTSENA) /* SIFS + CTS */
- ctsduration += rt->info[cix].lpAckDuration;
- ctsduration += ath_hal_computetxtime(ah,
- rt, pktlen, rix, AH_FALSE);
- if ((flags & HAL_TXDESC_NOACK) == 0) /* SIFS + ACK */
- ctsduration += rt->info[rix].lpAckDuration;
- }
- /*
- * Must disable multi-rate retry when using RTS/CTS.
- */
- ismrr = 0;
- try0 = ATH_TXMGTTRY; /* XXX */
- } else
- ctsrate = 0;
-
- /*
- * At this point we are committed to sending the frame
- * and we don't need to look at m_nextpkt; clear it in
- * case this frame is part of frag chain.
- */
- 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)) {
- u_int64_t tsf = ath_hal_gettsf64(ah);
-
- sc->sc_tx_th.wt_tsf = htole64(tsf);
- 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 = ni->ni_txpower;
- sc->sc_tx_th.wt_antenna = sc->sc_txantenna;
-
- ieee80211_radiotap_tx(vap, m0);
- }
-
- /*
- * Determine if a tx interrupt should be generated for
- * this descriptor. We take a tx interrupt to reap
- * descriptors when the h/w hits an EOL condition or
- * when the descriptor is specifically marked to generate
- * an interrupt. We periodically mark descriptors in this
- * way to insure timely replenishing of the supply needed
- * for sending frames. Defering interrupts reduces system
- * load and potentially allows more concurrent work to be
- * done but if done to aggressively can cause senders to
- * backup.
- *
- * NB: use >= to deal with sc_txintrperiod changing
- * dynamically through sysctl.
- */
- 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;
- }
-
- /*
- * Formulate first tx descriptor with tx controls.
- */
- /* XXX check return value? */
- ath_hal_setuptxdesc(ah, ds
- , pktlen /* packet length */
- , hdrlen /* header length */
- , atype /* Atheros packet type */
- , ni->ni_txpower /* txpower */
- , txrate, try0 /* series 0 rate/tries */
- , keyix /* key cache index */
- , sc->sc_txantenna /* antenna mode */
- , flags /* flags */
- , ctsrate /* rts/cts rate */
- , ctsduration /* rts/cts duration */
- );
- bf->bf_txflags = flags;
- /*
- * Setup the multi-rate retry state only when we're
- * going to use it. This assumes ath_hal_setuptxdesc
- * initializes the descriptors (so we don't have to)
- * when the hardware supports multi-rate retry and
- * we don't use it.
- */
- if (ismrr)
- ath_rate_setupxtxdesc(sc, an, ds, shortPreamble, rix);
-
- ath_tx_handoff(sc, txq, bf);
- return 0;
-}
-
-/*
* Process completed xmit descriptors from the specified queue.
*/
static int
@@ -6586,276 +5909,6 @@ ath_sysctlattach(struct ath_softc *sc)
#endif
}
-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 ifnet *ifp = sc->sc_ifp;
- struct ieee80211com *ic = ifp->if_l2com;
- struct ath_hal *ah = sc->sc_ah;
- struct ieee80211vap *vap = ni->ni_vap;
- int error, ismcast, ismrr;
- int keyix, hdrlen, pktlen, try0, txantenna;
- u_int8_t rix, cix, txrate, ctsrate, rate1, rate2, rate3;
- struct ieee80211_frame *wh;
- u_int flags, ctsduration;
- HAL_PKT_TYPE atype;
- const HAL_RATE_TABLE *rt;
- struct ath_desc *ds;
- u_int pri;
-
- wh = mtod(m0, struct ieee80211_frame *);
- ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1);
- hdrlen = ieee80211_anyhdrsize(wh);
- /*
- * Packet length must not include any
- * pad bytes; deduct them here.
- */
- /* XXX honor IEEE80211_BPF_DATAPAD */
- pktlen = m0->m_pkthdr.len - (hdrlen & 3) + IEEE80211_CRC_LEN;
-
- if (params->ibp_flags & IEEE80211_BPF_CRYPTO) {
- const struct ieee80211_cipher *cip;
- struct ieee80211_key *k;
-
- /*
- * Construct the 802.11 header+trailer for an encrypted
- * frame. The only reason this can fail is because of an
- * unknown or unsupported cipher/key type.
- */
- k = ieee80211_crypto_encap(ni, m0);
- if (k == NULL) {
- /*
- * This can happen when the key is yanked after the
- * frame was queued. Just discard the frame; the
- * 802.11 layer counts failures and provides
- * debugging/diagnostics.
- */
- ath_freetx(m0);
- return EIO;
- }
- /*
- * Adjust the packet + header lengths for the crypto
- * additions and calculate the h/w key index. When
- * a s/w mic is done the frame will have had any mic
- * added to it prior to entry so m0->m_pkthdr.len will
- * account for it. Otherwise we need to add it to the
- * packet length.
- */
- cip = k->wk_cipher;
- hdrlen += cip->ic_header;
- pktlen += cip->ic_header + cip->ic_trailer;
- /* NB: frags always have any TKIP MIC done in s/w */
- if ((k->wk_flags & IEEE80211_KEY_SWMIC) == 0)
- pktlen += cip->ic_miclen;
- keyix = k->wk_keyix;
-
- /* packet header may have moved, reset our local pointer */
- wh = mtod(m0, struct ieee80211_frame *);
- } else if (ni->ni_ucastkey.wk_cipher == &ieee80211_cipher_none) {
- /*
- * Use station key cache slot, if assigned.
- */
- keyix = ni->ni_ucastkey.wk_keyix;
- if (keyix == IEEE80211_KEYIX_NONE)
- keyix = HAL_TXKEYIX_INVALID;
- } else
- keyix = HAL_TXKEYIX_INVALID;
-
- error = ath_tx_dmasetup(sc, bf, m0);
- if (error != 0)
- return error;
- m0 = bf->bf_m; /* NB: may have changed */
- wh = mtod(m0, struct ieee80211_frame *);
- bf->bf_node = ni; /* NB: held reference */
-
- flags = HAL_TXDESC_CLRDMASK; /* XXX needed for crypto errs */
- flags |= HAL_TXDESC_INTREQ; /* force interrupt */
- if (params->ibp_flags & IEEE80211_BPF_RTS)
- flags |= HAL_TXDESC_RTSENA;
- else if (params->ibp_flags & IEEE80211_BPF_CTS)
- flags |= HAL_TXDESC_CTSENA;
- /* XXX leave ismcast to injector? */
- 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);
- txrate = rt->info[rix].rateCode;
- if (params->ibp_flags & IEEE80211_BPF_SHORTPRE)
- txrate |= rt->info[rix].shortPreamble;
- sc->sc_txrix = rix;
- try0 = params->ibp_try0;
- ismrr = (params->ibp_try1 != 0);
- txantenna = params->ibp_pri >> 2;
- if (txantenna == 0) /* XXX? */
- txantenna = sc->sc_txantenna;
- ctsduration = 0;
- if (flags & (HAL_TXDESC_CTSENA | HAL_TXDESC_RTSENA)) {
- cix = ath_tx_findrix(sc, params->ibp_ctsrate);
- ctsrate = rt->info[cix].rateCode;
- if (params->ibp_flags & IEEE80211_BPF_SHORTPRE) {
- ctsrate |= rt->info[cix].shortPreamble;
- if (flags & HAL_TXDESC_RTSENA) /* SIFS + CTS */
- ctsduration += rt->info[cix].spAckDuration;
- ctsduration += ath_hal_computetxtime(ah,
- rt, pktlen, rix, AH_TRUE);
- if ((flags & HAL_TXDESC_NOACK) == 0) /* SIFS + ACK */
- ctsduration += rt->info[rix].spAckDuration;
- } else {
- if (flags & HAL_TXDESC_RTSENA) /* SIFS + CTS */
- ctsduration += rt->info[cix].lpAckDuration;
- ctsduration += ath_hal_computetxtime(ah,
- rt, pktlen, rix, AH_FALSE);
- if ((flags & HAL_TXDESC_NOACK) == 0) /* SIFS + ACK */
- ctsduration += rt->info[rix].lpAckDuration;
- }
- ismrr = 0; /* XXX */
- } else
- ctsrate = 0;
- pri = params->ibp_pri & 3;
- /*
- * NB: we mark all packets as type PSPOLL so the h/w won't
- * set the sequence number, duration, etc.
- */
- 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)) {
- u_int64_t tsf = ath_hal_gettsf64(ah);
-
- sc->sc_tx_th.wt_tsf = htole64(tsf);
- sc->sc_tx_th.wt_flags = sc->sc_hwmap[rix].txflags;
- if (wh->i_fc[1] & IEEE80211_FC1_WEP)
- 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 = ni->ni_txpower;
- sc->sc_tx_th.wt_antenna = sc->sc_txantenna;
-
- ieee80211_radiotap_tx(vap, m0);
- }
-
- /*
- * Formulate first tx descriptor with tx controls.
- */
- ds = bf->bf_desc;
- /* XXX check return value? */
- ath_hal_setuptxdesc(ah, ds
- , pktlen /* packet length */
- , hdrlen /* header length */
- , atype /* Atheros packet type */
- , params->ibp_power /* txpower */
- , txrate, try0 /* series 0 rate/tries */
- , keyix /* key cache index */
- , txantenna /* antenna mode */
- , flags /* flags */
- , ctsrate /* rts/cts rate */
- , ctsduration /* rts/cts duration */
- );
- bf->bf_txflags = flags;
-
- if (ismrr) {
- rix = ath_tx_findrix(sc, params->ibp_rate1);
- rate1 = rt->info[rix].rateCode;
- if (params->ibp_flags & IEEE80211_BPF_SHORTPRE)
- rate1 |= rt->info[rix].shortPreamble;
- if (params->ibp_try2) {
- rix = ath_tx_findrix(sc, params->ibp_rate2);
- rate2 = rt->info[rix].rateCode;
- if (params->ibp_flags & IEEE80211_BPF_SHORTPRE)
- rate2 |= rt->info[rix].shortPreamble;
- } else
- rate2 = 0;
- if (params->ibp_try3) {
- rix = ath_tx_findrix(sc, params->ibp_rate3);
- rate3 = rt->info[rix].rateCode;
- if (params->ibp_flags & IEEE80211_BPF_SHORTPRE)
- rate3 |= rt->info[rix].shortPreamble;
- } else
- rate3 = 0;
- ath_hal_setupxtxdesc(ah, ds
- , rate1, params->ibp_try1 /* series 1 */
- , rate2, params->ibp_try2 /* series 2 */
- , rate3, params->ibp_try3 /* series 3 */
- );
- }
-
- /* NB: no buffered multicast in power save support */
- ath_tx_handoff(sc, sc->sc_ac2q[pri], bf);
- return 0;
-}
-
-static int
-ath_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
- const struct ieee80211_bpf_params *params)
-{
- struct ieee80211com *ic = ni->ni_ic;
- struct ifnet *ifp = ic->ic_ifp;
- struct ath_softc *sc = ifp->if_softc;
- struct ath_buf *bf;
- int error;
-
- if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || sc->sc_invalid) {
- DPRINTF(sc, ATH_DEBUG_XMIT, "%s: discard frame, %s", __func__,
- (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 ?
- "!running" : "invalid");
- m_freem(m);
- error = ENETDOWN;
- goto bad;
- }
- /*
- * Grab a TX buffer and associated resources.
- */
- bf = ath_getbuf(sc);
- if (bf == NULL) {
- sc->sc_stats.ast_tx_nobuf++;
- m_freem(m);
- error = ENOBUFS;
- goto bad;
- }
-
- if (params == NULL) {
- /*
- * Legacy path; interpret frame contents to decide
- * precisely how to send the frame.
- */
- if (ath_tx_start(sc, ni, bf, m)) {
- error = EIO; /* XXX */
- goto bad2;
- }
- } else {
- /*
- * Caller supplied explicit parameters to use in
- * sending the frame.
- */
- if (ath_tx_raw_start(sc, ni, bf, m, params)) {
- error = EIO; /* XXX */
- goto bad2;
- }
- }
- sc->sc_wd_timer = 5;
- ifp->if_opackets++;
- sc->sc_stats.ast_tx_raw++;
-
- return 0;
-bad2:
- ATH_TXBUF_LOCK(sc);
- STAILQ_INSERT_HEAD(&sc->sc_txbuf, bf, bf_list);
- ATH_TXBUF_UNLOCK(sc);
-bad:
- ifp->if_oerrors++;
- sc->sc_stats.ast_tx_raw_fail++;
- ieee80211_free_node(ni);
- return error;
-}
-
/*
* Announce various information on device/driver attach.
*/
diff --git a/sys/dev/ath/if_ath_misc.h b/sys/dev/ath/if_ath_misc.h
new file mode 100644
index 0000000..2700ed0
--- /dev/null
+++ b/sys/dev/ath/if_ath_misc.h
@@ -0,0 +1,56 @@
+/*-
+ * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ * redistribution must be conditioned upon including a substantially
+ * similar Disclaimer requirement for further binary redistribution.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $FreeBSD$
+ */
+#ifndef __IF_ATH_MISC_H__
+#define __IF_ATH_MISC_H__
+
+/*
+ * This is where definitions for "public things" in if_ath.c
+ * will go for the time being.
+ *
+ * Anything in here should eventually be moved out of if_ath.c
+ * and into something else.
+ */
+
+/* unaligned little endian access */
+#define LE_READ_2(p) \
+ ((u_int16_t) \
+ ((((u_int8_t *)(p))[0] ) | (((u_int8_t *)(p))[1] << 8)))
+#define LE_READ_4(p) \
+ ((u_int32_t) \
+ ((((u_int8_t *)(p))[0] ) | (((u_int8_t *)(p))[1] << 8) | \
+ (((u_int8_t *)(p))[2] << 16) | (((u_int8_t *)(p))[3] << 24)))
+
+extern int ath_tx_findrix(const struct ath_softc *sc, uint8_t rate);
+
+extern struct ath_buf * ath_getbuf(struct ath_softc *sc);
+extern struct ath_buf * _ath_getbuf_locked(struct ath_softc *sc);
+
+#endif
diff --git a/sys/dev/ath/if_ath_tx.c b/sys/dev/ath/if_ath_tx.c
new file mode 100644
index 0000000..5568f43
--- /dev/null
+++ b/sys/dev/ath/if_ath_tx.c
@@ -0,0 +1,1029 @@
+/*-
+ * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ * redistribution must be conditioned upon including a substantially
+ * similar Disclaimer requirement for further binary redistribution.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Driver for the Atheros Wireless LAN controller.
+ *
+ * This software is derived from work of Atsushi Onoe; his contribution
+ * is greatly appreciated.
+ */
+
+#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 <machine/bus.h>
+
+#include <net/if.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 <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> /* XXX for softled */
+#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>
+
+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);
+
+ STAILQ_FOREACH_SAFE(bf, frags, bf_list, next) {
+ /* NB: bf assumed clean */
+ STAILQ_REMOVE_HEAD(frags, bf_list);
+ STAILQ_INSERT_HEAD(&sc->sc_txbuf, bf, bf_list);
+ ieee80211_node_decref(ni);
+ }
+}
+
+/*
+ * Setup xmit of a fragmented frame. Allocate a buffer
+ * for each frag and bump the node reference count to
+ * reflect the held reference to be setup by ath_tx_start.
+ */
+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);
+ if (bf == NULL) { /* out of buffers, cleanup */
+ ath_txfrag_cleanup(sc, frags, ni);
+ break;
+ }
+ ieee80211_node_incref(ni);
+ STAILQ_INSERT_TAIL(frags, bf, bf_list);
+ }
+ ATH_TXBUF_UNLOCK(sc);
+
+ return !STAILQ_EMPTY(frags);
+}
+
+/*
+ * Reclaim mbuf resources. For fragmented frames we
+ * need to claim each frag chained with m_nextpkt.
+ */
+void
+ath_freetx(struct mbuf *m)
+{
+ struct mbuf *next;
+
+ do {
+ next = m->m_nextpkt;
+ m->m_nextpkt = NULL;
+ m_freem(m);
+ } while ((m = next) != NULL);
+}
+
+static int
+ath_tx_dmasetup(struct ath_softc *sc, struct ath_buf *bf, struct mbuf *m0)
+{
+ struct mbuf *m;
+ int error;
+
+ /*
+ * Load the DMA map so any coalescing is done. This
+ * also calculates the number of descriptors we need.
+ */
+ 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) {
+ /* XXX packet requires too many descriptors */
+ bf->bf_nseg = ATH_TXDESC+1;
+ } else if (error != 0) {
+ sc->sc_stats.ast_tx_busdma++;
+ ath_freetx(m0);
+ return error;
+ }
+ /*
+ * Discard null packets and check for packets that
+ * require too many TX descriptors. We try to convert
+ * the latter to a cluster.
+ */
+ if (bf->bf_nseg > ATH_TXDESC) { /* too many desc's, linearize */
+ sc->sc_stats.ast_tx_linear++;
+ m = m_collapse(m0, M_DONTWAIT, ATH_TXDESC);
+ if (m == NULL) {
+ ath_freetx(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++;
+ ath_freetx(m0);
+ return error;
+ }
+ KASSERT(bf->bf_nseg <= ATH_TXDESC,
+ ("too many segments after defrag; nseg %u", bf->bf_nseg));
+ } else if (bf->bf_nseg == 0) { /* null packet, discard */
+ sc->sc_stats.ast_tx_nodata++;
+ ath_freetx(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_handoff(struct ath_softc *sc, struct ath_txq *txq, struct ath_buf *bf)
+{
+ struct ath_hal *ah = sc->sc_ah;
+ struct ath_desc *ds, *ds0;
+ int i;
+
+ /*
+ * Fillin the remainder of the descriptor info.
+ */
+ ds0 = ds = bf->bf_desc;
+ for (i = 0; i < bf->bf_nseg; i++, ds++) {
+ ds->ds_data = bf->bf_segs[i].ds_addr;
+ if (i == bf->bf_nseg - 1)
+ ds->ds_link = 0;
+ else
+ ds->ds_link = bf->bf_daddr + sizeof(*ds) * (i + 1);
+ ath_hal_filltxdesc(ah, ds
+ , bf->bf_segs[i].ds_len /* segment length */
+ , i == 0 /* first segment */
+ , i == bf->bf_nseg - 1 /* last segment */
+ , ds0 /* first descriptor */
+ );
+ DPRINTF(sc, ATH_DEBUG_XMIT,
+ "%s: %d: %08x %08x %08x %08x %08x %08x\n",
+ __func__, i, ds->ds_link, ds->ds_data,
+ ds->ds_ctl0, ds->ds_ctl1, ds->ds_hw[0], ds->ds_hw[1]);
+ }
+ /*
+ * Insert the frame on the outbound list and pass it on
+ * to the hardware. Multicast frames buffered for power
+ * save stations and transmit from the CAB queue are stored
+ * on a s/w only queue and loaded on to the CAB queue in
+ * the SWBA handler since frames only go out on DTIM and
+ * to avoid possible races.
+ */
+ ATH_TXQ_LOCK(txq);
+ KASSERT((bf->bf_flags & ATH_BUF_BUSY) == 0,
+ ("busy status 0x%x", bf->bf_flags));
+ if (txq->axq_qnum != ATH_TXQ_SWQ) {
+#ifdef IEEE80211_SUPPORT_TDMA
+ int qbusy;
+
+ ATH_TXQ_INSERT_TAIL(txq, bf, bf_list);
+ qbusy = ath_hal_txqenabled(ah, txq->axq_qnum);
+ if (txq->axq_link == NULL) {
+ /*
+ * Be careful writing the address to TXDP. If
+ * the tx q is enabled then this write will be
+ * ignored. Normally this is not an issue but
+ * when tdma is in use and the q is beacon gated
+ * this race can occur. If the q is busy then
+ * defer the work to later--either when another
+ * packet comes along or when we prepare a beacon
+ * frame at SWBA.
+ */
+ if (!qbusy) {
+ ath_hal_puttxbuf(ah, txq->axq_qnum, bf->bf_daddr);
+ txq->axq_flags &= ~ATH_TXQ_PUTPENDING;
+ DPRINTF(sc, ATH_DEBUG_XMIT,
+ "%s: TXDP[%u] = %p (%p) depth %d\n",
+ __func__, txq->axq_qnum,
+ (caddr_t)bf->bf_daddr, bf->bf_desc,
+ txq->axq_depth);
+ } else {
+ txq->axq_flags |= ATH_TXQ_PUTPENDING;
+ DPRINTF(sc, ATH_DEBUG_TDMA | ATH_DEBUG_XMIT,
+ "%s: Q%u busy, defer enable\n", __func__,
+ txq->axq_qnum);
+ }
+ } else {
+ *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);
+ if ((txq->axq_flags & ATH_TXQ_PUTPENDING) && !qbusy) {
+ /*
+ * The q was busy when we previously tried
+ * to write the address of the first buffer
+ * in the chain. Since it's not busy now
+ * handle this chore. We are certain the
+ * buffer at the front is the right one since
+ * axq_link is NULL only when the buffer list
+ * is/was empty.
+ */
+ ath_hal_puttxbuf(ah, txq->axq_qnum,
+ STAILQ_FIRST(&txq->axq_q)->bf_daddr);
+ txq->axq_flags &= ~ATH_TXQ_PUTPENDING;
+ DPRINTF(sc, ATH_DEBUG_TDMA | ATH_DEBUG_XMIT,
+ "%s: Q%u restarted\n", __func__,
+ txq->axq_qnum);
+ }
+ }
+#else
+ ATH_TXQ_INSERT_TAIL(txq, bf, bf_list);
+ if (txq->axq_link == NULL) {
+ ath_hal_puttxbuf(ah, txq->axq_qnum, bf->bf_daddr);
+ DPRINTF(sc, ATH_DEBUG_XMIT,
+ "%s: TXDP[%u] = %p (%p) depth %d\n",
+ __func__, txq->axq_qnum,
+ (caddr_t)bf->bf_daddr, bf->bf_desc,
+ txq->axq_depth);
+ } else {
+ *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);
+ }
+#endif /* IEEE80211_SUPPORT_TDMA */
+ txq->axq_link = &bf->bf_desc[bf->bf_nseg - 1].ds_link;
+ ath_hal_txstart(ah, txq->axq_qnum);
+ } else {
+ if (txq->axq_link != NULL) {
+ struct ath_buf *last = ATH_TXQ_LAST(txq);
+ struct ieee80211_frame *wh;
+
+ /* mark previous frame */
+ wh = mtod(last->bf_m, struct ieee80211_frame *);
+ wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA;
+ bus_dmamap_sync(sc->sc_dmat, last->bf_dmamap,
+ BUS_DMASYNC_PREWRITE);
+
+ /* link descriptor */
+ *txq->axq_link = bf->bf_daddr;
+ }
+ ATH_TXQ_INSERT_TAIL(txq, bf, bf_list);
+ txq->axq_link = &bf->bf_desc[bf->bf_nseg - 1].ds_link;
+ }
+ ATH_TXQ_UNLOCK(txq);
+}
+
+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);
+ struct ath_hal *ah = sc->sc_ah;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ const struct chanAccParams *cap = &ic->ic_wme.wme_chanParams;
+ int error, iswep, ismcast, isfrag, ismrr;
+ int keyix, hdrlen, pktlen, try0;
+ u_int8_t rix, txrate, ctsrate;
+ u_int8_t cix = 0xff; /* NB: silence compiler */
+ struct ath_desc *ds;
+ struct ath_txq *txq;
+ struct ieee80211_frame *wh;
+ u_int subtype, flags, ctsduration;
+ HAL_PKT_TYPE atype;
+ const HAL_RATE_TABLE *rt;
+ HAL_BOOL shortPreamble;
+ struct ath_node *an;
+ u_int pri;
+
+ wh = mtod(m0, struct ieee80211_frame *);
+ iswep = wh->i_fc[1] & IEEE80211_FC1_WEP;
+ ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1);
+ isfrag = m0->m_flags & M_FRAG;
+ hdrlen = ieee80211_anyhdrsize(wh);
+ /*
+ * Packet length must not include any
+ * pad bytes; deduct them here.
+ */
+ pktlen = m0->m_pkthdr.len - (hdrlen & 3);
+
+ if (iswep) {
+ const struct ieee80211_cipher *cip;
+ struct ieee80211_key *k;
+
+ /*
+ * Construct the 802.11 header+trailer for an encrypted
+ * frame. The only reason this can fail is because of an
+ * unknown or unsupported cipher/key type.
+ */
+ k = ieee80211_crypto_encap(ni, m0);
+ if (k == NULL) {
+ /*
+ * This can happen when the key is yanked after the
+ * frame was queued. Just discard the frame; the
+ * 802.11 layer counts failures and provides
+ * debugging/diagnostics.
+ */
+ ath_freetx(m0);
+ return EIO;
+ }
+ /*
+ * Adjust the packet + header lengths for the crypto
+ * additions and calculate the h/w key index. When
+ * a s/w mic is done the frame will have had any mic
+ * added to it prior to entry so m0->m_pkthdr.len will
+ * account for it. Otherwise we need to add it to the
+ * packet length.
+ */
+ cip = k->wk_cipher;
+ hdrlen += cip->ic_header;
+ pktlen += cip->ic_header + cip->ic_trailer;
+ /* NB: frags always have any TKIP MIC done in s/w */
+ if ((k->wk_flags & IEEE80211_KEY_SWMIC) == 0 && !isfrag)
+ pktlen += cip->ic_miclen;
+ keyix = k->wk_keyix;
+
+ /* packet header may have moved, reset our local pointer */
+ wh = mtod(m0, struct ieee80211_frame *);
+ } else if (ni->ni_ucastkey.wk_cipher == &ieee80211_cipher_none) {
+ /*
+ * Use station key cache slot, if assigned.
+ */
+ keyix = ni->ni_ucastkey.wk_keyix;
+ if (keyix == IEEE80211_KEYIX_NONE)
+ keyix = HAL_TXKEYIX_INVALID;
+ } else
+ keyix = HAL_TXKEYIX_INVALID;
+
+ pktlen += IEEE80211_CRC_LEN;
+
+ /*
+ * Load the DMA map so any coalescing is done. This
+ * also calculates the number of descriptors we need.
+ */
+ error = ath_tx_dmasetup(sc, bf, m0);
+ if (error != 0)
+ return error;
+ bf->bf_node = ni; /* NB: held reference */
+ m0 = bf->bf_m; /* NB: may have changed */
+ wh = mtod(m0, struct ieee80211_frame *);
+
+ /* setup descriptors */
+ ds = bf->bf_desc;
+ rt = sc->sc_currates;
+ KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode));
+
+ /*
+ * NB: the 802.11 layer marks whether or not we should
+ * use short preamble based on the current mode and
+ * negotiated parameters.
+ */
+ 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 = HAL_TXDESC_CLRDMASK; /* XXX needed for crypto errs */
+ ismrr = 0; /* default no multi-rate retry*/
+ pri = M_WME_GETAC(m0); /* honor classification */
+ /* XXX use txparams instead of fixed values */
+ /*
+ * Calculate Atheros packet type from IEEE80211 packet header,
+ * setup for rate calculations, and select h/w transmit queue.
+ */
+ 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; /* XXX */
+ rix = an->an_mgmtrix;
+ txrate = rt->info[rix].rateCode;
+ if (shortPreamble)
+ txrate |= rt->info[rix].shortPreamble;
+ try0 = ATH_TXMGTTRY;
+ flags |= HAL_TXDESC_INTREQ; /* force interrupt */
+ break;
+ case IEEE80211_FC0_TYPE_CTL:
+ atype = HAL_PKT_TYPE_PSPOLL; /* stop setting of duration */
+ rix = an->an_mgmtrix;
+ txrate = rt->info[rix].rateCode;
+ if (shortPreamble)
+ txrate |= rt->info[rix].shortPreamble;
+ try0 = ATH_TXMGTTRY;
+ flags |= HAL_TXDESC_INTREQ; /* force interrupt */
+ break;
+ case IEEE80211_FC0_TYPE_DATA:
+ atype = HAL_PKT_TYPE_NORMAL; /* default */
+ /*
+ * Data frames: multicast frames go out at a fixed rate,
+ * EAPOL frames use the mgmt frame rate; otherwise consult
+ * the rate control module for the rate to use.
+ */
+ 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) {
+ /* XXX? maybe always use long preamble? */
+ rix = an->an_mgmtrix;
+ txrate = rt->info[rix].rateCode;
+ if (shortPreamble)
+ txrate |= rt->info[rix].shortPreamble;
+ try0 = ATH_TXMAXTRY; /* XXX?too many? */
+ } else {
+ ath_rate_findrate(sc, an, shortPreamble, pktlen,
+ &rix, &try0, &txrate);
+ sc->sc_txrix = rix; /* for LED blinking */
+ sc->sc_lastdatarix = rix; /* for fast frames */
+ if (try0 != ATH_TXMAXTRY)
+ ismrr = 1;
+ }
+ if (cap->cap_wmeParams[pri].wmep_noackPolicy)
+ flags |= HAL_TXDESC_NOACK;
+ break;
+ default:
+ if_printf(ifp, "bogus frame type 0x%x (%s)\n",
+ wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK, __func__);
+ /* XXX statistic */
+ ath_freetx(m0);
+ return EIO;
+ }
+ txq = sc->sc_ac2q[pri];
+
+ /*
+ * When servicing one or more stations in power-save mode
+ * (or) if there is some mcast data waiting on the mcast
+ * queue (to prevent out of order delivery) multicast
+ * frames must be buffered until after the beacon.
+ */
+ if (ismcast && (vap->iv_ps_sta || avp->av_mcastq.axq_depth))
+ txq = &avp->av_mcastq;
+
+ /*
+ * Calculate miscellaneous flags.
+ */
+ if (ismcast) {
+ flags |= HAL_TXDESC_NOACK; /* no ack on broad/multicast */
+ } else if (pktlen > vap->iv_rtsthreshold &&
+ (ni->ni_ath_flags & IEEE80211_NODE_FF) == 0) {
+ flags |= HAL_TXDESC_RTSENA; /* RTS based on frame length */
+ cix = rt->info[rix].controlRate;
+ sc->sc_stats.ast_tx_rts++;
+ }
+ if (flags & HAL_TXDESC_NOACK) /* NB: avoid double counting */
+ 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++;
+ ath_freetx(m0);
+ return EIO;
+ }
+#endif
+
+ /*
+ * If 802.11g protection is enabled, determine whether
+ * to use RTS/CTS or just CTS. Note that this is only
+ * done for OFDM unicast frames.
+ */
+ if ((ic->ic_flags & IEEE80211_F_USEPROT) &&
+ rt->info[rix].phy == IEEE80211_T_OFDM &&
+ (flags & HAL_TXDESC_NOACK) == 0) {
+ /* XXX fragments must use CCK rates w/ protection */
+ if (ic->ic_protmode == IEEE80211_PROT_RTSCTS)
+ flags |= HAL_TXDESC_RTSENA;
+ else if (ic->ic_protmode == IEEE80211_PROT_CTSONLY)
+ flags |= HAL_TXDESC_CTSENA;
+ if (isfrag) {
+ /*
+ * For frags it would be desirable to use the
+ * highest CCK rate for RTS/CTS. But stations
+ * farther away may detect it at a lower CCK rate
+ * so use the configured protection rate instead
+ * (for now).
+ */
+ cix = rt->info[sc->sc_protrix].controlRate;
+ } else
+ cix = rt->info[sc->sc_protrix].controlRate;
+ sc->sc_stats.ast_tx_protect++;
+ }
+
+ /*
+ * Calculate duration. This logically belongs in the 802.11
+ * layer but it lacks sufficient information to calculate it.
+ */
+ if ((flags & HAL_TXDESC_NOACK) == 0 &&
+ (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_CTL) {
+ 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; /* additional SIFS+ACK */
+ KASSERT(m0->m_nextpkt != NULL, ("no fragment"));
+ /*
+ * Include the size of next fragment so NAV is
+ * updated properly. The last fragment uses only
+ * the ACK duration
+ */
+ dur += ath_hal_computetxtime(ah, rt,
+ m0->m_nextpkt->m_pkthdr.len,
+ rix, shortPreamble);
+ }
+ if (isfrag) {
+ /*
+ * Force hardware to use computed duration for next
+ * fragment by disabling multi-rate retry which updates
+ * duration based on the multi-rate duration table.
+ */
+ ismrr = 0;
+ try0 = ATH_TXMGTTRY; /* XXX? */
+ }
+ *(u_int16_t *)wh->i_dur = htole16(dur);
+ }
+
+ /*
+ * Calculate RTS/CTS rate and duration if needed.
+ */
+ ctsduration = 0;
+ if (flags & (HAL_TXDESC_RTSENA|HAL_TXDESC_CTSENA)) {
+ /*
+ * CTS transmit rate is derived from the transmit rate
+ * by looking in the h/w rate table. We must also factor
+ * in whether or not a short preamble is to be used.
+ */
+ /* NB: cix is set above where RTS/CTS is enabled */
+ KASSERT(cix != 0xff, ("cix not setup"));
+ ctsrate = rt->info[cix].rateCode;
+ /*
+ * Compute the transmit duration based on the frame
+ * size and the size of an ACK frame. We call into the
+ * HAL to do the computation since it depends on the
+ * characteristics of the actual PHY being used.
+ *
+ * NB: CTS is assumed the same size as an ACK so we can
+ * use the precalculated ACK durations.
+ */
+ if (shortPreamble) {
+ ctsrate |= rt->info[cix].shortPreamble;
+ if (flags & HAL_TXDESC_RTSENA) /* SIFS + CTS */
+ ctsduration += rt->info[cix].spAckDuration;
+ ctsduration += ath_hal_computetxtime(ah,
+ rt, pktlen, rix, AH_TRUE);
+ if ((flags & HAL_TXDESC_NOACK) == 0) /* SIFS + ACK */
+ ctsduration += rt->info[rix].spAckDuration;
+ } else {
+ if (flags & HAL_TXDESC_RTSENA) /* SIFS + CTS */
+ ctsduration += rt->info[cix].lpAckDuration;
+ ctsduration += ath_hal_computetxtime(ah,
+ rt, pktlen, rix, AH_FALSE);
+ if ((flags & HAL_TXDESC_NOACK) == 0) /* SIFS + ACK */
+ ctsduration += rt->info[rix].lpAckDuration;
+ }
+ /*
+ * Must disable multi-rate retry when using RTS/CTS.
+ */
+ ismrr = 0;
+ try0 = ATH_TXMGTTRY; /* XXX */
+ } else
+ ctsrate = 0;
+
+ /*
+ * At this point we are committed to sending the frame
+ * and we don't need to look at m_nextpkt; clear it in
+ * case this frame is part of frag chain.
+ */
+ 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)) {
+ u_int64_t tsf = ath_hal_gettsf64(ah);
+
+ sc->sc_tx_th.wt_tsf = htole64(tsf);
+ 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 = ni->ni_txpower;
+ sc->sc_tx_th.wt_antenna = sc->sc_txantenna;
+
+ ieee80211_radiotap_tx(vap, m0);
+ }
+
+ /*
+ * Determine if a tx interrupt should be generated for
+ * this descriptor. We take a tx interrupt to reap
+ * descriptors when the h/w hits an EOL condition or
+ * when the descriptor is specifically marked to generate
+ * an interrupt. We periodically mark descriptors in this
+ * way to insure timely replenishing of the supply needed
+ * for sending frames. Defering interrupts reduces system
+ * load and potentially allows more concurrent work to be
+ * done but if done to aggressively can cause senders to
+ * backup.
+ *
+ * NB: use >= to deal with sc_txintrperiod changing
+ * dynamically through sysctl.
+ */
+ 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;
+ }
+
+ /*
+ * Formulate first tx descriptor with tx controls.
+ */
+ /* XXX check return value? */
+ ath_hal_setuptxdesc(ah, ds
+ , pktlen /* packet length */
+ , hdrlen /* header length */
+ , atype /* Atheros packet type */
+ , ni->ni_txpower /* txpower */
+ , txrate, try0 /* series 0 rate/tries */
+ , keyix /* key cache index */
+ , sc->sc_txantenna /* antenna mode */
+ , flags /* flags */
+ , ctsrate /* rts/cts rate */
+ , ctsduration /* rts/cts duration */
+ );
+ bf->bf_txflags = flags;
+ /*
+ * Setup the multi-rate retry state only when we're
+ * going to use it. This assumes ath_hal_setuptxdesc
+ * initializes the descriptors (so we don't have to)
+ * when the hardware supports multi-rate retry and
+ * we don't use it.
+ */
+ if (ismrr)
+ ath_rate_setupxtxdesc(sc, an, ds, shortPreamble, rix);
+
+ ath_tx_handoff(sc, txq, bf);
+ 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 ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ath_hal *ah = sc->sc_ah;
+ struct ieee80211vap *vap = ni->ni_vap;
+ int error, ismcast, ismrr;
+ int keyix, hdrlen, pktlen, try0, txantenna;
+ u_int8_t rix, cix, txrate, ctsrate, rate1, rate2, rate3;
+ struct ieee80211_frame *wh;
+ u_int flags, ctsduration;
+ HAL_PKT_TYPE atype;
+ const HAL_RATE_TABLE *rt;
+ struct ath_desc *ds;
+ u_int pri;
+
+ wh = mtod(m0, struct ieee80211_frame *);
+ ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1);
+ hdrlen = ieee80211_anyhdrsize(wh);
+ /*
+ * Packet length must not include any
+ * pad bytes; deduct them here.
+ */
+ /* XXX honor IEEE80211_BPF_DATAPAD */
+ pktlen = m0->m_pkthdr.len - (hdrlen & 3) + IEEE80211_CRC_LEN;
+
+ if (params->ibp_flags & IEEE80211_BPF_CRYPTO) {
+ const struct ieee80211_cipher *cip;
+ struct ieee80211_key *k;
+
+ /*
+ * Construct the 802.11 header+trailer for an encrypted
+ * frame. The only reason this can fail is because of an
+ * unknown or unsupported cipher/key type.
+ */
+ k = ieee80211_crypto_encap(ni, m0);
+ if (k == NULL) {
+ /*
+ * This can happen when the key is yanked after the
+ * frame was queued. Just discard the frame; the
+ * 802.11 layer counts failures and provides
+ * debugging/diagnostics.
+ */
+ ath_freetx(m0);
+ return EIO;
+ }
+ /*
+ * Adjust the packet + header lengths for the crypto
+ * additions and calculate the h/w key index. When
+ * a s/w mic is done the frame will have had any mic
+ * added to it prior to entry so m0->m_pkthdr.len will
+ * account for it. Otherwise we need to add it to the
+ * packet length.
+ */
+ cip = k->wk_cipher;
+ hdrlen += cip->ic_header;
+ pktlen += cip->ic_header + cip->ic_trailer;
+ /* NB: frags always have any TKIP MIC done in s/w */
+ if ((k->wk_flags & IEEE80211_KEY_SWMIC) == 0)
+ pktlen += cip->ic_miclen;
+ keyix = k->wk_keyix;
+
+ /* packet header may have moved, reset our local pointer */
+ wh = mtod(m0, struct ieee80211_frame *);
+ } else if (ni->ni_ucastkey.wk_cipher == &ieee80211_cipher_none) {
+ /*
+ * Use station key cache slot, if assigned.
+ */
+ keyix = ni->ni_ucastkey.wk_keyix;
+ if (keyix == IEEE80211_KEYIX_NONE)
+ keyix = HAL_TXKEYIX_INVALID;
+ } else
+ keyix = HAL_TXKEYIX_INVALID;
+
+ error = ath_tx_dmasetup(sc, bf, m0);
+ if (error != 0)
+ return error;
+ m0 = bf->bf_m; /* NB: may have changed */
+ wh = mtod(m0, struct ieee80211_frame *);
+ bf->bf_node = ni; /* NB: held reference */
+
+ flags = HAL_TXDESC_CLRDMASK; /* XXX needed for crypto errs */
+ flags |= HAL_TXDESC_INTREQ; /* force interrupt */
+ if (params->ibp_flags & IEEE80211_BPF_RTS)
+ flags |= HAL_TXDESC_RTSENA;
+ else if (params->ibp_flags & IEEE80211_BPF_CTS)
+ flags |= HAL_TXDESC_CTSENA;
+ /* XXX leave ismcast to injector? */
+ 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);
+ txrate = rt->info[rix].rateCode;
+ if (params->ibp_flags & IEEE80211_BPF_SHORTPRE)
+ txrate |= rt->info[rix].shortPreamble;
+ sc->sc_txrix = rix;
+ try0 = params->ibp_try0;
+ ismrr = (params->ibp_try1 != 0);
+ txantenna = params->ibp_pri >> 2;
+ if (txantenna == 0) /* XXX? */
+ txantenna = sc->sc_txantenna;
+ ctsduration = 0;
+ if (flags & (HAL_TXDESC_CTSENA | HAL_TXDESC_RTSENA)) {
+ cix = ath_tx_findrix(sc, params->ibp_ctsrate);
+ ctsrate = rt->info[cix].rateCode;
+ if (params->ibp_flags & IEEE80211_BPF_SHORTPRE) {
+ ctsrate |= rt->info[cix].shortPreamble;
+ if (flags & HAL_TXDESC_RTSENA) /* SIFS + CTS */
+ ctsduration += rt->info[cix].spAckDuration;
+ ctsduration += ath_hal_computetxtime(ah,
+ rt, pktlen, rix, AH_TRUE);
+ if ((flags & HAL_TXDESC_NOACK) == 0) /* SIFS + ACK */
+ ctsduration += rt->info[rix].spAckDuration;
+ } else {
+ if (flags & HAL_TXDESC_RTSENA) /* SIFS + CTS */
+ ctsduration += rt->info[cix].lpAckDuration;
+ ctsduration += ath_hal_computetxtime(ah,
+ rt, pktlen, rix, AH_FALSE);
+ if ((flags & HAL_TXDESC_NOACK) == 0) /* SIFS + ACK */
+ ctsduration += rt->info[rix].lpAckDuration;
+ }
+ ismrr = 0; /* XXX */
+ } else
+ ctsrate = 0;
+ pri = params->ibp_pri & 3;
+ /*
+ * NB: we mark all packets as type PSPOLL so the h/w won't
+ * set the sequence number, duration, etc.
+ */
+ 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)) {
+ u_int64_t tsf = ath_hal_gettsf64(ah);
+
+ sc->sc_tx_th.wt_tsf = htole64(tsf);
+ sc->sc_tx_th.wt_flags = sc->sc_hwmap[rix].txflags;
+ if (wh->i_fc[1] & IEEE80211_FC1_WEP)
+ 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 = ni->ni_txpower;
+ sc->sc_tx_th.wt_antenna = sc->sc_txantenna;
+
+ ieee80211_radiotap_tx(vap, m0);
+ }
+
+ /*
+ * Formulate first tx descriptor with tx controls.
+ */
+ ds = bf->bf_desc;
+ /* XXX check return value? */
+ ath_hal_setuptxdesc(ah, ds
+ , pktlen /* packet length */
+ , hdrlen /* header length */
+ , atype /* Atheros packet type */
+ , params->ibp_power /* txpower */
+ , txrate, try0 /* series 0 rate/tries */
+ , keyix /* key cache index */
+ , txantenna /* antenna mode */
+ , flags /* flags */
+ , ctsrate /* rts/cts rate */
+ , ctsduration /* rts/cts duration */
+ );
+ bf->bf_txflags = flags;
+
+ if (ismrr) {
+ rix = ath_tx_findrix(sc, params->ibp_rate1);
+ rate1 = rt->info[rix].rateCode;
+ if (params->ibp_flags & IEEE80211_BPF_SHORTPRE)
+ rate1 |= rt->info[rix].shortPreamble;
+ if (params->ibp_try2) {
+ rix = ath_tx_findrix(sc, params->ibp_rate2);
+ rate2 = rt->info[rix].rateCode;
+ if (params->ibp_flags & IEEE80211_BPF_SHORTPRE)
+ rate2 |= rt->info[rix].shortPreamble;
+ } else
+ rate2 = 0;
+ if (params->ibp_try3) {
+ rix = ath_tx_findrix(sc, params->ibp_rate3);
+ rate3 = rt->info[rix].rateCode;
+ if (params->ibp_flags & IEEE80211_BPF_SHORTPRE)
+ rate3 |= rt->info[rix].shortPreamble;
+ } else
+ rate3 = 0;
+ ath_hal_setupxtxdesc(ah, ds
+ , rate1, params->ibp_try1 /* series 1 */
+ , rate2, params->ibp_try2 /* series 2 */
+ , rate3, params->ibp_try3 /* series 3 */
+ );
+ }
+
+ /* NB: no buffered multicast in power save support */
+ ath_tx_handoff(sc, sc->sc_ac2q[pri], bf);
+ 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 ifnet *ifp = ic->ic_ifp;
+ struct ath_softc *sc = ifp->if_softc;
+ struct ath_buf *bf;
+ int error;
+
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || sc->sc_invalid) {
+ DPRINTF(sc, ATH_DEBUG_XMIT, "%s: discard frame, %s", __func__,
+ (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 ?
+ "!running" : "invalid");
+ m_freem(m);
+ error = ENETDOWN;
+ goto bad;
+ }
+ /*
+ * Grab a TX buffer and associated resources.
+ */
+ bf = ath_getbuf(sc);
+ if (bf == NULL) {
+ sc->sc_stats.ast_tx_nobuf++;
+ m_freem(m);
+ error = ENOBUFS;
+ goto bad;
+ }
+
+ if (params == NULL) {
+ /*
+ * Legacy path; interpret frame contents to decide
+ * precisely how to send the frame.
+ */
+ if (ath_tx_start(sc, ni, bf, m)) {
+ error = EIO; /* XXX */
+ goto bad2;
+ }
+ } else {
+ /*
+ * Caller supplied explicit parameters to use in
+ * sending the frame.
+ */
+ if (ath_tx_raw_start(sc, ni, bf, m, params)) {
+ error = EIO; /* XXX */
+ goto bad2;
+ }
+ }
+ sc->sc_wd_timer = 5;
+ ifp->if_opackets++;
+ sc->sc_stats.ast_tx_raw++;
+
+ return 0;
+bad2:
+ ATH_TXBUF_LOCK(sc);
+ STAILQ_INSERT_HEAD(&sc->sc_txbuf, bf, bf_list);
+ ATH_TXBUF_UNLOCK(sc);
+bad:
+ ifp->if_oerrors++;
+ sc->sc_stats.ast_tx_raw_fail++;
+ ieee80211_free_node(ni);
+ return error;
+}
diff --git a/sys/dev/ath/if_ath_tx.h b/sys/dev/ath/if_ath_tx.h
new file mode 100644
index 0000000..e181f7a
--- /dev/null
+++ b/sys/dev/ath/if_ath_tx.h
@@ -0,0 +1,44 @@
+/*-
+ * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ * redistribution must be conditioned upon including a substantially
+ * similar Disclaimer requirement for further binary redistribution.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $FreeBSD$
+ */
+#ifndef __IF_ATH_TX_H__
+#define __IF_ATH_TX_H__
+
+extern void ath_freetx(struct mbuf *m);
+extern void ath_txfrag_cleanup(struct ath_softc *sc, ath_bufhead *frags,
+ struct ieee80211_node *ni);
+extern int ath_txfrag_setup(struct ath_softc *sc, ath_bufhead *frags,
+ struct mbuf *m0, struct ieee80211_node *ni);
+extern int ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni,
+ struct ath_buf *bf, struct mbuf *m0);
+extern int ath_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
+ const struct ieee80211_bpf_params *params);
+
+#endif
diff --git a/sys/modules/ath/Makefile b/sys/modules/ath/Makefile
index 44f5d89..8448e12 100644
--- a/sys/modules/ath/Makefile
+++ b/sys/modules/ath/Makefile
@@ -35,7 +35,7 @@ ATH_RATE?= sample # tx rate control algorithm
.PATH: ${.CURDIR}/../../dev/ath/ath_hal
KMOD= if_ath
-SRCS= if_ath.c if_ath_pci.c if_ath_debug.c
+SRCS= if_ath.c if_ath_pci.c if_ath_debug.c if_ath_tx.c
# NB: v3 eeprom support used by both AR5211 and AR5212; just include it
SRCS+= ah_osdep.c ah.c ah_regdomain.c ah_eeprom_v3.c
SRCS+= device_if.h bus_if.h pci_if.h opt_inet.h opt_ath.h opt_ah.h opt_wlan.h
OpenPOWER on IntegriCloud