/* SPDX-License-Identifier: GPL-2.0 */
|
#include "sdiohal.h"
|
|
static unsigned int sdiohal_rx_adapt_get(void)
|
{
|
struct sdiohal_data_t *p_data = sdiohal_get_data();
|
|
return p_data->dtbs;
|
}
|
|
static void sdiohal_rx_adapt_set_dtbs(unsigned int len)
|
{
|
struct sdiohal_data_t *p_data = sdiohal_get_data();
|
unsigned int off;
|
|
if (len == 0) {
|
p_data->dtbs = MAX_PAC_SIZE;
|
return;
|
}
|
|
off = (len >> 10) + 1;
|
len = SDIOHAL_ALIGN_BLK(len + 8 * off + 64);
|
#if (BITS_PER_LONG > 32) || (PAGE_SIZE >= 65536)
|
p_data->dtbs = (len >= SDIOHAL_RX_RECVBUF_LEN) ?
|
SDIOHAL_RX_RECVBUF_LEN : len;
|
#else
|
p_data->dtbs = (len >= SDIOHAL_32_BIT_RX_RECVBUF_LEN) ?
|
SDIOHAL_32_BIT_RX_RECVBUF_LEN : len;
|
#endif
|
}
|
|
static unsigned int sdiohal_rx_adapt_get_pac_num(void)
|
{
|
struct sdiohal_data_t *p_data = sdiohal_get_data();
|
|
return p_data->remain_pac_num;
|
}
|
|
static void sdiohal_rx_adapt_set_pac_num(unsigned int num)
|
{
|
struct sdiohal_data_t *p_data = sdiohal_get_data();
|
|
if (num == 0) {
|
p_data->remain_pac_num = 1;
|
return;
|
}
|
|
p_data->remain_pac_num =
|
(num >= MAX_CHAIN_NODE_NUM) ? MAX_CHAIN_NODE_NUM : num;
|
}
|
|
static int sdiohal_data_list_assignment(struct mbuf_t *mbuf_node,
|
struct sdio_puh_t *puh, int channel)
|
{
|
struct sdiohal_list_t *rx_channel_list;
|
|
rx_channel_list = sdiohal_get_rx_channel_list(channel);
|
if (rx_channel_list->node_num == 0)
|
rx_channel_list->mbuf_head = mbuf_node;
|
else
|
rx_channel_list->mbuf_tail->next = mbuf_node;
|
|
mbuf_node->next = NULL;
|
rx_channel_list->mbuf_tail = mbuf_node;
|
rx_channel_list->type = puh->type;
|
rx_channel_list->subtype = puh->subtype;
|
rx_channel_list->node_num++;
|
|
return 0;
|
}
|
|
/* for adma */
|
static int sdiohal_rx_list_parser(struct sdiohal_list_t *data_list,
|
int valid_len)
|
{
|
struct sdiohal_data_t *p_data = sdiohal_get_data();
|
struct sdio_puh_t *puh = NULL;
|
struct mbuf_t *mbuf_node, *mbuf_next;
|
unsigned int node_num, i;
|
int inout = 0, channel = 0;
|
unsigned int parse_len;
|
|
ktime_t kt;
|
u32 sec;
|
|
sdiohal_list_check(data_list, __func__, SDIOHAL_READ);
|
|
node_num = data_list->node_num;
|
mbuf_next = data_list->mbuf_head;
|
parse_len = 0;
|
for (i = 0; i < node_num; i++) {
|
mbuf_node = mbuf_next;
|
mbuf_next = mbuf_next->next;
|
puh = (struct sdio_puh_t *)mbuf_node->buf;
|
if ((puh->eof == 0) && (puh->type != 0xF)) {
|
channel = sdiohal_hwtype_to_channel(inout,
|
puh->type, puh->subtype);
|
parse_len += puh->len;
|
if (puh->check_sum)
|
puh->len += 2;
|
if ((channel >= SDIO_CHANNEL_NUM) || (puh->len >
|
(MAX_PAC_SIZE - SDIO_PUB_HEADER_SIZE)) ||
|
(puh->len == 0)) {
|
sdiohal_rx_list_free(mbuf_node, mbuf_node, 1);
|
sdiohal_err("%s skip type[%d]sub[%d]len[%d]\n",
|
__func__, puh->type, puh->subtype,
|
puh->len);
|
continue;
|
}
|
|
kt = ktime_get();
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
sec = (u32)(div_u64(kt, NSEC_PER_SEC));
|
#else/*LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)*/
|
sec = (u32)(div_u64(kt.tv64, NSEC_PER_SEC));
|
#endif/*LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)*/
|
p_data->throughtput_rx.bytes += puh->len;
|
if (p_data->throughtput_rx.sec != sec) {
|
p_data->throughtput_rx.throughtput = (p_data->throughtput_rx.bytes * 8) >> 10;
|
p_data->throughtput_rx.sec = sec;
|
p_data->throughtput_rx.bytes = 0;
|
sdiohal_pr_perf("tp_rx: %d Kbps\n", p_data->throughtput_rx.throughtput);
|
}
|
|
p_data->rx_packer_cnt++;
|
mbuf_node->len = MAX_MBUF_SIZE;
|
sdiohal_data_list_assignment(mbuf_node, puh, channel);
|
} else {
|
sdiohal_debug("%s eof pac:%d,parse[%d]%s valid[%d]\n",
|
__func__, puh->eof, parse_len,
|
(parse_len < valid_len ? "<":">="),
|
valid_len);
|
sdiohal_debug("%s type[%d]sub[%d]len[%d]\n", __func__,
|
puh->type, puh->subtype, puh->len);
|
|
sdiohal_rx_list_free(mbuf_node, mbuf_node, 1);
|
}
|
}
|
|
return 0;
|
}
|
|
/* for normal dma */
|
static int sdiohal_rx_buf_parser(char *data_buf, int valid_len)
|
{
|
struct sdiohal_data_t *p_data = sdiohal_get_data();
|
struct sdio_puh_t *puh = NULL;
|
struct sdiohal_list_t *data_list = NULL;
|
int inout = 0, channel;
|
unsigned char *p = NULL;
|
unsigned int parse_len;
|
|
ktime_t kt;
|
u32 sec;
|
|
puh = (struct sdio_puh_t *)data_buf;
|
for (parse_len = 0; parse_len < valid_len;) {
|
if (puh->eof != 0)
|
break;
|
|
p = (unsigned char *)puh;
|
if (puh->type != 0xF) {
|
channel = sdiohal_hwtype_to_channel(inout,
|
puh->type, puh->subtype);
|
parse_len += puh->len;
|
if (puh->check_sum)
|
puh->len += 2;
|
if ((channel >= SDIO_CHANNEL_NUM) || (puh->len >
|
(MAX_PAC_SIZE - SDIO_PUB_HEADER_SIZE)) ||
|
(puh->len == 0)) {
|
sdiohal_err("%s skip type[%d]sub[%d]len[%d]\n",
|
__func__, puh->type, puh->subtype,
|
puh->len);
|
continue;
|
}
|
|
kt = ktime_get();
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
sec = (u32)(div_u64(kt, NSEC_PER_SEC));
|
#else/*LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)*/
|
sec = (u32)(div_u64(kt.tv64, NSEC_PER_SEC));
|
#endif/*LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)*/
|
p_data->throughtput_rx.bytes += puh->len;
|
if (p_data->throughtput_rx.sec != sec) {
|
p_data->throughtput_rx.throughtput = (p_data->throughtput_rx.bytes * 8) >> 10;
|
p_data->throughtput_rx.sec = sec;
|
p_data->throughtput_rx.bytes = 0;
|
sdiohal_pr_perf("tp_rx: %d Kbps\n", p_data->throughtput_rx.throughtput);
|
}
|
|
p_data->rx_packer_cnt++;
|
|
data_list = sdiohal_get_rx_mbuf_node(1);
|
if (!data_list)
|
return -ENOMEM;
|
data_list->mbuf_head = data_list->mbuf_tail;
|
data_list->mbuf_head->buf = (unsigned char *)puh;
|
data_list->mbuf_head->len = puh->len;
|
data_list->node_num = 1;
|
p_data->frag_ctl.pagecnt_bias--;
|
|
sdiohal_list_check(data_list, __func__, SDIOHAL_READ);
|
sdiohal_print_list_data(channel, data_list, __func__,
|
SDIOHAL_DATA_LEVEL);
|
|
sdiohal_data_list_assignment(data_list->mbuf_head,
|
puh, channel);
|
kfree(data_list);
|
}
|
/* pointer to next packet */
|
p += sizeof(struct sdio_puh_t)
|
+ SDIOHAL_ALIGN_4BYTE(puh->len);
|
puh = (struct sdio_puh_t *)p;
|
}
|
|
return 0;
|
}
|
|
static void sdiohal_rx_wait(void)
|
{
|
struct sdiohal_data_t *p_data = sdiohal_get_data();
|
|
while (1) {
|
msleep(300);
|
if (WCN_CARD_EXIST(&p_data->xmit_cnt))
|
break;
|
}
|
}
|
|
int sdiohal_rx_thread(void *data)
|
{
|
struct sdiohal_data_t *p_data = sdiohal_get_data();
|
struct sched_param param;
|
int read_len, mbuf_num;
|
int ret = 0;
|
unsigned int rx_dtbs = 0;
|
unsigned int valid_len = 0;
|
static char *rx_buf;
|
struct sdiohal_list_t *data_list = NULL;
|
struct timespec tm_begin, tm_end;
|
static long time_total_ns;
|
static int times_count;
|
|
param.sched_priority = SDIO_RX_TASK_PRIO;
|
sched_setscheduler(current, SCHED_FIFO, ¶m);
|
sdiohal_rx_adapt_set_dtbs(0);
|
sdiohal_rx_adapt_set_pac_num(1);
|
|
while (1) {
|
/* Wait the semaphore */
|
if (p_data->irq_type == SDIOHAL_RX_POLLING)
|
sdiohal_rx_wait();
|
else
|
sdiohal_rx_down();
|
if (p_data->exit_flag)
|
break;
|
if (!WCN_CARD_EXIST(&p_data->xmit_cnt)) {
|
sdiohal_err("%s line %d not have card\n",
|
__func__, __LINE__);
|
continue;
|
}
|
|
getnstimeofday(&p_data->tm_end_irq);
|
sdiohal_pr_perf("rx sch time:%ld\n",
|
(long)(timespec_to_ns(&p_data->tm_end_irq)
|
- timespec_to_ns(&p_data->tm_begin_irq)));
|
|
sdiohal_resume_wait();
|
sdiohal_cp_rx_wakeup(PACKER_RX);
|
|
read_again:
|
getnstimeofday(&tm_begin);
|
|
if (p_data->adma_rx_enable) {
|
/* read len is packet num */
|
mbuf_num = sdiohal_rx_adapt_get_pac_num();
|
sdiohal_debug("%s mbuf_num:%d adma_rx_enable:%d\n",
|
__func__, mbuf_num,
|
p_data->adma_rx_enable);
|
|
data_list = sdiohal_get_rx_mbuf_list(mbuf_num);
|
if (!data_list) {
|
sdiohal_err("sdiohal_get_rx_mbuf_list fail\n");
|
msleep(100);
|
goto submit_list;
|
}
|
if (p_data->irq_type == SDIOHAL_RX_POLLING)
|
memset(p_data->dtbs_buf, 0x0,
|
SDIOHAL_DTBS_BUF_SIZE);
|
ret = sdiohal_adma_pt_read(data_list);
|
if (ret != 0) {
|
sdiohal_err("adma read fail ret:%d\n", ret);
|
rx_dtbs = 0;
|
if (p_data->irq_type != SDIOHAL_RX_POLLING) {
|
sdiohal_rx_list_free(
|
data_list->mbuf_head,
|
data_list->mbuf_tail,
|
data_list->node_num);
|
kfree(data_list);
|
data_list = NULL;
|
goto submit_list;
|
}
|
}
|
rx_dtbs = *((unsigned int *)(p_data->dtbs_buf
|
+ (SDIOHAL_DTBS_BUF_SIZE - 4)));
|
valid_len = *((unsigned int *)(p_data->dtbs_buf
|
+ (SDIOHAL_DTBS_BUF_SIZE - 8)));
|
sdiohal_debug("%s rx_pac_num:%d, valid len:%d\n",
|
__func__, rx_dtbs, valid_len);
|
sdiohal_rx_list_parser(data_list, valid_len);
|
kfree(data_list);
|
data_list = NULL;
|
} else {
|
unsigned int alloc_size;
|
|
/* read len is packet data len */
|
read_len = sdiohal_rx_adapt_get();
|
sdiohal_debug("%s read_len:%d adma_rx_enable:%d\n",
|
__func__, read_len,
|
p_data->adma_rx_enable);
|
|
/*get buf by readlen, dtbs always is SDIOHAL_BLK_SIZE aligned from sdiohal_rx_adapt_set_dtbs()*/
|
rx_buf = sdiohal_get_rx_free_buf(&alloc_size, read_len);
|
if (!rx_buf) {
|
sdiohal_err("get_rx_free_buf fail, rlen=%d\n",
|
read_len);
|
msleep(100);
|
goto submit_list;
|
}
|
if (alloc_size < read_len) {
|
read_len = alloc_size;
|
sdiohal_debug("alloc_size=%d < read_len=%d\n",
|
alloc_size, read_len);
|
}
|
|
ret = sdiohal_sdio_pt_read(rx_buf, read_len);
|
if (ret != 0) {
|
sdiohal_err("sdio pt read fail ret:%d\n", ret);
|
rx_dtbs = 0;
|
if (p_data->irq_type != SDIOHAL_RX_POLLING)
|
goto submit_list;
|
}
|
rx_dtbs = *((unsigned int *)(rx_buf + (read_len - 4)));
|
valid_len =
|
*((unsigned int *)(rx_buf + (read_len - 8)));
|
sdiohal_debug("%s rx_dtbs:%d,valid len:%d\n",
|
__func__, rx_dtbs, valid_len);
|
sdiohal_rx_buf_parser(rx_buf, valid_len);
|
}
|
|
submit_list:
|
getnstimeofday(&tm_end);
|
time_total_ns += timespec_to_ns(&tm_end)
|
- timespec_to_ns(&tm_begin);
|
times_count++;
|
if (!(times_count % PERFORMANCE_COUNT)) {
|
sdiohal_pr_perf("rx list avg time:%ld\n",
|
(time_total_ns / PERFORMANCE_COUNT));
|
time_total_ns = 0;
|
times_count = 0;
|
}
|
|
sdiohal_rx_list_dispatch();
|
if (p_data->adma_rx_enable)
|
sdiohal_rx_adapt_set_pac_num(rx_dtbs);
|
else
|
sdiohal_rx_adapt_set_dtbs(rx_dtbs);
|
if (rx_dtbs > 0)
|
goto read_again;
|
|
sdiohal_cp_rx_sleep(PACKER_RX);
|
sdiohal_unlock_rx_ws();
|
if (p_data->irq_type != SDIOHAL_RX_POLLING)
|
sdiohal_enable_rx_irq();
|
}
|
|
return 0;
|
}
|