From: Johannes Berg UHR defines bit 8 to mean multi-link power management, add a definition for it. Also reindent the other definitions to use tabs, not spaces. Signed-off-by: Johannes Berg --- include/linux/ieee80211-eht.h | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/include/linux/ieee80211-eht.h b/include/linux/ieee80211-eht.h index 335e78bd4b5d..87d92fb86fab 100644 --- a/include/linux/ieee80211-eht.h +++ b/include/linux/ieee80211-eht.h @@ -9,7 +9,7 @@ * Copyright (c) 2006, Michael Wu * Copyright (c) 2013 - 2014 Intel Mobile Communications GmbH * Copyright (c) 2016 - 2017 Intel Deutschland GmbH - * Copyright (c) 2018 - 2025 Intel Corporation + * Copyright (c) 2018 - 2026 Intel Corporation */ #ifndef LINUX_IEEE80211_EHT_H @@ -750,11 +750,13 @@ static inline u16 ieee80211_mle_get_mld_capa_op(const u8 *data) } /* Defined in Figure 9-1074t in P802.11be_D7.0 */ -#define IEEE80211_EHT_ML_EXT_MLD_CAPA_OP_PARAM_UPDATE 0x0001 -#define IEEE80211_EHT_ML_EXT_MLD_CAPA_OP_RECO_MAX_LINKS_MASK 0x001e -#define IEEE80211_EHT_ML_EXT_MLD_CAPA_NSTR_UPDATE 0x0020 -#define IEEE80211_EHT_ML_EXT_MLD_CAPA_EMLSR_ENA_ON_ONE_LINK 0x0040 -#define IEEE80211_EHT_ML_EXT_MLD_CAPA_BTM_MLD_RECO_MULTI_AP 0x0080 +#define IEEE80211_EHT_ML_EXT_MLD_CAPA_OP_PARAM_UPDATE 0x0001 +#define IEEE80211_EHT_ML_EXT_MLD_CAPA_OP_RECO_MAX_LINKS_MASK 0x001e +#define IEEE80211_EHT_ML_EXT_MLD_CAPA_NSTR_UPDATE 0x0020 +#define IEEE80211_EHT_ML_EXT_MLD_CAPA_EMLSR_ENA_ON_ONE_LINK 0x0040 +#define IEEE80211_EHT_ML_EXT_MLD_CAPA_BTM_MLD_RECO_MULTI_AP 0x0080 +/* defined by UHR Draft P802.11bn_D1.3 Figure 9-1147 */ +#define IEEE80211_UHR_ML_EXT_MLD_CAPA_ML_PM 0x0100 /** * ieee80211_mle_get_ext_mld_capa_op - returns the extended MLD capabilities -- 2.53.0 From: Johannes Berg For UHR multi-link power management, the driver/device needs to know if the AP supports it, to be able to use it. Track the AP's extended MLD capabilities and operations so it does. Signed-off-by: Johannes Berg --- include/net/mac80211.h | 3 +++ net/mac80211/mlme.c | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 02318a4be0e1..5dc4ac08606f 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2674,6 +2674,8 @@ struct ieee80211_link_sta { * @epp_peer: indicates that the peer is an EPP peer. * @nmi: For NDI stations, pointer to the NMI station of the peer. * @nan_sched: NAN peer schedule for this station. Valid only for NMI stations. + * @ext_mld_capa_ops: the MLD's extended MLD capabilities and operations + * NOTE: currently only tracked for AP STAs */ struct ieee80211_sta { u8 addr[ETH_ALEN] __aligned(2); @@ -2698,6 +2700,7 @@ struct ieee80211_sta { struct ieee80211_txq *txq[IEEE80211_NUM_TIDS + 1]; u16 valid_links; + u16 ext_mld_capa_ops; bool epp_peer; struct ieee80211_link_sta deflink; struct ieee80211_link_sta __rcu *link[IEEE80211_MLD_MAX_NUM_LINKS]; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 00b4beff0e43..d24db2c2cde9 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -6462,6 +6462,12 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, sta->sta.spp_amsdu = assoc_data->spp_amsdu; if (ieee80211_vif_is_mld(&sdata->vif)) { + if (!elems->ml_basic) + goto out_err; + + sta->sta.ext_mld_capa_ops = + ieee80211_mle_get_ext_mld_capa_op((const void *)elems->ml_basic); + for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) { if (!assoc_data->link[link_id].bss) continue; -- 2.53.0 From: Johannes Berg We check that extended MLD capabilities and operations are consistent across APs in an AP MLD, but didn't check reserved fields since they could be defined to differ. Check bit 8 now since it's defined by UHR to be consistent. Signed-off-by: Johannes Berg --- net/wireless/mlme.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index bd72317c4964..65a39428f508 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -360,17 +360,18 @@ cfg80211_mlme_check_mlo_compat(const struct ieee80211_multi_link_elem *mle_a, * reserved when included in a unicast Probe Response frame and may * also change when the AP adds/removes links. The BTM MLD * Recommendation For Multiple APs Support subfield is reserved when - * transmitted by an AP. All other bits are currently reserved. - * See IEEE P802.11be/D7.0, Table 9-417o. + * transmitted by an AP. */ if ((ieee80211_mle_get_ext_mld_capa_op((const u8 *)mle_a) & (IEEE80211_EHT_ML_EXT_MLD_CAPA_OP_PARAM_UPDATE | IEEE80211_EHT_ML_EXT_MLD_CAPA_NSTR_UPDATE | - IEEE80211_EHT_ML_EXT_MLD_CAPA_EMLSR_ENA_ON_ONE_LINK)) != + IEEE80211_EHT_ML_EXT_MLD_CAPA_EMLSR_ENA_ON_ONE_LINK | + IEEE80211_UHR_ML_EXT_MLD_CAPA_ML_PM)) != (ieee80211_mle_get_ext_mld_capa_op((const u8 *)mle_b) & (IEEE80211_EHT_ML_EXT_MLD_CAPA_OP_PARAM_UPDATE | IEEE80211_EHT_ML_EXT_MLD_CAPA_NSTR_UPDATE | - IEEE80211_EHT_ML_EXT_MLD_CAPA_EMLSR_ENA_ON_ONE_LINK))) { + IEEE80211_EHT_ML_EXT_MLD_CAPA_EMLSR_ENA_ON_ONE_LINK | + IEEE80211_UHR_ML_EXT_MLD_CAPA_ML_PM))) { NL_SET_ERR_MSG(extack, "extended link MLD capabilities/ops mismatch"); return -EINVAL; -- 2.53.0 From: Johannes Berg For UHR, multi-link power-management capability lives there, and so it's needed that hostapd knows what to advertise, and clients should have it shown to userspace for information. Repurpose the existing NL80211_ATTR_ASSOC_MLD_EXT_CAPA_OPS by renaming it to NL80211_ATTR_EXT_MLD_CAPA_AND_OPS (with a define for compatibility) and advertise the capabilities. We can also later use the value, if needed, to set per-station capabilities on STAs added to AP interfaces. Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 2 ++ include/uapi/linux/nl80211.h | 15 +++++++++------ net/wireless/nl80211.c | 18 ++++++++++++------ 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index a40ab36b8edb..7c2ddaf2bcd7 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -5855,6 +5855,7 @@ struct wiphy_vendor_command { * @extended_capabilities_len: length of the extended capabilities * @eml_capabilities: EML capabilities (for MLO) * @mld_capa_and_ops: MLD capabilities and operations (for MLO) + * @ext_mld_capa_and_ops: Extended MLD capabilities and operations (for MLO) */ struct wiphy_iftype_ext_capab { enum nl80211_iftype iftype; @@ -5863,6 +5864,7 @@ struct wiphy_iftype_ext_capab { u8 extended_capabilities_len; u16 eml_capabilities; u16 mld_capa_and_ops; + u16 ext_mld_capa_and_ops; }; /** diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 072b383d7d3c..e104943b7e3d 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -2993,11 +2993,13 @@ enum nl80211_commands { * @NL80211_ATTR_EPCS: Flag attribute indicating that EPCS is enabled for a * station interface. * - * @NL80211_ATTR_ASSOC_MLD_EXT_CAPA_OPS: Extended MLD capabilities and - * operations that userspace implements to use during association/ML - * link reconfig, currently only "BTM MLD Recommendation For Multiple - * APs Support". Drivers may set additional flags that they support - * in the kernel or device. + * @NL80211_ATTR_EXT_MLD_CAPA_AND_OPS: Extended MLD capabilities and operations. + * For association and link reconfiguration, indicates extra capabilities + * that userspace implements, currently only "BTM MLD Recommendation For + * Multiple APs Support". + * For wiphy information, additional flags that drivers will set, but + * this is informational only for userspace (it's not expected to set + * these.) * * @NL80211_ATTR_WIPHY_RADIO_INDEX: (int) Integer attribute denoting the index * of the radio in interest. Internally a value of -1 is used to @@ -3697,7 +3699,7 @@ enum nl80211_attrs { NL80211_ATTR_MLO_RECONF_REM_LINKS, NL80211_ATTR_EPCS, - NL80211_ATTR_ASSOC_MLD_EXT_CAPA_OPS, + NL80211_ATTR_EXT_MLD_CAPA_AND_OPS, NL80211_ATTR_WIPHY_RADIO_INDEX, @@ -3749,6 +3751,7 @@ enum nl80211_attrs { #define NL80211_ATTR_SAE_DATA NL80211_ATTR_AUTH_DATA #define NL80211_ATTR_CSA_C_OFF_BEACON NL80211_ATTR_CNTDWN_OFFS_BEACON #define NL80211_ATTR_CSA_C_OFF_PRESP NL80211_ATTR_CNTDWN_OFFS_PRESP +#define NL80211_ATTR_ASSOC_MLD_EXT_CAPA_OPS NL80211_ATTR_EXT_MLD_CAPA_AND_OPS /* * Allow user space programs to use #ifdef on new attributes by defining them diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index cf236307cca9..6e76285c1045 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1047,7 +1047,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { NL80211_MAX_SUPP_SELECTORS), [NL80211_ATTR_MLO_RECONF_REM_LINKS] = { .type = NLA_U16 }, [NL80211_ATTR_EPCS] = { .type = NLA_FLAG }, - [NL80211_ATTR_ASSOC_MLD_EXT_CAPA_OPS] = { .type = NLA_U16 }, + [NL80211_ATTR_EXT_MLD_CAPA_AND_OPS] = { .type = NLA_U16 }, [NL80211_ATTR_WIPHY_RADIO_INDEX] = { .type = NLA_U8 }, [NL80211_ATTR_S1G_LONG_BEACON_PERIOD] = NLA_POLICY_MIN(NLA_U8, 2), [NL80211_ATTR_S1G_SHORT_BEACON] = @@ -3461,6 +3461,12 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev, NL80211_ATTR_MLD_CAPA_AND_OPS, capab->mld_capa_and_ops))) goto nla_put_failure; + if (rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_MLO && + capab->ext_mld_capa_and_ops && + nla_put_u16(msg, + NL80211_ATTR_EXT_MLD_CAPA_AND_OPS, + capab->ext_mld_capa_and_ops)) + goto nla_put_failure; nla_nest_end(msg, nested_ext_capab); if (state->split) @@ -12921,9 +12927,9 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) goto free; } - if (info->attrs[NL80211_ATTR_ASSOC_MLD_EXT_CAPA_OPS]) + if (info->attrs[NL80211_ATTR_EXT_MLD_CAPA_AND_OPS]) req.ext_mld_capa_ops = - nla_get_u16(info->attrs[NL80211_ATTR_ASSOC_MLD_EXT_CAPA_OPS]); + nla_get_u16(info->attrs[NL80211_ATTR_EXT_MLD_CAPA_AND_OPS]); } else { if (req.link_id >= 0) return -EINVAL; @@ -12934,7 +12940,7 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) return PTR_ERR(req.bss); ap_addr = req.bss->bssid; - if (info->attrs[NL80211_ATTR_ASSOC_MLD_EXT_CAPA_OPS]) + if (info->attrs[NL80211_ATTR_EXT_MLD_CAPA_AND_OPS]) return -EINVAL; } @@ -18798,9 +18804,9 @@ static int nl80211_assoc_ml_reconf(struct sk_buff *skb, struct genl_info *info) goto out; } - if (info->attrs[NL80211_ATTR_ASSOC_MLD_EXT_CAPA_OPS]) + if (info->attrs[NL80211_ATTR_EXT_MLD_CAPA_AND_OPS]) req.ext_mld_capa_ops = - nla_get_u16(info->attrs[NL80211_ATTR_ASSOC_MLD_EXT_CAPA_OPS]); + nla_get_u16(info->attrs[NL80211_ATTR_EXT_MLD_CAPA_AND_OPS]); err = cfg80211_assoc_ml_reconf(rdev, dev, &req); -- 2.53.0 From: Johannes Berg If the AP has extended MLD capa/ops we may advertise our own from userspace. Also add the driver's in this case. This is fine since the only one right now from the driver is UHR ML-PM and that's only relevant if the AP already has it too. Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index d24db2c2cde9..9d7afbf3700e 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -9849,7 +9849,9 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgd_assoc_data *assoc_data; const struct element *ssid_elem; struct ieee80211_vif_cfg *vif_cfg = &sdata->vif.cfg; + const struct wiphy_iftype_ext_capab *ift_ext_capa; struct ieee80211_link_data *link; + u16 driver_ext_mld_capa_ops = 0; struct cfg80211_bss *cbss; bool override, uapsd_supported; bool match_auth; @@ -9888,17 +9890,26 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, else memcpy(assoc_data->ap_addr, cbss->bssid, ETH_ALEN); + ift_ext_capa = cfg80211_get_iftype_ext_capa(local->hw.wiphy, + ieee80211_vif_type_p2p(&sdata->vif)); + if (ift_ext_capa) + driver_ext_mld_capa_ops = ift_ext_capa->ext_mld_capa_and_ops; + /* * Many APs have broken parsing of the extended MLD capa/ops field, * dropping (re-)association request frames or replying with association * response with a failure status if it's present. * Set our value from the userspace request only in strict mode or if * the AP also had that field present. + * For UHR we may want to advertise ML-PM (per driver_ext_mld_capa_ops) + * but if the AP doesn't have it then it's pointless, and if it does + * then it has to have the extended MLD capa/ops field. */ if (ieee80211_hw_check(&local->hw, STRICT) || ieee80211_mgd_assoc_bss_has_mld_ext_capa_ops(req)) assoc_data->ext_mld_capa_ops = - cpu_to_le16(req->ext_mld_capa_ops); + cpu_to_le16(req->ext_mld_capa_ops | + driver_ext_mld_capa_ops); if (ifmgd->associated) { u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN]; @@ -10892,11 +10903,13 @@ ieee80211_build_ml_reconf_req(struct ieee80211_sub_if_data *sdata, int ieee80211_mgd_assoc_ml_reconf(struct ieee80211_sub_if_data *sdata, struct cfg80211_ml_reconf_req *req) { + const struct wiphy_iftype_ext_capab *ift_ext_capa; struct ieee80211_local *local = sdata->local; struct ieee80211_mgd_assoc_data *data = NULL; struct sta_info *sta; struct sk_buff *skb; u16 added_links, new_valid_links; + u16 driver_ext_mld_capa_ops = 0; int link_id, err; if (!ieee80211_vif_is_mld(&sdata->vif) || @@ -11054,6 +11067,11 @@ int ieee80211_mgd_assoc_ml_reconf(struct ieee80211_sub_if_data *sdata, } } + ift_ext_capa = cfg80211_get_iftype_ext_capa(local->hw.wiphy, + ieee80211_vif_type_p2p(&sdata->vif)); + if (ift_ext_capa) + driver_ext_mld_capa_ops = ift_ext_capa->ext_mld_capa_and_ops; + /* Build the SKB before the link removal as the construction of the * station info for removed links requires the local address. * Invalidate the removed links, so that the transmission of the ML @@ -11062,7 +11080,8 @@ int ieee80211_mgd_assoc_ml_reconf(struct ieee80211_sub_if_data *sdata, * on which the request was received. */ skb = ieee80211_build_ml_reconf_req(sdata, data, req->rem_links, - cpu_to_le16(req->ext_mld_capa_ops)); + cpu_to_le16(req->ext_mld_capa_ops | + driver_ext_mld_capa_ops)); if (!skb) { err = -ENOMEM; goto err_free; -- 2.53.0