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