ril - 2

/***************************************************************************/

 上文說到request是接收,是通過ril_event_loop中的多路複用I/O,也對初始化做了 分析.現在我們來仔細看看這個機制如何運轉。

  ril_event_set負責配置一個event,主要有兩種event:
ril_event_add添加使用多路I/O的event,它負責將其掛到隊列,同時將event的通道 句柄fd加入到watch_table,然後通過select等待.
ril_timer_add添加timer event,它將其掛在隊列,同時重新計算最短超時時間.
無論哪種add,最後都會調用triggerEvLoop來刷新隊列,更新超時值或等待對象.

  刷新之後, ril_event_loop從阻塞的位置,select返回,只有兩種可能,一是超時,二 是等待到了某I/O操作.
超時的處理在processTimeouts中,摘下超時的event,加入pending_list.
檢查有I/O操作的通道的處理在processReadReadies中,將超時的event加入 pending_list.
最後在firePending中,檢索pending_list的event並依次執行event->func.
這些操作完之後,計算新超時時間,並重新select阻塞於多路I/O.

  前面的初始化流程已分析得知,初始化完成以後,隊列上掛了3個event對象,分別是:
s_listen_event: 名爲rild的socket,主要requeset & response通道
s_debug_event: 名爲rild-debug的socket,調試用requeset & response通道(流 程與s_listen_event基本相同,後面僅分析s_listen_event)
s_wakeupfd_event: 無名管道,用於隊列主動喚醒(前面提到的隊列刷新,就用它來 實現,請參考使用它的相關地方)。

 

/***************************************************************************/

 

明白了event隊列的基本運行流程,我們可以來看看request是怎麼傳入和dispatch的了.
上層的部分,核心代碼在 frameworks/base/telephony/java/com/android/internal/telephony/gsm/RIL.java, 這是android java框架處理radio(gsm)的核心組件.本文因爲主要關注rild,也就是 驅動部分,所以這裏只作簡單介紹.
我們看一個具體的例子,RIL.java中的dial函數:
public void
dial (String address, int clirMode, Message result)
{
RILRequest rr = RILRequest.obtain(RIL_REQUEST_DIAL, result);

rr.mp.writeString(address);
rr.mp.writeInt(clirMode);

if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));

send(rr);
}

  rr是以RIL_REQUEST_DIAL爲request號而申請的一個RILRequest對象.這個request 號在java框架和rild庫中共享(參考RILConstants.java中這些值的由來)。

  RILRequest初始化的時候,會連接名爲rild的socket(也就是rild中 s_listen_event綁定的socket),初始化數據傳輸的通道.
rr.mp是Parcel對象,Parcel是一套簡單的序列化協議,用於將對象(或對象的成 員)序列化成字節流,以供傳遞參數之用.這裏可以看到 String address和int clirMode都是將依次序列化的成員.在這之前,rr初始化的時候,request號跟 request的序列號(自動生成的遞增數),已經成爲頭兩個將被序列化的成員.這爲 後面的request解析打下了基礎。

  接下來是send到handleMessage的流程,send將rr直接傳遞給另一個線程的 handleMessage,handleMessage執行data = rr.mp.marshall()執行序列化操作, 並 將data字節流寫入到rild socket。

  接下來回到我們的rild,select發現rild socket有了請求鏈接的信號,導致 s_listen_event被掛入pending_list,執行event->func,即
static void listenCallback (int fd, short flags, void *param);。

  接下來,s_fdCommand = accept(s_fdListen, (sockaddr *) &peeraddr, &socklen),獲取傳入的socket描述符,也就是上層的java RIL傳入的連接。

  然後,通過record_stream_new建立起一個record_stream, 將其與s_fdCommand綁 定, 這裏我們不關注record_stream 的具體流程, 我們來關注command event的回 調, processCommandsCallback函數, 從前面的event機制分析, 一旦s_fdCommand 上有數據, 此回調函數就會被調用. (略過onNewCommandConnect的分析)。

  processCommandsCallback通過record_stream_get_next阻塞讀取s_fdCommand上發 來的 數據, 直到收到一完整的request(request包的完整性由record_stream的機 制保證), 然後將其送達processCommandBuffer。

  進入processCommandBuffer以後,我們就正式進入了命令的解析部分. 每個命令將 以RequestInfo的形式存在。
 

typedef struct RequestInfo {
int32_t token;
//this is not RIL_Token
CommandInfo *pCI;
struct RequestInfo
*p_next;
char cancelled;
char local; // responses to local commands do not go back to command process
} RequestInfo;

  這裏的pRI就是一個RequestInfo結構指針, 從socket過來的數據流, 前面提到是 Parcel處理過的序列化字節流, 這裏會通過反序列化的方法提取出來. 最前面的是 request號, 以及token域(request的遞增序列號). 我們更關注這個request號, 前 面提到, 上層和rild之間, 這個號是統一的. 它的定義是一個包含ril_commands.h 的枚舉, 在ril.cpp中
 

static CommandInfo s_commands[] = {
#include
"ril_commands.h"
};
pRI直接訪問這個數組, 來獲取自己的pCI.
這是一個CommandInfo結構:
typedef struct {
int requestNumber;
void (*dispatchFunction) (Parcel &p, struct RequestInfo *pRI);
int(*responseFunction) (Parcel &p, void *response, size_t responselen);
} CommandInfo;

  基本解析到這裏就完成了, 接下來, pRI被掛入pending的request隊列, 執行具體 的pCI->dispatchFunction, 進行詳細解析.

 

 

/***************************************************************************/

 

 

 

對dial而言, CommandInfo結構是這樣初始化的:
{RIL_REQUEST_DIAL, dispatchDial, responseVoid},

  這裏執行dispatchFunction, 也就是dispatchDial這一函數.我們可以看到其實有 很多種類的dispatch function, 比如dispatchVoid, dispatchStrings, dispatchSIM_IO等等, 這些函數的區別, 在於Parcel傳入的參數形式,Void就是不 帶參數的,Strings是以string[]做參數,又如Dial等,有自己的參數解析方式,以此類推。

  request號和參數現在都有了,那麼可以進行具體的request函數調用了 

  s_callbacks.onRequest(pRI->pCI->requestNumber, xxx, len, pRI)完成這一操作。

  s_callbacks是上篇文章中提到的獲取自libreference-ril的RIL_RadioFunctions 結構指針,request請求在這裏轉入底層的libreference-ril處理,handler是 reference-ril.c中的onRequest。

  onRequest進行一個簡單的switch分發,我們依然來看RIL_REQUEST_DIAL,流程是 onRequest-->requestDial-->at_send_command-->at_send_command_full-->at_send_command_full_nolock-->writeline。

  requestDial中將命令和參數轉換成對應的AT命令,調用公共send command接口 at_send_command。

  除了這個接口之外,還有at_send_command_singleline,at_send_command_sms, at_send_command_multiline等,這是根據at返回值,以及發命令流程的類型來區別 的.比如at+csq這類,需要 at_send_command_singleline,而發送短信,因爲有 prompt提示符">",傳裸數據,結束符等一系列操作,需要專門用 at_send_command_sms來實現。

  然後執行at_send_command_full,前面幾個接口都會最終到這裏,再通過一個互斥的 at_send_command_full_nolock調用,然後完成最終的寫出操作,在writeline中,寫 出到初始化時打開的設備中。writeline返回之後,還有一些操作,如保存type等信息,供response回來時候使用, 以及一些超時處理. 不再詳述。到這裏,request的詳細流程,就分析完畢了。

 

 

 

 

 

發佈了0 篇原創文章 · 獲贊 16 · 訪問量 16萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章