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 LKPI_80211_TRACE_MO("TODO link/radio_idx"); 106 error = lhw->ops->get_antenna(hw, 0, txs, rxs); 107 108 out: 109 return (error); 110 } 111 112 int 113 lkpi_80211_mo_set_frag_threshold(struct ieee80211_hw *hw, uint32_t frag_th) 114 { 115 struct lkpi_hw *lhw; 116 int error; 117 118 lhw = HW_TO_LHW(hw); 119 if (lhw->ops->set_frag_threshold == NULL) { 120 error = EOPNOTSUPP; 121 goto out; 122 } 123 124 LKPI_80211_TRACE_MO("hw %p frag_th %u", hw, frag_th); 125 LKPI_80211_TRACE_MO("TODO link/radio_idx"); 126 error = lhw->ops->set_frag_threshold(hw, 0, frag_th); 127 128 out: 129 return (error); 130 } 131 132 int 133 lkpi_80211_mo_set_rts_threshold(struct ieee80211_hw *hw, uint32_t rts_th) 134 { 135 struct lkpi_hw *lhw; 136 int error; 137 138 lhw = HW_TO_LHW(hw); 139 if (lhw->ops->set_rts_threshold == NULL) { 140 error = EOPNOTSUPP; 141 goto out; 142 } 143 144 LKPI_80211_TRACE_MO("hw %p rts_th %u", hw, rts_th); 145 LKPI_80211_TRACE_MO("TODO link/radio_idx"); 146 error = lhw->ops->set_rts_threshold(hw, 0, rts_th); 147 148 out: 149 return (error); 150 } 151 152 153 int 154 lkpi_80211_mo_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) 155 { 156 struct lkpi_hw *lhw; 157 struct lkpi_vif *lvif; 158 int error; 159 160 lhw = HW_TO_LHW(hw); 161 if (lhw->ops->add_interface == NULL) { 162 error = EOPNOTSUPP; 163 goto out; 164 } 165 166 lvif = VIF_TO_LVIF(vif); 167 LKPI_80211_LVIF_LOCK(lvif); 168 if (lvif->added_to_drv) { 169 LKPI_80211_LVIF_UNLOCK(lvif); 170 /* Trying to add twice is an error. */ 171 error = EEXIST; 172 goto out; 173 } 174 LKPI_80211_LVIF_UNLOCK(lvif); 175 176 LKPI_80211_TRACE_MO("hw %p vif %p", hw, vif); 177 error = lhw->ops->add_interface(hw, vif); 178 if (error == 0) { 179 LKPI_80211_LVIF_LOCK(lvif); 180 lvif->added_to_drv = true; 181 LKPI_80211_LVIF_UNLOCK(lvif); 182 } 183 184 out: 185 return (error); 186 } 187 188 void 189 lkpi_80211_mo_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) 190 { 191 struct lkpi_hw *lhw; 192 struct lkpi_vif *lvif; 193 194 lhw = HW_TO_LHW(hw); 195 if (lhw->ops->remove_interface == NULL) 196 return; 197 198 lvif = VIF_TO_LVIF(vif); 199 LKPI_80211_LVIF_LOCK(lvif); 200 if (!lvif->added_to_drv) { 201 LKPI_80211_LVIF_UNLOCK(lvif); 202 return; 203 } 204 LKPI_80211_LVIF_UNLOCK(lvif); 205 206 LKPI_80211_TRACE_MO("hw %p vif %p", hw, vif); 207 lhw->ops->remove_interface(hw, vif); 208 LKPI_80211_LVIF_LOCK(lvif); 209 lvif->added_to_drv = false; 210 LKPI_80211_LVIF_UNLOCK(lvif); 211 } 212 213 214 int 215 lkpi_80211_mo_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 216 struct ieee80211_scan_request *sr) 217 { 218 struct lkpi_hw *lhw; 219 int error; 220 221 /* 222 * MUST NOT return EPERM as that is a "magic number 1" based on rtw88 223 * driver indicating hw_scan is not supported despite the ops call 224 * being available. 225 */ 226 227 lhw = HW_TO_LHW(hw); 228 if (lhw->ops->hw_scan == NULL) { 229 /* Return magic number to use sw scan. */ 230 error = 1; 231 goto out; 232 } 233 234 LKPI_80211_TRACE_MO("CALLING hw %p vif %p sr %p", hw, vif, sr); 235 error = lhw->ops->hw_scan(hw, vif, sr); 236 LKPI_80211_TRACE_MO("RETURNING hw %p vif %p sr %p error %d", hw, vif, sr, error); 237 238 out: 239 return (error); 240 } 241 242 void 243 lkpi_80211_mo_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif) 244 { 245 struct lkpi_hw *lhw; 246 247 lhw = HW_TO_LHW(hw); 248 if (lhw->ops->cancel_hw_scan == NULL) 249 return; 250 251 LKPI_80211_TRACE_MO("hw %p vif %p", hw, vif); 252 lhw->ops->cancel_hw_scan(hw, vif); 253 } 254 255 void 256 lkpi_80211_mo_sw_scan_complete(struct ieee80211_hw *hw, struct ieee80211_vif *vif) 257 { 258 struct lkpi_hw *lhw; 259 260 lhw = HW_TO_LHW(hw); 261 if (lhw->ops->sw_scan_complete == NULL) 262 return; 263 264 LKPI_80211_TRACE_MO("hw %p vif %p", hw, vif); 265 lhw->ops->sw_scan_complete(hw, vif); 266 lhw->scan_flags &= ~LKPI_LHW_SCAN_RUNNING; 267 } 268 269 void 270 lkpi_80211_mo_sw_scan_start(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 271 const u8 *addr) 272 { 273 struct lkpi_hw *lhw; 274 275 lhw = HW_TO_LHW(hw); 276 if (lhw->ops->sw_scan_start == NULL) 277 return; 278 279 LKPI_80211_TRACE_MO("hw %p vif %p", hw, vif); 280 lhw->ops->sw_scan_start(hw, vif, addr); 281 } 282 283 284 /* 285 * We keep the Linux type here; it really is an uintptr_t. 286 */ 287 u64 288 lkpi_80211_mo_prepare_multicast(struct ieee80211_hw *hw, 289 struct netdev_hw_addr_list *mc_list) 290 { 291 struct lkpi_hw *lhw; 292 u64 ptr; 293 294 lhw = HW_TO_LHW(hw); 295 if (lhw->ops->prepare_multicast == NULL) 296 return (0); 297 298 LKPI_80211_TRACE_MO("hw %p mc_list %p", hw, mc_list); 299 ptr = lhw->ops->prepare_multicast(hw, mc_list); 300 return (ptr); 301 } 302 303 void 304 lkpi_80211_mo_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, 305 unsigned int *total_flags, u64 mc_ptr) 306 { 307 struct lkpi_hw *lhw; 308 309 lhw = HW_TO_LHW(hw); 310 if (lhw->ops->configure_filter == NULL) 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 LKPI_80211_TRACE_MO("TODO link/radio_idx"); 440 error = lhw->ops->config(hw, 0, changed); 441 442 out: 443 return (error); 444 } 445 446 447 int 448 lkpi_80211_mo_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 449 struct ieee80211_bss_conf *conf, struct ieee80211_chanctx_conf *chanctx_conf) 450 { 451 struct lkpi_hw *lhw; 452 int error; 453 454 lhw = HW_TO_LHW(hw); 455 if (lhw->ops->assign_vif_chanctx == NULL) { 456 error = EOPNOTSUPP; 457 goto out; 458 } 459 460 LKPI_80211_TRACE_MO("hw %p vif %p bss_conf %p chanctx_conf %p", 461 hw, vif, conf, chanctx_conf); 462 error = lhw->ops->assign_vif_chanctx(hw, vif, conf, chanctx_conf); 463 if (error == 0) 464 vif->bss_conf.chanctx_conf = chanctx_conf; 465 466 out: 467 return (error); 468 } 469 470 void 471 lkpi_80211_mo_unassign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 472 struct ieee80211_bss_conf *conf, struct ieee80211_chanctx_conf *chanctx_conf) 473 { 474 struct lkpi_hw *lhw; 475 476 might_sleep(); 477 lockdep_assert_wiphy(hw->wiphy); 478 479 lhw = HW_TO_LHW(hw); 480 if (lhw->ops->unassign_vif_chanctx == NULL) 481 return; 482 483 if (chanctx_conf == NULL) 484 return; 485 486 LKPI_80211_TRACE_MO("hw %p vif %p bss_conf %p chanctx_conf %p", 487 hw, vif, conf, chanctx_conf); 488 lhw->ops->unassign_vif_chanctx(hw, vif, conf, chanctx_conf); 489 } 490 491 492 int 493 lkpi_80211_mo_add_chanctx(struct ieee80211_hw *hw, 494 struct ieee80211_chanctx_conf *chanctx_conf) 495 { 496 struct lkpi_hw *lhw; 497 struct lkpi_chanctx *lchanctx; 498 int error; 499 500 lhw = HW_TO_LHW(hw); 501 if (lhw->ops->add_chanctx == NULL) { 502 error = EOPNOTSUPP; 503 goto out; 504 } 505 506 LKPI_80211_TRACE_MO("hw %p chanctx_conf %p", hw, chanctx_conf); 507 error = lhw->ops->add_chanctx(hw, chanctx_conf); 508 if (error == 0) { 509 lchanctx = CHANCTX_CONF_TO_LCHANCTX(chanctx_conf); 510 lchanctx->added_to_drv = true; 511 } 512 513 out: 514 return (error); 515 } 516 517 void 518 lkpi_80211_mo_change_chanctx(struct ieee80211_hw *hw, 519 struct ieee80211_chanctx_conf *chanctx_conf, uint32_t changed) 520 { 521 struct lkpi_hw *lhw; 522 523 lhw = HW_TO_LHW(hw); 524 if (lhw->ops->change_chanctx == NULL) 525 return; 526 527 LKPI_80211_TRACE_MO("hw %p chanctx_conf %p changed %u", hw, chanctx_conf, changed); 528 lhw->ops->change_chanctx(hw, chanctx_conf, changed); 529 } 530 531 void 532 lkpi_80211_mo_remove_chanctx(struct ieee80211_hw *hw, 533 struct ieee80211_chanctx_conf *chanctx_conf) 534 { 535 struct lkpi_hw *lhw; 536 struct lkpi_chanctx *lchanctx; 537 538 lhw = HW_TO_LHW(hw); 539 if (lhw->ops->remove_chanctx == NULL) 540 return; 541 542 LKPI_80211_TRACE_MO("hw %p chanctx_conf %p", hw, chanctx_conf); 543 lhw->ops->remove_chanctx(hw, chanctx_conf); 544 lchanctx = CHANCTX_CONF_TO_LCHANCTX(chanctx_conf); 545 lchanctx->added_to_drv = false; 546 } 547 548 void 549 lkpi_80211_mo_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 550 struct ieee80211_bss_conf *conf, uint64_t changed) 551 { 552 struct lkpi_hw *lhw; 553 554 lhw = HW_TO_LHW(hw); 555 if (lhw->ops->link_info_changed == NULL && 556 lhw->ops->bss_info_changed == NULL) 557 return; 558 559 if (changed == 0) 560 return; 561 562 LKPI_80211_TRACE_MO("hw %p vif %p conf %p changed %#jx", hw, vif, conf, (uintmax_t)changed); 563 if (lhw->ops->link_info_changed != NULL) 564 lhw->ops->link_info_changed(hw, vif, conf, changed); 565 else 566 lhw->ops->bss_info_changed(hw, vif, conf, changed); 567 } 568 569 int 570 lkpi_80211_mo_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 571 uint32_t link_id, uint16_t ac, const struct ieee80211_tx_queue_params *txqp) 572 { 573 struct lkpi_hw *lhw; 574 int error; 575 576 lhw = HW_TO_LHW(hw); 577 if (lhw->ops->conf_tx == NULL) { 578 error = EOPNOTSUPP; 579 goto out; 580 } 581 582 LKPI_80211_TRACE_MO("hw %p vif %p link_id %u ac %u txpq %p", 583 hw, vif, link_id, ac, txqp); 584 error = lhw->ops->conf_tx(hw, vif, link_id, ac, txqp); 585 586 out: 587 return (error); 588 } 589 590 void 591 lkpi_80211_mo_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 592 uint32_t nqueues, bool drop) 593 { 594 struct lkpi_hw *lhw; 595 596 lhw = HW_TO_LHW(hw); 597 if (lhw->ops->flush == NULL) 598 return; 599 600 LKPI_80211_TRACE_MO("hw %p vif %p nqueues %u drop %d", hw, vif, nqueues, drop); 601 lhw->ops->flush(hw, vif, nqueues, drop); 602 } 603 604 void 605 lkpi_80211_mo_mgd_prepare_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 606 struct ieee80211_prep_tx_info *txinfo) 607 { 608 struct lkpi_hw *lhw; 609 610 lhw = HW_TO_LHW(hw); 611 if (lhw->ops->mgd_prepare_tx == NULL) 612 return; 613 614 LKPI_80211_TRACE_MO("hw %p vif %p txinfo %p", hw, vif, txinfo); 615 lhw->ops->mgd_prepare_tx(hw, vif, txinfo); 616 } 617 618 void 619 lkpi_80211_mo_mgd_complete_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 620 struct ieee80211_prep_tx_info *txinfo) 621 { 622 struct lkpi_hw *lhw; 623 624 lhw = HW_TO_LHW(hw); 625 if (lhw->ops->mgd_complete_tx == NULL) 626 return; 627 628 LKPI_80211_TRACE_MO("hw %p vif %p txinfo %p", hw, vif, txinfo); 629 lhw->ops->mgd_complete_tx(hw, vif, txinfo); 630 } 631 632 void 633 lkpi_80211_mo_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *txctrl, 634 struct sk_buff *skb) 635 { 636 struct lkpi_hw *lhw; 637 638 lhw = HW_TO_LHW(hw); 639 if (lhw->ops->tx == NULL) 640 return; 641 642 LKPI_80211_TRACE_MO("hw %p txctrl %p skb %p", hw, txctrl, skb); 643 lhw->ops->tx(hw, txctrl, skb); 644 } 645 646 void 647 lkpi_80211_mo_wake_tx_queue(struct ieee80211_hw *hw, struct ieee80211_txq *txq) 648 { 649 struct lkpi_hw *lhw; 650 651 lhw = HW_TO_LHW(hw); 652 if (lhw->ops->wake_tx_queue == NULL) 653 return; 654 655 LKPI_80211_TRACE_MO("hw %p txq %p", hw, txq); 656 lhw->ops->wake_tx_queue(hw, txq); 657 } 658 659 void 660 lkpi_80211_mo_sync_rx_queues(struct ieee80211_hw *hw) 661 { 662 struct lkpi_hw *lhw; 663 664 lhw = HW_TO_LHW(hw); 665 if (lhw->ops->sync_rx_queues == NULL) 666 return; 667 668 LKPI_80211_TRACE_MO("hw %p", hw); 669 lhw->ops->sync_rx_queues(hw); 670 } 671 672 void 673 lkpi_80211_mo_sta_pre_rcu_remove(struct ieee80211_hw *hw, 674 struct ieee80211_vif *vif, struct ieee80211_sta *sta) 675 { 676 struct lkpi_hw *lhw; 677 678 lhw = HW_TO_LHW(hw); 679 if (lhw->ops->sta_pre_rcu_remove == NULL) 680 return; 681 682 LKPI_80211_TRACE_MO("hw %p vif %p sta %p", hw, vif, sta); 683 lhw->ops->sta_pre_rcu_remove(hw, vif, sta); 684 } 685 686 int 687 lkpi_80211_mo_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, 688 struct ieee80211_vif *vif, struct ieee80211_sta *sta, 689 struct ieee80211_key_conf *kc) 690 { 691 struct lkpi_hw *lhw; 692 int error; 693 694 lockdep_assert_wiphy(hw->wiphy); 695 696 lhw = HW_TO_LHW(hw); 697 if (lhw->ops->set_key == NULL) { 698 error = EOPNOTSUPP; 699 goto out; 700 } 701 702 LKPI_80211_TRACE_MO("hw %p cmd %d vif %p sta %p kc %p", hw, cmd, vif, sta, kc); 703 error = lhw->ops->set_key(hw, cmd, vif, sta, kc); 704 705 out: 706 return (error); 707 } 708 709 int 710 lkpi_80211_mo_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 711 struct ieee80211_ampdu_params *params) 712 { 713 struct lkpi_hw *lhw; 714 int error; 715 716 lhw = HW_TO_LHW(hw); 717 if (lhw->ops->ampdu_action == NULL) { 718 error = EOPNOTSUPP; 719 goto out; 720 } 721 722 LKPI_80211_TRACE_MO("hw %p vif %p params %p { %p, %d, %u, %u, %u, %u, %d }", 723 hw, vif, params, params->sta, params->action, params->buf_size, 724 params->timeout, params->ssn, params->tid, params->amsdu); 725 error = lhw->ops->ampdu_action(hw, vif, params); 726 727 out: 728 return (error); 729 } 730 731 int 732 lkpi_80211_mo_sta_statistics(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 733 struct ieee80211_sta *sta, struct station_info *sinfo) 734 { 735 struct lkpi_hw *lhw; 736 struct lkpi_sta *lsta; 737 int error; 738 739 lhw = HW_TO_LHW(hw); 740 if (lhw->ops->sta_statistics == NULL) { 741 error = EOPNOTSUPP; 742 goto out; 743 } 744 745 lsta = STA_TO_LSTA(sta); 746 if (!lsta->added_to_drv) { 747 error = EEXIST; 748 goto out; 749 } 750 751 lockdep_assert_wiphy(hw->wiphy); 752 753 LKPI_80211_TRACE_MO("hw %p vif %p sta %p sinfo %p", hw, vif, sta, sinfo); 754 lhw->ops->sta_statistics(hw, vif, sta, sinfo); 755 error = 0; 756 757 out: 758 return (error); 759 } 760