459 lines
10 KiB
C
Executable File
459 lines
10 KiB
C
Executable File
/*
|
|
* Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the
|
|
* distribution.
|
|
* 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of
|
|
* its contributors may be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <ctype.h>
|
|
#include "kernel/os/os.h"
|
|
#include "atcmd/at_types.h"
|
|
#include <stdarg.h>
|
|
|
|
#include "serial.h"
|
|
#include "serial_debug.h"
|
|
|
|
#define SERIAL_CACHE_BUF_NUM 16
|
|
#define SERIAL_CACHE_BUF_SIZE (64/2)
|
|
|
|
#define ATCMDSEND_MAX_BUFF_SIZE (1024L+64L)
|
|
|
|
typedef enum {
|
|
SERIAL_STATE_STOP = 0,
|
|
SERIAL_STATE_START,
|
|
SERIAL_STATE_TERMINATE, /* to be terminated */
|
|
} serial_state;
|
|
|
|
#define ATCMDSEND_THREAD_STACK_SIZE (1 * 1024)
|
|
static OS_Thread_t g_atcmdSend_thread;
|
|
|
|
#define QLEN_ATCMDSEND 2
|
|
static OS_Queue_t q_atcmdSend;
|
|
static OS_Semaphore_t sem_atSend;
|
|
|
|
typedef struct atcmdSend_queueinf_def {
|
|
int datalen;
|
|
uint8_t *p_senddata;
|
|
} atcmdSend_queueinf_t;
|
|
|
|
typedef struct serial_priv {
|
|
UART_ID uartID;
|
|
serial_state state;
|
|
|
|
OS_Semaphore_t cmd_sem;
|
|
|
|
serial_cmd_exec_func cmd_exec;
|
|
|
|
struct {
|
|
volatile uint8_t cnt;
|
|
uint8_t widx;
|
|
uint8_t ridx;
|
|
uint8_t len[SERIAL_CACHE_BUF_NUM];
|
|
uint8_t buf[SERIAL_CACHE_BUF_NUM][SERIAL_CACHE_BUF_SIZE];
|
|
} cache;
|
|
} serial_priv_t;
|
|
|
|
static serial_priv_t g_serial;
|
|
void atcmd_sendTask(void *pvParameters);
|
|
|
|
/* Note: only support line end with "\r\n" or "\n", not support "\r" */
|
|
static void serial_rx_callback(void *arg)
|
|
{
|
|
serial_priv_t *serial;
|
|
UART_T *uart;
|
|
uint32_t cnt;
|
|
uint32_t idx;
|
|
uint32_t len;
|
|
uint8_t data;
|
|
|
|
uart = (UART_T *) arg;
|
|
serial = &g_serial;
|
|
|
|
cnt = serial->cache.cnt;
|
|
|
|
if (cnt < SERIAL_CACHE_BUF_NUM) {
|
|
idx = serial->cache.widx;
|
|
len = 0;
|
|
while (1) {
|
|
if (HAL_UART_IsRxReady(uart)) {
|
|
data = HAL_UART_GetRxData(uart);
|
|
|
|
serial->cache.buf[idx][len++] = data;
|
|
|
|
if (len >= SERIAL_CACHE_BUF_SIZE) {
|
|
serial->cache.len[idx] = len;
|
|
idx++;
|
|
if (idx >= SERIAL_CACHE_BUF_NUM) {
|
|
idx = 0;
|
|
}
|
|
cnt++;
|
|
serial->cache.widx = idx;
|
|
serial->cache.cnt = cnt;
|
|
OS_SemaphoreRelease(&serial->cmd_sem);
|
|
|
|
if (cnt >= SERIAL_CACHE_BUF_NUM) {
|
|
if (HAL_UART_IsRxReady(uart)) {
|
|
/* discard data */
|
|
while (HAL_UART_IsRxReady(uart)) {
|
|
data = HAL_UART_GetRxData(uart);
|
|
}
|
|
|
|
SERIAL_WARN("no buf for rx, discard received data\n");
|
|
}
|
|
|
|
break;
|
|
}
|
|
len = 0;
|
|
}
|
|
} else {
|
|
if (len > 0) {
|
|
serial->cache.len[idx] = len;
|
|
idx++;
|
|
if (idx >= SERIAL_CACHE_BUF_NUM) {
|
|
idx = 0;
|
|
}
|
|
cnt++;
|
|
serial->cache.widx = idx;
|
|
serial->cache.cnt = cnt;
|
|
OS_SemaphoreRelease(&serial->cmd_sem);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
/* discard data */
|
|
while (HAL_UART_IsRxReady(uart)) {
|
|
data = HAL_UART_GetRxData(uart);
|
|
}
|
|
SERIAL_WARN("no buf for rx, discard received data\n");
|
|
}
|
|
}
|
|
|
|
int serial_config(UART_ID uart_id, int baudrate, int data_bits, int parity,
|
|
int stop_bits, int hwfc)
|
|
{
|
|
UART_InitParam uart_param;
|
|
|
|
uart_param.baudRate = baudrate;
|
|
uart_param.parity = UART_PARITY_NONE;
|
|
uart_param.stopBits = UART_STOP_BITS_1;
|
|
uart_param.dataBits = UART_DATA_BITS_8;
|
|
uart_param.isAutoHwFlowCtrl = hwfc;
|
|
|
|
HAL_UART_Init(uart_id, &uart_param);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int serial_init(UART_ID uart_id, int baudrate, int data_bits, int parity,
|
|
int stop_bits, int hwfc)
|
|
{
|
|
serial_priv_t *serial;
|
|
|
|
switch (baudrate) {
|
|
case 115200:
|
|
baudrate = 115200;
|
|
break;
|
|
case 230400:
|
|
baudrate = 230400;
|
|
break;
|
|
case 460800:
|
|
baudrate = 460800;
|
|
break;
|
|
case 921600:
|
|
baudrate = 921600;
|
|
break;
|
|
default:
|
|
baudrate = 115200;
|
|
break;
|
|
}
|
|
|
|
serial_config(uart_id, baudrate, data_bits, parity, stop_bits, hwfc);
|
|
|
|
serial = &g_serial;
|
|
if (serial->state != SERIAL_STATE_STOP) {
|
|
SERIAL_ERR("serial state %d\n", serial->state);
|
|
return -1;
|
|
}
|
|
|
|
memset(serial, 0, sizeof(*serial));
|
|
serial->uartID = uart_id;
|
|
|
|
if (OS_SemaphoreCreate(&serial->cmd_sem, 0, OS_SEMAPHORE_MAX_COUNT) != OS_OK) {
|
|
SERIAL_ERR("create semaphore failed\n");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int serial_deinit(UART_ID uart_id)
|
|
{
|
|
serial_priv_t *serial;
|
|
|
|
serial = &g_serial;
|
|
|
|
HAL_UART_DeInit(uart_id);
|
|
|
|
OS_SemaphoreDelete(&serial->cmd_sem);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* NB: Make sure uart is inited before calling this function. */
|
|
int serial_start(void)
|
|
{
|
|
UART_T *uart;
|
|
serial_priv_t *serial;
|
|
|
|
serial = &g_serial;
|
|
|
|
uart = HAL_UART_GetInstance(serial->uartID);
|
|
HAL_UART_EnableRxCallback(serial->uartID, serial_rx_callback, uart);
|
|
serial->state = SERIAL_STATE_START;
|
|
|
|
/* start atcmd task */
|
|
if (OS_ThreadCreate(&g_atcmdSend_thread,
|
|
"atcmdSend",
|
|
atcmd_sendTask,
|
|
NULL,
|
|
OS_THREAD_PRIO_APP,
|
|
ATCMDSEND_THREAD_STACK_SIZE) != OS_OK) {
|
|
SERIAL_DBG("create serial task failed\n");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void serial_stop(void)
|
|
{
|
|
serial_priv_t *serial;
|
|
|
|
serial = &g_serial;
|
|
HAL_UART_DisableRxCallback(serial->uartID);
|
|
serial->state = SERIAL_STATE_STOP;
|
|
}
|
|
|
|
void serial_bezero_g_serial(void)
|
|
{
|
|
memset(&g_serial, 0, sizeof(serial_priv_t));
|
|
}
|
|
|
|
int serial_read(uint8_t *buf, int32_t size)
|
|
{
|
|
serial_priv_t *serial;
|
|
uint32_t cnt;
|
|
uint32_t idx;
|
|
int rlen = 0;
|
|
|
|
serial = &g_serial;
|
|
|
|
idx = serial->cache.ridx;
|
|
|
|
while (1) {
|
|
/*
|
|
if (OS_SemaphoreWait(&serial->cmd_sem, OS_WAIT_FOREVER) != OS_OK)
|
|
continue;
|
|
*/
|
|
if (OS_SemaphoreWait(&serial->cmd_sem, 10) != OS_OK)
|
|
break;
|
|
|
|
if (serial->state != SERIAL_STATE_START)
|
|
break;
|
|
|
|
arch_irq_disable();
|
|
cnt = serial->cache.cnt;
|
|
arch_irq_enable();
|
|
|
|
if (cnt > 0) {
|
|
rlen = serial->cache.len[idx];
|
|
if (rlen > size) {
|
|
return -1; /* buffer size is too small */
|
|
}
|
|
|
|
memcpy(buf, serial->cache.buf[idx], rlen);
|
|
|
|
idx++;
|
|
if (idx >= SERIAL_CACHE_BUF_NUM) {
|
|
idx = 0;
|
|
}
|
|
|
|
serial->cache.ridx = idx;
|
|
|
|
arch_irq_disable();
|
|
serial->cache.cnt--;
|
|
arch_irq_enable();
|
|
|
|
break;
|
|
} else {
|
|
SERIAL_WARN("no valid command\n");
|
|
return -2; /* no data */
|
|
}
|
|
}
|
|
|
|
return rlen;
|
|
}
|
|
|
|
int serial_write(uint8_t *buf, int32_t len)
|
|
{
|
|
serial_priv_t *serial;
|
|
|
|
serial = &g_serial;
|
|
return HAL_UART_Transmit_Poll(serial->uartID, buf, len);
|
|
}
|
|
|
|
void serial_disable(void)
|
|
{
|
|
serial_priv_t *serial;
|
|
|
|
serial = &g_serial;
|
|
if (serial->state == SERIAL_STATE_START) {
|
|
HAL_UART_DisableRxCallback(serial->uartID);
|
|
}
|
|
}
|
|
|
|
void serial_enable(void)
|
|
{
|
|
serial_priv_t *serial;
|
|
UART_T *uart;
|
|
|
|
serial = &g_serial;
|
|
if (serial->state == SERIAL_STATE_START) {
|
|
uart = HAL_UART_GetInstance(serial->uartID);
|
|
HAL_UART_EnableRxCallback(serial->uartID, serial_rx_callback, uart);
|
|
}
|
|
}
|
|
|
|
UART_ID serial_get_uart_id(void)
|
|
{
|
|
serial_priv_t *serial;
|
|
|
|
serial = &g_serial;
|
|
if (serial->state == SERIAL_STATE_START) {
|
|
return serial->uartID;
|
|
} else {
|
|
return UART_INVALID_ID;
|
|
}
|
|
}
|
|
|
|
int at_data_output(char *buf, int size)
|
|
{
|
|
atcmdSend_queueinf_t sendinf;
|
|
|
|
if (OS_SemaphoreWait(&sem_atSend, OS_WAIT_FOREVER) != OS_OK) {
|
|
SERIAL_DBG("wait semaphore failed\n");
|
|
return -1;
|
|
}
|
|
|
|
while (1) {
|
|
sendinf.p_senddata = malloc(ATCMDSEND_MAX_BUFF_SIZE);
|
|
if (sendinf.p_senddata == NULL) {
|
|
SERIAL_DBG("malloc buffer for atcmdSend failed\n");
|
|
OS_MSleep(200);
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
memset(sendinf.p_senddata, 0x00, ATCMDSEND_MAX_BUFF_SIZE);
|
|
memcpy(sendinf.p_senddata, buf, size);
|
|
sendinf.datalen = size;
|
|
SERIAL_DBG("data output xQueueSend!");
|
|
OS_QueueSend(&q_atcmdSend, &sendinf, 0);
|
|
return 0;
|
|
}
|
|
|
|
s32 at_dump(char *format, ...)
|
|
{
|
|
int len;
|
|
va_list vp;
|
|
atcmdSend_queueinf_t sendinf;
|
|
|
|
SERIAL_DBG("%s() start...\n", __func__);
|
|
if (OS_SemaphoreWait(&sem_atSend, OS_WAIT_FOREVER) != OS_OK) {
|
|
SERIAL_DBG("wait semaphore failed\n");
|
|
return -1;
|
|
}
|
|
|
|
while (1) {
|
|
sendinf.p_senddata = malloc(ATCMDSEND_MAX_BUFF_SIZE);
|
|
if (sendinf.p_senddata == NULL) {
|
|
SERIAL_DBG("malloc buffer for atcmdSend failed\n");
|
|
OS_MSleep(200);
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
memset(sendinf.p_senddata, 0x00, ATCMDSEND_MAX_BUFF_SIZE);
|
|
|
|
va_start(vp, format);
|
|
len = vsnprintf((char *)sendinf.p_senddata, ATCMDSEND_MAX_BUFF_SIZE, format, vp);
|
|
va_end(vp);
|
|
SERIAL_DBG("%s() len = %d strlen = %d\n", __func__, len,
|
|
strlen((char *)sendinf.p_senddata));
|
|
|
|
if (strlen((char *)sendinf.p_senddata) < len) {
|
|
SERIAL_DBG("dump info is too long!");
|
|
free(sendinf.p_senddata);
|
|
OS_SemaphoreRelease(&sem_atSend);
|
|
return -1;
|
|
}
|
|
sendinf.datalen = len;
|
|
SERIAL_DBG("at_dump xQueueSend!");
|
|
OS_QueueSend(&q_atcmdSend, &sendinf, 0);
|
|
return 0;
|
|
}
|
|
|
|
void atcmd_sendTask(void *pvParameters)
|
|
{
|
|
atcmdSend_queueinf_t sendinf;
|
|
|
|
OS_QueueCreate(&q_atcmdSend, QLEN_ATCMDSEND, sizeof(atcmdSend_queueinf_t));
|
|
|
|
if (OS_SemaphoreCreate(&sem_atSend, QLEN_ATCMDSEND, QLEN_ATCMDSEND) != OS_OK) {
|
|
SERIAL_DBG("create semaphore failed\n");
|
|
return;
|
|
}
|
|
|
|
while (1) {
|
|
if (OS_QueueReceive(&q_atcmdSend, &sendinf, OS_WAIT_FOREVER) != OS_OK)
|
|
continue;
|
|
|
|
SERIAL_DBG("%s() datalen = %d...\n", __func__, sendinf.datalen);
|
|
|
|
serial_write(sendinf.p_senddata, sendinf.datalen);
|
|
free(sendinf.p_senddata);
|
|
OS_SemaphoreRelease(&sem_atSend);
|
|
}
|
|
return;
|
|
}
|