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