xref: /linux/drivers/net/wireless/nxp/nxpwifi/11n_rxreorder.c (revision 91ec2035134982b98fab0609a9fd8480e8217dc1)
173b01e57SJeff Chen // SPDX-License-Identifier: GPL-2.0-only
273b01e57SJeff Chen /*
373b01e57SJeff Chen  * nxpwifi: 802.11n RX Re-ordering
473b01e57SJeff Chen  *
573b01e57SJeff Chen  * Copyright 2011-2024 NXP
673b01e57SJeff Chen  */
773b01e57SJeff Chen 
873b01e57SJeff Chen #include "cfg.h"
973b01e57SJeff Chen #include "util.h"
1073b01e57SJeff Chen #include "fw.h"
1173b01e57SJeff Chen #include "main.h"
1273b01e57SJeff Chen #include "cmdevt.h"
1373b01e57SJeff Chen #include "wmm.h"
1473b01e57SJeff Chen #include "11n.h"
1573b01e57SJeff Chen #include "11n_rxreorder.h"
1673b01e57SJeff Chen /* Dispatch A-MSDU to stack. */
nxpwifi_11n_dispatch_amsdu_pkt(struct nxpwifi_private * priv,struct sk_buff * skb)1773b01e57SJeff Chen static int nxpwifi_11n_dispatch_amsdu_pkt(struct nxpwifi_private *priv,
1873b01e57SJeff Chen 					  struct sk_buff *skb)
1973b01e57SJeff Chen {
2073b01e57SJeff Chen 	struct rxpd *local_rx_pd = (struct rxpd *)(skb->data);
2173b01e57SJeff Chen 	int ret;
2273b01e57SJeff Chen 
2373b01e57SJeff Chen 	if (le16_to_cpu(local_rx_pd->rx_pkt_type) == PKT_TYPE_AMSDU) {
2473b01e57SJeff Chen 		struct sk_buff_head list;
2573b01e57SJeff Chen 		struct sk_buff *rx_skb;
2673b01e57SJeff Chen 
2773b01e57SJeff Chen 		__skb_queue_head_init(&list);
2873b01e57SJeff Chen 
2973b01e57SJeff Chen 		skb_pull(skb, le16_to_cpu(local_rx_pd->rx_pkt_offset));
3073b01e57SJeff Chen 		skb_trim(skb, le16_to_cpu(local_rx_pd->rx_pkt_length));
3173b01e57SJeff Chen 
3273b01e57SJeff Chen 		ieee80211_amsdu_to_8023s(skb, &list, priv->curr_addr,
3373b01e57SJeff Chen 					 priv->wdev.iftype, 0, NULL, NULL, false);
3473b01e57SJeff Chen 
3573b01e57SJeff Chen 		while (!skb_queue_empty(&list)) {
3673b01e57SJeff Chen 			rx_skb = __skb_dequeue(&list);
3773b01e57SJeff Chen 
3873b01e57SJeff Chen 			if (priv->bss_role == NXPWIFI_BSS_ROLE_UAP)
3973b01e57SJeff Chen 				ret = nxpwifi_uap_recv_packet(priv, rx_skb);
4073b01e57SJeff Chen 			else
4173b01e57SJeff Chen 				ret = nxpwifi_recv_packet(priv, rx_skb);
4273b01e57SJeff Chen 			if (ret)
4373b01e57SJeff Chen 				nxpwifi_dbg(priv->adapter, ERROR,
4473b01e57SJeff Chen 					    "Rx of A-MSDU failed");
4573b01e57SJeff Chen 		}
4673b01e57SJeff Chen 		return 0;
4773b01e57SJeff Chen 	}
4873b01e57SJeff Chen 
4973b01e57SJeff Chen 	return -EINVAL;
5073b01e57SJeff Chen }
5173b01e57SJeff Chen 
5273b01e57SJeff Chen /* Process RX packet and forward to stack. */
nxpwifi_11n_dispatch_pkt(struct nxpwifi_private * priv,struct sk_buff * payload)5373b01e57SJeff Chen static int nxpwifi_11n_dispatch_pkt(struct nxpwifi_private *priv,
5473b01e57SJeff Chen 				    struct sk_buff *payload)
5573b01e57SJeff Chen {
5673b01e57SJeff Chen 	int ret;
5773b01e57SJeff Chen 
5873b01e57SJeff Chen 	if (!payload) {
5973b01e57SJeff Chen 		nxpwifi_dbg(priv->adapter, INFO, "info: fw drop data\n");
6073b01e57SJeff Chen 		return 0;
6173b01e57SJeff Chen 	}
6273b01e57SJeff Chen 
6373b01e57SJeff Chen 	ret = nxpwifi_11n_dispatch_amsdu_pkt(priv, payload);
6473b01e57SJeff Chen 	if (!ret)
6573b01e57SJeff Chen 		return 0;
6673b01e57SJeff Chen 
6773b01e57SJeff Chen 	if (priv->bss_role == NXPWIFI_BSS_ROLE_UAP)
6873b01e57SJeff Chen 		return nxpwifi_handle_uap_rx_forward(priv, payload);
6973b01e57SJeff Chen 
7073b01e57SJeff Chen 	return nxpwifi_process_rx_packet(priv, payload);
7173b01e57SJeff Chen }
7273b01e57SJeff Chen 
7373b01e57SJeff Chen /* Dispatch packets up to start_win. */
7473b01e57SJeff Chen static void
nxpwifi_11n_dispatch_pkt_until_start_win(struct nxpwifi_private * priv,struct nxpwifi_rx_reorder_tbl * tbl,int start_win)7573b01e57SJeff Chen nxpwifi_11n_dispatch_pkt_until_start_win(struct nxpwifi_private *priv,
7673b01e57SJeff Chen 					 struct nxpwifi_rx_reorder_tbl *tbl,
7773b01e57SJeff Chen 					 int start_win)
7873b01e57SJeff Chen {
7973b01e57SJeff Chen 	struct sk_buff_head list;
8073b01e57SJeff Chen 	struct sk_buff *skb;
8173b01e57SJeff Chen 	int pkt_to_send, i, tid;
8273b01e57SJeff Chen 
8373b01e57SJeff Chen 	tid = tbl->tid;
8473b01e57SJeff Chen 	__skb_queue_head_init(&list);
8573b01e57SJeff Chen 	spin_lock_bh(&priv->rx_reorder_tbl_lock[tid]);
8673b01e57SJeff Chen 
8773b01e57SJeff Chen 	pkt_to_send = (start_win > tbl->start_win) ?
8873b01e57SJeff Chen 		      min((start_win - tbl->start_win), tbl->win_size) :
8973b01e57SJeff Chen 		      tbl->win_size;
9073b01e57SJeff Chen 
9173b01e57SJeff Chen 	for (i = 0; i < pkt_to_send; ++i) {
9273b01e57SJeff Chen 		if (tbl->rx_reorder_ptr[i]) {
9373b01e57SJeff Chen 			skb = tbl->rx_reorder_ptr[i];
9473b01e57SJeff Chen 			__skb_queue_tail(&list, skb);
9573b01e57SJeff Chen 			tbl->rx_reorder_ptr[i] = NULL;
9673b01e57SJeff Chen 		}
9773b01e57SJeff Chen 	}
9873b01e57SJeff Chen 
9973b01e57SJeff Chen 	/* Simulate circular buffer via rotation. */
10073b01e57SJeff Chen 	for (i = 0; i < tbl->win_size - pkt_to_send; ++i) {
10173b01e57SJeff Chen 		tbl->rx_reorder_ptr[i] = tbl->rx_reorder_ptr[pkt_to_send + i];
10273b01e57SJeff Chen 		tbl->rx_reorder_ptr[pkt_to_send + i] = NULL;
10373b01e57SJeff Chen 	}
10473b01e57SJeff Chen 
10573b01e57SJeff Chen 	tbl->start_win = start_win;
10673b01e57SJeff Chen 	spin_unlock_bh(&priv->rx_reorder_tbl_lock[tid]);
10773b01e57SJeff Chen 
10873b01e57SJeff Chen 	while ((skb = __skb_dequeue(&list)))
10973b01e57SJeff Chen 		nxpwifi_11n_dispatch_pkt(priv, skb);
11073b01e57SJeff Chen }
11173b01e57SJeff Chen 
11273b01e57SJeff Chen /* Dispatch packets until a hole is found. */
11373b01e57SJeff Chen static void
nxpwifi_11n_scan_and_dispatch(struct nxpwifi_private * priv,struct nxpwifi_rx_reorder_tbl * tbl)11473b01e57SJeff Chen nxpwifi_11n_scan_and_dispatch(struct nxpwifi_private *priv,
11573b01e57SJeff Chen 			      struct nxpwifi_rx_reorder_tbl *tbl)
11673b01e57SJeff Chen {
11773b01e57SJeff Chen 	struct sk_buff_head list;
11873b01e57SJeff Chen 	struct sk_buff *skb;
11973b01e57SJeff Chen 	int i, j, xchg, tid;
12073b01e57SJeff Chen 
12173b01e57SJeff Chen 	tid = tbl->tid;
12273b01e57SJeff Chen 	__skb_queue_head_init(&list);
12373b01e57SJeff Chen 	spin_lock_bh(&priv->rx_reorder_tbl_lock[tid]);
12473b01e57SJeff Chen 
12573b01e57SJeff Chen 	for (i = 0; i < tbl->win_size; ++i) {
12673b01e57SJeff Chen 		if (!tbl->rx_reorder_ptr[i])
12773b01e57SJeff Chen 			break;
12873b01e57SJeff Chen 		skb = tbl->rx_reorder_ptr[i];
12973b01e57SJeff Chen 		__skb_queue_tail(&list, skb);
13073b01e57SJeff Chen 		tbl->rx_reorder_ptr[i] = NULL;
13173b01e57SJeff Chen 	}
13273b01e57SJeff Chen 
13373b01e57SJeff Chen 	/* Simulate circular buffer via rotation. */
13473b01e57SJeff Chen 	if (i > 0) {
13573b01e57SJeff Chen 		xchg = tbl->win_size - i;
13673b01e57SJeff Chen 		for (j = 0; j < xchg; ++j) {
13773b01e57SJeff Chen 			tbl->rx_reorder_ptr[j] = tbl->rx_reorder_ptr[i + j];
13873b01e57SJeff Chen 			tbl->rx_reorder_ptr[i + j] = NULL;
13973b01e57SJeff Chen 		}
14073b01e57SJeff Chen 	}
14173b01e57SJeff Chen 	tbl->start_win = (tbl->start_win + i) & (MAX_TID_VALUE - 1);
14273b01e57SJeff Chen 
14373b01e57SJeff Chen 	spin_unlock_bh(&priv->rx_reorder_tbl_lock[tid]);
14473b01e57SJeff Chen 
14573b01e57SJeff Chen 	while ((skb = __skb_dequeue(&list)))
14673b01e57SJeff Chen 		nxpwifi_11n_dispatch_pkt(priv, skb);
14773b01e57SJeff Chen }
14873b01e57SJeff Chen 
14973b01e57SJeff Chen /* Delete RX reorder entry and flush pending packets. */
15073b01e57SJeff Chen static void
nxpwifi_del_rx_reorder_entry(struct nxpwifi_private * priv,struct nxpwifi_rx_reorder_tbl * tbl)15173b01e57SJeff Chen nxpwifi_del_rx_reorder_entry(struct nxpwifi_private *priv,
15273b01e57SJeff Chen 			     struct nxpwifi_rx_reorder_tbl *tbl)
15373b01e57SJeff Chen {
15473b01e57SJeff Chen 	int start_win, tid;
15573b01e57SJeff Chen 
15673b01e57SJeff Chen 	if (!tbl)
15773b01e57SJeff Chen 		return;
15873b01e57SJeff Chen 
15973b01e57SJeff Chen 	tid = tbl->tid;
16073b01e57SJeff Chen 
16173b01e57SJeff Chen 	atomic_set(&priv->adapter->rx_ba_teardown_pending, 1);
16273b01e57SJeff Chen 	flush_workqueue(priv->adapter->rx_workqueue);
16373b01e57SJeff Chen 
16473b01e57SJeff Chen 	start_win = (tbl->start_win + tbl->win_size) & (MAX_TID_VALUE - 1);
16573b01e57SJeff Chen 	nxpwifi_11n_dispatch_pkt_until_start_win(priv, tbl, start_win);
16673b01e57SJeff Chen 
16773b01e57SJeff Chen 	timer_delete_sync(&tbl->timer_context.timer);
16873b01e57SJeff Chen 	tbl->timer_context.timer_is_set = false;
16973b01e57SJeff Chen 
17073b01e57SJeff Chen 	spin_lock_bh(&priv->rx_reorder_tbl_lock[tid]);
17173b01e57SJeff Chen 	list_del_rcu(&tbl->list);
17273b01e57SJeff Chen 	spin_unlock_bh(&priv->rx_reorder_tbl_lock[tid]);
17373b01e57SJeff Chen 
17473b01e57SJeff Chen 	kfree_rcu(tbl, rcu);
17573b01e57SJeff Chen 
17673b01e57SJeff Chen 	atomic_set(&priv->adapter->rx_ba_teardown_pending, 0);
17773b01e57SJeff Chen }
17873b01e57SJeff Chen 
17973b01e57SJeff Chen /* Lookup RX reorder entry by TID/TA. */
18073b01e57SJeff Chen struct nxpwifi_rx_reorder_tbl *
nxpwifi_11n_get_rx_reorder_tbl(struct nxpwifi_private * priv,int tid,u8 * ta)18173b01e57SJeff Chen nxpwifi_11n_get_rx_reorder_tbl(struct nxpwifi_private *priv, int tid, u8 *ta)
18273b01e57SJeff Chen {
18373b01e57SJeff Chen 	struct nxpwifi_rx_reorder_tbl *tbl, *found = NULL;
18473b01e57SJeff Chen 
18573b01e57SJeff Chen 	guard(rcu)();
18673b01e57SJeff Chen 
18773b01e57SJeff Chen 	list_for_each_entry_rcu(tbl, &priv->rx_reorder_tbl_ptr[tid], list) {
18873b01e57SJeff Chen 		if (!memcmp(tbl->ta, ta, ETH_ALEN) && tbl->tid == tid) {
18973b01e57SJeff Chen 			found = tbl;
19073b01e57SJeff Chen 			break;
19173b01e57SJeff Chen 		}
19273b01e57SJeff Chen 	}
19373b01e57SJeff Chen 
19473b01e57SJeff Chen 	return found;
19573b01e57SJeff Chen }
19673b01e57SJeff Chen 
19773b01e57SJeff Chen /* Delete RX reorder entries by TA. */
nxpwifi_11n_del_rx_reorder_tbl_by_ta(struct nxpwifi_private * priv,u8 * ta)19873b01e57SJeff Chen void nxpwifi_11n_del_rx_reorder_tbl_by_ta(struct nxpwifi_private *priv, u8 *ta)
19973b01e57SJeff Chen {
20073b01e57SJeff Chen 	struct nxpwifi_rx_reorder_tbl *tbl, *tmp;
20173b01e57SJeff Chen 	LIST_HEAD(to_delete);
20273b01e57SJeff Chen 	int i;
20373b01e57SJeff Chen 
20473b01e57SJeff Chen 	if (!ta)
20573b01e57SJeff Chen 		return;
20673b01e57SJeff Chen 
20773b01e57SJeff Chen 	for (i = 0; i < MAX_NUM_TID; i++) {
20873b01e57SJeff Chen 		guard(rcu)();
20973b01e57SJeff Chen 		list_for_each_entry_rcu(tbl, &priv->rx_reorder_tbl_ptr[i], list) {
21073b01e57SJeff Chen 			if (!memcmp(tbl->ta, ta, ETH_ALEN)) {
21173b01e57SJeff Chen 				INIT_LIST_HEAD(&tbl->tmp_list);
21273b01e57SJeff Chen 				list_add_tail(&tbl->tmp_list, &to_delete);
21373b01e57SJeff Chen 			}
21473b01e57SJeff Chen 		}
21573b01e57SJeff Chen 
21673b01e57SJeff Chen 		list_for_each_entry_safe(tbl, tmp, &to_delete, tmp_list)
21773b01e57SJeff Chen 			nxpwifi_del_rx_reorder_entry(priv, tbl);
21873b01e57SJeff Chen 
21973b01e57SJeff Chen 		INIT_LIST_HEAD(&to_delete);
22073b01e57SJeff Chen 	}
22173b01e57SJeff Chen }
22273b01e57SJeff Chen 
22373b01e57SJeff Chen /* Find last buffered sequence index. */
22473b01e57SJeff Chen static int
nxpwifi_11n_find_last_seq_num(struct reorder_tmr_cnxt * ctx)22573b01e57SJeff Chen nxpwifi_11n_find_last_seq_num(struct reorder_tmr_cnxt *ctx)
22673b01e57SJeff Chen {
22773b01e57SJeff Chen 	struct nxpwifi_rx_reorder_tbl *rx_reorder_tbl_ptr = ctx->ptr;
22873b01e57SJeff Chen 	int i;
22973b01e57SJeff Chen 
23073b01e57SJeff Chen 	guard(rcu)();
23173b01e57SJeff Chen 	for (i = rx_reorder_tbl_ptr->win_size - 1; i >= 0; --i) {
23273b01e57SJeff Chen 		if (rx_reorder_tbl_ptr->rx_reorder_ptr[i])
23373b01e57SJeff Chen 			return i;
23473b01e57SJeff Chen 	}
23573b01e57SJeff Chen 
23673b01e57SJeff Chen 	return -EINVAL;
23773b01e57SJeff Chen }
23873b01e57SJeff Chen 
23973b01e57SJeff Chen /* Flush and dispatch buffered packets on timer. */
24073b01e57SJeff Chen static void
nxpwifi_flush_data(struct timer_list * t)24173b01e57SJeff Chen nxpwifi_flush_data(struct timer_list *t)
24273b01e57SJeff Chen {
24373b01e57SJeff Chen 	struct reorder_tmr_cnxt *ctx =
24473b01e57SJeff Chen 		timer_container_of(ctx, t, timer);
24573b01e57SJeff Chen 	int start_win, seq_num;
24673b01e57SJeff Chen 
24773b01e57SJeff Chen 	ctx->timer_is_set = false;
24873b01e57SJeff Chen 	seq_num = nxpwifi_11n_find_last_seq_num(ctx);
24973b01e57SJeff Chen 
25073b01e57SJeff Chen 	if (seq_num < 0)
25173b01e57SJeff Chen 		return;
25273b01e57SJeff Chen 
25373b01e57SJeff Chen 	nxpwifi_dbg(ctx->priv->adapter, INFO, "info: flush data %d\n", seq_num);
25473b01e57SJeff Chen 	start_win = (ctx->ptr->start_win + seq_num + 1) & (MAX_TID_VALUE - 1);
25573b01e57SJeff Chen 	nxpwifi_11n_dispatch_pkt_until_start_win(ctx->priv, ctx->ptr,
25673b01e57SJeff Chen 						 start_win);
25773b01e57SJeff Chen }
25873b01e57SJeff Chen 
25973b01e57SJeff Chen /* Create RX reorder entry (TID/TA, SSN, winsize, timer). */
26073b01e57SJeff Chen static void
nxpwifi_11n_create_rx_reorder_tbl(struct nxpwifi_private * priv,u8 * ta,int tid,int win_size,int seq_num)26173b01e57SJeff Chen nxpwifi_11n_create_rx_reorder_tbl(struct nxpwifi_private *priv, u8 *ta,
26273b01e57SJeff Chen 				  int tid, int win_size, int seq_num)
26373b01e57SJeff Chen {
26473b01e57SJeff Chen 	struct nxpwifi_rx_reorder_tbl *tbl, *new_node;
26573b01e57SJeff Chen 	u16 last_seq = 0;
26673b01e57SJeff Chen 	struct nxpwifi_sta_node *node;
26773b01e57SJeff Chen 
26873b01e57SJeff Chen 	/* Existing TID/TA: flush and move window to SSN. */
26973b01e57SJeff Chen 	tbl = nxpwifi_11n_get_rx_reorder_tbl(priv, tid, ta);
27073b01e57SJeff Chen 	if (tbl) {
27173b01e57SJeff Chen 		nxpwifi_11n_dispatch_pkt_until_start_win(priv, tbl, seq_num);
27273b01e57SJeff Chen 		return;
27373b01e57SJeff Chen 	}
274*cc3e7fdbSRosen Penev 
275*cc3e7fdbSRosen Penev 	if (win_size <= 0)
276*cc3e7fdbSRosen Penev 		return;
277*cc3e7fdbSRosen Penev 
27873b01e57SJeff Chen 	/* if !tbl then create one */
279*cc3e7fdbSRosen Penev 	new_node = kzalloc_flex(*new_node, rx_reorder_ptr, win_size);
28073b01e57SJeff Chen 	if (!new_node)
28173b01e57SJeff Chen 		return;
28273b01e57SJeff Chen 
283*cc3e7fdbSRosen Penev 	new_node->win_size = win_size;
28473b01e57SJeff Chen 	INIT_LIST_HEAD(&new_node->list);
28573b01e57SJeff Chen 	new_node->tid = tid;
28673b01e57SJeff Chen 	memcpy(new_node->ta, ta, ETH_ALEN);
28773b01e57SJeff Chen 	new_node->start_win = seq_num;
28873b01e57SJeff Chen 	new_node->init_win = seq_num;
28973b01e57SJeff Chen 	new_node->flags = 0;
29073b01e57SJeff Chen 
29173b01e57SJeff Chen 	if (nxpwifi_queuing_ra_based(priv)) {
29273b01e57SJeff Chen 		if (priv->bss_role == NXPWIFI_BSS_ROLE_UAP) {
29373b01e57SJeff Chen 			guard(rcu)();
29473b01e57SJeff Chen 			node = nxpwifi_get_sta_entry(priv, ta);
29573b01e57SJeff Chen 			if (node)
29673b01e57SJeff Chen 				last_seq = node->rx_seq[tid];
29773b01e57SJeff Chen 		}
29873b01e57SJeff Chen 	} else {
29973b01e57SJeff Chen 		guard(rcu)();
30073b01e57SJeff Chen 		node = nxpwifi_get_sta_entry(priv, ta);
30173b01e57SJeff Chen 		if (node)
30273b01e57SJeff Chen 			last_seq = node->rx_seq[tid];
30373b01e57SJeff Chen 		else
30473b01e57SJeff Chen 			last_seq = priv->rx_seq[tid];
30573b01e57SJeff Chen 	}
30673b01e57SJeff Chen 
30773b01e57SJeff Chen 	nxpwifi_dbg(priv->adapter, INFO,
30873b01e57SJeff Chen 		    "info: last_seq=%d start_win=%d\n",
30973b01e57SJeff Chen 		    last_seq, new_node->start_win);
31073b01e57SJeff Chen 
31173b01e57SJeff Chen 	if (last_seq != NXPWIFI_DEF_11N_RX_SEQ_NUM &&
31273b01e57SJeff Chen 	    last_seq >= new_node->start_win) {
31373b01e57SJeff Chen 		new_node->start_win = last_seq + 1;
31473b01e57SJeff Chen 		new_node->flags |= RXREOR_INIT_WINDOW_SHIFT;
31573b01e57SJeff Chen 	}
31673b01e57SJeff Chen 
31773b01e57SJeff Chen 	new_node->timer_context.ptr = new_node;
31873b01e57SJeff Chen 	new_node->timer_context.priv = priv;
31973b01e57SJeff Chen 	new_node->timer_context.timer_is_set = false;
32073b01e57SJeff Chen 
32173b01e57SJeff Chen 	timer_setup(&new_node->timer_context.timer, nxpwifi_flush_data, 0);
32273b01e57SJeff Chen 
32373b01e57SJeff Chen 	spin_lock_bh(&priv->rx_reorder_tbl_lock[tid]);
32473b01e57SJeff Chen 	list_add_tail_rcu(&new_node->list, &priv->rx_reorder_tbl_ptr[tid]);
32573b01e57SJeff Chen 	spin_unlock_bh(&priv->rx_reorder_tbl_lock[tid]);
32673b01e57SJeff Chen }
32773b01e57SJeff Chen 
32873b01e57SJeff Chen static void
nxpwifi_11n_rxreorder_timer_restart(struct nxpwifi_rx_reorder_tbl * tbl)32973b01e57SJeff Chen nxpwifi_11n_rxreorder_timer_restart(struct nxpwifi_rx_reorder_tbl *tbl)
33073b01e57SJeff Chen {
33173b01e57SJeff Chen 	u32 min_flush_time;
33273b01e57SJeff Chen 
33373b01e57SJeff Chen 	if (tbl->win_size >= NXPWIFI_BA_WIN_SIZE_32)
33473b01e57SJeff Chen 		min_flush_time = MIN_FLUSH_TIMER_15_MS;
33573b01e57SJeff Chen 	else
33673b01e57SJeff Chen 		min_flush_time = MIN_FLUSH_TIMER_MS;
33773b01e57SJeff Chen 
33873b01e57SJeff Chen 	mod_timer(&tbl->timer_context.timer,
33973b01e57SJeff Chen 		  jiffies + msecs_to_jiffies(min_flush_time * tbl->win_size));
34073b01e57SJeff Chen 
34173b01e57SJeff Chen 	tbl->timer_context.timer_is_set = true;
34273b01e57SJeff Chen }
34373b01e57SJeff Chen 
34473b01e57SJeff Chen /* Prepare ADDBA request. */
nxpwifi_cmd_11n_addba_req(struct host_cmd_ds_command * cmd,void * data_buf)34573b01e57SJeff Chen int nxpwifi_cmd_11n_addba_req(struct host_cmd_ds_command *cmd, void *data_buf)
34673b01e57SJeff Chen {
34773b01e57SJeff Chen 	struct host_cmd_ds_11n_addba_req *add_ba_req = &cmd->params.add_ba_req;
34873b01e57SJeff Chen 
34973b01e57SJeff Chen 	cmd->command = cpu_to_le16(HOST_CMD_11N_ADDBA_REQ);
35073b01e57SJeff Chen 	cmd->size = cpu_to_le16(sizeof(*add_ba_req) + S_DS_GEN);
35173b01e57SJeff Chen 	memcpy(add_ba_req, data_buf, sizeof(*add_ba_req));
35273b01e57SJeff Chen 
35373b01e57SJeff Chen 	return 0;
35473b01e57SJeff Chen }
35573b01e57SJeff Chen 
35673b01e57SJeff Chen /* Prepare ADDBA response and create RX reorder table. */
nxpwifi_cmd_11n_addba_rsp_gen(struct nxpwifi_private * priv,struct host_cmd_ds_command * cmd,struct host_cmd_ds_11n_addba_req * cmd_addba_req)35773b01e57SJeff Chen int nxpwifi_cmd_11n_addba_rsp_gen(struct nxpwifi_private *priv,
35873b01e57SJeff Chen 				  struct host_cmd_ds_command *cmd,
35973b01e57SJeff Chen 				  struct host_cmd_ds_11n_addba_req
36073b01e57SJeff Chen 				  *cmd_addba_req)
36173b01e57SJeff Chen {
36273b01e57SJeff Chen 	struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = &cmd->params.add_ba_rsp;
36373b01e57SJeff Chen 	u32 rx_win_size = priv->add_ba_param.rx_win_size;
36473b01e57SJeff Chen 	u8 tid;
36573b01e57SJeff Chen 	int win_size;
36673b01e57SJeff Chen 	u16 block_ack_param_set;
36773b01e57SJeff Chen 
36873b01e57SJeff Chen 	cmd->command = cpu_to_le16(HOST_CMD_11N_ADDBA_RSP);
36973b01e57SJeff Chen 	cmd->size = cpu_to_le16(sizeof(*add_ba_rsp) + S_DS_GEN);
37073b01e57SJeff Chen 
37173b01e57SJeff Chen 	memcpy(add_ba_rsp->peer_mac_addr, cmd_addba_req->peer_mac_addr,
37273b01e57SJeff Chen 	       ETH_ALEN);
37373b01e57SJeff Chen 	add_ba_rsp->dialog_token = cmd_addba_req->dialog_token;
37473b01e57SJeff Chen 	add_ba_rsp->block_ack_tmo = cmd_addba_req->block_ack_tmo;
37573b01e57SJeff Chen 	add_ba_rsp->ssn = cmd_addba_req->ssn;
37673b01e57SJeff Chen 
37773b01e57SJeff Chen 	block_ack_param_set = le16_to_cpu(cmd_addba_req->block_ack_param_set);
37873b01e57SJeff Chen 	tid = (block_ack_param_set & IEEE80211_ADDBA_PARAM_TID_MASK)
37973b01e57SJeff Chen 		>> BLOCKACKPARAM_TID_POS;
38073b01e57SJeff Chen 	add_ba_rsp->status_code = cpu_to_le16(ADDBA_RSP_STATUS_ACCEPT);
38173b01e57SJeff Chen 	block_ack_param_set &= ~IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK;
38273b01e57SJeff Chen 
38373b01e57SJeff Chen 	/* If we don't support AMSDU inside AMPDU, reset the bit */
38473b01e57SJeff Chen 	if (!priv->add_ba_param.rx_amsdu ||
38573b01e57SJeff Chen 	    priv->aggr_prio_tbl[tid].amsdu == BA_STREAM_NOT_ALLOWED)
38673b01e57SJeff Chen 		block_ack_param_set &= ~IEEE80211_ADDBA_PARAM_AMSDU_MASK;
38773b01e57SJeff Chen 	block_ack_param_set |= rx_win_size << BLOCKACKPARAM_WINSIZE_POS;
38873b01e57SJeff Chen 	add_ba_rsp->block_ack_param_set = cpu_to_le16(block_ack_param_set);
38973b01e57SJeff Chen 	win_size = (le16_to_cpu(add_ba_rsp->block_ack_param_set)
39073b01e57SJeff Chen 		    & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK)
39173b01e57SJeff Chen 		   >> BLOCKACKPARAM_WINSIZE_POS;
39273b01e57SJeff Chen 	cmd_addba_req->block_ack_param_set = cpu_to_le16(block_ack_param_set);
39373b01e57SJeff Chen 
39473b01e57SJeff Chen 	nxpwifi_11n_create_rx_reorder_tbl(priv, cmd_addba_req->peer_mac_addr,
39573b01e57SJeff Chen 					  tid, win_size,
39673b01e57SJeff Chen 					  le16_to_cpu(cmd_addba_req->ssn));
39773b01e57SJeff Chen 	return 0;
39873b01e57SJeff Chen }
39973b01e57SJeff Chen 
40073b01e57SJeff Chen /* Prepare DELBA command. */
nxpwifi_cmd_11n_delba(struct host_cmd_ds_command * cmd,void * data_buf)40173b01e57SJeff Chen int nxpwifi_cmd_11n_delba(struct host_cmd_ds_command *cmd, void *data_buf)
40273b01e57SJeff Chen {
40373b01e57SJeff Chen 	struct host_cmd_ds_11n_delba *del_ba = &cmd->params.del_ba;
40473b01e57SJeff Chen 
40573b01e57SJeff Chen 	cmd->command = cpu_to_le16(HOST_CMD_11N_DELBA);
40673b01e57SJeff Chen 	cmd->size = cpu_to_le16(sizeof(*del_ba) + S_DS_GEN);
40773b01e57SJeff Chen 	memcpy(del_ba, data_buf, sizeof(*del_ba));
40873b01e57SJeff Chen 
40973b01e57SJeff Chen 	return 0;
41073b01e57SJeff Chen }
41173b01e57SJeff Chen 
41273b01e57SJeff Chen /* Decide and perform RX reordering for a packet. */
nxpwifi_11n_rx_reorder_pkt(struct nxpwifi_private * priv,u16 seq_num,u16 tid,u8 * ta,u8 pkt_type,void * payload)41373b01e57SJeff Chen int nxpwifi_11n_rx_reorder_pkt(struct nxpwifi_private *priv,
41473b01e57SJeff Chen 			       u16 seq_num, u16 tid,
41573b01e57SJeff Chen 			       u8 *ta, u8 pkt_type, void *payload)
41673b01e57SJeff Chen {
41773b01e57SJeff Chen 	struct nxpwifi_rx_reorder_tbl *tbl;
41873b01e57SJeff Chen 	int prev_start_win, start_win, end_win, win_size;
41973b01e57SJeff Chen 	u16 pkt_index;
42073b01e57SJeff Chen 	bool init_window_shift = false;
42173b01e57SJeff Chen 	int ret = 0;
42273b01e57SJeff Chen 
42373b01e57SJeff Chen 	tbl = nxpwifi_11n_get_rx_reorder_tbl(priv, tid, ta);
42473b01e57SJeff Chen 	if (!tbl) {
42573b01e57SJeff Chen 		if (pkt_type != PKT_TYPE_BAR)
42673b01e57SJeff Chen 			nxpwifi_11n_dispatch_pkt(priv, payload);
42773b01e57SJeff Chen 		return ret;
42873b01e57SJeff Chen 	}
42973b01e57SJeff Chen 
43073b01e57SJeff Chen 	if (pkt_type == PKT_TYPE_AMSDU && !tbl->amsdu) {
43173b01e57SJeff Chen 		nxpwifi_11n_dispatch_pkt(priv, payload);
43273b01e57SJeff Chen 		return ret;
43373b01e57SJeff Chen 	}
43473b01e57SJeff Chen 
43573b01e57SJeff Chen 	start_win = tbl->start_win;
43673b01e57SJeff Chen 	prev_start_win = start_win;
43773b01e57SJeff Chen 	win_size = tbl->win_size;
43873b01e57SJeff Chen 	end_win = ((start_win + win_size) - 1) & (MAX_TID_VALUE - 1);
43973b01e57SJeff Chen 	if (tbl->flags & RXREOR_INIT_WINDOW_SHIFT) {
44073b01e57SJeff Chen 		init_window_shift = true;
44173b01e57SJeff Chen 		tbl->flags &= ~RXREOR_INIT_WINDOW_SHIFT;
44273b01e57SJeff Chen 	}
44373b01e57SJeff Chen 
44473b01e57SJeff Chen 	if (tbl->flags & RXREOR_FORCE_NO_DROP) {
44573b01e57SJeff Chen 		nxpwifi_dbg(priv->adapter, INFO,
44673b01e57SJeff Chen 			    "RXREOR_FORCE_NO_DROP when HS is activated\n");
44773b01e57SJeff Chen 		tbl->flags &= ~RXREOR_FORCE_NO_DROP;
44873b01e57SJeff Chen 	} else if (init_window_shift && seq_num < start_win &&
44973b01e57SJeff Chen 		   seq_num >= tbl->init_win) {
45073b01e57SJeff Chen 		nxpwifi_dbg(priv->adapter, INFO,
45173b01e57SJeff Chen 			    "Sender TID sequence number reset %d->%d for SSN %d\n",
45273b01e57SJeff Chen 			    start_win, seq_num, tbl->init_win);
45373b01e57SJeff Chen 		start_win = seq_num;
45473b01e57SJeff Chen 		tbl->start_win = start_win;
45573b01e57SJeff Chen 		end_win = ((start_win + win_size) - 1) & (MAX_TID_VALUE - 1);
45673b01e57SJeff Chen 	} else {
45773b01e57SJeff Chen 		/* Drop packet if seq_num < start_win. */
45873b01e57SJeff Chen 		if ((start_win + TWOPOW11) > (MAX_TID_VALUE - 1)) {
45973b01e57SJeff Chen 			if (seq_num >= ((start_win + TWOPOW11) &
46073b01e57SJeff Chen 					(MAX_TID_VALUE - 1)) &&
46173b01e57SJeff Chen 			    seq_num < start_win) {
46273b01e57SJeff Chen 				ret = -EINVAL;
46373b01e57SJeff Chen 				goto done;
46473b01e57SJeff Chen 			}
46573b01e57SJeff Chen 		} else if ((seq_num < start_win) ||
46673b01e57SJeff Chen 			   (seq_num >= (start_win + TWOPOW11))) {
46773b01e57SJeff Chen 			ret = -EINVAL;
46873b01e57SJeff Chen 			goto done;
46973b01e57SJeff Chen 		}
47073b01e57SJeff Chen 	}
47173b01e57SJeff Chen 
47273b01e57SJeff Chen 	/* Adjust seq_num for BAR (WinStart = seq_num). */
47373b01e57SJeff Chen 	if (pkt_type == PKT_TYPE_BAR)
47473b01e57SJeff Chen 		seq_num = ((seq_num + win_size) - 1) & (MAX_TID_VALUE - 1);
47573b01e57SJeff Chen 
47673b01e57SJeff Chen 	if ((end_win < start_win &&
47773b01e57SJeff Chen 	     seq_num < start_win && seq_num > end_win) ||
47873b01e57SJeff Chen 	    (end_win > start_win && (seq_num > end_win ||
47973b01e57SJeff Chen 				     seq_num < start_win))) {
48073b01e57SJeff Chen 		end_win = seq_num;
48173b01e57SJeff Chen 		if (((end_win - win_size) + 1) >= 0)
48273b01e57SJeff Chen 			start_win = (end_win - win_size) + 1;
48373b01e57SJeff Chen 		else
48473b01e57SJeff Chen 			start_win = (MAX_TID_VALUE - (win_size - end_win)) + 1;
48573b01e57SJeff Chen 		nxpwifi_11n_dispatch_pkt_until_start_win(priv, tbl, start_win);
48673b01e57SJeff Chen 	}
48773b01e57SJeff Chen 
48873b01e57SJeff Chen 	if (pkt_type != PKT_TYPE_BAR) {
48973b01e57SJeff Chen 		if (seq_num >= start_win)
49073b01e57SJeff Chen 			pkt_index = seq_num - start_win;
49173b01e57SJeff Chen 		else
49273b01e57SJeff Chen 			pkt_index = (seq_num + MAX_TID_VALUE) - start_win;
49373b01e57SJeff Chen 
49473b01e57SJeff Chen 		if (tbl->rx_reorder_ptr[pkt_index]) {
49573b01e57SJeff Chen 			ret = -EINVAL;
49673b01e57SJeff Chen 			goto done;
49773b01e57SJeff Chen 		}
49873b01e57SJeff Chen 
49973b01e57SJeff Chen 		tbl->rx_reorder_ptr[pkt_index] = payload;
50073b01e57SJeff Chen 	}
50173b01e57SJeff Chen 
50273b01e57SJeff Chen 	/* Dispatch sequentially until a hole; update start_win. */
50373b01e57SJeff Chen 	nxpwifi_11n_scan_and_dispatch(priv, tbl);
50473b01e57SJeff Chen 
50573b01e57SJeff Chen done:
50673b01e57SJeff Chen 	if (!tbl->timer_context.timer_is_set ||
50773b01e57SJeff Chen 	    prev_start_win != tbl->start_win)
50873b01e57SJeff Chen 		nxpwifi_11n_rxreorder_timer_restart(tbl);
50973b01e57SJeff Chen 	return ret;
51073b01e57SJeff Chen }
51173b01e57SJeff Chen 
51273b01e57SJeff Chen /* Delete BA entry for TID/TA. */
51373b01e57SJeff Chen void
nxpwifi_del_ba_tbl(struct nxpwifi_private * priv,int tid,u8 * peer_mac,u8 type,int initiator)51473b01e57SJeff Chen nxpwifi_del_ba_tbl(struct nxpwifi_private *priv, int tid, u8 *peer_mac,
51573b01e57SJeff Chen 		   u8 type, int initiator)
51673b01e57SJeff Chen {
51773b01e57SJeff Chen 	struct nxpwifi_rx_reorder_tbl *tbl;
51873b01e57SJeff Chen 	struct nxpwifi_tx_ba_stream_tbl *ptx_tbl;
51973b01e57SJeff Chen 	struct nxpwifi_ra_list_tbl *ra_list;
52073b01e57SJeff Chen 	u8 cleanup_rx_reorder_tbl;
52173b01e57SJeff Chen 	int tid_down;
52273b01e57SJeff Chen 
52373b01e57SJeff Chen 	if (type == TYPE_DELBA_RECEIVE)
52473b01e57SJeff Chen 		cleanup_rx_reorder_tbl = (initiator) ? true : false;
52573b01e57SJeff Chen 	else
52673b01e57SJeff Chen 		cleanup_rx_reorder_tbl = (initiator) ? false : true;
52773b01e57SJeff Chen 
52873b01e57SJeff Chen 	nxpwifi_dbg(priv->adapter, EVENT, "event: DELBA: %pM tid=%d initiator=%d\n",
52973b01e57SJeff Chen 		    peer_mac, tid, initiator);
53073b01e57SJeff Chen 
53173b01e57SJeff Chen 	if (cleanup_rx_reorder_tbl) {
53273b01e57SJeff Chen 		tbl = nxpwifi_11n_get_rx_reorder_tbl(priv, tid, peer_mac);
53373b01e57SJeff Chen 		if (!tbl) {
53473b01e57SJeff Chen 			nxpwifi_dbg(priv->adapter, EVENT,
53573b01e57SJeff Chen 				    "event: TID, TA not found in table\n");
53673b01e57SJeff Chen 			return;
53773b01e57SJeff Chen 		}
53873b01e57SJeff Chen 		nxpwifi_del_rx_reorder_entry(priv, tbl);
53973b01e57SJeff Chen 	} else {
54073b01e57SJeff Chen 		guard(rcu)();
54173b01e57SJeff Chen 		ptx_tbl = nxpwifi_get_ba_tbl(priv, tid, peer_mac);
54273b01e57SJeff Chen 
54373b01e57SJeff Chen 		if (!ptx_tbl) {
54473b01e57SJeff Chen 			nxpwifi_dbg(priv->adapter, EVENT,
54573b01e57SJeff Chen 				    "event: TID, RA not found in table\n");
54673b01e57SJeff Chen 			return;
54773b01e57SJeff Chen 		}
54873b01e57SJeff Chen 
54973b01e57SJeff Chen 		tid_down = nxpwifi_wmm_downgrade_tid(priv, tid);
55073b01e57SJeff Chen 		ra_list = nxpwifi_wmm_get_ralist_node(priv, tid_down, peer_mac);
55173b01e57SJeff Chen 		if (ra_list) {
55273b01e57SJeff Chen 			ra_list->amsdu_in_ampdu = false;
55373b01e57SJeff Chen 			ra_list->ba_status = BA_SETUP_NONE;
55473b01e57SJeff Chen 		}
55573b01e57SJeff Chen 		spin_lock_bh(&priv->tx_ba_stream_tbl_lock[tid]);
55673b01e57SJeff Chen 		nxpwifi_11n_delete_tx_ba_stream_tbl_entry(priv, ptx_tbl);
55773b01e57SJeff Chen 		spin_unlock_bh(&priv->tx_ba_stream_tbl_lock[tid]);
55873b01e57SJeff Chen 	}
55973b01e57SJeff Chen }
56073b01e57SJeff Chen 
56173b01e57SJeff Chen /* Handle ADDBA response. */
nxpwifi_ret_11n_addba_resp(struct nxpwifi_private * priv,struct host_cmd_ds_command * resp)56273b01e57SJeff Chen int nxpwifi_ret_11n_addba_resp(struct nxpwifi_private *priv,
56373b01e57SJeff Chen 			       struct host_cmd_ds_command *resp)
56473b01e57SJeff Chen {
56573b01e57SJeff Chen 	struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = &resp->params.add_ba_rsp;
56673b01e57SJeff Chen 	int tid, win_size;
56773b01e57SJeff Chen 	struct nxpwifi_rx_reorder_tbl *tbl;
56873b01e57SJeff Chen 	u16 block_ack_param_set;
56973b01e57SJeff Chen 
57073b01e57SJeff Chen 	block_ack_param_set = le16_to_cpu(add_ba_rsp->block_ack_param_set);
57173b01e57SJeff Chen 
57273b01e57SJeff Chen 	tid = (block_ack_param_set & IEEE80211_ADDBA_PARAM_TID_MASK)
57373b01e57SJeff Chen 		>> BLOCKACKPARAM_TID_POS;
57473b01e57SJeff Chen 	/* Check if we had rejected the ADDBA, if yes then do not create the stream */
57573b01e57SJeff Chen 	if (le16_to_cpu(add_ba_rsp->status_code) != BA_RESULT_SUCCESS) {
57673b01e57SJeff Chen 		nxpwifi_dbg(priv->adapter, ERROR, "ADDBA RSP: failed %pM tid=%d)\n",
57773b01e57SJeff Chen 			    add_ba_rsp->peer_mac_addr, tid);
57873b01e57SJeff Chen 
57973b01e57SJeff Chen 		tbl = nxpwifi_11n_get_rx_reorder_tbl(priv, tid,
58073b01e57SJeff Chen 						     add_ba_rsp->peer_mac_addr);
58173b01e57SJeff Chen 		if (tbl)
58273b01e57SJeff Chen 			nxpwifi_del_rx_reorder_entry(priv, tbl);
58373b01e57SJeff Chen 
58473b01e57SJeff Chen 		return 0;
58573b01e57SJeff Chen 	}
58673b01e57SJeff Chen 
58773b01e57SJeff Chen 	win_size = (block_ack_param_set & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK)
58873b01e57SJeff Chen 		    >> BLOCKACKPARAM_WINSIZE_POS;
58973b01e57SJeff Chen 
59073b01e57SJeff Chen 	tbl = nxpwifi_11n_get_rx_reorder_tbl(priv, tid,
59173b01e57SJeff Chen 					     add_ba_rsp->peer_mac_addr);
59273b01e57SJeff Chen 	if (tbl) {
59373b01e57SJeff Chen 		if ((block_ack_param_set & IEEE80211_ADDBA_PARAM_AMSDU_MASK) &&
59473b01e57SJeff Chen 		    priv->add_ba_param.rx_amsdu &&
59573b01e57SJeff Chen 		    priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED)
59673b01e57SJeff Chen 			tbl->amsdu = true;
59773b01e57SJeff Chen 		else
59873b01e57SJeff Chen 			tbl->amsdu = false;
59973b01e57SJeff Chen 	}
60073b01e57SJeff Chen 
60173b01e57SJeff Chen 	nxpwifi_dbg(priv->adapter, CMD,
60273b01e57SJeff Chen 		    "cmd: ADDBA RSP: %pM tid=%d ssn=%d win_size=%d\n",
60373b01e57SJeff Chen 		    add_ba_rsp->peer_mac_addr, tid, add_ba_rsp->ssn, win_size);
60473b01e57SJeff Chen 
60573b01e57SJeff Chen 	return 0;
60673b01e57SJeff Chen }
60773b01e57SJeff Chen 
60873b01e57SJeff Chen /* Handle BA stream timeout: send DELBA. */
nxpwifi_11n_ba_stream_timeout(struct nxpwifi_private * priv,struct host_cmd_ds_11n_batimeout * event)60973b01e57SJeff Chen void nxpwifi_11n_ba_stream_timeout(struct nxpwifi_private *priv,
61073b01e57SJeff Chen 				   struct host_cmd_ds_11n_batimeout *event)
61173b01e57SJeff Chen {
61273b01e57SJeff Chen 	struct host_cmd_ds_11n_delba delba;
61373b01e57SJeff Chen 
61473b01e57SJeff Chen 	memset(&delba, 0, sizeof(struct host_cmd_ds_11n_delba));
61573b01e57SJeff Chen 	memcpy(delba.peer_mac_addr, event->peer_mac_addr, ETH_ALEN);
61673b01e57SJeff Chen 
61773b01e57SJeff Chen 	delba.del_ba_param_set |=
61873b01e57SJeff Chen 		cpu_to_le16((u16)event->tid << DELBA_TID_POS);
61973b01e57SJeff Chen 	delba.del_ba_param_set |=
62073b01e57SJeff Chen 		cpu_to_le16((u16)event->origninator << DELBA_INITIATOR_POS);
62173b01e57SJeff Chen 	delba.reason_code = cpu_to_le16(WLAN_REASON_QSTA_TIMEOUT);
62273b01e57SJeff Chen 	nxpwifi_send_cmd(priv, HOST_CMD_11N_DELBA, 0, 0, &delba, false);
62373b01e57SJeff Chen }
62473b01e57SJeff Chen 
62573b01e57SJeff Chen /* Cleanup all RX reorder entries. */
nxpwifi_11n_cleanup_reorder_tbl(struct nxpwifi_private * priv)62673b01e57SJeff Chen void nxpwifi_11n_cleanup_reorder_tbl(struct nxpwifi_private *priv)
62773b01e57SJeff Chen {
62873b01e57SJeff Chen 	struct nxpwifi_rx_reorder_tbl *del_tbl_ptr, *tmp_node;
62973b01e57SJeff Chen 	LIST_HEAD(to_delete_list);
63073b01e57SJeff Chen 	int i;
63173b01e57SJeff Chen 
63273b01e57SJeff Chen 	for (i = 0; i < MAX_NUM_TID; i++) {
63373b01e57SJeff Chen 		spin_lock_bh(&priv->rx_reorder_tbl_lock[i]);
63473b01e57SJeff Chen 		list_splice_init(&priv->rx_reorder_tbl_ptr[i], &to_delete_list);
63573b01e57SJeff Chen 		spin_unlock_bh(&priv->rx_reorder_tbl_lock[i]);
63673b01e57SJeff Chen 
63773b01e57SJeff Chen 		list_for_each_entry_safe(del_tbl_ptr, tmp_node, &to_delete_list, list)
63873b01e57SJeff Chen 			nxpwifi_del_rx_reorder_entry(priv, del_tbl_ptr);
63973b01e57SJeff Chen 
64073b01e57SJeff Chen 		INIT_LIST_HEAD(&to_delete_list);
64173b01e57SJeff Chen 	}
64273b01e57SJeff Chen 
64373b01e57SJeff Chen 	nxpwifi_reset_11n_rx_seq_num(priv);
64473b01e57SJeff Chen }
64573b01e57SJeff Chen 
64673b01e57SJeff Chen /* Update flags for all RX reorder tables. */
nxpwifi_update_rxreor_flags(struct nxpwifi_adapter * adapter,u8 flags)64773b01e57SJeff Chen void nxpwifi_update_rxreor_flags(struct nxpwifi_adapter *adapter, u8 flags)
64873b01e57SJeff Chen {
64973b01e57SJeff Chen 	struct nxpwifi_private *priv;
65073b01e57SJeff Chen 	struct nxpwifi_rx_reorder_tbl *tbl;
65173b01e57SJeff Chen 	int i, j;
65273b01e57SJeff Chen 
65373b01e57SJeff Chen 	for (i = 0; i < adapter->priv_num; i++) {
65473b01e57SJeff Chen 		priv = adapter->priv[i];
65573b01e57SJeff Chen 
65673b01e57SJeff Chen 		for (j = 0; j < MAX_NUM_TID; j++) {
65773b01e57SJeff Chen 			spin_lock_bh(&priv->rx_reorder_tbl_lock[j]);
65873b01e57SJeff Chen 			list_for_each_entry_rcu(tbl, &priv->rx_reorder_tbl_ptr[j], list)
65973b01e57SJeff Chen 				tbl->flags = flags;
66073b01e57SJeff Chen 			spin_unlock_bh(&priv->rx_reorder_tbl_lock[j]);
66173b01e57SJeff Chen 		}
66273b01e57SJeff Chen 	}
66373b01e57SJeff Chen }
66473b01e57SJeff Chen 
66573b01e57SJeff Chen /* Update RX window size based on coex flag. */
nxpwifi_update_ampdu_rxwinsize(struct nxpwifi_adapter * adapter,bool coex_flag)66673b01e57SJeff Chen static void nxpwifi_update_ampdu_rxwinsize(struct nxpwifi_adapter *adapter,
66773b01e57SJeff Chen 					   bool coex_flag)
66873b01e57SJeff Chen {
66973b01e57SJeff Chen 	u8 i, j;
67073b01e57SJeff Chen 	u32 rx_win_size;
67173b01e57SJeff Chen 	struct nxpwifi_private *priv;
67273b01e57SJeff Chen 
67373b01e57SJeff Chen 	nxpwifi_dbg(adapter, INFO, "Update rxwinsize %d\n", coex_flag);
67473b01e57SJeff Chen 
67573b01e57SJeff Chen 	for (i = 0; i < adapter->priv_num; i++) {
67673b01e57SJeff Chen 		priv = adapter->priv[i];
67773b01e57SJeff Chen 		rx_win_size = priv->add_ba_param.rx_win_size;
67873b01e57SJeff Chen 		if (coex_flag) {
67973b01e57SJeff Chen 			if (priv->bss_type == NXPWIFI_BSS_TYPE_STA)
68073b01e57SJeff Chen 				priv->add_ba_param.rx_win_size =
68173b01e57SJeff Chen 					NXPWIFI_STA_COEX_AMPDU_DEF_RXWINSIZE;
68273b01e57SJeff Chen 			if (priv->bss_type == NXPWIFI_BSS_TYPE_UAP)
68373b01e57SJeff Chen 				priv->add_ba_param.rx_win_size =
68473b01e57SJeff Chen 					NXPWIFI_UAP_COEX_AMPDU_DEF_RXWINSIZE;
68573b01e57SJeff Chen 		} else {
68673b01e57SJeff Chen 			if (priv->bss_type == NXPWIFI_BSS_TYPE_STA)
68773b01e57SJeff Chen 				priv->add_ba_param.rx_win_size =
68873b01e57SJeff Chen 					NXPWIFI_STA_AMPDU_DEF_RXWINSIZE;
68973b01e57SJeff Chen 			if (priv->bss_type == NXPWIFI_BSS_TYPE_UAP)
69073b01e57SJeff Chen 				priv->add_ba_param.rx_win_size =
69173b01e57SJeff Chen 					NXPWIFI_UAP_AMPDU_DEF_RXWINSIZE;
69273b01e57SJeff Chen 		}
69373b01e57SJeff Chen 
69473b01e57SJeff Chen 		if (adapter->coex_win_size && adapter->coex_rx_win_size)
69573b01e57SJeff Chen 			priv->add_ba_param.rx_win_size =
69673b01e57SJeff Chen 				adapter->coex_rx_win_size;
69773b01e57SJeff Chen 
69873b01e57SJeff Chen 		if (rx_win_size != priv->add_ba_param.rx_win_size) {
69973b01e57SJeff Chen 			if (!priv->media_connected)
70073b01e57SJeff Chen 				continue;
70173b01e57SJeff Chen 			for (j = 0; j < MAX_NUM_TID; j++)
70273b01e57SJeff Chen 				nxpwifi_11n_delba(priv, j);
70373b01e57SJeff Chen 		}
70473b01e57SJeff Chen 	}
70573b01e57SJeff Chen }
70673b01e57SJeff Chen 
70773b01e57SJeff Chen /* Check coex for RX BA. */
nxpwifi_coex_ampdu_rxwinsize(struct nxpwifi_adapter * adapter)70873b01e57SJeff Chen void nxpwifi_coex_ampdu_rxwinsize(struct nxpwifi_adapter *adapter)
70973b01e57SJeff Chen {
71073b01e57SJeff Chen 	u8 i;
71173b01e57SJeff Chen 	struct nxpwifi_private *priv;
71273b01e57SJeff Chen 	u8 count = 0;
71373b01e57SJeff Chen 
71473b01e57SJeff Chen 	for (i = 0; i < adapter->priv_num; i++) {
71573b01e57SJeff Chen 		priv = adapter->priv[i];
71673b01e57SJeff Chen 		if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_STA) {
71773b01e57SJeff Chen 			if (priv->media_connected)
71873b01e57SJeff Chen 				count++;
71973b01e57SJeff Chen 		}
72073b01e57SJeff Chen 		if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_UAP) {
72173b01e57SJeff Chen 			if (priv->bss_started)
72273b01e57SJeff Chen 				count++;
72373b01e57SJeff Chen 		}
72473b01e57SJeff Chen 		if (count >= NXPWIFI_BSS_COEX_COUNT)
72573b01e57SJeff Chen 			break;
72673b01e57SJeff Chen 	}
72773b01e57SJeff Chen 	if (count >= NXPWIFI_BSS_COEX_COUNT)
72873b01e57SJeff Chen 		nxpwifi_update_ampdu_rxwinsize(adapter, true);
72973b01e57SJeff Chen 	else
73073b01e57SJeff Chen 		nxpwifi_update_ampdu_rxwinsize(adapter, false);
73173b01e57SJeff Chen }
73273b01e57SJeff Chen 
73373b01e57SJeff Chen /* Handle RXBA sync event. */
nxpwifi_11n_rxba_sync_event(struct nxpwifi_private * priv,u8 * event_buf,u16 len)73473b01e57SJeff Chen void nxpwifi_11n_rxba_sync_event(struct nxpwifi_private *priv,
73573b01e57SJeff Chen 				 u8 *event_buf, u16 len)
73673b01e57SJeff Chen {
73773b01e57SJeff Chen 	struct nxpwifi_ie_types_rxba_sync *tlv_rxba = (void *)event_buf;
73873b01e57SJeff Chen 	u16 tlv_type, tlv_len;
73973b01e57SJeff Chen 	struct nxpwifi_rx_reorder_tbl *rx_reor_tbl_ptr;
74073b01e57SJeff Chen 	u8 i, j;
74173b01e57SJeff Chen 	u16 seq_num, tlv_seq_num, tlv_bitmap_len;
74273b01e57SJeff Chen 	int tlv_buf_left = len;
74373b01e57SJeff Chen 	int ret;
74473b01e57SJeff Chen 	u8 *tmp;
74573b01e57SJeff Chen 
74673b01e57SJeff Chen 	nxpwifi_dbg_dump(priv->adapter, EVT_D, "RXBA_SYNC event:",
74773b01e57SJeff Chen 			 event_buf, len);
74873b01e57SJeff Chen 	while (tlv_buf_left > sizeof(*tlv_rxba)) {
74973b01e57SJeff Chen 		tlv_type = le16_to_cpu(tlv_rxba->header.type);
75073b01e57SJeff Chen 		tlv_len  = le16_to_cpu(tlv_rxba->header.len);
75173b01e57SJeff Chen 		if (size_add(sizeof(tlv_rxba->header), tlv_len) > tlv_buf_left) {
75273b01e57SJeff Chen 			nxpwifi_dbg(priv->adapter, WARN,
75373b01e57SJeff Chen 				    "TLV size (%zu) overflows event_buf buf_left=%d\n",
75473b01e57SJeff Chen 				    size_add(sizeof(tlv_rxba->header), tlv_len),
75573b01e57SJeff Chen 				    tlv_buf_left);
75673b01e57SJeff Chen 			return;
75773b01e57SJeff Chen 		}
75873b01e57SJeff Chen 
75973b01e57SJeff Chen 		if (tlv_type != TLV_TYPE_RXBA_SYNC) {
76073b01e57SJeff Chen 			nxpwifi_dbg(priv->adapter, ERROR,
76173b01e57SJeff Chen 				    "Wrong TLV id=0x%x\n", tlv_type);
76273b01e57SJeff Chen 			return;
76373b01e57SJeff Chen 		}
76473b01e57SJeff Chen 
76573b01e57SJeff Chen 		tlv_seq_num = le16_to_cpu(tlv_rxba->seq_num);
76673b01e57SJeff Chen 		tlv_bitmap_len = le16_to_cpu(tlv_rxba->bitmap_len);
76773b01e57SJeff Chen 		if (size_add(sizeof(*tlv_rxba), tlv_bitmap_len) > tlv_buf_left) {
76873b01e57SJeff Chen 			nxpwifi_dbg(priv->adapter, WARN,
76973b01e57SJeff Chen 				    "TLV size (%zu) overflows event_buf buf_left=%d\n",
77073b01e57SJeff Chen 				    size_add(sizeof(*tlv_rxba), tlv_bitmap_len),
77173b01e57SJeff Chen 				    tlv_buf_left);
77273b01e57SJeff Chen 			return;
77373b01e57SJeff Chen 		}
77473b01e57SJeff Chen 
77573b01e57SJeff Chen 		nxpwifi_dbg(priv->adapter, INFO,
77673b01e57SJeff Chen 			    "%pM tid=%d seq_num=%d bitmap_len=%d\n",
77773b01e57SJeff Chen 			    tlv_rxba->mac, tlv_rxba->tid, tlv_seq_num,
77873b01e57SJeff Chen 			    tlv_bitmap_len);
77973b01e57SJeff Chen 
78073b01e57SJeff Chen 		rx_reor_tbl_ptr =
78173b01e57SJeff Chen 			nxpwifi_11n_get_rx_reorder_tbl(priv, tlv_rxba->tid,
78273b01e57SJeff Chen 						       tlv_rxba->mac);
78373b01e57SJeff Chen 		if (!rx_reor_tbl_ptr) {
78473b01e57SJeff Chen 			nxpwifi_dbg(priv->adapter, ERROR,
78573b01e57SJeff Chen 				    "Can not find rx_reorder_tbl!");
78673b01e57SJeff Chen 			return;
78773b01e57SJeff Chen 		}
78873b01e57SJeff Chen 
78973b01e57SJeff Chen 		for (i = 0; i < tlv_bitmap_len; i++) {
79073b01e57SJeff Chen 			for (j = 0 ; j < 8; j++) {
79173b01e57SJeff Chen 				if (tlv_rxba->bitmap[i] & (1 << j)) {
79273b01e57SJeff Chen 					seq_num = (MAX_TID_VALUE - 1) &
79373b01e57SJeff Chen 						(tlv_seq_num + i * 8 + j);
79473b01e57SJeff Chen 
79573b01e57SJeff Chen 					nxpwifi_dbg(priv->adapter, ERROR,
79673b01e57SJeff Chen 						    "drop packet,seq=%d\n",
79773b01e57SJeff Chen 						    seq_num);
79873b01e57SJeff Chen 
79973b01e57SJeff Chen 					ret = nxpwifi_11n_rx_reorder_pkt
80073b01e57SJeff Chen 					(priv, seq_num, tlv_rxba->tid,
80173b01e57SJeff Chen 					 tlv_rxba->mac, 0, NULL);
80273b01e57SJeff Chen 
80373b01e57SJeff Chen 					if (ret)
80473b01e57SJeff Chen 						nxpwifi_dbg(priv->adapter,
80573b01e57SJeff Chen 							    ERROR,
80673b01e57SJeff Chen 							    "Fail to drop packet");
80773b01e57SJeff Chen 				}
80873b01e57SJeff Chen 			}
80973b01e57SJeff Chen 		}
81073b01e57SJeff Chen 
81173b01e57SJeff Chen 		tlv_buf_left -= (sizeof(tlv_rxba->header) + tlv_len);
81273b01e57SJeff Chen 		tmp = (u8 *)tlv_rxba + sizeof(tlv_rxba->header) + tlv_len;
81373b01e57SJeff Chen 		tlv_rxba = (struct nxpwifi_ie_types_rxba_sync *)tmp;
81473b01e57SJeff Chen 	}
81573b01e57SJeff Chen }
816