2020-12-05 11:42:54 +00:00
|
|
|
/** @file mlan_11ac.c
|
|
|
|
*
|
2022-08-31 02:00:56 +00:00
|
|
|
* @brief This file contains the functions for station ioctl.
|
2020-12-05 11:42:54 +00:00
|
|
|
*
|
|
|
|
*
|
2021-04-01 03:15:14 +00:00
|
|
|
* Copyright 2011-2021 NXP
|
2020-12-05 11:42:54 +00:00
|
|
|
*
|
2022-08-31 02:00:56 +00:00
|
|
|
* 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.
|
2020-12-05 11:42:54 +00:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#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"
|
2021-04-01 03:15:14 +00:00
|
|
|
#include "mlan_11ac.h"
|
2020-12-05 11:42:54 +00:00
|
|
|
|
|
|
|
/********************************************************
|
|
|
|
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)
|
|
|
|
{
|
2023-04-18 04:54:05 +00:00
|
|
|
struct center_freq_desc {
|
|
|
|
t_u8 pri_chan;
|
|
|
|
t_u8 ch_40;
|
|
|
|
t_u8 ch_80;
|
|
|
|
t_u8 ch_160;
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct center_freq_desc center_freq_idx_map_5g[] = {
|
|
|
|
{.pri_chan = 36, .ch_40 = 38, .ch_80 = 42, .ch_160 = 50},
|
|
|
|
{.pri_chan = 40, .ch_40 = 38, .ch_80 = 42, .ch_160 = 50},
|
|
|
|
{.pri_chan = 44, .ch_40 = 46, .ch_80 = 42, .ch_160 = 50},
|
|
|
|
{.pri_chan = 48, .ch_40 = 46, .ch_80 = 42, .ch_160 = 50},
|
|
|
|
{.pri_chan = 52, .ch_40 = 54, .ch_80 = 58, .ch_160 = 50},
|
|
|
|
{.pri_chan = 56, .ch_40 = 54, .ch_80 = 58, .ch_160 = 50},
|
|
|
|
{.pri_chan = 60, .ch_40 = 62, .ch_80 = 58, .ch_160 = 50},
|
|
|
|
{.pri_chan = 64, .ch_40 = 62, .ch_80 = 58, .ch_160 = 50},
|
|
|
|
{.pri_chan = 68, .ch_40 = 70, .ch_80 = 74, .ch_160 = 0},
|
|
|
|
{.pri_chan = 72, .ch_40 = 70, .ch_80 = 74, .ch_160 = 0},
|
|
|
|
{.pri_chan = 76, .ch_40 = 78, .ch_80 = 74, .ch_160 = 0},
|
|
|
|
{.pri_chan = 80, .ch_40 = 78, .ch_80 = 74, .ch_160 = 0},
|
|
|
|
{.pri_chan = 84, .ch_40 = 86, .ch_80 = 90, .ch_160 = 0},
|
|
|
|
{.pri_chan = 88, .ch_40 = 86, .ch_80 = 90, .ch_160 = 0},
|
|
|
|
{.pri_chan = 92, .ch_40 = 94, .ch_80 = 90, .ch_160 = 0},
|
|
|
|
{.pri_chan = 96, .ch_40 = 94, .ch_80 = 90, .ch_160 = 0},
|
|
|
|
{.pri_chan = 100, .ch_40 = 102, .ch_80 = 106, .ch_160 = 114},
|
|
|
|
{.pri_chan = 104, .ch_40 = 102, .ch_80 = 106, .ch_160 = 114},
|
|
|
|
{.pri_chan = 108, .ch_40 = 110, .ch_80 = 106, .ch_160 = 114},
|
|
|
|
{.pri_chan = 112, .ch_40 = 110, .ch_80 = 106, .ch_160 = 114},
|
|
|
|
{.pri_chan = 116, .ch_40 = 118, .ch_80 = 122, .ch_160 = 114},
|
|
|
|
{.pri_chan = 120, .ch_40 = 118, .ch_80 = 122, .ch_160 = 114},
|
|
|
|
{.pri_chan = 124, .ch_40 = 126, .ch_80 = 122, .ch_160 = 114},
|
|
|
|
{.pri_chan = 128, .ch_40 = 126, .ch_80 = 122, .ch_160 = 114},
|
|
|
|
{.pri_chan = 132, .ch_40 = 134, .ch_80 = 138, .ch_160 = 0},
|
|
|
|
{.pri_chan = 136, .ch_40 = 134, .ch_80 = 138, .ch_160 = 0},
|
|
|
|
{.pri_chan = 140, .ch_40 = 142, .ch_80 = 138, .ch_160 = 0},
|
|
|
|
{.pri_chan = 144, .ch_40 = 142, .ch_80 = 138, .ch_160 = 0},
|
|
|
|
{.pri_chan = 149, .ch_40 = 151, .ch_80 = 155, .ch_160 = 163},
|
|
|
|
{.pri_chan = 153, .ch_40 = 151, .ch_80 = 155, .ch_160 = 163},
|
|
|
|
{.pri_chan = 157, .ch_40 = 159, .ch_80 = 155, .ch_160 = 163},
|
|
|
|
{.pri_chan = 161, .ch_40 = 159, .ch_80 = 155, .ch_160 = 163},
|
|
|
|
{.pri_chan = 165, .ch_40 = 167, .ch_80 = 171, .ch_160 = 163},
|
|
|
|
{.pri_chan = 169, .ch_40 = 167, .ch_80 = 171, .ch_160 = 163},
|
|
|
|
{.pri_chan = 173, .ch_40 = 175, .ch_80 = 171, .ch_160 = 163},
|
|
|
|
{.pri_chan = 177, .ch_40 = 175, .ch_80 = 171, .ch_160 = 163},
|
|
|
|
{.pri_chan = 184, .ch_40 = 186, .ch_80 = 190, .ch_160 = 0},
|
|
|
|
{.pri_chan = 188, .ch_40 = 186, .ch_80 = 190, .ch_160 = 0},
|
|
|
|
{.pri_chan = 192, .ch_40 = 194, .ch_80 = 190, .ch_160 = 0},
|
|
|
|
{.pri_chan = 196, .ch_40 = 194, .ch_80 = 190, .ch_160 = 0},
|
|
|
|
{.pri_chan = 0,
|
|
|
|
.ch_40 = 42 /* terminator with default cfreq */}};
|
|
|
|
|
|
|
|
const struct center_freq_desc *map = MNULL;
|
|
|
|
|
|
|
|
if (band == BAND_5GHZ)
|
|
|
|
map = center_freq_idx_map_5g;
|
|
|
|
|
|
|
|
for (; map != MNULL; map++) {
|
|
|
|
/* reached end of map, return default value for that map */
|
|
|
|
if (map->pri_chan == 0)
|
|
|
|
return map->ch_40;
|
|
|
|
|
|
|
|
if (map->pri_chan == pri_chan) {
|
|
|
|
if (chan_bw == CHANNEL_BW_40MHZ_ABOVE ||
|
|
|
|
chan_bw == CHANNEL_BW_40MHZ_BELOW)
|
|
|
|
return map->ch_40;
|
|
|
|
|
|
|
|
if (chan_bw == CHANNEL_BW_80MHZ)
|
|
|
|
return map->ch_80;
|
|
|
|
|
|
|
|
if (chan_bw == CHANNEL_BW_160MHZ)
|
|
|
|
return map->ch_160;
|
2020-12-05 11:42:54 +00:00
|
|
|
}
|
|
|
|
}
|
2023-04-18 04:54:05 +00:00
|
|
|
|
|
|
|
return 0;
|
2020-12-05 11:42:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @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,
|
2022-04-19 06:46:37 +00:00
|
|
|
t_u16 bands)
|
2020-12-05 11:42:54 +00:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
|
2021-10-09 02:29:40 +00:00
|
|
|
RESET_VHTCAP_MAXMPDULEN(vht_cap->vht_cap_info);
|
2020-12-05 11:42:54 +00:00
|
|
|
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;
|
|
|
|
t_u16 rx_nss = 0;
|
|
|
|
t_u16 tx_nss = 0;
|
|
|
|
|
|
|
|
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 (IS_CARD9098(pmadapter->card_type) ||
|
2023-07-15 06:41:14 +00:00
|
|
|
IS_CARDIW624(pmadapter->card_type) ||
|
2020-12-05 11:42:54 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* 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 ((rx_nss != 0) && (nss > rx_nss))
|
|
|
|
cfg_value = NO_NSS_SUPPORT;
|
|
|
|
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);
|
2021-04-01 03:15:14 +00:00
|
|
|
if ((tx_nss != 0) && (nss > tx_nss))
|
2020-12-05 11:42:54 +00:00
|
|
|
cfg_value = NO_NSS_SUPPORT;
|
|
|
|
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,
|
2021-04-01 03:15:14 +00:00
|
|
|
"GET_HW_SPEC: Maximum MPDU length = 3895 octets\n");
|
2020-12-05 11:42:54 +00:00
|
|
|
break;
|
|
|
|
case 0x1:
|
|
|
|
PRINTM(MINFO,
|
2021-04-01 03:15:14 +00:00
|
|
|
"GET_HW_SPEC: Maximum MPDU length = 7991 octets\n");
|
2020-12-05 11:42:54 +00:00
|
|
|
break;
|
|
|
|
case 0x2:
|
|
|
|
PRINTM(MINFO,
|
2021-04-01 03:15:14 +00:00
|
|
|
"GET_HW_SPEC: Maximum MPDU length = 11454 octets\n");
|
2020-12-05 11:42:54 +00:00
|
|
|
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;
|
|
|
|
t_u16 rx_nss = 0, tx_nss = 0;
|
|
|
|
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 (IS_CARD9098(priv->adapter->card_type) ||
|
2023-07-15 06:41:14 +00:00
|
|
|
IS_CARDIW624(priv->adapter->card_type) ||
|
2020-12-05 11:42:54 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
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 ((rx_nss != 0) && (nss > rx_nss))
|
|
|
|
mcs_user = NO_NSS_SUPPORT;
|
|
|
|
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 ((tx_nss != 0) && (nss > tx_nss))
|
|
|
|
mcs_user = NO_NSS_SUPPORT;
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2021-04-01 03:15:14 +00:00
|
|
|
/*
|
|
|
|
* @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(
|
2023-04-18 04:54:05 +00:00
|
|
|
priv, BAND_5GHZ, pbss_desc->channel, chan_bw);
|
2021-04-01 03:15:14 +00:00
|
|
|
|
|
|
|
LEAVE();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-12-05 11:42:54 +00:00
|
|
|
/**
|
|
|
|
* @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;
|
|
|
|
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;
|
|
|
|
|
|
|
|
ENTER();
|
|
|
|
|
|
|
|
if (!IS_CARD9098(pmpriv->adapter->card_type) &&
|
2023-07-15 06:41:14 +00:00
|
|
|
!IS_CARDIW624(pmpriv->adapter->card_type) &&
|
2020-12-05 11:42:54 +00:00
|
|
|
!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;
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
t_u16 rx_nss = 0;
|
|
|
|
|
|
|
|
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,
|
2022-01-23 13:24:49 +00:00
|
|
|
MFALSE, bw_80p80);
|
2020-12-05 11:42:54 +00:00
|
|
|
|
|
|
|
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 (IS_CARD9098(pmadapter->card_type) ||
|
2023-07-15 06:41:14 +00:00
|
|
|
IS_CARDIW624(pmadapter->card_type) ||
|
2020-12-05 11:42:54 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
mcs_map_user =
|
|
|
|
GET_DEVRXMCSMAP(pmpriv->usr_dot_11ac_mcs_support);
|
|
|
|
nss = wlan_get_nss_num_vht_mcs(mcs_map_user);
|
|
|
|
|
|
|
|
if (IS_CARD9098(pmadapter->card_type) ||
|
2023-07-15 06:41:14 +00:00
|
|
|
IS_CARDIW624(pmadapter->card_type) ||
|
2020-12-05 11:42:54 +00:00
|
|
|
IS_CARD9097(pmadapter->card_type)) {
|
|
|
|
PRINTM(MCMND, "rx_nss=%d nss=%d\n", rx_nss, nss);
|
|
|
|
nss = MIN(rx_nss, nss);
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|