ril_event_set負(fù)責(zé)配置一個(gè)event,主要有兩種event:
ril_event_add添加使用多路I/O的event,它負(fù)責(zé)將其掛到隊(duì)列,同時(shí)將event的通道 句柄fd加入到watch_table,然后通過select等待.
ril_timer_add添加timer event,它將其掛在隊(duì)列,同時(shí)重新計(jì)算最短超時(shí)時(shí)間.
無論哪種add,最后都會調(diào)用triggerEvLoop來刷新隊(duì)列,更新超時(shí)值或等待對象.
刷新之后, ril_event_loop從阻塞的位置,select返回,只有兩種可能,一是超時(shí),二是等待到了某I/O操作.
超時(shí)的處理在processTimeouts中,摘下超時(shí)的event,加入pending_list.
檢查有I/O操作的通道的處理在processReadReadies中,將超時(shí)的event加入 pending_list.
最后在firePending中,檢索pending_list的event并依次執(zhí)行event->func.
這些操作完之后,計(jì)算新超時(shí)時(shí)間,并重新select阻塞于多路I/O.
前面的初始化流程已分析得知,初始化完成以后,隊(duì)列上掛了3個(gè)event對象,分別是:
s_listen_event:名為rild的socket,主要requeset & response通道
s_debug_event:名為rild-debug的socket,調(diào)試用requeset & response通道(流程與s_listen_event基本相同,后面僅分析s_listen_event)
s_wakeupfd_event:無名管道,用于隊(duì)列主動喚醒(前面提到的隊(duì)列刷新,就用它來 實(shí)現(xiàn),請參考使用它的相關(guān)地方)。
明白了event隊(duì)列的基本運(yùn)行流程,我們可以來看看request是怎么傳入和dispatch的了.
上層的部分,核心代碼在frameworks/base/telephony/java/com/android/internal/telephony/gsm/RIL.java,這是android java框架處理radio(gsm)的核心組件.本文因?yàn)橹饕P(guān)注rild,也就是 驅(qū)動部分,所以這里只作簡單介紹.
我們看一個(gè)具體的例子,RIL.java中的dial函數(shù):
publicvoid
dial (String address, int clirMode, Message result)
{
RILRequestrr = 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號而申請的一個(gè)RILRequest對象.這個(gè)request號在java框架和rild庫中共享(參考RILConstants.java中這些值的由來)。
RILRequest初始化的時(shí)候,會連接名為rild的socket(也就是rild中 s_listen_event綁定的socket),初始化數(shù)據(jù)傳輸?shù)耐ǖ?
rr.mp是Parcel對象,Parcel是一套簡單的序列化協(xié)議,用于將對象(或?qū)ο蟮某?員)序列化成字節(jié)流,以供傳遞參數(shù)之用.這里可以看到 Stringaddress和int clirMode都是將依次序列化的成員.在這之前,rr初始化的時(shí)候,request號跟request的序列號(自動生成的遞增數(shù)),已經(jīng)成為頭兩個(gè)將被序列化的成員.這為 后面的request解析打下了基礎(chǔ)。
接下來是send到handleMessage的流程,send將rr直接傳遞給另一個(gè)線程的handleMessage,handleMessage執(zhí)行data = rr.mp.marshall()執(zhí)行序列化操作, 并將data字節(jié)流寫入到rild socket。
接下來回到我們的rild,select發(fā)現(xiàn)rildsocket有了請求鏈接的信號,導(dǎo)致 s_listen_event被掛入pending_list,執(zhí)行event->func,即
staticvoid listenCallback (int fd, short flags, void *param);。
接下來,s_fdCommand = accept(s_fdListen, (sockaddr *) &peeraddr,&socklen),獲取傳入的socket描述符,也就是上層的java RIL傳入的連接。
然后,通過record_stream_new建立起一個(gè)record_stream, 將其與s_fdCommand綁 定,這里我們不關(guān)注record_stream 的具體流程, 我們來關(guān)注command event的回 調(diào),processCommandsCallback函數(shù), 從前面的event機(jī)制分析, 一旦s_fdCommand 上有數(shù)據(jù),此回調(diào)函數(shù)就會被調(diào)用. (略過onNewCommandConnect的分析)。
processCommandsCallback通過record_stream_get_next阻塞讀取s_fdCommand上發(fā) 來的 數(shù)據(jù),直到收到一完整的request(request包的完整性由record_stream的機(jī) 制保證),然后將其送達(dá)processCommandBuffer。
進(jìn)入processCommandBuffer以后,我們就正式進(jìn)入了命令的解析部分. 每個(gè)命令將 以RequestInfo的形式存在。
這里的pRI就是一個(gè)RequestInfo結(jié)構(gòu)指針, 從socket過來的數(shù)據(jù)流, 前面提到是 Parcel處理過的序列化字節(jié)流, 這里會通過反序列化的方法提取出來. 最前面的是request號, 以及token域(request的遞增序列號). 我們更關(guān)注這個(gè)request號, 前 面提到, 上層和rild之間,這個(gè)號是統(tǒng)一的. 它的定義是一個(gè)包含ril_commands.h 的枚舉, 在ril.cpp中
基本解析到這里就完成了, 接下來,pRI被掛入pending的request隊(duì)列, 執(zhí)行具體 的pCI->dispatchFunction, 進(jìn)行詳細(xì)解析.
對dial而言, CommandInfo結(jié)構(gòu)是這樣初始化的:
{RIL_REQUEST_DIAL, dispatchDial,responseVoid},
這里執(zhí)行dispatchFunction,也就是dispatchDial這一函數(shù).我們可以看到其實(shí)有 很多種類的dispatch function, 比如dispatchVoid,dispatchStrings, dispatchSIM_IO等等, 這些函數(shù)的區(qū)別, 在于Parcel傳入的參數(shù)形式,Void就是不帶參數(shù)的,Strings是以string[]做參數(shù),又如Dial等,有自己的參數(shù)解析方式,以此類推。
request號和參數(shù)現(xiàn)在都有了,那么可以進(jìn)行具體的request函數(shù)調(diào)用了
s_callbacks.onRequest(pRI->pCI->requestNumber, xxx, len,pRI)完成這一操作。
s_callbacks是上篇文章中提到的獲取自libreference-ril的RIL_RadioFunctions 結(jié)構(gòu)指針,request請求在這里轉(zhuǎn)入底層的libreference-ril處理,handler是reference-ril.c中的onRequest。
onRequest進(jìn)行一個(gè)簡單的switch分發(fā),我們依然來看RIL_REQUEST_DIAL,流程是onRequest-->requestDial-->at_send_command-->at_send_command_full-->at_send_command_full_nolock-->writeline。
requestDial中將命令和參數(shù)轉(zhuǎn)換成對應(yīng)的AT命令,調(diào)用公共send command接口 at_send_command。
除了這個(gè)接口之外,還有at_send_command_singleline,at_send_command_sms,at_send_command_multiline等,這是根據(jù)at返回值,以及發(fā)命令流程的類型來區(qū)別 的.比如at+csq這類,需要at_send_command_singleline,而發(fā)送短信,因?yàn)橛衟rompt提示符">",傳裸數(shù)據(jù),結(jié)束符等一系列操作,需要專門用 at_send_command_sms來實(shí)現(xiàn)。
然后執(zhí)行at_send_command_full,前面幾個(gè)接口都會最終到這里,再通過一個(gè)互斥的at_send_command_full_nolock調(diào)用,然后完成最終的寫出操作,在writeline中,寫出到初始化時(shí)打開的設(shè)備中。writeline返回之后,還有一些操作,如保存type等信息,供response回來時(shí)候使用, 以及一些超時(shí)處理.不再詳述。到這里,request的詳細(xì)流程,就分析完畢了。