xref: /linux/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c (revision ab93e0dd72c37d378dd936f031ffb83ff2bd87ce)
1828c91f7SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
25e6e3a92SBing Zhao /*
3932183aaSGanapathi Bhat  * NXP Wireless LAN device driver: 802.11n RX Re-ordering
45e6e3a92SBing Zhao  *
5932183aaSGanapathi Bhat  * Copyright 2011-2020 NXP
65e6e3a92SBing Zhao  */
75e6e3a92SBing Zhao 
85e6e3a92SBing Zhao #include "decl.h"
95e6e3a92SBing Zhao #include "ioctl.h"
105e6e3a92SBing Zhao #include "util.h"
115e6e3a92SBing Zhao #include "fw.h"
125e6e3a92SBing Zhao #include "main.h"
135e6e3a92SBing Zhao #include "wmm.h"
145e6e3a92SBing Zhao #include "11n.h"
155e6e3a92SBing Zhao #include "11n_rxreorder.h"
165e6e3a92SBing Zhao 
174c9f9fb2SAmitkumar Karwar /* This function will dispatch amsdu packet and forward it to kernel/upper
184c9f9fb2SAmitkumar Karwar  * layer.
194c9f9fb2SAmitkumar Karwar  */
mwifiex_11n_dispatch_amsdu_pkt(struct mwifiex_private * priv,struct sk_buff * skb)204c9f9fb2SAmitkumar Karwar static int mwifiex_11n_dispatch_amsdu_pkt(struct mwifiex_private *priv,
214c9f9fb2SAmitkumar Karwar 					  struct sk_buff *skb)
224c9f9fb2SAmitkumar Karwar {
234c9f9fb2SAmitkumar Karwar 	struct rxpd *local_rx_pd = (struct rxpd *)(skb->data);
244c9f9fb2SAmitkumar Karwar 	int ret;
254c9f9fb2SAmitkumar Karwar 
264c9f9fb2SAmitkumar Karwar 	if (le16_to_cpu(local_rx_pd->rx_pkt_type) == PKT_TYPE_AMSDU) {
274c9f9fb2SAmitkumar Karwar 		struct sk_buff_head list;
284c9f9fb2SAmitkumar Karwar 		struct sk_buff *rx_skb;
294c9f9fb2SAmitkumar Karwar 
304c9f9fb2SAmitkumar Karwar 		__skb_queue_head_init(&list);
314c9f9fb2SAmitkumar Karwar 
324c9f9fb2SAmitkumar Karwar 		skb_pull(skb, le16_to_cpu(local_rx_pd->rx_pkt_offset));
334c9f9fb2SAmitkumar Karwar 		skb_trim(skb, le16_to_cpu(local_rx_pd->rx_pkt_length));
344c9f9fb2SAmitkumar Karwar 
354c9f9fb2SAmitkumar Karwar 		ieee80211_amsdu_to_8023s(skb, &list, priv->curr_addr,
36986e43b1SFelix Fietkau 					 priv->wdev.iftype, 0, NULL, NULL, false);
374c9f9fb2SAmitkumar Karwar 
384c9f9fb2SAmitkumar Karwar 		while (!skb_queue_empty(&list)) {
39776f7420SAmitkumar Karwar 			struct rx_packet_hdr *rx_hdr;
40776f7420SAmitkumar Karwar 
414c9f9fb2SAmitkumar Karwar 			rx_skb = __skb_dequeue(&list);
42776f7420SAmitkumar Karwar 			rx_hdr = (struct rx_packet_hdr *)rx_skb->data;
43776f7420SAmitkumar Karwar 			if (ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) &&
44776f7420SAmitkumar Karwar 			    ntohs(rx_hdr->eth803_hdr.h_proto) == ETH_P_TDLS) {
45776f7420SAmitkumar Karwar 				mwifiex_process_tdls_action_frame(priv,
46776f7420SAmitkumar Karwar 								  (u8 *)rx_hdr,
47776f7420SAmitkumar Karwar 								  skb->len);
48776f7420SAmitkumar Karwar 			}
49776f7420SAmitkumar Karwar 
50bf00dc22SXinming Hu 			if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP)
51bf00dc22SXinming Hu 				ret = mwifiex_uap_recv_packet(priv, rx_skb);
52bf00dc22SXinming Hu 			else
534c9f9fb2SAmitkumar Karwar 				ret = mwifiex_recv_packet(priv, rx_skb);
544c9f9fb2SAmitkumar Karwar 			if (ret == -1)
55acebe8c1SZhaoyang Liu 				mwifiex_dbg(priv->adapter, ERROR,
564c9f9fb2SAmitkumar Karwar 					    "Rx of A-MSDU failed");
574c9f9fb2SAmitkumar Karwar 		}
584c9f9fb2SAmitkumar Karwar 		return 0;
594c9f9fb2SAmitkumar Karwar 	}
604c9f9fb2SAmitkumar Karwar 
614c9f9fb2SAmitkumar Karwar 	return -1;
624c9f9fb2SAmitkumar Karwar }
634c9f9fb2SAmitkumar Karwar 
645e6e43ebSAmitkumar Karwar /* This function will process the rx packet and forward it to kernel/upper
655e6e43ebSAmitkumar Karwar  * layer.
665e6e43ebSAmitkumar Karwar  */
mwifiex_11n_dispatch_pkt(struct mwifiex_private * priv,struct sk_buff * payload)67ce2e942eSBrian Norris static int mwifiex_11n_dispatch_pkt(struct mwifiex_private *priv,
68ce2e942eSBrian Norris 				    struct sk_buff *payload)
695e6e43ebSAmitkumar Karwar {
704c9f9fb2SAmitkumar Karwar 
7199ffe72cSXinming Hu 	int ret;
7299ffe72cSXinming Hu 
7399ffe72cSXinming Hu 	if (!payload) {
7499ffe72cSXinming Hu 		mwifiex_dbg(priv->adapter, INFO, "info: fw drop data\n");
7599ffe72cSXinming Hu 		return 0;
7699ffe72cSXinming Hu 	}
7799ffe72cSXinming Hu 
7899ffe72cSXinming Hu 	ret = mwifiex_11n_dispatch_amsdu_pkt(priv, payload);
794c9f9fb2SAmitkumar Karwar 	if (!ret)
804c9f9fb2SAmitkumar Karwar 		return 0;
814c9f9fb2SAmitkumar Karwar 
825e6e43ebSAmitkumar Karwar 	if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP)
835e6e43ebSAmitkumar Karwar 		return mwifiex_handle_uap_rx_forward(priv, payload);
845e6e43ebSAmitkumar Karwar 
855e6e43ebSAmitkumar Karwar 	return mwifiex_process_rx_packet(priv, payload);
865e6e43ebSAmitkumar Karwar }
875e6e43ebSAmitkumar Karwar 
885e6e3a92SBing Zhao /*
898c00228eSYogesh Ashok Powar  * This function dispatches all packets in the Rx reorder table until the
908c00228eSYogesh Ashok Powar  * start window.
915e6e3a92SBing Zhao  *
925e6e3a92SBing Zhao  * There could be holes in the buffer, which are skipped by the function.
935e6e3a92SBing Zhao  * Since the buffer is linear, the function uses rotation to simulate
945e6e3a92SBing Zhao  * circular buffer.
955e6e3a92SBing Zhao  */
96ab581472SYogesh Ashok Powar static void
mwifiex_11n_dispatch_pkt_until_start_win(struct mwifiex_private * priv,struct mwifiex_rx_reorder_tbl * tbl,int start_win)975e6e43ebSAmitkumar Karwar mwifiex_11n_dispatch_pkt_until_start_win(struct mwifiex_private *priv,
985e6e43ebSAmitkumar Karwar 					 struct mwifiex_rx_reorder_tbl *tbl,
995e6e43ebSAmitkumar Karwar 					 int start_win)
1005e6e3a92SBing Zhao {
101ce2e942eSBrian Norris 	struct sk_buff_head list;
102ce2e942eSBrian Norris 	struct sk_buff *skb;
1038c00228eSYogesh Ashok Powar 	int pkt_to_send, i;
1045e6e3a92SBing Zhao 
105ce2e942eSBrian Norris 	__skb_queue_head_init(&list);
1068a7f9fd8SBrian Norris 	spin_lock_bh(&priv->rx_reorder_tbl_lock);
107ce2e942eSBrian Norris 
1088c00228eSYogesh Ashok Powar 	pkt_to_send = (start_win > tbl->start_win) ?
1098c00228eSYogesh Ashok Powar 		      min((start_win - tbl->start_win), tbl->win_size) :
1108c00228eSYogesh Ashok Powar 		      tbl->win_size;
1115e6e3a92SBing Zhao 
1128c00228eSYogesh Ashok Powar 	for (i = 0; i < pkt_to_send; ++i) {
1138c00228eSYogesh Ashok Powar 		if (tbl->rx_reorder_ptr[i]) {
114ce2e942eSBrian Norris 			skb = tbl->rx_reorder_ptr[i];
115ce2e942eSBrian Norris 			__skb_queue_tail(&list, skb);
1168c00228eSYogesh Ashok Powar 			tbl->rx_reorder_ptr[i] = NULL;
1175e6e3a92SBing Zhao 		}
1185e6e3a92SBing Zhao 	}
1195e6e3a92SBing Zhao 
1205e6e3a92SBing Zhao 	/*
1215e6e3a92SBing Zhao 	 * We don't have a circular buffer, hence use rotation to simulate
1225e6e3a92SBing Zhao 	 * circular buffer
1235e6e3a92SBing Zhao 	 */
1248c00228eSYogesh Ashok Powar 	for (i = 0; i < tbl->win_size - pkt_to_send; ++i) {
1258c00228eSYogesh Ashok Powar 		tbl->rx_reorder_ptr[i] = tbl->rx_reorder_ptr[pkt_to_send + i];
1268c00228eSYogesh Ashok Powar 		tbl->rx_reorder_ptr[pkt_to_send + i] = NULL;
1275e6e3a92SBing Zhao 	}
1285e6e3a92SBing Zhao 
1298c00228eSYogesh Ashok Powar 	tbl->start_win = start_win;
1308a7f9fd8SBrian Norris 	spin_unlock_bh(&priv->rx_reorder_tbl_lock);
131ce2e942eSBrian Norris 
132ce2e942eSBrian Norris 	while ((skb = __skb_dequeue(&list)))
133ce2e942eSBrian Norris 		mwifiex_11n_dispatch_pkt(priv, skb);
1345e6e3a92SBing Zhao }
1355e6e3a92SBing Zhao 
1365e6e3a92SBing Zhao /*
1375e6e3a92SBing Zhao  * This function dispatches all packets in the Rx reorder table until
1385e6e3a92SBing Zhao  * a hole is found.
1395e6e3a92SBing Zhao  *
1405e6e3a92SBing Zhao  * The start window is adjusted automatically when a hole is located.
1415e6e3a92SBing Zhao  * Since the buffer is linear, the function uses rotation to simulate
1425e6e3a92SBing Zhao  * circular buffer.
1435e6e3a92SBing Zhao  */
144ab581472SYogesh Ashok Powar static void
mwifiex_11n_scan_and_dispatch(struct mwifiex_private * priv,struct mwifiex_rx_reorder_tbl * tbl)1455e6e3a92SBing Zhao mwifiex_11n_scan_and_dispatch(struct mwifiex_private *priv,
1468c00228eSYogesh Ashok Powar 			      struct mwifiex_rx_reorder_tbl *tbl)
1475e6e3a92SBing Zhao {
148ce2e942eSBrian Norris 	struct sk_buff_head list;
149ce2e942eSBrian Norris 	struct sk_buff *skb;
1505e6e3a92SBing Zhao 	int i, j, xchg;
1515e6e3a92SBing Zhao 
152ce2e942eSBrian Norris 	__skb_queue_head_init(&list);
1538a7f9fd8SBrian Norris 	spin_lock_bh(&priv->rx_reorder_tbl_lock);
154ce2e942eSBrian Norris 
155ce2e942eSBrian Norris 	for (i = 0; i < tbl->win_size; ++i) {
156ce2e942eSBrian Norris 		if (!tbl->rx_reorder_ptr[i])
1575e6e3a92SBing Zhao 			break;
158ce2e942eSBrian Norris 		skb = tbl->rx_reorder_ptr[i];
159ce2e942eSBrian Norris 		__skb_queue_tail(&list, skb);
1608c00228eSYogesh Ashok Powar 		tbl->rx_reorder_ptr[i] = NULL;
1615e6e3a92SBing Zhao 	}
1625e6e3a92SBing Zhao 
1635e6e3a92SBing Zhao 	/*
1645e6e3a92SBing Zhao 	 * We don't have a circular buffer, hence use rotation to simulate
1655e6e3a92SBing Zhao 	 * circular buffer
1665e6e3a92SBing Zhao 	 */
1675e6e3a92SBing Zhao 	if (i > 0) {
1688c00228eSYogesh Ashok Powar 		xchg = tbl->win_size - i;
1695e6e3a92SBing Zhao 		for (j = 0; j < xchg; ++j) {
1708c00228eSYogesh Ashok Powar 			tbl->rx_reorder_ptr[j] = tbl->rx_reorder_ptr[i + j];
1718c00228eSYogesh Ashok Powar 			tbl->rx_reorder_ptr[i + j] = NULL;
1725e6e3a92SBing Zhao 		}
1735e6e3a92SBing Zhao 	}
1748c00228eSYogesh Ashok Powar 	tbl->start_win = (tbl->start_win + i) & (MAX_TID_VALUE - 1);
175ce2e942eSBrian Norris 
1768a7f9fd8SBrian Norris 	spin_unlock_bh(&priv->rx_reorder_tbl_lock);
177ce2e942eSBrian Norris 
178ce2e942eSBrian Norris 	while ((skb = __skb_dequeue(&list)))
179ce2e942eSBrian Norris 		mwifiex_11n_dispatch_pkt(priv, skb);
1805e6e3a92SBing Zhao }
1815e6e3a92SBing Zhao 
1825e6e3a92SBing Zhao /*
1835e6e3a92SBing Zhao  * This function deletes the Rx reorder table and frees the memory.
1845e6e3a92SBing Zhao  *
1855e6e3a92SBing Zhao  * The function stops the associated timer and dispatches all the
1865e6e3a92SBing Zhao  * pending packets in the Rx reorder table before deletion.
1875e6e3a92SBing Zhao  */
1885e6e3a92SBing Zhao static void
mwifiex_del_rx_reorder_entry(struct mwifiex_private * priv,struct mwifiex_rx_reorder_tbl * tbl)1898c00228eSYogesh Ashok Powar mwifiex_del_rx_reorder_entry(struct mwifiex_private *priv,
1908c00228eSYogesh Ashok Powar 			     struct mwifiex_rx_reorder_tbl *tbl)
1915e6e3a92SBing Zhao {
19263410c37SAmitkumar Karwar 	int start_win;
1935e6e3a92SBing Zhao 
1948c00228eSYogesh Ashok Powar 	if (!tbl)
1955e6e3a92SBing Zhao 		return;
1965e6e3a92SBing Zhao 
1978a7f9fd8SBrian Norris 	spin_lock_bh(&priv->adapter->rx_proc_lock);
1986e251174SAvinash Patil 	priv->adapter->rx_locked = true;
1996e251174SAvinash Patil 	if (priv->adapter->rx_processing) {
2008a7f9fd8SBrian Norris 		spin_unlock_bh(&priv->adapter->rx_proc_lock);
2016e251174SAvinash Patil 		flush_workqueue(priv->adapter->rx_workqueue);
2026e251174SAvinash Patil 	} else {
2038a7f9fd8SBrian Norris 		spin_unlock_bh(&priv->adapter->rx_proc_lock);
2046e251174SAvinash Patil 	}
2056e251174SAvinash Patil 
20663410c37SAmitkumar Karwar 	start_win = (tbl->start_win + tbl->win_size) & (MAX_TID_VALUE - 1);
2075e6e43ebSAmitkumar Karwar 	mwifiex_11n_dispatch_pkt_until_start_win(priv, tbl, start_win);
2085e6e3a92SBing Zhao 
2098fa7292fSThomas Gleixner 	timer_delete_sync(&tbl->timer_context.timer);
2103a8fede1SMarc Yang 	tbl->timer_context.timer_is_set = false;
2111aa48f08SBrian Norris 
2128a7f9fd8SBrian Norris 	spin_lock_bh(&priv->rx_reorder_tbl_lock);
2138c00228eSYogesh Ashok Powar 	list_del(&tbl->list);
2148a7f9fd8SBrian Norris 	spin_unlock_bh(&priv->rx_reorder_tbl_lock);
2151aa48f08SBrian Norris 
2168c00228eSYogesh Ashok Powar 	kfree(tbl->rx_reorder_ptr);
2178c00228eSYogesh Ashok Powar 	kfree(tbl);
2186e251174SAvinash Patil 
2198a7f9fd8SBrian Norris 	spin_lock_bh(&priv->adapter->rx_proc_lock);
2206e251174SAvinash Patil 	priv->adapter->rx_locked = false;
2218a7f9fd8SBrian Norris 	spin_unlock_bh(&priv->adapter->rx_proc_lock);
2226e251174SAvinash Patil 
2235e6e3a92SBing Zhao }
2245e6e3a92SBing Zhao 
2255e6e3a92SBing Zhao /*
2265e6e3a92SBing Zhao  * This function returns the pointer to an entry in Rx reordering
2275e6e3a92SBing Zhao  * table which matches the given TA/TID pair.
2285e6e3a92SBing Zhao  */
229d1cf3b95SAvinash Patil struct mwifiex_rx_reorder_tbl *
mwifiex_11n_get_rx_reorder_tbl(struct mwifiex_private * priv,int tid,u8 * ta)2305e6e3a92SBing Zhao mwifiex_11n_get_rx_reorder_tbl(struct mwifiex_private *priv, int tid, u8 *ta)
2315e6e3a92SBing Zhao {
2328c00228eSYogesh Ashok Powar 	struct mwifiex_rx_reorder_tbl *tbl;
2335e6e3a92SBing Zhao 
2348a7f9fd8SBrian Norris 	spin_lock_bh(&priv->rx_reorder_tbl_lock);
2351aa48f08SBrian Norris 	list_for_each_entry(tbl, &priv->rx_reorder_tbl_ptr, list) {
2361aa48f08SBrian Norris 		if (!memcmp(tbl->ta, ta, ETH_ALEN) && tbl->tid == tid) {
2378a7f9fd8SBrian Norris 			spin_unlock_bh(&priv->rx_reorder_tbl_lock);
2388c00228eSYogesh Ashok Powar 			return tbl;
2391aa48f08SBrian Norris 		}
2401aa48f08SBrian Norris 	}
2418a7f9fd8SBrian Norris 	spin_unlock_bh(&priv->rx_reorder_tbl_lock);
2425e6e3a92SBing Zhao 
2435e6e3a92SBing Zhao 	return NULL;
2445e6e3a92SBing Zhao }
2455e6e3a92SBing Zhao 
2463e238a11SAvinash Patil /* This function retrieves the pointer to an entry in Rx reordering
2473e238a11SAvinash Patil  * table which matches the given TA and deletes it.
2483e238a11SAvinash Patil  */
mwifiex_11n_del_rx_reorder_tbl_by_ta(struct mwifiex_private * priv,u8 * ta)2493e238a11SAvinash Patil void mwifiex_11n_del_rx_reorder_tbl_by_ta(struct mwifiex_private *priv, u8 *ta)
2503e238a11SAvinash Patil {
2513e238a11SAvinash Patil 	struct mwifiex_rx_reorder_tbl *tbl, *tmp;
2523e238a11SAvinash Patil 
2533e238a11SAvinash Patil 	if (!ta)
2543e238a11SAvinash Patil 		return;
2553e238a11SAvinash Patil 
2568a7f9fd8SBrian Norris 	spin_lock_bh(&priv->rx_reorder_tbl_lock);
2571aa48f08SBrian Norris 	list_for_each_entry_safe(tbl, tmp, &priv->rx_reorder_tbl_ptr, list) {
2581aa48f08SBrian Norris 		if (!memcmp(tbl->ta, ta, ETH_ALEN)) {
2598a7f9fd8SBrian Norris 			spin_unlock_bh(&priv->rx_reorder_tbl_lock);
2603e238a11SAvinash Patil 			mwifiex_del_rx_reorder_entry(priv, tbl);
2618a7f9fd8SBrian Norris 			spin_lock_bh(&priv->rx_reorder_tbl_lock);
2621aa48f08SBrian Norris 		}
2631aa48f08SBrian Norris 	}
2648a7f9fd8SBrian Norris 	spin_unlock_bh(&priv->rx_reorder_tbl_lock);
2653e238a11SAvinash Patil 
2663e238a11SAvinash Patil 	return;
2673e238a11SAvinash Patil }
2683e238a11SAvinash Patil 
2695e6e3a92SBing Zhao /*
2705e6e3a92SBing Zhao  * This function finds the last sequence number used in the packets
2715e6e3a92SBing Zhao  * buffered in Rx reordering table.
2725e6e3a92SBing Zhao  */
2735e6e3a92SBing Zhao static int
mwifiex_11n_find_last_seq_num(struct reorder_tmr_cnxt * ctx)2742d702830SAmitkumar Karwar mwifiex_11n_find_last_seq_num(struct reorder_tmr_cnxt *ctx)
2755e6e3a92SBing Zhao {
2762d702830SAmitkumar Karwar 	struct mwifiex_rx_reorder_tbl *rx_reorder_tbl_ptr = ctx->ptr;
2771aa48f08SBrian Norris 	struct mwifiex_private *priv = ctx->priv;
2785e6e3a92SBing Zhao 	int i;
2795e6e3a92SBing Zhao 
2808a7f9fd8SBrian Norris 	spin_lock_bh(&priv->rx_reorder_tbl_lock);
2811aa48f08SBrian Norris 	for (i = rx_reorder_tbl_ptr->win_size - 1; i >= 0; --i) {
2821aa48f08SBrian Norris 		if (rx_reorder_tbl_ptr->rx_reorder_ptr[i]) {
2838a7f9fd8SBrian Norris 			spin_unlock_bh(&priv->rx_reorder_tbl_lock);
2845e6e3a92SBing Zhao 			return i;
2851aa48f08SBrian Norris 		}
2861aa48f08SBrian Norris 	}
2878a7f9fd8SBrian Norris 	spin_unlock_bh(&priv->rx_reorder_tbl_lock);
2885e6e3a92SBing Zhao 
2895e6e3a92SBing Zhao 	return -1;
2905e6e3a92SBing Zhao }
2915e6e3a92SBing Zhao 
2925e6e3a92SBing Zhao /*
2935e6e3a92SBing Zhao  * This function flushes all the packets in Rx reordering table.
2945e6e3a92SBing Zhao  *
2955e6e3a92SBing Zhao  * The function checks if any packets are currently buffered in the
2965e6e3a92SBing Zhao  * table or not. In case there are packets available, it dispatches
2975e6e3a92SBing Zhao  * them and then dumps the Rx reordering table.
2985e6e3a92SBing Zhao  */
2995e6e3a92SBing Zhao static void
mwifiex_flush_data(struct timer_list * t)30008c2eb8eSKees Cook mwifiex_flush_data(struct timer_list *t)
3015e6e3a92SBing Zhao {
3028c00228eSYogesh Ashok Powar 	struct reorder_tmr_cnxt *ctx =
303*41cb0855SIngo Molnar 		timer_container_of(ctx, t, timer);
30463410c37SAmitkumar Karwar 	int start_win, seq_num;
3055e6e3a92SBing Zhao 
3063a8fede1SMarc Yang 	ctx->timer_is_set = false;
3072d702830SAmitkumar Karwar 	seq_num = mwifiex_11n_find_last_seq_num(ctx);
308bb7de2baSYogesh Ashok Powar 
3091aa48f08SBrian Norris 	if (seq_num < 0)
310bb7de2baSYogesh Ashok Powar 		return;
311bb7de2baSYogesh Ashok Powar 
312acebe8c1SZhaoyang Liu 	mwifiex_dbg(ctx->priv->adapter, INFO, "info: flush data %d\n", seq_num);
31363410c37SAmitkumar Karwar 	start_win = (ctx->ptr->start_win + seq_num + 1) & (MAX_TID_VALUE - 1);
3145e6e43ebSAmitkumar Karwar 	mwifiex_11n_dispatch_pkt_until_start_win(ctx->priv, ctx->ptr,
3155e6e43ebSAmitkumar Karwar 						 start_win);
3165e6e3a92SBing Zhao }
3175e6e3a92SBing Zhao 
3185e6e3a92SBing Zhao /*
3195e6e3a92SBing Zhao  * This function creates an entry in Rx reordering table for the
3205e6e3a92SBing Zhao  * given TA/TID.
3215e6e3a92SBing Zhao  *
3225e6e3a92SBing Zhao  * The function also initializes the entry with sequence number, window
3235e6e3a92SBing Zhao  * size as well as initializes the timer.
3245e6e3a92SBing Zhao  *
3255e6e3a92SBing Zhao  * If the received TA/TID pair is already present, all the packets are
3265e6e3a92SBing Zhao  * dispatched and the window size is moved until the SSN.
3275e6e3a92SBing Zhao  */
3285e6e3a92SBing Zhao static void
mwifiex_11n_create_rx_reorder_tbl(struct mwifiex_private * priv,u8 * ta,int tid,int win_size,int seq_num)3295e6e3a92SBing Zhao mwifiex_11n_create_rx_reorder_tbl(struct mwifiex_private *priv, u8 *ta,
3305e6e3a92SBing Zhao 				  int tid, int win_size, int seq_num)
3315e6e3a92SBing Zhao {
3325e6e3a92SBing Zhao 	int i;
3338c00228eSYogesh Ashok Powar 	struct mwifiex_rx_reorder_tbl *tbl, *new_node;
3345e6e3a92SBing Zhao 	u16 last_seq = 0;
3355a009adfSAvinash Patil 	struct mwifiex_sta_node *node;
3365e6e3a92SBing Zhao 
3375e6e3a92SBing Zhao 	/*
338ed03a2afSJason Wang 	 * If we get a TID, ta pair which is already present dispatch all
3395e6e3a92SBing Zhao 	 * the packets and move the window size until the ssn
3405e6e3a92SBing Zhao 	 */
3418c00228eSYogesh Ashok Powar 	tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid, ta);
3428c00228eSYogesh Ashok Powar 	if (tbl) {
3435e6e43ebSAmitkumar Karwar 		mwifiex_11n_dispatch_pkt_until_start_win(priv, tbl, seq_num);
3445e6e3a92SBing Zhao 		return;
3455e6e3a92SBing Zhao 	}
3468c00228eSYogesh Ashok Powar 	/* if !tbl then create one */
3475e6e3a92SBing Zhao 	new_node = kzalloc(sizeof(struct mwifiex_rx_reorder_tbl), GFP_KERNEL);
3480d2e7a5cSJoe Perches 	if (!new_node)
3495e6e3a92SBing Zhao 		return;
3505e6e3a92SBing Zhao 
3515e6e3a92SBing Zhao 	INIT_LIST_HEAD(&new_node->list);
3525e6e3a92SBing Zhao 	new_node->tid = tid;
3535e6e3a92SBing Zhao 	memcpy(new_node->ta, ta, ETH_ALEN);
3545e6e3a92SBing Zhao 	new_node->start_win = seq_num;
3558acbea61SPaul Stewart 	new_node->init_win = seq_num;
3568acbea61SPaul Stewart 	new_node->flags = 0;
3575a009adfSAvinash Patil 
3588a7f9fd8SBrian Norris 	spin_lock_bh(&priv->sta_list_spinlock);
3595a009adfSAvinash Patil 	if (mwifiex_queuing_ra_based(priv)) {
3605a009adfSAvinash Patil 		if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP) {
3615a009adfSAvinash Patil 			node = mwifiex_get_sta_entry(priv, ta);
3625a009adfSAvinash Patil 			if (node)
3635a009adfSAvinash Patil 				last_seq = node->rx_seq[tid];
3645a009adfSAvinash Patil 		}
3655a009adfSAvinash Patil 	} else {
366daeb5bb4SAvinash Patil 		node = mwifiex_get_sta_entry(priv, ta);
367daeb5bb4SAvinash Patil 		if (node)
368daeb5bb4SAvinash Patil 			last_seq = node->rx_seq[tid];
369daeb5bb4SAvinash Patil 		else
3705e6e3a92SBing Zhao 			last_seq = priv->rx_seq[tid];
3715a009adfSAvinash Patil 	}
3728a7f9fd8SBrian Norris 	spin_unlock_bh(&priv->sta_list_spinlock);
3735e6e3a92SBing Zhao 
374acebe8c1SZhaoyang Liu 	mwifiex_dbg(priv->adapter, INFO,
375acebe8c1SZhaoyang Liu 		    "info: last_seq=%d start_win=%d\n",
37609bd1976SAvinash Patil 		    last_seq, new_node->start_win);
37709bd1976SAvinash Patil 
37892583924SStone Piao 	if (last_seq != MWIFIEX_DEF_11N_RX_SEQ_NUM &&
3798acbea61SPaul Stewart 	    last_seq >= new_node->start_win) {
3805e6e3a92SBing Zhao 		new_node->start_win = last_seq + 1;
3818acbea61SPaul Stewart 		new_node->flags |= RXREOR_INIT_WINDOW_SHIFT;
3828acbea61SPaul Stewart 	}
3835e6e3a92SBing Zhao 
3845e6e3a92SBing Zhao 	new_node->win_size = win_size;
3855e6e3a92SBing Zhao 
3866396bb22SKees Cook 	new_node->rx_reorder_ptr = kcalloc(win_size, sizeof(void *),
3875e6e3a92SBing Zhao 					   GFP_KERNEL);
3885e6e3a92SBing Zhao 	if (!new_node->rx_reorder_ptr) {
38961494648SXu Wang 		kfree(new_node);
390acebe8c1SZhaoyang Liu 		mwifiex_dbg(priv->adapter, ERROR,
3915e6e3a92SBing Zhao 			    "%s: failed to alloc reorder_ptr\n", __func__);
3925e6e3a92SBing Zhao 		return;
3935e6e3a92SBing Zhao 	}
3945e6e3a92SBing Zhao 
3955e6e3a92SBing Zhao 	new_node->timer_context.ptr = new_node;
3965e6e3a92SBing Zhao 	new_node->timer_context.priv = priv;
3973a8fede1SMarc Yang 	new_node->timer_context.timer_is_set = false;
3985e6e3a92SBing Zhao 
39908c2eb8eSKees Cook 	timer_setup(&new_node->timer_context.timer, mwifiex_flush_data, 0);
4005e6e3a92SBing Zhao 
4015e6e3a92SBing Zhao 	for (i = 0; i < win_size; ++i)
4025e6e3a92SBing Zhao 		new_node->rx_reorder_ptr[i] = NULL;
4035e6e3a92SBing Zhao 
4048a7f9fd8SBrian Norris 	spin_lock_bh(&priv->rx_reorder_tbl_lock);
4055e6e3a92SBing Zhao 	list_add_tail(&new_node->list, &priv->rx_reorder_tbl_ptr);
4068a7f9fd8SBrian Norris 	spin_unlock_bh(&priv->rx_reorder_tbl_lock);
4075e6e3a92SBing Zhao }
4085e6e3a92SBing Zhao 
4093a8fede1SMarc Yang static void
mwifiex_11n_rxreorder_timer_restart(struct mwifiex_rx_reorder_tbl * tbl)4103a8fede1SMarc Yang mwifiex_11n_rxreorder_timer_restart(struct mwifiex_rx_reorder_tbl *tbl)
4113a8fede1SMarc Yang {
4123a8fede1SMarc Yang 	u32 min_flush_time;
4133a8fede1SMarc Yang 
4143a8fede1SMarc Yang 	if (tbl->win_size >= MWIFIEX_BA_WIN_SIZE_32)
4153a8fede1SMarc Yang 		min_flush_time = MIN_FLUSH_TIMER_15_MS;
4163a8fede1SMarc Yang 	else
4173a8fede1SMarc Yang 		min_flush_time = MIN_FLUSH_TIMER_MS;
4183a8fede1SMarc Yang 
4193a8fede1SMarc Yang 	mod_timer(&tbl->timer_context.timer,
4203a8fede1SMarc Yang 		  jiffies + msecs_to_jiffies(min_flush_time * tbl->win_size));
4213a8fede1SMarc Yang 
4223a8fede1SMarc Yang 	tbl->timer_context.timer_is_set = true;
4233a8fede1SMarc Yang }
4243a8fede1SMarc Yang 
4255e6e3a92SBing Zhao /*
4265e6e3a92SBing Zhao  * This function prepares command for adding a BA request.
4275e6e3a92SBing Zhao  *
4285e6e3a92SBing Zhao  * Preparation includes -
4295e6e3a92SBing Zhao  *      - Setting command ID and proper size
4305e6e3a92SBing Zhao  *      - Setting add BA request buffer
4315e6e3a92SBing Zhao  *      - Ensuring correct endian-ness
4325e6e3a92SBing Zhao  */
mwifiex_cmd_11n_addba_req(struct host_cmd_ds_command * cmd,void * data_buf)433572e8f3eSAmitkumar Karwar int mwifiex_cmd_11n_addba_req(struct host_cmd_ds_command *cmd, void *data_buf)
4345e6e3a92SBing Zhao {
4352c208890SJoe Perches 	struct host_cmd_ds_11n_addba_req *add_ba_req = &cmd->params.add_ba_req;
4365e6e3a92SBing Zhao 
4375e6e3a92SBing Zhao 	cmd->command = cpu_to_le16(HostCmd_CMD_11N_ADDBA_REQ);
4385e6e3a92SBing Zhao 	cmd->size = cpu_to_le16(sizeof(*add_ba_req) + S_DS_GEN);
4395e6e3a92SBing Zhao 	memcpy(add_ba_req, data_buf, sizeof(*add_ba_req));
4405e6e3a92SBing Zhao 
4415e6e3a92SBing Zhao 	return 0;
4425e6e3a92SBing Zhao }
4435e6e3a92SBing Zhao 
4445e6e3a92SBing Zhao /*
4455e6e3a92SBing Zhao  * This function prepares command for adding a BA response.
4465e6e3a92SBing Zhao  *
4475e6e3a92SBing Zhao  * Preparation includes -
4485e6e3a92SBing Zhao  *      - Setting command ID and proper size
4495e6e3a92SBing Zhao  *      - Setting add BA response buffer
4505e6e3a92SBing Zhao  *      - Ensuring correct endian-ness
4515e6e3a92SBing Zhao  */
mwifiex_cmd_11n_addba_rsp_gen(struct mwifiex_private * priv,struct host_cmd_ds_command * cmd,struct host_cmd_ds_11n_addba_req * cmd_addba_req)4525e6e3a92SBing Zhao int mwifiex_cmd_11n_addba_rsp_gen(struct mwifiex_private *priv,
4535e6e3a92SBing Zhao 				  struct host_cmd_ds_command *cmd,
454a5ffddb7SAmitkumar Karwar 				  struct host_cmd_ds_11n_addba_req
455a5ffddb7SAmitkumar Karwar 				  *cmd_addba_req)
4565e6e3a92SBing Zhao {
4572c208890SJoe Perches 	struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = &cmd->params.add_ba_rsp;
458b06c5321SAvinash Patil 	struct mwifiex_sta_node *sta_ptr;
459b06c5321SAvinash Patil 	u32 rx_win_size = priv->add_ba_param.rx_win_size;
460270e58e8SYogesh Ashok Powar 	u8 tid;
461270e58e8SYogesh Ashok Powar 	int win_size;
4625e6e3a92SBing Zhao 	uint16_t block_ack_param_set;
4635e6e3a92SBing Zhao 
464b06c5321SAvinash Patil 	if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) &&
465b06c5321SAvinash Patil 	    ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) &&
466b06c5321SAvinash Patil 	    priv->adapter->is_hw_11ac_capable &&
467b06c5321SAvinash Patil 	    memcmp(priv->cfg_bssid, cmd_addba_req->peer_mac_addr, ETH_ALEN)) {
4688a7f9fd8SBrian Norris 		spin_lock_bh(&priv->sta_list_spinlock);
469b06c5321SAvinash Patil 		sta_ptr = mwifiex_get_sta_entry(priv,
470b06c5321SAvinash Patil 						cmd_addba_req->peer_mac_addr);
471b06c5321SAvinash Patil 		if (!sta_ptr) {
4728a7f9fd8SBrian Norris 			spin_unlock_bh(&priv->sta_list_spinlock);
473acebe8c1SZhaoyang Liu 			mwifiex_dbg(priv->adapter, ERROR,
474b06c5321SAvinash Patil 				    "BA setup with unknown TDLS peer %pM!\n",
475b06c5321SAvinash Patil 				    cmd_addba_req->peer_mac_addr);
476b06c5321SAvinash Patil 			return -1;
477b06c5321SAvinash Patil 		}
478b06c5321SAvinash Patil 		if (sta_ptr->is_11ac_enabled)
479b06c5321SAvinash Patil 			rx_win_size = MWIFIEX_11AC_STA_AMPDU_DEF_RXWINSIZE;
4808a7f9fd8SBrian Norris 		spin_unlock_bh(&priv->sta_list_spinlock);
481b06c5321SAvinash Patil 	}
482b06c5321SAvinash Patil 
4835e6e3a92SBing Zhao 	cmd->command = cpu_to_le16(HostCmd_CMD_11N_ADDBA_RSP);
4845e6e3a92SBing Zhao 	cmd->size = cpu_to_le16(sizeof(*add_ba_rsp) + S_DS_GEN);
4855e6e3a92SBing Zhao 
4865e6e3a92SBing Zhao 	memcpy(add_ba_rsp->peer_mac_addr, cmd_addba_req->peer_mac_addr,
4875e6e3a92SBing Zhao 	       ETH_ALEN);
4885e6e3a92SBing Zhao 	add_ba_rsp->dialog_token = cmd_addba_req->dialog_token;
4895e6e3a92SBing Zhao 	add_ba_rsp->block_ack_tmo = cmd_addba_req->block_ack_tmo;
4905e6e3a92SBing Zhao 	add_ba_rsp->ssn = cmd_addba_req->ssn;
4915e6e3a92SBing Zhao 
4925e6e3a92SBing Zhao 	block_ack_param_set = le16_to_cpu(cmd_addba_req->block_ack_param_set);
4935e6e3a92SBing Zhao 	tid = (block_ack_param_set & IEEE80211_ADDBA_PARAM_TID_MASK)
4945e6e3a92SBing Zhao 		>> BLOCKACKPARAM_TID_POS;
4955e6e3a92SBing Zhao 	add_ba_rsp->status_code = cpu_to_le16(ADDBA_RSP_STATUS_ACCEPT);
4965e6e3a92SBing Zhao 	block_ack_param_set &= ~IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK;
4974c9f9fb2SAmitkumar Karwar 
4984c9f9fb2SAmitkumar Karwar 	/* If we don't support AMSDU inside AMPDU, reset the bit */
4994c9f9fb2SAmitkumar Karwar 	if (!priv->add_ba_param.rx_amsdu ||
5004c9f9fb2SAmitkumar Karwar 	    (priv->aggr_prio_tbl[tid].amsdu == BA_STREAM_NOT_ALLOWED))
5015e6e3a92SBing Zhao 		block_ack_param_set &= ~BLOCKACKPARAM_AMSDU_SUPP_MASK;
502b06c5321SAvinash Patil 	block_ack_param_set |= rx_win_size << BLOCKACKPARAM_WINSIZE_POS;
5035e6e3a92SBing Zhao 	add_ba_rsp->block_ack_param_set = cpu_to_le16(block_ack_param_set);
5045e6e3a92SBing Zhao 	win_size = (le16_to_cpu(add_ba_rsp->block_ack_param_set)
5055e6e3a92SBing Zhao 					& IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK)
5065e6e3a92SBing Zhao 					>> BLOCKACKPARAM_WINSIZE_POS;
5075e6e3a92SBing Zhao 	cmd_addba_req->block_ack_param_set = cpu_to_le16(block_ack_param_set);
5085e6e3a92SBing Zhao 
5095e6e3a92SBing Zhao 	mwifiex_11n_create_rx_reorder_tbl(priv, cmd_addba_req->peer_mac_addr,
51084266841SYogesh Ashok Powar 					  tid, win_size,
51184266841SYogesh Ashok Powar 					  le16_to_cpu(cmd_addba_req->ssn));
5125e6e3a92SBing Zhao 	return 0;
5135e6e3a92SBing Zhao }
5145e6e3a92SBing Zhao 
5155e6e3a92SBing Zhao /*
5165e6e3a92SBing Zhao  * This function prepares command for deleting a BA request.
5175e6e3a92SBing Zhao  *
5185e6e3a92SBing Zhao  * Preparation includes -
5195e6e3a92SBing Zhao  *      - Setting command ID and proper size
5205e6e3a92SBing Zhao  *      - Setting del BA request buffer
5215e6e3a92SBing Zhao  *      - Ensuring correct endian-ness
5225e6e3a92SBing Zhao  */
mwifiex_cmd_11n_delba(struct host_cmd_ds_command * cmd,void * data_buf)523572e8f3eSAmitkumar Karwar int mwifiex_cmd_11n_delba(struct host_cmd_ds_command *cmd, void *data_buf)
5245e6e3a92SBing Zhao {
5252c208890SJoe Perches 	struct host_cmd_ds_11n_delba *del_ba = &cmd->params.del_ba;
5265e6e3a92SBing Zhao 
5275e6e3a92SBing Zhao 	cmd->command = cpu_to_le16(HostCmd_CMD_11N_DELBA);
5285e6e3a92SBing Zhao 	cmd->size = cpu_to_le16(sizeof(*del_ba) + S_DS_GEN);
5295e6e3a92SBing Zhao 	memcpy(del_ba, data_buf, sizeof(*del_ba));
5305e6e3a92SBing Zhao 
5315e6e3a92SBing Zhao 	return 0;
5325e6e3a92SBing Zhao }
5335e6e3a92SBing Zhao 
5345e6e3a92SBing Zhao /*
5355e6e3a92SBing Zhao  * This function identifies if Rx reordering is needed for a received packet.
5365e6e3a92SBing Zhao  *
5375e6e3a92SBing Zhao  * In case reordering is required, the function will do the reordering
5385e6e3a92SBing Zhao  * before sending it to kernel.
5395e6e3a92SBing Zhao  *
5405e6e3a92SBing Zhao  * The Rx reorder table is checked first with the received TID/TA pair. If
5415e6e3a92SBing Zhao  * not found, the received packet is dispatched immediately. But if found,
5425e6e3a92SBing Zhao  * the packet is reordered and all the packets in the updated Rx reordering
5435e6e3a92SBing Zhao  * table is dispatched until a hole is found.
5445e6e3a92SBing Zhao  *
5455e6e3a92SBing Zhao  * For sequence number less than the starting window, the packet is dropped.
5465e6e3a92SBing Zhao  */
mwifiex_11n_rx_reorder_pkt(struct mwifiex_private * priv,u16 seq_num,u16 tid,u8 * ta,u8 pkt_type,void * payload)5475e6e3a92SBing Zhao int mwifiex_11n_rx_reorder_pkt(struct mwifiex_private *priv,
5485e6e3a92SBing Zhao 				u16 seq_num, u16 tid,
5495e6e3a92SBing Zhao 				u8 *ta, u8 pkt_type, void *payload)
5505e6e3a92SBing Zhao {
5518c00228eSYogesh Ashok Powar 	struct mwifiex_rx_reorder_tbl *tbl;
5523a8fede1SMarc Yang 	int prev_start_win, start_win, end_win, win_size;
553270e58e8SYogesh Ashok Powar 	u16 pkt_index;
5548acbea61SPaul Stewart 	bool init_window_shift = false;
5553a8fede1SMarc Yang 	int ret = 0;
5565e6e3a92SBing Zhao 
5572c208890SJoe Perches 	tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid, ta);
5588c00228eSYogesh Ashok Powar 	if (!tbl) {
5595e6e43ebSAmitkumar Karwar 		if (pkt_type != PKT_TYPE_BAR)
5605e6e43ebSAmitkumar Karwar 			mwifiex_11n_dispatch_pkt(priv, payload);
5613a8fede1SMarc Yang 		return ret;
5625e6e3a92SBing Zhao 	}
5634c9f9fb2SAmitkumar Karwar 
5644c9f9fb2SAmitkumar Karwar 	if ((pkt_type == PKT_TYPE_AMSDU) && !tbl->amsdu) {
5654c9f9fb2SAmitkumar Karwar 		mwifiex_11n_dispatch_pkt(priv, payload);
5663a8fede1SMarc Yang 		return ret;
5674c9f9fb2SAmitkumar Karwar 	}
5684c9f9fb2SAmitkumar Karwar 
5698c00228eSYogesh Ashok Powar 	start_win = tbl->start_win;
5703a8fede1SMarc Yang 	prev_start_win = start_win;
5718c00228eSYogesh Ashok Powar 	win_size = tbl->win_size;
5725e6e3a92SBing Zhao 	end_win = ((start_win + win_size) - 1) & (MAX_TID_VALUE - 1);
5738acbea61SPaul Stewart 	if (tbl->flags & RXREOR_INIT_WINDOW_SHIFT) {
5748acbea61SPaul Stewart 		init_window_shift = true;
5758acbea61SPaul Stewart 		tbl->flags &= ~RXREOR_INIT_WINDOW_SHIFT;
5768acbea61SPaul Stewart 	}
5775e6e3a92SBing Zhao 
5782db96c3dSAvinash Patil 	if (tbl->flags & RXREOR_FORCE_NO_DROP) {
579acebe8c1SZhaoyang Liu 		mwifiex_dbg(priv->adapter, INFO,
5802db96c3dSAvinash Patil 			    "RXREOR_FORCE_NO_DROP when HS is activated\n");
5812db96c3dSAvinash Patil 		tbl->flags &= ~RXREOR_FORCE_NO_DROP;
5828acbea61SPaul Stewart 	} else if (init_window_shift && seq_num < start_win &&
5838acbea61SPaul Stewart 		   seq_num >= tbl->init_win) {
584acebe8c1SZhaoyang Liu 		mwifiex_dbg(priv->adapter, INFO,
5858acbea61SPaul Stewart 			    "Sender TID sequence number reset %d->%d for SSN %d\n",
5868acbea61SPaul Stewart 			    start_win, seq_num, tbl->init_win);
5878acbea61SPaul Stewart 		tbl->start_win = start_win = seq_num;
5888acbea61SPaul Stewart 		end_win = ((start_win + win_size) - 1) & (MAX_TID_VALUE - 1);
5892db96c3dSAvinash Patil 	} else {
5908acbea61SPaul Stewart 		/*
5918acbea61SPaul Stewart 		 * If seq_num is less then starting win then ignore and drop
5928acbea61SPaul Stewart 		 * the packet
5938acbea61SPaul Stewart 		 */
5942db96c3dSAvinash Patil 		if ((start_win + TWOPOW11) > (MAX_TID_VALUE - 1)) {
59584266841SYogesh Ashok Powar 			if (seq_num >= ((start_win + TWOPOW11) &
5962db96c3dSAvinash Patil 					(MAX_TID_VALUE - 1)) &&
5973a8fede1SMarc Yang 			    seq_num < start_win) {
5983a8fede1SMarc Yang 				ret = -1;
5993a8fede1SMarc Yang 				goto done;
6003a8fede1SMarc Yang 			}
60184266841SYogesh Ashok Powar 		} else if ((seq_num < start_win) ||
6023a8fede1SMarc Yang 			   (seq_num >= (start_win + TWOPOW11))) {
6033a8fede1SMarc Yang 			ret = -1;
6043a8fede1SMarc Yang 			goto done;
6055e6e3a92SBing Zhao 		}
6062db96c3dSAvinash Patil 	}
6075e6e3a92SBing Zhao 
6085e6e3a92SBing Zhao 	/*
6095e6e3a92SBing Zhao 	 * If this packet is a BAR we adjust seq_num as
6105e6e3a92SBing Zhao 	 * WinStart = seq_num
6115e6e3a92SBing Zhao 	 */
6125e6e3a92SBing Zhao 	if (pkt_type == PKT_TYPE_BAR)
6135e6e3a92SBing Zhao 		seq_num = ((seq_num + win_size) - 1) & (MAX_TID_VALUE - 1);
6145e6e3a92SBing Zhao 
61584266841SYogesh Ashok Powar 	if (((end_win < start_win) &&
6162db96c3dSAvinash Patil 	     (seq_num < start_win) && (seq_num > end_win)) ||
61784266841SYogesh Ashok Powar 	    ((end_win > start_win) && ((seq_num > end_win) ||
61884266841SYogesh Ashok Powar 				       (seq_num < start_win)))) {
6195e6e3a92SBing Zhao 		end_win = seq_num;
620cc751498SAndrzej Hajda 		if (((end_win - win_size) + 1) >= 0)
6215e6e3a92SBing Zhao 			start_win = (end_win - win_size) + 1;
6225e6e3a92SBing Zhao 		else
623cc751498SAndrzej Hajda 			start_win = (MAX_TID_VALUE - (win_size - end_win)) + 1;
6245e6e43ebSAmitkumar Karwar 		mwifiex_11n_dispatch_pkt_until_start_win(priv, tbl, start_win);
6255e6e3a92SBing Zhao 	}
6265e6e3a92SBing Zhao 
6275e6e3a92SBing Zhao 	if (pkt_type != PKT_TYPE_BAR) {
6285e6e3a92SBing Zhao 		if (seq_num >= start_win)
6295e6e3a92SBing Zhao 			pkt_index = seq_num - start_win;
6305e6e3a92SBing Zhao 		else
6315e6e3a92SBing Zhao 			pkt_index = (seq_num+MAX_TID_VALUE) - start_win;
6325e6e3a92SBing Zhao 
6333a8fede1SMarc Yang 		if (tbl->rx_reorder_ptr[pkt_index]) {
6343a8fede1SMarc Yang 			ret = -1;
6353a8fede1SMarc Yang 			goto done;
6363a8fede1SMarc Yang 		}
6375e6e3a92SBing Zhao 
6388c00228eSYogesh Ashok Powar 		tbl->rx_reorder_ptr[pkt_index] = payload;
6395e6e3a92SBing Zhao 	}
6405e6e3a92SBing Zhao 
6415e6e3a92SBing Zhao 	/*
6425e6e3a92SBing Zhao 	 * Dispatch all packets sequentially from start_win until a
6435e6e3a92SBing Zhao 	 * hole is found and adjust the start_win appropriately
6445e6e3a92SBing Zhao 	 */
6458c00228eSYogesh Ashok Powar 	mwifiex_11n_scan_and_dispatch(priv, tbl);
6465e6e3a92SBing Zhao 
6473a8fede1SMarc Yang done:
6483a8fede1SMarc Yang 	if (!tbl->timer_context.timer_is_set ||
6493a8fede1SMarc Yang 	    prev_start_win != tbl->start_win)
6503a8fede1SMarc Yang 		mwifiex_11n_rxreorder_timer_restart(tbl);
6513a8fede1SMarc Yang 	return ret;
6525e6e3a92SBing Zhao }
6535e6e3a92SBing Zhao 
6545e6e3a92SBing Zhao /*
6555e6e3a92SBing Zhao  * This function deletes an entry for a given TID/TA pair.
6565e6e3a92SBing Zhao  *
6575e6e3a92SBing Zhao  * The TID/TA are taken from del BA event body.
6585e6e3a92SBing Zhao  */
6595e6e3a92SBing Zhao void
mwifiex_del_ba_tbl(struct mwifiex_private * priv,int tid,u8 * peer_mac,u8 type,int initiator)6603e822635SYogesh Ashok Powar mwifiex_del_ba_tbl(struct mwifiex_private *priv, int tid, u8 *peer_mac,
6613e822635SYogesh Ashok Powar 		   u8 type, int initiator)
6625e6e3a92SBing Zhao {
6638c00228eSYogesh Ashok Powar 	struct mwifiex_rx_reorder_tbl *tbl;
6645e6e3a92SBing Zhao 	struct mwifiex_tx_ba_stream_tbl *ptx_tbl;
66539df5e82SZhaoyang Liu 	struct mwifiex_ra_list_tbl *ra_list;
6665e6e3a92SBing Zhao 	u8 cleanup_rx_reorder_tbl;
667719a25e3SXinming Hu 	int tid_down;
6685e6e3a92SBing Zhao 
6695e6e3a92SBing Zhao 	if (type == TYPE_DELBA_RECEIVE)
6705e6e3a92SBing Zhao 		cleanup_rx_reorder_tbl = (initiator) ? true : false;
6715e6e3a92SBing Zhao 	else
6725e6e3a92SBing Zhao 		cleanup_rx_reorder_tbl = (initiator) ? false : true;
6735e6e3a92SBing Zhao 
674acebe8c1SZhaoyang Liu 	mwifiex_dbg(priv->adapter, EVENT, "event: DELBA: %pM tid=%d initiator=%d\n",
67584266841SYogesh Ashok Powar 		    peer_mac, tid, initiator);
6765e6e3a92SBing Zhao 
6775e6e3a92SBing Zhao 	if (cleanup_rx_reorder_tbl) {
6788c00228eSYogesh Ashok Powar 		tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid,
6795e6e3a92SBing Zhao 								 peer_mac);
6808c00228eSYogesh Ashok Powar 		if (!tbl) {
681acebe8c1SZhaoyang Liu 			mwifiex_dbg(priv->adapter, EVENT,
6825e6e3a92SBing Zhao 				    "event: TID, TA not found in table\n");
6835e6e3a92SBing Zhao 			return;
6845e6e3a92SBing Zhao 		}
6858c00228eSYogesh Ashok Powar 		mwifiex_del_rx_reorder_entry(priv, tbl);
6865e6e3a92SBing Zhao 	} else {
6873e822635SYogesh Ashok Powar 		ptx_tbl = mwifiex_get_ba_tbl(priv, tid, peer_mac);
6885e6e3a92SBing Zhao 		if (!ptx_tbl) {
689acebe8c1SZhaoyang Liu 			mwifiex_dbg(priv->adapter, EVENT,
6905e6e3a92SBing Zhao 				    "event: TID, RA not found in table\n");
6915e6e3a92SBing Zhao 			return;
6925e6e3a92SBing Zhao 		}
693719a25e3SXinming Hu 
694719a25e3SXinming Hu 		tid_down = mwifiex_wmm_downgrade_tid(priv, tid);
695719a25e3SXinming Hu 		ra_list = mwifiex_wmm_get_ralist_node(priv, tid_down, peer_mac);
69639df5e82SZhaoyang Liu 		if (ra_list) {
69739df5e82SZhaoyang Liu 			ra_list->amsdu_in_ampdu = false;
69839df5e82SZhaoyang Liu 			ra_list->ba_status = BA_SETUP_NONE;
69939df5e82SZhaoyang Liu 		}
7008a7f9fd8SBrian Norris 		spin_lock_bh(&priv->tx_ba_stream_tbl_lock);
7015e6e3a92SBing Zhao 		mwifiex_11n_delete_tx_ba_stream_tbl_entry(priv, ptx_tbl);
7028a7f9fd8SBrian Norris 		spin_unlock_bh(&priv->tx_ba_stream_tbl_lock);
7035e6e3a92SBing Zhao 	}
7045e6e3a92SBing Zhao }
7055e6e3a92SBing Zhao 
7065e6e3a92SBing Zhao /*
7075e6e3a92SBing Zhao  * This function handles the command response of an add BA response.
7085e6e3a92SBing Zhao  *
7095e6e3a92SBing Zhao  * Handling includes changing the header fields into CPU format and
7105e6e3a92SBing Zhao  * creating the stream, provided the add BA is accepted.
7115e6e3a92SBing Zhao  */
mwifiex_ret_11n_addba_resp(struct mwifiex_private * priv,struct host_cmd_ds_command * resp)7125e6e3a92SBing Zhao int mwifiex_ret_11n_addba_resp(struct mwifiex_private *priv,
7135e6e3a92SBing Zhao 			       struct host_cmd_ds_command *resp)
7145e6e3a92SBing Zhao {
7152c208890SJoe Perches 	struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = &resp->params.add_ba_rsp;
7165e6e3a92SBing Zhao 	int tid, win_size;
7178c00228eSYogesh Ashok Powar 	struct mwifiex_rx_reorder_tbl *tbl;
7185e6e3a92SBing Zhao 	uint16_t block_ack_param_set;
7195e6e3a92SBing Zhao 
7205e6e3a92SBing Zhao 	block_ack_param_set = le16_to_cpu(add_ba_rsp->block_ack_param_set);
7215e6e3a92SBing Zhao 
7225e6e3a92SBing Zhao 	tid = (block_ack_param_set & IEEE80211_ADDBA_PARAM_TID_MASK)
7235e6e3a92SBing Zhao 		>> BLOCKACKPARAM_TID_POS;
7245e6e3a92SBing Zhao 	/*
7255e6e3a92SBing Zhao 	 * Check if we had rejected the ADDBA, if yes then do not create
7265e6e3a92SBing Zhao 	 * the stream
7275e6e3a92SBing Zhao 	 */
72863410c37SAmitkumar Karwar 	if (le16_to_cpu(add_ba_rsp->status_code) != BA_RESULT_SUCCESS) {
729acebe8c1SZhaoyang Liu 		mwifiex_dbg(priv->adapter, ERROR, "ADDBA RSP: failed %pM tid=%d)\n",
7305e6e3a92SBing Zhao 			    add_ba_rsp->peer_mac_addr, tid);
7315e6e3a92SBing Zhao 
7328c00228eSYogesh Ashok Powar 		tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid,
7338c00228eSYogesh Ashok Powar 						     add_ba_rsp->peer_mac_addr);
7348c00228eSYogesh Ashok Powar 		if (tbl)
7358c00228eSYogesh Ashok Powar 			mwifiex_del_rx_reorder_entry(priv, tbl);
73663410c37SAmitkumar Karwar 
73763410c37SAmitkumar Karwar 		return 0;
7385e6e3a92SBing Zhao 	}
7395e6e3a92SBing Zhao 
74063410c37SAmitkumar Karwar 	win_size = (block_ack_param_set & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK)
74163410c37SAmitkumar Karwar 		    >> BLOCKACKPARAM_WINSIZE_POS;
74263410c37SAmitkumar Karwar 
7434c9f9fb2SAmitkumar Karwar 	tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid,
7444c9f9fb2SAmitkumar Karwar 					     add_ba_rsp->peer_mac_addr);
7454c9f9fb2SAmitkumar Karwar 	if (tbl) {
7464c9f9fb2SAmitkumar Karwar 		if ((block_ack_param_set & BLOCKACKPARAM_AMSDU_SUPP_MASK) &&
7474c9f9fb2SAmitkumar Karwar 		    priv->add_ba_param.rx_amsdu &&
7484c9f9fb2SAmitkumar Karwar 		    (priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED))
7494c9f9fb2SAmitkumar Karwar 			tbl->amsdu = true;
7504c9f9fb2SAmitkumar Karwar 		else
7514c9f9fb2SAmitkumar Karwar 			tbl->amsdu = false;
7524c9f9fb2SAmitkumar Karwar 	}
7534c9f9fb2SAmitkumar Karwar 
754acebe8c1SZhaoyang Liu 	mwifiex_dbg(priv->adapter, CMD,
75563410c37SAmitkumar Karwar 		    "cmd: ADDBA RSP: %pM tid=%d ssn=%d win_size=%d\n",
75663410c37SAmitkumar Karwar 		add_ba_rsp->peer_mac_addr, tid, add_ba_rsp->ssn, win_size);
75763410c37SAmitkumar Karwar 
7585e6e3a92SBing Zhao 	return 0;
7595e6e3a92SBing Zhao }
7605e6e3a92SBing Zhao 
7615e6e3a92SBing Zhao /*
7625e6e3a92SBing Zhao  * This function handles BA stream timeout event by preparing and sending
7635e6e3a92SBing Zhao  * a command to the firmware.
7645e6e3a92SBing Zhao  */
mwifiex_11n_ba_stream_timeout(struct mwifiex_private * priv,struct host_cmd_ds_11n_batimeout * event)7655e6e3a92SBing Zhao void mwifiex_11n_ba_stream_timeout(struct mwifiex_private *priv,
7665e6e3a92SBing Zhao 				   struct host_cmd_ds_11n_batimeout *event)
7675e6e3a92SBing Zhao {
7685e6e3a92SBing Zhao 	struct host_cmd_ds_11n_delba delba;
7695e6e3a92SBing Zhao 
7705e6e3a92SBing Zhao 	memset(&delba, 0, sizeof(struct host_cmd_ds_11n_delba));
7715e6e3a92SBing Zhao 	memcpy(delba.peer_mac_addr, event->peer_mac_addr, ETH_ALEN);
7725e6e3a92SBing Zhao 
7735e6e3a92SBing Zhao 	delba.del_ba_param_set |=
7745e6e3a92SBing Zhao 		cpu_to_le16((u16) event->tid << DELBA_TID_POS);
7755e6e3a92SBing Zhao 	delba.del_ba_param_set |= cpu_to_le16(
7765e6e3a92SBing Zhao 		(u16) event->origninator << DELBA_INITIATOR_POS);
7775e6e3a92SBing Zhao 	delba.reason_code = cpu_to_le16(WLAN_REASON_QSTA_TIMEOUT);
778fa0ecbb9SBing Zhao 	mwifiex_send_cmd(priv, HostCmd_CMD_11N_DELBA, 0, 0, &delba, false);
7795e6e3a92SBing Zhao }
7805e6e3a92SBing Zhao 
7815e6e3a92SBing Zhao /*
7825e6e3a92SBing Zhao  * This function cleans up the Rx reorder table by deleting all the entries
7835e6e3a92SBing Zhao  * and re-initializing.
7845e6e3a92SBing Zhao  */
mwifiex_11n_cleanup_reorder_tbl(struct mwifiex_private * priv)7855e6e3a92SBing Zhao void mwifiex_11n_cleanup_reorder_tbl(struct mwifiex_private *priv)
7865e6e3a92SBing Zhao {
7875e6e3a92SBing Zhao 	struct mwifiex_rx_reorder_tbl *del_tbl_ptr, *tmp_node;
7885e6e3a92SBing Zhao 
7898a7f9fd8SBrian Norris 	spin_lock_bh(&priv->rx_reorder_tbl_lock);
7905e6e3a92SBing Zhao 	list_for_each_entry_safe(del_tbl_ptr, tmp_node,
7911aa48f08SBrian Norris 				 &priv->rx_reorder_tbl_ptr, list) {
7928a7f9fd8SBrian Norris 		spin_unlock_bh(&priv->rx_reorder_tbl_lock);
7938c00228eSYogesh Ashok Powar 		mwifiex_del_rx_reorder_entry(priv, del_tbl_ptr);
7948a7f9fd8SBrian Norris 		spin_lock_bh(&priv->rx_reorder_tbl_lock);
7951aa48f08SBrian Norris 	}
7962d702830SAmitkumar Karwar 	INIT_LIST_HEAD(&priv->rx_reorder_tbl_ptr);
7978a7f9fd8SBrian Norris 	spin_unlock_bh(&priv->rx_reorder_tbl_lock);
7985e6e3a92SBing Zhao 
79992583924SStone Piao 	mwifiex_reset_11n_rx_seq_num(priv);
8005e6e3a92SBing Zhao }
8012db96c3dSAvinash Patil 
8022db96c3dSAvinash Patil /*
8032db96c3dSAvinash Patil  * This function updates all rx_reorder_tbl's flags.
8042db96c3dSAvinash Patil  */
mwifiex_update_rxreor_flags(struct mwifiex_adapter * adapter,u8 flags)8052db96c3dSAvinash Patil void mwifiex_update_rxreor_flags(struct mwifiex_adapter *adapter, u8 flags)
8062db96c3dSAvinash Patil {
8072db96c3dSAvinash Patil 	struct mwifiex_private *priv;
8082db96c3dSAvinash Patil 	struct mwifiex_rx_reorder_tbl *tbl;
8092db96c3dSAvinash Patil 	int i;
8102db96c3dSAvinash Patil 
8112db96c3dSAvinash Patil 	for (i = 0; i < adapter->priv_num; i++) {
8122db96c3dSAvinash Patil 		priv = adapter->priv[i];
8132db96c3dSAvinash Patil 
8148a7f9fd8SBrian Norris 		spin_lock_bh(&priv->rx_reorder_tbl_lock);
8152db96c3dSAvinash Patil 		list_for_each_entry(tbl, &priv->rx_reorder_tbl_ptr, list)
8162db96c3dSAvinash Patil 			tbl->flags = flags;
8178a7f9fd8SBrian Norris 		spin_unlock_bh(&priv->rx_reorder_tbl_lock);
8182db96c3dSAvinash Patil 	}
8192db96c3dSAvinash Patil 
8202db96c3dSAvinash Patil 	return;
8212db96c3dSAvinash Patil }
822d219b7ebSChunfan Chen 
823d219b7ebSChunfan Chen /* This function update all the rx_win_size based on coex flag
824d219b7ebSChunfan Chen  */
mwifiex_update_ampdu_rxwinsize(struct mwifiex_adapter * adapter,bool coex_flag)825d219b7ebSChunfan Chen static void mwifiex_update_ampdu_rxwinsize(struct mwifiex_adapter *adapter,
826d219b7ebSChunfan Chen 					   bool coex_flag)
827d219b7ebSChunfan Chen {
828d219b7ebSChunfan Chen 	u8 i;
829d219b7ebSChunfan Chen 	u32 rx_win_size;
830d219b7ebSChunfan Chen 	struct mwifiex_private *priv;
831d219b7ebSChunfan Chen 
832d219b7ebSChunfan Chen 	dev_dbg(adapter->dev, "Update rxwinsize %d\n", coex_flag);
833d219b7ebSChunfan Chen 
834d219b7ebSChunfan Chen 	for (i = 0; i < adapter->priv_num; i++) {
835d219b7ebSChunfan Chen 		priv = adapter->priv[i];
836d219b7ebSChunfan Chen 		rx_win_size = priv->add_ba_param.rx_win_size;
837d219b7ebSChunfan Chen 		if (coex_flag) {
838d219b7ebSChunfan Chen 			if (priv->bss_type == MWIFIEX_BSS_TYPE_STA)
839d219b7ebSChunfan Chen 				priv->add_ba_param.rx_win_size =
840d219b7ebSChunfan Chen 					MWIFIEX_STA_COEX_AMPDU_DEF_RXWINSIZE;
841d219b7ebSChunfan Chen 			if (priv->bss_type == MWIFIEX_BSS_TYPE_P2P)
842d219b7ebSChunfan Chen 				priv->add_ba_param.rx_win_size =
843d219b7ebSChunfan Chen 					MWIFIEX_STA_COEX_AMPDU_DEF_RXWINSIZE;
844d219b7ebSChunfan Chen 			if (priv->bss_type == MWIFIEX_BSS_TYPE_UAP)
845d219b7ebSChunfan Chen 				priv->add_ba_param.rx_win_size =
846d219b7ebSChunfan Chen 					MWIFIEX_UAP_COEX_AMPDU_DEF_RXWINSIZE;
847d219b7ebSChunfan Chen 		} else {
848d219b7ebSChunfan Chen 			if (priv->bss_type == MWIFIEX_BSS_TYPE_STA)
849d219b7ebSChunfan Chen 				priv->add_ba_param.rx_win_size =
850d219b7ebSChunfan Chen 					MWIFIEX_STA_AMPDU_DEF_RXWINSIZE;
851d219b7ebSChunfan Chen 			if (priv->bss_type == MWIFIEX_BSS_TYPE_P2P)
852d219b7ebSChunfan Chen 				priv->add_ba_param.rx_win_size =
853d219b7ebSChunfan Chen 					MWIFIEX_STA_AMPDU_DEF_RXWINSIZE;
854d219b7ebSChunfan Chen 			if (priv->bss_type == MWIFIEX_BSS_TYPE_UAP)
855d219b7ebSChunfan Chen 				priv->add_ba_param.rx_win_size =
856d219b7ebSChunfan Chen 					MWIFIEX_UAP_AMPDU_DEF_RXWINSIZE;
857d219b7ebSChunfan Chen 		}
858d219b7ebSChunfan Chen 
859d219b7ebSChunfan Chen 		if (adapter->coex_win_size && adapter->coex_rx_win_size)
860d219b7ebSChunfan Chen 			priv->add_ba_param.rx_win_size =
861d219b7ebSChunfan Chen 					adapter->coex_rx_win_size;
862d219b7ebSChunfan Chen 
863d219b7ebSChunfan Chen 		if (rx_win_size != priv->add_ba_param.rx_win_size) {
864d219b7ebSChunfan Chen 			if (!priv->media_connected)
865d219b7ebSChunfan Chen 				continue;
866d219b7ebSChunfan Chen 			for (i = 0; i < MAX_NUM_TID; i++)
867d219b7ebSChunfan Chen 				mwifiex_11n_delba(priv, i);
868d219b7ebSChunfan Chen 		}
869d219b7ebSChunfan Chen 	}
870d219b7ebSChunfan Chen }
871d219b7ebSChunfan Chen 
872d219b7ebSChunfan Chen /* This function check coex for RX BA
873d219b7ebSChunfan Chen  */
mwifiex_coex_ampdu_rxwinsize(struct mwifiex_adapter * adapter)874d219b7ebSChunfan Chen void mwifiex_coex_ampdu_rxwinsize(struct mwifiex_adapter *adapter)
875d219b7ebSChunfan Chen {
876d219b7ebSChunfan Chen 	u8 i;
877d219b7ebSChunfan Chen 	struct mwifiex_private *priv;
878d219b7ebSChunfan Chen 	u8 count = 0;
879d219b7ebSChunfan Chen 
880d219b7ebSChunfan Chen 	for (i = 0; i < adapter->priv_num; i++) {
881d219b7ebSChunfan Chen 		priv = adapter->priv[i];
882d219b7ebSChunfan Chen 		if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) {
883d219b7ebSChunfan Chen 			if (priv->media_connected)
884d219b7ebSChunfan Chen 				count++;
885d219b7ebSChunfan Chen 		}
886d219b7ebSChunfan Chen 		if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) {
887d219b7ebSChunfan Chen 			if (priv->bss_started)
888d219b7ebSChunfan Chen 				count++;
889d219b7ebSChunfan Chen 		}
89067a72043SSascha Hauer 
891d219b7ebSChunfan Chen 		if (count >= MWIFIEX_BSS_COEX_COUNT)
892d219b7ebSChunfan Chen 			break;
893d219b7ebSChunfan Chen 	}
894d219b7ebSChunfan Chen 	if (count >= MWIFIEX_BSS_COEX_COUNT)
895d219b7ebSChunfan Chen 		mwifiex_update_ampdu_rxwinsize(adapter, true);
896d219b7ebSChunfan Chen 	else
897d219b7ebSChunfan Chen 		mwifiex_update_ampdu_rxwinsize(adapter, false);
898d219b7ebSChunfan Chen }
89999ffe72cSXinming Hu 
90099ffe72cSXinming Hu /* This function handles rxba_sync event
90199ffe72cSXinming Hu  */
mwifiex_11n_rxba_sync_event(struct mwifiex_private * priv,u8 * event_buf,u16 len)90299ffe72cSXinming Hu void mwifiex_11n_rxba_sync_event(struct mwifiex_private *priv,
90399ffe72cSXinming Hu 				 u8 *event_buf, u16 len)
90499ffe72cSXinming Hu {
90599ffe72cSXinming Hu 	struct mwifiex_ie_types_rxba_sync *tlv_rxba = (void *)event_buf;
90699ffe72cSXinming Hu 	u16 tlv_type, tlv_len;
90799ffe72cSXinming Hu 	struct mwifiex_rx_reorder_tbl *rx_reor_tbl_ptr;
90899ffe72cSXinming Hu 	u8 i, j;
90999ffe72cSXinming Hu 	u16 seq_num, tlv_seq_num, tlv_bitmap_len;
91099ffe72cSXinming Hu 	int tlv_buf_left = len;
91199ffe72cSXinming Hu 	int ret;
91299ffe72cSXinming Hu 	u8 *tmp;
91399ffe72cSXinming Hu 
91499ffe72cSXinming Hu 	mwifiex_dbg_dump(priv->adapter, EVT_D, "RXBA_SYNC event:",
91599ffe72cSXinming Hu 			 event_buf, len);
916c7847241SGustavo A. R. Silva 	while (tlv_buf_left > sizeof(*tlv_rxba)) {
91799ffe72cSXinming Hu 		tlv_type = le16_to_cpu(tlv_rxba->header.type);
91899ffe72cSXinming Hu 		tlv_len  = le16_to_cpu(tlv_rxba->header.len);
919d5a93b7dSGustavo A. R. Silva 		if (size_add(sizeof(tlv_rxba->header), tlv_len) > tlv_buf_left) {
920d5a93b7dSGustavo A. R. Silva 			mwifiex_dbg(priv->adapter, WARN,
921d5a93b7dSGustavo A. R. Silva 				    "TLV size (%zu) overflows event_buf buf_left=%d\n",
922d5a93b7dSGustavo A. R. Silva 				    size_add(sizeof(tlv_rxba->header), tlv_len),
923d5a93b7dSGustavo A. R. Silva 				    tlv_buf_left);
924d5a93b7dSGustavo A. R. Silva 			return;
925d5a93b7dSGustavo A. R. Silva 		}
926d5a93b7dSGustavo A. R. Silva 
92799ffe72cSXinming Hu 		if (tlv_type != TLV_TYPE_RXBA_SYNC) {
92899ffe72cSXinming Hu 			mwifiex_dbg(priv->adapter, ERROR,
92999ffe72cSXinming Hu 				    "Wrong TLV id=0x%x\n", tlv_type);
93099ffe72cSXinming Hu 			return;
93199ffe72cSXinming Hu 		}
93299ffe72cSXinming Hu 
93399ffe72cSXinming Hu 		tlv_seq_num = le16_to_cpu(tlv_rxba->seq_num);
93499ffe72cSXinming Hu 		tlv_bitmap_len = le16_to_cpu(tlv_rxba->bitmap_len);
935d5a93b7dSGustavo A. R. Silva 		if (size_add(sizeof(*tlv_rxba), tlv_bitmap_len) > tlv_buf_left) {
936d5a93b7dSGustavo A. R. Silva 			mwifiex_dbg(priv->adapter, WARN,
937d5a93b7dSGustavo A. R. Silva 				    "TLV size (%zu) overflows event_buf buf_left=%d\n",
938d5a93b7dSGustavo A. R. Silva 				    size_add(sizeof(*tlv_rxba), tlv_bitmap_len),
939d5a93b7dSGustavo A. R. Silva 				    tlv_buf_left);
940d5a93b7dSGustavo A. R. Silva 			return;
941d5a93b7dSGustavo A. R. Silva 		}
942d5a93b7dSGustavo A. R. Silva 
94399ffe72cSXinming Hu 		mwifiex_dbg(priv->adapter, INFO,
94499ffe72cSXinming Hu 			    "%pM tid=%d seq_num=%d bitmap_len=%d\n",
94599ffe72cSXinming Hu 			    tlv_rxba->mac, tlv_rxba->tid, tlv_seq_num,
94699ffe72cSXinming Hu 			    tlv_bitmap_len);
94799ffe72cSXinming Hu 
94899ffe72cSXinming Hu 		rx_reor_tbl_ptr =
94999ffe72cSXinming Hu 			mwifiex_11n_get_rx_reorder_tbl(priv, tlv_rxba->tid,
95099ffe72cSXinming Hu 						       tlv_rxba->mac);
95199ffe72cSXinming Hu 		if (!rx_reor_tbl_ptr) {
95299ffe72cSXinming Hu 			mwifiex_dbg(priv->adapter, ERROR,
95399ffe72cSXinming Hu 				    "Can not find rx_reorder_tbl!");
95499ffe72cSXinming Hu 			return;
95599ffe72cSXinming Hu 		}
95699ffe72cSXinming Hu 
95799ffe72cSXinming Hu 		for (i = 0; i < tlv_bitmap_len; i++) {
95899ffe72cSXinming Hu 			for (j = 0 ; j < 8; j++) {
95999ffe72cSXinming Hu 				if (tlv_rxba->bitmap[i] & (1 << j)) {
96099ffe72cSXinming Hu 					seq_num = (MAX_TID_VALUE - 1) &
96199ffe72cSXinming Hu 						(tlv_seq_num + i * 8 + j);
96299ffe72cSXinming Hu 
96399ffe72cSXinming Hu 					mwifiex_dbg(priv->adapter, ERROR,
96499ffe72cSXinming Hu 						    "drop packet,seq=%d\n",
96599ffe72cSXinming Hu 						    seq_num);
96699ffe72cSXinming Hu 
96799ffe72cSXinming Hu 					ret = mwifiex_11n_rx_reorder_pkt
96899ffe72cSXinming Hu 					(priv, seq_num, tlv_rxba->tid,
96999ffe72cSXinming Hu 					 tlv_rxba->mac, 0, NULL);
97099ffe72cSXinming Hu 
97199ffe72cSXinming Hu 					if (ret)
97299ffe72cSXinming Hu 						mwifiex_dbg(priv->adapter,
97399ffe72cSXinming Hu 							    ERROR,
97499ffe72cSXinming Hu 							    "Fail to drop packet");
97599ffe72cSXinming Hu 				}
97699ffe72cSXinming Hu 			}
97799ffe72cSXinming Hu 		}
97899ffe72cSXinming Hu 
979eec679e4SGustavo A. R. Silva 		tlv_buf_left -= (sizeof(tlv_rxba->header) + tlv_len);
980eec679e4SGustavo A. R. Silva 		tmp = (u8 *)tlv_rxba  + sizeof(tlv_rxba->header) + tlv_len;
98199ffe72cSXinming Hu 		tlv_rxba = (struct mwifiex_ie_types_rxba_sync *)tmp;
98299ffe72cSXinming Hu 	}
98399ffe72cSXinming Hu }
984