2025-10-10 16:07:00 +08:00

815 lines
18 KiB
C
Executable File

#include <stdio.h>
#include <string.h>
#include "cli.h"
#include "shell_drv.h"
#if CONFIG_AT
#include "atsvr_port.h"
#endif
#define TX_QUEUE_LEN 8
#define TX_BUFF_SIZE 1024
#define RX_BUFF_SIZE 200
#define ECHO_BUFF_SIZE 64
typedef struct
{
u8 * packet;
u16 len;
u16 tag;
} dev_tx_packet_t;
typedef struct
{
u8 uart_id;
/* ======== TX channel ======= */
/* tx queue */
dev_tx_packet_t tx_list[TX_QUEUE_LEN];
u16 list_out_idx;
u16 list_in_idx;
/* currently tx packet info */
u8 *cur_packet;
u16 packet_len;
u16 packet_tag;
u16 packet_tx_len;
u8 tx_stopped;
u8 tx_suspend;
u8 echo_buff[ECHO_BUFF_SIZE];
u8 echo_wr_idx;
u8 echo_rd_idx;
tx_complete_t tx_complete_callback;
/* ======== RX channel ======= */
/* rx buffer */
u8 rx_buff[RX_BUFF_SIZE];
u16 rx_buff_wr_idx;
u16 rx_buff_rd_idx;
u8 rx_over_flow;
rx_indicate_t rx_indicate_callback;
#if CONFIG_UART_RING_BUFF
/* patch ---> tx buff. */
u8 tx_ring_buff[TX_BUFF_SIZE];
u16 tx_buf_wr_idx;
u16 tx_buf_rd_idx;
#endif
} shell_uart_ext_t;
static bool_t shell_uart_init(shell_dev_t * shell_dev);
static bool_t shell_uart_open(shell_dev_t * shell_dev, tx_complete_t tx_callback, rx_indicate_t rx_callback);
static u16 shell_uart_write_async(shell_dev_t * shell_dev, u8 * pBuf, u16 BufLen, u16 Tag);
static u16 shell_uart_read(shell_dev_t * shell_dev, u8 * pBuf, u16 BufLen);
static u16 shell_uart_write_sync(shell_dev_t * shell_dev, u8 * pBuf, u16 BufLen);
static u16 shell_uart_write_echo(shell_dev_t * shell_dev, u8 * pBuf, u16 BufLen);
static bool_t shell_uart_ctrl(shell_dev_t * shell_dev, u8 cmd, void *param);
static bool_t shell_uart_close(shell_dev_t * shell_dev);
static const shell_dev_drv_t shell_uart_drv =
{
.init = shell_uart_init,
.open = shell_uart_open,
.write_async = shell_uart_write_async,
.read = shell_uart_read,
.write_sync = shell_uart_write_sync,
.write_echo = shell_uart_write_echo,
.io_ctrl = shell_uart_ctrl,
.close = shell_uart_close
};
static shell_uart_ext_t uart_ext = /* default uart. */
{
.uart_id = CONFIG_UART_PRINT_PORT
};
shell_dev_t shell_uart =
{
.dev_drv = (struct _shell_dev_drv *)&shell_uart_drv,
.dev_type = SHELL_DEV_UART,
.dev_ext = &uart_ext
};
#if 1
static shell_uart_ext_t uart1_ext =
{
.uart_id = UART_ID_1
};
shell_dev_t shell_uart1 =
{
.dev_drv = (struct _shell_dev_drv *)&shell_uart_drv,
.dev_type = SHELL_DEV_UART,
.dev_ext = &uart1_ext
};
static shell_uart_ext_t uart2_ext =
{
.uart_id = UART_ID_2
};
shell_dev_t shell_uart2 =
{
.dev_drv = (struct _shell_dev_drv *)&shell_uart_drv,
.dev_type = SHELL_DEV_UART,
.dev_ext = &uart2_ext
};
#endif
#if CONFIG_AT
static shell_uart_ext_t atsvr_uart_ext =
{
.uart_id = AT_UART_PORT_CFG
};
shell_dev_t atsvr_shell_uart =
{
.dev_drv = (struct _shell_dev_drv *)&shell_uart_drv,
.dev_type = SHELL_DEV_UART,
.dev_ext = &atsvr_uart_ext
};
#endif
#if 1
static const uart_config_t config =
{
.baud_rate = UART_BAUD_RATE,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_NONE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_FLOWCTRL_DISABLE,
.src_clk = UART_SCLK_XTAL_26M
};
#endif
/* =============================== internal functions =========================== */
static void shell_uart_rx_isr(int uartn, shell_uart_ext_t *uart_ext)
{
u16 free_buff_len, rx_cnt = 0;
u8 rx_data;
int ret = -1;
(void)uartn;
if(uart_ext->rx_buff_wr_idx >= uart_ext->rx_buff_rd_idx)
{
free_buff_len = RX_BUFF_SIZE - uart_ext->rx_buff_wr_idx + uart_ext->rx_buff_rd_idx;
}
else
{
free_buff_len = uart_ext->rx_buff_rd_idx - uart_ext->rx_buff_wr_idx;
}
while(bTRUE) /* read all data from rx-FIFO. */
{
ret = uart_read_byte_ex(uart_ext->uart_id, &rx_data);
if (ret == -1)
break;
rx_cnt++;
/* rx_buff_wr_idx == rx_buff_rd_idx means empty, so reserve one byte. */
if(rx_cnt < free_buff_len) /* reserved one byte space. */
{
uart_ext->rx_buff[uart_ext->rx_buff_wr_idx] = rx_data;
uart_ext->rx_buff_wr_idx = (uart_ext->rx_buff_wr_idx + 1) % RX_BUFF_SIZE;
}
else
{
/* discard rx-data, rx overflow. */
uart_ext->rx_over_flow = 1; // bTRUE; // rx overflow, disable rx interrupt to stop rx.
}
}
if(uart_ext->rx_indicate_callback != NULL)
{
uart_ext->rx_indicate_callback();
}
}
#if !CONFIG_UART_RING_BUFF
static void shell_uart_tx_isr(int uartn, shell_uart_ext_t *uart_ext)
{
int ret;
(void)uartn;
while(bTRUE) /* write data to tx-FIFO. */
{
ret = uart_write_ready(uart_ext->uart_id);
if(ret != 0)
break;
/* previous packet tx complete, check ECHO before new packet. */
if((uart_ext->cur_packet == NULL) || (uart_ext->packet_len == 0))
{
if(uart_ext->echo_rd_idx != uart_ext->echo_wr_idx) /* tx echo firstly. */
{
uart_write_byte(uart_ext->uart_id, uart_ext->echo_buff[uart_ext->echo_rd_idx]);
uart_ext->echo_rd_idx = (uart_ext->echo_rd_idx + 1) % ECHO_BUFF_SIZE;
continue; /* continue to ECHO next byte to tx-FIFO. */
}
else
{
if(uart_ext->list_out_idx != uart_ext->list_in_idx)
{
uart_ext->cur_packet = uart_ext->tx_list[uart_ext->list_out_idx].packet;
uart_ext->packet_len = uart_ext->tx_list[uart_ext->list_out_idx].len;
uart_ext->packet_tag = uart_ext->tx_list[uart_ext->list_out_idx].tag;
uart_ext->packet_tx_len = 0;
}
else
{
/* all packets tx complete. */
/* disable tx interrupt ? */ // disable TX firstly, then set tx_stopped to 1.
bk_uart_disable_tx_interrupt(uart_ext->uart_id);
uart_ext->tx_stopped = 1; /* bTRUE;*/ /* all data tranferred, tx stopped.*/
break;
}
}
}
if(uart_ext->packet_tx_len < uart_ext->packet_len)
{
uart_write_byte(uart_ext->uart_id, uart_ext->cur_packet[uart_ext->packet_tx_len]);
uart_ext->packet_tx_len++;
continue; /* continue to TX next byte to tx-FIFO. */
}
else
{
/* sent the whole packet, notify app. */
if(uart_ext->tx_complete_callback != NULL)
{
uart_ext->tx_complete_callback(uart_ext->cur_packet, uart_ext->packet_tag);
}
uart_ext->cur_packet = NULL;
uart_ext->packet_len = 0;
uart_ext->packet_tx_len = 0;
/* to next packet. */
uart_ext->list_out_idx = (uart_ext->list_out_idx + 1) % TX_QUEUE_LEN;
continue; /* continue to TX next packet. */
}
}
}
#else
static void shell_uart_tx_isr(int uartn, shell_uart_ext_t *uart_ext)
{
int ret;
int tx_cnt = 40;
(void)uartn;
int free_cnt;
int cpy_cnt;
#if 0
if(uart_ext->tx_buf_rd_idx > uart_ext->tx_buf_wr_idx)
{
free_cnt = (uart_ext->tx_buf_rd_idx - uart_ext->tx_buf_wr_idx) - 1;
}
else
{
free_cnt = TX_BUFF_SIZE - 1 - (uart_ext->tx_buf_wr_idx - uart_ext->tx_buf_rd_idx);
}
if(free_cnt >= 128)
tx_cnt = 40; // to generate tx interrupt frequently to copy more data into tx_ring_buff.
else
tx_cnt = 128;
#endif
while(bTRUE) /* write data to tx-FIFO. */
{
if(tx_cnt > 0)
{
while(uart_ext->tx_buf_rd_idx != uart_ext->tx_buf_wr_idx)
{
ret = uart_write_ready(uart_ext->uart_id);
if(ret != 0)
break;
uart_write_byte(uart_ext->uart_id, uart_ext->tx_ring_buff[uart_ext->tx_buf_rd_idx]);
uart_ext->tx_buf_rd_idx = (uart_ext->tx_buf_rd_idx + 1) % TX_BUFF_SIZE;
tx_cnt--;
if(tx_cnt == 0)
break;
}
}
if(uart_ext->tx_buf_rd_idx > uart_ext->tx_buf_wr_idx)
{
cpy_cnt = (uart_ext->tx_buf_rd_idx - uart_ext->tx_buf_wr_idx) - 1;
}
else
{
cpy_cnt = TX_BUFF_SIZE - uart_ext->tx_buf_wr_idx;
if(uart_ext->tx_buf_rd_idx == 0)
{
cpy_cnt--;
}
}
if(cpy_cnt == 0) // buffer full.
break;
free_cnt = cpy_cnt; // saved the cpy_cnt.
while(cpy_cnt > 0)
{
/* previous packet tx complete, check ECHO before new packet. */
if((uart_ext->cur_packet == NULL) || (uart_ext->packet_len == 0))
{
if(uart_ext->echo_rd_idx != uart_ext->echo_wr_idx) /* tx echo firstly. */
{
uart_ext->tx_ring_buff[uart_ext->tx_buf_wr_idx] = uart_ext->echo_buff[uart_ext->echo_rd_idx];
uart_ext->echo_rd_idx = (uart_ext->echo_rd_idx + 1) % ECHO_BUFF_SIZE;
uart_ext->tx_buf_wr_idx++;
cpy_cnt--;
continue; /* continue to ECHO next byte to tx-FIFO. */
}
else
{
if(uart_ext->list_out_idx != uart_ext->list_in_idx)
{
uart_ext->cur_packet = uart_ext->tx_list[uart_ext->list_out_idx].packet;
uart_ext->packet_len = uart_ext->tx_list[uart_ext->list_out_idx].len;
uart_ext->packet_tag = uart_ext->tx_list[uart_ext->list_out_idx].tag;
uart_ext->packet_tx_len = 0;
}
else
{
/* all packets tx complete. */
break;
}
}
}
if(uart_ext->packet_tx_len < uart_ext->packet_len)
{
ret = uart_ext->packet_len - uart_ext->packet_tx_len;
if(ret > cpy_cnt)
ret = cpy_cnt;
while(ret > 0)
{
uart_ext->tx_ring_buff[uart_ext->tx_buf_wr_idx] = uart_ext->cur_packet[uart_ext->packet_tx_len];
uart_ext->packet_tx_len++;
uart_ext->tx_buf_wr_idx++;
cpy_cnt--;
ret--;
}
continue; /* continue to TX next byte to tx-FIFO. */
}
else
{
/* sent the whole packet, notify app. */
if(uart_ext->tx_complete_callback != NULL)
{
uart_ext->tx_complete_callback(uart_ext->cur_packet, uart_ext->packet_tag);
}
uart_ext->cur_packet = NULL;
uart_ext->packet_len = 0;
uart_ext->packet_tx_len = 0;
/* to next packet. */
uart_ext->list_out_idx = (uart_ext->list_out_idx + 1) % TX_QUEUE_LEN;
continue; /* continue to TX next packet. */
}
}
if (uart_ext->tx_buf_wr_idx == TX_BUFF_SIZE)
uart_ext->tx_buf_wr_idx = 0;
if(uart_ext->tx_buf_rd_idx == uart_ext->tx_buf_wr_idx) /* all data tranferred */
{
/* disable tx interrupt ? */ // disable TX firstly, then set tx_stopped to 1.
bk_uart_disable_tx_interrupt(uart_ext->uart_id);
uart_ext->tx_stopped = 1; /* tx stopped.*/
break;
}
if(free_cnt == cpy_cnt) // nothing to copy, so leave the ISR.
{
break;
}
}
}
#endif
static void shell_uart_tx_trigger(shell_uart_ext_t *uart_ext)
{
if(uart_ext->tx_suspend != 0)
return;
if(uart_ext->tx_stopped == 0)
return;
uart_ext->tx_stopped = 0; // set tx_stopped to 0 firstly, then enable TX.
bk_uart_enable_tx_interrupt(uart_ext->uart_id);
}
static void shell_uart_flush(shell_uart_ext_t *uart_ext)
{
int ret;
if(uart_ext->tx_suspend != 0)
{
// resume tx.....
uart_ext->tx_suspend = 0;
shell_uart_tx_trigger(uart_ext);
}
while(uart_ext->tx_stopped == 0) /* log tx pending. */
{
ret = uart_write_ready(uart_ext->uart_id);
if(ret == BK_OK)
{
shell_uart_tx_isr(uart_ext->uart_id, uart_ext);
}
}
}
/* =============================== shell uart driver APIs =========================== */
static bool_t shell_uart_init(shell_dev_t * shell_dev)
{
u8 uart_id;
shell_uart_ext_t *uart_ext;
if(shell_dev == NULL)
return bFALSE;
uart_ext = (shell_uart_ext_t *)shell_dev->dev_ext;
uart_id = uart_ext->uart_id;
memset(uart_ext, 0, sizeof(shell_uart_ext_t));
uart_ext->rx_over_flow = 0;
uart_ext->tx_stopped = 1;
uart_ext->tx_suspend = 0;
uart_ext->uart_id = uart_id;
if(CONFIG_UART_PRINT_PORT != uart_id)
{
bk_uart_init(uart_id, &config);
bk_uart_isr_set_priority(uart_id, BK_PRINT_UART_ISR_DEFAULT_PRIORITY);
}
else
{
bk_uart_set_enable_rx(uart_id, 0);
bk_uart_set_enable_rx(uart_id, 1);
}
return bTRUE;
}
static bool_t shell_uart_open(shell_dev_t * shell_dev, tx_complete_t tx_callback, rx_indicate_t rx_callback)
{
shell_uart_ext_t *uart_ext;
if(shell_dev == NULL)
return bFALSE;
uart_ext = (shell_uart_ext_t *)shell_dev->dev_ext;
uart_ext->tx_complete_callback = tx_callback;
uart_ext->rx_indicate_callback = rx_callback;
bk_uart_disable_sw_fifo(uart_ext->uart_id);
// call uart driver to register isr callback;
bk_uart_register_rx_isr(uart_ext->uart_id, (uart_isr_t)shell_uart_rx_isr, uart_ext);
bk_uart_register_tx_isr(uart_ext->uart_id, (uart_isr_t)shell_uart_tx_isr, uart_ext);
if(rx_callback != NULL)
{
bk_uart_enable_rx_interrupt(uart_ext->uart_id);
}
return bTRUE;
}
static u16 shell_uart_write_async(shell_dev_t * shell_dev, u8 * pBuf, u16 BufLen, u16 Tag)
{
u16 free_items;
shell_uart_ext_t *uart_ext;
if(shell_dev == NULL)
return 0;
uart_ext = (shell_uart_ext_t *)shell_dev->dev_ext;
if((pBuf == NULL) /*|| (BufLen == 0)*/)
return 0;
/* enqueue pBuf even if BufLen is 0, upper layer need tx-complete-callback to free this pBuf. */
if(uart_ext->list_out_idx > uart_ext->list_in_idx)
free_items = uart_ext->list_out_idx - uart_ext->list_in_idx;
else
free_items = TX_QUEUE_LEN - uart_ext->list_in_idx + uart_ext->list_out_idx;
/* list_out_idx == list_in_idx means empty, so reserved one item. */
if(free_items > 1)
{
uart_ext->tx_list[uart_ext->list_in_idx].packet = pBuf;
uart_ext->tx_list[uart_ext->list_in_idx].len = BufLen;
uart_ext->tx_list[uart_ext->list_in_idx].tag = Tag;
uart_ext->list_in_idx = (uart_ext->list_in_idx + 1) % TX_QUEUE_LEN;
shell_uart_tx_trigger(uart_ext);
return 1;
}
else
{
return 0;
}
}
static u16 shell_uart_read(shell_dev_t * shell_dev, u8 * pBuf, u16 BufLen)
{
u16 read_cnt = 0;
shell_uart_ext_t *uart_ext;
if(shell_dev == NULL)
return 0;
uart_ext = (shell_uart_ext_t *)shell_dev->dev_ext;
if((pBuf == NULL) || (BufLen == 0))
return 0;
while(uart_ext->rx_buff_rd_idx != uart_ext->rx_buff_wr_idx)
{
pBuf[read_cnt] = uart_ext->rx_buff[uart_ext->rx_buff_rd_idx];
uart_ext->rx_buff_rd_idx = (uart_ext->rx_buff_rd_idx + 1) % RX_BUFF_SIZE;
read_cnt++;
if(read_cnt >= BufLen)
break;
}
return read_cnt;
}
/* call this after interrupt is DISABLED. */
static u16 shell_uart_write_sync(shell_dev_t * shell_dev, u8 * pBuf, u16 BufLen)
{
shell_uart_ext_t *uart_ext;
if(shell_dev == NULL)
return 0;
uart_ext = (shell_uart_ext_t *)shell_dev->dev_ext;
if((pBuf == NULL) || (BufLen == 0))
return 0;
u16 wr_cnt = 0;
while(bTRUE) /* write data to tx-FIFO. */
{
uart_write_byte(uart_ext->uart_id, *pBuf); // it is macro define, do NOT use *pBuf++;
pBuf++; wr_cnt++;
if(wr_cnt >= BufLen)
break;
}
return BufLen;
}
static u16 shell_uart_write_echo(shell_dev_t * shell_dev, u8 * pBuf, u16 BufLen)
{
u16 free_buff_len, wr_cnt = 0;
shell_uart_ext_t *uart_ext;
if(shell_dev == NULL)
return 0;
uart_ext = (shell_uart_ext_t *)shell_dev->dev_ext;
if((pBuf == NULL) || (BufLen == 0))
return 0;
if(uart_ext->echo_wr_idx >= uart_ext->echo_rd_idx)
{
free_buff_len = ECHO_BUFF_SIZE - uart_ext->echo_wr_idx + uart_ext->echo_rd_idx;
}
else
{
free_buff_len = uart_ext->echo_rd_idx - uart_ext->echo_wr_idx;
}
/* echo_wr_idx == echo_rd_idx means empty, so reserved one byte. */
while(free_buff_len > 1)
{
uart_ext->echo_buff[uart_ext->echo_wr_idx] = pBuf[wr_cnt];
uart_ext->echo_wr_idx = (uart_ext->echo_wr_idx + 1) % ECHO_BUFF_SIZE;
free_buff_len--;
wr_cnt++;
if(wr_cnt >= BufLen)
break;
}
if(wr_cnt > 0)
shell_uart_tx_trigger(uart_ext);
return wr_cnt;
}
static bool_t shell_uart_ctrl(shell_dev_t * shell_dev, u8 cmd, void *param)
{
shell_uart_ext_t *uart_ext;
if(shell_dev == NULL)
return bFALSE;
uart_ext = (shell_uart_ext_t *)shell_dev->dev_ext;
switch(cmd)
{
case SHELL_IO_CTRL_GET_STATUS:
if(param == NULL)
return bFALSE;
u16 free_items;
if(uart_ext->list_out_idx > uart_ext->list_in_idx)
free_items = uart_ext->list_out_idx - uart_ext->list_in_idx;
else
free_items = TX_QUEUE_LEN - uart_ext->list_in_idx + uart_ext->list_out_idx;
if(free_items > 1)
*((u16 *)param) = free_items - 1;
else
*((u16 *)param) = 0;
break;
case SHELL_IO_CTRL_RX_RESET:
uart_ext->rx_buff_rd_idx = 0;
uart_ext->rx_buff_wr_idx = 0;
break;
case SHELL_IO_CTRL_TX_RESET:
uart_ext->list_out_idx = 0;
uart_ext->list_in_idx = 0;
uart_ext->cur_packet = NULL;
uart_ext->packet_len = 0;
uart_ext->packet_tx_len = 0;
break;
case SHELL_IO_CTRL_FLUSH:
shell_uart_flush(uart_ext);
break;
case SHELL_IO_CTRL_TX_SUSPEND:
uart_ext->tx_suspend = 1;
// disable TX firstly, then set tx_stopped to 1.
bk_uart_disable_tx_interrupt(uart_ext->uart_id);
uart_ext->tx_stopped = 1; /* suspend, tx stopped after fifo empty.*/
extern bool bk_uart_is_tx_over(uart_id_t id);
while((bk_uart_is_tx_over(uart_ext->uart_id) == 0) && (param != 0))
{
}
bk_uart_set_enable_tx(uart_ext->uart_id, 0);
break;
case SHELL_IO_CTRL_TX_RESUME:
if(uart_ext->tx_suspend != 0)
{
bk_uart_set_enable_tx(uart_ext->uart_id, 1);
// resume tx.....
uart_ext->tx_suspend = 0;
shell_uart_tx_trigger(uart_ext);
}
break;
case SHELL_IO_CTRL_RX_SUSPEND:
bk_uart_set_enable_rx(uart_ext->uart_id, 0);
break;
case SHELL_IO_CTRL_RX_RESUME:
bk_uart_set_enable_rx(uart_ext->uart_id, 1);
break;
case SHELL_IO_CTRL_SET_UART_PORT:
if(param == NULL) {
return bFALSE;
}
u8 uart_port = *(u8 *)param;
uart_ext->uart_id = uart_port;
break;
case SHELL_IO_CTRL_GET_UART_PORT:
if(param == NULL)
{
return bFALSE;
}
*(u8 *)param = uart_ext->uart_id;
break;
case SHELL_IO_CTRL_GET_RX_STATUS:
if(param == NULL)
return bFALSE;
*((u16 *)param) = uart_ext->rx_over_flow;
uart_ext->rx_over_flow = 0; // clear it after read by user.
break;
case SHELL_IO_CTRL_SET_RX_ISR:
uart_ext->rx_indicate_callback = (rx_indicate_t)param;
if((rx_indicate_t)param != NULL)
{
bk_uart_enable_rx_interrupt(uart_ext->uart_id);
}
else
{
bk_uart_disable_rx_interrupt(uart_ext->uart_id);
}
break;
case SHELL_IO_CTRL_SET_TX_CMPL_ISR:
uart_ext->tx_complete_callback = (tx_complete_t)param;
break;
default:
return bFALSE;
break;
}
return bTRUE;
}
static bool_t shell_uart_close(shell_dev_t * shell_dev)
{
shell_uart_ext_t *uart_ext;
if(shell_dev == NULL)
return bFALSE;
uart_ext = (shell_uart_ext_t *)shell_dev->dev_ext;
bk_uart_disable_rx_interrupt(uart_ext->uart_id);
// bk_uart_disable_tx_interrupt(uart_ext->uart_id);
// call uart driver to register isr callback;
bk_uart_register_rx_isr(uart_ext->uart_id, NULL, NULL);
bk_uart_register_tx_isr(uart_ext->uart_id, NULL, NULL);
uart_ext->tx_complete_callback = NULL;
uart_ext->rx_indicate_callback = NULL;
return bTRUE;
}