xref: /linux/drivers/net/wireless/nxp/nxpwifi/11n.c (revision 91ec2035134982b98fab0609a9fd8480e8217dc1)
173b01e57SJeff Chen // SPDX-License-Identifier: GPL-2.0-only
273b01e57SJeff Chen /*
373b01e57SJeff Chen  * nxpwifi 802.11n helpers
473b01e57SJeff Chen  * Copyright 2011-2024 NXP
573b01e57SJeff Chen  */
673b01e57SJeff Chen 
773b01e57SJeff Chen #include "cfg.h"
873b01e57SJeff Chen #include "util.h"
973b01e57SJeff Chen #include "fw.h"
1073b01e57SJeff Chen #include "main.h"
1173b01e57SJeff Chen #include "cmdevt.h"
1273b01e57SJeff Chen #include "wmm.h"
1373b01e57SJeff Chen #include "11n.h"
1473b01e57SJeff Chen #include "11ax.h"
1573b01e57SJeff Chen 
1673b01e57SJeff Chen /*
1773b01e57SJeff Chen  * Fills HT capability information field, AMPDU Parameters field, HT extended
1873b01e57SJeff Chen  * capability field, and supported MCS set fields.
1973b01e57SJeff Chen  *
2073b01e57SJeff Chen  * HT capability information field, AMPDU Parameters field, supported MCS set
2173b01e57SJeff Chen  * fields are retrieved from cfg80211 stack
2273b01e57SJeff Chen  *
2373b01e57SJeff Chen  * RD responder bit to set to clear in the extended capability header.
2473b01e57SJeff Chen  */
nxpwifi_fill_cap_info(struct nxpwifi_private * priv,u8 radio_type,struct ieee80211_ht_cap * ht_cap)2573b01e57SJeff Chen int nxpwifi_fill_cap_info(struct nxpwifi_private *priv, u8 radio_type,
2673b01e57SJeff Chen 			  struct ieee80211_ht_cap *ht_cap)
2773b01e57SJeff Chen {
2873b01e57SJeff Chen 	u16 ht_cap_info;
2973b01e57SJeff Chen 	u16 bcn_ht_cap = le16_to_cpu(ht_cap->cap_info);
3073b01e57SJeff Chen 	u16 ht_ext_cap = le16_to_cpu(ht_cap->extended_ht_cap_info);
3173b01e57SJeff Chen 	struct ieee80211_supported_band *sband =
3273b01e57SJeff Chen 		priv->wdev.wiphy->bands[radio_type];
3373b01e57SJeff Chen 
3473b01e57SJeff Chen 	if (WARN_ON_ONCE(!sband)) {
3573b01e57SJeff Chen 		nxpwifi_dbg(priv->adapter, ERROR, "Invalid radio type!\n");
3673b01e57SJeff Chen 		return -EINVAL;
3773b01e57SJeff Chen 	}
3873b01e57SJeff Chen 
3973b01e57SJeff Chen 	ht_cap->ampdu_params_info =
4073b01e57SJeff Chen 		(AMPDU_FACTOR_64K & IEEE80211_HT_AMPDU_PARM_FACTOR) |
4173b01e57SJeff Chen 		((priv->adapter->hw_mpdu_density <<
4273b01e57SJeff Chen 		 IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT) &
4373b01e57SJeff Chen 		 IEEE80211_HT_AMPDU_PARM_DENSITY);
4473b01e57SJeff Chen 
4573b01e57SJeff Chen 	memcpy((u8 *)&ht_cap->mcs, &sband->ht_cap.mcs,
4673b01e57SJeff Chen 	       sizeof(sband->ht_cap.mcs));
4773b01e57SJeff Chen 
4873b01e57SJeff Chen 	if (priv->bss_mode == NL80211_IFTYPE_STATION ||
4973b01e57SJeff Chen 	    (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 &&
5073b01e57SJeff Chen 	     priv->adapter->sec_chan_offset != IEEE80211_HT_PARAM_CHA_SEC_NONE))
5173b01e57SJeff Chen 		/* Set MCS32 for infra mode or ad-hoc mode with 40MHz support */
5273b01e57SJeff Chen 		SETHT_MCS32(ht_cap->mcs.rx_mask);
5373b01e57SJeff Chen 
5473b01e57SJeff Chen 	/* Clear RD responder bit */
5573b01e57SJeff Chen 	ht_ext_cap &= ~IEEE80211_HT_EXT_CAP_RD_RESPONDER;
5673b01e57SJeff Chen 
5773b01e57SJeff Chen 	ht_cap_info = sband->ht_cap.cap;
5873b01e57SJeff Chen 	if (bcn_ht_cap) {
5973b01e57SJeff Chen 		if (!(bcn_ht_cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40))
6073b01e57SJeff Chen 			ht_cap_info &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
6173b01e57SJeff Chen 		if (!(bcn_ht_cap & IEEE80211_HT_CAP_SGI_40))
6273b01e57SJeff Chen 			ht_cap_info &= ~IEEE80211_HT_CAP_SGI_40;
6373b01e57SJeff Chen 		if (!(bcn_ht_cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT))
6473b01e57SJeff Chen 			ht_cap_info &= ~IEEE80211_HT_CAP_40MHZ_INTOLERANT;
6573b01e57SJeff Chen 	}
6673b01e57SJeff Chen 	ht_cap->cap_info = cpu_to_le16(ht_cap_info);
6773b01e57SJeff Chen 	ht_cap->extended_ht_cap_info = cpu_to_le16(ht_ext_cap);
6873b01e57SJeff Chen 
6973b01e57SJeff Chen 	if (ISSUPP_BEAMFORMING(priv->adapter->hw_dot_11n_dev_cap))
7073b01e57SJeff Chen 		ht_cap->tx_BF_cap_info = cpu_to_le32(NXPWIFI_DEF_11N_TX_BF_CAP);
7173b01e57SJeff Chen 
7273b01e57SJeff Chen 	return 0;
7373b01e57SJeff Chen }
7473b01e57SJeff Chen 
7573b01e57SJeff Chen /* Return BA stream entry that matches the requested status. */
7673b01e57SJeff Chen static struct nxpwifi_tx_ba_stream_tbl *
nxpwifi_get_ba_status(struct nxpwifi_private * priv,int tid,enum nxpwifi_ba_status ba_status)7773b01e57SJeff Chen nxpwifi_get_ba_status(struct nxpwifi_private *priv, int tid,
7873b01e57SJeff Chen 		      enum nxpwifi_ba_status ba_status)
7973b01e57SJeff Chen {
8073b01e57SJeff Chen 	struct nxpwifi_tx_ba_stream_tbl *tx_ba_tsr_tbl, *found = NULL;
8173b01e57SJeff Chen 
8273b01e57SJeff Chen 	guard(rcu)();
8373b01e57SJeff Chen 	list_for_each_entry_rcu(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr[tid], list) {
8473b01e57SJeff Chen 		if (tx_ba_tsr_tbl->ba_status == ba_status) {
8573b01e57SJeff Chen 			found = tx_ba_tsr_tbl;
8673b01e57SJeff Chen 			break;
8773b01e57SJeff Chen 		}
8873b01e57SJeff Chen 	}
8973b01e57SJeff Chen 	return found;
9073b01e57SJeff Chen }
9173b01e57SJeff Chen 
9273b01e57SJeff Chen /* Handle DELBA command response (recreate or continue ADDBA as needed). */
nxpwifi_ret_11n_delba(struct nxpwifi_private * priv,struct host_cmd_ds_command * resp)9373b01e57SJeff Chen int nxpwifi_ret_11n_delba(struct nxpwifi_private *priv,
9473b01e57SJeff Chen 			  struct host_cmd_ds_command *resp)
9573b01e57SJeff Chen {
9673b01e57SJeff Chen 	int tid;
9773b01e57SJeff Chen 	struct nxpwifi_tx_ba_stream_tbl *tx_ba_tbl;
9873b01e57SJeff Chen 	struct host_cmd_ds_11n_delba *del_ba = &resp->params.del_ba;
9973b01e57SJeff Chen 	u16 del_ba_param_set = le16_to_cpu(del_ba->del_ba_param_set);
10073b01e57SJeff Chen 
10173b01e57SJeff Chen 	tid = del_ba_param_set >> DELBA_TID_POS;
10273b01e57SJeff Chen 	if (del_ba->del_result == BA_RESULT_SUCCESS) {
10373b01e57SJeff Chen 		nxpwifi_del_ba_tbl(priv, tid, del_ba->peer_mac_addr,
10473b01e57SJeff Chen 				   TYPE_DELBA_SENT,
10573b01e57SJeff Chen 				   INITIATOR_BIT(del_ba_param_set));
10673b01e57SJeff Chen 
10773b01e57SJeff Chen 		tx_ba_tbl = nxpwifi_get_ba_status(priv, tid, BA_SETUP_INPROGRESS);
10873b01e57SJeff Chen 		if (tx_ba_tbl)
10973b01e57SJeff Chen 			nxpwifi_send_addba(priv, tx_ba_tbl->tid,
11073b01e57SJeff Chen 					   tx_ba_tbl->ra);
11173b01e57SJeff Chen 	} else {
11273b01e57SJeff Chen 		/*
11373b01e57SJeff Chen 		 * In case of failure, recreate the deleted stream in case
11473b01e57SJeff Chen 		 * we initiated the DELBA
11573b01e57SJeff Chen 		 */
11673b01e57SJeff Chen 		if (!INITIATOR_BIT(del_ba_param_set))
11773b01e57SJeff Chen 			return 0;
11873b01e57SJeff Chen 
11973b01e57SJeff Chen 		nxpwifi_create_ba_tbl(priv, del_ba->peer_mac_addr, tid,
12073b01e57SJeff Chen 				      BA_SETUP_INPROGRESS);
12173b01e57SJeff Chen 
12273b01e57SJeff Chen 		tx_ba_tbl = nxpwifi_get_ba_status(priv, tid, BA_SETUP_INPROGRESS);
12373b01e57SJeff Chen 
12473b01e57SJeff Chen 		if (tx_ba_tbl)
12573b01e57SJeff Chen 			nxpwifi_del_ba_tbl(priv, tx_ba_tbl->tid, tx_ba_tbl->ra,
12673b01e57SJeff Chen 					   TYPE_DELBA_SENT, true);
12773b01e57SJeff Chen 	}
12873b01e57SJeff Chen 
12973b01e57SJeff Chen 	return 0;
13073b01e57SJeff Chen }
13173b01e57SJeff Chen 
13273b01e57SJeff Chen /* Handle ADDBA response; delete BA stream on failure. */
nxpwifi_ret_11n_addba_req(struct nxpwifi_private * priv,struct host_cmd_ds_command * resp)13373b01e57SJeff Chen int nxpwifi_ret_11n_addba_req(struct nxpwifi_private *priv,
13473b01e57SJeff Chen 			      struct host_cmd_ds_command *resp)
13573b01e57SJeff Chen {
13673b01e57SJeff Chen 	int tid, tid_down;
13773b01e57SJeff Chen 	struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = &resp->params.add_ba_rsp;
13873b01e57SJeff Chen 	struct nxpwifi_tx_ba_stream_tbl *tx_ba_tbl;
13973b01e57SJeff Chen 	struct nxpwifi_ra_list_tbl *ra_list;
14073b01e57SJeff Chen 	u16 block_ack_param_set = le16_to_cpu(add_ba_rsp->block_ack_param_set);
14173b01e57SJeff Chen 
14273b01e57SJeff Chen 	add_ba_rsp->ssn = cpu_to_le16((le16_to_cpu(add_ba_rsp->ssn))
14373b01e57SJeff Chen 			& SSN_MASK);
14473b01e57SJeff Chen 
14573b01e57SJeff Chen 	tid = u16_get_bits(block_ack_param_set, IEEE80211_ADDBA_PARAM_TID_MASK);
14673b01e57SJeff Chen 
14773b01e57SJeff Chen 	tid_down = nxpwifi_wmm_downgrade_tid(priv, tid);
14873b01e57SJeff Chen 	ra_list = nxpwifi_wmm_get_ralist_node(priv, tid_down,
14973b01e57SJeff Chen 					      add_ba_rsp->peer_mac_addr);
15073b01e57SJeff Chen 	if (le16_to_cpu(add_ba_rsp->status_code) != BA_RESULT_SUCCESS) {
15173b01e57SJeff Chen 		if (ra_list) {
15273b01e57SJeff Chen 			ra_list->ba_status = BA_SETUP_NONE;
15373b01e57SJeff Chen 			ra_list->amsdu_in_ampdu = false;
15473b01e57SJeff Chen 		}
15573b01e57SJeff Chen 		nxpwifi_del_ba_tbl(priv, tid, add_ba_rsp->peer_mac_addr,
15673b01e57SJeff Chen 				   TYPE_DELBA_SENT, true);
15773b01e57SJeff Chen 		if (add_ba_rsp->add_rsp_result != BA_RESULT_TIMEOUT)
15873b01e57SJeff Chen 			priv->aggr_prio_tbl[tid].ampdu_ap =
15973b01e57SJeff Chen 				BA_STREAM_NOT_ALLOWED;
16073b01e57SJeff Chen 		return 0;
16173b01e57SJeff Chen 	}
16273b01e57SJeff Chen 
16373b01e57SJeff Chen 	guard(rcu)();
16473b01e57SJeff Chen 	tx_ba_tbl = nxpwifi_get_ba_tbl(priv, tid, add_ba_rsp->peer_mac_addr);
16573b01e57SJeff Chen 	if (tx_ba_tbl) {
16673b01e57SJeff Chen 		nxpwifi_dbg(priv->adapter, EVENT, "info: BA stream complete\n");
16773b01e57SJeff Chen 		tx_ba_tbl->ba_status = BA_SETUP_COMPLETE;
16873b01e57SJeff Chen 		if ((block_ack_param_set & IEEE80211_ADDBA_PARAM_AMSDU_MASK) &&
16973b01e57SJeff Chen 		    priv->add_ba_param.tx_amsdu &&
17073b01e57SJeff Chen 		    priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED)
17173b01e57SJeff Chen 			tx_ba_tbl->amsdu = true;
17273b01e57SJeff Chen 		else
17373b01e57SJeff Chen 			tx_ba_tbl->amsdu = false;
17473b01e57SJeff Chen 		if (ra_list) {
17573b01e57SJeff Chen 			ra_list->amsdu_in_ampdu = tx_ba_tbl->amsdu;
17673b01e57SJeff Chen 			ra_list->ba_status = BA_SETUP_COMPLETE;
17773b01e57SJeff Chen 		}
17873b01e57SJeff Chen 	} else {
17973b01e57SJeff Chen 		nxpwifi_dbg(priv->adapter, ERROR, "BA stream not created\n");
18073b01e57SJeff Chen 	}
18173b01e57SJeff Chen 
18273b01e57SJeff Chen 	return 0;
18373b01e57SJeff Chen }
18473b01e57SJeff Chen 
18573b01e57SJeff Chen /*
18673b01e57SJeff Chen  * Reconfigure Tx buffer command.
18773b01e57SJeff Chen  * Set command ID/action/size; set Tx buffer size on SET; ensure little-endian.
18873b01e57SJeff Chen  */
nxpwifi_cmd_recfg_tx_buf(struct nxpwifi_private * priv,struct host_cmd_ds_command * cmd,int cmd_action,u16 * buf_size)18973b01e57SJeff Chen int nxpwifi_cmd_recfg_tx_buf(struct nxpwifi_private *priv,
19073b01e57SJeff Chen 			     struct host_cmd_ds_command *cmd, int cmd_action,
19173b01e57SJeff Chen 			     u16 *buf_size)
19273b01e57SJeff Chen {
19373b01e57SJeff Chen 	struct host_cmd_ds_txbuf_cfg *tx_buf = &cmd->params.tx_buf;
19473b01e57SJeff Chen 	u16 action = (u16)cmd_action;
19573b01e57SJeff Chen 
19673b01e57SJeff Chen 	cmd->command = cpu_to_le16(HOST_CMD_RECONFIGURE_TX_BUFF);
19773b01e57SJeff Chen 	cmd->size =
19873b01e57SJeff Chen 		cpu_to_le16(sizeof(struct host_cmd_ds_txbuf_cfg) + S_DS_GEN);
19973b01e57SJeff Chen 	tx_buf->action = cpu_to_le16(action);
20073b01e57SJeff Chen 	switch (action) {
20173b01e57SJeff Chen 	case HOST_ACT_GEN_SET:
20273b01e57SJeff Chen 		nxpwifi_dbg(priv->adapter, CMD,
20373b01e57SJeff Chen 			    "cmd: set tx_buf=%d\n", *buf_size);
20473b01e57SJeff Chen 		tx_buf->buff_size = cpu_to_le16(*buf_size);
20573b01e57SJeff Chen 		break;
20673b01e57SJeff Chen 	case HOST_ACT_GEN_GET:
20773b01e57SJeff Chen 	default:
20873b01e57SJeff Chen 		tx_buf->buff_size = 0;
20973b01e57SJeff Chen 		break;
21073b01e57SJeff Chen 	}
21173b01e57SJeff Chen 	return 0;
21273b01e57SJeff Chen }
21373b01e57SJeff Chen 
21473b01e57SJeff Chen /*
21573b01e57SJeff Chen  * AMSDU aggregation control command.
21673b01e57SJeff Chen  * Set ID/action/size; set AMSDU params on SET; ensure little-endian.
21773b01e57SJeff Chen  */
nxpwifi_cmd_amsdu_aggr_ctrl(struct host_cmd_ds_command * cmd,int cmd_action,struct nxpwifi_ds_11n_amsdu_aggr_ctrl * aa_ctrl)21873b01e57SJeff Chen int nxpwifi_cmd_amsdu_aggr_ctrl(struct host_cmd_ds_command *cmd,
21973b01e57SJeff Chen 				int cmd_action,
22073b01e57SJeff Chen 				struct nxpwifi_ds_11n_amsdu_aggr_ctrl *aa_ctrl)
22173b01e57SJeff Chen {
22273b01e57SJeff Chen 	struct host_cmd_ds_amsdu_aggr_ctrl *amsdu_ctrl =
22373b01e57SJeff Chen 		&cmd->params.amsdu_aggr_ctrl;
22473b01e57SJeff Chen 	u16 action = (u16)cmd_action;
22573b01e57SJeff Chen 
22673b01e57SJeff Chen 	cmd->command = cpu_to_le16(HOST_CMD_AMSDU_AGGR_CTRL);
22773b01e57SJeff Chen 	cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_amsdu_aggr_ctrl)
22873b01e57SJeff Chen 				+ S_DS_GEN);
22973b01e57SJeff Chen 	amsdu_ctrl->action = cpu_to_le16(action);
23073b01e57SJeff Chen 	switch (action) {
23173b01e57SJeff Chen 	case HOST_ACT_GEN_SET:
23273b01e57SJeff Chen 		amsdu_ctrl->enable = cpu_to_le16(aa_ctrl->enable);
23373b01e57SJeff Chen 		amsdu_ctrl->curr_buf_size = 0;
23473b01e57SJeff Chen 		break;
23573b01e57SJeff Chen 	case HOST_ACT_GEN_GET:
23673b01e57SJeff Chen 	default:
23773b01e57SJeff Chen 		amsdu_ctrl->curr_buf_size = 0;
23873b01e57SJeff Chen 		break;
23973b01e57SJeff Chen 	}
24073b01e57SJeff Chen 	return 0;
24173b01e57SJeff Chen }
24273b01e57SJeff Chen 
24373b01e57SJeff Chen /*
24473b01e57SJeff Chen  * 11n configuration command.
24573b01e57SJeff Chen  * Set action, HT Tx capability/info, and misc config when 11ac HW is present.
24673b01e57SJeff Chen  */
nxpwifi_cmd_11n_cfg(struct nxpwifi_private * priv,struct host_cmd_ds_command * cmd,u16 cmd_action,struct nxpwifi_ds_11n_tx_cfg * txcfg)24773b01e57SJeff Chen int nxpwifi_cmd_11n_cfg(struct nxpwifi_private *priv,
24873b01e57SJeff Chen 			struct host_cmd_ds_command *cmd, u16 cmd_action,
24973b01e57SJeff Chen 			struct nxpwifi_ds_11n_tx_cfg *txcfg)
25073b01e57SJeff Chen {
25173b01e57SJeff Chen 	struct host_cmd_ds_11n_cfg *htcfg = &cmd->params.htcfg;
25273b01e57SJeff Chen 
25373b01e57SJeff Chen 	cmd->command = cpu_to_le16(HOST_CMD_11N_CFG);
25473b01e57SJeff Chen 	cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_11n_cfg) + S_DS_GEN);
25573b01e57SJeff Chen 	htcfg->action = cpu_to_le16(cmd_action);
25673b01e57SJeff Chen 	htcfg->ht_tx_cap = cpu_to_le16(txcfg->tx_htcap);
25773b01e57SJeff Chen 	htcfg->ht_tx_info = cpu_to_le16(txcfg->tx_htinfo);
25873b01e57SJeff Chen 
25973b01e57SJeff Chen 	if (priv->adapter->is_hw_11ac_capable)
26073b01e57SJeff Chen 		htcfg->misc_config = cpu_to_le16(txcfg->misc_config);
26173b01e57SJeff Chen 
26273b01e57SJeff Chen 	return 0;
26373b01e57SJeff Chen }
26473b01e57SJeff Chen 
26573b01e57SJeff Chen /*
26673b01e57SJeff Chen  * Append 11n TLVs to the caller-owned buffer.
26773b01e57SJeff Chen  * Caller allocates space; no size checks here.
26873b01e57SJeff Chen  * May add: HT Cap, HT Operation + channel list, 20/40 BSS Coexistence,
26973b01e57SJeff Chen  * and Extended Capabilities (HS2/TWT bits when applicable).
27073b01e57SJeff Chen  */
27173b01e57SJeff Chen int
nxpwifi_cmd_append_11n_tlv(struct nxpwifi_private * priv,struct nxpwifi_bssdescriptor * bss_desc,u8 ** buffer)27273b01e57SJeff Chen nxpwifi_cmd_append_11n_tlv(struct nxpwifi_private *priv,
27373b01e57SJeff Chen 			   struct nxpwifi_bssdescriptor *bss_desc,
27473b01e57SJeff Chen 			   u8 **buffer)
27573b01e57SJeff Chen {
27673b01e57SJeff Chen 	struct nxpwifi_ie_types_htcap *ht_cap;
27773b01e57SJeff Chen 	struct nxpwifi_ie_types_chan_list_param_set *chan_list;
27873b01e57SJeff Chen 	struct nxpwifi_chan_scan_param_set *chan_param;
27973b01e57SJeff Chen 	struct nxpwifi_ie_types_2040bssco *bss_co_2040;
28073b01e57SJeff Chen 	struct nxpwifi_ie_types_extcap *ext_cap;
28173b01e57SJeff Chen 	int ret_len = 0;
28273b01e57SJeff Chen 	struct ieee80211_supported_band *sband;
28373b01e57SJeff Chen 	struct element *hdr;
28473b01e57SJeff Chen 	u8 radio_type;
28573b01e57SJeff Chen 
28673b01e57SJeff Chen 	if (!buffer || !*buffer)
28773b01e57SJeff Chen 		return ret_len;
28873b01e57SJeff Chen 
28973b01e57SJeff Chen 	radio_type = nxpwifi_band_to_radio_type((u8)bss_desc->bss_band);
29073b01e57SJeff Chen 	sband = priv->wdev.wiphy->bands[radio_type];
29173b01e57SJeff Chen 
29273b01e57SJeff Chen 	if (bss_desc->bcn_ht_cap) {
29373b01e57SJeff Chen 		ht_cap = (struct nxpwifi_ie_types_htcap *)*buffer;
29473b01e57SJeff Chen 		memset(ht_cap, 0, sizeof(struct nxpwifi_ie_types_htcap));
29573b01e57SJeff Chen 		ht_cap->header.type = cpu_to_le16(WLAN_EID_HT_CAPABILITY);
29673b01e57SJeff Chen 		ht_cap->header.len =
29773b01e57SJeff Chen 			cpu_to_le16(sizeof(struct ieee80211_ht_cap));
29873b01e57SJeff Chen 		memcpy((u8 *)ht_cap + sizeof(struct nxpwifi_ie_types_header),
29973b01e57SJeff Chen 		       (u8 *)bss_desc->bcn_ht_cap,
30073b01e57SJeff Chen 		       le16_to_cpu(ht_cap->header.len));
30173b01e57SJeff Chen 
30273b01e57SJeff Chen 		nxpwifi_fill_cap_info(priv, radio_type, &ht_cap->ht_cap);
30373b01e57SJeff Chen 		/* Update HT40 capability from current channel. */
30473b01e57SJeff Chen 		if (bss_desc->bcn_ht_oper) {
30573b01e57SJeff Chen 			u8 ht_param = bss_desc->bcn_ht_oper->ht_param;
30673b01e57SJeff Chen 			u8 radio =
30773b01e57SJeff Chen 				nxpwifi_band_to_radio_type(bss_desc->bss_band);
30873b01e57SJeff Chen 			int freq =
30973b01e57SJeff Chen 				ieee80211_channel_to_frequency(bss_desc->channel,
31073b01e57SJeff Chen 							       radio);
31173b01e57SJeff Chen 			struct ieee80211_channel *chan =
31273b01e57SJeff Chen 				ieee80211_get_channel(priv->adapter->wiphy, freq);
31373b01e57SJeff Chen 
31473b01e57SJeff Chen 			switch (ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
31573b01e57SJeff Chen 			case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
31673b01e57SJeff Chen 				if (chan->flags & IEEE80211_CHAN_NO_HT40PLUS) {
31773b01e57SJeff Chen 					ht_cap->ht_cap.cap_info &=
31873b01e57SJeff Chen 					cpu_to_le16
31973b01e57SJeff Chen 					(~IEEE80211_HT_CAP_SUP_WIDTH_20_40);
32073b01e57SJeff Chen 					ht_cap->ht_cap.cap_info &=
32173b01e57SJeff Chen 					cpu_to_le16(~IEEE80211_HT_CAP_SGI_40);
32273b01e57SJeff Chen 				}
32373b01e57SJeff Chen 				break;
32473b01e57SJeff Chen 			case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
32573b01e57SJeff Chen 				if (chan->flags & IEEE80211_CHAN_NO_HT40MINUS) {
32673b01e57SJeff Chen 					ht_cap->ht_cap.cap_info &=
32773b01e57SJeff Chen 					cpu_to_le16
32873b01e57SJeff Chen 					(~IEEE80211_HT_CAP_SUP_WIDTH_20_40);
32973b01e57SJeff Chen 					ht_cap->ht_cap.cap_info &=
33073b01e57SJeff Chen 					cpu_to_le16(~IEEE80211_HT_CAP_SGI_40);
33173b01e57SJeff Chen 				}
33273b01e57SJeff Chen 				break;
33373b01e57SJeff Chen 			}
33473b01e57SJeff Chen 		}
33573b01e57SJeff Chen 
33673b01e57SJeff Chen 		*buffer += sizeof(struct nxpwifi_ie_types_htcap);
33773b01e57SJeff Chen 		ret_len += sizeof(struct nxpwifi_ie_types_htcap);
33873b01e57SJeff Chen 	}
33973b01e57SJeff Chen 
34073b01e57SJeff Chen 	if (bss_desc->bcn_ht_oper) {
34173b01e57SJeff Chen 		chan_list =
34273b01e57SJeff Chen 			(struct nxpwifi_ie_types_chan_list_param_set *)*buffer;
34373b01e57SJeff Chen 		chan_param = chan_list->chan_scan_param;
34473b01e57SJeff Chen 		memset(chan_list, 0, struct_size(chan_list, chan_scan_param, 1));
34573b01e57SJeff Chen 		chan_list->header.type = cpu_to_le16(TLV_TYPE_CHANLIST);
34673b01e57SJeff Chen 		chan_list->header.len = cpu_to_le16(sizeof(*chan_param));
34773b01e57SJeff Chen 		chan_param->chan_number = bss_desc->bcn_ht_oper->primary_chan;
34873b01e57SJeff Chen 		chan_param->band_cfg =
34973b01e57SJeff Chen 			nxpwifi_band_to_radio_type((u8)bss_desc->bss_band);
35073b01e57SJeff Chen 
35173b01e57SJeff Chen 		if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info) &&
35273b01e57SJeff Chen 		    bss_desc->bcn_vht_oper &&
35373b01e57SJeff Chen 		    bss_desc->bcn_vht_oper->chan_width ==
35473b01e57SJeff Chen 		    IEEE80211_VHT_CHANWIDTH_80MHZ) {
35573b01e57SJeff Chen 			SET_SECONDARYCHAN(chan_param->band_cfg,
35673b01e57SJeff Chen 					  (bss_desc->bcn_ht_oper->ht_param &
35773b01e57SJeff Chen 					   IEEE80211_HT_PARAM_CHA_SEC_OFFSET));
35873b01e57SJeff Chen 			chan_param->band_cfg |=
35973b01e57SJeff Chen 				((CHAN_BW_80MHZ <<
36073b01e57SJeff Chen 				  BAND_CFG_CHAN_WIDTH_SHIFT_BIT) &
36173b01e57SJeff Chen 				 BAND_CFG_CHAN_WIDTH_MASK);
36273b01e57SJeff Chen 		} else if (sband->ht_cap.cap &
36373b01e57SJeff Chen 			   IEEE80211_HT_CAP_SUP_WIDTH_20_40 &&
36473b01e57SJeff Chen 			   bss_desc->bcn_ht_oper->ht_param &
36573b01e57SJeff Chen 			   IEEE80211_HT_PARAM_CHAN_WIDTH_ANY) {
36673b01e57SJeff Chen 			SET_SECONDARYCHAN(chan_param->band_cfg,
36773b01e57SJeff Chen 					  (bss_desc->bcn_ht_oper->ht_param &
36873b01e57SJeff Chen 					   IEEE80211_HT_PARAM_CHA_SEC_OFFSET));
36973b01e57SJeff Chen 			chan_param->band_cfg |=
37073b01e57SJeff Chen 				((CHAN_BW_40MHZ <<
37173b01e57SJeff Chen 				  BAND_CFG_CHAN_WIDTH_SHIFT_BIT) &
37273b01e57SJeff Chen 				 BAND_CFG_CHAN_WIDTH_MASK);
37373b01e57SJeff Chen 		}
37473b01e57SJeff Chen 
37573b01e57SJeff Chen 		*buffer += struct_size(chan_list, chan_scan_param, 1);
37673b01e57SJeff Chen 		ret_len += struct_size(chan_list, chan_scan_param, 1);
37773b01e57SJeff Chen 	}
37873b01e57SJeff Chen 
37973b01e57SJeff Chen 	if (bss_desc->bcn_bss_co_2040) {
38073b01e57SJeff Chen 		bss_co_2040 = (struct nxpwifi_ie_types_2040bssco *)*buffer;
38173b01e57SJeff Chen 		memset(bss_co_2040, 0,
38273b01e57SJeff Chen 		       sizeof(struct nxpwifi_ie_types_2040bssco));
38373b01e57SJeff Chen 		bss_co_2040->header.type = cpu_to_le16(WLAN_EID_BSS_COEX_2040);
38473b01e57SJeff Chen 		bss_co_2040->header.len =
38573b01e57SJeff Chen 		       cpu_to_le16(sizeof(bss_co_2040->bss_co_2040));
38673b01e57SJeff Chen 
38773b01e57SJeff Chen 		memcpy((u8 *)bss_co_2040 +
38873b01e57SJeff Chen 		       sizeof(struct nxpwifi_ie_types_header),
38973b01e57SJeff Chen 		       bss_desc->bcn_bss_co_2040 +
39073b01e57SJeff Chen 		       sizeof(struct element),
39173b01e57SJeff Chen 		       le16_to_cpu(bss_co_2040->header.len));
39273b01e57SJeff Chen 
39373b01e57SJeff Chen 		*buffer += sizeof(struct nxpwifi_ie_types_2040bssco);
39473b01e57SJeff Chen 		ret_len += sizeof(struct nxpwifi_ie_types_2040bssco);
39573b01e57SJeff Chen 	}
39673b01e57SJeff Chen 
39773b01e57SJeff Chen 	if (bss_desc->bcn_ext_cap) {
39873b01e57SJeff Chen 		u8 *ext_capab;
39973b01e57SJeff Chen 
40073b01e57SJeff Chen 		hdr = (void *)bss_desc->bcn_ext_cap;
40173b01e57SJeff Chen 
40273b01e57SJeff Chen 		ext_capab = (u8 *)cfg80211_find_ie(WLAN_EID_EXT_CAPABILITY, priv->gen_ie_buf,
40373b01e57SJeff Chen 				  priv->gen_ie_buf_len);
40473b01e57SJeff Chen 		if (ext_capab) {
40573b01e57SJeff Chen 			ext_capab += 2;
40673b01e57SJeff Chen 		} else {
40773b01e57SJeff Chen 			ext_cap = (struct nxpwifi_ie_types_extcap *)*buffer;
40873b01e57SJeff Chen 			memset(ext_cap, 0, sizeof(struct nxpwifi_ie_types_extcap) + hdr->datalen);
40973b01e57SJeff Chen 			ext_cap->header.type = cpu_to_le16(WLAN_EID_EXT_CAPABILITY);
41073b01e57SJeff Chen 			ext_cap->header.len = cpu_to_le16(hdr->datalen);
41173b01e57SJeff Chen 			ext_capab = ext_cap->ext_capab;
41273b01e57SJeff Chen 			*buffer += sizeof(struct nxpwifi_ie_types_extcap) + hdr->datalen;
41373b01e57SJeff Chen 			ret_len += sizeof(struct nxpwifi_ie_types_extcap) + hdr->datalen;
41473b01e57SJeff Chen 		}
41573b01e57SJeff Chen 
41673b01e57SJeff Chen 		if (hdr->datalen > 3 &&
41773b01e57SJeff Chen 		    ext_capab[3] & WLAN_EXT_CAPA4_INTERWORKING_ENABLED)
41873b01e57SJeff Chen 			priv->hs2_enabled = true;
41973b01e57SJeff Chen 		else
42073b01e57SJeff Chen 			priv->hs2_enabled = false;
42173b01e57SJeff Chen 
42273b01e57SJeff Chen 		if (nxpwifi_is_11ax_twt_supported(priv, bss_desc))
42373b01e57SJeff Chen 			ext_capab[9] |=
42473b01e57SJeff Chen 			WLAN_EXT_CAPA10_TWT_REQUESTER_SUPPORT;
42573b01e57SJeff Chen 	}
42673b01e57SJeff Chen 	return ret_len;
42773b01e57SJeff Chen }
42873b01e57SJeff Chen 
42973b01e57SJeff Chen /* Check if pointer is a valid Tx BA stream entry. */
43073b01e57SJeff Chen static bool
nxpwifi_is_tx_ba_stream_ptr_valid(struct nxpwifi_private * priv,struct nxpwifi_tx_ba_stream_tbl * tx_tbl_ptr)43173b01e57SJeff Chen nxpwifi_is_tx_ba_stream_ptr_valid(struct nxpwifi_private *priv,
43273b01e57SJeff Chen 				  struct nxpwifi_tx_ba_stream_tbl *tx_tbl_ptr)
43373b01e57SJeff Chen {
43473b01e57SJeff Chen 	struct nxpwifi_tx_ba_stream_tbl *tx_ba_tsr_tbl;
43573b01e57SJeff Chen 	bool ret = false;
43673b01e57SJeff Chen 	int tid;
43773b01e57SJeff Chen 
43873b01e57SJeff Chen 	tid = tx_tbl_ptr->tid;
43973b01e57SJeff Chen 	guard(rcu)();
44073b01e57SJeff Chen 	list_for_each_entry_rcu(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr[tid], list) {
44173b01e57SJeff Chen 		if (tx_ba_tsr_tbl == tx_tbl_ptr) {
44273b01e57SJeff Chen 			ret = true;
44373b01e57SJeff Chen 			break;
44473b01e57SJeff Chen 		}
44573b01e57SJeff Chen 	}
44673b01e57SJeff Chen 	return ret;
44773b01e57SJeff Chen }
44873b01e57SJeff Chen 
44973b01e57SJeff Chen /* Delete a Tx BA stream entry (after validating pointer). */
45073b01e57SJeff Chen void
nxpwifi_11n_delete_tx_ba_stream_tbl_entry(struct nxpwifi_private * priv,struct nxpwifi_tx_ba_stream_tbl * tbl)45173b01e57SJeff Chen nxpwifi_11n_delete_tx_ba_stream_tbl_entry(struct nxpwifi_private *priv,
45273b01e57SJeff Chen 					  struct nxpwifi_tx_ba_stream_tbl *tbl)
45373b01e57SJeff Chen {
454*00c786a7SJeff Chen 	if (!tbl || nxpwifi_is_tx_ba_stream_ptr_valid(priv, tbl))
45573b01e57SJeff Chen 		return;
45673b01e57SJeff Chen 
45773b01e57SJeff Chen 	nxpwifi_dbg(priv->adapter, INFO,
45873b01e57SJeff Chen 		    "info: tx_ba_tsr_tbl %p\n", tbl);
45973b01e57SJeff Chen 
46073b01e57SJeff Chen 	list_del_rcu(&tbl->list);
46173b01e57SJeff Chen 	kfree_rcu(tbl, rcu);
46273b01e57SJeff Chen }
46373b01e57SJeff Chen 
46473b01e57SJeff Chen /* Delete all entries in Tx BA stream table. */
nxpwifi_11n_delete_all_tx_ba_stream_tbl(struct nxpwifi_private * priv)46573b01e57SJeff Chen void nxpwifi_11n_delete_all_tx_ba_stream_tbl(struct nxpwifi_private *priv)
46673b01e57SJeff Chen {
46773b01e57SJeff Chen 	int i;
46873b01e57SJeff Chen 	struct nxpwifi_tx_ba_stream_tbl *del_tbl_ptr, *tmp_node;
46973b01e57SJeff Chen 
47073b01e57SJeff Chen 	for (i = 0; i < MAX_NUM_TID; i++) {
47173b01e57SJeff Chen 		spin_lock_bh(&priv->tx_ba_stream_tbl_lock[i]);
47273b01e57SJeff Chen 		list_for_each_entry_safe(del_tbl_ptr, tmp_node,
47373b01e57SJeff Chen 					 &priv->tx_ba_stream_tbl_ptr[i], list)
47473b01e57SJeff Chen 			nxpwifi_11n_delete_tx_ba_stream_tbl_entry(priv, del_tbl_ptr);
47573b01e57SJeff Chen 		spin_unlock_bh(&priv->tx_ba_stream_tbl_lock[i]);
47673b01e57SJeff Chen 
47773b01e57SJeff Chen 		INIT_LIST_HEAD(&priv->tx_ba_stream_tbl_ptr[i]);
47873b01e57SJeff Chen 
47973b01e57SJeff Chen 		priv->aggr_prio_tbl[i].ampdu_ap =
48073b01e57SJeff Chen 			priv->aggr_prio_tbl[i].ampdu_user;
48173b01e57SJeff Chen 	}
48273b01e57SJeff Chen }
48373b01e57SJeff Chen 
48473b01e57SJeff Chen /* Return BA stream entry for given RA/TID. */
48573b01e57SJeff Chen struct nxpwifi_tx_ba_stream_tbl *
nxpwifi_get_ba_tbl(struct nxpwifi_private * priv,int tid,u8 * ra)48673b01e57SJeff Chen nxpwifi_get_ba_tbl(struct nxpwifi_private *priv, int tid, u8 *ra)
48773b01e57SJeff Chen {
48873b01e57SJeff Chen 	struct nxpwifi_tx_ba_stream_tbl *tx_ba_tsr_tbl = NULL;
48973b01e57SJeff Chen 
49073b01e57SJeff Chen 	list_for_each_entry_rcu(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr[tid], list) {
49173b01e57SJeff Chen 		if (ether_addr_equal_unaligned(tx_ba_tsr_tbl->ra, ra) &&
49273b01e57SJeff Chen 		    tx_ba_tsr_tbl->tid == tid)
49373b01e57SJeff Chen 			return tx_ba_tsr_tbl;
49473b01e57SJeff Chen 	}
49573b01e57SJeff Chen 	return NULL;
49673b01e57SJeff Chen }
49773b01e57SJeff Chen 
49873b01e57SJeff Chen /* Create Tx BA stream entry for given RA/TID. */
nxpwifi_create_ba_tbl(struct nxpwifi_private * priv,u8 * ra,int tid,enum nxpwifi_ba_status ba_status)49973b01e57SJeff Chen void nxpwifi_create_ba_tbl(struct nxpwifi_private *priv, u8 *ra, int tid,
50073b01e57SJeff Chen 			   enum nxpwifi_ba_status ba_status)
50173b01e57SJeff Chen {
50273b01e57SJeff Chen 	struct nxpwifi_tx_ba_stream_tbl *new_node;
50373b01e57SJeff Chen 	struct nxpwifi_ra_list_tbl *ra_list;
50473b01e57SJeff Chen 	int tid_down;
50573b01e57SJeff Chen 	struct nxpwifi_tx_ba_stream_tbl *tx_ba_tbl;
50673b01e57SJeff Chen 
50773b01e57SJeff Chen 	guard(rcu)();
50873b01e57SJeff Chen 	tx_ba_tbl = nxpwifi_get_ba_tbl(priv, tid, ra);
50973b01e57SJeff Chen 
51073b01e57SJeff Chen 	if (!tx_ba_tbl) {
51173b01e57SJeff Chen 		new_node = kzalloc_obj(*new_node, GFP_ATOMIC);
51273b01e57SJeff Chen 		if (!new_node)
51373b01e57SJeff Chen 			return;
51473b01e57SJeff Chen 
51573b01e57SJeff Chen 		tid_down = nxpwifi_wmm_downgrade_tid(priv, tid);
51673b01e57SJeff Chen 		ra_list = nxpwifi_wmm_get_ralist_node(priv, tid_down, ra);
51773b01e57SJeff Chen 		if (ra_list) {
51873b01e57SJeff Chen 			ra_list->ba_status = ba_status;
51973b01e57SJeff Chen 			ra_list->amsdu_in_ampdu = false;
52073b01e57SJeff Chen 		}
52173b01e57SJeff Chen 		INIT_LIST_HEAD(&new_node->list);
52273b01e57SJeff Chen 
52373b01e57SJeff Chen 		new_node->tid = tid;
52473b01e57SJeff Chen 		new_node->ba_status = ba_status;
52573b01e57SJeff Chen 		memcpy(new_node->ra, ra, ETH_ALEN);
52673b01e57SJeff Chen 
52773b01e57SJeff Chen 		spin_lock_bh(&priv->tx_ba_stream_tbl_lock[tid]);
52873b01e57SJeff Chen 		list_add_tail_rcu(&new_node->list, &priv->tx_ba_stream_tbl_ptr[tid]);
52973b01e57SJeff Chen 		spin_unlock_bh(&priv->tx_ba_stream_tbl_lock[tid]);
53073b01e57SJeff Chen 	}
53173b01e57SJeff Chen }
53273b01e57SJeff Chen 
53373b01e57SJeff Chen /* Send ADDBA request to the given TID/RA. */
nxpwifi_send_addba(struct nxpwifi_private * priv,int tid,u8 * peer_mac)53473b01e57SJeff Chen int nxpwifi_send_addba(struct nxpwifi_private *priv, int tid, u8 *peer_mac)
53573b01e57SJeff Chen {
53673b01e57SJeff Chen 	struct host_cmd_ds_11n_addba_req add_ba_req;
53773b01e57SJeff Chen 	u32 tx_win_size = priv->add_ba_param.tx_win_size;
53873b01e57SJeff Chen 	static u8 dialog_tok;
53973b01e57SJeff Chen 	u16 block_ack_param_set;
54073b01e57SJeff Chen 
54173b01e57SJeff Chen 	nxpwifi_dbg(priv->adapter, CMD, "cmd: %s: tid %d\n", __func__, tid);
54273b01e57SJeff Chen 
54373b01e57SJeff Chen 	memset(&add_ba_req, 0, sizeof(add_ba_req));
54473b01e57SJeff Chen 
54573b01e57SJeff Chen 	block_ack_param_set = (u16)((tid << BLOCKACKPARAM_TID_POS) |
54673b01e57SJeff Chen 				    tx_win_size << BLOCKACKPARAM_WINSIZE_POS |
54773b01e57SJeff Chen 				    IMMEDIATE_BLOCK_ACK);
54873b01e57SJeff Chen 
54973b01e57SJeff Chen 	/* enable AMSDU inside AMPDU */
55073b01e57SJeff Chen 	if (priv->add_ba_param.tx_amsdu &&
55173b01e57SJeff Chen 	    priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED)
55273b01e57SJeff Chen 		block_ack_param_set |= IEEE80211_ADDBA_PARAM_AMSDU_MASK;
55373b01e57SJeff Chen 
55473b01e57SJeff Chen 	add_ba_req.block_ack_param_set = cpu_to_le16(block_ack_param_set);
55573b01e57SJeff Chen 	add_ba_req.block_ack_tmo = cpu_to_le16((u16)priv->add_ba_param.timeout);
55673b01e57SJeff Chen 
55773b01e57SJeff Chen 	++dialog_tok;
55873b01e57SJeff Chen 
55973b01e57SJeff Chen 	if (dialog_tok == 0)
56073b01e57SJeff Chen 		dialog_tok = 1;
56173b01e57SJeff Chen 
56273b01e57SJeff Chen 	add_ba_req.dialog_token = dialog_tok;
56373b01e57SJeff Chen 	memcpy(&add_ba_req.peer_mac_addr, peer_mac, ETH_ALEN);
56473b01e57SJeff Chen 
56573b01e57SJeff Chen 	/* We don't wait for the response of this command */
56673b01e57SJeff Chen 	return nxpwifi_send_cmd(priv, HOST_CMD_11N_ADDBA_REQ,
56773b01e57SJeff Chen 			       0, 0, &add_ba_req, false);
56873b01e57SJeff Chen }
56973b01e57SJeff Chen 
57073b01e57SJeff Chen /* Send DELBA request to the given TID/RA. */
nxpwifi_send_delba(struct nxpwifi_private * priv,int tid,u8 * peer_mac,int initiator)57173b01e57SJeff Chen int nxpwifi_send_delba(struct nxpwifi_private *priv, int tid, u8 *peer_mac,
57273b01e57SJeff Chen 		       int initiator)
57373b01e57SJeff Chen {
57473b01e57SJeff Chen 	struct host_cmd_ds_11n_delba delba;
57573b01e57SJeff Chen 	u16 del_ba_param_set;
57673b01e57SJeff Chen 
57773b01e57SJeff Chen 	memset(&delba, 0, sizeof(delba));
57873b01e57SJeff Chen 
57973b01e57SJeff Chen 	del_ba_param_set = tid << DELBA_TID_POS;
58073b01e57SJeff Chen 
58173b01e57SJeff Chen 	if (initiator)
58273b01e57SJeff Chen 		del_ba_param_set |= IEEE80211_DELBA_PARAM_INITIATOR_MASK;
58373b01e57SJeff Chen 	else
58473b01e57SJeff Chen 		del_ba_param_set &= ~IEEE80211_DELBA_PARAM_INITIATOR_MASK;
58573b01e57SJeff Chen 
58673b01e57SJeff Chen 	delba.del_ba_param_set = cpu_to_le16(del_ba_param_set);
58773b01e57SJeff Chen 	memcpy(&delba.peer_mac_addr, peer_mac, ETH_ALEN);
58873b01e57SJeff Chen 
58973b01e57SJeff Chen 	/* We don't wait for the response of this command */
59073b01e57SJeff Chen 	return nxpwifi_send_cmd(priv, HOST_CMD_11N_DELBA,
59173b01e57SJeff Chen 			       HOST_ACT_GEN_SET, 0, &delba, false);
59273b01e57SJeff Chen }
59373b01e57SJeff Chen 
59473b01e57SJeff Chen /* Send DELBA to specific TID. */
nxpwifi_11n_delba(struct nxpwifi_private * priv,int tid)59573b01e57SJeff Chen void nxpwifi_11n_delba(struct nxpwifi_private *priv, int tid)
59673b01e57SJeff Chen {
59773b01e57SJeff Chen 	struct nxpwifi_rx_reorder_tbl *rx_reor_tbl_ptr;
59873b01e57SJeff Chen 	u8 ta[ETH_ALEN];
59973b01e57SJeff Chen 	bool found = false;
60073b01e57SJeff Chen 
60173b01e57SJeff Chen 	rcu_read_lock();
60273b01e57SJeff Chen 	list_for_each_entry_rcu(rx_reor_tbl_ptr, &priv->rx_reorder_tbl_ptr[tid], list) {
60373b01e57SJeff Chen 		if (rx_reor_tbl_ptr->tid == tid) {
60473b01e57SJeff Chen 			memcpy(ta, rx_reor_tbl_ptr->ta, ETH_ALEN);
60573b01e57SJeff Chen 			found = true;
60673b01e57SJeff Chen 			break;
60773b01e57SJeff Chen 		}
60873b01e57SJeff Chen 	}
60973b01e57SJeff Chen 	rcu_read_unlock();
61073b01e57SJeff Chen 
61173b01e57SJeff Chen 	if (found) {
61273b01e57SJeff Chen 		nxpwifi_dbg(priv->adapter, INFO,
61373b01e57SJeff Chen 			    "Send delba to tid=%d, %pM\n", tid, ta);
61473b01e57SJeff Chen 		nxpwifi_send_delba(priv, tid, ta, 0);
61573b01e57SJeff Chen 	}
61673b01e57SJeff Chen }
61773b01e57SJeff Chen 
61873b01e57SJeff Chen /* Handle DELBA event; remove BA stream. */
nxpwifi_11n_delete_ba_stream(struct nxpwifi_private * priv,u8 * del_ba)61973b01e57SJeff Chen void nxpwifi_11n_delete_ba_stream(struct nxpwifi_private *priv, u8 *del_ba)
62073b01e57SJeff Chen {
62173b01e57SJeff Chen 	struct host_cmd_ds_11n_delba *cmd_del_ba =
62273b01e57SJeff Chen 		(struct host_cmd_ds_11n_delba *)del_ba;
62373b01e57SJeff Chen 	u16 del_ba_param_set = le16_to_cpu(cmd_del_ba->del_ba_param_set);
62473b01e57SJeff Chen 	int tid;
62573b01e57SJeff Chen 
62673b01e57SJeff Chen 	tid = del_ba_param_set >> DELBA_TID_POS;
62773b01e57SJeff Chen 
62873b01e57SJeff Chen 	nxpwifi_del_ba_tbl(priv, tid, cmd_del_ba->peer_mac_addr,
62973b01e57SJeff Chen 			   TYPE_DELBA_RECEIVE, INITIATOR_BIT(del_ba_param_set));
63073b01e57SJeff Chen }
63173b01e57SJeff Chen 
63273b01e57SJeff Chen /* Retrieve Rx reordering table. */
nxpwifi_get_rx_reorder_tbl(struct nxpwifi_private * priv,struct nxpwifi_ds_rx_reorder_tbl * buf)63373b01e57SJeff Chen int nxpwifi_get_rx_reorder_tbl(struct nxpwifi_private *priv,
63473b01e57SJeff Chen 			       struct nxpwifi_ds_rx_reorder_tbl *buf)
63573b01e57SJeff Chen {
63673b01e57SJeff Chen 	int i, j;
63773b01e57SJeff Chen 	struct nxpwifi_ds_rx_reorder_tbl *rx_reo_tbl = buf;
63873b01e57SJeff Chen 	struct nxpwifi_rx_reorder_tbl *rx_reorder_tbl_ptr;
63973b01e57SJeff Chen 	int count = 0;
64073b01e57SJeff Chen 
64173b01e57SJeff Chen 	guard(rcu)();
64273b01e57SJeff Chen 	for (j = 0; j < MAX_NUM_TID; j++) {
64373b01e57SJeff Chen 		list_for_each_entry_rcu(rx_reorder_tbl_ptr,
64473b01e57SJeff Chen 					&priv->rx_reorder_tbl_ptr[j],
64573b01e57SJeff Chen 					list) {
64673b01e57SJeff Chen 			rx_reo_tbl->tid = (u16)rx_reorder_tbl_ptr->tid;
64773b01e57SJeff Chen 			memcpy(rx_reo_tbl->ta, rx_reorder_tbl_ptr->ta, ETH_ALEN);
64873b01e57SJeff Chen 			rx_reo_tbl->start_win = rx_reorder_tbl_ptr->start_win;
64973b01e57SJeff Chen 			rx_reo_tbl->win_size = rx_reorder_tbl_ptr->win_size;
65073b01e57SJeff Chen 			for (i = 0; i < rx_reorder_tbl_ptr->win_size; ++i) {
65173b01e57SJeff Chen 				if (rx_reorder_tbl_ptr->rx_reorder_ptr[i])
65273b01e57SJeff Chen 					rx_reo_tbl->buffer[i] = true;
65373b01e57SJeff Chen 				else
65473b01e57SJeff Chen 					rx_reo_tbl->buffer[i] = false;
65573b01e57SJeff Chen 			}
65673b01e57SJeff Chen 			rx_reo_tbl++;
65773b01e57SJeff Chen 			count++;
65873b01e57SJeff Chen 
65973b01e57SJeff Chen 			if (count >= NXPWIFI_MAX_RX_BASTREAM_SUPPORTED)
66073b01e57SJeff Chen 				return count;
66173b01e57SJeff Chen 		}
66273b01e57SJeff Chen 	}
66373b01e57SJeff Chen 
66473b01e57SJeff Chen 	return count;
66573b01e57SJeff Chen }
66673b01e57SJeff Chen 
66773b01e57SJeff Chen /* Retrieve Tx BA stream table. */
nxpwifi_get_tx_ba_stream_tbl(struct nxpwifi_private * priv,struct nxpwifi_ds_tx_ba_stream_tbl * buf)66873b01e57SJeff Chen int nxpwifi_get_tx_ba_stream_tbl(struct nxpwifi_private *priv,
66973b01e57SJeff Chen 				 struct nxpwifi_ds_tx_ba_stream_tbl *buf)
67073b01e57SJeff Chen {
67173b01e57SJeff Chen 	struct nxpwifi_tx_ba_stream_tbl *tx_ba_tsr_tbl;
67273b01e57SJeff Chen 	struct nxpwifi_ds_tx_ba_stream_tbl *rx_reo_tbl = buf;
67373b01e57SJeff Chen 	int count = 0;
67473b01e57SJeff Chen 	int i;
67573b01e57SJeff Chen 
67673b01e57SJeff Chen 	guard(rcu)();
67773b01e57SJeff Chen 	for (i = 0; i < MAX_NUM_TID; i++) {
67873b01e57SJeff Chen 		list_for_each_entry_rcu(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr[i], list) {
67973b01e57SJeff Chen 			rx_reo_tbl->tid = (u16)tx_ba_tsr_tbl->tid;
68073b01e57SJeff Chen 			nxpwifi_dbg(priv->adapter, DATA, "data: %s tid=%d\n",
68173b01e57SJeff Chen 				    __func__, rx_reo_tbl->tid);
68273b01e57SJeff Chen 			memcpy(rx_reo_tbl->ra, tx_ba_tsr_tbl->ra, ETH_ALEN);
68373b01e57SJeff Chen 			rx_reo_tbl->amsdu = tx_ba_tsr_tbl->amsdu;
68473b01e57SJeff Chen 			rx_reo_tbl++;
68573b01e57SJeff Chen 			count++;
68673b01e57SJeff Chen 			if (count >= NXPWIFI_MAX_TX_BASTREAM_SUPPORTED)
68773b01e57SJeff Chen 				return count;
68873b01e57SJeff Chen 		}
68973b01e57SJeff Chen 	}
69073b01e57SJeff Chen 
69173b01e57SJeff Chen 	return count;
69273b01e57SJeff Chen }
69373b01e57SJeff Chen 
69473b01e57SJeff Chen /* Delete Tx BA stream entry by RA. */
nxpwifi_del_tx_ba_stream_tbl_by_ra(struct nxpwifi_private * priv,u8 * ra)69573b01e57SJeff Chen void nxpwifi_del_tx_ba_stream_tbl_by_ra(struct nxpwifi_private *priv, u8 *ra)
69673b01e57SJeff Chen {
697*00c786a7SJeff Chen 	struct nxpwifi_tx_ba_stream_tbl *tbl, *tmp;
69873b01e57SJeff Chen 	int i;
69973b01e57SJeff Chen 
70073b01e57SJeff Chen 	if (!ra)
70173b01e57SJeff Chen 		return;
70273b01e57SJeff Chen 
70373b01e57SJeff Chen 	for (i = 0; i < MAX_NUM_TID; i++) {
70473b01e57SJeff Chen 		spin_lock_bh(&priv->tx_ba_stream_tbl_lock[i]);
705*00c786a7SJeff Chen 		list_for_each_entry_safe(tbl, tmp,
706*00c786a7SJeff Chen 					 &priv->tx_ba_stream_tbl_ptr[i], list)
70773b01e57SJeff Chen 			if (!memcmp(tbl->ra, ra, ETH_ALEN))
70873b01e57SJeff Chen 				nxpwifi_11n_delete_tx_ba_stream_tbl_entry(priv, tbl);
70973b01e57SJeff Chen 
71073b01e57SJeff Chen 		spin_unlock_bh(&priv->tx_ba_stream_tbl_lock[i]);
71173b01e57SJeff Chen 	}
71273b01e57SJeff Chen }
71373b01e57SJeff Chen 
71473b01e57SJeff Chen /* Initialize BlockAck parameters. */
nxpwifi_set_ba_params(struct nxpwifi_private * priv)71573b01e57SJeff Chen void nxpwifi_set_ba_params(struct nxpwifi_private *priv)
71673b01e57SJeff Chen {
71773b01e57SJeff Chen 	priv->add_ba_param.timeout = NXPWIFI_DEFAULT_BLOCK_ACK_TIMEOUT;
71873b01e57SJeff Chen 
71973b01e57SJeff Chen 	if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_UAP) {
72073b01e57SJeff Chen 		priv->add_ba_param.tx_win_size =
72173b01e57SJeff Chen 			NXPWIFI_UAP_AMPDU_DEF_TXWINSIZE;
72273b01e57SJeff Chen 		priv->add_ba_param.rx_win_size =
72373b01e57SJeff Chen 			NXPWIFI_UAP_AMPDU_DEF_RXWINSIZE;
72473b01e57SJeff Chen 	} else {
72573b01e57SJeff Chen 		priv->add_ba_param.tx_win_size =
72673b01e57SJeff Chen 			NXPWIFI_STA_AMPDU_DEF_TXWINSIZE;
72773b01e57SJeff Chen 		priv->add_ba_param.rx_win_size =
72873b01e57SJeff Chen 			NXPWIFI_STA_AMPDU_DEF_RXWINSIZE;
72973b01e57SJeff Chen 	}
73073b01e57SJeff Chen 
73173b01e57SJeff Chen 	priv->add_ba_param.tx_amsdu = true;
73273b01e57SJeff Chen 	priv->add_ba_param.rx_amsdu = true;
73373b01e57SJeff Chen }
73473b01e57SJeff Chen 
nxpwifi_get_sec_chan_offset(int chan)73573b01e57SJeff Chen u8 nxpwifi_get_sec_chan_offset(int chan)
73673b01e57SJeff Chen {
73773b01e57SJeff Chen 	u8 sec_offset;
73873b01e57SJeff Chen 
73973b01e57SJeff Chen 	switch (chan) {
74073b01e57SJeff Chen 	case 36:
74173b01e57SJeff Chen 	case 44:
74273b01e57SJeff Chen 	case 52:
74373b01e57SJeff Chen 	case 60:
74473b01e57SJeff Chen 	case 100:
74573b01e57SJeff Chen 	case 108:
74673b01e57SJeff Chen 	case 116:
74773b01e57SJeff Chen 	case 124:
74873b01e57SJeff Chen 	case 132:
74973b01e57SJeff Chen 	case 140:
75073b01e57SJeff Chen 	case 149:
75173b01e57SJeff Chen 	case 157:
75273b01e57SJeff Chen 	case 173:
75373b01e57SJeff Chen 		sec_offset = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
75473b01e57SJeff Chen 		break;
75573b01e57SJeff Chen 	case 40:
75673b01e57SJeff Chen 	case 48:
75773b01e57SJeff Chen 	case 56:
75873b01e57SJeff Chen 	case 64:
75973b01e57SJeff Chen 	case 104:
76073b01e57SJeff Chen 	case 112:
76173b01e57SJeff Chen 	case 120:
76273b01e57SJeff Chen 	case 128:
76373b01e57SJeff Chen 	case 136:
76473b01e57SJeff Chen 	case 144:
76573b01e57SJeff Chen 	case 153:
76673b01e57SJeff Chen 	case 161:
76773b01e57SJeff Chen 	case 169:
76873b01e57SJeff Chen 	case 177:
76973b01e57SJeff Chen 		sec_offset = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
77073b01e57SJeff Chen 		break;
77173b01e57SJeff Chen 	case 165:
77273b01e57SJeff Chen 	default:
77373b01e57SJeff Chen 		sec_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE;
77473b01e57SJeff Chen 		break;
77573b01e57SJeff Chen 	}
77673b01e57SJeff Chen 
77773b01e57SJeff Chen 	return sec_offset;
77873b01e57SJeff Chen }
77973b01e57SJeff Chen 
78073b01e57SJeff Chen /* Send DELBA to entries in the Tx BA stream table. */
78173b01e57SJeff Chen static void
nxpwifi_send_delba_txbastream_tbl(struct nxpwifi_private * priv,u8 tid)78273b01e57SJeff Chen nxpwifi_send_delba_txbastream_tbl(struct nxpwifi_private *priv, u8 tid)
78373b01e57SJeff Chen {
78473b01e57SJeff Chen 	struct nxpwifi_adapter *adapter = priv->adapter;
78573b01e57SJeff Chen 	struct nxpwifi_tx_ba_stream_tbl *tx_ba_stream_tbl_ptr;
78673b01e57SJeff Chen 
78773b01e57SJeff Chen 	guard(rcu)();
78873b01e57SJeff Chen 	list_for_each_entry_rcu(tx_ba_stream_tbl_ptr,
78973b01e57SJeff Chen 				&priv->tx_ba_stream_tbl_ptr[tid], list) {
79073b01e57SJeff Chen 		if (tx_ba_stream_tbl_ptr->ba_status == BA_SETUP_COMPLETE) {
79173b01e57SJeff Chen 			if (tid == tx_ba_stream_tbl_ptr->tid) {
79273b01e57SJeff Chen 				nxpwifi_dbg(adapter, INFO,
79373b01e57SJeff Chen 					    "Tx:Send delba to tid=%d, %pM\n", tid,
79473b01e57SJeff Chen 					    tx_ba_stream_tbl_ptr->ra);
79573b01e57SJeff Chen 				nxpwifi_send_delba(priv,
79673b01e57SJeff Chen 						   tx_ba_stream_tbl_ptr->tid,
79773b01e57SJeff Chen 						   tx_ba_stream_tbl_ptr->ra, 1);
79873b01e57SJeff Chen 				break;
79973b01e57SJeff Chen 			}
80073b01e57SJeff Chen 		}
80173b01e57SJeff Chen 	}
80273b01e57SJeff Chen }
80373b01e57SJeff Chen 
80473b01e57SJeff Chen /*
80573b01e57SJeff Chen  * Update tx_win_size for all interfaces and send DELBA when it changes.
80673b01e57SJeff Chen  */
nxpwifi_update_ampdu_txwinsize(struct nxpwifi_adapter * adapter)80773b01e57SJeff Chen void nxpwifi_update_ampdu_txwinsize(struct nxpwifi_adapter *adapter)
80873b01e57SJeff Chen {
80973b01e57SJeff Chen 	u8 i, j;
81073b01e57SJeff Chen 	u32 tx_win_size;
81173b01e57SJeff Chen 	struct nxpwifi_private *priv;
81273b01e57SJeff Chen 
81373b01e57SJeff Chen 	for (i = 0; i < adapter->priv_num; i++) {
81473b01e57SJeff Chen 		priv = adapter->priv[i];
81573b01e57SJeff Chen 		tx_win_size = priv->add_ba_param.tx_win_size;
81673b01e57SJeff Chen 
81773b01e57SJeff Chen 		if (priv->bss_type == NXPWIFI_BSS_TYPE_STA)
81873b01e57SJeff Chen 			priv->add_ba_param.tx_win_size =
81973b01e57SJeff Chen 				NXPWIFI_STA_AMPDU_DEF_TXWINSIZE;
82073b01e57SJeff Chen 
82173b01e57SJeff Chen 		if (priv->bss_type == NXPWIFI_BSS_TYPE_UAP)
82273b01e57SJeff Chen 			priv->add_ba_param.tx_win_size =
82373b01e57SJeff Chen 				NXPWIFI_UAP_AMPDU_DEF_TXWINSIZE;
82473b01e57SJeff Chen 
82573b01e57SJeff Chen 		if (adapter->coex_win_size) {
82673b01e57SJeff Chen 			if (adapter->coex_tx_win_size)
82773b01e57SJeff Chen 				priv->add_ba_param.tx_win_size =
82873b01e57SJeff Chen 					adapter->coex_tx_win_size;
82973b01e57SJeff Chen 		}
83073b01e57SJeff Chen 
83173b01e57SJeff Chen 		if (tx_win_size != priv->add_ba_param.tx_win_size) {
83273b01e57SJeff Chen 			if (!priv->media_connected)
83373b01e57SJeff Chen 				continue;
83473b01e57SJeff Chen 			for (j = 0; j < MAX_NUM_TID; j++)
83573b01e57SJeff Chen 				nxpwifi_send_delba_txbastream_tbl(priv, j);
83673b01e57SJeff Chen 		}
83773b01e57SJeff Chen 	}
83873b01e57SJeff Chen }
839