mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 14:11:52 +00:00
mac80211: avoid using ext NSS high BW if not supported
If the AP advertises inconsistent data, namely it has CCFS1 or CCFS2, but doesn't advertise support for 160/80+80 bandwidth or "Extended NSS BW Support", then we cannot use any MCSes in the the higher bandwidth. Thus, avoid connecting with higher bandwidth since it's less efficient that way. Link: https://lore.kernel.org/r/20200528213443.0e55d40c3ccc.I6fd0b4708ebd087e5e46466c3e91f6efbcbef668@changeid Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
parent
607ca9ea34
commit
2a333a0db2
@ -9,7 +9,7 @@
|
||||
* Copyright 2009, Johannes Berg <johannes@sipsolutions.net>
|
||||
* Copyright 2013-2014 Intel Mobile Communications GmbH
|
||||
* Copyright(c) 2016 Intel Deutschland GmbH
|
||||
* Copyright(c) 2018-2019 Intel Corporation
|
||||
* Copyright(c) 2018-2020 Intel Corporation
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
@ -781,6 +781,7 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
|
||||
enum nl80211_channel_type ch_type;
|
||||
int err;
|
||||
u32 sta_flags;
|
||||
u32 vht_cap_info = 0;
|
||||
|
||||
sdata_assert_lock(sdata);
|
||||
|
||||
@ -798,9 +799,13 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
|
||||
break;
|
||||
}
|
||||
|
||||
if (elems->vht_cap_elem)
|
||||
vht_cap_info = le32_to_cpu(elems->vht_cap_elem->vht_cap_info);
|
||||
|
||||
memset(¶ms, 0, sizeof(params));
|
||||
err = ieee80211_parse_ch_switch_ie(sdata, elems,
|
||||
ifibss->chandef.chan->band,
|
||||
vht_cap_info,
|
||||
sta_flags, ifibss->bssid, &csa_ie);
|
||||
/* can't switch to destination channel, fail */
|
||||
if (err < 0)
|
||||
@ -1060,8 +1065,10 @@ static void ieee80211_update_sta_info(struct ieee80211_sub_if_data *sdata,
|
||||
/* we both use VHT */
|
||||
struct ieee80211_vht_cap cap_ie;
|
||||
struct ieee80211_sta_vht_cap cap = sta->sta.vht_cap;
|
||||
u32 vht_cap_info =
|
||||
le32_to_cpu(elems->vht_cap_elem->vht_cap_info);
|
||||
|
||||
ieee80211_chandef_vht_oper(&local->hw,
|
||||
ieee80211_chandef_vht_oper(&local->hw, vht_cap_info,
|
||||
elems->vht_operation,
|
||||
elems->ht_operation,
|
||||
&chandef);
|
||||
|
@ -111,6 +111,8 @@ struct ieee80211_bss {
|
||||
size_t supp_rates_len;
|
||||
struct ieee80211_rate *beacon_rate;
|
||||
|
||||
u32 vht_cap_info;
|
||||
|
||||
/*
|
||||
* During association, we save an ERP value from a probe response so
|
||||
* that we can feed ERP info to the driver when handling the
|
||||
@ -1915,6 +1917,7 @@ void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
|
||||
* @sdata: the sdata of the interface which has received the frame
|
||||
* @elems: parsed 802.11 elements received with the frame
|
||||
* @current_band: indicates the current band
|
||||
* @vht_cap_info: VHT capabilities of the transmitter
|
||||
* @sta_flags: contains information about own capabilities and restrictions
|
||||
* to decide which channel switch announcements can be accepted. Only the
|
||||
* following subset of &enum ieee80211_sta_flags are evaluated:
|
||||
@ -1929,6 +1932,7 @@ void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
|
||||
int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee802_11_elems *elems,
|
||||
enum nl80211_band current_band,
|
||||
u32 vht_cap_info,
|
||||
u32 sta_flags, u8 *bssid,
|
||||
struct ieee80211_csa_ie *csa_ie);
|
||||
|
||||
@ -2194,7 +2198,7 @@ u8 *ieee80211_add_wmm_info_ie(u8 *buf, u8 qosinfo);
|
||||
/* channel management */
|
||||
bool ieee80211_chandef_ht_oper(const struct ieee80211_ht_operation *ht_oper,
|
||||
struct cfg80211_chan_def *chandef);
|
||||
bool ieee80211_chandef_vht_oper(struct ieee80211_hw *hw,
|
||||
bool ieee80211_chandef_vht_oper(struct ieee80211_hw *hw, u32 vht_cap_info,
|
||||
const struct ieee80211_vht_operation *oper,
|
||||
const struct ieee80211_ht_operation *htop,
|
||||
struct cfg80211_chan_def *chandef);
|
||||
|
@ -1,7 +1,7 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2008, 2009 open80211s Ltd.
|
||||
* Copyright (C) 2018 - 2019 Intel Corporation
|
||||
* Copyright (C) 2018 - 2020 Intel Corporation
|
||||
* Authors: Luis Carlos Cobo <luisca@cozybit.com>
|
||||
* Javier Cardona <javier@cozybit.com>
|
||||
*/
|
||||
@ -63,6 +63,7 @@ bool mesh_matches_local(struct ieee80211_sub_if_data *sdata,
|
||||
u32 basic_rates = 0;
|
||||
struct cfg80211_chan_def sta_chan_def;
|
||||
struct ieee80211_supported_band *sband;
|
||||
u32 vht_cap_info = 0;
|
||||
|
||||
/*
|
||||
* As support for each feature is added, check for matching
|
||||
@ -96,7 +97,11 @@ bool mesh_matches_local(struct ieee80211_sub_if_data *sdata,
|
||||
cfg80211_chandef_create(&sta_chan_def, sdata->vif.bss_conf.chandef.chan,
|
||||
NL80211_CHAN_NO_HT);
|
||||
ieee80211_chandef_ht_oper(ie->ht_operation, &sta_chan_def);
|
||||
ieee80211_chandef_vht_oper(&sdata->local->hw,
|
||||
|
||||
if (ie->vht_cap_elem)
|
||||
vht_cap_info = le32_to_cpu(ie->vht_cap_elem->vht_cap_info);
|
||||
|
||||
ieee80211_chandef_vht_oper(&sdata->local->hw, vht_cap_info,
|
||||
ie->vht_operation, ie->ht_operation,
|
||||
&sta_chan_def);
|
||||
|
||||
@ -1076,7 +1081,7 @@ ieee80211_mesh_process_chnswitch(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
|
||||
struct ieee80211_supported_band *sband;
|
||||
int err;
|
||||
u32 sta_flags;
|
||||
u32 sta_flags, vht_cap_info = 0;
|
||||
|
||||
sdata_assert_lock(sdata);
|
||||
|
||||
@ -1099,8 +1104,13 @@ ieee80211_mesh_process_chnswitch(struct ieee80211_sub_if_data *sdata,
|
||||
break;
|
||||
}
|
||||
|
||||
if (elems->vht_cap_elem)
|
||||
vht_cap_info =
|
||||
le32_to_cpu(elems->vht_cap_elem->vht_cap_info);
|
||||
|
||||
memset(¶ms, 0, sizeof(params));
|
||||
err = ieee80211_parse_ch_switch_ie(sdata, elems, sband->band,
|
||||
vht_cap_info,
|
||||
sta_flags, sdata->vif.addr,
|
||||
&csa_ie);
|
||||
if (err < 0)
|
||||
|
@ -145,6 +145,7 @@ static u32
|
||||
ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_supported_band *sband,
|
||||
struct ieee80211_channel *channel,
|
||||
u32 vht_cap_info,
|
||||
const struct ieee80211_ht_operation *ht_oper,
|
||||
const struct ieee80211_vht_operation *vht_oper,
|
||||
const struct ieee80211_he_operation *he_oper,
|
||||
@ -223,7 +224,7 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
|
||||
memcpy(&he_oper_vht_cap, he_oper->optional, 3);
|
||||
he_oper_vht_cap.basic_mcs_set = cpu_to_le16(0);
|
||||
|
||||
if (!ieee80211_chandef_vht_oper(&sdata->local->hw,
|
||||
if (!ieee80211_chandef_vht_oper(&sdata->local->hw, vht_cap_info,
|
||||
&he_oper_vht_cap, ht_oper,
|
||||
&vht_chandef)) {
|
||||
if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HE))
|
||||
@ -232,8 +233,10 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
|
||||
ret = IEEE80211_STA_DISABLE_HE;
|
||||
goto out;
|
||||
}
|
||||
} else if (!ieee80211_chandef_vht_oper(&sdata->local->hw, vht_oper,
|
||||
ht_oper, &vht_chandef)) {
|
||||
} else if (!ieee80211_chandef_vht_oper(&sdata->local->hw,
|
||||
vht_cap_info,
|
||||
vht_oper, ht_oper,
|
||||
&vht_chandef)) {
|
||||
if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT))
|
||||
sdata_info(sdata,
|
||||
"AP VHT information is invalid, disable VHT\n");
|
||||
@ -329,6 +332,7 @@ out:
|
||||
static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata,
|
||||
struct sta_info *sta,
|
||||
const struct ieee80211_ht_cap *ht_cap,
|
||||
const struct ieee80211_vht_cap *vht_cap,
|
||||
const struct ieee80211_ht_operation *ht_oper,
|
||||
const struct ieee80211_vht_operation *vht_oper,
|
||||
const struct ieee80211_he_operation *he_oper,
|
||||
@ -343,6 +347,7 @@ static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata,
|
||||
u16 ht_opmode;
|
||||
u32 flags;
|
||||
enum ieee80211_sta_rx_bandwidth new_sta_bw;
|
||||
u32 vht_cap_info = 0;
|
||||
int ret;
|
||||
|
||||
/* if HT was/is disabled, don't track any bandwidth changes */
|
||||
@ -371,8 +376,11 @@ static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata,
|
||||
sdata->vif.bss_conf.ht_operation_mode = ht_opmode;
|
||||
}
|
||||
|
||||
if (vht_cap)
|
||||
vht_cap_info = le32_to_cpu(vht_cap->vht_cap_info);
|
||||
|
||||
/* calculate new channel (type) based on HT/VHT/HE operation IEs */
|
||||
flags = ieee80211_determine_chantype(sdata, sband, chan,
|
||||
flags = ieee80211_determine_chantype(sdata, sband, chan, vht_cap_info,
|
||||
ht_oper, vht_oper, he_oper,
|
||||
&chandef, true);
|
||||
|
||||
@ -1327,6 +1335,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
|
||||
enum nl80211_band current_band;
|
||||
struct ieee80211_csa_ie csa_ie;
|
||||
struct ieee80211_channel_switch ch_switch;
|
||||
struct ieee80211_bss *bss;
|
||||
int res;
|
||||
|
||||
sdata_assert_lock(sdata);
|
||||
@ -1338,7 +1347,9 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
|
||||
return;
|
||||
|
||||
current_band = cbss->channel->band;
|
||||
bss = (void *)cbss->priv;
|
||||
res = ieee80211_parse_ch_switch_ie(sdata, elems, current_band,
|
||||
bss->vht_cap_info,
|
||||
ifmgd->flags,
|
||||
ifmgd->associated->bssid, &csa_ie);
|
||||
|
||||
@ -4097,8 +4108,8 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
|
||||
|
||||
changed |= ieee80211_recalc_twt_req(sdata, sta, &elems);
|
||||
|
||||
if (ieee80211_config_bw(sdata, sta,
|
||||
elems.ht_cap_elem, elems.ht_operation,
|
||||
if (ieee80211_config_bw(sdata, sta, elems.ht_cap_elem,
|
||||
elems.vht_cap_elem, elems.ht_operation,
|
||||
elems.vht_operation, elems.he_operation,
|
||||
bssid, &changed)) {
|
||||
mutex_unlock(&local->sta_mtx);
|
||||
@ -4815,6 +4826,7 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
|
||||
const struct ieee80211_he_operation *he_oper = NULL;
|
||||
struct ieee80211_supported_band *sband;
|
||||
struct cfg80211_chan_def chandef;
|
||||
struct ieee80211_bss *bss = (void *)cbss->priv;
|
||||
int ret;
|
||||
u32 i;
|
||||
bool have_80mhz;
|
||||
@ -4913,6 +4925,7 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
|
||||
|
||||
ifmgd->flags |= ieee80211_determine_chantype(sdata, sband,
|
||||
cbss->channel,
|
||||
bss->vht_cap_info,
|
||||
ht_oper, vht_oper, he_oper,
|
||||
&chandef, false);
|
||||
|
||||
|
@ -132,6 +132,12 @@ ieee80211_update_bss_from_elems(struct ieee80211_local *local,
|
||||
bss->beacon_rate =
|
||||
&sband->bitrates[rx_status->rate_idx];
|
||||
}
|
||||
|
||||
if (elems->vht_cap_elem)
|
||||
bss->vht_cap_info =
|
||||
le32_to_cpu(elems->vht_cap_elem->vht_cap_info);
|
||||
else
|
||||
bss->vht_cap_info = 0;
|
||||
}
|
||||
|
||||
struct ieee80211_bss *
|
||||
|
@ -9,7 +9,7 @@
|
||||
* Copyright 2007, Michael Wu <flamingice@sourmilk.net>
|
||||
* Copyright 2007-2008, Intel Corporation
|
||||
* Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
|
||||
* Copyright (C) 2018 Intel Corporation
|
||||
* Copyright (C) 2018, 2020 Intel Corporation
|
||||
*/
|
||||
|
||||
#include <linux/ieee80211.h>
|
||||
@ -22,6 +22,7 @@
|
||||
int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee802_11_elems *elems,
|
||||
enum nl80211_band current_band,
|
||||
u32 vht_cap_info,
|
||||
u32 sta_flags, u8 *bssid,
|
||||
struct ieee80211_csa_ie *csa_ie)
|
||||
{
|
||||
@ -150,6 +151,7 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
|
||||
|
||||
/* ignore if parsing fails */
|
||||
if (!ieee80211_chandef_vht_oper(&sdata->local->hw,
|
||||
vht_cap_info,
|
||||
&vht_oper, &ht_oper,
|
||||
&new_vht_chandef))
|
||||
new_vht_chandef.chan = NULL;
|
||||
|
@ -3120,7 +3120,7 @@ bool ieee80211_chandef_ht_oper(const struct ieee80211_ht_operation *ht_oper,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ieee80211_chandef_vht_oper(struct ieee80211_hw *hw,
|
||||
bool ieee80211_chandef_vht_oper(struct ieee80211_hw *hw, u32 vht_cap_info,
|
||||
const struct ieee80211_vht_operation *oper,
|
||||
const struct ieee80211_ht_operation *htop,
|
||||
struct cfg80211_chan_def *chandef)
|
||||
@ -3132,6 +3132,10 @@ bool ieee80211_chandef_vht_oper(struct ieee80211_hw *hw,
|
||||
u32 vht_cap;
|
||||
bool support_80_80 = false;
|
||||
bool support_160 = false;
|
||||
u8 ext_nss_bw_supp = u32_get_bits(vht_cap_info,
|
||||
IEEE80211_VHT_CAP_EXT_NSS_BW_MASK);
|
||||
u8 supp_chwidth = u32_get_bits(vht_cap_info,
|
||||
IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK);
|
||||
|
||||
if (!oper || !htop)
|
||||
return false;
|
||||
@ -3151,11 +3155,48 @@ bool ieee80211_chandef_vht_oper(struct ieee80211_hw *hw,
|
||||
IEEE80211_HT_OP_MODE_CCFS2_MASK)
|
||||
>> IEEE80211_HT_OP_MODE_CCFS2_SHIFT;
|
||||
|
||||
/* when parsing (and we know how to) CCFS1 and CCFS2 are equivalent */
|
||||
ccf0 = ccfs0;
|
||||
ccf1 = ccfs1;
|
||||
if (!ccfs1 && ieee80211_hw_check(hw, SUPPORTS_VHT_EXT_NSS_BW))
|
||||
|
||||
/* if not supported, parse as though we didn't understand it */
|
||||
if (!ieee80211_hw_check(hw, SUPPORTS_VHT_EXT_NSS_BW))
|
||||
ext_nss_bw_supp = 0;
|
||||
|
||||
/*
|
||||
* Cf. IEEE 802.11 Table 9-250
|
||||
*
|
||||
* We really just consider that because it's inefficient to connect
|
||||
* at a higher bandwidth than we'll actually be able to use.
|
||||
*/
|
||||
switch ((supp_chwidth << 4) | ext_nss_bw_supp) {
|
||||
default:
|
||||
case 0x00:
|
||||
ccf1 = 0;
|
||||
support_160 = false;
|
||||
support_80_80 = false;
|
||||
break;
|
||||
case 0x01:
|
||||
support_80_80 = false;
|
||||
/* fall through */
|
||||
case 0x02:
|
||||
case 0x03:
|
||||
ccf1 = ccfs2;
|
||||
break;
|
||||
case 0x10:
|
||||
ccf1 = ccfs1;
|
||||
break;
|
||||
case 0x11:
|
||||
case 0x12:
|
||||
if (!ccfs1)
|
||||
ccf1 = ccfs2;
|
||||
else
|
||||
ccf1 = ccfs1;
|
||||
break;
|
||||
case 0x13:
|
||||
case 0x20:
|
||||
case 0x23:
|
||||
ccf1 = ccfs1;
|
||||
break;
|
||||
}
|
||||
|
||||
cf0 = ieee80211_channel_to_frequency(ccf0, chandef->chan->band);
|
||||
cf1 = ieee80211_channel_to_frequency(ccf1, chandef->chan->band);
|
||||
|
Loading…
Reference in New Issue
Block a user