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