xref: /wlan-dirver/qca-wifi-host-cmn/umac/mlme/connection_mgr/core/src/wlan_cm_host_roam.c (revision 901120c066e139c7f8a2c8e4820561fdd83c67ef)
1 /*
2  * Copyright (c) 2011-2021 The Linux Foundation. All rights reserved.
3  * Copyright (c) 2022 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
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
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
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
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
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 
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 
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
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 
262 static QDF_STATUS cm_host_roam_start(struct cnx_mgr *cm_ctx,
263 				     struct cm_req *cm_req)
264 {
265 	struct wlan_cm_roam_req *req;
266 	struct qdf_mac_addr connected_bssid;
267 
268 	req = &cm_req->roam_req.req;
269 
270 	wlan_vdev_get_bss_peer_mac(cm_ctx->vdev, &connected_bssid);
271 	if (qdf_is_macaddr_equal(&req->bssid, &connected_bssid)) {
272 		mlme_info(CM_PREFIX_FMT "Self reassoc with" QDF_MAC_ADDR_FMT,
273 			  CM_PREFIX_REF(wlan_vdev_get_id(cm_ctx->vdev),
274 					cm_req->cm_id),
275 			  QDF_MAC_ADDR_REF(req->bssid.bytes));
276 		req->self_reassoc = true;
277 	}
278 
279 	/* if self reassoc continue with reassoc and skip preauth */
280 	if (req->self_reassoc)
281 		return cm_sm_deliver_event_sync(cm_ctx,
282 						WLAN_CM_SM_EV_START_REASSOC,
283 						sizeof(cm_req->roam_req),
284 						&cm_req->roam_req);
285 	/*
286 	 * if not self reassoc reset cur candidate to perform preauth with
287 	 * all candidate.
288 	 */
289 	cm_req->roam_req.cur_candidate = NULL;
290 	return cm_host_roam_preauth_start(cm_ctx, cm_req);
291 }
292 
293 static
294 QDF_STATUS cm_host_roam_start_fail(struct cnx_mgr *cm_ctx,
295 				   struct cm_req *cm_req,
296 				   enum wlan_cm_connect_fail_reason reason)
297 {
298 	cm_send_preauth_start_fail(cm_ctx, cm_req->cm_id, reason);
299 
300 	return QDF_STATUS_SUCCESS;
301 }
302 #else
303 static QDF_STATUS cm_host_roam_start(struct cnx_mgr *cm_ctx,
304 				     struct cm_req *cm_req)
305 {
306 	return cm_sm_deliver_event_sync(cm_ctx, WLAN_CM_SM_EV_START_REASSOC,
307 					sizeof(cm_req->roam_req),
308 					&cm_req->roam_req);
309 }
310 
311 static
312 QDF_STATUS cm_host_roam_start_fail(struct cnx_mgr *cm_ctx,
313 				   struct cm_req *cm_req,
314 				   enum wlan_cm_connect_fail_reason reason)
315 {
316 	return cm_send_reassoc_start_fail(cm_ctx, cm_req->cm_id, reason, true);
317 }
318 #endif
319 
320 QDF_STATUS cm_host_roam_start_req(struct cnx_mgr *cm_ctx,
321 				  struct cm_req *cm_req)
322 {
323 	QDF_STATUS status;
324 	struct wlan_objmgr_pdev *pdev;
325 	enum wlan_cm_connect_fail_reason reason = CM_GENERIC_FAILURE;
326 
327 	mlme_cm_roam_start_ind(cm_ctx->vdev, &cm_req->roam_req.req);
328 
329 	pdev = wlan_vdev_get_pdev(cm_ctx->vdev);
330 	if (!pdev) {
331 		reason = CM_GENERIC_FAILURE;
332 		goto roam_err;
333 	}
334 
335 	status = cm_roam_get_candidates(pdev, cm_ctx, &cm_req->roam_req);
336 	if (QDF_IS_STATUS_ERROR(status)) {
337 		reason = CM_NO_CANDIDATE_FOUND;
338 		goto roam_err;
339 	}
340 
341 	status = cm_host_roam_start(cm_ctx, cm_req);
342 	if (QDF_IS_STATUS_SUCCESS(status))
343 		return status;
344 
345 roam_err:
346 	return cm_host_roam_start_fail(cm_ctx, cm_req, reason);
347 }
348 
349 bool cm_roam_resp_cmid_match_list_head(struct cnx_mgr *cm_ctx,
350 				       struct wlan_cm_connect_resp *resp)
351 {
352 	return cm_check_cmid_match_list_head(cm_ctx, &resp->cm_id);
353 }
354 
355 QDF_STATUS cm_reassoc_active(struct cnx_mgr *cm_ctx, wlan_cm_id *cm_id)
356 {
357 	struct cm_req *cm_req;
358 	struct wlan_cm_vdev_discon_req req;
359 	struct cm_disconnect_req *discon_req;
360 
361 	cm_req = cm_get_req_by_cm_id(cm_ctx, *cm_id);
362 	if (!cm_req)
363 		return QDF_STATUS_E_INVAL;
364 
365 	cm_ctx->active_cm_id = *cm_id;
366 
367 	/* For self reassoc no need to disconnect or create peer */
368 	if (cm_req->roam_req.req.self_reassoc)
369 		return cm_resume_reassoc_after_peer_create(cm_ctx, cm_id);
370 
371 	qdf_mem_zero(&req, sizeof(req));
372 	req.cm_id = *cm_id;
373 	req.req.vdev_id = wlan_vdev_get_id(cm_ctx->vdev);
374 	req.req.source = CM_ROAM_DISCONNECT;
375 	wlan_vdev_get_bss_peer_mac(cm_ctx->vdev, &req.req.bssid);
376 
377 	discon_req = qdf_mem_malloc(sizeof(*discon_req));
378 	if (!discon_req)
379 		return QDF_STATUS_E_NOMEM;
380 
381 	discon_req->cm_id = *cm_id;
382 	discon_req->req.vdev_id = req.req.vdev_id;
383 	qdf_copy_macaddr(&discon_req->req.bssid,
384 			 &req.req.bssid);
385 	cm_update_scan_mlme_on_disconnect(cm_ctx->vdev, discon_req);
386 	qdf_mem_free(discon_req);
387 
388 	return mlme_cm_disconnect_req(cm_ctx->vdev, &req);
389 }
390 
391 QDF_STATUS cm_reassoc_disconnect_complete(struct cnx_mgr *cm_ctx,
392 					  struct wlan_cm_discon_rsp *resp)
393 {
394 	QDF_STATUS status;
395 	struct cm_req *cm_req;
396 	struct qdf_mac_addr *bssid;
397 	uint8_t vdev_id = wlan_vdev_get_id(cm_ctx->vdev);
398 	wlan_cm_id cm_id = cm_ctx->active_cm_id;
399 
400 	cm_req = cm_get_req_by_cm_id(cm_ctx, cm_id);
401 	if (!cm_req)
402 		return QDF_STATUS_E_INVAL;
403 
404 	mlme_cm_disconnect_complete_ind(cm_ctx->vdev, resp);
405 	bssid = &cm_req->roam_req.cur_candidate->entry->bssid;
406 
407 	status = mlme_cm_bss_peer_create_req(cm_ctx->vdev, bssid, NULL, false);
408 	if (QDF_IS_STATUS_ERROR(status)) {
409 		mlme_err(CM_PREFIX_FMT "Peer create request failed",
410 			 CM_PREFIX_REF(vdev_id, cm_id));
411 		status = cm_send_reassoc_start_fail(cm_ctx, cm_id,
412 						    CM_PEER_CREATE_FAILED,
413 						    true);
414 	}
415 
416 	return status;
417 }
418 
419 QDF_STATUS
420 cm_resume_reassoc_after_peer_create(struct cnx_mgr *cm_ctx, wlan_cm_id *cm_id)
421 {
422 	struct wlan_cm_vdev_reassoc_req *req;
423 	struct cm_req *cm_req;
424 	QDF_STATUS status;
425 
426 	cm_req = cm_get_req_by_cm_id(cm_ctx, *cm_id);
427 	if (!cm_req)
428 		return QDF_STATUS_E_FAILURE;
429 
430 	req = qdf_mem_malloc(sizeof(*req));
431 	if (!req)
432 		return QDF_STATUS_E_NOMEM;
433 
434 	req->vdev_id = wlan_vdev_get_id(cm_ctx->vdev);
435 	req->cm_id = *cm_id;
436 	req->self_reassoc = cm_req->roam_req.req.self_reassoc;
437 	req->bss = cm_req->roam_req.cur_candidate;
438 
439 	mlme_nofl_info(CM_PREFIX_FMT "Reassoc to " QDF_SSID_FMT " " QDF_MAC_ADDR_FMT " rssi: %d freq: %d source %d",
440 		       CM_PREFIX_REF(req->vdev_id, req->cm_id),
441 		       QDF_SSID_REF(req->bss->entry->ssid.length,
442 				    req->bss->entry->ssid.ssid),
443 		       QDF_MAC_ADDR_REF(req->bss->entry->bssid.bytes),
444 		       req->bss->entry->rssi_raw,
445 		       req->bss->entry->channel.chan_freq,
446 		       cm_req->roam_req.req.source);
447 
448 	status = mlme_cm_reassoc_req(cm_ctx->vdev, req);
449 	if (QDF_IS_STATUS_ERROR(status)) {
450 		mlme_err(CM_PREFIX_FMT "Reassoc request failed",
451 			 CM_PREFIX_REF(req->vdev_id, req->cm_id));
452 		/* Delete peer only if not self reassoc */
453 		if (!cm_req->roam_req.req.self_reassoc)
454 			mlme_cm_bss_peer_delete_req(cm_ctx->vdev);
455 		status = cm_send_reassoc_start_fail(cm_ctx, *cm_id,
456 						    CM_JOIN_FAILED, true);
457 	}
458 
459 	qdf_mem_free(req);
460 	return status;
461 }
462 
463 QDF_STATUS cm_reassoc_complete(struct cnx_mgr *cm_ctx,
464 			       struct wlan_cm_connect_resp *resp)
465 {
466 	/*
467 	 * If the entry is not present in the list, it must have been cleared
468 	 * already.
469 	 */
470 	if (!cm_get_req_by_cm_id(cm_ctx, resp->cm_id))
471 		return QDF_STATUS_SUCCESS;
472 
473 	resp->is_reassoc = true;
474 	cm_connect_complete(cm_ctx, resp);
475 	/*
476 	 * If roaming fails and conn_sm is in ROAMING state, then
477 	 * initiate disconnect to cleanup and move conn_sm to INIT state
478 	 */
479 	if (QDF_IS_STATUS_ERROR(resp->connect_status) &&
480 	    cm_get_state(cm_ctx) == WLAN_CM_S_ROAMING) {
481 		cm_reassoc_fail_disconnect(cm_ctx->vdev, CM_ROAM_DISCONNECT,
482 					   REASON_UNSPEC_FAILURE,
483 					   &resp->bssid);
484 	}
485 
486 	return QDF_STATUS_SUCCESS;
487 }
488 
489 static void
490 cm_reassoc_handle_event_post_fail(struct cnx_mgr *cm_ctx, wlan_cm_id cm_id)
491 {
492 	struct wlan_cm_connect_resp *resp;
493 
494 	resp = qdf_mem_malloc(sizeof(*resp));
495 	if (!resp)
496 		return;
497 
498 	cm_fill_roam_fail_resp_from_cm_id(cm_ctx, resp, cm_id,
499 					  CM_GENERIC_FAILURE);
500 	cm_reassoc_complete(cm_ctx, resp);
501 	qdf_mem_free(resp);
502 }
503 
504 static QDF_STATUS cm_reassoc_cmd_timeout(struct cnx_mgr *cm_ctx,
505 					 wlan_cm_id cm_id)
506 {
507 	struct wlan_cm_connect_resp *resp;
508 	QDF_STATUS status;
509 
510 	resp = qdf_mem_malloc(sizeof(*resp));
511 	if (!resp)
512 		return QDF_STATUS_E_NOMEM;
513 
514 	cm_fill_roam_fail_resp_from_cm_id(cm_ctx, resp, cm_id, CM_SER_TIMEOUT);
515 	status = cm_sm_deliver_event(cm_ctx->vdev,
516 				     WLAN_CM_SM_EV_REASSOC_FAILURE,
517 				     sizeof(*resp), resp);
518 	if (QDF_IS_STATUS_ERROR(status))
519 		cm_reassoc_complete(cm_ctx, resp);
520 
521 	qdf_mem_free(resp);
522 
523 	return status;
524 }
525 
526 #ifdef WLAN_CM_USE_SPINLOCK
527 static QDF_STATUS cm_activate_reassoc_req_sched_cb(struct scheduler_msg *msg)
528 {
529 	struct wlan_serialization_command *cmd = msg->bodyptr;
530 	struct wlan_objmgr_vdev *vdev;
531 	struct cnx_mgr *cm_ctx;
532 	QDF_STATUS ret = QDF_STATUS_E_FAILURE;
533 
534 	if (!cmd || !cmd->vdev) {
535 		mlme_err("Invalid Input");
536 		return QDF_STATUS_E_INVAL;
537 	}
538 
539 	vdev = cmd->vdev;
540 	cm_ctx = cm_get_cm_ctx(vdev);
541 	if (!cm_ctx)
542 		return QDF_STATUS_E_INVAL;
543 
544 	ret = cm_sm_deliver_event(vdev,
545 				  WLAN_CM_SM_EV_REASSOC_ACTIVE,
546 				  sizeof(wlan_cm_id),
547 				  &cmd->cmd_id);
548 	/*
549 	 * Called from scheduler context hence posting failure
550 	 */
551 	if (QDF_IS_STATUS_ERROR(ret))
552 		cm_reassoc_handle_event_post_fail(cm_ctx, cmd->cmd_id);
553 
554 	wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_CM_ID);
555 	return ret;
556 }
557 
558 static QDF_STATUS
559 cm_activate_reassoc_req(struct wlan_serialization_command *cmd)
560 {
561 	struct wlan_objmgr_vdev *vdev = cmd->vdev;
562 	struct scheduler_msg msg = {0};
563 	QDF_STATUS ret;
564 
565 	msg.bodyptr = cmd;
566 	msg.callback = cm_activate_reassoc_req_sched_cb;
567 	msg.flush_callback = cm_activate_cmd_req_flush_cb;
568 
569 	ret = wlan_objmgr_vdev_try_get_ref(vdev, WLAN_MLME_CM_ID);
570 	if (QDF_IS_STATUS_ERROR(ret))
571 		return ret;
572 
573 	ret = scheduler_post_message(QDF_MODULE_ID_MLME,
574 				     QDF_MODULE_ID_MLME,
575 				     QDF_MODULE_ID_MLME, &msg);
576 
577 	if (QDF_IS_STATUS_ERROR(ret)) {
578 		mlme_err(CM_PREFIX_FMT "Failed to post scheduler_msg",
579 			 CM_PREFIX_REF(wlan_vdev_get_id(vdev), cmd->cmd_id));
580 		wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_CM_ID);
581 		return ret;
582 	}
583 
584 	return ret;
585 }
586 #else
587 static QDF_STATUS
588 cm_activate_reassoc_req(struct wlan_serialization_command *cmd)
589 {
590 	return cm_sm_deliver_event(cmd->vdev,
591 				   WLAN_CM_SM_EV_REASSOC_ACTIVE,
592 				   sizeof(wlan_cm_id),
593 				   &cmd->cmd_id);
594 }
595 #endif
596 
597 static QDF_STATUS
598 cm_ser_reassoc_cb(struct wlan_serialization_command *cmd,
599 		  enum wlan_serialization_cb_reason reason)
600 {
601 	QDF_STATUS status = QDF_STATUS_SUCCESS;
602 	struct wlan_objmgr_vdev *vdev;
603 	struct cnx_mgr *cm_ctx;
604 
605 	if (!cmd) {
606 		mlme_err("cmd is NULL, reason: %d", reason);
607 		QDF_ASSERT(0);
608 		return QDF_STATUS_E_NULL_VALUE;
609 	}
610 
611 	vdev = cmd->vdev;
612 	cm_ctx = cm_get_cm_ctx(vdev);
613 	if (!cm_ctx)
614 		return QDF_STATUS_E_NULL_VALUE;
615 
616 	switch (reason) {
617 	case WLAN_SER_CB_ACTIVATE_CMD:
618 		/*
619 		 * For pending to active reason, use async api to take lock.
620 		 * For direct activation use sync api to avoid taking lock
621 		 * as lock is already acquired by the requester.
622 		 */
623 		if (cmd->activation_reason == SER_PENDING_TO_ACTIVE)
624 			status = cm_activate_reassoc_req(cmd);
625 		else
626 			status = cm_sm_deliver_event_sync(
627 					cm_ctx, WLAN_CM_SM_EV_REASSOC_ACTIVE,
628 					sizeof(wlan_cm_id), &cmd->cmd_id);
629 
630 		if (QDF_IS_STATUS_SUCCESS(status))
631 			break;
632 		/*
633 		 * Handle failure if posting fails, i.e. the SM state has
634 		 * changed or head cm_id doesn't match the active cm_id.
635 		 * connect active should be handled only in JOIN_PENDING. If
636 		 * new command has been received connect activation should be
637 		 * aborted from here with connect req cleanup.
638 		 */
639 		cm_reassoc_handle_event_post_fail(cm_ctx, cmd->cmd_id);
640 		break;
641 	case WLAN_SER_CB_CANCEL_CMD:
642 		/* command removed from pending list. */
643 		break;
644 	case WLAN_SER_CB_ACTIVE_CMD_TIMEOUT:
645 		mlme_err(CM_PREFIX_FMT "Active command timeout",
646 			 CM_PREFIX_REF(wlan_vdev_get_id(vdev), cmd->cmd_id));
647 		cm_trigger_panic_on_cmd_timeout(cm_ctx->vdev);
648 		cm_reassoc_cmd_timeout(cm_ctx, cmd->cmd_id);
649 		break;
650 	case WLAN_SER_CB_RELEASE_MEM_CMD:
651 		cm_reset_active_cm_id(vdev, cmd->cmd_id);
652 		wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_CM_ID);
653 		break;
654 	default:
655 		QDF_ASSERT(0);
656 		status = QDF_STATUS_E_INVAL;
657 		break;
658 	}
659 
660 	return status;
661 }
662 
663 #define REASSOC_TIMEOUT	10000
664 static QDF_STATUS cm_ser_reassoc_req(struct cnx_mgr *cm_ctx,
665 				     struct cm_roam_req *cm_req)
666 {
667 	struct wlan_serialization_command cmd = {0, };
668 	enum wlan_serialization_status ser_cmd_status;
669 	QDF_STATUS status;
670 	uint8_t vdev_id = wlan_vdev_get_id(cm_ctx->vdev);
671 
672 	mlme_cm_osif_roam_sync_ind(cm_ctx->vdev);
673 
674 	status = wlan_objmgr_vdev_try_get_ref(cm_ctx->vdev, WLAN_MLME_CM_ID);
675 	if (QDF_IS_STATUS_ERROR(status)) {
676 		mlme_err(CM_PREFIX_FMT "unable to get reference",
677 			 CM_PREFIX_REF(vdev_id, cm_req->cm_id));
678 		return status;
679 	}
680 
681 	cmd.cmd_type = WLAN_SER_CMD_VDEV_ROAM;
682 	cmd.cmd_id = cm_req->cm_id;
683 	cmd.cmd_cb = cm_ser_reassoc_cb;
684 	cmd.source = WLAN_UMAC_COMP_MLME;
685 	cmd.is_high_priority = false;
686 	cmd.cmd_timeout_duration = REASSOC_TIMEOUT;
687 	cmd.vdev = cm_ctx->vdev;
688 	cmd.is_blocking = cm_ser_get_blocking_cmd();
689 
690 	ser_cmd_status = wlan_serialization_request(&cmd);
691 	switch (ser_cmd_status) {
692 	case WLAN_SER_CMD_PENDING:
693 		/* command moved to pending list.Do nothing */
694 		break;
695 	case WLAN_SER_CMD_ACTIVE:
696 		/* command moved to active list. Do nothing */
697 		break;
698 	default:
699 		mlme_err(CM_PREFIX_FMT "ser cmd status %d",
700 			 CM_PREFIX_REF(vdev_id, cm_req->cm_id), ser_cmd_status);
701 		wlan_objmgr_vdev_release_ref(cm_ctx->vdev, WLAN_MLME_CM_ID);
702 
703 		return QDF_STATUS_E_FAILURE;
704 	}
705 
706 	return QDF_STATUS_SUCCESS;
707 }
708 
709 #ifdef WLAN_POLICY_MGR_ENABLE
710 QDF_STATUS
711 cm_handle_reassoc_hw_mode_change(struct cnx_mgr *cm_ctx, wlan_cm_id *cm_id,
712 				 enum wlan_cm_sm_evt event)
713 {
714 	struct cm_req *cm_req;
715 	enum wlan_cm_connect_fail_reason reason = CM_GENERIC_FAILURE;
716 	struct wlan_objmgr_pdev *pdev;
717 	QDF_STATUS status;
718 
719 	if (!cm_id)
720 		return QDF_STATUS_E_FAILURE;
721 
722 	cm_req = cm_get_req_by_cm_id(cm_ctx, *cm_id);
723 	if (!cm_req)
724 		return QDF_STATUS_E_INVAL;
725 
726 	pdev = wlan_vdev_get_pdev(cm_ctx->vdev);
727 	if (!pdev) {
728 		mlme_err(CM_PREFIX_FMT "Failed to find pdev",
729 			 CM_PREFIX_REF(wlan_vdev_get_id(cm_ctx->vdev),
730 				       cm_req->cm_id));
731 		goto send_failure;
732 	}
733 
734 	if (event == WLAN_CM_SM_EV_HW_MODE_SUCCESS) {
735 		status = cm_ser_reassoc_req(cm_ctx, &cm_req->roam_req);
736 		if (QDF_IS_STATUS_ERROR(status)) {
737 			reason = CM_SER_FAILURE;
738 			goto send_failure;
739 		}
740 		return status;
741 	}
742 
743 	/* Set reason HW mode fail for event WLAN_CM_SM_EV_HW_MODE_FAILURE */
744 	reason = CM_HW_MODE_FAILURE;
745 
746 send_failure:
747 	return cm_send_reassoc_start_fail(cm_ctx, cm_req->cm_id, reason, true);
748 }
749 
750 void cm_reassoc_hw_mode_change_resp(struct wlan_objmgr_pdev *pdev,
751 				    uint8_t vdev_id, wlan_cm_id cm_id,
752 				    QDF_STATUS status)
753 {
754 	struct wlan_objmgr_vdev *vdev;
755 	QDF_STATUS qdf_status;
756 	enum wlan_cm_sm_evt event = WLAN_CM_SM_EV_HW_MODE_SUCCESS;
757 	struct cnx_mgr *cm_ctx;
758 
759 	mlme_debug(CM_PREFIX_FMT "Continue Reassoc after HW mode change, status %d",
760 		   CM_PREFIX_REF(vdev_id, cm_id), status);
761 
762 	vdev = wlan_objmgr_get_vdev_by_id_from_pdev(pdev, vdev_id,
763 						    WLAN_MLME_CM_ID);
764 	if (!vdev)
765 		return;
766 
767 	cm_ctx = cm_get_cm_ctx(vdev);
768 	if (!cm_ctx) {
769 		wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_CM_ID);
770 		return;
771 	}
772 
773 	if (QDF_IS_STATUS_ERROR(status))
774 		event = WLAN_CM_SM_EV_HW_MODE_FAILURE;
775 	qdf_status = cm_sm_deliver_event(vdev, event, sizeof(wlan_cm_id),
776 					 &cm_id);
777 
778 	/*
779 	 * Handle failure if posting fails, i.e. the SM state has
780 	 * changed or head cm_id doesn't match the active cm_id.
781 	 * hw mode change resp should be handled in REASSOC state. If
782 	 * new command has been received reassoc should be
783 	 * aborted from here with reassoc req cleanup.
784 	 */
785 	if (QDF_IS_STATUS_ERROR(status))
786 		cm_reassoc_handle_event_post_fail(cm_ctx, cm_id);
787 	wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_CM_ID);
788 }
789 
790 static QDF_STATUS
791 cm_check_for_reassoc_hw_mode_change(struct cnx_mgr *cm_ctx,
792 				    struct cm_roam_req *cm_req)
793 {
794 	qdf_freq_t candidate_freq;
795 	struct wlan_objmgr_psoc *psoc;
796 	QDF_STATUS status;
797 
798 	psoc = wlan_vdev_get_psoc(cm_ctx->vdev);
799 	if (!psoc)
800 		return QDF_STATUS_E_INVAL;
801 
802 	if (!cm_req->cur_candidate)
803 		return QDF_STATUS_E_EMPTY;
804 
805 	/* HW mode change not required for self reassoc */
806 	if (cm_req->req.self_reassoc)
807 		return QDF_STATUS_E_ALREADY;
808 
809 	candidate_freq = cm_req->cur_candidate->entry->channel.chan_freq;
810 	status = policy_mgr_handle_conc_multiport(
811 			psoc, cm_req->req.vdev_id,
812 			candidate_freq, POLICY_MGR_UPDATE_REASON_LFR2_ROAM,
813 			cm_req->cm_id);
814 	if (status == QDF_STATUS_E_NOSUPPORT)
815 		status = QDF_STATUS_E_ALREADY;
816 
817 	return status;
818 }
819 #else
820 static inline QDF_STATUS
821 cm_check_for_reassoc_hw_mode_change(struct cnx_mgr *cm_ctx,
822 				    struct cm_roam_req *cm_req)
823 {
824 	return QDF_STATUS_E_ALREADY;
825 }
826 #endif
827 
828 QDF_STATUS cm_reassoc_start(struct cnx_mgr *cm_ctx,
829 			    struct cm_roam_req *cm_req)
830 {
831 	QDF_STATUS status = QDF_STATUS_E_FAILURE;
832 	uint8_t vdev_id = wlan_vdev_get_id(cm_ctx->vdev);
833 	enum wlan_cm_connect_fail_reason reason = CM_GENERIC_FAILURE;
834 
835 	status = cm_check_for_reassoc_hw_mode_change(cm_ctx, cm_req);
836 	if (QDF_IS_STATUS_ERROR(status) && status != QDF_STATUS_E_ALREADY) {
837 		reason = CM_HW_MODE_FAILURE;
838 		mlme_err(CM_PREFIX_FMT "Failed to set HW mode change status %d",
839 			 CM_PREFIX_REF(vdev_id, cm_req->cm_id), status);
840 		goto err;
841 	} else if (QDF_IS_STATUS_SUCCESS(status)) {
842 		mlme_debug(CM_PREFIX_FMT "Reassoc will continue after HW mode change",
843 			   CM_PREFIX_REF(vdev_id, cm_req->cm_id));
844 		return QDF_STATUS_SUCCESS;
845 	}
846 
847 	status = cm_ser_reassoc_req(cm_ctx, cm_req);
848 	if (QDF_IS_STATUS_SUCCESS(status))
849 		return status;
850 
851 	reason = CM_SER_FAILURE;
852 	mlme_err(CM_PREFIX_FMT "Serialization of reassoc failed",
853 		 CM_PREFIX_REF(vdev_id, cm_req->cm_id));
854 err:
855 	return cm_send_reassoc_start_fail(cm_ctx, cm_req->cm_id, reason, true);
856 }
857 
858 QDF_STATUS cm_reassoc_rsp(struct wlan_objmgr_vdev *vdev,
859 			  struct wlan_cm_connect_resp *resp)
860 {
861 	struct cnx_mgr *cm_ctx;
862 	QDF_STATUS qdf_status;
863 	wlan_cm_id cm_id;
864 	uint32_t prefix;
865 	enum wlan_cm_sm_evt event;
866 
867 	cm_ctx = cm_get_cm_ctx(vdev);
868 	if (!cm_ctx)
869 		return QDF_STATUS_E_INVAL;
870 
871 	cm_id = cm_ctx->active_cm_id;
872 	prefix = CM_ID_GET_PREFIX(cm_id);
873 
874 	if (prefix != ROAM_REQ_PREFIX ||
875 	    cm_id != resp->cm_id) {
876 		mlme_err(CM_PREFIX_FMT " Active cm_id 0x%x is different",
877 			 CM_PREFIX_REF(wlan_vdev_get_id(vdev), resp->cm_id),
878 			 cm_id);
879 		qdf_status = QDF_STATUS_E_FAILURE;
880 		goto post_err;
881 	}
882 
883 	if (QDF_IS_STATUS_SUCCESS(resp->connect_status)) {
884 		/*
885 		 * On successful connection to sae single pmk AP,
886 		 * clear all the single pmk AP.
887 		 */
888 		if (cm_is_cm_id_current_candidate_single_pmk(cm_ctx, cm_id))
889 			wlan_crypto_selective_clear_sae_single_pmk_entries(
890 					vdev, &resp->bssid);
891 		event = WLAN_CM_SM_EV_REASSOC_DONE;
892 	} else {
893 		event = WLAN_CM_SM_EV_REASSOC_FAILURE;
894 	}
895 
896 	qdf_status = cm_sm_deliver_event(cm_ctx->vdev, event, sizeof(*resp),
897 					 resp);
898 	if (QDF_IS_STATUS_SUCCESS(qdf_status))
899 		return qdf_status;
900 post_err:
901 	cm_reassoc_complete(cm_ctx, resp);
902 
903 	return qdf_status;
904 }
905 
906 QDF_STATUS cm_roam_bss_peer_create_rsp(struct wlan_objmgr_vdev *vdev,
907 				       QDF_STATUS status,
908 				       struct qdf_mac_addr *peer_mac)
909 {
910 	struct cnx_mgr *cm_ctx;
911 	QDF_STATUS qdf_status;
912 	wlan_cm_id cm_id;
913 	uint32_t prefix;
914 	struct cm_req *cm_req;
915 
916 	cm_ctx = cm_get_cm_ctx(vdev);
917 	if (!cm_ctx)
918 		return QDF_STATUS_E_INVAL;
919 
920 	cm_id = cm_ctx->active_cm_id;
921 	prefix = CM_ID_GET_PREFIX(cm_id);
922 
923 	if (prefix != ROAM_REQ_PREFIX) {
924 		mlme_err(CM_PREFIX_FMT "Active req is not roam req",
925 			 CM_PREFIX_REF(wlan_vdev_get_id(cm_ctx->vdev), cm_id));
926 		mlme_cm_bss_peer_delete_req(vdev);
927 		return QDF_STATUS_E_INVAL;
928 	}
929 
930 	if (QDF_IS_STATUS_ERROR(status)) {
931 		cm_req = cm_get_req_by_cm_id(cm_ctx, cm_id);
932 		if (!cm_req)
933 			return QDF_STATUS_E_INVAL;
934 
935 		return cm_send_reassoc_start_fail(
936 				cm_ctx, cm_id,
937 				CM_PEER_CREATE_FAILED, false);
938 	}
939 
940 	qdf_status = cm_sm_deliver_event(
941 			vdev, WLAN_CM_SM_EV_BSS_CREATE_PEER_SUCCESS,
942 			sizeof(wlan_cm_id), &cm_id);
943 	if (QDF_IS_STATUS_SUCCESS(qdf_status))
944 		return qdf_status;
945 
946 	mlme_cm_bss_peer_delete_req(vdev);
947 	cm_reassoc_handle_event_post_fail(cm_ctx, cm_id);
948 
949 	return qdf_status;
950 }
951 
952 QDF_STATUS cm_roam_disconnect_rsp(struct wlan_objmgr_vdev *vdev,
953 				  struct wlan_cm_discon_rsp *resp)
954 {
955 	struct cnx_mgr *cm_ctx;
956 	QDF_STATUS qdf_status;
957 	wlan_cm_id cm_id;
958 	uint32_t prefix;
959 
960 	cm_ctx = cm_get_cm_ctx(vdev);
961 	if (!cm_ctx)
962 		return QDF_STATUS_E_INVAL;
963 
964 	cm_id = cm_ctx->active_cm_id;
965 	prefix = CM_ID_GET_PREFIX(cm_id);
966 
967 	if (prefix != ROAM_REQ_PREFIX || cm_id != resp->req.cm_id) {
968 		mlme_err(CM_PREFIX_FMT "Active cm_id 0x%x is different",
969 			 CM_PREFIX_REF(wlan_vdev_get_id(vdev), resp->req.cm_id),
970 			 cm_id);
971 		qdf_status = QDF_STATUS_E_FAILURE;
972 		goto disconnect_complete;
973 	}
974 	qdf_status =
975 		cm_sm_deliver_event(vdev,
976 				    WLAN_CM_SM_EV_HO_ROAM_DISCONNECT_DONE,
977 				    sizeof(*resp), resp);
978 	if (QDF_IS_STATUS_SUCCESS(qdf_status))
979 		return qdf_status;
980 
981 disconnect_complete:
982 	cm_reassoc_handle_event_post_fail(cm_ctx, cm_id);
983 	return qdf_status;
984 }
985