RIL 机制---消息从reference-ril到RIL

来源:互联网 发布:微信数据储存到sd卡里 编辑:程序博客网 时间:2024/06/05 02:05

7, reference-ril -->RIL

7.1 线程阻塞

在上一章的最后的at_send_command_full_nolock函数中,调用writeline将命令写入Modem后,

还做了一个很重要的动作,就是阻塞当前线程,等待Modem回应。

再次回到at_send_command_full_nolock,相关代码如下,

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

1,sp_response是返回给RILC的数据。而while循环中就是在阻塞状态下等待Modem的回应。

2,pthread_cond_timeout_np,第一个参数s_commandcond是一种条件变量,

而条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:

一个线程等待"条件变量的条件成立"而挂起;另一个线程使"条件成立"(给出条件成立信号)。

为了防止竞争,条件变量的使用总是和一个互斥锁结合在一起。 

    在这里,为了等待s_commandcond条件变量而自动阻塞了当前线程。

   那么,只有当另一个线程通过pthread_cond_signal接口是s_commandcond条件满足时,当前阻塞的线程才会被唤醒。

    那么是在哪里去唤醒当前线程的呢?

    分析一下,当发送数据或命令给Modem的时候,阻塞了当前的线程,阻塞的目的就是等待Modem的回应,

而如果Modem有数据上来,那么肯定是先被reference的ReaderLoop检测到并处理,

因此,也应该是在ReaderLoop的消息处理中去唤醒当前阻塞的线程,而且把Modem的反馈传输给阻塞线程。

 

7.2 读取消息

读取消息流程图如下,


mainLoop方法主要部分如下,

at_set_on_reader_closed(onATReaderClosed);at_set_on_timeout(onATTimeout); //初始化AT通道的关闭方法和超时方法ret = at_open(fd, onUnsolicited); /打开AT通信串口并把处理上报消息的onUnsolicited方法传进去RIL_requestTimedCallback(initializeCallback, NULL, &TIMEVAL_0);waitForClose();

上面可以看到,不仅打开了AT通道,而且还设置了超时方法。当前线程在打开AT通道后,

在waitForClose中阻塞等待,如果AT通道在检测超时后,将会主动的关闭当前的AT通道,

此时将会激活waitForClose中的阻塞线程,然后waitForClose将会返回。

而一旦waitForClose函数返回,将会再次进入for循环,重新打开AT通道。

主要是在mainLoop方法的for循环打开串口。

at_open方法主要部分如下,

s_unsolHandler = h;//s_unsolHandler指向onUnsolicited方法//创建线程读取AT命令并处理Modem发过来的信息,入口方法为readerLoopret = pthread_create(&s_tid_reader, &attr, readerLoop, &attr); 

readerLoop方法如下,

static void *readerLoop(void *arg){    for (;;) {        const char * line;        line = readline();//读取命令          if (line == NULL) {            break;        }        if(isSMSUnsolicited(line)) { //上报消息              char *line1;            const char *line2;            // The scope of string returned by 'readline()' is valid only            // till next call to 'readline()' hence making a copy of line            // before calling readline again.            line1 = strdup(line);            line2 = readline();            if (line2 == NULL) {                break;            }            if (s_unsolHandler != NULL) {                s_unsolHandler (line1, line2); //上报消息处理            }            free(line1);        } else {            processLine(line);//进一步处理        }    }    onReaderClosed();//关闭read    return NULL;}

1,首先调用isSMSUnsolicited方法判断是否是上报消息,如果是就直接调用s_unsolHandler进行处理。

2,调用processLine方法对命令进行进一步处理。

isSMSUnsolicited方法如下,

static int isSMSUnsolicited(const char *line){    size_t i;    for (i = 0 ; i < NUM_ELEMS(s_smsUnsoliciteds) ; i++) {        if (strStartsWith(line, s_smsUnsoliciteds[i])) {            return 1;        }    }    return 0;}

s_smsUnsoliciteds定义如下,

static const char * s_smsUnsoliciteds[] = {    "+CMT:",    "+CDS:",    "+CBM:"};

因此,以CMT/CDS/CBM开头的命令肯定是上报消息。

processLine方法如下,

static void processLine(const char *line){    pthread_mutex_lock(&s_commandmutex);    if (sp_response == NULL) {        /* no command pending */        handleUnsolicited(line); //上报消息处理    } else if (isFinalResponseSuccess(line)) {        sp_response->success = 1;        handleFinalResponse(line); //发送回应消息给EventLoop    } else if (isFinalResponseError(line)) {        sp_response->success = 0;        handleFinalResponse(line);    } else if (s_smsPDU != NULL && 0 == strcmp(line, "> ")) {        // See eg. TS 27.005 4.3        // Commands like AT+CMGS have a "> " prompt        writeCtrlZ(s_smsPDU);        s_smsPDU = NULL;    } else switch (s_type) {        case NO_RESULT:            handleUnsolicited(line); //上报消息处理            break;        case NUMERIC:            if (sp_response->p_intermediates == NULL                && isdigit(line[0])            ) {                addIntermediate(line);//回应消息处理            } else {                /* either we already have an intermediate response or                   the line doesn't begin with a digit */                handleUnsolicited(line); //上报消息处理            }            break;        case SINGLELINE:            if (sp_response->p_intermediates == NULL                && strStartsWith (line, s_responsePrefix)            ) {                addIntermediate(line); //回应消息处理            } else {                /* we already have an intermediate response */                handleUnsolicited(line); //上报消息处理            }            break;        case MULTILINE:            if (strStartsWith (line, s_responsePrefix)) {                addIntermediate(line); //回应消息处理            } else {                handleUnsolicited(line); //上报消息处理            }        break;        default: /* this should never be reached */            RLOGE("Unsupported AT command type %d\n", s_type);            handleUnsolicited(line); //上报消息处理        break;    }    pthread_mutex_unlock(&s_commandmutex);}

因此,上报消息是通过handleUnsolicited方法最后通过onUnsolicited方法处(s_unsolHandler指向onUnsolicited),

回应消息有2种处理方法,分别是handleFinalResponse和addIntermediate方法。

7.3 上报消息处理

handleUnsolicited方法如下,

static void handleUnsolicited(const char *line){    if (s_unsolHandler != NULL) {        s_unsolHandler(line, NULL); // s_unsolHandler指向onUnsolicited方法    }}

reference-ril.c的onUnsolicited方法主要如下,

else if (strStartsWith(s, "+CMT:")) {        RIL_onUnsolicitedResponse (RIL_UNSOL_RESPONSE_NEW_SMS, sms_pdu, strlen(sms_pdu));    } else if (strStartsWith(s, "+CDS:")) {        RIL_onUnsolicitedResponse (RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT,            sms_pdu, strlen(sms_pdu));

首先根据不同的命令头封装成不同的命令索引(int型),然后调用RIL_onUnsolicitedResponse函数统一处理,该方法定义如下,

#define RIL_onUnsolicitedResponse(a,b,c) s_rilenv->OnUnsolicitedResponse(a,b,c)

根据3.2小节论述,实际上调用的是ril.cpp的RIL_onUnsolicitedResponse方法,。

该方法在下章中论述。

7.4 回应消息处理

addIntermediate方法如下,

static void addIntermediate(const char *line){    ATLine *p_new;    p_new = (ATLine  *) malloc(sizeof(ATLine));    p_new->line = strdup(line);    /* note: this adds to the head of the list, so the list       will be in reverse order of lines received. the order is flipped       again before passing on to the command issuer */    p_new->p_next = sp_response->p_intermediates;    sp_response->p_intermediates = p_new;}

Modem给的数据可能有很多行,组后一行是OK的标志位,而之前的行都是有效数据。

第一次会调用addIntermediate方法,主要将Modem的有效数据放到sp_response->p_intermediates中。

当有效数据处理完后,再次进入processLine时,会调用handleFinalResponse方法,如下,

static void handleFinalResponse(const char *line){    sp_response->finalResponse = strdup(line); //把回应消息返回给ril    pthread_cond_signal(&s_commandcond);// 唤醒阻塞线程}

到这里,完全符合我们的预期,也就是在ReaderLoop的处理过程中,当modem有回应消息时,

从而使等待在pthread_cond_timeout_np的线程脱离阻塞状态,

同时,把Modem的回应(line)传递给sp_response->p_intermediates。

再次回到at_send_command_full_nolock中阻塞的线程,解除阻塞后,会发出Modem的回应消息,相关代码如下,

if (pp_outResponse == NULL) {        at_response_free(sp_response); //释放sp_response    } else {        /* line reader stores intermediate responses in reverse order */        reverseIntermediates(sp_response); //将回应消息回传给当初发送AT指令的线程        *pp_outResponse = sp_response;    }

首先回顾一下RIL_REQUEST_DIAL的消息发送流程:

requestDial –>at_send_command–>at_send_command_full–>at_send_command_full_nolock

requestDial代码如下,

ret = at_send_command(cmd, NULL);RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);

除了返回失败之外,Modem回应消息的内容有2种,一种返回空消息,另外一种返回查询的内容。RIL_REQUEST_DIAL返回的就是空消息。

RIL_REQUEST_QUERY_NETWORK_SELECTION_MODE消息返回查询内容,对应的requestQueryNetworkSelectionMode方法主要内容如下,

err = at_send_command_singleline("AT+COPS?", "+COPS:", &p_response);//打包AT指令line = p_response->p_intermediates->line;err = at_tok_nextint(&line, &response); //解析Modem返回的消息RIL_onRequestComplete(t, RIL_E_SUCCESS, &response, sizeof(int));//向ril发送modem的返回内容。RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);//查询失败。

其实,一连串流程查询的消息最后通过ATResponse的p_response返回。

解析之后通过RIL_onRequestComplete方法回调到ril.cpp的RIL_onRequestComplete方法。

小结:本章节主要论述了reference-ril从Modem阻塞读取消息的过程,然后对消息进行分类,分别进行处理。

0 0
原创粉丝点击