diff --git a/include/linux/ieee80211-eht.h b/include/linux/ieee80211-eht.h index 18f9c662cf4c..928811328bb0 100644 --- a/include/linux/ieee80211-eht.h +++ b/include/linux/ieee80211-eht.h @@ -481,6 +481,7 @@ struct ieee80211_multi_link_elem { #define IEEE80211_MLC_BASIC_PRES_MLD_CAPA_OP 0x0100 #define IEEE80211_MLC_BASIC_PRES_MLD_ID 0x0200 #define IEEE80211_MLC_BASIC_PRES_EXT_MLD_CAPA_OP 0x0400 +#define IEEE80211_MLC_BASIC_PRES_ENH_CRIT_UPD 0x0800 #define IEEE80211_MED_SYNC_DELAY_DURATION 0x00ff #define IEEE80211_MED_SYNC_DELAY_SYNC_OFDM_ED_THRESH 0x0f00 @@ -809,6 +810,49 @@ static inline u16 ieee80211_mle_get_ext_mld_capa_op(const u8 *data) return get_unaligned_le16(common); } +/** + * ieee80211_mle_get_enh_crit_upd_info - returns the enhanced critical + * updates information + * @data: pointer to the multi-link element + * Return: the enhanced critical updates information field, or %NULL + * + * The element is assumed to be of the correct type (BASIC) and big enough, + * this must be checked using ieee80211_mle_type_ok(). + */ +static inline const struct ieee80211_enh_crit_upd * +ieee80211_mle_get_enh_crit_upd_info(const u8 *data) +{ + const struct ieee80211_multi_link_elem *mle = (const void *)data; + u16 control = le16_to_cpu(mle->control); + const u8 *common = mle->variable; + + /* + * common points now at the beginning of + * ieee80211_mle_basic_common_info + */ + common += sizeof(struct ieee80211_mle_basic_common_info); + + if (!(control & IEEE80211_MLC_BASIC_PRES_ENH_CRIT_UPD)) + return NULL; + + if (control & IEEE80211_MLC_BASIC_PRES_LINK_ID) + common += 1; + if (control & IEEE80211_MLC_BASIC_PRES_BSS_PARAM_CH_CNT) + common += 1; + if (control & IEEE80211_MLC_BASIC_PRES_MED_SYNC_DELAY) + common += 2; + if (control & IEEE80211_MLC_BASIC_PRES_EML_CAPA) + common += 2; + if (control & IEEE80211_MLC_BASIC_PRES_MLD_CAPA_OP) + common += 2; + if (control & IEEE80211_MLC_BASIC_PRES_MLD_ID) + common += 1; + if (control & IEEE80211_MLC_BASIC_PRES_EXT_MLD_CAPA_OP) + common += 2; + + return (const void *)common; +} + /** * ieee80211_mle_get_mld_id - returns the MLD ID * @data: pointer to the multi-link element @@ -883,6 +927,8 @@ static inline bool ieee80211_mle_size_ok(const u8 *data, size_t len) common += 1; if (control & IEEE80211_MLC_BASIC_PRES_EXT_MLD_CAPA_OP) common += 2; + if (control & IEEE80211_MLC_BASIC_PRES_ENH_CRIT_UPD) + common += 1; break; case IEEE80211_ML_CONTROL_TYPE_PREQ: common += sizeof(struct ieee80211_mle_preq_common_info); @@ -959,6 +1005,8 @@ enum ieee80211_mle_subelems { #define IEEE80211_MLE_STA_CONTROL_NSTR_LINK_PAIR_PRESENT 0x0200 #define IEEE80211_MLE_STA_CONTROL_NSTR_BITMAP_SIZE 0x0400 #define IEEE80211_MLE_STA_CONTROL_BSS_PARAM_CHANGE_CNT_PRESENT 0x0800 +#define IEEE80211_MLE_STA_CONTROL_ENH_CRIT_UPD_PRESENT 0x1000 +#define IEEE80211_MLE_STA_CONTROL_AP_CONDUCTED_TX_PWR_PRESENT 0x2000 struct ieee80211_mle_per_sta_profile { __le16 control; @@ -1004,6 +1052,12 @@ static inline bool ieee80211_mle_basic_sta_prof_size_ok(const u8 *data, if (control & IEEE80211_MLE_STA_CONTROL_BSS_PARAM_CHANGE_CNT_PRESENT) info_len += 1; + if (control & IEEE80211_MLE_STA_CONTROL_ENH_CRIT_UPD_PRESENT) + info_len += 1; + + if (control & IEEE80211_MLE_STA_CONTROL_AP_CONDUCTED_TX_PWR_PRESENT) + info_len += 1; + return prof->sta_info_len >= info_len && fixed + prof->sta_info_len - 1 <= len; } @@ -1044,6 +1098,44 @@ ieee80211_mle_basic_sta_prof_bss_param_ch_cnt(const struct ieee80211_mle_per_sta return *pos; } +/** + * ieee80211_mle_basic_sta_prof_enh_crit_upd - get per-STA profile enhanced + * critical updates field + * @prof: the per-STA profile, having been checked with + * ieee80211_mle_basic_sta_prof_size_ok() for the correct length + * + * Return: The enhanced critical updates field if present, %NULL otherwise. + */ +static inline const struct ieee80211_enh_crit_upd * +ieee80211_mle_basic_sta_prof_enh_crit_upd(const struct ieee80211_mle_per_sta_profile *prof) +{ + u16 control = le16_to_cpu(prof->control); + const u8 *pos = prof->variable; + + if (!(control & IEEE80211_MLE_STA_CONTROL_ENH_CRIT_UPD_PRESENT)) + return NULL; + + if (control & IEEE80211_MLE_STA_CONTROL_STA_MAC_ADDR_PRESENT) + pos += 6; + if (control & IEEE80211_MLE_STA_CONTROL_BEACON_INT_PRESENT) + pos += 2; + if (control & IEEE80211_MLE_STA_CONTROL_TSF_OFFS_PRESENT) + pos += 8; + if (control & IEEE80211_MLE_STA_CONTROL_DTIM_INFO_PRESENT) + pos += 2; + if (control & IEEE80211_MLE_STA_CONTROL_COMPLETE_PROFILE && + control & IEEE80211_MLE_STA_CONTROL_NSTR_LINK_PAIR_PRESENT) { + if (control & IEEE80211_MLE_STA_CONTROL_NSTR_BITMAP_SIZE) + pos += 2; + else + pos += 1; + } + if (control & IEEE80211_MLE_STA_CONTROL_BSS_PARAM_CHANGE_CNT_PRESENT) + pos += 1; + + return (const void *)pos; +} + #define IEEE80211_MLE_STA_RECONF_CONTROL_LINK_ID 0x000f #define IEEE80211_MLE_STA_RECONF_CONTROL_COMPLETE_PROFILE 0x0010 #define IEEE80211_MLE_STA_RECONF_CONTROL_STA_MAC_ADDR_PRESENT 0x0020 diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 4f95da023746..948a7cdab27c 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -790,6 +790,10 @@ struct ieee80211_bss_npca_params { * be updated to 1, even if bss_param_ch_cnt didn't change. This allows * the link to know that it heard the latest value from its own beacon * (as opposed to hearing its value from another link's beacon). + * @enh_bss_param_ch_cnt: In BSS-mode, the enhanced BSS parameters change + * counter. See @bss_param_ch_cnt, it works the same way. + * @enh_bss_param_ch_cnt_link_id: In BSS-mode, the link_id for the enhanced + * BSS parameter change counter, see @bss_param_ch_cnt_link_id. * @s1g_long_beacon_period: number of beacon intervals between each long * beacon transmission. * @npca: NPCA parameters @@ -894,6 +898,8 @@ struct ieee80211_bss_conf { u8 bss_param_ch_cnt; u8 bss_param_ch_cnt_link_id; + u8 enh_bss_param_ch_cnt; + u8 enh_bss_param_ch_cnt_link_id; u8 s1g_long_beacon_period; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 3e421efe870c..d577252dbb9f 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -5931,11 +5931,28 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link, ieee80211_mle_basic_sta_prof_bss_param_ch_cnt(elems->prof); bss_conf->bss_param_ch_cnt = bss_param_ch_cnt; bss_conf->bss_param_ch_cnt_link_id = link_id; + + if (link->u.mgd.conn.mode >= IEEE80211_CONN_MODE_UHR) { + const struct ieee80211_enh_crit_upd *enh_crit_upd; + + enh_crit_upd = ieee80211_mle_basic_sta_prof_enh_crit_upd(elems->prof); + if (!enh_crit_upd) { + link_info(link, + "per-STA profile missing enhanced critical updates\n"); + ret = false; + goto out; + } + + bss_conf->enh_bss_param_ch_cnt = + u8_get_bits(enh_crit_upd->v, + IEEE80211_ENH_CRIT_UPD_EBPCC); + bss_conf->enh_bss_param_ch_cnt_link_id = link_id; + } } if (link_id == assoc_data->assoc_link_id && elems->ml_basic) { - int bss_param_ch_cnt = - ieee80211_mle_get_bss_param_ch_cnt((const void *)elems->ml_basic); + const void *mle = (const void *)elems->ml_basic; + int bss_param_ch_cnt = ieee80211_mle_get_bss_param_ch_cnt(mle); if (bss_param_ch_cnt < 0) { sdata_info(sdata, @@ -5945,6 +5962,23 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link, } bss_conf->bss_param_ch_cnt = bss_param_ch_cnt; bss_conf->bss_param_ch_cnt_link_id = link_id; + + if (link->u.mgd.conn.mode >= IEEE80211_CONN_MODE_UHR) { + const struct ieee80211_enh_crit_upd *enh_crit_upd; + + enh_crit_upd = ieee80211_mle_get_enh_crit_upd_info(mle); + if (!enh_crit_upd) { + link_info(link, + "No enhanced critical updates in assoc response\n"); + ret = false; + goto out; + } + + bss_conf->enh_bss_param_ch_cnt = + u8_get_bits(enh_crit_upd->v, + IEEE80211_ENH_CRIT_UPD_EBPCC); + bss_conf->enh_bss_param_ch_cnt_link_id = link_id; + } } if (!is_s1g && !elems->supp_rates) {