wlan: Dynamically change tcp delay ack using hdd stats

We start a periodic timer in which we monitor number of rx
packets received. Using this count we decide bus width/rx_level.
We send this throughput level to cnss-daemon so that daemon can
dynamically set tcp delay ack value based on current receive
traffic situation to improve tcp performace. Added ini item to
enable this feature.

CRs-Fixed: 763246
Change-Id: I61f8a55ec9215a8fe86433d9f34a8ee7082a04bb
diff --git a/CORE/HDD/inc/wlan_hdd_cfg.h b/CORE/HDD/inc/wlan_hdd_cfg.h
index f269ca6..f789725 100644
--- a/CORE/HDD/inc/wlan_hdd_cfg.h
+++ b/CORE/HDD/inc/wlan_hdd_cfg.h
@@ -1531,6 +1531,10 @@
 #define CFG_MULTICAST_HOST_FW_MSGS_MAX      (1)
 #define CFG_MULTICAST_HOST_FW_MSGS_DEFAULT  (1)
 
+#define CFG_ENABLE_TCP_DELACK_NAME           "gEnableDelAck"
+#define CFG_ENABLE_TCP_DELACK_MIN            (0)
+#define CFG_ENABLE_TCP_DELACK_MAX            (1)
+#define CFG_ENABLE_TCP_DELACK_DEFAULT        (1)
 
 /* In cfg.dat 1=1MBPS, 2=2MBPS, 3=5_5MBPS, 4=11MBPS, 5=6MBPS, 6=9MBPS,
  * 7=12MBPS, 8=18MBPS, 9=24MBPS. But 6=9MBPS and 8=18MBPS are not basic
@@ -2270,6 +2274,21 @@
 #define CFG_ROAMING_DFS_CHANNEL_MAX                 (1)
 #define CFG_ROAMING_DFS_CHANNEL_DEFAULT             (1)
 
+#define CFG_TCP_DELACK_COMPUTE_INTERVAL         "gTcpDelAckComputeInterval"
+#define CFG_TCP_DELACK_COMPUTE_INTERVAL_DEFAULT ( 2000 )
+#define CFG_TCP_DELACK_COMPUTE_INTERVAL_MIN     ( 1000 )
+#define CFG_TCP_DELACK_COMPUTE_INTERVAL_MAX     ( 10000 )
+
+#define CFG_TCP_DELACK_THRESHOLD_HIGH              "gTcpDelAckThresholdHigh"
+#define CFG_TCP_DELACK_THRESHOLD_HIGH_DEFAULT      ( 17000 )
+#define CFG_TCP_DELACK_THRESHOLD_HIGH_MIN          ( 1000 )
+#define CFG_TCP_DELACK_THRESHOLD_HIGH_MAX          ( 25000 )
+
+#define CFG_TCP_DELACK_THRESHOLD_LOW               "gTcpDelAckThresholdLow"
+#define CFG_TCP_DELACK_THRESHOLD_LOW_DEFAULT       ( 12000 )
+#define CFG_TCP_DELACK_THRESHOLD_LOW_MIN           ( 0 )
+#define CFG_TCP_DELACK_THRESHOLD_LOW_MAX           ( 25000 )
+
 
 #ifdef WLAN_LOGGING_SOCK_SVC_ENABLE
 //Enable WLAN Logging to app space
@@ -3065,6 +3084,9 @@
    v_U32_t                     cfgBtcCriticalHidnSniffBlkGuidance;
    v_U32_t                     cfgBtcA2dpTxQueueThold;
    v_U32_t                     cfgBtcOppTxQueueThold;
+   v_U32_t                     tcpDelAckComputeInterval;
+   v_U32_t                     tcpDelAckThresholdHigh;
+   v_U32_t                     tcpDelAckThresholdLow;
 
 #ifdef WLAN_FEATURE_11W
    v_U32_t                     pmfSaQueryMaxRetries;
@@ -3119,6 +3141,7 @@
    v_U16_t                     rps_mask;
    v_BOOL_t                    fEnableWifiConfig;
    v_BOOL_t                    crash_inject_enabled;
+   v_U32_t                     enable_delack;
 } hdd_config_t;
 
 /*--------------------------------------------------------------------------- 
diff --git a/CORE/HDD/inc/wlan_hdd_main.h b/CORE/HDD/inc/wlan_hdd_main.h
index ecaab10..b037679 100644
--- a/CORE/HDD/inc/wlan_hdd_main.h
+++ b/CORE/HDD/inc/wlan_hdd_main.h
@@ -1141,6 +1141,7 @@
 #endif
    /* Flag to ensure PSB is configured through framework */
    v_U8_t psbChanged;
+   v_ULONG_t prev_rx_packets;
    /* UAPSD psb value configured through framework */
    v_U8_t configuredPsb;
    v_BOOL_t is_roc_inprogress;
@@ -1563,8 +1564,26 @@
 
    /* work queue to defer mac spoofing */
    struct delayed_work spoof_mac_addr_work;
+
+    vos_timer_t    delack_timer;
+    struct mutex   cur_rx_level_lock;
+    v_U32_t        cur_rx_level;
+    v_U64_t        prev_rx;
+    v_ULONG_t      mode;
 };
 
+typedef enum  {
+        TP_IND_LOW = 1,
+        TP_IND_MEDIUM,
+        TP_IND_HIGH,
+}TP_IND_TYPE;
+
+/* Use to notify the TDLS or BTCOEX is mode enable */
+typedef enum
+{
+   WLAN_TDLS_MODE,
+   WLAN_BTCOEX_MODE,
+} WLAN_MODE_TYPE;
 
 #define WLAN_HDD_IS_LOAD_IN_PROGRESS(pHddCtx)  \
             (pHddCtx->isLoadUnloadInProgress & WLAN_HDD_LOAD_IN_PROGRESS)
@@ -1726,6 +1745,25 @@
 #endif
 int hdd_wmmps_helper(hdd_adapter_t *pAdapter, tANI_U8 *ptr);
 
+/*
+ * start/stop bandwidth compute timer, Based on which tcp delack
+ * value will be configured
+ */
+void hdd_manage_delack_timer(hdd_context_t *pHddCtx);
+
+void hdd_update_prev_rx_packet_count(hdd_context_t *pHddCtx);
+void hdd_start_delack_timer(hdd_context_t *pHddCtx);
+void hdd_set_default_stop_delack_timer(hdd_context_t *pHddCtx);
+v_U8_t hdd_get_total_sessions(hdd_context_t *pHddCtx);
+void hdd_set_delack_value(hdd_context_t *pHddCtx, v_U32_t next_rx_level);
+
+/*
+ *  Calculate the packet channel bandwidth and send notification to cnss demon
+ */
+void hdd_request_tcp_delack(hdd_context_t *pHddCtx,
+                                               uint64_t rx_packets);
+void hdd_tcp_delack_compute_function(void *priv);
+
 #ifdef FEATURE_WLAN_BATCH_SCAN
 /**---------------------------------------------------------------------------
 
diff --git a/CORE/HDD/inc/wlan_hdd_softap_tx_rx.h b/CORE/HDD/inc/wlan_hdd_softap_tx_rx.h
index 249dda1..d7c23ec 100644
--- a/CORE/HDD/inc/wlan_hdd_softap_tx_rx.h
+++ b/CORE/HDD/inc/wlan_hdd_softap_tx_rx.h
@@ -64,6 +64,17 @@
   -------------------------------------------------------------------------*/ 
 
 /**============================================================================
+  @brief hdd_softap_get_connected_sta() - Function to get numbeer of connected
+  stations with softap
+
+  @param pHostapdAdapter      : [in]  pointer to  saftap adapter
+
+  @return         :  Number of connected peer with softap
+  ===========================================================================*/
+
+v_U8_t hdd_softap_get_connected_sta(hdd_adapter_t *pHostapdAdapter);
+
+/**============================================================================
   @brief hdd_softap_hard_start_xmit() - Function registered with the Linux OS for 
   transmitting packets
 
diff --git a/CORE/HDD/src/wlan_hdd_assoc.c b/CORE/HDD/src/wlan_hdd_assoc.c
index 4956bb4..40716c8 100644
--- a/CORE/HDD/src/wlan_hdd_assoc.c
+++ b/CORE/HDD/src/wlan_hdd_assoc.c
@@ -675,6 +675,8 @@
                 MAC_ADDR_ARRAY(wrqu.ap_addr.sa_data));
         hdd_SendUpdateBeaconIEsEvent(pAdapter, pCsrRoamInfo);
 
+        hdd_manage_delack_timer(pHddCtx);
+
         /* Send IWEVASSOCRESPIE Event if WLAN_FEATURE_CIQ_METRICS is Enabled Or
          * Send IWEVASSOCRESPIE Event if WLAN_FEATURE_VOWIFI_11R is Enabled
          * and fFTEnable is TRUE */
@@ -705,6 +707,8 @@
         pr_info("wlan: disconnected\n");
         type = WLAN_STA_DISASSOC_DONE_IND;
         memset(wrqu.ap_addr.sa_data,'\0',ETH_ALEN);
+
+        hdd_manage_delack_timer(pHddCtx);
     }
     hdd_dump_concurrency_info(pHddCtx);
 
diff --git a/CORE/HDD/src/wlan_hdd_cfg.c b/CORE/HDD/src/wlan_hdd_cfg.c
index 9e7c6e3..7263728 100644
--- a/CORE/HDD/src/wlan_hdd_cfg.c
+++ b/CORE/HDD/src/wlan_hdd_cfg.c
@@ -3398,6 +3398,27 @@
                  CFG_BTC_STATIC_OPP_WLAN_IDLE_BT_LEN_MIN,
                  CFG_BTC_STATIC_OPP_WLAN_IDLE_BT_LEN_MAX ),
 
+   REG_VARIABLE( CFG_TCP_DELACK_COMPUTE_INTERVAL, WLAN_PARAM_Integer,
+                 hdd_config_t, tcpDelAckComputeInterval,
+                 VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT,
+                 CFG_TCP_DELACK_COMPUTE_INTERVAL_DEFAULT,
+                 CFG_TCP_DELACK_COMPUTE_INTERVAL_MIN,
+                 CFG_TCP_DELACK_COMPUTE_INTERVAL_MAX),
+
+   REG_VARIABLE( CFG_TCP_DELACK_THRESHOLD_HIGH, WLAN_PARAM_Integer,
+                hdd_config_t, tcpDelAckThresholdHigh,
+                VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT,
+                CFG_TCP_DELACK_THRESHOLD_HIGH_DEFAULT,
+                CFG_TCP_DELACK_THRESHOLD_HIGH_MIN,
+                CFG_TCP_DELACK_THRESHOLD_HIGH_MAX ),
+
+   REG_VARIABLE( CFG_TCP_DELACK_THRESHOLD_LOW, WLAN_PARAM_Integer,
+                hdd_config_t, tcpDelAckThresholdLow,
+                VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT,
+                CFG_TCP_DELACK_THRESHOLD_LOW_DEFAULT,
+                CFG_TCP_DELACK_THRESHOLD_LOW_MIN,
+                CFG_TCP_DELACK_THRESHOLD_LOW_MAX ),
+
    REG_VARIABLE( CFG_LINK_FAIL_TIMEOUT_NAME , WLAN_PARAM_Integer,
                  hdd_config_t, linkFailTimeout,
                  VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT,
@@ -3460,6 +3481,13 @@
                 CFG_ENABLE_CRASH_INJECT_DEFAULT,
                 CFG_ENABLE_CRASH_INJECT_MIN,
                 CFG_ENABLE_CRASH_INJECT_MAX),
+
+  REG_VARIABLE( CFG_ENABLE_TCP_DELACK_NAME, WLAN_PARAM_Integer,
+                 hdd_config_t, enable_delack,
+                 VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT,
+                 CFG_ENABLE_TCP_DELACK_DEFAULT,
+                 CFG_ENABLE_TCP_DELACK_MIN,
+                 CFG_ENABLE_TCP_DELACK_MAX ),
 };
 
 /*
@@ -3888,6 +3916,18 @@
   VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_HIGH, "Name = [toggleArpBDRates] Value = [%u] ", pHddCtx->cfg_ini->toggleArpBDRates);
   VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_HIGH, "Name = [ExtScanConcMode] Value = [%u] ", pHddCtx->cfg_ini->cfgExtScanConcMode);
   VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_HIGH, "Name = [gEnableForceTargetAssert] Value = [%u] ", pHddCtx->cfg_ini->crash_inject_enabled);
+  VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_HIGH,
+          "Name = [gTcpDelAckComputeInterval] Value = [%u] ",
+          pHddCtx->cfg_ini->tcpDelAckComputeInterval);
+  VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_HIGH,
+          "Name = [gTcpDelAckThresholdHigh] Value = [%u] ",
+          pHddCtx->cfg_ini->tcpDelAckThresholdHigh);
+  VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_HIGH,
+          "Name = [gTcpDelAckThresholdLow] Value = [%u] ",
+          pHddCtx->cfg_ini->tcpDelAckThresholdLow);
+  VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_HIGH,
+          "Name = [gEnableDelAck] Value = [%u] ",
+          pHddCtx->cfg_ini->enable_delack);
 }
 
 
diff --git a/CORE/HDD/src/wlan_hdd_cfg80211.c b/CORE/HDD/src/wlan_hdd_cfg80211.c
index 0125dc6..8a1ab9c 100644
--- a/CORE/HDD/src/wlan_hdd_cfg80211.c
+++ b/CORE/HDD/src/wlan_hdd_cfg80211.c
@@ -18033,6 +18033,9 @@
                         }
                     }
                 }
+                /* stop TCP delack timer if TDLS is enable  */
+                set_bit(WLAN_TDLS_MODE, &pHddCtx->mode);
+                hdd_manage_delack_timer(pHddCtx);
             }
             break;
         case NL80211_TDLS_DISABLE_LINK:
@@ -18174,6 +18177,11 @@
                     VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
                               "%s: TDLS Peer Station doesn't exist.", __func__);
                 }
+                if (numCurrTdlsPeers == 0) {
+                   /* start TCP delack timer if TDLS is disable  */
+                    clear_bit(WLAN_TDLS_MODE, &pHddCtx->mode);
+                    hdd_manage_delack_timer(pHddCtx);
+                }
             }
             break;
         case NL80211_TDLS_TEARDOWN:
diff --git a/CORE/HDD/src/wlan_hdd_early_suspend.c b/CORE/HDD/src/wlan_hdd_early_suspend.c
index 63f264a..b8eaba8 100644
--- a/CORE/HDD/src/wlan_hdd_early_suspend.c
+++ b/CORE/HDD/src/wlan_hdd_early_suspend.c
@@ -1921,6 +1921,10 @@
 
    vos_flush_delayed_work(&pHddCtx->spoof_mac_addr_work);
    hdd_reset_all_adapters(pHddCtx);
+
+   /* set default value of Tcp delack and stop timer */
+   hdd_set_default_stop_delack_timer(pHddCtx);
+
    /* DeRegister with platform driver as client for Suspend/Resume */
    vosStatus = hddDeregisterPmOps(pHddCtx);
    if ( !VOS_IS_STATUS_SUCCESS( vosStatus ) )
diff --git a/CORE/HDD/src/wlan_hdd_hostapd.c b/CORE/HDD/src/wlan_hdd_hostapd.c
index e3494ae..3cce315 100644
--- a/CORE/HDD/src/wlan_hdd_hostapd.c
+++ b/CORE/HDD/src/wlan_hdd_hostapd.c
@@ -1087,6 +1087,8 @@
                 }
              }
 #endif
+            hdd_manage_delack_timer(pHddCtx);
+
             pScanInfo =  &pHddCtx->scan_info;
             // Lets do abort scan to ensure smooth authentication for client
             if ((pScanInfo != NULL) && pScanInfo->mScanPending)
@@ -1151,6 +1153,7 @@
                 hddLog(LOGE, "%s: failed to update Beacon interval %d",
                         __func__, vos_status);
             }
+            hdd_manage_delack_timer(pHddCtx);
             break;
         case eSAP_WPS_PBC_PROBE_REQ_EVENT:
         {
diff --git a/CORE/HDD/src/wlan_hdd_main.c b/CORE/HDD/src/wlan_hdd_main.c
index 6795ca1..cfd03d2 100755
--- a/CORE/HDD/src/wlan_hdd_main.c
+++ b/CORE/HDD/src/wlan_hdd_main.c
@@ -7072,6 +7072,7 @@
              INIT_DELAYED_WORK(&pAdapter->roc_work,
                      hdd_p2p_roc_work_queue);
          }
+
          break;
       }
       case WLAN_HDD_MONITOR:
@@ -8761,6 +8762,17 @@
            "%s: Cannot deallocate Traffic monitor timer", __func__);
    }
 
+   if (VOS_TIMER_STATE_RUNNING ==
+                      vos_timer_getCurrentState(&pHddCtx->delack_timer)) {
+       vos_timer_stop(&pHddCtx->delack_timer);
+   }
+
+   if (!VOS_IS_STATUS_SUCCESS(vos_timer_destroy(
+                                   &pHddCtx->delack_timer))) {
+       hddLog(VOS_TRACE_LEVEL_ERROR,
+            "%s: Cannot deallocate Bus bandwidth timer", __func__);
+   }
+
    //Disable IMPS/BMPS as we do not want the device to enter any power
    //save mode during shutdown
    sme_DisablePowerSave(pHddCtx->hHal, ePMC_IDLE_MODE_POWER_SAVE);
@@ -9261,6 +9273,14 @@
                        nlh->nlmsg_len = NLMSG_LENGTH((sizeof(tAniMsgHdr)));
                        skb_put(skb, NLMSG_SPACE(sizeof(tAniMsgHdr)));
                        break;
+               case WLAN_SVC_WLAN_TP_IND:
+                        ani_hdr->length = len;
+                        nlh->nlmsg_len = NLMSG_LENGTH((sizeof(tAniMsgHdr)
+                                                                       + len));
+                        nl_data = (char *)ani_hdr + sizeof(tAniMsgHdr);
+                        memcpy(nl_data, data, len);
+                        skb_put(skb, NLMSG_SPACE(sizeof(tAniMsgHdr) + len));
+                        break;
                case WLAN_MSG_RPS_ENABLE_IND:
                         ani_hdr->length = len;
                         nlh->nlmsg_len = NLMSG_LENGTH((sizeof(tAniMsgHdr) + len));
@@ -9280,7 +9300,68 @@
     return;
 }
 
+/**
+ * hdd_request_tcp_delack() - Find the Delack value based on RX packet
+ * @pHddCtx: Valid Global HDD context pointer
+ * @rx_packets: Number of RX packet in perticular time
+ *
+ * Based on the RX packet this function calculate  next value of tcp delack.
+ * This function compare rx packet value to high and low threshold limit.
+ *
+ * Return: void
+ */
+void hdd_request_tcp_delack(hdd_context_t *pHddCtx, uint64_t rx_packets)
+{
+    /* average of rx_packets and prev_rx is taken so that
+       bus width doesnot fluctuate much */
+    uint64_t temp_rx = (rx_packets + pHddCtx->prev_rx)/2;
+    TP_IND_TYPE next_rx_level = pHddCtx->cur_rx_level;
 
+    pHddCtx->prev_rx = rx_packets;
+    if (temp_rx > pHddCtx->cfg_ini->tcpDelAckThresholdHigh)
+        next_rx_level = TP_IND_HIGH;
+    else if (temp_rx <= pHddCtx->cfg_ini->tcpDelAckThresholdLow)
+        next_rx_level = TP_IND_LOW;
+
+    hdd_set_delack_value(pHddCtx, next_rx_level);
+}
+
+#define HDD_BW_GET_DIFF(x, y) ((x) >= (y) ? (x) - (y) : (ULONG_MAX - (y) + (x)))
+
+/**
+ * hdd_tcp_delack_compute_function() - get link status
+ * @priv: Valid Global HDD context pointer
+ *
+ * This function find number of RX packet during timer life span.
+ * It request tcp delack with number of RX packet and re-configure delack timer
+ * for tcpDelAckComputeInterval timer interval.
+ *
+ * Return: void
+ */
+void hdd_tcp_delack_compute_function(void *priv)
+{
+    hdd_context_t *pHddCtx = (hdd_context_t *)priv;
+    hdd_adapter_t *pAdapter = NULL;
+    v_U32_t rx_packets = 0;
+    hdd_adapter_list_node_t *pAdapterNode = NULL;
+    VOS_STATUS status = 0;
+
+    for (status = hdd_get_front_adapter(pHddCtx, &pAdapterNode);
+          NULL != pAdapterNode && VOS_STATUS_SUCCESS == status;
+          status = hdd_get_next_adapter(pHddCtx, pAdapterNode, &pAdapterNode)) {
+        if ((pAdapter = pAdapterNode->pAdapter) == NULL)
+             continue;
+
+        rx_packets += HDD_BW_GET_DIFF(pAdapter->stats.rx_packets,
+                    pAdapter->prev_rx_packets);
+        pAdapter->prev_rx_packets = pAdapter->stats.rx_packets;
+    }
+
+    hdd_request_tcp_delack(pHddCtx, rx_packets);
+
+    vos_timer_start(&pHddCtx->delack_timer,
+            pHddCtx->cfg_ini->tcpDelAckComputeInterval);
+}
 
 /**---------------------------------------------------------------------------
 
@@ -10497,6 +10578,10 @@
    }
    wlan_hdd_cfg80211_nan_init(pHddCtx);
 
+   mutex_init(&pHddCtx->cur_rx_level_lock);
+   vos_timer_init(&pHddCtx->delack_timer, VOS_TIMER_TYPE_SW,
+                             hdd_tcp_delack_compute_function,(void *)pHddCtx);
+
 #ifdef WLAN_FEATURE_EXTSCAN
     sme_EXTScanRegisterCallback(pHddCtx->hHal,
             wlan_hdd_cfg80211_extscan_callback,
@@ -11403,6 +11488,7 @@
  * --------------------------------------------------------------------------*/
 void wlan_hdd_decr_active_session(hdd_context_t *pHddCtx, tVOS_CON_MODE mode)
 {
+
    switch (mode) {
    case VOS_STA_MODE:
    case VOS_P2P_CLIENT_MODE:
@@ -11615,6 +11701,178 @@
    return status;
 }
 
+/**
+ * hdd_get_total_sessions() -  provide total number of active sessions
+ * @pHddCtx: Valid Global HDD context pointer
+ *
+ * This function iterates through pAdaptors and find the number of all active
+ * sessions. This active sessions includes connected sta, p2p client and number
+ * of client connected to sap/p2p go.
+ *
+ * Return:  Total number of active sessions.
+ */
+v_U8_t  hdd_get_total_sessions(hdd_context_t *pHddCtx)
+{
+    v_U8_t active_session = 0;
+    hdd_station_ctx_t *pHddStaCtx;
+    hdd_adapter_list_node_t *pAdapterNode, *pNext;
+    hdd_adapter_t *pAdapter;
+    VOS_STATUS status;
+
+    status = hdd_get_front_adapter(pHddCtx, &pAdapterNode);
+    while (NULL != pAdapterNode && VOS_STATUS_SUCCESS == status) {
+        pAdapter = pAdapterNode->pAdapter;
+        switch (pAdapter->device_mode) {
+        case VOS_STA_MODE:
+        case VOS_P2P_CLIENT_MODE:
+            pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
+            if(eConnectionState_Associated == pHddStaCtx->conn_info.connState)
+               active_session += 1;
+            break;
+        case VOS_STA_SAP_MODE:
+        case VOS_P2P_GO_MODE:
+           active_session += hdd_softap_get_connected_sta(pAdapter);
+           break;
+        default:
+           break;
+        }
+
+        status = hdd_get_next_adapter(pHddCtx, pAdapterNode, &pNext);
+        pAdapterNode = pNext;
+    }
+
+    return active_session;
+}
+
+/**
+ * hdd_set_delack_value() - Set delack value
+ * @pHddCtx: Valid Global HDD context pointer
+ * @next_rx_level: Value to set for delack
+ *
+ * This function compare  present value and  next value of delack. If the both
+ * are diffrent then it sets next value .
+ *
+ * Return: void.
+ */
+void hdd_set_delack_value(hdd_context_t *pHddCtx, v_U32_t next_rx_level)
+{
+    if (pHddCtx->cur_rx_level != next_rx_level) {
+        VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_DEBUG,
+               "%s: TCP DELACK trigger level %d",
+               __func__, next_rx_level);
+        mutex_lock(&pHddCtx->cur_rx_level_lock);
+        pHddCtx->cur_rx_level = next_rx_level;
+        mutex_unlock(&pHddCtx->cur_rx_level_lock);
+        wlan_hdd_send_svc_nlink_msg(WLAN_SVC_WLAN_TP_IND, &next_rx_level,
+                                                       sizeof(next_rx_level));
+    }
+}
+
+/**
+ * hdd_set_default_stop_delack_timer() - Start delack timer
+ * @pHddCtx: Valid Global HDD context pointer
+ *
+ * This function stop delack timer and set delack value to default..
+ *
+ * Return: void.
+ */
+
+void hdd_set_default_stop_delack_timer(hdd_context_t *pHddCtx)
+{
+    if (VOS_TIMER_STATE_RUNNING !=
+                            vos_timer_getCurrentState(&pHddCtx->delack_timer)) {
+         VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_DEBUG,
+               "%s: Can not stop timer", __func__);
+        return;
+    }
+
+    vos_timer_stop(&pHddCtx->delack_timer);
+    hdd_set_delack_value(pHddCtx, TP_IND_LOW);
+}
+
+/**
+ * hdd_start_delack_timer() - Start delack timer
+ * @pHddCtx: Valid Global HDD context pointer
+ *
+ * This function starts the delack timer for tcpDelAckComputeInterval time
+ * interval.The default timer value is 2 second.
+ *
+ * Return: void.
+ */
+void hdd_start_delack_timer(hdd_context_t *pHddCtx)
+{
+    if (VOS_TIMER_STATE_RUNNING ==
+                            vos_timer_getCurrentState(&pHddCtx->delack_timer)) {
+        VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_DEBUG,
+              "%s: Timer is already running", __func__);
+        return;
+    }
+
+    vos_timer_start(&pHddCtx->delack_timer,
+                                pHddCtx->cfg_ini->tcpDelAckComputeInterval);
+}
+
+/**
+ * hdd_update_prev_rx_packet_count() - Update previous rx packet count
+ * @pHddCtx: Valid Global HDD context pointer
+ *
+ * This function updates the prev_rx_packets count from the corresponding
+ * pAdapter states. This prev_rx_packets will diffed with the packet count
+ * at the end of delack timer. That can give number of RX packet is spacific
+ * time.
+ *
+ * Return: void.
+ */
+void hdd_update_prev_rx_packet_count(hdd_context_t *pHddCtx)
+{
+    hdd_adapter_list_node_t *pAdapterNode, *pNext;
+    hdd_adapter_t *pAdapter;
+    VOS_STATUS status;
+
+    status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode );
+    while (NULL != pAdapterNode && VOS_STATUS_SUCCESS == status) {
+        pAdapter = pAdapterNode->pAdapter;
+        pAdapter->prev_rx_packets = pAdapter->stats.rx_packets;
+        status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext );
+        pAdapterNode = pNext;
+    }
+}
+
+/**
+ * hdd_manage_delack_timer() - start\stop delack timer
+ * @pHddCtx: Valid Global HDD context pointer
+ *
+ * This function check the number of concerent session present, it starts the
+ * delack timer if only one session is present.
+ * In the case of BT_COEX and TDLS mode it blindly stop delack functionality.
+ *
+ * Return: void.
+ */
+void hdd_manage_delack_timer(hdd_context_t *pHddCtx)
+{
+    uint8_t sessions;
+
+    if (!pHddCtx->cfg_ini->enable_delack) {
+        VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_DEBUG,
+               "%s: TCP DELACK is not enabled", __func__);
+        return;
+    }
+
+    /* Blindly stop timer of BTCOEX and TDLS Session is up */
+    if (pHddCtx->mode != 0) {
+        hdd_set_default_stop_delack_timer(pHddCtx);
+        return;
+    }
+
+    sessions = hdd_get_total_sessions(pHddCtx);
+    if (sessions == 1) {
+        hdd_update_prev_rx_packet_count(pHddCtx);
+        hdd_start_delack_timer(pHddCtx);
+    } else {
+        hdd_set_default_stop_delack_timer(pHddCtx);
+    }
+}
+
 /**---------------------------------------------------------------------------
  *
  *   \brief wlan_hdd_init_channels
diff --git a/CORE/HDD/src/wlan_hdd_softap_tx_rx.c b/CORE/HDD/src/wlan_hdd_softap_tx_rx.c
index 3bcf17f..b4c919c 100644
--- a/CORE/HDD/src/wlan_hdd_softap_tx_rx.c
+++ b/CORE/HDD/src/wlan_hdd_softap_tx_rx.c
@@ -278,6 +278,41 @@
    return status;
 }
 
+/**
+ * hdd_softap_get_connected_sta() -  provide number of connected STA
+ * @pHostapdAdapter: pAdapter for SAP
+ *
+ * This function is invoked for SAP mode to get connected STA.
+ *
+ * Return:  Total number of connected STA to SAP.
+ */
+v_U8_t hdd_softap_get_connected_sta(hdd_adapter_t *pHostapdAdapter)
+{
+    v_U8_t i, sta_ct = 0;
+    v_CONTEXT_t pVosContext = NULL;
+    ptSapContext pSapCtx = NULL;
+
+    pVosContext = (WLAN_HDD_GET_CTX(pHostapdAdapter))->pvosContext;
+    pSapCtx = VOS_GET_SAP_CB(pVosContext);
+    if (pSapCtx == NULL) {
+        VOS_TRACE(VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_INFO,
+                 FL("psapCtx is NULL"));
+        goto error;
+    }
+
+    spin_lock_bh(&pSapCtx->staInfo_lock);
+    // get stations associated with SAP
+    for (i = 0; i < WLAN_MAX_STA_COUNT; i++) {
+        if (pSapCtx->aStaInfo[i].isUsed &&
+                  (!vos_is_macaddr_broadcast(&pSapCtx->aStaInfo[i].macAddrSTA)))
+               sta_ct++;
+    }
+    spin_unlock_bh( &pSapCtx->staInfo_lock );
+
+error:
+    return sta_ct;
+}
+
 /**============================================================================
   @brief hdd_softap_hard_start_xmit() - Function registered with the Linux OS for 
   transmitting packets. There are 2 versions of this function. One that uses
diff --git a/CORE/HDD/src/wlan_hdd_tdls.c b/CORE/HDD/src/wlan_hdd_tdls.c
index 0b0f418..e548cf9 100644
--- a/CORE/HDD/src/wlan_hdd_tdls.c
+++ b/CORE/HDD/src/wlan_hdd_tdls.c
@@ -802,6 +802,7 @@
     hdd_context_t *pHddCtx;
     u16 connectedTdlsPeers;
     hddTdlsPeer_t *currPeer;
+    tANI_U16 numCurrTdlsPeers = 0;
 
     ENTER();
     if ((NULL == data) || (indType < 0))
@@ -887,6 +888,9 @@
                }
             }
         }
+        /* stop TCP delack timer if BtCoex is enable  */
+        set_bit(WLAN_BTCOEX_MODE, &pHddCtx->mode);
+        hdd_manage_delack_timer(pHddCtx);
    }
    /* BtCoex notification type enabled, Enable TDLS */
    else if (indType == SIR_COEX_IND_TYPE_TDLS_ENABLE)
@@ -903,7 +907,15 @@
             pHddCtx->is_tdls_btc_enabled = TRUE;
             wlan_hdd_tdls_set_mode(pHddCtx, pHddCtx->tdls_mode_last, FALSE);
         }
-   }
+
+        clear_bit(WLAN_BTCOEX_MODE, &pHddCtx->mode);
+        numCurrTdlsPeers = wlan_hdd_tdlsConnectedPeers(pAdapter);
+        if(numCurrTdlsPeers == 0) {
+             /* start delack timer if BtCoex is disable and tdls is not present */
+             hdd_manage_delack_timer(pHddCtx);
+        }
+  }
+
    EXIT();
    return;
 }
diff --git a/CORE/SVC/external/wlan_nlink_common.h b/CORE/SVC/external/wlan_nlink_common.h
index 75d0625..6ffcee6 100644
--- a/CORE/SVC/external/wlan_nlink_common.h
+++ b/CORE/SVC/external/wlan_nlink_common.h
@@ -89,6 +89,7 @@
 #define WLAN_SVC_IFACE_NUM_QUEUES      6
 
 #define WLAN_SVC_SAP_RESTART_IND 0x108
+#define WLAN_SVC_WLAN_TP_IND     0x109
 // Event data for WLAN_BTC_QUERY_STATE_RSP & WLAN_STA_ASSOC_DONE_IND
 typedef struct
 {
diff --git a/firmware_bin/WCNSS_qcom_cfg.ini b/firmware_bin/WCNSS_qcom_cfg.ini
index e9d6727..97b61e9 100644
--- a/firmware_bin/WCNSS_qcom_cfg.ini
+++ b/firmware_bin/WCNSS_qcom_cfg.ini
@@ -465,6 +465,18 @@
 #Default is set to 0 (disable)
 gEnableRtsCtsHtVht=0
 
+#Enable delACK
+gEnableDelAck=1
+
+#Compute interval for delack
+gTcpDelAckComputeInterval=2000
+
+#Delack set higher limit
+gTcpDelAckThresholdHigh=17000
+
+#Delack set lower limit
+gTcpDelAckThresholdLow=12000
+
 END
 
 # Note: Configuration parser would not read anything past the END marker