Pārlūkot izejas kodu

qcacmn: Add reassoc changes related to host roaming

Support reassoc request for host roaming

Change-Id: Iacb2950e314561c22ed1900de6522dd192cda12e
CRs-Fixed: 2845076
Santosh Anbu 4 gadi atpakaļ
vecāks
revīzija
d788ea076d

+ 866 - 0
umac/mlme/connection_mgr/core/src/wlan_cm_host_roam.c

@@ -0,0 +1,866 @@
+/*
+ * Copyright (c) 2011-2021 The Linux Foundation. All rights reserved.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "wlan_cm_main.h"
+#include "wlan_cm_roam_sm.h"
+#include "wlan_cm_sm.h"
+#include <wlan_mlme_cmn.h>
+#include "wlan_cm_main_api.h"
+#include <wlan_scan_api.h>
+#include <wlan_serialization_api.h>
+#include <wlan_utility.h>
+#include <wlan_cm_api.h>
+
+static void
+cm_fill_roam_fail_resp_from_cm_id(struct cnx_mgr *cm_ctx,
+				  struct wlan_cm_roam_resp *resp,
+				  wlan_cm_id cm_id,
+				  enum wlan_cm_connect_fail_reason reason)
+{
+	resp->reassoc_status = QDF_STATUS_E_FAILURE;
+	resp->cm_id = cm_id;
+	resp->vdev_id = wlan_vdev_get_id(cm_ctx->vdev);
+	resp->reason = reason;
+	cm_fill_bss_info_in_roam_rsp_by_cm_id(cm_ctx, cm_id, resp);
+}
+
+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)
+{
+	struct cnx_mgr *cm_ctx;
+	struct cm_req *cm_req;
+	struct cm_disconnect_req *disconnect_req;
+	struct wlan_cm_disconnect_req req = {0};
+	QDF_STATUS status;
+
+	cm_ctx = cm_get_cm_ctx(vdev);
+	if (!cm_ctx)
+		return QDF_STATUS_E_INVAL;
+
+	/*
+	 * This would be freed as part of removal from cm req list if adding
+	 * to list is success after posting WLAN_CM_SM_EV_DISCONNECT_REQ.
+	 */
+	cm_req = qdf_mem_malloc(sizeof(*cm_req));
+
+	if (!cm_req)
+		return QDF_STATUS_E_NOMEM;
+
+	req.vdev_id = wlan_vdev_get_id(vdev);
+	req.source = source;
+	req.reason_code = reason_code;
+	if (bssid)
+		qdf_copy_macaddr(&req.bssid, bssid);
+
+	disconnect_req = &cm_req->discon_req;
+	disconnect_req->req = req;
+
+	status = cm_sm_deliver_event_sync(cm_ctx, WLAN_CM_SM_EV_DISCONNECT_REQ,
+					  sizeof(*disconnect_req),
+					  disconnect_req);
+	if (QDF_IS_STATUS_ERROR(status))
+		qdf_mem_free(cm_req);
+
+	return status;
+}
+
+QDF_STATUS
+cm_send_reassoc_start_fail(struct cnx_mgr *cm_ctx,
+			   wlan_cm_id cm_id,
+			   enum wlan_cm_connect_fail_reason reason)
+{
+	struct wlan_cm_roam_resp *resp;
+	QDF_STATUS status;
+
+	resp = qdf_mem_malloc(sizeof(*resp));
+	if (!resp)
+		return QDF_STATUS_E_NOMEM;
+
+	cm_fill_roam_fail_resp_from_cm_id(cm_ctx, resp, cm_id, reason);
+	status = cm_sm_deliver_event(cm_ctx->vdev,
+				     WLAN_CM_SM_EV_REASSOC_FAILURE,
+				     sizeof(*resp), resp);
+	if (QDF_IS_STATUS_ERROR(status))
+		cm_reassoc_complete(cm_ctx, resp);
+
+	qdf_mem_free(resp);
+
+	return status;
+}
+
+static void cm_connect_prepare_scan_filter_for_roam(
+		struct wlan_objmgr_pdev *pdev,
+		struct cnx_mgr *cm_ctx, struct cm_roam_req *cm_req,
+		struct scan_filter *filter, bool security_valid_for_6ghz)
+{
+	uint16_t rsn_caps;
+	struct wlan_objmgr_vdev *vdev = cm_ctx->vdev;
+
+	if (!qdf_is_macaddr_zero(&cm_req->req.bssid)) {
+		filter->num_of_bssid = 1;
+		qdf_copy_macaddr(&filter->bssid_list[0], &cm_req->req.bssid);
+	}
+
+	filter->num_of_ssid = 1;
+	wlan_vdev_mlme_get_ssid(vdev, filter->ssid_list[0].ssid,
+				&filter->ssid_list[0].length);
+
+	if (cm_req->req.chan_freq) {
+		filter->num_of_channels = 1;
+		filter->chan_freq_list[0] = cm_req->req.chan_freq;
+	}
+
+	/* Security is not valid for 6Ghz so ignore 6Ghz APs */
+	if (!security_valid_for_6ghz)
+		filter->ignore_6ghz_channel = true;
+
+	filter->authmodeset =
+		wlan_crypto_get_param(vdev, WLAN_CRYPTO_PARAM_AUTH_MODE);
+
+	filter->ucastcipherset =
+		wlan_crypto_get_param(vdev, WLAN_CRYPTO_PARAM_UCAST_CIPHER);
+
+	filter->mcastcipherset =
+		wlan_crypto_get_param(vdev, WLAN_CRYPTO_PARAM_MCAST_CIPHER);
+
+	filter->key_mgmt =
+		wlan_crypto_get_param(vdev, WLAN_CRYPTO_PARAM_KEY_MGMT);
+
+	filter->mgmtcipherset =
+		wlan_crypto_get_param(vdev, WLAN_CRYPTO_PARAM_MGMT_CIPHER);
+
+	if (!QDF_HAS_PARAM(filter->authmodeset, WLAN_CRYPTO_AUTH_WAPI) &&
+	    !QDF_HAS_PARAM(filter->authmodeset, WLAN_CRYPTO_AUTH_RSNA) &&
+	    !QDF_HAS_PARAM(filter->authmodeset, WLAN_CRYPTO_AUTH_WPA)) {
+		filter->ignore_auth_enc_type = 1;
+	}
+
+	rsn_caps =
+		wlan_crypto_get_param(vdev, WLAN_CRYPTO_PARAM_RSN_CAP);
+
+	if (rsn_caps & WLAN_CRYPTO_RSN_CAP_MFP_REQUIRED)
+		filter->pmf_cap = WLAN_PMF_REQUIRED;
+	else if (rsn_caps & WLAN_CRYPTO_RSN_CAP_MFP_ENABLED)
+		filter->pmf_cap = WLAN_PMF_CAPABLE;
+	else
+		filter->pmf_cap = WLAN_PMF_DISABLED;
+}
+
+QDF_STATUS cm_roam_get_candidates(
+		struct wlan_objmgr_pdev *pdev,
+		struct cnx_mgr *cm_ctx,
+		struct cm_roam_req *cm_req)
+{
+	struct scan_filter *filter;
+	uint32_t num_bss = 0;
+	enum QDF_OPMODE op_mode;
+	qdf_list_t *candidate_list;
+	uint8_t vdev_id = wlan_vdev_get_id(cm_ctx->vdev);
+	qdf_list_node_t *cur_node = NULL;
+	bool security_valid_for_6ghz = true;
+
+	filter = qdf_mem_malloc(sizeof(*filter));
+	if (!filter)
+		return QDF_STATUS_E_NOMEM;
+
+	cm_connect_prepare_scan_filter_for_roam(pdev, cm_ctx, cm_req, filter,
+						security_valid_for_6ghz);
+
+	candidate_list = wlan_scan_get_result(pdev, filter);
+	if (candidate_list) {
+		num_bss = qdf_list_size(candidate_list);
+		mlme_debug(CM_PREFIX_FMT "num_entries found %d",
+			   CM_PREFIX_REF(vdev_id, cm_req->cm_id), num_bss);
+	}
+
+	op_mode = wlan_vdev_mlme_get_opmode(cm_ctx->vdev);
+	if (num_bss && op_mode == QDF_STA_MODE)
+		cm_calculate_scores(pdev, filter, candidate_list);
+
+	qdf_mem_free(filter);
+
+	if (!candidate_list || !qdf_list_size(candidate_list)) {
+		if (candidate_list)
+			wlan_scan_purge_results(candidate_list);
+
+		mlme_info(CM_PREFIX_FMT "no valid candidate found, num_bss %d",
+			  CM_PREFIX_REF(vdev_id, cm_req->cm_id), num_bss);
+		cm_req->candidate_list = NULL;
+		return QDF_STATUS_E_EMPTY;
+	}
+
+	qdf_list_peek_front(candidate_list, &cur_node);
+	cm_req->candidate_list = candidate_list;
+	cm_req->cur_candidate = qdf_container_of(cur_node,
+						 struct scan_cache_node,
+						 node);
+	return QDF_STATUS_SUCCESS;
+}
+
+QDF_STATUS cm_start_roam_req(struct cnx_mgr *cm_ctx,
+			     struct cm_req *cm_req)
+{
+	cm_req->roam_req.cm_id =
+		cm_get_cm_id(cm_ctx, cm_req->roam_req.req.source);
+	cm_req->cm_id = cm_req->roam_req.cm_id;
+	cm_req->roam_req.req.vdev_id = wlan_vdev_get_id(cm_ctx->vdev);
+	cm_add_req_to_list_and_indicate_osif(cm_ctx, cm_req,
+					     cm_req->roam_req.req.source);
+
+	cm_sm_transition_to(cm_ctx, WLAN_CM_SS_PREAUTH);
+
+	return cm_sm_deliver_event_sync(cm_ctx, WLAN_CM_SM_EV_ROAM_START,
+					sizeof(*cm_req), cm_req);
+}
+
+QDF_STATUS cm_host_roam_start_req(struct cnx_mgr *cm_ctx,
+				  struct cm_req *cm_req)
+{
+	QDF_STATUS status;
+	struct wlan_objmgr_pdev *pdev;
+	enum wlan_cm_connect_fail_reason reason = CM_GENERIC_FAILURE;
+	uint8_t vdev_id = wlan_vdev_get_id(cm_ctx->vdev);
+
+	mlme_cm_roam_start_ind(cm_ctx->vdev, &cm_req->roam_req.req);
+
+	pdev = wlan_vdev_get_pdev(cm_ctx->vdev);
+	if (!pdev) {
+		mlme_err(CM_PREFIX_FMT "Failed to find pdev",
+			 CM_PREFIX_REF(vdev_id, cm_req->cm_id));
+		reason = CM_GENERIC_FAILURE;
+		goto roam_err;
+	}
+
+	status = cm_roam_get_candidates(pdev, cm_ctx,
+					&cm_req->roam_req);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		reason = CM_NO_CANDIDATE_FOUND;
+		goto roam_err;
+	}
+
+	status = cm_sm_deliver_event_sync(cm_ctx, WLAN_CM_SM_EV_START_REASSOC,
+					  sizeof(cm_req->roam_req),
+					  &cm_req->roam_req);
+	if (QDF_IS_STATUS_SUCCESS(status))
+		return status;
+
+roam_err:
+	return cm_send_reassoc_start_fail(cm_ctx, cm_req->cm_id, reason);
+}
+
+bool cm_roam_resp_cmid_match_list_head(struct cnx_mgr *cm_ctx,
+				       struct wlan_cm_roam_resp *resp)
+{
+	return cm_check_cmid_match_list_head(cm_ctx, &resp->cm_id);
+}
+
+static QDF_STATUS
+cm_inform_if_mgr_reassoc_complete(struct wlan_objmgr_vdev *vdev,
+				  QDF_STATUS status)
+{
+	return QDF_STATUS_SUCCESS;
+}
+
+static inline QDF_STATUS
+cm_inform_blm_reassoc_complete(struct wlan_objmgr_vdev *vdev,
+			       struct wlan_cm_roam_resp *resp)
+{
+	return QDF_STATUS_SUCCESS;
+}
+
+QDF_STATUS cm_reassoc_active(struct cnx_mgr *cm_ctx, wlan_cm_id *cm_id)
+{
+	struct cm_req *cm_req;
+	struct wlan_cm_vdev_discon_req req;
+
+	cm_req = cm_get_req_by_cm_id(cm_ctx, *cm_id);
+	if (!cm_req)
+		return QDF_STATUS_E_INVAL;
+
+	cm_ctx->active_cm_id = *cm_id;
+	qdf_mem_zero(&req, sizeof(req));
+	req.cm_id = *cm_id;
+	req.req.vdev_id = wlan_vdev_get_id(cm_ctx->vdev);
+	req.req.source = CM_ROAM_DISCONNECT;
+	wlan_vdev_get_bss_peer_mac(cm_ctx->vdev, &req.req.bssid);
+
+	return mlme_cm_disconnect_req(cm_ctx->vdev, &req);
+}
+
+QDF_STATUS cm_reassoc_disconnect_complete(struct cnx_mgr *cm_ctx,
+					  struct wlan_cm_discon_rsp *resp)
+{
+	QDF_STATUS status;
+	struct cm_req *cm_req;
+	struct qdf_mac_addr *bssid;
+	struct cm_disconnect_req discon_req;
+	uint8_t vdev_id = wlan_vdev_get_id(cm_ctx->vdev);
+	wlan_cm_id cm_id = cm_ctx->active_cm_id;
+
+	cm_req = cm_get_req_by_cm_id(cm_ctx, cm_id);
+	if (!cm_req)
+		return QDF_STATUS_E_INVAL;
+
+	qdf_mem_zero(&discon_req, sizeof(discon_req));
+
+	discon_req.cm_id = cm_id;
+	discon_req.req.vdev_id = vdev_id;
+	qdf_copy_macaddr(&discon_req.req.bssid,
+			 &resp->req.req.bssid);
+	cm_update_scan_mlme_on_disconnect(cm_ctx->vdev, &discon_req);
+	mlme_cm_disconnect_complete_ind(cm_ctx->vdev, resp);
+
+	bssid = &cm_req->roam_req.cur_candidate->entry->bssid;
+	status = mlme_cm_bss_peer_create_req(cm_ctx->vdev, bssid);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		mlme_err(CM_PREFIX_FMT "Peer create request failed",
+			 CM_PREFIX_REF(vdev_id, cm_id));
+		status = cm_send_reassoc_start_fail(cm_ctx, cm_id,
+						    CM_PEER_CREATE_FAILED);
+	}
+
+	return status;
+}
+
+QDF_STATUS
+cm_resume_reassoc_after_peer_create(struct cnx_mgr *cm_ctx, wlan_cm_id *cm_id)
+{
+	struct wlan_cm_vdev_reassoc_req req;
+	struct cm_req *cm_req;
+	QDF_STATUS status;
+
+	cm_req = cm_get_req_by_cm_id(cm_ctx, *cm_id);
+	if (!cm_req)
+		return QDF_STATUS_E_FAILURE;
+
+	req.vdev_id = wlan_vdev_get_id(cm_ctx->vdev);
+	req.cm_id = *cm_id;
+	req.bss = cm_req->roam_req.cur_candidate;
+
+	status = mlme_cm_reassoc_req(cm_ctx->vdev, &req);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		mlme_err(CM_PREFIX_FMT "Reassoc request failed",
+			 CM_PREFIX_REF(req.vdev_id, req.cm_id));
+		mlme_cm_bss_peer_delete_req(cm_ctx->vdev);
+		status = cm_send_reassoc_start_fail(cm_ctx, *cm_id,
+						    CM_JOIN_FAILED);
+	}
+
+	return status;
+}
+
+static void
+cm_update_scan_db_on_reassoc_success(struct cnx_mgr *cm_ctx,
+				     struct wlan_cm_roam_resp *resp)
+{
+	struct element_info *bcn_probe_rsp;
+	struct cm_req *cm_req;
+	int32_t rssi;
+
+	if (!cm_is_vdev_connected(cm_ctx->vdev))
+		return;
+
+	cm_req = cm_get_req_by_cm_id(cm_ctx, resp->cm_id);
+	if (!cm_req)
+		return;
+
+	if (!cm_req->roam_req.cur_candidate)
+		return;
+
+	/*
+	 * Get beacon or probe resp from connect response, and if not present
+	 * use cur candidate to get beacon or probe resp
+	 */
+	if (resp->connect_ies.bcn_probe_rsp.ptr)
+		bcn_probe_rsp = &resp->connect_ies.bcn_probe_rsp;
+	else
+		bcn_probe_rsp =
+			&cm_req->roam_req.cur_candidate->entry->raw_frame;
+
+	rssi = cm_req->roam_req.cur_candidate->entry->rssi_raw;
+
+	cm_inform_bcn_probe(cm_ctx, bcn_probe_rsp->ptr, bcn_probe_rsp->len,
+			    resp->freq, rssi, resp->cm_id);
+}
+
+inline void cm_set_fils_wep_key_on_reassoc(struct cnx_mgr *cm_ctx,
+					   struct wlan_cm_roam_resp *resp)
+{
+}
+
+QDF_STATUS cm_reassoc_complete(struct cnx_mgr *cm_ctx,
+			       struct wlan_cm_roam_resp *resp)
+{
+	enum wlan_cm_sm_state sm_state;
+	struct bss_info bss_info;
+	struct mlme_info mlme_info;
+	struct wlan_objmgr_vdev *vdev = cm_ctx->vdev;
+
+	/*
+	 * If the entry is not present in the list, it must have been cleared
+	 * already.
+	 */
+	if (!cm_get_req_by_cm_id(cm_ctx, resp->cm_id))
+		return QDF_STATUS_SUCCESS;
+
+	sm_state = cm_get_state(cm_ctx);
+	if (QDF_IS_STATUS_ERROR(resp->reassoc_status))
+		goto reassoc_fail;
+
+	if (QDF_IS_STATUS_SUCCESS(resp->reassoc_status) &&
+	    sm_state == WLAN_CM_S_CONNECTED) {
+		cm_update_scan_db_on_reassoc_success(cm_ctx, resp);
+		cm_set_fils_wep_key_on_reassoc(cm_ctx, resp);
+	}
+
+	mlme_cm_reassoc_complete_ind(vdev, resp);
+	mlme_cm_osif_reassoc_complete(vdev, resp);
+	cm_inform_if_mgr_reassoc_complete(vdev, resp->reassoc_status);
+	cm_inform_blm_reassoc_complete(vdev, resp);
+
+reassoc_fail:
+	/* Update scan entry in case connect is success or fails with bssid */
+	if (!qdf_is_macaddr_zero(&resp->bssid)) {
+		if (QDF_IS_STATUS_SUCCESS(resp->reassoc_status))
+			mlme_info.assoc_state  = SCAN_ENTRY_CON_STATE_ASSOC;
+		else
+			mlme_info.assoc_state = SCAN_ENTRY_CON_STATE_NONE;
+		qdf_copy_macaddr(&bss_info.bssid, &resp->bssid);
+		bss_info.freq = resp->freq;
+		bss_info.ssid.length = resp->ssid.length;
+		qdf_mem_copy(&bss_info.ssid.ssid, resp->ssid.ssid,
+			     bss_info.ssid.length);
+		wlan_scan_update_mlme_by_bssinfo(
+					wlan_vdev_get_pdev(vdev),
+					&bss_info, &mlme_info);
+	}
+
+	mlme_debug(CM_PREFIX_FMT,
+		   CM_PREFIX_REF(wlan_vdev_get_id(vdev),
+				 resp->cm_id));
+	cm_remove_cmd(cm_ctx, resp->cm_id);
+
+	/*
+	 * If roaming fails and conn_sm is in ROAMING state, then
+	 * initiate disconnect to cleanup and move conn_sm to INIT state
+	 */
+	if (QDF_IS_STATUS_ERROR(resp->reassoc_status) &&
+	    sm_state == WLAN_CM_S_ROAMING) {
+		cm_reassoc_fail_disconnect(cm_ctx->vdev, CM_ROAM_DISCONNECT,
+					   REASON_UNSPEC_FAILURE,
+					   &resp->bssid);
+	}
+
+	return QDF_STATUS_SUCCESS;
+}
+
+void
+cm_reassoc_handle_event_post_fail(struct cnx_mgr *cm_ctx, wlan_cm_id cm_id)
+{
+	struct wlan_cm_roam_resp *resp;
+
+	resp = qdf_mem_malloc(sizeof(*resp));
+	if (!resp)
+		return;
+
+	cm_fill_roam_fail_resp_from_cm_id(cm_ctx, resp, cm_id,
+					  CM_GENERIC_FAILURE);
+	cm_reassoc_complete(cm_ctx, resp);
+	qdf_mem_free(resp);
+}
+
+static QDF_STATUS cm_reassoc_cmd_timeout(struct cnx_mgr *cm_ctx,
+					 wlan_cm_id cm_id)
+{
+	struct wlan_cm_roam_resp *resp;
+	QDF_STATUS status;
+
+	resp = qdf_mem_malloc(sizeof(*resp));
+	if (!resp)
+		return QDF_STATUS_E_NOMEM;
+
+	cm_fill_roam_fail_resp_from_cm_id(cm_ctx, resp, cm_id, CM_SER_TIMEOUT);
+	status = cm_sm_deliver_event(cm_ctx->vdev,
+				     WLAN_CM_SM_EV_REASSOC_FAILURE,
+				     sizeof(*resp), resp);
+	if (QDF_IS_STATUS_ERROR(status))
+		cm_reassoc_complete(cm_ctx, resp);
+
+	qdf_mem_free(resp);
+
+	return status;
+}
+
+#ifdef WLAN_CM_USE_SPINLOCK
+static QDF_STATUS cm_activate_reassoc_req_flush_cb(struct scheduler_msg *msg)
+{
+	struct wlan_serialization_command *cmd = msg->bodyptr;
+
+	if (!cmd || !cmd->vdev) {
+		mlme_err("Null input cmd:%pK", cmd);
+		return QDF_STATUS_E_INVAL;
+	}
+
+	wlan_objmgr_vdev_release_ref(cmd->vdev, WLAN_MLME_CM_ID);
+	return QDF_STATUS_SUCCESS;
+}
+
+static QDF_STATUS cm_activate_reassoc_req_sched_cb(struct scheduler_msg *msg)
+{
+	struct wlan_serialization_command *cmd = msg->bodyptr;
+	struct wlan_objmgr_vdev *vdev;
+	struct cnx_mgr *cm_ctx;
+	QDF_STATUS ret = QDF_STATUS_E_FAILURE;
+
+	if (!cmd) {
+		mlme_err("cmd is null");
+		return QDF_STATUS_E_INVAL;
+	}
+
+	vdev = cmd->vdev;
+	if (!vdev) {
+		mlme_err("vdev is null");
+		return QDF_STATUS_E_INVAL;
+	}
+
+	cm_ctx = cm_get_cm_ctx(vdev);
+	if (!cm_ctx)
+		return QDF_STATUS_E_INVAL;
+
+	ret = cm_sm_deliver_event(vdev,
+				  WLAN_CM_SM_EV_REASSOC_ACTIVE,
+				  sizeof(wlan_cm_id),
+				  &cmd->cmd_id);
+
+	/*
+	 * Called from scheduler context hence posting failure
+	 */
+	if (QDF_IS_STATUS_ERROR(ret)) {
+		mlme_err(CM_PREFIX_FMT "Activation failed for cmd:%d",
+			 CM_PREFIX_REF(wlan_vdev_get_id(vdev), cmd->cmd_id),
+			 cmd->cmd_type);
+		cm_reassoc_handle_event_post_fail(cm_ctx, cmd->cmd_id);
+	}
+
+	wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_CM_ID);
+	return ret;
+}
+
+static QDF_STATUS
+cm_activate_reassoc_req(struct wlan_serialization_command *cmd)
+{
+	struct wlan_objmgr_vdev *vdev = cmd->vdev;
+	struct scheduler_msg msg = {0};
+	QDF_STATUS ret;
+
+	msg.bodyptr = cmd;
+	msg.callback = cm_activate_reassoc_req_sched_cb;
+	msg.flush_callback = cm_activate_reassoc_req_flush_cb;
+
+	ret = wlan_objmgr_vdev_try_get_ref(vdev, WLAN_MLME_CM_ID);
+	if (QDF_IS_STATUS_ERROR(ret))
+		return ret;
+
+	ret = scheduler_post_message(QDF_MODULE_ID_MLME,
+				     QDF_MODULE_ID_MLME,
+				     QDF_MODULE_ID_MLME, &msg);
+
+	if (QDF_IS_STATUS_ERROR(ret)) {
+		mlme_err(CM_PREFIX_FMT "Failed to post scheduler_msg",
+			 CM_PREFIX_REF(wlan_vdev_get_id(vdev), cmd->cmd_id));
+		wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_CM_ID);
+		return ret;
+	}
+	mlme_debug(CM_PREFIX_FMT "Cmd act in sched cmd type:%d",
+		   CM_PREFIX_REF(wlan_vdev_get_id(vdev), cmd->cmd_id),
+		   cmd->cmd_type);
+
+	return ret;
+}
+#else
+static QDF_STATUS
+cm_activate_reassoc_req(struct wlan_serialization_command *cmd)
+{
+	return cm_sm_deliver_event(cmd->vdev,
+				   WLAN_CM_SM_EV_REASSOC_ACTIVE,
+				   sizeof(wlan_cm_id),
+				   &cmd->cmd_id);
+}
+#endif
+
+static QDF_STATUS
+cm_ser_reassoc_cb(struct wlan_serialization_command *cmd,
+		  enum wlan_serialization_cb_reason reason)
+{
+	QDF_STATUS status = QDF_STATUS_SUCCESS;
+	struct wlan_objmgr_vdev *vdev;
+	struct cnx_mgr *cm_ctx;
+
+	if (!cmd) {
+		mlme_err("cmd is NULL, reason: %d", reason);
+		QDF_ASSERT(0);
+		return QDF_STATUS_E_NULL_VALUE;
+	}
+
+	vdev = cmd->vdev;
+
+	cm_ctx = cm_get_cm_ctx(vdev);
+	if (!cm_ctx)
+		return QDF_STATUS_E_NULL_VALUE;
+
+	switch (reason) {
+	case WLAN_SER_CB_ACTIVATE_CMD:
+		/*
+		 * For pending to active reason, use async api to take lock.
+		 * For direct activation use sync api to avoid taking lock
+		 * as lock is already acquired by the requester.
+		 */
+		if (cmd->activation_reason == SER_PENDING_TO_ACTIVE)
+			status = cm_activate_reassoc_req(cmd);
+		else
+			status = cm_sm_deliver_event_sync(
+					cm_ctx, WLAN_CM_SM_EV_REASSOC_ACTIVE,
+					sizeof(wlan_cm_id), &cmd->cmd_id);
+
+		if (QDF_IS_STATUS_SUCCESS(status))
+			break;
+		/*
+		 * Handle failure if posting fails, i.e. the SM state has
+		 * changed or head cm_id doesn't match the active cm_id.
+		 * connect active should be handled only in JOIN_PENDING. If
+		 * new command has been received connect activation should be
+		 * aborted from here with connect req cleanup.
+		 */
+		cm_reassoc_handle_event_post_fail(cm_ctx, cmd->cmd_id);
+		break;
+	case WLAN_SER_CB_CANCEL_CMD:
+		/* command removed from pending list. */
+		break;
+	case WLAN_SER_CB_ACTIVE_CMD_TIMEOUT:
+		mlme_err(CM_PREFIX_FMT "Active command timeout",
+			 CM_PREFIX_REF(wlan_vdev_get_id(vdev), cmd->cmd_id));
+		QDF_ASSERT(0);
+
+		cm_reassoc_cmd_timeout(cm_ctx, cmd->cmd_id);
+		break;
+	case WLAN_SER_CB_RELEASE_MEM_CMD:
+		cm_reset_active_cm_id(vdev, cmd->cmd_id);
+		wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_CM_ID);
+		break;
+	default:
+		QDF_ASSERT(0);
+		status = QDF_STATUS_E_INVAL;
+		break;
+	}
+
+	return status;
+}
+
+#define REASSOC_TIMEOUT	10000
+static QDF_STATUS cm_ser_reassoc_req(struct cnx_mgr *cm_ctx,
+				     struct cm_roam_req *cm_req)
+{
+	struct wlan_serialization_command cmd = {0, };
+	enum wlan_serialization_status ser_cmd_status;
+	QDF_STATUS status;
+	uint8_t vdev_id = wlan_vdev_get_id(cm_ctx->vdev);
+
+	status = wlan_objmgr_vdev_try_get_ref(cm_ctx->vdev, WLAN_MLME_CM_ID);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		mlme_err(CM_PREFIX_FMT "unable to get reference",
+			 CM_PREFIX_REF(vdev_id, cm_req->cm_id));
+		return status;
+	}
+
+	cmd.cmd_type = WLAN_SER_CMD_VDEV_REASSOC;
+	cmd.cmd_id = cm_req->cm_id;
+	cmd.cmd_cb = cm_ser_reassoc_cb;
+	cmd.source = WLAN_UMAC_COMP_MLME;
+	cmd.is_high_priority = false;
+	cmd.cmd_timeout_duration = REASSOC_TIMEOUT;
+	cmd.vdev = cm_ctx->vdev;
+	cmd.is_blocking = cm_ser_get_blocking_cmd();
+
+	ser_cmd_status = wlan_serialization_request(&cmd);
+	switch (ser_cmd_status) {
+	case WLAN_SER_CMD_PENDING:
+		/* command moved to pending list.Do nothing */
+		break;
+	case WLAN_SER_CMD_ACTIVE:
+		/* command moved to active list. Do nothing */
+		break;
+	default:
+		mlme_err(CM_PREFIX_FMT "ser cmd status %d",
+			 CM_PREFIX_REF(vdev_id, cm_req->cm_id), ser_cmd_status);
+		wlan_objmgr_vdev_release_ref(cm_ctx->vdev, WLAN_MLME_CM_ID);
+
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	return QDF_STATUS_SUCCESS;
+}
+
+QDF_STATUS cm_reassoc_start(struct cnx_mgr *cm_ctx,
+			    struct cm_roam_req *cm_req)
+{
+	QDF_STATUS status;
+	uint8_t vdev_id = wlan_vdev_get_id(cm_ctx->vdev);
+
+	status = cm_ser_reassoc_req(cm_ctx, cm_req);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		mlme_err(CM_PREFIX_FMT "Serialization of reassoc failed",
+			 CM_PREFIX_REF(vdev_id, cm_req->cm_id));
+		return cm_send_reassoc_start_fail(cm_ctx, cm_req->cm_id,
+						  CM_SER_FAILURE);
+	}
+
+	return status;
+}
+
+QDF_STATUS cm_reassoc_rsp(struct wlan_objmgr_vdev *vdev,
+			  struct wlan_cm_roam_resp *resp)
+{
+	struct cnx_mgr *cm_ctx;
+	QDF_STATUS qdf_status;
+	wlan_cm_id cm_id;
+	uint32_t prefix;
+
+	cm_ctx = cm_get_cm_ctx(vdev);
+	if (!cm_ctx)
+		return QDF_STATUS_E_INVAL;
+
+	cm_id = cm_ctx->active_cm_id;
+	prefix = CM_ID_GET_PREFIX(cm_id);
+
+	if (prefix != ROAM_REQ_PREFIX ||
+	    cm_id != resp->cm_id) {
+		mlme_err(CM_PREFIX_FMT " Active cm_id 0x%x is different",
+			 CM_PREFIX_REF(wlan_vdev_get_id(vdev), resp->cm_id),
+			 cm_id);
+		qdf_status = QDF_STATUS_E_FAILURE;
+		goto post_err;
+	}
+
+	if (QDF_IS_STATUS_SUCCESS(resp->reassoc_status)) {
+		/*
+		 * On successful connection to sae single pmk AP,
+		 * clear all the single pmk AP.
+		 */
+		if (cm_is_cm_id_current_candidate_single_pmk(cm_ctx, cm_id))
+			wlan_crypto_selective_clear_sae_single_pmk_entries(
+					vdev, &resp->bssid);
+		qdf_status = cm_sm_deliver_event(vdev,
+						 WLAN_CM_SM_EV_REASSOC_DONE,
+						 sizeof(*resp), resp);
+		if (QDF_IS_STATUS_SUCCESS(qdf_status))
+			return qdf_status;
+	}
+post_err:
+	cm_reassoc_complete(cm_ctx, resp);
+
+	return qdf_status;
+}
+
+QDF_STATUS cm_roam_bss_peer_create_rsp(struct wlan_objmgr_vdev *vdev,
+				       QDF_STATUS status,
+				       struct qdf_mac_addr *peer_mac)
+{
+	struct cnx_mgr *cm_ctx;
+	QDF_STATUS qdf_status;
+	wlan_cm_id cm_id;
+	uint32_t prefix;
+	struct cm_req *cm_req;
+
+	cm_ctx = cm_get_cm_ctx(vdev);
+	if (!cm_ctx)
+		return QDF_STATUS_E_INVAL;
+
+	cm_id = cm_ctx->active_cm_id;
+	prefix = CM_ID_GET_PREFIX(cm_id);
+
+	if (prefix != ROAM_REQ_PREFIX) {
+		mlme_err(CM_PREFIX_FMT "Active req is not roam req",
+			 CM_PREFIX_REF(wlan_vdev_get_id(cm_ctx->vdev), cm_id));
+		mlme_cm_bss_peer_delete_req(vdev);
+		return QDF_STATUS_E_INVAL;
+	}
+
+	if (QDF_IS_STATUS_ERROR(status)) {
+		cm_req = cm_get_req_by_cm_id(cm_ctx, cm_id);
+		if (!cm_req)
+			return QDF_STATUS_E_INVAL;
+
+		return cm_send_reassoc_start_fail(
+				cm_ctx, cm_id,
+				CM_PEER_CREATE_FAILED);
+	}
+
+	qdf_status = cm_sm_deliver_event(
+			vdev, WLAN_CM_SM_EV_BSS_CREATE_PEER_SUCCESS,
+			sizeof(wlan_cm_id), &cm_id);
+	if (QDF_IS_STATUS_SUCCESS(qdf_status))
+		return qdf_status;
+
+	mlme_cm_bss_peer_delete_req(vdev);
+	cm_reassoc_handle_event_post_fail(cm_ctx, cm_id);
+
+	return qdf_status;
+}
+
+QDF_STATUS cm_roam_disconnect_rsp(struct wlan_objmgr_vdev *vdev,
+				  struct wlan_cm_discon_rsp *resp)
+{
+	struct cnx_mgr *cm_ctx;
+	QDF_STATUS qdf_status;
+	wlan_cm_id cm_id;
+	uint32_t prefix;
+
+	cm_ctx = cm_get_cm_ctx(vdev);
+	if (!cm_ctx)
+		return QDF_STATUS_E_INVAL;
+
+	cm_id = cm_ctx->active_cm_id;
+	prefix = CM_ID_GET_PREFIX(cm_id);
+
+	if (prefix != ROAM_REQ_PREFIX || cm_id != resp->req.cm_id) {
+		mlme_err(CM_PREFIX_FMT "Active cm_id 0x%x is different",
+			 CM_PREFIX_REF(wlan_vdev_get_id(vdev), resp->req.cm_id),
+			 cm_id);
+		qdf_status = QDF_STATUS_E_FAILURE;
+		goto disconnect_complete;
+	}
+	qdf_status =
+		cm_sm_deliver_event(vdev,
+				    WLAN_CM_SM_EV_DISCONNECT_DONE,
+				    sizeof(*resp), resp);
+	if (QDF_IS_STATUS_SUCCESS(qdf_status))
+		return qdf_status;
+
+disconnect_complete:
+	cm_reassoc_handle_event_post_fail(cm_ctx, cm_id);
+	return qdf_status;
+}
+
+void cm_start_roam_invoke(struct cnx_mgr *cm_ctx)
+{
+}
+
+void cm_fw_roam_start(struct cnx_mgr *cm_ctx)
+{
+}

+ 236 - 0
umac/mlme/connection_mgr/core/src/wlan_cm_roam.h

@@ -0,0 +1,236 @@
+/*
+ * Copyright (c) 2011-2021 The Linux Foundation. All rights reserved.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/**
+ * DOC: wlan_cm_roam.h
+ *
+ * This header file maintain APIs required for connection mgr roam functions
+ */
+
+#ifndef __WLAN_CM_ROAM_H__
+#define __WLAN_CM_ROAM_H__
+
+/**
+ * cm_check_and_prepare_roam_req() - Initiate roam request
+ * @cm_ctx: connection manager context
+ * @connect_req: connection manager request
+ * @roam_req: Roam request
+ *
+ * Context: Can be called only while handling connection manager event
+ *          ie holding state machine lock
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS
+cm_check_and_prepare_roam_req(struct cnx_mgr *cm_ctx,
+			      struct cm_connect_req *connect_req,
+			      struct cm_req **roam_req);
+
+/**
+ * cm_roam_bss_peer_create_rsp() - handle bss peer create response for roam
+ * @vdev: vdev
+ * @status: bss peer create status
+ * @peer_mac: peer mac
+ *
+ * Return: QDF status
+ */
+QDF_STATUS cm_roam_bss_peer_create_rsp(struct wlan_objmgr_vdev *vdev,
+				       QDF_STATUS status,
+				       struct qdf_mac_addr *peer_mac);
+
+/**
+ * cm_reassoc_rsp() - Connection manager reassoc response
+ * @vdev: vdev pointer
+ * @resp: Reassoc response
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS cm_reassoc_rsp(struct wlan_objmgr_vdev *vdev,
+			  struct wlan_cm_roam_resp *resp);
+
+/**
+ * cm_roam_disconnect_rsp() - Connection manager api to post connect event
+ * @vdev: VDEV object
+ * @cm_discon_rsp: Disconnect response
+ *
+ * Context: Any context.
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS cm_roam_disconnect_rsp(struct wlan_objmgr_vdev *vdev,
+				  struct wlan_cm_discon_rsp *resp);
+
+/**
+ * cm_reassoc_complete() - This API would be called after reassoc complete
+ * request from the serialization.
+ * @cm_ctx: connection manager context
+ * @resp: Roam complete resp.
+ *
+ * This API would be called after roam completion resp from VDEV mgr
+ *
+ * Return: QDF status
+ */
+#ifdef WLAN_FEATURE_HOST_ROAM
+QDF_STATUS cm_reassoc_complete(struct cnx_mgr *cm_ctx,
+			       struct wlan_cm_roam_resp *resp);
+#else
+static inline QDF_STATUS cm_reassoc_complete(struct cnx_mgr *cm_ctx,
+					     struct wlan_cm_roam_resp *resp)
+{}
+#endif
+
+/**
+ * cm_free_roam_req_mem() - free croam req internal memory, to be called
+ * before cm_req is freed
+ * @roam_req: roam req
+ *
+ * Return: void
+ */
+void cm_free_roam_req_mem(struct cm_roam_req *roam_req);
+
+/**
+ * cm_get_active_reassoc_req() - Get copy of active reassoc request
+ * @vdev: vdev pointer
+ * @req: pointer to the copy of the active reassoc request
+ * *
+ * Context: Should be called only in the conext of the
+ * cm request activation
+ *
+ * Return: true and reassoc req if any request is active
+ */
+bool cm_get_active_reassoc_req(struct wlan_objmgr_vdev *vdev,
+			       struct wlan_cm_vdev_reassoc_req *req);
+
+/**
+ * cm_start_roam_req() - Initiate roam request
+ * @cm_ctx: Connection manager context
+ * @cm_req: Struct containing the roam request
+ *
+ * Return: QDF_STATUS_SUCCESS if roam initiation delivers the event
+ * to connection state machine else error value.
+ */
+QDF_STATUS cm_start_roam_req(struct cnx_mgr *cm_ctx,
+			     struct cm_req *cm_req);
+
+/**
+ * cm_host_roam_start_req() - Start host roam request
+ * @cm_ctx: Connection manager context
+ * @cm_req: Struct containing the roam request
+ *
+ * Return: QDF_STATUS_SUCCESS on delivering the event
+ * to connection state machine else error value.
+ */
+QDF_STATUS cm_host_roam_start_req(struct cnx_mgr *cm_ctx,
+				  struct cm_req *cm_req);
+
+/**
+ * cm_start_roam_invoke() - Start roam request
+ * @cm_ctx: Connection manager context
+ *
+ * Return: void
+ */
+void cm_start_roam_invoke(struct cnx_mgr *cm_ctx);
+
+/**
+ * cm_fw_roam_start() - Initiate FW roam request
+ * @cm_ctx: Connection manager context
+ *
+ * Return: void
+ */
+void cm_fw_roam_start(struct cnx_mgr *cm_ctx);
+
+/**
+ * cm_reassoc_start() - This API will be called to initiate the reassoc
+ * process
+ * @cm_ctx: connection manager context
+ * @req: roam request.
+ *
+ * Return: QDF status
+ */
+QDF_STATUS cm_reassoc_start(struct cnx_mgr *cm_ctx, struct cm_roam_req *req);
+
+/**
+ * cm_reassoc_active() - This API would be called after the reassoc
+ * request gets activated in serialization.
+ * @cm_ctx: connection manager context
+ * @cm_id: Connection mgr ID assigned to this reassoc request.
+ *
+ * Return: QDF status
+ */
+QDF_STATUS cm_reassoc_active(struct cnx_mgr *cm_ctx, wlan_cm_id *cm_id);
+
+/**
+ * cm_reassoc_disconnect_complete() - This API would be called after
+ * disconnect complete due to reassoc request.
+ * @cm_ctx: connection manager context
+ * @resp: disconnection complete resp.
+ *
+ * This API would be called after disconnection completion resp from VDEV mgr
+ *
+ * Return: QDF status
+ */
+
+QDF_STATUS cm_reassoc_disconnect_complete(struct cnx_mgr *cm_ctx,
+					  struct wlan_cm_discon_rsp *resp);
+
+/**
+ * cm_resume_reassoc_after_peer_create() - Called after bss create rsp
+ * @cm_ctx: connection manager context
+ * @cm_id: Connection mgr ID assigned to this reassoc request.
+ *
+ * Return: QDF status
+ */
+QDF_STATUS
+cm_resume_reassoc_after_peer_create(struct cnx_mgr *cm_ctx, wlan_cm_id *cm_id);
+
+/**
+ * cm_roam_resp_cmid_match_list_head() - Check if resp cmid is same as list
+ * head
+ * @cm_ctx: connection manager context
+ * @resp: roam resp
+ *
+ * Return: bool
+ */
+bool cm_roam_resp_cmid_match_list_head(struct cnx_mgr *cm_ctx,
+				       struct wlan_cm_roam_resp *resp);
+
+/**
+ * cm_send_reassoc_start_fail() - initiate reassoc failure
+ * @cm_ctx: connection manager context
+ * @cm_id: active command id
+ * @reason: failure reason
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS
+cm_send_reassoc_start_fail(struct cnx_mgr *cm_ctx,
+			   wlan_cm_id cm_id,
+			   enum wlan_cm_connect_fail_reason reason);
+
+/**
+ * cm_fill_bss_info_in_roam_rsp_by_cm_id() - fill bss info for the cm id
+ * @cm_ctx: connection manager context
+ * @cm_id: cm id of connect/disconnect req
+ * @resp: resp to copy bss info like ssid/bssid and freq
+ *
+ * Return: Success if entry was found else failure
+ */
+QDF_STATUS
+cm_fill_bss_info_in_roam_rsp_by_cm_id(struct cnx_mgr *cm_ctx,
+				      wlan_cm_id cm_id,
+				      struct wlan_cm_roam_resp *resp);
+
+#endif /* __WLAN_CM_ROAM_H__ */

+ 168 - 0
umac/mlme/connection_mgr/core/src/wlan_cm_roam_util.c

@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2011-2021 The Linux Foundation. All rights reserved.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/**
+ * DOC: Implements general Roam utils for connection manager
+ */
+
+#include "wlan_cm_main.h"
+#include "wlan_cm_roam_sm.h"
+#include "wlan_cm_sm.h"
+#include "wlan_cm_main_api.h"
+#include "wlan_cm_roam.h"
+#include <wlan_scan_api.h>
+
+QDF_STATUS cm_check_and_prepare_roam_req(struct cnx_mgr *cm_ctx,
+					 struct cm_connect_req *connect_req,
+					 struct cm_req **roam_req)
+{
+	QDF_STATUS status;
+	struct wlan_cm_connect_req *req;
+	struct qdf_mac_addr bssid;
+	struct wlan_ssid ssid;
+	struct cm_req *cm_req, *req_ptr;
+
+	cm_req = qdf_container_of(connect_req, struct cm_req, connect_req);
+	req = &connect_req->req;
+	/*
+	 * Reject re-assoc unless freq along with prev bssid and one
+	 * of bssid or bssid hint is present.
+	 */
+	if (!req->chan_freq || qdf_is_macaddr_zero(&req->prev_bssid) ||
+	    (qdf_is_macaddr_zero(&req->bssid) &&
+	     qdf_is_macaddr_zero(&req->bssid_hint)))
+		return QDF_STATUS_E_FAILURE;
+
+	wlan_vdev_get_bss_peer_mac(cm_ctx->vdev, &bssid);
+
+	/* Reject re-assoc unless prev_bssid matches the current BSSID. */
+	if (!qdf_is_macaddr_equal(&req->prev_bssid, &bssid))
+		return QDF_STATUS_E_FAILURE;
+
+	status = wlan_vdev_mlme_get_ssid(cm_ctx->vdev, ssid.ssid, &ssid.length);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		mlme_err("failed to get ssid");
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	/* Reject re-assoc unless ssid matches. */
+	if (ssid.length != req->ssid.length ||
+	    qdf_mem_cmp(ssid.ssid, req->ssid.ssid, ssid.length))
+		return QDF_STATUS_E_FAILURE;
+
+	/* fill roam_req for roaming and free cm_req */
+	*roam_req = qdf_mem_malloc(sizeof(**roam_req));
+	if (!*roam_req)
+		return QDF_STATUS_E_NOMEM;
+
+	req_ptr = *roam_req;
+	if (!qdf_is_macaddr_zero(&req->bssid))
+		qdf_copy_macaddr(&req_ptr->roam_req.req.bssid, &req->bssid);
+	else
+		qdf_copy_macaddr(&req_ptr->roam_req.req.bssid,
+				 &req->bssid_hint);
+
+	qdf_copy_macaddr(&req_ptr->roam_req.req.prev_bssid, &req->prev_bssid);
+	req_ptr->roam_req.req.chan_freq = req->chan_freq;
+	req_ptr->roam_req.req.source = CM_ROAMING_HOST;
+
+	/* Free the connect req, as reassoc is tried */
+	cm_free_connect_req_mem(connect_req);
+	qdf_mem_free(cm_req);
+
+	return QDF_STATUS_SUCCESS;
+}
+
+#ifdef WLAN_FEATURE_HOST_ROAM
+bool cm_get_active_reassoc_req(struct wlan_objmgr_vdev *vdev,
+			       struct wlan_cm_vdev_reassoc_req *req)
+{
+	struct cnx_mgr *cm_ctx;
+	qdf_list_node_t *cur_node = NULL, *next_node = NULL;
+	struct cm_req *cm_req = NULL;
+	bool status = false;
+	uint32_t cm_id_prefix;
+
+	cm_ctx = cm_get_cm_ctx(vdev);
+
+	cm_req_lock_acquire(cm_ctx);
+	qdf_list_peek_front(&cm_ctx->req_list, &cur_node);
+	while (cur_node) {
+		qdf_list_peek_next(&cm_ctx->req_list, cur_node, &next_node);
+
+		cm_req = qdf_container_of(cur_node, struct cm_req, node);
+		cm_id_prefix = CM_ID_GET_PREFIX((cm_req->cm_id));
+
+		if (cm_req->cm_id == cm_ctx->active_cm_id &&
+		    cm_id_prefix == ROAM_REQ_PREFIX) {
+			req->vdev_id = wlan_vdev_get_id(vdev);
+			req->cm_id = cm_req->roam_req.cm_id;
+			qdf_copy_macaddr(&req->prev_bssid,
+					 &cm_req->roam_req.req.prev_bssid);
+			req->bss = cm_req->roam_req.cur_candidate;
+			status = true;
+			cm_req_lock_release(cm_ctx);
+			return status;
+		}
+
+		cur_node = next_node;
+		next_node = NULL;
+	}
+	cm_req_lock_release(cm_ctx);
+
+	return status;
+}
+#endif
+
+QDF_STATUS
+cm_fill_bss_info_in_roam_rsp_by_cm_id(struct cnx_mgr *cm_ctx,
+				      wlan_cm_id cm_id,
+				      struct wlan_cm_roam_resp *resp)
+{
+	qdf_list_node_t *cur_node = NULL, *next_node = NULL;
+	struct cm_req *cm_req;
+	uint32_t prefix = CM_ID_GET_PREFIX(cm_id);
+	struct wlan_cm_roam_req *req;
+
+	if (prefix != ROAM_REQ_PREFIX)
+		return QDF_STATUS_E_INVAL;
+
+	cm_req_lock_acquire(cm_ctx);
+	qdf_list_peek_front(&cm_ctx->req_list, &cur_node);
+	while (cur_node) {
+		qdf_list_peek_next(&cm_ctx->req_list, cur_node, &next_node);
+		cm_req = qdf_container_of(cur_node, struct cm_req, node);
+
+		if (cm_req->cm_id == cm_id) {
+			req = &cm_req->roam_req.req;
+			resp->freq = req->chan_freq;
+			wlan_vdev_mlme_get_ssid(cm_ctx->vdev, resp->ssid.ssid,
+						&resp->ssid.length);
+
+			if (!qdf_is_macaddr_zero(&req->bssid))
+				qdf_copy_macaddr(&resp->bssid, &req->bssid);
+
+			cm_req_lock_release(cm_ctx);
+			return QDF_STATUS_SUCCESS;
+		}
+
+		cur_node = next_node;
+		next_node = NULL;
+	}
+	cm_req_lock_release(cm_ctx);
+
+	return QDF_STATUS_E_FAILURE;
+}