xref: /freebsd/sys/compat/linuxkpi/common/src/linux_80211_macops.c (revision 592ae60e2b2eff6c2ec467c34be9a457ce24b044)
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 	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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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