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/cdefs.h> 30 __FBSDID("$FreeBSD$"); 31 32 #include <sys/param.h> 33 #include <sys/types.h> 34 #include <sys/kernel.h> 35 #include <sys/errno.h> 36 37 #define LINUXKPI_NET80211 38 #include <net/mac80211.h> 39 40 #include "linux_80211.h" 41 42 int 43 lkpi_80211_mo_start(struct ieee80211_hw *hw) 44 { 45 struct lkpi_hw *lhw; 46 int error; 47 48 lhw = HW_TO_LHW(hw); 49 if (lhw->ops->start == NULL) { 50 error = EOPNOTSUPP; 51 goto out; 52 } 53 54 if ((lhw->sc_flags & LKPI_MAC80211_DRV_STARTED)) { 55 /* Trying to start twice is an error. */ 56 error = EEXIST; 57 goto out; 58 } 59 error = lhw->ops->start(hw); 60 if (error == 0) 61 lhw->sc_flags |= LKPI_MAC80211_DRV_STARTED; 62 63 out: 64 return (error); 65 } 66 67 void 68 lkpi_80211_mo_stop(struct ieee80211_hw *hw) 69 { 70 struct lkpi_hw *lhw; 71 72 lhw = HW_TO_LHW(hw); 73 if (lhw->ops->stop == NULL) 74 return; 75 76 lhw->ops->stop(hw); 77 lhw->sc_flags &= ~LKPI_MAC80211_DRV_STARTED; 78 } 79 80 int 81 lkpi_80211_mo_get_antenna(struct ieee80211_hw *hw, u32 *txs, u32 *rxs) 82 { 83 struct lkpi_hw *lhw; 84 int error; 85 86 lhw = HW_TO_LHW(hw); 87 if (lhw->ops->get_antenna == NULL) { 88 error = EOPNOTSUPP; 89 goto out; 90 } 91 92 error = lhw->ops->get_antenna(hw, txs, rxs); 93 94 out: 95 return (error); 96 } 97 98 int 99 lkpi_80211_mo_set_frag_threshold(struct ieee80211_hw *hw, uint32_t frag_th) 100 { 101 struct lkpi_hw *lhw; 102 int error; 103 104 lhw = HW_TO_LHW(hw); 105 if (lhw->ops->set_frag_threshold == NULL) { 106 error = EOPNOTSUPP; 107 goto out; 108 } 109 110 error = lhw->ops->set_frag_threshold(hw, frag_th); 111 112 out: 113 return (error); 114 } 115 116 int 117 lkpi_80211_mo_set_rts_threshold(struct ieee80211_hw *hw, uint32_t rts_th) 118 { 119 struct lkpi_hw *lhw; 120 int error; 121 122 lhw = HW_TO_LHW(hw); 123 if (lhw->ops->set_rts_threshold == NULL) { 124 error = EOPNOTSUPP; 125 goto out; 126 } 127 128 error = lhw->ops->set_rts_threshold(hw, rts_th); 129 130 out: 131 return (error); 132 } 133 134 135 int 136 lkpi_80211_mo_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) 137 { 138 struct lkpi_hw *lhw; 139 struct lkpi_vif *lvif; 140 int error; 141 142 lhw = HW_TO_LHW(hw); 143 if (lhw->ops->add_interface == NULL) { 144 error = EOPNOTSUPP; 145 goto out; 146 } 147 148 lvif = VIF_TO_LVIF(vif); 149 LKPI_80211_LVIF_LOCK(lvif); 150 if (lvif->added_to_drv) { 151 LKPI_80211_LVIF_UNLOCK(lvif); 152 /* Trying to add twice is an error. */ 153 error = EEXIST; 154 goto out; 155 } 156 LKPI_80211_LVIF_UNLOCK(lvif); 157 158 error = lhw->ops->add_interface(hw, vif); 159 if (error == 0) { 160 LKPI_80211_LVIF_LOCK(lvif); 161 lvif->added_to_drv = true; 162 LKPI_80211_LVIF_UNLOCK(lvif); 163 } 164 165 out: 166 return (error); 167 } 168 169 void 170 lkpi_80211_mo_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) 171 { 172 struct lkpi_hw *lhw; 173 struct lkpi_vif *lvif; 174 175 lhw = HW_TO_LHW(hw); 176 if (lhw->ops->remove_interface == NULL) 177 return; 178 179 lvif = VIF_TO_LVIF(vif); 180 LKPI_80211_LVIF_LOCK(lvif); 181 if (!lvif->added_to_drv) { 182 LKPI_80211_LVIF_UNLOCK(lvif); 183 return; 184 } 185 LKPI_80211_LVIF_UNLOCK(lvif); 186 187 lhw->ops->remove_interface(hw, vif); 188 LKPI_80211_LVIF_LOCK(lvif); 189 lvif->added_to_drv = false; 190 LKPI_80211_LVIF_UNLOCK(lvif); 191 } 192 193 194 int 195 lkpi_80211_mo_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 196 struct ieee80211_scan_request *sr) 197 { 198 struct lkpi_hw *lhw; 199 int error; 200 201 lhw = HW_TO_LHW(hw); 202 if (lhw->ops->hw_scan == NULL) { 203 /* XXX-BZ can we hide other scans like we can for sta_add..? */ 204 error = EOPNOTSUPP; 205 goto out; 206 } 207 208 lhw->scan_flags |= LKPI_LHW_SCAN_RUNNING; 209 error = lhw->ops->hw_scan(hw, vif, sr); 210 if (error != 0) 211 lhw->scan_flags &= ~LKPI_LHW_SCAN_RUNNING; 212 213 out: 214 return (error); 215 } 216 217 void 218 lkpi_80211_mo_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif) 219 { 220 struct lkpi_hw *lhw; 221 222 lhw = HW_TO_LHW(hw); 223 if (lhw->ops->cancel_hw_scan == NULL) 224 return; 225 226 lhw->ops->cancel_hw_scan(hw, vif); 227 } 228 229 void 230 lkpi_80211_mo_sw_scan_complete(struct ieee80211_hw *hw, struct ieee80211_vif *vif) 231 { 232 struct lkpi_hw *lhw; 233 234 lhw = HW_TO_LHW(hw); 235 if (lhw->ops->sw_scan_complete == NULL) 236 return; 237 238 lhw->ops->sw_scan_complete(hw, vif); 239 lhw->scan_flags &= ~LKPI_LHW_SCAN_RUNNING; 240 } 241 242 void 243 lkpi_80211_mo_sw_scan_start(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 244 const u8 *addr) 245 { 246 struct lkpi_hw *lhw; 247 248 lhw = HW_TO_LHW(hw); 249 if (lhw->ops->sw_scan_start == NULL) 250 return; 251 252 lhw->ops->sw_scan_start(hw, vif, addr); 253 } 254 255 256 /* 257 * We keep the Linux type here; it really is an uintptr_t. 258 */ 259 u64 260 lkpi_80211_mo_prepare_multicast(struct ieee80211_hw *hw, 261 struct netdev_hw_addr_list *mc_list) 262 { 263 struct lkpi_hw *lhw; 264 u64 ptr; 265 266 lhw = HW_TO_LHW(hw); 267 if (lhw->ops->prepare_multicast == NULL) 268 return (0); 269 270 ptr = lhw->ops->prepare_multicast(hw, mc_list); 271 return (ptr); 272 } 273 274 void 275 lkpi_80211_mo_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, 276 unsigned int *total_flags, u64 mc_ptr) 277 { 278 struct lkpi_hw *lhw; 279 280 lhw = HW_TO_LHW(hw); 281 if (lhw->ops->configure_filter == NULL) 282 return; 283 284 if (mc_ptr == 0) 285 return; 286 287 lhw->ops->configure_filter(hw, changed_flags, total_flags, mc_ptr); 288 } 289 290 291 /* 292 * So far we only called sta_{add,remove} as an alternative to sta_state. 293 * Let's keep the implementation simpler and hide sta_{add,remove} under the 294 * hood here calling them if state_state is not available from mo_sta_state. 295 */ 296 static int 297 lkpi_80211_mo_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 298 struct ieee80211_sta *sta) 299 { 300 struct lkpi_hw *lhw; 301 struct lkpi_sta *lsta; 302 int error; 303 304 lhw = HW_TO_LHW(hw); 305 if (lhw->ops->sta_add == NULL) { 306 error = EOPNOTSUPP; 307 goto out; 308 } 309 310 lsta = STA_TO_LSTA(sta); 311 if (lsta->added_to_drv) { 312 error = EEXIST; 313 goto out; 314 } 315 316 error = lhw->ops->sta_add(hw, vif, sta); 317 if (error == 0) 318 lsta->added_to_drv = true; 319 320 out: 321 return error; 322 } 323 324 static int 325 lkpi_80211_mo_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 326 struct ieee80211_sta *sta) 327 { 328 struct lkpi_hw *lhw; 329 struct lkpi_sta *lsta; 330 int error; 331 332 lhw = HW_TO_LHW(hw); 333 if (lhw->ops->sta_remove == NULL) { 334 error = EOPNOTSUPP; 335 goto out; 336 } 337 338 lsta = STA_TO_LSTA(sta); 339 if (!lsta->added_to_drv) { 340 /* If we never added the sta, do not complain on cleanup. */ 341 error = 0; 342 goto out; 343 } 344 345 error = lhw->ops->sta_remove(hw, vif, sta); 346 if (error == 0) 347 lsta->added_to_drv = false; 348 349 out: 350 return error; 351 } 352 353 int 354 lkpi_80211_mo_sta_state(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 355 struct ieee80211_sta *sta, enum ieee80211_sta_state nstate) 356 { 357 struct lkpi_hw *lhw; 358 struct lkpi_sta *lsta; 359 int error; 360 361 lhw = HW_TO_LHW(hw); 362 lsta = STA_TO_LSTA(sta); 363 if (lhw->ops->sta_state != NULL) { 364 error = lhw->ops->sta_state(hw, vif, sta, lsta->state, nstate); 365 if (error == 0) { 366 if (nstate == IEEE80211_STA_NOTEXIST) 367 lsta->added_to_drv = false; 368 else 369 lsta->added_to_drv = true; 370 lsta->state = nstate; 371 } 372 goto out; 373 } 374 375 /* XXX-BZ is the change state AUTH or ASSOC here? */ 376 if (lsta->state < IEEE80211_STA_ASSOC && nstate == IEEE80211_STA_ASSOC) { 377 error = lkpi_80211_mo_sta_add(hw, vif, sta); 378 if (error == 0) 379 lsta->added_to_drv = true; 380 } else if (lsta->state >= IEEE80211_STA_ASSOC && 381 nstate < IEEE80211_STA_ASSOC) { 382 error = lkpi_80211_mo_sta_remove(hw, vif, sta); 383 if (error == 0) 384 lsta->added_to_drv = false; 385 } else 386 /* Nothing to do. */ 387 error = 0; 388 if (error == 0) 389 lsta->state = nstate; 390 391 out: 392 /* XXX-BZ should we manage state in here? */ 393 return (error); 394 } 395 396 int 397 lkpi_80211_mo_config(struct ieee80211_hw *hw, uint32_t changed) 398 { 399 struct lkpi_hw *lhw; 400 int error; 401 402 lhw = HW_TO_LHW(hw); 403 if (lhw->ops->config == NULL) { 404 error = EOPNOTSUPP; 405 goto out; 406 } 407 408 error = lhw->ops->config(hw, changed); 409 410 out: 411 return (error); 412 } 413 414 415 int 416 lkpi_80211_mo_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 417 struct ieee80211_chanctx_conf *chanctx_conf) 418 { 419 struct lkpi_hw *lhw; 420 int error; 421 422 lhw = HW_TO_LHW(hw); 423 if (lhw->ops->assign_vif_chanctx == NULL) { 424 error = EOPNOTSUPP; 425 goto out; 426 } 427 428 error = lhw->ops->assign_vif_chanctx(hw, vif, NULL, chanctx_conf); 429 if (error == 0) 430 vif->chanctx_conf = chanctx_conf; 431 432 out: 433 return (error); 434 } 435 436 void 437 lkpi_80211_mo_unassign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 438 struct ieee80211_chanctx_conf **chanctx_conf) 439 { 440 struct lkpi_hw *lhw; 441 442 lhw = HW_TO_LHW(hw); 443 if (lhw->ops->unassign_vif_chanctx == NULL) 444 return; 445 446 if (*chanctx_conf == NULL) 447 return; 448 449 lhw->ops->unassign_vif_chanctx(hw, vif, NULL, *chanctx_conf); 450 *chanctx_conf = NULL; 451 } 452 453 454 int 455 lkpi_80211_mo_add_chanctx(struct ieee80211_hw *hw, 456 struct ieee80211_chanctx_conf *chanctx_conf) 457 { 458 struct lkpi_hw *lhw; 459 int error; 460 461 lhw = HW_TO_LHW(hw); 462 if (lhw->ops->add_chanctx == NULL) { 463 error = EOPNOTSUPP; 464 goto out; 465 } 466 467 error = lhw->ops->add_chanctx(hw, chanctx_conf); 468 469 out: 470 return (error); 471 } 472 473 void 474 lkpi_80211_mo_change_chanctx(struct ieee80211_hw *hw, 475 struct ieee80211_chanctx_conf *chanctx_conf, uint32_t changed) 476 { 477 struct lkpi_hw *lhw; 478 479 lhw = HW_TO_LHW(hw); 480 if (lhw->ops->change_chanctx == NULL) 481 return; 482 483 lhw->ops->change_chanctx(hw, chanctx_conf, changed); 484 } 485 486 void 487 lkpi_80211_mo_remove_chanctx(struct ieee80211_hw *hw, 488 struct ieee80211_chanctx_conf *chanctx_conf) 489 { 490 struct lkpi_hw *lhw; 491 492 lhw = HW_TO_LHW(hw); 493 if (lhw->ops->remove_chanctx == NULL) 494 return; 495 496 lhw->ops->remove_chanctx(hw, chanctx_conf); 497 } 498 499 void 500 lkpi_80211_mo_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 501 struct ieee80211_bss_conf *conf, uint64_t changed) 502 { 503 struct lkpi_hw *lhw; 504 505 lhw = HW_TO_LHW(hw); 506 if (lhw->ops->bss_info_changed == NULL) 507 return; 508 509 lhw->ops->bss_info_changed(hw, vif, conf, changed); 510 } 511 512 513 int 514 lkpi_80211_mo_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 515 uint16_t ac, const struct ieee80211_tx_queue_params *txqp) 516 { 517 struct lkpi_hw *lhw; 518 int error; 519 520 lhw = HW_TO_LHW(hw); 521 if (lhw->ops->conf_tx == NULL) { 522 error = EOPNOTSUPP; 523 goto out; 524 } 525 526 error = lhw->ops->conf_tx(hw, vif, 0, ac, txqp); 527 528 out: 529 return (error); 530 } 531 532 void 533 lkpi_80211_mo_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 534 uint32_t nqueues, bool drop) 535 { 536 struct lkpi_hw *lhw; 537 538 lhw = HW_TO_LHW(hw); 539 if (lhw->ops->flush == NULL) 540 return; 541 542 lhw->ops->flush(hw, vif, nqueues, drop); 543 } 544 545 void 546 lkpi_80211_mo_mgd_prepare_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 547 struct ieee80211_prep_tx_info *txinfo) 548 { 549 struct lkpi_hw *lhw; 550 551 lhw = HW_TO_LHW(hw); 552 if (lhw->ops->mgd_prepare_tx == NULL) 553 return; 554 555 lhw->ops->mgd_prepare_tx(hw, vif, txinfo); 556 } 557 558 void 559 lkpi_80211_mo_mgd_complete_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 560 struct ieee80211_prep_tx_info *txinfo) 561 { 562 struct lkpi_hw *lhw; 563 564 lhw = HW_TO_LHW(hw); 565 if (lhw->ops->mgd_complete_tx == NULL) 566 return; 567 568 lhw->ops->mgd_complete_tx(hw, vif, txinfo); 569 } 570 571 void 572 lkpi_80211_mo_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *txctrl, 573 struct sk_buff *skb) 574 { 575 struct lkpi_hw *lhw; 576 577 lhw = HW_TO_LHW(hw); 578 if (lhw->ops->tx == NULL) 579 return; 580 581 lhw->ops->tx(hw, txctrl, skb); 582 } 583 584 void 585 lkpi_80211_mo_wake_tx_queue(struct ieee80211_hw *hw, struct ieee80211_txq *txq) 586 { 587 struct lkpi_hw *lhw; 588 589 lhw = HW_TO_LHW(hw); 590 if (lhw->ops->wake_tx_queue == NULL) 591 return; 592 593 lhw->ops->wake_tx_queue(hw, txq); 594 } 595 596 void 597 lkpi_80211_mo_sync_rx_queues(struct ieee80211_hw *hw) 598 { 599 struct lkpi_hw *lhw; 600 601 lhw = HW_TO_LHW(hw); 602 if (lhw->ops->sync_rx_queues == NULL) 603 return; 604 605 lhw->ops->sync_rx_queues(hw); 606 } 607 608 void 609 lkpi_80211_mo_sta_pre_rcu_remove(struct ieee80211_hw *hw, 610 struct ieee80211_vif *vif, struct ieee80211_sta *sta) 611 { 612 struct lkpi_hw *lhw; 613 614 lhw = HW_TO_LHW(hw); 615 if (lhw->ops->sta_pre_rcu_remove == NULL) 616 return; 617 618 lhw->ops->sta_pre_rcu_remove(hw, vif, sta); 619 } 620 621 int 622 lkpi_80211_mo_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, 623 struct ieee80211_vif *vif, struct ieee80211_sta *sta, 624 struct ieee80211_key_conf *kc) 625 { 626 struct lkpi_hw *lhw; 627 int error; 628 629 lhw = HW_TO_LHW(hw); 630 if (lhw->ops->set_key == NULL) { 631 error = EOPNOTSUPP; 632 goto out; 633 } 634 635 error = lhw->ops->set_key(hw, cmd, vif, sta, kc); 636 637 out: 638 return (error); 639 } 640