xref: /wlan-dirver/qca-wifi-host-cmn/umac/mlo_mgr/src/wlan_mlo_mgr_link_switch.c (revision 784c8b910daf70ef96a1d04018327055e3cd080a)
1 /*
2  * Copyright (c) 2023-2024 Qualcomm Innovation Center, Inc. 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 /*
18  * DOC: contains MLO manager Link Switch related functionality
19  */
20 #include <wlan_mlo_mgr_link_switch.h>
21 #include <wlan_mlo_mgr_main.h>
22 #include <wlan_mlo_mgr_sta.h>
23 #include <wlan_serialization_api.h>
24 #include <wlan_cm_api.h>
25 #include <wlan_crypto_def_i.h>
26 #ifdef WLAN_FEATURE_11BE_MLO_ADV_FEATURE
27 #include "wlan_cm_roam_api.h"
28 #endif
29 #include "host_diag_core_event.h"
30 
31 void mlo_mgr_update_link_info_mac_addr(struct wlan_objmgr_vdev *vdev,
32 				       struct wlan_mlo_link_mac_update *ml_mac_update)
33 {
34 	struct mlo_link_info *link_info;
35 	uint8_t link_info_iter;
36 	struct mlo_vdev_link_mac_info *link_mac_info;
37 
38 	if (!vdev || !vdev->mlo_dev_ctx || !ml_mac_update)
39 		return;
40 
41 	link_mac_info = &ml_mac_update->link_mac_info[0];
42 	link_info = &vdev->mlo_dev_ctx->link_ctx->links_info[0];
43 
44 	for (link_info_iter = 0; link_info_iter < WLAN_MAX_ML_BSS_LINKS;
45 	     link_info_iter++) {
46 		qdf_mem_copy(&link_info->link_addr,
47 			     &link_mac_info->link_mac_addr,
48 			     QDF_MAC_ADDR_SIZE);
49 
50 		link_info->vdev_id = link_mac_info->vdev_id;
51 		mlo_debug("Update STA Link info for vdev_id %d, link_addr:" QDF_MAC_ADDR_FMT,
52 			  link_info->vdev_id,
53 			  QDF_MAC_ADDR_REF(link_info->link_addr.bytes));
54 		link_mac_info++;
55 		link_info++;
56 	}
57 }
58 
59 void mlo_mgr_update_ap_link_info(struct wlan_objmgr_vdev *vdev, uint8_t link_id,
60 				 uint8_t *ap_link_addr,
61 				 struct wlan_channel channel)
62 {
63 	struct mlo_link_info *link_info;
64 	uint8_t link_info_iter;
65 
66 	if (!vdev || !vdev->mlo_dev_ctx || !ap_link_addr)
67 		return;
68 
69 	link_info = &vdev->mlo_dev_ctx->link_ctx->links_info[0];
70 	for (link_info_iter = 0; link_info_iter < WLAN_MAX_ML_BSS_LINKS;
71 	     link_info_iter++) {
72 		if (qdf_is_macaddr_zero(&link_info->ap_link_addr))
73 			break;
74 
75 		link_info++;
76 	}
77 
78 	if (link_info_iter == WLAN_MAX_ML_BSS_LINKS)
79 		return;
80 
81 	qdf_mem_copy(&link_info->ap_link_addr, ap_link_addr, QDF_MAC_ADDR_SIZE);
82 
83 	qdf_mem_copy(link_info->link_chan_info, &channel, sizeof(channel));
84 	link_info->link_status_flags = 0;
85 	link_info->link_id = link_id;
86 
87 	mlo_debug("Update AP Link info for link_id: %d, vdev_id:%d, link_addr:" QDF_MAC_ADDR_FMT,
88 		  link_info->link_id, link_info->vdev_id,
89 		  QDF_MAC_ADDR_REF(link_info->ap_link_addr.bytes));
90 }
91 
92 void mlo_mgr_clear_ap_link_info(struct wlan_objmgr_vdev *vdev,
93 				uint8_t *ap_link_addr)
94 {
95 	struct mlo_link_info *link_info;
96 	uint8_t link_info_iter;
97 
98 	if (!vdev || !vdev->mlo_dev_ctx || !ap_link_addr)
99 		return;
100 
101 	link_info = &vdev->mlo_dev_ctx->link_ctx->links_info[0];
102 	for (link_info_iter = 0; link_info_iter < WLAN_MAX_ML_BSS_LINKS;
103 	     link_info_iter++) {
104 		if (qdf_is_macaddr_equal(&link_info->ap_link_addr,
105 					 (struct qdf_mac_addr *)ap_link_addr)) {
106 			mlo_debug("Clear AP link info for link_id: %d, vdev_id:%d, link_addr:" QDF_MAC_ADDR_FMT,
107 				  link_info->link_id, link_info->vdev_id,
108 				  QDF_MAC_ADDR_REF(link_info->ap_link_addr.bytes));
109 
110 			qdf_mem_zero(&link_info->ap_link_addr,
111 				     QDF_MAC_ADDR_SIZE);
112 			qdf_mem_zero(link_info->link_chan_info,
113 				     sizeof(*link_info->link_chan_info));
114 			link_info->link_id = WLAN_INVALID_LINK_ID;
115 			link_info->link_status_flags = 0;
116 
117 			return;
118 		}
119 
120 		link_info++;
121 	}
122 }
123 
124 void mlo_mgr_update_ap_channel_info(struct wlan_objmgr_vdev *vdev, uint8_t link_id,
125 				    uint8_t *ap_link_addr,
126 				    struct wlan_channel channel)
127 {
128 	struct mlo_link_info *link_info;
129 
130 	if (!vdev || !vdev->mlo_dev_ctx || !ap_link_addr)
131 		return;
132 
133 	link_info = mlo_mgr_get_ap_link_by_link_id(vdev->mlo_dev_ctx, link_id);
134 	if (!link_info)
135 		return;
136 
137 	qdf_mem_copy(link_info->link_chan_info, &channel,
138 		     sizeof(*link_info->link_chan_info));
139 
140 	mlo_debug("Update AP Channel info link_id: %d, vdev_id:%d, link_addr:" QDF_MAC_ADDR_FMT,
141 		  link_info->link_id, link_info->vdev_id,
142 		  QDF_MAC_ADDR_REF(link_info->ap_link_addr.bytes));
143 	mlo_debug("Ch_freq: %d, freq1: %d, freq2: %d phy_mode: %d",
144 		  link_info->link_chan_info->ch_freq,
145 		  link_info->link_chan_info->ch_cfreq1,
146 		  link_info->link_chan_info->ch_cfreq2,
147 		  link_info->link_chan_info->ch_phymode);
148 }
149 
150 void mlo_mgr_update_link_info_reset(struct wlan_objmgr_psoc *psoc,
151 				    struct wlan_mlo_dev_context *ml_dev)
152 {
153 	struct mlo_link_info *link_info;
154 	uint8_t link_info_iter;
155 
156 	if (!ml_dev)
157 		return;
158 
159 	link_info = &ml_dev->link_ctx->links_info[0];
160 
161 	for (link_info_iter = 0; link_info_iter < WLAN_MAX_ML_BSS_LINKS;
162 	     link_info_iter++) {
163 		if (!qdf_is_macaddr_zero(&link_info->ap_link_addr) &&
164 		    !qdf_is_macaddr_zero(&link_info->link_addr))
165 			wlan_crypto_free_key_by_link_id(
166 						psoc,
167 						&link_info->link_addr,
168 						link_info->link_id);
169 		qdf_mem_zero(&link_info->link_addr, QDF_MAC_ADDR_SIZE);
170 		qdf_mem_zero(&link_info->ap_link_addr, QDF_MAC_ADDR_SIZE);
171 		qdf_mem_zero(link_info->link_chan_info,
172 			     sizeof(*link_info->link_chan_info));
173 		link_info->vdev_id = WLAN_INVALID_VDEV_ID;
174 		link_info->link_id = WLAN_INVALID_LINK_ID;
175 		link_info->link_status_flags = 0;
176 		link_info++;
177 	}
178 }
179 
180 void mlo_mgr_reset_ap_link_info(struct wlan_objmgr_vdev *vdev)
181 {
182 	struct mlo_link_info *link_info;
183 	uint8_t link_info_iter;
184 	struct wlan_objmgr_psoc *psoc;
185 
186 	if (!vdev || !vdev->mlo_dev_ctx || !vdev->mlo_dev_ctx->link_ctx)
187 		return;
188 
189 	psoc = wlan_vdev_get_psoc(vdev);
190 	if (!psoc) {
191 		mlo_err("psoc NULL");
192 		return;
193 	}
194 
195 	link_info = &vdev->mlo_dev_ctx->link_ctx->links_info[0];
196 
197 	for (link_info_iter = 0; link_info_iter < WLAN_MAX_ML_BSS_LINKS;
198 	     link_info_iter++) {
199 		if (!qdf_is_macaddr_zero(&link_info->ap_link_addr) &&
200 		    !qdf_is_macaddr_zero(&link_info->link_addr))
201 			wlan_crypto_free_key_by_link_id(
202 						psoc,
203 						&link_info->link_addr,
204 						link_info->link_id);
205 		qdf_mem_zero(&link_info->ap_link_addr, QDF_MAC_ADDR_SIZE);
206 		qdf_mem_zero(link_info->link_chan_info,
207 			     sizeof(*link_info->link_chan_info));
208 		link_info->link_id = WLAN_INVALID_LINK_ID;
209 		link_info->link_status_flags = 0;
210 		link_info++;
211 	}
212 }
213 
214 struct mlo_link_info
215 *mlo_mgr_get_ap_link(struct wlan_objmgr_vdev *vdev)
216 {
217 	if (!vdev || !vdev->mlo_dev_ctx)
218 		return NULL;
219 
220 	return &vdev->mlo_dev_ctx->link_ctx->links_info[0];
221 }
222 
223 static
224 void mlo_mgr_alloc_link_info_wmi_chan(struct wlan_mlo_dev_context *ml_dev)
225 {
226 	struct mlo_link_info *link_info;
227 	uint8_t link_info_iter;
228 
229 	if (!ml_dev)
230 		return;
231 
232 	link_info = &ml_dev->link_ctx->links_info[0];
233 	for (link_info_iter = 0; link_info_iter < WLAN_MAX_ML_BSS_LINKS;
234 	     link_info_iter++) {
235 		link_info->link_chan_info =
236 			qdf_mem_malloc(sizeof(*link_info->link_chan_info));
237 		if (!link_info->link_chan_info)
238 			return;
239 		link_info++;
240 	}
241 }
242 
243 static
244 void mlo_mgr_free_link_info_wmi_chan(struct wlan_mlo_dev_context *ml_dev)
245 {
246 	struct mlo_link_info *link_info;
247 	uint8_t link_info_iter;
248 
249 	if (!ml_dev)
250 		return;
251 
252 	link_info = &ml_dev->link_ctx->links_info[0];
253 	for (link_info_iter = 0; link_info_iter < WLAN_MAX_ML_BSS_LINKS;
254 	     link_info_iter++) {
255 		if (link_info->link_chan_info)
256 			qdf_mem_free(link_info->link_chan_info);
257 		link_info++;
258 	}
259 }
260 
261 #ifdef WLAN_FEATURE_11BE_MLO_ADV_FEATURE
262 struct mlo_link_info
263 *mlo_mgr_get_ap_link_by_link_id(struct wlan_mlo_dev_context *mlo_dev_ctx,
264 				int link_id)
265 {
266 	struct mlo_link_info *link_info;
267 	uint8_t link_info_iter;
268 
269 	if (!mlo_dev_ctx || link_id < 0 || link_id > 15)
270 		return NULL;
271 
272 	link_info = &mlo_dev_ctx->link_ctx->links_info[0];
273 	for (link_info_iter = 0; link_info_iter < WLAN_MAX_ML_BSS_LINKS;
274 	     link_info_iter++) {
275 		if (link_info->link_id == link_id)
276 			return link_info;
277 		link_info++;
278 	}
279 
280 	return NULL;
281 }
282 
283 bool mlo_mgr_update_csa_link_info(struct wlan_objmgr_pdev *pdev,
284 				  struct wlan_mlo_dev_context *mlo_dev_ctx,
285 				  struct csa_offload_params *csa_param,
286 				  uint8_t link_id)
287 {
288 	struct mlo_link_info *link_info;
289 	uint16_t bw_val;
290 	uint32_t ch_cfreq1, ch_cfreq2;
291 
292 	if (!mlo_dev_ctx) {
293 		mlo_err("invalid mlo dev ctx");
294 		goto done;
295 	}
296 
297 	bw_val = wlan_reg_get_bw_value(csa_param->new_ch_width);
298 
299 	link_info = mlo_mgr_get_ap_link_by_link_id(mlo_dev_ctx, link_id);
300 	if (!link_info) {
301 		mlo_err("invalid link_info");
302 		goto done;
303 	}
304 
305 	link_info->link_chan_info->ch_freq = csa_param->csa_chan_freq;
306 
307 	if (wlan_reg_is_6ghz_chan_freq(csa_param->csa_chan_freq)) {
308 		ch_cfreq1 = wlan_reg_compute_6g_center_freq_from_cfi(
309 					csa_param->new_ch_freq_seg1);
310 		ch_cfreq2 = wlan_reg_compute_6g_center_freq_from_cfi(
311 					csa_param->new_ch_freq_seg2);
312 	} else {
313 		ch_cfreq1 = wlan_reg_legacy_chan_to_freq(pdev,
314 					csa_param->new_ch_freq_seg1);
315 		ch_cfreq2 = wlan_reg_legacy_chan_to_freq(pdev,
316 					csa_param->new_ch_freq_seg2);
317 	}
318 
319 	link_info->link_chan_info->ch_cfreq1 = ch_cfreq1;
320 	link_info->link_chan_info->ch_cfreq2 = ch_cfreq2;
321 
322 	link_info->link_chan_info->ch_phymode = wlan_eht_chan_phy_mode(
323 					csa_param->csa_chan_freq,
324 					bw_val, csa_param->new_ch_width);
325 
326 	mlo_debug("CSA: freq: %d, cfreq1: %d, cfreq2: %d, bw: %d, phymode:%d",
327 		  link_info->link_chan_info->ch_freq, ch_cfreq1, ch_cfreq2,
328 		  bw_val, link_info->link_chan_info->ch_phymode);
329 
330 	return true;
331 done:
332 	return false;
333 }
334 
335 struct wlan_objmgr_vdev *
336 mlo_mgr_link_switch_get_assoc_vdev(struct wlan_objmgr_vdev *vdev)
337 {
338 	uint8_t vdev_id;
339 	struct wlan_objmgr_psoc *psoc;
340 	struct wlan_objmgr_vdev *assoc_vdev;
341 
342 	if (!vdev)
343 		return NULL;
344 
345 	if (!mlo_mgr_is_link_switch_on_assoc_vdev(vdev))
346 		return NULL;
347 
348 	vdev_id = vdev->mlo_dev_ctx->link_ctx->last_req.vdev_id;
349 	psoc = wlan_vdev_get_psoc(vdev);
350 	if (!psoc) {
351 		mlo_err("PSOC NULL");
352 		return NULL;
353 	}
354 
355 	assoc_vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, vdev_id,
356 							  WLAN_MLO_MGR_ID);
357 
358 	return assoc_vdev;
359 }
360 
361 bool mlo_mgr_is_link_switch_in_progress(struct wlan_objmgr_vdev *vdev)
362 {
363 	enum mlo_link_switch_req_state state;
364 	struct wlan_mlo_dev_context *mlo_dev_ctx = vdev->mlo_dev_ctx;
365 
366 	if (!mlo_dev_ctx)
367 		return false;
368 
369 	state = mlo_mgr_link_switch_get_curr_state(mlo_dev_ctx);
370 	return (state > MLO_LINK_SWITCH_STATE_INIT);
371 }
372 
373 bool mlo_mgr_is_link_switch_on_assoc_vdev(struct wlan_objmgr_vdev *vdev)
374 {
375 	if (!mlo_mgr_is_link_switch_in_progress(vdev))
376 		return false;
377 
378 	return vdev->mlo_dev_ctx->link_ctx->last_req.restore_vdev_flag;
379 }
380 
381 void mlo_mgr_link_switch_init_state(struct wlan_mlo_dev_context *mlo_dev_ctx)
382 {
383 	mlo_dev_lock_acquire(mlo_dev_ctx);
384 	mlo_dev_ctx->link_ctx->last_req.state = MLO_LINK_SWITCH_STATE_IDLE;
385 	mlo_dev_lock_release(mlo_dev_ctx);
386 }
387 
388 QDF_STATUS
389 mlo_mgr_link_switch_trans_next_state(struct wlan_mlo_dev_context *mlo_dev_ctx)
390 {
391 	QDF_STATUS status = QDF_STATUS_SUCCESS;
392 	enum mlo_link_switch_req_state cur_state, next_state;
393 
394 	mlo_dev_lock_acquire(mlo_dev_ctx);
395 	cur_state = mlo_dev_ctx->link_ctx->last_req.state;
396 	switch (cur_state) {
397 	case MLO_LINK_SWITCH_STATE_IDLE:
398 		next_state = MLO_LINK_SWITCH_STATE_INIT;
399 		break;
400 	case MLO_LINK_SWITCH_STATE_INIT:
401 		next_state = MLO_LINK_SWITCH_STATE_DISCONNECT_CURR_LINK;
402 		break;
403 	case MLO_LINK_SWITCH_STATE_DISCONNECT_CURR_LINK:
404 		next_state = MLO_LINK_SWITCH_STATE_SET_MAC_ADDR;
405 		break;
406 	case MLO_LINK_SWITCH_STATE_SET_MAC_ADDR:
407 		next_state = MLO_LINK_SWITCH_STATE_CONNECT_NEW_LINK;
408 		break;
409 	case MLO_LINK_SWITCH_STATE_CONNECT_NEW_LINK:
410 		next_state = MLO_LINK_SWITCH_STATE_COMPLETE_SUCCESS;
411 		break;
412 	case MLO_LINK_SWITCH_STATE_COMPLETE_SUCCESS:
413 		next_state = MLO_LINK_SWITCH_STATE_IDLE;
414 		break;
415 	case MLO_LINK_SWITCH_STATE_ABORT_TRANS:
416 		next_state = cur_state;
417 		status = QDF_STATUS_E_PERM;
418 		mlo_debug("State transition not allowed");
419 		break;
420 	default:
421 		QDF_ASSERT(0);
422 		break;
423 	}
424 	mlo_dev_ctx->link_ctx->last_req.state = next_state;
425 	mlo_dev_lock_release(mlo_dev_ctx);
426 
427 	return status;
428 }
429 
430 void
431 mlo_mgr_link_switch_trans_abort_state(struct wlan_mlo_dev_context *mlo_dev_ctx)
432 {
433 	enum mlo_link_switch_req_state next_state =
434 					MLO_LINK_SWITCH_STATE_ABORT_TRANS;
435 
436 	mlo_dev_lock_acquire(mlo_dev_ctx);
437 	mlo_dev_ctx->link_ctx->last_req.state = next_state;
438 	mlo_dev_lock_release(mlo_dev_ctx);
439 }
440 
441 enum mlo_link_switch_req_state
442 mlo_mgr_link_switch_get_curr_state(struct wlan_mlo_dev_context *mlo_dev_ctx)
443 {
444 	enum mlo_link_switch_req_state state;
445 
446 	mlo_dev_lock_acquire(mlo_dev_ctx);
447 	state = mlo_dev_ctx->link_ctx->last_req.state;
448 	mlo_dev_lock_release(mlo_dev_ctx);
449 
450 	return state;
451 }
452 
453 #ifdef WLAN_FEATURE_11BE_MLO_ADV_FEATURE
454 static void
455 mlo_mgr_reset_roam_state_for_link_vdev(struct wlan_objmgr_vdev *vdev,
456 				       struct wlan_objmgr_vdev *assoc_vdev)
457 {
458 	QDF_STATUS status;
459 
460 	status = wlan_cm_roam_state_change(wlan_vdev_get_pdev(vdev),
461 					   wlan_vdev_get_id(assoc_vdev),
462 					   WLAN_ROAM_DEINIT,
463 					   REASON_ROAM_LINK_SWITCH_ASSOC_VDEV_CHANGE);
464 	if (QDF_IS_STATUS_ERROR(status))
465 		mlo_err("vdev:%d failed to change RSO state to deinit",
466 			wlan_vdev_get_id(assoc_vdev));
467 }
468 
469 static void
470 mlo_mgr_restore_rso_upon_link_switch_failure(struct wlan_objmgr_vdev *vdev)
471 {
472 	wlan_cm_roam_state_change(wlan_vdev_get_pdev(vdev),
473 				  wlan_vdev_get_id(vdev),
474 				  WLAN_ROAM_RSO_ENABLED,
475 				  REASON_CONNECT);
476 }
477 #else
478 static inline void
479 mlo_mgr_reset_roam_state_for_link_vdev(struct wlan_objmgr_vdev *vdev,
480 				       struct wlan_objmgr_vdev *assoc_vdev)
481 {}
482 
483 static inline void
484 mlo_mgr_restore_rso_upon_link_switch_failure(struct wlan_objmgr_vdev *vdev)
485 {}
486 #endif
487 
488 static QDF_STATUS
489 mlo_mgr_link_switch_osif_notification(struct wlan_objmgr_vdev *vdev,
490 				      struct wlan_mlo_link_switch_req *lswitch_req)
491 {
492 	uint8_t idx;
493 	uint16_t vdev_count;
494 	struct wlan_objmgr_vdev *assoc_vdev;
495 	struct wlan_mlo_sta *sta_ctx;
496 	struct wlan_objmgr_vdev *vdev_list[WLAN_UMAC_MLO_MAX_VDEVS];
497 	QDF_STATUS status = QDF_STATUS_E_INVAL;
498 	struct mlo_mgr_context *g_mlo_ctx = wlan_objmgr_get_mlo_ctx();
499 	QDF_STATUS(*cb)(struct wlan_objmgr_vdev *vdev,
500 			uint8_t non_trans_vdev_id);
501 
502 	if (!vdev->mlo_dev_ctx)
503 		return status;
504 
505 	sta_ctx = vdev->mlo_dev_ctx->sta_ctx;
506 	if (!sta_ctx)
507 		return status;
508 
509 	assoc_vdev = wlan_mlo_get_assoc_link_vdev(vdev);
510 	if (!assoc_vdev)
511 		return status;
512 
513 	cb = g_mlo_ctx->osif_ops->mlo_mgr_osif_link_switch_notification;
514 
515 	if (lswitch_req->restore_vdev_flag) {
516 		wlan_vdev_mlme_clear_mlo_link_vdev(vdev);
517 		wlan_vdev_mlme_set_mlo_link_vdev(assoc_vdev);
518 		mlo_mgr_reset_roam_state_for_link_vdev(vdev, assoc_vdev);
519 
520 		lswitch_req->restore_vdev_flag = false;
521 
522 		status = cb(assoc_vdev, wlan_vdev_get_id(vdev));
523 		if (QDF_IS_STATUS_ERROR(status))
524 			mlo_debug("OSIF deflink restore failed");
525 
526 		return status;
527 	}
528 
529 	if (wlan_vdev_get_id(assoc_vdev) != lswitch_req->vdev_id) {
530 		mlo_debug("Not on assoc VDEV no need to swap");
531 		return QDF_STATUS_SUCCESS;
532 	}
533 
534 	mlo_sta_get_vdev_list(vdev, &vdev_count, vdev_list);
535 	for (idx = 0; idx < vdev_count; idx++) {
536 		if (wlan_vdev_get_id(vdev_list[idx]) != lswitch_req->vdev_id &&
537 		    qdf_test_bit(idx, sta_ctx->wlan_connected_links)) {
538 			wlan_vdev_mlme_clear_mlo_link_vdev(vdev_list[idx]);
539 			wlan_vdev_mlme_set_mlo_link_vdev(assoc_vdev);
540 			lswitch_req->restore_vdev_flag = true;
541 
542 			status = cb(assoc_vdev,
543 				    wlan_vdev_get_id(vdev_list[idx]));
544 			break;
545 		}
546 
547 		mlo_release_vdev_ref(vdev_list[idx]);
548 	}
549 
550 	for (; idx < vdev_count; idx++)
551 		mlo_release_vdev_ref(vdev_list[idx]);
552 
553 	return status;
554 }
555 
556 QDF_STATUS
557 mlo_mgr_link_switch_notification(struct wlan_objmgr_vdev *vdev,
558 				 struct wlan_mlo_link_switch_req *lswitch_req,
559 				 enum wlan_mlo_link_switch_notify_reason notify_reason)
560 {
561 	QDF_STATUS status;
562 
563 	if ((notify_reason == MLO_LINK_SWITCH_NOTIFY_REASON_PRE_START_PRE_SER ||
564 	     notify_reason ==
565 		MLO_LINK_SWITCH_NOTIFY_REASON_PRE_START_POST_SER) &&
566 		mlo_is_chan_switch_in_progress(vdev)) {
567 		mlo_debug("CSA is in progress on one of ML vdevs, abort link switch");
568 		return QDF_STATUS_E_AGAIN;
569 	}
570 
571 	if (notify_reason == MLO_LINK_SWITCH_NOTIFY_REASON_PRE_START_PRE_SER)
572 		return QDF_STATUS_SUCCESS;
573 
574 	status = mlo_mgr_link_switch_osif_notification(vdev, lswitch_req);
575 	return status;
576 }
577 
578 QDF_STATUS mlo_mgr_link_switch_init(struct wlan_objmgr_psoc *psoc,
579 				    struct wlan_mlo_dev_context *ml_dev)
580 {
581 	ml_dev->link_ctx =
582 		qdf_mem_malloc(sizeof(struct mlo_link_switch_context));
583 
584 	if (!ml_dev->link_ctx)
585 		return QDF_STATUS_E_NOMEM;
586 
587 	mlo_mgr_link_switch_init_state(ml_dev);
588 	mlo_mgr_alloc_link_info_wmi_chan(ml_dev);
589 	mlo_mgr_update_link_info_reset(psoc, ml_dev);
590 
591 	return QDF_STATUS_SUCCESS;
592 }
593 
594 QDF_STATUS mlo_mgr_link_switch_deinit(struct wlan_mlo_dev_context *ml_dev)
595 {
596 	mlo_mgr_free_link_info_wmi_chan(ml_dev);
597 	qdf_mem_free(ml_dev->link_ctx);
598 	ml_dev->link_ctx = NULL;
599 	return QDF_STATUS_SUCCESS;
600 }
601 
602 void
603 mlo_mgr_osif_update_connect_info(struct wlan_objmgr_vdev *vdev, int32_t link_id)
604 {
605 	struct mlo_mgr_context *g_mlo_ctx = wlan_objmgr_get_mlo_ctx();
606 	struct mlo_link_info *link_info;
607 	QDF_STATUS(*osif_bss_update_cb)(struct qdf_mac_addr *self_mac,
608 					struct qdf_mac_addr *bssid,
609 					int32_t link_id);
610 
611 	if (!g_mlo_ctx || !vdev->mlo_dev_ctx || !g_mlo_ctx->osif_ops ||
612 	    !g_mlo_ctx->osif_ops->mlo_mgr_osif_update_bss_info)
613 		return;
614 
615 	link_info = mlo_mgr_get_ap_link_by_link_id(vdev->mlo_dev_ctx, link_id);
616 	if (!link_info)
617 		return;
618 
619 	mlo_debug("VDEV ID %d, Link ID %d, STA MAC " QDF_MAC_ADDR_FMT ", BSSID " QDF_MAC_ADDR_FMT,
620 		  link_info->vdev_id, link_id,
621 		  QDF_MAC_ADDR_REF(link_info->link_addr.bytes),
622 		  QDF_MAC_ADDR_REF(link_info->ap_link_addr.bytes));
623 	osif_bss_update_cb = g_mlo_ctx->osif_ops->mlo_mgr_osif_update_bss_info;
624 
625 	osif_bss_update_cb(&link_info->link_addr, &link_info->ap_link_addr,
626 			   link_id);
627 }
628 
629 QDF_STATUS mlo_mgr_link_switch_disconnect_done(struct wlan_objmgr_vdev *vdev,
630 					       QDF_STATUS discon_status,
631 					       bool is_link_switch_resp)
632 {
633 	QDF_STATUS status;
634 	enum mlo_link_switch_req_state cur_state;
635 	struct mlo_link_info *new_link_info;
636 	struct qdf_mac_addr mac_addr, mld_addr;
637 	struct wlan_mlo_dev_context *mlo_dev_ctx = vdev->mlo_dev_ctx;
638 	struct wlan_mlo_link_switch_req *req = &mlo_dev_ctx->link_ctx->last_req;
639 
640 	if (!is_link_switch_resp) {
641 		mlo_mgr_link_switch_trans_abort_state(mlo_dev_ctx);
642 		return QDF_STATUS_SUCCESS;
643 	}
644 
645 	cur_state = mlo_mgr_link_switch_get_curr_state(mlo_dev_ctx);
646 	if (QDF_IS_STATUS_ERROR(discon_status) ||
647 	    cur_state != MLO_LINK_SWITCH_STATE_DISCONNECT_CURR_LINK) {
648 		mlo_err("VDEV %d link switch disconnect req failed",
649 			req->vdev_id);
650 		mlo_mgr_remove_link_switch_cmd(vdev);
651 		return QDF_STATUS_SUCCESS;
652 	}
653 
654 	mlo_debug("VDEV %d link switch disconnect complete",
655 		  wlan_vdev_get_id(vdev));
656 
657 	new_link_info =
658 		mlo_mgr_get_ap_link_by_link_id(vdev->mlo_dev_ctx,
659 					       req->new_ieee_link_id);
660 	if (!new_link_info) {
661 		mlo_err("New link not found in mlo dev ctx");
662 		mlo_mgr_remove_link_switch_cmd(vdev);
663 		return QDF_STATUS_E_INVAL;
664 	}
665 
666 	qdf_copy_macaddr(&mld_addr, &mlo_dev_ctx->mld_addr);
667 	qdf_copy_macaddr(&mac_addr, &new_link_info->link_addr);
668 
669 	status = mlo_mgr_link_switch_trans_next_state(mlo_dev_ctx);
670 	if (QDF_IS_STATUS_ERROR(status)) {
671 		mlo_mgr_remove_link_switch_cmd(vdev);
672 		return status;
673 	}
674 
675 	status = wlan_vdev_mlme_send_set_mac_addr(mac_addr, mld_addr, vdev);
676 	if (QDF_IS_STATUS_ERROR(status)) {
677 		mlo_debug("VDEV %d set MAC addr FW request failed",
678 			  req->vdev_id);
679 		mlo_mgr_remove_link_switch_cmd(vdev);
680 	}
681 
682 	return status;
683 }
684 
685 QDF_STATUS mlo_mgr_link_switch_set_mac_addr_resp(struct wlan_objmgr_vdev *vdev,
686 						 uint8_t resp_status)
687 {
688 	QDF_STATUS status = QDF_STATUS_E_INVAL;
689 	enum mlo_link_switch_req_state cur_state;
690 	struct mlo_mgr_context *g_mlo_ctx = wlan_objmgr_get_mlo_ctx();
691 	struct wlan_mlo_link_switch_req *req;
692 	struct mlo_link_info *new_link_info;
693 
694 	if (resp_status) {
695 		mlo_err("VDEV %d set MAC address response %d",
696 			wlan_vdev_get_id(vdev), resp_status);
697 		mlo_mgr_remove_link_switch_cmd(vdev);
698 		return status;
699 	}
700 
701 	if (!g_mlo_ctx) {
702 		mlo_err("global mlo ctx NULL");
703 		mlo_mgr_remove_link_switch_cmd(vdev);
704 		return status;
705 	}
706 
707 	req = &vdev->mlo_dev_ctx->link_ctx->last_req;
708 	cur_state = mlo_mgr_link_switch_get_curr_state(vdev->mlo_dev_ctx);
709 	if (cur_state != MLO_LINK_SWITCH_STATE_SET_MAC_ADDR) {
710 		mlo_err("Link switch cmd flushed, there can be MAC addr mismatch with FW");
711 		mlo_mgr_remove_link_switch_cmd(vdev);
712 		return status;
713 	}
714 
715 	new_link_info =
716 		mlo_mgr_get_ap_link_by_link_id(vdev->mlo_dev_ctx,
717 					       req->new_ieee_link_id);
718 	if (!new_link_info) {
719 		mlo_mgr_remove_link_switch_cmd(vdev);
720 		return status;
721 	}
722 
723 	wlan_vdev_mlme_set_macaddr(vdev, new_link_info->link_addr.bytes);
724 	wlan_vdev_mlme_set_linkaddr(vdev, new_link_info->link_addr.bytes);
725 
726 	status = g_mlo_ctx->osif_ops->mlo_mgr_osif_update_mac_addr(
727 							req->curr_ieee_link_id,
728 							req->new_ieee_link_id,
729 							req->vdev_id);
730 	if (QDF_IS_STATUS_ERROR(status)) {
731 		mlo_debug("VDEV %d OSIF MAC addr update failed %d",
732 			  req->vdev_id, status);
733 		mlo_mgr_remove_link_switch_cmd(vdev);
734 		return status;
735 	}
736 
737 	status = mlo_mgr_link_switch_trans_next_state(vdev->mlo_dev_ctx);
738 	if (QDF_IS_STATUS_ERROR(status)) {
739 		mlo_mgr_remove_link_switch_cmd(vdev);
740 		return status;
741 	}
742 
743 	status = mlo_mgr_link_switch_start_connect(vdev);
744 
745 	return status;
746 }
747 
748 QDF_STATUS mlo_mgr_link_switch_start_connect(struct wlan_objmgr_vdev *vdev)
749 {
750 	QDF_STATUS status = QDF_STATUS_E_INVAL;
751 	struct wlan_cm_connect_req conn_req = {0};
752 	struct mlo_link_info *mlo_link_info;
753 	uint8_t *vdev_mac;
754 	struct wlan_mlo_sta *sta_ctx;
755 	struct wlan_mlo_dev_context *mlo_dev_ctx = vdev->mlo_dev_ctx;
756 	struct wlan_mlo_link_switch_req *req = &mlo_dev_ctx->link_ctx->last_req;
757 	struct wlan_objmgr_vdev *assoc_vdev = wlan_mlo_get_assoc_link_vdev(vdev);
758 
759 	if (!assoc_vdev) {
760 		mlo_err("Assoc VDEV not found");
761 		goto out;
762 	}
763 
764 	mlo_link_info = mlo_mgr_get_ap_link_by_link_id(mlo_dev_ctx,
765 						       req->new_ieee_link_id);
766 
767 	if (!mlo_link_info) {
768 		mlo_err("New link ID not found");
769 		goto out;
770 	}
771 
772 	vdev_mac = wlan_vdev_mlme_get_linkaddr(vdev);
773 	if (!qdf_is_macaddr_equal(&mlo_link_info->link_addr,
774 				  (struct qdf_mac_addr *)vdev_mac)) {
775 		mlo_err("MAC address not equal for the new Link ID VDEV: " QDF_MAC_ADDR_FMT ", MLO_LINK: " QDF_MAC_ADDR_FMT,
776 			QDF_MAC_ADDR_REF(vdev_mac),
777 			QDF_MAC_ADDR_REF(mlo_link_info->link_addr.bytes));
778 		goto out;
779 	}
780 
781 	sta_ctx = mlo_dev_ctx->sta_ctx;
782 	copied_conn_req_lock_acquire(sta_ctx);
783 	if (sta_ctx->copied_conn_req) {
784 		qdf_mem_copy(&conn_req, sta_ctx->copied_conn_req,
785 			     sizeof(struct wlan_cm_connect_req));
786 	} else {
787 		copied_conn_req_lock_release(sta_ctx);
788 		goto out;
789 	}
790 	copied_conn_req_lock_release(sta_ctx);
791 
792 	conn_req.vdev_id = wlan_vdev_get_id(vdev);
793 	conn_req.source = CM_MLO_LINK_SWITCH_CONNECT;
794 	wlan_vdev_set_link_id(vdev, req->new_ieee_link_id);
795 
796 	qdf_copy_macaddr(&conn_req.bssid, &mlo_link_info->ap_link_addr);
797 	wlan_vdev_mlme_get_ssid(assoc_vdev, conn_req.ssid.ssid,
798 				&conn_req.ssid.length);
799 	status = wlan_vdev_get_bss_peer_mld_mac(assoc_vdev, &conn_req.mld_addr);
800 	if (QDF_IS_STATUS_ERROR(status)) {
801 		mlo_debug("Get MLD addr failed");
802 		goto out;
803 	}
804 
805 	conn_req.crypto.auth_type = 0;
806 	conn_req.ml_parnter_info = sta_ctx->ml_partner_info;
807 	mlo_allocate_and_copy_ies(&conn_req, sta_ctx->copied_conn_req);
808 
809 	status = wlan_cm_start_connect(vdev, &conn_req);
810 	if (QDF_IS_STATUS_SUCCESS(status))
811 		mlo_update_connected_links(vdev, 1);
812 
813 	wlan_cm_free_connect_req_param(&conn_req);
814 
815 out:
816 	if (QDF_IS_STATUS_ERROR(status)) {
817 		mlo_err("VDEV %d link switch connect request failed",
818 			wlan_vdev_get_id(vdev));
819 		mlo_mgr_remove_link_switch_cmd(vdev);
820 	}
821 
822 	return status;
823 }
824 
825 static void
826 mlo_mgr_link_switch_connect_success_trans_state(struct wlan_objmgr_vdev *vdev)
827 {
828 	enum mlo_link_switch_req_state curr_state;
829 
830 	/*
831 	 * If connection is success, then sending link switch failure to FW
832 	 * might result in not updating VDEV to link mapping in FW and FW may
833 	 * immediately send next link switch with params corresponding to
834 	 * pre-link switch which may vary post-link switch in host and might
835 	 * not be valid and results in Host-FW out-of-sync.
836 	 *
837 	 * Force the result of link switch in align with link switch connect
838 	 * so that Host and FW are not out of sync.
839 	 */
840 	mlo_dev_lock_acquire(vdev->mlo_dev_ctx);
841 	curr_state = vdev->mlo_dev_ctx->link_ctx->last_req.state;
842 	vdev->mlo_dev_ctx->link_ctx->last_req.state =
843 					MLO_LINK_SWITCH_STATE_COMPLETE_SUCCESS;
844 	mlo_dev_lock_release(vdev->mlo_dev_ctx);
845 
846 	if (curr_state != MLO_LINK_SWITCH_STATE_CONNECT_NEW_LINK)
847 		mlo_debug("Current link switch state %d changed", curr_state);
848 }
849 
850 void mlo_mgr_link_switch_connect_done(struct wlan_objmgr_vdev *vdev,
851 				      QDF_STATUS status)
852 {
853 	struct wlan_mlo_link_switch_req *req;
854 
855 	req = &vdev->mlo_dev_ctx->link_ctx->last_req;
856 	if (QDF_IS_STATUS_SUCCESS(status)) {
857 		mlo_mgr_link_switch_connect_success_trans_state(vdev);
858 	} else {
859 		mlo_update_connected_links(vdev, 0);
860 		mlo_err("VDEV %d link switch connect failed", req->vdev_id);
861 	}
862 
863 	mlo_mgr_remove_link_switch_cmd(vdev);
864 
865 	if (QDF_IS_STATUS_ERROR(status))
866 		mlo_mgr_restore_rso_upon_link_switch_failure(
867 				wlan_mlo_get_assoc_link_vdev(vdev));
868 }
869 
870 static enum wlan_mlo_link_switch_notify_reason
871 mlo_mgr_link_switch_get_notify_reason(struct wlan_objmgr_vdev *vdev)
872 {
873 	enum mlo_link_switch_req_state curr_state;
874 	enum wlan_mlo_link_switch_notify_reason notify_reason;
875 
876 	curr_state = mlo_mgr_link_switch_get_curr_state(vdev->mlo_dev_ctx);
877 	switch (curr_state) {
878 	case MLO_LINK_SWITCH_STATE_IDLE:
879 		notify_reason = MLO_LINK_SWITCH_NOTIFY_REASON_PRE_START_PRE_SER;
880 		break;
881 	case MLO_LINK_SWITCH_STATE_INIT:
882 		notify_reason =
883 			MLO_LINK_SWITCH_NOTIFY_REASON_PRE_START_POST_SER;
884 		break;
885 	case MLO_LINK_SWITCH_STATE_COMPLETE_SUCCESS:
886 		notify_reason = MLO_LINK_SWITCH_NOTIFY_REASON_STOP_SUCCESS;
887 		break;
888 	default:
889 		notify_reason = MLO_LINK_SWITCH_NOTIFY_REASON_STOP_FAILURE;
890 		break;
891 	}
892 
893 	return notify_reason;
894 }
895 
896 static QDF_STATUS
897 mlo_mgr_start_link_switch(struct wlan_objmgr_vdev *vdev,
898 			  struct wlan_serialization_command *cmd)
899 {
900 	QDF_STATUS status = QDF_STATUS_E_INVAL;
901 	uint8_t vdev_id, old_link_id, new_link_id;
902 	struct wlan_mlo_dev_context *mlo_dev_ctx = vdev->mlo_dev_ctx;
903 	struct wlan_mlo_link_switch_req *req = &mlo_dev_ctx->link_ctx->last_req;
904 	struct qdf_mac_addr bssid;
905 
906 	vdev_id = wlan_vdev_get_id(vdev);
907 	old_link_id = req->curr_ieee_link_id;
908 	new_link_id = req->new_ieee_link_id;
909 
910 	mlo_debug("VDEV %d start link switch", vdev_id);
911 	mlo_mgr_link_switch_trans_next_state(mlo_dev_ctx);
912 
913 	if (!wlan_cm_is_vdev_connected(vdev)) {
914 		mlo_err("VDEV %d not in connected state", vdev_id);
915 		return status;
916 	}
917 
918 	status = wlan_vdev_get_bss_peer_mac(vdev, &bssid);
919 	if (QDF_IS_STATUS_ERROR(status))
920 		return status;
921 
922 	status = wlan_vdev_get_bss_peer_mld_mac(vdev, &req->peer_mld_addr);
923 	if (QDF_IS_STATUS_ERROR(status))
924 		return status;
925 
926 	status = mlo_mgr_link_switch_notify(vdev, req);
927 	if (QDF_IS_STATUS_ERROR(status))
928 		return status;
929 
930 	wlan_vdev_mlme_set_mlo_link_switch_in_progress(vdev);
931 	status = mlo_mgr_link_switch_trans_next_state(mlo_dev_ctx);
932 	if (QDF_IS_STATUS_ERROR(status))
933 		return status;
934 
935 	status = wlan_cm_disconnect(vdev, CM_MLO_LINK_SWITCH_DISCONNECT,
936 				    REASON_FW_TRIGGERED_LINK_SWITCH, &bssid);
937 
938 	if (QDF_IS_STATUS_ERROR(status))
939 		mlo_err("VDEV %d disconnect request not handled", req->vdev_id);
940 
941 	return status;
942 }
943 
944 static QDF_STATUS
945 mlo_mgr_ser_link_switch_cb(struct wlan_serialization_command *cmd,
946 			   enum wlan_serialization_cb_reason cb_reason)
947 {
948 	struct wlan_objmgr_vdev *vdev;
949 	QDF_STATUS status = QDF_STATUS_SUCCESS;
950 	struct wlan_mlo_link_switch_req *req;
951 	enum qdf_hang_reason reason = QDF_VDEV_ACTIVE_SER_LINK_SWITCH_TIMEOUT;
952 
953 	if (!cmd) {
954 		mlo_err("cmd is NULL, reason: %d", cb_reason);
955 		QDF_ASSERT(0);
956 		return QDF_STATUS_E_NULL_VALUE;
957 	}
958 
959 	vdev = cmd->vdev;
960 	req = &vdev->mlo_dev_ctx->link_ctx->last_req;
961 
962 	switch (cb_reason) {
963 	case WLAN_SER_CB_ACTIVATE_CMD:
964 		status = mlo_mgr_start_link_switch(vdev, cmd);
965 		if (QDF_IS_STATUS_ERROR(status)) {
966 			mlo_mgr_link_switch_trans_abort_state(vdev->mlo_dev_ctx);
967 			mlo_mgr_link_switch_notify(vdev, req);
968 		}
969 		break;
970 	case WLAN_SER_CB_RELEASE_MEM_CMD:
971 		mlo_mgr_link_switch_complete(vdev);
972 		break;
973 	case WLAN_SER_CB_CANCEL_CMD:
974 		mlo_err("Link switch cmd cancelled");
975 		break;
976 	case WLAN_SER_CB_ACTIVE_CMD_TIMEOUT:
977 		mlo_err("Link switch active cmd timeout");
978 		wlan_cm_trigger_panic_on_cmd_timeout(vdev, reason);
979 		break;
980 	default:
981 		QDF_ASSERT(0);
982 		mlo_mgr_link_switch_complete(vdev);
983 		break;
984 	}
985 
986 	return status;
987 }
988 
989 void mlo_mgr_remove_link_switch_cmd(struct wlan_objmgr_vdev *vdev)
990 {
991 	struct wlan_serialization_queued_cmd_info cmd_info;
992 	enum mlo_link_switch_req_state cur_state;
993 	uint8_t vdev_id = wlan_vdev_get_id(vdev);
994 	struct wlan_mlo_link_switch_req *req;
995 
996 	cur_state = mlo_mgr_link_switch_get_curr_state(vdev->mlo_dev_ctx);
997 	if (cur_state == MLO_LINK_SWITCH_STATE_IDLE)
998 		return;
999 
1000 	req = &vdev->mlo_dev_ctx->link_ctx->last_req;
1001 	mlo_mgr_link_switch_notify(vdev, req);
1002 
1003 	/* Handle any pending disconnect */
1004 	mlo_handle_pending_disconnect(vdev);
1005 
1006 	if (req->reason == MLO_LINK_SWITCH_REASON_HOST_FORCE) {
1007 		mlo_debug("Link switch not serialized");
1008 		mlo_mgr_link_switch_complete(vdev);
1009 		return;
1010 	}
1011 
1012 	cmd_info.cmd_id = (vdev_id << 16) + (req->new_ieee_link_id << 8) +
1013 			  (req->curr_ieee_link_id);
1014 	cmd_info.req_type = WLAN_SER_CANCEL_NON_SCAN_CMD;
1015 	cmd_info.cmd_type = WLAN_SER_CMD_MLO_VDEV_LINK_SWITCH;
1016 	cmd_info.vdev = vdev;
1017 	cmd_info.queue_type = WLAN_SERIALIZATION_ACTIVE_QUEUE;
1018 
1019 	wlan_serialization_remove_cmd(&cmd_info);
1020 }
1021 
1022 #define MLO_MGR_MAX_LSWITCH_TIMEOUT	35000
1023 
1024 QDF_STATUS mlo_mgr_ser_link_switch_cmd(struct wlan_objmgr_vdev *vdev,
1025 				       struct wlan_mlo_link_switch_req *req)
1026 {
1027 	QDF_STATUS status;
1028 	enum wlan_serialization_status ser_cmd_status;
1029 	struct wlan_serialization_command cmd = {0};
1030 	uint8_t vdev_id = wlan_vdev_get_id(vdev);
1031 	struct mlo_link_switch_context *link_ctx;
1032 
1033 	if (!vdev->mlo_dev_ctx) {
1034 		mlo_err("ML dev ctx NULL, reject link switch");
1035 		return QDF_STATUS_E_INVAL;
1036 	}
1037 
1038 	link_ctx = vdev->mlo_dev_ctx->link_ctx;
1039 	link_ctx->last_req = *req;
1040 
1041 	cmd.cmd_type = WLAN_SER_CMD_MLO_VDEV_LINK_SWITCH;
1042 	cmd.cmd_id = (vdev_id << 16) + (req->new_ieee_link_id << 8) +
1043 		     (req->curr_ieee_link_id);
1044 	cmd.cmd_cb = mlo_mgr_ser_link_switch_cb;
1045 	cmd.source = WLAN_UMAC_COMP_MLO_MGR;
1046 	cmd.is_high_priority = false;
1047 	cmd.cmd_timeout_duration = MLO_MGR_MAX_LSWITCH_TIMEOUT;
1048 	cmd.vdev = vdev;
1049 	cmd.is_blocking = true;
1050 
1051 	if (req->reason == MLO_LINK_SWITCH_REASON_HOST_FORCE) {
1052 		mlo_debug("Do not serialize link switch");
1053 		status = mlo_mgr_start_link_switch(vdev, &cmd);
1054 		if (QDF_IS_STATUS_ERROR(status)) {
1055 			mlo_mgr_link_switch_trans_abort_state(vdev->mlo_dev_ctx);
1056 			mlo_mgr_link_switch_notify(vdev, req);
1057 		}
1058 		return status;
1059 	}
1060 
1061 	ser_cmd_status = wlan_serialization_request(&cmd);
1062 	switch (ser_cmd_status) {
1063 	case WLAN_SER_CMD_PENDING:
1064 		mlo_debug("Link switch cmd in pending queue");
1065 		break;
1066 	case WLAN_SER_CMD_ACTIVE:
1067 		mlo_debug("Link switch cmd in active queue");
1068 		break;
1069 	default:
1070 		return QDF_STATUS_E_INVAL;
1071 	}
1072 
1073 	return QDF_STATUS_SUCCESS;
1074 }
1075 
1076 QDF_STATUS mlo_mgr_link_switch_notify(struct wlan_objmgr_vdev *vdev,
1077 				      struct wlan_mlo_link_switch_req *req)
1078 {
1079 	int8_t i;
1080 	QDF_STATUS status, ret_status = QDF_STATUS_SUCCESS;
1081 	enum wlan_mlo_link_switch_notify_reason notify_reason;
1082 	struct mlo_mgr_context *mlo_mgr_ctx = wlan_objmgr_get_mlo_ctx();
1083 
1084 	if (!mlo_mgr_ctx) {
1085 		mlo_err("Global mlo mgr NULL");
1086 		return QDF_STATUS_E_NULL_VALUE;
1087 	}
1088 
1089 	notify_reason = mlo_mgr_link_switch_get_notify_reason(vdev);
1090 	for (i = 0; i < WLAN_UMAC_COMP_ID_MAX; i++) {
1091 		if (!mlo_mgr_ctx->lswitch_notifier[i].in_use)
1092 			continue;
1093 
1094 		status = mlo_mgr_ctx->lswitch_notifier[i].cb(vdev, req,
1095 							     notify_reason);
1096 		if (QDF_IS_STATUS_SUCCESS(status))
1097 			continue;
1098 
1099 		mlo_debug("Link switch notify %d failed in %d",
1100 			  notify_reason, i);
1101 		ret_status = status;
1102 		if (notify_reason == MLO_LINK_SWITCH_NOTIFY_REASON_PRE_START_PRE_SER)
1103 			break;
1104 	}
1105 
1106 	return ret_status;
1107 }
1108 
1109 QDF_STATUS
1110 mlo_mgr_link_switch_validate_request(struct wlan_objmgr_vdev *vdev,
1111 				     struct wlan_mlo_link_switch_req *req)
1112 {
1113 	QDF_STATUS status = QDF_STATUS_E_INVAL;
1114 	uint8_t vdev_id = wlan_vdev_get_id(vdev);
1115 	struct mlo_link_info *new_link_info;
1116 
1117 	if (req->curr_ieee_link_id >= WLAN_INVALID_LINK_ID ||
1118 	    req->new_ieee_link_id >= WLAN_INVALID_LINK_ID) {
1119 		mlo_err("Invalid link params, curr link id %d, new link id %d",
1120 			req->curr_ieee_link_id, req->new_ieee_link_id);
1121 		return status;
1122 	}
1123 
1124 	new_link_info = mlo_mgr_get_ap_link_by_link_id(vdev->mlo_dev_ctx,
1125 						       req->new_ieee_link_id);
1126 	if (!new_link_info) {
1127 		mlo_err("New link id %d not part of association",
1128 			req->new_ieee_link_id);
1129 		return status;
1130 	}
1131 
1132 	if (new_link_info->vdev_id != WLAN_INVALID_VDEV_ID) {
1133 		mlo_err("requested link already active on other vdev:%d",
1134 			new_link_info->vdev_id);
1135 		return status;
1136 	}
1137 
1138 	if (!mlo_is_mld_sta(vdev)) {
1139 		mlo_err("Link switch req not valid for VDEV %d", vdev_id);
1140 		return status;
1141 	}
1142 
1143 	if (!wlan_cm_is_vdev_connected(vdev)) {
1144 		mlo_err("VDEV %d not in connected state", vdev_id);
1145 		return status;
1146 	}
1147 
1148 	if (mlo_mgr_is_link_switch_in_progress(vdev)) {
1149 		mlo_err("Link switch already in progress");
1150 		return status;
1151 	}
1152 
1153 	if (wlan_vdev_get_link_id(vdev) != req->curr_ieee_link_id) {
1154 		mlo_err("VDEV %d link id wrong, curr link id %d",
1155 			vdev_id, wlan_vdev_get_link_id(vdev));
1156 		return status;
1157 	}
1158 
1159 	/* Notify callers on the new link switch request before serializing */
1160 	status = mlo_mgr_link_switch_notify(vdev, req);
1161 	if (QDF_IS_STATUS_ERROR(status)) {
1162 		mlo_err("Link switch rejected in pre-serialize notify");
1163 		return status;
1164 	}
1165 
1166 	return QDF_STATUS_SUCCESS;
1167 }
1168 
1169 QDF_STATUS mlo_mgr_link_switch_request_params(struct wlan_objmgr_psoc *psoc,
1170 					      void *evt_params)
1171 {
1172 	QDF_STATUS status;
1173 	struct wlan_mlo_link_switch_cnf cnf_params = {0};
1174 	struct wlan_mlo_link_switch_req *req;
1175 	struct wlan_objmgr_vdev *vdev;
1176 
1177 	if (!evt_params) {
1178 		mlo_err("Invalid params");
1179 		return QDF_STATUS_E_INVAL;
1180 	}
1181 
1182 	req = (struct wlan_mlo_link_switch_req *)evt_params;
1183 
1184 	/* The reference is released on Link Switch status confirm to FW */
1185 	vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, req->vdev_id,
1186 						    WLAN_MLO_MGR_ID);
1187 	if (!vdev) {
1188 		mlo_err("Invalid link switch VDEV %d", req->vdev_id);
1189 
1190 		/* Fill reject params here and send to FW as VDEV is invalid */
1191 		cnf_params.vdev_id = req->vdev_id;
1192 		cnf_params.status = MLO_LINK_SWITCH_CNF_STATUS_REJECT;
1193 		mlo_mgr_link_switch_send_cnf_cmd(psoc, &cnf_params);
1194 		return QDF_STATUS_E_INVAL;
1195 	}
1196 
1197 	mlo_debug("VDEV %d, curr_link_id %d, new_link_id %d, new_freq %d, new_phymode: %d, reason %d",
1198 		  req->vdev_id, req->curr_ieee_link_id, req->new_ieee_link_id,
1199 		  req->new_primary_freq, req->new_phymode, req->reason);
1200 
1201 	status = mlo_mgr_link_switch_validate_request(vdev, req);
1202 	if (QDF_IS_STATUS_ERROR(status)) {
1203 		mlo_debug("Link switch params/request invalid");
1204 		mlo_mgr_link_switch_complete(vdev);
1205 		return QDF_STATUS_E_INVAL;
1206 	}
1207 
1208 	status = mlo_mgr_ser_link_switch_cmd(vdev, req);
1209 	if (QDF_IS_STATUS_ERROR(status)) {
1210 		mlo_err("Failed to serialize link switch command");
1211 		mlo_mgr_link_switch_complete(vdev);
1212 	}
1213 
1214 	return status;
1215 }
1216 
1217 QDF_STATUS
1218 mlo_mgr_link_state_switch_info_handler(struct wlan_objmgr_psoc *psoc,
1219 				       struct mlo_link_switch_state_info *info)
1220 {
1221 	uint8_t i;
1222 
1223 	for (i = 0; i < info->num_params; i++)
1224 		wlan_connectivity_mld_link_status_event(psoc,
1225 							&info->link_switch_param[i]);
1226 
1227 	return QDF_STATUS_SUCCESS;
1228 }
1229 
1230 QDF_STATUS mlo_mgr_link_switch_complete(struct wlan_objmgr_vdev *vdev)
1231 {
1232 	enum mlo_link_switch_req_state state;
1233 	struct wlan_mlo_link_switch_cnf params = {0};
1234 	struct mlo_link_switch_context *link_ctx;
1235 	struct wlan_mlo_link_switch_req *req;
1236 	struct wlan_objmgr_psoc *psoc;
1237 
1238 	/* Not checking NULL value as reference is already taken for vdev */
1239 	psoc = wlan_vdev_get_psoc(vdev);
1240 
1241 	link_ctx = vdev->mlo_dev_ctx->link_ctx;
1242 	req = &link_ctx->last_req;
1243 
1244 	state = mlo_mgr_link_switch_get_curr_state(vdev->mlo_dev_ctx);
1245 	if (state != MLO_LINK_SWITCH_STATE_COMPLETE_SUCCESS)
1246 		params.status = MLO_LINK_SWITCH_CNF_STATUS_REJECT;
1247 	else
1248 		params.status = MLO_LINK_SWITCH_CNF_STATUS_ACCEPT;
1249 
1250 	params.vdev_id = wlan_vdev_get_id(vdev);
1251 	params.reason = MLO_LINK_SWITCH_CNF_REASON_BSS_PARAMS_CHANGED;
1252 
1253 	mlo_mgr_link_switch_send_cnf_cmd(psoc, &params);
1254 
1255 	mlo_mgr_link_switch_init_state(vdev->mlo_dev_ctx);
1256 	wlan_vdev_mlme_clear_mlo_link_switch_in_progress(vdev);
1257 	wlan_objmgr_vdev_release_ref(vdev, WLAN_MLO_MGR_ID);
1258 	return QDF_STATUS_SUCCESS;
1259 }
1260 
1261 QDF_STATUS
1262 mlo_mgr_link_switch_send_cnf_cmd(struct wlan_objmgr_psoc *psoc,
1263 				 struct wlan_mlo_link_switch_cnf *cnf_params)
1264 {
1265 	QDF_STATUS status;
1266 	struct wlan_lmac_if_mlo_tx_ops *mlo_tx_ops;
1267 
1268 	mlo_debug("VDEV %d link switch completed, %s", cnf_params->vdev_id,
1269 		  (cnf_params->status == MLO_LINK_SWITCH_CNF_STATUS_ACCEPT) ?
1270 		  "success" : "fail");
1271 
1272 	mlo_tx_ops = &psoc->soc_cb.tx_ops->mlo_ops;
1273 	if (!mlo_tx_ops || !mlo_tx_ops->send_mlo_link_switch_cnf_cmd) {
1274 		mlo_err("handler is not registered");
1275 		return QDF_STATUS_E_INVAL;
1276 	}
1277 
1278 	status = mlo_tx_ops->send_mlo_link_switch_cnf_cmd(psoc, cnf_params);
1279 	if (QDF_IS_STATUS_ERROR(status))
1280 		mlo_err("Link switch status update to FW failed");
1281 
1282 	return status;
1283 }
1284 
1285 QDF_STATUS
1286 mlo_mgr_link_switch_defer_disconnect_req(struct wlan_objmgr_vdev *vdev,
1287 					 enum wlan_cm_source source,
1288 					 enum wlan_reason_code reason)
1289 {
1290 	struct wlan_mlo_dev_context *mlo_dev_ctx;
1291 	struct wlan_mlo_sta *sta_ctx;
1292 
1293 	if (!mlo_mgr_is_link_switch_in_progress(vdev)) {
1294 		mlo_info("Link switch not in progress");
1295 		return QDF_STATUS_E_INVAL;
1296 	}
1297 
1298 	mlo_dev_ctx = vdev->mlo_dev_ctx;
1299 	sta_ctx = mlo_dev_ctx->sta_ctx;
1300 
1301 	if (!sta_ctx) {
1302 		mlo_err("sta ctx null");
1303 		return QDF_STATUS_E_NULL_VALUE;
1304 	}
1305 
1306 	/* Move current link switch to abort state */
1307 	mlo_mgr_link_switch_trans_abort_state(mlo_dev_ctx);
1308 
1309 	if (sta_ctx->disconn_req) {
1310 		mlo_debug("Pending disconnect from source %d, reason %d",
1311 			  sta_ctx->disconn_req->source,
1312 			  sta_ctx->disconn_req->reason_code);
1313 		return QDF_STATUS_E_ALREADY;
1314 	}
1315 
1316 	sta_ctx->disconn_req =
1317 			qdf_mem_malloc(sizeof(struct wlan_cm_disconnect_req));
1318 	if (!sta_ctx->disconn_req)
1319 		return QDF_STATUS_E_NOMEM;
1320 
1321 	sta_ctx->disconn_req->vdev_id = wlan_vdev_get_id(vdev);
1322 	sta_ctx->disconn_req->source = source;
1323 	sta_ctx->disconn_req->reason_code = reason;
1324 
1325 	mlo_debug("Deferred disconnect source: %d, reason: %d", source, reason);
1326 	return QDF_STATUS_SUCCESS;
1327 }
1328 #endif
1329