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