Jelajahi Sumber

qcacld-3.0: Determine transition status for BSS transition candidates

Determine bss transition status for preferrable candidates provided
by userspace based on the transition reason, rssi of connected and
candidate bssids and other parameters like whether transitiong to the
candidate will result in sub-optimal scenario. The transition status
is either accept or a reason for reject.

Change-Id: Ib83c81909f4d8e31b4125309b8ac392a26a0d6bf
CRs-Fixed: 2007107
Vignesh Viswanathan 7 tahun lalu
induk
melakukan
9dd88d39b7

+ 9 - 0
core/hdd/src/wlan_hdd_cfg.c

@@ -9053,6 +9053,15 @@ QDF_STATUS hdd_set_sme_config(struct hdd_context *hdd_ctx)
 		WMI_VDEV_OCE_REASSOC_REJECT_FEATURE_BITMAP);
 	smeConfig->csrConfig.oce_feature_bitmap = val;
 
+	smeConfig->csrConfig.mbo_thresholds.mbo_candidate_rssi_thres =
+		hdd_ctx->config->mbo_candidate_rssi_thres;
+	smeConfig->csrConfig.mbo_thresholds.mbo_current_rssi_thres =
+		hdd_ctx->config->mbo_current_rssi_thres;
+	smeConfig->csrConfig.mbo_thresholds.mbo_current_rssi_mcc_thres =
+		hdd_ctx->config->mbo_current_rssi_mcc_thres;
+	smeConfig->csrConfig.mbo_thresholds.mbo_candidate_rssi_btc_thres =
+		hdd_ctx->config->mbo_candidate_rssi_btc_thres;
+
 	hdd_update_bss_score_params(hdd_ctx->config,
 			&smeConfig->csrConfig.bss_score_params);
 

+ 243 - 0
core/hdd/src/wlan_hdd_cfg80211.c

@@ -12191,6 +12191,240 @@ static int wlan_hdd_cfg80211_set_limit_offchan_param(struct wiphy *wiphy,
 	return ret;
 }
 
+/**
+ * wlan_hdd_fill_btm_resp() - Fill bss candidate response buffer
+ * @reply_skb : pointer to reply_skb
+ * @info : bss candidate information
+ * @index : attribute type index for nla_next_start()
+ *
+ * Return : 0 on success and errno on failure
+ */
+static int wlan_hdd_fill_btm_resp(struct sk_buff *reply_skb,
+				  struct bss_candidate_info *info,
+				  int index)
+{
+	struct nlattr *attr;
+
+	attr = nla_nest_start(reply_skb, index);
+	if (!attr) {
+		hdd_err("nla_nest_start failed");
+		kfree_skb(reply_skb);
+		return -EINVAL;
+	}
+
+	if (nla_put(reply_skb,
+		  QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID,
+		  ETH_ALEN, info->bssid.bytes) ||
+	    nla_put_u32(reply_skb,
+		 QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_STATUS,
+		 info->status)) {
+		hdd_err("nla_put failed");
+		kfree_skb(reply_skb);
+		return -EINVAL;
+	}
+
+	nla_nest_end(reply_skb, attr);
+
+	return 0;
+}
+
+/**
+ * __wlan_hdd_cfg80211_fetch_bss_transition_status() - fetch bss transition
+ * status
+ * @wiphy : WIPHY structure pointer
+ * @wdev : Wireless device structure pointer
+ * @data : Pointer to the data received
+ * @data_len : Length of the data received
+ *
+ * This fuction is used to fetch transition status for candidate bss. The
+ * transition status is either accept or reason for reject.
+ *
+ * Return : 0 on success and errno on failure
+ */
+static int __wlan_hdd_cfg80211_fetch_bss_transition_status(struct wiphy *wiphy,
+						struct wireless_dev *wdev,
+						const void *data, int data_len)
+{
+	struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_MAX + 1];
+	struct nlattr *tb_msg[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_MAX + 1];
+	uint8_t transition_reason;
+	struct nlattr *attr;
+	struct sk_buff *reply_skb;
+	int rem, j;
+	int ret;
+	bool is_bt_in_progress;
+	struct bss_candidate_info candidate_info[MAX_CANDIDATE_INFO];
+	uint16_t nof_candidates, i = 0;
+	struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
+	struct net_device *dev = wdev->netdev;
+	struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
+	struct hdd_station_ctx *hdd_sta_ctx =
+					WLAN_HDD_GET_STATION_CTX_PTR(adapter);
+	tHalHandle hal = WLAN_HDD_GET_HAL_CTX(adapter);
+
+	const struct nla_policy
+	btm_params_policy[QCA_WLAN_VENDOR_ATTR_MAX + 1] = {
+		[QCA_WLAN_VENDOR_ATTR_BTM_MBO_TRANSITION_REASON] = {
+							.type = NLA_U8},
+		[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO] = {
+							.type = NLA_NESTED},
+	};
+	const struct nla_policy
+	btm_cand_list_policy[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_MAX + 1]
+		= {[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID] = {
+						.len = QDF_MAC_ADDR_SIZE},
+		   [QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_STATUS] = {
+							.type = NLA_U32},
+		};
+
+	ENTER();
+
+	if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
+		hdd_err("Command not allowed in FTM mode");
+		return -EINVAL;
+	}
+
+	ret = wlan_hdd_validate_context(hdd_ctx);
+	if (ret)
+		return ret;
+
+	if (adapter->device_mode != QDF_STA_MODE ||
+	    hdd_sta_ctx->conn_info.connState != eConnectionState_Associated) {
+		hdd_err("Command is either not invoked for STA mode (device mode: %d) or STA is not associated (Connection state: %d)",
+			adapter->device_mode, hdd_sta_ctx->conn_info.connState);
+		return -EINVAL;
+	}
+
+	ret = hdd_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_MAX, data,
+			data_len, btm_params_policy);
+	if (ret) {
+		hdd_err("Attribute parse failed");
+		return -EINVAL;
+	}
+
+	if (!tb[QCA_WLAN_VENDOR_ATTR_BTM_MBO_TRANSITION_REASON] ||
+	    !tb[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO]) {
+		hdd_err("Missing attributes");
+		return -EINVAL;
+	}
+
+	transition_reason = nla_get_u8(
+			    tb[QCA_WLAN_VENDOR_ATTR_BTM_MBO_TRANSITION_REASON]);
+
+	nla_for_each_nested(attr,
+			    tb[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO],
+			    rem) {
+		ret = nla_parse_nested(
+				tb_msg,
+				QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_MAX,
+				attr, btm_cand_list_policy);
+		if (ret) {
+			hdd_err("Attribute parse failed");
+			return -EINVAL;
+		}
+
+		if (!tb_msg[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID]) {
+			hdd_err("Missing BSSID attribute");
+			return -EINVAL;
+		}
+
+		qdf_mem_copy((void *)candidate_info[i].bssid.bytes,
+			     nla_data(tb_msg[
+			     QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID]),
+			     QDF_MAC_ADDR_SIZE);
+		i++;
+		if (i == MAX_CANDIDATE_INFO)
+			break;
+	}
+
+	/*
+	 * Determine status for each candidate and fill in the status field.
+	 * Also arrange the candidates in the order of preference.
+	 */
+	nof_candidates = i;
+
+	is_bt_in_progress = wlan_hdd_is_bt_in_progress(hdd_ctx);
+
+	ret = sme_get_bss_transition_status(hal, transition_reason,
+					    &hdd_sta_ctx->conn_info.bssId,
+					    candidate_info,
+					    nof_candidates,
+					    is_bt_in_progress);
+	if (ret)
+		return -EINVAL;
+
+	/* Prepare the reply and send it to userspace */
+	reply_skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy,
+			((QDF_MAC_ADDR_SIZE + sizeof(uint32_t)) *
+			 nof_candidates) + NLMSG_HDRLEN);
+	if (!reply_skb) {
+		hdd_err("reply buffer alloc failed");
+		return -ENOMEM;
+	}
+
+	attr = nla_nest_start(reply_skb,
+			      QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO);
+	if (!attr) {
+		hdd_err("nla_nest_start failed");
+		kfree_skb(reply_skb);
+		return -EINVAL;
+	}
+
+	/*
+	 * Order candidates as - accepted candidate list followed by rejected
+	 * candidate list
+	 */
+	for (i = 0, j = 0; i < nof_candidates; i++) {
+		/* copy accepted candidate list */
+		if (candidate_info[i].status == QCA_STATUS_ACCEPT) {
+			if (wlan_hdd_fill_btm_resp(reply_skb,
+						   &candidate_info[i], j))
+				return -EINVAL;
+			j++;
+		}
+	}
+	for (i = 0; i < nof_candidates; i++) {
+		/* copy rejected candidate list */
+		if (candidate_info[i].status != QCA_STATUS_ACCEPT) {
+			if (wlan_hdd_fill_btm_resp(reply_skb,
+						   &candidate_info[i], j))
+				return -EINVAL;
+			j++;
+		}
+	}
+	nla_nest_end(reply_skb, attr);
+
+	EXIT();
+
+	return cfg80211_vendor_cmd_reply(reply_skb);
+}
+
+/**
+ * wlan_hdd_cfg80211_fetch_bss_transition_status() - fetch bss transition status
+ * @wiphy : WIPHY structure pointer
+ * @wdev : Wireless device structure pointer
+ * @data : Pointer to the data received
+ * @data_len : Length of the data received
+ *
+ * This fuction is used to fetch transition status for candidate bss. The
+ * transition status is either accept or reason for reject.
+ *
+ * Return : 0 on success and errno on failure
+ */
+static int wlan_hdd_cfg80211_fetch_bss_transition_status(struct wiphy *wiphy,
+						struct wireless_dev *wdev,
+						const void *data, int data_len)
+{
+	int ret;
+
+	cds_ssr_protect(__func__);
+	ret = __wlan_hdd_cfg80211_fetch_bss_transition_status(wiphy, wdev,
+							      data, data_len);
+	cds_ssr_unprotect(__func__);
+
+	return ret;
+}
+
 const struct wiphy_vendor_command hdd_wiphy_vendor_commands[] = {
 	{
 		.info.vendor_id = QCA_NL80211_VENDOR_ID,
@@ -12854,6 +13088,15 @@ const struct wiphy_vendor_command hdd_wiphy_vendor_commands[] = {
 			WIPHY_VENDOR_CMD_NEED_RUNNING,
 		.doit = wlan_hdd_cfg80211_get_nud_stats
 	},
+	{
+		.info.vendor_id = QCA_NL80211_VENDOR_ID,
+		.info.subcmd =
+			QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS,
+		.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
+			 WIPHY_VENDOR_CMD_NEED_NETDEV |
+			 WIPHY_VENDOR_CMD_NEED_RUNNING,
+		.doit = wlan_hdd_cfg80211_fetch_bss_transition_status
+	},
 
 #ifdef WLAN_UMAC_CONVERGENCE
 	COMMON_VENDOR_COMMANDS

+ 15 - 0
core/sme/inc/csr_api.h

@@ -1122,6 +1122,20 @@ struct csr_sta_roam_policy_params {
 	uint8_t sap_operating_band;
 };
 
+/**
+ * struct csr_mbo_thresholds - mbo related thresholds
+ * @mbo_candidate_rssi_thres - Candidate RSSI threshold
+ * @mbo_current_rssi_thres - Current RSSI threshold
+ * @mbo_current_rssi_mcc_thres - Current RSSI MCC threshold
+ * mbo_candidate_rssi_btc_thres - Candidate RSSI BTC threshold
+ */
+struct csr_mbo_thresholds {
+	int8_t mbo_candidate_rssi_thres;
+	int8_t mbo_current_rssi_thres;
+	int8_t mbo_current_rssi_mcc_thres;
+	int8_t mbo_candidate_rssi_btc_thres;
+};
+
 typedef struct tagCsrConfigParam {
 	uint32_t FragmentationThreshold;
 	/* keep this uint32_t. This gets converted to ePhyChannelBondState */
@@ -1354,6 +1368,7 @@ typedef struct tagCsrConfigParam {
 	uint32_t num_disallowed_aps;
 	struct sir_score_config bss_score_params;
 	uint8_t oce_feature_bitmap;
+	struct csr_mbo_thresholds mbo_thresholds;
 } tCsrConfigParam;
 
 /* Tush */

+ 10 - 0
core/sme/inc/csr_internal.h

@@ -636,6 +636,7 @@ struct csr_config {
 	uint32_t wlm_latency_flags[CSR_NUM_WLM_LATENCY_LEVEL];
 	struct sir_score_config bss_score_params;
 	uint8_t oce_feature_bitmap;
+	struct csr_mbo_thresholds mbo_thresholds;
 };
 
 struct csr_channel_powerinfo {
@@ -1543,4 +1544,13 @@ QDF_STATUS csr_roam_update_config(
 			tpAniSirGlobal mac_ctx, uint8_t session_id,
 				  uint16_t capab, uint32_t value);
 
+/**
+ * csr_is_mcc_channel() - check if using the channel results into MCC
+ * @hal: pointer to HAL
+ * @channel : channel number to check for MCC scenario
+ *
+ * Return : true if channel causes MCC, else false
+ */
+bool csr_is_mcc_channel(tHalHandle hal, uint8_t channel);
+
 #endif

+ 61 - 0
core/sme/inc/sme_api.h

@@ -258,6 +258,35 @@ struct sme_session_params {
 	uint32_t subtype_of_persona;
 };
 
+#define MAX_CANDIDATE_INFO 10
+
+/**
+ * struct bss_candidate_info - Candidate bss information
+ *
+ * @bssid : BSSID of candidate bss
+ * @status : status code for candidate bss
+ */
+struct bss_candidate_info {
+	struct qdf_mac_addr bssid;
+	uint32_t status;
+};
+
+/*
+ * MBO transition reason codes
+ */
+enum {
+	MBO_TRANSITION_REASON_UNSPECIFIED,
+	MBO_TRANSITION_REASON_EXCESSIVE_FRAME_LOSS_RATE,
+	MBO_TRANSITION_REASON_EXCESSIVE_DELAY_FOR_CURRENT_TRAFFIC,
+	MBO_TRANSITION_REASON_INSUFFICIENT_BANDWIDTH_FOR_CURRENT_TRAFFIC,
+	MBO_TRANSITION_REASON_LOAD_BALANCING,
+	MBO_TRANSITION_REASON_LOW_RSSI,
+	MBO_TRANSITION_REASON_RECEIVED_EXCESSIVE_RETRANSMISSIONS,
+	MBO_TRANSITION_REASON_HIGH_INTERFERENCE,
+	MBO_TRANSITION_REASON_GRAY_ZONE,
+	MBO_TRANSITION_REASON_TRANSITIONING_TO_PREMIUM_AP,
+};
+
 /*-------------------------------------------------------------------------
   Function declarations and documenation
   ------------------------------------------------------------------------*/
@@ -1934,4 +1963,36 @@ static inline QDF_STATUS sme_set_he_bss_color(tHalHandle hal,
 	return QDF_STATUS_SUCCESS;
 }
 #endif
+
+/**
+ * sme_scan_get_result_for_bssid - gets the scan result from scan cache for the
+ * bssid specified
+ * @hal: handle returned by mac_open
+ * @bssid: bssid to get the scan result for
+ * @res: pointer to tCsrScanResultInfo allocated from caller
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS sme_scan_get_result_for_bssid(tHalHandle hal_handle,
+					 struct qdf_mac_addr *bssid,
+					 tCsrScanResultInfo *res);
+
+/**
+ * sme_get_bss_transition_status() - get bss transition status all cadidates
+ * @hal: handle returned by mac_open
+ * @transition_reason : Transition reason
+ * @bssid: bssid to get BSS transition status
+ * @info : bss candidate information
+ * @n_candidates : number of candidates
+ * @is_bt_in_progress: bt activity indicator
+ *
+ * Return : 0 on success otherwise errno
+ */
+int sme_get_bss_transition_status(tHalHandle hal,
+		uint8_t transition_reason,
+		struct qdf_mac_addr *bssid,
+		struct bss_candidate_info *info,
+		uint16_t n_candidates,
+		bool is_bt_in_progress);
+
 #endif /* #if !defined( __SME_API_H ) */

+ 199 - 0
core/sme/src/common/sme_api.c

@@ -63,6 +63,8 @@
 #include "wlan_reg_ucfg_api.h"
 #include "ol_txrx.h"
 #include "wifi_pos_api.h"
+#include "net/cfg80211.h"
+#include <qca_vendor.h>
 
 static tSelfRecoveryStats g_self_recovery_stats;
 
@@ -2632,6 +2634,22 @@ QDF_STATUS sme_scan_get_result(tHalHandle hHal, uint8_t sessionId,
 	return status;
 }
 
+QDF_STATUS sme_scan_get_result_for_bssid(tHalHandle hal_handle,
+					 struct qdf_mac_addr *bssid,
+					 tCsrScanResultInfo *res)
+{
+	tpAniSirGlobal mac_ctx = PMAC_STRUCT(hal_handle);
+	QDF_STATUS status;
+
+	status = sme_acquire_global_lock(&mac_ctx->sme);
+	if (QDF_IS_STATUS_SUCCESS(status)) {
+		status = csr_scan_get_result_for_bssid(hal_handle, bssid, res);
+		sme_release_global_lock(&mac_ctx->sme);
+	}
+
+	return status;
+}
+
 /**
  * sme_get_ap_channel_from_scan() - a wrapper function to get
  *				  AP's channel id from
@@ -15661,3 +15679,184 @@ QDF_STATUS sme_send_limit_off_channel_params(tHalHandle hal, uint8_t vdev_id,
 
 	return QDF_STATUS_SUCCESS;
 }
+
+/**
+ * sme_get_status_for_candidate() - Get bss transition status for candidate
+ * @hal: Handle for HAL
+ * @conn_bss_desc: connected bss descriptor
+ * @bss_desc: candidate bss descriptor
+ * @info: candiadate bss information
+ * @trans_reason: transition reason code
+ * @is_bt_in_progress: bt activity indicator
+ *
+ * Return : true if candidate is rejected and reject reason is filled
+ * @info->status. Otherwise returns false.
+ */
+static bool sme_get_status_for_candidate(tHalHandle *hal,
+					tSirBssDescription *conn_bss_desc,
+					tSirBssDescription *bss_desc,
+					struct bss_candidate_info *info,
+					uint8_t trans_reason,
+					bool is_bt_in_progress)
+{
+	tpAniSirGlobal mac_ctx = PMAC_STRUCT(hal);
+
+	/*
+	 * Low RSSI based rejection
+	 * If candidate rssi is less than mbo_candidate_rssi_thres and connected
+	 * bss rssi is greater than mbo_current_rssi_thres, then reject the
+	 * candidate with MBO reason code 4.
+	 */
+	if ((bss_desc->rssi < mac_ctx->roam.configParam.mbo_thresholds.
+	    mbo_candidate_rssi_thres) &&
+	    (conn_bss_desc->rssi > mac_ctx->roam.configParam.mbo_thresholds.
+	    mbo_current_rssi_thres)) {
+		sme_err("Candidate BSS "MAC_ADDRESS_STR" has LOW RSSI(%d), hence reject",
+			MAC_ADDR_ARRAY(bss_desc->bssId), bss_desc->rssi);
+		info->status = QCA_STATUS_REJECT_LOW_RSSI;
+		return true;
+	}
+
+	if (trans_reason == MBO_TRANSITION_REASON_LOAD_BALANCING ||
+	    trans_reason == MBO_TRANSITION_REASON_TRANSITIONING_TO_PREMIUM_AP) {
+		/*
+		 * MCC rejection
+		 * If moving to candidate's channel will result in MCC scenario
+		 * and the rssi of connected bss is greater than
+		 * mbo_current_rssi_mss_thres, then reject the candidate with
+		 * MBO reason code 3.
+		 */
+		if ((conn_bss_desc->rssi >
+		    mac_ctx->roam.configParam.mbo_thresholds.
+		    mbo_current_rssi_mcc_thres) &&
+		    csr_is_mcc_channel(hal, bss_desc->channelId)) {
+			sme_err("Candidate BSS "MAC_ADDRESS_STR" causes MCC, hence reject",
+				MAC_ADDR_ARRAY(bss_desc->bssId));
+			info->status =
+				QCA_STATUS_REJECT_INSUFFICIENT_QOS_CAPACITY;
+			return true;
+		}
+
+		/*
+		 * BT coex rejection
+		 * If AP is trying to move the client from 5G to 2.4G and moving
+		 * to 2.4G will result in BT coex and candidate channel rssi is
+		 * less than mbo_candidate_rssi_btc_thres, then reject the
+		 * candidate with MBO reason code 2.
+		 */
+		if (WLAN_REG_IS_5GHZ_CH(conn_bss_desc->channelId) &&
+		    WLAN_REG_IS_24GHZ_CH(bss_desc->channelId) &&
+		    is_bt_in_progress &&
+		    (bss_desc->rssi <
+		    mac_ctx->roam.configParam.mbo_thresholds.
+		    mbo_candidate_rssi_btc_thres)) {
+			sme_err("Candidate BSS "MAC_ADDRESS_STR" causes BT coex, hence reject",
+				MAC_ADDR_ARRAY(bss_desc->bssId));
+			info->status =
+				QCA_STATUS_REJECT_EXCESSIVE_DELAY_EXPECTED;
+			return true;
+		}
+
+		/*
+		 * LTE coex rejection
+		 * If moving to candidate's channel can cause LTE coex, then
+		 * reject the candidate with MBO reason code 5.
+		 */
+		if (policy_mgr_is_safe_channel(mac_ctx->psoc,
+		    conn_bss_desc->channelId) &&
+		    !(policy_mgr_is_safe_channel(mac_ctx->psoc,
+		    bss_desc->channelId))) {
+			sme_err("High interference expected if transitioned to BSS "
+				MAC_ADDRESS_STR" hence reject",
+				MAC_ADDR_ARRAY(bss_desc->bssId));
+			info->status =
+				QCA_STATUS_REJECT_HIGH_INTERFERENCE;
+			return true;
+		}
+	}
+
+	return false;
+}
+
+/**
+ * wlan_hdd_get_bss_transition_status() - get bss transition status all cadidates
+ * @adapter : Pointer to adapter
+ * @transition_reason : Transition reason
+ * @info : bss candidate information
+ * @n_candidates : number of candidates
+ *
+ * Return : 0 on success otherwise errno
+ */
+int sme_get_bss_transition_status(tHalHandle hal,
+					uint8_t transition_reason,
+					struct qdf_mac_addr *bssid,
+					struct bss_candidate_info *info,
+					uint16_t n_candidates,
+					bool is_bt_in_progress)
+{
+	QDF_STATUS status = QDF_STATUS_SUCCESS;
+	tSirBssDescription *bss_desc, *conn_bss_desc;
+	tCsrScanResultInfo *res, *conn_res;
+	uint16_t i;
+
+	if (!n_candidates || !info) {
+		sme_err("No candidate info available");
+		return QDF_STATUS_E_INVAL;
+	}
+
+	conn_res = qdf_mem_malloc(sizeof(tCsrScanResultInfo));
+	if (!conn_res) {
+		sme_err("Failed to allocate memory for conn_res");
+		return QDF_STATUS_E_NOMEM;
+	}
+
+	res = qdf_mem_malloc(sizeof(tCsrScanResultInfo));
+	if (!res) {
+		sme_err("Failed to allocate memory for conn_res");
+		status = QDF_STATUS_E_NOMEM;
+		goto free;
+	}
+
+	/* Get the connected BSS descriptor */
+	status = sme_scan_get_result_for_bssid(hal, bssid, conn_res);
+	if (!QDF_IS_STATUS_SUCCESS(status)) {
+		sme_err("Failed to find connected BSS in scan list");
+		goto free;
+	}
+	conn_bss_desc = &conn_res->BssDescriptor;
+
+	for (i = 0; i < n_candidates; i++) {
+		/* Get candidate BSS descriptors */
+		status = sme_scan_get_result_for_bssid(hal, &info[i].bssid,
+						       res);
+		if (!QDF_IS_STATUS_SUCCESS(status)) {
+			sme_err("BSS "MAC_ADDRESS_STR" not present in scan list",
+				MAC_ADDR_ARRAY(info[i].bssid.bytes));
+			info[i].status = QCA_STATUS_REJECT_UNKNOWN;
+			continue;
+		}
+
+		bss_desc = &res->BssDescriptor;
+		if (!sme_get_status_for_candidate(hal, conn_bss_desc, bss_desc,
+		    &info[i], transition_reason, is_bt_in_progress)) {
+			/*
+			 * If status is not over written, it means it is a
+			 * candidate for accept.
+			 */
+			info[i].status = QCA_STATUS_ACCEPT;
+		}
+	}
+
+	/* success */
+	status = QDF_STATUS_SUCCESS;
+
+free:
+	/* free allocated memory */
+	if (conn_res)
+		qdf_mem_free(conn_res);
+	if (res)
+		qdf_mem_free(res);
+
+	return status;
+}
+

+ 26 - 0
core/sme/src/csr/csr_api_roam.c

@@ -2976,6 +2976,19 @@ QDF_STATUS csr_change_default_config_param(tpAniSirGlobal pMac,
 		pMac->roam.configParam.oce_feature_bitmap =
 			pParam->oce_feature_bitmap;
 
+		pMac->roam.configParam.mbo_thresholds.
+			mbo_candidate_rssi_thres =
+			pParam->mbo_thresholds.mbo_candidate_rssi_thres;
+		pMac->roam.configParam.mbo_thresholds.
+			mbo_current_rssi_thres =
+			pParam->mbo_thresholds.mbo_current_rssi_thres;
+		pMac->roam.configParam.mbo_thresholds.
+			mbo_current_rssi_mcc_thres =
+			pParam->mbo_thresholds.mbo_current_rssi_mcc_thres;
+		pMac->roam.configParam.mbo_thresholds.
+			mbo_candidate_rssi_btc_thres =
+			pParam->mbo_thresholds.mbo_candidate_rssi_btc_thres;
+
 		qdf_mem_copy(&pMac->roam.configParam.bss_score_params,
 			     &pParam->bss_score_params,
 			     sizeof(struct sir_score_config));
@@ -3249,6 +3262,19 @@ QDF_STATUS csr_get_config_param(tpAniSirGlobal pMac, tCsrConfigParam *pParam)
 		     &pMac->roam.configParam.bss_score_params,
 		     sizeof(struct sir_score_config));
 
+	pParam->mbo_thresholds.mbo_candidate_rssi_thres =
+		pMac->roam.configParam.mbo_thresholds.
+		mbo_candidate_rssi_thres;
+	pParam->mbo_thresholds.mbo_current_rssi_thres =
+		pMac->roam.configParam.mbo_thresholds.
+		mbo_current_rssi_thres;
+	pParam->mbo_thresholds.mbo_current_rssi_mcc_thres =
+		pMac->roam.configParam.mbo_thresholds.
+		mbo_current_rssi_mcc_thres;
+	pParam->mbo_thresholds.mbo_candidate_rssi_btc_thres =
+		pMac->roam.configParam.mbo_thresholds.
+		mbo_candidate_rssi_btc_thres;
+
 	csr_get_he_config_param(pParam, pMac);
 
 	return QDF_STATUS_SUCCESS;

+ 66 - 2
core/sme/src/csr/csr_api_scan.c

@@ -5710,8 +5710,8 @@ QDF_STATUS csr_scan_get_result(tpAniSirGlobal mac_ctx,
 	ret_list->pCurEntry = NULL;
 	status = csr_parse_scan_list(mac_ctx,
 		ret_list, list);
-	sme_debug("return %d BSS status %d",
-			csr_ll_count(&ret_list->List), status);
+	sme_debug("status: %d No of BSS: %d",
+		  status, csr_ll_count(&ret_list->List));
 	if (QDF_IS_STATUS_ERROR(status) || !results)
 		/* Fail or No one wants the result. */
 		csr_scan_result_purge(mac_ctx, (tScanResultHandle) ret_list);
@@ -5737,6 +5737,70 @@ error:
 	return status;
 }
 
+QDF_STATUS csr_scan_get_result_for_bssid(tpAniSirGlobal mac_ctx,
+					struct qdf_mac_addr *bssid,
+					tCsrScanResultInfo *res)
+{
+	QDF_STATUS status = QDF_STATUS_E_FAILURE;
+	tCsrScanResultFilter *scan_filter = NULL;
+	tScanResultHandle filtered_scan_result = NULL;
+	tCsrScanResultInfo *scan_result;
+
+	if (!mac_ctx) {
+		QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR,
+				FL("mac_ctx is NULL"));
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	scan_filter = qdf_mem_malloc(sizeof(tCsrScanResultFilter));
+	if (!scan_filter) {
+		sme_err("Failed to allocated memory for scan_filter");
+		return QDF_STATUS_E_NOMEM;
+	}
+
+	scan_filter->BSSIDs.bssid = qdf_mem_malloc(sizeof(bssid));
+	if (!scan_filter->BSSIDs.bssid) {
+		sme_err("Failed to allocate memory for BSSIDs");
+		status = QDF_STATUS_E_FAILURE;
+		goto free_filter;
+	}
+
+	scan_filter->BSSIDs.numOfBSSIDs = 1;
+	qdf_mem_copy(scan_filter->BSSIDs.bssid, bssid, sizeof(bssid));
+
+	status = csr_scan_get_result(mac_ctx, scan_filter,
+				&filtered_scan_result);
+
+	if (!QDF_IS_STATUS_SUCCESS(status)) {
+		sme_err("Failed to get scan result");
+		goto free_filter;
+	}
+
+	scan_result = csr_scan_result_get_first(mac_ctx, filtered_scan_result);
+
+	if (scan_result) {
+		res->pvIes = NULL;
+		res->ssId.length = scan_result->ssId.length;
+		qdf_mem_copy(&res->ssId.ssId, &scan_result->ssId.ssId,
+			res->ssId.length);
+		res->timer = scan_result->timer;
+		qdf_mem_copy(&res->BssDescriptor, &scan_result->BssDescriptor,
+			sizeof(tSirBssDescription));
+		status = QDF_STATUS_SUCCESS;
+	} else {
+		status = QDF_STATUS_E_FAILURE;
+	}
+
+	csr_scan_result_purge(mac_ctx, filtered_scan_result);
+
+free_filter:
+	csr_free_scan_filter(mac_ctx, scan_filter);
+	if (scan_filter)
+		qdf_mem_free(scan_filter);
+
+	return status;
+}
+
 static inline QDF_STATUS
 csr_flush_scan_results(tpAniSirGlobal mac_ctx,
 	struct scan_filter *filter)

+ 13 - 0
core/sme/src/csr/csr_inside_api.h

@@ -560,6 +560,19 @@ QDF_STATUS csr_scan_request(tpAniSirGlobal pMac, uint16_t sessionId,
 QDF_STATUS csr_scan_get_result(tpAniSirGlobal pMac, tCsrScanResultFilter
 				*pFilter, tScanResultHandle *phResult);
 
+/**
+ * csr_scan_get_result_for_bssid - gets the scan result from scan cache for the
+ *      bssid specified
+ * @mac_ctx: mac context
+ * @bssid: bssid to get the scan result for
+ * @res: pointer to tCsrScanResultInfo
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS csr_scan_get_result_for_bssid(tpAniSirGlobal mac_ctx,
+					 struct qdf_mac_addr *bssid,
+					 tCsrScanResultInfo *res);
+
 /*
  * csr_scan_flush_result() -
  * Clear scan results.

+ 40 - 0
core/sme/src/csr/csr_util.c

@@ -6752,3 +6752,43 @@ bool csr_is_ndi_started(tpAniSirGlobal mac_ctx, uint32_t session_id)
 
 	return eCSR_CONNECT_STATE_TYPE_NDI_STARTED == session->connectState;
 }
+
+bool csr_is_mcc_channel(tHalHandle hal, uint8_t channel)
+{
+	tpAniSirGlobal mac_ctx = PMAC_STRUCT(hal);
+	struct csr_roam_session *session;
+	enum QDF_OPMODE oper_mode;
+	uint8_t oper_channel = 0;
+	uint8_t session_id;
+
+	if (channel == 0)
+		return false;
+
+	for (session_id = 0; session_id < CSR_ROAM_SESSION_MAX; session_id++) {
+		if (CSR_IS_SESSION_VALID(mac_ctx, session_id)) {
+			session = CSR_GET_SESSION(mac_ctx, session_id);
+			if (NULL == session->pCurRoamProfile)
+				continue;
+			oper_mode = session->pCurRoamProfile->csrPersona;
+			if ((((oper_mode == QDF_STA_MODE) ||
+			    (oper_mode == QDF_P2P_CLIENT_MODE)) &&
+			    (session->connectState ==
+			    eCSR_ASSOC_STATE_TYPE_INFRA_ASSOCIATED)) ||
+			    (((oper_mode == QDF_P2P_GO_MODE) ||
+			    (oper_mode == QDF_SAP_MODE))
+			    && (session->connectState !=
+			    eCSR_ASSOC_STATE_TYPE_NOT_CONNECTED)))
+				oper_channel = session->connectedProfile.
+					operationChannel;
+
+			if (oper_channel && channel != oper_channel &&
+			    (!policy_mgr_is_hw_dbs_capable(mac_ctx->psoc) ||
+			    WLAN_REG_IS_SAME_BAND_CHANNELS(channel,
+						 oper_channel)))
+				return true;
+		}
+	}
+
+	return false;
+}
+