1. 程式人生 > >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工作執行緒中處理該客戶端的各種請求 。