1 From: Felix Fietkau <nbd@nbd.name>
2 Date: Wed, 22 May 2024 11:42:57 +0200
3 Subject: [PATCH] wifi: cfg80211: add support for advertising multiple
4 radios belonging to a wiphy
6 The prerequisite for MLO support in cfg80211/mac80211 is that all the links
7 participating in MLO must be from the same wiphy/ieee80211_hw. To meet this
8 expectation, some drivers may need to group multiple discrete hardware each
9 acting as a link in MLO under single wiphy.
11 With this change, supported frequencies and interface combinations of each
12 individual radio are reported to user space. This allows user space to figure
13 out the limitations of what combination of channels can be used concurrently.
15 Even for non-MLO devices, this improves support for devices capable of
16 running on multiple channels at the same time.
18 Signed-off-by: Felix Fietkau <nbd@nbd.name>
21 --- a/include/net/cfg80211.h
22 +++ b/include/net/cfg80211.h
23 @@ -5045,7 +5045,9 @@ struct ieee80211_iface_limit {
24 * struct ieee80211_iface_combination - possible interface combination
26 * With this structure the driver can describe which interface
27 - * combinations it supports concurrently.
28 + * combinations it supports concurrently. When set in a struct wiphy_radio,
29 + * the combinations refer to combinations of interfaces currently active on
34 @@ -5403,6 +5405,38 @@ struct wiphy_iftype_akm_suites {
39 + * struct wiphy_radio_freq_range - wiphy frequency range
40 + * @start_freq: start range edge frequency (kHz)
41 + * @end_freq: end range edge frequency (kHz)
43 +struct wiphy_radio_freq_range {
50 + * struct wiphy_radio - physical radio of a wiphy
51 + * This structure describes a physical radio belonging to a wiphy.
52 + * It is used to describe concurrent-channel capabilities. Only one channel
53 + * can be active on the radio described by struct wiphy_radio.
55 + * @freq_range: frequency range that the radio can operate on.
56 + * @n_freq_range: number of elements in @freq_range
58 + * @iface_combinations: Valid interface combinations array, should not
59 + * list single interface types.
60 + * @n_iface_combinations: number of entries in @iface_combinations array.
63 + const struct wiphy_radio_freq_range *freq_range;
66 + const struct ieee80211_iface_combination *iface_combinations;
67 + int n_iface_combinations;
70 #define CFG80211_HW_TIMESTAMP_ALL_PEERS 0xffff
73 @@ -5621,6 +5655,9 @@ struct wiphy_iftype_akm_suites {
74 * A value of %CFG80211_HW_TIMESTAMP_ALL_PEERS indicates the driver
75 * supports enabling HW timestamping for all peers (i.e. no need to
76 * specify a mac address).
78 + * @radio: radios belonging to this wiphy
79 + * @n_radio: number of radios
83 @@ -5771,6 +5808,9 @@ struct wiphy {
85 u16 hw_timestamp_max_peers;
88 + const struct wiphy_radio *radio;
90 char priv[] __aligned(NETDEV_ALIGN);
93 --- a/include/uapi/linux/nl80211.h
94 +++ b/include/uapi/linux/nl80211.h
95 @@ -2061,6 +2061,10 @@ enum nl80211_commands {
96 * @NL80211_ATTR_INTERFACE_COMBINATIONS: Nested attribute listing the supported
97 * interface combinations. In each nested item, it contains attributes
98 * defined in &enum nl80211_if_combination_attrs.
99 + * If the wiphy uses multiple radios (@NL80211_ATTR_WIPHY_RADIOS is set),
100 + * this attribute contains the interface combinations of the first radio.
101 + * See @NL80211_ATTR_WIPHY_INTERFACE_COMBINATIONS for the global wiphy
102 + * combinations for the sum of all radios.
103 * @NL80211_ATTR_SOFTWARE_IFTYPES: Nested attribute (just like
104 * %NL80211_ATTR_SUPPORTED_IFTYPES) containing the interface types that
105 * are managed in software: interfaces of these types aren't subject to
106 @@ -2856,6 +2860,14 @@ enum nl80211_commands {
107 * %NL80211_CMD_ASSOCIATE indicating the SPP A-MSDUs
108 * are used on this connection
110 + * @NL80211_ATTR_WIPHY_RADIOS: Nested attribute describing physical radios
111 + * belonging to this wiphy. See &enum nl80211_wiphy_radio_attrs.
113 + * @NL80211_ATTR_WIPHY_INTERFACE_COMBINATIONS: Nested attribute listing the
114 + * supported interface combinations for all radios combined. In each
115 + * nested item, it contains attributes defined in
116 + * &enum nl80211_if_combination_attrs.
118 * @NUM_NL80211_ATTR: total number of nl80211_attrs available
119 * @NL80211_ATTR_MAX: highest attribute number currently defined
120 * @__NL80211_ATTR_AFTER_LAST: internal use
121 @@ -3401,6 +3413,9 @@ enum nl80211_attrs {
123 NL80211_ATTR_ASSOC_SPP_AMSDU,
125 + NL80211_ATTR_WIPHY_RADIOS,
126 + NL80211_ATTR_WIPHY_INTERFACE_COMBINATIONS,
128 /* add attributes here, update the policy in nl80211.c */
130 __NL80211_ATTR_AFTER_LAST,
131 @@ -7987,4 +8002,54 @@ enum nl80211_ap_settings_flags {
132 NL80211_AP_SETTINGS_SA_QUERY_OFFLOAD_SUPPORT = 1 << 1,
136 + * enum nl80211_wiphy_radio_attrs - wiphy radio attributes
138 + * @__NL80211_WIPHY_RADIO_ATTR_INVALID: Invalid
140 + * @NL80211_WIPHY_RADIO_ATTR_INDEX: Index of this radio (u32)
141 + * @NL80211_WIPHY_RADIO_ATTR_FREQ_RANGE: Frequency range supported by this
142 + * radio. Attribute may be present multiple times.
143 + * @NL80211_WIPHY_RADIO_ATTR_INTERFACE_COMBINATION: Supported interface
144 + * combination for this radio. Attribute may be present multiple times
145 + * and contains attributes defined in &enum nl80211_if_combination_attrs.
147 + * @__NL80211_WIPHY_RADIO_ATTR_LAST: Internal
148 + * @NL80211_WIPHY_RADIO_ATTR_MAX: Highest attribute
150 +enum nl80211_wiphy_radio_attrs {
151 + __NL80211_WIPHY_RADIO_ATTR_INVALID,
153 + NL80211_WIPHY_RADIO_ATTR_INDEX,
154 + NL80211_WIPHY_RADIO_ATTR_FREQ_RANGE,
155 + NL80211_WIPHY_RADIO_ATTR_INTERFACE_COMBINATION,
158 + __NL80211_WIPHY_RADIO_ATTR_LAST,
159 + NL80211_WIPHY_RADIO_ATTR_MAX = __NL80211_WIPHY_RADIO_ATTR_LAST - 1,
163 + * enum nl80211_wiphy_radio_freq_range - wiphy radio frequency range
165 + * @__NL80211_WIPHY_RADIO_FREQ_ATTR_INVALID: Invalid
167 + * @NL80211_WIPHY_RADIO_FREQ_ATTR_START: Frequency range start (u32).
169 + * @NL80211_WIPHY_RADIO_FREQ_ATTR_END: Frequency range end (u32).
172 + * @__NL80211_WIPHY_RADIO_FREQ_ATTR_LAST: Internal
173 + * @NL80211_WIPHY_RADIO_FREQ_ATTR_MAX: Highest attribute
175 +enum nl80211_wiphy_radio_freq_range {
176 + __NL80211_WIPHY_RADIO_FREQ_ATTR_INVALID,
178 + NL80211_WIPHY_RADIO_FREQ_ATTR_START,
179 + NL80211_WIPHY_RADIO_FREQ_ATTR_END,
181 + __NL80211_WIPHY_RADIO_FREQ_ATTR_LAST,
182 + NL80211_WIPHY_RADIO_FREQ_ATTR_MAX = __NL80211_WIPHY_RADIO_FREQ_ATTR_LAST - 1,
185 #endif /* __LINUX_NL80211_H */
186 --- a/net/wireless/nl80211.c
187 +++ b/net/wireless/nl80211.c
188 @@ -1621,16 +1621,18 @@ nla_put_failure:
191 static int nl80211_put_ifcomb_data(struct sk_buff *msg, bool large, int idx,
192 - const struct ieee80211_iface_combination *c)
193 + const struct ieee80211_iface_combination *c,
196 struct nlattr *nl_combi, *nl_limits;
199 - nl_combi = nla_nest_start_noflag(msg, idx);
200 + nl_combi = nla_nest_start_noflag(msg, idx | nested);
202 goto nla_put_failure;
204 - nl_limits = nla_nest_start_noflag(msg, NL80211_IFACE_COMB_LIMITS);
205 + nl_limits = nla_nest_start_noflag(msg, NL80211_IFACE_COMB_LIMITS |
208 goto nla_put_failure;
210 @@ -1678,19 +1680,26 @@ nla_put_failure:
212 static int nl80211_put_iface_combinations(struct wiphy *wiphy,
215 + int attr, int radio,
216 + bool large, u16 nested)
218 + const struct ieee80211_iface_combination *c;
219 struct nlattr *nl_combis;
223 - nl_combis = nla_nest_start_noflag(msg,
224 - NL80211_ATTR_INTERFACE_COMBINATIONS);
225 + nl_combis = nla_nest_start_noflag(msg, attr | nested);
227 goto nla_put_failure;
229 - for (i = 0; i < wiphy->n_iface_combinations; i++)
230 - if (nl80211_put_ifcomb_data(msg, large, i + 1,
231 - &wiphy->iface_combinations[i]))
233 + c = wiphy->radio[0].iface_combinations;
234 + n = wiphy->radio[0].n_iface_combinations;
236 + c = wiphy->iface_combinations;
237 + n = wiphy->n_iface_combinations;
239 + for (i = 0; i < n; i++)
240 + if (nl80211_put_ifcomb_data(msg, large, i + 1, &c[i], nested))
241 goto nla_put_failure;
243 nla_nest_end(msg, nl_combis);
244 @@ -2397,6 +2406,80 @@ fail:
248 +static int nl80211_put_radio(struct wiphy *wiphy, struct sk_buff *msg, int idx)
250 + const struct wiphy_radio *r = &wiphy->radio[idx];
251 + struct nlattr *radio, *freq;
254 + radio = nla_nest_start(msg, idx);
258 + if (nla_put_u32(msg, NL80211_WIPHY_RADIO_ATTR_INDEX, idx))
259 + goto nla_put_failure;
261 + for (i = 0; i < r->n_freq_range; i++) {
262 + const struct wiphy_radio_freq_range *range = &r->freq_range[i];
264 + freq = nla_nest_start(msg, NL80211_WIPHY_RADIO_ATTR_FREQ_RANGE);
266 + goto nla_put_failure;
268 + if (nla_put_u32(msg, NL80211_WIPHY_RADIO_FREQ_ATTR_START,
269 + range->start_freq) ||
270 + nla_put_u32(msg, NL80211_WIPHY_RADIO_FREQ_ATTR_END,
272 + goto nla_put_failure;
274 + nla_nest_end(msg, freq);
277 + for (i = 0; i < r->n_iface_combinations; i++)
278 + if (nl80211_put_ifcomb_data(msg, true,
279 + NL80211_WIPHY_RADIO_ATTR_INTERFACE_COMBINATION,
280 + &r->iface_combinations[i],
282 + goto nla_put_failure;
284 + nla_nest_end(msg, radio);
292 +static int nl80211_put_radios(struct wiphy *wiphy, struct sk_buff *msg)
294 + struct nlattr *radios;
297 + if (!wiphy->n_radio)
300 + radios = nla_nest_start(msg, NL80211_ATTR_WIPHY_RADIOS);
304 + for (i = 0; i < wiphy->n_radio; i++)
305 + if (nl80211_put_radio(wiphy, msg, i))
308 + nla_nest_end(msg, radios);
310 + if (nl80211_put_iface_combinations(wiphy, msg,
311 + NL80211_ATTR_WIPHY_INTERFACE_COMBINATIONS,
312 + -1, true, NLA_F_NESTED))
318 + nla_nest_cancel(msg, radios);
322 struct nl80211_dump_wiphy_state {
325 @@ -2692,7 +2775,9 @@ static int nl80211_send_wiphy(struct cfg
326 goto nla_put_failure;
328 if (nl80211_put_iface_combinations(&rdev->wiphy, msg,
330 + NL80211_ATTR_INTERFACE_COMBINATIONS,
331 + rdev->wiphy.n_radio ? 0 : -1,
333 goto nla_put_failure;
335 state->split_start++;
336 @@ -3006,6 +3091,12 @@ static int nl80211_send_wiphy(struct cfg
337 rdev->wiphy.hw_timestamp_max_peers))
338 goto nla_put_failure;
340 + state->split_start++;
343 + if (nl80211_put_radios(&rdev->wiphy, msg))
344 + goto nla_put_failure;
347 state->split_start = 0;