qcacld-3.0: Send Deauth and delete all TDLS stations

This change addresses sending Deauth to TDLS peers,
delete TDLS stations and clear hdd structures if
concurrency is detected.

Change-Id: Ia9f6724a9db20c6d9ebfc2fa735569305f35f084
CRs-Fixed: 2022489
diff --git a/core/hdd/inc/wlan_hdd_assoc.h b/core/hdd/inc/wlan_hdd_assoc.h
index 82377a7..18e793f 100644
--- a/core/hdd/inc/wlan_hdd_assoc.h
+++ b/core/hdd/inc/wlan_hdd_assoc.h
@@ -289,6 +289,8 @@
 				     uint8_t ucastSig, uint8_t qos);
 #endif
 
+QDF_STATUS hdd_roam_deregister_tdlssta(hdd_adapter_t *pAdapter, uint8_t staId);
+
 /**
  * hdd_perform_roam_set_key_complete() - perform set key complete
  * @pAdapter: pointer to adapter
diff --git a/core/hdd/src/wlan_hdd_assoc.c b/core/hdd/src/wlan_hdd_assoc.c
index 85f5009..25ea28d 100644
--- a/core/hdd/src/wlan_hdd_assoc.c
+++ b/core/hdd/src/wlan_hdd_assoc.c
@@ -3565,8 +3565,7 @@
  *
  * Return: QDF_STATUS enumeration
  */
-static QDF_STATUS hdd_roam_deregister_tdlssta(hdd_adapter_t *pAdapter,
-					      uint8_t staId)
+QDF_STATUS hdd_roam_deregister_tdlssta(hdd_adapter_t *pAdapter, uint8_t staId)
 {
 	QDF_STATUS qdf_status;
 	qdf_status = cdp_clear_peer(cds_get_context(QDF_MODULE_ID_SOC),
@@ -3826,9 +3825,11 @@
 				} else
 				    mutex_unlock(&pHddCtx->tdls_lock);
 
+				mutex_lock(&pHddCtx->tdls_lock);
 				wlan_hdd_tdls_reset_peer(pAdapter,
 							 pRoamInfo->
 							 peerMac.bytes);
+				mutex_unlock(&pHddCtx->tdls_lock);
 
 				pHddCtx->tdlsConnInfo[staIdx].staId = 0;
 				pHddCtx->tdlsConnInfo[staIdx].
@@ -3878,11 +3879,13 @@
 						       [staIdx].
 						       peerMac.
 						       bytes));
+				mutex_lock(&pHddCtx->tdls_lock);
 				wlan_hdd_tdls_reset_peer(pAdapter,
 							 pHddCtx->
 							 tdlsConnInfo
 							 [staIdx].
 							 peerMac.bytes);
+				mutex_unlock(&pHddCtx->tdls_lock);
 				hdd_roam_deregister_tdlssta(pAdapter,
 							    pHddCtx->
 							    tdlsConnInfo
@@ -4148,7 +4151,7 @@
 }
 #else
 
-static inline QDF_STATUS hdd_roam_deregister_tdlssta(hdd_adapter_t *pAdapter,
+inline QDF_STATUS hdd_roam_deregister_tdlssta(hdd_adapter_t *pAdapter,
 					      uint8_t staId)
 {
 	return QDF_STATUS_SUCCESS;
diff --git a/core/hdd/src/wlan_hdd_tdls.c b/core/hdd/src/wlan_hdd_tdls.c
index 1e92181..2e58dad 100644
--- a/core/hdd/src/wlan_hdd_tdls.c
+++ b/core/hdd/src/wlan_hdd_tdls.c
@@ -43,6 +43,8 @@
 #include <net/ieee80211_radiotap.h>
 #include "wlan_hdd_tdls.h"
 #include "wlan_hdd_cfg80211.h"
+#include "wlan_hdd_assoc.h"
+#include "sme_api.h"
 #include "cds_sched.h"
 #include "wma_types.h"
 #include "cds_concurrency.h"
@@ -234,8 +236,10 @@
 
 	connected_tdls_peers = wlan_hdd_tdls_connected_peers(adapter);
 
-	if (!connected_tdls_peers)
-		return ;
+	if (!connected_tdls_peers) {
+		hdd_notice("No TDLS connected peers to delete");
+		return;
+	}
 
 	/* TDLS is not supported in case of concurrency.
 	 * Disable TDLS Offchannel in FW to avoid more
@@ -254,6 +258,9 @@
 			TDLS_SEC_OFFCHAN_OFFSET_40PLUS);
 	hdd_set_tdls_offchannelmode(adapter, DISABLE_CHANSWITCH);
 
+	/* Send Msg to PE for deleting all the TDLS peers */
+	sme_delete_all_tdls_peers(hddctx->hHal, adapter->sessionId);
+
 	for (staidx = 0; staidx < hddctx->max_num_tdls_sta;
 							staidx++) {
 		if (!hddctx->tdlsConnInfo[staidx].staId)
@@ -262,20 +269,35 @@
 		mutex_lock(&hddctx->tdls_lock);
 		curr_peer = wlan_hdd_tdls_find_all_peer(hddctx,
 				hddctx->tdlsConnInfo[staidx].peerMac.bytes);
-
 		if (!curr_peer)
 			continue;
 
 		hdd_notice("indicate TDLS teardown (staId %d)",
 			   curr_peer->staId);
 
+		/* Indicate teardown to supplicant */
 		wlan_hdd_tdls_indicate_teardown(
 					curr_peer->pHddTdlsCtx->pAdapter,
 					curr_peer,
 					eSIR_MAC_TDLS_TEARDOWN_UNSPEC_REASON);
+
+		/*
+		 * Del Sta happened already as part of sme_delete_all_tdls_peers
+		 * Hence clear hdd data structure.
+		 */
+		wlan_hdd_tdls_reset_peer(adapter, curr_peer->peerMac);
 		hdd_send_wlan_tdls_teardown_event(eTDLS_TEARDOWN_CONCURRENCY,
 			curr_peer->peerMac);
 		mutex_unlock(&hddctx->tdls_lock);
+
+		hdd_roam_deregister_tdlssta(adapter,
+			hddctx->tdlsConnInfo[staidx].staId);
+		wlan_hdd_tdls_decrement_peer_count(adapter);
+		hddctx->tdlsConnInfo[staidx].staId = 0;
+		hddctx->tdlsConnInfo[staidx].sessionId = 255;
+
+		qdf_mem_zero(&hddctx->tdlsConnInfo[staidx].peerMac,
+			     sizeof(struct qdf_mac_addr));
 	}
 }
 
@@ -1918,7 +1940,6 @@
 	tdlsCtx_t *hdd_tdls_ctx;
 	tdlsInfo_t *tdls_param;
 	QDF_STATUS qdf_ret_status = QDF_STATUS_E_FAILURE;
-	uint16_t staIdx;
 
 	/* If TDLS support is disabled then no need to update target */
 	if (false == hdd_ctx->config->fEnableTDLSSupport) {
@@ -1986,29 +2007,6 @@
 		hdd_warn("Concurrency not allowed in TDLS! set state cnt %d",
 			hdd_ctx->set_state_info.set_state_cnt);
 		wlan_hdd_tdls_disable_offchan_and_teardown_links(hdd_ctx);
-		if (hdd_ctx->connected_peer_count >= 1) {
-			/* clean up the tdls peers if any */
-			for (staIdx = 0; staIdx < hdd_ctx->max_num_tdls_sta;
-			     staIdx++) {
-				if ((hdd_ctx->tdlsConnInfo[staIdx].sessionId ==
-				     adapter->sessionId)
-				    && (hdd_ctx->tdlsConnInfo[staIdx].staId)) {
-					uint8_t *mac;
-					mac = hdd_ctx->tdlsConnInfo[staIdx].
-						peerMac.bytes;
-					hdd_notice("call sme_delete_tdls_peer_"
-						   "sta staId %d sessionId %d "
-						   MAC_ADDRESS_STR,
-						   hdd_ctx->tdlsConnInfo
-						   [staIdx].staId,
-						   adapter->sessionId,
-						   MAC_ADDR_ARRAY(mac));
-					sme_delete_tdls_peer_sta(
-						WLAN_HDD_GET_HAL_CTX(adapter),
-						adapter->sessionId, mac);
-				}
-			}
-		}
 		tdls_prohibited = true;
 		hdd_ctx->tdls_mode = eTDLS_SUPPORT_NOT_ENABLED;
 		tdls_param->vdev_id = hdd_ctx->set_state_info.vdev_id;
@@ -2364,12 +2362,11 @@
 		goto ret_status;
 	}
 
-	mutex_lock(&pHddCtx->tdls_lock);
 	curr_peer = wlan_hdd_tdls_get_peer(pAdapter, mac);
 	if (curr_peer == NULL) {
 		hdd_err("curr_peer is NULL");
 		status = -EINVAL;
-		goto rel_lock;
+		goto ret_status;
 	}
 
 	/*
@@ -2392,8 +2389,6 @@
 					   eTDLS_LINK_IDLE,
 					   eTDLS_LINK_UNSPECIFIED);
 	curr_peer->staId = 0;
-rel_lock:
-	mutex_unlock(&pHddCtx->tdls_lock);
 ret_status:
 	return status;
 }
diff --git a/core/mac/inc/sir_api.h b/core/mac/inc/sir_api.h
index af33223..88dca21 100644
--- a/core/mac/inc/sir_api.h
+++ b/core/mac/inc/sir_api.h
@@ -6900,4 +6900,15 @@
 
 #endif
 
+/**
+ * struct sir_del_all_tdls_peers - delete all tdls peers
+ * @msg_type: type of message
+ * @msg_len: length of message
+ * @bssid: bssid of peer device
+ */
+struct sir_del_all_tdls_peers {
+	uint16_t msg_type;
+	uint16_t msg_len;
+	struct qdf_mac_addr bssid;
+};
 #endif /* __SIR_API_H */
diff --git a/core/mac/inc/wni_api.h b/core/mac/inc/wni_api.h
index 969327c..6bed944 100644
--- a/core/mac/inc/wni_api.h
+++ b/core/mac/inc/wni_api.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2017 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -263,6 +263,7 @@
 	eWNI_SME_DEFAULT_SCAN_IE,
 	eWNI_SME_ROAM_SCAN_OFFLOAD_REQ,
 	eWNI_SME_LOST_LINK_INFO_IND,
+	eWNI_SME_DEL_ALL_TDLS_PEERS,
 	eWNI_SME_MSG_TYPES_END
 };
 
diff --git a/core/mac/src/pe/lim/lim_process_message_queue.c b/core/mac/src/pe/lim/lim_process_message_queue.c
index bc9e321..e467268 100644
--- a/core/mac/src/pe/lim/lim_process_message_queue.c
+++ b/core/mac/src/pe/lim/lim_process_message_queue.c
@@ -1964,6 +1964,11 @@
 		qdf_mem_free((void *)msg->bodyptr);
 		msg->bodyptr = NULL;
 		break;
+	case eWNI_SME_DEL_ALL_TDLS_PEERS:
+		lim_process_sme_del_all_tdls_peers(mac_ctx, msg->bodyptr);
+		qdf_mem_free((void *)msg->bodyptr);
+		msg->bodyptr = NULL;
+		break;
 	default:
 		qdf_mem_free((void *)msg->bodyptr);
 		msg->bodyptr = NULL;
diff --git a/core/mac/src/pe/lim/lim_process_tdls.c b/core/mac/src/pe/lim/lim_process_tdls.c
index a382137..1a5d46f 100644
--- a/core/mac/src/pe/lim/lim_process_tdls.c
+++ b/core/mac/src/pe/lim/lim_process_tdls.c
@@ -3368,6 +3368,67 @@
 }
 
 /**
+ * lim_check_aid_and_delete_peer() - Funtion to check aid and delete peer
+ * @p_mac: pointer to mac context
+ * @session_entry: pointer to PE session
+ *
+ * This function verifies aid and delete's peer with that aid from hash table
+ *
+ * Return: None
+ */
+static void lim_check_aid_and_delete_peer(tpAniSirGlobal p_mac,
+					  tpPESession session_entry)
+{
+	tpDphHashNode stads = NULL;
+	int i, aid;
+	size_t aid_bitmap_size = sizeof(session_entry->peerAIDBitmap);
+	struct qdf_mac_addr mac_addr;
+
+	/*
+	 * Check all the set bit in peerAIDBitmap and delete the peer
+	 * (with that aid) entry from the hash table and add the aid
+	 * in free pool
+	 */
+	lim_log(p_mac, LOG1, FL("Delete all the TDLS peer connected"));
+	for (i = 0; i < aid_bitmap_size / sizeof(uint32_t); i++) {
+		for (aid = 0; aid < (sizeof(uint32_t) << 3); aid++) {
+			if (!CHECK_BIT(session_entry->peerAIDBitmap[i], aid))
+				continue;
+			stads = dph_get_hash_entry(p_mac,
+					(aid + i * (sizeof(uint32_t) << 3)),
+					&session_entry->dph.dphHashTable);
+
+			if (NULL == stads)
+				goto skip;
+
+			lim_log(p_mac, LOG1,
+				FL("Deleting "MAC_ADDRESS_STR),
+				MAC_ADDR_ARRAY(stads->staAddr));
+
+			lim_send_deauth_mgmt_frame(p_mac,
+				eSIR_MAC_DEAUTH_LEAVING_BSS_REASON,
+				stads->staAddr, session_entry, false);
+
+			/* Delete TDLS peer */
+			qdf_mem_copy(mac_addr.bytes, stads->staAddr,
+				     QDF_MAC_ADDR_SIZE);
+
+			lim_tdls_del_sta(p_mac, mac_addr,
+					 session_entry, false);
+
+			dph_delete_hash_entry(p_mac,
+				stads->staAddr, stads->assocId,
+				&session_entry->dph.dphHashTable);
+skip:
+			lim_release_peer_idx(p_mac,
+				(aid + i * (sizeof(uint32_t) << 3)),
+				session_entry);
+			CLEAR_BIT(session_entry->peerAIDBitmap[i], aid);
+		}
+	}
+}
+
+/**
  * lim_delete_tdls_peers() - delete tdls peers
  *
  * @mac_ctx - global MAC context
@@ -3380,56 +3441,12 @@
 tSirRetStatus lim_delete_tdls_peers(tpAniSirGlobal mac_ctx,
 				    tpPESession session_entry)
 {
-	tpDphHashNode stads = NULL;
-	int i, aid;
-	size_t aid_bitmap_size = sizeof(session_entry->peerAIDBitmap);
-	struct qdf_mac_addr mac_addr;
-
 	if (NULL == session_entry) {
 		lim_log(mac_ctx, LOGE, FL("NULL session_entry"));
 		return eSIR_FAILURE;
 	}
 
-	/*
-	 * Check all the set bit in peerAIDBitmap and delete the peer
-	 * (with that aid) entry from the hash table and add the aid
-	 * in free pool
-	 */
-	lim_log(mac_ctx, LOGE, FL("Delete all the TDLS peer connected."));
-	for (i = 0; i < aid_bitmap_size / sizeof(uint32_t); i++) {
-		for (aid = 0; aid < (sizeof(uint32_t) << 3); aid++) {
-			if (!CHECK_BIT(session_entry->peerAIDBitmap[i], aid))
-				continue;
-			stads = dph_get_hash_entry(mac_ctx,
-					(aid + i * (sizeof(uint32_t) << 3)),
-					&session_entry->dph.dphHashTable);
-
-			if (NULL != stads) {
-				lim_log(mac_ctx, LOGE,
-					FL("Deleting "MAC_ADDRESS_STR),
-					MAC_ADDR_ARRAY(stads->staAddr));
-
-				lim_send_deauth_mgmt_frame(mac_ctx,
-					eSIR_MAC_DEAUTH_LEAVING_BSS_REASON,
-					stads->staAddr, session_entry, false);
-
-				/* Delete TDLS peer */
-				qdf_mem_copy(mac_addr.bytes, stads->staAddr,
-						QDF_MAC_ADDR_SIZE);
-
-				lim_tdls_del_sta(mac_ctx, mac_addr,
-						session_entry, false);
-
-				dph_delete_hash_entry(mac_ctx,
-					stads->staAddr, stads->assocId,
-					&session_entry->dph.dphHashTable);
-			}
-			lim_release_peer_idx(mac_ctx,
-				(aid + i * (sizeof(uint32_t) << 3)),
-				session_entry);
-			CLEAR_BIT(session_entry->peerAIDBitmap[i], aid);
-		}
-	}
+	lim_check_aid_and_delete_peer(mac_ctx, session_entry);
 	if (lim_is_roam_synch_in_progress(session_entry))
 		return eSIR_SUCCESS;
 	lim_send_sme_tdls_delete_all_peer_ind(mac_ctx, session_entry);
@@ -3438,6 +3455,40 @@
 }
 
 /**
+ * lim_process_sme_del_all_tdls_peers(): process delete tdls peers
+ * @p_mac: pointer to mac context
+ * @msg_buf: message buffer
+ *
+ * This function processes request to delete tdls peers
+ *
+ * Return: Success: eSIR_SUCCESS Failure: Error value
+ */
+tSirRetStatus lim_process_sme_del_all_tdls_peers(tpAniSirGlobal p_mac,
+						 uint32_t *msg_buf)
+{
+	struct sir_del_all_tdls_peers *msg;
+	tpPESession session_entry;
+	uint8_t session_id;
+
+	msg = (struct sir_del_all_tdls_peers *)msg_buf;
+	if (msg == NULL) {
+		lim_log(p_mac, LOGE, FL("NULL msg"));
+		return eSIR_FAILURE;
+	}
+
+	session_entry = pe_find_session_by_bssid(p_mac,
+						 msg->bssid.bytes, &session_id);
+	if (NULL == session_entry) {
+		lim_log(p_mac, LOGE, FL("NULL psessionEntry"));
+		return eSIR_FAILURE;
+	}
+
+	lim_check_aid_and_delete_peer(p_mac, session_entry);
+
+	return eSIR_SUCCESS;
+}
+
+/**
  * lim_process_tdls_del_sta_rsp() - Handle WDA_DELETE_STA_RSP for TDLS
  * @mac_ctx: Global MAC context
  * @lim_msg: LIM message
diff --git a/core/mac/src/pe/lim/lim_types.h b/core/mac/src/pe/lim/lim_types.h
index 7fd7082..e7fc7d4 100644
--- a/core/mac/src/pe/lim/lim_types.h
+++ b/core/mac/src/pe/lim/lim_types.h
@@ -910,7 +910,8 @@
 void lim_remain_on_chn_rsp(tpAniSirGlobal pMac, QDF_STATUS status, uint32_t *data);
 void lim_send_sme_disassoc_deauth_ntf(tpAniSirGlobal mac_ctx,
 				QDF_STATUS status, uint32_t *ctx);
-
+tSirRetStatus lim_process_sme_del_all_tdls_peers(tpAniSirGlobal p_mac,
+						 uint32_t *msg_buf);
 /* / Bit value data structure */
 typedef enum sHalBitVal         /* For Bit operations */
 {
diff --git a/core/sme/inc/sme_api.h b/core/sme/inc/sme_api.h
index 6ecb2c2..d56aa31 100644
--- a/core/sme/inc/sme_api.h
+++ b/core/sme/inc/sme_api.h
@@ -1435,4 +1435,6 @@
 			    const tSirMacAddr bssid,
 			    uint8_t **frame_buf, uint32_t *frame_len);
 
+QDF_STATUS sme_delete_all_tdls_peers(tHalHandle hal, uint8_t session_id);
+
 #endif /* #if !defined( __SME_API_H ) */
diff --git a/core/sme/src/common/sme_api.c b/core/sme/src/common/sme_api.c
index cea3414..6840a77 100644
--- a/core/sme/src/common/sme_api.c
+++ b/core/sme/src/common/sme_api.c
@@ -16458,3 +16458,43 @@
 	return QDF_STATUS_SUCCESS;
 }
 
+/**
+ * sme_delete_all_tdls_peers(): send request to delete tdls peers
+ * @hal: handler for HAL
+ * @session_id: session id
+ *
+ * This function sends request to lim to delete tdls peers
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS sme_delete_all_tdls_peers(tHalHandle hal, uint8_t session_id)
+{
+	struct sir_del_all_tdls_peers *msg;
+	QDF_STATUS status = QDF_STATUS_SUCCESS;
+	tpAniSirGlobal p_mac = PMAC_STRUCT(hal);
+	tCsrRoamSession *session = CSR_GET_SESSION(p_mac, session_id);
+
+	msg = qdf_mem_malloc(sizeof(*msg));
+	if (NULL == msg) {
+		sms_log(p_mac, LOGE, FL("memory alloc failed"));
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	qdf_mem_zero(msg, sizeof(*msg));
+
+	msg->msg_type = eWNI_SME_DEL_ALL_TDLS_PEERS;
+	msg->msg_len = (uint16_t) sizeof(*msg);
+
+	qdf_mem_copy(msg->bssid.bytes, session->connectedProfile.bssid.bytes,
+		     sizeof(struct qdf_mac_addr));
+
+	status = umac_send_mb_message_to_mac(msg);
+
+	if (status != QDF_STATUS_SUCCESS) {
+		QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR,
+			  FL("cds_send_mb_message_to_mac Failed"));
+		status = QDF_STATUS_E_FAILURE;
+	}
+
+	return status;
+}