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