1 /*
2  * Copyright (c) 2012-2015, 2020-2021, The Linux Foundation. All rights reserved.
3  * Copyright (c) 2023-2024 Qualcomm Innovation Center, Inc. All rights reserved.
4  *
5  * Permission to use, copy, modify, and/or distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 /**
19  * DOC: Implements legacy disconnect connect specific APIs of
20  * connection mgr to initiate vdev manager operations
21  */
22 
23 #include "wlan_cm_vdev_api.h"
24 #include "wlan_mlme_main.h"
25 #include "wlan_cm_api.h"
26 #include "wlan_p2p_api.h"
27 #include "wlan_tdls_api.h"
28 #include <wlan_policy_mgr_api.h>
29 #include <wlan_objmgr_psoc_obj.h>
30 #include <wlan_objmgr_pdev_obj.h>
31 #include <wlan_objmgr_vdev_obj.h>
32 #include <wlan_cm_roam_api.h>
33 #include "wni_api.h"
34 #include "connection_mgr/core/src/wlan_cm_roam.h"
35 #include <wlan_mlo_mgr_sta.h>
36 #include "wlan_mlo_mgr_roam.h"
37 #include "wlan_t2lm_api.h"
38 #include "wlan_mlo_link_force.h"
39 #include <wlan_mlo_mgr_public_api.h>
40 #include <wlan_cp_stats_chipset_stats.h>
41 
cm_abort_connect_request_timers(struct wlan_objmgr_vdev * vdev)42 static void cm_abort_connect_request_timers(struct wlan_objmgr_vdev *vdev)
43 {
44 	struct scheduler_msg msg;
45 	QDF_STATUS status;
46 
47 	qdf_mem_zero(&msg, sizeof(msg));
48 	msg.bodyval = wlan_vdev_get_id(vdev);
49 	msg.type = CM_ABORT_CONN_TIMER;
50 	status = scheduler_post_message(QDF_MODULE_ID_MLME,
51 					QDF_MODULE_ID_PE,
52 					QDF_MODULE_ID_PE, &msg);
53 	if (QDF_IS_STATUS_ERROR(status))
54 		mlme_debug("msg CM_ABORT_CONN_TIMER post fail");
55 }
56 
cm_disconnect_start_ind(struct wlan_objmgr_vdev * vdev,struct wlan_cm_disconnect_req * req)57 QDF_STATUS cm_disconnect_start_ind(struct wlan_objmgr_vdev *vdev,
58 				   struct wlan_cm_disconnect_req *req)
59 {
60 	struct wlan_objmgr_psoc *psoc;
61 	struct wlan_objmgr_pdev *pdev;
62 	bool user_disconnect;
63 
64 	if (!vdev || !req) {
65 		mlme_err("vdev or req is NULL");
66 		return QDF_STATUS_E_INVAL;
67 	}
68 
69 	pdev = wlan_vdev_get_pdev(vdev);
70 	if (!pdev) {
71 		mlme_err("vdev_id: %d pdev not found", req->vdev_id);
72 		return QDF_STATUS_E_INVAL;
73 	}
74 	psoc = wlan_pdev_get_psoc(pdev);
75 	if (!psoc) {
76 		mlme_err("vdev_id: %d psoc not found", req->vdev_id);
77 		return QDF_STATUS_E_INVAL;
78 	}
79 	mlo_sta_stop_reconfig_timer(vdev);
80 	if (req->source != CM_MLO_LINK_SWITCH_DISCONNECT)
81 		ml_nlink_conn_change_notify(
82 			psoc, wlan_vdev_get_id(vdev),
83 			ml_nlink_disconnect_start_evt, NULL);
84 	if (cm_csr_is_ss_wait_for_key(req->vdev_id)) {
85 		mlme_debug("Stop Wait for key timer");
86 		cm_stop_wait_for_key_timer(psoc, req->vdev_id);
87 		cm_csr_set_ss_none(req->vdev_id);
88 	}
89 
90 	user_disconnect =
91 		(req->source == CM_OSIF_DISCONNECT ||
92 		 req->source == CM_MLO_LINK_SWITCH_DISCONNECT) ? true : false;
93 	if (user_disconnect) {
94 		wlan_p2p_cleanup_roc_by_vdev(vdev, false);
95 		wlan_tdls_notify_sta_disconnect(req->vdev_id, false,
96 						user_disconnect, vdev);
97 	}
98 	cm_abort_connect_request_timers(vdev);
99 
100 	if (req->source != CM_MLO_ROAM_INTERNAL_DISCONNECT &&
101 	    req->source != CM_MLO_LINK_SWITCH_DISCONNECT) {
102 		mlme_debug("Free copied reassoc rsp");
103 		mlo_roam_free_copied_reassoc_rsp(vdev);
104 	}
105 
106 	wlan_t2lm_clear_all_tid_mapping(vdev);
107 
108 	return QDF_STATUS_SUCCESS;
109 }
110 
111 #ifdef WLAN_CHIPSET_STATS
112 static void
cm_cp_stats_cstats_disconn_req_event(struct wlan_objmgr_vdev * vdev,struct wlan_cm_vdev_discon_req * req)113 cm_cp_stats_cstats_disconn_req_event(struct wlan_objmgr_vdev *vdev,
114 				     struct wlan_cm_vdev_discon_req *req)
115 {
116 	struct cstats_sta_disconnect_req stat = {0};
117 
118 	stat.cmn.hdr.evt_id = WLAN_CHIPSET_STATS_STA_DISCONNECT_REQ_EVENT_ID;
119 	stat.cmn.hdr.length = sizeof(struct cstats_sta_disconnect_req) -
120 			      sizeof(struct cstats_hdr);
121 	stat.cmn.opmode = wlan_vdev_mlme_get_opmode(vdev);
122 	stat.cmn.vdev_id = req->req.vdev_id;
123 	stat.cmn.timestamp_us = qdf_get_time_of_the_day_us();
124 	stat.cmn.time_tick = qdf_get_log_timestamp();
125 	stat.reason_code = req->req.reason_code;
126 	stat.source = req->req.source;
127 	stat.is_no_disassoc_disconnect = req->req.is_no_disassoc_disconnect;
128 	CSTATS_MAC_COPY(stat.bssid, req->req.bssid.bytes);
129 
130 	wlan_cstats_host_stats(sizeof(struct cstats_sta_disconnect_req), &stat);
131 }
132 
133 static void
cm_cp_stats_cstats_disconn_resp_event(struct wlan_objmgr_vdev * vdev,struct wlan_cm_discon_rsp * rsp)134 cm_cp_stats_cstats_disconn_resp_event(struct wlan_objmgr_vdev *vdev,
135 				      struct wlan_cm_discon_rsp *rsp)
136 {
137 	struct cstats_sta_disconnect_resp stat = {0};
138 
139 	stat.cmn.hdr.evt_id = WLAN_CHIPSET_STATS_STA_DISCONNECT_DONE_EVENT_ID;
140 	stat.cmn.hdr.length = sizeof(struct cstats_sta_disconnect_resp) -
141 			      sizeof(struct cstats_hdr);
142 	stat.cmn.opmode = wlan_vdev_mlme_get_opmode(vdev);
143 	stat.cmn.vdev_id = wlan_vdev_get_id(vdev);
144 	stat.cmn.timestamp_us = qdf_get_time_of_the_day_us();
145 	stat.cmn.time_tick = qdf_get_log_timestamp();
146 	stat.cm_id = rsp->req.cm_id;
147 	stat.reason_code = rsp->req.req.reason_code;
148 	stat.source = rsp->req.req.source;
149 	CSTATS_MAC_COPY(stat.bssid, rsp->req.req.bssid.bytes);
150 
151 	wlan_cstats_host_stats(sizeof(struct cstats_sta_disconnect_resp),
152 			       &stat);
153 }
154 #else
155 static inline void
cm_cp_stats_cstats_disconn_req_event(struct wlan_objmgr_vdev * vdev,struct wlan_cm_vdev_discon_req * req)156 cm_cp_stats_cstats_disconn_req_event(struct wlan_objmgr_vdev *vdev,
157 				     struct wlan_cm_vdev_discon_req *req)
158 {
159 }
160 
161 static inline void
cm_cp_stats_cstats_disconn_resp_event(struct wlan_objmgr_vdev * vdev,struct wlan_cm_discon_rsp * rsp)162 cm_cp_stats_cstats_disconn_resp_event(struct wlan_objmgr_vdev *vdev,
163 				      struct wlan_cm_discon_rsp *rsp)
164 {
165 }
166 #endif /* WLAN_CHIPSET_STATS */
167 
168 QDF_STATUS
cm_handle_disconnect_req(struct wlan_objmgr_vdev * vdev,struct wlan_cm_vdev_discon_req * req)169 cm_handle_disconnect_req(struct wlan_objmgr_vdev *vdev,
170 			 struct wlan_cm_vdev_discon_req *req)
171 {
172 	struct wlan_cm_vdev_discon_req *discon_req;
173 	struct scheduler_msg msg;
174 	QDF_STATUS status;
175 	enum QDF_OPMODE opmode;
176 	struct wlan_objmgr_pdev *pdev;
177 	uint8_t vdev_id;
178 	struct rso_config *rso_cfg;
179 	struct wlan_objmgr_psoc *psoc;
180 
181 	if (!vdev || !req)
182 		return QDF_STATUS_E_FAILURE;
183 
184 	pdev = wlan_vdev_get_pdev(vdev);
185 	if (!pdev) {
186 		mlme_err(CM_PREFIX_FMT "pdev not found",
187 			 CM_PREFIX_REF(req->req.vdev_id, req->cm_id));
188 		return QDF_STATUS_E_INVAL;
189 	}
190 	psoc = wlan_pdev_get_psoc(pdev);
191 	if (!psoc) {
192 		mlme_err(CM_PREFIX_FMT "psoc not found",
193 			 CM_PREFIX_REF(req->req.vdev_id, req->cm_id));
194 		return QDF_STATUS_E_INVAL;
195 	}
196 	rso_cfg = wlan_cm_get_rso_config(vdev);
197 	if (!rso_cfg)
198 		return QDF_STATUS_E_INVAL;
199 	vdev_id = wlan_vdev_get_id(vdev);
200 
201 	qdf_mem_zero(&msg, sizeof(msg));
202 
203 	discon_req = qdf_mem_malloc(sizeof(*discon_req));
204 	if (!discon_req)
205 		return QDF_STATUS_E_NOMEM;
206 	cm_cp_stats_cstats_disconn_req_event(vdev, req);
207 
208 	cm_csr_handle_diconnect_req(vdev, req);
209 	wlan_roam_reset_roam_params(vdev);
210 	cm_roam_restore_default_config(pdev, vdev_id);
211 	opmode = wlan_vdev_mlme_get_opmode(vdev);
212 	if (opmode == QDF_STA_MODE && !wlan_vdev_mlme_is_link_sta_vdev(vdev))
213 		wlan_cm_roam_state_change(pdev, vdev_id,
214 					  WLAN_ROAM_DEINIT,
215 					  REASON_DISCONNECTED);
216 	if (rso_cfg->roam_scan_freq_lst.freq_list)
217 		qdf_mem_free(rso_cfg->roam_scan_freq_lst.freq_list);
218 	rso_cfg->roam_scan_freq_lst.freq_list = NULL;
219 	rso_cfg->roam_scan_freq_lst.num_chan = 0;
220 
221 	qdf_mem_copy(discon_req, req, sizeof(*req));
222 	msg.bodyptr = discon_req;
223 	msg.type = CM_DISCONNECT_REQ;
224 
225 	status = scheduler_post_message(QDF_MODULE_ID_MLME,
226 					QDF_MODULE_ID_PE,
227 					QDF_MODULE_ID_PE, &msg);
228 	if (QDF_IS_STATUS_ERROR(status)) {
229 		mlme_err(CM_PREFIX_FMT "msg post fail",
230 			 CM_PREFIX_REF(req->req.vdev_id, req->cm_id));
231 		qdf_mem_free(discon_req);
232 	}
233 	return status;
234 }
235 
236 #ifdef FEATURE_WLAN_DIAG_SUPPORT_CSR
cm_disconnect_diag_event(struct wlan_objmgr_vdev * vdev,struct wlan_cm_discon_rsp * rsp)237 static void cm_disconnect_diag_event(struct wlan_objmgr_vdev *vdev,
238 				     struct wlan_cm_discon_rsp *rsp)
239 {
240 	struct wlan_objmgr_psoc *psoc;
241 	struct wlan_mlme_psoc_ext_obj *mlme_obj;
242 	WLAN_HOST_DIAG_EVENT_DEF(connect_status,
243 				 host_event_wlan_status_payload_type);
244 
245 	psoc = wlan_vdev_get_psoc(vdev);
246 	if (!psoc)
247 		return;
248 
249 	mlme_obj = mlme_get_psoc_ext_obj(psoc);
250 	if (!mlme_obj)
251 		return;
252 	qdf_mem_zero(&connect_status,
253 		     sizeof(host_event_wlan_status_payload_type));
254 	qdf_mem_copy(&connect_status,
255 		     &mlme_obj->cfg.sta.event_payload,
256 		     sizeof(host_event_wlan_status_payload_type));
257 
258 	connect_status.rssi = mlme_obj->cfg.sta.current_rssi;
259 	connect_status.eventId = DIAG_WLAN_STATUS_DISCONNECT;
260 	if (rsp->req.req.reason_code == REASON_MIC_FAILURE) {
261 		connect_status.reason = DIAG_REASON_MIC_ERROR;
262 	} else if (rsp->req.req.source == CM_PEER_DISCONNECT) {
263 		switch (rsp->req.req.reason_code) {
264 		case REASON_PREV_AUTH_NOT_VALID:
265 		case REASON_CLASS2_FRAME_FROM_NON_AUTH_STA:
266 		case REASON_DEAUTH_NETWORK_LEAVING:
267 			connect_status.reason = DIAG_REASON_DEAUTH;
268 			break;
269 		default:
270 			connect_status.reason = DIAG_REASON_DISASSOC;
271 			break;
272 		}
273 		connect_status.reasonDisconnect = rsp->req.req.reason_code;
274 	} else if (rsp->req.req.source == CM_SB_DISCONNECT) {
275 		connect_status.reason = DIAG_REASON_DEAUTH;
276 		connect_status.reasonDisconnect = rsp->req.req.reason_code;
277 	} else {
278 		connect_status.reason = DIAG_REASON_USER_REQUESTED;
279 	}
280 
281 	WLAN_HOST_DIAG_EVENT_REPORT(&connect_status,
282 				    EVENT_WLAN_STATUS_V2);
283 }
284 #else
cm_disconnect_diag_event(struct wlan_objmgr_vdev * vdev,struct wlan_cm_discon_rsp * rsp)285 static inline void cm_disconnect_diag_event(struct wlan_objmgr_vdev *vdev,
286 					    struct wlan_cm_discon_rsp *rsp)
287 {}
288 #endif
289 
290 QDF_STATUS
cm_disconnect_complete_ind(struct wlan_objmgr_vdev * vdev,struct wlan_cm_discon_rsp * rsp)291 cm_disconnect_complete_ind(struct wlan_objmgr_vdev *vdev,
292 			   struct wlan_cm_discon_rsp *rsp)
293 {
294 	uint8_t vdev_id;
295 	struct wlan_objmgr_psoc *psoc;
296 	struct wlan_objmgr_pdev *pdev;
297 	enum QDF_OPMODE op_mode;
298 
299 	if (!vdev || !rsp) {
300 		mlme_err("vdev or rsp is NULL");
301 		return QDF_STATUS_E_INVAL;
302 	}
303 	cm_csr_disconnect_done_ind(vdev, rsp);
304 
305 	vdev_id = wlan_vdev_get_id(vdev);
306 	op_mode = wlan_vdev_mlme_get_opmode(vdev);
307 	pdev = wlan_vdev_get_pdev(vdev);
308 	if (!pdev) {
309 		mlme_err(CM_PREFIX_FMT "pdev not found",
310 			 CM_PREFIX_REF(vdev_id, rsp->req.cm_id));
311 		return QDF_STATUS_E_INVAL;
312 	}
313 	psoc = wlan_pdev_get_psoc(pdev);
314 	if (!psoc) {
315 		mlme_err(CM_PREFIX_FMT "psoc not found",
316 			 CM_PREFIX_REF(vdev_id, rsp->req.cm_id));
317 		return QDF_STATUS_E_INVAL;
318 	}
319 	cm_cp_stats_cstats_disconn_resp_event(vdev, rsp);
320 
321 	cm_disconnect_diag_event(vdev, rsp);
322 	wlan_tdls_notify_sta_disconnect(vdev_id, false, false, vdev);
323 	policy_mgr_decr_session_set_pcl(psoc, op_mode, vdev_id);
324 	if (rsp->req.req.source != CM_MLO_LINK_SWITCH_DISCONNECT) {
325 		wlan_clear_mlo_sta_link_removed_flag(vdev);
326 		ml_nlink_conn_change_notify(
327 			psoc, vdev_id, ml_nlink_disconnect_completion_evt,
328 			NULL);
329 	}
330 
331 	return QDF_STATUS_SUCCESS;
332 }
333 
cm_send_vdev_down_req(struct wlan_objmgr_vdev * vdev)334 QDF_STATUS cm_send_vdev_down_req(struct wlan_objmgr_vdev *vdev)
335 {
336 	struct del_bss_resp *resp;
337 
338 	resp = qdf_mem_malloc(sizeof(*resp));
339 	if (!resp)
340 		return QDF_STATUS_E_NOMEM;
341 
342 	resp->status = QDF_STATUS_SUCCESS;
343 	resp->vdev_id = wlan_vdev_get_id(vdev);
344 	return wlan_vdev_mlme_sm_deliver_evt(vdev,
345 					     WLAN_VDEV_SM_EV_MLME_DOWN_REQ,
346 					     sizeof(*resp), resp);
347 }
348 
cm_disconnect(struct wlan_objmgr_psoc * psoc,uint8_t vdev_id,enum wlan_cm_source source,enum wlan_reason_code reason_code,struct qdf_mac_addr * bssid)349 QDF_STATUS cm_disconnect(struct wlan_objmgr_psoc *psoc, uint8_t vdev_id,
350 			 enum wlan_cm_source source,
351 			 enum wlan_reason_code reason_code,
352 			 struct qdf_mac_addr *bssid)
353 {
354 	struct wlan_objmgr_vdev *vdev;
355 	QDF_STATUS status;
356 
357 	vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, vdev_id,
358 						    WLAN_MLME_CM_ID);
359 	if (!vdev) {
360 		mlme_err("vdev_id: %d: vdev not found", vdev_id);
361 		return QDF_STATUS_E_INVAL;
362 	}
363 
364 	status = wlan_cm_disconnect(vdev, source, reason_code, bssid);
365 	wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_CM_ID);
366 
367 	return status;
368 }
369 
cm_send_sb_disconnect_req(struct scheduler_msg * msg)370 QDF_STATUS cm_send_sb_disconnect_req(struct scheduler_msg *msg)
371 {
372 	struct cm_vdev_discon_ind *ind;
373 	QDF_STATUS status;
374 	struct wlan_objmgr_vdev *vdev;
375 
376 	if (!msg || !msg->bodyptr)
377 		return QDF_STATUS_E_FAILURE;
378 
379 	ind = msg->bodyptr;
380 
381 	vdev = wlan_objmgr_get_vdev_by_id_from_psoc(ind->psoc,
382 						ind->disconnect_param.vdev_id,
383 						WLAN_MLME_CM_ID);
384 
385 	if (!vdev) {
386 		mlme_err("vdev_id: %d: vdev not found",
387 			 ind->disconnect_param.vdev_id);
388 		return QDF_STATUS_E_INVAL;
389 	}
390 
391 	status = wlan_mlo_mgr_link_switch_defer_disconnect_req(vdev,
392 							       ind->disconnect_param.source,
393 							       ind->disconnect_param.reason_code);
394 	if (QDF_IS_STATUS_ERROR(status)) {
395 		status = mlo_disconnect(vdev, ind->disconnect_param.source,
396 					ind->disconnect_param.reason_code,
397 					&ind->disconnect_param.bssid);
398 	}
399 
400 	qdf_mem_free(ind);
401 	wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_CM_ID);
402 
403 	return status;
404 }
405 
cm_copy_peer_disconnect_ies(struct wlan_objmgr_vdev * vdev,struct element_info * ap_ie)406 static void cm_copy_peer_disconnect_ies(struct wlan_objmgr_vdev *vdev,
407 					struct element_info *ap_ie)
408 {
409 	struct element_info *discon_ie;
410 
411 	discon_ie = mlme_get_peer_disconnect_ies(vdev);
412 	if (!discon_ie)
413 		return;
414 
415 	ap_ie->len = discon_ie->len;
416 	ap_ie->ptr = discon_ie->ptr;
417 }
418 
419 #ifdef WLAN_FEATURE_HOST_ROAM
420 static inline
cm_fill_disconnect_resp(struct wlan_objmgr_vdev * vdev,struct wlan_cm_discon_rsp * resp)421 QDF_STATUS cm_fill_disconnect_resp(struct wlan_objmgr_vdev *vdev,
422 				   struct wlan_cm_discon_rsp *resp)
423 {
424 	struct wlan_cm_vdev_reassoc_req req;
425 
426 	/* Fill from reassoc req for Handsoff disconnect */
427 	if (cm_get_active_reassoc_req(vdev, &req)) {
428 		resp->req.cm_id = req.cm_id;
429 		resp->req.req.vdev_id = req.vdev_id;
430 		qdf_copy_macaddr(&resp->req.req.bssid, &req.prev_bssid);
431 		resp->req.req.source = CM_ROAM_DISCONNECT;
432 	} else if (!cm_get_active_disconnect_req(vdev, &resp->req)) {
433 		/* If not reassoc then fill from disconnect active */
434 		return QDF_STATUS_E_FAILURE;
435 	}
436 	mlme_debug(CM_PREFIX_FMT "disconnect found source %d",
437 		   CM_PREFIX_REF(resp->req.req.vdev_id, resp->req.cm_id),
438 		   resp->req.req.source);
439 
440 	return QDF_STATUS_SUCCESS;
441 }
442 #else
443 static inline
cm_fill_disconnect_resp(struct wlan_objmgr_vdev * vdev,struct wlan_cm_discon_rsp * resp)444 QDF_STATUS cm_fill_disconnect_resp(struct wlan_objmgr_vdev *vdev,
445 				   struct wlan_cm_discon_rsp *resp)
446 {
447 	if (!cm_get_active_disconnect_req(vdev, &resp->req))
448 		return QDF_STATUS_E_FAILURE;
449 
450 	return QDF_STATUS_SUCCESS;
451 }
452 #endif
453 
cm_handle_disconnect_resp(struct scheduler_msg * msg)454 QDF_STATUS cm_handle_disconnect_resp(struct scheduler_msg *msg)
455 {
456 	QDF_STATUS status;
457 	struct cm_vdev_disconnect_rsp *ind;
458 	struct wlan_cm_discon_rsp resp;
459 	struct wlan_objmgr_vdev *vdev;
460 
461 	if (!msg || !msg->bodyptr)
462 		return QDF_STATUS_E_FAILURE;
463 
464 	ind = msg->bodyptr;
465 	vdev = wlan_objmgr_get_vdev_by_id_from_psoc(ind->psoc, ind->vdev_id,
466 						    WLAN_MLME_CM_ID);
467 	if (!vdev) {
468 		mlme_err("vdev_id: %d : vdev not found", ind->vdev_id);
469 		qdf_mem_free(ind);
470 		return QDF_STATUS_E_INVAL;
471 	}
472 
473 	qdf_mem_zero(&resp, sizeof(resp));
474 	status = cm_fill_disconnect_resp(vdev, &resp);
475 	if (QDF_IS_STATUS_ERROR(status)) {
476 		mlme_err("Active disconnect not found for vdev %d",
477 			 ind->vdev_id);
478 		qdf_mem_free(ind);
479 		wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_CM_ID);
480 		return QDF_STATUS_E_FAILURE;
481 	}
482 
483 	if (resp.req.req.source == CM_PEER_DISCONNECT)
484 		cm_copy_peer_disconnect_ies(vdev, &resp.ap_discon_ie);
485 
486 	status = wlan_cm_disconnect_rsp(vdev, &resp);
487 	mlme_free_peer_disconnect_ies(vdev);
488 	wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_CM_ID);
489 
490 	qdf_mem_free(ind);
491 
492 	return QDF_STATUS_SUCCESS;
493 }
494 
495 #ifdef WLAN_FEATURE_11BE_MLO
496 static QDF_STATUS
wlan_cm_mlo_update_disconnecting_vdev_id(struct wlan_objmgr_psoc * psoc,uint8_t * vdev_id)497 wlan_cm_mlo_update_disconnecting_vdev_id(struct wlan_objmgr_psoc *psoc,
498 					 uint8_t *vdev_id)
499 {
500 	struct wlan_objmgr_vdev *vdev = NULL;
501 	struct wlan_objmgr_vdev *vdev_list[WLAN_UMAC_MLO_MAX_VDEVS] = {NULL};
502 	uint16_t num_links = 0, i = 0;
503 	QDF_STATUS status = QDF_STATUS_SUCCESS;
504 
505 	vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, *vdev_id,
506 						    WLAN_MLME_SB_ID);
507 	if (!vdev) {
508 		mlme_err("vdev_id: %d vdev not found", *vdev_id);
509 		return QDF_STATUS_E_NULL_VALUE;
510 	}
511 	if (!wlan_vdev_mlme_is_mlo_vdev(vdev)) {
512 		status = QDF_STATUS_SUCCESS;
513 		goto done;
514 	}
515 
516 	/*
517 	 * During the link vdev disconnection the RSO stop vdev id will be of
518 	 * assoc vdev (changed during cm_handle_mlo_rso_state_change), So try
519 	 * to get the link vdev on which disconnect was actually happening i.e
520 	 * the one with active disconnecting state from the mlo links, so that
521 	 * continue disconnect is initiated on a proper vdev in connection
522 	 * manager.
523 	 */
524 	mlo_get_ml_vdev_list(vdev, &num_links, vdev_list);
525 	if (!num_links) {
526 		mlme_err("No VDEVs under vdev id: %d", *vdev_id);
527 		status = QDF_STATUS_E_NULL_VALUE;
528 		goto done;
529 	}
530 
531 	if (num_links > QDF_ARRAY_SIZE(vdev_list)) {
532 		mlme_err("Invalid number of VDEVs num_links:%u", num_links);
533 		status = QDF_STATUS_E_INVAL;
534 		goto release_mlo_ref;
535 	}
536 
537 	for (i = 0; i < num_links; i++) {
538 		if (wlan_vdev_mlme_is_mlo_link_vdev(vdev_list[i]) &&
539 		    (wlan_cm_is_vdev_disconnecting(vdev_list[i]) ||
540 		     wlan_cm_is_vdev_connecting(vdev_list[i])) &&
541 		    wlan_cm_get_active_req_type(vdev_list[i]) ==
542 							CM_DISCONNECT_ACTIVE) {
543 			/*
544 			 * This is expected to match only once as per current
545 			 * design.
546 			 */
547 			*vdev_id = wlan_vdev_get_id(vdev_list[i]);
548 			break;
549 		}
550 	}
551 
552 release_mlo_ref:
553 	for (i = 0; i < num_links; i++)
554 		mlo_release_vdev_ref(vdev_list[i]);
555 
556 done:
557 	wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_SB_ID);
558 
559 	return status;
560 }
561 #else
562 static QDF_STATUS
wlan_cm_mlo_update_disconnecting_vdev_id(struct wlan_objmgr_psoc * psoc,uint8_t * vdev_id)563 wlan_cm_mlo_update_disconnecting_vdev_id(struct wlan_objmgr_psoc *psoc,
564 					 uint8_t *vdev_id)
565 {
566 	return QDF_STATUS_E_NOSUPPORT;
567 }
568 #endif /* WLAN_FEATURE_11BE_MLO */
569 
570 QDF_STATUS
wlan_cm_rso_stop_continue_disconnect(struct wlan_objmgr_psoc * psoc,uint8_t rso_stop_vdev_id,bool is_ho_fail)571 wlan_cm_rso_stop_continue_disconnect(struct wlan_objmgr_psoc *psoc,
572 				     uint8_t rso_stop_vdev_id, bool is_ho_fail)
573 {
574 	struct wlan_objmgr_vdev *vdev = NULL;
575 	struct wlan_cm_vdev_discon_req *req;
576 	QDF_STATUS status = QDF_STATUS_SUCCESS;
577 	uint8_t vdev_id = rso_stop_vdev_id;
578 
579 	wlan_cm_mlo_update_disconnecting_vdev_id(psoc, &vdev_id);
580 	vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, vdev_id,
581 						    WLAN_MLME_SB_ID);
582 	if (!vdev) {
583 		mlme_err("vdev_id: %d vdev not found", vdev_id);
584 		return QDF_STATUS_E_NULL_VALUE;
585 	}
586 
587 	req = qdf_mem_malloc(sizeof(*req));
588 	if (!req) {
589 		status = QDF_STATUS_E_NOMEM;
590 		goto done;
591 	}
592 
593 	if (!cm_get_active_disconnect_req(vdev, req)) {
594 		mlme_err("vdev: %d: Active disconnect not found", vdev_id);
595 		status = QDF_STATUS_E_EXISTS;
596 		goto done;
597 	}
598 
599 	if (is_ho_fail) {
600 		req->req.source = CM_MLME_DISCONNECT;
601 		req->req.reason_code = REASON_FW_TRIGGERED_ROAM_FAILURE;
602 		mlme_debug(CM_PREFIX_FMT "Updating source(%d) and reason code (%d) to RSO reason and source as ho fail is received in RSO stop",
603 			   CM_PREFIX_REF(req->req.vdev_id, req->cm_id),
604 			   req->req.source, req->req.reason_code);
605 	}
606 	wlan_cm_disc_cont_after_rso_stop(vdev, req);
607 
608 done:
609 	wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_SB_ID);
610 	qdf_mem_free(req);
611 
612 	return status;
613 }
614