RILC
RIL层的作用大体上就是将上层的命令转换成相应的AT指令,控制modem工作。生产modem的厂家有很多:Qualcomm, STE, Infineon... 不同的厂家都有各自的特点,当然也会有各自不同的驱动,但驱动代码的公开多少会涉及到modem厂家的技术细节,所以,Android系统开源了绝大部分代码,对于 部分驱动(Reference-RIL) 允许厂家以二进制Lib的形式成为一套完整Android系统的一部分。
有Lib就需要有加载的概念,能够加载各种驱动说明驱动们都遵从一个统一的接口。这个接口是什么?RILC又是如何接收并处理RILJ向下传来的请求?
进入hardware il ild ild.c,一切从main开始。
int main(int argc, char **argv) { ... ... dlHandle = dlopen(rilLibPath, RTLD_NOW); if (dlHandle == NULL) { fprintf(stderr, "dlopen failed: %s ", dlerror()); exit(-1); } RIL_startEventLoop(); // ril_event rilInit = (const RIL_RadioFunctions *(*)(const struct RIL_Env *, int, char **))dlsym(dlHandle, "RIL_Init"); if (rilInit == NULL) { fprintf(stderr, "RIL_Init not defined or exported in %s ", rilLibPath); exit(-1); } ... ... funcs = rilInit(&s_rilEnv, argc, rilArgv); // Reference-RIL 获得 LibRIL 的Interface RIL_register(funcs); // LibRIL 获得 Reference-RIL 的Interface }
从dlopen看到了动态加载的痕迹,加载Reference-RIL之后便启动了监听线程,也就在RIL_startEventLoop。每一次从上层传来的请求都是一个event,可见要了解该层的消息传输,关键是要了解 结构体 ril_event。
与其相关的文件是ril_event.h、ril_event.cpp,对于文件的分析还是引用ACE1985兄台的博文为好,抱拳为敬。
ril_event.h
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
ril_event.c
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
若干ril_event构成watch_table数组,同时也被两个双向链表timer_list、pending_list串起来,不禁想起了内核链表。select对watch_table数组上的ril_event们进行监听。
RILJ与RILC通过socket连接,前者为client,后者为server。
server通过select监听对外开放的socket端口fd,若RILJ请求连接,则回调listenCallback(),accept()出一个s_fdCommand,加入select监听数组,这个s_fdCommand便成为了上层传入请求的通道,RILC通过这个通道接收具体的command,而后转化为AT指令。
static struct ril_event s_commands_event; static struct ril_event s_wakeupfd_event; static struct ril_event s_listen_event; static struct ril_event s_wake_timeout_event; static struct ril_event s_debug_event;
以上便是大致的思路,select+socket连接的经典模式。通道打通后,从s_fdCommand中到底会接收到什么?
ril_event_set (&s_commands_event, s_fdCommand, 1, processCommandsCallback, p_rs);
函数层层嵌套,终会有一个办实事的命令。
static int processCommandBuffer(void *buffer, size_t buflen) { Parcel p; status_t status; int32_t request; int32_t token; RequestInfo *pRI; //构造该结构体,尤其是其中的pCI int ret; p.setData((uint8_t *) buffer, buflen); //获得有效p // status checked at end status = p.readInt32(&request); //取得request值 status = p.readInt32 (&token); if (status != NO_ERROR) { LOGE("invalid request block"); return 0; } if (request < 1 || request >= (int32_t)NUM_ELEMS(s_commands)) { LOGE("unsupported request code %d token %d", request, token); // FIXME this should perhaps return a response return 0; } pRI = (RequestInfo *)calloc(1, sizeof(RequestInfo)); pRI->token = token; pRI->pCI = &(s_commands[request]); //确定早已待命的command号 ret = pthread_mutex_lock(&s_pendingRequestsMutex); assert (ret == 0); pRI->p_next = s_pendingRequests; s_pendingRequests = pRI; ret = pthread_mutex_unlock(&s_pendingRequestsMutex); assert (ret == 0); /* sLastDispatchedToken = token; */ pRI->pCI->dispatchFunction(p, pRI); //命令,发射! return 0; }
Ok,这个办实事的命令就是s_comands数组第request个结构体中的dispatchFunction().
s_comands数组是个啥?
static CommandInfo s_commands[] = { #include "ril_commands.h" };
typedef struct {
int requestNumber;
void (*dispatchFunction) (Parcel &p, struct RequestInfo *pRI);
int (*responseFunction) (Parcel &p, void *response, size_t responselen);
} CommandInfo;
Ref: http://blog.csdn.net/ace1985/article/details/7051522
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
RIL中有两种Response类型:
一是Solicited Response(经过请求的回复),应用的场景是AP主动向BP发送一个AT指令,请求BP进行相应处理并在处理结束时回复一个AT指令通知AP执行的结果。源码中对应的文件是ril_commands.h。
一是Unsolicited Response(未经请求的回复),应用场景是BP主动向AP发送AT指令,用于通知AP当前系统发生的与Telephony相关的事件,例如网络信号变化,有电话呼入等。源码中对应的文件是ril_unsol_commands.h。
static UnsolResponseInfo s_unsolResponses[] = { #include "ril_unsol_commands.h" }; typedef struct { int requestNumber; int (*responseFunction) (Parcel &p, void *response, size_t responselen); WakeType wakeType; } UnsolResponseInfo;
面对这上百的s_command元素们,顿觉代码的流程并非难点,难在对每一个s_command的理解。
Ref:hardware ilinclude elephonyRil.h
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
打电话,则调用的是:
{RIL_REQUEST_DIAL, dispatchDial, responseVoid},
看来dispatchDial才是办实事的好同志,而dispatchDial中最终调用了s_callbacks,即之前通过 RIL_register(funcs),LibRIL 获得 Reference-RIL 的Interface 。
s_callbacks.onRequest(pRI->pCI->requestNumber, &dial, sizeOfDial, pRI);
至此,终于进入了Reference-RIL。
中场休息,做个简单的回顾:
1. 我们构造了RequestInfo,pCI指向了对应的s_commands:
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;
2. CommandInfo中的dispatchFunction最终调用了Reference-RIL提供的接口。
typedef struct { int requestNumber; void (*dispatchFunction) (Parcel &p, struct RequestInfo *pRI); int(*responseFunction) (Parcel &p, void *response, size_t responselen); } CommandInfo;
3. RIL_RadioFunctions 便是RIL对Reference-RIL的实现要求。
typedef struct { int version; /* set to RIL_VERSION */ RIL_RequestFunc onRequest; RIL_RadioStateRequest onStateRequest; RIL_Supports supports; RIL_Cancel onCancel; RIL_GetVersion getVersion; } RIL_RadioFunctions;
4. onRequest 根据request号做出对应的处理,也就是ril_commands.h。
/** * RIL_Request Function pointer * * @param request is one of RIL_REQUEST_* * @param data is pointer to data defined for that RIL_REQUEST_* * data is owned by caller, and should not be modified or freed by callee * @param t should be used in subsequent call to RIL_onResponse * @param datalen the length of data * */ typedef void (*RIL_RequestFunc) (int request, void *data, size_t datalen, RIL_Token t);
RIL_RadioFunctions需要实现ril_commands.h中定义的request,当然,不一定全部支持。
OK,继续 dialing...
case RIL_REQUEST_DIAL: requestDial(data, datalen, t);
终于要见到AT的影子:
static void requestDial(void *data, size_t datalen, RIL_Token t) { RIL_Dial *p_dial; char *cmd; const char *clir; int ret; p_dial = (RIL_Dial *)data; switch (p_dial->clir) { case 1: clir = "I"; break; /*invocation*/ case 2: clir = "i"; break; /*suppression*/ default: case 0: clir = ""; break; /*subscription default*/ } asprintf(&cmd, "ATD%s%s;", p_dial->address, clir); ret = at_send_command(cmd, NULL); free(cmd); /* success or failure is ignored by the upper layer here. it will call GET_CURRENT_CALLS and determine success that way */ RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0); }
之后的事情便是将AT string通过某种通道发送给BP。至于这个通道的建立,可能是串口也可能是其他,但最终都会表现为一个文件描述符,这就是 rilInit 的事儿了。
以上便是基于Dial的流程浏览,到这一层,重点还是对ril_commands.h, ril_unsol_commands.h的理解,"得此二物者得RIL"!
NEXT, LET'S GO INTO BP.