1 /* Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. 2 * 3 * Permission to use, copy, modify, and/or distribute this software for any 4 * purpose with or without fee is hereby granted, provided that the above 5 * copyright notice and this permission notice appear in all copies. 6 * 7 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 */ 15 16 /* 17 * DOC: contains MLO manager ap related functionality 18 */ 19 #include "wlan_mlo_mgr_cmn.h" 20 #include "wlan_mlo_mgr_main.h" 21 #ifdef WLAN_MLO_MULTI_CHIP 22 #include "wlan_lmac_if_def.h" 23 #include <cdp_txrx_mlo.h> 24 #endif 25 #include <wlan_mgmt_txrx_rx_reo_utils_api.h> 26 27 #ifdef WLAN_MLO_MULTI_CHIP 28 static inline 29 bool mlo_psoc_get_index_id(struct wlan_objmgr_psoc *psoc, 30 uint8_t grp_id, 31 uint8_t *index, 32 bool teardown) 33 { 34 struct mlo_mgr_context *mlo_ctx = wlan_objmgr_get_mlo_ctx(); 35 uint8_t id; 36 37 if (!mlo_ctx) 38 return false; 39 40 if (!psoc) 41 return false; 42 43 if (!index) 44 return false; 45 46 if (grp_id >= mlo_ctx->total_grp) { 47 mlo_err("Invalid grp id %d, total no of groups %d", 48 grp_id, mlo_ctx->total_grp); 49 return false; 50 } 51 52 for (id = 0; id < mlo_ctx->setup_info[grp_id].tot_socs; id++) 53 if (mlo_ctx->setup_info[grp_id].curr_soc_list[id] == psoc) { 54 *index = id; 55 return true; 56 } 57 58 if (teardown) 59 return false; 60 61 for (id = 0; id < mlo_ctx->setup_info[grp_id].tot_socs; id++) 62 if (!mlo_ctx->setup_info[grp_id].curr_soc_list[id]) { 63 *index = id; 64 return true; 65 } 66 67 return false; 68 } 69 70 bool mlo_psoc_get_grp_id(struct wlan_objmgr_psoc *psoc, uint8_t *ret_id) 71 { 72 struct mlo_mgr_context *mlo_ctx = wlan_objmgr_get_mlo_ctx(); 73 uint8_t grp_id; 74 uint8_t tot_socs; 75 uint8_t id; 76 77 if (!mlo_ctx) 78 return false; 79 80 if (!psoc) 81 return false; 82 83 if (!ret_id) 84 return false; 85 86 for (grp_id = 0; grp_id < mlo_ctx->total_grp; grp_id++) { 87 tot_socs = mlo_ctx->setup_info[grp_id].tot_socs; 88 for (id = 0; id < tot_socs; id++) 89 if (mlo_ctx->setup_info[grp_id].soc_list[id] == psoc) { 90 *ret_id = grp_id; 91 return true; 92 } 93 } 94 95 return false; 96 } 97 98 qdf_export_symbol(mlo_psoc_get_grp_id); 99 100 bool mlo_is_ml_soc(struct wlan_objmgr_psoc *psoc, uint8_t grp_id) 101 { 102 struct mlo_mgr_context *mlo_ctx = wlan_objmgr_get_mlo_ctx(); 103 uint8_t id; 104 105 if (!mlo_ctx) 106 return false; 107 108 if (!psoc) 109 return false; 110 111 if (grp_id >= mlo_ctx->total_grp) { 112 mlo_err("Invalid grp id %d, total no of groups %d", 113 grp_id, mlo_ctx->total_grp); 114 return false; 115 } 116 117 for (id = 0; id < mlo_ctx->setup_info[grp_id].tot_socs; id++) 118 if (mlo_ctx->setup_info[grp_id].curr_soc_list[id] == psoc) 119 return true; 120 121 return false; 122 } 123 124 qdf_export_symbol(mlo_is_ml_soc); 125 126 static void mlo_set_soc_list(uint8_t grp_id, struct wlan_objmgr_psoc *psoc) 127 { 128 struct mlo_mgr_context *mlo_ctx = wlan_objmgr_get_mlo_ctx(); 129 uint8_t idx; 130 131 if (!mlo_ctx) 132 return; 133 134 if (!psoc) 135 return; 136 137 if (grp_id >= mlo_ctx->total_grp) { 138 mlo_err("Invalid grp id %d, total no of groups %d", 139 grp_id, mlo_ctx->total_grp); 140 return; 141 } 142 143 for (idx = 0; idx < mlo_ctx->setup_info[grp_id].tot_socs; idx++) { 144 if (mlo_ctx->setup_info[grp_id].soc_id_list[idx] == 145 psoc->soc_objmgr.psoc_id) { 146 mlo_ctx->setup_info[grp_id].soc_list[idx] = psoc; 147 mlo_wsi_link_info_update_soc(psoc, grp_id); 148 } 149 } 150 } 151 152 void mlo_get_soc_list(struct wlan_objmgr_psoc **soc_list, 153 uint8_t grp_id, 154 uint8_t total_socs, 155 enum MLO_SOC_LIST curr) 156 { 157 struct mlo_mgr_context *mlo_ctx = wlan_objmgr_get_mlo_ctx(); 158 uint8_t chip_idx; 159 160 if (!mlo_ctx) 161 goto err_case; 162 163 if (grp_id >= mlo_ctx->total_grp) { 164 mlo_err("Invalid grp id %d, total no of groups %d", 165 grp_id, mlo_ctx->total_grp); 166 goto err_case; 167 } 168 169 if (total_socs != mlo_ctx->setup_info[grp_id].tot_socs) { 170 mlo_err("Mismatch in number of socs in the grp id %d, expected %d observed %d", 171 grp_id, total_socs, 172 mlo_ctx->setup_info[grp_id].tot_socs); 173 goto err_case; 174 } 175 176 if (curr == WLAN_MLO_GROUP_CURRENT_SOC_LIST) { 177 for (chip_idx = 0; chip_idx < total_socs; chip_idx++) 178 soc_list[chip_idx] = 179 mlo_ctx->setup_info[grp_id].curr_soc_list[chip_idx]; 180 } else { 181 for (chip_idx = 0; chip_idx < total_socs; chip_idx++) 182 soc_list[chip_idx] = 183 mlo_ctx->setup_info[grp_id].soc_list[chip_idx]; 184 } 185 186 return; 187 188 err_case: 189 for (chip_idx = 0; chip_idx < total_socs; chip_idx++) 190 soc_list[chip_idx] = NULL; 191 192 return; 193 } 194 195 qdf_export_symbol(mlo_get_soc_list); 196 197 void mlo_cleanup_asserted_soc_setup_info(struct wlan_objmgr_psoc *psoc, 198 uint8_t grp_id) 199 { 200 struct mlo_mgr_context *mlo_ctx = wlan_objmgr_get_mlo_ctx(); 201 uint8_t link_idx; 202 struct wlan_objmgr_pdev *pdev; 203 struct mlo_setup_info *setup_info; 204 205 if (!mlo_ctx) 206 return; 207 208 if (!psoc) 209 return; 210 211 if (grp_id >= mlo_ctx->total_grp) { 212 mlo_err("Invalid grp id %d, total no of groups %d", 213 grp_id, mlo_ctx->total_grp); 214 return; 215 } 216 217 setup_info = &mlo_ctx->setup_info[grp_id]; 218 219 if (!setup_info->num_links) 220 return; 221 222 if (!psoc) { 223 mlo_info("NULL psoc"); 224 return; 225 } 226 227 for (link_idx = 0; link_idx < MAX_MLO_LINKS; link_idx++) { 228 pdev = setup_info->pdev_list[link_idx]; 229 if (pdev) { 230 if (wlan_pdev_get_psoc(pdev) == psoc) { 231 setup_info->pdev_list[link_idx] = NULL; 232 setup_info->state[link_idx] = MLO_LINK_TEARDOWN; 233 setup_info->num_links--; 234 } 235 } 236 } 237 } 238 239 qdf_export_symbol(mlo_cleanup_asserted_soc_setup_info); 240 241 void mlo_setup_update_soc_id_list(uint8_t grp_id, uint8_t *soc_id_list) 242 { 243 struct mlo_mgr_context *mlo_ctx = wlan_objmgr_get_mlo_ctx(); 244 uint32_t tot_socs; 245 uint32_t num_soc; 246 uint8_t *soc_list; 247 248 if (!mlo_ctx) 249 return; 250 251 if (grp_id >= mlo_ctx->total_grp) { 252 mlo_err("Invalid grp id %d, total no of groups %d", 253 grp_id, mlo_ctx->total_grp); 254 return; 255 } 256 257 tot_socs = mlo_ctx->setup_info[grp_id].tot_socs; 258 soc_list = mlo_ctx->setup_info[grp_id].soc_id_list; 259 260 for (num_soc = 0; num_soc < tot_socs; num_soc++) 261 soc_list[num_soc] = soc_id_list[num_soc]; 262 } 263 264 qdf_export_symbol(mlo_setup_update_soc_id_list); 265 266 uint8_t mlo_setup_get_total_socs(uint8_t grp_id) 267 { 268 struct mlo_mgr_context *mlo_ctx = wlan_objmgr_get_mlo_ctx(); 269 270 if (!mlo_ctx) 271 return 0; 272 273 if (grp_id >= mlo_ctx->total_grp) { 274 mlo_err("Invalid grp id %d, total no of groups %d", 275 grp_id, mlo_ctx->total_grp); 276 return 0; 277 } 278 279 return mlo_ctx->setup_info[grp_id].tot_socs; 280 } 281 282 qdf_export_symbol(mlo_setup_get_total_socs); 283 284 void mlo_setup_update_total_socs(uint8_t grp_id, uint8_t tot_socs) 285 { 286 struct mlo_mgr_context *mlo_ctx = wlan_objmgr_get_mlo_ctx(); 287 288 if (!mlo_ctx) 289 return; 290 291 if (grp_id >= mlo_ctx->total_grp) { 292 mlo_err("Invalid grp id %d, total no of groups %d", 293 grp_id, mlo_ctx->total_grp); 294 return; 295 } 296 297 mlo_ctx->setup_info[grp_id].tot_socs = tot_socs; 298 mlo_ctx->setup_info[grp_id].ml_grp_id = grp_id; 299 mlo_ctx->setup_info[grp_id].tot_links = 0; 300 qdf_info("Grp_id %d Total MLO socs = %d links = %d", 301 grp_id, mlo_ctx->setup_info[grp_id].tot_socs, 302 mlo_ctx->setup_info[grp_id].tot_links); 303 } 304 305 qdf_export_symbol(mlo_setup_update_total_socs); 306 307 static QDF_STATUS mlo_find_pdev_idx(struct wlan_objmgr_pdev *pdev, 308 uint8_t *link_idx, uint8_t grp_id) 309 { 310 struct mlo_mgr_context *mlo_ctx = wlan_objmgr_get_mlo_ctx(); 311 uint8_t idx; 312 313 if (!mlo_ctx) 314 return QDF_STATUS_E_FAILURE; 315 316 if (!pdev) 317 return QDF_STATUS_E_FAILURE; 318 319 if (!link_idx) 320 return QDF_STATUS_E_FAILURE; 321 322 if (grp_id >= mlo_ctx->total_grp) { 323 mlo_err("Invalid grp id %d, total no of groups %d", 324 grp_id, mlo_ctx->total_grp); 325 return QDF_STATUS_E_FAILURE; 326 } 327 328 for (idx = 0; idx < mlo_ctx->setup_info[grp_id].tot_links; idx++) { 329 if (mlo_ctx->setup_info[grp_id].pdev_list[idx] == pdev) { 330 *link_idx = idx; 331 return QDF_STATUS_SUCCESS; 332 } 333 } 334 335 return QDF_STATUS_E_FAILURE; 336 } 337 338 bool mlo_check_start_stop_inprogress(uint8_t grp_id) 339 { 340 struct mlo_mgr_context *mlo_ctx = wlan_objmgr_get_mlo_ctx(); 341 342 if (!mlo_ctx) 343 return true; 344 345 if (grp_id >= mlo_ctx->total_grp) { 346 mlo_err("Invalid grp id %d, total no of groups %d", 347 grp_id, mlo_ctx->total_grp); 348 return true; 349 } 350 351 return qdf_atomic_test_and_set_bit( 352 START_STOP_INPROGRESS_BIT, 353 &mlo_ctx->setup_info[grp_id].start_stop_inprogress); 354 } 355 356 qdf_export_symbol(mlo_check_start_stop_inprogress); 357 358 void mlo_clear_start_stop_inprogress(uint8_t grp_id) 359 { 360 struct mlo_mgr_context *mlo_ctx = wlan_objmgr_get_mlo_ctx(); 361 362 if (!mlo_ctx) 363 return; 364 365 if (grp_id >= mlo_ctx->total_grp) { 366 mlo_err("Invalid grp id %d, total no of groups %d", 367 grp_id, mlo_ctx->total_grp); 368 return; 369 } 370 371 qdf_atomic_clear_bit( 372 START_STOP_INPROGRESS_BIT, 373 &mlo_ctx->setup_info[grp_id].start_stop_inprogress); 374 } 375 376 qdf_export_symbol(mlo_clear_start_stop_inprogress); 377 378 #define WLAN_SOC_ID_NOT_INITIALIZED -1 379 bool mlo_vdevs_check_single_soc(struct wlan_objmgr_vdev **wlan_vdev_list, 380 uint8_t vdev_count) 381 { 382 int i; 383 uint8_t soc_id = WLAN_SOC_ID_NOT_INITIALIZED; 384 385 for (i = 0; i < vdev_count; i++) { 386 uint8_t vdev_soc_id = wlan_vdev_get_psoc_id(wlan_vdev_list[i]); 387 388 if (i == 0) 389 soc_id = vdev_soc_id; 390 else if (soc_id != vdev_soc_id) 391 return false; 392 } 393 394 return true; 395 } 396 397 qdf_export_symbol(mlo_vdevs_check_single_soc); 398 399 static void mlo_check_state(struct wlan_objmgr_psoc *psoc, 400 void *obj, void *args) 401 { 402 struct wlan_objmgr_pdev *pdev; 403 uint8_t link_idx; 404 struct mlo_mgr_context *mlo_ctx = wlan_objmgr_get_mlo_ctx(); 405 struct mlo_state_params *params = (struct mlo_state_params *)args; 406 407 uint8_t grp_id = params->grp_id; 408 pdev = (struct wlan_objmgr_pdev *)obj; 409 410 if (!mlo_ctx) 411 return; 412 413 if (!psoc) 414 return; 415 416 if (grp_id >= mlo_ctx->total_grp) { 417 mlo_err("Invalid grp id %d, total no of groups %d", 418 grp_id, mlo_ctx->total_grp); 419 return; 420 } 421 422 if (mlo_find_pdev_idx(pdev, &link_idx, grp_id) != QDF_STATUS_SUCCESS) { 423 mlo_info("Failed to find pdev"); 424 return; 425 } 426 427 if (mlo_ctx->setup_info[grp_id].state[link_idx] != params->check_state) 428 params->link_state_fail = 1; 429 } 430 431 QDF_STATUS mlo_check_all_pdev_state(struct wlan_objmgr_psoc *psoc, 432 uint8_t grp_id, 433 enum MLO_LINK_STATE state) 434 { 435 QDF_STATUS status = QDF_STATUS_E_INVAL; 436 struct mlo_state_params params = {0}; 437 438 params.check_state = state; 439 params.grp_id = grp_id; 440 441 wlan_objmgr_iterate_obj_list(psoc, WLAN_PDEV_OP, 442 mlo_check_state, ¶ms, 443 0, WLAN_MLME_NB_ID); 444 445 if (params.link_state_fail) 446 status = QDF_STATUS_E_INVAL; 447 else 448 status = QDF_STATUS_SUCCESS; 449 450 return status; 451 } 452 453 void mlo_setup_init(uint8_t total_grp) 454 { 455 struct mlo_mgr_context *mlo_ctx = wlan_objmgr_get_mlo_ctx(); 456 struct mlo_setup_info *setup_info; 457 uint8_t id; 458 459 if (!mlo_ctx) 460 return; 461 462 if (!total_grp && total_grp > WLAN_MAX_MLO_GROUPS) { 463 mlo_err("Total number of groups (%d) is greater than MAX (%d), MLD Setup failed!!", 464 total_grp, WLAN_MAX_MLO_GROUPS); 465 return; 466 } 467 468 mlo_ctx->total_grp = total_grp; 469 setup_info = qdf_mem_malloc(sizeof(struct mlo_setup_info) * 470 total_grp); 471 472 if (!setup_info) 473 return; 474 475 mlo_ctx->setup_info = setup_info; 476 mlo_ctx->setup_info[0].ml_grp_id = 0; 477 for (id = 0; id < total_grp; id++) { 478 mlo_ctx->setup_info[id].tsf_sync_enabled = true; 479 mlo_ctx->setup_info[id].wsi_stats_info_support = 0xff; 480 481 if (qdf_event_create(&mlo_ctx->setup_info[id].event) != 482 QDF_STATUS_SUCCESS) 483 mlo_err("Unable to create teardown event"); 484 } 485 } 486 487 qdf_export_symbol(mlo_setup_init); 488 489 void mlo_setup_deinit(void) 490 { 491 struct mlo_mgr_context *mlo_ctx = wlan_objmgr_get_mlo_ctx(); 492 uint8_t id; 493 494 if (!mlo_ctx) 495 return; 496 497 if (!mlo_ctx->setup_info) 498 return; 499 500 for (id = 0; id < mlo_ctx->total_grp; id++) 501 qdf_event_destroy(&mlo_ctx->setup_info[id].event); 502 503 qdf_mem_free(mlo_ctx->setup_info); 504 mlo_ctx->setup_info = NULL; 505 } 506 507 qdf_export_symbol(mlo_setup_deinit); 508 509 void mlo_setup_update_chip_info(struct wlan_objmgr_psoc *psoc, 510 uint8_t chip_id, uint8_t *adj_chip_id) 511 { 512 uint8_t psoc_id, i; 513 struct mlo_mgr_context *mlo_ctx = wlan_objmgr_get_mlo_ctx(); 514 struct mlo_chip_info *chip_info; 515 516 if (!mlo_ctx) 517 return; 518 519 chip_info = &mlo_ctx->setup_info->chip_info; 520 /* get psoc_id of a soc */ 521 psoc_id = wlan_psoc_get_id(psoc); 522 523 if (psoc_id >= MAX_MLO_CHIPS) 524 return; 525 /* chip id & psoc id need not be same, assign here based on psoc index*/ 526 chip_info->chip_id[psoc_id] = chip_id; 527 528 /* For a particular psoc id populate the adjacent chip id's */ 529 for (i = 0; i < MAX_ADJ_CHIPS; i++) 530 chip_info->adj_chip_ids[psoc_id][i] = adj_chip_id[i]; 531 532 chip_info->info_valid = 1; 533 } 534 535 qdf_export_symbol(mlo_setup_update_chip_info); 536 537 QDF_STATUS mlo_chip_adjacent(uint8_t psoc_id_1, uint8_t psoc_id_2, 538 uint8_t *is_adjacent) 539 { 540 struct mlo_mgr_context *mlo_ctx = wlan_objmgr_get_mlo_ctx(); 541 uint8_t chip_id2, i; 542 struct mlo_chip_info *chip_info; 543 544 if (!mlo_ctx) 545 return QDF_STATUS_E_FAILURE; 546 547 if ((psoc_id_1 >= MAX_MLO_CHIPS) || (psoc_id_2 >= MAX_MLO_CHIPS)) { 548 mlo_err("psoc id's greater then max limit of %d", 549 MAX_MLO_CHIPS); 550 return QDF_STATUS_E_FAILURE; 551 } 552 553 chip_info = &mlo_ctx->setup_info->chip_info; 554 555 /* default case is adjacent */ 556 *is_adjacent = 1; 557 558 if (!chip_info->info_valid) { 559 /* This is default (non-ini), they are adjacent */ 560 return QDF_STATUS_SUCCESS; 561 } 562 563 if (psoc_id_1 == psoc_id_2) { 564 /* this is probably a single soc case, they are adjacent */ 565 return QDF_STATUS_SUCCESS; 566 } 567 /* get chip id from psoc */ 568 chip_id2 = chip_info->chip_id[psoc_id_2]; 569 for (i = 0; i < MAX_ADJ_CHIPS; i++) { 570 if (chip_info->adj_chip_ids[psoc_id_1][i] == chip_id2) 571 return QDF_STATUS_SUCCESS; 572 } 573 574 *is_adjacent = 0; 575 return QDF_STATUS_SUCCESS; 576 } 577 578 qdf_export_symbol(mlo_chip_adjacent); 579 580 void mlo_setup_update_num_links(struct wlan_objmgr_psoc *psoc, 581 uint8_t grp_id, uint8_t num_links) 582 { 583 struct mlo_mgr_context *mlo_ctx = wlan_objmgr_get_mlo_ctx(); 584 585 if (!mlo_ctx) 586 return; 587 588 if (!psoc) 589 return; 590 591 if (grp_id >= mlo_ctx->total_grp) { 592 mlo_err("Invalid grp id %d, total no of groups %d", 593 grp_id, mlo_ctx->total_grp); 594 return; 595 } 596 597 mlo_ctx->setup_info[grp_id].tot_links += num_links; 598 qdf_info("Grp_id %d Total MLO links = %d", 599 grp_id, mlo_ctx->setup_info[grp_id].tot_links); 600 } 601 602 qdf_export_symbol(mlo_setup_update_num_links); 603 604 void mlo_setup_update_soc_ready(struct wlan_objmgr_psoc *psoc, uint8_t grp_id) 605 { 606 struct mlo_mgr_context *mlo_ctx = wlan_objmgr_get_mlo_ctx(); 607 struct mlo_setup_info *setup_info; 608 uint8_t chip_idx, tot_socs; 609 struct cdp_mlo_ctxt *dp_mlo_ctxt = NULL; 610 611 if (!mlo_ctx) 612 return; 613 614 if (!psoc) 615 return; 616 617 if (grp_id >= mlo_ctx->total_grp) { 618 mlo_err("Invalid grp id %d, total no of groups %d", 619 grp_id, mlo_ctx->total_grp); 620 return; 621 } 622 623 setup_info = &mlo_ctx->setup_info[grp_id]; 624 625 if (!setup_info->tot_socs) 626 return; 627 628 tot_socs = setup_info->tot_socs; 629 if (!mlo_psoc_get_index_id(psoc, grp_id, &chip_idx, 0)) { 630 mlo_err("Unable to fetch chip idx for psoc id %d grp id %d", 631 psoc->soc_objmgr.psoc_id, 632 grp_id); 633 return; 634 } 635 636 if (!(chip_idx < tot_socs)) { 637 mlo_err("Invalid chip index, SoC setup failed"); 638 return; 639 } 640 641 setup_info->curr_soc_list[chip_idx] = psoc; 642 mlo_set_soc_list(grp_id, psoc); 643 setup_info->num_soc++; 644 645 mlo_debug("SoC updated to mld grp %d , chip idx %d num soc %d", 646 grp_id, chip_idx, setup_info->num_soc); 647 648 if (setup_info->num_soc != tot_socs) 649 return; 650 651 dp_mlo_ctxt = wlan_objmgr_get_dp_mlo_ctx(grp_id); 652 653 if (!dp_mlo_ctxt) { 654 dp_mlo_ctxt = cdp_mlo_ctxt_attach( 655 wlan_psoc_get_dp_handle(psoc), 656 (struct cdp_ctrl_mlo_mgr *)mlo_ctx); 657 wlan_objmgr_set_dp_mlo_ctx(dp_mlo_ctxt, grp_id); 658 } 659 660 for (chip_idx = 0; chip_idx < tot_socs; chip_idx++) { 661 struct wlan_objmgr_psoc *tmp_soc = 662 setup_info->curr_soc_list[chip_idx]; 663 if (tmp_soc) 664 cdp_soc_mlo_soc_setup(wlan_psoc_get_dp_handle(tmp_soc), 665 setup_info->dp_handle); 666 } 667 668 cdp_mlo_setup_complete(wlan_psoc_get_dp_handle(psoc), 669 setup_info->dp_handle); 670 } 671 672 qdf_export_symbol(mlo_setup_update_soc_ready); 673 674 void mlo_setup_link_ready(struct wlan_objmgr_pdev *pdev, uint8_t grp_id) 675 { 676 struct mlo_mgr_context *mlo_ctx = wlan_objmgr_get_mlo_ctx(); 677 struct mlo_setup_info *setup_info; 678 uint8_t link_idx; 679 uint16_t link_id; 680 681 if (!mlo_ctx) 682 return; 683 684 if (!pdev) 685 return; 686 687 if (grp_id >= mlo_ctx->total_grp) { 688 mlo_err("Invalid grp id %d, total no of groups %d", 689 grp_id, mlo_ctx->total_grp); 690 return; 691 } 692 693 setup_info = &mlo_ctx->setup_info[grp_id]; 694 695 if (!setup_info->tot_links) { 696 mlo_err("Setup info total links %d for grp id %d", 697 setup_info->tot_links, grp_id); 698 return; 699 } 700 701 if (mlo_find_pdev_idx(pdev, &link_idx, grp_id) == QDF_STATUS_SUCCESS) { 702 mlo_debug("Pdev already part of list link idx %d", link_idx); 703 return; 704 } 705 706 for (link_idx = 0; link_idx < setup_info->tot_links; link_idx++) 707 if (!setup_info->pdev_list[link_idx]) 708 break; 709 710 if (link_idx >= setup_info->tot_links) { 711 mlo_err("Exceeding max total mld links"); 712 return; 713 } 714 715 setup_info->pdev_list[link_idx] = pdev; 716 setup_info->state[link_idx] = MLO_LINK_SETUP_INIT; 717 setup_info->num_links++; 718 719 link_id = wlan_mlo_get_pdev_hw_link_id(pdev); 720 if (link_id == INVALID_HW_LINK_ID) { 721 mlo_err("Invalid HW link id for the pdev"); 722 return; 723 } 724 setup_info->valid_link_bitmap |= (1 << link_id); 725 726 qdf_info("Pdev updated to Grp id %d mld link %d num_links %d hw link id %d Valid link bitmap %d", 727 grp_id, link_idx, setup_info->num_links, 728 link_id, setup_info->valid_link_bitmap); 729 730 qdf_assert_always(link_idx < MAX_MLO_LINKS); 731 732 if (setup_info->num_links == setup_info->tot_links && 733 setup_info->num_soc == setup_info->tot_socs) { 734 struct wlan_objmgr_psoc *psoc; 735 struct wlan_lmac_if_tx_ops *tx_ops; 736 QDF_STATUS status; 737 738 psoc = wlan_pdev_get_psoc(pdev); 739 tx_ops = wlan_psoc_get_lmac_if_txops(psoc); 740 741 status = wlan_mgmt_rx_reo_validate_mlo_link_info(psoc); 742 if (QDF_IS_STATUS_ERROR(status)) { 743 mlo_err("Failed to validate MLO HW link info"); 744 qdf_assert_always(0); 745 } 746 747 qdf_info("Trigger MLO Setup request"); 748 if (tx_ops && tx_ops->mops.target_if_mlo_setup_req) { 749 tx_ops->mops.target_if_mlo_setup_req( 750 setup_info->pdev_list, 751 setup_info->num_links, 752 grp_id); 753 } 754 } 755 } 756 757 qdf_export_symbol(mlo_setup_link_ready); 758 759 void mlo_link_setup_complete(struct wlan_objmgr_pdev *pdev, uint8_t grp_id) 760 { 761 struct mlo_mgr_context *mlo_ctx = wlan_objmgr_get_mlo_ctx(); 762 struct mlo_setup_info *setup_info; 763 uint8_t link_idx; 764 765 if (!mlo_ctx) 766 return; 767 768 if (!pdev) 769 return; 770 771 if (grp_id >= mlo_ctx->total_grp) { 772 mlo_err("Invalid grp id %d, total no of groups %d", 773 grp_id, mlo_ctx->total_grp); 774 return; 775 } 776 777 setup_info = &mlo_ctx->setup_info[grp_id]; 778 779 for (link_idx = 0; link_idx < setup_info->tot_links; link_idx++) 780 if (setup_info->pdev_list[link_idx] == pdev) { 781 setup_info->state[link_idx] = 782 MLO_LINK_SETUP_DONE; 783 break; 784 } 785 786 mlo_debug("Setup complete for pdev id %d mlo group %d", 787 pdev->pdev_objmgr.wlan_pdev_id, grp_id); 788 789 for (link_idx = 0; link_idx < setup_info->tot_links; link_idx++) 790 if (setup_info->state[link_idx] == MLO_LINK_SETUP_DONE) 791 continue; 792 else 793 break; 794 795 if (link_idx == setup_info->tot_links) { 796 struct wlan_objmgr_psoc *psoc; 797 struct wlan_lmac_if_tx_ops *tx_ops; 798 799 psoc = wlan_pdev_get_psoc(pdev); 800 tx_ops = wlan_psoc_get_lmac_if_txops(psoc); 801 mlo_debug("Trigger MLO ready"); 802 if (tx_ops && tx_ops->mops.target_if_mlo_ready) { 803 tx_ops->mops.target_if_mlo_ready( 804 setup_info->pdev_list, 805 setup_info->num_links); 806 } 807 } 808 } 809 810 qdf_export_symbol(mlo_link_setup_complete); 811 812 static void mlo_setup_link_down(struct wlan_objmgr_psoc *psoc, 813 void *obj, void *args) 814 { 815 struct wlan_objmgr_pdev *pdev; 816 uint8_t link_idx; 817 struct mlo_mgr_context *mlo_ctx = wlan_objmgr_get_mlo_ctx(); 818 struct mlo_setup_info *setup_info; 819 uint16_t link_id; 820 uint8_t grp_id = *(uint8_t *)args; 821 822 if (!mlo_ctx) 823 return; 824 825 if (!psoc) 826 return; 827 828 if (grp_id >= mlo_ctx->total_grp) { 829 mlo_err("Invalid grp id %d, total no of groups %d", 830 grp_id, mlo_ctx->total_grp); 831 return; 832 } 833 834 setup_info = &mlo_ctx->setup_info[grp_id]; 835 836 pdev = (struct wlan_objmgr_pdev *)obj; 837 838 if (mlo_find_pdev_idx(pdev, &link_idx, grp_id) != QDF_STATUS_SUCCESS) { 839 mlo_info("Failed to find pdev"); 840 return; 841 } 842 843 if (setup_info->pdev_list[link_idx]) { 844 setup_info->pdev_list[link_idx] = NULL; 845 setup_info->state[link_idx] = MLO_LINK_UNINITIALIZED; 846 setup_info->num_links--; 847 848 link_id = wlan_mlo_get_pdev_hw_link_id(pdev); 849 if (link_id == INVALID_HW_LINK_ID) { 850 mlo_err("Invalid HW link id for the pdev"); 851 return; 852 } 853 setup_info->valid_link_bitmap &= ~(1 << link_id); 854 } 855 856 mlo_debug("Pdev link down grp_id %d link_idx %d num_links %d", 857 grp_id, link_idx, setup_info->num_links); 858 } 859 860 static void mlo_dp_ctxt_detach(struct wlan_objmgr_psoc *psoc, 861 uint8_t grp_id, 862 struct cdp_mlo_ctxt *dp_mlo_ctxt) 863 { 864 if (!psoc) 865 return; 866 867 wlan_objmgr_set_dp_mlo_ctx(NULL, grp_id); 868 if (dp_mlo_ctxt) 869 cdp_mlo_ctxt_detach(wlan_psoc_get_dp_handle(psoc), dp_mlo_ctxt); 870 } 871 872 void mlo_setup_update_soc_down(struct wlan_objmgr_psoc *psoc, uint8_t grp_id) 873 { 874 struct mlo_mgr_context *mlo_ctx = wlan_objmgr_get_mlo_ctx(); 875 struct mlo_setup_info *setup_info; 876 uint8_t chip_idx; 877 struct wlan_objmgr_psoc *soc; 878 879 if (!mlo_ctx) 880 return; 881 882 if (!psoc) 883 return; 884 885 if (grp_id >= mlo_ctx->total_grp) { 886 mlo_err("Invalid grp id %d, total no of groups %d", 887 grp_id, mlo_ctx->total_grp); 888 return; 889 } 890 891 setup_info = &mlo_ctx->setup_info[grp_id]; 892 893 if (setup_info->num_links) { 894 wlan_objmgr_iterate_obj_list(psoc, WLAN_PDEV_OP, 895 mlo_setup_link_down, &grp_id, 896 0, WLAN_MLME_NB_ID); 897 } 898 899 if (!mlo_psoc_get_index_id(psoc, grp_id, &chip_idx, 1)) { 900 mlo_err("Unable to fetch chip idx for psoc id %d grp id %d", 901 psoc->soc_objmgr.psoc_id, 902 grp_id); 903 return; 904 } 905 906 if (!(chip_idx < MAX_MLO_CHIPS)) { 907 mlo_err("Invalid chip index, SoC setup down failed"); 908 return; 909 } 910 911 if (setup_info->curr_soc_list[chip_idx]) { 912 soc = setup_info->curr_soc_list[chip_idx]; 913 cdp_soc_mlo_soc_teardown(wlan_psoc_get_dp_handle(soc), 914 setup_info->dp_handle, false); 915 916 setup_info->curr_soc_list[chip_idx] = NULL; 917 setup_info->num_soc--; 918 919 if (!setup_info->num_soc) 920 mlo_dp_ctxt_detach(soc, grp_id, setup_info->dp_handle); 921 } 922 923 mlo_debug("Soc down, mlo group %d num soc %d num links %d", 924 grp_id, setup_info->num_soc, 925 setup_info->num_links); 926 } 927 928 qdf_export_symbol(mlo_setup_update_soc_down); 929 930 void mlo_link_teardown_complete(struct wlan_objmgr_pdev *pdev, uint8_t grp_id) 931 { 932 struct mlo_mgr_context *mlo_ctx = wlan_objmgr_get_mlo_ctx(); 933 struct mlo_setup_info *setup_info; 934 uint8_t link_idx; 935 936 if (!mlo_ctx) 937 return; 938 939 if (!pdev) 940 return; 941 942 if (grp_id >= mlo_ctx->total_grp) { 943 mlo_err("Invalid grp id %d, total no of groups %d", 944 grp_id, mlo_ctx->total_grp); 945 return; 946 } 947 948 setup_info = &mlo_ctx->setup_info[grp_id]; 949 950 if (!setup_info->num_links) { 951 mlo_err("Delayed response ignore"); 952 return; 953 } 954 955 if (mlo_find_pdev_idx(pdev, &link_idx, grp_id) != QDF_STATUS_SUCCESS) { 956 mlo_info("Failed to find pdev"); 957 return; 958 } 959 960 mlo_debug("Teardown link idx = %d", link_idx); 961 setup_info->state[link_idx] = MLO_LINK_TEARDOWN; 962 963 /* Waiting for teardown on other links */ 964 for (link_idx = 0; link_idx < setup_info->tot_links; link_idx++) 965 if (setup_info->state[link_idx] != MLO_LINK_TEARDOWN) 966 return; 967 968 qdf_info("Teardown complete"); 969 970 setup_info->trigger_umac_reset = false; 971 972 qdf_event_set(&setup_info->event); 973 } 974 975 qdf_export_symbol(mlo_link_teardown_complete); 976 977 static void mlo_force_teardown(uint8_t grp_id) 978 { 979 struct mlo_mgr_context *mlo_ctx = wlan_objmgr_get_mlo_ctx(); 980 struct mlo_setup_info *setup_info; 981 uint8_t link_idx; 982 983 if (!mlo_ctx) 984 return; 985 986 if (grp_id >= mlo_ctx->total_grp) { 987 mlo_err("Invalid grp id %d, total no of groups %d", 988 grp_id, mlo_ctx->total_grp); 989 return; 990 } 991 992 setup_info = &mlo_ctx->setup_info[grp_id]; 993 994 for (link_idx = 0; link_idx < setup_info->tot_links; link_idx++) 995 setup_info->state[link_idx] = MLO_LINK_TEARDOWN; 996 } 997 998 static void mlo_send_teardown_req(struct wlan_objmgr_psoc *psoc, 999 uint8_t grp_id, uint32_t reason) 1000 { 1001 struct mlo_mgr_context *mlo_ctx = wlan_objmgr_get_mlo_ctx(); 1002 struct wlan_lmac_if_tx_ops *tx_ops; 1003 struct wlan_objmgr_pdev *temp_pdev; 1004 struct mlo_setup_info *setup_info; 1005 uint8_t link_idx; 1006 uint8_t tot_links; 1007 bool umac_reset = 0; 1008 1009 if (!mlo_ctx) 1010 return; 1011 1012 if (grp_id >= mlo_ctx->total_grp) { 1013 mlo_err("Invalid grp id %d, total no of groups %d", 1014 grp_id, mlo_ctx->total_grp); 1015 return; 1016 } 1017 1018 tx_ops = wlan_psoc_get_lmac_if_txops(psoc); 1019 if (!tx_ops) { 1020 mlo_err("Tx Ops is null for the psoc id %d", 1021 wlan_psoc_get_id(psoc)); 1022 return; 1023 } 1024 1025 setup_info = &mlo_ctx->setup_info[grp_id]; 1026 tot_links = setup_info->tot_links; 1027 1028 if (reason == WMI_HOST_MLO_TEARDOWN_REASON_MODE1_SSR || 1029 reason == WMI_HOST_MLO_TEARDOWN_REASON_STANDBY) { 1030 for (link_idx = 0; link_idx < tot_links; link_idx++) { 1031 umac_reset = 0; 1032 temp_pdev = setup_info->pdev_list[link_idx]; 1033 if (!temp_pdev) 1034 continue; 1035 1036 if (!setup_info->trigger_umac_reset) { 1037 if (psoc == wlan_pdev_get_psoc(temp_pdev)) { 1038 umac_reset = 1; 1039 setup_info->trigger_umac_reset = 1; 1040 } 1041 } 1042 1043 if (tx_ops && tx_ops->mops.target_if_mlo_teardown_req) { 1044 mlo_info( 1045 "Trigger Teardown with Pdev id: %d Psoc id: %d link idx: %d Umac reset: %d Standby Active: %d", 1046 wlan_objmgr_pdev_get_pdev_id(temp_pdev), 1047 wlan_psoc_get_id(wlan_pdev_get_psoc(temp_pdev)), 1048 link_idx, umac_reset, 1049 temp_pdev->standby_active); 1050 tx_ops->mops.target_if_mlo_teardown_req( 1051 setup_info->pdev_list[link_idx], 1052 reason, umac_reset, 1053 temp_pdev->standby_active); 1054 } 1055 } 1056 } else { 1057 for (link_idx = 0; link_idx < setup_info->tot_links; link_idx++) 1058 if (tx_ops && tx_ops->mops.target_if_mlo_teardown_req) { 1059 if (!setup_info->pdev_list[link_idx]) 1060 continue; 1061 tx_ops->mops.target_if_mlo_teardown_req( 1062 setup_info->pdev_list[link_idx], 1063 reason, 0, 0); 1064 } 1065 } 1066 } 1067 1068 static bool mlo_grp_in_teardown(uint8_t grp_id) 1069 { 1070 struct mlo_mgr_context *mlo_ctx = wlan_objmgr_get_mlo_ctx(); 1071 struct mlo_setup_info *setup_info; 1072 uint8_t link_idx; 1073 1074 if (!mlo_ctx) { 1075 mlo_err("MLO ctx null, teardown failed"); 1076 return true; 1077 } 1078 1079 if (grp_id >= mlo_ctx->total_grp) { 1080 mlo_err("Invalid grp id %d, total no of groups %d", 1081 grp_id, mlo_ctx->total_grp); 1082 return true; 1083 } 1084 1085 setup_info = &mlo_ctx->setup_info[grp_id]; 1086 1087 for (link_idx = 0; link_idx < setup_info->tot_links; link_idx++) 1088 if (setup_info->pdev_list[link_idx] && 1089 (setup_info->state[link_idx] == MLO_LINK_TEARDOWN)) 1090 return true; 1091 1092 return false; 1093 } 1094 1095 #define MLO_MGR_TEARDOWN_TIMEOUT 3000 1096 QDF_STATUS mlo_link_teardown_link(struct wlan_objmgr_psoc *psoc, 1097 uint8_t grp_id, 1098 uint32_t reason) 1099 { 1100 QDF_STATUS status; 1101 struct mlo_setup_info *setup_info; 1102 struct mlo_mgr_context *mlo_ctx = wlan_objmgr_get_mlo_ctx(); 1103 1104 if (!mlo_ctx) 1105 return QDF_STATUS_E_FAILURE; 1106 1107 if (!psoc) 1108 return QDF_STATUS_E_FAILURE; 1109 1110 if (grp_id >= mlo_ctx->total_grp) { 1111 mlo_err("Invalid grp id %d, total no of groups %d", 1112 grp_id, mlo_ctx->total_grp); 1113 return QDF_STATUS_E_INVAL; 1114 } 1115 1116 setup_info = &mlo_ctx->setup_info[grp_id]; 1117 1118 mlo_debug("Teardown req with grp_id %d num_soc %d num_link %d", 1119 grp_id, setup_info->num_soc, setup_info->num_links); 1120 1121 if (!setup_info->num_soc) 1122 return QDF_STATUS_SUCCESS; 1123 1124 if (mlo_grp_in_teardown(grp_id)) { 1125 mlo_debug("Skip teardown: as teardown sent already, grp_id %d num_soc %d num_link %d", 1126 grp_id, setup_info->num_soc, setup_info->num_links); 1127 return QDF_STATUS_SUCCESS; 1128 } 1129 1130 /* Trigger MLO teardown */ 1131 mlo_send_teardown_req(psoc, grp_id, reason); 1132 1133 if (reason == WMI_HOST_MLO_TEARDOWN_REASON_SSR) { 1134 /* do not wait for teardown event completion here for SSR */ 1135 return QDF_STATUS_SUCCESS; 1136 } 1137 1138 status = qdf_wait_for_event_completion( 1139 &setup_info->event, 1140 MLO_MGR_TEARDOWN_TIMEOUT); 1141 1142 if (status != QDF_STATUS_SUCCESS) { 1143 qdf_info("Teardown timeout"); 1144 mlo_force_teardown(grp_id); 1145 } 1146 1147 return status; 1148 } 1149 1150 qdf_export_symbol(mlo_link_teardown_link); 1151 1152 void mlo_update_wsi_stats_info_support(struct wlan_objmgr_psoc *psoc, 1153 bool wsi_stats_info_support) 1154 { 1155 uint8_t ml_grp_id; 1156 struct mlo_mgr_context *mlo_ctx = wlan_objmgr_get_mlo_ctx(); 1157 struct mlo_setup_info *mlo_setup; 1158 1159 ml_grp_id = wlan_mlo_get_psoc_group_id(psoc); 1160 if ((ml_grp_id == WLAN_MLO_GROUP_INVALID) || 1161 (ml_grp_id < 0)) { 1162 mlo_err("Invalid ML Grp ID %d", ml_grp_id); 1163 return; 1164 } 1165 1166 mlo_setup = &mlo_ctx->setup_info[ml_grp_id]; 1167 if (mlo_setup->wsi_stats_info_support == 0xFF) 1168 mlo_setup->wsi_stats_info_support = wsi_stats_info_support; 1169 else 1170 mlo_setup->wsi_stats_info_support &= wsi_stats_info_support; 1171 } 1172 1173 qdf_export_symbol(mlo_update_wsi_stats_info_support); 1174 1175 uint8_t mlo_get_wsi_stats_info_support(struct wlan_objmgr_psoc *psoc) 1176 { 1177 uint8_t ml_grp_id; 1178 struct mlo_mgr_context *mlo_ctx = wlan_objmgr_get_mlo_ctx(); 1179 struct mlo_setup_info *mlo_setup; 1180 1181 ml_grp_id = wlan_mlo_get_psoc_group_id(psoc); 1182 if ((ml_grp_id == WLAN_MLO_GROUP_INVALID) || 1183 (ml_grp_id < 0)) { 1184 mlo_err("Invalid ML Grp ID %d", ml_grp_id); 1185 return 0; 1186 } 1187 1188 mlo_setup = &mlo_ctx->setup_info[ml_grp_id]; 1189 if (mlo_setup->wsi_stats_info_support == 0xFF) 1190 return 0; 1191 1192 return mlo_setup->wsi_stats_info_support; 1193 } 1194 1195 void mlo_update_tsf_sync_support(struct wlan_objmgr_psoc *psoc, 1196 bool tsf_sync_enab) 1197 { 1198 uint8_t ml_grp_id; 1199 struct mlo_mgr_context *mlo_ctx = wlan_objmgr_get_mlo_ctx(); 1200 struct mlo_setup_info *mlo_setup; 1201 1202 ml_grp_id = wlan_mlo_get_psoc_group_id(psoc); 1203 if (ml_grp_id < 0) { 1204 mlo_err("Invalid ML Grp ID %d", ml_grp_id); 1205 return; 1206 } 1207 1208 mlo_setup = &mlo_ctx->setup_info[ml_grp_id]; 1209 mlo_setup->tsf_sync_enabled &= tsf_sync_enab; 1210 } 1211 1212 qdf_export_symbol(mlo_update_tsf_sync_support); 1213 1214 bool mlo_pdev_derive_bridge_link_pdevs(struct wlan_objmgr_pdev *pdev, 1215 struct wlan_objmgr_pdev **pdev_list) 1216 { 1217 struct wlan_objmgr_psoc *psoc; 1218 struct wlan_objmgr_psoc *grp_soc_list[MAX_MLO_CHIPS]; 1219 struct wlan_objmgr_pdev *tmp_pdev; 1220 uint8_t tot_grp_socs; 1221 uint8_t grp_id; 1222 uint8_t psoc_id, tmp_psoc_id; 1223 uint8_t idx; 1224 uint8_t is_adjacent; 1225 QDF_STATUS status; 1226 1227 psoc = wlan_pdev_get_psoc(pdev); 1228 1229 /* Initialize pdev list to NULL by default */ 1230 for (idx = 0; idx < MLO_MAX_BRIDGE_LINKS_PER_MLD; idx++) 1231 pdev_list[idx] = NULL; 1232 1233 if (!mlo_psoc_get_grp_id(psoc, &grp_id)) { 1234 qdf_err("Unable to get group id"); 1235 return false; 1236 } 1237 1238 /* Get the total SOCs in the MLO group */ 1239 tot_grp_socs = mlo_setup_get_total_socs(grp_id); 1240 if (!tot_grp_socs || tot_grp_socs > MAX_MLO_CHIPS) { 1241 qdf_err("Unable to get total SOCs"); 1242 return false; 1243 } 1244 qdf_info("Total SOCs in MLO group%d: %d", grp_id, tot_grp_socs); 1245 1246 /* Get the SOC list in the MLO group */ 1247 mlo_get_soc_list(grp_soc_list, grp_id, tot_grp_socs, 1248 WLAN_MLO_GROUP_DEFAULT_SOC_LIST); 1249 1250 psoc_id = wlan_psoc_get_id(psoc); 1251 1252 /* 1253 * Check the current pdev for num bridge links created and 1254 * add to the pdev list if possible otherwise find opposite pdev 1255 */ 1256 if (wlan_pdev_get_mlo_bridge_vdev_count(pdev) 1257 < MLO_MAX_BRIDGE_LINKS_PER_RADIO) 1258 pdev_list[0] = pdev; 1259 1260 /* 1261 * Iterate over the MLO group SOC list 1262 * and get the pdevs for bridge links 1263 */ 1264 for (idx = 0; idx < tot_grp_socs; idx++) { 1265 if (!grp_soc_list[idx]) 1266 continue; 1267 1268 if (grp_soc_list[idx] == psoc) 1269 continue; 1270 1271 /* Skip the pdev if bridge link quota is over */ 1272 tmp_pdev = grp_soc_list[idx]->soc_objmgr.wlan_pdev_list[0]; 1273 1274 if (wlan_pdev_get_mlo_bridge_vdev_count(tmp_pdev) 1275 >= MLO_MAX_BRIDGE_LINKS_PER_RADIO) 1276 continue; 1277 1278 tmp_psoc_id = wlan_psoc_get_id(grp_soc_list[idx]); 1279 1280 qdf_info("Checking adjacency of soc %d and %d", 1281 psoc_id, tmp_psoc_id); 1282 status = mlo_chip_adjacent(psoc_id, tmp_psoc_id, &is_adjacent); 1283 if (status != QDF_STATUS_SUCCESS) { 1284 qdf_info("Check adjacency failed"); 1285 return false; 1286 } 1287 1288 if (is_adjacent) { 1289 if (!pdev_list[1]) 1290 pdev_list[1] = tmp_pdev; 1291 } else if (!pdev_list[0]) { 1292 pdev_list[0] = tmp_pdev; 1293 } 1294 1295 if (pdev_list[0] && pdev_list[1]) 1296 return true; 1297 } 1298 1299 return false; 1300 } 1301 1302 qdf_export_symbol(mlo_pdev_derive_bridge_link_pdevs); 1303 #endif /*WLAN_MLO_MULTI_CHIP*/ 1304