第4章 RILC运行机制
4.1 RILC运行机制
RILC代码重点在于:hardware/ril/libril(Runtime运行环境的源文件)、hardware/ril/reference-ril.so、hardware/ril/rild这三个目录下的C/C++代码文件。
Rilc运行在UserLibraries系统运行库中的HAL层,它使用HAL Stub运行结构。最关键的为Runtime对外提供Proxy代理接口,Stub向Runtime提供Operations操作函数,Runtime向Stub提供Callback函数。
RILC运行机制主要围绕libRIL与Reference-RIL相互调用,从而完成solicited和unsolicited消息处理机制,包括两个过程:启动和运行。
启动过程:rild完成;
运行核心:LibRIL和Reference-RIL消息交互。
RILC运行机制
LibRIL为Reference-RIL实现了RIL请求转换成AT命令,并执行AT命令逻辑。
LibRIL提供了Reference-RIL的proxy代理接口。RILJ基于Socket网络连接完成solicited和unsolicited消息和LibRIL进行交互,最终交给Reference-RIL进行处理。
Reference-RIL和modem之间通过串口进行通信,主要用于AT命令执行。
4.1.1 rild.c的main函数
第一章所说的main函数,它整体负责启动rilc。关键职责是建立LibRIL和Reference-RIL的一种相互协调的能力。
LibRIL中有指向Reference-RIL中funcs结构体的指针。
Reference-RIL中有指向LibRIL中s_rilEnv结构体(Runtime)的指针。
4.1.2 LibRIL目录结构
hardware/ril/libril
Android.mk
Ril_commands.h //定义了LibRIL接收到RILJ发出的solicited请求消息所对应的调用函数和返回调用函数。
Ril.cpp //建立Runtime框架;
Ril_event.cpp //实现基于ril_event双向链表操作函数;
Ril_event.h //定义ril_event事件的结构定义;
Ril_ex.h
Ril_unsol_commands.h //定义了unsolicited消息返回函数的调用。
……
1)LibRIL以ril.cpp代码为核心,其他代码协助他完成LibRIL Runtime的启动和运行,LibRIl Runtime的两个作用:
1、与RILJ基于socket交互;
2、与Reference-RIL基于函数调用的交互。
2)结构体RIL_env定义了三个指向函数的指针。
3)结构体RIL_RadioFunctions:当LibRIL接收到RILJ发起的solicited请求后,其他5个指向函数的指针会调用Reference-RIL提供给funcs中对应请求函数。
4.1.3 LibRIL Runtime的加载
LibRIl Runtime的加载体现在RIL_startEventLoop和RIL_register两个函数。
1)RILC启动该过程中,先调用LibRIL中的RIL_startEventLoop函数完成LibRIl运行环境的准备,然后开始循环监听socket相关RIL事件。RIL_startEventLoop调用以下相关函数:
eventLoop函数:
s_wakeupfd_event的事件处理分为三大块:
a. 创建管道获取其输入输出文件描述符s_fdWakeupRead、 s_fdWakeupWrite;
b使用s_fdWakeupRead和processWakeupCallback创建s_wakeupfd_event事件;
c增加并激活s_wakeupfd_event事件。
2)RIL_register函数引入三方RIL_RadioFunctions
3)ril_event事件处理机制
Ril_event_init:双向链表初始化;
Ril_event_set:设置新创建ril_event事件参数;
Ril_event_add:增加event;
Ril_event_loop:开始监听ril事件,分两步:
a.增加pending_list双向链表中的RIL事件节点。processTimeouts和processReadReadies两个函数都是将对应RIL时间增加到pending_list双向链表;
b. 调用firePending函数遍历pending_list双向链表获取ril_event,调用其他func回调函数,完成对应ril事件的回应。
4.1.3 LibRIL 运行机制
分为两个部分:
1)RIJ建立与RIL的socket连接;
2) RILJ向RIL发起solicited消息的交互流程和处理机制。
4.2 AP侧主动请求,以dial为例
RIL.java dail()àril_service.cpp dial()àril_service.cpp CALL_ONREQUESTàreference-ril.c onRequest()
在这里根据发送的请求类型到requestXXX(),àrequestDial() “ ret = at_send_command(cmd, NULL);” à发送AT指令 atchannel.c at_send_command() at_send_command_full() at_send_command_full_nolock() writeLine()
最后都会到RIL_onRequestComplete
而这里的RIL_onRequestComplete都是调用ril.cppàril.cpp responseFunction()
RIL.dial()
//获取service proxy IRadio radioProxy = getRadioProxy(result);
//构造request消息
//调用dial方法
àril_serivce.dial()
RequestInfo *pRI = android::addRequestToList(serial, mSlotId, RIL_REQUEST_DIAL);
//为RequestInfo类型分配了一块内存,并将地址给pRI,这个是response时用的
…
//解析参数,并保存在RIL_Dial类型的变量内
CALL_ONREQUEST(RIL_REQUEST_DIAL, &dial, sizeOfDial, pRI, mSlotId);
CALL_ONREQUEST:s_vendorFunctions->onRequest((a), (b), (c), (d), ((RIL_SOCKET_ID)(e)))
s_vendorFunctions是vendor lib返回的函数指针,所以到这里也就调用了lib里面相应的函数
当lib处理完之后会调用RIL_onRequestComplete,dialResponse函数会被调用
àril_service.dialResponse()
radioService[slotId]->mRadioResponse != NULL
//radioService[slotId]中的mRadioResponse是RILJ在启动时放的回调,在RILJ对应的类是RadioResponse
4.3 modem主动上报消息
Modem主动上报:
前面提到过的qcril初始化流程中,调用了Ril_init函数,其中传递的参数”s_rilEnv”是rild.c的全局变量
static struct RIL_Env s_rilEnv = {
RIL_onRequestComplete,
RIL_onUnsolicitedResponse,
RIL_requestTimedCallback,
RIL_onRequestAck
};
将s_rilEnv的地址传进Ril_Init函数是为了给modem上报消息提供调用接口,当modem有消息上报时,RIL_onUnsolicitedResponse函数会被调用
ril. RIL_onUnsolicitedResponse()
ret = s_unsolResponses[unsolResponseIndex].responseFunction(
(int) soc_id, responseType, 0, RIL_E_SUCCESS, const_cast<void*>(data),
datalen);
s_unsolResponses是ril.cpp的全局变量,使用头文件ril_unsol_commands.h初始化。该文件定义了每个unsolicited消息对应的函数;RIL_onUnsolicitedResponse通过s_unsolResponses找到对应函数,并调用。
ril_unsol_commands.h:
{RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED, radio::radioStateChangedInd, WAKE_PARTIAL},
{RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED, radio::callStateChangedInd, WAKE_PARTIAL},
以RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED为例,RIL_onUnsolicitedResponse会调用callStateChangedInd函数:
callStateChangedInd通过radioService[slotId]的mRadioIndication将消息传到Java层,Java层对应的类是RadioIndication.java。RadioIndication.callStateChanged会调用ril.java将消息进一步上传。
RIL.java
public class RIL extends BaseCommands implements CommandsInterface
RadioIndication RadioResponse
从名字就可以看出,RadioIndication用于处理modem主动上报的消息,RadioResponse用于处理AP侧request的response
public class RadioIndication extends IRadioIndication.Stub
public class RadioResponse extends IRadioResponse.Stub
根据上面的定义,RadioIndication和RadioResponse都继承于Stub,明显是为了进程间的通信
Ril.java对象是在开机时PhoneApp应用启动的过程中创建的,Ril.java的构造函数完成了初始化工作,包括RadioIndication、RadioResponse对象的创建以及HIDL service的获取。
getRadioProxy方法获取了HIDL service,并将RadioResponse和RadioIndication对象传递给service,经过binder传递后,这两个对象的引用分别被保存在RadioImpl::mRadioResponse和RadioImpl::mRadioIndication中,后续用于modem给AP侧上报消息。
**************************************************************************************************************************************************************************
术语:
fd:unix文件描述符
pipe:unix管道
cond:一般是condition variable的缩写
tty:通常使用tty来简称各种类型的终端设备
unsolicited response:被动请求命令来自baseband
eventLoop:Android的消息队列机制,有Unix的系统调用select()实现
init.rc:init守护进程启动后被执行的启动脚本
HAL:硬件抽象层
4.4 solicited消息处理机制
Solicited消息的交互流程和处理机制
- RILJ与LibRIL建立socket连接后,LibRIL的watch_table数组增加了一个事件监听:s_commands_event;
- RILJ基于rild端口的socket向LibRIL发起Solicited Request消息请求时,s_commands_event通过func发起RILJ事件callback函数调用
- 即调用processCommandsCallback函数处理RILJ发起的request请求,在ril_commands.h头文件中定义了107个RIL请求的处理函数和回调函数
- RILJ在发起RIL请求后,在这里通过RIL请求类型获取请求处理和返回处理的函数(比如:发起拨号请求,LibRIL匹配CommandInfo的请求调用函数为dispatchDial,返回调用函数responseVoid;当LibRIL发起pRI->pCI->dispatchFunction函数调用时,实际调用的是dispatchDial函数)
- LibRIL请求调用函数,最终通过Reference_RIL提供的s_callbacks发起onRequest函数调用
- Reference-RIL接收到LibRIL的请求,根据请求类型转换成对应的AT命令,向modem发起AT指令;接着便会调用LinRIL提供的RIL_onRequestComplete函数,完成RIL请求处理完成后的回调
- RIL_onRequestComplete函数处理逻辑:responseFunction函数调用,完成不用返回的Parcel数据设置;sendResponse函数调用,通过socket连接发生Parcel数据,即RILJ接收此数据;同样发起拨号请求后,pRI->pCI->responseFunction函数调用,实际是responseCallList函数调用
Reference-ril.c主要负责:
- 将Solicited Request请求转化为AT命令交给Modem执行,并将AT命令执行的结果以Solicited Response消息的方式反馈给LibRIL;
- 同时负责接收modem主动上报的消息,以UnSolicited Response消息的方式反馈给LibRIL处理,逻辑可分为两部分:与LibRIL的交互完成RIL消息的处理、与modem通信模块的交互完成AT命令的执行
Reference-ril.c运行机制:
- Reference-ril的初始化函数RIL_Init
- onRequest函数接收LibRIL的请求调用
- 接收modem发出的UnSolicited Response消息处理逻辑
RIL_Init函数初始化Reference-ril:
- 记录LibRIL提供的RIL_Env指针,通过他调用LibRIL提供相应函数
- 启动mainLoop子进程,负责监听和接受modem主动上报的UnSolicited消息
- 返回Reference-ril提供的指向RIL_RadioFunctions指针s_callbacks
onRequest接收LinRIL的请求调用:LibRIL接收到RILJ发起的ril请求后,通过onRequest函数调用,向Reference-ril发起对应的RIL请求
- 将RIL请求转化为对应的AT命令,并向modem发出AT命令
- 调用LibRIL的RIL_onRequestComplete函数,完成RIL请求处理结果的返回
UnSolicited消息处理逻辑:mainLoop函数主要负责监听和处理modem上报的UnSolicited消息
- 与modem建立基于串口的通信连接,同时获取连接的文件扫描符fd
- 调用at_open函数开启AT命令通道:保存与modem建立连接的文件描述符fd,以及接收到modem发出的AT命令后的回调函数onUnsolicited、启动readerLoop子进程,readerLoop函数会for循环监听并接受modem发出的AT命令
- onUnsolicited函数:提供LibRIL提供的RIL_onUnsolicitedResponse函数发出不同类型的UnSolicited Response消息通知
- RIL_onUnsolicitedResponse函数:根据UnSolicited Response消息类型获取s_unsolResponses数组中对应的UnSolicited ResponseInfo结构体对象,其中包括此消息电源唤醒策略和Parcel数据处理函数、应用UnsolResponseInfo中的电源管理唤醒策略,进行电源唤醒操作、调用UnsolResponseInfo中的Parcel数据处理函数,完成Parcel数据的组织和设置、调用sendResponse函数,通过socket连接发送Parcel给RILJ
现在都是用的都是qcril-hal, 但是流程和我讲的qcril流程差不多。