wifi: mac80211: clean up connection process

Rewrite the station-side connection handling. The connection
flags (IEEE80211_DISABLE_*) are rather confusing, and they're
not always maintained well. Additionally, for wider-bandwidth
OFDMA support we need to know the precise bandwidth of the AP,
which is currently somewhat difficult.

Rewrite this to have a 'mode' (S1G/legacy/HT/...) and a limit
on the bandwidth. This is not entirely clean because some of
those modes aren't completely sequenced (as this assumes in
some places), e.g. VHT doesn't exist on 2.4 GHz, but HE does.
However, it still simplifies things and gives us a good idea
what we're operating as, so we can parse elements accordingly
etc.

This leaves a FIXME for puncturing, this is addressed in a
later patch.

Reviewed-by: Ilan Peer <ilan.peer@intel.com>
Reviewed-by: Miriam Rachel Korenblit <miriam.rachel.korenblit@intel.com>
Link: https://msgid.link/20240129194108.9451722c0110.I3e61f4cfe9da89008e1854160093c76a1e69dc2a@changeid
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
Johannes Berg 2024-01-29 19:34:36 +01:00
parent 61f0261131
commit 310c8387c6
10 changed files with 1328 additions and 1057 deletions

View File

@ -382,7 +382,7 @@ _ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local,
/* downgrade chandef up to max_bw */
min_def = ctx->conf.def;
while (min_def.width > max_bw)
ieee80211_chandef_downgrade(&min_def);
ieee80211_chandef_downgrade(&min_def, NULL);
if (cfg80211_chandef_identical(&ctx->conf.min_def, &min_def))
return 0;

View File

@ -152,16 +152,17 @@ do { \
else \
_sdata_err((link)->sdata, fmt, ##__VA_ARGS__); \
} while (0)
#define link_dbg(link, fmt, ...) \
#define _link_id_dbg(print, sdata, link_id, fmt, ...) \
do { \
if (ieee80211_vif_is_mld(&(link)->sdata->vif)) \
_sdata_dbg(1, (link)->sdata, "[link %d] " fmt, \
(link)->link_id, \
##__VA_ARGS__); \
if (ieee80211_vif_is_mld(&(sdata)->vif)) \
_sdata_dbg(print, sdata, "[link %d] " fmt, \
link_id, ##__VA_ARGS__); \
else \
_sdata_dbg(1, (link)->sdata, fmt, \
##__VA_ARGS__); \
_sdata_dbg(1, sdata, fmt, ##__VA_ARGS__); \
} while (0)
#define link_dbg(link, fmt, ...) \
_link_id_dbg(1, (link)->sdata, (link)->link_id, \
fmt, ##__VA_ARGS__)
#define ht_dbg(sdata, fmt, ...) \
_sdata_dbg(MAC80211_HT_DEBUG, \
@ -226,6 +227,9 @@ do { \
#define mlme_dbg(sdata, fmt, ...) \
_sdata_dbg(MAC80211_MLME_DEBUG, \
sdata, fmt, ##__VA_ARGS__)
#define mlme_link_id_dbg(sdata, link_id, fmt, ...) \
_link_id_dbg(MAC80211_MLME_DEBUG, sdata, link_id, \
fmt, ##__VA_ARGS__)
#define mlme_dbg_ratelimited(sdata, fmt, ...) \
_sdata_dbg(MAC80211_MLME_DEBUG && net_ratelimit(), \

View File

@ -757,21 +757,22 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
enum nl80211_channel_type ch_type;
int err;
ieee80211_conn_flags_t conn_flags;
struct ieee80211_conn_settings conn = {
.mode = IEEE80211_CONN_MODE_HT,
.bw_limit = IEEE80211_CONN_BW_LIMIT_40,
};
u32 vht_cap_info = 0;
lockdep_assert_wiphy(sdata->local->hw.wiphy);
conn_flags = IEEE80211_CONN_DISABLE_VHT;
switch (ifibss->chandef.width) {
case NL80211_CHAN_WIDTH_5:
case NL80211_CHAN_WIDTH_10:
case NL80211_CHAN_WIDTH_20_NOHT:
conn_flags |= IEEE80211_CONN_DISABLE_HT;
conn.mode = IEEE80211_CONN_MODE_LEGACY;
fallthrough;
case NL80211_CHAN_WIDTH_20:
conn_flags |= IEEE80211_CONN_DISABLE_40MHZ;
conn.bw_limit = IEEE80211_CONN_BW_LIMIT_20;
break;
default:
break;
@ -783,8 +784,8 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
memset(&params, 0, sizeof(params));
err = ieee80211_parse_ch_switch_ie(sdata, elems,
ifibss->chandef.chan->band,
vht_cap_info,
conn_flags, ifibss->bssid, &csa_ie);
vht_cap_info, &conn,
ifibss->bssid, &csa_ie);
/* can't switch to destination channel, fail */
if (err < 0)
goto disconnect;

View File

@ -370,19 +370,32 @@ enum ieee80211_sta_flags {
IEEE80211_STA_ENABLE_RRM = BIT(15),
};
typedef u32 __bitwise ieee80211_conn_flags_t;
enum ieee80211_conn_flags {
IEEE80211_CONN_DISABLE_HT = (__force ieee80211_conn_flags_t)BIT(0),
IEEE80211_CONN_DISABLE_40MHZ = (__force ieee80211_conn_flags_t)BIT(1),
IEEE80211_CONN_DISABLE_VHT = (__force ieee80211_conn_flags_t)BIT(2),
IEEE80211_CONN_DISABLE_80P80MHZ = (__force ieee80211_conn_flags_t)BIT(3),
IEEE80211_CONN_DISABLE_160MHZ = (__force ieee80211_conn_flags_t)BIT(4),
IEEE80211_CONN_DISABLE_HE = (__force ieee80211_conn_flags_t)BIT(5),
IEEE80211_CONN_DISABLE_EHT = (__force ieee80211_conn_flags_t)BIT(6),
IEEE80211_CONN_DISABLE_320MHZ = (__force ieee80211_conn_flags_t)BIT(7),
enum ieee80211_conn_mode {
IEEE80211_CONN_MODE_S1G,
IEEE80211_CONN_MODE_LEGACY,
IEEE80211_CONN_MODE_HT,
IEEE80211_CONN_MODE_VHT,
IEEE80211_CONN_MODE_HE,
IEEE80211_CONN_MODE_EHT,
};
#define IEEE80211_CONN_MODE_HIGHEST IEEE80211_CONN_MODE_EHT
enum ieee80211_conn_bw_limit {
IEEE80211_CONN_BW_LIMIT_20,
IEEE80211_CONN_BW_LIMIT_40,
IEEE80211_CONN_BW_LIMIT_80,
IEEE80211_CONN_BW_LIMIT_160, /* also 80+80 */
IEEE80211_CONN_BW_LIMIT_320,
};
struct ieee80211_conn_settings {
enum ieee80211_conn_mode mode;
enum ieee80211_conn_bw_limit bw_limit;
};
extern const struct ieee80211_conn_settings ieee80211_conn_settings_unlimited;
struct ieee80211_mgd_auth_data {
struct cfg80211_bss *bss;
unsigned long timeout;
@ -416,7 +429,7 @@ struct ieee80211_mgd_assoc_data {
size_t elems_len;
u8 *elems; /* pointing to inside ie[] below */
ieee80211_conn_flags_t conn_flags;
struct ieee80211_conn_settings conn;
u16 status;
@ -943,7 +956,7 @@ struct ieee80211_link_data_managed {
enum ieee80211_smps_mode req_smps, /* requested smps mode */
driver_smps_mode; /* smps mode request */
ieee80211_conn_flags_t conn_flags;
struct ieee80211_conn_settings conn;
s16 p2p_noa_index;
@ -2171,9 +2184,8 @@ void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
* @elems: parsed 802.11 elements received with the frame
* @current_band: indicates the current band
* @vht_cap_info: VHT capabilities of the transmitter
* @conn_flags: contains information about own capabilities and restrictions
* to decide which channel switch announcements can be accepted, using
* flags from &enum ieee80211_conn_flags.
* @conn: contains information about own capabilities and restrictions
* to decide which channel switch announcements can be accepted
* @bssid: the currently connected bssid (for reporting)
* @csa_ie: parsed 802.11 csa elements on count, mode, chandef and mesh ttl.
* All of them will be filled with if success only.
@ -2183,7 +2195,8 @@ 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,
ieee80211_conn_flags_t conn_flags, u8 *bssid,
struct ieee80211_conn_settings *conn,
u8 *bssid,
struct ieee80211_csa_ie *csa_ie);
/* Suspend/resume and hw reconfiguration */
@ -2207,6 +2220,9 @@ static inline int __ieee80211_resume(struct ieee80211_hw *hw)
/* utility functions/constants */
extern const void *const mac80211_wiphy_privid; /* for wiphy privid */
const char *ieee80211_conn_mode_str(enum ieee80211_conn_mode mode);
enum ieee80211_conn_bw_limit
ieee80211_min_bw_limit_from_chandef(struct cfg80211_chan_def *chandef);
int ieee80211_frame_duration(enum nl80211_band band, size_t len,
int rate, int erp, int short_preamble);
void ieee80211_regulatory_limit_wmm_params(struct ieee80211_sub_if_data *sdata,
@ -2248,6 +2264,7 @@ static inline void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata,
/**
* struct ieee80211_elems_parse_params - element parsing parameters
* @mode: connection mode for parsing
* @start: pointer to the elements
* @len: length of the elements
* @action: %true if the elements came from an action frame
@ -2265,6 +2282,7 @@ static inline void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata,
* for EHT capabilities parsing)
*/
struct ieee80211_elems_parse_params {
enum ieee80211_conn_mode mode;
const u8 *start;
size_t len;
bool action;
@ -2284,6 +2302,7 @@ ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
struct cfg80211_bss *bss)
{
struct ieee80211_elems_parse_params params = {
.mode = IEEE80211_CONN_MODE_HIGHEST,
.start = start,
.len = len,
.action = action,
@ -2459,9 +2478,9 @@ u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
u8 *ieee80211_ie_build_vht_oper(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
const struct cfg80211_chan_def *chandef);
u8 ieee80211_ie_len_he_cap(struct ieee80211_sub_if_data *sdata, u8 iftype);
u8 *ieee80211_ie_build_he_cap(ieee80211_conn_flags_t disable_flags, u8 *pos,
u8 *ieee80211_ie_build_he_cap(const struct ieee80211_conn_settings *conn,
const struct ieee80211_sta_he_cap *he_cap,
u8 *end);
u8 *pos, u8 *end);
void ieee80211_ie_build_he_6ghz_cap(struct ieee80211_sub_if_data *sdata,
enum ieee80211_smps_mode smps_mode,
struct sk_buff *skb);
@ -2501,7 +2520,8 @@ bool ieee80211_chandef_he_6ghz_oper(struct ieee80211_sub_if_data *sdata,
struct cfg80211_chan_def *chandef);
bool ieee80211_chandef_s1g_oper(const struct ieee80211_s1g_oper_ie *oper,
struct cfg80211_chan_def *chandef);
ieee80211_conn_flags_t ieee80211_chandef_downgrade(struct cfg80211_chan_def *c);
void ieee80211_chandef_downgrade(struct cfg80211_chan_def *chandef,
struct ieee80211_conn_settings *conn);
int __must_check
ieee80211_link_use_channel(struct ieee80211_link_data *link,

View File

@ -586,7 +586,7 @@ int mesh_add_he_cap_ie(struct ieee80211_sub_if_data *sdata,
return -ENOMEM;
pos = skb_put(skb, ie_len);
ieee80211_ie_build_he_cap(0, pos, he_cap, pos + ie_len);
ieee80211_ie_build_he_cap(NULL, he_cap, pos, pos + ie_len);
return 0;
}
@ -1292,7 +1292,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;
ieee80211_conn_flags_t conn_flags = 0;
struct ieee80211_conn_settings conn = ieee80211_conn_settings_unlimited;
u32 vht_cap_info = 0;
lockdep_assert_wiphy(sdata->local->hw.wiphy);
@ -1303,13 +1303,16 @@ ieee80211_mesh_process_chnswitch(struct ieee80211_sub_if_data *sdata,
switch (sdata->vif.bss_conf.chandef.width) {
case NL80211_CHAN_WIDTH_20_NOHT:
conn_flags |= IEEE80211_CONN_DISABLE_HT;
fallthrough;
conn.mode = IEEE80211_CONN_MODE_LEGACY;
conn.bw_limit = IEEE80211_CONN_BW_LIMIT_20;
break;
case NL80211_CHAN_WIDTH_20:
conn_flags |= IEEE80211_CONN_DISABLE_40MHZ;
fallthrough;
conn.mode = IEEE80211_CONN_MODE_HT;
conn.bw_limit = IEEE80211_CONN_BW_LIMIT_20;
break;
case NL80211_CHAN_WIDTH_40:
conn_flags |= IEEE80211_CONN_DISABLE_VHT;
conn.mode = IEEE80211_CONN_MODE_HT;
conn.bw_limit = IEEE80211_CONN_BW_LIMIT_40;
break;
default:
break;
@ -1321,8 +1324,8 @@ ieee80211_mesh_process_chnswitch(struct ieee80211_sub_if_data *sdata,
memset(&params, 0, sizeof(params));
err = ieee80211_parse_ch_switch_ie(sdata, elems, sband->band,
vht_cap_info,
conn_flags, sdata->vif.addr,
vht_cap_info, &conn,
sdata->vif.addr,
&csa_ie);
if (err < 0)
return false;

File diff suppressed because it is too large Load Diff

View File

@ -23,7 +23,8 @@ 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,
ieee80211_conn_flags_t conn_flags, u8 *bssid,
struct ieee80211_conn_settings *conn,
u8 *bssid,
struct ieee80211_csa_ie *csa_ie)
{
enum nl80211_band new_band = current_band;
@ -42,13 +43,13 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
wide_bw_chansw_ie = elems->wide_bw_chansw_ie;
bwi = elems->bandwidth_indication;
if (conn_flags & (IEEE80211_CONN_DISABLE_HT |
IEEE80211_CONN_DISABLE_40MHZ)) {
if (conn->mode < IEEE80211_CONN_MODE_HT ||
conn->bw_limit < IEEE80211_CONN_BW_LIMIT_40) {
sec_chan_offs = NULL;
wide_bw_chansw_ie = NULL;
}
if (conn_flags & IEEE80211_CONN_DISABLE_VHT)
if (conn->mode < IEEE80211_CONN_MODE_VHT)
wide_bw_chansw_ie = NULL;
if (elems->ext_chansw_ie) {
@ -95,7 +96,7 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
if (sec_chan_offs) {
secondary_channel_offset = sec_chan_offs->sec_chan_offs;
} else if (!(conn_flags & IEEE80211_CONN_DISABLE_HT)) {
} else if (conn->mode >= IEEE80211_CONN_MODE_HT) {
/* If the secondary channel offset IE is not present,
* we can't know what's the post-CSA offset, so the
* best we can do is use 20MHz.
@ -169,12 +170,10 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
&new_vht_chandef))
new_vht_chandef.chan = NULL;
if (conn_flags & IEEE80211_CONN_DISABLE_80P80MHZ &&
new_vht_chandef.width == NL80211_CHAN_WIDTH_80P80)
ieee80211_chandef_downgrade(&new_vht_chandef);
if (conn_flags & IEEE80211_CONN_DISABLE_160MHZ &&
new_vht_chandef.width == NL80211_CHAN_WIDTH_160)
ieee80211_chandef_downgrade(&new_vht_chandef);
if (conn->bw_limit < IEEE80211_CONN_BW_LIMIT_160 &&
(new_vht_chandef.width == NL80211_CHAN_WIDTH_80P80 ||
new_vht_chandef.width == NL80211_CHAN_WIDTH_160))
ieee80211_chandef_downgrade(&new_vht_chandef, NULL);
}
/* if VHT data is there validate & use it */

View File

@ -347,7 +347,7 @@ ieee80211_tdls_chandef_vht_upgrade(struct ieee80211_sub_if_data *sdata,
(uc.width > sta->tdls_chandef.width &&
!cfg80211_reg_can_beacon_relax(sdata->local->hw.wiphy, &uc,
sdata->wdev.iftype)))
ieee80211_chandef_downgrade(&uc);
ieee80211_chandef_downgrade(&uc, NULL);
if (!cfg80211_chandef_identical(&uc, &sta->tdls_chandef)) {
tdls_dbg(sdata, "TDLS ch width upgraded %d -> %d\n",
@ -561,7 +561,7 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_link_data *link,
ieee80211_he_ppe_size(he_cap->ppe_thres[0],
he_cap->he_cap_elem.phy_cap_info);
pos = skb_put(skb, cap_size);
pos = ieee80211_ie_build_he_cap(0, pos, he_cap, pos + cap_size);
pos = ieee80211_ie_build_he_cap(NULL, he_cap, pos, pos + cap_size);
/* Build HE 6Ghz capa IE from sband */
if (sband->band == NL80211_BAND_6GHZ) {
@ -1413,8 +1413,8 @@ iee80211_tdls_recalc_ht_protection(struct ieee80211_sub_if_data *sdata,
IEEE80211_HT_OP_MODE_NON_HT_STA_PRSNT;
u16 opmode;
/* Nothing to do if the BSS connection uses HT */
if (!(sdata->deflink.u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HT))
/* Nothing to do if the BSS connection uses (at least) HT */
if (sdata->deflink.u.mgd.conn.mode >= IEEE80211_CONN_MODE_HT)
return;
tdls_ht = (sta && sta->sta.deflink.ht_cap.ht_supported) ||

View File

@ -14,6 +14,7 @@ static void mle_defrag(struct kunit *test)
struct ieee80211_elems_parse_params parse_params = {
.link_id = 12,
.from_ap = true,
.mode = IEEE80211_CONN_MODE_EHT,
};
struct ieee802_11_elems *parsed;
struct sk_buff *skb;

View File

@ -46,6 +46,11 @@ struct ieee80211_hw *wiphy_to_ieee80211_hw(struct wiphy *wiphy)
}
EXPORT_SYMBOL(wiphy_to_ieee80211_hw);
const struct ieee80211_conn_settings ieee80211_conn_settings_unlimited = {
.mode = IEEE80211_CONN_MODE_EHT,
.bw_limit = IEEE80211_CONN_BW_LIMIT_320,
};
u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len,
enum nl80211_iftype type)
{
@ -929,23 +934,31 @@ ieee80211_parse_extension_element(u32 *crc,
switch (elem->data[0]) {
case WLAN_EID_EXT_HE_MU_EDCA:
if (params->mode < IEEE80211_CONN_MODE_HE)
break;
calc_crc = true;
if (len >= sizeof(*elems->mu_edca_param_set))
elems->mu_edca_param_set = data;
break;
case WLAN_EID_EXT_HE_CAPABILITY:
if (params->mode < IEEE80211_CONN_MODE_HE)
break;
if (ieee80211_he_capa_size_ok(data, len)) {
elems->he_cap = data;
elems->he_cap_len = len;
}
break;
case WLAN_EID_EXT_HE_OPERATION:
if (params->mode < IEEE80211_CONN_MODE_HE)
break;
calc_crc = true;
if (len >= sizeof(*elems->he_operation) &&
len >= ieee80211_he_oper_size(data) - 1)
elems->he_operation = data;
break;
case WLAN_EID_EXT_UORA:
if (params->mode < IEEE80211_CONN_MODE_HE)
break;
if (len >= 1)
elems->uora_element = data;
break;
@ -958,15 +971,21 @@ ieee80211_parse_extension_element(u32 *crc,
elems->mbssid_config_ie = data;
break;
case WLAN_EID_EXT_HE_SPR:
if (params->mode < IEEE80211_CONN_MODE_HE)
break;
if (len >= sizeof(*elems->he_spr) &&
len >= ieee80211_he_spr_size(data))
elems->he_spr = data;
break;
case WLAN_EID_EXT_HE_6GHZ_CAPA:
if (params->mode < IEEE80211_CONN_MODE_HE)
break;
if (len >= sizeof(*elems->he_6ghz_capa))
elems->he_6ghz_capa = data;
break;
case WLAN_EID_EXT_EHT_CAPABILITY:
if (params->mode < IEEE80211_CONN_MODE_EHT)
break;
if (ieee80211_eht_capa_size_ok(elems->he_cap,
data, len,
params->from_ap)) {
@ -975,11 +994,15 @@ ieee80211_parse_extension_element(u32 *crc,
}
break;
case WLAN_EID_EXT_EHT_OPERATION:
if (params->mode < IEEE80211_CONN_MODE_EHT)
break;
if (ieee80211_eht_oper_size_ok(data, len))
elems->eht_operation = data;
calc_crc = true;
break;
case WLAN_EID_EXT_EHT_MULTI_LINK:
if (params->mode < IEEE80211_CONN_MODE_EHT)
break;
calc_crc = true;
if (ieee80211_mle_size_ok(data, len)) {
@ -1004,11 +1027,15 @@ ieee80211_parse_extension_element(u32 *crc,
}
break;
case WLAN_EID_EXT_BANDWIDTH_INDICATION:
if (params->mode < IEEE80211_CONN_MODE_EHT)
break;
if (ieee80211_bandwidth_indication_size_ok(data, len))
elems->bandwidth_indication = data;
calc_crc = true;
break;
case WLAN_EID_EXT_TID_TO_LINK_MAPPING:
if (params->mode < IEEE80211_CONN_MODE_EHT)
break;
calc_crc = true;
if (ieee80211_tid_to_link_map_size_ok(data, len) &&
elems->ttlm_num < ARRAY_SIZE(elems->ttlm)) {
@ -1178,24 +1205,32 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params,
elems->ext_supp_rates_len = elen;
break;
case WLAN_EID_HT_CAPABILITY:
if (params->mode < IEEE80211_CONN_MODE_HT)
break;
if (elen >= sizeof(struct ieee80211_ht_cap))
elems->ht_cap_elem = (void *)pos;
else
elem_parse_failed = true;
break;
case WLAN_EID_HT_OPERATION:
if (params->mode < IEEE80211_CONN_MODE_HT)
break;
if (elen >= sizeof(struct ieee80211_ht_operation))
elems->ht_operation = (void *)pos;
else
elem_parse_failed = true;
break;
case WLAN_EID_VHT_CAPABILITY:
if (params->mode < IEEE80211_CONN_MODE_VHT)
break;
if (elen >= sizeof(struct ieee80211_vht_cap))
elems->vht_cap_elem = (void *)pos;
else
elem_parse_failed = true;
break;
case WLAN_EID_VHT_OPERATION:
if (params->mode < IEEE80211_CONN_MODE_VHT)
break;
if (elen >= sizeof(struct ieee80211_vht_operation)) {
elems->vht_operation = (void *)pos;
if (calc_crc)
@ -1205,6 +1240,8 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params,
elem_parse_failed = true;
break;
case WLAN_EID_OPMODE_NOTIF:
if (params->mode < IEEE80211_CONN_MODE_VHT)
break;
if (elen > 0) {
elems->opmode_notif = pos;
if (calc_crc)
@ -1264,6 +1301,8 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params,
elems->ext_chansw_ie = (void *)pos;
break;
case WLAN_EID_SECONDARY_CHANNEL_OFFSET:
if (params->mode < IEEE80211_CONN_MODE_HT)
break;
if (elen != sizeof(struct ieee80211_sec_chan_offs_ie)) {
elem_parse_failed = true;
break;
@ -1279,6 +1318,8 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params,
elems->mesh_chansw_params_ie = (void *)pos;
break;
case WLAN_EID_WIDE_BW_CHANNEL_SWITCH:
if (params->mode < IEEE80211_CONN_MODE_VHT)
break;
if (!params->action ||
elen < sizeof(*elems->wide_bw_chansw_ie)) {
elem_parse_failed = true;
@ -1287,6 +1328,8 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params,
elems->wide_bw_chansw_ie = (void *)pos;
break;
case WLAN_EID_CHANNEL_SWITCH_WRAPPER:
if (params->mode < IEEE80211_CONN_MODE_VHT)
break;
if (params->action) {
elem_parse_failed = true;
break;
@ -1305,6 +1348,9 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params,
elem_parse_failed = true;
}
if (params->mode < IEEE80211_CONN_MODE_EHT)
break;
subelem = cfg80211_find_ext_elem(WLAN_EID_EXT_BANDWIDTH_INDICATION,
pos, elen);
if (subelem) {
@ -1393,24 +1439,32 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params,
elem, elems, params);
break;
case WLAN_EID_S1G_CAPABILITIES:
if (params->mode != IEEE80211_CONN_MODE_S1G)
break;
if (elen >= sizeof(*elems->s1g_capab))
elems->s1g_capab = (void *)pos;
else
elem_parse_failed = true;
break;
case WLAN_EID_S1G_OPERATION:
if (params->mode != IEEE80211_CONN_MODE_S1G)
break;
if (elen == sizeof(*elems->s1g_oper))
elems->s1g_oper = (void *)pos;
else
elem_parse_failed = true;
break;
case WLAN_EID_S1G_BCN_COMPAT:
if (params->mode != IEEE80211_CONN_MODE_S1G)
break;
if (elen == sizeof(*elems->s1g_bcn_compat))
elems->s1g_bcn_compat = (void *)pos;
else
elem_parse_failed = true;
break;
case WLAN_EID_AID_RESPONSE:
if (params->mode != IEEE80211_CONN_MODE_S1G)
break;
if (elen == sizeof(struct ieee80211_aid_response_ie))
elems->aid_resp = (void *)pos;
else
@ -1562,6 +1616,7 @@ static void ieee80211_mle_parse_link(struct ieee802_11_elems *elems,
{
struct ieee80211_mle_per_sta_profile *prof;
struct ieee80211_elems_parse_params sub = {
.mode = params->mode,
.action = params->action,
.from_ap = params->from_ap,
.link_id = -1,
@ -1649,6 +1704,7 @@ ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params)
/* Override with nontransmitted profile, if found */
if (nontransmitted_profile_len) {
struct ieee80211_elems_parse_params sub = {
.mode = params->mode,
.start = nontransmitted_profile,
.len = nontransmitted_profile_len,
.action = params->action,
@ -2142,7 +2198,7 @@ static int ieee80211_build_preq_ies_band(struct ieee80211_sub_if_data *sdata,
if (he_cap &&
cfg80211_any_usable_channels(local->hw.wiphy, BIT(sband->band),
IEEE80211_CHAN_NO_HE)) {
pos = ieee80211_ie_build_he_cap(0, pos, he_cap, end);
pos = ieee80211_ie_build_he_cap(NULL, he_cap, pos, end);
if (!pos)
goto out_err;
}
@ -3201,15 +3257,18 @@ u8 ieee80211_ie_len_he_cap(struct ieee80211_sub_if_data *sdata, u8 iftype)
he_cap->he_cap_elem.phy_cap_info);
}
u8 *ieee80211_ie_build_he_cap(ieee80211_conn_flags_t disable_flags, u8 *pos,
u8 *ieee80211_ie_build_he_cap(const struct ieee80211_conn_settings *conn,
const struct ieee80211_sta_he_cap *he_cap,
u8 *end)
u8 *pos, u8 *end)
{
struct ieee80211_he_cap_elem elem;
u8 n;
u8 ie_len;
u8 *orig_pos = pos;
if (!conn)
conn = &ieee80211_conn_settings_unlimited;
/* Make sure we have place for the IE */
/*
* TODO: the 1 added is because this temporarily is under the EXTENSION
@ -3221,18 +3280,15 @@ u8 *ieee80211_ie_build_he_cap(ieee80211_conn_flags_t disable_flags, u8 *pos,
/* modify on stack first to calculate 'n' and 'ie_len' correctly */
elem = he_cap->he_cap_elem;
if (disable_flags & IEEE80211_CONN_DISABLE_40MHZ)
if (conn->bw_limit < IEEE80211_CONN_BW_LIMIT_40)
elem.phy_cap_info[0] &=
~(IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G |
IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G);
if (disable_flags & IEEE80211_CONN_DISABLE_160MHZ)
if (conn->bw_limit < IEEE80211_CONN_BW_LIMIT_160)
elem.phy_cap_info[0] &=
~IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
if (disable_flags & IEEE80211_CONN_DISABLE_80P80MHZ)
elem.phy_cap_info[0] &=
~IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G;
~(IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G |
IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G);
n = ieee80211_he_mcs_nss_size(&elem);
ie_len = 2 + 1 +
@ -4367,21 +4423,32 @@ void ieee80211_radar_detected(struct ieee80211_hw *hw)
}
EXPORT_SYMBOL(ieee80211_radar_detected);
ieee80211_conn_flags_t ieee80211_chandef_downgrade(struct cfg80211_chan_def *c)
void ieee80211_chandef_downgrade(struct cfg80211_chan_def *c,
struct ieee80211_conn_settings *conn)
{
ieee80211_conn_flags_t ret;
struct ieee80211_conn_settings _ignored = {};
int tmp;
/* allow passing NULL if caller doesn't care */
if (!conn)
conn = &_ignored;
switch (c->width) {
default:
case NL80211_CHAN_WIDTH_20_NOHT:
WARN_ON_ONCE(1);
fallthrough;
case NL80211_CHAN_WIDTH_20:
c->width = NL80211_CHAN_WIDTH_20_NOHT;
ret = IEEE80211_CONN_DISABLE_HT | IEEE80211_CONN_DISABLE_VHT;
conn->mode = IEEE80211_CONN_MODE_LEGACY;
conn->bw_limit = IEEE80211_CONN_BW_LIMIT_20;
break;
case NL80211_CHAN_WIDTH_40:
c->width = NL80211_CHAN_WIDTH_20;
c->center_freq1 = c->chan->center_freq;
ret = IEEE80211_CONN_DISABLE_40MHZ |
IEEE80211_CONN_DISABLE_VHT;
if (conn->mode == IEEE80211_CONN_MODE_VHT)
conn->mode = IEEE80211_CONN_MODE_HT;
conn->bw_limit = IEEE80211_CONN_BW_LIMIT_20;
break;
case NL80211_CHAN_WIDTH_80:
tmp = (30 + c->chan->center_freq - c->center_freq1)/20;
@ -4390,13 +4457,14 @@ ieee80211_conn_flags_t ieee80211_chandef_downgrade(struct cfg80211_chan_def *c)
/* freq_P40 */
c->center_freq1 = c->center_freq1 - 20 + 40 * tmp;
c->width = NL80211_CHAN_WIDTH_40;
ret = IEEE80211_CONN_DISABLE_VHT;
if (conn->mode == IEEE80211_CONN_MODE_VHT)
conn->mode = IEEE80211_CONN_MODE_HT;
conn->bw_limit = IEEE80211_CONN_BW_LIMIT_40;
break;
case NL80211_CHAN_WIDTH_80P80:
c->center_freq2 = 0;
c->width = NL80211_CHAN_WIDTH_80;
ret = IEEE80211_CONN_DISABLE_80P80MHZ |
IEEE80211_CONN_DISABLE_160MHZ;
conn->bw_limit = IEEE80211_CONN_BW_LIMIT_80;
break;
case NL80211_CHAN_WIDTH_160:
/* n_P20 */
@ -4405,8 +4473,7 @@ ieee80211_conn_flags_t ieee80211_chandef_downgrade(struct cfg80211_chan_def *c)
tmp /= 4;
c->center_freq1 = c->center_freq1 - 40 + 80 * tmp;
c->width = NL80211_CHAN_WIDTH_80;
ret = IEEE80211_CONN_DISABLE_80P80MHZ |
IEEE80211_CONN_DISABLE_160MHZ;
conn->bw_limit = IEEE80211_CONN_BW_LIMIT_80;
break;
case NL80211_CHAN_WIDTH_320:
/* n_P20 */
@ -4415,30 +4482,28 @@ ieee80211_conn_flags_t ieee80211_chandef_downgrade(struct cfg80211_chan_def *c)
tmp /= 8;
c->center_freq1 = c->center_freq1 - 80 + 160 * tmp;
c->width = NL80211_CHAN_WIDTH_160;
ret = IEEE80211_CONN_DISABLE_320MHZ;
break;
default:
case NL80211_CHAN_WIDTH_20_NOHT:
WARN_ON_ONCE(1);
c->width = NL80211_CHAN_WIDTH_20_NOHT;
ret = IEEE80211_CONN_DISABLE_HT | IEEE80211_CONN_DISABLE_VHT;
conn->bw_limit = IEEE80211_CONN_BW_LIMIT_160;
break;
case NL80211_CHAN_WIDTH_1:
case NL80211_CHAN_WIDTH_2:
case NL80211_CHAN_WIDTH_4:
case NL80211_CHAN_WIDTH_8:
case NL80211_CHAN_WIDTH_16:
WARN_ON_ONCE(1);
/* keep c->width */
conn->mode = IEEE80211_CONN_MODE_S1G;
conn->bw_limit = IEEE80211_CONN_BW_LIMIT_20;
break;
case NL80211_CHAN_WIDTH_5:
case NL80211_CHAN_WIDTH_10:
WARN_ON_ONCE(1);
/* keep c->width */
ret = IEEE80211_CONN_DISABLE_HT | IEEE80211_CONN_DISABLE_VHT;
conn->mode = IEEE80211_CONN_MODE_LEGACY;
conn->bw_limit = IEEE80211_CONN_BW_LIMIT_20;
break;
}
WARN_ON_ONCE(!cfg80211_chandef_valid(c));
return ret;
}
/*
@ -5090,3 +5155,42 @@ u8 *ieee80211_ie_build_eht_cap(u8 *pos,
return pos;
}
const char *ieee80211_conn_mode_str(enum ieee80211_conn_mode mode)
{
static const char * const modes[] = {
[IEEE80211_CONN_MODE_S1G] = "S1G",
[IEEE80211_CONN_MODE_LEGACY] = "legacy",
[IEEE80211_CONN_MODE_HT] = "HT",
[IEEE80211_CONN_MODE_VHT] = "VHT",
[IEEE80211_CONN_MODE_HE] = "HE",
[IEEE80211_CONN_MODE_EHT] = "EHT",
};
if (WARN_ON(mode >= ARRAY_SIZE(modes)))
return "<out of range>";
return modes[mode] ?: "<missing string>";
}
enum ieee80211_conn_bw_limit
ieee80211_min_bw_limit_from_chandef(struct cfg80211_chan_def *chandef)
{
switch (chandef->width) {
case NL80211_CHAN_WIDTH_20_NOHT:
case NL80211_CHAN_WIDTH_20:
return IEEE80211_CONN_BW_LIMIT_20;
case NL80211_CHAN_WIDTH_40:
return IEEE80211_CONN_BW_LIMIT_40;
case NL80211_CHAN_WIDTH_80:
return IEEE80211_CONN_BW_LIMIT_80;
case NL80211_CHAN_WIDTH_80P80:
case NL80211_CHAN_WIDTH_160:
return IEEE80211_CONN_BW_LIMIT_160;
case NL80211_CHAN_WIDTH_320:
return IEEE80211_CONN_BW_LIMIT_320;
default:
WARN(1, "unhandled chandef width %d\n", chandef->width);
return IEEE80211_CONN_BW_LIMIT_20;
}
}