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