/*
|
* Copyright (C) 2016-2018 Spreadtrum Communications Inc.
|
* This software is licensed under the terms of the GNU General Public
|
* License version 2, as published by the Free Software Foundation, and
|
* may be copied, distributed, and modified under those terms.
|
|
* This program is distributed in the hope that it will be useful,
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* GNU General Public License for more details.
|
*/
|
|
#include <wcn_bus.h>
|
|
#include "edma_engine.h"
|
#include "mchn.h"
|
#include "pcie.h"
|
|
#define TX_CHN (0)
|
#define RX_CHN (1)
|
|
struct cfg_e {
|
int pool_size;
|
int buf_size;
|
int num;
|
int chn[8][2];
|
};
|
|
struct test_link {
|
struct mbuf_t *head;
|
struct mbuf_t *tail;
|
int num;
|
int chn;
|
};
|
|
struct loopback {
|
struct cfg_e cfg;
|
int seq;
|
int loop;
|
struct mchn_ops_t ops[16];
|
struct test_link link[8][2];
|
|
};
|
struct loopback g_lo;
|
|
int cfg[] = {3, 1024, 2, 0, 1, 2, 3};
|
|
static struct mchn_ops_t *lo_ops(int chn)
|
{
|
struct loopback *lo = &g_lo;
|
|
return &lo->ops[chn];
|
}
|
|
static int lo_index(int chn)
|
{
|
int i;
|
struct loopback *lo = &g_lo;
|
|
for (i = 0; i < lo->cfg.num; i++) {
|
if ((chn == lo->link[i][TX_CHN].chn) ||
|
(chn == lo->link[i][RX_CHN].chn))
|
return i;
|
|
}
|
PCIE_INFO("%s(%d) err\n", __func__, chn);
|
while (1)
|
;
|
|
return -1;
|
}
|
|
static int lo_buf_alloc(int chn, int size, int num)
|
{
|
int ret, i;
|
struct dma_buf dm = {0};
|
struct mbuf_t *mbuf, *head, *tail;
|
struct edma_info *edma = edma_info();
|
|
ret = mbuf_link_alloc(chn, &head, &tail, &num);
|
if (ret != 0)
|
return -1;
|
for (i = 0, mbuf = head; i < num; i++) {
|
ret = dmalloc(edma->pcie_info, &dm, size);
|
if (ret != 0)
|
return -1;
|
mbuf->buf = (unsigned char *)(dm.vir);
|
mbuf->phy = (unsigned long)(dm.phy);
|
mbuf->len = dm.size;
|
memset(mbuf->buf, (unsigned char)(i+1), mbuf->len);
|
mbuf = mbuf->next;
|
}
|
ret = mbuf_link_free(chn, head, tail, num);
|
|
return ret;
|
}
|
|
static int lo_tx_pop(int chn, struct mbuf_t *head, struct mbuf_t *tail,
|
int num)
|
{
|
struct loopback *lo = &g_lo;
|
struct test_link *tx_link = &(lo->link[lo_index(chn)][TX_CHN]);
|
|
if (tx_link->num == 0) {
|
tx_link->head = head;
|
tx_link->tail = tail;
|
tx_link->num = num;
|
} else {
|
tx_link->tail->next = head;
|
tx_link->tail = tail;
|
tx_link->num += num;
|
}
|
|
return 0;
|
}
|
|
static int lo_push(int chn)
|
{
|
int num, ret, i, j;
|
struct mchn_ops_t *ops = lo_ops(chn);
|
struct mbuf_t *head, *tail, *mbuf;
|
struct loopback *lo = &g_lo;
|
|
num = ops->pool_size;
|
ret = mbuf_link_alloc(chn, &head, &tail, &num);
|
for (i = 0, mbuf = head; i < num; i++) {
|
for (j = 0; j < mbuf->len/4; j++)
|
*(int *)(mbuf->buf + j * 4) = lo->seq;
|
|
lo->seq++;
|
mbuf = mbuf->next;
|
}
|
ret = mchn_push_link(chn, head, tail, num);
|
|
return ret;
|
}
|
|
static int lo_rx_push(int chn, struct mbuf_t **head, struct mbuf_t **tail,
|
int *num)
|
{
|
int ret = mbuf_link_alloc(chn, head, tail, num);
|
|
return ret;
|
}
|
|
static int lo_rx_pop(int chn, struct mbuf_t *head, struct mbuf_t *tail,
|
int num)
|
{
|
int i, pos = 0;
|
unsigned char string[128];
|
struct mbuf_t *tx_mbuf, *rx_mbuf;
|
struct loopback *lo = &g_lo;
|
struct mchn_ops_t *ops = lo_ops(chn);
|
struct test_link *tx_link = &(lo->link[lo_index(chn)][TX_CHN]);
|
struct test_link *rx_link = &(lo->link[lo_index(chn)][RX_CHN]);
|
|
if (rx_link->num == 0) {
|
rx_link->head = head;
|
rx_link->tail = tail;
|
rx_link->num = num;
|
rx_link->chn = chn;
|
} else {
|
rx_link->tail->next = head;
|
rx_link->tail = (void *)tail;
|
rx_link->num += num;
|
}
|
if (rx_link->num == ops->pool_size) {
|
if (tx_link->num != rx_link->num) {
|
PCIE_ERR("%s line:%d err\n", __func__, __LINE__);
|
while (1)
|
;
|
|
return -1;
|
}
|
pos = sprintf(string + pos, "lo(%d,%d){",
|
tx_link->chn, rx_link->chn);
|
for (i = 0, tx_mbuf = tx_link->head, rx_mbuf = rx_link->head;
|
i < tx_link->num; i++) {
|
if (memcmp(tx_mbuf->buf, rx_mbuf->buf, 1024) != 0) {
|
PCIE_ERR("%s line:%d err\n", __func__,
|
__LINE__);
|
while (1)
|
;
|
}
|
pos += sprintf(string+pos, "%d ",
|
*(int *)(tx_mbuf->buf));
|
tx_mbuf = tx_mbuf->next;
|
rx_mbuf = rx_mbuf->next;
|
}
|
PCIE_INFO("%s}\n", string);
|
mbuf_link_free(rx_link->chn, rx_link->head, rx_link->tail,
|
rx_link->num);
|
mbuf_link_free(tx_link->chn, tx_link->head, tx_link->tail,
|
tx_link->num);
|
|
rx_link->num = 0;
|
rx_link->head = rx_link->tail = NULL;
|
|
tx_link->num = 0;
|
tx_link->head = tx_link->tail = NULL;
|
|
if (lo->loop)
|
lo_push(tx_link->chn);
|
}
|
|
return 0;
|
}
|
|
static int lo_tx_complete(int chn, int timeout)
|
{
|
return 0;
|
}
|
|
int lo_init(void)
|
{
|
int i, tx_chn, rx_chn;
|
struct mchn_ops_t *ops;
|
struct loopback *lo = &g_lo;
|
|
PCIE_INFO("[+]%s\n", __func__);
|
memset(lo, 0x00, sizeof(struct loopback));
|
for (i = 0; i < 8; i++) {
|
lo->link[i][0].chn = -1;
|
lo->link[i][1].chn = -1;
|
}
|
memcpy((unsigned char *)(&lo->cfg), (unsigned char *)(&cfg),
|
sizeof(cfg));
|
for (i = 0; i < lo->cfg.num; i++) {
|
tx_chn = lo->cfg.chn[i][TX_CHN];
|
rx_chn = lo->cfg.chn[i][RX_CHN];
|
|
ops = lo_ops(tx_chn);
|
ops->channel = tx_chn;
|
ops->inout = 1;
|
ops->hif_type = HW_TYPE_PCIE;
|
ops->buf_size = lo->cfg.buf_size;
|
ops->pool_size = lo->cfg.pool_size;
|
ops->cb_in_irq = 0;
|
ops->pop_link = lo_tx_pop;
|
ops->tx_complete = lo_tx_complete;
|
mchn_init(ops);
|
lo_buf_alloc(ops->channel, ops->buf_size, ops->pool_size);
|
|
ops = lo_ops(rx_chn);
|
ops->channel = rx_chn;
|
ops->inout = 0;
|
ops->hif_type = HW_TYPE_PCIE;
|
ops->buf_size = lo->cfg.buf_size;
|
ops->pool_size = lo->cfg.pool_size;
|
ops->cb_in_irq = 0;
|
ops->pop_link = lo_rx_pop;
|
ops->push_link = lo_rx_push;
|
mchn_init(ops);
|
lo_buf_alloc(ops->channel, ops->buf_size, ops->pool_size);
|
|
lo->link[i][TX_CHN].chn = tx_chn;
|
lo->link[i][RX_CHN].chn = rx_chn;
|
}
|
PCIE_INFO("[-]%s\n", __func__);
|
|
return 0;
|
}
|
|
int lo_start(int mode)
|
{
|
static int lo_init_state;
|
int i, tx_chn;
|
struct loopback *lo = &g_lo;
|
|
PCIE_INFO("[+]%s\n", __func__);
|
if (lo_init_state == 0) {
|
lo_init();
|
lo_init_state = 1;
|
}
|
lo->loop = mode;
|
lo->seq = 0;
|
for (i = 0; i < lo->cfg.num; i++) {
|
tx_chn = lo->link[i][TX_CHN].chn;
|
lo_push(tx_chn);
|
}
|
PCIE_INFO("[-]%s\n", __func__);
|
|
return 0;
|
}
|
|
int lo_stop(void)
|
{
|
struct loopback *lo = &g_lo;
|
|
lo->loop = 0;
|
|
return 0;
|
}
|