Introduce support for configuring UHR capabilities of stations via userspace. This includes adding new attributes and corresponding logic to parse and apply the UHR parameters provided by userspace applications. Signed-off-by: Karthikeyan Kathirvel --- include/net/cfg80211.h | 4 +++ include/uapi/linux/nl80211.h | 13 ++++++++ net/wireless/nl80211.c | 63 ++++++++++++++++++++++++++++++++---- 3 files changed, 74 insertions(+), 6 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 7964dd1ee691..7593f1a1875b 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1734,6 +1734,8 @@ struct sta_txpwr { * @eht_capa: EHT capabilities of station * @eht_capa_len: the length of the EHT capabilities * @s1g_capa: S1G capabilities of station + * @uhr_capa: UHR capabilities of station + * @uhr_capa_len: the length of the UHR capabilities */ struct link_station_parameters { const u8 *mld_mac; @@ -1753,6 +1755,8 @@ struct link_station_parameters { const struct ieee80211_eht_cap_elem *eht_capa; u8 eht_capa_len; const struct ieee80211_s1g_cap *s1g_capa; + const struct ieee80211_uhr_cap_elem *uhr_capa; + u8 uhr_capa_len; }; /** diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 8d9038ba1208..3488fa638bbd 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -2973,6 +2973,10 @@ enum nl80211_commands { * primary channel is 2 MHz wide, and the control channel designates * the 1 MHz primary subchannel within that 2 MHz primary. * + * @NL80211_ATTR_UHR_CAPABILITY: UHR Capability information element (from + * association request when used with NL80211_CMD_NEW_STATION). Can be set + * only if %NL80211_STA_FLAG_WME is set. + * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use @@ -3541,6 +3545,8 @@ enum nl80211_attrs { NL80211_ATTR_S1G_PRIMARY_2MHZ, + NL80211_ATTR_UHR_CAPABILITY, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -3612,6 +3618,13 @@ enum nl80211_attrs { #define NL80211_CQM_TXE_MAX_INTVL 1800 +/* + * TODO: as of now used the len same as EHT, modify it + * based on UHR once spec is finalized + */ +#define NL80211_UHR_MIN_CAPABILITY_LEN 2 +#define NL80211_UHR_MAX_CAPABILITY_LEN 51 + /** * enum nl80211_iftype - (virtual) interface types * diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index d05d098c4dc7..a54618eae8e5 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -932,6 +932,9 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { NLA_POLICY_NESTED(nl80211_s1g_short_beacon), [NL80211_ATTR_BSS_PARAM] = { .type = NLA_FLAG }, [NL80211_ATTR_S1G_PRIMARY_2MHZ] = { .type = NLA_FLAG }, + [NL80211_ATTR_UHR_CAPABILITY] = + NLA_POLICY_RANGE(NLA_BINARY, NL80211_UHR_MIN_CAPABILITY_LEN, + NL80211_UHR_MAX_CAPABILITY_LEN), }; /* policy for the key attributes */ @@ -8292,6 +8295,19 @@ static int nl80211_set_station_tdls(struct genl_info *info, } } + if (info->attrs[NL80211_ATTR_HE_CAPABILITY] && + info->attrs[NL80211_ATTR_EHT_CAPABILITY] && + info->attrs[NL80211_ATTR_UHR_CAPABILITY]) { + lsta->uhr_capa = + nla_data(info->attrs[NL80211_ATTR_UHR_CAPABILITY]); + lsta->uhr_capa_len = + nla_len(info->attrs[NL80211_ATTR_UHR_CAPABILITY]); + + if (!ieee80211_uhr_capa_size_ok((const u8 *)lsta->uhr_capa, + lsta->uhr_capa_len)) + return -EINVAL; + } + if (info->attrs[NL80211_ATTR_S1G_CAPABILITY]) lsta->s1g_capa = nla_data(info->attrs[NL80211_ATTR_S1G_CAPABILITY]); @@ -8615,6 +8631,23 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) } } + if (info->attrs[NL80211_ATTR_HE_CAPABILITY] && + info->attrs[NL80211_ATTR_EHT_CAPABILITY] && + info->attrs[NL80211_ATTR_UHR_CAPABILITY]) { + const u8 *uhr_capa; + + lsta->uhr_capa = + nla_data(info->attrs[NL80211_ATTR_UHR_CAPABILITY]); + lsta->uhr_capa_len = + nla_len(info->attrs[NL80211_ATTR_UHR_CAPABILITY]); + + uhr_capa = (const u8 *)lsta->uhr_capa; + + if (!ieee80211_uhr_capa_size_ok(uhr_capa, + lsta->uhr_capa_len)) + return -EINVAL; + } + if (info->attrs[NL80211_ATTR_EML_CAPABILITY]) { params.eml_cap_present = true; params.eml_cap = @@ -8665,19 +8698,20 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) if (parse_station_flags(info, dev->ieee80211_ptr->iftype, ¶ms)) return -EINVAL; - /* HT/VHT requires QoS, but if we don't have that just ignore HT/VHT - * as userspace might just pass through the capabilities from the IEs - * directly, rather than enforcing this restriction and returning an - * error in this case. + /* HT/VHT/EHT and UHR requires QoS, but if we don't have that just + * ignore HT/VHT/EHT and UHR as userspace might just pass through + * the capabilities from the IEs directly, rather than enforcing + * this restriction and returning an error in this case. */ if (!(params.sta_flags_set & BIT(NL80211_STA_FLAG_WME))) { lsta->ht_capa = NULL; lsta->vht_capa = NULL; - /* HE and EHT require WME */ + /* HE, EHT and UHR require WME */ if (lsta->he_capa_len || lsta->he_6ghz_capa || - lsta->eht_capa_len) + lsta->eht_capa_len || + lsta->uhr_capa_len) return -EINVAL; } @@ -17651,6 +17685,23 @@ nl80211_add_mod_link_station(struct sk_buff *skb, struct genl_info *info, } } + if (info->attrs[NL80211_ATTR_HE_CAPABILITY] && + info->attrs[NL80211_ATTR_EHT_CAPABILITY] && + info->attrs[NL80211_ATTR_UHR_CAPABILITY]) { + const u8 *uhr_capa; + + params.uhr_capa = + nla_data(info->attrs[NL80211_ATTR_UHR_CAPABILITY]); + params.uhr_capa_len = + nla_len(info->attrs[NL80211_ATTR_UHR_CAPABILITY]); + + uhr_capa = (const u8 *)params.uhr_capa; + + if (!ieee80211_uhr_capa_size_ok(uhr_capa, + params.uhr_capa_len)) + return -EINVAL; + } + if (info->attrs[NL80211_ATTR_HE_6GHZ_CAPABILITY]) params.he_6ghz_capa = nla_data(info->attrs[NL80211_ATTR_HE_6GHZ_CAPABILITY]); -- 2.34.1