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