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/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