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