xref: /wlan-driver/qca-wifi-host-cmn/umac/mlme/connection_mgr/core/src/wlan_cm_host_roam.c (revision 5113495b16420b49004c444715d2daae2066e7dc)
1 /*
2  * Copyright (c) 2011-2021 The Linux Foundation. All rights reserved.
3  * Copyright (c) 2022-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 #include "wlan_cm_main.h"
19 #include "wlan_cm_roam_sm.h"
20 #include "wlan_cm_sm.h"
21 #include <include/wlan_mlme_cmn.h>
22 #include "wlan_cm_main_api.h"
23 #include <wlan_scan_api.h>
24 #include <wlan_serialization_api.h>
25 #include <wlan_utility.h>
26 #include <wlan_cm_api.h>
27 #ifdef WLAN_POLICY_MGR_ENABLE
28 #include "wlan_policy_mgr_api.h"
29 #endif
30 
31 static void
cm_fill_roam_fail_resp_from_cm_id(struct cnx_mgr * cm_ctx,struct wlan_cm_connect_resp * resp,wlan_cm_id cm_id,enum wlan_cm_connect_fail_reason reason)32 cm_fill_roam_fail_resp_from_cm_id(struct cnx_mgr *cm_ctx,
33 				  struct wlan_cm_connect_resp *resp,
34 				  wlan_cm_id cm_id,
35 				  enum wlan_cm_connect_fail_reason reason)
36 {
37 	resp->connect_status = QDF_STATUS_E_FAILURE;
38 	resp->cm_id = cm_id;
39 	resp->vdev_id = wlan_vdev_get_id(cm_ctx->vdev);
40 	resp->reason = reason;
41 	cm_fill_bss_info_in_roam_rsp_by_cm_id(cm_ctx, cm_id, resp);
42 }
43 
44 static QDF_STATUS
cm_reassoc_fail_disconnect(struct wlan_objmgr_vdev * vdev,enum wlan_cm_source source,enum wlan_reason_code reason_code,struct qdf_mac_addr * bssid)45 cm_reassoc_fail_disconnect(struct wlan_objmgr_vdev *vdev,
46 			   enum wlan_cm_source source,
47 			   enum wlan_reason_code reason_code,
48 			   struct qdf_mac_addr *bssid)
49 {
50 	struct cnx_mgr *cm_ctx;
51 	struct cm_req *cm_req;
52 	struct cm_disconnect_req *disconnect_req;
53 	struct wlan_cm_disconnect_req req = {0};
54 	QDF_STATUS status;
55 
56 	cm_ctx = cm_get_cm_ctx(vdev);
57 	if (!cm_ctx)
58 		return QDF_STATUS_E_INVAL;
59 
60 	/*
61 	 * This would be freed as part of removal from cm req list if adding
62 	 * to list is success after posting WLAN_CM_SM_EV_DISCONNECT_REQ.
63 	 */
64 	cm_req = qdf_mem_malloc(sizeof(*cm_req));
65 	if (!cm_req)
66 		return QDF_STATUS_E_NOMEM;
67 
68 	req.vdev_id = wlan_vdev_get_id(vdev);
69 	req.source = source;
70 	req.reason_code = reason_code;
71 	if (bssid)
72 		qdf_copy_macaddr(&req.bssid, bssid);
73 
74 	disconnect_req = &cm_req->discon_req;
75 	disconnect_req->req = req;
76 
77 	status = cm_sm_deliver_event_sync(cm_ctx, WLAN_CM_SM_EV_DISCONNECT_REQ,
78 					  sizeof(*disconnect_req),
79 					  disconnect_req);
80 	if (QDF_IS_STATUS_ERROR(status))
81 		qdf_mem_free(cm_req);
82 
83 	return status;
84 }
85 
86 QDF_STATUS
cm_send_reassoc_start_fail(struct cnx_mgr * cm_ctx,wlan_cm_id cm_id,enum wlan_cm_connect_fail_reason reason,bool sync)87 cm_send_reassoc_start_fail(struct cnx_mgr *cm_ctx,
88 			   wlan_cm_id cm_id,
89 			   enum wlan_cm_connect_fail_reason reason,
90 			   bool sync)
91 {
92 	struct wlan_cm_connect_resp *resp;
93 	QDF_STATUS status;
94 
95 	resp = qdf_mem_malloc(sizeof(*resp));
96 	if (!resp)
97 		return QDF_STATUS_E_NOMEM;
98 
99 	cm_fill_roam_fail_resp_from_cm_id(cm_ctx, resp, cm_id, reason);
100 	if (sync)
101 		status = cm_sm_deliver_event_sync(
102 				cm_ctx, WLAN_CM_SM_EV_REASSOC_FAILURE,
103 				sizeof(*resp), resp);
104 	else
105 		status = cm_sm_deliver_event(cm_ctx->vdev,
106 					     WLAN_CM_SM_EV_REASSOC_FAILURE,
107 					     sizeof(*resp), resp);
108 
109 	if (QDF_IS_STATUS_ERROR(status))
110 		cm_reassoc_complete(cm_ctx, resp);
111 
112 	qdf_mem_free(resp);
113 
114 	return status;
115 }
116 
117 #ifdef CONN_MGR_ADV_FEATURE
118 static QDF_STATUS
cm_update_roam_scan_filter(struct wlan_objmgr_vdev * vdev,struct cm_roam_req * cm_req,struct scan_filter * filter,bool security_valid_for_6ghz)119 cm_update_roam_scan_filter(
120 		struct wlan_objmgr_vdev *vdev, struct cm_roam_req *cm_req,
121 		struct scan_filter *filter, bool security_valid_for_6ghz)
122 {
123 	return cm_update_advance_roam_scan_filter(vdev, filter);
124 }
125 #else
126 static QDF_STATUS
cm_update_roam_scan_filter(struct wlan_objmgr_vdev * vdev,struct cm_roam_req * cm_req,struct scan_filter * filter,bool security_valid_for_6ghz)127 cm_update_roam_scan_filter(
128 		struct wlan_objmgr_vdev *vdev, struct cm_roam_req *cm_req,
129 		struct scan_filter *filter, bool security_valid_for_6ghz)
130 {
131 	uint16_t rsn_caps;
132 
133 	filter->num_of_ssid = 1;
134 	wlan_vdev_mlme_get_ssid(vdev, filter->ssid_list[0].ssid,
135 				&filter->ssid_list[0].length);
136 
137 	if (cm_req->req.chan_freq) {
138 		filter->num_of_channels = 1;
139 		filter->chan_freq_list[0] = cm_req->req.chan_freq;
140 	}
141 
142 	/* Security is not valid for 6Ghz so ignore 6Ghz APs */
143 	if (!security_valid_for_6ghz)
144 		filter->ignore_6ghz_channel = true;
145 
146 	if (!QDF_HAS_PARAM(filter->authmodeset, WLAN_CRYPTO_AUTH_WAPI) &&
147 	    !QDF_HAS_PARAM(filter->authmodeset, WLAN_CRYPTO_AUTH_RSNA) &&
148 	    !QDF_HAS_PARAM(filter->authmodeset, WLAN_CRYPTO_AUTH_WPA)) {
149 		filter->ignore_auth_enc_type = 1;
150 	}
151 
152 	rsn_caps =
153 		wlan_crypto_get_param(vdev, WLAN_CRYPTO_PARAM_RSN_CAP);
154 
155 	if (rsn_caps & WLAN_CRYPTO_RSN_CAP_MFP_REQUIRED)
156 		filter->pmf_cap = WLAN_PMF_REQUIRED;
157 	else if (rsn_caps & WLAN_CRYPTO_RSN_CAP_MFP_ENABLED)
158 		filter->pmf_cap = WLAN_PMF_CAPABLE;
159 	else
160 		filter->pmf_cap = WLAN_PMF_DISABLED;
161 	return QDF_STATUS_SUCCESS;
162 }
163 #endif
164 
cm_connect_prepare_scan_filter_for_roam(struct cnx_mgr * cm_ctx,struct cm_roam_req * cm_req,struct scan_filter * filter,bool security_valid_for_6ghz)165 static QDF_STATUS cm_connect_prepare_scan_filter_for_roam(
166 		struct cnx_mgr *cm_ctx, struct cm_roam_req *cm_req,
167 		struct scan_filter *filter, bool security_valid_for_6ghz)
168 {
169 	struct wlan_objmgr_vdev *vdev = cm_ctx->vdev;
170 
171 	if (!qdf_is_macaddr_zero(&cm_req->req.bssid)) {
172 		filter->num_of_bssid = 1;
173 		qdf_copy_macaddr(&filter->bssid_list[0], &cm_req->req.bssid);
174 	}
175 
176 	filter->authmodeset =
177 		wlan_crypto_get_param(vdev, WLAN_CRYPTO_PARAM_AUTH_MODE);
178 
179 	filter->ucastcipherset =
180 		wlan_crypto_get_param(vdev, WLAN_CRYPTO_PARAM_UCAST_CIPHER);
181 
182 	filter->mcastcipherset =
183 		wlan_crypto_get_param(vdev, WLAN_CRYPTO_PARAM_MCAST_CIPHER);
184 
185 	filter->key_mgmt =
186 		wlan_crypto_get_param(vdev, WLAN_CRYPTO_PARAM_KEY_MGMT);
187 
188 	filter->mgmtcipherset =
189 		wlan_crypto_get_param(vdev, WLAN_CRYPTO_PARAM_MGMT_CIPHER);
190 
191 	return cm_update_roam_scan_filter(vdev, cm_req, filter,
192 					  security_valid_for_6ghz);
193 }
194 
cm_roam_get_candidates(struct wlan_objmgr_pdev * pdev,struct cnx_mgr * cm_ctx,struct cm_roam_req * cm_req)195 static QDF_STATUS cm_roam_get_candidates(struct wlan_objmgr_pdev *pdev,
196 					 struct cnx_mgr *cm_ctx,
197 					 struct cm_roam_req *cm_req)
198 {
199 	struct scan_filter *filter;
200 	uint32_t num_bss = 0;
201 	enum QDF_OPMODE op_mode;
202 	qdf_list_t *candidate_list;
203 	uint8_t vdev_id = wlan_vdev_get_id(cm_ctx->vdev);
204 	qdf_list_node_t *cur_node = NULL;
205 	bool security_valid_for_6ghz = true;
206 
207 	filter = qdf_mem_malloc(sizeof(*filter));
208 	if (!filter)
209 		return QDF_STATUS_E_NOMEM;
210 
211 	cm_connect_prepare_scan_filter_for_roam(cm_ctx, cm_req, filter,
212 						security_valid_for_6ghz);
213 
214 	candidate_list = wlan_scan_get_result(pdev, filter);
215 	if (candidate_list) {
216 		num_bss = qdf_list_size(candidate_list);
217 		mlme_debug(CM_PREFIX_FMT "num_entries found %d",
218 			   CM_PREFIX_REF(vdev_id, cm_req->cm_id), num_bss);
219 	}
220 
221 	op_mode = wlan_vdev_mlme_get_opmode(cm_ctx->vdev);
222 	if (num_bss && op_mode == QDF_STA_MODE)
223 		cm_calculate_scores(cm_ctx, pdev, filter, candidate_list);
224 
225 	qdf_mem_free(filter);
226 
227 	if (!candidate_list || !qdf_list_size(candidate_list)) {
228 		if (candidate_list)
229 			wlan_scan_purge_results(candidate_list);
230 
231 		mlme_info(CM_PREFIX_FMT "no valid candidate found, num_bss %d",
232 			  CM_PREFIX_REF(vdev_id, cm_req->cm_id), num_bss);
233 		cm_req->candidate_list = NULL;
234 		return QDF_STATUS_E_EMPTY;
235 	}
236 
237 	qdf_list_peek_front(candidate_list, &cur_node);
238 	cm_req->candidate_list = candidate_list;
239 	cm_req->cur_candidate = qdf_container_of(cur_node,
240 						 struct scan_cache_node,
241 						 node);
242 	return QDF_STATUS_SUCCESS;
243 }
244 
245 #ifdef WLAN_FEATURE_PREAUTH_ENABLE
cm_handle_reassoc_timer(struct cnx_mgr * cm_ctx,wlan_cm_id * cm_id)246 QDF_STATUS cm_handle_reassoc_timer(struct cnx_mgr *cm_ctx, wlan_cm_id *cm_id)
247 {
248 	struct cm_req *cm_req;
249 
250 	if (!cm_id)
251 		return QDF_STATUS_E_INVAL;
252 
253 	cm_req = cm_get_req_by_cm_id(cm_ctx, *cm_id);
254 	if (!cm_req)
255 		return QDF_STATUS_E_INVAL;
256 
257 	return cm_sm_deliver_event_sync(cm_ctx, WLAN_CM_SM_EV_START_REASSOC,
258 					sizeof(cm_req->roam_req),
259 					&cm_req->roam_req);
260 }
261 
cm_host_roam_start(struct cnx_mgr * cm_ctx,struct cm_req * cm_req)262 static QDF_STATUS cm_host_roam_start(struct cnx_mgr *cm_ctx,
263 				     struct cm_req *cm_req)
264 {
265 	cm_req->roam_req.cur_candidate = NULL;
266 
267 	return cm_host_roam_preauth_start(cm_ctx, cm_req);
268 }
269 
270 static
cm_host_roam_start_fail(struct cnx_mgr * cm_ctx,struct cm_req * cm_req,enum wlan_cm_connect_fail_reason reason)271 QDF_STATUS cm_host_roam_start_fail(struct cnx_mgr *cm_ctx,
272 				   struct cm_req *cm_req,
273 				   enum wlan_cm_connect_fail_reason reason)
274 {
275 	cm_send_preauth_start_fail(cm_ctx, cm_req->cm_id, reason);
276 
277 	return QDF_STATUS_SUCCESS;
278 }
279 #else
cm_host_roam_start(struct cnx_mgr * cm_ctx,struct cm_req * cm_req)280 static QDF_STATUS cm_host_roam_start(struct cnx_mgr *cm_ctx,
281 				     struct cm_req *cm_req)
282 {
283 	return cm_sm_deliver_event_sync(cm_ctx, WLAN_CM_SM_EV_START_REASSOC,
284 					sizeof(cm_req->roam_req),
285 					&cm_req->roam_req);
286 }
287 
288 static
cm_host_roam_start_fail(struct cnx_mgr * cm_ctx,struct cm_req * cm_req,enum wlan_cm_connect_fail_reason reason)289 QDF_STATUS cm_host_roam_start_fail(struct cnx_mgr *cm_ctx,
290 				   struct cm_req *cm_req,
291 				   enum wlan_cm_connect_fail_reason reason)
292 {
293 	return cm_send_reassoc_start_fail(cm_ctx, cm_req->cm_id, reason, true);
294 }
295 #endif
296 
cm_host_roam_start_req(struct cnx_mgr * cm_ctx,struct cm_req * cm_req)297 QDF_STATUS cm_host_roam_start_req(struct cnx_mgr *cm_ctx,
298 				  struct cm_req *cm_req)
299 {
300 	QDF_STATUS status;
301 	struct wlan_objmgr_pdev *pdev;
302 	enum wlan_cm_connect_fail_reason reason = CM_GENERIC_FAILURE;
303 
304 	mlme_cm_roam_start_ind(cm_ctx->vdev, &cm_req->roam_req.req);
305 
306 	pdev = wlan_vdev_get_pdev(cm_ctx->vdev);
307 	if (!pdev) {
308 		reason = CM_GENERIC_FAILURE;
309 		goto roam_err;
310 	}
311 
312 	status = cm_roam_get_candidates(pdev, cm_ctx, &cm_req->roam_req);
313 	if (QDF_IS_STATUS_ERROR(status)) {
314 		reason = CM_NO_CANDIDATE_FOUND;
315 		goto roam_err;
316 	}
317 
318 	status = cm_host_roam_start(cm_ctx, cm_req);
319 	if (QDF_IS_STATUS_SUCCESS(status))
320 		return status;
321 
322 roam_err:
323 	return cm_host_roam_start_fail(cm_ctx, cm_req, reason);
324 }
325 
cm_roam_resp_cmid_match_list_head(struct cnx_mgr * cm_ctx,struct wlan_cm_connect_resp * resp)326 bool cm_roam_resp_cmid_match_list_head(struct cnx_mgr *cm_ctx,
327 				       struct wlan_cm_connect_resp *resp)
328 {
329 	return cm_check_cmid_match_list_head(cm_ctx, &resp->cm_id);
330 }
331 
cm_reassoc_active(struct cnx_mgr * cm_ctx,wlan_cm_id * cm_id)332 QDF_STATUS cm_reassoc_active(struct cnx_mgr *cm_ctx, wlan_cm_id *cm_id)
333 {
334 	struct cm_req *cm_req;
335 	struct wlan_cm_vdev_discon_req req;
336 	struct cm_disconnect_req *discon_req;
337 
338 	cm_req = cm_get_req_by_cm_id(cm_ctx, *cm_id);
339 	if (!cm_req)
340 		return QDF_STATUS_E_INVAL;
341 
342 	cm_ctx->active_cm_id = *cm_id;
343 
344 	qdf_mem_zero(&req, sizeof(req));
345 	req.cm_id = *cm_id;
346 	req.req.vdev_id = wlan_vdev_get_id(cm_ctx->vdev);
347 	req.req.source = CM_ROAM_DISCONNECT;
348 	wlan_vdev_get_bss_peer_mac(cm_ctx->vdev, &req.req.bssid);
349 
350 	discon_req = qdf_mem_malloc(sizeof(*discon_req));
351 	if (!discon_req)
352 		return QDF_STATUS_E_NOMEM;
353 
354 	discon_req->cm_id = *cm_id;
355 	discon_req->req.vdev_id = req.req.vdev_id;
356 	qdf_copy_macaddr(&discon_req->req.bssid,
357 			 &req.req.bssid);
358 	cm_update_scan_mlme_on_disconnect(cm_ctx->vdev, discon_req);
359 	qdf_mem_free(discon_req);
360 
361 	return mlme_cm_disconnect_req(cm_ctx->vdev, &req);
362 }
363 
cm_reassoc_disconnect_complete(struct cnx_mgr * cm_ctx,struct wlan_cm_discon_rsp * resp)364 QDF_STATUS cm_reassoc_disconnect_complete(struct cnx_mgr *cm_ctx,
365 					  struct wlan_cm_discon_rsp *resp)
366 {
367 	QDF_STATUS status;
368 	struct cm_req *cm_req;
369 	struct qdf_mac_addr *bssid;
370 	uint8_t vdev_id = wlan_vdev_get_id(cm_ctx->vdev);
371 	wlan_cm_id cm_id = cm_ctx->active_cm_id;
372 
373 	cm_req = cm_get_req_by_cm_id(cm_ctx, cm_id);
374 	if (!cm_req)
375 		return QDF_STATUS_E_INVAL;
376 
377 	mlme_cm_disconnect_complete_ind(cm_ctx->vdev, resp);
378 	bssid = &cm_req->roam_req.cur_candidate->entry->bssid;
379 
380 	status = mlme_cm_bss_peer_create_req(cm_ctx->vdev, bssid, NULL, false);
381 	if (QDF_IS_STATUS_ERROR(status)) {
382 		mlme_err(CM_PREFIX_FMT "Peer create request failed",
383 			 CM_PREFIX_REF(vdev_id, cm_id));
384 		status = cm_send_reassoc_start_fail(cm_ctx, cm_id,
385 						    CM_PEER_CREATE_FAILED,
386 						    true);
387 	}
388 
389 	return status;
390 }
391 
392 QDF_STATUS
cm_resume_reassoc_after_peer_create(struct cnx_mgr * cm_ctx,wlan_cm_id * cm_id)393 cm_resume_reassoc_after_peer_create(struct cnx_mgr *cm_ctx, wlan_cm_id *cm_id)
394 {
395 	struct wlan_cm_vdev_reassoc_req *req;
396 	struct cm_req *cm_req;
397 	QDF_STATUS status;
398 
399 	cm_req = cm_get_req_by_cm_id(cm_ctx, *cm_id);
400 	if (!cm_req)
401 		return QDF_STATUS_E_FAILURE;
402 
403 	req = qdf_mem_malloc(sizeof(*req));
404 	if (!req)
405 		return QDF_STATUS_E_NOMEM;
406 
407 	req->vdev_id = wlan_vdev_get_id(cm_ctx->vdev);
408 	req->cm_id = *cm_id;
409 	req->bss = cm_req->roam_req.cur_candidate;
410 
411 	mlme_nofl_info(CM_PREFIX_FMT "Reassoc to " QDF_SSID_FMT " " QDF_MAC_ADDR_FMT " rssi: %d freq: %d source %d",
412 		       CM_PREFIX_REF(req->vdev_id, req->cm_id),
413 		       QDF_SSID_REF(req->bss->entry->ssid.length,
414 				    req->bss->entry->ssid.ssid),
415 		       QDF_MAC_ADDR_REF(req->bss->entry->bssid.bytes),
416 		       req->bss->entry->rssi_raw,
417 		       req->bss->entry->channel.chan_freq,
418 		       cm_req->roam_req.req.source);
419 
420 	status = mlme_cm_reassoc_req(cm_ctx->vdev, req);
421 	if (QDF_IS_STATUS_ERROR(status)) {
422 		mlme_err(CM_PREFIX_FMT "Reassoc request failed",
423 			 CM_PREFIX_REF(req->vdev_id, req->cm_id));
424 		mlme_cm_bss_peer_delete_req(cm_ctx->vdev);
425 		status = cm_send_reassoc_start_fail(cm_ctx, *cm_id,
426 						    CM_JOIN_FAILED, true);
427 	}
428 
429 	qdf_mem_free(req);
430 	return status;
431 }
432 
cm_reassoc_complete(struct cnx_mgr * cm_ctx,struct wlan_cm_connect_resp * resp)433 QDF_STATUS cm_reassoc_complete(struct cnx_mgr *cm_ctx,
434 			       struct wlan_cm_connect_resp *resp)
435 {
436 	/*
437 	 * If the entry is not present in the list, it must have been cleared
438 	 * already.
439 	 */
440 	if (!cm_get_req_by_cm_id(cm_ctx, resp->cm_id))
441 		return QDF_STATUS_SUCCESS;
442 
443 	resp->is_reassoc = true;
444 	cm_connect_complete(cm_ctx, resp);
445 	/*
446 	 * If roaming fails and conn_sm is in ROAMING state, then
447 	 * initiate disconnect to cleanup and move conn_sm to INIT state
448 	 */
449 	if (QDF_IS_STATUS_ERROR(resp->connect_status) &&
450 	    cm_get_state(cm_ctx) == WLAN_CM_S_ROAMING) {
451 		cm_reassoc_fail_disconnect(cm_ctx->vdev, CM_ROAM_DISCONNECT,
452 					   REASON_UNSPEC_FAILURE,
453 					   &resp->bssid);
454 	}
455 
456 	return QDF_STATUS_SUCCESS;
457 }
458 
459 static void
cm_reassoc_handle_event_post_fail(struct cnx_mgr * cm_ctx,wlan_cm_id cm_id)460 cm_reassoc_handle_event_post_fail(struct cnx_mgr *cm_ctx, wlan_cm_id cm_id)
461 {
462 	struct wlan_cm_connect_resp *resp;
463 
464 	resp = qdf_mem_malloc(sizeof(*resp));
465 	if (!resp)
466 		return;
467 
468 	cm_fill_roam_fail_resp_from_cm_id(cm_ctx, resp, cm_id,
469 					  CM_GENERIC_FAILURE);
470 	cm_reassoc_complete(cm_ctx, resp);
471 	qdf_mem_free(resp);
472 }
473 
cm_reassoc_cmd_timeout(struct cnx_mgr * cm_ctx,wlan_cm_id cm_id)474 static QDF_STATUS cm_reassoc_cmd_timeout(struct cnx_mgr *cm_ctx,
475 					 wlan_cm_id cm_id)
476 {
477 	struct wlan_cm_connect_resp *resp;
478 	QDF_STATUS status;
479 
480 	resp = qdf_mem_malloc(sizeof(*resp));
481 	if (!resp)
482 		return QDF_STATUS_E_NOMEM;
483 
484 	cm_fill_roam_fail_resp_from_cm_id(cm_ctx, resp, cm_id, CM_SER_TIMEOUT);
485 	status = cm_sm_deliver_event(cm_ctx->vdev,
486 				     WLAN_CM_SM_EV_REASSOC_FAILURE,
487 				     sizeof(*resp), resp);
488 	if (QDF_IS_STATUS_ERROR(status))
489 		cm_reassoc_complete(cm_ctx, resp);
490 
491 	qdf_mem_free(resp);
492 
493 	return status;
494 }
495 
496 #ifdef WLAN_CM_USE_SPINLOCK
cm_activate_reassoc_req_sched_cb(struct scheduler_msg * msg)497 static QDF_STATUS cm_activate_reassoc_req_sched_cb(struct scheduler_msg *msg)
498 {
499 	struct wlan_serialization_command *cmd = msg->bodyptr;
500 	struct wlan_objmgr_vdev *vdev;
501 	struct cnx_mgr *cm_ctx;
502 	QDF_STATUS ret = QDF_STATUS_E_FAILURE;
503 
504 	if (!cmd || !cmd->vdev) {
505 		mlme_err("Invalid Input");
506 		return QDF_STATUS_E_INVAL;
507 	}
508 
509 	vdev = cmd->vdev;
510 	cm_ctx = cm_get_cm_ctx(vdev);
511 	if (!cm_ctx)
512 		return QDF_STATUS_E_INVAL;
513 
514 	ret = cm_sm_deliver_event(vdev,
515 				  WLAN_CM_SM_EV_REASSOC_ACTIVE,
516 				  sizeof(wlan_cm_id),
517 				  &cmd->cmd_id);
518 	/*
519 	 * Called from scheduler context hence posting failure
520 	 */
521 	if (QDF_IS_STATUS_ERROR(ret))
522 		cm_reassoc_handle_event_post_fail(cm_ctx, cmd->cmd_id);
523 
524 	wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_CM_ID);
525 	return ret;
526 }
527 
528 static QDF_STATUS
cm_activate_reassoc_req(struct wlan_serialization_command * cmd)529 cm_activate_reassoc_req(struct wlan_serialization_command *cmd)
530 {
531 	struct wlan_objmgr_vdev *vdev = cmd->vdev;
532 	struct scheduler_msg msg = {0};
533 	QDF_STATUS ret;
534 
535 	msg.bodyptr = cmd;
536 	msg.callback = cm_activate_reassoc_req_sched_cb;
537 	msg.flush_callback = cm_activate_cmd_req_flush_cb;
538 
539 	ret = wlan_objmgr_vdev_try_get_ref(vdev, WLAN_MLME_CM_ID);
540 	if (QDF_IS_STATUS_ERROR(ret))
541 		return ret;
542 
543 	ret = scheduler_post_message(QDF_MODULE_ID_MLME,
544 				     QDF_MODULE_ID_MLME,
545 				     QDF_MODULE_ID_MLME, &msg);
546 
547 	if (QDF_IS_STATUS_ERROR(ret)) {
548 		mlme_err(CM_PREFIX_FMT "Failed to post scheduler_msg",
549 			 CM_PREFIX_REF(wlan_vdev_get_id(vdev), cmd->cmd_id));
550 		wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_CM_ID);
551 		return ret;
552 	}
553 
554 	return ret;
555 }
556 #else
557 static QDF_STATUS
cm_activate_reassoc_req(struct wlan_serialization_command * cmd)558 cm_activate_reassoc_req(struct wlan_serialization_command *cmd)
559 {
560 	return cm_sm_deliver_event(cmd->vdev,
561 				   WLAN_CM_SM_EV_REASSOC_ACTIVE,
562 				   sizeof(wlan_cm_id),
563 				   &cmd->cmd_id);
564 }
565 #endif
566 
567 static QDF_STATUS
cm_ser_reassoc_cb(struct wlan_serialization_command * cmd,enum wlan_serialization_cb_reason reason)568 cm_ser_reassoc_cb(struct wlan_serialization_command *cmd,
569 		  enum wlan_serialization_cb_reason reason)
570 {
571 	QDF_STATUS status = QDF_STATUS_SUCCESS;
572 	struct wlan_objmgr_vdev *vdev;
573 	struct cnx_mgr *cm_ctx;
574 	enum qdf_hang_reason hang_reason =
575 				QDF_VDEV_ACTIVE_SER_REASSOC_TIMEOUT;
576 
577 	if (!cmd) {
578 		mlme_err("cmd is NULL, reason: %d", reason);
579 		QDF_ASSERT(0);
580 		return QDF_STATUS_E_NULL_VALUE;
581 	}
582 
583 	vdev = cmd->vdev;
584 	cm_ctx = cm_get_cm_ctx(vdev);
585 	if (!cm_ctx)
586 		return QDF_STATUS_E_NULL_VALUE;
587 
588 	switch (reason) {
589 	case WLAN_SER_CB_ACTIVATE_CMD:
590 		/*
591 		 * For pending to active reason, use async api to take lock.
592 		 * For direct activation use sync api to avoid taking lock
593 		 * as lock is already acquired by the requester.
594 		 */
595 		if (cmd->activation_reason == SER_PENDING_TO_ACTIVE)
596 			status = cm_activate_reassoc_req(cmd);
597 		else
598 			status = cm_sm_deliver_event_sync(
599 					cm_ctx, WLAN_CM_SM_EV_REASSOC_ACTIVE,
600 					sizeof(wlan_cm_id), &cmd->cmd_id);
601 
602 		if (QDF_IS_STATUS_SUCCESS(status))
603 			break;
604 		/*
605 		 * Handle failure if posting fails, i.e. the SM state has
606 		 * changed or head cm_id doesn't match the active cm_id.
607 		 * connect active should be handled only in JOIN_PENDING. If
608 		 * new command has been received connect activation should be
609 		 * aborted from here with connect req cleanup.
610 		 */
611 		cm_reassoc_handle_event_post_fail(cm_ctx, cmd->cmd_id);
612 		break;
613 	case WLAN_SER_CB_CANCEL_CMD:
614 		/* command removed from pending list. */
615 		break;
616 	case WLAN_SER_CB_ACTIVE_CMD_TIMEOUT:
617 		mlme_err(CM_PREFIX_FMT "Active command timeout",
618 			 CM_PREFIX_REF(wlan_vdev_get_id(vdev), cmd->cmd_id));
619 		cm_trigger_panic_on_cmd_timeout(cm_ctx->vdev, hang_reason);
620 		cm_reassoc_cmd_timeout(cm_ctx, cmd->cmd_id);
621 		break;
622 	case WLAN_SER_CB_RELEASE_MEM_CMD:
623 		cm_reset_active_cm_id(vdev, cmd->cmd_id);
624 		wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_CM_ID);
625 		break;
626 	default:
627 		QDF_ASSERT(0);
628 		status = QDF_STATUS_E_INVAL;
629 		break;
630 	}
631 
632 	return status;
633 }
634 
635 #define REASSOC_TIMEOUT	10000
cm_ser_reassoc_req(struct cnx_mgr * cm_ctx,struct cm_roam_req * cm_req)636 static QDF_STATUS cm_ser_reassoc_req(struct cnx_mgr *cm_ctx,
637 				     struct cm_roam_req *cm_req)
638 {
639 	struct wlan_serialization_command cmd = {0, };
640 	enum wlan_serialization_status ser_cmd_status;
641 	QDF_STATUS status;
642 	uint8_t vdev_id = wlan_vdev_get_id(cm_ctx->vdev);
643 
644 	mlme_cm_osif_roam_sync_ind(cm_ctx->vdev);
645 
646 	status = wlan_objmgr_vdev_try_get_ref(cm_ctx->vdev, WLAN_MLME_CM_ID);
647 	if (QDF_IS_STATUS_ERROR(status)) {
648 		mlme_err(CM_PREFIX_FMT "unable to get reference",
649 			 CM_PREFIX_REF(vdev_id, cm_req->cm_id));
650 		return status;
651 	}
652 
653 	cmd.cmd_type = WLAN_SER_CMD_VDEV_ROAM;
654 	cmd.cmd_id = cm_req->cm_id;
655 	cmd.cmd_cb = cm_ser_reassoc_cb;
656 	cmd.source = WLAN_UMAC_COMP_MLME;
657 	cmd.is_high_priority = false;
658 	cmd.cmd_timeout_duration = REASSOC_TIMEOUT;
659 	cmd.vdev = cm_ctx->vdev;
660 	cmd.is_blocking = cm_ser_get_blocking_cmd();
661 
662 	ser_cmd_status = wlan_serialization_request(&cmd);
663 	switch (ser_cmd_status) {
664 	case WLAN_SER_CMD_PENDING:
665 		/* command moved to pending list.Do nothing */
666 		break;
667 	case WLAN_SER_CMD_ACTIVE:
668 		/* command moved to active list. Do nothing */
669 		break;
670 	default:
671 		mlme_err(CM_PREFIX_FMT "ser cmd status %d",
672 			 CM_PREFIX_REF(vdev_id, cm_req->cm_id), ser_cmd_status);
673 		wlan_objmgr_vdev_release_ref(cm_ctx->vdev, WLAN_MLME_CM_ID);
674 
675 		return QDF_STATUS_E_FAILURE;
676 	}
677 
678 	return QDF_STATUS_SUCCESS;
679 }
680 
681 #ifdef WLAN_POLICY_MGR_ENABLE
682 QDF_STATUS
cm_handle_reassoc_hw_mode_change(struct cnx_mgr * cm_ctx,wlan_cm_id * cm_id,enum wlan_cm_sm_evt event)683 cm_handle_reassoc_hw_mode_change(struct cnx_mgr *cm_ctx, wlan_cm_id *cm_id,
684 				 enum wlan_cm_sm_evt event)
685 {
686 	struct cm_req *cm_req;
687 	enum wlan_cm_connect_fail_reason reason = CM_GENERIC_FAILURE;
688 	struct wlan_objmgr_pdev *pdev;
689 	QDF_STATUS status;
690 
691 	if (!cm_id)
692 		return QDF_STATUS_E_FAILURE;
693 
694 	cm_req = cm_get_req_by_cm_id(cm_ctx, *cm_id);
695 	if (!cm_req)
696 		return QDF_STATUS_E_INVAL;
697 
698 	pdev = wlan_vdev_get_pdev(cm_ctx->vdev);
699 	if (!pdev) {
700 		mlme_err(CM_PREFIX_FMT "Failed to find pdev",
701 			 CM_PREFIX_REF(wlan_vdev_get_id(cm_ctx->vdev),
702 				       cm_req->cm_id));
703 		goto send_failure;
704 	}
705 
706 	if (event == WLAN_CM_SM_EV_HW_MODE_SUCCESS) {
707 		status = cm_ser_reassoc_req(cm_ctx, &cm_req->roam_req);
708 		if (QDF_IS_STATUS_ERROR(status)) {
709 			reason = CM_SER_FAILURE;
710 			goto send_failure;
711 		}
712 		return status;
713 	}
714 
715 	/* Set reason HW mode fail for event WLAN_CM_SM_EV_HW_MODE_FAILURE */
716 	reason = CM_HW_MODE_FAILURE;
717 
718 send_failure:
719 	return cm_send_reassoc_start_fail(cm_ctx, cm_req->cm_id, reason, true);
720 }
721 
cm_reassoc_hw_mode_change_resp(struct wlan_objmgr_pdev * pdev,uint8_t vdev_id,wlan_cm_id cm_id,QDF_STATUS status)722 void cm_reassoc_hw_mode_change_resp(struct wlan_objmgr_pdev *pdev,
723 				    uint8_t vdev_id, wlan_cm_id cm_id,
724 				    QDF_STATUS status)
725 {
726 	struct wlan_objmgr_vdev *vdev;
727 	QDF_STATUS qdf_status;
728 	enum wlan_cm_sm_evt event = WLAN_CM_SM_EV_HW_MODE_SUCCESS;
729 	struct cnx_mgr *cm_ctx;
730 
731 	mlme_debug(CM_PREFIX_FMT "Continue Reassoc after HW mode change, status %d",
732 		   CM_PREFIX_REF(vdev_id, cm_id), status);
733 
734 	vdev = wlan_objmgr_get_vdev_by_id_from_pdev(pdev, vdev_id,
735 						    WLAN_MLME_CM_ID);
736 	if (!vdev)
737 		return;
738 
739 	cm_ctx = cm_get_cm_ctx(vdev);
740 	if (!cm_ctx) {
741 		wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_CM_ID);
742 		return;
743 	}
744 
745 	if (QDF_IS_STATUS_ERROR(status))
746 		event = WLAN_CM_SM_EV_HW_MODE_FAILURE;
747 	qdf_status = cm_sm_deliver_event(vdev, event, sizeof(wlan_cm_id),
748 					 &cm_id);
749 
750 	/*
751 	 * Handle failure if posting fails, i.e. the SM state has
752 	 * changed or head cm_id doesn't match the active cm_id.
753 	 * hw mode change resp should be handled in REASSOC state. If
754 	 * new command has been received reassoc should be
755 	 * aborted from here with reassoc req cleanup.
756 	 */
757 	if (QDF_IS_STATUS_ERROR(qdf_status))
758 		cm_reassoc_handle_event_post_fail(cm_ctx, cm_id);
759 	wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_CM_ID);
760 }
761 
762 static QDF_STATUS
cm_check_for_reassoc_hw_mode_change(struct cnx_mgr * cm_ctx,struct cm_roam_req * cm_req)763 cm_check_for_reassoc_hw_mode_change(struct cnx_mgr *cm_ctx,
764 				    struct cm_roam_req *cm_req)
765 {
766 	qdf_freq_t candidate_freq;
767 	struct wlan_objmgr_psoc *psoc;
768 	QDF_STATUS status;
769 
770 	psoc = wlan_vdev_get_psoc(cm_ctx->vdev);
771 	if (!psoc)
772 		return QDF_STATUS_E_INVAL;
773 
774 	if (!cm_req->cur_candidate)
775 		return QDF_STATUS_E_EMPTY;
776 
777 	candidate_freq = cm_req->cur_candidate->entry->channel.chan_freq;
778 	status = policy_mgr_handle_conc_multiport(
779 			psoc, cm_req->req.vdev_id,
780 			candidate_freq, POLICY_MGR_UPDATE_REASON_LFR2_ROAM,
781 			cm_req->cm_id);
782 	if (status == QDF_STATUS_E_NOSUPPORT)
783 		status = QDF_STATUS_E_ALREADY;
784 
785 	return status;
786 }
787 #else
788 static inline QDF_STATUS
cm_check_for_reassoc_hw_mode_change(struct cnx_mgr * cm_ctx,struct cm_roam_req * cm_req)789 cm_check_for_reassoc_hw_mode_change(struct cnx_mgr *cm_ctx,
790 				    struct cm_roam_req *cm_req)
791 {
792 	return QDF_STATUS_E_ALREADY;
793 }
794 #endif
795 
cm_reassoc_start(struct cnx_mgr * cm_ctx,struct cm_roam_req * cm_req)796 QDF_STATUS cm_reassoc_start(struct cnx_mgr *cm_ctx,
797 			    struct cm_roam_req *cm_req)
798 {
799 	QDF_STATUS status = QDF_STATUS_E_FAILURE;
800 	uint8_t vdev_id = wlan_vdev_get_id(cm_ctx->vdev);
801 	enum wlan_cm_connect_fail_reason reason = CM_GENERIC_FAILURE;
802 
803 	status = cm_check_for_reassoc_hw_mode_change(cm_ctx, cm_req);
804 	if (QDF_IS_STATUS_ERROR(status) && status != QDF_STATUS_E_ALREADY) {
805 		reason = CM_HW_MODE_FAILURE;
806 		mlme_err(CM_PREFIX_FMT "Failed to set HW mode change status %d",
807 			 CM_PREFIX_REF(vdev_id, cm_req->cm_id), status);
808 		goto err;
809 	} else if (QDF_IS_STATUS_SUCCESS(status)) {
810 		mlme_debug(CM_PREFIX_FMT "Reassoc will continue after HW mode change",
811 			   CM_PREFIX_REF(vdev_id, cm_req->cm_id));
812 		return QDF_STATUS_SUCCESS;
813 	}
814 
815 	status = cm_ser_reassoc_req(cm_ctx, cm_req);
816 	if (QDF_IS_STATUS_SUCCESS(status))
817 		return status;
818 
819 	reason = CM_SER_FAILURE;
820 	mlme_err(CM_PREFIX_FMT "Serialization of reassoc failed",
821 		 CM_PREFIX_REF(vdev_id, cm_req->cm_id));
822 err:
823 	return cm_send_reassoc_start_fail(cm_ctx, cm_req->cm_id, reason, true);
824 }
825 
cm_reassoc_rsp(struct wlan_objmgr_vdev * vdev,struct wlan_cm_connect_resp * resp)826 QDF_STATUS cm_reassoc_rsp(struct wlan_objmgr_vdev *vdev,
827 			  struct wlan_cm_connect_resp *resp)
828 {
829 	struct cnx_mgr *cm_ctx;
830 	QDF_STATUS qdf_status;
831 	wlan_cm_id cm_id;
832 	uint32_t prefix;
833 	enum wlan_cm_sm_evt event;
834 	struct qdf_mac_addr pmksa_mac = QDF_MAC_ADDR_ZERO_INIT;
835 
836 	cm_ctx = cm_get_cm_ctx(vdev);
837 	if (!cm_ctx)
838 		return QDF_STATUS_E_INVAL;
839 
840 	cm_id = cm_ctx->active_cm_id;
841 	prefix = CM_ID_GET_PREFIX(cm_id);
842 
843 	if (prefix != ROAM_REQ_PREFIX ||
844 	    cm_id != resp->cm_id) {
845 		mlme_err(CM_PREFIX_FMT " Active cm_id 0x%x is different",
846 			 CM_PREFIX_REF(wlan_vdev_get_id(vdev), resp->cm_id),
847 			 cm_id);
848 		qdf_status = QDF_STATUS_E_FAILURE;
849 		goto post_err;
850 	}
851 
852 	cm_connect_rsp_get_mld_addr_or_bssid(resp, &pmksa_mac);
853 
854 	if (QDF_IS_STATUS_SUCCESS(resp->connect_status)) {
855 		/*
856 		 * On successful connection to sae single pmk AP,
857 		 * clear all the single pmk AP.
858 		 */
859 		if (cm_is_cm_id_current_candidate_single_pmk(cm_ctx, cm_id))
860 			wlan_crypto_selective_clear_sae_single_pmk_entries(
861 					vdev, &pmksa_mac);
862 		event = WLAN_CM_SM_EV_REASSOC_DONE;
863 	} else {
864 		event = WLAN_CM_SM_EV_REASSOC_FAILURE;
865 	}
866 
867 	qdf_status = cm_sm_deliver_event(cm_ctx->vdev, event, sizeof(*resp),
868 					 resp);
869 	if (QDF_IS_STATUS_SUCCESS(qdf_status))
870 		return qdf_status;
871 post_err:
872 	cm_reassoc_complete(cm_ctx, resp);
873 
874 	return qdf_status;
875 }
876 
cm_roam_bss_peer_create_rsp(struct wlan_objmgr_vdev * vdev,QDF_STATUS status,struct qdf_mac_addr * peer_mac)877 QDF_STATUS cm_roam_bss_peer_create_rsp(struct wlan_objmgr_vdev *vdev,
878 				       QDF_STATUS status,
879 				       struct qdf_mac_addr *peer_mac)
880 {
881 	struct cnx_mgr *cm_ctx;
882 	QDF_STATUS qdf_status;
883 	wlan_cm_id cm_id;
884 	uint32_t prefix;
885 	struct cm_req *cm_req;
886 
887 	cm_ctx = cm_get_cm_ctx(vdev);
888 	if (!cm_ctx)
889 		return QDF_STATUS_E_INVAL;
890 
891 	cm_id = cm_ctx->active_cm_id;
892 	prefix = CM_ID_GET_PREFIX(cm_id);
893 
894 	if (prefix != ROAM_REQ_PREFIX) {
895 		mlme_err(CM_PREFIX_FMT "Active req is not roam req",
896 			 CM_PREFIX_REF(wlan_vdev_get_id(cm_ctx->vdev), cm_id));
897 		mlme_cm_bss_peer_delete_req(vdev);
898 		return QDF_STATUS_E_INVAL;
899 	}
900 
901 	if (QDF_IS_STATUS_SUCCESS(status)) {
902 		qdf_status = cm_bss_peer_create_resp_mlo_attach(vdev, peer_mac);
903 		if (QDF_IS_STATUS_ERROR(qdf_status)) {
904 			mlme_cm_bss_peer_delete_req(vdev);
905 			goto send_err;
906 		}
907 
908 		qdf_status =
909 		    cm_sm_deliver_event(vdev,
910 					WLAN_CM_SM_EV_BSS_CREATE_PEER_SUCCESS,
911 					sizeof(wlan_cm_id), &cm_id);
912 
913 		if (QDF_IS_STATUS_ERROR(qdf_status)) {
914 			mlme_cm_bss_peer_delete_req(vdev);
915 			cm_reassoc_handle_event_post_fail(cm_ctx, cm_id);
916 		}
917 
918 		return qdf_status;
919 	}
920 
921 send_err:
922 	cm_req = cm_get_req_by_cm_id(cm_ctx, cm_id);
923 	if (!cm_req)
924 		return QDF_STATUS_E_INVAL;
925 
926 	return cm_send_reassoc_start_fail(cm_ctx, cm_id,
927 					  CM_PEER_CREATE_FAILED, false);
928 }
929 
cm_roam_disconnect_rsp(struct wlan_objmgr_vdev * vdev,struct wlan_cm_discon_rsp * resp)930 QDF_STATUS cm_roam_disconnect_rsp(struct wlan_objmgr_vdev *vdev,
931 				  struct wlan_cm_discon_rsp *resp)
932 {
933 	struct cnx_mgr *cm_ctx;
934 	QDF_STATUS qdf_status;
935 	wlan_cm_id cm_id;
936 	uint32_t prefix;
937 
938 	cm_ctx = cm_get_cm_ctx(vdev);
939 	if (!cm_ctx)
940 		return QDF_STATUS_E_INVAL;
941 
942 	cm_id = cm_ctx->active_cm_id;
943 	prefix = CM_ID_GET_PREFIX(cm_id);
944 
945 	if (prefix != ROAM_REQ_PREFIX || cm_id != resp->req.cm_id) {
946 		mlme_err(CM_PREFIX_FMT "Active cm_id 0x%x is different",
947 			 CM_PREFIX_REF(wlan_vdev_get_id(vdev), resp->req.cm_id),
948 			 cm_id);
949 		qdf_status = QDF_STATUS_E_FAILURE;
950 		goto disconnect_complete;
951 	}
952 	qdf_status =
953 		cm_sm_deliver_event(vdev,
954 				    WLAN_CM_SM_EV_HO_ROAM_DISCONNECT_DONE,
955 				    sizeof(*resp), resp);
956 	if (QDF_IS_STATUS_SUCCESS(qdf_status))
957 		return qdf_status;
958 
959 disconnect_complete:
960 	cm_reassoc_handle_event_post_fail(cm_ctx, cm_id);
961 	return qdf_status;
962 }
963