BCM VOIP 启动分析

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; i<MAX_PHYS_ENDPT; i++ )
					//endpt驱动模块创建线路
					vrgEndptCreate
					
					//获取线路能力集
					endptCapabilities( &cmPhysEndpt[i].endptObjState,
&cmPhysEndpt[i].capabilities );
					
					cmPhysEndpt[i].assignedCmEndptId = CMENDPT_UNASSIGNED;
			
			// cmEndpt[i]线路结构体赋值
			ep = &cmEndpt[0];
			for( i = 0 ; i < MAX_ENDPT ; i++ )
				ep->cnxCount = 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 );


 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值