AT指令解析:TencentOS Tiny AT指令解析源码分析1-TencentOS Tiny 简介

文章目录

TencentOS tiny简述

腾讯物联网终端操作系统(TencentOS tiny)是腾讯面向物联网领域开发的实时操作系统,具有低功耗,低资源占用,模块化,安全可靠等特点。TencentOS tiny 提供精简的 RTOS 内核以及丰富的可裁剪可配置内核组件,可快速移植到多种主流 MCU 及模组芯片上。在物联网方面,TencentOS tiny基于RTOS内核提供了丰富的物联网组件,内部集成了多种主流物联网协议栈(如 CoAP/MQTT/TLS/DTLS/LoRaWAN/NB-IoT 等),极大的方便了物联网终端设备及业务快速接入物联网平台。针对AT指令的处理,TencentOS tiny设计了一套可读性高、稳定可靠的处理框架,本文及其后续的文章,均是对TencentOS tiny AT指令处理代码的源码分析。

源码下载相关链接

官网
Gitee下载链接

TencentOS tiny AT指令处理

TencentOS tiny AT处理相关的代码,为了net文件夹下的at文件夹中,其中tos_at.h主要是数据类型、枚举的定义和tos_at.c中是函数的具体实现。

下一篇内容,我们将对源码进行分析,详细讲解具体实现

tos_at.h代码

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.
 *---------------------------------------------------------------------------*/

#ifndef _TOS_AT_H_
#define _TOS_AT_H_

#include "tos_k.h"
#include "tos_hal.h"

#define AT_DATA_CHANNEL_NUM                         6
#define AT_DATA_CHANNEL_FIFO_BUFFER_DEFAULT_SIZE    (2048 + 1024)

#define AT_UART_RX_FIFO_BUFFER_SIZE                 (2048 + 1024)
#define AT_RECV_CACHE_SIZE                          2048

#define AT_CMD_BUFFER_SIZE                          512

#define AT_PARSER_TASK_STACK_SIZE                   2048
#define AT_PARSER_TASK_PRIO                         2

#define AT_INPUT_TYPE_FRAME_EN                      0
#define AT_FRAME_LEN_MAIL_MAX                       5

#define AT_INPUT_SIMULATE_IDLE_EN                   0
#define SIMULATE_IDLE_DEFAULT_TIME                  5

#define AT_DEBUG_LOG_EN                             0

#if AT_DEBUG_LOG_EN
#define AT_LOG(...) tos_kprintf(__VA_ARGS__)
#else
#define AT_LOG(...)
#endif

#if (AT_INPUT_SIMULATE_IDLE_EN == 1) && (AT_INPUT_TYPE_FRAME_EN == 1)
#error  "please choose AT_INPUT_SIMULATE_IDLE or AT_INPUT_TYPE_FRAM!"
#elif (AT_INPUT_SIMULATE_IDLE_EN == 1) && (TOS_CFG_TIMER_EN == 0)
#error  "please enable TOS_CFG_TIMER_EN!"
#endif

typedef enum at_status_en {
    AT_STATUS_OK,
    AT_STATUS_ERROR,
    AT_STATUS_INVALID_ARGS,
} at_status_t;

typedef struct at_cache_st {
    uint8_t    *buffer;
    size_t      buffer_size;
    size_t      recv_len;
} at_cache_t;

typedef enum at_parse_status_en {
    AT_PARSE_STATUS_NONE,
    AT_PARSE_STATUS_NEWLINE,
    AT_PARSE_STATUS_EVENT,
    AT_PARSE_STATUS_EXPECT,
    AT_PARSE_STATUS_OVERFLOW,
    AT_PARSE_STATUS_OK,
    AT_PARSE_STATUS_FAIL,
    AT_PARSE_STATUS_ERROR
} at_parse_status_t;

typedef enum at_echo_status_en {
    AT_ECHO_STATUS_NONE,
    AT_ECHO_STATUS_OK,
    AT_ECHO_STATUS_FAIL,
    AT_ECHO_STATUS_ERROR,
    AT_ECHO_STATUS_EXPECT,
} at_echo_status_t;

typedef enum at_channel_status_en {
    AT_CHANNEL_STATUS_NONE,         /*< usually means we are try to get a channel status with invalid id */
    AT_CHANNEL_STATUS_HANGING,      /*< channel is not used */
    AT_CHANNEL_STATUS_WORKING,      /*< channel is being using */
    AT_CHANNEL_STATUS_BROKEN,       /*< channel is broken(module link to remote server is broken) */
} at_channel_status_t;

typedef struct at_data_channel_st {
    uint8_t             is_free;
    k_chr_fifo_t        rx_fifo;
    uint8_t            *rx_fifo_buffer;
    k_mutex_t           rx_lock;
    k_sem_t             rx_sem;

    at_channel_status_t status;

    k_stopwatch_t       timer;

    const char         *remote_ip;
    const char         *remote_port;
} at_data_channel_t;

typedef struct at_echo_st {
    char               *buffer;
    size_t              buffer_size;
    char               *echo_expect;
    int                 line_num;
    at_echo_status_t    status;
    size_t              __w_idx;
    int                 __is_expecting;
    k_sem_t             __expect_notify;
    k_sem_t             __status_set_notify;
    int                 __is_fuzzy_match;
} at_echo_t;

typedef struct at_frame_len_mail_st {
    uint16_t frame_len;
} at_frame_len_mail_t;

typedef void (*at_event_callback_t)(void);

typedef struct at_event_st {
    const char         *event_header;
    at_event_callback_t event_callback;
} at_event_t;

typedef struct at_agent_st {
    at_data_channel_t   data_channel[AT_DATA_CHANNEL_NUM];

    at_event_t     *event_table;
    size_t          event_table_size;

    at_echo_t      *echo;

    k_task_t        parser;
    at_cache_t      recv_cache;

    /* protected the AT agent, so only one AT instruction is executing any one time. */
    k_mutex_t       global_lock;

    char           *cmd_buf;

    /* global_lock has protected the at agent, so cmd buf lock is unnecessary,
        the code will be removed in next version. */
    // k_mutex_t       cmd_buf_lock;

    hal_uart_t      uart;
    k_mutex_t       uart_tx_lock;
//    k_mutex_t       uart_rx_lock;

#if AT_INPUT_TYPE_FRAME_EN
    k_mail_q_t      uart_rx_frame_mail;
    uint8_t        *uart_rx_frame_mail_buffer;
    uint16_t        fifo_available_len;
#else
    k_sem_t         uart_rx_sem;

#if AT_INPUT_SIMULATE_IDLE_EN
    k_timer_t       idle_check_timer;
#endif  /* AT_INPUT_SIMULATE_IDLE_EN */
#endif  /* AT_INPUT_TYPE_FRAME_EN */

    k_chr_fifo_t    uart_rx_fifo;
    uint8_t        *uart_rx_fifo_buffer;
} at_agent_t;

/**
 * @brief Write data to a channel.
 * Write data to a channel with certain id.
 *
 * @attention None
 *
 * @param[in]   at_agent    pointer to the at agent struct.
 * @param[in]   channel_id  id of the channel.
 * @param[in]   buffer      data buffer to write.
 * @param[in]   buffer_len  length of the buffer.
 *
 * @return  errcode
 * @retval  -1              write failed(error).
 * @retval  none -1         the number of bytes written.
 */
__API__ int tos_at_channel_write(at_agent_t *at_agent, int channel_id, uint8_t *buffer, size_t buffer_len);

/**
 * @brief Read data from a channel.
 * Read data from a channel with a timeout.
 *
 * @attention None
 *
 * @param[in]   at_agent    pointer to the at agent struct.
 * @param[in]   channel_id  id of the channel.
 * @param[out]  buffer      buffer to hold the data read.
 * @param[in]   buffer_len  length of the buffer.
 * @param[in]   timeout     timeout.
 *
 * @return  errcode
 * @retval  -1              read failed(error).
 * @retval  none -1         the number of bytes read.
 */
__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);

/**
 * @brief Read data from a channel.
 * Read data from a channel.
 *
 * @attention None
 *
 * @param[in]   at_agent    pointer to the at agent struct.
 * @param[in]   channel_id  id of the channel.
 * @param[out]  buffer      buffer to hold the data read.
 * @param[in]   buffer_len  length of the buffer.
 *
 * @return  errcode
 * @retval  -1              read failed(error).
 * @retval  none -1         the number of bytes read.
 */
__API__ int tos_at_channel_read(at_agent_t *at_agent, int channel_id, uint8_t *buffer, size_t buffer_len);

/**
 * @brief Allocate a channel.
 * Allocate a channel with certain id.
 *
 * @attention None
 *
 * @param[in]   at_agent    pointer to the at agent struct.
 * @param[in]   channel_id  id of the channel.
 * @param[in]   ip          remote ip of the channel.
 * @param[in]   port        remote port of the channel.
 *
 * @return  errcode
 * @retval  -1              allocate failed(error).
 * @retval  none -1         the id of the channel.
 */
__API__ int tos_at_channel_alloc_id(at_agent_t *at_agent, int channel_id, const char *ip, const char *port);

/**
 * @brief Allocate a channel.
 * Allocate a channel.
 *
 * @attention None
 *
 * @param[in]   at_agent    pointer to the at agent struct.
 * @param[in]   ip          remote ip of the channel.
 * @param[in]   port        remote port of the channel.
 *
 * @return  errcode
 * @retval  -1              allocate failed(error).
 * @retval  none -1         the id of the channel.
 */
__API__ int tos_at_channel_alloc(at_agent_t *at_agent, const char *ip, const char *port);

/**
 * @brief Allocate a channel.
 * Allocate a channel with certain socket buffer size.
 *
 * @attention None
 *
 * @param[in]   at_agent            pointer to the at agent struct.
 * @param[in]   channel_id          id of the channel.
 * @param[in]   ip                  remote ip of the channel.
 * @param[in]   port                remote port of the channel.
 * @param[in]   socket_buffer_size  buffer size of the channel.
 *
 * @return  errcode
 * @retval  -1              allocate failed(error).
 * @retval  none -1         the id of the channel.
 */
__API__ int tos_at_channel_alloc_with_size(at_agent_t *at_agent, const char *ip, const char *port, size_t socket_buffer_size);

/**
 * @brief Free a channel.
 * Free a channel with certain id.
 *
 * @attention None
 *
 * @param[in]   at_agent    pointer to the at agent struct.
 * @param[in]   channel_id  id of the channel.
 *
 * @return  errcode
 * @retval  -1              free failed(error).
 * @retval  0               free successfully.
 */
__API__ int tos_at_channel_free(at_agent_t *at_agent, int channel_id);

/**
 * @brief Set channel broken.
 *
 * @attention None
 *
 * @param[in]   at_agent    pointer to the at agent struct.
 * @param[in]   channel_id  id of the channel.
 *
 * @return  errcode
 * @retval  -1              set failed(error).
 * @retval  0               set successfully.
 */
__API__ int tos_at_channel_set_broken(at_agent_t *at_agent, int channel_id);

/**
 * @brief Judge whether channel is working.
 *
 * @attention None
 *
 * @param[in]   at_agent    pointer to the at agent struct.
 * @param[in]   channel_id  id of the channel.
 *
 * @return  at channel status(type of at_channel_status_t)
 */
__API__ int tos_at_channel_is_working(at_agent_t *at_agent, int channel_id);

/**
 * @brief Initialize the at framework.
 *
 * @attention None
 *
 * @param[in]   at_agent            pointer to the at agent struct.
 * @param[in]   uart_port           port number of the uart thougth which the module connect to the MCU.
 * @param[in]   event_table         the listened event table.
 * @param[in]   event_table_size    the size of the listened event table.
 *
 * @return  errcode
 * @retval  -1              initialize failed(error).
 * @retval  0               initialize successfully.
 */
__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);

/**
 * @brief De-initialize the at framework.
 *
 * @param[in]   at_agent    pointer to the at agent struct.
 *
 * @attention None
 *
 * @return
None
 */
__API__ void tos_at_deinit(at_agent_t *at_agent);

/**
 * @brief Create a echo struct.
 *
 * @attention None
 *
 * @param[in]   echo           pointer to the echo struct.
 * @param[out]  buffer         buffer to hold the received message from the module.
 * @param[in]   buffer_size    size of the buffer.
 * @param[in]   echo_expect    the expected echo message.
 *
 * @return  errcode
 * @retval  -1              create failed(error).
 * @retval  0               create successfully.
 */
__API__ int tos_at_echo_create(at_echo_t *echo, char *buffer, size_t buffer_size, char *echo_expect);

/**
 * @brief Create a echo struct with fuzzy matching for expected echo.
 *
 * @attention None
 *
 * @param[in]   echo                    pointer to the echo struct.
 * @param[out]  buffer                  buffer to hold the received message from the module.
 * @param[in]   buffer_size             size of the buffer.
 * @param[in]   echo_expect_contains    if the echo message contains echo_expect_contains, it is a matching.
 *
 * @return  errcode
 * @retval  -1              create failed(error).
 * @retval  0               create successfully.
 */
__API__ int tos_at_echo_fuzzy_matching_create(at_echo_t *echo, char *buffer, size_t buffer_size, char *echo_expect_contains);

/**
 * @brief Execute an at command.
 *
 * @attention None
 *
 * @param[in]   at_agent        pointer to the at agent struct.
 * @param[in]   echo            pointer to the echo struct.
 * @param[in]   timeout         command wait timeout .
 * @param[in]   cmd             at command.
 *
 * @return  errcode
 * @retval  -1              execute failed(error).
 * @retval  0               execute successfully.
 */
__API__ int tos_at_cmd_exec(at_agent_t *at_agent, at_echo_t *echo, uint32_t timeout, const char *cmd, ...);

/**
 * @brief Execute an at command.
 * Execute an at command and wait until the expected echo message received or timeout.
 *
 * @attention None
 *
 * @param[in]   at_agent        pointer to the at agent struct.
 * @param[in]   echo            pointer to the echo struct.
 * @param[in]   timeout         command wait timeout .
 * @param[in]   cmd             at command.
 *
 * @return  errcode
 * @retval  -1              execute failed(error).
 * @retval  0               execute successfully.
 */
__API__ int tos_at_cmd_exec_until(at_agent_t *at_agent, at_echo_t *echo, uint32_t timeout, const char *cmd, ...);

/**
 * @brief Send raw data througth uart.
 *
 * @attention None
 *
 * @param[in]   at_agent        pointer to the at agent struct.
 * @param[in]   echo            pointer to the echo struct.
 * @param[in]   timeout         command wait timeout .
 * @param[in]   buf             data to send.
 * @param[in]   size            size of the buf.
 *
 * @return  errcode
 * @retval  -1              execute failed(error).
 * @retval  0               execute successfully.
 */
__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);

/**
 * @brief Send raw data througth uart.
 * Send raw data througth uart and wait until the expected echo message received or timeout.
 *
 * @attention None
 *
 * @param[in]   at_agent        pointer to the at agent struct.
 * @param[in]   echo            pointer to the echo struct.
 * @param[in]   timeout         command wait timeout .
 * @param[in]   buf             data to send.
 * @param[in]   size            size of the buf.
 *
 * @return  errcode
 * @retval  -1              execute failed(error).
 * @retval  0               execute successfully.
 */
__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);

#if AT_INPUT_TYPE_FRAME_EN
/**
 * @brief Write amount bytes to the at uart.
 * The function called by the uart interrupt, to put the data from the uart to the at framework.
 *
 * @attention None
 *
 * @param[in]   at_agent        pointer to the at agent struct.
 * @param[in]   pdata           pointer of the uart received data.
 * @param[in]   len             length of the uart received data.
 *
 * @return  None
 */
__API__ void tos_at_uart_input_frame(at_agent_t *at_agent, uint8_t *pdata, uint16_t len);

#elif AT_INPUT_SIMULATE_IDLE_EN
/**
 * @brief Write byte to the at uart.
 * The function called by the uart receive interrupt.
 *
 * @attention No notification is given after writing.
 *
 * @param[in]   at_agent        pointer to the at agent struct.
 * @param[in]   data            uart received data.
 *
 * @return  None
 */
__API__ void tos_at_uart_input_byte_no_notify(at_agent_t *at_agent, uint8_t data);
#else
/**
 * @brief Write byte to the at uart.
 * The function called by the uart interrupt, to put the data from the uart to the at framework.
 *
 * @attention None
 *
 * @param[in]   at_agent        pointer to the at agent struct.
 * @param[in]   data            uart received data.
 *
 * @return  None
 */
__API__ void tos_at_uart_input_byte(at_agent_t *at_agent, uint8_t data);
#endif
/**
 * @brief A global lock provided by at framework.
 * The lock usually used to make a atomic function.
 *
 * @attention None
 *
 * @param[in]   at_agent        pointer to the at agent struct.
 *
 * @return  errcode
 * @retval  -1              pend failed(error).
 * @retval  0               pend successfully.
 */
__API__ int tos_at_global_lock_pend(at_agent_t *at_agent);

/**
 * @brief A global lock provided by at framework.
 * The lock usually used to make a atomic function.
 *
 * @attention None
 *
 * @param[in]   at_agent        pointer to the at agent struct.
 *
 * @return  errcode
 * @retval  -1              post failed(error).
 * @retval  0               post successfully.
 */
__API__ int tos_at_global_lock_post(at_agent_t *at_agent);

/**
 * @brief Read data from the uart.
 * Read data from the uart, usually called in listened event callback.
 *
 * @attention None
 *
 * @param[in]   at_agent        pointer to the at agent struct.
 * @param[out]  buffer          buffer to hold the data read from the uart.
 * @param[in]   buffer_len      length of the buffer.
 *
 * @return  length of the data read from the uart.
 */
__API__ int tos_at_uart_read(at_agent_t *at_agent, uint8_t *buffer, size_t buffer_len);

/**
 * @brief Read data from the uart.
 * Read data from the uart until meet a '\n', usually called in listened event callback.
 *
 * @attention None
 *
 * @param[in]   at_agent        pointer to the at agent struct.
 * @param[out]  buffer          buffer to hold the data read from the uart.
 * @param[in]   buffer_len      length of the buffer.
 *
 * @return  length of the data read from the uart.
 */
__API__ int tos_at_uart_readline(at_agent_t *at_agent, uint8_t *buffer, size_t buffer_len);

/**
 * @brief Read data from the uart.
 * Read data from the uart until no more incoming data, usually called in listened event callback.
 *
 * @attention None
 *
 * @param[in]   at_agent        pointer to the at agent struct.
 * @param[out]  buffer          buffer to hold the data read from the uart.
 * @param[in]   buffer_len      length of the buffer.
 *
 * @return  length of the data read from the uart.
 */
__API__ int tos_at_uart_drain(at_agent_t *at_agent, uint8_t *buffer, size_t buffer_len);

/**
 * @brief Get the remote ip of a channel.
 * Get the remote ip of a channel with certain id.
 *
 * @attention None
 *
 * @param[in]   at_agent        pointer to the at agent struct.
 * @param[in]   channel_id      id of the channel.
 *
 * @return  remote ip of the channel.
 */
__API__ const char *tos_at_channel_ip_get(at_agent_t *at_agent, int channel_id);

/**
 * @brief Get the remote port of a channel.
 * Get the remote port of a channel with certain id.
 *
 * @attention None
 *
 * @param[in]   at_agent        pointer to the at agent struct.
 * @param[in]   channel_id      id of the channel.
 *
 * @return  remote port of the channel.
 */
__API__ const char *tos_at_channel_port_get(at_agent_t *at_agent, int channel_id);

#endif /* _TOS_AT_H_ */

tos_at.c代码

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 */
相关推荐
小张数码4 小时前
随身WiFi技术深探:通信芯片/信号增益原理解析+开源方案参考!随身WiFi建议买吗?随身WiFi品牌哪个网速快信号稳定?格行随身WiFi怎么样?
网络·物联网
jz_ddk4 小时前
[LVGL] 从0开始,学LVGL:基础构建篇 - 掌握UI的核心构建块
linux·网络协议·ui·rpc·嵌入式·gui·lvgl
要做朋鱼燕4 小时前
STM32与W25Q64 SPI通信全解析
笔记·嵌入式·hal·spi
无限进步_5 小时前
扫雷游戏的设计与实现:扫雷游戏3.0
c语言·开发语言·c++·后端·算法·游戏·游戏程序
jianqiang.xue5 小时前
单片机图形化编程:课程目录介绍 总纲
c++·人工智能·python·单片机·物联网·青少年编程·arduino
文火冰糖的硅基工坊5 小时前
[嵌入式系统-100]:IoT(物联网)与AIoT(人工智能物联网)
人工智能·物联网·架构·创业
Yupureki6 小时前
从零开始的C++学习生活 8:list的入门使用
c语言·c++·学习·visual studio
kyle~8 小时前
计算机系统---CPU的进程与线程处理
linux·服务器·c语言·c++·操作系统·计算机系统
小此方9 小时前
C语言自定义变量类型结构体理论:从初见到精通(下)
c语言·数据结构·算法