发信人: 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.*]