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