1 /* 2 * Copyright (c) 2012-2021 The Linux Foundation. All rights reserved. 3 * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. 4 * 5 * Permission to use, copy, modify, and/or distribute this software for 6 * any purpose with or without fee is hereby granted, provided that the 7 * above copyright notice and this permission notice appear in all 8 * copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 11 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 12 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 13 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 14 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 15 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 16 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 17 * PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 /* 21 * DOC: wlan_cm_roam_offload_event.c 22 * 23 * Implementation for the FW based roaming events api interfaces. 24 */ 25 #include "qdf_status.h" 26 #include "wlan_objmgr_psoc_obj.h" 27 #include "wlan_objmgr_pdev_obj.h" 28 #include "wlan_objmgr_vdev_obj.h" 29 #include "wlan_cm_roam_i.h" 30 #include <wlan_cm_public_struct.h> 31 #include "wlan_scan_public_structs.h" 32 #include "wlan_cm_roam_public_struct.h" 33 #include "wlan_serialization_api.h" 34 #include "wlan_cm_roam_api.h" 35 #include <wlan_cfg80211_scan.h> 36 #include "connection_mgr/core/src/wlan_cm_roam.h" 37 #include "connection_mgr/core/src/wlan_cm_sm.h" 38 #include "connection_mgr/core/src/wlan_cm_main_api.h" 39 #include "wlan_roam_debug.h" 40 #include "wlan_mlo_mgr_roam.h" 41 42 #define FW_ROAM_SYNC_TIMEOUT 7000 43 44 static QDF_STATUS 45 cm_fw_roam_ser_cb(struct wlan_serialization_command *cmd, 46 enum wlan_serialization_cb_reason reason) 47 { 48 QDF_STATUS status = QDF_STATUS_SUCCESS; 49 struct wlan_objmgr_vdev *vdev; 50 struct cnx_mgr *cm_ctx; 51 52 if (!cmd) { 53 mlme_err("cmd is NULL, reason: %d", reason); 54 QDF_ASSERT(0); 55 return QDF_STATUS_E_NULL_VALUE; 56 } 57 58 vdev = cmd->vdev; 59 60 cm_ctx = cm_get_cm_ctx(vdev); 61 if (!cm_ctx) 62 return QDF_STATUS_E_NULL_VALUE; 63 64 switch (reason) { 65 case WLAN_SER_CB_ACTIVATE_CMD: 66 cm_ctx->active_cm_id = cmd->cmd_id; 67 break; 68 case WLAN_SER_CB_CANCEL_CMD: 69 /* command removed from pending list. */ 70 break; 71 case WLAN_SER_CB_ACTIVE_CMD_TIMEOUT: 72 mlme_err(CM_PREFIX_FMT "Active command timeout", 73 CM_PREFIX_REF(wlan_vdev_get_id(vdev), cmd->cmd_id)); 74 75 cm_abort_fw_roam(cm_ctx, cmd->cmd_id); 76 break; 77 case WLAN_SER_CB_RELEASE_MEM_CMD: 78 cm_reset_active_cm_id(vdev, cmd->cmd_id); 79 wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_CM_ID); 80 break; 81 default: 82 QDF_ASSERT(0); 83 status = QDF_STATUS_E_INVAL; 84 break; 85 } 86 87 return status; 88 } 89 90 QDF_STATUS cm_abort_fw_roam(struct cnx_mgr *cm_ctx, 91 wlan_cm_id cm_id) 92 { 93 QDF_STATUS status; 94 enum wlan_cm_source source = CM_SOURCE_INVALID; 95 struct cm_roam_req *roam_req; 96 struct qdf_mac_addr bssid = QDF_MAC_ADDR_ZERO_INIT; 97 98 roam_req = cm_get_first_roam_command(cm_ctx->vdev); 99 if (roam_req) { 100 source = roam_req->req.source; 101 bssid = roam_req->req.bssid; 102 } 103 104 mlme_cm_osif_roam_abort_ind(cm_ctx->vdev); 105 status = cm_sm_deliver_event(cm_ctx->vdev, 106 WLAN_CM_SM_EV_ROAM_ABORT, 107 sizeof(wlan_cm_id), &cm_id); 108 if (QDF_IS_STATUS_ERROR(status)) 109 cm_remove_cmd(cm_ctx, &cm_id); 110 111 return status; 112 } 113 114 QDF_STATUS 115 cm_add_fw_roam_dummy_ser_cb(struct wlan_objmgr_pdev *pdev, 116 struct cnx_mgr *cm_ctx, 117 struct cm_req *cm_req) 118 { 119 struct wlan_serialization_command cmd = {0, }; 120 enum wlan_serialization_status ser_cmd_status; 121 QDF_STATUS status; 122 uint8_t vdev_id = wlan_vdev_get_id(cm_ctx->vdev); 123 124 status = wlan_objmgr_vdev_try_get_ref(cm_ctx->vdev, WLAN_MLME_CM_ID); 125 if (QDF_IS_STATUS_ERROR(status)) { 126 mlme_err(CM_PREFIX_FMT "unable to get reference", 127 CM_PREFIX_REF(vdev_id, cm_req->cm_id)); 128 return status; 129 } 130 131 cmd.cmd_type = WLAN_SER_CMD_VDEV_ROAM; 132 cmd.cmd_id = cm_req->cm_id; 133 cmd.cmd_cb = cm_fw_roam_ser_cb; 134 cmd.source = WLAN_UMAC_COMP_MLME; 135 cmd.is_high_priority = true; 136 cmd.cmd_timeout_duration = FW_ROAM_SYNC_TIMEOUT; 137 cmd.vdev = cm_ctx->vdev; 138 cmd.is_blocking = cm_ser_get_blocking_cmd(); 139 140 ser_cmd_status = wlan_serialization_request(&cmd); 141 switch (ser_cmd_status) { 142 case WLAN_SER_CMD_PENDING: 143 /* command moved to pending list.Do nothing */ 144 break; 145 case WLAN_SER_CMD_ACTIVE: 146 /* command moved to active list. Do nothing */ 147 break; 148 default: 149 mlme_err(CM_PREFIX_FMT "ser cmd status %d", 150 CM_PREFIX_REF(vdev_id, cm_req->cm_id), ser_cmd_status); 151 wlan_objmgr_vdev_release_ref(cm_ctx->vdev, WLAN_MLME_CM_ID); 152 153 return QDF_STATUS_E_FAILURE; 154 } 155 return QDF_STATUS_SUCCESS; 156 } 157 158 QDF_STATUS cm_prepare_roam_cmd(struct cnx_mgr *cm_ctx, 159 struct cm_req **roam_req, 160 enum wlan_cm_source source) 161 { 162 struct cm_req *req; 163 164 *roam_req = qdf_mem_malloc(sizeof(**roam_req)); 165 if (!*roam_req) 166 return QDF_STATUS_E_NOMEM; 167 168 req = *roam_req; 169 req->roam_req.req.source = source; 170 171 return QDF_STATUS_SUCCESS; 172 } 173 174 QDF_STATUS cm_add_fw_roam_cmd_to_list_n_ser(struct cnx_mgr *cm_ctx, 175 struct cm_req *cm_req) 176 { 177 struct wlan_objmgr_pdev *pdev; 178 QDF_STATUS status; 179 180 pdev = wlan_vdev_get_pdev(cm_ctx->vdev); 181 182 if (!pdev) { 183 mlme_err("Failed to find pdev for vdev id %d", 184 wlan_vdev_get_id(cm_ctx->vdev)); 185 return QDF_STATUS_E_FAILURE; 186 } 187 188 status = cm_add_roam_req_to_list(cm_ctx, cm_req); 189 if (QDF_IS_STATUS_ERROR(status)) { 190 cm_abort_fw_roam(cm_ctx, CM_ID_INVALID); 191 cm_free_roam_req_mem(&cm_req->roam_req); 192 qdf_mem_free(cm_req); 193 return status; 194 } 195 196 /** 197 * Skip adding dummy SER command for MLO link vdev. It's expected to add 198 * only for MLO sta in case of MLO connection 199 */ 200 if (wlan_vdev_mlme_is_mlo_link_vdev(cm_ctx->vdev)) 201 return status; 202 203 status = cm_add_fw_roam_dummy_ser_cb(pdev, cm_ctx, cm_req); 204 if (QDF_IS_STATUS_ERROR(status)) { 205 cm_abort_fw_roam(cm_ctx, cm_req->roam_req.cm_id); 206 return status; 207 } 208 209 return status; 210 } 211 212 QDF_STATUS cm_fw_roam_start_req(struct wlan_objmgr_psoc *psoc, uint8_t vdev_id) 213 { 214 QDF_STATUS status; 215 struct wlan_objmgr_vdev *vdev; 216 217 vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, vdev_id, 218 WLAN_MLME_SB_ID); 219 220 if (!vdev) { 221 mlme_err("vdev object is NULL"); 222 return QDF_STATUS_E_NULL_VALUE; 223 } 224 225 status = cm_sm_deliver_event(vdev, WLAN_CM_SM_EV_ROAM_START, 226 0, NULL); 227 228 if (QDF_IS_STATUS_ERROR(status)) 229 mlme_err("EV ROAM START not handled"); 230 231 wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_SB_ID); 232 233 return status; 234 } 235 236 QDF_STATUS cm_fw_roam_start(struct cnx_mgr *cm_ctx) 237 { 238 struct wlan_objmgr_pdev *pdev; 239 struct wlan_objmgr_psoc *psoc; 240 QDF_STATUS status; 241 wlan_scan_id scan_id; 242 bool abort_host_scan_cap = false; 243 wlan_cm_id cm_id; 244 struct cm_roam_req *roam_req = NULL; 245 246 roam_req = cm_get_first_roam_command(cm_ctx->vdev); 247 if (!roam_req) { 248 mlme_err("Failed to find roam req from list"); 249 cm_id = CM_ID_INVALID; 250 status = QDF_STATUS_E_FAILURE; 251 goto error; 252 } 253 254 cm_id = roam_req->cm_id; 255 pdev = wlan_vdev_get_pdev(cm_ctx->vdev); 256 if (!pdev) { 257 mlme_err(CM_PREFIX_FMT "Failed to find pdev", 258 CM_PREFIX_REF(roam_req->req.vdev_id, 259 roam_req->cm_id)); 260 status = QDF_STATUS_E_FAILURE; 261 goto error; 262 } 263 psoc = wlan_pdev_get_psoc(pdev); 264 if (!psoc) { 265 mlme_err(CM_PREFIX_FMT "Failed to find psoc", 266 CM_PREFIX_REF(roam_req->req.vdev_id, 267 roam_req->cm_id)); 268 status = QDF_STATUS_E_FAILURE; 269 goto error; 270 } 271 272 status = wlan_cm_roam_state_change(pdev, 273 roam_req->req.vdev_id, 274 WLAN_ROAMING_IN_PROG, 275 REASON_ROAM_CANDIDATE_FOUND); 276 277 if (QDF_IS_STATUS_ERROR(status)) 278 goto error; 279 280 mlme_cm_osif_roam_start_ind(cm_ctx->vdev); 281 /* 282 * For emergency deauth roaming, firmware sends ROAM start 283 * instead of ROAM scan start notification as data path queues 284 * will be stopped only during roam start notification. 285 * This is because, for deauth/disassoc triggered roam, the 286 * AP has sent deauth, and packets shouldn't be sent to AP 287 * after that. Since firmware is sending roam start directly 288 * host sends scan abort during roam scan, but in other 289 * triggers, the host receives roam start after candidate 290 * selection and roam scan is complete. So when host sends 291 * roam abort for emergency deauth roam trigger, the firmware 292 * roam scan is also aborted. This results in roaming failure. 293 * So send scan_id as CANCEL_HOST_SCAN_ID to scan module to 294 * abort only host triggered scans. 295 */ 296 abort_host_scan_cap = 297 wlan_mlme_get_host_scan_abort_support(psoc); 298 if (abort_host_scan_cap) 299 scan_id = CANCEL_HOST_SCAN_ID; 300 else 301 scan_id = INVAL_SCAN_ID; 302 303 wlan_abort_scan(pdev, INVAL_PDEV_ID, 304 roam_req->req.vdev_id, 305 scan_id, false); 306 error: 307 if (QDF_IS_STATUS_ERROR(status)) 308 cm_abort_fw_roam(cm_ctx, cm_id); 309 310 return status; 311 } 312 313 QDF_STATUS cm_fw_roam_abort_req(struct wlan_objmgr_psoc *psoc, uint8_t vdev_id) 314 { 315 struct wlan_objmgr_pdev *pdev; 316 struct wlan_objmgr_vdev *vdev; 317 struct cnx_mgr *cm_ctx; 318 QDF_STATUS status = QDF_STATUS_E_FAILURE; 319 struct cm_roam_req *roam_req = NULL; 320 wlan_cm_id cm_id = CM_ID_INVALID; 321 322 vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, vdev_id, 323 WLAN_MLME_SB_ID); 324 if (!vdev) { 325 mlme_err("vdev object is NULL"); 326 return QDF_STATUS_E_NULL_VALUE; 327 } 328 329 pdev = wlan_vdev_get_pdev(vdev); 330 if (!pdev) { 331 mlme_err("Failed to find pdev for vdev id %d", 332 vdev_id); 333 goto rel_ref; 334 } 335 336 cm_ctx = cm_get_cm_ctx(vdev); 337 if (!cm_ctx) 338 goto rel_ref; 339 340 roam_req = cm_get_first_roam_command(vdev); 341 if (roam_req) 342 cm_id = roam_req->cm_id; 343 344 /* continue even if no roam command is found */ 345 346 /* 347 * Switch to RSO enabled state only if the current state is 348 * WLAN_ROAMING_IN_PROG or WLAN_ROAM_SYNCH_IN_PROG. 349 * This API can be called in internal roam aborts also when 350 * RSO state is deinit and cause RSO start to be sent in 351 * disconnected state. 352 */ 353 if (MLME_IS_ROAMING_IN_PROG(psoc, vdev_id) || 354 MLME_IS_ROAM_SYNCH_IN_PROGRESS(psoc, vdev_id)) 355 status = wlan_cm_roam_state_change(pdev, vdev_id, 356 WLAN_ROAM_RSO_ENABLED, 357 REASON_ROAM_ABORT); 358 else if (MLME_IS_MLO_ROAM_SYNCH_IN_PROGRESS(psoc, vdev_id)) 359 status = wlan_cm_roam_state_change(pdev, vdev_id, 360 WLAN_ROAM_DEINIT, 361 REASON_ROAM_ABORT); 362 363 cm_abort_fw_roam(cm_ctx, cm_id); 364 rel_ref: 365 wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_SB_ID); 366 367 return status; 368 } 369 370 QDF_STATUS 371 cm_roam_sync_event_handler(struct wlan_objmgr_psoc *psoc, 372 uint8_t *event, 373 uint32_t len, 374 struct roam_offload_synch_ind *sync_ind) 375 { 376 if (!sync_ind) { 377 mlme_err("invalid sync_ind"); 378 return QDF_STATUS_E_NULL_VALUE; 379 } 380 if (sync_ind->hw_mode_trans_present) 381 cm_handle_roam_sync_update_hw_mode( 382 &sync_ind->hw_mode_trans_ind); 383 384 return mlo_fw_roam_sync_req(psoc, sync_ind->roamed_vdev_id, 385 sync_ind, sizeof(sync_ind)); 386 } 387 388 QDF_STATUS 389 cm_roam_sync_frame_event_handler(struct wlan_objmgr_psoc *psoc, 390 struct roam_synch_frame_ind *frame_ind) 391 { 392 struct wlan_objmgr_vdev *vdev; 393 struct rso_config *rso_cfg; 394 struct roam_synch_frame_ind *sync_frame_ind = frame_ind; 395 struct roam_synch_frame_ind *roam_synch_frame_ind; 396 struct roam_scan_candidate_frame roam_candidate = {0}; 397 uint8_t vdev_id; 398 QDF_STATUS status = QDF_STATUS_SUCCESS; 399 400 if (!sync_frame_ind) 401 return QDF_STATUS_E_NULL_VALUE; 402 403 vdev_id = sync_frame_ind->vdev_id; 404 405 vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, vdev_id, 406 WLAN_MLME_SB_ID); 407 if (!vdev) { 408 mlme_err("vdev object is NULL"); 409 return QDF_STATUS_E_FAILURE; 410 } 411 412 rso_cfg = wlan_cm_get_rso_config(vdev); 413 if (!rso_cfg) { 414 status = QDF_STATUS_E_FAILURE; 415 goto complete; 416 } 417 418 roam_synch_frame_ind = &rso_cfg->roam_sync_frame_ind; 419 420 if (MLME_IS_ROAM_SYNCH_IN_PROGRESS(psoc, vdev_id)) { 421 mlme_err("Ignoring this event as it is unexpected"); 422 wlan_cm_free_roam_synch_frame_ind(rso_cfg); 423 status = QDF_STATUS_E_FAILURE; 424 goto complete; 425 } 426 427 if (sync_frame_ind->bcn_probe_rsp_len) { 428 roam_synch_frame_ind->bcn_probe_rsp_len = 429 sync_frame_ind->bcn_probe_rsp_len; 430 roam_synch_frame_ind->is_beacon = 431 sync_frame_ind->is_beacon; 432 if (roam_synch_frame_ind->bcn_probe_rsp) 433 qdf_mem_free(roam_synch_frame_ind->bcn_probe_rsp); 434 roam_synch_frame_ind->bcn_probe_rsp = 435 sync_frame_ind->bcn_probe_rsp; 436 } 437 438 if (sync_frame_ind->link_bcn_probe_rsp_len) { 439 roam_synch_frame_ind->link_bcn_probe_rsp_len = 440 sync_frame_ind->link_bcn_probe_rsp_len; 441 roam_synch_frame_ind->is_link_beacon = 442 sync_frame_ind->is_link_beacon; 443 if (roam_synch_frame_ind->link_bcn_probe_rsp) 444 qdf_mem_free(roam_synch_frame_ind->link_bcn_probe_rsp); 445 roam_synch_frame_ind->link_bcn_probe_rsp = 446 sync_frame_ind->link_bcn_probe_rsp; 447 } 448 449 if (sync_frame_ind->reassoc_req_len) { 450 roam_synch_frame_ind->reassoc_req_len = 451 sync_frame_ind->reassoc_req_len; 452 if (roam_synch_frame_ind->reassoc_req) 453 qdf_mem_free(roam_synch_frame_ind->reassoc_req); 454 roam_synch_frame_ind->reassoc_req = 455 sync_frame_ind->reassoc_req; 456 } 457 458 if (sync_frame_ind->reassoc_rsp_len) { 459 roam_synch_frame_ind->reassoc_rsp_len = 460 sync_frame_ind->reassoc_rsp_len; 461 if (roam_synch_frame_ind->reassoc_rsp) 462 qdf_mem_free(roam_synch_frame_ind->reassoc_rsp); 463 roam_synch_frame_ind->reassoc_rsp = 464 sync_frame_ind->reassoc_rsp; 465 } 466 467 if (!sync_frame_ind->bcn_probe_rsp_len && 468 !sync_frame_ind->link_bcn_probe_rsp_len) 469 goto complete; 470 471 roam_candidate.vdev_id = vdev_id; 472 473 if (sync_frame_ind->bcn_probe_rsp_len) { 474 roam_candidate.frame_length = sync_frame_ind->bcn_probe_rsp_len; 475 roam_candidate.frame = sync_frame_ind->bcn_probe_rsp; 476 roam_candidate.rssi = sync_frame_ind->rssi; 477 roam_candidate.roam_offload_candidate_frm = false; 478 wlan_cm_add_all_link_probe_rsp_to_scan_db(psoc, 479 &roam_candidate); 480 } 481 482 if (sync_frame_ind->link_bcn_probe_rsp_len) { 483 roam_candidate.frame_length = 484 sync_frame_ind->link_bcn_probe_rsp_len; 485 roam_candidate.frame = sync_frame_ind->link_bcn_probe_rsp; 486 roam_candidate.rssi = sync_frame_ind->rssi; 487 roam_candidate.roam_offload_candidate_frm = false; 488 wlan_cm_add_all_link_probe_rsp_to_scan_db(psoc, 489 &roam_candidate); 490 } 491 492 complete: 493 wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_SB_ID); 494 return status; 495 } 496 497 #if defined(WLAN_FEATURE_11BE_MLO) && defined(WLAN_FEATURE_ROAM_OFFLOAD) 498 QDF_STATUS cm_roam_sync_key_event_handler(struct wlan_objmgr_psoc *psoc, 499 struct wlan_crypto_key_entry *keys, 500 uint8_t num_keys) 501 { 502 QDF_STATUS status = QDF_STATUS_SUCCESS; 503 uint8_t i; 504 505 for (i = 0; i < num_keys; i++) { 506 status = wlan_crypto_add_key_entry(psoc, &keys[i]); 507 if (QDF_IS_STATUS_ERROR(status)) { 508 mlme_err("Failed to add key entry for link:%d", 509 keys[i].link_id); 510 wlan_crypto_free_key(&keys[i].keys); 511 qdf_mem_zero(&keys[i], 512 sizeof(struct wlan_crypto_key_entry)); 513 qdf_mem_free(&keys[i]); 514 } 515 } 516 517 return status; 518 } 519 #endif 520 521 QDF_STATUS cm_roam_sync_event_handler_cb(struct wlan_objmgr_vdev *vdev, 522 uint8_t *event, 523 uint32_t len) 524 { 525 struct roam_offload_synch_ind *sync_ind = NULL; 526 struct wlan_objmgr_psoc *psoc = NULL; 527 QDF_STATUS status = QDF_STATUS_SUCCESS; 528 struct rso_config *rso_cfg; 529 uint16_t ie_len = 0; 530 uint8_t vdev_id; 531 532 sync_ind = (struct roam_offload_synch_ind *)event; 533 if (!sync_ind) { 534 mlme_err("Roam Sync ind ptr is NULL"); 535 return QDF_STATUS_E_NULL_VALUE; 536 } 537 538 if (!vdev) { 539 mlme_err("Vdev is NULL"); 540 return QDF_STATUS_E_NULL_VALUE; 541 } 542 543 psoc = wlan_vdev_get_psoc(vdev); 544 if (!psoc) { 545 mlme_err("Psoc is NULL"); 546 return QDF_STATUS_E_NULL_VALUE; 547 } 548 549 vdev_id = wlan_vdev_get_id(vdev); 550 rso_cfg = wlan_cm_get_rso_config(vdev); 551 if (!rso_cfg) 552 return QDF_STATUS_E_NULL_VALUE; 553 554 wlan_roam_debug_log(sync_ind->roamed_vdev_id, DEBUG_ROAM_SYNCH_IND, 555 DEBUG_INVALID_PEER_ID, sync_ind->bssid.bytes, NULL, 556 0, 0); 557 DPTRACE(qdf_dp_trace_record_event(QDF_DP_TRACE_EVENT_RECORD, 558 sync_ind->roamed_vdev_id, 559 QDF_TRACE_DEFAULT_PDEV_ID, 560 QDF_PROTO_TYPE_EVENT, 561 QDF_ROAM_SYNCH)); 562 563 if (MLME_IS_ROAM_SYNCH_IN_PROGRESS(psoc, sync_ind->roamed_vdev_id) && 564 !is_multi_link_roam(sync_ind)) { 565 mlme_err("vdev:%d Ignoring RSI as its already in progress on roamed vdev:%d", 566 vdev_id, sync_ind->roamed_vdev_id); 567 return QDF_STATUS_E_FAILURE; 568 } 569 570 status = cm_fw_roam_sync_start_ind(vdev, sync_ind); 571 if (QDF_IS_STATUS_ERROR(status)) { 572 mlme_err("LFR3: vdev:%d CSR Roam synch cb failed", vdev_id); 573 wlan_cm_free_roam_synch_frame_ind(rso_cfg); 574 return status; 575 } 576 577 /* 24 byte MAC header and 12 byte to ssid IE */ 578 if (wlan_vdev_mlme_is_mlo_link_vdev(vdev) && 579 sync_ind->link_beacon_probe_resp_length) { 580 if (sync_ind->link_beacon_probe_resp_length > 581 (QDF_IEEE80211_3ADDR_HDR_LEN + MAC_B_PR_SSID_OFFSET)) { 582 ie_len = MAX_MGMT_MPDU_LEN - 583 (QDF_IEEE80211_3ADDR_HDR_LEN + MAC_B_PR_SSID_OFFSET); 584 } else { 585 mlme_err("LFR3: MLO: vdev:%d Invalid link Beacon Length", 586 vdev_id); 587 return QDF_STATUS_E_FAILURE; 588 } 589 } else if (sync_ind->beacon_probe_resp_length > 590 (QDF_IEEE80211_3ADDR_HDR_LEN + MAC_B_PR_SSID_OFFSET)) { 591 /* 592 * When STA roams to an MLO AP, non-assoc link might be superior 593 * in features compared to assoc link and the per-STA profile 594 * info may carry corresponding IEs. These IEs are extracted 595 * and added to IE list of link probe response while generating 596 * it. So, the link probe response generated from assoc link 597 * probe response might be of more size than assoc link probe 598 * rsp. Allocate buffer for the bss descriptor to accommodate 599 * all of the IEs got generated as part of link probe rsp 600 * generation. Allocate MAX_MGMT_MPDU_LEN bytes for IEs as the 601 * max frame size that can be received from AP is 602 * MAX_MGMT_MPDU_LEN bytes. 603 */ 604 if (is_multi_link_roam(sync_ind)) 605 ie_len = MAX_MGMT_MPDU_LEN - 606 (QDF_IEEE80211_3ADDR_HDR_LEN + MAC_B_PR_SSID_OFFSET); 607 else 608 ie_len = sync_ind->beacon_probe_resp_length - 609 (QDF_IEEE80211_3ADDR_HDR_LEN + MAC_B_PR_SSID_OFFSET); 610 611 } else { 612 mlme_err("LFR3: vdev:%d Invalid Beacon Length:%d", vdev_id, 613 sync_ind->beacon_probe_resp_length); 614 return QDF_STATUS_E_FAILURE; 615 } 616 617 if (QDF_IS_STATUS_ERROR(cm_roam_pe_sync_callback(sync_ind, vdev_id, 618 ie_len))) { 619 mlme_err("LFR3: vdev:%d PE roam synch cb failed", vdev_id); 620 return QDF_STATUS_E_BUSY; 621 } 622 623 status = cm_roam_update_vdev(vdev, sync_ind); 624 if (QDF_IS_STATUS_ERROR(status)) 625 return status; 626 627 /* 628 * update phy_mode in wma to avoid mismatch in phymode between host and 629 * firmware. The phymode stored in peer->peer_mlme.phymode is 630 * sent to firmware as part of opmode update during either - vht opmode 631 * action frame received or during opmode change detected while 632 * processing beacon. Any mismatch of this value with firmware phymode 633 * results in firmware assert. 634 */ 635 cm_update_phymode_on_roam(vdev_id, 636 sync_ind); 637 status = cm_fw_roam_sync_propagation(psoc, 638 vdev_id, 639 sync_ind); 640 641 return status; 642 } 643