目的
AP/BP通过shared memory上网, shared memory虚拟成多个tty端口,通过其中一个tty端口上网,
上网端口的行规设置为 N_PPP,在kernel一直没有找到哪里设置了这个行规。在哪里设置的哪?从系统调用的接口开始跟踪:看那个进程调用了这只行规的函数。
long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg){
case TIOCSETD:
return tiocsetd(tty, p);
}
static int tiocsetd(struct tty_struct *tty, int __user *p)
{
int ldisc;
int ret;
if (get_user(ldisc, p))
return -EFAULT;
pr_err("tiocsetd: where set N_PPP=3 from %d to %d\n", tty->ldisc->ops->num, ldisc);
BUG_ON(1);
ret = tty_set_ldisc(tty, ldisc);
return ret;
}
/************************************************************************/
通过上面的bug_on可以找到当前的进程:pppd
pppd应用程序是怎样运行的?
从android/device/xxx/yyy/init.rc中可以找到
#telink add for pppd
chown root shell /system/xbin/ppp_gprs0.sh
chown root shell /system/xbin/ppp_gprs1.sh
chmod 0777 /system/xbin/ppp_gprs0.sh
chmod 0777 /system/xbin/ppp_gprs1.sh
service pppd-ppp0 /system/xbin/ppp_gprs0.sh
class main
user root
group radio cache inet misc audio sdcard_rw log
disabled
oneshot
service pppd-ppp1 /system/xbin/ppp_gprs1.sh
class main
user root
group radio cache inet misc audio sdcard_rw log
disabled
oneshot
可以找到:/common/pre-built/tl7689/ril/ppp_gprs0.sh
ppp_gprs0.sh的内容为:
#!/system/bin/shUSER0=`/system/bin/getprop net.gprs.user0`
PASSWORD0=`/system/bin/getprop net.gprs.password0`
DEVICE0=`/system/bin/getprop net.gprs.device0`
#USER0='/system/bin/getprop net.gprs.user0'
#PASSWORD0='/system/bin/getprop net.gprs.password0'
#DEVICE0='/system/bin/getprop net.gprs.device0'
#/system/bin/setprop net.gprs.ppp-exit ""
/system/bin/pppd $DEVICE0 115200 crtscts noauth debug nodetach usepeerdns noipdefault defaultroute user $USER0 password $PASSWORD0 ipcp-accept-local ipcp-accept-remote linkname ppp0
#/system/bin/pppd -detach $DEVICE 115200 noipdefault noauth debug novj usepeerdns noccp ipcp-no-addresses kdebug 4 defaultroute user $USER password $PASSWORD
#/system/bin/setprop net.gprs.ppp-exit $?
#exit $?
# $DEVICE0等是函数的参数,它是通过getprop net.gprs.device0得到的;
下面搜索net.gprs.device0:
property_set("net.gprs.device0","/dev/ttyN1");void requestSetupDefaultPDP(void *data, size_t datalen, RIL_Token t)
{
const char *apn, *user, *pass;
char *auth;
apn = ((const char **) data)[2];
user = ((const char **) data)[3];
pass = ((const char **) data)[4];
auth = strdup(((const char **) data)[5]);
//type = getNWType(((const char **) data)[6]);
type = strdup(((const char **) data)[6]);
if(type == NULL) type = "IP";
if(ppp_connection[0].isConnected == 0){
LOGD("use ppp0 ===pppd service===");
connect_cid = 1;
strcpy(type_0,type);
property_set("net.gprs.device0","/dev/ttyN1");
if(user != NULL && pass != NULL){
property_set("net.gprs.user0", user);
property_set("net.gprs.password0",pass);
}
}else if(ppp_connection[0].isConnected != 0 && ppp_connection[1].isConnected == 0){
LOGD("use ppp1 ===pppd service===");
connect_cid = 2;
strcpy(type_1,type);
property_set("net.gprs.device1","/dev/ttyN4");
if(user != NULL && pass != NULL){
property_set("net.gprs.user1", user);
property_set("net.gprs.password1",pass);
}
}
}
哪里启动了这个server?
service pppd-ppp0 /system/xbin/ppp_gprs0.shvoid *startPpp0Attach(void *param){
property_set("ctl.start", "pppd-ppp0");
}
/*
*蓝牙模块中一个比较核心的文件是bluetooth.c, 在我们上电的时候, 会调用这个文件中bt_enable()这个函数, 在这个函数里面*先调用set_bluetooth_power()上电,然后调用property_set("ctl.start", "hciattach"), 去启动hciattach这个服务,从而*运行brcm_patchram_plus这个进程。这个服务会加载我们firmware等一些工作。这部分工作做完后, 我们会调用property_set* *("ctl.start","bluetoothd"),这个服务是启动我们的bluz进程。如果以上成功的话,蓝牙芯片将会开始工作。
*/
哪里设置了ttyNx discipline?
/*********************************************************************
* tty_establish_ppp - Turn the serial port into a ppp interface.
*/
int tty_establish_ppp (int tty_fd)
{
int ret_fd;
ioctl(tty_fd, TIOCSETD, &ppp_disc);
generic_establish_ppp(tty_fd);
}
/********************************************************************
* 哪里设置了tty_fd? 我们可以从调用函数的地方入手
*/
void
link_required(unit)
int unit;
{
new_phase(PHASE_SERIALCONN);
/*devfd来自connect函数*/
devfd = the_channel->connect();
if (devfd < 0)
goto fail;
/* set up the serial device as a ppp interface */
/*
* N.B. we used to do tdb_writelock/tdb_writeunlock around this
* (from establish_ppp to set_ifunit). However, we won't be
* doing the set_ifunit in multilink mode, which is the only time
* we need the atomicity that the tdb_writelock/tdb_writeunlock
* gives us. Thus we don't need the tdb_writelock/tdb_writeunlock.
*/
fd_ppp = the_channel->establish_ppp(devfd);
}
the_channel来自哪里?
void tty_init()
{
add_notifier(&pidchange, maybe_relock, 0);
the_channel = &tty_channel;
xmit_accm[3] = 0x60000000;
}
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 - 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()
{
if (devnam[0] != 0) {
for (;;) {
real_ttyfd = open(devnam, O_NONBLOCK | O_RDWR, 0);
if (real_ttyfd >= 0)
break;
}
ttyfd = real_ttyfd;
/*
* Set line speed, flow control, etc.
* If we have a non-null connection or initializer script,
* on most systems we set CLOCAL for now so that we can talk
* to the modem before carrier comes up. But this has the
* side effect that we might miss it if CD drops before we
* get to clear CLOCAL below. On systems where we can talk
* successfully to the modem with CLOCAL clear and CD down,
* we could clear CLOCAL at this point.
*/
set_up_tty(ttyfd, ((connector != NULL && connector[0] != 0)
|| initializer != NULL));
}
}
devnam来自哪里
devnam是个全局变量,哪里赋的值?
pppd入口
int
main(argc, argv)
int argc;
char *argv[];
{
/*
* Initialize each protocol.
*/
for (i = 0; (protp = protocols[i]) != NULL; ++i)
(*protp->init)(0);
/*
* Initialize the default channel.
*/
tty_init();
parse_args(argc-1, argv+1);
lcp_open(0); /* Start protocol */
}
分析程序的输入参数
/*
* parse_args - parse a string of arguments from the command line.*/
int
parse_args(argc, argv)
int argc;
char **argv;
{
char *arg;
option_t *opt;
opt = find_option(arg);
n = n_arguments(opt);
process_option(opt, arg, argv);
}
/* option descriptors */
option_t tty_options[] = {
/* device name must be first, or change connect_tty() below! */
{ "device name", o_wild, (void *) &setdevname,
"Serial port device name",
OPT_DEVNAM | OPT_PRIVFIX | OPT_NOARG | OPT_A2STRVAL | OPT_STATIC,
devnam},
}
/*
* setdevname - Set the device name.
* If doit is 0, the call is to check whether this option is
* potentially a device name.
*/
static int
setdevname(cp, argv, doit)
char *cp;
char **argv;
int doit;
{
if (doit) {
strlcpy(devnam, cp, sizeof(devnam));
devstat = statbuf;
default_device = 0;
}
return 1;
}
总结
用过建立上网通道的代码分为两部分 ril/ pppd:
android/hardware/ril:
android/external/ppp:
ril会告诉pppd使用那个通过上网,并启动 pppd,而pppd会进而设置tty的行规。