162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * NXP Wireless LAN device driver: 802.11n
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright 2011-2020 NXP
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include "decl.h"
962306a36Sopenharmony_ci#include "ioctl.h"
1062306a36Sopenharmony_ci#include "util.h"
1162306a36Sopenharmony_ci#include "fw.h"
1262306a36Sopenharmony_ci#include "main.h"
1362306a36Sopenharmony_ci#include "wmm.h"
1462306a36Sopenharmony_ci#include "11n.h"
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci/*
1762306a36Sopenharmony_ci * Fills HT capability information field, AMPDU Parameters field, HT extended
1862306a36Sopenharmony_ci * capability field, and supported MCS set fields.
1962306a36Sopenharmony_ci *
2062306a36Sopenharmony_ci * HT capability information field, AMPDU Parameters field, supported MCS set
2162306a36Sopenharmony_ci * fields are retrieved from cfg80211 stack
2262306a36Sopenharmony_ci *
2362306a36Sopenharmony_ci * RD responder bit to set to clear in the extended capability header.
2462306a36Sopenharmony_ci */
2562306a36Sopenharmony_ciint mwifiex_fill_cap_info(struct mwifiex_private *priv, u8 radio_type,
2662306a36Sopenharmony_ci			  struct ieee80211_ht_cap *ht_cap)
2762306a36Sopenharmony_ci{
2862306a36Sopenharmony_ci	uint16_t ht_ext_cap = le16_to_cpu(ht_cap->extended_ht_cap_info);
2962306a36Sopenharmony_ci	struct ieee80211_supported_band *sband =
3062306a36Sopenharmony_ci					priv->wdev.wiphy->bands[radio_type];
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	if (WARN_ON_ONCE(!sband)) {
3362306a36Sopenharmony_ci		mwifiex_dbg(priv->adapter, ERROR, "Invalid radio type!\n");
3462306a36Sopenharmony_ci		return -EINVAL;
3562306a36Sopenharmony_ci	}
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	ht_cap->ampdu_params_info =
3862306a36Sopenharmony_ci		(sband->ht_cap.ampdu_factor &
3962306a36Sopenharmony_ci		 IEEE80211_HT_AMPDU_PARM_FACTOR) |
4062306a36Sopenharmony_ci		((sband->ht_cap.ampdu_density <<
4162306a36Sopenharmony_ci		 IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT) &
4262306a36Sopenharmony_ci		 IEEE80211_HT_AMPDU_PARM_DENSITY);
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	memcpy((u8 *)&ht_cap->mcs, &sband->ht_cap.mcs,
4562306a36Sopenharmony_ci	       sizeof(sband->ht_cap.mcs));
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	if (priv->bss_mode == NL80211_IFTYPE_STATION ||
4862306a36Sopenharmony_ci	    (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 &&
4962306a36Sopenharmony_ci	     (priv->adapter->sec_chan_offset !=
5062306a36Sopenharmony_ci					IEEE80211_HT_PARAM_CHA_SEC_NONE)))
5162306a36Sopenharmony_ci		/* Set MCS32 for infra mode or ad-hoc mode with 40MHz support */
5262306a36Sopenharmony_ci		SETHT_MCS32(ht_cap->mcs.rx_mask);
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	/* Clear RD responder bit */
5562306a36Sopenharmony_ci	ht_ext_cap &= ~IEEE80211_HT_EXT_CAP_RD_RESPONDER;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	ht_cap->cap_info = cpu_to_le16(sband->ht_cap.cap);
5862306a36Sopenharmony_ci	ht_cap->extended_ht_cap_info = cpu_to_le16(ht_ext_cap);
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	if (ISSUPP_BEAMFORMING(priv->adapter->hw_dot_11n_dev_cap))
6162306a36Sopenharmony_ci		ht_cap->tx_BF_cap_info = cpu_to_le32(MWIFIEX_DEF_11N_TX_BF_CAP);
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	return 0;
6462306a36Sopenharmony_ci}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci/*
6762306a36Sopenharmony_ci * This function returns the pointer to an entry in BA Stream
6862306a36Sopenharmony_ci * table which matches the requested BA status.
6962306a36Sopenharmony_ci */
7062306a36Sopenharmony_cistatic struct mwifiex_tx_ba_stream_tbl *
7162306a36Sopenharmony_cimwifiex_get_ba_status(struct mwifiex_private *priv,
7262306a36Sopenharmony_ci		      enum mwifiex_ba_status ba_status)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	struct mwifiex_tx_ba_stream_tbl *tx_ba_tsr_tbl;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	spin_lock_bh(&priv->tx_ba_stream_tbl_lock);
7762306a36Sopenharmony_ci	list_for_each_entry(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr, list) {
7862306a36Sopenharmony_ci		if (tx_ba_tsr_tbl->ba_status == ba_status) {
7962306a36Sopenharmony_ci			spin_unlock_bh(&priv->tx_ba_stream_tbl_lock);
8062306a36Sopenharmony_ci			return tx_ba_tsr_tbl;
8162306a36Sopenharmony_ci		}
8262306a36Sopenharmony_ci	}
8362306a36Sopenharmony_ci	spin_unlock_bh(&priv->tx_ba_stream_tbl_lock);
8462306a36Sopenharmony_ci	return NULL;
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci/*
8862306a36Sopenharmony_ci * This function handles the command response of delete a block
8962306a36Sopenharmony_ci * ack request.
9062306a36Sopenharmony_ci *
9162306a36Sopenharmony_ci * The function checks the response success status and takes action
9262306a36Sopenharmony_ci * accordingly (send an add BA request in case of success, or recreate
9362306a36Sopenharmony_ci * the deleted stream in case of failure, if the add BA was also
9462306a36Sopenharmony_ci * initiated by us).
9562306a36Sopenharmony_ci */
9662306a36Sopenharmony_ciint mwifiex_ret_11n_delba(struct mwifiex_private *priv,
9762306a36Sopenharmony_ci			  struct host_cmd_ds_command *resp)
9862306a36Sopenharmony_ci{
9962306a36Sopenharmony_ci	int tid;
10062306a36Sopenharmony_ci	struct mwifiex_tx_ba_stream_tbl *tx_ba_tbl;
10162306a36Sopenharmony_ci	struct host_cmd_ds_11n_delba *del_ba = &resp->params.del_ba;
10262306a36Sopenharmony_ci	uint16_t del_ba_param_set = le16_to_cpu(del_ba->del_ba_param_set);
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	tid = del_ba_param_set >> DELBA_TID_POS;
10562306a36Sopenharmony_ci	if (del_ba->del_result == BA_RESULT_SUCCESS) {
10662306a36Sopenharmony_ci		mwifiex_del_ba_tbl(priv, tid, del_ba->peer_mac_addr,
10762306a36Sopenharmony_ci				   TYPE_DELBA_SENT,
10862306a36Sopenharmony_ci				   INITIATOR_BIT(del_ba_param_set));
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci		tx_ba_tbl = mwifiex_get_ba_status(priv, BA_SETUP_INPROGRESS);
11162306a36Sopenharmony_ci		if (tx_ba_tbl)
11262306a36Sopenharmony_ci			mwifiex_send_addba(priv, tx_ba_tbl->tid,
11362306a36Sopenharmony_ci					   tx_ba_tbl->ra);
11462306a36Sopenharmony_ci	} else { /*
11562306a36Sopenharmony_ci		  * In case of failure, recreate the deleted stream in case
11662306a36Sopenharmony_ci		  * we initiated the DELBA
11762306a36Sopenharmony_ci		  */
11862306a36Sopenharmony_ci		if (!INITIATOR_BIT(del_ba_param_set))
11962306a36Sopenharmony_ci			return 0;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci		mwifiex_create_ba_tbl(priv, del_ba->peer_mac_addr, tid,
12262306a36Sopenharmony_ci				      BA_SETUP_INPROGRESS);
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci		tx_ba_tbl = mwifiex_get_ba_status(priv, BA_SETUP_INPROGRESS);
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci		if (tx_ba_tbl)
12762306a36Sopenharmony_ci			mwifiex_del_ba_tbl(priv, tx_ba_tbl->tid, tx_ba_tbl->ra,
12862306a36Sopenharmony_ci					   TYPE_DELBA_SENT, true);
12962306a36Sopenharmony_ci	}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	return 0;
13262306a36Sopenharmony_ci}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci/*
13562306a36Sopenharmony_ci * This function handles the command response of add a block
13662306a36Sopenharmony_ci * ack request.
13762306a36Sopenharmony_ci *
13862306a36Sopenharmony_ci * Handling includes changing the header fields to CPU formats, checking
13962306a36Sopenharmony_ci * the response success status and taking actions accordingly (delete the
14062306a36Sopenharmony_ci * BA stream table in case of failure).
14162306a36Sopenharmony_ci */
14262306a36Sopenharmony_ciint mwifiex_ret_11n_addba_req(struct mwifiex_private *priv,
14362306a36Sopenharmony_ci			      struct host_cmd_ds_command *resp)
14462306a36Sopenharmony_ci{
14562306a36Sopenharmony_ci	int tid, tid_down;
14662306a36Sopenharmony_ci	struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = &resp->params.add_ba_rsp;
14762306a36Sopenharmony_ci	struct mwifiex_tx_ba_stream_tbl *tx_ba_tbl;
14862306a36Sopenharmony_ci	struct mwifiex_ra_list_tbl *ra_list;
14962306a36Sopenharmony_ci	u16 block_ack_param_set = le16_to_cpu(add_ba_rsp->block_ack_param_set);
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	add_ba_rsp->ssn = cpu_to_le16((le16_to_cpu(add_ba_rsp->ssn))
15262306a36Sopenharmony_ci			& SSN_MASK);
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	tid = (block_ack_param_set & IEEE80211_ADDBA_PARAM_TID_MASK)
15562306a36Sopenharmony_ci	       >> BLOCKACKPARAM_TID_POS;
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	tid_down = mwifiex_wmm_downgrade_tid(priv, tid);
15862306a36Sopenharmony_ci	ra_list = mwifiex_wmm_get_ralist_node(priv, tid_down, add_ba_rsp->
15962306a36Sopenharmony_ci		peer_mac_addr);
16062306a36Sopenharmony_ci	if (le16_to_cpu(add_ba_rsp->status_code) != BA_RESULT_SUCCESS) {
16162306a36Sopenharmony_ci		if (ra_list) {
16262306a36Sopenharmony_ci			ra_list->ba_status = BA_SETUP_NONE;
16362306a36Sopenharmony_ci			ra_list->amsdu_in_ampdu = false;
16462306a36Sopenharmony_ci		}
16562306a36Sopenharmony_ci		mwifiex_del_ba_tbl(priv, tid, add_ba_rsp->peer_mac_addr,
16662306a36Sopenharmony_ci				   TYPE_DELBA_SENT, true);
16762306a36Sopenharmony_ci		if (add_ba_rsp->add_rsp_result != BA_RESULT_TIMEOUT)
16862306a36Sopenharmony_ci			priv->aggr_prio_tbl[tid].ampdu_ap =
16962306a36Sopenharmony_ci				BA_STREAM_NOT_ALLOWED;
17062306a36Sopenharmony_ci		return 0;
17162306a36Sopenharmony_ci	}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	tx_ba_tbl = mwifiex_get_ba_tbl(priv, tid, add_ba_rsp->peer_mac_addr);
17462306a36Sopenharmony_ci	if (tx_ba_tbl) {
17562306a36Sopenharmony_ci		mwifiex_dbg(priv->adapter, EVENT, "info: BA stream complete\n");
17662306a36Sopenharmony_ci		tx_ba_tbl->ba_status = BA_SETUP_COMPLETE;
17762306a36Sopenharmony_ci		if ((block_ack_param_set & BLOCKACKPARAM_AMSDU_SUPP_MASK) &&
17862306a36Sopenharmony_ci		    priv->add_ba_param.tx_amsdu &&
17962306a36Sopenharmony_ci		    (priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED))
18062306a36Sopenharmony_ci			tx_ba_tbl->amsdu = true;
18162306a36Sopenharmony_ci		else
18262306a36Sopenharmony_ci			tx_ba_tbl->amsdu = false;
18362306a36Sopenharmony_ci		if (ra_list) {
18462306a36Sopenharmony_ci			ra_list->amsdu_in_ampdu = tx_ba_tbl->amsdu;
18562306a36Sopenharmony_ci			ra_list->ba_status = BA_SETUP_COMPLETE;
18662306a36Sopenharmony_ci		}
18762306a36Sopenharmony_ci	} else {
18862306a36Sopenharmony_ci		mwifiex_dbg(priv->adapter, ERROR, "BA stream not created\n");
18962306a36Sopenharmony_ci	}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	return 0;
19262306a36Sopenharmony_ci}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci/*
19562306a36Sopenharmony_ci * This function prepares command of reconfigure Tx buffer.
19662306a36Sopenharmony_ci *
19762306a36Sopenharmony_ci * Preparation includes -
19862306a36Sopenharmony_ci *      - Setting command ID, action and proper size
19962306a36Sopenharmony_ci *      - Setting Tx buffer size (for SET only)
20062306a36Sopenharmony_ci *      - Ensuring correct endian-ness
20162306a36Sopenharmony_ci */
20262306a36Sopenharmony_ciint mwifiex_cmd_recfg_tx_buf(struct mwifiex_private *priv,
20362306a36Sopenharmony_ci			     struct host_cmd_ds_command *cmd, int cmd_action,
20462306a36Sopenharmony_ci			     u16 *buf_size)
20562306a36Sopenharmony_ci{
20662306a36Sopenharmony_ci	struct host_cmd_ds_txbuf_cfg *tx_buf = &cmd->params.tx_buf;
20762306a36Sopenharmony_ci	u16 action = (u16) cmd_action;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	cmd->command = cpu_to_le16(HostCmd_CMD_RECONFIGURE_TX_BUFF);
21062306a36Sopenharmony_ci	cmd->size =
21162306a36Sopenharmony_ci		cpu_to_le16(sizeof(struct host_cmd_ds_txbuf_cfg) + S_DS_GEN);
21262306a36Sopenharmony_ci	tx_buf->action = cpu_to_le16(action);
21362306a36Sopenharmony_ci	switch (action) {
21462306a36Sopenharmony_ci	case HostCmd_ACT_GEN_SET:
21562306a36Sopenharmony_ci		mwifiex_dbg(priv->adapter, CMD,
21662306a36Sopenharmony_ci			    "cmd: set tx_buf=%d\n", *buf_size);
21762306a36Sopenharmony_ci		tx_buf->buff_size = cpu_to_le16(*buf_size);
21862306a36Sopenharmony_ci		break;
21962306a36Sopenharmony_ci	case HostCmd_ACT_GEN_GET:
22062306a36Sopenharmony_ci	default:
22162306a36Sopenharmony_ci		tx_buf->buff_size = 0;
22262306a36Sopenharmony_ci		break;
22362306a36Sopenharmony_ci	}
22462306a36Sopenharmony_ci	return 0;
22562306a36Sopenharmony_ci}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci/*
22862306a36Sopenharmony_ci * This function prepares command of AMSDU aggregation control.
22962306a36Sopenharmony_ci *
23062306a36Sopenharmony_ci * Preparation includes -
23162306a36Sopenharmony_ci *      - Setting command ID, action and proper size
23262306a36Sopenharmony_ci *      - Setting AMSDU control parameters (for SET only)
23362306a36Sopenharmony_ci *      - Ensuring correct endian-ness
23462306a36Sopenharmony_ci */
23562306a36Sopenharmony_ciint mwifiex_cmd_amsdu_aggr_ctrl(struct host_cmd_ds_command *cmd,
23662306a36Sopenharmony_ci				int cmd_action,
23762306a36Sopenharmony_ci				struct mwifiex_ds_11n_amsdu_aggr_ctrl *aa_ctrl)
23862306a36Sopenharmony_ci{
23962306a36Sopenharmony_ci	struct host_cmd_ds_amsdu_aggr_ctrl *amsdu_ctrl =
24062306a36Sopenharmony_ci		&cmd->params.amsdu_aggr_ctrl;
24162306a36Sopenharmony_ci	u16 action = (u16) cmd_action;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	cmd->command = cpu_to_le16(HostCmd_CMD_AMSDU_AGGR_CTRL);
24462306a36Sopenharmony_ci	cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_amsdu_aggr_ctrl)
24562306a36Sopenharmony_ci				+ S_DS_GEN);
24662306a36Sopenharmony_ci	amsdu_ctrl->action = cpu_to_le16(action);
24762306a36Sopenharmony_ci	switch (action) {
24862306a36Sopenharmony_ci	case HostCmd_ACT_GEN_SET:
24962306a36Sopenharmony_ci		amsdu_ctrl->enable = cpu_to_le16(aa_ctrl->enable);
25062306a36Sopenharmony_ci		amsdu_ctrl->curr_buf_size = 0;
25162306a36Sopenharmony_ci		break;
25262306a36Sopenharmony_ci	case HostCmd_ACT_GEN_GET:
25362306a36Sopenharmony_ci	default:
25462306a36Sopenharmony_ci		amsdu_ctrl->curr_buf_size = 0;
25562306a36Sopenharmony_ci		break;
25662306a36Sopenharmony_ci	}
25762306a36Sopenharmony_ci	return 0;
25862306a36Sopenharmony_ci}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci/*
26162306a36Sopenharmony_ci * This function prepares 11n configuration command.
26262306a36Sopenharmony_ci *
26362306a36Sopenharmony_ci * Preparation includes -
26462306a36Sopenharmony_ci *      - Setting command ID, action and proper size
26562306a36Sopenharmony_ci *      - Setting HT Tx capability and HT Tx information fields
26662306a36Sopenharmony_ci *      - Ensuring correct endian-ness
26762306a36Sopenharmony_ci */
26862306a36Sopenharmony_ciint mwifiex_cmd_11n_cfg(struct mwifiex_private *priv,
26962306a36Sopenharmony_ci			struct host_cmd_ds_command *cmd, u16 cmd_action,
27062306a36Sopenharmony_ci			struct mwifiex_ds_11n_tx_cfg *txcfg)
27162306a36Sopenharmony_ci{
27262306a36Sopenharmony_ci	struct host_cmd_ds_11n_cfg *htcfg = &cmd->params.htcfg;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	cmd->command = cpu_to_le16(HostCmd_CMD_11N_CFG);
27562306a36Sopenharmony_ci	cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_11n_cfg) + S_DS_GEN);
27662306a36Sopenharmony_ci	htcfg->action = cpu_to_le16(cmd_action);
27762306a36Sopenharmony_ci	htcfg->ht_tx_cap = cpu_to_le16(txcfg->tx_htcap);
27862306a36Sopenharmony_ci	htcfg->ht_tx_info = cpu_to_le16(txcfg->tx_htinfo);
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	if (priv->adapter->is_hw_11ac_capable)
28162306a36Sopenharmony_ci		htcfg->misc_config = cpu_to_le16(txcfg->misc_config);
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	return 0;
28462306a36Sopenharmony_ci}
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci/*
28762306a36Sopenharmony_ci * This function appends an 11n TLV to a buffer.
28862306a36Sopenharmony_ci *
28962306a36Sopenharmony_ci * Buffer allocation is responsibility of the calling
29062306a36Sopenharmony_ci * function. No size validation is made here.
29162306a36Sopenharmony_ci *
29262306a36Sopenharmony_ci * The function fills up the following sections, if applicable -
29362306a36Sopenharmony_ci *      - HT capability IE
29462306a36Sopenharmony_ci *      - HT information IE (with channel list)
29562306a36Sopenharmony_ci *      - 20/40 BSS Coexistence IE
29662306a36Sopenharmony_ci *      - HT Extended Capabilities IE
29762306a36Sopenharmony_ci */
29862306a36Sopenharmony_ciint
29962306a36Sopenharmony_cimwifiex_cmd_append_11n_tlv(struct mwifiex_private *priv,
30062306a36Sopenharmony_ci			   struct mwifiex_bssdescriptor *bss_desc,
30162306a36Sopenharmony_ci			   u8 **buffer)
30262306a36Sopenharmony_ci{
30362306a36Sopenharmony_ci	struct mwifiex_ie_types_htcap *ht_cap;
30462306a36Sopenharmony_ci	struct mwifiex_ie_types_htinfo *ht_info;
30562306a36Sopenharmony_ci	struct mwifiex_ie_types_chan_list_param_set *chan_list;
30662306a36Sopenharmony_ci	struct mwifiex_ie_types_2040bssco *bss_co_2040;
30762306a36Sopenharmony_ci	struct mwifiex_ie_types_extcap *ext_cap;
30862306a36Sopenharmony_ci	int ret_len = 0;
30962306a36Sopenharmony_ci	struct ieee80211_supported_band *sband;
31062306a36Sopenharmony_ci	struct ieee_types_header *hdr;
31162306a36Sopenharmony_ci	u8 radio_type;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	if (!buffer || !*buffer)
31462306a36Sopenharmony_ci		return ret_len;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	radio_type = mwifiex_band_to_radio_type((u8) bss_desc->bss_band);
31762306a36Sopenharmony_ci	sband = priv->wdev.wiphy->bands[radio_type];
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	if (bss_desc->bcn_ht_cap) {
32062306a36Sopenharmony_ci		ht_cap = (struct mwifiex_ie_types_htcap *) *buffer;
32162306a36Sopenharmony_ci		memset(ht_cap, 0, sizeof(struct mwifiex_ie_types_htcap));
32262306a36Sopenharmony_ci		ht_cap->header.type = cpu_to_le16(WLAN_EID_HT_CAPABILITY);
32362306a36Sopenharmony_ci		ht_cap->header.len =
32462306a36Sopenharmony_ci				cpu_to_le16(sizeof(struct ieee80211_ht_cap));
32562306a36Sopenharmony_ci		memcpy((u8 *) ht_cap + sizeof(struct mwifiex_ie_types_header),
32662306a36Sopenharmony_ci		       (u8 *)bss_desc->bcn_ht_cap,
32762306a36Sopenharmony_ci		       le16_to_cpu(ht_cap->header.len));
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci		mwifiex_fill_cap_info(priv, radio_type, &ht_cap->ht_cap);
33062306a36Sopenharmony_ci		/* Update HT40 capability from current channel information */
33162306a36Sopenharmony_ci		if (bss_desc->bcn_ht_oper) {
33262306a36Sopenharmony_ci			u8 ht_param = bss_desc->bcn_ht_oper->ht_param;
33362306a36Sopenharmony_ci			u8 radio =
33462306a36Sopenharmony_ci			mwifiex_band_to_radio_type(bss_desc->bss_band);
33562306a36Sopenharmony_ci			int freq =
33662306a36Sopenharmony_ci			ieee80211_channel_to_frequency(bss_desc->channel,
33762306a36Sopenharmony_ci						       radio);
33862306a36Sopenharmony_ci			struct ieee80211_channel *chan =
33962306a36Sopenharmony_ci			ieee80211_get_channel(priv->adapter->wiphy, freq);
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci			switch (ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
34262306a36Sopenharmony_ci			case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
34362306a36Sopenharmony_ci				if (chan->flags & IEEE80211_CHAN_NO_HT40PLUS) {
34462306a36Sopenharmony_ci					ht_cap->ht_cap.cap_info &=
34562306a36Sopenharmony_ci					cpu_to_le16
34662306a36Sopenharmony_ci					(~IEEE80211_HT_CAP_SUP_WIDTH_20_40);
34762306a36Sopenharmony_ci					ht_cap->ht_cap.cap_info &=
34862306a36Sopenharmony_ci					cpu_to_le16(~IEEE80211_HT_CAP_SGI_40);
34962306a36Sopenharmony_ci				}
35062306a36Sopenharmony_ci				break;
35162306a36Sopenharmony_ci			case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
35262306a36Sopenharmony_ci				if (chan->flags & IEEE80211_CHAN_NO_HT40MINUS) {
35362306a36Sopenharmony_ci					ht_cap->ht_cap.cap_info &=
35462306a36Sopenharmony_ci					cpu_to_le16
35562306a36Sopenharmony_ci					(~IEEE80211_HT_CAP_SUP_WIDTH_20_40);
35662306a36Sopenharmony_ci					ht_cap->ht_cap.cap_info &=
35762306a36Sopenharmony_ci					cpu_to_le16(~IEEE80211_HT_CAP_SGI_40);
35862306a36Sopenharmony_ci				}
35962306a36Sopenharmony_ci				break;
36062306a36Sopenharmony_ci			}
36162306a36Sopenharmony_ci		}
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci		*buffer += sizeof(struct mwifiex_ie_types_htcap);
36462306a36Sopenharmony_ci		ret_len += sizeof(struct mwifiex_ie_types_htcap);
36562306a36Sopenharmony_ci	}
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	if (bss_desc->bcn_ht_oper) {
36862306a36Sopenharmony_ci		if (priv->bss_mode == NL80211_IFTYPE_ADHOC) {
36962306a36Sopenharmony_ci			ht_info = (struct mwifiex_ie_types_htinfo *) *buffer;
37062306a36Sopenharmony_ci			memset(ht_info, 0,
37162306a36Sopenharmony_ci			       sizeof(struct mwifiex_ie_types_htinfo));
37262306a36Sopenharmony_ci			ht_info->header.type =
37362306a36Sopenharmony_ci					cpu_to_le16(WLAN_EID_HT_OPERATION);
37462306a36Sopenharmony_ci			ht_info->header.len =
37562306a36Sopenharmony_ci				cpu_to_le16(
37662306a36Sopenharmony_ci					sizeof(struct ieee80211_ht_operation));
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci			memcpy((u8 *) ht_info +
37962306a36Sopenharmony_ci			       sizeof(struct mwifiex_ie_types_header),
38062306a36Sopenharmony_ci			       (u8 *)bss_desc->bcn_ht_oper,
38162306a36Sopenharmony_ci			       le16_to_cpu(ht_info->header.len));
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci			if (!(sband->ht_cap.cap &
38462306a36Sopenharmony_ci					IEEE80211_HT_CAP_SUP_WIDTH_20_40))
38562306a36Sopenharmony_ci				ht_info->ht_oper.ht_param &=
38662306a36Sopenharmony_ci					~(IEEE80211_HT_PARAM_CHAN_WIDTH_ANY |
38762306a36Sopenharmony_ci					IEEE80211_HT_PARAM_CHA_SEC_OFFSET);
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci			*buffer += sizeof(struct mwifiex_ie_types_htinfo);
39062306a36Sopenharmony_ci			ret_len += sizeof(struct mwifiex_ie_types_htinfo);
39162306a36Sopenharmony_ci		}
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci		chan_list =
39462306a36Sopenharmony_ci			(struct mwifiex_ie_types_chan_list_param_set *) *buffer;
39562306a36Sopenharmony_ci		memset(chan_list, 0,
39662306a36Sopenharmony_ci		       sizeof(struct mwifiex_ie_types_chan_list_param_set));
39762306a36Sopenharmony_ci		chan_list->header.type = cpu_to_le16(TLV_TYPE_CHANLIST);
39862306a36Sopenharmony_ci		chan_list->header.len = cpu_to_le16(
39962306a36Sopenharmony_ci			sizeof(struct mwifiex_ie_types_chan_list_param_set) -
40062306a36Sopenharmony_ci			sizeof(struct mwifiex_ie_types_header));
40162306a36Sopenharmony_ci		chan_list->chan_scan_param[0].chan_number =
40262306a36Sopenharmony_ci			bss_desc->bcn_ht_oper->primary_chan;
40362306a36Sopenharmony_ci		chan_list->chan_scan_param[0].radio_type =
40462306a36Sopenharmony_ci			mwifiex_band_to_radio_type((u8) bss_desc->bss_band);
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci		if (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 &&
40762306a36Sopenharmony_ci		    bss_desc->bcn_ht_oper->ht_param &
40862306a36Sopenharmony_ci		    IEEE80211_HT_PARAM_CHAN_WIDTH_ANY)
40962306a36Sopenharmony_ci			SET_SECONDARYCHAN(chan_list->chan_scan_param[0].
41062306a36Sopenharmony_ci					  radio_type,
41162306a36Sopenharmony_ci					  (bss_desc->bcn_ht_oper->ht_param &
41262306a36Sopenharmony_ci					  IEEE80211_HT_PARAM_CHA_SEC_OFFSET));
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci		*buffer += sizeof(struct mwifiex_ie_types_chan_list_param_set);
41562306a36Sopenharmony_ci		ret_len += sizeof(struct mwifiex_ie_types_chan_list_param_set);
41662306a36Sopenharmony_ci	}
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	if (bss_desc->bcn_bss_co_2040) {
41962306a36Sopenharmony_ci		bss_co_2040 = (struct mwifiex_ie_types_2040bssco *) *buffer;
42062306a36Sopenharmony_ci		memset(bss_co_2040, 0,
42162306a36Sopenharmony_ci		       sizeof(struct mwifiex_ie_types_2040bssco));
42262306a36Sopenharmony_ci		bss_co_2040->header.type = cpu_to_le16(WLAN_EID_BSS_COEX_2040);
42362306a36Sopenharmony_ci		bss_co_2040->header.len =
42462306a36Sopenharmony_ci		       cpu_to_le16(sizeof(bss_co_2040->bss_co_2040));
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci		memcpy((u8 *) bss_co_2040 +
42762306a36Sopenharmony_ci		       sizeof(struct mwifiex_ie_types_header),
42862306a36Sopenharmony_ci		       bss_desc->bcn_bss_co_2040 +
42962306a36Sopenharmony_ci		       sizeof(struct ieee_types_header),
43062306a36Sopenharmony_ci		       le16_to_cpu(bss_co_2040->header.len));
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci		*buffer += sizeof(struct mwifiex_ie_types_2040bssco);
43362306a36Sopenharmony_ci		ret_len += sizeof(struct mwifiex_ie_types_2040bssco);
43462306a36Sopenharmony_ci	}
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	if (bss_desc->bcn_ext_cap) {
43762306a36Sopenharmony_ci		hdr = (void *)bss_desc->bcn_ext_cap;
43862306a36Sopenharmony_ci		ext_cap = (struct mwifiex_ie_types_extcap *) *buffer;
43962306a36Sopenharmony_ci		memset(ext_cap, 0, sizeof(struct mwifiex_ie_types_extcap));
44062306a36Sopenharmony_ci		ext_cap->header.type = cpu_to_le16(WLAN_EID_EXT_CAPABILITY);
44162306a36Sopenharmony_ci		ext_cap->header.len = cpu_to_le16(hdr->len);
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci		memcpy((u8 *)ext_cap->ext_capab,
44462306a36Sopenharmony_ci		       bss_desc->bcn_ext_cap + sizeof(struct ieee_types_header),
44562306a36Sopenharmony_ci		       le16_to_cpu(ext_cap->header.len));
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci		if (hdr->len > 3 &&
44862306a36Sopenharmony_ci		    ext_cap->ext_capab[3] & WLAN_EXT_CAPA4_INTERWORKING_ENABLED)
44962306a36Sopenharmony_ci			priv->hs2_enabled = true;
45062306a36Sopenharmony_ci		else
45162306a36Sopenharmony_ci			priv->hs2_enabled = false;
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci		*buffer += sizeof(struct mwifiex_ie_types_extcap) + hdr->len;
45462306a36Sopenharmony_ci		ret_len += sizeof(struct mwifiex_ie_types_extcap) + hdr->len;
45562306a36Sopenharmony_ci	}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	return ret_len;
45862306a36Sopenharmony_ci}
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci/*
46162306a36Sopenharmony_ci * This function checks if the given pointer is valid entry of
46262306a36Sopenharmony_ci * Tx BA Stream table.
46362306a36Sopenharmony_ci */
46462306a36Sopenharmony_cistatic int mwifiex_is_tx_ba_stream_ptr_valid(struct mwifiex_private *priv,
46562306a36Sopenharmony_ci				struct mwifiex_tx_ba_stream_tbl *tx_tbl_ptr)
46662306a36Sopenharmony_ci{
46762306a36Sopenharmony_ci	struct mwifiex_tx_ba_stream_tbl *tx_ba_tsr_tbl;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	list_for_each_entry(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr, list) {
47062306a36Sopenharmony_ci		if (tx_ba_tsr_tbl == tx_tbl_ptr)
47162306a36Sopenharmony_ci			return true;
47262306a36Sopenharmony_ci	}
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	return false;
47562306a36Sopenharmony_ci}
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci/*
47862306a36Sopenharmony_ci * This function deletes the given entry in Tx BA Stream table.
47962306a36Sopenharmony_ci *
48062306a36Sopenharmony_ci * The function also performs a validity check on the supplied
48162306a36Sopenharmony_ci * pointer before trying to delete.
48262306a36Sopenharmony_ci */
48362306a36Sopenharmony_civoid mwifiex_11n_delete_tx_ba_stream_tbl_entry(struct mwifiex_private *priv,
48462306a36Sopenharmony_ci				struct mwifiex_tx_ba_stream_tbl *tx_ba_tsr_tbl)
48562306a36Sopenharmony_ci{
48662306a36Sopenharmony_ci	if (!tx_ba_tsr_tbl &&
48762306a36Sopenharmony_ci	    mwifiex_is_tx_ba_stream_ptr_valid(priv, tx_ba_tsr_tbl))
48862306a36Sopenharmony_ci		return;
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	mwifiex_dbg(priv->adapter, INFO,
49162306a36Sopenharmony_ci		    "info: tx_ba_tsr_tbl %p\n", tx_ba_tsr_tbl);
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	list_del(&tx_ba_tsr_tbl->list);
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	kfree(tx_ba_tsr_tbl);
49662306a36Sopenharmony_ci}
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci/*
49962306a36Sopenharmony_ci * This function deletes all the entries in Tx BA Stream table.
50062306a36Sopenharmony_ci */
50162306a36Sopenharmony_civoid mwifiex_11n_delete_all_tx_ba_stream_tbl(struct mwifiex_private *priv)
50262306a36Sopenharmony_ci{
50362306a36Sopenharmony_ci	int i;
50462306a36Sopenharmony_ci	struct mwifiex_tx_ba_stream_tbl *del_tbl_ptr, *tmp_node;
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	spin_lock_bh(&priv->tx_ba_stream_tbl_lock);
50762306a36Sopenharmony_ci	list_for_each_entry_safe(del_tbl_ptr, tmp_node,
50862306a36Sopenharmony_ci				 &priv->tx_ba_stream_tbl_ptr, list)
50962306a36Sopenharmony_ci		mwifiex_11n_delete_tx_ba_stream_tbl_entry(priv, del_tbl_ptr);
51062306a36Sopenharmony_ci	spin_unlock_bh(&priv->tx_ba_stream_tbl_lock);
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	INIT_LIST_HEAD(&priv->tx_ba_stream_tbl_ptr);
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	for (i = 0; i < MAX_NUM_TID; ++i)
51562306a36Sopenharmony_ci		priv->aggr_prio_tbl[i].ampdu_ap =
51662306a36Sopenharmony_ci			priv->aggr_prio_tbl[i].ampdu_user;
51762306a36Sopenharmony_ci}
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci/*
52062306a36Sopenharmony_ci * This function returns the pointer to an entry in BA Stream
52162306a36Sopenharmony_ci * table which matches the given RA/TID pair.
52262306a36Sopenharmony_ci */
52362306a36Sopenharmony_cistruct mwifiex_tx_ba_stream_tbl *
52462306a36Sopenharmony_cimwifiex_get_ba_tbl(struct mwifiex_private *priv, int tid, u8 *ra)
52562306a36Sopenharmony_ci{
52662306a36Sopenharmony_ci	struct mwifiex_tx_ba_stream_tbl *tx_ba_tsr_tbl;
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	spin_lock_bh(&priv->tx_ba_stream_tbl_lock);
52962306a36Sopenharmony_ci	list_for_each_entry(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr, list) {
53062306a36Sopenharmony_ci		if (ether_addr_equal_unaligned(tx_ba_tsr_tbl->ra, ra) &&
53162306a36Sopenharmony_ci		    tx_ba_tsr_tbl->tid == tid) {
53262306a36Sopenharmony_ci			spin_unlock_bh(&priv->tx_ba_stream_tbl_lock);
53362306a36Sopenharmony_ci			return tx_ba_tsr_tbl;
53462306a36Sopenharmony_ci		}
53562306a36Sopenharmony_ci	}
53662306a36Sopenharmony_ci	spin_unlock_bh(&priv->tx_ba_stream_tbl_lock);
53762306a36Sopenharmony_ci	return NULL;
53862306a36Sopenharmony_ci}
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci/*
54162306a36Sopenharmony_ci * This function creates an entry in Tx BA stream table for the
54262306a36Sopenharmony_ci * given RA/TID pair.
54362306a36Sopenharmony_ci */
54462306a36Sopenharmony_civoid mwifiex_create_ba_tbl(struct mwifiex_private *priv, u8 *ra, int tid,
54562306a36Sopenharmony_ci			   enum mwifiex_ba_status ba_status)
54662306a36Sopenharmony_ci{
54762306a36Sopenharmony_ci	struct mwifiex_tx_ba_stream_tbl *new_node;
54862306a36Sopenharmony_ci	struct mwifiex_ra_list_tbl *ra_list;
54962306a36Sopenharmony_ci	int tid_down;
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	if (!mwifiex_get_ba_tbl(priv, tid, ra)) {
55262306a36Sopenharmony_ci		new_node = kzalloc(sizeof(struct mwifiex_tx_ba_stream_tbl),
55362306a36Sopenharmony_ci				   GFP_ATOMIC);
55462306a36Sopenharmony_ci		if (!new_node)
55562306a36Sopenharmony_ci			return;
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci		tid_down = mwifiex_wmm_downgrade_tid(priv, tid);
55862306a36Sopenharmony_ci		ra_list = mwifiex_wmm_get_ralist_node(priv, tid_down, ra);
55962306a36Sopenharmony_ci		if (ra_list) {
56062306a36Sopenharmony_ci			ra_list->ba_status = ba_status;
56162306a36Sopenharmony_ci			ra_list->amsdu_in_ampdu = false;
56262306a36Sopenharmony_ci		}
56362306a36Sopenharmony_ci		INIT_LIST_HEAD(&new_node->list);
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci		new_node->tid = tid;
56662306a36Sopenharmony_ci		new_node->ba_status = ba_status;
56762306a36Sopenharmony_ci		memcpy(new_node->ra, ra, ETH_ALEN);
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci		spin_lock_bh(&priv->tx_ba_stream_tbl_lock);
57062306a36Sopenharmony_ci		list_add_tail(&new_node->list, &priv->tx_ba_stream_tbl_ptr);
57162306a36Sopenharmony_ci		spin_unlock_bh(&priv->tx_ba_stream_tbl_lock);
57262306a36Sopenharmony_ci	}
57362306a36Sopenharmony_ci}
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci/*
57662306a36Sopenharmony_ci * This function sends an add BA request to the given TID/RA pair.
57762306a36Sopenharmony_ci */
57862306a36Sopenharmony_ciint mwifiex_send_addba(struct mwifiex_private *priv, int tid, u8 *peer_mac)
57962306a36Sopenharmony_ci{
58062306a36Sopenharmony_ci	struct host_cmd_ds_11n_addba_req add_ba_req;
58162306a36Sopenharmony_ci	u32 tx_win_size = priv->add_ba_param.tx_win_size;
58262306a36Sopenharmony_ci	static u8 dialog_tok;
58362306a36Sopenharmony_ci	int ret;
58462306a36Sopenharmony_ci	u16 block_ack_param_set;
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	mwifiex_dbg(priv->adapter, CMD, "cmd: %s: tid %d\n", __func__, tid);
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	memset(&add_ba_req, 0, sizeof(add_ba_req));
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) &&
59162306a36Sopenharmony_ci	    ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) &&
59262306a36Sopenharmony_ci	    priv->adapter->is_hw_11ac_capable &&
59362306a36Sopenharmony_ci	    memcmp(priv->cfg_bssid, peer_mac, ETH_ALEN)) {
59462306a36Sopenharmony_ci		struct mwifiex_sta_node *sta_ptr;
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci		spin_lock_bh(&priv->sta_list_spinlock);
59762306a36Sopenharmony_ci		sta_ptr = mwifiex_get_sta_entry(priv, peer_mac);
59862306a36Sopenharmony_ci		if (!sta_ptr) {
59962306a36Sopenharmony_ci			spin_unlock_bh(&priv->sta_list_spinlock);
60062306a36Sopenharmony_ci			mwifiex_dbg(priv->adapter, ERROR,
60162306a36Sopenharmony_ci				    "BA setup with unknown TDLS peer %pM!\n",
60262306a36Sopenharmony_ci				    peer_mac);
60362306a36Sopenharmony_ci			return -1;
60462306a36Sopenharmony_ci		}
60562306a36Sopenharmony_ci		if (sta_ptr->is_11ac_enabled)
60662306a36Sopenharmony_ci			tx_win_size = MWIFIEX_11AC_STA_AMPDU_DEF_TXWINSIZE;
60762306a36Sopenharmony_ci		spin_unlock_bh(&priv->sta_list_spinlock);
60862306a36Sopenharmony_ci	}
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	block_ack_param_set = (u16)((tid << BLOCKACKPARAM_TID_POS) |
61162306a36Sopenharmony_ci				    tx_win_size << BLOCKACKPARAM_WINSIZE_POS |
61262306a36Sopenharmony_ci				    IMMEDIATE_BLOCK_ACK);
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	/* enable AMSDU inside AMPDU */
61562306a36Sopenharmony_ci	if (priv->add_ba_param.tx_amsdu &&
61662306a36Sopenharmony_ci	    (priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED))
61762306a36Sopenharmony_ci		block_ack_param_set |= BLOCKACKPARAM_AMSDU_SUPP_MASK;
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	add_ba_req.block_ack_param_set = cpu_to_le16(block_ack_param_set);
62062306a36Sopenharmony_ci	add_ba_req.block_ack_tmo = cpu_to_le16((u16)priv->add_ba_param.timeout);
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	++dialog_tok;
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	if (dialog_tok == 0)
62562306a36Sopenharmony_ci		dialog_tok = 1;
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	add_ba_req.dialog_token = dialog_tok;
62862306a36Sopenharmony_ci	memcpy(&add_ba_req.peer_mac_addr, peer_mac, ETH_ALEN);
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	/* We don't wait for the response of this command */
63162306a36Sopenharmony_ci	ret = mwifiex_send_cmd(priv, HostCmd_CMD_11N_ADDBA_REQ,
63262306a36Sopenharmony_ci			       0, 0, &add_ba_req, false);
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	return ret;
63562306a36Sopenharmony_ci}
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci/*
63862306a36Sopenharmony_ci * This function sends a delete BA request to the given TID/RA pair.
63962306a36Sopenharmony_ci */
64062306a36Sopenharmony_ciint mwifiex_send_delba(struct mwifiex_private *priv, int tid, u8 *peer_mac,
64162306a36Sopenharmony_ci		       int initiator)
64262306a36Sopenharmony_ci{
64362306a36Sopenharmony_ci	struct host_cmd_ds_11n_delba delba;
64462306a36Sopenharmony_ci	int ret;
64562306a36Sopenharmony_ci	uint16_t del_ba_param_set;
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	memset(&delba, 0, sizeof(delba));
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	del_ba_param_set = tid << DELBA_TID_POS;
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	if (initiator)
65262306a36Sopenharmony_ci		del_ba_param_set |= IEEE80211_DELBA_PARAM_INITIATOR_MASK;
65362306a36Sopenharmony_ci	else
65462306a36Sopenharmony_ci		del_ba_param_set &= ~IEEE80211_DELBA_PARAM_INITIATOR_MASK;
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	delba.del_ba_param_set = cpu_to_le16(del_ba_param_set);
65762306a36Sopenharmony_ci	memcpy(&delba.peer_mac_addr, peer_mac, ETH_ALEN);
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	/* We don't wait for the response of this command */
66062306a36Sopenharmony_ci	ret = mwifiex_send_cmd(priv, HostCmd_CMD_11N_DELBA,
66162306a36Sopenharmony_ci			       HostCmd_ACT_GEN_SET, 0, &delba, false);
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	return ret;
66462306a36Sopenharmony_ci}
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci/*
66762306a36Sopenharmony_ci * This function sends delba to specific tid
66862306a36Sopenharmony_ci */
66962306a36Sopenharmony_civoid mwifiex_11n_delba(struct mwifiex_private *priv, int tid)
67062306a36Sopenharmony_ci{
67162306a36Sopenharmony_ci	struct mwifiex_rx_reorder_tbl *rx_reor_tbl_ptr;
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	spin_lock_bh(&priv->rx_reorder_tbl_lock);
67462306a36Sopenharmony_ci	list_for_each_entry(rx_reor_tbl_ptr, &priv->rx_reorder_tbl_ptr, list) {
67562306a36Sopenharmony_ci		if (rx_reor_tbl_ptr->tid == tid) {
67662306a36Sopenharmony_ci			dev_dbg(priv->adapter->dev,
67762306a36Sopenharmony_ci				"Send delba to tid=%d, %pM\n",
67862306a36Sopenharmony_ci				tid, rx_reor_tbl_ptr->ta);
67962306a36Sopenharmony_ci			mwifiex_send_delba(priv, tid, rx_reor_tbl_ptr->ta, 0);
68062306a36Sopenharmony_ci			goto exit;
68162306a36Sopenharmony_ci		}
68262306a36Sopenharmony_ci	}
68362306a36Sopenharmony_ciexit:
68462306a36Sopenharmony_ci	spin_unlock_bh(&priv->rx_reorder_tbl_lock);
68562306a36Sopenharmony_ci}
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci/*
68862306a36Sopenharmony_ci * This function handles the command response of a delete BA request.
68962306a36Sopenharmony_ci */
69062306a36Sopenharmony_civoid mwifiex_11n_delete_ba_stream(struct mwifiex_private *priv, u8 *del_ba)
69162306a36Sopenharmony_ci{
69262306a36Sopenharmony_ci	struct host_cmd_ds_11n_delba *cmd_del_ba =
69362306a36Sopenharmony_ci		(struct host_cmd_ds_11n_delba *) del_ba;
69462306a36Sopenharmony_ci	uint16_t del_ba_param_set = le16_to_cpu(cmd_del_ba->del_ba_param_set);
69562306a36Sopenharmony_ci	int tid;
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	tid = del_ba_param_set >> DELBA_TID_POS;
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	mwifiex_del_ba_tbl(priv, tid, cmd_del_ba->peer_mac_addr,
70062306a36Sopenharmony_ci			   TYPE_DELBA_RECEIVE, INITIATOR_BIT(del_ba_param_set));
70162306a36Sopenharmony_ci}
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci/*
70462306a36Sopenharmony_ci * This function retrieves the Rx reordering table.
70562306a36Sopenharmony_ci */
70662306a36Sopenharmony_ciint mwifiex_get_rx_reorder_tbl(struct mwifiex_private *priv,
70762306a36Sopenharmony_ci			       struct mwifiex_ds_rx_reorder_tbl *buf)
70862306a36Sopenharmony_ci{
70962306a36Sopenharmony_ci	int i;
71062306a36Sopenharmony_ci	struct mwifiex_ds_rx_reorder_tbl *rx_reo_tbl = buf;
71162306a36Sopenharmony_ci	struct mwifiex_rx_reorder_tbl *rx_reorder_tbl_ptr;
71262306a36Sopenharmony_ci	int count = 0;
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	spin_lock_bh(&priv->rx_reorder_tbl_lock);
71562306a36Sopenharmony_ci	list_for_each_entry(rx_reorder_tbl_ptr, &priv->rx_reorder_tbl_ptr,
71662306a36Sopenharmony_ci			    list) {
71762306a36Sopenharmony_ci		rx_reo_tbl->tid = (u16) rx_reorder_tbl_ptr->tid;
71862306a36Sopenharmony_ci		memcpy(rx_reo_tbl->ta, rx_reorder_tbl_ptr->ta, ETH_ALEN);
71962306a36Sopenharmony_ci		rx_reo_tbl->start_win = rx_reorder_tbl_ptr->start_win;
72062306a36Sopenharmony_ci		rx_reo_tbl->win_size = rx_reorder_tbl_ptr->win_size;
72162306a36Sopenharmony_ci		for (i = 0; i < rx_reorder_tbl_ptr->win_size; ++i) {
72262306a36Sopenharmony_ci			if (rx_reorder_tbl_ptr->rx_reorder_ptr[i])
72362306a36Sopenharmony_ci				rx_reo_tbl->buffer[i] = true;
72462306a36Sopenharmony_ci			else
72562306a36Sopenharmony_ci				rx_reo_tbl->buffer[i] = false;
72662306a36Sopenharmony_ci		}
72762306a36Sopenharmony_ci		rx_reo_tbl++;
72862306a36Sopenharmony_ci		count++;
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci		if (count >= MWIFIEX_MAX_RX_BASTREAM_SUPPORTED)
73162306a36Sopenharmony_ci			break;
73262306a36Sopenharmony_ci	}
73362306a36Sopenharmony_ci	spin_unlock_bh(&priv->rx_reorder_tbl_lock);
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci	return count;
73662306a36Sopenharmony_ci}
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci/*
73962306a36Sopenharmony_ci * This function retrieves the Tx BA stream table.
74062306a36Sopenharmony_ci */
74162306a36Sopenharmony_ciint mwifiex_get_tx_ba_stream_tbl(struct mwifiex_private *priv,
74262306a36Sopenharmony_ci				 struct mwifiex_ds_tx_ba_stream_tbl *buf)
74362306a36Sopenharmony_ci{
74462306a36Sopenharmony_ci	struct mwifiex_tx_ba_stream_tbl *tx_ba_tsr_tbl;
74562306a36Sopenharmony_ci	struct mwifiex_ds_tx_ba_stream_tbl *rx_reo_tbl = buf;
74662306a36Sopenharmony_ci	int count = 0;
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci	spin_lock_bh(&priv->tx_ba_stream_tbl_lock);
74962306a36Sopenharmony_ci	list_for_each_entry(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr, list) {
75062306a36Sopenharmony_ci		rx_reo_tbl->tid = (u16) tx_ba_tsr_tbl->tid;
75162306a36Sopenharmony_ci		mwifiex_dbg(priv->adapter, DATA, "data: %s tid=%d\n",
75262306a36Sopenharmony_ci			    __func__, rx_reo_tbl->tid);
75362306a36Sopenharmony_ci		memcpy(rx_reo_tbl->ra, tx_ba_tsr_tbl->ra, ETH_ALEN);
75462306a36Sopenharmony_ci		rx_reo_tbl->amsdu = tx_ba_tsr_tbl->amsdu;
75562306a36Sopenharmony_ci		rx_reo_tbl++;
75662306a36Sopenharmony_ci		count++;
75762306a36Sopenharmony_ci		if (count >= MWIFIEX_MAX_TX_BASTREAM_SUPPORTED)
75862306a36Sopenharmony_ci			break;
75962306a36Sopenharmony_ci	}
76062306a36Sopenharmony_ci	spin_unlock_bh(&priv->tx_ba_stream_tbl_lock);
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	return count;
76362306a36Sopenharmony_ci}
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci/*
76662306a36Sopenharmony_ci * This function retrieves the entry for specific tx BA stream table by RA and
76762306a36Sopenharmony_ci * deletes it.
76862306a36Sopenharmony_ci */
76962306a36Sopenharmony_civoid mwifiex_del_tx_ba_stream_tbl_by_ra(struct mwifiex_private *priv, u8 *ra)
77062306a36Sopenharmony_ci{
77162306a36Sopenharmony_ci	struct mwifiex_tx_ba_stream_tbl *tbl, *tmp;
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	if (!ra)
77462306a36Sopenharmony_ci		return;
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	spin_lock_bh(&priv->tx_ba_stream_tbl_lock);
77762306a36Sopenharmony_ci	list_for_each_entry_safe(tbl, tmp, &priv->tx_ba_stream_tbl_ptr, list)
77862306a36Sopenharmony_ci		if (!memcmp(tbl->ra, ra, ETH_ALEN))
77962306a36Sopenharmony_ci			mwifiex_11n_delete_tx_ba_stream_tbl_entry(priv, tbl);
78062306a36Sopenharmony_ci	spin_unlock_bh(&priv->tx_ba_stream_tbl_lock);
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	return;
78362306a36Sopenharmony_ci}
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci/* This function initializes the BlockACK setup information for given
78662306a36Sopenharmony_ci * mwifiex_private structure.
78762306a36Sopenharmony_ci */
78862306a36Sopenharmony_civoid mwifiex_set_ba_params(struct mwifiex_private *priv)
78962306a36Sopenharmony_ci{
79062306a36Sopenharmony_ci	priv->add_ba_param.timeout = MWIFIEX_DEFAULT_BLOCK_ACK_TIMEOUT;
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci	if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) {
79362306a36Sopenharmony_ci		priv->add_ba_param.tx_win_size =
79462306a36Sopenharmony_ci						MWIFIEX_UAP_AMPDU_DEF_TXWINSIZE;
79562306a36Sopenharmony_ci		priv->add_ba_param.rx_win_size =
79662306a36Sopenharmony_ci						MWIFIEX_UAP_AMPDU_DEF_RXWINSIZE;
79762306a36Sopenharmony_ci	} else {
79862306a36Sopenharmony_ci		priv->add_ba_param.tx_win_size =
79962306a36Sopenharmony_ci						MWIFIEX_STA_AMPDU_DEF_TXWINSIZE;
80062306a36Sopenharmony_ci		priv->add_ba_param.rx_win_size =
80162306a36Sopenharmony_ci						MWIFIEX_STA_AMPDU_DEF_RXWINSIZE;
80262306a36Sopenharmony_ci	}
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	priv->add_ba_param.tx_amsdu = true;
80562306a36Sopenharmony_ci	priv->add_ba_param.rx_amsdu = true;
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	return;
80862306a36Sopenharmony_ci}
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ciu8 mwifiex_get_sec_chan_offset(int chan)
81162306a36Sopenharmony_ci{
81262306a36Sopenharmony_ci	u8 sec_offset;
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci	switch (chan) {
81562306a36Sopenharmony_ci	case 36:
81662306a36Sopenharmony_ci	case 44:
81762306a36Sopenharmony_ci	case 52:
81862306a36Sopenharmony_ci	case 60:
81962306a36Sopenharmony_ci	case 100:
82062306a36Sopenharmony_ci	case 108:
82162306a36Sopenharmony_ci	case 116:
82262306a36Sopenharmony_ci	case 124:
82362306a36Sopenharmony_ci	case 132:
82462306a36Sopenharmony_ci	case 140:
82562306a36Sopenharmony_ci	case 149:
82662306a36Sopenharmony_ci	case 157:
82762306a36Sopenharmony_ci		sec_offset = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
82862306a36Sopenharmony_ci		break;
82962306a36Sopenharmony_ci	case 40:
83062306a36Sopenharmony_ci	case 48:
83162306a36Sopenharmony_ci	case 56:
83262306a36Sopenharmony_ci	case 64:
83362306a36Sopenharmony_ci	case 104:
83462306a36Sopenharmony_ci	case 112:
83562306a36Sopenharmony_ci	case 120:
83662306a36Sopenharmony_ci	case 128:
83762306a36Sopenharmony_ci	case 136:
83862306a36Sopenharmony_ci	case 144:
83962306a36Sopenharmony_ci	case 153:
84062306a36Sopenharmony_ci	case 161:
84162306a36Sopenharmony_ci		sec_offset = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
84262306a36Sopenharmony_ci		break;
84362306a36Sopenharmony_ci	case 165:
84462306a36Sopenharmony_ci	default:
84562306a36Sopenharmony_ci		sec_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE;
84662306a36Sopenharmony_ci		break;
84762306a36Sopenharmony_ci	}
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_ci	return sec_offset;
85062306a36Sopenharmony_ci}
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci/* This function will send DELBA to entries in the priv's
85362306a36Sopenharmony_ci * Tx BA stream table
85462306a36Sopenharmony_ci */
85562306a36Sopenharmony_cistatic void
85662306a36Sopenharmony_cimwifiex_send_delba_txbastream_tbl(struct mwifiex_private *priv, u8 tid)
85762306a36Sopenharmony_ci{
85862306a36Sopenharmony_ci	struct mwifiex_adapter *adapter = priv->adapter;
85962306a36Sopenharmony_ci	struct mwifiex_tx_ba_stream_tbl *tx_ba_stream_tbl_ptr;
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci	list_for_each_entry(tx_ba_stream_tbl_ptr,
86262306a36Sopenharmony_ci			    &priv->tx_ba_stream_tbl_ptr, list) {
86362306a36Sopenharmony_ci		if (tx_ba_stream_tbl_ptr->ba_status == BA_SETUP_COMPLETE) {
86462306a36Sopenharmony_ci			if (tid == tx_ba_stream_tbl_ptr->tid) {
86562306a36Sopenharmony_ci				dev_dbg(adapter->dev,
86662306a36Sopenharmony_ci					"Tx:Send delba to tid=%d, %pM\n", tid,
86762306a36Sopenharmony_ci					tx_ba_stream_tbl_ptr->ra);
86862306a36Sopenharmony_ci				mwifiex_send_delba(priv,
86962306a36Sopenharmony_ci						   tx_ba_stream_tbl_ptr->tid,
87062306a36Sopenharmony_ci						   tx_ba_stream_tbl_ptr->ra, 1);
87162306a36Sopenharmony_ci				return;
87262306a36Sopenharmony_ci			}
87362306a36Sopenharmony_ci		}
87462306a36Sopenharmony_ci	}
87562306a36Sopenharmony_ci}
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci/* This function updates all the tx_win_size
87862306a36Sopenharmony_ci */
87962306a36Sopenharmony_civoid mwifiex_update_ampdu_txwinsize(struct mwifiex_adapter *adapter)
88062306a36Sopenharmony_ci{
88162306a36Sopenharmony_ci	u8 i, j;
88262306a36Sopenharmony_ci	u32 tx_win_size;
88362306a36Sopenharmony_ci	struct mwifiex_private *priv;
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci	for (i = 0; i < adapter->priv_num; i++) {
88662306a36Sopenharmony_ci		if (!adapter->priv[i])
88762306a36Sopenharmony_ci			continue;
88862306a36Sopenharmony_ci		priv = adapter->priv[i];
88962306a36Sopenharmony_ci		tx_win_size = priv->add_ba_param.tx_win_size;
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci		if (priv->bss_type == MWIFIEX_BSS_TYPE_STA)
89262306a36Sopenharmony_ci			priv->add_ba_param.tx_win_size =
89362306a36Sopenharmony_ci				MWIFIEX_STA_AMPDU_DEF_TXWINSIZE;
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci		if (priv->bss_type == MWIFIEX_BSS_TYPE_P2P)
89662306a36Sopenharmony_ci			priv->add_ba_param.tx_win_size =
89762306a36Sopenharmony_ci				MWIFIEX_STA_AMPDU_DEF_TXWINSIZE;
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci		if (priv->bss_type == MWIFIEX_BSS_TYPE_UAP)
90062306a36Sopenharmony_ci			priv->add_ba_param.tx_win_size =
90162306a36Sopenharmony_ci				MWIFIEX_UAP_AMPDU_DEF_TXWINSIZE;
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci		if (adapter->coex_win_size) {
90462306a36Sopenharmony_ci			if (adapter->coex_tx_win_size)
90562306a36Sopenharmony_ci				priv->add_ba_param.tx_win_size =
90662306a36Sopenharmony_ci					adapter->coex_tx_win_size;
90762306a36Sopenharmony_ci		}
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci		if (tx_win_size != priv->add_ba_param.tx_win_size) {
91062306a36Sopenharmony_ci			if (!priv->media_connected)
91162306a36Sopenharmony_ci				continue;
91262306a36Sopenharmony_ci			for (j = 0; j < MAX_NUM_TID; j++)
91362306a36Sopenharmony_ci				mwifiex_send_delba_txbastream_tbl(priv, j);
91462306a36Sopenharmony_ci		}
91562306a36Sopenharmony_ci	}
91662306a36Sopenharmony_ci}
917