// SPDX-License-Identifier: GPL-2.0-only
/*
* NXP Wireless LAN device driver: 802.11ac
*
* Copyright 2011-2020 NXP
*/
#include "decl.h"
#include "ioctl.h"
#include "fw.h"
#include "main.h"
#include "11ac.h"
/* Tables of the MCS map to the highest data rate (in Mbps) supported
* for long GI.
*/
static const u16 max_rate_lgi_80MHZ[8 ][3 ] = {
{0 x124, 0 x15F, 0 x186}, /* NSS = 1 */
{0 x249, 0 x2BE, 0 x30C}, /* NSS = 2 */
{0 x36D, 0 x41D, 0 x492}, /* NSS = 3 */
{0 x492, 0 x57C, 0 x618}, /* NSS = 4 */
{0 x5B6, 0 x6DB, 0 x79E}, /* NSS = 5 */
{0 x6DB, 0 x83A, 0 x0}, /* NSS = 6 */
{0 x7FF, 0 x999, 0 xAAA}, /* NSS = 7 */
{0 x924, 0 xAF8, 0 xC30} /* NSS = 8 */
};
static const u16 max_rate_lgi_160MHZ[8 ][3 ] = {
{0 x249, 0 x2BE, 0 x30C}, /* NSS = 1 */
{0 x492, 0 x57C, 0 x618}, /* NSS = 2 */
{0 x6DB, 0 x83A, 0 x0}, /* NSS = 3 */
{0 x924, 0 xAF8, 0 xC30}, /* NSS = 4 */
{0 xB6D, 0 xDB6, 0 xF3C}, /* NSS = 5 */
{0 xDB6, 0 x1074, 0 x1248}, /* NSS = 6 */
{0 xFFF, 0 x1332, 0 x1554}, /* NSS = 7 */
{0 x1248, 0 x15F0, 0 x1860} /* NSS = 8 */
};
/* This function converts the 2-bit MCS map to the highest long GI
* VHT data rate.
*/
static u16
mwifiex_convert_mcsmap_to_maxrate(struct mwifiex_private *priv,
u8 bands, u16 mcs_map)
{
u8 i, nss, mcs;
u16 max_rate = 0 ;
u32 usr_vht_cap_info = 0 ;
struct mwifiex_adapter *adapter = priv->adapter;
if (bands & BAND_AAC)
usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_a;
else
usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_bg;
/* find the max NSS supported */
nss = 1 ;
for (i = 1 ; i <= 8 ; i++) {
mcs = GET_VHTNSSMCS(mcs_map, i);
if (mcs < IEEE80211_VHT_MCS_NOT_SUPPORTED)
nss = i;
}
mcs = GET_VHTNSSMCS(mcs_map, nss);
/* if mcs is 3, nss must be 1 (NSS = 1). Default mcs to MCS 0~9 */
if (mcs == IEEE80211_VHT_MCS_NOT_SUPPORTED)
mcs = IEEE80211_VHT_MCS_SUPPORT_0_9;
if (GET_VHTCAP_CHWDSET(usr_vht_cap_info)) {
/* support 160 MHz */
max_rate = max_rate_lgi_160MHZ[nss - 1 ][mcs];
if (!max_rate)
/* MCS9 is not supported in NSS6 */
max_rate = max_rate_lgi_160MHZ[nss - 1 ][mcs - 1 ];
} else {
max_rate = max_rate_lgi_80MHZ[nss - 1 ][mcs];
if (!max_rate)
/* MCS9 is not supported in NSS3 */
max_rate = max_rate_lgi_80MHZ[nss - 1 ][mcs - 1 ];
}
return max_rate;
}
static void
mwifiex_fill_vht_cap_info(struct mwifiex_private *priv,
struct ieee80211_vht_cap *vht_cap, u8 bands)
{
struct mwifiex_adapter *adapter = priv->adapter;
if (bands & BAND_A)
vht_cap->vht_cap_info =
cpu_to_le32(adapter->usr_dot_11ac_dev_cap_a);
else
vht_cap->vht_cap_info =
cpu_to_le32(adapter->usr_dot_11ac_dev_cap_bg);
}
void mwifiex_fill_vht_cap_tlv(struct mwifiex_private *priv,
struct ieee80211_vht_cap *vht_cap, u8 bands)
{
struct mwifiex_adapter *adapter = priv->adapter;
u16 mcs_map_user, mcs_map_resp, mcs_map_result;
u16 mcs_user, mcs_resp, nss, tmp;
/* Fill VHT cap info */
mwifiex_fill_vht_cap_info(priv, vht_cap, bands);
/* rx MCS Set: find the minimum of the user rx mcs and ap rx mcs */
mcs_map_user = GET_DEVRXMCSMAP(adapter->usr_dot_11ac_mcs_support);
mcs_map_resp = le16_to_cpu(vht_cap->supp_mcs.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 == IEEE80211_VHT_MCS_NOT_SUPPORTED) ||
(mcs_resp == IEEE80211_VHT_MCS_NOT_SUPPORTED))
SET_VHTNSSMCS(mcs_map_result, nss,
IEEE80211_VHT_MCS_NOT_SUPPORTED);
else
SET_VHTNSSMCS(mcs_map_result, nss,
min(mcs_user, mcs_resp));
}
vht_cap->supp_mcs.rx_mcs_map = cpu_to_le16(mcs_map_result);
tmp = mwifiex_convert_mcsmap_to_maxrate(priv, bands, mcs_map_result);
vht_cap->supp_mcs.rx_highest = cpu_to_le16(tmp);
/* tx MCS Set: find the minimum of the user tx mcs and ap tx mcs */
mcs_map_user = GET_DEVTXMCSMAP(adapter->usr_dot_11ac_mcs_support);
mcs_map_resp = le16_to_cpu(vht_cap->supp_mcs.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 ((mcs_user == IEEE80211_VHT_MCS_NOT_SUPPORTED) ||
(mcs_resp == IEEE80211_VHT_MCS_NOT_SUPPORTED))
SET_VHTNSSMCS(mcs_map_result, nss,
IEEE80211_VHT_MCS_NOT_SUPPORTED);
else
SET_VHTNSSMCS(mcs_map_result, nss,
min(mcs_user, mcs_resp));
}
vht_cap->supp_mcs.tx_mcs_map = cpu_to_le16(mcs_map_result);
tmp = mwifiex_convert_mcsmap_to_maxrate(priv, bands, mcs_map_result);
vht_cap->supp_mcs.tx_highest = cpu_to_le16(tmp);
return ;
}
int mwifiex_cmd_append_11ac_tlv(struct mwifiex_private *priv,
struct mwifiex_bssdescriptor *bss_desc,
u8 **buffer)
{
struct mwifiex_ie_types_vhtcap *vht_cap;
struct mwifiex_ie_types_oper_mode_ntf *oper_ntf;
struct ieee_types_oper_mode_ntf *ieee_oper_ntf;
struct mwifiex_ie_types_vht_oper *vht_op;
struct mwifiex_adapter *adapter = priv->adapter;
u8 supp_chwd_set;
u32 usr_vht_cap_info;
int ret_len = 0 ;
if (bss_desc->bss_band & BAND_A)
usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_a;
else
usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_bg;
/* VHT Capabilities IE */
if (bss_desc->bcn_vht_cap) {
vht_cap = (struct mwifiex_ie_types_vhtcap *)*buffer;
memset(vht_cap, 0 , sizeof (*vht_cap));
vht_cap->header.type = cpu_to_le16(WLAN_EID_VHT_CAPABILITY);
vht_cap->header.len =
cpu_to_le16(sizeof (struct ieee80211_vht_cap));
memcpy((u8 *)vht_cap + sizeof (struct mwifiex_ie_types_header),
(u8 *)bss_desc->bcn_vht_cap,
le16_to_cpu(vht_cap->header.len));
mwifiex_fill_vht_cap_tlv(priv, &vht_cap->vht_cap,
bss_desc->bss_band);
*buffer += sizeof (*vht_cap);
ret_len += sizeof (*vht_cap);
}
/* VHT Operation IE */
if (bss_desc->bcn_vht_oper) {
if (priv->bss_mode == NL80211_IFTYPE_STATION) {
vht_op = (struct mwifiex_ie_types_vht_oper *)*buffer;
memset(vht_op, 0 , sizeof (*vht_op));
vht_op->header.type =
cpu_to_le16(WLAN_EID_VHT_OPERATION);
vht_op->header.len = cpu_to_le16(sizeof (*vht_op) -
sizeof (struct mwifiex_ie_types_header));
memcpy((u8 *)vht_op +
sizeof (struct mwifiex_ie_types_header),
(u8 *)bss_desc->bcn_vht_oper,
le16_to_cpu(vht_op->header.len));
/* negotiate the channel width and central freq
* and keep the central freq as the peer suggests
*/
supp_chwd_set = GET_VHTCAP_CHWDSET(usr_vht_cap_info);
switch (supp_chwd_set) {
case 0 :
vht_op->chan_width =
min_t(u8, IEEE80211_VHT_CHANWIDTH_80MHZ,
bss_desc->bcn_vht_oper->chan_width);
break ;
case 1 :
vht_op->chan_width =
min_t(u8, IEEE80211_VHT_CHANWIDTH_160MHZ,
bss_desc->bcn_vht_oper->chan_width);
break ;
case 2 :
vht_op->chan_width =
min_t(u8, IEEE80211_VHT_CHANWIDTH_80P80MHZ,
bss_desc->bcn_vht_oper->chan_width);
break ;
default :
vht_op->chan_width =
IEEE80211_VHT_CHANWIDTH_USE_HT;
break ;
}
*buffer += sizeof (*vht_op);
ret_len += sizeof (*vht_op);
}
}
/* Operating Mode Notification IE */
if (bss_desc->oper_mode) {
ieee_oper_ntf = bss_desc->oper_mode;
oper_ntf = (void *)*buffer;
memset(oper_ntf, 0 , sizeof (*oper_ntf));
oper_ntf->header.type = cpu_to_le16(WLAN_EID_OPMODE_NOTIF);
oper_ntf->header.len = cpu_to_le16(sizeof (u8));
oper_ntf->oper_mode = ieee_oper_ntf->oper_mode;
*buffer += sizeof (*oper_ntf);
ret_len += sizeof (*oper_ntf);
}
return ret_len;
}
int mwifiex_cmd_11ac_cfg(struct mwifiex_private *priv,
struct host_cmd_ds_command *cmd, u16 cmd_action,
struct mwifiex_11ac_vht_cfg *cfg)
{
struct host_cmd_11ac_vht_cfg *vhtcfg = &cmd->params.vht_cfg;
cmd->command = cpu_to_le16(HostCmd_CMD_11AC_CFG);
cmd->size = cpu_to_le16(sizeof (struct host_cmd_11ac_vht_cfg) +
S_DS_GEN);
vhtcfg->action = cpu_to_le16(cmd_action);
vhtcfg->band_config = cfg->band_config;
vhtcfg->misc_config = cfg->misc_config;
vhtcfg->cap_info = cpu_to_le32(cfg->cap_info);
vhtcfg->mcs_tx_set = cpu_to_le32(cfg->mcs_tx_set);
vhtcfg->mcs_rx_set = cpu_to_le32(cfg->mcs_rx_set);
return 0 ;
}
/* This function initializes the BlockACK setup information for given
* mwifiex_private structure for 11ac enabled networks.
*/
void mwifiex_set_11ac_ba_params(struct mwifiex_private *priv)
{
priv->add_ba_param.timeout = MWIFIEX_DEFAULT_BLOCK_ACK_TIMEOUT;
if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) {
priv->add_ba_param.tx_win_size =
MWIFIEX_11AC_UAP_AMPDU_DEF_TXWINSIZE;
priv->add_ba_param.rx_win_size =
MWIFIEX_11AC_UAP_AMPDU_DEF_RXWINSIZE;
} else {
priv->add_ba_param.tx_win_size =
MWIFIEX_11AC_STA_AMPDU_DEF_TXWINSIZE;
priv->add_ba_param.rx_win_size =
MWIFIEX_11AC_STA_AMPDU_DEF_RXWINSIZE;
}
return ;
}
bool mwifiex_is_bss_in_11ac_mode(struct mwifiex_private *priv)
{
struct mwifiex_bssdescriptor *bss_desc;
struct ieee80211_vht_operation *vht_oper;
bss_desc = &priv->curr_bss_params.bss_descriptor;
vht_oper = bss_desc->bcn_vht_oper;
if (!bss_desc->bcn_vht_cap || !vht_oper)
return false ;
if (vht_oper->chan_width == IEEE80211_VHT_CHANWIDTH_USE_HT)
return false ;
return true ;
}
u8 mwifiex_get_center_freq_index(struct mwifiex_private *priv, u8 band,
u32 pri_chan, u8 chan_bw)
{
u8 center_freq_idx = 0 ;
if (band & BAND_AAC) {
switch (pri_chan) {
case 36 :
case 40 :
case 44 :
case 48 :
if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ)
center_freq_idx = 42 ;
break ;
case 52 :
case 56 :
case 60 :
case 64 :
if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ)
center_freq_idx = 58 ;
else if (chan_bw == IEEE80211_VHT_CHANWIDTH_160MHZ)
center_freq_idx = 50 ;
break ;
case 100 :
case 104 :
case 108 :
case 112 :
if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ)
center_freq_idx = 106 ;
break ;
case 116 :
case 120 :
case 124 :
case 128 :
if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ)
center_freq_idx = 122 ;
else if (chan_bw == IEEE80211_VHT_CHANWIDTH_160MHZ)
center_freq_idx = 114 ;
break ;
case 132 :
case 136 :
case 140 :
case 144 :
if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ)
center_freq_idx = 138 ;
break ;
case 149 :
case 153 :
case 157 :
case 161 :
if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ)
center_freq_idx = 155 ;
break ;
default :
center_freq_idx = 42 ;
}
}
return center_freq_idx;
}
Messung V0.5 in Prozent C=96 H=93 G=94
¤ Dauer der Verarbeitung: 0.11 Sekunden
(vorverarbeitet am 2026-06-07)
¤
*© Formatika GbR, Deutschland