diff options
author | sam <sam@FreeBSD.org> | 2006-08-05 05:07:17 +0000 |
---|---|---|
committer | sam <sam@FreeBSD.org> | 2006-08-05 05:07:17 +0000 |
commit | e835c6590d39961282197e3226709b5697f85562 (patch) | |
tree | 0774a424ba55ec998c1f4db1c799a44d8fbe00d4 | |
parent | 850b9c89c40046982f5e1947289cee5e825f307c (diff) | |
download | FreeBSD-src-e835c6590d39961282197e3226709b5697f85562.zip FreeBSD-src-e835c6590d39961282197e3226709b5697f85562.tar.gz |
raw 802.11 packet transmit support
Joint work with: Andrea Bittau <a.bittau@cs.ucl.ac.uk>
-rw-r--r-- | sys/dev/ath/if_ath.c | 455 | ||||
-rw-r--r-- | sys/dev/ath/if_athioctl.h | 3 |
2 files changed, 357 insertions, 101 deletions
diff --git a/sys/dev/ath/if_ath.c b/sys/dev/ath/if_ath.c index f7d6e28..2aa3fcc 100644 --- a/sys/dev/ath/if_ath.c +++ b/sys/dev/ath/if_ath.c @@ -180,6 +180,8 @@ 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_bpfattach(struct ath_softc *); static void ath_announce(struct ath_softc *); @@ -607,6 +609,7 @@ ath_attach(u_int16_t devid, struct ath_softc *sc) ic->ic_crypto.cs_key_set = ath_key_set; ic->ic_crypto.cs_key_update_begin = ath_key_update_begin; ic->ic_crypto.cs_key_update_end = ath_key_update_end; + ic->ic_raw_xmit = ath_raw_xmit; /* complete initialization */ ieee80211_media_init(ic, ath_media_change, ieee80211_media_status); @@ -3353,6 +3356,126 @@ ath_tx_findrix(const HAL_RATE_TABLE *rt, int rate) } 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++; + m_freem(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 = ath_defrag(m0, M_DONTWAIT, ATH_TXDESC); + if (m == NULL) { + m_freem(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++; + m_freem(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++; + m_freem(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); + ATH_TXQ_INSERT_TAIL(txq, bf, bf_list); + if (txq != &sc->sc_mcastq) { + 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); + } + 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) + *txq->axq_link = bf->bf_daddr; + 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) { @@ -3360,11 +3483,11 @@ ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf struct ath_hal *ah = sc->sc_ah; struct ifnet *ifp = sc->sc_ifp; const struct chanAccParams *cap = &ic->ic_wme.wme_chanParams; - int i, error, iswep, ismcast, ismrr; + int error, iswep, ismcast, ismrr; int keyix, hdrlen, pktlen, try0; u_int8_t rix, txrate, ctsrate; u_int8_t cix = 0xff; /* NB: silence compiler */ - struct ath_desc *ds, *ds0; + struct ath_desc *ds; struct ath_txq *txq; struct ieee80211_frame *wh; u_int subtype, flags, ctsduration; @@ -3372,7 +3495,6 @@ ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf const HAL_RATE_TABLE *rt; HAL_BOOL shortPreamble; struct ath_node *an; - struct mbuf *m; u_int pri; wh = mtod(m0, struct ieee80211_frame *); @@ -3438,50 +3560,10 @@ ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf * 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++; - m_freem(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 = ath_defrag(m0, M_DONTWAIT, ATH_TXDESC); - if (m == NULL) { - m_freem(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++; - m_freem(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++; - m_freem(m0); - return EIO; - } - DPRINTF(sc, ATH_DEBUG_XMIT, "%s: m %p len %u\n", __func__, m0, pktlen); - bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREWRITE); - bf->bf_m = m0; + error = ath_tx_dmasetup(sc, bf, m0); 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; @@ -3763,60 +3845,7 @@ ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf if (ismrr) ath_rate_setupxtxdesc(sc, an, ds, shortPreamble, rix); - /* - * Fillin the remainder of the descriptor info. - */ - ds0 = ds; - 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); - ATH_TXQ_INSERT_TAIL(txq, bf, bf_list); - if (txq != &sc->sc_mcastq) { - 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); - } - 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) - *txq->axq_link = bf->bf_daddr; - txq->axq_link = &bf->bf_desc[bf->bf_nseg - 1].ds_link; - } - ATH_TXQ_UNLOCK(txq); - + ath_tx_handoff(sc, txq, bf); return 0; } @@ -5480,6 +5509,232 @@ ath_bpfattach(struct ath_softc *sc) sc->sc_rx_th.wr_ihdr.it_present = htole32(ATH_RX_RADIOTAP_PRESENT); } +static int +ath_tx_raw_start(struct ath_softc *sc, struct ieee80211_node *ni, + struct ath_buf *bf, struct mbuf *m0, + const struct ieee80211_bpf_params *params) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ath_hal *ah = sc->sc_ah; + int error, ismcast, ismrr; + int hdrlen, pktlen, try0, txantenna; + u_int8_t rix, cix, txrate, ctsrate, rate1, rate2, rate3; + struct ath_txq *txq; + 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; + + 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(rt, params->ibp_rate0); + txrate = rt->info[rix].rateCode; + if (params->ibp_flags & IEEE80211_BPF_SHORTPRE) + txrate |= rt->info[rix].shortPreamble; + 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(rt, 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(mtod(m0, caddr_t), m0->m_len, + sc->sc_hwmap[txrate].ieeerate, -1); + + if (bpf_peers_present(ic->ic_rawbpf)) + bpf_mtap(ic->ic_rawbpf, m0); + if (bpf_peers_present(sc->sc_drvbpf)) { + 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[txrate].txflags; + if (wh->i_fc[1] & IEEE80211_FC1_WEP) + sc->sc_tx_th.wt_flags |= IEEE80211_RADIOTAP_F_WEP; + sc->sc_tx_th.wt_rate = sc->sc_hwmap[txrate].ieeerate; + sc->sc_tx_th.wt_txpower = ni->ni_txpower; + sc->sc_tx_th.wt_antenna = sc->sc_txantenna; + + bpf_mtap2(sc->sc_drvbpf, + &sc->sc_tx_th, sc->sc_tx_th_len, 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 */ + , HAL_TXKEYIX_INVALID /* key cache index */ + , txantenna /* antenna mode */ + , flags /* flags */ + , ctsrate /* rts/cts rate */ + , ctsduration /* rts/cts duration */ + ); + bf->bf_flags = flags; + + if (ismrr) { + rix = ath_tx_findrix(rt, 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(rt, 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(rt, 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 */ + ); + } + + /* + * 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. + */ + txq = sc->sc_ac2q[pri]; + if (ismcast && (ic->ic_ps_sta || sc->sc_mcastq.axq_depth)) + txq = &sc->sc_mcastq; + ath_tx_handoff(sc, txq, 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; + + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || sc->sc_invalid) { + m_freem(m); + return ENETDOWN; + } + /* + * Grab a TX buffer and associated resources. + */ + ATH_TXBUF_LOCK(sc); + bf = STAILQ_FIRST(&sc->sc_txbuf); + if (bf != NULL) + STAILQ_REMOVE_HEAD(&sc->sc_txbuf, bf_list); + ATH_TXBUF_UNLOCK(sc); + if (bf == NULL) { + DPRINTF(sc, ATH_DEBUG_XMIT, "%s: out of xmit buffers\n", + __func__); + sc->sc_stats.ast_tx_qstop++; + ifp->if_drv_flags |= IFF_DRV_OACTIVE; + m_freem(m); + return ENOBUFS; + } + + ifp->if_opackets++; + sc->sc_stats.ast_tx_raw++; + + if (params == NULL) { + /* + * Legacy path; interpret frame contents to decide + * precisely how to send the frame. + */ + if (ath_tx_start(sc, ni, bf, m)) + goto bad; + } else { + /* + * Caller supplied explicit parameters to use in + * sending the frame. + */ + if (ath_tx_raw_start(sc, ni, bf, m, params)) + goto bad; + } + sc->sc_tx_timer = 5; + ifp->if_timer = 1; + + return 0; +bad: + ifp->if_oerrors++; + ATH_TXBUF_LOCK(sc); + STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list); + ATH_TXBUF_UNLOCK(sc); + ieee80211_free_node(ni); + return EIO; /* XXX */ +} + /* * Announce various information on device/driver attach. */ diff --git a/sys/dev/ath/if_athioctl.h b/sys/dev/ath/if_athioctl.h index a1b5198..6a03e22 100644 --- a/sys/dev/ath/if_athioctl.h +++ b/sys/dev/ath/if_athioctl.h @@ -108,7 +108,8 @@ struct ath_stats { u_int32_t ast_ant_tx[8]; /* tx frames with antenna */ u_int32_t ast_cabq_xmit; /* cabq frames transmitted */ u_int32_t ast_cabq_busy; /* cabq found busy */ - u_int32_t ast_pad[30]; + u_int32_t ast_tx_raw; /* tx frames through raw api */ + u_int32_t ast_pad[29]; }; #define SIOCGATHSTATS _IOWR('i', 137, struct ifreq) |