wlan: Add debugfs interface support for WoWL feature

Add debugfs interface and corresponding support for WoWL feature to WLAN
driver. The feature supports up to 16 WOWL patterns.

CRs-Fixed: 524374
Change-Id: I8a426d06d1f00bed73339c917a3ed28240b6540a
diff --git a/CORE/HDD/inc/wlan_hdd_debugfs.h b/CORE/HDD/inc/wlan_hdd_debugfs.h
new file mode 100644
index 0000000..7b912c8
--- /dev/null
+++ b/CORE/HDD/inc/wlan_hdd_debugfs.h
@@ -0,0 +1,24 @@
+/*
+* Copyright (c) 2013 Qualcomm Atheros, Inc.
+* All Rights Reserved.
+* Qualcomm Atheros Confidential and Proprietary.
+*/
+
+#ifndef _WLAN_HDD_DEBUGFS_H
+#define _WLAN_HDD_DEBUGFS_H
+
+#ifdef WLAN_OPEN_SOURCE
+VOS_STATUS hdd_debugfs_init(hdd_adapter_t *pAdapter);
+void hdd_debugfs_exit(hdd_context_t *pHddCtx);
+#else
+inline VOS_STATUS hdd_debugfs_init(hdd_adapter_t *pAdapter)
+{
+    return VOS_STATUS_SUCCESS;
+}
+inline void hdd_debugfs_exit(hdd_context_t *pHddCtx)
+{
+}
+#endif
+
+#endif /* #ifndef _WLAN_HDD_DEBUGFS_H */
+
diff --git a/CORE/HDD/inc/wlan_hdd_main.h b/CORE/HDD/inc/wlan_hdd_main.h
index 6bc593f..88a7e41 100644
--- a/CORE/HDD/inc/wlan_hdd_main.h
+++ b/CORE/HDD/inc/wlan_hdd_main.h
@@ -1026,6 +1026,9 @@
 #ifdef FEATURE_WLAN_LPHB
     lphbEnableStruct  lphbEnableReq;
 #endif /* FEATURE_WLAN_LPHB */
+
+    /* debugfs entry */
+    struct dentry *debugfs_phy;
 };
 
 
diff --git a/CORE/HDD/inc/wlan_hdd_wowl.h b/CORE/HDD/inc/wlan_hdd_wowl.h
index d2d672d..82e3ff8 100644
--- a/CORE/HDD/inc/wlan_hdd_wowl.h
+++ b/CORE/HDD/inc/wlan_hdd_wowl.h
@@ -152,6 +152,32 @@
 v_BOOL_t hdd_del_wowl_ptrn (hdd_adapter_t *pAdapter, const char * ptrn);
 
 /**============================================================================
+  @brief hdd_add_wowl_ptrn_debugfs() - Function which will add a WoW pattern
+  to be used when PBM filtering is enabled and MP filtering is disabled
+
+  @param pAdapter       : [in] pointer to the adapter
+         pattern_idx    : [in] index of the pattern to be added
+         pattern_offset : [in] offset of the pattern in the frame payload
+         pattern_buf    : [in] pointer to the pattern hex string to be added
+
+  @return               : FALSE if any errors encountered
+                        : TRUE otherwise
+  ===========================================================================*/
+v_BOOL_t hdd_add_wowl_ptrn_debugfs(hdd_adapter_t *pAdapter, v_U8_t pattern_idx,
+                                   v_U8_t pattern_offset, char *pattern_buf);
+
+/**============================================================================
+  @brief hdd_del_wowl_ptrn_debugfs() - Function which will remove a WoW pattern
+
+  @param pAdapter    : [in] pointer to the adapter
+         pattern_idx : [in] index of the pattern to be removed
+
+  @return            : FALSE if any errors encountered
+                     : TRUE otherwise
+  ===========================================================================*/
+v_BOOL_t hdd_del_wowl_ptrn_debugfs(hdd_adapter_t *pAdapter, v_U8_t pattern_idx);
+
+/**============================================================================
   @brief hdd_enter_wowl() - Function which will enable WoWL. Atleast one
   of MP and PBM must be enabled
 
diff --git a/CORE/HDD/src/wlan_hdd_debugfs.c b/CORE/HDD/src/wlan_hdd_debugfs.c
new file mode 100644
index 0000000..0337b4a
--- /dev/null
+++ b/CORE/HDD/src/wlan_hdd_debugfs.c
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ *
+ * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
+ *
+ *
+ * Permission to use, copy, modify, and/or distribute this software for
+ * any purpose with or without fee is hereby granted, provided that the
+ * above copyright notice and this permission notice appear in all
+ * copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
+ * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifdef WLAN_OPEN_SOURCE
+#include <wlan_hdd_includes.h>
+#include <wlan_hdd_wowl.h>
+
+#define MAX_USER_COMMAND_SIZE_WOWL_PATTERN 512
+
+static ssize_t wcnss_wowpattern_write(struct file *file,
+               const char __user *buf, size_t count, loff_t *ppos)
+{
+    hdd_adapter_t *pAdapter = (hdd_adapter_t *)file->private_data;
+
+    char cmd[MAX_USER_COMMAND_SIZE_WOWL_PATTERN];
+    char *sptr, *token;
+    v_U8_t pattern_idx = 0;
+    v_U8_t pattern_offset = 0;
+    char *pattern_buf;
+
+    if ((NULL == pAdapter) || (WLAN_HDD_ADAPTER_MAGIC != pAdapter->magic))
+    {
+        VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
+                   "%s: Invalid adapter or adapter has invalid magic.",
+                   __func__);
+
+        return -EINVAL;
+    }
+
+    if (count > MAX_USER_COMMAND_SIZE_WOWL_PATTERN)
+    {
+        VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+                   "%s: Command length is larger than %d bytes.",
+                   __func__, MAX_USER_COMMAND_SIZE_WOWL_PATTERN);
+
+        return -EINVAL;
+    }
+
+    /* Get command from user */
+    if (copy_from_user(cmd, buf, count))
+        return -EFAULT;
+    cmd[count] = '\0';
+    sptr = cmd;
+
+    /* Get pattern idx */
+    token = strsep(&sptr, " ");
+    if (!token)
+        return -EINVAL;
+
+    if (kstrtou8(token, 0, &pattern_idx))
+        return -EINVAL;
+
+    /* Get pattern offset */
+    token = strsep(&sptr, " ");
+
+    /* Delete pattern if no further argument */
+    if (!token) {
+        hdd_del_wowl_ptrn_debugfs(pAdapter, pattern_idx);
+
+        return count;
+    }
+
+    if (kstrtou8(token, 0, &pattern_offset))
+        return -EINVAL;
+
+    /* Get pattern */
+    token = strsep(&sptr, " ");
+    if (!token)
+        return -EINVAL;
+
+    pattern_buf = token;
+    pattern_buf[strlen(pattern_buf) - 1] = '\0';
+
+    hdd_add_wowl_ptrn_debugfs(pAdapter, pattern_idx, pattern_offset,
+                              pattern_buf);
+
+    return count;
+}
+
+static int wcnss_debugfs_open(struct inode *inode, struct file *file)
+{
+    if (inode->i_private)
+    {
+        file->private_data = inode->i_private;
+    }
+
+    return 0;
+}
+
+static const struct file_operations fops_wowpattern = {
+    .write = wcnss_wowpattern_write,
+    .open = wcnss_debugfs_open,
+    .owner = THIS_MODULE,
+    .llseek = default_llseek,
+};
+
+VOS_STATUS hdd_debugfs_init(hdd_adapter_t *pAdapter)
+{
+    hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
+    pHddCtx->debugfs_phy = debugfs_create_dir("wlan_wcnss", 0);
+
+    if (NULL == pHddCtx->debugfs_phy)
+        return VOS_STATUS_E_FAILURE;
+
+    if (NULL == debugfs_create_file("wow_pattern", S_IRUSR | S_IWUSR,
+        pHddCtx->debugfs_phy, pAdapter, &fops_wowpattern))
+        return VOS_STATUS_E_FAILURE;
+
+    return VOS_STATUS_SUCCESS;
+}
+
+void hdd_debugfs_exit(hdd_context_t *pHddCtx)
+{
+    debugfs_remove_recursive(pHddCtx->debugfs_phy);
+}
+#endif //#ifdef WLAN_OPEN_SOURCE
+
diff --git a/CORE/HDD/src/wlan_hdd_main.c b/CORE/HDD/src/wlan_hdd_main.c
index 1ea73af..b868e32 100644
--- a/CORE/HDD/src/wlan_hdd_main.c
+++ b/CORE/HDD/src/wlan_hdd_main.c
@@ -123,6 +123,7 @@
 #ifdef FEATURE_WLAN_TDLS
 #include "wlan_hdd_tdls.h"
 #endif
+#include "wlan_hdd_debugfs.h"
 
 #ifdef MODULE
 #define WLAN_MODULE_NAME  module_name(THIS_MODULE)
@@ -5048,6 +5049,8 @@
       }
    }
 
+   hdd_debugfs_exit(pHddCtx);
+
    // Unregister the Net Device Notifier
    unregister_netdevice_notifier(&hdd_netdev_notifier);
    
@@ -5911,6 +5914,14 @@
 #endif //WLAN_BTAMP_FEATURE
    }
 
+   /* Open debugfs interface */
+   if (VOS_STATUS_SUCCESS != hdd_debugfs_init(pAdapter))
+   {
+      VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
+                 "%s: hdd_debugfs_init failed!", __func__);
+      goto err_close_debugfs;
+   }
+
    /* Register TM level change handler function to the platform */
    status = hddDevTmRegisterNotifyCallback(pHddCtx);
    if ( !VOS_IS_STATUS_SUCCESS( status ) )
@@ -6018,6 +6029,9 @@
    hddDevTmUnregisterNotifyCallback(pHddCtx);
    hddDeregisterPmOps(pHddCtx);
 
+err_close_debugfs:
+   hdd_debugfs_exit(pHddCtx);
+
 #ifdef WLAN_BTAMP_FEATURE
 err_bap_stop:
   WLANBAP_Stop(pVosContext);
diff --git a/CORE/HDD/src/wlan_hdd_wowl.c b/CORE/HDD/src/wlan_hdd_wowl.c
index 05d2f5d..6299fae 100644
--- a/CORE/HDD/src/wlan_hdd_wowl.c
+++ b/CORE/HDD/src/wlan_hdd_wowl.c
@@ -61,7 +61,7 @@
 
 #define WOWL_PTRN_MAX_SIZE          128
 #define WOWL_PTRN_MASK_MAX_SIZE      16
-#define WOWL_MAX_PTRNS_ALLOWED        8
+#define WOWL_MAX_PTRNS_ALLOWED       16
 #define WOWL_INTER_PTRN_TOKENIZER   ';'
 #define WOWL_INTRA_PTRN_TOKENIZER   ':'
 
@@ -69,7 +69,9 @@
  * Type Declarations
  * -------------------------------------------------------------------------*/
 
-char *g_hdd_wowl_ptrns[WOWL_MAX_PTRNS_ALLOWED]; //Patterns 0-7 
+static char *g_hdd_wowl_ptrns[WOWL_MAX_PTRNS_ALLOWED]; //Patterns 0-15
+static v_BOOL_t g_hdd_wowl_ptrns_debugfs[WOWL_MAX_PTRNS_ALLOWED] = {0};
+static v_U8_t g_hdd_wowl_ptrns_count = 0;
 
 int hdd_parse_hex(unsigned char c)
 {
@@ -346,6 +348,181 @@
 }
 
 /**============================================================================
+  @brief hdd_add_wowl_ptrn_debugfs() - Function which will add a WoW pattern
+  to be used when PBM filtering is enabled and MP filtering is disabled
+
+  @param pAdapter       : [in] pointer to the adapter
+         pattern_idx    : [in] index of the pattern to be added
+         pattern_offset : [in] offset of the pattern in the frame payload
+         pattern_buf    : [in] pointer to the pattern hex string to be added
+
+  @return               : FALSE if any errors encountered
+                        : TRUE otherwise
+  ===========================================================================*/
+v_BOOL_t hdd_add_wowl_ptrn_debugfs(hdd_adapter_t *pAdapter, v_U8_t pattern_idx,
+                                   v_U8_t pattern_offset, char *pattern_buf)
+{
+  tSirWowlAddBcastPtrn localPattern;
+  eHalStatus halStatus;
+  tHalHandle hHal = WLAN_HDD_GET_HAL_CTX(pAdapter);
+  v_U8_t sessionId = pAdapter->sessionId;
+  v_U16_t pattern_len, i;
+
+  if (pattern_idx > (WOWL_MAX_PTRNS_ALLOWED - 1))
+  {
+    VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+               "%s: WoW pattern index %d is out of range (0 ~ %d).",
+               __func__, pattern_idx, WOWL_MAX_PTRNS_ALLOWED - 1);
+
+    return VOS_FALSE;
+  }
+
+  pattern_len = strlen(pattern_buf);
+
+  /* Since the pattern is a hex string, 2 characters represent 1 byte. */
+  if (pattern_len % 2)
+  {
+    VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+               "%s: Malformed WoW pattern!", __func__);
+
+    return VOS_FALSE;
+  }
+  else
+    pattern_len >>= 1;
+
+  if (!pattern_len || pattern_len > WOWL_PTRN_MAX_SIZE)
+  {
+    VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+               "%s: WoW pattern length %d is out of range (1 ~ %d).",
+               __func__, pattern_len, WOWL_PTRN_MAX_SIZE);
+
+    return VOS_FALSE;
+  }
+
+  localPattern.ucPatternId = pattern_idx;
+  localPattern.ucPatternByteOffset = pattern_offset;
+  localPattern.ucPatternSize = pattern_len;
+
+  /* Extract the pattern */
+  for (i = 0; i < localPattern.ucPatternSize; i++)
+  {
+    localPattern.ucPattern[i] =
+      (hdd_parse_hex(pattern_buf[0]) << 4) + hdd_parse_hex(pattern_buf[1]);
+
+    /* Skip to next byte */
+    pattern_buf += 2;
+  }
+
+  /* Generate bytemask by pattern length */
+  for (i = 0; i < (pattern_len >> 3); i++)
+    localPattern.ucPatternMask[i] = 0xFF;
+
+  localPattern.ucPatternMaskSize = i;
+
+  if (pattern_len % 8)
+  {
+    localPattern.ucPatternMask[i] = (1 << (pattern_len % 8)) - 1;
+    localPattern.ucPatternMaskSize += 1;
+  }
+
+  /* Register the pattern downstream */
+  halStatus = sme_WowlAddBcastPattern(hHal, &localPattern, sessionId);
+
+  if (!HAL_STATUS_SUCCESS(halStatus))
+  {
+    VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+               "%s: sme_WowlAddBcastPattern failed with error code (%ld).",
+               __func__, halStatus);
+
+    return VOS_FALSE;
+  }
+
+  /* Enable WoW immediately after add a pattern. By default,
+   * disable magic packet mode and enable pattern byte matching mode. */
+  if (!hdd_enter_wowl(pAdapter, 0, 1))
+  {
+    VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+               "%s: hdd_enter_wowl failed!", __func__);
+
+    return VOS_FALSE;
+  }
+
+  /* All is good. */
+  if (!g_hdd_wowl_ptrns_debugfs[pattern_idx])
+  {
+    g_hdd_wowl_ptrns_debugfs[pattern_idx] = 1;
+    g_hdd_wowl_ptrns_count++;
+  }
+
+  dump_hdd_wowl_ptrn(&localPattern);
+
+  return VOS_TRUE;
+}
+
+/**============================================================================
+  @brief hdd_del_wowl_ptrn_debugfs() - Function which will remove a WoW pattern
+
+  @param pAdapter    : [in] pointer to the adapter
+         pattern_idx : [in] index of the pattern to be removed
+
+  @return            : FALSE if any errors encountered
+                     : TRUE otherwise
+  ===========================================================================*/
+v_BOOL_t hdd_del_wowl_ptrn_debugfs(hdd_adapter_t *pAdapter, v_U8_t pattern_idx)
+{
+  tSirWowlDelBcastPtrn delPattern;
+  tHalHandle hHal = WLAN_HDD_GET_HAL_CTX(pAdapter);
+  eHalStatus halStatus;
+  v_U8_t sessionId = pAdapter->sessionId;
+
+  if (pattern_idx > (WOWL_MAX_PTRNS_ALLOWED - 1))
+  {
+    VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+               "%s: WoW pattern index %d is not in the range (0 ~ %d).",
+               __func__, pattern_idx, WOWL_MAX_PTRNS_ALLOWED - 1);
+
+    return VOS_FALSE;
+  }
+
+  if (!g_hdd_wowl_ptrns_debugfs[pattern_idx])
+  {
+    VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+               "%s: WoW pattern %d is not in the table.",
+               __func__, pattern_idx);
+
+    return VOS_FALSE;
+  }
+
+  delPattern.ucPatternId = pattern_idx;
+  halStatus = sme_WowlDelBcastPattern(hHal, &delPattern, sessionId);
+
+  if (!HAL_STATUS_SUCCESS(halStatus))
+  {
+    VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+               "%s: sme_WowlDelBcastPattern failed with error code (%ld).",
+               __func__, halStatus);
+
+    return VOS_FALSE;
+  }
+
+  g_hdd_wowl_ptrns_debugfs[pattern_idx] = 0;
+  g_hdd_wowl_ptrns_count--;
+
+  if (g_hdd_wowl_ptrns_count == 0)
+  {
+    if (!hdd_exit_wowl(pAdapter))
+    {
+      VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+                 "%s: hdd_exit_wowl failed!", __func__);
+
+      return VOS_FALSE;
+    }
+  }
+
+  return VOS_TRUE;
+}
+
+/**============================================================================
   @brief hdd_enter_wowl() - Function which will enable WoWL. Atleast one
   of MP and PBM must be enabled
 
diff --git a/Kbuild b/Kbuild
index 1167e20..301645c 100644
--- a/Kbuild
+++ b/Kbuild
@@ -118,14 +118,15 @@
 HDD_OBJS := 	$(HDD_SRC_DIR)/bap_hdd_main.o \
 		$(HDD_SRC_DIR)/wlan_hdd_assoc.o \
 		$(HDD_SRC_DIR)/wlan_hdd_cfg.o \
+		$(HDD_SRC_DIR)/wlan_hdd_debugfs.o \
 		$(HDD_SRC_DIR)/wlan_hdd_dev_pwr.o \
 		$(HDD_SRC_DIR)/wlan_hdd_dp_utils.o \
 		$(HDD_SRC_DIR)/wlan_hdd_early_suspend.o \
 		$(HDD_SRC_DIR)/wlan_hdd_ftm.o \
 		$(HDD_SRC_DIR)/wlan_hdd_hostapd.o \
-		$(HDD_SRC_DIR)/wlan_hdd_oemdata.o \
 		$(HDD_SRC_DIR)/wlan_hdd_main.o \
 		$(HDD_SRC_DIR)/wlan_hdd_mib.o \
+		$(HDD_SRC_DIR)/wlan_hdd_oemdata.o \
 		$(HDD_SRC_DIR)/wlan_hdd_scan.o \
 		$(HDD_SRC_DIR)/wlan_hdd_softap_tx_rx.o \
 		$(HDD_SRC_DIR)/wlan_hdd_tx_rx.o \