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