45349891b69e0ce68020b8b9ae7c3af6f6b18050
[openwrt/staging/neocturne.git] /
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
5
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.
10
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.
14
15 Even for non-MLO devices, this improves support for devices capable of
16 running on multiple channels at the same time.
17
18 Signed-off-by: Felix Fietkau <nbd@nbd.name>
19 ---
20
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
25 *
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
30 + * that radio.
31 *
32 * Examples:
33 *
34 @@ -5403,6 +5405,38 @@ struct wiphy_iftype_akm_suites {
35 int n_akm_suites;
36 };
37
38 +/**
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)
42 + */
43 +struct wiphy_radio_freq_range {
44 + u32 start_freq;
45 + u32 end_freq;
46 +};
47 +
48 +
49 +/**
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.
54 + *
55 + * @freq_range: frequency range that the radio can operate on.
56 + * @n_freq_range: number of elements in @freq_range
57 + *
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.
61 + */
62 +struct wiphy_radio {
63 + const struct wiphy_radio_freq_range *freq_range;
64 + int n_freq_range;
65 +
66 + const struct ieee80211_iface_combination *iface_combinations;
67 + int n_iface_combinations;
68 +};
69 +
70 #define CFG80211_HW_TIMESTAMP_ALL_PEERS 0xffff
71
72 /**
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).
77 + *
78 + * @radio: radios belonging to this wiphy
79 + * @n_radio: number of radios
80 */
81 struct wiphy {
82 struct mutex mtx;
83 @@ -5771,6 +5808,9 @@ struct wiphy {
84
85 u16 hw_timestamp_max_peers;
86
87 + int n_radio;
88 + const struct wiphy_radio *radio;
89 +
90 char priv[] __aligned(NETDEV_ALIGN);
91 };
92
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
109 *
110 + * @NL80211_ATTR_WIPHY_RADIOS: Nested attribute describing physical radios
111 + * belonging to this wiphy. See &enum nl80211_wiphy_radio_attrs.
112 + *
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.
117 + *
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 {
122
123 NL80211_ATTR_ASSOC_SPP_AMSDU,
124
125 + NL80211_ATTR_WIPHY_RADIOS,
126 + NL80211_ATTR_WIPHY_INTERFACE_COMBINATIONS,
127 +
128 /* add attributes here, update the policy in nl80211.c */
129
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,
133 };
134
135 +/**
136 + * enum nl80211_wiphy_radio_attrs - wiphy radio attributes
137 + *
138 + * @__NL80211_WIPHY_RADIO_ATTR_INVALID: Invalid
139 + *
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.
146 + *
147 + * @__NL80211_WIPHY_RADIO_ATTR_LAST: Internal
148 + * @NL80211_WIPHY_RADIO_ATTR_MAX: Highest attribute
149 + */
150 +enum nl80211_wiphy_radio_attrs {
151 + __NL80211_WIPHY_RADIO_ATTR_INVALID,
152 +
153 + NL80211_WIPHY_RADIO_ATTR_INDEX,
154 + NL80211_WIPHY_RADIO_ATTR_FREQ_RANGE,
155 + NL80211_WIPHY_RADIO_ATTR_INTERFACE_COMBINATION,
156 +
157 + /* keep last */
158 + __NL80211_WIPHY_RADIO_ATTR_LAST,
159 + NL80211_WIPHY_RADIO_ATTR_MAX = __NL80211_WIPHY_RADIO_ATTR_LAST - 1,
160 +};
161 +
162 +/**
163 + * enum nl80211_wiphy_radio_freq_range - wiphy radio frequency range
164 + *
165 + * @__NL80211_WIPHY_RADIO_FREQ_ATTR_INVALID: Invalid
166 + *
167 + * @NL80211_WIPHY_RADIO_FREQ_ATTR_START: Frequency range start (u32).
168 + * The unit is kHz.
169 + * @NL80211_WIPHY_RADIO_FREQ_ATTR_END: Frequency range end (u32).
170 + * The unit is kHz.
171 + *
172 + * @__NL80211_WIPHY_RADIO_FREQ_ATTR_LAST: Internal
173 + * @NL80211_WIPHY_RADIO_FREQ_ATTR_MAX: Highest attribute
174 + */
175 +enum nl80211_wiphy_radio_freq_range {
176 + __NL80211_WIPHY_RADIO_FREQ_ATTR_INVALID,
177 +
178 + NL80211_WIPHY_RADIO_FREQ_ATTR_START,
179 + NL80211_WIPHY_RADIO_FREQ_ATTR_END,
180 +
181 + __NL80211_WIPHY_RADIO_FREQ_ATTR_LAST,
182 + NL80211_WIPHY_RADIO_FREQ_ATTR_MAX = __NL80211_WIPHY_RADIO_FREQ_ATTR_LAST - 1,
183 +};
184 +
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:
189 }
190
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,
194 + u16 nested)
195 {
196 struct nlattr *nl_combi, *nl_limits;
197 int i;
198
199 - nl_combi = nla_nest_start_noflag(msg, idx);
200 + nl_combi = nla_nest_start_noflag(msg, idx | nested);
201 if (!nl_combi)
202 goto nla_put_failure;
203
204 - nl_limits = nla_nest_start_noflag(msg, NL80211_IFACE_COMB_LIMITS);
205 + nl_limits = nla_nest_start_noflag(msg, NL80211_IFACE_COMB_LIMITS |
206 + nested);
207 if (!nl_limits)
208 goto nla_put_failure;
209
210 @@ -1678,19 +1680,26 @@ nla_put_failure:
211
212 static int nl80211_put_iface_combinations(struct wiphy *wiphy,
213 struct sk_buff *msg,
214 - bool large)
215 + int attr, int radio,
216 + bool large, u16 nested)
217 {
218 + const struct ieee80211_iface_combination *c;
219 struct nlattr *nl_combis;
220 - int i;
221 + int i, n;
222
223 - nl_combis = nla_nest_start_noflag(msg,
224 - NL80211_ATTR_INTERFACE_COMBINATIONS);
225 + nl_combis = nla_nest_start_noflag(msg, attr | nested);
226 if (!nl_combis)
227 goto nla_put_failure;
228
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]))
232 + if (radio >= 0) {
233 + c = wiphy->radio[0].iface_combinations;
234 + n = wiphy->radio[0].n_iface_combinations;
235 + } else {
236 + c = wiphy->iface_combinations;
237 + n = wiphy->n_iface_combinations;
238 + }
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;
242
243 nla_nest_end(msg, nl_combis);
244 @@ -2397,6 +2406,80 @@ fail:
245 return -ENOBUFS;
246 }
247
248 +static int nl80211_put_radio(struct wiphy *wiphy, struct sk_buff *msg, int idx)
249 +{
250 + const struct wiphy_radio *r = &wiphy->radio[idx];
251 + struct nlattr *radio, *freq;
252 + int i;
253 +
254 + radio = nla_nest_start(msg, idx);
255 + if (!radio)
256 + return -ENOBUFS;
257 +
258 + if (nla_put_u32(msg, NL80211_WIPHY_RADIO_ATTR_INDEX, idx))
259 + goto nla_put_failure;
260 +
261 + for (i = 0; i < r->n_freq_range; i++) {
262 + const struct wiphy_radio_freq_range *range = &r->freq_range[i];
263 +
264 + freq = nla_nest_start(msg, NL80211_WIPHY_RADIO_ATTR_FREQ_RANGE);
265 + if (!freq)
266 + goto nla_put_failure;
267 +
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,
271 + range->end_freq))
272 + goto nla_put_failure;
273 +
274 + nla_nest_end(msg, freq);
275 + }
276 +
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],
281 + NLA_F_NESTED))
282 + goto nla_put_failure;
283 +
284 + nla_nest_end(msg, radio);
285 +
286 + return 0;
287 +
288 +nla_put_failure:
289 + return -ENOBUFS;
290 +}
291 +
292 +static int nl80211_put_radios(struct wiphy *wiphy, struct sk_buff *msg)
293 +{
294 + struct nlattr *radios;
295 + int i;
296 +
297 + if (!wiphy->n_radio)
298 + return 0;
299 +
300 + radios = nla_nest_start(msg, NL80211_ATTR_WIPHY_RADIOS);
301 + if (!radios)
302 + return -ENOBUFS;
303 +
304 + for (i = 0; i < wiphy->n_radio; i++)
305 + if (nl80211_put_radio(wiphy, msg, i))
306 + goto fail;
307 +
308 + nla_nest_end(msg, radios);
309 +
310 + if (nl80211_put_iface_combinations(wiphy, msg,
311 + NL80211_ATTR_WIPHY_INTERFACE_COMBINATIONS,
312 + -1, true, NLA_F_NESTED))
313 + return -ENOBUFS;
314 +
315 + return 0;
316 +
317 +fail:
318 + nla_nest_cancel(msg, radios);
319 + return -ENOBUFS;
320 +}
321 +
322 struct nl80211_dump_wiphy_state {
323 s64 filter_wiphy;
324 long start;
325 @@ -2692,7 +2775,9 @@ static int nl80211_send_wiphy(struct cfg
326 goto nla_put_failure;
327
328 if (nl80211_put_iface_combinations(&rdev->wiphy, msg,
329 - state->split))
330 + NL80211_ATTR_INTERFACE_COMBINATIONS,
331 + rdev->wiphy.n_radio ? 0 : -1,
332 + state->split, 0))
333 goto nla_put_failure;
334
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;
339
340 + state->split_start++;
341 + break;
342 + case 17:
343 + if (nl80211_put_radios(&rdev->wiphy, msg))
344 + goto nla_put_failure;
345 +
346 /* done */
347 state->split_start = 0;
348 break;