Bladeren bron

[net][at] Add AT commands component

chenyong 6 jaren geleden
bovenliggende
commit
8b887e76e9

+ 4 - 2
components/net/Kconfig

@@ -19,9 +19,9 @@ config RT_USING_SAL
 
             config SAL_USING_AT
                 bool "Support AT Commands stack"
-                default y if RT_USING_AT_SOCKET
+                default y if AT_USING_SOCKET
                 default n
-                depends on RT_USING_AT_SOCKET
+                depends on AT_USING_SOCKET
         endmenu
 
         config SAL_USING_POSIX
@@ -369,6 +369,8 @@ endmenu
 
 source "$RTT_DIR/components/net/freemodbus/Kconfig"
 
+source "$RTT_DIR/components/net/at/Kconfig"
+
 if RT_USING_LWIP
 
 config LWIP_USING_DHCPD

+ 78 - 0
components/net/at/Kconfig

@@ -0,0 +1,78 @@
+menu "AT commands"
+
+config RT_USING_AT
+    bool "Enable AT commands"
+    default n
+
+if RT_USING_AT
+
+    config AT_DEBUG
+        bool "Enable debug log output"
+        default n
+    
+    config AT_USING_SERVER
+        bool "Enable AT commands server"
+        default n
+    
+    if AT_USING_SERVER
+
+        config AT_SERVER_DEVICE
+            string "Server device name"
+            default "uart3"
+
+        config AT_SERVER_RECV_BUFF_LEN
+            int "The maximum length of server data accepted"
+            default 256
+
+        choice
+            prompt "The commands new line sign"
+            help
+                This end mark can used for AT server determine the end of commands ,
+                it can choose "\r", "\n" or "\r\n"
+
+            default AT_CMD_END_MARK_CRLF
+
+            config AT_CMD_END_MARK_CRLF
+                bool "\\r\\n"
+
+            config AT_CMD_END_MARK_CR
+                bool "\\r"
+
+            config AT_CMD_END_MARK_LF
+                bool "\\n"
+
+        endchoice
+
+    endif
+    
+    config AT_USING_CLIENT
+        bool "Enable AT commands client"
+        default n
+    
+    if AT_USING_CLIENT
+
+        config AT_CLIENT_DEVICE
+            string "Client device name"
+            default "uart2"
+
+        config AT_CLIENT_RECV_BUFF_LEN
+            int "The maximum length of client data accepted"
+            default 512
+            
+        config AT_USING_SOCKET
+            bool "Provide similar BSD Socket API by AT"
+            default n
+    endif
+
+    config AT_USING_CLI
+        bool "Enable command-line interface for AT commands"
+        default y
+        depends on FINSH_USING_MSH
+
+    config AT_PRINT_RAW_CMD
+        bool "Enable print RAW format AT command communication data"
+        default n
+    
+endif
+
+endmenu

+ 26 - 0
components/net/at/SConscript

@@ -0,0 +1,26 @@
+from building import *
+
+cwd = GetCurrentDir()
+path = [cwd + '/include']
+
+src = Split('''
+src/at_cli.c
+src/at_utils.c
+''')
+
+if GetDepend(['AT_USING_SERVER']):
+    src += Split('''
+    src/at_server.c
+    src/at_base_cmd.c
+    ''')
+    
+if GetDepend(['AT_USING_CLIENT']):
+    src += Glob('src/at_client.c')
+    
+if GetDepend(['AT_USING_SOCKET']):
+    src += Glob('at_socket/*.c')
+    path += [cwd + '/at_socket']   
+
+group = DefineGroup('AT', src, depend = ['RT_USING_AT'], CPPPATH = path)
+
+Return('group')

+ 1138 - 0
components/net/at/at_socket/at_socket.c

@@ -0,0 +1,1138 @@
+/*
+ * File      : at_socket.c
+ * This file is part of RT-Thread RTOS
+ * COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2018-06-06     chenyong     first version
+ */
+
+#include <at.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <at_socket.h>
+
+#ifdef SAL_USING_POSIX
+#include <dfs_poll.h>
+#endif
+
+#ifdef DBG_SECTION_NAME
+#undef DBG_SECTION_NAME
+#define DBG_SECTION_NAME     "[AT_SOC] "
+#endif
+
+
+#define HTONS_PORT(x) ((((x) & 0x00ffUL) << 8) | (((x) & 0xff00UL) >> 8))
+#define NIPQUAD(addr) \
+        ((unsigned char *)&addr)[0], \
+        ((unsigned char *)&addr)[1], \
+        ((unsigned char *)&addr)[2], \
+        ((unsigned char *)&addr)[3]
+
+#ifdef AT_DEVICE_NOT_SELECTED
+#error The AT socket device is not selected, please select it through the env menuconfig.
+#endif
+
+/* The maximum number of sockets structure */
+#ifndef AT_SOCKETS_NUM
+#define AT_SOCKETS_NUM       AT_DEVICE_SOCKETS_NUM
+#endif
+
+typedef enum {
+    AT_EVENT_SEND,
+    AT_EVENT_RECV,
+    AT_EVENT_ERROR,
+} at_event_t;
+
+/* the global array of available sockets */
+static struct at_socket sockets[AT_SOCKETS_NUM] = { 0 };
+/* the global AT socket lock */
+static rt_mutex_t at_socket_lock = RT_NULL;
+/* AT device socket options */
+static struct at_device_ops *at_dev_ops = RT_NULL;
+
+struct at_socket *at_get_socket(int socket)
+{
+    if (socket < 0 || socket >= AT_SOCKETS_NUM)
+    {
+        return RT_NULL;
+    }
+
+    /* check socket structure valid or not */
+    if (sockets[socket].magic != AT_SOCKET_MAGIC)
+    {
+        return RT_NULL;
+    }
+
+    return &sockets[socket];
+}
+
+/* get a block to the AT socket receive list*/
+static size_t at_recvpkt_put(rt_slist_t *rlist, const char *ptr, size_t length)
+{
+    at_recv_pkt_t pkt;
+
+    pkt = (at_recv_pkt_t) rt_calloc(1, sizeof(struct at_recv_pkt));
+    if (!pkt)
+    {
+        LOG_E("No memory for receive packet table!");
+        return 0;
+    }
+
+    pkt->bfsz_totle = length;
+    pkt->bfsz_index = 0;
+    pkt->buff = (char *) ptr;
+
+    rt_slist_append(rlist, &pkt->list);
+
+    return length;
+}
+
+/* delete and free all receive buffer list */
+static int at_recvpkt_all_delete(rt_slist_t *rlist)
+{
+    at_recv_pkt_t pkt;
+    rt_slist_t *node;
+
+    if(rt_slist_isempty(rlist))
+        return 0;
+
+    for(node = rt_slist_first(rlist); node; node = rt_slist_next(node))
+    {
+        pkt = rt_slist_entry(node, struct at_recv_pkt, list);
+        if (pkt->buff)
+        {
+            rt_free(pkt->buff);
+        }
+        if(pkt)
+        {
+            rt_free(pkt);
+            pkt = RT_NULL;
+        }
+    }
+
+    return 0;
+}
+
+/* delete and free specified list block */
+static int at_recvpkt_node_delete(rt_slist_t *rlist, rt_slist_t *node)
+{
+    at_recv_pkt_t pkt;
+
+    if(rt_slist_isempty(rlist))
+        return 0;
+
+    rt_slist_remove(rlist, node);
+
+    pkt= rt_slist_entry(node, struct at_recv_pkt, list);
+    if (pkt->buff)
+    {
+        rt_free(pkt->buff);
+    }
+    if (pkt)
+    {
+        rt_free(pkt);
+        pkt = RT_NULL;
+    }
+
+    return 0;
+}
+
+/* get a block from AT socket receive list */
+static size_t at_recvpkt_get(rt_slist_t *rlist, char *mem, size_t len)
+{
+    rt_slist_t *node;
+    at_recv_pkt_t pkt;
+    size_t content_pos = 0, page_pos = 0;
+
+    if(rt_slist_isempty(rlist))
+        return 0;
+
+    for (node = rt_slist_first(rlist); node; node = rt_slist_next(node))
+    {
+        pkt = rt_slist_entry(node, struct at_recv_pkt, list);
+
+        page_pos = pkt->bfsz_totle - pkt->bfsz_index;
+
+        if (page_pos >= len - content_pos)
+        {
+            memcpy((char *) mem + content_pos, pkt->buff + pkt->bfsz_index, len - content_pos);
+            pkt->bfsz_index += len - content_pos;
+            if (pkt->bfsz_index == pkt->bfsz_totle)
+            {
+                at_recvpkt_node_delete(rlist, node);
+            }
+            content_pos = len;
+            break;
+        }
+        else
+        {
+            memcpy((char *) mem + content_pos, pkt->buff + pkt->bfsz_index, page_pos);
+            content_pos += page_pos;
+            pkt->bfsz_index += page_pos;
+            at_recvpkt_node_delete(rlist, node);
+        }
+    }
+
+    return content_pos;
+}
+
+static void at_do_event_changes(struct at_socket *sock, at_event_t event, rt_bool_t is_plus)
+{
+    switch (event)
+    {
+    case AT_EVENT_SEND:
+    {
+        if (is_plus)
+        {
+            sock->sendevent++;
+
+#ifdef SAL_USING_POSIX
+            rt_wqueue_wakeup(&sock->wait_head, (void*) POLLOUT);
+#endif
+
+        }
+        else if (sock->sendevent)
+        {
+            sock->sendevent --;
+        }
+        break;
+    }
+    case AT_EVENT_RECV:
+    {
+        if (is_plus)
+        {
+            sock->rcvevent++;
+
+#ifdef SAL_USING_POSIX
+            rt_wqueue_wakeup(&sock->wait_head, (void*) POLLIN);
+#endif
+
+        }
+        else if (sock->rcvevent)
+        {
+            sock->rcvevent --;
+        }
+        break;
+    }
+    case AT_EVENT_ERROR:
+    {
+        if (is_plus)
+        {
+            sock->errevent++;
+
+#ifdef SAL_USING_POSIX
+            rt_wqueue_wakeup(&sock->wait_head, (void*) POLLERR);
+#endif
+
+        }
+        else if (sock->errevent)
+        {
+            sock->errevent --;
+        }
+        break;
+    }
+    default:
+        LOG_E("Not supported event (%d)", event)
+    }
+}
+
+static struct at_socket *alloc_socket(void)
+{
+    char sem_name[RT_NAME_MAX];
+    char lock_name[RT_NAME_MAX];
+    struct at_socket *sock;
+    int idx;
+
+    rt_mutex_take(at_socket_lock, RT_WAITING_FOREVER);
+
+    /* find an empty at socket entry */
+    for (idx = 0; idx < AT_SOCKETS_NUM && sockets[idx].magic; idx++);
+
+    /* can't find an empty protocol family entry */
+    if (idx == AT_SOCKETS_NUM)
+    {
+        goto __err;
+    }
+
+    sock = &(sockets[idx]);
+    sock->magic = AT_SOCKET_MAGIC;
+    sock->socket = idx;
+    sock->state = AT_SOCKET_NONE;
+    sock->rcvevent = RT_NULL;
+    sock->sendevent = RT_NULL;
+    sock->errevent = RT_NULL;
+    rt_slist_init(&sock->recvpkt_list);
+
+    rt_snprintf(sem_name, RT_NAME_MAX, "%s%d", "at_recv_notice_", idx);
+    /* create AT socket receive mailbox */
+    if ((sock->recv_notice = rt_sem_create(sem_name, 0, RT_IPC_FLAG_FIFO)) == RT_NULL)
+    {
+        goto __err;
+    }
+
+    rt_snprintf(lock_name, RT_NAME_MAX, "%s%d", "at_recv_lock_", idx);
+    /* create AT socket receive ring buffer lock */
+    if((sock->recv_lock = rt_mutex_create(lock_name, RT_IPC_FLAG_FIFO)) == RT_NULL)
+    {
+        goto __err;
+    }
+
+    rt_mutex_release(at_socket_lock);
+    return sock;
+
+__err:
+    rt_mutex_release(at_socket_lock);
+    return RT_NULL;
+}
+
+int at_socket(int domain, int type, int protocol)
+{
+    struct at_socket *sock;
+    enum at_socket_type socket_type;
+
+    /* check socket family protocol */
+    RT_ASSERT(domain == AF_AT||domain == AF_INET);
+
+    //TODO check protocol
+
+    switch(type)
+    {
+    case SOCK_STREAM:
+        socket_type = AT_SOCKET_TCP;
+        break;
+
+    case SOCK_DGRAM:
+        socket_type = AT_SOCKET_UDP;
+        break;
+
+    default :
+        LOG_E("Don't support socket type (%d)!", type);
+        return -1;
+    }
+
+    /* allocate and initialize a new AT socket */
+    sock = alloc_socket();
+    if(!sock)
+    {
+        LOG_E("Allocate a new AT socket failed!");
+        return RT_NULL;
+    }
+    sock->type = socket_type;
+
+#ifdef SAL_USING_POSIX
+    rt_wqueue_init(&sock->wait_head);
+#endif
+
+    return sock->socket;
+}
+
+static int free_socket(struct at_socket *sock)
+{
+    if (sock->recv_notice)
+    {
+        rt_sem_delete(sock->recv_notice);
+    }
+
+    if (sock->recv_lock)
+    {
+        rt_mutex_delete(sock->recv_lock);
+    }
+
+    if (!rt_slist_isempty(&sock->recvpkt_list))
+    {
+        at_recvpkt_all_delete(&sock->recvpkt_list);
+    }
+
+    memset(sock, 0x00, sizeof(struct at_socket));
+
+    return 0;
+}
+
+int at_closesocket(int socket)
+{
+    struct at_socket *sock;
+    enum at_socket_state last_state;
+
+    if (!at_dev_ops)
+    {
+        LOG_E("Please register AT device socket options first!");
+        return -1;
+    }
+
+    if ((sock = at_get_socket(socket)) == RT_NULL)
+        return -1;
+
+    last_state = sock->state;
+
+    /* the rt_at_socket_close is need some time, so change state in advance */
+    sock->state = AT_SOCKET_CLOSED;
+
+    if (last_state == AT_SOCKET_CONNECT)
+    {
+        if (at_dev_ops->close(socket) != 0)
+        {
+            LOG_E("AT socket (%d) closesocket failed!", socket);
+        }
+    }
+
+    return free_socket(sock);
+}
+
+int at_shutdown(int socket, int how)
+{
+    struct at_socket *sock;
+
+    if (!at_dev_ops)
+    {
+        LOG_E("Please register AT device socket options first!");
+        return -1;
+    }
+
+    if ((sock = at_get_socket(socket)) == RT_NULL)
+        return -1;
+
+    if (sock->state == AT_SOCKET_CONNECT)
+    {
+        if (at_dev_ops->close(socket) != 0)
+        {
+            LOG_E("AT socket (%d) shutdown failed!", socket);
+        }
+    }
+
+    return free_socket(sock);
+}
+
+int at_bind(int socket, const struct sockaddr *name, socklen_t namelen)
+{
+
+    if (at_get_socket(socket) == RT_NULL)
+        return -1;
+
+    return 0;
+}
+
+/* get IP address and port by socketaddr structure information */
+static int socketaddr_to_ipaddr_port(const struct sockaddr *sockaddr, ip_addr_t *addr, uint16_t *port)
+{
+    const struct sockaddr_in* sin = (const struct sockaddr_in*) (const void *) sockaddr;
+
+    (*addr).u_addr.ip4.addr = sin->sin_addr.s_addr;
+
+    *port = (uint16_t) HTONS_PORT(sin->sin_port);
+
+    return 0;
+}
+
+/* ipaddr structure change to IP address */
+static int ipaddr_to_ipstr(const struct sockaddr *sockaddr, char *ipstr)
+{
+    struct sockaddr_in *sin = (struct sockaddr_in *) sockaddr;
+
+    /* change network ip_addr to ip string  */
+    rt_snprintf(ipstr, 16, "%u.%u.%u.%u", NIPQUAD(sin->sin_addr.s_addr));
+
+    return 0;
+}
+
+static void at_recv_notice_cb(int socket, at_socket_evt_t event, const char *buff, size_t bfsz)
+{
+    struct at_socket *sock;
+
+    RT_ASSERT(buff);
+    RT_ASSERT(bfsz);
+    RT_ASSERT(event == AT_SOCKET_EVT_RECV);
+
+    if ((sock = at_get_socket(socket)) == RT_NULL)
+        return ;
+
+    /* put receive buffer to receiver packet list */
+    rt_mutex_take(sock->recv_lock, RT_WAITING_FOREVER);
+    at_recvpkt_put(&(sock->recvpkt_list), buff, bfsz);
+    rt_mutex_release(sock->recv_lock);
+
+    rt_sem_release(sock->recv_notice);
+
+    at_do_event_changes(sock, AT_EVENT_RECV, RT_TRUE);
+}
+
+static void at_closed_notice_cb(int socket, at_socket_evt_t event, const char *buff, size_t bfsz)
+{
+    struct at_socket *sock;
+
+    RT_ASSERT(event == AT_SOCKET_EVT_CLOSED);
+
+    if ((sock = at_get_socket(socket)) == RT_NULL)
+        return ;
+
+    at_do_event_changes(sock, AT_EVENT_RECV, RT_TRUE);
+    at_do_event_changes(sock, AT_EVENT_ERROR, RT_TRUE);
+
+//    LOG_D("socket (%d) closed by remote");
+    sock->state = AT_SOCKET_CLOSED;
+    rt_sem_release(sock->recv_notice);
+}
+int at_connect(int socket, const struct sockaddr *name, socklen_t namelen)
+{
+    struct at_socket *sock;
+    ip_addr_t remote_addr;
+    uint16_t remote_port;
+    char ipstr[16] = { 0 };
+    int result = 0;
+
+    if (!at_dev_ops)
+    {
+        LOG_E("Please register AT device socket options first!");
+        return -1;
+    }
+
+    sock = at_get_socket(socket);
+    if (!sock)
+    {
+        result = -1;
+        goto __exit;
+    }
+
+    if (sock->state != AT_SOCKET_NONE)
+    {
+        LOG_E("Socket %d connect state is %d.", sock->socket, sock->state);
+        result = -1;
+        goto __exit;
+    }
+
+    /* get IP address and port by socketaddr structure */
+    socketaddr_to_ipaddr_port(name, &remote_addr, &remote_port);
+    ipaddr_to_ipstr(name, ipstr);
+
+    if (at_dev_ops->connect(socket, ipstr, remote_port, sock->type, RT_TRUE) < 0)
+    {
+        LOG_E("AT socket(%d) connect failed!", socket);
+        result = -1;
+        goto __exit;
+    }
+
+    sock->state = AT_SOCKET_CONNECT;
+
+    /* set AT socket receive data callback function */
+    at_dev_ops->set_event_cb(AT_SOCKET_EVT_RECV, at_recv_notice_cb);
+    at_dev_ops->set_event_cb(AT_SOCKET_EVT_CLOSED, at_closed_notice_cb);
+
+__exit:
+
+    if (result < 0)
+    {
+        at_do_event_changes(sock, AT_EVENT_ERROR, RT_TRUE);
+    }
+
+    return result;
+}
+
+int at_recvfrom(int socket, void *mem, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen)
+{
+    struct at_socket *sock;
+    int timeout;
+    int result = 0;
+    size_t recv_len = 0;
+
+    if (!mem || len == 0)
+    {
+        LOG_E("AT recvfrom input data or length error!");
+        result = -1;
+        goto __exit;
+    }
+
+    if (!at_dev_ops)
+    {
+        LOG_E("Please register AT device socket options first!");
+        return -1;
+    }
+
+    sock = at_get_socket(socket);
+    if (!sock)
+    {
+        result = -1;
+        goto __exit;
+    }
+
+    /* if the socket type is UDP, nead to connect socket first */
+    if (from && sock->type == AT_SOCKET_UDP && sock->state == AT_SOCKET_NONE)
+    {
+        ip_addr_t remote_addr;
+        uint16_t remote_port;
+        char ipstr[16] = { 0 };
+
+        socketaddr_to_ipaddr_port(from, &remote_addr, &remote_port);
+        ipaddr_to_ipstr(from, ipstr);
+
+        if (at_dev_ops->connect(socket, ipstr, remote_port, sock->type, RT_TRUE) < 0)
+        {
+            LOG_E("AT socket UDP connect failed!");
+            result = -1;
+            goto __exit;
+        }
+        sock->state = AT_SOCKET_CONNECT;
+    }
+
+    if (sock->state != AT_SOCKET_CONNECT)
+    {
+        LOG_E("received data error, current socket (%d) state (%d) is error.", socket, sock->state);
+        result = -1;
+        goto __exit;
+    }
+
+    /* receive packet list last transmission of remaining data */
+    rt_mutex_take(sock->recv_lock, RT_WAITING_FOREVER);
+    if((recv_len = at_recvpkt_get(&(sock->recvpkt_list), (char *)mem, len)) > 0)
+    {
+        rt_mutex_release(sock->recv_lock);
+        goto __exit;
+    }
+    rt_mutex_release(sock->recv_lock);
+
+    /* non-blocking sockets receive data */
+    if (flags & MSG_DONTWAIT)
+    {
+        goto __exit;
+    }
+
+    /* set AT socket receive timeout */
+    if((timeout = sock->recv_timeout) == 0)
+    {
+        timeout = RT_WAITING_FOREVER;
+    }
+
+    while (1)
+    {
+        /* wait the receive semaphore */
+        if (rt_sem_take(sock->recv_notice, timeout) < 0)
+        {
+            LOG_E("AT socket (%d) receive timeout (%d)!", socket, timeout);
+            result = -1;
+            goto __exit;
+        }
+        else
+        {
+            if (sock->state == AT_SOCKET_CONNECT)
+            {
+                /* get receive buffer to receiver ring buffer */
+                rt_mutex_take(sock->recv_lock, RT_WAITING_FOREVER);
+                recv_len = at_recvpkt_get(&(sock->recvpkt_list), (char *) mem, len);
+                rt_mutex_release(sock->recv_lock);
+                if (recv_len > 0)
+                {
+                    break;
+                }
+            }
+            else
+            {
+                LOG_D("received data exit, current socket (%d) is closed by remote.", socket);
+                result = -1;
+                goto __exit;
+            }
+        }
+    }
+
+__exit:
+
+    if (result < 0)
+    {
+        at_do_event_changes(sock, AT_EVENT_ERROR, RT_TRUE);
+    }
+    else
+    {
+        result = recv_len;
+        if (recv_len)
+        {
+            at_do_event_changes(sock, AT_EVENT_RECV, RT_FALSE);
+        }
+    }
+
+    return result;
+}
+
+int at_recv(int s, void *mem, size_t len, int flags)
+{
+    return at_recvfrom(s, mem, len, flags, RT_NULL, RT_NULL);
+}
+
+int at_sendto(int socket, const void *data, size_t size, int flags, const struct sockaddr *to, socklen_t tolen)
+{
+    struct at_socket *sock;
+    int len, result = 0;
+
+    if (!at_dev_ops)
+    {
+        LOG_E("Please register AT device socket options first!");
+        result = -1;
+        goto __exit;
+    }
+
+    if (!data || size == 0)
+    {
+        LOG_E("AT sendto input data or size error!");
+        result = -1;
+        goto __exit;
+    }
+
+    sock = at_get_socket(socket);
+    if (!sock)
+    {
+        result = -1;
+        goto __exit;
+    }
+
+    switch (sock->type)
+    {
+    case AT_SOCKET_TCP:
+        if (sock->state != AT_SOCKET_CONNECT)
+        {
+            LOG_E("send data error, current socket (%d) state (%d) is error.", socket, sock->state);
+            result = -1;
+            goto __exit;
+        }
+
+        if ((len = at_dev_ops->send(sock->socket, (const char *) data, size, sock->type)) < 0)
+        {
+            result = -1;
+            goto __exit;
+        }
+        break;
+
+    case AT_SOCKET_UDP:
+        if (to && sock->state == AT_SOCKET_NONE)
+        {
+            ip_addr_t remote_addr;
+            uint16_t remote_port;
+            char ipstr[16] = { 0 };
+
+            socketaddr_to_ipaddr_port(to, &remote_addr, &remote_port);
+            ipaddr_to_ipstr(to, ipstr);
+
+            if (at_dev_ops->connect(socket, ipstr, remote_port, sock->type, RT_TRUE) < 0)
+            {
+                LOG_E("AT socket (%d) UDP connect failed!", socket);
+                result = -1;
+                goto __exit;
+            }
+            sock->state = AT_SOCKET_CONNECT;
+        }
+
+        if ((len = at_dev_ops->send(sock->socket, (char *) data, size, sock->type)) < 0)
+        {
+            result = -1;
+            goto __exit;
+        }
+        break;
+
+    default:
+        LOG_E("Socket (%d) type %d is not support.", socket, sock->type);
+        result = -1;
+        goto __exit;
+    }
+
+__exit:
+
+    if (result < 0)
+    {
+        at_do_event_changes(sock, AT_EVENT_ERROR, RT_TRUE);
+    }
+    else
+    {
+        result = len;
+    }
+
+    return result;
+}
+
+int at_send(int socket, const void *data, size_t size, int flags)
+{
+    return at_sendto(socket, data, size, flags, RT_NULL, 0);
+}
+
+
+int at_getsockopt(int socket, int level, int optname, void *optval, socklen_t *optlen)
+{
+    struct at_socket *sock;
+    int32_t timeout;
+
+    if (!optval || !optlen)
+    {
+        LOG_E("AT getsocketopt input option value or option length error!");
+        return -1;
+    }
+
+    sock = at_get_socket(socket);
+    if (!sock)
+    {
+        return -1;
+    }
+
+    switch (level)
+    {
+    case SOL_SOCKET:
+        switch (optname)
+        {
+        case SO_RCVTIMEO:
+            timeout = sock->recv_timeout;
+            ((struct timeval *)(optval))->tv_sec = (timeout) / 1000U;
+            ((struct timeval *)(optval))->tv_usec = (timeout % 1000U) * 1000U;
+            break;
+
+        case SO_SNDTIMEO:
+            timeout = sock->send_timeout;
+            ((struct timeval *) optval)->tv_sec = timeout / 1000U;
+            ((struct timeval *) optval)->tv_usec = (timeout % 1000U) * 1000U;
+            break;
+
+        default:
+            LOG_E("AT socket (%d) not support option name : %d.", socket, optname);
+            return -1;
+        }
+        break;
+
+    default:
+        LOG_E("AT socket (%d) not support option level : %d.", socket, level);
+        return -1;
+    }
+
+    return 0;
+}
+
+int at_setsockopt(int socket, int level, int optname, const void *optval, socklen_t optlen)
+{
+    struct at_socket *sock;
+
+    if (!optval)
+    {
+        LOG_E("AT setsockopt input option value error!");
+        return -1;
+    }
+
+    sock = at_get_socket(socket);
+    if (!sock)
+    {
+        return -1;
+    }
+
+    switch (level)
+    {
+    case SOL_SOCKET:
+        switch (optname)
+        {
+        case SO_RCVTIMEO:
+            sock->recv_timeout = ((const struct timeval *) optval)->tv_sec * 1000
+                    + ((const struct timeval *) optval)->tv_usec / 1000;
+            break;
+
+        case SO_SNDTIMEO:
+            sock->send_timeout = ((const struct timeval *) optval)->tv_sec * 1000
+                    + ((const struct timeval *) optval)->tv_usec / 1000;
+            break;
+
+        default:
+            LOG_E("AT socket (%d) not support option name : %d.", socket, optname);
+            return -1;
+        }
+        break;
+    case IPPROTO_TCP:
+        switch (optname)
+        {
+        case TCP_NODELAY:
+            break;
+        }
+        break;
+    default:
+        LOG_E("AT socket (%d) not support option level : %d.", socket, level);
+        return -1;
+    }
+
+    return 0;
+}
+
+static uint32_t ipstr_atol(const char* nptr)
+{
+    uint32_t total = 0;
+    char sign = '+';
+    /* jump space */
+    while (isspace(*nptr))
+    {
+        ++nptr;
+    }
+    if (*nptr == '-' || *nptr == '+')
+    {
+        sign = *nptr++;
+    }
+    while (isdigit(*nptr))
+    {
+        total = 10 * total + ((*nptr++) - '0');
+    }
+    return (sign == '-') ? -total : total;
+}
+
+/* IP address to unsigned int type */
+static uint32_t ipstr_to_u32(char *ipstr)
+{
+    char ipBytes[4] = { 0 };
+    uint32_t i;
+
+    for (i = 0; i < 4; i++, ipstr++)
+    {
+        ipBytes[i] = (char) ipstr_atol(ipstr);
+        if ((ipstr = strchr(ipstr, '.')) == RT_NULL)
+        {
+            break;
+        }
+    }
+    return *(uint32_t *) ipBytes;
+}
+
+struct hostent *at_gethostbyname(const char *name)
+{
+    ip_addr_t addr;
+    char ipstr[16] = { 0 };
+    /* buffer variables for at_gethostbyname() */
+    static struct hostent s_hostent;
+    static char *s_aliases;
+    static ip_addr_t s_hostent_addr;
+    static ip_addr_t *s_phostent_addr[2];
+    static char s_hostname[DNS_MAX_NAME_LENGTH + 1];
+    size_t idx = 0;
+
+    if (!name)
+    {
+        LOG_E("AT gethostbyname input name error!");
+        return RT_NULL;
+    }
+
+    if (!at_dev_ops)
+    {
+        LOG_E("Please register AT device socket options first!");
+        return RT_NULL;
+    }
+
+    for (idx = 0; idx < strlen(name) && !isalpha(name[idx]); idx++);
+
+    if (idx < strlen(name))
+    {
+        if (at_dev_ops->domain_resolve(name, ipstr) < 0)
+        {
+            LOG_E("AT domain (%s) resolve error!", name);
+            return RT_NULL;
+        }
+    }
+    else
+    {
+        strncpy(ipstr, name, strlen(name));
+    }
+
+    addr.u_addr.ip4.addr = ipstr_to_u32(ipstr);
+
+    /* fill hostent structure */
+    s_hostent_addr = addr;
+    s_phostent_addr[0] = &s_hostent_addr;
+    s_phostent_addr[1] = RT_NULL;
+    strncpy(s_hostname, name, DNS_MAX_NAME_LENGTH);
+    s_hostname[DNS_MAX_NAME_LENGTH] = 0;
+    s_hostent.h_name = s_hostname;
+    s_aliases = RT_NULL;
+    s_hostent.h_aliases = &s_aliases;
+    s_hostent.h_addrtype = AF_AT;
+    s_hostent.h_length = sizeof(ip_addr_t);
+    s_hostent.h_addr_list = (char**) &s_phostent_addr;
+
+    return &s_hostent;
+}
+
+int at_getaddrinfo(const char *nodename, const char *servname,
+                    const struct addrinfo *hints, struct addrinfo **res)
+{
+    int port_nr = 0;
+    ip_addr_t addr;
+    struct addrinfo *ai;
+    struct sockaddr_storage *sa;
+    size_t total_size = 0;
+    size_t namelen = 0;
+    int ai_family = 0;
+
+    if (res == RT_NULL)
+    {
+        return EAI_FAIL;
+    }
+    if (!at_dev_ops)
+    {
+        LOG_E("Please register AT device socket options first!");
+        return EAI_FAIL;
+    }
+    *res = RT_NULL;
+    if ((nodename == RT_NULL) && (servname == RT_NULL))
+    {
+        return EAI_NONAME;
+    }
+
+    if (hints != RT_NULL)
+    {
+        ai_family = hints->ai_family;
+        if (hints->ai_family != AF_AT && hints->ai_family != AF_INET && hints->ai_family != AF_UNSPEC)
+        {
+            return EAI_FAMILY;
+        }
+    }
+
+    if (servname != RT_NULL)
+    {
+        /* service name specified: convert to port number */
+        port_nr = atoi(servname);
+        if ((port_nr <= 0) || (port_nr > 0xffff))
+        {
+            return EAI_SERVICE;
+        }
+    }
+
+    if (nodename != RT_NULL)
+    {
+        /* service location specified, try to resolve */
+        if ((hints != RT_NULL) && (hints->ai_flags & AI_NUMERICHOST))
+        {
+            /* no DNS lookup, just parse for an address string */
+            if (!inet_aton(nodename, (ip4_addr_t * )&addr))
+            {
+                return EAI_NONAME;
+            }
+
+            if (ai_family == AF_AT || ai_family == AF_INET)
+            {
+                return EAI_NONAME;
+            }
+        }
+        else
+        {
+            char ip_str[16] = { 0 };
+            size_t idx = 0;
+
+            for (idx = 0; idx < strlen(nodename) && !isalpha(nodename[idx]); idx++);
+
+            if(idx < strlen(nodename))
+            {
+                if (at_dev_ops->domain_resolve((char *) nodename, ip_str) != 0)
+                {
+                    return EAI_FAIL;
+                }
+            }
+            else
+            {
+                strncpy(ip_str, nodename, strlen(nodename));
+            }
+
+            addr.type = IPADDR_TYPE_V4;
+            if ((addr.u_addr.ip4.addr = ipstr_to_u32(ip_str)) == 0)
+            {
+                return EAI_FAIL;
+            }
+        }
+    }
+    else
+    {
+        /* to do service location specified, use loopback address */
+    }
+
+    total_size = sizeof(struct addrinfo) + sizeof(struct sockaddr_storage);
+    if (nodename != RT_NULL)
+    {
+        namelen = strlen(nodename);
+        if (namelen > DNS_MAX_NAME_LENGTH)
+        {
+            /* invalid name length */
+            return EAI_FAIL;
+        }
+        RT_ASSERT(total_size + namelen + 1 > total_size);
+        total_size += namelen + 1;
+    }
+    /* If this fails, please report to lwip-devel! :-) */
+    RT_ASSERT(total_size <= sizeof(struct addrinfo) + sizeof(struct sockaddr_storage) + DNS_MAX_NAME_LENGTH + 1);
+    ai = (struct addrinfo *) rt_malloc(total_size);
+    if (ai == RT_NULL)
+    {
+        return EAI_MEMORY;
+    }
+    memset(ai, 0, total_size);
+    /* cast through void* to get rid of alignment warnings */
+    sa = (struct sockaddr_storage *) (void *) ((uint8_t *) ai + sizeof(struct addrinfo));
+    struct sockaddr_in *sa4 = (struct sockaddr_in *) sa;
+    /* set up sockaddr */
+    sa4->sin_addr.s_addr = addr.u_addr.ip4.addr;
+    sa4->sin_family = AF_AT;
+    sa4->sin_len = sizeof(struct sockaddr_in);
+    sa4->sin_port = htons((u16_t )port_nr);
+    ai->ai_family = AF_AT;
+
+    /* set up addrinfo */
+    if (hints != RT_NULL)
+    {
+        /* copy socktype & protocol from hints if specified */
+        ai->ai_socktype = hints->ai_socktype;
+        ai->ai_protocol = hints->ai_protocol;
+    }
+    if (nodename != RT_NULL)
+    {
+        /* copy nodename to canonname if specified */
+        ai->ai_canonname = ((char *) ai + sizeof(struct addrinfo) + sizeof(struct sockaddr_storage));
+        memcpy(ai->ai_canonname, nodename, namelen);
+        ai->ai_canonname[namelen] = 0;
+    }
+    ai->ai_addrlen = sizeof(struct sockaddr_storage);
+    ai->ai_addr = (struct sockaddr *) sa;
+
+    *res = ai;
+
+    return 0;
+}
+
+void at_freeaddrinfo(struct addrinfo *ai)
+{
+    if (ai != RT_NULL)
+    {
+        rt_free(ai);
+    }
+}
+
+void at_scoket_device_register(const struct at_device_ops *ops)
+{
+    RT_ASSERT(ops);
+    RT_ASSERT(ops->connect);
+    RT_ASSERT(ops->close);
+    RT_ASSERT(ops->send);
+    RT_ASSERT(ops->domain_resolve);
+    RT_ASSERT(ops->set_event_cb);
+    at_dev_ops = (struct at_device_ops *) ops;
+}
+
+static int at_socket_init(void)
+{
+    /* create AT socket lock */
+    at_socket_lock = rt_mutex_create("at_socket_lock", RT_IPC_FLAG_FIFO);
+    if (!at_socket_lock)
+    {
+        LOG_E("No memory for AT socket lock!");
+        return -RT_ENOMEM;
+    }
+
+    return RT_EOK;
+}
+INIT_COMPONENT_EXPORT(at_socket_init);

+ 163 - 0
components/net/at/at_socket/at_socket.h

@@ -0,0 +1,163 @@
+/*
+ * File      : at_socket.h
+ * This file is part of RT-Thread RTOS
+ * COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2018-06-06     chenYong     first version
+ */
+
+#ifndef __AT_SOCKET_H__
+#define __AT_SOCKET_H__
+
+#include <rtthread.h>
+#include <rtdevice.h>
+#include <rthw.h>
+
+#include <netdb.h>
+#include <sys/socket.h>
+
+#ifndef AT_SOCKET_RECV_BFSZ
+#define AT_SOCKET_RECV_BFSZ            512
+#endif
+
+#define AT_DEFAULT_RECVMBOX_SIZE       10
+#define AT_DEFAULT_ACCEPTMBOX_SIZE     10
+
+/* sal socket magic word */
+#define AT_SOCKET_MAGIC                0xA100
+
+/* Current state of the AT socket. */
+enum at_socket_state
+{
+    AT_SOCKET_NONE,
+    AT_SOCKET_LISTEN,
+    AT_SOCKET_CONNECT,
+    AT_SOCKET_CLOSED
+};
+
+enum at_socket_type
+{
+    AT_SOCKET_INVALID   = 0,
+    AT_SOCKET_TCP       = 0x10,      /* TCP IPv4 */
+    AT_SOCKET_UDP       = 0x20,      /* UDP IPv4 */
+};
+
+typedef enum
+{
+    AT_SOCKET_EVT_RECV,
+    AT_SOCKET_EVT_CLOSED,
+} at_socket_evt_t;
+
+typedef void (*at_evt_cb_t)(int socket, at_socket_evt_t event, const char *buff, size_t bfsz);
+
+struct at_socket;
+/* A callback prototype to inform about events for AT socket */
+typedef void (* at_socket_callback)(struct at_socket *conn, int event, uint16_t len);
+
+/* AT device socket options function */
+struct at_device_ops
+{
+    int (*connect)(int socket, char *ip, int32_t port, enum at_socket_type type, rt_bool_t is_client);
+    int (*close)(int socket);
+    int (*send)(int socket, const char *buff, size_t bfsz, enum at_socket_type type);
+    int (*domain_resolve)(const char *name, char ip[16]);
+    void (*set_event_cb)(at_socket_evt_t event, at_evt_cb_t cb);
+};
+
+/* AT receive package list structure */
+struct at_recv_pkt
+{
+    rt_slist_t list;
+    size_t bfsz_totle;
+    size_t bfsz_index;
+    char *buff;
+};
+typedef struct at_recv_pkt *at_recv_pkt_t;
+
+struct at_socket
+{
+    /* AT socket magic word */
+    uint32_t magic;
+
+    int socket;
+    /* type of the AT socket (TCP, UDP or RAW) */
+    enum at_socket_type type;
+    /* current state of the AT socket */
+    enum at_socket_state state;
+    /* receive semaphore, received data release semaphore */
+    rt_sem_t recv_notice;
+    rt_mutex_t recv_lock;
+    rt_slist_t recvpkt_list;
+
+    /* timeout to wait for send or received data in milliseconds */
+    int32_t recv_timeout;
+    int32_t send_timeout;
+    /* A callback function that is informed about events for this AT socket */
+    at_socket_callback callback;
+
+    /* number of times data was received, set by event_callback() */
+    uint16_t rcvevent;
+    /* number of times data was ACKed (free send buffer), set by event_callback() */
+    uint16_t sendevent;
+    /* error happened for this socket, set by event_callback() */
+    uint16_t errevent;
+
+#ifdef SAL_USING_POSIX
+    rt_wqueue_t wait_head;
+#endif
+};
+
+int at_socket(int domain, int type, int protocol);
+int at_closesocket(int socket);
+int at_shutdown(int socket, int how);
+int at_bind(int socket, const struct sockaddr *name, socklen_t namelen);
+int at_connect(int socket, const struct sockaddr *name, socklen_t namelen);
+int at_sendto(int socket, const void *data, size_t size, int flags, const struct sockaddr *to, socklen_t tolen);
+int at_send(int socket, const void *data, size_t size, int flags);
+int at_recvfrom(int socket, void *mem, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen);
+int at_recv(int socket, void *mem, size_t len, int flags);
+int at_getsockopt(int socket, int level, int optname, void *optval, socklen_t *optlen);
+int at_setsockopt(int socket, int level, int optname, const void *optval, socklen_t optlen);
+struct hostent *at_gethostbyname(const char *name);
+int at_getaddrinfo(const char *nodename, const char *servname, const struct addrinfo *hints, struct addrinfo **res);
+void at_freeaddrinfo(struct addrinfo *ai);
+
+struct at_socket *at_get_socket(int socket);
+void at_scoket_device_register(const struct at_device_ops *ops);
+
+#ifndef RT_USING_SAL
+
+#define socket(domain, type, protocol)                      at_socket(domain, type, protocol)
+#define closescoket(socket)                                 at_closesocket(socket)
+#define shutdown(socket, how)                               at_shutdown(socket, how)
+#define bind(socket, name, namelen)                         at_bind(socket, name, namelen)
+#define connect(socket, name, namelen)                      at_connect(socket, name, namelen)
+#define sendto(socket, data, size, flags, to, tolen)        at_sendto(socket, data, size, flags, to, tolen)
+#define send(socket, data, size, flags)                     at_send(socket, data, size, flags)
+#define recvfrom(socket, mem, len, flags, from, fromlen)    at_recvfrom(socket, mem, len, flags, from, fromlen)
+#define getsockopt(socket, level, optname, optval, optlen)  at_getsockopt(socket, level, optname, optval, optlen)
+#define setsockopt(socket, level, optname, optval, optlen)  at_setsockopt(socket, level, optname, optval, optlen)
+
+#define gethostbyname(name)                                 at_gethostbyname(name)
+#define getaddrinfo(nodename, servname, hints, res)         at_getaddrinfo(nodename, servname, hints, res)
+#define freeaddrinfo(ai)                                    at_freeaddrinfo(ai)
+
+#endif /* RT_USING_SAL */
+
+#endif /* AT_SOCKET_H__ */

+ 256 - 0
components/net/at/include/at.h

@@ -0,0 +1,256 @@
+/*
+ * File      : at.h
+ * This file is part of RT-Thread RTOS
+ * COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2018-03-30     chenyong     first version
+ */
+
+#ifndef __AT_H__
+#define __AT_H__
+
+#include <rtthread.h>
+
+#define AT_SW_VERSION                  "0.2.1"
+
+#define DBG_ENABLE
+#define DBG_SECTION_NAME               "AT"
+#ifdef AT_DEBUG
+#define DBG_LEVEL                      DBG_LOG
+#else
+#define DBG_LEVEL                      DBG_INFO
+#endif /* AT_DEBUG */
+#define DBG_COLOR
+#include <rtdbg.h>
+
+#define AT_CMD_NAME_LEN                16
+#define AT_END_MARK_LEN                4
+
+#ifndef AT_CMD_MAX_LEN
+#define AT_CMD_MAX_LEN                 128
+#endif
+
+/* client receive idle timeout, client will wait this timeout when send data, unit: ms */
+#ifndef AT_CLIENT_RECV_IDEL
+#define AT_CLIENT_RECV_IDEL            50
+#endif
+
+/* the server AT commands new line sign */
+#if defined(AT_CMD_END_MARK_CRLF)
+#define AT_CMD_END_MARK                "\r\n"
+#elif defined(AT_CMD_END_MARK_CR)
+#define AT_CMD_END_MARK                "\r"
+#elif defined(AT_CMD_END_MARK_LF)
+#define AT_CMD_END_MARK                "\n"
+#endif
+
+#ifndef AT_SERVER_RECV_BUFF_LEN
+#define AT_SERVER_RECV_BUFF_LEN        256
+#endif
+
+#ifndef AT_CLIENT_RECV_BUFF_LEN
+#define AT_CLIENT_RECV_BUFF_LEN        512
+#endif
+
+#ifndef AT_SERVER_DEVICE
+#define AT_SERVER_DEVICE               "uart2"
+#endif
+
+#ifndef AT_CLIENT_DEVICE
+#define AT_CLIENT_DEVICE               "uart2"
+#endif
+
+#define AT_CMD_EXPORT(_name_, _args_expr_, _test_, _query_, _setup_, _exec_)   \
+    RT_USED static const struct at_cmd __at_cmd_##_test_##_query_##_setup_##_exec_ SECTION("RtAtCmdTab") = \
+    {                                                                          \
+        _name_,                                                                \
+        _args_expr_,                                                           \
+        _test_,                                                                \
+        _query_,                                                               \
+        _setup_,                                                               \
+        _exec_,                                                                \
+    };
+
+enum at_status
+{
+    AT_STATUS_UNINITIALIZED = 0,
+    AT_STATUS_INITIALIZED,
+    AT_STATUS_BUSY,
+};
+typedef enum at_status at_status_t;
+
+#ifdef AT_USING_SERVER
+enum at_result
+{
+    AT_RESULT_OK = 0,                  /* AT result is no error */
+    AT_RESULT_FAILE = -1,              /* AT result have a generic error */
+    AT_RESULT_NULL = -2,               /* AT result not need return */
+    AT_RESULT_CMD_ERR = -3,            /* AT command format error or No way to execute */
+    AT_RESULT_CHECK_FAILE = -4,        /* AT command expression format is error */
+    AT_RESULT_PARSE_FAILE = -5,        /* AT command arguments parse is error */
+};
+typedef enum at_result at_result_t;
+
+struct at_cmd
+{
+    char name[AT_CMD_NAME_LEN];
+    char *args_expr;
+    at_result_t (*test)(void);
+    at_result_t (*query)(void);
+    at_result_t (*setup)(const char *args);
+    at_result_t (*exec)(void);
+};
+typedef struct at_cmd *at_cmd_t;
+
+struct at_server
+{
+    rt_device_t device;
+
+    at_status_t status;
+    char (*get_char)(void);
+    rt_bool_t echo_mode;
+
+    char recv_buffer[AT_SERVER_RECV_BUFF_LEN];
+    rt_size_t cur_recv_len;
+    rt_sem_t rx_notice;
+    char end_mark[AT_END_MARK_LEN];
+
+    rt_thread_t parser;
+    void (*parser_entry)(struct at_server *server);
+};
+
+typedef struct at_server *at_server_t;
+#endif /* AT_USING_SERVER */
+
+#ifdef AT_USING_CLIENT
+enum at_resp_status
+{
+     AT_RESP_OK = 0,                   /* AT response end is OK */
+     AT_RESP_ERROR = -1,               /* AT response end is ERROR */
+     AT_RESP_TIMEOUT = -2,             /* AT response is timeout */
+     AT_RESP_BUFF_FULL= -3,            /* AT response buffer is full */
+};
+typedef enum at_resp_status at_resp_status_t;
+
+struct at_response
+{
+    /* response buffer */
+    char *buf;
+    /* the maximum response buffer size */
+    rt_size_t buf_size;
+    /* the number of setting response lines
+     * == 0: the response data will auto return when received 'OK' or 'ERROR'
+     * != 0: the response data will return when received setting lines number data */
+    rt_size_t line_num;
+    /* the count of received response lines */
+    rt_size_t line_counts;
+    /* the maximum response time */
+    rt_int32_t timeout;
+};
+
+typedef struct at_response *at_response_t;
+
+/* URC(Unsolicited Result Code) object, such as: 'RING', 'READY' request by AT server */
+struct at_urc
+{
+    const char *cmd_prefix;
+    const char *cmd_suffix;
+    void (*func)(const char *data, rt_size_t size);
+};
+typedef struct at_urc *at_urc_t;
+
+struct at_client
+{
+    rt_device_t device;
+
+    at_status_t status;
+
+    char recv_buffer[AT_CLIENT_RECV_BUFF_LEN];
+    rt_size_t cur_recv_len;
+    rt_sem_t rx_notice;
+    rt_mutex_t lock;
+
+    at_response_t resp;
+    rt_sem_t resp_notice;
+    at_resp_status_t resp_status;
+
+    const struct at_urc *urc_table;
+    rt_size_t urc_table_size;
+
+    rt_thread_t parser;
+};
+
+typedef struct at_client *at_client_t;
+#endif /* AT_USING_CLIENT */
+
+#ifdef AT_USING_SERVER
+/* AT server initialize and start */
+int at_server_init(void);
+
+/* AT server send command execute result to AT device */
+void at_server_printf(const char *format, ...);
+void at_server_printfln(const char *format, ...);
+void at_server_print_result(at_result_t result);
+
+/* AT server request arguments parse */
+int at_req_parse_args(const char *req_args, const char *req_expr, ...);
+#endif /* AT_USING_SERVER */
+
+#ifdef AT_USING_CLIENT
+/* AT client initialize and start */
+int at_client_init(void);
+
+/* AT client send commands to AT server and waiter response */
+int at_exec_cmd(at_response_t resp, const char *cmd_expr, ...);
+
+/* AT Client send or receive data */
+rt_size_t at_client_send(const char *buf, rt_size_t size);
+rt_size_t at_client_recv(char *buf, rt_size_t size);
+
+/* AT response structure create and delete */
+at_response_t at_create_resp(rt_size_t buf_size, rt_size_t line_num, rt_int32_t timeout);
+void at_delete_resp(at_response_t resp);
+at_response_t at_resp_set_info(at_response_t resp, rt_size_t buf_size, rt_size_t line_num, rt_int32_t timeout);
+
+/* AT response line buffer get and parse response buffer arguments */
+const char *at_resp_get_line(at_response_t resp, rt_size_t resp_line);
+const char *at_resp_get_line_by_kw(at_response_t resp, const char *keyword);
+int at_resp_parse_line_args(at_response_t resp, rt_size_t resp_line, const char *resp_expr, ...);
+int at_resp_parse_line_args_by_kw(at_response_t resp, const char *keyword, const char *resp_expr, ...);
+
+/* Set URC(Unsolicited Result Code) table */
+void at_set_urc_table(const struct at_urc * table, rt_size_t size);
+#endif /* AT_USING_CLIENT */
+
+/* ========================== User port function ============================ */
+
+#ifdef AT_USING_SERVER
+/* AT server device reset */
+void at_port_reset(void);
+
+/* AT server device factory reset */
+void at_port_factory_reset(void);
+#endif
+
+#ifdef AT_USING_CLIENT
+/* AT client port initialization */
+int at_client_port_init(void);
+#endif
+
+#endif /* __AT_H__ */

+ 131 - 0
components/net/at/src/at_base_cmd.c

@@ -0,0 +1,131 @@
+/*
+ * File      : at_base_cmd.c
+ * This file is part of RT-Thread RTOS
+ * COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2018-04-01     armink       first version
+ * 2018-04-04     chenyong     add base commands
+ */
+
+#include <at.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <rtdevice.h>
+
+#define AT_ECHO_MODE_CLOSE             0
+#define AT_ECHO_MODE_OPEN              1
+
+extern at_server_t at_get_server(void);
+
+static at_result_t at_exec(void)
+{
+    return AT_RESULT_OK;
+}
+AT_CMD_EXPORT("AT", RT_NULL, RT_NULL, RT_NULL, RT_NULL, at_exec);
+
+static at_result_t atz_exec(void)
+{
+    at_server_printfln("OK");
+
+    at_port_factory_reset();
+
+    return AT_RESULT_NULL;
+}
+AT_CMD_EXPORT("ATZ", RT_NULL, RT_NULL, RT_NULL, RT_NULL, atz_exec);
+
+static at_result_t at_rst_exec(void)
+{
+    at_server_printfln("OK");
+
+    at_port_reset();
+
+    return AT_RESULT_NULL;
+}
+AT_CMD_EXPORT("AT+RST", RT_NULL, RT_NULL, RT_NULL, RT_NULL, at_rst_exec);
+
+static at_result_t ate_setup(const char *args)
+{
+    int echo_mode = atoi(args);
+
+    if(echo_mode == AT_ECHO_MODE_CLOSE || echo_mode == AT_ECHO_MODE_OPEN)
+    {
+        at_get_server()->echo_mode = echo_mode;
+    }
+    else
+    {
+        return AT_RESULT_FAILE;
+    }
+
+    return AT_RESULT_OK;
+}
+AT_CMD_EXPORT("ATE", "<value>", RT_NULL, RT_NULL, ate_setup, RT_NULL);
+
+static at_result_t at_show_cmd_exec(void)
+{
+    extern void rt_at_server_print_all_cmd(void);
+
+    rt_at_server_print_all_cmd();
+
+    return AT_RESULT_OK;
+}
+AT_CMD_EXPORT("AT&L", RT_NULL, RT_NULL, RT_NULL, RT_NULL, at_show_cmd_exec);
+
+static at_result_t at_uart_query(void)
+{
+    struct rt_serial_device *serial = (struct rt_serial_device *)at_get_server()->device;
+
+    at_server_printfln("AT+UART=%d,%d,%d,%d,%d", serial->config.baud_rate, serial->config.data_bits,
+            serial->config.stop_bits, serial->config.parity, 1);
+
+    return AT_RESULT_OK;
+}
+
+static at_result_t at_uart_setup(const char *args)
+{
+    struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;
+    int baudrate, databits, stopbits, parity, flow_control, argc;
+    const char *req_expr = "=%d,%d,%d,%d,%d";
+
+    argc = at_req_parse_args(args, req_expr, &baudrate, &databits, &stopbits, &parity, &flow_control);
+    if (argc != 5)
+    {
+        return AT_RESULT_PARSE_FAILE;
+    }
+
+    at_server_printfln("UART baudrate : %d", baudrate);
+    at_server_printfln("UART databits : %d", databits);
+    at_server_printfln("UART stopbits : %d", stopbits);
+    at_server_printfln("UART parity   : %d", parity);
+    at_server_printfln("UART control  : %d", flow_control);
+
+    config.baud_rate = baudrate;
+    config.data_bits = databits;
+    config.stop_bits = stopbits;
+    config.parity = parity;
+
+    if(rt_device_control(at_get_server()->device, RT_DEVICE_CTRL_CONFIG, &config) != RT_EOK)
+    {
+        return AT_RESULT_FAILE;
+    }
+
+    return AT_RESULT_OK;
+}
+
+AT_CMD_EXPORT("AT+UART", "=<baudrate>,<databits>,<stopbits>,<parity>,<flow_control>", RT_NULL, at_uart_query, at_uart_setup, RT_NULL);

+ 332 - 0
components/net/at/src/at_cli.c

@@ -0,0 +1,332 @@
+/*
+ * File      : at_cli.c
+ * This file is part of RT-Thread RTOS
+ * COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2018-04-02     armink       first version
+ */
+
+#include <at.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <rtthread.h>
+#include <rtdevice.h>
+#include <rthw.h>
+
+#ifdef AT_USING_CLI
+
+#define AT_CLI_FIFO_SIZE                      256
+
+static struct rt_semaphore console_rx_notice;
+static struct rt_ringbuffer *console_rx_fifo = RT_NULL;
+static rt_err_t (*odev_rx_ind)(rt_device_t dev, rt_size_t size) = RT_NULL;
+
+#ifdef AT_USING_CLIENT
+static struct rt_semaphore client_rx_notice;
+static struct rt_ringbuffer *client_rx_fifo = RT_NULL;
+#endif 
+
+static char console_getchar(void)
+{
+    char ch;
+
+    rt_sem_take(&console_rx_notice, RT_WAITING_FOREVER);
+    rt_ringbuffer_getchar(console_rx_fifo, (rt_uint8_t *)&ch);
+
+    return ch;
+}
+
+static rt_err_t console_getchar_rx_ind(rt_device_t dev, rt_size_t size)
+{
+    uint8_t ch;
+    rt_size_t i;
+
+    for (i = 0; i < size; i++)
+    {
+        /* read a char */
+        if (rt_device_read(dev, 0, &ch, 1))
+        {
+            rt_ringbuffer_put_force(console_rx_fifo, &ch, 1);
+            rt_sem_release(&console_rx_notice);
+        }
+    }
+
+    return RT_EOK;
+}
+
+void at_cli_init(void)
+{
+    rt_base_t int_lvl;
+    rt_device_t console;
+
+    rt_sem_init(&console_rx_notice, "at_cli_notice", 0, RT_IPC_FLAG_FIFO);
+
+    /* create RX FIFO */
+    console_rx_fifo = rt_ringbuffer_create(AT_CLI_FIFO_SIZE);
+    /* created must success */
+    RT_ASSERT(console_rx_fifo);
+
+    int_lvl = rt_hw_interrupt_disable();
+    console = rt_console_get_device();
+    if (console)
+    {
+        /* backup RX indicate */
+        odev_rx_ind = console->rx_indicate;
+        rt_device_set_rx_indicate(console, console_getchar_rx_ind);
+    }
+
+    rt_hw_interrupt_enable(int_lvl);
+}
+
+void at_cli_deinit(void)
+{
+    rt_base_t int_lvl;
+    rt_device_t console;
+
+    rt_sem_detach(&console_rx_notice);
+    rt_ringbuffer_destroy(console_rx_fifo);
+
+    int_lvl = rt_hw_interrupt_disable();
+    console = rt_console_get_device();
+    if (console && odev_rx_ind)
+    {
+        /* restore RX indicate */
+        rt_device_set_rx_indicate(console, odev_rx_ind);
+    }
+    rt_hw_interrupt_enable(int_lvl);
+}
+
+#ifdef AT_USING_SERVER
+static void server_cli_parser(void)
+{
+    extern at_server_t at_get_server(void);
+
+    at_server_t server = at_get_server();
+    rt_base_t int_lvl;
+    static rt_device_t device_bak;
+    static char (*getchar_bak)(void);
+    static char endmark_back[AT_END_MARK_LEN];
+
+    /* backup server device and getchar function */
+    {
+        int_lvl = rt_hw_interrupt_disable();
+
+        device_bak = server->device;
+        getchar_bak = server->get_char;
+
+        memset(endmark_back, 0x00, AT_END_MARK_LEN);
+        memcpy(endmark_back, server->end_mark, strlen(server->end_mark));
+
+        /* setup server device as console device */
+        server->device = rt_console_get_device();
+        server->get_char = console_getchar;
+
+        memset(server->end_mark, 0x00, AT_END_MARK_LEN);
+        server->end_mark[0] = '\r';
+
+        rt_hw_interrupt_enable(int_lvl);
+    }
+
+    if (server)
+    {
+        rt_kprintf("======== Welcome to using RT-Thread AT command server cli ========\n");
+        rt_kprintf("Input your at command for test server. Press 'ESC' to exit.\n");
+        server->parser_entry(server);
+    }
+    else
+    {
+        rt_kprintf("AT client not initialized\n");
+    }
+
+    /* restore server device and getchar function */
+    {
+        int_lvl = rt_hw_interrupt_disable();
+
+        server->device = device_bak;
+        server->get_char = getchar_bak;
+
+        memset(server->end_mark, 0x00, AT_END_MARK_LEN);
+        memcpy(server->end_mark, endmark_back, strlen(endmark_back));
+
+        rt_hw_interrupt_enable(int_lvl);
+    }
+}
+#endif /* AT_USING_SERVER */
+
+#ifdef AT_USING_CLIENT
+static char client_getchar(void)
+{
+    char ch;
+
+    rt_sem_take(&client_rx_notice, RT_WAITING_FOREVER);
+    rt_ringbuffer_getchar(client_rx_fifo, (rt_uint8_t *)&ch);
+
+    return ch;
+}
+
+static void at_client_entry(void *param)
+{
+    char ch;
+
+    while(1)
+    {
+        ch = client_getchar();
+        rt_kprintf("%c", ch);
+    }
+}
+
+static rt_err_t client_getchar_rx_ind(rt_device_t dev, rt_size_t size)
+{
+    uint8_t ch;
+    rt_size_t i;
+
+    for (i = 0; i < size; i++)
+    {
+        /* read a char */
+        if (rt_device_read(dev, 0, &ch, 1))
+        {
+            rt_ringbuffer_put_force(client_rx_fifo, &ch, 1);
+            rt_sem_release(&client_rx_notice);
+        }
+    }
+
+    return RT_EOK;
+}
+static void client_cli_parser(void)
+{
+#define ESC_KEY                 0x1B
+#define BACKSPACE_KEY           0x08
+#define DELECT_KEY              0x7F
+
+    extern at_client_t rt_at_get_client(void);
+    at_client_t  client = rt_at_get_client();
+    char ch;
+    char cur_line[FINSH_CMD_SIZE] = { 0 };
+    rt_size_t cur_line_len = 0;
+    static rt_err_t (*client_odev_rx_ind)(rt_device_t dev, rt_size_t size) = RT_NULL;
+    rt_base_t int_lvl;
+    rt_thread_t at_client;
+
+    if (client)
+    {
+        /* backup client device RX indicate */
+        {
+            int_lvl = rt_hw_interrupt_disable();
+            client_odev_rx_ind = client->device->rx_indicate;
+            rt_device_set_rx_indicate(client->device, client_getchar_rx_ind);
+            rt_hw_interrupt_enable(int_lvl);
+        }
+
+        rt_sem_init(&client_rx_notice, "at_cli_client_notice", 0, RT_IPC_FLAG_FIFO);
+        client_rx_fifo = rt_ringbuffer_create(AT_CLI_FIFO_SIZE);
+
+        at_client = rt_thread_create("at_cli_client", at_client_entry, RT_NULL, 512, 8, 8);
+        if (client_rx_fifo && at_client)
+        {
+            rt_kprintf("======== Welcome to using RT-Thread AT command client cli ========\n");
+            rt_kprintf("Cli will forward your command to server port(%s). Press 'ESC' to exit.\n", client->device->parent.name);
+            rt_thread_startup(at_client);
+            /* process user input */
+            while (ESC_KEY != (ch = console_getchar()))
+            {
+                if (ch == BACKSPACE_KEY || ch == DELECT_KEY)
+                {
+                    if (cur_line_len)
+                    {
+                        cur_line[--cur_line_len] = 0;
+                        rt_kprintf("\b \b");
+                    }
+                    continue;
+                }
+                else if (ch == '\r' || ch == '\n')
+                {
+                    /* execute a AT request */
+                    if (cur_line_len)
+                    {
+                        rt_kprintf("\n");
+                        at_exec_cmd(RT_NULL, "%.*s", cur_line_len, cur_line);
+                    }
+                    cur_line_len = 0;
+                }
+                else
+                {
+                    rt_kprintf("%c", ch);
+                    cur_line[cur_line_len++] = ch;
+                }
+            }
+
+            rt_thread_delete(at_client);
+            rt_sem_detach(&client_rx_notice);
+            rt_ringbuffer_destroy(client_rx_fifo);
+            /* restore client device RX indicate */
+            {
+                int_lvl = rt_hw_interrupt_disable();
+                rt_device_set_rx_indicate(client->device, client_odev_rx_ind);
+                rt_hw_interrupt_enable(int_lvl);
+            }
+        }
+        else
+        {
+            rt_kprintf("No mem for AT cli client\n");
+        }
+    }
+    else
+    {
+        rt_kprintf("AT client not initialized\n");
+    }
+}
+#endif /* AT_USING_CLIENT */
+
+static void at(int argc, char **argv)
+{
+    if (argc < 2)
+    {
+        rt_kprintf("Please input 'at <server|client>' \n");
+        return;
+    }
+
+    at_cli_init();
+
+    if (!strcmp(argv[1], "server"))
+    {
+#ifdef AT_USING_SERVER
+        server_cli_parser();
+#else
+        rt_kprintf("Not support AT server, please check your configure!\n");
+#endif
+    }
+    else if (!strcmp(argv[1], "client"))
+    {
+#ifdef AT_USING_CLIENT
+        client_cli_parser();
+#else
+        rt_kprintf("Not support AT client, please check your configure!\n");
+#endif
+    }
+    else
+    {
+        rt_kprintf("Please input 'at <server|client>' \n");
+    }
+
+    at_cli_deinit();
+}
+MSH_CMD_EXPORT(at, RT-Thread AT component cli: at <server|client>);
+
+#endif /* AT_USING_CLI */

+ 716 - 0
components/net/at/src/at_client.c

@@ -0,0 +1,716 @@
+/*
+ * File      : at_client.c
+ * This file is part of RT-Thread RTOS
+ * COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2018-03-30     chenyong     first version
+ * 2018-04-12     chenyong     add client implement
+ */
+
+#include <at.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define AT_RESP_END_OK                 "OK"
+#define AT_RESP_END_ERROR              "ERROR"
+#define AT_RESP_END_FAIL               "FAIL"
+#define AT_END_CR_LF                   "\r\n"
+
+static at_client_t at_client_local = RT_NULL;
+static char cust_end_sign = 0;
+
+extern rt_size_t at_vprintfln(rt_device_t device, const char *format, va_list args);
+extern void at_print_raw_cmd(const char *type, const char *cmd, rt_size_t size);
+extern const char *at_get_last_cmd(rt_size_t *cmd_size);
+
+/**
+ * Create response structure.
+ *
+ * @param buf_size the maximum response buffer size
+ * @param line_num the number of setting response lines
+ *         = 0: the response data will auto return when received 'OK' or 'ERROR'
+ *        != 0: the response data will return when received setting lines number data
+ * @param timeout the maximum response time
+ *
+ * @return != RT_NULL: response structure
+ *          = RT_NULL: no memory
+ */
+at_response_t at_create_resp(rt_size_t buf_size, rt_size_t line_num, rt_int32_t timeout)
+{
+    at_response_t resp = RT_NULL;
+
+    resp = (at_response_t) rt_calloc(1, sizeof(struct at_response));
+    if (!resp)
+    {
+        LOG_E("AT create response structure failed! No memory for response structure!");
+        return RT_NULL;
+    }
+
+    resp->buf = (char *) rt_calloc(1, buf_size);
+    if (!resp->buf)
+    {
+        LOG_E("AT create response structure failed! No memory for response buf structure!");
+        rt_free(resp);
+        return RT_NULL;
+    }
+
+    resp->buf_size = buf_size;
+    resp->line_num = line_num;
+    resp->line_counts = 0;
+    resp->timeout = timeout;
+
+    return resp;
+}
+
+/**
+ * Delete and free response structure.
+ *
+ * @param resp response structure
+ */
+void at_delete_resp(at_response_t resp)
+{
+    if (resp && resp->buf)
+    {
+        rt_free(resp->buf);
+    }
+
+    if (resp)
+    {
+        rt_free(resp);
+        resp = RT_NULL;
+    }
+}
+
+/**
+ * Set response structure information
+ *
+ * @param resp response structure
+ * @param buf_size the maximum response buffer size
+ * @param line_num the number of setting response lines
+ *         = 0: the response data will auto return when received 'OK' or 'ERROR'
+ *        != 0: the response data will return when received setting lines number data
+ * @param timeout the maximum response time
+ *
+ * @return  != RT_NULL: response structure
+ *           = RT_NULL: no memory
+ */
+at_response_t at_resp_set_info(at_response_t resp, rt_size_t buf_size, rt_size_t line_num, rt_int32_t timeout)
+{
+    RT_ASSERT(resp);
+
+    if(resp->buf_size != buf_size)
+    {
+        resp->buf_size = buf_size;
+
+        resp->buf = rt_realloc(resp->buf, buf_size);
+        if(!resp->buf)
+        {
+            LOG_D("No memory for realloc response buffer size(%d).", buf_size);
+            return RT_NULL;
+        }
+    }
+
+    resp->line_num = line_num;
+    resp->timeout = timeout;
+
+    return resp;
+}
+
+/**
+ * Get one line AT response buffer by line number.
+ *
+ * @param resp response structure
+ * @param resp_line line number, start from '1'
+ *
+ * @return != RT_NULL: response line buffer
+ *          = RT_NULL: input response line error
+ */
+const char *at_resp_get_line(at_response_t resp, rt_size_t resp_line)
+{
+    char *resp_buf = resp->buf;
+    char *resp_line_buf = RT_NULL;
+    rt_size_t line_num = 1;
+
+    RT_ASSERT(resp);
+
+    if (resp_line > resp->line_counts || resp_line <= 0)
+    {
+        LOG_E("AT response get line failed! Input response line(%d) error!", resp_line);
+        return RT_NULL;
+    }
+
+    for (line_num = 1; line_num <= resp->line_counts; line_num++)
+    {
+        if (resp_line == line_num)
+        {
+            resp_line_buf = resp_buf;
+
+            return resp_line_buf;
+        }
+
+        resp_buf += strlen(resp_buf) + 1;
+    }
+
+    return RT_NULL;
+}
+
+/**
+ * Get one line AT response buffer by keyword
+ *
+ * @param resp response structure
+ * @param keyword query keyword
+ *
+ * @return != RT_NULL: response line buffer
+ *          = RT_NULL: no matching data
+ */
+const char *at_resp_get_line_by_kw(at_response_t resp, const char *keyword)
+{
+    char *resp_buf = resp->buf;
+    char *resp_line_buf = RT_NULL;
+    rt_size_t line_num = 1;
+
+    RT_ASSERT(resp);
+    RT_ASSERT(keyword);
+
+    for (line_num = 1; line_num <= resp->line_counts; line_num++)
+    {
+        if(strstr(resp_buf, keyword))
+        {
+            resp_line_buf = resp_buf;
+
+            return resp_line_buf;
+        }
+
+        resp_buf += strlen(resp_buf) + 1;
+    }
+
+    return RT_NULL;
+}
+
+/**
+ * Get and parse AT response buffer arguments by line number.
+ *
+ * @param resp response structure
+ * @param resp_line line number, start from '1'
+ * @param resp_expr response buffer expression
+ *
+ * @return -1 : input response line number error or get line buffer error
+ *          0 : parsed without match
+ *         >0 : the number of arguments successfully parsed
+ */
+int at_resp_parse_line_args(at_response_t resp, rt_size_t resp_line, const char *resp_expr, ...)
+{
+    va_list args;
+    int resp_args_num = 0;
+    const char *resp_line_buf = RT_NULL;
+
+    RT_ASSERT(resp);
+    RT_ASSERT(resp_expr);
+
+    if ((resp_line_buf = at_resp_get_line(resp, resp_line)) == RT_NULL)
+    {
+        return -1;
+    }
+
+    va_start(args, resp_expr);
+
+    resp_args_num = vsscanf(resp_line_buf, resp_expr, args);
+
+    va_end(args);
+
+    return resp_args_num;
+}
+
+/**
+ * Get and parse AT response buffer arguments by keyword.
+ *
+ * @param resp response structure
+ * @param keyword query keyword
+ * @param resp_expr response buffer expression
+ *
+ * @return -1 : input keyword error or get line buffer error
+ *          0 : parsed without match
+ *         >0 : the number of arguments successfully parsed
+ */
+int at_resp_parse_line_args_by_kw(at_response_t resp, const char *keyword, const char *resp_expr, ...)
+{
+    va_list args;
+    int resp_args_num = 0;
+    const char *resp_line_buf = RT_NULL;
+
+    RT_ASSERT(resp);
+    RT_ASSERT(resp_expr);
+
+    if ((resp_line_buf = at_resp_get_line_by_kw(resp, keyword)) == RT_NULL)
+    {
+        return -1;
+    }
+
+    va_start(args, resp_expr);
+
+    resp_args_num = vsscanf(resp_line_buf, resp_expr, args);
+
+    va_end(args);
+
+    return resp_args_num;
+}
+
+/**
+ * Send commands to AT server and wait response.
+ *
+ * @param resp AT response structure, using RT_NULL when you don't care response
+ * @param cmd_expr AT commands expression
+ *
+ * @return 0 : success
+ *        -1 : response status error
+ *        -2 : wait timeout
+ */
+int at_exec_cmd(at_response_t resp, const char *cmd_expr, ...)
+{
+    at_client_t client = at_client_local;
+    va_list args;
+    rt_size_t cmd_size = 0;
+    rt_err_t result = RT_EOK;
+    const char *cmd = RT_NULL;
+
+    RT_ASSERT(cmd_expr);
+
+    rt_mutex_take(client->lock, RT_WAITING_FOREVER);
+
+    client->resp_status = AT_RESP_OK;
+    client->resp = resp;
+
+    va_start(args, cmd_expr);
+    at_vprintfln(client->device, cmd_expr, args);
+    va_end(args);
+
+    if (resp)
+    {
+        resp->line_counts = 0;
+        if (rt_sem_take(client->resp_notice, resp->timeout) != RT_EOK)
+        {
+            cmd = at_get_last_cmd(&cmd_size);
+            LOG_E("execute command (%.*s) timeout (%d ticks)!", cmd_size, cmd, resp->timeout);
+            client->resp_status = AT_RESP_TIMEOUT;
+            result = -RT_ETIMEOUT;
+            goto __exit;
+        }
+        if (client->resp_status != AT_RESP_OK)
+        {
+            cmd = at_get_last_cmd(&cmd_size);
+            LOG_E("execute command (%.*s) failed!", cmd_size, cmd);
+            result = -RT_ERROR;
+            goto __exit;
+        }
+    }
+
+__exit:
+    client->resp = RT_NULL;
+
+    rt_mutex_release(client->lock);
+
+    return result;
+}
+
+/**
+ * Send data to AT server, send data don't have end sign(eg: \r\n).
+ *
+ * @param buf   send data buffer
+ * @param size  send fixed data size
+ *
+ * @return send data size
+ */
+rt_size_t at_client_send(const char *buf, rt_size_t size)
+{
+    at_client_t client = at_client_local;
+
+    RT_ASSERT(buf);
+
+#ifdef AT_PRINT_RAW_CMD
+    at_print_raw_cmd("send", buf, size);
+#endif
+
+    return rt_device_write(client->device, 0, buf, size);
+
+}
+
+static char at_client_getchar(void)
+{
+    char ch;
+    at_client_t client = at_client_local;
+
+    rt_sem_take(client->rx_notice, RT_WAITING_FOREVER);
+
+    rt_device_read(client->device, 0, &ch, 1);
+
+    return ch;
+}
+
+/**
+ * AT client receive fixed-length data.
+ *
+ * @param buf   receive data buffer
+ * @param size  receive fixed data size
+ *
+ * @note this function can only be used in execution function of URC data
+ *
+ * @return success receive data size
+ */
+rt_size_t at_client_recv(char *buf, rt_size_t size)
+{
+    rt_size_t read_idx = 0;
+    char ch;
+
+    RT_ASSERT(buf);
+
+    while (1)
+    {
+        if (read_idx < size)
+        {
+            ch = at_client_getchar();
+
+            buf[read_idx++] = ch;
+        }
+        else
+        {
+            break;
+        }
+    }
+
+#ifdef AT_PRINT_RAW_CMD
+    at_print_raw_cmd("urc_recv", buf, size);
+#endif
+
+    return read_idx;
+
+}
+
+/**
+ * get AT client structure pointer.
+ *
+ * @return AT client structure pointer
+ */
+at_client_t rt_at_get_client(void)
+{
+    RT_ASSERT(at_client_local);
+    RT_ASSERT(at_client_local->status != AT_STATUS_UNINITIALIZED);
+
+    return at_client_local;
+}
+
+/**
+ *  AT client set end sign.
+ *
+ * @param ch the end sign, can not be used when it is '\0'
+ *
+ * @return 0: set success
+ */
+int at_set_end_sign(char ch)
+{
+    cust_end_sign = ch;
+
+    return 0;
+}
+
+static const struct at_urc *get_urc_obj(char *data, rt_size_t size)
+{
+    rt_size_t i, prefix_len, suffix_len;
+    at_client_t client = at_client_local;
+
+    if (client->urc_table == RT_NULL)
+    {
+        return RT_NULL;
+    }
+
+    for (i = 0; i < client->urc_table_size; i++)
+    {
+        prefix_len = strlen(client->urc_table[i].cmd_prefix);
+        suffix_len = strlen(client->urc_table[i].cmd_suffix);
+        if (size < prefix_len + suffix_len)
+        {
+            continue;
+        }
+        if ((prefix_len ? !strncmp(data, client->urc_table[i].cmd_prefix, prefix_len) : 1)
+                && (suffix_len ? !strncmp(data + size - suffix_len, client->urc_table[i].cmd_suffix, suffix_len) : 1))
+        {
+            return &client->urc_table[i];
+        }
+    }
+
+    return RT_NULL;
+}
+
+static int at_recv_readline(void)
+{
+    rt_size_t read_len = 0;
+    char ch = 0, last_ch = 0;
+    rt_bool_t is_full = RT_FALSE;
+    at_client_t client = at_client_local;
+
+    memset(client->recv_buffer, 0x00, AT_CLIENT_RECV_BUFF_LEN);
+    client->cur_recv_len = 0;
+
+    while (1)
+    {
+        ch = at_client_getchar();
+
+        if (read_len < AT_CLIENT_RECV_BUFF_LEN)
+        {
+            client->recv_buffer[read_len++] = ch;
+        }
+        else
+        {
+            is_full = RT_TRUE;
+        }
+
+        /* is newline or URC data */
+        if ((ch == '\n' && last_ch == '\r') || (cust_end_sign != 0 && ch == cust_end_sign)
+                || get_urc_obj(client->recv_buffer, read_len))
+        {
+            if (is_full)
+            {
+                LOG_E("read line failed. The line data length is out of buffer size(%d)!", AT_CLIENT_RECV_BUFF_LEN);
+                memset(client->recv_buffer, 0x00, AT_CLIENT_RECV_BUFF_LEN);
+                client->cur_recv_len = 0;
+                return -RT_EFULL;
+            }
+            client->cur_recv_len = read_len;
+            break;
+        }
+        last_ch = ch;
+    }
+
+#ifdef AT_PRINT_RAW_CMD
+    at_print_raw_cmd("recvline", client->recv_buffer, read_len);
+#endif
+
+    return read_len;
+}
+
+static void client_parser(at_client_t client)
+{
+    int resp_buf_len = 0;
+    const struct at_urc *urc;
+    rt_size_t line_counts = 0;
+
+    while(1)
+    {
+        if (at_recv_readline() > 0)
+        {
+            if ((urc = get_urc_obj(client->recv_buffer, client->cur_recv_len)) != RT_NULL)
+            {
+                /* current receive is request, try to execute related operations */
+                if (urc->func != RT_NULL)
+                {
+                    urc->func(client->recv_buffer, client->cur_recv_len);
+                }
+            }
+            else if (client->resp != RT_NULL)
+            {
+                /* current receive is response */
+                client->recv_buffer[client->cur_recv_len - 1] = '\0';
+                if (resp_buf_len + client->cur_recv_len < client->resp->buf_size)
+                {
+                    /* copy response lines, separated by '\0' */
+                    memcpy(client->resp->buf + resp_buf_len, client->recv_buffer, client->cur_recv_len);
+                    resp_buf_len += client->cur_recv_len;
+
+                    line_counts++;
+                }
+                else
+                {
+                    client->resp_status = AT_RESP_BUFF_FULL;
+                    LOG_E("Read response buffer failed. The Response buffer size is out of buffer size(%d)!", client->resp->buf_size);
+                }
+                /* check response result */
+                if (memcmp(client->recv_buffer, AT_RESP_END_OK, strlen(AT_RESP_END_OK)) == 0
+                        && client->resp->line_num == 0)
+                {
+                    /* get the end data by response result, return response state END_OK. */
+                    client->resp_status = AT_RESP_OK;
+                }
+                else if ((memcmp(client->recv_buffer, AT_RESP_END_ERROR, strlen(AT_RESP_END_ERROR)) == 0)
+                        || (memcmp(client->recv_buffer, AT_RESP_END_FAIL, strlen(AT_RESP_END_FAIL)) == 0))
+                {
+                    client->resp_status = AT_RESP_ERROR;
+                }
+                else if (line_counts == client->resp->line_num && client->resp->line_num)
+                {
+                    /* get the end data by response line, return response state END_OK.*/
+                    client->resp_status = AT_RESP_OK;
+                }
+                else
+                {
+                    continue;
+                }
+                client->resp->line_counts = line_counts;
+
+                client->resp = RT_NULL;
+                rt_sem_release(client->resp_notice);
+                resp_buf_len = 0, line_counts = 0;
+            }
+            else
+            {
+//                log_d("unrecognized line: %.*s", client->cur_recv_len, client->recv_buffer);
+            }
+        }
+    }
+}
+
+static rt_err_t at_client_rx_ind(rt_device_t dev, rt_size_t size)
+{
+    rt_sem_release(at_client_local->rx_notice);
+    return RT_EOK;
+}
+
+/**
+ * Set URC(Unsolicited Result Code) table
+ *
+ * @param table URC table
+ * @param size table size
+ */
+void at_set_urc_table(const struct at_urc *table, rt_size_t size)
+{
+    rt_size_t idx;
+
+    for(idx = 0; idx < size; idx++)
+    {
+        RT_ASSERT(table[idx].cmd_prefix);
+        RT_ASSERT(table[idx].cmd_suffix);
+    }
+
+    at_client_local->urc_table = table;
+    at_client_local->urc_table_size = size;
+
+}
+
+int at_client_init(void)
+{
+    int result = RT_EOK;
+
+    if (at_client_local)
+    {
+        return result;
+    }
+
+    at_client_local = (at_client_t) rt_calloc(1, sizeof(struct at_client));
+    if (!at_client_local)
+    {
+        result = -RT_ERROR;
+        LOG_E("AT client session initialize failed! No memory for at_client structure !");
+        goto __exit;
+    }
+
+    at_client_local->status = AT_STATUS_UNINITIALIZED;
+    at_client_local->lock = rt_mutex_create("at_lock", RT_IPC_FLAG_FIFO);
+    if(!at_client_local->lock)
+    {
+        LOG_E("AT client session initialize failed! at_client_recv_lock create failed!");
+        result = -RT_ENOMEM;
+        goto __exit;
+    }
+
+    at_client_local->cur_recv_len = 0;
+
+    at_client_local->rx_notice = rt_sem_create("at_client_notice", 0, RT_IPC_FLAG_FIFO);
+    if (!at_client_local->rx_notice)
+    {
+        LOG_E("AT client session initialize failed! at_client_notice semaphore create failed!");
+        result = -RT_ENOMEM;
+        goto __exit;
+    }
+
+    at_client_local->resp_notice = rt_sem_create("at_client_resp",  0, RT_IPC_FLAG_FIFO);
+    if (!at_client_local->resp_notice)
+    {
+        LOG_E("AT client session initialize failed! at_client_resp semaphore create failed!");
+        result = -RT_ENOMEM;
+        goto __exit;
+    }
+
+    /* Find and open command device */
+    at_client_local->device = rt_device_find(AT_CLIENT_DEVICE);
+    if (at_client_local->device)
+    {
+        RT_ASSERT(at_client_local->device->type == RT_Device_Class_Char);
+
+        rt_device_open(at_client_local->device, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_RX);
+
+        rt_device_set_rx_indicate(at_client_local->device, at_client_rx_ind);
+    }
+    else
+    {
+        LOG_E("AT client device initialize failed! Not find the device : %s.", AT_CLIENT_DEVICE);
+        result = -RT_ERROR;
+        goto __exit;
+    }
+
+    at_client_local->urc_table = RT_NULL;
+    at_client_local->urc_table_size = 0;
+
+    at_client_local->parser = rt_thread_create("at_client",
+                                         (void (*)(void *parameter))client_parser,
+                                         at_client_local,
+                                         1024 + 512,
+                                         RT_THREAD_PRIORITY_MAX / 3 - 1,
+                                         5);
+    if (at_client_local->parser == RT_NULL)
+    {
+        result = -RT_ENOMEM;
+        goto __exit;
+    }
+
+    if ((result = at_client_port_init()) != RT_EOK)
+    {
+        LOG_E("AT client port initialize failed(%d).", result);
+    }
+
+__exit:
+    if (!result)
+    {
+        at_client_local->status = AT_STATUS_INITIALIZED;
+
+        rt_thread_startup(at_client_local->parser);
+
+        LOG_I("RT-Thread AT client (V%s) initialize success.", AT_SW_VERSION);
+    }
+    else
+    {
+        if (at_client_local)
+        {
+            rt_free(at_client_local);
+        }
+
+        LOG_E("RT-Thread AT client (V%s) initialize failed(%d).", AT_SW_VERSION, result);
+    }
+
+    return result;
+}
+INIT_COMPONENT_EXPORT(at_client_init);
+
+RT_WEAK int at_client_port_init(void)
+{
+    at_client_local->urc_table = RT_NULL;
+    at_client_local->urc_table_size = 0;
+
+    LOG_E("The client porting initialize for AT client is not implement.");
+
+    return 0;
+}

+ 554 - 0
components/net/at/src/at_server.c

@@ -0,0 +1,554 @@
+/*
+ * File      : at_server.c
+ * This file is part of RT-Thread RTOS
+ * COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2018-03-30     chenyong     first version
+ * 2018-04-14     chenyong     modify parse arguments
+ */
+
+#include <at.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <rthw.h>
+
+#define AT_CMD_CHAR_0                  '0'
+#define AT_CMD_CHAR_9                  '9'
+#define AT_CMD_QUESTION_MARK           '?'
+#define AT_CMD_EQUAL_MARK              '='
+#define AT_CMD_L_SQ_BRACKET            '['
+#define AT_CMD_R_SQ_BRACKET            ']'
+#define AT_CMD_L_ANGLE_BRACKET         '<'
+#define AT_CMD_R_ANGLE_BRACKET         '>'
+#define AT_CMD_COMMA_MARK              ','
+#define AT_CMD_SEMICOLON               ';'
+#define AT_CMD_CR                      '\r'
+#define AT_CMD_LF                      '\n'
+
+static at_server_t at_server_local = RT_NULL;
+static at_cmd_t cmd_table = RT_NULL;
+static rt_size_t cmd_num;
+
+extern void at_vprintf(rt_device_t device, const char *format, va_list args);
+extern void at_vprintfln(rt_device_t device, const char *format, va_list args);
+
+/**
+ * AT server send data to AT device
+ *
+ * @param format the input format
+ */
+void at_server_printf(const char *format, ...)
+{
+    va_list args;
+
+    va_start(args, format);
+
+    at_vprintf(at_server_local->device, format, args);
+
+    va_end(args);
+}
+
+/**
+ * AT server send data and newline to AT device
+ *
+ * @param format the input format
+ */
+void at_server_printfln(const char *format, ...)
+{
+    va_list args;
+
+    va_start(args, format);
+
+    at_vprintfln(at_server_local->device, format, args);
+
+    va_end(args);
+}
+
+
+/**
+ * AT server request arguments parse arguments
+ *
+ * @param req_args request arguments
+ * @param req_expr request expression
+ *
+ * @return  -1 : parse arguments failed
+ *           0 : parse without match
+ *          >0 : The number of arguments successfully parsed
+ */
+int at_req_parse_args(const char *req_args, const char *req_expr, ...)
+{
+    va_list args;
+    int req_args_num = 0;
+
+    RT_ASSERT(req_args);
+    RT_ASSERT(req_expr);
+
+    va_start(args, req_expr);
+
+    req_args_num = vsscanf(req_args, req_expr, args);
+
+    va_end(args);
+
+    return req_args_num;
+}
+
+/**
+ * AT server send command execute result to AT device
+ *
+ * @param result AT command execute result
+ */
+void at_server_print_result(at_result_t result)
+{
+    switch (result)
+    {
+    case AT_RESULT_OK:
+        at_server_printfln("");
+        at_server_printfln("OK");
+        break;
+
+    case AT_RESULT_FAILE:
+        at_server_printfln("");
+        at_server_printfln("ERROR");
+        break;
+
+    case AT_RESULT_NULL:
+        break;
+
+    case AT_RESULT_CMD_ERR:
+        at_server_printfln("ERR CMD MATCH FAILED!");
+        at_server_print_result(AT_RESULT_FAILE);
+        break;
+
+    case AT_RESULT_CHECK_FAILE:
+        at_server_printfln("ERR CHECK ARGS FORMAT FAILED!");
+        at_server_print_result(AT_RESULT_FAILE);
+        break;
+
+    case AT_RESULT_PARSE_FAILE:
+        at_server_printfln("ERR PARSE ARGS FAILED!");
+        at_server_print_result(AT_RESULT_FAILE);
+        break;
+
+    default:
+        break;
+    }
+}
+
+/**
+ *  AT server print all commands to AT device
+ */
+void rt_at_server_print_all_cmd(void)
+{
+    rt_size_t i = 0;
+
+    at_server_printfln("Commands list : ");
+
+    for (i = 0; i < cmd_num; i++)
+    {
+        at_server_printf("%s", cmd_table[i].name);
+
+        if (cmd_table[i].args_expr)
+        {
+            at_server_printfln("%s", cmd_table[i].args_expr);
+        }
+        else
+        {
+            at_server_printf("%c%c", AT_CMD_CR, AT_CMD_LF);
+        }
+    }
+}
+
+at_server_t at_get_server(void)
+{
+    RT_ASSERT(at_server_local);
+    RT_ASSERT(at_server_local->status != AT_STATUS_UNINITIALIZED);
+
+    return at_server_local;
+}
+
+static rt_err_t at_check_args(const char *args, const char *args_format)
+{
+    rt_size_t left_sq_bracket_num = 0, right_sq_bracket_num = 0;
+    rt_size_t left_angle_bracket_num = 0, right_angle_bracket_num = 0;
+    rt_size_t comma_mark_num = 0;
+    rt_size_t i = 0;
+
+    RT_ASSERT(args);
+    RT_ASSERT(args_format);
+
+    for (i = 0; i < strlen(args_format); i++)
+    {
+        switch (args_format[i])
+        {
+        case AT_CMD_L_SQ_BRACKET:
+            left_sq_bracket_num++;
+            break;
+
+        case AT_CMD_R_SQ_BRACKET:
+            right_sq_bracket_num++;
+            break;
+
+        case AT_CMD_L_ANGLE_BRACKET:
+            left_angle_bracket_num++;
+            break;
+
+        case AT_CMD_R_ANGLE_BRACKET:
+            right_angle_bracket_num++;
+            break;
+
+        default:
+            break;
+        }
+    }
+
+    if (left_sq_bracket_num != right_sq_bracket_num || left_angle_bracket_num != right_angle_bracket_num
+            || left_sq_bracket_num > left_angle_bracket_num)
+    {
+        return -RT_ERROR;
+    }
+
+    for (i = 0; i < strlen(args); i++)
+    {
+        if (args[i] == AT_CMD_COMMA_MARK)
+        {
+            comma_mark_num++;
+        }
+    }
+
+    if ((comma_mark_num + 1 < left_angle_bracket_num - left_sq_bracket_num)
+            || comma_mark_num + 1 > left_angle_bracket_num)
+    {
+        return -RT_ERROR;
+    }
+
+    return RT_EOK;
+}
+
+static rt_err_t at_cmd_process(at_cmd_t cmd, const char *cmd_args)
+{
+    at_result_t result = AT_RESULT_OK;
+
+    RT_ASSERT(cmd);
+    RT_ASSERT(cmd_args);
+
+    if (cmd_args[0] == AT_CMD_EQUAL_MARK && cmd_args[1] == AT_CMD_QUESTION_MARK && cmd_args[2] == AT_CMD_CR)
+    {
+        if (cmd->test == RT_NULL)
+        {
+            at_server_print_result(AT_RESULT_CMD_ERR);
+            return -RT_ERROR;
+        }
+
+        result = cmd->test();
+        at_server_print_result(result);
+    }
+    else if (cmd_args[0] == AT_CMD_QUESTION_MARK && cmd_args[1] == AT_CMD_CR)
+    {
+        if (cmd->query == RT_NULL)
+        {
+            at_server_print_result(AT_RESULT_CMD_ERR);
+            return -RT_ERROR;
+        }
+
+        result = cmd->query();
+        at_server_print_result(result);
+    }
+    else if (cmd_args[0] == AT_CMD_EQUAL_MARK
+            || (cmd_args[0] >= AT_CMD_CHAR_0 && cmd_args[0] <= AT_CMD_CHAR_9 && cmd_args[1] == AT_CMD_CR))
+    {
+        if (cmd->setup == RT_NULL)
+        {
+            at_server_print_result(AT_RESULT_CMD_ERR);
+            return -RT_ERROR;
+        }
+
+        if(at_check_args(cmd_args, cmd->args_expr) < 0)
+        {
+            at_server_print_result(AT_RESULT_CHECK_FAILE);
+            return -RT_ERROR;
+        }
+
+        result = cmd->setup(cmd_args);
+        at_server_print_result(result);
+    }
+    else if (cmd_args[0] == AT_CMD_CR)
+    {
+        if (cmd->exec == RT_NULL)
+        {
+            at_server_print_result(AT_RESULT_CMD_ERR);
+            return -RT_ERROR;
+        }
+
+        result = cmd->exec();
+        at_server_print_result(result);
+    }
+    else
+    {
+        return -RT_ERROR;
+    }
+
+    return RT_EOK;
+}
+
+static at_cmd_t at_find_cmd(const char *cmd)
+{
+    rt_size_t i = 0;
+
+    RT_ASSERT(cmd_table);
+
+    for (i = 0; i < cmd_num; i++)
+    {
+        if (!strcasecmp(cmd, cmd_table[i].name))
+        {
+            return &cmd_table[i];
+        }
+    }
+    return RT_NULL;
+}
+
+static rt_err_t at_cmd_get_name(const char *cmd_buffer, char *cmd_name)
+{
+    rt_size_t cmd_name_len = 0, i = 0;
+
+    RT_ASSERT(cmd_name);
+    RT_ASSERT(cmd_buffer);
+
+    for (i = 0; i < strlen(cmd_buffer) + 1; i++)
+    {
+        if (*(cmd_buffer + i) == AT_CMD_QUESTION_MARK || *(cmd_buffer + i) == AT_CMD_EQUAL_MARK
+                || *(cmd_buffer + i) == AT_CMD_CR
+                || (*(cmd_buffer + i) >= AT_CMD_CHAR_0 && *(cmd_buffer + i) <= AT_CMD_CHAR_9))
+        {
+            cmd_name_len = i;
+            memcpy(cmd_name, cmd_buffer, cmd_name_len);
+            *(cmd_name + cmd_name_len) = '\0';
+
+            return RT_EOK;
+        }
+    }
+
+    return -RT_ERROR;
+}
+
+static char at_server_gerchar(void)
+{
+    char ch;
+
+    rt_sem_take(at_server_local->rx_notice, RT_WAITING_FOREVER);
+
+    rt_device_read(at_server_local->device, 0, &ch, 1);
+
+    return ch;
+}
+
+static void server_parser(at_server_t server)
+{
+#define ESC_KEY                 0x1B
+#define BACKSPACE_KEY           0x08
+#define DELECT_KEY              0x7F
+
+    char cur_cmd_name[AT_CMD_NAME_LEN] = { 0 };
+    at_cmd_t cur_cmd = RT_NULL;
+    char *cur_cmd_args = RT_NULL, ch, last_ch;
+
+    RT_ASSERT(server);
+    RT_ASSERT(server->status != AT_STATUS_UNINITIALIZED);
+
+    while (ESC_KEY != (ch = server->get_char()))
+    {
+        if (server->echo_mode)
+        {
+            if (ch == AT_CMD_CR || (ch == AT_CMD_LF && last_ch != AT_CMD_CR))
+            {
+                at_server_printf("%c%c", AT_CMD_CR, AT_CMD_LF);
+            }
+            else if (ch == BACKSPACE_KEY || ch == DELECT_KEY)
+            {
+                if (server->cur_recv_len)
+                {
+                    server->recv_buffer[--server->cur_recv_len] = 0;
+                    at_server_printf("\b \b");
+                }
+
+                continue;
+            }
+            else
+            {
+                at_server_printf("%c", ch);
+            }
+        }
+
+        server->recv_buffer[server->cur_recv_len++] = ch;
+        last_ch = ch;
+
+        if(!strstr(server->recv_buffer, server->end_mark))
+        {
+            continue;
+        }
+
+        if (at_cmd_get_name(server->recv_buffer, cur_cmd_name) < 0)
+        {
+            at_server_print_result(AT_RESULT_CMD_ERR);
+            goto __retry;
+        }
+
+        cur_cmd = at_find_cmd(cur_cmd_name);
+        if (!cur_cmd)
+        {
+            at_server_print_result(AT_RESULT_CMD_ERR);
+            goto __retry;
+        }
+
+        cur_cmd_args = server->recv_buffer + strlen(cur_cmd_name);
+        if (at_cmd_process(cur_cmd, cur_cmd_args) < 0)
+        {
+            goto __retry;
+        }
+
+__retry:
+        memset(server->recv_buffer, 0x00, AT_SERVER_RECV_BUFF_LEN);
+        server->cur_recv_len = 0;
+    }
+}
+
+static rt_err_t at_rx_ind(rt_device_t dev, rt_size_t size)
+{
+    rt_sem_release(at_server_local->rx_notice);
+
+    return RT_EOK;
+}
+
+#if defined(__ICCARM__) || defined(__ICCRX__)               /* for IAR compiler */
+#pragma section="RtAtCmdTab"
+#endif
+
+int at_server_init(void)
+{
+    rt_err_t result = RT_EOK;
+
+    if (at_server_local)
+    {
+        return result;
+    }
+
+    /* initialize the AT commands table.*/
+#if defined(__CC_ARM)                                 /* ARM C Compiler */
+    extern const int RtAtCmdTab$$Base;
+    extern const int RtAtCmdTab$$Limit;
+    cmd_table = (at_cmd_t)&RtAtCmdTab$$Base;
+    cmd_num = (at_cmd_t)&RtAtCmdTab$$Limit - cmd_table;
+#elif defined (__ICCARM__) || defined(__ICCRX__)      /* for IAR Compiler */
+    cmd_table = (at_cmd_t)__section_begin("RtAtCmdTab");
+    cmd_num = (at_cmd_t)__section_end("RtAtCmdTab") - cmd_table;
+#elif defined (__GNUC__)                             /* for GCC Compiler */
+    extern const int __rtatcmdtab_start;
+    extern const int __rtatcmdtab_end;
+    cmd_table = (at_cmd_t)&__rtatcmdtab_start;
+    cmd_num = (at_cmd_t) &__rtatcmdtab_end - cmd_table;
+#endif /* defined(__CC_ARM) */
+
+    at_server_local = (at_server_t) rt_calloc(1, sizeof(struct at_server));
+    if (!at_server_local)
+    {
+        result = -RT_ENOMEM;
+        LOG_E("AT server session initialize failed! No memory for at_server structure !");
+        goto __exit;
+    }
+
+    at_server_local->echo_mode = 1;
+    at_server_local->status = AT_STATUS_UNINITIALIZED;
+
+    memset(at_server_local->recv_buffer, 0x00, AT_SERVER_RECV_BUFF_LEN);
+    at_server_local->cur_recv_len = 0;
+
+    at_server_local->rx_notice = rt_sem_create("at_server_notice", 0, RT_IPC_FLAG_FIFO);
+    if (!at_server_local->rx_notice)
+    {
+        LOG_E("AT server session initialize failed! at_rx_notice semaphore create failed!");
+        result = -RT_ENOMEM;
+        goto __exit;
+    }
+
+    /* Find and open command device */
+    at_server_local->device = rt_device_find(AT_SERVER_DEVICE);
+    if (at_server_local->device)
+    {
+        RT_ASSERT(at_server_local->device->type == RT_Device_Class_Char);
+
+        rt_device_open(at_server_local->device, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_RX);
+
+        rt_device_set_rx_indicate(at_server_local->device, at_rx_ind);
+    }
+    else
+    {
+        LOG_E("AT device initialize failed! Not find the device : %s.", AT_SERVER_DEVICE);
+        result = -RT_ERROR;
+        goto __exit;
+    }
+
+    at_server_local->get_char = at_server_gerchar;
+    memcpy(at_server_local->end_mark, AT_CMD_END_MARK, sizeof(AT_CMD_END_MARK));
+
+    at_server_local->parser_entry = server_parser;
+    at_server_local->parser = rt_thread_create("at_server",
+                                         (void (*)(void *parameter))server_parser,
+                                         at_server_local,
+                                         2 * 1024,
+                                         RT_THREAD_PRIORITY_MAX / 3 - 1,
+                                         5);
+    if (at_server_local->parser == RT_NULL)
+    {
+        result = -RT_ENOMEM;
+        goto __exit;
+    }
+
+__exit:
+    if (!result)
+    {
+        at_server_local->status = AT_STATUS_INITIALIZED;
+
+        rt_thread_startup(at_server_local->parser);
+
+        LOG_I("RT-Thread AT server (V%s) initialize success.", AT_SW_VERSION);
+    }
+    else
+    {
+        if (at_server_local)
+        {
+            rt_free(at_server_local);
+        }
+
+        LOG_E("RT-Thread AT server (V%s) initialize failed(%d).", AT_SW_VERSION, result);
+    }
+
+    return result;
+}
+INIT_COMPONENT_EXPORT(at_server_init);
+
+RT_WEAK void at_port_reset(void)
+{
+    LOG_E("The reset for AT server is not implement.");
+}
+
+RT_WEAK void at_port_factory_reset(void)
+{
+    LOG_E("The factory reset for AT server is not implement.");
+}

+ 102 - 0
components/net/at/src/at_utils.c

@@ -0,0 +1,102 @@
+/*
+ * File      : at_utils.c
+ * This file is part of RT-Thread RTOS
+ * COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2018-04-14     chenyong     first version
+ */
+
+#include <at.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+static char send_buf[AT_CMD_MAX_LEN];
+static rt_size_t last_cmd_len = 0;
+
+/**
+ * dump hex format data to console device
+ *
+ * @param name name for hex object, it will show on log header
+ * @param buf hex buffer
+ * @param size buffer size
+ */
+void at_print_raw_cmd(const char *name, const char *buf, rt_size_t size)
+{
+#define __is_print(ch)       ((unsigned int)((ch) - ' ') < 127u - ' ')
+#define WIDTH_SIZE           32
+
+    rt_size_t i, j;
+
+    for (i = 0; i < size; i += WIDTH_SIZE)
+    {
+        rt_kprintf("[D/AT] %s: %04X-%04X: ", name, i, i + WIDTH_SIZE);
+        for (j = 0; j < WIDTH_SIZE; j++)
+        {
+            if (i + j < size)
+            {
+                rt_kprintf("%02X ", buf[i + j]);
+            }
+            else
+            {
+                rt_kprintf("   ");
+            }
+            if ((j + 1) % 8 == 0)
+            {
+                rt_kprintf(" ");
+            }
+        }
+        rt_kprintf("  ");
+        for (j = 0; j < WIDTH_SIZE; j++)
+        {
+            if (i + j < size)
+            {
+                rt_kprintf("%c", __is_print(buf[i + j]) ? buf[i + j] : '.');
+            }
+        }
+        rt_kprintf("\n");
+    }
+}
+
+const char *at_get_last_cmd(rt_size_t *cmd_size)
+{
+    *cmd_size = last_cmd_len;
+    return send_buf;
+}
+
+rt_size_t at_vprintf(rt_device_t device, const char *format, va_list args)
+{
+    last_cmd_len = vsnprintf(send_buf, sizeof(send_buf), format, args);
+
+#ifdef AT_PRINT_RAW_CMD
+    at_print_raw_cmd("send", send_buf, last_cmd_len);
+#endif
+
+    return rt_device_write(device, 0, send_buf, last_cmd_len);
+}
+
+rt_size_t at_vprintfln(rt_device_t device, const char *format, va_list args)
+{
+    rt_size_t len;
+
+    len = at_vprintf(device, format, args);
+
+    rt_device_write(device, 0, "\r\n", 2);
+
+    return len + 2;
+}