summaryrefslogtreecommitdiffstats
path: root/sys/dev/ath/if_ath.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/ath/if_ath.c')
-rw-r--r--sys/dev/ath/if_ath.c21
1 files changed, 19 insertions, 2 deletions
diff --git a/sys/dev/ath/if_ath.c b/sys/dev/ath/if_ath.c
index 26cdebcb..ce7317e 100644
--- a/sys/dev/ath/if_ath.c
+++ b/sys/dev/ath/if_ath.c
@@ -595,6 +595,7 @@ ath_attach(u_int16_t devid, struct ath_softc *sc)
sc->sc_hasbmask = ath_hal_hasbssidmask(ah);
sc->sc_hasbmatch = ath_hal_hasbssidmatch(ah);
sc->sc_hastsfadd = ath_hal_hastsfadjust(ah);
+ sc->sc_rxslink = ath_hal_self_linked_final_rxdesc(ah);
if (ath_hal_hasfastframes(ah))
ic->ic_caps |= IEEE80211_C_FF;
wmodes = ath_hal_getwirelessmodes(ah);
@@ -3135,8 +3136,17 @@ ath_rxbuf_init(struct ath_softc *sc, struct ath_buf *bf)
* 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;
- ds->ds_link = bf->bf_daddr; /* link to self */
+ 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 */
@@ -3321,9 +3331,16 @@ ath_rx_proc(void *arg, int npending)
tsf = ath_hal_gettsf64(ah);
do {
bf = STAILQ_FIRST(&sc->sc_rxbuf);
- if (bf == NULL) { /* NB: shouldn't happen */
+ 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 */
OpenPOWER on IntegriCloud