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