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只監測read的fd_set,所要監聽的
fd都存放在全域性變數readFds中,ptv決定select block的形態,要麼設定時間block直到到期,要麼無限block直到有監聽fd上
資料可讀,當select返回後就會查詢是哪個事件的fd的觸發的,然後通過firePending()呼叫該事件的callback。注意這是循
環的內部,也就是說每當select返回並執行其他動作之後,又會重新把readFds加到select中。熟悉Linux的同學應該很清楚這
種IO多路複用的select機制。
七、RIL_Init()函式分析
首先通過引數獲取硬體介面的裝置檔案或模擬硬體介面的socket. 接下來便新開一個執行緒繼續初始化,即mainLoop。mainLoop
的主要任務是建立起與硬體的通訊,然後通過read方法阻塞等待硬體的主動上報或響應在註冊一些基礎回撥(timeout,readerclose)
後,mainLoop首先開啟硬體裝置檔案,建立起與硬體的通訊,s_device_path和s_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結束時的返回值為rilInit(RIL_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工作執行緒中處理該客戶端的各種請求 。