mwifiex/mxm_wifiex/wlan_src/mlan/mlan_11ax.c
Sherry Sun 13b6fdb0b4 mxm_wifiex: update to mxm5x17283 release
changes:
1. WCSWREL-191: Fixed the error when loading module param from user config for SD8801
2. WCSWREL-186: Fixed the issue of mlanutl failing on kernel higher than L5.15
3. Fixed low throughput issue for WPA3 SAE
4. Added driver change for WLAN throughput improvement on 8997 SoC
5. Updated README to recommend not to use WEP/TKIP for all chipsets
6. WCSWREL-180: Fix P2P test fail on kernel higher than L5.12
7. WCSWREL-156: kernel_write/kernel_read not allowed by drivers for L5.10 kernel GKI buildou
8. Alternative for pm_qos_add_request/pm_qos_remove_request

Signed-off-by: Sherry Sun <sherry.sun@nxp.com>
Approved-by: Tian Yang <yang.tian@nxp.com>
2021-10-14 10:50:37 +08:00

1016 lines
31 KiB
C

/** @file mlan_11ax.c
*
* @brief This file contains the functions for 11ax related features.
*
*
* Copyright 2018-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.
*
*/
#include "mlan.h"
#include "mlan_join.h"
#include "mlan_util.h"
#include "mlan_ioctl.h"
#include "mlan_fw.h"
#include "mlan_main.h"
#include "mlan_wmm.h"
#include "mlan_11n.h"
#include "mlan_11ax.h"
#include "mlan_11ac.h"
/********************************************************
Local Variables
********************************************************/
/********************************************************
Global Variables
********************************************************/
/********************************************************
Local Functions
********************************************************/
/********************************************************
Global Functions
********************************************************/
#if 0
/**
* @brief This function prints the 802.11ax HE mac capability
*
* @param pmadapter A pointer to mlan_adapter structure
* @param cap Capability value
*
* @return N/A
*/
static void wlan_show_dot11axmaccap(pmlan_adapter pmadapter, t_u32 cap)
{
ENTER();
LEAVE();
return;
}
#endif
/**
* @brief This function check if AP support TWT Response.
*
* @param pbss_desc A pointer to BSSDescriptor_t structure
*
* @return MTRUE/MFALSE
*/
static t_u8 wlan_check_ap_11ax_twt_supported(BSSDescriptor_t *pbss_desc)
{
if (!pbss_desc->phe_cap)
return MFALSE;
if (!(pbss_desc->phe_cap->he_mac_cap[0] & HE_MAC_CAP_TWT_REQ_SUPPORT))
return MFALSE;
if (!pbss_desc->pext_cap)
return MFALSE;
if (!ISSUPP_EXTCAP_EXT_TWT_RESP(pbss_desc->pext_cap->ext_cap))
return MFALSE;
return MTRUE;
}
/**
* @brief This function check if we should enable TWT support
*
* @param pbss_desc A pointer to BSSDescriptor_t structure
*
* @return MTRUE/MFALSE
*/
t_u8 wlan_check_11ax_twt_supported(mlan_private *pmpriv,
BSSDescriptor_t *pbss_desc)
{
MrvlIEtypes_He_cap_t *phecap =
(MrvlIEtypes_He_cap_t *)&pmpriv->user_he_cap;
MrvlIEtypes_He_cap_t *hw_he_cap =
(MrvlIEtypes_He_cap_t *)&pmpriv->adapter->hw_he_cap;
if (pbss_desc && !wlan_check_ap_11ax_twt_supported(pbss_desc)) {
PRINTM(MINFO, "AP don't support twt feature\n");
return MFALSE;
}
if (pbss_desc) {
if (pbss_desc->bss_band & BAND_A) {
hw_he_cap = (MrvlIEtypes_He_cap_t *)&pmpriv->adapter
->hw_he_cap;
phecap = (MrvlIEtypes_He_cap_t *)&pmpriv->user_he_cap;
} else {
hw_he_cap = (MrvlIEtypes_He_cap_t *)&pmpriv->adapter
->hw_2g_he_cap;
phecap =
(MrvlIEtypes_He_cap_t *)&pmpriv->user_2g_he_cap;
}
}
if (!(hw_he_cap->he_mac_cap[0] & HE_MAC_CAP_TWT_REQ_SUPPORT)) {
PRINTM(MINFO, "FW don't support TWT\n");
return MFALSE;
}
if (phecap->he_mac_cap[0] & HE_MAC_CAP_TWT_REQ_SUPPORT)
return MTRUE;
PRINTM(MINFO, "USER HE_MAC_CAP don't support TWT\n");
return MFALSE;
}
#if 0
/**
* @brief This function prints the 802.11ax HE PHY cap
*
* @param pmadapter A pointer to mlan_adapter structure
* @param support Support value
*
* @return N/A
*/
static void wlan_show_dot11axphycap(pmlan_adapter pmadapter, t_u32 support)
{
ENTER();
LEAVE();
return;
}
#endif
/**
* @brief This function fills the HE cap tlv out put format is LE, not CPU
*
* @param priv A pointer to mlan_private structure
* @param band 5G or 2.4 G
* @param phe_cap A pointer to MrvlIEtypes_Data_t structure
* @param flag TREU--pvht_cap has the setting for resp
* MFALSE -- pvht_cap is clean
*
* @return bytes added to the phe_cap
*/
t_u16 wlan_fill_he_cap_tlv(mlan_private *pmpriv, t_u8 band,
MrvlIEtypes_Extension_t *phe_cap, t_u8 flag)
{
pmlan_adapter pmadapter = pmpriv->adapter;
t_u16 len = 0;
#if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || \
defined(PCIE9097) || defined(SD9097) || defined(USB9097)
t_u16 rx_nss = 0, tx_nss = 0;
#endif
MrvlIEtypes_He_cap_t *phecap = MNULL;
t_u8 nss = 0;
t_u16 cfg_value = 0;
t_u16 hw_value = 0;
MrvlIEtypes_He_cap_t *phw_hecap = MNULL;
if (!phe_cap) {
LEAVE();
return 0;
}
if (band & BAND_A) {
memcpy_ext(pmadapter, (t_u8 *)phe_cap, pmpriv->user_he_cap,
pmpriv->user_hecap_len,
sizeof(MrvlIEtypes_He_cap_t));
len = pmpriv->user_hecap_len;
phw_hecap = (MrvlIEtypes_He_cap_t *)pmadapter->hw_he_cap;
} else {
memcpy_ext(pmadapter, (t_u8 *)phe_cap, pmpriv->user_2g_he_cap,
pmpriv->user_2g_hecap_len,
sizeof(MrvlIEtypes_He_cap_t));
len = pmpriv->user_2g_hecap_len;
phw_hecap = (MrvlIEtypes_He_cap_t *)pmadapter->hw_2g_he_cap;
}
phe_cap->type = wlan_cpu_to_le16(phe_cap->type);
phe_cap->len = wlan_cpu_to_le16(phe_cap->len);
#if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || \
defined(PCIE9097) || defined(SD9097) || defined(USB9097)
if (IS_CARD9098(pmpriv->adapter->card_type) ||
IS_CARD9097(pmpriv->adapter->card_type)) {
if (band & BAND_A) {
rx_nss = GET_RXMCSSUPP(pmpriv->adapter->user_htstream >>
8);
tx_nss = GET_TXMCSSUPP(pmpriv->adapter->user_htstream >>
8) &
0x0f;
} else {
rx_nss = GET_RXMCSSUPP(pmpriv->adapter->user_htstream);
tx_nss = GET_TXMCSSUPP(pmpriv->adapter->user_htstream) &
0x0f;
}
}
#endif
phecap = (MrvlIEtypes_He_cap_t *)phe_cap;
for (nss = 1; nss <= 8; nss++) {
cfg_value = GET_HE_NSSMCS(phecap->rx_mcs_80, nss);
hw_value = GET_HE_NSSMCS(phw_hecap->rx_mcs_80, nss);
#if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || \
defined(PCIE9097) || defined(SD9097) || defined(USB9097)
if ((rx_nss != 0) && (nss > rx_nss))
cfg_value = NO_NSS_SUPPORT;
#endif
if ((hw_value == NO_NSS_SUPPORT) ||
(cfg_value == NO_NSS_SUPPORT))
SET_HE_NSSMCS(phecap->rx_mcs_80, nss, NO_NSS_SUPPORT);
else
SET_HE_NSSMCS(phecap->rx_mcs_80, nss,
MIN(cfg_value, hw_value));
}
for (nss = 1; nss <= 8; nss++) {
cfg_value = GET_HE_NSSMCS(phecap->tx_mcs_80, nss);
hw_value = GET_HE_NSSMCS(phw_hecap->tx_mcs_80, nss);
#if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || \
defined(PCIE9097) || defined(SD9097) || defined(USB9097)
if ((tx_nss != 0) && (nss > tx_nss))
cfg_value = NO_NSS_SUPPORT;
#endif
if ((hw_value == NO_NSS_SUPPORT) ||
(cfg_value == NO_NSS_SUPPORT))
SET_HE_NSSMCS(phecap->tx_mcs_80, nss, NO_NSS_SUPPORT);
else
SET_HE_NSSMCS(phecap->tx_mcs_80, nss,
MIN(cfg_value, hw_value));
}
PRINTM(MCMND, "Set: HE rx mcs set 0x%08x tx mcs set 0x%08x\n",
phecap->rx_mcs_80, phecap->tx_mcs_80);
DBG_HEXDUMP(MCMD_D, "fill_11ax_tlv", (t_u8 *)phecap, len);
LEAVE();
return len;
}
/**
* @brief This function append the 802_11ax HE capability 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_11ax_tlv(mlan_private *pmpriv, BSSDescriptor_t *pbss_desc,
t_u8 **ppbuffer)
{
pmlan_adapter pmadapter = pmpriv->adapter;
MrvlIEtypes_He_cap_t *phecap = MNULL;
int len = 0;
t_u8 bw_80p80 = MFALSE;
#if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || \
defined(PCIE9097) || defined(SD9097) || defined(USB9097)
t_u16 rx_nss = 0, tx_nss = 0;
#endif
t_u8 nss = 0;
t_u16 cfg_value = 0;
t_u16 hw_value = 0;
MrvlIEtypes_He_cap_t *phw_hecap = MNULL;
ENTER();
/* Null Checks */
if (ppbuffer == MNULL) {
LEAVE();
return 0;
}
if (*ppbuffer == MNULL) {
LEAVE();
return 0;
}
/** check if AP support HE, if not return right away */
if (!pbss_desc->phe_cap) {
LEAVE();
return 0;
}
bw_80p80 = wlan_is_80_80_support(pmpriv, pbss_desc);
phecap = (MrvlIEtypes_He_cap_t *)*ppbuffer;
if (pbss_desc->bss_band & BAND_A) {
memcpy_ext(pmadapter, *ppbuffer, pmpriv->user_he_cap,
pmpriv->user_hecap_len, pmpriv->user_hecap_len);
*ppbuffer += pmpriv->user_hecap_len;
len = pmpriv->user_hecap_len;
phw_hecap = (MrvlIEtypes_He_cap_t *)pmadapter->hw_he_cap;
} else {
memcpy_ext(pmadapter, *ppbuffer, pmpriv->user_2g_he_cap,
pmpriv->user_2g_hecap_len,
pmpriv->user_2g_hecap_len);
*ppbuffer += pmpriv->user_2g_hecap_len;
len = pmpriv->user_2g_hecap_len;
phw_hecap = (MrvlIEtypes_He_cap_t *)pmadapter->hw_2g_he_cap;
}
phecap->type = wlan_cpu_to_le16(phecap->type);
phecap->len = wlan_cpu_to_le16(phecap->len);
#if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || \
defined(PCIE9097) || defined(SD9097) || defined(USB9097)
if (IS_CARD9098(pmpriv->adapter->card_type) ||
IS_CARD9097(pmpriv->adapter->card_type)) {
if (pbss_desc->bss_band & BAND_A) {
rx_nss = GET_RXMCSSUPP(pmpriv->adapter->user_htstream >>
8);
tx_nss = GET_TXMCSSUPP(pmpriv->adapter->user_htstream >>
8) &
0x0f;
} else {
rx_nss = GET_RXMCSSUPP(pmpriv->adapter->user_htstream);
tx_nss = GET_TXMCSSUPP(pmpriv->adapter->user_htstream) &
0x0f;
}
/** force 1x1 when enable 80P80 */
if (bw_80p80)
rx_nss = tx_nss = 1;
}
#endif
for (nss = 1; nss <= 8; nss++) {
cfg_value = GET_HE_NSSMCS(phecap->rx_mcs_80, nss);
hw_value = GET_HE_NSSMCS(phw_hecap->rx_mcs_80, nss);
#if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || \
defined(PCIE9097) || defined(SD9097) || defined(USB9097)
if ((rx_nss != 0) && (nss > rx_nss))
cfg_value = NO_NSS_SUPPORT;
#endif
if ((hw_value == NO_NSS_SUPPORT) ||
(cfg_value == NO_NSS_SUPPORT))
SET_HE_NSSMCS(phecap->rx_mcs_80, nss, NO_NSS_SUPPORT);
else
SET_HE_NSSMCS(phecap->rx_mcs_80, nss,
MIN(cfg_value, hw_value));
}
for (nss = 1; nss <= 8; nss++) {
cfg_value = GET_HE_NSSMCS(phecap->tx_mcs_80, nss);
hw_value = GET_HE_NSSMCS(phw_hecap->tx_mcs_80, nss);
#if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || \
defined(PCIE9097) || defined(SD9097) || defined(USB9097)
if ((tx_nss != 0) && (nss > tx_nss))
cfg_value = NO_NSS_SUPPORT;
#endif
if ((hw_value == NO_NSS_SUPPORT) ||
(cfg_value == NO_NSS_SUPPORT))
SET_HE_NSSMCS(phecap->tx_mcs_80, nss, NO_NSS_SUPPORT);
else
SET_HE_NSSMCS(phecap->tx_mcs_80, nss,
MIN(cfg_value, hw_value));
}
PRINTM(MCMND, "Set: HE rx mcs set 0x%08x tx mcs set 0x%08x\n",
phecap->rx_mcs_80, phecap->tx_mcs_80);
if (!bw_80p80) {
/** reset BIT3 and BIT4 channel width ,not support 80 + 80*/
/** not support 160Mhz now, if support,not reset bit3 */
phecap->he_phy_cap[0] &= ~(MBIT(3) | MBIT(4));
}
DBG_HEXDUMP(MCMD_D, "append_11ax_tlv", (t_u8 *)phecap, len);
LEAVE();
return len;
}
/**
* @brief This function save the 11ax cap from FW.
*
* @param pmadapater A pointer to mlan_adapter
* @param hw_he_cap A pointer to MrvlIEtypes_Extension_t
*
* @return N/A
*/
void wlan_update_11ax_cap(mlan_adapter *pmadapter,
MrvlIEtypes_Extension_t *hw_he_cap)
{
MrvlIEtypes_He_cap_t *phe_cap = MNULL;
t_u8 i = 0;
t_u8 he_cap_2g = 0;
ENTER();
if ((hw_he_cap->len + sizeof(MrvlIEtypesHeader_t)) >
sizeof(pmadapter->hw_he_cap)) {
PRINTM(MERROR, "hw_he_cap too big, len=%d\n", hw_he_cap->len);
LEAVE();
return;
}
phe_cap = (MrvlIEtypes_He_cap_t *)hw_he_cap;
if (phe_cap->he_phy_cap[0] &
(AX_2G_40MHZ_SUPPORT | AX_2G_20MHZ_SUPPORT)) {
pmadapter->hw_2g_hecap_len =
hw_he_cap->len + sizeof(MrvlIEtypesHeader_t);
memcpy_ext(pmadapter, pmadapter->hw_2g_he_cap,
(t_u8 *)hw_he_cap,
hw_he_cap->len + sizeof(MrvlIEtypesHeader_t),
sizeof(pmadapter->hw_2g_he_cap));
pmadapter->fw_bands |= BAND_GAX;
pmadapter->config_bands |= BAND_GAX;
he_cap_2g = MTRUE;
DBG_HEXDUMP(MCMD_D, "2.4G HE capability IE ",
(t_u8 *)pmadapter->hw_2g_he_cap,
pmadapter->hw_2g_hecap_len);
} else {
pmadapter->fw_bands |= BAND_AAX;
pmadapter->config_bands |= BAND_AAX;
pmadapter->hw_hecap_len =
hw_he_cap->len + sizeof(MrvlIEtypesHeader_t);
memcpy_ext(pmadapter, pmadapter->hw_he_cap, (t_u8 *)hw_he_cap,
hw_he_cap->len + sizeof(MrvlIEtypesHeader_t),
sizeof(pmadapter->hw_he_cap));
DBG_HEXDUMP(MCMD_D, "5G HE capability IE ",
(t_u8 *)pmadapter->hw_he_cap,
pmadapter->hw_hecap_len);
}
for (i = 0; i < pmadapter->priv_num; i++) {
if (pmadapter->priv[i]) {
pmadapter->priv[i]->config_bands =
pmadapter->config_bands;
if (he_cap_2g) {
pmadapter->priv[i]->user_2g_hecap_len =
pmadapter->hw_2g_hecap_len;
memcpy_ext(pmadapter,
pmadapter->priv[i]->user_2g_he_cap,
pmadapter->hw_2g_he_cap,
pmadapter->hw_2g_hecap_len,
sizeof(pmadapter->priv[i]
->user_2g_he_cap));
} else {
pmadapter->priv[i]->user_hecap_len =
pmadapter->hw_hecap_len;
memcpy_ext(
pmadapter,
pmadapter->priv[i]->user_he_cap,
pmadapter->hw_he_cap,
pmadapter->hw_hecap_len,
sizeof(pmadapter->priv[i]->user_he_cap));
}
}
}
LEAVE();
return;
}
/**
* @brief This function check if 11AX 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_u16 wlan_11ax_bandconfig_allowed(mlan_private *pmpriv, t_u16 bss_band)
{
if (!IS_FW_SUPPORT_11AX(pmpriv->adapter))
return MFALSE;
if (pmpriv->bss_mode == MLAN_BSS_MODE_IBSS) {
if (bss_band & BAND_G)
return (pmpriv->adapter->adhoc_start_band & BAND_GAX);
else if (bss_band & BAND_A)
return (pmpriv->adapter->adhoc_start_band & BAND_AAX);
} else {
if (bss_band & BAND_G)
return (pmpriv->config_bands & BAND_GAX);
else if (bss_band & BAND_A)
return (pmpriv->config_bands & BAND_AAX);
}
return MFALSE;
}
/**
* @brief Set 11ax 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_11ax_ioctl_hecfg(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_11ax_cfg *cfg = MNULL;
t_u16 cmd_action = 0;
ENTER();
if (pioctl_req->buf_len < sizeof(mlan_ds_11ax_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_11ax_cfg);
pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER;
LEAVE();
return MLAN_STATUS_RESOURCE;
}
cfg = (mlan_ds_11ax_cfg *)pioctl_req->pbuf;
if ((cfg->param.he_cfg.band & MBIT(0)) &&
!(pmadapter->fw_bands & BAND_GAX)) {
PRINTM(MERROR, "FW don't support 2.4G AX\n");
return MLAN_STATUS_FAILURE;
}
if ((cfg->param.he_cfg.band & MBIT(1)) &&
!(pmadapter->fw_bands & BAND_AAX)) {
PRINTM(MERROR, "FW don't support 5G AX\n");
return MLAN_STATUS_FAILURE;
}
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_11AX_CFG, cmd_action, 0,
(t_void *)pioctl_req,
(t_void *)&cfg->param.he_cfg);
if (ret == MLAN_STATUS_SUCCESS)
ret = MLAN_STATUS_PENDING;
LEAVE();
return ret;
}
/**
* @brief 11ax 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_11ax_cfg_ioctl(pmlan_adapter pmadapter,
pmlan_ioctl_req pioctl_req)
{
mlan_status status = MLAN_STATUS_SUCCESS;
mlan_ds_11ax_cfg *cfg = MNULL;
ENTER();
cfg = (mlan_ds_11ax_cfg *)pioctl_req->pbuf;
switch (cfg->sub_command) {
case MLAN_OID_11AX_HE_CFG:
status = wlan_11ax_ioctl_hecfg(pmadapter, pioctl_req);
break;
case MLAN_OID_11AX_CMD_CFG:
status = wlan_11ax_ioctl_cmd(pmadapter, pioctl_req);
break;
case MLAN_OID_11AX_TWT_CFG:
status = wlan_11ax_ioctl_twtcfg(pmadapter, pioctl_req);
break;
default:
pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID;
status = MLAN_STATUS_FAILURE;
break;
}
LEAVE();
return status;
}
/**
* @brief This function prepares 11ax 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_11ax_cfg(pmlan_private pmpriv, HostCmd_DS_COMMAND *cmd,
t_u16 cmd_action, t_void *pdata_buf)
{
pmlan_adapter pmadapter = pmpriv->adapter;
HostCmd_DS_11AX_CFG *axcfg = &cmd->params.axcfg;
mlan_ds_11ax_he_cfg *hecfg = (mlan_ds_11ax_he_cfg *)pdata_buf;
MrvlIEtypes_Extension_t *tlv = MNULL;
t_u8 *pos = MNULL;
ENTER();
cmd->command = wlan_cpu_to_le16(HostCmd_CMD_11AX_CFG);
cmd->size = sizeof(HostCmd_DS_11AX_CFG) + S_DS_GEN;
axcfg->action = wlan_cpu_to_le16(cmd_action);
axcfg->band_config = hecfg->band & 0xFF;
pos = (t_u8 *)axcfg->val;
/**HE Capability */
if (hecfg->he_cap.len && (hecfg->he_cap.ext_id == HE_CAPABILITY)) {
tlv = (MrvlIEtypes_Extension_t *)pos;
tlv->type = wlan_cpu_to_le16(hecfg->he_cap.id);
tlv->len = wlan_cpu_to_le16(hecfg->he_cap.len);
memcpy_ext(pmadapter, &tlv->ext_id, &hecfg->he_cap.ext_id,
hecfg->he_cap.len,
MRVDRV_SIZE_OF_CMD_BUFFER - cmd->size);
cmd->size += hecfg->he_cap.len + sizeof(MrvlIEtypesHeader_t);
pos += hecfg->he_cap.len + sizeof(MrvlIEtypesHeader_t);
}
cmd->size = wlan_cpu_to_le16(cmd->size);
LEAVE();
return MLAN_STATUS_SUCCESS;
}
/**
* @brief This function handles the command response of 11axcfg
*
* @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_11ax_cfg(pmlan_private pmpriv, HostCmd_DS_COMMAND *resp,
mlan_ioctl_req *pioctl_buf)
{
pmlan_adapter pmadapter = pmpriv->adapter;
mlan_ds_11ax_cfg *cfg = MNULL;
mlan_ds_11ax_he_capa *hecap = MNULL;
HostCmd_DS_11AX_CFG *axcfg = &resp->params.axcfg;
MrvlIEtypes_Extension_t *tlv = MNULL;
t_u16 left_len = 0, tlv_type = 0, tlv_len = 0;
ENTER();
if (pioctl_buf == MNULL)
goto done;
cfg = (mlan_ds_11ax_cfg *)pioctl_buf->pbuf;
cfg->param.he_cfg.band = axcfg->band_config;
hecap = (mlan_ds_11ax_he_capa *)&cfg->param.he_cfg.he_cap;
/* TLV parse */
left_len = resp->size - sizeof(HostCmd_DS_11AX_CFG) - S_DS_GEN;
tlv = (MrvlIEtypes_Extension_t *)axcfg->val;
while (left_len > sizeof(MrvlIEtypesHeader_t)) {
tlv_type = wlan_le16_to_cpu(tlv->type);
tlv_len = wlan_le16_to_cpu(tlv->len);
if (tlv_type == EXTENSION) {
switch (tlv->ext_id) {
case HE_CAPABILITY:
hecap->id = tlv_type;
hecap->len = tlv_len;
memcpy_ext(pmadapter, (t_u8 *)&hecap->ext_id,
(t_u8 *)&tlv->ext_id, tlv_len,
sizeof(mlan_ds_11ax_he_capa) -
sizeof(MrvlIEtypesHeader_t));
if (cfg->param.he_cfg.band & MBIT(1)) {
memcpy_ext(
pmadapter,
(t_u8 *)&pmpriv->user_he_cap,
(t_u8 *)tlv,
tlv_len +
sizeof(MrvlIEtypesHeader_t),
sizeof(pmpriv->user_he_cap));
pmpriv->user_hecap_len = MIN(
tlv_len +
sizeof(MrvlIEtypesHeader_t),
sizeof(pmpriv->user_he_cap));
PRINTM(MCMND, "user_hecap_len=%d\n",
pmpriv->user_hecap_len);
} else {
memcpy_ext(
pmadapter,
(t_u8 *)&pmpriv->user_2g_he_cap,
(t_u8 *)tlv,
tlv_len +
sizeof(MrvlIEtypesHeader_t),
sizeof(pmpriv->user_2g_he_cap));
pmpriv->user_2g_hecap_len = MIN(
tlv_len +
sizeof(MrvlIEtypesHeader_t),
sizeof(pmpriv->user_2g_he_cap));
PRINTM(MCMND, "user_2g_hecap_len=%d\n",
pmpriv->user_2g_hecap_len);
}
break;
default:
break;
}
}
left_len -= (sizeof(MrvlIEtypesHeader_t) + tlv_len);
tlv = (MrvlIEtypes_Extension_t *)((t_u8 *)tlv + tlv_len +
sizeof(MrvlIEtypesHeader_t));
}
done:
LEAVE();
return MLAN_STATUS_SUCCESS;
}
/**
* @brief 11ax command 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_11ax_ioctl_cmd(pmlan_adapter pmadapter,
pmlan_ioctl_req pioctl_req)
{
mlan_status status = MLAN_STATUS_SUCCESS;
mlan_ds_11ax_cmd_cfg *cfg = MNULL;
mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index];
t_u16 cmd_action = 0;
ENTER();
if (pioctl_req->buf_len < sizeof(mlan_ds_11ax_cmd_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_11ax_cmd_cfg);
pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER;
LEAVE();
return MLAN_STATUS_RESOURCE;
}
cfg = (mlan_ds_11ax_cmd_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 */
status = wlan_prepare_cmd(pmpriv, HostCmd_CMD_11AX_CMD, cmd_action, 0,
(t_void *)pioctl_req, (t_void *)cfg);
if (status == MLAN_STATUS_SUCCESS)
status = MLAN_STATUS_PENDING;
LEAVE();
return status;
}
/**
* @brief This function prepares 11ax 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_11ax_cmd(pmlan_private pmpriv, HostCmd_DS_COMMAND *cmd,
t_u16 cmd_action, t_void *pdata_buf)
{
pmlan_adapter pmadapter = pmpriv->adapter;
HostCmd_DS_11AX_CMD_CFG *axcmd = &cmd->params.axcmd;
mlan_ds_11ax_cmd_cfg *ds_11ax_cmd = (mlan_ds_11ax_cmd_cfg *)pdata_buf;
mlan_ds_11ax_sr_cmd *sr_cmd =
(mlan_ds_11ax_sr_cmd *)&ds_11ax_cmd->param;
mlan_ds_11ax_beam_cmd *beam_cmd =
(mlan_ds_11ax_beam_cmd *)&ds_11ax_cmd->param;
mlan_ds_11ax_htc_cmd *htc_cmd =
(mlan_ds_11ax_htc_cmd *)&ds_11ax_cmd->param;
mlan_ds_11ax_txop_cmd *txop_cmd =
(mlan_ds_11ax_txop_cmd *)&ds_11ax_cmd->param;
mlan_ds_11ax_txomi_cmd *txomi_cmd =
(mlan_ds_11ax_txomi_cmd *)&ds_11ax_cmd->param;
mlan_ds_11ax_toltime_cmd *toltime_cmd =
(mlan_ds_11ax_toltime_cmd *)&ds_11ax_cmd->param;
MrvlIEtypes_Data_t *tlv = MNULL;
ENTER();
cmd->command = wlan_cpu_to_le16(HostCmd_CMD_11AX_CMD);
cmd->size = sizeof(HostCmd_DS_11AX_CMD_CFG) + S_DS_GEN;
axcmd->action = wlan_cpu_to_le16(cmd_action);
axcmd->sub_id = wlan_cpu_to_le16(ds_11ax_cmd->sub_id);
switch (ds_11ax_cmd->sub_id) {
case MLAN_11AXCMD_SR_SUBID:
tlv = (MrvlIEtypes_Data_t *)axcmd->val;
tlv->header.type = wlan_cpu_to_le16(sr_cmd->type);
tlv->header.len = wlan_cpu_to_le16(sr_cmd->len);
memcpy_ext(pmadapter, tlv->data,
&sr_cmd->param.obss_pd_offset.offset, sr_cmd->len,
sr_cmd->len);
cmd->size += sizeof(MrvlIEtypesHeader_t) + sr_cmd->len;
break;
case MLAN_11AXCMD_BEAM_SUBID:
axcmd->val[0] = beam_cmd->value;
cmd->size += sizeof(t_u8);
break;
case MLAN_11AXCMD_HTC_SUBID:
axcmd->val[0] = htc_cmd->value;
cmd->size += sizeof(t_u8);
break;
case MLAN_11AXCMD_TXOPRTS_SUBID:
memcpy_ext(pmadapter, axcmd->val, &txop_cmd->rts_thres,
sizeof(t_u16), sizeof(t_u16));
cmd->size += sizeof(t_u16);
break;
case MLAN_11AXCMD_TXOMI_SUBID:
memcpy_ext(pmadapter, axcmd->val, &txomi_cmd->omi,
sizeof(t_u16), sizeof(t_u16));
cmd->size += sizeof(t_u16);
break;
case MLAN_11AXCMD_OBSS_TOLTIME_SUBID:
memcpy_ext(pmadapter, axcmd->val, &toltime_cmd->tol_time,
sizeof(t_u32), sizeof(t_u32));
cmd->size += sizeof(t_u32);
break;
default:
PRINTM(MERROR, "Unknown subcmd %x\n", ds_11ax_cmd->sub_id);
break;
}
cmd->size = wlan_cpu_to_le16(cmd->size);
LEAVE();
return MLAN_STATUS_SUCCESS;
}
/**
* @brief This function handles the command response of 11axcmd
*
* @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_11ax_cmd(pmlan_private pmpriv, HostCmd_DS_COMMAND *resp,
mlan_ioctl_req *pioctl_buf)
{
pmlan_adapter pmadapter = pmpriv->adapter;
mlan_ds_11ax_cmd_cfg *cfg = MNULL;
HostCmd_DS_11AX_CMD_CFG *axcmd = &resp->params.axcmd;
MrvlIEtypes_Data_t *tlv = MNULL;
t_s16 left_len = 0;
t_u16 tlv_len = 0;
ENTER();
if (pioctl_buf == MNULL)
goto done;
cfg = (mlan_ds_11ax_cmd_cfg *)pioctl_buf->pbuf;
cfg->sub_id = wlan_le16_to_cpu(axcmd->sub_id);
switch (axcmd->sub_id) {
case MLAN_11AXCMD_SR_SUBID:
/* TLV parse */
left_len =
resp->size - sizeof(HostCmd_DS_11AX_CMD_CFG) - S_DS_GEN;
// tlv = (MrvlIEtypes_Extension_t *)axcfg->val;
tlv = (MrvlIEtypes_Data_t *)axcmd->val;
while (left_len > (t_s16)sizeof(MrvlIEtypesHeader_t)) {
tlv_len = wlan_le16_to_cpu(tlv->header.len);
memcpy_ext(
pmadapter,
cfg->param.sr_cfg.param.obss_pd_offset.offset,
tlv->data, tlv_len, tlv_len);
left_len -= (sizeof(MrvlIEtypesHeader_t) + tlv_len);
tlv = (MrvlIEtypes_Data_t
*)((t_u8 *)tlv + tlv_len +
sizeof(MrvlIEtypesHeader_t));
}
break;
case MLAN_11AXCMD_BEAM_SUBID:
cfg->param.beam_cfg.value = *axcmd->val;
break;
case MLAN_11AXCMD_HTC_SUBID:
cfg->param.htc_cfg.value = *axcmd->val;
break;
case MLAN_11AXCMD_TXOPRTS_SUBID:
memcpy_ext(pmadapter, &cfg->param.txop_cfg.rts_thres,
axcmd->val, sizeof(t_u16), sizeof(t_u16));
break;
case MLAN_11AXCMD_TXOMI_SUBID:
memcpy_ext(pmadapter, &cfg->param.txomi_cfg.omi, axcmd->val,
sizeof(t_u16), sizeof(t_u16));
break;
case MLAN_11AXCMD_OBSS_TOLTIME_SUBID:
memcpy_ext(pmadapter, &cfg->param.toltime_cfg.tol_time,
axcmd->val, sizeof(t_u32), sizeof(t_u32));
break;
default:
PRINTM(MERROR, "Unknown subcmd %x\n", axcmd->sub_id);
break;
}
done:
LEAVE();
return MLAN_STATUS_SUCCESS;
}
/**
* @brief This function prepares TWT cfg command to configure
* setup/teardown
*
* @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 Status returned
*/
mlan_status wlan_cmd_twt_cfg(pmlan_private pmpriv, HostCmd_DS_COMMAND *cmd,
t_u16 cmd_action, t_void *pdata_buf)
{
pmlan_adapter pmadapter = pmpriv->adapter;
HostCmd_DS_TWT_CFG *hostcmd_twtcfg =
(HostCmd_DS_TWT_CFG *)&cmd->params.twtcfg;
mlan_ds_twtcfg *ds_twtcfg = (mlan_ds_twtcfg *)pdata_buf;
hostcmd_twt_setup *twt_setup_params = MNULL;
hostcmd_twt_teardown *twt_teardown_params = MNULL;
mlan_status ret = MLAN_STATUS_SUCCESS;
ENTER();
cmd->command = wlan_cpu_to_le16(HostCmd_CMD_TWT_CFG);
hostcmd_twtcfg->action = wlan_cpu_to_le16(cmd_action);
hostcmd_twtcfg->sub_id = wlan_cpu_to_le16(ds_twtcfg->sub_id);
cmd->size = S_DS_GEN + sizeof(hostcmd_twtcfg->action) +
sizeof(hostcmd_twtcfg->sub_id);
switch (hostcmd_twtcfg->sub_id) {
case MLAN_11AX_TWT_SETUP_SUBID:
twt_setup_params = &hostcmd_twtcfg->param.twt_setup;
memset(pmadapter, twt_setup_params, 0x00,
sizeof(hostcmd_twtcfg->param.twt_setup));
twt_setup_params->implicit =
ds_twtcfg->param.twt_setup.implicit;
twt_setup_params->announced =
ds_twtcfg->param.twt_setup.announced;
twt_setup_params->trigger_enabled =
ds_twtcfg->param.twt_setup.trigger_enabled;
twt_setup_params->twt_info_disabled =
ds_twtcfg->param.twt_setup.twt_info_disabled;
twt_setup_params->negotiation_type =
ds_twtcfg->param.twt_setup.negotiation_type;
twt_setup_params->twt_wakeup_duration =
ds_twtcfg->param.twt_setup.twt_wakeup_duration;
twt_setup_params->flow_identifier =
ds_twtcfg->param.twt_setup.flow_identifier;
twt_setup_params->hard_constraint =
ds_twtcfg->param.twt_setup.hard_constraint;
twt_setup_params->twt_exponent =
ds_twtcfg->param.twt_setup.twt_exponent;
twt_setup_params->twt_mantissa = wlan_cpu_to_le16(
ds_twtcfg->param.twt_setup.twt_mantissa);
twt_setup_params->twt_request =
ds_twtcfg->param.twt_setup.twt_request;
cmd->size += sizeof(hostcmd_twtcfg->param.twt_setup);
break;
case MLAN_11AX_TWT_TEARDOWN_SUBID:
twt_teardown_params = &hostcmd_twtcfg->param.twt_teardown;
memset(pmadapter, twt_teardown_params, 0x00,
sizeof(hostcmd_twtcfg->param.twt_teardown));
twt_teardown_params->flow_identifier =
ds_twtcfg->param.twt_teardown.flow_identifier;
twt_teardown_params->negotiation_type =
ds_twtcfg->param.twt_teardown.negotiation_type;
twt_teardown_params->teardown_all_twt =
ds_twtcfg->param.twt_teardown.teardown_all_twt;
cmd->size += sizeof(hostcmd_twtcfg->param.twt_teardown);
break;
default:
PRINTM(MERROR, "Unknown subcmd %x\n", ds_twtcfg->sub_id);
ret = MLAN_STATUS_FAILURE;
break;
}
cmd->size = wlan_cpu_to_le16(cmd->size);
LEAVE();
return ret;
}
/**
* @brief TWT config command 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_11ax_ioctl_twtcfg(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_twtcfg *cfg = MNULL;
t_u16 cmd_action = 0;
ENTER();
if (pioctl_req->buf_len < sizeof(mlan_ds_twtcfg)) {
PRINTM(MERROR, "MLAN bss IOCTL length is too short.\n");
pioctl_req->data_read_written = 0;
pioctl_req->buf_len_needed = sizeof(mlan_ds_twtcfg);
pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER;
LEAVE();
return MLAN_STATUS_RESOURCE;
}
cfg = (mlan_ds_twtcfg *)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_TWT_CFG, cmd_action, 0,
(t_void *)pioctl_req, (t_void *)cfg);
if (ret == MLAN_STATUS_SUCCESS)
ret = MLAN_STATUS_PENDING;
LEAVE();
return ret;
}