RILC
RILC
RIL层的作用大体上就是将上层的命令转换成相应的AT指令,控制modem工作。生产modem的厂家有很多:Qualcomm, STE, Infineon... 不同的厂家都有各自的特点,当然也会有各自不同的驱动,但驱动代码的公开多少会涉及到modem厂家的技术细节,所以,Android系统开源了绝大部分代码,对于 部分驱动(Reference-RIL) 允许厂家以二进制Lib的形式成为一套完整Android系统的一部分。
有Lib就需要有加载的概念,能够加载各种驱动说明驱动们都遵从一个统一的接口。这个接口是什么?RILC又是如何接收并处理RILJ向下传来的请求?
进入hardware\ril\rild\rild.c,一切从main开始。
int main(int argc, char **argv)
{
... ... dlHandle = dlopen(rilLibPath, RTLD_NOW);
if (dlHandle == NULL) {
fprintf(stderr, "dlopen failed: %s\n", 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\n", 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
ril_event.c
若干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
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\ril\include\telephony\Ril.h
打电话,则调用的是:
{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.
RILC的更多相关文章
- Communication - 03.RILC
RIL层的作用大体上就是将上层的命令转换成相应的AT指令,控制modem工作.生产modem的厂家有很多:Qualcomm, STE, Infineon... 不同的厂家都有各自的特点,当然也会有各自 ...
- Android 7.0 UICC 分析(一)
UICC(Universal Intergrated Circuit Card) 框架 * Following is class diagram for uicc classes: * * UiccC ...
- [Code::Blocks] Install wxWidgets & openCV
The open source, cross platform, free C++ IDE. Code::Blocks is a free C++ IDE built to meet the most ...
- Windows Thin PC 激活方法
Windows Thin PC 激活方法 笔者之前分享了Windows Thin PC ,如果你已经安装了Windows Thin PC ,但还没有激活,可以参照以下方式进行Windows Thin ...
- 说Win7激活
今天晚上给电脑来了个强制关机,后来打开后提示我,该Windos不是正版,顿时无语.诺,看下图:我的桌面也全部变成黑色了…… 后来一想……哦,应该是我的安装光盘里的激活工具激活的不彻底,或者说只是给我激 ...
- win7旗舰版通知windows不是正版副本解决方法
原文转载http://www.cnblogs.com/simple_666/archive/2013/04/13/win7%E6%97%97%E8%88%B0%E7%89%88%E9%80%9A%E7 ...
- Android4.0(Phone)来电过程分析
在开机时.系统会启动PhoneApp类,那是由于在AndroidManifest.xml文件里配置了 <application android:name="PhoneApp" ...
- Android RIL Log
转载: 要调试 RIL,最好的方法就是打开 radio的log: $ adb logcat -b radio 最好加上 log语法亮度工具coloredlogcat.py ,一些常见的LOG TAG要 ...
- Logcat过滤及常见用法整理
Usage: logcat [options] [filterspecs] options include:-s Set default filter to silent. ...
随机推荐
- POJ 2262 Goldbach's Conjecture(素数相关)
POJ 2262 Goldbach's Conjecture(素数相关) http://poj.org/problem?id=2262 题意: 给你一个[6,1000000]范围内的偶数,要你将它表示 ...
- MVC验证12-使用DataAnnotationsExtensions对整型、邮件、最小值、文件类型、Url地址等验证
原文:MVC验证12-使用DataAnnotationsExtensions对整型.邮件.最小值.文件类型.Url地址等验证 本文体验来自http://dataannotationsextension ...
- springmvc继承activemq(原创)
1.加入jar包 我项目是用maven构建的,所以这里就给大家贴一下配置了,具体jar大家可以参看下Activemq分类中的文章 <project xmlns="http://mave ...
- sqlserver检测数据库是否能连接的小技巧
有时候可能需要检测下某台机器的服务是不是起来了,或者某台机器的某个库是不是能被连接又不能打开ssms也不想登陆服务器的话就可以用这个方法. 1.在桌面上右键创建个文本,然后改后缀名为udl以后保存(1 ...
- Installshield设置feature为必须选中状态,即必定安装状态
原文:Installshield设置feature为必须选中状态,即必定安装状态 上一篇: 解决卸载时残留目标文件夹的问题Installation Designer --> Organizati ...
- js实现文字横向滚动
页面布局 <div id="scroll_div" class="fl"> <div id="scroll ...
- Oracle 11g+FluentNHibernate AutoMapping组合尝试
一次Oracle 11g+FluentNHibernate AutoMapping组合尝试 本文的上下文环境 操作系统:Win7 x64 Ultimate开发工具:Visual Studio 2013 ...
- mysql查询字段值为数字
原文:mysql查询字段值为数字 我想查询字段值为数字的sql如下:select * from tj_item_result where tj_value REGEXP '^[0-9]'
- Mysql插入内容过长(Packet for query is too large)
原文:Mysql插入内容过长(Packet for query is too large) 这个以前一直没有碰到过,一次性向Mysql数据库插入内容过长的话会出现这个问题,解决办法就是在Mysql配置 ...
- php表单(2)
学习php表单 主要是想知道 前端通过submit之后 后端是如何进行操作的.现在实现一个效果:点击submit,输入框的信息不会被刷掉:刷新页面,输入框的信息被刷掉(index.php). < ...