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 lockdep_assert_wiphy(hw->wiphy); 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, bool suspend) 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 suspend %d", hw, suspend); 88 lhw->ops->stop(hw, suspend); 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 /* 219 * MUST NOT return EPERM as that is a "magic number 1" based on rtw88 220 * driver indicating hw_scan is not supported despite the ops call 221 * being available. 222 */ 223 224 lhw = HW_TO_LHW(hw); 225 if (lhw->ops->hw_scan == NULL) { 226 /* Return magic number to use sw scan. */ 227 error = 1; 228 goto out; 229 } 230 231 LKPI_80211_TRACE_MO("CALLING hw %p vif %p sr %p", hw, vif, sr); 232 error = lhw->ops->hw_scan(hw, vif, sr); 233 LKPI_80211_TRACE_MO("RETURNING hw %p vif %p sr %p error %d", hw, vif, sr, error); 234 235 out: 236 return (error); 237 } 238 239 void 240 lkpi_80211_mo_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif) 241 { 242 struct lkpi_hw *lhw; 243 244 lhw = HW_TO_LHW(hw); 245 if (lhw->ops->cancel_hw_scan == NULL) 246 return; 247 248 LKPI_80211_TRACE_MO("hw %p vif %p", hw, vif); 249 lhw->ops->cancel_hw_scan(hw, vif); 250 } 251 252 void 253 lkpi_80211_mo_sw_scan_complete(struct ieee80211_hw *hw, struct ieee80211_vif *vif) 254 { 255 struct lkpi_hw *lhw; 256 257 lhw = HW_TO_LHW(hw); 258 if (lhw->ops->sw_scan_complete == NULL) 259 return; 260 261 LKPI_80211_TRACE_MO("hw %p vif %p", hw, vif); 262 lhw->ops->sw_scan_complete(hw, vif); 263 lhw->scan_flags &= ~LKPI_LHW_SCAN_RUNNING; 264 } 265 266 void 267 lkpi_80211_mo_sw_scan_start(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 268 const u8 *addr) 269 { 270 struct lkpi_hw *lhw; 271 272 lhw = HW_TO_LHW(hw); 273 if (lhw->ops->sw_scan_start == NULL) 274 return; 275 276 LKPI_80211_TRACE_MO("hw %p vif %p", hw, vif); 277 lhw->ops->sw_scan_start(hw, vif, addr); 278 } 279 280 281 /* 282 * We keep the Linux type here; it really is an uintptr_t. 283 */ 284 u64 285 lkpi_80211_mo_prepare_multicast(struct ieee80211_hw *hw, 286 struct netdev_hw_addr_list *mc_list) 287 { 288 struct lkpi_hw *lhw; 289 u64 ptr; 290 291 lhw = HW_TO_LHW(hw); 292 if (lhw->ops->prepare_multicast == NULL) 293 return (0); 294 295 LKPI_80211_TRACE_MO("hw %p mc_list %p", hw, mc_list); 296 ptr = lhw->ops->prepare_multicast(hw, mc_list); 297 return (ptr); 298 } 299 300 void 301 lkpi_80211_mo_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, 302 unsigned int *total_flags, u64 mc_ptr) 303 { 304 struct lkpi_hw *lhw; 305 306 lhw = HW_TO_LHW(hw); 307 if (lhw->ops->configure_filter == NULL) 308 return; 309 310 if (mc_ptr == 0) 311 return; 312 313 LKPI_80211_TRACE_MO("hw %p changed_flags %#x total_flags %p mc_ptr %ju", hw, changed_flags, total_flags, (uintmax_t)mc_ptr); 314 lhw->ops->configure_filter(hw, changed_flags, total_flags, mc_ptr); 315 } 316 317 318 /* 319 * So far we only called sta_{add,remove} as an alternative to sta_state. 320 * Let's keep the implementation simpler and hide sta_{add,remove} under the 321 * hood here calling them if state_state is not available from mo_sta_state. 322 */ 323 static int 324 lkpi_80211_mo_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 325 struct ieee80211_sta *sta) 326 { 327 struct lkpi_hw *lhw; 328 struct lkpi_sta *lsta; 329 int error; 330 331 lhw = HW_TO_LHW(hw); 332 if (lhw->ops->sta_add == NULL) { 333 error = EOPNOTSUPP; 334 goto out; 335 } 336 337 lsta = STA_TO_LSTA(sta); 338 if (lsta->added_to_drv) { 339 error = EEXIST; 340 goto out; 341 } 342 343 LKPI_80211_TRACE_MO("hw %p vif %p sta %p", hw, vif, sta); 344 error = lhw->ops->sta_add(hw, vif, sta); 345 if (error == 0) 346 lsta->added_to_drv = true; 347 348 out: 349 return error; 350 } 351 352 static int 353 lkpi_80211_mo_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 354 struct ieee80211_sta *sta) 355 { 356 struct lkpi_hw *lhw; 357 struct lkpi_sta *lsta; 358 int error; 359 360 lhw = HW_TO_LHW(hw); 361 if (lhw->ops->sta_remove == NULL) { 362 error = EOPNOTSUPP; 363 goto out; 364 } 365 366 lsta = STA_TO_LSTA(sta); 367 if (!lsta->added_to_drv) { 368 /* If we never added the sta, do not complain on cleanup. */ 369 error = 0; 370 goto out; 371 } 372 373 LKPI_80211_TRACE_MO("hw %p vif %p sta %p", hw, vif, sta); 374 error = lhw->ops->sta_remove(hw, vif, sta); 375 if (error == 0) 376 lsta->added_to_drv = false; 377 378 out: 379 return error; 380 } 381 382 int 383 lkpi_80211_mo_sta_state(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 384 struct lkpi_sta *lsta, enum ieee80211_sta_state nstate) 385 { 386 struct lkpi_hw *lhw; 387 struct ieee80211_sta *sta; 388 int error; 389 390 lhw = HW_TO_LHW(hw); 391 sta = LSTA_TO_STA(lsta); 392 if (lhw->ops->sta_state != NULL) { 393 LKPI_80211_TRACE_MO("hw %p vif %p sta %p nstate %d", hw, vif, sta, nstate); 394 error = lhw->ops->sta_state(hw, vif, sta, lsta->state, nstate); 395 if (error == 0) { 396 if (nstate == IEEE80211_STA_NOTEXIST) 397 lsta->added_to_drv = false; 398 else 399 lsta->added_to_drv = true; 400 lsta->state = nstate; 401 } 402 goto out; 403 } 404 405 /* XXX-BZ is the change state AUTH or ASSOC here? */ 406 if (lsta->state < IEEE80211_STA_ASSOC && nstate == IEEE80211_STA_ASSOC) { 407 error = lkpi_80211_mo_sta_add(hw, vif, sta); 408 if (error == 0) 409 lsta->added_to_drv = true; 410 } else if (lsta->state >= IEEE80211_STA_ASSOC && 411 nstate < IEEE80211_STA_ASSOC) { 412 error = lkpi_80211_mo_sta_remove(hw, vif, sta); 413 if (error == 0) 414 lsta->added_to_drv = false; 415 } else 416 /* Nothing to do. */ 417 error = 0; 418 if (error == 0) 419 lsta->state = nstate; 420 421 out: 422 /* XXX-BZ should we manage state in here? */ 423 return (error); 424 } 425 426 int 427 lkpi_80211_mo_config(struct ieee80211_hw *hw, uint32_t changed) 428 { 429 struct lkpi_hw *lhw; 430 int error; 431 432 lhw = HW_TO_LHW(hw); 433 if (lhw->ops->config == NULL) { 434 error = EOPNOTSUPP; 435 goto out; 436 } 437 438 LKPI_80211_TRACE_MO("hw %p changed %u", hw, changed); 439 error = lhw->ops->config(hw, changed); 440 441 out: 442 return (error); 443 } 444 445 446 int 447 lkpi_80211_mo_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 448 struct ieee80211_bss_conf *conf, struct ieee80211_chanctx_conf *chanctx_conf) 449 { 450 struct lkpi_hw *lhw; 451 int error; 452 453 lhw = HW_TO_LHW(hw); 454 if (lhw->ops->assign_vif_chanctx == NULL) { 455 error = EOPNOTSUPP; 456 goto out; 457 } 458 459 LKPI_80211_TRACE_MO("hw %p vif %p bss_conf %p chanctx_conf %p", 460 hw, vif, conf, chanctx_conf); 461 error = lhw->ops->assign_vif_chanctx(hw, vif, conf, chanctx_conf); 462 if (error == 0) 463 vif->bss_conf.chanctx_conf = chanctx_conf; 464 465 out: 466 return (error); 467 } 468 469 void 470 lkpi_80211_mo_unassign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 471 struct ieee80211_bss_conf *conf, struct ieee80211_chanctx_conf *chanctx_conf) 472 { 473 struct lkpi_hw *lhw; 474 475 might_sleep(); 476 lockdep_assert_wiphy(hw->wiphy); 477 478 lhw = HW_TO_LHW(hw); 479 if (lhw->ops->unassign_vif_chanctx == NULL) 480 return; 481 482 if (chanctx_conf == NULL) 483 return; 484 485 LKPI_80211_TRACE_MO("hw %p vif %p bss_conf %p chanctx_conf %p", 486 hw, vif, conf, chanctx_conf); 487 lhw->ops->unassign_vif_chanctx(hw, vif, conf, chanctx_conf); 488 } 489 490 491 int 492 lkpi_80211_mo_add_chanctx(struct ieee80211_hw *hw, 493 struct ieee80211_chanctx_conf *chanctx_conf) 494 { 495 struct lkpi_hw *lhw; 496 struct lkpi_chanctx *lchanctx; 497 int error; 498 499 lhw = HW_TO_LHW(hw); 500 if (lhw->ops->add_chanctx == NULL) { 501 error = EOPNOTSUPP; 502 goto out; 503 } 504 505 LKPI_80211_TRACE_MO("hw %p chanctx_conf %p", hw, chanctx_conf); 506 error = lhw->ops->add_chanctx(hw, chanctx_conf); 507 if (error == 0) { 508 lchanctx = CHANCTX_CONF_TO_LCHANCTX(chanctx_conf); 509 lchanctx->added_to_drv = true; 510 } 511 512 out: 513 return (error); 514 } 515 516 void 517 lkpi_80211_mo_change_chanctx(struct ieee80211_hw *hw, 518 struct ieee80211_chanctx_conf *chanctx_conf, uint32_t changed) 519 { 520 struct lkpi_hw *lhw; 521 522 lhw = HW_TO_LHW(hw); 523 if (lhw->ops->change_chanctx == NULL) 524 return; 525 526 LKPI_80211_TRACE_MO("hw %p chanctx_conf %p changed %u", hw, chanctx_conf, changed); 527 lhw->ops->change_chanctx(hw, chanctx_conf, changed); 528 } 529 530 void 531 lkpi_80211_mo_remove_chanctx(struct ieee80211_hw *hw, 532 struct ieee80211_chanctx_conf *chanctx_conf) 533 { 534 struct lkpi_hw *lhw; 535 struct lkpi_chanctx *lchanctx; 536 537 lhw = HW_TO_LHW(hw); 538 if (lhw->ops->remove_chanctx == NULL) 539 return; 540 541 LKPI_80211_TRACE_MO("hw %p chanctx_conf %p", hw, chanctx_conf); 542 lhw->ops->remove_chanctx(hw, chanctx_conf); 543 lchanctx = CHANCTX_CONF_TO_LCHANCTX(chanctx_conf); 544 lchanctx->added_to_drv = false; 545 } 546 547 void 548 lkpi_80211_mo_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 549 struct ieee80211_bss_conf *conf, uint64_t changed) 550 { 551 struct lkpi_hw *lhw; 552 553 lhw = HW_TO_LHW(hw); 554 if (lhw->ops->link_info_changed == NULL && 555 lhw->ops->bss_info_changed == NULL) 556 return; 557 558 if (changed == 0) 559 return; 560 561 LKPI_80211_TRACE_MO("hw %p vif %p conf %p changed %#jx", hw, vif, conf, (uintmax_t)changed); 562 if (lhw->ops->link_info_changed != NULL) 563 lhw->ops->link_info_changed(hw, vif, conf, changed); 564 else 565 lhw->ops->bss_info_changed(hw, vif, conf, changed); 566 } 567 568 int 569 lkpi_80211_mo_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 570 uint32_t link_id, uint16_t ac, const struct ieee80211_tx_queue_params *txqp) 571 { 572 struct lkpi_hw *lhw; 573 int error; 574 575 lhw = HW_TO_LHW(hw); 576 if (lhw->ops->conf_tx == NULL) { 577 error = EOPNOTSUPP; 578 goto out; 579 } 580 581 LKPI_80211_TRACE_MO("hw %p vif %p link_id %u ac %u txpq %p", 582 hw, vif, link_id, ac, txqp); 583 error = lhw->ops->conf_tx(hw, vif, link_id, ac, txqp); 584 585 out: 586 return (error); 587 } 588 589 void 590 lkpi_80211_mo_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 591 uint32_t nqueues, bool drop) 592 { 593 struct lkpi_hw *lhw; 594 595 lhw = HW_TO_LHW(hw); 596 if (lhw->ops->flush == NULL) 597 return; 598 599 LKPI_80211_TRACE_MO("hw %p vif %p nqueues %u drop %d", hw, vif, nqueues, drop); 600 lhw->ops->flush(hw, vif, nqueues, drop); 601 } 602 603 void 604 lkpi_80211_mo_mgd_prepare_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 605 struct ieee80211_prep_tx_info *txinfo) 606 { 607 struct lkpi_hw *lhw; 608 609 lhw = HW_TO_LHW(hw); 610 if (lhw->ops->mgd_prepare_tx == NULL) 611 return; 612 613 LKPI_80211_TRACE_MO("hw %p vif %p txinfo %p", hw, vif, txinfo); 614 lhw->ops->mgd_prepare_tx(hw, vif, txinfo); 615 } 616 617 void 618 lkpi_80211_mo_mgd_complete_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 619 struct ieee80211_prep_tx_info *txinfo) 620 { 621 struct lkpi_hw *lhw; 622 623 lhw = HW_TO_LHW(hw); 624 if (lhw->ops->mgd_complete_tx == NULL) 625 return; 626 627 LKPI_80211_TRACE_MO("hw %p vif %p txinfo %p", hw, vif, txinfo); 628 lhw->ops->mgd_complete_tx(hw, vif, txinfo); 629 } 630 631 void 632 lkpi_80211_mo_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *txctrl, 633 struct sk_buff *skb) 634 { 635 struct lkpi_hw *lhw; 636 637 lhw = HW_TO_LHW(hw); 638 if (lhw->ops->tx == NULL) 639 return; 640 641 LKPI_80211_TRACE_MO("hw %p txctrl %p skb %p", hw, txctrl, skb); 642 lhw->ops->tx(hw, txctrl, skb); 643 } 644 645 void 646 lkpi_80211_mo_wake_tx_queue(struct ieee80211_hw *hw, struct ieee80211_txq *txq) 647 { 648 struct lkpi_hw *lhw; 649 650 lhw = HW_TO_LHW(hw); 651 if (lhw->ops->wake_tx_queue == NULL) 652 return; 653 654 LKPI_80211_TRACE_MO("hw %p txq %p", hw, txq); 655 lhw->ops->wake_tx_queue(hw, txq); 656 } 657 658 void 659 lkpi_80211_mo_sync_rx_queues(struct ieee80211_hw *hw) 660 { 661 struct lkpi_hw *lhw; 662 663 lhw = HW_TO_LHW(hw); 664 if (lhw->ops->sync_rx_queues == NULL) 665 return; 666 667 LKPI_80211_TRACE_MO("hw %p", hw); 668 lhw->ops->sync_rx_queues(hw); 669 } 670 671 void 672 lkpi_80211_mo_sta_pre_rcu_remove(struct ieee80211_hw *hw, 673 struct ieee80211_vif *vif, struct ieee80211_sta *sta) 674 { 675 struct lkpi_hw *lhw; 676 677 lhw = HW_TO_LHW(hw); 678 if (lhw->ops->sta_pre_rcu_remove == NULL) 679 return; 680 681 LKPI_80211_TRACE_MO("hw %p vif %p sta %p", hw, vif, sta); 682 lhw->ops->sta_pre_rcu_remove(hw, vif, sta); 683 } 684 685 int 686 lkpi_80211_mo_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, 687 struct ieee80211_vif *vif, struct ieee80211_sta *sta, 688 struct ieee80211_key_conf *kc) 689 { 690 struct lkpi_hw *lhw; 691 int error; 692 693 lockdep_assert_wiphy(hw->wiphy); 694 695 lhw = HW_TO_LHW(hw); 696 if (lhw->ops->set_key == NULL) { 697 error = EOPNOTSUPP; 698 goto out; 699 } 700 701 LKPI_80211_TRACE_MO("hw %p cmd %d vif %p sta %p kc %p", hw, cmd, vif, sta, kc); 702 error = lhw->ops->set_key(hw, cmd, vif, sta, kc); 703 704 out: 705 return (error); 706 } 707 708 int 709 lkpi_80211_mo_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 710 struct ieee80211_ampdu_params *params) 711 { 712 struct lkpi_hw *lhw; 713 int error; 714 715 lhw = HW_TO_LHW(hw); 716 if (lhw->ops->ampdu_action == NULL) { 717 error = EOPNOTSUPP; 718 goto out; 719 } 720 721 LKPI_80211_TRACE_MO("hw %p vif %p params %p { %p, %d, %u, %u, %u, %u, %d }", 722 hw, vif, params, params->sta, params->action, params->buf_size, 723 params->timeout, params->ssn, params->tid, params->amsdu); 724 error = lhw->ops->ampdu_action(hw, vif, params); 725 726 out: 727 return (error); 728 } 729 730 int 731 lkpi_80211_mo_sta_statistics(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 732 struct ieee80211_sta *sta, struct station_info *sinfo) 733 { 734 struct lkpi_hw *lhw; 735 struct lkpi_sta *lsta; 736 int error; 737 738 lhw = HW_TO_LHW(hw); 739 if (lhw->ops->sta_statistics == NULL) { 740 error = EOPNOTSUPP; 741 goto out; 742 } 743 744 lsta = STA_TO_LSTA(sta); 745 if (!lsta->added_to_drv) { 746 error = EEXIST; 747 goto out; 748 } 749 750 lockdep_assert_wiphy(hw->wiphy); 751 752 LKPI_80211_TRACE_MO("hw %p vif %p sta %p sinfo %p", hw, vif, sta, sinfo); 753 lhw->ops->sta_statistics(hw, vif, sta, sinfo); 754 error = 0; 755 756 out: 757 return (error); 758 } 759