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
cm_fw_roam_ser_cb(struct wlan_serialization_command * cmd,enum wlan_serialization_cb_reason reason)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
cm_abort_fw_roam(struct cnx_mgr * cm_ctx,wlan_cm_id cm_id)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
cm_add_fw_roam_dummy_ser_cb(struct wlan_objmgr_pdev * pdev,struct cnx_mgr * cm_ctx,struct cm_req * cm_req)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
cm_prepare_roam_cmd(struct cnx_mgr * cm_ctx,struct cm_req ** roam_req,enum wlan_cm_source source)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
cm_add_fw_roam_cmd_to_list_n_ser(struct cnx_mgr * cm_ctx,struct cm_req * cm_req)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
cm_fw_roam_start_req(struct wlan_objmgr_psoc * psoc,uint8_t vdev_id)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
cm_fw_roam_start(struct cnx_mgr * cm_ctx)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
cm_fw_roam_abort_req(struct wlan_objmgr_psoc * psoc,uint8_t vdev_id)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
cm_roam_sync_event_handler(struct wlan_objmgr_psoc * psoc,uint8_t * event,uint32_t len,struct roam_offload_synch_ind * sync_ind)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
cm_roam_sync_frame_event_handler(struct wlan_objmgr_psoc * psoc,struct roam_synch_frame_ind * frame_ind)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)
cm_roam_sync_key_event_handler(struct wlan_objmgr_psoc * psoc,struct wlan_crypto_key_entry * keys,uint8_t num_keys)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
cm_roam_sync_event_handler_cb(struct wlan_objmgr_vdev * vdev,uint8_t * event,uint32_t len)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