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 lhw = HW_TO_LHW(hw);
84 if (lhw->ops->stop == NULL)
85 return;
86
87 LKPI_80211_TRACE_MO("hw %p suspend %d", hw, suspend);
88 lhw->ops->stop(hw, suspend);
89 lhw->sc_flags &= ~LKPI_MAC80211_DRV_STARTED;
90 }
91
92 int
lkpi_80211_mo_get_antenna(struct ieee80211_hw * hw,u32 * txs,u32 * rxs)93 lkpi_80211_mo_get_antenna(struct ieee80211_hw *hw, u32 *txs, u32 *rxs)
94 {
95 struct lkpi_hw *lhw;
96 int error;
97
98 lhw = HW_TO_LHW(hw);
99 if (lhw->ops->get_antenna == NULL) {
100 error = EOPNOTSUPP;
101 goto out;
102 }
103
104 LKPI_80211_TRACE_MO("hw %p", hw);
105 LKPI_80211_TRACE_MO("TODO link/radio_idx");
106 error = lhw->ops->get_antenna(hw, 0, txs, rxs);
107
108 out:
109 return (error);
110 }
111
112 int
lkpi_80211_mo_set_frag_threshold(struct ieee80211_hw * hw,uint32_t frag_th)113 lkpi_80211_mo_set_frag_threshold(struct ieee80211_hw *hw, uint32_t frag_th)
114 {
115 struct lkpi_hw *lhw;
116 int error;
117
118 lhw = HW_TO_LHW(hw);
119 if (lhw->ops->set_frag_threshold == NULL) {
120 error = EOPNOTSUPP;
121 goto out;
122 }
123
124 LKPI_80211_TRACE_MO("hw %p frag_th %u", hw, frag_th);
125 LKPI_80211_TRACE_MO("TODO link/radio_idx");
126 error = lhw->ops->set_frag_threshold(hw, 0, frag_th);
127
128 out:
129 return (error);
130 }
131
132 int
lkpi_80211_mo_set_rts_threshold(struct ieee80211_hw * hw,uint32_t rts_th)133 lkpi_80211_mo_set_rts_threshold(struct ieee80211_hw *hw, uint32_t rts_th)
134 {
135 struct lkpi_hw *lhw;
136 int error;
137
138 lhw = HW_TO_LHW(hw);
139 if (lhw->ops->set_rts_threshold == NULL) {
140 error = EOPNOTSUPP;
141 goto out;
142 }
143
144 LKPI_80211_TRACE_MO("hw %p rts_th %u", hw, rts_th);
145 LKPI_80211_TRACE_MO("TODO link/radio_idx");
146 error = lhw->ops->set_rts_threshold(hw, 0, rts_th);
147
148 out:
149 return (error);
150 }
151
152
153 int
lkpi_80211_mo_add_interface(struct ieee80211_hw * hw,struct ieee80211_vif * vif)154 lkpi_80211_mo_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
155 {
156 struct lkpi_hw *lhw;
157 struct lkpi_vif *lvif;
158 int error;
159
160 lhw = HW_TO_LHW(hw);
161 if (lhw->ops->add_interface == NULL) {
162 error = EOPNOTSUPP;
163 goto out;
164 }
165
166 lvif = VIF_TO_LVIF(vif);
167 LKPI_80211_LVIF_LOCK(lvif);
168 if (lvif->added_to_drv) {
169 LKPI_80211_LVIF_UNLOCK(lvif);
170 /* Trying to add twice is an error. */
171 error = EEXIST;
172 goto out;
173 }
174 LKPI_80211_LVIF_UNLOCK(lvif);
175
176 LKPI_80211_TRACE_MO("hw %p vif %p", hw, vif);
177 error = lhw->ops->add_interface(hw, vif);
178 if (error == 0) {
179 LKPI_80211_LVIF_LOCK(lvif);
180 lvif->added_to_drv = true;
181 LKPI_80211_LVIF_UNLOCK(lvif);
182 }
183
184 out:
185 return (error);
186 }
187
188 void
lkpi_80211_mo_remove_interface(struct ieee80211_hw * hw,struct ieee80211_vif * vif)189 lkpi_80211_mo_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
190 {
191 struct lkpi_hw *lhw;
192 struct lkpi_vif *lvif;
193
194 lhw = HW_TO_LHW(hw);
195 if (lhw->ops->remove_interface == NULL)
196 return;
197
198 lvif = VIF_TO_LVIF(vif);
199 LKPI_80211_LVIF_LOCK(lvif);
200 if (!lvif->added_to_drv) {
201 LKPI_80211_LVIF_UNLOCK(lvif);
202 return;
203 }
204 LKPI_80211_LVIF_UNLOCK(lvif);
205
206 LKPI_80211_TRACE_MO("hw %p vif %p", hw, vif);
207 lhw->ops->remove_interface(hw, vif);
208 LKPI_80211_LVIF_LOCK(lvif);
209 lvif->added_to_drv = false;
210 LKPI_80211_LVIF_UNLOCK(lvif);
211 }
212
213
214 int
lkpi_80211_mo_hw_scan(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_scan_request * sr)215 lkpi_80211_mo_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
216 struct ieee80211_scan_request *sr)
217 {
218 struct lkpi_hw *lhw;
219 int error;
220
221 /*
222 * MUST NOT return EPERM as that is a "magic number 1" based on rtw88
223 * driver indicating hw_scan is not supported despite the ops call
224 * being available.
225 */
226
227 lhw = HW_TO_LHW(hw);
228 if (lhw->ops->hw_scan == NULL) {
229 /* Return magic number to use sw scan. */
230 error = 1;
231 goto out;
232 }
233
234 LKPI_80211_TRACE_MO("CALLING hw %p vif %p sr %p", hw, vif, sr);
235 error = lhw->ops->hw_scan(hw, vif, sr);
236 LKPI_80211_TRACE_MO("RETURNING hw %p vif %p sr %p error %d", hw, vif, sr, error);
237
238 out:
239 return (error);
240 }
241
242 void
lkpi_80211_mo_cancel_hw_scan(struct ieee80211_hw * hw,struct ieee80211_vif * vif)243 lkpi_80211_mo_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
244 {
245 struct lkpi_hw *lhw;
246
247 lhw = HW_TO_LHW(hw);
248 if (lhw->ops->cancel_hw_scan == NULL)
249 return;
250
251 LKPI_80211_TRACE_MO("hw %p vif %p", hw, vif);
252 lhw->ops->cancel_hw_scan(hw, vif);
253 }
254
255 void
lkpi_80211_mo_sw_scan_complete(struct ieee80211_hw * hw,struct ieee80211_vif * vif)256 lkpi_80211_mo_sw_scan_complete(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
257 {
258 struct lkpi_hw *lhw;
259
260 lhw = HW_TO_LHW(hw);
261 if (lhw->ops->sw_scan_complete == NULL)
262 return;
263
264 LKPI_80211_TRACE_MO("hw %p vif %p", hw, vif);
265 lhw->ops->sw_scan_complete(hw, vif);
266 lhw->scan_flags &= ~LKPI_LHW_SCAN_RUNNING;
267 }
268
269 void
lkpi_80211_mo_sw_scan_start(struct ieee80211_hw * hw,struct ieee80211_vif * vif,const u8 * addr)270 lkpi_80211_mo_sw_scan_start(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
271 const u8 *addr)
272 {
273 struct lkpi_hw *lhw;
274
275 lhw = HW_TO_LHW(hw);
276 if (lhw->ops->sw_scan_start == NULL)
277 return;
278
279 LKPI_80211_TRACE_MO("hw %p vif %p", hw, vif);
280 lhw->ops->sw_scan_start(hw, vif, addr);
281 }
282
283
284 /*
285 * We keep the Linux type here; it really is an uintptr_t.
286 */
287 u64
lkpi_80211_mo_prepare_multicast(struct ieee80211_hw * hw,struct netdev_hw_addr_list * mc_list)288 lkpi_80211_mo_prepare_multicast(struct ieee80211_hw *hw,
289 struct netdev_hw_addr_list *mc_list)
290 {
291 struct lkpi_hw *lhw;
292 u64 ptr;
293
294 lhw = HW_TO_LHW(hw);
295 if (lhw->ops->prepare_multicast == NULL)
296 return (0);
297
298 LKPI_80211_TRACE_MO("hw %p mc_list %p", hw, mc_list);
299 ptr = lhw->ops->prepare_multicast(hw, mc_list);
300 return (ptr);
301 }
302
303 void
lkpi_80211_mo_configure_filter(struct ieee80211_hw * hw,unsigned int changed_flags,unsigned int * total_flags,u64 mc_ptr)304 lkpi_80211_mo_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags,
305 unsigned int *total_flags, u64 mc_ptr)
306 {
307 struct lkpi_hw *lhw;
308
309 lhw = HW_TO_LHW(hw);
310 if (lhw->ops->configure_filter == NULL)
311 return;
312
313 LKPI_80211_TRACE_MO("hw %p changed_flags %#x total_flags %p mc_ptr %ju", hw, changed_flags, total_flags, (uintmax_t)mc_ptr);
314 lhw->ops->configure_filter(hw, changed_flags, total_flags, mc_ptr);
315 }
316
317
318 /*
319 * So far we only called sta_{add,remove} as an alternative to sta_state.
320 * Let's keep the implementation simpler and hide sta_{add,remove} under the
321 * hood here calling them if state_state is not available from mo_sta_state.
322 */
323 static int
lkpi_80211_mo_sta_add(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_sta * sta)324 lkpi_80211_mo_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
325 struct ieee80211_sta *sta)
326 {
327 struct lkpi_hw *lhw;
328 struct lkpi_sta *lsta;
329 int error;
330
331 lhw = HW_TO_LHW(hw);
332 if (lhw->ops->sta_add == NULL) {
333 error = EOPNOTSUPP;
334 goto out;
335 }
336
337 lsta = STA_TO_LSTA(sta);
338 if (lsta->added_to_drv) {
339 error = EEXIST;
340 goto out;
341 }
342
343 LKPI_80211_TRACE_MO("hw %p vif %p sta %p", hw, vif, sta);
344 error = lhw->ops->sta_add(hw, vif, sta);
345 if (error == 0)
346 lsta->added_to_drv = true;
347
348 out:
349 return error;
350 }
351
352 static int
lkpi_80211_mo_sta_remove(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_sta * sta)353 lkpi_80211_mo_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
354 struct ieee80211_sta *sta)
355 {
356 struct lkpi_hw *lhw;
357 struct lkpi_sta *lsta;
358 int error;
359
360 lhw = HW_TO_LHW(hw);
361 if (lhw->ops->sta_remove == NULL) {
362 error = EOPNOTSUPP;
363 goto out;
364 }
365
366 lsta = STA_TO_LSTA(sta);
367 if (!lsta->added_to_drv) {
368 /* If we never added the sta, do not complain on cleanup. */
369 error = 0;
370 goto out;
371 }
372
373 LKPI_80211_TRACE_MO("hw %p vif %p sta %p", hw, vif, sta);
374 error = lhw->ops->sta_remove(hw, vif, sta);
375 if (error == 0)
376 lsta->added_to_drv = false;
377
378 out:
379 return error;
380 }
381
382 int
lkpi_80211_mo_sta_state(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct lkpi_sta * lsta,enum ieee80211_sta_state nstate)383 lkpi_80211_mo_sta_state(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
384 struct lkpi_sta *lsta, enum ieee80211_sta_state nstate)
385 {
386 struct lkpi_hw *lhw;
387 struct ieee80211_sta *sta;
388 int error;
389
390 lhw = HW_TO_LHW(hw);
391 sta = LSTA_TO_STA(lsta);
392 if (lhw->ops->sta_state != NULL) {
393 LKPI_80211_TRACE_MO("hw %p vif %p sta %p nstate %d", hw, vif, sta, nstate);
394 error = lhw->ops->sta_state(hw, vif, sta, lsta->state, nstate);
395 if (error == 0) {
396 if (nstate == IEEE80211_STA_NOTEXIST)
397 lsta->added_to_drv = false;
398 else
399 lsta->added_to_drv = true;
400 lsta->state = nstate;
401 }
402 goto out;
403 }
404
405 /* XXX-BZ is the change state AUTH or ASSOC here? */
406 if (lsta->state < IEEE80211_STA_ASSOC && nstate == IEEE80211_STA_ASSOC) {
407 error = lkpi_80211_mo_sta_add(hw, vif, sta);
408 if (error == 0)
409 lsta->added_to_drv = true;
410 } else if (lsta->state >= IEEE80211_STA_ASSOC &&
411 nstate < IEEE80211_STA_ASSOC) {
412 error = lkpi_80211_mo_sta_remove(hw, vif, sta);
413 if (error == 0)
414 lsta->added_to_drv = false;
415 } else
416 /* Nothing to do. */
417 error = 0;
418 if (error == 0)
419 lsta->state = nstate;
420
421 out:
422 /* XXX-BZ should we manage state in here? */
423 return (error);
424 }
425
426 int
lkpi_80211_mo_config(struct ieee80211_hw * hw,uint32_t changed)427 lkpi_80211_mo_config(struct ieee80211_hw *hw, uint32_t changed)
428 {
429 struct lkpi_hw *lhw;
430 int error;
431
432 lhw = HW_TO_LHW(hw);
433 if (lhw->ops->config == NULL) {
434 error = EOPNOTSUPP;
435 goto out;
436 }
437
438 LKPI_80211_TRACE_MO("hw %p changed %u", hw, changed);
439 LKPI_80211_TRACE_MO("TODO link/radio_idx");
440 error = lhw->ops->config(hw, 0, changed);
441
442 out:
443 return (error);
444 }
445
446
447 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)448 lkpi_80211_mo_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
449 struct ieee80211_bss_conf *conf, struct ieee80211_chanctx_conf *chanctx_conf)
450 {
451 struct lkpi_hw *lhw;
452 int error;
453
454 lhw = HW_TO_LHW(hw);
455 if (lhw->ops->assign_vif_chanctx == NULL) {
456 error = EOPNOTSUPP;
457 goto out;
458 }
459
460 LKPI_80211_TRACE_MO("hw %p vif %p bss_conf %p chanctx_conf %p",
461 hw, vif, conf, chanctx_conf);
462 error = lhw->ops->assign_vif_chanctx(hw, vif, conf, chanctx_conf);
463 if (error == 0)
464 vif->bss_conf.chanctx_conf = chanctx_conf;
465
466 out:
467 return (error);
468 }
469
470 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)471 lkpi_80211_mo_unassign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
472 struct ieee80211_bss_conf *conf, struct ieee80211_chanctx_conf *chanctx_conf)
473 {
474 struct lkpi_hw *lhw;
475
476 might_sleep();
477 lockdep_assert_wiphy(hw->wiphy);
478
479 lhw = HW_TO_LHW(hw);
480 if (lhw->ops->unassign_vif_chanctx == NULL)
481 return;
482
483 if (chanctx_conf == NULL)
484 return;
485
486 LKPI_80211_TRACE_MO("hw %p vif %p bss_conf %p chanctx_conf %p",
487 hw, vif, conf, chanctx_conf);
488 lhw->ops->unassign_vif_chanctx(hw, vif, conf, chanctx_conf);
489 }
490
491
492 int
lkpi_80211_mo_add_chanctx(struct ieee80211_hw * hw,struct ieee80211_chanctx_conf * chanctx_conf)493 lkpi_80211_mo_add_chanctx(struct ieee80211_hw *hw,
494 struct ieee80211_chanctx_conf *chanctx_conf)
495 {
496 struct lkpi_hw *lhw;
497 struct lkpi_chanctx *lchanctx;
498 int error;
499
500 lhw = HW_TO_LHW(hw);
501 if (lhw->ops->add_chanctx == NULL) {
502 error = EOPNOTSUPP;
503 goto out;
504 }
505
506 LKPI_80211_TRACE_MO("hw %p chanctx_conf %p", hw, chanctx_conf);
507 error = lhw->ops->add_chanctx(hw, chanctx_conf);
508 if (error == 0) {
509 lchanctx = CHANCTX_CONF_TO_LCHANCTX(chanctx_conf);
510 lchanctx->added_to_drv = true;
511 }
512
513 out:
514 return (error);
515 }
516
517 void
lkpi_80211_mo_change_chanctx(struct ieee80211_hw * hw,struct ieee80211_chanctx_conf * chanctx_conf,uint32_t changed)518 lkpi_80211_mo_change_chanctx(struct ieee80211_hw *hw,
519 struct ieee80211_chanctx_conf *chanctx_conf, uint32_t changed)
520 {
521 struct lkpi_hw *lhw;
522
523 lhw = HW_TO_LHW(hw);
524 if (lhw->ops->change_chanctx == NULL)
525 return;
526
527 LKPI_80211_TRACE_MO("hw %p chanctx_conf %p changed %u", hw, chanctx_conf, changed);
528 lhw->ops->change_chanctx(hw, chanctx_conf, changed);
529 }
530
531 void
lkpi_80211_mo_remove_chanctx(struct ieee80211_hw * hw,struct ieee80211_chanctx_conf * chanctx_conf)532 lkpi_80211_mo_remove_chanctx(struct ieee80211_hw *hw,
533 struct ieee80211_chanctx_conf *chanctx_conf)
534 {
535 struct lkpi_hw *lhw;
536 struct lkpi_chanctx *lchanctx;
537
538 lhw = HW_TO_LHW(hw);
539 if (lhw->ops->remove_chanctx == NULL)
540 return;
541
542 LKPI_80211_TRACE_MO("hw %p chanctx_conf %p", hw, chanctx_conf);
543 lhw->ops->remove_chanctx(hw, chanctx_conf);
544 lchanctx = CHANCTX_CONF_TO_LCHANCTX(chanctx_conf);
545 lchanctx->added_to_drv = false;
546 }
547
548 void
lkpi_80211_mo_vif_cfg_changed(struct ieee80211_hw * hw,struct ieee80211_vif * vif,uint64_t vif_cfg_bits,bool fallback)549 lkpi_80211_mo_vif_cfg_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
550 uint64_t vif_cfg_bits, bool fallback)
551 {
552 struct lkpi_hw *lhw;
553
554 might_sleep();
555 /* XXX-FINISH all callers for lockdep_assert_wiphy(hw->wiphy); */
556
557 lhw = HW_TO_LHW(hw);
558 if (lhw->ops->vif_cfg_changed == NULL &&
559 lhw->ops->bss_info_changed == NULL)
560 return;
561
562 if (vif_cfg_bits == 0)
563 return;
564
565 LKPI_80211_TRACE_MO("hw %p vif %p vif_cfg_bits %#jx", hw, vif, (uintmax_t)vif_cfg_bits);
566 if (lhw->ops->link_info_changed != NULL)
567 lhw->ops->vif_cfg_changed(hw, vif, vif_cfg_bits);
568 else if (fallback)
569 lhw->ops->bss_info_changed(hw, vif, &vif->bss_conf, vif_cfg_bits);
570 }
571
572 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)573 lkpi_80211_mo_link_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
574 struct ieee80211_bss_conf *conf, uint64_t link_info_bits, uint8_t link_id,
575 bool fallback)
576 {
577 struct lkpi_hw *lhw;
578
579 might_sleep();
580 /* XXX-FINISH all callers for lockdep_assert_wiphy(hw->wiphy); */
581
582 lhw = HW_TO_LHW(hw);
583 if (lhw->ops->link_info_changed == NULL &&
584 lhw->ops->bss_info_changed == NULL)
585 return;
586
587 if (link_info_bits == 0)
588 return;
589
590 if (!ieee80211_vif_link_active(vif, link_id))
591 return;
592
593 LKPI_80211_TRACE_MO("hw %p vif %p conf %p link_info_bits %#jx", hw, vif, conf, (uintmax_t)link_info_bits);
594 if (lhw->ops->link_info_changed != NULL)
595 lhw->ops->link_info_changed(hw, vif, conf, link_info_bits);
596 else if (fallback)
597 lhw->ops->bss_info_changed(hw, vif, conf, link_info_bits);
598 }
599
600 /*
601 * This is basically obsolete but one caller.
602 * The functionality is now split between lkpi_80211_mo_link_info_changed() and
603 * lkpi_80211_mo_vif_cfg_changed(). Those functions have a flag whether to call
604 * the (*bss_info_changed) fallback or not. See lkpi_bss_info_change().
605 */
606 void
lkpi_80211_mo_bss_info_changed(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_bss_conf * conf,uint64_t bss_changed)607 lkpi_80211_mo_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
608 struct ieee80211_bss_conf *conf, uint64_t bss_changed)
609 {
610 struct lkpi_hw *lhw;
611
612 /* XXX-FINISH all callers for lockdep_assert_wiphy(hw->wiphy); */
613
614 lhw = HW_TO_LHW(hw);
615 if (lhw->ops->bss_info_changed == NULL)
616 return;
617
618 if (bss_changed == 0)
619 return;
620
621 LKPI_80211_TRACE_MO("hw %p vif %p conf %p changed %#jx", hw, vif, conf, (uintmax_t)bss_changed);
622 lhw->ops->bss_info_changed(hw, vif, conf, bss_changed);
623 }
624
625 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)626 lkpi_80211_mo_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
627 uint32_t link_id, uint16_t ac, const struct ieee80211_tx_queue_params *txqp)
628 {
629 struct lkpi_hw *lhw;
630 int error;
631
632 lhw = HW_TO_LHW(hw);
633 if (lhw->ops->conf_tx == NULL) {
634 error = EOPNOTSUPP;
635 goto out;
636 }
637
638 LKPI_80211_TRACE_MO("hw %p vif %p link_id %u ac %u txpq %p",
639 hw, vif, link_id, ac, txqp);
640 error = lhw->ops->conf_tx(hw, vif, link_id, ac, txqp);
641
642 out:
643 return (error);
644 }
645
646 void
lkpi_80211_mo_flush(struct ieee80211_hw * hw,struct ieee80211_vif * vif,uint32_t nqueues,bool drop)647 lkpi_80211_mo_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
648 uint32_t nqueues, bool drop)
649 {
650 struct lkpi_hw *lhw;
651
652 lhw = HW_TO_LHW(hw);
653 if (lhw->ops->flush == NULL)
654 return;
655
656 LKPI_80211_TRACE_MO("hw %p vif %p nqueues %u drop %d", hw, vif, nqueues, drop);
657 lhw->ops->flush(hw, vif, nqueues, drop);
658 }
659
660 void
lkpi_80211_mo_mgd_prepare_tx(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_prep_tx_info * txinfo)661 lkpi_80211_mo_mgd_prepare_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
662 struct ieee80211_prep_tx_info *txinfo)
663 {
664 struct lkpi_hw *lhw;
665
666 lhw = HW_TO_LHW(hw);
667 if (lhw->ops->mgd_prepare_tx == NULL)
668 return;
669
670 LKPI_80211_TRACE_MO("hw %p vif %p txinfo %p", hw, vif, txinfo);
671 lhw->ops->mgd_prepare_tx(hw, vif, txinfo);
672 }
673
674 void
lkpi_80211_mo_mgd_complete_tx(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_prep_tx_info * txinfo)675 lkpi_80211_mo_mgd_complete_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
676 struct ieee80211_prep_tx_info *txinfo)
677 {
678 struct lkpi_hw *lhw;
679
680 lhw = HW_TO_LHW(hw);
681 if (lhw->ops->mgd_complete_tx == NULL)
682 return;
683
684 LKPI_80211_TRACE_MO("hw %p vif %p txinfo %p", hw, vif, txinfo);
685 lhw->ops->mgd_complete_tx(hw, vif, txinfo);
686 }
687
688 void
lkpi_80211_mo_tx(struct ieee80211_hw * hw,struct ieee80211_tx_control * txctrl,struct sk_buff * skb)689 lkpi_80211_mo_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *txctrl,
690 struct sk_buff *skb)
691 {
692 struct lkpi_hw *lhw;
693
694 lhw = HW_TO_LHW(hw);
695 if (lhw->ops->tx == NULL)
696 return;
697
698 LKPI_80211_TRACE_MO("hw %p txctrl %p skb %p", hw, txctrl, skb);
699 lhw->ops->tx(hw, txctrl, skb);
700 }
701
702 void
lkpi_80211_mo_wake_tx_queue(struct ieee80211_hw * hw,struct ieee80211_txq * txq,bool schedule)703 lkpi_80211_mo_wake_tx_queue(struct ieee80211_hw *hw, struct ieee80211_txq *txq,
704 bool schedule)
705 {
706 struct lkpi_hw *lhw;
707
708 lhw = HW_TO_LHW(hw);
709
710 /* Do the schedule before the check for wake_tx_queue supported! */
711 if (schedule)
712 ieee80211_schedule_txq(hw, txq);
713
714 if (lhw->ops->wake_tx_queue == NULL)
715 return;
716
717 LKPI_80211_TRACE_MO("hw %p txq %p", hw, txq);
718 lhw->ops->wake_tx_queue(hw, txq);
719 }
720
721 void
lkpi_80211_mo_sync_rx_queues(struct ieee80211_hw * hw)722 lkpi_80211_mo_sync_rx_queues(struct ieee80211_hw *hw)
723 {
724 struct lkpi_hw *lhw;
725
726 lhw = HW_TO_LHW(hw);
727 if (lhw->ops->sync_rx_queues == NULL)
728 return;
729
730 LKPI_80211_TRACE_MO("hw %p", hw);
731 lhw->ops->sync_rx_queues(hw);
732 }
733
734 void
lkpi_80211_mo_sta_pre_rcu_remove(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_sta * sta)735 lkpi_80211_mo_sta_pre_rcu_remove(struct ieee80211_hw *hw,
736 struct ieee80211_vif *vif, struct ieee80211_sta *sta)
737 {
738 struct lkpi_hw *lhw;
739
740 lhw = HW_TO_LHW(hw);
741 if (lhw->ops->sta_pre_rcu_remove == NULL)
742 return;
743
744 LKPI_80211_TRACE_MO("hw %p vif %p sta %p", hw, vif, sta);
745 lhw->ops->sta_pre_rcu_remove(hw, vif, sta);
746 }
747
748 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)749 lkpi_80211_mo_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
750 struct ieee80211_vif *vif, struct ieee80211_sta *sta,
751 struct ieee80211_key_conf *kc)
752 {
753 struct lkpi_hw *lhw;
754 int error;
755
756 lockdep_assert_wiphy(hw->wiphy);
757
758 lhw = HW_TO_LHW(hw);
759 if (lhw->ops->set_key == NULL) {
760 error = EOPNOTSUPP;
761 goto out;
762 }
763
764 LKPI_80211_TRACE_MO("hw %p cmd %d vif %p sta %p kc %p", hw, cmd, vif, sta, kc);
765 error = lhw->ops->set_key(hw, cmd, vif, sta, kc);
766
767 out:
768 return (error);
769 }
770
771 int
lkpi_80211_mo_ampdu_action(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_ampdu_params * params)772 lkpi_80211_mo_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
773 struct ieee80211_ampdu_params *params)
774 {
775 struct lkpi_hw *lhw;
776 int error;
777
778 lhw = HW_TO_LHW(hw);
779 if (lhw->ops->ampdu_action == NULL) {
780 error = EOPNOTSUPP;
781 goto out;
782 }
783
784 LKPI_80211_TRACE_MO("hw %p vif %p params %p { %p, %d, %u, %u, %u, %u, %d }",
785 hw, vif, params, params->sta, params->action, params->buf_size,
786 params->timeout, params->ssn, params->tid, params->amsdu);
787 error = lhw->ops->ampdu_action(hw, vif, params);
788
789 out:
790 return (error);
791 }
792
793 int
lkpi_80211_mo_sta_statistics(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_sta * sta,struct station_info * sinfo)794 lkpi_80211_mo_sta_statistics(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
795 struct ieee80211_sta *sta, struct station_info *sinfo)
796 {
797 struct lkpi_hw *lhw;
798 struct lkpi_sta *lsta;
799 int error;
800
801 lhw = HW_TO_LHW(hw);
802 if (lhw->ops->sta_statistics == NULL) {
803 error = EOPNOTSUPP;
804 goto out;
805 }
806
807 lsta = STA_TO_LSTA(sta);
808 if (!lsta->added_to_drv) {
809 error = EEXIST;
810 goto out;
811 }
812
813 lockdep_assert_wiphy(hw->wiphy);
814
815 LKPI_80211_TRACE_MO("hw %p vif %p sta %p sinfo %p", hw, vif, sta, sinfo);
816 lhw->ops->sta_statistics(hw, vif, sta, sinfo);
817 error = 0;
818
819 out:
820 return (error);
821 }
822