brintos

brintos / linux-shallow public Read only

0
0
Text · 25.7 KiB · 66f0f53 Raw
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