Android的RIL机制中的 reference-ril.c 即为厂商提供的驱动接口,这个驱动源码各个厂商都是有提供的,网上也有下载,我现在用的就是huawei wcdma的,最后编译成libreference-ril.so,关于这个接口驱动在RIL中所扮演的角色可参考 Android——RIL 机制源码分析
android 4.2自带pppd源码在/external/ppp/pppd中.同样,kernel中也是需要打开对point-to-point 的支持,在network support里面.
撰写不易,转载请注明出处:http://blog.csdn.net/jscese/article/details/40340665
这个就是在reference-ril.c 中的数据流量的request,上层的接口通过onRequest 的RIL_REQUEST_SETUP_DATA_CALL请求。这些在上面说到的源码分析里有详细分析,这里就只从拨号连接分析。
static void requestSetupDataCall(void *data, size_t datalen, RIL_Token t) { const char *apn; char *cmd; int err; ATResponse *p_response = NULL; char ppp_dnses[(PROPERTY_VALUE_MAX * 2) + 3] = {'\0'}; //初始化属性数组,用于临时存储拨号的属性变量 char ppp_local_ip[PROPERTY_VALUE_MAX] = {'\0'}; char ril_pppd_tty[PROPERTY_VALUE_MAX] = {'\0'}; char ppp_dns1[PROPERTY_VALUE_MAX] = {'\0'}; char ppp_dns2[PROPERTY_VALUE_MAX] = {'\0'}; char ppp_gw[PROPERTY_VALUE_MAX] = {'\0'}; char exit_code[PROPERTY_VALUE_MAX] = {'\0'}; int n = 1; RIL_Data_Call_Response_v6 *responses = alloca(n * sizeof(RIL_Data_Call_Response_v6)); //使用的是ipv6 apn = ((const char **)data)[2]; //取传进来的接入点 apn /* ALOGD("jscese display in reference APN == '%s' \n",apn); apn ="3gnet";*/ ALOGD("[%s] jscese display in reference APN '%s' ", __func__, apn); #ifdef USE_TI_COMMANDS // Config for multislot class 10 (probably default anyway eh?) err = at_send_command("AT%CPRIM=\"GMM\",\"CONFIG MULTISLOT_CLASS=<10>\"", NULL); err = at_send_command("AT%DATA=2,\"UART\",1,,\"SER\",\"UART\",0", NULL); #endif /* USE_TI_COMMANDS */ int fd, qmistatus; size_t cur = 0; size_t len; ssize_t written, rlen; char status[32] = {0}; int retry = 10; const char *pdp_type; ALOGD("requesting data connection to APN '%s'", apn); pdp_type = "IP"; // jscese add for dial asprintf(&cmd, "AT+CGDCONT=1,\"%s\",\"%s\",,0,0", pdp_type, apn); //FIXME check for error here err = at_send_command(cmd, NULL); //发送接入点的AT指令 free(cmd); #if 0 // Set required QoS params to default err = at_send_command("AT+CGQREQ=1", NULL); // Set minimum QoS params to default err = at_send_command("AT+CGQMIN=1", NULL); // packet-domain event reporting err = at_send_command("AT+CGEREP=1,0", NULL); // Hangup anything that's happening there now err = at_send_command("AT+CGACT=1,0", NULL); #endif // Start data on PDP context 1 if (strcmp(apn, "3gnet") == 0) { ALOGD("jscese display in reference is 3gnet \n"); err = at_send_command("ATD*99***1#", &p_response); //这个就是联通3G上网需要拨的号码 ATD*99***1# 获取连接 } else if (strcmp(apn, "ctnet") == 0) { ALOGD("jscese display in reference is ctnet \n"); err = at_send_command("ATD#777", &p_response); } if (err < 0 || p_response->success == 0) { goto error; } at_response_free(p_response); sleep(1); //Wait for the modem to finish property_set("net.ppp1.local-ip", ""); property_set("net.gprs.ppp-exit", ""); property_set("ctl.start", "pppd_gprs"); //如果上面的拨号AT指令成功返回,这里就启用之前定义好的一个pppd service 调用脚本去拨号获取IP 等网络参数 // Dialup sleep(3); /*jscese add try 5 times to get ip*/ int iRetry = 5; while (iRetry > 0) { property_get("net.gprs.ppp-exit", exit_code, ""); if (strcmp(exit_code, "0") != 0) { ALOGE("PPPd exit with code %s", exit_code); iRetry = 0; break; } ALOGI("Waiting For Property"); if (wait_for_property("net.ppp1.local-ip", NULL, 10) < 0) //监测 ip 地址的属性值,这个值我放在 pppd拨号脚本里面来进行设置,如果成功这里就能监测到 { ALOGE("[%s]: wait for IP from ppp link at %d\n", __func__, iRetry); } else { ALOGI("[%s]: got IP from ppp link\r\n", __func__); break; } iRetry--; } if (iRetry <= 0) { ALOGE("[%s]: fail to get IP\r\n", __func__); goto error; } /* if (wait_for_property("net.ppp1.local-ip", NULL, 10) < 0) { ALOGE("Timeout waiting net.ppp1.local-ip - giving up!\n"); goto error; }*/ property_get("net.ppp1.local-ip", ppp_local_ip, NULL); property_get("net.ppp1.dns1", ppp_dns1, NULL); property_get("net.ppp1.dns2", ppp_dns2, NULL); property_get("net.ppp1.gw", ppp_gw, NULL); sprintf(ppp_dnses, "%s %s", ppp_dns1, ppp_dns2); ALOGI("Got net.ppp1.local-ip: %s\n", ppp_local_ip); responses[0].status = 0; responses[0].suggestedRetryTime = -1; responses[0].cid = 1; responses[0].active = 2; responses[0].type = "PPP"; responses[0].ifname = PPP_TTY_PATH; responses[0].addresses = ppp_local_ip; responses[0].dnses = ppp_dnses; responses[0].gateways = ppp_gw; //通过获取到的 网络属性 设置这个responses 提供给上层 RIL_onRequestComplete(t, RIL_E_SUCCESS, responses, n * sizeof(RIL_Data_Call_Response_v6)); return; error: RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0); at_response_free(p_response); }
在上面 requestSetupDataCall 中启动一个服务来调用pppd拨号,先在init.rc中添加:
#jscese add for usb_switch service when 3G dongle add for serial service ril-daemon /system/bin/rild -l /system/lib/libreference-ril.so -- -d /dev/ttyUSB2 class main socket rild stream 660 root radio socket rild-debug stream 660 radio system user root group radio cache inet misc audio log service pppd_gprs /system/etc/ppp/init.gprs-pppd /dev/ttyUSB0 user root group radio cache inet misc disabled oneshot ## end
上面的服务是用来开机启动rild的,加载libreference-ril动态库,
并且指定了通信端口为串口 -d /dev/ttyUSB2,另外还有 -s 代表是socket设备 -p 代表回环接口
下面的就是pppd拨号的脚本服务了,用来启动拨号脚本,并且指定数据端口.
这个pppd_gprs 服务需要设置权限,因为我是在reference-ril里面通过属性启动的 在/system/core/init/property_service.c中添加如下:
struct { const char *service; unsigned int uid; unsigned int gid; } control_perms[] = { { "dumpstate",AID_SHELL, AID_LOG }, { "ril-daemon",AID_RADIO, AID_RADIO }, /*jscese add for pppd 3G*/ { "pppd_gprs",AID_RADIO, AID_LOG }, /*end*/ {NULL, 0, 0 } };
struct { const char *prefix; unsigned int uid; unsigned int gid; } property_perms[] = { ... /*jscese add pppd for 3G*/ { "net.ppp1.",AID_RADIO,0 }, /*end*/ { NULL, 0, 0 } };
init.gprs-pppd 设置执行权限,/system/core/include/private/android_filesystem_config.h中:
static struct fs_path_config android_files[] = { ... /*jscese add for pppd */ { 00777, AID_ROOT, AID_SHELL, "system/etc/init.gprs-pppd" }, /* end*/ ... }
贴出拨号脚本
#!/system/bin/sh PPPD_PID= USER=`/system/bin/getprop net.gprs.user` PASSWORD=`/system/bin/getprop net.gprs.password` /system/bin/setprop "net.gprs.ppp-exit" "" /system/bin/pppd $1 115200 linkname datakey unit 1 crtscts usepeerdns noauth defaultroute noipdefault ipcp-accept-local ipcp-accept-remote ipcp-max-failure 30 lcp-echo-interval 5 lcp-echo-failure 30 modem dump debug kdebug 8 PPPD_EXIT=$? PPPD_PID=$! /system/bin/log -t pppd "pppd exited with $PPPD_EXIT" /system/bin/setprop "net.gprs.ppp-exit" "$PPPD_EXIT"
另外在这个拨号脚本同目录下 /system/etc/ppp/ 需要设置和注销ip等参数的脚本,一个在拨号成功时调用,一个在断开网络时调用:
#!/system/bin/sh case $1 in ppp1) /android/bin/iptables --flush; /android/bin/iptables --table nat --flush; /android/bin/iptables --delete-chain; /android/bin/iptables --table nat --append POSTROUTING --out-interface ppp0 -j MASQUERADE; /android/bin/iptables --append FORWARD --in-interface ppp1 -j ACCEPT; echo 0 > /proc/sys/net/ipv4/ip_forward; echo 1 > /proc/sys/net/ipv4/ip_forward; ;; ppp0) /system/bin/setprop "net.interfaces.defaultroute" "ppp1"; ;; esac /system/bin/setprop "net.ppp1.dns1" "$DNS1" /system/bin/setprop "net.ppp1.dns2" "$DNS2" /system/bin/setprop "net.ppp1.local-ip" "$IPLOCAL" /system/bin/setprop "net.ppp1.remote-ip" "$IPREMOTE" /system/bin/setprop "net.ppp1.gw" "$IPREMOTE" /system/bin/setprop "net.ppp1.if" "$IFNAME"
#!/system/bin/sh case $1 in ppp1) echo 0 > /proc/sys/net/ipv4/ip_forward; ;; esac /system/bin/setprop "net.ppp1.dns1" "" /system/bin/setprop "net.ppp1.dns2" "" /system/bin/setprop "net.ppp1.local-ip" "" /system/bin/setprop "net.ppp1.remote-ip" "" /system/bin/setprop "net.ppp1.gw" "" /system/bin/setprop "net.ppp1.if" ""
贴张联通apn为 3gnet 的拨号log: