1 /*- 2 * Copyright (c) 2021-2026 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 might_sleep(); 119 lockdep_assert_wiphy(hw->wiphy); 120 121 lhw = HW_TO_LHW(hw); 122 if (lhw->ops->set_frag_threshold == NULL) { 123 error = EOPNOTSUPP; 124 goto out; 125 } 126 127 LKPI_80211_TRACE_MO("hw %p frag_th %u", hw, frag_th); 128 LKPI_80211_TRACE_MO("TODO link/radio_idx"); 129 error = lhw->ops->set_frag_threshold(hw, 0, frag_th); 130 131 out: 132 return (error); 133 } 134 135 int 136 lkpi_80211_mo_set_rts_threshold(struct ieee80211_hw *hw, uint32_t rts_th) 137 { 138 struct lkpi_hw *lhw; 139 int error; 140 141 might_sleep(); 142 lockdep_assert_wiphy(hw->wiphy); 143 144 lhw = HW_TO_LHW(hw); 145 if (lhw->ops->set_rts_threshold == NULL) { 146 error = EOPNOTSUPP; 147 goto out; 148 } 149 150 LKPI_80211_TRACE_MO("hw %p rts_th %u", hw, rts_th); 151 LKPI_80211_TRACE_MO("TODO link/radio_idx"); 152 error = lhw->ops->set_rts_threshold(hw, 0, rts_th); 153 154 out: 155 return (error); 156 } 157 158 159 int 160 lkpi_80211_mo_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) 161 { 162 struct lkpi_hw *lhw; 163 struct lkpi_vif *lvif; 164 int error; 165 166 lhw = HW_TO_LHW(hw); 167 if (lhw->ops->add_interface == NULL) { 168 error = EOPNOTSUPP; 169 goto out; 170 } 171 172 lvif = VIF_TO_LVIF(vif); 173 LKPI_80211_LVIF_LOCK(lvif); 174 if (lvif->added_to_drv) { 175 LKPI_80211_LVIF_UNLOCK(lvif); 176 /* Trying to add twice is an error. */ 177 error = EEXIST; 178 goto out; 179 } 180 LKPI_80211_LVIF_UNLOCK(lvif); 181 182 LKPI_80211_TRACE_MO("hw %p vif %p", hw, vif); 183 error = lhw->ops->add_interface(hw, vif); 184 if (error == 0) { 185 LKPI_80211_LVIF_LOCK(lvif); 186 lvif->added_to_drv = true; 187 LKPI_80211_LVIF_UNLOCK(lvif); 188 } 189 190 out: 191 return (error); 192 } 193 194 void 195 lkpi_80211_mo_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) 196 { 197 struct lkpi_hw *lhw; 198 struct lkpi_vif *lvif; 199 200 lhw = HW_TO_LHW(hw); 201 if (lhw->ops->remove_interface == NULL) 202 return; 203 204 lvif = VIF_TO_LVIF(vif); 205 LKPI_80211_LVIF_LOCK(lvif); 206 if (!lvif->added_to_drv) { 207 LKPI_80211_LVIF_UNLOCK(lvif); 208 return; 209 } 210 LKPI_80211_LVIF_UNLOCK(lvif); 211 212 LKPI_80211_TRACE_MO("hw %p vif %p", hw, vif); 213 lhw->ops->remove_interface(hw, vif); 214 LKPI_80211_LVIF_LOCK(lvif); 215 lvif->added_to_drv = false; 216 LKPI_80211_LVIF_UNLOCK(lvif); 217 } 218 219 220 int 221 lkpi_80211_mo_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 222 struct ieee80211_scan_request *sr) 223 { 224 struct lkpi_hw *lhw; 225 int error; 226 227 lockdep_assert_wiphy(hw->wiphy); 228 229 /* 230 * MUST NOT return EPERM as that is a "magic number 1" based on rtw88 231 * driver indicating hw_scan is not supported despite the ops call 232 * being available. 233 */ 234 235 lhw = HW_TO_LHW(hw); 236 if (lhw->ops->hw_scan == NULL) { 237 /* Return magic number to use sw scan. */ 238 error = 1; 239 goto out; 240 } 241 242 LKPI_80211_TRACE_MO("CALLING hw %p vif %p sr %p", hw, vif, sr); 243 error = lhw->ops->hw_scan(hw, vif, sr); 244 LKPI_80211_TRACE_MO("RETURNING hw %p vif %p sr %p error %d", hw, vif, sr, error); 245 246 out: 247 return (error); 248 } 249 250 void 251 lkpi_80211_mo_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif) 252 { 253 struct lkpi_hw *lhw; 254 255 lockdep_assert_wiphy(hw->wiphy); 256 257 lhw = HW_TO_LHW(hw); 258 if (lhw->ops->cancel_hw_scan == NULL) 259 return; 260 261 LKPI_80211_TRACE_MO("hw %p vif %p", hw, vif); 262 lhw->ops->cancel_hw_scan(hw, vif); 263 } 264 265 void 266 lkpi_80211_mo_sw_scan_complete(struct ieee80211_hw *hw, struct ieee80211_vif *vif) 267 { 268 struct lkpi_hw *lhw; 269 270 lhw = HW_TO_LHW(hw); 271 if (lhw->ops->sw_scan_complete == NULL) 272 return; 273 274 LKPI_80211_TRACE_MO("hw %p vif %p", hw, vif); 275 lhw->ops->sw_scan_complete(hw, vif); 276 lhw->scan_flags &= ~LKPI_LHW_SCAN_RUNNING; 277 } 278 279 void 280 lkpi_80211_mo_sw_scan_start(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 281 const u8 *addr) 282 { 283 struct lkpi_hw *lhw; 284 285 lhw = HW_TO_LHW(hw); 286 if (lhw->ops->sw_scan_start == NULL) 287 return; 288 289 LKPI_80211_TRACE_MO("hw %p vif %p", hw, vif); 290 lhw->ops->sw_scan_start(hw, vif, addr); 291 } 292 293 294 /* 295 * We keep the Linux type here; it really is an uintptr_t. 296 */ 297 u64 298 lkpi_80211_mo_prepare_multicast(struct ieee80211_hw *hw, 299 struct netdev_hw_addr_list *mc_list) 300 { 301 struct lkpi_hw *lhw; 302 u64 ptr; 303 304 /* This seems fine without the wiphy lock. */ 305 306 lhw = HW_TO_LHW(hw); 307 if (lhw->ops->prepare_multicast == NULL) 308 return (0); 309 310 LKPI_80211_TRACE_MO("hw %p mc_list %p", hw, mc_list); 311 ptr = lhw->ops->prepare_multicast(hw, mc_list); 312 return (ptr); 313 } 314 315 void 316 lkpi_80211_mo_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, 317 unsigned int *total_flags, u64 mc_ptr) 318 { 319 struct lkpi_hw *lhw; 320 321 lockdep_assert_wiphy(hw->wiphy); 322 323 lhw = HW_TO_LHW(hw); 324 if (lhw->ops->configure_filter == NULL) 325 return; 326 327 LKPI_80211_TRACE_MO("hw %p changed_flags %#x total_flags %p mc_ptr %ju", hw, changed_flags, total_flags, (uintmax_t)mc_ptr); 328 lhw->ops->configure_filter(hw, changed_flags, total_flags, mc_ptr); 329 } 330 331 332 /* 333 * So far we only called sta_{add,remove} as an alternative to sta_state. 334 * Let's keep the implementation simpler and hide sta_{add,remove} under the 335 * hood here calling them if state_state is not available from mo_sta_state. 336 */ 337 static int 338 lkpi_80211_mo_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 339 struct ieee80211_sta *sta) 340 { 341 struct lkpi_hw *lhw; 342 struct lkpi_sta *lsta; 343 int error; 344 345 lhw = HW_TO_LHW(hw); 346 if (lhw->ops->sta_add == NULL) { 347 error = EOPNOTSUPP; 348 goto out; 349 } 350 351 lsta = STA_TO_LSTA(sta); 352 if (lsta->added_to_drv) { 353 error = EEXIST; 354 goto out; 355 } 356 357 LKPI_80211_TRACE_MO("hw %p vif %p sta %p", hw, vif, sta); 358 error = lhw->ops->sta_add(hw, vif, sta); 359 if (error == 0) 360 lsta->added_to_drv = true; 361 362 out: 363 return error; 364 } 365 366 static int 367 lkpi_80211_mo_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 368 struct ieee80211_sta *sta) 369 { 370 struct lkpi_hw *lhw; 371 struct lkpi_sta *lsta; 372 int error; 373 374 lhw = HW_TO_LHW(hw); 375 if (lhw->ops->sta_remove == NULL) { 376 error = EOPNOTSUPP; 377 goto out; 378 } 379 380 lsta = STA_TO_LSTA(sta); 381 if (!lsta->added_to_drv) { 382 /* If we never added the sta, do not complain on cleanup. */ 383 error = 0; 384 goto out; 385 } 386 387 LKPI_80211_TRACE_MO("hw %p vif %p sta %p", hw, vif, sta); 388 error = lhw->ops->sta_remove(hw, vif, sta); 389 if (error == 0) 390 lsta->added_to_drv = false; 391 392 out: 393 return error; 394 } 395 396 int 397 lkpi_80211_mo_sta_state(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 398 struct lkpi_sta *lsta, enum ieee80211_sta_state nstate) 399 { 400 struct lkpi_hw *lhw; 401 struct ieee80211_sta *sta; 402 int error; 403 404 lhw = HW_TO_LHW(hw); 405 sta = LSTA_TO_STA(lsta); 406 if (lhw->ops->sta_state != NULL) { 407 LKPI_80211_TRACE_MO("hw %p vif %p sta %p nstate %d", hw, vif, sta, nstate); 408 error = lhw->ops->sta_state(hw, vif, sta, lsta->state, nstate); 409 if (error == 0) { 410 if (nstate == IEEE80211_STA_NOTEXIST) 411 lsta->added_to_drv = false; 412 else 413 lsta->added_to_drv = true; 414 lsta->state = nstate; 415 } 416 goto out; 417 } 418 419 /* XXX-BZ is the change state AUTH or ASSOC here? */ 420 if (lsta->state < IEEE80211_STA_ASSOC && nstate == IEEE80211_STA_ASSOC) { 421 error = lkpi_80211_mo_sta_add(hw, vif, sta); 422 if (error == 0) 423 lsta->added_to_drv = true; 424 } else if (lsta->state >= IEEE80211_STA_ASSOC && 425 nstate < IEEE80211_STA_ASSOC) { 426 error = lkpi_80211_mo_sta_remove(hw, vif, sta); 427 if (error == 0) 428 lsta->added_to_drv = false; 429 } else 430 /* Nothing to do. */ 431 error = 0; 432 if (error == 0) 433 lsta->state = nstate; 434 435 out: 436 /* XXX-BZ should we manage state in here? */ 437 return (error); 438 } 439 440 int 441 lkpi_80211_mo_config(struct ieee80211_hw *hw, uint32_t changed) 442 { 443 struct lkpi_hw *lhw; 444 int error; 445 446 lockdep_assert_wiphy(hw->wiphy); 447 448 lhw = HW_TO_LHW(hw); 449 if (lhw->ops->config == NULL) { 450 error = EOPNOTSUPP; 451 goto out; 452 } 453 454 LKPI_80211_TRACE_MO("hw %p changed %u", hw, changed); 455 LKPI_80211_TRACE_MO("TODO link/radio_idx"); 456 error = lhw->ops->config(hw, 0, changed); 457 458 out: 459 return (error); 460 } 461 462 463 int 464 lkpi_80211_mo_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 465 struct ieee80211_bss_conf *conf, struct ieee80211_chanctx_conf *chanctx_conf) 466 { 467 struct lkpi_hw *lhw; 468 int error; 469 470 lhw = HW_TO_LHW(hw); 471 if (lhw->ops->assign_vif_chanctx == NULL) { 472 error = EOPNOTSUPP; 473 goto out; 474 } 475 476 LKPI_80211_TRACE_MO("hw %p vif %p bss_conf %p chanctx_conf %p", 477 hw, vif, conf, chanctx_conf); 478 error = lhw->ops->assign_vif_chanctx(hw, vif, conf, chanctx_conf); 479 if (error == 0) 480 vif->bss_conf.chanctx_conf = chanctx_conf; 481 482 out: 483 return (error); 484 } 485 486 void 487 lkpi_80211_mo_unassign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 488 struct ieee80211_bss_conf *conf, struct ieee80211_chanctx_conf *chanctx_conf) 489 { 490 struct lkpi_hw *lhw; 491 492 might_sleep(); 493 lockdep_assert_wiphy(hw->wiphy); 494 495 lhw = HW_TO_LHW(hw); 496 if (lhw->ops->unassign_vif_chanctx == NULL) 497 return; 498 499 if (chanctx_conf == NULL) 500 return; 501 502 LKPI_80211_TRACE_MO("hw %p vif %p bss_conf %p chanctx_conf %p", 503 hw, vif, conf, chanctx_conf); 504 lhw->ops->unassign_vif_chanctx(hw, vif, conf, chanctx_conf); 505 } 506 507 508 int 509 lkpi_80211_mo_add_chanctx(struct ieee80211_hw *hw, 510 struct ieee80211_chanctx_conf *chanctx_conf) 511 { 512 struct lkpi_hw *lhw; 513 struct lkpi_chanctx *lchanctx; 514 int error; 515 516 might_sleep(); 517 lockdep_assert_wiphy(hw->wiphy); 518 519 lhw = HW_TO_LHW(hw); 520 if (lhw->ops->add_chanctx == NULL) { 521 error = EOPNOTSUPP; 522 goto out; 523 } 524 525 LKPI_80211_TRACE_MO("hw %p chanctx_conf %p", hw, chanctx_conf); 526 error = lhw->ops->add_chanctx(hw, chanctx_conf); 527 if (error == 0) { 528 lchanctx = CHANCTX_CONF_TO_LCHANCTX(chanctx_conf); 529 lchanctx->added_to_drv = true; 530 } 531 532 out: 533 return (error); 534 } 535 536 void 537 lkpi_80211_mo_change_chanctx(struct ieee80211_hw *hw, 538 struct ieee80211_chanctx_conf *chanctx_conf, uint32_t changed) 539 { 540 struct lkpi_hw *lhw; 541 542 might_sleep(); 543 lockdep_assert_wiphy(hw->wiphy); 544 545 lhw = HW_TO_LHW(hw); 546 if (lhw->ops->change_chanctx == NULL) 547 return; 548 549 LKPI_80211_TRACE_MO("hw %p chanctx_conf %p changed %u", hw, chanctx_conf, changed); 550 lhw->ops->change_chanctx(hw, chanctx_conf, changed); 551 } 552 553 void 554 lkpi_80211_mo_remove_chanctx(struct ieee80211_hw *hw, 555 struct ieee80211_chanctx_conf *chanctx_conf) 556 { 557 struct lkpi_hw *lhw; 558 struct lkpi_chanctx *lchanctx; 559 560 might_sleep(); 561 lockdep_assert_wiphy(hw->wiphy); 562 563 lhw = HW_TO_LHW(hw); 564 if (lhw->ops->remove_chanctx == NULL) 565 return; 566 567 LKPI_80211_TRACE_MO("hw %p chanctx_conf %p", hw, chanctx_conf); 568 lhw->ops->remove_chanctx(hw, chanctx_conf); 569 lchanctx = CHANCTX_CONF_TO_LCHANCTX(chanctx_conf); 570 lchanctx->added_to_drv = false; 571 } 572 573 void 574 lkpi_80211_mo_vif_cfg_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 575 uint64_t vif_cfg_bits, bool fallback) 576 { 577 struct lkpi_hw *lhw; 578 579 might_sleep(); 580 /* XXX-FINISH all callers for lockdep_assert_wiphy(hw->wiphy); */ 581 582 lhw = HW_TO_LHW(hw); 583 if (lhw->ops->vif_cfg_changed == NULL && 584 lhw->ops->bss_info_changed == NULL) 585 return; 586 587 if (vif_cfg_bits == 0) 588 return; 589 590 LKPI_80211_TRACE_MO("hw %p vif %p vif_cfg_bits %#jx", hw, vif, (uintmax_t)vif_cfg_bits); 591 if (lhw->ops->link_info_changed != NULL) 592 lhw->ops->vif_cfg_changed(hw, vif, vif_cfg_bits); 593 else if (fallback) 594 lhw->ops->bss_info_changed(hw, vif, &vif->bss_conf, vif_cfg_bits); 595 } 596 597 void 598 lkpi_80211_mo_link_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 599 struct ieee80211_bss_conf *conf, uint64_t link_info_bits, uint8_t link_id, 600 bool fallback) 601 { 602 struct lkpi_hw *lhw; 603 604 might_sleep(); 605 /* XXX-FINISH all callers for lockdep_assert_wiphy(hw->wiphy); */ 606 607 lhw = HW_TO_LHW(hw); 608 if (lhw->ops->link_info_changed == NULL && 609 lhw->ops->bss_info_changed == NULL) 610 return; 611 612 if (link_info_bits == 0) 613 return; 614 615 if (!ieee80211_vif_link_active(vif, link_id)) 616 return; 617 618 LKPI_80211_TRACE_MO("hw %p vif %p conf %p link_info_bits %#jx", hw, vif, conf, (uintmax_t)link_info_bits); 619 if (lhw->ops->link_info_changed != NULL) 620 lhw->ops->link_info_changed(hw, vif, conf, link_info_bits); 621 else if (fallback) 622 lhw->ops->bss_info_changed(hw, vif, conf, link_info_bits); 623 } 624 625 /* 626 * This is basically obsolete but one caller. 627 * The functionality is now split between lkpi_80211_mo_link_info_changed() and 628 * lkpi_80211_mo_vif_cfg_changed(). Those functions have a flag whether to call 629 * the (*bss_info_changed) fallback or not. See lkpi_bss_info_change(). 630 */ 631 void 632 lkpi_80211_mo_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 633 struct ieee80211_bss_conf *conf, uint64_t bss_changed) 634 { 635 struct lkpi_hw *lhw; 636 637 /* XXX-FINISH all callers for lockdep_assert_wiphy(hw->wiphy); */ 638 639 lhw = HW_TO_LHW(hw); 640 if (lhw->ops->bss_info_changed == NULL) 641 return; 642 643 if (bss_changed == 0) 644 return; 645 646 LKPI_80211_TRACE_MO("hw %p vif %p conf %p changed %#jx", hw, vif, conf, (uintmax_t)bss_changed); 647 lhw->ops->bss_info_changed(hw, vif, conf, bss_changed); 648 } 649 650 int 651 lkpi_80211_mo_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 652 uint32_t link_id, uint16_t ac, const struct ieee80211_tx_queue_params *txqp) 653 { 654 struct lkpi_hw *lhw; 655 int error; 656 657 lhw = HW_TO_LHW(hw); 658 if (lhw->ops->conf_tx == NULL) { 659 error = EOPNOTSUPP; 660 goto out; 661 } 662 663 LKPI_80211_TRACE_MO("hw %p vif %p link_id %u ac %u txpq %p", 664 hw, vif, link_id, ac, txqp); 665 error = lhw->ops->conf_tx(hw, vif, link_id, ac, txqp); 666 667 out: 668 return (error); 669 } 670 671 void 672 lkpi_80211_mo_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 673 uint32_t nqueues, bool drop) 674 { 675 struct lkpi_hw *lhw; 676 677 lhw = HW_TO_LHW(hw); 678 if (lhw->ops->flush == NULL) 679 return; 680 681 LKPI_80211_TRACE_MO("hw %p vif %p nqueues %u drop %d", hw, vif, nqueues, drop); 682 lhw->ops->flush(hw, vif, nqueues, drop); 683 } 684 685 void 686 lkpi_80211_mo_mgd_prepare_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 687 struct ieee80211_prep_tx_info *txinfo) 688 { 689 struct lkpi_hw *lhw; 690 691 lhw = HW_TO_LHW(hw); 692 if (lhw->ops->mgd_prepare_tx == NULL) 693 return; 694 695 LKPI_80211_TRACE_MO("hw %p vif %p txinfo %p", hw, vif, txinfo); 696 lhw->ops->mgd_prepare_tx(hw, vif, txinfo); 697 } 698 699 void 700 lkpi_80211_mo_mgd_complete_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 701 struct ieee80211_prep_tx_info *txinfo) 702 { 703 struct lkpi_hw *lhw; 704 705 lhw = HW_TO_LHW(hw); 706 if (lhw->ops->mgd_complete_tx == NULL) 707 return; 708 709 LKPI_80211_TRACE_MO("hw %p vif %p txinfo %p", hw, vif, txinfo); 710 lhw->ops->mgd_complete_tx(hw, vif, txinfo); 711 } 712 713 void 714 lkpi_80211_mo_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *txctrl, 715 struct sk_buff *skb) 716 { 717 struct lkpi_hw *lhw; 718 719 lhw = HW_TO_LHW(hw); 720 if (lhw->ops->tx == NULL) 721 return; 722 723 LKPI_80211_TRACE_MO("hw %p txctrl %p skb %p", hw, txctrl, skb); 724 lhw->ops->tx(hw, txctrl, skb); 725 } 726 727 void 728 lkpi_80211_mo_wake_tx_queue(struct ieee80211_hw *hw, struct ieee80211_txq *txq, 729 bool schedule) 730 { 731 struct lkpi_hw *lhw; 732 733 lhw = HW_TO_LHW(hw); 734 735 /* Do the schedule before the check for wake_tx_queue supported! */ 736 if (schedule) 737 ieee80211_schedule_txq(hw, txq); 738 739 if (lhw->ops->wake_tx_queue == NULL) 740 return; 741 742 LKPI_80211_TRACE_MO("hw %p txq %p", hw, txq); 743 lhw->ops->wake_tx_queue(hw, txq); 744 } 745 746 void 747 lkpi_80211_mo_sync_rx_queues(struct ieee80211_hw *hw) 748 { 749 struct lkpi_hw *lhw; 750 751 lhw = HW_TO_LHW(hw); 752 if (lhw->ops->sync_rx_queues == NULL) 753 return; 754 755 LKPI_80211_TRACE_MO("hw %p", hw); 756 lhw->ops->sync_rx_queues(hw); 757 } 758 759 void 760 lkpi_80211_mo_sta_pre_rcu_remove(struct ieee80211_hw *hw, 761 struct ieee80211_vif *vif, struct ieee80211_sta *sta) 762 { 763 struct lkpi_hw *lhw; 764 765 lhw = HW_TO_LHW(hw); 766 if (lhw->ops->sta_pre_rcu_remove == NULL) 767 return; 768 769 LKPI_80211_TRACE_MO("hw %p vif %p sta %p", hw, vif, sta); 770 lhw->ops->sta_pre_rcu_remove(hw, vif, sta); 771 } 772 773 void 774 lkpi_80211_mo_link_sta_rc_update(struct ieee80211_hw *hw, 775 struct ieee80211_vif *vif, struct ieee80211_link_sta *link_sta, 776 enum ieee80211_rate_control_changed_flags rc_changed) 777 { 778 struct lkpi_hw *lhw; 779 780 lhw = HW_TO_LHW(hw); 781 if (lhw->ops->link_sta_rc_update == NULL) 782 return; 783 784 LKPI_80211_TRACE_MO("hw %p vif %p link_sta %p rc_changed %#010x", 785 hw, vif, link_sta, rc_changed); 786 lhw->ops->link_sta_rc_update(hw, vif, link_sta, rc_changed); 787 } 788 789 int 790 lkpi_80211_mo_set_bitrate_mask(struct ieee80211_hw *hw, 791 struct ieee80211_vif *vif, const struct cfg80211_bitrate_mask *br_mask) 792 { 793 struct lkpi_hw *lhw; 794 int error; 795 796 might_sleep(); 797 lockdep_assert_wiphy(hw->wiphy); 798 799 lhw = HW_TO_LHW(hw); 800 if (lhw->ops->set_bitrate_mask == NULL) { 801 error = EOPNOTSUPP; 802 goto out; 803 } 804 805 LKPI_80211_TRACE_MO("hw %p vif %p br_mask %p", 806 hw, vif, br_mask); 807 error = lhw->ops->set_bitrate_mask(hw, vif, br_mask); 808 809 out: 810 return (error); 811 } 812 813 int 814 lkpi_80211_mo_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, 815 struct ieee80211_vif *vif, struct ieee80211_sta *sta, 816 struct ieee80211_key_conf *kc) 817 { 818 struct lkpi_hw *lhw; 819 int error; 820 821 lockdep_assert_wiphy(hw->wiphy); 822 823 lhw = HW_TO_LHW(hw); 824 if (lhw->ops->set_key == NULL) { 825 error = EOPNOTSUPP; 826 goto out; 827 } 828 829 /* 830 * Drivers will apply different logic depending on sta being set 831 * here or not and that depends on whether we have an address or 832 * not. wpa_spplucoant::driver_bsd::bsd_set_key() will set a 833 * broadcast address if we do not have one; further up in 834 * wpa_supplicant something presumably sets the broadcast address 835 * for group keys as well. 836 * We have to "undo" this here and set sta to NULL to avoid 837 * problems with hw_crypto in various drivers. 838 * We do this here so all set_key calls for (SET_KEY and DISABLE_KEY) 839 * are covered. 840 */ 841 MPASS(kc->_k != NULL); 842 if (is_broadcast_ether_addr(kc->_k->wk_macaddr)) 843 sta = NULL; 844 845 LKPI_80211_TRACE_MO("hw %p cmd %d vif %p sta %p kc %p", hw, cmd, vif, sta, kc); 846 error = lhw->ops->set_key(hw, cmd, vif, sta, kc); 847 848 out: 849 return (error); 850 } 851 852 void 853 lkpi_80211_mo_sta_set_decap_offload(struct ieee80211_hw *hw, 854 struct ieee80211_vif *vif, struct ieee80211_sta *sta, 855 bool enable) 856 { 857 struct lkpi_hw *lhw; 858 859 lockdep_assert_wiphy(hw->wiphy); 860 861 lhw = HW_TO_LHW(hw); 862 if (lhw->ops->sta_set_decap_offload == NULL) 863 return; 864 865 LKPI_80211_TRACE_MO("hw %p vif %p sta %p enable %d", hw, vif, sta, enable); 866 lhw->ops->sta_set_decap_offload(hw, vif, sta, enable); 867 } 868 869 int 870 lkpi_80211_mo_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 871 struct ieee80211_ampdu_params *params) 872 { 873 struct lkpi_hw *lhw; 874 int error; 875 876 lhw = HW_TO_LHW(hw); 877 if (lhw->ops->ampdu_action == NULL) { 878 error = EOPNOTSUPP; 879 goto out; 880 } 881 882 LKPI_80211_TRACE_MO("hw %p vif %p params %p { %p, %d, %u, %u, %u, %u, %d }", 883 hw, vif, params, params->sta, params->action, params->buf_size, 884 params->timeout, params->ssn, params->tid, params->amsdu); 885 error = lhw->ops->ampdu_action(hw, vif, params); 886 887 out: 888 return (error); 889 } 890 891 int 892 lkpi_80211_mo_sta_statistics(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 893 struct ieee80211_sta *sta, struct station_info *sinfo) 894 { 895 struct lkpi_hw *lhw; 896 struct lkpi_sta *lsta; 897 int error; 898 899 lhw = HW_TO_LHW(hw); 900 if (lhw->ops->sta_statistics == NULL) { 901 error = EOPNOTSUPP; 902 goto out; 903 } 904 905 lsta = STA_TO_LSTA(sta); 906 if (!lsta->added_to_drv) { 907 error = EEXIST; 908 goto out; 909 } 910 911 lockdep_assert_wiphy(hw->wiphy); 912 913 LKPI_80211_TRACE_MO("hw %p vif %p sta %p sinfo %p", hw, vif, sta, sinfo); 914 lhw->ops->sta_statistics(hw, vif, sta, sinfo); 915 error = 0; 916 917 out: 918 return (error); 919 } 920 921 int 922 lkpi_80211_mo_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) 923 { 924 struct lkpi_hw *lhw; 925 int error; 926 927 might_sleep(); 928 lockdep_assert_wiphy(hw->wiphy); 929 930 lhw = HW_TO_LHW(hw); 931 if (lhw->ops->suspend == NULL) { 932 error = EOPNOTSUPP; 933 goto out; 934 } 935 936 LKPI_80211_TRACE_MO("hw %p wowlan %p", hw, wowlan); 937 error = lhw->ops->suspend(hw, wowlan); 938 939 out: 940 return (error); 941 } 942 943 int 944 lkpi_80211_mo_resume(struct ieee80211_hw *hw) 945 { 946 struct lkpi_hw *lhw; 947 int error; 948 949 might_sleep(); 950 lockdep_assert_wiphy(hw->wiphy); 951 952 lhw = HW_TO_LHW(hw); 953 if (lhw->ops->resume == NULL) { 954 error = EOPNOTSUPP; 955 goto out; 956 } 957 958 LKPI_80211_TRACE_MO("hw %p", hw); 959 error = lhw->ops->resume(hw); 960 961 out: 962 return (error); 963 } 964 965 int 966 lkpi_80211_mo_set_wakeup(struct ieee80211_hw *hw, bool enable) 967 { 968 struct lkpi_hw *lhw; 969 int error; 970 971 might_sleep(); 972 lockdep_assert_wiphy(hw->wiphy); 973 974 lhw = HW_TO_LHW(hw); 975 if (lhw->ops->set_wakeup == NULL) { 976 error = EOPNOTSUPP; 977 goto out; 978 } 979 980 LKPI_80211_TRACE_MO("hw %p enable %d", hw, enable); 981 lhw->ops->set_wakeup(hw, enable); 982 error = 0; 983 984 out: 985 return (error); 986 } 987 988 int 989 lkpi_80211_mo_set_rekey_data(struct ieee80211_hw *hw, 990 struct ieee80211_vif *vif, struct cfg80211_gtk_rekey_data *grd) 991 { 992 struct lkpi_hw *lhw; 993 int error; 994 995 might_sleep(); 996 lockdep_assert_wiphy(hw->wiphy); 997 998 lhw = HW_TO_LHW(hw); 999 if (lhw->ops->set_rekey_data == NULL) { 1000 error = EOPNOTSUPP; 1001 goto out; 1002 } 1003 1004 LKPI_80211_TRACE_MO("hw %p vif %p grd %p", hw, vif, grd); 1005 lhw->ops->set_rekey_data(hw, vif, grd); 1006 error = 0; 1007 1008 out: 1009 return (error); 1010 } 1011 1012 int 1013 lkpi_80211_mo_set_default_unicast_key(struct ieee80211_hw *hw, 1014 struct ieee80211_vif *vif, int idx) 1015 { 1016 struct lkpi_hw *lhw; 1017 int error; 1018 1019 might_sleep(); 1020 lockdep_assert_wiphy(hw->wiphy); 1021 1022 lhw = HW_TO_LHW(hw); 1023 if (lhw->ops->set_default_unicast_key == NULL) { 1024 error = EOPNOTSUPP; 1025 goto out; 1026 } 1027 1028 LKPI_80211_TRACE_MO("hw %p vif %p idx %d", hw, vif, idx); 1029 lhw->ops->set_default_unicast_key(hw, vif, idx); 1030 error = 0; 1031 1032 out: 1033 return (error); 1034 } 1035 1036