xref: /freebsd/sys/compat/linuxkpi/common/src/linux_80211_macops.c (revision 255538cd906045095d0c2113ae6c4731ce36c0cf)
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
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
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
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
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
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
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
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
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 	lockdep_assert_wiphy(hw->wiphy);
222 
223 	/*
224 	 * MUST NOT return EPERM as that is a "magic number 1" based on rtw88
225 	 * driver indicating hw_scan is not supported despite the ops call
226 	 * being available.
227 	 */
228 
229 	lhw = HW_TO_LHW(hw);
230 	if (lhw->ops->hw_scan == NULL) {
231 		/* Return magic number to use sw scan. */
232 		error = 1;
233 		goto out;
234 	}
235 
236 	LKPI_80211_TRACE_MO("CALLING hw %p vif %p sr %p", hw, vif, sr);
237 	error = lhw->ops->hw_scan(hw, vif, sr);
238 	LKPI_80211_TRACE_MO("RETURNING hw %p vif %p sr %p error %d", hw, vif, sr, error);
239 
240 out:
241 	return (error);
242 }
243 
244 void
245 lkpi_80211_mo_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
246 {
247 	struct lkpi_hw *lhw;
248 
249 	lockdep_assert_wiphy(hw->wiphy);
250 
251 	lhw = HW_TO_LHW(hw);
252 	if (lhw->ops->cancel_hw_scan == NULL)
253 		return;
254 
255 	LKPI_80211_TRACE_MO("hw %p vif %p", hw, vif);
256 	lhw->ops->cancel_hw_scan(hw, vif);
257 }
258 
259 void
260 lkpi_80211_mo_sw_scan_complete(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
261 {
262 	struct lkpi_hw *lhw;
263 
264 	lhw = HW_TO_LHW(hw);
265 	if (lhw->ops->sw_scan_complete == NULL)
266 		return;
267 
268 	LKPI_80211_TRACE_MO("hw %p vif %p", hw, vif);
269 	lhw->ops->sw_scan_complete(hw, vif);
270 	lhw->scan_flags &= ~LKPI_LHW_SCAN_RUNNING;
271 }
272 
273 void
274 lkpi_80211_mo_sw_scan_start(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
275     const u8 *addr)
276 {
277 	struct lkpi_hw *lhw;
278 
279 	lhw = HW_TO_LHW(hw);
280 	if (lhw->ops->sw_scan_start == NULL)
281 		return;
282 
283 	LKPI_80211_TRACE_MO("hw %p vif %p", hw, vif);
284 	lhw->ops->sw_scan_start(hw, vif, addr);
285 }
286 
287 
288 /*
289  * We keep the Linux type here;  it really is an uintptr_t.
290  */
291 u64
292 lkpi_80211_mo_prepare_multicast(struct ieee80211_hw *hw,
293     struct netdev_hw_addr_list *mc_list)
294 {
295 	struct lkpi_hw *lhw;
296 	u64 ptr;
297 
298 	/* This seems fine without the wiphy lock. */
299 
300 	lhw = HW_TO_LHW(hw);
301 	if (lhw->ops->prepare_multicast == NULL)
302 		return (0);
303 
304 	LKPI_80211_TRACE_MO("hw %p mc_list %p", hw, mc_list);
305 	ptr = lhw->ops->prepare_multicast(hw, mc_list);
306 	return (ptr);
307 }
308 
309 void
310 lkpi_80211_mo_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags,
311     unsigned int *total_flags, u64 mc_ptr)
312 {
313 	struct lkpi_hw *lhw;
314 
315 	lockdep_assert_wiphy(hw->wiphy);
316 
317 	lhw = HW_TO_LHW(hw);
318 	if (lhw->ops->configure_filter == NULL)
319 		return;
320 
321 	LKPI_80211_TRACE_MO("hw %p changed_flags %#x total_flags %p mc_ptr %ju", hw, changed_flags, total_flags, (uintmax_t)mc_ptr);
322 	lhw->ops->configure_filter(hw, changed_flags, total_flags, mc_ptr);
323 }
324 
325 
326 /*
327  * So far we only called sta_{add,remove} as an alternative to sta_state.
328  * Let's keep the implementation simpler and hide sta_{add,remove} under the
329  * hood here calling them if state_state is not available from mo_sta_state.
330  */
331 static int
332 lkpi_80211_mo_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
333     struct ieee80211_sta *sta)
334 {
335 	struct lkpi_hw *lhw;
336 	struct lkpi_sta *lsta;
337 	int error;
338 
339 	lhw = HW_TO_LHW(hw);
340 	if (lhw->ops->sta_add == NULL) {
341 		error = EOPNOTSUPP;
342 		goto out;
343 	}
344 
345 	lsta = STA_TO_LSTA(sta);
346 	if (lsta->added_to_drv) {
347 		error = EEXIST;
348 		goto out;
349 	}
350 
351 	LKPI_80211_TRACE_MO("hw %p vif %p sta %p", hw, vif, sta);
352 	error = lhw->ops->sta_add(hw, vif, sta);
353 	if (error == 0)
354 		lsta->added_to_drv = true;
355 
356 out:
357 	return error;
358 }
359 
360 static int
361 lkpi_80211_mo_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
362     struct ieee80211_sta *sta)
363 {
364 	struct lkpi_hw *lhw;
365 	struct lkpi_sta *lsta;
366 	int error;
367 
368 	lhw = HW_TO_LHW(hw);
369 	if (lhw->ops->sta_remove == NULL) {
370 		error = EOPNOTSUPP;
371 		goto out;
372 	}
373 
374 	lsta = STA_TO_LSTA(sta);
375 	if (!lsta->added_to_drv) {
376 		/* If we never added the sta, do not complain on cleanup. */
377 		error = 0;
378 		goto out;
379 	}
380 
381 	LKPI_80211_TRACE_MO("hw %p vif %p sta %p", hw, vif, sta);
382 	error = lhw->ops->sta_remove(hw, vif, sta);
383 	if (error == 0)
384 		lsta->added_to_drv = false;
385 
386 out:
387 	return error;
388 }
389 
390 int
391 lkpi_80211_mo_sta_state(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
392     struct lkpi_sta *lsta, enum ieee80211_sta_state nstate)
393 {
394 	struct lkpi_hw *lhw;
395 	struct ieee80211_sta *sta;
396 	int error;
397 
398 	lhw = HW_TO_LHW(hw);
399 	sta = LSTA_TO_STA(lsta);
400 	if (lhw->ops->sta_state != NULL) {
401 		LKPI_80211_TRACE_MO("hw %p vif %p sta %p nstate %d", hw, vif, sta, nstate);
402 		error = lhw->ops->sta_state(hw, vif, sta, lsta->state, nstate);
403 		if (error == 0) {
404 			if (nstate == IEEE80211_STA_NOTEXIST)
405 				lsta->added_to_drv = false;
406 			else
407 				lsta->added_to_drv = true;
408 			lsta->state = nstate;
409 		}
410 		goto out;
411 	}
412 
413 	/* XXX-BZ is the change state AUTH or ASSOC here? */
414 	if (lsta->state < IEEE80211_STA_ASSOC && nstate == IEEE80211_STA_ASSOC) {
415 		error = lkpi_80211_mo_sta_add(hw, vif, sta);
416 		if (error == 0)
417 			lsta->added_to_drv = true;
418 	} else if (lsta->state >= IEEE80211_STA_ASSOC &&
419 	    nstate < IEEE80211_STA_ASSOC) {
420 		error = lkpi_80211_mo_sta_remove(hw, vif, sta);
421 		if (error == 0)
422 			lsta->added_to_drv = false;
423 	} else
424 		/* Nothing to do. */
425 		error = 0;
426 	if (error == 0)
427 		lsta->state = nstate;
428 
429 out:
430 	/* XXX-BZ should we manage state in here? */
431 	return (error);
432 }
433 
434 int
435 lkpi_80211_mo_config(struct ieee80211_hw *hw, uint32_t changed)
436 {
437 	struct lkpi_hw *lhw;
438 	int error;
439 
440 	lockdep_assert_wiphy(hw->wiphy);
441 
442 	lhw = HW_TO_LHW(hw);
443 	if (lhw->ops->config == NULL) {
444 		error = EOPNOTSUPP;
445 		goto out;
446 	}
447 
448 	LKPI_80211_TRACE_MO("hw %p changed %u", hw, changed);
449 	LKPI_80211_TRACE_MO("TODO link/radio_idx");
450 	error = lhw->ops->config(hw, 0, changed);
451 
452 out:
453 	return (error);
454 }
455 
456 
457 int
458 lkpi_80211_mo_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
459     struct ieee80211_bss_conf *conf, struct ieee80211_chanctx_conf *chanctx_conf)
460 {
461 	struct lkpi_hw *lhw;
462 	int error;
463 
464 	lhw = HW_TO_LHW(hw);
465 	if (lhw->ops->assign_vif_chanctx == NULL) {
466 		error = EOPNOTSUPP;
467 		goto out;
468 	}
469 
470 	LKPI_80211_TRACE_MO("hw %p vif %p bss_conf %p chanctx_conf %p",
471 	    hw, vif, conf, chanctx_conf);
472 	error = lhw->ops->assign_vif_chanctx(hw, vif, conf, chanctx_conf);
473 	if (error == 0)
474 		vif->bss_conf.chanctx_conf = chanctx_conf;
475 
476 out:
477 	return (error);
478 }
479 
480 void
481 lkpi_80211_mo_unassign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
482     struct ieee80211_bss_conf *conf, struct ieee80211_chanctx_conf *chanctx_conf)
483 {
484 	struct lkpi_hw *lhw;
485 
486 	might_sleep();
487 	lockdep_assert_wiphy(hw->wiphy);
488 
489 	lhw = HW_TO_LHW(hw);
490 	if (lhw->ops->unassign_vif_chanctx == NULL)
491 		return;
492 
493 	if (chanctx_conf == NULL)
494 		return;
495 
496 	LKPI_80211_TRACE_MO("hw %p vif %p bss_conf %p chanctx_conf %p",
497 	    hw, vif, conf, chanctx_conf);
498 	lhw->ops->unassign_vif_chanctx(hw, vif, conf, chanctx_conf);
499 }
500 
501 
502 int
503 lkpi_80211_mo_add_chanctx(struct ieee80211_hw *hw,
504     struct ieee80211_chanctx_conf *chanctx_conf)
505 {
506 	struct lkpi_hw *lhw;
507 	struct lkpi_chanctx *lchanctx;
508 	int error;
509 
510 	might_sleep();
511 	lockdep_assert_wiphy(hw->wiphy);
512 
513 	lhw = HW_TO_LHW(hw);
514 	if (lhw->ops->add_chanctx == NULL) {
515 		error = EOPNOTSUPP;
516 		goto out;
517 	}
518 
519 	LKPI_80211_TRACE_MO("hw %p chanctx_conf %p", hw, chanctx_conf);
520 	error = lhw->ops->add_chanctx(hw, chanctx_conf);
521 	if (error == 0) {
522 		lchanctx = CHANCTX_CONF_TO_LCHANCTX(chanctx_conf);
523 		lchanctx->added_to_drv = true;
524 	}
525 
526 out:
527 	return (error);
528 }
529 
530 void
531 lkpi_80211_mo_change_chanctx(struct ieee80211_hw *hw,
532     struct ieee80211_chanctx_conf *chanctx_conf, uint32_t changed)
533 {
534 	struct lkpi_hw *lhw;
535 
536 	might_sleep();
537 	lockdep_assert_wiphy(hw->wiphy);
538 
539 	lhw = HW_TO_LHW(hw);
540 	if (lhw->ops->change_chanctx == NULL)
541 		return;
542 
543 	LKPI_80211_TRACE_MO("hw %p chanctx_conf %p changed %u", hw, chanctx_conf, changed);
544 	lhw->ops->change_chanctx(hw, chanctx_conf, changed);
545 }
546 
547 void
548 lkpi_80211_mo_remove_chanctx(struct ieee80211_hw *hw,
549     struct ieee80211_chanctx_conf *chanctx_conf)
550 {
551 	struct lkpi_hw *lhw;
552 	struct lkpi_chanctx *lchanctx;
553 
554 	might_sleep();
555 	lockdep_assert_wiphy(hw->wiphy);
556 
557 	lhw = HW_TO_LHW(hw);
558 	if (lhw->ops->remove_chanctx == NULL)
559 		return;
560 
561 	LKPI_80211_TRACE_MO("hw %p chanctx_conf %p", hw, chanctx_conf);
562 	lhw->ops->remove_chanctx(hw, chanctx_conf);
563 	lchanctx = CHANCTX_CONF_TO_LCHANCTX(chanctx_conf);
564 	lchanctx->added_to_drv = false;
565 }
566 
567 void
568 lkpi_80211_mo_vif_cfg_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
569     uint64_t vif_cfg_bits, bool fallback)
570 {
571 	struct lkpi_hw *lhw;
572 
573 	might_sleep();
574 	/* XXX-FINISH all callers for lockdep_assert_wiphy(hw->wiphy); */
575 
576 	lhw = HW_TO_LHW(hw);
577 	if (lhw->ops->vif_cfg_changed == NULL &&
578 	    lhw->ops->bss_info_changed == NULL)
579 		return;
580 
581 	if (vif_cfg_bits == 0)
582 		return;
583 
584 	LKPI_80211_TRACE_MO("hw %p vif %p vif_cfg_bits %#jx", hw, vif, (uintmax_t)vif_cfg_bits);
585 	if (lhw->ops->link_info_changed != NULL)
586 		lhw->ops->vif_cfg_changed(hw, vif, vif_cfg_bits);
587 	else if (fallback)
588 		lhw->ops->bss_info_changed(hw, vif, &vif->bss_conf, vif_cfg_bits);
589 }
590 
591 void
592 lkpi_80211_mo_link_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
593     struct ieee80211_bss_conf *conf, uint64_t link_info_bits, uint8_t link_id,
594     bool fallback)
595 {
596 	struct lkpi_hw *lhw;
597 
598 	might_sleep();
599 	/* XXX-FINISH all callers for lockdep_assert_wiphy(hw->wiphy); */
600 
601 	lhw = HW_TO_LHW(hw);
602 	if (lhw->ops->link_info_changed == NULL &&
603 	    lhw->ops->bss_info_changed == NULL)
604 		return;
605 
606 	if (link_info_bits == 0)
607 		return;
608 
609 	if (!ieee80211_vif_link_active(vif, link_id))
610 		return;
611 
612 	LKPI_80211_TRACE_MO("hw %p vif %p conf %p link_info_bits %#jx", hw, vif, conf, (uintmax_t)link_info_bits);
613 	if (lhw->ops->link_info_changed != NULL)
614 		lhw->ops->link_info_changed(hw, vif, conf, link_info_bits);
615 	else if (fallback)
616 		lhw->ops->bss_info_changed(hw, vif, conf, link_info_bits);
617 }
618 
619 /*
620  * This is basically obsolete but one caller.
621  * The functionality is now split between lkpi_80211_mo_link_info_changed() and
622  * lkpi_80211_mo_vif_cfg_changed().  Those functions have a flag whether to call
623  * the (*bss_info_changed) fallback or not.  See lkpi_bss_info_change().
624  */
625 void
626 lkpi_80211_mo_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
627     struct ieee80211_bss_conf *conf, uint64_t bss_changed)
628 {
629 	struct lkpi_hw *lhw;
630 
631 	/* XXX-FINISH all callers for lockdep_assert_wiphy(hw->wiphy); */
632 
633 	lhw = HW_TO_LHW(hw);
634 	if (lhw->ops->bss_info_changed == NULL)
635 		return;
636 
637 	if (bss_changed == 0)
638 		return;
639 
640 	LKPI_80211_TRACE_MO("hw %p vif %p conf %p changed %#jx", hw, vif, conf, (uintmax_t)bss_changed);
641 	lhw->ops->bss_info_changed(hw, vif, conf, bss_changed);
642 }
643 
644 int
645 lkpi_80211_mo_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
646     uint32_t link_id, uint16_t ac, const struct ieee80211_tx_queue_params *txqp)
647 {
648 	struct lkpi_hw *lhw;
649 	int error;
650 
651 	lhw = HW_TO_LHW(hw);
652 	if (lhw->ops->conf_tx == NULL) {
653 		error = EOPNOTSUPP;
654 		goto out;
655 	}
656 
657 	LKPI_80211_TRACE_MO("hw %p vif %p link_id %u ac %u txpq %p",
658 	    hw, vif, link_id, ac, txqp);
659 	error = lhw->ops->conf_tx(hw, vif, link_id, ac, txqp);
660 
661 out:
662 	return (error);
663 }
664 
665 void
666 lkpi_80211_mo_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
667     uint32_t nqueues, bool drop)
668 {
669 	struct lkpi_hw *lhw;
670 
671 	lhw = HW_TO_LHW(hw);
672 	if (lhw->ops->flush == NULL)
673 		return;
674 
675 	LKPI_80211_TRACE_MO("hw %p vif %p nqueues %u drop %d", hw, vif, nqueues, drop);
676 	lhw->ops->flush(hw, vif, nqueues, drop);
677 }
678 
679 void
680 lkpi_80211_mo_mgd_prepare_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
681     struct ieee80211_prep_tx_info *txinfo)
682 {
683 	struct lkpi_hw *lhw;
684 
685 	lhw = HW_TO_LHW(hw);
686 	if (lhw->ops->mgd_prepare_tx == NULL)
687 		return;
688 
689 	LKPI_80211_TRACE_MO("hw %p vif %p txinfo %p", hw, vif, txinfo);
690 	lhw->ops->mgd_prepare_tx(hw, vif, txinfo);
691 }
692 
693 void
694 lkpi_80211_mo_mgd_complete_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
695     struct ieee80211_prep_tx_info *txinfo)
696 {
697 	struct lkpi_hw *lhw;
698 
699 	lhw = HW_TO_LHW(hw);
700 	if (lhw->ops->mgd_complete_tx == NULL)
701 		return;
702 
703 	LKPI_80211_TRACE_MO("hw %p vif %p txinfo %p", hw, vif, txinfo);
704 	lhw->ops->mgd_complete_tx(hw, vif, txinfo);
705 }
706 
707 void
708 lkpi_80211_mo_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *txctrl,
709     struct sk_buff *skb)
710 {
711 	struct lkpi_hw *lhw;
712 
713 	lhw = HW_TO_LHW(hw);
714 	if (lhw->ops->tx == NULL)
715 		return;
716 
717 	LKPI_80211_TRACE_MO("hw %p txctrl %p skb %p", hw, txctrl, skb);
718 	lhw->ops->tx(hw, txctrl, skb);
719 }
720 
721 void
722 lkpi_80211_mo_wake_tx_queue(struct ieee80211_hw *hw, struct ieee80211_txq *txq,
723     bool schedule)
724 {
725 	struct lkpi_hw *lhw;
726 
727 	lhw = HW_TO_LHW(hw);
728 
729 	/* Do the schedule before the check for wake_tx_queue supported! */
730 	if (schedule)
731 		ieee80211_schedule_txq(hw, txq);
732 
733 	if (lhw->ops->wake_tx_queue == NULL)
734 		return;
735 
736 	LKPI_80211_TRACE_MO("hw %p txq %p", hw, txq);
737 	lhw->ops->wake_tx_queue(hw, txq);
738 }
739 
740 void
741 lkpi_80211_mo_sync_rx_queues(struct ieee80211_hw *hw)
742 {
743 	struct lkpi_hw *lhw;
744 
745 	lhw = HW_TO_LHW(hw);
746 	if (lhw->ops->sync_rx_queues == NULL)
747 		return;
748 
749 	LKPI_80211_TRACE_MO("hw %p", hw);
750 	lhw->ops->sync_rx_queues(hw);
751 }
752 
753 void
754 lkpi_80211_mo_sta_pre_rcu_remove(struct ieee80211_hw *hw,
755     struct ieee80211_vif *vif, struct ieee80211_sta *sta)
756 {
757 	struct lkpi_hw *lhw;
758 
759 	lhw = HW_TO_LHW(hw);
760 	if (lhw->ops->sta_pre_rcu_remove == NULL)
761 		return;
762 
763 	LKPI_80211_TRACE_MO("hw %p vif %p sta %p", hw, vif, sta);
764 	lhw->ops->sta_pre_rcu_remove(hw, vif, sta);
765 }
766 
767 void
768 lkpi_80211_mo_link_sta_rc_update(struct ieee80211_hw *hw,
769     struct ieee80211_vif *vif, struct ieee80211_link_sta *link_sta,
770     enum ieee80211_rate_control_changed_flags rc_changed)
771 {
772 	struct lkpi_hw *lhw;
773 
774 	lhw = HW_TO_LHW(hw);
775 	if (lhw->ops->link_sta_rc_update == NULL)
776 		return;
777 
778 	LKPI_80211_TRACE_MO("hw %p vif %p link_sta %p rc_changed %#010x",
779 	    hw, vif, link_sta, rc_changed);
780 	lhw->ops->link_sta_rc_update(hw, vif, link_sta, rc_changed);
781 }
782 
783 int
784 lkpi_80211_mo_set_bitrate_mask(struct ieee80211_hw *hw,
785     struct ieee80211_vif *vif, const struct cfg80211_bitrate_mask *br_mask)
786 {
787 	struct lkpi_hw *lhw;
788 	int error;
789 
790 	might_sleep();
791 	lockdep_assert_wiphy(hw->wiphy);
792 
793 	lhw = HW_TO_LHW(hw);
794 	if (lhw->ops->set_bitrate_mask == NULL) {
795 		error = EOPNOTSUPP;
796 		goto out;
797 	}
798 
799 	LKPI_80211_TRACE_MO("hw %p vif %p br_mask %p",
800 	    hw, vif, br_mask);
801 	error = lhw->ops->set_bitrate_mask(hw, vif, br_mask);
802 
803 out:
804 	return (error);
805 }
806 
807 int
808 lkpi_80211_mo_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
809     struct ieee80211_vif *vif, struct ieee80211_sta *sta,
810     struct ieee80211_key_conf *kc)
811 {
812 	struct lkpi_hw *lhw;
813 	int error;
814 
815 	lockdep_assert_wiphy(hw->wiphy);
816 
817 	lhw = HW_TO_LHW(hw);
818 	if (lhw->ops->set_key == NULL) {
819 		error = EOPNOTSUPP;
820 		goto out;
821 	}
822 
823 	/*
824 	 * Drivers will apply different logic depending on sta being set
825 	 * here or not and that depends on whether we have an address or
826 	 * not.  wpa_spplucoant::driver_bsd::bsd_set_key() will set a
827 	 * broadcast address if we do not have one;  further up in
828 	 * wpa_supplicant something presumably sets the broadcast address
829 	 * for group keys as well.
830 	 * We have to "undo" this here and set sta to NULL to avoid
831 	 * problems with hw_crypto in various drivers.
832 	 * We do this here so all set_key calls for (SET_KEY and DISABLE_KEY)
833 	 * are covered.
834 	 */
835 	MPASS(kc->_k != NULL);
836 	if (is_broadcast_ether_addr(kc->_k->wk_macaddr))
837 		sta = NULL;
838 
839 	LKPI_80211_TRACE_MO("hw %p cmd %d vif %p sta %p kc %p", hw, cmd, vif, sta, kc);
840 	error = lhw->ops->set_key(hw, cmd, vif, sta, kc);
841 
842 out:
843 	return (error);
844 }
845 
846 void
847 lkpi_80211_mo_sta_set_decap_offload(struct ieee80211_hw *hw,
848     struct ieee80211_vif *vif, struct ieee80211_sta *sta,
849     bool enable)
850 {
851 	struct lkpi_hw *lhw;
852 
853 	lockdep_assert_wiphy(hw->wiphy);
854 
855 	lhw = HW_TO_LHW(hw);
856 	if (lhw->ops->sta_set_decap_offload == NULL)
857 		return;
858 
859 	LKPI_80211_TRACE_MO("hw %p vif %p sta %p enable %d", hw, vif, sta, enable);
860 	lhw->ops->sta_set_decap_offload(hw, vif, sta, enable);
861 }
862 
863 int
864 lkpi_80211_mo_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
865     struct ieee80211_ampdu_params *params)
866 {
867 	struct lkpi_hw *lhw;
868 	int error;
869 
870 	lhw = HW_TO_LHW(hw);
871 	if (lhw->ops->ampdu_action == NULL) {
872 		error = EOPNOTSUPP;
873 		goto out;
874 	}
875 
876 	LKPI_80211_TRACE_MO("hw %p vif %p params %p { %p, %d, %u, %u, %u, %u, %d }",
877 	    hw, vif, params, params->sta, params->action, params->buf_size,
878 	    params->timeout, params->ssn, params->tid, params->amsdu);
879 	error = lhw->ops->ampdu_action(hw, vif, params);
880 
881 out:
882 	return (error);
883 }
884 
885 int
886 lkpi_80211_mo_sta_statistics(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
887     struct ieee80211_sta *sta, struct station_info *sinfo)
888 {
889 	struct lkpi_hw *lhw;
890 	struct lkpi_sta *lsta;
891 	int error;
892 
893 	lhw = HW_TO_LHW(hw);
894 	if (lhw->ops->sta_statistics == NULL) {
895 		error = EOPNOTSUPP;
896 		goto out;
897 	}
898 
899 	lsta = STA_TO_LSTA(sta);
900 	if (!lsta->added_to_drv) {
901 		error = EEXIST;
902 		goto out;
903 	}
904 
905 	lockdep_assert_wiphy(hw->wiphy);
906 
907 	LKPI_80211_TRACE_MO("hw %p vif %p sta %p sinfo %p", hw, vif, sta, sinfo);
908 	lhw->ops->sta_statistics(hw, vif, sta, sinfo);
909 	error = 0;
910 
911 out:
912 	return (error);
913 }
914 
915 int
916 lkpi_80211_mo_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
917 {
918 	struct lkpi_hw *lhw;
919 	int error;
920 
921 	might_sleep();
922 	lockdep_assert_wiphy(hw->wiphy);
923 
924 	lhw = HW_TO_LHW(hw);
925 	if (lhw->ops->suspend == NULL) {
926 		error = EOPNOTSUPP;
927 		goto out;
928 	}
929 
930 	LKPI_80211_TRACE_MO("hw %p wowlan %p", hw, wowlan);
931 	error = lhw->ops->suspend(hw, wowlan);
932 
933 out:
934 	return (error);
935 }
936 
937 int
938 lkpi_80211_mo_resume(struct ieee80211_hw *hw)
939 {
940 	struct lkpi_hw *lhw;
941 	int error;
942 
943 	might_sleep();
944 	lockdep_assert_wiphy(hw->wiphy);
945 
946 	lhw = HW_TO_LHW(hw);
947 	if (lhw->ops->resume == NULL) {
948 		error = EOPNOTSUPP;
949 		goto out;
950 	}
951 
952 	LKPI_80211_TRACE_MO("hw %p", hw);
953 	error = lhw->ops->resume(hw);
954 
955 out:
956 	return (error);
957 }
958 
959 int
960 lkpi_80211_mo_set_wakeup(struct ieee80211_hw *hw, bool enable)
961 {
962 	struct lkpi_hw *lhw;
963 	int error;
964 
965 	might_sleep();
966 	lockdep_assert_wiphy(hw->wiphy);
967 
968 	lhw = HW_TO_LHW(hw);
969 	if (lhw->ops->set_wakeup == NULL) {
970 		error = EOPNOTSUPP;
971 		goto out;
972 	}
973 
974 	LKPI_80211_TRACE_MO("hw %p enable %d", hw, enable);
975 	lhw->ops->set_wakeup(hw, enable);
976 	error = 0;
977 
978 out:
979 	return (error);
980 }
981 
982 int
983 lkpi_80211_mo_set_rekey_data(struct ieee80211_hw *hw,
984     struct ieee80211_vif *vif, struct cfg80211_gtk_rekey_data *grd)
985 {
986 	struct lkpi_hw *lhw;
987 	int error;
988 
989 	might_sleep();
990 	lockdep_assert_wiphy(hw->wiphy);
991 
992 	lhw = HW_TO_LHW(hw);
993 	if (lhw->ops->set_rekey_data == NULL) {
994 		error = EOPNOTSUPP;
995 		goto out;
996 	}
997 
998 	LKPI_80211_TRACE_MO("hw %p vif %p grd %p", hw, vif, grd);
999 	lhw->ops->set_rekey_data(hw, vif, grd);
1000 	error = 0;
1001 
1002 out:
1003 	return (error);
1004 }
1005 
1006 int
1007 lkpi_80211_mo_set_default_unicast_key(struct ieee80211_hw *hw,
1008     struct ieee80211_vif *vif, int idx)
1009 {
1010 	struct lkpi_hw *lhw;
1011 	int error;
1012 
1013 	might_sleep();
1014 	lockdep_assert_wiphy(hw->wiphy);
1015 
1016 	lhw = HW_TO_LHW(hw);
1017 	if (lhw->ops->set_default_unicast_key == NULL) {
1018 		error = EOPNOTSUPP;
1019 		goto out;
1020 	}
1021 
1022 	LKPI_80211_TRACE_MO("hw %p vif %p idx %d", hw, vif, idx);
1023 	lhw->ops->set_default_unicast_key(hw, vif, idx);
1024 	error = 0;
1025 
1026 out:
1027 	return (error);
1028 }
1029 
1030