1 /*- 2 * Copyright (c) 2021 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 hid 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 else if (lsta->state >= IEEE80211_STA_ASSOC && 361 nstate < IEEE80211_STA_ASSOC) 362 error = lkpi_80211_mo_sta_remove(hw, vif, sta); 363 else 364 /* Nothing to do. */ 365 error = 0; 366 367 out: 368 /* XXX-BZ should we manage state in here? */ 369 return (error); 370 } 371 372 int 373 lkpi_80211_mo_config(struct ieee80211_hw *hw, uint32_t changed) 374 { 375 struct lkpi_hw *lhw; 376 int error; 377 378 lhw = HW_TO_LHW(hw); 379 if (lhw->ops->config == NULL) { 380 error = EOPNOTSUPP; 381 goto out; 382 } 383 384 error = lhw->ops->config(hw, changed); 385 386 out: 387 return (error); 388 } 389 390 391 int 392 lkpi_80211_mo_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 393 struct ieee80211_chanctx_conf *chanctx_conf) 394 { 395 struct lkpi_hw *lhw; 396 int error; 397 398 lhw = HW_TO_LHW(hw); 399 if (lhw->ops->assign_vif_chanctx == NULL) { 400 error = EOPNOTSUPP; 401 goto out; 402 } 403 404 error = lhw->ops->assign_vif_chanctx(hw, vif, chanctx_conf); 405 if (error == 0) 406 vif->chanctx_conf = chanctx_conf; 407 408 out: 409 return (error); 410 } 411 412 void 413 lkpi_80211_mo_unassign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 414 struct ieee80211_chanctx_conf **chanctx_conf) 415 { 416 struct lkpi_hw *lhw; 417 418 lhw = HW_TO_LHW(hw); 419 if (lhw->ops->unassign_vif_chanctx == NULL) 420 return; 421 422 if (*chanctx_conf == NULL) 423 return; 424 425 lhw->ops->unassign_vif_chanctx(hw, vif, *chanctx_conf); 426 *chanctx_conf = NULL; 427 } 428 429 430 int 431 lkpi_80211_mo_add_chanctx(struct ieee80211_hw *hw, 432 struct ieee80211_chanctx_conf *chanctx_conf) 433 { 434 struct lkpi_hw *lhw; 435 int error; 436 437 lhw = HW_TO_LHW(hw); 438 if (lhw->ops->add_chanctx == NULL) { 439 error = EOPNOTSUPP; 440 goto out; 441 } 442 443 error = lhw->ops->add_chanctx(hw, chanctx_conf); 444 445 out: 446 return (error); 447 } 448 449 void 450 lkpi_80211_mo_change_chanctx(struct ieee80211_hw *hw, 451 struct ieee80211_chanctx_conf *chanctx_conf, uint32_t changed) 452 { 453 struct lkpi_hw *lhw; 454 455 lhw = HW_TO_LHW(hw); 456 if (lhw->ops->change_chanctx == NULL) 457 return; 458 459 lhw->ops->change_chanctx(hw, chanctx_conf, changed); 460 } 461 462 void 463 lkpi_80211_mo_remove_chanctx(struct ieee80211_hw *hw, 464 struct ieee80211_chanctx_conf *chanctx_conf) 465 { 466 struct lkpi_hw *lhw; 467 468 lhw = HW_TO_LHW(hw); 469 if (lhw->ops->remove_chanctx == NULL) 470 return; 471 472 lhw->ops->remove_chanctx(hw, chanctx_conf); 473 } 474 475 void 476 lkpi_80211_mo_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 477 struct ieee80211_bss_conf *conf, uint32_t changed) 478 { 479 struct lkpi_hw *lhw; 480 481 lhw = HW_TO_LHW(hw); 482 if (lhw->ops->bss_info_changed == NULL) 483 return; 484 485 lhw->ops->bss_info_changed(hw, vif, conf, changed); 486 } 487 488 489 int 490 lkpi_80211_mo_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 491 uint16_t ac, const struct ieee80211_tx_queue_params *txqp) 492 { 493 struct lkpi_hw *lhw; 494 int error; 495 496 lhw = HW_TO_LHW(hw); 497 if (lhw->ops->conf_tx == NULL) { 498 error = EOPNOTSUPP; 499 goto out; 500 } 501 502 error = lhw->ops->conf_tx(hw, vif, ac, txqp); 503 504 out: 505 return (error); 506 } 507 508 void 509 lkpi_80211_mo_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 510 uint32_t nqueues, bool drop) 511 { 512 struct lkpi_hw *lhw; 513 514 lhw = HW_TO_LHW(hw); 515 if (lhw->ops->flush == NULL) 516 return; 517 518 lhw->ops->flush(hw, vif, nqueues, drop); 519 } 520 521 void 522 lkpi_80211_mo_mgd_prepare_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 523 struct ieee80211_prep_tx_info *txinfo) 524 { 525 struct lkpi_hw *lhw; 526 527 lhw = HW_TO_LHW(hw); 528 if (lhw->ops->mgd_prepare_tx == NULL) 529 return; 530 531 lhw->ops->mgd_prepare_tx(hw, vif, txinfo); 532 } 533 534 void 535 lkpi_80211_mo_mgd_complete_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 536 struct ieee80211_prep_tx_info *txinfo) 537 { 538 struct lkpi_hw *lhw; 539 540 lhw = HW_TO_LHW(hw); 541 if (lhw->ops->mgd_complete_tx == NULL) 542 return; 543 544 lhw->ops->mgd_complete_tx(hw, vif, txinfo); 545 } 546 547 void 548 lkpi_80211_mo_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *txctrl, 549 struct sk_buff *skb) 550 { 551 struct lkpi_hw *lhw; 552 553 lhw = HW_TO_LHW(hw); 554 if (lhw->ops->tx == NULL) 555 return; 556 557 lhw->ops->tx(hw, txctrl, skb); 558 } 559 560 void 561 lkpi_80211_mo_wake_tx_queue(struct ieee80211_hw *hw, struct ieee80211_txq *txq) 562 { 563 struct lkpi_hw *lhw; 564 565 lhw = HW_TO_LHW(hw); 566 if (lhw->ops->wake_tx_queue == NULL) 567 return; 568 569 lhw->ops->wake_tx_queue(hw, txq); 570 } 571 572 void 573 lkpi_80211_mo_sync_rx_queues(struct ieee80211_hw *hw) 574 { 575 struct lkpi_hw *lhw; 576 577 lhw = HW_TO_LHW(hw); 578 if (lhw->ops->sync_rx_queues == NULL) 579 return; 580 581 lhw->ops->sync_rx_queues(hw); 582 } 583 584 void 585 lkpi_80211_mo_sta_pre_rcu_remove(struct ieee80211_hw *hw, 586 struct ieee80211_vif *vif, struct ieee80211_sta *sta) 587 { 588 struct lkpi_hw *lhw; 589 590 lhw = HW_TO_LHW(hw); 591 if (lhw->ops->sta_pre_rcu_remove == NULL) 592 return; 593 594 lhw->ops->sta_pre_rcu_remove(hw, vif, sta); 595 } 596 597 int 598 lkpi_80211_mo_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, 599 struct ieee80211_vif *vif, struct ieee80211_sta *sta, 600 struct ieee80211_key_conf *kc) 601 { 602 struct lkpi_hw *lhw; 603 int error; 604 605 lhw = HW_TO_LHW(hw); 606 if (lhw->ops->set_key == NULL) { 607 error = EOPNOTSUPP; 608 goto out; 609 } 610 611 error = lhw->ops->set_key(hw, cmd, vif, sta, kc); 612 613 out: 614 return (error); 615 } 616