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