1197 lines
32 KiB
C
1197 lines
32 KiB
C
/*----------------------------------------------------------------------------
|
|
* Tencent is pleased to support the open source community by making TencentOS
|
|
* available.
|
|
*
|
|
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
|
|
* If you have downloaded a copy of the TencentOS binary from Tencent, please
|
|
* note that the TencentOS binary is licensed under the BSD 3-Clause License.
|
|
*
|
|
* If you have downloaded a copy of the TencentOS source code from Tencent,
|
|
* please note that TencentOS source code is licensed under the BSD 3-Clause
|
|
* License, except for the third-party components listed below which are
|
|
* subject to different license terms. Your integration of TencentOS into your
|
|
* own projects may require compliance with the BSD 3-Clause License, as well
|
|
* as the other licenses applicable to the third-party components included
|
|
* within TencentOS.
|
|
*---------------------------------------------------------------------------*/
|
|
|
|
/*
|
|
Note:
|
|
If you find that the AT framework occasionally loses characters,
|
|
this may be caused by the unnecessary critical section of at_channel,
|
|
so you can remove the critical section of ring_queue in tos_ring_queue.c.
|
|
Once you remove, ring queue becomes only a data structure,
|
|
you must use critical section or mutex to protect the data in ring_queue.
|
|
*/
|
|
|
|
#include "tos_at.h"
|
|
|
|
__API__ int tos_at_global_lock_pend(at_agent_t *at_agent)
|
|
{
|
|
if (tos_mutex_pend(&at_agent->global_lock) != K_ERR_NONE) {
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
__API__ int tos_at_global_lock_post(at_agent_t *at_agent)
|
|
{
|
|
if (tos_mutex_post(&at_agent->global_lock) != K_ERR_NONE) {
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
__STATIC__ int at_uart_getchar_from_fifo(at_agent_t *at_agent, uint8_t *data)
|
|
{
|
|
TOS_CPU_CPSR_ALLOC();
|
|
k_err_t err;
|
|
|
|
TOS_CPU_INT_DISABLE();
|
|
err = tos_chr_fifo_pop(&at_agent->uart_rx_fifo, data);
|
|
TOS_CPU_INT_ENABLE();
|
|
|
|
return err;
|
|
}
|
|
|
|
__STATIC__ int at_uart_getchar(at_agent_t *at_agent, uint8_t *data, k_tick_t timeout)
|
|
{
|
|
#if AT_INPUT_TYPE_FRAME_EN
|
|
at_frame_len_mail_t frame_len_mail;
|
|
size_t mail_size;
|
|
|
|
if (at_agent->fifo_available_len == 0) {
|
|
if (tos_mail_q_pend(&at_agent->uart_rx_frame_mail, &frame_len_mail, &mail_size, timeout) != K_ERR_NONE) {
|
|
return -1;
|
|
}
|
|
at_agent->fifo_available_len = frame_len_mail.frame_len;
|
|
}
|
|
|
|
if (at_uart_getchar_from_fifo(at_agent, data) != K_ERR_NONE) {
|
|
return -1;
|
|
}
|
|
|
|
at_agent->fifo_available_len -= 1;
|
|
return 0;
|
|
#elif AT_INPUT_SIMULATE_IDLE_EN
|
|
if (tos_chr_fifo_is_empty(&at_agent->uart_rx_fifo)) {
|
|
if (tos_sem_pend(&at_agent->uart_rx_sem, timeout) != K_ERR_NONE) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (at_uart_getchar_from_fifo(at_agent, data) != K_ERR_NONE) {
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
|
|
#else
|
|
tos_stopwatch_delay(1);
|
|
|
|
if (tos_sem_pend(&at_agent->uart_rx_sem, timeout) != K_ERR_NONE) {
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
the uart_rx_fifo is only read by at_parser task,
|
|
and it will be written in usart interrupt handler,
|
|
so it is more effective to use critical sections.
|
|
*/
|
|
|
|
// if (tos_mutex_pend(&at_agent->uart_rx_lock) != K_ERR_NONE) {
|
|
// return -1;
|
|
// }
|
|
|
|
if (at_uart_getchar_from_fifo(at_agent, data) != K_ERR_NONE) {
|
|
return -1;
|
|
}
|
|
|
|
// tos_mutex_post(&at_agent->uart_rx_lock);
|
|
|
|
return 0;
|
|
#endif /* AT_INPUT_TYPE_FRAME_EN */
|
|
}
|
|
|
|
__STATIC__ at_event_t *at_event_do_get(at_agent_t *at_agent, char *buffer, size_t buffer_len)
|
|
{
|
|
int i = 0;
|
|
at_event_t *event_table = K_NULL, *event = K_NULL;
|
|
size_t event_table_size = 0, event_len;
|
|
|
|
event_table = at_agent->event_table;
|
|
event_table_size = at_agent->event_table_size;
|
|
|
|
for (i = 0; i < event_table_size; ++i) {
|
|
event = &event_table[i];
|
|
event_len = strlen(event->event_header);
|
|
|
|
if (buffer_len < event_len) {
|
|
continue;
|
|
}
|
|
|
|
if (strncmp(event->event_header, buffer, event_len) == 0) {
|
|
return event;
|
|
}
|
|
}
|
|
|
|
return K_NULL;
|
|
}
|
|
|
|
__STATIC__ at_event_t *at_get_event(at_agent_t *at_agent)
|
|
{
|
|
char *buffer;
|
|
size_t buffer_len;
|
|
at_cache_t *at_cache = K_NULL;
|
|
|
|
at_cache = &at_agent->recv_cache;
|
|
|
|
buffer = (char *)at_cache->buffer;
|
|
buffer_len = at_cache->recv_len;
|
|
|
|
return at_event_do_get(at_agent, buffer, buffer_len);
|
|
}
|
|
|
|
__API__ int tos_at_uart_read(at_agent_t *at_agent, uint8_t *buffer, size_t buffer_len)
|
|
{
|
|
uint8_t data;
|
|
size_t read_len = 0;
|
|
|
|
while (K_TRUE) {
|
|
if (at_uart_getchar(at_agent, &data, TOS_TIME_FOREVER) != 0) {
|
|
return read_len;
|
|
}
|
|
|
|
buffer[read_len++] = data;
|
|
|
|
if (read_len == buffer_len) {
|
|
return buffer_len;
|
|
}
|
|
}
|
|
}
|
|
|
|
__API__ int tos_at_uart_readline(at_agent_t *at_agent, uint8_t *buffer, size_t buffer_len)
|
|
{
|
|
uint8_t data;
|
|
size_t read_len = 0;
|
|
|
|
while (K_TRUE) {
|
|
if (at_uart_getchar(at_agent, &data, TOS_TIME_FOREVER) != 0) {
|
|
return read_len;
|
|
}
|
|
|
|
buffer[read_len++] = data;
|
|
|
|
if (data == '\n') {
|
|
return read_len;
|
|
} else if (read_len == buffer_len) {
|
|
return buffer_len;
|
|
}
|
|
}
|
|
}
|
|
|
|
__API__ int tos_at_uart_drain(at_agent_t *at_agent, uint8_t *buffer, size_t buffer_len)
|
|
{
|
|
uint8_t data;
|
|
size_t read_len = 0;
|
|
|
|
while (K_TRUE) {
|
|
if (at_uart_getchar(at_agent, &data, TOS_TIME_NOWAIT) != 0) {
|
|
return read_len;
|
|
}
|
|
|
|
buffer[read_len++] = data;
|
|
|
|
if (read_len == buffer_len) {
|
|
return buffer_len;
|
|
}
|
|
}
|
|
}
|
|
|
|
__STATIC__ int at_is_echo_expect(at_agent_t *at_agent)
|
|
{
|
|
char *recv_buffer, *expect;
|
|
size_t recv_buffer_len, expect_len;
|
|
at_echo_t *at_echo = K_NULL;
|
|
at_cache_t *at_cache = K_NULL;
|
|
|
|
at_echo = at_agent->echo;
|
|
|
|
if (!at_echo || !at_echo->echo_expect) {
|
|
return 0;
|
|
}
|
|
|
|
at_cache = &at_agent->recv_cache;
|
|
|
|
recv_buffer = (char *)at_cache->buffer;
|
|
recv_buffer_len = at_cache->recv_len;
|
|
|
|
expect = at_echo->echo_expect;
|
|
expect_len = strlen(expect);
|
|
|
|
if (recv_buffer_len < expect_len) {
|
|
return 0;
|
|
}
|
|
|
|
if (at_echo->__is_fuzzy_match) {
|
|
if (strstr(recv_buffer, expect) != NULL) {
|
|
return 0;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
if (strncmp(expect, recv_buffer, expect_len) == 0) {
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
__STATIC__ at_parse_status_t at_uart_line_parse(at_agent_t *at_agent)
|
|
{
|
|
size_t curr_len = 0;
|
|
uint8_t data, last_data = 0;
|
|
at_cache_t *recv_cache = K_NULL;
|
|
|
|
recv_cache = &at_agent->recv_cache;
|
|
|
|
recv_cache->recv_len = 0;
|
|
memset(recv_cache->buffer, 0, recv_cache->buffer_size);
|
|
|
|
while (K_TRUE) {
|
|
if (at_uart_getchar(at_agent, &data, TOS_TIME_FOREVER) != 0) {
|
|
continue;
|
|
}
|
|
|
|
// if (data == '\0') {
|
|
// continue;
|
|
// }
|
|
|
|
if (curr_len < recv_cache->buffer_size) {
|
|
recv_cache->buffer[curr_len++] = data;
|
|
recv_cache->recv_len = curr_len;
|
|
} else {
|
|
recv_cache->buffer[recv_cache->buffer_size - 1] = '\0';
|
|
return AT_PARSE_STATUS_OVERFLOW;
|
|
}
|
|
|
|
if (at_get_event(at_agent) != K_NULL) {
|
|
return AT_PARSE_STATUS_EVENT;
|
|
}
|
|
|
|
if (at_is_echo_expect(at_agent)) {
|
|
return AT_PARSE_STATUS_EXPECT;
|
|
}
|
|
|
|
AT_LOG("recv_cache:[%s](%d)\r\n", recv_cache->buffer, recv_cache->recv_len);
|
|
|
|
if (strstr((char*)recv_cache->buffer, "OK")) {
|
|
return AT_PARSE_STATUS_OK;
|
|
} else if (strstr((char*)recv_cache->buffer, "FAIL")) {
|
|
return AT_PARSE_STATUS_FAIL;
|
|
} else if (strstr((char*)recv_cache->buffer, "ERROR")) {
|
|
return AT_PARSE_STATUS_ERROR;
|
|
}
|
|
|
|
if (data == '\n' && last_data == '\r') { // 0xd 0xa
|
|
// curr_len -= 1;
|
|
// recv_cache->buffer[curr_len - 1] = '\n';
|
|
// recv_cache->recv_len = curr_len;
|
|
|
|
if (curr_len == 2) { // only a blank newline, ignore
|
|
last_data = 0;
|
|
curr_len = 0;
|
|
recv_cache->recv_len = 0;
|
|
continue;
|
|
}
|
|
|
|
return AT_PARSE_STATUS_NEWLINE;
|
|
}
|
|
|
|
last_data = data;
|
|
}
|
|
}
|
|
|
|
__STATIC__ void at_echo_buffer_copy(at_cache_t *at_cache, at_echo_t *echo)
|
|
{
|
|
uint8_t *recv_buffer = K_NULL;
|
|
size_t recv_buffer_len, copy_len, remain_len;
|
|
|
|
recv_buffer = at_cache->buffer;
|
|
recv_buffer_len = at_cache->recv_len;
|
|
|
|
remain_len = echo->buffer_size - echo->__w_idx;
|
|
if (remain_len == 0) {
|
|
return;
|
|
}
|
|
|
|
copy_len = remain_len < recv_buffer_len ? remain_len : recv_buffer_len;
|
|
memcpy(echo->buffer + echo->__w_idx, recv_buffer, copy_len);
|
|
echo->__w_idx += copy_len;
|
|
|
|
++echo->line_num;
|
|
}
|
|
|
|
__STATIC__ void at_parser(void *arg)
|
|
{
|
|
at_echo_t *at_echo = K_NULL;
|
|
at_event_t *at_event = K_NULL;
|
|
at_cache_t *recv_cache = K_NULL;
|
|
at_parse_status_t at_parse_status;
|
|
|
|
at_agent_t *at_agent = (at_agent_t *)arg;
|
|
|
|
recv_cache = &at_agent->recv_cache;
|
|
|
|
while (K_TRUE) {
|
|
|
|
at_parse_status = at_uart_line_parse(at_agent);
|
|
|
|
AT_LOG("at line parser end!(%d)\r\n", at_parse_status);
|
|
|
|
tos_kprintln("--->%s", recv_cache->buffer);
|
|
|
|
if (at_parse_status == AT_PARSE_STATUS_OVERFLOW) {
|
|
tos_kprintln("AT parse overflow!");
|
|
continue;
|
|
}
|
|
|
|
if (at_parse_status == AT_PARSE_STATUS_EVENT) {
|
|
at_event = at_get_event(at_agent);
|
|
if (at_event && at_event->event_callback) {
|
|
at_event->event_callback();
|
|
}
|
|
continue;
|
|
}
|
|
|
|
at_echo = at_agent->echo;
|
|
if (!at_echo) {
|
|
continue;
|
|
}
|
|
|
|
if (at_echo->buffer) {
|
|
at_echo_buffer_copy(recv_cache, at_echo);
|
|
}
|
|
|
|
if (at_parse_status == AT_PARSE_STATUS_EXPECT) {
|
|
at_echo->status = AT_ECHO_STATUS_EXPECT;
|
|
if (at_echo->__is_expecting) {
|
|
tos_sem_post(&at_echo->__expect_notify);
|
|
}
|
|
} else if (at_parse_status == AT_PARSE_STATUS_OK) {
|
|
at_echo->status = AT_ECHO_STATUS_OK;
|
|
if (!at_echo->__is_expecting) {
|
|
tos_sem_post(&at_echo->__status_set_notify);
|
|
}
|
|
} else if (at_parse_status == AT_PARSE_STATUS_FAIL) {
|
|
at_echo->status = AT_ECHO_STATUS_FAIL;
|
|
if (!at_echo->__is_expecting) {
|
|
tos_sem_post(&at_echo->__status_set_notify);
|
|
}
|
|
} else if (at_parse_status == AT_PARSE_STATUS_ERROR) {
|
|
at_echo->status = AT_ECHO_STATUS_ERROR;
|
|
if (!at_echo->__is_expecting) {
|
|
tos_sem_post(&at_echo->__status_set_notify);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
__STATIC__ int at_uart_send(at_agent_t *at_agent, const uint8_t *buf, size_t size, uint32_t timeout)
|
|
{
|
|
int ret;
|
|
|
|
tos_mutex_pend(&at_agent->uart_tx_lock);
|
|
ret = tos_hal_uart_write(&at_agent->uart, buf, size, timeout);
|
|
tos_mutex_post(&at_agent->uart_tx_lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
__API__ int tos_at_echo_create(at_echo_t *echo, char *buffer, size_t buffer_size, char *echo_expect)
|
|
{
|
|
if (!echo) {
|
|
return -1;
|
|
}
|
|
|
|
if (buffer) {
|
|
memset(buffer, 0, buffer_size);
|
|
}
|
|
|
|
echo->buffer = buffer;
|
|
echo->buffer_size = buffer_size;
|
|
echo->echo_expect = echo_expect;
|
|
echo->line_num = 0;
|
|
echo->status = AT_ECHO_STATUS_NONE;
|
|
echo->__w_idx = 0;
|
|
echo->__is_expecting = K_FALSE;
|
|
echo->__is_fuzzy_match = K_FALSE;
|
|
return 0;
|
|
}
|
|
|
|
__API__ int tos_at_echo_fuzzy_matching_create(at_echo_t *echo, char *buffer, size_t buffer_size, char *echo_expect_contains)
|
|
{
|
|
if (!echo) {
|
|
return -1;
|
|
}
|
|
|
|
if (buffer) {
|
|
memset(buffer, 0, buffer_size);
|
|
}
|
|
|
|
echo->buffer = buffer;
|
|
echo->buffer_size = buffer_size;
|
|
echo->echo_expect = echo_expect_contains;
|
|
echo->line_num = 0;
|
|
echo->status = AT_ECHO_STATUS_NONE;
|
|
echo->__w_idx = 0;
|
|
echo->__is_expecting = K_FALSE;
|
|
echo->__is_fuzzy_match = K_TRUE;
|
|
return 0;
|
|
}
|
|
|
|
__STATIC_INLINE__ void at_echo_flush(at_echo_t *echo)
|
|
{
|
|
echo->line_num = 0;
|
|
echo->status = AT_ECHO_STATUS_NONE;
|
|
echo->__w_idx = 0;
|
|
}
|
|
|
|
__STATIC_INLINE__ void at_echo_attach(at_agent_t *at_agent, at_echo_t *echo)
|
|
{
|
|
at_echo_flush(echo);
|
|
at_agent->echo = echo;
|
|
}
|
|
|
|
__API__ int tos_at_raw_data_send(at_agent_t *at_agent, at_echo_t *echo, uint32_t timeout, const uint8_t *buf, size_t size)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (!echo) {
|
|
return -1;
|
|
}
|
|
|
|
if (tos_at_global_lock_pend(at_agent) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
at_echo_attach(at_agent, echo);
|
|
|
|
ret = at_uart_send(at_agent, buf, size, 0xFFFF);
|
|
|
|
tos_task_delay(tos_millisec2tick(timeout));
|
|
|
|
at_agent->echo = K_NULL;
|
|
|
|
tos_at_global_lock_post(at_agent);
|
|
|
|
return ret;
|
|
}
|
|
|
|
__API__ int tos_at_raw_data_send_until(at_agent_t *at_agent, at_echo_t *echo, uint32_t timeout, const uint8_t *buf, size_t size)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (!echo || !echo->echo_expect) {
|
|
return -1;
|
|
}
|
|
|
|
if (tos_at_global_lock_pend(at_agent) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (tos_sem_create(&echo->__expect_notify, 0) != K_ERR_NONE) {
|
|
tos_at_global_lock_post(at_agent);
|
|
return -1;
|
|
}
|
|
echo->__is_expecting = K_TRUE;
|
|
at_echo_attach(at_agent, echo);
|
|
|
|
ret = at_uart_send(at_agent, buf, size, 0xFFFF);
|
|
|
|
if (tos_sem_pend(&echo->__expect_notify, tos_millisec2tick(timeout)) != K_ERR_NONE) {
|
|
ret = -1;
|
|
}
|
|
|
|
tos_sem_destroy(&echo->__expect_notify);
|
|
|
|
at_agent->echo = K_NULL;
|
|
|
|
tos_at_global_lock_post(at_agent);
|
|
|
|
return ret;
|
|
}
|
|
|
|
__STATIC__ int at_cmd_do_exec(at_agent_t *at_agent, const char *format, va_list args)
|
|
{
|
|
size_t cmd_len = 0;
|
|
|
|
// if (tos_mutex_pend(&at_agent->cmd_buf_lock) != K_ERR_NONE) {
|
|
// return -1;
|
|
// }
|
|
|
|
cmd_len = vsnprintf(at_agent->cmd_buf, AT_CMD_BUFFER_SIZE, format, args);
|
|
|
|
tos_kprintln("AT CMD:\n%s\n", at_agent->cmd_buf);
|
|
|
|
at_uart_send(at_agent, (uint8_t *)at_agent->cmd_buf, cmd_len, 0xFFFF);
|
|
|
|
// tos_mutex_post(&at_agent->cmd_buf_lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
__API__ int tos_at_cmd_exec(at_agent_t *at_agent, at_echo_t *echo, uint32_t timeout, const char *cmd, ...)
|
|
{
|
|
int ret = 0;
|
|
va_list args;
|
|
|
|
if (!echo) {
|
|
return -1;
|
|
}
|
|
|
|
if (tos_at_global_lock_pend(at_agent) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (tos_sem_create(&echo->__status_set_notify, 0) != K_ERR_NONE) {
|
|
tos_at_global_lock_post(at_agent);
|
|
return -1;
|
|
}
|
|
|
|
at_echo_attach(at_agent, echo);
|
|
|
|
va_start(args, cmd);
|
|
ret = at_cmd_do_exec(at_agent, cmd, args);
|
|
va_end(args);
|
|
|
|
if (ret != 0) {
|
|
at_agent->echo = K_NULL;
|
|
tos_at_global_lock_post(at_agent);
|
|
return -1;
|
|
}
|
|
|
|
if (tos_sem_pend(&echo->__status_set_notify, tos_millisec2tick(timeout)) != K_ERR_NONE) {
|
|
ret = -1;
|
|
}
|
|
|
|
tos_sem_destroy(&echo->__status_set_notify);
|
|
|
|
at_agent->echo = K_NULL;
|
|
|
|
tos_at_global_lock_post(at_agent);
|
|
|
|
return ret;
|
|
}
|
|
|
|
__API__ int tos_at_cmd_exec_until(at_agent_t *at_agent, at_echo_t *echo, uint32_t timeout, const char *cmd, ...)
|
|
{
|
|
int ret = 0;
|
|
va_list args;
|
|
|
|
if (!echo || !echo->echo_expect) {
|
|
return -1;
|
|
}
|
|
|
|
if (tos_at_global_lock_pend(at_agent) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (tos_sem_create(&echo->__expect_notify, 0) != K_ERR_NONE) {
|
|
tos_at_global_lock_post(at_agent);
|
|
return -1;
|
|
}
|
|
echo->__is_expecting = K_TRUE;
|
|
at_echo_attach(at_agent, echo);
|
|
|
|
va_start(args, cmd);
|
|
ret = at_cmd_do_exec(at_agent, cmd, args);
|
|
va_end(args);
|
|
|
|
if (ret != 0) {
|
|
at_agent->echo = K_NULL;
|
|
tos_at_global_lock_post(at_agent);
|
|
return -1;
|
|
}
|
|
|
|
if (tos_sem_pend(&echo->__expect_notify, tos_millisec2tick(timeout)) != K_ERR_NONE) {
|
|
ret = -1;
|
|
}
|
|
|
|
tos_sem_destroy(&echo->__expect_notify);
|
|
|
|
at_agent->echo = K_NULL;
|
|
|
|
tos_at_global_lock_post(at_agent);
|
|
|
|
return ret;
|
|
}
|
|
|
|
__STATIC__ int at_recv_cache_init(at_agent_t *at_agent)
|
|
{
|
|
uint8_t *buffer = K_NULL;
|
|
|
|
buffer = tos_mmheap_alloc(AT_RECV_CACHE_SIZE);
|
|
if (!buffer) {
|
|
at_agent->recv_cache.buffer = K_NULL;
|
|
return - 1;
|
|
}
|
|
|
|
at_agent->recv_cache.buffer = buffer;
|
|
at_agent->recv_cache.buffer_size = AT_RECV_CACHE_SIZE;
|
|
at_agent->recv_cache.recv_len = 0;
|
|
return 0;
|
|
}
|
|
|
|
__STATIC__ void at_recv_cache_deinit(at_agent_t *at_agent)
|
|
{
|
|
uint8_t *buffer = K_NULL;
|
|
|
|
buffer = at_agent->recv_cache.buffer;
|
|
if (buffer) {
|
|
tos_mmheap_free(buffer);
|
|
}
|
|
|
|
at_agent->recv_cache.buffer = K_NULL;
|
|
at_agent->recv_cache.buffer_size = 0;
|
|
at_agent->recv_cache.recv_len = 0;
|
|
}
|
|
|
|
__STATIC__ at_data_channel_t *at_channel_get(at_agent_t *at_agent, int channel_id, int is_alloc)
|
|
{
|
|
/*
|
|
if is_alloc is K_TRUE, means we are allocating a channel with certain id,
|
|
data_channel[channel_id] must be free if return none K_NULL.
|
|
otherwise if is_alloc is K_FALSE, means we are trying to get a channel with
|
|
certain id, data_channel[channel_id] must be not free if return none K_NULL.
|
|
*/
|
|
at_data_channel_t *data_channel = K_NULL;
|
|
|
|
if (channel_id < 0 || channel_id >= AT_DATA_CHANNEL_NUM) {
|
|
return K_NULL;
|
|
}
|
|
|
|
data_channel = &at_agent->data_channel[channel_id];
|
|
|
|
if (is_alloc && data_channel->is_free) {
|
|
return data_channel;
|
|
}
|
|
|
|
if (!is_alloc && !data_channel->is_free) {
|
|
return data_channel;
|
|
}
|
|
|
|
return K_NULL;
|
|
}
|
|
|
|
__API__ int tos_at_channel_read(at_agent_t *at_agent, int channel_id, uint8_t *buffer, size_t buffer_len)
|
|
{
|
|
int read_len;
|
|
size_t total_read_len = 0;
|
|
at_data_channel_t *data_channel = K_NULL;
|
|
|
|
data_channel = at_channel_get(at_agent, channel_id, K_FALSE);
|
|
if (!data_channel || data_channel->status == AT_CHANNEL_STATUS_BROKEN) {
|
|
return -1;
|
|
}
|
|
|
|
while (K_TRUE) {
|
|
if (tos_mutex_pend(&data_channel->rx_lock) != K_ERR_NONE) {
|
|
return total_read_len;
|
|
}
|
|
|
|
read_len = tos_chr_fifo_pop_stream(&data_channel->rx_fifo, buffer, buffer_len);
|
|
|
|
tos_mutex_post(&data_channel->rx_lock);
|
|
|
|
total_read_len += read_len;
|
|
if (total_read_len < buffer_len) {
|
|
continue;
|
|
} else {
|
|
return buffer_len;
|
|
}
|
|
}
|
|
}
|
|
|
|
__API__ int tos_at_channel_read_timed(at_agent_t *at_agent, int channel_id, uint8_t *buffer, size_t buffer_len, uint32_t timeout)
|
|
{
|
|
int read_len = 0;
|
|
size_t total_read_len = 0;
|
|
k_tick_t remain_tick;
|
|
at_data_channel_t *data_channel = K_NULL;
|
|
|
|
data_channel = at_channel_get(at_agent, channel_id, K_FALSE);
|
|
if (!data_channel || data_channel->status == AT_CHANNEL_STATUS_BROKEN) {
|
|
return -1;
|
|
}
|
|
|
|
remain_tick = tos_millisec2tick(timeout);
|
|
tos_stopwatch_countdown(&data_channel->timer, remain_tick);
|
|
|
|
while (!tos_stopwatch_is_expired(&data_channel->timer)) {
|
|
remain_tick = tos_stopwatch_remain(&data_channel->timer);
|
|
if (remain_tick == (k_tick_t)0u) {
|
|
return total_read_len;
|
|
}
|
|
|
|
if (tos_mutex_pend_timed(&data_channel->rx_lock, remain_tick) != K_ERR_NONE) {
|
|
return total_read_len;
|
|
}
|
|
|
|
read_len = tos_chr_fifo_pop_stream(&data_channel->rx_fifo, buffer + total_read_len, buffer_len - total_read_len);
|
|
|
|
tos_mutex_post(&data_channel->rx_lock);
|
|
|
|
if (read_len == 0) {
|
|
remain_tick = tos_stopwatch_remain(&data_channel->timer);
|
|
tos_sem_pend(&data_channel->rx_sem, remain_tick);
|
|
}
|
|
|
|
total_read_len += read_len;
|
|
if (total_read_len < buffer_len) {
|
|
continue;
|
|
} else {
|
|
return buffer_len;
|
|
}
|
|
}
|
|
|
|
return total_read_len;
|
|
}
|
|
|
|
__API__ int tos_at_channel_write(at_agent_t *at_agent, int channel_id, uint8_t *buffer, size_t buffer_len)
|
|
{
|
|
int ret;
|
|
at_data_channel_t *data_channel = K_NULL;
|
|
|
|
data_channel = at_channel_get(at_agent, channel_id, K_FALSE);
|
|
if (!data_channel) {
|
|
return -1;
|
|
}
|
|
|
|
if (tos_mutex_pend(&data_channel->rx_lock) != K_ERR_NONE) {
|
|
return -1;
|
|
}
|
|
|
|
ret = tos_chr_fifo_push_stream(&data_channel->rx_fifo, buffer, buffer_len);
|
|
|
|
tos_mutex_post(&data_channel->rx_lock);
|
|
tos_sem_post(&data_channel->rx_sem);
|
|
|
|
return ret;
|
|
}
|
|
|
|
__STATIC_INLINE__ int at_channel_construct(at_data_channel_t *data_channel, const char *ip, const char *port, size_t socket_buffer_size)
|
|
{
|
|
uint8_t *fifo_buffer = K_NULL;
|
|
|
|
fifo_buffer = tos_mmheap_alloc(socket_buffer_size);
|
|
|
|
if (!fifo_buffer) {
|
|
return -1;
|
|
}
|
|
|
|
if (tos_sem_create_max(&data_channel->rx_sem, 0, 1) != K_ERR_NONE) {
|
|
goto errout;
|
|
}
|
|
|
|
if (tos_mutex_create(&data_channel->rx_lock) != K_ERR_NONE) {
|
|
goto errout;
|
|
}
|
|
|
|
if (tos_stopwatch_create(&data_channel->timer) != K_ERR_NONE) {
|
|
goto errout;
|
|
}
|
|
|
|
data_channel->rx_fifo_buffer = fifo_buffer;
|
|
tos_chr_fifo_create(&data_channel->rx_fifo, fifo_buffer, socket_buffer_size);
|
|
data_channel->remote_ip = ip;
|
|
data_channel->remote_port = port;
|
|
|
|
data_channel->is_free = K_FALSE;
|
|
data_channel->status = AT_CHANNEL_STATUS_WORKING;
|
|
|
|
return 0;
|
|
|
|
errout:
|
|
tos_mmheap_free(fifo_buffer);
|
|
return -1;
|
|
}
|
|
|
|
__API__ int tos_at_channel_alloc_id(at_agent_t *at_agent, int channel_id, const char *ip, const char *port)
|
|
{
|
|
at_data_channel_t *data_channel = K_NULL;
|
|
size_t socket_buffer_size = 0;
|
|
|
|
data_channel = at_channel_get(at_agent, channel_id, K_TRUE);
|
|
if (!data_channel) {
|
|
return -1;
|
|
}
|
|
|
|
socket_buffer_size = AT_DATA_CHANNEL_FIFO_BUFFER_DEFAULT_SIZE;
|
|
if (at_channel_construct(data_channel, ip, port, socket_buffer_size) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
return channel_id;
|
|
}
|
|
|
|
__API__ int tos_at_channel_alloc(at_agent_t *at_agent, const char *ip, const char *port)
|
|
{
|
|
int id = 0;
|
|
at_data_channel_t *data_channel = K_NULL;
|
|
size_t socket_buffer_size = 0;
|
|
|
|
for (id = 0; id < AT_DATA_CHANNEL_NUM; ++id) {
|
|
data_channel = &at_agent->data_channel[id];
|
|
if (data_channel->is_free) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (id == AT_DATA_CHANNEL_NUM || !data_channel) {
|
|
return -1;
|
|
}
|
|
|
|
socket_buffer_size = AT_DATA_CHANNEL_FIFO_BUFFER_DEFAULT_SIZE;
|
|
if (at_channel_construct(data_channel, ip, port, socket_buffer_size) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
return id;
|
|
}
|
|
|
|
__API__ int tos_at_channel_alloc_with_size(at_agent_t *at_agent, const char *ip, const char *port, size_t socket_buffer_size)
|
|
{
|
|
int id = 0;
|
|
at_data_channel_t *data_channel = K_NULL;
|
|
|
|
for (id = 0; id < AT_DATA_CHANNEL_NUM; ++id) {
|
|
data_channel = &at_agent->data_channel[id];
|
|
if (data_channel->is_free) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (id == AT_DATA_CHANNEL_NUM || !data_channel) {
|
|
return -1;
|
|
}
|
|
|
|
if (at_channel_construct(data_channel, ip, port, socket_buffer_size) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
return id;
|
|
}
|
|
|
|
__API__ int tos_at_channel_free(at_agent_t *at_agent, int channel_id)
|
|
{
|
|
at_data_channel_t *data_channel = K_NULL;
|
|
|
|
data_channel = at_channel_get(at_agent, channel_id, K_FALSE);
|
|
if (!data_channel) {
|
|
return -1;
|
|
}
|
|
|
|
tos_sem_destroy(&data_channel->rx_sem);
|
|
|
|
tos_mutex_destroy(&data_channel->rx_lock);
|
|
|
|
tos_stopwatch_destroy(&data_channel->timer);
|
|
|
|
tos_mmheap_free(data_channel->rx_fifo_buffer);
|
|
tos_chr_fifo_destroy(&data_channel->rx_fifo);
|
|
|
|
memset(data_channel, 0, sizeof(at_data_channel_t));
|
|
|
|
data_channel->is_free = K_TRUE;
|
|
data_channel->status = AT_CHANNEL_STATUS_HANGING;
|
|
|
|
return 0;
|
|
}
|
|
|
|
__API__ int tos_at_channel_set_broken(at_agent_t *at_agent, int channel_id)
|
|
{
|
|
at_data_channel_t *data_channel = K_NULL;
|
|
|
|
data_channel = at_channel_get(at_agent, channel_id, K_FALSE);
|
|
if (!data_channel) {
|
|
return -1;
|
|
}
|
|
|
|
data_channel->status = AT_CHANNEL_STATUS_BROKEN;
|
|
return 0;
|
|
}
|
|
|
|
__API__ int tos_at_channel_is_working(at_agent_t *at_agent, int channel_id)
|
|
{
|
|
at_data_channel_t *data_channel = K_NULL;
|
|
|
|
data_channel = at_channel_get(at_agent, channel_id, K_FALSE);
|
|
return data_channel && data_channel->status == AT_CHANNEL_STATUS_WORKING;
|
|
}
|
|
|
|
__STATIC__ void at_channel_init(at_agent_t *at_agent)
|
|
{
|
|
int i = 0;
|
|
|
|
for (i = 0; i < AT_DATA_CHANNEL_NUM; ++i) {
|
|
memset(&at_agent->data_channel[i], 0, sizeof(at_data_channel_t));
|
|
at_agent->data_channel[i].is_free = K_TRUE;
|
|
at_agent->data_channel[i].status = AT_CHANNEL_STATUS_HANGING;
|
|
}
|
|
}
|
|
|
|
__STATIC__ void at_channel_deinit(at_agent_t *at_agent)
|
|
{
|
|
int i = 0;
|
|
|
|
for (i = 0; i < AT_DATA_CHANNEL_NUM; ++i) {
|
|
tos_at_channel_free(at_agent, i);
|
|
}
|
|
}
|
|
|
|
__API__ const char *tos_at_channel_ip_get(at_agent_t *at_agent, int channel_id)
|
|
{
|
|
at_data_channel_t *data_channel = K_NULL;
|
|
|
|
data_channel = at_channel_get(at_agent, channel_id, K_FALSE);
|
|
if (!data_channel) {
|
|
return K_NULL;
|
|
}
|
|
|
|
return data_channel->remote_ip;
|
|
}
|
|
|
|
__API__ const char *tos_at_channel_port_get(at_agent_t *at_agent, int channel_id)
|
|
{
|
|
at_data_channel_t *data_channel = K_NULL;
|
|
|
|
data_channel = at_channel_get(at_agent, channel_id, K_FALSE);
|
|
if (!data_channel) {
|
|
return K_NULL;
|
|
}
|
|
|
|
return data_channel->remote_port;
|
|
}
|
|
|
|
__STATIC__ void at_event_table_set(at_agent_t *at_agent, at_event_t *event_table, size_t event_table_size)
|
|
{
|
|
at_agent->event_table = event_table;
|
|
at_agent->event_table_size = event_table_size;
|
|
}
|
|
|
|
#if AT_INPUT_SIMULATE_IDLE_EN
|
|
|
|
__STATIC__ void tos_at_uart_input_notify(at_agent_t *at_agent)
|
|
{
|
|
tos_sem_post(&at_agent->uart_rx_sem);
|
|
}
|
|
|
|
__STATIC__ void idle_check_timer_callback(void *args)
|
|
{
|
|
at_agent_t *at_agent = (at_agent_t *)args;
|
|
tos_at_uart_input_notify(at_agent);
|
|
}
|
|
#endif /* #if AT_INPUT_SIMULATE_IDLE_EN */
|
|
|
|
__API__ int tos_at_init(at_agent_t *at_agent, char *task_name, k_stack_t *stk, hal_uart_port_t uart_port, at_event_t *event_table, size_t event_table_size)
|
|
{
|
|
void *buffer = K_NULL;
|
|
|
|
memset(at_agent, 0, sizeof(at_agent_t));
|
|
|
|
at_event_table_set(at_agent, event_table, event_table_size);
|
|
|
|
at_channel_init(at_agent);
|
|
|
|
buffer = tos_mmheap_alloc(AT_UART_RX_FIFO_BUFFER_SIZE);
|
|
if (!buffer) {
|
|
return -1;
|
|
}
|
|
|
|
at_agent->uart_rx_fifo_buffer = (uint8_t *)buffer;
|
|
tos_chr_fifo_create(&at_agent->uart_rx_fifo, buffer, AT_UART_RX_FIFO_BUFFER_SIZE);
|
|
|
|
buffer = tos_mmheap_alloc(AT_CMD_BUFFER_SIZE);
|
|
if (!buffer) {
|
|
goto errout0;
|
|
}
|
|
at_agent->cmd_buf = (char *)buffer;
|
|
|
|
// if (tos_mutex_create(&at_agent->cmd_buf_lock) != K_ERR_NONE) {
|
|
// goto errout1;
|
|
// }
|
|
|
|
if (at_recv_cache_init(at_agent) != 0) {
|
|
goto errout2;
|
|
}
|
|
|
|
#if AT_INPUT_TYPE_FRAME_EN
|
|
buffer = tos_mmheap_alloc(AT_FRAME_LEN_MAIL_MAX * sizeof(at_frame_len_mail_t));
|
|
if (!buffer) {
|
|
goto errout3;
|
|
}
|
|
|
|
at_agent->uart_rx_frame_mail_buffer = (uint8_t *)buffer;
|
|
|
|
if (tos_mail_q_create(&at_agent->uart_rx_frame_mail, buffer, AT_FRAME_LEN_MAIL_MAX, sizeof(at_frame_len_mail_t)) != K_ERR_NONE) {
|
|
goto errout4;
|
|
}
|
|
#else
|
|
if (tos_sem_create(&at_agent->uart_rx_sem, (k_sem_cnt_t)0u) != K_ERR_NONE) {
|
|
goto errout3;
|
|
}
|
|
#endif /* AT_INPUT_TYPE_FRAME_EN */
|
|
|
|
// if (tos_mutex_create(&at_agent->uart_rx_lock) != K_ERR_NONE) {
|
|
// goto errout5;
|
|
// }
|
|
|
|
if (tos_mutex_create(&at_agent->uart_tx_lock) != K_ERR_NONE) {
|
|
goto errout6;
|
|
}
|
|
|
|
if (tos_mutex_create(&at_agent->global_lock) != K_ERR_NONE) {
|
|
goto errout7;
|
|
}
|
|
|
|
#if AT_INPUT_SIMULATE_IDLE_EN
|
|
if (tos_timer_create(&at_agent->idle_check_timer, SIMULATE_IDLE_DEFAULT_TIME,
|
|
0, idle_check_timer_callback, at_agent, TOS_OPT_TIMER_ONESHOT) != K_ERR_NONE) {
|
|
goto errout8;
|
|
}
|
|
#endif /* AT_INPUT_SIMULATE_IDLE_EN */
|
|
|
|
if (tos_hal_uart_init(&at_agent->uart, uart_port) != 0) {
|
|
goto errout9;
|
|
}
|
|
|
|
if (tos_task_create(&at_agent->parser, task_name, at_parser,
|
|
at_agent, AT_PARSER_TASK_PRIO, stk,
|
|
AT_PARSER_TASK_STACK_SIZE, 0) != K_ERR_NONE) {
|
|
goto errout10;
|
|
}
|
|
|
|
return 0;
|
|
errout10:
|
|
tos_hal_uart_deinit(&at_agent->uart);
|
|
|
|
errout9:
|
|
#if AT_INPUT_SIMULATE_IDLE_EN
|
|
tos_timer_destroy(&at_agent->idle_check_timer);
|
|
errout8:
|
|
#endif /* AT_INPUT_SIMULATE_IDLE_EN */
|
|
|
|
tos_mutex_destroy(&at_agent->global_lock);
|
|
|
|
errout7:
|
|
tos_mutex_destroy(&at_agent->uart_tx_lock);
|
|
|
|
errout6:
|
|
// tos_mutex_destroy(&at_agent->uart_rx_lock);
|
|
|
|
//errout5:
|
|
#if AT_INPUT_TYPE_FRAME_EN
|
|
tos_mail_q_destroy(&at_agent->uart_rx_frame_mail);
|
|
#else
|
|
tos_sem_destroy(&at_agent->uart_rx_sem);
|
|
#endif /* AT_INPUT_TYPE_FRAME_EN */
|
|
|
|
#if AT_INPUT_TYPE_FRAME_EN
|
|
errout4:
|
|
tos_mmheap_free(at_agent->uart_rx_frame_mail_buffer);
|
|
at_agent->uart_rx_frame_mail_buffer = K_NULL;
|
|
#endif /* AT_INPUT_TYPE_FRAME_EN */
|
|
|
|
errout3:
|
|
at_recv_cache_deinit(at_agent);
|
|
|
|
errout2:
|
|
// tos_mutex_destroy(&at_agent->cmd_buf_lock);
|
|
|
|
//errout1:
|
|
tos_mmheap_free(at_agent->cmd_buf);
|
|
at_agent->cmd_buf = K_NULL;
|
|
|
|
errout0:
|
|
tos_mmheap_free(at_agent->uart_rx_fifo_buffer);
|
|
at_agent->uart_rx_fifo_buffer = K_NULL;
|
|
tos_chr_fifo_destroy(&at_agent->uart_rx_fifo);
|
|
|
|
return -1;
|
|
}
|
|
|
|
__API__ void tos_at_deinit(at_agent_t *at_agent)
|
|
{
|
|
tos_task_destroy(&at_agent->parser);
|
|
|
|
tos_hal_uart_deinit(&at_agent->uart);
|
|
|
|
tos_mutex_destroy(&at_agent->global_lock);
|
|
|
|
tos_mutex_destroy(&at_agent->uart_tx_lock);
|
|
|
|
//tos_mutex_destroy(&at_agent->uart_tx_lock);
|
|
|
|
#if AT_INPUT_SIMULATE_IDLE_EN
|
|
tos_timer_destroy(&at_agent->idle_check_timer);
|
|
#endif /* AT_INPUT_SIMULATE_IDLE_EN */
|
|
|
|
#if AT_INPUT_TYPE_FRAME_EN
|
|
tos_mail_q_destroy(&at_agent->uart_rx_frame_mail);
|
|
tos_mmheap_free(at_agent->uart_rx_frame_mail_buffer);
|
|
at_agent->uart_rx_frame_mail_buffer = K_NULL;
|
|
#else
|
|
tos_sem_destroy(&at_agent->uart_rx_sem);
|
|
#endif /* AT_INPUT_TYPE_FRAME_EN */
|
|
|
|
at_recv_cache_deinit(at_agent);
|
|
|
|
// tos_mutex_destroy(&at_agent->cmd_buf_lock);
|
|
|
|
tos_mmheap_free(at_agent->cmd_buf);
|
|
at_agent->cmd_buf = K_NULL;
|
|
|
|
tos_mmheap_free(at_agent->uart_rx_fifo_buffer);
|
|
at_agent->uart_rx_fifo_buffer = K_NULL;
|
|
|
|
tos_chr_fifo_destroy(&at_agent->uart_rx_fifo);
|
|
|
|
at_channel_deinit(at_agent);
|
|
}
|
|
|
|
/* To completely decouple the uart intterupt and at agent, we need a more powerful
|
|
hal(driver framework), that would be a huge work, we place it in future plans. */
|
|
#if AT_INPUT_TYPE_FRAME_EN
|
|
__API__ void tos_at_uart_input_frame(at_agent_t *at_agent, uint8_t *pdata, uint16_t len)
|
|
{
|
|
int ret;
|
|
at_frame_len_mail_t at_frame_len_mail;
|
|
|
|
ret = tos_chr_fifo_push_stream(&at_agent->uart_rx_fifo, pdata, len);
|
|
if (ret != len) {
|
|
return;
|
|
}
|
|
|
|
at_frame_len_mail.frame_len = len;
|
|
tos_mail_q_post(&at_agent->uart_rx_frame_mail, &at_frame_len_mail, sizeof(at_frame_len_mail_t));
|
|
}
|
|
|
|
#elif AT_INPUT_SIMULATE_IDLE_EN
|
|
__API__ void tos_at_uart_input_byte_no_notify(at_agent_t *at_agent, uint8_t data)
|
|
{
|
|
tos_timer_stop(&at_agent->idle_check_timer);
|
|
tos_chr_fifo_push(&at_agent->uart_rx_fifo, data);
|
|
tos_timer_start(&at_agent->idle_check_timer);
|
|
}
|
|
|
|
#else
|
|
__API__ void tos_at_uart_input_byte(at_agent_t *at_agent, uint8_t data)
|
|
{
|
|
if (tos_chr_fifo_push(&at_agent->uart_rx_fifo, data) == K_ERR_NONE) {
|
|
tos_sem_post(&at_agent->uart_rx_sem);
|
|
}
|
|
}
|
|
|
|
#endif /* AT_INPUT_TYPE_FRAME_EN or AT_INPUT_SIMULATE_IDLE_EN */
|