diff options
author | adrian <adrian@FreeBSD.org> | 2011-01-29 11:35:23 +0000 |
---|---|---|
committer | adrian <adrian@FreeBSD.org> | 2011-01-29 11:35:23 +0000 |
commit | 05a9c90aff7fc24d4ff0c691901e9a44607ab93d (patch) | |
tree | 0855be70ab386f365706ad46e7bd48b820580f7c | |
parent | cc338a54f711e91735e9c9640940ef576495186c (diff) | |
download | FreeBSD-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/files | 2 | ||||
-rw-r--r-- | sys/dev/ath/if_ath.c | 957 | ||||
-rw-r--r-- | sys/dev/ath/if_ath_misc.h | 56 | ||||
-rw-r--r-- | sys/dev/ath/if_ath_tx.c | 1029 | ||||
-rw-r--r-- | sys/dev/ath/if_ath_tx.h | 44 | ||||
-rw-r--r-- | sys/modules/ath/Makefile | 2 |
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 |