首页 > 代码库 > android下调试3G之Ril库分析

android下调试3G之Ril库分析

一、基本架构概述    

      Android RIL (Radio Interface Layer)提供了Telephony服务和Radio硬件之间的抽象层。RIL负责数据的可靠传输、AT命令的发送

以及response(响应)的解析。一般的,应用处理器(AP)通过AT命令集与无线通讯模块(基带/BP)通信。通信的方式又分为主动

请求的request(诸如拨号、发短信……),以及Modem主动上报的例如信号强度、基站信息、来电、来短信等,称之为

unsolicitedresponse(未经请求的响应)。系统框架如下图:

技术分享

二、ril-daemon的启动:

      ril-daemon进程是由init进程在系统开机时负责启动的,该进程在我们系统启动之后就一直存在在系统里面了。

init.rc(.../out/target/product/sabresd_6dq/root/init.rc对应源码  .../system/core/rootdir/init.rc)中可以看到如下代码:

     service ril-daemon /system/bin/rild -l /system/lib/libreference-ril.so -- -d /dev/ttyUSB2 # -u /dev/ttyUSB0

              class main

              socket rild stream 660 root radio

              socket rild-debug stream 660 radio system

              user root

              group radio cache inet misc audio sdcard_rw log

     ril-daemon守护进程指的是system/bin/下的可执行程序rild,而rild是由.../hardware/ril/rild/目录下的rild.c文件编译生成的。

三、rild启动流程分析

     1、rild(hardware/ril/rild/rild.c):仅实现main函数作为整个ril层的入口点,负责完成初始化。

     2、libril.so(hardware/ril/libril/*):与rild结合相当紧密,是其共享库,编译时就已经建立了这一关系。

           组成部分为:rild.cpp、ril_event.cpp。libril.so驻留在rild这一守护进程中,主要完成同上层通信的工作,接受ril请求并传递

           给libreference-ril.so,同时把libreference-ril.so的反馈传给调用进程。

     3、libreference-ril.so(hardware/ril/libreference-ril/*):rild通过dlopen方式加载,主要负责跟Modem硬件通信。它转换来自

           librild.so的请求为AT命令,同时监控Modem的反馈信息,并传递回libril.so。在初始化时,rild通过符号RIL_Init获取一组函数

           指针并以此与之建立联系。

     4、radiooptions(hardware/ril/rild/radiooptions.c):radiooptions通过获取启动参数。利用socket与rild通信,可供调试时配置

           Modem参数。

     5、rild.c 代码分析

         1)dlHandle = dlopen(rilLibPath, RTLD_NOW);//打开动态链接库,根据上面脚本中的语句打开的为:

                /system/lib/libreference-ril.so库

          2)rilInit = (const RIL_RadioFunctions *(*)(const struct RIL_Env *, int, char **))dlsym(dlHandle, "RIL_Init");       

              根据动态链接库操作句柄与符号,返回对应的地址,这里返回"RIL_Init"函数地址,该函数在libreference-ril.so库里面定义

               rilLibPath是通过property属性值的方式来获取的。它的值为:“/system/lib/libreference-ril.so “动态库文件的属

        性值。                     

         3)RIL_startEventLoop():开启libril.so中的event机制,在RIL_startEventLoop()中是最核心的由多路I/O驱动的信息循环。

         4)RIL_Init:初始化libreference-ril.so,也就是跟硬件或模拟硬件Modem通信的部分,通过RIL_Init函数完成,函数的返回值

              为rilInit为一个RIL_RadioFunctions类型的结构体的指针。

         5)RIL_register( ):通过RIL_Init获取一组函数指针RIL_RadioFunctions  s_callbacks,并通过RIL_register完成注册,并打开

              接受上层命令的socket通道。

四、RIL_startEventLoop( )函数分析

     1、ret = pthread_create(&s_tid_dispatch, &attr, eventLoop, NULL):创建一个消息循环的s_tid_dispatch线程,它的回调函数

          eventLoop( )。

     2、while (s_started == 0) {

                    pthread_cond_wait(&s_startupCond, &s_startupMutex);

         }:如果eventLoop( )方法不执行,RIL_startEventLoop( )方法就不会返回。

五、eventLoop( )函数分析

     1、s_started = 1:改变s_started 的值,让RIL_startEventLoop( )能正常结束。

     2、ril_event_init():初始化ril_event.cpp几个非常重要的成员变量:readFds、timer_list、pending_list、watch_table。

     3、ril_event_set (&s_wakeupfd_event, s_fdWakeupRead, true,processWakeupCallback, NULL):注册进程唤醒时的回调。

     4、ril_event_loop( ):建立起消息队列机制。

六、ril_event.cpp代码分析

     每个ril_event结构,与一个fd句柄绑定(可以是文件,socket,管道等),并且每一个func指针所指的函数是个回调函数,它指定

了当所有绑定的fd准备好进行读取时所要进行的操作。

     1、fd_set  readFds:存放所有的ril_event对应的fd信息,便于通socket()来查找对应的ril_event消息。

     2、fil_event  * watch_table[ MAX_FD_EVENTS ]:监听事件队列,就是一个ril_event数据类型的链表或者数组。

     3、ril_event  timer_list:超时事件队列,也就是说本来某某事件是在time_table里面的,表示正在被监听,过了些时间,超时了

          还没有被触发,那么这个事件被扔pending_list里面,待执行。

     4、ril_event  pending_list:待执行的事件集合。

     5、Ril_event_loop就是一个for的无限循环,在这个for内部看到select函数了,其实select只监测readfd_set,所要监听的

     fd都存放在全局变量readFds中,ptv决定select block的形态,要么设定时间block直到到期,要么无限block直到有监听fd

     数据可读,当select返回后就会查找是哪个事件的fd的触发的,然后通过firePending()呼叫该事件的callback。注意这是循

     环的内部,也就是说每当select返回并执行其他动作之后,又会重新把readFds加到select中。熟悉Linux的同学应该很清楚这

     种IO多路复用的select机制。

七、RIL_Init()函数分析

   首先通过参数获取硬件接口的设备文件或模拟硬件接口的socket. 接下来便新开一个线程继续初始化,即mainLoopmainLoop

主要任务是建立起与硬件的通信,然后通过read方法阻塞等待硬件的主动上报或响应在注册一些基础回调(timeout,readerclose

后,mainLoop首先打开硬件设备文件,建立起与硬件的通信,s_device_paths_port是前面获取的设备路径参数,将其打开。

RIL_Init的主要任务:

   1、向librefrence.so注册libril.so提供的接口RIL_Env;

   2、创建一个mainLoop工作线程,用于初始化AT模块,并监控AT模块的状态,一旦AT被关闭,则重新打开并初始化AT;

   3、当AT被打开后,mainLoop工作线程将向Rild提交一个定时事件,并触发eventLoop来完成对modem的初始化;

   4、创建一个readLoop工作线程,用于从AT串口中读取数据;

   5、返回librefrence.so提供的接口RIL_RadioFunctions;

   6、ret = pthread_create(&s_tid_mainloop, &attr, mainLoop, NULL):创建一个mainLoop线程 

   7、RIL_requestTimedCallback(initializeCallback, NULL, &TIMEVAL_0):有了响应机制,通过此函数跑到

      initializeCallback中,执行一些Modem的初始化命令,主要都是AT命令方式。

八、at_open( )函数分析

   1、ret = pthread_create(&s_tid_reader, &attr, readerLoop, &attr):创建readerLoop工作线程,该线程用于从串口读取

      数据。AT指令都是以/r/n或/n/r的换行符作为分隔符的,所以readerLoop是line驱动的,除非出错,超时等,否则会读到一

      行完整的相应或主动上报,才会返回,这个循环跑起来以后,基本的AT响应机制已经建立起来了。

   2、readerLoop()函数分析

     for (;;) {

          . . . . . .

          line2 = readline();//AT命令都是以/n/r/t结束,都是一行为单位读取

          processLine(line); //处理接收到的数据,根据line中的指令调用不同的回调函数

          . . . . . .

     }

九、RIL_register( )函数分析

   RIL_init结束时的返回值为rilInitRIL_RadioFunctions结构体类型),先来看看RIL_RadioFunctions结构体的构成,其中

最重要的是onRequest,上层来的请求都由这个函数进行映射后转换成对应的AT命令发给硬件。RIL_register的另外一个重要的作用

是:打开和上层通信的socket管道。有了这样一个通道,上层App就可以同过这个通道来和Modem端通信,Modem端也可以通过这个通

道向上层App发送响应消息了。

   1、typedef struct {

                 int version;       //Rild版本 /* set to RIL_VERSION */

                 RIL_RequestFunc onRequest;//AP请求接口 

                 RIL_RadioStateRequest onStateRequest;//BP状态查询

                 RIL_Supports supports;

                 RIL_Cancel onCancel;

                 RIL_GetVersion getVersion;//动态库版本  

            } RIL_RadioFunctions;

     2、监听rild  Socket管道

         s_fdListen = android_get_control_socket(SOCKET_NAME_RIL);// 得到名为rild的socket句柄 

          ret = listen(s_fdListen, 4);

         此处即监听上层RIL_java中创建的那个“rild”的socket。

     3、监听rild——deBug  Socket管道

         s_fdDebug = android_get_control_socket(SOCKET_NAME_RIL_DEBUG);// 得到调试socket的句柄rild-debug

         ret = listen(s_fdDebug, 4);

     4、ril_event_set (&s_listen_event, s_fdListen, false,listenCallback, NULL):设置s_listen_event事件,一旦有客户端连接,即

         s_fdListen可读就会导致eventLoop工作线程中的select返回,因为该事件不是持久的,因此调用为listenCallback处理完后,将

         从watch_table移除该事件,所以Rild只支持一个客户端连接。

     5、rilEventAddWakeup (&s_listen_event): 添加s_listen_event事件,并触发eventLoop工作线程。

十、listenCallback()函数分析

     1、s_fdCommand = accept(s_fdListen, (sockaddr *) &peeraddr, &socklen):接收一个客户端的连接,并将该socket连接保存在

          变量s_fdCommand中。

     2、p_rs = record_stream_new(s_fdCommand, MAX_COMMAND_BYTES):p_rs为RecordStream类型,它内部会分配一个缓冲

          区来存储客户端发送过来的数据。

     3、ril_event_set (&s_commands_event, s_fdCommand, 1,processCommandsCallback, p_rs):添加一个针对接收到的客户端连

          接的处理事件,从而在eventLoop工作线程中处理该客户端的各种请求 。

 

 

 

android下调试3G之Ril库分析