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
lkpi_80211_mo_start(struct ieee80211_hw * hw)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
lkpi_80211_mo_stop(struct ieee80211_hw * hw)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
lkpi_80211_mo_set_frag_threshold(struct ieee80211_hw * hw,uint32_t frag_th)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
lkpi_80211_mo_set_rts_threshold(struct ieee80211_hw * hw,uint32_t rts_th)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
lkpi_80211_mo_add_interface(struct ieee80211_hw * hw,struct ieee80211_vif * vif)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
lkpi_80211_mo_remove_interface(struct ieee80211_hw * hw,struct ieee80211_vif * vif)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
lkpi_80211_mo_hw_scan(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_scan_request * sr)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
lkpi_80211_mo_cancel_hw_scan(struct ieee80211_hw * hw,struct ieee80211_vif * vif)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
lkpi_80211_mo_sw_scan_complete(struct ieee80211_hw * hw,struct ieee80211_vif * vif)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
lkpi_80211_mo_sw_scan_start(struct ieee80211_hw * hw,struct ieee80211_vif * vif,const u8 * addr)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
lkpi_80211_mo_prepare_multicast(struct ieee80211_hw * hw,struct netdev_hw_addr_list * mc_list)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
lkpi_80211_mo_configure_filter(struct ieee80211_hw * hw,unsigned int changed_flags,unsigned int * total_flags,u64 mc_ptr)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
lkpi_80211_mo_sta_add(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_sta * sta)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
lkpi_80211_mo_sta_remove(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_sta * sta)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
lkpi_80211_mo_sta_state(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_sta * sta,enum ieee80211_sta_state nstate)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
lkpi_80211_mo_config(struct ieee80211_hw * hw,uint32_t changed)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
lkpi_80211_mo_assign_vif_chanctx(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_chanctx_conf * chanctx_conf)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
lkpi_80211_mo_unassign_vif_chanctx(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_chanctx_conf ** chanctx_conf)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
lkpi_80211_mo_add_chanctx(struct ieee80211_hw * hw,struct ieee80211_chanctx_conf * chanctx_conf)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
lkpi_80211_mo_change_chanctx(struct ieee80211_hw * hw,struct ieee80211_chanctx_conf * chanctx_conf,uint32_t changed)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
lkpi_80211_mo_remove_chanctx(struct ieee80211_hw * hw,struct ieee80211_chanctx_conf * chanctx_conf)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
lkpi_80211_mo_bss_info_changed(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_bss_conf * conf,uint32_t changed)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
lkpi_80211_mo_conf_tx(struct ieee80211_hw * hw,struct ieee80211_vif * vif,uint16_t ac,const struct ieee80211_tx_queue_params * txqp)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
lkpi_80211_mo_flush(struct ieee80211_hw * hw,struct ieee80211_vif * vif,uint32_t nqueues,bool drop)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
lkpi_80211_mo_mgd_prepare_tx(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_prep_tx_info * txinfo)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
lkpi_80211_mo_mgd_complete_tx(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_prep_tx_info * txinfo)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
lkpi_80211_mo_tx(struct ieee80211_hw * hw,struct ieee80211_tx_control * txctrl,struct sk_buff * skb)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
lkpi_80211_mo_wake_tx_queue(struct ieee80211_hw * hw,struct ieee80211_txq * txq)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
lkpi_80211_mo_sync_rx_queues(struct ieee80211_hw * hw)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
lkpi_80211_mo_sta_pre_rcu_remove(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_sta * sta)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
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)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