我们之前说到,ATCommands以处理方式可以分有两类,一类是直接在modem下进行处理的,还有一部分是在AP侧进行处理更加方便有效的。而对于APSide的AT命令,高通也提供了一套框架对齐进行实现,在这我们就这一块做详细的学习。
同样的,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的具体实现:
-
Android.mk:编译一个主进程(ATFWD-daemon)到system/bin下面做实时监听从modem下传来的AT命令。
-
atfwd_daemon.c:主进程的main函数定义,做AT命令的注册已经QMI初始化,并循环监听传来的AT命令并处理返回。
-
sendcmd.cpp:初始化获取binder服务,以此实现将AT命令传到实际处理的地方。
-
IAtCmdFwd.cpp:对binder服务的定义。
下面我将就几个重要的点,简单的介绍ATFWD的代码流程:
-
ATFWD-daemon进程的入口是main()函数;
-
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;
}
- 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流程分析之具体实现