MTK 6737平臺RILD的實現

1、概述android

      關於RILD的功能,就很少說了,對上服務於Phone進程,也能夠認爲是RILJ層,向下同modem層進行通訊,對MTK平臺來講就是使用AT命令了。架構

2、RILD的架構app

RILD主要由三部分組成,一個是rild.c,第二個是libril這個庫(對於MTK來講就是librilmtk),第三個是廠商關於實現同modem進行通訊的reference-ril庫,對於MTK來講就是mtk-ril。出於保護第三方廠商利益的考慮,這個庫是在rild運行的時候動態加載進去的,因爲運行在同一個進程中,因此rild同reference-ril之間的通訊是函數調用,因此二者之間定義了用於通訊的包含函數指針的結構體。socket

typedef struct {
    int version;        /* set to RIL_VERSION */
    RIL_RequestFunc onRequest;
    RIL_RadioStateRequest onStateRequest;
    RIL_Supports supports;
    RIL_Cancel onCancel;
    RIL_GetVersion getVersion;
    RIL_ReportUsbDisconn reportUsbDisconn;
    RIL_ReportSocketConn reportRILDConn;
} RIL_RadioFunctions;
static struct RIL_Env s_rilEnv = {
    RIL_onRequestComplete,
    RIL_onUnsolicitedResponse,
    RIL_requestTimedCallback
    // For multi channel support
    ,RIL_requestProxyTimedCallback
    ,RIL_queryMyChannelId
    ,RIL_queryMyProxyIdByThread
};

首先介紹下如何RILJ層下來的請求消息是如何調用到第三方庫的,流程以下,對於回調很明顯第三方庫提供具體實現,而libril提供函數指針,這有點相似於面向對象的多態。函數

rild.coop

RIL_register(funcs);//funnc 指向具體的實現,經過註冊使得libril中的指針指向實現

ril.cpp學習

RIL_register (const RIL_RadioFunctions *callbacks) {
 
...

    memcpy(&s_callbacks, callbacks, sizeof (RIL_RadioFunctions));//將callbacks賦值給全局變量s_callbacks
#define CALL_ONREQUEST(a, b, c, d, e) s_callbacks.onRequest((a), (b), (c), (d), (e))
#define CALL_ONSTATEREQUEST(a) s_callbacks.onStateRequest(a)
static void
dispatchSIM_IO (Parcel &p, RequestInfo *pRI) {
    union RIL_SIM_IO {
        RIL_SIM_IO_v6 v6;
        RIL_SIM_IO_v5 v5;
    } simIO;

    int32_t t;
    int size;
    status_t status;

#if VDBG
    RLOGD("dispatchSIM_IO");
#endif
    memset (&simIO, 0, sizeof(simIO));

    // note we only check status at the end

    status = p.readInt32(&t);
    simIO.v6.command = (int)t;

    status = p.readInt32(&t);
    simIO.v6.fileid = (int)t;

    simIO.v6.path = strdupReadString(p);

    status = p.readInt32(&t);
    simIO.v6.p1 = (int)t;

    status = p.readInt32(&t);
    simIO.v6.p2 = (int)t;

    status = p.readInt32(&t);
    simIO.v6.p3 = (int)t;

    simIO.v6.data = strdupReadString(p);
    simIO.v6.pin2 = strdupReadString(p);
    simIO.v6.aidPtr = strdupReadString(p);

    startRequest;
    appendPrintBuf("%scmd=0x%X,efid=0x%X,path=%s,%d,%d,%d,%s,pin2=%s,aid=%s", printBuf,
        simIO.v6.command, simIO.v6.fileid, (char*)simIO.v6.path,
        simIO.v6.p1, simIO.v6.p2, simIO.v6.p3,
        (char*)simIO.v6.data,  (char*)simIO.v6.pin2, simIO.v6.aidPtr);
    closeRequest;
    printRequest(pRI->token, pRI->pCI->requestNumber);

    if (status != NO_ERROR) {
        goto invalid;
    }

    size = (s_callbacks.version < 6) ? sizeof(simIO.v5) : sizeof(simIO.v6);
    CALL_ONREQUEST(pRI->pCI->requestNumber, &simIO, size, pRI, pRI->socket_id);//關於sim卡相關的上層消息分配

 

 

3、關於RILD的啓動ui

n以前的平臺就不說了,網上一搜一大堆,都是在init.rc中啓動的,可是Android N以後因爲init.rc啓動腳本的改動不少deamon程序一下找不到了,如下都是基於mtk 6737n平臺的源碼爲基準的spa

尋找n平臺rild啓動入口.net

init.rc中有一行
import /init.${ro.hardware}.rc
查看屬性值:
[ro.hardware]: [mt6735]

/device/mediatek/mt6735/init.mt6735.rc

其中有一行:
import init.modem.rc

在下面這個rc文件中找到了rild的啓動入口

/device/mediatek/mt6735/init.modem.rc
/device/mediatek/common/init.rilproxy.rc

4、RILD的初始化過程

只說最關鍵的部分,具體怎麼銜接起來的,本身閱讀代碼,由於每一個廠家仍是不同的

dlHandle = dlopen(rilLibPath, RTLD_NOW);//根據路徑打開第三方庫

    if (dlHandle == NULL) {
        RLOGE("dlopen failed: %s", dlerror());
        exit(EXIT_FAILURE);
    }

    RIL_startEventLoop();//最重要的就是啓動子線程循環監聽ril_event
ret = pipe(filedes);

    if (ret < 0) {
        RLOGE("Error in pipe() errno:%d", errno);
        return NULL;
    }

    s_fdWakeupRead = filedes[0];
    s_fdWakeupWrite = filedes[1];

    fcntl(s_fdWakeupRead, F_SETFL, O_NONBLOCK);

    ril_event_set (&s_wakeupfd_event, s_fdWakeupRead, true,
                processWakeupCallback, NULL);

    rilEventAddWakeup (&s_wakeupfd_event);

    // Only returns on error
    ril_event_loop();
    RLOGE ("error in event_loop_base errno:%d", errno);
    // kill self to restart on error

看到eventLoop這個函數中這些封裝的函數主要是來自於Ril_event.cpp,因此搞清這個循環的關鍵是看懂這個類的原理,其實就是個事件鏈表,監聽到發生的event消息,對應調用這個結構體中的回調函數

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;
};

添加事件

// Add event to watch list
void ril_event_add(struct ril_event * ev)
{
    dlog("~~~~ +ril_event_add ~~~~");
    MUTEX_ACQUIRE();
    for (int i = 0; i < MAX_FD_EVENTS; i++) {
        if (watch_table[i] == NULL) {
            watch_table[i] = ev;//ril_event結構全局鏈表
            ev->index = i;
            dlog("~~~~ added at %d ~~~~", i);
            dump_event(ev);
            FD_SET(ev->fd, &readFds);//將ril_event結構體中的成員變量fd添加到全局變量readFds,下面會看到使用select監聽這個fd_set類型的變量
            if (ev->fd >= nfds) nfds = ev->fd+1;
            dlog("~~~~ nfds = %d ~~~~", nfds);
            break;
        }
    }
    MUTEX_RELEASE();
    dlog("~~~~ -ril_event_add ~~~~");
}

監聽事件

void ril_event_loop()
{
    int n;
    fd_set rfds;
    struct timeval tv;
    struct timeval * ptv;


    for (;;) {

        // make local copy of read fd_set
        memcpy(&rfds, &readFds, sizeof(fd_set));
        if (-1 == calcNextTimeout(&tv)) {
            // no pending timers; block indefinitely
            dlog("~~~~ no timers; blocking indefinitely ~~~~");
            ptv = NULL;
        } else {
            dlog("~~~~ blocking for %ds + %dus ~~~~", (int)tv.tv_sec, (int)tv.tv_usec);
            ptv = &tv;
        }
        printReadies(&rfds);
        n = select(nfds, &rfds, NULL, NULL, ptv);//監聽對應的文件描述符是否發生變化
        printReadies(&rfds);

第二個關鍵的函數就是RIL_register

關於怎麼把第三方庫的具體實現填充到本地指針上的前面已經說過,下面看如何監聽socker,獲取rilj發過來的消息

// start listen socket1
    for (i = 0; i < SIM_COUNT; i++) {
        startListen((RIL_SOCKET_ID)(RIL_SOCKET_1+i), &s_ril_param_socket[i]);//RIL_SOCKET_ID是個枚舉結構,以下,這種用法學習了,主要看startListen的實現
    }
typedef enum {
    RIL_SOCKET_1,
#if (SIM_COUNT >= 2)
    RIL_SOCKET_2,
#if (SIM_COUNT >= 3)
    RIL_SOCKET_3,
#endif
#if (SIM_COUNT >= 4)
    RIL_SOCKET_4,
#endif
#endif
    RIL_SOCKET_NUM
} RIL_SOCKET_ID;
static void startListen(RIL_SOCKET_ID socket_id, SocketListenParam* socket_listen_p) {
    int fdListen = -1;
    int ret;
    char socket_name[10];

    memset(socket_name, 0, sizeof(char)*10);

    switch(socket_id) {
        case RIL_SOCKET_1:
            strncpy(socket_name, RIL_getRilSocketName(), 9);
            break;
        case RIL_SOCKET_2:
            strncpy(socket_name, SOCKET2_NAME_RIL, 9);
            break;
        case RIL_SOCKET_3:
            strncpy(socket_name, SOCKET3_NAME_RIL, 9);
            break;
        case RIL_SOCKET_4:
            strncpy(socket_name, SOCKET4_NAME_RIL, 9);
            break;
        default:
            RLOGE("Socket id is wrong!!");
            return;
    }

    RLOGI("Start to listen socket_name: %s, socketId: %s",
            socket_name, rilSocketIdToString(socket_id));

    fdListen = android_get_control_socket(socket_name);//這個是根據socket的名字得到對應的文件描述符,那麼這些socket在哪裏建立的呢,答案是在init進程中建立的,init在啓動rild服務執行fork之後在子進程的返回中會建立socket
    if (fdListen < 0) {
        RLOGE("Failed to get socket %s", socket_name);
        exit(-1);
    }

    ret = listen(fdListen, 4);

    if (ret < 0) {
        RLOGE("Failed to listen on control socket '%d': %s",
             fdListen, strerror(errno));
        exit(-1);
    }
    socket_listen_p->fdListen = fdListen;

    /* note: non-persistent so we can accept only one connection at a time */
    ril_event_set (socket_listen_p->listen_event, fdListen, false,
                listenCallback, socket_listen_p);//從這裏能夠看到當fd發生變化後,也就是收到rilj的消息後調用的是listenCallback

    rilEventAddWakeup (socket_listen_p->listen_event);
}
#define SOCKET_NAME_RIL "rild" //這些均可以和.rc文件中定義的socket對應上,對mtk 6737n能夠去我上面說的那個rc文件中去找
#define SOCKET2_NAME_RIL "rild2"
#define SOCKET3_NAME_RIL "rild3"
#define SOCKET4_NAME_RIL "rild4"
p_info->fdCommand = fdCommand;

    p_rs = record_stream_new(p_info->fdCommand, MAX_COMMAND_BYTES);

    p_info->p_rs = p_rs;

    ril_event_set (p_info->commands_event, p_info->fdCommand, 1,
        p_info->processCommandsCallback, p_info);//本覺得找到了最終處理ril消息的地方,沒想到它只是添加了另外一個ril_event結構體,由這個結構體的回調函數processCommandsCallback處理,而這個函數又會調用到processClientCommandBuffer

    rilEventAddWakeup (p_info->commands_event);
#ifdef MTK_RIL
    {
        enqueue(pRI, buffer, buflen, NULL, socket_id);
    }
#else
    pRI->pCI->dispatchFunction(p, pRI);//這個應該是尋找對應dispatch函數,分析完這個應該就能夠和上面的那個sim卡消息接住了
#endif

而看到每一個ril消息會對應一個處理函數的入口,就應該明白必然是有一張以這種結構體存在的表,而上面提到的函數即是負責爲每個ril消息尋找入口

 

{RIL_REQUEST_SEND_SMS, dispatchStrings, responseSMS},
    {RIL_REQUEST_SEND_SMS_EXPECT_MORE, dispatchStrings, responseSMS},
    {RIL_REQUEST_SETUP_DATA_CALL, dispatchDataCall, responseSetupDataCall},
    {RIL_REQUEST_SIM_IO, dispatchSIM_IO, responseSIM_IO},
    {RIL_REQUEST_SEND_USSD, dispatchString, responseVoid},

關於rilj層監聽rild socket中創建socket如何經過jni調用本地代碼可參考:

http://blog.csdn.net/yangzhihuiguming/article/details/51697801

相關文章
相關標籤/搜索