一、基本架构概述
Android RIL (Radio Interface Layer)提供了Telephony服务和Radio硬件之间的抽象层。RIL负责数据的可靠传输、AT命令的发送
以及response(响应)的解析。一般的,应用处理器(AP)通过AT命令集与无线通讯模块(基带/BP)通信。通信的方式又分为主动
请求的request(诸如拨号、发短信……),以及Modem主动上报的例如信号强度、基站信息、来电、来短信等,称之为
unsolicitedresponse(未经请求的响应)。系统框架如下图:
二、ril-daemon的启动:
ril-daemon进程是由init进程在系统开机时负责启动的,该进程在我们系统启动之后就一直存在在系统里面了。
在init.rc(.../out/target/product/sabresd_6dq/root/init.rc对应源码
.../system/core/rootdir/init.rc)中可以看到如下代码:
service ril-daemon /system/bin/rild -l /system/lib/libreference-ril.so -- -d /dev/ttyUSB2 # -u /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 sdcard_rw log
ril-daemon守护进程指的是system/bin/下的可执行程序rild,而rild是由.../hardware/ril/rild/目录下的rild.c文件编译生成的。
三、rild启动流程分析
1、rild(hardware/ril/rild/rild.c):仅实现main函数作为整个ril层的入口点,负责完成初始化。
2、libril.so(hardware/ril/libril/*):与rild结合相当紧密,是其共享库,编译时就已经建立了这一关系。
组成部分为:rild.cpp、ril_event.cpp。libril.so驻留在rild这一守护进程中,主要完成同上层通信的工作,接受ril请求并传递
给libreference-ril.so,同时把libreference-ril.so的反馈传给调用进程。
3、libreference-ril.so(hardware/ril/libreference-ril/*):rild通过dlopen方式加载,主要负责跟Modem硬件通信。它转换来自
librild.so的请求为AT命令,同时监控Modem的反馈信息,并传递回libril.so。在初始化时,rild通过符号RIL_Init获取一组函数
指针并以此与之建立联系。
4、radiooptions(hardware/ril/rild/radiooptions.c):radiooptions通过获取启动参数。利用socket与rild通信,可供调试时配置
Modem参数。
5、rild.c 代码分析
1)dlHandle = dlopen(rilLibPath, RTLD_NOW);//打开动态链接库,根据上面脚本中的语句打开的为:
/system/lib/libreference-ril.so库
2)rilInit = (const RIL_RadioFunctions *(*)(const struct RIL_Env *, int, char **))dlsym(dlHandle, "RIL_Init");
根据动态链接库操作句柄与符号,返回对应的地址,这里返回"RIL_Init"函数地址,该函数在libreference-ril.so库里面定义
rilLibPath是通过property属性值的方式来获取的。它的值为:“/system/lib/libreference-ril.so “动态库文件的属
性值。
3)RIL_startEventLoop():开启libril.so中的event机制,在RIL_startEventLoop()中是最核心的由多路I/O驱动的信息循环。
4)RIL_Init:初始化libreference-ril.so,也就是跟硬件或模拟硬件Modem通信的部分,通过RIL_Init函数完成,函数的返回值
为rilInit为一个RIL_RadioFunctions类型的结构体的指针。
5)RIL_register( ):通过RIL_Init获取一组函数指针RIL_RadioFunctions
s_callbacks,并通过RIL_register完成注册,并打开
接受上层命令的socket通道。
四、RIL_startEventLoop(
)函数分析
1、ret = pthread_create(&s_tid_dispatch, &attr, eventLoop, NULL):创建一个消息循环的s_tid_dispatch线程,它的回调函数
eventLoop( )。
2、while (s_started == 0) {
pthread_cond_wait(&s_startupCond, &s_startupMutex);
}:如果eventLoop( )方法不执行,RIL_startEventLoop(
)方法就不会返回。
五、eventLoop(
)函数分析
1、s_started = 1:改变s_started 的值,让RIL_startEventLoop(
)能正常结束。
2、ril_event_init():初始化ril_event.cpp几个非常重要的成员变量:readFds、timer_list、pending_list、watch_table。
3、ril_event_set (&s_wakeupfd_event, s_fdWakeupRead, true,processWakeupCallback,
NULL):注册进程唤醒时的回调。
4、ril_event_loop( ):建立起消息队列机制。
六、ril_event.cpp代码分析
每个ril_event结构,与一个fd句柄绑定(可以是文件,socket,管道等),并且每一个func指针所指的函数是个回调函数,它指定
了当所有绑定的fd准备好进行读取时所要进行的操作。
1、fd_set readFds:存放所有的ril_event对应的fd信息,便于通socket()来查找对应的ril_event消息。
2、fil_event * watch_table[ MAX_FD_EVENTS ]:监听事件队列,就是一个ril_event数据类型的链表或者数组。
3、ril_event timer_list:超时事件队列,也就是说本来某某事件是在time_table里面的,表示正在被监听,过了些时间,超时了
还没有被触发,那么这个事件被扔pending_list里面,待执行。
4、ril_event pending_list:待执行的事件集合。
5、Ril_event_loop就是一个for的无限循环,在这个for内部看到select函数了,其实select只监测read的fd_set,所要监听的
fd都存放在全局变量readFds中,ptv决定select
block的形态,要么设定时间block直到到期,要么无限block直到有监听fd上
数据可读,当select返回后就会查找是哪个事件的fd的触发的,然后通过firePending()呼叫该事件的callback。注意这是循
环的内部,也就是说每当select返回并执行其他动作之后,又会重新把readFds加到select中。熟悉Linux的同学应该很清楚这
种IO多路复用的select机制。
七、RIL_Init()函数分析
首先通过参数获取硬件接口的设备文件或模拟硬件接口的socket. 接下来便新开一个线程继续初始化,即mainLoop。mainLoop
的主要任务是建立起与硬件的通信,然后通过read方法阻塞等待硬件的主动上报或响应在注册一些基础回调(timeout,readerclose)
后,mainLoop首先打开硬件设备文件,建立起与硬件的通信,s_device_path和s_port是前面获取的设备路径参数,将其打开。
RIL_Init的主要任务:
1、向librefrence.so注册libril.so提供的接口RIL_Env;
2、创建一个mainLoop工作线程,用于初始化AT模块,并监控AT模块的状态,一旦AT被关闭,则重新打开并初始化AT;
3、当AT被打开后,mainLoop工作线程将向Rild提交一个定时事件,并触发eventLoop来完成对modem的初始化;
4、创建一个readLoop工作线程,用于从AT串口中读取数据;
5、返回librefrence.so提供的接口RIL_RadioFunctions;
6、ret = pthread_create(&s_tid_mainloop, &attr, mainLoop, NULL):创建一个mainLoop线程
7、RIL_requestTimedCallback(initializeCallback, NULL, &TIMEVAL_0):有了响应机制,通过此函数跑到
initializeCallback中,执行一些Modem的初始化命令,主要都是AT命令方式。
八、at_open( )函数分析
1、ret = pthread_create(&s_tid_reader, &attr, readerLoop, &attr):创建readerLoop工作线程,该线程用于从串口读取
数据。AT指令都是以/r/n或/n/r的换行符作为分隔符的,所以readerLoop是line驱动的,除非出错,超时等,否则会读到一
行完整的相应或主动上报,才会返回,这个循环跑起来以后,基本的AT响应机制已经建立起来了。
2、readerLoop()函数分析
for (;;) {
. . . . . .
line2 = readline();//AT命令都是以/n/r/t结束,都是一行为单位读取
processLine(line); //处理接收到的数据,根据line中的指令调用不同的回调函数
. . . . . .
}
九、RIL_register(
)函数分析
在RIL_init结束时的返回值为rilInit(RIL_RadioFunctions结构体类型),先来看看RIL_RadioFunctions结构体的构成,其中
最重要的是onRequest,上层来的请求都由这个函数进行映射后转换成对应的AT命令发给硬件。RIL_register的另外一个重要的作用
是:打开和上层通信的socket管道。有了这样一个通道,上层App就可以同过这个通道来和Modem端通信,Modem端也可以通过这个通
道向上层App发送响应消息了。
1、typedef struct {
int version; //Rild版本 /* set to RIL_VERSION */
RIL_RequestFunc onRequest;//AP请求接口
RIL_RadioStateRequest onStateRequest;//BP状态查询
RIL_Supports supports;
RIL_Cancel onCancel;
RIL_GetVersion getVersion;//动态库版本
} RIL_RadioFunctions;
2、监听rild Socket管道
s_fdListen = android_get_control_socket(SOCKET_NAME_RIL);// 得到名为rild的socket句柄
ret = listen(s_fdListen,
4);
此处即监听上层RIL_java中创建的那个“rild”的socket。
3、监听rild——deBug
Socket管道
s_fdDebug = android_get_control_socket(SOCKET_NAME_RIL_DEBUG);// 得到调试socket的句柄rild-debug
ret = listen(s_fdDebug, 4);
4、ril_event_set (&s_listen_event, s_fdListen, false,listenCallback,
NULL):设置s_listen_event事件,一旦有客户端连接,即
s_fdListen可读就会导致eventLoop工作线程中的select返回,因为该事件不是持久的,因此调用为listenCallback处理完后,将
从watch_table移除该事件,所以Rild只支持一个客户端连接。
5、rilEventAddWakeup (&s_listen_event): 添加s_listen_event事件,并触发eventLoop工作线程。
十、listenCallback()函数分析
1、s_fdCommand = accept(s_fdListen, (sockaddr *) &peeraddr, &socklen):接收一个客户端的连接,并将该socket连接保存在
变量s_fdCommand中。
2、p_rs = record_stream_new(s_fdCommand, MAX_COMMAND_BYTES):p_rs为RecordStream类型,它内部会分配一个缓冲
区来存储客户端发送过来的数据。
3、ril_event_set (&s_commands_event, s_fdCommand,
1,processCommandsCallback, p_rs):添加一个针对接收到的客户端连
接的处理事件,从而在eventLoop工作线程中处理该客户端的各种请求 。