/* SPDX-License-Identifier: GPL-2.0 */ #include #include #include #include #include #include "sprdwl.h" #include "edma_test.h" #include "wl_intf.h" #define PCIE_CHANNEL_PAIR (8) #define RUN 1 #define STOP 0 #define RAW_DATA_LEN 10 #define WL_SEQ_NUM_MASK 0xfff /* allow 12 bit */ #define WL_SEQ_NUM_SHIFT 0 #define WL_SEQ_SET_NUM(x, val) ((x) = \ ((x) & ~(WL_SEQ_NUM_MASK << WL_SEQ_NUM_SHIFT)) | \ (((val) & WL_SEQ_NUM_MASK) << WL_SEQ_NUM_SHIFT)) #define WL_SEQ_GET_NUM(x) (((x) >> WL_SEQ_NUM_SHIFT) & \ WL_SEQ_NUM_MASK) extern struct sprdwl_intf_ops g_intf_ops; extern int if_tx_one(struct sprdwl_intf *intf, unsigned char *data, int len, int chn); unsigned long loop; static struct dentry *chn_tx_dentry[8]; static unsigned int chn_tx_num[8]; static unsigned int chn_tx_success[8]; static unsigned int chn_tx_fail[8]; struct edma_test_cmd_header { u16 subcmd; u16 len; u8 data[0]; } __packed; struct task_struct *task_array[PCIE_CHANNEL_PAIR]; int task_status; void edma_transceive_test_init(void) { int i, err; wl_err("Enter %s\n", __func__); for (i = 0; i < PCIE_CHANNEL_PAIR; i++) { task_array[i] = kthread_create(edma_transceive_test_exec, NULL, "edma_transceive_task"); if (IS_ERR(task_array[i])) { err = PTR_ERR(task_array[i]); wl_err("%s, Fail, thread_num=%d,error_num=%d.", __func__, i, err); } } wl_err("Exit %s\n", __func__); } void edma_transceive_test_deinit(void) { int i; wl_err("Enter %s\n", __func__); for (i = 0; i < PCIE_CHANNEL_PAIR; i++) { if (!IS_ERR_OR_NULL(task_array[i])) { wl_err(" %s, Destory thread, thread_num=%d,\n", __func__, i); kthread_stop(task_array[i]); task_array[i] = NULL; } } wl_err("Exit %s\n", __func__); } int do_tx(int channel) { int ret = 0; struct sprdwl_msg_buf *msg; struct edma_test_cmd_header *p; unsigned char raw_data[RAW_DATA_LEN]; struct sprdwl_intf *intf = (struct sprdwl_intf *)g_intf_ops.intf; struct sprdwl_priv *priv = intf->priv; memset(raw_data, channel, RAW_DATA_LEN); msg = sprdwl_cmd_getbuf(priv, sizeof(*p) + RAW_DATA_LEN, 0, SPRDWL_HEAD_RSP, channel); if (!msg) { wl_err(" %s, No mem for msg,\n", __func__); return -ENOMEM; } p = (struct edma_test_cmd_header *)msg->data; p->subcmd = channel; p->len = RAW_DATA_LEN; memcpy(p->data, raw_data, RAW_DATA_LEN); sprdwl_queue_msg_buf(msg, msg->msglist); ret = if_tx_one(intf, msg->tran_data, msg->len, channel); if (ret) { wl_err("%s send cmd failed (ret=%d)\n", __func__, ret); kfree(msg->tran_data); msg->tran_data = NULL; } sprdwl_dequeue_msg_buf(msg, msg->msglist); return 0; } #define SPRDWL_SDIO_DEBUG_BUFLEN 128 static ssize_t pcie_read_info(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { size_t ret = 0; unsigned int buflen, len; unsigned char *buf; unsigned long chn = (unsigned long)file->private_data; buflen = SPRDWL_SDIO_DEBUG_BUFLEN; buf = kzalloc(buflen, GFP_KERNEL); if (!buf) return -ENOMEM; len = 0; len += scnprintf(buf, buflen, "chn: %d, num: %d, success: %d, fail: %d\n", ((int)chn)*2, chn_tx_num[chn], chn_tx_success[chn], chn_tx_fail[chn]); if (len > buflen) len = buflen; ret = simple_read_from_buffer(user_buf, count, ppos, buf, len); kfree(buf); return ret; } static const struct file_operations pcie_debug_fops = { .read = pcie_read_info, .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, }; const unsigned char *tx_name[] = { "edma_transceive_0", "edma_transceive_2", "edma_transceive_4", "edma_transceive_6", "edma_transceive_8", "edma_transceive_10", "edma_transceive_12", "edma_transceive_14", }; void edma_transceive_test_run(int pairs) { int i, err; struct sprdwl_intf *intf = (struct sprdwl_intf *)g_intf_ops.intf; struct sprdwl_priv *priv = intf->priv; wl_info("Enter %s\n", __func__); memset(task_array, 0, sizeof(struct task_struct *)*PCIE_CHANNEL_PAIR); memset(chn_tx_dentry, 0, sizeof(struct dentry *)*PCIE_CHANNEL_PAIR); loop = 0; for (i = 0; i < pairs; i++) { chn_tx_num[i] = 0; chn_tx_success[i] = 0; chn_tx_fail[i] = 0; task_array[i] = kthread_create(edma_transceive_test_exec, (unsigned long *)loop, "edma_transceive_%d", i); chn_tx_dentry[i] = debugfs_create_file(tx_name[i], S_IRUSR, priv->debugfs, (unsigned long *)loop, &pcie_debug_fops); loop++; if (IS_ERR(task_array[i])) { err = PTR_ERR(task_array[i]); wl_err("%s, Fail, thread_num=%d,error_num=%d.", __func__, i, err); } else { wl_info(" %s, Start thread, thread_num=%d,\n", __func__, i); wake_up_process(task_array[i]); } } wl_info("Exit %s\n", __func__); } void edma_transceive_test_stop(void) { int i; wl_info("Enter %s\n", __func__); for (i = 0; i < PCIE_CHANNEL_PAIR; i++) { if (!IS_ERR_OR_NULL(task_array[i])) { wl_err(" %s, Stop thread, thread_num=%d,\n", __func__, i); kthread_stop(task_array[i]); task_array[i] = NULL; } if (!IS_ERR_OR_NULL(chn_tx_dentry[i])) { wl_err(" %s, deinit tx dentry: %d\n", __func__, i); debugfs_remove(chn_tx_dentry[i]); chn_tx_dentry[i] = NULL; } } wl_info("Exit %s\n", __func__); } int edma_transceive_test_exec(void *args) { unsigned long chn = (unsigned long)args; int ret = 0; //struct sched_param param; wl_err("Enter %s\n", __func__); while (1) { if (kthread_should_stop()) { wl_err(" %s, Exec thread, stopped.\n", __func__); break; } __set_current_state(TASK_RUNNING); wl_info(" %s, Exec thread, thread_num=%ld.\n", __func__, chn); /* do tx */ chn_tx_num[chn]++; ret = do_tx((int)chn * 2); if (ret) chn_tx_fail[chn]++; else chn_tx_success[chn]++; msleep(1); } wl_err("Exit %s\n", __func__); return 0; } char * __strtok(char **string, const char *delimiters, char *tokdelim) { unsigned char *str; unsigned long map[8]; int count; char *nextoken; if (tokdelim != NULL) { /* Prime the token delimiter */ *tokdelim = '\0'; } /* Clear control map */ for (count = 0; count < 8; count++) map[count] = 0; /* Set bits in delimiter table */ do { map[*delimiters >> 5] |= (1 << (*delimiters & 31)); } while (*delimiters++); str = (unsigned char *)*string; /* Find beginning of token (skip over leading delimiters). Note that * there is no token iff this loop sets str to point to the terminal * null (*str == '\0') */ while (((map[*str >> 5] & (1 << (*str & 31))) && *str) || (*str == ' ')) str++; nextoken = (char *)str; /* Find the end of the token. If it is not the end of the string, * put a null there. */ for (; *str; str++) { if (map[*str >> 5] & (1 << (*str & 31))) { if (tokdelim != NULL) *tokdelim = *str; *str++ = '\0'; break; } } *string = (char *)str; /* Determine if a token has been found. */ if (nextoken == (char *)str) return NULL; else return nextoken; } void edma_transceive_test_trigger(char *cmd) { long oper, pairs; char *delim_1 = "="; char *delim_2 = ","; __strtok(&cmd, delim_1, 0); /* oper = (int)simple_strtol(cmd, NULL, 0); */ if (kstrtol(cmd, 10, &oper) < 0) { wl_err("%s, %s is invalid\n", __func__, cmd); return; } __strtok(&cmd, delim_2, 0); /* pairs = (int)simple_strtol(cmd, NULL, 0); */ if (kstrtol(cmd, 10, &pairs) < 0) { wl_err("%s, %s is invalid\n", __func__, cmd); return; } wl_err("Enter %s\n", __func__); if (pairs <= 0 || pairs > PCIE_CHANNEL_PAIR) { wl_err("%s, Invalid chn pairs(%ld), use default val(8)\n", __func__, pairs); pairs = PCIE_CHANNEL_PAIR; } switch (oper) { case RUN: wl_err("%s, Run task\n", __func__); if (task_status == RUN) { wl_err("%s, Already running\n", __func__); return; } edma_transceive_test_run((int)pairs); task_status = RUN; break; case STOP: wl_err("%s, Stop task\n", __func__); if (task_status == STOP) { wl_err("%s, Already stopped\n", __func__); return; } edma_transceive_test_stop(); task_status = STOP; break; default: wl_err("%s, Unknown trigger\n", __func__); break; } wl_err("Exit %s\n", __func__); }