diff --git a/drivers/staging/brcm80211/brcmsmac/wlc_ampdu.c b/drivers/staging/brcm80211/brcmsmac/wlc_ampdu.c index 3d00180efacc..0a0ff3057f11 100644 --- a/drivers/staging/brcm80211/brcmsmac/wlc_ampdu.c +++ b/drivers/staging/brcm80211/brcmsmac/wlc_ampdu.c @@ -1387,6 +1387,19 @@ static bool cb_del_ampdu_pkt(void *p, int arg_a) return rc; } +/* + * callback function that helps invalidating ampdu packets in a DMA queue + */ +static void dma_cb_fn_ampdu(void *txi, void *arg_a) +{ + struct ieee80211_sta *sta = arg_a; + struct ieee80211_tx_info *tx_info = (struct ieee80211_tx_info *)txi; + + if ((tx_info->flags & IEEE80211_TX_CTL_AMPDU) && + (tx_info->control.sta == sta || sta == NULL)) + tx_info->control.sta = NULL; +} + /* * When a remote party is no longer available for ampdu communication, any * pending tx ampdu packets in the driver have to be flushed. @@ -1405,4 +1418,5 @@ void wlc_ampdu_flush(struct wlc_info *wlc, pktq_pflush(pq, prec, true, cb_del_ampdu_pkt, (int)&du_pars); } + wlc_inval_dma_pkts(wlc->hw, sta, dma_cb_fn_ampdu); } diff --git a/drivers/staging/brcm80211/brcmsmac/wlc_main.c b/drivers/staging/brcm80211/brcmsmac/wlc_main.c index 8bee149f6316..785357047d2c 100644 --- a/drivers/staging/brcm80211/brcmsmac/wlc_main.c +++ b/drivers/staging/brcm80211/brcmsmac/wlc_main.c @@ -8477,3 +8477,21 @@ void wlc_associate_upd(struct wlc_info *wlc, bool state) wlc->pub->associated = state; wlc->cfg->associated = state; } + +/* + * When a remote STA/AP is removed by Mac80211, or when it can no longer accept + * AMPDU traffic, packets pending in hardware have to be invalidated so that + * when later on hardware releases them, they can be handled appropriately. + */ +void wlc_inval_dma_pkts(struct wlc_hw_info *hw, + struct ieee80211_sta *sta, + void (*dma_callback_fn)) +{ + struct hnddma_pub *dmah; + int i; + for (i = 0; i < NFIFO; i++) { + dmah = hw->di[i]; + if (dmah != NULL) + dma_walk_packets(dmah, dma_callback_fn, sta); + } +} diff --git a/drivers/staging/brcm80211/brcmsmac/wlc_main.h b/drivers/staging/brcm80211/brcmsmac/wlc_main.h index 7c5d7e1df07c..0eaef07d3324 100644 --- a/drivers/staging/brcm80211/brcmsmac/wlc_main.h +++ b/drivers/staging/brcm80211/brcmsmac/wlc_main.h @@ -873,6 +873,9 @@ extern u16 wlc_compute_rtscts_dur(struct wlc_info *wlc, bool cts_only, bool ba); extern void wlc_tbtt(struct wlc_info *wlc, d11regs_t *regs); +extern void wlc_inval_dma_pkts(struct wlc_hw_info *hw, + struct ieee80211_sta *sta, + void (*dma_callback_fn)); #if defined(BCMDBG) extern void wlc_dump_ie(struct wlc_info *wlc, bcm_tlv_t *ie, diff --git a/drivers/staging/brcm80211/include/hnddma.h b/drivers/staging/brcm80211/include/hnddma.h index 121768eccfea..5d079e77490e 100644 --- a/drivers/staging/brcm80211/include/hnddma.h +++ b/drivers/staging/brcm80211/include/hnddma.h @@ -202,5 +202,6 @@ extern const di_fcn_t dma64proc; * This info is needed by DMA_ALLOC_CONSISTENT in dma attach */ extern uint dma_addrwidth(si_t *sih, void *dmaregs); - +void dma_walk_packets(struct hnddma_pub *dmah, void (*callback_fnc) + (void *pkt, void *arg_a), void *arg_a); #endif /* _hnddma_h_ */ diff --git a/drivers/staging/brcm80211/util/hnddma.c b/drivers/staging/brcm80211/util/hnddma.c index 60afd06297e8..122f7d3fd704 100644 --- a/drivers/staging/brcm80211/util/hnddma.c +++ b/drivers/staging/brcm80211/util/hnddma.c @@ -1800,3 +1800,27 @@ uint dma_addrwidth(si_t *sih, void *dmaregs) return DMADDRWIDTH_64; } +/* + * Mac80211 initiated actions sometimes require packets in the DMA queue to be + * modified. The modified portion of the packet is not under control of the DMA + * engine. This function calls a caller-supplied function for each packet in + * the caller specified dma chain. + */ +void dma_walk_packets(struct hnddma_pub *dmah, void (*callback_fnc) + (void *pkt, void *arg_a), void *arg_a) +{ + dma_info_t *di = (dma_info_t *) dmah; + uint i = di->txin; + uint end = di->txout; + struct sk_buff *skb; + struct ieee80211_tx_info *tx_info; + + while (i != end) { + skb = (struct sk_buff *)di->txp[i]; + if (skb != NULL) { + tx_info = (struct ieee80211_tx_info *)skb->cb; + (callback_fnc)(tx_info, arg_a); + } + i = NEXTTXD(i); + } +}