wlan: Add Periodic TX Packet Offload feature through debugfs

This feature is to add support in host driver/FW to transmit specific
packet periodically without waking up host. Application can program the
content of the data packet, and the content is transparent to host
driver/FW.

Change-Id: I77f07a8de23804bfe340486105263b9257a1e6b0
CRs-Fixed: 528870
diff --git a/CORE/HDD/src/wlan_hdd_debugfs.c b/CORE/HDD/src/wlan_hdd_debugfs.c
index 0337b4a..ef1f252 100644
--- a/CORE/HDD/src/wlan_hdd_debugfs.c
+++ b/CORE/HDD/src/wlan_hdd_debugfs.c
@@ -24,6 +24,7 @@
 #include <wlan_hdd_wowl.h>
 
 #define MAX_USER_COMMAND_SIZE_WOWL_PATTERN 512
+#define MAX_USER_COMMAND_SIZE_FRAME 4096
 
 static ssize_t wcnss_wowpattern_write(struct file *file,
                const char __user *buf, size_t count, loff_t *ppos)
@@ -95,6 +96,173 @@
     return count;
 }
 
+static ssize_t wcnss_patterngen_write(struct file *file,
+               const char __user *buf, size_t count, loff_t *ppos)
+{
+    hdd_adapter_t *pAdapter = (hdd_adapter_t *)file->private_data;
+    hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
+    tSirAddPeriodicTxPtrn *addPeriodicTxPtrnParams;
+    tSirDelPeriodicTxPtrn *delPeriodicTxPtrnParams;
+
+    char *cmd, *sptr, *token;
+    v_U8_t pattern_idx = 0;
+    v_U8_t pattern_duration = 0;
+    char *pattern_buf;
+    v_U16_t pattern_len = 0;
+    v_U16_t i = 0;
+
+    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 (!hdd_connIsConnected(WLAN_HDD_GET_STATION_CTX_PTR(pAdapter)))
+    {
+        VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+                   "%s: Not in Connected state!", __func__);
+
+        return -EINVAL;
+    }
+
+    /* Get command from user */
+    if (count <= MAX_USER_COMMAND_SIZE_FRAME)
+        cmd = vos_mem_malloc(count);
+    else
+    {
+        VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+                   "%s: Command length is larger than d% bytes.",
+                   __func__, MAX_USER_COMMAND_SIZE_FRAME);
+
+        return -EINVAL;
+    }
+
+    if (copy_from_user(cmd, buf, count))
+    {
+        vos_mem_free(cmd);
+        return -EFAULT;
+    }
+    cmd[count] = '\0';
+    sptr = cmd;
+
+    /* Get pattern idx */
+    token = strsep(&sptr, " ");
+    if (!token)
+        goto failure;
+    if (kstrtou8(token, 0, &pattern_idx))
+        goto failure;
+
+    if (pattern_idx > (MAXNUM_PERIODIC_TX_PTRNS - 1))
+    {
+        VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+                   "%s: Pattern index %d is not in the range (0 ~ %d).",
+                   __func__, pattern_idx, MAXNUM_PERIODIC_TX_PTRNS - 1);
+
+        goto failure;
+    }
+
+    /* Get pattern duration */
+    token = strsep(&sptr, " ");
+    if (!token)
+        goto failure;
+    if (kstrtou8(token, 0, &pattern_duration))
+        goto failure;
+
+    /* Delete pattern using index if duration is 0*/
+    if (!pattern_duration)
+    {
+        delPeriodicTxPtrnParams =
+            vos_mem_malloc(sizeof(tSirDelPeriodicTxPtrn));
+
+        delPeriodicTxPtrnParams->ucPatternIdBitmap = 1 << pattern_idx;
+        vos_mem_copy(delPeriodicTxPtrnParams->macAddress,
+                     pAdapter->macAddressCurrent.bytes, 6);
+
+        /* Delete pattern */
+        if (eHAL_STATUS_SUCCESS != sme_DelPeriodicTxPtrn(pHddCtx->hHal,
+                                                delPeriodicTxPtrnParams))
+        {
+            VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+                       "%s: sme_DelPeriodicTxPtrn() failed!", __func__);
+
+            vos_mem_free(delPeriodicTxPtrnParams);
+            goto failure;
+        }
+
+        vos_mem_free(delPeriodicTxPtrnParams);
+        vos_mem_free(cmd);
+        return count;
+    }
+
+    /* Get pattern */
+    token = strsep(&sptr, " ");
+    if (!token)
+        goto failure;
+
+    pattern_buf = token;
+    pattern_buf[strlen(pattern_buf) - 1] = '\0';
+    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 pattern!", __func__);
+
+        goto failure;
+    }
+    else
+        pattern_len >>= 1;
+
+    if (pattern_len < 14 || pattern_len > PERIODIC_TX_PTRN_MAX_SIZE)
+    {
+        VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+                   "%s: Not an 802.3 frame!", __func__);
+
+        goto failure;
+    }
+
+    addPeriodicTxPtrnParams = vos_mem_malloc(sizeof(tSirAddPeriodicTxPtrn));
+
+    addPeriodicTxPtrnParams->ucPtrnId = pattern_idx;
+    addPeriodicTxPtrnParams->usPtrnIntervalMs = pattern_duration * 500;
+    addPeriodicTxPtrnParams->ucPtrnSize = pattern_len;
+    vos_mem_copy(addPeriodicTxPtrnParams->macAddress,
+                 pAdapter->macAddressCurrent.bytes, 6);
+
+    /* Extract the pattern */
+    for(i = 0; i < addPeriodicTxPtrnParams->ucPtrnSize; i++)
+    {
+        addPeriodicTxPtrnParams->ucPattern[i] =
+        (hdd_parse_hex(pattern_buf[0]) << 4) + hdd_parse_hex(pattern_buf[1]);
+
+        /* Skip to next byte */
+        pattern_buf += 2;
+    }
+
+    /* Add pattern */
+    if (eHAL_STATUS_SUCCESS != sme_AddPeriodicTxPtrn(pHddCtx->hHal,
+                                            addPeriodicTxPtrnParams))
+    {
+        VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+                   "%s: sme_AddPeriodicTxPtrn() failed!", __func__);
+
+        vos_mem_free(addPeriodicTxPtrnParams);
+        goto failure;
+    }
+
+    vos_mem_free(addPeriodicTxPtrnParams);
+    vos_mem_free(cmd);
+    return count;
+
+failure:
+    vos_mem_free(cmd);
+    return EINVAL;
+}
+
 static int wcnss_debugfs_open(struct inode *inode, struct file *file)
 {
     if (inode->i_private)
@@ -112,6 +280,13 @@
     .llseek = default_llseek,
 };
 
+static const struct file_operations fops_patterngen = {
+    .write = wcnss_patterngen_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);
@@ -124,6 +299,10 @@
         pHddCtx->debugfs_phy, pAdapter, &fops_wowpattern))
         return VOS_STATUS_E_FAILURE;
 
+    if (NULL == debugfs_create_file("pattern_gen", S_IRUSR | S_IWUSR,
+        pHddCtx->debugfs_phy, pAdapter, &fops_patterngen))
+        return VOS_STATUS_E_FAILURE;
+
     return VOS_STATUS_SUCCESS;
 }
 
@@ -131,5 +310,5 @@
 {
     debugfs_remove_recursive(pHddCtx->debugfs_phy);
 }
-#endif //#ifdef WLAN_OPEN_SOURCE
+#endif /* #ifdef WLAN_OPEN_SOURCE */