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