prima: Add support for AP mode WoW

Presently in the AP mode only probse respnse is offloaded. Add
support to configure the various WoW offloads like arp,ns and magic
pattern during suspend.

Change-Id: Ibcb6e41bd37e74d68b00fd19ea75986d24514217
CRs-Fixed: 1066854
diff --git a/CORE/HDD/inc/qc_sap_ioctl.h b/CORE/HDD/inc/qc_sap_ioctl.h
index 2bc3b6a..303376b 100644
--- a/CORE/HDD/inc/qc_sap_ioctl.h
+++ b/CORE/HDD/inc/qc_sap_ioctl.h
@@ -142,6 +142,10 @@
 #define QCSAP_IOCTL_GETPARAM          (SIOCIWFIRSTPRIV+1)
 #define QCSAP_IOCTL_COMMIT            (SIOCIWFIRSTPRIV+2)
 
+#define QCSAP_IOCTL_SET_CHAR_GET_NONE (SIOCIWFIRSTPRIV + 3)
+#define WE_WOWL_ADD_PTRN     1
+#define WE_WOWL_DEL_PTRN     2
+
 #define QCSAP_IOCTL_GET_STAWPAIE      (SIOCIWFIRSTPRIV+4)
 
 #define QCSAP_IOCTL_STOPBSS           (SIOCIWFIRSTPRIV+6)
@@ -186,7 +190,8 @@
     QCSAP_PARAM_SET_MC_RATE = 10,
     QCSAP_PARAM_SET_AUTO_CHANNEL = 11,
     QCSAP_PARAM_GET_FRAME_LOGS = 12,
-    QCSAP_PARAM_SET_PROXIMITY = 13
+    QCSAP_PARAM_SET_PROXIMITY = 13,
+    QCSAP_PARAM_SET_WOWL = 14
 };
 
 int iw_softap_get_channel_list(struct net_device *dev, 
diff --git a/CORE/HDD/inc/wlan_hdd_main.h b/CORE/HDD/inc/wlan_hdd_main.h
index 7e5c098..a4a16d4 100644
--- a/CORE/HDD/inc/wlan_hdd_main.h
+++ b/CORE/HDD/inc/wlan_hdd_main.h
@@ -1625,6 +1625,7 @@
     v_U8_t last_scan_reject_session_id;
     scan_reject_states last_scan_reject_reason;
     v_TIME_t last_scan_reject_timestamp;
+    bool is_ap_mode_wow_supported;
 };
 
 typedef enum  {
diff --git a/CORE/HDD/src/wlan_hdd_cfg80211.c b/CORE/HDD/src/wlan_hdd_cfg80211.c
index 51afef5..f0e19b2 100644
--- a/CORE/HDD/src/wlan_hdd_cfg80211.c
+++ b/CORE/HDD/src/wlan_hdd_cfg80211.c
@@ -19266,6 +19266,7 @@
 }
 #endif /* CONFIG_NL80211_TESTMODE */
 
+extern void hdd_set_wlan_suspend_mode(bool suspend);
 static int __wlan_hdd_cfg80211_dump_survey(struct wiphy *wiphy,
                                          struct net_device *dev,
                                          int idx, struct survey_info *survey)
@@ -19412,6 +19413,15 @@
 
     MTRACE(vos_trace(VOS_MODULE_ID_HDD, TRACE_CODE_HDD_CFG80211_RESUME_WLAN,
                      NO_SESSION, pHddCtx->isWiphySuspended));
+
+    if ((VOS_STA_SAP_MODE == hdd_get_conparam()) &&
+        pHddCtx->is_ap_mode_wow_supported)
+    {
+        VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
+                  "%s: Resume SoftAP", __func__);
+           hdd_set_wlan_suspend_mode(false);
+    }
+
     spin_lock(&pHddCtx->schedScan_lock);
     pHddCtx->isWiphySuspended = FALSE;
     if (TRUE != pHddCtx->isSchedScanUpdatePending)
@@ -19496,6 +19506,13 @@
         return ret;
     }
 
+   if ((VOS_STA_SAP_MODE == hdd_get_conparam()) &&
+       (pHddCtx->is_ap_mode_wow_supported)) {
+        VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
+                  "%s: Suspend SoftAP", __func__);
+         hdd_set_wlan_suspend_mode(true);
+    }
+
 
     MTRACE(vos_trace(VOS_MODULE_ID_HDD,
                      TRACE_CODE_HDD_CFG80211_SUSPEND_WLAN,
diff --git a/CORE/HDD/src/wlan_hdd_early_suspend.c b/CORE/HDD/src/wlan_hdd_early_suspend.c
index 1607774..18d76aa 100644
--- a/CORE/HDD/src/wlan_hdd_early_suspend.c
+++ b/CORE/HDD/src/wlan_hdd_early_suspend.c
@@ -634,12 +634,16 @@
     }
 
     if ((WLAN_HDD_INFRA_STATION == pAdapter->device_mode) ||
-           (WLAN_HDD_P2P_CLIENT == pAdapter->device_mode))
+       (WLAN_HDD_P2P_CLIENT == pAdapter->device_mode) ||
+       ((WLAN_HDD_SOFTAP == pAdapter->device_mode) &&
+       (pHddCtx->is_ap_mode_wow_supported)))
     {
         if (fenable)
         {
-            if (eConnectionState_Associated ==
-                    (WLAN_HDD_GET_STATION_CTX_PTR(pAdapter))->conn_info.connState)
+            if (((WLAN_HDD_INFRA_STATION == pAdapter->device_mode) &&
+                (eConnectionState_Associated ==
+                (WLAN_HDD_GET_STATION_CTX_PTR(pAdapter))->conn_info.connState))
+                || (WLAN_HDD_SOFTAP == pAdapter->device_mode))
             {
                 if ((pHddCtx->cfg_ini->fhostArpOffload))
                 {
diff --git a/CORE/HDD/src/wlan_hdd_hostapd.c b/CORE/HDD/src/wlan_hdd_hostapd.c
index cb63b90..f2e8d13 100644
--- a/CORE/HDD/src/wlan_hdd_hostapd.c
+++ b/CORE/HDD/src/wlan_hdd_hostapd.c
@@ -79,6 +79,7 @@
 #include "wlan_hdd_p2p.h"
 #include "cfgApi.h"
 #include "wniCfg.h"
+#include <wlan_hdd_wowl.h>
 
 #ifdef FEATURE_WLAN_CH_AVOID
 #include "wcnss_wlan.h"
@@ -1805,6 +1806,7 @@
     int set_value = value[1];
     eHalStatus status;
     int ret = 0; /* success */
+    int enable_pattrn_byte_match, enable_magic_pkt;
     v_CONTEXT_t pVosContext;
 
     ENTER();
@@ -1961,6 +1963,37 @@
                 ret = wlan_hdd_set_proximity(set_value, hHal);
                 break;
             }
+        case QCSAP_PARAM_SET_WOWL:
+            {
+                if (!pHddCtx->is_ap_mode_wow_supported)
+                {
+                    VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+                              "%s: Not supported",__func__);
+                    return -ENOTSUPP;
+                }
+                switch (set_value)
+                {
+                   case 0x00:
+                     hdd_exit_wowl(pHostapdAdapter, eWOWL_EXIT_USER);
+                     break;
+                   case 0x01:
+                   case 0x02:
+                   case 0x03:
+                     enable_magic_pkt =  (set_value & 0x01) ? 1 : 0;
+                     enable_pattrn_byte_match = (set_value & 0x02) ? 1 : 0;
+                     hddLog(LOGE, "magic packet ? = %s pattern byte matching ? = %s",
+                           (enable_magic_pkt ? "YES":"NO"),
+                           (enable_pattrn_byte_match ? "YES":"NO"));
+                     hdd_enter_wowl(pHostapdAdapter, enable_magic_pkt,
+                                    enable_pattrn_byte_match);
+                     break;
+                   default:
+                     hddLog(LOGE, "Invalid arg  %d in WE_WOWL IOCTL", set_value);
+                     ret = -EINVAL;
+                     break;
+                }
+                break;
+            }
         default:
             hddLog(LOGE, FL("Invalid setparam command %d value %d"),
                     sub_cmd, set_value);
@@ -2119,6 +2152,112 @@
 
     return ret;
 }
+
+int
+static __iw_softap_setchar_getnone(struct net_device *dev,
+                                 struct iw_request_info *info,
+                                 union iwreq_data *wrqu, char *extra)
+{
+    int sub_cmd;
+    int ret = 0; /* success */
+    char *pBuffer = NULL;
+    hdd_adapter_t *pAdapter;
+    hdd_context_t *pHddCtx;
+    struct iw_point s_priv_data;
+
+    ENTER();
+
+    if (!capable(CAP_NET_ADMIN))
+    {
+        VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+                  FL("permission check failed"));
+        return -EPERM;
+    }
+
+    pAdapter = (netdev_priv(dev));
+    pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
+    ret = wlan_hdd_validate_context(pHddCtx);
+    if (0 != ret)
+    {
+        return ret;
+    }
+
+    if (!pHddCtx->is_ap_mode_wow_supported)
+    {
+      VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
+                "%s: Not supported",__func__);
+      return -ENOTSUPP;
+   }
+
+    /* helper function to get iwreq_data with compat handling. */
+    if (hdd_priv_get_data(&s_priv_data, wrqu))
+    {
+       return -EINVAL;
+    }
+
+    /* make sure all params are correctly passed to function */
+    if ((NULL == s_priv_data.pointer) || (0 == s_priv_data.length))
+    {
+       return -EINVAL;
+    }
+
+    sub_cmd = s_priv_data.flags;
+
+    /* ODD number is used for set, copy data using copy_from_user */
+    pBuffer = mem_alloc_copy_from_user_helper(s_priv_data.pointer,
+                                               s_priv_data.length);
+    if (NULL == pBuffer)
+    {
+        VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+                  "mem_alloc_copy_from_user_helper fail");
+        return -ENOMEM;
+    }
+
+    VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
+              "%s: Received length %d", __func__, s_priv_data.length);
+    VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
+              "%s: Received data %s", __func__, pBuffer);
+
+    switch(sub_cmd)
+    {
+       case WE_WOWL_ADD_PTRN:
+          VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, "ADD_PTRN");
+          ret = hdd_add_wowl_ptrn(pAdapter, pBuffer);
+          if (!ret)
+             VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+                       "Failed to add pattern :%d", ret);
+          break;
+       case WE_WOWL_DEL_PTRN:
+          VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, "DEL_PTRN");
+          ret = hdd_del_wowl_ptrn(pAdapter, pBuffer);
+          if (!ret)
+             VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+                       "Failed to del pattern :%d", ret);
+          break;
+        default:
+           VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, "ioctl not supported in SOFTAP");
+           ret = -EINVAL;
+           break;
+     }
+
+     kfree(pBuffer);
+     return ret;
+}
+
+int
+static iw_softap_setchar_getnone(struct net_device *dev,
+                                 struct iw_request_info *info,
+                                 union iwreq_data *wrqu, char *extra)
+{
+    int ret;
+
+    vos_ssr_protect(__func__);
+    ret = __iw_softap_setchar_getnone(dev, info, wrqu, extra);
+    vos_ssr_unprotect(__func__);
+
+    return ret;
+}
+
 /* Usage:
     BLACK_LIST  = 0
     WHITE_LIST  = 1 
@@ -4419,6 +4558,8 @@
       IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0,  "setMcRate" },
   { QCSAP_PARAM_SET_PROXIMITY,
       IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0,  "setProximity" },
+  { QCSAP_PARAM_SET_WOWL,
+      IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0,  "wowl" },
   { QCSAP_IOCTL_GETPARAM,
       IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
       IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,    "getparam" },
@@ -4530,11 +4671,29 @@
         IW_PRIV_TYPE_INT| IW_PRIV_SIZE_FIXED | 1,
         0,
         "setTrafficMon" },
+    /* handlers for main ioctl */
+    {   QCSAP_IOCTL_SET_CHAR_GET_NONE,
+        IW_PRIV_TYPE_CHAR| 512,
+        0,
+        "" },
+
+    /* handlers for sub-ioctl */
+    {   WE_WOWL_ADD_PTRN,
+        IW_PRIV_TYPE_CHAR| 512,
+        0,
+        "wowlAddPtrn" },
+
+    {   WE_WOWL_DEL_PTRN,
+        IW_PRIV_TYPE_CHAR| 512,
+        0,
+        "wowlDelPtrn" },
 };
 
 static const iw_handler hostapd_private[] = {
    [QCSAP_IOCTL_SETPARAM - SIOCIWFIRSTPRIV] = iw_softap_setparam,  //set priv ioctl
-   [QCSAP_IOCTL_GETPARAM - SIOCIWFIRSTPRIV] = iw_softap_getparam,  //get priv ioctl   
+   [QCSAP_IOCTL_GETPARAM - SIOCIWFIRSTPRIV] = iw_softap_getparam,  //get priv ioctl
+   [QCSAP_IOCTL_SET_CHAR_GET_NONE - SIOCIWFIRSTPRIV] =
+                                    iw_softap_setchar_getnone,
    [QCSAP_IOCTL_GET_STAWPAIE - SIOCIWFIRSTPRIV] = iw_get_genie, //get station genIE
    [QCSAP_IOCTL_STOPBSS - SIOCIWFIRSTPRIV] = iw_softap_stopbss,       // stop bss
    [QCSAP_IOCTL_VERSION - SIOCIWFIRSTPRIV] = iw_softap_version,       // get driver version
@@ -4683,6 +4842,8 @@
 
     set_bit(WMM_INIT_DONE, &pAdapter->event_flags);
 
+    pHddCtx->is_ap_mode_wow_supported =
+              sme_IsFeatureSupportedByFW(SAP_MODE_WOW);
     return status;
 
 error_wmm_init:
diff --git a/CORE/MAC/src/include/sirParams.h b/CORE/MAC/src/include/sirParams.h
index 1f64736..a6f265d 100644
--- a/CORE/MAC/src/include/sirParams.h
+++ b/CORE/MAC/src/include/sirParams.h
@@ -144,6 +144,7 @@
 #ifdef WLAN_FEATURE_ROAM_SCAN_OFFLOAD
    PER_BASED_ROAMING = 63,
 #endif
+   SAP_MODE_WOW = 64,
    //MAX_FEATURE_SUPPORTED = 128
 } placeHolderInCapBitmap;
 
diff --git a/CORE/MAC/src/pe/pmm/pmmApi.c b/CORE/MAC/src/pe/pmm/pmmApi.c
index 21ac26c..6aff9d9 100644
--- a/CORE/MAC/src/pe/pmm/pmmApi.c
+++ b/CORE/MAC/src/pe/pmm/pmmApi.c
@@ -2176,14 +2176,22 @@
         goto end;
     }
 #endif
-
-
-    if ((pMac->pmm.gPmmState != ePMM_STATE_BMPS_SLEEP) && (pMac->pmm.gPmmState != ePMM_STATE_WOWLAN))
+    /**
+     * In SAP mode BMPS is not supported skip bmps validation and
+     * send command directly.
+     */
+    if (pSessionEntry->operMode != BSS_OPERATIONAL_MODE_AP)
     {
-        pmmLog(pMac, LOGE, FL("Rcvd PMC_ENTER_WOWL_REQ in invalid Power Save state "));
-        limSendSmeRsp(pMac, eWNI_PMC_ENTER_WOWL_RSP, eSIR_SME_INVALID_PMM_STATE, 0, 0);
-        goto end;
-    }
+       if ((pMac->pmm.gPmmState != ePMM_STATE_BMPS_SLEEP) &&
+           (pMac->pmm.gPmmState != ePMM_STATE_WOWLAN))
+       {
+          pmmLog(pMac, LOGE, FL("Rcvd PMC_ENTER_WOWL_REQ in invalid Power Save state "));
+          limSendSmeRsp(pMac, eWNI_PMC_ENTER_WOWL_RSP,
+                        eSIR_SME_INVALID_PMM_STATE, 0, 0);
+          goto end;
+        }
+    } else
+         pmmLog(pMac,LOG1, FL("SAP dosn't support BMPS mode directly post wowl request"));
 
     pHalWowlParams = vos_mem_malloc(sizeof(*pHalWowlParams));
     if ( NULL == pHalWowlParams )
diff --git a/CORE/SME/src/pmc/pmc.c b/CORE/SME/src/pmc/pmc.c
index f87339e..f5539ff 100644
--- a/CORE/SME/src/pmc/pmc.c
+++ b/CORE/SME/src/pmc/pmc.c
@@ -1584,7 +1584,38 @@
 eHalStatus pmcRequestEnterWowlState(tHalHandle hHal, tpSirSmeWowlEnterParams wowlEnterParams)
 {
    tpAniSirGlobal pMac = PMAC_STRUCT(hHal);
-   pmcLog(pMac, LOG1, FL("Enter. PMC State is %d"),pMac->pmc.pmcState);
+   tpPESession pSessionEntry;
+   tANI_U8  peSessionId = 0;
+
+   pSessionEntry = peFindSessionByBssid(pMac, wowlEnterParams->bssId,
+                                         &peSessionId);
+    if (NULL == pSessionEntry)
+    {
+        pmcLog(pMac, LOGE,
+               FL("session does not exist for given BSSId"));
+
+        if (wowlEnterParams != NULL)
+          vos_mem_free(wowlEnterParams);
+
+        return eHAL_STATUS_FAILURE;
+    }
+
+    pmcLog(pMac, LOG1, FL("Enter. PMC State is %d"),pMac->pmc.pmcState);
+
+   /* Incase of SAP send command directly */
+   if ((pSessionEntry->operMode == BSS_OPERATIONAL_MODE_AP))
+   {
+       if (pmcIssueCommand(hHal, eSmeCommandEnterWowl, wowlEnterParams,
+            sizeof(tSirSmeWowlEnterParams), FALSE) != eHAL_STATUS_SUCCESS)
+         {
+            pmcLog(pMac, LOGE,
+                   FL("PMC: failure to send message eWNI_PMC_ENTER_WOWL_REQ"));
+            if (wowlEnterParams != NULL)
+               vos_mem_free(wowlEnterParams);
+            return eHAL_STATUS_FAILURE;
+         }
+       return eHAL_STATUS_SUCCESS;
+   }
 
    switch (pMac->pmc.pmcState)
    {
@@ -2069,6 +2100,9 @@
 tANI_BOOLEAN pmcProcessCommand( tpAniSirGlobal pMac, tSmeCmd *pCommand )
 {
     eHalStatus status = eHAL_STATUS_SUCCESS;
+    tpPESession pSessionEntry;
+    tANI_U8  peSessionId = 0;
+
     tANI_BOOLEAN fRemoveCmd = eANI_BOOLEAN_TRUE;
     pmcLog(pMac, LOG1, FL("PMC command is 0x%x"), pCommand->command);
     do
@@ -2255,7 +2289,13 @@
         case eSmeCommandEnterWowl:
            {
                tPmcState origState = pMac->pmc.pmcState;
-               if( ( BMPS == pMac->pmc.pmcState ) || ( WOWL == pMac->pmc.pmcState ) )
+               pSessionEntry = peFindSessionByBssid(pMac,
+                                      pCommand->u.pmcCmd.u.enterWowlInfo.bssId,
+                                      &peSessionId);
+               if (( BMPS == pMac->pmc.pmcState ) ||
+                   ( WOWL == pMac->pmc.pmcState ) ||
+                   ((pSessionEntry != NULL) &&
+                     (pSessionEntry->operMode == BSS_OPERATIONAL_MODE_AP)))
                {
                    pMac->pmc.pmcState = REQUEST_ENTER_WOWL;
                    status = pmcSendMessage(pMac, eWNI_PMC_ENTER_WOWL_REQ,
diff --git a/CORE/SME/src/pmc/pmcApi.c b/CORE/SME/src/pmc/pmcApi.c
index 406317d..e895403 100644
--- a/CORE/SME/src/pmc/pmcApi.c
+++ b/CORE/SME/src/pmc/pmcApi.c
@@ -2258,11 +2258,14 @@
         return eHAL_STATUS_FAILURE;
     }
 
-    if( !csrIsConnStateConnected(pMac, sessionId) )
+    if (!(CSR_IS_INFRA_AP(pSession->pCurRoamProfile)))
     {
-        pmcLog(pMac, LOGE, FL("Cannot add WoWL Pattern session in %d state"),
-           pSession->connectState);
-        return eHAL_STATUS_FAILURE;
+       if( !csrIsConnStateConnected(pMac, sessionId) )
+       {
+          pmcLog(pMac, LOGE, FL("Cannot add WoWL Pattern session in %d state"),
+                 pSession->connectState);
+           return eHAL_STATUS_FAILURE;
+       }
     }
 
     vos_mem_copy(pattern->bssId, pSession->connectedProfile.bssid, sizeof(tSirMacAddr));
@@ -2448,11 +2451,21 @@
        return eHAL_STATUS_FAILURE;
    }
 
-   /* Check if BMPS is enabled. */
-   if (!pMac->pmc.bmpsEnabled)
+   if (!(CSR_IS_INFRA_AP(pSession->pCurRoamProfile)))
    {
-      pmcLog(pMac, LOGE, "PMC: Cannot enter WoWL. BMPS is disabled");
-      return eHAL_STATUS_PMC_DISABLED;
+      /* Check if BMPS is enabled. */
+      if (!pMac->pmc.bmpsEnabled)
+      {
+         pmcLog(pMac, LOGE, "PMC: Cannot enter WoWL. BMPS is disabled");
+         return eHAL_STATUS_PMC_DISABLED;
+      }
+      /* Check that we are associated with single Session. */
+      if (!pmcValidateConnectState( pMac ))
+      {
+         pmcLog(pMac, LOGE, "PMC: Cannot enable WOWL. STA not associated "
+             "with an Access Point in Infra Mode with single active session");
+          return eHAL_STATUS_FAILURE;
+      }
    }
 
    /* Check if WoWL is enabled. */
@@ -2462,14 +2475,6 @@
       return eHAL_STATUS_PMC_DISABLED;
    }
 
-   /* Check that we are associated with single Session. */
-   if (!pmcValidateConnectState( pMac ))
-   {
-      pmcLog(pMac, LOGE, "PMC: Cannot enable WOWL. STA not associated "
-             "with an Access Point in Infra Mode with single active session");
-      return eHAL_STATUS_FAILURE;
-   }
-
    /* Is there a pending UAPSD request? HDD should have triggered QoS
       module to do the necessary cleanup before triggring WOWL*/
    if(pMac->pmc.uapsdSessionRequired)