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