上面绕了一圈,还是把对消息的处理从动态库(也就是reference-ril.c文件)饶回到了ril.c文件中。这也符合整个RIL架构的设计理念:框架和处理方式由ril.c管理,差异化的AT命令由reference实现。 @ril.cpp
void RIL_onUnsolicitedResponse(int unsolResponse, void *data, size_t datalen, RILId id) {
//得到当前命令的请求码
unsolResponseIndex = unsolResponse - RIL_UNSOL_RESPONSE_BASE; //从ril_unsol_commands.h文件中得到命令的类型
wakeType = s_unsolResponses[unsolResponseIndex].wakeType; //根据不同命令的不同类型进行解析 switch (wakeType) { case WAKE_PARTIAL: case DONT_WAKE: }
appendPrintBuf(\
Parcel p;
p.writeInt32 (RESPONSE_UNSOLICITED); p.writeInt32 (unsolResponse);
//调用当前命令的打包函数进行数据打包
ret = s_unsolResponses[unsolResponseIndex].responseFunction(p, data, datalen); //把数据发送到RILJ中 ret = sendResponse(p,id);
if (shouldScheduleTimeout) { }
return; }
上面的处理过程分为2步:1、调用当前命令对应的打包函数进行数据打包;2、将数据发送给RILJ。 数据打包的过程涉及到一个数组s_unsolResponses,他的详细作用在本文的最后一张有说明,这里简要介绍一下。s_unsolResponses是一个文件,文件中对所有的URC消息都有一个对应的数组相对应,每个数组分3部分:1、命令的请求码;2、命令的打包函数;3、命令的类型;我们要做的就是用当前Modem给出的命令,找到对应的请求码,然后得到相应的打包函数进行数据打包。
而发送的过程就是调用sendResponse把Parcel数据发送给RILJ。
在上面的过程中,用reference中通过AT头转换的命令值与RIL_UNSOL_RESPONSE_BASE相减得到在
s_unsolResponses表中对应的命令索引。查找相应的wakeType类型去决定是否计算超时(shouldScheduleTimeout),之后就用s_unsolResponses中的responseFunction去解析命令,最后通过sendResponse将数据发送给RILJ: static int sendResponse (Parcel &p) {
return sendResponseRaw(p.data(), p.dataSize()); }
发送数据:
static int sendResponseRaw (const void *data, size_t dataSize) { //发送通道的Socket ID int fd = s_fdCommand; int ret;
uint32_t header;
//将数据长度这个整数转换为适合Socket 传输的字节顺序 header = htonl(dataSize);
//先发送一个关于数据大小的字节
ret = blockingWrite(fd, (void *)&header, sizeof(header));
//再发送数据本身
ret = blockingWrite(fd, data, dataSize);
return 0; }
继续看发送过程:
static int blockingWrite(int fd, const void *buffer, size_t len) { size_t writeOffset = 0; const uint8_t *toWrite;
toWrite = (const uint8_t *)buffer;
while (writeOffset < len) { ssize_t written; do {
//发送数据到fd指定的句柄中,也就是RILJ对应的Socket通道 written = write (fd, toWrite + writeOffset,len - writeOffset); } while (written < 0 && ((errno == EINTR) || (errno == EAGAIN))); } return 0; }
这里注意到,发送的最终操作,就是把数据放到一个fd指向的句柄中,那么这个句柄从哪里来的呢? #define SOCKET_NAME_RIL \ //得到\的Socket连接
s_fdListen = android_get_control_socket(SOCKET_NAME_RIL);
//accept的函数默认会阻塞进程,直到有一个客户连接建立后把可用的连接套接字返回 s_fdCommand = accept(s_fdListen, (sockaddr *) &peeraddr, &socklen);
上面的递归关系我们可以看出,s_fdCommand就是RILJ与RILC之间建立的Socket通道的套接字!因此我们可以通过这个通道发送数据给RILJ。
2.3、非URC消息处理流程
上面介绍了URC消息的流程,下面分析一下更普遍的非URC消息的处理流程。
前面说道,非URC消息就是一种回应。当上层通过AT向Modem发送请求后,会一直处于阻塞状态等待回应,一旦readerLoop得到了非URC的消息,就会去唤醒Event端等待的进程。 我们在此主要介绍reference如何唤醒eventLoop端的线程。 非URC消息的上报流程也是从processLine开始的: @atchannel.c
static void processLine(const char *line) {
if (sp_response == NULL) {
} else if (isFinalResponseSuccess(line)) { sp_response->success = 1; //发送回应消息给eventLoop handleFinalResponse(line); } else if (isFinalResponseError(line)) {
} else if (s_smsPDU != NULL && 0 == strcmp(line, \ } else switch (s_type) { case NO_RESULT: case NUMERIC:
if (sp_response->p_intermediates == NULL && isdigit(line[0]) ) {
addIntermediate(line); } else {
handleUnsolicited(line); } break; case SINGLELINE:
if (sp_response->p_intermediates == NULL && strStartsWith (line, s_responsePrefix) ) {
addIntermediate(line); } else {
handleUnsolicited(line); } break; case MULTILINE: break; } }
这里简要介绍以下Modem对于非URC消息的回复格式。消息一般大于2行,前几行是返回值的有效数据,最后一行是作为当前命令结束的标志位。如果是有效数据,那么当前数据就有一个s_type的类型与之相关联(从EventLoop发送给reference时决定)。因此就会在processLine中的switch中进入addIntermediate函数,而这个函数的作用就是把当前的数据放入到反馈数据的sp_response->p_intermediates里面。等到命令的最后,因为是标志位,就会走到processLine的handleFinalResponse中,将数据发送给Event侧。 下面贴出涉及到的重要函数:
a、将数据放到反馈数据中:addIntermediate static void addIntermediate(const char *line) {
ATLine *p_new;
p_new = (ATLine *) malloc(sizeof(ATLine)); p_new->line = strdup(line);
p_new->p_next = sp_response->p_intermediates; //把有效的返回值放到sp_response->p_intermediates中 sp_response->p_intermediates = p_new; }