深入详解Android GSM驱动模块作者:IT168 熊猫哥哥
2009-03-31
上文说到request是接收,是通过ril_event_loop中的多路复用I/O,也对初始化做了 分析.现在我们来仔细看看这个机制如何运转。 ril_event_set负责配置一个event,主要有两种event: 刷新之后, ril_event_loop从阻塞的位置,select返回,只有两种可能,一是超时,二
是等待到了某I/O操作. 前面的初始化流程已分析得知,初始化完成以后,队列上挂了3个event对象,分别是: 明白了event队列的基本运行流程,我们可以来看看request是怎么传入和dispatch的了. rr.mp.writeString(address); if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); send(rr); rr是以 RIL_REQUEST_DIAL为request号而申请的一个RILRequest对象.这个request 号在java框架和rild库中共享(参考RILConstants.java中这些值的由来)。 RILRequest初始化的时候,
会连接名为rild的socket(也就是rild中 s_listen_event绑定的socket),初始化数据传输的通道. 接下来是 send到handleMessage的流程,send将rr直接传递给另一个线程的 handleMessage,handleMessage执行data = rr.mp.marshall()执行序列化操作, 并 将data字节流写入到rild socket。 接下来回到我们的rild,select发现rild
socket有了请求链接的信号,导致 s_listen_event被挂入pending_list,执行event->func,即 接下 来,s_fdCommand = accept(s_fdListen, (sockaddr *) &peeraddr, &socklen),获取传入的socket描述符,也就是上层的java RIL传入的连接。 然后,通过 record_stream_new建立起一个record_stream, 将其与s_fdCommand绑 定, 这里我们不关注record_stream 的具体流程, 我们来关注command event的回 调, processCommandsCallback函数, 从前面的event机制分析, 一旦s_fdCommand 上有数据, 此回调函数就会被调用. (略过onNewCommandConnect的分析)。 processCommandsCallback通 过record_stream_get_next阻塞读取s_fdCommand上发 来的 数据, 直到收到一完整的request(request包的完整性由record_stream的机 制保证), 然后将其送达processCommandBuffer。 进入processCommandBuffer以后,我们就正式进入了命令的
解析部分. 每个命令将 以RequestInfo的形式存在。 typedef struct RequestInfo {
int32_t token; //this is not RIL_Token CommandInfo *pCI; struct RequestInfo *p_next; char cancelled; char local; // responses to local commands do not go back to command process } RequestInfo; 这里的pRI就是一个RequestInfo结构指
针, 从socket过来的数据流, 前面提到是 Parcel处理过的序列化字节流, 这里会通过反序列化的方法提取出来. 最前面的是
request号, 以及token域(request的递增序列号). 我们更关注这个request号, 前 面提到, 上层和rild之间,
这个号是统一的. 它的定义是一个包含ril_commands.h 的枚举, 在ril.cpp中 static CommandInfo s_commands[] = {
#include "ril_commands.h" }; pRI 直接访问这个数组, 来获取自己的pCI. 这是一个CommandInfo结构: typedef struct { int requestNumber; void (*dispatchFunction) (Parcel &p, struct RequestInfo *pRI); int(*responseFunction) (Parcel &p, void *response, size_t responselen); } CommandInfo; 基本解析到这里就完成了, 接下来, pRI被挂入pending的request队列, 执行具体 的pCI->dispatchFunction, 进行详细解析. 对dial而言, CommandInfo结构是这样初始化的: 这里执行dispatchFunction, 也就是dispatchDial这一函数.我们可以看到其实有 很多种类的dispatch function, 比如dispatchVoid, dispatchStrings, dispatchSIM_IO等等, 这些函数的区别, 在于Parcel传入的参数形式,Void就是不 带参数的,Strings是以string[]做参数,又如Dial等,有自己的参数解析方式,以此类推。 request号和参数现在 都有了,那么可以进行具体的request函数调用了 s_callbacks.onRequest(pRI->pCI->requestNumber, xxx, len, pRI)完成这一操作。 s_callbacks是上篇文章中提到的获取自libreference-ril的 RIL_RadioFunctions 结构指针,request请求在这里转入底层的libreference-ril处理,handler是 reference-ril.c中的onRequest。 onRequest进行一个简单的switch分发,我们依然来看 RIL_REQUEST_DIAL,流程是 onRequest-->requestDial-->at_send_command-->at_send_command_full-->at_send_command_full_nolock-->writeline。 requestDial中将命令和参数转换成对应的AT命令,调用公共send command接口 at_send_command。 除了这个接口之外,还有at_send_command_singleline,at_send_command_sms, at_send_command_multiline等,这是根据at返回值,以及发命令流程的类型来区别 的.比如at+csq这类,需要 at_send_command_singleline,而发送短信,因为有 prompt提示符">",传裸数据,结束符等一系列操作,需要专门用 at_send_command_sms来实现。 然后执 行at_send_command_full,前面几个接口都会最终到这里,再通过一个互斥的 at_send_command_full_nolock调用,然后完成最终的写出操作,在writeline中,写 出到初始化时打开的设备中。writeline返回之后,还有一些操作,如保存type等信息,供response回来时候使用, 以及一些超时处理. 不再详述。到这里,request的详细流程,就分析完毕了。 |
|