summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBernard Xiong <[email protected]>2018-07-26 10:40:06 +0800
committerGitHub <[email protected]>2018-07-26 10:40:06 +0800
commit4468c4ebffd2431ba7cb9b5e3037c1f759b7646b (patch)
tree4d17f14d1f33e939c39c98389c962abbd883bd6a
parenta400bd77df3a18e5dd67e1aef829914ff01c7cf0 (diff)
parent8b887e76e929904a365dff64c1507afc47b93e3f (diff)
Merge pull request #1663 from chenyong111/sal
[net][at] Add AT commands component
-rw-r--r--components/net/Kconfig6
-rw-r--r--components/net/at/Kconfig78
-rw-r--r--components/net/at/SConscript26
-rw-r--r--components/net/at/at_socket/at_socket.c1138
-rw-r--r--components/net/at/at_socket/at_socket.h163
-rw-r--r--components/net/at/include/at.h256
-rw-r--r--components/net/at/src/at_base_cmd.c131
-rw-r--r--components/net/at/src/at_cli.c332
-rw-r--r--components/net/at/src/at_client.c716
-rw-r--r--components/net/at/src/at_server.c554
-rw-r--r--components/net/at/src/at_utils.c102
11 files changed, 3500 insertions, 2 deletions
diff --git a/components/net/Kconfig b/components/net/Kconfig
index ac7a063edd..f79ac27843 100644
--- a/components/net/Kconfig
+++ b/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
diff --git a/components/net/at/Kconfig b/components/net/at/Kconfig
new file mode 100644
index 0000000000..df34b70091
--- /dev/null
+++ b/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
diff --git a/components/net/at/SConscript b/components/net/at/SConscript
new file mode 100644
index 0000000000..2a25aaeb42
--- /dev/null
+++ b/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')
diff --git a/components/net/at/at_socket/at_socket.c b/components/net/at/at_socket/at_socket.c
new file mode 100644
index 0000000000..0beaa9645c
--- /dev/null
+++ b/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);
diff --git a/components/net/at/at_socket/at_socket.h b/components/net/at/at_socket/at_socket.h
new file mode 100644
index 0000000000..612078522d
--- /dev/null
+++ b/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__ */
diff --git a/components/net/at/include/at.h b/components/net/at/include/at.h
new file mode 100644
index 0000000000..2c866891d2
--- /dev/null
+++ b/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__ */
diff --git a/components/net/at/src/at_base_cmd.c b/components/net/at/src/at_base_cmd.c
new file mode 100644
index 0000000000..b334b1dba4
--- /dev/null
+++ b/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);
diff --git a/components/net/at/src/at_cli.c b/components/net/at/src/at_cli.c
new file mode 100644
index 0000000000..0136922199
--- /dev/null
+++ b/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 */
diff --git a/components/net/at/src/at_client.c b/components/net/at/src/at_client.c
new file mode 100644
index 0000000000..f6145232b3
--- /dev/null
+++ b/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;
+}
diff --git a/components/net/at/src/at_server.c b/components/net/at/src/at_server.c
new file mode 100644
index 0000000000..a2ff765ee8
--- /dev/null
+++ b/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.");
+}
diff --git a/components/net/at/src/at_utils.c b/components/net/at/src/at_utils.c
new file mode 100644
index 0000000000..25ee705ee6
--- /dev/null
+++ b/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;
+}