Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
727da60b | 2 | /* |
968a76ce | 3 | * Copyright 2015 Intel Deutschland GmbH |
68f6c6af | 4 | * Copyright (C) 2022-2024 Intel Corporation |
727da60b DV |
5 | */ |
6 | #include <net/mac80211.h> | |
7 | #include "ieee80211_i.h" | |
8 | #include "trace.h" | |
9 | #include "driver-ops.h" | |
d2caad52 | 10 | #include "debugfs_sta.h" |
170cd6a6 | 11 | #include "debugfs_netdev.h" |
727da60b | 12 | |
968a76ce EP |
13 | int drv_start(struct ieee80211_local *local) |
14 | { | |
15 | int ret; | |
16 | ||
17 | might_sleep(); | |
0e8185ce | 18 | lockdep_assert_wiphy(local->hw.wiphy); |
968a76ce EP |
19 | |
20 | if (WARN_ON(local->started)) | |
21 | return -EALREADY; | |
22 | ||
23 | trace_drv_start(local); | |
24 | local->started = true; | |
25 | /* allow rx frames */ | |
26 | smp_mb(); | |
27 | ret = local->ops->start(&local->hw); | |
28 | trace_drv_return_int(local, ret); | |
29 | ||
30 | if (ret) | |
31 | local->started = false; | |
32 | ||
33 | return ret; | |
34 | } | |
35 | ||
36 | void drv_stop(struct ieee80211_local *local) | |
37 | { | |
38 | might_sleep(); | |
0e8185ce | 39 | lockdep_assert_wiphy(local->hw.wiphy); |
968a76ce EP |
40 | |
41 | if (WARN_ON(!local->started)) | |
42 | return; | |
43 | ||
44 | trace_drv_stop(local); | |
45 | local->ops->stop(&local->hw); | |
46 | trace_drv_return_void(local); | |
47 | ||
48 | /* sync away all work on the tasklet before clearing started */ | |
49 | tasklet_disable(&local->tasklet); | |
50 | tasklet_enable(&local->tasklet); | |
51 | ||
52 | barrier(); | |
53 | ||
54 | local->started = false; | |
55 | } | |
56 | ||
9aae296a DV |
57 | int drv_add_interface(struct ieee80211_local *local, |
58 | struct ieee80211_sub_if_data *sdata) | |
59 | { | |
60 | int ret; | |
61 | ||
62 | might_sleep(); | |
0e8185ce | 63 | lockdep_assert_wiphy(local->hw.wiphy); |
9aae296a DV |
64 | |
65 | if (WARN_ON(sdata->vif.type == NL80211_IFTYPE_AP_VLAN || | |
66 | (sdata->vif.type == NL80211_IFTYPE_MONITOR && | |
67 | !ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF) && | |
d8212184 | 68 | !(sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE)))) |
9aae296a DV |
69 | return -EINVAL; |
70 | ||
71 | trace_drv_add_interface(local, sdata); | |
72 | ret = local->ops->add_interface(&local->hw, &sdata->vif); | |
73 | trace_drv_return_int(local, ret); | |
74 | ||
a1f5dcb1 MK |
75 | if (ret) |
76 | return ret; | |
9aae296a | 77 | |
0a3d898e BB |
78 | if (!(sdata->flags & IEEE80211_SDATA_IN_DRIVER)) { |
79 | sdata->flags |= IEEE80211_SDATA_IN_DRIVER; | |
a1f5dcb1 | 80 | |
a1f5dcb1 | 81 | drv_vif_add_debugfs(local, sdata); |
c942398f MK |
82 | /* initially vif is not MLD */ |
83 | ieee80211_link_debugfs_drv_add(&sdata->deflink); | |
84 | } | |
a1f5dcb1 MK |
85 | |
86 | return 0; | |
9aae296a DV |
87 | } |
88 | ||
89 | int drv_change_interface(struct ieee80211_local *local, | |
90 | struct ieee80211_sub_if_data *sdata, | |
91 | enum nl80211_iftype type, bool p2p) | |
92 | { | |
93 | int ret; | |
94 | ||
95 | might_sleep(); | |
0e8185ce | 96 | lockdep_assert_wiphy(local->hw.wiphy); |
9aae296a DV |
97 | |
98 | if (!check_sdata_in_driver(sdata)) | |
99 | return -EIO; | |
100 | ||
101 | trace_drv_change_interface(local, sdata, type, p2p); | |
102 | ret = local->ops->change_interface(&local->hw, &sdata->vif, type, p2p); | |
103 | trace_drv_return_int(local, ret); | |
104 | return ret; | |
105 | } | |
106 | ||
107 | void drv_remove_interface(struct ieee80211_local *local, | |
108 | struct ieee80211_sub_if_data *sdata) | |
109 | { | |
110 | might_sleep(); | |
0e8185ce | 111 | lockdep_assert_wiphy(local->hw.wiphy); |
9aae296a DV |
112 | |
113 | if (!check_sdata_in_driver(sdata)) | |
114 | return; | |
115 | ||
0a3d898e BB |
116 | sdata->flags &= ~IEEE80211_SDATA_IN_DRIVER; |
117 | ||
118 | /* Remove driver debugfs entries */ | |
119 | ieee80211_debugfs_recreate_netdev(sdata, sdata->vif.valid_links); | |
120 | ||
9aae296a DV |
121 | trace_drv_remove_interface(local, sdata); |
122 | local->ops->remove_interface(&local->hw, &sdata->vif); | |
9aae296a DV |
123 | trace_drv_return_void(local); |
124 | } | |
125 | ||
727da60b DV |
126 | __must_check |
127 | int drv_sta_state(struct ieee80211_local *local, | |
128 | struct ieee80211_sub_if_data *sdata, | |
129 | struct sta_info *sta, | |
130 | enum ieee80211_sta_state old_state, | |
131 | enum ieee80211_sta_state new_state) | |
132 | { | |
133 | int ret = 0; | |
134 | ||
135 | might_sleep(); | |
0e8185ce | 136 | lockdep_assert_wiphy(local->hw.wiphy); |
727da60b DV |
137 | |
138 | sdata = get_bss_sdata(sdata); | |
139 | if (!check_sdata_in_driver(sdata)) | |
140 | return -EIO; | |
141 | ||
142 | trace_drv_sta_state(local, sdata, &sta->sta, old_state, new_state); | |
143 | if (local->ops->sta_state) { | |
144 | ret = local->ops->sta_state(&local->hw, &sdata->vif, &sta->sta, | |
145 | old_state, new_state); | |
146 | } else if (old_state == IEEE80211_STA_AUTH && | |
147 | new_state == IEEE80211_STA_ASSOC) { | |
148 | ret = drv_sta_add(local, sdata, &sta->sta); | |
18fe0fae | 149 | if (ret == 0) { |
727da60b | 150 | sta->uploaded = true; |
18fe0fae FF |
151 | if (rcu_access_pointer(sta->sta.rates)) |
152 | drv_sta_rate_tbl_update(local, sdata, &sta->sta); | |
153 | } | |
727da60b DV |
154 | } else if (old_state == IEEE80211_STA_ASSOC && |
155 | new_state == IEEE80211_STA_AUTH) { | |
156 | drv_sta_remove(local, sdata, &sta->sta); | |
157 | } | |
158 | trace_drv_return_int(local, ret); | |
159 | return ret; | |
160 | } | |
b23dcd4a | 161 | |
ba905bf4 ARN |
162 | __must_check |
163 | int drv_sta_set_txpwr(struct ieee80211_local *local, | |
164 | struct ieee80211_sub_if_data *sdata, | |
165 | struct sta_info *sta) | |
166 | { | |
167 | int ret = -EOPNOTSUPP; | |
168 | ||
169 | might_sleep(); | |
0e8185ce | 170 | lockdep_assert_wiphy(local->hw.wiphy); |
ba905bf4 ARN |
171 | |
172 | sdata = get_bss_sdata(sdata); | |
173 | if (!check_sdata_in_driver(sdata)) | |
174 | return -EIO; | |
175 | ||
176 | trace_drv_sta_set_txpwr(local, sdata, &sta->sta); | |
177 | if (local->ops->sta_set_txpwr) | |
178 | ret = local->ops->sta_set_txpwr(&local->hw, &sdata->vif, | |
179 | &sta->sta); | |
180 | trace_drv_return_int(local, ret); | |
181 | return ret; | |
182 | } | |
183 | ||
4fbd572c DV |
184 | void drv_sta_rc_update(struct ieee80211_local *local, |
185 | struct ieee80211_sub_if_data *sdata, | |
186 | struct ieee80211_sta *sta, u32 changed) | |
187 | { | |
188 | sdata = get_bss_sdata(sdata); | |
189 | if (!check_sdata_in_driver(sdata)) | |
190 | return; | |
191 | ||
192 | WARN_ON(changed & IEEE80211_RC_SUPP_RATES_CHANGED && | |
193 | (sdata->vif.type != NL80211_IFTYPE_ADHOC && | |
194 | sdata->vif.type != NL80211_IFTYPE_MESH_POINT)); | |
195 | ||
196 | trace_drv_sta_rc_update(local, sdata, sta, changed); | |
197 | if (local->ops->sta_rc_update) | |
198 | local->ops->sta_rc_update(&local->hw, &sdata->vif, | |
199 | sta, changed); | |
200 | ||
201 | trace_drv_return_void(local); | |
202 | } | |
203 | ||
b23dcd4a | 204 | int drv_conf_tx(struct ieee80211_local *local, |
b3e2130b | 205 | struct ieee80211_link_data *link, u16 ac, |
b23dcd4a DV |
206 | const struct ieee80211_tx_queue_params *params) |
207 | { | |
b3e2130b | 208 | struct ieee80211_sub_if_data *sdata = link->sdata; |
b23dcd4a DV |
209 | int ret = -EOPNOTSUPP; |
210 | ||
211 | might_sleep(); | |
0e8185ce | 212 | lockdep_assert_wiphy(local->hw.wiphy); |
b23dcd4a DV |
213 | |
214 | if (!check_sdata_in_driver(sdata)) | |
215 | return -EIO; | |
216 | ||
68f6c6af | 217 | if (!ieee80211_vif_link_active(&sdata->vif, link->link_id)) |
efe9c2bf JB |
218 | return 0; |
219 | ||
d2b3fe42 BN |
220 | if (params->cw_min == 0 || params->cw_min > params->cw_max) { |
221 | /* | |
222 | * If we can't configure hardware anyway, don't warn. We may | |
223 | * never have initialized the CW parameters. | |
224 | */ | |
225 | WARN_ONCE(local->ops->conf_tx, | |
226 | "%s: invalid CW_min/CW_max: %d/%d\n", | |
227 | sdata->name, params->cw_min, params->cw_max); | |
b23dcd4a | 228 | return -EINVAL; |
d2b3fe42 | 229 | } |
b23dcd4a | 230 | |
b3e2130b | 231 | trace_drv_conf_tx(local, sdata, link->link_id, ac, params); |
b23dcd4a DV |
232 | if (local->ops->conf_tx) |
233 | ret = local->ops->conf_tx(&local->hw, &sdata->vif, | |
b3e2130b | 234 | link->link_id, ac, params); |
b23dcd4a DV |
235 | trace_drv_return_int(local, ret); |
236 | return ret; | |
237 | } | |
42677ed3 | 238 | |
416eb9fc DV |
239 | u64 drv_get_tsf(struct ieee80211_local *local, |
240 | struct ieee80211_sub_if_data *sdata) | |
241 | { | |
242 | u64 ret = -1ULL; | |
243 | ||
244 | might_sleep(); | |
0e8185ce | 245 | lockdep_assert_wiphy(local->hw.wiphy); |
416eb9fc DV |
246 | |
247 | if (!check_sdata_in_driver(sdata)) | |
248 | return ret; | |
249 | ||
250 | trace_drv_get_tsf(local, sdata); | |
251 | if (local->ops->get_tsf) | |
252 | ret = local->ops->get_tsf(&local->hw, &sdata->vif); | |
253 | trace_drv_return_u64(local, ret); | |
254 | return ret; | |
255 | } | |
256 | ||
257 | void drv_set_tsf(struct ieee80211_local *local, | |
258 | struct ieee80211_sub_if_data *sdata, | |
259 | u64 tsf) | |
260 | { | |
261 | might_sleep(); | |
0e8185ce | 262 | lockdep_assert_wiphy(local->hw.wiphy); |
416eb9fc DV |
263 | |
264 | if (!check_sdata_in_driver(sdata)) | |
265 | return; | |
266 | ||
267 | trace_drv_set_tsf(local, sdata, tsf); | |
268 | if (local->ops->set_tsf) | |
269 | local->ops->set_tsf(&local->hw, &sdata->vif, tsf); | |
270 | trace_drv_return_void(local); | |
271 | } | |
272 | ||
354d381b PT |
273 | void drv_offset_tsf(struct ieee80211_local *local, |
274 | struct ieee80211_sub_if_data *sdata, | |
275 | s64 offset) | |
276 | { | |
277 | might_sleep(); | |
0e8185ce | 278 | lockdep_assert_wiphy(local->hw.wiphy); |
354d381b PT |
279 | |
280 | if (!check_sdata_in_driver(sdata)) | |
281 | return; | |
282 | ||
283 | trace_drv_offset_tsf(local, sdata, offset); | |
284 | if (local->ops->offset_tsf) | |
285 | local->ops->offset_tsf(&local->hw, &sdata->vif, offset); | |
286 | trace_drv_return_void(local); | |
287 | } | |
288 | ||
416eb9fc DV |
289 | void drv_reset_tsf(struct ieee80211_local *local, |
290 | struct ieee80211_sub_if_data *sdata) | |
291 | { | |
292 | might_sleep(); | |
0e8185ce | 293 | lockdep_assert_wiphy(local->hw.wiphy); |
416eb9fc DV |
294 | |
295 | if (!check_sdata_in_driver(sdata)) | |
296 | return; | |
297 | ||
298 | trace_drv_reset_tsf(local, sdata); | |
299 | if (local->ops->reset_tsf) | |
300 | local->ops->reset_tsf(&local->hw, &sdata->vif); | |
301 | trace_drv_return_void(local); | |
302 | } | |
303 | ||
efe9c2bf JB |
304 | int drv_assign_vif_chanctx(struct ieee80211_local *local, |
305 | struct ieee80211_sub_if_data *sdata, | |
306 | struct ieee80211_bss_conf *link_conf, | |
307 | struct ieee80211_chanctx *ctx) | |
308 | { | |
309 | int ret = 0; | |
310 | ||
5549b088 | 311 | might_sleep(); |
0e8185ce | 312 | lockdep_assert_wiphy(local->hw.wiphy); |
5549b088 | 313 | |
0d9c2bee JB |
314 | /* |
315 | * We should perhaps push emulate chanctx down and only | |
316 | * make it call ->config() when the chanctx is actually | |
317 | * assigned here (and unassigned below), but that's yet | |
318 | * another change to all drivers to add assign/unassign | |
319 | * emulation callbacks. Maybe later. | |
320 | */ | |
321 | if (sdata->vif.type == NL80211_IFTYPE_MONITOR && | |
322 | local->emulate_chanctx && | |
323 | !ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF)) | |
324 | return 0; | |
325 | ||
efe9c2bf JB |
326 | if (!check_sdata_in_driver(sdata)) |
327 | return -EIO; | |
328 | ||
68f6c6af | 329 | if (!ieee80211_vif_link_active(&sdata->vif, link_conf->link_id)) |
efe9c2bf JB |
330 | return 0; |
331 | ||
332 | trace_drv_assign_vif_chanctx(local, sdata, link_conf, ctx); | |
333 | if (local->ops->assign_vif_chanctx) { | |
334 | WARN_ON_ONCE(!ctx->driver_present); | |
335 | ret = local->ops->assign_vif_chanctx(&local->hw, | |
336 | &sdata->vif, | |
337 | link_conf, | |
338 | &ctx->conf); | |
339 | } | |
340 | trace_drv_return_int(local, ret); | |
341 | ||
342 | return ret; | |
343 | } | |
344 | ||
345 | void drv_unassign_vif_chanctx(struct ieee80211_local *local, | |
346 | struct ieee80211_sub_if_data *sdata, | |
347 | struct ieee80211_bss_conf *link_conf, | |
348 | struct ieee80211_chanctx *ctx) | |
349 | { | |
350 | might_sleep(); | |
0e8185ce | 351 | lockdep_assert_wiphy(local->hw.wiphy); |
efe9c2bf | 352 | |
0d9c2bee JB |
353 | if (sdata->vif.type == NL80211_IFTYPE_MONITOR && |
354 | local->emulate_chanctx && | |
355 | !ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF)) | |
356 | return; | |
357 | ||
efe9c2bf JB |
358 | if (!check_sdata_in_driver(sdata)) |
359 | return; | |
360 | ||
68f6c6af | 361 | if (!ieee80211_vif_link_active(&sdata->vif, link_conf->link_id)) |
efe9c2bf JB |
362 | return; |
363 | ||
364 | trace_drv_unassign_vif_chanctx(local, sdata, link_conf, ctx); | |
365 | if (local->ops->unassign_vif_chanctx) { | |
366 | WARN_ON_ONCE(!ctx->driver_present); | |
367 | local->ops->unassign_vif_chanctx(&local->hw, | |
368 | &sdata->vif, | |
369 | link_conf, | |
370 | &ctx->conf); | |
371 | } | |
372 | trace_drv_return_void(local); | |
373 | } | |
374 | ||
42677ed3 DV |
375 | int drv_switch_vif_chanctx(struct ieee80211_local *local, |
376 | struct ieee80211_vif_chanctx_switch *vifs, | |
377 | int n_vifs, enum ieee80211_chanctx_switch_mode mode) | |
378 | { | |
379 | int ret = 0; | |
380 | int i; | |
381 | ||
dcae9e02 | 382 | might_sleep(); |
0e8185ce | 383 | lockdep_assert_wiphy(local->hw.wiphy); |
dcae9e02 | 384 | |
42677ed3 DV |
385 | if (!local->ops->switch_vif_chanctx) |
386 | return -EOPNOTSUPP; | |
387 | ||
388 | for (i = 0; i < n_vifs; i++) { | |
389 | struct ieee80211_chanctx *new_ctx = | |
390 | container_of(vifs[i].new_ctx, | |
391 | struct ieee80211_chanctx, | |
392 | conf); | |
393 | struct ieee80211_chanctx *old_ctx = | |
394 | container_of(vifs[i].old_ctx, | |
395 | struct ieee80211_chanctx, | |
396 | conf); | |
397 | ||
398 | WARN_ON_ONCE(!old_ctx->driver_present); | |
399 | WARN_ON_ONCE((mode == CHANCTX_SWMODE_SWAP_CONTEXTS && | |
400 | new_ctx->driver_present) || | |
401 | (mode == CHANCTX_SWMODE_REASSIGN_VIF && | |
402 | !new_ctx->driver_present)); | |
403 | } | |
404 | ||
405 | trace_drv_switch_vif_chanctx(local, vifs, n_vifs, mode); | |
406 | ret = local->ops->switch_vif_chanctx(&local->hw, | |
407 | vifs, n_vifs, mode); | |
408 | trace_drv_return_int(local, ret); | |
409 | ||
410 | if (!ret && mode == CHANCTX_SWMODE_SWAP_CONTEXTS) { | |
411 | for (i = 0; i < n_vifs; i++) { | |
412 | struct ieee80211_chanctx *new_ctx = | |
413 | container_of(vifs[i].new_ctx, | |
414 | struct ieee80211_chanctx, | |
415 | conf); | |
416 | struct ieee80211_chanctx *old_ctx = | |
417 | container_of(vifs[i].old_ctx, | |
418 | struct ieee80211_chanctx, | |
419 | conf); | |
420 | ||
421 | new_ctx->driver_present = true; | |
422 | old_ctx->driver_present = false; | |
423 | } | |
424 | } | |
425 | ||
426 | return ret; | |
427 | } | |
6db96838 DV |
428 | |
429 | int drv_ampdu_action(struct ieee80211_local *local, | |
430 | struct ieee80211_sub_if_data *sdata, | |
50ea05ef | 431 | struct ieee80211_ampdu_params *params) |
6db96838 DV |
432 | { |
433 | int ret = -EOPNOTSUPP; | |
434 | ||
435 | might_sleep(); | |
0e8185ce | 436 | lockdep_assert_wiphy(local->hw.wiphy); |
6db96838 DV |
437 | |
438 | sdata = get_bss_sdata(sdata); | |
439 | if (!check_sdata_in_driver(sdata)) | |
440 | return -EIO; | |
441 | ||
50ea05ef | 442 | trace_drv_ampdu_action(local, sdata, params); |
6db96838 DV |
443 | |
444 | if (local->ops->ampdu_action) | |
50ea05ef | 445 | ret = local->ops->ampdu_action(&local->hw, &sdata->vif, params); |
6db96838 DV |
446 | |
447 | trace_drv_return_int(local, ret); | |
448 | ||
449 | return ret; | |
450 | } | |
efe9c2bf JB |
451 | |
452 | void drv_link_info_changed(struct ieee80211_local *local, | |
453 | struct ieee80211_sub_if_data *sdata, | |
454 | struct ieee80211_bss_conf *info, | |
455 | int link_id, u64 changed) | |
456 | { | |
457 | might_sleep(); | |
0e8185ce | 458 | lockdep_assert_wiphy(local->hw.wiphy); |
efe9c2bf JB |
459 | |
460 | if (WARN_ON_ONCE(changed & (BSS_CHANGED_BEACON | | |
461 | BSS_CHANGED_BEACON_ENABLED) && | |
462 | sdata->vif.type != NL80211_IFTYPE_AP && | |
463 | sdata->vif.type != NL80211_IFTYPE_ADHOC && | |
464 | sdata->vif.type != NL80211_IFTYPE_MESH_POINT && | |
465 | sdata->vif.type != NL80211_IFTYPE_OCB)) | |
466 | return; | |
467 | ||
468 | if (WARN_ON_ONCE(sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE || | |
469 | sdata->vif.type == NL80211_IFTYPE_NAN || | |
470 | (sdata->vif.type == NL80211_IFTYPE_MONITOR && | |
471 | !sdata->vif.bss_conf.mu_mimo_owner && | |
472 | !(changed & BSS_CHANGED_TXPOWER)))) | |
473 | return; | |
474 | ||
475 | if (!check_sdata_in_driver(sdata)) | |
476 | return; | |
477 | ||
68f6c6af | 478 | if (!ieee80211_vif_link_active(&sdata->vif, link_id)) |
efe9c2bf JB |
479 | return; |
480 | ||
481 | trace_drv_link_info_changed(local, sdata, info, changed); | |
482 | if (local->ops->link_info_changed) | |
483 | local->ops->link_info_changed(&local->hw, &sdata->vif, | |
484 | info, changed); | |
485 | else if (local->ops->bss_info_changed) | |
486 | local->ops->bss_info_changed(&local->hw, &sdata->vif, | |
487 | info, changed); | |
488 | trace_drv_return_void(local); | |
489 | } | |
490 | ||
491 | int drv_set_key(struct ieee80211_local *local, | |
492 | enum set_key_cmd cmd, | |
493 | struct ieee80211_sub_if_data *sdata, | |
494 | struct ieee80211_sta *sta, | |
495 | struct ieee80211_key_conf *key) | |
496 | { | |
497 | int ret; | |
498 | ||
499 | might_sleep(); | |
0e8185ce | 500 | lockdep_assert_wiphy(local->hw.wiphy); |
efe9c2bf JB |
501 | |
502 | sdata = get_bss_sdata(sdata); | |
503 | if (!check_sdata_in_driver(sdata)) | |
504 | return -EIO; | |
505 | ||
506 | if (WARN_ON(key->link_id >= 0 && sdata->vif.active_links && | |
507 | !(sdata->vif.active_links & BIT(key->link_id)))) | |
508 | return -ENOLINK; | |
509 | ||
510 | trace_drv_set_key(local, cmd, sdata, sta, key); | |
511 | ret = local->ops->set_key(&local->hw, cmd, &sdata->vif, sta, key); | |
512 | trace_drv_return_int(local, ret); | |
513 | return ret; | |
514 | } | |
515 | ||
516 | int drv_change_vif_links(struct ieee80211_local *local, | |
517 | struct ieee80211_sub_if_data *sdata, | |
518 | u16 old_links, u16 new_links, | |
519 | struct ieee80211_bss_conf *old[IEEE80211_MLD_MAX_NUM_LINKS]) | |
520 | { | |
170cd6a6 BB |
521 | struct ieee80211_link_data *link; |
522 | unsigned long links_to_add; | |
523 | unsigned long links_to_rem; | |
524 | unsigned int link_id; | |
efe9c2bf JB |
525 | int ret = -EOPNOTSUPP; |
526 | ||
527 | might_sleep(); | |
0e8185ce | 528 | lockdep_assert_wiphy(local->hw.wiphy); |
efe9c2bf JB |
529 | |
530 | if (!check_sdata_in_driver(sdata)) | |
531 | return -EIO; | |
532 | ||
533 | if (old_links == new_links) | |
534 | return 0; | |
535 | ||
170cd6a6 BB |
536 | links_to_add = ~old_links & new_links; |
537 | links_to_rem = old_links & ~new_links; | |
538 | ||
539 | for_each_set_bit(link_id, &links_to_rem, IEEE80211_MLD_MAX_NUM_LINKS) { | |
540 | link = rcu_access_pointer(sdata->link[link_id]); | |
541 | ||
542 | ieee80211_link_debugfs_drv_remove(link); | |
543 | } | |
544 | ||
efe9c2bf JB |
545 | trace_drv_change_vif_links(local, sdata, old_links, new_links); |
546 | if (local->ops->change_vif_links) | |
547 | ret = local->ops->change_vif_links(&local->hw, &sdata->vif, | |
548 | old_links, new_links, old); | |
549 | trace_drv_return_int(local, ret); | |
550 | ||
170cd6a6 BB |
551 | if (ret) |
552 | return ret; | |
553 | ||
8c917f16 | 554 | if (!local->in_reconfig && !local->resuming) { |
822cab19 JB |
555 | for_each_set_bit(link_id, &links_to_add, |
556 | IEEE80211_MLD_MAX_NUM_LINKS) { | |
557 | link = rcu_access_pointer(sdata->link[link_id]); | |
170cd6a6 | 558 | |
822cab19 JB |
559 | ieee80211_link_debugfs_drv_add(link); |
560 | } | |
170cd6a6 BB |
561 | } |
562 | ||
563 | return 0; | |
efe9c2bf JB |
564 | } |
565 | ||
566 | int drv_change_sta_links(struct ieee80211_local *local, | |
567 | struct ieee80211_sub_if_data *sdata, | |
568 | struct ieee80211_sta *sta, | |
569 | u16 old_links, u16 new_links) | |
570 | { | |
d2caad52 BB |
571 | struct sta_info *info = container_of(sta, struct sta_info, sta); |
572 | struct link_sta_info *link_sta; | |
573 | unsigned long links_to_add; | |
574 | unsigned long links_to_rem; | |
575 | unsigned int link_id; | |
efe9c2bf JB |
576 | int ret = -EOPNOTSUPP; |
577 | ||
578 | might_sleep(); | |
0e8185ce | 579 | lockdep_assert_wiphy(local->hw.wiphy); |
efe9c2bf JB |
580 | |
581 | if (!check_sdata_in_driver(sdata)) | |
582 | return -EIO; | |
583 | ||
584 | old_links &= sdata->vif.active_links; | |
585 | new_links &= sdata->vif.active_links; | |
586 | ||
587 | if (old_links == new_links) | |
588 | return 0; | |
589 | ||
d2caad52 BB |
590 | links_to_add = ~old_links & new_links; |
591 | links_to_rem = old_links & ~new_links; | |
592 | ||
593 | for_each_set_bit(link_id, &links_to_rem, IEEE80211_MLD_MAX_NUM_LINKS) { | |
594 | link_sta = rcu_dereference_protected(info->link[link_id], | |
4d3acf43 | 595 | lockdep_is_held(&local->hw.wiphy->mtx)); |
d2caad52 BB |
596 | |
597 | ieee80211_link_sta_debugfs_drv_remove(link_sta); | |
598 | } | |
599 | ||
efe9c2bf JB |
600 | trace_drv_change_sta_links(local, sdata, sta, old_links, new_links); |
601 | if (local->ops->change_sta_links) | |
602 | ret = local->ops->change_sta_links(&local->hw, &sdata->vif, sta, | |
603 | old_links, new_links); | |
604 | trace_drv_return_int(local, ret); | |
605 | ||
d2caad52 BB |
606 | if (ret) |
607 | return ret; | |
608 | ||
63bafd9d | 609 | /* during reconfig don't add it to debugfs again */ |
8c917f16 | 610 | if (local->in_reconfig || local->resuming) |
63bafd9d JB |
611 | return 0; |
612 | ||
d2caad52 BB |
613 | for_each_set_bit(link_id, &links_to_add, IEEE80211_MLD_MAX_NUM_LINKS) { |
614 | link_sta = rcu_dereference_protected(info->link[link_id], | |
4d3acf43 | 615 | lockdep_is_held(&local->hw.wiphy->mtx)); |
d2caad52 BB |
616 | ieee80211_link_sta_debugfs_drv_add(link_sta); |
617 | } | |
618 | ||
619 | return 0; | |
efe9c2bf | 620 | } |