基于嵌入式Linux 的移动终端的软件设计
3.4 GPRS 通信程序
3.4.1 拨号到 GPRS 网络的基本原理
移动终端要想通过 GPRS 通信模块访问 Internet,首先得附着在 GPRS 网络上,然后发起 PDP(Packet Data Protocol,分组数据协议)上下文激活过程[4],如图 4 所示。只有通过此过程,GPRS 通信模块才能与 GGSN 建立一条逻辑通路,从而访问 Internet。
图 4 PDP 上下文激活过程示意图
在嵌入式 Linux 系统平台下,移动终端利用 pppd(包含 chat)拨号到 GPRS 网络。pppd 是一个用户空间的后台服务进程(daemon),而 chat 是 pppd 所带一个辅助工具,用来与 GPRS 通信模块建立会话。在 PDP 上下文激活过程中,chat 完成了第 ① 步,而 pppd 完成了第 ②、③、④、⑩ 步。pppd 拨号程序的层次结构如图 5 所示。
图 5 pppd 拨号程序的层次结构
其中,N_PPP 层就是 PPP 协议层。PPP 协议模块不仅提供简单的数据链路层功能,它还提供诸如鉴权(如PAP/CHAP),数据压缩/解压(如CCP)和数据加密/解密(如ECP)等扩展功能。由于 GPRS 通信程序要求透明化地使用这些扩展功能,而 PPP 协议模块本身无法对各种策略进行选择,于是 pppd 应运而生。PPP 协议模块中策略性的内容都移到了 pppd 中,由 pppd 完成对鉴权、压缩/解压和加密/解密等扩展功能的选用。
在运行 pppd 的时候,pppd 首先读取配置文件中的配置信息,其中包含了设置 PPP 协议模块的参数、GPRS 通信模块连接的端口(/dev/ttyS1)以及对 chat 进行调用的语句,等等。随后 pppd 调用 chat,chat 也会读取相应的配置文件(其中包含一些应答语句对和 AT 命令),然后使用默认的行规程 N_TTY 向 GPRS 通信模块发送 AT 命令,接着 chat 将控制权返还给 pppd。pppd 将行规程切换为 N_PPP,而 pppd 与 PPP 协议模块之间采用了设备文件来进行通信,设备文件名是 /dev/ppp。通过 read 系统调用,pppd 可以读取 PPP 协议模块的数据包(当然,PPP 协议模块只会把应该由 pppd 处理的数据包发给 pppd)。通过 write 系统调用,pppd 可以把要发送的数据包传递给 PPP 协议模块,而通过 ioctl 系统调用,pppd 可以设置 PPP 协议模块的参数,可以建立/关闭连接。
此后,pppd 执行了 PDP 上下文激活过程的第 ②、③、④ 步。等 PDP 上下文激活过程的第 ⑤-⑨ 步(与移动终端不直接相关)完成之后,pppd 执行第 ⑩ 步,在函数 make_ppp_unit( ) 中调用 ioctl(PPPIOCNEWUNIT) 创建一个网络接口(如ppp0)。当 PPP 协议模块在处理 PPPIOCNEWUNIT 时,调用函数 register_netdev( ) 向内核注册 PPP 网络接口,该网络接口的传输函数指向函数 ppp_start_xmit( )。值得注意的一点是,如果关闭进程 pppd,行规程会由 N_PPP 切换回默认的 N_TTY,因此,在移动终端与监控中心通信的过程中不能关闭 pppd 进程。
至此,移动终端完成了向 GPRS 网络的拨号,这样它就拥有了一个可以用于与监控中心进行通信的网络接口(如ppp0)。
3.4.3 移动终端与监控中心的数据交互
前面,移动终端已经与监控中心建立了网络链接。接下来,移动终端就可以与监控中心进行通信了。GPRS 通信程序的层次结构如图 1 的右半部分所示。
在移动终端向监控中心发送定位信息的过程中,移动终端上的 GPRS 通信程序通过 socket 接口发送 TCP/IP 数据包,内核根据 IP 地址和路由表,找到 PPP 网络接口,然后调用函数 ppp_start_xmit( ),此时控制权就转移到了 PPP 协议模块。函数 ppp_start_xmit( ) 调用函数 ppp_xmit_process( ) 去发送队列中的所有数据包,而函数 ppp_xmit_process( ) 会进一步调用函数 ppp_send_frame( ) 去发送单个数据包。函数 ppp_send_frame( ) 根据前面 pppd 对 PPP 协议模块的设置调用压缩等扩展功能之后,又经函数 ppp_push( ) 调用函数 pch->chan->ops->start_xmit( ) 发送数据包。函数 pch->chan->ops->start_xmit( ) 是具体的传输方式,对于串口发送方式,则是 ppp_async.c:ppp_asynctty_open 中注册的函数 ppp_async_send( ),函数 ppp_async_send( ) 经函数 ppp_async_push( ) 调用函数 tty->driver->write( )(定义在低层驱动程序中)把数据发送到串口 2(GPRS 通信模块接在串口 2 上)。
ppp_async.c 在初始化时(ppp_async_init),调用函数 tty_register_ldisc( ) 向 tty 注册了行规程 N_PPP 的处理接口,也就是一组回调函数。在移动终端接收监控中心指令的过程中,当 GPRS 通信模块收到数据时,就会回调 N_PPP 行规程中的函数 ppp_asynctty_receive( ) 来接收数据。函数 ppp_asynctty_receive( ) 调用函数 ppp_async_input( ) 把数据 buffer 转换成 sk_buff,并放入接收队列 ap->rqueue 中。ppp_async 另外有一个 tasklet(ppp_async_process)专门处理接收队列 ap->rqueue 中的数据包,ppp_async_process 一直挂在接收队列 ap->rqueue 上,一旦被唤醒,它就调用函数 ppp_input( ) 让 PPP 协议模块处理该数据包。在函数 ppp_input( ) 中,数据被分成两路,一路是协议控制数据包,放入队列 pch->file.rqb 中,交给 pppd 处理。另外一路是用户数据包,经函数 ppp_do_recv( )、ppp_receive_frame( ) 进行 PPP 协议相关的处理后,再由函数 netif_rx( ) 提交给上层的 TCP/IP 协议模块进行处理,最后经 socket 接口传递给应用层的 GPRS 通信程序。
4 总结
近几年,智能交通系统(包括车辆监控系统)发展非常迅速,因此,移动终端将会有非常广泛的应用前景。随着市场需求的不断扩大,更加丰富的功能将会被集成到移动终端上,而嵌入式 Linux 系统凭借其自身的优势将会被越来越多地应用到这个领域。
本文作者创新点:本方案充分利用了嵌入式 Linux 平台所提供的系统功能,大大简化了应用程序的开发,并且具有良好的可扩展性。在详细介绍 GPS 信号的接收过程并给出一个简洁的 GPS 数据处理办法后,本文提供了一种切实可行的拨号到 GPRS 网络的方法,从而实现了移动终端跨越 GPRS 网络与 Internet 上监控中心的通信。
linux操作系统文章专题:linux操作系统详解(linux不再难懂)
评论