1 /* 2 * Copyright (c) 2021, The Linux Foundation. All rights reserved. 3 * 4 * Permission to use, copy, modify, and/or distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17 #include "wlan_mlo_mgr_main.h" 18 #include "qdf_types.h" 19 #include "wlan_cmn.h" 20 #include "wlan_mlo_mgr_msgq.h" 21 #include "wlan_objmgr_peer_obj.h" 22 #include "wlan_mlo_mgr_peer.h" 23 #include "wlan_mlo_mgr_ap.h" 24 25 static void mlo_partner_peer_create_post(struct wlan_mlo_dev_context *ml_dev, 26 struct wlan_objmgr_vdev *vdev_link, 27 struct wlan_mlo_peer_context *ml_peer, 28 qdf_nbuf_t frm_buf, 29 struct mlo_partner_info *ml_info) 30 { 31 struct peer_create_notif_s peer_create; 32 QDF_STATUS status; 33 uint8_t i; 34 uint8_t link_id; 35 36 if (wlan_objmgr_vdev_try_get_ref(vdev_link, WLAN_MLO_MGR_ID) == 37 QDF_STATUS_SUCCESS) { 38 peer_create.vdev_link = vdev_link; 39 } else { 40 mlo_err("VDEV is not in created state"); 41 return; 42 } 43 44 wlan_mlo_peer_get_ref(ml_peer); 45 peer_create.ml_peer = ml_peer; 46 link_id = wlan_vdev_get_link_id(vdev_link); 47 for (i = 0; i < ml_info->num_partner_links; i++) { 48 if (link_id != ml_info->partner_link_info[i].link_id) 49 continue; 50 51 qdf_copy_macaddr(&peer_create.addr, 52 &ml_info->partner_link_info[i].link_addr); 53 break; 54 } 55 56 peer_create.frm_buf = qdf_nbuf_clone(frm_buf); 57 if (!peer_create.frm_buf) { 58 wlan_mlo_peer_release_ref(ml_peer); 59 wlan_objmgr_vdev_release_ref(vdev_link, WLAN_MLO_MGR_ID); 60 mlo_err("nbuf clone is failed"); 61 return; 62 } 63 64 status = mlo_msgq_post(MLO_PEER_CREATE, ml_dev, &peer_create); 65 if (status != QDF_STATUS_SUCCESS) { 66 qdf_nbuf_free(frm_buf); 67 wlan_mlo_peer_release_ref(ml_peer); 68 wlan_objmgr_vdev_release_ref(vdev_link, WLAN_MLO_MGR_ID); 69 } 70 } 71 72 static void mlo_link_peer_assoc_notify(struct wlan_mlo_dev_context *ml_dev, 73 struct wlan_objmgr_peer *peer) 74 { 75 struct peer_assoc_notify_s peer_assoc; 76 QDF_STATUS status; 77 78 peer_assoc.peer = peer; 79 status = mlo_msgq_post(MLO_PEER_ASSOC, ml_dev, &peer_assoc); 80 if (status != QDF_STATUS_SUCCESS) 81 wlan_objmgr_peer_release_ref(peer, WLAN_MLO_MGR_ID); 82 } 83 84 static void mlo_link_peer_send_assoc_fail(struct wlan_mlo_dev_context *ml_dev, 85 struct wlan_objmgr_peer *peer) 86 { 87 struct peer_assoc_fail_notify_s peer_assoc_fail; 88 QDF_STATUS status; 89 90 peer_assoc_fail.peer = peer; 91 status = mlo_msgq_post(MLO_PEER_ASSOC_FAIL, ml_dev, &peer_assoc_fail); 92 if (status != QDF_STATUS_SUCCESS) 93 wlan_objmgr_peer_release_ref(peer, WLAN_MLO_MGR_ID); 94 } 95 96 static void mlo_link_peer_disconnect_notify(struct wlan_mlo_dev_context *ml_dev, 97 struct wlan_objmgr_peer *peer) 98 { 99 struct peer_discon_notify_s peer_disconn; 100 QDF_STATUS status; 101 102 peer_disconn.peer = peer; 103 status = mlo_msgq_post(MLO_PEER_DISCONNECT, ml_dev, &peer_disconn); 104 if (status != QDF_STATUS_SUCCESS) 105 wlan_objmgr_peer_release_ref(peer, WLAN_MLO_MGR_ID); 106 } 107 108 QDF_STATUS 109 wlan_mlo_peer_is_disconnect_progress(struct wlan_mlo_peer_context *ml_peer) 110 { 111 QDF_STATUS status; 112 113 mlo_peer_lock_acquire(ml_peer); 114 115 if (ml_peer->mlpeer_state == ML_PEER_DISCONN_INITIATED) 116 status = QDF_STATUS_SUCCESS; 117 else 118 status = QDF_STATUS_E_FAILURE; 119 120 mlo_peer_lock_release(ml_peer); 121 122 return status; 123 } 124 125 QDF_STATUS wlan_mlo_peer_is_assoc_done(struct wlan_mlo_peer_context *ml_peer) 126 { 127 QDF_STATUS status; 128 129 mlo_peer_lock_acquire(ml_peer); 130 131 if (ml_peer->mlpeer_state == ML_PEER_ASSOC_DONE) 132 status = QDF_STATUS_SUCCESS; 133 else 134 status = QDF_STATUS_E_FAILURE; 135 136 mlo_peer_lock_release(ml_peer); 137 138 return status; 139 } 140 141 struct wlan_objmgr_peer *wlan_mlo_peer_get_assoc_peer( 142 struct wlan_mlo_peer_context *ml_peer) 143 { 144 struct wlan_mlo_link_peer_entry *peer_entry; 145 struct wlan_objmgr_peer *assoc_peer = NULL; 146 147 mlo_peer_lock_acquire(ml_peer); 148 149 peer_entry = &ml_peer->peer_list[0]; 150 151 if (peer_entry->link_peer) 152 assoc_peer = peer_entry->link_peer; 153 154 mlo_peer_lock_release(ml_peer); 155 156 return assoc_peer; 157 } 158 159 void wlan_mlo_partner_peer_assoc_post(struct wlan_objmgr_peer *assoc_peer) 160 { 161 struct wlan_mlo_dev_context *ml_dev; 162 struct wlan_mlo_peer_context *ml_peer; 163 struct wlan_objmgr_peer *link_peer; 164 struct wlan_objmgr_peer *link_peers[MAX_MLO_LINK_PEERS]; 165 struct wlan_mlo_link_peer_entry *peer_entry; 166 uint16_t i; 167 168 ml_peer = assoc_peer->mlo_peer_ctx; 169 mlo_peer_lock_acquire(ml_peer); 170 171 if (ml_peer->mlpeer_state != ML_PEER_CREATED) { 172 mlo_peer_lock_release(ml_peer); 173 return; 174 } 175 176 ml_peer->mlpeer_state = ML_PEER_ASSOC_DONE; 177 ml_dev = ml_peer->ml_dev; 178 179 for (i = 0; i < MAX_MLO_LINK_PEERS; i++) { 180 link_peers[i] = NULL; 181 peer_entry = &ml_peer->peer_list[i]; 182 183 if (!peer_entry->link_peer) 184 continue; 185 186 if (peer_entry->link_peer == assoc_peer) 187 continue; 188 189 link_peer = peer_entry->link_peer; 190 191 if (wlan_objmgr_peer_try_get_ref(link_peer, WLAN_MLO_MGR_ID) != 192 QDF_STATUS_SUCCESS) 193 continue; 194 195 link_peers[i] = link_peer; 196 } 197 mlo_peer_lock_release(ml_peer); 198 199 for (i = 0; i < MAX_MLO_LINK_PEERS; i++) { 200 if (!link_peers[i]) 201 continue; 202 203 /* Prepare and queue message */ 204 mlo_link_peer_assoc_notify(ml_dev, link_peers[i]); 205 } 206 } 207 208 void 209 wlan_mlo_partner_peer_create_failed_notify( 210 struct wlan_mlo_peer_context *ml_peer) 211 { 212 struct wlan_mlo_dev_context *ml_dev; 213 struct wlan_objmgr_peer *link_peer; 214 struct wlan_objmgr_peer *link_peers[MAX_MLO_LINK_PEERS]; 215 struct wlan_mlo_link_peer_entry *peer_entry; 216 uint16_t i; 217 218 mlo_peer_lock_acquire(ml_peer); 219 220 if (ml_peer->mlpeer_state == ML_PEER_DISCONN_INITIATED) { 221 mlo_peer_lock_release(ml_peer); 222 return; 223 } 224 225 ml_peer->mlpeer_state = ML_PEER_DISCONN_INITIATED; 226 ml_dev = ml_peer->ml_dev; 227 228 for (i = 0; i < MAX_MLO_LINK_PEERS; i++) { 229 link_peers[i] = NULL; 230 peer_entry = &ml_peer->peer_list[i]; 231 if (!peer_entry->link_peer) 232 continue; 233 234 link_peer = peer_entry->link_peer; 235 if (wlan_objmgr_peer_try_get_ref(link_peer, WLAN_MLO_MGR_ID) != 236 QDF_STATUS_SUCCESS) 237 continue; 238 239 link_peers[i] = link_peer; 240 } 241 mlo_peer_lock_release(ml_peer); 242 243 for (i = 0; i < MAX_MLO_LINK_PEERS; i++) { 244 if (!link_peers[i]) 245 continue; 246 247 /* Prepare and queue message */ 248 if (i == 0) 249 mlo_link_peer_send_assoc_fail(ml_dev, link_peers[i]); 250 else 251 mlo_link_peer_disconnect_notify(ml_dev, link_peers[i]); 252 } 253 } 254 255 void wlan_mlo_partner_peer_disconnect_notify(struct wlan_objmgr_peer *src_peer) 256 { 257 struct wlan_mlo_dev_context *ml_dev; 258 struct wlan_mlo_peer_context *ml_peer; 259 struct wlan_objmgr_peer *link_peer; 260 struct wlan_objmgr_peer *link_peers[MAX_MLO_LINK_PEERS]; 261 struct wlan_mlo_link_peer_entry *peer_entry; 262 uint16_t i; 263 264 ml_peer = src_peer->mlo_peer_ctx; 265 mlo_peer_lock_acquire(ml_peer); 266 267 if (ml_peer->mlpeer_state == ML_PEER_DISCONN_INITIATED) { 268 mlo_peer_lock_release(ml_peer); 269 return; 270 } 271 272 ml_peer->mlpeer_state = ML_PEER_DISCONN_INITIATED; 273 ml_dev = ml_peer->ml_dev; 274 for (i = 0; i < MAX_MLO_LINK_PEERS; i++) { 275 link_peers[i] = NULL; 276 peer_entry = &ml_peer->peer_list[i]; 277 if (!peer_entry->link_peer) { 278 mlo_debug("link peer is null"); 279 continue; 280 } 281 282 if (peer_entry->link_peer == src_peer) 283 continue; 284 285 link_peer = peer_entry->link_peer; 286 if (wlan_objmgr_peer_try_get_ref(link_peer, WLAN_MLO_MGR_ID) != 287 QDF_STATUS_SUCCESS) 288 continue; 289 290 link_peers[i] = link_peer; 291 } 292 mlo_peer_lock_release(ml_peer); 293 294 for (i = 0; i < MAX_MLO_LINK_PEERS; i++) { 295 if (!link_peers[i]) 296 continue; 297 298 /* Prepare and queue message */ 299 mlo_link_peer_disconnect_notify(ml_dev, link_peers[i]); 300 } 301 } 302 303 static void mlo_peer_populate_link_peer( 304 struct wlan_mlo_peer_context *ml_peer, 305 struct wlan_objmgr_peer *link_peer) 306 { 307 mlo_peer_lock_acquire(ml_peer); 308 wlan_mlo_peer_get_ref(ml_peer); 309 link_peer->mlo_peer_ctx = ml_peer; 310 mlo_peer_lock_release(ml_peer); 311 } 312 313 static void mlo_reset_link_peer( 314 struct wlan_mlo_peer_context *ml_peer, 315 struct wlan_objmgr_peer *link_peer) 316 { 317 mlo_peer_lock_acquire(ml_peer); 318 link_peer->mlo_peer_ctx = NULL; 319 mlo_peer_lock_release(ml_peer); 320 } 321 322 void mlo_peer_free(struct wlan_mlo_peer_context *ml_peer) 323 { 324 struct wlan_mlo_dev_context *ml_dev; 325 326 ml_dev = ml_peer->ml_dev; 327 if (!ml_dev) { 328 mlo_err("ML DEV is NULL"); 329 return; 330 } 331 332 mlo_peer_lock_destroy(ml_peer); 333 mlo_ap_ml_peerid_free(ml_peer->mlo_peer_id); 334 mlo_peer_free_aid(ml_dev, ml_peer); 335 mlo_peer_free_primary_umac(ml_dev, ml_peer); 336 mlo_dev_mlpeer_detach(ml_dev, ml_peer); 337 qdf_mem_free(ml_peer); 338 } 339 340 static QDF_STATUS mlo_peer_attach_link_peer( 341 struct wlan_mlo_peer_context *ml_peer, 342 struct wlan_objmgr_peer *link_peer) 343 { 344 struct wlan_mlo_link_peer_entry *peer_entry; 345 QDF_STATUS status = QDF_STATUS_E_RESOURCES; 346 uint16_t i; 347 348 mlo_peer_lock_acquire(ml_peer); 349 350 if (ml_peer->mlpeer_state != ML_PEER_CREATED) { 351 mlo_peer_lock_release(ml_peer); 352 return status; 353 } 354 355 for (i = 0; i < MAX_MLO_LINK_PEERS; i++) { 356 peer_entry = &ml_peer->peer_list[i]; 357 if (peer_entry->link_peer) 358 continue; 359 360 if (wlan_objmgr_peer_try_get_ref(link_peer, WLAN_MLO_MGR_ID) != 361 QDF_STATUS_SUCCESS) 362 break; 363 364 peer_entry->link_peer = link_peer; 365 qdf_copy_macaddr(&peer_entry->link_addr, 366 (struct qdf_mac_addr *)&link_peer->macaddr[0]); 367 368 peer_entry->link_ix = i + 1; 369 peer_entry->hw_link_id = 1; 370 /*wlan_peer_get_hw_link_id(link_peer)TODO*/ 371 mlo_peer_assign_primary_umac(ml_peer, peer_entry); 372 373 status = QDF_STATUS_SUCCESS; 374 break; 375 } 376 if (QDF_IS_STATUS_SUCCESS(status)) 377 ml_peer->link_peer_cnt++; 378 379 mlo_peer_lock_release(ml_peer); 380 381 return status; 382 } 383 384 static QDF_STATUS mlo_peer_detach_link_peer( 385 struct wlan_mlo_peer_context *ml_peer, 386 struct wlan_objmgr_peer *link_peer) 387 { 388 struct wlan_mlo_link_peer_entry *peer_entry; 389 QDF_STATUS status = QDF_STATUS_E_RESOURCES; 390 uint16_t i; 391 392 mlo_peer_lock_acquire(ml_peer); 393 394 if (ml_peer->mlpeer_state != ML_PEER_DISCONN_INITIATED) { 395 mlo_peer_lock_release(ml_peer); 396 return status; 397 } 398 399 for (i = 0; i < MAX_MLO_LINK_PEERS; i++) { 400 peer_entry = &ml_peer->peer_list[i]; 401 if (!peer_entry->link_peer) 402 continue; 403 404 if (peer_entry->link_peer != link_peer) 405 continue; 406 407 wlan_objmgr_peer_release_ref(link_peer, WLAN_MLO_MGR_ID); 408 peer_entry->link_peer = NULL; 409 ml_peer->link_peer_cnt--; 410 status = QDF_STATUS_SUCCESS; 411 break; 412 } 413 mlo_peer_lock_release(ml_peer); 414 415 return status; 416 } 417 418 static QDF_STATUS mlo_dev_get_link_vdevs( 419 struct wlan_objmgr_vdev *vdev, 420 struct wlan_mlo_dev_context *ml_dev, 421 struct mlo_partner_info *ml_info, 422 struct wlan_objmgr_vdev *link_vdevs[]) 423 { 424 uint16_t i, j; 425 struct wlan_objmgr_vdev *vdev_link; 426 uint8_t link_id; 427 428 if (!ml_dev) { 429 mlo_err("ml_dev is null"); 430 return QDF_STATUS_E_INVAL; 431 } 432 433 if (!ml_info) { 434 mlo_err("ml_info is null"); 435 return QDF_STATUS_E_INVAL; 436 } 437 438 mlo_debug("num_partner_links %d", ml_info->num_partner_links); 439 for (i = 0; i < ml_info->num_partner_links; i++) { 440 link_id = ml_info->partner_link_info[i].link_id; 441 vdev_link = mlo_get_vdev_by_link_id(vdev, link_id); 442 if (vdev_link) { 443 link_vdevs[i] = vdev_link; 444 } else { 445 /* release ref which were taken before failure */ 446 for (j = 0; j < i; j++) { 447 vdev_link = link_vdevs[j]; 448 if (!vdev_link) 449 continue; 450 451 wlan_objmgr_vdev_release_ref(vdev_link, 452 WLAN_MLO_MGR_ID); 453 } 454 return QDF_STATUS_E_INVAL; 455 } 456 } 457 458 return QDF_STATUS_SUCCESS; 459 } 460 461 static void mlo_dev_release_link_vdevs( 462 struct wlan_objmgr_vdev *link_vdevs[]) 463 { 464 uint16_t i; 465 struct wlan_objmgr_vdev *vdev_link; 466 467 for (i = 0; i < WLAN_UMAC_MLO_MAX_VDEVS; i++) { 468 vdev_link = link_vdevs[i]; 469 if (!vdev_link) 470 continue; 471 472 wlan_objmgr_vdev_release_ref(vdev_link, WLAN_MLO_MGR_ID); 473 } 474 } 475 476 QDF_STATUS wlan_mlo_peer_create(struct wlan_objmgr_vdev *vdev, 477 struct wlan_objmgr_peer *link_peer, 478 struct mlo_partner_info *ml_info, 479 qdf_nbuf_t frm_buf, 480 uint16_t aid) 481 { 482 struct wlan_mlo_dev_context *ml_dev; 483 struct wlan_mlo_peer_context *ml_peer; 484 struct wlan_objmgr_vdev *link_vdevs[WLAN_UMAC_MLO_MAX_VDEVS] = { NULL }; 485 struct wlan_objmgr_vdev *vdev_link; 486 QDF_STATUS status; 487 uint16_t i; 488 489 /* get ML VDEV from VDEV */ 490 ml_dev = vdev->mlo_dev_ctx; 491 492 /* Check resources of Partner VDEV */ 493 status = mlo_dev_get_link_vdevs(vdev, ml_dev, ml_info, link_vdevs); 494 if (QDF_IS_STATUS_ERROR(status)) 495 return QDF_STATUS_E_FAILURE; 496 497 for (i = 0; i < WLAN_UMAC_MLO_MAX_VDEVS; i++) { 498 vdev_link = link_vdevs[i]; 499 if (!vdev_link) { 500 mlo_debug("vdev_link is null"); 501 continue; 502 } 503 504 if (wlan_vdev_is_peer_create_allowed(vdev_link) 505 != QDF_STATUS_SUCCESS) { 506 mlo_dev_release_link_vdevs(link_vdevs); 507 return QDF_STATUS_E_INVAL; 508 } 509 } 510 511 for (i = 0; i < WLAN_UMAC_MLO_MAX_VDEVS; i++) { 512 vdev_link = link_vdevs[i]; 513 if (vdev_link && (vdev_link != vdev) && 514 (wlan_vdev_get_peer_count(vdev_link) > 515 wlan_vdev_get_max_peer_count(vdev_link))) { 516 mlo_dev_release_link_vdevs(link_vdevs); 517 return QDF_STATUS_E_RESOURCES; 518 } 519 } 520 521 /* Allocate MLO peer */ 522 ml_peer = qdf_mem_malloc(sizeof(*ml_peer)); 523 if (!ml_peer) { 524 mlo_dev_release_link_vdevs(link_vdevs); 525 return QDF_STATUS_E_NOMEM; 526 } 527 528 qdf_atomic_init(&ml_peer->ref_cnt); 529 mlo_peer_lock_create(ml_peer); 530 ml_peer->ml_dev = ml_dev; 531 ml_peer->mlpeer_state = ML_PEER_CREATED; 532 ml_peer->max_links = ml_info->num_partner_links; 533 ml_peer->primary_umac_psoc_id = ML_PRIMARY_UMAC_ID_INVAL; 534 ml_peer->mlo_peer_id = mlo_ap_ml_peerid_alloc(); 535 qdf_copy_macaddr((struct qdf_mac_addr *)&ml_peer->peer_mld_addr, 536 (struct qdf_mac_addr *)&link_peer->mldaddr[0]); 537 /* Allocate AID */ 538 if (aid == (uint16_t)-1) 539 mlo_peer_allocate_aid(ml_dev, ml_peer); 540 else 541 ml_peer->assoc_id = aid; 542 543 /* Populate Link peer pointer, peer MAC address, 544 * MLD address. HW link ID, update ref count 545 */ 546 mlo_peer_attach_link_peer(ml_peer, link_peer); 547 548 /* Allocate Primary UMAC */ 549 mlo_peer_allocate_primary_umac(ml_dev, ml_peer, link_vdevs); 550 551 /* Store AID, MLO Peer pointer in link peer, take link peer ref count */ 552 mlo_peer_populate_link_peer(ml_peer, link_peer); 553 554 /* Attach MLO peer to ML Peer table */ 555 status = mlo_dev_mlpeer_attach(ml_dev, ml_peer); 556 if (status != QDF_STATUS_SUCCESS) { 557 mlo_reset_link_peer(ml_peer, link_peer); 558 mlo_peer_free(ml_peer); 559 mlo_dev_release_link_vdevs(link_vdevs); 560 return status; 561 } 562 563 if (wlan_vdev_mlme_get_opmode(vdev) == QDF_SAP_MODE) { 564 /* Notify other vdevs about link peer creation */ 565 for (i = 0; i < WLAN_UMAC_MLO_MAX_VDEVS; i++) { 566 vdev_link = link_vdevs[i]; 567 if (!vdev_link) 568 continue; 569 570 if (vdev_link == vdev) 571 continue; 572 573 mlo_partner_peer_create_post(ml_dev, vdev_link, 574 ml_peer, frm_buf, ml_info); 575 } 576 } 577 mlo_dev_release_link_vdevs(link_vdevs); 578 579 return QDF_STATUS_SUCCESS; 580 } 581 582 QDF_STATUS wlan_mlo_link_peer_attach(struct wlan_mlo_peer_context *ml_peer, 583 struct wlan_objmgr_peer *peer) 584 { 585 QDF_STATUS status; 586 struct wlan_objmgr_peer *assoc_peer; 587 588 /* Populate Link peer pointer, peer MAC address, 589 * MLD address. HW link ID, update ref count 590 */ 591 status = mlo_peer_attach_link_peer(ml_peer, peer); 592 if (QDF_IS_STATUS_ERROR(status)) 593 return status; 594 595 /* Store AID, MLO Peer pointer in link peer, take link peer ref count */ 596 mlo_peer_populate_link_peer(ml_peer, peer); 597 598 if (ml_peer->max_links == ml_peer->link_peer_cnt) { 599 assoc_peer = ml_peer->peer_list[0].link_peer; 600 if (assoc_peer) 601 mlo_mlme_peer_assoc_resp(assoc_peer); 602 } 603 604 return status; 605 } 606 607 QDF_STATUS wlan_mlo_link_peer_delete(struct wlan_objmgr_peer *peer) 608 { 609 struct wlan_mlo_peer_context *ml_peer; 610 611 ml_peer = peer->mlo_peer_ctx; 612 613 if (!ml_peer) 614 return QDF_STATUS_E_NOENT; 615 616 mlo_reset_link_peer(ml_peer, peer); 617 mlo_peer_detach_link_peer(ml_peer, peer); 618 wlan_mlo_peer_release_ref(ml_peer); 619 620 return QDF_STATUS_SUCCESS; 621 } 622