1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright 2015 Intel Deutschland GmbH |
4 | * Copyright (C) 2022-2024 Intel Corporation |
5 | */ |
6 | #include <net/mac80211.h> |
7 | #include "ieee80211_i.h" |
8 | #include "trace.h" |
9 | #include "driver-ops.h" |
10 | #include "debugfs_sta.h" |
11 | #include "debugfs_netdev.h" |
12 | |
13 | int drv_start(struct ieee80211_local *local) |
14 | { |
15 | int ret; |
16 | |
17 | might_sleep(); |
18 | lockdep_assert_wiphy(local->hw.wiphy); |
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(); |
39 | lockdep_assert_wiphy(local->hw.wiphy); |
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(t: &local->tasklet); |
50 | tasklet_enable(t: &local->tasklet); |
51 | |
52 | barrier(); |
53 | |
54 | local->started = false; |
55 | } |
56 | |
57 | int drv_add_interface(struct ieee80211_local *local, |
58 | struct ieee80211_sub_if_data *sdata) |
59 | { |
60 | int ret; |
61 | |
62 | might_sleep(); |
63 | lockdep_assert_wiphy(local->hw.wiphy); |
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) && |
68 | !(sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE)))) |
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 | |
75 | if (ret) |
76 | return ret; |
77 | |
78 | if (!(sdata->flags & IEEE80211_SDATA_IN_DRIVER)) { |
79 | sdata->flags |= IEEE80211_SDATA_IN_DRIVER; |
80 | |
81 | drv_vif_add_debugfs(local, sdata); |
82 | /* initially vif is not MLD */ |
83 | ieee80211_link_debugfs_drv_add(link: &sdata->deflink); |
84 | } |
85 | |
86 | return 0; |
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(); |
96 | lockdep_assert_wiphy(local->hw.wiphy); |
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(); |
111 | lockdep_assert_wiphy(local->hw.wiphy); |
112 | |
113 | if (!check_sdata_in_driver(sdata)) |
114 | return; |
115 | |
116 | sdata->flags &= ~IEEE80211_SDATA_IN_DRIVER; |
117 | |
118 | /* Remove driver debugfs entries */ |
119 | ieee80211_debugfs_recreate_netdev(sdata, mld_vif: sdata->vif.valid_links); |
120 | |
121 | trace_drv_remove_interface(local, sdata); |
122 | local->ops->remove_interface(&local->hw, &sdata->vif); |
123 | trace_drv_return_void(local); |
124 | } |
125 | |
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(); |
136 | lockdep_assert_wiphy(local->hw.wiphy); |
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->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->sta); |
149 | if (ret == 0) { |
150 | sta->uploaded = true; |
151 | if (rcu_access_pointer(sta->sta.rates)) |
152 | drv_sta_rate_tbl_update(local, sdata, sta: &sta->sta); |
153 | } |
154 | } else if (old_state == IEEE80211_STA_ASSOC && |
155 | new_state == IEEE80211_STA_AUTH) { |
156 | drv_sta_remove(local, sdata, sta: &sta->sta); |
157 | } |
158 | trace_drv_return_int(local, ret); |
159 | return ret; |
160 | } |
161 | |
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(); |
170 | lockdep_assert_wiphy(local->hw.wiphy); |
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->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 | |
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 | |
204 | int drv_conf_tx(struct ieee80211_local *local, |
205 | struct ieee80211_link_data *link, u16 ac, |
206 | const struct ieee80211_tx_queue_params *params) |
207 | { |
208 | struct ieee80211_sub_if_data *sdata = link->sdata; |
209 | int ret = -EOPNOTSUPP; |
210 | |
211 | might_sleep(); |
212 | lockdep_assert_wiphy(local->hw.wiphy); |
213 | |
214 | if (!check_sdata_in_driver(sdata)) |
215 | return -EIO; |
216 | |
217 | if (!ieee80211_vif_link_active(vif: &sdata->vif, link_id: link->link_id)) |
218 | return 0; |
219 | |
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); |
228 | return -EINVAL; |
229 | } |
230 | |
231 | trace_drv_conf_tx(local, sdata, link_id: link->link_id, ac, params); |
232 | if (local->ops->conf_tx) |
233 | ret = local->ops->conf_tx(&local->hw, &sdata->vif, |
234 | link->link_id, ac, params); |
235 | trace_drv_return_int(local, ret); |
236 | return ret; |
237 | } |
238 | |
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(); |
245 | lockdep_assert_wiphy(local->hw.wiphy); |
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(); |
262 | lockdep_assert_wiphy(local->hw.wiphy); |
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 | |
273 | void drv_offset_tsf(struct ieee80211_local *local, |
274 | struct ieee80211_sub_if_data *sdata, |
275 | s64 offset) |
276 | { |
277 | might_sleep(); |
278 | lockdep_assert_wiphy(local->hw.wiphy); |
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 | |
289 | void drv_reset_tsf(struct ieee80211_local *local, |
290 | struct ieee80211_sub_if_data *sdata) |
291 | { |
292 | might_sleep(); |
293 | lockdep_assert_wiphy(local->hw.wiphy); |
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 | |
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 | |
311 | might_sleep(); |
312 | lockdep_assert_wiphy(local->hw.wiphy); |
313 | |
314 | if (!check_sdata_in_driver(sdata)) |
315 | return -EIO; |
316 | |
317 | if (!ieee80211_vif_link_active(vif: &sdata->vif, link_id: link_conf->link_id)) |
318 | return 0; |
319 | |
320 | trace_drv_assign_vif_chanctx(local, sdata, link_conf, ctx); |
321 | if (local->ops->assign_vif_chanctx) { |
322 | WARN_ON_ONCE(!ctx->driver_present); |
323 | ret = local->ops->assign_vif_chanctx(&local->hw, |
324 | &sdata->vif, |
325 | link_conf, |
326 | &ctx->conf); |
327 | } |
328 | trace_drv_return_int(local, ret); |
329 | |
330 | return ret; |
331 | } |
332 | |
333 | void drv_unassign_vif_chanctx(struct ieee80211_local *local, |
334 | struct ieee80211_sub_if_data *sdata, |
335 | struct ieee80211_bss_conf *link_conf, |
336 | struct ieee80211_chanctx *ctx) |
337 | { |
338 | might_sleep(); |
339 | lockdep_assert_wiphy(local->hw.wiphy); |
340 | |
341 | if (!check_sdata_in_driver(sdata)) |
342 | return; |
343 | |
344 | if (!ieee80211_vif_link_active(vif: &sdata->vif, link_id: link_conf->link_id)) |
345 | return; |
346 | |
347 | trace_drv_unassign_vif_chanctx(local, sdata, link_conf, ctx); |
348 | if (local->ops->unassign_vif_chanctx) { |
349 | WARN_ON_ONCE(!ctx->driver_present); |
350 | local->ops->unassign_vif_chanctx(&local->hw, |
351 | &sdata->vif, |
352 | link_conf, |
353 | &ctx->conf); |
354 | } |
355 | trace_drv_return_void(local); |
356 | } |
357 | |
358 | int drv_switch_vif_chanctx(struct ieee80211_local *local, |
359 | struct ieee80211_vif_chanctx_switch *vifs, |
360 | int n_vifs, enum ieee80211_chanctx_switch_mode mode) |
361 | { |
362 | int ret = 0; |
363 | int i; |
364 | |
365 | might_sleep(); |
366 | lockdep_assert_wiphy(local->hw.wiphy); |
367 | |
368 | if (!local->ops->switch_vif_chanctx) |
369 | return -EOPNOTSUPP; |
370 | |
371 | for (i = 0; i < n_vifs; i++) { |
372 | struct ieee80211_chanctx *new_ctx = |
373 | container_of(vifs[i].new_ctx, |
374 | struct ieee80211_chanctx, |
375 | conf); |
376 | struct ieee80211_chanctx *old_ctx = |
377 | container_of(vifs[i].old_ctx, |
378 | struct ieee80211_chanctx, |
379 | conf); |
380 | |
381 | WARN_ON_ONCE(!old_ctx->driver_present); |
382 | WARN_ON_ONCE((mode == CHANCTX_SWMODE_SWAP_CONTEXTS && |
383 | new_ctx->driver_present) || |
384 | (mode == CHANCTX_SWMODE_REASSIGN_VIF && |
385 | !new_ctx->driver_present)); |
386 | } |
387 | |
388 | trace_drv_switch_vif_chanctx(local, vifs, n_vifs, mode); |
389 | ret = local->ops->switch_vif_chanctx(&local->hw, |
390 | vifs, n_vifs, mode); |
391 | trace_drv_return_int(local, ret); |
392 | |
393 | if (!ret && mode == CHANCTX_SWMODE_SWAP_CONTEXTS) { |
394 | for (i = 0; i < n_vifs; i++) { |
395 | struct ieee80211_chanctx *new_ctx = |
396 | container_of(vifs[i].new_ctx, |
397 | struct ieee80211_chanctx, |
398 | conf); |
399 | struct ieee80211_chanctx *old_ctx = |
400 | container_of(vifs[i].old_ctx, |
401 | struct ieee80211_chanctx, |
402 | conf); |
403 | |
404 | new_ctx->driver_present = true; |
405 | old_ctx->driver_present = false; |
406 | } |
407 | } |
408 | |
409 | return ret; |
410 | } |
411 | |
412 | int drv_ampdu_action(struct ieee80211_local *local, |
413 | struct ieee80211_sub_if_data *sdata, |
414 | struct ieee80211_ampdu_params *params) |
415 | { |
416 | int ret = -EOPNOTSUPP; |
417 | |
418 | might_sleep(); |
419 | lockdep_assert_wiphy(local->hw.wiphy); |
420 | |
421 | sdata = get_bss_sdata(sdata); |
422 | if (!check_sdata_in_driver(sdata)) |
423 | return -EIO; |
424 | |
425 | trace_drv_ampdu_action(local, sdata, params); |
426 | |
427 | if (local->ops->ampdu_action) |
428 | ret = local->ops->ampdu_action(&local->hw, &sdata->vif, params); |
429 | |
430 | trace_drv_return_int(local, ret); |
431 | |
432 | return ret; |
433 | } |
434 | |
435 | void drv_link_info_changed(struct ieee80211_local *local, |
436 | struct ieee80211_sub_if_data *sdata, |
437 | struct ieee80211_bss_conf *info, |
438 | int link_id, u64 changed) |
439 | { |
440 | might_sleep(); |
441 | lockdep_assert_wiphy(local->hw.wiphy); |
442 | |
443 | if (WARN_ON_ONCE(changed & (BSS_CHANGED_BEACON | |
444 | BSS_CHANGED_BEACON_ENABLED) && |
445 | sdata->vif.type != NL80211_IFTYPE_AP && |
446 | sdata->vif.type != NL80211_IFTYPE_ADHOC && |
447 | sdata->vif.type != NL80211_IFTYPE_MESH_POINT && |
448 | sdata->vif.type != NL80211_IFTYPE_OCB)) |
449 | return; |
450 | |
451 | if (WARN_ON_ONCE(sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE || |
452 | sdata->vif.type == NL80211_IFTYPE_NAN || |
453 | (sdata->vif.type == NL80211_IFTYPE_MONITOR && |
454 | !sdata->vif.bss_conf.mu_mimo_owner && |
455 | !(changed & BSS_CHANGED_TXPOWER)))) |
456 | return; |
457 | |
458 | if (!check_sdata_in_driver(sdata)) |
459 | return; |
460 | |
461 | if (!ieee80211_vif_link_active(vif: &sdata->vif, link_id)) |
462 | return; |
463 | |
464 | trace_drv_link_info_changed(local, sdata, link_conf: info, changed); |
465 | if (local->ops->link_info_changed) |
466 | local->ops->link_info_changed(&local->hw, &sdata->vif, |
467 | info, changed); |
468 | else if (local->ops->bss_info_changed) |
469 | local->ops->bss_info_changed(&local->hw, &sdata->vif, |
470 | info, changed); |
471 | trace_drv_return_void(local); |
472 | } |
473 | |
474 | int drv_set_key(struct ieee80211_local *local, |
475 | enum set_key_cmd cmd, |
476 | struct ieee80211_sub_if_data *sdata, |
477 | struct ieee80211_sta *sta, |
478 | struct ieee80211_key_conf *key) |
479 | { |
480 | int ret; |
481 | |
482 | might_sleep(); |
483 | lockdep_assert_wiphy(local->hw.wiphy); |
484 | |
485 | sdata = get_bss_sdata(sdata); |
486 | if (!check_sdata_in_driver(sdata)) |
487 | return -EIO; |
488 | |
489 | if (WARN_ON(key->link_id >= 0 && sdata->vif.active_links && |
490 | !(sdata->vif.active_links & BIT(key->link_id)))) |
491 | return -ENOLINK; |
492 | |
493 | trace_drv_set_key(local, cmd, sdata, sta, key); |
494 | ret = local->ops->set_key(&local->hw, cmd, &sdata->vif, sta, key); |
495 | trace_drv_return_int(local, ret); |
496 | return ret; |
497 | } |
498 | |
499 | int drv_change_vif_links(struct ieee80211_local *local, |
500 | struct ieee80211_sub_if_data *sdata, |
501 | u16 old_links, u16 new_links, |
502 | struct ieee80211_bss_conf *old[IEEE80211_MLD_MAX_NUM_LINKS]) |
503 | { |
504 | struct ieee80211_link_data *link; |
505 | unsigned long links_to_add; |
506 | unsigned long links_to_rem; |
507 | unsigned int link_id; |
508 | int ret = -EOPNOTSUPP; |
509 | |
510 | might_sleep(); |
511 | lockdep_assert_wiphy(local->hw.wiphy); |
512 | |
513 | if (!check_sdata_in_driver(sdata)) |
514 | return -EIO; |
515 | |
516 | if (old_links == new_links) |
517 | return 0; |
518 | |
519 | links_to_add = ~old_links & new_links; |
520 | links_to_rem = old_links & ~new_links; |
521 | |
522 | for_each_set_bit(link_id, &links_to_rem, IEEE80211_MLD_MAX_NUM_LINKS) { |
523 | link = rcu_access_pointer(sdata->link[link_id]); |
524 | |
525 | ieee80211_link_debugfs_drv_remove(link); |
526 | } |
527 | |
528 | trace_drv_change_vif_links(local, sdata, old_links, new_links); |
529 | if (local->ops->change_vif_links) |
530 | ret = local->ops->change_vif_links(&local->hw, &sdata->vif, |
531 | old_links, new_links, old); |
532 | trace_drv_return_int(local, ret); |
533 | |
534 | if (ret) |
535 | return ret; |
536 | |
537 | if (!local->in_reconfig && !local->resuming) { |
538 | for_each_set_bit(link_id, &links_to_add, |
539 | IEEE80211_MLD_MAX_NUM_LINKS) { |
540 | link = rcu_access_pointer(sdata->link[link_id]); |
541 | |
542 | ieee80211_link_debugfs_drv_add(link); |
543 | } |
544 | } |
545 | |
546 | return 0; |
547 | } |
548 | |
549 | int drv_change_sta_links(struct ieee80211_local *local, |
550 | struct ieee80211_sub_if_data *sdata, |
551 | struct ieee80211_sta *sta, |
552 | u16 old_links, u16 new_links) |
553 | { |
554 | struct sta_info *info = container_of(sta, struct sta_info, sta); |
555 | struct link_sta_info *link_sta; |
556 | unsigned long links_to_add; |
557 | unsigned long links_to_rem; |
558 | unsigned int link_id; |
559 | int ret = -EOPNOTSUPP; |
560 | |
561 | might_sleep(); |
562 | lockdep_assert_wiphy(local->hw.wiphy); |
563 | |
564 | if (!check_sdata_in_driver(sdata)) |
565 | return -EIO; |
566 | |
567 | old_links &= sdata->vif.active_links; |
568 | new_links &= sdata->vif.active_links; |
569 | |
570 | if (old_links == new_links) |
571 | return 0; |
572 | |
573 | links_to_add = ~old_links & new_links; |
574 | links_to_rem = old_links & ~new_links; |
575 | |
576 | for_each_set_bit(link_id, &links_to_rem, IEEE80211_MLD_MAX_NUM_LINKS) { |
577 | link_sta = rcu_dereference_protected(info->link[link_id], |
578 | lockdep_is_held(&local->hw.wiphy->mtx)); |
579 | |
580 | ieee80211_link_sta_debugfs_drv_remove(link_sta); |
581 | } |
582 | |
583 | trace_drv_change_sta_links(local, sdata, sta, old_links, new_links); |
584 | if (local->ops->change_sta_links) |
585 | ret = local->ops->change_sta_links(&local->hw, &sdata->vif, sta, |
586 | old_links, new_links); |
587 | trace_drv_return_int(local, ret); |
588 | |
589 | if (ret) |
590 | return ret; |
591 | |
592 | /* during reconfig don't add it to debugfs again */ |
593 | if (local->in_reconfig || local->resuming) |
594 | return 0; |
595 | |
596 | for_each_set_bit(link_id, &links_to_add, IEEE80211_MLD_MAX_NUM_LINKS) { |
597 | link_sta = rcu_dereference_protected(info->link[link_id], |
598 | lockdep_is_held(&local->hw.wiphy->mtx)); |
599 | ieee80211_link_sta_debugfs_drv_add(link_sta); |
600 | } |
601 | |
602 | return 0; |
603 | } |
604 | |