mwifiex/mxm_wifiex/wlan_src/mlan/mlan_11ac.c
Sherry Sun 8ffae47921 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-12 12:16:50 +08:00

1449 lines
43 KiB
C

/** @file mlan_11ac.c
*
* @brief This file contains the functions for station ioctl.
*
*
* Copyright 2011-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_fw.h"
#include "mlan_main.h"
#include "mlan_wmm.h"
#include "mlan_11n.h"
#include "mlan_11ac.h"
/********************************************************
Local Variables
********************************************************/
/********************************************************
Global Variables
********************************************************/
/********************************************************
Local Functions
********************************************************/
t_u16 wlan_convert_mcsmap_to_maxrate(mlan_private *priv, t_u16 bands,
t_u16 mcs_map);
/**
* @brief determine the center frquency center index for bandwidth
* of 80 MHz and 160 MHz
*
* @param pmpriv A pointer to mlan_private structure
* @param band band
* @param pri_chan primary channel
* @param chan_bw channel bandwidth
*
* @return channel center frequency center, if found; O, otherwise
*/
t_u8 wlan_get_center_freq_idx(mlan_private *pmpriv, t_u16 band, t_u32 pri_chan,
t_u8 chan_bw)
{
t_u8 center_freq_idx = 0;
if (band & BAND_AAC) {
switch (pri_chan) {
case 36:
case 40:
case 44:
case 48:
if (chan_bw == CHANNEL_BW_80MHZ) {
center_freq_idx = 42;
break;
}
/* fall through */
case 52:
case 56:
case 60:
case 64:
if (chan_bw == CHANNEL_BW_80MHZ) {
center_freq_idx = 58;
break;
} else if (chan_bw == CHANNEL_BW_160MHZ) {
center_freq_idx = 50;
break;
}
/* fall through */
case 100:
case 104:
case 108:
case 112:
if (chan_bw == CHANNEL_BW_80MHZ) {
center_freq_idx = 106;
break;
}
/* fall through */
case 116:
case 120:
case 124:
case 128:
if (chan_bw == CHANNEL_BW_80MHZ) {
center_freq_idx = 122;
break;
} else if (chan_bw == CHANNEL_BW_160MHZ) {
center_freq_idx = 114;
break;
}
/* fall through */
case 132:
case 136:
case 140:
case 144:
if (chan_bw == CHANNEL_BW_80MHZ) {
center_freq_idx = 138;
break;
}
/* fall through */
case 149:
case 153:
case 157:
case 161:
if (chan_bw == CHANNEL_BW_80MHZ) {
center_freq_idx = 155;
break;
}
/* fall through */
case 165:
case 169:
case 173:
case 177:
if (chan_bw == CHANNEL_BW_80MHZ) {
center_freq_idx = 171;
break;
}
/* fall through */
case 184:
case 188:
case 192:
case 196:
if (chan_bw == CHANNEL_BW_80MHZ) {
center_freq_idx = 190;
break;
}
/* fall through */
default: /* error. go to the default */
center_freq_idx = 42;
}
}
return center_freq_idx;
}
/**
* @brief This function gets the bitmap of nss which supports VHT mcs
*
* @param mcs_map_set VHT mcs map
*
* @return The bitmap of supported nss
*/
static t_u8 wlan_get_nss_vht_mcs(t_u16 mcs_map_set)
{
t_u8 nss, nss_map = 0;
for (nss = 1; nss <= 8; nss++) {
if (GET_VHTNSSMCS(mcs_map_set, nss) != NO_NSS_SUPPORT)
nss_map |= 1 << (nss - 1);
}
PRINTM(MCMND, "Supported nss bit map:0x%02x\n", nss_map);
return nss_map;
}
/**
* @brief This function gets the bitmap of nss which supports VHT mcs
*
* @param mcs_map_set VHT mcs map
*
* @return The bitmap of supported nss
*/
static t_u8 wlan_get_nss_num_vht_mcs(t_u16 mcs_map_set)
{
t_u8 nss, nss_num = 0;
for (nss = 1; nss <= 8; nss++) {
if (GET_VHTNSSMCS(mcs_map_set, nss) != NO_NSS_SUPPORT)
nss_num++;
}
PRINTM(MCMND, "Supported nss:%d\n", nss_num);
return nss_num;
}
/**
* @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, VHT_capa_t *vht_cap,
t_u8 bands)
{
t_u32 usr_dot_11ac_dev_cap;
ENTER();
if (bands & BAND_A)
usr_dot_11ac_dev_cap = priv->usr_dot_11ac_dev_cap_a;
else
usr_dot_11ac_dev_cap = priv->usr_dot_11ac_dev_cap_bg;
vht_cap->vht_cap_info = usr_dot_11ac_dev_cap;
RESET_VHTCAP_MAXMPDULEN(vht_cap->vht_cap_info);
LEAVE();
}
/**
* @brief Set/get 11ac 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_11ac_ioctl_vhtcfg(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_11ac_cfg *cfg = MNULL;
t_u16 cmd_action = 0;
t_u32 usr_vht_cap_info = 0;
t_u32 cfg_value = 0;
t_u32 hw_value = 0;
t_u8 nss = 0;
#if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || \
defined(PCIE9097) || defined(USB9097) || defined(SD9097)
t_u16 rx_nss = 0;
t_u16 tx_nss = 0;
#endif
ENTER();
#define VHT_CAP_INFO_BIT_FIELDS \
(MBIT(4) | MBIT(5) | MBIT(6) | MBIT(7) | MBIT(11) | MBIT(12) | \
MBIT(19) | MBIT(20) | MBIT(21) | MBIT(22) | MBIT(28) | MBIT(29))
cfg = (mlan_ds_11ac_cfg *)pioctl_req->pbuf;
if (pioctl_req->action == MLAN_ACT_SET) {
/** SET operation */
/** validate the user input and correct it if necessary */
if (pmpriv->bss_role == MLAN_BSS_ROLE_STA) {
if (cfg->param.vht_cfg.txrx == 3) {
PRINTM(MERROR,
"Configuration of VHT capabilities for TX/RX 3 is not supported in STA mode!\n");
return MLAN_STATUS_FAILURE;
}
}
if (pmpriv->bss_role == MLAN_BSS_ROLE_UAP) {
if (cfg->param.vht_cfg.txrx != 3) {
PRINTM(MERROR,
"Configuration of VHT capabilities for TX/RX %d is not supported in UAP mode!\n",
cfg->param.vht_cfg.txrx);
return MLAN_STATUS_FAILURE;
}
}
/** set bit fileds */
usr_vht_cap_info = VHT_CAP_INFO_BIT_FIELDS &
cfg->param.vht_cfg.vht_cap_info &
pmadapter->hw_dot_11ac_dev_cap;
/** set MAX MPDU LEN field (bit 0 - bit 1) */
cfg_value =
GET_VHTCAP_MAXMPDULEN(cfg->param.vht_cfg.vht_cap_info);
hw_value =
GET_VHTCAP_MAXMPDULEN(pmadapter->hw_dot_11ac_dev_cap);
SET_VHTCAP_MAXMPDULEN(usr_vht_cap_info,
MIN(cfg_value, hw_value));
/** set CHAN Width Set field (bit 2 - bit 3) */
cfg_value = GET_VHTCAP_CHWDSET(cfg->param.vht_cfg.vht_cap_info);
hw_value = GET_VHTCAP_CHWDSET(pmadapter->hw_dot_11ac_dev_cap);
SET_VHTCAP_CHWDSET(usr_vht_cap_info, MIN(cfg_value, hw_value));
/** set Rx STBC field (bit 8 - bit 10) */
cfg_value = GET_VHTCAP_RXSTBC(cfg->param.vht_cfg.vht_cap_info);
hw_value = GET_VHTCAP_RXSTBC(pmadapter->hw_dot_11ac_dev_cap);
SET_VHTCAP_RXSTBC(usr_vht_cap_info, MIN(cfg_value, hw_value));
/** set Steering Number of BFer Ant (bit 13 - bit 15) */
cfg_value =
GET_VHTCAP_SNBFERANT(cfg->param.vht_cfg.vht_cap_info);
hw_value = GET_VHTCAP_SNBFERANT(pmadapter->hw_dot_11ac_dev_cap);
SET_VHTCAP_SNBFERANT(usr_vht_cap_info,
MIN(cfg_value, hw_value));
/** set Number of Sounding Dimension (bit 16 - bit 18) */
cfg_value =
GET_VHTCAP_NUMSNDDM(cfg->param.vht_cfg.vht_cap_info);
hw_value = GET_VHTCAP_NUMSNDDM(pmadapter->hw_dot_11ac_dev_cap);
SET_VHTCAP_NUMSNDDM(usr_vht_cap_info, MIN(cfg_value, hw_value));
/** set Number of Max AMPDU Length Exponent (bit 23 - bit 25) */
cfg_value = GET_VHTCAP_MAXAMPDULENEXP(
cfg->param.vht_cfg.vht_cap_info);
hw_value = GET_VHTCAP_MAXAMPDULENEXP(
pmadapter->hw_dot_11ac_dev_cap);
SET_VHTCAP_MAXAMPDULENEXP(usr_vht_cap_info,
MIN(cfg_value, hw_value));
/** set VHT Link Adaptation Capable (bit 26 - bit 27) */
cfg_value =
GET_VHTCAP_LINKADPCAP(cfg->param.vht_cfg.vht_cap_info);
hw_value =
GET_VHTCAP_LINKADPCAP(pmadapter->hw_dot_11ac_dev_cap);
SET_VHTCAP_LINKADPCAP(usr_vht_cap_info,
MIN(cfg_value, hw_value));
/** update the user setting if it is beyond the hw capabiliteis
*/
cfg->param.vht_cfg.vht_cap_info = usr_vht_cap_info;
PRINTM(MINFO, "Set: vht cap info 0x%x\n", usr_vht_cap_info);
/** update the RX MCS map */
if (cfg->param.vht_cfg.txrx & MLAN_RADIO_RX) {
#if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || \
defined(PCIE9097) || defined(SD9097) || defined(USB9097)
if (IS_CARD9098(pmadapter->card_type) ||
IS_CARD9097(pmadapter->card_type)) {
if (cfg->param.vht_cfg.band == BAND_SELECT_A) {
rx_nss = GET_RXMCSSUPP(
pmadapter->user_htstream >> 8);
tx_nss =
GET_TXMCSSUPP(
pmadapter->user_htstream >>
8) &
0x0f;
} else {
rx_nss = GET_RXMCSSUPP(
pmadapter->user_htstream);
tx_nss =
GET_TXMCSSUPP(
pmadapter->user_htstream) &
0x0f;
}
}
#endif
/* use the previous user value */
if (cfg->param.vht_cfg.vht_rx_mcs == 0xffffffff)
cfg->param.vht_cfg.vht_rx_mcs = GET_VHTMCS(
pmpriv->usr_dot_11ac_mcs_support);
for (nss = 1; nss <= 8; nss++) {
cfg_value = GET_VHTNSSMCS(
cfg->param.vht_cfg.vht_rx_mcs, nss);
hw_value = GET_DEVNSSRXMCS(
pmadapter->hw_dot_11ac_mcs_support,
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_VHTNSSMCS(
cfg->param.vht_cfg.vht_rx_mcs,
nss, NO_NSS_SUPPORT);
else
SET_VHTNSSMCS(
cfg->param.vht_cfg.vht_rx_mcs,
nss, MIN(cfg_value, hw_value));
}
PRINTM(MINFO, "Set: vht rx mcs set 0x%08x\n",
cfg->param.vht_cfg.vht_rx_mcs);
/* use the previous user value */
if (cfg->param.vht_cfg.vht_tx_mcs == 0xffffffff)
cfg->param.vht_cfg.vht_tx_mcs = GET_VHTMCS(
pmpriv->usr_dot_11ac_mcs_support >> 16);
for (nss = 1; nss <= 8; nss++) {
cfg_value = GET_VHTNSSMCS(
cfg->param.vht_cfg.vht_tx_mcs, nss);
hw_value = GET_DEVNSSTXMCS(
pmadapter->hw_dot_11ac_mcs_support,
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_VHTNSSMCS(
cfg->param.vht_cfg.vht_tx_mcs,
nss, NO_NSS_SUPPORT);
else
SET_VHTNSSMCS(
cfg->param.vht_cfg.vht_tx_mcs,
nss, MIN(cfg_value, hw_value));
}
PRINTM(MINFO, "Set: vht tx mcs set 0x%08x\n",
cfg->param.vht_cfg.vht_tx_mcs);
if (!cfg->param.vht_cfg.skip_usr_11ac_mcs_cfg) {
RESET_DEVRXMCSMAP(
pmpriv->usr_dot_11ac_mcs_support);
pmpriv->usr_dot_11ac_mcs_support |= GET_VHTMCS(
cfg->param.vht_cfg.vht_rx_mcs);
RESET_DEVTXMCSMAP(
pmpriv->usr_dot_11ac_mcs_support);
pmpriv->usr_dot_11ac_mcs_support |=
(GET_VHTMCS(
cfg->param.vht_cfg.vht_tx_mcs)
<< 16);
PRINTM(MINFO, "Set: vht mcs set 0x%08x\n",
pmpriv->usr_dot_11ac_mcs_support);
} else {
PRINTM(MINFO,
"Skipped user 11ac mcs configuration\n");
cfg->param.vht_cfg.skip_usr_11ac_mcs_cfg =
MFALSE;
}
}
}
if (pmpriv->bss_role == MLAN_BSS_ROLE_STA) {
if (cfg->param.vht_cfg.txrx & MLAN_RADIO_RX) {
/* maximum VHT configuration used in association */
if (pioctl_req->action == MLAN_ACT_SET) {
if (cfg->param.vht_cfg.band == BAND_SELECT_BG)
pmpriv->usr_dot_11ac_dev_cap_bg =
usr_vht_cap_info;
else if (cfg->param.vht_cfg.band ==
BAND_SELECT_A)
pmpriv->usr_dot_11ac_dev_cap_a =
usr_vht_cap_info;
else {
pmpriv->usr_dot_11ac_dev_cap_bg =
usr_vht_cap_info;
pmpriv->usr_dot_11ac_dev_cap_a =
usr_vht_cap_info;
}
pmpriv->usr_dot_11ac_bw =
cfg->param.vht_cfg.bwcfg;
} else {
/** GET operation */
if (cfg->param.vht_cfg.band == BAND_SELECT_BG) {
cfg->param.vht_cfg.vht_cap_info =
pmpriv->usr_dot_11ac_dev_cap_bg;
PRINTM(MINFO,
"Get: vht cap info for 2.4GHz 0x%x\n",
pmpriv->usr_dot_11ac_dev_cap_bg);
} else if (cfg->param.vht_cfg.band ==
BAND_SELECT_A) {
cfg->param.vht_cfg.vht_cap_info =
pmpriv->usr_dot_11ac_dev_cap_a;
PRINTM(MINFO,
"Get: vht cap info for 5GHz 0x%x\n",
pmpriv->usr_dot_11ac_dev_cap_a);
} else {
PRINTM(MINFO,
"Get: invalid band selection for vht cap info\n");
ret = MLAN_STATUS_FAILURE;
}
cfg->param.vht_cfg.bwcfg =
pmpriv->usr_dot_11ac_bw;
cfg->param.vht_cfg.vht_rx_mcs = GET_DEVRXMCSMAP(
pmpriv->usr_dot_11ac_mcs_support);
cfg->param.vht_cfg.vht_tx_mcs = GET_DEVTXMCSMAP(
pmpriv->usr_dot_11ac_mcs_support);
cfg->param.vht_cfg.vht_rx_max_rate =
wlan_convert_mcsmap_to_maxrate(
pmpriv, cfg->param.vht_cfg.band,
cfg->param.vht_cfg.vht_rx_mcs);
cfg->param.vht_cfg.vht_tx_max_rate =
wlan_convert_mcsmap_to_maxrate(
pmpriv, cfg->param.vht_cfg.band,
cfg->param.vht_cfg.vht_tx_mcs);
}
LEAVE();
return ret;
}
}
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_11AC_CFG, cmd_action, 0,
(t_void *)pioctl_req,
(t_void *)&cfg->param.vht_cfg);
if (ret == MLAN_STATUS_SUCCESS)
ret = MLAN_STATUS_PENDING;
LEAVE();
return ret;
}
/**
* @brief Get/Set Operating Mode Notification cfg
*
* @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_11ac_ioctl_opermodecfg(pmlan_adapter pmadapter,
pmlan_ioctl_req pioctl_req)
{
mlan_ds_11ac_cfg *cfg = MNULL;
mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index];
t_u8 hw_bw_160or8080 = 0;
t_u8 hw_rx_nss = 0;
ENTER();
cfg = (mlan_ds_11ac_cfg *)pioctl_req->pbuf;
if (pioctl_req->action == MLAN_ACT_GET) {
cfg->param.opermode_cfg.bw = pmpriv->usr_dot_11ac_opermode_bw;
cfg->param.opermode_cfg.nss = pmpriv->usr_dot_11ac_opermode_nss;
} else if (pioctl_req->action == MLAN_ACT_SET) {
hw_bw_160or8080 =
GET_VHTCAP_CHWDSET(pmadapter->hw_dot_11ac_dev_cap);
hw_rx_nss = wlan_get_nss_num_vht_mcs(
GET_DEVRXMCSMAP(pmadapter->hw_dot_11ac_mcs_support));
if ((((cfg->param.opermode_cfg.bw - 1) > BW_80MHZ) &&
!hw_bw_160or8080) ||
(cfg->param.opermode_cfg.nss > hw_rx_nss)) {
PRINTM(MERROR,
"bw or nss NOT supported. HW support bw_160or8080=%d rx_nss=%d.\n",
hw_bw_160or8080, hw_rx_nss);
LEAVE();
return MLAN_STATUS_FAILURE;
}
pmpriv->usr_dot_11ac_opermode_bw = cfg->param.opermode_cfg.bw;
pmpriv->usr_dot_11ac_opermode_nss = cfg->param.opermode_cfg.nss;
}
LEAVE();
return MLAN_STATUS_SUCCESS;
}
/**
* @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_11ac_ioctl_supported_mcs_set(pmlan_adapter pmadapter,
pmlan_ioctl_req pioctl_req)
{
/*mlan_ds_11ac_cfg *cfg= MNULL;*/
/*int rx_mcs_supp;*/
/*t_u8 mcs_set[NUM_MCS_SUPP];*/
ENTER();
#if 0
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_11ACRXMCSSUPP(pmadapter->usr_dot_11ac_mcs_support);
/* Set MCS */
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_CHANWIDTH80(pmadapter->usr_dot_11ac_dev_cap_bg)
|| ISSUPP_CHANWIDTH80(pmadapter->usr_dot_11ac_dev_cap_a)
)
SETHT_MCS32(mcs_set);
cfg = (mlan_ds_11ac_cfg *)pioctl_req->pbuf;
memcpy_ext(pmadapter, cfg->param.supported_mcs_set, mcs_set,
NUM_MCS_SUPP, sizeof(cfg->param.supported_mcs_set));
#endif
LEAVE();
return MLAN_STATUS_SUCCESS;
}
/********************************************************
Global Functions
********************************************************/
/**
* @brief This function prints the 802.11ac device capability
*
* @param pmadapter A pointer to mlan_adapter structure
* @param cap Capability value
*
* @return N/A
*/
void wlan_show_dot11acdevcap(pmlan_adapter pmadapter, t_u32 cap)
{
ENTER();
switch (GET_VHTCAP_MAXMPDULEN(cap)) {
case 0x0:
PRINTM(MINFO,
"GET_HW_SPEC: Maximum MPDU length = 3895 octets\n");
break;
case 0x1:
PRINTM(MINFO,
"GET_HW_SPEC: Maximum MPDU length = 7991 octets\n");
break;
case 0x2:
PRINTM(MINFO,
"GET_HW_SPEC: Maximum MPDU length = 11454 octets\n");
break;
default:
PRINTM(MINFO, "Unsupport value\n");
break;
}
PRINTM(MINFO, "GET_HW_SPEC: HTC-VHT %s\n",
(ISSUPP_11ACVHTHTCVHT(cap) ? "supported" : "not supported"));
PRINTM(MINFO, "GET_HW_SPEC: VHT TXOP PS %s\n",
(ISSUPP_11ACVHTTXOPPS(cap) ? "supported" : "not supported"));
PRINTM(MINFO, "GET_HW_SPEC: MU RX beamformee %s\n",
(ISSUPP_11ACMURXBEAMFORMEE(cap) ? "supported" :
"not supported"));
PRINTM(MINFO, "GET_HW_SPEC: MU TX beamformee %s\n",
(ISSUPP_11ACMUTXBEAMFORMEE(cap) ? "supported" :
"not supported"));
PRINTM(MINFO, "GET_HW_SPEC: SU RX Beamformee %s\n",
(ISSUPP_11ACSUBEAMFORMEE(cap) ? "supported" : "not supported"));
PRINTM(MINFO, "GET_HW_SPEC: SU TX Beamformer %s\n",
(ISSUPP_11ACSUBEAMFORMER(cap) ? "supported" : "not supported"));
PRINTM(MINFO, "GET_HW_SPEC: Rx STBC %s\n",
(ISSUPP_11ACRXSTBC(cap) ? "supported" : "not supported"));
PRINTM(MINFO, "GET_HW_SPEC: Tx STBC %s\n",
(ISSUPP_11ACTXSTBC(cap) ? "supported" : "not supported"));
PRINTM(MINFO, "GET_HW_SPEC: Short GI %s for 160MHz BW\n",
(ISSUPP_11ACSGI160(cap) ? "supported" : "not supported"));
PRINTM(MINFO, "GET_HW_SPEC: Short GI %s for 80MHz BW\n",
(ISSUPP_11ACSGI80(cap) ? "supported" : "not supported"));
PRINTM(MINFO, "GET_HW_SPEC: LDPC coding %s\n",
(ISSUPP_11ACLDPC(cap) ? "supported" : "not supported"));
PRINTM(MINFO, "GET_HW_SPEC: Channel BW 20/40/80/160/80+80 MHz %s\n",
(ISSUPP_11ACBW8080(cap) ? "supported" : "not supported"));
PRINTM(MINFO, "GET_HW_SPEC: Channel BW 20/40/80/160 MHz %s\n",
(ISSUPP_11ACBW160(cap) ? "supported" : "not supported"));
LEAVE();
return;
}
/**
* @brief This function prints the 802.11ac device MCS
*
* @param pmadapter A pointer to mlan_adapter structure
* @param support Support value
*
* @return N/A
*/
void wlan_show_dot11acmcssupport(pmlan_adapter pmadapter, t_u32 support)
{
ENTER();
PRINTM(MINFO, "GET_HW_SPEC: MCSs for %2dx%2d MIMO\n",
GET_DEVRXMCSMAP(support), GET_DEVTXMCSMAP(support));
LEAVE();
return;
}
/**
* @brief This function converts the 2-bit MCS map to the highest long GI
* VHT PPDU data rate
*
* @param priv A pointer to mlan_private structure
* @param bands Supported bands
* @param mcs_map 2-bit MCS map
*
* @return the max data rate for long GI
*/
t_u16 wlan_convert_mcsmap_to_maxrate(mlan_private *priv, t_u16 bands,
t_u16 mcs_map)
{
t_u8 i;
t_u8 nss;
t_u8 max_mcs;
t_u16 max_rate = 0;
t_u32 usr_vht_cap_info = 0;
t_u32 usr_dot_11n_dev_cap;
/* tables of the MCS map to the highest data rate (in Mbps)
* supported for long GI */
t_u16 max_rate_lgi_20MHZ[8][3] = {
{0x41, 0x4E, 0x0}, /* NSS = 1 */
{0x82, 0x9C, 0x0}, /* NSS = 2 */
{0xC3, 0xEA, 0x104}, /* NSS = 3 */
{0x104, 0x138, 0x0}, /* NSS = 4 */
{0x145, 0x186, 0x0}, /* NSS = 5 */
{0x186, 0x1D4, 0x208}, /* NSS = 6 */
{0x1C7, 0x222, 0x0}, /* NSS = 7 */
{0x208, 0x270, 0x0} /* NSS = 8 */
};
t_u16 max_rate_lgi_40MHZ[8][3] = {
{0x87, 0xA2, 0xB4}, /* NSS = 1 */
{0x10E, 0x144, 0x168}, /* NSS = 2 */
{0x195, 0x1E6, 0x21C}, /* NSS = 3 */
{0x21C, 0x288, 0x2D0}, /* NSS = 4 */
{0x2A3, 0x32A, 0x384}, /* NSS = 5 */
{0x32A, 0x3CC, 0x438}, /* NSS = 6 */
{0x3B1, 0x46E, 0x4EC}, /* NSS = 7 */
{0x438, 0x510, 0x5A0} /* NSS = 8 */
};
t_u16 max_rate_lgi_80MHZ[8][3] = {
{0x124, 0x15F, 0x186}, /* NSS = 1 */
{0x249, 0x2BE, 0x30C}, /* NSS = 2 */
{0x36D, 0x41D, 0x492}, /* NSS = 3 */
{0x492, 0x57C, 0x618}, /* NSS = 4 */
{0x5B6, 0x6DB, 0x79E}, /* NSS = 5 */
{0x6DB, 0x83A, 0x0}, /* NSS = 6 */
{0x7FF, 0x999, 0xAAA}, /* NSS = 7 */
{0x924, 0xAF8, 0xC30} /* NSS = 8 */
};
t_u16 max_rate_lgi_160MHZ[8][3] = {
{0x249, 0x2BE, 0x30C}, /* NSS = 1 */
{0x492, 0x57C, 0x618}, /* NSS = 2 */
{0x6DB, 0x83A, 0x0}, /* NSS = 3 */
{0x924, 0xAF8, 0xC30}, /* NSS = 4 */
{0xB6D, 0xDB6, 0xF3C}, /* NSS = 5 */
{0xDB6, 0x1074, 0x1248}, /* NSS = 6 */
{0xFFF, 0x1332, 0x1554}, /* NSS = 7 */
{0x1248, 0x15F0, 0x1860} /* NSS = 8 */
};
if (bands & BAND_AAC) {
usr_vht_cap_info = priv->usr_dot_11ac_dev_cap_a;
usr_dot_11n_dev_cap = priv->usr_dot_11n_dev_cap_a;
} else {
usr_vht_cap_info = priv->usr_dot_11ac_dev_cap_bg;
usr_dot_11n_dev_cap = priv->usr_dot_11n_dev_cap_bg;
}
/* find the max NSS supported */
nss = 0;
for (i = 0; i < 8; i++) {
max_mcs = (mcs_map >> (2 * i)) & 0x3;
if (max_mcs < 3)
nss = i;
}
max_mcs = (mcs_map >> (2 * nss)) & 0x3;
/* if max_mcs is 3, nss must be 0 (SS = 1). Thus, max mcs is MCS 9*/
if (max_mcs >= 3)
max_mcs = 2;
if (GET_VHTCAP_CHWDSET(usr_vht_cap_info)) {
/* support 160 MHz */
max_rate = max_rate_lgi_160MHZ[nss][max_mcs];
if (max_mcs >= 1 && max_rate == 0)
/* MCS9 is not supported in NSS6 */
max_rate = max_rate_lgi_160MHZ[nss][max_mcs - 1];
} else {
if (priv->usr_dot_11ac_bw == BW_FOLLOW_VHTCAP) {
max_rate = max_rate_lgi_80MHZ[nss][max_mcs];
if (max_mcs >= 1 && max_rate == 0)
/* MCS9 is not supported in NSS3 */
max_rate = max_rate_lgi_80MHZ[nss][max_mcs - 1];
} else {
if (ISSUPP_CHANWIDTH40(usr_dot_11n_dev_cap)) {
max_rate = max_rate_lgi_40MHZ[nss][max_mcs];
} else {
max_rate = max_rate_lgi_20MHZ[nss][max_mcs];
/* MCS9 is not supported in NSS1/2/4/5/7/8 */
if (max_mcs >= 1 && max_rate == 0)
max_rate =
max_rate_lgi_20MHZ[nss]
[max_mcs - 1];
}
}
}
PRINTM(MCMND, "max_rate=%dM\n", max_rate);
return max_rate;
}
/**
* @brief This function fills the VHT cap tlv out put format is LE, not CPU
*
* @param priv A pointer to mlan_private structure
* @param pvht_cap A pointer to MrvlIETypes_HTCap_t structure
* @param bands Band configuration
* @param flag TREU--pvht_cap has the setting for resp
* MFALSE -- pvht_cap is clean
* @param bw_80p80 TRUE -- enable 80p80
* @return N/A
*/
void wlan_fill_vht_cap_tlv(mlan_private *priv, MrvlIETypes_VHTCap_t *pvht_cap,
t_u16 bands, t_u8 flag, t_u8 bw_80p80)
{
t_u16 mcs_map_user = 0;
t_u16 mcs_map_resp = 0;
t_u16 mcs_map_result = 0;
t_u16 mcs_user = 0;
t_u16 mcs_resp = 0;
t_u16 nss;
#if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || \
defined(PCIE9097) || defined(SD9097) || defined(USB9097)
t_u16 rx_nss = 0, tx_nss = 0;
#endif
ENTER();
/* Fill VHT cap info */
wlan_fill_cap_info(priv, &pvht_cap->vht_cap, bands);
/* clear 80p80 in vht_cap_info */
if (!bw_80p80)
pvht_cap->vht_cap.vht_cap_info &= ~(MBIT(2) | MBIT(3));
pvht_cap->vht_cap.vht_cap_info =
wlan_cpu_to_le32(pvht_cap->vht_cap.vht_cap_info);
/* Fill VHT MCS Set */
/* rx MCS Set, find the minimum of the user rx mcs and ap rx mcs*/
mcs_map_resp = mcs_map_user =
GET_DEVRXMCSMAP(priv->usr_dot_11ac_mcs_support);
if (flag)
mcs_map_resp =
wlan_le16_to_cpu(pvht_cap->vht_cap.mcs_sets.rx_mcs_map);
#if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || \
defined(PCIE9097) || defined(SD9097) || defined(USB9097)
if (IS_CARD9098(priv->adapter->card_type) ||
IS_CARD9097(priv->adapter->card_type)) {
if (bands & BAND_A) {
rx_nss = GET_RXMCSSUPP(priv->adapter->user_htstream >>
8);
tx_nss = GET_TXMCSSUPP(priv->adapter->user_htstream >>
8) &
0x0f;
} else {
rx_nss = GET_RXMCSSUPP(priv->adapter->user_htstream);
tx_nss = GET_TXMCSSUPP(priv->adapter->user_htstream) &
0x0f;
}
/** force 1x1 when enable 80P80 */
if (bw_80p80)
rx_nss = tx_nss = 1;
}
#endif
mcs_map_result = 0;
for (nss = 1; nss <= 8; nss++) {
mcs_user = GET_VHTNSSMCS(mcs_map_user, nss);
mcs_resp = GET_VHTNSSMCS(mcs_map_resp, nss);
#if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || \
defined(PCIE9097) || defined(SD9097) || defined(USB9097)
if ((rx_nss != 0) && (nss > rx_nss))
mcs_user = NO_NSS_SUPPORT;
#endif
if ((mcs_user == NO_NSS_SUPPORT) ||
(mcs_resp == NO_NSS_SUPPORT))
SET_VHTNSSMCS(mcs_map_result, nss, NO_NSS_SUPPORT);
else
SET_VHTNSSMCS(mcs_map_result, nss,
MIN(mcs_user, mcs_resp));
}
/* rx MCS map */
pvht_cap->vht_cap.mcs_sets.rx_mcs_map =
wlan_cpu_to_le16(mcs_map_result);
/* rx highest rate */
pvht_cap->vht_cap.mcs_sets.rx_max_rate =
wlan_convert_mcsmap_to_maxrate(priv, bands, mcs_map_result);
pvht_cap->vht_cap.mcs_sets.rx_max_rate =
wlan_cpu_to_le16(pvht_cap->vht_cap.mcs_sets.rx_max_rate);
/* tx MCS Set find the minimum of the user tx mcs and ap tx mcs */
mcs_map_resp = mcs_map_user =
GET_DEVTXMCSMAP(priv->usr_dot_11ac_mcs_support);
if (flag)
mcs_map_resp =
wlan_le16_to_cpu(pvht_cap->vht_cap.mcs_sets.tx_mcs_map);
mcs_map_result = 0;
for (nss = 1; nss <= 8; nss++) {
mcs_user = GET_VHTNSSMCS(mcs_map_user, nss);
mcs_resp = GET_VHTNSSMCS(mcs_map_resp, nss);
#if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || \
defined(PCIE9097) || defined(SD9097) || defined(USB9097)
if ((tx_nss != 0) && (nss > tx_nss))
mcs_user = NO_NSS_SUPPORT;
#endif
if ((mcs_user == NO_NSS_SUPPORT) ||
(mcs_resp == NO_NSS_SUPPORT))
SET_VHTNSSMCS(mcs_map_result, nss, NO_NSS_SUPPORT);
else
SET_VHTNSSMCS(mcs_map_result, nss,
MIN(mcs_user, mcs_resp));
}
/* tx MCS map */
pvht_cap->vht_cap.mcs_sets.tx_mcs_map =
wlan_cpu_to_le16(mcs_map_result);
/* tx highest rate */
pvht_cap->vht_cap.mcs_sets.tx_max_rate =
wlan_convert_mcsmap_to_maxrate(priv, bands, mcs_map_result);
pvht_cap->vht_cap.mcs_sets.tx_max_rate =
wlan_cpu_to_le16(pvht_cap->vht_cap.mcs_sets.tx_max_rate);
LEAVE();
return;
}
/**
* @brief This function fills the VHT cap tlv out put format is CPU
*
* @param priv A pointer to mlan_private structure
* @param pvht_cap A pointer to MrvlIETypes_HTCap_t structure
* @param bands Band configuration
*
* @return N/A
*/
void wlan_fill_vht_cap_ie(mlan_private *priv, IEEEtypes_VHTCap_t *pvht_cap,
t_u16 bands)
{
ENTER();
pvht_cap->ieee_hdr.element_id = VHT_CAPABILITY;
pvht_cap->ieee_hdr.len = sizeof(VHT_capa_t);
/* Fill VHT cap info */
wlan_fill_cap_info(priv, &pvht_cap->vht_cap, bands);
/* rx MCS map */
pvht_cap->vht_cap.mcs_sets.rx_mcs_map =
GET_DEVRXMCSMAP(priv->usr_dot_11ac_mcs_support);
/* rx highest rate */
pvht_cap->vht_cap.mcs_sets.rx_max_rate = wlan_convert_mcsmap_to_maxrate(
priv, bands, pvht_cap->vht_cap.mcs_sets.rx_mcs_map);
/* tx MCS map */
pvht_cap->vht_cap.mcs_sets.tx_mcs_map =
GET_DEVTXMCSMAP(priv->usr_dot_11ac_mcs_support);
/* tx highest rate */
pvht_cap->vht_cap.mcs_sets.tx_max_rate = wlan_convert_mcsmap_to_maxrate(
priv, bands, pvht_cap->vht_cap.mcs_sets.tx_mcs_map);
LEAVE();
return;
}
/*
* @brief This function check if AP is in 11ac mode
*
* @param priv A pointer to mlan_private structure
*
* @return MTRUE/MFALSE
*/
t_u8 wlan_is_ap_in_11ac_mode(mlan_private *priv)
{
BSSDescriptor_t *pbss_desc;
IEEEtypes_VHTOprat_t *vht_oprat = MNULL;
pbss_desc = &priv->curr_bss_params.bss_descriptor;
vht_oprat = pbss_desc->pvht_oprat;
if (!pbss_desc->pvht_cap)
return MFALSE;
if (vht_oprat && (vht_oprat->ieee_hdr.element_id == VHT_OPERATION)) {
if (vht_oprat->chan_width == VHT_OPER_CHWD_20_40MHZ)
return MFALSE;
else
return MTRUE;
} else
return MFALSE;
}
/**
* @brief This function fills the VHTOperation ie out put format is CPU
*
* @param priv A pointer to mlan_private structure
* @param vht_oprat A pointer to IEEEtypes_VHTOprat_t structure
* @param sta_ptr A pointer to sta_node
*
* @return N/A
*/
void wlan_fill_tdls_vht_oprat_ie(mlan_private *priv,
IEEEtypes_VHTOprat_t *vht_oprat,
sta_node *sta_ptr)
{
t_u8 supp_chwd_set;
t_u8 peer_supp_chwd_set;
t_u8 ap_supp_chwd_set;
t_u32 usr_vht_cap_info;
t_u16 mcs_map_user = 0;
t_u16 mcs_map_resp = 0;
t_u16 mcs_map_result = 0;
t_u16 mcs_user = 0;
t_u16 mcs_resp = 0;
t_u16 nss;
t_u8 chan_bw = 0;
BSSDescriptor_t *pbss_desc;
IEEEtypes_VHTCap_t *pvht_cap = &sta_ptr->vht_cap;
IEEEtypes_VHTCap_t *ap_vht_cap = MNULL;
ENTER();
pbss_desc = &priv->curr_bss_params.bss_descriptor;
/* Check if AP is in 11ac mode */
if (MFALSE == wlan_is_ap_in_11ac_mode(priv)) {
if (sta_ptr->ExtCap.ieee_hdr.element_id != EXT_CAPABILITY) {
PRINTM(MMSG, "No Peer's Ext_cap info\n");
return;
}
if (!ISSUPP_EXTCAP_TDLS_WIDER_BANDWIDTH(
sta_ptr->ExtCap.ext_cap)) {
PRINTM(MMSG,
"Peer don't support Wider Bandwitch in Ext_cap\n");
return;
}
} else {
ap_vht_cap = pbss_desc->pvht_cap;
}
vht_oprat->ieee_hdr.element_id = VHT_OPERATION;
vht_oprat->ieee_hdr.len =
sizeof(IEEEtypes_VHTOprat_t) - sizeof(IEEEtypes_Header_t);
if (pbss_desc->bss_band & BAND_A)
usr_vht_cap_info = priv->usr_dot_11ac_dev_cap_a;
else
usr_vht_cap_info = priv->usr_dot_11ac_dev_cap_bg;
/* find the minmum bandwith between AP/TDLS peers */
supp_chwd_set = GET_VHTCAP_CHWDSET(usr_vht_cap_info);
peer_supp_chwd_set = GET_VHTCAP_CHWDSET(pvht_cap->vht_cap.vht_cap_info);
supp_chwd_set = MIN(supp_chwd_set, peer_supp_chwd_set);
/* We need check AP's bandwidth when TDLS_WIDER_BANDWIDTH is off */
if (ap_vht_cap &&
!ISSUPP_EXTCAP_TDLS_WIDER_BANDWIDTH(sta_ptr->ExtCap.ext_cap)) {
ap_supp_chwd_set =
GET_VHTCAP_CHWDSET(ap_vht_cap->vht_cap.vht_cap_info);
supp_chwd_set = MIN(supp_chwd_set, ap_supp_chwd_set);
}
switch (supp_chwd_set) {
case VHT_CAP_CHWD_80MHZ:
vht_oprat->chan_width = VHT_OPER_CHWD_80MHZ;
break;
case VHT_CAP_CHWD_160MHZ:
vht_oprat->chan_width = VHT_OPER_CHWD_160MHZ;
break;
case VHT_CAP_CHWD_80_80MHZ:
vht_oprat->chan_width = VHT_OPER_CHWD_80_80MHZ;
break;
}
/* Fill BASIC VHT MCS and NSS Set */
/* rx MCS Set, find the minimum of the user rx mcs and peer rx mcs*/
mcs_map_user = GET_DEVRXMCSMAP(priv->usr_dot_11ac_mcs_support);
mcs_map_resp = pvht_cap->vht_cap.mcs_sets.rx_mcs_map;
mcs_map_result = 0;
for (nss = 1; nss <= 8; nss++) {
mcs_user = GET_VHTNSSMCS(mcs_map_user, nss);
mcs_resp = GET_VHTNSSMCS(mcs_map_resp, nss);
if ((mcs_user == NO_NSS_SUPPORT) ||
(mcs_resp == NO_NSS_SUPPORT))
SET_VHTNSSMCS(mcs_map_result, nss, NO_NSS_SUPPORT);
else
SET_VHTNSSMCS(mcs_map_result, nss,
MIN(mcs_user, mcs_resp));
}
/* Basic MCS map */
vht_oprat->basic_MCS_map = mcs_map_result;
switch (vht_oprat->chan_width) {
case VHT_OPER_CHWD_80MHZ:
chan_bw = CHANNEL_BW_80MHZ;
break;
case VHT_OPER_CHWD_160MHZ:
chan_bw = CHANNEL_BW_160MHZ;
break;
case VHT_OPER_CHWD_80_80MHZ:
chan_bw = CHANNEL_BW_80MHZ;
break;
}
vht_oprat->chan_center_freq_1 = wlan_get_center_freq_idx(
priv, BAND_AAC, pbss_desc->channel, chan_bw);
LEAVE();
return;
}
/**
* @brief This function would check whether support 80+80Mhz
*
* @param pmpriv A pointer to mlan_private structure
* @param pbss_desc A pointer to BSSDescriptor_t structure
*
* @return ret suport 80+80Mhz or not
*/
t_u8 wlan_is_80_80_support(mlan_private *pmpriv, BSSDescriptor_t *pbss_desc)
{
t_u8 ret = MFALSE;
#if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || \
defined(PCIE9097) || defined(SD9097) || defined(USB9097)
t_u16 rx_nss = 0, tx_nss = 0;
IEEEtypes_VHTCap_t *pvht_cap = pbss_desc->pvht_cap;
MrvlIEtypes_He_cap_t *phecap = MNULL;
IEEEtypes_HECap_t *pBsshecap = MNULL;
#endif
ENTER();
#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))
return ret;
/** check band A */
if (!(pbss_desc->bss_band & BAND_A))
return ret;
/** check band A antenna setting */
rx_nss = GET_RXMCSSUPP(pmpriv->adapter->user_htstream >> 8);
tx_nss = GET_TXMCSSUPP(pmpriv->adapter->user_htstream >> 8) & 0x0f;
/** check if support 2*2 */
if (rx_nss != 2 || tx_nss != 2)
return ret;
/** check if AP support AC 80P80 */
if (ISSUPP_11ACBW8080(pmpriv->usr_dot_11ac_dev_cap_a) && pvht_cap &&
ISSUPP_11ACBW8080(pvht_cap->vht_cap.vht_cap_info))
ret = MTRUE;
/** check if AP support AX 80P80 */
if (pbss_desc->phe_cap) {
pBsshecap = (IEEEtypes_HECap_t *)pbss_desc->phe_cap;
phecap = (MrvlIEtypes_He_cap_t *)pmpriv->user_he_cap;
if (ret && (phecap->he_phy_cap[0] & MBIT(4)) &&
(pBsshecap->he_phy_cap[0] & MBIT(4)))
ret = MTRUE;
else
ret = MFALSE;
}
#endif
LEAVE();
return ret;
}
/**
* @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_11ac_tlv(mlan_private *pmpriv, BSSDescriptor_t *pbss_desc,
t_u8 **ppbuffer)
{
pmlan_adapter pmadapter = pmpriv->adapter;
MrvlIETypes_VHTCap_t *pvht_cap;
MrvlIETypes_OperModeNtf_t *pmrvl_oper_mode;
t_u16 mcs_map_user = 0;
t_u16 nss;
int ret_len = 0;
t_u8 bw_80p80 = MFALSE;
#if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || \
defined(PCIE9097) || defined(USB9097) || defined(SD9097)
t_u16 rx_nss = 0;
#endif
ENTER();
/* Null Checks */
if (ppbuffer == MNULL) {
LEAVE();
return 0;
}
if (*ppbuffer == MNULL) {
LEAVE();
return 0;
}
/* VHT Capabilities IE */
if (pbss_desc->pvht_cap &&
wlan_get_nss_vht_mcs(
pbss_desc->pvht_cap->vht_cap.mcs_sets.rx_mcs_map)) {
pvht_cap = (MrvlIETypes_VHTCap_t *)*ppbuffer;
memset(pmadapter, pvht_cap, 0, sizeof(MrvlIETypes_VHTCap_t));
pvht_cap->header.type = wlan_cpu_to_le16(VHT_CAPABILITY);
pvht_cap->header.len = sizeof(VHT_capa_t);
memcpy_ext(pmadapter,
(t_u8 *)pvht_cap + sizeof(MrvlIEtypesHeader_t),
(t_u8 *)pbss_desc->pvht_cap +
sizeof(IEEEtypes_Header_t),
pvht_cap->header.len, sizeof(VHT_capa_t));
bw_80p80 = wlan_is_80_80_support(pmpriv, pbss_desc);
wlan_fill_vht_cap_tlv(pmpriv, pvht_cap, pbss_desc->bss_band,
MTRUE, bw_80p80);
HEXDUMP("VHT_CAPABILITIES IE", (t_u8 *)pvht_cap,
sizeof(MrvlIETypes_VHTCap_t));
*ppbuffer += sizeof(MrvlIETypes_VHTCap_t);
ret_len += sizeof(MrvlIETypes_VHTCap_t);
pvht_cap->header.len = wlan_cpu_to_le16(pvht_cap->header.len);
} else {
LEAVE();
return 0;
}
/* Operating Mode Notification IE */
pmrvl_oper_mode = (MrvlIETypes_OperModeNtf_t *)*ppbuffer;
memset(pmadapter, pmrvl_oper_mode, 0,
sizeof(MrvlIETypes_OperModeNtf_t));
pmrvl_oper_mode->header.type = wlan_cpu_to_le16(OPER_MODE_NTF);
pmrvl_oper_mode->header.len = sizeof(t_u8);
if (pmpriv->usr_dot_11ac_opermode_bw ||
pmpriv->usr_dot_11ac_opermode_nss) {
pmrvl_oper_mode->oper_mode |=
(pmpriv->usr_dot_11ac_opermode_nss - 1) << 4;
pmrvl_oper_mode->oper_mode |=
pmpriv->usr_dot_11ac_opermode_bw - 1;
if (pbss_desc->bss_band & BAND_G) {
if (!(IS_OPER_MODE_20M(pmrvl_oper_mode->oper_mode))) {
if (pbss_desc->pht_cap->ht_cap.ht_cap_info &
MBIT(1))
SET_OPER_MODE_40M(
pmrvl_oper_mode->oper_mode);
else
SET_OPER_MODE_20M(
pmrvl_oper_mode->oper_mode);
}
}
} else {
/** set default bandwidth:80M*/
SET_OPER_MODE_80M(pmrvl_oper_mode->oper_mode);
#if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || \
defined(PCIE9097) || defined(SD9097) || defined(USB9097)
if (IS_CARD9098(pmadapter->card_type) ||
IS_CARD9097(pmadapter->card_type)) {
if (pbss_desc->bss_band & BAND_A)
rx_nss = GET_RXMCSSUPP(
pmadapter->user_htstream >> 8);
else
rx_nss =
GET_RXMCSSUPP(pmadapter->user_htstream);
}
#endif
mcs_map_user =
GET_DEVRXMCSMAP(pmpriv->usr_dot_11ac_mcs_support);
nss = wlan_get_nss_num_vht_mcs(mcs_map_user);
#if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || \
defined(PCIE9097) || defined(SD9097) || defined(USB9097)
if (IS_CARD9098(pmadapter->card_type) ||
IS_CARD9097(pmadapter->card_type)) {
PRINTM(MCMND, "rx_nss=%d nss=%d\n", rx_nss, nss);
nss = MIN(rx_nss, nss);
}
#endif
pmrvl_oper_mode->oper_mode |= (nss - 1) << 4;
switch (pbss_desc->curr_bandwidth) {
case BW_20MHZ:
SET_OPER_MODE_20M(pmrvl_oper_mode->oper_mode);
break;
case BW_40MHZ:
SET_OPER_MODE_40M(pmrvl_oper_mode->oper_mode);
break;
case BW_80MHZ:
default:
break;
}
}
HEXDUMP("OPER MODE NTF IE", (t_u8 *)pmrvl_oper_mode,
sizeof(MrvlIETypes_OperModeNtf_t));
*ppbuffer += sizeof(MrvlIETypes_OperModeNtf_t);
ret_len += sizeof(MrvlIETypes_OperModeNtf_t);
pmrvl_oper_mode->header.len =
wlan_cpu_to_le16(pmrvl_oper_mode->header.len);
LEAVE();
return ret_len;
}
/**
* @brief 11ac 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_11ac_cfg_ioctl(pmlan_adapter pmadapter,
pmlan_ioctl_req pioctl_req)
{
mlan_status status = MLAN_STATUS_SUCCESS;
mlan_ds_11ac_cfg *cfg = MNULL;
ENTER();
if (pioctl_req->buf_len < sizeof(mlan_ds_11ac_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_11ac_cfg);
pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER;
LEAVE();
return MLAN_STATUS_RESOURCE;
}
cfg = (mlan_ds_11ac_cfg *)pioctl_req->pbuf;
switch (cfg->sub_command) {
case MLAN_OID_11AC_VHT_CFG:
status = wlan_11ac_ioctl_vhtcfg(pmadapter, pioctl_req);
break;
case MLAN_OID_11AC_CFG_SUPPORTED_MCS_SET:
status = wlan_11ac_ioctl_supported_mcs_set(pmadapter,
pioctl_req);
break;
case MLAN_OID_11AC_OPERMODE_CFG:
status = wlan_11ac_ioctl_opermodecfg(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 11ac 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_11ac_cfg(pmlan_private pmpriv, HostCmd_DS_COMMAND *cmd,
t_u16 cmd_action, t_void *pdata_buf)
{
pmlan_adapter pmadapter = pmpriv->adapter;
HostCmd_DS_11AC_CFG *vhtcfg = &cmd->params.vhtcfg;
mlan_ds_11ac_vht_cfg *vht_cfg = (mlan_ds_11ac_vht_cfg *)pdata_buf;
ENTER();
cmd->command = wlan_cpu_to_le16(HostCmd_CMD_11AC_CFG);
cmd->size = wlan_cpu_to_le16(sizeof(HostCmd_DS_11AC_CFG) + S_DS_GEN);
vhtcfg->action = wlan_cpu_to_le16(cmd_action);
vhtcfg->band_config = vht_cfg->band & 0xFF;
// block user enable 80MHZ
if (IS_FW_SUPPORT_NO_80MHZ(pmadapter))
vht_cfg->bwcfg = 0;
vhtcfg->misc_config = vht_cfg->txrx & 0x3;
if (vhtcfg->misc_config != 2)
vhtcfg->misc_config |= (vht_cfg->bwcfg << 2);
vhtcfg->vht_cap_info = wlan_cpu_to_le32(vht_cfg->vht_cap_info);
vht_cfg->vht_rx_mcs = wlan_cpu_to_le32(vht_cfg->vht_rx_mcs);
memcpy_ext(pmadapter, &vhtcfg->vht_supp_mcs_set[0],
&vht_cfg->vht_rx_mcs, sizeof(t_u32), sizeof(t_u32));
vht_cfg->vht_tx_mcs = wlan_cpu_to_le32(vht_cfg->vht_tx_mcs);
memcpy_ext(pmadapter, &vhtcfg->vht_supp_mcs_set[4],
&vht_cfg->vht_tx_mcs, sizeof(t_u32), sizeof(t_u32));
LEAVE();
return MLAN_STATUS_SUCCESS;
}
/**
* @brief This function handles the command response of 11accfg
*
* @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_11ac_cfg(pmlan_private pmpriv, HostCmd_DS_COMMAND *resp,
mlan_ioctl_req *pioctl_buf)
{
pmlan_adapter pmadapter = pmpriv->adapter;
mlan_ds_11ac_cfg *cfg = MNULL;
HostCmd_DS_11AC_CFG *vhtcfg = &resp->params.vhtcfg;
ENTER();
if (pioctl_buf &&
(wlan_le16_to_cpu(vhtcfg->action) == HostCmd_ACT_GEN_GET)) {
cfg = (mlan_ds_11ac_cfg *)pioctl_buf->pbuf;
cfg->param.vht_cfg.band = vhtcfg->band_config;
cfg->param.vht_cfg.txrx = vhtcfg->misc_config & 0x03;
if (cfg->param.vht_cfg.txrx & 0x1)
cfg->param.vht_cfg.bwcfg =
(vhtcfg->misc_config & 0x04) >> 2;
else
cfg->param.vht_cfg.bwcfg = 0;
cfg->param.vht_cfg.vht_cap_info =
wlan_le32_to_cpu(vhtcfg->vht_cap_info);
memcpy_ext(pmadapter, &cfg->param.vht_cfg.vht_rx_mcs,
&vhtcfg->vht_supp_mcs_set[0], sizeof(t_u32),
sizeof(t_u32));
cfg->param.vht_cfg.vht_rx_mcs =
wlan_le32_to_cpu(cfg->param.vht_cfg.vht_rx_mcs);
memcpy_ext(pmadapter, &cfg->param.vht_cfg.vht_tx_mcs,
&vhtcfg->vht_supp_mcs_set[4], sizeof(t_u32),
sizeof(t_u32));
cfg->param.vht_cfg.vht_tx_mcs =
wlan_le32_to_cpu(cfg->param.vht_cfg.vht_tx_mcs);
cfg->param.vht_cfg.vht_rx_max_rate =
wlan_convert_mcsmap_to_maxrate(
pmpriv, cfg->param.vht_cfg.band,
cfg->param.vht_cfg.vht_rx_mcs);
cfg->param.vht_cfg.vht_tx_max_rate =
wlan_convert_mcsmap_to_maxrate(
pmpriv, cfg->param.vht_cfg.band,
cfg->param.vht_cfg.vht_tx_mcs);
}
LEAVE();
return MLAN_STATUS_SUCCESS;
}
void wlan_update_11ac_cap(mlan_private *pmpriv)
{
mlan_adapter *pmadapter = pmpriv->adapter;
pmpriv->usr_dot_11ac_mcs_support = pmadapter->hw_dot_11ac_mcs_support;
pmpriv->usr_dot_11ac_dev_cap_bg =
pmadapter->hw_dot_11ac_dev_cap &
~DEFALUT_11AC_CAP_BEAMFORMING_RESET_MASK;
pmpriv->usr_dot_11ac_dev_cap_a =
pmadapter->hw_dot_11ac_dev_cap &
~DEFALUT_11AC_CAP_BEAMFORMING_RESET_MASK;
pmpriv->usr_dot_11ac_bw = BW_FOLLOW_VHTCAP;
}
/**
* @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_11ac_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_GAC);
else if (bss_band & BAND_A)
return (pmpriv->adapter->adhoc_start_band & BAND_AAC);
} else {
if (bss_band & BAND_G)
return (pmpriv->config_bands & BAND_GAC);
else if (bss_band & BAND_A)
return (pmpriv->config_bands & BAND_AAC);
}
return 0;
}