mirror of
https://github.com/openwrt/openwrt.git
synced 2025-02-25 10:52:48 +00:00
349 lines
10 KiB
Diff
349 lines
10 KiB
Diff
|
From: Felix Fietkau <nbd@nbd.name>
|
||
|
Date: Wed, 22 May 2024 11:42:57 +0200
|
||
|
Subject: [PATCH] wifi: cfg80211: add support for advertising multiple
|
||
|
radios belonging to a wiphy
|
||
|
|
||
|
The prerequisite for MLO support in cfg80211/mac80211 is that all the links
|
||
|
participating in MLO must be from the same wiphy/ieee80211_hw. To meet this
|
||
|
expectation, some drivers may need to group multiple discrete hardware each
|
||
|
acting as a link in MLO under single wiphy.
|
||
|
|
||
|
With this change, supported frequencies and interface combinations of each
|
||
|
individual radio are reported to user space. This allows user space to figure
|
||
|
out the limitations of what combination of channels can be used concurrently.
|
||
|
|
||
|
Even for non-MLO devices, this improves support for devices capable of
|
||
|
running on multiple channels at the same time.
|
||
|
|
||
|
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||
|
---
|
||
|
|
||
|
--- a/include/net/cfg80211.h
|
||
|
+++ b/include/net/cfg80211.h
|
||
|
@@ -5045,7 +5045,9 @@ struct ieee80211_iface_limit {
|
||
|
* struct ieee80211_iface_combination - possible interface combination
|
||
|
*
|
||
|
* With this structure the driver can describe which interface
|
||
|
- * combinations it supports concurrently.
|
||
|
+ * combinations it supports concurrently. When set in a struct wiphy_radio,
|
||
|
+ * the combinations refer to combinations of interfaces currently active on
|
||
|
+ * that radio.
|
||
|
*
|
||
|
* Examples:
|
||
|
*
|
||
|
@@ -5403,6 +5405,38 @@ struct wiphy_iftype_akm_suites {
|
||
|
int n_akm_suites;
|
||
|
};
|
||
|
|
||
|
+/**
|
||
|
+ * struct wiphy_radio_freq_range - wiphy frequency range
|
||
|
+ * @start_freq: start range edge frequency (kHz)
|
||
|
+ * @end_freq: end range edge frequency (kHz)
|
||
|
+ */
|
||
|
+struct wiphy_radio_freq_range {
|
||
|
+ u32 start_freq;
|
||
|
+ u32 end_freq;
|
||
|
+};
|
||
|
+
|
||
|
+
|
||
|
+/**
|
||
|
+ * struct wiphy_radio - physical radio of a wiphy
|
||
|
+ * This structure describes a physical radio belonging to a wiphy.
|
||
|
+ * It is used to describe concurrent-channel capabilities. Only one channel
|
||
|
+ * can be active on the radio described by struct wiphy_radio.
|
||
|
+ *
|
||
|
+ * @freq_range: frequency range that the radio can operate on.
|
||
|
+ * @n_freq_range: number of elements in @freq_range
|
||
|
+ *
|
||
|
+ * @iface_combinations: Valid interface combinations array, should not
|
||
|
+ * list single interface types.
|
||
|
+ * @n_iface_combinations: number of entries in @iface_combinations array.
|
||
|
+ */
|
||
|
+struct wiphy_radio {
|
||
|
+ const struct wiphy_radio_freq_range *freq_range;
|
||
|
+ int n_freq_range;
|
||
|
+
|
||
|
+ const struct ieee80211_iface_combination *iface_combinations;
|
||
|
+ int n_iface_combinations;
|
||
|
+};
|
||
|
+
|
||
|
#define CFG80211_HW_TIMESTAMP_ALL_PEERS 0xffff
|
||
|
|
||
|
/**
|
||
|
@@ -5621,6 +5655,9 @@ struct wiphy_iftype_akm_suites {
|
||
|
* A value of %CFG80211_HW_TIMESTAMP_ALL_PEERS indicates the driver
|
||
|
* supports enabling HW timestamping for all peers (i.e. no need to
|
||
|
* specify a mac address).
|
||
|
+ *
|
||
|
+ * @radio: radios belonging to this wiphy
|
||
|
+ * @n_radio: number of radios
|
||
|
*/
|
||
|
struct wiphy {
|
||
|
struct mutex mtx;
|
||
|
@@ -5771,6 +5808,9 @@ struct wiphy {
|
||
|
|
||
|
u16 hw_timestamp_max_peers;
|
||
|
|
||
|
+ int n_radio;
|
||
|
+ const struct wiphy_radio *radio;
|
||
|
+
|
||
|
char priv[] __aligned(NETDEV_ALIGN);
|
||
|
};
|
||
|
|
||
|
--- a/include/uapi/linux/nl80211.h
|
||
|
+++ b/include/uapi/linux/nl80211.h
|
||
|
@@ -2061,6 +2061,10 @@ enum nl80211_commands {
|
||
|
* @NL80211_ATTR_INTERFACE_COMBINATIONS: Nested attribute listing the supported
|
||
|
* interface combinations. In each nested item, it contains attributes
|
||
|
* defined in &enum nl80211_if_combination_attrs.
|
||
|
+ * If the wiphy uses multiple radios (@NL80211_ATTR_WIPHY_RADIOS is set),
|
||
|
+ * this attribute contains the interface combinations of the first radio.
|
||
|
+ * See @NL80211_ATTR_WIPHY_INTERFACE_COMBINATIONS for the global wiphy
|
||
|
+ * combinations for the sum of all radios.
|
||
|
* @NL80211_ATTR_SOFTWARE_IFTYPES: Nested attribute (just like
|
||
|
* %NL80211_ATTR_SUPPORTED_IFTYPES) containing the interface types that
|
||
|
* are managed in software: interfaces of these types aren't subject to
|
||
|
@@ -2856,6 +2860,14 @@ enum nl80211_commands {
|
||
|
* %NL80211_CMD_ASSOCIATE indicating the SPP A-MSDUs
|
||
|
* are used on this connection
|
||
|
*
|
||
|
+ * @NL80211_ATTR_WIPHY_RADIOS: Nested attribute describing physical radios
|
||
|
+ * belonging to this wiphy. See &enum nl80211_wiphy_radio_attrs.
|
||
|
+ *
|
||
|
+ * @NL80211_ATTR_WIPHY_INTERFACE_COMBINATIONS: Nested attribute listing the
|
||
|
+ * supported interface combinations for all radios combined. In each
|
||
|
+ * nested item, it contains attributes defined in
|
||
|
+ * &enum nl80211_if_combination_attrs.
|
||
|
+ *
|
||
|
* @NUM_NL80211_ATTR: total number of nl80211_attrs available
|
||
|
* @NL80211_ATTR_MAX: highest attribute number currently defined
|
||
|
* @__NL80211_ATTR_AFTER_LAST: internal use
|
||
|
@@ -3401,6 +3413,9 @@ enum nl80211_attrs {
|
||
|
|
||
|
NL80211_ATTR_ASSOC_SPP_AMSDU,
|
||
|
|
||
|
+ NL80211_ATTR_WIPHY_RADIOS,
|
||
|
+ NL80211_ATTR_WIPHY_INTERFACE_COMBINATIONS,
|
||
|
+
|
||
|
/* add attributes here, update the policy in nl80211.c */
|
||
|
|
||
|
__NL80211_ATTR_AFTER_LAST,
|
||
|
@@ -7987,4 +8002,54 @@ enum nl80211_ap_settings_flags {
|
||
|
NL80211_AP_SETTINGS_SA_QUERY_OFFLOAD_SUPPORT = 1 << 1,
|
||
|
};
|
||
|
|
||
|
+/**
|
||
|
+ * enum nl80211_wiphy_radio_attrs - wiphy radio attributes
|
||
|
+ *
|
||
|
+ * @__NL80211_WIPHY_RADIO_ATTR_INVALID: Invalid
|
||
|
+ *
|
||
|
+ * @NL80211_WIPHY_RADIO_ATTR_INDEX: Index of this radio (u32)
|
||
|
+ * @NL80211_WIPHY_RADIO_ATTR_FREQ_RANGE: Frequency range supported by this
|
||
|
+ * radio. Attribute may be present multiple times.
|
||
|
+ * @NL80211_WIPHY_RADIO_ATTR_INTERFACE_COMBINATION: Supported interface
|
||
|
+ * combination for this radio. Attribute may be present multiple times
|
||
|
+ * and contains attributes defined in &enum nl80211_if_combination_attrs.
|
||
|
+ *
|
||
|
+ * @__NL80211_WIPHY_RADIO_ATTR_LAST: Internal
|
||
|
+ * @NL80211_WIPHY_RADIO_ATTR_MAX: Highest attribute
|
||
|
+ */
|
||
|
+enum nl80211_wiphy_radio_attrs {
|
||
|
+ __NL80211_WIPHY_RADIO_ATTR_INVALID,
|
||
|
+
|
||
|
+ NL80211_WIPHY_RADIO_ATTR_INDEX,
|
||
|
+ NL80211_WIPHY_RADIO_ATTR_FREQ_RANGE,
|
||
|
+ NL80211_WIPHY_RADIO_ATTR_INTERFACE_COMBINATION,
|
||
|
+
|
||
|
+ /* keep last */
|
||
|
+ __NL80211_WIPHY_RADIO_ATTR_LAST,
|
||
|
+ NL80211_WIPHY_RADIO_ATTR_MAX = __NL80211_WIPHY_RADIO_ATTR_LAST - 1,
|
||
|
+};
|
||
|
+
|
||
|
+/**
|
||
|
+ * enum nl80211_wiphy_radio_freq_range - wiphy radio frequency range
|
||
|
+ *
|
||
|
+ * @__NL80211_WIPHY_RADIO_FREQ_ATTR_INVALID: Invalid
|
||
|
+ *
|
||
|
+ * @NL80211_WIPHY_RADIO_FREQ_ATTR_START: Frequency range start (u32).
|
||
|
+ * The unit is kHz.
|
||
|
+ * @NL80211_WIPHY_RADIO_FREQ_ATTR_END: Frequency range end (u32).
|
||
|
+ * The unit is kHz.
|
||
|
+ *
|
||
|
+ * @__NL80211_WIPHY_RADIO_FREQ_ATTR_LAST: Internal
|
||
|
+ * @NL80211_WIPHY_RADIO_FREQ_ATTR_MAX: Highest attribute
|
||
|
+ */
|
||
|
+enum nl80211_wiphy_radio_freq_range {
|
||
|
+ __NL80211_WIPHY_RADIO_FREQ_ATTR_INVALID,
|
||
|
+
|
||
|
+ NL80211_WIPHY_RADIO_FREQ_ATTR_START,
|
||
|
+ NL80211_WIPHY_RADIO_FREQ_ATTR_END,
|
||
|
+
|
||
|
+ __NL80211_WIPHY_RADIO_FREQ_ATTR_LAST,
|
||
|
+ NL80211_WIPHY_RADIO_FREQ_ATTR_MAX = __NL80211_WIPHY_RADIO_FREQ_ATTR_LAST - 1,
|
||
|
+};
|
||
|
+
|
||
|
#endif /* __LINUX_NL80211_H */
|
||
|
--- a/net/wireless/nl80211.c
|
||
|
+++ b/net/wireless/nl80211.c
|
||
|
@@ -1621,16 +1621,18 @@ nla_put_failure:
|
||
|
}
|
||
|
|
||
|
static int nl80211_put_ifcomb_data(struct sk_buff *msg, bool large, int idx,
|
||
|
- const struct ieee80211_iface_combination *c)
|
||
|
+ const struct ieee80211_iface_combination *c,
|
||
|
+ u16 nested)
|
||
|
{
|
||
|
struct nlattr *nl_combi, *nl_limits;
|
||
|
int i;
|
||
|
|
||
|
- nl_combi = nla_nest_start_noflag(msg, idx);
|
||
|
+ nl_combi = nla_nest_start_noflag(msg, idx | nested);
|
||
|
if (!nl_combi)
|
||
|
goto nla_put_failure;
|
||
|
|
||
|
- nl_limits = nla_nest_start_noflag(msg, NL80211_IFACE_COMB_LIMITS);
|
||
|
+ nl_limits = nla_nest_start_noflag(msg, NL80211_IFACE_COMB_LIMITS |
|
||
|
+ nested);
|
||
|
if (!nl_limits)
|
||
|
goto nla_put_failure;
|
||
|
|
||
|
@@ -1678,19 +1680,26 @@ nla_put_failure:
|
||
|
|
||
|
static int nl80211_put_iface_combinations(struct wiphy *wiphy,
|
||
|
struct sk_buff *msg,
|
||
|
- bool large)
|
||
|
+ int attr, int radio,
|
||
|
+ bool large, u16 nested)
|
||
|
{
|
||
|
+ const struct ieee80211_iface_combination *c;
|
||
|
struct nlattr *nl_combis;
|
||
|
- int i;
|
||
|
+ int i, n;
|
||
|
|
||
|
- nl_combis = nla_nest_start_noflag(msg,
|
||
|
- NL80211_ATTR_INTERFACE_COMBINATIONS);
|
||
|
+ nl_combis = nla_nest_start_noflag(msg, attr | nested);
|
||
|
if (!nl_combis)
|
||
|
goto nla_put_failure;
|
||
|
|
||
|
- for (i = 0; i < wiphy->n_iface_combinations; i++)
|
||
|
- if (nl80211_put_ifcomb_data(msg, large, i + 1,
|
||
|
- &wiphy->iface_combinations[i]))
|
||
|
+ if (radio >= 0) {
|
||
|
+ c = wiphy->radio[0].iface_combinations;
|
||
|
+ n = wiphy->radio[0].n_iface_combinations;
|
||
|
+ } else {
|
||
|
+ c = wiphy->iface_combinations;
|
||
|
+ n = wiphy->n_iface_combinations;
|
||
|
+ }
|
||
|
+ for (i = 0; i < n; i++)
|
||
|
+ if (nl80211_put_ifcomb_data(msg, large, i + 1, &c[i], nested))
|
||
|
goto nla_put_failure;
|
||
|
|
||
|
nla_nest_end(msg, nl_combis);
|
||
|
@@ -2397,6 +2406,80 @@ fail:
|
||
|
return -ENOBUFS;
|
||
|
}
|
||
|
|
||
|
+static int nl80211_put_radio(struct wiphy *wiphy, struct sk_buff *msg, int idx)
|
||
|
+{
|
||
|
+ const struct wiphy_radio *r = &wiphy->radio[idx];
|
||
|
+ struct nlattr *radio, *freq;
|
||
|
+ int i;
|
||
|
+
|
||
|
+ radio = nla_nest_start(msg, idx);
|
||
|
+ if (!radio)
|
||
|
+ return -ENOBUFS;
|
||
|
+
|
||
|
+ if (nla_put_u32(msg, NL80211_WIPHY_RADIO_ATTR_INDEX, idx))
|
||
|
+ goto nla_put_failure;
|
||
|
+
|
||
|
+ for (i = 0; i < r->n_freq_range; i++) {
|
||
|
+ const struct wiphy_radio_freq_range *range = &r->freq_range[i];
|
||
|
+
|
||
|
+ freq = nla_nest_start(msg, NL80211_WIPHY_RADIO_ATTR_FREQ_RANGE);
|
||
|
+ if (!freq)
|
||
|
+ goto nla_put_failure;
|
||
|
+
|
||
|
+ if (nla_put_u32(msg, NL80211_WIPHY_RADIO_FREQ_ATTR_START,
|
||
|
+ range->start_freq) ||
|
||
|
+ nla_put_u32(msg, NL80211_WIPHY_RADIO_FREQ_ATTR_END,
|
||
|
+ range->end_freq))
|
||
|
+ goto nla_put_failure;
|
||
|
+
|
||
|
+ nla_nest_end(msg, freq);
|
||
|
+ }
|
||
|
+
|
||
|
+ for (i = 0; i < r->n_iface_combinations; i++)
|
||
|
+ if (nl80211_put_ifcomb_data(msg, true,
|
||
|
+ NL80211_WIPHY_RADIO_ATTR_INTERFACE_COMBINATION,
|
||
|
+ &r->iface_combinations[i],
|
||
|
+ NLA_F_NESTED))
|
||
|
+ goto nla_put_failure;
|
||
|
+
|
||
|
+ nla_nest_end(msg, radio);
|
||
|
+
|
||
|
+ return 0;
|
||
|
+
|
||
|
+nla_put_failure:
|
||
|
+ return -ENOBUFS;
|
||
|
+}
|
||
|
+
|
||
|
+static int nl80211_put_radios(struct wiphy *wiphy, struct sk_buff *msg)
|
||
|
+{
|
||
|
+ struct nlattr *radios;
|
||
|
+ int i;
|
||
|
+
|
||
|
+ if (!wiphy->n_radio)
|
||
|
+ return 0;
|
||
|
+
|
||
|
+ radios = nla_nest_start(msg, NL80211_ATTR_WIPHY_RADIOS);
|
||
|
+ if (!radios)
|
||
|
+ return -ENOBUFS;
|
||
|
+
|
||
|
+ for (i = 0; i < wiphy->n_radio; i++)
|
||
|
+ if (nl80211_put_radio(wiphy, msg, i))
|
||
|
+ goto fail;
|
||
|
+
|
||
|
+ nla_nest_end(msg, radios);
|
||
|
+
|
||
|
+ if (nl80211_put_iface_combinations(wiphy, msg,
|
||
|
+ NL80211_ATTR_WIPHY_INTERFACE_COMBINATIONS,
|
||
|
+ -1, true, NLA_F_NESTED))
|
||
|
+ return -ENOBUFS;
|
||
|
+
|
||
|
+ return 0;
|
||
|
+
|
||
|
+fail:
|
||
|
+ nla_nest_cancel(msg, radios);
|
||
|
+ return -ENOBUFS;
|
||
|
+}
|
||
|
+
|
||
|
struct nl80211_dump_wiphy_state {
|
||
|
s64 filter_wiphy;
|
||
|
long start;
|
||
|
@@ -2692,7 +2775,9 @@ static int nl80211_send_wiphy(struct cfg
|
||
|
goto nla_put_failure;
|
||
|
|
||
|
if (nl80211_put_iface_combinations(&rdev->wiphy, msg,
|
||
|
- state->split))
|
||
|
+ NL80211_ATTR_INTERFACE_COMBINATIONS,
|
||
|
+ rdev->wiphy.n_radio ? 0 : -1,
|
||
|
+ state->split, 0))
|
||
|
goto nla_put_failure;
|
||
|
|
||
|
state->split_start++;
|
||
|
@@ -3006,6 +3091,12 @@ static int nl80211_send_wiphy(struct cfg
|
||
|
rdev->wiphy.hw_timestamp_max_peers))
|
||
|
goto nla_put_failure;
|
||
|
|
||
|
+ state->split_start++;
|
||
|
+ break;
|
||
|
+ case 17:
|
||
|
+ if (nl80211_put_radios(&rdev->wiphy, msg))
|
||
|
+ goto nla_put_failure;
|
||
|
+
|
||
|
/* done */
|
||
|
state->split_start = 0;
|
||
|
break;
|