TDLS : Initiate the TDLS setup only when externally configured.

There are use cases where the driver has to initiate the TDLS
setup to the peer only when configured by the external applications
This commit addresses the same and the functionality is configured
by gTDLSExternalControl parameter .

CRs-fixed: 537537
Change-Id: Ie22cf6969d498e4c1994ebdbde3eb3e5bcb00b25
diff --git a/CORE/HDD/inc/wlan_hdd_cfg.h b/CORE/HDD/inc/wlan_hdd_cfg.h
index d80ffcd..be556f9 100644
--- a/CORE/HDD/inc/wlan_hdd_cfg.h
+++ b/CORE/HDD/inc/wlan_hdd_cfg.h
@@ -1754,6 +1754,11 @@
 #define CFG_TDLS_PUAPSD_RX_FRAME_THRESHOLD_MIN      (10)
 #define CFG_TDLS_PUAPSD_RX_FRAME_THRESHOLD_MAX      (20)
 #define CFG_TDLS_PUAPSD_RX_FRAME_THRESHOLD_DEFAULT  (10)
+
+#define CFG_TDLS_EXTERNAL_CONTROL                   "gTDLSExternalControl"
+#define CFG_TDLS_EXTERNAL_CONTROL_MIN               (0)
+#define CFG_TDLS_EXTERNAL_CONTROL_MAX               (1)
+#define CFG_TDLS_EXTERNAL_CONTROL_DEFAULT           (0)
 #endif
 
 #ifdef WLAN_ACTIVEMODE_OFFLOAD_FEATURE
@@ -2331,6 +2336,7 @@
    v_U32_t                     fEnableTDLSBufferSta;
    v_U32_t                     fTDLSPuapsdInactivityTimer;
    v_U32_t                     fTDLSRxFrameThreshold;
+   v_BOOL_t                    fTDLSExternalControl;
 #endif
    v_U32_t                     enableLpwrImgTransition;
 #ifdef WLAN_SOFTAP_VSTA_FEATURE
diff --git a/CORE/HDD/inc/wlan_hdd_main.h b/CORE/HDD/inc/wlan_hdd_main.h
index 47164f8..60bc5dd 100644
--- a/CORE/HDD/inc/wlan_hdd_main.h
+++ b/CORE/HDD/inc/wlan_hdd_main.h
@@ -1161,8 +1161,8 @@
     /* TDLS peer connected count */
     tANI_U16 connected_peer_count;
     tdls_scan_context_t tdls_scan_ctxt;
-   /* Lock to avoid race condition during TDLS operations*/
-   struct mutex tdls_lock;
+    /* Lock to avoid race condition during TDLS operations*/
+    struct mutex tdls_lock;
 #endif
 
     hdd_traffic_monitor_t traffic_monitor;
diff --git a/CORE/HDD/inc/wlan_hdd_tdls.h b/CORE/HDD/inc/wlan_hdd_tdls.h
index 152db64..3738cf3 100644
--- a/CORE/HDD/inc/wlan_hdd_tdls.h
+++ b/CORE/HDD/inc/wlan_hdd_tdls.h
@@ -125,6 +125,11 @@
 } tdls_rssi_config_t;
 
 struct _hddTdlsPeer_t;
+
+typedef struct {
+    tSirMacAddr macAddr;
+} hddTdlsForcePeer_t;
+
 typedef struct {
     struct list_head peer_list[256];
     hdd_adapter_t   *pAdapter;
@@ -140,6 +145,8 @@
     struct _hddTdlsPeer_t  *curr_candidate;
     struct work_struct implicit_setup;
     v_U32_t            magic;
+    hddTdlsForcePeer_t forcePeer[HDD_MAX_NUM_TDLS_STA];
+    tANI_U8            forcePeerCnt;
 } tdlsCtx_t;
 
 typedef struct _hddTdlsPeer_t {
@@ -271,5 +278,8 @@
                                            hddTdlsPeer_t *curr_peer,
                                            tANI_U16 reason);
 
+int wlan_hdd_tdls_add_force_peer(hdd_adapter_t *pAdapter, u8 *mac);
+
+int wlan_hdd_tdls_remove_force_peer(hdd_adapter_t *pAdapter, u8 *mac);
 
 #endif // __HDD_TDSL_H
diff --git a/CORE/HDD/src/wlan_hdd_cfg.c b/CORE/HDD/src/wlan_hdd_cfg.c
index 2530ca3..a6c2aa0 100644
--- a/CORE/HDD/src/wlan_hdd_cfg.c
+++ b/CORE/HDD/src/wlan_hdd_cfg.c
@@ -2515,6 +2515,13 @@
               CFG_TDLS_PUAPSD_RX_FRAME_THRESHOLD_DEFAULT,
               CFG_TDLS_PUAPSD_RX_FRAME_THRESHOLD_MIN,
               CFG_TDLS_PUAPSD_RX_FRAME_THRESHOLD_MAX ),
+
+REG_VARIABLE( CFG_TDLS_EXTERNAL_CONTROL, WLAN_PARAM_Integer,
+              hdd_config_t, fTDLSExternalControl,
+              VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT,
+              CFG_TDLS_EXTERNAL_CONTROL_DEFAULT,
+              CFG_TDLS_EXTERNAL_CONTROL_MIN,
+              CFG_TDLS_EXTERNAL_CONTROL_MAX ),
 #endif
 
 #ifdef WLAN_SOFTAP_VSTA_FEATURE
diff --git a/CORE/HDD/src/wlan_hdd_cfg80211.c b/CORE/HDD/src/wlan_hdd_cfg80211.c
index 3c19d80..86a005c 100644
--- a/CORE/HDD/src/wlan_hdd_cfg80211.c
+++ b/CORE/HDD/src/wlan_hdd_cfg80211.c
@@ -8384,15 +8384,6 @@
     hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
     hdd_context_t *pHddCtx = wiphy_priv(wiphy);
     int status;
-#ifdef WLAN_FEATURE_TDLS_DEBUG
-    const char *tdls_oper_str[]= {
-        "NL80211_TDLS_DISCOVERY_REQ",
-        "NL80211_TDLS_SETUP",
-        "NL80211_TDLS_TEARDOWN",
-        "NL80211_TDLS_ENABLE_LINK",
-        "NL80211_TDLS_DISABLE_LINK",
-        "NL80211_TDLS_UNKNOWN_OPER"};
-#endif
     hddTdlsPeer_t *pTdlsPeer;
 
     if ( NULL == peer )
@@ -8411,23 +8402,6 @@
         return status;
     }
 
-    pTdlsPeer = wlan_hdd_tdls_find_peer(pAdapter, peer, TRUE);
-
-    if ( NULL == pTdlsPeer ) {
-        hddLog(VOS_TRACE_LEVEL_ERROR, "%s: " MAC_ADDRESS_STR " (oper %d) not exsting. ignored",
-               __func__, MAC_ADDR_ARRAY(peer), (int)oper);
-        return -EINVAL;
-    }
-
-#ifdef WLAN_FEATURE_TDLS_DEBUG
-    if((int)oper > 4)
-        oper = 5;
-
-    VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
-               "%s: " MAC_ADDRESS_STR " link_status %d (%s) ", "tdls_oper",
-               MAC_ADDR_ARRAY(peer), pTdlsPeer->link_status,
-               tdls_oper_str[(int)oper]);
-#endif
 
     if( FALSE == pHddCtx->cfg_ini->fEnableTDLSSupport ||
         FALSE == sme_IsFeatureSupportedByFW(TDLS))
@@ -8444,6 +8418,20 @@
                 VOS_STATUS status;
                 tCsrTdlsLinkEstablishParams tdlsLinkEstablishParams;
 
+                pTdlsPeer = wlan_hdd_tdls_find_peer(pAdapter, peer, TRUE);
+
+                if ( NULL == pTdlsPeer ) {
+                    hddLog(VOS_TRACE_LEVEL_ERROR, "%s: " MAC_ADDRESS_STR
+                           " (oper %d) not exsting. ignored",
+                           __func__, MAC_ADDR_ARRAY(peer), (int)oper);
+                    return -EINVAL;
+                }
+
+                VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+                           "%s: " MAC_ADDRESS_STR " link_status %d (%s) ", "tdls_oper",
+                           MAC_ADDR_ARRAY(peer), pTdlsPeer->link_status,
+                           "NL80211_TDLS_ENABLE_LINK");
+
                 if (!TDLS_STA_INDEX_VALID(pTdlsPeer->staId))
                 {
                     hddLog(VOS_TRACE_LEVEL_ERROR, "%s: Invalid Staion Index %u "
@@ -8527,6 +8515,20 @@
             break;
         case NL80211_TDLS_DISABLE_LINK:
             {
+                pTdlsPeer = wlan_hdd_tdls_find_peer(pAdapter, peer, TRUE);
+
+                if ( NULL == pTdlsPeer ) {
+                    hddLog(VOS_TRACE_LEVEL_ERROR, "%s: " MAC_ADDRESS_STR
+                           " (oper %d) not exsting. ignored",
+                           __func__, MAC_ADDR_ARRAY(peer), (int)oper);
+                    return -EINVAL;
+                }
+
+                VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+                           "%s: " MAC_ADDRESS_STR " link_status %d (%s) ", "tdls_oper",
+                           MAC_ADDR_ARRAY(peer), pTdlsPeer->link_status,
+                           "NL80211_TDLS_DISABLE_LINK");
+
                 if(TDLS_STA_INDEX_VALID(pTdlsPeer->staId))
                 {
                     long status;
@@ -8556,7 +8558,53 @@
             }
             break;
         case NL80211_TDLS_TEARDOWN:
+            {
+                VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
+                          " %s : NL80211_TDLS_TEARDOWN for " MAC_ADDRESS_STR,
+                          __func__, MAC_ADDR_ARRAY(peer));
+
+                if ( (FALSE == pHddCtx->cfg_ini->fTDLSExternalControl) ||
+                     (FALSE == pHddCtx->cfg_ini->fEnableTDLSImplicitTrigger) ) {
+
+                    VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
+                          " %s TDLS External control and Implicit Trigger not enabled ",
+                          __func__);
+                    return -ENOTSUPP;
+                }
+
+                if ( 0 != wlan_hdd_tdls_remove_force_peer(pAdapter, peer) )
+                    return -EINVAL;
+
+                pTdlsPeer = wlan_hdd_tdls_find_peer(pAdapter, peer, TRUE);
+
+                if ( NULL == pTdlsPeer ) {
+                    hddLog(VOS_TRACE_LEVEL_INFO, "%s: " MAC_ADDRESS_STR
+                           " peer not exsting",
+                           __func__, MAC_ADDR_ARRAY(peer));
+                }
+                else {
+                    wlan_hdd_tdls_indicate_teardown(pAdapter, pTdlsPeer,
+                                       eSIR_MAC_TDLS_TEARDOWN_UNSPEC_REASON);
+                }
+                break;
+            }
         case NL80211_TDLS_SETUP:
+            {
+                VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
+                          " %s : NL80211_TDLS_SETUP for " MAC_ADDRESS_STR,
+                          __func__, MAC_ADDR_ARRAY(peer));
+
+                if ( (FALSE == pHddCtx->cfg_ini->fTDLSExternalControl) ||
+                     (FALSE == pHddCtx->cfg_ini->fEnableTDLSImplicitTrigger) ) {
+
+                    VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
+                          " %s TDLS External control and Implicit Trigger not enabled ",
+                          __func__);
+                    return -ENOTSUPP;
+                }
+
+                return wlan_hdd_tdls_add_force_peer(pAdapter, peer);
+            }
         case NL80211_TDLS_DISCOVERY_REQ:
             /* We don't support in-driver setup/teardown/discovery */
             return -ENOTSUPP;
diff --git a/CORE/HDD/src/wlan_hdd_tdls.c b/CORE/HDD/src/wlan_hdd_tdls.c
index daec620..4abbf89 100644
--- a/CORE/HDD/src/wlan_hdd_tdls.c
+++ b/CORE/HDD/src/wlan_hdd_tdls.c
@@ -43,6 +43,7 @@
 static tANI_S32 wlan_hdd_tdls_peer_reset_discovery_processed(tdlsCtx_t *pHddTdlsCtx);
 static void wlan_hdd_tdls_timers_destroy(tdlsCtx_t *pHddTdlsCtx);
 static void wlan_hdd_tdls_peer_timers_destroy(tdlsCtx_t *pHddTdlsCtx);
+int wpa_tdls_is_allowed_force_peer(tdlsCtx_t *pHddTdlsCtx, u8 *mac);
 #ifdef CONFIG_TDLS_IMPLICIT
 static void wlan_hdd_tdls_pre_setup(struct work_struct *work);
 #endif
@@ -284,6 +285,10 @@
                        "%s: " MAC_ADDRESS_STR " link_status %d"
                        " tdls_support %d", __func__, MAC_ADDR_ARRAY(curr_peer->peerMac),
                        curr_peer->link_status, curr_peer->tdls_support);
+            if (pHddCtx->cfg_ini->fTDLSExternalControl &&
+                (0 == wpa_tdls_is_allowed_force_peer (pHddTdlsCtx, curr_peer->peerMac))) {
+                continue;
+            }
 
             if (eTDLS_CAP_SUPPORTED == curr_peer->tdls_support) {
                 VOS_TRACE( VOS_MODULE_ID_HDD, TDLS_LOG_LEVEL,
@@ -1265,6 +1270,137 @@
     return 0;
 }
 
+int wpa_tdls_is_allowed_force_peer(tdlsCtx_t *pHddTdlsCtx, u8 *mac)
+{
+    int i;
+    for (i=0; i<pHddTdlsCtx->forcePeerCnt; i++)
+    {
+        if (!memcmp(mac, pHddTdlsCtx->forcePeer[i].macAddr, 6))
+        {
+            VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
+                      "%s: " MAC_ADDRESS_STR "is a forced peer",
+                      __func__,MAC_ADDR_ARRAY(mac));
+            return 1;
+        }
+    }
+    VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
+              "%s: " MAC_ADDRESS_STR "is not a forced peer",
+              __func__,MAC_ADDR_ARRAY(mac));
+    return 0;
+}
+
+int wlan_hdd_tdls_remove_force_peer(hdd_adapter_t *pAdapter, u8 *mac)
+{
+    int i;
+    tdlsCtx_t *pHddTdlsCtx;
+    hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
+
+    if ((NULL == pHddCtx)) return -1;
+
+    if (mutex_lock_interruptible(&pHddCtx->tdls_lock))
+    {
+        VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+            "%s: unable to lock list", __func__);
+        return -1;
+    }
+
+    pHddTdlsCtx = WLAN_HDD_GET_TDLS_CTX_PTR(pAdapter);
+    if (NULL == pHddTdlsCtx)
+    {
+        mutex_unlock(&pHddCtx->tdls_lock);
+        return -1;
+    }
+
+    for (i=0; i<HDD_MAX_NUM_TDLS_STA; i++)
+    {
+        if (!memcmp(mac, pHddTdlsCtx->forcePeer[i].macAddr, 6))
+        {
+            /*
+             * Entry Already Exists,Clear the entry and return.
+             */
+            memset(pHddTdlsCtx->forcePeer[i].macAddr, 0, 6);
+            pHddTdlsCtx->forcePeerCnt--;
+            mutex_unlock(&pHddCtx->tdls_lock);
+            VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
+                      "%s: Removed the requested foce peer", __func__);
+            return 0;
+        }
+    }
+
+    /* Should not have come here if the existing entry has to be removed */
+    mutex_unlock(&pHddCtx->tdls_lock);
+    VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+              "%s: Failed to remove the requested force peer: No Entry Found ", __func__);
+    return -1;
+}
+
+int wlan_hdd_tdls_add_force_peer(hdd_adapter_t *pAdapter, u8 *mac)
+{
+    int i ;
+    tdlsCtx_t *pHddTdlsCtx;
+    tANI_U8 zeroMac[6] = {0};
+    hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
+
+    if ((NULL == pHddCtx)) return -1;
+
+    if (mutex_lock_interruptible(&pHddCtx->tdls_lock))
+    {
+        VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+            "%s: unable to lock list", __func__);
+        return -1;
+    }
+
+    pHddTdlsCtx = WLAN_HDD_GET_TDLS_CTX_PTR(pAdapter);
+    if (NULL == pHddTdlsCtx)
+    {
+        mutex_unlock(&pHddCtx->tdls_lock);
+        return -1;
+    }
+
+    if (pHddTdlsCtx->forcePeerCnt >= HDD_MAX_NUM_TDLS_STA )
+    {
+       VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
+                 "%s: Fail to add the requested foce peer : Quota Full"
+                 " forcePeerCnt is %d ", __func__,pHddTdlsCtx->forcePeerCnt);
+       /* Quota Full, hence return failure from here*/
+        goto error;
+    }
+
+    for (i=0; i<HDD_MAX_NUM_TDLS_STA; i++)
+    {
+        if (!memcmp(mac, pHddTdlsCtx->forcePeer[i].macAddr, 6))
+        {
+            /*
+             * Entry Already Exists,No need to create a new one.
+             * Hence return from here.
+             */
+            mutex_unlock(&pHddCtx->tdls_lock);
+            VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
+              "%s: Add the requested foce peer success : Entry present"
+              " forcePeerCnt is %d ", __func__,
+              pHddTdlsCtx->forcePeerCnt);
+            return 0;
+        }
+    }
+    for (i=0; i<HDD_MAX_NUM_TDLS_STA; i++)
+    {
+        if ( !memcmp(pHddTdlsCtx->forcePeer[i].macAddr, zeroMac, 6) )
+        {
+            memcpy(pHddTdlsCtx->forcePeer[i].macAddr, mac, 6);
+            pHddTdlsCtx->forcePeerCnt++;
+            mutex_unlock(&pHddCtx->tdls_lock);
+            VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
+              "%s: Add the requested foce peer success at index i"
+              " forcePeerCnt is %d ", __func__, i,
+              pHddTdlsCtx->forcePeerCnt);
+            return 0;
+        }
+    }
+error:
+    mutex_unlock(&pHddCtx->tdls_lock);
+    return -1;
+}
+
 /* if peerMac is found, then it returns pointer to hddTdlsPeer_t
    otherwise, it returns NULL
 */
@@ -2041,7 +2177,6 @@
     /* if tdls is not enabled, then continue scan */
     if (eTDLS_SUPPORT_NOT_ENABLED == pHddCtx->tdls_mode)
         return 1;
-
     curr_peer = wlan_hdd_tdls_is_progress(pHddCtx, NULL, 0);
     if (NULL != curr_peer)
     {
@@ -2146,6 +2281,7 @@
     if (NULL == pHddCtx)
         return;
 
+
     /* free allocated memory at scan time */
     wlan_hdd_tdls_free_scan_request (&pHddCtx->tdls_scan_ctxt);