Files
luban-lite/application/baremetal/bootloader/lib/aicupg/uart_proto_layer.c
刘可亮 11c97ef399 v1.2.1
2025-07-22 11:15:46 +08:00

570 lines
15 KiB
C

/*
* Copyright (c) 2023-2025, ArtInChip Technology Co., Ltd
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <aic_common.h>
#include <aic_utils.h>
#include <aic_core.h>
#include <uart.h>
#include <uart_proto_layer.h>
#include <aicupg.h>
#include <crc16.h>
//#define AICUPG_UART_DEBUG
#ifdef AICUPG_UART_DEBUG
#undef pr_err
#undef pr_warn
#undef pr_info
#undef pr_debug
#define pr_err printf
#define pr_warn printf
#define pr_info printf
#define pr_debug printf
#endif
#define update_last_recv_time() \
do { \
uart_proto.last_recv_time = aic_get_time_ms(); \
} while(0)
static LIST_HEAD(task);
struct uart_proto_device uart_proto;
static u8 g_uart_send_recv_buf[LNG_FRM_MAX_LEN];
static int uart_proto_id = 0;
static int uart_proto_add_task(u32 dir, u8 *data, u32 data_len)
{
struct proto_task *t = NULL;
t = aicos_malloc(0, sizeof(struct proto_task));
if (!t)
{
pr_err("alloc task failed\n");
return -1;
}
t->direction = dir;
t->status = UPG_UART_CMD_RECV;
t->data = data;
t->data_len = data_len;
t->xfer_len = 0;
t->single_frame_trans_siz = 0;
t->single_frame_transed_siz = 0;
list_add_tail(&t->list, &task);
return 0;
}
static struct proto_task *uart_proto_get_task(void)
{
struct list_head *pos;
list_for_each(pos, &task) {
return list_entry(pos, struct proto_task, list);
}
return NULL;
}
static int uart_proto_del_task(void)
{
struct proto_task *t;
t = uart_proto_get_task();
if (!t) {
pr_info("list is NULL.\n");
return -ENOENT;
}
list_del(&t->list);
free(t);
return 0;
}
static void uart_proto_putchar(int id, u8 ch)
{
pr_debug("---> Send 0x%x\n", ch);
uart_putchar(id, ch);
uart_proto.last_send_time = aic_get_time_ms();
}
static int uart_proto_gets(int id, u8 *buf, int len)
{
int ret = 0;
ret = uart_gets(id, buf, len);
if (ret) {
uart_proto.last_recv_time = aic_get_time_ms();
#ifdef AICUPG_UART_DEBUG
if (ret <= 64)
hexdump_msg("Recvs:", buf, ret, 1);
#endif
}
return ret;
}
static int uart_proto_puts(int id, u8 *buf, int len)
{
int ret = 0;
ret = uart_puts(id, buf, len);
if (ret) {
uart_proto.last_send_time = aic_get_time_ms();
#ifdef AICUPG_UART_DEBUG
if (ret <= 64)
hexdump_msg("Sends:", buf, ret, 1);
#endif
}
return ret;
}
static int pack_frame(u8 blk, u8 *frame, u8 *data, int len)
{
u16 crc16;
int flen;
if (len == LNG_FRM_DLEN) {
pr_debug("pack long frame\n");
frame[0] = STX;
frame[1] = blk;
frame[2] = 255 - blk;
memcpy(&frame[3], data, len);
crc16 = crc16_ccitt(0, data, len);
frame[LNG_FRM_DLEN + 3] = (u8)(crc16 >> 8);
frame[LNG_FRM_DLEN + 4] = (u8)(crc16 & 0xFF);
flen = len + 5;
} else if (len <= SHT_FRM_DLEN) {
pr_debug("pack short frame\n");
frame[0] = SOH;
frame[1] = blk;
frame[2] = 255 - blk;
frame[3] = (u8)len;
memcpy(&frame[4], data, len);
crc16 = crc16_ccitt(0, data, len);
frame[len + 4] = (u8)(crc16 >> 8);
frame[len + 5] = (u8)(crc16 & 0xFF);
flen = len + 6;
} else {
return 0;
}
return flen;
}
static int read_short_frame_data(u8 *buf, int len)
{
int flen;
u16 crc16, crc16_i;
u8 blk1, blk2, nextblk;
pr_debug("SHORT: %llu\n", aic_get_time_us());
// hexdump(buf, len, 1);
blk1 = buf[1];
blk2 = buf[2];
flen = buf[3];
if (blk1 != (255 - blk2)) {
pr_err("blk1 %d != blk2 %d\n", blk1, (255 - blk2));
return UART_ERR_DATA;
}
pr_debug("blk no %d\n", blk1);
crc16_i = buf[4 + flen] << 8 | buf[4 + flen + 1];
crc16 = crc16_ccitt(0, &buf[4], flen);
if (crc16 != crc16_i) {
pr_err("crc16 error: 0x%x != 0x%x\n", crc16, crc16_i);
return UART_ERR_DATA;
}
nextblk = uart_proto.recv_blk_no + 1;
if (blk1 != 1 && nextblk != blk1 && !uart_proto.first_connect) {
pr_err("blk no discontinue should be %d, got %d\n", nextblk, blk1);
return UART_ERR_DATA;
}
if (blk1 == uart_proto.recv_blk_no) {
printf("Repeat frame.\n");
return UART_SKIP_DATA;
}
uart_proto.recv_blk_no = blk1;
uart_proto.first_connect = 0;
pr_debug("%s done\n", __func__);
return flen;
}
/*
* return > 0: ACK
* return = 0: ACK
* return < 0: NAK
*/
static int read_long_frame_data(u8 *buf, int len)
{
int flen;
u16 crc16, crc16_i;
u8 blk1, blk2, nextblk;
pr_debug("LONG: %llu\n", aic_get_time_us());
blk1 = buf[1];
blk2 = buf[2];
flen = LNG_FRM_DLEN;
if (blk1 != (255 - blk2)) {
pr_err("blk1 %d != blk2 %d\n", blk1, (255 - blk2));
return UART_ERR_DATA;
}
pr_debug("blk no %d\n", blk1);
crc16_i = buf[3 + flen] << 8 | buf[3 + flen + 1];
crc16 = crc16_ccitt(0, &buf[3], flen);
if (crc16 != crc16_i) {
pr_err("crc16 error: 0x%x != 0x%x\n", crc16, crc16_i);
hexdump(buf, LNG_FRM_MAX_LEN, 1);
return UART_ERR_DATA;
}
nextblk = uart_proto.recv_blk_no + 1;
if (blk1 != 1 && nextblk != blk1 && !uart_proto.first_connect) {
pr_err("blk no discontinue should be %d, got %d\n", nextblk, blk1);
return UART_ERR_DATA;
}
/* Recv repeat frame, just skip it */
if (blk1 == uart_proto.recv_blk_no) {
pr_warn("Repeat frame.\n");
return UART_SKIP_DATA;
}
uart_proto.recv_blk_no = blk1;
uart_proto.first_connect = 0;
pr_debug("%s done\n", __func__);
return flen;
}
static int read_frame_data(u8 *buf, int len)
{
if (buf[0] == SOH) {
return read_short_frame_data(buf, len);
} else if (buf[0] == STX) {
return read_long_frame_data(buf, len);
} else {
return UART_ERR_DATA;
}
}
static int uart_proto_layer_host_send_proc(struct proto_task *t, u8 *pframe)
{
int ret, rest;
u8 ch = 0, *p;
u64 delta = 0;
switch (t->status) {
case UPG_UART_DATA_RECV:
if (t->single_frame_transed_siz == 0) {
ret = uart_proto_gets(uart_proto_id, &pframe[0], 1);
if (ret == 0) {
//pr_debug("read head failed.\n");
return -1;
}
if (pframe[0] != SOH && pframe[0] != STX) {
//pr_err("frame error. unknown frame data.\n");
break;
}
t->single_frame_transed_siz++;
}
if (t->single_frame_transed_siz == 1) {
ret = uart_proto_gets(uart_proto_id, &pframe[1], 1);
if (ret == 0) {
pr_err("read blk1 failed.\n");
return -1;
}
t->single_frame_transed_siz++;
}
if (t->single_frame_transed_siz == 2) {
ret = uart_proto_gets(uart_proto_id, &pframe[2], 1);
if (ret == 0) {
pr_err("read blk2 failed.\n");
return -1;
}
t->single_frame_transed_siz++;
}
if (t->single_frame_transed_siz == 3) {
ret = uart_proto_gets(uart_proto_id, &pframe[3], 1);
if (ret == 0) {
pr_err("read frm_len failed.\n");
return -1;
}
t->single_frame_transed_siz++;
if (pframe[0] == SOH) {
t->single_frame_trans_siz = pframe[3] + 6;
} else if (pframe[0] == STX) {
t->single_frame_trans_siz = LNG_FRM_DLEN + 5;
}
}
rest = t->single_frame_trans_siz - t->single_frame_transed_siz;
ret = uart_proto_gets(uart_proto_id, &pframe[t->single_frame_transed_siz], rest);
if (ret != rest) {
t->single_frame_transed_siz += ret;
delta = aic_get_time_ms() - uart_proto.last_recv_time;
if (delta > 1000) {
pr_warn("Long time no data, re-recv it ...\n");
update_last_recv_time();
t->single_frame_trans_siz = 0;
t->single_frame_transed_siz = 0;
uart_proto_putchar(uart_proto_id, NAK);
}
return -1;
} else {
t->single_frame_transed_siz += ret;
ret = read_frame_data(pframe, t->single_frame_transed_siz);
if (ret > 0) {
pr_debug("Recv %d\n", ret);
p = t->data + t->xfer_len;
if (pframe[0] == SOH)
memcpy(p, &pframe[4], ret);
else if (pframe[0] == STX)
memcpy(p, &pframe[3], ret);
t->xfer_len += ret;
uart_proto_putchar(uart_proto_id, ACK);
} else if (ret == 0) {
uart_proto_putchar(uart_proto_id, ACK);
} else {
t->single_frame_trans_siz = 0;
t->single_frame_transed_siz = 0;
uart_proto_putchar(uart_proto_id, NAK);
break;
}
t->single_frame_trans_siz = 0;
t->single_frame_transed_siz = 0;
memset(pframe, 0, LNG_FRM_MAX_LEN);
pr_debug("Ack frame\n");
}
if (t->xfer_len >= t->data_len) {
pr_debug("Recv in data len %d\n", t->xfer_len);
uart_proto_layer_set_status(UPG_UART_DATA_RECV_DONE);
t->status = UPG_UART_CMD_RECV;
uart_proto_del_task();
return -1;
}
break;
case UPG_UART_CMD_RECV:
ret = uart_proto_gets(uart_proto_id, &ch, 1);
if (ret == 0) {
delta = aic_get_time_ms() - uart_proto.last_recv_time;
if (delta > SIG_A_INTERVAL_MS) {
update_last_recv_time();
pr_debug("Wait host connect ...\n");
}
return -1;
}
if (ch == SOH || ch == STX) {
pframe[0] = ch;
t->status = UPG_UART_DATA_RECV;
t->single_frame_transed_siz++;
} else if (ch == SIG_C) {
uart_proto_putchar(uart_proto_id, ACK);
pr_debug("Ack SIG_C\n");
break;
} else if (ch == DC1_SEND) {
uart_proto_putchar(uart_proto_id, ACK);
t->direction = DIR_HOST_SEND;
t->status = UPG_UART_DATA_RECV;
memset(pframe, 0, LNG_FRM_MAX_LEN);
pr_debug("Host switch to send mode.\n");
break;
} else if (ch == DC2_RECV) {
uart_proto_putchar(uart_proto_id, ACK);
t->direction = DIR_HOST_RECV;
t->status = UPG_UART_DATA_SEND;
pr_debug("Host switch to recv mode.\n");
//csi_coret_config(drv_get_sys_freq() / 200, CORET_IRQn);
break;
}
break;
}
return 0;
}
static int uart_proto_layer_host_recv_proc(struct proto_task *t, u8 *pframe)
{
int ret, rest, slice, flen;
u8 ch = 0, *p;
u64 delta = 0;
switch (t->status) {
case UPG_UART_DATA_SEND:
p = t->data + t->xfer_len;
rest = t->data_len - t->xfer_len;
if (rest > 0) {
if (rest > LNG_FRM_DLEN)
slice = LNG_FRM_DLEN;
else if (rest > SHT_FRM_DLEN)
slice = SHT_FRM_DLEN;
else
slice = rest;
t->single_frame_transed_siz = slice;
flen = pack_frame(uart_proto.send_blk_no, pframe, p, slice);
ret = uart_proto_puts(uart_proto_id, pframe, flen);
if (ret != flen) {
pr_err("write frame data error. ret:%d\n", ret);
return -1;
}
pr_debug("frame data is written.\n");
}
t->status = UPG_UART_CMD_RECV;
break;
case UPG_UART_CMD_RECV:
ret = uart_proto_gets(uart_proto_id, &ch, 1);
if (ret == 0) {
delta = aic_get_time_ms() - uart_proto.last_recv_time;
if (delta > 20) {
update_last_recv_time();
t->status = UPG_UART_DATA_SEND;
pr_debug("Timeout retry ...\n");
break;
}
return -1;
}
if (ch == SIG_C) {
uart_proto_putchar(uart_proto_id, ACK);
pr_debug("Ack SIG_C\n");
break;
} else if (ch == DC1_SEND) {
uart_proto_putchar(uart_proto_id, ACK);
t->direction = DIR_HOST_SEND;
t->status = UPG_UART_DATA_RECV;
memset(pframe, 0, LNG_FRM_MAX_LEN);
pr_debug("Host switch to send mode.\n");
break;
} else if (ch == DC2_RECV) {
uart_proto_putchar(uart_proto_id, ACK);
t->direction = DIR_HOST_RECV;
t->status = UPG_UART_DATA_SEND;
pr_debug("Host switch to recv mode.\n");
//csi_coret_config(drv_get_sys_freq() / 200, CORET_IRQn);
break;
} else if (ch == NAK) {
t->status = UPG_UART_DATA_SEND;
break;
} else if (ch == ACK) {
t->xfer_len += t->single_frame_transed_siz;
uart_proto.send_blk_no++;
t->status = UPG_UART_DATA_SEND;
}
if (t->xfer_len >= t->data_len) {
pr_debug("Send out data len %d\n", t->xfer_len);
uart_proto_layer_set_status(UPG_UART_DATA_SEND_DONE);
t->status = UPG_UART_CMD_RECV;
uart_proto_del_task();
return -1;
}
break;
}
return 0;
}
static int uart_proto_layer_proc(void)
{
struct proto_task *t = NULL;
int ret;
u8 *pframe;
t = uart_proto_get_task();
if (!t)
return 0;
pframe = g_uart_send_recv_buf;
while (1) {
switch (t->direction) {
case DIR_HOST_SEND:
ret = uart_proto_layer_host_send_proc(t, pframe);
if (ret) {
return -1;
}
break;
case DIR_HOST_RECV:
ret = uart_proto_layer_host_recv_proc(t, pframe);
if (ret) {
return -1;
}
break;
}
}
return 0;
}
int uart_proto_start_read(u8 *data, u32 data_len)
{
return uart_proto_add_task(DIR_HOST_SEND, data, data_len);
}
int uart_proto_start_write(u8 *data, u32 data_len)
{
return uart_proto_add_task(DIR_HOST_RECV, data, data_len);
}
int uart_proto_layer_get_status(void)
{
u8 status = 0;
ringbuf_out(&(uart_proto.status_fifo.rb), &status, 1);
return status;
}
void uart_proto_layer_set_status(int status)
{
ringbuf_in(&(uart_proto.status_fifo.rb), &status, 1);
}
irqreturn_t uart_proto_layer_irqhandler(int irq, void *data)
{
csi_coret_config(drv_get_sys_freq() / 5000, CORET_IRQn);
uart_proto_layer_proc();
return 0;
}
int uart_proto_layer_init(int id)
{
uart_proto_id = id;
INIT_LIST_HEAD(&task);
memset(&uart_proto, 0, sizeof(uart_proto));
uart_proto.first_connect = 1;
ringbuf_init(&(uart_proto.status_fifo.rb), uart_proto.status_fifo.buffer, 4);
//10ms
csi_coret_config(drv_get_sys_freq() / 1000, CORET_IRQn);
aicos_request_irq(CORET_IRQn, uart_proto_layer_irqhandler, 0, NULL, NULL);
aicos_irq_set_prio(CORET_IRQn, 8);
aicos_irq_enable(CORET_IRQn);
/* Send CAN first */
uart_proto_putchar(uart_proto_id, CAN);
return 0;
}
int uart_proto_layer_deinit()
{
aicos_irq_disable(CORET_IRQn);
return 0;
}