mirror of
https://github.com/nxp-imx/mwifiex.git
synced 2024-10-31 21:15:22 +00:00
2a2b474597
New Additions/Removals/Improvements: ------------------------------------ 1. WSW-18868: Improve FW download time after suspend/resume cycle 2. WSFD-484: WIFI 6E support 3. WSW-17954: Enable turbo mode command for mlanutl 4. WSW-19363: Add debug prints to WLAN driver regarding WPA_supplicant 5. WSW-19307: Support robust btc related commands [0x00e0] in mlanutl 6. WSW-16429: Disabling of WEP/TKIP on all V16 devices 7. Use CFG80211_VERSION_CODE to control cfg80211 related code 8. Enabled these new commands for mlanutl: min_ba_threshold, stop_su and ed_mac_ctrl_v3 9. WSFD-386: FW and VHAL Implementation for WOWLAN by adding code for MDNS wakeup Driver Bug Fixes: ----------------- 1. WSW-18708: Setting domain info fails for W8801 Murata M.2 Module (2DS) 2. WSW-18541: Debug logs not seen if drvdbg is part of wifi_mod_para.conf 3. WSW-19136: Command 0x107 timeout is seen radomly during connect-disconnect test 4. WSW-18865: VHT80 TCP-tx throughput only run about 170Mbps, both STA and MMH mode 5. WSW-17923: 4K Video Stoppage Issue, Wi-Fi throughput drops to lower than 20Mbps without recovery 6. WSW-17925: NO_IR flags are not cleared for ch 12,13 and 14 if country is changed to JP in runtime 7. WCSWREL-251: There is "mlan0: Failed to initialize driver interface" during stress load/unload testing 8. WSW-18431: UAP beacon (in 1x1 mode) not reporting correct values for tx_highest_rate and rx_highest_rate in VhtCap IE 9. WSW-18116: TCP Keep alive packets are not seen after starting cloud keep alive 10. WSW-18690: STAUT is advertising support of MCS-0 to 9 only in HE-IE in Probe Request 11. WCSWREL-266: Fix L5.16 build errors and warnings Signed-off-by: Sherry Sun <sherry.sun@nxp.com>
3188 lines
91 KiB
C
3188 lines
91 KiB
C
/** @file mlan_11n.c
|
|
*
|
|
* @brief This file contains functions for 11n handling.
|
|
*
|
|
*
|
|
* Copyright 2008-2021 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:
|
|
11/10/2008: initial version
|
|
********************************************************/
|
|
|
|
#include "mlan.h"
|
|
#include "mlan_join.h"
|
|
#include "mlan_util.h"
|
|
#include "mlan_fw.h"
|
|
#include "mlan_main.h"
|
|
#include "mlan_wmm.h"
|
|
#include "mlan_11n.h"
|
|
#include "mlan_11ac.h"
|
|
|
|
/********************************************************
|
|
Local Variables
|
|
********************************************************/
|
|
|
|
/********************************************************
|
|
Global Variables
|
|
********************************************************/
|
|
|
|
/********************************************************
|
|
Local Functions
|
|
********************************************************/
|
|
|
|
/**
|
|
*
|
|
* @brief set/get max tx buf size
|
|
*
|
|
* @param pmadapter A pointer to mlan_adapter structure
|
|
* @param pioctl_req A pointer to ioctl request buffer
|
|
*
|
|
* @return MLAN_STATUS_SUCCESS --success, otherwise
|
|
* fail
|
|
*/
|
|
static mlan_status wlan_11n_ioctl_max_tx_buf_size(pmlan_adapter pmadapter,
|
|
pmlan_ioctl_req pioctl_req)
|
|
{
|
|
mlan_status ret = MLAN_STATUS_SUCCESS;
|
|
mlan_ds_11n_cfg *cfg = MNULL;
|
|
|
|
ENTER();
|
|
cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf;
|
|
cfg->param.tx_buf_size = (t_u32)pmadapter->max_tx_buf_size;
|
|
pioctl_req->data_read_written = sizeof(t_u32) + MLAN_SUB_COMMAND_SIZE;
|
|
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Set/get htcapinfo configuration
|
|
*
|
|
* @param pmadapter A pointer to mlan_adapter structure
|
|
* @param pioctl_req A pointer to ioctl request buffer
|
|
*
|
|
* @return MLAN_STATUS_SUCCESS --success, otherwise
|
|
* fail
|
|
*/
|
|
static mlan_status wlan_11n_ioctl_htusrcfg(pmlan_adapter pmadapter,
|
|
pmlan_ioctl_req pioctl_req)
|
|
{
|
|
mlan_status ret = MLAN_STATUS_SUCCESS;
|
|
mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index];
|
|
mlan_ds_11n_cfg *cfg = MNULL;
|
|
|
|
ENTER();
|
|
|
|
cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf;
|
|
|
|
if (pioctl_req->action == MLAN_ACT_SET) {
|
|
if (((cfg->param.htcap_cfg.htcap & ~IGN_HW_DEV_CAP) &
|
|
pmpriv->adapter->hw_dot_11n_dev_cap) !=
|
|
(cfg->param.htcap_cfg.htcap & ~IGN_HW_DEV_CAP)) {
|
|
pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER;
|
|
ret = MLAN_STATUS_FAILURE;
|
|
} else {
|
|
if (cfg->param.htcap_cfg.misc_cfg == BAND_SELECT_BG) {
|
|
pmpriv->usr_dot_11n_dev_cap_bg =
|
|
cfg->param.htcap_cfg.htcap;
|
|
PRINTM(MINFO,
|
|
"Set: UsrDot11nCap for 2.4GHz 0x%x\n",
|
|
pmpriv->usr_dot_11n_dev_cap_bg);
|
|
}
|
|
if (cfg->param.htcap_cfg.misc_cfg == BAND_SELECT_A) {
|
|
pmpriv->usr_dot_11n_dev_cap_a =
|
|
cfg->param.htcap_cfg.htcap;
|
|
PRINTM(MINFO,
|
|
"Set: UsrDot11nCap for 5GHz 0x%x\n",
|
|
pmpriv->usr_dot_11n_dev_cap_a);
|
|
}
|
|
if (cfg->param.htcap_cfg.misc_cfg == BAND_SELECT_BOTH) {
|
|
pmpriv->usr_dot_11n_dev_cap_bg =
|
|
cfg->param.htcap_cfg.htcap;
|
|
pmpriv->usr_dot_11n_dev_cap_a =
|
|
cfg->param.htcap_cfg.htcap;
|
|
PRINTM(MINFO,
|
|
"Set: UsrDot11nCap for 2.4GHz and 5GHz 0x%x\n",
|
|
cfg->param.htcap_cfg.htcap);
|
|
}
|
|
}
|
|
} else {
|
|
/* Hardware 11N device capability required */
|
|
if (cfg->param.htcap_cfg.hw_cap_req)
|
|
cfg->param.htcap_cfg.htcap =
|
|
pmadapter->hw_dot_11n_dev_cap;
|
|
else {
|
|
if (cfg->param.htcap_cfg.misc_cfg == BAND_SELECT_BG) {
|
|
cfg->param.htcap_cfg.htcap =
|
|
pmpriv->usr_dot_11n_dev_cap_bg;
|
|
PRINTM(MINFO,
|
|
"Get: UsrDot11nCap for 2.4GHz 0x%x\n",
|
|
cfg->param.htcap_cfg.htcap);
|
|
}
|
|
if (cfg->param.htcap_cfg.misc_cfg == BAND_SELECT_A) {
|
|
cfg->param.htcap_cfg.htcap =
|
|
pmpriv->usr_dot_11n_dev_cap_a;
|
|
PRINTM(MINFO,
|
|
"Get: UsrDot11nCap for 5GHz 0x%x\n",
|
|
cfg->param.htcap_cfg.htcap);
|
|
}
|
|
}
|
|
}
|
|
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Enable/Disable AMSDU AGGR CTRL
|
|
*
|
|
* @param pmadapter A pointer to mlan_adapter structure
|
|
* @param pioctl_req A pointer to ioctl request buffer
|
|
*
|
|
* @return MLAN_STATUS_PENDING --success, otherwise fail
|
|
*/
|
|
static mlan_status wlan_11n_ioctl_amsdu_aggr_ctrl(pmlan_adapter pmadapter,
|
|
pmlan_ioctl_req pioctl_req)
|
|
{
|
|
mlan_status ret = MLAN_STATUS_SUCCESS;
|
|
mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index];
|
|
mlan_ds_11n_cfg *cfg = MNULL;
|
|
t_u16 cmd_action = 0;
|
|
|
|
ENTER();
|
|
|
|
cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf;
|
|
if (pioctl_req->action == MLAN_ACT_SET)
|
|
cmd_action = HostCmd_ACT_GEN_SET;
|
|
else
|
|
cmd_action = HostCmd_ACT_GEN_GET;
|
|
|
|
/* Send request to firmware */
|
|
ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_AMSDU_AGGR_CTRL, cmd_action,
|
|
0, (t_void *)pioctl_req,
|
|
(t_void *)&cfg->param.amsdu_aggr_ctrl);
|
|
if (ret == MLAN_STATUS_SUCCESS)
|
|
ret = MLAN_STATUS_PENDING;
|
|
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Set/get 11n configuration
|
|
*
|
|
* @param pmadapter A pointer to mlan_adapter structure
|
|
* @param pioctl_req A pointer to ioctl request buffer
|
|
*
|
|
* @return MLAN_STATUS_PENDING --success, otherwise fail
|
|
*/
|
|
static mlan_status wlan_11n_ioctl_httxcfg(pmlan_adapter pmadapter,
|
|
pmlan_ioctl_req pioctl_req)
|
|
{
|
|
mlan_status ret = MLAN_STATUS_SUCCESS;
|
|
mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index];
|
|
mlan_ds_11n_cfg *cfg = MNULL;
|
|
t_u16 cmd_action = 0;
|
|
|
|
ENTER();
|
|
|
|
cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf;
|
|
if (pioctl_req->action == MLAN_ACT_SET)
|
|
cmd_action = HostCmd_ACT_GEN_SET;
|
|
else
|
|
cmd_action = HostCmd_ACT_GEN_GET;
|
|
|
|
/* Send request to firmware */
|
|
ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_11N_CFG, cmd_action, 0,
|
|
(t_void *)pioctl_req,
|
|
(t_void *)&cfg->param.tx_cfg);
|
|
if (ret == MLAN_STATUS_SUCCESS)
|
|
ret = MLAN_STATUS_PENDING;
|
|
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Set/get TX beamforming capabilities
|
|
*
|
|
* @param pmadapter A pointer to mlan_adapter structure
|
|
* @param pioctl_req A pointer to ioctl request buffer
|
|
*
|
|
* @return MLAN_STATUS_SUCCESS --success
|
|
*/
|
|
static mlan_status wlan_11n_ioctl_tx_bf_cap(pmlan_adapter pmadapter,
|
|
pmlan_ioctl_req pioctl_req)
|
|
{
|
|
mlan_status ret = MLAN_STATUS_SUCCESS;
|
|
mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index];
|
|
mlan_ds_11n_cfg *cfg = MNULL;
|
|
|
|
ENTER();
|
|
|
|
cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf;
|
|
if (pioctl_req->action == MLAN_ACT_SET)
|
|
pmpriv->tx_bf_cap = cfg->param.tx_bf_cap;
|
|
else
|
|
cfg->param.tx_bf_cap = pmpriv->tx_bf_cap;
|
|
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Set/get TX beamforming configurations
|
|
*
|
|
* @param pmadapter A pointer to mlan_adapter structure
|
|
* @param pioctl_req A pointer to ioctl request buffer
|
|
*
|
|
* @return MLAN_STATUS_PENDING --success, otherwise fail
|
|
*/
|
|
static mlan_status wlan_11n_ioctl_tx_bf_cfg(pmlan_adapter pmadapter,
|
|
pmlan_ioctl_req pioctl_req)
|
|
{
|
|
mlan_status ret = MLAN_STATUS_SUCCESS;
|
|
mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index];
|
|
mlan_ds_11n_cfg *cfg = MNULL;
|
|
t_u16 cmd_action = 0;
|
|
|
|
ENTER();
|
|
|
|
cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf;
|
|
if (pioctl_req->action == MLAN_ACT_SET)
|
|
cmd_action = HostCmd_ACT_GEN_SET;
|
|
else
|
|
cmd_action = HostCmd_ACT_GEN_GET;
|
|
|
|
/* Send request to firmware */
|
|
ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_TX_BF_CFG, cmd_action, 0,
|
|
(t_void *)pioctl_req,
|
|
(t_void *)&cfg->param.tx_bf);
|
|
if (ret == MLAN_STATUS_SUCCESS)
|
|
ret = MLAN_STATUS_PENDING;
|
|
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Set/get HT stream configurations
|
|
*
|
|
* @param pmadapter A pointer to mlan_adapter structure
|
|
* @param pioctl_req A pointer to ioctl request buffer
|
|
*
|
|
* @return MLAN_STATUS_SUCCESS --success, otherwise fail
|
|
*/
|
|
static mlan_status wlan_11n_ioctl_stream_cfg(pmlan_adapter pmadapter,
|
|
pmlan_ioctl_req pioctl_req)
|
|
{
|
|
mlan_status ret = MLAN_STATUS_SUCCESS;
|
|
mlan_ds_11n_cfg *cfg = MNULL;
|
|
mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index];
|
|
|
|
ENTER();
|
|
|
|
cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf;
|
|
if (pioctl_req->action == MLAN_ACT_GET) {
|
|
cfg->param.stream_cfg = pmpriv->usr_dev_mcs_support;
|
|
} else if (pioctl_req->action == MLAN_ACT_SET) {
|
|
switch (cfg->param.stream_cfg) {
|
|
case HT_STREAM_MODE_2X2:
|
|
if (pmadapter->hw_dev_mcs_support ==
|
|
HT_STREAM_MODE_1X1) {
|
|
PRINTM(MERROR,
|
|
"HW does not support this mode\n");
|
|
ret = MLAN_STATUS_FAILURE;
|
|
} else
|
|
pmpriv->usr_dev_mcs_support =
|
|
cfg->param.stream_cfg;
|
|
break;
|
|
case HT_STREAM_MODE_1X1:
|
|
pmpriv->usr_dev_mcs_support = cfg->param.stream_cfg;
|
|
break;
|
|
default:
|
|
PRINTM(MERROR, "Invalid stream mode\n");
|
|
pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER;
|
|
ret = MLAN_STATUS_FAILURE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Set/get control to coex RX window size configuration
|
|
*
|
|
* @param pmadapter A pointer to mlan_adapter structure
|
|
* @param pioctl_req A pointer to ioctl request buffer
|
|
*
|
|
* @return MLAN_STATUS_SUCCESS --success, otherwise fail
|
|
*/
|
|
static mlan_status wlan_11n_ioctl_coex_rx_winsize(pmlan_adapter pmadapter,
|
|
pmlan_ioctl_req pioctl_req)
|
|
{
|
|
mlan_status ret = MLAN_STATUS_SUCCESS;
|
|
mlan_ds_11n_cfg *cfg = MNULL;
|
|
|
|
ENTER();
|
|
|
|
cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf;
|
|
if (pioctl_req->action == MLAN_ACT_GET)
|
|
cfg->param.coex_rx_winsize = pmadapter->coex_rx_winsize;
|
|
else if (pioctl_req->action == MLAN_ACT_SET)
|
|
pmadapter->coex_rx_winsize = (t_u8)cfg->param.coex_rx_winsize;
|
|
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief This function will send delba request to
|
|
* the peer in the TxBAStreamTbl
|
|
*
|
|
* @param priv A pointer to mlan_private
|
|
* @param ra MAC Address to send DELBA
|
|
*
|
|
* @return N/A
|
|
*/
|
|
void wlan_11n_send_delba_to_peer(mlan_private *priv, t_u8 *ra)
|
|
{
|
|
TxBAStreamTbl *ptx_tbl;
|
|
|
|
ENTER();
|
|
wlan_request_ralist_lock(priv);
|
|
ptx_tbl = (TxBAStreamTbl *)util_peek_list(priv->adapter->pmoal_handle,
|
|
&priv->tx_ba_stream_tbl_ptr,
|
|
MNULL, MNULL);
|
|
if (!ptx_tbl) {
|
|
wlan_release_ralist_lock(priv);
|
|
LEAVE();
|
|
return;
|
|
}
|
|
|
|
while (ptx_tbl != (TxBAStreamTbl *)&priv->tx_ba_stream_tbl_ptr) {
|
|
if (!memcmp(priv->adapter, ptx_tbl->ra, ra,
|
|
MLAN_MAC_ADDR_LENGTH)) {
|
|
PRINTM(MIOCTL, "Tx:Send delba to tid=%d, " MACSTR "\n",
|
|
ptx_tbl->tid, MAC2STR(ptx_tbl->ra));
|
|
wlan_send_delba(priv, MNULL, ptx_tbl->tid, ptx_tbl->ra,
|
|
1);
|
|
}
|
|
ptx_tbl = ptx_tbl->pnext;
|
|
}
|
|
wlan_release_ralist_lock(priv);
|
|
/* Signal MOAL to trigger mlan_main_process */
|
|
wlan_recv_event(priv, MLAN_EVENT_ID_DRV_DEFER_HANDLING, MNULL);
|
|
LEAVE();
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* @brief Set/Get control to TX AMPDU configuration on infra link
|
|
*
|
|
* @param pmadapter A pointer to mlan_adapter structure
|
|
* @param pioctl_req A pointer to ioctl request buffer
|
|
*
|
|
* @return MLAN_STATUS_SUCCESS --success, otherwise fail
|
|
*/
|
|
static mlan_status wlan_11n_ioctl_txaggrctrl(pmlan_adapter pmadapter,
|
|
pmlan_ioctl_req pioctl_req)
|
|
{
|
|
mlan_status ret = MLAN_STATUS_SUCCESS;
|
|
mlan_ds_11n_cfg *cfg = MNULL;
|
|
mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index];
|
|
|
|
ENTER();
|
|
|
|
cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf;
|
|
if (pioctl_req->action == MLAN_ACT_GET)
|
|
cfg->param.txaggrctrl = pmpriv->txaggrctrl;
|
|
else if (pioctl_req->action == MLAN_ACT_SET)
|
|
pmpriv->txaggrctrl = (t_u8)cfg->param.txaggrctrl;
|
|
|
|
if (pmpriv->media_connected == MTRUE) {
|
|
if (pioctl_req->action == MLAN_ACT_SET && !pmpriv->txaggrctrl &&
|
|
pmpriv->adapter->tdls_status != TDLS_NOT_SETUP)
|
|
wlan_11n_send_delba_to_peer(
|
|
pmpriv, pmpriv->curr_bss_params.bss_descriptor
|
|
.mac_address);
|
|
}
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief This function will resend addba request to all
|
|
* the peer in the TxBAStreamTbl
|
|
*
|
|
* @param priv A pointer to mlan_private
|
|
*
|
|
* @return N/A
|
|
*/
|
|
static void wlan_11n_update_addba_request(mlan_private *priv)
|
|
{
|
|
TxBAStreamTbl *ptx_tbl;
|
|
|
|
ENTER();
|
|
|
|
wlan_request_ralist_lock(priv);
|
|
ptx_tbl = (TxBAStreamTbl *)util_peek_list(priv->adapter->pmoal_handle,
|
|
&priv->tx_ba_stream_tbl_ptr,
|
|
MNULL, MNULL);
|
|
if (!ptx_tbl) {
|
|
wlan_release_ralist_lock(priv);
|
|
LEAVE();
|
|
return;
|
|
}
|
|
|
|
while (ptx_tbl != (TxBAStreamTbl *)&priv->tx_ba_stream_tbl_ptr) {
|
|
wlan_send_addba(priv, ptx_tbl->tid, ptx_tbl->ra);
|
|
ptx_tbl = ptx_tbl->pnext;
|
|
}
|
|
wlan_release_ralist_lock(priv);
|
|
/* Signal MOAL to trigger mlan_main_process */
|
|
wlan_recv_event(priv, MLAN_EVENT_ID_DRV_DEFER_HANDLING, MNULL);
|
|
LEAVE();
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* @brief Set/get addba parameter
|
|
*
|
|
* @param pmadapter A pointer to mlan_adapter structure
|
|
* @param pioctl_req A pointer to ioctl request buffer
|
|
*
|
|
* @return MLAN_STATUS_SUCCESS --success
|
|
*/
|
|
static mlan_status wlan_11n_ioctl_addba_param(pmlan_adapter pmadapter,
|
|
pmlan_ioctl_req pioctl_req)
|
|
{
|
|
mlan_status ret = MLAN_STATUS_SUCCESS;
|
|
mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index];
|
|
mlan_ds_11n_cfg *cfg = MNULL;
|
|
t_u32 timeout;
|
|
|
|
ENTER();
|
|
|
|
cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf;
|
|
if (pioctl_req->action == MLAN_ACT_GET) {
|
|
cfg->param.addba_param.timeout = pmpriv->add_ba_param.timeout;
|
|
cfg->param.addba_param.txwinsize =
|
|
pmpriv->add_ba_param.tx_win_size;
|
|
cfg->param.addba_param.rxwinsize =
|
|
pmpriv->add_ba_param.rx_win_size;
|
|
cfg->param.addba_param.txamsdu = pmpriv->add_ba_param.tx_amsdu;
|
|
cfg->param.addba_param.rxamsdu = pmpriv->add_ba_param.rx_amsdu;
|
|
} else {
|
|
timeout = pmpriv->add_ba_param.timeout;
|
|
pmpriv->add_ba_param.timeout = cfg->param.addba_param.timeout;
|
|
pmpriv->add_ba_param.tx_win_size =
|
|
cfg->param.addba_param.txwinsize;
|
|
|
|
pmpriv->add_ba_param.rx_win_size =
|
|
cfg->param.addba_param.rxwinsize;
|
|
pmpriv->user_rxwinsize = pmpriv->add_ba_param.rx_win_size;
|
|
pmpriv->add_ba_param.tx_amsdu = cfg->param.addba_param.txamsdu;
|
|
pmpriv->add_ba_param.rx_amsdu = cfg->param.addba_param.rxamsdu;
|
|
if (timeout != pmpriv->add_ba_param.timeout)
|
|
wlan_11n_update_addba_request(pmpriv);
|
|
}
|
|
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief This function send delba to specific tid
|
|
*
|
|
* @param priv A pointer to mlan_priv
|
|
* @param tid tid
|
|
* @return N/A
|
|
*/
|
|
void wlan_11n_delba(mlan_private *priv, int tid)
|
|
{
|
|
RxReorderTbl *rx_reor_tbl_ptr;
|
|
|
|
ENTER();
|
|
|
|
rx_reor_tbl_ptr = (RxReorderTbl *)util_peek_list(
|
|
priv->adapter->pmoal_handle, &priv->rx_reorder_tbl_ptr,
|
|
priv->adapter->callbacks.moal_spin_lock,
|
|
priv->adapter->callbacks.moal_spin_unlock);
|
|
if (!rx_reor_tbl_ptr) {
|
|
LEAVE();
|
|
return;
|
|
}
|
|
|
|
while (rx_reor_tbl_ptr != (RxReorderTbl *)&priv->rx_reorder_tbl_ptr) {
|
|
if (rx_reor_tbl_ptr->tid == tid) {
|
|
PRINTM(MIOCTL, "Send delba to tid=%d, " MACSTR "\n",
|
|
tid, MAC2STR(rx_reor_tbl_ptr->ta));
|
|
wlan_send_delba(priv, MNULL, tid, rx_reor_tbl_ptr->ta,
|
|
0);
|
|
LEAVE();
|
|
return;
|
|
}
|
|
rx_reor_tbl_ptr = rx_reor_tbl_ptr->pnext;
|
|
}
|
|
|
|
LEAVE();
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* @brief Set/get addba reject set
|
|
*
|
|
* @param pmadapter A pointer to mlan_adapter structure
|
|
* @param pioctl_req A pointer to ioctl request buffer
|
|
*
|
|
* @return MLAN_STATUS_SUCCESS --success, otherwise fail
|
|
*/
|
|
static mlan_status wlan_11n_ioctl_addba_reject(pmlan_adapter pmadapter,
|
|
pmlan_ioctl_req pioctl_req)
|
|
{
|
|
int i = 0;
|
|
mlan_status ret = MLAN_STATUS_SUCCESS;
|
|
mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index];
|
|
mlan_ds_11n_cfg *cfg = MNULL;
|
|
|
|
ENTER();
|
|
|
|
cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf;
|
|
|
|
if (pioctl_req->action == MLAN_ACT_GET) {
|
|
PRINTM(MINFO, "Get Addba reject\n");
|
|
memcpy_ext(pmadapter, cfg->param.addba_reject,
|
|
pmpriv->addba_reject, MAX_NUM_TID, MAX_NUM_TID);
|
|
} else {
|
|
for (i = 0; i < MAX_NUM_TID; i++) {
|
|
/* For AMPDU */
|
|
if (cfg->param.addba_reject[i] >
|
|
ADDBA_RSP_STATUS_REJECT) {
|
|
pioctl_req->status_code =
|
|
MLAN_ERROR_INVALID_PARAMETER;
|
|
ret = MLAN_STATUS_FAILURE;
|
|
break;
|
|
}
|
|
|
|
pmpriv->addba_reject[i] = cfg->param.addba_reject[i];
|
|
}
|
|
if (pmpriv->media_connected == MTRUE) {
|
|
for (i = 0; i < MAX_NUM_TID; i++) {
|
|
if (cfg->param.addba_reject[i] ==
|
|
ADDBA_RSP_STATUS_REJECT) {
|
|
PRINTM(MIOCTL,
|
|
"Receive addba reject: tid=%d\n",
|
|
i);
|
|
wlan_11n_delba(pmpriv, i);
|
|
}
|
|
}
|
|
wlan_recv_event(pmpriv,
|
|
MLAN_EVENT_ID_DRV_DEFER_HANDLING,
|
|
MNULL);
|
|
}
|
|
}
|
|
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Set/get ibss ampdu param
|
|
*
|
|
* @param pmadapter A pointer to mlan_adapter structure
|
|
* @param pioctl_req A pointer to ioctl request buffer
|
|
*
|
|
* @return MLAN_STATUS_SUCCESS --success, otherwise fail
|
|
*/
|
|
static mlan_status wlan_11n_ioctl_ibss_ampdu_param(pmlan_adapter pmadapter,
|
|
pmlan_ioctl_req pioctl_req)
|
|
{
|
|
int i = 0;
|
|
mlan_status ret = MLAN_STATUS_SUCCESS;
|
|
mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index];
|
|
mlan_ds_11n_cfg *cfg = MNULL;
|
|
|
|
ENTER();
|
|
|
|
cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf;
|
|
|
|
if (pioctl_req->action == MLAN_ACT_GET) {
|
|
PRINTM(MINFO, "Get IBSS AMPDU param\n");
|
|
for (i = 0; i < MAX_NUM_TID; i++) {
|
|
cfg->param.ibss_ampdu.ampdu[i] = pmpriv->ibss_ampdu[i];
|
|
cfg->param.ibss_ampdu.addba_reject[i] =
|
|
pmpriv->ibss_addba_reject[i];
|
|
}
|
|
} else {
|
|
for (i = 0; i < MAX_NUM_TID; i++) {
|
|
/* For AMPDU RX*/
|
|
if (cfg->param.ibss_ampdu.addba_reject[i] >
|
|
ADDBA_RSP_STATUS_REJECT) {
|
|
pioctl_req->status_code =
|
|
MLAN_ERROR_INVALID_PARAMETER;
|
|
ret = MLAN_STATUS_FAILURE;
|
|
break;
|
|
}
|
|
pmpriv->ibss_addba_reject[i] =
|
|
cfg->param.ibss_ampdu.addba_reject[i];
|
|
/* For AMPDU TX*/
|
|
if ((cfg->param.ibss_ampdu.ampdu[i] > HIGH_PRIO_TID) &&
|
|
(cfg->param.ibss_ampdu.ampdu[i] !=
|
|
BA_STREAM_NOT_ALLOWED)) {
|
|
pioctl_req->status_code =
|
|
MLAN_ERROR_INVALID_PARAMETER;
|
|
ret = MLAN_STATUS_FAILURE;
|
|
break;
|
|
}
|
|
pmpriv->ibss_ampdu[i] = cfg->param.ibss_ampdu.ampdu[i];
|
|
}
|
|
PRINTM(MMSG, "IBSS addba reject: %d %d %d %d %d %d %d %d\n",
|
|
pmpriv->ibss_addba_reject[0],
|
|
pmpriv->ibss_addba_reject[1],
|
|
pmpriv->ibss_addba_reject[2],
|
|
pmpriv->ibss_addba_reject[3],
|
|
pmpriv->ibss_addba_reject[4],
|
|
pmpriv->ibss_addba_reject[5],
|
|
pmpriv->ibss_addba_reject[6],
|
|
pmpriv->ibss_addba_reject[7]);
|
|
PRINTM(MMSG, "IBSS ampdu %d %d %d %d %d %d %d %d\n",
|
|
pmpriv->ibss_ampdu[0], pmpriv->ibss_ampdu[1],
|
|
pmpriv->ibss_ampdu[2], pmpriv->ibss_ampdu[3],
|
|
pmpriv->ibss_ampdu[4], pmpriv->ibss_ampdu[5],
|
|
pmpriv->ibss_ampdu[6], pmpriv->ibss_ampdu[7]);
|
|
}
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Set/Get Minimum BA Threshold
|
|
*
|
|
* @param pmadapter A pointer to mlan_adapter structure
|
|
* @param pioctl_req A pointer to ioctl request buffer
|
|
*
|
|
* @return MLAN_STATUS_SUCCESS --success
|
|
*/
|
|
static mlan_status
|
|
wlan_11n_ioctl_min_ba_threshold_cfg(pmlan_adapter pmadapter,
|
|
pmlan_ioctl_req pioctl_req)
|
|
{
|
|
mlan_status ret = MLAN_STATUS_SUCCESS;
|
|
mlan_ds_11n_cfg *cfg = MNULL;
|
|
ENTER();
|
|
cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf;
|
|
if (pioctl_req->action == MLAN_ACT_GET)
|
|
cfg->param.min_ba_threshold = pmadapter->min_ba_threshold;
|
|
else
|
|
pmadapter->min_ba_threshold = cfg->param.min_ba_threshold;
|
|
pioctl_req->data_read_written = sizeof(t_u8) + MLAN_SUB_COMMAND_SIZE;
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief This function will send DELBA to entries in the priv's
|
|
* Tx BA stream table
|
|
*
|
|
* @param priv A pointer to mlan_private
|
|
* @param pioctl_req A pointer to ioctl request buffer
|
|
* @param tid TID
|
|
* @param peer_address A pointer to peer address
|
|
* @param last_tx_ba_to_delete A pointer to the last entry in TxBAStreamTbl
|
|
*
|
|
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_PENDING
|
|
*/
|
|
static mlan_status wlan_send_delba_to_entry_in_txbastream_tbl(
|
|
pmlan_private priv, pmlan_ioctl_req pioctl_req, t_u8 tid,
|
|
t_u8 *peer_address, TxBAStreamTbl *last_tx_ba_to_delete)
|
|
{
|
|
pmlan_adapter pmadapter = priv->adapter;
|
|
TxBAStreamTbl *tx_ba_stream_tbl_ptr;
|
|
t_u8 zero_mac[MLAN_MAC_ADDR_LENGTH] = {0};
|
|
mlan_status ret = MLAN_STATUS_SUCCESS;
|
|
|
|
ENTER();
|
|
|
|
wlan_request_ralist_lock(priv);
|
|
tx_ba_stream_tbl_ptr =
|
|
(TxBAStreamTbl *)util_peek_list(pmadapter->pmoal_handle,
|
|
&priv->tx_ba_stream_tbl_ptr,
|
|
MNULL, MNULL);
|
|
if (!tx_ba_stream_tbl_ptr) {
|
|
wlan_release_ralist_lock(priv);
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
while (tx_ba_stream_tbl_ptr !=
|
|
(TxBAStreamTbl *)&priv->tx_ba_stream_tbl_ptr) {
|
|
if (tx_ba_stream_tbl_ptr->ba_status ==
|
|
BA_STREAM_SETUP_COMPLETE) {
|
|
if (((tid == DELBA_ALL_TIDS) ||
|
|
(tid == tx_ba_stream_tbl_ptr->tid)) &&
|
|
(!memcmp(pmadapter, peer_address, zero_mac,
|
|
MLAN_MAC_ADDR_LENGTH) ||
|
|
!memcmp(pmadapter, peer_address,
|
|
tx_ba_stream_tbl_ptr->ra,
|
|
MLAN_MAC_ADDR_LENGTH))) {
|
|
if (last_tx_ba_to_delete &&
|
|
(tx_ba_stream_tbl_ptr ==
|
|
last_tx_ba_to_delete))
|
|
ret = wlan_send_delba(
|
|
priv, pioctl_req,
|
|
tx_ba_stream_tbl_ptr->tid,
|
|
tx_ba_stream_tbl_ptr->ra, 1);
|
|
else
|
|
ret = wlan_send_delba(
|
|
priv, MNULL,
|
|
tx_ba_stream_tbl_ptr->tid,
|
|
tx_ba_stream_tbl_ptr->ra, 1);
|
|
}
|
|
}
|
|
tx_ba_stream_tbl_ptr = tx_ba_stream_tbl_ptr->pnext;
|
|
}
|
|
wlan_release_ralist_lock(priv);
|
|
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief This function will send DELBA to entries in the priv's
|
|
* rx reordering table
|
|
*
|
|
* @param priv A pointer to mlan_private
|
|
* @param pioctl_req A pointer to ioctl request buffer
|
|
* @param tid TID
|
|
* @param peer_address A pointer to peer address
|
|
* @param last_rx_ba_to_delete A pointer to the last entry in RxReorderTbl
|
|
*
|
|
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_PENDING
|
|
*/
|
|
static mlan_status wlan_send_delba_to_entry_in_reorder_tbl(
|
|
pmlan_private priv, pmlan_ioctl_req pioctl_req, t_u8 tid,
|
|
t_u8 *peer_address, RxReorderTbl *last_rx_ba_to_delete)
|
|
{
|
|
pmlan_adapter pmadapter = priv->adapter;
|
|
RxReorderTbl *rx_reor_tbl_ptr;
|
|
t_u8 zero_mac[MLAN_MAC_ADDR_LENGTH] = {0};
|
|
mlan_status ret = MLAN_STATUS_SUCCESS;
|
|
|
|
ENTER();
|
|
|
|
rx_reor_tbl_ptr = (RxReorderTbl *)util_peek_list(
|
|
pmadapter->pmoal_handle, &priv->rx_reorder_tbl_ptr,
|
|
pmadapter->callbacks.moal_spin_lock,
|
|
pmadapter->callbacks.moal_spin_unlock);
|
|
if (!rx_reor_tbl_ptr) {
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
while (rx_reor_tbl_ptr != (RxReorderTbl *)&priv->rx_reorder_tbl_ptr) {
|
|
if (rx_reor_tbl_ptr->ba_status == BA_STREAM_SETUP_COMPLETE) {
|
|
if (((tid == DELBA_ALL_TIDS) ||
|
|
(tid == rx_reor_tbl_ptr->tid)) &&
|
|
(!memcmp(pmadapter, peer_address, zero_mac,
|
|
MLAN_MAC_ADDR_LENGTH) ||
|
|
!memcmp(pmadapter, peer_address,
|
|
rx_reor_tbl_ptr->ta,
|
|
MLAN_MAC_ADDR_LENGTH))) {
|
|
if (last_rx_ba_to_delete &&
|
|
(rx_reor_tbl_ptr == last_rx_ba_to_delete))
|
|
ret = wlan_send_delba(
|
|
priv, pioctl_req,
|
|
rx_reor_tbl_ptr->tid,
|
|
rx_reor_tbl_ptr->ta, 0);
|
|
else
|
|
ret = wlan_send_delba(
|
|
priv, MNULL,
|
|
rx_reor_tbl_ptr->tid,
|
|
rx_reor_tbl_ptr->ta, 0);
|
|
}
|
|
}
|
|
rx_reor_tbl_ptr = rx_reor_tbl_ptr->pnext;
|
|
}
|
|
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief IOCTL to delete BA
|
|
*
|
|
* @param pmadapter A pointer to mlan_adapter structure
|
|
* @param pioctl_req A pointer to ioctl request buffer
|
|
*
|
|
* @return MLAN_STATUS_SUCCESS --success, otherwise fail
|
|
*/
|
|
static mlan_status wlan_11n_ioctl_delba(pmlan_adapter pmadapter,
|
|
pmlan_ioctl_req pioctl_req)
|
|
{
|
|
mlan_status ret = MLAN_STATUS_SUCCESS;
|
|
mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index];
|
|
mlan_ds_11n_cfg *cfg = MNULL;
|
|
TxBAStreamTbl *tx_ba_stream_tbl_ptr, *last_tx_ba_to_delete = MNULL;
|
|
RxReorderTbl *rx_reor_tbl_ptr, *last_rx_ba_to_delete = MNULL;
|
|
t_u8 zero_mac[MLAN_MAC_ADDR_LENGTH] = {0};
|
|
t_u8 tid, *peer_address;
|
|
|
|
ENTER();
|
|
|
|
cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf;
|
|
tid = cfg->param.del_ba.tid;
|
|
peer_address = cfg->param.del_ba.peer_mac_addr;
|
|
|
|
PRINTM(MINFO, "DelBA: direction %d, TID %d, peer address " MACSTR "\n",
|
|
cfg->param.del_ba.direction, tid, MAC2STR(peer_address));
|
|
|
|
if (cfg->param.del_ba.direction & DELBA_RX) {
|
|
rx_reor_tbl_ptr = (RxReorderTbl *)util_peek_list(
|
|
pmadapter->pmoal_handle, &pmpriv->rx_reorder_tbl_ptr,
|
|
pmadapter->callbacks.moal_spin_lock,
|
|
pmadapter->callbacks.moal_spin_unlock);
|
|
|
|
if (rx_reor_tbl_ptr) {
|
|
while (rx_reor_tbl_ptr !=
|
|
(RxReorderTbl *)&pmpriv->rx_reorder_tbl_ptr) {
|
|
if (rx_reor_tbl_ptr->ba_status ==
|
|
BA_STREAM_SETUP_COMPLETE) {
|
|
if (((tid == DELBA_ALL_TIDS) ||
|
|
(tid == rx_reor_tbl_ptr->tid)) &&
|
|
(!memcmp(pmadapter, peer_address,
|
|
zero_mac,
|
|
MLAN_MAC_ADDR_LENGTH) ||
|
|
!memcmp(pmadapter, peer_address,
|
|
rx_reor_tbl_ptr->ta,
|
|
MLAN_MAC_ADDR_LENGTH))) {
|
|
/* Found RX BA to delete */
|
|
last_rx_ba_to_delete =
|
|
rx_reor_tbl_ptr;
|
|
}
|
|
}
|
|
rx_reor_tbl_ptr = rx_reor_tbl_ptr->pnext;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((last_rx_ba_to_delete == MNULL) &&
|
|
(cfg->param.del_ba.direction & DELBA_TX)) {
|
|
wlan_request_ralist_lock(pmpriv);
|
|
tx_ba_stream_tbl_ptr = (TxBAStreamTbl *)util_peek_list(
|
|
pmadapter->pmoal_handle, &pmpriv->tx_ba_stream_tbl_ptr,
|
|
MNULL, MNULL);
|
|
|
|
if (tx_ba_stream_tbl_ptr) {
|
|
while (tx_ba_stream_tbl_ptr !=
|
|
(TxBAStreamTbl *)&pmpriv->tx_ba_stream_tbl_ptr) {
|
|
if (tx_ba_stream_tbl_ptr->ba_status ==
|
|
BA_STREAM_SETUP_COMPLETE) {
|
|
if (((tid == DELBA_ALL_TIDS) ||
|
|
(tid ==
|
|
tx_ba_stream_tbl_ptr->tid)) &&
|
|
(!memcmp(pmadapter, peer_address,
|
|
zero_mac,
|
|
MLAN_MAC_ADDR_LENGTH) ||
|
|
!memcmp(pmadapter, peer_address,
|
|
tx_ba_stream_tbl_ptr->ra,
|
|
MLAN_MAC_ADDR_LENGTH))) {
|
|
/* Found TX BA to delete */
|
|
last_tx_ba_to_delete =
|
|
tx_ba_stream_tbl_ptr;
|
|
}
|
|
}
|
|
tx_ba_stream_tbl_ptr =
|
|
tx_ba_stream_tbl_ptr->pnext;
|
|
}
|
|
}
|
|
wlan_release_ralist_lock(pmpriv);
|
|
}
|
|
|
|
if (cfg->param.del_ba.direction & DELBA_TX) {
|
|
if (last_rx_ba_to_delete)
|
|
ret = wlan_send_delba_to_entry_in_txbastream_tbl(
|
|
pmpriv, MNULL, tid, peer_address, MNULL);
|
|
else
|
|
ret = wlan_send_delba_to_entry_in_txbastream_tbl(
|
|
pmpriv, pioctl_req, tid, peer_address,
|
|
last_tx_ba_to_delete);
|
|
}
|
|
if (last_rx_ba_to_delete) {
|
|
ret = wlan_send_delba_to_entry_in_reorder_tbl(
|
|
pmpriv, pioctl_req, tid, peer_address,
|
|
last_rx_ba_to_delete);
|
|
}
|
|
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief IOCTL to reject addba req
|
|
*
|
|
* @param pmadapter A pointer to mlan_adapter structure
|
|
* @param pioctl_req A pointer to ioctl request buffer
|
|
*
|
|
* @return MLAN_STATUS_SUCCESS --success, otherwise fail
|
|
*/
|
|
static mlan_status wlan_11n_ioctl_rejectaddbareq(pmlan_adapter pmadapter,
|
|
pmlan_ioctl_req pioctl_req)
|
|
{
|
|
mlan_status ret = MLAN_STATUS_SUCCESS;
|
|
mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index];
|
|
mlan_ds_11n_cfg *cfg = MNULL;
|
|
t_u16 cmd_action = 0;
|
|
|
|
ENTER();
|
|
|
|
cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf;
|
|
if (pioctl_req->action == MLAN_ACT_SET)
|
|
cmd_action = HostCmd_ACT_GEN_SET;
|
|
else
|
|
cmd_action = HostCmd_ACT_GEN_GET;
|
|
|
|
/* Send command to firmware */
|
|
ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_REJECT_ADDBA_REQ, cmd_action,
|
|
0, (t_void *)pioctl_req,
|
|
&cfg->param.reject_addba_req);
|
|
|
|
if (ret == MLAN_STATUS_SUCCESS)
|
|
ret = MLAN_STATUS_PENDING;
|
|
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief This function will send DELBA to entries in the priv's
|
|
* Tx BA stream table
|
|
*
|
|
* @param priv A pointer to mlan_private
|
|
* @param tid TID
|
|
*
|
|
* @return N/A
|
|
*/
|
|
static void wlan_send_delba_txbastream_tbl(pmlan_private priv, t_u8 tid)
|
|
{
|
|
pmlan_adapter pmadapter = priv->adapter;
|
|
TxBAStreamTbl *tx_ba_stream_tbl_ptr;
|
|
|
|
ENTER();
|
|
|
|
wlan_request_ralist_lock(priv);
|
|
tx_ba_stream_tbl_ptr =
|
|
(TxBAStreamTbl *)util_peek_list(pmadapter->pmoal_handle,
|
|
&priv->tx_ba_stream_tbl_ptr,
|
|
MNULL, MNULL);
|
|
if (!tx_ba_stream_tbl_ptr) {
|
|
wlan_release_ralist_lock(priv);
|
|
LEAVE();
|
|
return;
|
|
}
|
|
|
|
while (tx_ba_stream_tbl_ptr !=
|
|
(TxBAStreamTbl *)&priv->tx_ba_stream_tbl_ptr) {
|
|
if (tx_ba_stream_tbl_ptr->ba_status ==
|
|
BA_STREAM_SETUP_COMPLETE) {
|
|
if (tid == tx_ba_stream_tbl_ptr->tid) {
|
|
PRINTM(MIOCTL,
|
|
"Tx:Send delba to tid=%d, " MACSTR "\n",
|
|
tid, MAC2STR(tx_ba_stream_tbl_ptr->ra));
|
|
wlan_release_ralist_lock(priv);
|
|
wlan_send_delba(priv, MNULL,
|
|
tx_ba_stream_tbl_ptr->tid,
|
|
tx_ba_stream_tbl_ptr->ra, 1);
|
|
LEAVE();
|
|
return;
|
|
}
|
|
}
|
|
tx_ba_stream_tbl_ptr = tx_ba_stream_tbl_ptr->pnext;
|
|
}
|
|
wlan_release_ralist_lock(priv);
|
|
|
|
LEAVE();
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* @brief update station list for the new aggr_prio_tbl setting
|
|
*
|
|
* @param priv A pointer to mlan_private structure
|
|
*
|
|
*
|
|
* @return N/A
|
|
*/
|
|
static void wlan_update_all_stations_ampdu(mlan_private *priv)
|
|
{
|
|
sta_node *sta_ptr;
|
|
mlan_adapter *pmadapter = priv->adapter;
|
|
int i = 0;
|
|
|
|
ENTER();
|
|
pmadapter->callbacks.moal_spin_lock(pmadapter->pmoal_handle,
|
|
priv->wmm.ra_list_spinlock);
|
|
sta_ptr = (sta_node *)util_peek_list(pmadapter->pmoal_handle,
|
|
&priv->sta_list, MNULL, MNULL);
|
|
if (!sta_ptr) {
|
|
pmadapter->callbacks.moal_spin_unlock(
|
|
pmadapter->pmoal_handle, priv->wmm.ra_list_spinlock);
|
|
LEAVE();
|
|
return;
|
|
}
|
|
while (sta_ptr != (sta_node *)&priv->sta_list) {
|
|
for (i = 0; i < MAX_NUM_TID; i++) {
|
|
if (sta_ptr->is_11n_enabled)
|
|
sta_ptr->ampdu_sta[i] =
|
|
priv->aggr_prio_tbl[i].ampdu_user;
|
|
}
|
|
sta_ptr = sta_ptr->pnext;
|
|
}
|
|
pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle,
|
|
priv->wmm.ra_list_spinlock);
|
|
LEAVE();
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* @brief Set/get aggr_prio_tbl
|
|
*
|
|
* @param pmadapter A pointer to mlan_adapter structure
|
|
* @param pioctl_req A pointer to ioctl request buffer
|
|
*
|
|
* @return MLAN_STATUS_SUCCESS --success, otherwise fail
|
|
*/
|
|
static mlan_status wlan_11n_ioctl_aggr_prio_tbl(pmlan_adapter pmadapter,
|
|
pmlan_ioctl_req pioctl_req)
|
|
{
|
|
int i = 0;
|
|
mlan_status ret = MLAN_STATUS_SUCCESS;
|
|
mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index];
|
|
mlan_ds_11n_cfg *cfg = MNULL;
|
|
|
|
ENTER();
|
|
|
|
cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf;
|
|
|
|
if (pioctl_req->action == MLAN_ACT_GET) {
|
|
for (i = 0; i < MAX_NUM_TID; i++) {
|
|
cfg->param.aggr_prio_tbl.ampdu[i] =
|
|
pmpriv->aggr_prio_tbl[i].ampdu_user;
|
|
cfg->param.aggr_prio_tbl.amsdu[i] =
|
|
pmpriv->aggr_prio_tbl[i].amsdu;
|
|
}
|
|
} else {
|
|
for (i = 0; i < MAX_NUM_TID; i++) {
|
|
/* For AMPDU */
|
|
if ((cfg->param.aggr_prio_tbl.ampdu[i] >
|
|
HIGH_PRIO_TID) &&
|
|
(cfg->param.aggr_prio_tbl.ampdu[i] !=
|
|
BA_STREAM_NOT_ALLOWED)) {
|
|
pioctl_req->status_code =
|
|
MLAN_ERROR_INVALID_PARAMETER;
|
|
ret = MLAN_STATUS_FAILURE;
|
|
break;
|
|
}
|
|
|
|
pmpriv->aggr_prio_tbl[i].ampdu_ap =
|
|
pmpriv->aggr_prio_tbl[i].ampdu_user =
|
|
cfg->param.aggr_prio_tbl.ampdu[i];
|
|
|
|
/* For AMSDU */
|
|
if ((cfg->param.aggr_prio_tbl.amsdu[i] >
|
|
HIGH_PRIO_TID &&
|
|
cfg->param.aggr_prio_tbl.amsdu[i] !=
|
|
BA_STREAM_NOT_ALLOWED)) {
|
|
pioctl_req->status_code =
|
|
MLAN_ERROR_INVALID_PARAMETER;
|
|
ret = MLAN_STATUS_FAILURE;
|
|
break;
|
|
} else {
|
|
pmpriv->aggr_prio_tbl[i].amsdu =
|
|
cfg->param.aggr_prio_tbl.amsdu[i];
|
|
}
|
|
}
|
|
if (pmpriv->media_connected == MTRUE) {
|
|
for (i = 0; i < MAX_NUM_TID; i++) {
|
|
if (cfg->param.aggr_prio_tbl.ampdu[i] ==
|
|
BA_STREAM_NOT_ALLOWED) {
|
|
PRINTM(MIOCTL,
|
|
"Receive aggrpriotbl: BA not allowed tid=%d\n",
|
|
i);
|
|
wlan_send_delba_txbastream_tbl(pmpriv,
|
|
i);
|
|
}
|
|
}
|
|
wlan_update_all_stations_ampdu(pmpriv);
|
|
wlan_recv_event(pmpriv,
|
|
MLAN_EVENT_ID_DRV_DEFER_HANDLING,
|
|
MNULL);
|
|
}
|
|
}
|
|
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief This function update all the tx_win_size
|
|
*
|
|
* @param pmadapter A pointer to mlan_adapter
|
|
*
|
|
*
|
|
* @return N/A
|
|
*/
|
|
void wlan_update_ampdu_txwinsize(pmlan_adapter pmadapter)
|
|
{
|
|
t_u8 i;
|
|
t_u32 tx_win_size = 0;
|
|
pmlan_private priv = MNULL;
|
|
|
|
ENTER();
|
|
|
|
for (i = 0; i < pmadapter->priv_num; i++) {
|
|
if (pmadapter->priv[i]) {
|
|
priv = pmadapter->priv[i];
|
|
tx_win_size = priv->add_ba_param.tx_win_size;
|
|
#ifdef STA_SUPPORT
|
|
if (priv->bss_type == MLAN_BSS_TYPE_STA)
|
|
priv->add_ba_param.tx_win_size =
|
|
MLAN_STA_AMPDU_DEF_TXWINSIZE;
|
|
#endif
|
|
#ifdef WIFI_DIRECT_SUPPORT
|
|
if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT)
|
|
priv->add_ba_param.tx_win_size =
|
|
MLAN_WFD_AMPDU_DEF_TXRXWINSIZE;
|
|
#endif
|
|
#ifdef UAP_SUPPORT
|
|
if (priv->bss_type == MLAN_BSS_TYPE_UAP)
|
|
priv->add_ba_param.tx_win_size =
|
|
MLAN_UAP_AMPDU_DEF_TXWINSIZE;
|
|
#endif
|
|
if (pmadapter->coex_win_size &&
|
|
pmadapter->coex_tx_win_size)
|
|
priv->add_ba_param.tx_win_size =
|
|
pmadapter->coex_tx_win_size;
|
|
|
|
if (tx_win_size != priv->add_ba_param.tx_win_size) {
|
|
if (priv->media_connected == MTRUE) {
|
|
for (i = 0; i < MAX_NUM_TID; i++)
|
|
wlan_send_delba_txbastream_tbl(
|
|
priv, i);
|
|
wlan_recv_event(
|
|
priv,
|
|
MLAN_EVENT_ID_DRV_DEFER_HANDLING,
|
|
MNULL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
LEAVE();
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* @brief Get supported MCS set
|
|
*
|
|
* @param pmadapter A pointer to mlan_adapter structure
|
|
* @param pioctl_req A pointer to ioctl request buffer
|
|
*
|
|
* @return MLAN_STATUS_SUCCESS --success, otherwise fail
|
|
*/
|
|
static mlan_status wlan_11n_ioctl_supported_mcs_set(pmlan_adapter pmadapter,
|
|
pmlan_ioctl_req pioctl_req)
|
|
{
|
|
mlan_ds_11n_cfg *cfg = MNULL;
|
|
int rx_mcs_supp;
|
|
t_u8 mcs_set[NUM_MCS_FIELD];
|
|
mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index];
|
|
|
|
ENTER();
|
|
|
|
if (pioctl_req->action == MLAN_ACT_SET) {
|
|
PRINTM(MERROR, "Set operation is not supported\n");
|
|
pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID;
|
|
LEAVE();
|
|
return MLAN_STATUS_FAILURE;
|
|
}
|
|
rx_mcs_supp = GET_RXMCSSUPP(pmpriv->usr_dev_mcs_support);
|
|
/* Set MCS for 1x1/2x2*/
|
|
memset(pmadapter, (t_u8 *)mcs_set, 0xff, rx_mcs_supp);
|
|
/* Clear all the other values */
|
|
memset(pmadapter, (t_u8 *)&mcs_set[rx_mcs_supp], 0,
|
|
NUM_MCS_FIELD - rx_mcs_supp);
|
|
/* Set MCS32 with 40MHz support */
|
|
if ((ISSUPP_CHANWIDTH40(pmpriv->usr_dot_11n_dev_cap_bg) ||
|
|
ISSUPP_CHANWIDTH40(pmpriv->usr_dot_11n_dev_cap_a)) &&
|
|
!(pmpriv->curr_chan_flags & CHAN_FLAGS_NO_HT40PLUS &&
|
|
pmpriv->curr_chan_flags & CHAN_FLAGS_NO_HT40MINUS))
|
|
SETHT_MCS32(mcs_set);
|
|
|
|
cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf;
|
|
memcpy_ext(pmadapter, cfg->param.supported_mcs_set, mcs_set,
|
|
NUM_MCS_FIELD, NUM_MCS_FIELD);
|
|
|
|
LEAVE();
|
|
return MLAN_STATUS_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief This function checks if the given pointer is valid entry of
|
|
* Tx BA Stream table
|
|
*
|
|
* @param priv Pointer to mlan_private
|
|
* @param ptxtblptr Pointer to tx ba stream entry
|
|
*
|
|
* @return MTRUE or MFALSE
|
|
*/
|
|
static int wlan_is_txbastreamptr_valid(mlan_private *priv,
|
|
TxBAStreamTbl *ptxtblptr)
|
|
{
|
|
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 MFALSE;
|
|
}
|
|
|
|
while (ptx_tbl != (TxBAStreamTbl *)&priv->tx_ba_stream_tbl_ptr) {
|
|
if (ptx_tbl == ptxtblptr) {
|
|
LEAVE();
|
|
return MTRUE;
|
|
}
|
|
ptx_tbl = ptx_tbl->pnext;
|
|
}
|
|
LEAVE();
|
|
return MFALSE;
|
|
}
|
|
|
|
/**
|
|
* @brief This function will return the pointer to a entry in BA Stream
|
|
* table which matches the ba_status requested
|
|
*
|
|
* @param priv A pointer to mlan_private
|
|
* @param ba_status Current status of the BA stream
|
|
*
|
|
* @return A pointer to first entry matching status in BA stream
|
|
* NULL if not found
|
|
*/
|
|
static TxBAStreamTbl *wlan_11n_get_txbastream_status(mlan_private *priv,
|
|
baStatus_e ba_status)
|
|
{
|
|
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 MNULL;
|
|
}
|
|
|
|
while (ptx_tbl != (TxBAStreamTbl *)&priv->tx_ba_stream_tbl_ptr) {
|
|
if (ptx_tbl->ba_status == ba_status) {
|
|
LEAVE();
|
|
return ptx_tbl;
|
|
}
|
|
ptx_tbl = ptx_tbl->pnext;
|
|
}
|
|
|
|
LEAVE();
|
|
return MNULL;
|
|
}
|
|
|
|
/********************************************************
|
|
Global Functions
|
|
********************************************************/
|
|
|
|
#ifdef STA_SUPPORT
|
|
/**
|
|
* @brief This function fills the cap info
|
|
*
|
|
* @param priv A pointer to mlan_private structure
|
|
* @param pht_cap A pointer to MrvlIETypes_HTCap_t structure
|
|
* @param bands Band configuration
|
|
*
|
|
* @return N/A
|
|
*/
|
|
static void wlan_fill_cap_info(mlan_private *priv, HTCap_t *ht_cap, t_u16 bands)
|
|
{
|
|
t_u32 usr_dot_11n_dev_cap;
|
|
|
|
ENTER();
|
|
|
|
if (bands & BAND_A)
|
|
usr_dot_11n_dev_cap = priv->usr_dot_11n_dev_cap_a;
|
|
else
|
|
usr_dot_11n_dev_cap = priv->usr_dot_11n_dev_cap_bg;
|
|
|
|
if (ISSUPP_CHANWIDTH40(usr_dot_11n_dev_cap))
|
|
SETHT_SUPPCHANWIDTH(ht_cap->ht_cap_info);
|
|
else
|
|
RESETHT_SUPPCHANWIDTH(ht_cap->ht_cap_info);
|
|
|
|
if (ISSUPP_GREENFIELD(usr_dot_11n_dev_cap))
|
|
SETHT_GREENFIELD(ht_cap->ht_cap_info);
|
|
else
|
|
RESETHT_GREENFIELD(ht_cap->ht_cap_info);
|
|
|
|
if (ISSUPP_SHORTGI20(usr_dot_11n_dev_cap))
|
|
SETHT_SHORTGI20(ht_cap->ht_cap_info);
|
|
else
|
|
RESETHT_SHORTGI20(ht_cap->ht_cap_info);
|
|
|
|
if (ISSUPP_SHORTGI40(usr_dot_11n_dev_cap))
|
|
SETHT_SHORTGI40(ht_cap->ht_cap_info);
|
|
else
|
|
RESETHT_SHORTGI40(ht_cap->ht_cap_info);
|
|
if (ISSUPP_RXSTBC(usr_dot_11n_dev_cap))
|
|
SETHT_RXSTBC(ht_cap->ht_cap_info, 1);
|
|
else
|
|
RESETHT_RXSTBC(ht_cap->ht_cap_info);
|
|
|
|
if (ISENABLED_40MHZ_INTOLARENT(usr_dot_11n_dev_cap))
|
|
SETHT_40MHZ_INTOLARANT(ht_cap->ht_cap_info);
|
|
else
|
|
RESETHT_40MHZ_INTOLARANT(ht_cap->ht_cap_info);
|
|
|
|
/** if current channel only allow 20Mhz, we should cler 40Mhz support */
|
|
if (priv->curr_chan_flags & CHAN_FLAGS_NO_HT40PLUS &&
|
|
priv->curr_chan_flags & CHAN_FLAGS_NO_HT40MINUS) {
|
|
RESETHT_SUPPCHANWIDTH(ht_cap->ht_cap_info);
|
|
RESETHT_SHORTGI40(ht_cap->ht_cap_info);
|
|
RESETHT_40MHZ_INTOLARANT(ht_cap->ht_cap_info);
|
|
}
|
|
/* No user config for LDPC coding capability yet */
|
|
if (ISSUPP_RXLDPC(usr_dot_11n_dev_cap))
|
|
SETHT_LDPCCODINGCAP(ht_cap->ht_cap_info);
|
|
else
|
|
RESETHT_LDPCCODINGCAP(ht_cap->ht_cap_info);
|
|
|
|
/* No user config for TX STBC yet */
|
|
if (ISSUPP_TXSTBC(usr_dot_11n_dev_cap))
|
|
SETHT_TXSTBC(ht_cap->ht_cap_info);
|
|
else
|
|
RESETHT_TXSTBC(ht_cap->ht_cap_info);
|
|
|
|
/* No user config for Delayed BACK yet */
|
|
RESETHT_DELAYEDBACK(ht_cap->ht_cap_info);
|
|
|
|
/* Need change to support 8k AMSDU receive */
|
|
RESETHT_MAXAMSDU(ht_cap->ht_cap_info);
|
|
|
|
/* SM power save */
|
|
RESETHT_SM_POWERSAVE(ht_cap->ht_cap_info); /* Clear to HT SMPS static
|
|
mode*/
|
|
if (ISSUPP_MIMOPS(usr_dot_11n_dev_cap)) {
|
|
if (ISSUPP_SMPS_DYNAMIC_MODE(usr_dot_11n_dev_cap))
|
|
SETHT_SMPS_DYNAMIC(ht_cap->ht_cap_info); /* Set to HT
|
|
SMPS dynamic
|
|
mode */
|
|
} else {
|
|
SETHT_SMPS_DISABLE(ht_cap->ht_cap_info); /* Disable HT SMPS */
|
|
}
|
|
|
|
LEAVE();
|
|
}
|
|
|
|
/**
|
|
* @brief This function clear the bit in cap info which we don't support
|
|
*
|
|
* @param priv A pointer to mlan_private structure
|
|
* @param pht_cap A pointer to MrvlIETypes_HTCap_t structure
|
|
* @param bands Band configuration
|
|
*
|
|
* @return N/A
|
|
*/
|
|
static void wlan_reset_cap_info(mlan_private *priv, HTCap_t *ht_cap,
|
|
t_u16 bands)
|
|
{
|
|
t_u32 usr_dot_11n_dev_cap;
|
|
|
|
ENTER();
|
|
|
|
if (bands & BAND_A)
|
|
usr_dot_11n_dev_cap = priv->usr_dot_11n_dev_cap_a;
|
|
else
|
|
usr_dot_11n_dev_cap = priv->usr_dot_11n_dev_cap_bg;
|
|
|
|
if (!ISSUPP_CHANWIDTH40(usr_dot_11n_dev_cap))
|
|
RESETHT_SUPPCHANWIDTH(ht_cap->ht_cap_info);
|
|
|
|
if (!ISSUPP_GREENFIELD(usr_dot_11n_dev_cap))
|
|
RESETHT_GREENFIELD(ht_cap->ht_cap_info);
|
|
|
|
if (!ISSUPP_SHORTGI20(usr_dot_11n_dev_cap))
|
|
RESETHT_SHORTGI20(ht_cap->ht_cap_info);
|
|
|
|
if (!ISSUPP_SHORTGI40(usr_dot_11n_dev_cap))
|
|
RESETHT_SHORTGI40(ht_cap->ht_cap_info);
|
|
if (!ISSUPP_RXSTBC(usr_dot_11n_dev_cap))
|
|
RESETHT_RXSTBC(ht_cap->ht_cap_info);
|
|
|
|
if (!ISENABLED_40MHZ_INTOLARENT(usr_dot_11n_dev_cap))
|
|
RESETHT_40MHZ_INTOLARANT(ht_cap->ht_cap_info);
|
|
|
|
/** if current channel only allow 20Mhz, we should cler 40Mhz support */
|
|
if (priv->curr_chan_flags & CHAN_FLAGS_NO_HT40PLUS &&
|
|
priv->curr_chan_flags & CHAN_FLAGS_NO_HT40MINUS) {
|
|
RESETHT_SUPPCHANWIDTH(ht_cap->ht_cap_info);
|
|
RESETHT_SHORTGI40(ht_cap->ht_cap_info);
|
|
RESETHT_40MHZ_INTOLARANT(ht_cap->ht_cap_info);
|
|
}
|
|
/* No user config for LDPC coding capability yet */
|
|
if (!ISSUPP_RXLDPC(usr_dot_11n_dev_cap))
|
|
RESETHT_LDPCCODINGCAP(ht_cap->ht_cap_info);
|
|
|
|
/* No user config for TX STBC yet */
|
|
if (!ISSUPP_TXSTBC(usr_dot_11n_dev_cap))
|
|
RESETHT_TXSTBC(ht_cap->ht_cap_info);
|
|
|
|
/* No user config for Delayed BACK yet */
|
|
RESETHT_DELAYEDBACK(ht_cap->ht_cap_info);
|
|
|
|
/* Need change to support 8k AMSDU receive */
|
|
RESETHT_MAXAMSDU(ht_cap->ht_cap_info);
|
|
/* SM power save */
|
|
if (!ISSUPP_MIMOPS(usr_dot_11n_dev_cap))
|
|
SETHT_SMPS_DISABLE(ht_cap->ht_cap_info); /* Disable HT SMPS */
|
|
|
|
LEAVE();
|
|
}
|
|
|
|
/**
|
|
* @brief This function fills the HT cap tlv
|
|
*
|
|
* @param priv A pointer to mlan_private structure
|
|
* @param pht_cap A pointer to MrvlIETypes_HTCap_t structure
|
|
* @param bands Band configuration
|
|
* @param fill A flag for fill the htcap info
|
|
*
|
|
* @return N/A
|
|
*/
|
|
void wlan_fill_ht_cap_tlv(mlan_private *priv, MrvlIETypes_HTCap_t *pht_cap,
|
|
t_u16 bands, t_u8 fill)
|
|
{
|
|
mlan_adapter *pmadapter = priv->adapter;
|
|
int rx_mcs_supp;
|
|
t_u32 usr_dot_11n_dev_cap;
|
|
|
|
ENTER();
|
|
|
|
if (bands & BAND_A)
|
|
usr_dot_11n_dev_cap = priv->usr_dot_11n_dev_cap_a;
|
|
else
|
|
usr_dot_11n_dev_cap = priv->usr_dot_11n_dev_cap_bg;
|
|
|
|
/* Fill HT cap info */
|
|
if (fill)
|
|
wlan_fill_cap_info(priv, &pht_cap->ht_cap, bands);
|
|
else
|
|
wlan_reset_cap_info(priv, &pht_cap->ht_cap, bands);
|
|
|
|
pht_cap->ht_cap.ht_cap_info =
|
|
wlan_cpu_to_le16(pht_cap->ht_cap.ht_cap_info);
|
|
|
|
/* Set ampdu param */
|
|
SETAMPDU_SIZE(pht_cap->ht_cap.ampdu_param, AMPDU_FACTOR_64K);
|
|
SETAMPDU_SPACING(pht_cap->ht_cap.ampdu_param, 0);
|
|
|
|
rx_mcs_supp = GET_RXMCSSUPP(priv->usr_dev_mcs_support);
|
|
#if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || \
|
|
defined(PCIE9097) || defined(SD9097) || defined(USB9097) || \
|
|
defined(SDNW62X) || defined(PCIENW62X) || defined(USBNW62X)
|
|
if (IS_CARD9098(pmadapter->card_type) ||
|
|
IS_CARDNW62X(pmadapter->card_type) ||
|
|
IS_CARD9097(pmadapter->card_type)) {
|
|
if (bands & BAND_A)
|
|
rx_mcs_supp = MIN(
|
|
rx_mcs_supp,
|
|
GET_RXMCSSUPP(pmadapter->user_htstream >> 8));
|
|
else
|
|
rx_mcs_supp =
|
|
MIN(rx_mcs_supp,
|
|
GET_RXMCSSUPP(pmadapter->user_htstream));
|
|
}
|
|
#endif
|
|
memset(pmadapter, (t_u8 *)pht_cap->ht_cap.supported_mcs_set, 0xff,
|
|
rx_mcs_supp);
|
|
/* Clear all the other values to get the minimum mcs set btw STA and AP
|
|
*/
|
|
memset(pmadapter,
|
|
(t_u8 *)&pht_cap->ht_cap.supported_mcs_set[rx_mcs_supp], 0,
|
|
NUM_MCS_FIELD - rx_mcs_supp);
|
|
/* Set MCS32 with 40MHz support */
|
|
/* if current channel only support 20MHz, we should not set 40Mz
|
|
* supprot*/
|
|
if (ISSUPP_CHANWIDTH40(usr_dot_11n_dev_cap) &&
|
|
!(priv->curr_chan_flags & CHAN_FLAGS_NO_HT40PLUS &&
|
|
priv->curr_chan_flags & CHAN_FLAGS_NO_HT40MINUS))
|
|
SETHT_MCS32(pht_cap->ht_cap.supported_mcs_set);
|
|
|
|
/* Clear RD responder bit */
|
|
RESETHT_EXTCAP_RDG(pht_cap->ht_cap.ht_ext_cap);
|
|
pht_cap->ht_cap.ht_ext_cap =
|
|
wlan_cpu_to_le16(pht_cap->ht_cap.ht_ext_cap);
|
|
|
|
/* Set Tx BF cap */
|
|
pht_cap->ht_cap.tx_bf_cap = wlan_cpu_to_le32(priv->tx_bf_cap);
|
|
|
|
LEAVE();
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* @brief This function fills the HT cap ie
|
|
*
|
|
* @param priv A pointer to mlan_private structure
|
|
* @param pht_cap A pointer to IEEEtypes_HTCap_t structure
|
|
* @param bands Band configuration
|
|
*
|
|
* @return N/A
|
|
*/
|
|
void wlan_fill_ht_cap_ie(mlan_private *priv, IEEEtypes_HTCap_t *pht_cap,
|
|
t_u16 bands)
|
|
{
|
|
mlan_adapter *pmadapter = priv->adapter;
|
|
int rx_mcs_supp;
|
|
t_u32 usr_dot_11n_dev_cap;
|
|
|
|
ENTER();
|
|
|
|
pht_cap->ieee_hdr.element_id = HT_CAPABILITY;
|
|
pht_cap->ieee_hdr.len = sizeof(HTCap_t);
|
|
if (bands & BAND_A)
|
|
usr_dot_11n_dev_cap = priv->usr_dot_11n_dev_cap_a;
|
|
else
|
|
usr_dot_11n_dev_cap = priv->usr_dot_11n_dev_cap_bg;
|
|
|
|
/* Fill HT cap info */
|
|
wlan_fill_cap_info(priv, &pht_cap->ht_cap, bands);
|
|
|
|
/* Set ampdu param */
|
|
SETAMPDU_SIZE(pht_cap->ht_cap.ampdu_param, AMPDU_FACTOR_64K);
|
|
SETAMPDU_SPACING(pht_cap->ht_cap.ampdu_param, 0);
|
|
|
|
rx_mcs_supp = GET_RXMCSSUPP(priv->usr_dev_mcs_support);
|
|
#if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || \
|
|
defined(PCIE9097) || defined(SD9097) || defined(USB9097) || \
|
|
defined(SDNW62X) || defined(PCIENW62X) || defined(USBNW62X)
|
|
if (IS_CARD9098(pmadapter->card_type) ||
|
|
IS_CARDNW62X(pmadapter->card_type) ||
|
|
IS_CARD9097(pmadapter->card_type)) {
|
|
if (bands & BAND_A)
|
|
rx_mcs_supp = MIN(
|
|
rx_mcs_supp,
|
|
GET_RXMCSSUPP(pmadapter->user_htstream >> 8));
|
|
else
|
|
rx_mcs_supp =
|
|
MIN(rx_mcs_supp,
|
|
GET_RXMCSSUPP(pmadapter->user_htstream));
|
|
}
|
|
#endif
|
|
memset(pmadapter, (t_u8 *)pht_cap->ht_cap.supported_mcs_set, 0xff,
|
|
rx_mcs_supp);
|
|
/* Clear all the other values to get the minimum mcs set btw STA and AP
|
|
*/
|
|
memset(pmadapter,
|
|
(t_u8 *)&pht_cap->ht_cap.supported_mcs_set[rx_mcs_supp], 0,
|
|
NUM_MCS_FIELD - rx_mcs_supp);
|
|
/* Set MCS32 with 40MHz support */
|
|
/* if current channel only support 20MHz, we should not set 40Mz
|
|
* supprot*/
|
|
if (ISSUPP_CHANWIDTH40(usr_dot_11n_dev_cap) &&
|
|
!(priv->curr_chan_flags & CHAN_FLAGS_NO_HT40PLUS &&
|
|
priv->curr_chan_flags & CHAN_FLAGS_NO_HT40MINUS))
|
|
SETHT_MCS32(pht_cap->ht_cap.supported_mcs_set);
|
|
|
|
/* Clear RD responder bit */
|
|
RESETHT_EXTCAP_RDG(pht_cap->ht_cap.ht_ext_cap);
|
|
|
|
/* Set Tx BF cap */
|
|
pht_cap->ht_cap.tx_bf_cap = priv->tx_bf_cap;
|
|
|
|
LEAVE();
|
|
return;
|
|
}
|
|
#endif /* STA_SUPPORT */
|
|
|
|
/**
|
|
* @brief This function prints the 802.11n device capability
|
|
*
|
|
* @param pmadapter A pointer to mlan_adapter structure
|
|
* @param cap Capability value
|
|
*
|
|
* @return N/A
|
|
*/
|
|
void wlan_show_dot11ndevcap(pmlan_adapter pmadapter, t_u32 cap)
|
|
{
|
|
ENTER();
|
|
|
|
PRINTM(MINFO, "GET_HW_SPEC: Maximum MSDU length = %s octets\n",
|
|
(ISSUPP_MAXAMSDU(cap) ? "7935" : "3839"));
|
|
PRINTM(MINFO, "GET_HW_SPEC: Beam forming %s\n",
|
|
(ISSUPP_BEAMFORMING(cap) ? "supported" : "not supported"));
|
|
PRINTM(MINFO, "GET_HW_SPEC: Greenfield preamble %s\n",
|
|
(ISSUPP_GREENFIELD(cap) ? "supported" : "not supported"));
|
|
PRINTM(MINFO, "GET_HW_SPEC: AMPDU %s\n",
|
|
(ISSUPP_AMPDU(cap) ? "supported" : "not supported"));
|
|
PRINTM(MINFO, "GET_HW_SPEC: MIMO Power Save %s\n",
|
|
(ISSUPP_MIMOPS(cap) ? "supported" : "not supported"));
|
|
PRINTM(MINFO, "GET_HW_SPEC: Rx STBC %s\n",
|
|
(ISSUPP_RXSTBC(cap) ? "supported" : "not supported"));
|
|
PRINTM(MINFO, "GET_HW_SPEC: Tx STBC %s\n",
|
|
(ISSUPP_TXSTBC(cap) ? "supported" : "not supported"));
|
|
PRINTM(MINFO, "GET_HW_SPEC: Short GI for 40 Mhz %s\n",
|
|
(ISSUPP_SHORTGI40(cap) ? "supported" : "not supported"));
|
|
PRINTM(MINFO, "GET_HW_SPEC: Short GI for 20 Mhz %s\n",
|
|
(ISSUPP_SHORTGI20(cap) ? "supported" : "not supported"));
|
|
PRINTM(MINFO, "GET_HW_SPEC: LDPC coded packet receive %s\n",
|
|
(ISSUPP_RXLDPC(cap) ? "supported" : "not supported"));
|
|
|
|
PRINTM(MINFO, "GET_HW_SPEC: Number of Tx BA streams supported = %d\n",
|
|
ISSUPP_GETTXBASTREAM(cap));
|
|
PRINTM(MINFO, "GET_HW_SPEC: 40 Mhz channel width %s\n",
|
|
(ISSUPP_CHANWIDTH40(cap) ? "supported" : "not supported"));
|
|
PRINTM(MINFO, "GET_HW_SPEC: 20 Mhz channel width %s\n",
|
|
(ISSUPP_CHANWIDTH20(cap) ? "supported" : "not supported"));
|
|
PRINTM(MINFO, "GET_HW_SPEC: 10 Mhz channel width %s\n",
|
|
(ISSUPP_CHANWIDTH10(cap) ? "supported" : "not supported"));
|
|
|
|
if (ISSUPP_RXANTENNAA(cap))
|
|
PRINTM(MINFO, "GET_HW_SPEC: Presence of Rx antenna A\n");
|
|
if (ISSUPP_RXANTENNAB(cap))
|
|
PRINTM(MINFO, "GET_HW_SPEC: Presence of Rx antenna B\n");
|
|
if (ISSUPP_RXANTENNAC(cap))
|
|
PRINTM(MINFO, "GET_HW_SPEC: Presence of Rx antenna C\n");
|
|
if (ISSUPP_RXANTENNAD(cap))
|
|
PRINTM(MINFO, "GET_HW_SPEC: Presence of Rx antenna D\n");
|
|
if (ISSUPP_TXANTENNAA(cap))
|
|
PRINTM(MINFO, "GET_HW_SPEC: Presence of Tx antenna A\n");
|
|
if (ISSUPP_TXANTENNAB(cap))
|
|
PRINTM(MINFO, "GET_HW_SPEC: Presence of Tx antenna B\n");
|
|
if (ISSUPP_TXANTENNAC(cap))
|
|
PRINTM(MINFO, "GET_HW_SPEC: Presence of Tx antenna C\n");
|
|
if (ISSUPP_TXANTENNAD(cap))
|
|
PRINTM(MINFO, "GET_HW_SPEC: Presence of Tx antenna D\n");
|
|
|
|
LEAVE();
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* @brief This function prints the 802.11n device MCS
|
|
*
|
|
* @param pmadapter A pointer to mlan_adapter structure
|
|
* @param support Support value
|
|
*
|
|
* @return N/A
|
|
*/
|
|
void wlan_show_devmcssupport(pmlan_adapter pmadapter, t_u8 support)
|
|
{
|
|
ENTER();
|
|
|
|
PRINTM(MINFO, "GET_HW_SPEC: MCSs for %dx%d MIMO\n",
|
|
GET_RXMCSSUPP(support), GET_TXMCSSUPP(support));
|
|
|
|
LEAVE();
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* @brief This function handles the command response of
|
|
* delete a block ack request
|
|
*
|
|
* @param priv A pointer to mlan_private structure
|
|
* @param resp A pointer to HostCmd_DS_COMMAND
|
|
*
|
|
* @return MLAN_STATUS_SUCCESS
|
|
*/
|
|
mlan_status wlan_ret_11n_delba(mlan_private *priv, HostCmd_DS_COMMAND *resp)
|
|
{
|
|
int tid;
|
|
TxBAStreamTbl *ptx_ba_tbl;
|
|
HostCmd_DS_11N_DELBA *pdel_ba =
|
|
(HostCmd_DS_11N_DELBA *)&resp->params.del_ba;
|
|
|
|
ENTER();
|
|
|
|
pdel_ba->del_ba_param_set = wlan_le16_to_cpu(pdel_ba->del_ba_param_set);
|
|
pdel_ba->reason_code = wlan_le16_to_cpu(pdel_ba->reason_code);
|
|
|
|
tid = pdel_ba->del_ba_param_set >> DELBA_TID_POS;
|
|
if (pdel_ba->del_result == BA_RESULT_SUCCESS) {
|
|
mlan_11n_delete_bastream_tbl(
|
|
priv, tid, pdel_ba->peer_mac_addr, TYPE_DELBA_SENT,
|
|
INITIATOR_BIT(pdel_ba->del_ba_param_set), 0);
|
|
wlan_request_ralist_lock(priv);
|
|
ptx_ba_tbl = wlan_11n_get_txbastream_status(
|
|
priv, BA_STREAM_SETUP_INPROGRESS);
|
|
wlan_release_ralist_lock(priv);
|
|
if (ptx_ba_tbl)
|
|
wlan_send_addba(priv, ptx_ba_tbl->tid, ptx_ba_tbl->ra);
|
|
} else { /*
|
|
* In case of failure, recreate
|
|
* the deleted stream in case
|
|
* we initiated the ADDBA
|
|
*/
|
|
if (INITIATOR_BIT(pdel_ba->del_ba_param_set)) {
|
|
wlan_request_ralist_lock(priv);
|
|
if (!wlan_11n_get_txbastream_tbl(
|
|
priv, tid, pdel_ba->peer_mac_addr, MFALSE))
|
|
wlan_11n_create_txbastream_tbl(
|
|
priv, pdel_ba->peer_mac_addr, tid,
|
|
BA_STREAM_SETUP_INPROGRESS);
|
|
ptx_ba_tbl = wlan_11n_get_txbastream_status(
|
|
priv, BA_STREAM_SETUP_INPROGRESS);
|
|
wlan_release_ralist_lock(priv);
|
|
if (ptx_ba_tbl) {
|
|
mlan_11n_delete_bastream_tbl(
|
|
priv, ptx_ba_tbl->tid, ptx_ba_tbl->ra,
|
|
TYPE_DELBA_SENT, MTRUE, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
LEAVE();
|
|
return MLAN_STATUS_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief This function handles the command response of
|
|
* add a block ack request
|
|
*
|
|
* @param priv A pointer to mlan_private structure
|
|
* @param resp A pointer to HostCmd_DS_COMMAND
|
|
*
|
|
* @return MLAN_STATUS_SUCCESS
|
|
*/
|
|
mlan_status wlan_ret_11n_addba_req(mlan_private *priv, HostCmd_DS_COMMAND *resp)
|
|
{
|
|
t_u8 tid;
|
|
HostCmd_DS_11N_ADDBA_RSP *padd_ba_rsp =
|
|
(HostCmd_DS_11N_ADDBA_RSP *)&resp->params.add_ba_rsp;
|
|
TxBAStreamTbl *ptx_ba_tbl;
|
|
raListTbl *ra_list = MNULL;
|
|
int tid_down;
|
|
|
|
ENTER();
|
|
|
|
padd_ba_rsp->block_ack_param_set =
|
|
wlan_le16_to_cpu(padd_ba_rsp->block_ack_param_set);
|
|
padd_ba_rsp->block_ack_tmo =
|
|
wlan_le16_to_cpu(padd_ba_rsp->block_ack_tmo);
|
|
padd_ba_rsp->ssn = (wlan_le16_to_cpu(padd_ba_rsp->ssn)) & SSN_MASK;
|
|
padd_ba_rsp->status_code = wlan_le16_to_cpu(padd_ba_rsp->status_code);
|
|
|
|
tid = (padd_ba_rsp->block_ack_param_set & BLOCKACKPARAM_TID_MASK) >>
|
|
BLOCKACKPARAM_TID_POS;
|
|
tid_down = wlan_get_wmm_tid_down(priv, tid);
|
|
ra_list = wlan_wmm_get_ralist_node(priv, tid_down,
|
|
padd_ba_rsp->peer_mac_addr);
|
|
if (padd_ba_rsp->status_code == BA_RESULT_SUCCESS) {
|
|
ptx_ba_tbl = wlan_11n_get_txbastream_tbl(
|
|
priv, tid, padd_ba_rsp->peer_mac_addr, MTRUE);
|
|
if (ptx_ba_tbl) {
|
|
PRINTM(MCMND,
|
|
"ADDBA REQ: " MACSTR
|
|
" tid=%d ssn=%d win_size=%d,amsdu=%d\n",
|
|
MAC2STR(padd_ba_rsp->peer_mac_addr), tid,
|
|
padd_ba_rsp->ssn,
|
|
((padd_ba_rsp->block_ack_param_set &
|
|
BLOCKACKPARAM_WINSIZE_MASK) >>
|
|
BLOCKACKPARAM_WINSIZE_POS),
|
|
padd_ba_rsp->block_ack_param_set &
|
|
BLOCKACKPARAM_AMSDU_SUPP_MASK);
|
|
ptx_ba_tbl->ba_status = BA_STREAM_SETUP_COMPLETE;
|
|
if ((padd_ba_rsp->block_ack_param_set &
|
|
BLOCKACKPARAM_AMSDU_SUPP_MASK) &&
|
|
priv->add_ba_param.tx_amsdu &&
|
|
(priv->aggr_prio_tbl[tid].amsdu !=
|
|
BA_STREAM_NOT_ALLOWED))
|
|
ptx_ba_tbl->amsdu = MTRUE;
|
|
else
|
|
ptx_ba_tbl->amsdu = MFALSE;
|
|
if (ra_list) {
|
|
ra_list->amsdu_in_ampdu = ptx_ba_tbl->amsdu;
|
|
ra_list->ba_status = BA_STREAM_SETUP_COMPLETE;
|
|
}
|
|
} else {
|
|
PRINTM(MERROR, "BA stream not created\n");
|
|
}
|
|
} else {
|
|
if (ra_list) {
|
|
ra_list->amsdu_in_ampdu = MFALSE;
|
|
ra_list->ba_status = BA_STREAM_NOT_SETUP;
|
|
}
|
|
mlan_11n_delete_bastream_tbl(priv, tid,
|
|
padd_ba_rsp->peer_mac_addr,
|
|
TYPE_DELBA_SENT, MTRUE, 0);
|
|
if (padd_ba_rsp->add_rsp_result != BA_RESULT_TIMEOUT) {
|
|
#ifdef UAP_SUPPORT
|
|
if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP)
|
|
disable_station_ampdu(
|
|
priv, tid, padd_ba_rsp->peer_mac_addr);
|
|
#endif /* UAP_SUPPORT */
|
|
if (ra_list && ra_list->is_tdls_link)
|
|
disable_station_ampdu(
|
|
priv, tid, padd_ba_rsp->peer_mac_addr);
|
|
priv->aggr_prio_tbl[tid].ampdu_ap =
|
|
BA_STREAM_NOT_ALLOWED;
|
|
|
|
} else {
|
|
if (ra_list) {
|
|
ra_list->packet_count = 0;
|
|
ra_list->ba_packet_threshold =
|
|
wlan_get_random_ba_threshold(
|
|
priv->adapter);
|
|
}
|
|
}
|
|
}
|
|
|
|
LEAVE();
|
|
return MLAN_STATUS_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief This function restore tx_pause flag
|
|
*
|
|
* @param priv A pointer to mlan_private structure
|
|
* @param flag MTRUE/MFALSE;
|
|
*
|
|
* @return N/A
|
|
*/
|
|
void wlan_set_tx_pause_flag(mlan_private *priv, t_u8 flag)
|
|
{
|
|
mlan_private *pmpriv = MNULL;
|
|
t_u8 i;
|
|
for (i = 0; i < priv->adapter->priv_num; i++) {
|
|
pmpriv = priv->adapter->priv[i];
|
|
if (pmpriv)
|
|
pmpriv->tx_pause = flag;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief This function prepares command of reconfigure tx buf
|
|
*
|
|
* @param priv A pointer to mlan_private structure
|
|
* @param cmd A pointer to HostCmd_DS_COMMAND structure
|
|
* @param cmd_action The action: GET or SET
|
|
* @param pdata_buf A pointer to data buffer
|
|
*
|
|
* @return MLAN_STATUS_SUCCESS
|
|
*/
|
|
mlan_status wlan_cmd_recfg_tx_buf(mlan_private *priv, HostCmd_DS_COMMAND *cmd,
|
|
int cmd_action, void *pdata_buf)
|
|
{
|
|
HostCmd_DS_TXBUF_CFG *ptx_buf = &cmd->params.tx_buf;
|
|
t_u16 action = (t_u16)cmd_action;
|
|
t_u16 buf_size = *((t_u16 *)pdata_buf);
|
|
|
|
ENTER();
|
|
cmd->command = wlan_cpu_to_le16(HostCmd_CMD_RECONFIGURE_TX_BUFF);
|
|
cmd->size = wlan_cpu_to_le16(sizeof(HostCmd_DS_TXBUF_CFG) + S_DS_GEN);
|
|
ptx_buf->action = wlan_cpu_to_le16(action);
|
|
switch (action) {
|
|
case HostCmd_ACT_GEN_SET:
|
|
PRINTM(MCMND, "set tx_buf = %d\n", buf_size);
|
|
ptx_buf->buff_size = wlan_cpu_to_le16(buf_size);
|
|
/** stop tx traffic */
|
|
wlan_set_tx_pause_flag(priv, MTRUE);
|
|
break;
|
|
case HostCmd_ACT_GEN_GET:
|
|
default:
|
|
ptx_buf->buff_size = 0;
|
|
break;
|
|
}
|
|
LEAVE();
|
|
return MLAN_STATUS_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief This function prepares command of amsdu aggr control
|
|
*
|
|
* @param priv A pointer to mlan_private structure
|
|
* @param cmd A pointer to HostCmd_DS_COMMAND structure
|
|
* @param cmd_action The action: GET or SET
|
|
* @param pdata_buf A pointer to data buffer
|
|
*
|
|
* @return MLAN_STATUS_SUCCESS
|
|
*/
|
|
mlan_status wlan_cmd_amsdu_aggr_ctrl(mlan_private *priv,
|
|
HostCmd_DS_COMMAND *cmd, int cmd_action,
|
|
void *pdata_buf)
|
|
{
|
|
HostCmd_DS_AMSDU_AGGR_CTRL *pamsdu_ctrl = &cmd->params.amsdu_aggr_ctrl;
|
|
t_u16 action = (t_u16)cmd_action;
|
|
mlan_ds_11n_amsdu_aggr_ctrl *aa_ctrl =
|
|
(mlan_ds_11n_amsdu_aggr_ctrl *)pdata_buf;
|
|
|
|
ENTER();
|
|
|
|
cmd->command = wlan_cpu_to_le16(HostCmd_CMD_AMSDU_AGGR_CTRL);
|
|
cmd->size =
|
|
wlan_cpu_to_le16(sizeof(HostCmd_DS_AMSDU_AGGR_CTRL) + S_DS_GEN);
|
|
pamsdu_ctrl->action = wlan_cpu_to_le16(action);
|
|
switch (action) {
|
|
case HostCmd_ACT_GEN_SET:
|
|
pamsdu_ctrl->enable = wlan_cpu_to_le16(aa_ctrl->enable);
|
|
pamsdu_ctrl->curr_buf_size = 0;
|
|
break;
|
|
case HostCmd_ACT_GEN_GET:
|
|
default:
|
|
pamsdu_ctrl->curr_buf_size = 0;
|
|
break;
|
|
}
|
|
LEAVE();
|
|
return MLAN_STATUS_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief This function handles the command response of amsdu aggr ctrl
|
|
*
|
|
* @param pmpriv A pointer to mlan_private structure
|
|
* @param resp A pointer to HostCmd_DS_COMMAND
|
|
* @param pioctl_buf A pointer to mlan_ioctl_req structure
|
|
*
|
|
* @return MLAN_STATUS_SUCCESS
|
|
*/
|
|
mlan_status wlan_ret_amsdu_aggr_ctrl(pmlan_private pmpriv,
|
|
HostCmd_DS_COMMAND *resp,
|
|
mlan_ioctl_req *pioctl_buf)
|
|
{
|
|
mlan_ds_11n_cfg *cfg = MNULL;
|
|
HostCmd_DS_AMSDU_AGGR_CTRL *amsdu_ctrl = &resp->params.amsdu_aggr_ctrl;
|
|
|
|
ENTER();
|
|
|
|
if (pioctl_buf) {
|
|
cfg = (mlan_ds_11n_cfg *)pioctl_buf->pbuf;
|
|
cfg->param.amsdu_aggr_ctrl.enable =
|
|
wlan_le16_to_cpu(amsdu_ctrl->enable);
|
|
cfg->param.amsdu_aggr_ctrl.curr_buf_size =
|
|
wlan_le16_to_cpu(amsdu_ctrl->curr_buf_size);
|
|
}
|
|
LEAVE();
|
|
return MLAN_STATUS_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief This function prepares 11n cfg command
|
|
*
|
|
* @param pmpriv A pointer to mlan_private structure
|
|
* @param cmd A pointer to HostCmd_DS_COMMAND structure
|
|
* @param cmd_action the action: GET or SET
|
|
* @param pdata_buf A pointer to data buffer
|
|
* @return MLAN_STATUS_SUCCESS
|
|
*/
|
|
mlan_status wlan_cmd_11n_cfg(pmlan_private pmpriv, HostCmd_DS_COMMAND *cmd,
|
|
t_u16 cmd_action, t_void *pdata_buf)
|
|
{
|
|
HostCmd_DS_11N_CFG *htcfg = &cmd->params.htcfg;
|
|
mlan_ds_11n_tx_cfg *txcfg = (mlan_ds_11n_tx_cfg *)pdata_buf;
|
|
|
|
ENTER();
|
|
cmd->command = wlan_cpu_to_le16(HostCmd_CMD_11N_CFG);
|
|
cmd->size = wlan_cpu_to_le16(sizeof(HostCmd_DS_11N_CFG) + S_DS_GEN);
|
|
htcfg->action = wlan_cpu_to_le16(cmd_action);
|
|
htcfg->ht_tx_cap = wlan_cpu_to_le16(txcfg->httxcap);
|
|
htcfg->ht_tx_info = wlan_cpu_to_le16(txcfg->httxinfo);
|
|
htcfg->misc_config = wlan_cpu_to_le16(txcfg->misc_cfg);
|
|
LEAVE();
|
|
return MLAN_STATUS_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief This function handles the command response of 11ncfg
|
|
*
|
|
* @param pmpriv A pointer to mlan_private structure
|
|
* @param resp A pointer to HostCmd_DS_COMMAND
|
|
* @param pioctl_buf A pointer to mlan_ioctl_req structure
|
|
*
|
|
* @return MLAN_STATUS_SUCCESS
|
|
*/
|
|
mlan_status wlan_ret_11n_cfg(pmlan_private pmpriv, HostCmd_DS_COMMAND *resp,
|
|
mlan_ioctl_req *pioctl_buf)
|
|
{
|
|
mlan_ds_11n_cfg *cfg = MNULL;
|
|
HostCmd_DS_11N_CFG *htcfg = &resp->params.htcfg;
|
|
|
|
ENTER();
|
|
if (pioctl_buf &&
|
|
(wlan_le16_to_cpu(htcfg->action) == HostCmd_ACT_GEN_GET)) {
|
|
cfg = (mlan_ds_11n_cfg *)pioctl_buf->pbuf;
|
|
cfg->param.tx_cfg.httxcap = wlan_le16_to_cpu(htcfg->ht_tx_cap);
|
|
cfg->param.tx_cfg.httxinfo =
|
|
wlan_le16_to_cpu(htcfg->ht_tx_info);
|
|
cfg->param.tx_cfg.misc_cfg =
|
|
wlan_le16_to_cpu(htcfg->misc_config);
|
|
}
|
|
LEAVE();
|
|
return MLAN_STATUS_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief This function prepares reject addba req command
|
|
*
|
|
* @param pmpriv A pointer to mlan_private structure
|
|
* @param cmd A pointer to HostCmd_DS_COMMAND structure
|
|
* @param cmd_action the action: GET or SET
|
|
* @param pdata_buf A pointer to data buffer
|
|
* @return MLAN_STATUS_SUCCESS
|
|
*/
|
|
mlan_status wlan_cmd_reject_addba_req(pmlan_private pmpriv,
|
|
HostCmd_DS_COMMAND *cmd, t_u16 cmd_action,
|
|
t_void *pdata_buf)
|
|
{
|
|
HostCmd_DS_REJECT_ADDBA_REQ *preject_addba_req =
|
|
&cmd->params.rejectaddbareq;
|
|
mlan_ds_reject_addba_req *prejaddbareq =
|
|
(mlan_ds_reject_addba_req *)pdata_buf;
|
|
|
|
ENTER();
|
|
cmd->command = wlan_cpu_to_le16(HostCmd_CMD_REJECT_ADDBA_REQ);
|
|
cmd->size = wlan_cpu_to_le16(sizeof(HostCmd_DS_REJECT_ADDBA_REQ) +
|
|
S_DS_GEN);
|
|
preject_addba_req->action = wlan_cpu_to_le16(cmd_action);
|
|
preject_addba_req->conditions =
|
|
wlan_cpu_to_le32(prejaddbareq->conditions);
|
|
LEAVE();
|
|
return MLAN_STATUS_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief This function handles the command response of reject addba req
|
|
*
|
|
* @param pmpriv A pointer to mlan_private structure
|
|
* @param resp A pointer to HostCmd_DS_COMMAND
|
|
* @param pioctl_buf A pointer to mlan_ioctl_req structure
|
|
*
|
|
* @return MLAN_STATUS_SUCCESS
|
|
*/
|
|
mlan_status wlan_ret_reject_addba_req(pmlan_private pmpriv,
|
|
HostCmd_DS_COMMAND *resp,
|
|
mlan_ioctl_req *pioctl_buf)
|
|
{
|
|
mlan_ds_11n_cfg *cfg = MNULL;
|
|
HostCmd_DS_REJECT_ADDBA_REQ *preject_addba_req =
|
|
&resp->params.rejectaddbareq;
|
|
|
|
ENTER();
|
|
if (pioctl_buf && (wlan_le16_to_cpu(preject_addba_req->action) ==
|
|
HostCmd_ACT_GEN_GET)) {
|
|
cfg = (mlan_ds_11n_cfg *)pioctl_buf->pbuf;
|
|
cfg->param.reject_addba_req.conditions =
|
|
wlan_le32_to_cpu(preject_addba_req->conditions);
|
|
}
|
|
LEAVE();
|
|
return MLAN_STATUS_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief This function prepares TX BF configuration command
|
|
*
|
|
* @param pmpriv A pointer to mlan_private structure
|
|
* @param cmd A pointer to HostCmd_DS_COMMAND structure
|
|
* @param cmd_action The action: GET or SET
|
|
* @param pdata_buf A pointer to data buffer
|
|
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
|
|
*/
|
|
mlan_status wlan_cmd_tx_bf_cfg(pmlan_private pmpriv, HostCmd_DS_COMMAND *cmd,
|
|
t_u16 cmd_action, t_void *pdata_buf)
|
|
{
|
|
pmlan_adapter pmadapter = pmpriv->adapter;
|
|
HostCmd_DS_TX_BF_CFG *txbfcfg = &cmd->params.tx_bf_cfg;
|
|
mlan_ds_11n_tx_bf_cfg *txbf = (mlan_ds_11n_tx_bf_cfg *)pdata_buf;
|
|
|
|
ENTER();
|
|
|
|
cmd->command = wlan_cpu_to_le16(HostCmd_CMD_TX_BF_CFG);
|
|
cmd->size = wlan_cpu_to_le16(sizeof(HostCmd_DS_TX_BF_CFG) + S_DS_GEN);
|
|
|
|
if (txbf->bf_action == SET_GET_BF_PERIODICITY) {
|
|
memcpy_ext(pmadapter, txbfcfg->body.bf_periodicity.peer_mac,
|
|
txbf->body.bf_periodicity[0].peer_mac,
|
|
MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH);
|
|
}
|
|
txbfcfg->action = wlan_cpu_to_le16(txbf->action);
|
|
txbfcfg->bf_action = wlan_cpu_to_le16(txbf->bf_action);
|
|
if (cmd_action == HostCmd_ACT_GEN_SET) {
|
|
switch (txbf->bf_action) {
|
|
case BF_GLOBAL_CONFIGURATION:
|
|
txbfcfg->body.bf_global_cfg.bf_enbl =
|
|
txbf->body.bf_global_cfg.bf_enbl;
|
|
txbfcfg->body.bf_global_cfg.sounding_enbl =
|
|
txbf->body.bf_global_cfg.sounding_enbl;
|
|
txbfcfg->body.bf_global_cfg.fb_type =
|
|
txbf->body.bf_global_cfg.fb_type;
|
|
txbfcfg->body.bf_global_cfg.snr_threshold =
|
|
txbf->body.bf_global_cfg.snr_threshold;
|
|
txbfcfg->body.bf_global_cfg.sounding_interval =
|
|
wlan_cpu_to_le16(txbf->body.bf_global_cfg
|
|
.sounding_interval);
|
|
txbfcfg->body.bf_global_cfg.bf_mode =
|
|
txbf->body.bf_global_cfg.bf_mode;
|
|
break;
|
|
case TRIGGER_SOUNDING_FOR_PEER:
|
|
memcpy_ext(pmadapter,
|
|
txbfcfg->body.bf_sound_args.peer_mac,
|
|
txbf->body.bf_sound[0].peer_mac,
|
|
MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH);
|
|
break;
|
|
case SET_GET_BF_PERIODICITY:
|
|
txbfcfg->body.bf_periodicity.interval =
|
|
wlan_cpu_to_le16(
|
|
txbf->body.bf_periodicity->interval);
|
|
break;
|
|
case TX_BF_FOR_PEER_ENBL:
|
|
memcpy_ext(pmadapter, txbfcfg->body.tx_bf_peer.peer_mac,
|
|
txbf->body.tx_bf_peer[0].peer_mac,
|
|
MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH);
|
|
txbfcfg->body.tx_bf_peer.bf_enbl =
|
|
txbf->body.tx_bf_peer[0].bf_enbl;
|
|
txbfcfg->body.tx_bf_peer.sounding_enbl =
|
|
txbf->body.tx_bf_peer[0].sounding_enbl;
|
|
txbfcfg->body.tx_bf_peer.fb_type =
|
|
txbf->body.tx_bf_peer[0].fb_type;
|
|
break;
|
|
case SET_SNR_THR_PEER:
|
|
memcpy_ext(pmadapter, txbfcfg->body.bf_snr.peer_mac,
|
|
txbf->body.bf_snr[0].peer_mac,
|
|
MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH);
|
|
txbfcfg->body.bf_snr.snr = txbf->body.bf_snr[0].snr;
|
|
break;
|
|
default:
|
|
LEAVE();
|
|
return MLAN_STATUS_FAILURE;
|
|
}
|
|
}
|
|
|
|
LEAVE();
|
|
return MLAN_STATUS_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief This function handles the command response
|
|
* of TX BF configuration
|
|
*
|
|
* @param pmpriv A pointer to mlan_private structure
|
|
* @param resp A pointer to HostCmd_DS_COMMAND
|
|
* @param pioctl_buf A pointer to mlan_ioctl_req structure
|
|
*
|
|
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
|
|
*/
|
|
mlan_status wlan_ret_tx_bf_cfg(pmlan_private pmpriv, HostCmd_DS_COMMAND *resp,
|
|
mlan_ioctl_req *pioctl_buf)
|
|
{
|
|
pmlan_adapter pmadapter = pmpriv->adapter;
|
|
HostCmd_DS_TX_BF_CFG *txbfcfg = &resp->params.tx_bf_cfg;
|
|
mlan_ds_11n_cfg *cfg_11n = MNULL;
|
|
mlan_ds_11n_tx_bf_cfg *txbf = MNULL;
|
|
bf_peer_args *tx_bf_peer;
|
|
bf_snr_thr_t *bf_snr;
|
|
int i;
|
|
|
|
ENTER();
|
|
|
|
if (pioctl_buf) {
|
|
cfg_11n = (mlan_ds_11n_cfg *)pioctl_buf->pbuf;
|
|
txbf = (mlan_ds_11n_tx_bf_cfg *)&cfg_11n->param.tx_bf;
|
|
txbf->bf_action = wlan_le16_to_cpu(txbfcfg->bf_action);
|
|
switch (txbf->bf_action) {
|
|
case BF_GLOBAL_CONFIGURATION:
|
|
txbf->body.bf_global_cfg.bf_enbl =
|
|
txbfcfg->body.bf_global_cfg.bf_enbl;
|
|
txbf->body.bf_global_cfg.sounding_enbl =
|
|
txbfcfg->body.bf_global_cfg.sounding_enbl;
|
|
txbf->body.bf_global_cfg.fb_type =
|
|
txbfcfg->body.bf_global_cfg.fb_type;
|
|
txbf->body.bf_global_cfg.snr_threshold =
|
|
txbfcfg->body.bf_global_cfg.snr_threshold;
|
|
txbf->body.bf_global_cfg.sounding_interval =
|
|
wlan_le16_to_cpu(txbfcfg->body.bf_global_cfg
|
|
.sounding_interval);
|
|
txbf->body.bf_global_cfg.bf_mode =
|
|
txbfcfg->body.bf_global_cfg.bf_mode;
|
|
break;
|
|
case TRIGGER_SOUNDING_FOR_PEER:
|
|
memcpy_ext(pmadapter, txbf->body.bf_sound[0].peer_mac,
|
|
txbfcfg->body.bf_sound_args.peer_mac,
|
|
MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH);
|
|
txbf->body.bf_sound[0].status =
|
|
txbfcfg->body.bf_sound_args.status;
|
|
break;
|
|
case SET_GET_BF_PERIODICITY:
|
|
memcpy_ext(pmadapter,
|
|
txbf->body.bf_periodicity->peer_mac,
|
|
txbfcfg->body.bf_periodicity.peer_mac,
|
|
MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH);
|
|
txbf->body.bf_periodicity->interval = wlan_le16_to_cpu(
|
|
txbfcfg->body.bf_periodicity.interval);
|
|
break;
|
|
case TX_BF_FOR_PEER_ENBL:
|
|
txbf->no_of_peers = *(t_u8 *)&txbfcfg->body;
|
|
tx_bf_peer = (bf_peer_args *)((t_u8 *)&txbfcfg->body +
|
|
sizeof(t_u8));
|
|
for (i = 0; i < (int)txbf->no_of_peers; i++) {
|
|
memcpy_ext(pmadapter,
|
|
txbf->body.tx_bf_peer[i].peer_mac,
|
|
(t_u8 *)tx_bf_peer->peer_mac,
|
|
MLAN_MAC_ADDR_LENGTH,
|
|
MLAN_MAC_ADDR_LENGTH);
|
|
txbf->body.tx_bf_peer[i].bf_enbl =
|
|
tx_bf_peer->bf_enbl;
|
|
txbf->body.tx_bf_peer[i].sounding_enbl =
|
|
tx_bf_peer->sounding_enbl;
|
|
txbf->body.tx_bf_peer[i].fb_type =
|
|
tx_bf_peer->fb_type;
|
|
tx_bf_peer++;
|
|
}
|
|
break;
|
|
case SET_SNR_THR_PEER:
|
|
txbf->no_of_peers = *(t_u8 *)&txbfcfg->body;
|
|
bf_snr = (bf_snr_thr_t *)((t_u8 *)&txbfcfg->body +
|
|
sizeof(t_u8));
|
|
for (i = 0; i < (int)txbf->no_of_peers; i++) {
|
|
memcpy_ext(pmadapter,
|
|
txbf->body.bf_snr[i].peer_mac,
|
|
(t_u8 *)bf_snr->peer_mac,
|
|
MLAN_MAC_ADDR_LENGTH,
|
|
MLAN_MAC_ADDR_LENGTH);
|
|
txbf->body.bf_snr[i].snr = bf_snr->snr;
|
|
bf_snr++;
|
|
}
|
|
break;
|
|
default:
|
|
LEAVE();
|
|
return MLAN_STATUS_FAILURE;
|
|
}
|
|
}
|
|
|
|
LEAVE();
|
|
return MLAN_STATUS_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief Get second channel offset
|
|
*
|
|
* @param chan channel num
|
|
* @return second channel offset
|
|
*/
|
|
t_u8 wlan_get_second_channel_offset(mlan_private *priv, int chan)
|
|
{
|
|
t_u8 chan2Offset = SEC_CHAN_NONE;
|
|
|
|
/* Special Case: 20Mhz-only Channel */
|
|
if (priv->adapter->region_code != COUNTRY_CODE_US && chan == 165)
|
|
return chan2Offset;
|
|
|
|
switch (chan) {
|
|
case 36:
|
|
case 44:
|
|
case 52:
|
|
case 60:
|
|
case 100:
|
|
case 108:
|
|
case 116:
|
|
case 124:
|
|
case 132:
|
|
case 140:
|
|
case 149:
|
|
case 157:
|
|
case 165:
|
|
case 173:
|
|
chan2Offset = SEC_CHAN_ABOVE;
|
|
break;
|
|
case 40:
|
|
case 48:
|
|
case 56:
|
|
case 64:
|
|
case 104:
|
|
case 112:
|
|
case 120:
|
|
case 128:
|
|
case 136:
|
|
case 144:
|
|
case 153:
|
|
case 161:
|
|
case 169:
|
|
case 177:
|
|
chan2Offset = SEC_CHAN_BELOW;
|
|
break;
|
|
}
|
|
return chan2Offset;
|
|
}
|
|
|
|
#ifdef STA_SUPPORT
|
|
/**
|
|
* @brief validate the channel offset for Infra/Ad-hoc band configuration
|
|
*
|
|
* @param pmpriv A pointer to mlan_private structure
|
|
* @param band band
|
|
* @param chan primary channel
|
|
* @param chan_bw channel bandwidth
|
|
*
|
|
* @return channel offset (NO_SEC_CHANNEL, SEC_CHANNEL_ABOVE,
|
|
* SEC_CHANNEL_BELOW)
|
|
*/
|
|
t_u8 wlan_validate_chan_offset(mlan_private *pmpriv, t_u16 band, t_u32 chan,
|
|
t_u8 chan_bw)
|
|
{
|
|
t_u8 chan_offset;
|
|
pmlan_adapter pmadapter = pmpriv->adapter;
|
|
|
|
if (chan_bw == CHANNEL_BW_40MHZ_ABOVE)
|
|
chan_offset = SEC_CHAN_ABOVE;
|
|
else if (chan_bw == CHANNEL_BW_40MHZ_BELOW)
|
|
chan_offset = SEC_CHAN_BELOW;
|
|
else
|
|
chan_offset = SEC_CHAN_NONE;
|
|
|
|
/* validation */
|
|
if (chan_offset != SEC_CHAN_NONE) {
|
|
if (band & BAND_GN) {
|
|
if ((chan == 1) || (chan == 2) || (chan == 3) ||
|
|
(chan == 4))
|
|
chan_offset = SEC_CHAN_ABOVE;
|
|
else if ((chan == 10) || (chan == 11) || (chan == 12) ||
|
|
(chan == 13))
|
|
chan_offset = SEC_CHAN_BELOW;
|
|
|
|
/* check if channel 12 is supported in the region */
|
|
if (!wlan_find_cfp_by_band_and_channel(pmadapter, band,
|
|
12))
|
|
if ((chan == 8) || (chan == 9))
|
|
chan_offset = SEC_CHAN_BELOW;
|
|
} else if (band & BAND_AN)
|
|
chan_offset =
|
|
wlan_get_second_channel_offset(pmpriv, chan);
|
|
}
|
|
return chan_offset;
|
|
}
|
|
|
|
/**
|
|
* @brief This function check if ht40 is allowed in current region
|
|
*
|
|
* @param pmpriv A pointer to mlan_private structure
|
|
* @param pbss_desc A pointer to BSSDescriptor_t structure
|
|
*
|
|
* @return MTRUE/MFALSE
|
|
*/
|
|
static int wlan_check_chan_width_ht40_by_region(mlan_private *pmpriv,
|
|
BSSDescriptor_t *pbss_desc)
|
|
{
|
|
pmlan_adapter pmadapter = pmpriv->adapter;
|
|
int i = 0;
|
|
int cover_pri_chan = MFALSE;
|
|
t_u8 pri_chan;
|
|
t_u8 chan_offset;
|
|
t_u8 num_cfp;
|
|
|
|
ENTER();
|
|
|
|
if (pbss_desc->pht_info == MNULL) {
|
|
PRINTM(MERROR, "ht_info pointer NULL, force use HT20\n");
|
|
LEAVE();
|
|
return MFALSE;
|
|
}
|
|
if (pmpriv->curr_chan_flags & CHAN_FLAGS_NO_HT40PLUS &&
|
|
pmpriv->curr_chan_flags & CHAN_FLAGS_NO_HT40MINUS) {
|
|
LEAVE();
|
|
return MFALSE;
|
|
}
|
|
|
|
pri_chan = pbss_desc->pht_info->ht_info.pri_chan;
|
|
chan_offset = GET_SECONDARYCHAN(pbss_desc->pht_info->ht_info.field2);
|
|
if ((chan_offset == SEC_CHAN_ABOVE) &&
|
|
(pmpriv->curr_chan_flags & CHAN_FLAGS_NO_HT40PLUS)) {
|
|
pmpriv->curr_chan_flags |=
|
|
CHAN_FLAGS_NO_HT40MINUS | CHAN_FLAGS_NO_80MHZ;
|
|
return MFALSE;
|
|
}
|
|
if ((chan_offset == SEC_CHAN_BELOW) &&
|
|
(pmpriv->curr_chan_flags & CHAN_FLAGS_NO_HT40MINUS)) {
|
|
pmpriv->curr_chan_flags |=
|
|
CHAN_FLAGS_NO_HT40PLUS | CHAN_FLAGS_NO_80MHZ;
|
|
return MFALSE;
|
|
}
|
|
if (pmpriv->curr_chan_flags & CHAN_FLAGS_MAX)
|
|
return MTRUE;
|
|
|
|
num_cfp = pmadapter->region_channel[0].num_cfp;
|
|
|
|
if ((pbss_desc->bss_band & (BAND_B | BAND_G)) &&
|
|
pmadapter->region_channel[0].valid) {
|
|
for (i = 0; i < num_cfp; i++) {
|
|
if (pri_chan ==
|
|
pmadapter->region_channel[0].pcfp[i].channel) {
|
|
cover_pri_chan = MTRUE;
|
|
break;
|
|
}
|
|
}
|
|
if (!cover_pri_chan) {
|
|
PRINTM(MERROR, "Invalid channel, force use HT20\n");
|
|
LEAVE();
|
|
return MFALSE;
|
|
}
|
|
|
|
if (chan_offset == SEC_CHAN_ABOVE) {
|
|
if (pri_chan > num_cfp - 4) {
|
|
PRINTM(MERROR,
|
|
"Invalid second channel offset, force use HT20\n");
|
|
LEAVE();
|
|
return MFALSE;
|
|
}
|
|
}
|
|
}
|
|
LEAVE();
|
|
return MTRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief This function append the 802_11N tlv
|
|
*
|
|
* @param pmpriv A pointer to mlan_private structure
|
|
* @param pbss_desc A pointer to BSSDescriptor_t structure
|
|
* @param ppbuffer A Pointer to command buffer pointer
|
|
*
|
|
* @return bytes added to the buffer
|
|
*/
|
|
int wlan_cmd_append_11n_tlv(mlan_private *pmpriv, BSSDescriptor_t *pbss_desc,
|
|
t_u8 **ppbuffer)
|
|
{
|
|
pmlan_adapter pmadapter = pmpriv->adapter;
|
|
MrvlIETypes_HTCap_t *pht_cap;
|
|
MrvlIEtypes_ChanListParamSet_t *pchan_list;
|
|
MrvlIETypes_2040BSSCo_t *p2040_bss_co;
|
|
MrvlIETypes_ExtCap_t *pext_cap;
|
|
t_u32 usr_dot_11n_dev_cap, orig_usr_dot_11n_dev_cap = 0;
|
|
t_u8 usr_dot_11ac_bw;
|
|
int ret_len = 0;
|
|
|
|
ENTER();
|
|
|
|
/* Null Checks */
|
|
if (ppbuffer == MNULL) {
|
|
LEAVE();
|
|
return 0;
|
|
}
|
|
if (*ppbuffer == MNULL) {
|
|
LEAVE();
|
|
return 0;
|
|
}
|
|
if (pbss_desc == MNULL) {
|
|
LEAVE();
|
|
return 0;
|
|
}
|
|
|
|
if (pbss_desc->bss_band & BAND_A)
|
|
usr_dot_11n_dev_cap = pmpriv->usr_dot_11n_dev_cap_a;
|
|
else
|
|
usr_dot_11n_dev_cap = pmpriv->usr_dot_11n_dev_cap_bg;
|
|
|
|
if (pmpriv->bss_mode == MLAN_BSS_MODE_IBSS)
|
|
usr_dot_11ac_bw = BW_FOLLOW_VHTCAP;
|
|
else
|
|
usr_dot_11ac_bw = pmpriv->usr_dot_11ac_bw;
|
|
if ((pbss_desc->bss_band & (BAND_B | BAND_G | BAND_A)) &&
|
|
ISSUPP_CHANWIDTH40(usr_dot_11n_dev_cap) &&
|
|
!wlan_check_chan_width_ht40_by_region(pmpriv, pbss_desc)) {
|
|
orig_usr_dot_11n_dev_cap = usr_dot_11n_dev_cap;
|
|
RESETSUPP_CHANWIDTH40(usr_dot_11n_dev_cap);
|
|
RESET_40MHZ_INTOLARENT(usr_dot_11n_dev_cap);
|
|
RESETSUPP_SHORTGI40(usr_dot_11n_dev_cap);
|
|
pmpriv->usr_dot_11n_dev_cap_bg = usr_dot_11n_dev_cap;
|
|
pbss_desc->curr_bandwidth = BW_20MHZ;
|
|
}
|
|
if (pbss_desc->pht_cap) {
|
|
pht_cap = (MrvlIETypes_HTCap_t *)*ppbuffer;
|
|
memset(pmadapter, pht_cap, 0, sizeof(MrvlIETypes_HTCap_t));
|
|
pht_cap->header.type = wlan_cpu_to_le16(HT_CAPABILITY);
|
|
pht_cap->header.len = sizeof(HTCap_t);
|
|
memcpy_ext(pmadapter,
|
|
(t_u8 *)pht_cap + sizeof(MrvlIEtypesHeader_t),
|
|
(t_u8 *)pbss_desc->pht_cap +
|
|
sizeof(IEEEtypes_Header_t),
|
|
pht_cap->header.len, pht_cap->header.len);
|
|
|
|
pht_cap->ht_cap.ht_cap_info =
|
|
wlan_le16_to_cpu(pht_cap->ht_cap.ht_cap_info);
|
|
pht_cap->ht_cap.ht_ext_cap =
|
|
wlan_le16_to_cpu(pht_cap->ht_cap.ht_ext_cap);
|
|
wlan_fill_ht_cap_tlv(pmpriv, pht_cap, pbss_desc->bss_band,
|
|
MTRUE);
|
|
|
|
/** check if need support 80+80MHZ */
|
|
/** reset the 2 spatial stream rate for 80 + 80 Mhz */
|
|
if (wlan_is_80_80_support(pmpriv, pbss_desc))
|
|
pht_cap->ht_cap.supported_mcs_set[1] = 0;
|
|
HEXDUMP("HT_CAPABILITIES IE", (t_u8 *)pht_cap,
|
|
sizeof(MrvlIETypes_HTCap_t));
|
|
*ppbuffer += sizeof(MrvlIETypes_HTCap_t);
|
|
ret_len += sizeof(MrvlIETypes_HTCap_t);
|
|
pht_cap->header.len = wlan_cpu_to_le16(pht_cap->header.len);
|
|
} else {
|
|
// AP don't support 11N
|
|
LEAVE();
|
|
return 0;
|
|
}
|
|
|
|
if (pbss_desc->pht_info) {
|
|
pchan_list = (MrvlIEtypes_ChanListParamSet_t *)*ppbuffer;
|
|
memset(pmadapter, pchan_list, 0,
|
|
sizeof(MrvlIEtypes_ChanListParamSet_t));
|
|
pchan_list->header.type = wlan_cpu_to_le16(TLV_TYPE_CHANLIST);
|
|
pchan_list->header.len =
|
|
sizeof(MrvlIEtypes_ChanListParamSet_t) -
|
|
sizeof(MrvlIEtypesHeader_t);
|
|
pchan_list->chan_scan_param[0].chan_number =
|
|
pbss_desc->pht_info->ht_info.pri_chan;
|
|
pchan_list->chan_scan_param[0].bandcfg.chanBand =
|
|
wlan_band_to_radio_type(pbss_desc->bss_band);
|
|
/* support the VHT if the network to be join has the VHT
|
|
* operation */
|
|
if (ISSUPP_11ACENABLED(pmadapter->fw_cap_info) &&
|
|
(usr_dot_11ac_bw == BW_FOLLOW_VHTCAP) &&
|
|
(!(pmpriv->curr_chan_flags & CHAN_FLAGS_NO_80MHZ)) &&
|
|
wlan_11ac_bandconfig_allowed(pmpriv, pbss_desc->bss_band) &&
|
|
pbss_desc->pvht_oprat &&
|
|
pbss_desc->pvht_oprat->chan_width == VHT_OPER_CHWD_80MHZ) {
|
|
pchan_list->chan_scan_param[0].bandcfg.chanWidth =
|
|
CHAN_BW_80MHZ;
|
|
pchan_list->chan_scan_param[0].bandcfg.chan2Offset =
|
|
GET_SECONDARYCHAN(
|
|
pbss_desc->pht_info->ht_info.field2);
|
|
pbss_desc->curr_bandwidth = BW_80MHZ;
|
|
} else if (ISSUPP_CHANWIDTH40(usr_dot_11n_dev_cap) &&
|
|
ISALLOWED_CHANWIDTH40(
|
|
pbss_desc->pht_info->ht_info.field2) &&
|
|
wlan_check_chan_width_ht40_by_region(pmpriv,
|
|
pbss_desc)) {
|
|
pchan_list->chan_scan_param[0].bandcfg.chan2Offset =
|
|
GET_SECONDARYCHAN(
|
|
pbss_desc->pht_info->ht_info.field2);
|
|
pbss_desc->curr_bandwidth = BW_40MHZ;
|
|
pchan_list->chan_scan_param[0].bandcfg.chanWidth =
|
|
CHAN_BW_40MHZ;
|
|
}
|
|
pchan_list->chan_scan_param[0].bandcfg.scanMode =
|
|
SCAN_MODE_USER;
|
|
HEXDUMP("ChanList", (t_u8 *)pchan_list,
|
|
sizeof(MrvlIEtypes_ChanListParamSet_t));
|
|
HEXDUMP("pht_info", (t_u8 *)pbss_desc->pht_info,
|
|
sizeof(MrvlIETypes_HTInfo_t) - 2);
|
|
*ppbuffer += sizeof(MrvlIEtypes_ChanListParamSet_t);
|
|
ret_len += sizeof(MrvlIEtypes_ChanListParamSet_t);
|
|
pchan_list->header.len =
|
|
wlan_cpu_to_le16(pchan_list->header.len);
|
|
}
|
|
|
|
if (pbss_desc->pbss_co_2040) {
|
|
p2040_bss_co = (MrvlIETypes_2040BSSCo_t *)*ppbuffer;
|
|
memset(pmadapter, p2040_bss_co, 0,
|
|
sizeof(MrvlIETypes_2040BSSCo_t));
|
|
p2040_bss_co->header.type = wlan_cpu_to_le16(BSSCO_2040);
|
|
p2040_bss_co->header.len = sizeof(BSSCo2040_t);
|
|
|
|
memcpy_ext(pmadapter,
|
|
(t_u8 *)p2040_bss_co + sizeof(MrvlIEtypesHeader_t),
|
|
(t_u8 *)pbss_desc->pbss_co_2040 +
|
|
sizeof(IEEEtypes_Header_t),
|
|
p2040_bss_co->header.len, p2040_bss_co->header.len);
|
|
|
|
HEXDUMP("20/40 BSS Coexistence IE", (t_u8 *)p2040_bss_co,
|
|
sizeof(MrvlIETypes_2040BSSCo_t));
|
|
*ppbuffer += sizeof(MrvlIETypes_2040BSSCo_t);
|
|
ret_len += sizeof(MrvlIETypes_2040BSSCo_t);
|
|
p2040_bss_co->header.len =
|
|
wlan_cpu_to_le16(p2040_bss_co->header.len);
|
|
}
|
|
|
|
if (pbss_desc->pext_cap) {
|
|
pext_cap = (MrvlIETypes_ExtCap_t *)*ppbuffer;
|
|
memset(pmadapter, pext_cap, 0, sizeof(MrvlIETypes_ExtCap_t));
|
|
pext_cap->header.type = wlan_cpu_to_le16(EXT_CAPABILITY);
|
|
pext_cap->header.len = sizeof(ExtCap_t);
|
|
|
|
memcpy_ext(pmadapter,
|
|
(t_u8 *)pext_cap + sizeof(MrvlIEtypesHeader_t),
|
|
(t_u8 *)&pmpriv->ext_cap, sizeof(ExtCap_t),
|
|
pext_cap->header.len);
|
|
if (pbss_desc && pbss_desc->multi_bssid_ap)
|
|
SET_EXTCAP_MULTI_BSSID(pext_cap->ext_cap);
|
|
if (!pmadapter->ecsa_enable)
|
|
RESET_EXTCAP_EXT_CHANNEL_SWITCH(pext_cap->ext_cap);
|
|
else
|
|
SET_EXTCAP_EXT_CHANNEL_SWITCH(pext_cap->ext_cap);
|
|
|
|
HEXDUMP("Extended Capabilities IE", (t_u8 *)pext_cap,
|
|
sizeof(MrvlIETypes_ExtCap_t));
|
|
*ppbuffer += sizeof(MrvlIETypes_ExtCap_t);
|
|
ret_len += sizeof(MrvlIETypes_ExtCap_t);
|
|
pext_cap->header.len = wlan_cpu_to_le16(pext_cap->header.len);
|
|
} else if (wlan_is_ext_capa_support(pmpriv) ||
|
|
(pmpriv->config_bands & BAND_AAC)) {
|
|
wlan_add_ext_capa_info_ie(pmpriv, pbss_desc, ppbuffer);
|
|
ret_len += sizeof(MrvlIETypes_ExtCap_t);
|
|
}
|
|
PRINTM(MCMND, "curr_bandwidth=%d\n", pbss_desc->curr_bandwidth);
|
|
if (orig_usr_dot_11n_dev_cap)
|
|
pmpriv->usr_dot_11n_dev_cap_bg = orig_usr_dot_11n_dev_cap;
|
|
|
|
LEAVE();
|
|
return ret_len;
|
|
}
|
|
|
|
#endif /* STA_SUPPORT */
|
|
|
|
/**
|
|
* @brief 11n configuration handler
|
|
*
|
|
* @param pmadapter A pointer to mlan_adapter structure
|
|
* @param pioctl_req A pointer to ioctl request buffer
|
|
*
|
|
* @return MLAN_STATUS_SUCCESS --success, otherwise fail
|
|
*/
|
|
mlan_status wlan_11n_cfg_ioctl(pmlan_adapter pmadapter,
|
|
pmlan_ioctl_req pioctl_req)
|
|
{
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
mlan_ds_11n_cfg *cfg = MNULL;
|
|
|
|
ENTER();
|
|
|
|
if (pioctl_req->buf_len < sizeof(mlan_ds_11n_cfg)) {
|
|
PRINTM(MINFO, "MLAN bss IOCTL length is too short.\n");
|
|
pioctl_req->data_read_written = 0;
|
|
pioctl_req->buf_len_needed = sizeof(mlan_ds_11n_cfg);
|
|
pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER;
|
|
LEAVE();
|
|
return MLAN_STATUS_RESOURCE;
|
|
}
|
|
cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf;
|
|
switch (cfg->sub_command) {
|
|
case MLAN_OID_11N_CFG_TX:
|
|
status = wlan_11n_ioctl_httxcfg(pmadapter, pioctl_req);
|
|
break;
|
|
case MLAN_OID_11N_HTCAP_CFG:
|
|
status = wlan_11n_ioctl_htusrcfg(pmadapter, pioctl_req);
|
|
break;
|
|
case MLAN_OID_11N_CFG_AGGR_PRIO_TBL:
|
|
status = wlan_11n_ioctl_aggr_prio_tbl(pmadapter, pioctl_req);
|
|
break;
|
|
case MLAN_OID_11N_CFG_ADDBA_REJECT:
|
|
status = wlan_11n_ioctl_addba_reject(pmadapter, pioctl_req);
|
|
break;
|
|
case MLAN_OID_11N_CFG_ADDBA_PARAM:
|
|
status = wlan_11n_ioctl_addba_param(pmadapter, pioctl_req);
|
|
break;
|
|
case MLAN_OID_11N_CFG_DELBA:
|
|
status = wlan_11n_ioctl_delba(pmadapter, pioctl_req);
|
|
break;
|
|
case MLAN_OID_11N_CFG_REJECT_ADDBA_REQ:
|
|
status = wlan_11n_ioctl_rejectaddbareq(pmadapter, pioctl_req);
|
|
break;
|
|
case MLAN_OID_11N_CFG_MAX_TX_BUF_SIZE:
|
|
status = wlan_11n_ioctl_max_tx_buf_size(pmadapter, pioctl_req);
|
|
break;
|
|
case MLAN_OID_11N_CFG_AMSDU_AGGR_CTRL:
|
|
status = wlan_11n_ioctl_amsdu_aggr_ctrl(pmadapter, pioctl_req);
|
|
break;
|
|
case MLAN_OID_11N_CFG_SUPPORTED_MCS_SET:
|
|
status =
|
|
wlan_11n_ioctl_supported_mcs_set(pmadapter, pioctl_req);
|
|
break;
|
|
case MLAN_OID_11N_CFG_TX_BF_CAP:
|
|
status = wlan_11n_ioctl_tx_bf_cap(pmadapter, pioctl_req);
|
|
break;
|
|
case MLAN_OID_11N_CFG_TX_BF_CFG:
|
|
status = wlan_11n_ioctl_tx_bf_cfg(pmadapter, pioctl_req);
|
|
break;
|
|
case MLAN_OID_11N_CFG_STREAM_CFG:
|
|
status = wlan_11n_ioctl_stream_cfg(pmadapter, pioctl_req);
|
|
break;
|
|
case MLAN_OID_11N_CFG_COEX_RX_WINSIZE:
|
|
status = wlan_11n_ioctl_coex_rx_winsize(pmadapter, pioctl_req);
|
|
break;
|
|
case MLAN_OID_11N_CFG_TX_AGGR_CTRL:
|
|
status = wlan_11n_ioctl_txaggrctrl(pmadapter, pioctl_req);
|
|
break;
|
|
case MLAN_OID_11N_CFG_IBSS_AMPDU_PARAM:
|
|
status = wlan_11n_ioctl_ibss_ampdu_param(pmadapter, pioctl_req);
|
|
break;
|
|
case MLAN_OID_11N_CFG_MIN_BA_THRESHOLD:
|
|
status = wlan_11n_ioctl_min_ba_threshold_cfg(pmadapter,
|
|
pioctl_req);
|
|
break;
|
|
default:
|
|
pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID;
|
|
status = MLAN_STATUS_FAILURE;
|
|
break;
|
|
}
|
|
LEAVE();
|
|
return status;
|
|
}
|
|
|
|
/**
|
|
* @brief This function will delete the given entry in Tx BA Stream table
|
|
*
|
|
* @param priv Pointer to mlan_private
|
|
* @param ptx_tbl Pointer to tx ba stream entry to delete
|
|
*
|
|
* @return N/A
|
|
*/
|
|
void wlan_11n_delete_txbastream_tbl_entry(mlan_private *priv,
|
|
TxBAStreamTbl *ptx_tbl)
|
|
{
|
|
pmlan_adapter pmadapter = priv->adapter;
|
|
|
|
ENTER();
|
|
|
|
if (!ptx_tbl || !wlan_is_txbastreamptr_valid(priv, ptx_tbl))
|
|
goto exit;
|
|
PRINTM(MINFO, "Delete BA stream table entry: %p\n", ptx_tbl);
|
|
util_unlink_list(pmadapter->pmoal_handle, &priv->tx_ba_stream_tbl_ptr,
|
|
(pmlan_linked_list)ptx_tbl, MNULL, MNULL);
|
|
pmadapter->callbacks.moal_mfree(pmadapter->pmoal_handle,
|
|
(t_u8 *)ptx_tbl);
|
|
exit:
|
|
LEAVE();
|
|
}
|
|
|
|
/**
|
|
* @brief This function will delete all the entries in Tx BA Stream table
|
|
*
|
|
* @param priv A pointer to mlan_private
|
|
*
|
|
* @return N/A
|
|
*/
|
|
void wlan_11n_deleteall_txbastream_tbl(mlan_private *priv)
|
|
{
|
|
int i;
|
|
TxBAStreamTbl *del_tbl_ptr = MNULL;
|
|
|
|
ENTER();
|
|
|
|
wlan_request_ralist_lock(priv);
|
|
while ((del_tbl_ptr = (TxBAStreamTbl *)util_peek_list(
|
|
priv->adapter->pmoal_handle,
|
|
&priv->tx_ba_stream_tbl_ptr, MNULL, MNULL))) {
|
|
wlan_11n_delete_txbastream_tbl_entry(priv, del_tbl_ptr);
|
|
}
|
|
|
|
util_init_list((pmlan_linked_list)&priv->tx_ba_stream_tbl_ptr);
|
|
wlan_release_ralist_lock(priv);
|
|
for (i = 0; i < MAX_NUM_TID; ++i) {
|
|
priv->aggr_prio_tbl[i].ampdu_ap =
|
|
priv->aggr_prio_tbl[i].ampdu_user;
|
|
}
|
|
|
|
LEAVE();
|
|
}
|
|
|
|
/**
|
|
* @brief This function will return the pointer to an entry in BA Stream
|
|
* table which matches the give RA/TID pair
|
|
*
|
|
* @param priv A pointer to mlan_private
|
|
* @param tid TID to find in reordering table
|
|
* @param ra RA to find in reordering table
|
|
* @param lock flag for request the spin_lock
|
|
*
|
|
* @return A pointer to first entry matching RA/TID in BA stream
|
|
* NULL if not found
|
|
*/
|
|
TxBAStreamTbl *wlan_11n_get_txbastream_tbl(mlan_private *priv, int tid,
|
|
t_u8 *ra, int lock)
|
|
{
|
|
TxBAStreamTbl *ptx_tbl;
|
|
pmlan_adapter pmadapter = priv->adapter;
|
|
|
|
ENTER();
|
|
|
|
if (lock)
|
|
wlan_request_ralist_lock(priv);
|
|
ptx_tbl = (TxBAStreamTbl *)util_peek_list(pmadapter->pmoal_handle,
|
|
&priv->tx_ba_stream_tbl_ptr,
|
|
MNULL, MNULL);
|
|
if (!ptx_tbl) {
|
|
if (lock)
|
|
wlan_release_ralist_lock(priv);
|
|
LEAVE();
|
|
return MNULL;
|
|
}
|
|
|
|
while (ptx_tbl != (TxBAStreamTbl *)&priv->tx_ba_stream_tbl_ptr) {
|
|
PRINTM(MDAT_D, "get_txbastream_tbl TID %d\n", ptx_tbl->tid);
|
|
DBG_HEXDUMP(MDAT_D, "RA", ptx_tbl->ra, MLAN_MAC_ADDR_LENGTH);
|
|
|
|
if ((!memcmp(pmadapter, ptx_tbl->ra, ra,
|
|
MLAN_MAC_ADDR_LENGTH)) &&
|
|
(ptx_tbl->tid == tid)) {
|
|
if (lock)
|
|
wlan_release_ralist_lock(priv);
|
|
LEAVE();
|
|
return ptx_tbl;
|
|
}
|
|
|
|
ptx_tbl = ptx_tbl->pnext;
|
|
}
|
|
if (lock)
|
|
wlan_release_ralist_lock(priv);
|
|
LEAVE();
|
|
return MNULL;
|
|
}
|
|
|
|
/**
|
|
* @brief This function will create a entry in tx ba stream table for the
|
|
* given RA/TID.
|
|
*
|
|
* @param priv A pointer to mlan_private
|
|
* @param ra RA to find in reordering table
|
|
* @param tid TID to find in reordering table
|
|
* @param ba_status BA stream status to create the stream with
|
|
*
|
|
* @return N/A
|
|
*/
|
|
void wlan_11n_create_txbastream_tbl(mlan_private *priv, t_u8 *ra, int tid,
|
|
baStatus_e ba_status)
|
|
{
|
|
TxBAStreamTbl *new_node = MNULL;
|
|
pmlan_adapter pmadapter = priv->adapter;
|
|
raListTbl *ra_list = MNULL;
|
|
int tid_down;
|
|
|
|
ENTER();
|
|
|
|
PRINTM(MDAT_D, "create_txbastream_tbl TID %d\n", tid);
|
|
DBG_HEXDUMP(MDAT_D, "RA", ra, MLAN_MAC_ADDR_LENGTH);
|
|
|
|
if (pmadapter->callbacks.moal_malloc(
|
|
pmadapter->pmoal_handle, sizeof(TxBAStreamTbl),
|
|
MLAN_MEM_DEF, (t_u8 **)&new_node)) {
|
|
PRINTM(MERROR,
|
|
"wlan_11n_create_txbastream_tbl Failed to allocate new_node\n");
|
|
LEAVE();
|
|
return;
|
|
}
|
|
tid_down = wlan_get_wmm_tid_down(priv, tid);
|
|
ra_list = wlan_wmm_get_ralist_node(priv, tid_down, ra);
|
|
if (ra_list) {
|
|
ra_list->amsdu_in_ampdu = MFALSE;
|
|
ra_list->ba_status = ba_status;
|
|
}
|
|
util_init_list((pmlan_linked_list)new_node);
|
|
|
|
new_node->tid = tid;
|
|
new_node->ba_status = ba_status;
|
|
memcpy_ext(pmadapter, new_node->ra, ra, MLAN_MAC_ADDR_LENGTH,
|
|
MLAN_MAC_ADDR_LENGTH);
|
|
|
|
util_enqueue_list_tail(pmadapter->pmoal_handle,
|
|
&priv->tx_ba_stream_tbl_ptr,
|
|
(pmlan_linked_list)new_node, MNULL, MNULL);
|
|
|
|
LEAVE();
|
|
}
|
|
|
|
/**
|
|
* @brief This function will send a block ack to given tid/ra
|
|
*
|
|
* @param priv A pointer to mlan_private
|
|
* @param tid TID to send the ADDBA
|
|
* @param peer_mac MAC address to send the ADDBA
|
|
*
|
|
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
|
|
*/
|
|
int wlan_send_addba(mlan_private *priv, int tid, t_u8 *peer_mac)
|
|
{
|
|
HostCmd_DS_11N_ADDBA_REQ add_ba_req;
|
|
static t_u8 dialog_tok;
|
|
mlan_status ret;
|
|
|
|
ENTER();
|
|
|
|
PRINTM(MCMND, "Send addba: TID %d\n", tid);
|
|
DBG_HEXDUMP(MCMD_D, "Send addba RA", peer_mac, MLAN_MAC_ADDR_LENGTH);
|
|
|
|
add_ba_req.block_ack_param_set = (t_u16)(
|
|
(tid << BLOCKACKPARAM_TID_POS) |
|
|
(priv->add_ba_param.tx_win_size << BLOCKACKPARAM_WINSIZE_POS) |
|
|
IMMEDIATE_BLOCK_ACK);
|
|
/** enable AMSDU inside AMPDU */
|
|
if (priv->add_ba_param.tx_amsdu &&
|
|
(priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED))
|
|
add_ba_req.block_ack_param_set |= BLOCKACKPARAM_AMSDU_SUPP_MASK;
|
|
add_ba_req.block_ack_tmo = (t_u16)priv->add_ba_param.timeout;
|
|
|
|
++dialog_tok;
|
|
|
|
if (dialog_tok == 0)
|
|
dialog_tok = 1;
|
|
|
|
add_ba_req.dialog_token = dialog_tok;
|
|
memcpy_ext(priv->adapter, &add_ba_req.peer_mac_addr, peer_mac,
|
|
MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH);
|
|
|
|
/* We don't wait for the response of this command */
|
|
ret = wlan_prepare_cmd(priv, HostCmd_CMD_11N_ADDBA_REQ, 0, 0, MNULL,
|
|
&add_ba_req);
|
|
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief This function will delete a block ack to given tid/ra
|
|
*
|
|
* @param priv A pointer to mlan_private
|
|
* @param pioctl_req A pointer to ioctl request buffer
|
|
* @param tid TID to send the ADDBA
|
|
* @param peer_mac MAC address to send the ADDBA
|
|
* @param initiator MTRUE if we have initiated ADDBA, MFALSE otherwise
|
|
*
|
|
* @return MLAN_STATUS_PENDING --success, otherwise fail
|
|
*/
|
|
int wlan_send_delba(mlan_private *priv, pmlan_ioctl_req pioctl_req, int tid,
|
|
t_u8 *peer_mac, int initiator)
|
|
{
|
|
HostCmd_DS_11N_DELBA delba;
|
|
mlan_status ret;
|
|
|
|
ENTER();
|
|
|
|
memset(priv->adapter, &delba, 0, sizeof(delba));
|
|
delba.del_ba_param_set = (tid << DELBA_TID_POS);
|
|
|
|
if (initiator)
|
|
DELBA_INITIATOR(delba.del_ba_param_set);
|
|
else
|
|
DELBA_RECIPIENT(delba.del_ba_param_set);
|
|
|
|
memcpy_ext(priv->adapter, &delba.peer_mac_addr, peer_mac,
|
|
MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH);
|
|
|
|
ret = wlan_prepare_cmd(priv, HostCmd_CMD_11N_DELBA, HostCmd_ACT_GEN_SET,
|
|
0, (t_void *)pioctl_req, (t_void *)&delba);
|
|
|
|
if (ret == MLAN_STATUS_SUCCESS)
|
|
ret = MLAN_STATUS_PENDING;
|
|
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief This function handles the command response of
|
|
* delete a block ack request
|
|
*
|
|
* @param priv A pointer to mlan_private structure
|
|
* @param del_ba A pointer to command response buffer
|
|
*
|
|
* @return N/A
|
|
*/
|
|
void wlan_11n_delete_bastream(mlan_private *priv, t_u8 *del_ba)
|
|
{
|
|
HostCmd_DS_11N_DELBA *pdel_ba = (HostCmd_DS_11N_DELBA *)del_ba;
|
|
int tid;
|
|
|
|
ENTER();
|
|
|
|
DBG_HEXDUMP(MCMD_D, "Delba:", (t_u8 *)pdel_ba, 20);
|
|
pdel_ba->del_ba_param_set = wlan_le16_to_cpu(pdel_ba->del_ba_param_set);
|
|
pdel_ba->reason_code = wlan_le16_to_cpu(pdel_ba->reason_code);
|
|
|
|
tid = pdel_ba->del_ba_param_set >> DELBA_TID_POS;
|
|
|
|
mlan_11n_delete_bastream_tbl(priv, tid, pdel_ba->peer_mac_addr,
|
|
TYPE_DELBA_RECEIVE,
|
|
INITIATOR_BIT(pdel_ba->del_ba_param_set),
|
|
pdel_ba->reason_code);
|
|
|
|
LEAVE();
|
|
}
|
|
|
|
/**
|
|
* @brief Get Rx reordering table
|
|
*
|
|
* @param priv A pointer to mlan_private structure
|
|
* @param buf A pointer to rx_reorder_tbl structure
|
|
* @return number of rx reorder table entry
|
|
*/
|
|
int wlan_get_rxreorder_tbl(mlan_private *priv, rx_reorder_tbl *buf)
|
|
{
|
|
int i;
|
|
rx_reorder_tbl *ptbl = buf;
|
|
RxReorderTbl *rx_reorder_tbl_ptr;
|
|
int count = 0;
|
|
ENTER();
|
|
priv->adapter->callbacks.moal_spin_lock(priv->adapter->pmoal_handle,
|
|
priv->rx_reorder_tbl_ptr.plock);
|
|
rx_reorder_tbl_ptr =
|
|
(RxReorderTbl *)util_peek_list(priv->adapter->pmoal_handle,
|
|
&priv->rx_reorder_tbl_ptr, MNULL,
|
|
MNULL);
|
|
if (!rx_reorder_tbl_ptr) {
|
|
priv->adapter->callbacks.moal_spin_unlock(
|
|
priv->adapter->pmoal_handle,
|
|
priv->rx_reorder_tbl_ptr.plock);
|
|
LEAVE();
|
|
return count;
|
|
}
|
|
while (rx_reorder_tbl_ptr !=
|
|
(RxReorderTbl *)&priv->rx_reorder_tbl_ptr) {
|
|
ptbl->tid = (t_u16)rx_reorder_tbl_ptr->tid;
|
|
memcpy_ext(priv->adapter, ptbl->ta, rx_reorder_tbl_ptr->ta,
|
|
MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH);
|
|
ptbl->start_win = rx_reorder_tbl_ptr->start_win;
|
|
ptbl->win_size = rx_reorder_tbl_ptr->win_size;
|
|
|
|
ptbl->amsdu = rx_reorder_tbl_ptr->amsdu;
|
|
for (i = 0; i < rx_reorder_tbl_ptr->win_size; ++i) {
|
|
if (rx_reorder_tbl_ptr->rx_reorder_ptr[i])
|
|
ptbl->buffer[i] = MTRUE;
|
|
else
|
|
ptbl->buffer[i] = MFALSE;
|
|
}
|
|
rx_reorder_tbl_ptr = rx_reorder_tbl_ptr->pnext;
|
|
ptbl++;
|
|
count++;
|
|
if (count >= MLAN_MAX_RX_BASTREAM_SUPPORTED)
|
|
break;
|
|
}
|
|
priv->adapter->callbacks.moal_spin_unlock(
|
|
priv->adapter->pmoal_handle, priv->rx_reorder_tbl_ptr.plock);
|
|
LEAVE();
|
|
return count;
|
|
}
|
|
|
|
/**
|
|
* @brief Get transmit BA stream table
|
|
*
|
|
* @param priv A pointer to mlan_private structure
|
|
* @param buf A pointer to tx_ba_stream_tbl structure
|
|
* @return number of ba stream table entry
|
|
*/
|
|
int wlan_get_txbastream_tbl(mlan_private *priv, tx_ba_stream_tbl *buf)
|
|
{
|
|
TxBAStreamTbl *ptxtbl;
|
|
tx_ba_stream_tbl *ptbl = buf;
|
|
int count = 0;
|
|
t_u32 bastream_max = 0;
|
|
|
|
ENTER();
|
|
|
|
wlan_request_ralist_lock(priv);
|
|
ptxtbl = (TxBAStreamTbl *)util_peek_list(priv->adapter->pmoal_handle,
|
|
&priv->tx_ba_stream_tbl_ptr,
|
|
MNULL, MNULL);
|
|
if (!ptxtbl) {
|
|
wlan_release_ralist_lock(priv);
|
|
LEAVE();
|
|
return count;
|
|
}
|
|
bastream_max = ISSUPP_GETTXBASTREAM(priv->adapter->hw_dot_11n_dev_cap);
|
|
if (bastream_max == 0)
|
|
bastream_max = MLAN_MAX_TX_BASTREAM_DEFAULT;
|
|
|
|
while (ptxtbl != (TxBAStreamTbl *)&priv->tx_ba_stream_tbl_ptr) {
|
|
ptbl->tid = (t_u16)ptxtbl->tid;
|
|
PRINTM(MINFO, "tid=%d\n", ptbl->tid);
|
|
memcpy_ext(priv->adapter, ptbl->ra, ptxtbl->ra,
|
|
MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH);
|
|
ptbl->amsdu = ptxtbl->amsdu;
|
|
ptxtbl = ptxtbl->pnext;
|
|
ptbl++;
|
|
count++;
|
|
if (count >= (int)bastream_max)
|
|
break;
|
|
}
|
|
wlan_release_ralist_lock(priv);
|
|
LEAVE();
|
|
return count;
|
|
}
|
|
|
|
/**
|
|
* @brief This function check if 11AC is allowed in bandcfg
|
|
*
|
|
* @param pmpriv A pointer to mlan_private structure
|
|
* @param bss_band bss band
|
|
*
|
|
* @return 0--not allowed, other value allowed
|
|
*/
|
|
t_u8 wlan_11n_bandconfig_allowed(mlan_private *pmpriv, t_u16 bss_band)
|
|
{
|
|
if (pmpriv->bss_mode == MLAN_BSS_MODE_IBSS) {
|
|
if (bss_band & BAND_G)
|
|
return (pmpriv->adapter->adhoc_start_band & BAND_GN);
|
|
else if (bss_band & BAND_A)
|
|
return (pmpriv->adapter->adhoc_start_band & BAND_AN);
|
|
} else {
|
|
if (bss_band & BAND_G)
|
|
return (pmpriv->config_bands & BAND_GN);
|
|
else if (bss_band & BAND_A)
|
|
return (pmpriv->config_bands & BAND_AN);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief This function cleans up txbastream_tbl for specific station
|
|
*
|
|
* @param priv A pointer to mlan_private
|
|
* @param ra RA to find in txbastream_tbl
|
|
* @return N/A
|
|
*/
|
|
void wlan_11n_cleanup_txbastream_tbl(mlan_private *priv, t_u8 *ra)
|
|
{
|
|
TxBAStreamTbl *ptx_tbl = MNULL;
|
|
t_u8 i;
|
|
ENTER();
|
|
|
|
wlan_request_ralist_lock(priv);
|
|
for (i = 0; i < MAX_NUM_TID; ++i) {
|
|
ptx_tbl = wlan_11n_get_txbastream_tbl(priv, i, ra, MFALSE);
|
|
if (ptx_tbl)
|
|
wlan_11n_delete_txbastream_tbl_entry(priv, ptx_tbl);
|
|
}
|
|
wlan_release_ralist_lock(priv);
|
|
LEAVE();
|
|
return;
|
|
}
|
|
|
|
void wlan_update_11n_cap(mlan_private *pmpriv)
|
|
{
|
|
mlan_adapter *pmadapter = pmpriv->adapter;
|
|
|
|
pmpriv->usr_dev_mcs_support = pmadapter->hw_dev_mcs_support;
|
|
pmpriv->usr_dot_11n_dev_cap_bg =
|
|
pmadapter->hw_dot_11n_dev_cap & DEFAULT_11N_CAP_MASK_BG;
|
|
pmpriv->usr_dot_11n_dev_cap_a =
|
|
pmadapter->hw_dot_11n_dev_cap & DEFAULT_11N_CAP_MASK_A;
|
|
}
|