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