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