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工作線程中處理該客戶端的各種請求 。

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章