xref: /freebsd/sys/compat/linuxkpi/common/src/linux_80211_macops.c (revision f943454bfbd98ce49516c6ede8626b5f2602edc1)
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/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 	/*
222 	 * MUST NOT return EPERM as that is a "magic number 1" based on rtw88
223 	 * driver indicating hw_scan is not supported despite the ops call
224 	 * being available.
225 	 */
226 
227 	lhw = HW_TO_LHW(hw);
228 	if (lhw->ops->hw_scan == NULL) {
229 		/* Return magic number to use sw scan. */
230 		error = 1;
231 		goto out;
232 	}
233 
234 	LKPI_80211_TRACE_MO("CALLING hw %p vif %p sr %p", hw, vif, sr);
235 	error = lhw->ops->hw_scan(hw, vif, sr);
236 	LKPI_80211_TRACE_MO("RETURNING hw %p vif %p sr %p error %d", hw, vif, sr, error);
237 
238 out:
239 	return (error);
240 }
241 
242 void
243 lkpi_80211_mo_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
244 {
245 	struct lkpi_hw *lhw;
246 
247 	lhw = HW_TO_LHW(hw);
248 	if (lhw->ops->cancel_hw_scan == NULL)
249 		return;
250 
251 	LKPI_80211_TRACE_MO("hw %p vif %p", hw, vif);
252 	lhw->ops->cancel_hw_scan(hw, vif);
253 }
254 
255 void
256 lkpi_80211_mo_sw_scan_complete(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
257 {
258 	struct lkpi_hw *lhw;
259 
260 	lhw = HW_TO_LHW(hw);
261 	if (lhw->ops->sw_scan_complete == NULL)
262 		return;
263 
264 	LKPI_80211_TRACE_MO("hw %p vif %p", hw, vif);
265 	lhw->ops->sw_scan_complete(hw, vif);
266 	lhw->scan_flags &= ~LKPI_LHW_SCAN_RUNNING;
267 }
268 
269 void
270 lkpi_80211_mo_sw_scan_start(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
271     const u8 *addr)
272 {
273 	struct lkpi_hw *lhw;
274 
275 	lhw = HW_TO_LHW(hw);
276 	if (lhw->ops->sw_scan_start == NULL)
277 		return;
278 
279 	LKPI_80211_TRACE_MO("hw %p vif %p", hw, vif);
280 	lhw->ops->sw_scan_start(hw, vif, addr);
281 }
282 
283 
284 /*
285  * We keep the Linux type here;  it really is an uintptr_t.
286  */
287 u64
288 lkpi_80211_mo_prepare_multicast(struct ieee80211_hw *hw,
289     struct netdev_hw_addr_list *mc_list)
290 {
291 	struct lkpi_hw *lhw;
292 	u64 ptr;
293 
294 	lhw = HW_TO_LHW(hw);
295 	if (lhw->ops->prepare_multicast == NULL)
296 		return (0);
297 
298 	LKPI_80211_TRACE_MO("hw %p mc_list %p", hw, mc_list);
299 	ptr = lhw->ops->prepare_multicast(hw, mc_list);
300 	return (ptr);
301 }
302 
303 void
304 lkpi_80211_mo_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags,
305     unsigned int *total_flags, u64 mc_ptr)
306 {
307 	struct lkpi_hw *lhw;
308 
309 	lhw = HW_TO_LHW(hw);
310 	if (lhw->ops->configure_filter == NULL)
311 		return;
312 
313 	LKPI_80211_TRACE_MO("hw %p changed_flags %#x total_flags %p mc_ptr %ju", hw, changed_flags, total_flags, (uintmax_t)mc_ptr);
314 	lhw->ops->configure_filter(hw, changed_flags, total_flags, mc_ptr);
315 }
316 
317 
318 /*
319  * So far we only called sta_{add,remove} as an alternative to sta_state.
320  * Let's keep the implementation simpler and hide sta_{add,remove} under the
321  * hood here calling them if state_state is not available from mo_sta_state.
322  */
323 static int
324 lkpi_80211_mo_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
325     struct ieee80211_sta *sta)
326 {
327 	struct lkpi_hw *lhw;
328 	struct lkpi_sta *lsta;
329 	int error;
330 
331 	lhw = HW_TO_LHW(hw);
332 	if (lhw->ops->sta_add == NULL) {
333 		error = EOPNOTSUPP;
334 		goto out;
335 	}
336 
337 	lsta = STA_TO_LSTA(sta);
338 	if (lsta->added_to_drv) {
339 		error = EEXIST;
340 		goto out;
341 	}
342 
343 	LKPI_80211_TRACE_MO("hw %p vif %p sta %p", hw, vif, sta);
344 	error = lhw->ops->sta_add(hw, vif, sta);
345 	if (error == 0)
346 		lsta->added_to_drv = true;
347 
348 out:
349 	return error;
350 }
351 
352 static int
353 lkpi_80211_mo_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
354     struct ieee80211_sta *sta)
355 {
356 	struct lkpi_hw *lhw;
357 	struct lkpi_sta *lsta;
358 	int error;
359 
360 	lhw = HW_TO_LHW(hw);
361 	if (lhw->ops->sta_remove == NULL) {
362 		error = EOPNOTSUPP;
363 		goto out;
364 	}
365 
366 	lsta = STA_TO_LSTA(sta);
367 	if (!lsta->added_to_drv) {
368 		/* If we never added the sta, do not complain on cleanup. */
369 		error = 0;
370 		goto out;
371 	}
372 
373 	LKPI_80211_TRACE_MO("hw %p vif %p sta %p", hw, vif, sta);
374 	error = lhw->ops->sta_remove(hw, vif, sta);
375 	if (error == 0)
376 		lsta->added_to_drv = false;
377 
378 out:
379 	return error;
380 }
381 
382 int
383 lkpi_80211_mo_sta_state(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
384     struct lkpi_sta *lsta, enum ieee80211_sta_state nstate)
385 {
386 	struct lkpi_hw *lhw;
387 	struct ieee80211_sta *sta;
388 	int error;
389 
390 	lhw = HW_TO_LHW(hw);
391 	sta = LSTA_TO_STA(lsta);
392 	if (lhw->ops->sta_state != NULL) {
393 		LKPI_80211_TRACE_MO("hw %p vif %p sta %p nstate %d", hw, vif, sta, nstate);
394 		error = lhw->ops->sta_state(hw, vif, sta, lsta->state, nstate);
395 		if (error == 0) {
396 			if (nstate == IEEE80211_STA_NOTEXIST)
397 				lsta->added_to_drv = false;
398 			else
399 				lsta->added_to_drv = true;
400 			lsta->state = nstate;
401 		}
402 		goto out;
403 	}
404 
405 	/* XXX-BZ is the change state AUTH or ASSOC here? */
406 	if (lsta->state < IEEE80211_STA_ASSOC && nstate == IEEE80211_STA_ASSOC) {
407 		error = lkpi_80211_mo_sta_add(hw, vif, sta);
408 		if (error == 0)
409 			lsta->added_to_drv = true;
410 	} else if (lsta->state >= IEEE80211_STA_ASSOC &&
411 	    nstate < IEEE80211_STA_ASSOC) {
412 		error = lkpi_80211_mo_sta_remove(hw, vif, sta);
413 		if (error == 0)
414 			lsta->added_to_drv = false;
415 	} else
416 		/* Nothing to do. */
417 		error = 0;
418 	if (error == 0)
419 		lsta->state = nstate;
420 
421 out:
422 	/* XXX-BZ should we manage state in here? */
423 	return (error);
424 }
425 
426 int
427 lkpi_80211_mo_config(struct ieee80211_hw *hw, uint32_t changed)
428 {
429 	struct lkpi_hw *lhw;
430 	int error;
431 
432 	lhw = HW_TO_LHW(hw);
433 	if (lhw->ops->config == NULL) {
434 		error = EOPNOTSUPP;
435 		goto out;
436 	}
437 
438 	LKPI_80211_TRACE_MO("hw %p changed %u", hw, changed);
439 	LKPI_80211_TRACE_MO("TODO link/radio_idx");
440 	error = lhw->ops->config(hw, 0, changed);
441 
442 out:
443 	return (error);
444 }
445 
446 
447 int
448 lkpi_80211_mo_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
449     struct ieee80211_bss_conf *conf, struct ieee80211_chanctx_conf *chanctx_conf)
450 {
451 	struct lkpi_hw *lhw;
452 	int error;
453 
454 	lhw = HW_TO_LHW(hw);
455 	if (lhw->ops->assign_vif_chanctx == NULL) {
456 		error = EOPNOTSUPP;
457 		goto out;
458 	}
459 
460 	LKPI_80211_TRACE_MO("hw %p vif %p bss_conf %p chanctx_conf %p",
461 	    hw, vif, conf, chanctx_conf);
462 	error = lhw->ops->assign_vif_chanctx(hw, vif, conf, chanctx_conf);
463 	if (error == 0)
464 		vif->bss_conf.chanctx_conf = chanctx_conf;
465 
466 out:
467 	return (error);
468 }
469 
470 void
471 lkpi_80211_mo_unassign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
472     struct ieee80211_bss_conf *conf, struct ieee80211_chanctx_conf *chanctx_conf)
473 {
474 	struct lkpi_hw *lhw;
475 
476 	might_sleep();
477 	lockdep_assert_wiphy(hw->wiphy);
478 
479 	lhw = HW_TO_LHW(hw);
480 	if (lhw->ops->unassign_vif_chanctx == NULL)
481 		return;
482 
483 	if (chanctx_conf == NULL)
484 		return;
485 
486 	LKPI_80211_TRACE_MO("hw %p vif %p bss_conf %p chanctx_conf %p",
487 	    hw, vif, conf, chanctx_conf);
488 	lhw->ops->unassign_vif_chanctx(hw, vif, conf, chanctx_conf);
489 }
490 
491 
492 int
493 lkpi_80211_mo_add_chanctx(struct ieee80211_hw *hw,
494     struct ieee80211_chanctx_conf *chanctx_conf)
495 {
496 	struct lkpi_hw *lhw;
497 	struct lkpi_chanctx *lchanctx;
498 	int error;
499 
500 	lhw = HW_TO_LHW(hw);
501 	if (lhw->ops->add_chanctx == NULL) {
502 		error = EOPNOTSUPP;
503 		goto out;
504 	}
505 
506 	LKPI_80211_TRACE_MO("hw %p chanctx_conf %p", hw, chanctx_conf);
507 	error = lhw->ops->add_chanctx(hw, chanctx_conf);
508 	if (error == 0) {
509 		lchanctx = CHANCTX_CONF_TO_LCHANCTX(chanctx_conf);
510 		lchanctx->added_to_drv = true;
511 	}
512 
513 out:
514 	return (error);
515 }
516 
517 void
518 lkpi_80211_mo_change_chanctx(struct ieee80211_hw *hw,
519     struct ieee80211_chanctx_conf *chanctx_conf, uint32_t changed)
520 {
521 	struct lkpi_hw *lhw;
522 
523 	lhw = HW_TO_LHW(hw);
524 	if (lhw->ops->change_chanctx == NULL)
525 		return;
526 
527 	LKPI_80211_TRACE_MO("hw %p chanctx_conf %p changed %u", hw, chanctx_conf, changed);
528 	lhw->ops->change_chanctx(hw, chanctx_conf, changed);
529 }
530 
531 void
532 lkpi_80211_mo_remove_chanctx(struct ieee80211_hw *hw,
533     struct ieee80211_chanctx_conf *chanctx_conf)
534 {
535 	struct lkpi_hw *lhw;
536 	struct lkpi_chanctx *lchanctx;
537 
538 	lhw = HW_TO_LHW(hw);
539 	if (lhw->ops->remove_chanctx == NULL)
540 		return;
541 
542 	LKPI_80211_TRACE_MO("hw %p chanctx_conf %p", hw, chanctx_conf);
543 	lhw->ops->remove_chanctx(hw, chanctx_conf);
544 	lchanctx = CHANCTX_CONF_TO_LCHANCTX(chanctx_conf);
545 	lchanctx->added_to_drv = false;
546 }
547 
548 void
549 lkpi_80211_mo_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
550     struct ieee80211_bss_conf *conf, uint64_t changed)
551 {
552 	struct lkpi_hw *lhw;
553 
554 	lhw = HW_TO_LHW(hw);
555 	if (lhw->ops->link_info_changed == NULL &&
556 	    lhw->ops->bss_info_changed == NULL)
557 		return;
558 
559 	if (changed == 0)
560 		return;
561 
562 	LKPI_80211_TRACE_MO("hw %p vif %p conf %p changed %#jx", hw, vif, conf, (uintmax_t)changed);
563 	if (lhw->ops->link_info_changed != NULL)
564 		lhw->ops->link_info_changed(hw, vif, conf, changed);
565 	else
566 		lhw->ops->bss_info_changed(hw, vif, conf, changed);
567 }
568 
569 int
570 lkpi_80211_mo_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
571     uint32_t link_id, uint16_t ac, const struct ieee80211_tx_queue_params *txqp)
572 {
573 	struct lkpi_hw *lhw;
574 	int error;
575 
576 	lhw = HW_TO_LHW(hw);
577 	if (lhw->ops->conf_tx == NULL) {
578 		error = EOPNOTSUPP;
579 		goto out;
580 	}
581 
582 	LKPI_80211_TRACE_MO("hw %p vif %p link_id %u ac %u txpq %p",
583 	    hw, vif, link_id, ac, txqp);
584 	error = lhw->ops->conf_tx(hw, vif, link_id, ac, txqp);
585 
586 out:
587 	return (error);
588 }
589 
590 void
591 lkpi_80211_mo_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
592     uint32_t nqueues, bool drop)
593 {
594 	struct lkpi_hw *lhw;
595 
596 	lhw = HW_TO_LHW(hw);
597 	if (lhw->ops->flush == NULL)
598 		return;
599 
600 	LKPI_80211_TRACE_MO("hw %p vif %p nqueues %u drop %d", hw, vif, nqueues, drop);
601 	lhw->ops->flush(hw, vif, nqueues, drop);
602 }
603 
604 void
605 lkpi_80211_mo_mgd_prepare_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
606     struct ieee80211_prep_tx_info *txinfo)
607 {
608 	struct lkpi_hw *lhw;
609 
610 	lhw = HW_TO_LHW(hw);
611 	if (lhw->ops->mgd_prepare_tx == NULL)
612 		return;
613 
614 	LKPI_80211_TRACE_MO("hw %p vif %p txinfo %p", hw, vif, txinfo);
615 	lhw->ops->mgd_prepare_tx(hw, vif, txinfo);
616 }
617 
618 void
619 lkpi_80211_mo_mgd_complete_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
620     struct ieee80211_prep_tx_info *txinfo)
621 {
622 	struct lkpi_hw *lhw;
623 
624 	lhw = HW_TO_LHW(hw);
625 	if (lhw->ops->mgd_complete_tx == NULL)
626 		return;
627 
628 	LKPI_80211_TRACE_MO("hw %p vif %p txinfo %p", hw, vif, txinfo);
629 	lhw->ops->mgd_complete_tx(hw, vif, txinfo);
630 }
631 
632 void
633 lkpi_80211_mo_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *txctrl,
634     struct sk_buff *skb)
635 {
636 	struct lkpi_hw *lhw;
637 
638 	lhw = HW_TO_LHW(hw);
639 	if (lhw->ops->tx == NULL)
640 		return;
641 
642 	LKPI_80211_TRACE_MO("hw %p txctrl %p skb %p", hw, txctrl, skb);
643 	lhw->ops->tx(hw, txctrl, skb);
644 }
645 
646 void
647 lkpi_80211_mo_wake_tx_queue(struct ieee80211_hw *hw, struct ieee80211_txq *txq)
648 {
649 	struct lkpi_hw *lhw;
650 
651 	lhw = HW_TO_LHW(hw);
652 	if (lhw->ops->wake_tx_queue == NULL)
653 		return;
654 
655 	LKPI_80211_TRACE_MO("hw %p txq %p", hw, txq);
656 	lhw->ops->wake_tx_queue(hw, txq);
657 }
658 
659 void
660 lkpi_80211_mo_sync_rx_queues(struct ieee80211_hw *hw)
661 {
662 	struct lkpi_hw *lhw;
663 
664 	lhw = HW_TO_LHW(hw);
665 	if (lhw->ops->sync_rx_queues == NULL)
666 		return;
667 
668 	LKPI_80211_TRACE_MO("hw %p", hw);
669 	lhw->ops->sync_rx_queues(hw);
670 }
671 
672 void
673 lkpi_80211_mo_sta_pre_rcu_remove(struct ieee80211_hw *hw,
674     struct ieee80211_vif *vif, struct ieee80211_sta *sta)
675 {
676 	struct lkpi_hw *lhw;
677 
678 	lhw = HW_TO_LHW(hw);
679 	if (lhw->ops->sta_pre_rcu_remove == NULL)
680 		return;
681 
682 	LKPI_80211_TRACE_MO("hw %p vif %p sta %p", hw, vif, sta);
683 	lhw->ops->sta_pre_rcu_remove(hw, vif, sta);
684 }
685 
686 int
687 lkpi_80211_mo_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
688     struct ieee80211_vif *vif, struct ieee80211_sta *sta,
689     struct ieee80211_key_conf *kc)
690 {
691 	struct lkpi_hw *lhw;
692 	int error;
693 
694 	lockdep_assert_wiphy(hw->wiphy);
695 
696 	lhw = HW_TO_LHW(hw);
697 	if (lhw->ops->set_key == NULL) {
698 		error = EOPNOTSUPP;
699 		goto out;
700 	}
701 
702 	LKPI_80211_TRACE_MO("hw %p cmd %d vif %p sta %p kc %p", hw, cmd, vif, sta, kc);
703 	error = lhw->ops->set_key(hw, cmd, vif, sta, kc);
704 
705 out:
706 	return (error);
707 }
708 
709 int
710 lkpi_80211_mo_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
711     struct ieee80211_ampdu_params *params)
712 {
713 	struct lkpi_hw *lhw;
714 	int error;
715 
716 	lhw = HW_TO_LHW(hw);
717 	if (lhw->ops->ampdu_action == NULL) {
718 		error = EOPNOTSUPP;
719 		goto out;
720 	}
721 
722 	LKPI_80211_TRACE_MO("hw %p vif %p params %p { %p, %d, %u, %u, %u, %u, %d }",
723 	    hw, vif, params, params->sta, params->action, params->buf_size,
724 	    params->timeout, params->ssn, params->tid, params->amsdu);
725 	error = lhw->ops->ampdu_action(hw, vif, params);
726 
727 out:
728 	return (error);
729 }
730 
731 int
732 lkpi_80211_mo_sta_statistics(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
733     struct ieee80211_sta *sta, struct station_info *sinfo)
734 {
735 	struct lkpi_hw *lhw;
736 	struct lkpi_sta *lsta;
737 	int error;
738 
739 	lhw = HW_TO_LHW(hw);
740 	if (lhw->ops->sta_statistics == NULL) {
741 		error = EOPNOTSUPP;
742 		goto out;
743 	}
744 
745 	lsta = STA_TO_LSTA(sta);
746 	if (!lsta->added_to_drv) {
747 		error = EEXIST;
748 		goto out;
749 	}
750 
751 	lockdep_assert_wiphy(hw->wiphy);
752 
753 	LKPI_80211_TRACE_MO("hw %p vif %p sta %p sinfo %p", hw, vif, sta, sinfo);
754 	lhw->ops->sta_statistics(hw, vif, sta, sinfo);
755 	error = 0;
756 
757 out:
758 	return (error);
759 }
760