RIL研究報告

一.整體架構綜述
ril用於實現打電話,發短信等通信業務功能,它主要通過AT命令與硬件模塊交互實現功能。  

這裏一共涉及兩個進程,一個是Phone process,一個是ril進程,Phone顯然是上層進程,ril是負責與硬件打交道的進程。在ril中有四個線程,rild啓動了一個主線程和一個eventLoop線程,主線程一直處於休眠狀態,eventLoop線程在接收到Phone進程的命令之後,會調用ril.so的api函數發送request,這個request實際上就是AT command,也就是說eventLoop線程是發送AT command的線程。ril.so也啓動了兩個線程,一個是mainLoop線程,一個是readerLoop,mainLoop線程主要用於啓動和恢復readerLoop線程,readerLoop線程則用於讀取AT command的響應。下面將詳細分析ril的實現。

二.rild
hardware/ril/rild/rild.c
main函數
----->    dlHandle = dlopen(rilLibPath, RTLD_NOW);
----->    RIL_startEventLoop();
----->    rilInit = (const RIL_RadioFunctions *(*)(const struct RIL_Env *, int, char **))dlsym(dlHandle, "RIL_Init");
----->    funcs = rilInit(&s_rilEnv, argc, rilArgv);
----->    RIL_register(funcs);
該函數主要是載入so文件,然後調用so文件的RIL_Init函數,該函數的作用是使得rild和so文件互相註冊回調函數,它把RIL_Env註冊給so,同時返回RIL_RadioFunctions,使得rild得到一組函數指針,RIL_RadioFunctions和RIL_Env的結構如下:
//so文件交給ril調用的api
typedef struct {
    int version;        /* set to RIL_VERSION */
    RIL_RequestFunc onRequest;
    RIL_RadioStateRequest onStateRequest;
    RIL_Supports supports;
    RIL_Cancel onCancel;
    RIL_GetVersion getVersion;
} RIL_RadioFunctions;

//ril交給so文件調用的callback函數,用於通知某個事件
struct RIL_Env {
    void (*OnRequestComplete)(RIL_Token t, RIL_Errno e,
                           void *response, size_t responselen);
    void (*OnUnsolicitedResponse)(int unsolResponse, const void *data,
                                    size_t datalen);
    void (*RequestTimedCallback) (RIL_TimedCallback callback,
                                   void *param, const struct timeval *relativeTime);
};

static struct RIL_Env s_rilEnv = {
    RIL_onRequestComplete,
    RIL_onUnsolicitedResponse,
    RIL_requestTimedCallback
};

RIL_startEventLoop函數
hardware/ril/libril/ril.cpp
RIL_startEventLoop(void)該函數啓動eventLoop進程
--->eventLoop(void *param)
              ----->    ril_event_init();
              ----->    ret = pipe(filedes);
              ----->    s_fdWakeupRead = filedes[0];
                          s_fdWakeupWrite = filedes[1];
              ----->ril_event_set (&s_wakeupfd_event, s_fdWakeupRead, true,processWakeupCallback, NULL);
                       rilEventAddWakeup (&s_wakeupfd_event);
                                   -------->void ril_event_add(struct ril_event * ev)  //add event to watch_table and rfds read set
                                   -------->static void triggerEvLoop()
              ----------->void ril_event_loop()
                                  for (;;) {
                                     ----->calcNextTimeout//獲取timer_list第一個節點的timeout減去當前時間的差值作爲select的timeout時間
                                     ----->        n = select(nfds, &rfds, NULL, NULL, ptv);//等待讀事件或者超時
                                     ----->processTimeouts//把timer_list中的超時節點取出來放到pending_list中
                                     ----->processReadReadies//把watch_table中所有讀事件置位的節點取出來放到pending_list中
                                     ----->firePending//觸發pending_list中的事件
                                 }
首先要說明的是ril_event結構,它的定義如下,該結構定義了rild的基本事件,其中fd表示一個讀事件,timeout表示一個超時時間,在觸發讀事件或者超時到期後,將會調用func回調函數,這就是ril_event_loop循環所做的事情。
struct ril_event {
    struct ril_event *next;
    struct ril_event *prev;

    int fd;
    int index;
    bool persist;
    struct timeval timeout;
    ril_event_cb func;
    void *param;
};
ril_event_init
對幾個重要的結構進行了初始化,readFds讀事件FD_SET,timer_list超時列表,pending_list是一個臨時列表,watch_table是所有ril_event的列表
void ril_event_init()
{
    MUTEX_INIT();

    FD_ZERO(&readFds);
    init_list(&timer_list);
    init_list(&pending_list);
    memset(watch_table, 0, sizeof(watch_table));
}

RIL_register (const RIL_RadioFunctions *callbacks)
----->    s_fdListen = android_get_control_socket(SOCKET_NAME_RIL);設備路徑是//dev/socket/rild,屬性名是ANDROID_SOCKET_rild
                ret = listen(s_fdListen, 4);
            ril_event_set (&s_listen_event, s_fdListen, false,//這個事件在觸發回調函數後,會被刪除,如果需要再次觸發,需要重新加入事件
                listenCallback, NULL);
            rilEventAddWakeup (&s_listen_event);
    //把rild-debug加入到讀事件列表中
    s_fdDebug = android_get_control_socket(SOCKET_NAME_RIL_DEBUG);
    ret = listen(s_fdDebug, 4);
    ril_event_set (&s_debug_event, s_fdDebug, true,
                debugCallback, NULL);
    rilEventAddWakeup (&s_debug_event);
該函數主要就是加入監聽事件,用於監聽Phone Process的連接

listenCallback
在發生listen的callback的時候,連接會被加入到讀事件列表中,同時監聽事件會從讀事件列表中刪除,這樣就不會有新的連接進來,在連接退出的時候,監聽事件會被重新加入進來
----->    s_fdCommand = accept(s_fdListen, (sockaddr *) &peeraddr, &socklen);
------>     err = getsockopt(s_fdCommand, SOL_SOCKET, SO_PEERCRED, &creds, &szCreds);//這裏所做的檢查是隻接受來自PHONE_PROCESS("radio")進程的連接
------> p_rs = record_stream_new(s_fdCommand, MAX_COMMAND_BYTES);
------>    ril_event_set (&s_commands_event, s_fdCommand, 1,//這個事件會一直存在於讀事件列表中
        processCommandsCallback, p_rs);//
    rilEventAddWakeup (&s_commands_event);//把得到的連接加入到read列表中

processCommandsCallback
void processCommandsCallback(int fd, short flags, void *param)
這個fd即是連接對應的fd,s_fdCommand,param即爲listenCallback中生成的p_rs,p_rs的類型是struct RecordStream,它有一段8k的內存用於從fd中讀取數據,數據的格式是長度加內容
    for (;;) {
        ret = record_stream_get_next(p_rs, &p_record, &recordlen);每次從RecordStream中讀取到的內容稱爲一個record,其格式爲request+參數
            processCommandBuffer(p_record, recordlen);
            ----->   讀取record中的request和token
                        p.setData((uint8_t *) buffer, buflen);
                        status = p.readInt32(&request);
                        status = p.readInt32 (&token);
                        //建立RequestInfo,並把它加入到s_pendingRequests鏈表中
                        pRI = (RequestInfo *)calloc(1, sizeof(RequestInfo));
                        pRI->token = token;
                        pRI->pCI = &(s_commands[request]);//s_commands是一個列表,定義在ril_commands.h中
                        pRI->p_next = s_pendingRequests;
                        s_pendingRequests = pRI;
                        //dispatch函數會根據不同的request類型,在調用s_callbacks.onRequest函數時傳入不同的參數,該函數就是向so文件發送請求.
                        pRI->pCI->dispatchFunction(p, pRI);
}
我們返回去再去看main函數,在調用RIL_startEventLoop函數啓動了eventLoop線程後,該線程由於沒有事件發生,由select函數導致處於休眠狀態。在調用RIL_register函數之後,加入了監聽事件,在Phone進程連接進來後,eventLoop將處於等待Phone進程的命令狀態,在接收到命令之後,會調用dispatchFunction,向so文件發送請求,在後面的分析中,我們知道,實際上就是發送AT命令,也就是說發送AT命令是運行在eventLoop線程上的。

綜上所述,Phone進程和Rild進程通過socket進行通信,它們之間的數據被稱爲RecordStream,rild和so互相註冊回調函 數,rild調用so的RIL_RadioFunctions函數來發送請求,so通過RIL_Env中的函數指針通知rild,請求的響應 (solicit),或者異步事件的發生(unsolicit)。
rild在收到Phone process的command後,使用RIL_RadioFunctions的onRequest發送請求,so通過env的 RIL_onRequestComplete發送響應,該函數會繼續通過socket把響應發送給Phone process。
在異步事件發生的時候,so通過env的RIL_onUnsolicitedResponse把事件發送給Phone process
so也會通過env的RIL_requestTimedCallback,啓動一個定時器,超時後回調它指定的函數。
在rild進程中,有兩個線程,一個是主線程,一個是eventLoop線程,所有的命令都是運行在eventLoop線程

三.libreference-ril.so
reference-ril.c
RIL_Init
const RIL_RadioFunctions *RIL_Init(const struct RIL_Env *env, int argc, char **argv)
----->    s_rilenv = env;//保存rild的env
-----> s_dev_path = optarg;//在模擬器上so使用ttyS0,即串口與模塊通信
----->    ret = pthread_create(&s_tid_mainloop, &attr, mainLoop, NULL);//啓動一個線程

mainLoop
    for (;;) {
                fd = open (s_device_path, O_RDWR);//打開s_device_path設備,即ttyS0設備
                s_closed = 0
        ret = at_open(fd, onUnsolicited);//onUnsolicited會在readerLoop線程中調用
                    ----->    s_fd = fd;
                   ----->    s_unsolHandler = h;
                    ----->    ret = pthread_create(&s_tid_reader, &attr, readerLoop, &attr);//啓動一個新的線程
        RIL_requestTimedCallback(initializeCallback, NULL, &TIMEVAL_0);//在rild的eventLoop線程中,執行initializeCallback
        sleep(1);
        waitForClose();//等待s_state_cond條件,該函數一旦退出,那將導致重新初始化
              ----->    while (s_closed == 0) {
                                  pthread_cond_wait(&s_state_cond, &s_state_mutex);
                          }
}
由此可見,mainLoop線程主要是爲了啓動和恢復readerLoop的。在so中有三個宏需要特別說明一下:
#define RIL_onRequestComplete(t, e, response, responselen) s_rilenv->OnRequestComplete(t,e, response, responselen)
#define RIL_onUnsolicitedResponse(a,b,c) s_rilenv->OnUnsolicitedResponse(a,b,c)
#define RIL_requestTimedCallback(a,b,c) s_rilenv->RequestTimedCallback(a,b,c)
RIL_onRequestComplete
用於在rild發送的請求有響應的時候,回調該函數通知rild
RIL_onUnsolicitedResponse
用於底層有異步事件發生的時候,回調該函數通知rild
RIL_requestTimedCallback
用於請求eventLoop執行一個超時回調
這三個函數指針都是rild註冊給so的,他們分別對應ril.cpp中的
RIL_onRequestComplete(RIL_Token t, RIL_Errno e, void *response, size_t responselen)
該函數會調用sendResponse函數,這個函數會把response通過socket,以Record的格式發送給Phone Process,該函數運行在eventLoop線程上,特別主要,該函數調用了checkAndDequeueRequestInfo函數,該函數將會檢查請求是否在pending隊列中,如果在的話,則將其刪除,因爲已經收到該請求的響應了。
void RIL_onUnsolicitedResponse(int unsolResponse, void *data,
                                size_t datalen)
該函數會調用sendResponse函數,這個函數會把response通過socket,以Record的格式發送給Phone Process,該函數運行在eventLoop線程上
RIL_requestTimedCallback (RIL_TimedCallback callback, void *param,
                                const struct timeval *relativeTime)
該函數會在eventLoop中加入一個超時事件

在mainLoop中就調用了RIL_requestTimedCallback函數,它將會導致initializeCallback函數被調用

initializeCallback
static void initializeCallback(void *param)
    at_handshake();
         -----> err = at_send_command_full_nolock ("ATE0Q0V1", NO_RESULT,NULL, NULL, HANDSHAKE_TIMEOUT_MSEC, NULL);
                                      ----->    err = writeline (command);
                                      ----->    err = pthread_cond_wait(&s_commandcond, &s_commandmutex);//等待s_commandcond條件,在readerLoop線程中讀到OK響應的時候,該條件會置位
    at_send_command
        ----->    err = at_send_command_full_nolock(command, type,
                    responsePrefix, smspdu,
                    timeoutMsec, pp_outResponse);
    。。。
該函數會調用一系列的at_send_command,發送一系列的AT command

readerLoop
mainLoop中啓動了新的線程,該線程是讀取at command的response
static void *readerLoop(void *arg)
{
    for (;;) {
        line = readline();
        if(isSMSUnsolicited(line)) {
            line1 = strdup(line);
            line2 = readline();
            if (s_unsolHandler != NULL) {
                s_unsolHandler (line1, line2);
            }
        } else {
            processLine(line);
        }
    }
    onReaderClosed();
    return NULL;
}
至此,我們對整個AT command的發送和接收,就比較清楚了,rild的eventLoop調用RIL_RadioFunctions.onRequest發送請求,該函數實際上是so中的onRequest函數,它最終調用了at_send_command_full_nolock函數發送AT command,並進入休眠狀態,等待響應。另外一個讀線程readerLoop不斷讀取底層通過ttyS0發來的數據,在確認收到響應後,將喚醒eventLoop線程,繼續後面的請求。

at_send_command_full_nolock
但是除此之外,還需要考慮在發送請求後,一直收不到響應,或者讀線程在讀取數據過程中讀取失敗的請求(例如硬件錯誤?),看如下的分析:
static int at_send_command_full_nolock (const char *command, ATCommandType type,
                    const char *responsePrefix, const char *smspdu,
                    long long timeoutMsec, ATResponse **pp_outResponse)
{
    err = writeline (command);

    s_type = type;
    s_responsePrefix = responsePrefix;
    s_smsPDU = smspdu;
    sp_response = at_response_new();

    while (sp_response->finalResponse == NULL && s_readerClosed == 0) {
        if (timeoutMsec != 0) {
#ifdef USE_NP
            err = pthread_cond_timeout_np(&s_commandcond, &s_commandmutex, timeoutMsec);
#else
            err = pthread_cond_timedwait(&s_commandcond, &s_commandmutex, &ts);
#endif /*USE_NP*/
        } else {
            err = pthread_cond_wait(&s_commandcond, &s_commandmutex);
        }

        if (err == ETIMEDOUT) {
            err = AT_ERROR_TIMEOUT;
            goto error;
        }
    }

    if (pp_outResponse == NULL) {
        at_response_free(sp_response);
    } else {
        /* line reader stores intermediate responses in reverse order */
        reverseIntermediates(sp_response);
        *pp_outResponse = sp_response;
    }

    sp_response = NULL;

    if(s_readerClosed > 0) {
        err = AT_ERROR_CHANNEL_CLOSED;
        goto error;
    }

    err = 0;
error:
    clearPendingCommand();

    return err;
}
該函數是一個阻塞函數,它運行在rild的eventLoop線程中,它指定了條件等待的超時時間,在正常情況下,如果在等待時間內,條件滿足,也就是讀 線程讀到了響應,那麼at命令發送成功。如果等待超時,則要進行超時處理,即clearPendingCommand(),在mainLoop中調用了 at_set_on_timeout(onATTimeout),clearPendingCommand會調用該函數
static void onATTimeout()
{
    LOGI("AT channel timeout; closing\n");
    at_close();//該函數將導致readerLoop線程退出

    s_closed = 1;

    /* FIXME cause a radio reset here */
    setRadioState (RADIO_STATE_UNAVAILABLE);//s_closed和s_state_cond置位將導致mainLoop中的WaitForClose函數退出,繼而導致重新初始化reader線程
}
void at_close()
{
    if (s_fd >= 0) {
        close(s_fd);
    }
    s_fd = -1;

    pthread_mutex_lock(&s_commandmutex);

    s_readerClosed = 1;

    pthread_cond_signal(&s_commandcond);

    pthread_mutex_unlock(&s_commandmutex);

    /* the reader thread should eventually die */
}

在mainLoop中的at_set_on_reader_closed函數,也是爲了防止ttyS0讀取失敗,意外發生,此時也會導致讀線程重新初始化
at_set_on_reader_closed(onATReaderClosed)
static void onATReaderClosed()
{
    LOGI("AT channel closed\n");
    at_close();
    s_closed = 1;

    setRadioState (RADIO_STATE_UNAVAILABLE);
}

四.at command
at命令可以看做是硬件和軟件的接口,不同的硬件廠家提供不同的AT命令集以提供不同的功能,但是at命令的基本格式是固定,它一般通過串口承載數據,當然也不是一定要使用串口,也可以使用其他的承載方式,例如socket。由於at命令是半雙工,即同時只能有一個方向的數據流動,所以一般的情況下,由上層發送AT+CMD,下層發送響應,通常都以\r\n作爲結束,如果內容中有\r\n的話,則需要轉義。另外一種情況是由底層發送請求,一般的格式是+CMD:,此時上層一般不需要發送響應
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章