VOIP进程启动后,用户线程涉及如下:
1、 和外界通讯的主线程
2、 Callmgr模块线程
3、 Endpt驱动事件处理线程
4、 Endpt驱动RTP/T38/RTCP数据包发送线程
5、 几个RTP数据包接收线程(个数取决于线路端口资源连接数)
6、一个RTCP数据包接收线程(所有资源连接的RTCP接收由一个线程负责处理)
7、协议栈线程???这个不清楚,没有开放源码
VOIP进程启动分析:
上电后,启动脚本拉起SMD进程,SMD进程拉起SSK进程,在ssk_init里,调用initializeVoice进行第一次VOIP启动尝试,这里根据VOIP绑定的接口,判断对应的接口是否已经UP,如果UP,则设置VOIP当前绑定接口的地址信息,并在地址信息设置的RCL回调中,触发VOIP进程启动。
--------------------------------------------------------------------------------------------------------------------------------
main
//系统适配层初始化,不细分析
bosInit();
//主程序启动
sip_start(argc, argv);
//打开endpt模块句柄
vrgEndptDriverOpen()
//初始化日志
cmsLog_init(EID_VODSL);
//关闭endpt模块句柄
vrgEndptDriverClose()
//重新打开endpt模块句柄
vrgEndptDriverOpen()
//初始化消息通讯
cmsMsg_init(EID_VODSL, &msgHandle);
//初始化MDM
cmsMdm_init(EID_VODSL, msgHandle, &shmId);
//初始化CMS锁
vodslCmsMutexLockInit();
//provis_data全局配置结构初始化
provisDefault();
//信号处理
signalRegisterHandler( signalHandler );
//删除所有语音相关的防火墙规则
voicePerformFilterOperation(DELETE_ALL, 0, "", 0, "", 0);
//改变VOIP进程优先级
changeProcessPriority( VOICE_PROCESS_ADJUSTED_PRIORITY );
//根据用户配置更改rtp端口范围
//minRtpPortNum
//maxRtpPortNum
setRtpPortRange();
//callmgr模块初始化,主要挂载一些回调,并标记callmgr模块的状态
//cmCfgBlk.getProvisData = getProvisData;
//cmCfgBlk.setProvisData = setProvisData;
//cmCfgBlk.provisAction = null;
//cmCfgBlk.endptGetProvision = null;
//cmCfgBlk.endptSetProvision = null;
//cmCfgBlk.publishEvent = publishEventCB;
//cmCfgBlk.announcement = null;
//cmCfgBlk.setEndptArchivePtr = gwSetEndptArchivePtr;
//cmCfgBlk.firewallControl = voipQosFirewallCB;
//cmCfgBlk.shutdownComplete = shutdownCompleteCB;
//cmCfgBlk.muteConnection = muteConnectionCB;
//cmCfgBlk.sigPktSnd = cmSigPktSndCb;
//cmCfgBlk.sigPktRcv = cmSigPktRcvCb;
//cmCfgBlk.socketCtrl = cmSocketCtrlCb;
// cmState = CMSTATE_INIT;
vrgCmgrInit(getProvisData,
setProvisData,
NULL,
NULL,
NULL,
publishEventCB,
NULL,
gwSetEndptArchivePtr,
voipQosFirewallCB,
shutdownCompleteCB,
muteConnectionCB);
//启动callmgr模块
vrgCmgrStart( &cmgrTaskId );
//启动callmgr模块任务,该任务的主线程函数为 cmMainThread,单独分析。
bosTaskCreateMain( VRG_CMGR_CFG_TASK_NAME,
VRG_CMGR_CFG_TASK_STACK, VRG_CMGR_CFG_TASK_PRIORITY, NULL,
(void *)cmMainThread, NULL, 0, taskId, FALSE, BOS_TASKSTART_RUNNING);
//等待callmgr模块任务可以工作后,该主线程才会继续执行,在cmMainThread
//主线程函数内部会调用cmPublishEvent( ALL_ENDPT, CMEVT_DEV_ENABLE, 0 ),该
//代码即改变voiceStatus的值。
while (voiceStatus != VOICE_STATUS_INIT)
cmsMsg_receiveWithTimeout(msgHandle, &msgHdr, 0);
while ( 1 )
//通过CMS主消息处理模块接收外部进程消息
cmsMsg_receiveWithTimeout(msgHandle, &msgHdr, 1000)
if (ret == CMSRET_SUCCESS)
//处理外部消息
processCmsMsg(msgHdr);
switch (msgHdr->type)
//重启callmgr及协议栈
case CMS_MSG_RESTART_VODSL_CALLMGR:
//如果FXO口正在使用,则延时重启
if ( (vrgEndptGetNumFxoEndpoints() > 0) &&
(vrgEndptGetHookStatus(&status) != EPSTATUS_SUCCESS ||
status != EPCAS_ONHOOK) )
resMethod = VRGCMGR_RESTART_GRACEFUL;
else
resMethod = VRGCMGR_RESTART_FORCED;
//给callmgr模块发送信息,告诉需要重启
vrgCmgrSignal( 0, VRGCMGR_CMD_RESTART_STACK, &resMethod,
NULL);
provisSetVodslLogLevel();
//当VOIP相关配置节点被修改时,在RCL回调中会发送该配置修
//改消息,这里仅标记一下,待后面再次收到配置保存的消息后,
//会根据此标记,来判断是否需要重启callmgr模块。
case CMS_MSG_VOICE_CONFIG_CHANGED:
voiceChangedFlag = 1 ;
//该消息是在cmPublishEvent( ALL_ENDPT, CMEVT_DEV_ENABLE, 0 )流
//程执行中向CMS订阅的,之后有配置往FLASH中保存则会收到此
//消息。
case CMS_MSG_CONFIG_WRITTEN:
//仅处理voip配置改变
if ( voiceChangedFlag )
//如果FXO口正在使用,则延时重启
if ( (vrgEndptGetNumFxoEndpoints() > 0) &&
(vrgEndptGetHookStatus(&status) != EPSTATUS_SUCCESS ||
status != EPCAS_ONHOOK) )
resMethod = VRGCMGR_RESTART_GRACEFUL;
else
resMethod = VRGCMGR_RESTART_FORCED;
//配置改变后给callmgr模块发送重启信号
vrgCmgrSignal( 0, VRGCMGR_CMD_RESTART_STACK,
&resMethod, NULL);
//标记复位
voiceChangedFlag = 0;
//全局统计信息复位
case CMS_MSG_VOICE_STATISTICS_RESET:
resetStatsFlag = 1;
//给callmgr模块发送统计复位信号
vrgCmgrSignal( (msgHdr->wordData),
VRGCMGR_CMD_RESET_STATS, NULL, NULL);
//收到TR069的呼叫统计查询
case CMS_MSG_VOICE_STATISTICS_REQUEST:
//将当前消息压入队列,并标记需要发送回应,但下面callmgr
//模块处理时,会根据此队列及标记,向TR069发送统计响应。
addToQueue(msgHdr);
sendStatsFlag = 1;
//向callmgr模块发送获取统计信息的信号
vrgCmgrSignal( (msgHdr->wordData), VRGCMGR_CMD_GET_STATS,
NULL, NULL);
//收到TR069的RTP统计查询
case CMS_MSG_VOICE_GET_RTP_STATS:
//获取RTP统计信息,并响应给TR069,这里最终统计实现是DSP
//的接口实现的。
getRtpStats(msgHdr, msgHandle);
//收到TR069 查询呼叫状态及注册状态
case CMS_MSG_VOICE_CM_ENDPT_STATUS:
addToQueue(msgHdr);
vrgCmgrSignal( (msgHdr->wordData),
VRGCMGR_CMD_GET_CM_ENDPT_STATUS, NULL, NULL);
//诊断及调试相关
case CMS_MSG_VOICE_DIAG:
switch (diagMsg->type)
//给endpt驱动模块发送调试命令
case VOICE_DIAG_EPTCMD:
endptcmd(diagMsg->cmdLine);
//更改endpt驱动模块配置profile
case VOICE_DIAG_EPTPROV:
endptprov(diagMsg->cmdLine);
//SLAC寄存器调试,当前6828没有独立SLAC,没有对应处理
case VOICE_DIAG_EPTPROBE:
endptprobe(diagMsg->cmdLine);
//查询VOIP是否已经启动完成
case CMS_MSG_VODSL_IS_READY_FOR_DEINIT:
if (voiceStatus == VOICE_STATUS_INIT)
replyMsg.wordData = TRUE;
else
replyMsg.wordData = FALSE;
cmsMsg_send(msgHandle, &replyMsg);
//查询支持DECT端口数
case CMS_MSG_VOICE_DECT_NUM_ENDPT:
cmsMsg_sendReply( msgHandle, msgHdr,
vrgEndptGetNumDectEndpoints() )
//日志关闭
vrgEndptLogClose();
---------------------------------------------------------------------------------------------------------------------------------
cmMainThread
//显示打印管理块初始化
cmSigDispMgr.dropBuf = 0;
cmSigDispMgr.printBuf = 0;
for( i = 0 ; i < MAX_SIG_DISP_BUFFER ; i++ )
cmSigDispMgr.dispBuf[i].inUse = 0;
cmState = CMSTATE_STARTUP;
//callmgr模块启动
cmStartup();
//Buffer管理初始化
bmMgrInit();
//事件队列初始化
qmInit( &cmEventQueue );
//事件信号
bosSemCreate( "VRGCMGR", BOS_CFG_SEM_INIT_COUNT,
BOS_CFG_SEM_MAX_COUNT, &cmEventSem );
//获取国家码,及电源类型
countryCode = cmProvisionGet(PROVIS_COMMON_COUNTRY_CODE, UNKNOWN));
vrgEndptInitCfg.country = countryCode;
vrgEndptInitCfg.currentPowerSource=cmProvisionGet(
PROVIS_COMMON_POWER_SOURCE,…));
//endpt驱动模块初始化
vrgEndptInit( &vrgEndptInitCfg, cmEndptEventCb, cmIngressPktRecvCb,
cmCfgBlk.endptGetProvision, cmCfgBlk.endptSetProvision, NULL, NULL );
//设置endpt模块事件回调及向外发送媒体包回调
endptUserCtrlBlock.pEventCallBack = cmEndptEventCb;
endptUserCtrlBlock.pPacketCallBack = cmIngressPktRecvCb;
bGlobalTaskExit = FALSE;
//endpt模块初始化
ioctl( endptUserCtrlBlock.fileHandle, ENDPOINTIOCTL_ENDPT_INIT,
&tStartupParam )
//创建endpt模块事件处理任务,该线程主要负责从endpt模块中获取
//底层上报的事件。
bosTaskCreate( "eptEvent", BOS_CFG_TASK_LINUX_DEFAULT_STACK_SIZE,
BOS_CFG_TASK_HIGH_VAL, &EndpointEventTask, 0, &eventTaskId );
//创建endpt模块发包处理任务,该线程主要负责从底层获取包向网络
//侧发送。
bosTaskCreate( "eptPacket", BOS_CFG_TASK_LINUX_DEFAULT_STACK_SIZE,
BOS_CFG_TASK_HIGH_VAL, &EndpointPacketTask, 0, &packetTaskId );
//获取所有类型端口总数
cmNumEndpts = vrgEndptGetNumEndpoints();
//获取FXO口数,当前为0
cmNumFxoEndpts = vrgEndptGetNumFxoEndpoints();
//defProvDataSet配置结构设置为默认值
cmProvisionInitDefault();
//获取callmgr端口数
cmNumEndpts = *((UINT16 *)cmProvisionGet(PROVIS_COMMON_NUMACTIVEPORTS,
UNKNOWN));
cmNumEndpts = (cmNumEndpts > 0 && cmNumEndpts <= MAX_ENDPT) ?
cmNumEndpts : MAX_ENDPT;
//线路初始化
cmEndptInit();
//呼叫业务码初始化
cmCallFeatureInit();
//初始化物理线路
cmPhysEndptInit()
for ( i=0; icnxCount = 0;
ep->regId = UNKNOWN;
ep->curCid = UNKNOWN;
ep->confCid = UNKNOWN;
//注册事件控制块复位
// cmRegEvtPkg.regEvtProfBlk[i]
cmRegEvtPkgInit();
//订阅事件控制块复位
// cmPresAgt.presAgtBlk
cmPresenceInit ();
//设置来显特性
// cmCaller.callerSet = 0;
// cmCaller.generic.disp[0] = CMCALLDISP_ACCEPT_GENERIC;
// cmCaller.generic.ring[0 ] = EPSIG_RINGING;
cmCallerInit()
//呼叫统计信息复位
cmCdrInit();
//呼叫统计代理复位
// cmCsa.state = CMCSASTATE_ACTIVE;
// for( i = 0 ; i < MAX_ENDPT ; i++ )
// cmCsa.csaBlk[i].reqId = UNKNOWN;
// cmCsa.csaBlk[i].endpt = i;
// cmCsa.csaBlk[i].agtType = CMCSATYPE_DISABLED
cmCsaInit();
//callctrl模块配置初始化,并启动协议栈
cmCallConfig()
//临时配置结构赋值
parm
//协议栈请求、应答、超时回调
parm.protcfg.ext.reqcb = cmSipBasReqCB;
parm.protcfg.ext.statuscb = cmSipBasStatusCB;
parm.protcfg.ext.timeoutcb = cmSipBasTimeoutCB;
//协议栈收、发包回调
parm.protcfg.ext.recvcb = cmCfgBlk.sigPktRcv;
parm.protcfg.ext.sendcb = cmCfgBlk.sigPktSnd;
//协议栈SOCKET控制回调
parm.protcfg.ext.socketcb = cmCfgBlk.socketCtrl;
//callctrl模块配置
callConfig( (CCEVTCB)cmEventCallback, &parm );
callgcb.lastCid = -1;
callgcb.state = CCSTS_CFGINPROGRESS;
//将配置存储到呼叫控制块中
ConfigGcb(eventCB, cfgParm);
callgcb.ccevt = eventCB; // cmEventCallback
callgcb.cfgParm = *cfgParm;
//针对callgcb配置控制块的各种存储
callgcb
//初始化协议栈
InitStack();
//协议栈一些扩展配置开关设置
MX_NS CNameAddr::SetForceQuotedDisplayName
((protCfg->ext.extraCfgs & CCCFG_FORCE_QUOTES) != 0);
……
//协议栈一些socket回调设置,最终会关联上面设置的
//callgcb.cfgParm.protcfg.ext.sendcb
//callgcb.cfgParm.protcfg.ext.recvcb
//callgcb.cfgParm.protcfg.ext.socketcb
callSocketInfoConfig();
//配置并启动协议栈
ConfigStack()
//初始化用户代理组件
CSipUaComponentsInitializer::Initialize()
//启动消息处理线程
callgcb.pUAStackCtrl->StartUpThreads()
//启动协议栈
callgcb.pUAStackCtrl->StartUpStack(……)
//协议栈配置
callgcb.pUAStackCtrl->Configure(……)
//设置事件回调,这里的事件回调函数应该是
//ccevt.cpp文件里的SIPCB::EvCalledA等等。
callgcb.pUAStackCtrl->SetUAManagersS(……)
//启动监听端口
callgcb.pUAStackCtrl->OpenListeningPortS(……)
callgcb.state = CCSTS_CFGCMPL;
//callctrl线路级配置
cmUsersConfig( );
//遍历所有非FXO口线路
for( i = 0; i < cmNumEndpts - cmNumFxoEndpts ; i++ )
//这里查找是否存在相同注册地址、相同帐号的线路,如果存在则
//标记这两条线路为兄弟关系,在下面不会再次发起注册,仅仅标记
//已经在服务的状态。
for( j = 0 ; j < i ; j++ )
bosIpAddrCreateFromStr(cmProvisionGet(PROVIS_SIP_REGADDR, j)
,&localaddr )
if( (i != j) && cmEndpt[i].cmSibEpt == UNKNOWN )
if( !bosIpAddrIsZero( &localaddr ) &&
!strcmp( (char *)cmProvisionGet(PROVIS_SIP_USERID, i),
(char *)cmProvisionGet(PROVIS_SIP_USERID, j) ) &&
!strcmp( (char *)cmProvisionGet(PROVIS_SIP_REGADDR, i),
(char *)cmProvisionGet(PROVIS_SIP_REGADDR, j) ) &&
*((UINT16 *)cmProvisionGet(PROVIS_SIP_REGPORT, i)) ==
*((UINT16 *)cmProvisionGet(PROVIS_SIP_REGPORT, j)) )
cmEndpt[i].cmSibEpt = j;
cmEndpt[j].cmSibEpt = i;
sibbling = TRUE;
//如果没有配置用户线路关闭
if ( !(*((UINT8 *)cmProvisionGet(PROVIS_SIP_USER_DISABLED, i)) ) )
//设置数图
callSetDialPlan( cmDigitMap, cmEndpt[i].dialPlan,
MAXDIALPLAN - 1 );
//设置呼叫拒绝数图
callSetDialPlan( cmDigitMap, cmEndpt[i].cbDialPlan, MAXFEATDP - 1 )
//如果用户线路没有关闭,必且没有相同帐号信息
if( !(*((UINT8 *)cmProvisionGet(PROVIS_SIP_USER_DISABLED, i)))
&& !sibbling)
//协议栈注册参数设置
callRegSetup( ®Id, &pu[i] );
cmEndpt[i].regId = regId;
//设置请求URL
callSetParm( regId, CCPARM_GENHDRS, (void *)&headers );
cmEndpt[i].regRetryTimerMs = 0;
cmEndpt[i].regActTimerMs = EPT_REGACT_TIMER;
//调用协议栈,发起注册请求
callRegister( regId, NULL );
//媒体流模块初始化
cmStreamInit()
//初始化rtp栈
rtpInitStack( cmCfgBlk.ipv6Enabled ? BOS_IPADDRESS_TYPE_V6 :
BOS_IPADDRESS_TYPE_V4 );
//生成随机种子
bosTimeGetMs( (BOS_TIME_MS *)&msec )
srand((UINT32)BOS_MS_TO_TICKS(msec));
rtpcb.addressFamily = ipAddressFamily;
//初始化rtp控制块 rtpHandle[i]
for (i = 0; i < VODSL_MAX_CNX; i++)
InitRTPCb(i);
bosCritSectCreate( &critSectionSend );
bosCritSectCreate( &critSectionRecv );
rtcpCreateCritSection();
bosMutexCreate( RTP_MUTEX_NAME, &rtpMutex );
//创建N个RTP接收处理线程,线程主函数为rtpReadThread
for (i = 0; i < VODSL_MAX_CNX; i++)
rtpHandle[i].readThreadId = rtpReadThreadCreate( i );
//创建1个RTCP接收处理线程,线程主函数为rtcpThread
rtpcb.rtcpThreadId = rtcpThreadCreate(rtpHandle));
//设置随机生成端口范围
//minRtpPortNum = DEFAULT_RTP_LOCAL_PORT
//maxRtpPortNum = RTP_MAX_LOCAL_PORT
rtpSetMediaPortRange(DEFAULT_RTP_LOCAL_PORT, RTP_MAX_LOCAL_PORT);
for ( i = 0; i < MAX_CNX; i++ )
cmStream[i].cid = UNKNOWN;
cmStream[i].endpt = UNKNOWN;
for ( i = 0; i < MAX_CALLS; i++ )
cx = &cmCnx[i];
cx->state = CMST_IDLE;
cx->endpt = UNKNOWN;
cx->rtpCnxId = UNKNOWN;
for ( i = 0; i < MAX_CNX; i++ )
//初始化cmRtpCnx[i]相关控制块
//配置本地编码
cmSetCfgCodec();
//遍历所有非FXO口设备
for ( endpt = 0; endpt < (cmNumEndpts - cmNumFxoEndpts) ; endpt++ )
//判断线路没有禁用
if ( !(*((UINT8 *)cmProvisionGet(PROVIS_SIP_USER_DISABLED,
endpt)) ) )
cfgCodecp = &cmCfgCodec[endpt];
cfgCodecLitep = &cmCfgCodecLite[endpt];
//获取用户配置的ptime值,如果小于DSP支持能力,则校正为 //DSP支持的最小能力值。
maxPTime =cmProvisionGet(PROVIS_COMMON_PACKETPERIOD,
endpt)));
maxPTime = (maxPTime <= cmEndpt[endpt].capabilities.pCap[1]) ?
maxPTime : cmEndpt[endpt].capabilities.pCap[1];
//获取用户配置的vad、cng值
vadMode=cmProvisionGet(PROVIS_COMMON_GVADMODE,
endpt));
cngMode=cmProvisionGet(PROVIS_COMMON_CNGMODE, endpt));
//获取用户设置的数据模式配置
vbdEnabled =cmProvisionGet(PROVIS_COMMON_VOICEBANDDATA
, endpt));
if( vbdEnabled == 0 )
dataMode = EPDATAMODE_T38;
else
dataMode = EPDATAMODE_VBD;
cfgCodecp->num = 0;
//当前最大用户编码配置支持6个
for( i = 0 ; i < MAX_VOICE_ENCODER ; i++ )
//依次获取用户配置的编码
codecType = cmProvisionGet2
(PROVIS_COMMON_VOICEENCODER, endpt, i)));
//T38和RFC2833后面单独处理
if (codecType == CODEC_T38 || codecType == CODEC_NTE)
continue;
//如果DSP支持用户配置的编码类型,并且编码类型有效, //则设置对应编码的属性参数
if( cmEndpt[endpt].capabilities.codecCap[codecType] ==
CODEC_SUPPORTED )
codecType = cmRemapCodec( codecType );
if( !cmFilterCodec( cfgCodecp, codecType ))
cfgCodecp->codec[cfgCodecp->num].type =
codecType;
cfgCodecp->codec[cfgCodecp->num].size =
maxPTime;
cfgCodecp->codec[cfgCodecp->num].silsupp =
vadMode ? CCSDPSILSUPP_ON : CCSDPSILSUPP_OFF;
cfgCodecp->codec[cfgCodecp->num].cng =
cngMode;
cfgCodecp->codec[cfgCodecp->num].rtpcode =
CCRTPC_NEGOTIATED;
cfgCodecp->num++;
//设置轻量级编码列表控制块
cfgCodecLitep->num = 2;
for( i = 0; i < cfgCodecLitep->num; i++ )
cfgCodecLitep->codec[i].type = (i == 0) ? CODEC_PCMU :
CODEC_PCMA;
cfgCodecLitep->codec[i].size = maxPTime;
cfgCodecLitep->codec[i].silsupp = vadMode ?
CCSDPSILSUPP_ON : CCSDPSILSUPP_OFF;
cfgCodecLitep->codec[i].cng = cngMode;
cfgCodecLitep->codec[i].rtpcode = CCRTPC_NEGOTIATED;
for ( i = 0; i < CODEC_MAX_TYPES &&
cfgCodecp->num < CC_MAXCODECS; i++ )
codec = i;
//遍历DSP支持的所有编码
if( cmEndpt[endpt].capabilities.codecCap[codec] ==
CODEC_SUPPORTED )
//如果编码有效
codec = cmRemapCodec( codec );
if ( !cmFilterCodec( cfgCodecp, codec ) )
switch( codec )
case CODEC_NTE:
//如果DSP支持rfc2833,则将该编码加入到
//完整编码控制块中及轻量级编码控制块中
case CODEC_T38:
if ( (dataMode >= EPDATAMODE_T38)
&& (dataMode <= EPDATAMODE_T38_MUTE))
//如果DSP支持T38,并且用户配置T38,
//则将T38加入到完整编码控制块中,并保存 //数据模式。
//cmEndpt[endpt].cfgDataMode = dataMode;
default:
if( !( cmProvisionGet(
PROVIS_COMMON_VOICEENCODER_STRICT,
endpt))) )
//如果用户没有配置精确编码开关,则把DSP
//支持的其它编码列表都加入到完整编码控
//制块中。
//如果配置了VBD数据模式,则保存数据模式。
if ( cmEndpt[endpt].cfgDataMode == EPDATAMODE_NONE &&
dataMode >= 1 )
cmEndpt[endpt].cfgDataMode = EPDATAMODE_VBD;
//轻量级编码控制块的优先级参考完整编码控制块的优先级
if ( cfgCodecp->codec[0].type == CODEC_PCMA )
cfgCodecLitep->codec[0].type = CODEC_PCMA;
cfgCodecLitep->codec[1].type = CODEC_PCMU;
//配置VBD数据模式的优先编码
switch(cmProvisionGet(PROVIS_COMMON_PREFVBDCODEC,
endpt)) )
case CM_VBBCODEC_PCMU:
cmEndpt[endpt].cfgFaxPassCodec = CODEC_PCMU;
case CM_VBBCODEC_PCMA:
cmEndpt[endpt].cfgFaxPassCodec = CODEC_PCMA;
case CM_VBBCODEC_NOPREF:
cmEndpt[endpt].cfgFaxPassCodec =
cfgCodecLitep->codec[0].type;
//删除所有endpt连接对象
for (i = 0; i < (MAX_ENDPT - cmNumFxoEndpts); i++)
endptDeleteConnection( &cmEndpt[i].endptObjState, UNKNOWN );
//NAT保活管理初始化
cmNatKeepAliveInit()
//获取NAT保活消息类型及保活时间
cmNatKeepAliveMsg=cmProvisionGet(PROVIS_SIP_NATKEEPALIVE_MESSAGE,
UNKNOWN));
cmNatKeepAliveInterMs = 1000 * cmProvisionGet(
PROVIS_SIP_NATKEEPALIVE_INTERVAL, UNKNOWN)));
cmNatKeepAliveElapsedMs = 0;
//如果保活配置有效
if ( cmNatKeepAliveMsg > 0 && cmNatKeepAliveInterMs > 0 )
//获取用户配置的保活消息目的地址
strncpy(cmNatKeepAliveDst,cmProvisionGet(
PROVIS_SIP_NATKEEPALIVE_DESTINATION, UNKNOWN), 40 );
cmNatKeepAliveDst[MAX_HOSTADDR_LEN-1] = '\0';
//空地址校正
if ( strlen(cmNatKeepAliveDst) == 0 || strcmp(cmNatKeepAliveDst, "0") == 0 )
strcpy(cmNatKeepAliveDst, ZERO_IPADDR);
//如果保活消息地址无效,则依次从外出代理地址、代理地址中找到一个
//有效的地址做为保活消息的发送地址。
if ( strcmp(cmNatKeepAliveDst, ZERO_IPADDR) == 0 )
obProxyAddr=cmProvisionGet(PROVIS_SIP_OBPROXYADDR, UNKNOWN);
if ( strcmp(obProxyAddr, ZERO_IPADDR) != 0 )
strcpy(cmNatKeepAliveDst, obProxyAddr);
else
strcpy(cmNatKeepAliveDst, cmProvisionGet(PROVIS_SIP_PROXYADDR, UNKNOWN));
//补充业务初始化
cmFeatInit()
ep = &cmEndpt[0];
//遍历所有非FXO口
for ( i = 0; i < MAX_ENDPT-cmNumFxoEndpts; i++ )
//如果线路没有关闭
if ( !(*((UINT8 *)cmProvisionGet(PROVIS_SIP_USER_DISABLED, i)) ) )
//初始化用户配置的业务码
for( j = 0 ; j < MAX_FEATURE_CODES ; j++ )
featureMap = &cmFeatureCodeMap[i][j];
if( featureMap->id != UNKNOWN )
sipProvValue = cmProvisionGet2(
PROVIS_COMMON_FEATURESTRING, i, featureMap->provId );
strcpy( featureMap->dialString, sipProvValue );
//初始化用户配置各种业务开关
ep->callwaiting =
cmProvisionGet2(PROVIS_COMMON_FEATURESTRING_STARTED,
i, FEATURE_CODE_CALLWAIT_ON ) ? TRUE : FALSE;
……
cmState = CMSTATE_ACTIVE;
//仅仅打印显示
cmDisplay( UNKNOWN, UNKNOWN, CMLCD_IDLE);
//调用publishEventCB回调函数,执行设备已经开启的处理。在publishEventCB函数中
//主要做了以下事情:
//1、registerInterestInEvent(CMS_MSG_CONFIG_WRITTEN, TRUE, NULL, 0); 向CMS订阅了
//配置保存的事件通知。
//2、setEndptGain(); 设置DSP收发增益
//3、setV18Detection() 向DSP设置V18声音信号检测
//4、voiceStatus = VOICE_STATUS_INIT; 设置callmgr模块启动状态
cmPublishEvent( ALL_ENDPT, CMEVT_DEV_ENABLE, 0 );
while ( TRUE )
//处理callmgr事件队列
rc = bosSemTimedTake( &cmEventSem, VRG_CMGR_CFG_TASK_WAKEUP_MS );
if( rc == BOS_STATUS_RESET )
//当收到复位结果后,执行endpt驱动去初始化等,查了一下用户层的
//bosSemTimedTake代码,好像没有返回BOS_STATUS_RESET这种状态。
vrgEndptDeinit();
cmState = CMSTATE_UNINIT;
bosTaskResetAck();
return;
//没有收到待处理的事件
else if( rc == BOS_STATUS_TIMEOUT )
//计算当前时间与上一次时间时间差
bosTimeGetMs( &osCurrentTime );
bosTimeCalcDeltaMs( &osPrevTime, &osCurrentTime, &osDeltaTime );
elapsedMsec = (int)osDeltaTime;
//保存最近时间
osPrevTime = osCurrentTime;
//回调函数中没有VRGCMGR_EVT_OVERCURRENT_CHK事件处理
for( i = 0 ; i < cmNumEndpts ; i++ )
cmCfgBlk.publishEvent( i, VRGCMGR_EVT_OVERCURRENT_CHK,
(void *)&elapsedMsec );
//数图定时器超时处理,当时间到达后,调用
// cmEndptStateEngine( ep, UNKNOWN, CMEVT_DIGIT_TIMEOUT, 0 );向callmgr
//模块发送数图超时事件。
cmDigitTimer( elapsedMsec );
//线路相关定时器处理
cmEndptTimer( elapsedMsec );
for( ep = 0; ep < cmNumEndpts; ep++ )
//当用户按下话机按键时,该条件至true,并开启统计按下时长, //当用户松开话机按键时,该条件至false。
if( cmEndpt[ ep].dtmfDurCollect )
cmEndpt[ ep].dtmfDurMs += elapsedmsec;
//遇忙重呼业务,激活超时处理
if (cmEndpt[ ep].cmSsBlk.actTimer && (cmEndpt[ ep].cmSsBlk.actTimer
-= elapsedmsec) <= 0)
cmEndpt[ ep].cmSsBlk.actTimer = 0;
cmSSStateEngine( ep, CMEVT_TIMEOUT,
cmEndpt[ep].cmSsBlk.service );
//遇忙重呼业务,发起呼叫超时处理
if (cmEndpt[ ep].cmSsBlk.appTimer && (cmEndpt[ ep].cmSsBlk.appTimer
-= elapsedmsec) <= 0)
cmEndpt[ ep].cmSsBlk.appTimer = 0;
cmSSStateEngine( ep, CMEVT_TIMEOUT,
cmEndpt[ep].cmSsBlk.service );
//遇忙重呼业务,总超时处理
if (cmEndpt[ ep].cmSsBlk.progTimer
&& (cmEndpt[ ep].cmSsBlk.progTimer -= elapsedmsec) <= 0)
cmEndpt[ ep].cmSsBlk.progTimer = 0;
cmSSStateEngine( ep, CMEVT_TIMEOUT,
cmEndpt[ep].cmSsBlk.service );
//热线业务超时处理
if ( cmEndpt[ep].callwarmline && cmEndpt[ep].warmLineTimer &&
(cmEndpt[ep].warmLineTimer -= elapsedmsec) <= 0 )
cmEndpt[ep].warmLineTimer = 0;
cmEndptStateEngine( ep, UNKNOWN, CMEVT_TIMEOUT, 0 );
//各种零碎的补充业务使用该定时器
if ( cmEndpt[ep].timer && (cmEndpt[ep].timer -= elapsedmsec) <= 0 )
cmEndpt[ep].timer = 0;
cmEndptStateEngine( ep, UNKNOWN, CMEVT_TIMEOUT, 0 );
//注册相关定时器处理
cmCnxStateTimer( elapsedMsec );
for( endpt = 0; endpt < cmNumEndpts; endpt++ )
// regActTimerMs定时器主要防止callctrl模块在特殊情况下没有注
//册请求的回应而启动的一个防错定时器。超时后重新发起注册。
if( cmEndpt[endpt].regActTimerMs &&
( cmEndpt[endpt].regActTimerMs -= elapsedmsec ) <= 0 )
cmEndpt[endpt].regActTimerMs = 0;
callRegister( cmEndpt[endpt].regId, NULL );
cmEndpt[endpt].regActTimerMs = EPT_REGACT_TIMER;
//超时或刷新情况下的注册定时器
if ( cmEndpt[endpt].regRetryTimerMs &&
( cmEndpt[endpt].regRetryTimerMs -= elapsedmsec ) <= 0 )
cmEndpt[endpt].regRetryTimerMs = 0;
callRegister( cmEndpt[endpt].regId, NULL );
cmEndpt[endpt].regActTimerMs = EPT_REGACT_TIMER;
//NAT保活定时器
cmNatKeepAliveTimer( elapsedMsec );
//NAT保活配置有效
if ( (cmNatKeepAliveMsg > 0 && cmNatKeepAliveInterMs > 0) &&
strcmp(cmNatKeepAliveDst, "0.0.0.0") != 0 )
cmNatKeepAliveElapsedMs += elapsedmsec;
if ( cmNatKeepAliveElapsedMs >= cmNatKeepAliveInterMs )
//根据保活消息类型,发送指定的NAT保活消息
//当前没有开启出席功能宏,暂不分析
cmPresenceTimer( elapsedMsec );
else
//callmgr有事件处理,提取事件
cmdp = qmDeq( &cmEventQueue )
switch( cmdp->command & CMEVT_CLASS_MASK)
//endpt驱动模块传来的事件,包括
//DTMF按键
//摘挂机、闪断
//第一媒体编码
//传真事件
//T38事件
//VBD事件
//编码切换事件
//线路测试事件等
case CMEVT_CLASS_EPT:
cmProcessEptEvent( cmdp);
//统计信息处理事件
case CMEVT_CLASS_KBD:
switch( cmdp->command )
//CDR
case CMEVT_CLASS_KBD | CMKBD_PRTCDR:
cmCdrPrt( ((int)cmdp->op1 == -1) ? CMCDR_PRT_ALL_ENDPTS :
(int)cmdp->op1, ((int)cmdp->op2 == -1) ? CMCDR_PRT_ALL_LISTS :
(int)cmdp->op2 );
//呼叫统计
case CMEVT_CLASS_KBD | CMKBD_GLOBSTATS:
cmCdrPublishGlobalStats( (int)cmdp->op1 );
//呼叫统计复位
case CMEVT_CLASS_KBD | CMKBD_GLOBSTATS_RESET:
cmCdrResetGlobalStats( (int)cmdp->op1 );
//呼叫及注册状态
case CMEVT_CLASS_KBD | CMKBD_CM_ENDPT_STATUS:
cmPublishEndptStatus( (int)cmdp->op1 );
//callctrl模块处理事件,主要和协议栈相关事件
case CMEVT_CLASS_CALLCTRL:
cmProcessCallEvent( cmdp );
//主要处理外部配置重新下发,及VODSL关闭的处理
case CMEVT_CLASS_PROVIS:
cmProcessProvisEvent( cmdp )
if( cmdp->command == (CMEVT_CLASS_PROVIS |
PROVISEVT_CFG_SHUTDOWN) )
cmCfgBlk.shutdownComplete();
cmFlushEventQueue(&cmEventQueue);
cmState = CMSTATE_UNINIT;
return;
//消息BUF打印处理
case CMEVT_CLASS_DISP_MGR:
cmProcessDispMgrEvent( cmdp->command, cmdp->op1 );
//callmgr内部异步事件处理
case CMEVT_CLASS_INTERNAL:
cmProcessCallManagerEvent( cmdp );
//直接事件处理,当前仅给用户提供了一个命令行发起呼叫的触发接口
case CMEVT_CLASS_DIRECTORY:
cmProcessDirectoryEvent( cmdp );