From 84303edb8a007e593370e4c42901be909189c3b4 Mon Sep 17 00:00:00 2001 From: chenyong <1521761801@qq.com> Date: Mon, 27 May 2019 13:41:17 +0800 Subject: [PATCH] =?UTF-8?q?=E3=80=90=E4=BF=AE=E6=94=B9=E3=80=91=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=20V2.0.0=20=E7=89=88=E6=9C=AC=EF=BC=8C=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=20AT=20Socket=20=E5=A4=9A=E5=AE=A2=E6=88=B7=E7=AB=AF?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: chenyong <1521761801@qq.com> --- README.md | 142 +- SConscript | 71 +- at_socket_ec20.c | 1715 ----------------- at_socket_esp8266.c | 1169 ----------- at_socket_m26.c | 1273 ------------ at_socket_rw007.c | 773 -------- at_socket_sim76xx.c | 1225 ------------ at_socket_sim800c.c | 1302 ------------- class/ec20/at_device_ec20.c | 1040 ++++++++++ class/ec20/at_device_ec20.h | 67 + class/ec20/at_socket_ec20.c | 1007 ++++++++++ class/esp8266/at_device_esp8266.c | 929 +++++++++ class/esp8266/at_device_esp8266.h | 66 + class/esp8266/at_socket_esp8266.c | 559 ++++++ class/m26/at_device_m26.c | 881 +++++++++ class/m26/at_device_m26.h | 66 + class/m26/at_socket_m26.c | 702 +++++++ class/rw007/at_device_rw007.c | 403 ++++ class/rw007/at_device_rw007.h | 66 + class/rw007/at_socket_rw007.c | 561 ++++++ class/sim76xx/at_device_sim76xx.c | 610 ++++++ class/sim76xx/at_device_sim76xx.h | 66 + class/sim76xx/at_socket_sim76xx.c | 882 +++++++++ class/sim800c/at_device_sim800c.c | 971 ++++++++++ class/sim800c/at_device_sim800c.h | 66 + class/sim800c/at_socket_sim800c.c | 658 +++++++ inc/at_device.h | 135 ++ .../at_sample_client.c | 2 +- samples/at_sample_ec20.c | 52 + samples/at_sample_esp8266.c | 52 + samples/at_sample_m26.c | 52 + samples/at_sample_rw007.c | 52 + samples/at_sample_sim76xx.c | 52 + samples/at_sample_sim800c.c | 52 + src/at_device.c | 330 ++++ 35 files changed, 10543 insertions(+), 7506 deletions(-) delete mode 100644 at_socket_ec20.c delete mode 100644 at_socket_esp8266.c delete mode 100644 at_socket_m26.c delete mode 100644 at_socket_rw007.c delete mode 100755 at_socket_sim76xx.c delete mode 100644 at_socket_sim800c.c create mode 100644 class/ec20/at_device_ec20.c create mode 100644 class/ec20/at_device_ec20.h create mode 100644 class/ec20/at_socket_ec20.c create mode 100644 class/esp8266/at_device_esp8266.c create mode 100644 class/esp8266/at_device_esp8266.h create mode 100644 class/esp8266/at_socket_esp8266.c create mode 100644 class/m26/at_device_m26.c create mode 100644 class/m26/at_device_m26.h create mode 100644 class/m26/at_socket_m26.c create mode 100644 class/rw007/at_device_rw007.c create mode 100644 class/rw007/at_device_rw007.h create mode 100644 class/rw007/at_socket_rw007.c create mode 100644 class/sim76xx/at_device_sim76xx.c create mode 100644 class/sim76xx/at_device_sim76xx.h create mode 100644 class/sim76xx/at_socket_sim76xx.c create mode 100644 class/sim800c/at_device_sim800c.c create mode 100644 class/sim800c/at_device_sim800c.h create mode 100644 class/sim800c/at_socket_sim800c.c create mode 100644 inc/at_device.h rename at_client_sample.c => samples/at_sample_client.c (99%) create mode 100644 samples/at_sample_ec20.c create mode 100644 samples/at_sample_esp8266.c create mode 100644 samples/at_sample_m26.c create mode 100644 samples/at_sample_rw007.c create mode 100644 samples/at_sample_sim76xx.c create mode 100644 samples/at_sample_sim800c.c create mode 100644 src/at_device.c diff --git a/README.md b/README.md index 3c8eb36..49b6ced 100644 --- a/README.md +++ b/README.md @@ -2,19 +2,22 @@ ## 1. 简介 ## -AT device 软件包是由 RT-Thread AT 组件针对不同 AT 设备的移植文件和示例代码组成,目前支持的 AT 设备有:ESP8266、M26、MC20、EC20、RW007、SIM800C 以及 SIM76XX 系列设备等,目前上述设备都完成对 `AT socket` 功能的移植,及设备通过 AT 命令实现标准 socket 编程接口,完成 socket 通讯的功能,具体功能介绍可参考 [《RT-Thread 编程指南》](https://github.com/RT-Thread/rtthread-manual-doc)AT 命令章节 。 +AT device 软件包是由 RT-Thread AT 组件针对不同 AT 设备的移植文件和示例代码组成,目前支持的 AT 设备有:ESP8266、M26、MC20、RW007、SIM800C 以及 SIM76XX 系列设备等,目前上述设备都完成对 `AT socket` 功能的移植,及设备通过 AT 命令实现标准 socket 编程接口,完成 socket 通讯的功能,具体功能介绍可参考 [《RT-Thread 编程指南》](https://github.com/RT-Thread/rtthread-manual-doc)AT 命令章节 。 -### 1.1. 文件结构 ### +### 1.1. 目录结构 ### | 名称 | 说明 | | ---- | ---- | -| at_socket_esp8266.c | ESP8266 模块针对 AT 组件的移植文件,实现 AT socket | -| at_socket_rw007.c | RW007 模块针对 AT 组件的移植文件,实现 AT socket | -| at_socket_sim800c.c | SIM800C 模块针对 AT 组件的移植文件,实现 AT socket | -| at_socket_sim76xx.c | SIM76XX 模块针对 AT 组件的移植文件,实现 AT socket | -| at_socket_m26.c | M26/MC20 模块针对 AT 组件的移植文件,实现 AT socket | -| at_socket_ec20.c | EC20 模块针对 AT 组件的移植文件,实现 AT socket | -| at_client_sample.c | ESP8266 模块 AT Client 功能示例文件 | +| src | AT device 实现源码目录 | +| inc | AT device 头文件目录 | +| sample | 不同设备示例文件目录 | +| class | 不同设备针对 AT 组件的移植适配目录 | +| class/esp8266 | ESP8266 设备针对 AT 组件的移植目录,实现 AT Socket 功能 | +| class/rw007 | RW007 设备针对 AT 组件的移植目录,实现 AT Socket 功能 | +| class/sim800c | SIM800C 设备针对 AT 组件的移植目录,实现 AT Socket 功能 | +| class/sim76xx | SIM76XX 设备针对 AT 组件的移植目录,实现 AT Socket 功能 | +| class/m26 | M26/MC20 设备针对 AT 组件的移植目录,实现 AT Socket 功能 | +| class/ec20 | EC20 设备针对 AT 组件的移植目录,实现 AT Socket 功能 | ### 1.2 许可证 ### @@ -29,42 +32,123 @@ at_device package 遵循 LGPLv2.1 许可,详见 `LICENSE` 文件。 ## 2. 获取方式 ## -AT device 软件包是对 AT 组件库和 AT socket 功能的移植,需开启 AT 组件库和 AT socket 功能来获取 AT device 软件包。 +AT device 软件包是对 AT 组件和 AT socket 功能的移植,需开启 AT 组件库和 AT socket 功能来获取 AT device 软件包。 -先要开启 AT 组件库和 AT socket 功能, 具体路径如下所示: +**版本号说明** - RT-Thread Components ---> - Network stack ---> - Socket abstraction layer ---> - protocol stack implement ---> - [ ] Support lwIP stack - [*] Support AT Commands stack +AT device 软件包目前已经发布多个版本,各个版本之间选项配置方式和其对应的系统版本有所不同,下面主要列出当前可使用的软件包版本信息: -开启 AT socket 功能之后,默认开启 AT device 软件包, 具体路径如下所示: +- **V1.2.0**:RT-Thread 版本小于 V3.1.3,AT 组件版本等于 V1.0.0; +- **V1.3.0**:RT-Thread 版本小于 V3.1.3,AT 组件版本等于 V1.1.0; +- **V1.4.0**:RT-Thread 版本小于 V3.1.3或等于 V4.0.0, AT 组件版本等于 V1.2.0; +- **V1.5.0**: RT-Thread 版本小于 V3.1.3 或等于 V4.0.0, AT 组件版本等于 V1.2.0; +- **V1.6.0**: RT-Thread 版本等于 V3.1.3 或等于 V4.0.1, AT 组件版本等于 V1.2.0; +- **laster**: RT-Thread 版本小于大于 V4.0.1 或者 大于 3.1.3, AT 组件版本大于 V1.2.0; + +> 上述 版本判断在 menuconfig 中自动完成,at_device 软件包选择版本时会根据当前系统环境给出最佳版本支持,这里版本介绍作为运行环境参考。 + +针对不同的版本号,在 ENV 中的选项配置也有所不同,主要分为如下几部分: + +**V1.X.X 版本配置选项介绍** + +开启 AT device 软件包,该版本只支持**同时开启一种 AT 设备**, 配置选项具体如下所示: RT-Thread online packages ---> IoT - internet of things ---> -*- AT DEVICE: RT-Thread AT component porting or samples for different device [ ] Enable at device init by thread AT socket device modules (Not selected, please select) ---> - Version (latest) ---> + Version (V1.6.0) ---> -- `Enable at device init by thread`: 配置开启设备网络初始化是否通过创建线程完成; -- `AT socket device modules`: AT 设备选择,目前支持 RW007、ESP8266、M26/MC20、EC20、SIM800C、SIM76XX 等设备; -- `Version`: 下载软件包版本; - - **V1.2.0**:RT-Thread 版本小于 V3.1.3,AT 组件版本等于 V1.0.0; - - **V1.3.0**:RT-Thread 版本小于 V3.1.3,AT 组件版本等于 V1.1.0; - - **V1.4.0**:RT-Thread 版本小于 V3.1.3 或等于 V4.0.0, AT 组件版本等于 V1.2.0; - - **V1.5.0**:RT-Thread 版本小于 V3.1.3 或等于 V4.0.0, AT 组件版本等于 V1.2.0; - - **V1.6.0**:RT-Thread 版本等于 V3.1.3 或等于 V4.0.1, AT 组件版本等于 V1.2.0; - - **laster**:RT-Thread 版本小于大于 V4.0.1 或者 大于 3.1.3, AT 组件版本大于 V1.2.0; +- **Enable at device init by thread**: 配置开启设备网络初始化是否通过创建线程完成; +- **AT socket device modules**: AT 设备选择,目前支持 RW007、ESP8266、M26/MC20、EC20、SIM800C、SIM76XX 等设备; +- **Version**: 下载软件包版本; ->上述 版本判断在 menuconfig 中自动完成,at_device 软件包选择版本时会根据当前系统环境给出最佳版本支持,这里版本介绍作为运行环境参考。 +**V2.X.X (laster) 版本配置选项介绍** + +开启 AT device 软件包,该版本支持**同时开启多种 AT 设备**配置选项具体如下所示: + +```c +RT-Thread online packages ---> + IoT - internet of things ---> + -*- AT DEVICE: RT-Thread AT component porting or samples for different device + [*] Quectel M26/MC20 ---> + [*] Enable initialize by thread + [*] Enable sample + (-1) Power pin + (-1) Power status pin + (uart3) AT client device name + (512) The maximum length of receive line buffer + [ ] Quectel EC20 ---> + [*] Espressif ESP8266 ---> + [*] Enable initialize by thread + [*] Enable sample + (realthread) WIFI ssid + (12345678) WIFI password + (uart2) AT client device name + (512) The maximum length of receive line buffer + [ ] Realthread RW007 ---> + [ ] SIMCom SIM800C ---> + [ ] SIMCom SIM76XX ---> + Version (latest) ---> +``` + +- **Quectel M26/MC20**:开启 M20/MC20(2G 模块)设备支持; + - **Enable initialize by thread**:开启使用线程进行设备初始化功能(非阻塞模式初始化); + - **Enable sample**:开启示例代码,该示例代码中有对示例设备的注册; + - **Power pin**:配置该示例设备上电引脚; + - **Power status pin**:配置该示例设备上电状态引脚; + - **AT client device name**:配置该示例设备使用的串口设备名称; + - **The maximum length of receive line buffer**:配置该示例设备最大一行接收的数据长度; +- **Quectel EC20 **:开启 EC20(4G 模块)设备支持; +- **Espressif ESP8266**:开启 ESP8266 (WIFI 模块)设备支持; + - **Enable initialize by thread**:开启使用线程进行设备初始化功能(非阻塞模式初始化); + - **Enable sample**:开启示例代码,该示例代码中有对示例设备的注册; + - **WIFI ssid**:配置该示例设备连接的 WIFI 用户名; + - **WIFI password**:配置该示例设备连接的 WIFI 密码; + - **AT client device name**:配置该示例设备使用的串口设备名称; + - **The maximum length of receive line buffer**:配置该示例设备最大一行接收的数据长度; +- **Realthread RW007**:开启 RW007 (WIFI 模块)设备支持; +- **SIMCom SIM800C**:开启 SIM800C (2G 模块)设备支持; +- **SIMCom SIM76XX**:开启 SIM76XX (4G 模块)设备支持; +- **Version** 下载软件包版本; + +上面配置选项以 2G 模块和 WIFI 模块选项为例,介绍了`V2.X.X` 版本 AT device 软件包配置方式,如下几点值得注意: + +- `V2.X.X` 版本支持同时开启多个 AT 设备,可以在 FinSH 中通过 `ifocnfig` 命令查看开启的设备信息; +- `V2.X.X` 版本设备需要注册之后才可使用,目前在 samples 目录文件中完成设备注册,用户也可以在应用层自定义设备注册。 +- `Power pin` 和 `Power status pin` 等引脚选项根据具体设备硬件连接情况配置,如果不使用硬件上电功能,可以配置为 `-1`; +- 一个 AT 设备对应一个串口名称,及每个设备配置的`AT client device name` 应该都不相同。 + +**AT 组件相关配置选项介绍** + +选中 AT device 软件包并开启相关设备支持之后,会默认选中 AT 组件的客户端功能,下面是 AT 组件配置选项, + +```c +RT-Thread Components ---> + Network ---> + AT commands ---> + [ ] Enable debug log output + [ ] Enable AT commands server + -*- Enable AT commands client + (1) The maximum number of supported clients + -*- Enable BSD Socket API support by AT commnads + [*] Enable CLI(Command-Line Interface) for AT commands + [ ] Enable print RAW format AT command communication data + (128) The maximum lenght of AT Commonds buffe +``` + +其中和 AT device 软件包相关的配置选项: + +- **The maximum number of supported clients**:最大支持的同时开启的 AT 客户端数量(AT device 软件包中选中多个设备需要配置该选项为对应数值); +- **Enable BSD Socket API support by AT commnads**:开启 AT Socket 功能支持,选中 AT device 软件包默认选中该选项; +- **The maximum lenght of AT Commonds buffe**:最大支持的发送命令数据的长度。 ## 3. 注意事项 ## - AT device 软件包适配的模块暂时不支持作为 TCP Server 完成服务器相关操作(如 accept 等); - AT device 软件包默认设备类型为未选择,使用时需要指定使用设备型号; +- `laster` 版本支持多个选中多个 AT 设备接入实现 AT Socket 功能,`V1.X.X` 版本只支持单个 AT 设备接入。 - AT device 软件包目前多个版本主要用于适配 AT 组件和系统的改动,推荐使用最新版本 RT-Thread 系统,并在 menuconfig 选项中选择 `latest` 版本; ## 5. 联系方式 diff --git a/SConscript b/SConscript index 4d20382..758ed9f 100644 --- a/SConscript +++ b/SConscript @@ -1,30 +1,63 @@ from building import * cwd = GetCurrentDir() +path = [cwd + '/inc'] +src = Glob('src/*.c') -src = [] +# M26/MC20 +if GetDepend(['AT_DEVICE_USING_M26']): + path += [cwd + '/class/m26'] + src += Glob('class/m26/at_device_m26.c') + if GetDepend(['AT_USING_SOCKET']): + src += Glob('class/m26/at_socket_m26.c') + if GetDepend(['AT_DEVICE_M26_SAMPLE']): + src += Glob('samples/at_sample_m26.c') -if GetDepend(['AT_DEVICE_M26']): - src = Glob('at_socket_m26.c') +# EC20 +if GetDepend(['AT_DEVICE_USING_EC20']): + path += [cwd + '/class/ec20'] + src += Glob('class/ec20/at_device_ec20.c') + if GetDepend(['AT_USING_SOCKET']): + src += Glob('class/ec20/at_socket_ec20.c') + if GetDepend(['AT_DEVICE_EC20_SAMPLE']): + src += Glob('samples/at_sample_ec20.c') -if GetDepend(['AT_DEVICE_EC20']): - src = Glob('at_socket_ec20.c') +# ESP8266 +if GetDepend(['AT_DEVICE_USING_ESP8266']): + path += [cwd + '/class/esp8266'] + src += Glob('class/esp8266/at_device_esp8266.c') + if GetDepend(['AT_USING_SOCKET']): + src += Glob('class/esp8266/at_socket_esp8266.c') + if GetDepend(['AT_DEVICE_ESP8266_SAMPLE']): + src += Glob('samples/at_sample_esp8266.c') + +# RW007 +if GetDepend(['AT_DEVICE_USING_RW007']): + path += [cwd + '/class/rw007'] + src += Glob('class/rw007/at_device_rw007.c') + if GetDepend(['AT_USING_SOCKET']): + src += Glob('class/rw007/at_socket_rw007.c') + if GetDepend(['AT_DEVICE_RW007_SAMPLE']): + src += Glob('samples/at_sample_rw007.c') -if GetDepend(['AT_DEVICE_ESP8266']): - src = Glob('at_socket_esp8266.c') +# SIM800C +if GetDepend(['AT_DEVICE_USING_SIM800C']): + path += [cwd + '/class/sim800c'] + src += Glob('class/sim800c/at_device_sim800c.c') + if GetDepend(['AT_USING_SOCKET']): + src += Glob('class/sim800c/at_socket_sim800c.c') + if GetDepend(['AT_DEVICE_SIM800C_SAMPLE']): + src += Glob('samples/at_sample_sim800c.c') -if GetDepend(['AT_DEVICE_RW007']): - src = Glob('at_socket_rw007.c') +# SIM76XX +if GetDepend(['AT_DEVICE_USING_SIM76XX']): + path += [cwd + '/class/sim76xx'] + src += Glob('class/sim76xx/at_device_sim76xx.c') + if GetDepend(['AT_USING_SOCKET']): + src += Glob('class/sim76xx/at_socket_sim76xx.c') + if GetDepend(['AT_DEVICE_SIM76XX_SAMPLE']): + src += Glob('samples/at_sample_sim76xx.c') -if GetDepend(['AT_DEVICE_SIM800C']): - src += Glob('at_socket_sim800c.c') - -if GetDepend(['AT_DEVICE_SIM76XX']): - src += Glob('at_socket_sim76xx.c') - -if GetDepend(['AT_DEVICE_NOT_SELECTED']): - src = Glob('*.c') - -group = DefineGroup('at_device', src, depend = ['PKG_USING_AT_DEVICE','AT_USING_SOCKET'], CPPPATH = [cwd]) +group = DefineGroup('at_device', src, depend = ['PKG_USING_AT_DEVICE'], CPPPATH = path) Return('group') diff --git a/at_socket_ec20.c b/at_socket_ec20.c deleted file mode 100644 index d72340d..0000000 --- a/at_socket_ec20.c +++ /dev/null @@ -1,1715 +0,0 @@ -/* - * File : at_socket_ec20.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-12 chenyong first version - * 2018-08-12 Marcus port to ec20 - */ - -#include -#include - -#include -#include - -#include -#include - -#if !defined(RT_USING_NETDEV) -#error "This RT-Thread version is older, please check and updata laster RT-Thread!" -#else -#include -#include -#endif /* RT_USING_NETDEV */ - -#if !defined(AT_SW_VERSION_NUM) || AT_SW_VERSION_NUM < 0x10200 -#error "This AT Client version is older, please check and update latest AT Client!" -#endif - -#define LOG_TAG "at.ec20" -#include - -#ifdef AT_DEVICE_EC20 - -#define EC20_NETDEV_NAME "ec20" - -#define EC20_MODULE_SEND_MAX_SIZE 1460 -#define EC20_WAIT_CONNECT_TIME 5000 -#define EC20_THREAD_STACK_SIZE 1024 -#define EC20_THREAD_PRIORITY (RT_THREAD_PRIORITY_MAX/2) - -/* set real event by current socket and current state */ -#define SET_EVENT(socket, event) (((socket + 1) << 16) | (event)) - -/* AT socket event type */ -#define EC20_EVENT_CONN_OK (1L << 0) -#define EC20_EVENT_SEND_OK (1L << 1) -#define EC20_EVENT_RECV_OK (1L << 2) -#define EC20_EVNET_CLOSE_OK (1L << 3) -#define EC20_EVENT_CONN_FAIL (1L << 4) -#define EC20_EVENT_SEND_FAIL (1L << 5) -#define EC20_EVENT_DOMAIN_OK (1L << 6) - -/* AT+QICSGP command default*/ -char *QICSGP_CHINA_MOBILE = "AT+QICSGP=1,1,\"CMNET\",\"\",\"\",0"; -char *QICSGP_CHINA_UNICOM = "AT+QICSGP=1,1,\"UNINET\",\"\",\"\",0"; -char *QICSGP_CHINA_TELECOM = "AT+QICSGP=1,1,\"CTNET\",\"\",\"\",0"; - -static int cur_socket; -static char recv_ip[16] = { 0 }; -static rt_event_t at_socket_event; -static rt_mutex_t at_event_lock; -static at_evt_cb_t at_evt_cb_set[] = { - [AT_SOCKET_EVT_RECV] = NULL, - [AT_SOCKET_EVT_CLOSED] = NULL, -}; - -static void at_cme_errcode_parse(int result) -{ - switch(result) - { - case 0 : LOG_E("%d : Phone failure", result); break; - case 1 : LOG_E("%d : No connection to phone", result); break; - case 2 : LOG_E("%d : Phone-adaptor link reserved", result); break; - case 3 : LOG_E("%d : Operation not allowed", result); break; - case 4 : LOG_E("%d : Operation not supported", result); break; - case 5 : LOG_E("%d : PH-SIM PIN required", result); break; - case 6 : LOG_E("%d : PH-FSIM PIN required", result); break; - case 7 : LOG_E("%d : PH-FSIM PUK required", result); break; - case 10 : LOG_E("%d : SIM not inserted", result); break; - case 11 : LOG_E("%d : SIM PIN required", result); break; - case 12 : LOG_E("%d : SIM PUK required", result); break; - case 13 : LOG_E("%d : SIM failure", result); break; - case 14 : LOG_E("%d : SIM busy", result); break; - case 15 : LOG_E("%d : SIM wrong", result); break; - case 16 : LOG_E("%d : Incorrect password", result); break; - case 17 : LOG_E("%d : SIM PIN2 required", result); break; - case 18 : LOG_E("%d : SIM PUK2 required", result); break; - case 20 : LOG_E("%d : Memory full", result); break; - case 21 : LOG_E("%d : Invalid index", result); break; - case 22 : LOG_E("%d : Not found", result); break; - case 23 : LOG_E("%d : Memory failure", result); break; - case 24 : LOG_E("%d : Text string too long", result); break; - case 25 : LOG_E("%d : Invalid characters in text string", result); break; - case 26 : LOG_E("%d : Dial string too long", result); break; - case 27 : LOG_E("%d : Invalid characters in dial string", result); break; - case 30 : LOG_E("%d : No network service", result); break; - case 31 : LOG_E("%d : Network timeout", result); break; - case 32 : LOG_E("%d : Network not allowed - emergency calls only", result); break; - case 40 : LOG_E("%d : Network personalization PIN required", result); break; - case 41 : LOG_E("%d : Network personalization PUK required", result); break; - case 42 : LOG_E("%d : Network subset personalization PIN required", result); break; - case 43 : LOG_E("%d : Network subset personalization PUK required", result); break; - case 44 : LOG_E("%d : Service provider personalization PIN required", result); break; - case 45 : LOG_E("%d : Service provider personalization PUK required", result); break; - case 46 : LOG_E("%d : Corporate personalization PIN required", result); break; - case 47 : LOG_E("%d : Corporate personalization PUK required", result); break; - case 901 : LOG_E("%d : Audio unknown error", result); break; - case 902 : LOG_E("%d : Audio invalid parameters", result); break; - case 903 : LOG_E("%d : Audio operation not supported", result); break; - case 904 : LOG_E("%d : Audio device busy", result); break; - default : LOG_E("%d : Unknown err code", result); break; - } -} - -static void at_cms_errcode_parse(int result) -{ - switch(result) - { - case 300 : LOG_E("%d : ME failure", result); break; - case 301 : LOG_E("%d : SMS ME reserved", result); break; - case 302 : LOG_E("%d : Operation not allowed", result); break; - case 303 : LOG_E("%d : Operation not supported", result); break; - case 304 : LOG_E("%d : Invalid PDU mode", result); break; - case 305 : LOG_E("%d : Invalid text mode", result); break; - case 310 : LOG_E("%d : SIM not inserted", result); break; - case 311 : LOG_E("%d : SIM pin necessary", result); break; - case 312 : LOG_E("%d : PH SIM pin necessary", result); break; - case 313 : LOG_E("%d : SIM failure", result); break; - case 314 : LOG_E("%d : SIM busy", result); break; - case 315 : LOG_E("%d : SIM wrong", result); break; - case 316 : LOG_E("%d : SIM PUK required", result); break; - case 317 : LOG_E("%d : SIM PIN2 required", result); break; - case 318 : LOG_E("%d : SIM PUK2 required", result); break; - case 320 : LOG_E("%d : Memory failure", result); break; - case 321 : LOG_E("%d : Invalid memory index", result); break; - case 322 : LOG_E("%d : Memory full", result); break; - case 330 : LOG_E("%d : SMSC address unknown", result); break; - case 331 : LOG_E("%d : No network", result); break; - case 332 : LOG_E("%d : Network timeout", result); break; - case 500 : LOG_E("%d : Unknown", result); break; - case 512 : LOG_E("%d : SIM not ready", result); break; - case 513 : LOG_E("%d : Message length exceeds", result); break; - case 514 : LOG_E("%d : Invalid request parameters", result); break; - case 515 : LOG_E("%d : ME storage failure", result); break; - case 517 : LOG_E("%d : Invalid service mode", result); break; - case 528 : LOG_E("%d : More message to send state error", result); break; - case 529 : LOG_E("%d : MO SMS is not allow", result); break; - case 530 : LOG_E("%d : GPRS is suspended", result); break; - case 531 : LOG_E("%d : ME storage full", result); break; - default : LOG_E("%d : Unknown err code", result); break; - } -} - -static void at_mms_errcode_parse(int result)//MMS -{ - switch(result) - { - case 751 : LOG_E("%d : Unknown error", result); break; - case 752 : LOG_E("%d : URL length error", result); break; - case 753 : LOG_E("%d : URL error", result); break; - case 754 : LOG_E("%d : Invalid proxy type", result); break; - case 755 : LOG_E("%d : Proxy address error", result); break; - case 756 : LOG_E("%d : Invalid parameter", result); break; - case 757 : LOG_E("%d : Recipient address full", result); break; - case 758 : LOG_E("%d : CC recipient address full", result); break; - case 759 : LOG_E("%d : BCC recipient address full", result); break; - case 760 : LOG_E("%d : Attachments full", result); break; - case 761 : LOG_E("%d : File error", result); break; - case 762 : LOG_E("%d : No recipient", result); break; - case 763 : LOG_E("%d : File not found", result); break; - case 764 : LOG_E("%d : MMS busy", result); break; - case 765 : LOG_E("%d : Server response failed", result); break; - case 766 : LOG_E("%d : Error response of HTTP(S) post", result); break; - case 767 : LOG_E("%d : Invalid report of HTTP(S) post", result); break; - case 768 : LOG_E("%d : PDP activation failed", result); break; - case 769 : LOG_E("%d : PDP deactivated", result); break; - case 770 : LOG_E("%d : Socket creation failed", result); break; - case 771 : LOG_E("%d : Socket connection failed", result); break; - case 772 : LOG_E("%d : Socket read failed", result); break; - case 773 : LOG_E("%d : Socket write failed", result); break; - case 774 : LOG_E("%d : Socket closed", result); break; - case 775 : LOG_E("%d : Timeout", result); break; - case 776 : LOG_E("%d : Encode data error", result); break; - case 777 : LOG_E("%d : HTTP(S) decode data error", result); break; - default : LOG_E("%d : Unknown err code", result); break; - } -} - -static void at_tcp_ip_errcode_parse(int result)//TCP/IP_QIGETERROR -{ - switch(result) - { - case 0 : LOG_D("%d : Operation successful", result); break; - case 550 : LOG_E("%d : Unknown error", result); break; - case 551 : LOG_E("%d : Operation blocked", result); break; - case 552 : LOG_E("%d : Invalid parameters", result); break; - case 553 : LOG_E("%d : Memory not enough", result); break; - case 554 : LOG_E("%d : Create socket failed", result); break; - case 555 : LOG_E("%d : Operation not supported", result); break; - case 556 : LOG_E("%d : Socket bind failed", result); break; - case 557 : LOG_E("%d : Socket listen failed", result); break; - case 558 : LOG_E("%d : Socket write failed", result); break; - case 559 : LOG_E("%d : Socket read failed", result); break; - case 560 : LOG_E("%d : Socket accept failed", result); break; - case 561 : LOG_E("%d : Open PDP context failed", result); break; - case 562 : LOG_E("%d : Close PDP context failed", result); break; - case 563 : LOG_W("%d : Socket identity has been used", result); break; - case 564 : LOG_E("%d : DNS busy", result); break; - case 565 : LOG_E("%d : DNS parse failed", result); break; - case 566 : LOG_E("%d : Socket connect failed", result); break; - case 567 : LOG_W("%d : Socket has been closed", result); break; - case 568 : LOG_E("%d : Operation busy", result); break; - case 569 : LOG_E("%d : Operation timeout", result); break; - case 570 : LOG_E("%d : PDP context broken down", result); break; - case 571 : LOG_E("%d : Cancel send", result); break; - case 572 : LOG_E("%d : Operation not allowed", result); break; - case 573 : LOG_E("%d : APN not configured", result); break; - case 574 : LOG_E("%d : Port busy", result); break; - default : LOG_E("%d : Unknown err code", result); break; - } -} - -static void at_http_errcode_parse(int result)//HTTP -{ - switch(result) - { - case 0 : LOG_D("%d : Operation successful", result); break; - case 701 : LOG_E("%d : HTTP(S) unknown error", result); break; - case 702 : LOG_E("%d : HTTP(S) timeout", result); break; - case 703 : LOG_E("%d : HTTP(S) busy", result); break; - case 704 : LOG_E("%d : HTTP(S) UART busy", result); break; - case 705 : LOG_E("%d : HTTP(S) no GET/POST requests", result); break; - case 706 : LOG_E("%d : HTTP(S) network busy", result); break; - case 707 : LOG_E("%d : HTTP(S) network open failed", result); break; - case 708 : LOG_E("%d : HTTP(S) network no configuration", result); break; - case 709 : LOG_E("%d : HTTP(S) network deactivated", result); break; - case 710 : LOG_E("%d : HTTP(S) network error", result); break; - case 711 : LOG_E("%d : HTTP(S) URL error", result); break; - case 712 : LOG_E("%d : HTTP(S) empty URL", result); break; - case 713 : LOG_E("%d : HTTP(S) IP address error", result); break; - case 714 : LOG_E("%d : HTTP(S) DNS error", result); break; - case 715 : LOG_E("%d : HTTP(S) socket create error", result); break; - case 716 : LOG_E("%d : HTTP(S) socket connect error", result); break; - case 717 : LOG_E("%d : HTTP(S) socket read error", result); break; - case 718 : LOG_E("%d : HTTP(S) socket write error", result); break; - case 719 : LOG_E("%d : HTTP(S) socket closed", result); break; - case 720 : LOG_E("%d : HTTP(S) data encode error", result); break; - case 721 : LOG_E("%d : HTTP(S) data decode error", result); break; - case 722 : LOG_E("%d : HTTP(S) read timeout", result); break; - case 723 : LOG_E("%d : HTTP(S) response failed", result); break; - case 724 : LOG_E("%d : Incoming call busy", result); break; - case 725 : LOG_E("%d : Voice call busy", result); break; - case 726 : LOG_E("%d : Input timeout", result); break; - case 727 : LOG_E("%d : Wait data timeout", result); break; - case 728 : LOG_E("%d : Wait HTTP(S) response timeout", result); break; - case 729 : LOG_E("%d : Memory allocation failed", result); break; - case 730 : LOG_E("%d : Invalid parameter", result); break; - default : LOG_E("%d : Unknown err code", result); break; - } -} - -static void at_http_rsponsecode_parse(int result)//HTTP -{ - switch(result) - { - case 200 : LOG_D("%d : OK", result); break; - case 400 : LOG_E("%d : Bad request", result); break; - case 403 : LOG_E("%d : Forbidden", result); break; - case 404 : LOG_E("%d : Not found", result); break; - case 409 : LOG_E("%d : Conflict", result); break; - case 411 : LOG_E("%d : Length required", result); break; - case 500 : LOG_E("%d : Internal server error", result); break; - case 502 : LOG_E("%d : Bad gate way", result); break; - default : LOG_E("%d : Unknown err code", result); break; - } -} - -static void at_ftp_errcode_parse(int result)//FTP -{ - switch(result) - { - case 0 : LOG_D("%d : Operation successful", result); break; - case 601 : LOG_E("%d : Unknown error", result); break; - case 602 : LOG_E("%d : FTP(S) server blocked", result); break; - case 603 : LOG_E("%d : FTP(S) server busy", result); break; - case 604 : LOG_E("%d : DNS parse failed", result); break; - case 605 : LOG_E("%d : Network error", result); break; - case 606 : LOG_E("%d : Control connection closed.", result); break; - case 607 : LOG_E("%d : Data connection closed", result); break; - case 608 : LOG_E("%d : Socket closed by peer", result); break; - case 609 : LOG_E("%d : Timeout error", result); break; - case 610 : LOG_E("%d : Invalid parameter", result); break; - case 611 : LOG_E("%d : Failed to open file", result); break; - case 612 : LOG_E("%d : File position invalid", result); break; - case 613 : LOG_E("%d : File error", result); break; - case 614 : LOG_E("%d : Service not available, closing control connection", result); break; - case 615 : LOG_E("%d : Open data connection failed", result); break; - case 616 : LOG_E("%d : Connection closed; transfer aborted", result); break; - case 617 : LOG_E("%d : Requested file action not taken", result); break; - case 618 : LOG_E("%d : Requested action aborted: local error in processing", result); break; - case 619 : LOG_E("%d : Requested action not taken: insufficient system storage", result); break; - case 620 : LOG_E("%d : Syntax error, command unrecognized", result); break; - case 621 : LOG_E("%d : Syntax error in parameters or arguments", result); break; - case 622 : LOG_E("%d : Command not implemented", result); break; - case 623 : LOG_E("%d : Bad sequence of commands", result); break; - case 624 : LOG_E("%d : Command parameter not implemented", result); break; - case 625 : LOG_E("%d : Not logged in", result); break; - case 626 : LOG_E("%d : Need account for storing files", result); break; - case 627 : LOG_E("%d : Requested action not taken", result); break; - case 628 : LOG_E("%d : Requested action aborted: page type unknown", result); break; - case 629 : LOG_E("%d : Requested file action aborted", result); break; - case 630 : LOG_E("%d : Requested file name invalid", result); break; - case 631 : LOG_E("%d : SSL authentication failed", result); break; - default : LOG_E("%d : Unknown err code", result); break; - } -} - -static void at_ftp_protocol_errcode_parse(int result)//FTP_Protocol -{ - switch(result) - { - case 421 : LOG_E("%d : Service not available, closing control connection", result); break; - case 425 : LOG_E("%d : Open data connection failed", result); break; - case 426 : LOG_E("%d : Connection closed; transfer aborted", result); break; - case 450 : LOG_E("%d : Requested file action not taken", result); break; - case 451 : LOG_E("%d : Requested action aborted: local error in processing", result); break; - case 452 : LOG_E("%d : Requested action not taken: insufficient system storage", result); break; - case 500 : LOG_E("%d : Syntax error, command unrecognized", result); break; - case 501 : LOG_E("%d : Syntax error in parameters or arguments", result); break; - case 502 : LOG_E("%d : Command not implemented", result); break; - case 503 : LOG_E("%d : Bad sequence of commands", result); break; - case 504 : LOG_E("%d : Command parameter not implemented", result); break; - case 530 : LOG_E("%d : Not logged in", result); break; - case 532 : LOG_E("%d : Need account for storing files", result); break; - case 550 : LOG_E("%d : Requested action not taken: file unavailable", result); break; - case 551 : LOG_E("%d : Requested action aborted: page type unknown", result); break; - case 552 : LOG_E("%d : Requested file action aborted: exceeded storage allocation", result); break; - case 553 : LOG_E("%d : Requested action not taken: file name not allowed", result); break; - default : LOG_E("%d : Unknown err code", result); break; - } -} - -static void at_smtp_errcode_parse(int result)//Email -{ - switch(result) - { - case 651 : LOG_E("%d : Unknown error", result); break; - case 652 : LOG_E("%d : The SMTP server is busy, such as uploading the body or sending an email.", result); break; - case 653 : LOG_E("%d : Failed to get IP address according to the domain name.", result); break; - case 654 : LOG_E("%d : Network error, such as failed to activate GPRS/CSD context, failed to establish the TCP connection with the SMTP server or failed to send an email to the SMTP server, etc.", result); break; - case 655 : LOG_E("%d : Unsupported authentication type", result); break; - case 656 : LOG_E("%d : The connection for the SMTP server is closed by peer.", result); break; - case 657 : LOG_E("%d : GPRS/CSD context is deactivated.", result); break; - case 658 : LOG_E("%d : Timeout", result); break; - case 659 : LOG_E("%d : No recipient for the SMTP server", result); break; - case 660 : LOG_E("%d : Failed to send an email", result); break; - case 661 : LOG_E("%d : Failed to open a file", result); break; - case 662 : LOG_E("%d : No enough memory for the attachment", result); break; - case 663 : LOG_E("%d : Failed to save the attachment", result); break; - case 664 : LOG_E("%d : The input parameter is wrong", result); break; - case 665 : LOG_E("%d : SSL authentication failed", result); break; - case 666 : LOG_E("%d : Service not available, closing transmission channel", result); break; - case 667 : LOG_E("%d : Requested mail action not taken: mailbox unavailable", result); break; - case 668 : LOG_E("%d : Requested action aborted: local error in processing", result); break; - case 669 : LOG_E("%d : Requested action not taken: insufficient system storage", result); break; - case 670 : LOG_E("%d : Syntax error, command unrecognized", result); break; - case 671 : LOG_E("%d : Syntax error in parameters or arguments", result); break; - case 672 : LOG_E("%d : Command not implemented", result); break; - case 673 : LOG_E("%d : Bad sequence of commands", result); break; - case 674 : LOG_E("%d : Command parameter not implemented", result); break; - case 675 : LOG_E("%d : does not accept mail (see RFC1846)", result); break; - case 676 : LOG_E("%d : Access denied", result); break; - case 677 : LOG_E("%d : Authentication failed", result); break; - case 678 : LOG_E("%d : Requested action not taken: mailbox unavailable", result); break; - case 679 : LOG_E("%d : User not local; please try ", result); break; - case 680 : LOG_E("%d : Requested mail action aborted: exceeded storage allocation", result); break; - case 681 : LOG_E("%d : Requested action not taken: mailbox name not allowed", result); break; - case 682 : LOG_E("%d : Transaction failed", result); break; - default : LOG_E("%d : Unknown err code", result); break; - } -} - -static void at_smtp_protocol_errcode_parse(int result)//Email_Protocol -{ - switch(result) - { - case 421 : LOG_E("%d : Service not available, closing transmission channel", result); break; - case 450 : LOG_E("%d : Requested mail action not taken: mailbox unavailable", result); break; - case 451 : LOG_E("%d : Requested action aborted: local error in processing", result); break; - case 452 : LOG_E("%d : Requested action not taken: insufficient system storage", result); break; - case 500 : LOG_E("%d : Syntax error, command unrecognized", result); break; - case 501 : LOG_E("%d : Syntax error in parameters or arguments", result); break; - case 502 : LOG_E("%d : Command not implemented", result); break; - case 503 : LOG_E("%d : Bad sequence of commands", result); break; - case 504 : LOG_E("%d : Command parameter not implemented", result); break; - case 521 : LOG_E("%d : does not accept mail (see RFC1846)", result); break; - case 530 : LOG_E("%d : Access denied", result); break; - case 535 : LOG_E("%d : Authentication failed", result); break; - case 550 : LOG_E("%d : Requested action not taken: mailbox unavailable", result); break; - case 551 : LOG_E("%d : User not local; please try ", result); break; - case 552 : LOG_E("%d : Requested mail action aborted: exceeded storage allocation", result); break; - case 553 : LOG_E("%d : Requested action not taken: mailbox name not allowed", result); break; - case 554 : LOG_E("%d : Transaction failed", result); break; - default : LOG_E("%d : Unknown err code", result); break; - } -} - -static int at_socket_event_send(uint32_t event) -{ - return (int) rt_event_send(at_socket_event, event); -} - -static int at_socket_event_recv(uint32_t event, uint32_t timeout, rt_uint8_t option) -{ - int result = 0; - rt_uint32_t recved; - - result = rt_event_recv(at_socket_event, event, option | RT_EVENT_FLAG_CLEAR, timeout, &recved); - if (result != RT_EOK) - { - return -RT_ETIMEOUT; - } - - return recved; -} - -/** - * close socket by AT commands. - * - * @param current socket - * - * @return 0: close socket success - * -1: send AT commands error - * -2: wait socket event timeout - * -5: no memory - */ -static int ec20_socket_close(int socket) -{ - int result = 0; - at_response_t resp = RT_NULL; - - resp = at_create_resp(128, 0, RT_TICK_PER_SECOND); - if (!resp) - { - LOG_E("No memory for response structure!"); - return -RT_ENOMEM; - } - - rt_mutex_take(at_event_lock, RT_WAITING_FOREVER); - - /* default connection timeout is 10 seconds, but it set to 1 seconds is convenient to use.*/ - result = at_exec_cmd(resp, "AT+QICLOSE=%d,1", socket); - if (result < 0) - { - return result; - } - - rt_mutex_release(at_event_lock); - - if (resp) - { - at_delete_resp(resp); - } - - return result; -} - -/** - * create TCP/UDP client or server connect by AT commands. - * - * @param socket current socket - * @param ip server or client IP address - * @param port server or client port - * @param type connect socket type(tcp, udp) - * @param is_client connection is client - * - * @return 0: connect success - * -1: connect failed, send commands error or type error - * -2: wait socket event timeout - * -5: no memory - */ -static int ec20_socket_connect(int socket, char *ip, int32_t port, enum at_socket_type type, rt_bool_t is_client) -{ - int result = 0, event_result = 0; - rt_bool_t retryed = RT_FALSE; - at_response_t resp = RT_NULL; - - RT_ASSERT(ip); - RT_ASSERT(port >= 0); - - resp = at_create_resp(128, 0, 5 * RT_TICK_PER_SECOND); - if (!resp) - { - LOG_E("No memory for response structure!"); - return -RT_ENOMEM; - } - - /* lock AT socket connect */ - rt_mutex_take(at_event_lock, RT_WAITING_FOREVER); - -__retry: - - /* Clear socket connect event */ - at_socket_event_recv(SET_EVENT(socket, EC20_EVENT_CONN_OK | EC20_EVENT_CONN_FAIL), 0, RT_EVENT_FLAG_OR); - - if (is_client) - { - switch (type) - { - case AT_SOCKET_TCP: - /* send AT commands(AT+QIOPEN=,,"","/",,,) to connect TCP server */ - /* contextID = 1 : use same contextID as AT+QICSGP & AT+QIACT */ - /* local_port=0 : local port assigned automatically */ - /* access_mode = 1 : Direct push mode */ - if (at_exec_cmd(resp, "AT+QIOPEN=1,%d,\"TCP\",\"%s\",%d,0,1", socket, ip, port) < 0) - { - result = -RT_ERROR; - goto __exit; - } - break; - - case AT_SOCKET_UDP: - if (at_exec_cmd(resp, "AT+QIOPEN=1,%d,\"UDP\",\"%s\",%d,0,1", socket, ip, port) < 0) - { - result = -RT_ERROR; - goto __exit; - } - break; - - default: - LOG_E("Not supported connect type : %d.", type); - return -RT_ERROR; - } - } - - /* waiting result event from AT URC, the device default connection timeout is 75 seconds, but it set to 10 seconds is convenient to use.*/ - if (at_socket_event_recv(SET_EVENT(socket, 0), 10 * RT_TICK_PER_SECOND, RT_EVENT_FLAG_OR) < 0) - { - LOG_E("socket (%d) connect failed, wait connect result timeout.", socket); - result = -RT_ETIMEOUT; - goto __exit; - } - /* waiting OK or failed result */ - if ((event_result = at_socket_event_recv(EC20_EVENT_CONN_OK | EC20_EVENT_CONN_FAIL, 1 * RT_TICK_PER_SECOND, - RT_EVENT_FLAG_OR)) < 0) - { - LOG_E("socket (%d) connect failed, wait connect OK|FAIL timeout.", socket); - result = -RT_ETIMEOUT; - goto __exit; - } - /* check result */ - if (event_result & EC20_EVENT_CONN_FAIL) - { - if (!retryed) - { - LOG_W("socket (%d) connect failed, maybe the socket was not be closed at the last time and now will retry.", socket); - /* default connection timeout is 10 seconds, but it set to 1 seconds is convenient to use.*/ - if (ec20_socket_close < 0) - { - result = -RT_ERROR; - goto __exit; - } - retryed = RT_TRUE; - goto __retry; - } - LOG_E("socket (%d) connect failed, failed to establish a connection.", socket); - result = -RT_ERROR; - goto __exit; - } - -__exit: - /* unlock AT socket connect */ - rt_mutex_release(at_event_lock); - - if (resp) - { - at_delete_resp(resp); - } - - return result; -} - -static int at_get_send_size(int socket, size_t *size, size_t *acked, size_t *nacked) -{ - at_response_t resp = at_create_resp(128, 0, 5 * RT_TICK_PER_SECOND); - int result = 0; - - if (!resp) - { - LOG_E("No memory for response structure!"); - result = -RT_ENOMEM; - goto __exit; - } - - if (at_exec_cmd(resp, "AT+QISEND=%d,0", socket) < 0) - { - result = -RT_ERROR; - goto __exit; - } - - if (at_resp_parse_line_args_by_kw(resp, "+QISEND:", "+QISEND: %d,%d,%d", size, acked, nacked) <= 0) - { - result = -RT_ERROR; - goto __exit; - } - -__exit: - if (resp) - { - at_delete_resp(resp); - } - - return result; -} - -static int at_wait_send_finish(int socket, size_t settings_size) -{ - /* get the timeout by the input data size */ - rt_tick_t timeout = rt_tick_from_millisecond(settings_size); - rt_tick_t last_time = rt_tick_get(); - size_t size = 0, acked = 0, nacked = 0xFFFF; - - while (rt_tick_get() - last_time <= timeout) - { - at_get_send_size(socket, &size, &acked, &nacked); - if (nacked == 0) - { - return RT_EOK; - } - rt_thread_mdelay(50); - } - - return -RT_ETIMEOUT; -} - -/** - * send data to server or client by AT commands. - * - * @param socket current socket - * @param buff send buffer - * @param bfsz send buffer size - * @param type connect socket type(tcp, udp) - * - * @return >=0: the size of send success - * -1: send AT commands error or send data error - * -2: waited socket event timeout - * -5: no memory - */ -static int ec20_socket_send(int socket, const char *buff, size_t bfsz, enum at_socket_type type) -{ - int result = 0, event_result = 0; - at_response_t resp = RT_NULL; - size_t cur_pkt_size = 0, sent_size = 0; - - RT_ASSERT(buff); - - resp = at_create_resp(128, 2, 5 * RT_TICK_PER_SECOND); - if (!resp) - { - LOG_E("No memory for response structure!"); - return -RT_ENOMEM; - } - - rt_mutex_take(at_event_lock, RT_WAITING_FOREVER); - - /* Clear socket send event */ - at_socket_event_recv(SET_EVENT(socket, EC20_EVENT_SEND_OK | EC20_EVENT_SEND_FAIL), 0, RT_EVENT_FLAG_OR); - - /* set current socket for send URC event */ - cur_socket = socket; - /* set AT client end sign to deal with '>' sign.*/ - at_set_end_sign('>'); - - while (sent_size < bfsz) - { - if (bfsz - sent_size < EC20_MODULE_SEND_MAX_SIZE) - { - cur_pkt_size = bfsz - sent_size; - } - else - { - cur_pkt_size = EC20_MODULE_SEND_MAX_SIZE; - } - - /* send the "AT+QISEND" commands to AT server than receive the '>' response on the first line. */ - if (at_exec_cmd(resp, "AT+QISEND=%d,%d", socket, cur_pkt_size) < 0) - { - result = -RT_ERROR; - goto __exit; - } - - /* send the real data to server or client */ - result = (int) at_client_send(buff + sent_size, cur_pkt_size); - if (result == 0) - { - result = -RT_ERROR; - goto __exit; - } - - /* waiting result event from AT URC */ - if (at_socket_event_recv(SET_EVENT(socket, 0), 10 * RT_TICK_PER_SECOND, RT_EVENT_FLAG_OR) < 0) - { - result = -RT_ETIMEOUT; - goto __exit; - } - /* waiting OK or failed result */ - if ((event_result = at_socket_event_recv(EC20_EVENT_SEND_OK | EC20_EVENT_SEND_FAIL, 1 * RT_TICK_PER_SECOND, - RT_EVENT_FLAG_OR)) < 0) - { - LOG_E("socket (%d) send failed, wait connect OK|FAIL timeout.", socket); - result = -RT_ETIMEOUT; - goto __exit; - } - /* check result */ - if (event_result & EC20_EVENT_SEND_FAIL) - { - LOG_E("socket (%d) send failed, return failed.", socket); - result = -RT_ERROR; - goto __exit; - } - - if (type == AT_SOCKET_TCP) - { - at_wait_send_finish(socket, cur_pkt_size); - } - - sent_size += cur_pkt_size; - } - - -__exit: - /* reset the end sign for data conflict */ - at_set_end_sign(0); - - rt_mutex_release(at_event_lock); - - if (resp) - { - at_delete_resp(resp); - } - - return result; -} - -/** - * domain resolve by AT commands. - * - * @param name domain name - * @param ip parsed IP address, it's length must be 16 - * - * @return 0: domain resolve success - * -1: send AT commands error or response error - * -2: wait socket event timeout - * -5: no memory - */ -static int ec20_domain_resolve(const char *name, char ip[16]) -{ -#define RESOLVE_RETRY 3 - - int i, result; -// char recv_ip[16] = { 0 }; - at_response_t resp = RT_NULL; - - RT_ASSERT(name); - RT_ASSERT(ip); - - /* The maximum response time is 60 seconds, but it set to 10 seconds is convenient to use. */ - resp = at_create_resp(128, 0, 10 * RT_TICK_PER_SECOND); - if (!resp) - { - LOG_E("No memory for response structure!"); - return -RT_ENOMEM; - } - - rt_mutex_take(at_event_lock, RT_WAITING_FOREVER); - - /* Clear EC20_EVENT_DOMAIN_OK */ - at_socket_event_recv(EC20_EVENT_DOMAIN_OK, 0, RT_EVENT_FLAG_OR); - - result = at_exec_cmd(resp, "AT+QIDNSGIP=1,\"%s\"", name); - if (result < 0) - { - goto __exit; - } - - if (result == RT_EOK) - { - for(i = 0; i < RESOLVE_RETRY; i++) - { - /* waiting result event from AT URC, the device default connection timeout is 60 seconds.*/ - if (at_socket_event_recv(EC20_EVENT_DOMAIN_OK, 10 * RT_TICK_PER_SECOND, RT_EVENT_FLAG_OR) < 0) - { - continue; - } - else - { - if (strlen(recv_ip) < 8) - { - rt_thread_mdelay(100); - /* resolve failed, maybe receive an URC CRLF */ - result = -RT_ERROR; - continue; - } - else - { - strncpy(ip, recv_ip, 15); - ip[15] = '\0'; - result = RT_EOK; - break; - } - } - } - - /* response timeout */ - if (i == RESOLVE_RETRY) - { - result = -RT_ENOMEM; - } - } - - __exit: - rt_mutex_release(at_event_lock); - - if (resp) - { - at_delete_resp(resp); - } - - return result; - -} - -/** - * set AT socket event notice callback - * - * @param event notice event - * @param cb notice callback - */ -static void ec20_socket_set_event_cb(at_socket_evt_t event, at_evt_cb_t cb) -{ - if (event < sizeof(at_evt_cb_set) / sizeof(at_evt_cb_set[1])) - { - at_evt_cb_set[event] = cb; - } -} - -static void urc_connect_func(const char *data, rt_size_t size) -{ - int socket = 0; - int result = 0; - - RT_ASSERT(data && size); - - sscanf(data, "+QIOPEN: %d,%d", &socket , &result); - - if (result == 0) - { - at_socket_event_send(SET_EVENT(socket, EC20_EVENT_CONN_OK)); - } - else - { - at_tcp_ip_errcode_parse(result); - at_socket_event_send(SET_EVENT(socket, EC20_EVENT_CONN_FAIL)); - } -} - -static void urc_send_func(const char *data, rt_size_t size) -{ - RT_ASSERT(data && size); - - if (strstr(data, "SEND OK")) - { - at_socket_event_send(SET_EVENT(cur_socket, EC20_EVENT_SEND_OK)); - } - else if (strstr(data, "SEND FAIL")) - { - at_socket_event_send(SET_EVENT(cur_socket, EC20_EVENT_SEND_FAIL)); - } -} - -static void urc_close_func(const char *data, rt_size_t size) -{ - int socket = 0; - - RT_ASSERT(data && size); - - sscanf(data, "+QIURC: \"closed\",%d", &socket); - - /* notice the socket is disconnect by remote */ - if (at_evt_cb_set[AT_SOCKET_EVT_CLOSED]) - { - at_evt_cb_set[AT_SOCKET_EVT_CLOSED](socket, AT_SOCKET_EVT_CLOSED, NULL, 0); - } - - // /* when TCP socket service is closed, host must send "AT+QICLOSE= ,0" command to close socket */ - // at_exec_cmd(RT_NULL, "AT+QICLOSE=%d,0\r\n", socket); - // rt_thread_mdelay(100); -} - -static void urc_recv_func(const char *data, rt_size_t size) -{ - int socket = 0; - rt_size_t bfsz = 0, temp_size = 0; - rt_int32_t timeout; - char *recv_buf = RT_NULL, temp[8]; - - RT_ASSERT(data && size); - - /* get the current socket and receive buffer size by receive data */ - sscanf(data, "+QIURC: \"recv\",%d,%d", &socket, (int *) &bfsz); - /* get receive timeout by receive buffer length */ - timeout = bfsz; - - if (socket < 0 || bfsz == 0) - return; - - recv_buf = rt_calloc(1, bfsz); - if (!recv_buf) - { - LOG_E("no memory for URC receive buffer (%d)!", bfsz); - /* read and clean the coming data */ - while (temp_size < bfsz) - { - if (bfsz - temp_size > sizeof(temp)) - { - at_client_recv(temp, sizeof(temp), timeout); - } - else - { - at_client_recv(temp, bfsz - temp_size, timeout); - } - temp_size += sizeof(temp); - } - return; - } - - /* sync receive data */ - if (at_client_recv(recv_buf, bfsz, timeout) != bfsz) - { - LOG_E("receive size(%d) data failed!", bfsz); - rt_free(recv_buf); - return; - } - - /* notice the receive buffer and buffer size */ - if (at_evt_cb_set[AT_SOCKET_EVT_RECV]) - { - at_evt_cb_set[AT_SOCKET_EVT_RECV](socket, AT_SOCKET_EVT_RECV, recv_buf, bfsz); - } -} - -static void urc_pdpdeact_func(const char *data, rt_size_t size) -{ - int connectID = 0; - - RT_ASSERT(data && size); - - sscanf(data, "+QIURC: \"pdpdeact\",%d", &connectID); - - LOG_E("Context (%d) is deactivated.", connectID); -} - -static void urc_dnsqip_func(const char *data, rt_size_t size) -{ - int i = 0, j = 0; - int result, ip_count, dns_ttl; - - RT_ASSERT(data && size); - - for (i = 0; i < size; i++) - { - if (*(data + i) == '.') - j++; - } - /* There would be several dns result, we just pickup one */ - if (j == 3) - { - sscanf(data, "+QIURC: \"dnsgip\",\"%[^\"]", recv_ip); - recv_ip[15] = '\0'; - at_socket_event_send(EC20_EVENT_DOMAIN_OK); - } - else - { - sscanf(data, "+QIURC: \"dnsgip\",%d,%d,%d", &result, &ip_count, &dns_ttl); - if (result) - { - at_tcp_ip_errcode_parse(result); - } - } -} - -static void urc_func(const char *data, rt_size_t size) -{ - RT_ASSERT(data); - - LOG_I("URC data : %.*s", size, data); -} - -static void urc_qiurc_func(const char *data, rt_size_t size) -{ - RT_ASSERT(data && size); - -// LOG_D("qiurc : %s", data); - switch(*(data+9)) - { - case 'c' : urc_close_func(data, size); break;//+QIURC: "closed" - case 'r' : urc_recv_func(data, size); break;//+QIURC: "recv" - case 'p' : urc_pdpdeact_func(data, size); break;//+QIURC: "pdpdeact" - case 'd' : urc_dnsqip_func(data, size); break;//+QIURC: "dnsgip" - default : urc_func(data, size); break;// - } -} - -static const struct at_urc urc_table[] = { - {"SEND OK", "\r\n", urc_send_func}, - {"SEND FAIL", "\r\n", urc_send_func}, - {"+QIOPEN:", "\r\n", urc_connect_func}, - {"+QIURC:", "\r\n", urc_qiurc_func}, -}; - -#define AT_SEND_CMD(resp, resp_line, timeout, cmd) \ - do \ - { \ - if (at_exec_cmd(at_resp_set_info(resp, 128, resp_line, rt_tick_from_millisecond(timeout)), cmd) < 0) \ - { \ - result = -RT_ERROR; \ - goto __exit; \ - } \ - } while(0); \ - -static int ec20_netdev_set_info(struct netdev *netdev); -static int ec20_netdev_check_link_status(struct netdev *netdev); - -/* init for EC20 */ -static void ec20_init_thread_entry(void *parameter) -{ -#define AT_RETRY 10 -#define CIMI_RETRY 10 -#define CSQ_RETRY 20 -#define CREG_RETRY 10 -#define CGREG_RETRY 20 - - at_response_t resp = RT_NULL; - int i, qi_arg[3]; - char parsed_data[20]; - rt_err_t result = RT_EOK; - - resp = at_create_resp(128, 0, rt_tick_from_millisecond(300)); - if (!resp) - { - LOG_E("No memory for response structure!"); - result = -RT_ENOMEM; - goto __exit; - } - LOG_D("Start initializing the EC20 module"); - /* wait EC20 startup finish, Send AT every 500ms, if receive OK, SYNC success*/ - if (at_client_wait_connect(EC20_WAIT_CONNECT_TIME)) - { - result = -RT_ETIMEOUT; - goto __exit; - } - /* set response format to ATV1 */ - AT_SEND_CMD(resp, 0, 300, "ATV1"); - /* disable echo */ - AT_SEND_CMD(resp, 0, 300, "ATE0"); - /* Use AT+CMEE=2 to enable result code and use verbose values */ - AT_SEND_CMD(resp, 0, 300, "AT+CMEE=2"); - /* Get the baudrate */ - AT_SEND_CMD(resp, 0, 300, "AT+IPR?"); - at_resp_parse_line_args_by_kw(resp, "+IPR:", "+IPR: %d", &i); - LOG_D("Baudrate %d", i); - /* get module version */ - AT_SEND_CMD(resp, 0, 300, "ATI"); - /* show module version */ - for (i = 0; i < (int) resp->line_counts - 1; i++) - { - LOG_D("%s", at_resp_get_line(resp, i + 1)); - } - /* Use AT+GSN to query the IMEI of module */ - AT_SEND_CMD(resp, 0, 300, "AT+GSN"); - - /* check SIM card */ - AT_SEND_CMD(resp, 2, 5 * 1000, "AT+CPIN?"); - if (!at_resp_get_line_by_kw(resp, "READY")) - { - LOG_E("SIM card detection failed"); - result = -RT_ERROR; - goto __exit; - } - /* waiting for dirty data to be digested */ - rt_thread_mdelay(10); - - - /* Use AT+CIMI to query the IMSI of SIM card */ -// AT_SEND_CMD(resp, 2, 300, "AT+CIMI"); - i = 0; - while(at_exec_cmd(at_resp_set_info(resp, 128, 0, rt_tick_from_millisecond(300)), "AT+CIMI") < 0) - { - i++; - LOG_D("AT+CIMI %d", i); - if(i > CIMI_RETRY) - { - LOG_E("Read CIMI failed"); - result = -RT_ERROR; - goto __exit; - } - rt_thread_mdelay(1000); - } - - /* Use AT+QCCID to query ICCID number of SIM card */ - AT_SEND_CMD(resp, 0, 300, "AT+QCCID"); - /* check signal strength */ - for (i = 0; i < CSQ_RETRY; i++) - { - AT_SEND_CMD(resp, 0, 300, "AT+CSQ"); - at_resp_parse_line_args_by_kw(resp, "+CSQ:", "+CSQ: %d,%d", &qi_arg[0], &qi_arg[1]); - if (qi_arg[0] != 99) - { - LOG_D("Signal strength: %d Channel bit error rate: %d", qi_arg[0], qi_arg[1]); - break; - } - rt_thread_mdelay(1000); - } - if (i == CSQ_RETRY) - { - LOG_E("Signal strength check failed (%s)", parsed_data); - result = -RT_ERROR; - goto __exit; - } - /* check the GSM network is registered */ - for (i = 0; i < CREG_RETRY; i++) - { - AT_SEND_CMD(resp, 0, 300, "AT+CREG?"); - at_resp_parse_line_args_by_kw(resp, "+CREG:", "+CREG: %s", &parsed_data); - if (!strncmp(parsed_data, "0,1", sizeof(parsed_data)) || !strncmp(parsed_data, "0,5", sizeof(parsed_data))) - { - LOG_D("GSM network is registered (%s)", parsed_data); - break; - } - rt_thread_mdelay(1000); - } - if (i == CREG_RETRY) - { - LOG_E("The GSM network is register failed (%s)", parsed_data); - result = -RT_ERROR; - goto __exit; - } - /* check the GPRS network is registered */ - for (i = 0; i < CGREG_RETRY; i++) - { - AT_SEND_CMD(resp, 0, 300, "AT+CGREG?"); - at_resp_parse_line_args_by_kw(resp, "+CGREG:", "+CGREG: %s", &parsed_data); - if (!strncmp(parsed_data, "0,1", sizeof(parsed_data)) || !strncmp(parsed_data, "0,5", sizeof(parsed_data))) - { - LOG_D("GPRS network is registered (%s)", parsed_data); - break; - } - rt_thread_mdelay(1000); - } - if (i == CGREG_RETRY) - { - LOG_E("The GPRS network is register failed (%s)", parsed_data); - result = -RT_ERROR; - goto __exit; - } - /*Use AT+CEREG? to query current EPS Network Registration Status*/ - AT_SEND_CMD(resp, 0, 300, "AT+CEREG?"); - /* Use AT+COPS? to query current Network Operator */ - AT_SEND_CMD(resp, 0, 300, "AT+COPS?"); - at_resp_parse_line_args_by_kw(resp, "+COPS:", "+COPS: %*[^\"]\"%[^\"]", &parsed_data); - if(strcmp(parsed_data,"CHINA MOBILE") == 0) - { - /* "CMCC" */ - LOG_I("%s", parsed_data); - AT_SEND_CMD(resp, 0, 300, QICSGP_CHINA_MOBILE); - } - else if(strcmp(parsed_data,"CHN-UNICOM") == 0) - { - /* "UNICOM" */ - LOG_I("%s", parsed_data); - AT_SEND_CMD(resp, 0, 300, QICSGP_CHINA_UNICOM); - } - else if(strcmp(parsed_data,"CHN-CT") == 0) - { - AT_SEND_CMD(resp, 0, 300, QICSGP_CHINA_TELECOM); - /* "CT" */ - LOG_I("%s", parsed_data); - } - /* Enable automatic time zone update via NITZ and update LOCAL time to RTC */ - AT_SEND_CMD(resp, 0, 300, "AT+CTZU=3"); - /* Get RTC time */ - AT_SEND_CMD(resp, 0, 300, "AT+CCLK?"); - - /* Deactivate context profile */ - AT_SEND_CMD(resp, 0, 40 * 1000, "AT+QIDEACT=1"); - /* Activate context profile */ - AT_SEND_CMD(resp, 0, 150 * 1000, "AT+QIACT=1"); - /* Query the status of the context profile */ - AT_SEND_CMD(resp, 0, 150 * 1000, "AT+QIACT?"); - at_resp_parse_line_args_by_kw(resp, "+QIACT:", "+QIACT: %*[^\"]\"%[^\"]", &parsed_data); - LOG_I("%s", parsed_data); - -__exit: - if (resp) - { - at_delete_resp(resp); - } - - if (!result) - { - /* set network interface device status and address information */ - ec20_netdev_set_info(netdev_get_by_name(EC20_NETDEV_NAME)); - ec20_netdev_check_link_status(netdev_get_by_name(EC20_NETDEV_NAME)); - - LOG_I("AT network initialize success!"); - } - else - { - LOG_E("AT network initialize failed (%d)!", result); - } - -} - -/* EC20 device network initialize */ -void ec20_net_init(void) -{ -#ifdef PKG_AT_INIT_BY_THREAD - rt_thread_t tid; - tid = rt_thread_create("ec20_net_init", ec20_init_thread_entry, RT_NULL, EC20_THREAD_STACK_SIZE, EC20_THREAD_PRIORITY, 20); - if (tid) - { - rt_thread_startup(tid); - } - else - { - LOG_E("Create AT initialization thread failed!"); - } -#else - ec20_init_thread_entry(RT_NULL); -#endif -} - -#ifdef FINSH_USING_MSH -#include -MSH_CMD_EXPORT_ALIAS(ec20_net_init, at_net_init, initialize AT network); -#endif - -static const struct at_device_ops ec20_socket_ops = { - ec20_socket_connect, - ec20_socket_close, - ec20_socket_send, - ec20_domain_resolve, - ec20_socket_set_event_cb, -}; - -/* set ec20 network interface device status and address information */ -static int ec20_netdev_set_info(struct netdev *netdev) -{ -#define EC20_IEMI_RESP_SIZE 32 -#define EC20_IPADDR_RESP_SIZE 64 -#define EC20_DNS_RESP_SIZE 96 -#define EC20_INFO_RESP_TIMO rt_tick_from_millisecond(300) - - int result = RT_EOK; - at_response_t resp = RT_NULL; - ip_addr_t addr; - - if (netdev == RT_NULL) - { - LOG_E("Input network interface device is NULL.\n"); - return -RT_ERROR; - } - - rt_mutex_take(at_event_lock, RT_WAITING_FOREVER); - - /* set network interface device status */ - netdev_low_level_set_status(netdev, RT_TRUE); - netdev_low_level_set_link_status(netdev, RT_TRUE); - netdev_low_level_set_dhcp_status(netdev, RT_TRUE); - - resp = at_create_resp(EC20_IEMI_RESP_SIZE, 0, EC20_INFO_RESP_TIMO); - if (resp == RT_NULL) - { - LOG_E("EC20 set netdev information failed, no memory for response object."); - result = -RT_ENOMEM; - goto __exit; - } - - /* set network interface device hardware address(IEMI) */ - { - #define EC20_NETDEV_HWADDR_LEN 8 - #define EC20_IEMI_LEN 15 - - char iemi[EC20_IEMI_LEN] = {0}; - int i = 0, j = 0; - - /* send "AT+GSN" commond to get device IEMI */ - if (at_exec_cmd(resp, "AT+GSN") < 0) - { - result = -RT_ERROR; - goto __exit; - } - - if (at_resp_parse_line_args(resp, 2, "%s", iemi) <= 0) - { - LOG_E("Prase \"AT+GSN\" commands resposne data error!"); - result = -RT_ERROR; - goto __exit; - } - - LOG_D("EC20 IEMI number: %s", iemi); - - netdev->hwaddr_len = EC20_NETDEV_HWADDR_LEN; - /* get hardware address by IEMI */ - for (i = 0, j = 0; i < EC20_NETDEV_HWADDR_LEN && j < EC20_IEMI_LEN; i++, j+=2) - { - if (j != EC20_IEMI_LEN - 1) - { - netdev->hwaddr[i] = (iemi[j] - '0') * 10 + (iemi[j + 1] - '0'); - } - else - { - netdev->hwaddr[i] = (iemi[j] - '0'); - } - } - } - - /* set network interface device IP address */ - { - #define IP_ADDR_SIZE_MAX 16 - char ipaddr[IP_ADDR_SIZE_MAX] = {0}; - - at_resp_set_info(resp, EC20_IPADDR_RESP_SIZE, 0, EC20_INFO_RESP_TIMO); - - /* send "AT+QIACT?" commond to get IP address */ - if (at_exec_cmd(resp, "AT+QIACT?") < 0) - { - result = -RT_ERROR; - goto __exit; - } - - /* parse response data "+QIACT: 1,,[,]" */ - if (at_resp_parse_line_args_by_kw(resp, "+QIACT:", "+QIACT: %*[^\"]\"%[^\"]", ipaddr) <= 0) - { - LOG_E("Prase \"AT+QIACT?\" commands resposne data error!"); - result = -RT_ERROR; - goto __exit; - } - - LOG_D("EC20 IP address: %s", ipaddr); - - /* set network interface address information */ - inet_aton(ipaddr, &addr); - netdev_low_level_set_ipaddr(netdev, &addr); - } - - /* set network interface device dns server */ - { - #define DNS_ADDR_SIZE_MAX 16 - char dns_server1[DNS_ADDR_SIZE_MAX] = {0}, dns_server2[DNS_ADDR_SIZE_MAX] = {0}; - - at_resp_set_info(resp, EC20_DNS_RESP_SIZE, 0, EC20_INFO_RESP_TIMO); - - /* send "AT+QIDNSCFG=1" commond to get DNS servers address */ - if (at_exec_cmd(resp, "AT+QIDNSCFG=1") < 0) - { - result = -RT_ERROR; - goto __exit; - } - - /* parse response data "+QIDNSCFG: ,," */ - if (at_resp_parse_line_args_by_kw(resp, "+QIDNSCFG:", "+QIDNSCFG: 1,\"%[^\"]\",\"%[^\"]\"", - dns_server1, dns_server2) <= 0) - { - LOG_E("Prase \"AT+QIDNSCFG=1\" commands resposne data error!"); - result = -RT_ERROR; - goto __exit; - } - - LOG_D("EC20 primary DNS server address: %s", dns_server1); - LOG_D("EC20 secondary DNS server address: %s", dns_server2); - - inet_aton(dns_server1, &addr); - netdev_low_level_set_dns_server(netdev, 0, &addr); - - inet_aton(dns_server2, &addr); - netdev_low_level_set_dns_server(netdev, 1, &addr); - } - -__exit: - if (resp) - { - at_delete_resp(resp); - } - - rt_mutex_release(at_event_lock); - - return result; -} - -static void ec20_check_link_status_entry(void *parameter) -{ -#define EC20_LINK_RESP_SIZE 64 -#define EC20_LINK_RESP_TIMO (3 * RT_TICK_PER_SECOND) -#define EC20_LINK_DELAY_TIME (30 * RT_TICK_PER_SECOND) - - int link_stat = 0; - at_response_t resp = RT_NULL; - struct netdev *netdev = (struct netdev *) parameter; - - resp = at_create_resp(EC20_LINK_RESP_SIZE, 0, EC20_LINK_RESP_TIMO); - if (resp == RT_NULL) - { - LOG_E("EC20 set check link status failed, no memory for response object."); - return; - } - - while (1) - { - rt_mutex_take(at_event_lock, RT_WAITING_FOREVER); - - /* send "AT+CGREG" commond to check netweork interface device link status */ - if (at_exec_cmd(resp, "AT+CGREG?") < 0) - { - if (netdev_is_link_up(netdev)) - { - netdev_low_level_set_link_status(netdev, RT_FALSE); - } - - rt_mutex_release(at_event_lock); - rt_thread_mdelay(EC20_LINK_DELAY_TIME); - continue; - } - else - { - at_resp_parse_line_args_by_kw(resp, "+CGREG:", "+CGREG: %*d,%d", &link_stat); - - /* 1 Registered, home network,5 Registered, roaming */ - if (link_stat == 1 || link_stat == 5) - { - if (netdev_is_link_up(netdev) == RT_FALSE) - { - netdev_low_level_set_link_status(netdev, RT_TRUE); - } - } - else - { - if (netdev_is_link_up(netdev)) - { - netdev_low_level_set_link_status(netdev, RT_FALSE); - } - } - } - - rt_mutex_release(at_event_lock); - rt_thread_mdelay(EC20_LINK_DELAY_TIME); - } -} - -static int ec20_netdev_check_link_status(struct netdev *netdev) -{ -#define EC20_LINK_THREAD_STACK_SIZE 1024 -#define EC20_LINK_THREAD_PRIORITY (RT_THREAD_PRIORITY_MAX - 2) -#define EC20_LINK_THREAD_TICK 20 - - rt_thread_t tid; - - if (netdev == RT_NULL) - { - LOG_E("Input network interface device is NULL.\n"); - return -RT_ERROR; - } - - /* create WIZnet link status Polling thread */ - tid = rt_thread_create("ec20_link", ec20_check_link_status_entry, (void *) netdev, - EC20_LINK_THREAD_STACK_SIZE, EC20_LINK_THREAD_PRIORITY, EC20_LINK_THREAD_TICK); - if (tid != RT_NULL) - { - rt_thread_startup(tid); - } - - return RT_EOK; -} - -static int ec20_netdev_set_up(struct netdev *netdev) -{ - netdev_low_level_set_status(netdev, RT_TRUE); - LOG_D("EC20 network interface set up status."); - return RT_EOK; -} - -static int ec20_netdev_set_down(struct netdev *netdev) -{ - netdev_low_level_set_status(netdev, RT_FALSE); - LOG_D("EC20 network interface set down status."); - return RT_EOK; -} - -static int ec20_netdev_set_dns_server(struct netdev *netdev, uint8_t dns_num, ip_addr_t *dns_server) -{ -#define EC20_DNS_RESP_LEN 8 -#define EC20_DNS_RESP_TIMEO rt_tick_from_millisecond(300) - - at_response_t resp = RT_NULL; - int result = RT_EOK; - - RT_ASSERT(netdev); - RT_ASSERT(dns_server); - - rt_mutex_take(at_event_lock, RT_WAITING_FOREVER); - - resp = at_create_resp(EC20_DNS_RESP_LEN, 0, EC20_DNS_RESP_TIMEO); - if (resp == RT_NULL) - { - LOG_D("EC20 set dns server failed, no memory for response object."); - result = -RT_ENOMEM; - goto __exit; - } - - /* send "AT+QIDNSCFG=[,]" commond to set dns servers */ - if (at_exec_cmd(resp, "AT+QIDNSCFG=1,\"%s\"", inet_ntoa(*dns_server)) < 0) - { - result = -RT_ERROR; - goto __exit; - } - - netdev_low_level_set_dns_server(netdev, dns_num, dns_server); - -__exit: - if (resp) - { - at_delete_resp(resp); - } - - rt_mutex_release(at_event_lock); - - return result; -} - -static int ec20_netdev_ping(struct netdev *netdev, const char *host, size_t data_len, uint32_t timeout, struct netdev_ping_resp *ping_resp) -{ -#define EC20_PING_RESP_SIZE 128 -#define EC20_PING_IP_SIZE 16 -#define EC20_PING_TIMEO (5 * RT_TICK_PER_SECOND) - - at_response_t resp = RT_NULL; - rt_err_t result = RT_EOK; - int response = -1, recv_data_len, ping_time, ttl; - char ip_addr[EC20_PING_IP_SIZE] = {0}; - - RT_ASSERT(netdev); - RT_ASSERT(host); - RT_ASSERT(ping_resp); - - rt_mutex_take(at_event_lock, RT_WAITING_FOREVER); - - resp = at_create_resp(EC20_PING_RESP_SIZE, 4, EC20_PING_TIMEO); - if (resp == RT_NULL) - { - LOG_D("No memory for response structure!\n"); - return -RT_ENOMEM; - } - - /* send "AT+QPING=""[,[][,]]" commond to send ping request */ - if (at_exec_cmd(resp, "AT+QPING=1,\"%s\",%d,1", host, timeout / RT_TICK_PER_SECOND) < 0) - { - result = -RT_ERROR; - goto __exit; - } - - at_resp_parse_line_args_by_kw(resp, "+QPING:", "+QPING:%d", &response); - /* Received the ping response from the server */ - if (response == 0) - { - if (at_resp_parse_line_args_by_kw(resp, "+QPING:", "+QPING:%d,\"%[^\"]\",%d,%d,%d", - &response, ip_addr, &recv_data_len, &ping_time, &ttl) <= 0) - { - result = -RT_ERROR; - goto __exit; - } - } - - /* prase response number */ - switch (response) - { - case 0: - inet_aton(ip_addr, &(ping_resp->ip_addr)); - ping_resp->data_len = recv_data_len; - ping_resp->ticks = ping_time; - ping_resp->ttl = ttl; - result = RT_EOK; - break; - case 569: - result = -RT_ETIMEOUT; - break; - default: - result = -RT_ERROR; - break; - } - -__exit: - if (resp) - { - at_delete_resp(resp); - } - - rt_mutex_release(at_event_lock); - - return result; -} - -void ec20_netdev_netstat(struct netdev *netdev) -{ - // TODO - return; -} - -const struct netdev_ops ec20_netdev_ops = -{ - ec20_netdev_set_up, - ec20_netdev_set_down, - - RT_NULL, - ec20_netdev_set_dns_server, - RT_NULL, - - ec20_netdev_ping, - ec20_netdev_netstat, -}; - -static int ec20_netdev_add(const char *netdev_name) -{ -#define ETHERNET_MTU 1500 -#define HWADDR_LEN 6 - struct netdev *netdev = RT_NULL; - - netdev = (struct netdev *)rt_calloc(1, sizeof(struct netdev)); - if (netdev == RT_NULL) - { - return RT_NULL; - } - - netdev->mtu = ETHERNET_MTU; - netdev->ops = &ec20_netdev_ops; - netdev->hwaddr_len = HWADDR_LEN; - -#ifdef SAL_USING_AT - extern int sal_at_netdev_set_pf_info(struct netdev *netdev); - /* set the network interface socket/netdb operations */ - sal_at_netdev_set_pf_info(netdev); -#endif - - return netdev_register(netdev, netdev_name, RT_NULL); -} - -static int at_socket_device_init(void) -{ - /* create current AT socket event */ - at_socket_event = rt_event_create("at_se", RT_IPC_FLAG_FIFO); - if (!at_socket_event) - { - LOG_E("AT client port initialize failed! at_sock_event create failed!"); - return -RT_ENOMEM; - } - - /* create current AT socket lock */ - at_event_lock = rt_mutex_create("at_se", RT_IPC_FLAG_FIFO); - if (!at_event_lock) - { - LOG_E("AT client port initialize failed! at_sock_lock create failed!"); - rt_event_delete(at_socket_event); - return -RT_ENOMEM; - } - - /* initialize AT client */ - at_client_init(AT_DEVICE_NAME, AT_DEVICE_RECV_BUFF_LEN); - - /* register URC data execution function */ - at_set_urc_table(urc_table, sizeof(urc_table) / sizeof(urc_table[0])); - - /* Add ec20 network inetrface device to the netdev list */ - if (ec20_netdev_add(EC20_NETDEV_NAME) < 0) - { - LOG_E("EC20 network interface device(%d) add failed.", EC20_NETDEV_NAME); - return -RT_ENOMEM; - } - - /* initialize EC20 network */ - ec20_net_init(); - - /* set EC20 AT Socket options */ - at_socket_device_register(&ec20_socket_ops); - - return RT_EOK; -} -INIT_APP_EXPORT(at_socket_device_init); - -#endif /* AT_DEVICE_EC20 */ diff --git a/at_socket_esp8266.c b/at_socket_esp8266.c deleted file mode 100644 index df9a93e..0000000 --- a/at_socket_esp8266.c +++ /dev/null @@ -1,1169 +0,0 @@ -/* - * File : at_socket_esp8266.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-20 chenyong first version - */ - -#include -#include - -#include -#include - -#include -#include - -#if !defined(RT_USING_NETDEV) -#error "This RT-Thread version is older, please check and updata laster RT-Thread!" -#else -#include -#include -#endif /* RT_USING_NETDEV */ - -#if !defined(AT_SW_VERSION_NUM) || AT_SW_VERSION_NUM < 0x10200 -#error "This AT Client version is older, please check and update latest AT Client!" -#endif - -#define LOG_TAG "at.esp8266" -#include - -#ifdef AT_DEVICE_ESP8266 - -#define ESP8266_MODULE_SEND_MAX_SIZE 2048 -#define ESP8266_WAIT_CONNECT_TIME 5000 -#define ESP8266_THREAD_STACK_SIZE 1024 -#define ESP8266_THREAD_PRIORITY (RT_THREAD_PRIORITY_MAX/2) - -/* set real event by current socket and current state */ -#define SET_EVENT(socket, event) (((socket + 1) << 16) | (event)) - -/* AT socket event type */ -#define ESP8266_EVENT_CONN_OK (1L << 0) -#define ESP8266_EVENT_SEND_OK (1L << 1) -#define ESP8266_EVENT_RECV_OK (1L << 2) -#define ESP8266_EVNET_CLOSE_OK (1L << 3) -#define ESP8266_EVENT_CONN_FAIL (1L << 4) -#define ESP8266_EVENT_SEND_FAIL (1L << 5) - -#define ESP8266_NETDEV_NAME "esp8266" - -static int cur_socket; -static int cur_send_bfsz; -static struct rt_delayed_work esp8266_net_work; -static rt_event_t at_socket_event; -static rt_mutex_t at_event_lock; -static at_evt_cb_t at_evt_cb_set[] = { - [AT_SOCKET_EVT_RECV] = NULL, - [AT_SOCKET_EVT_CLOSED] = NULL, -}; - -static int at_socket_event_send(uint32_t event) -{ - return (int) rt_event_send(at_socket_event, event); -} - -static int at_socket_event_recv(uint32_t event, uint32_t timeout, rt_uint8_t option) -{ - int result = 0; - rt_uint32_t recved; - - result = rt_event_recv(at_socket_event, event, option | RT_EVENT_FLAG_CLEAR, timeout, &recved); - if (result != RT_EOK) - { - return -RT_ETIMEOUT; - } - - return recved; -} - -/** - * close socket by AT commands. - * - * @param current socket - * - * @return 0: close socket success - * -1: send AT commands error - * -2: wait socket event timeout - * -5: no memory - */ -static int esp8266_socket_close(int socket) -{ - at_response_t resp = RT_NULL; - int result = RT_EOK; - - resp = at_create_resp(64, 0, rt_tick_from_millisecond(5000)); - if (!resp) - { - LOG_E("No memory for response structure!"); - return -RT_ENOMEM; - } - - rt_mutex_take(at_event_lock, RT_WAITING_FOREVER); - - if (at_exec_cmd(resp, "AT+CIPCLOSE=%d", socket) < 0) - { - result = -RT_ERROR; - goto __exit; - } - -__exit: - rt_mutex_release(at_event_lock); - - if (resp) - { - at_delete_resp(resp); - } - - return result; -} - -/** - * create TCP/UDP client or server connect by AT commands. - * - * @param socket current socket - * @param ip server or client IP address - * @param port server or client port - * @param type connect socket type(tcp, udp) - * @param is_client connection is client - * - * @return 0: connect success - * -1: connect failed, send commands error or type error - * -2: wait socket event timeout - * -5: no memory - */ -static int esp8266_socket_connect(int socket, char *ip, int32_t port, enum at_socket_type type, rt_bool_t is_client) -{ - at_response_t resp = RT_NULL; - int result = RT_EOK; - rt_bool_t retryed = RT_FALSE; - - RT_ASSERT(ip); - RT_ASSERT(port >= 0); - - resp = at_create_resp(128, 0, rt_tick_from_millisecond(5000)); - if (!resp) - { - LOG_E("No memory for response structure!"); - return -RT_ENOMEM; - } - - rt_mutex_take(at_event_lock, RT_WAITING_FOREVER); - -__retry: - if (is_client) - { - switch (type) - { - case AT_SOCKET_TCP: - /* send AT commands to connect TCP server */ - if (at_exec_cmd(resp, "AT+CIPSTART=%d,\"TCP\",\"%s\",%d,60", socket, ip, port) < 0) - { - result = -RT_ERROR; - } - break; - - case AT_SOCKET_UDP: - if (at_exec_cmd(resp, "AT+CIPSTART=%d,\"UDP\",\"%s\",%d", socket, ip, port) < 0) - { - result = -RT_ERROR; - } - break; - - default: - LOG_E("Not supported connect type : %d.", type); - result = -RT_ERROR; - goto __exit; - } - } - - if (result != RT_EOK && !retryed) - { - LOG_D("socket (%d) connect failed, maybe the socket was not be closed at the last time and now will retry.", socket); - if (esp8266_socket_close(socket) < 0) - { - goto __exit; - } - retryed = RT_TRUE; - result = RT_EOK; - goto __retry; - } - -__exit: - rt_mutex_release(at_event_lock); - - if (resp) - { - at_delete_resp(resp); - } - - return result; -} - -/** - * send data to server or client by AT commands. - * - * @param socket current socket - * @param buff send buffer - * @param bfsz send buffer size - * @param type connect socket type(tcp, udp) - * - * @return >=0: the size of send success - * -1: send AT commands error or send data error - * -2: waited socket event timeout - * -5: no memory - */ -static int esp8266_socket_send(int socket, const char *buff, size_t bfsz, enum at_socket_type type) -{ - int result = RT_EOK; - int event_result = 0; - at_response_t resp = RT_NULL; - size_t cur_pkt_size = 0, sent_size = 0; - - RT_ASSERT(buff); - RT_ASSERT(bfsz > 0); - - resp = at_create_resp(128, 2, rt_tick_from_millisecond(5000)); - if (!resp) - { - LOG_E("No memory for response structure!"); - return -RT_ENOMEM; - } - - rt_mutex_take(at_event_lock, RT_WAITING_FOREVER); - - /* set current socket for send URC event */ - cur_socket = socket; - /* set AT client end sign to deal with '>' sign.*/ - at_set_end_sign('>'); - - while (sent_size < bfsz) - { - if (bfsz - sent_size < ESP8266_MODULE_SEND_MAX_SIZE) - { - cur_pkt_size = bfsz - sent_size; - } - else - { - cur_pkt_size = ESP8266_MODULE_SEND_MAX_SIZE; - } - - /* send the "AT+CIPSEND" commands to AT server than receive the '>' response on the first line. */ - if (at_exec_cmd(resp, "AT+CIPSEND=%d,%d", socket, cur_pkt_size) < 0) - { - result = -RT_ERROR; - goto __exit; - } - - /* send the real data to server or client */ - result = (int) at_client_send(buff + sent_size, cur_pkt_size); - if (result == 0) - { - result = -RT_ERROR; - goto __exit; - } - - /* waiting result event from AT URC */ - if (at_socket_event_recv(SET_EVENT(socket, 0), rt_tick_from_millisecond(5 * 1000), RT_EVENT_FLAG_OR) < 0) - { - LOG_E("socket (%d) send failed, wait connect result timeout.", socket); - result = -RT_ETIMEOUT; - goto __exit; - } - /* waiting OK or failed result */ - if ((event_result = at_socket_event_recv(ESP8266_EVENT_SEND_OK | ESP8266_EVENT_SEND_FAIL, rt_tick_from_millisecond(5 * 1000), - RT_EVENT_FLAG_OR)) < 0) - { - LOG_E("socket (%d) send failed, wait connect OK|FAIL timeout.", socket); - result = -RT_ETIMEOUT; - goto __exit; - } - /* check result */ - if (event_result & ESP8266_EVENT_SEND_FAIL) - { - LOG_E("socket (%d) send failed, return failed.", socket); - result = -RT_ERROR; - goto __exit; - } - - if (type == AT_SOCKET_TCP) - { - cur_pkt_size = cur_send_bfsz; - } - - sent_size += cur_pkt_size; - } - -__exit: - /* reset the end sign for data */ - at_set_end_sign(0); - - rt_mutex_release(at_event_lock); - - if (resp) - { - at_delete_resp(resp); - } - - return result; -} - -/** - * domain resolve by AT commands. - * - * @param name domain name - * @param ip parsed IP address, it's length must be 16 - * - * @return 0: domain resolve success - * -2: wait socket event timeout - * -5: no memory - */ -static int esp8266_domain_resolve(const char *name, char ip[16]) -{ -#define RESOLVE_RETRY 5 - - int i, result = RT_EOK; - char recv_ip[16] = { 0 }; - at_response_t resp = RT_NULL; - - RT_ASSERT(name); - RT_ASSERT(ip); - - resp = at_create_resp(128, 0, rt_tick_from_millisecond(20000)); - if (!resp) - { - LOG_E("No memory for response structure!"); - return -RT_ENOMEM; - } - - rt_mutex_take(at_event_lock, RT_WAITING_FOREVER); - - for (i = 0; i < RESOLVE_RETRY; i++) - { - if (at_exec_cmd(resp, "AT+CIPDOMAIN=\"%s\"", name) < 0) - { - result = -RT_ERROR; - goto __exit; - } - - /* parse the third line of response data, get the IP address */ - if (at_resp_parse_line_args_by_kw(resp, "+CIPDOMAIN:", "+CIPDOMAIN:%s", recv_ip) < 0) - { - rt_thread_delay(rt_tick_from_millisecond(100)); - /* resolve failed, maybe receive an URC CRLF */ - continue; - } - - if (strlen(recv_ip) < 8) - { - rt_thread_delay(rt_tick_from_millisecond(100)); - /* resolve failed, maybe receive an URC CRLF */ - continue; - } - else - { - strncpy(ip, recv_ip, 15); - ip[15] = '\0'; - break; - } - } - -__exit: - rt_mutex_release(at_event_lock); - - if (resp) - { - at_delete_resp(resp); - } - - return result; - -} - -/** - * set AT socket event notice callback - * - * @param event notice event - * @param cb notice callback - */ -static void esp8266_socket_set_event_cb(at_socket_evt_t event, at_evt_cb_t cb) -{ - if (event < sizeof(at_evt_cb_set) / sizeof(at_evt_cb_set[1])) - { - at_evt_cb_set[event] = cb; - } -} - -static void urc_send_func(const char *data, rt_size_t size) -{ - RT_ASSERT(data && size); - - if (strstr(data, "SEND OK")) - { - at_socket_event_send(SET_EVENT(cur_socket, ESP8266_EVENT_SEND_OK)); - } - else if (strstr(data, "SEND FAIL")) - { - at_socket_event_send(SET_EVENT(cur_socket, ESP8266_EVENT_SEND_FAIL)); - } -} - -static void urc_send_bfsz_func(const char *data, rt_size_t size) -{ - int send_bfsz = 0; - - RT_ASSERT(data && size); - - sscanf(data, "Recv %d bytes", &send_bfsz); - - cur_send_bfsz = send_bfsz; -} - -static void urc_close_func(const char *data, rt_size_t size) -{ - int socket = 0; - - RT_ASSERT(data && size); - - sscanf(data, "%d,CLOSED", &socket); - /* notice the socket is disconnect by remote */ - if (at_evt_cb_set[AT_SOCKET_EVT_CLOSED]) - { - at_evt_cb_set[AT_SOCKET_EVT_CLOSED](socket, AT_SOCKET_EVT_CLOSED, RT_NULL, 0); - } -} - -static void urc_recv_func(const char *data, rt_size_t size) -{ - int socket = 0; - rt_size_t bfsz = 0, temp_size = 0; - rt_int32_t timeout; - char *recv_buf = RT_NULL, temp[8]; - - RT_ASSERT(data && size); - - /* get the current socket and receive buffer size by receive data */ - sscanf(data, "+IPD,%d,%d:", &socket, (int *) &bfsz); - /* get receive timeout by receive buffer length */ - timeout = bfsz; - - if (socket < 0 || bfsz == 0) - return; - - recv_buf = rt_calloc(1, bfsz); - if (!recv_buf) - { - LOG_E("no memory for URC receive buffer (%d)!", bfsz); - /* read and clean the coming data */ - while (temp_size < bfsz) - { - if (bfsz - temp_size > sizeof(temp)) - { - at_client_recv(temp, sizeof(temp), timeout); - } - else - { - at_client_recv(temp, bfsz - temp_size, timeout); - } - temp_size += sizeof(temp); - } - return; - } - - /* sync receive data */ - if (at_client_recv(recv_buf, bfsz, timeout) != bfsz) - { - LOG_E("receive size(%d) data failed!", bfsz); - rt_free(recv_buf); - return; - } - - /* notice the receive buffer and buffer size */ - if (at_evt_cb_set[AT_SOCKET_EVT_RECV]) - { - at_evt_cb_set[AT_SOCKET_EVT_RECV](socket, AT_SOCKET_EVT_RECV, recv_buf, bfsz); - } -} - -static void urc_busy_p_func(const char *data, rt_size_t size) -{ - RT_ASSERT(data && size); - - LOG_D("system is processing a commands and it cannot respond to the current commands."); -} - -static void urc_busy_s_func(const char *data, rt_size_t size) -{ - RT_ASSERT(data && size); - - LOG_D("system is sending data and it cannot respond to the current commands."); -} - -static void urc_func(const char *data, rt_size_t size) -{ - RT_ASSERT(data && size); - - if (strstr(data, "WIFI CONNECTED")) - { - netdev_low_level_set_link_status(netdev_get_by_name(ESP8266_NETDEV_NAME), RT_TRUE); - rt_work_submit(&(esp8266_net_work.work), RT_TICK_PER_SECOND); - LOG_I("ESP8266 WIFI is connected."); - } - else if (strstr(data, "WIFI DISCONNECT")) - { - netdev_low_level_set_link_status(netdev_get_by_name(ESP8266_NETDEV_NAME), RT_FALSE); - LOG_I("ESP8266 WIFI is disconnect."); - } -} - -static struct at_urc urc_table[] = { - {"SEND OK", "\r\n", urc_send_func}, - {"SEND FAIL", "\r\n", urc_send_func}, - {"Recv", "bytes\r\n", urc_send_bfsz_func}, - {"", ",CLOSED\r\n", urc_close_func}, - {"+IPD", ":", urc_recv_func}, - {"busy p", "\r\n", urc_busy_p_func}, - {"busy s", "\r\n", urc_busy_s_func}, - {"WIFI CONNECTED", "\r\n", urc_func}, - {"WIFI DISCONNECT", "\r\n", urc_func}, -}; - -#define AT_SEND_CMD(resp, cmd) \ - do \ - { \ - if (at_exec_cmd(at_resp_set_info(resp, 256, 0, rt_tick_from_millisecond(5000)), cmd) < 0) \ - { \ - LOG_E("RT AT send commands(%s) error!", cmd); \ - result = -RT_ERROR; \ - goto __exit; \ - } \ - } while(0); \ - -static void exp8266_get_netdev_info(struct rt_work *work, void *work_data) -{ -#define AT_ADDR_LEN 32 - at_response_t resp = RT_NULL; - char ip[AT_ADDR_LEN], mac[AT_ADDR_LEN]; - char gateway[AT_ADDR_LEN], netmask[AT_ADDR_LEN]; - char dns_server1[AT_ADDR_LEN] = {0}, dns_server2[AT_ADDR_LEN] = {0}; - const char *resp_expr = "%*[^\"]\"%[^\"]\""; - const char *resp_dns = "+CIPDNS_CUR:%s"; - ip_addr_t sal_ip_addr; - rt_uint32_t mac_addr[6] = {0}; - rt_uint32_t num = 0; - rt_uint8_t dhcp_stat = 0; - struct netdev *netdev = RT_NULL; - - netdev = (struct netdev *)work_data; - - rt_memset(ip, 0x00, sizeof(ip)); - rt_memset(mac, 0x00, sizeof(mac)); - rt_memset(gateway, 0x00, sizeof(gateway)); - rt_memset(netmask, 0x00, sizeof(netmask)); - - resp = at_create_resp(512, 0, rt_tick_from_millisecond(300)); - if (!resp) - { - LOG_E("No memory for response structure!"); - return; - } - - rt_mutex_take(at_event_lock, RT_WAITING_FOREVER); - /* send mac addr query commond "AT+CIFSR" and wait response */ - if (at_exec_cmd(resp, "AT+CIFSR") < 0) - { - LOG_E("AT send \"AT+CIFSR\" commands error!"); - goto __exit; - } - - if (at_resp_parse_line_args(resp, 2, resp_expr, mac) <= 0) - { - LOG_E("Parse error, current line buff : %s", at_resp_get_line(resp, 2)); - goto __exit; - } - - /* send addr info query commond "AT+CIPSTA?" and wait response */ - if (at_exec_cmd(resp, "AT+CIPSTA?") < 0) - { - LOG_E("AT send \"AT+CIPSTA?\" commands error!"); - goto __exit; - } - - if (at_resp_parse_line_args(resp, 1, resp_expr, ip) <= 0 || - at_resp_parse_line_args(resp, 2, resp_expr, gateway) <= 0 || - at_resp_parse_line_args(resp, 3, resp_expr, netmask) <= 0) - { - LOG_E("Prase \"AT+CIPSTA?\" commands resposne data error!"); - goto __exit; - } - - /* set netdev info */ - inet_aton(ip, &sal_ip_addr); - netdev_low_level_set_ipaddr(netdev, &sal_ip_addr); - inet_aton(gateway, &sal_ip_addr); - netdev_low_level_set_gw(netdev, &sal_ip_addr); - inet_aton(netmask, &sal_ip_addr); - netdev_low_level_set_netmask(netdev, &sal_ip_addr); - sscanf(mac, "%x:%x:%x:%x:%x:%x", &mac_addr[0], &mac_addr[1], &mac_addr[2], &mac_addr[3], &mac_addr[4], &mac_addr[5]); - for (num = 0; num < netdev->hwaddr_len; num++) - { - netdev->hwaddr[num] = mac_addr[num]; - } - - /* send dns server query commond "AT+CIPDNS_CUR?" and wait response */ - if (at_exec_cmd(resp, "AT+CIPDNS_CUR?") < 0) - { - LOG_W("Get dns server failed! Please check and update your firmware to support the \"AT+CIPDNS_CUR?\" command."); - goto __exit; - } - - if (at_resp_parse_line_args(resp, 1, resp_dns, dns_server1) <= 0 && - at_resp_parse_line_args(resp, 2, resp_dns, dns_server2) <= 0) - { - LOG_E("Prase \"AT+CIPDNS_CUR?\" commands resposne data error!"); - goto __exit; - } - - if (strlen(dns_server1) > 0) - { - inet_aton(dns_server1, &sal_ip_addr); - netdev_low_level_set_dns_server(netdev, 0, &sal_ip_addr); - } - - if (strlen(dns_server2) > 0) - { - inet_aton(dns_server2, &sal_ip_addr); - netdev_low_level_set_dns_server(netdev, 1, &sal_ip_addr); - } - - /* send DHCP query commond " AT+CWDHCP_CUR?" and wait response */ - if (at_exec_cmd(resp, "AT+CWDHCP_CUR?") < 0) - { - LOG_E("AT send \"AT+CWDHCP_CUR?\" commands error!"); - goto __exit; - } - - /* parse response data, get the DHCP status */ - if (at_resp_parse_line_args_by_kw(resp, "+CWDHCP_CUR:", "+CWDHCP_CUR:%d", &dhcp_stat) < 0) - { - LOG_E("get DHCP status failed!"); - goto __exit; - } - - /* Bit0 - SoftAP DHCP status, Bit1 - Station DHCP status */ - if (dhcp_stat & 0x02) - { - netdev_low_level_set_dhcp_status(netdev, RT_TRUE); - } - else - { - netdev_low_level_set_dhcp_status(netdev, RT_FALSE); - } - -__exit: - rt_mutex_release(at_event_lock); - - if (resp) - { - at_delete_resp(resp); - } -} - -static void esp8266_init_thread_entry(void *parameter) -{ - at_response_t resp = RT_NULL; - rt_err_t result = RT_EOK; - rt_size_t i; - - resp = at_create_resp(128, 0, rt_tick_from_millisecond(5000)); - if (!resp) - { - LOG_E("No memory for response structure!"); - result = -RT_ENOMEM; - goto __exit; - } - - rt_thread_delay(rt_tick_from_millisecond(5000)); - /* reset module */ - AT_SEND_CMD(resp, "AT+RST"); - /* reset waiting delay */ - rt_thread_delay(rt_tick_from_millisecond(1000)); - /* disable echo */ - AT_SEND_CMD(resp, "ATE0"); - /* set current mode to Wi-Fi station */ - AT_SEND_CMD(resp, "AT+CWMODE=1"); - /* get module version */ - AT_SEND_CMD(resp, "AT+GMR"); - /* show module version */ - for (i = 0; i < resp->line_counts - 1; i++) - { - LOG_D("%s", at_resp_get_line(resp, i + 1)); - } - /* connect to WiFi AP */ - if (at_exec_cmd(at_resp_set_info(resp, 128, 0, 20 * RT_TICK_PER_SECOND), "AT+CWJAP=\"%s\",\"%s\"", - AT_DEVICE_WIFI_SSID, AT_DEVICE_WIFI_PASSWORD) != RT_EOK) - { - LOG_E("AT network initialize failed, check ssid(%s) and password(%s).", AT_DEVICE_WIFI_SSID, AT_DEVICE_WIFI_PASSWORD); - result = -RT_ERROR; - goto __exit; - } - - AT_SEND_CMD(resp, "AT+CIPMUX=1"); - -__exit: - if (resp) - { - at_delete_resp(resp); - } - - if (!result) - { - netdev_low_level_set_status(netdev_get_by_name(ESP8266_NETDEV_NAME), RT_TRUE); - LOG_I("AT network initialize success!"); - } - else - { - netdev_low_level_set_status(netdev_get_by_name(ESP8266_NETDEV_NAME), RT_FALSE); - LOG_E("AT network initialize failed (%d)!", result); - } -} - -int esp8266_net_init(void) -{ -#ifdef PKG_AT_INIT_BY_THREAD - rt_thread_t tid; - - tid = rt_thread_create("esp8266_net_init", esp8266_init_thread_entry, RT_NULL, ESP8266_THREAD_STACK_SIZE, ESP8266_THREAD_PRIORITY, 20); - if (tid) - { - rt_thread_startup(tid); - } - else - { - LOG_E("Create AT initialization thread fail!"); - } -#else - esp8266_init_thread_entry(RT_NULL); -#endif - - return RT_EOK; -} -#ifdef FINSH_USING_MSH - #include - MSH_CMD_EXPORT_ALIAS(esp8266_net_init, at_net_init, initialize AT network); -#endif - -static const struct at_device_ops esp8266_socket_ops = -{ - esp8266_socket_connect, - esp8266_socket_close, - esp8266_socket_send, - esp8266_domain_resolve, - esp8266_socket_set_event_cb, -}; - -static int esp8266_netdev_set_up(struct netdev *netdev) -{ - netdev_low_level_set_status(netdev, RT_TRUE); - LOG_D("esp8266 network interface set up status."); - return RT_EOK; -} - -static int esp8266_netdev_set_down(struct netdev *netdev) -{ - netdev_low_level_set_status(netdev, RT_FALSE); - LOG_D("esp8266 network interface set down status."); - return RT_EOK; -} - -static int esp8266_netdev_set_addr_info(struct netdev *netdev, ip_addr_t *ip_addr, ip_addr_t *netmask, ip_addr_t *gw) -{ -#define RESP_SIZE 128 -#define IPV4_ADDR_STRLEN_MAX 16 - at_response_t resp = RT_NULL; - int result = RT_EOK; - char esp8266_ip_addr[IPV4_ADDR_STRLEN_MAX] = {0}; - char esp8266_gw_addr[IPV4_ADDR_STRLEN_MAX] = {0}; - char esp8266_netmask_addr[IPV4_ADDR_STRLEN_MAX] = {0}; - - RT_ASSERT(netdev); - RT_ASSERT(ip_addr || netmask || gw); - - resp = at_create_resp(RESP_SIZE, 0, rt_tick_from_millisecond(300)); - if (!resp) - { - LOG_E("No memory for response structure!"); - result = -RT_ENOMEM; - goto __exit; - } - - /* Convert numeric IP address into decimal dotted ASCII representation. */ - if (ip_addr) - rt_memcpy(esp8266_ip_addr, inet_ntoa(*ip_addr), IPV4_ADDR_STRLEN_MAX); - else - rt_memcpy(esp8266_ip_addr, inet_ntoa(netdev->ip_addr), IPV4_ADDR_STRLEN_MAX); - - if (gw) - rt_memcpy(esp8266_gw_addr, inet_ntoa(*gw), IPV4_ADDR_STRLEN_MAX); - else - rt_memcpy(esp8266_gw_addr, inet_ntoa(netdev->gw), IPV4_ADDR_STRLEN_MAX); - - if (netmask) - rt_memcpy(esp8266_netmask_addr, inet_ntoa(*netmask), IPV4_ADDR_STRLEN_MAX); - else - rt_memcpy(esp8266_netmask_addr, inet_ntoa(netdev->netmask), IPV4_ADDR_STRLEN_MAX); - - /* send addr info set commond "AT+CIPSTA_CUR=[,,]" and wait response */ - if (at_exec_cmd(resp, "AT+CIPSTA_CUR=\"%s\",\"%s\",\"%s\"", esp8266_ip_addr, esp8266_gw_addr, esp8266_netmask_addr) < 0) - { - LOG_D("esp8266 set addr info failed."); - result = -RT_ERROR; - } - else - { - /* Update netdev information */ - if (ip_addr) - netdev_low_level_set_ipaddr(netdev, ip_addr); - - if (gw) - netdev_low_level_set_gw(netdev, gw); - - if (netmask) - netdev_low_level_set_netmask(netdev, netmask); - - LOG_D("esp8266 set addr info successfully."); - } - -__exit: - if (resp) - { - at_delete_resp(resp); - } - - return result; -} - -static int esp8266_netdev_set_dns_server(struct netdev *netdev, uint8_t dns_num, ip_addr_t *dns_server) -{ -#define RESP_SIZE 128 - at_response_t resp = RT_NULL; - int result = RT_EOK; - - RT_ASSERT(netdev); - RT_ASSERT(dns_server); - - resp = at_create_resp(RESP_SIZE, 0, rt_tick_from_millisecond(300)); - if (!resp) - { - LOG_E("No memory for response structure!"); - return -RT_ENOMEM; - } - - rt_mutex_take(at_event_lock, RT_WAITING_FOREVER); - - /* send dns server set commond "AT+CIPDNS_CUR=[,,]" and wait response */ - if (at_exec_cmd(resp, "AT+CIPDNS_CUR=1,\"%s\"", inet_ntoa(*dns_server)) < 0) - { - LOG_E("set dns server(%s) failed", inet_ntoa(*dns_server)); - result = -RT_ERROR; - } - else - { - netdev_low_level_set_dns_server(netdev, dns_num, dns_server); - LOG_D("esp8266 set dns server successfully."); - } - - if (resp) - { - at_delete_resp(resp); - } - - rt_mutex_release(at_event_lock); - - return result; -} - -static int esp8266_netdev_set_dhcp(struct netdev *netdev, rt_bool_t is_enabled) -{ -#define ESP8266_STATION 1 -#define RESP_SIZE 128 - - at_response_t resp = RT_NULL; - int result = RT_EOK; - - RT_ASSERT(netdev); - - resp = at_create_resp(RESP_SIZE, 0, rt_tick_from_millisecond(300)); - if (!resp) - { - LOG_E("No memory for response structure!"); - return -RT_ENOMEM; - } - - rt_mutex_take(at_event_lock, RT_WAITING_FOREVER); - - /* send dhcp set commond "AT+CWDHCP_CUR=," and wait response */ - if (at_exec_cmd(resp, "AT+CWDHCP_CUR=%d,%d", ESP8266_STATION, is_enabled) < 0) - { - LOG_E("set dhcp status(%d) failed", is_enabled); - result = -RT_ERROR; - goto __exit; - } - else - { - netdev_low_level_set_dhcp_status(netdev, is_enabled); - LOG_D("esp8266 set dhcp successfully."); - } - -__exit: - if (resp) - { - at_delete_resp(resp); - } - - rt_mutex_release(at_event_lock); - - return result; -} - -static int esp8266_netdev_ping(struct netdev *netdev, const char *host, size_t data_len, uint32_t timeout, struct netdev_ping_resp *ping_resp) -{ -#define ESP8266_PING_IP_SIZE 16 - - at_response_t resp = RT_NULL; - rt_err_t result = RT_EOK; - int req_time; - char ip_addr[ESP8266_PING_IP_SIZE] = {0}; - - RT_ASSERT(netdev); - RT_ASSERT(host); - RT_ASSERT(ping_resp); - - resp = at_create_resp(64, 0, timeout); - if (!resp) - { - LOG_E("No memory for response structure!"); - return -RT_ENOMEM; - } - - rt_mutex_take(at_event_lock, RT_WAITING_FOREVER); - - /* send domain commond "AT+CIPDOMAIN=" and wait response */ - if (at_exec_cmd(resp, "AT+CIPDOMAIN=\"%s\"", host) < 0) - { - LOG_D("ping: send commond AT+CIPDOMAIN= failed"); - result = -RT_ERROR; - goto __exit; - } - - /* parse the third line of response data, get the IP address */ - if (at_resp_parse_line_args_by_kw(resp, "+CIPDOMAIN:", "+CIPDOMAIN:%s", ip_addr) < 0) - { - LOG_E("ping: get the IP address failed"); - result = -RT_ERROR; - goto __exit; - } - - /* send ping commond "AT+PING=" and wait response */ - if (at_exec_cmd(resp, "AT+PING=\"%s\"", host) < 0) - { - LOG_D("ping: unknown remote server host"); - result = -RT_ERROR; - goto __exit; - } - - if (at_resp_parse_line_args_by_kw(resp, "+", "+%d", &req_time) < 0) - { - result = -RT_ERROR; - goto __exit; - } - - if (req_time) - { - inet_aton(ip_addr, &(ping_resp->ip_addr)); - ping_resp->data_len = data_len; - ping_resp->ttl = 0; - ping_resp->ticks = req_time; - } - -__exit: - if (resp) - { - at_delete_resp(resp); - } - - rt_mutex_release(at_event_lock); - - return result; -} - -void esp8266_netdev_netstat(struct netdev *netdev) -{ -#define ESP8266_NETSTAT_RESP_SIZE 320 -#define ESP8266_NETSTAT_TYPE_SIZE 4 -#define ESP8266_NETSTAT_IPADDR_SIZE 17 -#define ESP8266_NETSTAT_EXPRESSION "+CIPSTATUS:%*d,\"%[^\"]\",\"%[^\"]\",%d,%d,%*d" - - at_response_t resp = RT_NULL; - int remote_port, local_port, i; - char *type = RT_NULL; - char *ipaddr = RT_NULL; - - type = rt_calloc(1, ESP8266_NETSTAT_TYPE_SIZE); - ipaddr = rt_calloc(1, ESP8266_NETSTAT_IPADDR_SIZE); - if ((type && ipaddr) == RT_NULL) - { - LOG_E("No memory for response structure!"); - goto __exit; - } - - resp = at_create_resp(ESP8266_NETSTAT_RESP_SIZE, 0, rt_tick_from_millisecond(5000)); - if (!resp) - { - LOG_E("No memory for response structure!"); - goto __exit; - } - - rt_mutex_take(at_event_lock, RT_WAITING_FOREVER); - - /* send network connection information commond "AT+CIPSTATUS" and wait response */ - if (at_exec_cmd(resp, "AT+CIPSTATUS") < 0) - { - LOG_E("netstat: send commond AT+CIPSTATUS failed"); - goto __exit; - } - - for (i = 1; i <= resp->line_counts; i++) - { - if (strstr(at_resp_get_line(resp, i), "+CIPSTATUS")) - { - /* parse the third line of response data, get the network connection information */ - if (at_resp_parse_line_args(resp, i, ESP8266_NETSTAT_EXPRESSION, type, ipaddr, &remote_port, &local_port) < 0) - goto __exit; - else - { - LOG_RAW("%s: %s:%d ==> %s:%d\n", type, inet_ntoa(netdev->ip_addr), local_port, ipaddr, remote_port); - } - } - } - -__exit: - if (resp) - { - at_delete_resp(resp); - } - - if (type) - { - rt_free(type); - } - - if (ipaddr) - { - rt_free(ipaddr); - } - - rt_mutex_release(at_event_lock); -} - -const struct netdev_ops esp8266_netdev_ops = -{ - esp8266_netdev_set_up, - esp8266_netdev_set_down, - - esp8266_netdev_set_addr_info, - esp8266_netdev_set_dns_server, - esp8266_netdev_set_dhcp, - - esp8266_netdev_ping, - esp8266_netdev_netstat, -}; - -static struct netdev *esp8266_netdev_add(const char *netdev_name) -{ -#define ETHERNET_MTU 1500 -#define HWADDR_LEN 6 - struct netdev *netdev = RT_NULL; - - RT_ASSERT(netdev_name); - - netdev = (struct netdev *) rt_calloc(1, sizeof(struct netdev)); - if (netdev == RT_NULL) - { - return RT_NULL; - } - - netdev->mtu = ETHERNET_MTU; - netdev->ops = &esp8266_netdev_ops; - netdev->hwaddr_len = HWADDR_LEN; - -#ifdef SAL_USING_AT - extern int sal_at_netdev_set_pf_info(struct netdev *netdev); - /* set the network interface socket/netdb operations */ - sal_at_netdev_set_pf_info(netdev); -#endif - - netdev_register(netdev, netdev_name, RT_NULL); - - return netdev; -} - -static int at_socket_device_init(void) -{ - /* create current AT socket event */ - at_socket_event = rt_event_create("at_se", RT_IPC_FLAG_FIFO); - if (at_socket_event == RT_NULL) - { - LOG_E("RT AT client port initialize failed! at_sock_event create failed!"); - return -RT_ENOMEM; - } - - /* create current AT socket event lock */ - at_event_lock = rt_mutex_create("at_se", RT_IPC_FLAG_FIFO); - if (at_event_lock == RT_NULL) - { - LOG_E("RT AT client port initialize failed! at_sock_lock create failed!"); - rt_event_delete(at_socket_event); - return -RT_ENOMEM; - } - - /* initialize AT client */ - at_client_init(AT_DEVICE_NAME, AT_DEVICE_RECV_BUFF_LEN); - - /* register URC data execution function */ - at_set_urc_table(urc_table, sizeof(urc_table) / sizeof(urc_table[0])); - - /* Add esp8266 to the netdev list */ - esp8266_netdev_add(ESP8266_NETDEV_NAME); - - /* initialize esp8266 net workqueue */ - rt_delayed_work_init(&esp8266_net_work, exp8266_get_netdev_info, (void *)netdev_get_by_name(ESP8266_NETDEV_NAME)); - - /* initialize esp8266 network */ - esp8266_net_init(); - - /* set esp8266 AT Socket options */ - at_socket_device_register(&esp8266_socket_ops); - - return RT_EOK; -} -INIT_APP_EXPORT(at_socket_device_init); - -#endif /* AT_DEVICE_ESP8266 */ diff --git a/at_socket_m26.c b/at_socket_m26.c deleted file mode 100644 index e979712..0000000 --- a/at_socket_m26.c +++ /dev/null @@ -1,1273 +0,0 @@ -/* - * File : at_socket_m26.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-12 chenyong first version - */ - -#include -#include - -#include -#include - -#include -#include - -#if !defined(RT_USING_NETDEV) -#error "This RT-Thread version is older, please check and updata laster RT-Thread!" -#else -#include -#include -#endif /* RT_USING_NETDEV */ - -#if !defined(AT_SW_VERSION_NUM) || AT_SW_VERSION_NUM < 0x10200 -#error "This AT Client version is older, please check and update latest AT Client!" -#endif - -#define LOG_TAG "at.m26|mc20" -#include - -#ifdef AT_DEVICE_M26 - -#define M26_NETDEV_NAME "m26" - -#define M26_MODULE_SEND_MAX_SIZE 1460 -#define M26_WAIT_CONNECT_TIME 5000 -#define M26_THREAD_STACK_SIZE 1024 -#define M26_THREAD_PRIORITY (RT_THREAD_PRIORITY_MAX/2) - -/* set real event by current socket and current state */ -#define SET_EVENT(socket, event) (((socket + 1) << 16) | (event)) - -/* AT socket event type */ -#define M26_EVENT_CONN_OK (1L << 0) -#define M26_EVENT_SEND_OK (1L << 1) -#define M26_EVENT_RECV_OK (1L << 2) -#define M26_EVNET_CLOSE_OK (1L << 3) -#define M26_EVENT_CONN_FAIL (1L << 4) -#define M26_EVENT_SEND_FAIL (1L << 5) - -static int cur_socket; -static rt_event_t at_socket_event; -static rt_mutex_t at_event_lock; -static at_evt_cb_t at_evt_cb_set[] = { - [AT_SOCKET_EVT_RECV] = NULL, - [AT_SOCKET_EVT_CLOSED] = NULL, -}; - -static int at_socket_event_send(uint32_t event) -{ - return (int) rt_event_send(at_socket_event, event); -} - -static int at_socket_event_recv(uint32_t event, uint32_t timeout, rt_uint8_t option) -{ - int result = 0; - rt_uint32_t recved; - - result = rt_event_recv(at_socket_event, event, option | RT_EVENT_FLAG_CLEAR, timeout, &recved); - if (result != RT_EOK) - { - return -RT_ETIMEOUT; - } - - return recved; -} - -/** - * close socket by AT commands. - * - * @param current socket - * - * @return 0: close socket success - * -1: send AT commands error - * -2: wait socket event timeout - * -5: no memory - */ -static int m26_socket_close(int socket) -{ - int result = 0; - at_response_t resp = RT_NULL; - - resp = at_create_resp(128, 0, RT_TICK_PER_SECOND); - if (!resp) - { - LOG_E("No memory for response structure!"); - return -RT_ENOMEM; - } - - rt_mutex_take(at_event_lock, RT_WAITING_FOREVER); - cur_socket = socket; - - /* Clear socket close event */ - at_socket_event_recv(SET_EVENT(socket, M26_EVNET_CLOSE_OK), 0, RT_EVENT_FLAG_OR); - - if (at_exec_cmd(resp, "AT+QICLOSE=%d", socket) < 0) - { - result = -RT_ERROR; - goto __exit; - } - - if (at_socket_event_recv(SET_EVENT(socket, M26_EVNET_CLOSE_OK), rt_tick_from_millisecond(300*3), RT_EVENT_FLAG_AND) < 0) - { - LOG_E("socket (%d) close failed, wait close OK timeout.", socket); - result = -RT_ETIMEOUT; - goto __exit; - } - -__exit: - rt_mutex_release(at_event_lock); - - if (resp) - { - at_delete_resp(resp); - } - - return result; -} - - -/** - * create TCP/UDP client or server connect by AT commands. - * - * @param socket current socket - * @param ip server or client IP address - * @param port server or client port - * @param type connect socket type(tcp, udp) - * @param is_client connection is client - * - * @return 0: connect success - * -1: connect failed, send commands error or type error - * -2: wait socket event timeout - * -5: no memory - */ -static int m26_socket_connect(int socket, char *ip, int32_t port, enum at_socket_type type, rt_bool_t is_client) -{ - int result = 0, event_result = 0; - rt_bool_t retryed = RT_FALSE; - at_response_t resp = RT_NULL; - - RT_ASSERT(ip); - RT_ASSERT(port >= 0); - - resp = at_create_resp(128, 0, 5 * RT_TICK_PER_SECOND); - if (!resp) - { - LOG_E("No memory for response structure!"); - return -RT_ENOMEM; - } - - /* lock AT socket connect */ - rt_mutex_take(at_event_lock, RT_WAITING_FOREVER); - -__retry: - - /* Clear socket connect event */ - at_socket_event_recv(SET_EVENT(socket, M26_EVENT_CONN_OK | M26_EVENT_CONN_FAIL), 0, RT_EVENT_FLAG_OR); - - if (is_client) - { - switch (type) - { - case AT_SOCKET_TCP: - /* send AT commands(eg: AT+QIOPEN=0,"TCP","x.x.x.x", 1234) to connect TCP server */ - if (at_exec_cmd(resp, "AT+QIOPEN=%d,\"TCP\",\"%s\",%d", socket, ip, port) < 0) - { - result = -RT_ERROR; - goto __exit; - } - break; - - case AT_SOCKET_UDP: - if (at_exec_cmd(resp, "AT+QIOPEN=%d,\"UDP\",\"%s\",%d", socket, ip, port) < 0) - { - result = -RT_ERROR; - goto __exit; - } - break; - - default: - LOG_E("Not supported connect type : %d.", type); - return -RT_ERROR; - } - } - - /* waiting result event from AT URC, the device default connection timeout is 75 seconds, but it set to 10 seconds is convenient to use.*/ - if (at_socket_event_recv(SET_EVENT(socket, 0), 10 * RT_TICK_PER_SECOND, RT_EVENT_FLAG_OR) < 0) - { - LOG_E("socket (%d) connect failed, wait connect result timeout.", socket); - result = -RT_ETIMEOUT; - goto __exit; - } - /* waiting OK or failed result */ - if ((event_result = at_socket_event_recv(M26_EVENT_CONN_OK | M26_EVENT_CONN_FAIL, 1 * RT_TICK_PER_SECOND, - RT_EVENT_FLAG_OR)) < 0) - { - LOG_E("socket (%d) connect failed, wait connect OK|FAIL timeout.", socket); - result = -RT_ETIMEOUT; - goto __exit; - } - /* check result */ - if (event_result & M26_EVENT_CONN_FAIL) - { - if (!retryed) - { - LOG_E("socket (%d) connect failed, maybe the socket was not be closed at the last time and now will retry.", socket); - if (m26_socket_close(socket) < 0) - { - goto __exit; - } - retryed = RT_TRUE; - goto __retry; - } - LOG_E("socket (%d) connect failed, failed to establish a connection.", socket); - result = -RT_ERROR; - goto __exit; - } - -__exit: - /* unlock AT socket connect */ - rt_mutex_release(at_event_lock); - - if (resp) - { - at_delete_resp(resp); - } - - return result; -} - -static int at_get_send_size(int socket, size_t *size, size_t *acked, size_t *nacked) -{ - at_response_t resp = at_create_resp(64, 0, 5 * RT_TICK_PER_SECOND); - int result = 0; - - if (!resp) - { - LOG_E("No memory for response structure!"); - result = -RT_ENOMEM; - goto __exit; - } - - if (at_exec_cmd(resp, "AT+QISACK=%d", socket) < 0) - { - result = -RT_ERROR; - goto __exit; - } - - if (at_resp_parse_line_args_by_kw(resp, "+QISACK:", "+QISACK: %d, %d, %d", size, acked, nacked) <= 0) - { - result = -RT_ERROR; - goto __exit; - } - -__exit: - if (resp) - { - at_delete_resp(resp); - } - - return result; -} - -static int at_wait_send_finish(int socket, size_t settings_size) -{ - /* get the timeout by the input data size */ - rt_tick_t timeout = rt_tick_from_millisecond(settings_size); - rt_tick_t last_time = rt_tick_get(); - size_t size = 0, acked = 0, nacked = 0xFFFF; - - while (rt_tick_get() - last_time <= timeout) - { - at_get_send_size(socket, &size, &acked, &nacked); - if (nacked == 0) - { - return RT_EOK; - } - rt_thread_delay(rt_tick_from_millisecond(50)); - } - - return -RT_ETIMEOUT; -} - -/** - * send data to server or client by AT commands. - * - * @param socket current socket - * @param buff send buffer - * @param bfsz send buffer size - * @param type connect socket type(tcp, udp) - * - * @return >=0: the size of send success - * -1: send AT commands error or send data error - * -2: waited socket event timeout - * -5: no memory - */ -static int m26_socket_send(int socket, const char *buff, size_t bfsz, enum at_socket_type type) -{ - int result = 0, event_result = 0; - at_response_t resp = RT_NULL; - size_t cur_pkt_size = 0, sent_size = 0; - - RT_ASSERT(buff); - - resp = at_create_resp(128, 2, 5 * RT_TICK_PER_SECOND); - if (!resp) - { - LOG_E("No memory for response structure!"); - return -RT_ENOMEM; - } - - rt_mutex_take(at_event_lock, RT_WAITING_FOREVER); - - /* Clear socket send event */ - at_socket_event_recv(SET_EVENT(socket, M26_EVENT_SEND_OK | M26_EVENT_SEND_FAIL), 0, RT_EVENT_FLAG_OR); - - /* set current socket for send URC event */ - cur_socket = socket; - /* set AT client end sign to deal with '>' sign.*/ - at_set_end_sign('>'); - - while (sent_size < bfsz) - { - if (bfsz - sent_size < M26_MODULE_SEND_MAX_SIZE) - { - cur_pkt_size = bfsz - sent_size; - } - else - { - cur_pkt_size = M26_MODULE_SEND_MAX_SIZE; - } - - /* send the "AT+QISEND" commands to AT server than receive the '>' response on the first line. */ - if (at_exec_cmd(resp, "AT+QISEND=%d,%d", socket, cur_pkt_size) < 0) - { - result = -RT_ERROR; - goto __exit; - } - - /* send the real data to server or client */ - result = (int) at_client_send(buff + sent_size, cur_pkt_size); - if (result == 0) - { - result = -RT_ERROR; - goto __exit; - } - - /* waiting result event from AT URC */ - if (at_socket_event_recv(SET_EVENT(socket, 0), 1 * RT_TICK_PER_SECOND, RT_EVENT_FLAG_OR) < 0) - { - LOG_E("socket (%d) send failed, wait connect result timeout.", socket); - result = -RT_ETIMEOUT; - goto __exit; - } - /* waiting OK or failed result */ - if ((event_result = at_socket_event_recv(M26_EVENT_SEND_OK | M26_EVENT_SEND_FAIL, 1 * RT_TICK_PER_SECOND, - RT_EVENT_FLAG_OR)) < 0) - { - LOG_E("socket (%d) send failed, wait connect OK|FAIL timeout.", socket); - result = -RT_ETIMEOUT; - goto __exit; - } - /* check result */ - if (event_result & M26_EVENT_SEND_FAIL) - { - LOG_E("socket (%d) send failed, return failed.", socket); - result = -RT_ERROR; - goto __exit; - } - - if (type == AT_SOCKET_TCP) - { - at_wait_send_finish(socket, cur_pkt_size); - } - - sent_size += cur_pkt_size; - } - - -__exit: - /* reset the end sign for data conflict */ - at_set_end_sign(0); - - rt_mutex_release(at_event_lock); - - if (resp) - { - at_delete_resp(resp); - } - - return result; -} - -/** - * domain resolve by AT commands. - * - * @param name domain name - * @param ip parsed IP address, it's length must be 16 - * - * @return 0: domain resolve success - * -1: send AT commands error or response error - * -2: wait socket event timeout - * -5: no memory - */ -static int m26_domain_resolve(const char *name, char ip[16]) -{ -#define RESOLVE_RETRY 5 - - int i, result = RT_EOK; - char recv_ip[16] = { 0 }; - at_response_t resp = RT_NULL; - - RT_ASSERT(name); - RT_ASSERT(ip); - - /* The maximum response time is 14 seconds, affected by network status */ - resp = at_create_resp(128, 4, 14 * RT_TICK_PER_SECOND); - if (!resp) - { - LOG_E("No memory for response structure!"); - return -RT_ENOMEM; - } - - rt_mutex_take(at_event_lock, RT_WAITING_FOREVER); - - for(i = 0; i < RESOLVE_RETRY; i++) - { - if (at_exec_cmd(resp, "AT+QIDNSGIP=\"%s\"", name) < 0) - { - result = -RT_ERROR; - goto __exit; - } - - /* parse the third line of response data, get the IP address */ - if(at_resp_parse_line_args_by_kw(resp, ".", "%s", recv_ip) < 0) - { - rt_thread_mdelay(100); - /* resolve failed, maybe receive an URC CRLF */ - continue; - } - - if (strlen(recv_ip) < 8) - { - rt_thread_mdelay(100); - /* resolve failed, maybe receive an URC CRLF */ - continue; - } - else - { - strncpy(ip, recv_ip, 15); - ip[15] = '\0'; - break; - } - } - -__exit: - rt_mutex_release(at_event_lock); - - if (resp) - { - at_delete_resp(resp); - } - - return result; - -} - -/** - * set AT socket event notice callback - * - * @param event notice event - * @param cb notice callback - */ -static void m26_socket_set_event_cb(at_socket_evt_t event, at_evt_cb_t cb) -{ - if (event < sizeof(at_evt_cb_set) / sizeof(at_evt_cb_set[1])) - { - at_evt_cb_set[event] = cb; - } -} - -static void urc_connect_func(const char *data, rt_size_t size) -{ - int socket = 0; - - RT_ASSERT(data && size); - - sscanf(data, "%d%*[^0-9]", &socket); - - if (strstr(data, "CONNECT OK")) - { - at_socket_event_send(SET_EVENT(socket, M26_EVENT_CONN_OK)); - } - else - { - at_socket_event_send(SET_EVENT(socket, M26_EVENT_CONN_FAIL)); - } -} - -static void urc_send_func(const char *data, rt_size_t size) -{ - RT_ASSERT(data && size); - - if (strstr(data, "SEND OK")) - { - at_socket_event_send(SET_EVENT(cur_socket, M26_EVENT_SEND_OK)); - } - else if (strstr(data, "SEND FAIL")) - { - at_socket_event_send(SET_EVENT(cur_socket, M26_EVENT_SEND_FAIL)); - } -} - -static void urc_close_func(const char *data, rt_size_t size) -{ - int socket = 0; - - RT_ASSERT(data && size); - - if (strstr(data, "CLOSE OK")) - { - at_socket_event_send(SET_EVENT(cur_socket, M26_EVNET_CLOSE_OK)); - } - else if (strstr(data, "CLOSED")) - { - sscanf(data, "%d, CLOSED", &socket); - /* notice the socket is disconnect by remote */ - if (at_evt_cb_set[AT_SOCKET_EVT_CLOSED]) - { - at_evt_cb_set[AT_SOCKET_EVT_CLOSED](socket, AT_SOCKET_EVT_CLOSED, NULL, 0); - } - } -} - -static void urc_recv_func(const char *data, rt_size_t size) -{ - int socket = 0; - rt_size_t bfsz = 0, temp_size = 0; - rt_int32_t timeout; - char *recv_buf = RT_NULL, temp[8]; - - RT_ASSERT(data && size); - - /* get the current socket and receive buffer size by receive data */ - sscanf(data, "+RECEIVE: %d, %d", &socket, (int *) &bfsz); - /* get receive timeout by receive buffer length */ - timeout = bfsz; - - if (socket < 0 || bfsz == 0) - return; - - recv_buf = rt_calloc(1, bfsz); - if (!recv_buf) - { - LOG_E("no memory for URC receive buffer (%d)!", bfsz); - /* read and clean the coming data */ - while (temp_size < bfsz) - { - if (bfsz - temp_size > sizeof(temp)) - { - at_client_recv(temp, sizeof(temp), timeout); - } - else - { - at_client_recv(temp, bfsz - temp_size, timeout); - } - temp_size += sizeof(temp); - } - return; - } - - /* sync receive data */ - if (at_client_recv(recv_buf, bfsz, timeout) != bfsz) - { - LOG_E("receive size(%d) data failed!", bfsz); - rt_free(recv_buf); - return; - } - - /* notice the receive buffer and buffer size */ - if (at_evt_cb_set[AT_SOCKET_EVT_RECV]) - { - at_evt_cb_set[AT_SOCKET_EVT_RECV](socket, AT_SOCKET_EVT_RECV, recv_buf, bfsz); - } -} - -static void urc_func(const char *data, rt_size_t size) -{ - RT_ASSERT(data); - - LOG_I("URC data : %.*s", size, data); -} - -static const struct at_urc urc_table[] = { - {"RING", "\r\n", urc_func}, - {"Call Ready", "\r\n", urc_func}, - {"RDY", "\r\n", urc_func}, - {"NO CARRIER", "\r\n", urc_func}, - {"", ", CONNECT OK\r\n", urc_connect_func}, - {"", ", CONNECT FAIL\r\n", urc_connect_func}, - {"SEND OK", "\r\n", urc_send_func}, - {"SEND FAIL", "\r\n", urc_send_func}, - {"", ", CLOSE OK\r\n", urc_close_func}, - {"", ", CLOSED\r\n", urc_close_func}, - {"+RECEIVE:", "\r\n", urc_recv_func}, -}; - -#define AT_SEND_CMD(resp, resp_line, timeout, cmd) \ - do \ - { \ - if (at_exec_cmd(at_resp_set_info(resp, 128, resp_line, rt_tick_from_millisecond(timeout)), cmd) < 0) \ - { \ - result = -RT_ERROR; \ - goto __exit; \ - } \ - } while(0); \ - -static int m26_netdev_set_info(struct netdev *netdev); -static int m26_netdev_check_link_status(struct netdev *netdev); - -/* init for M26 or MC20 */ -static void m26_init_thread_entry(void *parameter) -{ -#define CPIN_RETRY 10 -#define CSQ_RETRY 10 -#define CREG_RETRY 10 -#define CGREG_RETRY 20 - - at_response_t resp = RT_NULL; - int i, qimux, qimode; - char parsed_data[10]; - rt_err_t result = RT_EOK; - - resp = at_create_resp(128, 0, rt_tick_from_millisecond(300)); - if (!resp) - { - LOG_E("No memory for response structure!"); - result = -RT_ENOMEM; - goto __exit; - } - LOG_D("Start initializing the M26/MC20 module"); - /* wait M26 startup finish */ - if (at_client_wait_connect(M26_WAIT_CONNECT_TIME)) - { - LOG_E("AT device connection error."); - result = -RT_ETIMEOUT; - goto __exit; - } - /* disable echo */ - AT_SEND_CMD(resp, 0, 300, "ATE0"); - /* get module version */ - AT_SEND_CMD(resp, 0, 300, "ATI"); - /* show module version */ - for (i = 0; i < (int) resp->line_counts - 1; i++) - { - LOG_D("%s", at_resp_get_line(resp, i + 1)); - } - /* check SIM card */ - for (i = 0; i < CPIN_RETRY; i++) - { - at_exec_cmd(at_resp_set_info(resp, 128, 2, 5 * RT_TICK_PER_SECOND), "AT+CPIN?"); - - if (at_resp_get_line_by_kw(resp, "READY")) - { - LOG_D("SIM card detection success"); - break; - } - rt_thread_mdelay(1000); - } - if (i == CPIN_RETRY) - { - LOG_E("SIM card detection failed!"); - result = -RT_ERROR; - goto __exit; - } - /* waiting for dirty data to be digested */ - rt_thread_mdelay(10); - /* check signal strength */ - for (i = 0; i < CSQ_RETRY; i++) - { - AT_SEND_CMD(resp, 0, 300, "AT+CSQ"); - at_resp_parse_line_args_by_kw(resp, "+CSQ:", "+CSQ: %s", &parsed_data); - if (strncmp(parsed_data, "99,99", sizeof(parsed_data))) - { - LOG_D("Signal strength: %s", parsed_data); - break; - } - rt_thread_mdelay(1000); - } - if (i == CSQ_RETRY) - { - LOG_E("Signal strength check failed (%s)", parsed_data); - result = -RT_ERROR; - goto __exit; - } - /* check the GSM network is registered */ - for (i = 0; i < CREG_RETRY; i++) - { - AT_SEND_CMD(resp, 0, 300, "AT+CREG?"); - at_resp_parse_line_args_by_kw(resp, "+CREG:", "+CREG: %s", &parsed_data); - if (!strncmp(parsed_data, "0,1", sizeof(parsed_data)) || !strncmp(parsed_data, "0,5", sizeof(parsed_data))) - { - LOG_D("GSM network is registered (%s)", parsed_data); - break; - } - rt_thread_mdelay(1000); - } - if (i == CREG_RETRY) - { - LOG_E("The GSM network is register failed (%s)", parsed_data); - result = -RT_ERROR; - goto __exit; - } - /* check the GPRS network is registered */ - for (i = 0; i < CGREG_RETRY; i++) - { - AT_SEND_CMD(resp, 0, 300, "AT+CGREG?"); - at_resp_parse_line_args_by_kw(resp, "+CGREG:", "+CGREG: %s", &parsed_data); - if (!strncmp(parsed_data, "0,1", sizeof(parsed_data)) || !strncmp(parsed_data, "0,5", sizeof(parsed_data))) - { - LOG_D("GPRS network is registered (%s)", parsed_data); - break; - } - rt_thread_mdelay(1000); - } - if (i == CGREG_RETRY) - { - LOG_E("The GPRS network is register failed (%s)", parsed_data); - result = -RT_ERROR; - goto __exit; - } - - AT_SEND_CMD(resp, 0, 300, "AT+QIFGCNT=0"); - AT_SEND_CMD(resp, 0, 300, "AT+QICSGP=1, \"CMNET\""); - AT_SEND_CMD(resp, 0, 300, "AT+QIMODE?"); - - at_resp_parse_line_args_by_kw(resp, "+QIMODE:", "+QIMODE: %d", &qimode); - if (qimode == 1) - { - AT_SEND_CMD(resp, 0, 300, "AT+QIMODE=0"); - } - - /* the device default response timeout is 40 seconds, but it set to 15 seconds is convenient to use. */ - AT_SEND_CMD(resp, 2, 20 * 1000, "AT+QIDEACT"); - - /* Set to multiple connections */ - AT_SEND_CMD(resp, 0, 300, "AT+QIMUX?"); - at_resp_parse_line_args_by_kw(resp, "+QIMUX:", "+QIMUX: %d", &qimux); - if (qimux == 0) - { - AT_SEND_CMD(resp, 0, 300, "AT+QIMUX=1"); - } - - AT_SEND_CMD(resp, 0, 300, "AT+QIREGAPP"); - - /* the device default response timeout is 150 seconds, but it set to 20 seconds is convenient to use. */ - AT_SEND_CMD(resp, 0, 20 * 1000, "AT+QIACT"); - - AT_SEND_CMD(resp, 2, 300, "AT+QILOCIP"); - -__exit: - if (resp) - { - at_delete_resp(resp); - } - - if (!result) - { - m26_netdev_set_info(netdev_get_by_name(M26_NETDEV_NAME)); - m26_netdev_check_link_status(netdev_get_by_name(M26_NETDEV_NAME)); - - LOG_I("AT network initialize success!"); - } - else - { - LOG_E("AT network initialize failed (%d)!", result); - } -} - -int m26_net_init(void) -{ -#ifdef PKG_AT_INIT_BY_THREAD - rt_thread_t tid; - - tid = rt_thread_create("m26_net_init", m26_init_thread_entry, RT_NULL, M26_THREAD_STACK_SIZE, M26_THREAD_PRIORITY, 20); - if (tid) - { - rt_thread_startup(tid); - } - else - { - LOG_E("Create AT initialization thread fail!"); - } -#else - m26_init_thread_entry(RT_NULL); -#endif - - return RT_EOK; -} - -#ifdef FINSH_USING_MSH -#include -MSH_CMD_EXPORT_ALIAS(m26_net_init, at_net_init, initialize AT network); -#endif - -static const struct at_device_ops m26_socket_ops = { - m26_socket_connect, - m26_socket_close, - m26_socket_send, - m26_domain_resolve, - m26_socket_set_event_cb, -}; - - -/* set m26 network interface device status and address information */ -static int m26_netdev_set_info(struct netdev *netdev) -{ -#define M26_IEMI_RESP_SIZE 32 -#define M26_IPADDR_RESP_SIZE 32 -#define M26_DNS_RESP_SIZE 96 -#define M26_INFO_RESP_TIMO rt_tick_from_millisecond(300) - - int result = RT_EOK; - at_response_t resp = RT_NULL; - ip_addr_t addr; - - if (netdev == RT_NULL) - { - LOG_E("Input network interface device is NULL.\n"); - return -RT_ERROR; - } - - rt_mutex_take(at_event_lock, RT_WAITING_FOREVER); - - /* set network interface device up status */ - netdev_low_level_set_status(netdev, RT_TRUE); - netdev_low_level_set_dhcp_status(netdev, RT_TRUE); - - resp = at_create_resp(M26_IEMI_RESP_SIZE, 0, M26_INFO_RESP_TIMO); - if (resp == RT_NULL) - { - LOG_E("M26 set IP address failed, no memory for response object."); - result = -RT_ENOMEM; - goto __exit; - } - - /* set network interface device hardware address(IEMI) */ - { - #define M26_NETDEV_HWADDR_LEN 8 - #define M26_IEMI_LEN 15 - - char iemi[M26_IEMI_LEN] = {0}; - int i = 0, j = 0; - - /* send "AT+GSN" commond to get device IEMI */ - if (at_exec_cmd(resp, "AT+GSN") < 0) - { - result = -RT_ERROR; - goto __exit; - } - - if (at_resp_parse_line_args(resp, 2, "%s", iemi) <= 0) - { - LOG_E("Prase \"AT+GSN\" commands resposne data error!"); - result = -RT_ERROR; - goto __exit; - } - - LOG_D("M26 IEMI number: %s", iemi); - - netdev->hwaddr_len = M26_NETDEV_HWADDR_LEN; - /* get hardware address by IEMI */ - for (i = 0, j = 0; i < M26_NETDEV_HWADDR_LEN && j < M26_IEMI_LEN; i++, j+=2) - { - if (j != M26_IEMI_LEN - 1) - { - netdev->hwaddr[i] = (iemi[j] - '0') * 10 + (iemi[j + 1] - '0'); - } - else - { - netdev->hwaddr[i] = (iemi[j] - '0'); - } - } - } - - /* set network interface device IP address */ - { - #define IP_ADDR_SIZE_MAX 16 - char ipaddr[IP_ADDR_SIZE_MAX] = {0}; - - at_resp_set_info(resp, M26_IPADDR_RESP_SIZE, 2, M26_INFO_RESP_TIMO); - - /* send "AT+QILOCIP" commond to get IP address */ - if (at_exec_cmd(resp, "AT+QILOCIP") < 0) - { - result = -RT_ERROR; - goto __exit; - } - - if (at_resp_parse_line_args_by_kw(resp, ".", "%s", ipaddr) <= 0) - { - LOG_E("Prase \"AT+QILOCIP\" commands resposne data error!"); - result = -RT_ERROR; - goto __exit; - } - - LOG_D("M26 IP address: %s", ipaddr); - - /* set network interface address information */ - inet_aton(ipaddr, &addr); - netdev_low_level_set_ipaddr(netdev, &addr); - } - - /* set network interface device dns server */ - { - #define DNS_ADDR_SIZE_MAX 16 - char dns_server1[DNS_ADDR_SIZE_MAX] = {0}, dns_server2[DNS_ADDR_SIZE_MAX] = {0}; - - at_resp_set_info(resp, M26_DNS_RESP_SIZE, 0, M26_INFO_RESP_TIMO); - - /* send "AT+QIDNSCFG?" commond to get DNS servers address */ - if (at_exec_cmd(resp, "AT+QIDNSCFG?") < 0) - { - result = -RT_ERROR; - goto __exit; - } - - if (at_resp_parse_line_args_by_kw(resp, "PrimaryDns:", "PrimaryDns:%s", dns_server1) <= 0 || - at_resp_parse_line_args_by_kw(resp, "SecondaryDns:", "SecondaryDns:%s", dns_server2) <= 0) - { - LOG_E("Prase \"AT+QIDNSCFG?\" commands resposne data error!"); - result = -RT_ERROR; - goto __exit; - } - - LOG_D("M26 primary DNS server address: %s", dns_server1); - LOG_D("M26 secondary DNS server address: %s", dns_server2); - - inet_aton(dns_server1, &addr); - netdev_low_level_set_dns_server(netdev, 0, &addr); - - inet_aton(dns_server2, &addr); - netdev_low_level_set_dns_server(netdev, 1, &addr); - } - -__exit: - if (resp) - { - at_delete_resp(resp); - } - - rt_mutex_release(at_event_lock); - - return result; -} - -static void check_link_status_entry(void *parameter) -{ -#define M26_LINK_STATUS_OK 0 -#define M26_LINK_RESP_SIZE 64 -#define M26_LINK_RESP_TIMO (3 * RT_TICK_PER_SECOND) -#define M26_LINK_DELAY_TIME (30 * RT_TICK_PER_SECOND) - - struct netdev *netdev = (struct netdev *)parameter; - at_response_t resp = RT_NULL; - int link_status; - - resp = at_create_resp(M26_LINK_RESP_SIZE, 0, M26_LINK_RESP_TIMO); - if (resp == RT_NULL) - { - LOG_E("m26 set check link status failed, no memory for response object."); - return; - } - - while (1) - { - - rt_mutex_take(at_event_lock, RT_WAITING_FOREVER); - - /* send "AT+QNSTATUS" commond to check netweork interface device link status */ - if (at_exec_cmd(resp, "AT+QNSTATUS") < 0) - { - rt_mutex_release(at_event_lock); - rt_thread_mdelay(M26_LINK_DELAY_TIME); - - continue; - } - - link_status = -1; - at_resp_parse_line_args_by_kw(resp, "+QNSTATUS:", "+QNSTATUS: %d", &link_status); - - /* check the network interface device link status */ - if ((M26_LINK_STATUS_OK == link_status) != netdev_is_link_up(netdev)) - { - netdev_low_level_set_link_status(netdev, (M26_LINK_STATUS_OK == link_status)); - } - - rt_mutex_release(at_event_lock); - rt_thread_mdelay(M26_LINK_DELAY_TIME); - } -} - -static int m26_netdev_check_link_status(struct netdev *netdev) -{ -#define M26_LINK_THREAD_STACK_SIZE 512 -#define M26_LINK_THREAD_PRIORITY (RT_THREAD_PRIORITY_MAX - 2) -#define M26_LINK_THREAD_TICK 20 - - rt_thread_t tid; - char tname[RT_NAME_MAX]; - - if (netdev == RT_NULL) - { - LOG_E("Input network interface device is NULL.\n"); - return -RT_ERROR; - } - - rt_memset(tname, 0x00, sizeof(tname)); - rt_snprintf(tname, RT_NAME_MAX, "%s_link", netdev->name); - - tid = rt_thread_create(tname, check_link_status_entry, (void *)netdev, - M26_LINK_THREAD_STACK_SIZE, M26_LINK_THREAD_PRIORITY, M26_LINK_THREAD_TICK); - if (tid) - { - rt_thread_startup(tid); - } - - return RT_EOK; -} - -static int m26_netdev_set_up(struct netdev *netdev) -{ - netdev_low_level_set_status(netdev, RT_TRUE); - LOG_D("The network interface device(%s) set up status", netdev->name); - - return RT_EOK; -} - -static int m26_netdev_set_down(struct netdev *netdev) -{ - netdev_low_level_set_status(netdev, RT_FALSE); - LOG_D("The network interface device(%s) set down status", netdev->name); - return RT_EOK; -} - -static int m26_netdev_set_dns_server(struct netdev *netdev, uint8_t dns_num, ip_addr_t *dns_server) -{ -#define M26_DNS_RESP_LEN 8 -#define M26_DNS_RESP_TIMEO rt_tick_from_millisecond(300) - - at_response_t resp = RT_NULL; - int result = RT_EOK; - - RT_ASSERT(netdev); - RT_ASSERT(dns_server); - - resp = at_create_resp(M26_DNS_RESP_LEN, 0, M26_DNS_RESP_TIMEO); - if (resp == RT_NULL) - { - LOG_D("m26 set dns server failed, no memory for response object."); - return -RT_ENOMEM; - } - - rt_mutex_take(at_event_lock, RT_WAITING_FOREVER); - - /* send "AT+QIDNSCFG=[,]" commond to set dns servers */ - if (at_exec_cmd(resp, "AT+QIDNSCFG=\"%s\"", inet_ntoa(*dns_server)) < 0) - { - result = -RT_ERROR; - goto __exit; - } - - netdev_low_level_set_dns_server(netdev, dns_num, dns_server); - -__exit: - if (resp) - { - at_delete_resp(resp); - } - - rt_mutex_release(at_event_lock); - - return result; -} - -static int m26_netdev_ping(struct netdev *netdev, const char *host, size_t data_len, uint32_t timeout, struct netdev_ping_resp *ping_resp) -{ -#define M26_PING_RESP_SIZE 128 -#define M26_PING_IP_SIZE 16 -#define M26_PING_TIMEO (5 * RT_TICK_PER_SECOND) - - int result = RT_EOK; - at_response_t resp = RT_NULL; - char ip_addr[M26_PING_IP_SIZE] = {0}; - int response, recv_data_len, time, ttl; - - RT_ASSERT(netdev); - RT_ASSERT(host); - RT_ASSERT(ping_resp); - - resp = at_create_resp(M26_PING_RESP_SIZE, 5, M26_PING_TIMEO); - if (resp == RT_NULL) - { - LOG_D("m26 set dns server failed, no memory for response object."); - return -RT_ENOMEM; - } - - rt_mutex_take(at_event_lock, RT_WAITING_FOREVER); - - /* send "AT+QPING=""[,[][,]]" commond to send ping request */ - if (at_exec_cmd(resp, "AT+QPING=\"%s\",%d,1", host, M26_PING_TIMEO / RT_TICK_PER_SECOND) < 0) - { - result = -RT_ERROR; - goto __exit; - } - - at_resp_parse_line_args_by_kw(resp, "+QPING:","+QPING:%d", &response); - /* Received the ping response from the server */ - if (response == 0) - { - if (at_resp_parse_line_args_by_kw(resp, "+QPING:", "+QPING:%d,%[^,],%d,%d,%d", - &response, ip_addr, &recv_data_len, &time, &ttl) <= 0) - { - LOG_D("Prase \"AT+QPING\" commands resposne data error!"); - result = -RT_ERROR; - goto __exit; - } - } - - - /* prase response number */ - switch (response) - { - case 0: - inet_aton(ip_addr, &(ping_resp->ip_addr)); - ping_resp->data_len = recv_data_len; - ping_resp->ticks = time; - ping_resp->ttl = ttl; - result = RT_EOK; - break; - case 1: - result = -RT_ETIMEOUT; - break; - default: - result = -RT_ERROR; - break; - } - - __exit: - if (resp) - { - at_delete_resp(resp); - } - - rt_mutex_release(at_event_lock); - - return result; -} - -void m26_netdev_netstat(struct netdev *netdev) -{ - // TODO netstat support -} - -const struct netdev_ops m26_netdev_ops = -{ - m26_netdev_set_up, - m26_netdev_set_down, - - RT_NULL, /* not support set ip, netmask, gatway address */ - m26_netdev_set_dns_server, - RT_NULL, /* not support set DHCP status */ - - m26_netdev_ping, - m26_netdev_netstat, -}; - -static int m26_netdev_add(const char *netdev_name) -{ -#define M26_NETDEV_MTU 1500 - - struct netdev *netdev = RT_NULL; - - RT_ASSERT(netdev_name); - - netdev = (struct netdev *) rt_calloc(1, sizeof(struct netdev)); - if (netdev == RT_NULL) - { - return RT_NULL; - } - - netdev->mtu = M26_NETDEV_MTU; - netdev->ops = &m26_netdev_ops; - -#ifdef SAL_USING_AT - extern int sal_at_netdev_set_pf_info(struct netdev *netdev); - /* set the network interface socket/netdb operations */ - sal_at_netdev_set_pf_info(netdev); -#endif - - return netdev_register(netdev, netdev_name, RT_NULL); -} - -static int at_socket_device_init(void) -{ - /* create current AT socket event */ - at_socket_event = rt_event_create("at_se", RT_IPC_FLAG_FIFO); - if (at_socket_event == RT_NULL) - { - LOG_E("AT client port initialize failed! at_sock_event create failed!"); - return -RT_ENOMEM; - } - - /* create current AT socket event lock */ - at_event_lock = rt_mutex_create("at_se", RT_IPC_FLAG_FIFO); - if (at_event_lock == RT_NULL) - { - LOG_E("AT client port initialize failed! at_sock_lock create failed!"); - rt_event_delete(at_socket_event); - return -RT_ENOMEM; - } - - /* initialize AT client */ - at_client_init(AT_DEVICE_NAME, AT_DEVICE_RECV_BUFF_LEN); - - /* register URC data execution function */ - at_set_urc_table(urc_table, sizeof(urc_table) / sizeof(urc_table[0])); - - /* add network interface device to netdev list */ - if (m26_netdev_add(M26_NETDEV_NAME) < 0) - { - LOG_E("M26 network interface device(%d) add failed.", M26_NETDEV_NAME); - return -RT_ENOMEM; - } - - /* initialize m26 network */ - m26_net_init(); - - /* set m26 AT Socket options */ - at_socket_device_register(&m26_socket_ops); - - return RT_EOK; -} -INIT_APP_EXPORT(at_socket_device_init); - -#endif /* AT_DEVICE_M26 */ diff --git a/at_socket_rw007.c b/at_socket_rw007.c deleted file mode 100644 index 145a983..0000000 --- a/at_socket_rw007.c +++ /dev/null @@ -1,773 +0,0 @@ -/* - * File : at_socket_rw007.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-20 chenyong first version - */ - -#include -#include - -#include -#include - -#include -#include - -#if !defined(RT_USING_NETDEV) -#error "This RT-Thread version is older, please check and updata laster RT-Thread!" -#else -#include -#include -#endif /* RT_USING_NETDEV */ - -#if !defined(AT_SW_VERSION_NUM) || AT_SW_VERSION_NUM < 0x10200 -#error "This AT Client version is older, please check and update latest AT Client!" -#endif - -#define LOG_TAG "at.rw007" -#include - -#ifdef AT_DEVICE_RW007 - -#define RW007_NETDEV_NAME "rw007" - -#define RW007_MODULE_SEND_MAX_SIZE 2048 -#define RW007_WAIT_CONNECT_TIME 5000 -#define RW007_THREAD_STACK_SIZE 1024 -#define RW007_THREAD_PRIORITY (RT_THREAD_PRIORITY_MAX/2) - -/* set real event by current socket and current state */ -#define SET_EVENT(socket, event) (((socket + 1) << 16) | (event)) - -/* AT socket event type */ -#define RW007_EVENT_CONN_OK (1L << 0) -#define RW007_EVENT_SEND_OK (1L << 1) -#define RW007_EVENT_RECV_OK (1L << 2) -#define RW007_EVNET_CLOSE_OK (1L << 3) -#define RW007_EVENT_CONN_FAIL (1L << 4) -#define RW007_EVENT_SEND_FAIL (1L << 5) - -static int cur_socket; -static int cur_send_bfsz; -static rt_event_t at_socket_event; -static rt_mutex_t at_event_lock; -static at_evt_cb_t at_evt_cb_set[] = { - [AT_SOCKET_EVT_RECV] = NULL, - [AT_SOCKET_EVT_CLOSED] = NULL, -}; - -static int at_socket_event_send(uint32_t event) -{ - return (int) rt_event_send(at_socket_event, event); -} - -static int at_socket_event_recv(uint32_t event, uint32_t timeout, rt_uint8_t option) -{ - int result = 0; - rt_uint32_t recved; - - result = rt_event_recv(at_socket_event, event, option | RT_EVENT_FLAG_CLEAR, timeout, &recved); - if (result != RT_EOK) - { - return -RT_ETIMEOUT; - } - - return recved; -} - -/** - * close socket by AT commands. - * - * @param current socket - * - * @return 0: close socket success - * -1: send AT commands error - * -2: wait socket event timeout - * -5: no memory - */ -static int rw007_socket_close(int socket) -{ - at_response_t resp = RT_NULL; - int result = RT_EOK; - - resp = at_create_resp(64, 0, rt_tick_from_millisecond(5000)); - if (!resp) - { - LOG_E("No memory for response structure!"); - return -RT_ENOMEM; - } - - rt_mutex_take(at_event_lock, RT_WAITING_FOREVER); - - if (at_exec_cmd(resp, "AT+CIPCLOSE=%d", socket) < 0) - { - result = -RT_ERROR; - goto __exit; - } - - __exit: - rt_mutex_release(at_event_lock); - - if (resp) - { - at_delete_resp(resp); - } - - return result; -} - -/** - * create TCP/UDP client or server connect by AT commands. - * - * @param socket current socket - * @param ip server or client IP address - * @param port server or client port - * @param type connect socket type(tcp, udp) - * @param is_client connection is client - * - * @return 0: connect success - * -1: connect failed, send commands error or type error - * -2: wait socket event timeout - * -5: no memory - */ -static int rw007_socket_connect(int socket, char *ip, int32_t port, enum at_socket_type type, rt_bool_t is_client) -{ - at_response_t resp = RT_NULL; - int result = RT_EOK; - rt_bool_t retryed = RT_FALSE; - - RT_ASSERT(ip); - RT_ASSERT(port >= 0); - - resp = at_create_resp(128, 0, rt_tick_from_millisecond(5000)); - if (!resp) - { - LOG_E("No memory for response structure!"); - return -RT_ENOMEM; - } - - rt_mutex_take(at_event_lock, RT_WAITING_FOREVER); - -__retry: - if (is_client) - { - switch (type) - { - case AT_SOCKET_TCP: - /* send AT commands to connect TCP server */ - if (at_exec_cmd(resp, "AT+CIPSTART=%d,\"TCP\",\"%s\",%d,60", socket, ip, port) < 0) - { - result = -RT_ERROR; - } - break; - - case AT_SOCKET_UDP: - if (at_exec_cmd(resp, "AT+CIPSTART=%d,\"UDP\",\"%s\",%d", socket, ip, port) < 0) - { - result = -RT_ERROR; - } - break; - - default: - LOG_E("Not supported connect type : %d.", type); - result = -RT_ERROR; - goto __exit; - } - } - - if (result != RT_EOK && !retryed) - { - LOG_D("socket (%d) connect failed, maybe the socket was not be closed at the last time and now will retry.", socket); - if (rw007_socket_close(socket) < 0) - { - goto __exit; - } - retryed = RT_TRUE; - result = RT_EOK; - goto __retry; - } - -__exit: - rt_mutex_release(at_event_lock); - - if (resp) - { - at_delete_resp(resp); - } - - return result; -} - -/** - * send data to server or client by AT commands. - * - * @param socket current socket - * @param buff send buffer - * @param bfsz send buffer size - * @param type connect socket type(tcp, udp) - * - * @return >=0: the size of send success - * -1: send AT commands error or send data error - * -2: waited socket event timeout - * -5: no memory - */ -static int rw007_socket_send(int socket, const char *buff, size_t bfsz, enum at_socket_type type) -{ - int result = RT_EOK; - int event_result = 0; - at_response_t resp = RT_NULL; - size_t cur_pkt_size = 0, sent_size = 0; - - RT_ASSERT(buff); - RT_ASSERT(bfsz > 0); - - resp = at_create_resp(128, 2, rt_tick_from_millisecond(5000)); - if (!resp) - { - LOG_E("No memory for response structure!"); - return -RT_ENOMEM; - } - - rt_mutex_take(at_event_lock, RT_WAITING_FOREVER); - - /* set current socket for send URC event */ - cur_socket = socket; - /* set AT client end sign to deal with '>' sign.*/ - at_set_end_sign('>'); - - while (sent_size < bfsz) - { - if (bfsz - sent_size < RW007_MODULE_SEND_MAX_SIZE) - { - cur_pkt_size = bfsz - sent_size; - } - else - { - cur_pkt_size = RW007_MODULE_SEND_MAX_SIZE; - } - - /* send the "AT+CIPSEND" commands to AT server than receive the '>' response on the first line. */ - if (at_exec_cmd(resp, "AT+CIPSEND=%d,%d", socket, cur_pkt_size) < 0) - { - result = -RT_ERROR; - goto __exit; - } - - /* send the real data to server or client */ - result = (int) at_client_send(buff + sent_size, cur_pkt_size); - if (result == 0) - { - result = -RT_ERROR; - goto __exit; - } - - /* waiting result event from AT URC */ - if (at_socket_event_recv(SET_EVENT(socket, 0), rt_tick_from_millisecond(5 * 1000), RT_EVENT_FLAG_OR) < 0) - { - LOG_E("socket (%d) send failed, wait connect result timeout.", socket); - result = -RT_ETIMEOUT; - goto __exit; - } - /* waiting OK or failed result */ - if ((event_result = at_socket_event_recv(RW007_EVENT_SEND_OK | RW007_EVENT_SEND_FAIL, rt_tick_from_millisecond(5 * 1000), - RT_EVENT_FLAG_OR)) < 0) - { - LOG_E("socket (%d) send failed, wait connect OK|FAIL timeout.", socket); - result = -RT_ETIMEOUT; - goto __exit; - } - /* check result */ - if (event_result & RW007_EVENT_SEND_FAIL) - { - LOG_E("socket (%d) send failed, return failed.", socket); - result = -RT_ERROR; - goto __exit; - } - - if (type == AT_SOCKET_TCP) - { - cur_pkt_size = cur_send_bfsz; - } - - sent_size += cur_pkt_size; - } - -__exit: - /* reset the end sign for data */ - at_set_end_sign(0); - - rt_mutex_release(at_event_lock); - - if (resp) - { - at_delete_resp(resp); - } - - return result; -} - -/** - * domain resolve by AT commands. - * - * @param name domain name - * @param ip parsed IP address, it's length must be 16 - * - * @return 0: domain resolve success - * -2: wait socket event timeout - * -5: no memory - */ -static int rw007_domain_resolve(const char *name, char ip[16]) -{ -#define RESOLVE_RETRY 5 - - int i, result = RT_EOK; - char recv_ip[16] = { 0 }; - at_response_t resp = RT_NULL; - - RT_ASSERT(name); - RT_ASSERT(ip); - - resp = at_create_resp(128, 0, rt_tick_from_millisecond(5000)); - if (!resp) - { - LOG_E("No memory for response structure!"); - return -RT_ENOMEM; - } - - rt_mutex_take(at_event_lock, RT_WAITING_FOREVER); - - for(i = 0; i < RESOLVE_RETRY; i++) - { - if (at_exec_cmd(resp, "AT+CIPDOMAIN=\"%s\"", name) < 0) - { - result = -RT_ERROR; - goto __exit; - } - - /* parse the third line of response data, get the IP address */ - if(at_resp_parse_line_args_by_kw(resp, "+CIPDOMAIN:", "+CIPDOMAIN:%s", recv_ip) < 0) - { - rt_thread_delay(rt_tick_from_millisecond(100)); - /* resolve failed, maybe receive an URC CRLF */ - continue; - } - - if (strlen(recv_ip) < 8) - { - rt_thread_delay(rt_tick_from_millisecond(100)); - /* resolve failed, maybe receive an URC CRLF */ - continue; - } - else - { - strncpy(ip, recv_ip, 15); - ip[15] = '\0'; - break; - } - } - -__exit: - rt_mutex_release(at_event_lock); - - if (resp) - { - at_delete_resp(resp); - } - - return result; - -} - -/** - * set AT socket event notice callback - * - * @param event notice event - * @param cb notice callback - */ -static void rw007_socket_set_event_cb(at_socket_evt_t event, at_evt_cb_t cb) -{ - if (event < sizeof(at_evt_cb_set) / sizeof(at_evt_cb_set[1])) - { - at_evt_cb_set[event] = cb; - } -} - -static void urc_send_func(const char *data, rt_size_t size) -{ - RT_ASSERT(data && size); - - if (strstr(data, "SEND OK")) - { - at_socket_event_send(SET_EVENT(cur_socket, RW007_EVENT_SEND_OK)); - } - else if (strstr(data, "SEND FAIL")) - { - at_socket_event_send(SET_EVENT(cur_socket, RW007_EVENT_SEND_FAIL)); - } -} - -static void urc_send_bfsz_func(const char *data, rt_size_t size) -{ - int send_bfsz = 0; - - RT_ASSERT(data && size); - - sscanf(data, "Recv %d bytes", &send_bfsz); - - cur_send_bfsz = send_bfsz; -} - -static void urc_close_func(const char *data, rt_size_t size) -{ - int socket = 0; - - RT_ASSERT(data && size); - - sscanf(data, "%d,CLOSED", &socket); - /* notice the socket is disconnect by remote */ - if (at_evt_cb_set[AT_SOCKET_EVT_CLOSED]) - { - at_evt_cb_set[AT_SOCKET_EVT_CLOSED](socket, AT_SOCKET_EVT_CLOSED, RT_NULL, 0); - } -} - -static void urc_recv_func(const char *data, rt_size_t size) -{ - int socket = 0; - rt_size_t bfsz = 0, temp_size = 0; - rt_int32_t timeout; - char *recv_buf = RT_NULL, temp[8]; - - RT_ASSERT(data && size); - - /* get the current socket and receive buffer size by receive data */ - sscanf(data, "+IPD,%d,%d:", &socket, (int *) &bfsz); - /* get receive timeout by receive buffer length */ - timeout = bfsz; - - if (socket < 0 || bfsz == 0) - return; - - recv_buf = rt_calloc(1, bfsz); - if (!recv_buf) - { - LOG_E("no memory for URC receive buffer (%d)!", bfsz); - /* read and clean the coming data */ - while (temp_size < bfsz) - { - if (bfsz - temp_size > sizeof(temp)) - { - at_client_recv(temp, sizeof(temp), timeout); - } - else - { - at_client_recv(temp, bfsz - temp_size, timeout); - } - temp_size += sizeof(temp); - } - return; - } - - /* sync receive data */ - if (at_client_recv(recv_buf, bfsz, timeout) != bfsz) - { - LOG_E("receive size(%d) data failed!", bfsz); - rt_free(recv_buf); - return; - } - - /* notice the receive buffer and buffer size */ - if (at_evt_cb_set[AT_SOCKET_EVT_RECV]) - { - at_evt_cb_set[AT_SOCKET_EVT_RECV](socket, AT_SOCKET_EVT_RECV, recv_buf, bfsz); - } -} - -static void urc_busy_p_func(const char *data, rt_size_t size) -{ - RT_ASSERT(data && size); - - LOG_D("system is processing a commands and it cannot respond to the current commands."); -} - -static void urc_busy_s_func(const char *data, rt_size_t size) -{ - RT_ASSERT(data && size); - - LOG_D("system is sending data and it cannot respond to the current commands."); -} - -static void urc_func(const char *data, rt_size_t size) -{ - RT_ASSERT(data && size); - - if(strstr(data, "WIFI CONNECTED")) - { - LOG_I("RW007 WIFI is connected."); - } - else if(strstr(data, "WIFI DISCONNECT")) - { - LOG_I("RW007 WIFI is disconnect."); - } -} - -static struct at_urc urc_table[] = { - {"SEND OK", "\r\n", urc_send_func}, - {"SEND FAIL", "\r\n", urc_send_func}, - {"Recv", "bytes\r\n", urc_send_bfsz_func}, - {"", ",CLOSED\r\n", urc_close_func}, - {"+IPD", ":", urc_recv_func}, - {"busy p", "\r\n", urc_busy_p_func}, - {"busy s", "\r\n", urc_busy_s_func}, - {"WIFI CONNECTED", "\r\n", urc_func}, - {"WIFI DISCONNECT", "\r\n", urc_func}, -}; - -#define AT_SEND_CMD(resp, cmd) \ - do \ - { \ - if (at_exec_cmd(at_resp_set_info(resp, 256, 0, rt_tick_from_millisecond(5000)), cmd) < 0) \ - { \ - LOG_E("RT AT send commands(%s) error!", cmd); \ - result = -RT_ERROR; \ - goto __exit; \ - } \ - } while(0); \ - -static void rw007_init_thread_entry(void *parameter) -{ - at_response_t resp = RT_NULL; - rt_err_t result = RT_EOK; - rt_size_t i; - - resp = at_create_resp(128, 0, rt_tick_from_millisecond(5000)); - if (!resp) - { - LOG_E("No memory for response structure!"); - result = -RT_ENOMEM; - goto __exit; - } - - rt_thread_delay(rt_tick_from_millisecond(5000)); - /* reset module */ - AT_SEND_CMD(resp, "AT+RST"); - /* reset waiting delay */ - rt_thread_delay(rt_tick_from_millisecond(1000)); - /* disable echo */ - AT_SEND_CMD(resp, "ATE0"); - /* set current mode to Wi-Fi station */ - AT_SEND_CMD(resp, "AT+CWMODE=1"); - /* get module version */ - AT_SEND_CMD(resp, "AT+GMR"); - /* show module version */ - for (i = 0; i < resp->line_counts - 1; i++) - { - LOG_D("%s", at_resp_get_line(resp, i + 1)); - } - /* connect to WiFi AP */ - if (at_exec_cmd(at_resp_set_info(resp, 128, 0, 20 * RT_TICK_PER_SECOND), "AT+CWJAP=\"%s\",\"%s\"", - AT_DEVICE_WIFI_SSID, AT_DEVICE_WIFI_PASSWORD) != RT_EOK) - { - LOG_E("AT network initialize failed, check ssid(%s) and password(%s).", AT_DEVICE_WIFI_SSID, AT_DEVICE_WIFI_PASSWORD); - result = -RT_ERROR; - goto __exit; - } - - AT_SEND_CMD(resp, "AT+CIPMUX=1"); - -__exit: - if (resp) - { - at_delete_resp(resp); - } - - if (!result) - { - LOG_I("AT network initialize success!"); - } - else - { - LOG_E("AT network initialize failed (%d)!", result); - } -} - -int rw007_net_init(void) -{ -#ifdef PKG_AT_INIT_BY_THREAD - rt_thread_t tid; - - tid = rt_thread_create("rw007_net_init", rw007_init_thread_entry, RT_NULL,RW007_THREAD_STACK_SIZE, RW007_THREAD_PRIORITY, 20); - if (tid) - { - rt_thread_startup(tid); - } - else - { - LOG_E("Create AT initialization thread fail!"); - } -#else - rw007_init_thread_entry(RT_NULL); -#endif - - return RT_EOK; -} - -int rw007_ping(int argc, char **argv) -{ - at_response_t resp = RT_NULL; - static int icmp_seq; - int req_time; - - if (argc != 2) - { - rt_kprintf("Please input: at_ping \n"); - return -RT_ERROR; - } - - resp = at_create_resp(64, 0, rt_tick_from_millisecond(5000)); - if (!resp) - { - rt_kprintf("No memory for response structure!\n"); - return -RT_ENOMEM; - } - - for(icmp_seq = 1; icmp_seq <= 4; icmp_seq++) - { - if (at_exec_cmd(resp, "AT+PING=\"%s\"", argv[1]) < 0) - { - rt_kprintf("ping: unknown remote server host\n"); - at_delete_resp(resp); - return -RT_ERROR; - } - - if(at_resp_parse_line_args_by_kw(resp, "+", "+%d", &req_time) < 0) - { - continue; - } - - if (req_time) - { - rt_kprintf("32 bytes from %s icmp_seq=%d time=%d ms\n", argv[1], icmp_seq, req_time); - } - } - - if (resp) - { - at_delete_resp(resp); - } - - return RT_EOK; -} - -#ifdef FINSH_USING_MSH -#include -MSH_CMD_EXPORT_ALIAS(rw007_net_init, at_net_init, initialize AT network); -MSH_CMD_EXPORT_ALIAS(rw007_ping, at_ping, AT ping network host); -#endif - -static const struct at_device_ops rw007_socket_ops = { - rw007_socket_connect, - rw007_socket_close, - rw007_socket_send, - rw007_domain_resolve, - rw007_socket_set_event_cb, -}; - -static int rw007_netdev_add(const char *netdev_name) -{ -#define ETHERNET_MTU 1500 -#define HWADDR_LEN 8 - struct netdev *netdev = RT_NULL; - int result = 0; - - RT_ASSERT(netdev_name); - - netdev = (struct netdev *) rt_calloc(1, sizeof(struct netdev)); - if (netdev == RT_NULL) - { - return RT_NULL; - } - - /* TODO: improve netdev adaptation */ - netdev->mtu = ETHERNET_MTU; - netdev->hwaddr_len = HWADDR_LEN; - netdev->ops = RT_NULL; - -#ifdef SAL_USING_AT - extern int sal_at_netdev_set_pf_info(struct netdev *netdev); - /* set the network interface socket/netdb operations */ - sal_at_netdev_set_pf_info(netdev); -#endif - - result = netdev_register(netdev, netdev_name, RT_NULL); - - /*TODO: improve netdev adaptation */ - netdev_low_level_set_status(netdev, RT_TRUE); - netdev_low_level_set_link_status(netdev, RT_TRUE); - netdev_low_level_set_dhcp_status(netdev, RT_TRUE); - netdev->flags |= NETDEV_FLAG_INTERNET_UP; - - return result; -} - -static int at_socket_device_init(void) -{ - /* create current AT socket event */ - at_socket_event = rt_event_create("at_se", RT_IPC_FLAG_FIFO); - if (at_socket_event == RT_NULL) - { - LOG_E("RT AT client port initialize failed! at_sock_event create failed!"); - return -RT_ENOMEM; - } - - /* create current AT socket event lock */ - at_event_lock = rt_mutex_create("at_se", RT_IPC_FLAG_FIFO); - if (at_event_lock == RT_NULL) - { - LOG_E("RT AT client port initialize failed! at_sock_lock create failed!"); - rt_event_delete(at_socket_event); - return -RT_ENOMEM; - } - - /* initialize AT client */ - at_client_init(AT_DEVICE_NAME, AT_DEVICE_RECV_BUFF_LEN); - - /* register URC data execution function */ - at_set_urc_table(urc_table, sizeof(urc_table) / sizeof(urc_table[0])); - - /* add network interface device to netdev list */ - if (rw007_netdev_add(RW007_NETDEV_NAME) < 0) - { - LOG_E("RW007 network interface device(%d) add failed.", RW007_NETDEV_NAME); - return -RT_ENOMEM; - } - - /* initialize rw007 network */ - rw007_net_init(); - - /* set rw007 AT Socket options */ - at_socket_device_register(&rw007_socket_ops); - - return RT_EOK; -} -INIT_APP_EXPORT(at_socket_device_init); - -#endif /* AT_DEVICE_RW007 */ diff --git a/at_socket_sim76xx.c b/at_socket_sim76xx.c deleted file mode 100755 index 6e8f099..0000000 --- a/at_socket_sim76xx.c +++ /dev/null @@ -1,1225 +0,0 @@ -/* - * File : at_socket_sim76xx.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-12-22 thomasonegd first version - * 2019-03-06 thomasonegd fix udp connection. - * 2019-03-08 thomasonegd add power_on & power_off api - */ - -#include -#include - -#include -#include - -#include -#include - -#if !defined(RT_USING_NETDEV) -#error "This RT-Thread version is older, please check and updata laster RT-Thread!" -#else -#include -#include -#endif /* RT_USING_NETDEV */ - -#if !defined(AT_SW_VERSION_NUM) || AT_SW_VERSION_NUM < 0x10200 -#error "This AT Client version is older, please check and update latest AT Client!" -#endif - -#define LOG_TAG "at.sim76xx" -#include - -#ifdef AT_DEVICE_SIM76XX - -#define SIM76XX_NETDEV_NAME "sim76xx" - -#define SIM76XX_MODULE_SEND_MAX_SIZE 1500 -#define SIM76XX_WAIT_CONNECT_TIME 5000 -#define SIM76XX_THREAD_STACK_SIZE 1024 -#define SIM76XX_THREAD_PRIORITY (RT_THREAD_PRIORITY_MAX/2) - -#define SIM76XX_MAX_CONNECTIONS 10 - -/* set real event by current socket and current state */ -#define SET_EVENT(socket, event) (((socket + 1) << 16) | (event)) - -/* AT socket event type */ -#define SIM76XX_EVENT_CONN_OK (1L << 0) -#define SIM76XX_EVENT_SEND_OK (1L << 1) -#define SIM76XX_EVENT_RECV_OK (1L << 2) -#define SIM76XX_EVNET_CLOSE_OK (1L << 3) -#define SIM76XX_EVENT_CONN_FAIL (1L << 4) -#define SIM76XX_EVENT_SEND_FAIL (1L << 5) - -uint8_t at_socket_init = 0; - -static int cur_socket; -static int cur_send_bfsz; -static rt_event_t at_socket_event; -static rt_mutex_t at_event_lock; -static at_evt_cb_t at_evt_cb_set[] = { - [AT_SOCKET_EVT_RECV] = NULL, - [AT_SOCKET_EVT_CLOSED] = NULL, -}; - -static char udp_ipstr[SIM76XX_MAX_CONNECTIONS][16]; -static int udp_port[SIM76XX_MAX_CONNECTIONS]; - -static void at_tcp_ip_errcode_parse(int result)//Unsolicited TCP/IP command codes -{ - switch(result) - { - case 0 : LOG_D("%d : operation succeeded ", result); break; - case 1 : LOG_E("%d : UNetwork failure", result); break; - case 2 : LOG_E("%d : Network not opened", result); break; - case 3 : LOG_E("%d : Wrong parameter", result); break; - case 4 : LOG_E("%d : Operation not supported", result); break; - case 5 : LOG_E("%d : Failed to create socket", result); break; - case 6 : LOG_E("%d : Failed to bind socket", result); break; - case 7 : LOG_E("%d : TCP server is already listening", result); break; - case 8 : LOG_E("%d : Busy", result); break; - case 9 : LOG_E("%d : Sockets opened", result); break; - case 10 : LOG_E("%d : Timeout ", result); break; - case 11 : LOG_E("%d : DNS parse failed for AT+CIPOPEN", result); break; - case 255 : LOG_E("%d : Unknown error", result); break; - } -} - - -static int at_socket_event_send(uint32_t event) -{ - return (int) rt_event_send(at_socket_event, event); -} - -static int at_socket_event_recv(uint32_t event, uint32_t timeout, rt_uint8_t option) -{ - int result = 0; - rt_uint32_t recved; - - result = rt_event_recv(at_socket_event, event, option | RT_EVENT_FLAG_CLEAR, timeout, &recved); - if (result != RT_EOK) - { - return -RT_ETIMEOUT; - } - - return recved; -} - -/** - * close socket by AT commands. - * - * @param current socket - * - * @return 0: close socket success - * -1: send AT commands error - * -2: wait socket event timeout - * -5: no memory - */ -static int sim76xx_socket_close(int socket) -{ - at_response_t resp = RT_NULL; - int result = RT_EOK; - int activated; - uint8_t lnk_stat[10]; - - resp = at_create_resp(128, 0, rt_tick_from_millisecond(500)); - if (!resp) - { - LOG_E("No memory for response structure!"); - return -RT_ENOMEM; - } - - rt_mutex_take(at_event_lock, RT_WAITING_FOREVER); - - rt_thread_delay(rt_tick_from_millisecond(100)); - - // check socket link_state - if (at_exec_cmd(resp,"AT+CIPCLOSE?") < 0) - { - result = -RT_ERROR; - goto __exit; - } - - if(at_resp_parse_line_args_by_kw(resp,"+CIPCLOSE:", "+CIPCLOSE: %d,%d,%d,%d,%d,%d,%d,%d,%d,%d",&lnk_stat[0],&lnk_stat[1],&lnk_stat[1] - ,&lnk_stat[2],&lnk_stat[3],&lnk_stat[4],&lnk_stat[5],&lnk_stat[6],&lnk_stat[7],&lnk_stat[8],&lnk_stat[9]) < 0) - { - result = -RT_ERROR; - goto __exit; - } - - if (lnk_stat[socket]) - { - // close tcp or udp socket if connected - if (at_exec_cmd(resp, "AT+CIPCLOSE=%d", socket) < 0) - { - result = -RT_ERROR; - goto __exit; - } - } - // check the network open or not - if (at_exec_cmd(resp,"AT+NETOPEN?") < 0) - { - result = -RT_ERROR; - - goto __exit; - } - - if(at_resp_parse_line_args_by_kw(resp, "+NETOPEN:", "+NETOPEN: %d", &activated) < 0) - { - result = -RT_ERROR; - goto __exit; - } - - if (activated) - { - // if already open,then close it. - if (at_exec_cmd(resp,"AT+NETCLOSE") < 0) - { - result = -RT_ERROR; - - goto __exit; - } - } - __exit: - rt_mutex_release(at_event_lock); - - if (resp) - { - at_delete_resp(resp); - } - - return result; -} - -/** - * open packet network - */ -static int sim76xx_network_socket_open(void) -{ - int result = RT_EOK; - at_response_t resp = RT_NULL; - int activated; - - resp = at_create_resp(128, 0, rt_tick_from_millisecond(5000)); - if (!resp) - { - LOG_E("No memory for response structure!"); - return -RT_ENOMEM; - } - - rt_mutex_take(at_event_lock, RT_WAITING_FOREVER); - - // check the network open or not - if (at_exec_cmd(resp,"AT+NETOPEN?") < 0) - { - result = -RT_ERROR; - - goto __exit; - } - - if(at_resp_parse_line_args_by_kw(resp, "+NETOPEN:", "+NETOPEN: %d", &activated) < 0) - { - result = -RT_ERROR; - goto __exit; - } - - if (activated) - {//network socket is already opened - goto __exit; - }else - { - // if not opened the open it. - if (at_exec_cmd(resp,"AT+NETOPEN") < 0) - { - result = -RT_ERROR; - - goto __exit; - } - } -__exit: - rt_mutex_release(at_event_lock); - - if (resp) - { - at_delete_resp(resp); - } - - return result; -} - -/** - * create TCP/UDP client or server connect by AT commands. - * - * @param socket current socket - * @param ip server or client IP address - * @param port server or client port - * @param type connect socket type(tcp, udp) - * @param is_client connection is client - * - * @return 0: connect success - * -1: connect failed, send commands error or type error - * -2: wait socket event timeout - * -5: no memory - */ -static int sim76xx_socket_connect(int socket, char *ip, int32_t port, enum at_socket_type type, rt_bool_t is_client) -{ - at_response_t resp = RT_NULL; - int result = RT_EOK,event_result = 0; - rt_bool_t retryed = RT_FALSE; - - RT_ASSERT(ip); - RT_ASSERT(port >= 0); - - resp = at_create_resp(128, 0, rt_tick_from_millisecond(5000)); - if (!resp) - { - LOG_E("No memory for response structure!"); - return -RT_ENOMEM; - } - - rt_mutex_take(at_event_lock, RT_WAITING_FOREVER); - -__retry: - if (is_client) - { - //open network socket first(AT+NETOPEN) - sim76xx_network_socket_open(); - - switch (type) - { - case AT_SOCKET_TCP: - /* send AT commands to connect TCP server */ - if (at_exec_cmd(resp, "AT+CIPOPEN=%d,\"TCP\",\"%s\",%d", socket, ip, port) < 0) - { - result = -RT_ERROR; - } - break; - - case AT_SOCKET_UDP: - if (at_exec_cmd(resp, "AT+CIPOPEN=%d,\"UDP\",,,%d", socket, port) < 0) - { - result = -RT_ERROR; - } - strcpy(udp_ipstr[socket],ip); - udp_port[socket] = port; - break; - - default: - LOG_E("Not supported connect type : %d.", type); - result = -RT_ERROR; - goto __exit; - } - } - - /* waiting result event from AT URC, the device default connection timeout is 75 seconds, but it set to 10 seconds is convenient to use.*/ - if (at_socket_event_recv(SET_EVENT(socket, 0), rt_tick_from_millisecond(10 * 1000), RT_EVENT_FLAG_OR) < 0) - { - LOG_E("socket (%d) connect failed, wait connect result timeout.", socket); - result = -RT_ETIMEOUT; - goto __exit; - } - /* waiting OK or failed result */ - if ((event_result = at_socket_event_recv(SIM76XX_EVENT_CONN_OK | SIM76XX_EVENT_CONN_FAIL, rt_tick_from_millisecond(1 * 1000), - RT_EVENT_FLAG_OR)) < 0) - { - LOG_E("socket (%d) connect failed, wait connect OK|FAIL timeout.", socket); - result = -RT_ETIMEOUT; - goto __exit; - } - /* check result */ - if (event_result & SIM76XX_EVENT_CONN_FAIL) - { - if (!retryed) - { - LOG_E("socket (%d) connect failed, maybe the socket was not be closed at the last time and now will retry.", socket); - if (sim76xx_socket_close(socket) < 0) - { - goto __exit; - } - retryed = RT_TRUE; - goto __retry; - } - LOG_E("socket (%d) connect failed, failed to establish a connection.", socket); - result = -RT_ERROR; - goto __exit; - } - - if (result != RT_EOK && !retryed) - { - LOG_D("socket (%d) connect failed, maybe the socket was not be closed at the last time and now will retry.", socket); - if (sim76xx_socket_close(socket) < 0) - { - goto __exit; - } - retryed = RT_TRUE; - result = RT_EOK; - goto __retry; - } - -__exit: - rt_mutex_release(at_event_lock); - - if (resp) - { - at_delete_resp(resp); - } - - return result; -} - -/** - * send data to server or client by AT commands. - * - * @param socket current socket - * @param buff send buffer - * @param bfsz send buffer size - * @param type connect socket type(tcp, udp) - * - * @return >=0: the size of send success - * -1: send AT commands error or send data error - * -2: waited socket event timeout - * -5: no memory - */ -static int sim76xx_socket_send(int socket, const char *buff, size_t bfsz, enum at_socket_type type) -{ - int result = RT_EOK; - int event_result = 0; - at_response_t resp = RT_NULL; - size_t cur_pkt_size = 0, sent_size = 0; - - RT_ASSERT(buff); - RT_ASSERT(bfsz > 0); - - resp = at_create_resp(128, 2, rt_tick_from_millisecond(5000)); - if (!resp) - { - LOG_E("No memory for response structure!"); - return -RT_ENOMEM; - } - - rt_mutex_take(at_event_lock, RT_WAITING_FOREVER); - - /* set current socket for send URC event */ - cur_socket = socket; - /* set AT client end sign to deal with '>' sign.*/ - at_set_end_sign('>'); - - while (sent_size < bfsz) - { - if (bfsz - sent_size < SIM76XX_MODULE_SEND_MAX_SIZE) - { - cur_pkt_size = bfsz - sent_size; - } - else - { - cur_pkt_size = SIM76XX_MODULE_SEND_MAX_SIZE; - } - - switch(type) - { - case AT_SOCKET_TCP: - /* send the "AT+CIPSEND" commands to AT server than receive the '>' response on the first line. */ - if (at_exec_cmd(resp, "AT+CIPSEND=%d,%d", socket, cur_pkt_size) < 0) - { - result = -RT_ERROR; - goto __exit; - } - break; - case AT_SOCKET_UDP: - /* send the "AT+CIPSEND" commands to AT server than receive the '>' response on the first line. */ - if (at_exec_cmd(resp, "AT+CIPSEND=%d,%d,\"%s\",%d", socket, cur_pkt_size,udp_ipstr[socket],udp_port[socket]) < 0) - { - result = -RT_ERROR; - goto __exit; - } - break; - } - - /* send the real data to server or client */ - result = (int) at_client_send(buff + sent_size, cur_pkt_size); - if (result == 0) - { - result = -RT_ERROR; - goto __exit; - } - - /* waiting result event from AT URC */ - if (at_socket_event_recv(SET_EVENT(socket, 0), rt_tick_from_millisecond(5 * 1000), RT_EVENT_FLAG_OR) < 0) - { - LOG_E("socket (%d) send failed, wait connect result timeout.", socket); - result = -RT_ETIMEOUT; - goto __exit; - } - /* waiting OK or failed result */ - if ((event_result = at_socket_event_recv(SIM76XX_EVENT_SEND_OK | SIM76XX_EVENT_SEND_FAIL, rt_tick_from_millisecond(5 * 1000), - RT_EVENT_FLAG_OR)) < 0) - { - LOG_E("socket (%d) send failed, wait connect OK|FAIL timeout.", socket); - result = -RT_ETIMEOUT; - goto __exit; - } - /* check result */ - if (event_result & SIM76XX_EVENT_SEND_FAIL) - { - LOG_E("socket (%d) send failed, return failed.", socket); - result = -RT_ERROR; - goto __exit; - } - - if (type == AT_SOCKET_TCP) - { - cur_pkt_size = cur_send_bfsz; - } - - sent_size += cur_pkt_size; - } - -__exit: - /* reset the end sign for data */ - at_set_end_sign(0); - - rt_mutex_release(at_event_lock); - - if (resp) - { - at_delete_resp(resp); - } - - return result; -} - -/** - * domain resolve by AT commands. - * - * @param name domain name - * @param ip parsed IP address, it's length must be 16 - * - * @return 0: domain resolve success - * -2: wait socket event timeout - * -5: no memory - */ -static int sim76xx_domain_resolve(const char *name, char ip[16]) -{ -#define RESOLVE_RETRY 5 - - int i, result = RT_EOK; - char domain[32] = { 0 }; - char domain_ip[16] = {0}; - at_response_t resp = RT_NULL; - - RT_ASSERT(name); - RT_ASSERT(ip); - - resp = at_create_resp(128, 0, rt_tick_from_millisecond(5000)); - if (!resp) - { - LOG_E("No memory for response structure!"); - return -RT_ENOMEM; - } - - rt_mutex_take(at_event_lock, RT_WAITING_FOREVER); - - for(i = 0; i < RESOLVE_RETRY; i++) - { - if (at_exec_cmd(resp, "AT+CDNSGIP=\"%s\"", name) < 0) - { - rt_thread_delay(rt_tick_from_millisecond(200)); - /* resolve failed, maybe receive an URC CRLF */ - continue; - } - - /* parse the third line of response data, get the IP address */ - /* +CDNSGIP: 1,"www.baidu.com","14.215.177.39" */ - if(at_resp_parse_line_args_by_kw(resp, "+CDNSGIP:", "+CDNSGIP: 1,%[^,],\"%[^\"]", domain,domain_ip) < 0) - { - rt_thread_delay(rt_tick_from_millisecond(200)); - /* resolve failed, maybe receive an URC CRLF */ - continue; - } - - if (strlen(domain_ip) < 8) - { - rt_thread_delay(rt_tick_from_millisecond(200)); - /* resolve failed, maybe receive an URC CRLF */ - continue; - } - else - { - strncpy(ip, domain_ip, 15); - ip[15] = '\0'; - break; - } - } - - rt_mutex_release(at_event_lock); - - if (i == RESOLVE_RETRY) - { - result = -RT_ERROR; - } - - if (resp) - { - at_delete_resp(resp); - } - - return result; - -} - -/** - * set AT socket event notice callback - * - * @param event notice event - * @param cb notice callback - */ -static void sim76xx_socket_set_event_cb(at_socket_evt_t event, at_evt_cb_t cb) -{ - if (event < (sizeof(at_evt_cb_set) / sizeof(at_evt_cb_set[1]))) - { - at_evt_cb_set[event] = cb; - } -} - -static void urc_send_func(const char *data, rt_size_t size) -{ - int socket = 0; - int rqst_size; - int cnf_size; - - RT_ASSERT(data && size); - - sscanf(data,"+CIPSEND: %d,%d,%d",&socket,&rqst_size,&cnf_size); - - cur_send_bfsz = cnf_size; - - at_socket_event_send(SET_EVENT(socket, SIM76XX_EVENT_SEND_OK)); -} - -static void urc_ping_func(const char *data, rt_size_t size) -{ - static int icmp_seq = 0; - int i, j = 0; - int result, recv_len, time, ttl; - int sent, rcvd, lost, min, max, avg; - char dst_ip[16] = { 0 }; - - RT_ASSERT(data); - - for (i=0;i sizeof(temp)) - { - at_client_recv(temp, sizeof(temp), timeout); - } - else - { - at_client_recv(temp, bfsz - temp_size, timeout); - } - temp_size += sizeof(temp); - } - return; - } - - /* sync receive data */ - if (at_client_recv(recv_buf, bfsz, timeout) != bfsz) - { - LOG_E("receive size(%d) data failed!", bfsz); - rt_free(recv_buf); - return; - } - - /* notice the receive buffer and buffer size */ - if (at_evt_cb_set[AT_SOCKET_EVT_RECV]) - { - at_evt_cb_set[AT_SOCKET_EVT_RECV](cur_socket, AT_SOCKET_EVT_RECV, recv_buf, bfsz); - } -} - -static struct at_urc urc_table[] = { - {"+CIPSEND:", "\r\n", urc_send_func}, - {"+CIPOPEN:", "\r\n", urc_connect_func}, - {"+CPING:", "\r\n", urc_ping_func}, - {"+IPCLOSE", "\r\n", urc_close_func}, - {"+IPD", "\r\n", urc_recv_func}, -}; - -#define AT_SEND_CMD(resp, cmd) \ - do \ - { \ - if (at_exec_cmd(at_resp_set_info(resp, 256, 0, rt_tick_from_millisecond(5000)), cmd) < 0) \ - { \ - LOG_E("RT AT send commands(%s) error!", cmd); \ - result = -RT_ERROR; \ - goto __exit; \ - } \ - } while(0); \ - - -/** - * power up sim76xx modem - */ -static void sim76xx_power_on(void) -{ - rt_pin_write(AT_DEVICE_POWER_PIN, PIN_HIGH); - rt_thread_delay(rt_tick_from_millisecond(300)); - rt_pin_write(AT_DEVICE_POWER_PIN, PIN_LOW); - - while (rt_pin_read(AT_DEVICE_STATUS_PIN) == PIN_LOW) - { - rt_thread_delay(rt_tick_from_millisecond(10)); - } -} - -static void sim76xx_power_off(void) -{ - rt_pin_write(AT_DEVICE_POWER_PIN, PIN_HIGH); - rt_thread_delay(rt_tick_from_millisecond(3000)); - rt_pin_write(AT_DEVICE_POWER_PIN, PIN_LOW); - - while (rt_pin_read(AT_DEVICE_STATUS_PIN) == PIN_HIGH) - { - rt_thread_delay(rt_tick_from_millisecond(10)); - } - rt_pin_write(AT_DEVICE_POWER_PIN, PIN_LOW); -} - - -static void sim76xx_init_thread_entry(void *parameter) -{ -#define CSQ_RETRY 20 -#define CREG_RETRY 10 -#define CGREG_RETRY 20 -#define CGATT_RETRY 10 -#define CCLK_RETRY 10 - - at_response_t resp = RT_NULL; - rt_err_t result = RT_EOK; - rt_size_t i, qi_arg[3]; - char parsed_data[20]; - - resp = at_create_resp(128, 0, rt_tick_from_millisecond(300)); - if (!resp) - { - LOG_E("No memory for response structure!"); - result = -RT_ENOMEM; - goto __exit; - } - - /* power-up sim76xx */ - sim76xx_power_on(); - - LOG_D("Start initializing the SIM76XXE module"); - /* wait SIM76XX startup finish, Send AT every 5s, if receive OK, SYNC success*/ - if (at_client_wait_connect(SIM76XX_WAIT_CONNECT_TIME)) - { - result = -RT_ETIMEOUT; - goto __exit; - } - - /* disable echo */ - AT_SEND_CMD(resp, "ATE0"); - - /* get module version */ - AT_SEND_CMD(resp, "ATI"); - - /* show module version */ - for (i = 0; i < (int) resp->line_counts - 1; i++) - { - LOG_D("%s", at_resp_get_line(resp, i + 1)); - } - /* check SIM card */ - - AT_SEND_CMD(resp, "AT+CPIN?"); - if (!at_resp_get_line_by_kw(resp, "READY")) - { - LOG_E("SIM card detection failed"); - result = -RT_ERROR; - goto __exit; - } - - /* waiting for dirty data to be digested */ - rt_thread_delay(rt_tick_from_millisecond(10)); - /* check signal strength */ - for (i = 0; i < CSQ_RETRY; i++) - { - AT_SEND_CMD(resp, "AT+CSQ"); - at_resp_parse_line_args_by_kw(resp, "+CSQ:", "+CSQ: %d,%d", &qi_arg[0], &qi_arg[1]); - if (qi_arg[0] != 99) - { - LOG_D("Signal strength: %d Channel bit error rate: %d", qi_arg[0], qi_arg[1]); - break; - } - rt_thread_delay(rt_tick_from_millisecond(1000)); - } - - if (i == CSQ_RETRY) - { - LOG_E("Signal strength check failed (%s)", parsed_data); - result = -RT_ERROR; - goto __exit; - } - - //do not show the prompt when receiving data - AT_SEND_CMD(resp, "AT+CIPSRIP=0"); - - /* check the GSM network is registered */ - for (i = 0; i < CREG_RETRY; i++) - { - AT_SEND_CMD(resp,"AT+CREG?"); - at_resp_parse_line_args_by_kw(resp, "+CREG:", "+CREG: %s", &parsed_data); - if (!strncmp(parsed_data, "0,1", sizeof(parsed_data)) || !strncmp(parsed_data, "0,5", sizeof(parsed_data))) - { - LOG_D("GSM network is registered (%s)", parsed_data); - break; - } - rt_thread_delay(rt_tick_from_millisecond(1000)); - } - if (i == CREG_RETRY) - { - LOG_E("The GSM network is register failed (%s)", parsed_data); - result = -RT_ERROR; - goto __exit; - } - /* check the GPRS network is registered */ - for (i = 0; i < CGREG_RETRY; i++) - { - AT_SEND_CMD(resp,"AT+CGREG?"); - at_resp_parse_line_args_by_kw(resp, "+CGREG:", "+CGREG: %s", &parsed_data); - if (!strncmp(parsed_data, "0,1", sizeof(parsed_data)) || !strncmp(parsed_data, "0,5", sizeof(parsed_data))) - { - LOG_D("GPRS network is registered (%s)", parsed_data); - break; - } - rt_thread_delay(rt_tick_from_millisecond(1000)); - } - - if (i == CGREG_RETRY) - { - LOG_E("The GPRS network is register failed (%s)", parsed_data); - result = -RT_ERROR; - goto __exit; - } - - /* check packet domain attach or detach */ - for (i = 0;i < CGATT_RETRY; i++) - { - AT_SEND_CMD(resp,"AT+CGATT?") - at_resp_parse_line_args_by_kw(resp,"+CGATT:","+CGATT: %s",&parsed_data); - if (!strncmp(parsed_data,"1",1)) - { - LOG_I("Packet domain attach"); - break; - } - - rt_thread_delay(rt_tick_from_millisecond(1000)); - } - - if (i == CGATT_RETRY) - { - LOG_E("The GPRS network attach failed"); - result = -RT_ERROR; - goto __exit; - } - - /* get real time */ - int year,month,day,hour,min,sec; - - for (i = 0;i < CCLK_RETRY;i++) - { - if (at_exec_cmd(at_resp_set_info(resp, 256, 0, rt_tick_from_millisecond(5000)), "AT+CCLK?") < 0) - { - rt_thread_delay(rt_tick_from_millisecond(500)); - continue; - } - - //+CCLK: "18/12/22,18:33:12+32" - if (at_resp_parse_line_args_by_kw(resp,"+CCLK:","+CCLK: \"%d/%d/%d,%d:%d:%d",&year,&month,&day,&hour,&min,&sec) < 0) - { - rt_thread_delay(rt_tick_from_millisecond(500)); - continue; - } - - set_date(year + 2000,month,day); - set_time(hour,min,sec); - - break; - } - - if (i == CCLK_RETRY) - { - LOG_E("The GPRS network attach failed"); - result = -RT_ERROR; - goto __exit; - } - - /* set active PDP context's profile number */ - AT_SEND_CMD(resp, "AT+CSOCKSETPN=1"); - -__exit: - if (resp) - { - at_delete_resp(resp); - } - - if (!result) - { - LOG_I("AT network initialize success!"); - at_socket_init = 1; - } - else - { - LOG_E("AT network initialize failed (%d)!", result); - } -} - -int sim76xx_net_init(void) -{ -#ifdef PKG_AT_INIT_BY_THREAD - rt_thread_t tid; - - tid = rt_thread_create("sim76xx_net_init", sim76xx_init_thread_entry, RT_NULL,SIM76XX_THREAD_STACK_SIZE, SIM76XX_THREAD_PRIORITY, 20); - if (tid) - { - rt_thread_startup(tid); - } - else - { - LOG_E("Create AT initialization thread fail!"); - } -#else - sim76xx_init_thread_entry(RT_NULL); -#endif - - return RT_EOK; -} - -int sim76xx_ping(int argc, char **argv) -{ - at_response_t resp = RT_NULL; - - if (argc != 2) - { - rt_kprintf("Please input: at_ping \n"); - return -RT_ERROR; - } - - resp = at_create_resp(64, 0, rt_tick_from_millisecond(5000)); - if (!resp) - { - rt_kprintf("No memory for response structure!\n"); - return -RT_ENOMEM; - } - - if (at_exec_cmd(resp, "AT+CPING=\"%s\",1,4,64,1000,10000,255", argv[1]) < 0) - { - if (resp) - { - at_delete_resp(resp); - } - rt_kprintf("AT send ping commands error!\n"); - return -RT_ERROR; - } - - if (resp) - { - at_delete_resp(resp); - } - - return RT_EOK; -} - -int sim76xx_connect(int argc, char **argv) -{ - int32_t port; - - if (argc != 3) - { - rt_kprintf("Please input: at_connect \n"); - return -RT_ERROR; - } - sscanf(argv[2],"%d",&port); - sim76xx_socket_connect(0, argv[1], port, AT_SOCKET_TCP, 1); - - return RT_EOK; -} - -int sim76xx_close(int argc, char **argv) -{ - if (sim76xx_socket_close(0) < 0) - { - rt_kprintf("sim76xx_socket_close fail\n"); - } - else - { - rt_kprintf("sim76xx_socket_closeed\n"); - } - return RT_EOK; -} - -int sim76xx_send(int argc, char **argv) -{ - const char *buff = "1234567890\n"; - if (sim76xx_socket_send(0, buff, 11, AT_SOCKET_TCP) < 0) - { - rt_kprintf("sim76xx_socket_send fail\n"); - } - - return RT_EOK; -} - -int sim76xx_domain(int argc, char **argv) -{ - char ip[16]; - if (sim76xx_domain_resolve("www.baidu.com", ip) < 0) - { - rt_kprintf("sim76xx_socket_send fail\n"); - } - else - { - rt_kprintf("baidu.com : %s\n", ip); - } - - return RT_EOK; -} - -int sim76xx_ifconfig(void) -{ - at_response_t resp = RT_NULL; - char resp_arg[AT_CMD_MAX_LEN] = { 0 }; - rt_err_t result = RT_EOK; - - resp = at_create_resp(128, 2, rt_tick_from_millisecond(300)); - if (!resp) - { - rt_kprintf("No memory for response structure!\n"); - return -RT_ENOMEM; - } - - /* Show PDP address */ - AT_SEND_CMD(resp, "AT+CGPADDR"); - at_resp_parse_line_args_by_kw(resp, "+CGPADDR:", "+CGPADDR: %s", &resp_arg); - rt_kprintf("IP adress : %s\n", resp_arg); - -__exit: - if (resp) - { - at_delete_resp(resp); - } - - return result; -} - - -#ifdef FINSH_USING_MSH -#include -MSH_CMD_EXPORT_ALIAS(sim76xx_net_init, at_net_init, initialize AT network); -MSH_CMD_EXPORT_ALIAS(sim76xx_ping, at_ping, AT ping network host); -MSH_CMD_EXPORT_ALIAS(sim76xx_ifconfig, at_ifconfig, list the information of network interfaces); -MSH_CMD_EXPORT_ALIAS(sim76xx_connect, at_connect, AT connect network host); -MSH_CMD_EXPORT_ALIAS(sim76xx_close, at_close, AT close a socket); -MSH_CMD_EXPORT_ALIAS(sim76xx_send, at_send, AT send a pack); -MSH_CMD_EXPORT_ALIAS(sim76xx_domain, at_domain, AT domain resolve); -#endif - -static const struct at_device_ops sim76xx_socket_ops = { - sim76xx_socket_connect, - sim76xx_socket_close, - sim76xx_socket_send, - sim76xx_domain_resolve, - sim76xx_socket_set_event_cb, -}; - -static int sim76xx_netdev_add(const char *netdev_name) -{ -#define ETHERNET_MTU 1500 -#define HWADDR_LEN 8 - struct netdev *netdev = RT_NULL; - int result = 0; - - RT_ASSERT(netdev_name); - - netdev = (struct netdev *) rt_calloc(1, sizeof(struct netdev)); - if (netdev == RT_NULL) - { - return RT_NULL; - } - - netdev->mtu = ETHERNET_MTU; - netdev->hwaddr_len = HWADDR_LEN; - netdev->ops = RT_NULL; - -#ifdef SAL_USING_AT - extern int sal_at_netdev_set_pf_info(struct netdev *netdev); - /* set the network interface socket/netdb operations */ - sal_at_netdev_set_pf_info(netdev); -#endif - - result = netdev_register(netdev, netdev_name, RT_NULL); - - /*TODO: improve netdev adaptation */ - netdev_low_level_set_status(netdev, RT_TRUE); - netdev_low_level_set_link_status(netdev, RT_TRUE); - netdev_low_level_set_dhcp_status(netdev, RT_TRUE); - netdev->flags |= NETDEV_FLAG_INTERNET_UP; - - return result; -} - -static int at_socket_device_init(void) -{ - /* create current AT socket event */ - at_socket_event = rt_event_create("at_se", RT_IPC_FLAG_FIFO); - if (at_socket_event == RT_NULL) - { - LOG_E("RT AT client port initialize failed! at_sock_event create failed!"); - return -RT_ENOMEM; - } - - /* create current AT socket event lock */ - at_event_lock = rt_mutex_create("at_se", RT_IPC_FLAG_FIFO); - if (at_event_lock == RT_NULL) - { - LOG_E("RT AT client port initialize failed! at_sock_lock create failed!"); - rt_event_delete(at_socket_event); - return -RT_ENOMEM; - } - - /* initialize AT client */ - at_client_init(AT_DEVICE_NAME, AT_DEVICE_RECV_BUFF_LEN); - - /* register URC data execution function */ - at_set_urc_table(urc_table, sizeof(urc_table) / sizeof(urc_table[0])); - - /* add network interface device to netdev list */ - if (sim76xx_netdev_add(SIM76XX_NETDEV_NAME) < 0) - { - LOG_E("SIM76xx network interface device(%d) add failed.", SIM76XX_NETDEV_NAME); - return -RT_ENOMEM; - } - - /* initialize sim76xx pin config */ - rt_pin_mode(AT_DEVICE_POWER_PIN, PIN_MODE_OUTPUT); - rt_pin_mode(AT_DEVICE_STATUS_PIN, PIN_MODE_INPUT); - - /* initialize sim76xx network */ - sim76xx_net_init(); - - /* set sim76xx AT Socket options */ - at_socket_device_register(&sim76xx_socket_ops); - - return RT_EOK; -} -INIT_APP_EXPORT(at_socket_device_init); - -#endif /* AT_DEVICE_SIM76XX */ diff --git a/at_socket_sim800c.c b/at_socket_sim800c.c deleted file mode 100644 index dd7256c..0000000 --- a/at_socket_sim800c.c +++ /dev/null @@ -1,1302 +0,0 @@ -/* - * File : at_socket_sim800c.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-12 malongwei first version - */ - -#include -#include - -#include -#include -#include - -#include -#include - -#if !defined(RT_USING_NETDEV) -#error "This RT-Thread version is older, please check and updata laster RT-Thread!" -#else -#include -#include -#endif /* RT_USING_NETDEV */ - -#if !defined(AT_SW_VERSION_NUM) || AT_SW_VERSION_NUM < 0x10200 -#error "This AT Client version is older, please check and update latest AT Client!" -#endif - -#define LOG_TAG "at.sim800c" -#include - -#ifdef AT_DEVICE_SIM800C - -#define SIM800C_NETDEV_NAME "sim800c" - -#define SIM800C_MODULE_SEND_MAX_SIZE 1000 -#define SIM800C_WAIT_CONNECT_TIME 5000 -#define SIM800C_THREAD_STACK_SIZE 1024 -#define SIM800C_THREAD_PRIORITY (RT_THREAD_PRIORITY_MAX/2) - -/* set real event by current socket and current state */ -#define SET_EVENT(socket, event) (((socket + 1) << 16) | (event)) - -/* AT socket event type */ -#define SIM800C_EVENT_CONN_OK (1L << 0) -#define SIM800C_EVENT_SEND_OK (1L << 1) -#define SIM800C_EVENT_RECV_OK (1L << 2) -#define SIM800C_EVNET_CLOSE_OK (1L << 3) -#define SIM800C_EVENT_CONN_FAIL (1L << 4) -#define SIM800C_EVENT_SEND_FAIL (1L << 5) - -/* AT+CSTT command default*/ -char *CSTT_CHINA_MOBILE = "AT+CSTT=\"CMNET\""; -char *CSTT_CHINA_UNICOM = "AT+CSTT=\"UNINET\""; -char *CSTT_CHINA_TELECOM = "AT+CSTT=\"CTNET\""; - - -static int cur_socket; -static rt_event_t at_socket_event; -static rt_mutex_t at_event_lock; -static at_evt_cb_t at_evt_cb_set[] = { - [AT_SOCKET_EVT_RECV] = NULL, - [AT_SOCKET_EVT_CLOSED] = NULL, -}; - -static int at_socket_event_send(uint32_t event) -{ - return (int) rt_event_send(at_socket_event, event); -} - -static int at_socket_event_recv(uint32_t event, uint32_t timeout, rt_uint8_t option) -{ - int result = 0; - rt_uint32_t recved; - - result = rt_event_recv(at_socket_event, event, option | RT_EVENT_FLAG_CLEAR, timeout, &recved); - if (result != RT_EOK) - { - return -RT_ETIMEOUT; - } - - return recved; -} - -/** - * close socket by AT commands. - * - * @param current socket - * - * @return 0: close socket success - * -1: send AT commands error - * -2: wait socket event timeout - * -5: no memory - */ -static int sim800c_socket_close(int socket) -{ - int result = 0; - at_response_t resp = RT_NULL; - - resp = at_create_resp(128, 0, RT_TICK_PER_SECOND); - if (!resp) - { - LOG_E("No memory for response structure!"); - return -RT_ENOMEM; - } - - rt_mutex_take(at_event_lock, RT_WAITING_FOREVER); - cur_socket = socket; - - /* Clear socket close event */ - at_socket_event_recv(SET_EVENT(socket, SIM800C_EVNET_CLOSE_OK), 0, RT_EVENT_FLAG_OR); - - if (at_exec_cmd(resp, "AT+CIPCLOSE=%d", socket) < 0) - { - result = -RT_ERROR; - goto __exit; - } - - if (at_socket_event_recv(SET_EVENT(socket, SIM800C_EVNET_CLOSE_OK), rt_tick_from_millisecond(300*3), RT_EVENT_FLAG_AND) < 0) - { - LOG_E("socket (%d) close failed, wait close OK timeout.", socket); - result = -RT_ETIMEOUT; - goto __exit; - } - -__exit: - rt_mutex_release(at_event_lock); - - if (resp) - { - at_delete_resp(resp); - } - - return result; -} - - -/** - * create TCP/UDP client or server connect by AT commands. - * - * @param socket current socket - * @param ip server or client IP address - * @param port server or client port - * @param type connect socket type(tcp, udp) - * @param is_client connection is client - * - * @return 0: connect success - * -1: connect failed, send commands error or type error - * -2: wait socket event timeout - * -5: no memory - */ -static int sim800c_socket_connect(int socket, char *ip, int32_t port, enum at_socket_type type, rt_bool_t is_client) -{ - int result = 0, event_result = 0; - rt_bool_t retryed = RT_FALSE; - at_response_t resp = RT_NULL; - - RT_ASSERT(ip); - RT_ASSERT(port >= 0); - - resp = at_create_resp(128, 0, 5 * RT_TICK_PER_SECOND); - if (!resp) - { - LOG_E("No memory for response structure!"); - return -RT_ENOMEM; - } - - /* lock AT socket connect */ - rt_mutex_take(at_event_lock, RT_WAITING_FOREVER); - -__retry: - - /* Clear socket connect event */ - at_socket_event_recv(SET_EVENT(socket, SIM800C_EVENT_CONN_OK | SIM800C_EVENT_CONN_FAIL), 0, RT_EVENT_FLAG_OR); - - if (is_client) - { - switch (type) - { - case AT_SOCKET_TCP: - /* send AT commands(eg: AT+QIOPEN=0,"TCP","x.x.x.x", 1234) to connect TCP server */ - if (at_exec_cmd(resp, "AT+CIPSTART=%d,\"TCP\",\"%s\",%d", socket, ip, port) < 0) - { - result = -RT_ERROR; - goto __exit; - } - break; - - case AT_SOCKET_UDP: - if (at_exec_cmd(resp, "AT+CIPSTART=%d,\"UDP\",\"%s\",%d", socket, ip, port) < 0) - { - result = -RT_ERROR; - goto __exit; - } - break; - - default: - LOG_E("Not supported connect type : %d.", type); - result = -RT_ERROR; - goto __exit; - } - } - - /* waiting result event from AT URC, the device default connection timeout is 75 seconds, but it set to 10 seconds is convenient to use.*/ - if (at_socket_event_recv(SET_EVENT(socket, 0), 10 * RT_TICK_PER_SECOND, RT_EVENT_FLAG_OR) < 0) - { - LOG_E("socket (%d) connect failed, wait connect result timeout.", socket); - result = -RT_ETIMEOUT; - goto __exit; - } - /* waiting OK or failed result */ - if ((event_result = at_socket_event_recv(SIM800C_EVENT_CONN_OK | SIM800C_EVENT_CONN_FAIL, 1 * RT_TICK_PER_SECOND, - RT_EVENT_FLAG_OR)) < 0) - { - LOG_E("socket (%d) connect failed, wait connect OK|FAIL timeout.", socket); - result = -RT_ETIMEOUT; - goto __exit; - } - /* check result */ - if (event_result & SIM800C_EVENT_CONN_FAIL) - { - if (!retryed) - { - LOG_E("socket (%d) connect failed, maybe the socket was not be closed at the last time and now will retry.", socket); - if (sim800c_socket_close(socket) < 0) - { - goto __exit; - } - retryed = RT_TRUE; - goto __retry; - } - LOG_E("socket (%d) connect failed, failed to establish a connection.", socket); - result = -RT_ERROR; - goto __exit; - } - -__exit: - /* unlock AT socket connect */ - rt_mutex_release(at_event_lock); - - if (resp) - { - at_delete_resp(resp); - } - - return result; -} - -/** - * send data to server or client by AT commands. - * - * @param socket current socket - * @param buff send buffer - * @param bfsz send buffer size - * @param type connect socket type(tcp, udp) - * - * @return >=0: the size of send success - * -1: send AT commands error or send data error - * -2: waited socket event timeout - * -5: no memory - */ -static int sim800c_socket_send(int socket, const char *buff, size_t bfsz, enum at_socket_type type) -{ - int result = 0, event_result = 0; - at_response_t resp = RT_NULL; - size_t cur_pkt_size = 0, sent_size = 0; - - RT_ASSERT(buff); - - resp = at_create_resp(128, 2, 5 * RT_TICK_PER_SECOND); - if (!resp) - { - LOG_E("No memory for response structure!"); - return -RT_ENOMEM; - } - - rt_mutex_take(at_event_lock, RT_WAITING_FOREVER); - - /* Clear socket connect event */ - at_socket_event_recv(SET_EVENT(socket, SIM800C_EVENT_SEND_OK | SIM800C_EVENT_SEND_FAIL), 0, RT_EVENT_FLAG_OR); - - /* set current socket for send URC event */ - cur_socket = socket; - /* set AT client end sign to deal with '>' sign.*/ - at_set_end_sign('>'); - - while (sent_size < bfsz) - { - if (bfsz - sent_size < SIM800C_MODULE_SEND_MAX_SIZE) - { - cur_pkt_size = bfsz - sent_size; - } - else - { - cur_pkt_size = SIM800C_MODULE_SEND_MAX_SIZE; - } - - /* send the "AT+QISEND" commands to AT server than receive the '>' response on the first line. */ - if (at_exec_cmd(resp, "AT+CIPSEND=%d,%d", socket, cur_pkt_size) < 0) - { - result = -RT_ERROR; - goto __exit; - } - - /* send the real data to server or client */ - result = (int) at_client_send(buff + sent_size, cur_pkt_size); - if (result == 0) - { - result = -RT_ERROR; - goto __exit; - } - - /* waiting result event from AT URC */ - if (at_socket_event_recv(SET_EVENT(socket, 0), 10 * RT_TICK_PER_SECOND, RT_EVENT_FLAG_OR) < 0) - { - LOG_E("socket (%d) send failed, wait connect result timeout.", socket); - result = -RT_ETIMEOUT; - goto __exit; - } - /* waiting OK or failed result */ - if ((event_result = at_socket_event_recv(SIM800C_EVENT_SEND_OK | SIM800C_EVENT_SEND_FAIL, 5 * RT_TICK_PER_SECOND, - RT_EVENT_FLAG_OR)) < 0) - { - LOG_E("socket (%d) send failed, wait connect OK|FAIL timeout.", socket); - result = -RT_ETIMEOUT; - goto __exit; - } - /* check result */ - if (event_result & SIM800C_EVENT_SEND_FAIL) - { - LOG_E("socket (%d) send failed, return failed.", socket); - result = -RT_ERROR; - goto __exit; - } - - if (type == AT_SOCKET_TCP) - { - //cur_pkt_size = cur_send_bfsz; - } - - sent_size += cur_pkt_size; - } - - -__exit: - /* reset the end sign for data conflict */ - at_set_end_sign(0); - - rt_mutex_release(at_event_lock); - - if (resp) - { - at_delete_resp(resp); - } - - return result; -} - -/** - * domain resolve by AT commands. - * - * @param name domain name - * @param ip parsed IP address, it's length must be 16 - * - * @return 0: domain resolve success - * -1: send AT commands error or response error - * -2: wait socket event timeout - * -5: no memory - */ -static int sim800c_domain_resolve(const char *name, char ip[16]) -{ -#define RESOLVE_RETRY 5 - - int i, result = RT_EOK; - char recv_ip[16] = { 0 }; - at_response_t resp = RT_NULL; - - RT_ASSERT(name); - RT_ASSERT(ip); - - /* The maximum response time is 14 seconds, affected by network status */ - resp = at_create_resp(128, 4, 14 * RT_TICK_PER_SECOND); - if (!resp) - { - LOG_E("No memory for response structure!"); - return -RT_ENOMEM; - } - - rt_mutex_take(at_event_lock, RT_WAITING_FOREVER); - - for (i = 0; i < RESOLVE_RETRY; i++) - { - int err_code = 0; - - if (at_exec_cmd(resp, "AT+CDNSGIP=\"%s\"", name) < 0) - { - result = -RT_ERROR; - goto __exit; - } - - /* domain name prase error options */ - if (at_resp_parse_line_args_by_kw(resp, "+CDNSGIP: 0", "+CDNSGIP: 0,%d", &err_code) > 0) - { - /* 3 - network error, 8 - dns common error */ - if (err_code == 3 || err_code == 8) - { - result = -RT_ERROR; - goto __exit; - } - } - - /* parse the third line of response data, get the IP address */ - if (at_resp_parse_line_args_by_kw(resp, "+CDNSGIP:", "%*[^,],%*[^,],\"%[^\"]", recv_ip) < 0) - { - rt_thread_mdelay(100); - /* resolve failed, maybe receive an URC CRLF */ - continue; - } - - if (strlen(recv_ip) < 8) - { - rt_thread_mdelay(100); - /* resolve failed, maybe receive an URC CRLF */ - continue; - } - else - { - strncpy(ip, recv_ip, 15); - ip[15] = '\0'; - //LOG_I("DNS IP:%s",ip); - break; - } - } - -__exit: - rt_mutex_release(at_event_lock); - - if (resp) - { - at_delete_resp(resp); - } - - return result; - -} - -/** - * set AT socket event notice callback - * - * @param event notice event - * @param cb notice callback - */ -static void sim800c_socket_set_event_cb(at_socket_evt_t event, at_evt_cb_t cb) -{ - if (event < sizeof(at_evt_cb_set) / sizeof(at_evt_cb_set[1])) - { - at_evt_cb_set[event] = cb; - } -} - -static void urc_connect_func(const char *data, rt_size_t size) -{ - int socket = 0; - - RT_ASSERT(data && size); - - sscanf(data, "%d,%*", &socket); - - if (strstr(data, "CONNECT OK")) - { - at_socket_event_send(SET_EVENT(socket, SIM800C_EVENT_CONN_OK)); - } - else if (strstr(data, "CONNECT FAIL")) - { - at_socket_event_send(SET_EVENT(socket, SIM800C_EVENT_CONN_FAIL)); - } -} - -static void urc_send_func(const char *data, rt_size_t size) -{ - RT_ASSERT(data && size); - - if (strstr(data, "SEND OK")) - { - at_socket_event_send(SET_EVENT(cur_socket, SIM800C_EVENT_SEND_OK)); - } - else if (strstr(data, "SEND FAIL")) - { - at_socket_event_send(SET_EVENT(cur_socket, SIM800C_EVENT_SEND_FAIL)); - } -} - -static void urc_close_func(const char *data, rt_size_t size) -{ - int socket = 0; - - RT_ASSERT(data && size); - - if (strstr(data, "CLOSE OK")) - { - at_socket_event_send(SET_EVENT(cur_socket, SIM800C_EVNET_CLOSE_OK)); - } - else if (strstr(data, "CLOSED")) - { - sscanf(data, "%d, CLOSED", &socket); - /* notice the socket is disconnect by remote */ - if (at_evt_cb_set[AT_SOCKET_EVT_CLOSED]) - { - at_evt_cb_set[AT_SOCKET_EVT_CLOSED](socket, AT_SOCKET_EVT_CLOSED, NULL, 0); - } - } -} - -static void urc_recv_func(const char *data, rt_size_t size) -{ - int socket = 0; - rt_size_t bfsz = 0, temp_size = 0; - rt_int32_t timeout; - char *recv_buf = RT_NULL, temp[8]; - - RT_ASSERT(data && size); - - /* get the current socket and receive buffer size by receive data */ - sscanf(data, "+RECEIVE,%d,%d:", &socket, (int *) &bfsz); - /* get receive timeout by receive buffer length */ - timeout = bfsz; - - if (socket < 0 || bfsz == 0) - return; - - recv_buf = rt_calloc(1, bfsz); - if (!recv_buf) - { - LOG_E("no memory for URC receive buffer (%d)!", bfsz); - /* read and clean the coming data */ - while (temp_size < bfsz) - { - if (bfsz - temp_size > sizeof(temp)) - { - at_client_recv(temp, sizeof(temp), timeout); - } - else - { - at_client_recv(temp, bfsz - temp_size, timeout); - } - temp_size += sizeof(temp); - } - return; - } - - /* sync receive data */ - if (at_client_recv(recv_buf, bfsz, timeout) != bfsz) - { - LOG_E("receive size(%d) data failed!", bfsz); - rt_free(recv_buf); - return; - } - - /* notice the receive buffer and buffer size */ - if (at_evt_cb_set[AT_SOCKET_EVT_RECV]) - { - at_evt_cb_set[AT_SOCKET_EVT_RECV](socket, AT_SOCKET_EVT_RECV, recv_buf, bfsz); - } -} - - -static void urc_func(const char *data, rt_size_t size) -{ - RT_ASSERT(data); - - LOG_I("URC data : %.*s", size, data); -} - -static const struct at_urc urc_table[] = { - {"RDY", "\r\n", urc_func}, - {"", ", CONNECT OK\r\n", urc_connect_func}, - {"", ", CONNECT FAIL\r\n", urc_connect_func}, - {"", ", SEND OK\r\n", urc_send_func}, - {"", ", SEND FAIL\r\n", urc_send_func}, - {"", ", CLOSE OK\r\n", urc_close_func}, - {"", ", CLOSED\r\n", urc_close_func}, - {"+RECEIVE,", "\r\n", urc_recv_func}, -}; - -#define AT_SEND_CMD(resp, resp_line, timeout, cmd) \ - do \ - { \ - if (at_exec_cmd(at_resp_set_info(resp, 128, resp_line, rt_tick_from_millisecond(timeout)), cmd) < 0) \ - { \ - result = -RT_ERROR; \ - goto __exit; \ - } \ - } while(0); \ - -static void sim800c_power_on(void) -{ - if (rt_pin_read(AT_DEVICE_STATUS_PIN) == PIN_HIGH) - return; - rt_pin_write(AT_DEVICE_POWER_PIN, PIN_HIGH); - while (rt_pin_read(AT_DEVICE_STATUS_PIN) == PIN_LOW) - { - rt_thread_mdelay(10); - } - rt_pin_write(AT_DEVICE_POWER_PIN, PIN_LOW); -} - -static void sim800c_power_off(void) -{ - if (rt_pin_read(AT_DEVICE_STATUS_PIN) == PIN_LOW) - return; - rt_pin_write(AT_DEVICE_POWER_PIN, PIN_HIGH); - while (rt_pin_read(AT_DEVICE_STATUS_PIN) == PIN_HIGH) - { - rt_thread_mdelay(10); - } - rt_pin_write(AT_DEVICE_POWER_PIN, PIN_LOW); -} - -static int sim800c_netdev_set_info(struct netdev *netdev); -static int sim800c_netdev_check_link_status(struct netdev *netdev); - -/* init for SIM800C */ -static void sim800c_init_thread_entry(void *parameter) -{ -#define CPIN_RETRY 10 -#define CSQ_RETRY 10 -#define CREG_RETRY 10 -#define CGREG_RETRY 20 - - at_response_t resp = RT_NULL; - int i, qimux; - char parsed_data[10]; - rt_err_t result = RT_EOK; - - rt_mutex_take(at_event_lock, RT_WAITING_FOREVER); - - while (1) - { - result = RT_EOK; - rt_memset(parsed_data, 0, sizeof(parsed_data)); - rt_thread_mdelay(500); - sim800c_power_on(); - rt_thread_mdelay(2000); - resp = at_create_resp(128, 0, rt_tick_from_millisecond(300)); - if (!resp) - { - LOG_E("No memory for response structure!"); - result = -RT_ENOMEM; - goto __exit; - } - LOG_D("Start initializing the SIM800C module"); - /* wait SIM800C startup finish */ - if (at_client_wait_connect(SIM800C_WAIT_CONNECT_TIME)) - { - result = -RT_ETIMEOUT; - goto __exit; - } - /* disable echo */ - AT_SEND_CMD(resp, 0, 300, "ATE0"); - /* get module version */ - AT_SEND_CMD(resp, 0, 300, "ATI"); - /* show module version */ - for (i = 0; i < (int)resp->line_counts - 1; i++) - { - LOG_D("%s", at_resp_get_line(resp, i + 1)); - } - /* check SIM card */ - for (i = 0; i < CPIN_RETRY; i++) - { - at_exec_cmd(at_resp_set_info(resp, 128, 2, 5 * RT_TICK_PER_SECOND), "AT+CPIN?"); - - if (at_resp_get_line_by_kw(resp, "READY")) - { - LOG_D("SIM card detection success"); - break; - } - rt_thread_mdelay(1000); - } - if (i == CPIN_RETRY) - { - LOG_E("SIM card detection failed!"); - result = -RT_ERROR; - goto __exit; - } - /* waiting for dirty data to be digested */ - rt_thread_mdelay(10); - - /* check the GSM network is registered */ - for (i = 0; i < CREG_RETRY; i++) - { - AT_SEND_CMD(resp, 0, 300, "AT+CREG?"); - at_resp_parse_line_args_by_kw(resp, "+CREG:", "+CREG: %s", &parsed_data); - if (!strncmp(parsed_data, "0,1", sizeof(parsed_data)) || !strncmp(parsed_data, "0,5", sizeof(parsed_data))) - { - LOG_D("GSM network is registered (%s)", parsed_data); - break; - } - rt_thread_mdelay(1000); - } - if (i == CREG_RETRY) - { - LOG_E("The GSM network is register failed (%s)", parsed_data); - result = -RT_ERROR; - goto __exit; - } - /* check the GPRS network is registered */ - for (i = 0; i < CGREG_RETRY; i++) - { - AT_SEND_CMD(resp, 0, 300, "AT+CGREG?"); - at_resp_parse_line_args_by_kw(resp, "+CGREG:", "+CGREG: %s", &parsed_data); - if (!strncmp(parsed_data, "0,1", sizeof(parsed_data)) || !strncmp(parsed_data, "0,5", sizeof(parsed_data))) - { - LOG_D("GPRS network is registered (%s)", parsed_data); - break; - } - rt_thread_mdelay(1000); - } - if (i == CGREG_RETRY) - { - LOG_E("The GPRS network is register failed (%s)", parsed_data); - result = -RT_ERROR; - goto __exit; - } - - /* check signal strength */ - for (i = 0; i < CSQ_RETRY; i++) - { - AT_SEND_CMD(resp, 0, 300, "AT+CSQ"); - at_resp_parse_line_args_by_kw(resp, "+CSQ:", "+CSQ: %s", &parsed_data); - if (strncmp(parsed_data, "99,99", sizeof(parsed_data))) - { - LOG_D("Signal strength: %s", parsed_data); - break; - } - rt_thread_mdelay(1000); - } - if (i == CSQ_RETRY) - { - LOG_E("Signal strength check failed (%s)", parsed_data); - result = -RT_ERROR; - goto __exit; - } - - /* the device default response timeout is 40 seconds, but it set to 15 seconds is convenient to use. */ - AT_SEND_CMD(resp, 2, 20 * 1000, "AT+CIPSHUT"); - - /* Set to multiple connections */ - AT_SEND_CMD(resp, 0, 300, "AT+CIPMUX?"); - at_resp_parse_line_args_by_kw(resp, "+CIPMUX:", "+CIPMUX: %d", &qimux); - if (qimux == 0) - { - AT_SEND_CMD(resp, 0, 300, "AT+CIPMUX=1"); - } - - AT_SEND_CMD(resp, 0, 300, "AT+COPS?"); - at_resp_parse_line_args_by_kw(resp, "+COPS:", "+COPS: %*[^\"]\"%[^\"]", &parsed_data); - if (strcmp(parsed_data, "CHINA MOBILE") == 0) - { - /* "CMCC" */ - LOG_I("%s", parsed_data); - AT_SEND_CMD(resp, 0, 300, CSTT_CHINA_MOBILE); - } - else if (strcmp(parsed_data, "CHN-UNICOM") == 0) - { - /* "UNICOM" */ - LOG_I("%s", parsed_data); - AT_SEND_CMD(resp, 0, 300, CSTT_CHINA_UNICOM); - } - else if (strcmp(parsed_data, "CHN-CT") == 0) - { - AT_SEND_CMD(resp, 0, 300, CSTT_CHINA_TELECOM); - /* "CT" */ - LOG_I("%s", parsed_data); - } - - /* the device default response timeout is 150 seconds, but it set to 20 seconds is convenient to use. */ - AT_SEND_CMD(resp, 0, 20 * 1000, "AT+CIICR"); - - AT_SEND_CMD(resp, 2, 300, "AT+CIFSR"); - if(at_resp_get_line_by_kw(resp, "ERROR") != RT_NULL) - { - LOG_E("Get the local address failed"); - result = -RT_ERROR; - goto __exit; - } - - __exit: - if (resp) - { - at_delete_resp(resp); - } - - if (!result) - { - LOG_I("AT network initialize success!"); - rt_mutex_release(at_event_lock); - break; - } - else - { - LOG_E("AT network initialize failed (%d)!", result); - sim800c_power_off(); - } - - rt_thread_mdelay(1000); - } - - /* set network interface device status and address information */ - sim800c_netdev_set_info(netdev_get_by_name(SIM800C_NETDEV_NAME)); - sim800c_netdev_check_link_status(netdev_get_by_name(SIM800C_NETDEV_NAME)); -} - -int sim800c_net_init(void) -{ -#ifdef PKG_AT_INIT_BY_THREAD - rt_thread_t tid; - - tid = rt_thread_create("sim800c_net_init", sim800c_init_thread_entry, RT_NULL, SIM800C_THREAD_STACK_SIZE, SIM800C_THREAD_PRIORITY, 20); - if (tid) - { - rt_thread_startup(tid); - } - else - { - LOG_E("Create AT initialization thread fail!"); - } -#else - sim800c_init_thread_entry(RT_NULL); -#endif - - return RT_EOK; -} - -#ifdef FINSH_USING_MSH -#include -MSH_CMD_EXPORT_ALIAS(sim800c_net_init, at_net_init, initialize AT network); -#endif - -static const struct at_device_ops sim800c_socket_ops = { - sim800c_socket_connect, - sim800c_socket_close, - sim800c_socket_send, - sim800c_domain_resolve, - sim800c_socket_set_event_cb, -}; - -/* set sim800c network interface device status and address information */ -static int sim800c_netdev_set_info(struct netdev *netdev) -{ -#define SIM800C_IEMI_RESP_SIZE 32 -#define SIM800C_IPADDR_RESP_SIZE 32 -#define SIM800C_DNS_RESP_SIZE 96 -#define SIM800C_INFO_RESP_TIMO rt_tick_from_millisecond(300) - - int result = RT_EOK; - at_response_t resp = RT_NULL; - ip_addr_t addr; - - if (netdev == RT_NULL) - { - LOG_E("Input network interface device is NULL.\n"); - return -RT_ERROR; - } - - rt_mutex_take(at_event_lock, RT_WAITING_FOREVER); - - /* set network interface device status */ - netdev_low_level_set_status(netdev, RT_TRUE); - netdev_low_level_set_link_status(netdev, RT_TRUE); - netdev_low_level_set_dhcp_status(netdev, RT_TRUE); - - resp = at_create_resp(SIM800C_IEMI_RESP_SIZE, 0, SIM800C_INFO_RESP_TIMO); - if (resp == RT_NULL) - { - LOG_E("SIM800C set IP address failed, no memory for response object."); - result = -RT_ENOMEM; - goto __exit; - } - - /* set network interface device hardware address(IEMI) */ - { - #define SIM800C_NETDEV_HWADDR_LEN 8 - #define SIM800C_IEMI_LEN 15 - - char iemi[SIM800C_IEMI_LEN] = {0}; - int i = 0, j = 0; - - /* send "AT+GSN" commond to get device IEMI */ - if (at_exec_cmd(resp, "AT+GSN") < 0) - { - result = -RT_ERROR; - goto __exit; - } - - if (at_resp_parse_line_args(resp, 2, "%s", iemi) <= 0) - { - LOG_E("Prase \"AT+GSN\" commands resposne data error!"); - result = -RT_ERROR; - goto __exit; - } - - LOG_D("SIM800C IEMI number: %s", iemi); - - netdev->hwaddr_len = SIM800C_NETDEV_HWADDR_LEN; - /* get hardware address by IEMI */ - for (i = 0, j = 0; i < SIM800C_NETDEV_HWADDR_LEN && j < SIM800C_IEMI_LEN; i++, j+=2) - { - if (j != SIM800C_IEMI_LEN - 1) - { - netdev->hwaddr[i] = (iemi[j] - '0') * 10 + (iemi[j + 1] - '0'); - } - else - { - netdev->hwaddr[i] = (iemi[j] - '0'); - } - } - } - - /* set network interface device IP address */ - { - #define IP_ADDR_SIZE_MAX 16 - char ipaddr[IP_ADDR_SIZE_MAX] = {0}; - - at_resp_set_info(resp, SIM800C_IPADDR_RESP_SIZE, 2, SIM800C_INFO_RESP_TIMO); - - /* send "AT+CIFSR" commond to get IP address */ - if (at_exec_cmd(resp, "AT+CIFSR") < 0) - { - result = -RT_ERROR; - goto __exit; - } - - if (at_resp_parse_line_args_by_kw(resp, ".", "%s", ipaddr) <= 0) - { - LOG_E("Prase \"AT+CIFSR\" commands resposne data error!"); - result = -RT_ERROR; - goto __exit; - } - - LOG_D("SIM800C IP address: %s", ipaddr); - - /* set network interface address information */ - inet_aton(ipaddr, &addr); - netdev_low_level_set_ipaddr(netdev, &addr); - } - - /* set network interface device dns server */ - { - #define DNS_ADDR_SIZE_MAX 16 - char dns_server1[DNS_ADDR_SIZE_MAX] = {0}, dns_server2[DNS_ADDR_SIZE_MAX] = {0}; - - at_resp_set_info(resp, SIM800C_DNS_RESP_SIZE, 0, SIM800C_INFO_RESP_TIMO); - - /* send "AT+CDNSCFG?" commond to get DNS servers address */ - if (at_exec_cmd(resp, "AT+CDNSCFG?") < 0) - { - result = -RT_ERROR; - goto __exit; - } - - if (at_resp_parse_line_args_by_kw(resp, "PrimaryDns:", "PrimaryDns:%s", dns_server1) <= 0 || - at_resp_parse_line_args_by_kw(resp, "SecondaryDns:", "SecondaryDns:%s", dns_server2) <= 0) - { - LOG_E("Prase \"AT+CDNSCFG?\" commands resposne data error!"); - result = -RT_ERROR; - goto __exit; - } - - LOG_D("SIM800C primary DNS server address: %s", dns_server1); - LOG_D("SIM800C secondary DNS server address: %s", dns_server2); - - inet_aton(dns_server1, &addr); - netdev_low_level_set_dns_server(netdev, 0, &addr); - - inet_aton(dns_server2, &addr); - netdev_low_level_set_dns_server(netdev, 1, &addr); - } - -__exit: - if (resp) - { - at_delete_resp(resp); - } - - rt_mutex_release(at_event_lock); - - return result; -} - -static void check_link_status_entry(void *parameter) -{ -#define SIM800C_LINK_STATUS_OK 1 -#define SIM800C_LINK_RESP_SIZE 64 -#define SIM800C_LINK_RESP_TIMO (3 * RT_TICK_PER_SECOND) -#define SIM800C_LINK_DELAY_TIME (30 * RT_TICK_PER_SECOND) - - struct netdev *netdev = (struct netdev *)parameter; - at_response_t resp = RT_NULL; - int result_code, link_status; - - resp = at_create_resp(SIM800C_LINK_RESP_SIZE, 0, SIM800C_LINK_RESP_TIMO); - if (resp == RT_NULL) - { - LOG_E("sim800c set check link status failed, no memory for response object."); - return; - } - - while (1) - { - rt_mutex_take(at_event_lock, RT_WAITING_FOREVER); - - /* send "AT+CGREG?" commond to check netweork interface device link status */ - if (at_exec_cmd(resp, "AT+CGREG?") < 0) - { - rt_mutex_release(at_event_lock); - rt_thread_mdelay(SIM800C_LINK_DELAY_TIME); - - continue; - } - - link_status = -1; - at_resp_parse_line_args_by_kw(resp, "+CGREG:", "+CGREG: %d,%d", &result_code, &link_status); - - /* check the network interface device link status */ - if ((SIM800C_LINK_STATUS_OK == link_status) != netdev_is_link_up(netdev)) - { - netdev_low_level_set_link_status(netdev, (SIM800C_LINK_STATUS_OK == link_status)); - } - - rt_mutex_release(at_event_lock); - rt_thread_mdelay(SIM800C_LINK_DELAY_TIME); - } -} - -static int sim800c_netdev_check_link_status(struct netdev *netdev) -{ -#define SIM800C_LINK_THREAD_STACK_SIZE 512 -#define SIM800C_LINK_THREAD_PRIORITY (RT_THREAD_PRIORITY_MAX - 2) -#define SIM800C_LINK_THREAD_TICK 20 - - rt_thread_t tid; - char tname[RT_NAME_MAX]; - - if (netdev == RT_NULL) - { - LOG_E("Input network interface device is NULL.\n"); - return -RT_ERROR; - } - - rt_memset(tname, 0x00, sizeof(tname)); - rt_snprintf(tname, RT_NAME_MAX, "%s_link", netdev->name); - - tid = rt_thread_create(tname, check_link_status_entry, (void *)netdev, - SIM800C_LINK_THREAD_STACK_SIZE, SIM800C_LINK_THREAD_PRIORITY, SIM800C_LINK_THREAD_TICK); - if (tid) - { - rt_thread_startup(tid); - } - - return RT_EOK; -} - -static int sim800c_netdev_set_up(struct netdev *netdev) -{ - netdev_low_level_set_status(netdev, RT_TRUE); - LOG_D("The network interface device(%s) set up status", netdev->name); - - return RT_EOK; -} - -static int sim800c_netdev_set_down(struct netdev *netdev) -{ - netdev_low_level_set_status(netdev, RT_FALSE); - LOG_D("The network interface device(%s) set down status", netdev->name); - return RT_EOK; -} - -static int sim800c_netdev_set_dns_server(struct netdev *netdev, uint8_t dns_num, ip_addr_t *dns_server) -{ -#define SIM800C_DNS_RESP_LEN 8 -#define SIM800C_DNS_RESP_TIMEO rt_tick_from_millisecond(300) - - at_response_t resp = RT_NULL; - int result = RT_EOK; - - RT_ASSERT(netdev); - RT_ASSERT(dns_server); - - rt_mutex_take(at_event_lock, RT_WAITING_FOREVER); - - resp = at_create_resp(SIM800C_DNS_RESP_LEN, 0, SIM800C_DNS_RESP_TIMEO); - if (resp == RT_NULL) - { - LOG_D("sim800c set dns server failed, no memory for response object."); - result = -RT_ENOMEM; - goto __exit; - } - - /* send "AT+CDNSCFG=[,]" commond to set dns servers */ - if (at_exec_cmd(resp, "AT+CDNSCFG=\"%s\"", inet_ntoa(*dns_server)) < 0) - { - result = -RT_ERROR; - goto __exit; - } - - netdev_low_level_set_dns_server(netdev, dns_num, dns_server); - -__exit: - if (resp) - { - at_delete_resp(resp); - } - - rt_mutex_release(at_event_lock); - - return result; -} - -static int sim800c_netdev_ping(struct netdev *netdev, const char *host, size_t data_len, uint32_t timeout, struct netdev_ping_resp *ping_resp) -{ -#define SIM800C_PING_RESP_SIZE 128 -#define SIM800C_PING_IP_SIZE 16 -#define SIM800C_PING_TIMEO (5 * RT_TICK_PER_SECOND) - -#define SIM800C_PING_ERR_TIME 600 -#define SIM800C_PING_ERR_TTL 255 - - int result = RT_EOK; - at_response_t resp = RT_NULL; - char ip_addr[SIM800C_PING_IP_SIZE] = {0}; - int response, time, ttl, i; - - RT_ASSERT(netdev); - RT_ASSERT(host); - RT_ASSERT(ping_resp); - - for (i = 0; i < rt_strlen(host) && !isalpha(host[i]); i++); - - if (i < strlen(host)) - { - /* check domain name is usable */ - if (sim800c_domain_resolve(host, ip_addr) < 0) - { - return -RT_ERROR; - } - rt_memset(ip_addr, 0x00, SIM800C_PING_IP_SIZE); - } - - rt_mutex_take(at_event_lock, RT_WAITING_FOREVER); - - resp = at_create_resp(SIM800C_PING_RESP_SIZE, 0, SIM800C_PING_TIMEO); - if (resp == RT_NULL) - { - LOG_D("sim800c set dns server failed, no memory for response object."); - result = -RT_ERROR; - goto __exit; - } - - /* send "AT+CIPPING=[,[,[,[,]]]]" commond to send ping request */ - if (at_exec_cmd(resp, "AT+CIPPING=%s,1,%d,%d,64", host, data_len, SIM800C_PING_TIMEO / (RT_TICK_PER_SECOND / 10)) < 0) - { - result = -RT_ERROR; - goto __exit; - } - - if (at_resp_parse_line_args_by_kw(resp, "+CIPPING:", "+CIPPING:%d,\"%[^\"]\",%d,%d", - &response, ip_addr, &time, &ttl) <= 0) - { - LOG_D("Prase \"AT+CIPPING\" commands resposne data error!"); - result = -RT_ERROR; - goto __exit; - } - - /* the ping request timeout expires, the response time settting to 600 and ttl setting to 255 */ - if (time == SIM800C_PING_ERR_TIME && ttl == SIM800C_PING_ERR_TTL) - { - result = -RT_ETIMEOUT; - goto __exit; - } - - inet_aton(ip_addr, &(ping_resp->ip_addr)); - ping_resp->data_len = data_len; - /* reply time, in units of 100 ms */ - ping_resp->ticks = time * 100; - ping_resp->ttl = ttl; - - __exit: - if (resp) - { - at_delete_resp(resp); - } - - rt_mutex_release(at_event_lock); - - return result; -} - -void sim800c_netdev_netstat(struct netdev *netdev) -{ - // TODO netstat support -} - -const struct netdev_ops sim800c_netdev_ops = -{ - sim800c_netdev_set_up, - sim800c_netdev_set_down, - - RT_NULL, /* not support set ip, netmask, gatway address */ - sim800c_netdev_set_dns_server, - RT_NULL, /* not support set DHCP status */ - - sim800c_netdev_ping, - sim800c_netdev_netstat, -}; - -static int sim800c_netdev_add(const char *netdev_name) -{ -#define SIM800C_NETDEV_MTU 1500 - struct netdev *netdev = RT_NULL; - - RT_ASSERT(netdev_name); - - netdev = (struct netdev *) rt_calloc(1, sizeof(struct netdev)); - if (netdev == RT_NULL) - { - return -RT_ENOMEM; - } - - netdev->mtu = SIM800C_NETDEV_MTU; - netdev->ops = &sim800c_netdev_ops; - -#ifdef SAL_USING_AT - extern int sal_at_netdev_set_pf_info(struct netdev *netdev); - /* set the network interface socket/netdb operations */ - sal_at_netdev_set_pf_info(netdev); -#endif - - return netdev_register(netdev, netdev_name, RT_NULL); -} - -static int at_socket_device_init(void) -{ - /* create current AT socket event */ - at_socket_event = rt_event_create("at_se", RT_IPC_FLAG_FIFO); - if (at_socket_event == RT_NULL) - { - LOG_E("AT client port initialize failed! at_sock_event create failed!"); - return -RT_ENOMEM; - } - - /* create current AT socket event lock */ - at_event_lock = rt_mutex_create("at_se", RT_IPC_FLAG_FIFO); - if (at_event_lock == RT_NULL) - { - LOG_E("AT client port initialize failed! at_sock_lock create failed!"); - rt_event_delete(at_socket_event); - return -RT_ENOMEM; - } - - /* initialize AT client */ - at_client_init(AT_DEVICE_NAME, AT_DEVICE_RECV_BUFF_LEN); - - /* register URC data execution function */ - at_set_urc_table(urc_table, sizeof(urc_table) / sizeof(urc_table[0])); - - /* add network interface device to netdev list */ - if (sim800c_netdev_add(SIM800C_NETDEV_NAME) < 0) - { - LOG_E("SIM800C network interface device(%d) add failed.", SIM800C_NETDEV_NAME); - return -RT_ENOMEM; - } - - /* initialize sim800c network */ - rt_pin_mode(AT_DEVICE_POWER_PIN, PIN_MODE_OUTPUT); - rt_pin_mode(AT_DEVICE_STATUS_PIN, PIN_MODE_INPUT); - sim800c_net_init(); - - /* set sim800c AT Socket options */ - at_socket_device_register(&sim800c_socket_ops); - - return RT_EOK; -} -INIT_APP_EXPORT(at_socket_device_init); - -#endif /* AT_DEVICE_SIM800C */ diff --git a/class/ec20/at_device_ec20.c b/class/ec20/at_device_ec20.c new file mode 100644 index 0000000..76e964c --- /dev/null +++ b/class/ec20/at_device_ec20.c @@ -0,0 +1,1040 @@ +/* + * File : at_device_ec20.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-12 chenyong first version + * 2018-08-12 Marcus port to ec20 + * 2019-05-13 chenyong multi AT socket client support + */ + +#include +#include + +#include + +#if !defined(AT_SW_VERSION_NUM) || AT_SW_VERSION_NUM < 0x10200 +#error "This AT Client version is older, please check and update latest AT Client!" +#endif + +#define LOG_TAG "at.dev" +#include + +#ifdef AT_DEVICE_USING_EC20 + +#define EC20_WAIT_CONNECT_TIME 5000 +#define EC20_THREAD_STACK_SIZE 1024 +#define EC20_THREAD_PRIORITY (RT_THREAD_PRIORITY_MAX/2) + +/* AT+QICSGP command default*/ +static char *QICSGP_CHINA_MOBILE = "AT+QICSGP=1,1,\"CMNET\",\"\",\"\",0"; +static char *QICSGP_CHINA_UNICOM = "AT+QICSGP=1,1,\"UNINET\",\"\",\"\",0"; +static char *QICSGP_CHINA_TELECOM = "AT+QICSGP=1,1,\"CTNET\",\"\",\"\",0"; + +static void at_cme_errcode_parse(int result) +{ + switch(result) + { + case 0 : LOG_E("%d : Phone failure", result); break; + case 1 : LOG_E("%d : No connection to phone", result); break; + case 2 : LOG_E("%d : Phone-adaptor link reserved", result); break; + case 3 : LOG_E("%d : Operation not allowed", result); break; + case 4 : LOG_E("%d : Operation not supported", result); break; + case 5 : LOG_E("%d : PH-SIM PIN required", result); break; + case 6 : LOG_E("%d : PH-FSIM PIN required", result); break; + case 7 : LOG_E("%d : PH-FSIM PUK required", result); break; + case 10 : LOG_E("%d : SIM not inserted", result); break; + case 11 : LOG_E("%d : SIM PIN required", result); break; + case 12 : LOG_E("%d : SIM PUK required", result); break; + case 13 : LOG_E("%d : SIM failure", result); break; + case 14 : LOG_E("%d : SIM busy", result); break; + case 15 : LOG_E("%d : SIM wrong", result); break; + case 16 : LOG_E("%d : Incorrect password", result); break; + case 17 : LOG_E("%d : SIM PIN2 required", result); break; + case 18 : LOG_E("%d : SIM PUK2 required", result); break; + case 20 : LOG_E("%d : Memory full", result); break; + case 21 : LOG_E("%d : Invalid index", result); break; + case 22 : LOG_E("%d : Not found", result); break; + case 23 : LOG_E("%d : Memory failure", result); break; + case 24 : LOG_E("%d : Text string too long", result); break; + case 25 : LOG_E("%d : Invalid characters in text string", result); break; + case 26 : LOG_E("%d : Dial string too long", result); break; + case 27 : LOG_E("%d : Invalid characters in dial string", result); break; + case 30 : LOG_E("%d : No network service", result); break; + case 31 : LOG_E("%d : Network timeout", result); break; + case 32 : LOG_E("%d : Network not allowed - emergency calls only", result); break; + case 40 : LOG_E("%d : Network personalization PIN required", result); break; + case 41 : LOG_E("%d : Network personalization PUK required", result); break; + case 42 : LOG_E("%d : Network subset personalization PIN required", result); break; + case 43 : LOG_E("%d : Network subset personalization PUK required", result); break; + case 44 : LOG_E("%d : Service provider personalization PIN required", result); break; + case 45 : LOG_E("%d : Service provider personalization PUK required", result); break; + case 46 : LOG_E("%d : Corporate personalization PIN required", result); break; + case 47 : LOG_E("%d : Corporate personalization PUK required", result); break; + case 901 : LOG_E("%d : Audio unknown error", result); break; + case 902 : LOG_E("%d : Audio invalid parameters", result); break; + case 903 : LOG_E("%d : Audio operation not supported", result); break; + case 904 : LOG_E("%d : Audio device busy", result); break; + default : LOG_E("%d : Unknown err code", result); break; + } +} + +static void at_cms_errcode_parse(int result) +{ + switch(result) + { + case 300 : LOG_E("%d : ME failure", result); break; + case 301 : LOG_E("%d : SMS ME reserved", result); break; + case 302 : LOG_E("%d : Operation not allowed", result); break; + case 303 : LOG_E("%d : Operation not supported", result); break; + case 304 : LOG_E("%d : Invalid PDU mode", result); break; + case 305 : LOG_E("%d : Invalid text mode", result); break; + case 310 : LOG_E("%d : SIM not inserted", result); break; + case 311 : LOG_E("%d : SIM pin necessary", result); break; + case 312 : LOG_E("%d : PH SIM pin necessary", result); break; + case 313 : LOG_E("%d : SIM failure", result); break; + case 314 : LOG_E("%d : SIM busy", result); break; + case 315 : LOG_E("%d : SIM wrong", result); break; + case 316 : LOG_E("%d : SIM PUK required", result); break; + case 317 : LOG_E("%d : SIM PIN2 required", result); break; + case 318 : LOG_E("%d : SIM PUK2 required", result); break; + case 320 : LOG_E("%d : Memory failure", result); break; + case 321 : LOG_E("%d : Invalid memory index", result); break; + case 322 : LOG_E("%d : Memory full", result); break; + case 330 : LOG_E("%d : SMSC address unknown", result); break; + case 331 : LOG_E("%d : No network", result); break; + case 332 : LOG_E("%d : Network timeout", result); break; + case 500 : LOG_E("%d : Unknown", result); break; + case 512 : LOG_E("%d : SIM not ready", result); break; + case 513 : LOG_E("%d : Message length exceeds", result); break; + case 514 : LOG_E("%d : Invalid request parameters", result); break; + case 515 : LOG_E("%d : ME storage failure", result); break; + case 517 : LOG_E("%d : Invalid service mode", result); break; + case 528 : LOG_E("%d : More message to send state error", result); break; + case 529 : LOG_E("%d : MO SMS is not allow", result); break; + case 530 : LOG_E("%d : GPRS is suspended", result); break; + case 531 : LOG_E("%d : ME storage full", result); break; + default : LOG_E("%d : Unknown err code", result); break; + } +} + +static void at_mms_errcode_parse(int result)//MMS +{ + switch(result) + { + case 751 : LOG_E("%d : Unknown error", result); break; + case 752 : LOG_E("%d : URL length error", result); break; + case 753 : LOG_E("%d : URL error", result); break; + case 754 : LOG_E("%d : Invalid proxy type", result); break; + case 755 : LOG_E("%d : Proxy address error", result); break; + case 756 : LOG_E("%d : Invalid parameter", result); break; + case 757 : LOG_E("%d : Recipient address full", result); break; + case 758 : LOG_E("%d : CC recipient address full", result); break; + case 759 : LOG_E("%d : BCC recipient address full", result); break; + case 760 : LOG_E("%d : Attachments full", result); break; + case 761 : LOG_E("%d : File error", result); break; + case 762 : LOG_E("%d : No recipient", result); break; + case 763 : LOG_E("%d : File not found", result); break; + case 764 : LOG_E("%d : MMS busy", result); break; + case 765 : LOG_E("%d : Server response failed", result); break; + case 766 : LOG_E("%d : Error response of HTTP(S) post", result); break; + case 767 : LOG_E("%d : Invalid report of HTTP(S) post", result); break; + case 768 : LOG_E("%d : PDP activation failed", result); break; + case 769 : LOG_E("%d : PDP deactivated", result); break; + case 770 : LOG_E("%d : Socket creation failed", result); break; + case 771 : LOG_E("%d : Socket connection failed", result); break; + case 772 : LOG_E("%d : Socket read failed", result); break; + case 773 : LOG_E("%d : Socket write failed", result); break; + case 774 : LOG_E("%d : Socket closed", result); break; + case 775 : LOG_E("%d : Timeout", result); break; + case 776 : LOG_E("%d : Encode data error", result); break; + case 777 : LOG_E("%d : HTTP(S) decode data error", result); break; + default : LOG_E("%d : Unknown err code", result); break; + } +} + +static void ec20_power_on(struct at_device *device) +{ + struct at_device_ec20 *ec20 = RT_NULL; + + ec20 = (struct at_device_ec20 *)device->user_data; + + /* not nead to set pin configuration for ec20 device power on */ + if (ec20->power_pin == -1 || ec20->power_status_pin == -1) + { + return; + } + + if (rt_pin_read(ec20->power_status_pin) == PIN_HIGH) + { + return; + } + rt_pin_write(ec20->power_pin, PIN_HIGH); + + while (rt_pin_read(ec20->power_status_pin) == PIN_LOW) + { + rt_thread_mdelay(10); + } + rt_pin_write(ec20->power_pin, PIN_LOW); +} + +static void ec20_power_off(struct at_device *device) +{ + struct at_device_ec20 *ec20 = RT_NULL; + + ec20 = (struct at_device_ec20 *)device->user_data; + + /* not nead to set pin configuration for ec20 device power on */ + if (ec20->power_pin == -1 || ec20->power_status_pin == -1) + { + return; + } + + if (rt_pin_read(ec20->power_status_pin) == PIN_LOW) + { + return; + } + rt_pin_write(ec20->power_pin, PIN_HIGH); + + while (rt_pin_read(ec20->power_status_pin) == PIN_HIGH) + { + rt_thread_mdelay(10); + } + rt_pin_write(ec20->power_pin, PIN_LOW); +} + +/* ============================= ec20 network interface operations ============================= */ + +/* set ec20 network interface device status and address information */ +static int ec20_netdev_set_info(struct netdev *netdev) +{ +#define EC20_IEMI_RESP_SIZE 32 +#define EC20_IPADDR_RESP_SIZE 64 +#define EC20_DNS_RESP_SIZE 96 +#define EC20_INFO_RESP_TIMO rt_tick_from_millisecond(300) + + int result = RT_EOK; + ip_addr_t addr; + at_response_t resp = RT_NULL; + struct at_device *device = RT_NULL; + + if (netdev == RT_NULL) + { + LOG_E("input network interface device is NULL."); + return -RT_ERROR; + } + + device = at_device_get_by_name(AT_DEVICE_NAMETYPE_NETDEV, netdev->name); + if (device == RT_NULL) + { + LOG_E("get ec20 device by netdev name(%s) failed.", netdev->name); + return -RT_ERROR; + } + + /* set network interface device status */ + netdev_low_level_set_status(netdev, RT_TRUE); + netdev_low_level_set_link_status(netdev, RT_TRUE); + netdev_low_level_set_dhcp_status(netdev, RT_TRUE); + + resp = at_create_resp(EC20_IEMI_RESP_SIZE, 0, EC20_INFO_RESP_TIMO); + if (resp == RT_NULL) + { + LOG_E("no memory for ec20 device(%s) response structure.", device->name); + result = -RT_ENOMEM; + goto __exit; + } + + /* set network interface device hardware address(IEMI) */ + { + #define EC20_NETDEV_HWADDR_LEN 8 + #define EC20_IEMI_LEN 15 + + char iemi[EC20_IEMI_LEN] = {0}; + int i = 0, j = 0; + + /* send "AT+GSN" commond to get device IEMI */ + if (at_obj_exec_cmd(device->client, resp, "AT+GSN") < 0) + { + result = -RT_ERROR; + goto __exit; + } + + if (at_resp_parse_line_args(resp, 2, "%s", iemi) <= 0) + { + LOG_E("ec20 device(%s) prase \"AT+GSN\" commands resposne data error.", device->name); + result = -RT_ERROR; + goto __exit; + } + + LOG_D("ec20 deevice(%s) IEMI number: %s", device->name, iemi); + + netdev->hwaddr_len = EC20_NETDEV_HWADDR_LEN; + /* get hardware address by IEMI */ + for (i = 0, j = 0; i < EC20_NETDEV_HWADDR_LEN && j < EC20_IEMI_LEN; i++, j+=2) + { + if (j != EC20_IEMI_LEN - 1) + { + netdev->hwaddr[i] = (iemi[j] - '0') * 10 + (iemi[j + 1] - '0'); + } + else + { + netdev->hwaddr[i] = (iemi[j] - '0'); + } + } + } + + /* set network interface device IP address */ + { + #define IP_ADDR_SIZE_MAX 16 + char ipaddr[IP_ADDR_SIZE_MAX] = {0}; + + resp = at_resp_set_info(resp, EC20_IPADDR_RESP_SIZE, 0, EC20_INFO_RESP_TIMO); + + /* send "AT+QIACT?" commond to get IP address */ + if (at_obj_exec_cmd(device->client, resp, "AT+QIACT?") < 0) + { + result = -RT_ERROR; + goto __exit; + } + + /* parse response data "+QIACT: 1,,[,]" */ + if (at_resp_parse_line_args_by_kw(resp, "+QIACT:", "+QIACT: %*[^\"]\"%[^\"]", ipaddr) <= 0) + { + LOG_E("Prase \"AT+QIACT?\" commands resposne data error!"); + result = -RT_ERROR; + goto __exit; + } + + LOG_D("ec20 device(%s) IP address: %s", device->name, ipaddr); + + /* set network interface address information */ + inet_aton(ipaddr, &addr); + netdev_low_level_set_ipaddr(netdev, &addr); + } + + /* set network interface device dns server */ + { + #define DNS_ADDR_SIZE_MAX 16 + char dns_server1[DNS_ADDR_SIZE_MAX] = {0}, dns_server2[DNS_ADDR_SIZE_MAX] = {0}; + + resp = at_resp_set_info(resp, EC20_DNS_RESP_SIZE, 0, EC20_INFO_RESP_TIMO); + + /* send "AT+QIDNSCFG=1" commond to get DNS servers address */ + if (at_obj_exec_cmd(device->client, resp, "AT+QIDNSCFG=1") < 0) + { + result = -RT_ERROR; + goto __exit; + } + + /* parse response data "+QIDNSCFG: ,," */ + if (at_resp_parse_line_args_by_kw(resp, "+QIDNSCFG:", "+QIDNSCFG: 1,\"%[^\"]\",\"%[^\"]\"", + dns_server1, dns_server2) <= 0) + { + LOG_E("ec20 device(%s) prase \"AT+QIDNSCFG=1\" commands resposne data error.", device->name); + result = -RT_ERROR; + goto __exit; + } + + LOG_D("ec20 device(%s) primary DNS server address: %s", device->name, dns_server1); + LOG_D("ec20 devcie(%s) secondary DNS server address: %s", device->name, dns_server2); + + inet_aton(dns_server1, &addr); + netdev_low_level_set_dns_server(netdev, 0, &addr); + + inet_aton(dns_server2, &addr); + netdev_low_level_set_dns_server(netdev, 1, &addr); + } + +__exit: + if (resp) + { + at_delete_resp(resp); + } + + return result; +} + +static void ec20_check_link_status_entry(void *parameter) +{ +#define EC20_LINK_RESP_SIZE 64 +#define EC20_LINK_RESP_TIMO (3 * RT_TICK_PER_SECOND) +#define EC20_LINK_DELAY_TIME (30 * RT_TICK_PER_SECOND) + + int link_stat = 0; + at_response_t resp = RT_NULL; + struct at_device *device = RT_NULL; + struct netdev *netdev = (struct netdev *) parameter; + + device = at_device_get_by_name(AT_DEVICE_NAMETYPE_NETDEV, netdev->name); + if (device == RT_NULL) + { + LOG_E("get ec20 device by netdev name(%s) failed.", netdev->name); + return; + } + + resp = at_create_resp(EC20_LINK_RESP_SIZE, 0, EC20_LINK_RESP_TIMO); + if (resp == RT_NULL) + { + LOG_E("no memory for ec20 device(%s) response structure.", device->name); + return; + } + + while (1) + { + /* send "AT+CGREG" commond to check netweork interface device link status */ + if (at_obj_exec_cmd(device->client, resp, "AT+CGREG?") < 0) + { + if (netdev_is_link_up(netdev)) + { + netdev_low_level_set_link_status(netdev, RT_FALSE); + } + + rt_thread_mdelay(EC20_LINK_DELAY_TIME); + continue; + } + else + { + at_resp_parse_line_args_by_kw(resp, "+CGREG:", "+CGREG: %*d,%d", &link_stat); + + /* 1 Registered, home network,5 Registered, roaming */ + if (link_stat == 1 || link_stat == 5) + { + if (netdev_is_link_up(netdev) == RT_FALSE) + { + netdev_low_level_set_link_status(netdev, RT_TRUE); + } + } + else + { + if (netdev_is_link_up(netdev)) + { + netdev_low_level_set_link_status(netdev, RT_FALSE); + } + } + } + + rt_thread_mdelay(EC20_LINK_DELAY_TIME); + } +} + +static int ec20_netdev_check_link_status(struct netdev *netdev) +{ +#define EC20_LINK_THREAD_TICK 20 +#define EC20_LINK_THREAD_STACK_SIZE 1024 +#define EC20_LINK_THREAD_PRIORITY (RT_THREAD_PRIORITY_MAX - 2) + + rt_thread_t tid; + + if (netdev == RT_NULL) + { + LOG_E("input network interface device is NULL.\n"); + return -RT_ERROR; + } + + /* create ec20 link status polling thread */ + tid = rt_thread_create("ec20_link", ec20_check_link_status_entry, (void *) netdev, + EC20_LINK_THREAD_STACK_SIZE, EC20_LINK_THREAD_PRIORITY, EC20_LINK_THREAD_TICK); + if (tid != RT_NULL) + { + rt_thread_startup(tid); + } + + return RT_EOK; +} + +static int ec20_net_init(struct at_device *device); + +static int ec20_netdev_set_up(struct netdev *netdev) +{ + struct at_device *device = RT_NULL; + + device = at_device_get_by_name(AT_DEVICE_NAMETYPE_NETDEV, netdev->name); + if (device == RT_NULL) + { + LOG_E("get ec20 device by netdev name(%s) failed.", netdev->name); + return -RT_ERROR; + } + + if (device->is_init == RT_FALSE) + { + ec20_net_init(device); + device->is_init = RT_TRUE; + + netdev_low_level_set_status(netdev, RT_TRUE); + LOG_D("the network interface device(%s) set up status.", netdev->name); + } + + return RT_EOK; +} + +static int ec20_netdev_set_down(struct netdev *netdev) +{ + struct at_device *device = RT_NULL; + + device = at_device_get_by_name(AT_DEVICE_NAMETYPE_NETDEV, netdev->name); + if (device == RT_NULL) + { + LOG_E("get ec20 device by netdev name(%s) failed.", netdev->name); + return -RT_ERROR; + } + + if (device->is_init == RT_TRUE) + { + ec20_power_off(device); + device->is_init = RT_FALSE; + + netdev_low_level_set_status(netdev, RT_FALSE); + LOG_D("the network interface device(%s) set down status.", netdev->name); + } + + return RT_EOK; +} + +static int ec20_netdev_set_dns_server(struct netdev *netdev, uint8_t dns_num, ip_addr_t *dns_server) +{ +#define EC20_DNS_RESP_LEN 8 +#define EC20_DNS_RESP_TIMEO rt_tick_from_millisecond(300) + + int result = RT_EOK; + at_response_t resp = RT_NULL; + struct at_device *device = RT_NULL; + + RT_ASSERT(netdev); + RT_ASSERT(dns_server); + + device = at_device_get_by_name(AT_DEVICE_NAMETYPE_NETDEV, netdev->name); + if (device == RT_NULL) + { + LOG_E("get ec20 device by netdev name(%s) failed.", netdev->name); + return -RT_ERROR; + } + + resp = at_create_resp(EC20_DNS_RESP_LEN, 0, EC20_DNS_RESP_TIMEO); + if (resp == RT_NULL) + { + LOG_D("no memory for ec20 device(%s) response structure.", device->name); + result = -RT_ENOMEM; + goto __exit; + } + + /* send "AT+QIDNSCFG=[,]" commond to set dns servers */ + if (at_obj_exec_cmd(device->client, resp, "AT+QIDNSCFG=1,\"%s\"", inet_ntoa(*dns_server)) < 0) + { + result = -RT_ERROR; + goto __exit; + } + + netdev_low_level_set_dns_server(netdev, dns_num, dns_server); + +__exit: + if (resp) + { + at_delete_resp(resp); + } + + return result; +} + +#ifdef NETDEV_USING_PING +static int ec20_netdev_ping(struct netdev *netdev, const char *host, + size_t data_len, uint32_t timeout, struct netdev_ping_resp *ping_resp) +{ +#define EC20_PING_RESP_SIZE 128 +#define EC20_PING_IP_SIZE 16 +#define EC20_PING_TIMEO (5 * RT_TICK_PER_SECOND) + + rt_err_t result = RT_EOK; + int response = -1, recv_data_len, ping_time, ttl; + char ip_addr[EC20_PING_IP_SIZE] = {0}; + at_response_t resp = RT_NULL; + struct at_device *device = RT_NULL; + + RT_ASSERT(netdev); + RT_ASSERT(host); + RT_ASSERT(ping_resp); + + device = at_device_get_by_name(AT_DEVICE_NAMETYPE_NETDEV, netdev->name); + if (device == RT_NULL) + { + LOG_E("get ec20 device by netdev name(%s) failed.", netdev->name); + return -RT_ERROR; + } + + resp = at_create_resp(EC20_PING_RESP_SIZE, 4, EC20_PING_TIMEO); + if (resp == RT_NULL) + { + LOG_E("no memory for ec20 device(%s) response structure.", device->name); + return -RT_ENOMEM; + } + + /* send "AT+QPING=""[,[][,]]" commond to send ping request */ + if (at_obj_exec_cmd(device->client, resp, "AT+QPING=1,\"%s\",%d,1", host, timeout / RT_TICK_PER_SECOND) < 0) + { + result = -RT_ERROR; + goto __exit; + } + + at_resp_parse_line_args_by_kw(resp, "+QPING:", "+QPING:%d", &response); + /* Received the ping response from the server */ + if (response == 0) + { + if (at_resp_parse_line_args_by_kw(resp, "+QPING:", "+QPING:%d,\"%[^\"]\",%d,%d,%d", + &response, ip_addr, &recv_data_len, &ping_time, &ttl) <= 0) + { + result = -RT_ERROR; + goto __exit; + } + } + + /* prase response number */ + switch (response) + { + case 0: + inet_aton(ip_addr, &(ping_resp->ip_addr)); + ping_resp->data_len = recv_data_len; + ping_resp->ticks = ping_time; + ping_resp->ttl = ttl; + result = RT_EOK; + break; + case 569: + result = -RT_ETIMEOUT; + break; + default: + result = -RT_ERROR; + break; + } + +__exit: + if (resp) + { + at_delete_resp(resp); + } + + return result; +} +#endif /* NETDEV_USING_PING */ + +#ifdef NETDEV_USING_NETSTAT +static void ec20_netdev_netstat(struct netdev *netdev) +{ + // TODO +} +#endif /* NETDEV_USING_NETSTAT */ + +const struct netdev_ops ec20_netdev_ops = +{ + ec20_netdev_set_up, + ec20_netdev_set_down, + + RT_NULL, + ec20_netdev_set_dns_server, + RT_NULL, + +#ifdef NETDEV_USING_PING + ec20_netdev_ping, +#endif +#ifdef NETDEV_USING_NETSTAT + ec20_netdev_netstat, +#endif +}; + +static struct netdev *ec20_netdev_add(const char *netdev_name) +{ +#define ETHERNET_MTU 1500 +#define HWADDR_LEN 8 + struct netdev *netdev = RT_NULL; + + netdev = (struct netdev *)rt_calloc(1, sizeof(struct netdev)); + if (netdev == RT_NULL) + { + LOG_E("no memory for ec20 device(%s) netdev structure.", netdev->name); + return RT_NULL; + } + + netdev->mtu = ETHERNET_MTU; + netdev->ops = &ec20_netdev_ops; + netdev->hwaddr_len = HWADDR_LEN; + +#ifdef SAL_USING_AT + extern int sal_at_netdev_set_pf_info(struct netdev *netdev); + /* set the network interface socket/netdb operations */ + sal_at_netdev_set_pf_info(netdev); +#endif + + netdev_register(netdev, netdev_name, RT_NULL); + + return netdev; +} + +/* ============================= ec20 device operations ============================= */ + +#define AT_SEND_CMD(client, resp, resp_line, timeout, cmd) \ + do { \ + (resp) = at_resp_set_info((resp), 128, (resp_line), rt_tick_from_millisecond(timeout)); \ + if (at_obj_exec_cmd((client), (resp), (cmd)) < 0) \ + { \ + result = -RT_ERROR; \ + goto __exit; \ + } \ + } while(0) \ + +/* initialize for ec20 */ +static void ec20_init_thread_entry(void *parameter) +{ +#define INIT_RETRY 5 +#define CIMI_RETRY 10 +#define CSQ_RETRY 20 +#define CREG_RETRY 10 +#define CGREG_RETRY 20 + + int i, qi_arg[3] = {0}; + int retry_num = INIT_RETRY; + char parsed_data[20] = {0}; + rt_err_t result = RT_EOK; + at_response_t resp = RT_NULL; + struct at_device *device = (struct at_device *) parameter; + struct at_client *client = device->client; + + resp = at_create_resp(128, 0, rt_tick_from_millisecond(300)); + if (resp == RT_NULL) + { + LOG_E("no memory for ec20 device(%s) response structure.", device->name); + return; + } + + LOG_D("start initializing the ec20 device(%s).", device->name); + + while (retry_num--) + { + /* power on the ec20 device */ + ec20_power_on(device); + rt_thread_mdelay(1000); + + /* wait ec20 startup finish, send AT every 500ms, if receive OK, SYNC success*/ + if (at_client_obj_wait_connect(client, EC20_WAIT_CONNECT_TIME)) + { + result = -RT_ETIMEOUT; + goto __exit; + } + + /* set response format to ATV1 */ + AT_SEND_CMD(client, resp, 0, 300, "ATV1"); + /* disable echo */ + AT_SEND_CMD(client, resp, 0, 300, "ATE0"); + /* Use AT+CMEE=2 to enable result code and use verbose values */ + AT_SEND_CMD(client, resp, 0, 300, "AT+CMEE=2"); + /* Get the baudrate */ + AT_SEND_CMD(client, resp, 0, 300, "AT+IPR?"); + at_resp_parse_line_args_by_kw(resp, "+IPR:", "+IPR: %d", &i); + LOG_D("ec20 device(%s) baudrate %d", device->name, i); + /* get module version */ + AT_SEND_CMD(client, resp, 0, 300, "ATI"); + /* show module version */ + for (i = 0; i < (int) resp->line_counts - 1; i++) + { + LOG_D("%s", at_resp_get_line(resp, i + 1)); + } + /* Use AT+GSN to query the IMEI of module */ + AT_SEND_CMD(client, resp, 0, 300, "AT+GSN"); + + /* check SIM card */ + AT_SEND_CMD(client, resp, 2, 5 * 1000, "AT+CPIN?"); + if (!at_resp_get_line_by_kw(resp, "READY")) + { + LOG_E("ec20 device(%s) SIM card detection failed.", device->name); + result = -RT_ERROR; + goto __exit; + } + /* waiting for dirty data to be digested */ + rt_thread_mdelay(10); + + + /* Use AT+CIMI to query the IMSI of SIM card */ + // AT_SEND_CMD(client, resp, 2, 300, "AT+CIMI"); + i = 0; + resp = at_resp_set_info(resp, 128, 0, rt_tick_from_millisecond(300)); + while(at_obj_exec_cmd(device->client, resp, "AT+CIMI") < 0) + { + i++; + if(i > CIMI_RETRY) + { + LOG_E("ec20 device(%s) read CIMI failed.", device->name); + result = -RT_ERROR; + goto __exit; + } + rt_thread_mdelay(1000); + } + + /* Use AT+QCCID to query ICCID number of SIM card */ + AT_SEND_CMD(client, resp, 0, 300, "AT+QCCID"); + /* check signal strength */ + for (i = 0; i < CSQ_RETRY; i++) + { + AT_SEND_CMD(client, resp, 0, 300, "AT+CSQ"); + at_resp_parse_line_args_by_kw(resp, "+CSQ:", "+CSQ: %d,%d", &qi_arg[0], &qi_arg[1]); + if (qi_arg[0] != 99) + { + LOG_D("ec20 device(%s) signal strength: %d, channel bit error rate: %d", + device->name, qi_arg[0], qi_arg[1]); + break; + } + rt_thread_mdelay(1000); + } + if (i == CSQ_RETRY) + { + LOG_E("ec20 device(%s) signal strength check failed (%s)", device->name, parsed_data); + result = -RT_ERROR; + goto __exit; + } + /* check the GSM network is registered */ + for (i = 0; i < CREG_RETRY; i++) + { + AT_SEND_CMD(client, resp, 0, 300, "AT+CREG?"); + at_resp_parse_line_args_by_kw(resp, "+CREG:", "+CREG: %s", &parsed_data); + if (!rt_strncmp(parsed_data, "0,1", sizeof(parsed_data)) || + !rt_strncmp(parsed_data, "0,5", sizeof(parsed_data))) + { + LOG_D("ec20 device(%s) GSM network is registered(%s)", device->name, parsed_data); + break; + } + rt_thread_mdelay(1000); + } + if (i == CREG_RETRY) + { + LOG_E("ec20 device(%s) GSM network is register failed (%s)", device->name, parsed_data); + result = -RT_ERROR; + goto __exit; + } + /* check the GPRS network is registered */ + for (i = 0; i < CGREG_RETRY; i++) + { + AT_SEND_CMD(client, resp, 0, 300, "AT+CGREG?"); + at_resp_parse_line_args_by_kw(resp, "+CGREG:", "+CGREG: %s", &parsed_data); + if (!rt_strncmp(parsed_data, "0,1", sizeof(parsed_data)) || + !rt_strncmp(parsed_data, "0,5", sizeof(parsed_data))) + { + LOG_D("ec20 device(%s) GPRS network is registered(%s)", device->name, parsed_data); + break; + } + rt_thread_mdelay(1000); + } + if (i == CGREG_RETRY) + { + LOG_E("ec20 device(%s) GPRS network is register failed (%s)", device->name, parsed_data); + result = -RT_ERROR; + goto __exit; + } + /*Use AT+CEREG? to query current EPS Network Registration Status*/ + AT_SEND_CMD(client, resp, 0, 300, "AT+CEREG?"); + /* Use AT+COPS? to query current Network Operator */ + AT_SEND_CMD(client, resp, 0, 300, "AT+COPS?"); + at_resp_parse_line_args_by_kw(resp, "+COPS:", "+COPS: %*[^\"]\"%[^\"]", &parsed_data); + if(rt_strcmp(parsed_data,"CHINA MOBILE") == 0) + { + /* "CMCC" */ + LOG_I("ec20 device(%s) network operator: %s", device->name, parsed_data); + AT_SEND_CMD(client, resp, 0, 300, QICSGP_CHINA_MOBILE); + } + else if(strcmp(parsed_data,"CHN-UNICOM") == 0) + { + /* "UNICOM" */ + LOG_I("ec20 device(%s) network operator: %s", device->name, parsed_data); + AT_SEND_CMD(client, resp, 0, 300, QICSGP_CHINA_UNICOM); + } + else if(rt_strcmp(parsed_data,"CHN-CT") == 0) + { + /* "CT" */ + LOG_I("ec20 device(%s) network operator: %s", device->name, parsed_data); + AT_SEND_CMD(client, resp, 0, 300, QICSGP_CHINA_TELECOM); + } + /* Enable automatic time zone update via NITZ and update LOCAL time to RTC */ + AT_SEND_CMD(client, resp, 0, 300, "AT+CTZU=3"); + /* Get RTC time */ + AT_SEND_CMD(client, resp, 0, 300, "AT+CCLK?"); + + /* Deactivate context profile */ + AT_SEND_CMD(client, resp, 0, 40 * 1000, "AT+QIDEACT=1"); + /* Activate context profile */ + AT_SEND_CMD(client, resp, 0, 150 * 1000, "AT+QIACT=1"); + /* Query the status of the context profile */ + AT_SEND_CMD(client, resp, 0, 150 * 1000, "AT+QIACT?"); + at_resp_parse_line_args_by_kw(resp, "+QIACT:", "+QIACT: %*[^\"]\"%[^\"]", &parsed_data); + LOG_I("ec20 device(%s) IP address: %s", device->name, parsed_data); + result = RT_EOK; + + __exit: + if (result == RT_EOK) + { + break; + } + else + { + /* power off the ec20 device */ + ec20_power_off(device); + rt_thread_mdelay(1000); + + LOG_I("ec20 device(%s) initialize retry...", device->name); + } + } + + if (resp) + { + at_delete_resp(resp); + } + + if (result == RT_EOK) + { + /* set network interface device status and address information */ + ec20_netdev_set_info(device->netdev); + ec20_netdev_check_link_status(device->netdev); + + LOG_I("ec20 device(%s) network initialize success.", device->name); + } + else + { + LOG_E("ec20 device(%s) network initialize failed(%d).", device->name, result); + } + +} + +/* ec20 device network initialize */ +static int ec20_net_init(struct at_device *device) +{ +#ifdef AT_DEVICE_EC20_INIT_ASYN + rt_thread_t tid; + tid = rt_thread_create("ec20_net_init", ec20_init_thread_entry, (void *) device, + EC20_THREAD_STACK_SIZE, EC20_THREAD_PRIORITY, 20); + if (tid) + { + rt_thread_startup(tid); + } + else + { + LOG_E("create ec20 device(%s) initialization thread failed.", device->name); + return -RT_ERROR; + } +#else + ec20_init_thread_entry(device); +#endif /* AT_DEVICE_EC20_INIT_ASYN */ + + return RT_EOK; +} + +static int ec20_init(struct at_device *device) +{ + struct at_device_ec20 *ec20 = (struct at_device_ec20 *) device->user_data; + + /* initialize AT client */ + at_client_init(ec20->client_name, ec20->recv_line_num); + + device->client = at_client_get(ec20->client_name); + if (device->client == RT_NULL) + { + LOG_E("ec20 device(%s) initialize failed, get AT client(%s) failed.", ec20->device_name, ec20->client_name); + return -RT_ERROR; + } + + /* register URC data execution function */ +#ifdef AT_USING_SOCKET + ec20_socket_init(device); +#endif + + /* add ec20 device to the netdev list */ + device->netdev = ec20_netdev_add(ec20->device_name); + if (device->netdev == RT_NULL) + { + LOG_E("ec20 device(%s) initialize failed, get network interface device failed.", ec20->device_name); + return -RT_ERROR; + } + + /* initialize ec20 pin configuration */ + if (ec20->power_pin != -1 && ec20->power_status_pin != -1) + { + rt_pin_mode(ec20->power_pin, PIN_MODE_OUTPUT); + rt_pin_mode(ec20->power_status_pin, PIN_MODE_INPUT); + } + + /* initialize ec20 device network */ + return ec20_netdev_set_up(device->netdev); +} + +static int ec20_deinit(struct at_device *device) +{ + return ec20_netdev_set_down(device->netdev); +} + +static int ec20_control(struct at_device *device, int cmd, void *arg) +{ + int result = -RT_ERROR; + + RT_ASSERT(device); + + switch (cmd) + { + case AT_DEVICE_CTRL_POWER_ON: + case AT_DEVICE_CTRL_POWER_OFF: + case AT_DEVICE_CTRL_RESET: + case AT_DEVICE_CTRL_LOW_POWER: + case AT_DEVICE_CTRL_SLEEP: + case AT_DEVICE_CTRL_WAKEUP: + case AT_DEVICE_CTRL_NET_CONN: + case AT_DEVICE_CTRL_NET_DISCONN: + case AT_DEVICE_CTRL_SET_WIFI_INFO: + case AT_DEVICE_CTRL_GET_SIGNAL: + case AT_DEVICE_CTRL_GET_GPS: + case AT_DEVICE_CTRL_GET_VER: + LOG_W("ec20 not support the control command(%d).", cmd); + break; + default: + LOG_E("input error control command(%d).", cmd); + break; + } + + return result; +} + +const struct at_device_ops ec20_device_ops = +{ + ec20_init, + ec20_deinit, + ec20_control, +}; + +static int ec20_device_class_register(void) +{ + struct at_device_class *class = RT_NULL; + + class = (struct at_device_class *) rt_calloc(1, sizeof(struct at_device_class)); + if (class == RT_NULL) + { + LOG_E("no memory for ec20 device class create."); + return -RT_ENOMEM; + } + + /* fill ec20 device class object */ +#ifdef AT_USING_SOCKET + ec20_socket_class_register(class); +#endif + class->device_ops = &ec20_device_ops; + + return at_device_class_register(class, AT_DEVICE_CLASS_EC20); +} +INIT_DEVICE_EXPORT(ec20_device_class_register); + +#endif /* AT_DEVICE_USING_EC20 */ diff --git a/class/ec20/at_device_ec20.h b/class/ec20/at_device_ec20.h new file mode 100644 index 0000000..7df7046 --- /dev/null +++ b/class/ec20/at_device_ec20.h @@ -0,0 +1,67 @@ +/* + * File : at_device_ec20.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 + * 2019-05-16 chenyong first version + */ + +#ifndef __AT_DEVICE_EC20_H__ +#define __AT_DEVICE_EC20_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include + +/* The maximum number of sockets supported by the ec20 device */ +#define AT_DEVICE_EC20_SOCKETS_NUM 5 + +struct at_device_ec20 +{ + char *device_name; + char *client_name; + + int power_pin; + int power_status_pin; + size_t recv_line_num; + struct at_device device; + + void *socket_data; + void *user_data; +}; + +#ifdef AT_USING_SOCKET + +/* ec20 device socket initialize */ +int ec20_socket_init(struct at_device *device); + +/* ec20 device class socket register */ +int ec20_socket_class_register(struct at_device_class *class); + +#endif /* AT_USING_SOCKET */ + +#ifdef __cplusplus +} +#endif + +#endif /* __AT_DEVICE_EC20_H__ */ diff --git a/class/ec20/at_socket_ec20.c b/class/ec20/at_socket_ec20.c new file mode 100644 index 0000000..9186dff --- /dev/null +++ b/class/ec20/at_socket_ec20.c @@ -0,0 +1,1007 @@ +/* + * File : at_socket_ec20.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-12 chenyong first version + * 2018-08-12 Marcus port to ec20 + * 2019-05-13 chenyong multi AT socket client support + */ + +#include +#include + +#include + +#if !defined(AT_SW_VERSION_NUM) || AT_SW_VERSION_NUM < 0x10300 +#error "This AT Client version is older, please check and update latest AT Client!" +#endif + +#define LOG_TAG "at.skt" +#include + +#if defined(AT_DEVICE_USING_EC20) && defined(AT_USING_SOCKET) + +#define EC20_MODULE_SEND_MAX_SIZE 1460 + +/* set real event by current socket and current state */ +#define SET_EVENT(socket, event) (((socket + 1) << 16) | (event)) + +/* AT socket event type */ +#define EC20_EVENT_CONN_OK (1L << 0) +#define EC20_EVENT_SEND_OK (1L << 1) +#define EC20_EVENT_RECV_OK (1L << 2) +#define EC20_EVNET_CLOSE_OK (1L << 3) +#define EC20_EVENT_CONN_FAIL (1L << 4) +#define EC20_EVENT_SEND_FAIL (1L << 5) +#define EC20_EVENT_DOMAIN_OK (1L << 6) + +static at_evt_cb_t at_evt_cb_set[] = { + [AT_SOCKET_EVT_RECV] = NULL, + [AT_SOCKET_EVT_CLOSED] = NULL, +}; + +static void at_tcp_ip_errcode_parse(int result)//TCP/IP_QIGETERROR +{ + switch(result) + { + case 0 : LOG_D("%d : Operation successful", result); break; + case 550 : LOG_E("%d : Unknown error", result); break; + case 551 : LOG_E("%d : Operation blocked", result); break; + case 552 : LOG_E("%d : Invalid parameters", result); break; + case 553 : LOG_E("%d : Memory not enough", result); break; + case 554 : LOG_E("%d : Create socket failed", result); break; + case 555 : LOG_E("%d : Operation not supported", result); break; + case 556 : LOG_E("%d : Socket bind failed", result); break; + case 557 : LOG_E("%d : Socket listen failed", result); break; + case 558 : LOG_E("%d : Socket write failed", result); break; + case 559 : LOG_E("%d : Socket read failed", result); break; + case 560 : LOG_E("%d : Socket accept failed", result); break; + case 561 : LOG_E("%d : Open PDP context failed", result); break; + case 562 : LOG_E("%d : Close PDP context failed", result); break; + case 563 : LOG_W("%d : Socket identity has been used", result); break; + case 564 : LOG_E("%d : DNS busy", result); break; + case 565 : LOG_E("%d : DNS parse failed", result); break; + case 566 : LOG_E("%d : Socket connect failed", result); break; + // case 567 : LOG_W("%d : Socket has been closed", result); break; + case 567 : break; + case 568 : LOG_E("%d : Operation busy", result); break; + case 569 : LOG_E("%d : Operation timeout", result); break; + case 570 : LOG_E("%d : PDP context broken down", result); break; + case 571 : LOG_E("%d : Cancel send", result); break; + case 572 : LOG_E("%d : Operation not allowed", result); break; + case 573 : LOG_E("%d : APN not configured", result); break; + case 574 : LOG_E("%d : Port busy", result); break; + default : LOG_E("%d : Unknown err code", result); break; + } +} + +static void at_http_errcode_parse(int result)//HTTP +{ + switch(result) + { + case 0 : LOG_D("%d : Operation successful", result); break; + case 701 : LOG_E("%d : HTTP(S) unknown error", result); break; + case 702 : LOG_E("%d : HTTP(S) timeout", result); break; + case 703 : LOG_E("%d : HTTP(S) busy", result); break; + case 704 : LOG_E("%d : HTTP(S) UART busy", result); break; + case 705 : LOG_E("%d : HTTP(S) no GET/POST requests", result); break; + case 706 : LOG_E("%d : HTTP(S) network busy", result); break; + case 707 : LOG_E("%d : HTTP(S) network open failed", result); break; + case 708 : LOG_E("%d : HTTP(S) network no configuration", result); break; + case 709 : LOG_E("%d : HTTP(S) network deactivated", result); break; + case 710 : LOG_E("%d : HTTP(S) network error", result); break; + case 711 : LOG_E("%d : HTTP(S) URL error", result); break; + case 712 : LOG_E("%d : HTTP(S) empty URL", result); break; + case 713 : LOG_E("%d : HTTP(S) IP address error", result); break; + case 714 : LOG_E("%d : HTTP(S) DNS error", result); break; + case 715 : LOG_E("%d : HTTP(S) socket create error", result); break; + case 716 : LOG_E("%d : HTTP(S) socket connect error", result); break; + case 717 : LOG_E("%d : HTTP(S) socket read error", result); break; + case 718 : LOG_E("%d : HTTP(S) socket write error", result); break; + case 719 : LOG_E("%d : HTTP(S) socket closed", result); break; + case 720 : LOG_E("%d : HTTP(S) data encode error", result); break; + case 721 : LOG_E("%d : HTTP(S) data decode error", result); break; + case 722 : LOG_E("%d : HTTP(S) read timeout", result); break; + case 723 : LOG_E("%d : HTTP(S) response failed", result); break; + case 724 : LOG_E("%d : Incoming call busy", result); break; + case 725 : LOG_E("%d : Voice call busy", result); break; + case 726 : LOG_E("%d : Input timeout", result); break; + case 727 : LOG_E("%d : Wait data timeout", result); break; + case 728 : LOG_E("%d : Wait HTTP(S) response timeout", result); break; + case 729 : LOG_E("%d : Memory allocation failed", result); break; + case 730 : LOG_E("%d : Invalid parameter", result); break; + default : LOG_E("%d : Unknown err code", result); break; + } +} + +static void at_http_rsponsecode_parse(int result)//HTTP +{ + switch(result) + { + case 200 : LOG_D("%d : OK", result); break; + case 400 : LOG_E("%d : Bad request", result); break; + case 403 : LOG_E("%d : Forbidden", result); break; + case 404 : LOG_E("%d : Not found", result); break; + case 409 : LOG_E("%d : Conflict", result); break; + case 411 : LOG_E("%d : Length required", result); break; + case 500 : LOG_E("%d : Internal server error", result); break; + case 502 : LOG_E("%d : Bad gate way", result); break; + default : LOG_E("%d : Unknown err code", result); break; + } +} + +static void at_ftp_errcode_parse(int result)//FTP +{ + switch(result) + { + case 0 : LOG_D("%d : Operation successful", result); break; + case 601 : LOG_E("%d : Unknown error", result); break; + case 602 : LOG_E("%d : FTP(S) server blocked", result); break; + case 603 : LOG_E("%d : FTP(S) server busy", result); break; + case 604 : LOG_E("%d : DNS parse failed", result); break; + case 605 : LOG_E("%d : Network error", result); break; + case 606 : LOG_E("%d : Control connection closed.", result); break; + case 607 : LOG_E("%d : Data connection closed", result); break; + case 608 : LOG_E("%d : Socket closed by peer", result); break; + case 609 : LOG_E("%d : Timeout error", result); break; + case 610 : LOG_E("%d : Invalid parameter", result); break; + case 611 : LOG_E("%d : Failed to open file", result); break; + case 612 : LOG_E("%d : File position invalid", result); break; + case 613 : LOG_E("%d : File error", result); break; + case 614 : LOG_E("%d : Service not available, closing control connection", result); break; + case 615 : LOG_E("%d : Open data connection failed", result); break; + case 616 : LOG_E("%d : Connection closed; transfer aborted", result); break; + case 617 : LOG_E("%d : Requested file action not taken", result); break; + case 618 : LOG_E("%d : Requested action aborted: local error in processing", result); break; + case 619 : LOG_E("%d : Requested action not taken: insufficient system storage", result); break; + case 620 : LOG_E("%d : Syntax error, command unrecognized", result); break; + case 621 : LOG_E("%d : Syntax error in parameters or arguments", result); break; + case 622 : LOG_E("%d : Command not implemented", result); break; + case 623 : LOG_E("%d : Bad sequence of commands", result); break; + case 624 : LOG_E("%d : Command parameter not implemented", result); break; + case 625 : LOG_E("%d : Not logged in", result); break; + case 626 : LOG_E("%d : Need account for storing files", result); break; + case 627 : LOG_E("%d : Requested action not taken", result); break; + case 628 : LOG_E("%d : Requested action aborted: page type unknown", result); break; + case 629 : LOG_E("%d : Requested file action aborted", result); break; + case 630 : LOG_E("%d : Requested file name invalid", result); break; + case 631 : LOG_E("%d : SSL authentication failed", result); break; + default : LOG_E("%d : Unknown err code", result); break; + } +} + +static void at_ftp_protocol_errcode_parse(int result)//FTP_Protocol +{ + switch(result) + { + case 421 : LOG_E("%d : Service not available, closing control connection", result); break; + case 425 : LOG_E("%d : Open data connection failed", result); break; + case 426 : LOG_E("%d : Connection closed; transfer aborted", result); break; + case 450 : LOG_E("%d : Requested file action not taken", result); break; + case 451 : LOG_E("%d : Requested action aborted: local error in processing", result); break; + case 452 : LOG_E("%d : Requested action not taken: insufficient system storage", result); break; + case 500 : LOG_E("%d : Syntax error, command unrecognized", result); break; + case 501 : LOG_E("%d : Syntax error in parameters or arguments", result); break; + case 502 : LOG_E("%d : Command not implemented", result); break; + case 503 : LOG_E("%d : Bad sequence of commands", result); break; + case 504 : LOG_E("%d : Command parameter not implemented", result); break; + case 530 : LOG_E("%d : Not logged in", result); break; + case 532 : LOG_E("%d : Need account for storing files", result); break; + case 550 : LOG_E("%d : Requested action not taken: file unavailable", result); break; + case 551 : LOG_E("%d : Requested action aborted: page type unknown", result); break; + case 552 : LOG_E("%d : Requested file action aborted: exceeded storage allocation", result); break; + case 553 : LOG_E("%d : Requested action not taken: file name not allowed", result); break; + default : LOG_E("%d : Unknown err code", result); break; + } +} + +static void at_smtp_errcode_parse(int result)//Email +{ + switch(result) + { + case 651 : LOG_E("%d : Unknown error", result); break; + case 652 : LOG_E("%d : The SMTP server is busy, such as uploading the body or sending an email.", result); break; + case 653 : LOG_E("%d : Failed to get IP address according to the domain name.", result); break; + case 654 : LOG_E("%d : Network error, such as failed to activate GPRS/CSD context, failed to establish the TCP connection with the SMTP server or failed to send an email to the SMTP server, etc.", result); break; + case 655 : LOG_E("%d : Unsupported authentication type", result); break; + case 656 : LOG_E("%d : The connection for the SMTP server is closed by peer.", result); break; + case 657 : LOG_E("%d : GPRS/CSD context is deactivated.", result); break; + case 658 : LOG_E("%d : Timeout", result); break; + case 659 : LOG_E("%d : No recipient for the SMTP server", result); break; + case 660 : LOG_E("%d : Failed to send an email", result); break; + case 661 : LOG_E("%d : Failed to open a file", result); break; + case 662 : LOG_E("%d : No enough memory for the attachment", result); break; + case 663 : LOG_E("%d : Failed to save the attachment", result); break; + case 664 : LOG_E("%d : The input parameter is wrong", result); break; + case 665 : LOG_E("%d : SSL authentication failed", result); break; + case 666 : LOG_E("%d : Service not available, closing transmission channel", result); break; + case 667 : LOG_E("%d : Requested mail action not taken: mailbox unavailable", result); break; + case 668 : LOG_E("%d : Requested action aborted: local error in processing", result); break; + case 669 : LOG_E("%d : Requested action not taken: insufficient system storage", result); break; + case 670 : LOG_E("%d : Syntax error, command unrecognized", result); break; + case 671 : LOG_E("%d : Syntax error in parameters or arguments", result); break; + case 672 : LOG_E("%d : Command not implemented", result); break; + case 673 : LOG_E("%d : Bad sequence of commands", result); break; + case 674 : LOG_E("%d : Command parameter not implemented", result); break; + case 675 : LOG_E("%d : does not accept mail (see RFC1846)", result); break; + case 676 : LOG_E("%d : Access denied", result); break; + case 677 : LOG_E("%d : Authentication failed", result); break; + case 678 : LOG_E("%d : Requested action not taken: mailbox unavailable", result); break; + case 679 : LOG_E("%d : User not local; please try ", result); break; + case 680 : LOG_E("%d : Requested mail action aborted: exceeded storage allocation", result); break; + case 681 : LOG_E("%d : Requested action not taken: mailbox name not allowed", result); break; + case 682 : LOG_E("%d : Transaction failed", result); break; + default : LOG_E("%d : Unknown err code", result); break; + } +} + +static void at_smtp_protocol_errcode_parse(int result)//Email_Protocol +{ + switch(result) + { + case 421 : LOG_E("%d : Service not available, closing transmission channel", result); break; + case 450 : LOG_E("%d : Requested mail action not taken: mailbox unavailable", result); break; + case 451 : LOG_E("%d : Requested action aborted: local error in processing", result); break; + case 452 : LOG_E("%d : Requested action not taken: insufficient system storage", result); break; + case 500 : LOG_E("%d : Syntax error, command unrecognized", result); break; + case 501 : LOG_E("%d : Syntax error in parameters or arguments", result); break; + case 502 : LOG_E("%d : Command not implemented", result); break; + case 503 : LOG_E("%d : Bad sequence of commands", result); break; + case 504 : LOG_E("%d : Command parameter not implemented", result); break; + case 521 : LOG_E("%d : does not accept mail (see RFC1846)", result); break; + case 530 : LOG_E("%d : Access denied", result); break; + case 535 : LOG_E("%d : Authentication failed", result); break; + case 550 : LOG_E("%d : Requested action not taken: mailbox unavailable", result); break; + case 551 : LOG_E("%d : User not local; please try ", result); break; + case 552 : LOG_E("%d : Requested mail action aborted: exceeded storage allocation", result); break; + case 553 : LOG_E("%d : Requested action not taken: mailbox name not allowed", result); break; + case 554 : LOG_E("%d : Transaction failed", result); break; + default : LOG_E("%d : Unknown err code", result); break; + } +} + +static int ec20_socket_event_send(struct at_device *device, uint32_t event) +{ + return (int) rt_event_send(device->socket_event, event); +} + +static int ec20_socket_event_recv(struct at_device *device, uint32_t event, uint32_t timeout, rt_uint8_t option) +{ + int result = RT_EOK; + rt_uint32_t recved; + + result = rt_event_recv(device->socket_event, event, option | RT_EVENT_FLAG_CLEAR, timeout, &recved); + if (result != RT_EOK) + { + return -RT_ETIMEOUT; + } + + return recved; +} + +/** + * close socket by AT commands. + * + * @param current socket + * + * @return 0: close socket success + * -1: send AT commands error + * -2: wait socket event timeout + * -5: no memory + */ +static int ec20_socket_close(struct at_socket *socket) +{ + int result = RT_EOK; + at_response_t resp = RT_NULL; + int device_socket = (int) socket->user_data; + struct at_device *device = (struct at_device *) socket->device; + + resp = at_create_resp(64, 0, 5 * RT_TICK_PER_SECOND); + if (resp == RT_NULL) + { + LOG_E("no memory for ec20 device(%s) response structure.", device->name); + return -RT_ENOMEM; + } + + /* default connection timeout is 10 seconds, but it set to 1 seconds is convenient to use.*/ + result = at_obj_exec_cmd(device->client, resp, "AT+QICLOSE=%d,1", device_socket); + + if (resp) + { + at_delete_resp(resp); + } + + return result; +} + +/** + * create TCP/UDP client or server connect by AT commands. + * + * @param socket current socket + * @param ip server or client IP address + * @param port server or client port + * @param type connect socket type(tcp, udp) + * @param is_client connection is client + * + * @return 0: connect success + * -1: connect failed, send commands error or type error + * -2: wait socket event timeout + * -5: no memory + */ +static int ec20_socket_connect(struct at_socket *socket, char *ip, int32_t port, + enum at_socket_type type, rt_bool_t is_client) +{ + uint32_t event = 0; + rt_bool_t retryed = RT_FALSE; + at_response_t resp = RT_NULL; + int result = 0, event_result = 0; + int device_socket = (int) socket->user_data; + struct at_device *device = (struct at_device *) socket->device; + + RT_ASSERT(ip); + RT_ASSERT(port >= 0); + + resp = at_create_resp(128, 0, 5 * RT_TICK_PER_SECOND); + if (resp == RT_NULL) + { + LOG_E("no memory for ec20 device(%s) response structure.", device->name); + return -RT_ENOMEM; + } + +__retry: + /* clear socket connect event */ + event = SET_EVENT(device_socket, EC20_EVENT_CONN_OK | EC20_EVENT_CONN_FAIL); + ec20_socket_event_recv(device, event, 0, RT_EVENT_FLAG_OR); + + if (is_client) + { + switch (type) + { + case AT_SOCKET_TCP: + /* send AT commands(AT+QIOPEN=,,"","/", */ + /* ,,) to connect TCP server */ + /* contextID = 1 : use same contextID as AT+QICSGP & AT+QIACT */ + /* local_port = 0 : local port assigned automatically */ + /* access_mode = 1 : Direct push mode */ + if (at_obj_exec_cmd(device->client, resp, + "AT+QIOPEN=1,%d,\"TCP\",\"%s\",%d,0,1", device_socket, ip, port) < 0) + { + result = -RT_ERROR; + goto __exit; + } + break; + + case AT_SOCKET_UDP: + if (at_obj_exec_cmd(device->client, resp, + "AT+QIOPEN=1,%d,\"UDP\",\"%s\",%d,0,1", device_socket, ip, port) < 0) + { + result = -RT_ERROR; + goto __exit; + } + break; + + default: + LOG_E("ec20 device(%s) not supported connect type : %d.", device->name, type); + return -RT_ERROR; + } + } + + /* waiting result event from AT URC, the device default connection timeout is 75 seconds, but it set to 10 seconds is convenient to use.*/ + if (ec20_socket_event_recv(device, SET_EVENT(device_socket, 0), 10 * RT_TICK_PER_SECOND, RT_EVENT_FLAG_OR) < 0) + { + LOG_E("ec20 device(%s) socket(%d) connect failed, wait connect result timeout.", device->name, device_socket); + result = -RT_ETIMEOUT; + goto __exit; + } + /* waiting OK or failed result */ + event_result = ec20_socket_event_recv(device, + EC20_EVENT_CONN_OK | EC20_EVENT_CONN_FAIL, 1 * RT_TICK_PER_SECOND, RT_EVENT_FLAG_OR); + if (event_result < 0) + { + LOG_E("ec20 device(%s) socket(%d) connect failed, wait connect OK|FAIL timeout.", device->name, device_socket); + result = -RT_ETIMEOUT; + goto __exit; + } + /* check result */ + if (event_result & EC20_EVENT_CONN_FAIL) + { + if (retryed == RT_FALSE) + { + LOG_D("ec20 device(%s) socket(%d) connect failed, maybe the socket was not be closed at the last time and now will retry.", + device->name, device_socket); + /* default connection timeout is 10 seconds, but it set to 1 seconds is convenient to use.*/ + if (ec20_socket_close(socket) < 0) + { + result = -RT_ERROR; + goto __exit; + } + retryed = RT_TRUE; + goto __retry; + } + LOG_E("ec20 device(%s) socket(%d) connect failed.", device->name, device_socket); + result = -RT_ERROR; + goto __exit; + } + +__exit: + if (resp) + { + at_delete_resp(resp); + } + + return result; +} + +static int at_get_send_size(struct at_socket *socket, size_t *size, size_t *acked, size_t *nacked) +{ + int result = 0; + at_response_t resp = RT_NULL; + int device_socket = (int) socket->user_data; + struct at_device *device = (struct at_device *) socket->device; + + resp = at_create_resp(128, 0, 5 * RT_TICK_PER_SECOND); + if (resp == RT_NULL) + { + LOG_E("no memory for ec20 device(%s) response structure.", device->name); + result = -RT_ENOMEM; + goto __exit; + } + + if (at_obj_exec_cmd(device->client, resp, "AT+QISEND=%d,0", device_socket) < 0) + { + result = -RT_ERROR; + goto __exit; + } + + if (at_resp_parse_line_args_by_kw(resp, "+QISEND:", "+QISEND: %d,%d,%d", size, acked, nacked) <= 0) + { + result = -RT_ERROR; + goto __exit; + } + +__exit: + if (resp) + { + at_delete_resp(resp); + } + + return result; +} + +static int at_wait_send_finish(struct at_socket *socket, size_t settings_size) +{ + /* get the timeout by the input data size */ + rt_tick_t timeout = rt_tick_from_millisecond(settings_size); + rt_tick_t last_time = rt_tick_get(); + size_t size = 0, acked = 0, nacked = 0xFFFF; + + while (rt_tick_get() - last_time <= timeout) + { + at_get_send_size(socket, &size, &acked, &nacked); + if (nacked == 0) + { + return RT_EOK; + } + rt_thread_mdelay(50); + } + + return -RT_ETIMEOUT; +} + +/** + * send data to server or client by AT commands. + * + * @param socket current socket + * @param buff send buffer + * @param bfsz send buffer size + * @param type connect socket type(tcp, udp) + * + * @return >=0: the size of send success + * -1: send AT commands error or send data error + * -2: waited socket event timeout + * -5: no memory + */ +static int ec20_socket_send(struct at_socket *socket, const char *buff, size_t bfsz, enum at_socket_type type) +{ + uint32_t event = 0; + int result = 0, event_result = 0; + size_t cur_pkt_size = 0, sent_size = 0; + at_response_t resp = RT_NULL; + int device_socket = (int) socket->user_data; + struct at_device *device = (struct at_device *) socket->device; + struct at_device_ec20 *ec20 = (struct at_device_ec20 *) device->user_data; + rt_mutex_t lock = device->client->lock; + + RT_ASSERT(buff); + + resp = at_create_resp(128, 2, 5 * RT_TICK_PER_SECOND); + if (resp == RT_NULL) + { + LOG_E("no memory for ec20 device(%s) response structure.", device->name); + return -RT_ENOMEM; + } + + rt_mutex_take(lock, RT_WAITING_FOREVER); + + /* set current socket for send URC event */ + ec20->user_data = (void *) device_socket; + + /* clear socket send event */ + event = SET_EVENT(device_socket, EC20_EVENT_SEND_OK | EC20_EVENT_SEND_FAIL); + ec20_socket_event_recv(device, event, 0, RT_EVENT_FLAG_OR); + + /* set AT client end sign to deal with '>' sign.*/ + at_obj_set_end_sign(device->client, '>'); + + while (sent_size < bfsz) + { + if (bfsz - sent_size < EC20_MODULE_SEND_MAX_SIZE) + { + cur_pkt_size = bfsz - sent_size; + } + else + { + cur_pkt_size = EC20_MODULE_SEND_MAX_SIZE; + } + + /* send the "AT+QISEND" commands to AT server than receive the '>' response on the first line. */ + if (at_obj_exec_cmd(device->client, resp, "AT+QISEND=%d,%d", device_socket, cur_pkt_size) < 0) + { + result = -RT_ERROR; + goto __exit; + } + + /* send the real data to server or client */ + result = (int) at_client_send(buff + sent_size, cur_pkt_size); + if (result == 0) + { + result = -RT_ERROR; + goto __exit; + } + + /* waiting result event from AT URC */ + if (ec20_socket_event_recv(device, SET_EVENT(device_socket, 0), 10 * RT_TICK_PER_SECOND, RT_EVENT_FLAG_OR) < 0) + { + result = -RT_ETIMEOUT; + goto __exit; + } + /* waiting OK or failed result */ + event_result = ec20_socket_event_recv(device, + EC20_EVENT_SEND_OK | EC20_EVENT_SEND_FAIL, 1 * RT_TICK_PER_SECOND, RT_EVENT_FLAG_OR); + if (event_result < 0) + { + LOG_E("ec20 device(%s) socket (%d) send failed, wait connect OK|FAIL timeout.", device->name, device_socket); + result = -RT_ETIMEOUT; + goto __exit; + } + /* check result */ + if (event_result & EC20_EVENT_SEND_FAIL) + { + LOG_E("ec20 device(%s) socket (%d) send failed.", device->name, device_socket); + result = -RT_ERROR; + goto __exit; + } + + if (type == AT_SOCKET_TCP) + { + // at_wait_send_finish(socket, cur_pkt_size); + rt_thread_mdelay(10); + } + + sent_size += cur_pkt_size; + } + +__exit: + /* reset the end sign for data conflict */ + at_obj_set_end_sign(device->client, 0); + + rt_mutex_release(lock); + + if (resp) + { + at_delete_resp(resp); + } + + return result; +} + +/** + * domain resolve by AT commands. + * + * @param name domain name + * @param ip parsed IP address, it's length must be 16 + * + * @return 0: domain resolve success + * -1: send AT commands error or response error + * -2: wait socket event timeout + * -5: no memory + */ +static int ec20_domain_resolve(const char *name, char ip[16]) +{ +#define RESOLVE_RETRY 3 + + int i, result; + at_response_t resp = RT_NULL; + struct at_device *device = RT_NULL; + + RT_ASSERT(name); + RT_ASSERT(ip); + + device = at_device_get_first_initialized(); + if (device == RT_NULL) + { + LOG_E("get first initialization ec20 device failed."); + return -RT_ERROR; + } + + /* the maximum response time is 60 seconds, but it set to 10 seconds is convenient to use. */ + resp = at_create_resp(128, 0, 10 * RT_TICK_PER_SECOND); + if (!resp) + { + LOG_E("no memory for ec20 device(%s) response structure.", device->name); + return -RT_ENOMEM; + } + + /* clear EC20_EVENT_DOMAIN_OK */ + ec20_socket_event_recv(device, EC20_EVENT_DOMAIN_OK, 0, RT_EVENT_FLAG_OR); + + result = at_obj_exec_cmd(device->client, resp, "AT+QIDNSGIP=1,\"%s\"", name); + if (result < 0) + { + goto __exit; + } + + if (result == RT_EOK) + { + for(i = 0; i < RESOLVE_RETRY; i++) + { + /* waiting result event from AT URC, the device default connection timeout is 60 seconds.*/ + if (ec20_socket_event_recv(device, EC20_EVENT_DOMAIN_OK, 10 * RT_TICK_PER_SECOND, RT_EVENT_FLAG_OR) < 0) + { + continue; + } + else + { + struct at_device_ec20 *ec20 = (struct at_device_ec20 *) device->user_data; + char *recv_ip = (char *) ec20->socket_data; + + if (rt_strlen(recv_ip) < 8) + { + rt_thread_mdelay(100); + /* resolve failed, maybe receive an URC CRLF */ + result = -RT_ERROR; + continue; + } + else + { + rt_strncpy(ip, recv_ip, 15); + ip[15] = '\0'; + result = RT_EOK; + break; + } + } + } + + /* response timeout */ + if (i == RESOLVE_RETRY) + { + result = -RT_ENOMEM; + } + } + + __exit: + if (resp) + { + at_delete_resp(resp); + } + + return result; + +} + +/** + * set AT socket event notice callback + * + * @param event notice event + * @param cb notice callback + */ +static void ec20_socket_set_event_cb(at_socket_evt_t event, at_evt_cb_t cb) +{ + if (event < sizeof(at_evt_cb_set) / sizeof(at_evt_cb_set[1])) + { + at_evt_cb_set[event] = cb; + } +} + +static void urc_connect_func(struct at_client *client, const char *data, rt_size_t size) +{ + int device_socket = 0, result = 0; + struct at_device *device = RT_NULL; + char *client_name = client->device->parent.name; + + RT_ASSERT(data && size); + + device = at_device_get_by_name(AT_DEVICE_NAMETYPE_CLIENT, client_name); + if (device == RT_NULL) + { + LOG_E("get ec20 device by client name(%s) failed.", client_name); + return; + } + + sscanf(data, "+QIOPEN: %d,%d", &device_socket , &result); + + if (result == 0) + { + ec20_socket_event_send(device, SET_EVENT(device_socket, EC20_EVENT_CONN_OK)); + } + else + { + at_tcp_ip_errcode_parse(result); + ec20_socket_event_send(device, SET_EVENT(device_socket, EC20_EVENT_CONN_FAIL)); + } +} + +static void urc_send_func(struct at_client *client, const char *data, rt_size_t size) +{ + int device_socket = 0; + struct at_device *device = RT_NULL; + struct at_device_ec20 *ec20 = RT_NULL; + char *client_name = client->device->parent.name; + + RT_ASSERT(data && size); + + device = at_device_get_by_name(AT_DEVICE_NAMETYPE_CLIENT, client_name); + if (device == RT_NULL) + { + LOG_E("get ec20 device by client name(%s) failed.", client_name); + return; + } + ec20 = (struct at_device_ec20 *) device->user_data; + device_socket = (int) ec20->user_data; + + if (rt_strstr(data, "SEND OK")) + { + ec20_socket_event_send(device, SET_EVENT(device_socket, EC20_EVENT_SEND_OK)); + } + else if (rt_strstr(data, "SEND FAIL")) + { + ec20_socket_event_send(device, SET_EVENT(device_socket, EC20_EVENT_SEND_FAIL)); + } +} + +static void urc_close_func(struct at_client *client, const char *data, rt_size_t size) +{ + int device_socket = 0; + struct at_socket *socket = RT_NULL; + struct at_device *device = RT_NULL; + char *client_name = client->device->parent.name; + + RT_ASSERT(data && size); + + device = at_device_get_by_name(AT_DEVICE_NAMETYPE_CLIENT, client_name); + if (device == RT_NULL) + { + LOG_E("get ec20 device by client name(%s) failed.", client_name); + return; + } + + sscanf(data, "+QIURC: \"closed\",%d", &device_socket); + /* get at socket object by device socket descriptor */ + socket = &(device->sockets[device_socket]); + + /* notice the socket is disconnect by remote */ + if (at_evt_cb_set[AT_SOCKET_EVT_CLOSED]) + { + at_evt_cb_set[AT_SOCKET_EVT_CLOSED](socket, AT_SOCKET_EVT_CLOSED, NULL, 0); + } +} + +static void urc_recv_func(struct at_client *client, const char *data, rt_size_t size) +{ + int device_socket = 0; + rt_int32_t timeout; + rt_size_t bfsz = 0, temp_size = 0; + char *recv_buf = RT_NULL, temp[8] = {0}; + struct at_socket *socket = RT_NULL; + struct at_device *device = RT_NULL; + char *client_name = client->device->parent.name; + + RT_ASSERT(data && size); + + device = at_device_get_by_name(AT_DEVICE_NAMETYPE_CLIENT, client_name); + if (device == RT_NULL) + { + LOG_E("get ec20 device by client name(%s) failed.", client_name); + return; + } + + /* get the current socket and receive buffer size by receive data */ + sscanf(data, "+QIURC: \"recv\",%d,%d", &device_socket, (int *) &bfsz); + /* get receive timeout by receive buffer length */ + timeout = bfsz; + + if (device_socket < 0 || bfsz == 0) + { + return; + } + + recv_buf = (char *) rt_calloc(1, bfsz); + if (recv_buf == RT_NULL) + { + LOG_E("no memory for ec20 device(%s) URC receive buffer (%d).", device->name, bfsz); + /* read and clean the coming data */ + while (temp_size < bfsz) + { + if (bfsz - temp_size > sizeof(temp)) + { + at_client_obj_recv(client, temp, sizeof(temp), timeout); + } + else + { + at_client_obj_recv(client, temp, bfsz - temp_size, timeout); + } + temp_size += sizeof(temp); + } + return; + } + + /* sync receive data */ + if (at_client_obj_recv(client, recv_buf, bfsz, timeout) != bfsz) + { + LOG_E("ec20 device(%s) receive size(%d) data failed.", device->name, bfsz); + rt_free(recv_buf); + return; + } + + /* get at socket object by device socket descriptor */ + socket = &(device->sockets[device_socket]); + + /* notice the receive buffer and buffer size */ + if (at_evt_cb_set[AT_SOCKET_EVT_RECV]) + { + at_evt_cb_set[AT_SOCKET_EVT_RECV](socket, AT_SOCKET_EVT_RECV, recv_buf, bfsz); + } +} + +static void urc_pdpdeact_func(struct at_client *client, const char *data, rt_size_t size) +{ + int connectID = 0; + + RT_ASSERT(data && size); + + sscanf(data, "+QIURC: \"pdpdeact\",%d", &connectID); + + LOG_E("context (%d) is deactivated.", connectID); +} + +static void urc_dnsqip_func(struct at_client *client, const char *data, rt_size_t size) +{ + int i = 0, j = 0; + char recv_ip[16] = {0}; + int result, ip_count, dns_ttl; + struct at_device *device = RT_NULL; + struct at_device_ec20 *ec20 = RT_NULL; + char *client_name = client->device->parent.name; + + RT_ASSERT(data && size); + + device = at_device_get_by_name(AT_DEVICE_NAMETYPE_CLIENT, client_name); + if (device == RT_NULL) + { + LOG_E("get ec20 device by client name(%s) failed.", client_name); + return; + } + ec20 = (struct at_device_ec20 *) device->user_data; + + for (i = 0; i < size; i++) + { + if (*(data + i) == '.') + j++; + } + /* There would be several dns result, we just pickup one */ + if (j == 3) + { + sscanf(data, "+QIURC: \"dnsgip\",\"%[^\"]", recv_ip); + recv_ip[15] = '\0'; + + /* set ec20 information socket data */ + if (ec20->socket_data == RT_NULL) + { + ec20->socket_data = rt_calloc(1, sizeof(recv_ip)); + if (ec20->socket_data == RT_NULL) + { + return; + } + } + rt_memcpy(ec20->socket_data, recv_ip, sizeof(recv_ip)); + + + ec20_socket_event_send(device, EC20_EVENT_DOMAIN_OK); + } + else + { + sscanf(data, "+QIURC: \"dnsgip\",%d,%d,%d", &result, &ip_count, &dns_ttl); + if (result) + { + at_tcp_ip_errcode_parse(result); + } + } +} + +static void urc_func(struct at_client *client, const char *data, rt_size_t size) +{ + RT_ASSERT(data); + + LOG_I("URC data : %.*s", size, data); +} + +static void urc_qiurc_func(struct at_client *client, const char *data, rt_size_t size) +{ + RT_ASSERT(data && size); + + switch(*(data + 9)) + { + case 'c' : urc_close_func(client, data, size); break;//+QIURC: "closed" + case 'r' : urc_recv_func(client, data, size); break;//+QIURC: "recv" + case 'p' : urc_pdpdeact_func(client, data, size); break;//+QIURC: "pdpdeact" + case 'd' : urc_dnsqip_func(client, data, size); break;//+QIURC: "dnsgip" + default : urc_func(client, data, size); break; + } +} + +static const struct at_urc urc_table[] = +{ + {"SEND OK", "\r\n", urc_send_func}, + {"SEND FAIL", "\r\n", urc_send_func}, + {"+QIOPEN:", "\r\n", urc_connect_func}, + {"+QIURC:", "\r\n", urc_qiurc_func}, +}; + +static const struct at_socket_ops ec20_socket_ops = +{ + ec20_socket_connect, + ec20_socket_close, + ec20_socket_send, + ec20_domain_resolve, + ec20_socket_set_event_cb, +}; + +int ec20_socket_init(struct at_device *device) +{ + RT_ASSERT(device); + + /* register URC data execution function */ + at_obj_set_urc_table(device->client, urc_table, sizeof(urc_table) / sizeof(urc_table[0])); + + return RT_EOK; +} + +int ec20_socket_class_register(struct at_device_class *class) +{ + RT_ASSERT(class); + + class->socket_num = AT_DEVICE_EC20_SOCKETS_NUM; + class->socket_ops = &ec20_socket_ops; + + return RT_EOK; +} + + +#endif /* AT_DEVICE_USING_EC20 && AT_USING_SOCKET */ diff --git a/class/esp8266/at_device_esp8266.c b/class/esp8266/at_device_esp8266.c new file mode 100644 index 0000000..4bc8332 --- /dev/null +++ b/class/esp8266/at_device_esp8266.c @@ -0,0 +1,929 @@ +/* + * File : at_socket_esp8266.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-20 chenyong first version + * 2019-05-09 chenyong multi AT socket client support + */ + +#include +#include + +#include + +#if !defined(AT_SW_VERSION_NUM) || AT_SW_VERSION_NUM < 0x10300 +#error "This AT Client version is older, please check and update latest AT Client!" +#endif + +#define LOG_TAG "at.dev" + +#include + +#ifdef AT_DEVICE_USING_ESP8266 + +#define ESP8266_WAIT_CONNECT_TIME 5000 +#define ESP8266_THREAD_STACK_SIZE 1024 +#define ESP8266_THREAD_PRIORITY (RT_THREAD_PRIORITY_MAX / 2) + +/* ============================= esp8266 network interface operations ============================= */ + +static void esp8266_get_netdev_info(struct rt_work *work, void *work_data) +{ +#define AT_ADDR_LEN 32 + at_response_t resp = RT_NULL; + char ip[AT_ADDR_LEN] = {0}, mac[AT_ADDR_LEN] = {0}; + char gateway[AT_ADDR_LEN] = {0}, netmask[AT_ADDR_LEN] = {0}; + char dns_server1[AT_ADDR_LEN] = {0}, dns_server2[AT_ADDR_LEN] = {0}; + const char *resp_expr = "%*[^\"]\"%[^\"]\""; + const char *resp_dns = "+CIPDNS_CUR:%s"; + ip_addr_t ip_addr; + rt_uint32_t mac_addr[6] = {0}; + rt_uint32_t num = 0; + rt_uint8_t dhcp_stat = 0; + struct rt_delayed_work *delay_work = (struct rt_delayed_work *)work; + struct at_device *device = (struct at_device *)work_data; + struct netdev *netdev = device->netdev; + struct at_client *client = device->client; + + if (delay_work) + { + rt_free(delay_work); + } + + resp = at_create_resp(512, 0, rt_tick_from_millisecond(300)); + if (resp == RT_NULL) + { + LOG_E("no memory for esp8266 device(%d) response structure.", device->name); + return; + } + + /* send mac addr query commond "AT+CIFSR" and wait response */ + if (at_obj_exec_cmd(client, resp, "AT+CIFSR") < 0) + { + goto __exit; + } + + if (at_resp_parse_line_args_by_kw(resp, "STAMAC", resp_expr, mac) <= 0) + { + LOG_E("esp8266 device(%s) parse \"AT+CIFSR\" command response data error.", device->name); + goto __exit; + } + + /* send addr info query commond "AT+CIPSTA?" and wait response */ + if (at_obj_exec_cmd(client, resp, "AT+CIPSTA?") < 0) + { + LOG_E("esp8266 device(%s) send \"AT+CIPSTA?\" commands error.", device->name); + goto __exit; + } + + if (at_resp_parse_line_args_by_kw(resp, "ip", resp_expr, ip) <= 0 || + at_resp_parse_line_args_by_kw(resp, "gateway", resp_expr, gateway) <= 0 || + at_resp_parse_line_args_by_kw(resp, "netmask", resp_expr, netmask) <= 0) + { + LOG_E("esp8266 device(%s) prase \"AT+CIPSTA?\" command resposne data error.", device->name); + goto __exit; + } + + /* set netdev info */ + inet_aton(ip, &ip_addr); + netdev_low_level_set_ipaddr(netdev, &ip_addr); + inet_aton(gateway, &ip_addr); + netdev_low_level_set_gw(netdev, &ip_addr); + inet_aton(netmask, &ip_addr); + netdev_low_level_set_netmask(netdev, &ip_addr); + sscanf(mac, "%x:%x:%x:%x:%x:%x", + &mac_addr[0], &mac_addr[1], &mac_addr[2], &mac_addr[3], &mac_addr[4], &mac_addr[5]); + for (num = 0; num < netdev->hwaddr_len; num++) + { + netdev->hwaddr[num] = mac_addr[num]; + } + + /* send dns server query commond "AT+CIPDNS_CUR?" and wait response */ + if (at_obj_exec_cmd(device->client, resp, "AT+CIPDNS_CUR?") < 0) + { + LOG_W("please check and update device(%s) firmware to support the \"AT+CIPDNS_CUR?\" command.", device->name); + goto __exit; + } + + if (at_resp_parse_line_args(resp, 1, resp_dns, dns_server1) <= 0 && + at_resp_parse_line_args(resp, 2, resp_dns, dns_server2) <= 0) + { + LOG_E("esp8266 device(%d) prase \"AT+CIPDNS_CUR?\" commands resposne data error.", device->name); + goto __exit; + } + + if (rt_strlen(dns_server1) > 0) + { + inet_aton(dns_server1, &ip_addr); + netdev_low_level_set_dns_server(netdev, 0, &ip_addr); + } + + if (rt_strlen(dns_server2) > 0) + { + inet_aton(dns_server2, &ip_addr); + netdev_low_level_set_dns_server(netdev, 1, &ip_addr); + } + + /* send DHCP query commond " AT+CWDHCP_CUR?" and wait response */ + if (at_obj_exec_cmd(client, resp, "AT+CWDHCP_CUR?") < 0) + { + goto __exit; + } + + /* parse response data, get the DHCP status */ + if (at_resp_parse_line_args_by_kw(resp, "+CWDHCP_CUR:", "+CWDHCP_CUR:%d", &dhcp_stat) < 0) + { + LOG_E("esp8266 device(%s) get DHCP status failed.", device->name); + goto __exit; + } + + /* Bit0 - SoftAP DHCP status, Bit1 - Station DHCP status */ + netdev_low_level_set_dhcp_status(netdev, dhcp_stat & 0x02 ? RT_TRUE : RT_FALSE); + +__exit: + if (resp) + { + at_delete_resp(resp); + } +} + +static int esp8266_net_init(struct at_device *device); + +static int esp8266_netdev_set_up(struct netdev *netdev) +{ + struct at_device *device = RT_NULL; + + device = at_device_get_by_name(AT_DEVICE_NAMETYPE_NETDEV, netdev->name); + if (device == RT_NULL) + { + LOG_E("get esp8266 device by netdev name(%s) failed.", netdev->name); + return -RT_ERROR; + } + + if (device->is_init == RT_FALSE) + { + esp8266_net_init(device); + netdev_low_level_set_status(netdev, RT_TRUE); + LOG_D("the network interface device(%s) set up status", netdev->name); + } + + return RT_EOK; +} + +static int esp8266_netdev_set_down(struct netdev *netdev) +{ + struct at_device *device = RT_NULL; + + device = at_device_get_by_name(AT_DEVICE_NAMETYPE_NETDEV, netdev->name); + if (device == RT_NULL) + { + LOG_E("get esp8266 device by netdev name(%s) failed.", netdev->name); + return -RT_ERROR; + } + + if (device->is_init == RT_TRUE) + { + device->is_init = RT_FALSE; + netdev_low_level_set_status(netdev, RT_FALSE); + LOG_D("the network interface device(%s) set down status", netdev->name); + } + + return RT_EOK; +} + +static int esp8266_netdev_set_addr_info(struct netdev *netdev, ip_addr_t *ip_addr, ip_addr_t *netmask, ip_addr_t *gw) +{ +#define IPADDR_RESP_SIZE 128 +#define IPADDR_SIZE 16 + + int result = RT_EOK; + at_response_t resp = RT_NULL; + struct at_device *device = RT_NULL; + char esp8266_ip_addr[IPADDR_SIZE] = {0}; + char esp8266_gw_addr[IPADDR_SIZE] = {0}; + char esp8266_netmask_addr[IPADDR_SIZE] = {0}; + + RT_ASSERT(netdev); + RT_ASSERT(ip_addr || netmask || gw); + + device = at_device_get_by_name(AT_DEVICE_NAMETYPE_NETDEV, netdev->name); + if (device == RT_NULL) + { + LOG_E("get esp8266 device by netdev name(%s) failed.", netdev->name); + return -RT_ERROR; + } + + resp = at_create_resp(IPADDR_RESP_SIZE, 0, rt_tick_from_millisecond(300)); + if (resp == RT_NULL) + { + LOG_E("no memory for esp8266 device(%s) response structure.", device->name); + result = -RT_ENOMEM; + goto __exit; + } + + /* Convert numeric IP address into decimal dotted ASCII representation. */ + if (ip_addr) + rt_memcpy(esp8266_ip_addr, inet_ntoa(*ip_addr), IPADDR_SIZE); + else + rt_memcpy(esp8266_ip_addr, inet_ntoa(netdev->ip_addr), IPADDR_SIZE); + + if (gw) + rt_memcpy(esp8266_gw_addr, inet_ntoa(*gw), IPADDR_SIZE); + else + rt_memcpy(esp8266_gw_addr, inet_ntoa(netdev->gw), IPADDR_SIZE); + + if (netmask) + rt_memcpy(esp8266_netmask_addr, inet_ntoa(*netmask), IPADDR_SIZE); + else + rt_memcpy(esp8266_netmask_addr, inet_ntoa(netdev->netmask), IPADDR_SIZE); + + /* send addr info set commond "AT+CIPSTA_CUR=[,,]" and wait response */ + if (at_obj_exec_cmd(device->client, resp, "AT+CIPSTA_CUR=\"%s\",\"%s\",\"%s\"", + esp8266_ip_addr, esp8266_gw_addr, esp8266_netmask_addr) < 0) + { + LOG_E("esp8266 device(%s) set address information failed.", device->name); + result = -RT_ERROR; + } + else + { + /* Update netdev information */ + if (ip_addr) + netdev_low_level_set_ipaddr(netdev, ip_addr); + + if (gw) + netdev_low_level_set_gw(netdev, gw); + + if (netmask) + netdev_low_level_set_netmask(netdev, netmask); + + LOG_D("esp8266 device(%s) set address information successfully.", device->name); + } + +__exit: + if (resp) + { + at_delete_resp(resp); + } + + return result; +} + +static int esp8266_netdev_set_dns_server(struct netdev *netdev, uint8_t dns_num, ip_addr_t *dns_server) +{ +#define DNS_RESP_SIZE 128 + + int result = RT_EOK; + at_response_t resp = RT_NULL; + struct at_device *device = RT_NULL; + + RT_ASSERT(netdev); + RT_ASSERT(dns_server); + + device = at_device_get_by_name(AT_DEVICE_NAMETYPE_NETDEV, netdev->name); + if (device == RT_NULL) + { + LOG_E("get esp8266 device by netdev name(%s) failed.", netdev->name); + return -RT_ERROR; + } + + resp = at_create_resp(DNS_RESP_SIZE, 0, rt_tick_from_millisecond(300)); + if (resp == RT_NULL) + { + LOG_E("no memory for esp8266 device(%s) response structure.", device->name); + return -RT_ENOMEM; + } + + /* send dns server set commond "AT+CIPDNS_CUR=[,,]" and wait response */ + if (at_obj_exec_cmd(device->client, resp, "AT+CIPDNS_CUR=1,\"%s\"", inet_ntoa(*dns_server)) < 0) + { + LOG_E("esp8266 device(%s) set DNS server(%s) failed.", device->name, inet_ntoa(*dns_server)); + result = -RT_ERROR; + } + else + { + netdev_low_level_set_dns_server(netdev, dns_num, dns_server); + LOG_D("esp8266 device(%s) set DNS server(%s) successfully.", device->name, inet_ntoa(*dns_server)); + } + + if (resp) + { + at_delete_resp(resp); + } + + return result; +} + +static int esp8266_netdev_set_dhcp(struct netdev *netdev, rt_bool_t is_enabled) +{ +#define ESP8266_STATION 1 +#define RESP_SIZE 128 + + int result = RT_EOK; + at_response_t resp = RT_NULL; + struct at_device *device = RT_NULL; + + RT_ASSERT(netdev); + + device = at_device_get_by_name(AT_DEVICE_NAMETYPE_NETDEV, netdev->name); + if (device == RT_NULL) + { + LOG_E("get AT device by netdev name(%s) failed.", netdev->name); + return -RT_ERROR; + } + + resp = at_create_resp(RESP_SIZE, 0, rt_tick_from_millisecond(300)); + if (resp == RT_NULL) + { + LOG_E("no memory for esp8266 device(%s) response structure.",device->name); + return -RT_ENOMEM; + } + + /* send dhcp set commond "AT+CWDHCP_CUR=," and wait response */ + if (at_obj_exec_cmd(device->client, resp, "AT+CWDHCP_CUR=%d,%d", ESP8266_STATION, is_enabled) < 0) + { + LOG_E("esp8266 device(%s) set DHCP status(%d) failed.", device->name, is_enabled); + result = -RT_ERROR; + goto __exit; + } + else + { + netdev_low_level_set_dhcp_status(netdev, is_enabled); + LOG_D("esp8266 device(%d) set DHCP status(%d) successfully.", device->name, is_enabled); + } + +__exit: + if (resp) + { + at_delete_resp(resp); + } + + return result; +} + +#ifdef NETDEV_USING_PING +static int esp8266_netdev_ping(struct netdev *netdev, const char *host, + size_t data_len, uint32_t timeout, struct netdev_ping_resp *ping_resp) +{ +#define ESP8266_PING_IP_SIZE 16 + + rt_err_t result = RT_EOK; + at_response_t resp = RT_NULL; + struct at_device *device = RT_NULL; + char ip_addr[ESP8266_PING_IP_SIZE] = {0}; + int req_time; + + RT_ASSERT(netdev); + RT_ASSERT(host); + RT_ASSERT(ping_resp); + + device = at_device_get_by_name(AT_DEVICE_NAMETYPE_NETDEV, netdev->name); + if (device == RT_NULL) + { + LOG_E("get esp8266 device by netdev name(%s) failed.", netdev->name); + return -RT_ERROR; + } + + resp = at_create_resp(64, 0, timeout); + if (resp == RT_NULL) + { + LOG_E("no memory for esp8266 device(%s) response structure.", device->name); + return -RT_ENOMEM; + } + + /* send domain commond "AT+CIPDOMAIN=" and wait response */ + if (at_obj_exec_cmd(device->client, resp, "AT+CIPDOMAIN=\"%s\"", host) < 0) + { + result = -RT_ERROR; + goto __exit; + } + + /* parse the third line of response data, get the IP address */ + if (at_resp_parse_line_args_by_kw(resp, "+CIPDOMAIN:", "+CIPDOMAIN:%s", ip_addr) < 0) + { + result = -RT_ERROR; + goto __exit; + } + + /* send ping commond "AT+PING=" and wait response */ + if (at_obj_exec_cmd(device->client, resp, "AT+PING=\"%s\"", host) < 0) + { + result = -RT_ERROR; + goto __exit; + } + + if (at_resp_parse_line_args_by_kw(resp, "+", "+%d", &req_time) < 0) + { + result = -RT_ERROR; + goto __exit; + } + + if (req_time) + { + inet_aton(ip_addr, &(ping_resp->ip_addr)); + ping_resp->data_len = data_len; + ping_resp->ttl = 0; + ping_resp->ticks = req_time; + } + +__exit: + if (resp) + { + at_delete_resp(resp); + } + + return result; +} +#endif /* NETDEV_USING_PING */ + +#ifdef NETDEV_USING_NETSTAT +void esp8266_netdev_netstat(struct netdev *netdev) +{ +#define ESP8266_NETSTAT_RESP_SIZE 320 +#define ESP8266_NETSTAT_TYPE_SIZE 4 +#define ESP8266_NETSTAT_IPADDR_SIZE 17 +#define ESP8266_NETSTAT_EXPRESSION "+CIPSTATUS:%*d,\"%[^\"]\",\"%[^\"]\",%d,%d,%*d" + + at_response_t resp = RT_NULL; + struct at_device *device = RT_NULL; + int remote_port, local_port, i; + char *type = RT_NULL; + char *ipaddr = RT_NULL; + + device = at_device_get_by_name(AT_DEVICE_NAMETYPE_NETDEV, netdev->name); + if (device == RT_NULL) + { + LOG_E("get esp8266 device by netdev name(%s) failed.", netdev->name); + return; + } + + type = (char *) rt_calloc(1, ESP8266_NETSTAT_TYPE_SIZE); + ipaddr = (char *) rt_calloc(1, ESP8266_NETSTAT_IPADDR_SIZE); + if ((type && ipaddr) == RT_NULL) + { + LOG_E("no memory for esp8266 device(%s) response structure.", device->name); + goto __exit; + } + + resp = at_create_resp(ESP8266_NETSTAT_RESP_SIZE, 0, 5 * RT_TICK_PER_SECOND); + if (resp == RT_NULL) + { + LOG_E("no memory for esp8266 device(%s) response structure.", device->name); + goto __exit; + } + + /* send network connection information commond "AT+CIPSTATUS" and wait response */ + if (at_obj_exec_cmd(device->client, resp, "AT+CIPSTATUS") < 0) + { + goto __exit; + } + + for (i = 1; i <= resp->line_counts; i++) + { + if (strstr(at_resp_get_line(resp, i), "+CIPSTATUS")) + { + /* parse the third line of response data, get the network connection information */ + if (at_resp_parse_line_args(resp, i, ESP8266_NETSTAT_EXPRESSION, type, ipaddr, &remote_port, &local_port) < 0) + { + goto __exit; + } + else + { + LOG_RAW("%s: %s:%d ==> %s:%d\n", type, inet_ntoa(netdev->ip_addr), local_port, ipaddr, remote_port); + } + } + } + +__exit: + if (resp) + { + at_delete_resp(resp); + } + + if (type) + { + rt_free(type); + } + + if (ipaddr) + { + rt_free(ipaddr); + } +} +#endif /* NETDEV_USING_NETSTAT */ + +static const struct netdev_ops esp8266_netdev_ops = +{ + esp8266_netdev_set_up, + esp8266_netdev_set_down, + + esp8266_netdev_set_addr_info, + esp8266_netdev_set_dns_server, + esp8266_netdev_set_dhcp, + +#ifdef NETDEV_USING_PING + esp8266_netdev_ping, +#endif +#ifdef NETDEV_USING_NETSTAT + esp8266_netdev_netstat, +#endif +}; + +static struct netdev *esp8266_netdev_add(const char *netdev_name) +{ +#define ETHERNET_MTU 1500 +#define HWADDR_LEN 6 + struct netdev *netdev = RT_NULL; + + RT_ASSERT(netdev_name); + + netdev = (struct netdev *) rt_calloc(1, sizeof(struct netdev)); + if (netdev == RT_NULL) + { + LOG_E("no memory for esp8266 device(%s) netdev structure.", netdev_name); + return RT_NULL; + } + + netdev->mtu = ETHERNET_MTU; + netdev->ops = &esp8266_netdev_ops; + netdev->hwaddr_len = HWADDR_LEN; + +#ifdef SAL_USING_AT + extern int sal_at_netdev_set_pf_info(struct netdev *netdev); + /* set the network interface socket/netdb operations */ + sal_at_netdev_set_pf_info(netdev); +#endif + + netdev_register(netdev, netdev_name, RT_NULL); + + return netdev; +} + +/* ============================= esp8266 device operations ============================= */ + +#define AT_SEND_CMD(client, resp, cmd) \ + do { \ + (resp) = at_resp_set_info((resp), 256, 0, 5 * RT_TICK_PER_SECOND); \ + if (at_obj_exec_cmd((client), (resp), (cmd)) < 0) \ + { \ + result = -RT_ERROR; \ + goto __exit; \ + } \ + } while(0) \ + +static void esp8266_netdev_start_delay_work(struct at_device *device) +{ + struct rt_delayed_work *net_work = RT_NULL; + net_work = (struct rt_delayed_work *)rt_calloc(1, sizeof(struct rt_delayed_work)); + if (net_work == RT_NULL) + { + return; + } + + rt_delayed_work_init(net_work, esp8266_get_netdev_info, (void *)device); + rt_work_submit(&(net_work->work), RT_TICK_PER_SECOND); +} + +static void esp8266_init_thread_entry(void *parameter) +{ +#define INIT_RETRY 5 + + struct at_device *device = (struct at_device *) parameter; + struct at_device_esp8266 *esp8266 = (struct at_device_esp8266 *) device->user_data; + struct at_client *client = device->client; + at_response_t resp = RT_NULL; + rt_err_t result = RT_EOK; + rt_size_t i = 0, retry_num = INIT_RETRY; + + LOG_D("esp8266 device(%s) initialize start.", device->name); + + /* wait esp8266 device startup finish */ + if (at_client_obj_wait_connect(client, ESP8266_WAIT_CONNECT_TIME)) + { + return; + } + + resp = at_create_resp(128, 0, 5 * RT_TICK_PER_SECOND); + if (resp == RT_NULL) + { + LOG_E("no memory for esp8266 device(%d) response structure.", device->name); + return; + } + + while (retry_num--) + { + /* reset module */ + AT_SEND_CMD(client, resp, "AT+RST"); + /* reset waiting delay */ + rt_thread_mdelay(1000); + /* disable echo */ + AT_SEND_CMD(client, resp, "ATE0"); + /* set current mode to Wi-Fi station */ + AT_SEND_CMD(client, resp, "AT+CWMODE=1"); + /* get module version */ + AT_SEND_CMD(client, resp, "AT+GMR"); + /* show module version */ + for (i = 0; i < resp->line_counts - 1; i++) + { + LOG_D("%s", at_resp_get_line(resp, i + 1)); + } + + AT_SEND_CMD(client, resp, "AT+CIPMUX=1"); + + /* connect to WiFi AP */ + if (at_obj_exec_cmd(client, at_resp_set_info(resp, 128, 0, 20 * RT_TICK_PER_SECOND), + "AT+CWJAP=\"%s\",\"%s\"", esp8266->wifi_ssid, esp8266->wifi_password) != RT_EOK) + { + LOG_E("AT device(%s) network initialize failed, check ssid(%s) and password(%s).", + device->name, esp8266->wifi_ssid, esp8266->wifi_password); + result = -RT_ERROR; + goto __exit; + } + + __exit: + if (result == RT_EOK) + { + break; + } + else + { + rt_thread_mdelay(1000); + LOG_I("esp8266 device(%s) initialize retry...", device->name); + } + } + + if (resp) + { + at_delete_resp(resp); + } + + if (result != RT_EOK) + { + netdev_low_level_set_status(device->netdev, RT_FALSE); + LOG_E("esp8266 device(%s) network initialize failed(%d).", device->name, result); + } + else + { + device->is_init = RT_TRUE; + netdev_low_level_set_status(device->netdev, RT_TRUE); + netdev_low_level_set_link_status(device->netdev, RT_TRUE); + esp8266_netdev_start_delay_work(device); + LOG_I("esp8266 device(%s) network initialize successfully.", device->name); + } +} + +static int esp8266_net_init(struct at_device *device) +{ +#ifdef AT_DEVICE_ESP8266_INIT_ASYN + rt_thread_t tid; + + tid = rt_thread_create("esp8266_net_init", esp8266_init_thread_entry, (void *) device, + ESP8266_THREAD_STACK_SIZE, ESP8266_THREAD_PRIORITY, 20); + if (tid) + { + rt_thread_startup(tid); + } + else + { + LOG_E("create esp8266 device(%s) initialize thread failed.", device->name); + return -RT_ERROR; + } +#else + esp8266_init_thread_entry(device); +#endif /* AT_DEVICE_ESP8266_INIT_ASYN */ + + return RT_EOK; +} + +static void urc_busy_p_func(struct at_client *client, const char *data, rt_size_t size) +{ + LOG_D("system is processing a commands and it cannot respond to the current commands."); +} + +static void urc_busy_s_func(struct at_client *client, const char *data, rt_size_t size) +{ + LOG_D("system is sending data and it cannot respond to the current commands."); +} + +static void urc_func(struct at_client *client, const char *data, rt_size_t size) +{ + struct at_device *device = RT_NULL; + char *client_name = client->device->parent.name; + + RT_ASSERT(client && data && size); + + device = at_device_get_by_name(AT_DEVICE_NAMETYPE_CLIENT, client_name); + if (device == RT_NULL) + { + LOG_E("get esp8266 device by client name(%s) failed.", client_name); + return; + } + + if (rt_strstr(data, "WIFI CONNECTED")) + { + LOG_I("esp8266 device(%s) WIFI is connected.", device->name); + + if (device->is_init) + { + netdev_low_level_set_link_status(device->netdev, RT_TRUE); + + esp8266_netdev_start_delay_work(device); + } + } + else if (rt_strstr(data, "WIFI DISCONNECT")) + { + LOG_I("esp8266 device(%s) WIFI is disconnect.", device->name); + + if (device->is_init) + { + netdev_low_level_set_link_status(device->netdev, RT_FALSE); + } + } +} + +static const struct at_urc urc_table[] = +{ + {"busy p", "\r\n", urc_busy_p_func}, + {"busy s", "\r\n", urc_busy_s_func}, + {"WIFI CONNECTED", "\r\n", urc_func}, + {"WIFI DISCONNECT", "\r\n", urc_func}, +}; + +static int esp8266_init(struct at_device *device) +{ + struct at_device_esp8266 *esp8266 = (struct at_device_esp8266 *) device->user_data; + + /* initialize AT client */ + at_client_init(esp8266->client_name, esp8266->recv_line_num); + + device->client = at_client_get(esp8266->client_name); + if (device->client == RT_NULL) + { + LOG_E("esp8266 device(%s) initialize failed, get AT client(%s) failed.", + esp8266->device_name, esp8266->client_name); + return -RT_ERROR; + } + + /* register URC data execution function */ + at_obj_set_urc_table(device->client, urc_table, sizeof(urc_table) / sizeof(urc_table[0])); + +#ifdef AT_USING_SOCKET + esp8266_socket_init(device); +#endif + + /* add esp8266 device to the netdev list */ + device->netdev = esp8266_netdev_add(esp8266->device_name); + if (device->netdev == RT_NULL) + { + LOG_E("esp8266 device(%s) initialize failed, get network interface device failed.", esp8266->device_name); + return -RT_ERROR; + } + + /* initialize esp8266 device network */ + return esp8266_netdev_set_up(device->netdev); +} + +static int esp8266_deinit(struct at_device *device) +{ + return esp8266_netdev_set_down(device->netdev); +} + +/* reset eap8266 device and initialize device network again */ +static int esp8266_reset(struct at_device *device) +{ + int result = RT_EOK; + struct at_client *client = device->client; + + /* send "AT+RST" commonds to esp8266 device */ + result = at_obj_exec_cmd(client, RT_NULL, "AT+RST"); + rt_thread_mdelay(1000); + + /* waiting 10 seconds for esp8266 device reset */ + device->is_init = RT_FALSE; + if (at_client_obj_wait_connect(client, ESP8266_WAIT_CONNECT_TIME)) + { + return -RT_ETIMEOUT; + } + + /* initialize esp8266 device network */ + esp8266_net_init(device); + + device->is_init = RT_TRUE; + + return result; +} + +/* change eap8266 wifi ssid and password information */ +static int esp8266_wifi_info_set(struct at_device *device, struct at_device_ssid_pwd *info) +{ + int result = RT_EOK; + struct at_response *resp = RT_NULL; + + if (info->ssid == RT_NULL || info->password == RT_NULL) + { + LOG_E("input esp8266 wifi ssid(%s) and password(%s) error.", info->ssid, info->password); + return -RT_ERROR; + } + + resp = at_create_resp(128, 0, 20 * RT_TICK_PER_SECOND); + if (resp == RT_NULL) + { + LOG_E("no memory for esp8266 device(%s) response structure.", device->name); + return -RT_ENOMEM; + } + + /* connect to input wifi ap */ + if (at_obj_exec_cmd(device->client, resp, "AT+CWJAP=\"%s\",\"%s\"", info->ssid, info->password) != RT_EOK) + { + LOG_E("esp8266 device(%s) wifi connect failed, check ssid(%s) and password(%s).", + device->name, info->ssid, info->password); + result = -RT_ERROR; + } + + if (resp) + { + at_delete_resp(resp); + } + + return result; +} + +static int esp8266_control(struct at_device *device, int cmd, void *arg) +{ + int result = -RT_ERROR; + + RT_ASSERT(device); + + switch (cmd) + { + case AT_DEVICE_CTRL_POWER_ON: + case AT_DEVICE_CTRL_POWER_OFF: + case AT_DEVICE_CTRL_LOW_POWER: + case AT_DEVICE_CTRL_SLEEP: + case AT_DEVICE_CTRL_WAKEUP: + case AT_DEVICE_CTRL_NET_CONN: + case AT_DEVICE_CTRL_NET_DISCONN: + case AT_DEVICE_CTRL_GET_SIGNAL: + case AT_DEVICE_CTRL_GET_GPS: + case AT_DEVICE_CTRL_GET_VER: + LOG_W("esp8266 not support the control command(%d).", cmd); + break; + case AT_DEVICE_CTRL_RESET: + result = esp8266_reset(device); + break; + case AT_DEVICE_CTRL_SET_WIFI_INFO: + result = esp8266_wifi_info_set(device, (struct at_device_ssid_pwd *) arg); + break; + default: + LOG_E("input error control command(%d).", cmd); + break; + } + + return result; +} + +static const struct at_device_ops esp8266_device_ops = +{ + esp8266_init, + esp8266_deinit, + esp8266_control, +}; + +static int esp8266_device_class_register(void) +{ + struct at_device_class *class = RT_NULL; + + class = (struct at_device_class *) rt_calloc(1, sizeof(struct at_device_class)); + if (class == RT_NULL) + { + LOG_E("no memory for esp8266 device class create."); + return -RT_ENOMEM; + } + + /* fill ESP8266 device class object */ +#ifdef AT_USING_SOCKET + esp8266_socket_class_register(class); +#endif + class->device_ops = &esp8266_device_ops; + + return at_device_class_register(class, AT_DEVICE_CLASS_ESP8266); +} +INIT_DEVICE_EXPORT(esp8266_device_class_register); + +#endif /* AT_DEVICE_USING_ESP8266 */ diff --git a/class/esp8266/at_device_esp8266.h b/class/esp8266/at_device_esp8266.h new file mode 100644 index 0000000..ef36717 --- /dev/null +++ b/class/esp8266/at_device_esp8266.h @@ -0,0 +1,66 @@ +/* + * File : at_device_esp8266.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 + * 2019-05-16 chenyong first version + */ + +#ifndef __AT_DEVICE_ESP8266_H__ +#define __AT_DEVICE_ESP8266_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include + +/* The maximum number of sockets supported by the esp8266 device */ +#define AT_DEVICE_ESP8266_SOCKETS_NUM 5 + +struct at_device_esp8266 +{ + char *device_name; + char *client_name; + + char *wifi_ssid; + char *wifi_password; + size_t recv_line_num; + struct at_device device; + + void *user_data; +}; + +#ifdef AT_USING_SOCKET + +/* esp8266 device socket initialize */ +int esp8266_socket_init(struct at_device *device); + +/* esp8266 device class socket register */ +int esp8266_socket_class_register(struct at_device_class *class); + +#endif /* AT_USING_SOCKET */ + +#ifdef __cplusplus +} +#endif + +#endif /* __AT_DEVICE_ESP8266_H__ */ diff --git a/class/esp8266/at_socket_esp8266.c b/class/esp8266/at_socket_esp8266.c new file mode 100644 index 0000000..a0d1557 --- /dev/null +++ b/class/esp8266/at_socket_esp8266.c @@ -0,0 +1,559 @@ +/* + * File : at_socket_esp8266.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-20 chenyong first version + * 2019-05-09 chenyong multi AT socket client support + */ + +#include +#include + +#include + +#if !defined(AT_SW_VERSION_NUM) || AT_SW_VERSION_NUM < 0x10300 +#error "This AT Client version is older, please check and update latest AT Client!" +#endif + +#define LOG_TAG "at.skt" +#include + +#if defined(AT_DEVICE_USING_ESP8266) && defined(AT_USING_SOCKET) + +#define ESP8266_MODULE_SEND_MAX_SIZE 2048 +/* set real event by current socket and current state */ +#define SET_EVENT(socket, event) (((socket + 1) << 16) | (event)) + +/* AT socket event type */ +#define ESP8266_EVENT_CONN_OK (1L << 0) +#define ESP8266_EVENT_SEND_OK (1L << 1) +#define ESP8266_EVENT_RECV_OK (1L << 2) +#define ESP8266_EVNET_CLOSE_OK (1L << 3) +#define ESP8266_EVENT_CONN_FAIL (1L << 4) +#define ESP8266_EVENT_SEND_FAIL (1L << 5) + +static at_evt_cb_t at_evt_cb_set[] = { + [AT_SOCKET_EVT_RECV] = NULL, + [AT_SOCKET_EVT_CLOSED] = NULL, +}; + +static int esp8266_socket_event_send(struct at_device *device, uint32_t event) +{ + return (int) rt_event_send(device->socket_event, event); +} + +static int esp8266_socket_event_recv(struct at_device *device, uint32_t event, uint32_t timeout, rt_uint8_t option) +{ + int result = 0; + rt_uint32_t recved; + + result = rt_event_recv(device->socket_event, event, option | RT_EVENT_FLAG_CLEAR, timeout, &recved); + if (result != RT_EOK) + { + return -RT_ETIMEOUT; + } + + return recved; +} + +/** + * close socket by AT commands. + * + * @param current socket + * + * @return 0: close socket success + * -1: send AT commands error + * -2: wait socket event timeout + * -5: no memory + */ +static int esp8266_socket_close(struct at_socket *socket) +{ + int result = RT_EOK; + at_response_t resp = RT_NULL; + int device_socket = (int) socket->user_data; + struct at_device *device = (struct at_device *) socket->device; + + resp = at_create_resp(64, 0, rt_tick_from_millisecond(300)); + if (resp == RT_NULL) + { + LOG_E("no memory for esp8266 device(%s) response structure.", device->name); + return -RT_ENOMEM; + } + + result = at_obj_exec_cmd(device->client, resp, "AT+CIPCLOSE=%d", device_socket); + + if (resp) + { + at_delete_resp(resp); + } + + return result; +} + +/** + * create TCP/UDP client or server connect by AT commands. + * + * @param socket current socket + * @param ip server or client IP address + * @param port server or client port + * @param type connect socket type(tcp, udp) + * @param is_client connection is client + * + * @return 0: connect success + * -1: connect failed, send commands error or type error + * -2: wait socket event timeout + * -5: no memory + */ +static int esp8266_socket_connect(struct at_socket *socket, char *ip, int32_t port, enum at_socket_type type, rt_bool_t is_client) +{ + int result = RT_EOK; + rt_bool_t retryed = RT_FALSE; + at_response_t resp = RT_NULL; + int device_socket = (int) socket->user_data; + struct at_device *device = (struct at_device *) socket->device; + + RT_ASSERT(ip); + RT_ASSERT(port >= 0); + + resp = at_create_resp(128, 0, 5 * RT_TICK_PER_SECOND); + if (resp == RT_NULL) + { + LOG_E("no memory for esp8266 device(%s) response structure.", device->name); + return -RT_ENOMEM; + } + +__retry: + if (is_client) + { + switch (type) + { + case AT_SOCKET_TCP: + /* send AT commands to connect TCP server */ + if (at_obj_exec_cmd(device->client, resp, + "AT+CIPSTART=%d,\"TCP\",\"%s\",%d,60", device_socket, ip, port) < 0) + { + result = -RT_ERROR; + } + break; + + case AT_SOCKET_UDP: + if (at_obj_exec_cmd(device->client, resp, + "AT+CIPSTART=%d,\"UDP\",\"%s\",%d", device_socket, ip, port) < 0) + { + result = -RT_ERROR; + } + break; + + default: + LOG_E("esp8266 device(%s) not supported connect type %d.", device->name, type); + result = -RT_ERROR; + goto __exit; + } + } + + if (result != RT_EOK && retryed == RT_FALSE) + { + LOG_D("esp8266 device(%s) socket (%d) connect failed, maybe the socket was not be closed at the last time and now will retry.", + device->name, device_socket); + if (esp8266_socket_close(socket) < 0) + { + goto __exit; + } + retryed = RT_TRUE; + result = RT_EOK; + goto __retry; + } + +__exit: + if (resp) + { + at_delete_resp(resp); + } + + return result; +} + +/** + * send data to server or client by AT commands. + * + * @param socket current socket + * @param buff send buffer + * @param bfsz send buffer size + * @param type connect socket type(tcp, udp) + * + * @return >=0: the size of send success + * -1: send AT commands error or send data error + * -2: waited socket event timeout + * -5: no memory + */ +static int esp8266_socket_send(struct at_socket *socket, const char *buff, size_t bfsz, enum at_socket_type type) +{ + int result = RT_EOK; + int event_result = 0; + size_t cur_pkt_size = 0, sent_size = 0; + at_response_t resp = RT_NULL; + int device_socket = (int) socket->user_data; + struct at_device *device = (struct at_device *) socket->device; + struct at_device_esp8266 *esp8266 = (struct at_device_esp8266 *) device->user_data; + rt_mutex_t lock = device->client->lock; + + RT_ASSERT(buff); + RT_ASSERT(bfsz > 0); + + resp = at_create_resp(128, 2, 5 * RT_TICK_PER_SECOND); + if (resp == RT_NULL) + { + LOG_E("no memory for esp8266 device(%s) response structure.", device->name); + return -RT_ENOMEM; + } + + rt_mutex_take(lock, RT_WAITING_FOREVER); + + /* set current socket for send URC event */ + esp8266->user_data = (void *) device_socket; + + /* set AT client end sign to deal with '>' sign */ + at_obj_set_end_sign(device->client, '>'); + + while (sent_size < bfsz) + { + if (bfsz - sent_size < ESP8266_MODULE_SEND_MAX_SIZE) + { + cur_pkt_size = bfsz - sent_size; + } + else + { + cur_pkt_size = ESP8266_MODULE_SEND_MAX_SIZE; + } + + /* send the "AT+CIPSEND" commands to AT server than receive the '>' response on the first line */ + if (at_obj_exec_cmd(device->client, resp, "AT+CIPSEND=%d,%d", device_socket, cur_pkt_size) < 0) + { + result = -RT_ERROR; + goto __exit; + } + + /* send the real data to server or client */ + result = (int) at_client_obj_send(device->client, buff + sent_size, cur_pkt_size); + if (result == 0) + { + result = -RT_ERROR; + goto __exit; + } + + /* waiting result event from AT URC */ + if (esp8266_socket_event_recv(device, SET_EVENT(device_socket, 0), 10 * RT_TICK_PER_SECOND, RT_EVENT_FLAG_OR) < 0) + { + LOG_E("esp8266 device(%s) socket(%d) send failed, wait connect result timeout.", device->name, device_socket); + result = -RT_ETIMEOUT; + goto __exit; + } + /* waiting OK or failed result */ + event_result = esp8266_socket_event_recv(device, ESP8266_EVENT_SEND_OK | ESP8266_EVENT_SEND_FAIL, + 5 * RT_TICK_PER_SECOND, RT_EVENT_FLAG_OR); + if (event_result < 0) + { + LOG_E("esp8266 device(%s) socket(%d) send failed, wait connect OK|FAIL timeout.", device->name, device_socket); + result = -RT_ETIMEOUT; + goto __exit; + } + /* check result */ + if (event_result & ESP8266_EVENT_SEND_FAIL) + { + LOG_E("esp8266 device(%s) socket(%d) send failed.", device->name, device_socket); + result = -RT_ERROR; + goto __exit; + } + + sent_size += cur_pkt_size; + } + +__exit: + /* reset the end sign for data */ + at_obj_set_end_sign(device->client, 0); + + rt_mutex_release(lock); + + if (resp) + { + at_delete_resp(resp); + } + + return result; +} + +/** + * domain resolve by AT commands. + * + * @param name domain name + * @param ip parsed IP address, it's length must be 16 + * + * @return 0: domain resolve success + * -2: wait socket event timeout + * -5: no memory + */ +static int esp8266_domain_resolve(const char *name, char ip[16]) +{ +#define RESOLVE_RETRY 5 + + int i, result = RT_EOK; + char recv_ip[16] = { 0 }; + at_response_t resp = RT_NULL; + struct at_device *device = RT_NULL; + + RT_ASSERT(name); + RT_ASSERT(ip); + + device = at_device_get_first_initialized(); + if (device == RT_NULL) + { + LOG_E("get first initialization esp8266 device failed."); + return -RT_ERROR; + } + + resp = at_create_resp(128, 0, 20 * RT_TICK_PER_SECOND); + if (resp == RT_NULL) + { + LOG_E("no memory for esp8266 device(%s) response structure.", device->name); + return -RT_ENOMEM; + } + + for (i = 0; i < RESOLVE_RETRY; i++) + { + if (at_obj_exec_cmd(device->client, resp, "AT+CIPDOMAIN=\"%s\"", name) < 0) + { + result = -RT_ERROR; + goto __exit; + } + + /* parse the third line of response data, get the IP address */ + if (at_resp_parse_line_args_by_kw(resp, "+CIPDOMAIN:", "+CIPDOMAIN:%s", recv_ip) < 0) + { + rt_thread_mdelay(100); + /* resolve failed, maybe receive an URC CRLF */ + continue; + } + + if (rt_strlen(recv_ip) < 8) + { + rt_thread_mdelay(100); + /* resolve failed, maybe receive an URC CRLF */ + continue; + } + else + { + rt_strncpy(ip, recv_ip, 15); + ip[15] = '\0'; + break; + } + } + +__exit: + if (resp) + { + at_delete_resp(resp); + } + + return result; + +} + +/** + * set AT socket event notice callback + * + * @param event notice event + * @param cb notice callback + */ +static void esp8266_socket_set_event_cb(at_socket_evt_t event, at_evt_cb_t cb) +{ + if (event < sizeof(at_evt_cb_set) / sizeof(at_evt_cb_set[1])) + { + at_evt_cb_set[event] = cb; + } +} + +static const struct at_socket_ops esp8266_socket_ops = +{ + esp8266_socket_connect, + esp8266_socket_close, + esp8266_socket_send, + esp8266_domain_resolve, + esp8266_socket_set_event_cb, +}; + +static void urc_send_func(struct at_client *client, const char *data, rt_size_t size) +{ + int device_socket = 0; + struct at_device *device = RT_NULL; + struct at_device_esp8266 *esp8266 = RT_NULL; + char *client_name = client->device->parent.name; + + RT_ASSERT(data && size); + + device = at_device_get_by_name(AT_DEVICE_NAMETYPE_CLIENT, client_name); + if (device == RT_NULL) + { + LOG_E("get esp8266 device by client name(%s) failed.", client_name); + return; + } + esp8266 = (struct at_device_esp8266 *) device->user_data; + device_socket = (int) esp8266->user_data; + + if (rt_strstr(data, "SEND OK")) + { + esp8266_socket_event_send(device, SET_EVENT(device_socket, ESP8266_EVENT_SEND_OK)); + } + else if (rt_strstr(data, "SEND FAIL")) + { + esp8266_socket_event_send(device, SET_EVENT(device_socket, ESP8266_EVENT_SEND_FAIL)); + } +} + +static void urc_send_bfsz_func(struct at_client *client, const char *data, rt_size_t size) +{ + static int cur_send_bfsz = 0; + + RT_ASSERT(data && size); + + sscanf(data, "Recv %d bytes", &cur_send_bfsz); +} + +static void urc_close_func(struct at_client *client, const char *data, rt_size_t size) +{ + int index = 0; + struct at_socket *socket = RT_NULL; + struct at_device *device = RT_NULL; + char *client_name = client->device->parent.name; + + RT_ASSERT(data && size); + + device = at_device_get_by_name(AT_DEVICE_NAMETYPE_CLIENT, client_name); + if (device == RT_NULL) + { + LOG_E("get esp8266 device by client name(%s) failed.", client_name); + return; + } + + sscanf(data, "%d,CLOSED", &index); + socket = &(device->sockets[index]); + + /* notice the socket is disconnect by remote */ + if (at_evt_cb_set[AT_SOCKET_EVT_CLOSED]) + { + at_evt_cb_set[AT_SOCKET_EVT_CLOSED](socket, AT_SOCKET_EVT_CLOSED, RT_NULL, 0); + } +} + +static void urc_recv_func(struct at_client *client, const char *data, rt_size_t size) +{ + int device_socket = 0; + rt_int32_t timeout = 0; + rt_size_t bfsz = 0, temp_size = 0; + char *recv_buf = RT_NULL, temp[8] = {0}; + struct at_socket *socket = RT_NULL; + struct at_device *device = RT_NULL; + char *client_name = client->device->parent.name; + + RT_ASSERT(data && size); + + device = at_device_get_by_name(AT_DEVICE_NAMETYPE_CLIENT, client_name); + if (device == RT_NULL) + { + LOG_E("get esp8266 device by client name(%s) failed.", client_name); + return; + } + + /* get the at deveice socket and receive buffer size by receive data */ + sscanf(data, "+IPD,%d,%d:", &device_socket, (int *) &bfsz); + + /* get receive timeout by receive buffer length */ + timeout = bfsz; + + if (device_socket < 0 || bfsz == 0) + return; + + recv_buf = (char *) rt_calloc(1, bfsz); + if (recv_buf == RT_NULL) + { + LOG_E("no memory for esp8266 device(%s) URC receive buffer(%d).", device->name, bfsz); + /* read and clean the coming data */ + while (temp_size < bfsz) + { + if (bfsz - temp_size > sizeof(temp)) + { + at_client_obj_recv(client, temp, sizeof(temp), timeout); + } + else + { + at_client_obj_recv(client, temp, bfsz - temp_size, timeout); + } + temp_size += sizeof(temp); + } + return; + } + + /* sync receive data */ + if (at_client_obj_recv(client, recv_buf, bfsz, timeout) != bfsz) + { + LOG_E("esp8266 device(%s) receive size(%d) data failed.", device->name, bfsz); + rt_free(recv_buf); + return; + } + + /* get at socket object by device socket descriptor */ + socket = &(device->sockets[device_socket]); + + /* notice the receive buffer and buffer size */ + if (at_evt_cb_set[AT_SOCKET_EVT_RECV]) + { + at_evt_cb_set[AT_SOCKET_EVT_RECV](socket, AT_SOCKET_EVT_RECV, recv_buf, bfsz); + } +} + +static const struct at_urc urc_table[] = +{ + {"SEND OK", "\r\n", urc_send_func}, + {"SEND FAIL", "\r\n", urc_send_func}, + {"Recv", "bytes\r\n", urc_send_bfsz_func}, + {"", ",CLOSED\r\n", urc_close_func}, + {"+IPD", ":", urc_recv_func}, +}; + +int esp8266_socket_init(struct at_device *device) +{ + RT_ASSERT(device); + + /* register URC data execution function */ + at_obj_set_urc_table(device->client, urc_table, sizeof(urc_table) / sizeof(urc_table[0])); + + return RT_EOK; +} + +int esp8266_socket_class_register(struct at_device_class *class) +{ + RT_ASSERT(class); + + class->socket_num = AT_DEVICE_ESP8266_SOCKETS_NUM; + class->socket_ops = &esp8266_socket_ops; + + return RT_EOK; +} + +#endif /* AT_DEVICE_USING_ESP8266 && AT_USING_SOCKET */ diff --git a/class/m26/at_device_m26.c b/class/m26/at_device_m26.c new file mode 100644 index 0000000..9548f0b --- /dev/null +++ b/class/m26/at_device_m26.c @@ -0,0 +1,881 @@ +/* + * File : at_device_m26.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-12 chenyong first version + * 2019-05-12 chenyong multi AT socket client support + */ + +#include +#include + +#include + +#if !defined(AT_SW_VERSION_NUM) || AT_SW_VERSION_NUM < 0x10300 +#error "This AT Client version is older, please check and update latest AT Client!" +#endif + +#define LOG_TAG "at.dev" +#include + +#ifdef AT_DEVICE_USING_M26 + +#define M26_WAIT_CONNECT_TIME 5000 +#define M26_THREAD_STACK_SIZE 1024 +#define M26_THREAD_PRIORITY (RT_THREAD_PRIORITY_MAX/2) + +static void m26_power_on(struct at_device *device) +{ + struct at_device_m26 *m26 = RT_NULL; + + m26 = (struct at_device_m26 *) device->user_data; + + /* not nead to set pin configuration for m26 device power on */ + if (m26->power_pin == -1 || m26->power_status_pin == -1) + { + return; + } + + if (rt_pin_read(m26->power_status_pin) == PIN_HIGH) + { + return; + } + rt_pin_write(m26->power_pin, PIN_HIGH); + + while (rt_pin_read(m26->power_status_pin) == PIN_LOW) + { + rt_thread_mdelay(10); + } + rt_pin_write(m26->power_pin, PIN_LOW); +} + +static void m26_power_off(struct at_device *device) +{ + struct at_device_m26 *m26 = RT_NULL; + + m26 = (struct at_device_m26 *) device->user_data; + + /* not nead to set pin configuration for m26 device power on */ + if (m26->power_pin == -1 || m26->power_status_pin == -1) + { + return; + } + + if (rt_pin_read(m26->power_status_pin) == PIN_LOW) + { + return; + } + rt_pin_write(m26->power_pin, PIN_HIGH); + + while (rt_pin_read(m26->power_status_pin) == PIN_HIGH) + { + rt_thread_mdelay(10); + } + rt_pin_write(m26->power_pin, PIN_LOW); +} + +/* ============================= m26 network interface operations ============================= */ + +/* set m26 network interface device status and address information */ +static int m26_netdev_set_info(struct netdev *netdev) +{ +#define M26_IEMI_RESP_SIZE 32 +#define M26_IPADDR_RESP_SIZE 32 +#define M26_DNS_RESP_SIZE 96 +#define M26_INFO_RESP_TIMO rt_tick_from_millisecond(300) + + int result = RT_EOK; + ip_addr_t addr; + at_response_t resp = RT_NULL; + struct at_device *device = RT_NULL; + struct at_client *client = RT_NULL; + + if (netdev == RT_NULL) + { + LOG_E("input network interface device is NULL."); + return -RT_ERROR; + } + + device = at_device_get_by_name(AT_DEVICE_NAMETYPE_NETDEV, netdev->name); + if (device == RT_NULL) + { + LOG_E("get m26 deivce by netdev name failed.", netdev->name); + return -RT_ERROR; + } + client = device->client; + + /* set network interface device up status */ + netdev_low_level_set_status(netdev, RT_TRUE); + netdev_low_level_set_dhcp_status(netdev, RT_TRUE); + + resp = at_create_resp(M26_IEMI_RESP_SIZE, 0, M26_INFO_RESP_TIMO); + if (resp == RT_NULL) + { + LOG_E("no memory for m26 device(%s) response structure.", netdev->name); + result = -RT_ENOMEM; + goto __exit; + } + + /* set network interface device hardware address(IEMI) */ + { + #define M26_NETDEV_HWADDR_LEN 8 + #define M26_IEMI_LEN 15 + + char iemi[M26_IEMI_LEN] = {0}; + int i = 0, j = 0; + + /* send "AT+GSN" commond to get device IEMI */ + if (at_obj_exec_cmd(client, resp, "AT+GSN") < 0) + { + result = -RT_ERROR; + goto __exit; + } + + if (at_resp_parse_line_args(resp, 2, "%s", iemi) <= 0) + { + LOG_E("m26 device(%s) prase \"AT+GSN\" commands resposne data error.", device->name); + result = -RT_ERROR; + goto __exit; + } + + LOG_D("m26 device(%s) IEMI number: %s", device->name, iemi); + + netdev->hwaddr_len = M26_NETDEV_HWADDR_LEN; + /* get hardware address by IEMI */ + for (i = 0, j = 0; i < M26_NETDEV_HWADDR_LEN && j < M26_IEMI_LEN; i++, j+=2) + { + if (j != M26_IEMI_LEN - 1) + { + netdev->hwaddr[i] = (iemi[j] - '0') * 10 + (iemi[j + 1] - '0'); + } + else + { + netdev->hwaddr[i] = (iemi[j] - '0'); + } + } + } + + /* set network interface device IP address */ + { + #define IP_ADDR_SIZE_MAX 16 + char ipaddr[IP_ADDR_SIZE_MAX] = {0}; + + at_resp_set_info(resp, M26_IPADDR_RESP_SIZE, 2, M26_INFO_RESP_TIMO); + + /* send "AT+QILOCIP" commond to get IP address */ + if (at_obj_exec_cmd(client, resp, "AT+QILOCIP") < 0) + { + result = -RT_ERROR; + goto __exit; + } + + if (at_resp_parse_line_args_by_kw(resp, ".", "%s", ipaddr) <= 0) + { + LOG_E("m26 device(%s) prase \"AT+QILOCIP\" commands resposne data error.", device->name); + result = -RT_ERROR; + goto __exit; + } + + LOG_D("m26 device(%s) IP address: %s", device->name, ipaddr); + + /* set network interface address information */ + inet_aton(ipaddr, &addr); + netdev_low_level_set_ipaddr(netdev, &addr); + } + + /* set network interface device dns server */ + { + #define DNS_ADDR_SIZE_MAX 16 + char dns_server1[DNS_ADDR_SIZE_MAX] = {0}, dns_server2[DNS_ADDR_SIZE_MAX] = {0}; + + at_resp_set_info(resp, M26_DNS_RESP_SIZE, 0, M26_INFO_RESP_TIMO); + + /* send "AT+QIDNSCFG?" commond to get DNS servers address */ + if (at_obj_exec_cmd(client, resp, "AT+QIDNSCFG?") < 0) + { + result = -RT_ERROR; + goto __exit; + } + + if (at_resp_parse_line_args_by_kw(resp, "PrimaryDns:", "PrimaryDns:%s", dns_server1) <= 0 || + at_resp_parse_line_args_by_kw(resp, "SecondaryDns:", "SecondaryDns:%s", dns_server2) <= 0) + { + LOG_E("Prase \"AT+QIDNSCFG?\" commands resposne data error!"); + result = -RT_ERROR; + goto __exit; + } + + LOG_D("m26 device(%s) primary DNS server address: %s", device->name, dns_server1); + LOG_D("m26 device(%s) secondary DNS server address: %s", device->name, dns_server2); + + inet_aton(dns_server1, &addr); + netdev_low_level_set_dns_server(netdev, 0, &addr); + + inet_aton(dns_server2, &addr); + netdev_low_level_set_dns_server(netdev, 1, &addr); + } + +__exit: + if (resp) + { + at_delete_resp(resp); + } + + return result; +} + +static void check_link_status_entry(void *parameter) +{ +#define M26_LINK_STATUS_OK 0 +#define M26_LINK_RESP_SIZE 64 +#define M26_LINK_RESP_TIMO (3 * RT_TICK_PER_SECOND) +#define M26_LINK_DELAY_TIME (30 * RT_TICK_PER_SECOND) + + struct netdev *netdev = (struct netdev *)parameter; + struct at_device *device = RT_NULL; + at_response_t resp = RT_NULL; + int link_status; + + device = at_device_get_by_name(AT_DEVICE_NAMETYPE_NETDEV, netdev->name); + if (device == RT_NULL) + { + LOG_E("get m26 deivce by netdev name failed.", netdev->name); + return; + } + + resp = at_create_resp(M26_LINK_RESP_SIZE, 0, M26_LINK_RESP_TIMO); + if (resp == RT_NULL) + { + LOG_E("no memory for m26 device(%s) response object.", netdev->name); + return; + } + + while (1) + { + + /* send "AT+QNSTATUS" commond to check netweork interface device link status */ + if (at_obj_exec_cmd(device->client, resp, "AT+QNSTATUS") < 0) + { + rt_thread_mdelay(M26_LINK_DELAY_TIME); + + continue; + } + + link_status = -1; + at_resp_parse_line_args_by_kw(resp, "+QNSTATUS:", "+QNSTATUS: %d", &link_status); + + /* check the network interface device link status */ + if ((M26_LINK_STATUS_OK == link_status) != netdev_is_link_up(netdev)) + { + netdev_low_level_set_link_status(netdev, (M26_LINK_STATUS_OK == link_status)); + } + + rt_thread_mdelay(M26_LINK_DELAY_TIME); + } +} + +static int m26_netdev_check_link_status(struct netdev *netdev) +{ +#define M26_LINK_THREAD_TICK 20 +#define M26_LINK_THREAD_STACK_SIZE 512 +#define M26_LINK_THREAD_PRIORITY (RT_THREAD_PRIORITY_MAX - 2) + + rt_thread_t tid; + char tname[RT_NAME_MAX] = {0}; + + if (netdev == RT_NULL) + { + LOG_E("input network interface device is NULL."); + return -RT_ERROR; + } + + rt_snprintf(tname, RT_NAME_MAX, "%s_link", netdev->name); + + tid = rt_thread_create(tname, check_link_status_entry, (void *)netdev, + M26_LINK_THREAD_STACK_SIZE, M26_LINK_THREAD_PRIORITY, M26_LINK_THREAD_TICK); + if (tid) + { + rt_thread_startup(tid); + } + + return RT_EOK; +} + +static int m26_net_init(struct at_device *device); + +static int m26_netdev_set_up(struct netdev *netdev) +{ + struct at_device *device = RT_NULL; + + device = at_device_get_by_name(AT_DEVICE_NAMETYPE_NETDEV, netdev->name); + if (device == RT_NULL) + { + LOG_E("get m26 device by netdev name(%s) failed.", netdev->name); + return -RT_ERROR; + } + + if (device->is_init == RT_FALSE) + { + m26_net_init(device); + device->is_init = RT_TRUE; + + netdev_low_level_set_status(netdev, RT_TRUE); + LOG_D("the network interface device(%s) set up status.", netdev->name); + } + + return RT_EOK; +} + +static int m26_netdev_set_down(struct netdev *netdev) +{ + struct at_device *device = RT_NULL; + + device = at_device_get_by_name(AT_DEVICE_NAMETYPE_NETDEV, netdev->name); + if (device == RT_NULL) + { + LOG_E("get m26 device by netdev name(%s) failed.", netdev->name); + return -RT_ERROR; + } + + if (device->is_init == RT_TRUE) + { + m26_power_off(device); + device->is_init = RT_FALSE; + + netdev_low_level_set_status(netdev, RT_FALSE); + LOG_D("the network interface device(%s) set down status.", netdev->name); + } + + return RT_EOK; +} + +static int m26_netdev_set_dns_server(struct netdev *netdev, uint8_t dns_num, ip_addr_t *dns_server) +{ +#define M26_DNS_RESP_LEN 8 +#define M26_DNS_RESP_TIMEO rt_tick_from_millisecond(300) + + int result = RT_EOK; + at_response_t resp = RT_NULL; + struct at_device *device = RT_NULL; + + RT_ASSERT(netdev); + RT_ASSERT(dns_server); + + device = at_device_get_by_name(AT_DEVICE_NAMETYPE_NETDEV, netdev->name); + if (device == RT_NULL) + { + LOG_E("get m26 deivce by netdev name failed.", netdev->name); + return - RT_ERROR; + } + + resp = at_create_resp(M26_DNS_RESP_LEN, 0, M26_DNS_RESP_TIMEO); + if (resp == RT_NULL) + { + LOG_E("no memory for m26 device(%s) response object.", netdev->name); + return -RT_ENOMEM; + } + + /* send "AT+QIDNSCFG=[,]" commond to set dns servers */ + if (at_exec_cmd(resp, "AT+QIDNSCFG=\"%s\"", inet_ntoa(*dns_server)) < 0) + { + result = -RT_ERROR; + goto __exit; + } + + netdev_low_level_set_dns_server(netdev, dns_num, dns_server); + +__exit: + if (resp) + { + at_delete_resp(resp); + } + return result; +} + +#ifdef NETDEV_USING_PING +static int m26_netdev_ping(struct netdev *netdev, const char *host, + size_t data_len, uint32_t timeout, struct netdev_ping_resp *ping_resp) +{ +#define M26_PING_RESP_SIZE 128 +#define M26_PING_IP_SIZE 16 +#define M26_PING_TIMEO (5 * RT_TICK_PER_SECOND) + + int result = RT_EOK; + at_response_t resp = RT_NULL; + char ip_addr[M26_PING_IP_SIZE] = {0}; + int response, recv_data_len, time, ttl; + struct at_device *device = RT_NULL; + + RT_ASSERT(netdev); + RT_ASSERT(host); + RT_ASSERT(ping_resp); + + device = at_device_get_by_name(AT_DEVICE_NAMETYPE_NETDEV, netdev->name); + if (device == RT_NULL) + { + LOG_E("get m26 deivce by netdev name failed.", netdev->name); + return - RT_ERROR; + } + + resp = at_create_resp(M26_PING_RESP_SIZE, 5, M26_PING_TIMEO); + if (resp == RT_NULL) + { + LOG_E("no memory for m26 device(%s) response object.", netdev->name); + return -RT_ENOMEM; + } + /* send "AT+QPING=""[,[][,]]" commond to send ping request */ + if (at_obj_exec_cmd(device->client, resp, "AT+QPING=\"%s\",%d,1", host, M26_PING_TIMEO / RT_TICK_PER_SECOND) < 0) + { + result = -RT_ERROR; + goto __exit; + } + + at_resp_parse_line_args_by_kw(resp, "+QPING:","+QPING:%d", &response); + /* Received the ping response from the server */ + if (response == 0) + { + if (at_resp_parse_line_args_by_kw(resp, "+QPING:", "+QPING:%d,%[^,],%d,%d,%d", + &response, ip_addr, &recv_data_len, &time, &ttl) <= 0) + { + result = -RT_ERROR; + goto __exit; + } + } + + /* prase response number */ + switch (response) + { + case 0: + inet_aton(ip_addr, &(ping_resp->ip_addr)); + ping_resp->data_len = recv_data_len; + ping_resp->ticks = time; + ping_resp->ttl = ttl; + result = RT_EOK; + break; + case 1: + result = -RT_ETIMEOUT; + break; + default: + result = -RT_ERROR; + break; + } + + __exit: + if (resp) + { + at_delete_resp(resp); + } + + return result; +} +#endif /* NETDEV_USING_PING */ + +#ifdef NETDEV_USING_NETSTAT +static void m26_netdev_netstat(struct netdev *netdev) +{ + // TODO netstat support +} +#endif /* NETDEV_USING_NETSTAT */ + +const struct netdev_ops m26_netdev_ops = +{ + m26_netdev_set_up, + m26_netdev_set_down, + + RT_NULL, /* not support set ip, netmask, gatway address */ + m26_netdev_set_dns_server, + RT_NULL, /* not support set DHCP status */ + +#ifdef NETDEV_USING_PING + m26_netdev_ping, +#endif +#ifdef NETDEV_USING_NETSTAT + m26_netdev_netstat, +#endif +}; + +static struct netdev *m26_netdev_add(const char *netdev_name) +{ +#define M26_NETDEV_MTU 1500 + + struct netdev *netdev = RT_NULL; + + RT_ASSERT(netdev_name); + + netdev = (struct netdev *) rt_calloc(1, sizeof(struct netdev)); + if (netdev == RT_NULL) + { + LOG_E("no memory for m26 device(%s) netdev structure.", netdev_name); + return RT_NULL; + } + + netdev->mtu = M26_NETDEV_MTU; + netdev->ops = &m26_netdev_ops; + +#ifdef SAL_USING_AT + extern int sal_at_netdev_set_pf_info(struct netdev *netdev); + /* set the network interface socket/netdb operations */ + sal_at_netdev_set_pf_info(netdev); +#endif + + netdev_register(netdev, netdev_name, RT_NULL); + + return netdev; +} + +/* ============================= m26 device operations ============================= */ + +#define AT_SEND_CMD(client, resp, resp_line, timeout, cmd) \ + do { \ + (resp) = at_resp_set_info((resp), 128, (resp_line), rt_tick_from_millisecond(timeout)); \ + if (at_obj_exec_cmd((client),(resp), (cmd)) < 0) \ + { \ + result = -RT_ERROR; \ + goto __exit; \ + } \ + } while(0); \ + +/* init for m26 or mc20 */ +static void m26_init_thread_entry(void *parameter) +{ +#define INIT_RETRY 5 +#define CPIN_RETRY 10 +#define CSQ_RETRY 10 +#define CREG_RETRY 10 +#define CGREG_RETRY 20 + + at_response_t resp = RT_NULL; + int i, qimux, qimode; + int retry_num = INIT_RETRY; + char parsed_data[10]; + rt_err_t result = RT_EOK; + struct at_device *device = (struct at_device *)parameter; + struct at_client *client = device->client; + + resp = at_create_resp(128, 0, rt_tick_from_millisecond(300)); + if (resp == RT_NULL) + { + LOG_E("no memory for m26 device(%s) response structure.", device->name); + return; + } + + LOG_D("start initializing the m26/mc20 device(%s)", device->name); + + while (retry_num--) + { + /* power on the m26 device */ + m26_power_on(device); + rt_thread_mdelay(1000); + + /* wait m26|mc20 startup finish */ + if (at_client_obj_wait_connect(client, M26_WAIT_CONNECT_TIME)) + { + result = -RT_ETIMEOUT; + goto __exit; + } + + /* disable echo */ + AT_SEND_CMD(client, resp, 0, 300, "ATE0"); + /* get module version */ + AT_SEND_CMD(client, resp, 0, 300, "ATI"); + /* show module version */ + for (i = 0; i < (int) resp->line_counts - 1; i++) + { + LOG_D("%s", at_resp_get_line(resp, i + 1)); + } + /* check SIM card */ + for (i = 0; i < CPIN_RETRY; i++) + { + AT_SEND_CMD(client, resp, 2, 5 * RT_TICK_PER_SECOND, "AT+CPIN?"); + + if (at_resp_get_line_by_kw(resp, "READY")) + { + LOG_D("m26 device(%s) SIM card detection success.", device->name); + break; + } + rt_thread_mdelay(1000); + } + if (i == CPIN_RETRY) + { + LOG_E("SIM card detection failed!"); + result = -RT_ERROR; + goto __exit; + } + /* waiting for dirty data to be digested */ + rt_thread_mdelay(10); + /* check signal strength */ + for (i = 0; i < CSQ_RETRY; i++) + { + AT_SEND_CMD(client, resp, 0, 300, "AT+CSQ"); + at_resp_parse_line_args_by_kw(resp, "+CSQ:", "+CSQ: %s", &parsed_data); + if (rt_strncmp(parsed_data, "99,99", sizeof(parsed_data))) + { + LOG_D("m26 device(%s) signal strength: %s", device->name, parsed_data); + break; + } + rt_thread_mdelay(1000); + } + if (i == CSQ_RETRY) + { + LOG_E("m26 device(%s) signal strength check failed(%s).", device->name, parsed_data); + result = -RT_ERROR; + goto __exit; + } + /* check the GSM network is registered */ + for (i = 0; i < CREG_RETRY; i++) + { + AT_SEND_CMD(client, resp, 0, 300, "AT+CREG?"); + at_resp_parse_line_args_by_kw(resp, "+CREG:", "+CREG: %s", &parsed_data); + if (!rt_strncmp(parsed_data, "0,1", sizeof(parsed_data)) || + !rt_strncmp(parsed_data, "0,5", sizeof(parsed_data))) + { + LOG_D("m26 device(%s) GSM network is registered(%s).", device->name, parsed_data); + break; + } + rt_thread_mdelay(1000); + } + if (i == CREG_RETRY) + { + LOG_E("m26 device(%s) GSM network is register failed(%s)", device->name, parsed_data); + result = -RT_ERROR; + goto __exit; + } + /* check the GPRS network is registered */ + for (i = 0; i < CGREG_RETRY; i++) + { + AT_SEND_CMD(client, resp, 0, 300, "AT+CGREG?"); + at_resp_parse_line_args_by_kw(resp, "+CGREG:", "+CGREG: %s", &parsed_data); + if (!rt_strncmp(parsed_data, "0,1", sizeof(parsed_data)) || + !rt_strncmp(parsed_data, "0,5", sizeof(parsed_data))) + { + LOG_D("m26 device(%s) GPRS network is registered(%s).", device->name, parsed_data); + break; + } + rt_thread_mdelay(1000); + } + if (i == CGREG_RETRY) + { + LOG_E("m26 device(%s) GPRS network is register failed(%s).", device->name, parsed_data); + result = -RT_ERROR; + goto __exit; + } + + AT_SEND_CMD(client, resp, 0, 300, "AT+QIFGCNT=0"); + AT_SEND_CMD(client, resp, 0, 300, "AT+QICSGP=1, \"CMNET\""); + AT_SEND_CMD(client, resp, 0, 300, "AT+QIMODE?"); + + at_resp_parse_line_args_by_kw(resp, "+QIMODE:", "+QIMODE: %d", &qimode); + if (qimode == 1) + { + AT_SEND_CMD(client, resp, 0, 300, "AT+QIMODE=0"); + } + + /* the device default response timeout is 40 seconds, but it set to 15 seconds is convenient to use. */ + AT_SEND_CMD(client, resp, 2, 20 * 1000, "AT+QIDEACT"); + + /* Set to multiple connections */ + AT_SEND_CMD(client, resp, 0, 300, "AT+QIMUX?"); + at_resp_parse_line_args_by_kw(resp, "+QIMUX:", "+QIMUX: %d", &qimux); + if (qimux == 0) + { + AT_SEND_CMD(client, resp, 0, 300, "AT+QIMUX=1"); + } + + AT_SEND_CMD(client, resp, 0, 300, "AT+QIREGAPP"); + + /* the device default response timeout is 150 seconds, but it set to 20 seconds is convenient to use. */ + AT_SEND_CMD(client, resp, 0, 20 * 1000, "AT+QIACT"); + + AT_SEND_CMD(client, resp, 2, 300, "AT+QILOCIP"); + result = RT_EOK; + + __exit: + if (result == RT_EOK) + { + break; + } + else + { + /* power off the m26 device */ + m26_power_off(device); + rt_thread_mdelay(1000); + + LOG_I("m26 device(%s) initialize retry...", device->name); + } + } + + if (resp) + { + at_delete_resp(resp); + } + + if (result == RT_EOK) + { + m26_netdev_set_info(device->netdev); + m26_netdev_check_link_status(device->netdev); + + LOG_I("m26 device(%s) network initialize successfully.", device->name); + } + else + { + LOG_E("m26 device(%s) network initialize failed(%d).", device->name, result); + } +} + +static int m26_net_init(struct at_device *device) +{ +#ifdef AT_DEVICE_M26_INIT_ASYN + rt_thread_t tid; + + tid = rt_thread_create("m26_net_init", m26_init_thread_entry, (void *)device, + M26_THREAD_STACK_SIZE, M26_THREAD_PRIORITY, 20); + if (tid) + { + rt_thread_startup(tid); + } + else + { + LOG_E("create m26 device(%s) initialization thread failed.", device->name); + return -RT_ERROR; + } +#else + m26_init_thread_entry(device); +#endif /* AT_DEVICE_M26_INIT_ASYN */ + + return RT_EOK; +} + +static void urc_func(struct at_client *client, const char *data, rt_size_t size) +{ + RT_ASSERT(data); + + LOG_I("URC data : %.*s", size, data); +} + +static const struct at_urc urc_table[] = { + {"RING", "\r\n", urc_func}, + {"Call Ready", "\r\n", urc_func}, + {"RDY", "\r\n", urc_func}, + {"NO CARRIER", "\r\n", urc_func}, +}; + +static int m26_init(struct at_device *device) +{ + struct at_device_m26 *m26 = (struct at_device_m26 *) device->user_data; + + /* initialize AT client */ + at_client_init(m26->client_name, m26->recv_line_num); + + device->client = at_client_get(m26->client_name); + if (device->client == RT_NULL) + { + LOG_E("m26 device(%s) initialize failed, get AT client(%s) failed.", m26->device_name, m26->client_name); + return -RT_ERROR; + } + + /* register URC data execution function */ + at_obj_set_urc_table(device->client, urc_table, sizeof(urc_table) / sizeof(urc_table[0])); + +#ifdef AT_USING_SOCKET + m26_socket_init(device); +#endif + + /* add m26 netdev to the netdev list */ + device->netdev = m26_netdev_add(m26->device_name); + if (device->netdev == RT_NULL) + { + LOG_E("m26 device(%s) initialize failed, get network interface device failed.", m26->device_name); + return -RT_ERROR; + } + + /* initialize m26 pin configuration */ + if (m26->power_pin != -1 && m26->power_status_pin != -1) + { + rt_pin_mode(m26->power_pin, PIN_MODE_OUTPUT); + rt_pin_mode(m26->power_status_pin, PIN_MODE_INPUT); + } + + /* initialize m26 device network */ + return m26_netdev_set_up(device->netdev); +} + +static int m26_deinit(struct at_device *device) +{ + return m26_netdev_set_down(device->netdev); +} + +static int m26_control(struct at_device *device, int cmd, void *arg) +{ + int result = -RT_ERROR; + + RT_ASSERT(device); + + switch (cmd) + { + case AT_DEVICE_CTRL_POWER_ON: + case AT_DEVICE_CTRL_POWER_OFF: + case AT_DEVICE_CTRL_RESET: + case AT_DEVICE_CTRL_LOW_POWER: + case AT_DEVICE_CTRL_SLEEP: + case AT_DEVICE_CTRL_WAKEUP: + case AT_DEVICE_CTRL_NET_CONN: + case AT_DEVICE_CTRL_NET_DISCONN: + case AT_DEVICE_CTRL_SET_WIFI_INFO: + case AT_DEVICE_CTRL_GET_SIGNAL: + case AT_DEVICE_CTRL_GET_GPS: + case AT_DEVICE_CTRL_GET_VER: + LOG_W("m26 not support the control command(%d).", cmd); + break; + default: + LOG_E("input error control command(%d).", cmd); + break; + } + + return result; +} +static const struct at_device_ops m26_device_ops = +{ + m26_init, + m26_deinit, + m26_control, +}; + +static int m26_device_class_register(void) +{ + struct at_device_class *class = RT_NULL; + + class = (struct at_device_class *) rt_calloc(1, sizeof(struct at_device_class)); + if (class == RT_NULL) + { + LOG_E("no memory for m26 device class create."); + return -RT_ENOMEM; + } + + /* fill m26 device class object */ +#ifdef AT_USING_SOCKET + m26_socket_class_register(class); +#endif + class->device_ops = &m26_device_ops; + + return at_device_class_register(class, AT_DEVICE_CLASS_M26_MC20); +} +INIT_DEVICE_EXPORT(m26_device_class_register); + +#endif /* AT_DEVICE_USING_M26 */ diff --git a/class/m26/at_device_m26.h b/class/m26/at_device_m26.h new file mode 100644 index 0000000..164f599 --- /dev/null +++ b/class/m26/at_device_m26.h @@ -0,0 +1,66 @@ +/* + * File : at_device_m26.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 + * 2019-05-16 chenyong first version + */ + +#ifndef __AT_DEVICE_M26_H__ +#define __AT_DEVICE_M26_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include + +/* The maximum number of sockets supported by the m26 device */ +#define AT_DEVICE_M26_SOCKETS_NUM 6 + +struct at_device_m26 +{ + char *device_name; + char *client_name; + + int power_pin; + int power_status_pin; + size_t recv_line_num; + struct at_device device; + + void *user_data; +}; + +#ifdef AT_USING_SOCKET + +/* m26 device socket initialize */ +int m26_socket_init(struct at_device *device); + +/* m26 device class socket register */ +int m26_socket_class_register(struct at_device_class *class); + +#endif /* AT_USING_SOCKET */ + +#ifdef __cplusplus +} +#endif + +#endif /* __AT_DEVICE_M26_H__ */ diff --git a/class/m26/at_socket_m26.c b/class/m26/at_socket_m26.c new file mode 100644 index 0000000..ff17802 --- /dev/null +++ b/class/m26/at_socket_m26.c @@ -0,0 +1,702 @@ +/* + * File : at_socket_m26.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-12 chenyong first version + * 2019-05-12 chenyong multi AT socket client support + */ + +#include +#include + +#include + +#if !defined(AT_SW_VERSION_NUM) || AT_SW_VERSION_NUM < 0x10300 +#error "This AT Client version is older, please check and update latest AT Client!" +#endif + +#define LOG_TAG "at.dev" +#include + +#if defined(AT_DEVICE_USING_M26) && defined(AT_USING_SOCKET) + +#define M26_MODULE_SEND_MAX_SIZE 1460 + +/* set real event by current socket and current state */ +#define SET_EVENT(socket, event) (((socket + 1) << 16) | (event)) + +/* AT socket event type */ +#define M26_EVENT_CONN_OK (1L << 0) +#define M26_EVENT_SEND_OK (1L << 1) +#define M26_EVENT_RECV_OK (1L << 2) +#define M26_EVNET_CLOSE_OK (1L << 3) +#define M26_EVENT_CONN_FAIL (1L << 4) +#define M26_EVENT_SEND_FAIL (1L << 5) + +static at_evt_cb_t at_evt_cb_set[] = +{ + [AT_SOCKET_EVT_RECV] = NULL, + [AT_SOCKET_EVT_CLOSED] = NULL, +}; + +static int m26_socket_event_send(struct at_device *device, uint32_t event) +{ + return (int) rt_event_send(device->socket_event, event); +} + +static int m26_socket_event_recv(struct at_device *device, uint32_t event, uint32_t timeout, rt_uint8_t option) +{ + int result = 0; + rt_uint32_t recved = 0; + + result = rt_event_recv(device->socket_event, event, option | RT_EVENT_FLAG_CLEAR, timeout, &recved); + if (result != RT_EOK) + { + return -RT_ETIMEOUT; + } + + return recved; +} + +/** + * close socket by AT commands. + * + * @param current socket + * + * @return 0: close socket success + * -1: send AT commands error + * -2: wait socket event timeout + * -5: no memory + */ +static int m26_socket_close(struct at_socket *socket) +{ + int result = 0; + at_response_t resp = RT_NULL; + int device_socke = (int) socket->user_data; + struct at_device *device = (struct at_device *) socket->device; + + resp = at_create_resp(64, 0, rt_tick_from_millisecond(300)); + if (resp == RT_NULL) + { + LOG_E("no memory for m26 device(%s) response structure.", device->name); + return -RT_ENOMEM; + } + + /* clear socket close event */ + m26_socket_event_recv(device, SET_EVENT(device_socke, M26_EVNET_CLOSE_OK), 0, RT_EVENT_FLAG_OR); + + if (at_obj_exec_cmd(device->client, resp, "AT+QICLOSE=%d", device_socke) < 0) + { + result = -RT_ERROR; + goto __exit; + } + + if (m26_socket_event_recv(device, SET_EVENT(device_socke, M26_EVNET_CLOSE_OK), + rt_tick_from_millisecond(300 * 3), RT_EVENT_FLAG_AND) < 0) + { + LOG_E("m26 device(%s) socket(%d) close failed, wait close OK timeout.", device->name, device_socke); + result = -RT_ETIMEOUT; + goto __exit; + } + +__exit: + if (resp) + { + at_delete_resp(resp); + } + + return result; +} + +/** + * create TCP/UDP client or server connect by AT commands. + * + * @param socket current socket + * @param ip server or client IP address + * @param port server or client port + * @param type connect socket type(tcp, udp) + * @param is_client connection is client + * + * @return 0: connect success + * -1: connect failed, send commands error or type error + * -2: wait socket event timeout + * -5: no memory + */ +static int m26_socket_connect(struct at_socket *socket, char *ip, int32_t port, enum at_socket_type type, rt_bool_t is_client) +{ + rt_bool_t retryed = RT_FALSE; + at_response_t resp = RT_NULL; + int result = 0, event_result = 0; + int device_socket = (int) socket->user_data; + struct at_device *device = (struct at_device *) socket->device; + + resp = at_create_resp(128, 0, rt_tick_from_millisecond(300)); + if (resp == RT_NULL) + { + LOG_E("no memory for m26 device(%s) response structure.", device->name); + return -RT_ENOMEM; + } + + RT_ASSERT(ip); + RT_ASSERT(port >= 0); + +__retry: + + /* clear socket connect event */ + event_result = SET_EVENT(device_socket, M26_EVENT_CONN_OK | M26_EVENT_CONN_FAIL); + m26_socket_event_recv(device, event_result, 0, RT_EVENT_FLAG_OR); + + if (is_client) + { + switch (type) + { + case AT_SOCKET_TCP: + /* send AT commands(eg: AT+QIOPEN=0,"TCP","x.x.x.x", 1234) to connect TCP server */ + if (at_obj_exec_cmd(device->client, resp, + "AT+QIOPEN=%d,\"TCP\",\"%s\",%d", device_socket, ip, port) < 0) + { + result = -RT_ERROR; + goto __exit; + } + break; + + case AT_SOCKET_UDP: + if (at_obj_exec_cmd(device->client, resp, + "AT+QIOPEN=%d,\"UDP\",\"%s\",%d", device_socket, ip, port) < 0) + { + result = -RT_ERROR; + goto __exit; + } + break; + + default: + LOG_E("m26 device(%s) not supported connect type : %d.", device->name, type); + return -RT_ERROR; + } + } + + /* waiting result event from AT URC, the device default connection timeout is 75 seconds, but it set to 10 seconds is convenient to use.*/ + if (m26_socket_event_recv(device, SET_EVENT(device_socket, 0), 10 * RT_TICK_PER_SECOND, RT_EVENT_FLAG_OR) < 0) + { + LOG_E("m26 device(%s) socket(%d) connect failed, wait connect result timeout.", device->name, device_socket); + result = -RT_ETIMEOUT; + goto __exit; + } + /* waiting OK or failed result */ + if ((event_result = m26_socket_event_recv(device, M26_EVENT_CONN_OK | M26_EVENT_CONN_FAIL, + 1 * RT_TICK_PER_SECOND, RT_EVENT_FLAG_OR)) < 0) + { + LOG_E("m26 device(%s) socket(%d) connect failed, wait connect OK|FAIL timeout.", device->name, device_socket); + result = -RT_ETIMEOUT; + goto __exit; + } + /* check result */ + if (event_result & M26_EVENT_CONN_FAIL) + { + if (retryed == RT_FALSE) + { + LOG_D("m26 device(%s) socket(%d) connect failed, maybe the socket was not be closed and now will retry.", + device->name, device_socket); + if (m26_socket_close(socket) < 0) + { + goto __exit; + } + retryed = RT_TRUE; + goto __retry; + } + LOG_E("m26 device(%s) socket(%d) connect failed, failed to establish a connection.", device->name, device_socket); + result = -RT_ERROR; + goto __exit; + } + +__exit: + if (resp) + { + at_delete_resp(resp); + } + + return result; +} + +static int at_get_send_size(struct at_socket *socket, size_t *size, size_t *acked, size_t *nacked) +{ + int result = 0; + at_response_t resp = RT_NULL; + int device_socket = (int) socket->user_data; + struct at_device *device = (struct at_device *) socket->device; + + resp = at_create_resp(64, 0, 5 * RT_TICK_PER_SECOND); + if (resp == RT_NULL) + { + LOG_E("no memory for m26 device(%s) response structure!", device->name); + result = -RT_ENOMEM; + goto __exit; + } + + if (at_obj_exec_cmd(device->client, resp, "AT+QISACK=%d", device_socket) < 0) + { + result = -RT_ERROR; + goto __exit; + } + + if (at_resp_parse_line_args_by_kw(resp, "+QISACK:", "+QISACK: %d, %d, %d", size, acked, nacked) <= 0) + { + LOG_E("m26 device(%s) prase \"AT+QISACK\" commands resposne data error!", device->name); + result = -RT_ERROR; + goto __exit; + } + +__exit: + if (resp) + { + at_delete_resp(resp); + } + + return result; +} + +static int at_wait_send_finish(struct at_socket *socket, size_t settings_size) +{ + /* get the timeout by the input data size */ + rt_tick_t timeout = rt_tick_from_millisecond(settings_size); + rt_tick_t last_time = rt_tick_get(); + size_t size = 0, acked = 0, nacked = 0xFFFF; + + while (rt_tick_get() - last_time <= timeout) + { + at_get_send_size(socket, &size, &acked, &nacked); + if (nacked == 0) + { + return RT_EOK; + } + rt_thread_mdelay(50); + } + + return -RT_ETIMEOUT; +} + +/** + * send data to server or client by AT commands. + * + * @param socket current socket + * @param buff send buffer + * @param bfsz send buffer size + * @param type connect socket type(tcp, udp) + * + * @return >=0: the size of send success + * -1: send AT commands error or send data error + * -2: waited socket event timeout + * -5: no memory + */ +static int m26_socket_send(struct at_socket *socket, const char *buff, size_t bfsz, enum at_socket_type type) +{ + int result = 0, event_result = 0; + size_t pkt_size = 0, sent_size = 0; + at_response_t resp = RT_NULL; + int device_socket = (int) socket->user_data; + struct at_device *device = (struct at_device *) socket->device; + struct at_device_m26 *m26 = (struct at_device_m26 *) device->user_data; + rt_mutex_t lock = device->client->lock; + + RT_ASSERT(buff); + + resp = at_create_resp(128, 2, 5 * RT_TICK_PER_SECOND); + if (resp == RT_NULL) + { + LOG_E("no memory for m26 device(%s) response structure.", device->name); + return -RT_ENOMEM; + } + + rt_mutex_take(lock, RT_WAITING_FOREVER); + + /* Clear socket send event */ + event_result = SET_EVENT(device_socket, M26_EVENT_SEND_OK | M26_EVENT_SEND_FAIL); + m26_socket_event_recv(device, event_result, 0, RT_EVENT_FLAG_OR); + + /* set current socket for send URC event */ + m26->user_data = (void *) device_socket; + /* set AT client end sign to deal with '>' sign.*/ + at_obj_set_end_sign(device->client, '>'); + + while (sent_size < bfsz) + { + if (bfsz - sent_size < M26_MODULE_SEND_MAX_SIZE) + { + pkt_size = bfsz - sent_size; + } + else + { + pkt_size = M26_MODULE_SEND_MAX_SIZE; + } + + /* send the "AT+QISEND" commands to AT server than receive the '>' response on the first line. */ + if (at_obj_exec_cmd(device->client, resp, "AT+QISEND=%d,%d", device_socket, pkt_size) < 0) + { + result = -RT_ERROR; + goto __exit; + } + + /* send the real data to server or client */ + result = (int) at_client_obj_send(device->client, buff + sent_size, pkt_size); + if (result == 0) + { + result = -RT_ERROR; + goto __exit; + } + + /* waiting result event from AT URC */ + if (m26_socket_event_recv(device, SET_EVENT(device_socket, 0), 15 * RT_TICK_PER_SECOND, RT_EVENT_FLAG_OR) < 0) + { + LOG_E("m26 device(%s) socket(%d) send failed, wait connect result timeout.", device->name, device_socket); + result = -RT_ETIMEOUT; + goto __exit; + } + /* waiting OK or failed result */ + if ((event_result = m26_socket_event_recv(device, M26_EVENT_SEND_OK | M26_EVENT_SEND_FAIL, + 1 * RT_TICK_PER_SECOND, RT_EVENT_FLAG_OR)) < 0) + { + LOG_E("m26 device(%s) socket(%d) send failed, wait connect OK|FAIL timeout.", device->name, device_socket); + result = -RT_ETIMEOUT; + goto __exit; + } + /* check result */ + if (event_result & M26_EVENT_SEND_FAIL) + { + LOG_E("m26 device(%s) socket(%d) send failed.", device->name, device_socket); + result = -RT_ERROR; + goto __exit; + } + + if (type == AT_SOCKET_TCP) + { + at_wait_send_finish(socket, pkt_size); + } + + sent_size += pkt_size; + } + +__exit: + /* reset the end sign for data conflict */ + at_obj_set_end_sign(device->client, 0); + + rt_mutex_release(lock); + + if (resp) + { + at_delete_resp(resp); + } + + return result; +} + +/** + * domain resolve by AT commands. + * + * @param name domain name + * @param ip parsed IP address, it's length must be 16 + * + * @return 0: domain resolve success + * -1: send AT commands error or response error + * -2: wait socket event timeout + * -5: no memory + */ +static int m26_domain_resolve(const char *name, char ip[16]) +{ +#define RESOLVE_RETRY 5 + + int i, result = RT_EOK; + char recv_ip[16] = { 0 }; + at_response_t resp = RT_NULL; + struct at_device *device = RT_NULL; + + RT_ASSERT(name); + RT_ASSERT(ip); + + device = at_device_get_first_initialized(); + if (device == RT_NULL) + { + LOG_E("get first initialization m26 device failed."); + return -RT_ERROR; + } + + /* The maximum response time is 14 seconds, affected by network status */ + resp = at_create_resp(128, 4, 14 * RT_TICK_PER_SECOND); + if (resp == RT_NULL) + { + LOG_E("no memory for m26 device(%s) response structure.", device->name); + return -RT_ENOMEM; + } + + for(i = 0; i < RESOLVE_RETRY; i++) + { + if (at_obj_exec_cmd(device->client, resp, "AT+QIDNSGIP=\"%s\"", name) < 0) + { + result = -RT_ERROR; + goto __exit; + } + + /* parse the third line of response data, get the IP address */ + if(at_resp_parse_line_args_by_kw(resp, ".", "%s", recv_ip) < 0) + { + rt_thread_mdelay(100); + /* resolve failed, maybe receive an URC CRLF */ + continue; + } + + if (rt_strlen(recv_ip) < 8) + { + rt_thread_mdelay(100); + /* resolve failed, maybe receive an URC CRLF */ + continue; + } + else + { + rt_thread_mdelay(10); + rt_strncpy(ip, recv_ip, 15); + ip[15] = '\0'; + break; + } + } + +__exit: + if (resp) + { + at_delete_resp(resp); + } + + return result; + +} + +/** + * set AT socket event notice callback + * + * @param event notice event + * @param cb notice callback + */ +static void m26_socket_set_event_cb(at_socket_evt_t event, at_evt_cb_t cb) +{ + if (event < sizeof(at_evt_cb_set) / sizeof(at_evt_cb_set[1])) + { + at_evt_cb_set[event] = cb; + } +} + +static void urc_connect_func(struct at_client *client, const char *data, rt_size_t size) +{ + int device_socket = 0; + struct at_device *device = RT_NULL; + char *client_name = client->device->parent.name; + + RT_ASSERT(data && size); + + device = at_device_get_by_name(AT_DEVICE_NAMETYPE_CLIENT, client_name); + if (device == RT_NULL) + { + LOG_E("get m26 device by client name(%s) failed.", client_name); + return; + } + + sscanf(data, "%d%*[^0-9]", &device_socket); + + if (rt_strstr(data, "CONNECT OK")) + { + m26_socket_event_send(device, SET_EVENT(device_socket, M26_EVENT_CONN_OK)); + } + else + { + m26_socket_event_send(device, SET_EVENT(device_socket, M26_EVENT_CONN_FAIL)); + } +} + +static void urc_send_func(struct at_client *client, const char *data, rt_size_t size) +{ + int device_socket = 0; + struct at_device *device = RT_NULL; + struct at_device_m26 *m26 = RT_NULL; + char *client_name = client->device->parent.name; + + RT_ASSERT(data && size); + + device = at_device_get_by_name(AT_DEVICE_NAMETYPE_CLIENT, client_name); + if (device == RT_NULL) + { + LOG_E("get m26 device by client name(%s) failed.", client_name); + return; + } + m26 = (struct at_device_m26 *) device->user_data; + device_socket = (int) m26->user_data; + + if (rt_strstr(data, "SEND OK")) + { + m26_socket_event_send(device, SET_EVENT(device_socket, M26_EVENT_SEND_OK)); + } + else if (rt_strstr(data, "SEND FAIL")) + { + m26_socket_event_send(device, SET_EVENT(device_socket, M26_EVENT_SEND_FAIL)); + } +} + +static void urc_close_func(struct at_client *client, const char *data, rt_size_t size) +{ + int device_socket = 0; + struct at_device *device = RT_NULL; + char *client_name = client->device->parent.name; + + RT_ASSERT(data && size); + + device = at_device_get_by_name(AT_DEVICE_NAMETYPE_CLIENT, client_name); + if (device == RT_NULL) + { + LOG_E("get m26 device by client name(%s) failed.", client_name); + return; + } + + sscanf(data, "%d%*s", &device_socket); + + if (rt_strstr(data, "CLOSE OK")) + { + m26_socket_event_send(device, SET_EVENT(device_socket, M26_EVNET_CLOSE_OK)); + } + else if (rt_strstr(data, "CLOSED")) + { + struct at_socket *socket = RT_NULL; + + /* get at socket object by device socket descriptor */ + socket = &(device->sockets[device_socket]); + + /* notice the socket is disconnect by remote */ + if (at_evt_cb_set[AT_SOCKET_EVT_CLOSED]) + { + at_evt_cb_set[AT_SOCKET_EVT_CLOSED](socket, AT_SOCKET_EVT_CLOSED, NULL, 0); + } + } +} + +static void urc_recv_func(struct at_client *client, const char *data, rt_size_t size) +{ + int device_socket = 0; + rt_int32_t timeout; + rt_size_t bfsz = 0, temp_size = 0; + char *recv_buf = RT_NULL, temp[8] = {0}; + struct at_socket *socket = RT_NULL; + struct at_device *device = RT_NULL; + char *client_name = client->device->parent.name; + + RT_ASSERT(data && size); + + device = at_device_get_by_name(AT_DEVICE_NAMETYPE_CLIENT, client_name); + if (device == RT_NULL) + { + LOG_E("get m26 device by client name(%s) failed.", client_name); + return; + } + + /* get the current socket and receive buffer size by receive data */ + sscanf(data, "+RECEIVE: %d, %d", &device_socket, (int *) &bfsz); + + /* get receive timeout by receive buffer length */ + timeout = bfsz; + + if (device_socket < 0 || bfsz == 0) + { + return; + } + + recv_buf = (char *) rt_calloc(1, bfsz); + if (recv_buf == RT_NULL) + { + LOG_E("no memory for m26 device(%s) urc receive buffer (%d).", device->name, bfsz); + /* read and clean the coming data */ + while (temp_size < bfsz) + { + if (bfsz - temp_size > sizeof(temp)) + { + at_client_obj_recv(client, temp, sizeof(temp), timeout); + } + else + { + at_client_obj_recv(client, temp, bfsz - temp_size, timeout); + } + temp_size += sizeof(temp); + } + return; + } + + /* sync receive data */ + if (at_client_obj_recv(client, recv_buf, bfsz, timeout) != bfsz) + { + LOG_E("m26 device(%s) receive size(%d) data failed.", device->name, bfsz); + rt_free(recv_buf); + return; + } + + /* get at socket object by device socket descriptor */ + socket = &(device->sockets[device_socket]); + + /* notice the receive buffer and buffer size */ + if (at_evt_cb_set[AT_SOCKET_EVT_RECV]) + { + at_evt_cb_set[AT_SOCKET_EVT_RECV](socket, AT_SOCKET_EVT_RECV, recv_buf, bfsz); + } +} + +static const struct at_urc urc_table[] = +{ + {"", ", CONNECT OK\r\n", urc_connect_func}, + {"", ", CONNECT FAIL\r\n", urc_connect_func}, + {"SEND OK", "\r\n", urc_send_func}, + {"SEND FAIL", "\r\n", urc_send_func}, + {"", ", CLOSE OK\r\n", urc_close_func}, + {"", ", CLOSED\r\n", urc_close_func}, + {"+RECEIVE:", "\r\n", urc_recv_func}, +}; + +static const struct at_socket_ops m26_socket_ops = +{ + m26_socket_connect, + m26_socket_close, + m26_socket_send, + m26_domain_resolve, + m26_socket_set_event_cb, +}; + +int m26_socket_init(struct at_device *device) +{ + RT_ASSERT(device); + + /* register URC data execution function */ + at_obj_set_urc_table(device->client, urc_table, sizeof(urc_table) / sizeof(urc_table[0])); + + return RT_EOK; +} + +int m26_socket_class_register(struct at_device_class *class) +{ + RT_ASSERT(class); + + class->socket_num = AT_DEVICE_M26_SOCKETS_NUM; + class->socket_ops = &m26_socket_ops; + + return RT_EOK; +} + + +#endif /* AT_DEVICE_USING_M26 && AT_USING_SOCKET */ diff --git a/class/rw007/at_device_rw007.c b/class/rw007/at_device_rw007.c new file mode 100644 index 0000000..a4ff63c --- /dev/null +++ b/class/rw007/at_device_rw007.c @@ -0,0 +1,403 @@ +/* + * File : at_socket_rw007.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-20 chenyong first version + * 2019-05-12 chenyong multi AT socket client support + */ + +#include +#include + +#include + +#if !defined(AT_SW_VERSION_NUM) || AT_SW_VERSION_NUM < 0x10300 +#error "This AT Client version is older, please check and update latest AT Client!" +#endif + +#define LOG_TAG "at.dev" + +#include + +#ifdef AT_DEVICE_USING_RW007 + +#define RW007_WAIT_CONNECT_TIME 5000 +#define RW007_THREAD_STACK_SIZE 1024 +#define RW007_THREAD_PRIORITY (RT_THREAD_PRIORITY_MAX / 2) + +/* ============================= rw007 network interface operations ============================= */ + +static struct netdev *rw007_netdev_add(const char *netdev_name) +{ +#define ETHERNET_MTU 1500 +#define HWADDR_LEN 6 + struct netdev *netdev = RT_NULL; + + RT_ASSERT(netdev_name); + + netdev = (struct netdev *) rt_calloc(1, sizeof(struct netdev)); + if (netdev == RT_NULL) + { + return RT_NULL; + } + + /* TODO: improve netdev adaptation */ + netdev->mtu = ETHERNET_MTU; + netdev->hwaddr_len = HWADDR_LEN; + netdev->ops = RT_NULL; + +#ifdef SAL_USING_AT + extern int sal_at_netdev_set_pf_info(struct netdev *netdev); + /* set the network interface socket/netdb operations */ + sal_at_netdev_set_pf_info(netdev); +#endif + + netdev_register(netdev, netdev_name, RT_NULL); + + /*TODO: improve netdev adaptation */ + netdev_low_level_set_status(netdev, RT_TRUE); + netdev_low_level_set_link_status(netdev, RT_TRUE); + netdev_low_level_set_dhcp_status(netdev, RT_TRUE); + netdev->flags |= NETDEV_FLAG_INTERNET_UP; + + return netdev; +} + +/* ============================= rw007 device operations ============================= */ + +#define AT_SEND_CMD(client, resp, cmd) \ + do { \ + (resp) = at_resp_set_info((resp), 256, 0, 5 * RT_TICK_PER_SECOND); \ + if (at_obj_exec_cmd((client), (resp), (cmd)) < 0) \ + { \ + result = -RT_ERROR; \ + goto __exit; \ + } \ + } while(0) \ + +static void rw007_init_thread_entry(void *parameter) +{ +#define INIT_RETRY 5 + + struct at_device *device = (struct at_device *) parameter; + struct at_device_rw007 *rw007 = (struct at_device_rw007 *) device->user_data; + struct at_client *client = device->client; + at_response_t resp = RT_NULL; + rt_err_t result = RT_EOK; + rt_size_t i = 0, retry_num = INIT_RETRY; + + /* wait rw007 device startup finish */ + if (at_client_obj_wait_connect(client, RW007_WAIT_CONNECT_TIME)) + { + return; + } + + resp = at_create_resp(128, 0, 5 * RT_TICK_PER_SECOND); + if (resp == RT_NULL) + { + LOG_E("no memory for rw007 device(%d) response structure.", device->name); + return; + } + + while (retry_num--) + { + /* reset module */ + AT_SEND_CMD(client, resp, "AT+RST"); + /* reset waiting delay */ + rt_thread_mdelay(1000); + /* disable echo */ + AT_SEND_CMD(client, resp, "ATE0"); + /* set current mode to Wi-Fi station */ + AT_SEND_CMD(client, resp, "AT+CWMODE=1"); + /* get module version */ + AT_SEND_CMD(client, resp, "AT+GMR"); + /* show module version */ + for (i = 0; i < resp->line_counts - 1; i++) + { + LOG_D("%s", at_resp_get_line(resp, i + 1)); + } + /* connect to WiFi AP */ + if (at_obj_exec_cmd(client, at_resp_set_info(resp, 128, 0, 20 * RT_TICK_PER_SECOND), + "AT+CWJAP=\"%s\",\"%s\"", rw007->wifi_ssid, rw007->wifi_password) != RT_EOK) + { + LOG_E("rw007 device(%s) network initialize failed, check ssid(%s) and password(%s).", + device->name, rw007->wifi_ssid, rw007->wifi_password); + result = -RT_ERROR; + goto __exit; + } + + AT_SEND_CMD(client, resp, "AT+CIPMUX=1"); + + __exit: + if (result == RT_EOK) + { + break; + } + else + { + rt_thread_mdelay(1000); + LOG_I("rw007 device(%s) initialize retry...", device->name); + } + } + + if (resp) + { + at_delete_resp(resp); + } + + if (result != RT_EOK) + { + netdev_low_level_set_status(device->netdev, RT_FALSE); + LOG_E("rw007 device(%s) network initialize failed(%d).", device->name, result); + } + else + { + netdev_low_level_set_status(device->netdev, RT_TRUE); + LOG_I("rw007 device(%s) network initialize successfully.", device->name); + } +} + +int rw007_net_init(struct at_device *device) +{ +#ifdef AT_DEVICE_RW007_INIT_ASYN + rt_thread_t tid; + + tid = rt_thread_create("rw007_net_init", rw007_init_thread_entry, + (void *)device, RW007_THREAD_STACK_SIZE, RW007_THREAD_PRIORITY, 20); + if (tid) + { + rt_thread_startup(tid); + } + else + { + LOG_E("create rw007 device(%s) initialization thread failed.", device->name); + return -RT_ERROR; + } +#else + rw007_init_thread_entry(device); +#endif /* AT_DEVICE_RW007_INIT_ASYN */ + + return RT_EOK; +} + +static void urc_busy_p_func(struct at_client *client, const char *data, rt_size_t size) +{ + LOG_D("system is processing a commands and it cannot respond to the current commands."); +} + +static void urc_busy_s_func(struct at_client *client, const char *data, rt_size_t size) +{ + LOG_D("system is sending data and it cannot respond to the current commands."); +} + +static void urc_func(struct at_client *client, const char *data, rt_size_t size) +{ + struct at_device *device = RT_NULL; + char *client_name = client->device->parent.name; + + RT_ASSERT(client && data && size); + + device = at_device_get_by_name(AT_DEVICE_NAMETYPE_CLIENT, client_name); + if (device == RT_NULL) + { + LOG_E("get rw007 device by client name(%s) failed.", client_name); + return; + } + + if (rt_strstr(data, "WIFI CONNECTED")) + { + LOG_I("rw007 device(%s) WIFI is connected.", device->name); + } + else if (rt_strstr(data, "WIFI DISCONNECT")) + { + LOG_I("rw007 device(%s) WIFI is disconnect.", device->name); + } +} + +static struct at_urc urc_table[] = { + {"busy p", "\r\n", urc_busy_p_func}, + {"busy s", "\r\n", urc_busy_s_func}, + {"WIFI CONNECTED", "\r\n", urc_func}, + {"WIFI DISCONNECT", "\r\n", urc_func}, +}; + +static int rw007_init(struct at_device *device) +{ + struct at_device_rw007 *rw007 = (struct at_device_rw007 *) device->user_data; + + /* initialize AT client */ + at_client_init(rw007->client_name, rw007->recv_line_num); + + device->client = at_client_get(rw007->client_name); + if (device->client == RT_NULL) + { + LOG_E("rw007 device(%s) initialize failed, get AT client(%s) failed.", + rw007->device_name, rw007->client_name); + return -RT_ERROR; + } + + /* register URC data execution function */ + at_obj_set_urc_table(device->client, urc_table, sizeof(urc_table) / sizeof(urc_table[0])); + +#ifdef AT_USING_SOCKET + rw007_socket_init(device); +#endif + + /* add rw007 device to the netdev list */ + device->netdev = rw007_netdev_add(rw007->device_name); + if (device->netdev == RT_NULL) + { + LOG_E("rw007 device(%s) initialize failed, get network interface device failed.", rw007->device_name); + return -RT_ERROR; + } + + /* initialize rw007 device network */ + return rw007_net_init(device); +} + +static int rw007_deinit(struct at_device *device) +{ + // TODO add netdev operation + device->is_init = RT_FALSE; + return RT_EOK; +} + +/* reset eap8266 device and initialize device network again */ +static int rw007_reset(struct at_device *device) +{ + int result = RT_EOK; + struct at_client *client = device->client; + + /* send "AT+RST" commonds to rw007 device */ + result = at_obj_exec_cmd(client, RT_NULL, "AT+RST"); + rt_thread_delay(1000); + + /* waiting 10 seconds for rw007 device reset */ + device->is_init = RT_FALSE; + if (at_client_obj_wait_connect(client, RW007_WAIT_CONNECT_TIME)) + { + return -RT_ETIMEOUT; + } + + /* initialize rw007 device network */ + rw007_net_init(device); + + device->is_init = RT_TRUE; + + return result; +} + +/* change eap8266 wifi ssid and password information */ +static int rw007_wifi_info_set(struct at_device *device, struct at_device_ssid_pwd *info) +{ + int result = RT_EOK; + struct at_response *resp = RT_NULL; + + if (info->ssid == RT_NULL || info->password == RT_NULL) + { + LOG_E("input rwoo7 wifi ssid(%s) and password(%s) error.", info->ssid, info->password); + return -RT_ERROR; + } + + resp = at_create_resp(128, 0, 20 * RT_TICK_PER_SECOND); + if (resp == RT_NULL) + { + LOG_E("no memory for rw007 device(%s) response structure.", device->name); + return -RT_ENOMEM; + } + + /* connect to input wifi ap */ + if (at_obj_exec_cmd(device->client, resp, "AT+CWJAP=\"%s\",\"%s\"", info->ssid, info->password) != RT_EOK) + { + LOG_E("rw007 device(%s) wifi connect failed, check ssid(%s) and password(%s).", + device->name, info->ssid, info->password); + result = -RT_ERROR; + } + + if (resp) + { + at_delete_resp(resp); + } + + return result; +} +static int rw007_control(struct at_device *device, int cmd, void *arg) +{ + int result = -RT_ERROR; + + RT_ASSERT(device); + + switch (cmd) + { + case AT_DEVICE_CTRL_POWER_ON: + case AT_DEVICE_CTRL_POWER_OFF: + case AT_DEVICE_CTRL_LOW_POWER: + case AT_DEVICE_CTRL_SLEEP: + case AT_DEVICE_CTRL_WAKEUP: + case AT_DEVICE_CTRL_NET_CONN: + case AT_DEVICE_CTRL_NET_DISCONN: + case AT_DEVICE_CTRL_GET_SIGNAL: + case AT_DEVICE_CTRL_GET_GPS: + case AT_DEVICE_CTRL_GET_VER: + LOG_W("rw007 not support the control command(%d).", cmd); + break; + case AT_DEVICE_CTRL_RESET: + result = rw007_reset(device); + break; + case AT_DEVICE_CTRL_SET_WIFI_INFO: + result = rw007_wifi_info_set(device, (struct at_device_ssid_pwd *) arg); + break; + default: + LOG_E("input error control command(%d).", cmd); + break; + } + + return result; +} + +const struct at_device_ops rw007_device_ops = +{ + rw007_init, + rw007_deinit, + rw007_control, +}; + +static int rw007_device_class_register(void) +{ + struct at_device_class *class = RT_NULL; + + class = (struct at_device_class *) rt_calloc(1, sizeof(struct at_device_class)); + if (class == RT_NULL) + { + LOG_E("no memory for rw007 device class create."); + return -RT_ENOMEM; + } + + /* fill rw007 device class object */ +#ifdef AT_USING_SOCKET + rw007_socket_class_register(class); +#endif + class->device_ops = &rw007_device_ops; + + return at_device_class_register(class, AT_DEVICE_CLASS_RW007); +} +INIT_DEVICE_EXPORT(rw007_device_class_register); + +#endif /* AT_DEVICE_USING_RW007 */ diff --git a/class/rw007/at_device_rw007.h b/class/rw007/at_device_rw007.h new file mode 100644 index 0000000..fc906ae --- /dev/null +++ b/class/rw007/at_device_rw007.h @@ -0,0 +1,66 @@ +/* + * File : at_device_RW007.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 + * 2019-05-16 chenyong first version + */ + +#ifndef __AT_DEVICE_RW007_H__ +#define __AT_DEVICE_RW007_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include + +/* The maximum number of sockets supported by the rw007 device */ +#define AT_DEVICE_RW007_SOCKETS_NUM 5 + +struct at_device_rw007 +{ + char *device_name; + char *client_name; + + char *wifi_ssid; + char *wifi_password; + size_t recv_line_num; + struct at_device device; + + void *user_data; +}; + +#ifdef AT_USING_SOCKET + +/* rw007 device socket initialize */ +int rw007_socket_init(struct at_device *device); + +/* rw007 device class socket register */ +int rw007_socket_class_register(struct at_device_class *class); + +#endif /* AT_USING_SOCKET */ + +#ifdef __cplusplus +} +#endif + +#endif /* __AT_DEVICE_RW007_H__ */ diff --git a/class/rw007/at_socket_rw007.c b/class/rw007/at_socket_rw007.c new file mode 100644 index 0000000..846ea1e --- /dev/null +++ b/class/rw007/at_socket_rw007.c @@ -0,0 +1,561 @@ +/* + * File : at_socket_rw007.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-20 chenyong first version + * 2019-05-12 chenyong multi AT socket client support + */ + +#include +#include + +#include + +#if !defined(AT_SW_VERSION_NUM) || AT_SW_VERSION_NUM < 0x10300 +#error "This AT Client version is older, please check and update latest AT Client!" +#endif + +#define LOG_TAG "at.skt" +#include + +#if defined(AT_DEVICE_USING_RW007) && defined(AT_USING_SOCKET) + +#define RW007_MODULE_SEND_MAX_SIZE 2048 +/* set real event by current socket and current state */ +#define SET_EVENT(socket, event) (((socket + 1) << 16) | (event)) + +/* AT socket event type */ +#define RW007_EVENT_CONN_OK (1L << 0) +#define RW007_EVENT_SEND_OK (1L << 1) +#define RW007_EVENT_RECV_OK (1L << 2) +#define RW007_EVNET_CLOSE_OK (1L << 3) +#define RW007_EVENT_CONN_FAIL (1L << 4) +#define RW007_EVENT_SEND_FAIL (1L << 5) + +static at_evt_cb_t at_evt_cb_set[] = { + [AT_SOCKET_EVT_RECV] = NULL, + [AT_SOCKET_EVT_CLOSED] = NULL, +}; + +static int rw007_socket_event_send(struct at_device *device, uint32_t event) +{ + return (int) rt_event_send(device->socket_event, event); +} + +static int rw007_socket_event_recv(struct at_device *device, uint32_t event, uint32_t timeout, rt_uint8_t option) +{ + int result = RT_EOK; + rt_uint32_t recved; + + result = rt_event_recv(device->socket_event, event, option | RT_EVENT_FLAG_CLEAR, timeout, &recved); + if (result != RT_EOK) + { + return -RT_ETIMEOUT; + } + + return recved; +} + +/** + * close socket by AT commands. + * + * @param current socket + * + * @return 0: close socket success + * -1: send AT commands error + * -2: wait socket event timeout + * -5: no memory + */ +static int rw007_socket_close(struct at_socket *socket) +{ + int result = RT_EOK; + at_response_t resp = RT_NULL; + int device_socket = (int) socket->user_data; + struct at_device *device = (struct at_device *) socket->device; + + resp = at_create_resp(64, 0, RT_TICK_PER_SECOND); + if (resp == RT_NULL) + { + LOG_E("no memory for rw007 device(%s) response structure.", device->name); + return -RT_ENOMEM; + } + + result = at_obj_exec_cmd(device->client, resp, "AT+CIPCLOSE=%d", device_socket); + + if (resp) + { + at_delete_resp(resp); + } + + return result; +} + +/** + * create TCP/UDP client or server connect by AT commands. + * + * @param socket current socket + * @param ip server or client IP address + * @param port server or client port + * @param type connect socket type(tcp, udp) + * @param is_client connection is client + * + * @return 0: connect success + * -1: connect failed, send commands error or type error + * -2: wait socket event timeout + * -5: no memory + */ +static int rw007_socket_connect(struct at_socket *socket, char *ip, int32_t port, enum at_socket_type type, rt_bool_t is_client) +{ + int result = RT_EOK; + rt_bool_t retryed = RT_FALSE; + at_response_t resp = RT_NULL; + int device_socket = (int) socket->user_data; + struct at_device *device = (struct at_device *) socket->device; + + RT_ASSERT(ip); + RT_ASSERT(port >= 0); + + resp = at_create_resp(128, 0, 5 * RT_TICK_PER_SECOND); + if (resp == RT_NULL) + { + LOG_E("no memory for rw007 device(%s) response structure.", device->name); + return -RT_ENOMEM; + } + +__retry: + if (is_client) + { + switch (type) + { + case AT_SOCKET_TCP: + /* send AT commands to connect TCP server */ + if (at_obj_exec_cmd(device->client, resp, + "AT+CIPSTART=%d,\"TCP\",\"%s\",%d,60", device_socket, ip, port) < 0) + { + result = -RT_ERROR; + } + break; + + case AT_SOCKET_UDP: + if (at_obj_exec_cmd(device->client, resp, + "AT+CIPSTART=%d,\"UDP\",\"%s\",%d", device_socket, ip, port) < 0) + { + result = -RT_ERROR; + } + break; + + default: + LOG_E("rw007 device(%s) not supported connect type %d.", device->name, type); + result = -RT_ERROR; + goto __exit; + } + } + + if (result != RT_EOK && retryed == RT_FALSE) + { + LOG_D("rw007 device(%s) socket (%d) connect failed, maybe the socket was not be closed at the last time and now will retry.", + device->name, socket); + if (rw007_socket_close(socket) < 0) + { + goto __exit; + } + retryed = RT_TRUE; + result = RT_EOK; + goto __retry; + } + +__exit: + if (resp) + { + at_delete_resp(resp); + } + + return result; +} + +/** + * send data to server or client by AT commands. + * + * @param socket current socket + * @param buff send buffer + * @param bfsz send buffer size + * @param type connect socket type(tcp, udp) + * + * @return >=0: the size of send success + * -1: send AT commands error or send data error + * -2: waited socket event timeout + * -5: no memory + */ +static int rw007_socket_send(struct at_socket *socket, const char *buff, size_t bfsz, enum at_socket_type type) +{ + int result = RT_EOK; + int event_result = 0; + size_t cur_pkt_size = 0, sent_size = 0; + at_response_t resp = RT_NULL; + int device_socket = (int) socket->user_data; + struct at_device *device = (struct at_device *) socket->device; + struct at_device_rw007 *rw007 = (struct at_device_rw007 *) device->user_data; + rt_mutex_t lock = device->client->lock; + + RT_ASSERT(buff); + RT_ASSERT(bfsz > 0); + + resp = at_create_resp(128, 2, 5 * RT_TICK_PER_SECOND); + if (resp == RT_NULL) + { + LOG_E("no memory for rw007 device(%s) response structure.", device->name); + return -RT_ENOMEM; + } + + rt_mutex_take(lock, RT_WAITING_FOREVER); + + /* set current socket for send URC event */ + rw007->user_data = (void *) device_socket; + + /* set AT client end sign to deal with '>' sign */ + at_obj_set_end_sign(device->client, '>'); + + while (sent_size < bfsz) + { + if (bfsz - sent_size < RW007_MODULE_SEND_MAX_SIZE) + { + cur_pkt_size = bfsz - sent_size; + } + else + { + cur_pkt_size = RW007_MODULE_SEND_MAX_SIZE; + } + + /* send the "AT+CIPSEND" commands to AT server than receive the '>' response on the first line */ + if (at_obj_exec_cmd(device->client, resp, "AT+CIPSEND=%d,%d", device_socket, cur_pkt_size) < 0) + { + result = -RT_ERROR; + goto __exit; + } + + /* send the real data to server or client */ + result = (int) at_client_obj_send(device->client, buff + sent_size, cur_pkt_size); + if (result == 0) + { + result = -RT_ERROR; + goto __exit; + } + + /* waiting result event from AT URC */ + if (rw007_socket_event_recv(device, SET_EVENT(device_socket, 0), 10 * RT_TICK_PER_SECOND, RT_EVENT_FLAG_OR) < 0) + { + LOG_E("rw007 device(%s) socket (%d) send failed, wait connect result timeout.", device->name, device_socket); + result = -RT_ETIMEOUT; + goto __exit; + } + /* waiting OK or failed result */ + event_result = rw007_socket_event_recv(device, RW007_EVENT_SEND_OK | RW007_EVENT_SEND_FAIL, + 5 * RT_TICK_PER_SECOND, RT_EVENT_FLAG_OR); + if (event_result < 0) + { + LOG_E("rw007 device(%s) socket (%d) send failed, wait connect OK|FAIL timeout.", device->name, device_socket); + result = -RT_ETIMEOUT; + goto __exit; + } + /* check result */ + if (event_result & RW007_EVENT_SEND_FAIL) + { + LOG_E("rw007 device(%s) socket (%d) send failed.", device->name, device_socket); + result = -RT_ERROR; + goto __exit; + } + + sent_size += cur_pkt_size; + } + +__exit: + /* reset the end sign for data */ + at_obj_set_end_sign(device->client, 0); + + rt_mutex_release(lock); + + if (resp) + { + at_delete_resp(resp); + } + + return result; +} + +/** + * domain resolve by AT commands. + * + * @param name domain name + * @param ip parsed IP address, it's length must be 16 + * + * @return 0: domain resolve success + * -2: wait socket event timeout + * -5: no memory + */ +static int rw007_domain_resolve(const char *name, char ip[16]) +{ +#define RESOLVE_RETRY 5 + + int i, result = RT_EOK; + char recv_ip[16] = { 0 }; + at_response_t resp = RT_NULL; + struct at_device *device = RT_NULL; + + RT_ASSERT(name); + RT_ASSERT(ip); + + device = at_device_get_first_initialized(); + if (device == RT_NULL) + { + LOG_E("get first initialization rw007 device failed."); + return -RT_ERROR; + } + + resp = at_create_resp(128, 0, 20 * RT_TICK_PER_SECOND); + if (resp == RT_NULL) + { + LOG_E("no memory for rw007 device response structure.", device->name); + return -RT_ENOMEM; + } + + for (i = 0; i < RESOLVE_RETRY; i++) + { + if (at_obj_exec_cmd(device->client, resp, "AT+CIPDOMAIN=\"%s\"", name) < 0) + { + result = -RT_ERROR; + goto __exit; + } + + /* parse the third line of response data, get the IP address */ + if (at_resp_parse_line_args_by_kw(resp, "+CIPDOMAIN:", "+CIPDOMAIN:%s", recv_ip) < 0) + { + rt_thread_mdelay(100); + /* resolve failed, maybe receive an URC CRLF */ + continue; + } + + if (rt_strlen(recv_ip) < 8) + { + rt_thread_mdelay(100); + /* resolve failed, maybe receive an URC CRLF */ + continue; + } + else + { + rt_strncpy(ip, recv_ip, 15); + ip[15] = '\0'; + break; + } + } + +__exit: + if (resp) + { + at_delete_resp(resp); + } + + return result; + +} + +/** + * set AT socket event notice callback + * + * @param event notice event + * @param cb notice callback + */ +static void rw007_socket_set_event_cb(at_socket_evt_t event, at_evt_cb_t cb) +{ + if (event < sizeof(at_evt_cb_set) / sizeof(at_evt_cb_set[1])) + { + at_evt_cb_set[event] = cb; + } +} + +static const struct at_socket_ops rw007_socket_ops = +{ + rw007_socket_connect, + rw007_socket_close, + rw007_socket_send, + rw007_domain_resolve, + rw007_socket_set_event_cb, +}; + +static void urc_send_func(struct at_client *client, const char *data, rt_size_t size) +{ + int device_socket = 0; + struct at_device *device = RT_NULL; + struct at_device_rw007 *rw007 = RT_NULL; + char *client_name = client->device->parent.name; + + RT_ASSERT(data && size); + + device = at_device_get_by_name(AT_DEVICE_NAMETYPE_CLIENT, client_name); + if (device == RT_NULL) + { + LOG_E("get rw007 device by client name(%s) failed.", client_name); + return; + } + rw007 = (struct at_device_rw007 *) device->user_data; + device_socket = (int) rw007->user_data; + + if (rt_strstr(data, "SEND OK")) + { + rw007_socket_event_send(device, SET_EVENT(device_socket, RW007_EVENT_SEND_OK)); + } + else if (rt_strstr(data, "SEND FAIL")) + { + rw007_socket_event_send(device, SET_EVENT(device_socket, RW007_EVENT_SEND_FAIL)); + } +} + +static void urc_send_bfsz_func(struct at_client *client, const char *data, rt_size_t size) +{ + static int cur_send_bfsz = 0; + + RT_ASSERT(data && size); + + sscanf(data, "Recv %d bytes", &cur_send_bfsz); +} + +static void urc_close_func(struct at_client *client, const char *data, rt_size_t size) +{ + int device_socket = 0; + struct at_socket *socket = RT_NULL; + struct at_device *device = RT_NULL; + char *client_name = client->device->parent.name; + + RT_ASSERT(data && size); + + device = at_device_get_by_name(AT_DEVICE_NAMETYPE_CLIENT, client_name); + if (device == RT_NULL) + { + LOG_E("get rw007 device by client name(%s) failed.", client_name); + return; + } + + sscanf(data, "%d,CLOSED", &device_socket); + /* get at socket object by device socket descriptor */ + socket = &(device->sockets[device_socket]); + + /* notice the socket is disconnect by remote */ + if (at_evt_cb_set[AT_SOCKET_EVT_CLOSED]) + { + at_evt_cb_set[AT_SOCKET_EVT_CLOSED](socket, AT_SOCKET_EVT_CLOSED, RT_NULL, 0); + } +} + +static void urc_recv_func(struct at_client *client, const char *data, rt_size_t size) +{ + int device_socket = 0; + rt_int32_t timeout = 0; + rt_size_t bfsz = 0, temp_size = 0; + char *recv_buf = RT_NULL, temp[8] = {0}; + struct at_socket *socket = RT_NULL; + struct at_device *device = RT_NULL; + char *client_name = client->device->parent.name; + + RT_ASSERT(data && size); + + device = at_device_get_by_name(AT_DEVICE_NAMETYPE_CLIENT, client_name); + if (device == RT_NULL) + { + LOG_E("get rw007 device by client name(%s) failed.", client_name); + return; + } + + /* get the current socket and receive buffer size by receive data */ + sscanf(data, "+IPD,%d,%d:", &device_socket, (int *) &bfsz); + /* get receive timeout by receive buffer length */ + timeout = bfsz; + + if (device_socket < 0 || bfsz == 0) + { + return; + } + + recv_buf = (char *) rt_calloc(1, bfsz); + if (recv_buf == RT_NULL) + { + LOG_E("no memory for rw007 device(%s) URC receive buffer (%d).", device->name, bfsz); + /* read and clean the coming data */ + while (temp_size < bfsz) + { + if (bfsz - temp_size > sizeof(temp)) + { + at_client_obj_recv(client, temp, sizeof(temp), timeout); + } + else + { + at_client_obj_recv(client, temp, bfsz - temp_size, timeout); + } + temp_size += sizeof(temp); + } + return; + } + + /* sync receive data */ + if (at_client_obj_recv(client, recv_buf, bfsz, timeout) != bfsz) + { + LOG_E("rw007 device(%s) receive size(%d) data failed.", device->name, bfsz); + rt_free(recv_buf); + return; + } + + /* get at socket object by device socket descriptor */ + socket = &(device->sockets[device_socket]); + + /* notice the receive buffer and buffer size */ + if (at_evt_cb_set[AT_SOCKET_EVT_RECV]) + { + at_evt_cb_set[AT_SOCKET_EVT_RECV](socket, AT_SOCKET_EVT_RECV, recv_buf, bfsz); + } +} + +static struct at_urc urc_table[] = +{ + {"SEND OK", "\r\n", urc_send_func}, + {"SEND FAIL", "\r\n", urc_send_func}, + {"Recv", "bytes\r\n", urc_send_bfsz_func}, + {"", ",CLOSED\r\n", urc_close_func}, + {"+IPD", ":", urc_recv_func}, +}; + +int rw007_socket_init(struct at_device *device) +{ + RT_ASSERT(device); + + /* register URC data execution function */ + at_obj_set_urc_table(device->client, urc_table, sizeof(urc_table) / sizeof(urc_table[0])); + + return RT_EOK; +} + +int rw007_socket_class_register(struct at_device_class *class) +{ + RT_ASSERT(class); + + class->socket_num = AT_DEVICE_RW007_SOCKETS_NUM; + class->socket_ops = &rw007_socket_ops; + + return RT_EOK; +} + +#endif /* AT_DEVICE_USING_RW007 && AT_USING_SOCKET */ diff --git a/class/sim76xx/at_device_sim76xx.c b/class/sim76xx/at_device_sim76xx.c new file mode 100644 index 0000000..664faf0 --- /dev/null +++ b/class/sim76xx/at_device_sim76xx.c @@ -0,0 +1,610 @@ +/* + * File : at_socket_sim76xx.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-12-22 thomasonegd first version + * 2019-03-06 thomasonegd fix udp connection. + * 2019-03-08 thomasonegd add power_on & power_off api + * 2019-05-14 chenyong multi AT socket client support + */ + +#include +#include + +#include + +#if !defined(AT_SW_VERSION_NUM) || AT_SW_VERSION_NUM < 0x10300 +#error "This AT Client version is older, please check and update latest AT Client!" +#endif + +#define LOG_TAG "at.dev" +#include + +#ifdef AT_DEVICE_USING_SIM76XX + +#define SIM76XX_WAIT_CONNECT_TIME 5000 +#define SIM76XX_THREAD_STACK_SIZE 1024 +#define SIM76XX_THREAD_PRIORITY (RT_THREAD_PRIORITY_MAX / 2) + +/** + * power up sim76xx modem + */ +static void sim76xx_power_on(struct at_device *device) +{ + struct at_device_sim76xx *sim76xx = RT_NULL; + + sim76xx = (struct at_device_sim76xx *) device->user_data; + + /* not nead to set pin configuration for m26 device power on */ + if (sim76xx->power_pin == -1 || sim76xx->power_status_pin == -1) + { + return; + } + + if (rt_pin_read(sim76xx->power_status_pin) == PIN_HIGH) + { + return; + } + rt_pin_write(sim76xx->power_pin, PIN_HIGH); + + while (rt_pin_read(sim76xx->power_status_pin) == PIN_LOW) + { + rt_thread_mdelay(10); + } + rt_pin_write(sim76xx->power_pin, PIN_LOW); +} + +static void sim76xx_power_off(struct at_device *device) +{ + struct at_device_sim76xx *sim76xx = RT_NULL; + + sim76xx = (struct at_device_sim76xx *) device->user_data; + + /* not nead to set pin configuration for m26 device power on */ + if (sim76xx->power_pin == -1 || sim76xx->power_status_pin == -1) + { + return; + } + + if (rt_pin_read(sim76xx->power_status_pin) == PIN_LOW) + { + return; + } + rt_pin_write(sim76xx->power_pin, PIN_HIGH); + + while (rt_pin_read(sim76xx->power_status_pin) == PIN_HIGH) + { + rt_thread_mdelay(10); + } + rt_pin_write(sim76xx->power_pin, PIN_LOW); +} + +/* ============================= sim76xx network interface operations ============================= */ + +static struct netdev *sim76xx_netdev_add(const char *netdev_name) +{ +#define ETHERNET_MTU 1500 +#define HWADDR_LEN 8 + struct netdev *netdev = RT_NULL; + + RT_ASSERT(netdev_name); + + netdev = (struct netdev *)rt_calloc(1, sizeof(struct netdev)); + if (netdev == RT_NULL) + { + LOG_E("no memory for sim76xx device(%s) netdev structure.", netdev_name); + return RT_NULL; + } + + netdev->mtu = ETHERNET_MTU; + netdev->hwaddr_len = HWADDR_LEN; + netdev->ops = RT_NULL; + +#ifdef SAL_USING_AT + extern int sal_at_netdev_set_pf_info(struct netdev * netdev); + /* set the network interface socket/netdb operations */ + sal_at_netdev_set_pf_info(netdev); +#endif + + netdev_register(netdev, netdev_name, RT_NULL); + + /*TODO: improve netdev adaptation */ + netdev_low_level_set_status(netdev, RT_TRUE); + netdev_low_level_set_link_status(netdev, RT_TRUE); + netdev_low_level_set_dhcp_status(netdev, RT_TRUE); + netdev->flags |= NETDEV_FLAG_INTERNET_UP; + + return netdev; +} + +/* ============================= sim76xx device operations ============================= */ + +#define AT_SEND_CMD(client, resp, cmd) \ + do { \ + (resp) = at_resp_set_info((resp), 256, 0, 5 * RT_TICK_PER_SECOND); \ + if (at_obj_exec_cmd((client), (resp), (cmd)) < 0) \ + { \ + result = -RT_ERROR; \ + goto __exit; \ + } \ + } while(0) \ + + +static void sim76xx_init_thread_entry(void *parameter) +{ +#define INIT_RETRY 5 +#define CSQ_RETRY 20 +#define CREG_RETRY 10 +#define CGREG_RETRY 20 +#define CGATT_RETRY 10 +#define CCLK_RETRY 10 + + at_response_t resp = RT_NULL; + rt_err_t result = RT_EOK; + rt_size_t i, qi_arg[3] = {0}; + int retry_num = INIT_RETRY; + char parsed_data[20] = {0}; + struct at_device *device = (struct at_device *)parameter; + struct at_client *client = device->client; + + resp = at_create_resp(128, 0, rt_tick_from_millisecond(300)); + if (resp == RT_NULL) + { + LOG_E("no memory for sim76xx device(%s) response structure.", device->name); + return; + } + + LOG_D("start initializing the sim76xx device(%s).", device->name); + + while (retry_num--) + { + /* power-up sim76xx */ + sim76xx_power_on(device); + rt_thread_mdelay(1000); + + /* wait SIM76XX startup finish, Send AT every 5s, if receive OK, SYNC success*/ + if (at_client_wait_connect(SIM76XX_WAIT_CONNECT_TIME)) + { + result = -RT_ETIMEOUT; + goto __exit; + } + + /* disable echo */ + AT_SEND_CMD(client, resp, "ATE0"); + + /* get module version */ + AT_SEND_CMD(client, resp, "ATI"); + + /* show module version */ + for (i = 0; i < (int)resp->line_counts - 1; i++) + { + LOG_D("%s", at_resp_get_line(resp, i + 1)); + } + /* check SIM card */ + + AT_SEND_CMD(client, resp, "AT+CPIN?"); + if (!at_resp_get_line_by_kw(resp, "READY")) + { + LOG_E("sim76xx device(%s) SIM card detection failed.", device->name); + result = -RT_ERROR; + goto __exit; + } + + /* waiting for dirty data to be digested */ + rt_thread_mdelay(10); + /* check signal strength */ + for (i = 0; i < CSQ_RETRY; i++) + { + AT_SEND_CMD(client, resp, "AT+CSQ"); + at_resp_parse_line_args_by_kw(resp, "+CSQ:", "+CSQ: %d,%d", &qi_arg[0], &qi_arg[1]); + if (qi_arg[0] != 99) + { + LOG_D("sim76xx device(%s) signal strength: %d Channel bit error rate: %d", device->name, qi_arg[0], qi_arg[1]); + break; + } + rt_thread_mdelay(1000); + } + + if (i == CSQ_RETRY) + { + LOG_E("sim76xx device(%s) signal strength check failed (%s).", device->name, parsed_data); + result = -RT_ERROR; + goto __exit; + } + + /* do not show the prompt when receiving data */ + AT_SEND_CMD(client, resp, "AT+CIPSRIP=0"); + + /* check the GSM network is registered */ + for (i = 0; i < CREG_RETRY; i++) + { + AT_SEND_CMD(client, resp, "AT+CREG?"); + at_resp_parse_line_args_by_kw(resp, "+CREG:", "+CREG: %s", &parsed_data); + if (!strncmp(parsed_data, "0,1", sizeof(parsed_data)) || !strncmp(parsed_data, "0,5", sizeof(parsed_data))) + { + LOG_D("sim76xx device(%s) GSM network is registered(%s).", device->name, parsed_data); + break; + } + rt_thread_mdelay(1000); + } + if (i == CREG_RETRY) + { + LOG_E("sim76xx device(%s) GSM network is register failed(%s).", device->name, parsed_data); + result = -RT_ERROR; + goto __exit; + } + /* check the GPRS network is registered */ + for (i = 0; i < CGREG_RETRY; i++) + { + AT_SEND_CMD(client, resp, "AT+CGREG?"); + at_resp_parse_line_args_by_kw(resp, "+CGREG:", "+CGREG: %s", &parsed_data); + if (!strncmp(parsed_data, "0,1", sizeof(parsed_data)) || !strncmp(parsed_data, "0,5", sizeof(parsed_data))) + { + LOG_D("sim76xx device(%s) GPRS network is registered(%s).", device->name, parsed_data); + break; + } + rt_thread_mdelay(1000); + } + + if (i == CGREG_RETRY) + { + LOG_E("sim76xx device(%s) GPRS network is register failed(%s).", device->name, parsed_data); + result = -RT_ERROR; + goto __exit; + } + + /* check packet domain attach or detach */ + for (i = 0; i < CGATT_RETRY; i++) + { + AT_SEND_CMD(client, resp, "AT+CGATT?"); + at_resp_parse_line_args_by_kw(resp, "+CGATT:", "+CGATT: %s", &parsed_data); + if (!strncmp(parsed_data, "1", 1)) + { + LOG_I("sim76xx device(%s) Packet domain attach.", device->name); + break; + } + + rt_thread_mdelay(1000); + } + + if (i == CGATT_RETRY) + { + LOG_E("sim76xx device(%s) GPRS network attach failed.", device->name); + result = -RT_ERROR; + goto __exit; + } + + /* get real time */ + int year, month, day, hour, min, sec; + + for (i = 0; i < CCLK_RETRY; i++) + { + if (at_obj_exec_cmd(device->client, at_resp_set_info(resp, 256, 0, 5 * RT_TICK_PER_SECOND), "AT+CCLK?") < 0) + { + rt_thread_mdelay(500); + continue; + } + + /* +CCLK: "18/12/22,18:33:12+32" */ + if (at_resp_parse_line_args_by_kw(resp, "+CCLK:", "+CCLK: \"%d/%d/%d,%d:%d:%d", &year, &month, &day, &hour, &min, &sec) < 0) + { + rt_thread_mdelay(500); + continue; + } + + set_date(year + 2000, month, day); + set_time(hour, min, sec); + + break; + } + + if (i == CCLK_RETRY) + { + LOG_E("sim76xx device(%s) GPRS network attach failed.", device->name); + result = -RT_ERROR; + goto __exit; + } + + /* set active PDP context's profile number */ + AT_SEND_CMD(client, resp, "AT+CSOCKSETPN=1"); + + __exit: + if (result == RT_EOK) + { + break; + } + else + { + /* power off the sim76xx device */ + sim76xx_power_off(device); + rt_thread_mdelay(1000); + + LOG_I("sim76xx device(%s) initialize retry...", device->name); + } + } + + if (resp) + { + at_delete_resp(resp); + } + + if (result == RT_EOK) + { + LOG_I("sim76xx devuce(%s) network initialize success!", device->name); + } + else + { + LOG_E("sim76xx device(%s) network initialize failed(%d)!", device->name, result); + } +} + +int sim76xx_net_init(struct at_device *device) +{ +#ifdef AT_DEVICE_SIM76XX_INIT_ASYN + rt_thread_t tid; + + tid = rt_thread_create("sim76xx_net_init", sim76xx_init_thread_entry, (void *)device, + SIM76XX_THREAD_STACK_SIZE, SIM76XX_THREAD_PRIORITY, 20); + if (tid) + { + rt_thread_startup(tid); + } + else + { + LOG_E("create sim76xx device(%s) initialization thread failed.", device->name); + return -RT_ERROR; + } +#else + sim76xx_init_thread_entry(device); +#endif /* AT_DEVICE_SIM76XX_INIT_ASYN */ + + return RT_EOK; +} + +int sim76xx_ping(int argc, char **argv) +{ + at_response_t resp = RT_NULL; + struct at_device *device = RT_NULL; + + if (argc != 2) + { + rt_kprintf("Please input: at_ping \n"); + return -RT_ERROR; + } + + device = at_device_get_first_initialized(); + if (device == RT_NULL) + { + rt_kprintf("get first initialized sim76xx device failed.\n"); + return -RT_ERROR; + } + + resp = at_create_resp(64, 0, 5 * RT_TICK_PER_SECOND); + if (resp == RT_NULL) + { + rt_kprintf("no memory for sim76xx device(%s) response structure.\n", device->name); + return -RT_ENOMEM; + } + + if (at_obj_exec_cmd(device->client, resp, "AT+CPING=\"%s\",1,4,64,1000,10000,255", argv[1]) < 0) + { + if (resp) + { + at_delete_resp(resp); + } + rt_kprintf("sim76xx device(%s) send ping commands error.\n", device->name); + return -RT_ERROR; + } + + if (resp) + { + at_delete_resp(resp); + } + + return RT_EOK; +} + +int sim76xx_ifconfig(void) +{ + at_response_t resp = RT_NULL; + char resp_arg[AT_CMD_MAX_LEN] = {0}; + rt_err_t result = RT_EOK; + struct at_device *device = RT_NULL; + + device = at_device_get_first_initialized(); + if (device == RT_NULL) + { + rt_kprintf("get first initialized sim76xx device failed.\n"); + return -RT_ERROR; + } + + resp = at_create_resp(128, 2, rt_tick_from_millisecond(300)); + if (resp == RT_NULL) + { + rt_kprintf("no memory for sim76xx device(%s) response structure.\n", device->name); + return -RT_ENOMEM; + } + + /* Show PDP address */ + AT_SEND_CMD(device->client, resp, "AT+CGPADDR"); + at_resp_parse_line_args_by_kw(resp, "+CGPADDR:", "+CGPADDR: %s", &resp_arg); + rt_kprintf("IP adress : %s\n", resp_arg); + +__exit: + if (resp) + { + at_delete_resp(resp); + } + + return result; +} + +#ifdef FINSH_USING_MSH +#include +MSH_CMD_EXPORT_ALIAS(sim76xx_net_init, at_net_init, initialize AT network); +MSH_CMD_EXPORT_ALIAS(sim76xx_ping, at_ping, AT ping network host); +MSH_CMD_EXPORT_ALIAS(sim76xx_ifconfig, at_ifconfig, list the information of network interfaces); +#endif + +static void urc_ping_func(struct at_client *client, const char *data, rt_size_t size) +{ + static int icmp_seq = 0; + int i, j = 0; + int result, recv_len, time, ttl; + int sent, rcvd, lost, min, max, avg; + char dst_ip[16] = {0}; + + RT_ASSERT(data); + + for (i = 0; i < size; i++) + { + if (*(data + i) == '.') + j++; + } + if (j != 0) + { + sscanf(data, "+CPING: %d,%[^,],%d,%d,%d", &result, dst_ip, &recv_len, &time, &ttl); + if (result == 1) + LOG_I("%d bytes from %s icmp_seq=%d ttl=%d time=%d ms", recv_len, dst_ip, icmp_seq++, ttl, time); + } + else + { + sscanf(data, "+CPING: %d,%d,%d,%d,%d,%d,%d", &result, &sent, &rcvd, &lost, &min, &max, &avg); + if (result == 3) + LOG_I("%d sent %d received %d lost, min=%dms max=%dms average=%dms", sent, rcvd, lost, min, max, avg); + if (result == 2) + LOG_I("ping requst timeout"); + } +} + +/* sim76xx device URC table for the device control */ +static struct at_urc urc_table[] = +{ + {"+CPING:", "\r\n", urc_ping_func}, +}; + +static int sim76xx_init(struct at_device *device) +{ + struct at_device_sim76xx *sim76xx = (struct at_device_sim76xx *) device->user_data; + + /* initialize AT client */ + at_client_init(sim76xx->client_name, sim76xx->recv_line_num); + + device->client = at_client_get(sim76xx->client_name); + if (device->client == RT_NULL) + { + LOG_E("sim76xx device(%s) initialize failed, get AT client(%s) failed.", sim76xx->device_name, sim76xx->client_name); + return -RT_ERROR; + } + + /* register URC data execution function */ + at_obj_set_urc_table(device->client, urc_table, sizeof(urc_table) / sizeof(urc_table[0])); + +#ifdef AT_USING_SOCKET + sim76xx_socket_init(device); +#endif + + /* add sim76xx device to the netdev list */ + device->netdev = sim76xx_netdev_add(sim76xx->device_name); + if (device->netdev == RT_NULL) + { + LOG_E("sim76xx device(%s) initialize failed, get network interface device failed.", sim76xx->device_name); + return -RT_ERROR; + } + + /* initialize sim76xx pin configuration */ + if (sim76xx->power_pin != -1 && sim76xx->power_status_pin != -1) + { + rt_pin_mode(sim76xx->power_pin, PIN_MODE_OUTPUT); + rt_pin_mode(sim76xx->power_status_pin, PIN_MODE_INPUT); + } + + /* initialize sim76xx device network */ + sim76xx_net_init(device); + + return RT_EOK; +} + +static int sim76xx_deinit(struct at_device *device) +{ + // TODO netdev operation + device->is_init = RT_FALSE; + return RT_EOK; +} + +static int sim76xx_control(struct at_device *device, int cmd, void *arg) +{ + int result = -RT_ERROR; + + RT_ASSERT(device); + + switch (cmd) + { + case AT_DEVICE_CTRL_POWER_ON: + case AT_DEVICE_CTRL_POWER_OFF: + case AT_DEVICE_CTRL_RESET: + case AT_DEVICE_CTRL_LOW_POWER: + case AT_DEVICE_CTRL_SLEEP: + case AT_DEVICE_CTRL_WAKEUP: + case AT_DEVICE_CTRL_NET_CONN: + case AT_DEVICE_CTRL_NET_DISCONN: + case AT_DEVICE_CTRL_SET_WIFI_INFO: + case AT_DEVICE_CTRL_GET_SIGNAL: + case AT_DEVICE_CTRL_GET_GPS: + case AT_DEVICE_CTRL_GET_VER: + LOG_W("sim76xx not support the control command(%d).", cmd); + break; + default: + LOG_E("input error control command(%d).", cmd); + break; + } + + return result; +} + +const struct at_device_ops sim76xx_device_ops = +{ + sim76xx_init, + sim76xx_deinit, + sim76xx_control, +}; + +static int sim76xx_device_class_register(void) +{ + struct at_device_class *class = RT_NULL; + + class = (struct at_device_class *)rt_calloc(1, sizeof(struct at_device_class)); + if (class == RT_NULL) + { + LOG_E("no memory for sim76xx device class create."); + return -RT_ENOMEM; + } + + /* fill sim76xx device class object */ +#ifdef AT_USING_SOCKET + sim76xx_socket_class_register(class); +#endif + class->device_ops = &sim76xx_device_ops; + + return at_device_class_register(class, AT_DEVICE_CLASS_SIM76XX); +} +INIT_DEVICE_EXPORT(sim76xx_device_class_register); + +#endif /* AT_DEVICE_SIM76XX */ diff --git a/class/sim76xx/at_device_sim76xx.h b/class/sim76xx/at_device_sim76xx.h new file mode 100644 index 0000000..14e2537 --- /dev/null +++ b/class/sim76xx/at_device_sim76xx.h @@ -0,0 +1,66 @@ +/* + * File : at_device_sim76xx.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 + * 2019-05-16 chenyong first version + */ + +#ifndef __AT_DEVICE_SIM76XX_H__ +#define __AT_DEVICE_SIM76XX_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include + +/* The maximum number of sockets supported by the sim76xx device */ +#define AT_DEVICE_SIM76XX_SOCKETS_NUM 10 + +struct at_device_sim76xx +{ + char *device_name; + char *client_name; + + int power_pin; + int power_status_pin; + size_t recv_line_num; + struct at_device device; + + void *user_data; +}; + +#ifdef AT_USING_SOCKET + +/* sim76xx device socket initialize */ +int sim76xx_socket_init(struct at_device *device); + +/* sim76xx device class socket register */ +int sim76xx_socket_class_register(struct at_device_class *class); + +#endif /* AT_USING_SOCKET */ + +#ifdef __cplusplus +} +#endif + +#endif /* __AT_DEVICE_SIM76XX_H__ */ diff --git a/class/sim76xx/at_socket_sim76xx.c b/class/sim76xx/at_socket_sim76xx.c new file mode 100644 index 0000000..cd37c97 --- /dev/null +++ b/class/sim76xx/at_socket_sim76xx.c @@ -0,0 +1,882 @@ +/* + * File : at_socket_sim76xx.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-12-22 thomasonegd first version + * 2019-03-06 thomasonegd fix udp connection. + * 2019-03-08 thomasonegd add power_on & power_off api + * 2019-05-14 chenyong multi AT socket client support + */ + +#include +#include + +#include + +#if !defined(AT_SW_VERSION_NUM) || AT_SW_VERSION_NUM < 0x10300 +#error "This AT Client version is older, please check and update latest AT Client!" +#endif + +#define LOG_TAG "at.skt" +#include + +#ifdef AT_DEVICE_USING_SIM76XX + +#define SIM76XX_MODULE_SEND_MAX_SIZE 1500 +#define SIM76XX_MAX_CONNECTIONS 10 + +/* set real event by current socket and current state */ +#define SET_EVENT(socket, event) (((socket + 1) << 16) | (event)) + +/* AT socket event type */ +#define SIM76XX_EVENT_CONN_OK (1L << 0) +#define SIM76XX_EVENT_SEND_OK (1L << 1) +#define SIM76XX_EVENT_RECV_OK (1L << 2) +#define SIM76XX_EVNET_CLOSE_OK (1L << 3) +#define SIM76XX_EVENT_CONN_FAIL (1L << 4) +#define SIM76XX_EVENT_SEND_FAIL (1L << 5) + +static at_evt_cb_t at_evt_cb_set[] = +{ + [AT_SOCKET_EVT_RECV] = NULL, + [AT_SOCKET_EVT_CLOSED] = NULL, +}; + +static char udp_ipstr[SIM76XX_MAX_CONNECTIONS][16]; +static int udp_port[SIM76XX_MAX_CONNECTIONS]; + +/* unsolicited TCP/IP command codes */ +static void at_tcp_ip_errcode_parse(int result) +{ + switch(result) + { + case 0 : LOG_D("%d : operation succeeded ", result); break; + case 1 : LOG_E("%d : UNetwork failure", result); break; + case 2 : LOG_E("%d : Network not opened", result); break; + case 3 : LOG_E("%d : Wrong parameter", result); break; + case 4 : LOG_E("%d : Operation not supported", result); break; + case 5 : LOG_E("%d : Failed to create socket", result); break; + case 6 : LOG_E("%d : Failed to bind socket", result); break; + case 7 : LOG_E("%d : TCP server is already listening", result); break; + case 8 : LOG_E("%d : Busy", result); break; + case 9 : LOG_E("%d : Sockets opened", result); break; + case 10 : LOG_E("%d : Timeout ", result); break; + case 11 : LOG_E("%d : DNS parse failed for AT+CIPOPEN", result); break; + case 255 : LOG_E("%d : Unknown error", result); break; + } +} + +static int sim76xx_socket_event_send(struct at_device *device, uint32_t event) +{ + return (int)rt_event_send(device->socket_event, event); +} + +static int sim76xx_socket_event_recv(struct at_device *device, uint32_t event, uint32_t timeout, rt_uint8_t option) +{ + int result = RT_EOK; + rt_uint32_t recved; + + result = rt_event_recv(device->socket_event, event, option | RT_EVENT_FLAG_CLEAR, timeout, &recved); + if (result != RT_EOK) + { + return -RT_ETIMEOUT; + } + + return recved; +} + +/** + * close socket by AT commands. + * + * @param current socket + * + * @return 0: close socket success + * -1: send AT commands error + * -2: wait socket event timeout + * -5: no memory + */ +static int sim76xx_socket_close(struct at_socket *socket) +{ + int result = RT_EOK; + int activated = 0; + uint8_t lnk_stat[10] = {0}; + at_response_t resp = RT_NULL; + int device_socket = (int) socket->user_data; + struct at_device *device = (struct at_device *) socket->device; + + resp = at_create_resp(64, 0, RT_TICK_PER_SECOND); + if (resp == RT_NULL) + { + LOG_E("no memory for sim76xx device(%s) response structure.", device->name); + return -RT_ENOMEM; + } + + rt_thread_mdelay(100); + + /* check socket link_state */ + if (at_obj_exec_cmd(device->client, resp, "AT+CIPCLOSE?") < 0) + { + result = -RT_ERROR; + goto __exit; + } + + if (at_resp_parse_line_args_by_kw(resp, "+CIPCLOSE:", "+CIPCLOSE: %d,%d,%d,%d,%d,%d,%d,%d,%d,%d", + &lnk_stat[0], &lnk_stat[1], &lnk_stat[1], &lnk_stat[2], &lnk_stat[3], &lnk_stat[4], + &lnk_stat[5], &lnk_stat[6], &lnk_stat[7], &lnk_stat[8], &lnk_stat[9]) < 0) + { + result = -RT_ERROR; + goto __exit; + } + + if (lnk_stat[device_socket]) + { + /* close tcp or udp socket if connected */ + if (at_obj_exec_cmd(device->client, resp, "AT+CIPCLOSE=%d", device_socket) < 0) + { + result = -RT_ERROR; + goto __exit; + } + } + /* check the network open or not */ + if (at_obj_exec_cmd(device->client, resp, "AT+NETOPEN?") < 0) + { + result = -RT_ERROR; + goto __exit; + } + + if (at_resp_parse_line_args_by_kw(resp, "+NETOPEN:", "+NETOPEN: %d", &activated) < 0) + { + result = -RT_ERROR; + goto __exit; + } + + if (activated) + { + /* if already open,then close it */ + if (at_obj_exec_cmd(device->client, resp, "AT+NETCLOSE") < 0) + { + result = -RT_ERROR; + goto __exit; + } + } + + __exit: + if (resp) + { + at_delete_resp(resp); + } + + return result; +} + +/** + * open packet network + */ +static int sim76xx_network_socket_open(struct at_socket *socket) +{ + int result = RT_EOK, activated = 0; + at_response_t resp = RT_NULL; + struct at_device *device = (struct at_device *) socket->device; + + resp = at_create_resp(128, 0, 5 * RT_TICK_PER_SECOND); + if (resp == RT_NULL) + { + LOG_E("no memory for sim76xx device(%s) response structure.", device->name); + return -RT_ENOMEM; + } + + /* check the network open or not */ + if (at_obj_exec_cmd(device->client, resp, "AT+NETOPEN?") < 0) + { + result = -RT_ERROR; + goto __exit; + } + + if (at_resp_parse_line_args_by_kw(resp, "+NETOPEN:", "+NETOPEN: %d", &activated) < 0) + { + result = -RT_ERROR; + goto __exit; + } + + if (activated) + { + /* network socket is already opened */ + goto __exit; + } + else + { + /* if not opened the open it */ + if (at_obj_exec_cmd(device->client, resp, "AT+NETOPEN") < 0) + { + result = -RT_ERROR; + goto __exit; + } + } + +__exit: + if (resp) + { + at_delete_resp(resp); + } + + return result; +} + +/** + * create TCP/UDP client or server connect by AT commands. + * + * @param socket current socket + * @param ip server or client IP address + * @param port server or client port + * @param type connect socket type(tcp, udp) + * @param is_client connection is client + * + * @return 0: connect success + * -1: connect failed, send commands error or type error + * -2: wait socket event timeout + * -5: no memory + */ +static int sim76xx_socket_connect(struct at_socket *socket, char *ip, int32_t port, enum at_socket_type type, rt_bool_t is_client) +{ + int result = RT_EOK, event_result = 0; + rt_bool_t retryed = RT_FALSE; + at_response_t resp = RT_NULL; + int device_socket = (int) socket->user_data; + struct at_device *device = (struct at_device *) socket->device; + rt_mutex_t lock = device->client->lock; + + RT_ASSERT(ip); + RT_ASSERT(port >= 0); + + resp = at_create_resp(128, 0, 5 * RT_TICK_PER_SECOND); + if (resp == RT_NULL) + { + LOG_E("no memory for sim76xx device(%s) response structure.", device->name); + return -RT_ENOMEM; + } + + rt_mutex_take(lock, RT_WAITING_FOREVER); + +__retry: + if (is_client) + { + /* open network socket first(AT+NETOPEN) */ + sim76xx_network_socket_open(socket); + + switch (type) + { + case AT_SOCKET_TCP: + /* send AT commands to connect TCP server */ + if (at_obj_exec_cmd(device->client, resp, "AT+CIPOPEN=%d,\"TCP\",\"%s\",%d", device_socket, ip, port) < 0) + { + result = -RT_ERROR; + } + break; + + case AT_SOCKET_UDP: + if (at_obj_exec_cmd(device->client, resp, "AT+CIPOPEN=%d,\"UDP\",,,%d", device_socket, port) < 0) + { + result = -RT_ERROR; + } + strcpy(udp_ipstr[device_socket], ip); + udp_port[device_socket] = port; + break; + + default: + LOG_E("Not supported connect type : %d.", type); + result = -RT_ERROR; + goto __exit; + } + } + + /* waiting result event from AT URC, the device default connection timeout is 75 seconds, but it set to 10 seconds is convenient to use.*/ + if (sim76xx_socket_event_recv(device, SET_EVENT(device_socket, 0), 10 * RT_TICK_PER_SECOND, RT_EVENT_FLAG_OR) < 0) + { + LOG_E("sim76xx device(%s) socket(%d) connect failed, wait connect result timeout.", device->name, device_socket); + result = -RT_ETIMEOUT; + goto __exit; + } + /* waiting OK or failed result */ + event_result = sim76xx_socket_event_recv(device, SIM76XX_EVENT_CONN_OK | SIM76XX_EVENT_CONN_FAIL, + 1 * RT_TICK_PER_SECOND, RT_EVENT_FLAG_OR); + if (event_result < 0) + { + LOG_E("sim76xx device(%s) socket(%d) connect failed, wait connect OK|FAIL timeout.", device->name, socket); + result = -RT_ETIMEOUT; + goto __exit; + } + /* check result */ + if (event_result & SIM76XX_EVENT_CONN_FAIL) + { + if (!retryed) + { + LOG_E("socket (%d) connect failed, maybe the socket was not be closed at the last time and now will retry.", socket); + if (sim76xx_socket_close(socket) < 0) + { + result = -RT_ERROR; + goto __exit; + } + retryed = RT_TRUE; + goto __retry; + } + LOG_E("socket (%d) connect failed, failed to establish a connection.", socket); + result = -RT_ERROR; + goto __exit; + } + + if (result != RT_EOK && !retryed) + { + LOG_D("socket (%d) connect failed, maybe the socket was not be closed at the last time and now will retry.", socket); + if (sim76xx_socket_close(socket) < 0) + { + goto __exit; + } + retryed = RT_TRUE; + result = RT_EOK; + goto __retry; + } + +__exit: + rt_mutex_release(lock); + + if (resp) + { + at_delete_resp(resp); + } + + return result; +} + +/** + * send data to server or client by AT commands. + * + * @param socket current socket + * @param buff send buffer + * @param bfsz send buffer size + * @param type connect socket type(tcp, udp) + * + * @return >=0: the size of send success + * -1: send AT commands error or send data error + * -2: waited socket event timeout + * -5: no memory + */ +static int sim76xx_socket_send(struct at_socket *socket, const char *buff, size_t bfsz, enum at_socket_type type) +{ + int result = RT_EOK; + int event_result = 0; + size_t cur_pkt_size = 0, sent_size = 0; + at_response_t resp = RT_NULL; + int device_socket = (int) socket->user_data; + struct at_device *device = (struct at_device *) socket->device; + struct at_device_sim76xx *sim76xx = (struct at_device_sim76xx *) device->user_data; + rt_mutex_t lock = device->client->lock; + + RT_ASSERT(buff); + RT_ASSERT(bfsz > 0); + + resp = at_create_resp(128, 2, 5 * RT_TICK_PER_SECOND); + if (resp == RT_NULL) + { + LOG_E("no memory for sim76xx device(%s) response structure.", device->name); + return -RT_ENOMEM; + } + + rt_mutex_take(lock, RT_WAITING_FOREVER); + + /* set current socket for send URC event */ + sim76xx->user_data = (void *) device_socket; + /* set AT client end sign to deal with '>' sign.*/ + at_obj_set_end_sign(device->client, '>'); + + while (sent_size < bfsz) + { + if (bfsz - sent_size < SIM76XX_MODULE_SEND_MAX_SIZE) + { + cur_pkt_size = bfsz - sent_size; + } + else + { + cur_pkt_size = SIM76XX_MODULE_SEND_MAX_SIZE; + } + + switch (socket->type) + { + case AT_SOCKET_TCP: + /* send the "AT+CIPSEND" commands to AT server than receive the '>' response on the first line. */ + if (at_obj_exec_cmd(device->client, resp, "AT+CIPSEND=%d,%d", device_socket, cur_pkt_size) < 0) + { + result = -RT_ERROR; + goto __exit; + } + break; + case AT_SOCKET_UDP: + /* send the "AT+CIPSEND" commands to AT server than receive the '>' response on the first line. */ + if (at_obj_exec_cmd(device->client, resp, "AT+CIPSEND=%d,%d,\"%s\",%d", + device_socket, cur_pkt_size, udp_ipstr[device_socket], udp_port[device_socket]) < 0) + { + result = -RT_ERROR; + goto __exit; + } + break; + } + + /* send the real data to server or client */ + result = (int) at_client_send(buff + sent_size, cur_pkt_size); + if (result == 0) + { + result = -RT_ERROR; + goto __exit; + } + + /* waiting result event from AT URC */ + if (sim76xx_socket_event_recv(device, SET_EVENT(device_socket, 0), 5 * RT_TICK_PER_SECOND, RT_EVENT_FLAG_OR) < 0) + { + LOG_E("sim76xx device(%s) socket (%d) send failed, wait connect result timeout.", device->name, device_socket); + result = -RT_ETIMEOUT; + goto __exit; + } + /* waiting OK or failed result */ + event_result = sim76xx_socket_event_recv(device, SIM76XX_EVENT_SEND_OK | SIM76XX_EVENT_SEND_FAIL, + 5 * RT_TICK_PER_SECOND, RT_EVENT_FLAG_OR); + if (event_result < 0) + { + LOG_E("sim76xx device(%s) socket(%d) send failed, wait connect OK|FAIL timeout.", device->name, device_socket); + result = -RT_ETIMEOUT; + goto __exit; + } + /* check result */ + if (event_result & SIM76XX_EVENT_SEND_FAIL) + { + LOG_E("sim76xx device(%s) socket(%d) send failed.", device->name, device_socket); + result = -RT_ERROR; + goto __exit; + } + + if (type == AT_SOCKET_TCP) + { + //cur_pkt_size = cur_send_bfsz; + } + + sent_size += cur_pkt_size; + } + +__exit: + /* reset the end sign for data */ + at_obj_set_end_sign(device->client, 0); + + rt_mutex_release(lock); + + if (resp) + { + at_delete_resp(resp); + } + + return result; +} + +/** + * domain resolve by AT commands. + * + * @param name domain name + * @param ip parsed IP address, it's length must be 16 + * + * @return 0: domain resolve success + * -2: wait socket event timeout + * -5: no memory + */ +static int sim76xx_domain_resolve(const char *name, char ip[16]) +{ +#define RESOLVE_RETRY 5 + + int i, result = RT_EOK; + char domain[32] = {0}; + char domain_ip[16] = {0}; + at_response_t resp = RT_NULL; + struct at_device *device = RT_NULL; + + RT_ASSERT(name); + RT_ASSERT(ip); + + device = at_device_get_first_initialized(); + if (device == RT_NULL) + { + LOG_E("get first initialized sim76xx device failed."); + return -RT_ERROR; + } + + resp = at_create_resp(128, 0, 5 * RT_TICK_PER_SECOND); + if (resp == RT_NULL) + { + LOG_E("no memory for sim76xx device(%s) response structure.", device->name); + return -RT_ENOMEM; + } + + for (i = 0; i < RESOLVE_RETRY; i++) + { + if (at_obj_exec_cmd(device->client, resp, "AT+CDNSGIP=\"%s\"", name) < 0) + { + rt_thread_mdelay(200); + /* resolve failed, maybe receive an URC CRLF */ + continue; + } + + /* parse the third line of response data, get the IP address */ + /* +CDNSGIP: 1,"www.baidu.com","14.215.177.39" */ + if (at_resp_parse_line_args_by_kw(resp, "+CDNSGIP:", "+CDNSGIP: 1,%[^,],\"%[^\"]", domain, domain_ip) < 0) + { + rt_thread_mdelay(200); + /* resolve failed, maybe receive an URC CRLF */ + continue; + } + + if (rt_strlen(domain_ip) < 8) + { + rt_thread_mdelay(200); + /* resolve failed, maybe receive an URC CRLF */ + continue; + } + else + { + rt_strncpy(ip, domain_ip, 15); + ip[15] = '\0'; + break; + } + } + + if (i == RESOLVE_RETRY) + { + result = -RT_ERROR; + } + + if (resp) + { + at_delete_resp(resp); + } + + return result; +} + +/** + * set AT socket event notice callback + * + * @param event notice event + * @param cb notice callback + */ +static void sim76xx_socket_set_event_cb(at_socket_evt_t event, at_evt_cb_t cb) +{ + if (event < (sizeof(at_evt_cb_set) / sizeof(at_evt_cb_set[1]))) + { + at_evt_cb_set[event] = cb; + } +} + +static void urc_send_func(struct at_client *client, const char *data, rt_size_t size) +{ + int device_socket = 0, rqst_size, cnf_size; + struct at_device *device = RT_NULL; + char *client_name = client->device->parent.name; + + RT_ASSERT(data && size); + + device = at_device_get_by_name(AT_DEVICE_NAMETYPE_CLIENT, client_name); + if (device == RT_NULL) + { + LOG_E("get sim76xx device by client name(%s) failed.", client_name); + return; + } + + sscanf(data, "+CIPSEND: %d,%d,%d", &device_socket, &rqst_size, &cnf_size); + + //cur_send_bfsz = cnf_size; + + sim76xx_socket_event_send(device, SET_EVENT(device_socket, SIM76XX_EVENT_SEND_OK)); +} + +static void urc_ping_func(struct at_client *client, const char *data, rt_size_t size) +{ + static int icmp_seq = 0; + int i, j = 0; + int result, recv_len, time, ttl; + int sent, rcvd, lost, min, max, avg; + char dst_ip[16] = {0}; + + RT_ASSERT(data); + + for (i = 0; i < size; i++) + { + if (*(data + i) == '.') + j++; + } + if (j != 0) + { + sscanf(data, "+CPING: %d,%[^,],%d,%d,%d", &result, dst_ip, &recv_len, &time, &ttl); + if (result == 1) + LOG_I("%d bytes from %s icmp_seq=%d ttl=%d time=%d ms", recv_len, dst_ip, icmp_seq++, ttl, time); + } + else + { + sscanf(data, "+CPING: %d,%d,%d,%d,%d,%d,%d", &result, &sent, &rcvd, &lost, &min, &max, &avg); + if (result == 3) + LOG_I("%d sent %d received %d lost, min=%dms max=%dms average=%dms", sent, rcvd, lost, min, max, avg); + if (result == 2) + LOG_I("ping requst timeout"); + } +} + +static void urc_connect_func(struct at_client *client, const char *data, rt_size_t size) +{ + int device_socket = 0, result = 0; + struct at_device *device = RT_NULL; + char *client_name = client->device->parent.name; + + RT_ASSERT(data && size); + + device = at_device_get_by_name(AT_DEVICE_NAMETYPE_CLIENT, client_name); + if (device == RT_NULL) + { + LOG_E("get sim76xx device by client name(%s) failed.", client_name); + return; + } + + sscanf(data, "+CIPOPEN: %d,%d", &device_socket, &result); + + if (result == 0) + { + sim76xx_socket_event_send(device, SET_EVENT(device_socket, SIM76XX_EVENT_CONN_OK)); + } + else + { + at_tcp_ip_errcode_parse(result); + sim76xx_socket_event_send(device, SET_EVENT(device_socket, SIM76XX_EVENT_CONN_FAIL)); + } +} + +static void urc_close_func(struct at_client *client, const char *data, rt_size_t size) +{ + int device_socket = 0, reason = 0; + struct at_socket *socket = RT_NULL; + struct at_device *device = RT_NULL; + char *client_name = client->device->parent.name; + + RT_ASSERT(data && size); + + device = at_device_get_by_name(AT_DEVICE_NAMETYPE_CLIENT, client_name); + if (device == RT_NULL) + { + LOG_E("get sim76xx device by client name(%s) failed.", device->name); + return; + } + + sscanf(data, "+IPCLOSE %d,%d", &device_socket, &reason); + + switch (reason) + { + case 0: + LOG_E("socket is closed by local,active"); + break; + case 1: + LOG_E("socket is closed by remote,passive"); + break; + case 2: + LOG_E("socket is closed for sending timeout"); + break; + } + + /* get AT socket object by device socket descriptor */ + socket = &(device->sockets[device_socket]); + + /* notice the socket is disconnect by remote */ + if (at_evt_cb_set[AT_SOCKET_EVT_CLOSED]) + { + at_evt_cb_set[AT_SOCKET_EVT_CLOSED](socket, AT_SOCKET_EVT_CLOSED, RT_NULL, 0); + } +} + +static void urc_recv_func(struct at_client *client, const char *data, rt_size_t size) +{ + rt_size_t bfsz = 0, temp_size = 0; + rt_int32_t timeout; + char *recv_buf = RT_NULL, temp[8] = {0}; + int device_socket = 0; + struct at_socket *socket = RT_NULL; + struct at_device *device = RT_NULL; + struct at_device_sim76xx *sim76xx = RT_NULL; + char *client_name = client->device->parent.name; + + RT_ASSERT(data && size); + + device = at_device_get_by_name(AT_DEVICE_NAMETYPE_CLIENT, client_name); + if (device == RT_NULL) + { + LOG_E("get sim76xx device by client name(%s) failed.", client_name); + return; + } + sim76xx = (struct at_device_sim76xx *) device->user_data; + device_socket = (int) sim76xx->user_data; + + /* get the current socket and receive buffer size by receive data */ + sscanf(data, "+IPD%d:", (int *)&bfsz); + /* get receive timeout by receive buffer length */ + timeout = bfsz * 10; + + if (bfsz == 0) + return; + + recv_buf = (char *) rt_calloc(1, bfsz); + if (recv_buf == RT_NULL) + { + LOG_E("no memory for sim76xx device(%s) URC receive buffer(%d).", device->name, bfsz); + /* read and clean the coming data */ + while (temp_size < bfsz) + { + if (bfsz - temp_size > sizeof(temp)) + + + { + at_client_obj_recv(client, temp, sizeof(temp), timeout); + } + else + { + at_client_obj_recv(client, temp, bfsz - temp_size, timeout); + } + temp_size += sizeof(temp); + } + return; + } + + /* sync receive data */ + if (at_client_obj_recv(client, recv_buf, bfsz, timeout) != bfsz) + { + LOG_E("sim76xx device(%s) receive size(%d) data failed.", device->name, bfsz); + rt_free(recv_buf); + return; + } + + /* get AT socket object by device socket descriptor */ + socket = &(device->sockets[device_socket]); + + /* notice the receive buffer and buffer size */ + if (at_evt_cb_set[AT_SOCKET_EVT_RECV]) + { + at_evt_cb_set[AT_SOCKET_EVT_RECV](socket, AT_SOCKET_EVT_RECV, recv_buf, bfsz); + } +} + +static struct at_urc urc_table[] = +{ + {"+CIPSEND:", "\r\n", urc_send_func}, + {"+CIPOPEN:", "\r\n", urc_connect_func}, + {"+CPING:", "\r\n", urc_ping_func}, + {"+IPCLOSE", "\r\n", urc_close_func}, + {"+IPD", "\r\n", urc_recv_func}, +}; + +int sim76xx_connect(int argc, char **argv) +{ + int32_t port; + + if (argc != 3) + { + rt_kprintf("Please input: at_connect \n"); + return -RT_ERROR; + } + sscanf(argv[2], "%d", &port); + sim76xx_socket_connect(at_get_socket(0), argv[1], port, AT_SOCKET_TCP, 1); + + return RT_EOK; +} + +int sim76xx_close(int argc, char **argv) +{ + if (sim76xx_socket_close(at_get_socket(0)) < 0) + { + rt_kprintf("sim76xx_socket_close fail\n"); + } + else + { + rt_kprintf("sim76xx_socket_closeed\n"); + } + return RT_EOK; +} + +int sim76xx_send(int argc, char **argv) +{ + const char *buff = "1234567890\n"; + if (sim76xx_socket_send(at_get_socket(0), buff, 11, AT_SOCKET_TCP) < 0) + { + rt_kprintf("sim76xx_socket_send fail\n"); + } + + return RT_EOK; +} + +int sim76xx_domain(int argc, char **argv) +{ + char ip[16]; + if (sim76xx_domain_resolve("www.baidu.com", ip) < 0) + { + rt_kprintf("sim76xx_socket_send fail\n"); + } + else + { + rt_kprintf("baidu.com : %s\n", ip); + } + + return RT_EOK; +} + +#ifdef FINSH_USING_MSH +#include +MSH_CMD_EXPORT_ALIAS(sim76xx_connect, at_connect, AT connect network host); +MSH_CMD_EXPORT_ALIAS(sim76xx_close, at_close, AT close a socket); +MSH_CMD_EXPORT_ALIAS(sim76xx_send, at_send, AT send a pack); +MSH_CMD_EXPORT_ALIAS(sim76xx_domain, at_domain, AT domain resolve); +#endif + +static const struct at_socket_ops sim76xx_socket_ops = +{ + sim76xx_socket_connect, + sim76xx_socket_close, + sim76xx_socket_send, + sim76xx_domain_resolve, + sim76xx_socket_set_event_cb, +}; + +int sim76xx_socket_init(struct at_device *device) +{ + RT_ASSERT(device); + + /* register URC data execution function */ + at_obj_set_urc_table(device->client, urc_table, sizeof(urc_table) / sizeof(urc_table[0])); + + return RT_EOK; +} + +int sim76xx_socket_class_register(struct at_device_class *class) +{ + RT_ASSERT(class); + + class->socket_num = AT_DEVICE_SIM76XX_SOCKETS_NUM; + class->socket_ops = &sim76xx_socket_ops; + + return RT_EOK; +} + +#endif /* AT_DEVICE_SIM76XX */ diff --git a/class/sim800c/at_device_sim800c.c b/class/sim800c/at_device_sim800c.c new file mode 100644 index 0000000..e7aba15 --- /dev/null +++ b/class/sim800c/at_device_sim800c.c @@ -0,0 +1,971 @@ +/* + * File : at_socket_sim800c.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-12 malongwei first version + * 2019-05-13 chenyong multi AT socket client support + */ + +#include +#include +#include + +#include + +#if !defined(AT_SW_VERSION_NUM) || AT_SW_VERSION_NUM < 0x10200 +#error "This AT Client version is older, please check and update latest AT Client!" +#endif + +#define LOG_TAG "at.dev" +#include + +#ifdef AT_DEVICE_USING_SIM800C + +#define SIM800C_WAIT_CONNECT_TIME 5000 +#define SIM800C_THREAD_STACK_SIZE 1024 +#define SIM800C_THREAD_PRIORITY (RT_THREAD_PRIORITY_MAX/2) + +/* AT+CSTT command default*/ +static char *CSTT_CHINA_MOBILE = "AT+CSTT=\"CMNET\""; +static char *CSTT_CHINA_UNICOM = "AT+CSTT=\"UNINET\""; +static char *CSTT_CHINA_TELECOM = "AT+CSTT=\"CTNET\""; + +static void sim800c_power_on(struct at_device *device) +{ + struct at_device_sim800c *sim800c = RT_NULL; + + sim800c = (struct at_device_sim800c *) device->user_data; + + /* not nead to set pin configuration for m26 device power on */ + if (sim800c->power_pin == -1 || sim800c->power_status_pin == -1) + { + return; + } + + if (rt_pin_read(sim800c->power_status_pin) == PIN_HIGH) + { + return; + } + rt_pin_write(sim800c->power_pin, PIN_HIGH); + + while (rt_pin_read(sim800c->power_status_pin) == PIN_LOW) + { + rt_thread_mdelay(10); + } + rt_pin_write(sim800c->power_pin, PIN_LOW); +} + +static void sim800c_power_off(struct at_device *device) +{ + struct at_device_sim800c *sim800c = RT_NULL; + + sim800c = (struct at_device_sim800c *) device->user_data; + + /* not nead to set pin configuration for m26 device power on */ + if (sim800c->power_pin == -1 || sim800c->power_status_pin == -1) + { + return; + } + + if (rt_pin_read(sim800c->power_status_pin) == PIN_LOW) + { + return; + } + rt_pin_write(sim800c->power_pin, PIN_HIGH); + + while (rt_pin_read(sim800c->power_status_pin) == PIN_HIGH) + { + rt_thread_mdelay(10); + } + rt_pin_write(sim800c->power_pin, PIN_LOW); +} + +/* ============================= sim76xx network interface operations ============================= */ + +/* set sim800c network interface device status and address information */ +static int sim800c_netdev_set_info(struct netdev *netdev) +{ +#define SIM800C_IEMI_RESP_SIZE 32 +#define SIM800C_IPADDR_RESP_SIZE 32 +#define SIM800C_DNS_RESP_SIZE 96 +#define SIM800C_INFO_RESP_TIMO rt_tick_from_millisecond(300) + + int result = RT_EOK; + ip_addr_t addr; + at_response_t resp = RT_NULL; + struct at_device *device = RT_NULL; + + if (netdev == RT_NULL) + { + LOG_E("input network interface device is NULL."); + return -RT_ERROR; + } + + device = at_device_get_by_name(AT_DEVICE_NAMETYPE_NETDEV, netdev->name); + if (device == RT_NULL) + { + LOG_E("get sim800c device by netdev name(%s) failed.", netdev->name); + return -RT_ERROR; + } + + /* set network interface device status */ + netdev_low_level_set_status(netdev, RT_TRUE); + netdev_low_level_set_link_status(netdev, RT_TRUE); + netdev_low_level_set_dhcp_status(netdev, RT_TRUE); + + resp = at_create_resp(SIM800C_IEMI_RESP_SIZE, 0, SIM800C_INFO_RESP_TIMO); + if (resp == RT_NULL) + { + LOG_E("sim800c device(%s) set IP address failed, no memory for response object.", device->name); + result = -RT_ENOMEM; + goto __exit; + } + + /* set network interface device hardware address(IEMI) */ + { + #define SIM800C_NETDEV_HWADDR_LEN 8 + #define SIM800C_IEMI_LEN 15 + + char iemi[SIM800C_IEMI_LEN] = {0}; + int i = 0, j = 0; + + /* send "AT+GSN" commond to get device IEMI */ + if (at_obj_exec_cmd(device->client, resp, "AT+GSN") < 0) + { + result = -RT_ERROR; + goto __exit; + } + + if (at_resp_parse_line_args(resp, 2, "%s", iemi) <= 0) + { + LOG_E("sim800c device(%s) prase \"AT+GSN\" commands resposne data error.", device->name); + result = -RT_ERROR; + goto __exit; + } + + LOG_D("sim800c device(%s) IEMI number: %s", device->name, iemi); + + netdev->hwaddr_len = SIM800C_NETDEV_HWADDR_LEN; + /* get hardware address by IEMI */ + for (i = 0, j = 0; i < SIM800C_NETDEV_HWADDR_LEN && j < SIM800C_IEMI_LEN; i++, j += 2) + { + if (j != SIM800C_IEMI_LEN - 1) + { + netdev->hwaddr[i] = (iemi[j] - '0') * 10 + (iemi[j + 1] - '0'); + } + else + { + netdev->hwaddr[i] = (iemi[j] - '0'); + } + } + } + + /* set network interface device IP address */ + { + #define IP_ADDR_SIZE_MAX 16 + char ipaddr[IP_ADDR_SIZE_MAX] = {0}; + + at_resp_set_info(resp, SIM800C_IPADDR_RESP_SIZE, 2, SIM800C_INFO_RESP_TIMO); + + /* send "AT+CIFSR" commond to get IP address */ + if (at_obj_exec_cmd(device->client, resp, "AT+CIFSR") < 0) + { + result = -RT_ERROR; + goto __exit; + } + + if (at_resp_parse_line_args_by_kw(resp, ".", "%s", ipaddr) <= 0) + { + LOG_E("sim800c device(%s) prase \"AT+CIFSR\" commands resposne data error!", device->name); + result = -RT_ERROR; + goto __exit; + } + + LOG_D("sim800c device(%s) IP address: %s", device->name, ipaddr); + + /* set network interface address information */ + inet_aton(ipaddr, &addr); + netdev_low_level_set_ipaddr(netdev, &addr); + } + + /* set network interface device dns server */ + { + #define DNS_ADDR_SIZE_MAX 16 + char dns_server1[DNS_ADDR_SIZE_MAX] = {0}, dns_server2[DNS_ADDR_SIZE_MAX] = {0}; + + at_resp_set_info(resp, SIM800C_DNS_RESP_SIZE, 0, SIM800C_INFO_RESP_TIMO); + + /* send "AT+CDNSCFG?" commond to get DNS servers address */ + if (at_obj_exec_cmd(device->client, resp, "AT+CDNSCFG?") < 0) + { + result = -RT_ERROR; + goto __exit; + } + + if (at_resp_parse_line_args_by_kw(resp, "PrimaryDns:", "PrimaryDns:%s", dns_server1) <= 0 || + at_resp_parse_line_args_by_kw(resp, "SecondaryDns:", "SecondaryDns:%s", dns_server2) <= 0) + { + LOG_E("Prase \"AT+CDNSCFG?\" commands resposne data error!"); + result = -RT_ERROR; + goto __exit; + } + + LOG_D("sim800c device(%s) primary DNS server address: %s", device->name, dns_server1); + LOG_D("sim800c device(%s) secondary DNS server address: %s", device->name, dns_server2); + + inet_aton(dns_server1, &addr); + netdev_low_level_set_dns_server(netdev, 0, &addr); + + inet_aton(dns_server2, &addr); + netdev_low_level_set_dns_server(netdev, 1, &addr); + } + +__exit: + if (resp) + { + at_delete_resp(resp); + } + + return result; +} + +static void check_link_status_entry(void *parameter) +{ +#define SIM800C_LINK_STATUS_OK 1 +#define SIM800C_LINK_RESP_SIZE 64 +#define SIM800C_LINK_RESP_TIMO (3 * RT_TICK_PER_SECOND) +#define SIM800C_LINK_DELAY_TIME (30 * RT_TICK_PER_SECOND) + + at_response_t resp = RT_NULL; + int result_code, link_status; + struct at_device *device = RT_NULL; + struct netdev *netdev = (struct netdev *)parameter; + + device = at_device_get_by_name(AT_DEVICE_NAMETYPE_NETDEV, netdev->name); + if (device == RT_NULL) + { + LOG_E("get sim800c device by netdev name(%s) failed.", netdev->name); + return; + } + + resp = at_create_resp(SIM800C_LINK_RESP_SIZE, 0, SIM800C_LINK_RESP_TIMO); + if (resp == RT_NULL) + { + LOG_E("sim800c device(%s) set check link status failed, no memory for response object.", device->name); + return; + } + + while (1) + { + /* send "AT+CGREG?" commond to check netweork interface device link status */ + if (at_obj_exec_cmd(device->client, resp, "AT+CGREG?") < 0) + { + rt_thread_mdelay(SIM800C_LINK_DELAY_TIME); + + continue; + } + + link_status = -1; + at_resp_parse_line_args_by_kw(resp, "+CGREG:", "+CGREG: %d,%d", &result_code, &link_status); + + /* check the network interface device link status */ + if ((SIM800C_LINK_STATUS_OK == link_status) != netdev_is_link_up(netdev)) + { + netdev_low_level_set_link_status(netdev, (SIM800C_LINK_STATUS_OK == link_status)); + } + + rt_thread_mdelay(SIM800C_LINK_DELAY_TIME); + } +} + +static int sim800c_netdev_check_link_status(struct netdev *netdev) +{ +#define SIM800C_LINK_THREAD_TICK 20 +#define SIM800C_LINK_THREAD_STACK_SIZE 512 +#define SIM800C_LINK_THREAD_PRIORITY (RT_THREAD_PRIORITY_MAX - 2) + + rt_thread_t tid; + char tname[RT_NAME_MAX] = {0}; + + if (netdev == RT_NULL) + { + LOG_E("input network interface device is NULL.\n"); + return -RT_ERROR; + } + + rt_snprintf(tname, RT_NAME_MAX, "%s_link", netdev->name); + + tid = rt_thread_create(tname, check_link_status_entry, (void *) netdev, + SIM800C_LINK_THREAD_STACK_SIZE, SIM800C_LINK_THREAD_PRIORITY, SIM800C_LINK_THREAD_TICK); + if (tid) + { + rt_thread_startup(tid); + } + + return RT_EOK; +} + +static int sim800c_net_init(struct at_device *device); + +static int sim800c_netdev_set_up(struct netdev *netdev) +{ + struct at_device *device = RT_NULL; + + device = at_device_get_by_name(AT_DEVICE_NAMETYPE_NETDEV, netdev->name); + if (device == RT_NULL) + { + LOG_E("get sim800c device by netdev name(%s) failed.", netdev->name); + return -RT_ERROR; + } + + if (device->is_init == RT_FALSE) + { + sim800c_net_init(device); + device->is_init = RT_TRUE; + + netdev_low_level_set_status(netdev, RT_TRUE); + LOG_D("the network interface device(%s) set up status.", netdev->name); + } + + return RT_EOK; +} + +static int sim800c_netdev_set_down(struct netdev *netdev) +{ + struct at_device *device = RT_NULL; + + device = at_device_get_by_name(AT_DEVICE_NAMETYPE_NETDEV, netdev->name); + if (device == RT_NULL) + { + LOG_E("get sim800c device by netdev name(%s) failed.", netdev->name); + return -RT_ERROR; + } + + if (device->is_init == RT_TRUE) + { + sim800c_power_off(device); + device->is_init = RT_FALSE; + + netdev_low_level_set_status(netdev, RT_FALSE); + LOG_D("the network interface device(%s) set down status.", netdev->name); + } + + return RT_EOK; +} + +static int sim800c_netdev_set_dns_server(struct netdev *netdev, uint8_t dns_num, ip_addr_t *dns_server) +{ +#define SIM800C_DNS_RESP_LEN 8 +#define SIM800C_DNS_RESP_TIMEO rt_tick_from_millisecond(300) + + int result = RT_EOK; + at_response_t resp = RT_NULL; + struct at_device *device = RT_NULL; + + RT_ASSERT(netdev); + RT_ASSERT(dns_server); + + device = at_device_get_by_name(AT_DEVICE_NAMETYPE_NETDEV, netdev->name); + if (device == RT_NULL) + { + LOG_E("get sim800c device by netdev name(%s) failed.", netdev->name); + return -RT_ERROR; + } + + resp = at_create_resp(SIM800C_DNS_RESP_LEN, 0, SIM800C_DNS_RESP_TIMEO); + if (resp == RT_NULL) + { + LOG_D("sim800c set dns server failed, no memory for response object."); + result = -RT_ENOMEM; + goto __exit; + } + + /* send "AT+CDNSCFG=[,]" commond to set dns servers */ + if (at_obj_exec_cmd(device->client, resp, "AT+CDNSCFG=\"%s\"", inet_ntoa(*dns_server)) < 0) + { + result = -RT_ERROR; + goto __exit; + } + + netdev_low_level_set_dns_server(netdev, dns_num, dns_server); + +__exit: + if (resp) + { + at_delete_resp(resp); + } + + return result; +} + +static int sim800c_ping_domain_resolve(struct at_device *device, const char *name, char ip[16]) +{ + int result = RT_EOK; + char recv_ip[16] = { 0 }; + at_response_t resp = RT_NULL; + + /* The maximum response time is 14 seconds, affected by network status */ + resp = at_create_resp(128, 4, 14 * RT_TICK_PER_SECOND); + if (resp == RT_NULL) + { + LOG_E("no memory for sim800c device(%s) response structure.", device->name); + return -RT_ENOMEM; + } + + if (at_obj_exec_cmd(device->client, resp, "AT+CDNSGIP=\"%s\"", name) < 0) + { + result = -RT_ERROR; + goto __exit; + } + + /* parse the third line of response data, get the IP address */ + if (at_resp_parse_line_args_by_kw(resp, "+CDNSGIP:", "%*[^,],%*[^,],\"%[^\"]", recv_ip) < 0) + { + rt_thread_mdelay(100); + /* resolve failed, maybe receive an URC CRLF */ + } + + if (rt_strlen(recv_ip) < 8) + { + rt_thread_mdelay(100); + /* resolve failed, maybe receive an URC CRLF */ + } + else + { + rt_strncpy(ip, recv_ip, 15); + ip[15] = '\0'; + } + +__exit: + if (resp) + { + at_delete_resp(resp); + } + + return result; +} + +#ifdef NETDEV_USING_PING +static int sim800c_netdev_ping(struct netdev *netdev, const char *host, + size_t data_len, uint32_t timeout, struct netdev_ping_resp *ping_resp) +{ +#define SIM800C_PING_RESP_SIZE 128 +#define SIM800C_PING_IP_SIZE 16 +#define SIM800C_PING_TIMEO (5 * RT_TICK_PER_SECOND) + +#define SIM800C_PING_ERR_TIME 600 +#define SIM800C_PING_ERR_TTL 255 + + int result = RT_EOK; + int response, time, ttl, i, err_code = 0; + char ip_addr[SIM800C_PING_IP_SIZE] = {0}; + at_response_t resp = RT_NULL; + struct at_device *device = RT_NULL; + + RT_ASSERT(netdev); + RT_ASSERT(host); + RT_ASSERT(ping_resp); + + device = at_device_get_by_name(AT_DEVICE_NAMETYPE_NETDEV, netdev->name); + if (device == RT_NULL) + { + LOG_E("get sim800c device by netdev name(%s) failed.", netdev->name); + return -RT_ERROR; + } + + for (i = 0; i < rt_strlen(host) && !isalpha(host[i]); i++); + + if (i < strlen(host)) + { + /* check domain name is usable */ + if (sim800c_ping_domain_resolve(device, host, ip_addr) < 0) + { + return -RT_ERROR; + } + rt_memset(ip_addr, 0x00, SIM800C_PING_IP_SIZE); + } + + resp = at_create_resp(SIM800C_PING_RESP_SIZE, 0, SIM800C_PING_TIMEO); + if (resp == RT_NULL) + { + LOG_E("sim800c device(%s) set dns server failed, no memory for response object.", device->name); + result = -RT_ERROR; + goto __exit; + } + + /* domain name prase error options */ + if (at_resp_parse_line_args_by_kw(resp, "+CDNSGIP: 0", "+CDNSGIP: 0,%d", &err_code) > 0) + { + /* 3 - network error, 8 - dns common error */ + if (err_code == 3 || err_code == 8) + { + result = -RT_ERROR; + goto __exit; + } + } + + /* send "AT+CIPPING=[,[,[,[,]]]]" commond to send ping request */ + if (at_obj_exec_cmd(device->client, resp, "AT+CIPPING=%s,1,%d,%d,64", + host, data_len, SIM800C_PING_TIMEO / (RT_TICK_PER_SECOND / 10)) < 0) + { + result = -RT_ERROR; + goto __exit; + } + + if (at_resp_parse_line_args_by_kw(resp, "+CIPPING:", "+CIPPING:%d,\"%[^\"]\",%d,%d", + &response, ip_addr, &time, &ttl) <= 0) + { + result = -RT_ERROR; + goto __exit; + } + + /* the ping request timeout expires, the response time settting to 600 and ttl setting to 255 */ + if (time == SIM800C_PING_ERR_TIME && ttl == SIM800C_PING_ERR_TTL) + { + result = -RT_ETIMEOUT; + goto __exit; + } + + inet_aton(ip_addr, &(ping_resp->ip_addr)); + ping_resp->data_len = data_len; + /* reply time, in units of 100 ms */ + ping_resp->ticks = time * 100; + ping_resp->ttl = ttl; + + __exit: + if (resp) + { + at_delete_resp(resp); + } + + return result; +} +#endif /* NETDEV_USING_PING */ + +#ifdef NETDEV_USING_NETSTAT +void sim800c_netdev_netstat(struct netdev *netdev) +{ + // TODO netstat support +} +#endif /* NETDEV_USING_NETSTAT */ + +const struct netdev_ops sim800c_netdev_ops = +{ + sim800c_netdev_set_up, + sim800c_netdev_set_down, + + RT_NULL, /* not support set ip, netmask, gatway address */ + sim800c_netdev_set_dns_server, + RT_NULL, /* not support set DHCP status */ + +#ifdef NETDEV_USING_PING + sim800c_netdev_ping, +#endif +#ifdef NETDEV_USING_NETSTAT + sim800c_netdev_netstat, +#endif +}; + +static struct netdev *sim800c_netdev_add(const char *netdev_name) +{ +#define SIM800C_NETDEV_MTU 1500 + struct netdev *netdev = RT_NULL; + + RT_ASSERT(netdev_name); + + netdev = (struct netdev *) rt_calloc(1, sizeof(struct netdev)); + if (netdev == RT_NULL) + { + LOG_E("no memory for sim800c device(%s) netdev structure.", netdev_name); + return RT_NULL; + } + + netdev->mtu = SIM800C_NETDEV_MTU; + netdev->ops = &sim800c_netdev_ops; + +#ifdef SAL_USING_AT + extern int sal_at_netdev_set_pf_info(struct netdev *netdev); + /* set the network interface socket/netdb operations */ + sal_at_netdev_set_pf_info(netdev); +#endif + + netdev_register(netdev, netdev_name, RT_NULL); + + return netdev; +} + +/* ============================= sim76xx device operations ============================= */ + +#define AT_SEND_CMD(client, resp, resp_line, timeout, cmd) \ + do { \ + (resp) = at_resp_set_info((resp), 128, (resp_line), rt_tick_from_millisecond(timeout)); \ + if (at_obj_exec_cmd((client), (resp), (cmd)) < 0) \ + { \ + result = -RT_ERROR; \ + goto __exit; \ + } \ + } while(0) \ + +/* init for sim800c */ +static void sim800c_init_thread_entry(void *parameter) +{ +#define INIT_RETRY 5 +#define CPIN_RETRY 10 +#define CSQ_RETRY 10 +#define CREG_RETRY 10 +#define CGREG_RETRY 20 + + int i, qimux, retry_num = INIT_RETRY; + char parsed_data[10] = {0}; + rt_err_t result = RT_EOK; + at_response_t resp = RT_NULL; + struct at_device *device = (struct at_device *)parameter; + struct at_client *client = device->client; + + resp = at_create_resp(128, 0, rt_tick_from_millisecond(300)); + if (resp == RT_NULL) + { + LOG_E("no memory for sim800c device(%s) response structure.", device->name); + return; + } + + LOG_D("start initializing the sim800c device(%s)", device->name); + + while (retry_num--) + { + rt_memset(parsed_data, 0, sizeof(parsed_data)); + rt_thread_mdelay(500); + sim800c_power_on(device); + rt_thread_mdelay(1000); + + /* wait sim800c startup finish */ + if (at_client_obj_wait_connect(client, SIM800C_WAIT_CONNECT_TIME)) + { + result = -RT_ETIMEOUT; + goto __exit; + } + + /* disable echo */ + AT_SEND_CMD(client, resp, 0, 300, "ATE0"); + /* get module version */ + AT_SEND_CMD(client, resp, 0, 300, "ATI"); + /* show module version */ + for (i = 0; i < (int)resp->line_counts - 1; i++) + { + LOG_D("%s", at_resp_get_line(resp, i + 1)); + } + /* check SIM card */ + for (i = 0; i < CPIN_RETRY; i++) + { + AT_SEND_CMD(client, resp, 2, 5 * RT_TICK_PER_SECOND, "AT+CPIN?"); + + if (at_resp_get_line_by_kw(resp, "READY")) + { + LOG_D("sim800c device(%s) SIM card detection success.", device->name); + break; + } + rt_thread_mdelay(1000); + } + if (i == CPIN_RETRY) + { + LOG_E("sim800c device(%s) SIM card detection failed.", device->name); + result = -RT_ERROR; + goto __exit; + } + /* waiting for dirty data to be digested */ + rt_thread_mdelay(10); + + /* check the GSM network is registered */ + for (i = 0; i < CREG_RETRY; i++) + { + AT_SEND_CMD(client, resp, 0, 300, "AT+CREG?"); + at_resp_parse_line_args_by_kw(resp, "+CREG:", "+CREG: %s", &parsed_data); + if (!strncmp(parsed_data, "0,1", sizeof(parsed_data)) || + !strncmp(parsed_data, "0,5", sizeof(parsed_data))) + { + LOG_D("sim800c device(%s) GSM network is registered(%s),", device->name, parsed_data); + break; + } + rt_thread_mdelay(1000); + } + if (i == CREG_RETRY) + { + LOG_E("sim800c device(%s) GSM network is register failed(%s).", device->name, parsed_data); + result = -RT_ERROR; + goto __exit; + } + /* check the GPRS network is registered */ + for (i = 0; i < CGREG_RETRY; i++) + { + AT_SEND_CMD(client, resp, 0, 300, "AT+CGREG?"); + at_resp_parse_line_args_by_kw(resp, "+CGREG:", "+CGREG: %s", &parsed_data); + if (!strncmp(parsed_data, "0,1", sizeof(parsed_data)) || + !strncmp(parsed_data, "0,5", sizeof(parsed_data))) + { + LOG_D("sim800c device(%s) GPRS network is registered(%s).", device->name, parsed_data); + break; + } + rt_thread_mdelay(1000); + } + if (i == CGREG_RETRY) + { + LOG_E("sim800c device(%s) GPRS network is register failed(%s).", device->name, parsed_data); + result = -RT_ERROR; + goto __exit; + } + + /* check signal strength */ + for (i = 0; i < CSQ_RETRY; i++) + { + AT_SEND_CMD(client, resp, 0, 300, "AT+CSQ"); + at_resp_parse_line_args_by_kw(resp, "+CSQ:", "+CSQ: %s", &parsed_data); + if (strncmp(parsed_data, "99,99", sizeof(parsed_data))) + { + LOG_D("sim800c device(%s) signal strength: %s", device->name, parsed_data); + break; + } + rt_thread_mdelay(1000); + } + if (i == CSQ_RETRY) + { + LOG_E("sim800c device(%s) signal strength check failed (%s)", device->name, parsed_data); + result = -RT_ERROR; + goto __exit; + } + + /* the device default response timeout is 40 seconds, but it set to 15 seconds is convenient to use. */ + AT_SEND_CMD(client, resp, 2, 20 * 1000, "AT+CIPSHUT"); + + /* Set to multiple connections */ + AT_SEND_CMD(client, resp, 0, 300, "AT+CIPMUX?"); + at_resp_parse_line_args_by_kw(resp, "+CIPMUX:", "+CIPMUX: %d", &qimux); + if (qimux == 0) + { + AT_SEND_CMD(client, resp, 0, 300, "AT+CIPMUX=1"); + } + + AT_SEND_CMD(client, resp, 0, 300, "AT+COPS?"); + at_resp_parse_line_args_by_kw(resp, "+COPS:", "+COPS: %*[^\"]\"%[^\"]", &parsed_data); + if (rt_strcmp(parsed_data, "CHINA MOBILE") == 0) + { + /* "CMCC" */ + LOG_I("sim800c device(%s) network operator: %s", device->name, parsed_data); + AT_SEND_CMD(client, resp, 0, 300, CSTT_CHINA_MOBILE); + } + else if (rt_strcmp(parsed_data, "CHN-UNICOM") == 0) + { + /* "UNICOM" */ + LOG_I("sim800c device(%s) network operator: %s", device->name, parsed_data); + AT_SEND_CMD(client, resp, 0, 300, CSTT_CHINA_UNICOM); + } + else if (rt_strcmp(parsed_data, "CHN-CT") == 0) + { + AT_SEND_CMD(client, resp, 0, 300, CSTT_CHINA_TELECOM); + /* "CT" */ + LOG_I("sim800c device(%s) network operator: %s", device->name, parsed_data); + } + + /* the device default response timeout is 150 seconds, but it set to 20 seconds is convenient to use. */ + AT_SEND_CMD(client, resp, 0, 20 * 1000, "AT+CIICR"); + + AT_SEND_CMD(client, resp, 2, 300, "AT+CIFSR"); + if (at_resp_get_line_by_kw(resp, "ERROR") != RT_NULL) + { + LOG_E("sim800c device(%s) get the local address failed.", device->name); + result = -RT_ERROR; + goto __exit; + } + result = RT_EOK; + + __exit: + if (result == RT_EOK) + { + break; + } + else + { + /* power off the sim800c device */ + sim800c_power_off(device); + rt_thread_mdelay(1000); + + LOG_I("sim800c device(%s) initialize retry...", device->name); + } + } + + if (resp) + { + at_delete_resp(resp); + } + + if (result == RT_EOK) + { + /* set network interface device status and address information */ + sim800c_netdev_set_info(device->netdev); + sim800c_netdev_check_link_status(device->netdev); + + LOG_I("sim800c device(%s) network initialize success!", device->name); + + } + else + { + LOG_E("sim800c device(%s) network initialize failed(%d)!", device->name, result); + } +} + +static int sim800c_net_init(struct at_device *device) +{ +#ifdef AT_DEVICE_SIM800C_INIT_ASYN + rt_thread_t tid; + + tid = rt_thread_create("sim800c_net_init", sim800c_init_thread_entry, (void *)device, + SIM800C_THREAD_STACK_SIZE, SIM800C_THREAD_PRIORITY, 20); + if (tid) + { + rt_thread_startup(tid); + } + else + { + LOG_E("create sim800c device(%s) initialization thread failed.", device->name); + return -RT_ERROR; + } +#else + sim800c_init_thread_entry(device); +#endif /* AT_DEVICE_SIM800C_INIT_ASYN */ + + return RT_EOK; +} + +static void urc_func(struct at_client *client, const char *data, rt_size_t size) +{ + RT_ASSERT(data); + + LOG_I("URC data : %.*s", size, data); +} + +/* sim800c device URC table for the device control */ +static const struct at_urc urc_table[] = +{ + {"RDY", "\r\n", urc_func}, +}; + +static int sim800c_init(struct at_device *device) +{ + struct at_device_sim800c *sim800c = (struct at_device_sim800c *) device->user_data; + + /* initialize AT client */ + at_client_init(sim800c->client_name, sim800c->recv_line_num); + + device->client = at_client_get(sim800c->client_name); + if (device->client == RT_NULL) + { + LOG_E("sim800c device(%s) initialize failed, get AT client(%s) failed.", sim800c->device_name, sim800c->client_name); + return -RT_ERROR; + } + + /* register URC data execution function */ + at_obj_set_urc_table(device->client, urc_table, sizeof(urc_table) / sizeof(urc_table[0])); + +#ifdef AT_USING_SOCKET + sim800c_socket_init(device); +#endif + + /* add sim800c device to the netdev list */ + device->netdev = sim800c_netdev_add(sim800c->device_name); + if (device->netdev == RT_NULL) + { + LOG_E("sim800c device(%s) initialize failed, get network interface device failed.", sim800c->device_name); + return -RT_ERROR; + } + + /* initialize sim800c pin configuration */ + if (sim800c->power_pin != -1 && sim800c->power_status_pin != -1) + { + rt_pin_mode(sim800c->power_pin, PIN_MODE_OUTPUT); + rt_pin_mode(sim800c->power_status_pin, PIN_MODE_INPUT); + } + + /* initialize sim800c device network */ + return sim800c_netdev_set_up(device->netdev); +} + +static int sim800c_deinit(struct at_device *device) +{ + return sim800c_netdev_set_down(device->netdev); +} + +static int sim800c_control(struct at_device *device, int cmd, void *arg) +{ + int result = -RT_ERROR; + + RT_ASSERT(device); + + switch (cmd) + { + case AT_DEVICE_CTRL_POWER_ON: + case AT_DEVICE_CTRL_POWER_OFF: + case AT_DEVICE_CTRL_RESET: + case AT_DEVICE_CTRL_LOW_POWER: + case AT_DEVICE_CTRL_SLEEP: + case AT_DEVICE_CTRL_WAKEUP: + case AT_DEVICE_CTRL_NET_CONN: + case AT_DEVICE_CTRL_NET_DISCONN: + case AT_DEVICE_CTRL_SET_WIFI_INFO: + case AT_DEVICE_CTRL_GET_SIGNAL: + case AT_DEVICE_CTRL_GET_GPS: + case AT_DEVICE_CTRL_GET_VER: + LOG_W("sim800c not support the control command(%d).", cmd); + break; + default: + LOG_E("input error control command(%d).", cmd); + break; + } + + return result; +} + +const struct at_device_ops sim800c_device_ops = +{ + sim800c_init, + sim800c_deinit, + sim800c_control, +}; + +static int sim800c_device_class_register(void) +{ + struct at_device_class *class = RT_NULL; + + class = (struct at_device_class *) rt_calloc(1, sizeof(struct at_device_class)); + if (class == RT_NULL) + { + LOG_E("no memory for sim800c device class create."); + return -RT_ENOMEM; + } + + /* fill sim800c device class object */ +#ifdef AT_USING_SOCKET + sim800c_socket_class_register(class); +#endif + class->device_ops = &sim800c_device_ops; + + return at_device_class_register(class, AT_DEVICE_CLASS_SIM800C); +} +INIT_DEVICE_EXPORT(sim800c_device_class_register); + +#endif /* AT_DEVICE_USING_SIM800C */ diff --git a/class/sim800c/at_device_sim800c.h b/class/sim800c/at_device_sim800c.h new file mode 100644 index 0000000..9399d80 --- /dev/null +++ b/class/sim800c/at_device_sim800c.h @@ -0,0 +1,66 @@ +/* + * File : at_device_sim800c.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 + * 2019-05-16 chenyong first version + */ + +#ifndef __AT_DEVICE_SIM800C_H__ +#define __AT_DEVICE_SIM800C_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include + +/* The maximum number of sockets supported by the sim800c device */ +#define AT_DEVICE_SIM800C_SOCKETS_NUM 6 + +struct at_device_sim800c +{ + char *device_name; + char *client_name; + + int power_pin; + int power_status_pin; + size_t recv_line_num; + struct at_device device; + + void *user_data; +}; + +#ifdef AT_USING_SOCKET + +/* sim800c device socket initialize */ +int sim800c_socket_init(struct at_device *device); + +/* sim800c device class socket register */ +int sim800c_socket_class_register(struct at_device_class *class); + +#endif /* AT_USING_SOCKET */ + +#ifdef __cplusplus +} +#endif + +#endif /* __AT_DEVICE_SIM800C_H__ */ diff --git a/class/sim800c/at_socket_sim800c.c b/class/sim800c/at_socket_sim800c.c new file mode 100644 index 0000000..2213e51 --- /dev/null +++ b/class/sim800c/at_socket_sim800c.c @@ -0,0 +1,658 @@ +/* + * File : at_socket_sim800c.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-12 malongwei first version + * 2019-05-13 chenyong multi AT socket client support + */ + +#include +#include + +#include + +#if !defined(AT_SW_VERSION_NUM) || AT_SW_VERSION_NUM < 0x10200 +#error "This AT Client version is older, please check and update latest AT Client!" +#endif + +#define LOG_TAG "at.skt" +#include + +#if defined(AT_DEVICE_USING_SIM800C) && defined(AT_USING_SOCKET) + +#define SIM800C_MODULE_SEND_MAX_SIZE 1000 + +/* set real event by current socket and current state */ +#define SET_EVENT(socket, event) (((socket + 1) << 16) | (event)) + +/* AT socket event type */ +#define SIM800C_EVENT_CONN_OK (1L << 0) +#define SIM800C_EVENT_SEND_OK (1L << 1) +#define SIM800C_EVENT_RECV_OK (1L << 2) +#define SIM800C_EVNET_CLOSE_OK (1L << 3) +#define SIM800C_EVENT_CONN_FAIL (1L << 4) +#define SIM800C_EVENT_SEND_FAIL (1L << 5) + +static at_evt_cb_t at_evt_cb_set[] = { + [AT_SOCKET_EVT_RECV] = NULL, + [AT_SOCKET_EVT_CLOSED] = NULL, +}; + +static int sim800c_socket_event_send(struct at_device *device, uint32_t event) +{ + return (int) rt_event_send(device->socket_event, event); +} + +static int sim800c_socket_event_recv(struct at_device *device, uint32_t event, uint32_t timeout, rt_uint8_t option) +{ + int result = RT_EOK; + rt_uint32_t recved; + + result = rt_event_recv(device->socket_event, event, option | RT_EVENT_FLAG_CLEAR, timeout, &recved); + if (result != RT_EOK) + { + return -RT_ETIMEOUT; + } + + return recved; +} + +/** + * close socket by AT commands. + * + * @param current socket + * + * @return 0: close socket success + * -1: send AT commands error + * -2: wait socket event timeout + * -5: no memory + */ +static int sim800c_socket_close(struct at_socket *socket) +{ + uint32_t event = 0; + int result = RT_EOK; + at_response_t resp = RT_NULL; + int device_socket = (int) socket->user_data; + struct at_device *device = (struct at_device *) socket->device; + + resp = at_create_resp(64, 0, rt_tick_from_millisecond(300)); + if (resp == RT_NULL) + { + LOG_E("no memory for sim800c device(%s) response structure.", device->name); + return -RT_ENOMEM; + } + + /* clear socket close event */ + event = SET_EVENT(device_socket, SIM800C_EVNET_CLOSE_OK); + sim800c_socket_event_recv(device, event, 0, RT_EVENT_FLAG_OR); + + if (at_obj_exec_cmd(device->client, resp, "AT+CIPCLOSE=%d", device_socket) < 0) + { + result = -RT_ERROR; + goto __exit; + } + + if (sim800c_socket_event_recv(device, event, rt_tick_from_millisecond(300*3), RT_EVENT_FLAG_AND) < 0) + { + LOG_E("sim800c device(%s) socket(%d) close failed, wait close OK timeout.", device->name, device_socket); + result = -RT_ETIMEOUT; + goto __exit; + } + +__exit: + if (resp) + { + at_delete_resp(resp); + } + + return result; +} + + +/** + * create TCP/UDP client or server connect by AT commands. + * + * @param socket current socket + * @param ip server or client IP address + * @param port server or client port + * @param type connect socket type(tcp, udp) + * @param is_client connection is client + * + * @return 0: connect success + * -1: connect failed, send commands error or type error + * -2: wait socket event timeout + * -5: no memory + */ +static int sim800c_socket_connect(struct at_socket *socket, char *ip, int32_t port, enum at_socket_type type, rt_bool_t is_client) +{ + uint32_t event = 0; + rt_bool_t retryed = RT_FALSE; + at_response_t resp = RT_NULL; + int result = RT_EOK, event_result = 0; + int device_socket = (int) socket->user_data; + struct at_device *device = (struct at_device *) socket->device; + + RT_ASSERT(ip); + RT_ASSERT(port >= 0); + + resp = at_create_resp(128, 0, 5 * RT_TICK_PER_SECOND); + if (resp == RT_NULL) + { + LOG_E("no memory for sim800c device(%s) response structure.", device->name); + return -RT_ENOMEM; + } + +__retry: + + /* clear socket connect event */ + event = SET_EVENT(device_socket, SIM800C_EVENT_CONN_OK | SIM800C_EVENT_CONN_FAIL); + sim800c_socket_event_recv(device, event, 0, RT_EVENT_FLAG_OR); + + if (is_client) + { + switch (type) + { + case AT_SOCKET_TCP: + /* send AT commands(eg: AT+QIOPEN=0,"TCP","x.x.x.x", 1234) to connect TCP server */ + if (at_obj_exec_cmd(device->client, RT_NULL, + "AT+CIPSTART=%d,\"TCP\",\"%s\",%d", device_socket, ip, port) < 0) + { + result = -RT_ERROR; + goto __exit; + } + break; + + case AT_SOCKET_UDP: + if (at_obj_exec_cmd(device->client, RT_NULL, + "AT+CIPSTART=%d,\"UDP\",\"%s\",%d", device_socket, ip, port) < 0) + { + result = -RT_ERROR; + goto __exit; + } + break; + + default: + LOG_E("sim800c device(%s) not supported connect type : %d.", device->name, type); + result = -RT_ERROR; + goto __exit; + } + } + + /* waiting result event from AT URC, the device default connection timeout is 75 seconds, but it set to 10 seconds is convenient to use */ + if (sim800c_socket_event_recv(device, SET_EVENT(device_socket, 0), 10 * RT_TICK_PER_SECOND, RT_EVENT_FLAG_OR) < 0) + { + LOG_E("sim800c device(%s) socket(%d) connect failed, wait connect result timeout.",device->name, device_socket); + result = -RT_ETIMEOUT; + goto __exit; + } + /* waiting OK or failed result */ + event_result = sim800c_socket_event_recv(device, + SIM800C_EVENT_CONN_OK | SIM800C_EVENT_CONN_FAIL, 1 * RT_TICK_PER_SECOND, RT_EVENT_FLAG_OR); + if (event_result < 0) + { + LOG_E("sim800c device(%s) socket(%d) connect failed, wait connect OK|FAIL timeout.", device->name, device_socket); + result = -RT_ETIMEOUT; + goto __exit; + } + /* check result */ + if (event_result & SIM800C_EVENT_CONN_FAIL) + { + if (retryed == RT_FALSE) + { + LOG_D("sim800c device(%s) socket(%d) connect failed, maybe the socket was not be closed at the last time and now will retry.", + device->name, device_socket); + if (sim800c_socket_close(socket) < 0) + { + result = -RT_ERROR; + goto __exit; + } + retryed = RT_TRUE; + goto __retry; + } + LOG_E("sim800c device(%s) socket(%d) connect failed.", device->name, device_socket); + result = -RT_ERROR; + goto __exit; + } + +__exit: + if (resp) + { + at_delete_resp(resp); + } + + return result; +} + +/** + * send data to server or client by AT commands. + * + * @param socket current socket + * @param buff send buffer + * @param bfsz send buffer size + * @param type connect socket type(tcp, udp) + * + * @return >=0: the size of send success + * -1: send AT commands error or send data error + * -2: waited socket event timeout + * -5: no memory + */ +static int sim800c_socket_send(struct at_socket *socket, const char *buff, size_t bfsz, enum at_socket_type type) +{ + uint32_t event = 0; + int result = RT_EOK, event_result = 0; + size_t cur_pkt_size = 0, sent_size = 0; + at_response_t resp = RT_NULL; + int device_socket = (int) socket->user_data; + struct at_device *device = (struct at_device *) socket->device; + rt_mutex_t lock = device->client->lock; + + RT_ASSERT(buff); + + resp = at_create_resp(128, 2, 5 * RT_TICK_PER_SECOND); + if (resp == RT_NULL) + { + LOG_E("no memory for sim800c device(%s) response structure.", device->name); + return -RT_ENOMEM; + } + + rt_mutex_take(lock, RT_WAITING_FOREVER); + + /* clear socket connect event */ + event = SET_EVENT(device_socket, SIM800C_EVENT_SEND_OK | SIM800C_EVENT_SEND_FAIL); + sim800c_socket_event_recv(device, event, 0, RT_EVENT_FLAG_OR); + + /* set AT client end sign to deal with '>' sign.*/ + at_obj_set_end_sign(device->client, '>'); + + while (sent_size < bfsz) + { + if (bfsz - sent_size < SIM800C_MODULE_SEND_MAX_SIZE) + { + cur_pkt_size = bfsz - sent_size; + } + else + { + cur_pkt_size = SIM800C_MODULE_SEND_MAX_SIZE; + } + + /* send the "AT+QISEND" commands to AT server than receive the '>' response on the first line. */ + if (at_obj_exec_cmd(device->client, resp, "AT+CIPSEND=%d,%d", device_socket, cur_pkt_size) < 0) + { + result = -RT_ERROR; + goto __exit; + } + + /* send the real data to server or client */ + result = (int) at_client_obj_send(device->client, buff + sent_size, cur_pkt_size); + if (result == 0) + { + result = -RT_ERROR; + goto __exit; + } + + /* waiting result event from AT URC */ + if (sim800c_socket_event_recv(device, SET_EVENT(device_socket, 0), 15 * RT_TICK_PER_SECOND, RT_EVENT_FLAG_OR) < 0) + { + LOG_E("sim800c device(%s) socket(%d) send failed, wait connect result timeout.", device->name, device_socket); + result = -RT_ETIMEOUT; + goto __exit; + } + /* waiting OK or failed result */ + event_result = sim800c_socket_event_recv(device, + SIM800C_EVENT_SEND_OK | SIM800C_EVENT_SEND_FAIL, 5 * RT_TICK_PER_SECOND, RT_EVENT_FLAG_OR); + if (event_result < 0) + { + LOG_E("simm800c device(%s) socket(%d) send failed, wait connect OK|FAIL timeout.", device->name, device_socket); + result = -RT_ETIMEOUT; + goto __exit; + } + /* check result */ + if (event_result & SIM800C_EVENT_SEND_FAIL) + { + LOG_E("simm800c device(%s) socket(%d) send failed.",device->name, device_socket); + result = -RT_ERROR; + goto __exit; + } + + sent_size += cur_pkt_size; + } + +__exit: + /* reset the end sign for data conflict */ + at_obj_set_end_sign(device->client, 0); + + rt_mutex_release(lock); + + if (resp) + { + at_delete_resp(resp); + } + + return result; +} + +/** + * domain resolve by AT commands. + * + * @param name domain name + * @param ip parsed IP address, it's length must be 16 + * + * @return 0: domain resolve success + * -1: send AT commands error or response error + * -2: wait socket event timeout + * -5: no memory + */ +static int sim800c_domain_resolve(const char *name, char ip[16]) +{ +#define RESOLVE_RETRY 5 + + int i, result = RT_EOK; + char recv_ip[16] = { 0 }; + at_response_t resp = RT_NULL; + struct at_device *device = RT_NULL; + + RT_ASSERT(name); + RT_ASSERT(ip); + + device = at_device_get_first_initialized(); + if (device == RT_NULL) + { + LOG_E("get first initialization sim800c device failed."); + return -RT_ERROR; + } + + /* The maximum response time is 14 seconds, affected by network status */ + resp = at_create_resp(128, 4, 14 * RT_TICK_PER_SECOND); + if (resp == RT_NULL) + { + LOG_E("no memory for sim800c device(%s) response structure.", device->name); + return -RT_ENOMEM; + } + + for (i = 0; i < RESOLVE_RETRY; i++) + { + int err_code = 0; + + if (at_obj_exec_cmd(device->client, resp, "AT+CDNSGIP=\"%s\"", name) < 0) + { + result = -RT_ERROR; + goto __exit; + } + + /* domain name prase error options */ + if (at_resp_parse_line_args_by_kw(resp, "+CDNSGIP: 0", "+CDNSGIP: 0,%d", &err_code) > 0) + { + /* 3 - network error, 8 - dns common error */ + if (err_code == 3 || err_code == 8) + { + result = -RT_ERROR; + goto __exit; + } + } + + /* parse the third line of response data, get the IP address */ + if (at_resp_parse_line_args_by_kw(resp, "+CDNSGIP:", "%*[^,],%*[^,],\"%[^\"]", recv_ip) < 0) + { + rt_thread_mdelay(100); + /* resolve failed, maybe receive an URC CRLF */ + continue; + } + + if (rt_strlen(recv_ip) < 8) + { + rt_thread_mdelay(100); + /* resolve failed, maybe receive an URC CRLF */ + continue; + } + else + { + rt_thread_mdelay(10); + rt_strncpy(ip, recv_ip, 15); + ip[15] = '\0'; + break; + } + } + +__exit: + if (resp) + { + at_delete_resp(resp); + } + + return result; + +} + +/** + * set AT socket event notice callback + * + * @param event notice event + * @param cb notice callback + */ +static void sim800c_socket_set_event_cb(at_socket_evt_t event, at_evt_cb_t cb) +{ + if (event < sizeof(at_evt_cb_set) / sizeof(at_evt_cb_set[1])) + { + at_evt_cb_set[event] = cb; + } +} + +static void urc_connect_func(struct at_client *client, const char *data, rt_size_t size) +{ + int device_socket = 0; + struct at_device *device = RT_NULL; + char *client_name = client->device->parent.name; + + RT_ASSERT(data && size); + + device = at_device_get_by_name(AT_DEVICE_NAMETYPE_CLIENT, client_name); + if (device == RT_NULL) + { + LOG_E("get sim800c device by client name(%s) failed.", client_name); + return; + } + + /* get the current socket by receive data */ + sscanf(data, "%d,%*s", &device_socket); + + if (strstr(data, "CONNECT OK")) + { + sim800c_socket_event_send(device, SET_EVENT(device_socket, SIM800C_EVENT_CONN_OK)); + } + else if (strstr(data, "CONNECT FAIL")) + { + sim800c_socket_event_send(device, SET_EVENT(device_socket, SIM800C_EVENT_CONN_FAIL)); + } +} + +static void urc_send_func(struct at_client *client, const char *data, rt_size_t size) +{ + int device_socket = 0; + struct at_device *device = RT_NULL; + char *client_name = client->device->parent.name; + + RT_ASSERT(data && size); + + device = at_device_get_by_name(AT_DEVICE_NAMETYPE_CLIENT, client_name); + if (device == RT_NULL) + { + LOG_E("get sim800c device by client name(%s) failed.", client_name); + return; + } + + /* get the current socket by receive data */ + sscanf(data, "%d,%*s", &device_socket); + + if (rt_strstr(data, "SEND OK")) + { + sim800c_socket_event_send(device, SET_EVENT(device_socket, SIM800C_EVENT_SEND_OK)); + } + else if (rt_strstr(data, "SEND FAIL")) + { + sim800c_socket_event_send(device, SET_EVENT(device_socket, SIM800C_EVENT_SEND_FAIL)); + } +} + +static void urc_close_func(struct at_client *client, const char *data, rt_size_t size) +{ + int device_socket = 0; + struct at_device *device = RT_NULL; + char *client_name = client->device->parent.name; + + RT_ASSERT(data && size); + + device = at_device_get_by_name(AT_DEVICE_NAMETYPE_CLIENT, client_name); + if (device == RT_NULL) + { + LOG_E("get sim800c device by client name(%s) failed.", client_name); + return; + } + + /* get the current socket by receive data */ + sscanf(data, "%d,%*s", &device_socket); + + if (rt_strstr(data, "CLOSE OK")) + { + sim800c_socket_event_send(device, SET_EVENT(device_socket, SIM800C_EVNET_CLOSE_OK)); + } + else if (rt_strstr(data, "CLOSED")) + { + struct at_socket *socket = RT_NULL; + + /* get AT socket object by device socket descriptor */ + socket = &(device->sockets[device_socket]); + + /* notice the socket is disconnect by remote */ + if (at_evt_cb_set[AT_SOCKET_EVT_CLOSED]) + { + at_evt_cb_set[AT_SOCKET_EVT_CLOSED](socket, AT_SOCKET_EVT_CLOSED, RT_NULL, 0); + } + } +} + +static void urc_recv_func(struct at_client *client, const char *data, rt_size_t size) +{ + int device_socket = 0; + rt_int32_t timeout; + rt_size_t bfsz = 0, temp_size = 0; + char *recv_buf = RT_NULL, temp[8] = {0}; + struct at_socket *socket = RT_NULL; + struct at_device *device = RT_NULL; + char *client_name = client->device->parent.name; + + RT_ASSERT(data && size); + + /* get the current socket and receive buffer size by receive data */ + sscanf(data, "+RECEIVE,%d,%d:", &device_socket, (int *) &bfsz); + /* get receive timeout by receive buffer length */ + timeout = bfsz; + + if (device_socket < 0 || bfsz == 0) + { + return; + } + + device = at_device_get_by_name(AT_DEVICE_NAMETYPE_CLIENT, client_name); + if (device == RT_NULL) + { + LOG_E("get m26 device by client name(%s) failed.", client_name); + return; + } + + recv_buf = (char *) rt_calloc(1, bfsz); + if (recv_buf == RT_NULL) + { + LOG_E("no memory for sim800c device(%s) URC receive buffer (%d).", device->name, bfsz); + /* read and clean the coming data */ + while (temp_size < bfsz) + { + if (bfsz - temp_size > sizeof(temp)) + { + at_client_obj_recv(client, temp, sizeof(temp), timeout); + } + else + { + at_client_obj_recv(client, temp, bfsz - temp_size, timeout); + } + temp_size += sizeof(temp); + } + return; + } + + /* sync receive data */ + if (at_client_obj_recv(client, recv_buf, bfsz, timeout) != bfsz) + { + LOG_E("sim800c device(%s) receive size(%d) data failed.", device->name, bfsz); + rt_free(recv_buf); + return; + } + + /* get AT socket object by device socket descriptor */ + socket = &(device->sockets[device_socket]); + + /* notice the receive buffer and buffer size */ + if (at_evt_cb_set[AT_SOCKET_EVT_RECV]) + { + at_evt_cb_set[AT_SOCKET_EVT_RECV](socket, AT_SOCKET_EVT_RECV, recv_buf, bfsz); + } +} + +/* sim800c device URC table for the socket data */ +static const struct at_urc urc_table[] = +{ + {"", ", CONNECT OK\r\n", urc_connect_func}, + {"", ", CONNECT FAIL\r\n", urc_connect_func}, + {"", ", SEND OK\r\n", urc_send_func}, + {"", ", SEND FAIL\r\n", urc_send_func}, + {"", ", CLOSE OK\r\n", urc_close_func}, + {"", ", CLOSED\r\n", urc_close_func}, + {"+RECEIVE,", "\r\n", urc_recv_func}, +}; + +static const struct at_socket_ops sim800c_socket_ops = +{ + sim800c_socket_connect, + sim800c_socket_close, + sim800c_socket_send, + sim800c_domain_resolve, + sim800c_socket_set_event_cb, +}; + +int sim800c_socket_init(struct at_device *device) +{ + RT_ASSERT(device); + + /* register URC data execution function */ + at_obj_set_urc_table(device->client, urc_table, sizeof(urc_table) / sizeof(urc_table[0])); + + return RT_EOK; +} + +int sim800c_socket_class_register(struct at_device_class *class) +{ + RT_ASSERT(class); + + class->socket_num = AT_DEVICE_SIM800C_SOCKETS_NUM; + class->socket_ops = &sim800c_socket_ops; + + return RT_EOK; +} + +#endif /* AT_DEVICE_USING_SIM800C && AT_USING_SOCKET */ diff --git a/inc/at_device.h b/inc/at_device.h new file mode 100644 index 0000000..054a55e --- /dev/null +++ b/inc/at_device.h @@ -0,0 +1,135 @@ +/* + * File : at_device.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 + * 2019-05-08 chenyong first version + */ + +#ifndef __AT_DEVICE_H__ +#define __AT_DEVICE_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#if !defined(RT_USING_NETDEV) +#error "This RT-Thread version is older, please check and updata laster RT-Thread!" +#else +#include +#include +#endif /* RT_USING_NETDEV */ + +#define AT_DEVICE_SW_VERSION "2.0.0" +#define AT_DEVICE_SW_VERSION_NUM 0x20000 + +/* AT device class ID */ +#define AT_DEVICE_CLASS_ESP8266 0x01U +#define AT_DEVICE_CLASS_M26_MC20 0x02U +#define AT_DEVICE_CLASS_EC20 0x03U +#define AT_DEVICE_CLASS_SIM800C 0x04U +#define AT_DEVICE_CLASS_SIM76XX 0x05U +#define AT_DEVICE_CLASS_RW007 0x06U + +/* Options and Commands for AT device control opreations */ +#define AT_DEVICE_CTRL_POWER_ON 0x01L +#define AT_DEVICE_CTRL_POWER_OFF 0x02L +#define AT_DEVICE_CTRL_RESET 0x03L +#define AT_DEVICE_CTRL_LOW_POWER 0x04L +#define AT_DEVICE_CTRL_SLEEP 0x05L +#define AT_DEVICE_CTRL_WAKEUP 0x06L +#define AT_DEVICE_CTRL_NET_CONN 0x07L +#define AT_DEVICE_CTRL_NET_DISCONN 0x08L +#define AT_DEVICE_CTRL_SET_WIFI_INFO 0x09L +#define AT_DEVICE_CTRL_GET_SIGNAL 0x0AL +#define AT_DEVICE_CTRL_GET_GPS 0x0BL +#define AT_DEVICE_CTRL_GET_VER 0x0CL + +/* Name type */ +#define AT_DEVICE_NAMETYPE_DEVICE 0x01 +#define AT_DEVICE_NAMETYPE_NETDEV 0x02 +#define AT_DEVICE_NAMETYPE_CLIENT 0x03 + +struct at_device; + +/* AT device wifi ssid and password information */ +struct at_device_ssid_pwd +{ + char *ssid; + char *password; +}; + +/* AT device operations */ +struct at_device_ops +{ + int (*init)(struct at_device *device); + int (*deinit)(struct at_device *device); + int (*control)(struct at_device *device, int cmd, void *arg); +}; + +struct at_device_class +{ + uint16_t class_id; /* AT device class ID */ + const struct at_device_ops *device_ops; /* AT device operaiotns */ +#ifdef AT_USING_SOCKET + uint32_t socket_num; /* The maximum number of sockets support */ + const struct at_socket_ops *socket_ops; /* AT device socket operations */ +#endif + rt_slist_t list; /* AT device class list */ +}; + +struct at_device +{ + char name[RT_NAME_MAX]; /* AT device name */ + rt_bool_t is_init; /* AT device initialization completed */ + struct at_device_class *class; /* AT device class object */ + struct at_client *client; /* AT Client object for AT device */ + struct netdev *netdev; /* Network interface device for AT device */ +#ifdef AT_USING_SOCKET + rt_event_t socket_event; /* AT device socket event */ + struct at_socket *sockets; /* AT device sockets list */ +#endif + rt_slist_t list; /* AT device list */ + + void *user_data; /* User-specific data */ +}; + +/* Get AT device object */ +struct at_device *at_device_get_first_initialized(void); +struct at_device *at_device_get_by_name(int type, const char *name); +#ifdef AT_USING_SOCKET +struct at_device *at_device_get_by_socket(int at_socket); +#endif + +/* AT device control operaions */ +int at_device_control(struct at_device *device, int cmd, void *arg); +/* Register AT device class object */ +int at_device_class_register(struct at_device_class *class, uint16_t class_id); +/* Register AT device object */ +int at_device_register(struct at_device *device, const char *device_name, + const char *at_client_name, uint16_t class_id, void *user_data); + +#ifdef __cplusplus +} +#endif + +#endif /* __AT_DEVICE_H__ */ diff --git a/at_client_sample.c b/samples/at_sample_client.c similarity index 99% rename from at_client_sample.c rename to samples/at_sample_client.c index 655e186..e94115b 100644 --- a/at_client_sample.c +++ b/samples/at_sample_client.c @@ -1,5 +1,5 @@ /* - * File : at_client_sample.c + * File : at_sample_client.c * This file is part of RT-Thread RTOS * COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team * diff --git a/samples/at_sample_ec20.c b/samples/at_sample_ec20.c new file mode 100644 index 0000000..20208c8 --- /dev/null +++ b/samples/at_sample_ec20.c @@ -0,0 +1,52 @@ +/* + * File : at_sample_ec20.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 + * 2019-05-13 chenyong first version + */ + +#include + +#define LOG_TAG "at.sample" +#include + +#define EC20_SAMPLE_DEIVCE_NAME "e0" + +static struct at_device_ec20 e0 = +{ + EC20_SAMPLE_DEIVCE_NAME, + EC20_SAMPLE_CLIENT_NAME, + + EC20_SAMPLE_POWER_PIN, + EC20_SAMPLE_STATUS_PIN, + EC20_SAMPLE_RECV_BUFF_LEN, +}; + +static int ec20_device_register(void) +{ + struct at_device_ec20 *ec20 = &e0; + + return at_device_register(&(ec20->device), + ec20->device_name, + ec20->client_name, + AT_DEVICE_CLASS_EC20, + (void *) ec20); +} +INIT_APP_EXPORT(ec20_device_register); diff --git a/samples/at_sample_esp8266.c b/samples/at_sample_esp8266.c new file mode 100644 index 0000000..245ac41 --- /dev/null +++ b/samples/at_sample_esp8266.c @@ -0,0 +1,52 @@ +/* + * File : at_sample_esp8266.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 + * 2019-05-10 chenyong first version + */ + +#include + +#define LOG_TAG "at.sample" +#include + +#define ESP8266_SAMPLE_DEIVCE_NAME "esp0" + +static struct at_device_esp8266 esp0 = +{ + ESP8266_SAMPLE_DEIVCE_NAME, + ESP8266_SAMPLE_CLIENT_NAME, + + ESP8266_SAMPLE_WIFI_SSID, + ESP8266_SAMPLE_WIFI_PASSWORD, + ESP8266_SAMPLE_RECV_BUFF_LEN, +}; + +static int esp8266_device_register(void) +{ + struct at_device_esp8266 *esp8266 = &esp0; + + return at_device_register(&(esp8266->device), + esp8266->device_name, + esp8266->client_name, + AT_DEVICE_CLASS_ESP8266, + (void *) esp8266); +} +INIT_APP_EXPORT(esp8266_device_register); diff --git a/samples/at_sample_m26.c b/samples/at_sample_m26.c new file mode 100644 index 0000000..449b4d7 --- /dev/null +++ b/samples/at_sample_m26.c @@ -0,0 +1,52 @@ +/* + * File : at_sample_m26.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 + * 2019-05-13 chenyong first version + */ + +#include + +#define LOG_TAG "at.sample" +#include + +#define M26_SAMPLE_DEIVCE_NAME "m0" + +static struct at_device_m26 m0 = +{ + M26_SAMPLE_DEIVCE_NAME, + M26_SAMPLE_CLIENT_NAME, + + M26_SAMPLE_POWER_PIN, + M26_SAMPLE_STATUS_PIN, + M26_SAMPLE_RECV_BUFF_LEN, +}; + +static int m26_device_register(void) +{ + struct at_device_m26 *m26 = &m0; + + return at_device_register(&(m26->device), + m26->device_name, + m26->client_name, + AT_DEVICE_CLASS_M26_MC20, + (void *) m26); +} +INIT_APP_EXPORT(m26_device_register); diff --git a/samples/at_sample_rw007.c b/samples/at_sample_rw007.c new file mode 100644 index 0000000..ba5aa81 --- /dev/null +++ b/samples/at_sample_rw007.c @@ -0,0 +1,52 @@ +/* + * File : at_sample_rw007.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 + * 2019-05-13 chenyong first version + */ + +#include + +#define LOG_TAG "at.sample" +#include + +#define RW007_SAMPLE_DEIVCE_NAME "r0" + +static struct at_device_rw007 r0 = +{ + RW007_SAMPLE_DEIVCE_NAME, + RW007_SAMPLE_CLIENT_NAME, + + RW007_SAMPLE_WIFI_SSID, + RW007_SAMPLE_WIFI_PASSWORD, + RW007_SAMPLE_RECV_BUFF_LEN, +}; + +static int rw007_device_register(void) +{ + struct at_device_rw007 *rw007 = &r0; + + return at_device_register(&(rw007->device), + rw007->device_name, + rw007->client_name, + AT_DEVICE_CLASS_RW007, + (void *) rw007); +} +INIT_APP_EXPORT(rw007_device_register); diff --git a/samples/at_sample_sim76xx.c b/samples/at_sample_sim76xx.c new file mode 100644 index 0000000..21fc688 --- /dev/null +++ b/samples/at_sample_sim76xx.c @@ -0,0 +1,52 @@ +/* + * File : at_sample_sim76xx.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 + * 2019-05-13 chenyong first version + */ + +#include + +#define LOG_TAG "at.sample" +#include + +#define SIM76XX_SAMPLE_DEIVCE_NAME "sim1" + +static struct at_device_sim76xx sim1 = +{ + SIM76XX_SAMPLE_DEIVCE_NAME, + SIM76XX_SAMPLE_CLIENT_NAME, + + SIM76XX_SAMPLE_POWER_PIN, + SIM76XX_SAMPLE_STATUS_PIN, + SIM76XX_SAMPLE_RECV_BUFF_LEN, +}; + +static int sim76xx_device_register(void) +{ + struct at_device_sim76xx *sim76xx = &sim1; + + return at_device_register(&(sim76xx->device), + sim76xx->device_name, + sim76xx->client_name, + AT_DEVICE_CLASS_SIM76XX, + (void *) sim76xx); +} +INIT_APP_EXPORT(sim76xx_device_register); diff --git a/samples/at_sample_sim800c.c b/samples/at_sample_sim800c.c new file mode 100644 index 0000000..f2c28f8 --- /dev/null +++ b/samples/at_sample_sim800c.c @@ -0,0 +1,52 @@ +/* + * File : at_sample_sim800c.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 + * 2019-05-13 chenyong first version + */ + +#include + +#define LOG_TAG "at.sample" +#include + +#define SIM800C_SAMPLE_DEIVCE_NAME "sim0" + +static struct at_device_sim800c sim0 = +{ + SIM800C_SAMPLE_DEIVCE_NAME, + SIM800C_SAMPLE_CLIENT_NAME, + + SIM800C_SAMPLE_POWER_PIN, + SIM800C_SAMPLE_STATUS_PIN, + SIM800C_SAMPLE_RECV_BUFF_LEN, +}; + +static int sim800c_device_register(void) +{ + struct at_device_sim800c *sim800c = &sim0; + + return at_device_register(&(sim800c->device), + sim800c->device_name, + sim800c->client_name, + AT_DEVICE_CLASS_SIM800C, + (void *) sim800c); +} +INIT_APP_EXPORT(sim800c_device_register); diff --git a/src/at_device.c b/src/at_device.c new file mode 100644 index 0000000..573bcf1 --- /dev/null +++ b/src/at_device.c @@ -0,0 +1,330 @@ +/* + * File : at_device.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 + * 2019-05-08 chenyong first version + */ + +#include +#include + +#include + +#define DBG_TAG "at.dev" +#define DBG_LVL DBG_INFO +#include + +/* The global list of at device */ +static struct at_device *at_device_list = RT_NULL; +/* The global list of at device class */ +static struct at_device_class *at_device_class_list = RT_NULL; + +/** + * This function will get the first initialized AT device. + * + * @return the AT device structure pointer + */ +struct at_device *at_device_get_first_initialized(void) +{ + rt_base_t level; + rt_slist_t *node = RT_NULL; + struct at_device *device = RT_NULL; + + level = rt_hw_interrupt_disable(); + + for (node = &(at_device_list->list); node; node = rt_slist_next(node)) + { + device = rt_slist_entry(node, struct at_device, list); + if (device && device->is_init == RT_TRUE) + { + rt_hw_interrupt_enable(level); + return device; + } + } + + rt_hw_interrupt_enable(level); + + return RT_NULL; +} + +/** + * This function will get AT device by device name. + * + * @param type the name type + * @param name the device name or the client name + * + * @return the AT device structure pointer + */ +struct at_device *at_device_get_by_name(int type, const char *name) +{ + rt_base_t level; + rt_slist_t *node = RT_NULL; + struct at_device *device = RT_NULL; + + RT_ASSERT(name); + + level = rt_hw_interrupt_disable(); + + for (node = &(at_device_list->list); node; node = rt_slist_next(node)) + { + device = rt_slist_entry(node, struct at_device, list); + if (device) + { + if (((type == AT_DEVICE_NAMETYPE_DEVICE) || (type == AT_DEVICE_NAMETYPE_NETDEV)) && + (rt_strncmp(device->name, name, rt_strlen(name)) == 0)) + { + rt_hw_interrupt_enable(level); + return device; + } + else if ((type == AT_DEVICE_NAMETYPE_CLIENT) && + (rt_strncmp(device->client->device->parent.name, name, rt_strlen(name)) == 0)) + { + rt_hw_interrupt_enable(level); + return device; + } + } + } + + rt_hw_interrupt_enable(level); + + return RT_NULL; +} + +#ifdef AT_USING_SOCKET +/** + * This function will get AT device by ip address. + * + * @param ip_addr input ip address + * network + * @return != NULL: network interface device object + * NULL: get failed + */ +struct at_device *at_device_get_by_ipaddr(ip_addr_t *ip_addr) +{ + rt_base_t level; + rt_slist_t *node = RT_NULL; + struct at_device *device = RT_NULL; + + level = rt_hw_interrupt_disable(); + + for (node = &(at_device_list->list); node; node = rt_slist_next(node)) + { + device = rt_slist_entry(node, struct at_device, list); + if (device && ip_addr_cmp(ip_addr, &(device->netdev->ip_addr))) + { + rt_hw_interrupt_enable(level); + return device; + } + } + + rt_hw_interrupt_enable(level); + + return RT_NULL; + +} +#endif /* AT_USING_SOCKET */ + + +/** + * This function will perform a variety of control functions on AT devices. + * + * @param device the pointer of AT device structure + * @param cmd the command sent to AT device + * @param arg the argument of command + * + * @return = 0: perform successfully + * < 0: perform failed + */ +int at_device_control(struct at_device *device, int cmd, void *arg) +{ + if (device->class->device_ops->control) + { + return device->class->device_ops->control(device, cmd, arg); + } + else + { + LOG_W("AT device(%s) not support control operations.", device->name); + return RT_EOK; + } +} + +/** + * This function registers an AT device class with specified device class ID. + * + * @param class the pointer of AT device class structure + * @param class_id AT device class ID + * + * @return 0: register successfully + */ +int at_device_class_register(struct at_device_class *class, uint16_t class_id) +{ + rt_base_t level; + + RT_ASSERT(class); + + /* Fill AT device class */ + class->class_id = class_id; + + /* Initialize current AT device class single list */ + rt_slist_init(&(class->list)); + + level = rt_hw_interrupt_disable(); + + /* Add current AT device class to list */ + if (at_device_class_list == RT_NULL) + { + at_device_class_list = class; + } + else + { + /* Tail insertion */ + rt_slist_append(&(at_device_class_list->list), &(class->list)); + } + + rt_hw_interrupt_enable(level); + + return RT_EOK; +} + +/* Get AT device class by client ID */ +static struct at_device_class *at_device_class_get(uint16_t class_id) +{ + rt_base_t level; + rt_slist_t *node = RT_NULL; + struct at_device_class *class = RT_NULL; + + if (at_device_class_list == RT_NULL) + { + return RT_NULL; + } + + level = rt_hw_interrupt_disable(); + + /* Get AT device class by class ID */ + for (node = (&at_device_class_list->list); node; node = rt_slist_next(node)) + { + class = rt_slist_entry(node, struct at_device_class, list); + if (class && class->class_id == class_id) + { + rt_hw_interrupt_enable(level); + return class; + } + } + + rt_hw_interrupt_enable(level); + + return RT_NULL; +} + +/** + * This function registers an AT device with specified device name and AT client name. + * + * @param device the pointer of AT device structure + * @param device_name AT device name + * @param at_client_name AT device client name + * @param class_id AT device class ID + * @param user_data user-specific data + * + * @return = 0: register successfully + * < 0: register failed + */ +int at_device_register(struct at_device *device, const char *device_name, + const char *at_client_name, uint16_t class_id, void *user_data) +{ + rt_base_t level; + int result = 0; + static int device_counts = 0; + char name[RT_NAME_MAX] = {0}; + struct at_device_class *class = RT_NULL; + + RT_ASSERT(device); + RT_ASSERT(device_name); + RT_ASSERT(at_client_name); + + class = at_device_class_get(class_id); + if (class == RT_NULL) + { + LOG_E("get AT device class(%d) failed.", class_id); + result = -RT_ERROR; + goto __exit; + } + + /* Fill AT device object*/ +#ifdef AT_USING_SOCKET + device->sockets = (struct at_socket *) rt_calloc(class->socket_num, sizeof(struct at_socket)); + if (device->sockets == RT_NULL) + { + LOG_E("no memory for AT Socket number(%d) create.", class->socket_num); + result = -RT_ENOMEM; + goto __exit; + } + + /* create AT device socket event */ + rt_snprintf(name, RT_NAME_MAX, "at_se%d", device_counts++); + device->socket_event = rt_event_create(name, RT_IPC_FLAG_FIFO); + if (device->socket_event == RT_NULL) + { + LOG_E("no memory for AT device(%s) socket event create.", device_name); + result = -RT_ENOMEM; + goto __exit; + } +#endif /* AT_USING_SOCKET */ + + rt_memcpy(device->name, device_name, rt_strlen(device_name)); + device->class = class; + device->user_data = user_data; + + /* Initialize current AT device single list */ + rt_slist_init(&(device->list)); + + level = rt_hw_interrupt_disable(); + + /* Add current AT device to device list */ + if (at_device_list == RT_NULL) + { + at_device_list = device; + } + else + { + /* Tail insertion */ + rt_slist_append(&(at_device_list->list), &(device->list)); + } + + rt_hw_interrupt_enable(level); + + /* Initialize AT device */ + result = class->device_ops->init(device); + if (result < 0) + { + goto __exit; + } + +__exit: + if (result < 0) + { + device->is_init = RT_FALSE; + } + else + { + device->is_init = RT_TRUE; + } + + return RT_EOK; +}