Index: sys/net80211/ieee80211_input.h =================================================================== --- sys/net80211/ieee80211_input.h +++ sys/net80211/ieee80211_input.h @@ -131,7 +131,39 @@ return frm[1] > 3 && le32dec(frm+2) == ((BCM_OUI_HTINFO<<24)|BCM_OUI); } +static __inline int +ieee80211_check_rxseq_amsdu(const struct ieee80211_rx_stats *rxs) +{ + + return (!! (rxs->c_pktflags & IEEE80211_RX_F_AMSDU)); +} + /* + * Return 1 if the rxseq check should increment the sequence + * number. Return 0 if it's part of an AMSDU batch and it isn't + * the final frame in the decap'ed burst. + */ +static __inline int +ieee80211_check_rxseq_amsdu_more(const struct ieee80211_rx_stats *rxs) +{ + /* No state? ok */ + if (rxs == NULL) + return (1); + + /* State but no AMSDU set? ok */ + if ((rxs->c_pktflags & IEEE80211_RX_F_AMSDU) == 0) + return (1); + + /* State, AMSDU set, then _MORE means "don't inc yet" */ + if (rxs->c_pktflags & IEEE80211_RX_F_AMSDU_MORE) { + return (0); + } + + /* Both are set, so return ok */ + return (1); +} + +/* * Check the current frame sequence number against the current TID * state and return whether it's in sequence or should be dropped. * @@ -238,7 +270,20 @@ goto fail; ok: - ni->ni_rxseqs[tid] = rxseq; + /* + * Only bump the sequence number if it's the last frame + * in a batch. That way frames in the rest of the batch + * get included, and the last frame in the batch kicks + * it next. + */ + if (ieee80211_check_rxseq_amsdu_more(rxs)) { + ni->ni_rxseqs[tid] = rxseq; + if (ieee80211_check_rxseq_amsdu(rxs)) + IEEE80211_NODE_STAT(ni, rx_amsdu_more_end); + } else { + /* .. still waiting */ + IEEE80211_NODE_STAT(ni, rx_amsdu_more); + } return 1; Index: sys/net80211/ieee80211_ioctl.h =================================================================== --- sys/net80211/ieee80211_ioctl.h +++ sys/net80211/ieee80211_ioctl.h @@ -84,7 +84,11 @@ uint32_t ns_tx_deauth_code; /* last deauth reason */ uint32_t ns_tx_disassoc; /* disassociations */ uint32_t ns_tx_disassoc_code; /* last disassociation reason */ - uint32_t ns_spare[8]; + + /* Hardware A-MSDU decode */ + uint32_t ns_rx_amsdu_more; /* RX decap A-MSDU, more coming from A-MSDU */ + uint32_t ns_rx_amsdu_more_end; /* RX decap A-MSDU (or any other frame), no more coming */ + uint32_t ns_spare[6]; }; /* Index: tools/tools/net80211/wlanstats/wlanstats.c =================================================================== --- tools/tools/net80211/wlanstats/wlanstats.c +++ tools/tools/net80211/wlanstats/wlanstats.c @@ -380,6 +380,10 @@ { 9, "ampdu_bartxfail", "bartx_fail", "BAR frames failed to send" }, #define S_AMPDU_BARTX_RETRY AFTER(S_AMPDU_BARTX_FAIL) { 10, "ampdu_bartxretry", "bartx_retry", "BAR frames retried" }, +#define S_RX_AMSDU_MORE AFTER(S_AMPDU_BARTX_RETRY) + { 13, "rx_amsdu_more", "rx_amsdu_more", "decap AMSDU RX, more sub-frames coming" }, +#define S_RX_AMSDU_MORE_END AFTER(S_RX_AMSDU_MORE) + { 12, "rx_ampdu_end", "rx_amsdu_end", "decap AMSDU RX, no more sub-frames coming" }, }; struct wlanstatfoo_p { @@ -826,6 +830,10 @@ case S_AMPDU_BARTX: STAT(ampdu_bar_tx); case S_AMPDU_BARTX_RETRY: STAT(ampdu_bar_tx_retry); case S_AMPDU_BARTX_FAIL: STAT(ampdu_bar_tx_fail); + + case S_RX_AMSDU_MORE: NSTAT(rx_amsdu_more); + case S_RX_AMSDU_MORE_END: NSTAT(rx_amsdu_more_end); + } return wlan_getinfo(wf, s, b, bs); #undef NSTAT @@ -989,6 +997,10 @@ case S_AMPDU_BARTX: STAT(ampdu_bar_tx); case S_AMPDU_BARTX_RETRY: STAT(ampdu_bar_tx_retry); case S_AMPDU_BARTX_FAIL: STAT(ampdu_bar_tx_fail); + + case S_RX_AMSDU_MORE: NSTAT(rx_amsdu_more); + case S_RX_AMSDU_MORE_END: NSTAT(rx_amsdu_more_end); + } return wlan_getinfo(wf, s, b, bs); #undef NSTAT