Просмотр исходного кода

【添加】ppp_device 软件包

Signed-off-by: liuxianliang <liuxianliang@rt-thread.com>
liuxianliang 6 лет назад
Родитель
Сommit
91d64f12e3
11 измененных файлов с 1540 добавлено и 2 удалено
  1. 132 2
      README.md
  2. 16 0
      SConscript
  3. 145 0
      class/air720/ppp_device_air720.c
  4. 25 0
      class/air720/ppp_device_air720.h
  5. 43 0
      inc/ppp_chat.h
  6. 89 0
      inc/ppp_device.h
  7. 34 0
      inc/ppp_netif.h
  8. 101 0
      samples/ppp_sample_air720.c
  9. 235 0
      src/ppp_chat.c
  10. 613 0
      src/ppp_device.c
  11. 107 0
      src/ppp_netif.c

+ 132 - 2
README.md

@@ -1,2 +1,132 @@
-# ppp_device
-lwIP PPP porting for different devices
+# PPP device #
+
+## 1. 简介 ##
+
+ PPP(Piont to Piont Protocl) 点对点协议,相对于使用AT命令连接网络,PPP 则免去操作众多的AT命令及其解析。使用 PPP device 软件包,PPP连接成功后,便获取 IP 地址和 DNS ;之后就可以像使用网卡一样使用 2G/3G/4G 模块,剩下的工作内容就是使用 Socket 套接字,结合 TCP 和 UDP 协议实现数据的传输,操作会变得更加灵活轻巧,也可以轻巧的移植,获取更高的传输质量,提高网络吞吐量。PPP 是 lwIP 协议的一部分,原生 lwIP 即可支持,无需太多操作即可实现联网。
+
+目前 PPP 功能仅支持 Luat Air720 模块,后续会接入更多 2G/3G/4G 模块。
+
+### 1.1. 目录结构 ###
+
+| 名称 | 说明 |
+| ---- | ---- |
+| src | PPP device 实现源码目录 |
+| inc | PPP device 头文件目录 |
+| sample | 不同设备示例文件目录 |
+| class | 不同设备针对 PPP 组件的移植适配目录 |
+| class/air720 | Air720 设备针对 PPP 组件的移植目录,实现 PPP拨号上网 功能 |
+
+### 1.2 许可证 ###
+
+ppp_device 软件包遵循 Apache-2.0 许可,详见 LICENSE 文件。
+
+### 1.3 依赖 ###
+
+- RT_Thread 3.0+
+- lwIP 组件( ppp 功能)
+- AT 组件 (AT Client功能)
+
+## 2. 获取方式 ##
+
+**PPP 组件相关配置选项介绍**
+
+
+```c
+--- PPP DEVICE: lwIP PPP porting for different device
+    [ ]   Enable debug log output
+    [ ]   Enbale authorize connect feature
+    [*]   Enable lin status detect feature
+    (1)     Link status detecct timeout
+          Select modem type (Luat Air720)  --->
+          Select network operator (china mobile)  --->
+    [*]   nable air720 ppp device samples
+    (a0)    air720 device name
+    (uart3) air720 ppp device uart name
+          Version (latest)  --->
+```
+- **Enable debug log output:** 开启调试日志功能
+- **Enbale authorize connect feature:** 开启身份认证功能
+- **Enable lin status detect feature:** PPP链路连接监控,检测链路连接正常;设置为 0 则不开启链路监控;
+- **Select modem type:** 模块选择
+- **Select network operator:** 网络运营商选择
+- **Enable air720 ppp device samples:**  选择模块后会提示的模块使用示例
+- **air720 device name:** 模块名称,注册的网卡名称将会与该名称一致
+- **air720 ppp device uart name:** 模块使用的串口
+- **Version:** 软件包版本号
+
+## 3. 使用方式
+
+ppp device 软件包初始化函数如下所示:
+
+模块启动函数,该函数自动调用;没有调用停止函数前,不可再次调用。
+
+```c
+int air720_start(void);
+```
+
+* 初始化模块,获取模块基础信息;
+* 模块拨号,模块进入 PPP 模式;
+* 注册 netdev 设备,接入标准网络框架;
+
+模块停止函数,该函数可以退出 PPP 模式。
+
+```c
+int ppp_air720_stop(void);
+```
+
+* 退出 PPP 模式,模块退出拨号模式;
+* 解注册 netdev 设备;
+
+模块上电后,自动初始化流程如下:
+
+```c
+ \ | /
+- RT -     Thread Operating System
+ / | \     4.0.2 build Sep 23 2019
+ 2006 - 2019 Copyright by rt-thread team
+lwIP-2.0.2 initialized!
+[I/sal.skt] Socket Abstraction Layer initialize success.
+[I/ppp.chat] (uart3) has control by modem_chat.
+[I/ppp.chat] chat success
+[I/ppp.dev] (uart3) is used by ppp_device.
+msh />[I/ppp.dev] ppp connect successful.
+```
+
+设备上电初始化完成,模块提示拨号成功,然后可以在 FinSH 中输入命令 `ifconfig` 查看设备 IP 地址、MAC 地址等网络信息,如下所示:
+
+```shell
+msh />ifconfig
+network interface device: a0 (Default)           ## 设备名称
+MTU: 1500                                        ## 网络最大传输单元
+MAC: 56 3e 3e 04 03 05                           ## 设备 MAC 地址
+FLAGS: UP LINK_UP INTERNET_DOWN DHCP_DISABLE     ## 设备标志
+ip address: 10.32.76.151                         ## 设备 IP 地址
+gw address: 10.64.64.64                          ## 设备网关地址
+net mask  : 255.255.255.255                      ## 设备子网掩码
+dns server #0: 114.114.114.114                   ## 域名解析服务器地址0
+dns server #1: 120.196.165.7                     ## 域名解析服务器地址1
+```
+
+获取 IP 地址成功之后,如果开启 Ping 命令功能,可以在 FinSH 中输入命令 `ping + 域名地址` 测试网络连接状态, 如下所示:
+
+```shell
+msh />ping www.baidu.com
+60 bytes from 183.232.231.172 icmp_seq=0 ttl=55 time=84 ms
+60 bytes from 183.232.231.172 icmp_seq=1 ttl=55 time=78 ms
+60 bytes from 183.232.231.172 icmp_seq=2 ttl=55 time=78 ms
+60 bytes from 183.232.231.172 icmp_seq=3 ttl=55 time=78 ms
+```
+
+`ping` 命令测试正常说明 PPP DEVICE 设备网络连接成功,之后可以使用 SAL(套接字抽象层) 抽象出来的标准 BSD Socket APIs 进行网络开发(MQTT、HTTP、MbedTLS、NTP、Iperf 等)。
+
+## 4. 注意事项
+
+* 一般的SIM卡因为只能从运营商网络获取内网地址,所以不能实现服务器相关功能。
+
+## 5. 联系方式
+
+xiangxistu
+
+QQ:1254605504
+
+email: xiangxistu@foxmail.com

+ 16 - 0
SConscript

@@ -0,0 +1,16 @@
+from building import *
+
+cwd = GetCurrentDir()
+path = [cwd + '/inc']
+src  = Glob('src/*.c')
+
+# Air720
+if GetDepend(['PPP_DEVICE_USING_AIR720']):
+    path += [cwd + '/class/air720']
+    src += Glob('class/air720/ppp_device_air720.c')
+    if GetDepend(['PPP_DEVICE_AIR720_SAMPLE']):
+        src += Glob('samples/ppp_sample_air720.c')
+
+group = DefineGroup('ppp_device', src, depend = ['PKG_USING_PPP_DEVICE'], CPPPATH = path)
+
+Return('group')

+ 145 - 0
class/air720/ppp_device_air720.c

@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2006-2019, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author         Notes
+ * 2019-08-15    xiangxistu      the first version
+ */
+
+#include <ppp_device_air720.h>
+
+#define DBG_TAG    "ppp.air720"
+
+#ifdef PPP_DEVICE_DEBUG
+#define DBG_LVL   DBG_LOG
+#else
+#define DBG_LVL   DBG_INFO
+#endif
+
+#include <rtdbg.h>
+
+static const struct modem_chat_data mcd[] =
+{
+    {"ATH",          MODEM_CHAT_RESP_OK,        30, 1},
+    {"AT",           MODEM_CHAT_RESP_OK,        10, 1},
+    {"ATE0",         MODEM_CHAT_RESP_OK,        1,  1},
+    {PPP_APN_CMD,    MODEM_CHAT_RESP_OK,        1,  5},
+    {PPP_DAIL_CMD,   MODEM_CHAT_RESP_CONNECT,   2, 30},
+};
+
+/*
+ * Use AT command init modem
+ *
+ * @param struct ppp_device *device
+ *
+ * @return  0: execute successful
+ *         -1: send AT commands errorstruct struct ppp_device *device *devicestruct ppp_device *device
+ *         -5: no memory
+ */
+static rt_err_t ppp_air720_init(struct ppp_device *device)
+{
+    RT_ASSERT(device != RT_NULL);
+    return RT_EOK;
+}
+
+/*
+ * Get into PPP modem,using uart
+ *
+ * @param NULL
+ *
+ * @return  0: execute successful
+ *         -1: send AT commands error
+ *         -5: no memory
+ */
+static rt_err_t ppp_air720_open(struct ppp_device *device, rt_uint16_t oflag)
+{
+    rt_device_t uart_device = RT_NULL;
+    rt_err_t result = RT_EOK;
+
+    RT_ASSERT(device != RT_NULL);
+
+    uart_device = rt_device_find(device->rely_name);
+    if (uart_device == RT_NULL)
+    {
+        LOG_E("Can't find relying device %s.", device->rely_name);
+        return -RT_ERROR;
+    }
+    result = rt_device_open(uart_device, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_RX);
+    if (result != RT_EOK)
+    {
+        LOG_E("relying device open(%s) fail.", device->rely_name);
+        return RT_NULL;
+    }
+
+    rt_thread_mdelay(1000);
+    rt_device_write(uart_device, 0, "+++", 3);
+    rt_thread_mdelay(500);
+
+    return modem_chat(uart_device, mcd, sizeof(mcd) / sizeof(mcd[0]));
+}
+
+/*
+ * close ppp , deinit uart
+ *
+ * @param NULL
+ *
+ * @return  0: execute successful
+ *         -1: send AT commands error
+ *         -5: no memory
+ */
+static rt_err_t ppp_air720_close(struct ppp_device *device)
+{
+    RT_ASSERT(device != RT_NULL);
+    return RT_EOK;
+}
+
+/*
+ * control,using AT command to get csq,imei,net_type value
+ *
+ * @param struct ppp_device *
+ *               cmd
+ *               *arg
+ * @return  0: execute successful
+ *         -1: send AT commands error
+ *         -5: no memory
+ */
+static rt_err_t ppp_air720_control(struct ppp_device *device, int cmd, void *arg)
+{
+    RT_ASSERT(device != RT_NULL);
+    return RT_EOK;
+}
+
+/* ppp_air720_ops for ppp_device_ops , a common interface */
+static struct ppp_device_ops air720_ops =
+{
+    ppp_air720_init,
+    ppp_air720_open,
+    ppp_air720_close,
+    ppp_air720_control
+};
+
+/*
+ * register air720 into ppp_device
+ *
+ * @param struct ppp_air720 *       piont
+ *        const char *              name
+ *        int                       flag
+ *        void *                    resever data
+ * @return  ppp_device function piont
+ *
+ */
+int ppp_air720_register(struct ppp_air720 *air720, const char *dev_name, const char *uart_name, void *user_data)
+{
+    struct ppp_device *ppp_device = RT_NULL;
+
+    RT_ASSERT(air720 != RT_NULL);
+
+    ppp_device = &(air720->device);
+    ppp_device->ops = &air720_ops;
+
+    LOG_D("ppp air720 is registering ppp_device");
+
+    return ppp_device_register(ppp_device, dev_name, uart_name, user_data);
+}

+ 25 - 0
class/air720/ppp_device_air720.h

@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2006-2019, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author         Notes
+ * 2019-08-15    xiangxistu      the first version
+ */
+
+#ifndef __PPP_AIR720_H__
+#define __PPP_AIR720_H__
+
+#include <ppp_device.h>
+
+/* Air720  ppp_device base from ppp_device */
+struct ppp_air720
+{
+    struct ppp_device  device;          /* ppp_device struct in ppp_air720 */
+    enum ppp_trans_type type;           /* the type is used to establish a ppp connection */
+};
+
+extern int ppp_air720_register(struct ppp_air720 *air720, const char *dev_name, const char *rely_name, void *user_data);
+
+#endif  /* __PPP_AIR720_H__ */

+ 43 - 0
inc/ppp_chat.h

@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2019 xiaofan <xfan1024@live.com>
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author          Notes
+ * 2019-09-19     xiaofan         the first version
+ */
+
+#ifndef __MODEM_CHAT_H__
+#define __MODEM_CHAT_H__
+
+#include <rtthread.h>
+#include <rtdevice.h>
+
+#define MODEM_CHAT_RESP_LIST(F) \
+    F(MODEM_CHAT_RESP_OK,         "OK"), \
+    F(MODEM_CHAT_RESP_READY,      "READY"), \
+    F(MODEM_CHAT_RESP_CONNECT,    "CONNECT"), \
+    F(MODEM_CHAT_RESP_BUSY,       "BUSY"), \
+    F(MODEM_CHAT_RESP_NO_CARRIER, "NO CARRIER"), \
+    F(MODEM_CHAT_RESP_ERROR,      "ERROR") \
+
+#define DEFINE_MODEM_RESP_ID_TABLE(id, s) id
+
+enum {
+    MODEM_CHAT_RESP_LIST(DEFINE_MODEM_RESP_ID_TABLE),
+    MODEM_CHAT_RESP_MAX,
+    MODEM_CHAT_RESP_NOT_NEED = MODEM_CHAT_RESP_MAX,
+};
+
+struct modem_chat_data {
+    const char* transmit;
+    rt_uint8_t expect;      // use CHAT_RESP_xxx
+    rt_uint8_t retries;
+    rt_uint8_t timeout;     // second
+};
+
+
+rt_err_t modem_chat(rt_device_t serial, const struct modem_chat_data *data, rt_size_t len);
+
+#endif  /* __MODEM_CHAT_H__ */

+ 89 - 0
inc/ppp_device.h

@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2006-2019, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author          Notes
+ * 2019-08-15     xiangxistu      the first version
+ */
+
+#ifndef __PPP_DEVICE_H__
+#define __PPP_DEVICE_H__
+
+#include <rtdef.h>
+
+#include <netif/ppp/ppp.h>
+#include <netif/ppp/pppos.h>
+#include <netif/ppp/pppapi.h>
+#include <lwip/dns.h>
+#include <lwip/netif.h>
+#include <ppp_chat.h>
+
+#define PPP_DAIL_CMD         "ATD*99#"                                    /* common dailing cmd */
+#ifdef  PPP_APN_CMCC
+#define PPP_APN_CMD          "AT+CGDCONT=1,\"IP\",\"CMNET\""            /* China Mobile Communication Company */
+#endif
+#ifdef  PPP_APN_CUCC
+#define PPP_APN_CMD          "AT+CGDCONT=1,\"IP\",\"UNINET\""           /* China Unicom Communication Company */
+#endif
+#ifdef  PPP_APN_CTCC
+#define PPP_APN_CMD          "AT+CGDCONT=1,\"IP\",\"CTNET\""            /* China Telecom Communication Company */
+#endif
+#define PPP_CTL_GET_CSQ      1
+#define PPP_CTL_GET_IEMI     2
+#define PPP_CTL_GET_TYPE     3
+
+enum ppp_trans_type
+{
+    PPP_TRANS_CHAT,
+    PPP_TRANS_AT_CLIENT
+};
+
+enum ppp_conn_type
+{
+    PPP_CONNET_UART,                            /* using uart connect ppp */
+    PPP_CONNET_USB                              /* using  usb connect ppp */
+};
+
+/**
+ * @brief PPPoS Client IP Information
+ *
+ */
+struct ppp_device
+{
+    struct rt_device parent;                    /* join rt_device frame */
+    const char rely_name[RT_NAME_MAX];          /* the name of the low-level driver device */
+    const struct ppp_device_ops *ops;           /* ppp device ops interface */
+    enum ppp_conn_type conn_type;               /* using usb or uart */
+
+    ppp_pcb *pcb;                               /* ppp protocol control block */
+    struct netif pppif;
+
+    char *recv_line_buf;                        /* the current received one line data buffer */
+    rt_size_t recv_line_len;                    /* The length of the currently received one line data */
+    rt_size_t recv_bufsz;                       /* The maximum supported receive data length */
+
+    rt_sem_t rx_notice;                         /* attention uart to recieve data delivery to tcpip */
+    rt_mutex_t lock;                            /* protect uart  */
+
+    rt_thread_t recv_tid;                       /* recieve thread point */
+    rt_bool_t ppp_link_status;                  /* if ppp link is shut down, close recieve thread and shut down */
+    void *user_data;                            /* reserve */
+};
+
+struct ppp_device_ops
+{
+    rt_err_t  (*init)   (struct ppp_device *dev);
+    rt_err_t  (*open)   (struct ppp_device *dev, rt_uint16_t oflag);
+    rt_err_t  (*close)  (struct ppp_device *dev);
+    rt_err_t  (*control)(struct ppp_device *dev, int cmd, void *args);
+};
+
+/* store at_client rx_callback function */
+typedef  rt_err_t (*uart_rx_cb)(rt_device_t dev, rt_size_t size);
+
+/* offer register funciton to user */
+int ppp_device_register(struct ppp_device *ppp_device, const char *dev_name, const char *rely_name, void *user_data);
+
+#endif /* __PPP_DEVICE_H__ */

+ 34 - 0
inc/ppp_netif.h

@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2006-2019, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author          Notes
+ * 2019-08-15     xiangxistu      the first version
+ */
+
+#ifndef __NETIF_PPPNETIF_H__
+#define __NETIF_PPPNETIF_H__
+
+#include "lwip/netif.h"
+#include <rtthread.h>
+
+#define NIOCTL_GADDR        0x01
+#ifndef RT_LWIP_PPP_MTU
+#define PPPNET_MTU      1500
+#else
+#define PPPNET_MTU      RT_LWIP_PPP_MTU
+#endif
+
+/* eth flag with auto_linkup or phy_linkup */
+#define ETHIF_LINK_AUTOUP   0x0000
+#define ETHIF_LINK_PHYUP    0x0100
+
+/* proviode a public interface to register netdev */
+rt_err_t ppp_netdev_add(struct netif *ppp_netif);
+void ppp_netdev_del(struct netif *ppp_netif);
+extern struct netdev *netdev_get_by_name(const char *name);
+extern int netdev_unregister(struct netdev *netdev);
+
+#endif /* __NETIF_PPPNETIF_H__ */

+ 101 - 0
samples/ppp_sample_air720.c

@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2006-2019, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author         Notes
+ * 2019-08-15    xiangxistu      the first version
+ */
+
+#include<rtthread.h>
+#include<ppp_device_air720.h>
+
+
+#define DBG_TAG    "air720.sample"
+
+#ifdef PPP_DEVICE_DEBUG
+#define DBG_LVL   DBG_LOG
+#else
+#define DBG_LVL   DBG_INFO
+#endif
+
+#include <rtdbg.h>
+
+static struct ppp_air720 air720;
+
+/*
+ * init air720 mode
+ *
+ * @param NULL
+ *
+ *
+ * @return  RT_EOK      successful
+ *          -RT_ERROR   failed
+ *
+ */
+
+int air720_register(void)
+{
+    int result = RT_EOK;
+
+    result = ppp_air720_register(&air720, PPP_AIR720_DEVICE_NAME, PPP_AIR720_CLIENT_NAME, RT_NULL);
+    if (result != RT_EOK)
+    {
+        LOG_E("%s registered failed, please try again.", PPP_AIR720_DEVICE_NAME);
+    }
+
+    return result;
+}
+INIT_ENV_EXPORT(air720_register);
+
+int air720_start(void)
+{
+    rt_device_t device = RT_NULL;
+
+    device = rt_device_find(PPP_AIR720_DEVICE_NAME);
+    if (device == RT_NULL)
+    {
+        LOG_E("Cann't find device %s, execute failed", PPP_AIR720_DEVICE_NAME);
+        return -RT_ERROR;
+    }
+
+    if (rt_device_open(device, 0) != RT_EOK)
+    {
+        LOG_E("Cann't open device %s, execute failed", PPP_AIR720_DEVICE_NAME);
+        return -RT_ERROR;
+    }
+
+    return RT_EOK;
+}
+INIT_APP_EXPORT(air720_start);
+MSH_CMD_EXPORT(air720_start, a sample create air720 for dail to network);
+/*
+ * close air720 ppp mode, hang up from network
+ *
+ * @param NULL
+ *
+ *
+ * @return  NULL
+ *
+ */
+int ppp_air720_stop(void)
+{
+    rt_device_t device = RT_NULL;
+
+    device = rt_device_find(PPP_AIR720_DEVICE_NAME);
+    if (device == RT_NULL)
+    {
+        LOG_E("Cann't find device %s, execute failed", PPP_AIR720_DEVICE_NAME);
+        return -RT_ERROR;
+    }
+
+    if (rt_device_close(device) != RT_EOK)
+    {
+        LOG_E("Cann't close device %s, execute failed", PPP_AIR720_DEVICE_NAME);
+        return -RT_ERROR;
+    }
+
+    return RT_EOK;
+}
+MSH_CMD_EXPORT(ppp_air720_stop, a sample stop air720 for dail to network);

+ 235 - 0
src/ppp_chat.c

@@ -0,0 +1,235 @@
+/*
+ * Copyright (c) 2019 xiaofan <xfan1024@live.com>
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author          Notes
+ * 2019-09-19     xiaofan         the first version
+ */
+
+#include "ppp_chat.h"
+#define DBG_TAG    "ppp.chat"
+
+#ifdef PPP_DEVICE_DEBUG
+#define DBG_LVL   DBG_LOG
+#else
+#define DBG_LVL   DBG_INFO
+#endif
+
+#include <rtdbg.h>
+
+#define CHAT_READ_BUF_MAX 16
+
+// In order to match response, we need a string search algorithm
+// KMP and AC algorithm both are good choice, But we need the code
+// is simple, readable and use lower RAM/ROM.
+// So We use a simplified search algorithm, this alg is like the KMP.
+// Specifically we assume the failure vecotor is [-1, 0, 0, 0, ...]
+// This assuming is not work for all pattern string. Fortunately,
+// it's work  for this scene.
+
+#define DEFINE_MODEM_RESP_STRDATA_TABLE(id, str) [id] = str
+#define DEFINE_MODEM_RESP_STRLEN_TABLE(id, str)  [id] = (sizeof(str)-1)
+
+
+static char *resp_strdata[] =
+{
+    MODEM_CHAT_RESP_LIST(DEFINE_MODEM_RESP_STRDATA_TABLE)
+};
+
+static rt_uint8_t resp_strlen[] =
+{
+    MODEM_CHAT_RESP_LIST(DEFINE_MODEM_RESP_STRLEN_TABLE)
+};
+
+#define CHAT_DATA_FMT           "<tx: %s, want: %s, retries: %u, timeout: %u>"
+#define CHAT_DATA_STR(data)     (data)->transmit, resp2str((data)->expect), (data)->retries, (data)->timeout
+
+static const char* resp2str(rt_uint8_t resp_id)
+{
+    RT_ASSERT(resp_id < MODEM_CHAT_RESP_MAX);
+    return resp_strdata[resp_id];
+}
+
+/* only one device support */
+static struct rt_completion rx_comp_p;
+
+static rt_uint8_t resp_match(rt_uint8_t resp_id, rt_uint8_t state, char ch)
+{
+    while (1)
+    {
+        if (resp2str(resp_id)[state] == ch)
+            return state + 1;
+        if (state == 0)
+            return 0;
+        state = 0;
+    };
+}
+
+static rt_bool_t resp_matched(rt_uint8_t resp_id, rt_uint8_t state)
+{
+    return state == resp_strlen[resp_id];
+}
+
+/*
+ * chat_rx_ind , callback function if serial recieve data
+ *
+ * @param rt_device_t                       device
+ *        rt_size_t                         size
+ *
+ * @return  0   :   successful
+ *
+ */
+static rt_err_t chat_rx_ind(rt_device_t device, rt_size_t size)
+{
+    rt_completion_done(&rx_comp_p);
+    return RT_EOK;
+}
+
+/*
+ * chat_read_until , waitting for recieve data from serial
+ *
+ * @param struct rt_serial_device           *serial
+ *        const struct modem_chat_data      *data
+ *
+ * @return  0   :   timeout, can't recieve any data
+ *          size:   the size of recieve data
+ */
+static rt_size_t chat_read_until(rt_device_t serial, void *buffer, rt_size_t size, rt_tick_t stop)
+{
+    rt_size_t rdlen;
+    rt_tick_t wait;
+
+    rt_completion_init(&rx_comp_p);
+    rdlen = rt_device_read(serial, 0, buffer, size);
+    if (rdlen)
+        return rdlen;
+
+    wait = stop - rt_tick_get();
+    if (wait > RT_TICK_MAX / 2)
+        return 0;
+
+    rt_completion_wait(&rx_comp_p, wait);
+    return rt_device_read(serial, 0, buffer, size);
+}
+
+/*
+ * modem_chat_once , send an order to control modem
+ *
+ * @param struct rt_serial_device           *serial
+ *        const struct modem_chat_data      *data
+ *
+ * @return  0: execute successful
+ *
+ */
+static rt_err_t modem_chat_once(rt_device_t serial, const struct modem_chat_data *data)
+{
+    rt_uint8_t resp_state[MODEM_CHAT_RESP_MAX] = { 0 }, resp;
+    rt_tick_t stop = rt_tick_get() + data->timeout*RT_TICK_PER_SECOND;
+    rt_size_t rdlen, pos;
+    char rdbuf[CHAT_READ_BUF_MAX];
+
+    if (data->transmit)
+    {
+        LOG_D(CHAT_DATA_FMT" transmit --> modem", CHAT_DATA_STR(data));
+        rt_device_write(serial, 0, data->transmit, rt_strlen(data->transmit));
+        rt_device_write(serial, 0, "\r", 1);
+    }
+
+    if (data->expect == MODEM_CHAT_RESP_NOT_NEED)
+    {
+        rt_thread_mdelay(1000*data->timeout);
+        return RT_EOK;
+    }
+
+    do
+    {
+        rdlen = chat_read_until(serial, rdbuf, CHAT_READ_BUF_MAX, stop);
+        for (pos = 0; pos < rdlen; pos++)
+        {
+            for (resp = 0; resp < MODEM_CHAT_RESP_MAX; resp++)
+            {
+                resp_state[resp] = resp_match(resp, resp_state[resp], rdbuf[pos]);
+                if (resp_matched(resp, resp_state[resp]))
+                {
+                    if (resp == data->expect)
+                        return RT_EOK;
+
+                    LOG_W(CHAT_DATA_FMT" not matched, got: %s", CHAT_DATA_STR(data), resp2str(resp));
+                    return -RT_ERROR;
+                }
+            }
+        }
+    } while ( stop - rt_tick_get() < RT_TICK_MAX / 2);
+    LOG_W(CHAT_DATA_FMT" timeout", CHAT_DATA_STR(data));
+    return -RT_ETIMEOUT;
+}
+
+/*
+ * modem_chat_internal , init modem and turn modem into ppp type
+ *
+ * @param struct rt_serial_device           *serial
+ *        const struct modem_chat_data      *data
+ *        rt_size_t                         len
+ *
+ * @return  0: execute successful
+ *
+ */
+static rt_err_t modem_chat_internal(rt_device_t serial, const struct modem_chat_data *data, rt_size_t len)
+{
+    rt_err_t err = RT_EOK;
+    rt_size_t i;
+    rt_uint8_t retry_time;
+
+    for (i = 0; i < len; i++)
+    {
+        LOG_D(CHAT_DATA_FMT" running", CHAT_DATA_STR(&data[i]));
+        for (retry_time = 0; retry_time < data[i].retries; retry_time++)
+        {
+            err = modem_chat_once(serial, &data[i]);
+            if (err == RT_EOK)
+                break;
+        }
+        if (err != RT_EOK)
+        {
+            LOG_E(CHAT_DATA_FMT" fail", CHAT_DATA_STR(&data[i]));
+            break;
+        }
+        LOG_D(CHAT_DATA_FMT" success", CHAT_DATA_STR(&data[i]));
+    }
+    return err;
+}
+
+/*
+ * modem_chat , a function for ppp dailing to network
+ *
+ * @param struct rt_serial_device           *serial
+ *        const struct modem_chat_data      *data
+ *        rt_size_t                         len
+ *
+ * @return  0: execute successful
+ *
+ */
+rt_err_t modem_chat(rt_device_t serial, const struct modem_chat_data *data, rt_size_t len)
+{
+    rt_err_t (*old_rx_ind)(rt_device_t dev, rt_size_t size) = NULL;
+    rt_err_t err;
+
+    rt_completion_init(&rx_comp_p);
+    old_rx_ind = serial->rx_indicate;
+    rt_device_set_rx_indicate(serial, chat_rx_ind);
+
+    LOG_I("(%s) has control by modem_chat.", serial->parent.name);
+    err = modem_chat_internal(serial, data, len);
+    if (err != RT_EOK)
+    {
+        LOG_E("chat failed");
+        goto __exit;
+    }
+    LOG_I("chat success");
+
+    serial->rx_indicate = old_rx_ind;
+__exit:
+    return err;
+}

+ 613 - 0
src/ppp_device.c

@@ -0,0 +1,613 @@
+/*
+ * Copyright (c) 2006-2019, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author          Notes
+ * 2019-08-15     xiangxistu      the first version
+ */
+
+#include <ppp_device.h>
+#include <ppp_netif.h>
+
+#define DBG_TAG    "ppp.dev"
+
+#ifdef PPP_DEVICE_DEBUG
+#define DBG_LVL   DBG_LOG
+#else
+#define DBG_LVL   DBG_INFO
+#endif
+
+#include <rtdbg.h>
+
+#define PPP_DEV_TABLE_LEN    4
+
+#if RT_LWIP_TCPTHREAD_STACKSIZE < 2048
+#error "tcpip stack is too small, should greater than 2048."
+#endif
+
+
+static struct ppp_device *_g_ppp_device = NULL;
+
+/*
+ * Receive callback function , release rx_notice when uart acquire data
+ *
+ * @param rt_device_t
+ *        rt_size_t
+ *
+ * @return  0: execute successful
+ *
+ */
+static rt_err_t ppp_device_rx_ind(rt_device_t dev, rt_size_t size)
+{
+    RT_ASSERT(dev != RT_NULL);
+    struct ppp_device *ppp_dev = _g_ppp_device;
+
+    /* when recieve data from uart , release semphone to wake up recieve thread */
+    rt_sem_release(ppp_dev->rx_notice);
+
+    return RT_EOK;
+}
+
+/*
+ *  using ppp_data_send send data to lwIP procotol stack     PPPoS serial output callback
+ *
+ * @param ppp_pcb   *pcb                 pcb PPP control block
+ *        uint8_t   *data                data Buffer to write to serial port
+ *        uint32_t  len                  len Length of the data buffer
+ *        void      *ppp_device          ctx Context of callback , ppp_device
+ *
+ * @return  0: creat response fail
+ *
+ */
+static uint32_t ppp_data_send(ppp_pcb *pcb, uint8_t *data, uint32_t len, void *ppp_device)
+{
+    RT_ASSERT(pcb != RT_NULL);
+    RT_ASSERT(ppp_device != RT_NULL);
+
+    int result = RT_EOK;
+    struct ppp_device *device = (struct ppp_device *)ppp_device;
+    rt_device_t recv_device = RT_NULL;
+
+    RT_ASSERT(device != RT_NULL);
+
+    /* recv_device is rt_device , find recv_device through device name */
+    recv_device = rt_device_find(device->rely_name);
+    if (recv_device == RT_NULL)
+    {
+        LOG_E("Can find device (%s), ppp send data execute failed.",device->rely_name);
+        result = -RT_ERROR;
+        goto __exit;
+    }
+
+    /* the return data is the actually written size on successful */
+    len = rt_device_write(recv_device,0,data,len);
+
+    /* must be return data length , or will get warning like "pppos_write[0]: output failed len=24" */
+    return len;
+
+__exit:
+
+    return result;
+}
+
+/*
+ * ppp_status_changed callback function
+ *
+ * @param   ppp_pcb *pcb            protocol caontrol block
+ *          int     err_code
+ *          void    *ctx            reserver
+ *
+ * @return  0: creat response fail
+ *
+ */
+static void ppp_status_changed(ppp_pcb *pcb, int err_code, void *ctx)
+{
+    struct ppp_device *pppdev = (struct ppp_device *)ctx;
+    struct netif *pppif = ppp_netif(pcb);
+    static uint8_t re_count = 0;
+    switch (err_code)
+    {
+    case PPPERR_NONE:                /* Connected */
+        pppdev->pppif.mtu = pppif->mtu;
+        if (pppdev->ppp_link_status == 1)
+        {
+            ppp_netdev_add(&pppdev->pppif);
+        }
+        re_count = 0;
+        LOG_I("ppp connect successful.");
+        break;
+    case PPPERR_PARAM:
+        LOG_E("Invalid parameter.");
+        break;
+    case PPPERR_OPEN:
+        LOG_E("Unable to open PPP session.");
+        break;
+    case PPPERR_DEVICE:
+        LOG_E("Invalid I/O device for PPP.");
+        break;
+    case PPPERR_ALLOC:
+        LOG_E("Unable to allocate resources.");
+        break;
+    case PPPERR_USER:
+        LOG_E("free the ppp control block , because user control.");
+        pppapi_free(pcb);           /* Free the PPP control block */
+        break;
+    case PPPERR_CONNECT:            /* Connection lost */
+        LOG_E("ppp connect lost.");
+        pppapi_connect(pcb, 0);
+        LOG_I("Reconnection.");
+        break;
+    case PPPERR_AUTHFAIL:
+        LOG_E("Failed authentication challenge.");
+        break;
+    case PPPERR_PROTOCOL:
+        LOG_E("Failed to meet protocol.");
+        break;
+    case PPPERR_PEERDEAD:
+        LOG_E("Connection timeout.");
+        pppapi_connect(pcb, 0);
+        if(re_count++ == 2)
+        {
+            re_count = 0;
+            pppdev->ppp_link_status = 0;
+            pppdev->ops->close(pppdev);
+        }
+        else
+        {
+             LOG_I("Reconnection.");
+        }
+        break;
+    case PPPERR_IDLETIMEOUT:
+        LOG_E("Idle Timeout.");
+        break;
+    case PPPERR_CONNECTTIME:
+        LOG_E("Max connect time reached.");
+        break;
+    case PPPERR_LOOPBACK:
+        LOG_E("Loopback detected.");
+        break;
+    default:
+        LOG_E("Unknown error code %d.", err_code);
+        break;
+    }
+}
+
+/*
+ * Receive thread , store uart data and transform tcpip stack
+ *
+ * @param ppp_device *device
+ *
+ *
+ * @return  0: execute successful
+ *
+ */
+static int ppp_recv_entry(struct ppp_device *device)
+{
+    RT_ASSERT(device != RT_NULL);
+
+    char ch = 0, old_ch = 0;
+    int result = RT_EOK;
+    static char thrans_flag = 0;
+    rt_device_t recv_dev = RT_NULL;
+    device->recv_line_len = 0;
+
+    /* alloc buff to store uart data */
+    device->recv_line_buf = (char *)rt_calloc(1, 1550);
+    if (device->recv_line_buf == RT_NULL)
+    {
+        LOG_E("ppp_recv_line_buff alloc memory failed! No memory for receive buffer.");
+        result = -RT_ENOMEM;
+        goto __exit;
+    }
+
+    /* use name to find rt_devcie */
+    recv_dev = rt_device_find(device->rely_name);
+    if (recv_dev == RT_NULL)
+    {
+        LOG_E("Can find device (%s), ppp recv entry creat failed.",device->rely_name);
+        result = -RT_ERROR;
+        goto __exit;
+    }
+
+    while (1)
+    {
+        /* ppp link is closed, the recieve thread also need to exit */
+        if(device->ppp_link_status != 1)
+        {
+            break;
+        }
+        /* when rx_notice semphone come , recieve data from uart */
+        rt_sem_take(device->rx_notice, RT_WAITING_FOREVER);
+
+        /* uart devcie , recieve data from uart and store data in the recv_buff */
+        rt_device_read(recv_dev, 0, &ch, 1);
+
+        /* begin to recieve data from uart */
+        if (thrans_flag == 1)
+        {
+            /* if recieve 0x7e twice */
+            if (ch == 0x7e && old_ch == 0x7e)
+            {
+                /* choice the least 0x7e as frame head */
+                device->recv_line_buf[0] = ch;
+                device->recv_line_len = 1;
+
+                old_ch = ch;
+            }
+            else if(ch == 0x7e && old_ch == 0x00)
+            {
+                thrans_flag = 2;
+                device->recv_line_buf[device->recv_line_len] = ch;
+            }
+            else
+            {
+                old_ch = 0x00;
+                device->recv_line_buf[device->recv_line_len] = ch;
+                device->recv_line_len++;
+            }
+
+            /* when a frame is end, put data into tcpip */
+            if (thrans_flag == 2)
+            {
+                rt_enter_critical();
+                pppos_input_tcpip(device->pcb, (u8_t *)device->recv_line_buf, device->recv_line_len + 1);
+                rt_exit_critical();
+
+                thrans_flag = 0;
+                device->recv_line_len = 0;
+            }
+        }
+        else
+        {
+            /* if recieve 0x7e, begin to recieve data */
+            if (ch == 0x7e)
+            {
+                thrans_flag = 1;
+                old_ch = ch;
+                device->recv_line_buf[0] = ch;
+                device->recv_line_len = 1;
+            }
+        }
+
+    }
+
+__exit:
+
+    rt_free(device->recv_line_buf);
+
+    return result;
+}
+
+/*
+ * Creat a thread to creat receive thread function
+ *
+ * @param ppp_device *device
+ *
+ *
+ * @return  0: execute successful
+ *
+ */
+static int ppp_recv_entry_creat(struct ppp_device *device)
+{
+    rt_int8_t result = RT_EOK;
+
+    /* protect public resoure */
+    device->lock = rt_mutex_create("mutex_ppp_recv", RT_IPC_FLAG_FIFO);
+    if (device->lock == RT_NULL)
+    {
+        LOG_E("PPP device initialize failed! ppp_device_recv_lock create failed!");
+        result = -RT_ENOMEM;
+        goto __exit;
+    }
+
+    /* when recieve rx_notice, come to recieve a data from uart */
+    device->rx_notice = rt_sem_create("sem_ppp_recv", 0, RT_IPC_FLAG_FIFO);
+    if (device->rx_notice == RT_NULL)
+    {
+        LOG_E("PPP device initialize failed! ppp_device_recv_notice create failed!");
+        result = -RT_ENOMEM;
+        goto __exit;
+    }
+
+    /* dynamic creat a recv_thread */
+    device->recv_tid = rt_thread_create("ppp_recv",
+                                        (void (*)(void *parameter))ppp_recv_entry,
+                                        device,
+                                        2 * 1024,
+                                        8,
+                                        20);
+    if (device->recv_tid == RT_NULL)
+    {
+        LOG_E("PPP device initialize failed! ppp_device_recv_tid create failed!");
+        result = -RT_ENOMEM;
+        goto __exit;
+    }
+
+    /* if you create a thread, never forget to start it */
+    rt_thread_startup(device->recv_tid);
+
+__exit:
+    if (result != RT_EOK)
+    {
+        if (device->lock)
+        {
+            rt_mutex_delete(device->lock);
+        }
+
+        if (device->rx_notice)
+        {
+            rt_sem_delete(device->rx_notice);
+        }
+
+        rt_memset(device, 0x00, sizeof(struct ppp_device));
+    }
+    return result;
+}
+
+/*
+ * ppp device init function,set ops funciton and base config
+ *
+ * @param rt_device_t *device
+ *
+ *
+ * @return  0: execute successful
+ *
+ */
+static rt_err_t ppp_device_init(struct rt_device *device)
+{
+    int result = RT_EOK;
+    RT_ASSERT(device != RT_NULL);
+
+    struct ppp_device *ppp_device = (struct ppp_device *)device;
+    RT_ASSERT(ppp_device != RT_NULL);
+
+    result = ppp_device->ops->init(ppp_device);
+    if (result != RT_EOK)
+    {
+        LOG_E("ppp_device_init failed(%s).", ppp_device->rely_name);
+        goto __exit;
+    }
+
+__exit:
+
+    return result;
+}
+
+/*
+ * initialize ppp device and set callback function
+ *
+ * @param rt_device_t *device
+ *        rt_uint16_t oflag
+ *
+ * @return  0:  execute successful
+ *
+ */
+static rt_err_t ppp_device_open(struct rt_device *device, rt_uint16_t oflag)
+{
+    int result = RT_EOK;
+    RT_ASSERT(device != RT_NULL);
+
+    struct ppp_device *ppp_device = (struct ppp_device *)device;
+    RT_ASSERT(ppp_device != RT_NULL);
+    static rt_device_t serial;
+
+    ppp_device->parent.ref_count++;
+
+    ppp_device->ppp_link_status = 1;
+    /* Creat a thread to creat ppp recieve function */
+    result = ppp_recv_entry_creat(ppp_device);
+    if (result != RT_EOK)
+    {
+        LOG_E("Creat a thread to creat ppp recieve function failed.");
+        result = -RT_ERROR;
+        goto __exit;
+    }
+    LOG_D("Creat a thread to creat ppp recieve function successful.");
+
+    /* we can do nothing */
+    result = ppp_device->ops->open(ppp_device, oflag);
+    if (result != RT_EOK)
+    {
+        LOG_E("ppp device open failed.");
+        result = -RT_ERROR;
+        goto __exit;
+    }
+
+    /* uart conversion into ppp device , find and open command device */
+    serial = rt_device_find(ppp_device->rely_name);
+    if (serial  != RT_NULL)
+    {
+        RT_ASSERT(serial->type == RT_Device_Class_Char);
+
+        /* uart transfer into tcpip protocol stack */
+        rt_device_set_rx_indicate(serial, ppp_device_rx_ind);
+        LOG_I("(%s) is used by ppp_device.", ppp_device->rely_name);
+    }
+    else
+    {
+        LOG_E("Cannot find %s device.", ppp_device->rely_name);
+        result = -RT_ERROR;
+        goto __exit;
+    }
+
+    /* creat pppos */
+    ppp_device->pcb = pppapi_pppos_create(&(ppp_device->pppif), ppp_data_send, ppp_status_changed, ppp_device);
+    if (ppp_device->pcb == RT_NULL)
+    {
+        LOG_E("Create ppp pcb failed.");
+        result = -RT_ERROR;
+        goto __exit;
+    }
+    LOG_D("pppapi_pppos_create has created a protocol control block.");
+
+    /* set netif name */
+    ppp_device->pppif.name[0] = ppp_device->parent.parent.name[0];
+    ppp_device->pppif.name[1] = ppp_device->parent.parent.name[1];
+
+    /* set default route */
+    result = pppapi_set_default(ppp_device->pcb);
+    if (result != RT_EOK)
+    {
+        LOG_E("pppapi_set_default execute failed.");
+        result = -RT_ERROR;
+        goto __exit;
+    }
+    LOG_D("pppapi_set_default has set a default route.");
+
+    /* dns */
+    ppp_set_usepeerdns(ppp_device->pcb, 1);
+    LOG_D("ppp_set_usepeerdns has set a dns number.");
+
+#ifdef USING_PPP_AUTHORIZE
+    /* set authorize */
+ #if PAP_SUPPORT
+     ppp_set_auth(ppp_device->pcb , PPPAUTHTYPE_PAP, ppp_device->config.user_name, ppp_device->config.user_name);
+ #elif CHAP_SUPPORT
+     ppp_set_auth(ppp_device->pcb, PPPAUTHTYPE_CHAP, ppp_device->config.user_name, ppp_device->config.user_name);
+ #else
+ #error "Unsupported AUTH Negotiation"
+ #endif
+    LOG_D("ppp_set_auth has passed verification.");
+ #endif /* PPP_AUTHORIZE */
+
+    /* ppp connect */
+    result = pppapi_connect(ppp_device->pcb, 0);
+    if (result != RT_EOK)
+    {
+        LOG_E("pppapi_connect execute failed.");
+        result = -RT_ERROR;
+        goto __exit;
+    }
+    LOG_D("pppapi_connect execute successful, ppp has connected.");
+
+__exit:
+
+    return result;
+}
+
+/*
+ * Close ppp device
+ *
+ * @param rt_device_t   *device
+ *
+ *
+ * @return  0: execute successful
+ */
+static rt_err_t ppp_device_close(struct rt_device *device)
+{
+	extern void ppp_netdev_del(struct netif *ppp_netif);
+    RT_ASSERT(device != RT_NULL);
+
+    struct ppp_device *ppp_device = (struct ppp_device *)device;
+    RT_ASSERT(ppp_device != RT_NULL);
+
+    /* use pppapi_close to shutdown ppp link status */
+    pppapi_close(ppp_device->pcb, 0);
+
+    ppp_device->ppp_link_status = 0;
+    rt_sem_release(ppp_device->rx_notice);
+
+    /* delete netdev from netdev frame */
+    ppp_netdev_del(&ppp_device->pppif);
+    LOG_D("ppp netdev has been detach.");
+
+    /* cut down piont to piont at data link layer */
+    return ppp_device->ops->close(ppp_device);
+}
+
+/*
+ * Control ppp device , access ppp mode or accsee AT mode
+ *
+ * @param rt_device_t   *device
+ *        int           cmd
+ *        void          *args
+ *
+ * @return  0: execute successful
+ */
+static rt_err_t ppp_device_control(struct rt_device *device,int cmd, void *args)
+{
+    RT_ASSERT(device != RT_NULL);
+
+    struct ppp_device *ppp_device = (struct ppp_device *)device;
+    RT_ASSERT(ppp_device != RT_NULL);
+
+    /* use ppp_device_control function */
+    return ppp_device->ops->control(ppp_device,cmd,args);
+}
+
+/*
+ * ppp device ops
+ *
+ */
+#ifdef RT_USING_DEVICE_OPS
+const struct rt_device_ops ppp_device_ops =
+{
+    ppp_device_init,
+    ppp_device_open,
+    ppp_device_close,
+    ppp_device_control
+};
+#endif
+
+/*
+ * Register ppp_device into rt_device frame,set ops function to rt_device inferface
+ *
+ * @param struct ppp_device *ppp_device
+ *
+ * @return  0: execute successful
+ *
+ */
+int ppp_device_register(struct ppp_device *ppp_device, const char *dev_name, const char *rely_name, void *user_data)
+{
+    rt_err_t result = RT_EOK;
+
+    RT_ASSERT(ppp_device != RT_NULL);
+
+    struct rt_device *device = RT_NULL;
+    device = &(ppp_device->parent);
+
+#ifdef RT_USING_DEVICE_OPS
+    device->ops = &ppp_device_ops;
+#else
+    device->init = ppp_device_init;
+    device->open = ppp_device_open;
+    device->close = ppp_device_close;
+    device->read = RT_NULL;
+    device->write = RT_NULL;
+    device->control = ppp_device_control;
+#endif
+    device->user_data = user_data;
+
+    rt_strncpy((char *)ppp_device->rely_name, rely_name, rt_strlen(rely_name));
+
+    /* attention: you can't use ppp_device as a server in you network, unless
+     sim of the modem module used supprots getting public IP address. */
+     if(_g_ppp_device != RT_NULL)
+     {
+         RT_ASSERT("Only one device support.");
+     }
+
+    /* register ppp device into rt_device frame */
+    result = rt_device_register(&ppp_device->parent, dev_name, RT_Device_Class_NetIf);
+    if( result == RT_EOK)
+    {
+        _g_ppp_device = ppp_device;
+        LOG_D("ppp_device has registered rt_device frame successful.");
+    }
+
+    /* when ppp device has register rt_device frame, start up it */
+    do
+    {
+        result = device->init((rt_device_t)ppp_device);
+        if (result != RT_EOK)
+        {
+            LOG_E("ppp device init failed.try it in %ds", 2500/100);
+            rt_thread_mdelay(2500);
+            result = -RT_ERROR;
+        }
+    } while (result != RT_EOK);
+
+    return result;
+}

+ 107 - 0
src/ppp_netif.c

@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2006-2019, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author          Notes
+ * 2019-08-15     xiangxistu      the first version
+ * 2019-09-19     xiaofan         use lwip_netdev_ops instead of ppp_netdev_ops
+ */
+
+#include "lwip/opt.h"
+#include "ppp_netif.h"
+
+#ifdef RT_USING_NETDEV
+
+#include "lwip/ip.h"
+#include "lwip/netdb.h"
+#include <netdev.h>
+
+extern const struct netdev_ops lwip_netdev_ops;
+
+rt_err_t ppp_netdev_add(struct netif *ppp_netif)
+{
+#define LWIP_NETIF_NAME_LEN 2
+#define PPP_NETIF_ADDR_LEN  6
+    int result = 0;
+    struct netdev *netdev = RT_NULL;
+    char name[LWIP_NETIF_NAME_LEN + 1] = {0};
+
+    RT_ASSERT(ppp_netif);
+
+    netdev = (struct netdev *)rt_calloc(1, sizeof(struct netdev));
+    if (netdev == RT_NULL)
+    {
+        return -ERR_IF;
+    }
+
+#ifdef SAL_USING_LWIP
+    extern int sal_lwip_netdev_set_pf_info(struct netdev *netdev);
+
+    /* set the lwIP network interface device protocol family information */
+    sal_lwip_netdev_set_pf_info(netdev);
+#endif /* SAL_USING_LWIP */
+
+    rt_strncpy(name, ppp_netif->name, LWIP_NETIF_NAME_LEN);
+    result = netdev_register(netdev, name, (void *)ppp_netif);
+
+    /* OUI 00-04-A3 (hex): Microchip Technology, Inc. */
+    netdev->hwaddr[0] = 0x95;
+    netdev->hwaddr[1] = 0x45;
+    netdev->hwaddr[2] = 0x68;
+    /* set MAC address, only for test */
+    netdev->hwaddr[3] = 0x39;
+    netdev->hwaddr[4] = 0x68;
+    netdev->hwaddr[5] = 0x52;
+
+    /* Update netdev info after registered */
+    netdev->flags = ppp_netif->flags;
+    netdev->mtu = ppp_netif->mtu;
+    netdev->ops = &lwip_netdev_ops;
+    netdev->hwaddr_len =  PPP_NETIF_ADDR_LEN;
+    netdev->ip_addr = ppp_netif->ip_addr;
+    netdev->gw = ppp_netif->gw;
+    netdev->netmask = ppp_netif->netmask;
+
+	{
+        extern const ip_addr_t* dns_getserver(u8_t numdns);
+        extern void  dns_setserver(u8_t numdns, const ip_addr_t *dnsserver);
+
+		/* sometime we can get second dns server but first dns server is empty, wo need do something to fix it */
+		if (!ip_addr_isany(dns_getserver(0)))
+        {
+            netdev_low_level_set_dns_server(netdev, 0, dns_getserver(0));
+        }
+        else
+        {
+        #define DEF_DNS_SERVER "114.114.114.114"
+            ip_addr_t dns_server;
+            inet_aton(DEF_DNS_SERVER, &dns_server);
+            dns_setserver(0, &dns_server);
+        }
+
+        netdev_low_level_set_dns_server(netdev, 1, dns_getserver(1));
+    }
+
+
+    return result;
+}
+
+void ppp_netdev_del(struct netif *ppp_netif)
+{
+    char name[LWIP_NETIF_NAME_LEN + 1];
+    struct netdev *netdev;
+
+    RT_ASSERT(ppp_netif);
+
+    rt_strncpy(name, ppp_netif->name, LWIP_NETIF_NAME_LEN);
+    netdev = netdev_get_by_name(name);
+    if (netdev)
+    {
+        netdev_unregister(netdev);
+        rt_free(netdev);
+    }
+}
+
+#endif  /* RT_USING_NETDEV */