qcril:第1章RILD及Qcril初始化流程
Qcril初始化流程
Rild是android提供的框架,位置位于AP侧(AP主处理器),对接Phone进程和BP侧modem。(BP从处理器)
图1
双卡⼿机在phone进程中会有2个phone的实例对应各个卡槽,相应的RILD进程也会有2个,phone和RILD之间通过Socket保持连接,各卡槽之间的操作互相独⽴。
⾼通⽤qcril+qmi机制实现,⽽MTK则直接使⽤AT指令与modem通信。
进程的启动
Rild是系统服务,在中能找到启动的描述。
/hardware/ril/中启动第⼀个rild
图中启动第⼀个rild
/device/qcom/common/rootdir/etc/中启动第⼆个rild:
图2启动第⼆个rild(这⾥除了rild2还有rild3)
主函数
根据可以看出,rild的⼊⼝函数名字为main。/hardware/ril/rild/rild.c
接着看⼀下rild.c的main函数。
int main(int argc, char **argv) {
…….
//1. 从启动参数中获取clientID,代表⼏就是第⼏个RILD
for (i = 1; i < argc ;) {……
if (0 == strcmp(argv[i], "-l") && (argc - i > 1)) {……
clientId = argv[i+1];
//2. 使⽤clientID设置RILD的socket名字
if (strncmp(clientId, "0", MAX_CLIENT_ID_LENGTH)) {……}
//3. 获取vendor ril库路径(3.1)
if (rilLibPath == NULL) {
if ( 0 == property_get(LIB_PATH_PROPERTY, libPath, NULL)) {
goto done;
} el { rilLibPath = libPath; } }
//4. 打开vendor ril库(3.1)
dlHandle = dlopen(rilLibPath, RTLD_NOW);
//5. 启动消息循环,LIdRIL开始循环监听socket事件(3.2)
RIL_startEventLoop();
//6. 获取vendor RIL的初始化⼊⼝⽅法(3.3)
rilInit =(const RIL_RadioFunctions *(*)(const struct RIL_Env *, int, char **))dlsym(dlHandle, "RIL_Init");
//7. 调⽤reference-ril.so动态链接库的RIL_Init⽅法,传递s_rilEnv参数给reference-ril.so,并返回funcs(3.3)
rilArgv[argc++] = "-c";
rilArgv[argc++] = (char*)clientId;
rilArgv[0] = argv[0];
funcs = rilInit(&s_rilEnv, argc, rilArgv);
//8. 调⽤libril.so的RIL_register函数,将funcs传递给libril.so(3.4)
RIL_register(funcs);
总结main函数的主要内容:
1)获取vendor ril并打开;(第3、4步,对应3.1)
2)启动消息循环;调⽤RIL_startEventLoop函数,LibRIL开启循环监听socket连接,即可开始接收RILJ发起的socket连接请求和RIL solicited消息请求。(第5步,对应3.2)
3)获得vendor ril⼊⼝⽅法,并取得vendor ril的消息处理函数列表;(6、7步,对应3.3)
4)注册vendor ril回调⽅法。(第8步,对应3.4)
S_rilEnv对应RIL_Env;
Funcs对应RIL_RadioFunctions。
LibRIL运⾏环境的加载过程,体现在rild.c代码的main函数中RIL_startEventLoop和RIL_register两个函数的调⽤。
深⼊分析main函数
获取vendor RIL库⽂件路径
Main函数中通过getprop获取了vendor ril⽂件的路径,property名为“rild.libpath”。/hardware/ril/rild/rild.c⽂件中:
⾸先定义LIB_PATH_PROPERTY的值;
#define LIB_PATH_PROPERTY "rild.libpath"
然后在main函数中获取vendor ril⽂件的路径:
property_get(LIB_PATH_PROPERTY, libPath, NULL)。
1. 找到libril-qc-qmi-1.so库⽂件路径;
2. 在/vendor/qcom/proprietary/qcril/qcril_qmi/qcril_qmi.mk中有描述;
图3库⽂件路径描述
在取得了vendor ril库⽂件路径之后,打开库并取得⽂件句柄,流程似乎即将进⼊到vendor ril。但在此之前,rild会先启动⼀个⼦线程消息循环,然后再开始qcril的流程。
启动监听消息循环
RIL_startEventLoop创建了⼀个线程,⽤于执⾏eventLoop这个函数。
1. Ril.cpp中的RIL_startEventLoop():
启动线程,执⾏eventLoop函数,在eventLoop函数中会将s_started置为1.
S_started会在eventLoop被执⾏后置为1,保证了线程启动后才离开这个函数
图4在while循环主要是通过判断s_started的值,保证线程已经启动且eventLoop已经开始被执⾏,因为在eventLoop函数中会将
s_started值置为1。
2. eventLoop是Rild中消息获取和分发的中⼼,接下来来详细查看⼀下:
// 1.初始化fd_t(readFds)3.2.1;
// 2.初始化⼏个消息链表(watch_table/pending_list/timer_list);
//使RIL_startEventLoop退出;
//获得读&写管道3.2.2;
//初始化⼀个event,回调函数为processWakeupCallBack;
//1. 向watch_list加⼊这个event;
/
/2. 向fd_t加⼊s_fdWakeupRead,由于persist为true,此fd和event会⼀直保留;
//3. 调⽤triggerEvloop,向s_fdWakeupWrite写⼊字符,(将会唤醒lect);
//⽆限循环的event loop,lect fd_t(readFds);
//出现异常,kill⾃⼰完成重启。
图5
为eventLoop初始化数据
/hardware/ril/libril/ril_event.cpp
FD_ZERO(……)重置fd_t
然后就是init_list()初始化ril_event的timer_list、pending_list链表。
图6
看⼀下参数readFds,他是⼀个fd_t,后续连接客户端后,socket fd会加⼊到这个t去,监听输⼊。
/hardware/ril/libril/ril_event.h中的ril_event链表结构体,保存了前后2个ril_event的指针,元素中还有fd、persist值、回调函数func和空指针类型param。(func被具体调⽤的时候,参数就是ril_event⾃⾝的fd和param)
Watch_table是ril_event数组指针,保存需要观察的ril_event;
Timer_list是ril_event结构体,带有超时属性的event;通过ril_timer_add函数加⼊,在eventLoop中判断超时后处理。
Pending_list同样是ril_event结构体,存放需要马上被回调的event。eventLoop中lect返回后,watch_table和timer_list中需要进⾏回调的event都会加⼊到这⾥,在回调完成后就会移除。
创建pipe
上⼀步中准备好基础数据后,接下来向fd_t加⼊读管道,同时加⼊⼀个ril_event到watch_table。(在3.2中eventLoop()函数执⾏完ril_event_init后,接着就会执⾏ret=pipe(filedes)…….)
执⾏pipe函数获得读写fd:s_fdWakeupRead及s_fdWakeupWrite。
图7 eventLoop()函数中接着执⾏创建管道的代码
执⾏ril_event_t函数:初始化⼀个ril_event,fd为s_fdWakeupRead,回调函数为processWakeupCallback,persist值为true(决定了event的fd不会从fd_t移除,获得持续监听和触发回调的能⼒)---》看⼀下ril_event_t⾥⾯的参数
processWakeupCallback,processWakeupCallback的作⽤就是从s_fdWakeupRead中读数据,相当于清空管道。这样看来这个
ril_event存在的作⽤就是清空管道了。
Event建⽴完成后,接下来就是执⾏3.2中eventLoop函数中接下来要调⽤的rilEventAddWakeup()。rilEventAddWakeup:将s_fdWakeupRead加⼊readFds集合,event加⼊到watch_table,然后向s_fdWakeupWrite写⼊数据。
图8 rilEventAddWakeup()-----》ril_event_add()
再然后就是等待有程序过来lect和处理这个event了。
(3.2中的代码按照顺序,这是应该执⾏ril_event_loop了。/hardware/ril/libril/ril_event.cpp中定义)Ril_event_loop监听socket消息循环。Ril_event_loop函数进⼊for循环之后,将等待lect函数返回。(因为在3.2.2中已经加⼊了⼀个event,这⾥马上就会返回)
等lect返回后,将timer_list中超时的event和watch_table中event加⼊到pending_list中等待处理。最后遍历pending_list,执⾏所有ril_event的回调函数。
然后,循环会进⼊到下⼀轮。
在ril_event_loop中将会看到event中的fd、persist、func等成员如何被使⽤。在ril_event.cpp的ril_event_loop中将会--
-》processTimeouts()、processReadReadies(&rfds, n)、firePending()。这三个函数会处理timer_list、watch_table、
pending_list中的event。
图9 RIL_startEventLoop函数流程解析图