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