qcacld-3.0: Avoid new connection while HW mode change is issued

In the case of concurrency, when the session decrements, the
opportunistic timer is started. After a while, when the
opportunistic time is expired, and the hw mode change for SMM
is issued, and the same time new interface comes up, then the
new forms MCC with the existing connection instead of forming
DBS.

The change is to avoid the race condition to form MCC instead of
DBS.

Change-Id: I977039138509676b964ba089e3cb479cd16968bb
CRs-Fixed: 2006350
diff --git a/core/cds/inc/cds_concurrency.h b/core/cds/inc/cds_concurrency.h
index 6862292..87706bb 100644
--- a/core/cds/inc/cds_concurrency.h
+++ b/core/cds/inc/cds_concurrency.h
@@ -676,6 +676,24 @@
 };
 
 /**
+ * enum cds_hw_mode_change - identify the HW mode switching to.
+ *
+ * @CDS_HW_MODE_NOT_IN_PROGRESS: HW mode change not in progress
+ * @CDS_SMM_IN_PROGRESS: switching to SMM mode
+ * @CDS_DBS_IN_PROGRESS: switching to DBS mode
+ * @CDS_SBS_IN_PROGRESS: switching to SBS mode
+ *
+ * These are generic IDs that identify the various roles
+ * in the software system
+ */
+enum cds_hw_mode_change {
+	CDS_HW_MODE_NOT_IN_PROGRESS = 0,
+	CDS_SMM_IN_PROGRESS,
+	CDS_DBS_IN_PROGRESS,
+	CDS_SBS_IN_PROGRESS
+};
+
+/**
  * struct cds_conc_connection_info - information of all existing
  * connections in the wlan system
  *
@@ -968,4 +986,6 @@
 			uint32_t new_hw_mode_index,
 			uint32_t num_vdev_mac_entries,
 			 struct sir_vdev_mac_map *vdev_mac_map);
+void cds_set_hw_mode_change_in_progress(enum cds_hw_mode_change value);
+enum cds_hw_mode_change cds_is_hw_mode_change_in_progress(void);
 #endif /* __CDS_CONCURRENCY_H */
diff --git a/core/cds/inc/cds_sched.h b/core/cds/inc/cds_sched.h
index f76ae48..43a3b72 100644
--- a/core/cds/inc/cds_sched.h
+++ b/core/cds/inc/cds_sched.h
@@ -242,6 +242,9 @@
 	bool do_hw_mode_change;
 	bool enable_fatal_event;
 	struct cds_config_info *cds_cfg;
+
+	/* This is to track if HW mode change is in progress */
+	uint32_t hw_mode_change_in_progress;
 } cds_context_type, *p_cds_contextType;
 
 /*---------------------------------------------------------------------------
diff --git a/core/cds/src/cds_concurrency.c b/core/cds/src/cds_concurrency.c
index 1d8d1ef..aba4e52 100644
--- a/core/cds/src/cds_concurrency.c
+++ b/core/cds/src/cds_concurrency.c
@@ -654,6 +654,8 @@
 	struct sir_hw_mode_params hw_mode;
 	uint32_t i;
 
+	cds_set_hw_mode_change_in_progress(CDS_HW_MODE_NOT_IN_PROGRESS);
+
 	if (status != SET_HW_MODE_STATUS_OK) {
 		cds_err("Set HW mode failed with status %d", status);
 		return;
@@ -2308,6 +2310,7 @@
 	}
 
 	cds_ctx->do_hw_mode_change = false;
+	cds_ctx->hw_mode_change_in_progress = CDS_HW_MODE_NOT_IN_PROGRESS;
 	cds_ctx->sme_get_valid_channels = sme_cbacks->sme_get_valid_channels;
 	cds_ctx->sme_get_nss_for_vdev = sme_cbacks->sme_get_nss_for_vdev;
 
@@ -8308,3 +8311,58 @@
 
 	return flag;
 }
+
+/**
+ * cds_set_hw_mode_change_in_progress() - Set value corresponding to
+ * cds_hw_mode_change that indicate if HW mode change is in progress
+ * @value: Indicate if hw mode change is in progress
+ *
+ * Set the value corresponding to cds_hw_mode_change that
+ * indicated if hw mode change is in progress.
+ *
+ * Return: None
+ */
+void cds_set_hw_mode_change_in_progress(enum cds_hw_mode_change value)
+{
+	cds_context_type *cds_ctx;
+	cds_ctx = cds_get_context(QDF_MODULE_ID_QDF);
+
+	if (!cds_ctx) {
+		cds_err("Invalid CDS Context");
+		return;
+	}
+
+	qdf_mutex_acquire(&cds_ctx->qdf_conc_list_lock);
+	cds_ctx->hw_mode_change_in_progress = value;
+	qdf_mutex_release(&cds_ctx->qdf_conc_list_lock);
+
+	cds_debug("hw_mode_change_in_progress:%d", value);
+}
+
+/**
+ * cds_is_hw_mode_change_in_progress() - Check if HW mode change is in
+ * progress.
+ *
+ * Returns the corresponding cds_hw_mode_change value.
+ *
+ * Return: cds_hw_mode_change value.
+ */
+enum cds_hw_mode_change cds_is_hw_mode_change_in_progress(void)
+{
+	cds_context_type *cds_ctx;
+	enum cds_hw_mode_change value;
+	value = CDS_HW_MODE_NOT_IN_PROGRESS;
+
+	cds_ctx = cds_get_context(QDF_MODULE_ID_QDF);
+
+	if (!cds_ctx) {
+		cds_err("Invalid CDS Context");
+		return value;
+	}
+
+	qdf_mutex_acquire(&cds_ctx->qdf_conc_list_lock);
+	value = cds_ctx->hw_mode_change_in_progress;
+	qdf_mutex_release(&cds_ctx->qdf_conc_list_lock);
+
+	return value;
+}
diff --git a/core/hdd/src/wlan_hdd_cfg80211.c b/core/hdd/src/wlan_hdd_cfg80211.c
index 220e6e6..9eb174e 100644
--- a/core/hdd/src/wlan_hdd_cfg80211.c
+++ b/core/hdd/src/wlan_hdd_cfg80211.c
@@ -12819,11 +12819,18 @@
 
 	status = wlan_hdd_validate_context(pHddCtx);
 	if (status)
-		return status;
+		goto ret_status;
 
 	if (SIR_MAC_MAX_SSID_LENGTH < ssid_len) {
 		hdd_err("wrong SSID len");
-		return -EINVAL;
+		status = -EINVAL;
+		goto ret_status;
+	}
+
+	if (true == cds_is_connection_in_progress()) {
+		hdd_err("Connection refused: conn in progress");
+		status = -EINVAL;
+		goto ret_status;
 	}
 
 	pRoamProfile = &pWextState->roamProfile;
@@ -12834,6 +12841,24 @@
 		hdd_station_ctx_t *pHddStaCtx;
 		pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
 
+		/* Restart the opportunistic timer
+		 *
+		 * If hw_mode_change_in_progress is true, then wait
+		 * till firmware sends the callback for hw_mode change.
+		 *
+		 * Else set connect_in_progress as true and proceed.
+		 */
+		cds_restart_opportunistic_timer(false);
+		if (cds_is_hw_mode_change_in_progress()) {
+			status = qdf_wait_for_connection_update();
+			if (!QDF_IS_STATUS_SUCCESS(status)) {
+				hdd_err("qdf wait for event failed!!");
+				status = -EINVAL;
+				goto ret_status;
+			}
+		}
+		cds_set_connection_in_progress(true);
+
 		if (HDD_WMM_USER_MODE_NO_QOS ==
 		    (WLAN_HDD_GET_CTX(pAdapter))->config->WmmMode) {
 			/*QoS not enabled in cfg file */
@@ -12965,7 +12990,8 @@
 
 			if (QDF_STATUS_SUCCESS != status) {
 				hdd_err("Set IBSS Power Save Params Failed");
-				return -EINVAL;
+				status = -EINVAL;
+				goto conn_failure;
 			}
 			pRoamProfile->ch_params.ch_width =
 				hdd_map_nl_chan_width(ch_width);
@@ -12991,12 +13017,9 @@
 					pWextState->roamProfile.MFPEnabled,
 					pWextState->roamProfile.MFPRequired,
 					pWextState->roamProfile.MFPCapable);
-			return -EINVAL;
-		}
 
-		if (true == cds_is_connection_in_progress()) {
-			hdd_err("Connection refused: conn in progress");
-			return -EINVAL;
+			status = -EINVAL;
+			goto conn_failure;
 		}
 
 		/*
@@ -13023,8 +13046,10 @@
 		if (wma_is_hw_dbs_capable() == false) {
 			cds_handle_conc_rule1(pAdapter, pRoamProfile);
 			if (true != cds_handle_conc_rule2(
-					pAdapter, pRoamProfile, &roamId))
-				return 0;
+					pAdapter, pRoamProfile, &roamId)) {
+				status = 0;
+				goto conn_failure;
+			}
 		}
 
 		if ((wma_is_hw_dbs_capable() == true) &&
@@ -13033,7 +13058,8 @@
 			hdd_err("sap-sta conc will fail, can't allow sta");
 			hdd_conn_set_connection_state(pAdapter,
 					eConnectionState_NotConnected);
-			return -ENOMEM;
+			status = -ENOMEM;
+			goto conn_failure;
 		}
 
 		sme_config = qdf_mem_malloc(sizeof(*sme_config));
@@ -13041,7 +13067,8 @@
 			hdd_err("unable to allocate sme_config");
 			hdd_conn_set_connection_state(pAdapter,
 					eConnectionState_NotConnected);
-			return -ENOMEM;
+			status = -ENOMEM;
+			goto conn_failure;
 		}
 		sme_get_config_param(pHddCtx->hHal, sme_config);
 		/* These values are not sessionized. So, any change in these SME
@@ -13100,6 +13127,9 @@
 						     connect);
 		}
 
+		/* Reset connect_in_progress */
+		cds_set_connection_in_progress(false);
+
 		pRoamProfile->ChannelInfo.ChannelList = NULL;
 		pRoamProfile->ChannelInfo.numOfChannels = 0;
 
@@ -13114,8 +13144,15 @@
 
 	} else {
 		hdd_err("No valid Roam profile");
-		return -EINVAL;
+		status = -EINVAL;
 	}
+	goto ret_status;
+
+conn_failure:
+	/* Reset connect_in_progress */
+	cds_set_connection_in_progress(false);
+
+ret_status:
 	EXIT();
 	return status;
 }
diff --git a/core/hdd/src/wlan_hdd_hostapd.c b/core/hdd/src/wlan_hdd_hostapd.c
index 8eac2da..40a948f 100644
--- a/core/hdd/src/wlan_hdd_hostapd.c
+++ b/core/hdd/src/wlan_hdd_hostapd.c
@@ -7218,6 +7218,14 @@
 		return -EINVAL;
 	}
 
+	if (cds_is_hw_mode_change_in_progress()) {
+		status = qdf_wait_for_connection_update();
+		if (!QDF_IS_STATUS_SUCCESS(status)) {
+			hdd_err("qdf wait for event failed!!");
+			return -EINVAL;
+		}
+	}
+
 	iniConfig = pHddCtx->config;
 	pHostapdState = WLAN_HDD_GET_HOSTAP_STATE_PTR(pHostapdAdapter);
 
@@ -8082,6 +8090,14 @@
 		return -EBUSY;
 	}
 
+	if (cds_is_hw_mode_change_in_progress()) {
+		status = qdf_wait_for_connection_update();
+		if (!QDF_IS_STATUS_SUCCESS(status)) {
+			hdd_err("qdf wait for event failed!!");
+			return -EINVAL;
+		}
+	}
+
 	channel_width = wlan_hdd_get_channel_bw(params->chandef.width);
 	channel = ieee80211_frequency_to_channel(
 				params->chandef.chan->center_freq);
diff --git a/core/sme/src/csr/csr_api_roam.c b/core/sme/src/csr/csr_api_roam.c
index 0775c69..9483a1e 100644
--- a/core/sme/src/csr/csr_api_roam.c
+++ b/core/sme/src/csr/csr_api_roam.c
@@ -19342,6 +19342,7 @@
 	QDF_STATUS status;
 	struct scheduler_msg msg;
 	struct sir_set_hw_mode_resp *param;
+	enum cds_hw_mode_change cds_hw_mode;
 
 	/* Setting HW mode is for the entire system.
 	 * So, no need to check session
@@ -19375,6 +19376,22 @@
 		}
 	}
 
+	if ((SIR_UPDATE_REASON_OPPORTUNISTIC ==
+	     command->u.set_hw_mode_cmd.reason) &&
+	    (true == cds_is_connection_in_progress())) {
+		sms_log(mac, LOGE, FL("Set HW mode refused: conn in progress"));
+		cds_restart_opportunistic_timer(false);
+		goto fail;
+	}
+
+	cds_hw_mode = wma_get_cds_hw_mode_change_from_hw_mode_index(
+				command->u.set_hw_mode_cmd.hw_mode_index);
+
+	if (CDS_HW_MODE_NOT_IN_PROGRESS == cds_hw_mode)
+		goto fail;
+
+	cds_set_hw_mode_change_in_progress(cds_hw_mode);
+
 	cmd->messageType = eWNI_SME_SET_HW_MODE_REQ;
 	cmd->length = len;
 	cmd->set_hw.hw_mode_index = command->u.set_hw_mode_cmd.hw_mode_index;
@@ -19392,6 +19409,7 @@
 
 	status = umac_send_mb_message_to_mac(cmd);
 	if (QDF_STATUS_SUCCESS != status) {
+		cds_set_hw_mode_change_in_progress(CDS_HW_MODE_NOT_IN_PROGRESS);
 		sms_log(mac, LOGE, FL("Posting to PE failed"));
 		return;
 	}
diff --git a/core/wma/inc/wma_api.h b/core/wma/inc/wma_api.h
index 9133a7f..7fe6ab1 100644
--- a/core/wma/inc/wma_api.h
+++ b/core/wma/inc/wma_api.h
@@ -200,6 +200,9 @@
 QDF_STATUS wma_get_dbs_hw_modes(bool *one_by_one_dbs, bool *two_by_two_dbs);
 QDF_STATUS wma_get_current_hw_mode(struct sir_hw_mode_params *hw_mode);
 bool wma_is_dbs_enable(void);
+enum cds_hw_mode_change
+wma_get_cds_hw_mode_change_from_hw_mode_index(uint32_t hw_mode_index);
+
 QDF_STATUS wma_get_updated_scan_config(uint32_t *scan_config,
 		bool dbs_scan,
 		bool dbs_plus_agile_scan,
diff --git a/core/wma/src/wma_utils.c b/core/wma/src/wma_utils.c
index 8350bf6..75c11a9 100644
--- a/core/wma/src/wma_utils.c
+++ b/core/wma/src/wma_utils.c
@@ -3108,6 +3108,51 @@
 	return true;
 }
 
+/*
+ * wma_get_cds_hw_mode_change_from_hw_mode_index - Returns value in terms of
+ * cds_hw_mode_change enums derived from hw_mode_index.
+ *
+ * Returns cds_hw_mode_change value derived from hw_mode_index.
+ *
+ * Return: value in terms of cds_hw_mode_change enums.
+ */
+enum cds_hw_mode_change
+wma_get_cds_hw_mode_change_from_hw_mode_index(uint32_t hw_mode_index)
+{
+	tp_wma_handle wma;
+	uint32_t param = 0;
+	enum cds_hw_mode_change value = CDS_HW_MODE_NOT_IN_PROGRESS;
+
+	wma = cds_get_context(QDF_MODULE_ID_WMA);
+	if (!wma) {
+		WMA_LOGE("%s: Invalid WMA handle", __func__);
+		goto ret_value;
+	}
+
+	WMA_LOGI("%s: HW param: %x", __func__, param);
+
+	param = wma->hw_mode.hw_mode_list[hw_mode_index];
+	if (WMA_HW_MODE_DBS_MODE_GET(param)) {
+		WMA_LOGI("%s: DBS is requested with HW (%d)", __func__,
+			 hw_mode_index);
+		value = CDS_DBS_IN_PROGRESS;
+		goto ret_value;
+	}
+
+	if (WMA_HW_MODE_SBS_MODE_GET(param)) {
+		WMA_LOGI("%s: SBS is requested with HW (%d)", __func__,
+			 hw_mode_index);
+		value = CDS_SBS_IN_PROGRESS;
+		goto ret_value;
+	}
+
+	value = CDS_SMM_IN_PROGRESS;
+	WMA_LOGI("%s: SMM is requested with HW (%d)", __func__,
+		 hw_mode_index);
+ret_value:
+	return value;
+}
+
 /**
  * wma_get_mac_id_of_vdev() - Get MAC id corresponding to a vdev
  * @vdev_id: VDEV whose MAC ID is required