wlan: Add traffic monitor on SAP/P2P-GO mode

To address the issue wherein APPS fails to power collapse
when OBSS protection is enabled,
we need to implement an IDLE traffic monitoring
logic in the HDD: it needs to detect 2 states:
IDLE STATE: HDD transitions to that STATE
when there is no traffic for a configuration duration in cfg file.
ACTIVE STATE: this is the initial state when SAP comes up.
HDD moves also to that state
when it sees traffic when in IDLE State.

Change-Id: I45dda8a1b8d7aa588ec940cc3b115b918b01c932
CRs-fixed: 495899
diff --git a/CORE/HDD/inc/wlan_hdd_cfg.h b/CORE/HDD/inc/wlan_hdd_cfg.h
index f51d361..ee7bae4 100644
--- a/CORE/HDD/inc/wlan_hdd_cfg.h
+++ b/CORE/HDD/inc/wlan_hdd_cfg.h
@@ -1743,6 +1743,19 @@
 #define CFG_MAX_MEDIUM_TIME_STAMAX               WNI_CFG_MAX_MEDIUM_TIME_STAMAX
 #define CFG_MAX_MEDIUM_TIME_STADEFAULT           WNI_CFG_MAX_MEDIUM_TIME_STADEF
 
+/*
+ * SAP/P2P-GO mode traffic monitor
+ */
+#define CFG_ENABLE_TRAFFIC_MONITOR                "gEnableTrafficMonitor"
+#define CFG_ENABLE_TRAFFIC_MONITOR_MIN            ( 0 )
+#define CFG_ENABLE_TRAFFIC_MONITOR_MAX            ( 1 )
+#define CFG_ENABLE_TRAFFIC_MONITOR_DEFAULT        ( 0 )
+
+#define CFG_TRAFFIC_IDLE_TIMEOUT                  "gTrafficIdleTimeout"
+#define CFG_TRAFFIC_IDLE_TIMEOUT_MIN              ( 3000 )
+#define CFG_TRAFFIC_IDLE_TIMEOUT_MAX              ( 10000 )
+#define CFG_TRAFFIC_IDLE_TIMEOUT_DEFAULT          ( 5000 )
+
 /*--------------------------------------------------------------------------- 
   Type declarations
   -------------------------------------------------------------------------*/ 
@@ -2117,6 +2130,8 @@
    v_BOOL_t                    enableSSR;
    char                        listOfNon11acCountryCode[128];
    v_U32_t                     cfgMaxMediumTime;
+   v_U8_t                      enableTrafficMonitor;
+   v_U32_t                     trafficIdleTimeout;
 } hdd_config_t;
 /*--------------------------------------------------------------------------- 
   Function declarations and documenation
diff --git a/CORE/HDD/inc/wlan_hdd_main.h b/CORE/HDD/inc/wlan_hdd_main.h
index 848f439..7a9440a 100644
--- a/CORE/HDD/inc/wlan_hdd_main.h
+++ b/CORE/HDD/inc/wlan_hdd_main.h
@@ -856,6 +856,15 @@
    int total_len;
 }hdd_priv_data_t;
 
+typedef struct
+{
+   vos_timer_t trafficTimer;
+   atomic_t    isActiveMode;
+   v_U8_t      isInitialized;
+   vos_lock_t  trafficLock;
+   v_TIME_t    lastFrameTs;
+}hdd_traffic_monitor_t;
+
 /** Adapter stucture definition */
 
 struct hdd_context_s
@@ -1002,6 +1011,8 @@
     tANI_U16 connected_peer_count;
     tdls_scan_context_t tdls_scan_ctxt;
 #endif
+
+    hdd_traffic_monitor_t traffic_monitor;
 };
 
 
diff --git a/CORE/HDD/src/wlan_hdd_cfg.c b/CORE/HDD/src/wlan_hdd_cfg.c
index f7a72af..98fc710 100644
--- a/CORE/HDD/src/wlan_hdd_cfg.c
+++ b/CORE/HDD/src/wlan_hdd_cfg.c
@@ -2267,6 +2267,19 @@
              CFG_MAX_MEDIUM_TIME_STAMIN,
              CFG_MAX_MEDIUM_TIME_STAMAX ),
 
+   REG_VARIABLE( CFG_ENABLE_TRAFFIC_MONITOR, WLAN_PARAM_Integer,
+                hdd_config_t, enableTrafficMonitor,
+                VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT,
+                CFG_ENABLE_TRAFFIC_MONITOR_DEFAULT,
+                CFG_ENABLE_TRAFFIC_MONITOR_MIN,
+                CFG_ENABLE_TRAFFIC_MONITOR_MAX),
+
+   REG_VARIABLE( CFG_TRAFFIC_IDLE_TIMEOUT, WLAN_PARAM_Integer,
+                hdd_config_t, trafficIdleTimeout,
+                VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT,
+                CFG_TRAFFIC_IDLE_TIMEOUT_DEFAULT,
+                CFG_TRAFFIC_IDLE_TIMEOUT_MIN,
+                CFG_TRAFFIC_IDLE_TIMEOUT_MAX),
 };
 
 /*
@@ -2647,6 +2660,8 @@
   VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_HIGH, "Name = [gEnableLpwrImgTransition] Value = [%u] ",pHddCtx->cfg_ini->enableLpwrImgTransition);
   VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_HIGH, "Name = [gEnableSSR] Value = [%u] ",pHddCtx->cfg_ini->enableSSR);
 
+  VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_HIGH, "Name = [gEnableTrafficMonitor] Value = [%u] ", pHddCtx->cfg_ini->enableTrafficMonitor);
+  VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_HIGH, "Name = [gTrafficIdleTimeout] Value = [%u] ", pHddCtx->cfg_ini->trafficIdleTimeout);
 }
 
 
diff --git a/CORE/HDD/src/wlan_hdd_softap_tx_rx.c b/CORE/HDD/src/wlan_hdd_softap_tx_rx.c
index 0f66baa..7326079 100644
--- a/CORE/HDD/src/wlan_hdd_softap_tx_rx.c
+++ b/CORE/HDD/src/wlan_hdd_softap_tx_rx.c
@@ -98,6 +98,45 @@
      skb->data[13], skb->data[14], skb->data[15]); 
 }
 #endif
+
+extern void hdd_set_wlan_suspend_mode(bool suspend);
+
+/**============================================================================
+  @brief hdd_softap_traffic_monitor_timeout_handler() -
+         SAP/P2P GO traffin monitor timeout handler function
+         If no traffic during programmed time, trigger suspand mode
+
+  @param pUsrData : [in] pointer to hdd context
+  @return         : NONE
+  ===========================================================================*/
+void hdd_softap_traffic_monitor_timeout_handler( void *pUsrData )
+{
+   hdd_context_t *pHddCtx = (hdd_context_t *)pUsrData;
+   v_TIME_t       currentTS;
+
+   if (NULL == pHddCtx)
+   {
+      VOS_TRACE( VOS_MODULE_ID_HDD_SOFTAP, VOS_TRACE_LEVEL_ERROR,
+              "%s: Invalid user data, context", __func__);
+      return;
+   }
+
+   currentTS = vos_timer_get_system_time();
+   if (pHddCtx->cfg_ini->trafficIdleTimeout <
+       (currentTS - pHddCtx->traffic_monitor.lastFrameTs))
+   {
+      hdd_set_wlan_suspend_mode(1);
+      atomic_set(&pHddCtx->traffic_monitor.isActiveMode, 0);
+   }
+   else
+   {
+      vos_timer_start(&pHddCtx->traffic_monitor.trafficTimer,
+                      pHddCtx->cfg_ini->trafficIdleTimeout);
+   }
+
+   return;
+}
+
 /**============================================================================
   @brief hdd_softap_flush_tx_queues() - Utility function to flush the TX queues
 
@@ -521,6 +560,9 @@
                            HDD_SOFTAP_VI_WEIGHT_DEFAULT, 
                            HDD_SOFTAP_VO_WEIGHT_DEFAULT
                          };
+
+   hdd_context_t *pHddCtx = NULL;
+
    pAdapter->isVosOutOfResource = VOS_FALSE;
 
    vos_mem_zero(&pAdapter->stats, sizeof(struct net_device_stats));
@@ -550,6 +592,31 @@
    /* Update the AC weights suitable for SoftAP mode of operation */
    WLANTL_SetACWeights((WLAN_HDD_GET_CTX(pAdapter))->pvosContext, pACWeights);
 
+   /* Initialize SAP/P2P-GO traffin monitor */
+   pHddCtx = (hdd_context_t *)pAdapter->pHddCtx;
+   if (NULL == pHddCtx)
+   {
+      VOS_TRACE( VOS_MODULE_ID_HDD_SOFTAP, VOS_TRACE_LEVEL_ERROR,
+                 "%s: Invalid HDD cntxt", __func__ );
+      return VOS_STATUS_E_INVAL;
+   }
+   if ((pHddCtx->cfg_ini->enableTrafficMonitor) &&
+       (!pHddCtx->traffic_monitor.isInitialized))
+   {
+      atomic_set(&pHddCtx->traffic_monitor.isActiveMode, 1);
+      vos_timer_init(&pHddCtx->traffic_monitor.trafficTimer,
+                     VOS_TIMER_TYPE_SW,
+                     hdd_softap_traffic_monitor_timeout_handler,
+                     pHddCtx);
+      vos_lock_init(&pHddCtx->traffic_monitor.trafficLock);
+      pHddCtx->traffic_monitor.isInitialized = 1;
+      pHddCtx->traffic_monitor.lastFrameTs   = 0;
+      /* Start traffic monitor timer here
+       * If no AP assoc, immediatly go into suspend */
+      vos_timer_start(&pHddCtx->traffic_monitor.trafficTimer,
+                      pHddCtx->cfg_ini->trafficIdleTimeout);
+   }
+
    return status;
 }
 
@@ -564,6 +631,26 @@
 VOS_STATUS hdd_softap_deinit_tx_rx( hdd_adapter_t *pAdapter )
 {
    VOS_STATUS status = VOS_STATUS_SUCCESS;
+   hdd_context_t *pHddCtx = NULL;
+
+   pHddCtx = (hdd_context_t *)pAdapter->pHddCtx;
+   if (NULL == pHddCtx)
+   {
+      VOS_TRACE( VOS_MODULE_ID_HDD_SOFTAP, VOS_TRACE_LEVEL_ERROR,
+                 "%s: Invalid HDD cntxt", __func__ );
+      return VOS_STATUS_E_INVAL;
+   }
+   if (pHddCtx->traffic_monitor.isInitialized)
+   {
+      if (VOS_TIMER_STATE_STOPPED !=
+          vos_timer_getCurrentState(&pHddCtx->traffic_monitor.trafficTimer))
+      {
+         vos_timer_stop(&pHddCtx->traffic_monitor.trafficTimer);
+      }
+      vos_timer_destroy(&pHddCtx->traffic_monitor.trafficTimer);
+      vos_lock_destroy(&pHddCtx->traffic_monitor.trafficLock);
+      pHddCtx->traffic_monitor.isInitialized = 0;
+   }
 
    status = hdd_softap_flush_tx_queues(pAdapter);
 
@@ -853,6 +940,26 @@
       return VOS_STATUS_E_FAILURE;
    }
  
+   /* Monitor traffic */
+   if ( pHddCtx->cfg_ini->enableTrafficMonitor )
+   {
+      pHddCtx->traffic_monitor.lastFrameTs = vos_timer_get_system_time();
+      if ( !atomic_read(&pHddCtx->traffic_monitor.isActiveMode) )
+      {
+         vos_lock_acquire(&pHddCtx->traffic_monitor.trafficLock);
+         /* It was IDLE mode,
+          * this is new state, then switch mode from suspend to resume */
+         if ( !atomic_read(&pHddCtx->traffic_monitor.isActiveMode) )
+         {
+            hdd_set_wlan_suspend_mode(0);
+            vos_timer_start(&pHddCtx->traffic_monitor.trafficTimer,
+                            pHddCtx->cfg_ini->trafficIdleTimeout);
+            atomic_set(&pHddCtx->traffic_monitor.isActiveMode, 1);
+         }
+         vos_lock_release(&pHddCtx->traffic_monitor.trafficLock);
+      }
+   }
+
    ++pAdapter->hdd_stats.hddTxRxStats.txFetched;
 
    STAId = *pStaId;
@@ -1148,7 +1255,27 @@
       VOS_ASSERT(0);
       return VOS_STATUS_E_FAILURE;
    }
-   
+
+   /* Monitor traffic */
+   if ( pHddCtx->cfg_ini->enableTrafficMonitor )
+   {
+      pHddCtx->traffic_monitor.lastFrameTs = vos_timer_get_system_time();
+      if ( !atomic_read(&pHddCtx->traffic_monitor.isActiveMode) )
+      {
+         vos_lock_acquire(&pHddCtx->traffic_monitor.trafficLock);
+         /* It was IDLE mode,
+          * this is new state, then switch mode from suspend to resume */
+         if ( !atomic_read(&pHddCtx->traffic_monitor.isActiveMode) )
+         {
+            hdd_set_wlan_suspend_mode(0);
+            vos_timer_start(&pHddCtx->traffic_monitor.trafficTimer,
+                            pHddCtx->cfg_ini->trafficIdleTimeout);
+            atomic_set(&pHddCtx->traffic_monitor.isActiveMode, 1);
+         }
+         vos_lock_release(&pHddCtx->traffic_monitor.trafficLock);
+      }
+   }
+
    ++pAdapter->hdd_stats.hddTxRxStats.rxChains;
 
    // walk the chain until all are processed