1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright 2015 Intel Deutschland GmbH |
4 | * Copyright (C) 2022 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 | sdata->flags |= IEEE80211_SDATA_IN_DRIVER; |
79 | |
80 | if (!local->in_reconfig) { |
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 | trace_drv_remove_interface(local, sdata); |
117 | local->ops->remove_interface(&local->hw, &sdata->vif); |
118 | sdata->flags &= ~IEEE80211_SDATA_IN_DRIVER; |
119 | trace_drv_return_void(local); |
120 | } |
121 | |
122 | __must_check |
123 | int drv_sta_state(struct ieee80211_local *local, |
124 | struct ieee80211_sub_if_data *sdata, |
125 | struct sta_info *sta, |
126 | enum ieee80211_sta_state old_state, |
127 | enum ieee80211_sta_state new_state) |
128 | { |
129 | int ret = 0; |
130 | |
131 | might_sleep(); |
132 | lockdep_assert_wiphy(local->hw.wiphy); |
133 | |
134 | sdata = get_bss_sdata(sdata); |
135 | if (!check_sdata_in_driver(sdata)) |
136 | return -EIO; |
137 | |
138 | trace_drv_sta_state(local, sdata, sta: &sta->sta, old_state, new_state); |
139 | if (local->ops->sta_state) { |
140 | ret = local->ops->sta_state(&local->hw, &sdata->vif, &sta->sta, |
141 | old_state, new_state); |
142 | } else if (old_state == IEEE80211_STA_AUTH && |
143 | new_state == IEEE80211_STA_ASSOC) { |
144 | ret = drv_sta_add(local, sdata, sta: &sta->sta); |
145 | if (ret == 0) { |
146 | sta->uploaded = true; |
147 | if (rcu_access_pointer(sta->sta.rates)) |
148 | drv_sta_rate_tbl_update(local, sdata, sta: &sta->sta); |
149 | } |
150 | } else if (old_state == IEEE80211_STA_ASSOC && |
151 | new_state == IEEE80211_STA_AUTH) { |
152 | drv_sta_remove(local, sdata, sta: &sta->sta); |
153 | } |
154 | trace_drv_return_int(local, ret); |
155 | return ret; |
156 | } |
157 | |
158 | __must_check |
159 | int drv_sta_set_txpwr(struct ieee80211_local *local, |
160 | struct ieee80211_sub_if_data *sdata, |
161 | struct sta_info *sta) |
162 | { |
163 | int ret = -EOPNOTSUPP; |
164 | |
165 | might_sleep(); |
166 | lockdep_assert_wiphy(local->hw.wiphy); |
167 | |
168 | sdata = get_bss_sdata(sdata); |
169 | if (!check_sdata_in_driver(sdata)) |
170 | return -EIO; |
171 | |
172 | trace_drv_sta_set_txpwr(local, sdata, sta: &sta->sta); |
173 | if (local->ops->sta_set_txpwr) |
174 | ret = local->ops->sta_set_txpwr(&local->hw, &sdata->vif, |
175 | &sta->sta); |
176 | trace_drv_return_int(local, ret); |
177 | return ret; |
178 | } |
179 | |
180 | void drv_sta_rc_update(struct ieee80211_local *local, |
181 | struct ieee80211_sub_if_data *sdata, |
182 | struct ieee80211_sta *sta, u32 changed) |
183 | { |
184 | sdata = get_bss_sdata(sdata); |
185 | if (!check_sdata_in_driver(sdata)) |
186 | return; |
187 | |
188 | WARN_ON(changed & IEEE80211_RC_SUPP_RATES_CHANGED && |
189 | (sdata->vif.type != NL80211_IFTYPE_ADHOC && |
190 | sdata->vif.type != NL80211_IFTYPE_MESH_POINT)); |
191 | |
192 | trace_drv_sta_rc_update(local, sdata, sta, changed); |
193 | if (local->ops->sta_rc_update) |
194 | local->ops->sta_rc_update(&local->hw, &sdata->vif, |
195 | sta, changed); |
196 | |
197 | trace_drv_return_void(local); |
198 | } |
199 | |
200 | int drv_conf_tx(struct ieee80211_local *local, |
201 | struct ieee80211_link_data *link, u16 ac, |
202 | const struct ieee80211_tx_queue_params *params) |
203 | { |
204 | struct ieee80211_sub_if_data *sdata = link->sdata; |
205 | int ret = -EOPNOTSUPP; |
206 | |
207 | might_sleep(); |
208 | lockdep_assert_wiphy(local->hw.wiphy); |
209 | |
210 | if (!check_sdata_in_driver(sdata)) |
211 | return -EIO; |
212 | |
213 | if (sdata->vif.active_links && |
214 | !(sdata->vif.active_links & BIT(link->link_id))) |
215 | return 0; |
216 | |
217 | if (params->cw_min == 0 || params->cw_min > params->cw_max) { |
218 | /* |
219 | * If we can't configure hardware anyway, don't warn. We may |
220 | * never have initialized the CW parameters. |
221 | */ |
222 | WARN_ONCE(local->ops->conf_tx, |
223 | "%s: invalid CW_min/CW_max: %d/%d\n" , |
224 | sdata->name, params->cw_min, params->cw_max); |
225 | return -EINVAL; |
226 | } |
227 | |
228 | trace_drv_conf_tx(local, sdata, link_id: link->link_id, ac, params); |
229 | if (local->ops->conf_tx) |
230 | ret = local->ops->conf_tx(&local->hw, &sdata->vif, |
231 | link->link_id, ac, params); |
232 | trace_drv_return_int(local, ret); |
233 | return ret; |
234 | } |
235 | |
236 | u64 drv_get_tsf(struct ieee80211_local *local, |
237 | struct ieee80211_sub_if_data *sdata) |
238 | { |
239 | u64 ret = -1ULL; |
240 | |
241 | might_sleep(); |
242 | lockdep_assert_wiphy(local->hw.wiphy); |
243 | |
244 | if (!check_sdata_in_driver(sdata)) |
245 | return ret; |
246 | |
247 | trace_drv_get_tsf(local, sdata); |
248 | if (local->ops->get_tsf) |
249 | ret = local->ops->get_tsf(&local->hw, &sdata->vif); |
250 | trace_drv_return_u64(local, ret); |
251 | return ret; |
252 | } |
253 | |
254 | void drv_set_tsf(struct ieee80211_local *local, |
255 | struct ieee80211_sub_if_data *sdata, |
256 | u64 tsf) |
257 | { |
258 | might_sleep(); |
259 | lockdep_assert_wiphy(local->hw.wiphy); |
260 | |
261 | if (!check_sdata_in_driver(sdata)) |
262 | return; |
263 | |
264 | trace_drv_set_tsf(local, sdata, tsf); |
265 | if (local->ops->set_tsf) |
266 | local->ops->set_tsf(&local->hw, &sdata->vif, tsf); |
267 | trace_drv_return_void(local); |
268 | } |
269 | |
270 | void drv_offset_tsf(struct ieee80211_local *local, |
271 | struct ieee80211_sub_if_data *sdata, |
272 | s64 offset) |
273 | { |
274 | might_sleep(); |
275 | lockdep_assert_wiphy(local->hw.wiphy); |
276 | |
277 | if (!check_sdata_in_driver(sdata)) |
278 | return; |
279 | |
280 | trace_drv_offset_tsf(local, sdata, offset); |
281 | if (local->ops->offset_tsf) |
282 | local->ops->offset_tsf(&local->hw, &sdata->vif, offset); |
283 | trace_drv_return_void(local); |
284 | } |
285 | |
286 | void drv_reset_tsf(struct ieee80211_local *local, |
287 | struct ieee80211_sub_if_data *sdata) |
288 | { |
289 | might_sleep(); |
290 | lockdep_assert_wiphy(local->hw.wiphy); |
291 | |
292 | if (!check_sdata_in_driver(sdata)) |
293 | return; |
294 | |
295 | trace_drv_reset_tsf(local, sdata); |
296 | if (local->ops->reset_tsf) |
297 | local->ops->reset_tsf(&local->hw, &sdata->vif); |
298 | trace_drv_return_void(local); |
299 | } |
300 | |
301 | int drv_assign_vif_chanctx(struct ieee80211_local *local, |
302 | struct ieee80211_sub_if_data *sdata, |
303 | struct ieee80211_bss_conf *link_conf, |
304 | struct ieee80211_chanctx *ctx) |
305 | { |
306 | int ret = 0; |
307 | |
308 | might_sleep(); |
309 | lockdep_assert_wiphy(local->hw.wiphy); |
310 | |
311 | if (!check_sdata_in_driver(sdata)) |
312 | return -EIO; |
313 | |
314 | if (sdata->vif.active_links && |
315 | !(sdata->vif.active_links & BIT(link_conf->link_id))) |
316 | return 0; |
317 | |
318 | trace_drv_assign_vif_chanctx(local, sdata, link_conf, ctx); |
319 | if (local->ops->assign_vif_chanctx) { |
320 | WARN_ON_ONCE(!ctx->driver_present); |
321 | ret = local->ops->assign_vif_chanctx(&local->hw, |
322 | &sdata->vif, |
323 | link_conf, |
324 | &ctx->conf); |
325 | } |
326 | trace_drv_return_int(local, ret); |
327 | |
328 | return ret; |
329 | } |
330 | |
331 | void drv_unassign_vif_chanctx(struct ieee80211_local *local, |
332 | struct ieee80211_sub_if_data *sdata, |
333 | struct ieee80211_bss_conf *link_conf, |
334 | struct ieee80211_chanctx *ctx) |
335 | { |
336 | might_sleep(); |
337 | lockdep_assert_wiphy(local->hw.wiphy); |
338 | |
339 | if (!check_sdata_in_driver(sdata)) |
340 | return; |
341 | |
342 | if (sdata->vif.active_links && |
343 | !(sdata->vif.active_links & BIT(link_conf->link_id))) |
344 | return; |
345 | |
346 | trace_drv_unassign_vif_chanctx(local, sdata, link_conf, ctx); |
347 | if (local->ops->unassign_vif_chanctx) { |
348 | WARN_ON_ONCE(!ctx->driver_present); |
349 | local->ops->unassign_vif_chanctx(&local->hw, |
350 | &sdata->vif, |
351 | link_conf, |
352 | &ctx->conf); |
353 | } |
354 | trace_drv_return_void(local); |
355 | } |
356 | |
357 | int drv_switch_vif_chanctx(struct ieee80211_local *local, |
358 | struct ieee80211_vif_chanctx_switch *vifs, |
359 | int n_vifs, enum ieee80211_chanctx_switch_mode mode) |
360 | { |
361 | int ret = 0; |
362 | int i; |
363 | |
364 | might_sleep(); |
365 | lockdep_assert_wiphy(local->hw.wiphy); |
366 | |
367 | if (!local->ops->switch_vif_chanctx) |
368 | return -EOPNOTSUPP; |
369 | |
370 | for (i = 0; i < n_vifs; i++) { |
371 | struct ieee80211_chanctx *new_ctx = |
372 | container_of(vifs[i].new_ctx, |
373 | struct ieee80211_chanctx, |
374 | conf); |
375 | struct ieee80211_chanctx *old_ctx = |
376 | container_of(vifs[i].old_ctx, |
377 | struct ieee80211_chanctx, |
378 | conf); |
379 | |
380 | WARN_ON_ONCE(!old_ctx->driver_present); |
381 | WARN_ON_ONCE((mode == CHANCTX_SWMODE_SWAP_CONTEXTS && |
382 | new_ctx->driver_present) || |
383 | (mode == CHANCTX_SWMODE_REASSIGN_VIF && |
384 | !new_ctx->driver_present)); |
385 | } |
386 | |
387 | trace_drv_switch_vif_chanctx(local, vifs, n_vifs, mode); |
388 | ret = local->ops->switch_vif_chanctx(&local->hw, |
389 | vifs, n_vifs, mode); |
390 | trace_drv_return_int(local, ret); |
391 | |
392 | if (!ret && mode == CHANCTX_SWMODE_SWAP_CONTEXTS) { |
393 | for (i = 0; i < n_vifs; i++) { |
394 | struct ieee80211_chanctx *new_ctx = |
395 | container_of(vifs[i].new_ctx, |
396 | struct ieee80211_chanctx, |
397 | conf); |
398 | struct ieee80211_chanctx *old_ctx = |
399 | container_of(vifs[i].old_ctx, |
400 | struct ieee80211_chanctx, |
401 | conf); |
402 | |
403 | new_ctx->driver_present = true; |
404 | old_ctx->driver_present = false; |
405 | } |
406 | } |
407 | |
408 | return ret; |
409 | } |
410 | |
411 | int drv_ampdu_action(struct ieee80211_local *local, |
412 | struct ieee80211_sub_if_data *sdata, |
413 | struct ieee80211_ampdu_params *params) |
414 | { |
415 | int ret = -EOPNOTSUPP; |
416 | |
417 | might_sleep(); |
418 | lockdep_assert_wiphy(local->hw.wiphy); |
419 | |
420 | sdata = get_bss_sdata(sdata); |
421 | if (!check_sdata_in_driver(sdata)) |
422 | return -EIO; |
423 | |
424 | trace_drv_ampdu_action(local, sdata, params); |
425 | |
426 | if (local->ops->ampdu_action) |
427 | ret = local->ops->ampdu_action(&local->hw, &sdata->vif, params); |
428 | |
429 | trace_drv_return_int(local, ret); |
430 | |
431 | return ret; |
432 | } |
433 | |
434 | void drv_link_info_changed(struct ieee80211_local *local, |
435 | struct ieee80211_sub_if_data *sdata, |
436 | struct ieee80211_bss_conf *info, |
437 | int link_id, u64 changed) |
438 | { |
439 | might_sleep(); |
440 | lockdep_assert_wiphy(local->hw.wiphy); |
441 | |
442 | if (WARN_ON_ONCE(changed & (BSS_CHANGED_BEACON | |
443 | BSS_CHANGED_BEACON_ENABLED) && |
444 | sdata->vif.type != NL80211_IFTYPE_AP && |
445 | sdata->vif.type != NL80211_IFTYPE_ADHOC && |
446 | sdata->vif.type != NL80211_IFTYPE_MESH_POINT && |
447 | sdata->vif.type != NL80211_IFTYPE_OCB)) |
448 | return; |
449 | |
450 | if (WARN_ON_ONCE(sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE || |
451 | sdata->vif.type == NL80211_IFTYPE_NAN || |
452 | (sdata->vif.type == NL80211_IFTYPE_MONITOR && |
453 | !sdata->vif.bss_conf.mu_mimo_owner && |
454 | !(changed & BSS_CHANGED_TXPOWER)))) |
455 | return; |
456 | |
457 | if (!check_sdata_in_driver(sdata)) |
458 | return; |
459 | |
460 | if (sdata->vif.active_links && |
461 | !(sdata->vif.active_links & BIT(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) { |
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 | for_each_set_bit(link_id, &links_to_add, IEEE80211_MLD_MAX_NUM_LINKS) { |
593 | link_sta = rcu_dereference_protected(info->link[link_id], |
594 | lockdep_is_held(&local->hw.wiphy->mtx)); |
595 | ieee80211_link_sta_debugfs_drv_add(link_sta); |
596 | } |
597 | |
598 | return 0; |
599 | } |
600 | |