1 /*-
2 * Copyright (c) 2021-2026 The FreeBSD Foundation
3 *
4 * This software was developed by Björn Zeeb under sponsorship from
5 * the FreeBSD Foundation.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #include <sys/param.h>
30 #include <sys/types.h>
31 #include <sys/kernel.h>
32 #include <sys/errno.h>
33
34 #define LINUXKPI_NET80211
35 #include <net/mac80211.h>
36
37 #include "linux_80211.h"
38
39 /* Could be a different tracing framework later. */
40 #ifdef LINUXKPI_DEBUG_80211
41 #define LKPI_80211_TRACE_MO(fmt, ...) \
42 if (linuxkpi_debug_80211 & D80211_TRACE_MO) \
43 printf("LKPI_80211_TRACE_MO %s:%d: %d %d %lu: " fmt "\n", \
44 __func__, __LINE__, curcpu, curthread->td_tid, \
45 jiffies, ##__VA_ARGS__)
46 #else
47 #define LKPI_80211_TRACE_MO(...) do { } while(0)
48 #endif
49
50 int
lkpi_80211_mo_start(struct ieee80211_hw * hw)51 lkpi_80211_mo_start(struct ieee80211_hw *hw)
52 {
53 struct lkpi_hw *lhw;
54 int error;
55
56 lockdep_assert_wiphy(hw->wiphy);
57
58 lhw = HW_TO_LHW(hw);
59 if (lhw->ops->start == NULL) {
60 error = EOPNOTSUPP;
61 goto out;
62 }
63
64 if ((lhw->sc_flags & LKPI_MAC80211_DRV_STARTED)) {
65 /* Trying to start twice is an error. */
66 error = EEXIST;
67 goto out;
68 }
69 LKPI_80211_TRACE_MO("hw %p", hw);
70 error = lhw->ops->start(hw);
71 if (error == 0)
72 lhw->sc_flags |= LKPI_MAC80211_DRV_STARTED;
73
74 out:
75 return (error);
76 }
77
78 void
lkpi_80211_mo_stop(struct ieee80211_hw * hw,bool suspend)79 lkpi_80211_mo_stop(struct ieee80211_hw *hw, bool suspend)
80 {
81 struct lkpi_hw *lhw;
82
83 might_sleep();
84 lockdep_assert_wiphy(hw->wiphy);
85
86 lhw = HW_TO_LHW(hw);
87 if (lhw->ops->stop == NULL)
88 return;
89
90 LKPI_80211_TRACE_MO("hw %p suspend %d", hw, suspend);
91 lhw->ops->stop(hw, suspend);
92 lhw->sc_flags &= ~LKPI_MAC80211_DRV_STARTED;
93 }
94
95 int
lkpi_80211_mo_get_antenna(struct ieee80211_hw * hw,u32 * txs,u32 * rxs)96 lkpi_80211_mo_get_antenna(struct ieee80211_hw *hw, u32 *txs, u32 *rxs)
97 {
98 struct lkpi_hw *lhw;
99 int error;
100
101 lhw = HW_TO_LHW(hw);
102 if (lhw->ops->get_antenna == NULL) {
103 error = EOPNOTSUPP;
104 goto out;
105 }
106
107 LKPI_80211_TRACE_MO("hw %p", hw);
108 LKPI_80211_TRACE_MO("TODO link/radio_idx");
109 error = lhw->ops->get_antenna(hw, 0, txs, rxs);
110
111 out:
112 return (error);
113 }
114
115 int
lkpi_80211_mo_set_frag_threshold(struct ieee80211_hw * hw,uint32_t frag_th)116 lkpi_80211_mo_set_frag_threshold(struct ieee80211_hw *hw, uint32_t frag_th)
117 {
118 struct lkpi_hw *lhw;
119 int error;
120
121 might_sleep();
122 lockdep_assert_wiphy(hw->wiphy);
123
124 lhw = HW_TO_LHW(hw);
125 if (lhw->ops->set_frag_threshold == NULL) {
126 error = EOPNOTSUPP;
127 goto out;
128 }
129
130 LKPI_80211_TRACE_MO("hw %p frag_th %u", hw, frag_th);
131 LKPI_80211_TRACE_MO("TODO link/radio_idx");
132 error = lhw->ops->set_frag_threshold(hw, 0, frag_th);
133
134 out:
135 return (error);
136 }
137
138 int
lkpi_80211_mo_set_rts_threshold(struct ieee80211_hw * hw,uint32_t rts_th)139 lkpi_80211_mo_set_rts_threshold(struct ieee80211_hw *hw, uint32_t rts_th)
140 {
141 struct lkpi_hw *lhw;
142 int error;
143
144 might_sleep();
145 lockdep_assert_wiphy(hw->wiphy);
146
147 lhw = HW_TO_LHW(hw);
148 if (lhw->ops->set_rts_threshold == NULL) {
149 error = EOPNOTSUPP;
150 goto out;
151 }
152
153 LKPI_80211_TRACE_MO("hw %p rts_th %u", hw, rts_th);
154 LKPI_80211_TRACE_MO("TODO link/radio_idx");
155 error = lhw->ops->set_rts_threshold(hw, 0, rts_th);
156
157 out:
158 return (error);
159 }
160
161
162 int
lkpi_80211_mo_add_interface(struct ieee80211_hw * hw,struct ieee80211_vif * vif)163 lkpi_80211_mo_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
164 {
165 struct lkpi_hw *lhw;
166 struct lkpi_vif *lvif;
167 int error;
168
169 lhw = HW_TO_LHW(hw);
170 if (lhw->ops->add_interface == NULL) {
171 error = EOPNOTSUPP;
172 goto out;
173 }
174
175 lvif = VIF_TO_LVIF(vif);
176 LKPI_80211_LVIF_LOCK(lvif);
177 if (lvif->added_to_drv) {
178 LKPI_80211_LVIF_UNLOCK(lvif);
179 /* Trying to add twice is an error. */
180 error = EEXIST;
181 goto out;
182 }
183 LKPI_80211_LVIF_UNLOCK(lvif);
184
185 LKPI_80211_TRACE_MO("hw %p vif %p", hw, vif);
186 error = lhw->ops->add_interface(hw, vif);
187 if (error == 0) {
188 LKPI_80211_LVIF_LOCK(lvif);
189 lvif->added_to_drv = true;
190 LKPI_80211_LVIF_UNLOCK(lvif);
191 }
192
193 out:
194 return (error);
195 }
196
197 void
lkpi_80211_mo_remove_interface(struct ieee80211_hw * hw,struct ieee80211_vif * vif)198 lkpi_80211_mo_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
199 {
200 struct lkpi_hw *lhw;
201 struct lkpi_vif *lvif;
202
203 might_sleep();
204 lockdep_assert_wiphy(hw->wiphy);
205
206 lhw = HW_TO_LHW(hw);
207 if (lhw->ops->remove_interface == NULL)
208 return;
209
210 lvif = VIF_TO_LVIF(vif);
211 LKPI_80211_LVIF_LOCK(lvif);
212 if (!lvif->added_to_drv) {
213 LKPI_80211_LVIF_UNLOCK(lvif);
214 return;
215 }
216 LKPI_80211_LVIF_UNLOCK(lvif);
217
218 LKPI_80211_TRACE_MO("hw %p vif %p", hw, vif);
219 lhw->ops->remove_interface(hw, vif);
220 LKPI_80211_LVIF_LOCK(lvif);
221 lvif->added_to_drv = false;
222 LKPI_80211_LVIF_UNLOCK(lvif);
223 }
224
225
226 int
lkpi_80211_mo_hw_scan(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_scan_request * sr)227 lkpi_80211_mo_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
228 struct ieee80211_scan_request *sr)
229 {
230 struct lkpi_hw *lhw;
231 int error;
232
233 lockdep_assert_wiphy(hw->wiphy);
234
235 /*
236 * MUST NOT return EPERM as that is a "magic number 1" based on rtw88
237 * driver indicating hw_scan is not supported despite the ops call
238 * being available.
239 */
240
241 lhw = HW_TO_LHW(hw);
242 if (lhw->ops->hw_scan == NULL) {
243 /* Return magic number to use sw scan. */
244 error = 1;
245 goto out;
246 }
247
248 LKPI_80211_TRACE_MO("CALLING hw %p vif %p sr %p", hw, vif, sr);
249 error = lhw->ops->hw_scan(hw, vif, sr);
250 LKPI_80211_TRACE_MO("RETURNING hw %p vif %p sr %p error %d", hw, vif, sr, error);
251
252 out:
253 return (error);
254 }
255
256 void
lkpi_80211_mo_cancel_hw_scan(struct ieee80211_hw * hw,struct ieee80211_vif * vif)257 lkpi_80211_mo_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
258 {
259 struct lkpi_hw *lhw;
260
261 lockdep_assert_wiphy(hw->wiphy);
262
263 lhw = HW_TO_LHW(hw);
264 if (lhw->ops->cancel_hw_scan == NULL)
265 return;
266
267 LKPI_80211_TRACE_MO("hw %p vif %p", hw, vif);
268 lhw->ops->cancel_hw_scan(hw, vif);
269 }
270
271 void
lkpi_80211_mo_sw_scan_complete(struct ieee80211_hw * hw,struct ieee80211_vif * vif)272 lkpi_80211_mo_sw_scan_complete(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
273 {
274 struct lkpi_hw *lhw;
275
276 lhw = HW_TO_LHW(hw);
277 if (lhw->ops->sw_scan_complete == NULL)
278 return;
279
280 LKPI_80211_TRACE_MO("hw %p vif %p", hw, vif);
281 lhw->ops->sw_scan_complete(hw, vif);
282 lhw->scan_flags &= ~LKPI_LHW_SCAN_RUNNING;
283 }
284
285 void
lkpi_80211_mo_sw_scan_start(struct ieee80211_hw * hw,struct ieee80211_vif * vif,const u8 * addr)286 lkpi_80211_mo_sw_scan_start(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
287 const u8 *addr)
288 {
289 struct lkpi_hw *lhw;
290
291 lhw = HW_TO_LHW(hw);
292 if (lhw->ops->sw_scan_start == NULL)
293 return;
294
295 LKPI_80211_TRACE_MO("hw %p vif %p", hw, vif);
296 lhw->ops->sw_scan_start(hw, vif, addr);
297 }
298
299
300 /*
301 * We keep the Linux type here; it really is an uintptr_t.
302 */
303 u64
lkpi_80211_mo_prepare_multicast(struct ieee80211_hw * hw,struct netdev_hw_addr_list * mc_list)304 lkpi_80211_mo_prepare_multicast(struct ieee80211_hw *hw,
305 struct netdev_hw_addr_list *mc_list)
306 {
307 struct lkpi_hw *lhw;
308 u64 ptr;
309
310 /* This seems fine without the wiphy lock. */
311
312 lhw = HW_TO_LHW(hw);
313 if (lhw->ops->prepare_multicast == NULL)
314 return (0);
315
316 LKPI_80211_TRACE_MO("hw %p mc_list %p", hw, mc_list);
317 ptr = lhw->ops->prepare_multicast(hw, mc_list);
318 return (ptr);
319 }
320
321 void
lkpi_80211_mo_configure_filter(struct ieee80211_hw * hw,unsigned int changed_flags,unsigned int * total_flags,u64 mc_ptr)322 lkpi_80211_mo_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags,
323 unsigned int *total_flags, u64 mc_ptr)
324 {
325 struct lkpi_hw *lhw;
326
327 lockdep_assert_wiphy(hw->wiphy);
328
329 lhw = HW_TO_LHW(hw);
330 if (lhw->ops->configure_filter == NULL)
331 return;
332
333 LKPI_80211_TRACE_MO("hw %p changed_flags %#x total_flags %p mc_ptr %ju", hw, changed_flags, total_flags, (uintmax_t)mc_ptr);
334 lhw->ops->configure_filter(hw, changed_flags, total_flags, mc_ptr);
335 }
336
337
338 /*
339 * So far we only called sta_{add,remove} as an alternative to sta_state.
340 * Let's keep the implementation simpler and hide sta_{add,remove} under the
341 * hood here calling them if state_state is not available from mo_sta_state.
342 */
343 static int
lkpi_80211_mo_sta_add(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_sta * sta)344 lkpi_80211_mo_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
345 struct ieee80211_sta *sta)
346 {
347 struct lkpi_hw *lhw;
348 struct lkpi_sta *lsta;
349 int error;
350
351 lhw = HW_TO_LHW(hw);
352 if (lhw->ops->sta_add == NULL) {
353 error = EOPNOTSUPP;
354 goto out;
355 }
356
357 lsta = STA_TO_LSTA(sta);
358 if (lsta->added_to_drv) {
359 error = EEXIST;
360 goto out;
361 }
362
363 LKPI_80211_TRACE_MO("hw %p vif %p sta %p", hw, vif, sta);
364 error = lhw->ops->sta_add(hw, vif, sta);
365 if (error == 0)
366 lsta->added_to_drv = true;
367
368 out:
369 return error;
370 }
371
372 static int
lkpi_80211_mo_sta_remove(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_sta * sta)373 lkpi_80211_mo_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
374 struct ieee80211_sta *sta)
375 {
376 struct lkpi_hw *lhw;
377 struct lkpi_sta *lsta;
378 int error;
379
380 lhw = HW_TO_LHW(hw);
381 if (lhw->ops->sta_remove == NULL) {
382 error = EOPNOTSUPP;
383 goto out;
384 }
385
386 lsta = STA_TO_LSTA(sta);
387 if (!lsta->added_to_drv) {
388 /* If we never added the sta, do not complain on cleanup. */
389 error = 0;
390 goto out;
391 }
392
393 LKPI_80211_TRACE_MO("hw %p vif %p sta %p", hw, vif, sta);
394 error = lhw->ops->sta_remove(hw, vif, sta);
395 if (error == 0)
396 lsta->added_to_drv = false;
397
398 out:
399 return error;
400 }
401
402 int
lkpi_80211_mo_sta_state(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct lkpi_sta * lsta,enum ieee80211_sta_state nstate)403 lkpi_80211_mo_sta_state(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
404 struct lkpi_sta *lsta, enum ieee80211_sta_state nstate)
405 {
406 struct lkpi_hw *lhw;
407 struct ieee80211_sta *sta;
408 int error;
409
410 lhw = HW_TO_LHW(hw);
411 sta = LSTA_TO_STA(lsta);
412 if (lhw->ops->sta_state != NULL) {
413 LKPI_80211_TRACE_MO("hw %p vif %p sta %p nstate %d", hw, vif, sta, nstate);
414 error = lhw->ops->sta_state(hw, vif, sta, lsta->state, nstate);
415 if (error == 0) {
416 if (nstate == IEEE80211_STA_NOTEXIST)
417 lsta->added_to_drv = false;
418 else
419 lsta->added_to_drv = true;
420 lsta->state = nstate;
421 }
422 goto out;
423 }
424
425 /* XXX-BZ is the change state AUTH or ASSOC here? */
426 if (lsta->state < IEEE80211_STA_ASSOC && nstate == IEEE80211_STA_ASSOC) {
427 error = lkpi_80211_mo_sta_add(hw, vif, sta);
428 if (error == 0)
429 lsta->added_to_drv = true;
430 } else if (lsta->state >= IEEE80211_STA_ASSOC &&
431 nstate < IEEE80211_STA_ASSOC) {
432 error = lkpi_80211_mo_sta_remove(hw, vif, sta);
433 if (error == 0)
434 lsta->added_to_drv = false;
435 } else
436 /* Nothing to do. */
437 error = 0;
438 if (error == 0)
439 lsta->state = nstate;
440
441 out:
442 /* XXX-BZ should we manage state in here? */
443 return (error);
444 }
445
446 int
lkpi_80211_mo_config(struct ieee80211_hw * hw,uint32_t changed)447 lkpi_80211_mo_config(struct ieee80211_hw *hw, uint32_t changed)
448 {
449 struct lkpi_hw *lhw;
450 int error;
451
452 lockdep_assert_wiphy(hw->wiphy);
453
454 lhw = HW_TO_LHW(hw);
455 if (lhw->ops->config == NULL) {
456 error = EOPNOTSUPP;
457 goto out;
458 }
459
460 LKPI_80211_TRACE_MO("hw %p changed %u", hw, changed);
461 LKPI_80211_TRACE_MO("TODO link/radio_idx");
462 error = lhw->ops->config(hw, 0, changed);
463
464 out:
465 return (error);
466 }
467
468
469 int
lkpi_80211_mo_assign_vif_chanctx(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_bss_conf * conf,struct ieee80211_chanctx_conf * chanctx_conf)470 lkpi_80211_mo_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
471 struct ieee80211_bss_conf *conf, struct ieee80211_chanctx_conf *chanctx_conf)
472 {
473 struct lkpi_hw *lhw;
474 int error;
475
476 lhw = HW_TO_LHW(hw);
477 if (lhw->ops->assign_vif_chanctx == NULL) {
478 error = EOPNOTSUPP;
479 goto out;
480 }
481
482 LKPI_80211_TRACE_MO("hw %p vif %p bss_conf %p chanctx_conf %p",
483 hw, vif, conf, chanctx_conf);
484 error = lhw->ops->assign_vif_chanctx(hw, vif, conf, chanctx_conf);
485 if (error == 0)
486 vif->bss_conf.chanctx_conf = chanctx_conf;
487
488 out:
489 return (error);
490 }
491
492 void
lkpi_80211_mo_unassign_vif_chanctx(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_bss_conf * conf,struct ieee80211_chanctx_conf * chanctx_conf)493 lkpi_80211_mo_unassign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
494 struct ieee80211_bss_conf *conf, struct ieee80211_chanctx_conf *chanctx_conf)
495 {
496 struct lkpi_hw *lhw;
497
498 might_sleep();
499 lockdep_assert_wiphy(hw->wiphy);
500
501 lhw = HW_TO_LHW(hw);
502 if (lhw->ops->unassign_vif_chanctx == NULL)
503 return;
504
505 if (chanctx_conf == NULL)
506 return;
507
508 LKPI_80211_TRACE_MO("hw %p vif %p bss_conf %p chanctx_conf %p",
509 hw, vif, conf, chanctx_conf);
510 lhw->ops->unassign_vif_chanctx(hw, vif, conf, chanctx_conf);
511 }
512
513
514 int
lkpi_80211_mo_add_chanctx(struct ieee80211_hw * hw,struct ieee80211_chanctx_conf * chanctx_conf)515 lkpi_80211_mo_add_chanctx(struct ieee80211_hw *hw,
516 struct ieee80211_chanctx_conf *chanctx_conf)
517 {
518 struct lkpi_hw *lhw;
519 struct lkpi_chanctx *lchanctx;
520 int error;
521
522 might_sleep();
523 lockdep_assert_wiphy(hw->wiphy);
524
525 lhw = HW_TO_LHW(hw);
526 if (lhw->ops->add_chanctx == NULL) {
527 error = EOPNOTSUPP;
528 goto out;
529 }
530
531 LKPI_80211_TRACE_MO("hw %p chanctx_conf %p", hw, chanctx_conf);
532 error = lhw->ops->add_chanctx(hw, chanctx_conf);
533 if (error == 0) {
534 lchanctx = CHANCTX_CONF_TO_LCHANCTX(chanctx_conf);
535 lchanctx->added_to_drv = true;
536 }
537
538 out:
539 return (error);
540 }
541
542 void
lkpi_80211_mo_change_chanctx(struct ieee80211_hw * hw,struct ieee80211_chanctx_conf * chanctx_conf,uint32_t changed)543 lkpi_80211_mo_change_chanctx(struct ieee80211_hw *hw,
544 struct ieee80211_chanctx_conf *chanctx_conf, uint32_t changed)
545 {
546 struct lkpi_hw *lhw;
547
548 might_sleep();
549 lockdep_assert_wiphy(hw->wiphy);
550
551 lhw = HW_TO_LHW(hw);
552 if (lhw->ops->change_chanctx == NULL)
553 return;
554
555 LKPI_80211_TRACE_MO("hw %p chanctx_conf %p changed %u", hw, chanctx_conf, changed);
556 lhw->ops->change_chanctx(hw, chanctx_conf, changed);
557 }
558
559 void
lkpi_80211_mo_remove_chanctx(struct ieee80211_hw * hw,struct ieee80211_chanctx_conf * chanctx_conf)560 lkpi_80211_mo_remove_chanctx(struct ieee80211_hw *hw,
561 struct ieee80211_chanctx_conf *chanctx_conf)
562 {
563 struct lkpi_hw *lhw;
564 struct lkpi_chanctx *lchanctx;
565
566 might_sleep();
567 lockdep_assert_wiphy(hw->wiphy);
568
569 lhw = HW_TO_LHW(hw);
570 if (lhw->ops->remove_chanctx == NULL)
571 return;
572
573 LKPI_80211_TRACE_MO("hw %p chanctx_conf %p", hw, chanctx_conf);
574 lhw->ops->remove_chanctx(hw, chanctx_conf);
575 lchanctx = CHANCTX_CONF_TO_LCHANCTX(chanctx_conf);
576 lchanctx->added_to_drv = false;
577 }
578
579 void
lkpi_80211_mo_vif_cfg_changed(struct ieee80211_hw * hw,struct ieee80211_vif * vif,uint64_t vif_cfg_bits,bool fallback)580 lkpi_80211_mo_vif_cfg_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
581 uint64_t vif_cfg_bits, bool fallback)
582 {
583 struct lkpi_hw *lhw;
584
585 might_sleep();
586 /* XXX-FINISH all callers for lockdep_assert_wiphy(hw->wiphy); */
587
588 lhw = HW_TO_LHW(hw);
589 if (lhw->ops->vif_cfg_changed == NULL &&
590 lhw->ops->bss_info_changed == NULL)
591 return;
592
593 if (vif_cfg_bits == 0)
594 return;
595
596 LKPI_80211_TRACE_MO("hw %p vif %p vif_cfg_bits %#jx", hw, vif, (uintmax_t)vif_cfg_bits);
597 if (lhw->ops->link_info_changed != NULL)
598 lhw->ops->vif_cfg_changed(hw, vif, vif_cfg_bits);
599 else if (fallback)
600 lhw->ops->bss_info_changed(hw, vif, &vif->bss_conf, vif_cfg_bits);
601 }
602
603 void
lkpi_80211_mo_link_info_changed(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_bss_conf * conf,uint64_t link_info_bits,uint8_t link_id,bool fallback)604 lkpi_80211_mo_link_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
605 struct ieee80211_bss_conf *conf, uint64_t link_info_bits, uint8_t link_id,
606 bool fallback)
607 {
608 struct lkpi_hw *lhw;
609
610 might_sleep();
611 lockdep_assert_wiphy(hw->wiphy);
612
613 lhw = HW_TO_LHW(hw);
614 if (lhw->ops->link_info_changed == NULL &&
615 lhw->ops->bss_info_changed == NULL)
616 return;
617
618 if (link_info_bits == 0)
619 return;
620
621 if (!ieee80211_vif_link_active(vif, link_id))
622 return;
623
624 LKPI_80211_TRACE_MO("hw %p vif %p conf %p link_info_bits %#jx", hw, vif, conf, (uintmax_t)link_info_bits);
625 if (lhw->ops->link_info_changed != NULL)
626 lhw->ops->link_info_changed(hw, vif, conf, link_info_bits);
627 else if (fallback)
628 lhw->ops->bss_info_changed(hw, vif, conf, link_info_bits);
629 }
630
631 /*
632 * This is basically obsolete but one caller.
633 * The functionality is now split between lkpi_80211_mo_link_info_changed() and
634 * lkpi_80211_mo_vif_cfg_changed(). Those functions have a flag whether to call
635 * the (*bss_info_changed) fallback or not. See lkpi_bss_info_change().
636 */
637 void
lkpi_80211_mo_bss_info_changed(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_bss_conf * conf,uint64_t bss_changed)638 lkpi_80211_mo_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
639 struct ieee80211_bss_conf *conf, uint64_t bss_changed)
640 {
641 struct lkpi_hw *lhw;
642
643 lockdep_assert_wiphy(hw->wiphy);
644
645 lhw = HW_TO_LHW(hw);
646 if (lhw->ops->bss_info_changed == NULL)
647 return;
648
649 if (bss_changed == 0)
650 return;
651
652 LKPI_80211_TRACE_MO("hw %p vif %p conf %p changed %#jx", hw, vif, conf, (uintmax_t)bss_changed);
653 lhw->ops->bss_info_changed(hw, vif, conf, bss_changed);
654 }
655
656 int
lkpi_80211_mo_conf_tx(struct ieee80211_hw * hw,struct ieee80211_vif * vif,uint32_t link_id,uint16_t ac,const struct ieee80211_tx_queue_params * txqp)657 lkpi_80211_mo_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
658 uint32_t link_id, uint16_t ac, const struct ieee80211_tx_queue_params *txqp)
659 {
660 struct lkpi_hw *lhw;
661 int error;
662
663 lhw = HW_TO_LHW(hw);
664 if (lhw->ops->conf_tx == NULL) {
665 error = EOPNOTSUPP;
666 goto out;
667 }
668
669 LKPI_80211_TRACE_MO("hw %p vif %p link_id %u ac %u txpq %p",
670 hw, vif, link_id, ac, txqp);
671 error = lhw->ops->conf_tx(hw, vif, link_id, ac, txqp);
672
673 out:
674 return (error);
675 }
676
677 void
lkpi_80211_mo_flush(struct ieee80211_hw * hw,struct ieee80211_vif * vif,uint32_t nqueues,bool drop)678 lkpi_80211_mo_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
679 uint32_t nqueues, bool drop)
680 {
681 struct lkpi_hw *lhw;
682
683 lhw = HW_TO_LHW(hw);
684 if (lhw->ops->flush == NULL)
685 return;
686
687 LKPI_80211_TRACE_MO("hw %p vif %p nqueues %u drop %d", hw, vif, nqueues, drop);
688 lhw->ops->flush(hw, vif, nqueues, drop);
689 }
690
691 void
lkpi_80211_mo_mgd_prepare_tx(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_prep_tx_info * txinfo)692 lkpi_80211_mo_mgd_prepare_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
693 struct ieee80211_prep_tx_info *txinfo)
694 {
695 struct lkpi_hw *lhw;
696
697 lhw = HW_TO_LHW(hw);
698 if (lhw->ops->mgd_prepare_tx == NULL)
699 return;
700
701 LKPI_80211_TRACE_MO("hw %p vif %p txinfo %p", hw, vif, txinfo);
702 lhw->ops->mgd_prepare_tx(hw, vif, txinfo);
703 }
704
705 void
lkpi_80211_mo_mgd_complete_tx(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_prep_tx_info * txinfo)706 lkpi_80211_mo_mgd_complete_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
707 struct ieee80211_prep_tx_info *txinfo)
708 {
709 struct lkpi_hw *lhw;
710
711 lhw = HW_TO_LHW(hw);
712 if (lhw->ops->mgd_complete_tx == NULL)
713 return;
714
715 LKPI_80211_TRACE_MO("hw %p vif %p txinfo %p", hw, vif, txinfo);
716 lhw->ops->mgd_complete_tx(hw, vif, txinfo);
717 }
718
719 void
lkpi_80211_mo_tx(struct ieee80211_hw * hw,struct ieee80211_tx_control * txctrl,struct sk_buff * skb)720 lkpi_80211_mo_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *txctrl,
721 struct sk_buff *skb)
722 {
723 struct lkpi_hw *lhw;
724
725 lhw = HW_TO_LHW(hw);
726 if (lhw->ops->tx == NULL)
727 return;
728
729 LKPI_80211_TRACE_MO("hw %p txctrl %p skb %p", hw, txctrl, skb);
730 lhw->ops->tx(hw, txctrl, skb);
731 }
732
733 void
lkpi_80211_mo_wake_tx_queue(struct ieee80211_hw * hw,struct ieee80211_txq * txq,bool schedule)734 lkpi_80211_mo_wake_tx_queue(struct ieee80211_hw *hw, struct ieee80211_txq *txq,
735 bool schedule)
736 {
737 struct lkpi_hw *lhw;
738
739 lhw = HW_TO_LHW(hw);
740
741 /* Do the schedule before the check for wake_tx_queue supported! */
742 if (schedule)
743 ieee80211_schedule_txq(hw, txq);
744
745 if (lhw->ops->wake_tx_queue == NULL)
746 return;
747
748 LKPI_80211_TRACE_MO("hw %p txq %p", hw, txq);
749 lhw->ops->wake_tx_queue(hw, txq);
750 }
751
752 void
lkpi_80211_mo_sync_rx_queues(struct ieee80211_hw * hw)753 lkpi_80211_mo_sync_rx_queues(struct ieee80211_hw *hw)
754 {
755 struct lkpi_hw *lhw;
756
757 lhw = HW_TO_LHW(hw);
758 if (lhw->ops->sync_rx_queues == NULL)
759 return;
760
761 LKPI_80211_TRACE_MO("hw %p", hw);
762 lhw->ops->sync_rx_queues(hw);
763 }
764
765 void
lkpi_80211_mo_sta_pre_rcu_remove(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_sta * sta)766 lkpi_80211_mo_sta_pre_rcu_remove(struct ieee80211_hw *hw,
767 struct ieee80211_vif *vif, struct ieee80211_sta *sta)
768 {
769 struct lkpi_hw *lhw;
770
771 lhw = HW_TO_LHW(hw);
772 if (lhw->ops->sta_pre_rcu_remove == NULL)
773 return;
774
775 LKPI_80211_TRACE_MO("hw %p vif %p sta %p", hw, vif, sta);
776 lhw->ops->sta_pre_rcu_remove(hw, vif, sta);
777 }
778
779 void
lkpi_80211_mo_link_sta_rc_update(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_link_sta * link_sta,enum ieee80211_rate_control_changed_flags rc_changed)780 lkpi_80211_mo_link_sta_rc_update(struct ieee80211_hw *hw,
781 struct ieee80211_vif *vif, struct ieee80211_link_sta *link_sta,
782 enum ieee80211_rate_control_changed_flags rc_changed)
783 {
784 struct lkpi_hw *lhw;
785
786 lhw = HW_TO_LHW(hw);
787 if (lhw->ops->link_sta_rc_update == NULL)
788 return;
789
790 LKPI_80211_TRACE_MO("hw %p vif %p link_sta %p rc_changed %#010x",
791 hw, vif, link_sta, rc_changed);
792 lhw->ops->link_sta_rc_update(hw, vif, link_sta, rc_changed);
793 }
794
795 int
lkpi_80211_mo_set_bitrate_mask(struct ieee80211_hw * hw,struct ieee80211_vif * vif,const struct cfg80211_bitrate_mask * br_mask)796 lkpi_80211_mo_set_bitrate_mask(struct ieee80211_hw *hw,
797 struct ieee80211_vif *vif, const struct cfg80211_bitrate_mask *br_mask)
798 {
799 struct lkpi_hw *lhw;
800 int error;
801
802 might_sleep();
803 lockdep_assert_wiphy(hw->wiphy);
804
805 lhw = HW_TO_LHW(hw);
806 if (lhw->ops->set_bitrate_mask == NULL) {
807 error = EOPNOTSUPP;
808 goto out;
809 }
810
811 LKPI_80211_TRACE_MO("hw %p vif %p br_mask %p",
812 hw, vif, br_mask);
813 error = lhw->ops->set_bitrate_mask(hw, vif, br_mask);
814
815 out:
816 return (error);
817 }
818
819 int
lkpi_80211_mo_set_key(struct ieee80211_hw * hw,enum set_key_cmd cmd,struct ieee80211_vif * vif,struct ieee80211_sta * sta,struct ieee80211_key_conf * kc)820 lkpi_80211_mo_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
821 struct ieee80211_vif *vif, struct ieee80211_sta *sta,
822 struct ieee80211_key_conf *kc)
823 {
824 struct lkpi_hw *lhw;
825 int error;
826
827 lockdep_assert_wiphy(hw->wiphy);
828
829 lhw = HW_TO_LHW(hw);
830 if (lhw->ops->set_key == NULL) {
831 error = EOPNOTSUPP;
832 goto out;
833 }
834
835 /*
836 * Drivers will apply different logic depending on sta being set
837 * here or not and that depends on whether we have an address or
838 * not. wpa_spplucoant::driver_bsd::bsd_set_key() will set a
839 * broadcast address if we do not have one; further up in
840 * wpa_supplicant something presumably sets the broadcast address
841 * for group keys as well.
842 * We have to "undo" this here and set sta to NULL to avoid
843 * problems with hw_crypto in various drivers.
844 * We do this here so all set_key calls for (SET_KEY and DISABLE_KEY)
845 * are covered.
846 */
847 MPASS(kc->_k != NULL);
848 if (is_broadcast_ether_addr(kc->_k->wk_macaddr))
849 sta = NULL;
850
851 LKPI_80211_TRACE_MO("hw %p cmd %d vif %p sta %p kc %p", hw, cmd, vif, sta, kc);
852 error = lhw->ops->set_key(hw, cmd, vif, sta, kc);
853
854 out:
855 return (error);
856 }
857
858 void
lkpi_80211_mo_sta_set_decap_offload(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_sta * sta,bool enable)859 lkpi_80211_mo_sta_set_decap_offload(struct ieee80211_hw *hw,
860 struct ieee80211_vif *vif, struct ieee80211_sta *sta,
861 bool enable)
862 {
863 struct lkpi_hw *lhw;
864
865 lockdep_assert_wiphy(hw->wiphy);
866
867 lhw = HW_TO_LHW(hw);
868 if (lhw->ops->sta_set_decap_offload == NULL)
869 return;
870
871 LKPI_80211_TRACE_MO("hw %p vif %p sta %p enable %d", hw, vif, sta, enable);
872 lhw->ops->sta_set_decap_offload(hw, vif, sta, enable);
873 }
874
875 int
lkpi_80211_mo_ampdu_action(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_ampdu_params * params)876 lkpi_80211_mo_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
877 struct ieee80211_ampdu_params *params)
878 {
879 struct lkpi_hw *lhw;
880 int error;
881
882 lhw = HW_TO_LHW(hw);
883 if (lhw->ops->ampdu_action == NULL) {
884 error = EOPNOTSUPP;
885 goto out;
886 }
887
888 LKPI_80211_TRACE_MO("hw %p vif %p params %p { %p, %d, %u, %u, %u, %u, %d }",
889 hw, vif, params, params->sta, params->action, params->buf_size,
890 params->timeout, params->ssn, params->tid, params->amsdu);
891 error = lhw->ops->ampdu_action(hw, vif, params);
892
893 out:
894 return (error);
895 }
896
897 int
lkpi_80211_mo_sta_statistics(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_sta * sta,struct station_info * sinfo)898 lkpi_80211_mo_sta_statistics(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
899 struct ieee80211_sta *sta, struct station_info *sinfo)
900 {
901 struct lkpi_hw *lhw;
902 struct lkpi_sta *lsta;
903 int error;
904
905 lhw = HW_TO_LHW(hw);
906 if (lhw->ops->sta_statistics == NULL) {
907 error = EOPNOTSUPP;
908 goto out;
909 }
910
911 lsta = STA_TO_LSTA(sta);
912 if (!lsta->added_to_drv) {
913 error = EEXIST;
914 goto out;
915 }
916
917 lockdep_assert_wiphy(hw->wiphy);
918
919 LKPI_80211_TRACE_MO("hw %p vif %p sta %p sinfo %p", hw, vif, sta, sinfo);
920 lhw->ops->sta_statistics(hw, vif, sta, sinfo);
921 error = 0;
922
923 out:
924 return (error);
925 }
926
927 int
lkpi_80211_mo_suspend(struct ieee80211_hw * hw,struct cfg80211_wowlan * wowlan)928 lkpi_80211_mo_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
929 {
930 struct lkpi_hw *lhw;
931 int error;
932
933 might_sleep();
934 lockdep_assert_wiphy(hw->wiphy);
935
936 lhw = HW_TO_LHW(hw);
937 if (lhw->ops->suspend == NULL) {
938 error = EOPNOTSUPP;
939 goto out;
940 }
941
942 LKPI_80211_TRACE_MO("hw %p wowlan %p", hw, wowlan);
943 error = lhw->ops->suspend(hw, wowlan);
944
945 out:
946 return (error);
947 }
948
949 int
lkpi_80211_mo_resume(struct ieee80211_hw * hw)950 lkpi_80211_mo_resume(struct ieee80211_hw *hw)
951 {
952 struct lkpi_hw *lhw;
953 int error;
954
955 might_sleep();
956 lockdep_assert_wiphy(hw->wiphy);
957
958 lhw = HW_TO_LHW(hw);
959 if (lhw->ops->resume == NULL) {
960 error = EOPNOTSUPP;
961 goto out;
962 }
963
964 LKPI_80211_TRACE_MO("hw %p", hw);
965 error = lhw->ops->resume(hw);
966
967 out:
968 return (error);
969 }
970
971 int
lkpi_80211_mo_set_wakeup(struct ieee80211_hw * hw,bool enable)972 lkpi_80211_mo_set_wakeup(struct ieee80211_hw *hw, bool enable)
973 {
974 struct lkpi_hw *lhw;
975 int error;
976
977 might_sleep();
978 lockdep_assert_wiphy(hw->wiphy);
979
980 lhw = HW_TO_LHW(hw);
981 if (lhw->ops->set_wakeup == NULL) {
982 error = EOPNOTSUPP;
983 goto out;
984 }
985
986 LKPI_80211_TRACE_MO("hw %p enable %d", hw, enable);
987 lhw->ops->set_wakeup(hw, enable);
988 error = 0;
989
990 out:
991 return (error);
992 }
993
994 int
lkpi_80211_mo_set_rekey_data(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct cfg80211_gtk_rekey_data * grd)995 lkpi_80211_mo_set_rekey_data(struct ieee80211_hw *hw,
996 struct ieee80211_vif *vif, struct cfg80211_gtk_rekey_data *grd)
997 {
998 struct lkpi_hw *lhw;
999 int error;
1000
1001 might_sleep();
1002 lockdep_assert_wiphy(hw->wiphy);
1003
1004 lhw = HW_TO_LHW(hw);
1005 if (lhw->ops->set_rekey_data == NULL) {
1006 error = EOPNOTSUPP;
1007 goto out;
1008 }
1009
1010 LKPI_80211_TRACE_MO("hw %p vif %p grd %p", hw, vif, grd);
1011 lhw->ops->set_rekey_data(hw, vif, grd);
1012 error = 0;
1013
1014 out:
1015 return (error);
1016 }
1017
1018 int
lkpi_80211_mo_set_default_unicast_key(struct ieee80211_hw * hw,struct ieee80211_vif * vif,int idx)1019 lkpi_80211_mo_set_default_unicast_key(struct ieee80211_hw *hw,
1020 struct ieee80211_vif *vif, int idx)
1021 {
1022 struct lkpi_hw *lhw;
1023 int error;
1024
1025 might_sleep();
1026 lockdep_assert_wiphy(hw->wiphy);
1027
1028 lhw = HW_TO_LHW(hw);
1029 if (lhw->ops->set_default_unicast_key == NULL) {
1030 error = EOPNOTSUPP;
1031 goto out;
1032 }
1033
1034 LKPI_80211_TRACE_MO("hw %p vif %p idx %d", hw, vif, idx);
1035 lhw->ops->set_default_unicast_key(hw, vif, idx);
1036 error = 0;
1037
1038 out:
1039 return (error);
1040 }
1041
1042