linux串口热插拔,USB 3G卡热插拔那些事4------pppd

在上一节中我们知道3G卡设备驱动已经加载好了,并且和ttyUSB*已经绑定成功,意味着我们可以拨号了,和3G卡内部3G模块通信了,而我们知道3G模块通信是tty设备,通过串行设备,这里和3G卡的两种工作模式相吻合---moden模式.

首先我们这里先给出3G工作原理图

我们万事俱备只欠东风了,就是pppd拨号了,当然pppd的源码自己可以去网上下载最新的,自己编译(这里我们只说linux环境的).安装好pppd程序后,我们运行pppd拨号,当然在拨号前我们需要配置下我们的pppd才能完美的工作,才能真正为我们对于的tty驱动和3g驱动运作起来.

主要看pppd运行参数文件/etc/ppp/options(这个参数文件名字,看开发者自己是可以修改的)该文件指定pppd运行的参数,若运行pppd时通过命令行指定参数同时出现时,则选择/etc/ppp/options中的配置.下面我们就看相关的参数:

#tty Options

/dev/ttyS1               #这里可以将/dev/ttyS1注释,然后使用命令行pppd /dev/ttyS1 运行来指定;

115200                   #传输速率

lock                     #创建一个锁定文件,其他程序在发现存在这个文件后,就能得知相应的串口已经被使用。

modem                    #modem设备

crtscts                  #硬件流控为,无硬件流控为nocrtscts

#login

nodetach                 #若指定updetach则拨号成功后放入后台运行,若为nodetach,则在前台执行

debug                    #如果需要加入调试信息,就加入参数debug

#auth

require-pap

refuse-chap

name na_admin

connect "/usr/sbin/chat -v -E -V -f/etc/ppp/pstn-start-chat"

disconnect 'chat -s -v -f disconnect-chat'

...

当然这些信息我们都可以从pppd源码包的帮助文件中获取信息,比如readme等.

# 源码ppp-2.4.5/pppd/tty.c中的函数connect_tty()将会执行这个脚本。主要是对客户端的拨号的应答。

connect "/usr/sbin/chat -v -E -V -f /etc/ppp/pstn-start-chat"

配置信息中我只说明/dev/ttyS1 这个tty名字是我们自己设定,看我们3g驱动和哪一个tty设备绑定到哪一个作为通信用.电信或者联通(默认华为3G卡,evdo)则为ttyUSB0,因为我们在3G卡驱动识别过程中会见到3g  attach ttyUSB*等信息. 还有就是在pppd运行开始,要初始化tty. 就是调用tty_init().

下面给出pppd相关代码:

/*

* Initialize each protocol.

*/

for (i = 0; (protp = protocols[i]) != NULL; ++i)

(*protp->init)(0);

/*

* Initialize the default channel.

*/

tty_init();

展开tty_init():

void tty_init()

{

add_notifier(&pidchange, maybe_relock, 0);

the_channel = &tty_channel;

xmit_accm[3] = 0x60000000;

}

我们看到里面the_channel = &tty_channel;的操作,struct channel *the_channel;(main.c中)是一个全局变量.

/*

* This struct contains pointers to a set of procedures for

* doing operations on a "channel".  A channel provides a way

* to send and receive PPP packets - the canonical example is

* a serial port device in PPP line discipline (or equivalently

* with PPP STREAMS modules pushed onto it).

*/

struct channel {

/* set of options for this channel */

option_t *options;

/* find and process a per-channel options file */

void (*process_extra_options) __P((void));

/* check all the options that have been given */

void (*check_options) __P((void));

/* get the channel ready to do PPP, return a file descriptor */

int  (*connect) __P((void));

/* we're finished with the channel */

void (*disconnect) __P((void));

/* put the channel into PPP `mode' */

int  (*establish_ppp) __P((int));

/* take the channel out of PPP `mode', restore loopback if demand */

void (*disestablish_ppp) __P((int));

/* set the transmit-side PPP parameters of the channel */

void (*send_config) __P((int, u_int32_t, int, int));

/* set the receive-side PPP parameters of the channel */

void (*recv_config) __P((int, u_int32_t, int, int));

/* cleanup on error or normal exit */

void (*cleanup) __P((void));

/* close the device, called in children after fork */

void (*close) __P((void));

};

extern struct channel *the_channel;

在ppp.h中定义,并声明.

下面看看如何初始化的

struct channel tty_channel = {

tty_options,

&tty_process_extra_options,

&tty_check_options,

&connect_tty,

&disconnect_tty,

&tty_establish_ppp,

&tty_disestablish_ppp,

&tty_do_send_config,

&tty_recv_config,

&cleanup_tty,

&tty_close_fds

};

同理这里我们只关注&connect_tty, 这个初始化在tty.c中.

/*

* connect_tty - get the serial port ready to start doing PPP.

* That is, open the serial port, set its speed and mode, and run

* the connector and/or welcomer.

*/

int connect_tty()

{

char *connector;

int fdflags;

#ifndef __linux__

struct stat statbuf;

#endif

char numbuf[16];

/*

* Get a pty master/slave pair if the pty, notty, socket,

* or record options were specified.

*/

strlcpy(ppp_devnam, devnam, sizeof(ppp_devnam));

pty_master = -1;

pty_slave = -1;

real_ttyfd = -1;

if (using_pty || record_file != NULL) {

if (!get_pty(&pty_master, &pty_slave, ppp_devnam, uid)) {

error("Couldn't allocate pseudo-tty");

status = EXIT_FATAL_ERROR;

return -1;

}

set_up_tty(pty_slave, 1);

}

/*

* Lock the device if we've been asked to.

*/

status = EXIT_LOCK_FAILED;

if (lockflag && !privopen) {

if (lock(devnam) < 0)

goto errret;

locked = 1;

}

/*

* Open the serial device and set it up to be the ppp interface.

* First we open it in non-blocking mode so we can set the

* various termios flags appropriately.  If we aren't dialling

* out and we want to use the modem lines, we reopen it later

* in order to wait for the carrier detect signal from the modem.

*/

got_sigterm = 0;

connector = doing_callback? callback_script: connect_script;

if (devnam[0] != 0) {

for (;;) {

/* If the user specified the device name, become the

user before opening it. */

int err, prio;

...

这里我们要特别主要粗体注释部分.  就是在这里我们的ppp和tty联系了起来,才真正work. 当然还加载了前面的脚本文件,来获取必要的配置信息.

tty_init()完成后,就是来和ppp内核协议通信来建立必要的ppp网络接口,以供上层应用程序用

/*

* If we're doing dial-on-demand, set up the interface now.

*/

if (demand) {

/*

* Open the loopback channel and set it up to be the ppp interface.

*/

fd_loop = open_ppp_loopback();

set_ifunit(1);

/*

* Configure the interface and mark it up, etc.

*/

demand_conf();

}

在open_ppp_loopback()这个函数中调用 make_ppp_unit()建立ppp0接口(当然这个接口名我们也可以自定义)

/*

* make_ppp_unit - make a new ppp unit for ppp_dev_fd.

* Assumes new_style_driver.

*/

static int make_ppp_unit()

{

int x, flags;

if (ppp_dev_fd >= 0) {

dbglog("in make_ppp_unit, already had /dev/ppp open?");

close(ppp_dev_fd);

}

ppp_dev_fd = open("/dev/ppp", O_RDWR);

if (ppp_dev_fd < 0)

fatal("Couldn't open /dev/ppp: %m");

flags = fcntl(ppp_dev_fd, F_GETFL);

if (flags == -1

|| fcntl(ppp_dev_fd, F_SETFL, flags | O_NONBLOCK) == -1)

warn("Couldn't set /dev/ppp to nonblock: %m");

ifunit = req_unit;

x = ioctl(ppp_dev_fd, PPPIOCNEWUNIT, &ifunit);

if (x < 0 && req_unit >= 0 && errno == EEXIST) {

warn("Couldn't allocate PPP unit %d as it is already in use", req_unit);

ifunit = -1;

x = ioctl(ppp_dev_fd, PPPIOCNEWUNIT, &ifunit);

}

if (x < 0)

error("Couldn't create new ppp unit: %m");

return x;

}

这里我们看到open函数打开了/dev/ppp ,然后发送一个ioctl给ppp协议

在ppp协议里

static struct file_operations ppp_device_fops = {

.owner= THIS_MODULE,

.read= ppp_read,

.write= ppp_write,

.poll= ppp_poll,

.ioctl= ppp_ioctl,

.open= ppp_open,

.release= ppp_release

};

ppp_ioctl调用ppp_unattached_ioctl(),在这个函数里

switch (cmd) {

case PPPIOCNEWUNIT:

/* Create a new ppp unit */

if (copy_from_user(&req, p, sizeof req))

break;

ppp = ppp_create_interface(req.unit, &err, req.ifname);

ppp_create_interface来真正创建这个接口,以供将来应用程序使用.

既然tty和ppp都初始化了,也建立了某种联系,并且能一起协同工作了,那么我们还犹豫什么?

当然是start_link了

start_link(0);

在这个函数里我们才真正调用我们上面长篇大论的connect_tty().

建立连接的过程分两个阶段:LCP,PCAP.

pppd建立连接过程中chat辅助程序起到不可磨灭的作用.

pppd运行后作为一个守护进程来运行,它必然有监控程序,而这个就是handle_events.

有人认为我这里说这么多是扯淡,我感觉不是,为什么呢,虽然我们一般人直接用pppd拨号就可以,但是很多人并不知道为什么,为什么就把pppd、ppp协议、tty、3G模块联系起来的.这里分析下我想功夫不会是白费的.如果一旦出现问题,那么我们很容易定位问题的所在,知其然还要知其所以然.以后我们应该讲讲ppp相关的数据包收发流程.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值