xref: /freebsd/sys/compat/linuxkpi/common/src/linux_80211_macops.c (revision ccfd87fe2ac0e2e6aeb1911a7d7cce6712a8564f)
1 /*-
2  * Copyright (c) 2021-2022 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/cdefs.h>
30 __FBSDID("$FreeBSD$");
31 
32 #include <sys/param.h>
33 #include <sys/types.h>
34 #include <sys/kernel.h>
35 #include <sys/errno.h>
36 
37 #define	LINUXKPI_NET80211
38 #include <net/mac80211.h>
39 
40 #include "linux_80211.h"
41 
42 /* Could be a different tracing framework later. */
43 #ifdef LINUXKPI_DEBUG_80211
44 #define	LKPI_80211_TRACE_MO(fmt, ...)					\
45     if (linuxkpi_debug_80211 & D80211_TRACE_MO)				\
46 	printf("LKPI_80211_TRACE_MO %s:%d: %d %d %u_" fmt "\n",		\
47 	    __func__, __LINE__, curcpu, curthread->td_tid,		\
48 	    (unsigned int)ticks, __VA_ARGS__)
49 #else
50 #define	LKPI_80211_TRACE_MO(...)	do { } while(0)
51 #endif
52 
53 int
54 lkpi_80211_mo_start(struct ieee80211_hw *hw)
55 {
56 	struct lkpi_hw *lhw;
57 	int error;
58 
59 	lhw = HW_TO_LHW(hw);
60 	if (lhw->ops->start == NULL) {
61 		error = EOPNOTSUPP;
62 		goto out;
63 	}
64 
65 	if ((lhw->sc_flags & LKPI_MAC80211_DRV_STARTED)) {
66 		/* Trying to start twice is an error. */
67 		error = EEXIST;
68 		goto out;
69 	}
70 	LKPI_80211_TRACE_MO("hw %p", hw);
71 	error = lhw->ops->start(hw);
72 	if (error == 0)
73 		lhw->sc_flags |= LKPI_MAC80211_DRV_STARTED;
74 
75 out:
76 	return (error);
77 }
78 
79 void
80 lkpi_80211_mo_stop(struct ieee80211_hw *hw)
81 {
82 	struct lkpi_hw *lhw;
83 
84 	lhw = HW_TO_LHW(hw);
85 	if (lhw->ops->stop == NULL)
86 		return;
87 
88 	LKPI_80211_TRACE_MO("hw %p", hw);
89 	lhw->ops->stop(hw);
90 	lhw->sc_flags &= ~LKPI_MAC80211_DRV_STARTED;
91 }
92 
93 int
94 lkpi_80211_mo_get_antenna(struct ieee80211_hw *hw, u32 *txs, u32 *rxs)
95 {
96 	struct lkpi_hw *lhw;
97 	int error;
98 
99 	lhw = HW_TO_LHW(hw);
100 	if (lhw->ops->get_antenna == NULL) {
101 		error = EOPNOTSUPP;
102 		goto out;
103 	}
104 
105 	LKPI_80211_TRACE_MO("hw %p", hw);
106 	error = lhw->ops->get_antenna(hw, 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 	error = lhw->ops->set_frag_threshold(hw, frag_th);
126 
127 out:
128 	return (error);
129 }
130 
131 int
132 lkpi_80211_mo_set_rts_threshold(struct ieee80211_hw *hw, uint32_t rts_th)
133 {
134 	struct lkpi_hw *lhw;
135 	int error;
136 
137 	lhw = HW_TO_LHW(hw);
138 	if (lhw->ops->set_rts_threshold == NULL) {
139 		error = EOPNOTSUPP;
140 		goto out;
141 	}
142 
143 	LKPI_80211_TRACE_MO("hw %p rts_th %u", hw, rts_th);
144 	error = lhw->ops->set_rts_threshold(hw, rts_th);
145 
146 out:
147 	return (error);
148 }
149 
150 
151 int
152 lkpi_80211_mo_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
153 {
154 	struct lkpi_hw *lhw;
155 	struct lkpi_vif *lvif;
156 	int error;
157 
158 	lhw = HW_TO_LHW(hw);
159 	if (lhw->ops->add_interface == NULL) {
160 		error = EOPNOTSUPP;
161 		goto out;
162 	}
163 
164 	lvif = VIF_TO_LVIF(vif);
165 	LKPI_80211_LVIF_LOCK(lvif);
166 	if (lvif->added_to_drv) {
167 		LKPI_80211_LVIF_UNLOCK(lvif);
168 		/* Trying to add twice is an error. */
169 		error = EEXIST;
170 		goto out;
171 	}
172 	LKPI_80211_LVIF_UNLOCK(lvif);
173 
174 	LKPI_80211_TRACE_MO("hw %p vif %p", hw, vif);
175 	error = lhw->ops->add_interface(hw, vif);
176 	if (error == 0) {
177 		LKPI_80211_LVIF_LOCK(lvif);
178 		lvif->added_to_drv = true;
179 		LKPI_80211_LVIF_UNLOCK(lvif);
180 	}
181 
182 out:
183 	return (error);
184 }
185 
186 void
187 lkpi_80211_mo_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
188 {
189 	struct lkpi_hw *lhw;
190 	struct lkpi_vif *lvif;
191 
192 	lhw = HW_TO_LHW(hw);
193 	if (lhw->ops->remove_interface == NULL)
194 		return;
195 
196 	lvif = VIF_TO_LVIF(vif);
197 	LKPI_80211_LVIF_LOCK(lvif);
198 	if (!lvif->added_to_drv) {
199 		LKPI_80211_LVIF_UNLOCK(lvif);
200 		return;
201 	}
202 	LKPI_80211_LVIF_UNLOCK(lvif);
203 
204 	LKPI_80211_TRACE_MO("hw %p vif %p", hw, vif);
205 	lhw->ops->remove_interface(hw, vif);
206 	LKPI_80211_LVIF_LOCK(lvif);
207 	lvif->added_to_drv = false;
208 	LKPI_80211_LVIF_UNLOCK(lvif);
209 }
210 
211 
212 int
213 lkpi_80211_mo_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
214     struct ieee80211_scan_request *sr)
215 {
216 	struct lkpi_hw *lhw;
217 	int error;
218 
219 	/*
220 	 * MUST NOT return EPERM as that is a "magic number 1" based on rtw88
221 	 * driver indicating hw_scan is not supported despite the ops call
222 	 * being available.
223 	 */
224 
225 	lhw = HW_TO_LHW(hw);
226 	if (lhw->ops->hw_scan == NULL) {
227 		/* Return magic number to use sw scan. */
228 		error = 1;
229 		goto out;
230 	}
231 
232 	LKPI_80211_TRACE_MO("CALLING hw %p vif %p sr %p", hw, vif, sr);
233 	error = lhw->ops->hw_scan(hw, vif, sr);
234 	LKPI_80211_TRACE_MO("RETURNING hw %p vif %p sr %p error %d", hw, vif, sr, error);
235 
236 out:
237 	return (error);
238 }
239 
240 void
241 lkpi_80211_mo_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
242 {
243 	struct lkpi_hw *lhw;
244 
245 	lhw = HW_TO_LHW(hw);
246 	if (lhw->ops->cancel_hw_scan == NULL)
247 		return;
248 
249 	LKPI_80211_TRACE_MO("hw %p vif %p", hw, vif);
250 	lhw->ops->cancel_hw_scan(hw, vif);
251 }
252 
253 void
254 lkpi_80211_mo_sw_scan_complete(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
255 {
256 	struct lkpi_hw *lhw;
257 
258 	lhw = HW_TO_LHW(hw);
259 	if (lhw->ops->sw_scan_complete == NULL)
260 		return;
261 
262 	LKPI_80211_TRACE_MO("hw %p vif %p", hw, vif);
263 	lhw->ops->sw_scan_complete(hw, vif);
264 	lhw->scan_flags &= ~LKPI_LHW_SCAN_RUNNING;
265 }
266 
267 void
268 lkpi_80211_mo_sw_scan_start(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
269     const u8 *addr)
270 {
271 	struct lkpi_hw *lhw;
272 
273 	lhw = HW_TO_LHW(hw);
274 	if (lhw->ops->sw_scan_start == NULL)
275 		return;
276 
277 	LKPI_80211_TRACE_MO("hw %p vif %p", hw, vif);
278 	lhw->ops->sw_scan_start(hw, vif, addr);
279 }
280 
281 
282 /*
283  * We keep the Linux type here;  it really is an uintptr_t.
284  */
285 u64
286 lkpi_80211_mo_prepare_multicast(struct ieee80211_hw *hw,
287     struct netdev_hw_addr_list *mc_list)
288 {
289 	struct lkpi_hw *lhw;
290 	u64 ptr;
291 
292 	lhw = HW_TO_LHW(hw);
293 	if (lhw->ops->prepare_multicast == NULL)
294 		return (0);
295 
296 	LKPI_80211_TRACE_MO("hw %p mc_list %p", hw, mc_list);
297 	ptr = lhw->ops->prepare_multicast(hw, mc_list);
298 	return (ptr);
299 }
300 
301 void
302 lkpi_80211_mo_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags,
303     unsigned int *total_flags, u64 mc_ptr)
304 {
305 	struct lkpi_hw *lhw;
306 
307 	lhw = HW_TO_LHW(hw);
308 	if (lhw->ops->configure_filter == NULL)
309 		return;
310 
311 	if (mc_ptr == 0)
312 		return;
313 
314 	LKPI_80211_TRACE_MO("hw %p changed_flags %#x total_flags %p mc_ptr %ju", hw, changed_flags, total_flags, (uintmax_t)mc_ptr);
315 	lhw->ops->configure_filter(hw, changed_flags, total_flags, mc_ptr);
316 }
317 
318 
319 /*
320  * So far we only called sta_{add,remove} as an alternative to sta_state.
321  * Let's keep the implementation simpler and hide sta_{add,remove} under the
322  * hood here calling them if state_state is not available from mo_sta_state.
323  */
324 static int
325 lkpi_80211_mo_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
326     struct ieee80211_sta *sta)
327 {
328 	struct lkpi_hw *lhw;
329 	struct lkpi_sta *lsta;
330 	int error;
331 
332 	lhw = HW_TO_LHW(hw);
333 	if (lhw->ops->sta_add == NULL) {
334 		error = EOPNOTSUPP;
335 		goto out;
336 	}
337 
338 	lsta = STA_TO_LSTA(sta);
339 	if (lsta->added_to_drv) {
340 		error = EEXIST;
341 		goto out;
342 	}
343 
344 	LKPI_80211_TRACE_MO("hw %p vif %p sta %p", hw, vif, sta);
345 	error = lhw->ops->sta_add(hw, vif, sta);
346 	if (error == 0)
347 		lsta->added_to_drv = true;
348 
349 out:
350 	return error;
351 }
352 
353 static int
354 lkpi_80211_mo_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
355     struct ieee80211_sta *sta)
356 {
357 	struct lkpi_hw *lhw;
358 	struct lkpi_sta *lsta;
359 	int error;
360 
361 	lhw = HW_TO_LHW(hw);
362 	if (lhw->ops->sta_remove == NULL) {
363 		error = EOPNOTSUPP;
364 		goto out;
365 	}
366 
367 	lsta = STA_TO_LSTA(sta);
368 	if (!lsta->added_to_drv) {
369 		/* If we never added the sta, do not complain on cleanup. */
370 		error = 0;
371 		goto out;
372 	}
373 
374 	LKPI_80211_TRACE_MO("hw %p vif %p sta %p", hw, vif, sta);
375 	error = lhw->ops->sta_remove(hw, vif, sta);
376 	if (error == 0)
377 		lsta->added_to_drv = false;
378 
379 out:
380 	return error;
381 }
382 
383 int
384 lkpi_80211_mo_sta_state(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
385     struct lkpi_sta *lsta, enum ieee80211_sta_state nstate)
386 {
387 	struct lkpi_hw *lhw;
388 	struct ieee80211_sta *sta;
389 	int error;
390 
391 	lhw = HW_TO_LHW(hw);
392 	sta = LSTA_TO_STA(lsta);
393 	if (lhw->ops->sta_state != NULL) {
394 		LKPI_80211_TRACE_MO("hw %p vif %p sta %p nstate %d", hw, vif, sta, nstate);
395 		error = lhw->ops->sta_state(hw, vif, sta, lsta->state, nstate);
396 		if (error == 0) {
397 			if (nstate == IEEE80211_STA_NOTEXIST)
398 				lsta->added_to_drv = false;
399 			else
400 				lsta->added_to_drv = true;
401 			lsta->state = nstate;
402 		}
403 		goto out;
404 	}
405 
406 	/* XXX-BZ is the change state AUTH or ASSOC here? */
407 	if (lsta->state < IEEE80211_STA_ASSOC && nstate == IEEE80211_STA_ASSOC) {
408 		error = lkpi_80211_mo_sta_add(hw, vif, sta);
409 		if (error == 0)
410 			lsta->added_to_drv = true;
411 	} else if (lsta->state >= IEEE80211_STA_ASSOC &&
412 	    nstate < IEEE80211_STA_ASSOC) {
413 		error = lkpi_80211_mo_sta_remove(hw, vif, sta);
414 		if (error == 0)
415 			lsta->added_to_drv = false;
416 	} else
417 		/* Nothing to do. */
418 		error = 0;
419 	if (error == 0)
420 		lsta->state = nstate;
421 
422 out:
423 	/* XXX-BZ should we manage state in here? */
424 	return (error);
425 }
426 
427 int
428 lkpi_80211_mo_config(struct ieee80211_hw *hw, uint32_t changed)
429 {
430 	struct lkpi_hw *lhw;
431 	int error;
432 
433 	lhw = HW_TO_LHW(hw);
434 	if (lhw->ops->config == NULL) {
435 		error = EOPNOTSUPP;
436 		goto out;
437 	}
438 
439 	LKPI_80211_TRACE_MO("hw %p changed %u", hw, changed);
440 	error = lhw->ops->config(hw, changed);
441 
442 out:
443 	return (error);
444 }
445 
446 
447 int
448 lkpi_80211_mo_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
449     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 chanctx_conf %p", hw, vif, chanctx_conf);
461 	error = lhw->ops->assign_vif_chanctx(hw, vif, NULL, chanctx_conf);
462 	if (error == 0)
463 		vif->chanctx_conf = chanctx_conf;
464 
465 out:
466 	return (error);
467 }
468 
469 void
470 lkpi_80211_mo_unassign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
471     struct ieee80211_chanctx_conf **chanctx_conf)
472 {
473 	struct lkpi_hw *lhw;
474 
475 	lhw = HW_TO_LHW(hw);
476 	if (lhw->ops->unassign_vif_chanctx == NULL)
477 		return;
478 
479 	if (*chanctx_conf == NULL)
480 		return;
481 
482 	LKPI_80211_TRACE_MO("hw %p vif %p chanctx_conf %p", hw, vif, *chanctx_conf);
483 	lhw->ops->unassign_vif_chanctx(hw, vif, NULL, *chanctx_conf);
484 	*chanctx_conf = NULL;
485 }
486 
487 
488 int
489 lkpi_80211_mo_add_chanctx(struct ieee80211_hw *hw,
490     struct ieee80211_chanctx_conf *chanctx_conf)
491 {
492 	struct lkpi_hw *lhw;
493 	int error;
494 
495 	lhw = HW_TO_LHW(hw);
496 	if (lhw->ops->add_chanctx == NULL) {
497 		error = EOPNOTSUPP;
498 		goto out;
499 	}
500 
501 	LKPI_80211_TRACE_MO("hw %p chanctx_conf %p", hw, chanctx_conf);
502 	error = lhw->ops->add_chanctx(hw, chanctx_conf);
503 
504 out:
505 	return (error);
506 }
507 
508 void
509 lkpi_80211_mo_change_chanctx(struct ieee80211_hw *hw,
510     struct ieee80211_chanctx_conf *chanctx_conf, uint32_t changed)
511 {
512 	struct lkpi_hw *lhw;
513 
514 	lhw = HW_TO_LHW(hw);
515 	if (lhw->ops->change_chanctx == NULL)
516 		return;
517 
518 	LKPI_80211_TRACE_MO("hw %p chanctx_conf %p changed %u", hw, chanctx_conf, changed);
519 	lhw->ops->change_chanctx(hw, chanctx_conf, changed);
520 }
521 
522 void
523 lkpi_80211_mo_remove_chanctx(struct ieee80211_hw *hw,
524     struct ieee80211_chanctx_conf *chanctx_conf)
525 {
526 	struct lkpi_hw *lhw;
527 
528 	lhw = HW_TO_LHW(hw);
529 	if (lhw->ops->remove_chanctx == NULL)
530 		return;
531 
532 	LKPI_80211_TRACE_MO("hw %p chanctx_conf %p", hw, chanctx_conf);
533 	lhw->ops->remove_chanctx(hw, chanctx_conf);
534 }
535 
536 void
537 lkpi_80211_mo_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
538     struct ieee80211_bss_conf *conf, uint64_t changed)
539 {
540 	struct lkpi_hw *lhw;
541 
542 	lhw = HW_TO_LHW(hw);
543 	if (lhw->ops->bss_info_changed == NULL)
544 		return;
545 
546 	LKPI_80211_TRACE_MO("hw %p vif %p conf %p changed %#jx", hw, vif, conf, (uintmax_t)changed);
547 	lhw->ops->bss_info_changed(hw, vif, conf, changed);
548 }
549 
550 
551 int
552 lkpi_80211_mo_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
553     uint16_t ac, const struct ieee80211_tx_queue_params *txqp)
554 {
555 	struct lkpi_hw *lhw;
556 	int error;
557 
558 	lhw = HW_TO_LHW(hw);
559 	if (lhw->ops->conf_tx == NULL) {
560 		error = EOPNOTSUPP;
561 		goto out;
562 	}
563 
564 	LKPI_80211_TRACE_MO("hw %p vif %p ac %u txpq %p", hw, vif, ac, txqp);
565 	error = lhw->ops->conf_tx(hw, vif, 0, ac, txqp);
566 
567 out:
568 	return (error);
569 }
570 
571 void
572 lkpi_80211_mo_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
573     uint32_t nqueues, bool drop)
574 {
575 	struct lkpi_hw *lhw;
576 
577 	lhw = HW_TO_LHW(hw);
578 	if (lhw->ops->flush == NULL)
579 		return;
580 
581 	LKPI_80211_TRACE_MO("hw %p vif %p nqueues %u drop %d", hw, vif, nqueues, drop);
582 	lhw->ops->flush(hw, vif, nqueues, drop);
583 }
584 
585 void
586 lkpi_80211_mo_mgd_prepare_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
587     struct ieee80211_prep_tx_info *txinfo)
588 {
589 	struct lkpi_hw *lhw;
590 
591 	lhw = HW_TO_LHW(hw);
592 	if (lhw->ops->mgd_prepare_tx == NULL)
593 		return;
594 
595 	LKPI_80211_TRACE_MO("hw %p vif %p txinfo %p", hw, vif, txinfo);
596 	lhw->ops->mgd_prepare_tx(hw, vif, txinfo);
597 }
598 
599 void
600 lkpi_80211_mo_mgd_complete_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
601     struct ieee80211_prep_tx_info *txinfo)
602 {
603 	struct lkpi_hw *lhw;
604 
605 	lhw = HW_TO_LHW(hw);
606 	if (lhw->ops->mgd_complete_tx == NULL)
607 		return;
608 
609 	LKPI_80211_TRACE_MO("hw %p vif %p txinfo %p", hw, vif, txinfo);
610 	lhw->ops->mgd_complete_tx(hw, vif, txinfo);
611 }
612 
613 void
614 lkpi_80211_mo_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *txctrl,
615     struct sk_buff *skb)
616 {
617 	struct lkpi_hw *lhw;
618 
619 	lhw = HW_TO_LHW(hw);
620 	if (lhw->ops->tx == NULL)
621 		return;
622 
623 	LKPI_80211_TRACE_MO("hw %p txctrl %p skb %p", hw, txctrl, skb);
624 	lhw->ops->tx(hw, txctrl, skb);
625 }
626 
627 void
628 lkpi_80211_mo_wake_tx_queue(struct ieee80211_hw *hw, struct ieee80211_txq *txq)
629 {
630 	struct lkpi_hw *lhw;
631 
632 	lhw = HW_TO_LHW(hw);
633 	if (lhw->ops->wake_tx_queue == NULL)
634 		return;
635 
636 	LKPI_80211_TRACE_MO("hw %p txq %p", hw, txq);
637 	lhw->ops->wake_tx_queue(hw, txq);
638 }
639 
640 void
641 lkpi_80211_mo_sync_rx_queues(struct ieee80211_hw *hw)
642 {
643 	struct lkpi_hw *lhw;
644 
645 	lhw = HW_TO_LHW(hw);
646 	if (lhw->ops->sync_rx_queues == NULL)
647 		return;
648 
649 	LKPI_80211_TRACE_MO("hw %p", hw);
650 	lhw->ops->sync_rx_queues(hw);
651 }
652 
653 void
654 lkpi_80211_mo_sta_pre_rcu_remove(struct ieee80211_hw *hw,
655     struct ieee80211_vif *vif, struct ieee80211_sta *sta)
656 {
657 	struct lkpi_hw *lhw;
658 
659 	lhw = HW_TO_LHW(hw);
660 	if (lhw->ops->sta_pre_rcu_remove == NULL)
661 		return;
662 
663 	LKPI_80211_TRACE_MO("hw %p vif %p sta %p", hw, vif, sta);
664 	lhw->ops->sta_pre_rcu_remove(hw, vif, sta);
665 }
666 
667 int
668 lkpi_80211_mo_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
669     struct ieee80211_vif *vif, struct ieee80211_sta *sta,
670     struct ieee80211_key_conf *kc)
671 {
672 	struct lkpi_hw *lhw;
673 	int error;
674 
675 	lhw = HW_TO_LHW(hw);
676 	if (lhw->ops->set_key == NULL) {
677 		error = EOPNOTSUPP;
678 		goto out;
679 	}
680 
681 	LKPI_80211_TRACE_MO("hw %p cmd %d vif %p sta %p kc %p", hw, cmd, vif, sta, kc);
682 	error = lhw->ops->set_key(hw, cmd, vif, sta, kc);
683 
684 out:
685 	return (error);
686 }
687