wlan: Add BTSCO handling for SWPTA BTC design

If BTSCO profile is enabled, abort any ongoing scan,
disable scan and disconnect STA if connected.

If BTSCO profile is disabled, enable scan and allow
connection.

Change-Id: Ib32176ee627d4b21d8bbade8a8fb7577452ba967
CRs-Fixed: 2792127
diff --git a/CORE/HDD/inc/wlan_hdd_main.h b/CORE/HDD/inc/wlan_hdd_main.h
index ad479c9..31f5c6a 100644
--- a/CORE/HDD/inc/wlan_hdd_main.h
+++ b/CORE/HDD/inc/wlan_hdd_main.h
@@ -1572,6 +1572,10 @@
 	struct hdd_cache_channel_info *channel_info;
 };
 
+#ifdef FEATURE_WLAN_SW_PTA
+#define WLAN_WAIT_TIME_SW_PTA 1000
+#endif
+
 struct hdd_context_s
 {
    /** Global VOS context  */
@@ -1791,6 +1795,7 @@
 
     v_BOOL_t btCoexModeSet;
     v_BOOL_t isPnoEnable;
+    bool     is_sco_enabled;
     macAddrSpoof_t spoofMacAddr;
     /* flag to decide if driver need to scan DFS channels or not */
     v_BOOL_t  disable_dfs_flag;
@@ -1863,6 +1868,9 @@
     struct hdd_cache_channels *original_channels;
     struct mutex cache_channel_lock;
     bool force_rsne_override;
+#ifdef FEATURE_WLAN_SW_PTA
+    struct completion sw_pta_comp;
+#endif
 };
 
 /* Use to notify the TDLS or BTCOEX is mode enable */
diff --git a/CORE/HDD/src/wlan_hdd_cfg80211.c b/CORE/HDD/src/wlan_hdd_cfg80211.c
index 39ec1e1..ad272eb 100644
--- a/CORE/HDD/src/wlan_hdd_cfg80211.c
+++ b/CORE/HDD/src/wlan_hdd_cfg80211.c
@@ -15804,6 +15804,19 @@
     }
     mutex_unlock(&pHddCtx->tmInfo.tmOperationLock);
 
+    /**
+     * If sw pta is enabled, scan should not allowed.
+     * Returning error makes framework to trigger scan continuously
+     * for every second, so indicating framework that scan is aborted
+     * and return success.
+     */
+    if (pHddCtx->cfg_ini->is_sw_pta_enabled && pHddCtx->is_sco_enabled) {
+        VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
+                  FL("BT SCO operation in progress"));
+        hdd_cfg80211_scan_done(pAdapter, request, true);
+        return 0;
+    }
+
     /* Check if scan is allowed at this point of time.
      */
     if (TRUE == pHddCtx->btCoexModeSet)
@@ -16322,6 +16335,14 @@
         return -EINVAL;
     }
 
+    /**
+     * If sw pta is enabled, new connections should not allowed.
+     */
+    if (pHddCtx->cfg_ini->is_sw_pta_enabled && pHddCtx->is_sco_enabled) {
+        hddLog(VOS_TRACE_LEVEL_ERROR, "%s: BT SCO operation in progress",
+               __func__);
+        return -EINVAL;
+    }
 
     pRoamProfile = &pWextState->roamProfile;
 
@@ -20077,6 +20098,15 @@
     }
     spin_unlock(&pHddCtx->schedScan_lock);
 
+    /**
+     * If sw pta is enabled, scan results should not send to framework.
+     */
+    if (pHddCtx->cfg_ini->is_sw_pta_enabled && pHddCtx->is_sco_enabled) {
+        VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
+                  FL("BT SCO operation in progress"));
+        return;
+    }
+
     ret = wlan_hdd_cfg80211_update_bss(pHddCtx->wiphy, pAdapter);
 
     if (0 > ret)
diff --git a/CORE/HDD/src/wlan_hdd_main.c b/CORE/HDD/src/wlan_hdd_main.c
index d39b65d..86c7625 100644
--- a/CORE/HDD/src/wlan_hdd_main.c
+++ b/CORE/HDD/src/wlan_hdd_main.c
@@ -3922,9 +3922,15 @@
 		return;
 	}
 
-	if(sysfs_create_file(driver_kobject, &bt_profile_attribute.attr))
+	if (sysfs_create_file(driver_kobject, &bt_profile_attribute.attr)) {
 		hddLog(VOS_TRACE_LEVEL_ERROR,
 		       "%s:Failed to create BT profile sysfs entry", __func__);
+		kobject_put(driver_kobject);
+		driver_kobject = NULL;
+		return;
+	}
+
+	init_completion(&hdd_ctx->sw_pta_comp);
 }
 
 static void hdd_sysfs_bt_profile_destroy(hdd_context_t* hdd_ctx)
@@ -3932,8 +3938,10 @@
 	if(!hdd_ctx->cfg_ini->is_sw_pta_enabled)
 		return;
 
-	sysfs_remove_file(driver_kobject, &bt_profile_attribute.attr);
+	complete(&hdd_ctx->sw_pta_comp);
+
 	if (driver_kobject) {
+		sysfs_remove_file(driver_kobject, &bt_profile_attribute.attr);
 		kobject_put(driver_kobject);
 		driver_kobject = NULL;
 	}
@@ -3963,6 +3971,146 @@
 	return 0;
 }
 
+static void hdd_sco_resp_callback(uint8_t sco_status)
+{
+	hdd_context_t *hdd_ctx = NULL;
+	v_CONTEXT_t vos_ctx = NULL;
+
+	vos_ctx = vos_get_global_context(VOS_MODULE_ID_SYS, NULL);
+	if (!vos_ctx) {
+		hddLog(VOS_TRACE_LEVEL_FATAL,
+		       "%s: Global VOS context is Null", __func__);
+		return;
+	}
+
+	/* Get the HDD context. */
+	hdd_ctx = (hdd_context_t*)vos_get_context(VOS_MODULE_ID_HDD, vos_ctx);
+	if (!hdd_ctx) {
+		hddLog(VOS_TRACE_LEVEL_FATAL,
+		       "%s: HDD context is Null",__func__);
+		return;
+	}
+
+	hddLog(VOS_TRACE_LEVEL_DEBUG, "%s: Response status %d",
+	       __func__, sco_status);
+
+	if (sco_status) {
+		hddLog(VOS_TRACE_LEVEL_FATAL, "%s: Invalid sco status %d",
+		       __func__, sco_status);
+		return;
+	}
+
+	complete(&hdd_ctx->sw_pta_comp);
+}
+
+static ssize_t __hdd_process_bt_sco_profile(hdd_context_t *hdd_ctx,
+					    char *profile_mode)
+{
+	tpAniSirGlobal mac_ctx = PMAC_STRUCT(hdd_ctx->hHal);
+	hdd_station_ctx_t *hdd_sta_ctx;
+	eConnectionState conn_state;
+	hdd_adapter_t *adapter;
+	eHalStatus hal_status;
+	bool sco_status;
+	int rc;
+
+	if (!mac_ctx) {
+		hddLog(VOS_TRACE_LEVEL_ERROR, "%s: mac_ctx got NULL", __func__);
+		return -EINVAL;
+	}
+
+	if (!strcmp(profile_mode, "ENABLE")) {
+		if (hdd_ctx->is_sco_enabled) {
+			hddLog(VOS_TRACE_LEVEL_ERROR, "%s: BT SCO is already enabled",
+			       __func__);
+			return 0;
+		}
+		sco_status = true;
+	} else if (!strcmp(profile_mode, "DISABLE")) {
+		if (!hdd_ctx->is_sco_enabled) {
+			hddLog(VOS_TRACE_LEVEL_ERROR, "%s: BT SCO is already disabled",
+			       __func__);
+			return 0;
+		}
+		sco_status = false;
+	} else {
+		hddLog(VOS_TRACE_LEVEL_ERROR, "%s: Invalid profile mode %s",
+		       __func__, profile_mode);
+		return -EINVAL;
+	}
+
+	INIT_COMPLETION(hdd_ctx->sw_pta_comp);
+
+	hal_status = sme_sco_req(hdd_ctx->hHal,
+				 hdd_sco_resp_callback,
+				 adapter->sessionId, sco_status);
+	if (!HAL_STATUS_SUCCESS(hal_status)) {
+		hddLog(VOS_TRACE_LEVEL_ERROR,
+		       "%s: Error sending sme sco indication request",
+		       __func__);
+		return -EINVAL;
+	}
+
+	rc = wait_for_completion_timeout(&hdd_ctx->sw_pta_comp,
+			msecs_to_jiffies(WLAN_WAIT_TIME_SW_PTA));
+	if (!rc) {
+		hddLog(VOS_TRACE_LEVEL_ERROR,
+		       FL("Target response timed out for sw_pta_comp"));
+		return -EINVAL;
+	}
+
+	if (!strcmp(profile_mode, "DISABLE")) {
+		hdd_ctx->is_sco_enabled = false;
+		mac_ctx->isCoexScoIndSet = 0;
+		return 0;
+	}
+
+	adapter = hdd_get_adapter(hdd_ctx, WLAN_HDD_INFRA_STATION);
+	if (!adapter) {
+		hddLog(VOS_TRACE_LEVEL_ERROR,
+		       "%s: No station adapter to enable bt sco", __func__);
+		return -EINVAL;
+	}
+
+	hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
+	if (!hdd_sta_ctx) {
+		hddLog(VOS_TRACE_LEVEL_ERROR,
+		       "%s: No station context to enable bt sco", __func__);
+		return -EINVAL;
+	}
+
+	if (wlan_hdd_scan_abort(adapter)) {
+		hddLog(VOS_TRACE_LEVEL_ERROR, "%s: Error aborting scan request",
+		       __func__);
+		return -EINVAL;
+	}
+
+	hdd_ctx->is_sco_enabled = true;
+	mac_ctx->isCoexScoIndSet = 1;
+
+	conn_state = hdd_sta_ctx->conn_info.connState;
+	if (eConnectionState_Connecting == conn_state ||
+	    smeNeighborMiddleOfRoaming(hdd_sta_ctx) ||
+	    (eConnectionState_Associated == conn_state &&
+	      sme_is_sta_key_exchange_in_progress(hdd_ctx->hHal,
+						  adapter->sessionId)))
+		sme_abortConnection(hdd_ctx->hHal,
+				    adapter->sessionId);
+
+	if (hdd_connIsConnected(hdd_sta_ctx)) {
+		hal_status = sme_teardown_link_with_ap(mac_ctx,
+						       adapter->sessionId);
+		if (!HAL_STATUS_SUCCESS(hal_status)) {
+			hddLog(VOS_TRACE_LEVEL_ERROR,
+			       "%s: Error while Teardown link wih AP",
+			       __func__);
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
 static ssize_t __hdd_sysfs_bt_profile_ind_cmd_store(hdd_context_t *hdd_ctx,
 						    const char *buf,
 						    size_t count)
@@ -3973,8 +4121,11 @@
 
 	ENTER();
 
-	if (wlan_hdd_validate_context(hdd_ctx))
+	if (wlan_hdd_validate_context(hdd_ctx)) {
+		if (hdd_ctx && hdd_ctx->isLogpInProgress)
+			return -EAGAIN;
 		return -EINVAL;
+	}
 
 	ret = hdd_sysfs_validate_and_copy_buf(buf_local, sizeof(buf_local),
 					      buf, count);
@@ -4001,7 +4152,12 @@
 	hddLog(VOS_TRACE_LEVEL_INFO, "%s:profile = %s, profile_mode = %s",
 	       __func__, profile, profile_mode);
 
+	if (!strcmp(profile, "BT_PROFILE_SCO"))
+		if (__hdd_process_bt_sco_profile(hdd_ctx, profile_mode))
+			return -EINVAL;
+
 	EXIT();
+
 	return count;
 }
 
diff --git a/CORE/MAC/inc/wniApi.h b/CORE/MAC/inc/wniApi.h
index e1ea5a5..fcc2ea9 100644
--- a/CORE/MAC/inc/wniApi.h
+++ b/CORE/MAC/inc/wniApi.h
@@ -406,6 +406,7 @@
     eWNI_SME_SEND_SAE_MSG,
 #ifdef FEATURE_WLAN_SW_PTA
     eWNI_SME_SW_PTA_RESP,
+    eWNI_SME_TEARDOWN_LINK_WITH_AP,
 #endif
     eWNI_SME_MSG_TYPES_END
 };
diff --git a/CORE/MAC/src/include/sirParams.h b/CORE/MAC/src/include/sirParams.h
index 4f76572..d8c9af2 100644
--- a/CORE/MAC/src/include/sirParams.h
+++ b/CORE/MAC/src/include/sirParams.h
@@ -283,6 +283,18 @@
     uint8_t *data;
 };
 
+#ifdef FEATURE_WLAN_SW_PTA
+/**
+ * struct sir_teardown_link - Struct used to tear down link with AP
+ * @type: Message type
+ * @session_id: session id
+ */
+struct sir_teardown_link {
+    uint16_t type;
+    uint8_t session_id;
+};
+#endif
+
 /// Message queue definitions
 //  msgtype(2bytes) reserved(2bytes) bodyptr(4bytes) bodyval(4bytes)
 //  NOTE tSirMsgQ should be always multiples of WORD(4Bytes)
diff --git a/CORE/MAC/src/pe/lim/limProcessMessageQueue.c b/CORE/MAC/src/pe/lim/limProcessMessageQueue.c
index 441d943..0972fa6 100644
--- a/CORE/MAC/src/pe/lim/limProcessMessageQueue.c
+++ b/CORE/MAC/src/pe/lim/limProcessMessageQueue.c
@@ -1937,6 +1937,18 @@
             vos_mem_free(limMsg->bodyptr);
             limMsg->bodyptr = NULL;
             break;
+#ifdef FEATURE_WLAN_SW_PTA
+        case eWNI_SME_TEARDOWN_LINK_WITH_AP:
+        {
+            struct sir_teardown_link *msg;
+
+            msg = (struct sir_teardown_link *)limMsg->bodyptr;
+            limTearDownLinkWithAp(pMac, msg->session_id,
+                                  eSIR_MAC_UNSPEC_FAILURE_REASON);
+            vos_mem_free(limMsg->bodyptr);
+            limMsg->bodyptr = NULL;
+        }
+#endif
 
 #ifdef WLAN_FEATURE_RMC
         case eWNI_SME_ENABLE_RMC_REQ:
diff --git a/CORE/SME/inc/sme_Api.h b/CORE/SME/inc/sme_Api.h
index cb6592c..9c5d509 100644
--- a/CORE/SME/inc/sme_Api.h
+++ b/CORE/SME/inc/sme_Api.h
@@ -4128,6 +4128,17 @@
 eHalStatus sme_send_mgmt_tx(tHalHandle hal, uint8_t session_id,
                                 const uint8_t *buf, uint32_t len);
 
+#ifdef FEATURE_WLAN_SW_PTA
+/**
+ * sme_teardown_link_with_ap() - indicates teardown link with AP
+ * @mac: mac context
+ * @session_id: session id
+ *
+ * Return: eHalStatus
+ */
+eHalStatus sme_teardown_link_with_ap(tpAniSirGlobal mac, uint8_t session_id);
+#endif
+
 #ifdef WLAN_FEATURE_SAE
 /**
  * sme_handle_sae_msg() - Sends SAE message received from supplicant
diff --git a/CORE/SME/inc/sme_Trace.h b/CORE/SME/inc/sme_Trace.h
index 9fd4495..90c6f58 100644
--- a/CORE/SME/inc/sme_Trace.h
+++ b/CORE/SME/inc/sme_Trace.h
@@ -149,6 +149,9 @@
     TRACE_CODE_SME_RX_HDD_SEND_MGMT_TX,
     TRACE_CODE_SME_TX_HDD_CAP_TSF_REQ,
     TRACE_CODE_SME_TX_HDD_GET_TSF_REQ,
+#ifdef FEATURE_WLAN_SW_PTA
+    TRACE_CODE_SME_TX_HDD_TEARDOWN_LINK_WITH_AP,
+#endif
     TRACE_CODE_SME_DEL_STA_BA_SESSION_REQ,
     /* New trace commands to be added before this comment not at the end */
     /* Trace codes for SME commands */
diff --git a/CORE/SME/src/sme_common/sme_Api.c b/CORE/SME/src/sme_common/sme_Api.c
index 7d83661..3382ed1 100644
--- a/CORE/SME/src/sme_common/sme_Api.c
+++ b/CORE/SME/src/sme_common/sme_Api.c
@@ -15506,6 +15506,40 @@
     return status;
 }
 
+#ifdef FEATURE_WLAN_SW_PTA
+eHalStatus sme_teardown_link_with_ap(tpAniSirGlobal mac, uint8_t session_id)
+{
+	eHalStatus status = eHAL_STATUS_SUCCESS;
+	struct sir_teardown_link *msg;
+	vos_msg_t vos_message = {0};
+	VOS_STATUS vos_status;
+
+	MTRACE(vos_trace(VOS_MODULE_ID_SME,
+	       TRACE_CODE_SME_TX_HDD_TEARDOWN_LINK_WITH_AP, session_id, 0));
+
+	status = sme_AcquireGlobalLock(&mac->sme);
+	if (HAL_STATUS_SUCCESS(status)) {
+		msg = vos_mem_malloc(sizeof(*msg));
+		if (!msg) {
+			status = eHAL_STATUS_FAILED_ALLOC;
+		} else {
+			msg->type = eWNI_SME_TEARDOWN_LINK_WITH_AP;
+			msg->session_id = session_id;
+			vos_message.bodyptr = msg;
+			vos_message.type =  eWNI_SME_TEARDOWN_LINK_WITH_AP;
+			vos_status = vos_mq_post_message(VOS_MQ_ID_PE,
+							 &vos_message);
+			if (!VOS_IS_STATUS_SUCCESS(vos_status)) {
+				vos_mem_free(msg);
+				status = eHAL_STATUS_FAILURE;
+			}
+		}
+		sme_ReleaseGlobalLock(&mac->sme);
+	}
+	return status;
+}
+#endif
+
 eHalStatus sme_send_mgmt_tx(tHalHandle hal, uint8_t session_id,
                             const uint8_t *buf, uint32_t len)
 {
diff --git a/CORE/SYS/legacy/src/utils/src/macTrace.c b/CORE/SYS/legacy/src/utils/src/macTrace.c
index 6bd31ab..1c8f2db 100644
--- a/CORE/SYS/legacy/src/utils/src/macTrace.c
+++ b/CORE/SYS/legacy/src/utils/src/macTrace.c
@@ -589,6 +589,9 @@
         CASE_RETURN_STRING(eWNI_SME_TRIGGER_SAE);
         CASE_RETURN_STRING(eWNI_SME_SEND_MGMT_FRAME_TX);
         CASE_RETURN_STRING(eWNI_SME_SEND_SAE_MSG);
+#ifdef FEATURE_WLAN_SW_PTA
+        CASE_RETURN_STRING(eWNI_SME_TEARDOWN_LINK_WITH_AP);
+#endif
         default:
             return( (tANI_U8*)"UNKNOWN" );
             break;