wlan: TDLS Integration from Dev32Sta

Support for TDLS discovery, setup and teardown.
Exit BMPS while in active TDLS link and enable back afterwards.

Change-Id: Ic653c5369cb0b61e04e9a8be5e7f625ab7160365
CR-Fixed: 426223
diff --git a/CORE/HDD/inc/wlan_hdd_assoc.h b/CORE/HDD/inc/wlan_hdd_assoc.h
index f0b1ca5..bb8be32 100644
--- a/CORE/HDD/inc/wlan_hdd_assoc.h
+++ b/CORE/HDD/inc/wlan_hdd_assoc.h
@@ -23,6 +23,9 @@
 #define HDD_CONNECTION_H__ 
 #include <wlan_hdd_mib.h>
 #define HDD_MAX_NUM_IBSS_STA ( 4 )
+#ifdef FEATURE_WLAN_TDLS
+#define HDD_MAX_NUM_TDLS_STA ( 4 )
+#endif
 #define TKIP_COUNTER_MEASURE_STARTED 1
 #define TKIP_COUNTER_MEASURE_STOPED  0 
 /* Timeout (in ms) for Link to Up before Registering Station */
diff --git a/CORE/HDD/inc/wlan_hdd_cfg.h b/CORE/HDD/inc/wlan_hdd_cfg.h
index 327b78f..ebe9ba1 100644
--- a/CORE/HDD/inc/wlan_hdd_cfg.h
+++ b/CORE/HDD/inc/wlan_hdd_cfg.h
@@ -1410,6 +1410,12 @@
 #define CFG_ENABLE_RX_STBC_DEFAULT               ( 1 )
 
 
+#ifdef FEATURE_WLAN_TDLS
+#define CFG_TDLS_SUPPORT_ENABLE               "gEnableTDLSSupport"
+#define CFG_TDLS_SUPPORT_ENABLE_MIN           ( 0 )
+#define CFG_TDLS_SUPPORT_ENABLE_MAX           ( 1 )
+#define CFG_TDLS_SUPPORT_ENABLE_DEFAULT       ( 0 )
+#endif
 /*--------------------------------------------------------------------------- 
   Type declarations
   -------------------------------------------------------------------------*/ 
@@ -1721,6 +1727,9 @@
    v_U32_t                     numBuffAdvert;
    v_U8_t                      ignore_chan165;
    v_BOOL_t                    enableRxSTBC;
+#ifdef FEATURE_WLAN_TDLS       
+   v_BOOL_t                    fEnableTDLSSupport;
+#endif
 } hdd_config_t;
 /*--------------------------------------------------------------------------- 
   Function declarations and documenation
diff --git a/CORE/HDD/src/wlan_hdd_assoc.c b/CORE/HDD/src/wlan_hdd_assoc.c
index 5ef6468..bf0dc8a 100644
--- a/CORE/HDD/src/wlan_hdd_assoc.c
+++ b/CORE/HDD/src/wlan_hdd_assoc.c
@@ -61,6 +61,9 @@
 #if defined CONFIG_CFG80211
 #include "wlan_hdd_p2p.h"
 #endif
+#ifdef FEATURE_WLAN_TDLS
+#include "wlan_hdd_tdls.h"
+#endif
 #include "sme_Api.h"
 
 v_BOOL_t mibIsDot11DesiredBssTypeInfrastructure( hdd_adapter_t *pAdapter );
@@ -1737,6 +1740,186 @@
    return( eHAL_STATUS_SUCCESS );
 }
 
+#ifdef FEATURE_WLAN_TDLS
+/**============================================================================
+ *
+  @brief hdd_roamRegisterTDLSSTA() - Construct the staDesc and register with
+  TL the new STA. This is called as part of ADD_STA in the TDLS setup
+  Return: VOS_STATUS
+  
+  ===========================================================================*/
+static VOS_STATUS hdd_roamRegisterTDLSSTA( hdd_adapter_t *pAdapter,
+                                             tCsrRoamInfo *pRoamInfo,
+                                                v_U8_t tdlsEncryptionEnabled )
+{
+    hdd_station_ctx_t *pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
+    v_CONTEXT_t pVosContext = (WLAN_HDD_GET_CTX(pAdapter))->pvosContext;  
+    VOS_STATUS vosStatus = VOS_STATUS_E_FAILURE;
+    WLAN_STADescType staDesc = {0};
+
+    if (-1 == wlan_hdd_saveTdlsPeer(pRoamInfo)) {
+        VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, 
+                     "wlan_hdd_saveTdlsPeer() failed");
+    }
+    /*
+     * TDLS sta in BSS should be set as STA type TDLS and STA MAC should
+     * be peer MAC, here we are wokrking on direct Link
+     */
+    staDesc.ucSTAId = pRoamInfo->staId ;
+
+    staDesc.wSTAType = WLAN_STA_TDLS ;
+      
+    vos_mem_copy( staDesc.vSTAMACAddress.bytes, pRoamInfo->peerMac,
+                                         sizeof(tSirMacAddr) );
+
+    vos_mem_copy(staDesc.vBSSIDforIBSS.bytes, pHddStaCtx->conn_info.bssId,6 );
+    vos_copy_macaddr( &staDesc.vSelfMACAddress, &pAdapter->macAddressCurrent );
+
+    /* set the QoS field appropriately ..*/
+    (hdd_wmm_is_active(pAdapter)) ? (staDesc.ucQosEnabled = 1)
+                                          : (staDesc.ucQosEnabled = 0) ;
+
+    VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_HIGH, "HDD register \
+                                TL QoS_enabled=%d\n", staDesc.ucQosEnabled );
+
+    staDesc.ucProtectedFrame = tdlsEncryptionEnabled;
+
+    VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_MED,
+               "HDD register TL Sec_enabled= %d.\n", staDesc.ucProtectedFrame );
+
+    /* 
+     * UMA is ready we inform TL  to do frame translation.
+     */
+    staDesc.ucSwFrameTXXlation = 1;
+    staDesc.ucSwFrameRXXlation = 1;
+    staDesc.ucAddRmvLLC = 1;
+
+    /* Initialize signatures and state */
+    staDesc.ucUcastSig  = pRoamInfo->ucastSig ;
+
+    /* tdls Direct Link do not need bcastSig */
+    staDesc.ucBcastSig  = 0 ;
+    
+#ifdef VOLANS_ENABLE_SW_REPLAY_CHECK
+    if(staDesc.ucProtectedFrame)
+        staDesc.ucIsReplayCheckValid = VOS_TRUE;
+    else
+        staDesc.ucIsReplayCheckValid = VOS_FALSE;
+#endif
+
+    staDesc.ucInitState = WLANTL_STA_CONNECTED ;
+
+   (WLAN_HDD_GET_CTX(pAdapter))->sta_to_adapter[pRoamInfo->staId] = pAdapter;
+   /* Register the Station with TL...  */    
+    vosStatus = WLANTL_RegisterSTAClient( pVosContext, 
+                                          hdd_rx_packet_cbk, 
+                                          hdd_tx_complete_cbk, 
+                                          hdd_tx_fetch_packet_cbk, &staDesc, 0 );
+   
+    if ( !VOS_IS_STATUS_SUCCESS( vosStatus ) )
+    {
+        VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, 
+                     "WLANTL_RegisterSTAClient() failed to register.  \
+                            Status= %d [0x%08lX]", vosStatus, vosStatus );
+         return vosStatus;      
+    }                                            
+    
+    return( vosStatus );
+}
+
+/*
+ * HDD interface between SME and TL to ensure TDLS client registration with
+ * TL in case of new TDLS client is added and deregistration at the time
+ * TDLS client is deleted.
+ */
+
+eHalStatus hdd_RoamTdlsStatusUpdateHandler(hdd_adapter_t *pAdapter, 
+                                             tCsrRoamInfo *pRoamInfo, 
+                                              tANI_U32 roamId,
+                                                eRoamCmdStatus roamStatus, 
+                                                  eCsrRoamResult roamResult)
+{
+    hdd_station_ctx_t *pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
+    eHalStatus status = eHAL_STATUS_FAILURE ;
+ 
+    switch( roamResult )
+    {
+        case eCSR_ROAM_RESULT_ADD_TDLS_PEER:
+        {
+            tANI_U8 staIdx = 0 ;
+            /*
+             * check if there is available index for this new TDLS STA
+             * since TDLS is setup in BSS, we need to start from +1
+             */
+            for ( staIdx = 0 ; 
+                             staIdx < HDD_MAX_NUM_TDLS_STA; staIdx++ )
+            {
+                if (0 == pHddStaCtx->conn_info.staId[staIdx] )
+                {
+                    pHddStaCtx->conn_info.staId[staIdx] = pRoamInfo->staId;
+
+                    VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
+                     ("TDLS: STA IDX at %d \
+                       and mac = %d: %02x,%02x, %02x, %02x, %02x, %02x\n"),
+                              staIdx, pHddStaCtx->conn_info.staId[staIdx], 
+                                                pRoamInfo->peerMac[0],
+                                                pRoamInfo->peerMac[1],   
+                                                pRoamInfo->peerMac[2],   
+                                                pRoamInfo->peerMac[3],   
+                                                pRoamInfo->peerMac[4],   
+                                                pRoamInfo->peerMac[5]) ;
+  
+                    vos_copy_macaddr(
+                              &pHddStaCtx->conn_info.peerMacAddress[staIdx],
+                                     (v_MACADDR_t *)pRoamInfo->peerMac) ;
+                    status = eHAL_STATUS_SUCCESS ;
+                    break ;
+                }
+            }
+            if(eHAL_STATUS_SUCCESS == status)
+            {
+                /* start TDLS client registration with TL */
+                status = hdd_roamRegisterTDLSSTA( pAdapter, pRoamInfo, 1) ;
+            }
+            break ;
+        }
+        case eCSR_ROAM_RESULT_DELETE_TDLS_PEER:
+        {         
+            tANI_U8 staIdx = 0 ;
+            status = eHAL_STATUS_FAILURE ;
+
+            for ( staIdx = 0 ; 
+                             staIdx < HDD_MAX_NUM_TDLS_STA; staIdx++ )
+            {
+                if (pRoamInfo->staId == pHddStaCtx->conn_info.staId[staIdx] )
+                {
+                    pHddStaCtx->conn_info.staId[staIdx] = 0 ;
+                    vos_mem_zero(&pHddStaCtx->conn_info.peerMacAddress[staIdx],
+                                               sizeof(v_MACADDR_t)) ;
+                    status = eHAL_STATUS_SUCCESS ;
+
+                    break ;
+                }
+            }
+            if(eHAL_STATUS_SUCCESS == status)
+            {
+                VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
+                               ("HDD: del STA IDX = %x\n"), pRoamInfo->staId) ;
+                hdd_roamDeregisterSTA( pAdapter, pRoamInfo->staId );
+                (WLAN_HDD_GET_CTX(pAdapter))->sta_to_adapter[pRoamInfo->staId] = NULL;
+            }
+            break ; 
+        }
+        default:
+        {
+            break ;
+        }
+    }
+
+    return status ;
+}
+#endif
+
 eHalStatus hdd_smeRoamCallback( void *pContext, tCsrRoamInfo *pRoamInfo, tANI_U32 roamId, 
                                 eRoamCmdStatus roamStatus, eCsrRoamResult roamResult )
 {
@@ -1978,6 +2161,12 @@
                (roamResult == eCSR_ROAM_RESULT_NONE) ? TRUE : FALSE );
             break;
 #endif
+#ifdef FEATURE_WLAN_TDLS
+	case eCSR_ROAM_TDLS_STATUS_UPDATE:
+          halStatus = hdd_RoamTdlsStatusUpdateHandler( pAdapter, pRoamInfo, 
+                                            roamId, roamStatus, roamResult );
+          break ;
+#endif
         default:
             break;
     }
diff --git a/CORE/HDD/src/wlan_hdd_cfg.c b/CORE/HDD/src/wlan_hdd_cfg.c
index 26ed4d8..3090c2c 100644
--- a/CORE/HDD/src/wlan_hdd_cfg.c
+++ b/CORE/HDD/src/wlan_hdd_cfg.c
@@ -1711,6 +1711,14 @@
               CFG_ENABLE_RX_STBC_DEFAULT, 
               CFG_ENABLE_RX_STBC_MIN, 
               CFG_ENABLE_RX_STBC_MAX ),
+#ifdef FEATURE_WLAN_TDLS
+REG_VARIABLE( CFG_TDLS_SUPPORT_ENABLE, WLAN_PARAM_Integer,
+              hdd_config_t, fEnableTDLSSupport,
+              VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT,
+              CFG_TDLS_SUPPORT_ENABLE_DEFAULT,
+              CFG_TDLS_SUPPORT_ENABLE_MIN,
+              CFG_TDLS_SUPPORT_ENABLE_MAX ),
+#endif
 };
 
 /*
diff --git a/CORE/HDD/src/wlan_hdd_cfg80211.c b/CORE/HDD/src/wlan_hdd_cfg80211.c
index 0cca8f4..44d72bc 100644
--- a/CORE/HDD/src/wlan_hdd_cfg80211.c
+++ b/CORE/HDD/src/wlan_hdd_cfg80211.c
@@ -81,6 +81,9 @@
 #include "bap_hdd_misc.h"
 #endif
 #include <qc_sap_ioctl.h>
+#ifdef FEATURE_WLAN_TDLS
+#include "wlan_hdd_tdls.h"
+#endif
 
 #define g_mode_rates_size (12)
 #define a_mode_rates_size (8)
@@ -466,6 +469,11 @@
                  |  WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL
                     | WIPHY_FLAG_OFFCHAN_TX;
 #endif
+#ifdef FEATURE_WLAN_TDLS
+    wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS
+                 |  WIPHY_FLAG_TDLS_EXTERNAL_SETUP;
+#endif
+
     /* even with WIPHY_FLAG_CUSTOM_REGULATORY,
        driver can still register regulatory callback and
        it will get CRDA setting in wiphy->band[], but
@@ -6000,8 +6008,54 @@
 static int wlan_hdd_cfg80211_add_station(struct wiphy *wiphy,
           struct net_device *dev, u8 *mac, struct station_parameters *params)
 {
-    // TODO: Implement this later.
+#ifdef FEATURE_WLAN_TDLS
+    hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
+    hdd_context_t *pHddCtx = wiphy_priv(wiphy);
+    u32 mask, set;
+    VOS_STATUS status;
     ENTER();
+
+    if( NULL == pHddCtx || NULL == pHddCtx->cfg_ini )
+    {
+        VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, 
+                "Invalid arguments");
+        return -EINVAL;
+    }
+
+    if( FALSE == pHddCtx->cfg_ini->fEnableTDLSSupport ||
+        FALSE == sme_IsFeatureSupportedByFW(TDLS)) 
+    {
+        VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, 
+                "TDLS Disabled in INI OR not enabled in FW.\
+                Cannot process TDLS commands \n");
+        return -ENOTSUPP;
+    }
+
+    mask = params->sta_flags_mask;
+
+    set = params->sta_flags_set;
+
+
+    VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, 
+            "Add Station Request Mask = 0x%x set = 0x%x\n", mask, set);
+
+    if (mask & BIT(NL80211_STA_FLAG_TDLS_PEER)) {
+        if (set & BIT(NL80211_STA_FLAG_TDLS_PEER)) {
+            VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, 
+                    "Add TDLS peer");
+
+
+            status = sme_AddTdlsPeerSta(WLAN_HDD_GET_HAL_CTX(pAdapter), 
+                    pAdapter->sessionId, mac);
+
+            if (VOS_STATUS_SUCCESS != status) {
+                VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+                        "%s: sme_AddTdlsPeerSta failed!", __func__);
+            }
+        }
+    }
+#endif
+
     return 0;
 }
 
@@ -6150,6 +6204,139 @@
 }
 #endif
 
+#ifdef FEATURE_WLAN_TDLS
+static int wlan_hdd_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
+                     u8 *peer, u8 action_code,  u8 dialog_token,
+                                                 u16 status_code, const u8 *buf, size_t len)
+{
+
+    hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
+    hdd_context_t *pHddCtx = wiphy_priv(wiphy);
+    u8 *buf_1;
+    size_t len_1 = len;
+    u8 peerMac[6];
+    VOS_STATUS status;
+
+    if( NULL == pHddCtx || NULL == pHddCtx->cfg_ini )
+    {
+        VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, 
+                "Invalid arguments");
+        return -EINVAL;
+    }
+
+    if( FALSE == pHddCtx->cfg_ini->fEnableTDLSSupport ||
+        FALSE == sme_IsFeatureSupportedByFW(TDLS)) 
+    {
+        VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, 
+                "TDLS Disabled in INI OR not enabled in FW.\
+                Cannot process TDLS commands \n");
+        return -ENOTSUPP;
+    }
+    vos_mem_copy( peerMac, peer, 6);
+
+    VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, 
+                     "Request to send TDLS management: action = %d, status = %d, \
+		      len = %d", action_code, status_code, len);
+
+    buf_1 = vos_mem_malloc(len);
+    if(buf_1 == NULL) {
+        VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+                "%s: malloc failed!", __func__);
+        return -ENOMEM;
+    }
+    vos_mem_copy(buf_1, buf, len);
+
+    status = sme_SendTdlsMgmtFrame(WLAN_HDD_GET_HAL_CTX(pAdapter), pAdapter->sessionId,
+            peerMac, action_code, dialog_token, status_code, buf_1, len_1);
+
+    if (VOS_STATUS_SUCCESS != status) {
+        VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+                "%s: sme_SendTdlsMgmtFrame failed!", __func__);
+    }
+
+    vos_mem_free(buf_1);
+
+    return 0;
+}
+
+static int wlan_hdd_cfg80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
+                 u8 *peer, enum nl80211_tdls_operation oper)
+{
+    hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
+    hdd_context_t *pHddCtx = wiphy_priv(wiphy);
+
+    if( NULL == pHddCtx || NULL == pHddCtx->cfg_ini )
+    {
+        VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, 
+                "Invalid arguments");
+        return -EINVAL;
+    }
+       
+    VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, 
+            "Request for TDLS oper: %d", (int)oper);
+
+    if( FALSE == pHddCtx->cfg_ini->fEnableTDLSSupport ||
+        FALSE == sme_IsFeatureSupportedByFW(TDLS)) 
+    {
+        VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, 
+                "TDLS Disabled in INI OR not enabled in FW.\
+                Cannot process TDLS commands \n");
+        return -ENOTSUPP;
+    }
+
+    switch (oper) {
+        case NL80211_TDLS_ENABLE_LINK:
+            {
+                v_CONTEXT_t pVosContext = (WLAN_HDD_GET_CTX(pAdapter))->pvosContext;  
+                v_U8_t my_peer[6];
+                v_U8_t ucSTAId;
+                VOS_STATUS status;
+
+                if (peer) {
+                    vos_mem_copy(my_peer, peer, 6);
+                    ucSTAId = wlan_hdd_findTdlsPeer(my_peer);
+
+                    hddLog(VOS_TRACE_LEVEL_INFO_HIGH, 
+                            "%s: set key for peer %2x:%2x:%2x:%2x:%2x:%2x",
+                            __func__, peer[0], peer[1], 
+                            peer[2], peer[3], 
+                            peer[4], peer[5] );
+
+                    if (-1 == ucSTAId ) {
+                        hddLog(VOS_TRACE_LEVEL_ERROR, "wlan_hdd_findTdlsPeer failed" );
+                        return 0;
+                    }
+
+                    status = WLANTL_ChangeSTAState( pVosContext, ucSTAId, 
+                            WLANTL_STA_AUTHENTICATED );
+
+                    if (0 != status) {
+                        hddLog(VOS_TRACE_LEVEL_ERROR, 
+                                "%s: WLANTL_ChangeSTAState failed, returned %d", 
+                                __func__, status);
+                        return status;
+                    }
+                } else {
+                    hddLog(VOS_TRACE_LEVEL_WARN, "wlan_hdd_cfg80211_add_key: peer NULL" );
+                }
+            }
+            break;
+        case NL80211_TDLS_DISABLE_LINK:
+            sme_DeleteTdlsPeerSta( WLAN_HDD_GET_HAL_CTX(pAdapter), 
+                    pAdapter->sessionId, peer );
+            return 0;
+        case NL80211_TDLS_TEARDOWN:
+        case NL80211_TDLS_SETUP:
+        case NL80211_TDLS_DISCOVERY_REQ:
+            /* We don't support in-driver setup/teardown/discovery */
+            return -ENOTSUPP;
+        default:
+            return -ENOTSUPP;
+    }
+    return 0;
+}
+#endif
+
 /* cfg80211_ops */
 static struct cfg80211_ops wlan_hdd_cfg80211_ops = 
 {
@@ -6202,6 +6389,10 @@
 #if defined(WLAN_FEATURE_VOWIFI_11R) && defined(KERNEL_SUPPORT_11R_CFG80211)
      .update_ft_ies = wlan_hdd_cfg80211_update_ft_ies,
 #endif
+#ifdef FEATURE_WLAN_TDLS
+     .tdls_mgmt = wlan_hdd_cfg80211_tdls_mgmt,
+     .tdls_oper = wlan_hdd_cfg80211_tdls_oper,
+#endif
 };
 
 #endif // CONFIG_CFG80211