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