lwip之ppp在EasyARM2200和SmartARM2200上的移植zz

发信人: gdtyy (gdtyy), 信区: Embedded
标  题: lwip之ppp在EasyARM2200和SmartARM2200上的移植
发信站: 水木社区 (Mon Jun 25 23:22:10 2007), 站内

************************************************
* lwip之ppp在EasyARM2200和SmartARM2200上的移植 *
************************************************
                          ------ 浅谈《ecos增值包》在二次开发方面的用途
    2007/03/27  [email protected]  www.armecos.com

    ecos本身已经支持openBSD、FreeBSD、lwip三款TCP/IP协议栈,用户只要通过标准的
socket接口就可以实现网络编程,但很多网友希望看到底层实现细节,满足好奇心,学习
TCP/IP工作原理,虽然有些画蛇添足,不过《ecos增值包》的平台验证功能完全满足这类需
求,只要您愿意,完全可以忽略(裁减)掉ecos的协议栈而在应用层自行实现,随心所欲地做
自己想做的事情。其他如文件系统等也类似,您也可以旁路掉系统自带API,直接驱动硬件
,在应用层实现自己的文件操作函数。或者把自己以前总结的用着顺手的工具集移植到
ecos上替换相应函数。这完全取决于您的意愿,ecos不会限制。您有很大的自由度二次开发
ecos系统,不想用的部份通通去掉,搞不定的部份让ecos代劳。

    有很多文档介绍以太网(感兴趣的话可以看以前写过的《51+8019方案》,《ecos网络驱
动》,《ucos+lwip by 杨晔》),这次介绍个网络上比较少见的通过串口ppp实现lwip栈的
方案。

    ------------
    | lwip简介 |
    ------------
    lwip是轻量级的TCP/IP协议栈中间件,通过抽象操作系统平台接口,基于它编写的网络
应用程序可以跨OS平台使用。操作系统抽象了硬件,中间件又抽象了操作系统,所以应用程
序变得更容易移植,减少重复劳动,提高代码质量。因为是轻量级协议栈,所以特别适合用
来入门学习。lwip即可以在无操作系统情况下依靠回调函数运行,也可以在有OS情况下以兼
容socket插口方式工作或者使用自己的内部接口函数。lwip支持ARP、SLIP、PPP、DHCP、
ICMP、IPV4、IPV6、RAW、UDP、TCP、(DNS)等协议。

    --------------------------------
    | 《ecos增值包》中间件解决方案 |
    --------------------------------
    现在对设备间连网的需求比较大,在旧有设备上连网需要转换模块,缺点是成本高,二
次转换效率低下(模块接口界面烦琐晦涩,需要了解供应商自定义的非通用概念),好处是不
用/少量改造现有设备。本方案提供的是在新设备上一体化的解决方法,使用操作系统中间
件实现对外互联。因为是中间件,所以用户不必分心在非核心应用上,完全软件解决,器件
成本最低;没有二次转换,效率高;比自带协议模块更灵活,可实现特殊控制,如RESET、
掉电;可在ecos下实现分级电源管理,降低功耗。

    ----------------
    | lwip移植规划 |
    ----------------
    lwip网络接口支持:以太网、串口SLIP、PPP等,我们选择PPP接口,实现PAP、CHAP认
证,实现普通电话、GPRS、CDMA拨号。
    上层接口支持:回调、内部自定义、标准socket接口,我们选择标准的socket插口界面

          ------------------------   AT    --------   /dev/ser1
----------------------------   socket  ------------
          | 电话/GPRS/CDMA MODEM | <-----> | 串口 | <-----------> | lwip arch
|| lwip socket | <-------->| 应用程序 |
          ------------------------         --------
----------------------------           ------------

    由上图可知:
    MODEM和开发板是通过9线串口互联(本方案可以很方便地改造成光纤、PCMCIA、USB等硬
件接口界面),关于ecos串口,详见《第六讲 串口操作》及其附录。ecos将串口抽象为设备
文件,我们选择串口1连接MODEM,文件名为/dev/ser1,ecos会自动枚举出这个串口,并能
探测出是9线串口,自动关联正确的驱动程序。因为缺省配置了128字节缓冲,ecos会自动选
择基于中断的驱动方式。用户不必关心寄存器配置,因为16C55X是工业标准,ecos能够识别
且有通用驱动程序,由于ecos自动管理串口,所以不会使用户在串口驱动上遇到障碍。用户
需要注意的是跳线(TXD、RXD、DCD)要连接好,打开串口设备文件,配置属性、读写串口、
关闭,仅此而已。

    不同MODEM的AT指令集不完全相同,但都基本符合贺式标准。通过AT指令,用户可以摘
挂机、拨号、收发数据、收发短信等。中文短信需要编码,短信有延迟或者丢失现象,关键
信息、实时信息不要通过短信传送。还有要注意各地包年包月套餐费用(尤其注意是否多收
了莫名其妙的信息费),转发器分布和数量,天线接收灵敏度不同,需要综合考虑。

    ----------------
    | lwip结构分析 |
    ----------------
    lwip移植需要改动的部分如下:
    /arch/                   OS平台相关层:串口、线程、内存管理、信号量、时间管
理等封装函数及初始化。
    /include/arch/           类型定义、大小端等
    /include/net_lwip.h      网络参数配置
    /include/lwipopts.h      lwip选项
    /netif/ppp/ppp.c         PPP串行输入和输出接口
    /netif/ppp/modem.c       MODEM驱动

    ----------------
    | lwip移植难点 |
    ----------------
    对TCP/IP原理要熟悉,仔细阅读《TCP/IP详解》一书。
    对lwip目录结构和工作流程要特别熟悉。画出数据流路径。

    ----------------
    | lwip移植详述 |
    ----------------
    /arch/sio.c
--------------------------------------------------------------------------------
-----------
void
sio_send(char c,void * dev)
{
    len = 1;
    cyg_io_write(*(cyg_io_handle_t*)dev, &c, &len);
}

char
sio_recv(void * dev)
{
    char c;
    len = 1;
    cyg_io_read(*(cyg_io_handle_t *)dev, &c, &len);
    return c;
}

int
sio_write(void *dev, char *b, int size)
{
    Cyg_ErrNo err;

    unsigned int len;
    unsigned int tlen = 0;

    while(tlen < size){
        len = size - tlen;
        err = cyg_io_write(*(cyg_io_handle_t*)dev, b+tlen, &len);
        if(err != ENOERR){
            printf("/n/n/n/nSerial port ERROR!/n/n/n/n");
            return 0;
        }
        cyg_io_get_config(*(cyg_io_handle_t*)dev,
CYG_IO_GET_CONFIG_SERIAL_OUTPUT_DRAIN, NULL, 0 ) ; //等待所有数据被发送完
        tlen = tlen + len;
    }
    return size;
}

int
sio_read(void *dev, char *b, int size)
{
    int len = size;
    cyg_io_read(*(cyg_io_handle_t*)dev, b, &len);
    return len;
}

void *
sio_open(int devnum)  //devnum此处没有用到
{
    int res;
    cyg_uint32 len;

    cyg_uint16 _scpcr ;     //Enable RTS valid
    cyg_uint8 _scfcr ;      //Enable RTS/CTS control真正使能RTS和CTS

#if LWIP_SLIP
    #define SIODEV SLIP_DEV
#elif PPP_SUPPORT
    #define SIODEV PPP_DEV
#endif

    cyg_serial_info_t serial_info =
        CYG_SERIAL_INFO_INIT( SCIF_BAUDRATE,
        CYGNUM_SERIAL_STOP_1,
        CYGNUM_SERIAL_PARITY_NONE,
        CYGNUM_SERIAL_WORD_LENGTH_8,
        0 );

    len = sizeof(serial_info);

    cyg_io_lookup(SIODEV, &ser);

    res = cyg_io_set_config(ser, CYG_IO_SET_CONFIG_SERIAL_INFO, &serial_info,
&len);
    if (res != ENOERR){
      //diag_printf("Cannot open %s/n", SIODEV);
    }

    return &ser;
}

void
sio_read_abort(void * dev)
{
   //diag_printf("Abort called/n");
}
--------------------------------------------------------------------------------
-----------

/arch/sys_arch.c
--------------------------------------------------------------------------------
-----------
//建立内存池,初始化thread和to。
void sys_init(void)
{
    cyg_mempool_var_create(memvar, sizeof(memvar), &var_mempool_h, &var_mempool)
;

    threads = NULL;
    to.next = NULL;
}

//创建一个新邮箱。如果内存不够,返回NULL。
sys_mbox_t sys_mbox_new(void)
{
    cyg_mbox * mbox;
    cyg_handle_t m;
    mbox = (cyg_mbox *)cyg_mempool_var_try_alloc(var_mempool_h, sizeof(cyg_mbox)
);

    if(!mbox)
        return SYS_MBOX_NULL;

    //m和mbox实际是一样的
    cyg_mbox_create(&m, mbox);
    return m;
}

//销毁邮箱并释放其占用的内存空间。
void sys_mbox_free(sys_mbox_t mbox)
{
    if (mbox != SYS_MBOX_NULL) {
        cyg_mbox_delete(mbox);
        cyg_mempool_var_free(var_mempool_h,(void*)mbox);
    }
}

//cyg_mbox_put不能传送NULL,否则cyg_mbox_get将不能区分到底是真实数据还是错误条件

//但是,lwIP确实在某些适于使用信号量的时候传递NULL。所以要用&dummy_msg代替NULL。
int dummy_msg = 1;

//发送消息到信箱
void sys_mbox_post(sys_mbox_t mbox, void *data)
{
    if (!data)  //cyg_mbox_put的消息不能为NULL,用&dummy_msg代替。
        data = &dummy_msg;
    while (cyg_mbox_put(mbox,data) == false);
}

#if 0  //注释用
void
sys_mbox_fetch(sys_mbox_t mbox, void **msg){
    void *d;
    d = cyg_mbox_get(mbox);
    if (msg)
        *msg = d;
}
#endif

//超时等待信箱,超时返回-1,正常返回等待ms数。
u32_t sys_arch_mbox_fetch(sys_mbox_t mbox, void **data, u32_t timeout)
{
    void *d;
    cyg_tick_count_t end_time = 0, start_time = 0;
    if (timeout) {//超时等待信箱
        start_time = cyg_current_time();
        d = cyg_mbox_timed_get(mbox, start_time + msec_to_tick(timeout));
        end_time = cyg_current_time();

        //超时
        if (d == NULL)
            return SYS_ARCH_TIMEOUT;
    }
    else{//永远等待信箱
        d = cyg_mbox_get(mbox);
    }

    if (data) {
        if (d == (void *)&dummy_msg)
            *data = NULL;
        else
            *data = d;
    }
    //返回延时ms数
    return tick_to_msec(end_time - start_time);
}


//分配一个新信号量并初始化,如果没有空间可以提供则返回NULL
sys_sem_t sys_sem_new(u8_t count)
{
    sys_sem_t sem;   //????sys_sem_t*???

    //从内存池分配可变长内存块,如无空间则立即返回NULL,否则返回新指针。
    sem = (cyg_sem_t *)cyg_mempool_var_try_alloc(var_mempool_h,
sizeof(cyg_sem_t));
    if(!sem)
        return SYS_SEM_NULL;
    cyg_semaphore_init(sem, count);
    return sem;
}

#if 0  //注释用
void
sys_sem_wait(sys_sem_t sem)
{
    cyg_semaphore_wait(sem);
}

void
sys_timeout(u16_t msecs, sys_timeout_handler h, void *arg)
{}
#endif

//超时等待一个信号量,如果超时返回-1,否则返回等待时间.
u32_t sys_arch_sem_wait(sys_sem_t sem, u32_t timeout)
{
    cyg_bool_t r;
    cyg_tick_count_t end_time = 0, start_time = 0;

    if (timeout) {//带延时等待信号量
        start_time = cyg_current_time();
        r = cyg_semaphore_timed_wait(sem, start_time + msec_to_tick(timeout));
        end_time = cyg_current_time();

        if (r == false) {//超时
            return SYS_ARCH_TIMEOUT;
        }
    }
    else{//永远等待信号量
        cyg_semaphore_wait(sem);
    }
    //收到信号量,返回延时ms数
    return tick_to_msec(end_time - start_time);
}

//发送信号量
void sys_sem_signal(sys_sem_t sem)
{
    cyg_semaphore_post(sem);
}

//销毁信号量并释放其所占用的内存空间
void sys_sem_free(sys_sem_t sem)
{
    //销毁信号量
    cyg_semaphore_destroy(sem);
    //释放内存,内存池,分配时返回指针
    cyg_mempool_var_free(var_mempool_h,(void*)sem);
}

//创建新线程(线程函数,参数,优先级)
//thread_create(优先级,线程实体函数,线程参数,线程名字,堆栈基址,堆栈大小,返
回的线程句柄,线程数据存储空间)
sys_thread_t sys_thread_new(void (*function) (void *arg), void *arg,int prio)
{
    struct lwip_thread * nt;
    void * stack;
    static int thread_count = 0;
    //可变长度内存分配,内存池,大小
    nt = (struct lwip_thread *)cyg_mempool_var_alloc(var_mempool_h,
sizeof(struct lwip_thread));

    nt->next = threads;
    nt->to.next = NULL;

    threads = nt;

    //堆栈起址为memfix+每个堆栈大小*线程数++
    stack = (void *)(memfix+CYGNUM_LWIP_THREAD_STACK_SIZE*thread_count++);
    cyg_thread_create(prio, (cyg_thread_entry_t *)function, (cyg_addrword_t)arg,
        (char *)arg , stack, CYGNUM_LWIP_THREAD_STACK_SIZE, &(nt->th), &(nt->t)
);

    cyg_thread_resume(nt->th);
    return NULL;
}

//返回当前线程的timeouts信息结构指针
struct sys_timeouts *sys_arch_timeouts(void)
{
    cyg_handle_t ct;
    struct lwip_thread *t;

    ct = cyg_thread_self();
    for(t = threads; t; t = t->next)
        if (t->th == ct)
            return &(t->to);

    return &to;
}
--------------------------------------------------------------------------------
-----------

/include/net_lwip.h
--------------------------------------------------------------------------------
-----------
//sys_arch.c
#define CYGNUM_LWIP_VARMEMPOOL_SIZE       600*1024 //可变长内存池实体
#define CYGNUM_LWIP_APP_THREADS           15        //应用程序线程数
#define CYGNUM_LWIP_THREAD_STACK_SIZE     20*1024  //线程堆栈空间大小

/* ---------- 存储器选项 ---------- */
//MEM_ALIGNMENT:应被设置为被编译lwIP的目标CPU的字节排列。
//4字节排列--->MEM_ALIGNMENT=4
//2字节排列--->MEM_ALIGNMENT=2
#define CYGPKG_LWIP_MEM_ALIGNMENT         4         //?????

//MEM_SIZE:内存堆的大小。如果应用程序要发送许多需要内存拷贝的数据,这个值应被设
置得大些。
#define CYGPKG_LWIP_MEM_SIZE              600*1024    //400k

//MEMP_NUM_PBUF:memp结构的pbufs数目。如果应用程序要发送许多ROM类型(或者其他静态
内存)以外的数据,这个值应被设置得大些。
#define CYGPKG_LWIP_MEMP_NUM_PBUF         200

//MEMP_NUM_UDP_PCB:UDP协议控制块数目。每个活动的UDP“连接”对应一个。
#define CYGPKG_LWIP_MEMP_NUM_UDP_PCB      30

//MEMP_NUM_TCP_PCB:同时活动的TCP连接数
#define CYGPKG_LWIP_MEMP_NUM_TCP_PCB      30

//MEMP_NUM_TCP_PCB_LISTEN:监听TCP连接数
#define CYGPKG_LWIP_MEMP_NUM_TCP_PCB_LISTEN  40

//MEMP_NUM_TCP_SEG:同时排队TCP分段数
#define CYGPKG_LWIP_MEMP_NUM_TCP_SEG         300

//MEMP_NUM_SYS_TIMEOUT:同时活动超时数
#define CYGPKG_LWIP_MEMP_NUM_SYS_TIMEOUT     60

//以下四个选项只用于顺序API,如果应用程序只使用原始API,他们应被置0。

//MEMP_NUM_NETBUF:结构netbuf数
#define CYGPKG_LWIP_MEMP_NUM_NETBUF          200

//MEMP_NUM_NETCONN:结构netconn数
#define CYGPKG_LWIP_MEMP_NUM_NETCONN         200

//MEMP_NUM_APIMSG:结构api_msg数,用于在TCP/IP栈和顺序程序间通信。
#define CYGPKG_LWIP_MEMP_NUM_APIMSG          300

//MEMP_NUM_TCPIPMSG:结构tcpip_msg数,用于顺序API通信和收包。详见src/api/tcpip.c

#define CYGPKG_LWIP_MEMP_NUM_TCPIP_MSG       300

/* ---------- Pbuf 选项 ---------- */
//PBUF_POOL_SIZE:pbuf池中缓冲区个数
#define CYGPKG_LWIP_PBUF_POOL_SIZE           300

//PBUF_POOL_BUFSIZE:pbuf池中每个pbuf的大小
#define CYGPKG_LWIP_PBUF_POOL_BUFSIZE        1500

//PBUF_LINK_HLEN:应该分配给一个链路级包头的字节数
#define CYGPKG_LWIP_PBUF_LINK_HLEN           100

/* ---------- TCP 选项 ---------- */
#define CYGPKG_LWIP_TCP                      1
#define CYGPKG_LWIP_TCP_TTL                  255

//如果TCP需要排队顺序乱掉的分片那么置1。如果你的设备内存有限,那么定义为0。
#define CYGPKG_LWIP_TCP_QUEUE_OOSEQ          1

//TCP 最大分段长度
#define CYGPKG_LWIP_TCP_MSS                  1500

//TCP 发送器缓冲区空间(字节)
#define CYGPKG_LWIP_TCP_SND_BUF              1024*300  //设成20会造成链路断开,
可能CDMA对窗口内数据大小有限制。抑或是队列长度要大于50。

//TCP发送器缓冲区空间(pbufs)。这个值必须至少=2*TCP_SND_BUF/TCP_MSS以便正常工作。
实测最好保持2倍关系,不要大于2倍。怀疑注释有误,不是至少,是至多。
#define CYGPKG_LWIP_TCP_SND_QUEUELEN         1200

//TCP接收窗口
#define CYGPKG_LWIP_TCP_WND                  1024*300

//最大重发数据分段数
#define CYGPKG_LWIP_TCP_MAXRTX               3

//最大重发SYN段数
#define CYGPKG_LWIP_TCP_SYNMAXRTX            6

/* ---------- ARP 选项 ---------- */
#define CYGPKG_LWIP_ARP_TABLE_SIZE           10

/* ---------- IP 选项 ---------- */
//如果你希望拥有在多个网络接口间进行IP包转发的能力,那么定义IP_FORWARD为1。
//如果你要在只有一个网络接口的设备上运行lwIP的话,定义它为0。
#define CYGPKG_LWIP_IP_FORWARD               0

//如果为1,IP选项被允许(但不解析)。如果为0,所有带IP选项的包均被抛掉。
#define CYGPKG_LWIP_IP_OPTIONS               1

/* ---------- ICMP 选项 ---------- */
#define CYGPKG_LWIP_ICMP_TTL                 255

/* ---------- DHCP 选项 ---------- */
//如果你需要DHCP端口配置,那么定义LWIP_DHCP为1
#define CYGPKG_LWIP_DHCP                     0

//置1,如果你需要在给定地址上进行ARP检测(推荐)
#define CYGPKG_LWIP_DHCP_DOES_ARP_CHECK      1

/* ---------- UDP 选项 ---------- */
#define CYGPKG_LWIP_UDP                      1
#define CYGPKG_LWIP_UDP_TTL                  255

/* ---------- RAW 插口支持 ---------- */
#define CYGPKG_LWIP_RAW                      1

/* ---------- SLIP 选项 --------- */
//#define CYGPKG_LWIP_SLIP                     0
#define CYGPKG_LWIP_SLIP_DEV                 "/dev/ser1"

//#define CYGPKG_LWIP_LOOPIF                   0

/* ---------- PPP 选项 --------- */
#define CYGPKG_LWIP_PPP                      1
#define CYGPKG_LWIP_PPP_DEV                  "/dev/ser1"

#define CYGPKG_LWIP_PPP_PAP_AUTH             1

#define CYGPKG_LWIP_PPP_CHAP_AUTH            1

/* --------- 线程优先级 ----------*/
#define CYGPKG_LWIP_TCPIP_THREAD_PRIORITY    4
#define CYGPKG_LWIP_SLIPIF_THREAD_PRIORITY   8
#define CYGPKG_LWIP_PPP_THREAD_PRIORITY      4

/* ---------- 统计选项 ---------- */
#define CYGPKG_LWIP_STATS                    0

/* ---------- 调试选项 ---------- */
//#define CYGPKG_LWIP_ASSERTS                  0

//#define CYGPKG_LWIP_DEBUG                    0

//new add by yy
#define LWIP_PROVIDE_ERRNO

//arch/init.c
#define CYGPKG_LWIP_SERV_ADDR                192,168,0,1
#define CYGPKG_LWIP_MY_ADDR                  192,168,0,6
#define CYGPKG_LWIP_NETMASK                  255,255,255,0
--------------------------------------------------------------------------------
-----------

/netif/ppp/ppp.c
--------------------------------------------------------------------------------
-----------
......
while (lcp_phase[pd] != PHASE_DEAD) {
        if (pc->kill_link) {
            PPPDEBUG((LOG_DEBUG, "pppMainWakeup: unit %d kill_link ->
pppStopCB/n", pd));
        pc->errCode = PPPERR_USER;
        /* This will leave us at PHASE_DEAD. */
            tcpip_callback(pppStopCB, arg);
        pc->kill_link = 0;
        }
        else if (pc->sig_hup) {
            PPPDEBUG((LOG_DEBUG, "pppMainWakeup: unit %d sig_hup -> pppHupCB/n",
 pd));
        pc->sig_hup = 0;
            tcpip_callback(pppHupCB, arg);
        } else {
            if(!get_dcd_status()){
                printf("/n/n/nLost carrier......!!!/n/n/n");
                pppSigHUP(pd);  //载波丢失,关闭PPP。
                m_state=MDM_COMMAND;
                vbx_pppup = 0;
                continue;
            }
            //////c = sio_read(pc->fd, p->payload, p->len);//保留
            //c = sio_read(pc->fd, p->payload, 1);//本版本不支持串口非阻塞方式,
若没有读够指定长度字符数则不退出。因此挂起等1个字符。
            c=read_queue(p->payload);  //我自己设计的串口读API

            if(c > 0) {
                pppInProc(pd, p->payload, c);
            } else {
                PPPDEBUG((LOG_DEBUG, "pppMainWakeup: unit %d sio_read len=%d
returned %d/n", pd, p->len, c));
                sys_msleep(250); /* give other tasks a chance to run */
            }
        }
    }
......
--------------------------------------------------------------------------------
-----------

/include/arch/cc.h
--------------------------------------------------------------------------------
-----------
//确定大小端类型
#include <cyg/infra/cyg_type.h>

#if (CYG_BYTEORDER == CYG_LSBFIRST)
  #define BYTE_ORDER LITTLE_ENDIAN
#else
  #define BYTE_ORDER BIG_ENDIAN
#endif

//定义结构无关类型
typedef unsigned   char    u8_t;
typedef signed     char    s8_t;
typedef unsigned   short   u16_t;
typedef signed     short   s16_t;
typedef unsigned   long    u32_t;
typedef signed     long    s32_t;
typedef unsigned   long       mem_ptr_t;

//定义编译器字节对齐方式
#define PACK_STRUCT_FIELD(x) x __attribute__((packed))
#define PACK_STRUCT_STRUCT   __attribute__((packed))
#define PACK_STRUCT_BEGIN
#define PACK_STRUCT_END

//定义结构相关诊断打印输出
#include <cyg/infra/diag.h>
#include <cyg/infra/cyg_ass.h>
//#define LWIP_PLATFORM_DIAG(x)    {diag_printf x;}
#define LWIP_PLATFORM_DIAG(x)    {}
#define LWIP_PLATFORM_ASSERT(x) {CYG_FAIL(x);}

//定义保护模式
#define SYS_ARCH_DECL_PROTECT(x)
#define SYS_ARCH_PROTECT(x)
#define SYS_ARCH_UNPROTECT(x)
--------------------------------------------------------------------------------
-----------

/netif/ppp/modem.c
--------------------------------------------------------------------------------
-----------
int ppp_timeout_num;  //num个250ms没有收到串口数据
int ppp_drop_num;     //抛包数
long modem_disconnect_num;  //每次重连Modem,此变量都加1。

static char cmd_changecode[]={"+++"};  //+++是数据,不是命令
static char cmd_hangup[]={"ATH/r/n"};
static char cmd_init[]={"ATZE1V1/r/n"};
static char cmd_pickup[]={"ATH1/r/n"};
static char cmd_dial[]={"ATDT*99***1#/r/n"};
static char cmd_cdmadial[]={"ATDT#777/r/n"};
static char cmd_cdmainit[]={"AT+CRM=1;+CPS=33;+CMUX=1;+CTA=0/r/n"};

extern char vbx_g_ispnum[20];
extern unsigned char flag_watchdog;

#ifdef TEL_RING
static char tel_ring_flag = 0;
extern char vbx_g_ring;
#endif

ModemState m_state;  //Modem状态,命令和数据
MODEMqueue mqueue;
cyg_sem_t semq;

//#define STACK_SIZE 0x1000
static char stack[STACK_SIZE];
static cyg_thread thread_data;
static cyg_handle_t thread_handle;

void modem_init(void * ser)
{
    m_state=MDM_COMMAND;

    mqueue.num = mqueue.in = mqueue.out = 0;

    cyg_semaphore_init(&semq,1);         //创建信号量,初始为真。

    cyg_thread_create(5,                 // Priority - just a number
                      mgetty,            // entry
                      ser,               // entry parameter
                      "mgetty",          // Name
                      &stack[0],         // Stack
                      STACK_SIZE,        // Size
                      &thread_handle,    // Handle
                      &thread_data       // Thread data structure
            );
    cyg_thread_resume(thread_handle);  // Start it
}

void modem_driver(void * dev)
{
    int len;
    int i,t;
    char vbuf[50];

    ppp_timeout_num=0;
    ppp_drop_num=0;
    modem_disconnect_num=0;

    printf("/nmodem_driver/n");
    m_state=MDM_COMMAND;

#ifdef TEL_RING
    if(vbx_g_ring != 1){//未激活
        tel_ring_flag = 0 ;

        do{
            cyg_thread_delay(100);
            printf("Set DTR low...1/n");
            set_dtr(0);
            cyg_thread_delay(10);

            printf("Set DTR high.../n");
            set_dtr(1);

            cyg_thread_delay(10);
            len=sizeof(cmd_hangup)-1;
            sio_write(dev, cmd_hangup, len);
            cyg_thread_delay(10);
        }while(get_dcd_status());

        cyg_thread_delay(100);

        len=sizeof(cmd_init)-1;
        sio_write(dev, cmd_init, len);

        printf("Wait Ring.../n");
        while(tel_ring_flag == 0){
            flag_watchdog = 1;
            printf("*");
            cyg_thread_delay(100);
        }
        vbx_g_ring = 1;
        WriteVbxRing((unsigned char *)&vbx_g_ring);
    }
#endif

    t = 0;
modem_begin:
/*
    printf("Reset CDMA/n");
    reset_cdma();
    cyg_thread_delay(1000);
*/
    m_state=MDM_COMMAND;

    if(t >= 3){
        cyg_thread_delay(6000);
    }

    flag_watchdog = 1;

    do{
        cyg_thread_delay(100);
        printf("Set DTR low...2/n");
        set_dtr(0);
        cyg_thread_delay(10);

        printf("Set DTR high.../n");
        set_dtr(1);

        cyg_thread_delay(10);
        len=sizeof(cmd_hangup)-1;
        sio_write(dev, cmd_hangup, len);
        cyg_thread_delay(10);
    }while(get_dcd_status());

    flag_watchdog = 1;
    cyg_thread_delay(100);
//------------------------------------------------------------------------------
------------------
    // 1秒 +++ 1秒   换码序列
    //ATH(0)挂机
    //初始化"ATZE0&C1V1M0&D0&S0&K3"
    //ATH1摘机
//------------------------------------------------------------------------------
------------------
    //初始化"ATZE0&C1V1M0&D0&S0&K3"
    //Z复位、恢复保存的缺省文件,E0关闭回显。
    //&C1检测到载波时DCD才有效,V1结果码以字符形式显示
    //M0关闭扬声器,&D0忽略DTR信号,&S0 DSR一直有效
    //&K3允许RTS/CTS流控
    //CGCLASS+CGDCONT+CSQ+CGREG+CGATT+CGACT+IPR已经预先写在SIM卡里了。ATZ就可以
恢复卡里的配置。
    len=sizeof(cmd_init)-1;
    printf("/nATZE1V1//r//n(%d)/n",len);
    sio_write(dev, cmd_init, len);

    cyg_thread_delay(100);

    //台湾CDMA初始化
    //len=sizeof(cmd_cdmainit)-1;
    //printf("/nAT+CRM=1;+CPS=33;+CMUX=1;+CTA=0//r//n(%d)/n",len);
    //sio_write(dev, cmd_cdmainit, len);
    //cyg_thread_delay(500);

#if MODEM_CDMA
    //ATDT#777音频拨号,返回CONNECT。
    /*
    len=sizeof(cmd_cdmadial)-1;
    printf("/nATDT#777//r//n(%d)/n",len);
    sio_write(dev, cmd_cdmadial, len);
    */
    sprintf(vbuf,"ATDT%s/r/n",vbx_g_ispnum);
    len = strlen(vbuf);
    sio_write(dev, vbuf, len);
    printf("/n%s/n",vbuf);
#else  //GPRS
    //ATDT*99***1#音频拨号,返回CONNECT。

    len=sizeof(cmd_dial)-1;
    printf("/nATDT*99***1#//r//n(%d)/n",len);
    sio_write(dev, cmd_dial, len);
#endif

    cyg_thread_delay(200);///////////////////////

    flag_watchdog = 1;

    for(i=0;i<5;i++){
        if(get_dcd_status()){
            printf("Detect carrier!.../n");
            break;
        }
        cyg_thread_delay(50);
    }

    if(i >= 5){
        printf("Redail....../n");
        t++;
        goto modem_begin;
    }

    m_state=MDM_DATA;
}

void mgetty(void * dev)
{
    unsigned char c,len;
#ifdef TEL_RING
    int i;
    char buf[100];

    i = 0 ;
#endif

    do{
        len=sio_read(dev,&c,1);
        if(len>0){
            if(m_state==MDM_DATA)
                write_queue(c);
            else{
                //processcmd;
                printf("%c",c);
#ifdef TEL_RING
    if(vbx_g_ring != 1){//未激活
        if(i < 100 && c != '/n'){
            buf[i++] = c ;
        }
        else{
            i = 0;
            buf[4] = '/0' ;
            if(0 == strcmp(buf,"RING")){
                tel_ring_flag = 1 ;
            }
        }
    }
#endif
    }
        }
        else
            cyg_thread_delay(25);
    }while(1);
}

void write_queue(unsigned char c)
{
    cyg_semaphore_wait(&semq);

    //mqueue.buf[mqueue.num++]=c;
    if(mqueue.num < MAX_MODEM_QUEUE){
        mqueue.buf[mqueue.in++]=c;
        if(mqueue.in >= MAX_MODEM_QUEUE)
            mqueue.in = 0 ;
        mqueue.num++;
    }

    cyg_semaphore_post(&semq);
}

int read_queue(unsigned char * buf)
{
    int i;

    cyg_semaphore_wait(&semq);

    /*for(i=0;i<mqueue.num;i++)
    for(i=0;i<mqueue.num && i<1024;i++)
        *buf++=mqueue.buf[i];

    mqueue.num=0;
    */
    for(i=0;i < mqueue.num && i < 1024;i++){
        *buf++ = mqueue.buf[mqueue.out++];
        if(mqueue.out >= MAX_MODEM_QUEUE)
            mqueue.out = 0;
    }
    mqueue.num = mqueue.num - i;

    cyg_semaphore_post(&semq);

    return i;
}


//------------------------------------------------
bool get_dcd_status(void)  //DCD为0时有效
{
    unsigned short Control;
    unsigned char  Data;

     HAL_READ_UINT16( CYGARC_REG_PDCR   , Control );
     Control = ( Control & 0xffcf ) | 0x0020;
     HAL_WRITE_UINT16( CYGARC_REG_PDCR, Control );
     HAL_READ_UINT8( CYGARC_REG_PDDR, Data );
     Data = Data & 0x04;
    if(Data == 0)  return true;
    else  return false;
}


void reset_cdma(void)
void set_dtr(unsigned char Level)
--------------------------------------------------------------------------------
-----------

    -----------
    | PPP测试 |
    -----------
    刚开始测试时最好使用NULL-MODEM线连接开发板和Linux系统,使用本地测试,节省费
用。
    NULL-MODEM接线方式、Linux下PPP server配置(略)

    NULL-MODEM PAP 测试
    NULL-MODEM CHAP 测试

    GPRS PAP 测试
    GPRS CHAP 测试

    CDMA PAP 测试
    CDMA CHAP 测试

    -----------
    | DNS实现 |
    -----------
    DNS遵循RFC1034/1035协议

    按照协议要求组成一个请求包,然后解析应答包内容就可以从域名得到IP地址。下面有
一个示例,一般在单片机里实现的DNS就是指域名到IP转换,不必实现完整协议。在ECOS或
LINUX里有源码。

    因为对外的IP地址可能动态变化,所以最好用域名,方便用户访问。
    可以使用代理网关,或者ADSL,或CDMA等连接公网,关键是得到公网IP才能被访问,否
则只能发起主动连接。当得到动态公网IP时,就刷新DNS,用户用DNS就能间接访问到WEB。

www.163.com域名解析实例(2005/03/08 12:45 test by YangYi)

dns send 29 bytes:0,0,1,0,0,1,0,0,0,0,0,0,3,77,77,77,3,31,36,33,3,63,6f,6d,0,0,
1,0,1

dns recv 230 bytes:0,0,81,80,0,1,0,6,0,2,0,2,3,77,77,77,3,31,36,33,3,63,6f,6d,0,
0,1,0,1,c0,c,0,5,0,1,0,0,3a,c9,0,1d,3,77,77,77,5,63,61,63,68,65,5,73,70,6c,69,74
,7,6e,65,74,65,61,73,65,3,63,6f,6d,0,c0,29,0,1,0,1,0,0,8,60,0,4,ca,6a,a8,6d,c0,2
9,0,1,0,1,0,0,8,60,0,4,ca,6a,a8,79,c0,29,0,1,0,1,0,0,8,60,0,4,ca,6c,24,99,c0,29,
0,1,0,1,0,0,8,60,0,4,ca,6a,a8,67,c0,29,0,1,0,1,0,0,8,60,0,4,ca,6a,a8,68,c0,33,0,
2,0,1,0,0,40,a0,0,c,9,6e,73,2d,73,70,6c,69,74,32,c0,39,c0,33,0,2,0,1,0,0,40,a0,0
,c,9,6e,73,2d,73,70,6c,69,74,31,c0,39,c0,a2,0,1,0,1,0,0,3a,be,0,4,ca,6a,ba,7d,c0
,ba,0,1,0,1,0,0,3a,b0,0,4,ca,6a,a8,4f

PASS:<www.cache.split.netease.com. is 202.106.168.109>

name = "www.163.com" <-----> IP = 202.106.168.109

    --------------------------
    | 《ecos增值包》二次开发 |
    --------------------------
    《ecos增值包》提供了lwip的运行平台库和调试开发环境,包括串口驱动、线程、内存
管理、信号量、时间管理等丰富的API函数。
    在ecos平台上移植lwip的感觉是非常方便,不用改造现有的API函数,该有的功能都有
,可以专注于移植工作。
    ecos应用程序可以直接访问硬件,这使得用户绕过程序库移植CF卡、SD卡、USB等驱动
到ecos上成为可能,同时还可以借助ecos现成的功能简化设计,实在是一个非常好的快速验
证平台和二次开发平台。
--

※ 来源:·水木社区 http://newsmth.net·[FROM: 61.149.56.*] 

你可能感兴趣的:(lwip之ppp在EasyARM2200和SmartARM2200上的移植zz)