From e60c4fc2c94e85d222c8be576f46915568fc3a23 Mon Sep 17 00:00:00 2001 From: Adrian Chadd Date: Sun, 20 May 2012 02:05:10 +0000 Subject: [PATCH] Migrate the bulk of the RX routines out from if_ath.c to if_ath_rx.[ch]. * migrate the rx processing out into if_ath_rx.c * migrate the TSF functions into if_ath_tsf.h, as inlines This is in prepration for supporting the EDMA RX routines, required to support the AR93xx series NICs. TODO: * ath_start() shouldn't be private, but it's called as part of the RX path. I should likely migrate ath_rx_tasklet() back into if_ath.c and then return this to be 'static'. The RX code really shouldn't need to see TX routines (and vice versa.) * ath_beacon_* should be in if_ath_beacon.[ch]. * ath_tdma_* should be in if_ath_tdma.[ch] ... --- sys/conf/files | 2 + sys/dev/ath/if_ath.c | 917 +----------------------------------- sys/dev/ath/if_ath_misc.h | 15 + sys/dev/ath/if_ath_rx.c | 967 ++++++++++++++++++++++++++++++++++++++ sys/dev/ath/if_ath_rx.h | 43 ++ sys/dev/ath/if_ath_tsf.h | 81 ++++ sys/dev/ath/if_ath_tx.c | 12 +- 7 files changed, 1123 insertions(+), 914 deletions(-) create mode 100644 sys/dev/ath/if_ath_rx.c create mode 100644 sys/dev/ath/if_ath_rx.h create mode 100644 sys/dev/ath/if_ath_tsf.h diff --git a/sys/conf/files b/sys/conf/files index 3a2b65e6c51..5f766e526ce 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -722,6 +722,8 @@ dev/ath/if_ath_tx_ht.c optional ath \ compile-with "${NORMAL_C} -I$S/dev/ath" dev/ath/if_ath_sysctl.c optional ath \ compile-with "${NORMAL_C} -I$S/dev/ath" +dev/ath/if_ath_rx.c optional ath \ + compile-with "${NORMAL_C} -I$S/dev/ath" dev/ath/ah_osdep.c optional ath \ compile-with "${NORMAL_C} -I$S/dev/ath" # diff --git a/sys/dev/ath/if_ath.c b/sys/dev/ath/if_ath.c index 4511203a2e1..eafbeb9f9b7 100644 --- a/sys/dev/ath/if_ath.c +++ b/sys/dev/ath/if_ath.c @@ -102,10 +102,12 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include #include +#include #include #ifdef ATH_TX99_DIAG @@ -139,7 +141,6 @@ static void ath_vap_delete(struct ieee80211vap *); static void ath_init(void *); static void ath_stop_locked(struct ifnet *); static void ath_stop(struct ifnet *); -static void ath_start(struct ifnet *); static int ath_reset_vap(struct ieee80211vap *, u_long); static int ath_media_change(struct ifnet *); static void ath_watchdog(void *); @@ -151,7 +152,6 @@ static void ath_key_update_begin(struct ieee80211vap *); static void ath_key_update_end(struct ieee80211vap *); static void ath_update_mcast(struct ifnet *); static void ath_update_promisc(struct ifnet *); -static void ath_mode_init(struct ath_softc *); static void ath_setslottime(struct ath_softc *); static void ath_updateslot(struct ifnet *); static int ath_beaconq_setup(struct ath_hal *); @@ -165,7 +165,6 @@ static void ath_bstuck_proc(void *, int); static void ath_reset_proc(void *, int); static void ath_beacon_return(struct ath_softc *, struct ath_buf *); static void ath_beacon_free(struct ath_softc *); -static void ath_beacon_config(struct ath_softc *, struct ieee80211vap *); static void ath_descdma_cleanup(struct ath_softc *sc, struct ath_descdma *, ath_bufhead *); static int ath_desc_alloc(struct ath_softc *); @@ -176,12 +175,6 @@ static void ath_node_cleanup(struct ieee80211_node *); static void ath_node_free(struct ieee80211_node *); static void ath_node_getsignal(const struct ieee80211_node *, int8_t *, int8_t *); -static int ath_rxbuf_init(struct ath_softc *, struct ath_buf *); -static void ath_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m, - int subtype, int rssi, int nf); -static void ath_setdefantenna(struct ath_softc *, u_int); -static void ath_rx_proc(struct ath_softc *sc, int); -static void ath_rx_tasklet(void *, int); static void ath_txq_init(struct ath_softc *sc, struct ath_txq *, int); static struct ath_txq *ath_txq_setup(struct ath_softc*, int qtype, int subtype); static int ath_tx_setup(struct ath_softc *, int, int); @@ -194,8 +187,6 @@ static void ath_tx_proc(void *, int); static void ath_txq_sched_tasklet(void *, int); static int ath_chan_set(struct ath_softc *, struct ieee80211_channel *); static void ath_draintxq(struct ath_softc *, ATH_RESET_TYPE reset_type); -static void ath_stoprecv(struct ath_softc *, int); -static int ath_startrecv(struct ath_softc *); static void ath_chan_change(struct ath_softc *, struct ieee80211_channel *); static void ath_scan_start(struct ieee80211com *); static void ath_scan_end(struct ieee80211com *); @@ -2334,7 +2325,7 @@ ath_getbuf(struct ath_softc *sc) return bf; } -static void +void ath_start(struct ifnet *ifp) { struct ath_softc *sc = ifp->if_softc; @@ -2485,102 +2476,6 @@ ath_key_update_end(struct ieee80211vap *vap) taskqueue_unblock(sc->sc_tq); } -/* - * Calculate the receive filter according to the - * operating mode and state: - * - * o always accept unicast, broadcast, and multicast traffic - * o accept PHY error frames when hardware doesn't have MIB support - * to count and we need them for ANI (sta mode only until recently) - * and we are not scanning (ANI is disabled) - * NB: older hal's add rx filter bits out of sight and we need to - * blindly preserve them - * o probe request frames are accepted only when operating in - * hostap, adhoc, mesh, or monitor modes - * o enable promiscuous mode - * - when in monitor mode - * - if interface marked PROMISC (assumes bridge setting is filtered) - * o accept beacons: - * - when operating in station mode for collecting rssi data when - * the station is otherwise quiet, or - * - when operating in adhoc mode so the 802.11 layer creates - * node table entries for peers, - * - when scanning - * - when doing s/w beacon miss (e.g. for ap+sta) - * - when operating in ap mode in 11g to detect overlapping bss that - * require protection - * - when operating in mesh mode to detect neighbors - * o accept control frames: - * - when in monitor mode - * XXX HT protection for 11n - */ -static u_int32_t -ath_calcrxfilter(struct ath_softc *sc) -{ - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - u_int32_t rfilt; - - rfilt = HAL_RX_FILTER_UCAST | HAL_RX_FILTER_BCAST | HAL_RX_FILTER_MCAST; - if (!sc->sc_needmib && !sc->sc_scanning) - rfilt |= HAL_RX_FILTER_PHYERR; - if (ic->ic_opmode != IEEE80211_M_STA) - rfilt |= HAL_RX_FILTER_PROBEREQ; - /* XXX ic->ic_monvaps != 0? */ - if (ic->ic_opmode == IEEE80211_M_MONITOR || (ifp->if_flags & IFF_PROMISC)) - rfilt |= HAL_RX_FILTER_PROM; - if (ic->ic_opmode == IEEE80211_M_STA || - ic->ic_opmode == IEEE80211_M_IBSS || - sc->sc_swbmiss || sc->sc_scanning) - rfilt |= HAL_RX_FILTER_BEACON; - /* - * NB: We don't recalculate the rx filter when - * ic_protmode changes; otherwise we could do - * this only when ic_protmode != NONE. - */ - if (ic->ic_opmode == IEEE80211_M_HOSTAP && - IEEE80211_IS_CHAN_ANYG(ic->ic_curchan)) - rfilt |= HAL_RX_FILTER_BEACON; - - /* - * Enable hardware PS-POLL RX only for hostap mode; - * STA mode sends PS-POLL frames but never - * receives them. - */ - if (ath_hal_getcapability(sc->sc_ah, HAL_CAP_PSPOLL, - 0, NULL) == HAL_OK && - ic->ic_opmode == IEEE80211_M_HOSTAP) - rfilt |= HAL_RX_FILTER_PSPOLL; - - if (sc->sc_nmeshvaps) { - rfilt |= HAL_RX_FILTER_BEACON; - if (sc->sc_hasbmatch) - rfilt |= HAL_RX_FILTER_BSSID; - else - rfilt |= HAL_RX_FILTER_PROM; - } - if (ic->ic_opmode == IEEE80211_M_MONITOR) - rfilt |= HAL_RX_FILTER_CONTROL; - - /* - * Enable RX of compressed BAR frames only when doing - * 802.11n. Required for A-MPDU. - */ - if (IEEE80211_IS_CHAN_HT(ic->ic_curchan)) - rfilt |= HAL_RX_FILTER_COMPBAR; - - /* - * Enable radar PHY errors if requested by the - * DFS module. - */ - if (sc->sc_dodfs) - rfilt |= HAL_RX_FILTER_PHYRADAR; - - DPRINTF(sc, ATH_DEBUG_MODE, "%s: RX filter 0x%x, %s if_flags 0x%x\n", - __func__, rfilt, ieee80211_opmode_name[ic->ic_opmode], ifp->if_flags); - return rfilt; -} - static void ath_update_promisc(struct ifnet *ifp) { @@ -2630,7 +2525,7 @@ ath_update_mcast(struct ifnet *ifp) __func__, mfilt[0], mfilt[1]); } -static void +void ath_mode_init(struct ath_softc *sc) { struct ifnet *ifp = sc->sc_ifp; @@ -3313,7 +3208,7 @@ ath_beacon_free(struct ath_softc *sc) * interrupt when we stop seeing beacons from the AP * we've associated with. */ -static void +void ath_beacon_config(struct ath_softc *sc, struct ieee80211vap *vap) { #define TSF_TO_TU(_h,_l) \ @@ -3788,197 +3683,10 @@ ath_node_getsignal(const struct ieee80211_node *ni, int8_t *rssi, int8_t *noise) *noise = -95; /* nominally correct */ } -static int -ath_rxbuf_init(struct ath_softc *sc, struct ath_buf *bf) -{ - struct ath_hal *ah = sc->sc_ah; - int error; - struct mbuf *m; - struct ath_desc *ds; - - m = bf->bf_m; - if (m == NULL) { - /* - * NB: by assigning a page to the rx dma buffer we - * implicitly satisfy the Atheros requirement that - * this buffer be cache-line-aligned and sized to be - * multiple of the cache line size. Not doing this - * causes weird stuff to happen (for the 5210 at least). - */ - m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); - if (m == NULL) { - DPRINTF(sc, ATH_DEBUG_ANY, - "%s: no mbuf/cluster\n", __func__); - sc->sc_stats.ast_rx_nombuf++; - return ENOMEM; - } - m->m_pkthdr.len = m->m_len = m->m_ext.ext_size; - - error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, - bf->bf_dmamap, m, - bf->bf_segs, &bf->bf_nseg, - BUS_DMA_NOWAIT); - if (error != 0) { - DPRINTF(sc, ATH_DEBUG_ANY, - "%s: bus_dmamap_load_mbuf_sg failed; error %d\n", - __func__, error); - sc->sc_stats.ast_rx_busdma++; - m_freem(m); - return error; - } - KASSERT(bf->bf_nseg == 1, - ("multi-segment packet; nseg %u", bf->bf_nseg)); - bf->bf_m = m; - } - bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREREAD); - - /* - * Setup descriptors. For receive we always terminate - * the descriptor list with a self-linked entry so we'll - * not get overrun under high load (as can happen with a - * 5212 when ANI processing enables PHY error frames). - * - * To insure the last descriptor is self-linked we create - * each descriptor as self-linked and add it to the end. As - * each additional descriptor is added the previous self-linked - * entry is ``fixed'' naturally. This should be safe even - * if DMA is happening. When processing RX interrupts we - * never remove/process the last, self-linked, entry on the - * descriptor list. This insures the hardware always has - * someplace to write a new frame. - */ - /* - * 11N: we can no longer afford to self link the last descriptor. - * MAC acknowledges BA status as long as it copies frames to host - * buffer (or rx fifo). This can incorrectly acknowledge packets - * to a sender if last desc is self-linked. - */ - ds = bf->bf_desc; - if (sc->sc_rxslink) - ds->ds_link = bf->bf_daddr; /* link to self */ - else - ds->ds_link = 0; /* terminate the list */ - ds->ds_data = bf->bf_segs[0].ds_addr; - ath_hal_setuprxdesc(ah, ds - , m->m_len /* buffer size */ - , 0 - ); - - if (sc->sc_rxlink != NULL) - *sc->sc_rxlink = bf->bf_daddr; - sc->sc_rxlink = &ds->ds_link; - return 0; -} - -/* - * Extend 15-bit time stamp from rx descriptor to - * a full 64-bit TSF using the specified TSF. - */ -static __inline u_int64_t -ath_extend_tsf15(u_int32_t rstamp, u_int64_t tsf) -{ - if ((tsf & 0x7fff) < rstamp) - tsf -= 0x8000; - - return ((tsf &~ 0x7fff) | rstamp); -} - -/* - * Extend 32-bit time stamp from rx descriptor to - * a full 64-bit TSF using the specified TSF. - */ -static __inline u_int64_t -ath_extend_tsf32(u_int32_t rstamp, u_int64_t tsf) -{ - u_int32_t tsf_low = tsf & 0xffffffff; - u_int64_t tsf64 = (tsf & ~0xffffffffULL) | rstamp; - - if (rstamp > tsf_low && (rstamp - tsf_low > 0x10000000)) - tsf64 -= 0x100000000ULL; - - if (rstamp < tsf_low && (tsf_low - rstamp > 0x10000000)) - tsf64 += 0x100000000ULL; - - return tsf64; -} - -/* - * Extend the TSF from the RX descriptor to a full 64 bit TSF. - * Earlier hardware versions only wrote the low 15 bits of the - * TSF into the RX descriptor; later versions (AR5416 and up) - * include the 32 bit TSF value. - */ -static __inline u_int64_t -ath_extend_tsf(struct ath_softc *sc, u_int32_t rstamp, u_int64_t tsf) -{ - if (sc->sc_rxtsf32) - return ath_extend_tsf32(rstamp, tsf); - else - return ath_extend_tsf15(rstamp, tsf); -} - -/* - * Intercept management frames to collect beacon rssi data - * and to do ibss merges. - */ -static void -ath_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m, - int subtype, int rssi, int nf) -{ - struct ieee80211vap *vap = ni->ni_vap; - struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc; - - /* - * Call up first so subsequent work can use information - * potentially stored in the node (e.g. for ibss merge). - */ - ATH_VAP(vap)->av_recv_mgmt(ni, m, subtype, rssi, nf); - switch (subtype) { - case IEEE80211_FC0_SUBTYPE_BEACON: - /* update rssi statistics for use by the hal */ - /* XXX unlocked check against vap->iv_bss? */ - ATH_RSSI_LPF(sc->sc_halstats.ns_avgbrssi, rssi); - if (sc->sc_syncbeacon && - ni == vap->iv_bss && vap->iv_state == IEEE80211_S_RUN) { - /* - * Resync beacon timers using the tsf of the beacon - * frame we just received. - */ - ath_beacon_config(sc, vap); - } - /* fall thru... */ - case IEEE80211_FC0_SUBTYPE_PROBE_RESP: - if (vap->iv_opmode == IEEE80211_M_IBSS && - vap->iv_state == IEEE80211_S_RUN) { - uint32_t rstamp = sc->sc_lastrs->rs_tstamp; - uint64_t tsf = ath_extend_tsf(sc, rstamp, - ath_hal_gettsf64(sc->sc_ah)); - /* - * Handle ibss merge as needed; check the tsf on the - * frame before attempting the merge. The 802.11 spec - * says the station should change it's bssid to match - * the oldest station with the same ssid, where oldest - * is determined by the tsf. Note that hardware - * reconfiguration happens through callback to - * ath_newstate as the state machine will go from - * RUN -> RUN when this happens. - */ - if (le64toh(ni->ni_tstamp.tsf) >= tsf) { - DPRINTF(sc, ATH_DEBUG_STATE, - "ibss merge, rstamp %u tsf %ju " - "tstamp %ju\n", rstamp, (uintmax_t)tsf, - (uintmax_t)ni->ni_tstamp.tsf); - (void) ieee80211_ibss_merge(ni); - } - } - break; - } -} - /* * Set the default antenna. */ -static void +void ath_setdefantenna(struct ath_softc *sc, u_int antenna) { struct ath_hal *ah = sc->sc_ah; @@ -3991,538 +3699,6 @@ ath_setdefantenna(struct ath_softc *sc, u_int antenna) sc->sc_rxotherant = 0; } -static void -ath_rx_tap(struct ifnet *ifp, struct mbuf *m, - const struct ath_rx_status *rs, u_int64_t tsf, int16_t nf) -{ -#define CHAN_HT20 htole32(IEEE80211_CHAN_HT20) -#define CHAN_HT40U htole32(IEEE80211_CHAN_HT40U) -#define CHAN_HT40D htole32(IEEE80211_CHAN_HT40D) -#define CHAN_HT (CHAN_HT20|CHAN_HT40U|CHAN_HT40D) - struct ath_softc *sc = ifp->if_softc; - const HAL_RATE_TABLE *rt; - uint8_t rix; - - rt = sc->sc_currates; - KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode)); - rix = rt->rateCodeToIndex[rs->rs_rate]; - sc->sc_rx_th.wr_rate = sc->sc_hwmap[rix].ieeerate; - sc->sc_rx_th.wr_flags = sc->sc_hwmap[rix].rxflags; -#ifdef AH_SUPPORT_AR5416 - sc->sc_rx_th.wr_chan_flags &= ~CHAN_HT; - if (sc->sc_rx_th.wr_rate & IEEE80211_RATE_MCS) { /* HT rate */ - struct ieee80211com *ic = ifp->if_l2com; - - if ((rs->rs_flags & HAL_RX_2040) == 0) - sc->sc_rx_th.wr_chan_flags |= CHAN_HT20; - else if (IEEE80211_IS_CHAN_HT40U(ic->ic_curchan)) - sc->sc_rx_th.wr_chan_flags |= CHAN_HT40U; - else - sc->sc_rx_th.wr_chan_flags |= CHAN_HT40D; - if ((rs->rs_flags & HAL_RX_GI) == 0) - sc->sc_rx_th.wr_flags |= IEEE80211_RADIOTAP_F_SHORTGI; - } -#endif - sc->sc_rx_th.wr_tsf = htole64(ath_extend_tsf(sc, rs->rs_tstamp, tsf)); - if (rs->rs_status & HAL_RXERR_CRC) - sc->sc_rx_th.wr_flags |= IEEE80211_RADIOTAP_F_BADFCS; - /* XXX propagate other error flags from descriptor */ - sc->sc_rx_th.wr_antnoise = nf; - sc->sc_rx_th.wr_antsignal = nf + rs->rs_rssi; - sc->sc_rx_th.wr_antenna = rs->rs_antenna; -#undef CHAN_HT -#undef CHAN_HT20 -#undef CHAN_HT40U -#undef CHAN_HT40D -} - -static void -ath_handle_micerror(struct ieee80211com *ic, - struct ieee80211_frame *wh, int keyix) -{ - struct ieee80211_node *ni; - - /* XXX recheck MIC to deal w/ chips that lie */ - /* XXX discard MIC errors on !data frames */ - ni = ieee80211_find_rxnode(ic, (const struct ieee80211_frame_min *) wh); - if (ni != NULL) { - ieee80211_notify_michael_failure(ni->ni_vap, wh, keyix); - ieee80211_free_node(ni); - } -} - -/* - * Only run the RX proc if it's not already running. - * Since this may get run as part of the reset/flush path, - * the task can't clash with an existing, running tasklet. - */ -static void -ath_rx_tasklet(void *arg, int npending) -{ - struct ath_softc *sc = arg; - - CTR1(ATH_KTR_INTR, "ath_rx_proc: pending=%d", npending); - DPRINTF(sc, ATH_DEBUG_RX_PROC, "%s: pending %u\n", __func__, npending); - ATH_PCU_LOCK(sc); - if (sc->sc_inreset_cnt > 0) { - device_printf(sc->sc_dev, - "%s: sc_inreset_cnt > 0; skipping\n", __func__); - ATH_PCU_UNLOCK(sc); - return; - } - ATH_PCU_UNLOCK(sc); - ath_rx_proc(sc, 1); -} - -static void -ath_rx_proc(struct ath_softc *sc, int resched) -{ -#define PA2DESC(_sc, _pa) \ - ((struct ath_desc *)((caddr_t)(_sc)->sc_rxdma.dd_desc + \ - ((_pa) - (_sc)->sc_rxdma.dd_desc_paddr))) - struct ath_buf *bf; - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - struct ath_hal *ah = sc->sc_ah; - struct ath_desc *ds; - struct ath_rx_status *rs; - struct mbuf *m; - struct ieee80211_node *ni; - int len, type, ngood; - HAL_STATUS status; - int16_t nf; - u_int64_t tsf, rstamp; - int npkts = 0; - - /* XXX we must not hold the ATH_LOCK here */ - ATH_UNLOCK_ASSERT(sc); - ATH_PCU_UNLOCK_ASSERT(sc); - - ATH_PCU_LOCK(sc); - sc->sc_rxproc_cnt++; - ATH_PCU_UNLOCK(sc); - - DPRINTF(sc, ATH_DEBUG_RX_PROC, "%s: called\n", __func__); - ngood = 0; - nf = ath_hal_getchannoise(ah, sc->sc_curchan); - sc->sc_stats.ast_rx_noise = nf; - tsf = ath_hal_gettsf64(ah); - do { - bf = TAILQ_FIRST(&sc->sc_rxbuf); - if (sc->sc_rxslink && bf == NULL) { /* NB: shouldn't happen */ - if_printf(ifp, "%s: no buffer!\n", __func__); - break; - } else if (bf == NULL) { - /* - * End of List: - * this can happen for non-self-linked RX chains - */ - sc->sc_stats.ast_rx_hitqueueend++; - break; - } - m = bf->bf_m; - if (m == NULL) { /* NB: shouldn't happen */ - /* - * If mbuf allocation failed previously there - * will be no mbuf; try again to re-populate it. - */ - /* XXX make debug msg */ - if_printf(ifp, "%s: no mbuf!\n", __func__); - TAILQ_REMOVE(&sc->sc_rxbuf, bf, bf_list); - goto rx_next; - } - ds = bf->bf_desc; - if (ds->ds_link == bf->bf_daddr) { - /* NB: never process the self-linked entry at the end */ - sc->sc_stats.ast_rx_hitqueueend++; - break; - } - /* XXX sync descriptor memory */ - /* - * Must provide the virtual address of the current - * descriptor, the physical address, and the virtual - * address of the next descriptor in the h/w chain. - * This allows the HAL to look ahead to see if the - * hardware is done with a descriptor by checking the - * done bit in the following descriptor and the address - * of the current descriptor the DMA engine is working - * on. All this is necessary because of our use of - * a self-linked list to avoid rx overruns. - */ - rs = &bf->bf_status.ds_rxstat; - status = ath_hal_rxprocdesc(ah, ds, - bf->bf_daddr, PA2DESC(sc, ds->ds_link), rs); -#ifdef ATH_DEBUG - if (sc->sc_debug & ATH_DEBUG_RECV_DESC) - ath_printrxbuf(sc, bf, 0, status == HAL_OK); -#endif - if (status == HAL_EINPROGRESS) - break; - - TAILQ_REMOVE(&sc->sc_rxbuf, bf, bf_list); - npkts++; - - /* - * Calculate the correct 64 bit TSF given - * the TSF64 register value and rs_tstamp. - */ - rstamp = ath_extend_tsf(sc, rs->rs_tstamp, tsf); - - /* These aren't specifically errors */ -#ifdef AH_SUPPORT_AR5416 - if (rs->rs_flags & HAL_RX_GI) - sc->sc_stats.ast_rx_halfgi++; - if (rs->rs_flags & HAL_RX_2040) - sc->sc_stats.ast_rx_2040++; - if (rs->rs_flags & HAL_RX_DELIM_CRC_PRE) - sc->sc_stats.ast_rx_pre_crc_err++; - if (rs->rs_flags & HAL_RX_DELIM_CRC_POST) - sc->sc_stats.ast_rx_post_crc_err++; - if (rs->rs_flags & HAL_RX_DECRYPT_BUSY) - sc->sc_stats.ast_rx_decrypt_busy_err++; - if (rs->rs_flags & HAL_RX_HI_RX_CHAIN) - sc->sc_stats.ast_rx_hi_rx_chain++; -#endif /* AH_SUPPORT_AR5416 */ - - if (rs->rs_status != 0) { - if (rs->rs_status & HAL_RXERR_CRC) - sc->sc_stats.ast_rx_crcerr++; - if (rs->rs_status & HAL_RXERR_FIFO) - sc->sc_stats.ast_rx_fifoerr++; - if (rs->rs_status & HAL_RXERR_PHY) { - sc->sc_stats.ast_rx_phyerr++; - /* Process DFS radar events */ - if ((rs->rs_phyerr == HAL_PHYERR_RADAR) || - (rs->rs_phyerr == HAL_PHYERR_FALSE_RADAR_EXT)) { - /* Since we're touching the frame data, sync it */ - bus_dmamap_sync(sc->sc_dmat, - bf->bf_dmamap, - BUS_DMASYNC_POSTREAD); - /* Now pass it to the radar processing code */ - ath_dfs_process_phy_err(sc, mtod(m, char *), rstamp, rs); - } - - /* Be suitably paranoid about receiving phy errors out of the stats array bounds */ - if (rs->rs_phyerr < 64) - sc->sc_stats.ast_rx_phy[rs->rs_phyerr]++; - goto rx_error; /* NB: don't count in ierrors */ - } - if (rs->rs_status & HAL_RXERR_DECRYPT) { - /* - * Decrypt error. If the error occurred - * because there was no hardware key, then - * let the frame through so the upper layers - * can process it. This is necessary for 5210 - * parts which have no way to setup a ``clear'' - * key cache entry. - * - * XXX do key cache faulting - */ - if (rs->rs_keyix == HAL_RXKEYIX_INVALID) - goto rx_accept; - sc->sc_stats.ast_rx_badcrypt++; - } - if (rs->rs_status & HAL_RXERR_MIC) { - sc->sc_stats.ast_rx_badmic++; - /* - * Do minimal work required to hand off - * the 802.11 header for notification. - */ - /* XXX frag's and qos frames */ - len = rs->rs_datalen; - if (len >= sizeof (struct ieee80211_frame)) { - bus_dmamap_sync(sc->sc_dmat, - bf->bf_dmamap, - BUS_DMASYNC_POSTREAD); - ath_handle_micerror(ic, - mtod(m, struct ieee80211_frame *), - sc->sc_splitmic ? - rs->rs_keyix-32 : rs->rs_keyix); - } - } - ifp->if_ierrors++; -rx_error: - /* - * Cleanup any pending partial frame. - */ - if (sc->sc_rxpending != NULL) { - m_freem(sc->sc_rxpending); - sc->sc_rxpending = NULL; - } - /* - * When a tap is present pass error frames - * that have been requested. By default we - * pass decrypt+mic errors but others may be - * interesting (e.g. crc). - */ - if (ieee80211_radiotap_active(ic) && - (rs->rs_status & sc->sc_monpass)) { - bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, - BUS_DMASYNC_POSTREAD); - /* NB: bpf needs the mbuf length setup */ - len = rs->rs_datalen; - m->m_pkthdr.len = m->m_len = len; - bf->bf_m = NULL; - ath_rx_tap(ifp, m, rs, rstamp, nf); - ieee80211_radiotap_rx_all(ic, m); - m_freem(m); - } - /* XXX pass MIC errors up for s/w reclaculation */ - goto rx_next; - } -rx_accept: - /* - * Sync and unmap the frame. At this point we're - * committed to passing the mbuf somewhere so clear - * bf_m; this means a new mbuf must be allocated - * when the rx descriptor is setup again to receive - * another frame. - */ - bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, - BUS_DMASYNC_POSTREAD); - bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); - bf->bf_m = NULL; - - len = rs->rs_datalen; - m->m_len = len; - - if (rs->rs_more) { - /* - * Frame spans multiple descriptors; save - * it for the next completed descriptor, it - * will be used to construct a jumbogram. - */ - if (sc->sc_rxpending != NULL) { - /* NB: max frame size is currently 2 clusters */ - sc->sc_stats.ast_rx_toobig++; - m_freem(sc->sc_rxpending); - } - m->m_pkthdr.rcvif = ifp; - m->m_pkthdr.len = len; - sc->sc_rxpending = m; - goto rx_next; - } else if (sc->sc_rxpending != NULL) { - /* - * This is the second part of a jumbogram, - * chain it to the first mbuf, adjust the - * frame length, and clear the rxpending state. - */ - sc->sc_rxpending->m_next = m; - sc->sc_rxpending->m_pkthdr.len += len; - m = sc->sc_rxpending; - sc->sc_rxpending = NULL; - } else { - /* - * Normal single-descriptor receive; setup - * the rcvif and packet length. - */ - m->m_pkthdr.rcvif = ifp; - m->m_pkthdr.len = len; - } - - /* - * Validate rs->rs_antenna. - * - * Some users w/ AR9285 NICs have reported crashes - * here because rs_antenna field is bogusly large. - * Let's enforce the maximum antenna limit of 8 - * (and it shouldn't be hard coded, but that's a - * separate problem) and if there's an issue, print - * out an error and adjust rs_antenna to something - * sensible. - * - * This code should be removed once the actual - * root cause of the issue has been identified. - * For example, it may be that the rs_antenna - * field is only valid for the lsat frame of - * an aggregate and it just happens that it is - * "mostly" right. (This is a general statement - - * the majority of the statistics are only valid - * for the last frame in an aggregate. - */ - if (rs->rs_antenna > 7) { - device_printf(sc->sc_dev, "%s: rs_antenna > 7 (%d)\n", - __func__, rs->rs_antenna); -#ifdef ATH_DEBUG - ath_printrxbuf(sc, bf, 0, status == HAL_OK); -#endif /* ATH_DEBUG */ - rs->rs_antenna = 0; /* XXX better than nothing */ - } - - ifp->if_ipackets++; - sc->sc_stats.ast_ant_rx[rs->rs_antenna]++; - - /* - * Populate the rx status block. When there are bpf - * listeners we do the additional work to provide - * complete status. Otherwise we fill in only the - * material required by ieee80211_input. Note that - * noise setting is filled in above. - */ - if (ieee80211_radiotap_active(ic)) - ath_rx_tap(ifp, m, rs, rstamp, nf); - - /* - * From this point on we assume the frame is at least - * as large as ieee80211_frame_min; verify that. - */ - if (len < IEEE80211_MIN_LEN) { - if (!ieee80211_radiotap_active(ic)) { - DPRINTF(sc, ATH_DEBUG_RECV, - "%s: short packet %d\n", __func__, len); - sc->sc_stats.ast_rx_tooshort++; - } else { - /* NB: in particular this captures ack's */ - ieee80211_radiotap_rx_all(ic, m); - } - m_freem(m); - goto rx_next; - } - - if (IFF_DUMPPKTS(sc, ATH_DEBUG_RECV)) { - const HAL_RATE_TABLE *rt = sc->sc_currates; - uint8_t rix = rt->rateCodeToIndex[rs->rs_rate]; - - ieee80211_dump_pkt(ic, mtod(m, caddr_t), len, - sc->sc_hwmap[rix].ieeerate, rs->rs_rssi); - } - - m_adj(m, -IEEE80211_CRC_LEN); - - /* - * Locate the node for sender, track state, and then - * pass the (referenced) node up to the 802.11 layer - * for its use. - */ - ni = ieee80211_find_rxnode_withkey(ic, - mtod(m, const struct ieee80211_frame_min *), - rs->rs_keyix == HAL_RXKEYIX_INVALID ? - IEEE80211_KEYIX_NONE : rs->rs_keyix); - sc->sc_lastrs = rs; - -#ifdef AH_SUPPORT_AR5416 - if (rs->rs_isaggr) - sc->sc_stats.ast_rx_agg++; -#endif /* AH_SUPPORT_AR5416 */ - - if (ni != NULL) { - /* - * Only punt packets for ampdu reorder processing for - * 11n nodes; net80211 enforces that M_AMPDU is only - * set for 11n nodes. - */ - if (ni->ni_flags & IEEE80211_NODE_HT) - m->m_flags |= M_AMPDU; - - /* - * Sending station is known, dispatch directly. - */ - type = ieee80211_input(ni, m, rs->rs_rssi, nf); - ieee80211_free_node(ni); - /* - * Arrange to update the last rx timestamp only for - * frames from our ap when operating in station mode. - * This assumes the rx key is always setup when - * associated. - */ - if (ic->ic_opmode == IEEE80211_M_STA && - rs->rs_keyix != HAL_RXKEYIX_INVALID) - ngood++; - } else { - type = ieee80211_input_all(ic, m, rs->rs_rssi, nf); - } - /* - * Track rx rssi and do any rx antenna management. - */ - ATH_RSSI_LPF(sc->sc_halstats.ns_avgrssi, rs->rs_rssi); - if (sc->sc_diversity) { - /* - * When using fast diversity, change the default rx - * antenna if diversity chooses the other antenna 3 - * times in a row. - */ - if (sc->sc_defant != rs->rs_antenna) { - if (++sc->sc_rxotherant >= 3) - ath_setdefantenna(sc, rs->rs_antenna); - } else - sc->sc_rxotherant = 0; - } - - /* Newer school diversity - kite specific for now */ - /* XXX perhaps migrate the normal diversity code to this? */ - if ((ah)->ah_rxAntCombDiversity) - (*(ah)->ah_rxAntCombDiversity)(ah, rs, ticks, hz); - - if (sc->sc_softled) { - /* - * Blink for any data frame. Otherwise do a - * heartbeat-style blink when idle. The latter - * is mainly for station mode where we depend on - * periodic beacon frames to trigger the poll event. - */ - if (type == IEEE80211_FC0_TYPE_DATA) { - const HAL_RATE_TABLE *rt = sc->sc_currates; - ath_led_event(sc, - rt->rateCodeToIndex[rs->rs_rate]); - } else if (ticks - sc->sc_ledevent >= sc->sc_ledidle) - ath_led_event(sc, 0); - } -rx_next: - TAILQ_INSERT_TAIL(&sc->sc_rxbuf, bf, bf_list); - } while (ath_rxbuf_init(sc, bf) == 0); - - /* rx signal state monitoring */ - ath_hal_rxmonitor(ah, &sc->sc_halstats, sc->sc_curchan); - if (ngood) - sc->sc_lastrx = tsf; - - CTR2(ATH_KTR_INTR, "ath_rx_proc: npkts=%d, ngood=%d", npkts, ngood); - /* Queue DFS tasklet if needed */ - if (resched && ath_dfs_tasklet_needed(sc, sc->sc_curchan)) - taskqueue_enqueue(sc->sc_tq, &sc->sc_dfstask); - - /* - * Now that all the RX frames were handled that - * need to be handled, kick the PCU if there's - * been an RXEOL condition. - */ - ATH_PCU_LOCK(sc); - if (resched && sc->sc_kickpcu) { - CTR0(ATH_KTR_ERR, "ath_rx_proc: kickpcu"); - device_printf(sc->sc_dev, "%s: kickpcu; handled %d packets\n", - __func__, npkts); - - /* XXX rxslink? */ - /* - * XXX can we hold the PCU lock here? - * Are there any net80211 buffer calls involved? - */ - bf = TAILQ_FIRST(&sc->sc_rxbuf); - ath_hal_putrxbuf(ah, bf->bf_daddr); - ath_hal_rxena(ah); /* enable recv descriptors */ - ath_mode_init(sc); /* set filters, etc. */ - ath_hal_startpcurecv(ah); /* re-enable PCU/DMA engine */ - - ath_hal_intrset(ah, sc->sc_imask); - sc->sc_kickpcu = 0; - } - ATH_PCU_UNLOCK(sc); - - /* XXX check this inside of IF_LOCK? */ - if (resched && (ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0) { -#ifdef IEEE80211_SUPPORT_SUPERG - ieee80211_ff_age_all(ic, 100); -#endif - if (!IFQ_IS_EMPTY(&ifp->if_snd)) - ath_start(ifp); - } -#undef PA2DESC - - ATH_PCU_LOCK(sc); - sc->sc_rxproc_cnt--; - ATH_PCU_UNLOCK(sc); -} - static void ath_txq_init(struct ath_softc *sc, struct ath_txq *txq, int qnum) { @@ -5403,87 +4579,6 @@ ath_draintxq(struct ath_softc *sc, ATH_RESET_TYPE reset_type) sc->sc_wd_timer = 0; } -/* - * Disable the receive h/w in preparation for a reset. - */ -static void -ath_stoprecv(struct ath_softc *sc, int dodelay) -{ -#define PA2DESC(_sc, _pa) \ - ((struct ath_desc *)((caddr_t)(_sc)->sc_rxdma.dd_desc + \ - ((_pa) - (_sc)->sc_rxdma.dd_desc_paddr))) - struct ath_hal *ah = sc->sc_ah; - - ath_hal_stoppcurecv(ah); /* disable PCU */ - ath_hal_setrxfilter(ah, 0); /* clear recv filter */ - ath_hal_stopdmarecv(ah); /* disable DMA engine */ - /* - * TODO: see if this particular DELAY() is required; it may be - * masking some missing FIFO flush or DMA sync. - */ -#if 0 - if (dodelay) -#endif - DELAY(3000); /* 3ms is long enough for 1 frame */ -#ifdef ATH_DEBUG - if (sc->sc_debug & (ATH_DEBUG_RESET | ATH_DEBUG_FATAL)) { - struct ath_buf *bf; - u_int ix; - - device_printf(sc->sc_dev, - "%s: rx queue %p, link %p\n", - __func__, - (caddr_t)(uintptr_t) ath_hal_getrxbuf(ah), - sc->sc_rxlink); - ix = 0; - TAILQ_FOREACH(bf, &sc->sc_rxbuf, bf_list) { - struct ath_desc *ds = bf->bf_desc; - struct ath_rx_status *rs = &bf->bf_status.ds_rxstat; - HAL_STATUS status = ath_hal_rxprocdesc(ah, ds, - bf->bf_daddr, PA2DESC(sc, ds->ds_link), rs); - if (status == HAL_OK || (sc->sc_debug & ATH_DEBUG_FATAL)) - ath_printrxbuf(sc, bf, ix, status == HAL_OK); - ix++; - } - } -#endif - if (sc->sc_rxpending != NULL) { - m_freem(sc->sc_rxpending); - sc->sc_rxpending = NULL; - } - sc->sc_rxlink = NULL; /* just in case */ -#undef PA2DESC -} - -/* - * Enable the receive h/w following a reset. - */ -static int -ath_startrecv(struct ath_softc *sc) -{ - struct ath_hal *ah = sc->sc_ah; - struct ath_buf *bf; - - sc->sc_rxlink = NULL; - sc->sc_rxpending = NULL; - TAILQ_FOREACH(bf, &sc->sc_rxbuf, bf_list) { - int error = ath_rxbuf_init(sc, bf); - if (error != 0) { - DPRINTF(sc, ATH_DEBUG_RECV, - "%s: ath_rxbuf_init failed %d\n", - __func__, error); - return error; - } - } - - bf = TAILQ_FIRST(&sc->sc_rxbuf); - ath_hal_putrxbuf(ah, bf->bf_daddr); - ath_hal_rxena(ah); /* enable recv descriptors */ - ath_mode_init(sc); /* set filters, etc. */ - ath_hal_startpcurecv(ah); /* re-enable PCU/DMA engine */ - return 0; -} - /* * Update internal state after a channel change. */ diff --git a/sys/dev/ath/if_ath_misc.h b/sys/dev/ath/if_ath_misc.h index c48590edac4..ebc3a675fad 100644 --- a/sys/dev/ath/if_ath_misc.h +++ b/sys/dev/ath/if_ath_misc.h @@ -67,4 +67,19 @@ extern void ath_tx_update_ratectrl(struct ath_softc *sc, extern void ath_tx_freebuf(struct ath_softc *sc, struct ath_buf *bf, int status); +extern void ath_mode_init(struct ath_softc *sc); + +extern void ath_setdefantenna(struct ath_softc *sc, u_int antenna); + +/* + * This is only here so that the RX proc function can call it. + * It's very likely that the "start TX after RX" call should be + * done via something in if_ath.c, moving "rx tasklet" into + * if_ath.c and do the ath_start() call there. Once that's done, + * we can kill this. + */ +extern void ath_start(struct ifnet *ifp); + +extern void ath_beacon_config(struct ath_softc *sc, struct ieee80211vap *vap); + #endif diff --git a/sys/dev/ath/if_ath_rx.c b/sys/dev/ath/if_ath_rx.c new file mode 100644 index 00000000000..14b2346674e --- /dev/null +++ b/sys/dev/ath/if_ath_rx.c @@ -0,0 +1,967 @@ +/*- + * 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 +__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" +/* + * This is needed for register operations which are performed + * by the driver - eg, calls to ath_hal_gettsf32(). + * + * It's also required for any AH_DEBUG checks in here, eg the + * module dependencies. + */ +#include "opt_ah.h" +#include "opt_wlan.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for mp_ncpus */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#ifdef IEEE80211_SUPPORT_SUPERG +#include +#endif +#ifdef IEEE80211_SUPPORT_TDMA +#include +#endif + +#include + +#ifdef INET +#include +#include +#endif + +#include +#include /* XXX for softled */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef ATH_TX99_DIAG +#include +#endif + +#define ATH_KTR_INTR KTR_SPARE4 +#define ATH_KTR_ERR KTR_SPARE3 + +/* + * Calculate the receive filter according to the + * operating mode and state: + * + * o always accept unicast, broadcast, and multicast traffic + * o accept PHY error frames when hardware doesn't have MIB support + * to count and we need them for ANI (sta mode only until recently) + * and we are not scanning (ANI is disabled) + * NB: older hal's add rx filter bits out of sight and we need to + * blindly preserve them + * o probe request frames are accepted only when operating in + * hostap, adhoc, mesh, or monitor modes + * o enable promiscuous mode + * - when in monitor mode + * - if interface marked PROMISC (assumes bridge setting is filtered) + * o accept beacons: + * - when operating in station mode for collecting rssi data when + * the station is otherwise quiet, or + * - when operating in adhoc mode so the 802.11 layer creates + * node table entries for peers, + * - when scanning + * - when doing s/w beacon miss (e.g. for ap+sta) + * - when operating in ap mode in 11g to detect overlapping bss that + * require protection + * - when operating in mesh mode to detect neighbors + * o accept control frames: + * - when in monitor mode + * XXX HT protection for 11n + */ +u_int32_t +ath_calcrxfilter(struct ath_softc *sc) +{ + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + u_int32_t rfilt; + + rfilt = HAL_RX_FILTER_UCAST | HAL_RX_FILTER_BCAST | HAL_RX_FILTER_MCAST; + if (!sc->sc_needmib && !sc->sc_scanning) + rfilt |= HAL_RX_FILTER_PHYERR; + if (ic->ic_opmode != IEEE80211_M_STA) + rfilt |= HAL_RX_FILTER_PROBEREQ; + /* XXX ic->ic_monvaps != 0? */ + if (ic->ic_opmode == IEEE80211_M_MONITOR || (ifp->if_flags & IFF_PROMISC)) + rfilt |= HAL_RX_FILTER_PROM; + if (ic->ic_opmode == IEEE80211_M_STA || + ic->ic_opmode == IEEE80211_M_IBSS || + sc->sc_swbmiss || sc->sc_scanning) + rfilt |= HAL_RX_FILTER_BEACON; + /* + * NB: We don't recalculate the rx filter when + * ic_protmode changes; otherwise we could do + * this only when ic_protmode != NONE. + */ + if (ic->ic_opmode == IEEE80211_M_HOSTAP && + IEEE80211_IS_CHAN_ANYG(ic->ic_curchan)) + rfilt |= HAL_RX_FILTER_BEACON; + + /* + * Enable hardware PS-POLL RX only for hostap mode; + * STA mode sends PS-POLL frames but never + * receives them. + */ + if (ath_hal_getcapability(sc->sc_ah, HAL_CAP_PSPOLL, + 0, NULL) == HAL_OK && + ic->ic_opmode == IEEE80211_M_HOSTAP) + rfilt |= HAL_RX_FILTER_PSPOLL; + + if (sc->sc_nmeshvaps) { + rfilt |= HAL_RX_FILTER_BEACON; + if (sc->sc_hasbmatch) + rfilt |= HAL_RX_FILTER_BSSID; + else + rfilt |= HAL_RX_FILTER_PROM; + } + if (ic->ic_opmode == IEEE80211_M_MONITOR) + rfilt |= HAL_RX_FILTER_CONTROL; + + /* + * Enable RX of compressed BAR frames only when doing + * 802.11n. Required for A-MPDU. + */ + if (IEEE80211_IS_CHAN_HT(ic->ic_curchan)) + rfilt |= HAL_RX_FILTER_COMPBAR; + + /* + * Enable radar PHY errors if requested by the + * DFS module. + */ + if (sc->sc_dodfs) + rfilt |= HAL_RX_FILTER_PHYRADAR; + + DPRINTF(sc, ATH_DEBUG_MODE, "%s: RX filter 0x%x, %s if_flags 0x%x\n", + __func__, rfilt, ieee80211_opmode_name[ic->ic_opmode], ifp->if_flags); + return rfilt; +} + +int +ath_rxbuf_init(struct ath_softc *sc, struct ath_buf *bf) +{ + struct ath_hal *ah = sc->sc_ah; + int error; + struct mbuf *m; + struct ath_desc *ds; + + m = bf->bf_m; + if (m == NULL) { + /* + * NB: by assigning a page to the rx dma buffer we + * implicitly satisfy the Atheros requirement that + * this buffer be cache-line-aligned and sized to be + * multiple of the cache line size. Not doing this + * causes weird stuff to happen (for the 5210 at least). + */ + m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); + if (m == NULL) { + DPRINTF(sc, ATH_DEBUG_ANY, + "%s: no mbuf/cluster\n", __func__); + sc->sc_stats.ast_rx_nombuf++; + return ENOMEM; + } + m->m_pkthdr.len = m->m_len = m->m_ext.ext_size; + + error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, + bf->bf_dmamap, m, + bf->bf_segs, &bf->bf_nseg, + BUS_DMA_NOWAIT); + if (error != 0) { + DPRINTF(sc, ATH_DEBUG_ANY, + "%s: bus_dmamap_load_mbuf_sg failed; error %d\n", + __func__, error); + sc->sc_stats.ast_rx_busdma++; + m_freem(m); + return error; + } + KASSERT(bf->bf_nseg == 1, + ("multi-segment packet; nseg %u", bf->bf_nseg)); + bf->bf_m = m; + } + bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREREAD); + + /* + * Setup descriptors. For receive we always terminate + * the descriptor list with a self-linked entry so we'll + * not get overrun under high load (as can happen with a + * 5212 when ANI processing enables PHY error frames). + * + * To insure the last descriptor is self-linked we create + * each descriptor as self-linked and add it to the end. As + * each additional descriptor is added the previous self-linked + * entry is ``fixed'' naturally. This should be safe even + * if DMA is happening. When processing RX interrupts we + * never remove/process the last, self-linked, entry on the + * descriptor list. This insures the hardware always has + * someplace to write a new frame. + */ + /* + * 11N: we can no longer afford to self link the last descriptor. + * MAC acknowledges BA status as long as it copies frames to host + * buffer (or rx fifo). This can incorrectly acknowledge packets + * to a sender if last desc is self-linked. + */ + ds = bf->bf_desc; + if (sc->sc_rxslink) + ds->ds_link = bf->bf_daddr; /* link to self */ + else + ds->ds_link = 0; /* terminate the list */ + ds->ds_data = bf->bf_segs[0].ds_addr; + ath_hal_setuprxdesc(ah, ds + , m->m_len /* buffer size */ + , 0 + ); + + if (sc->sc_rxlink != NULL) + *sc->sc_rxlink = bf->bf_daddr; + sc->sc_rxlink = &ds->ds_link; + return 0; +} + +/* + * Intercept management frames to collect beacon rssi data + * and to do ibss merges. + */ +void +ath_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m, + int subtype, int rssi, int nf) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc; + + /* + * Call up first so subsequent work can use information + * potentially stored in the node (e.g. for ibss merge). + */ + ATH_VAP(vap)->av_recv_mgmt(ni, m, subtype, rssi, nf); + switch (subtype) { + case IEEE80211_FC0_SUBTYPE_BEACON: + /* update rssi statistics for use by the hal */ + /* XXX unlocked check against vap->iv_bss? */ + ATH_RSSI_LPF(sc->sc_halstats.ns_avgbrssi, rssi); + if (sc->sc_syncbeacon && + ni == vap->iv_bss && vap->iv_state == IEEE80211_S_RUN) { + /* + * Resync beacon timers using the tsf of the beacon + * frame we just received. + */ + ath_beacon_config(sc, vap); + } + /* fall thru... */ + case IEEE80211_FC0_SUBTYPE_PROBE_RESP: + if (vap->iv_opmode == IEEE80211_M_IBSS && + vap->iv_state == IEEE80211_S_RUN) { + uint32_t rstamp = sc->sc_lastrs->rs_tstamp; + uint64_t tsf = ath_extend_tsf(sc, rstamp, + ath_hal_gettsf64(sc->sc_ah)); + /* + * Handle ibss merge as needed; check the tsf on the + * frame before attempting the merge. The 802.11 spec + * says the station should change it's bssid to match + * the oldest station with the same ssid, where oldest + * is determined by the tsf. Note that hardware + * reconfiguration happens through callback to + * ath_newstate as the state machine will go from + * RUN -> RUN when this happens. + */ + if (le64toh(ni->ni_tstamp.tsf) >= tsf) { + DPRINTF(sc, ATH_DEBUG_STATE, + "ibss merge, rstamp %u tsf %ju " + "tstamp %ju\n", rstamp, (uintmax_t)tsf, + (uintmax_t)ni->ni_tstamp.tsf); + (void) ieee80211_ibss_merge(ni); + } + } + break; + } +} + +static void +ath_rx_tap(struct ifnet *ifp, struct mbuf *m, + const struct ath_rx_status *rs, u_int64_t tsf, int16_t nf) +{ +#define CHAN_HT20 htole32(IEEE80211_CHAN_HT20) +#define CHAN_HT40U htole32(IEEE80211_CHAN_HT40U) +#define CHAN_HT40D htole32(IEEE80211_CHAN_HT40D) +#define CHAN_HT (CHAN_HT20|CHAN_HT40U|CHAN_HT40D) + struct ath_softc *sc = ifp->if_softc; + const HAL_RATE_TABLE *rt; + uint8_t rix; + + rt = sc->sc_currates; + KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode)); + rix = rt->rateCodeToIndex[rs->rs_rate]; + sc->sc_rx_th.wr_rate = sc->sc_hwmap[rix].ieeerate; + sc->sc_rx_th.wr_flags = sc->sc_hwmap[rix].rxflags; +#ifdef AH_SUPPORT_AR5416 + sc->sc_rx_th.wr_chan_flags &= ~CHAN_HT; + if (sc->sc_rx_th.wr_rate & IEEE80211_RATE_MCS) { /* HT rate */ + struct ieee80211com *ic = ifp->if_l2com; + + if ((rs->rs_flags & HAL_RX_2040) == 0) + sc->sc_rx_th.wr_chan_flags |= CHAN_HT20; + else if (IEEE80211_IS_CHAN_HT40U(ic->ic_curchan)) + sc->sc_rx_th.wr_chan_flags |= CHAN_HT40U; + else + sc->sc_rx_th.wr_chan_flags |= CHAN_HT40D; + if ((rs->rs_flags & HAL_RX_GI) == 0) + sc->sc_rx_th.wr_flags |= IEEE80211_RADIOTAP_F_SHORTGI; + } +#endif + sc->sc_rx_th.wr_tsf = htole64(ath_extend_tsf(sc, rs->rs_tstamp, tsf)); + if (rs->rs_status & HAL_RXERR_CRC) + sc->sc_rx_th.wr_flags |= IEEE80211_RADIOTAP_F_BADFCS; + /* XXX propagate other error flags from descriptor */ + sc->sc_rx_th.wr_antnoise = nf; + sc->sc_rx_th.wr_antsignal = nf + rs->rs_rssi; + sc->sc_rx_th.wr_antenna = rs->rs_antenna; +#undef CHAN_HT +#undef CHAN_HT20 +#undef CHAN_HT40U +#undef CHAN_HT40D +} + +static void +ath_handle_micerror(struct ieee80211com *ic, + struct ieee80211_frame *wh, int keyix) +{ + struct ieee80211_node *ni; + + /* XXX recheck MIC to deal w/ chips that lie */ + /* XXX discard MIC errors on !data frames */ + ni = ieee80211_find_rxnode(ic, (const struct ieee80211_frame_min *) wh); + if (ni != NULL) { + ieee80211_notify_michael_failure(ni->ni_vap, wh, keyix); + ieee80211_free_node(ni); + } +} + +/* + * Only run the RX proc if it's not already running. + * Since this may get run as part of the reset/flush path, + * the task can't clash with an existing, running tasklet. + */ +void +ath_rx_tasklet(void *arg, int npending) +{ + struct ath_softc *sc = arg; + + CTR1(ATH_KTR_INTR, "ath_rx_proc: pending=%d", npending); + DPRINTF(sc, ATH_DEBUG_RX_PROC, "%s: pending %u\n", __func__, npending); + ATH_PCU_LOCK(sc); + if (sc->sc_inreset_cnt > 0) { + device_printf(sc->sc_dev, + "%s: sc_inreset_cnt > 0; skipping\n", __func__); + ATH_PCU_UNLOCK(sc); + return; + } + ATH_PCU_UNLOCK(sc); + ath_rx_proc(sc, 1); +} + +void +ath_rx_proc(struct ath_softc *sc, int resched) +{ +#define PA2DESC(_sc, _pa) \ + ((struct ath_desc *)((caddr_t)(_sc)->sc_rxdma.dd_desc + \ + ((_pa) - (_sc)->sc_rxdma.dd_desc_paddr))) + struct ath_buf *bf; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + struct ath_hal *ah = sc->sc_ah; + struct ath_desc *ds; + struct ath_rx_status *rs; + struct mbuf *m; + struct ieee80211_node *ni; + int len, type, ngood; + HAL_STATUS status; + int16_t nf; + u_int64_t tsf, rstamp; + int npkts = 0; + + /* XXX we must not hold the ATH_LOCK here */ + ATH_UNLOCK_ASSERT(sc); + ATH_PCU_UNLOCK_ASSERT(sc); + + ATH_PCU_LOCK(sc); + sc->sc_rxproc_cnt++; + ATH_PCU_UNLOCK(sc); + + DPRINTF(sc, ATH_DEBUG_RX_PROC, "%s: called\n", __func__); + ngood = 0; + nf = ath_hal_getchannoise(ah, sc->sc_curchan); + sc->sc_stats.ast_rx_noise = nf; + tsf = ath_hal_gettsf64(ah); + do { + bf = TAILQ_FIRST(&sc->sc_rxbuf); + if (sc->sc_rxslink && bf == NULL) { /* NB: shouldn't happen */ + if_printf(ifp, "%s: no buffer!\n", __func__); + break; + } else if (bf == NULL) { + /* + * End of List: + * this can happen for non-self-linked RX chains + */ + sc->sc_stats.ast_rx_hitqueueend++; + break; + } + m = bf->bf_m; + if (m == NULL) { /* NB: shouldn't happen */ + /* + * If mbuf allocation failed previously there + * will be no mbuf; try again to re-populate it. + */ + /* XXX make debug msg */ + if_printf(ifp, "%s: no mbuf!\n", __func__); + TAILQ_REMOVE(&sc->sc_rxbuf, bf, bf_list); + goto rx_next; + } + ds = bf->bf_desc; + if (ds->ds_link == bf->bf_daddr) { + /* NB: never process the self-linked entry at the end */ + sc->sc_stats.ast_rx_hitqueueend++; + break; + } + /* XXX sync descriptor memory */ + /* + * Must provide the virtual address of the current + * descriptor, the physical address, and the virtual + * address of the next descriptor in the h/w chain. + * This allows the HAL to look ahead to see if the + * hardware is done with a descriptor by checking the + * done bit in the following descriptor and the address + * of the current descriptor the DMA engine is working + * on. All this is necessary because of our use of + * a self-linked list to avoid rx overruns. + */ + rs = &bf->bf_status.ds_rxstat; + status = ath_hal_rxprocdesc(ah, ds, + bf->bf_daddr, PA2DESC(sc, ds->ds_link), rs); +#ifdef ATH_DEBUG + if (sc->sc_debug & ATH_DEBUG_RECV_DESC) + ath_printrxbuf(sc, bf, 0, status == HAL_OK); +#endif + if (status == HAL_EINPROGRESS) + break; + + TAILQ_REMOVE(&sc->sc_rxbuf, bf, bf_list); + npkts++; + + /* + * Calculate the correct 64 bit TSF given + * the TSF64 register value and rs_tstamp. + */ + rstamp = ath_extend_tsf(sc, rs->rs_tstamp, tsf); + + /* These aren't specifically errors */ +#ifdef AH_SUPPORT_AR5416 + if (rs->rs_flags & HAL_RX_GI) + sc->sc_stats.ast_rx_halfgi++; + if (rs->rs_flags & HAL_RX_2040) + sc->sc_stats.ast_rx_2040++; + if (rs->rs_flags & HAL_RX_DELIM_CRC_PRE) + sc->sc_stats.ast_rx_pre_crc_err++; + if (rs->rs_flags & HAL_RX_DELIM_CRC_POST) + sc->sc_stats.ast_rx_post_crc_err++; + if (rs->rs_flags & HAL_RX_DECRYPT_BUSY) + sc->sc_stats.ast_rx_decrypt_busy_err++; + if (rs->rs_flags & HAL_RX_HI_RX_CHAIN) + sc->sc_stats.ast_rx_hi_rx_chain++; +#endif /* AH_SUPPORT_AR5416 */ + + if (rs->rs_status != 0) { + if (rs->rs_status & HAL_RXERR_CRC) + sc->sc_stats.ast_rx_crcerr++; + if (rs->rs_status & HAL_RXERR_FIFO) + sc->sc_stats.ast_rx_fifoerr++; + if (rs->rs_status & HAL_RXERR_PHY) { + sc->sc_stats.ast_rx_phyerr++; + /* Process DFS radar events */ + if ((rs->rs_phyerr == HAL_PHYERR_RADAR) || + (rs->rs_phyerr == HAL_PHYERR_FALSE_RADAR_EXT)) { + /* Since we're touching the frame data, sync it */ + bus_dmamap_sync(sc->sc_dmat, + bf->bf_dmamap, + BUS_DMASYNC_POSTREAD); + /* Now pass it to the radar processing code */ + ath_dfs_process_phy_err(sc, mtod(m, char *), rstamp, rs); + } + + /* Be suitably paranoid about receiving phy errors out of the stats array bounds */ + if (rs->rs_phyerr < 64) + sc->sc_stats.ast_rx_phy[rs->rs_phyerr]++; + goto rx_error; /* NB: don't count in ierrors */ + } + if (rs->rs_status & HAL_RXERR_DECRYPT) { + /* + * Decrypt error. If the error occurred + * because there was no hardware key, then + * let the frame through so the upper layers + * can process it. This is necessary for 5210 + * parts which have no way to setup a ``clear'' + * key cache entry. + * + * XXX do key cache faulting + */ + if (rs->rs_keyix == HAL_RXKEYIX_INVALID) + goto rx_accept; + sc->sc_stats.ast_rx_badcrypt++; + } + if (rs->rs_status & HAL_RXERR_MIC) { + sc->sc_stats.ast_rx_badmic++; + /* + * Do minimal work required to hand off + * the 802.11 header for notification. + */ + /* XXX frag's and qos frames */ + len = rs->rs_datalen; + if (len >= sizeof (struct ieee80211_frame)) { + bus_dmamap_sync(sc->sc_dmat, + bf->bf_dmamap, + BUS_DMASYNC_POSTREAD); + ath_handle_micerror(ic, + mtod(m, struct ieee80211_frame *), + sc->sc_splitmic ? + rs->rs_keyix-32 : rs->rs_keyix); + } + } + ifp->if_ierrors++; +rx_error: + /* + * Cleanup any pending partial frame. + */ + if (sc->sc_rxpending != NULL) { + m_freem(sc->sc_rxpending); + sc->sc_rxpending = NULL; + } + /* + * When a tap is present pass error frames + * that have been requested. By default we + * pass decrypt+mic errors but others may be + * interesting (e.g. crc). + */ + if (ieee80211_radiotap_active(ic) && + (rs->rs_status & sc->sc_monpass)) { + bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, + BUS_DMASYNC_POSTREAD); + /* NB: bpf needs the mbuf length setup */ + len = rs->rs_datalen; + m->m_pkthdr.len = m->m_len = len; + bf->bf_m = NULL; + ath_rx_tap(ifp, m, rs, rstamp, nf); + ieee80211_radiotap_rx_all(ic, m); + m_freem(m); + } + /* XXX pass MIC errors up for s/w reclaculation */ + goto rx_next; + } +rx_accept: + /* + * Sync and unmap the frame. At this point we're + * committed to passing the mbuf somewhere so clear + * bf_m; this means a new mbuf must be allocated + * when the rx descriptor is setup again to receive + * another frame. + */ + bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, + BUS_DMASYNC_POSTREAD); + bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); + bf->bf_m = NULL; + + len = rs->rs_datalen; + m->m_len = len; + + if (rs->rs_more) { + /* + * Frame spans multiple descriptors; save + * it for the next completed descriptor, it + * will be used to construct a jumbogram. + */ + if (sc->sc_rxpending != NULL) { + /* NB: max frame size is currently 2 clusters */ + sc->sc_stats.ast_rx_toobig++; + m_freem(sc->sc_rxpending); + } + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = len; + sc->sc_rxpending = m; + goto rx_next; + } else if (sc->sc_rxpending != NULL) { + /* + * This is the second part of a jumbogram, + * chain it to the first mbuf, adjust the + * frame length, and clear the rxpending state. + */ + sc->sc_rxpending->m_next = m; + sc->sc_rxpending->m_pkthdr.len += len; + m = sc->sc_rxpending; + sc->sc_rxpending = NULL; + } else { + /* + * Normal single-descriptor receive; setup + * the rcvif and packet length. + */ + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = len; + } + + /* + * Validate rs->rs_antenna. + * + * Some users w/ AR9285 NICs have reported crashes + * here because rs_antenna field is bogusly large. + * Let's enforce the maximum antenna limit of 8 + * (and it shouldn't be hard coded, but that's a + * separate problem) and if there's an issue, print + * out an error and adjust rs_antenna to something + * sensible. + * + * This code should be removed once the actual + * root cause of the issue has been identified. + * For example, it may be that the rs_antenna + * field is only valid for the lsat frame of + * an aggregate and it just happens that it is + * "mostly" right. (This is a general statement - + * the majority of the statistics are only valid + * for the last frame in an aggregate. + */ + if (rs->rs_antenna > 7) { + device_printf(sc->sc_dev, "%s: rs_antenna > 7 (%d)\n", + __func__, rs->rs_antenna); +#ifdef ATH_DEBUG + ath_printrxbuf(sc, bf, 0, status == HAL_OK); +#endif /* ATH_DEBUG */ + rs->rs_antenna = 0; /* XXX better than nothing */ + } + + ifp->if_ipackets++; + sc->sc_stats.ast_ant_rx[rs->rs_antenna]++; + + /* + * Populate the rx status block. When there are bpf + * listeners we do the additional work to provide + * complete status. Otherwise we fill in only the + * material required by ieee80211_input. Note that + * noise setting is filled in above. + */ + if (ieee80211_radiotap_active(ic)) + ath_rx_tap(ifp, m, rs, rstamp, nf); + + /* + * From this point on we assume the frame is at least + * as large as ieee80211_frame_min; verify that. + */ + if (len < IEEE80211_MIN_LEN) { + if (!ieee80211_radiotap_active(ic)) { + DPRINTF(sc, ATH_DEBUG_RECV, + "%s: short packet %d\n", __func__, len); + sc->sc_stats.ast_rx_tooshort++; + } else { + /* NB: in particular this captures ack's */ + ieee80211_radiotap_rx_all(ic, m); + } + m_freem(m); + goto rx_next; + } + + if (IFF_DUMPPKTS(sc, ATH_DEBUG_RECV)) { + const HAL_RATE_TABLE *rt = sc->sc_currates; + uint8_t rix = rt->rateCodeToIndex[rs->rs_rate]; + + ieee80211_dump_pkt(ic, mtod(m, caddr_t), len, + sc->sc_hwmap[rix].ieeerate, rs->rs_rssi); + } + + m_adj(m, -IEEE80211_CRC_LEN); + + /* + * Locate the node for sender, track state, and then + * pass the (referenced) node up to the 802.11 layer + * for its use. + */ + ni = ieee80211_find_rxnode_withkey(ic, + mtod(m, const struct ieee80211_frame_min *), + rs->rs_keyix == HAL_RXKEYIX_INVALID ? + IEEE80211_KEYIX_NONE : rs->rs_keyix); + sc->sc_lastrs = rs; + +#ifdef AH_SUPPORT_AR5416 + if (rs->rs_isaggr) + sc->sc_stats.ast_rx_agg++; +#endif /* AH_SUPPORT_AR5416 */ + + if (ni != NULL) { + /* + * Only punt packets for ampdu reorder processing for + * 11n nodes; net80211 enforces that M_AMPDU is only + * set for 11n nodes. + */ + if (ni->ni_flags & IEEE80211_NODE_HT) + m->m_flags |= M_AMPDU; + + /* + * Sending station is known, dispatch directly. + */ + type = ieee80211_input(ni, m, rs->rs_rssi, nf); + ieee80211_free_node(ni); + /* + * Arrange to update the last rx timestamp only for + * frames from our ap when operating in station mode. + * This assumes the rx key is always setup when + * associated. + */ + if (ic->ic_opmode == IEEE80211_M_STA && + rs->rs_keyix != HAL_RXKEYIX_INVALID) + ngood++; + } else { + type = ieee80211_input_all(ic, m, rs->rs_rssi, nf); + } + /* + * Track rx rssi and do any rx antenna management. + */ + ATH_RSSI_LPF(sc->sc_halstats.ns_avgrssi, rs->rs_rssi); + if (sc->sc_diversity) { + /* + * When using fast diversity, change the default rx + * antenna if diversity chooses the other antenna 3 + * times in a row. + */ + if (sc->sc_defant != rs->rs_antenna) { + if (++sc->sc_rxotherant >= 3) + ath_setdefantenna(sc, rs->rs_antenna); + } else + sc->sc_rxotherant = 0; + } + + /* Newer school diversity - kite specific for now */ + /* XXX perhaps migrate the normal diversity code to this? */ + if ((ah)->ah_rxAntCombDiversity) + (*(ah)->ah_rxAntCombDiversity)(ah, rs, ticks, hz); + + if (sc->sc_softled) { + /* + * Blink for any data frame. Otherwise do a + * heartbeat-style blink when idle. The latter + * is mainly for station mode where we depend on + * periodic beacon frames to trigger the poll event. + */ + if (type == IEEE80211_FC0_TYPE_DATA) { + const HAL_RATE_TABLE *rt = sc->sc_currates; + ath_led_event(sc, + rt->rateCodeToIndex[rs->rs_rate]); + } else if (ticks - sc->sc_ledevent >= sc->sc_ledidle) + ath_led_event(sc, 0); + } +rx_next: + TAILQ_INSERT_TAIL(&sc->sc_rxbuf, bf, bf_list); + } while (ath_rxbuf_init(sc, bf) == 0); + + /* rx signal state monitoring */ + ath_hal_rxmonitor(ah, &sc->sc_halstats, sc->sc_curchan); + if (ngood) + sc->sc_lastrx = tsf; + + CTR2(ATH_KTR_INTR, "ath_rx_proc: npkts=%d, ngood=%d", npkts, ngood); + /* Queue DFS tasklet if needed */ + if (resched && ath_dfs_tasklet_needed(sc, sc->sc_curchan)) + taskqueue_enqueue(sc->sc_tq, &sc->sc_dfstask); + + /* + * Now that all the RX frames were handled that + * need to be handled, kick the PCU if there's + * been an RXEOL condition. + */ + ATH_PCU_LOCK(sc); + if (resched && sc->sc_kickpcu) { + CTR0(ATH_KTR_ERR, "ath_rx_proc: kickpcu"); + device_printf(sc->sc_dev, "%s: kickpcu; handled %d packets\n", + __func__, npkts); + + /* XXX rxslink? */ + /* + * XXX can we hold the PCU lock here? + * Are there any net80211 buffer calls involved? + */ + bf = TAILQ_FIRST(&sc->sc_rxbuf); + ath_hal_putrxbuf(ah, bf->bf_daddr); + ath_hal_rxena(ah); /* enable recv descriptors */ + ath_mode_init(sc); /* set filters, etc. */ + ath_hal_startpcurecv(ah); /* re-enable PCU/DMA engine */ + + ath_hal_intrset(ah, sc->sc_imask); + sc->sc_kickpcu = 0; + } + ATH_PCU_UNLOCK(sc); + + /* XXX check this inside of IF_LOCK? */ + if (resched && (ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0) { +#ifdef IEEE80211_SUPPORT_SUPERG + ieee80211_ff_age_all(ic, 100); +#endif + if (!IFQ_IS_EMPTY(&ifp->if_snd)) + ath_start(ifp); + } +#undef PA2DESC + + ATH_PCU_LOCK(sc); + sc->sc_rxproc_cnt--; + ATH_PCU_UNLOCK(sc); +} + +/* + * Disable the receive h/w in preparation for a reset. + */ +void +ath_stoprecv(struct ath_softc *sc, int dodelay) +{ +#define PA2DESC(_sc, _pa) \ + ((struct ath_desc *)((caddr_t)(_sc)->sc_rxdma.dd_desc + \ + ((_pa) - (_sc)->sc_rxdma.dd_desc_paddr))) + struct ath_hal *ah = sc->sc_ah; + + ath_hal_stoppcurecv(ah); /* disable PCU */ + ath_hal_setrxfilter(ah, 0); /* clear recv filter */ + ath_hal_stopdmarecv(ah); /* disable DMA engine */ + /* + * TODO: see if this particular DELAY() is required; it may be + * masking some missing FIFO flush or DMA sync. + */ +#if 0 + if (dodelay) +#endif + DELAY(3000); /* 3ms is long enough for 1 frame */ +#ifdef ATH_DEBUG + if (sc->sc_debug & (ATH_DEBUG_RESET | ATH_DEBUG_FATAL)) { + struct ath_buf *bf; + u_int ix; + + device_printf(sc->sc_dev, + "%s: rx queue %p, link %p\n", + __func__, + (caddr_t)(uintptr_t) ath_hal_getrxbuf(ah), + sc->sc_rxlink); + ix = 0; + TAILQ_FOREACH(bf, &sc->sc_rxbuf, bf_list) { + struct ath_desc *ds = bf->bf_desc; + struct ath_rx_status *rs = &bf->bf_status.ds_rxstat; + HAL_STATUS status = ath_hal_rxprocdesc(ah, ds, + bf->bf_daddr, PA2DESC(sc, ds->ds_link), rs); + if (status == HAL_OK || (sc->sc_debug & ATH_DEBUG_FATAL)) + ath_printrxbuf(sc, bf, ix, status == HAL_OK); + ix++; + } + } +#endif + if (sc->sc_rxpending != NULL) { + m_freem(sc->sc_rxpending); + sc->sc_rxpending = NULL; + } + sc->sc_rxlink = NULL; /* just in case */ +#undef PA2DESC +} + +/* + * Enable the receive h/w following a reset. + */ +int +ath_startrecv(struct ath_softc *sc) +{ + struct ath_hal *ah = sc->sc_ah; + struct ath_buf *bf; + + sc->sc_rxlink = NULL; + sc->sc_rxpending = NULL; + TAILQ_FOREACH(bf, &sc->sc_rxbuf, bf_list) { + int error = ath_rxbuf_init(sc, bf); + if (error != 0) { + DPRINTF(sc, ATH_DEBUG_RECV, + "%s: ath_rxbuf_init failed %d\n", + __func__, error); + return error; + } + } + + bf = TAILQ_FIRST(&sc->sc_rxbuf); + ath_hal_putrxbuf(ah, bf->bf_daddr); + ath_hal_rxena(ah); /* enable recv descriptors */ + ath_mode_init(sc); /* set filters, etc. */ + ath_hal_startpcurecv(ah); /* re-enable PCU/DMA engine */ + return 0; +} diff --git a/sys/dev/ath/if_ath_rx.h b/sys/dev/ath/if_ath_rx.h new file mode 100644 index 00000000000..d3bd4306a4c --- /dev/null +++ b/sys/dev/ath/if_ath_rx.h @@ -0,0 +1,43 @@ +/*- + * 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_RX_H__ +#define __IF_ATH_RX_H__ + +extern u_int32_t ath_calcrxfilter(struct ath_softc *sc); +extern int ath_rxbuf_init(struct ath_softc *sc, struct ath_buf *bf); +extern void ath_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m, + int subtype, int rssi, int nf); +extern void ath_rx_tasklet(void *arg, int npending); +extern void ath_rx_proc(struct ath_softc *sc, int resched); +extern void ath_stoprecv(struct ath_softc *sc, int dodelay); +extern int ath_startrecv(struct ath_softc *sc); + +#endif diff --git a/sys/dev/ath/if_ath_tsf.h b/sys/dev/ath/if_ath_tsf.h new file mode 100644 index 00000000000..cce089fab84 --- /dev/null +++ b/sys/dev/ath/if_ath_tsf.h @@ -0,0 +1,81 @@ +/*- + * 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_TSF_H__ +#define __IF_ATH_TSF_H__ + +/* + * Extend 15-bit time stamp from rx descriptor to + * a full 64-bit TSF using the specified TSF. + */ +static __inline u_int64_t +ath_extend_tsf15(u_int32_t rstamp, u_int64_t tsf) +{ + if ((tsf & 0x7fff) < rstamp) + tsf -= 0x8000; + + return ((tsf &~ 0x7fff) | rstamp); +} + +/* + * Extend 32-bit time stamp from rx descriptor to + * a full 64-bit TSF using the specified TSF. + */ +static __inline u_int64_t +ath_extend_tsf32(u_int32_t rstamp, u_int64_t tsf) +{ + u_int32_t tsf_low = tsf & 0xffffffff; + u_int64_t tsf64 = (tsf & ~0xffffffffULL) | rstamp; + + if (rstamp > tsf_low && (rstamp - tsf_low > 0x10000000)) + tsf64 -= 0x100000000ULL; + + if (rstamp < tsf_low && (tsf_low - rstamp > 0x10000000)) + tsf64 += 0x100000000ULL; + + return tsf64; +} + +/* + * Extend the TSF from the RX descriptor to a full 64 bit TSF. + * Earlier hardware versions only wrote the low 15 bits of the + * TSF into the RX descriptor; later versions (AR5416 and up) + * include the 32 bit TSF value. + */ +static __inline u_int64_t +ath_extend_tsf(struct ath_softc *sc, u_int32_t rstamp, u_int64_t tsf) +{ + if (sc->sc_rxtsf32) + return ath_extend_tsf32(rstamp, tsf); + else + return ath_extend_tsf15(rstamp, tsf); +} + +#endif diff --git a/sys/dev/ath/if_ath_tx.c b/sys/dev/ath/if_ath_tx.c index f5c6d31bb59..abe045e43d7 100644 --- a/sys/dev/ath/if_ath_tx.c +++ b/sys/dev/ath/if_ath_tx.c @@ -2674,9 +2674,11 @@ ath_tx_tid_bar_suspend(struct ath_softc *sc, struct ath_tid *tid) ATH_TXQ_LOCK_ASSERT(sc->sc_ac2q[tid->ac]); DPRINTF(sc, ATH_DEBUG_SW_TX_BAR, - "%s: tid=%p, called\n", + "%s: tid=%p, bar_wait=%d, bar_tx=%d, called\n", __func__, - tid); + tid, + tid->bar_wait, + tid->bar_tx); /* We shouldn't be called when bar_tx is 1 */ if (tid->bar_tx) { @@ -4445,8 +4447,12 @@ ath_bar_response(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap, int attempts = tap->txa_attempts; DPRINTF(sc, ATH_DEBUG_SW_TX_BAR, - "%s: called; status=%d, attempts=%d\n", + "%s: called; tap=%p, atid=%p, txa_tid=%d, atid->tid=%d, status=%d, attempts=%d\n", __func__, + tap, + atid, + tap->txa_tid, + atid->tid, status, attempts);