1/*
2 * Marvell Wireless LAN device driver: 802.11n
3 *
4 * Copyright (C) 2011-2014, Marvell International Ltd.
5 *
6 * This software file (the "File") is distributed by Marvell International
7 * Ltd. under the terms of the GNU General Public License Version 2, June 1991
8 * (the "License").  You may use, redistribute and/or modify this File in
9 * accordance with the terms and conditions of the License, a copy of which
10 * is available by writing to the Free Software Foundation, Inc.,
11 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
12 * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
13 *
14 * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
16 * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
17 * this warranty disclaimer.
18 */
19
20#include "decl.h"
21#include "ioctl.h"
22#include "util.h"
23#include "fw.h"
24#include "main.h"
25#include "wmm.h"
26#include "11n.h"
27
28/*
29 * Fills HT capability information field, AMPDU Parameters field, HT extended
30 * capability field, and supported MCS set fields.
31 *
32 * HT capability information field, AMPDU Parameters field, supported MCS set
33 * fields are retrieved from cfg80211 stack
34 *
35 * RD responder bit to set to clear in the extended capability header.
36 */
37int mwifiex_fill_cap_info(struct mwifiex_private *priv, u8 radio_type,
38			  struct ieee80211_ht_cap *ht_cap)
39{
40	uint16_t ht_ext_cap = le16_to_cpu(ht_cap->extended_ht_cap_info);
41	struct ieee80211_supported_band *sband =
42					priv->wdev.wiphy->bands[radio_type];
43
44	if (WARN_ON_ONCE(!sband)) {
45		dev_err(priv->adapter->dev, "Invalid radio type!\n");
46		return -EINVAL;
47	}
48
49	ht_cap->ampdu_params_info =
50		(sband->ht_cap.ampdu_factor &
51		 IEEE80211_HT_AMPDU_PARM_FACTOR) |
52		((sband->ht_cap.ampdu_density <<
53		 IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT) &
54		 IEEE80211_HT_AMPDU_PARM_DENSITY);
55
56	memcpy((u8 *)&ht_cap->mcs, &sband->ht_cap.mcs,
57	       sizeof(sband->ht_cap.mcs));
58
59	if (priv->bss_mode == NL80211_IFTYPE_STATION ||
60	    (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 &&
61	     (priv->adapter->sec_chan_offset !=
62					IEEE80211_HT_PARAM_CHA_SEC_NONE)))
63		/* Set MCS32 for infra mode or ad-hoc mode with 40MHz support */
64		SETHT_MCS32(ht_cap->mcs.rx_mask);
65
66	/* Clear RD responder bit */
67	ht_ext_cap &= ~IEEE80211_HT_EXT_CAP_RD_RESPONDER;
68
69	ht_cap->cap_info = cpu_to_le16(sband->ht_cap.cap);
70	ht_cap->extended_ht_cap_info = cpu_to_le16(ht_ext_cap);
71
72	if (ISSUPP_BEAMFORMING(priv->adapter->hw_dot_11n_dev_cap))
73		ht_cap->tx_BF_cap_info = cpu_to_le32(MWIFIEX_DEF_11N_TX_BF_CAP);
74
75	return 0;
76}
77
78/*
79 * This function returns the pointer to an entry in BA Stream
80 * table which matches the requested BA status.
81 */
82static struct mwifiex_tx_ba_stream_tbl *
83mwifiex_get_ba_status(struct mwifiex_private *priv,
84		      enum mwifiex_ba_status ba_status)
85{
86	struct mwifiex_tx_ba_stream_tbl *tx_ba_tsr_tbl;
87	unsigned long flags;
88
89	spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags);
90	list_for_each_entry(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr, list) {
91		if (tx_ba_tsr_tbl->ba_status == ba_status) {
92			spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock,
93					       flags);
94			return tx_ba_tsr_tbl;
95		}
96	}
97	spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags);
98	return NULL;
99}
100
101/*
102 * This function handles the command response of delete a block
103 * ack request.
104 *
105 * The function checks the response success status and takes action
106 * accordingly (send an add BA request in case of success, or recreate
107 * the deleted stream in case of failure, if the add BA was also
108 * initiated by us).
109 */
110int mwifiex_ret_11n_delba(struct mwifiex_private *priv,
111			  struct host_cmd_ds_command *resp)
112{
113	int tid;
114	struct mwifiex_tx_ba_stream_tbl *tx_ba_tbl;
115	struct host_cmd_ds_11n_delba *del_ba = &resp->params.del_ba;
116	uint16_t del_ba_param_set = le16_to_cpu(del_ba->del_ba_param_set);
117
118	tid = del_ba_param_set >> DELBA_TID_POS;
119	if (del_ba->del_result == BA_RESULT_SUCCESS) {
120		mwifiex_del_ba_tbl(priv, tid, del_ba->peer_mac_addr,
121				   TYPE_DELBA_SENT,
122				   INITIATOR_BIT(del_ba_param_set));
123
124		tx_ba_tbl = mwifiex_get_ba_status(priv, BA_SETUP_INPROGRESS);
125		if (tx_ba_tbl)
126			mwifiex_send_addba(priv, tx_ba_tbl->tid,
127					   tx_ba_tbl->ra);
128	} else { /*
129		  * In case of failure, recreate the deleted stream in case
130		  * we initiated the ADDBA
131		  */
132		if (!INITIATOR_BIT(del_ba_param_set))
133			return 0;
134
135		mwifiex_create_ba_tbl(priv, del_ba->peer_mac_addr, tid,
136				      BA_SETUP_INPROGRESS);
137
138		tx_ba_tbl = mwifiex_get_ba_status(priv, BA_SETUP_INPROGRESS);
139
140		if (tx_ba_tbl)
141			mwifiex_del_ba_tbl(priv, tx_ba_tbl->tid, tx_ba_tbl->ra,
142					   TYPE_DELBA_SENT, true);
143	}
144
145	return 0;
146}
147
148/*
149 * This function handles the command response of add a block
150 * ack request.
151 *
152 * Handling includes changing the header fields to CPU formats, checking
153 * the response success status and taking actions accordingly (delete the
154 * BA stream table in case of failure).
155 */
156int mwifiex_ret_11n_addba_req(struct mwifiex_private *priv,
157			      struct host_cmd_ds_command *resp)
158{
159	int tid;
160	struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = &resp->params.add_ba_rsp;
161	struct mwifiex_tx_ba_stream_tbl *tx_ba_tbl;
162	struct mwifiex_ra_list_tbl *ra_list;
163	u16 block_ack_param_set = le16_to_cpu(add_ba_rsp->block_ack_param_set);
164
165	add_ba_rsp->ssn = cpu_to_le16((le16_to_cpu(add_ba_rsp->ssn))
166			& SSN_MASK);
167
168	tid = (block_ack_param_set & IEEE80211_ADDBA_PARAM_TID_MASK)
169	       >> BLOCKACKPARAM_TID_POS;
170	ra_list = mwifiex_wmm_get_ralist_node(priv, tid, add_ba_rsp->
171		peer_mac_addr);
172	if (le16_to_cpu(add_ba_rsp->status_code) != BA_RESULT_SUCCESS) {
173		if (ra_list) {
174			ra_list->ba_status = BA_SETUP_NONE;
175			ra_list->amsdu_in_ampdu = false;
176		}
177		mwifiex_del_ba_tbl(priv, tid, add_ba_rsp->peer_mac_addr,
178				   TYPE_DELBA_SENT, true);
179		if (add_ba_rsp->add_rsp_result != BA_RESULT_TIMEOUT)
180			priv->aggr_prio_tbl[tid].ampdu_ap =
181				BA_STREAM_NOT_ALLOWED;
182		return 0;
183	}
184
185	tx_ba_tbl = mwifiex_get_ba_tbl(priv, tid, add_ba_rsp->peer_mac_addr);
186	if (tx_ba_tbl) {
187		dev_dbg(priv->adapter->dev, "info: BA stream complete\n");
188		tx_ba_tbl->ba_status = BA_SETUP_COMPLETE;
189		if ((block_ack_param_set & BLOCKACKPARAM_AMSDU_SUPP_MASK) &&
190		    priv->add_ba_param.tx_amsdu &&
191		    (priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED))
192			tx_ba_tbl->amsdu = true;
193		else
194			tx_ba_tbl->amsdu = false;
195		if (ra_list) {
196			ra_list->amsdu_in_ampdu = tx_ba_tbl->amsdu;
197			ra_list->ba_status = BA_SETUP_COMPLETE;
198		}
199	} else {
200		dev_err(priv->adapter->dev, "BA stream not created\n");
201	}
202
203	return 0;
204}
205
206/*
207 * This function prepares command of reconfigure Tx buffer.
208 *
209 * Preparation includes -
210 *      - Setting command ID, action and proper size
211 *      - Setting Tx buffer size (for SET only)
212 *      - Ensuring correct endian-ness
213 */
214int mwifiex_cmd_recfg_tx_buf(struct mwifiex_private *priv,
215			     struct host_cmd_ds_command *cmd, int cmd_action,
216			     u16 *buf_size)
217{
218	struct host_cmd_ds_txbuf_cfg *tx_buf = &cmd->params.tx_buf;
219	u16 action = (u16) cmd_action;
220
221	cmd->command = cpu_to_le16(HostCmd_CMD_RECONFIGURE_TX_BUFF);
222	cmd->size =
223		cpu_to_le16(sizeof(struct host_cmd_ds_txbuf_cfg) + S_DS_GEN);
224	tx_buf->action = cpu_to_le16(action);
225	switch (action) {
226	case HostCmd_ACT_GEN_SET:
227		dev_dbg(priv->adapter->dev, "cmd: set tx_buf=%d\n", *buf_size);
228		tx_buf->buff_size = cpu_to_le16(*buf_size);
229		break;
230	case HostCmd_ACT_GEN_GET:
231	default:
232		tx_buf->buff_size = 0;
233		break;
234	}
235	return 0;
236}
237
238/*
239 * This function prepares command of AMSDU aggregation control.
240 *
241 * Preparation includes -
242 *      - Setting command ID, action and proper size
243 *      - Setting AMSDU control parameters (for SET only)
244 *      - Ensuring correct endian-ness
245 */
246int mwifiex_cmd_amsdu_aggr_ctrl(struct host_cmd_ds_command *cmd,
247				int cmd_action,
248				struct mwifiex_ds_11n_amsdu_aggr_ctrl *aa_ctrl)
249{
250	struct host_cmd_ds_amsdu_aggr_ctrl *amsdu_ctrl =
251		&cmd->params.amsdu_aggr_ctrl;
252	u16 action = (u16) cmd_action;
253
254	cmd->command = cpu_to_le16(HostCmd_CMD_AMSDU_AGGR_CTRL);
255	cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_amsdu_aggr_ctrl)
256				+ S_DS_GEN);
257	amsdu_ctrl->action = cpu_to_le16(action);
258	switch (action) {
259	case HostCmd_ACT_GEN_SET:
260		amsdu_ctrl->enable = cpu_to_le16(aa_ctrl->enable);
261		amsdu_ctrl->curr_buf_size = 0;
262		break;
263	case HostCmd_ACT_GEN_GET:
264	default:
265		amsdu_ctrl->curr_buf_size = 0;
266		break;
267	}
268	return 0;
269}
270
271/*
272 * This function prepares 11n configuration command.
273 *
274 * Preparation includes -
275 *      - Setting command ID, action and proper size
276 *      - Setting HT Tx capability and HT Tx information fields
277 *      - Ensuring correct endian-ness
278 */
279int mwifiex_cmd_11n_cfg(struct mwifiex_private *priv,
280			struct host_cmd_ds_command *cmd, u16 cmd_action,
281			struct mwifiex_ds_11n_tx_cfg *txcfg)
282{
283	struct host_cmd_ds_11n_cfg *htcfg = &cmd->params.htcfg;
284
285	cmd->command = cpu_to_le16(HostCmd_CMD_11N_CFG);
286	cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_11n_cfg) + S_DS_GEN);
287	htcfg->action = cpu_to_le16(cmd_action);
288	htcfg->ht_tx_cap = cpu_to_le16(txcfg->tx_htcap);
289	htcfg->ht_tx_info = cpu_to_le16(txcfg->tx_htinfo);
290
291	if (priv->adapter->is_hw_11ac_capable)
292		htcfg->misc_config = cpu_to_le16(txcfg->misc_config);
293
294	return 0;
295}
296
297/*
298 * This function appends an 11n TLV to a buffer.
299 *
300 * Buffer allocation is responsibility of the calling
301 * function. No size validation is made here.
302 *
303 * The function fills up the following sections, if applicable -
304 *      - HT capability IE
305 *      - HT information IE (with channel list)
306 *      - 20/40 BSS Coexistence IE
307 *      - HT Extended Capabilities IE
308 */
309int
310mwifiex_cmd_append_11n_tlv(struct mwifiex_private *priv,
311			   struct mwifiex_bssdescriptor *bss_desc,
312			   u8 **buffer)
313{
314	struct mwifiex_ie_types_htcap *ht_cap;
315	struct mwifiex_ie_types_htinfo *ht_info;
316	struct mwifiex_ie_types_chan_list_param_set *chan_list;
317	struct mwifiex_ie_types_2040bssco *bss_co_2040;
318	struct mwifiex_ie_types_extcap *ext_cap;
319	int ret_len = 0;
320	struct ieee80211_supported_band *sband;
321	struct ieee_types_header *hdr;
322	u8 radio_type;
323
324	if (!buffer || !*buffer)
325		return ret_len;
326
327	radio_type = mwifiex_band_to_radio_type((u8) bss_desc->bss_band);
328	sband = priv->wdev.wiphy->bands[radio_type];
329
330	if (bss_desc->bcn_ht_cap) {
331		ht_cap = (struct mwifiex_ie_types_htcap *) *buffer;
332		memset(ht_cap, 0, sizeof(struct mwifiex_ie_types_htcap));
333		ht_cap->header.type = cpu_to_le16(WLAN_EID_HT_CAPABILITY);
334		ht_cap->header.len =
335				cpu_to_le16(sizeof(struct ieee80211_ht_cap));
336		memcpy((u8 *) ht_cap + sizeof(struct mwifiex_ie_types_header),
337		       (u8 *)bss_desc->bcn_ht_cap,
338		       le16_to_cpu(ht_cap->header.len));
339
340		mwifiex_fill_cap_info(priv, radio_type, &ht_cap->ht_cap);
341
342		*buffer += sizeof(struct mwifiex_ie_types_htcap);
343		ret_len += sizeof(struct mwifiex_ie_types_htcap);
344	}
345
346	if (bss_desc->bcn_ht_oper) {
347		if (priv->bss_mode == NL80211_IFTYPE_ADHOC) {
348			ht_info = (struct mwifiex_ie_types_htinfo *) *buffer;
349			memset(ht_info, 0,
350			       sizeof(struct mwifiex_ie_types_htinfo));
351			ht_info->header.type =
352					cpu_to_le16(WLAN_EID_HT_OPERATION);
353			ht_info->header.len =
354				cpu_to_le16(
355					sizeof(struct ieee80211_ht_operation));
356
357			memcpy((u8 *) ht_info +
358			       sizeof(struct mwifiex_ie_types_header),
359			       (u8 *)bss_desc->bcn_ht_oper,
360			       le16_to_cpu(ht_info->header.len));
361
362			if (!(sband->ht_cap.cap &
363					IEEE80211_HT_CAP_SUP_WIDTH_20_40))
364				ht_info->ht_oper.ht_param &=
365					~(IEEE80211_HT_PARAM_CHAN_WIDTH_ANY |
366					IEEE80211_HT_PARAM_CHA_SEC_OFFSET);
367
368			*buffer += sizeof(struct mwifiex_ie_types_htinfo);
369			ret_len += sizeof(struct mwifiex_ie_types_htinfo);
370		}
371
372		chan_list =
373			(struct mwifiex_ie_types_chan_list_param_set *) *buffer;
374		memset(chan_list, 0,
375		       sizeof(struct mwifiex_ie_types_chan_list_param_set));
376		chan_list->header.type = cpu_to_le16(TLV_TYPE_CHANLIST);
377		chan_list->header.len = cpu_to_le16(
378			sizeof(struct mwifiex_ie_types_chan_list_param_set) -
379			sizeof(struct mwifiex_ie_types_header));
380		chan_list->chan_scan_param[0].chan_number =
381			bss_desc->bcn_ht_oper->primary_chan;
382		chan_list->chan_scan_param[0].radio_type =
383			mwifiex_band_to_radio_type((u8) bss_desc->bss_band);
384
385		if (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 &&
386		    bss_desc->bcn_ht_oper->ht_param &
387		    IEEE80211_HT_PARAM_CHAN_WIDTH_ANY)
388			SET_SECONDARYCHAN(chan_list->chan_scan_param[0].
389					  radio_type,
390					  (bss_desc->bcn_ht_oper->ht_param &
391					  IEEE80211_HT_PARAM_CHA_SEC_OFFSET));
392
393		*buffer += sizeof(struct mwifiex_ie_types_chan_list_param_set);
394		ret_len += sizeof(struct mwifiex_ie_types_chan_list_param_set);
395	}
396
397	if (bss_desc->bcn_bss_co_2040) {
398		bss_co_2040 = (struct mwifiex_ie_types_2040bssco *) *buffer;
399		memset(bss_co_2040, 0,
400		       sizeof(struct mwifiex_ie_types_2040bssco));
401		bss_co_2040->header.type = cpu_to_le16(WLAN_EID_BSS_COEX_2040);
402		bss_co_2040->header.len =
403		       cpu_to_le16(sizeof(bss_co_2040->bss_co_2040));
404
405		memcpy((u8 *) bss_co_2040 +
406		       sizeof(struct mwifiex_ie_types_header),
407		       bss_desc->bcn_bss_co_2040 +
408		       sizeof(struct ieee_types_header),
409		       le16_to_cpu(bss_co_2040->header.len));
410
411		*buffer += sizeof(struct mwifiex_ie_types_2040bssco);
412		ret_len += sizeof(struct mwifiex_ie_types_2040bssco);
413	}
414
415	if (bss_desc->bcn_ext_cap) {
416		hdr = (void *)bss_desc->bcn_ext_cap;
417		ext_cap = (struct mwifiex_ie_types_extcap *) *buffer;
418		memset(ext_cap, 0, sizeof(struct mwifiex_ie_types_extcap));
419		ext_cap->header.type = cpu_to_le16(WLAN_EID_EXT_CAPABILITY);
420		ext_cap->header.len = cpu_to_le16(hdr->len);
421
422		memcpy((u8 *)ext_cap->ext_capab,
423		       bss_desc->bcn_ext_cap + sizeof(struct ieee_types_header),
424		       le16_to_cpu(ext_cap->header.len));
425
426		if (hdr->len > 3 &&
427		    ext_cap->ext_capab[3] & WLAN_EXT_CAPA4_INTERWORKING_ENABLED)
428			priv->hs2_enabled = true;
429		else
430			priv->hs2_enabled = false;
431
432		*buffer += sizeof(struct mwifiex_ie_types_extcap) + hdr->len;
433		ret_len += sizeof(struct mwifiex_ie_types_extcap) + hdr->len;
434	}
435
436	return ret_len;
437}
438
439/*
440 * This function checks if the given pointer is valid entry of
441 * Tx BA Stream table.
442 */
443static int mwifiex_is_tx_ba_stream_ptr_valid(struct mwifiex_private *priv,
444				struct mwifiex_tx_ba_stream_tbl *tx_tbl_ptr)
445{
446	struct mwifiex_tx_ba_stream_tbl *tx_ba_tsr_tbl;
447
448	list_for_each_entry(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr, list) {
449		if (tx_ba_tsr_tbl == tx_tbl_ptr)
450			return true;
451	}
452
453	return false;
454}
455
456/*
457 * This function deletes the given entry in Tx BA Stream table.
458 *
459 * The function also performs a validity check on the supplied
460 * pointer before trying to delete.
461 */
462void mwifiex_11n_delete_tx_ba_stream_tbl_entry(struct mwifiex_private *priv,
463				struct mwifiex_tx_ba_stream_tbl *tx_ba_tsr_tbl)
464{
465	if (!tx_ba_tsr_tbl &&
466	    mwifiex_is_tx_ba_stream_ptr_valid(priv, tx_ba_tsr_tbl))
467		return;
468
469	dev_dbg(priv->adapter->dev, "info: tx_ba_tsr_tbl %p\n", tx_ba_tsr_tbl);
470
471	list_del(&tx_ba_tsr_tbl->list);
472
473	kfree(tx_ba_tsr_tbl);
474}
475
476/*
477 * This function deletes all the entries in Tx BA Stream table.
478 */
479void mwifiex_11n_delete_all_tx_ba_stream_tbl(struct mwifiex_private *priv)
480{
481	int i;
482	struct mwifiex_tx_ba_stream_tbl *del_tbl_ptr, *tmp_node;
483	unsigned long flags;
484
485	spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags);
486	list_for_each_entry_safe(del_tbl_ptr, tmp_node,
487				 &priv->tx_ba_stream_tbl_ptr, list)
488		mwifiex_11n_delete_tx_ba_stream_tbl_entry(priv, del_tbl_ptr);
489	spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags);
490
491	INIT_LIST_HEAD(&priv->tx_ba_stream_tbl_ptr);
492
493	for (i = 0; i < MAX_NUM_TID; ++i)
494		priv->aggr_prio_tbl[i].ampdu_ap =
495			priv->aggr_prio_tbl[i].ampdu_user;
496}
497
498/*
499 * This function returns the pointer to an entry in BA Stream
500 * table which matches the given RA/TID pair.
501 */
502struct mwifiex_tx_ba_stream_tbl *
503mwifiex_get_ba_tbl(struct mwifiex_private *priv, int tid, u8 *ra)
504{
505	struct mwifiex_tx_ba_stream_tbl *tx_ba_tsr_tbl;
506	unsigned long flags;
507
508	spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags);
509	list_for_each_entry(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr, list) {
510		if (ether_addr_equal_unaligned(tx_ba_tsr_tbl->ra, ra) &&
511		    tx_ba_tsr_tbl->tid == tid) {
512			spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock,
513					       flags);
514			return tx_ba_tsr_tbl;
515		}
516	}
517	spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags);
518	return NULL;
519}
520
521/*
522 * This function creates an entry in Tx BA stream table for the
523 * given RA/TID pair.
524 */
525void mwifiex_create_ba_tbl(struct mwifiex_private *priv, u8 *ra, int tid,
526			   enum mwifiex_ba_status ba_status)
527{
528	struct mwifiex_tx_ba_stream_tbl *new_node;
529	struct mwifiex_ra_list_tbl *ra_list;
530	unsigned long flags;
531
532	if (!mwifiex_get_ba_tbl(priv, tid, ra)) {
533		new_node = kzalloc(sizeof(struct mwifiex_tx_ba_stream_tbl),
534				   GFP_ATOMIC);
535		if (!new_node)
536			return;
537		ra_list = mwifiex_wmm_get_ralist_node(priv, tid, ra);
538		if (ra_list) {
539			ra_list->ba_status = ba_status;
540			ra_list->amsdu_in_ampdu = false;
541		}
542		INIT_LIST_HEAD(&new_node->list);
543
544		new_node->tid = tid;
545		new_node->ba_status = ba_status;
546		memcpy(new_node->ra, ra, ETH_ALEN);
547
548		spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags);
549		list_add_tail(&new_node->list, &priv->tx_ba_stream_tbl_ptr);
550		spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags);
551	}
552}
553
554/*
555 * This function sends an add BA request to the given TID/RA pair.
556 */
557int mwifiex_send_addba(struct mwifiex_private *priv, int tid, u8 *peer_mac)
558{
559	struct host_cmd_ds_11n_addba_req add_ba_req;
560	u32 tx_win_size = priv->add_ba_param.tx_win_size;
561	static u8 dialog_tok;
562	int ret;
563	unsigned long flags;
564	u16 block_ack_param_set;
565
566	dev_dbg(priv->adapter->dev, "cmd: %s: tid %d\n", __func__, tid);
567
568	if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) &&
569	    ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) &&
570	    priv->adapter->is_hw_11ac_capable &&
571	    memcmp(priv->cfg_bssid, peer_mac, ETH_ALEN)) {
572		struct mwifiex_sta_node *sta_ptr;
573
574		spin_lock_irqsave(&priv->sta_list_spinlock, flags);
575		sta_ptr = mwifiex_get_sta_entry(priv, peer_mac);
576		if (!sta_ptr) {
577			spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
578			dev_warn(priv->adapter->dev,
579				 "BA setup with unknown TDLS peer %pM!\n",
580				peer_mac);
581			return -1;
582		}
583		if (sta_ptr->is_11ac_enabled)
584			tx_win_size = MWIFIEX_11AC_STA_AMPDU_DEF_TXWINSIZE;
585		spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
586	}
587
588	block_ack_param_set = (u16)((tid << BLOCKACKPARAM_TID_POS) |
589				    tx_win_size << BLOCKACKPARAM_WINSIZE_POS |
590				    IMMEDIATE_BLOCK_ACK);
591
592	/* enable AMSDU inside AMPDU */
593	if (priv->add_ba_param.tx_amsdu &&
594	    (priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED))
595		block_ack_param_set |= BLOCKACKPARAM_AMSDU_SUPP_MASK;
596
597	add_ba_req.block_ack_param_set = cpu_to_le16(block_ack_param_set);
598	add_ba_req.block_ack_tmo = cpu_to_le16((u16)priv->add_ba_param.timeout);
599
600	++dialog_tok;
601
602	if (dialog_tok == 0)
603		dialog_tok = 1;
604
605	add_ba_req.dialog_token = dialog_tok;
606	memcpy(&add_ba_req.peer_mac_addr, peer_mac, ETH_ALEN);
607
608	/* We don't wait for the response of this command */
609	ret = mwifiex_send_cmd(priv, HostCmd_CMD_11N_ADDBA_REQ,
610			       0, 0, &add_ba_req, false);
611
612	return ret;
613}
614
615/*
616 * This function sends a delete BA request to the given TID/RA pair.
617 */
618int mwifiex_send_delba(struct mwifiex_private *priv, int tid, u8 *peer_mac,
619		       int initiator)
620{
621	struct host_cmd_ds_11n_delba delba;
622	int ret;
623	uint16_t del_ba_param_set;
624
625	memset(&delba, 0, sizeof(delba));
626	delba.del_ba_param_set = cpu_to_le16(tid << DELBA_TID_POS);
627
628	del_ba_param_set = le16_to_cpu(delba.del_ba_param_set);
629	if (initiator)
630		del_ba_param_set |= IEEE80211_DELBA_PARAM_INITIATOR_MASK;
631	else
632		del_ba_param_set &= ~IEEE80211_DELBA_PARAM_INITIATOR_MASK;
633
634	memcpy(&delba.peer_mac_addr, peer_mac, ETH_ALEN);
635
636	/* We don't wait for the response of this command */
637	ret = mwifiex_send_cmd(priv, HostCmd_CMD_11N_DELBA,
638			       HostCmd_ACT_GEN_SET, 0, &delba, false);
639
640	return ret;
641}
642
643/*
644 * This function handles the command response of a delete BA request.
645 */
646void mwifiex_11n_delete_ba_stream(struct mwifiex_private *priv, u8 *del_ba)
647{
648	struct host_cmd_ds_11n_delba *cmd_del_ba =
649		(struct host_cmd_ds_11n_delba *) del_ba;
650	uint16_t del_ba_param_set = le16_to_cpu(cmd_del_ba->del_ba_param_set);
651	int tid;
652
653	tid = del_ba_param_set >> DELBA_TID_POS;
654
655	mwifiex_del_ba_tbl(priv, tid, cmd_del_ba->peer_mac_addr,
656			   TYPE_DELBA_RECEIVE, INITIATOR_BIT(del_ba_param_set));
657}
658
659/*
660 * This function retrieves the Rx reordering table.
661 */
662int mwifiex_get_rx_reorder_tbl(struct mwifiex_private *priv,
663			       struct mwifiex_ds_rx_reorder_tbl *buf)
664{
665	int i;
666	struct mwifiex_ds_rx_reorder_tbl *rx_reo_tbl = buf;
667	struct mwifiex_rx_reorder_tbl *rx_reorder_tbl_ptr;
668	int count = 0;
669	unsigned long flags;
670
671	spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags);
672	list_for_each_entry(rx_reorder_tbl_ptr, &priv->rx_reorder_tbl_ptr,
673			    list) {
674		rx_reo_tbl->tid = (u16) rx_reorder_tbl_ptr->tid;
675		memcpy(rx_reo_tbl->ta, rx_reorder_tbl_ptr->ta, ETH_ALEN);
676		rx_reo_tbl->start_win = rx_reorder_tbl_ptr->start_win;
677		rx_reo_tbl->win_size = rx_reorder_tbl_ptr->win_size;
678		for (i = 0; i < rx_reorder_tbl_ptr->win_size; ++i) {
679			if (rx_reorder_tbl_ptr->rx_reorder_ptr[i])
680				rx_reo_tbl->buffer[i] = true;
681			else
682				rx_reo_tbl->buffer[i] = false;
683		}
684		rx_reo_tbl++;
685		count++;
686
687		if (count >= MWIFIEX_MAX_RX_BASTREAM_SUPPORTED)
688			break;
689	}
690	spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags);
691
692	return count;
693}
694
695/*
696 * This function retrieves the Tx BA stream table.
697 */
698int mwifiex_get_tx_ba_stream_tbl(struct mwifiex_private *priv,
699				 struct mwifiex_ds_tx_ba_stream_tbl *buf)
700{
701	struct mwifiex_tx_ba_stream_tbl *tx_ba_tsr_tbl;
702	struct mwifiex_ds_tx_ba_stream_tbl *rx_reo_tbl = buf;
703	int count = 0;
704	unsigned long flags;
705
706	spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags);
707	list_for_each_entry(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr, list) {
708		rx_reo_tbl->tid = (u16) tx_ba_tsr_tbl->tid;
709		dev_dbg(priv->adapter->dev, "data: %s tid=%d\n",
710			__func__, rx_reo_tbl->tid);
711		memcpy(rx_reo_tbl->ra, tx_ba_tsr_tbl->ra, ETH_ALEN);
712		rx_reo_tbl->amsdu = tx_ba_tsr_tbl->amsdu;
713		rx_reo_tbl++;
714		count++;
715		if (count >= MWIFIEX_MAX_TX_BASTREAM_SUPPORTED)
716			break;
717	}
718	spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags);
719
720	return count;
721}
722
723/*
724 * This function retrieves the entry for specific tx BA stream table by RA and
725 * deletes it.
726 */
727void mwifiex_del_tx_ba_stream_tbl_by_ra(struct mwifiex_private *priv, u8 *ra)
728{
729	struct mwifiex_tx_ba_stream_tbl *tbl, *tmp;
730	unsigned long flags;
731
732	if (!ra)
733		return;
734
735	spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags);
736	list_for_each_entry_safe(tbl, tmp, &priv->tx_ba_stream_tbl_ptr, list) {
737		if (!memcmp(tbl->ra, ra, ETH_ALEN)) {
738			spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock,
739					       flags);
740			mwifiex_11n_delete_tx_ba_stream_tbl_entry(priv, tbl);
741			spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags);
742		}
743	}
744	spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags);
745
746	return;
747}
748
749/* This function initializes the BlockACK setup information for given
750 * mwifiex_private structure.
751 */
752void mwifiex_set_ba_params(struct mwifiex_private *priv)
753{
754	priv->add_ba_param.timeout = MWIFIEX_DEFAULT_BLOCK_ACK_TIMEOUT;
755
756	if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) {
757		priv->add_ba_param.tx_win_size =
758						MWIFIEX_UAP_AMPDU_DEF_TXWINSIZE;
759		priv->add_ba_param.rx_win_size =
760						MWIFIEX_UAP_AMPDU_DEF_RXWINSIZE;
761	} else {
762		priv->add_ba_param.tx_win_size =
763						MWIFIEX_STA_AMPDU_DEF_TXWINSIZE;
764		priv->add_ba_param.rx_win_size =
765						MWIFIEX_STA_AMPDU_DEF_RXWINSIZE;
766	}
767
768	priv->add_ba_param.tx_amsdu = true;
769	priv->add_ba_param.rx_amsdu = true;
770
771	return;
772}
773
774u8 mwifiex_get_sec_chan_offset(int chan)
775{
776	u8 sec_offset;
777
778	switch (chan) {
779	case 36:
780	case 44:
781	case 52:
782	case 60:
783	case 100:
784	case 108:
785	case 116:
786	case 124:
787	case 132:
788	case 140:
789	case 149:
790	case 157:
791		sec_offset = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
792		break;
793	case 40:
794	case 48:
795	case 56:
796	case 64:
797	case 104:
798	case 112:
799	case 120:
800	case 128:
801	case 136:
802	case 144:
803	case 153:
804	case 161:
805		sec_offset = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
806		break;
807	case 165:
808	default:
809		sec_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE;
810		break;
811	}
812
813	return sec_offset;
814}
815