Fork me on GitHub
RILC

RILC

RIL层的作用大体上就是将上层的命令转换成相应的AT指令,控制modem工作。生产modem的厂家有很多:Qualcomm, STE, Infineon... 不同的厂家都有各自的特点,当然也会有各自不同的驱动,但驱动代码的公开多少会涉及到modem厂家的技术细节,所以,Android系统开源了绝大部分代码,对于 部分驱动(Reference-RIL)  允许厂家以二进制Lib的形式成为一套完整Android系统的一部分。

  有Lib就需要有加载的概念,能够加载各种驱动说明驱动们都遵从一个统一的接口。这个接口是什么?RILC又是如何接收并处理RILJ向下传来的请求?

 


  

  进入hardware\ril\rild\rild.c,一切从main开始。

复制代码
int main(int argc, char **argv)
{
    ... ...

    dlHandle = dlopen(rilLibPath, RTLD_NOW);
    if (dlHandle == NULL) {
        fprintf(stderr, "dlopen failed: %s\n", dlerror());
        exit(-1);
    }

    RIL_startEventLoop();    // ril_event

    rilInit = (const RIL_RadioFunctions *(*)(const struct RIL_Env *, int, char **))dlsym(dlHandle, "RIL_Init");
    if (rilInit == NULL) {
        fprintf(stderr, "RIL_Init not defined or exported in %s\n", rilLibPath);
        exit(-1);

    }
    ... ...

    funcs = rilInit(&s_rilEnv, argc, rilArgv);   // Reference-RIL 获得 LibRIL 的Interface   

    RIL_register(funcs);   // LibRIL 获得 Reference-RIL 的Interface   

}
复制代码

 

  从dlopen看到了动态加载的痕迹,加载Reference-RIL之后便启动了监听线程,也就在RIL_startEventLoop。每一次从上层传来的请求都是一个event,可见要了解该层的消息传输,关键是要了解  结构体 ril_event

  与其相关的文件是ril_event.h、ril_event.cpp,对于文件的分析还是引用ACE1985兄台的博文为好,抱拳为敬。

 

ril_event.h

 View Code

ril_event.c

 View Code

 

若干ril_event构成watch_table数组,同时也被两个双向链表timer_list、pending_list串起来,不禁想起了内核链表。select对watch_table数组上的ril_event们进行监听。

RILJ与RILC通过socket连接,前者为client,后者为server。

  server通过select监听对外开放的socket端口fd,若RILJ请求连接,则回调listenCallback(),accept()出一个s_fdCommand,加入select监听数组,这个s_fdCommand便成为了上层传入请求的通道,RILC通过这个通道接收具体的command,而后转化为AT指令。

 

复制代码
static struct ril_event s_commands_event;
static struct ril_event s_wakeupfd_event;
static struct ril_event s_listen_event;
static struct ril_event s_wake_timeout_event;
static struct ril_event s_debug_event;
复制代码

 

  以上便是大致的思路,select+socket连接的经典模式。通道打通后,从s_fdCommand中到底会接收到什么?

ril_event_set (&s_commands_event, s_fdCommand, 1,
        processCommandsCallback, p_rs);

  

  函数层层嵌套,终会有一个办实事的命令。

复制代码
static int
processCommandBuffer(void *buffer, size_t buflen) {
    Parcel p;
    status_t status;
    int32_t request;
    int32_t token;
    RequestInfo *pRI;    //构造该结构体,尤其是其中的pCI
    int ret;

    p.setData((uint8_t *) buffer, buflen);    //获得有效p

    // status checked at end
    status = p.readInt32(&request);           //取得request值
    status = p.readInt32 (&token);

    if (status != NO_ERROR) {
        LOGE("invalid request block");
        return 0;
    }

    if (request < 1 || request >= (int32_t)NUM_ELEMS(s_commands)) {
        LOGE("unsupported request code %d token %d", request, token);
        // FIXME this should perhaps return a response
        return 0;
    }


    pRI = (RequestInfo *)calloc(1, sizeof(RequestInfo));

    pRI->token = token;
    pRI->pCI = &(s_commands[request]);    //确定早已待命的command号

    ret = pthread_mutex_lock(&s_pendingRequestsMutex);
    assert (ret == 0);

    pRI->p_next = s_pendingRequests;
    s_pendingRequests = pRI;

    ret = pthread_mutex_unlock(&s_pendingRequestsMutex);
    assert (ret == 0);

/*    sLastDispatchedToken = token; */

    pRI->pCI->dispatchFunction(p, pRI);    //命令,发射!

    return 0;
}
复制代码

  Ok,这个办实事的命令就是s_comands数组第request个结构体中的dispatchFunction().

  s_comands数组是个啥?

 

复制代码
static CommandInfo s_commands[] = {
#include "ril_commands.h"
};

typedef struct {
    int requestNumber;
    void (*dispatchFunction) (Parcel &p, struct RequestInfo *pRI);
    int (*responseFunction) (Parcel &p, void *response, size_t responselen);
} CommandInfo;
复制代码

  

  Ref: http://blog.csdn.net/ace1985/article/details/7051522

 View Code

 

RIL中有两种Response类型:

    一是Solicited Response(经过请求的回复),应用的场景是AP主动向BP发送一个AT指令,请求BP进行相应处理并在处理结束时回复一个AT指令通知AP执行的结果。源码中对应的文件是ril_commands.h。

    一是Unsolicited Response(未经请求的回复),应用场景是BP主动向AP发送AT指令,用于通知AP当前系统发生的与Telephony相关的事件,例如网络信号变化,有电话呼入等。源码中对应的文件是ril_unsol_commands.h。

 

复制代码
static UnsolResponseInfo s_unsolResponses[] = {
#include "ril_unsol_commands.h"
};

typedef struct {
    int         requestNumber;
    int         (*responseFunction) (Parcel &p, void *response, size_t responselen);
    WakeType    wakeType;
} UnsolResponseInfo;
复制代码

 

  面对这上百的s_command元素们,顿觉代码的流程并非难点,难在对每一个s_command的理解。

  Ref:hardware\ril\include\telephony\Ril.h

 View Code

 

 打电话,则调用的是:

{RIL_REQUEST_DIAL, dispatchDial, responseVoid},

看来dispatchDial才是办实事的好同志,而dispatchDial中最终调用了s_callbacks,即之前通过 RIL_register(funcs),LibRIL 获得 Reference-RIL 的Interface 。

s_callbacks.onRequest(pRI->pCI->requestNumber, &dial, sizeOfDial, pRI);

至此,终于进入了Reference-RIL。

 

中场休息,做个简单的回顾:

1. 我们构造了RequestInfo,pCI指向了对应的s_commands

复制代码
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;
复制代码

  2. CommandInfo中的dispatchFunction最终调用了Reference-RIL提供的接口。

复制代码
typedef struct {
    int requestNumber;
    void (*dispatchFunction) (Parcel &p, struct RequestInfo *pRI);
    int(*responseFunction) (Parcel &p, void *response, size_t responselen);
} CommandInfo;
复制代码

3. RIL_RadioFunctions 便是RIL对Reference-RIL的实现要求。

复制代码
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;
复制代码

4. onRequest 根据request号做出对应的处理,也就是ril_commands.h。

复制代码
/**
 * RIL_Request Function pointer
 *
 * @param request is one of RIL_REQUEST_*
 * @param data is pointer to data defined for that RIL_REQUEST_*
 *        data is owned by caller, and should not be modified or freed by callee
 * @param t should be used in subsequent call to RIL_onResponse
 * @param datalen the length of data
 *
 */
typedef void (*RIL_RequestFunc) (int request, void *data,
                                    size_t datalen, RIL_Token t);
复制代码

RIL_RadioFunctions需要实现ril_commands.h中定义的request,当然,不一定全部支持。

   

  OK,继续 dialing...

case RIL_REQUEST_DIAL:
       requestDial(data, datalen, t);

终于要见到AT的影子:

复制代码
static void requestDial(void *data, size_t datalen, RIL_Token t)
{
    RIL_Dial *p_dial;
    char *cmd;
    const char *clir;
    int ret;

    p_dial = (RIL_Dial *)data;

    switch (p_dial->clir) {
        case 1: clir = "I"; break;  /*invocation*/
        case 2: clir = "i"; break;  /*suppression*/
        default:
        case 0: clir = ""; break;   /*subscription default*/
    }

    asprintf(&cmd, "ATD%s%s;", p_dial->address, clir);

    ret = at_send_command(cmd, NULL);

    free(cmd);

    /* success or failure is ignored by the upper layer here.
       it will call GET_CURRENT_CALLS and determine success that way */
    RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
}
复制代码

 

  之后的事情便是将AT string通过某种通道发送给BP。至于这个通道的建立,可能是串口也可能是其他,但最终都会表现为一个文件描述符,这就是 rilInit 的事儿了。

以上便是基于Dial的流程浏览,到这一层,重点还是对ril_commands.h, ril_unsol_commands.h的理解,"得此二物者得RIL"!

NEXT, LET'S GO INTO BP.

 

 
 
分类: Communication
posted on 2013-07-03 15:36  HackerVirus  阅读(2668)  评论(0编辑  收藏  举报