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