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