/** @file mlan_11n.h
 *
 *  @brief Interface for the 802.11n mlan_11n module implemented in mlan_11n.c
 *
 *  Driver interface functions and type declarations for the 11n module
 *    implemented in mlan_11n.c.
 *
 *
 *  Copyright 2008-2020 NXP
 *
 *  This software file (the File) is distributed by NXP
 *  under the terms of the GNU General Public License Version 2, June 1991
 *  (the License).  You may use, redistribute and/or modify the File in
 *  accordance with the terms and conditions of the License, a copy of which
 *  is available by writing to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
 *  worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
 *
 *  THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
 *  ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
 *  this warranty disclaimer.
 *
 */

/********************************************************
Change log:
    12/01/2008: initial version
********************************************************/

#ifndef _MLAN_11N_H_
#define _MLAN_11N_H_

#include "mlan_11n_aggr.h"
#include "mlan_11n_rxreorder.h"
#include "mlan_wmm.h"

/** Print the 802.11n device capability */
void wlan_show_dot11ndevcap(pmlan_adapter pmadapter, t_u32 cap);
/** Print the 802.11n device MCS */
void wlan_show_devmcssupport(pmlan_adapter pmadapter, t_u8 support);
/** Handle the command response of a delete block ack request */
mlan_status wlan_ret_11n_delba(mlan_private *priv, HostCmd_DS_COMMAND *resp);
/** Handle the command response of an add block ack request */
mlan_status wlan_ret_11n_addba_req(mlan_private *priv,
				   HostCmd_DS_COMMAND *resp);
/** Handle the command response of 11ncfg command */
mlan_status wlan_ret_11n_cfg(pmlan_private pmpriv, HostCmd_DS_COMMAND *resp,
			     mlan_ioctl_req *pioctl_buf);
/** Prepare 11ncfg command */
mlan_status wlan_cmd_11n_cfg(pmlan_private pmpriv, HostCmd_DS_COMMAND *cmd,
			     t_u16 cmd_action, t_void *pdata_buf);
/** Prepare reject addba requst command */
mlan_status wlan_cmd_reject_addba_req(pmlan_private pmpriv,
				      HostCmd_DS_COMMAND *cmd, t_u16 cmd_action,
				      t_void *pdata_buf);
/** Handle the command response of rejecting addba request */
mlan_status wlan_ret_reject_addba_req(pmlan_private pmpriv,
				      HostCmd_DS_COMMAND *resp,
				      mlan_ioctl_req *pioctl_buf);
/** Prepare TX BF configuration command */
mlan_status wlan_cmd_tx_bf_cfg(pmlan_private pmpriv, HostCmd_DS_COMMAND *cmd,
			       t_u16 cmd_action, t_void *pdata_buf);
/** Handle the command response TX BF configuration */
mlan_status wlan_ret_tx_bf_cfg(pmlan_private pmpriv, HostCmd_DS_COMMAND *resp,
			       mlan_ioctl_req *pioctl_buf);
#ifdef STA_SUPPORT
t_u8 wlan_11n_bandconfig_allowed(mlan_private *pmpriv, t_u8 bss_band);
/** Append the 802_11N tlv */
int wlan_cmd_append_11n_tlv(mlan_private *pmpriv, BSSDescriptor_t *pbss_desc,
			    t_u8 **ppbuffer);
/** wlan fill HT cap tlv */
void wlan_fill_ht_cap_tlv(mlan_private *priv, MrvlIETypes_HTCap_t *pht_cap,
			  t_u16 band, t_u8 fill);
/** wlan fill HT cap IE */
void wlan_fill_ht_cap_ie(mlan_private *priv, IEEEtypes_HTCap_t *pht_cap,
			 t_u16 bands);
#endif /* STA_SUPPORT */
/** Miscellaneous configuration handler */
mlan_status wlan_11n_cfg_ioctl(pmlan_adapter pmadapter,
			       pmlan_ioctl_req pioctl_req);
/** Delete Tx BA stream table entry */
void wlan_11n_delete_txbastream_tbl_entry(mlan_private *priv,
					  TxBAStreamTbl *ptx_tbl);
/** Delete all Tx BA stream table entries */
void wlan_11n_deleteall_txbastream_tbl(mlan_private *priv);
/** Get Tx BA stream table */
TxBAStreamTbl *wlan_11n_get_txbastream_tbl(mlan_private *priv, int tid,
					   t_u8 *ra, int lock);
/** Create Tx BA stream table */
void wlan_11n_create_txbastream_tbl(mlan_private *priv, t_u8 *ra, int tid,
				    baStatus_e ba_status);
/** Send ADD BA request */
int wlan_send_addba(mlan_private *priv, int tid, t_u8 *peer_mac);
/** Send DEL BA request */
int wlan_send_delba(mlan_private *priv, pmlan_ioctl_req pioctl_req, int tid,
		    t_u8 *peer_mac, int initiator);
/** This function handles the command response of delete a block ack request*/
void wlan_11n_delete_bastream(mlan_private *priv, t_u8 *del_ba);
/** get rx reorder table */
int wlan_get_rxreorder_tbl(mlan_private *priv, rx_reorder_tbl *buf);
/** get tx ba stream table */
int wlan_get_txbastream_tbl(mlan_private *priv, tx_ba_stream_tbl *buf);
/** send delba */
void wlan_11n_delba(mlan_private *priv, int tid);
/** update amdpdu tx win size */
void wlan_update_ampdu_txwinsize(pmlan_adapter pmadapter);
/** Minimum number of AMSDU */
#define MIN_NUM_AMSDU 2
/** AMSDU Aggr control cmd resp */
mlan_status wlan_ret_amsdu_aggr_ctrl(pmlan_private pmpriv,
				     HostCmd_DS_COMMAND *resp,
				     mlan_ioctl_req *pioctl_buf);
void wlan_set_tx_pause_flag(mlan_private *priv, t_u8 flag);
/** reconfigure tx buf size */
mlan_status wlan_cmd_recfg_tx_buf(mlan_private *priv, HostCmd_DS_COMMAND *cmd,
				  int cmd_action, void *pdata_buf);
/** AMSDU aggr control cmd */
mlan_status wlan_cmd_amsdu_aggr_ctrl(mlan_private *priv,
				     HostCmd_DS_COMMAND *cmd, int cmd_action,
				     void *pdata_buf);

t_u8 wlan_validate_chan_offset(mlan_private *pmpriv, t_u16 band, t_u32 chan,
			       t_u8 chan_bw);
/** get channel offset */
t_u8 wlan_get_second_channel_offset(mlan_private *priv, int chan);

void wlan_update_11n_cap(mlan_private *pmpriv);

/** clean up txbastream_tbl */
void wlan_11n_cleanup_txbastream_tbl(mlan_private *priv, t_u8 *ra);
/**
 *  @brief This function checks whether a station has 11N enabled or not
 *
 *  @param priv     A pointer to mlan_private
 *  @param mac      station mac address
 *  @return         MTRUE or MFALSE
 */
static INLINE t_u8 is_station_11n_enabled(mlan_private *priv, t_u8 *mac)
{
	sta_node *sta_ptr = MNULL;
	sta_ptr = wlan_get_station_entry(priv, mac);
	if (sta_ptr)
		return (sta_ptr->is_11n_enabled) ? MTRUE : MFALSE;
	return MFALSE;
}

/**
 *  @brief This function get station max amsdu size
 *
 *  @param priv     A pointer to mlan_private
 *  @param mac      station mac address
 *  @return         max amsdu size statio supported
 */
static INLINE t_u16 get_station_max_amsdu_size(mlan_private *priv, t_u8 *mac)
{
	sta_node *sta_ptr = MNULL;
	sta_ptr = wlan_get_station_entry(priv, mac);
	if (sta_ptr)
		return sta_ptr->max_amsdu;
	return 0;
}

/**
 *  @brief This function checks whether a station allows AMPDU or not
 *
 *  @param priv     A pointer to mlan_private
 *  @param ptr      A pointer to RA list table
 *  @param tid      TID value for ptr
 *  @return         MTRUE or MFALSE
 */
static INLINE t_u8 is_station_ampdu_allowed(mlan_private *priv, raListTbl *ptr,
					    int tid)
{
	sta_node *sta_ptr = MNULL;
	sta_ptr = wlan_get_station_entry(priv, ptr->ra);
	if (sta_ptr) {
		if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) {
			if (priv->sec_info.wapi_enabled &&
			    !sta_ptr->wapi_key_on)
				return MFALSE;
		}
		return (sta_ptr->ampdu_sta[tid] != BA_STREAM_NOT_ALLOWED) ?
			       MTRUE :
			       MFALSE;
	}
	return MFALSE;
}

/**
 *  @brief This function disable station ampdu for specific tid
 *
 *  @param priv     A pointer to mlan_private
 *  @param tid     tid index
 *  @param ra      station mac address
 *  @return        N/A
 */
static INLINE void disable_station_ampdu(mlan_private *priv, t_u8 tid, t_u8 *ra)
{
	sta_node *sta_ptr = MNULL;
	sta_ptr = wlan_get_station_entry(priv, ra);
	if (sta_ptr)
		sta_ptr->ampdu_sta[tid] = BA_STREAM_NOT_ALLOWED;
	return;
}

/**
 *  @brief This function reset station ampdu for specific id to user setting.
 *
 *  @param priv     A pointer to mlan_private
 *  @param tid     tid index
 *  @param ra      station mac address
 *  @return        N/A
 */
static INLINE void reset_station_ampdu(mlan_private *priv, t_u8 tid, t_u8 *ra)
{
	sta_node *sta_ptr = MNULL;
	sta_ptr = wlan_get_station_entry(priv, ra);
	if (sta_ptr)
		sta_ptr->ampdu_sta[tid] = priv->aggr_prio_tbl[tid].ampdu_user;
	return;
}

#define IS_BG_RATE (priv->bitmap_rates[0] || priv->bitmap_rates[1])
/**
 *  @brief This function checks whether AMPDU is allowed or not
 *
 *  @param priv     A pointer to mlan_private
 *  @param ptr      A pointer to RA list table
 *  @param tid      TID value for ptr
 *
 *  @return         MTRUE or MFALSE
 */
static INLINE t_u8 wlan_is_ampdu_allowed(mlan_private *priv, raListTbl *ptr,
					 int tid)
{
	if (ptr->is_tdls_link)
		return is_station_ampdu_allowed(priv, ptr, tid);
	if (priv->adapter->tdls_status != TDLS_NOT_SETUP && !priv->txaggrctrl)
		return MFALSE;

	if ((!priv->is_data_rate_auto) && IS_BG_RATE)
		return MFALSE;
#ifdef UAP_SUPPORT
	if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP)
		return is_station_ampdu_allowed(priv, ptr, tid);
#endif /* UAP_SUPPORT */
	if (priv->sec_info.wapi_enabled && !priv->sec_info.wapi_key_on)
		return MFALSE;
	return (priv->aggr_prio_tbl[tid].ampdu_ap != BA_STREAM_NOT_ALLOWED) ?
		       MTRUE :
		       MFALSE;
}

#define BA_RSSI_HIGH_THRESHOLD -70

static INLINE void wlan_update_station_del_ba_count(mlan_private *priv,
						    raListTbl *ptr)
{
	sta_node *sta_ptr = MNULL;
	t_s8 rssi;
	sta_ptr = wlan_get_station_entry(priv, ptr->ra);
	if (sta_ptr) {
		rssi = sta_ptr->snr - sta_ptr->nf;
		if (rssi > BA_RSSI_HIGH_THRESHOLD)
			ptr->del_ba_count = 0;
	}
	return;
}

static INLINE void wlan_update_del_ba_count(mlan_private *priv, raListTbl *ptr)
{
	t_s8 rssi;
	if (ptr->is_tdls_link)
		return wlan_update_station_del_ba_count(priv, ptr);
	rssi = priv->snr - priv->nf;
	if (rssi > BA_RSSI_HIGH_THRESHOLD)
		ptr->del_ba_count = 0;
}

/**
 *  @brief This function checks whether AMSDU is allowed or not
 *
 *  @param priv     A pointer to mlan_private
 *  @param ptr      A pointer to RA list table
 *  @param tid      TID value for ptr
 *
 *  @return         MTRUE or MFALSE
 */
static INLINE t_u8 wlan_is_amsdu_allowed(mlan_private *priv, raListTbl *ptr,
					 int tid)
{
#ifdef UAP_SUPPORT
	sta_node *sta_ptr = MNULL;
#endif
	if (priv->amsdu_disable)
		return MFALSE;
#ifdef UAP_SUPPORT
	if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) {
		sta_ptr = wlan_get_station_entry(priv, ptr->ra);
		if (sta_ptr) {
			if (priv->sec_info.wapi_enabled &&
			    !sta_ptr->wapi_key_on)
				return MFALSE;
		}
	}
#endif /* UAP_SUPPORT */
	if (ptr->is_tdls_link)
		return (priv->aggr_prio_tbl[tid].amsdu !=
			BA_STREAM_NOT_ALLOWED) ?
			       MTRUE :
			       MFALSE;
#define TXRATE_BITMAP_INDEX_MCS0_7 2
	return ((priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED) &&
		((priv->is_data_rate_auto) ||
		 !(((priv->bitmap_rates[TXRATE_BITMAP_INDEX_MCS0_7]) & 0x03) ||
		   IS_BG_RATE))) ?
		       MTRUE :
		       MFALSE;
}

/**
 *  @brief This function checks whether a BA stream is available or not
 *
 *  @param priv     A pointer to mlan_private
 *
 *  @return         MTRUE or MFALSE
 */
static INLINE t_u8 wlan_is_bastream_avail(mlan_private *priv)
{
	mlan_private *pmpriv = MNULL;
	t_u8 i = 0;
	t_u32 bastream_num = 0;
	t_u32 bastream_max = 0;
	for (i = 0; i < priv->adapter->priv_num; i++) {
		pmpriv = priv->adapter->priv[i];
		if (pmpriv)
			bastream_num += wlan_wmm_list_len(
				(pmlan_list_head)&pmpriv->tx_ba_stream_tbl_ptr);
	}
	bastream_max = ISSUPP_GETTXBASTREAM(priv->adapter->hw_dot_11n_dev_cap);
	if (bastream_max == 0)
		bastream_max = MLAN_MAX_TX_BASTREAM_DEFAULT;
	return (bastream_num < bastream_max) ? MTRUE : MFALSE;
}

/**
 *  @brief This function finds the stream to delete
 *
 *  @param priv     A pointer to mlan_private
 *  @param ptr      A pointer to RA list table
 *  @param ptr_tid  TID value of ptr
 *  @param ptid     A pointer to TID of stream to delete, if return MTRUE
 *  @param ra       RA of stream to delete, if return MTRUE
 *
 *  @return         MTRUE or MFALSE
 */
static INLINE t_u8 wlan_find_stream_to_delete(mlan_private *priv,
					      raListTbl *ptr, int ptr_tid,
					      int *ptid, t_u8 *ra)
{
	int tid;
	t_u8 ret = MFALSE;
	TxBAStreamTbl *ptx_tbl;

	ENTER();

	ptx_tbl = (TxBAStreamTbl *)util_peek_list(priv->adapter->pmoal_handle,
						  &priv->tx_ba_stream_tbl_ptr,
						  MNULL, MNULL);
	if (!ptx_tbl) {
		LEAVE();
		return ret;
	}

	tid = priv->aggr_prio_tbl[ptr_tid].ampdu_user;

	while (ptx_tbl != (TxBAStreamTbl *)&priv->tx_ba_stream_tbl_ptr) {
		if (tid > priv->aggr_prio_tbl[ptx_tbl->tid].ampdu_user) {
			tid = priv->aggr_prio_tbl[ptx_tbl->tid].ampdu_user;
			*ptid = ptx_tbl->tid;
			memcpy_ext(priv->adapter, ra, ptx_tbl->ra,
				   MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH);
			ret = MTRUE;
		}

		ptx_tbl = ptx_tbl->pnext;
	}
	LEAVE();
	return ret;
}

/**
 *  @brief This function checks whether 11n is supported
 *
 *  @param priv     A pointer to mlan_private
 *  @param ra       Address of the receiver STA
 *
 *  @return         MTRUE or MFALSE
 */
static INLINE int wlan_is_11n_enabled(mlan_private *priv, t_u8 *ra)
{
	int ret = MFALSE;
	ENTER();
#ifdef UAP_SUPPORT
	if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) {
		if ((!(ra[0] & 0x01)) && (priv->is_11n_enabled))
			ret = is_station_11n_enabled(priv, ra);
	}
#endif /* UAP_SUPPORT */
	LEAVE();
	return ret;
}
#endif /* !_MLAN_11N_H_ */