RILC

摘要:
RILCRIL层的作用基本上是将上层的命令转换为相应的AT指令,并控制调制解调器的操作。RILC如何接收和处理RILJ的请求?RILJ和RILC通过插座连接。前者是客户端,后者是服务器。服务器通过select监听开放套接字端口fd。如果RILJ请求连接,它将调用listenCallback()并接受()以发送s_FdCommand,添加选择侦听数组,这个s_FdCcommand将成为上层传入请求的通道。RILC通过该通道接收特定命令,然后将其转换为AT命令。StaticinprocessCommandBuffer{Parcelp;status_tstatus;int32_task;int32\utoken;RequestInfo*pRI;//构造结构,尤其是pCIntret;p.setData;//获取有效的p//statuscheckeddatenstatus=p.readInt32;//获取请求值status=p.readInt32;if(status!=NO_ERROR){LOGE;return0;}if{LOGE;//FIXME this should perapsrturnareseponserturn0;}pRI=calloc;pRI-˃token=令牌;pRI-˃pCI=&;//确认命令号ret=pthread已经在standby_mutexe_lock上;明确肯定pRI-˃p_next=s_pendingRequests;s_pendingRequests=pRI;ret=pthread_互斥锁解锁;断言;/*sLastDispatchedToken=令牌;*/pRI-˃pCI-˃dispatchFunction;//命令,发射!
RILC

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

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


  

  进入hardware il ild ild.c,一切从main开始。

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

    dlHandle = dlopen(rilLibPath, RTLD_NOW);
    if (dlHandle == NULL) {
        fprintf(stderr, "dlopen failed: %s
", 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
", 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

RILC第3张 View Code

ril_event.c

RILC第4张 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

RILC第11张 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 ilinclude elephonyRil.h

RILC第14张 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

免责声明:文章转载自《RILC》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇技术,项目经理的命?——项目经理的误区(3)(转)Mysql 合并结果接横向拼接字段下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

随便看看

Core Dump

什么是在接收到某些特定信号后终止的核心转储程序?在终止过程中,将生成一个核心文件。核心文件包含程序终止时的内存状态。此过程称为coredump。默认情况下,内核在coredump期间生成的核心文件与程序放在同一目录中,文件名固定为core。通过修改内核参数,我们可以指定内核生成的coredump文件的文件名。应该注意,内核中还有一个与coredump相关的设...

SQL Server中执行正则表达式

如果执行上述函数,将报告以下错误:SQL Server阻塞进程'syssp_可以为OACreate的访问执行以下SQL语句:USEmasterGOsp_configure'showadvancedoptions',1;戈尔配置;GOsp_配置'OleAutomationProcedures',1;戈尔配置;GOsp_配置'AdHocDistributedQu...

解决微信公众平台接口配置信息配置失败问题

填写完URL和TOKEN后,当您单击“提交”时,系统将始终提示您“配置失败”或其他错误,以确认URL所指向的后台页面代码是否正常。请确认TOKEN配置是否正常。此时,请检查INDEX页面的编码格式,并将其更改为GB2312进行尝试,这可能会解决您的问题。我想把这篇文章献给我浮躁的自己。...

Element plus的tree组件实现单选和搜索功能

--elementplus树组件实现单选及搜索功能--˃Elementplus树组件实现单选及搜索功能获取选中的节点//给节点添加classconstcustomNodeClass==˃{if{return'no-checkbox-node';}returnnull;};exportdefault{name:'tree-radio',data(){retur...

java中cookie存取值

Cookie保存值:CookieuserCookie=newCookie(“loginInfo”,loginInfo);userCookie.setMaxAge(30*24*60*60);//生存期为一个月30*24*60*60userCookie.setPath(“/”);response.addCookie(userCookie);Cookie值:Coo...

uniapp之页面间传递和接收数组

uni-app如何在页面之前发送和传递数组?如果阵列是直接发送和传递的,则收到的消息如下所示。无法获取更多的对象值。接收数组对象的参数。您可以首先将数组转换为JSON字符串,然后在将其传递到页面后将其解析为JavaScript对象。...