1 /* 2 * Copyright (c) 2021, The Linux Foundation. All rights reserved. 3 * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. 4 * 5 * Permission to use, copy, modify, and/or distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 /* 19 * DOC: contains MLO manager STA related api's 20 */ 21 22 #include <wlan_cmn.h> 23 #include <wlan_mlo_mgr_sta.h> 24 #include <wlan_cm_public_struct.h> 25 #include <wlan_mlo_mgr_main.h> 26 #include <wlan_cm_api.h> 27 #include <wlan_mlo_mgr_cmn.h> 28 #include <wlan_scan_api.h> 29 #include <scheduler_api.h> 30 #include <wlan_crypto_global_api.h> 31 #include <utils_mlo.h> 32 #include <wlan_mlme_cmn.h> 33 #include <wlan_scan_utils_api.h> 34 #include <qdf_time.h> 35 #include <wlan_objmgr_peer_obj.h> 36 #include <wlan_scan_api.h> 37 #include <wlan_mlo_mgr_peer.h> 38 39 #ifdef WLAN_FEATURE_11BE_MLO 40 static QDF_STATUS mlo_disconnect_req(struct wlan_objmgr_vdev *vdev, 41 enum wlan_cm_source source, 42 enum wlan_reason_code reason_code, 43 struct qdf_mac_addr *bssid, 44 bool validate_req); 45 46 void 47 mlo_allocate_and_copy_ies(struct wlan_cm_connect_req *target, 48 struct wlan_cm_connect_req *source) 49 { 50 target->assoc_ie.ptr = NULL; 51 target->scan_ie.ptr = NULL; 52 53 if (source->scan_ie.ptr) { 54 target->scan_ie.ptr = qdf_mem_malloc(source->scan_ie.len); 55 if (!target->scan_ie.ptr) 56 target->scan_ie.len = 0; 57 else 58 qdf_mem_copy(target->scan_ie.ptr, 59 source->scan_ie.ptr, source->scan_ie.len); 60 } 61 62 if (source->assoc_ie.ptr) { 63 target->assoc_ie.ptr = qdf_mem_malloc(source->assoc_ie.len); 64 if (!target->assoc_ie.ptr) 65 target->assoc_ie.len = 0; 66 else 67 qdf_mem_copy(target->assoc_ie.ptr, source->assoc_ie.ptr, 68 source->assoc_ie.len); 69 } 70 } 71 72 void 73 mlo_free_connect_ies(struct wlan_cm_connect_req *connect_req) 74 { 75 if (connect_req->scan_ie.ptr) { 76 qdf_mem_free(connect_req->scan_ie.ptr); 77 connect_req->scan_ie.ptr = NULL; 78 } 79 80 if (connect_req->assoc_ie.ptr) { 81 qdf_mem_free(connect_req->assoc_ie.ptr); 82 connect_req->assoc_ie.ptr = NULL; 83 } 84 } 85 86 /* 87 * mlo_get_assoc_link_vdev - API to get assoc link vdev 88 * 89 * @mlo_dev_ctx: pointer to mlo dev context 90 * 91 * Return: MLD assoc link vdev 92 */ 93 static inline struct wlan_objmgr_vdev * 94 mlo_get_assoc_link_vdev(struct wlan_mlo_dev_context *mlo_dev_ctx) 95 { 96 uint8_t i = 0; 97 98 if (!mlo_dev_ctx) 99 return NULL; 100 101 for (i = 0; i < WLAN_UMAC_MLO_MAX_VDEVS; i++) { 102 if (!mlo_dev_ctx->wlan_vdev_list[i]) 103 continue; 104 105 if (wlan_vdev_mlme_is_mlo_vdev(mlo_dev_ctx->wlan_vdev_list[i]) && 106 !wlan_vdev_mlme_is_mlo_link_vdev(mlo_dev_ctx->wlan_vdev_list[i])) 107 return mlo_dev_ctx->wlan_vdev_list[i]; 108 } 109 return NULL; 110 } 111 112 struct wlan_objmgr_vdev * 113 wlan_mlo_get_assoc_link_vdev(struct wlan_objmgr_vdev *vdev) 114 { 115 struct wlan_mlo_dev_context *mlo_dev_ctx = vdev->mlo_dev_ctx; 116 117 if (!mlo_dev_ctx || !wlan_vdev_mlme_is_mlo_vdev(vdev)) 118 return NULL; 119 120 return mlo_get_assoc_link_vdev(mlo_dev_ctx); 121 } 122 123 struct wlan_objmgr_vdev * 124 ucfg_mlo_get_assoc_link_vdev(struct wlan_objmgr_vdev *vdev) 125 { 126 return wlan_mlo_get_assoc_link_vdev(vdev); 127 } 128 129 /** 130 * mlo_is_mld_disconnected - Check whether MLD is disconnected 131 * 132 * @vdev: pointer to vdev 133 * 134 * Return: true if mld is disconnected, false otherwise 135 */ 136 static inline 137 bool mlo_is_mld_disconnected(struct wlan_objmgr_vdev *vdev) 138 { 139 struct wlan_mlo_dev_context *mlo_dev_ctx = vdev->mlo_dev_ctx; 140 uint8_t i = 0; 141 142 if (!mlo_dev_ctx || !wlan_vdev_mlme_is_mlo_vdev(vdev)) 143 return true; 144 145 for (i = 0; i < WLAN_UMAC_MLO_MAX_VDEVS; i++) { 146 if (!mlo_dev_ctx->wlan_vdev_list[i]) 147 continue; 148 149 if (!wlan_cm_is_vdev_disconnected(mlo_dev_ctx->wlan_vdev_list[i])) 150 return false; 151 } 152 return true; 153 } 154 155 bool mlo_is_mld_disconnecting_connecting(struct wlan_objmgr_vdev *vdev) 156 { 157 struct wlan_mlo_dev_context *mlo_dev_ctx = vdev->mlo_dev_ctx; 158 uint8_t i = 0; 159 160 if (!mlo_dev_ctx || !wlan_vdev_mlme_is_mlo_vdev(vdev)) 161 return false; 162 163 for (i = 0; i < WLAN_UMAC_MLO_MAX_VDEVS; i++) { 164 if (!mlo_dev_ctx->wlan_vdev_list[i]) 165 continue; 166 if (wlan_cm_is_vdev_disconnecting(mlo_dev_ctx->wlan_vdev_list[i]) || 167 wlan_cm_is_vdev_connecting(mlo_dev_ctx->wlan_vdev_list[i])) 168 return true; 169 } 170 return false; 171 } 172 173 bool ucfg_mlo_is_mld_disconnected(struct wlan_objmgr_vdev *vdev) 174 { 175 return mlo_is_mld_disconnected(vdev); 176 } 177 178 /** 179 * mlo_send_link_disconnect- Issue the disconnect request on MLD links 180 * 181 * @vdev: pointer to vdev 182 * @source: disconnect source 183 * @reason_code: disconnect reason 184 * @bssid: bssid of AP to disconnect, can be null if not known 185 * 186 * Return: QDF_STATUS 187 */ 188 static QDF_STATUS 189 mlo_send_link_disconnect(struct wlan_objmgr_vdev *vdev, 190 enum wlan_cm_source source, 191 enum wlan_reason_code reason_code, 192 struct qdf_mac_addr *bssid) 193 { 194 uint8_t i = 0; 195 enum wlan_cm_source link_source = source; 196 struct wlan_objmgr_vdev *assoc_vdev = 197 mlo_get_assoc_link_vdev(vdev->mlo_dev_ctx); 198 struct wlan_objmgr_vdev *wlan_vdev_list[WLAN_UMAC_MLO_MAX_VDEVS]; 199 uint16_t vdev_count = 0; 200 struct wlan_mlo_sta *sta_ctx = NULL; 201 202 sta_ctx = vdev->mlo_dev_ctx->sta_ctx; 203 if (!sta_ctx) { 204 mlo_err("Invalid sta_ctx"); 205 return QDF_STATUS_E_FAILURE; 206 } 207 208 if (!assoc_vdev) 209 return QDF_STATUS_E_FAILURE; 210 211 /* 212 * Change the source for the link vdev to make sure it's handled as a 213 * Northbound disconnect in VDEV/PEER state machine. 214 */ 215 if (source != CM_OSIF_DISCONNECT) 216 link_source = CM_MLO_LINK_VDEV_DISCONNECT; 217 218 mlo_sta_get_vdev_list(vdev, &vdev_count, wlan_vdev_list); 219 for (i = 0; i < vdev_count; i++) { 220 if ((wlan_vdev_list[i] != mlo_get_assoc_link_vdev(vdev->mlo_dev_ctx)) && 221 (qdf_test_bit(i, sta_ctx->wlan_connected_links) || 222 (wlan_cm_is_vdev_connected(wlan_vdev_list[i]) && 223 !wlan_peer_is_mlo(wlan_vdev_get_bsspeer(wlan_vdev_list[i]))))) 224 wlan_cm_disconnect(wlan_vdev_list[i], 225 link_source, reason_code, 226 NULL); 227 mlo_release_vdev_ref(wlan_vdev_list[i]); 228 } 229 230 wlan_cm_disconnect(assoc_vdev, 231 source, reason_code, NULL); 232 233 return QDF_STATUS_SUCCESS; 234 } 235 236 static void mlo_free_copied_conn_req(struct wlan_mlo_sta *sta_ctx) 237 { 238 if (sta_ctx) { 239 mlo_debug("enter"); 240 copied_conn_req_lock_acquire(sta_ctx); 241 if (sta_ctx->copied_conn_req) { 242 mlo_free_connect_ies(sta_ctx->copied_conn_req); 243 qdf_mem_free(sta_ctx->copied_conn_req); 244 sta_ctx->copied_conn_req = NULL; 245 } 246 copied_conn_req_lock_release(sta_ctx); 247 } 248 } 249 250 #ifdef WLAN_FEATURE_11BE_MLO_ADV_FEATURE 251 static QDF_STATUS 252 mlo_validate_connect_req(struct wlan_objmgr_vdev *vdev, 253 struct wlan_mlo_dev_context *mlo_dev_ctx, 254 struct wlan_cm_connect_req *req) 255 { 256 /* check back to back connect handling */ 257 return QDF_STATUS_SUCCESS; 258 } 259 260 static QDF_STATUS 261 mlo_validate_disconn_req(struct wlan_objmgr_vdev *vdev, 262 enum wlan_cm_source source, 263 enum wlan_reason_code reason_code, 264 struct qdf_mac_addr *bssid) 265 { 266 return QDF_STATUS_SUCCESS; 267 } 268 269 static QDF_STATUS mlo_validate_mlo_cap(struct wlan_objmgr_vdev *vdev) 270 { 271 return QDF_STATUS_SUCCESS; 272 } 273 274 static inline 275 void mlo_mld_clear_mlo_cap(struct wlan_objmgr_vdev *vdev) 276 { } 277 #else 278 /** 279 * mlo_is_mld_connected - Check whether MLD is connected 280 * 281 * @vdev: pointer to vdev 282 * 283 * Return: true if mld is connected, false otherwise 284 */ 285 static inline 286 bool mlo_is_mld_connected(struct wlan_objmgr_vdev *vdev) 287 { 288 struct wlan_mlo_dev_context *mlo_dev_ctx = vdev->mlo_dev_ctx; 289 uint8_t i = 0; 290 291 if (!mlo_dev_ctx || !wlan_vdev_mlme_is_mlo_vdev(vdev)) 292 return true; 293 294 for (i = 0; i < WLAN_UMAC_MLO_MAX_VDEVS; i++) { 295 if (!mlo_dev_ctx->wlan_vdev_list[i]) 296 continue; 297 298 if (qdf_test_bit(i, mlo_dev_ctx->sta_ctx->wlan_connected_links)) { 299 if (!wlan_cm_is_vdev_connected(mlo_dev_ctx->wlan_vdev_list[i])) 300 return false; 301 } 302 } 303 return true; 304 } 305 306 bool ucfg_mlo_is_mld_connected(struct wlan_objmgr_vdev *vdev) 307 { 308 return mlo_is_mld_connected(vdev); 309 } 310 311 static inline 312 void mlo_mld_clear_mlo_cap(struct wlan_objmgr_vdev *vdev) 313 { 314 struct wlan_mlo_dev_context *mlo_dev_ctx = vdev->mlo_dev_ctx; 315 uint8_t i = 0; 316 317 if (!mlo_dev_ctx || !wlan_vdev_mlme_is_mlo_vdev(vdev)) 318 return; 319 320 for (i = 0; i < WLAN_UMAC_MLO_MAX_VDEVS; i++) { 321 if (!mlo_dev_ctx->wlan_vdev_list[i]) 322 continue; 323 wlan_vdev_mlme_clear_mlo_vdev(mlo_dev_ctx->wlan_vdev_list[i]); 324 wlan_vdev_mlme_clear_mlo_link_vdev(mlo_dev_ctx->wlan_vdev_list[i]); 325 } 326 } 327 328 void ucfg_mlo_mld_clear_mlo_cap(struct wlan_objmgr_vdev *vdev) 329 { 330 mlo_mld_clear_mlo_cap(vdev); 331 } 332 333 static void 334 mlo_cm_handle_connect_in_disconnection_state(struct wlan_objmgr_vdev *vdev, 335 struct wlan_cm_connect_req *req) 336 { 337 struct wlan_mlo_dev_context *mlo_dev_ctx = vdev->mlo_dev_ctx; 338 struct wlan_mlo_sta *sta_ctx; 339 340 if (!mlo_dev_ctx) { 341 mlo_err("ML dev ctx is NULL"); 342 return; 343 } 344 345 sta_ctx = mlo_dev_ctx->sta_ctx; 346 if (!sta_ctx->connect_req) 347 sta_ctx->connect_req = qdf_mem_malloc( 348 sizeof(struct wlan_cm_connect_req)); 349 350 if (sta_ctx->connect_req) { 351 qdf_mem_copy(sta_ctx->connect_req, req, 352 sizeof(struct wlan_cm_connect_req)); 353 mlo_allocate_and_copy_ies(sta_ctx->connect_req, req); 354 } else { 355 mlo_err("Failed to allocate connect req"); 356 } 357 } 358 359 static QDF_STATUS 360 mlo_validate_disconn_req(struct wlan_objmgr_vdev *vdev, 361 enum wlan_cm_source source, 362 enum wlan_reason_code reason_code, 363 struct qdf_mac_addr *bssid) 364 { 365 struct wlan_mlo_dev_context *mlo_dev = vdev->mlo_dev_ctx; 366 struct wlan_mlo_sta *sta_ctx = mlo_dev->sta_ctx; 367 uint8_t i = 0; 368 369 for (i = 0; i < WLAN_UMAC_MLO_MAX_VDEVS; i++) { 370 if (!mlo_dev->wlan_vdev_list[i]) 371 continue; 372 373 if (wlan_cm_is_vdev_connecting(mlo_dev->wlan_vdev_list[i])) { 374 if (!wlan_vdev_mlme_is_mlo_link_vdev( 375 mlo_dev->wlan_vdev_list[i])) 376 return QDF_STATUS_SUCCESS; 377 378 if (!sta_ctx->disconn_req) 379 sta_ctx->disconn_req = 380 qdf_mem_malloc( 381 sizeof(struct wlan_cm_disconnect_req)); 382 383 if (!sta_ctx->disconn_req) 384 return QDF_STATUS_SUCCESS; 385 386 sta_ctx->disconn_req->vdev_id = 387 wlan_vdev_get_id(vdev); 388 sta_ctx->disconn_req->source = source; 389 sta_ctx->disconn_req->reason_code = reason_code; 390 if (bssid) 391 qdf_copy_macaddr(&sta_ctx->disconn_req->bssid, 392 bssid); 393 return QDF_STATUS_E_BUSY; 394 } else if (wlan_cm_is_vdev_connected(mlo_dev->wlan_vdev_list[i]) && 395 !wlan_vdev_mlme_is_mlo_link_vdev( 396 mlo_dev->wlan_vdev_list[i]) && 397 wlan_peer_is_mlo(wlan_vdev_get_bsspeer( 398 mlo_dev->wlan_vdev_list[i]))) { 399 /* If the vdev is moved to connected state but 400 * MLO mgr is not yet notified, defer disconnect 401 * as it can cause race between connect complete 402 * and disconnect initiation 403 */ 404 if (!qdf_test_bit(i, sta_ctx->wlan_connected_links)) { 405 if (!sta_ctx->disconn_req) 406 sta_ctx->disconn_req = 407 qdf_mem_malloc( 408 sizeof(struct wlan_cm_disconnect_req)); 409 410 if (!sta_ctx->disconn_req) 411 return QDF_STATUS_SUCCESS; 412 413 sta_ctx->disconn_req->vdev_id = 414 wlan_vdev_get_id(vdev); 415 sta_ctx->disconn_req->source = source; 416 sta_ctx->disconn_req->reason_code = reason_code; 417 if (bssid) 418 qdf_copy_macaddr(&sta_ctx->disconn_req->bssid, 419 bssid); 420 421 return QDF_STATUS_E_BUSY; 422 } 423 } 424 } 425 return QDF_STATUS_SUCCESS; 426 } 427 428 static QDF_STATUS mlo_disconnect_no_lock(struct wlan_objmgr_vdev *vdev, 429 enum wlan_cm_source source, 430 enum wlan_reason_code reason_code, 431 struct qdf_mac_addr *bssid) 432 { 433 struct wlan_mlo_dev_context *mlo_dev_ctx = vdev->mlo_dev_ctx; 434 struct wlan_mlo_sta *sta_ctx = NULL; 435 QDF_STATUS status = QDF_STATUS_SUCCESS; 436 struct wlan_objmgr_vdev *assoc_vdev = NULL; 437 uint8_t i = 0; 438 439 if (mlo_dev_ctx) 440 sta_ctx = mlo_dev_ctx->sta_ctx; 441 if (sta_ctx) { 442 mlo_free_copied_conn_req(sta_ctx); 443 } else { 444 return QDF_STATUS_E_FAILURE; 445 } 446 447 if (wlan_vdev_mlme_is_mlo_vdev(vdev)) { 448 sta_ctx = mlo_dev_ctx->sta_ctx; 449 if (!sta_ctx) 450 return QDF_STATUS_E_FAILURE; 451 452 assoc_vdev = mlo_get_assoc_link_vdev(mlo_dev_ctx); 453 if (!assoc_vdev) 454 return QDF_STATUS_E_FAILURE; 455 456 if (sta_ctx->connect_req) { 457 mlo_free_connect_ies(sta_ctx->connect_req); 458 qdf_mem_free(sta_ctx->connect_req); 459 sta_ctx->connect_req = NULL; 460 } 461 462 status = mlo_validate_disconn_req(vdev, source, 463 reason_code, bssid); 464 if (QDF_IS_STATUS_ERROR(status)) { 465 mlo_err("Connect in progress, deferring disconnect"); 466 return status; 467 } 468 469 for (i = 0; i < WLAN_UMAC_MLO_MAX_VDEVS; i++) { 470 if (!mlo_dev_ctx->wlan_vdev_list[i]) 471 continue; 472 473 if ((mlo_dev_ctx->wlan_vdev_list[i] != assoc_vdev) && 474 (qdf_test_bit(i, mlo_dev_ctx->sta_ctx->wlan_connected_links) || 475 (wlan_cm_is_vdev_connected(mlo_dev_ctx->wlan_vdev_list[i]) && 476 !wlan_peer_is_mlo(wlan_vdev_get_bsspeer(mlo_dev_ctx->wlan_vdev_list[i]))))) 477 wlan_cm_disconnect(mlo_dev_ctx->wlan_vdev_list[i], 478 source, reason_code, 479 NULL); 480 } 481 482 wlan_cm_disconnect(assoc_vdev, 483 source, reason_code, NULL); 484 } 485 486 return status; 487 } 488 489 static void 490 mlo_cm_handle_connect_in_connection_state(struct wlan_objmgr_vdev *vdev, 491 struct wlan_cm_connect_req *req) 492 { 493 mlo_disconnect_no_lock(vdev, CM_INTERNAL_DISCONNECT, 494 REASON_UNSPEC_FAILURE, NULL); 495 mlo_cm_handle_connect_in_disconnection_state(vdev, req); 496 } 497 498 static QDF_STATUS 499 mlo_validate_connect_req(struct wlan_objmgr_vdev *vdev, 500 struct wlan_mlo_dev_context *mlo_dev_ctx, 501 struct wlan_cm_connect_req *req) 502 { 503 uint8_t i = 0; 504 QDF_STATUS status = QDF_STATUS_SUCCESS; 505 506 if (!wlan_vdev_mlme_is_mlo_vdev(vdev)) 507 return QDF_STATUS_SUCCESS; 508 509 // Handle connect in various states 510 for (i = 0; i < WLAN_UMAC_MLO_MAX_VDEVS; i++) { 511 if (!mlo_dev_ctx->wlan_vdev_list[i]) 512 continue; 513 514 if ((wlan_cm_is_vdev_connected(mlo_dev_ctx->wlan_vdev_list[i])) || 515 (wlan_cm_is_vdev_connecting(mlo_dev_ctx->wlan_vdev_list[i])) || 516 (wlan_cm_is_vdev_roaming(mlo_dev_ctx->wlan_vdev_list[i]))) { 517 mlo_cm_handle_connect_in_connection_state(mlo_dev_ctx->wlan_vdev_list[i], req); 518 return QDF_STATUS_E_BUSY; 519 } else if (wlan_cm_is_vdev_disconnecting(mlo_dev_ctx->wlan_vdev_list[i])) { 520 mlo_cm_handle_connect_in_disconnection_state(mlo_dev_ctx->wlan_vdev_list[i], req); 521 return QDF_STATUS_E_BUSY; 522 } 523 524 /* 525 * mlo_connect: update wlan_connect_req_links in 526 * wlan_cfg80211_conect on osif_cm_connect, 527 * Validate pre checks for connection 528 */ 529 if (qdf_test_bit(i, mlo_dev_ctx->sta_ctx->wlan_connect_req_links)) { 530 status = mlo_mlme_validate_conn_req( 531 mlo_dev_ctx->wlan_vdev_list[i], NULL); 532 if (status != QDF_STATUS_SUCCESS) 533 return status; 534 /* 535 * clone security params in all partner sta vaps 536 */ 537 mlo_mlme_clone_sta_security( 538 mlo_dev_ctx->wlan_vdev_list[i], req); 539 } 540 } 541 return status; 542 } 543 544 static QDF_STATUS mlo_validate_mlo_cap(struct wlan_objmgr_vdev *vdev) 545 { 546 if (wlan_vdev_mlme_is_mlo_vdev(vdev)) 547 return QDF_STATUS_SUCCESS; 548 549 return QDF_STATUS_E_FAILURE; 550 } 551 #endif 552 553 QDF_STATUS mlo_set_cu_bpcc(struct wlan_objmgr_vdev *vdev, 554 uint8_t vdev_id, uint8_t bpcc) 555 { 556 struct wlan_mlo_dev_context *mlo_dev_ctx; 557 struct mlo_sta_cu_params *cu_param; 558 uint8_t i; 559 560 mlo_dev_ctx = vdev->mlo_dev_ctx; 561 if (!mlo_dev_ctx) { 562 mlo_err("ML dev ctx is NULL"); 563 return QDF_STATUS_E_INVAL; 564 } 565 566 cu_param = &mlo_dev_ctx->sta_ctx->mlo_cu_param[0]; 567 for (i = 0; i < WLAN_UMAC_MLO_MAX_VDEVS; i++) { 568 if (cu_param[i].initialized && cu_param[i].vdev_id == vdev_id) { 569 cu_param[i].bpcc = bpcc; 570 return QDF_STATUS_SUCCESS; 571 } 572 } 573 574 return QDF_STATUS_E_INVAL; 575 } 576 577 QDF_STATUS mlo_get_cu_bpcc(struct wlan_objmgr_vdev *vdev, 578 uint8_t vdev_id, uint8_t *bpcc) 579 { 580 struct wlan_mlo_dev_context *mlo_dev_ctx; 581 struct mlo_sta_cu_params *cu_param; 582 uint8_t i; 583 584 mlo_dev_ctx = vdev->mlo_dev_ctx; 585 if (!mlo_dev_ctx) { 586 mlo_err("ML dev ctx is NULL"); 587 return QDF_STATUS_E_INVAL; 588 } 589 590 cu_param = &mlo_dev_ctx->sta_ctx->mlo_cu_param[0]; 591 for (i = 0; i < WLAN_UMAC_MLO_MAX_VDEVS; i++) { 592 if (cu_param[i].initialized && 593 cu_param[i].vdev_id == vdev_id) { 594 *bpcc = cu_param[i].bpcc; 595 return QDF_STATUS_SUCCESS; 596 } 597 } 598 599 return QDF_STATUS_E_INVAL; 600 } 601 602 void mlo_init_cu_bpcc(struct wlan_mlo_dev_context *mlo_dev_ctx, 603 uint8_t vdev_id) 604 { 605 uint8_t i; 606 struct mlo_sta_cu_params *cu_param; 607 uint8_t empty_slot = 0xff; 608 609 cu_param = &mlo_dev_ctx->sta_ctx->mlo_cu_param[0]; 610 611 for (i = 0; i < WLAN_UMAC_MLO_MAX_VDEVS; i++) { 612 if (cu_param[i].initialized && 613 cu_param[i].vdev_id == vdev_id) { 614 cu_param[i].bpcc = 0; 615 return; 616 } 617 618 if (!cu_param[i].initialized && empty_slot == 0xff) 619 empty_slot = i; 620 } 621 622 if (empty_slot != 0xff) { 623 cu_param[empty_slot].bpcc = 0; 624 cu_param[empty_slot].vdev_id = vdev_id; 625 cu_param[empty_slot].initialized = true; 626 mlo_debug("init cu bpcc idx %d, vdev_id %d", 627 empty_slot, vdev_id); 628 } else { 629 mlo_debug("No bpcc idx for vdev_id %d", vdev_id); 630 } 631 } 632 633 void mlo_clear_cu_bpcc(struct wlan_objmgr_vdev *vdev) 634 { 635 struct wlan_mlo_dev_context *mlo_dev_ctx = NULL; 636 struct wlan_mlo_sta *sta_ctx = NULL; 637 uint32_t size; 638 639 if (!vdev) 640 return; 641 642 mlo_dev_ctx = vdev->mlo_dev_ctx; 643 if (!mlo_dev_ctx) 644 return; 645 646 sta_ctx = mlo_dev_ctx->sta_ctx; 647 if (!sta_ctx) 648 return; 649 650 size = sizeof(sta_ctx->mlo_cu_param); 651 qdf_mem_zero(sta_ctx->mlo_cu_param, size); 652 653 mlo_debug("clear cu bpcc"); 654 } 655 656 QDF_STATUS mlo_connect(struct wlan_objmgr_vdev *vdev, 657 struct wlan_cm_connect_req *req) 658 { 659 struct wlan_mlo_dev_context *mlo_dev_ctx; 660 struct wlan_mlo_sta *sta_ctx = NULL; 661 QDF_STATUS status = QDF_STATUS_SUCCESS; 662 663 mlo_dev_ctx = vdev->mlo_dev_ctx; 664 if (mlo_dev_ctx) 665 sta_ctx = mlo_dev_ctx->sta_ctx; 666 if (sta_ctx) { 667 status = mlo_validate_mlo_cap(vdev); 668 if (QDF_IS_STATUS_ERROR(status)) 669 return wlan_cm_start_connect(vdev, req); 670 671 mlo_dev_lock_acquire(mlo_dev_ctx); 672 status = mlo_validate_connect_req(vdev, mlo_dev_ctx, req); 673 copied_conn_req_lock_acquire(sta_ctx); 674 if (!sta_ctx->copied_conn_req) 675 sta_ctx->copied_conn_req = qdf_mem_malloc( 676 sizeof(struct wlan_cm_connect_req)); 677 else 678 mlo_free_connect_ies(sta_ctx->copied_conn_req); 679 680 mlo_debug("storing orig connect req"); 681 if (sta_ctx->copied_conn_req) { 682 qdf_mem_copy(sta_ctx->copied_conn_req, req, 683 sizeof(struct wlan_cm_connect_req)); 684 mlo_allocate_and_copy_ies(sta_ctx->copied_conn_req, 685 req); 686 copied_conn_req_lock_release(sta_ctx); 687 } else { 688 mlo_err("Failed to allocate orig connect req"); 689 copied_conn_req_lock_release(sta_ctx); 690 mlo_dev_lock_release(mlo_dev_ctx); 691 692 return QDF_STATUS_E_NOMEM; 693 } 694 695 if (QDF_IS_STATUS_SUCCESS(status)) { 696 mlo_clear_cu_bpcc(vdev); 697 mlo_clear_connected_links_bmap(vdev); 698 mlo_dev_lock_release(mlo_dev_ctx); 699 700 status = wlan_cm_start_connect(vdev, req); 701 if (QDF_IS_STATUS_ERROR(status)) 702 mlo_mld_clear_mlo_cap(vdev); 703 return status; 704 } 705 706 mlo_dev_lock_release(mlo_dev_ctx); 707 708 return status; 709 } 710 711 return wlan_cm_start_connect(vdev, req); 712 } 713 714 static inline void 715 mlo_update_connect_req_chan_info(struct wlan_cm_connect_req *req) 716 { 717 req->chan_freq = 0; 718 req->chan_freq_hint = 0; 719 } 720 721 /** 722 * mlo_prepare_and_send_connect- Prepare and send the connect req 723 * 724 * @vdev: vdev pointer 725 * @ml_parnter_info: ml partner link info 726 * @link_info: link info on which connect req will be sent 727 * @ssid: ssid to connect 728 * 729 * Return: none 730 */ 731 732 static void 733 mlo_prepare_and_send_connect(struct wlan_objmgr_vdev *vdev, 734 struct mlo_partner_info ml_parnter_info, 735 struct mlo_link_info link_info, 736 struct wlan_ssid ssid) 737 { 738 struct wlan_cm_connect_req req = {0}; 739 struct wlan_mlo_dev_context *mlo_dev_ctx = vdev->mlo_dev_ctx; 740 struct wlan_mlo_sta *sta_ctx; 741 742 if (!mlo_dev_ctx) { 743 mlo_err("ML dev ctx is NULL"); 744 return; 745 } 746 747 sta_ctx = mlo_dev_ctx->sta_ctx; 748 749 mlo_debug("Partner link connect mac:" QDF_MAC_ADDR_FMT 750 " bssid:" QDF_MAC_ADDR_FMT " vdev_id:%d", 751 QDF_MAC_ADDR_REF(wlan_vdev_mlme_get_macaddr(vdev)), 752 QDF_MAC_ADDR_REF(link_info.link_addr.bytes), 753 wlan_vdev_get_id(vdev)); 754 755 qdf_mem_copy(&req, sta_ctx->copied_conn_req, 756 sizeof(struct wlan_cm_connect_req)); 757 758 mlo_update_connect_req_chan_info(&req); 759 760 qdf_mem_copy(req.bssid.bytes, 761 link_info.link_addr.bytes, 762 QDF_MAC_ADDR_SIZE); 763 764 qdf_mem_copy(&req.ml_parnter_info, 765 &ml_parnter_info, 766 sizeof(struct mlo_partner_info)); 767 768 req.ssid.length = ssid.length; 769 qdf_mem_copy(&req.ssid.ssid, &ssid.ssid, ssid.length); 770 771 mlo_allocate_and_copy_ies(&req, sta_ctx->copied_conn_req); 772 if (!req.assoc_ie.ptr) 773 mlo_err("Failed to allocate assoc IEs"); 774 775 if (!req.scan_ie.ptr) 776 mlo_err("Failed to allocate scan IEs"); 777 778 /* Reset crypto auth type for partner link. 779 * It will be set based on partner scan cache entry 780 */ 781 req.crypto.auth_type = 0; 782 783 wlan_cm_start_connect(vdev, &req); 784 mlo_free_connect_ies(&req); 785 } 786 787 /** 788 * mlo_send_link_connect- Create/Issue the connection on secondary link 789 * 790 * @vdev: vdev pointer 791 * @mlo_dev_ctx: ml dev context 792 * @assoc_rsp: assoc response 793 * @ml_parnter_info: ml partner link info 794 * 795 * Return: none 796 */ 797 #ifdef WLAN_FEATURE_11BE_MLO_ADV_FEATURE 798 static void 799 mlo_send_link_connect(struct wlan_objmgr_vdev *vdev, 800 struct wlan_mlo_dev_context *mlo_dev_ctx, 801 struct element_info *assoc_rsp, 802 struct mlo_partner_info *ml_parnter_info) 803 { 804 /* Create the secondary interface, Send keys if the last link */ 805 uint8_t i, partner_idx = 0; 806 struct wlan_ssid ssid = {0}; 807 struct wlan_objmgr_vdev *wlan_vdev_list[WLAN_UMAC_MLO_MAX_VDEVS]; 808 uint16_t vdev_count = 0; 809 810 mlo_debug("Sending link connect on partner interface"); 811 wlan_vdev_mlme_get_ssid( 812 vdev, ssid.ssid, 813 &ssid.length); 814 815 if (!ml_parnter_info->num_partner_links) { 816 mlo_err("No partner info in connect resp"); 817 return; 818 } 819 820 if(wlan_vdev_mlme_is_mlo_link_vdev(vdev)) 821 return; 822 823 mlo_sta_get_vdev_list(vdev, &vdev_count, wlan_vdev_list); 824 for (i = 0; i < vdev_count; i++) { 825 if (wlan_vdev_list[i] == vdev) { 826 mlo_release_vdev_ref(wlan_vdev_list[i]); 827 continue; 828 } 829 wlan_vdev_mlme_set_mlo_vdev(mlo_dev_ctx->wlan_vdev_list[i]); 830 wlan_vdev_mlme_set_mlo_link_vdev(mlo_dev_ctx->wlan_vdev_list[i]); 831 wlan_vdev_set_link_id( 832 wlan_vdev_list[i], 833 ml_parnter_info->partner_link_info[partner_idx].link_id); 834 ml_parnter_info->partner_link_info[partner_idx].vdev_id = 835 wlan_vdev_get_id(mlo_dev_ctx->wlan_vdev_list[i]); 836 wlan_crypto_free_vdev_key(wlan_vdev_list[i]); 837 mlo_prepare_and_send_connect( 838 wlan_vdev_list[i], 839 *ml_parnter_info, 840 ml_parnter_info->partner_link_info[partner_idx], 841 ssid); 842 mlo_update_connected_links(wlan_vdev_list[i], 1); 843 partner_idx++; 844 mlo_release_vdev_ref(wlan_vdev_list[i]); 845 } 846 } 847 #else 848 static void 849 mlo_send_link_connect(struct wlan_objmgr_vdev *vdev, 850 struct wlan_mlo_dev_context *mlo_dev_ctx, 851 struct element_info *assoc_rsp, 852 struct mlo_partner_info *ml_parnter_info) 853 { 854 struct wlan_ssid ssid = {0}; 855 uint8_t i = 0; 856 uint8_t j = 0; 857 858 if (!ml_parnter_info->num_partner_links) { 859 mlo_err("No partner info in connect resp"); 860 return; 861 } 862 863 mlo_dev_lock_acquire(mlo_dev_ctx); 864 if (wlan_cm_is_vdev_connected(vdev)) { 865 for (i = 0; i < WLAN_UMAC_MLO_MAX_VDEVS; i++) { 866 if (!mlo_dev_ctx->wlan_vdev_list[i]) 867 continue; 868 /* 869 * mlo_connect: update wlan_connected_links bitmap from 870 * assoc resp parsing 871 */ 872 if (qdf_test_bit(i, mlo_dev_ctx->sta_ctx->wlan_connected_links)) { 873 if (wlan_cm_is_vdev_disconnected( 874 mlo_dev_ctx->wlan_vdev_list[i])) { 875 for (j = 0; j < ml_parnter_info->num_partner_links; j++) { 876 if (mlo_dev_ctx->wlan_vdev_list[i]->vdev_mlme.mlo_link_id == 877 ml_parnter_info->partner_link_info[j].link_id) 878 break; 879 } 880 if (j < ml_parnter_info->num_partner_links) { 881 wlan_vdev_mlme_get_ssid( 882 vdev, ssid.ssid, 883 &ssid.length); 884 mlo_prepare_and_send_connect( 885 mlo_dev_ctx->wlan_vdev_list[i], 886 *ml_parnter_info, 887 ml_parnter_info->partner_link_info[j], 888 ssid); 889 } 890 mlo_dev_lock_release(mlo_dev_ctx); 891 return; 892 } 893 } 894 } 895 } 896 mlo_dev_lock_release(mlo_dev_ctx); 897 } 898 #endif 899 900 void 901 mlo_update_connected_links_bmap(struct wlan_mlo_dev_context *mlo_dev_ctx, 902 struct mlo_partner_info ml_parnter_info) 903 { 904 uint8_t i = 0; 905 uint8_t j = 0; 906 907 if (!mlo_dev_ctx) { 908 mlo_err("ML dev ctx is NULL"); 909 return; 910 } 911 912 for (i = 0; i < WLAN_UMAC_MLO_MAX_VDEVS; i++) { 913 if (!mlo_dev_ctx->wlan_vdev_list[i]) 914 continue; 915 916 for (j = 0; j < ml_parnter_info.num_partner_links; j++) { 917 if (wlan_vdev_get_link_id(mlo_dev_ctx->wlan_vdev_list[i]) == 918 ml_parnter_info.partner_link_info[j].link_id) 919 mlo_update_connected_links( 920 mlo_dev_ctx->wlan_vdev_list[i], 1); 921 } 922 } 923 } 924 925 void mlo_clear_connected_links_bmap(struct wlan_objmgr_vdev *vdev) 926 { 927 struct wlan_mlo_dev_context *mlo_dev_ctx = NULL; 928 struct wlan_mlo_sta *sta_ctx = NULL; 929 930 if (!vdev) 931 return; 932 933 mlo_dev_ctx = vdev->mlo_dev_ctx; 934 if (!mlo_dev_ctx) 935 return; 936 937 sta_ctx = mlo_dev_ctx->sta_ctx; 938 if (!sta_ctx) 939 return; 940 941 qdf_mem_zero(sta_ctx->wlan_connected_links, 942 sizeof(sta_ctx->wlan_connected_links)); 943 } 944 945 static QDF_STATUS ml_activate_disconnect_req_sched_cb(struct scheduler_msg *msg) 946 { 947 struct wlan_objmgr_vdev *vdev = msg->bodyptr; 948 949 if (!vdev) { 950 mlme_err("Null input vdev"); 951 return QDF_STATUS_E_INVAL; 952 } 953 954 mlo_disconnect(vdev, CM_OSIF_DISCONNECT, 955 REASON_UNSPEC_FAILURE, NULL); 956 957 wlan_objmgr_vdev_release_ref(vdev, WLAN_MLO_MGR_ID); 958 return QDF_STATUS_SUCCESS; 959 } 960 961 static QDF_STATUS ml_activate_disconnect_req_flush_cb(struct scheduler_msg *msg) 962 { 963 struct wlan_objmgr_vdev *vdev = msg->bodyptr; 964 965 if (!vdev) { 966 mlme_err("Null input vdev"); 967 return QDF_STATUS_E_INVAL; 968 } 969 970 wlan_objmgr_vdev_release_ref(vdev, WLAN_MLO_MGR_ID); 971 return QDF_STATUS_SUCCESS; 972 } 973 974 static QDF_STATUS ml_activate_pend_disconn_req_cb(struct scheduler_msg *msg) 975 { 976 struct wlan_objmgr_vdev *vdev = msg->bodyptr; 977 struct wlan_mlo_dev_context *mlo_dev_ctx = NULL; 978 struct wlan_mlo_sta *sta_ctx = NULL; 979 980 if (!vdev) { 981 mlme_err("Null input vdev"); 982 return QDF_STATUS_E_INVAL; 983 } 984 985 mlo_dev_ctx = vdev->mlo_dev_ctx; 986 sta_ctx = mlo_dev_ctx->sta_ctx; 987 mlo_disconnect_req(vdev, sta_ctx->disconn_req->source, 988 sta_ctx->disconn_req->reason_code, 989 &sta_ctx->disconn_req->bssid, false); 990 991 qdf_mem_free(sta_ctx->disconn_req); 992 sta_ctx->disconn_req = NULL; 993 wlan_objmgr_vdev_release_ref(vdev, WLAN_MLO_MGR_ID); 994 995 return QDF_STATUS_SUCCESS; 996 } 997 998 static QDF_STATUS ml_activate_pend_disconn_req_flush_cb( 999 struct scheduler_msg *msg) 1000 { 1001 struct wlan_objmgr_vdev *vdev = msg->bodyptr; 1002 1003 if (!vdev) { 1004 mlme_err("Null input vdev"); 1005 return QDF_STATUS_E_INVAL; 1006 } 1007 1008 wlan_objmgr_vdev_release_ref(vdev, WLAN_MLO_MGR_ID); 1009 return QDF_STATUS_SUCCESS; 1010 } 1011 1012 #ifdef WLAN_FEATURE_11BE_MLO_ADV_FEATURE 1013 static inline 1014 QDF_STATUS mlo_post_disconnect_msg(struct scheduler_msg *msg) 1015 { 1016 return scheduler_post_message( 1017 QDF_MODULE_ID_OS_IF, 1018 QDF_MODULE_ID_SCAN, 1019 QDF_MODULE_ID_OS_IF, 1020 msg); 1021 } 1022 #else 1023 static inline 1024 QDF_STATUS mlo_post_disconnect_msg(struct scheduler_msg *msg) 1025 { 1026 return scheduler_post_message( 1027 QDF_MODULE_ID_MLME, 1028 QDF_MODULE_ID_MLME, 1029 QDF_MODULE_ID_MLME, 1030 msg); 1031 } 1032 #endif 1033 1034 void mlo_handle_sta_link_connect_failure(struct wlan_objmgr_vdev *vdev, 1035 struct wlan_cm_connect_resp *rsp) 1036 { 1037 struct wlan_mlo_dev_context *mlo_dev_ctx = vdev->mlo_dev_ctx; 1038 struct scheduler_msg msg = {0}; 1039 QDF_STATUS ret; 1040 struct wlan_objmgr_vdev *assoc_vdev; 1041 1042 if (!mlo_dev_ctx) { 1043 mlo_err("ML dev ctx is NULL"); 1044 return; 1045 } 1046 1047 assoc_vdev = mlo_get_assoc_link_vdev(mlo_dev_ctx); 1048 if (!assoc_vdev) { 1049 mlo_err("Assoc Vdev is NULL"); 1050 return; 1051 } 1052 1053 if (vdev != assoc_vdev) { 1054 mlo_update_connected_links(vdev, 0); 1055 if (rsp->reason == CM_NO_CANDIDATE_FOUND || 1056 rsp->reason == CM_HW_MODE_FAILURE || 1057 rsp->reason == CM_SER_FAILURE) { 1058 ret = wlan_objmgr_vdev_try_get_ref( 1059 assoc_vdev, WLAN_MLO_MGR_ID); 1060 if (QDF_IS_STATUS_ERROR(ret)) { 1061 mlo_err("Failed to get ref vdev_id %d", 1062 wlan_vdev_get_id(assoc_vdev)); 1063 return; 1064 } 1065 /* Since these failures happen in same context. use 1066 * scheduler to avoid deadlock by deferring context 1067 */ 1068 msg.bodyptr = assoc_vdev; 1069 msg.callback = ml_activate_disconnect_req_sched_cb; 1070 msg.flush_callback = 1071 ml_activate_disconnect_req_flush_cb; 1072 mlo_post_disconnect_msg(&msg); 1073 if (QDF_IS_STATUS_ERROR(ret)) { 1074 wlan_objmgr_vdev_release_ref( 1075 assoc_vdev, 1076 WLAN_MLO_MGR_ID); 1077 return; 1078 } 1079 } else { 1080 mlo_disconnect(vdev, CM_OSIF_DISCONNECT, 1081 REASON_UNSPEC_FAILURE, NULL); 1082 } 1083 } 1084 } 1085 1086 void mlo_handle_pending_disconnect(struct wlan_objmgr_vdev *vdev) 1087 { 1088 struct scheduler_msg msg = {0}; 1089 QDF_STATUS ret; 1090 1091 ret = wlan_objmgr_vdev_try_get_ref( 1092 vdev, WLAN_MLO_MGR_ID); 1093 if (QDF_IS_STATUS_ERROR(ret)) { 1094 mlo_err("Failed to get ref vdev_id %d", 1095 wlan_vdev_get_id(vdev)); 1096 return; 1097 } 1098 1099 msg.bodyptr = vdev; 1100 msg.callback = ml_activate_pend_disconn_req_cb; 1101 msg.flush_callback = 1102 ml_activate_pend_disconn_req_flush_cb; 1103 ret = mlo_post_disconnect_msg(&msg); 1104 if (QDF_IS_STATUS_ERROR(ret)) { 1105 mlo_err("Failed to post scheduler msg"); 1106 wlan_objmgr_vdev_release_ref( 1107 vdev, 1108 WLAN_MLO_MGR_ID); 1109 QDF_BUG(0); 1110 return; 1111 } 1112 } 1113 1114 void mlo_sta_link_connect_notify(struct wlan_objmgr_vdev *vdev, 1115 struct wlan_cm_connect_resp *rsp) 1116 { 1117 struct wlan_mlo_dev_context *mlo_dev_ctx = vdev->mlo_dev_ctx; 1118 struct wlan_mlo_sta *sta_ctx = NULL; 1119 1120 if (mlo_dev_ctx) { 1121 sta_ctx = mlo_dev_ctx->sta_ctx; 1122 } else { 1123 mlo_debug_rl("mlo_dev_ctx is NULL"); 1124 return; 1125 } 1126 1127 if (sta_ctx && sta_ctx->disconn_req) { 1128 mlo_debug("Handle pending disocnnect for vdev %d", 1129 wlan_vdev_get_id(vdev)); 1130 mlo_handle_pending_disconnect(vdev); 1131 return; 1132 } 1133 1134 if (wlan_cm_is_vdev_disconnected(vdev)) 1135 mlo_free_copied_conn_req(sta_ctx); 1136 1137 if (wlan_vdev_mlme_is_mlo_vdev(vdev)) { 1138 mlo_debug("Vdev: %d", wlan_vdev_get_id(vdev)); 1139 if (wlan_cm_is_vdev_disconnected(vdev)) { 1140 mlo_handle_sta_link_connect_failure(vdev, rsp); 1141 return; 1142 } else if (!wlan_cm_is_vdev_connected(vdev)) { 1143 /* If vdev is not in disconnected or connected state, 1144 * then the event is received due to connect req being 1145 * flushed. Hence, ignore this event 1146 */ 1147 return; 1148 } 1149 1150 if (!wlan_vdev_mlme_is_mlo_link_vdev(vdev) && sta_ctx) { 1151 if (sta_ctx->assoc_rsp.ptr) { 1152 qdf_mem_free(sta_ctx->assoc_rsp.ptr); 1153 sta_ctx->assoc_rsp.ptr = NULL; 1154 } 1155 sta_ctx->assoc_rsp.len = rsp->connect_ies.assoc_rsp.len; 1156 sta_ctx->assoc_rsp.ptr = 1157 qdf_mem_malloc(rsp->connect_ies.assoc_rsp.len); 1158 if (!sta_ctx->assoc_rsp.ptr) { 1159 QDF_ASSERT(0); 1160 return; 1161 } 1162 if (rsp->connect_ies.assoc_rsp.ptr) 1163 qdf_mem_copy(sta_ctx->assoc_rsp.ptr, 1164 rsp->connect_ies.assoc_rsp.ptr, 1165 rsp->connect_ies.assoc_rsp.len); 1166 /* Update connected_links_bmap for all vdev taking 1167 * part in association 1168 */ 1169 mlo_update_connected_links(vdev, 1); 1170 mlo_update_connected_links_bmap(mlo_dev_ctx, 1171 rsp->ml_parnter_info); 1172 } 1173 mlo_send_link_connect(vdev, mlo_dev_ctx, 1174 &rsp->connect_ies.assoc_rsp, 1175 &rsp->ml_parnter_info); 1176 } 1177 } 1178 1179 /** 1180 * mlo_send_link_disconnect_sync- Issue sync the disconnect request on MLD links 1181 * 1182 * @mlo_dev_ctx: pointer to mlo dev context 1183 * @source: disconnect source 1184 * @reason_code: disconnect reason 1185 * @bssid: bssid of AP to disconnect, can be null if not known 1186 * 1187 * Return: QDF_STATUS 1188 */ 1189 static QDF_STATUS 1190 mlo_send_link_disconnect_sync(struct wlan_mlo_dev_context *mlo_dev_ctx, 1191 enum wlan_cm_source source, 1192 enum wlan_reason_code reason_code, 1193 struct qdf_mac_addr *bssid) 1194 { 1195 uint8_t i; 1196 struct wlan_objmgr_vdev *assoc_vdev = 1197 mlo_get_assoc_link_vdev(mlo_dev_ctx); 1198 1199 if (!assoc_vdev) 1200 return QDF_STATUS_E_FAILURE; 1201 1202 for (i = 0; i < WLAN_UMAC_MLO_MAX_VDEVS; i++) { 1203 if (!mlo_dev_ctx->wlan_vdev_list[i]) 1204 continue; 1205 1206 if (mlo_dev_ctx->wlan_vdev_list[i] != 1207 mlo_get_assoc_link_vdev(mlo_dev_ctx)) 1208 wlan_cm_disconnect_sync(mlo_dev_ctx->wlan_vdev_list[i], 1209 source, reason_code); 1210 } 1211 1212 wlan_cm_disconnect_sync(assoc_vdev, 1213 source, reason_code); 1214 return QDF_STATUS_SUCCESS; 1215 } 1216 1217 static QDF_STATUS mlo_disconnect_req(struct wlan_objmgr_vdev *vdev, 1218 enum wlan_cm_source source, 1219 enum wlan_reason_code reason_code, 1220 struct qdf_mac_addr *bssid, 1221 bool validate_req) 1222 { 1223 struct wlan_mlo_dev_context *mlo_dev_ctx = NULL; 1224 struct wlan_mlo_sta *sta_ctx = NULL; 1225 QDF_STATUS status = QDF_STATUS_SUCCESS; 1226 1227 if (!vdev) 1228 return QDF_STATUS_E_FAILURE; 1229 1230 mlo_dev_ctx = vdev->mlo_dev_ctx; 1231 if (mlo_dev_ctx) 1232 sta_ctx = mlo_dev_ctx->sta_ctx; 1233 if (mlo_dev_ctx && wlan_vdev_mlme_is_mlo_vdev(vdev)) { 1234 mlo_dev_lock_acquire(mlo_dev_ctx); 1235 if (sta_ctx && sta_ctx->connect_req && 1236 source != CM_INTERNAL_DISCONNECT) { 1237 mlo_free_connect_ies(sta_ctx->connect_req); 1238 qdf_mem_free(sta_ctx->connect_req); 1239 sta_ctx->connect_req = NULL; 1240 } 1241 1242 if (validate_req) { 1243 status = mlo_validate_disconn_req(vdev, source, 1244 reason_code, bssid); 1245 if (QDF_IS_STATUS_ERROR(status)) { 1246 mlo_err("Connect in progress, deferring disconnect"); 1247 mlo_dev_lock_release(mlo_dev_ctx); 1248 return status; 1249 } 1250 } 1251 1252 mlo_dev_lock_release(mlo_dev_ctx); 1253 1254 status = mlo_send_link_disconnect(vdev, source, 1255 reason_code, bssid); 1256 if (QDF_IS_STATUS_SUCCESS(status)) 1257 mlo_free_copied_conn_req(sta_ctx); 1258 1259 return status; 1260 } 1261 status = wlan_cm_disconnect(vdev, source, 1262 reason_code, NULL); 1263 if (QDF_IS_STATUS_SUCCESS(status)) 1264 mlo_free_copied_conn_req(sta_ctx); 1265 1266 return status; 1267 } 1268 1269 QDF_STATUS mlo_disconnect(struct wlan_objmgr_vdev *vdev, 1270 enum wlan_cm_source source, 1271 enum wlan_reason_code reason_code, 1272 struct qdf_mac_addr *bssid) 1273 { 1274 return mlo_disconnect_req(vdev, source, reason_code, bssid, true); 1275 } 1276 1277 QDF_STATUS mlo_sync_disconnect(struct wlan_objmgr_vdev *vdev, 1278 enum wlan_cm_source source, 1279 enum wlan_reason_code reason_code, 1280 struct qdf_mac_addr *bssid) 1281 { 1282 struct wlan_mlo_dev_context *mlo_dev_ctx = NULL; 1283 struct wlan_mlo_sta *sta_ctx = NULL; 1284 QDF_STATUS status = QDF_STATUS_SUCCESS; 1285 1286 if (!vdev) 1287 return QDF_STATUS_E_FAILURE; 1288 1289 mlo_dev_ctx = vdev->mlo_dev_ctx; 1290 if (mlo_dev_ctx) 1291 sta_ctx = mlo_dev_ctx->sta_ctx; 1292 if (mlo_dev_ctx && wlan_vdev_mlme_is_mlo_vdev(vdev)) { 1293 if (sta_ctx && sta_ctx->connect_req) { 1294 mlo_free_connect_ies(sta_ctx->connect_req); 1295 qdf_mem_free(sta_ctx->connect_req); 1296 sta_ctx->connect_req = NULL; 1297 } 1298 1299 status = mlo_send_link_disconnect_sync(mlo_dev_ctx, source, 1300 reason_code, bssid); 1301 if (QDF_IS_STATUS_SUCCESS(status)) 1302 mlo_free_copied_conn_req(sta_ctx); 1303 1304 return status; 1305 } 1306 status = wlan_cm_disconnect_sync(vdev, source, 1307 reason_code); 1308 if (QDF_IS_STATUS_SUCCESS(status)) 1309 mlo_free_copied_conn_req(sta_ctx); 1310 1311 return status; 1312 } 1313 1314 /** 1315 * mlo_handle_disconnect_resp- Issue desired actions on partner link vdev 1316 * 1317 * @vdev: pointer to vdev 1318 * @resp: disconnect resp 1319 * 1320 * Return: none 1321 */ 1322 #ifdef WLAN_FEATURE_11BE_MLO_ADV_FEATURE 1323 static 1324 void mlo_handle_disconnect_resp(struct wlan_objmgr_vdev *vdev, 1325 struct wlan_cm_discon_rsp *resp) 1326 { 1327 /* If it is secondary link then delete vdev object from mlo device. */ 1328 enum wlan_cm_source source; 1329 enum wlan_reason_code reason_code; 1330 uint8_t i = 0; 1331 struct wlan_objmgr_vdev *wlan_vdev_list[WLAN_UMAC_MLO_MAX_VDEVS]; 1332 uint16_t vdev_count = 0; 1333 1334 mlo_sta_get_vdev_list(vdev, &vdev_count, wlan_vdev_list); 1335 for (i = 0; i < vdev_count; i++) { 1336 if (wlan_cm_is_vdev_connected(wlan_vdev_list[i])) { 1337 if (wlan_vdev_mlme_is_mlo_link_vdev( 1338 wlan_vdev_list[i])) { 1339 source = resp->req.req.source; 1340 reason_code = resp->req.req.reason_code; 1341 wlan_cm_disconnect( 1342 wlan_vdev_list[i], 1343 source, reason_code, NULL); 1344 } 1345 } 1346 mlo_release_vdev_ref(wlan_vdev_list[i]); 1347 } 1348 } 1349 #else 1350 static 1351 void mlo_handle_disconnect_resp(struct wlan_objmgr_vdev *vdev, 1352 struct wlan_cm_discon_rsp *resp) 1353 { } 1354 1355 static QDF_STATUS ml_activate_connect_req_sched_cb(struct scheduler_msg *msg) 1356 { 1357 struct wlan_objmgr_vdev *vdev = msg->bodyptr; 1358 struct wlan_mlo_dev_context *mlo_dev_ctx = NULL; 1359 struct wlan_mlo_sta *sta_ctx = NULL; 1360 uint8_t i = 0; 1361 struct mlo_partner_info partner_info; 1362 struct mlo_link_info partner_link_info; 1363 struct wlan_objmgr_vdev *tmp_vdev; 1364 1365 if (!vdev) { 1366 mlme_err("Null input vdev"); 1367 return QDF_STATUS_E_INVAL; 1368 } 1369 1370 mlo_dev_ctx = vdev->mlo_dev_ctx; 1371 if (!mlo_dev_ctx) { 1372 wlan_objmgr_vdev_release_ref(vdev, WLAN_MLO_MGR_ID); 1373 return QDF_STATUS_E_INVAL; 1374 } 1375 1376 sta_ctx = mlo_dev_ctx->sta_ctx; 1377 if (!sta_ctx) { 1378 wlan_objmgr_vdev_release_ref(vdev, WLAN_MLO_MGR_ID); 1379 return QDF_STATUS_E_INVAL; 1380 } 1381 1382 if (sta_ctx->connect_req->ml_parnter_info.num_partner_links) { 1383 partner_info = sta_ctx->connect_req->ml_parnter_info; 1384 wlan_vdev_mlme_set_mlo_vdev(vdev); 1385 wlan_vdev_mlme_clear_mlo_link_vdev(vdev); 1386 mlo_clear_connect_req_links_bmap(vdev); 1387 mlo_update_connect_req_links(vdev, 1); 1388 for (i = 0; i < partner_info.num_partner_links; i++) { 1389 partner_link_info = partner_info.partner_link_info[i]; 1390 tmp_vdev = mlo_get_ml_vdev_by_mac( 1391 vdev, 1392 &partner_link_info.link_addr); 1393 if (tmp_vdev) { 1394 mlo_update_connect_req_links(tmp_vdev, 1); 1395 wlan_vdev_mlme_set_mlo_vdev(tmp_vdev); 1396 wlan_vdev_mlme_set_mlo_link_vdev(tmp_vdev); 1397 wlan_vdev_set_link_id( 1398 tmp_vdev, 1399 partner_link_info.link_id); 1400 } 1401 } 1402 } 1403 1404 mlo_connect(vdev, sta_ctx->connect_req); 1405 mlo_free_connect_ies(sta_ctx->connect_req); 1406 qdf_mem_free(sta_ctx->connect_req); 1407 sta_ctx->connect_req = NULL; 1408 1409 wlan_objmgr_vdev_release_ref(vdev, WLAN_MLO_MGR_ID); 1410 return QDF_STATUS_SUCCESS; 1411 } 1412 1413 static QDF_STATUS ml_activate_connect_req_flush_cb(struct scheduler_msg *msg) 1414 { 1415 struct wlan_objmgr_vdev *vdev = msg->bodyptr; 1416 1417 if (!vdev) { 1418 mlme_err("Null input vdev"); 1419 return QDF_STATUS_E_INVAL; 1420 } 1421 1422 wlan_objmgr_vdev_release_ref(vdev, WLAN_MLO_MGR_ID); 1423 return QDF_STATUS_SUCCESS; 1424 } 1425 #endif 1426 1427 #ifdef WLAN_FEATURE_11BE_MLO_ADV_FEATURE 1428 static inline 1429 void mlo_sta_link_handle_pending_connect(struct wlan_objmgr_vdev *vdev) 1430 { } 1431 #else 1432 static inline 1433 void mlo_sta_link_handle_pending_connect(struct wlan_objmgr_vdev *vdev) 1434 { 1435 struct wlan_mlo_dev_context *mlo_dev_ctx = vdev->mlo_dev_ctx; 1436 struct scheduler_msg msg = {0}; 1437 QDF_STATUS ret; 1438 struct wlan_mlo_sta *sta_ctx = NULL; 1439 1440 if (!mlo_dev_ctx) { 1441 mlo_err("ML dev ctx is null"); 1442 return; 1443 } 1444 sta_ctx = mlo_dev_ctx->sta_ctx; 1445 ret = wlan_objmgr_vdev_try_get_ref( 1446 vdev, 1447 WLAN_MLO_MGR_ID); 1448 if (QDF_IS_STATUS_ERROR(ret)) { 1449 mlo_free_connect_ies(sta_ctx->connect_req); 1450 qdf_mem_free(sta_ctx->connect_req); 1451 sta_ctx->connect_req = NULL; 1452 return; 1453 } 1454 msg.bodyptr = vdev; 1455 msg.callback = ml_activate_connect_req_sched_cb; 1456 msg.flush_callback = ml_activate_connect_req_flush_cb; 1457 1458 ret = scheduler_post_message(QDF_MODULE_ID_MLME, 1459 QDF_MODULE_ID_MLME, 1460 QDF_MODULE_ID_MLME, &msg); 1461 if (QDF_IS_STATUS_ERROR(ret)) { 1462 mlo_free_connect_ies(sta_ctx->connect_req); 1463 qdf_mem_free(sta_ctx->connect_req); 1464 sta_ctx->connect_req = NULL; 1465 wlan_objmgr_vdev_release_ref(vdev, 1466 WLAN_MLO_MGR_ID); 1467 return; 1468 } 1469 } 1470 #endif 1471 1472 void mlo_sta_link_disconn_notify(struct wlan_objmgr_vdev *vdev, 1473 struct wlan_cm_discon_rsp *resp) 1474 { 1475 struct wlan_mlo_dev_context *mlo_dev_ctx = vdev->mlo_dev_ctx; 1476 struct wlan_mlo_sta *sta_ctx = NULL; 1477 struct wlan_objmgr_vdev *assoc_vdev = NULL; 1478 1479 if (!mlo_dev_ctx || !(wlan_vdev_mlme_is_mlo_vdev(vdev))) 1480 return; 1481 1482 sta_ctx = mlo_dev_ctx->sta_ctx; 1483 if (!sta_ctx) 1484 return; 1485 1486 if (!wlan_cm_is_vdev_disconnected(vdev)) 1487 return; 1488 1489 mlo_update_connected_links(vdev, 0); 1490 if (mlo_is_mld_disconnected(vdev)) { 1491 if (sta_ctx->connect_req) { 1492 assoc_vdev = mlo_get_assoc_link_vdev(mlo_dev_ctx); 1493 if (!assoc_vdev) 1494 return; 1495 mlo_sta_link_handle_pending_connect(assoc_vdev); 1496 } 1497 } 1498 1499 mlo_handle_disconnect_resp(vdev, resp); 1500 } 1501 1502 bool mlo_is_mld_sta(struct wlan_objmgr_vdev *vdev) 1503 { 1504 if ((wlan_vdev_mlme_get_opmode(vdev) == QDF_STA_MODE) && 1505 wlan_vdev_mlme_is_mlo_vdev(vdev)) 1506 return true; 1507 1508 return false; 1509 } 1510 1511 #ifndef WLAN_FEATURE_11BE_MLO_ADV_FEATURE 1512 struct wlan_objmgr_vdev * 1513 mlo_get_ml_vdev_by_mac(struct wlan_objmgr_vdev *vdev, 1514 struct qdf_mac_addr *macaddr) 1515 { 1516 struct wlan_mlo_dev_context *mlo_dev_ctx = vdev->mlo_dev_ctx; 1517 uint8_t i = 0; 1518 1519 if (!mlo_dev_ctx) 1520 return NULL; 1521 1522 for (i = 0; i < WLAN_UMAC_MLO_MAX_VDEVS; i++) { 1523 if (!mlo_dev_ctx->wlan_vdev_list[i]) 1524 continue; 1525 1526 if(qdf_mem_cmp(macaddr, 1527 wlan_vdev_mlme_get_macaddr(mlo_dev_ctx->wlan_vdev_list[i]), 1528 QDF_MAC_ADDR_SIZE) == 0) { 1529 return mlo_dev_ctx->wlan_vdev_list[i]; 1530 } 1531 } 1532 return NULL; 1533 } 1534 #endif 1535 1536 qdf_freq_t 1537 mlo_get_chan_freq_by_bssid(struct wlan_objmgr_pdev *pdev, 1538 struct qdf_mac_addr *bssid) 1539 { 1540 struct scan_filter *scan_filter; 1541 int8_t ch_freq = 0; 1542 qdf_list_t *list = NULL; 1543 struct scan_cache_node *first_node = NULL; 1544 qdf_list_node_t *cur_node = NULL; 1545 1546 scan_filter = qdf_mem_malloc(sizeof(*scan_filter)); 1547 if (!scan_filter) 1548 return ch_freq; 1549 1550 scan_filter->num_of_bssid = 1; 1551 qdf_mem_copy(scan_filter->bssid_list[0].bytes, 1552 bssid, sizeof(struct qdf_mac_addr)); 1553 list = wlan_scan_get_result(pdev, scan_filter); 1554 qdf_mem_free(scan_filter); 1555 1556 if (!list || (list && !qdf_list_size(list))) { 1557 mlo_debug("scan list empty"); 1558 goto error; 1559 } 1560 1561 qdf_list_peek_front(list, &cur_node); 1562 first_node = qdf_container_of(cur_node, 1563 struct scan_cache_node, 1564 node); 1565 if (first_node && first_node->entry) 1566 ch_freq = first_node->entry->channel.chan_freq; 1567 error: 1568 if (list) 1569 wlan_scan_purge_results(list); 1570 1571 return ch_freq; 1572 } 1573 1574 void mlo_get_assoc_rsp(struct wlan_objmgr_vdev *vdev, 1575 struct element_info *assoc_rsp_frame) 1576 { 1577 struct wlan_mlo_dev_context *mlo_dev_ctx = vdev->mlo_dev_ctx; 1578 struct wlan_mlo_sta *sta_ctx = NULL; 1579 1580 if (!mlo_dev_ctx || !mlo_dev_ctx->sta_ctx) 1581 return; 1582 1583 sta_ctx = mlo_dev_ctx->sta_ctx; 1584 1585 if (!sta_ctx->assoc_rsp.len || !sta_ctx->assoc_rsp.ptr) { 1586 mlo_err("Assoc Resp info is empty"); 1587 return; 1588 } 1589 1590 *assoc_rsp_frame = sta_ctx->assoc_rsp; 1591 } 1592 1593 QDF_STATUS mlo_sta_save_quiet_status(struct wlan_mlo_dev_context *mlo_dev_ctx, 1594 uint8_t link_id, 1595 bool quiet_status) 1596 { 1597 struct wlan_mlo_sta *sta_ctx; 1598 int i; 1599 bool find_free_buffer = false; 1600 int free_idx; 1601 1602 if (!mlo_dev_ctx) { 1603 mlo_err("invalid mlo_dev_ctx"); 1604 return QDF_STATUS_E_INVAL; 1605 } 1606 1607 mlo_dev_lock_acquire(mlo_dev_ctx); 1608 sta_ctx = mlo_dev_ctx->sta_ctx; 1609 if (!sta_ctx) { 1610 mlo_err("invalid sta_ctx"); 1611 mlo_dev_lock_release(mlo_dev_ctx); 1612 return QDF_STATUS_E_INVAL; 1613 } 1614 for (i = 0; i < QDF_ARRAY_SIZE(sta_ctx->mlo_quiet_status); i++) { 1615 if (!sta_ctx->mlo_quiet_status[i].valid_status) { 1616 if (!find_free_buffer) { 1617 free_idx = i; 1618 find_free_buffer = true; 1619 } 1620 } else if (link_id == sta_ctx->mlo_quiet_status[i].link_id) { 1621 sta_ctx->mlo_quiet_status[i].quiet_status = 1622 quiet_status; 1623 mlo_debug("mld mac " QDF_MAC_ADDR_FMT " link id %d quiet status update %d", 1624 QDF_MAC_ADDR_REF(mlo_dev_ctx->mld_addr.bytes), 1625 link_id, quiet_status); 1626 mlo_dev_lock_release(mlo_dev_ctx); 1627 return QDF_STATUS_SUCCESS; 1628 } 1629 } 1630 if (!find_free_buffer) { 1631 mlo_err("no free buffer for link id %d to save quiet_status", 1632 link_id); 1633 mlo_dev_lock_release(mlo_dev_ctx); 1634 return QDF_STATUS_E_INVAL; 1635 } 1636 sta_ctx->mlo_quiet_status[free_idx].quiet_status = quiet_status; 1637 sta_ctx->mlo_quiet_status[free_idx].link_id = link_id; 1638 sta_ctx->mlo_quiet_status[free_idx].valid_status = true; 1639 1640 mlo_debug("mld mac " QDF_MAC_ADDR_FMT " link id %d in quiet status %d", 1641 QDF_MAC_ADDR_REF(mlo_dev_ctx->mld_addr.bytes), 1642 link_id, quiet_status); 1643 mlo_dev_lock_release(mlo_dev_ctx); 1644 1645 return QDF_STATUS_SUCCESS; 1646 } 1647 1648 bool mlo_is_sta_in_quiet_status(struct wlan_mlo_dev_context *mlo_dev_ctx, 1649 uint8_t link_id) 1650 { 1651 struct wlan_mlo_sta *sta_ctx; 1652 int i; 1653 bool quiet_status = false; 1654 1655 if (!mlo_dev_ctx) { 1656 mlo_err("invalid mlo_dev_ctx"); 1657 return quiet_status; 1658 } 1659 1660 mlo_dev_lock_acquire(mlo_dev_ctx); 1661 sta_ctx = mlo_dev_ctx->sta_ctx; 1662 if (!sta_ctx) { 1663 mlo_err("invalid sta_ctx"); 1664 mlo_dev_lock_release(mlo_dev_ctx); 1665 return quiet_status; 1666 } 1667 for (i = 0; i < QDF_ARRAY_SIZE(sta_ctx->mlo_quiet_status); i++) { 1668 if (sta_ctx->mlo_quiet_status[i].valid_status && 1669 link_id == sta_ctx->mlo_quiet_status[i].link_id) { 1670 quiet_status = 1671 sta_ctx->mlo_quiet_status[i].quiet_status; 1672 break; 1673 } 1674 } 1675 mlo_dev_lock_release(mlo_dev_ctx); 1676 1677 return quiet_status; 1678 } 1679 1680 bool mlo_is_sta_inactivity_allowed_with_quiet(struct wlan_objmgr_psoc *psoc, 1681 uint8_t *vdev_id_list, 1682 uint8_t num_mlo, uint8_t *mlo_idx, 1683 uint8_t affected_links, 1684 uint8_t *affected_list) 1685 { 1686 uint8_t i, j; 1687 struct wlan_objmgr_vdev *vdev; 1688 bool allowed = false; 1689 1690 for (i = 0; i < num_mlo; i++) { 1691 for (j = 0; j < affected_links; j++) { 1692 if (vdev_id_list[mlo_idx[i]] == affected_list[j]) 1693 break; 1694 } 1695 if (j != affected_links) 1696 continue; 1697 /* find vdev not in affected_list */ 1698 vdev = wlan_objmgr_get_vdev_by_id_from_psoc( 1699 psoc, vdev_id_list[mlo_idx[i]], 1700 WLAN_IF_MGR_ID); 1701 if (!vdev) { 1702 mlo_err("invalid vdev for id %d", 1703 vdev_id_list[mlo_idx[i]]); 1704 continue; 1705 } 1706 1707 /* for not affected vdev, check the vdev is in quiet or not*/ 1708 allowed = !mlo_is_sta_in_quiet_status( 1709 vdev->mlo_dev_ctx, wlan_vdev_get_link_id(vdev)); 1710 wlan_objmgr_vdev_release_ref(vdev, WLAN_IF_MGR_ID); 1711 if (allowed) { 1712 mlo_debug("vdev id %d link id %d is not in quiet, allow partner link to trigger inactivity", 1713 wlan_vdev_get_id(vdev), 1714 wlan_vdev_get_link_id(vdev)); 1715 break; 1716 } 1717 } 1718 1719 return allowed; 1720 } 1721 1722 bool mlo_is_sta_csa_synced(struct wlan_mlo_dev_context *mlo_dev_ctx, 1723 uint8_t link_id) 1724 { 1725 struct wlan_mlo_sta *sta_ctx; 1726 int i; 1727 bool sta_csa_synced = false; 1728 1729 if (!mlo_dev_ctx) { 1730 mlo_err("invalid mlo_dev_ctx"); 1731 return sta_csa_synced; 1732 } 1733 1734 mlo_dev_lock_acquire(mlo_dev_ctx); 1735 sta_ctx = mlo_dev_ctx->sta_ctx; 1736 if (!sta_ctx) { 1737 mlo_err("invalid sta_ctx"); 1738 mlo_dev_lock_release(mlo_dev_ctx); 1739 return sta_csa_synced; 1740 } 1741 for (i = 0; i < QDF_ARRAY_SIZE(sta_ctx->mlo_csa_param); i++) { 1742 if (link_id == sta_ctx->mlo_csa_param[i].link_id && 1743 (sta_ctx->mlo_csa_param[i].valid_csa_param || 1744 sta_ctx->mlo_csa_param[i].mlo_csa_synced)) { 1745 mlo_dev_lock_release(mlo_dev_ctx); 1746 sta_csa_synced = 1747 sta_ctx->mlo_csa_param[i].mlo_csa_synced; 1748 break; 1749 } 1750 } 1751 mlo_dev_lock_release(mlo_dev_ctx); 1752 1753 return sta_csa_synced; 1754 } 1755 1756 QDF_STATUS mlo_sta_csa_save_params(struct wlan_mlo_dev_context *mlo_dev_ctx, 1757 uint8_t link_id, 1758 struct csa_offload_params *csa_param) 1759 { 1760 struct wlan_mlo_sta *sta_ctx; 1761 int i; 1762 bool find_free_buffer = false; 1763 int free_idx; 1764 QDF_STATUS status = QDF_STATUS_SUCCESS; 1765 1766 if (!mlo_dev_ctx) { 1767 mlo_err("invalid mlo_dev_ctx"); 1768 status = QDF_STATUS_E_INVAL; 1769 goto done; 1770 } 1771 1772 mlo_dev_lock_acquire(mlo_dev_ctx); 1773 sta_ctx = mlo_dev_ctx->sta_ctx; 1774 if (!sta_ctx) { 1775 mlo_err("invalid sta_ctx"); 1776 status = QDF_STATUS_E_INVAL; 1777 goto rel_lock; 1778 } 1779 for (i = 0; i < QDF_ARRAY_SIZE(sta_ctx->mlo_csa_param); i++) { 1780 if (!sta_ctx->mlo_csa_param[i].valid_csa_param && 1781 !sta_ctx->mlo_csa_param[i].mlo_csa_synced) { 1782 if (!find_free_buffer) { 1783 free_idx = i; 1784 find_free_buffer = true; 1785 } 1786 } else if (link_id == sta_ctx->mlo_csa_param[i].link_id) { 1787 qdf_mem_copy(&sta_ctx->mlo_csa_param[i].csa_param, 1788 csa_param, sizeof(*csa_param)); 1789 mlo_debug("mld mac " QDF_MAC_ADDR_FMT " link id %d update csa", 1790 QDF_MAC_ADDR_REF(mlo_dev_ctx->mld_addr.bytes), 1791 link_id); 1792 goto rel_lock; 1793 } 1794 } 1795 if (!find_free_buffer) { 1796 mlo_err("no free buffer of csa param for link %d in sta_ctx", 1797 link_id); 1798 status = QDF_STATUS_E_INVAL; 1799 goto rel_lock; 1800 } 1801 qdf_mem_copy(&sta_ctx->mlo_csa_param[free_idx].csa_param, 1802 csa_param, sizeof(*csa_param)); 1803 sta_ctx->mlo_csa_param[free_idx].link_id = link_id; 1804 sta_ctx->mlo_csa_param[free_idx].valid_csa_param = true; 1805 mlo_debug("mld mac " QDF_MAC_ADDR_FMT " link id %d RX csa", 1806 QDF_MAC_ADDR_REF(mlo_dev_ctx->mld_addr.bytes), 1807 link_id); 1808 1809 rel_lock: 1810 mlo_dev_lock_release(mlo_dev_ctx); 1811 1812 done: 1813 1814 return status; 1815 } 1816 1817 QDF_STATUS mlo_sta_up_active_notify(struct wlan_objmgr_vdev *vdev) 1818 { 1819 struct wlan_mlo_sta *sta_ctx; 1820 struct wlan_mlo_dev_context *mlo_dev_ctx; 1821 uint8_t link_id; 1822 int i; 1823 bool find_free_buffer = false; 1824 int free_idx; 1825 struct csa_offload_params csa_param; 1826 struct wlan_channel *chan; 1827 QDF_STATUS status = QDF_STATUS_SUCCESS; 1828 1829 if (!vdev) { 1830 mlo_err("invalid vdev"); 1831 status = QDF_STATUS_E_INVAL; 1832 goto done; 1833 } 1834 link_id = wlan_vdev_get_link_id(vdev); 1835 mlo_dev_ctx = vdev->mlo_dev_ctx; 1836 if (!mlo_dev_ctx) { 1837 mlo_err("invalid mlo_dev_ctx"); 1838 status = QDF_STATUS_E_INVAL; 1839 goto done; 1840 } 1841 mlo_dev_lock_acquire(mlo_dev_ctx); 1842 sta_ctx = mlo_dev_ctx->sta_ctx; 1843 if (!sta_ctx) { 1844 mlo_err("invalid sta_ctx"); 1845 status = QDF_STATUS_E_INVAL; 1846 goto rel_lock; 1847 } 1848 1849 for (i = 0; i < QDF_ARRAY_SIZE(sta_ctx->mlo_csa_param); i++) { 1850 if (!sta_ctx->mlo_csa_param[i].valid_csa_param && 1851 !sta_ctx->mlo_csa_param[i].mlo_csa_synced) { 1852 if (!find_free_buffer) { 1853 free_idx = i; 1854 find_free_buffer = true; 1855 } 1856 } else if (link_id == sta_ctx->mlo_csa_param[i].link_id) { 1857 if (sta_ctx->mlo_csa_param[i].valid_csa_param && 1858 !sta_ctx->mlo_csa_param[i].mlo_csa_synced) { 1859 mlo_debug("mld mac " QDF_MAC_ADDR_FMT " vdev id %d link id %d handle csa", 1860 QDF_MAC_ADDR_REF( 1861 mlo_dev_ctx->mld_addr.bytes), 1862 wlan_vdev_get_id(vdev), link_id); 1863 csa_param = sta_ctx->mlo_csa_param[i].csa_param; 1864 sta_ctx->mlo_csa_param[i].mlo_csa_synced = true; 1865 mlo_dev_lock_release(mlo_dev_ctx); 1866 chan = wlan_vdev_mlme_get_bss_chan(vdev); 1867 if (csa_param.csa_chan_freq && chan && 1868 csa_param.csa_chan_freq != chan->ch_freq) 1869 mlo_mlme_handle_sta_csa_param( 1870 vdev, &csa_param); 1871 goto done; 1872 } 1873 sta_ctx->mlo_csa_param[i].mlo_csa_synced = true; 1874 goto rel_lock; 1875 } 1876 } 1877 if (!find_free_buffer) { 1878 mlo_err("no free buffer of csa param for link %d in sta_ctx", 1879 link_id); 1880 goto rel_lock; 1881 } 1882 sta_ctx->mlo_csa_param[free_idx].mlo_csa_synced = true; 1883 sta_ctx->mlo_csa_param[free_idx].link_id = link_id; 1884 mlo_debug("mld mac " QDF_MAC_ADDR_FMT " link id %d UP Active", 1885 QDF_MAC_ADDR_REF(mlo_dev_ctx->mld_addr.bytes), 1886 link_id); 1887 1888 rel_lock: 1889 mlo_dev_lock_release(mlo_dev_ctx); 1890 1891 done: 1892 1893 return status; 1894 } 1895 1896 bool mlo_is_sta_csa_param_handled(struct wlan_objmgr_vdev *vdev, 1897 struct csa_offload_params *csa_param) 1898 { 1899 struct wlan_mlo_sta *sta_ctx; 1900 struct wlan_mlo_dev_context *mlo_dev_ctx; 1901 uint8_t link_id; 1902 int i; 1903 bool handled = false; 1904 1905 if (!vdev) { 1906 mlo_err("invalid vdev"); 1907 goto done; 1908 } 1909 link_id = wlan_vdev_get_link_id(vdev); 1910 mlo_dev_ctx = vdev->mlo_dev_ctx; 1911 if (!mlo_dev_ctx) { 1912 mlo_err("invalid mlo_dev_ctx"); 1913 goto done; 1914 } 1915 mlo_dev_lock_acquire(mlo_dev_ctx); 1916 sta_ctx = mlo_dev_ctx->sta_ctx; 1917 if (!sta_ctx) { 1918 mlo_err("invalid sta_ctx"); 1919 goto rel_lock; 1920 } 1921 1922 for (i = 0; i < QDF_ARRAY_SIZE(sta_ctx->mlo_csa_param); i++) { 1923 if (link_id == sta_ctx->mlo_csa_param[i].link_id && 1924 (sta_ctx->mlo_csa_param[i].valid_csa_param || 1925 sta_ctx->mlo_csa_param[i].mlo_csa_synced)) 1926 break; 1927 } 1928 1929 if (i >= QDF_ARRAY_SIZE(sta_ctx->mlo_csa_param)) { 1930 mlo_debug("mlo csa synced does not happen before csa FW event"); 1931 goto rel_lock; 1932 } 1933 if (!sta_ctx->mlo_csa_param[i].csa_offload_event_recvd) { 1934 sta_ctx->mlo_csa_param[i].csa_offload_event_recvd = true; 1935 if (sta_ctx->mlo_csa_param[i].valid_csa_param && 1936 !qdf_mem_cmp(&sta_ctx->mlo_csa_param[i].csa_param, 1937 csa_param, sizeof(*csa_param))) 1938 handled = true; 1939 } 1940 1941 rel_lock: 1942 mlo_dev_lock_release(mlo_dev_ctx); 1943 1944 done: 1945 1946 return handled; 1947 } 1948 1949 #ifdef WLAN_FEATURE_11BE_MLO_ADV_FEATURE 1950 void mlo_internal_disconnect_links(struct wlan_objmgr_vdev *vdev) 1951 { 1952 struct wlan_mlo_dev_context *mlo_dev_ctx = NULL; 1953 struct wlan_mlo_sta *sta_ctx = NULL; 1954 uint8_t i; 1955 struct wlan_objmgr_vdev *wlan_vdev_list[WLAN_UMAC_MLO_MAX_VDEVS]; 1956 uint16_t vdev_count = 0; 1957 1958 if (!vdev) 1959 return; 1960 1961 if (!wlan_vdev_mlme_is_assoc_sta_vdev(vdev)) { 1962 mlo_debug("Not an assoc vdev, so ignore disconnect req"); 1963 return; 1964 } 1965 1966 mlo_dev_ctx = vdev->mlo_dev_ctx; 1967 if (mlo_dev_ctx) { 1968 sta_ctx = mlo_dev_ctx->sta_ctx; 1969 } else { 1970 mlo_err("Invalid mlo_dev_ctx"); 1971 return; 1972 } 1973 1974 if (sta_ctx) { 1975 mlo_free_copied_conn_req(sta_ctx); 1976 } else { 1977 mlo_err("Invalid sta_ctx"); 1978 return; 1979 } 1980 1981 if (sta_ctx->connect_req) { 1982 mlo_free_connect_ies(sta_ctx->connect_req); 1983 qdf_mem_free(sta_ctx->connect_req); 1984 sta_ctx->connect_req = NULL; 1985 } 1986 1987 mlo_sta_get_vdev_list(vdev, &vdev_count, wlan_vdev_list); 1988 for (i = 0; i < vdev_count; i++) { 1989 if (wlan_vdev_list[i] != mlo_get_assoc_link_vdev(mlo_dev_ctx) && 1990 (wlan_cm_is_vdev_connected(wlan_vdev_list[i]) || 1991 wlan_cm_is_vdev_connecting(wlan_vdev_list[i]))) 1992 wlan_cm_disconnect(wlan_vdev_list[i], 1993 CM_MLO_LINK_VDEV_DISCONNECT, 1994 REASON_UNSPEC_FAILURE, 1995 NULL); 1996 mlo_release_vdev_ref(wlan_vdev_list[i]); 1997 } 1998 } 1999 #else 2000 void mlo_internal_disconnect_links(struct wlan_objmgr_vdev *vdev) 2001 { 2002 } 2003 #endif /* WLAN_FEATURE_11BE_MLO_ADV_FEATURE */ 2004 2005 void mlo_sta_get_vdev_list(struct wlan_objmgr_vdev *vdev, uint16_t *vdev_count, 2006 struct wlan_objmgr_vdev **wlan_vdev_list) 2007 { 2008 struct wlan_mlo_dev_context *dev_ctx; 2009 int i; 2010 QDF_STATUS status; 2011 2012 *vdev_count = 0; 2013 2014 if (!vdev || !vdev->mlo_dev_ctx) { 2015 mlo_err("Invalid input"); 2016 return; 2017 } 2018 2019 dev_ctx = vdev->mlo_dev_ctx; 2020 2021 mlo_dev_lock_acquire(dev_ctx); 2022 *vdev_count = 0; 2023 for (i = 0; i < QDF_ARRAY_SIZE(dev_ctx->wlan_vdev_list); i++) { 2024 if (dev_ctx->wlan_vdev_list[i]) { 2025 status = 2026 wlan_objmgr_vdev_try_get_ref(dev_ctx->wlan_vdev_list[i], 2027 WLAN_MLO_MGR_ID); 2028 if (QDF_IS_STATUS_ERROR(status)) 2029 break; 2030 wlan_vdev_list[*vdev_count] = 2031 dev_ctx->wlan_vdev_list[i]; 2032 (*vdev_count) += 1; 2033 } 2034 } 2035 mlo_dev_lock_release(dev_ctx); 2036 } 2037 2038 bool mlo_sta_vdev_get_reconfig_timer_state(struct wlan_objmgr_vdev *vdev) 2039 { 2040 struct vdev_mlme_obj *vdev_mlme; 2041 2042 if (!vdev || !mlo_is_mld_sta(vdev)) 2043 return false; 2044 2045 vdev_mlme = wlan_vdev_mlme_get_cmpt_obj(vdev); 2046 if (!vdev_mlme) { 2047 mlo_err("vdev mlme is null"); 2048 return false; 2049 } 2050 2051 return vdev_mlme->ml_reconfig_started; 2052 } 2053 2054 void mlo_sta_stop_reconfig_timer_by_vdev(struct wlan_objmgr_vdev *vdev) 2055 { 2056 struct vdev_mlme_obj *vdev_mlme; 2057 2058 if (!vdev || !mlo_is_mld_sta(vdev)) 2059 return; 2060 2061 vdev_mlme = wlan_vdev_mlme_get_cmpt_obj(vdev); 2062 if (!vdev_mlme) { 2063 mlo_err("vdev mlme is null"); 2064 return; 2065 } 2066 2067 if (!vdev_mlme->ml_reconfig_started) 2068 return; 2069 2070 qdf_timer_stop(&vdev_mlme->ml_reconfig_timer); 2071 2072 mlo_debug("vdev %d reconfig timer active to stop", 2073 wlan_vdev_get_id(vdev)); 2074 vdev_mlme->ml_reconfig_started = false; 2075 } 2076 2077 void mlo_sta_stop_reconfig_timer(struct wlan_objmgr_vdev *vdev) 2078 { 2079 struct wlan_objmgr_vdev *wlan_vdev_list[WLAN_UMAC_MLO_MAX_VDEVS] = {0}; 2080 uint16_t vdev_count = 0; 2081 uint8_t i; 2082 2083 if (!vdev || !mlo_is_mld_sta(vdev)) 2084 return; 2085 2086 mlo_get_ml_vdev_list(vdev, &vdev_count, wlan_vdev_list); 2087 if (!vdev_count) { 2088 mlo_err("vdev num 0 in mld dev"); 2089 return; 2090 } 2091 2092 for (i = 0; i < vdev_count; i++) { 2093 if (!wlan_vdev_list[i]) { 2094 mlo_err("vdev is null in mld"); 2095 goto release_ref; 2096 } 2097 mlo_sta_stop_reconfig_timer_by_vdev(wlan_vdev_list[i]); 2098 } 2099 2100 release_ref: 2101 for (i = 0; i < vdev_count; i++) 2102 mlo_release_vdev_ref(wlan_vdev_list[i]); 2103 } 2104 2105 void mlo_set_keys_saved(struct wlan_objmgr_vdev *vdev, 2106 struct qdf_mac_addr *mac_address, bool value) 2107 { 2108 struct wlan_mlo_sta *sta_ctx; 2109 2110 if (!vdev || !vdev->mlo_dev_ctx) 2111 return; 2112 2113 sta_ctx = vdev->mlo_dev_ctx->sta_ctx; 2114 if (!sta_ctx) 2115 return; 2116 2117 sta_ctx->key_mgmt[0].vdev_id = wlan_vdev_get_id(vdev); 2118 sta_ctx->key_mgmt[0].keys_saved = value; 2119 qdf_copy_macaddr(&sta_ctx->key_mgmt[0].link_mac_address, 2120 mac_address); 2121 } 2122 2123 bool mlo_get_keys_saved(struct wlan_objmgr_vdev *vdev, 2124 uint8_t *mac_address) 2125 { 2126 struct wlan_mlo_sta *sta_ctx; 2127 2128 if (!vdev || !vdev->mlo_dev_ctx) 2129 return false; 2130 2131 sta_ctx = vdev->mlo_dev_ctx->sta_ctx; 2132 if (!sta_ctx) 2133 return false; 2134 2135 if ((qdf_is_macaddr_equal(&sta_ctx->key_mgmt[0].link_mac_address, 2136 (struct qdf_mac_addr *)mac_address)) && 2137 (wlan_vdev_get_id(vdev) == sta_ctx->key_mgmt[0].vdev_id)) 2138 return sta_ctx->key_mgmt[0].keys_saved; 2139 2140 return false; 2141 } 2142 2143 static uint16_t 2144 mlo_get_bcn_interval_by_bssid(struct wlan_objmgr_pdev *pdev, 2145 uint8_t *bssid) 2146 { 2147 struct scan_filter *scan_filter; 2148 uint16_t bcn_int = 0; 2149 qdf_list_t *list = NULL; 2150 struct scan_cache_node *first_node = NULL; 2151 qdf_list_node_t *cur_node = NULL; 2152 2153 scan_filter = qdf_mem_malloc(sizeof(*scan_filter)); 2154 if (!scan_filter) 2155 return bcn_int; 2156 2157 scan_filter->num_of_bssid = 1; 2158 qdf_mem_copy(scan_filter->bssid_list[0].bytes, 2159 bssid, sizeof(struct qdf_mac_addr)); 2160 list = wlan_scan_get_result(pdev, scan_filter); 2161 qdf_mem_free(scan_filter); 2162 2163 if (!list || (list && !qdf_list_size(list))) { 2164 mlo_debug("scan list empty"); 2165 goto error; 2166 } 2167 2168 qdf_list_peek_front(list, &cur_node); 2169 first_node = qdf_container_of(cur_node, 2170 struct scan_cache_node, 2171 node); 2172 if (first_node && first_node->entry) 2173 bcn_int = first_node->entry->bcn_int; 2174 error: 2175 if (list) 2176 wlan_scan_purge_results(list); 2177 2178 return bcn_int; 2179 } 2180 2181 static void mlo_process_link_remove(struct wlan_objmgr_vdev *vdev, 2182 struct ml_rv_partner_link_info *link_info) 2183 { 2184 struct vdev_mlme_obj *vdev_mlme = NULL; 2185 struct wlan_objmgr_peer *bss_peer = NULL; 2186 uint16_t bcn_int = 0; 2187 uint16_t tbtt_count = 0; 2188 2189 vdev_mlme = wlan_vdev_mlme_get_cmpt_obj(vdev); 2190 if (!vdev_mlme) 2191 return; 2192 2193 if (vdev_mlme->ml_reconfig_started == true) 2194 return; 2195 2196 bss_peer = wlan_vdev_get_bsspeer(vdev); 2197 if (!bss_peer) 2198 return; 2199 2200 /* Link delete triggered from AP, 2201 * start timer with tbtt count * beacon interval 2202 */ 2203 tbtt_count = link_info->delete_timer; 2204 bcn_int = mlo_get_bcn_interval_by_bssid( 2205 wlan_vdev_get_pdev(vdev), 2206 wlan_peer_get_macaddr(bss_peer)); 2207 if (!bcn_int) 2208 return; 2209 2210 vdev_mlme->ml_reconfig_started = true; 2211 qdf_timer_mod(&vdev_mlme->ml_reconfig_timer, 2212 qdf_time_uint_to_ms(tbtt_count * bcn_int)); 2213 } 2214 2215 #ifdef WLAN_FEATURE_11BE_MLO_ADV_FEATURE 2216 static inline 2217 QDF_STATUS mlo_process_link_add(struct wlan_objmgr_psoc *psoc, 2218 struct wlan_objmgr_vdev *vdev, 2219 struct mlo_partner_info *cache_partner_info, 2220 struct mlo_partner_info *partner_info, 2221 uint16_t vdev_count) 2222 { 2223 return QDF_STATUS_E_INVAL; 2224 } 2225 #else 2226 static 2227 QDF_STATUS mlo_process_link_add(struct wlan_objmgr_psoc *psoc, 2228 struct wlan_objmgr_vdev *vdev, 2229 struct mlo_partner_info *cache_partner_info, 2230 struct mlo_partner_info *partner_info, 2231 uint16_t vdev_count) 2232 { 2233 struct wlan_mlo_dev_context *ml_ctx = vdev->mlo_dev_ctx; 2234 2235 /* Check if ini to support dynamic link add is enable 2236 * or not 2237 */ 2238 if (!mlme_mlo_is_reconfig_reassoc_enable(psoc)) { 2239 mlo_debug("ML Reconfig link add support disabled"); 2240 return QDF_STATUS_E_INVAL; 2241 } 2242 2243 if (vdev_count == ml_ctx->wlan_vdev_count) { 2244 /* All links are participating in current ML connection */ 2245 return QDF_STATUS_E_INVAL; 2246 } 2247 2248 /* check if any new link in scan entry */ 2249 if (partner_info->num_partner_links == 2250 cache_partner_info->num_partner_links) { 2251 if (!qdf_mem_cmp(cache_partner_info, partner_info, 2252 sizeof(struct mlo_partner_info))) { 2253 mlo_debug("No new link found"); 2254 return QDF_STATUS_E_INVAL; 2255 } 2256 } 2257 2258 /* mlo connected on all links already */ 2259 if (partner_info->num_partner_links <= (vdev_count - 1)) 2260 return QDF_STATUS_E_INVAL; 2261 2262 /* Partner info changed compared to the cached scan entry. 2263 * Process link add 2264 */ 2265 mlo_err("Link addition detected, issue disconnect"); 2266 mlo_disconnect(vdev, CM_SB_DISCONNECT, 2267 REASON_UNSPEC_FAILURE, NULL); 2268 return QDF_STATUS_SUCCESS; 2269 } 2270 #endif 2271 2272 void mlo_process_ml_reconfig_ie(struct wlan_objmgr_vdev *vdev, 2273 struct scan_cache_entry *scan_entry, 2274 uint8_t *ml_ie, qdf_size_t ml_ie_len, 2275 struct mlo_partner_info *partner_info) 2276 { 2277 struct wlan_objmgr_psoc *psoc = NULL; 2278 struct wlan_objmgr_pdev *pdev = NULL; 2279 struct wlan_objmgr_vdev *co_mld_vdev = NULL; 2280 struct wlan_objmgr_vdev *wlan_vdev_list[WLAN_UMAC_MLO_MAX_VDEVS] = {NULL}; 2281 uint16_t vdev_count = 0; 2282 uint8_t idx = 0; 2283 uint8_t i = 0; 2284 uint8_t link_ix = 0; 2285 struct ml_rv_info reconfig_info = {0}; 2286 struct mlo_partner_info ml_partner_info = {0}; 2287 uint8_t *ml_rv_ie = NULL; 2288 qdf_size_t ml_rv_ie_len = 0; 2289 QDF_STATUS status = QDF_STATUS_SUCCESS; 2290 2291 if (!vdev || !mlo_is_mld_sta(vdev)) 2292 return; 2293 2294 pdev = wlan_vdev_get_pdev(vdev); 2295 if (!pdev) 2296 return; 2297 2298 psoc = wlan_pdev_get_psoc(pdev); 2299 if (!psoc) 2300 return; 2301 2302 mlo_get_ml_vdev_list(vdev, &vdev_count, wlan_vdev_list); 2303 if (!vdev_count) { 2304 mlo_debug("Number of VDEVs under MLD is reported as 0"); 2305 return; 2306 } 2307 2308 if (!scan_entry || 2309 QDF_IS_STATUS_ERROR(util_scan_get_ml_partner_info(scan_entry, 2310 &ml_partner_info))) { 2311 mlo_debug("Unable to fetch the partner info in scan db"); 2312 goto check_ml_rv; 2313 } 2314 2315 /* Processing for link add */ 2316 if (QDF_IS_STATUS_SUCCESS(mlo_process_link_add(psoc, vdev, 2317 partner_info, 2318 &ml_partner_info, 2319 vdev_count))) { 2320 mlo_err("Issued MLD disconnect for link add"); 2321 goto err_release_refs; 2322 } 2323 2324 check_ml_rv: 2325 /* Processing for ML Reconfig IE */ 2326 if (vdev_count == 1) { 2327 /* Single link MLO, no need to process link delete */ 2328 goto err_release_refs; 2329 } 2330 2331 status = util_find_mlie_by_variant(ml_ie, 2332 ml_ie_len, 2333 &ml_rv_ie, 2334 &ml_rv_ie_len, 2335 WLAN_ML_VARIANT_RECONFIG); 2336 if (QDF_IS_STATUS_ERROR(status) || !ml_rv_ie) { 2337 mlo_debug("ML IE for reconfig variant not found"); 2338 goto err_release_refs; 2339 } 2340 2341 status = util_get_rvmlie_persta_link_info(ml_rv_ie, ml_rv_ie_len, 2342 &reconfig_info); 2343 if (QDF_IS_STATUS_ERROR(status)) { 2344 mlo_err("Unable to get persta link info from ML RV IE"); 2345 goto err_release_refs; 2346 } 2347 2348 if (!reconfig_info.num_links) { 2349 mlo_err("No. of links is 0 in ML reconfig IE"); 2350 goto err_release_refs; 2351 } 2352 2353 for (idx = 0; idx < vdev_count; idx++) { 2354 co_mld_vdev = wlan_vdev_list[idx]; 2355 if (!co_mld_vdev) { 2356 mlo_debug("VDEV in MLD VDEV list is NULL"); 2357 goto err_release_refs; 2358 } 2359 2360 link_ix = wlan_vdev_get_link_id(co_mld_vdev); 2361 for (i = 0; i < reconfig_info.num_links; i++) { 2362 if (link_ix == reconfig_info.link_info[i].link_id) 2363 mlo_process_link_remove(co_mld_vdev, 2364 &reconfig_info.link_info[i]); 2365 } 2366 } 2367 2368 err_release_refs: 2369 2370 for (i = 0; i < vdev_count; i++) 2371 mlo_release_vdev_ref(wlan_vdev_list[i]); 2372 } 2373 #endif 2374