最近搞udx710,用了ATRouter,对展锐的AT处理逻辑大概做了下梳理,还有ATRouter整体的框架和处理流程。
1.AT处理简要流程
我大概简单说明下,从AT工具(我用的sscom)通过USB AT口发AT到模组内部对应的端口,内部端口由ATRouter管理,ATRouter读取到AT请求,初步解析,根据解析的AT名调用不同的处理函数,由不同的函数处理并调用ATRouter的接口返回响应。如果各分支有相同的AT名,则匹配规则MINIAP的优先级最高,然后是command AT,后面的可以自己看GetATRequestTarget()这个函数。
2. ATRouter框架逻辑梳理
接下来看看ATRouter整体框架逻辑。ATRouter配置文件在/etc/atrouter/system.ini,我们改成了/data/system.ini(原因未知)。
2.1 system.ini配置文件内容
下面是双卡的配置,双卡最多支持六组通路
[Log]
Level=3
[RouterSettings]
SimNum=2
INDChnl=/dev/stty_lte20
[SIM0Settings]
InitNum=3
APChnl0=/dev/ttyGS2
APChnl1=/dev/ttyGS3
APChnl2=/dev/ttyAT0
CPChnl0=/dev/stty_lte4
CPChnl1=/dev/stty_lte5
CPChnl2=/dev/stty_lte6
[SIM1Settings]
InitNum=3
APChnl0=/dev/ttyGS5
APChnl1=/dev/ttyAT8
APChnl2=/dev/ttySE1
CPChnl0=/dev/stty_lte7
CPChnl1=/dev/stty_lte8
CPChnl2=/dev/stty_lte9
[Propertys]
debug=1
debug_key=1234
diag=1
diag_key=1234
virtualcn=0
下面是单卡的配置,双卡最多支持八组通路
[Log]
Level=3
[RouterSettings]
SimNum=1
INDChnl=/dev/stty_lte20
[SIM0Settings]
InitNum=6
APChnl0=/dev/ttyGS2
APChnl1=/dev/ttyGS3
APChnl2=/dev/ttyAT0
APChnl3=/dev/ttyGS5
APChnl4=/dev/ttyAT8
APChnl5=/dev/ttySE1
CPChnl0=/dev/stty_lte4
CPChnl1=/dev/stty_lte5
CPChnl2=/dev/stty_lte6
CPChnl3=/dev/stty_lte7
CPChnl4=/dev/stty_lte8
CPChnl5=/dev/stty_lte9
[SIM1Settings]
InitNum=0
[Propertys]
debug=1
debug_key=1234
diag=1
diag_key=1234
virtualcn=0
2.2 main
首先ATRouter main函数进来创建ATMain对象,调用成员方法BeginRun();
2.2.1 ATMain->BeginRun()
2.2.1.1 GetBaseConfig()
ATMain->BeginRun()中首先调用GetBaseConfig();主要用于获取log等级配置、sim通道个数、主动上报的设备节点路径;
2.2.1.2 CreateModules()
然后调用CreateModules();主要用于创建核心管理对象MiniAPService、DATAHandler、MuxHandler、RouterManger;
如果是单卡模式,还需要创建ModemManager对象。
2.2.1.3 GetRouterConfig()
然后调用GetRouterConfig();主要用于获取sim通道配置,单卡则获取sim0的配置,双卡则获取sim0和sim1的配置;
2.2.1.3.1GetSIMConfig()
在GetRouterConfig()中调用GetSIMConfig()函数,主要用于获取对应sim通路的AT通路个数,每条通路有对应的AP和CP节点,然后默认给通路速率置为115200;
2.2.1.4 执行StarkWrok
创建线程去执行MiniAPService、DATAHandler、MuxHandler对象的StartWork()函数;
如果是单卡模式,则执行ModemManager的StartWrok()函数;
然后执行RouterManger的StartWork()函数;
重点是这几个StartWork函数,下面重点讲一下这几个StartWork
3. 核心对象StartWork
我就按启动顺序讲吧:
3.1 MiniAPService
MiniAPService StartWork主要是用于注册自定义AT。
展锐原生方案是用户添加动态库,实现register_this_lib_ext()注册AT函数,放在/usr/lib/atlib/目录下,由MiniAPService的AT_modules_load()加载动态库中register_this_lib_ext()函数并调用注册。
我们目前没有用展锐原生的逻辑,是在StartWork函数中加了自己的注册AT函数RegCustomAt()。省去了加载动态库这一步骤。
初始化的ofono句柄,会在AT触发时传入注册AT回调函数。
Liot_ATInitApiClient()这个是我们自己加的,目的是做一些初始化,例如AMS客户端初始化之类的,如果有需要在ATRouter初始化功能配置的可以加在这。
然后看看AT回调函数注册,展锐原生的注册AT结构体如下:
typedef struct at_linuxcmd_str {
/* AT名 */
char at_cmd[32];
/* AT回调函数 data为初始化时的ofono句柄,传入的是对应simID的ofono句柄 */
int (*cmd_hdlr)(ATRequest* atrequest, ATResponse* atresponse, void *data);
} at_linuxcmd_t;
因为展锐原生的这套逻辑没有对AT进行解析,所以在收到AT时还需要自己解码,我们自己的回调逻辑是先调用标准解析函数Liot_LATPreParser()预解析AT,将AT类型、参数等信息先解码出来,然后再调用最终对应的AT处理函数。解析完后的AT请求结构体如下:
typedef struct
{
Liot_ATCommandType_E type; /*!< 指令类型 */
int argc; /*!< 参数个数 */
char **argv; /*!< 所有参数 */
void *data; /*!< ofnon句柄 */
char *name; /*!< 指令名称 */
int simId; /*!< SIM卡ID */
}Liot_AtRequest;
3.2 DATAHandler
DATAHandler StartWork主要是用于PPP服务,具体逻辑不太清楚。
3.3 MuxHandler
MuxHandler StartWork主要是接收处理MUX消息。
首先创建线程,线程中启动netlink服务,阻塞接收MUX请求并处理;具体业务不太清楚。
3.4 ModemManager
ModemManager StartWork主要是在单卡模式下接收处理modem状态变化事件,并提供发送CP AT接口。
首先调用Init_CPCmd_Router(),打开CP通路,创建线程读取AT响应,读到响应发送AT响应信号,调用sendCpcmd()函数发送AT后内部会阻塞等待响应信号;
调用CreatSocketConnect()创建socket连接moded;
调用GetBootMode()获取boot状态,这个不是很清楚具体作用。
调用CreateAcceptAndReadThread()函数创建线程阻塞读socket,主要是读取modem状态,modem状态改变时在这里会收到,存储状态到全局变量,发送modem状态改变信号;
调用CreateWaitAndListenThread()函数创建线程阻塞等待modem状态改变信号,当modem状态变为ALIVE时,调用InitModem()函数初始化modem配置,下发modem配置相关AT,需要开机配置的AT列表可以在这里添加。
3.5 RouterManger
RouterManger StartWork属于核心业务,包括创建启动各个通路的ATRouter对象,主动上报逻辑处理,PPP、MUX业务处理等
首先调用GetInitInfo()获取主动上报通路和每个SIMID对应的通路个数,还有总的通路个数到RouterManager全局变量中。
然后调用IniCPChnlInfo()函数创建CP通路节点队列,并将/dev/stty_lte4~/dev/stty_lte10和/dev/stty_lte24入队,具体用法暂时没了解到。
然后调用CreateInitRouter()函数,根据simID创建对应simID下的通路个数个ATRouter对象,并加入到m_sim_router_info_tbl[simID]表中。
然后创建线程执行RouterHandler()函数,这个函数调用RouterHandler(),RouterHandler内部实现主要是调用StartRouter()和阻塞等待读取队列Router相关事件,包括以下几个事件
MESSAGE_TYPE_MESSAGE_ADD /* 新增ATRouter对象 */
MESSAGE_TYPE_MESSAGE_REMOVE /* 删除ATRouter对象 */
MESSAGE_TYPE_MESSAGE_RECOVERFROMMUX /* MUX相关 */
MESSAGE_TYPE_EVENT_PPPTRANSPORT /* PPP相关 */
3.5.1 StartRouter()
因为StartRouter(),内部调用过多,单独讲一下。
这个函数主要功能有四项,三项为展锐原生功能,一项是我们自己添加的,分别是:
1.根据simID启动各ATRouter的BeginRun()函数;
2.根据单双卡不同调用不同的主动上报处理逻辑StartWork(),单卡调用ATIndicationRouter的StartWork(),双卡调用LibatIndication()的StartWork();
3.调用ConnmanIndication的StartWork(),用于接收command进程的主动上报;
4.上报+LIND: AP READY到AT口,标识AT已经就绪,这个是我们自己加的;
3.5.1.1 ATRouter->BeginRun()
ATRouter->BeginRun(),主要是运行ATRouter对象,使其内部逻辑运行。
首先创建线程执行ThreadForStartRun()函数;
ThreadForStartRun()中分别调用InitializeRouter、ATSession->StartLoop()、CPTransChan->StartWork()、PPPTransport->StartWork()、UartChannel->StartWork();
调用InitializeRouter()函数给CP、UART节点(UART节点对应配置文件中APChnl节点)和串口速率赋值。
调用ATSession->StartLoop()创建线程阻塞读取m_session_queue_队列,根据AT名把AT分为七类,分别是发给MINIAP、CMUX、格式类AT、控制类AT、CommanD AT、AP和CP同时处理AT、CP直接处理AT,处理完AT将响应写入m_uart_queue_队列;
调用CPTransChan->StartWork()初始化CP端口;
调用PPPTransport->StartWork()打开PPP通路,创建线程,线程中阻塞接收处理数据。具体业务不清楚;
调用UartChannel->StartWork()打开ATRouter对象的AP端口,并创建读写线程,读线程读AT口请求数据,写入到m_session_queue_队列中,写线程读m_uart_queue_队列节点,读到的数据写回AP通道;
3.5.1.2 ATIndicationRouter->StartWork()
ATIndicationRouter->StartWork(),主要是在单卡模式下接收处理主动上报。
调用OpenCPIndicationChannel()打开CP上报端口;
调用WakeoutMsgQInit()初始化消息队列(具体功能未知,猜测是报给ril);
然后创建线程执行ReadThreadFunc()函数,ReadThreadFunc()中阻塞读取CP上报端口,读到数据调用IndicationSplitBroadcast()广播到AT口;
读取到数据如果是+CRING或者+CMT 调用msgsndf发给对端;
3.5.1.3 LibatIndication->StartWork()
LibatIndication->StartWork(),主要是在双卡模式下接收处理主动上报。
调用WakeoutMsgQInit()初始化消息队列;
注册回调函数为rsp_cmd();
收到主动上报调用HandleIndication(),判断如果是+CRING或者+CMT 调用msgsndf发给对端;调用Liot_ATUrcDeal()过滤上报(此处非展锐原生逻辑,原生逻辑不过滤),然后调用BroadcastIndication()广播到AT口,
3.5.1.4 ConnmanIndication->StartWork()
ConnmanIndication->StartWork(),主要是接收处理command过来的主动上报。
调用client_init()函数连接command,并注册回调函数rsp_connman();
主动上报会触发rsp_connman(),在rsp_connman()中调用RouterManager->BroadcastIndication()广播主动上报;