Android移植RIL库的过程
Posted a746742897
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android移植RIL库的过程相关的知识,希望对你有一定的参考价值。
android移植RIL库的过程
参考链接:https://jingyan.baidu.com/article/3aed632e3a1ecb7010809128.html
主要记录了之前在RK3188 Android4.2.2里移植RIL库去支持LTE模块的全过程,包含了过程分析和问题处理。
工具/原料
-
方案:RK3188
-
Linux内核:linux-3.0.36
-
OS:Android4.2.2
-
LTE模块:Kernel-i L200(FDD-LTE)
方法/步骤
-
1.RIL运行流程
-
在init.rc文件里有如下形式的service定义:
-
service ril-daemon /system/bin/rild -l reference-ril.so -- -d /dev/ttyUSB0
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定义也可写为service ril-daemon /system/bin/rild,此时关于参数的获取可通过设置如下变量处理:
-
rild.libpath= /system/lib/libreference-ril.so
rild.libargs= -d/dev/ttyUSB0
-
在Android系统运行时会启动该服务,该服务会创建socket rild stream 660 root radio这样的一个socket,用于与framework里的上层代码进行通信,相应对应处理文件为:frameworks/opt/telephony/src/java/com/android/internal/telephony/RIL.java,即RIL库与上层交互是通过Socket(socket rild),而与底层驱动是通过串口(/dev/ttyUSB0)。
-
上面涉及到的rild服务源码位于hardware/ril/rild目录,而使用的RIL库对应源码位于hardware/ril/reference-ril目录,而两者交互时会使用到的libril.so源码位于hardware/ril/libril目录。
-
下面是rild服务启动后加载RIL库及AT指令、上报数据的处理流程分析:
-
rild服务的启动流程:
-
其中,“开启eventLoop线程”(libril.so)的流程如下:
-
如果上面线程的循环处理失败而退出循环,将会调用kill(0,SIGKILL)杀死自己所在进程后重启。循环中主要是处理timer_list、pending_list、watch_table三个队列,其中对pending_list队列的处理将是每个event的最后归宿,即会调用其回调函数并清出队列。
-
“运行RIL库的RIL_Init函数,并返回funcs回调函数指针”部分会调用到reference-ril.so里的RIL_Init()函数,该函数执行流程如下:
-
其中,s_rilenv对应到rild.c文件里s_rilEnv的内容如下:
-
static struct RIL_Env s_rilEnv = {
RIL_onRequestComplete,
RIL_onUnsolicitedResponse,
RIL_requestTimedCallback
};
-
而mainLoop()的执行流程如下:
-
上面的at_open(fd,onUnsolicited)处理过程为:初始化s_fd为AT口对应的fd,s_unsolHandler为onUnsolicited,然后再创建线程readerLoop()。
-
而readerLoop()则是循环从AT口(s_fd)或缓存里读一行AT指令数据(readline()),如为NULL则退出循环,接下来对AT指令数据进行处理,如为SMS非请求数据(以+CMT:、+CDS:或+CBM:开头)则再读一行,如为NULL则退出循环,否则调用s_unsolHandler处理这两次读到的数据,如果不是SMS,则调用processLine()处理读到的AT数据,其最终是调用s_unsolHandler来处理非上层请求数据(即AT口自动返回数据,用于主动向上层上报)或将返回数据加入到sp_response->p_intermediates链头,让RIL库进行处理相应onRequest()上报。当循环退出后会设置s_readerClosed=1并发s_commandcond信号(在断ReaderClosed()中)。
-
上面的RIL_requestTimedCallback(initializeCallback, NULL, &TIMEVAL_0)相当于internalRequestTimedCallback()函数,其执行流程为:初始化p_info,配置其回调函数及参数成员p_callback和userParam为initializeCallback和NULL,创建p_info->event,其fd为-1,非persist,该event对应回调函数及参数为userTimerCallback(该回调处理最终调用上面的p_callback和参数userParam)和p_info,并加入timer_list函列(按升序),然后往s_fdWakeupWrite写入一空格,以唤醒队列处理线程,然后返回p_info。
-
而initializeCallback()的处理流程为:setRadiostate(RADIO_STATE_OFF)先向上层上报RADIO状态为OFF,然后发AT指令测试AT口能不能握手成功,成功后会再发送一系列AT指令来初始化模块,然后再通过AT指令确认模块是否正常工作,是的话就setRadioState(RADIO_STATE_ON)主动上报RADIO正常ON状态。
-
“注册funcs回调列表”(RIL_register(funcs))部分流程如下:
-
上面的listenCallback()函数对应执行流程如下:
-
上面中的processCommandsCallback()函数的执行流程为:从p_rs获取数据至p_record,如果获取成功,则processCommandBuffer()处理结束后继续从p_rs获取数据和处理这样的循环,如果获取失败或读至结尾,则退出循环,关闭s_fdCommand连接,删除s_commands_event事件,删除p_rs记录流,将s_pendingRequests队列里各元素的cancelled标志位置1,并将s_listen_event加入watch_table重启监听新连接操作。
-
而processCommandBuffer()则将上层新发下来的命令请求里的request和token两数据项封包为RequestInfo类型的pRI中的元素,并将其加入s_pendingRequests队列,并执行对应request的s_command中对应的dispatchFunction函数(生成不同request对应的参数再通过s_callbacks.onRequest()去调用RIL库里onRequest()里对应分支的请求)。
-
至此,整个RIL部分的大致流程已清楚了,其中RILD部分主要是启动整个RIL进程,进行队列的处理(相当于整个队列管理核心),而libril.so中相应函数主要是处理上层请求、AT处理后数据上报,而reference-ril.so中则是处理串口AT指令的获取,利用到libril.so中的AT指令解析等操作。
-
相关文件说明:
-
atchannel.c:AT口指令收发处理
at_tok.c:AT指令返回内容解析处理
radiooptions.c:与RILD对应debug socket通信,调试命令。
reference-ril.c:RIL库核心文件,处理AT口指令交互、solicited和unsolicited事件及上报处理结果。
ril.cpp:各类回调函数、socket通信处理、上层请求处理。
rild.cpp:整个RIL服务入口,处理参数、队列创建和管理等管理相关进程的创建。
ril_event.cpp:rild涉及到的相关函数定义。
-
(注:上述流程图中结尾没有再往下的流程则表示该函数执行结束后返回)
-
2.RIL主要函数
-
1).AT指令发送
-
a. at_send_command_singleline
-
该函数在发送AT指令后对返回值进行解析,前提是返回值是只有一行的情况才使用。
-
b. at_send_command_multiline
-
该函数在发送AT指令后对返回值进行解析,前提是返回值是多行的情况下使用,可适用于一条AT指令返回多行数据或多条AT指令同时发送返回多行数据的情况。
-
c. at_send_command
-
该函数仅仅发送AT指令,不对其返回值进行解析。
-
2).AT指令返回值解析
-
a. at_tok_start
-
该函数用于将解析的数据指针定位到AT指令头后面位置(即返回值的首个冒号“:”后面)。
-
b. at_tok_nextint
-
该函数用于获取返回的AT数据对应指针位置后的首个Integer数据,并将指针指向数据之后。
-
c. at_tok_nextbool
-
该函数获取的是boolean数据,同样会将指针后移。
-
d. at_tok_nextstr
-
该函数获取的是String数据,同样会将指针后移。
-
e. at_tok_hasmore
-
该函数用于判断还有没有数据,即是不是到达数据尾部,不会进行指针操作。
-
3).数据上报
-
a 主动式
-
RIL_onUnsolicitedResponse,当在RIL库中需要向上层主动上报数据时,需用该函数进行处理。
-
b 被动式
-
RIL_onRequestComplete,当上层向RIL库请求处理,并且处理后向上层上报数据时,需用该函数进行处理。
-
4).移植涉及修改的函数
-
移植时主要是reference-ril.c文件,相关函数为:
-
static void onRequest (int request, void *data, size_t datalen, RIL_Token t);
static void onUnsolicited (const char *s, const char *sms_pdu);
-
主要把整个RILD服务配置好并正常启动,将对应的Request进行代码完善及匹配处理,其中onRequest()处理上层请求后上报处理的数据,而onUnsolicited()处理AT口未请求返回数据并且处理后上报处理的数据。
-
3.拔号上网、DHCP及上网状态上报
-
在3G模块时,使用的是PPP协议进行拔号上网及PPPD服务进行DHCP信息的获取,其使用到的是串口进行数据传输,而在LTE模块中,使用串口已无法满足速率的要求,其使用的是模拟网口的方式进行数据传输,而在拔号上需要向AT口发送配置及连接指令,连接上后需要对模拟网口进行DHCP配置。
-
在3G模块里,一般会映射出/dev/ttyUSB0、1、2共3个USB转串口的设备结点,其中这三个串口会对应“AT指令口”、“语音通话口”(带语音功能的模块)、“数据上网口”,而在LTE模块,映射出来的有一个AT指令口(如ttyUSB0)和一个虚拟网口(如usb0、wan0)。
-
针对不同LTE模块,其上网的AT指令不同,需根据具体情况处理,但DHCP处理这块,在RK3188平台可使用如下两种方式处理:
-
a 使用busybox udhcpc命令,在RIL库中使用system函数调用,该方法过多的使用到命令行命令,移植性不佳,故不讨论;
-
b 使用libnetutils库(system/core/libnetutils/)里的dhcp_do_request函数,相应的函数格式如下:
-
dhcp_do_request(PPP_TTY_PATH, ppp_local_ip, ppp_gw, &prefixLength, ppp_dns1, ppp_dns2, server, &lease, vendorInfo)
-
当执行该函数时,会去启动dhcpcd_wan0的service,其中wan0为上面的PPP_TTY_PATH,还有DHCP服务到期后的重新请求服务,故而在init.rk30board.rc里添加如下内容:
-
service dhcpcd_wan0 /system/bin/dhcpcd -ABKL
class main
disabled
oneshot
service iprenew_wan0 system/bin/dhcpcd -n
class main
disabled
oneshot
-
当请求到相应数据后,我们需要上报到Android上层,此时需要使用如下格式的数据进行上报:
-
sprintf(ppp_dnses, "%s %s", ppp_dns1, ppp_dns2);
responses = alloca(sizeof(RIL_Data_Call_Response_v6));
responses[0].status = 0;
responses[0].suggestedRetryTime = -1;
responses[0].cid = 1;
responses[0].active = 2;
responses[0].type = "IP";
responses[0].ifname = PPP_TTY_PATH;
responses[0].addresses = ppp_local_ip;
responses[0].dnses = ppp_dnses;
responses[0].gateways = ppp_gw;
RIL_onRequestComplete(t, RIL_E_SUCCESS, responses, sizeof(RIL_Data_Call_Response_v6));
-
至此,涉及我们移植过程的细节问题及程序执行流程的分析,就大致说明了一遍,非常棒的一次学习体验。
以上是关于Android移植RIL库的过程的主要内容,如果未能解决你的问题,请参考以下文章