At Commands:从中级到大师的修炼历程(四)

AT Command流程分析之ATFWD解析
 
    这篇博客是起步于 At Commands学习系列的第二部分,主要是介绍高通实现的ATFWD框架。在这需要说明一下的是,或许你对AT Command很了解了,但是却貌似都不知道ATFWD,这很正常,严格来说,ATFWD都不算属于AT Command框架的一部分,只是对扩展的at命令做的一个扩展实现。

     我们之前说到,ATCommands以处理方式可以分有两类,一类是直接在modem下进行处理的,还有一部分是在AP侧进行处理更加方便有效的。而对于APSideAT命令,高通也提供了一套框架对齐进行实现,在这我们就这一块做详细的学习。

     同样的,AT命令通过ATCoP从串口传来并被解析,而对于APSide的AT命令我们会通过allow_list[]数组注册,这个时候modem会判断传来的命令是不是AP相关的,如果是,通过qmi通讯将AT命令传到AP侧进行处理,而在AP侧的流程便是通过ATFWD框架实现的。

     在vendor下,一般是在vendor/qcom/proprietary/telephony-apps/ATFWD-daemon目录下,有时候也可能存在于vendor/qcom/proprietary/data/ATFWD-daemon下,我们能看到ATFWD的具体实现:

  1. Android.mk:编译一个主进程(ATFWD-daemon)到system/bin下面做实时监听从modem下传来的AT命令。

  2. atfwd_daemon.c:主进程的main函数定义,做AT命令的注册已经QMI初始化,并循环监听传来的AT命令并处理返回。

  3. sendcmd.cpp:初始化获取binder服务,以此实现将AT命令传到实际处理的地方。

  4. IAtCmdFwd.cpp:对binder服务的定义。

下面我将就几个重要的点,简单的介绍ATFWD的代码流程:

  1. ATFWD-daemon进程的入口是main()函数;

  2. tryInit();实现三块的初始化,首先是QMI的初始化,其次是进行QMI连接的初始化,这两块主要是实现能接受到从modem下传上来的At命令。最后是对binder服务的获取初始化,以便能把相应的命令通过binder通讯方式传到相应的地方进行处理。

void tryInit (atfwd_init_type_t type, int *result) {
	LOGI("ATFWD :Going to tryInit ATFWD daemon\n");
    int retryCnt = 1;

    for (; retryCnt <= ATFWD_MAX_RETRY_ATTEMPTS; retryCnt++) {
	LOGI("ATFWD :retryCnt <= ATFWD_MAX_RETRY_ATTEMPTS\n");
        qmiErrorCode = 0;
        switch (type) {
            case INIT_QMI:
		LOGI("ATFWD :Going to qmi_init(atfwdSysEventHandler)\n");
                qmiHandle = qmi_init(atfwdSysEventHandler, NULL);
                *result = qmiHandle;
                break;
            case INIT_QMI_SRVC:
		LOGI("ATFWD :Going to qmi_connection_init(qmiPort, &qmiErrorCode)\n");
                *result = qmi_connection_init(qmiPort, &qmiErrorCode);
                break;
            case INIT_ATFWD_SRVC:
		LOGI("ATFWD :Going to initializeAtFwdService(case INIT_ATFWD_SRVC)\n");
                *result = initializeAtFwdService();
                break;
            default:
                LOGI("Invalid type %d", type);
                return;
        }
        LOGI("ATFWD :result : %d \t ,Init step :%d \t ,qmiErrorCode: %d", *result, type, qmiErrorCode);
        if (*result >= 0 && qmiErrorCode == 0) {
            break;
        }
        sleep(retryCnt * ATFWD_RETRY_DELAY);
    }

    return;
}

  1. 3. initAtcopServiceAndRegisterCommands();初始化并注册所有其添加的At命令;

     

    if (qmiPort) {
        LOGI("ATFWD --> QMI Port : %s\n" , qmiPort);
        initAtcopServiceAndRegisterCommands(qmiPort, &userHandle);
    }

    if (secondaryPort) {
        LOGI("ATFWD --> secondaryPort : %s\n" , secondaryPort);
        initAtcopServiceAndRegisterCommands(secondaryPort, &userHandleSMD);
    }
 

and

       if (regForPrimaryPort == 1) {
            if (qmiPort) {
                LOGI("Rcvd pthread notification for primary QMI port registration");
                initAtcopServiceAndRegisterCommands(qmiPort, &userHandle);
            } else {
                LOGI("Notification for primary QMI port registration when NOT valid, ignore...");
            }
            regForPrimaryPort = 0;
        }

        if (regForSecondaryPort == 1) {
            if (secondaryPort) {
                LOGI("Rcvd pthread notification for secondary QMI port registration");
                initAtcopServiceAndRegisterCommands(secondaryPort, &userHandleSMD);
            } else {
                LOGI("Notification for secondary QMI port registration when NOT valid, ignore...");
            }
            regForSecondaryPort = 0;
        }


   4. 最后函数会循环在while(1)中,当modem下有传来需要处理的命令的时候,newRequest会置为1,走if(newRequest == 1){……};
 

  if (newRequest == 1) {
            LOGI("pthread notified for new request\n");
            response = sendit(&fwdcmd);
            if (response == NULL) {
                sendInvalidCommandResponse();
            } else {
                sendResponse(response);
            }

            if (fwdcmd.name) free(fwdcmd.name);
            if (fwdcmd.tokens) {
                for (i = 0; i < fwdcmd.ntokens; i++) {
                    free(fwdcmd.tokens[i]);
                }
                free(fwdcmd.tokens);
            }
            freeAtCmdResponse(response);
            newRequest = 0;
        }


   5. main()函数下调用sendit(&fwdcmd)函数将命令传递出去,并将返回的结果给response数据结构;
   6. sendit()函数调用processCommand()函数,processCommand是继承于BpInterface类实现的,我们通过Parcel数据将数据写入data下,然后通过调用唤起RPC进行binder数据通讯:remote()->transact(processAtCmd,data, &reply);
 

extern "C" AtCmdResponse *sendit(const AtCmd *cmd)
{
    AtCmdResponse *result;

    if (!cmd) return NULL;

    result = gAtCmdFwdService->processCommand(*cmd);

    return result;
}
 

以上主要就是一个ATCommand在ATFWD下的大致流程了,那么,现在你估计就了解到什么是ATFWD了吧?

简单的说,那就是一个进程,进行数据中转的进程:数据从modem下传上来先通过venderril,再从venderril下传到framework(或者别的什么地方)下进行处理,ATFWD便是venderril下的一个中转站。其中与modem的通讯方式采用QMI,与framework采用bingerserver方式通讯。

接下来的学习:AT Command流程分析之具体实现

  • 0
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值