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 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, bool suspend) 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 suspend %d", hw, suspend); 86 lhw->ops->stop(hw, suspend); 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->bss_conf.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 might_sleep(); 474 lockdep_assert_wiphy(hw->wiphy); 475 476 lhw = HW_TO_LHW(hw); 477 if (lhw->ops->unassign_vif_chanctx == NULL) 478 return; 479 480 if (chanctx_conf == NULL) 481 return; 482 483 LKPI_80211_TRACE_MO("hw %p vif %p bss_conf %p chanctx_conf %p", 484 hw, vif, conf, chanctx_conf); 485 lhw->ops->unassign_vif_chanctx(hw, vif, conf, chanctx_conf); 486 } 487 488 489 int 490 lkpi_80211_mo_add_chanctx(struct ieee80211_hw *hw, 491 struct ieee80211_chanctx_conf *chanctx_conf) 492 { 493 struct lkpi_hw *lhw; 494 struct lkpi_chanctx *lchanctx; 495 int error; 496 497 lhw = HW_TO_LHW(hw); 498 if (lhw->ops->add_chanctx == NULL) { 499 error = EOPNOTSUPP; 500 goto out; 501 } 502 503 LKPI_80211_TRACE_MO("hw %p chanctx_conf %p", hw, chanctx_conf); 504 error = lhw->ops->add_chanctx(hw, chanctx_conf); 505 if (error == 0) { 506 lchanctx = CHANCTX_CONF_TO_LCHANCTX(chanctx_conf); 507 lchanctx->added_to_drv = true; 508 } 509 510 out: 511 return (error); 512 } 513 514 void 515 lkpi_80211_mo_change_chanctx(struct ieee80211_hw *hw, 516 struct ieee80211_chanctx_conf *chanctx_conf, uint32_t changed) 517 { 518 struct lkpi_hw *lhw; 519 520 lhw = HW_TO_LHW(hw); 521 if (lhw->ops->change_chanctx == NULL) 522 return; 523 524 LKPI_80211_TRACE_MO("hw %p chanctx_conf %p changed %u", hw, chanctx_conf, changed); 525 lhw->ops->change_chanctx(hw, chanctx_conf, changed); 526 } 527 528 void 529 lkpi_80211_mo_remove_chanctx(struct ieee80211_hw *hw, 530 struct ieee80211_chanctx_conf *chanctx_conf) 531 { 532 struct lkpi_hw *lhw; 533 struct lkpi_chanctx *lchanctx; 534 535 lhw = HW_TO_LHW(hw); 536 if (lhw->ops->remove_chanctx == NULL) 537 return; 538 539 LKPI_80211_TRACE_MO("hw %p chanctx_conf %p", hw, chanctx_conf); 540 lhw->ops->remove_chanctx(hw, chanctx_conf); 541 lchanctx = CHANCTX_CONF_TO_LCHANCTX(chanctx_conf); 542 lchanctx->added_to_drv = false; 543 } 544 545 void 546 lkpi_80211_mo_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 547 struct ieee80211_bss_conf *conf, uint64_t changed) 548 { 549 struct lkpi_hw *lhw; 550 551 lhw = HW_TO_LHW(hw); 552 if (lhw->ops->link_info_changed == NULL && 553 lhw->ops->bss_info_changed == NULL) 554 return; 555 556 if (changed == 0) 557 return; 558 559 LKPI_80211_TRACE_MO("hw %p vif %p conf %p changed %#jx", hw, vif, conf, (uintmax_t)changed); 560 if (lhw->ops->link_info_changed != NULL) 561 lhw->ops->link_info_changed(hw, vif, conf, changed); 562 else 563 lhw->ops->bss_info_changed(hw, vif, conf, changed); 564 } 565 566 int 567 lkpi_80211_mo_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 568 uint32_t link_id, uint16_t ac, const struct ieee80211_tx_queue_params *txqp) 569 { 570 struct lkpi_hw *lhw; 571 int error; 572 573 lhw = HW_TO_LHW(hw); 574 if (lhw->ops->conf_tx == NULL) { 575 error = EOPNOTSUPP; 576 goto out; 577 } 578 579 LKPI_80211_TRACE_MO("hw %p vif %p link_id %u ac %u txpq %p", 580 hw, vif, link_id, ac, txqp); 581 error = lhw->ops->conf_tx(hw, vif, link_id, ac, txqp); 582 583 out: 584 return (error); 585 } 586 587 void 588 lkpi_80211_mo_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 589 uint32_t nqueues, bool drop) 590 { 591 struct lkpi_hw *lhw; 592 593 lhw = HW_TO_LHW(hw); 594 if (lhw->ops->flush == NULL) 595 return; 596 597 LKPI_80211_TRACE_MO("hw %p vif %p nqueues %u drop %d", hw, vif, nqueues, drop); 598 lhw->ops->flush(hw, vif, nqueues, drop); 599 } 600 601 void 602 lkpi_80211_mo_mgd_prepare_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 603 struct ieee80211_prep_tx_info *txinfo) 604 { 605 struct lkpi_hw *lhw; 606 607 lhw = HW_TO_LHW(hw); 608 if (lhw->ops->mgd_prepare_tx == NULL) 609 return; 610 611 LKPI_80211_TRACE_MO("hw %p vif %p txinfo %p", hw, vif, txinfo); 612 lhw->ops->mgd_prepare_tx(hw, vif, txinfo); 613 } 614 615 void 616 lkpi_80211_mo_mgd_complete_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 617 struct ieee80211_prep_tx_info *txinfo) 618 { 619 struct lkpi_hw *lhw; 620 621 lhw = HW_TO_LHW(hw); 622 if (lhw->ops->mgd_complete_tx == NULL) 623 return; 624 625 LKPI_80211_TRACE_MO("hw %p vif %p txinfo %p", hw, vif, txinfo); 626 lhw->ops->mgd_complete_tx(hw, vif, txinfo); 627 } 628 629 void 630 lkpi_80211_mo_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *txctrl, 631 struct sk_buff *skb) 632 { 633 struct lkpi_hw *lhw; 634 635 lhw = HW_TO_LHW(hw); 636 if (lhw->ops->tx == NULL) 637 return; 638 639 LKPI_80211_TRACE_MO("hw %p txctrl %p skb %p", hw, txctrl, skb); 640 lhw->ops->tx(hw, txctrl, skb); 641 } 642 643 void 644 lkpi_80211_mo_wake_tx_queue(struct ieee80211_hw *hw, struct ieee80211_txq *txq) 645 { 646 struct lkpi_hw *lhw; 647 648 lhw = HW_TO_LHW(hw); 649 if (lhw->ops->wake_tx_queue == NULL) 650 return; 651 652 LKPI_80211_TRACE_MO("hw %p txq %p", hw, txq); 653 lhw->ops->wake_tx_queue(hw, txq); 654 } 655 656 void 657 lkpi_80211_mo_sync_rx_queues(struct ieee80211_hw *hw) 658 { 659 struct lkpi_hw *lhw; 660 661 lhw = HW_TO_LHW(hw); 662 if (lhw->ops->sync_rx_queues == NULL) 663 return; 664 665 LKPI_80211_TRACE_MO("hw %p", hw); 666 lhw->ops->sync_rx_queues(hw); 667 } 668 669 void 670 lkpi_80211_mo_sta_pre_rcu_remove(struct ieee80211_hw *hw, 671 struct ieee80211_vif *vif, struct ieee80211_sta *sta) 672 { 673 struct lkpi_hw *lhw; 674 675 lhw = HW_TO_LHW(hw); 676 if (lhw->ops->sta_pre_rcu_remove == NULL) 677 return; 678 679 LKPI_80211_TRACE_MO("hw %p vif %p sta %p", hw, vif, sta); 680 lhw->ops->sta_pre_rcu_remove(hw, vif, sta); 681 } 682 683 int 684 lkpi_80211_mo_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, 685 struct ieee80211_vif *vif, struct ieee80211_sta *sta, 686 struct ieee80211_key_conf *kc) 687 { 688 struct lkpi_hw *lhw; 689 int error; 690 691 lockdep_assert_wiphy(hw->wiphy); 692 693 lhw = HW_TO_LHW(hw); 694 if (lhw->ops->set_key == NULL) { 695 error = EOPNOTSUPP; 696 goto out; 697 } 698 699 LKPI_80211_TRACE_MO("hw %p cmd %d vif %p sta %p kc %p", hw, cmd, vif, sta, kc); 700 error = lhw->ops->set_key(hw, cmd, vif, sta, kc); 701 702 out: 703 return (error); 704 } 705 706 int 707 lkpi_80211_mo_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 708 struct ieee80211_ampdu_params *params) 709 { 710 struct lkpi_hw *lhw; 711 int error; 712 713 lhw = HW_TO_LHW(hw); 714 if (lhw->ops->ampdu_action == NULL) { 715 error = EOPNOTSUPP; 716 goto out; 717 } 718 719 LKPI_80211_TRACE_MO("hw %p vif %p params %p { %p, %d, %u, %u, %u, %u, %d }", 720 hw, vif, params, params->sta, params->action, params->buf_size, 721 params->timeout, params->ssn, params->tid, params->amsdu); 722 error = lhw->ops->ampdu_action(hw, vif, params); 723 724 out: 725 return (error); 726 } 727 728 int 729 lkpi_80211_mo_sta_statistics(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 730 struct ieee80211_sta *sta, struct station_info *sinfo) 731 { 732 struct lkpi_hw *lhw; 733 struct lkpi_sta *lsta; 734 int error; 735 736 lhw = HW_TO_LHW(hw); 737 if (lhw->ops->sta_statistics == NULL) { 738 error = EOPNOTSUPP; 739 goto out; 740 } 741 742 lsta = STA_TO_LSTA(sta); 743 if (!lsta->added_to_drv) { 744 error = EEXIST; 745 goto out; 746 } 747 748 lockdep_assert_wiphy(hw->wiphy); 749 750 LKPI_80211_TRACE_MO("hw %p vif %p sta %p sinfo %p", hw, vif, sta, sinfo); 751 lhw->ops->sta_statistics(hw, vif, sta, sinfo); 752 error = 0; 753 754 out: 755 return (error); 756 } 757