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