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