913 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * NXP Wireless LAN device driver: 802.11n4 *5 * Copyright 2011-2020 NXP6 */7 8#include "decl.h"9#include "ioctl.h"10#include "util.h"11#include "fw.h"12#include "main.h"13#include "wmm.h"14#include "11n.h"15 16/*17 * Fills HT capability information field, AMPDU Parameters field, HT extended18 * capability field, and supported MCS set fields.19 *20 * HT capability information field, AMPDU Parameters field, supported MCS set21 * fields are retrieved from cfg80211 stack22 *23 * RD responder bit to set to clear in the extended capability header.24 */25int mwifiex_fill_cap_info(struct mwifiex_private *priv, u8 radio_type,26 struct ieee80211_ht_cap *ht_cap)27{28 uint16_t ht_ext_cap = le16_to_cpu(ht_cap->extended_ht_cap_info);29 struct ieee80211_supported_band *sband =30 priv->wdev.wiphy->bands[radio_type];31 32 if (WARN_ON_ONCE(!sband)) {33 mwifiex_dbg(priv->adapter, ERROR, "Invalid radio type!\n");34 return -EINVAL;35 }36 37 ht_cap->ampdu_params_info =38 (sband->ht_cap.ampdu_factor &39 IEEE80211_HT_AMPDU_PARM_FACTOR) |40 ((sband->ht_cap.ampdu_density <<41 IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT) &42 IEEE80211_HT_AMPDU_PARM_DENSITY);43 44 memcpy((u8 *)&ht_cap->mcs, &sband->ht_cap.mcs,45 sizeof(sband->ht_cap.mcs));46 47 if (priv->bss_mode == NL80211_IFTYPE_STATION ||48 (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 &&49 (priv->adapter->sec_chan_offset !=50 IEEE80211_HT_PARAM_CHA_SEC_NONE)))51 /* Set MCS32 for infra mode or ad-hoc mode with 40MHz support */52 SETHT_MCS32(ht_cap->mcs.rx_mask);53 54 /* Clear RD responder bit */55 ht_ext_cap &= ~IEEE80211_HT_EXT_CAP_RD_RESPONDER;56 57 ht_cap->cap_info = cpu_to_le16(sband->ht_cap.cap);58 ht_cap->extended_ht_cap_info = cpu_to_le16(ht_ext_cap);59 60 if (ISSUPP_BEAMFORMING(priv->adapter->hw_dot_11n_dev_cap))61 ht_cap->tx_BF_cap_info = cpu_to_le32(MWIFIEX_DEF_11N_TX_BF_CAP);62 63 return 0;64}65 66/*67 * This function returns the pointer to an entry in BA Stream68 * table which matches the requested BA status.69 */70static struct mwifiex_tx_ba_stream_tbl *71mwifiex_get_ba_status(struct mwifiex_private *priv,72 enum mwifiex_ba_status ba_status)73{74 struct mwifiex_tx_ba_stream_tbl *tx_ba_tsr_tbl;75 76 spin_lock_bh(&priv->tx_ba_stream_tbl_lock);77 list_for_each_entry(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr, list) {78 if (tx_ba_tsr_tbl->ba_status == ba_status) {79 spin_unlock_bh(&priv->tx_ba_stream_tbl_lock);80 return tx_ba_tsr_tbl;81 }82 }83 spin_unlock_bh(&priv->tx_ba_stream_tbl_lock);84 return NULL;85}86 87/*88 * This function handles the command response of delete a block89 * ack request.90 *91 * The function checks the response success status and takes action92 * accordingly (send an add BA request in case of success, or recreate93 * the deleted stream in case of failure, if the add BA was also94 * initiated by us).95 */96int mwifiex_ret_11n_delba(struct mwifiex_private *priv,97 struct host_cmd_ds_command *resp)98{99 int tid;100 struct mwifiex_tx_ba_stream_tbl *tx_ba_tbl;101 struct host_cmd_ds_11n_delba *del_ba = &resp->params.del_ba;102 uint16_t del_ba_param_set = le16_to_cpu(del_ba->del_ba_param_set);103 104 tid = del_ba_param_set >> DELBA_TID_POS;105 if (del_ba->del_result == BA_RESULT_SUCCESS) {106 mwifiex_del_ba_tbl(priv, tid, del_ba->peer_mac_addr,107 TYPE_DELBA_SENT,108 INITIATOR_BIT(del_ba_param_set));109 110 tx_ba_tbl = mwifiex_get_ba_status(priv, BA_SETUP_INPROGRESS);111 if (tx_ba_tbl)112 mwifiex_send_addba(priv, tx_ba_tbl->tid,113 tx_ba_tbl->ra);114 } else { /*115 * In case of failure, recreate the deleted stream in case116 * we initiated the DELBA117 */118 if (!INITIATOR_BIT(del_ba_param_set))119 return 0;120 121 mwifiex_create_ba_tbl(priv, del_ba->peer_mac_addr, tid,122 BA_SETUP_INPROGRESS);123 124 tx_ba_tbl = mwifiex_get_ba_status(priv, BA_SETUP_INPROGRESS);125 126 if (tx_ba_tbl)127 mwifiex_del_ba_tbl(priv, tx_ba_tbl->tid, tx_ba_tbl->ra,128 TYPE_DELBA_SENT, true);129 }130 131 return 0;132}133 134/*135 * This function handles the command response of add a block136 * ack request.137 *138 * Handling includes changing the header fields to CPU formats, checking139 * the response success status and taking actions accordingly (delete the140 * BA stream table in case of failure).141 */142int mwifiex_ret_11n_addba_req(struct mwifiex_private *priv,143 struct host_cmd_ds_command *resp)144{145 int tid, tid_down;146 struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = &resp->params.add_ba_rsp;147 struct mwifiex_tx_ba_stream_tbl *tx_ba_tbl;148 struct mwifiex_ra_list_tbl *ra_list;149 u16 block_ack_param_set = le16_to_cpu(add_ba_rsp->block_ack_param_set);150 151 add_ba_rsp->ssn = cpu_to_le16((le16_to_cpu(add_ba_rsp->ssn))152 & SSN_MASK);153 154 tid = (block_ack_param_set & IEEE80211_ADDBA_PARAM_TID_MASK)155 >> BLOCKACKPARAM_TID_POS;156 157 tid_down = mwifiex_wmm_downgrade_tid(priv, tid);158 ra_list = mwifiex_wmm_get_ralist_node(priv, tid_down, add_ba_rsp->159 peer_mac_addr);160 if (le16_to_cpu(add_ba_rsp->status_code) != BA_RESULT_SUCCESS) {161 if (ra_list) {162 ra_list->ba_status = BA_SETUP_NONE;163 ra_list->amsdu_in_ampdu = false;164 }165 mwifiex_del_ba_tbl(priv, tid, add_ba_rsp->peer_mac_addr,166 TYPE_DELBA_SENT, true);167 if (add_ba_rsp->add_rsp_result != BA_RESULT_TIMEOUT)168 priv->aggr_prio_tbl[tid].ampdu_ap =169 BA_STREAM_NOT_ALLOWED;170 return 0;171 }172 173 tx_ba_tbl = mwifiex_get_ba_tbl(priv, tid, add_ba_rsp->peer_mac_addr);174 if (tx_ba_tbl) {175 mwifiex_dbg(priv->adapter, EVENT, "info: BA stream complete\n");176 tx_ba_tbl->ba_status = BA_SETUP_COMPLETE;177 if ((block_ack_param_set & BLOCKACKPARAM_AMSDU_SUPP_MASK) &&178 priv->add_ba_param.tx_amsdu &&179 (priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED))180 tx_ba_tbl->amsdu = true;181 else182 tx_ba_tbl->amsdu = false;183 if (ra_list) {184 ra_list->amsdu_in_ampdu = tx_ba_tbl->amsdu;185 ra_list->ba_status = BA_SETUP_COMPLETE;186 }187 } else {188 mwifiex_dbg(priv->adapter, ERROR, "BA stream not created\n");189 }190 191 return 0;192}193 194/*195 * This function prepares command of reconfigure Tx buffer.196 *197 * Preparation includes -198 * - Setting command ID, action and proper size199 * - Setting Tx buffer size (for SET only)200 * - Ensuring correct endian-ness201 */202int mwifiex_cmd_recfg_tx_buf(struct mwifiex_private *priv,203 struct host_cmd_ds_command *cmd, int cmd_action,204 u16 *buf_size)205{206 struct host_cmd_ds_txbuf_cfg *tx_buf = &cmd->params.tx_buf;207 u16 action = (u16) cmd_action;208 209 cmd->command = cpu_to_le16(HostCmd_CMD_RECONFIGURE_TX_BUFF);210 cmd->size =211 cpu_to_le16(sizeof(struct host_cmd_ds_txbuf_cfg) + S_DS_GEN);212 tx_buf->action = cpu_to_le16(action);213 switch (action) {214 case HostCmd_ACT_GEN_SET:215 mwifiex_dbg(priv->adapter, CMD,216 "cmd: set tx_buf=%d\n", *buf_size);217 tx_buf->buff_size = cpu_to_le16(*buf_size);218 break;219 case HostCmd_ACT_GEN_GET:220 default:221 tx_buf->buff_size = 0;222 break;223 }224 return 0;225}226 227/*228 * This function prepares command of AMSDU aggregation control.229 *230 * Preparation includes -231 * - Setting command ID, action and proper size232 * - Setting AMSDU control parameters (for SET only)233 * - Ensuring correct endian-ness234 */235int mwifiex_cmd_amsdu_aggr_ctrl(struct host_cmd_ds_command *cmd,236 int cmd_action,237 struct mwifiex_ds_11n_amsdu_aggr_ctrl *aa_ctrl)238{239 struct host_cmd_ds_amsdu_aggr_ctrl *amsdu_ctrl =240 &cmd->params.amsdu_aggr_ctrl;241 u16 action = (u16) cmd_action;242 243 cmd->command = cpu_to_le16(HostCmd_CMD_AMSDU_AGGR_CTRL);244 cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_amsdu_aggr_ctrl)245 + S_DS_GEN);246 amsdu_ctrl->action = cpu_to_le16(action);247 switch (action) {248 case HostCmd_ACT_GEN_SET:249 amsdu_ctrl->enable = cpu_to_le16(aa_ctrl->enable);250 amsdu_ctrl->curr_buf_size = 0;251 break;252 case HostCmd_ACT_GEN_GET:253 default:254 amsdu_ctrl->curr_buf_size = 0;255 break;256 }257 return 0;258}259 260/*261 * This function prepares 11n configuration command.262 *263 * Preparation includes -264 * - Setting command ID, action and proper size265 * - Setting HT Tx capability and HT Tx information fields266 * - Ensuring correct endian-ness267 */268int mwifiex_cmd_11n_cfg(struct mwifiex_private *priv,269 struct host_cmd_ds_command *cmd, u16 cmd_action,270 struct mwifiex_ds_11n_tx_cfg *txcfg)271{272 struct host_cmd_ds_11n_cfg *htcfg = &cmd->params.htcfg;273 274 cmd->command = cpu_to_le16(HostCmd_CMD_11N_CFG);275 cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_11n_cfg) + S_DS_GEN);276 htcfg->action = cpu_to_le16(cmd_action);277 htcfg->ht_tx_cap = cpu_to_le16(txcfg->tx_htcap);278 htcfg->ht_tx_info = cpu_to_le16(txcfg->tx_htinfo);279 280 if (priv->adapter->is_hw_11ac_capable)281 htcfg->misc_config = cpu_to_le16(txcfg->misc_config);282 283 return 0;284}285 286/*287 * This function appends an 11n TLV to a buffer.288 *289 * Buffer allocation is responsibility of the calling290 * function. No size validation is made here.291 *292 * The function fills up the following sections, if applicable -293 * - HT capability IE294 * - HT information IE (with channel list)295 * - 20/40 BSS Coexistence IE296 * - HT Extended Capabilities IE297 */298int299mwifiex_cmd_append_11n_tlv(struct mwifiex_private *priv,300 struct mwifiex_bssdescriptor *bss_desc,301 u8 **buffer)302{303 struct mwifiex_ie_types_htcap *ht_cap;304 struct mwifiex_ie_types_htinfo *ht_info;305 struct mwifiex_ie_types_chan_list_param_set *chan_list;306 struct mwifiex_ie_types_2040bssco *bss_co_2040;307 struct mwifiex_ie_types_extcap *ext_cap;308 int ret_len = 0;309 struct ieee80211_supported_band *sband;310 struct ieee_types_header *hdr;311 u8 radio_type;312 313 if (!buffer || !*buffer)314 return ret_len;315 316 radio_type = mwifiex_band_to_radio_type((u8) bss_desc->bss_band);317 sband = priv->wdev.wiphy->bands[radio_type];318 319 if (bss_desc->bcn_ht_cap) {320 ht_cap = (struct mwifiex_ie_types_htcap *) *buffer;321 memset(ht_cap, 0, sizeof(struct mwifiex_ie_types_htcap));322 ht_cap->header.type = cpu_to_le16(WLAN_EID_HT_CAPABILITY);323 ht_cap->header.len =324 cpu_to_le16(sizeof(struct ieee80211_ht_cap));325 memcpy((u8 *) ht_cap + sizeof(struct mwifiex_ie_types_header),326 (u8 *)bss_desc->bcn_ht_cap,327 le16_to_cpu(ht_cap->header.len));328 329 mwifiex_fill_cap_info(priv, radio_type, &ht_cap->ht_cap);330 /* Update HT40 capability from current channel information */331 if (bss_desc->bcn_ht_oper) {332 u8 ht_param = bss_desc->bcn_ht_oper->ht_param;333 u8 radio =334 mwifiex_band_to_radio_type(bss_desc->bss_band);335 int freq =336 ieee80211_channel_to_frequency(bss_desc->channel,337 radio);338 struct ieee80211_channel *chan =339 ieee80211_get_channel(priv->adapter->wiphy, freq);340 341 switch (ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {342 case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:343 if (chan->flags & IEEE80211_CHAN_NO_HT40PLUS) {344 ht_cap->ht_cap.cap_info &=345 cpu_to_le16346 (~IEEE80211_HT_CAP_SUP_WIDTH_20_40);347 ht_cap->ht_cap.cap_info &=348 cpu_to_le16(~IEEE80211_HT_CAP_SGI_40);349 }350 break;351 case IEEE80211_HT_PARAM_CHA_SEC_BELOW:352 if (chan->flags & IEEE80211_CHAN_NO_HT40MINUS) {353 ht_cap->ht_cap.cap_info &=354 cpu_to_le16355 (~IEEE80211_HT_CAP_SUP_WIDTH_20_40);356 ht_cap->ht_cap.cap_info &=357 cpu_to_le16(~IEEE80211_HT_CAP_SGI_40);358 }359 break;360 }361 }362 363 *buffer += sizeof(struct mwifiex_ie_types_htcap);364 ret_len += sizeof(struct mwifiex_ie_types_htcap);365 }366 367 if (bss_desc->bcn_ht_oper) {368 if (priv->bss_mode == NL80211_IFTYPE_ADHOC) {369 ht_info = (struct mwifiex_ie_types_htinfo *) *buffer;370 memset(ht_info, 0,371 sizeof(struct mwifiex_ie_types_htinfo));372 ht_info->header.type =373 cpu_to_le16(WLAN_EID_HT_OPERATION);374 ht_info->header.len =375 cpu_to_le16(376 sizeof(struct ieee80211_ht_operation));377 378 memcpy((u8 *) ht_info +379 sizeof(struct mwifiex_ie_types_header),380 (u8 *)bss_desc->bcn_ht_oper,381 le16_to_cpu(ht_info->header.len));382 383 if (!(sband->ht_cap.cap &384 IEEE80211_HT_CAP_SUP_WIDTH_20_40))385 ht_info->ht_oper.ht_param &=386 ~(IEEE80211_HT_PARAM_CHAN_WIDTH_ANY |387 IEEE80211_HT_PARAM_CHA_SEC_OFFSET);388 389 *buffer += sizeof(struct mwifiex_ie_types_htinfo);390 ret_len += sizeof(struct mwifiex_ie_types_htinfo);391 }392 393 chan_list =394 (struct mwifiex_ie_types_chan_list_param_set *) *buffer;395 memset(chan_list, 0, struct_size(chan_list, chan_scan_param, 1));396 chan_list->header.type = cpu_to_le16(TLV_TYPE_CHANLIST);397 chan_list->header.len =398 cpu_to_le16(sizeof(struct mwifiex_chan_scan_param_set));399 chan_list->chan_scan_param[0].chan_number =400 bss_desc->bcn_ht_oper->primary_chan;401 chan_list->chan_scan_param[0].radio_type =402 mwifiex_band_to_radio_type((u8) bss_desc->bss_band);403 404 if (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 &&405 bss_desc->bcn_ht_oper->ht_param &406 IEEE80211_HT_PARAM_CHAN_WIDTH_ANY)407 SET_SECONDARYCHAN(chan_list->chan_scan_param[0].408 radio_type,409 (bss_desc->bcn_ht_oper->ht_param &410 IEEE80211_HT_PARAM_CHA_SEC_OFFSET));411 412 *buffer += struct_size(chan_list, chan_scan_param, 1);413 ret_len += struct_size(chan_list, chan_scan_param, 1);414 }415 416 if (bss_desc->bcn_bss_co_2040) {417 bss_co_2040 = (struct mwifiex_ie_types_2040bssco *) *buffer;418 memset(bss_co_2040, 0,419 sizeof(struct mwifiex_ie_types_2040bssco));420 bss_co_2040->header.type = cpu_to_le16(WLAN_EID_BSS_COEX_2040);421 bss_co_2040->header.len =422 cpu_to_le16(sizeof(bss_co_2040->bss_co_2040));423 424 memcpy((u8 *) bss_co_2040 +425 sizeof(struct mwifiex_ie_types_header),426 bss_desc->bcn_bss_co_2040 +427 sizeof(struct ieee_types_header),428 le16_to_cpu(bss_co_2040->header.len));429 430 *buffer += sizeof(struct mwifiex_ie_types_2040bssco);431 ret_len += sizeof(struct mwifiex_ie_types_2040bssco);432 }433 434 if (bss_desc->bcn_ext_cap) {435 hdr = (void *)bss_desc->bcn_ext_cap;436 ext_cap = (struct mwifiex_ie_types_extcap *) *buffer;437 memset(ext_cap, 0, sizeof(struct mwifiex_ie_types_extcap));438 ext_cap->header.type = cpu_to_le16(WLAN_EID_EXT_CAPABILITY);439 ext_cap->header.len = cpu_to_le16(hdr->len);440 441 memcpy((u8 *)ext_cap->ext_capab,442 bss_desc->bcn_ext_cap + sizeof(struct ieee_types_header),443 le16_to_cpu(ext_cap->header.len));444 445 if (hdr->len > 3 &&446 ext_cap->ext_capab[3] & WLAN_EXT_CAPA4_INTERWORKING_ENABLED)447 priv->hs2_enabled = true;448 else449 priv->hs2_enabled = false;450 451 *buffer += sizeof(struct mwifiex_ie_types_extcap) + hdr->len;452 ret_len += sizeof(struct mwifiex_ie_types_extcap) + hdr->len;453 }454 455 return ret_len;456}457 458/*459 * This function checks if the given pointer is valid entry of460 * Tx BA Stream table.461 */462static int mwifiex_is_tx_ba_stream_ptr_valid(struct mwifiex_private *priv,463 struct mwifiex_tx_ba_stream_tbl *tx_tbl_ptr)464{465 struct mwifiex_tx_ba_stream_tbl *tx_ba_tsr_tbl;466 467 list_for_each_entry(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr, list) {468 if (tx_ba_tsr_tbl == tx_tbl_ptr)469 return true;470 }471 472 return false;473}474 475/*476 * This function deletes the given entry in Tx BA Stream table.477 *478 * The function also performs a validity check on the supplied479 * pointer before trying to delete.480 */481void mwifiex_11n_delete_tx_ba_stream_tbl_entry(struct mwifiex_private *priv,482 struct mwifiex_tx_ba_stream_tbl *tx_ba_tsr_tbl)483{484 if (!tx_ba_tsr_tbl &&485 mwifiex_is_tx_ba_stream_ptr_valid(priv, tx_ba_tsr_tbl))486 return;487 488 mwifiex_dbg(priv->adapter, INFO,489 "info: tx_ba_tsr_tbl %p\n", tx_ba_tsr_tbl);490 491 list_del(&tx_ba_tsr_tbl->list);492 493 kfree(tx_ba_tsr_tbl);494}495 496/*497 * This function deletes all the entries in Tx BA Stream table.498 */499void mwifiex_11n_delete_all_tx_ba_stream_tbl(struct mwifiex_private *priv)500{501 int i;502 struct mwifiex_tx_ba_stream_tbl *del_tbl_ptr, *tmp_node;503 504 spin_lock_bh(&priv->tx_ba_stream_tbl_lock);505 list_for_each_entry_safe(del_tbl_ptr, tmp_node,506 &priv->tx_ba_stream_tbl_ptr, list)507 mwifiex_11n_delete_tx_ba_stream_tbl_entry(priv, del_tbl_ptr);508 spin_unlock_bh(&priv->tx_ba_stream_tbl_lock);509 510 INIT_LIST_HEAD(&priv->tx_ba_stream_tbl_ptr);511 512 for (i = 0; i < MAX_NUM_TID; ++i)513 priv->aggr_prio_tbl[i].ampdu_ap =514 priv->aggr_prio_tbl[i].ampdu_user;515}516 517/*518 * This function returns the pointer to an entry in BA Stream519 * table which matches the given RA/TID pair.520 */521struct mwifiex_tx_ba_stream_tbl *522mwifiex_get_ba_tbl(struct mwifiex_private *priv, int tid, u8 *ra)523{524 struct mwifiex_tx_ba_stream_tbl *tx_ba_tsr_tbl;525 526 spin_lock_bh(&priv->tx_ba_stream_tbl_lock);527 list_for_each_entry(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr, list) {528 if (ether_addr_equal_unaligned(tx_ba_tsr_tbl->ra, ra) &&529 tx_ba_tsr_tbl->tid == tid) {530 spin_unlock_bh(&priv->tx_ba_stream_tbl_lock);531 return tx_ba_tsr_tbl;532 }533 }534 spin_unlock_bh(&priv->tx_ba_stream_tbl_lock);535 return NULL;536}537 538/*539 * This function creates an entry in Tx BA stream table for the540 * given RA/TID pair.541 */542void mwifiex_create_ba_tbl(struct mwifiex_private *priv, u8 *ra, int tid,543 enum mwifiex_ba_status ba_status)544{545 struct mwifiex_tx_ba_stream_tbl *new_node;546 struct mwifiex_ra_list_tbl *ra_list;547 int tid_down;548 549 if (!mwifiex_get_ba_tbl(priv, tid, ra)) {550 new_node = kzalloc(sizeof(struct mwifiex_tx_ba_stream_tbl),551 GFP_ATOMIC);552 if (!new_node)553 return;554 555 tid_down = mwifiex_wmm_downgrade_tid(priv, tid);556 ra_list = mwifiex_wmm_get_ralist_node(priv, tid_down, ra);557 if (ra_list) {558 ra_list->ba_status = ba_status;559 ra_list->amsdu_in_ampdu = false;560 }561 INIT_LIST_HEAD(&new_node->list);562 563 new_node->tid = tid;564 new_node->ba_status = ba_status;565 memcpy(new_node->ra, ra, ETH_ALEN);566 567 spin_lock_bh(&priv->tx_ba_stream_tbl_lock);568 list_add_tail(&new_node->list, &priv->tx_ba_stream_tbl_ptr);569 spin_unlock_bh(&priv->tx_ba_stream_tbl_lock);570 }571}572 573/*574 * This function sends an add BA request to the given TID/RA pair.575 */576int mwifiex_send_addba(struct mwifiex_private *priv, int tid, u8 *peer_mac)577{578 struct host_cmd_ds_11n_addba_req add_ba_req;579 u32 tx_win_size = priv->add_ba_param.tx_win_size;580 static u8 dialog_tok;581 int ret;582 u16 block_ack_param_set;583 584 mwifiex_dbg(priv->adapter, CMD, "cmd: %s: tid %d\n", __func__, tid);585 586 memset(&add_ba_req, 0, sizeof(add_ba_req));587 588 if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) &&589 ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) &&590 priv->adapter->is_hw_11ac_capable &&591 memcmp(priv->cfg_bssid, peer_mac, ETH_ALEN)) {592 struct mwifiex_sta_node *sta_ptr;593 594 spin_lock_bh(&priv->sta_list_spinlock);595 sta_ptr = mwifiex_get_sta_entry(priv, peer_mac);596 if (!sta_ptr) {597 spin_unlock_bh(&priv->sta_list_spinlock);598 mwifiex_dbg(priv->adapter, ERROR,599 "BA setup with unknown TDLS peer %pM!\n",600 peer_mac);601 return -1;602 }603 if (sta_ptr->is_11ac_enabled)604 tx_win_size = MWIFIEX_11AC_STA_AMPDU_DEF_TXWINSIZE;605 spin_unlock_bh(&priv->sta_list_spinlock);606 }607 608 block_ack_param_set = (u16)((tid << BLOCKACKPARAM_TID_POS) |609 tx_win_size << BLOCKACKPARAM_WINSIZE_POS |610 IMMEDIATE_BLOCK_ACK);611 612 /* enable AMSDU inside AMPDU */613 if (priv->add_ba_param.tx_amsdu &&614 (priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED))615 block_ack_param_set |= BLOCKACKPARAM_AMSDU_SUPP_MASK;616 617 add_ba_req.block_ack_param_set = cpu_to_le16(block_ack_param_set);618 add_ba_req.block_ack_tmo = cpu_to_le16((u16)priv->add_ba_param.timeout);619 620 ++dialog_tok;621 622 if (dialog_tok == 0)623 dialog_tok = 1;624 625 add_ba_req.dialog_token = dialog_tok;626 memcpy(&add_ba_req.peer_mac_addr, peer_mac, ETH_ALEN);627 628 /* We don't wait for the response of this command */629 ret = mwifiex_send_cmd(priv, HostCmd_CMD_11N_ADDBA_REQ,630 0, 0, &add_ba_req, false);631 632 return ret;633}634 635/*636 * This function sends a delete BA request to the given TID/RA pair.637 */638int mwifiex_send_delba(struct mwifiex_private *priv, int tid, u8 *peer_mac,639 int initiator)640{641 struct host_cmd_ds_11n_delba delba;642 int ret;643 uint16_t del_ba_param_set;644 645 memset(&delba, 0, sizeof(delba));646 647 del_ba_param_set = tid << DELBA_TID_POS;648 649 if (initiator)650 del_ba_param_set |= IEEE80211_DELBA_PARAM_INITIATOR_MASK;651 else652 del_ba_param_set &= ~IEEE80211_DELBA_PARAM_INITIATOR_MASK;653 654 delba.del_ba_param_set = cpu_to_le16(del_ba_param_set);655 memcpy(&delba.peer_mac_addr, peer_mac, ETH_ALEN);656 657 /* We don't wait for the response of this command */658 ret = mwifiex_send_cmd(priv, HostCmd_CMD_11N_DELBA,659 HostCmd_ACT_GEN_SET, 0, &delba, false);660 661 return ret;662}663 664/*665 * This function sends delba to specific tid666 */667void mwifiex_11n_delba(struct mwifiex_private *priv, int tid)668{669 struct mwifiex_rx_reorder_tbl *rx_reor_tbl_ptr;670 671 spin_lock_bh(&priv->rx_reorder_tbl_lock);672 list_for_each_entry(rx_reor_tbl_ptr, &priv->rx_reorder_tbl_ptr, list) {673 if (rx_reor_tbl_ptr->tid == tid) {674 dev_dbg(priv->adapter->dev,675 "Send delba to tid=%d, %pM\n",676 tid, rx_reor_tbl_ptr->ta);677 mwifiex_send_delba(priv, tid, rx_reor_tbl_ptr->ta, 0);678 goto exit;679 }680 }681exit:682 spin_unlock_bh(&priv->rx_reorder_tbl_lock);683}684 685/*686 * This function handles the command response of a delete BA request.687 */688void mwifiex_11n_delete_ba_stream(struct mwifiex_private *priv, u8 *del_ba)689{690 struct host_cmd_ds_11n_delba *cmd_del_ba =691 (struct host_cmd_ds_11n_delba *) del_ba;692 uint16_t del_ba_param_set = le16_to_cpu(cmd_del_ba->del_ba_param_set);693 int tid;694 695 tid = del_ba_param_set >> DELBA_TID_POS;696 697 mwifiex_del_ba_tbl(priv, tid, cmd_del_ba->peer_mac_addr,698 TYPE_DELBA_RECEIVE, INITIATOR_BIT(del_ba_param_set));699}700 701/*702 * This function retrieves the Rx reordering table.703 */704int mwifiex_get_rx_reorder_tbl(struct mwifiex_private *priv,705 struct mwifiex_ds_rx_reorder_tbl *buf)706{707 int i;708 struct mwifiex_ds_rx_reorder_tbl *rx_reo_tbl = buf;709 struct mwifiex_rx_reorder_tbl *rx_reorder_tbl_ptr;710 int count = 0;711 712 spin_lock_bh(&priv->rx_reorder_tbl_lock);713 list_for_each_entry(rx_reorder_tbl_ptr, &priv->rx_reorder_tbl_ptr,714 list) {715 rx_reo_tbl->tid = (u16) rx_reorder_tbl_ptr->tid;716 memcpy(rx_reo_tbl->ta, rx_reorder_tbl_ptr->ta, ETH_ALEN);717 rx_reo_tbl->start_win = rx_reorder_tbl_ptr->start_win;718 rx_reo_tbl->win_size = rx_reorder_tbl_ptr->win_size;719 for (i = 0; i < rx_reorder_tbl_ptr->win_size; ++i) {720 if (rx_reorder_tbl_ptr->rx_reorder_ptr[i])721 rx_reo_tbl->buffer[i] = true;722 else723 rx_reo_tbl->buffer[i] = false;724 }725 rx_reo_tbl++;726 count++;727 728 if (count >= MWIFIEX_MAX_RX_BASTREAM_SUPPORTED)729 break;730 }731 spin_unlock_bh(&priv->rx_reorder_tbl_lock);732 733 return count;734}735 736/*737 * This function retrieves the Tx BA stream table.738 */739int mwifiex_get_tx_ba_stream_tbl(struct mwifiex_private *priv,740 struct mwifiex_ds_tx_ba_stream_tbl *buf)741{742 struct mwifiex_tx_ba_stream_tbl *tx_ba_tsr_tbl;743 struct mwifiex_ds_tx_ba_stream_tbl *rx_reo_tbl = buf;744 int count = 0;745 746 spin_lock_bh(&priv->tx_ba_stream_tbl_lock);747 list_for_each_entry(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr, list) {748 rx_reo_tbl->tid = (u16) tx_ba_tsr_tbl->tid;749 mwifiex_dbg(priv->adapter, DATA, "data: %s tid=%d\n",750 __func__, rx_reo_tbl->tid);751 memcpy(rx_reo_tbl->ra, tx_ba_tsr_tbl->ra, ETH_ALEN);752 rx_reo_tbl->amsdu = tx_ba_tsr_tbl->amsdu;753 rx_reo_tbl++;754 count++;755 if (count >= MWIFIEX_MAX_TX_BASTREAM_SUPPORTED)756 break;757 }758 spin_unlock_bh(&priv->tx_ba_stream_tbl_lock);759 760 return count;761}762 763/*764 * This function retrieves the entry for specific tx BA stream table by RA and765 * deletes it.766 */767void mwifiex_del_tx_ba_stream_tbl_by_ra(struct mwifiex_private *priv, u8 *ra)768{769 struct mwifiex_tx_ba_stream_tbl *tbl, *tmp;770 771 if (!ra)772 return;773 774 spin_lock_bh(&priv->tx_ba_stream_tbl_lock);775 list_for_each_entry_safe(tbl, tmp, &priv->tx_ba_stream_tbl_ptr, list)776 if (!memcmp(tbl->ra, ra, ETH_ALEN))777 mwifiex_11n_delete_tx_ba_stream_tbl_entry(priv, tbl);778 spin_unlock_bh(&priv->tx_ba_stream_tbl_lock);779 780 return;781}782 783/* This function initializes the BlockACK setup information for given784 * mwifiex_private structure.785 */786void mwifiex_set_ba_params(struct mwifiex_private *priv)787{788 priv->add_ba_param.timeout = MWIFIEX_DEFAULT_BLOCK_ACK_TIMEOUT;789 790 if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) {791 priv->add_ba_param.tx_win_size =792 MWIFIEX_UAP_AMPDU_DEF_TXWINSIZE;793 priv->add_ba_param.rx_win_size =794 MWIFIEX_UAP_AMPDU_DEF_RXWINSIZE;795 } else {796 priv->add_ba_param.tx_win_size =797 MWIFIEX_STA_AMPDU_DEF_TXWINSIZE;798 priv->add_ba_param.rx_win_size =799 MWIFIEX_STA_AMPDU_DEF_RXWINSIZE;800 }801 802 priv->add_ba_param.tx_amsdu = true;803 priv->add_ba_param.rx_amsdu = true;804 805 return;806}807 808u8 mwifiex_get_sec_chan_offset(int chan)809{810 u8 sec_offset;811 812 switch (chan) {813 case 36:814 case 44:815 case 52:816 case 60:817 case 100:818 case 108:819 case 116:820 case 124:821 case 132:822 case 140:823 case 149:824 case 157:825 sec_offset = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;826 break;827 case 40:828 case 48:829 case 56:830 case 64:831 case 104:832 case 112:833 case 120:834 case 128:835 case 136:836 case 144:837 case 153:838 case 161:839 sec_offset = IEEE80211_HT_PARAM_CHA_SEC_BELOW;840 break;841 case 165:842 default:843 sec_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE;844 break;845 }846 847 return sec_offset;848}849 850/* This function will send DELBA to entries in the priv's851 * Tx BA stream table852 */853static void854mwifiex_send_delba_txbastream_tbl(struct mwifiex_private *priv, u8 tid)855{856 struct mwifiex_adapter *adapter = priv->adapter;857 struct mwifiex_tx_ba_stream_tbl *tx_ba_stream_tbl_ptr;858 859 list_for_each_entry(tx_ba_stream_tbl_ptr,860 &priv->tx_ba_stream_tbl_ptr, list) {861 if (tx_ba_stream_tbl_ptr->ba_status == BA_SETUP_COMPLETE) {862 if (tid == tx_ba_stream_tbl_ptr->tid) {863 dev_dbg(adapter->dev,864 "Tx:Send delba to tid=%d, %pM\n", tid,865 tx_ba_stream_tbl_ptr->ra);866 mwifiex_send_delba(priv,867 tx_ba_stream_tbl_ptr->tid,868 tx_ba_stream_tbl_ptr->ra, 1);869 return;870 }871 }872 }873}874 875/* This function updates all the tx_win_size876 */877void mwifiex_update_ampdu_txwinsize(struct mwifiex_adapter *adapter)878{879 u8 i, j;880 u32 tx_win_size;881 struct mwifiex_private *priv;882 883 for (i = 0; i < adapter->priv_num; i++) {884 priv = adapter->priv[i];885 tx_win_size = priv->add_ba_param.tx_win_size;886 887 if (priv->bss_type == MWIFIEX_BSS_TYPE_STA)888 priv->add_ba_param.tx_win_size =889 MWIFIEX_STA_AMPDU_DEF_TXWINSIZE;890 891 if (priv->bss_type == MWIFIEX_BSS_TYPE_P2P)892 priv->add_ba_param.tx_win_size =893 MWIFIEX_STA_AMPDU_DEF_TXWINSIZE;894 895 if (priv->bss_type == MWIFIEX_BSS_TYPE_UAP)896 priv->add_ba_param.tx_win_size =897 MWIFIEX_UAP_AMPDU_DEF_TXWINSIZE;898 899 if (adapter->coex_win_size) {900 if (adapter->coex_tx_win_size)901 priv->add_ba_param.tx_win_size =902 adapter->coex_tx_win_size;903 }904 905 if (tx_win_size != priv->add_ba_param.tx_win_size) {906 if (!priv->media_connected)907 continue;908 for (j = 0; j < MAX_NUM_TID; j++)909 mwifiex_send_delba_txbastream_tbl(priv, j);910 }911 }912}913