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