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