summaryrefslogtreecommitdiffstats
path: root/sys/dev/ath/if_ath_rx.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/ath/if_ath_rx.c')
-rw-r--r--sys/dev/ath/if_ath_rx.c250
1 files changed, 229 insertions, 21 deletions
diff --git a/sys/dev/ath/if_ath_rx.c b/sys/dev/ath/if_ath_rx.c
index d9e212b..bed9488 100644
--- a/sys/dev/ath/if_ath_rx.c
+++ b/sys/dev/ath/if_ath_rx.c
@@ -73,6 +73,7 @@ __FBSDID("$FreeBSD$");
#include <machine/bus.h>
#include <net/if.h>
+#include <net/if_var.h>
#include <net/if_dl.h>
#include <net/if_media.h>
#include <net/if_types.h>
@@ -165,10 +166,22 @@ ath_calcrxfilter(struct ath_softc *sc)
/* XXX ic->ic_monvaps != 0? */
if (ic->ic_opmode == IEEE80211_M_MONITOR || (ifp->if_flags & IFF_PROMISC))
rfilt |= HAL_RX_FILTER_PROM;
+
+ /*
+ * Only listen to all beacons if we're scanning.
+ *
+ * Otherwise we only really need to hear beacons from
+ * our own BSSID.
+ */
if (ic->ic_opmode == IEEE80211_M_STA ||
- ic->ic_opmode == IEEE80211_M_IBSS ||
- sc->sc_swbmiss || sc->sc_scanning)
- rfilt |= HAL_RX_FILTER_BEACON;
+ ic->ic_opmode == IEEE80211_M_IBSS || sc->sc_swbmiss) {
+ if (sc->sc_do_mybeacon && ! sc->sc_scanning) {
+ rfilt |= HAL_RX_FILTER_MYBEACON;
+ } else { /* scanning, non-mybeacon chips */
+ rfilt |= HAL_RX_FILTER_BEACON;
+ }
+ }
+
/*
* NB: We don't recalculate the rx filter when
* ic_protmode changes; otherwise we could do
@@ -232,6 +245,8 @@ ath_legacy_rxbuf_init(struct ath_softc *sc, struct ath_buf *bf)
struct mbuf *m;
struct ath_desc *ds;
+ /* XXX TODO: ATH_RX_LOCK_ASSERT(sc); */
+
m = bf->bf_m;
if (m == NULL) {
/*
@@ -316,6 +331,23 @@ ath_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m,
{
struct ieee80211vap *vap = ni->ni_vap;
struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc;
+ uint64_t tsf_beacon_old, tsf_beacon;
+ uint64_t nexttbtt;
+ int64_t tsf_delta;
+ int32_t tsf_delta_bmiss;
+ int32_t tsf_remainder;
+ uint64_t tsf_beacon_target;
+ int tsf_intval;
+
+ tsf_beacon_old = ((uint64_t) LE_READ_4(ni->ni_tstamp.data + 4)) << 32;
+ tsf_beacon_old |= LE_READ_4(ni->ni_tstamp.data);
+
+#define TU_TO_TSF(_tu) (((u_int64_t)(_tu)) << 10)
+ tsf_intval = 1;
+ if (ni->ni_intval > 0) {
+ tsf_intval = TU_TO_TSF(ni->ni_intval);
+ }
+#undef TU_TO_TSF
/*
* Call up first so subsequent work can use information
@@ -327,14 +359,79 @@ ath_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m,
/* update rssi statistics for use by the hal */
/* XXX unlocked check against vap->iv_bss? */
ATH_RSSI_LPF(sc->sc_halstats.ns_avgbrssi, rssi);
+
+ tsf_beacon = ((uint64_t) LE_READ_4(ni->ni_tstamp.data + 4)) << 32;
+ tsf_beacon |= LE_READ_4(ni->ni_tstamp.data);
+
+ nexttbtt = ath_hal_getnexttbtt(sc->sc_ah);
+
+ /*
+ * Let's calculate the delta and remainder, so we can see
+ * if the beacon timer from the AP is varying by more than
+ * a few TU. (Which would be a huge, huge problem.)
+ */
+ tsf_delta = (long long) tsf_beacon - (long long) tsf_beacon_old;
+
+ tsf_delta_bmiss = tsf_delta / tsf_intval;
+
+ /*
+ * If our delta is greater than half the beacon interval,
+ * let's round the bmiss value up to the next beacon
+ * interval. Ie, we're running really, really early
+ * on the next beacon.
+ */
+ if (tsf_delta % tsf_intval > (tsf_intval / 2))
+ tsf_delta_bmiss ++;
+
+ tsf_beacon_target = tsf_beacon_old +
+ (((unsigned long long) tsf_delta_bmiss) * (long long) tsf_intval);
+
+ /*
+ * The remainder using '%' is between 0 .. intval-1.
+ * If we're actually running too fast, then the remainder
+ * will be some large number just under intval-1.
+ * So we need to look at whether we're running
+ * before or after the target beacon interval
+ * and if we are, modify how we do the remainder
+ * calculation.
+ */
+ if (tsf_beacon < tsf_beacon_target) {
+ tsf_remainder =
+ -(tsf_intval - ((tsf_beacon - tsf_beacon_old) % tsf_intval));
+ } else {
+ tsf_remainder = (tsf_beacon - tsf_beacon_old) % tsf_intval;
+ }
+
+ DPRINTF(sc, ATH_DEBUG_BEACON, "%s: old_tsf=%llu, new_tsf=%llu, target_tsf=%llu, delta=%lld, bmiss=%d, remainder=%d\n",
+ __func__,
+ (unsigned long long) tsf_beacon_old,
+ (unsigned long long) tsf_beacon,
+ (unsigned long long) tsf_beacon_target,
+ (long long) tsf_delta,
+ tsf_delta_bmiss,
+ tsf_remainder);
+
+ DPRINTF(sc, ATH_DEBUG_BEACON, "%s: tsf=%llu, nexttbtt=%llu, delta=%d\n",
+ __func__,
+ (unsigned long long) tsf_beacon,
+ (unsigned long long) nexttbtt,
+ (int32_t) tsf_beacon - (int32_t) nexttbtt + tsf_intval);
+
if (sc->sc_syncbeacon &&
- ni == vap->iv_bss && vap->iv_state == IEEE80211_S_RUN) {
+ ni == vap->iv_bss &&
+ (vap->iv_state == IEEE80211_S_RUN || vap->iv_state == IEEE80211_S_SLEEP)) {
+ DPRINTF(sc, ATH_DEBUG_BEACON,
+ "%s: syncbeacon=1; syncing\n",
+ __func__);
/*
* Resync beacon timers using the tsf of the beacon
* frame we just received.
*/
ath_beacon_config(sc, vap);
+ sc->sc_syncbeacon = 0;
}
+
+
/* fall thru... */
case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
if (vap->iv_opmode == IEEE80211_M_IBSS &&
@@ -607,7 +704,7 @@ ath_rx_pkt(struct ath_softc *sc, struct ath_rx_status *rs, HAL_STATUS status,
rs->rs_keyix-32 : rs->rs_keyix);
}
}
- ifp->if_ierrors++;
+ if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
rx_error:
/*
* Cleanup any pending partial frame.
@@ -733,7 +830,7 @@ rx_accept:
rs->rs_antenna |= 0x4;
}
- ifp->if_ipackets++;
+ if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1);
sc->sc_stats.ast_ant_rx[rs->rs_antenna]++;
/*
@@ -879,6 +976,14 @@ rx_next:
#define ATH_RX_MAX 128
+/*
+ * XXX TODO: break out the "get buffers" from "call ath_rx_pkt()" like
+ * the EDMA code does.
+ *
+ * XXX TODO: then, do all of the RX list management stuff inside
+ * ATH_RX_LOCK() so we don't end up potentially racing. The EDMA
+ * code is doing it right.
+ */
static void
ath_rx_proc(struct ath_softc *sc, int resched)
{
@@ -900,6 +1005,7 @@ ath_rx_proc(struct ath_softc *sc, int resched)
u_int64_t tsf;
int npkts = 0;
int kickpcu = 0;
+ int ret;
/* XXX we must not hold the ATH_LOCK here */
ATH_UNLOCK_ASSERT(sc);
@@ -910,6 +1016,10 @@ ath_rx_proc(struct ath_softc *sc, int resched)
kickpcu = sc->sc_kickpcu;
ATH_PCU_UNLOCK(sc);
+ ATH_LOCK(sc);
+ ath_power_set_power_state(sc, HAL_PM_AWAKE);
+ ATH_UNLOCK(sc);
+
DPRINTF(sc, ATH_DEBUG_RX_PROC, "%s: called\n", __func__);
ngood = 0;
nf = ath_hal_getchannoise(ah, sc->sc_curchan);
@@ -995,8 +1105,26 @@ ath_rx_proc(struct ath_softc *sc, int resched)
if (ath_rx_pkt(sc, rs, status, tsf, nf, HAL_RX_QUEUE_HP, bf, m))
ngood++;
rx_proc_next:
- TAILQ_INSERT_TAIL(&sc->sc_rxbuf, bf, bf_list);
- } while (ath_rxbuf_init(sc, bf) == 0);
+ /*
+ * If there's a holding buffer, insert that onto
+ * the RX list; the hardware is now definitely not pointing
+ * to it now.
+ */
+ ret = 0;
+ if (sc->sc_rxedma[HAL_RX_QUEUE_HP].m_holdbf != NULL) {
+ TAILQ_INSERT_TAIL(&sc->sc_rxbuf,
+ sc->sc_rxedma[HAL_RX_QUEUE_HP].m_holdbf,
+ bf_list);
+ ret = ath_rxbuf_init(sc,
+ sc->sc_rxedma[HAL_RX_QUEUE_HP].m_holdbf);
+ }
+ /*
+ * Next, throw our buffer into the holding entry. The hardware
+ * may use the descriptor to read the link pointer before
+ * DMAing the next descriptor in to write out a packet.
+ */
+ sc->sc_rxedma[HAL_RX_QUEUE_HP].m_holdbf = bf;
+ } while (ret == 0);
/* rx signal state monitoring */
ath_hal_rxmonitor(ah, &sc->sc_halstats, sc->sc_curchan);
@@ -1028,6 +1156,13 @@ rx_proc_next:
* constantly write over the same frame, leading
* the RX driver code here to get heavily confused.
*/
+ /*
+ * XXX Has RX DMA stopped enough here to just call
+ * ath_startrecv()?
+ * XXX Do we need to use the holding buffer to restart
+ * RX DMA by appending entries to the final
+ * descriptor? Quite likely.
+ */
#if 1
ath_startrecv(sc);
#else
@@ -1065,6 +1200,13 @@ rx_proc_next:
#undef PA2DESC
/*
+ * Put the hardware to sleep again if we're done with it.
+ */
+ ATH_LOCK(sc);
+ ath_power_restore_power_state(sc);
+ ATH_UNLOCK(sc);
+
+ /*
* If we hit the maximum number of frames in this round,
* reschedule for another immediate pass. This gives
* the TX and TX completion routines time to run, which
@@ -1111,6 +1253,58 @@ ath_legacy_flushrecv(struct ath_softc *sc)
ath_rx_proc(sc, 0);
}
+static void
+ath_legacy_flush_rxpending(struct ath_softc *sc)
+{
+
+ /* XXX ATH_RX_LOCK_ASSERT(sc); */
+
+ if (sc->sc_rxedma[HAL_RX_QUEUE_LP].m_rxpending != NULL) {
+ m_freem(sc->sc_rxedma[HAL_RX_QUEUE_LP].m_rxpending);
+ sc->sc_rxedma[HAL_RX_QUEUE_LP].m_rxpending = NULL;
+ }
+ if (sc->sc_rxedma[HAL_RX_QUEUE_HP].m_rxpending != NULL) {
+ m_freem(sc->sc_rxedma[HAL_RX_QUEUE_HP].m_rxpending);
+ sc->sc_rxedma[HAL_RX_QUEUE_HP].m_rxpending = NULL;
+ }
+}
+
+static int
+ath_legacy_flush_rxholdbf(struct ath_softc *sc)
+{
+ struct ath_buf *bf;
+
+ /* XXX ATH_RX_LOCK_ASSERT(sc); */
+ /*
+ * If there are RX holding buffers, free them here and return
+ * them to the list.
+ *
+ * XXX should just verify that bf->bf_m is NULL, as it must
+ * be at this point!
+ */
+ bf = sc->sc_rxedma[HAL_RX_QUEUE_HP].m_holdbf;
+ if (bf != NULL) {
+ if (bf->bf_m != NULL)
+ m_freem(bf->bf_m);
+ bf->bf_m = NULL;
+ TAILQ_INSERT_TAIL(&sc->sc_rxbuf, bf, bf_list);
+ (void) ath_rxbuf_init(sc, bf);
+ }
+ sc->sc_rxedma[HAL_RX_QUEUE_HP].m_holdbf = NULL;
+
+ bf = sc->sc_rxedma[HAL_RX_QUEUE_LP].m_holdbf;
+ if (bf != NULL) {
+ if (bf->bf_m != NULL)
+ m_freem(bf->bf_m);
+ bf->bf_m = NULL;
+ TAILQ_INSERT_TAIL(&sc->sc_rxbuf, bf, bf_list);
+ (void) ath_rxbuf_init(sc, bf);
+ }
+ sc->sc_rxedma[HAL_RX_QUEUE_LP].m_holdbf = NULL;
+
+ return (0);
+}
+
/*
* Disable the receive h/w in preparation for a reset.
*/
@@ -1122,6 +1316,8 @@ ath_legacy_stoprecv(struct ath_softc *sc, int dodelay)
((_pa) - (_sc)->sc_rxdma.dd_desc_paddr)))
struct ath_hal *ah = sc->sc_ah;
+ ATH_RX_LOCK(sc);
+
ath_hal_stoppcurecv(ah); /* disable PCU */
ath_hal_setrxfilter(ah, 0); /* clear recv filter */
ath_hal_stopdmarecv(ah); /* disable DMA engine */
@@ -1155,22 +1351,23 @@ ath_legacy_stoprecv(struct ath_softc *sc, int dodelay)
}
}
#endif
- /*
- * Free both high/low RX pending, just in case.
- */
- if (sc->sc_rxedma[HAL_RX_QUEUE_LP].m_rxpending != NULL) {
- m_freem(sc->sc_rxedma[HAL_RX_QUEUE_LP].m_rxpending);
- sc->sc_rxedma[HAL_RX_QUEUE_LP].m_rxpending = NULL;
- }
- if (sc->sc_rxedma[HAL_RX_QUEUE_HP].m_rxpending != NULL) {
- m_freem(sc->sc_rxedma[HAL_RX_QUEUE_HP].m_rxpending);
- sc->sc_rxedma[HAL_RX_QUEUE_HP].m_rxpending = NULL;
- }
+
+ (void) ath_legacy_flush_rxpending(sc);
+ (void) ath_legacy_flush_rxholdbf(sc);
+
sc->sc_rxlink = NULL; /* just in case */
+
+ ATH_RX_UNLOCK(sc);
#undef PA2DESC
}
/*
+ * XXX TODO: something was calling startrecv without calling
+ * stoprecv. Let's figure out what/why. It was showing up
+ * as a mbuf leak (rxpending) and ath_buf leak (holdbf.)
+ */
+
+/*
* Enable the receive h/w following a reset.
*/
static int
@@ -1179,9 +1376,18 @@ ath_legacy_startrecv(struct ath_softc *sc)
struct ath_hal *ah = sc->sc_ah;
struct ath_buf *bf;
+ ATH_RX_LOCK(sc);
+
+ /*
+ * XXX should verify these are already all NULL!
+ */
sc->sc_rxlink = NULL;
- sc->sc_rxedma[HAL_RX_QUEUE_LP].m_rxpending = NULL;
- sc->sc_rxedma[HAL_RX_QUEUE_HP].m_rxpending = NULL;
+ (void) ath_legacy_flush_rxpending(sc);
+ (void) ath_legacy_flush_rxholdbf(sc);
+
+ /*
+ * Re-chain all of the buffers in the RX buffer list.
+ */
TAILQ_FOREACH(bf, &sc->sc_rxbuf, bf_list) {
int error = ath_rxbuf_init(sc, bf);
if (error != 0) {
@@ -1197,6 +1403,8 @@ ath_legacy_startrecv(struct ath_softc *sc)
ath_hal_rxena(ah); /* enable recv descriptors */
ath_mode_init(sc); /* set filters, etc. */
ath_hal_startpcurecv(ah); /* re-enable PCU/DMA engine */
+
+ ATH_RX_UNLOCK(sc);
return 0;
}
OpenPOWER on IntegriCloud