一、基本架構概述
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工作線程中處理該客戶端的各種請求 。