isEndptDeinitialized = FALSE; //标记endpt已经初始化
initStatus = 0; //初始到哪个阶段,有多位宏表示
initThreadPid = current->pid; //记载当前哪个进程调用了这个模块,当模块出现错误时,给该进程发送信号
//挂载默认断言回调,其中voiceAssertHandler回调函数有向initThreadPid发送信号
xchgAssertSetHandler( voiceAssertHandler );
//参数appinit、appDeinit都为空,主要是里面的监控函数有用
bosAppStart( appInit, appDeinit );
//初始化
boszcb.appTask.init = appInit;
boszcb.appTask.deinit = appDeinit;
boszcb.appTask.stackSize = (16*1024);
boszcb.appTask.taskClass = BOS_CFG_APP_TASK_PRIORITY;
boszcb.appTask.taskArg = 0;
boszcb.appTask.name = BOS_APPTASK_NAME;
//创建一个内核线程,用于监控所有注册的BOS任务正常退出,下面把AppResetRootTask
//单独放外面写
bosTaskCreate( BOS_ROOTTASK_NAME,
(4*1024),
BOS_CFG_ROOT_TASK_PRIORITY,
AppResetRootTask,
0,
&boszcb.rootTaskId );
AppResetRootTask
//设置复位检测模式开启,用于vrgEndptDeinit时判断
//boszcb.resetDetectMode = BOS_RESETDETECT_ENABLED;
bosAppResetDetectionEnable();
//复位任务启动后阻塞在这里,该任务主要和bosAppReset配合,当有调用
// bosAppReset时,bosAppReset函数会释放该信号量,复位任务被激活继
//续向下处理。同时bosAppReset函数被阻塞在复位完成信号量上。
bosSemTake( &boszcb.appResetSemId );
//通知所有注册到BOS的任务进行复位,如果任务是运行状态,则只改变为
//复位标记,如果任务为SUSPENDED状态,则发向恢复事件,并改变复位标//记,当给所有任务改变为复位标记后,阻塞等待所有任务回应,所有任务在
//发现状态改为复位后,则退出并进行回应
TaskResetNotifyAll();
boszcb.resetState = BOS_RESETSTATE_NORESET;//复位完成标记
bosTaskDestroy(&boszcb.rootTaskId); //BOS根任务销毁
//复位完成,释放完成信号量,保证bosAppReset可以正常退出
bosSemGive( &boszcb.appResetCompletedSemId );
bosSleep(1000); //延时
//从用户空间获取参数
copy_from_user( &KArg, (void *)arg, sizeof(KArg) );
copy_from_user( &endptInitCfg, KArg.endptInitCfg, sizeof(endptInitCfg);
//国家码、时钟寄存器的速率
boardHalInitCfg.country = endptInitCfg.country;
boardHalInitCfg.useJapDocsisClock = 0;
//板卡硬件适配层初始化
boardHalInit( &boardHalInitCfg );
//标记这些服务模块需要初始化
//TPD用于线路测试
//HVGRING 与HVG提供振铃电压有关,当前没有使用
// MODULE_FASTSLIC 与SLIC快速模式切换有关,当前没有使用
// MODULE_MSPI SPI初始化,上面注释是为LE88276、SI3226,我们当前是LE9530,所//以后面函数是空的
boardCfgModules = BROADCFG6816_MODULE_TPD |
BROADCFG6816_MODULE_HVGRING |
BROADCFG6816_MODULE_FASTSLIC |
BROADCFG6816_MODULE_MSPI; /* Added for le88276, si3226 */
// sharedReferencePin最后没看到哪个代码用到
// enhancedControl 用于节能模式特性,不清楚
boardHalInitCfg->sharedReferencePin = VRG_FALSE;
boardHalInitCfg->enhancedControl = VRG_FALSE;
boardHalInitUni( boardHalInitCfg, boardCfgModules);
//无源码,上面注释说在分布式的CPU上,将DSP所在的CPU核开启预读数据缓//存,并且给予比HOST核更高的优先级,这里如果HAL运行在CPU0,则DSP就
//在CPU1,反过来一样
xdrvSetTPDCache( (boardHalRunningOnTP0() == VRG_TRUE) ? 1 : 0 );
//获取语音相关参数,这个在endpoint_init时,已经从FLASH中取到
voiceParams = boardHalProvGetVoiceParms();
//内存分配
gHalItpcSyncRecvCmd = malloc( sizeof(XDRV_ITPC_CMD) );
gHalItpcSyncSendCmd = malloc( sizeof(XDRV_ITPC_CMD) );
gHalItpcRecvStatusReg = malloc( sizeof(XDRV_ITPC_STATUS_REG) );
gHalItpcSendStatusReg = malloc( sizeof(XDRV_ITPC_STATUS_REG) );
gHalItpcSharedMemData = malloc( sizeof(XDRV_ITPC_SHARED_MEM_DATA) );
gHalSpinLock = malloc( sizeof(XDRV_SPIN_LOCK) );
gHalDspStackPtr = malloc( XDRV_ITPC_DSP_STACK_PTR_MAX * sizeof(XDRV_ITPC_DSP_STACK_PTR) );
gHalDspStackDump = malloc( sizeof(XDRV_ITPC_DSP_STACK_DUMP) );
gHalshimConfig = (LHAPI_HALSHIM_CONFIG*)malloc( sizeof(halShimConfig) );
//这里设置硬件适配层芯片参数,就是从上面语语音参数voiceParams中获取,
//设置到gHalshimConfig数组中,该数组是线路级相关参数,如第0个通道为
// halShimConfig->halShimMode = HALSHIM_NARROWBAND_MODE;
// halShimConfig->halShimPcmChannels = HALSHIM_PCM_CHANNELS_ONE;
// halShimConfig->halShimType = HAL_SHIM_CONFIG_AUDIO;
// halShimConfig->halShimSampleSize = HALSHIM_SAMPLE_SIZE_16BITS
// halShimConfig->halShimPcmCompMode = HALSHIM_PCM_COMP_ALAW
// halShimConfig->halShimPcmChannelMute = HALSHIM_PCM_CHANNEL_MUTE_OFF
// halShimConfig->halShimByteOrder = HALSHIM_BYTE_ORDER_BIG_ENDIAN
//其它通道类似,其中有几个成员是从语音参数中获取的SLIC相关信息
//最后将所有通道总的数量设置到gHalshimEntryCount中
memset(gHalshimConfig, 0, sizeof(gHalshimConfig));
gHalshimEntryCount = gHalshimEntryCount = bhiPopulateHalshimConfig( gHalshimConfig );
//获取DSP相关支持参数,存入dspCfg
boardHalDspGetCfg(&dspCfg);
//获取当前DSP库的能力及配置,dspImageArchiveGet函数直接从BCM提供的
//DSP库的头文件数据结构中获取。
pCap = &(dspImageArchiveGet()->caps);
pConfig = &(dspImageArchiveGet()->config);
//根据头文件中是否支持G711编码进行配置,下面各种编码类似,就不列了
if ( pCap->g711Support )
{
dspCfg->codecCapabilities[CODEC_PCMU] = CODEC_SUPPORTED;
dspCfg->codecCapabilities[CODEC_PCMA] = CODEC_SUPPORTED;
}
//支持RFC2833
dspCfg->codecCapabilities[CODEC_NTE] = CODEC_SUPPORTED;
//支持RFC2198,最大冗余等级支持2级
dspCfg->codecCapabilities[CODEC_RED] = CODEC_SUPPORTED;
dspCfg->maxRfc2198Level = 2;
//支持两种VBD
vbdCapabilities |= ( EPVBDMODE_LEGACY );
vbdCapabilities |= ( EPVBDMODE_V152 );
dspCfg->vbdCapability = vbdCapabilities;
dspCfg->isIcpEnabled = pCap->icpSupport; //TRUE(空闲CPU profiler)
dspCfg->isDistributed = pConfig->isHauswareDistributed;//TRUE (分布式)
dspCfg->isWidebandEnabled = pConfig->isWidebandEnabled;//TRUE (开启宽带)
dspCfg->frameSyncSamples = pConfig->frameSyncSamples; //40 (采样率)
dspCfg->maxEndPts = pCap->numLineVhds; //7(最大线路数)
dspCfg->maxFullCnxs = pCap->numGatewayVhds; //6(最大连接??)
dspCfg->maxLiteCnxs = pCap->numConferenceVhds; //4(最大会议数)
//什么祯补偿相关,如果是G711编码优先用bvc,其它编码用gplc
dspCfg->gplcSupport = pCap->gplcSupport; //TRUE
dspCfg->bvcSupport = pCap->bvcSupport; //TRUE
dspCfg->isEquEnabled = pCap->equSupport; //FALSE(软均衡器)
// 从GPIO寄存器里读取芯片ID进行校验当前是不是6816的板子,不是就报错
if ( bhiVerifyChipId() != 0 )
{
return ( -1 );
}
//使用HVG振铃
if ( initModules & BROADCFG_MODULE_HVGRING )
bUseHvg = VRG_TRUE;
boardHalApmInit( boardHalInitCfg, XDRV_TRUE, dspCfg.frameSyncSamples, bUseHvg )
//网络参考时钟,函数已被屏蔽,因为使用了GPON驱动提供,不在需要外部
ntrInit();
apmCfg.sampleRate = 16; //采样速率
apmCfg.packetRate = packetRateSamples >> 3; //从秒转为毫秒级,转后为5
apmCfg.useRingGen = useRingGen; //TRUE(使用APM振铃)
apmCfg.useJapDocsisClock = boardHalInitCfg->useJapDocsisClock; //FALSE
//所有语音芯片
while ( voiceParams->voiceDevice[deviceId].voiceDeviceType != BP_VD_NONE )
//判断芯片类型,当前使用的是ZARLINK 9530 SLIC
switch ( voiceParams->voiceDevice[deviceId].voiceDeviceType )
//HVG类型为生降压
boardHalInitCfg->hvgDesignType= XDRV_APM_HVG_DESIGN_BUCKBOOST;
apmCfg.hvgType = XDRV_APM_HVG_DESIGN_BUCKBOOST;
devType = BP_VD_ZARLINK_9530;
//指向静态内存
apmCfg.pFlexiCalc = (APM6816_FLEXICALC_CFG*)dynFlexicalc;
//计算机配置参数设置,这里采样为16000,设备类型为BP_VD_ZARLINK_9530
//,用终指针指向flexicalcWBNORTH_AMERICAArchive9530全局数组,里面有
//振铃电压等参数值
apmCfg.pFlexiCalc = flexiCalcGetCfg6816( boardHalInitCfg->country,
apmCfg.sampleRate * 1000,
devType );
//共享引脚,这里为FALSE
apmCfg.sharedReferencePin = boardHalInitCfg->sharedReferencePin;
//初始化ATM模块,这里设置了一些ATM的回调及寄存器
apm6816_init( &gApmDriver, PERF, &apmCfg )
pDrv->pDrvFuncs = &bcm6816ApmDrvFuncs; //回调
pDrv->pApm = BCM6816_APM_PTR; //APM基址寄存器
pDrv->pIuDma = BCM6816_APM_IUDMA_PTR; //APM的IUDMA寄存器
pDrv->pIntCtl = pIntCtl; //芯片控制寄存器
pDrv->Cfg = *cfgp; //配置参数
//启动APM
apm6816_start(pDrv);
//为所有通道开启APM控制块
pDrv->pIntCtl->blkEnables |= APM_CLK_EN | ACP_A_CLK_EN | ACP_B_CLK_EN | BMU_CLK_EN;
//复拉ACP模块
PERF->softResetB &= ~SOFT_RST_ACP;
APM6816_USLEEP(1000);
PERF->softResetB |= SOFT_RST_ACP;
APM6816_USLEEP(1000);
//DPLL模块初始化
apm_pllStart( pDrv->pApm );
//APM寄存器初始化,里面太多寄存器操作,看不懂
apm6816_regInit( pDrv->pApm, &pDrv->Cfg )
//加载FlexiCalc设置,里面太多寄存器操作,看不懂
apm6816_flexicalcConfig( pDrv->pApm, pDrv->Cfg.pFlexiCalc )
//DMA模块初始化,里面没怎么看,不懂
apm6816_dmaInit( pDrv->pApm, pDrv->pIuDma, pDrv->Cfg.sampleRate, pDrv->Cfg.packetRate )
//初始化HVG模块,里面太多寄存器操作,不懂
hvg6816Init( (XDRV_APM*)&gApmDriver )
//通过之前的deviceChannelMap全局数组参数,填弃chanTsMap变量,记载每个
//通道的时隙
bhiPrepareChannelTimeslotMap( chanTsMap );
//目前不支持ISI,也没有DECT芯片,所以supportedPCMPllMode取默认
//值MODE_REG,这里传入的三个参数在函数里都没用到
pcmInit( chanTsMap, BCM_PCM_CHAN_MASK, supportedPCMPllMode);
//使用基控制器开启PCM相关时钟
PERF->blkEnables |= APM_CLK_EN | ACP_A_CLK_EN | ACP_B_CLK_EN;
PERF->blkEnables |= PCM_CLK_EN | NTP_CLK_EN;
//设置PCM相关功能开启(时钟同步、输入、输出、祯同步)
GPIO->GPIOMode |= GPIO_MODE_APM_CLK | GPIO_MODE_APM_SDIN | GPIO_MODE_APM_SDOUT | GPIO_MODE_APM_FRAME_SYNC;
//设置PCM寄存器,产生PCM时钟,里面好多寄存器操作,看不懂,大概
//知道有设置时钟频率为2.8147MHz,8倍分频
pcm6816_clkInit();
//还是一些寄存器操作,头好大,看不懂,返回了各个通道对着的时隙位置
//以前以为宽带时,一个通道会连续占两个时隙,但代码应该是每个通道间
//阁的获取时隙
chan_mask = pcm6816_regInit();
//根据上面通道时隙参数,开启PCM模块操作,及开启通道的收发
pcm6816_enable( chan_mask );
//操作和DMA相关寄存器,这块看不懂
pcm6816_dmaDescrInit();
pcm6816_iudmaInit();
xdrvSpinLockCreate( gHalSpinLock ); //创建自旋锁,用于共享数据互斥操作
//将全局IPC操作成员状态寄存器、收发命令指向之前已经分配好的内存BUG
//这里状态寄存器用于标识当前命令是否在阻塞处理
gHalItpcSharedMemData->recvStatusReg = gHalItpcRecvStatusReg;
gHalItpcSharedMemData->sendStatusReg = gHalItpcSendStatusReg;
gHalItpcSharedMemData->syncRecvCmd = gHalItpcSyncRecvCmd;
gHalItpcSharedMemData->syncSendCmd = gHalItpcSyncSendCmd;
//封装IPC信号量对象,对象内容主要包括信号量本身,及操作回调
memset( &gHalItpcMutex, 0, sizeof( gHalItpcMutex ) );
gHalItpcMutex.acquireFunc = ItpcMutexAcquire;
gHalItpcMutex.releaseFunc = ItpcMutexRelease;
gHalItpcMutex.data = &gHalItpcBosMutex;
bosMutexCreate( "ITPC-mutex", &gHalItpcBosMutex );
//复位DSP栈调试相关
gHalDspStackDump->stackTraceCount = 0;
gHalDspStackDump->stackPtr = gHalDspStackPtr;
//ITPC各线程通信模块初始化,使用系统软中断实现,发送命令时,填充命令相关
//结构,触发软中断,中断函数收到中断后,遍历所有注册的IPC命令,并执行对
//应的注册回调函数
xdrvItpcInit( INTERRUPT_ID_SOFTWARE_0,
gHalItpcSharedMemData,
gHalSpinLock,
&gHalItpcMutex,
&gHalItpcDrv );
//异常处理任务,函数内部创建了一个内核线程,该线程每次会阻塞在自创建的信
//号量上,同时使用ipc模块机制注册了一个软中断命令,当其它线程触发该软中
//断命令时,命令对应的回调函数仅仅释放信号量,与让该异常处理任务调用设置
//的异常处理回调CmtExceptionCallback执行一次
exceptionHdlrCmtInit( CmtExceptionCallback, NULL, &gHalItpcDrv );
//给动态补偿模块设置了一些回调,但看了一下好像6816产品没有使用
dlbStubCreate( &gHalDlbDrv );
boardHalDspInit()
boot6816Init( &gHalItpcDrv ) //添加一个DSP特殊异常IPC软中断,没代码
//无源码,大概意思就是让DSP第二个线程启动
boot6816LoadImage( NULL, (unsigned int *) NULL);
//初始化DEBUG模块,设置一些回调,注释说用于双核获取DSP核的信息用
debugCmtInterfaceInit(&gHalItpcDrv, &gDebugCmtDrv );
// gIpcSharedMem分配内存
// gIpcSharedMem->chan[i]分配内存
// IPC_gDrv内存清空
// gDuplexDrv内存清空
// gIpcSharedMem->initInfo.newDataFlag = BOARD_HAL_IPC_DATA_READ;标记
boardHalIpcInit(0, 0, 0);
//创建一个5MS触发一次的定时器内核线程,定时器索引为gTickTimer
//定时器的回调句柄为gTimerCallback,当前定时器还未启动,同时回调
//句柄为空指针
boardHalDspTaskInit()
bosTaskCreateEx( DSP_TIMER_TASK_NAME,
DSP_TIMER_TASK_STACKSIZE,
DSP_TIMER_TASK_PRIORITY,
DspTimerTaskInitCB,
DspTimerTaskMain,
DspTimerTaskDeinitCB,
0,
&gTimerTaskId );
boardHalSlicInit(boardHalInitCfg, VRG_FALSE)
boardHalSlicInitApm(boardHalInitCfg, bHighVring); // bHighVring=FALSE
//这里遍历所有语音板卡,目前6816上只有一个zarlink9530芯片
//这里开始初始化SLIC
boardHalSlicInit9530(boardHalInitCfg, bHighVring);
//不清楚
GPIO->GPIOMode &= ~( GPIO_MODE_PCI_Req1_GPIO_16 |
GPIO_MODE_PCI_Gnt1_GPIO_17 |
GPIO_MODE_PCI_Intb_GPIO_18 |
GPIO_MODE_EBI_CS2_GPIO_26 |
GPIO_MODE_SPI_SSN2_GPIO_28 |
GPIO_MODE_SPI_SSN3_GPIO_29 );
//获取之前的APM模块,因为HVG模块通过它引用
pApmDrv = boardHalApmGetDriver( 0 );
//遍历该驱动所有通道,目前有两个
for ( endpt = 0; endpt < BOARD_HAL_6816_NUM_ENDPTS; endpt++ )
//引用全局SLIC设备结构,后面操作SLIC驱动时,直接使用
pDev = &(gSlicDriver[endpt]);
memset( pDev, 0, sizeof( SLIC_6816_L9530_DRV ) );
pDev->chan = -1;
//准备初始化SLIC,其中参数分别为
//pDev SLIC设备对象
//endpt 第几路,从0开始
// gSlicPinInfo SLIC引脚信息,共5个脚,三个控制状态,一
//个用于测试,一个用于查询摘挂机
//pApmDrv APM模块对象
slic6816L9530Init( pDev, endpt, &gSlicPinInfo[endpt], pApmDrv);
//将引脚信息存入驱动对象
memcpy( &pDev->pinInfo, pSlicPinInfo, sizeof(pDev->pinInfo) );
//根据引脚信息,设置好GPIO操作的参数信息(读写控
//制,开关,IO位,以方便后面直接IO操作,这些值存
//驱动对象的pDev->slicCfgInfo中)
SetupGpioControl( pDev, pSlicPinInfo );
//初始化驱动对象
pDev->chan = chan;
pDev->pDrvFuncs = &slicDrvFuncs;
pDev->pApmDrv = pApmDrv;
pDev->bDrvEnabled = XDRV_TRUE;
pDev->bRingDCoffsetEnabled = XDRV_FALSE;
pDev->bEnhancedControl = XDRV_FALSE;
OpenSlic( pDev );
//拉高状态控制
*pDev->slicCfgInfo.slicIoCtrlp|=
pDev->slicCfgInfo.slicIoStateMask;
//拉高TEST LOAD
*pDev->slicCfgInfo.slicIoTldCtrlp|=
pDev->slicCfgInfo.slicIoTldMask;
//关闭TEST LOAD
*pDev->slicCfgInfo.slicIoTldDatap&=
~pDev->slicCfgInfo.slicIoTldMask;
//开启摘挂机检测
*pDev->slicCfgInfo.slicIoNstatCtrlp&=
~(pDev->slicCfgInfo.slicIoNstatMask);
//这个函数首先根据设置状态模式,得到SLIC当前所需
//电压,然后通过APM的高压生成模块HVG输出电压,
//如果HVG模块失败,则将SLIC置为断开状态,成功
//则设置为正常的初始状态
SetState( pDev, L9530_STATE_INIT )
hvg6816Start(endpt)
//暂停电流
hvgp->reg_hvg_duty_cycle &= ~MAX_DUTY_CYCLE;
hvgp->reg_hvg_duty_cycle |= 0x00000020;
//开启对应线路的HVG控制块
if( chan == 0 )
hvgp->reg_hvg_cha_misc &= ~HVG_SOFT_INIT_0;
else
hvgp->reg_hvg_chb_misc &= ~HVG_SOFT_INIT_0;
bosSleep( 20 ); //保证截流成功
//恢复HUV电流输出
hvgp->reg_hvg_duty_cycle &= ~MAX_DUTY_CYCLE;
hvgp->reg_hvg_duty_cycle |= 0x000000A0;
//这里设置SPI控制、复位、继电器的GPIO操作,但因为6816目前都不需要
//设置这三项,所以这里函数用不到
setGpios()
//创建用于操作SPI控制的信号量,防止多进程操作SPI
spiSiInit();
spiZarInit();
//这里是对所有VOIP板卡(SLIC等)驱动进行初始化,但因为6816使用9530
//比较特殊,在上面已经初始化完了,这里就不会再执行
//初始化CAS模块,为了摘挂机历史记录
boardHalCasInit(VRG_FALSE)
//遍历所以通道
for ( endpt = 0; endpt < boardHalGetNumEndpoints(); endpt++ )
//根据当前通道类型,获取对应类型的驱动对象,这里判断什么的都简化
//了,目前取的是之前创建的9530 SLIC驱动对象
slicDrv = boardHalSlicGetDriver( endpt );
//初始化CAS驱动对象
casDriverInit( slicDrv, fastSlicSupported, &gCasDriver[ endpt ]);
//历史记录相关参数初始化
pHistory = &casDriver->casHistory;
pHistory->debounceIntervalMsec = 0;
pHistory->historyEnabled = XDRV_FALSE;
pHistory->currentState = XDRV_CAS_UNKNOWN;
pHistory->currentIntervalMsec = 0;
pHistory->writep = &pHistory->hookStateBuf[0];
pHistory->readp = &pHistory->hookStateBuf[0];
pHistory->prevp = &pHistory->hookStateBuf[0];
for(i = 0; i < XDRV_CAS_HIST_BUFFER_SIZE; i++)
{
pHistory->hookStateBuf[i] = XDRV_CAS_UNKNOWN;
}
//引用将SLIC驱动对象,方便CAS直接操作
casDriver->slicDriver = slicDrv;
//设置SLIC快速状态转换
casDriver->bFastSlicStateModeSupported = fastSlicSupported;//false
casDriver->bFastSlicStateModeEnabled = XDRV_FALSE;
//将所有通道的CAS驱动串连起来
if ( gCasDriverListHead == NULL )
{
gCasDriverListHead = casDriver;
gCasDriverListTail = casDriver;
}
else
{
gCasDriverListTail->nextDrv = casDriver;
gCasDriverListTail = casDriver;
}
//标记当前通道的CAS驱动对象已经启用
casDriver->nextDrv = NULL;
casDriver->bDrvEnabled = XDRV_TRUE;
//启动CAS驱动的摘挂机历史记录功能,参数为
// CAS_BLOCK_RATE_MS CAS状态机每2MS则触发一次
// BOARD_HAL_VCM_SW_DEBOUNCE_MS 摘挂机防抖动值为15MS
//看函数里面功能宏应该没有开启,所以这个函数没有使用
casDriverStartHistoryLog(CAS_BLOCK_RATE_MS,BOARD_HAL_VCM_SW_DEBOUNCE_MS )
//FXO口驱动初始化
boardHalDaaInit( boardHalInitCfg->country );
voiceParams = boardHalProvGetVoiceParms(); //获取语音板卡参数集
//遍历当前所有芯片
for ( deviceId = 0; voiceParams->voiceDevice[deviceId].voiceDeviceType !=
BP_VD_NONE; deviceId++ )
//查找对应FXO芯片类型
switch ( voiceParams->voiceDevice[deviceId].voiceDeviceType )
case BP_VD_ZARLINK_88010: //假如是VP88010芯片
//获取当前空的DAA驱动对象
pDeviceDriver = &gDaaDriverLe88010[daaDriverLe88010index];
//保存SPI设备ID
pDeviceDriver->mspiId =
voiceParams->voiceDevice[deviceId].spiCtrl.spiDevId;
//保存默认PCM为线型模式
pDeviceDriver->pcmMode = LE88010_PCM_MODE_LINEAR;
//如果当前配置通道采样为8比特,则重新设置PCM模式
if ( voiceParams->voiceDevice[deviceId].channel[0].sampleSize ==
BP_VOICE_CHANNEL_SAMPLE_SIZE_8BITS )
if ( voiceParams->voiceDevice[deviceId].channel[0].pcmCompMode
== BP_VOICE_CHANNEL_PCMCOMP_MODE_ULAW )
pDeviceDriver->pcmMode = LE88010_PCM_MODE_ULAW;
else if
( voiceParams->voiceDevice[deviceId].channel[0].pcmCompMode
== BP_VOICE_CHANNEL_PCMCOMP_MODE_ALAW )
pDeviceDriver->pcmMode = LE88010_PCM_MODE_ALAW;
//设置当前通道的收发时隙
pDeviceDriver->txTimeSlot =
voiceParams->voiceDevice[deviceId].channel[0].txTimeslot * 2;
pDeviceDriver->rxTimeSlot =
voiceParams->voiceDevice[deviceId].channel[0].rxTimeslot * 2;
//驱动初始化
daaLe88010Init( locale, daaDriverLe88010index, pDeviceDriver,
voiceParams->voiceDevice[deviceId].resetGpio );
pDev->daaId = daaId; //通道ID
//挂载所有通道API回调函数
pDev->daaInfo.pDrvFuncs = &daaLe88010DrvFuncs;
//根据用户配置,设置临时编码类型变量
switch ( pDev->pcmMode )
case LE88010_PCM_MODE_LINEAR:
codecType = VP_OPTION_LINEAR;
……
//芯片复位
daaLe88010Reset(rstGpioPin);
//创建芯片设备对象,主要设置了一些底层API回调函数
VpMakeDeviceObject(VP_DEV_880_SERIES, pDev->mspiId,
&pDaaDevCtx[0], &pDevObj[0]);
//初始化芯片
VpInitDevice(&pDaaDevCtx[0], DEV_VE880_PROFILE,
VP_PTABLE_NULL, DC_25MA_CC, RING_20HZ_SINE,
VP_PTABLE_NULL, VP_PTABLE_NULL);
//等级初始化成功
for (i = 0; i < DAA_INIT_TIMEOUT; i++)
status = VpApiTick( &pDaaDevCtx[0], &vpApiEventPending );
if ( status == VP_STATUS_SUCCESS )
break;
bosSleep(10);
//如果芯片还未完成,则等待完成,并标记初始化成功
if ( vpApiEventPending == TRUE )
while(VpGetEvent(&pDaaDevCtx[0], &pEvent))
if (pEvent.eventCategory == VP_EVCAT_RESPONSE)
if(pEvent.eventId == VP_DEV_EVID_DEV_INIT_CMP)
daaDeviceInitialized = XDRV_TRUE;
//创建通道对象
VpMakeLineObject(VP_TERM_FXO_GENERIC, 0 , &pLineCtx[0],
&pLineObj[0], &pDaaDevCtx[0]);
//初始化通道
VpInitLine( &pLineCtx[chanNum],
daaLe88010GetLocale(locale, (VpProfileDataType**)gFXOProfiles),
daaLe88010GetLocale(locale, (VpProfileDataType**)gFXODialProfiles), VP_PTABLE_NULL );
//设置通道为回路断开状态
VpSetLineState(&pLineCtx[chanNum], VP_LINE_FXO_LOOP_OPEN );
//设置通道时隙
timeslot.tx = pDev->txTimeSlot;
timeslot.rx = pDev->rxTimeSlot;
VpSetOption( &pLineCtx[chanNum], VP_NULL,
VP_OPTION_ID_TIMESLOT, (void*)×lot );
//设置通道编码类型
VpSetOption( &pLineCtx[chanNum], VP_NULL,
VP_OPTION_ID_CODEC, (void*)&codecType );
daaDriverLe88010index++; //下一个通道索引
//初始化TPD驱动(用于线路测试)
boardHalTpdInit()
//遍历所有通道
for ( endpt = 0; endpt < BOARD_HAL_6816_NUM_ENDPTS; endpt++ )
//分别获取APM、SLIC、DEBUG驱动对象
apmDrv = boardHalApmGetDriver( 0 );
slicDrv = boardHalSlicGetDriver( endpt );
pDebugDriver = boardHalDspGetDebugDriver();
//仅仅赋予各驱动对象指针
tpd6816InitChannel( endpt, slicDrv, apmDrv, pDebugDriver );
pSlicDriver[chan] = slicDrv;
pApmDriver[chan] = apmDrv;
pDebugDriver[chan] = debugDrv;
//VRG线路级初始化
vrgEndptInit( &endptInitCfg, endpointdrvEventCB, endpointdrvPacketCB,
endptProvGetCB, endptProvSetCB, endpointdrvRtpPacketReleaseCB,
endpointdrvTaskShutdownCB );
//配置初始化,这里先查看配置是否标为不同国家指定属性,如果是,则按当前传入的
//国家参数进行COPY,否则查看是否默认值有效,如果默认值有效则使用默认值进行
//COPY
endptProvInitDefaults(country);
//从BCM提供的DSP库的头文件取出DSP相关配置及能力配置到gDspCfg中
boardHalDspGetCfg( &gDspCfg );
//心跳监控模块,一个比较有意思的模块,监控接管的病人,当所有病人都死了,则进
//入节能模式,一种设备级的节能(设置一个标记告诉DSP任务需要暂时),一种是线
//路相关节能(让SLIC停止,但代码中目前不支持),当有一个病人活了时,再退出节
//能模式。里面用到了BOS模块针对任务事件机制的使用。
hbInit();
bosMutexCreate( "hbMu", &hbcb.hbMutex );
bosTaskCreateEx( "HRTBEAT",
(4 * 1024),
HEARTBEAT_CFG_TASK_PRIORITY,
hbTaskInitCB,
hbTaskMain,
hbTaskDeinitCB,
0,
0,
&heartbeatTaskId );
hbcb.notifiedState = HBSTATE_ACTIVE; //通知方向(都死了告诉活的,活了告诉死的)
hbcb.endpointState = HBSTATE_ACTIVE; //线路级状态(活的,还是死的)
hbcb.bInited = VRG_TRUE;//是否初始化标记
hbcb.bExtDeviceActive = VRG_TRUE;//标记当前外部设备的状态(是活动,还是停止)
endptConfig.country = country; //当前为中国
endptConfig.notify = notifyCallback; //DTMF按钮等事件回调
endptConfig.packet = packetCallback; //RTP发包回调
endptConfig.getProvision = getProvisionCallback; //获取配置回调
endptConfig.setProvision = setProvisionCallback; //设置配置回调
endptConfig.packetRelease = packetReleaseCallback; //当前为空
endptConfig.acPowerHBId = -1;
for( i = 0; i < vrgEndptGetNumEndpoints(); i++) //遍历所有线路
memset( &vrgEndpt[i], 0, sizeof( vrgEndpt[i] ));
vrgEndpt[i].lineVhdHdl = 0xffff; //DSP的线路VHD句柄
vrgEndpt[i].casCtlHandle = 0xffff; //CAS控制服务的索引
vrgEndpt[i].pstnVhdHdl = 0xffff; //DSP的FXO类型VHD句柄
vrgEndpt[i].confCnxVhdHdl = 0x00; //会议资源VHD句柄
vrgEndpt[i].country = country; //中国
vrgEndpt[i].casCtlBits = EP_CAS_CTL_LCF; //CAS控制模式
vrgEndpt[i].testMode = TESTMODE_NONE; //线路测试用
vrgEndpt[i].hookStateHBId = -1; //心跳监控的关联句柄
vrgEndpt[i].routedFxsLineId = -1; //FXS口关联的FXS口ID
vrgEndpt[i].dspNum=i/(VRG_ENDPT_CFG_NUM_ENDPT/VRG_GLOBAL_CFG_MAX_NUM_DSP); //DSP数量
vrgEndpt[i].bGR303Support = VRG_FALSE; //RFC2833上报摘挂机等事件
vrgEndpt[i].offhookciding = VRG_FALSE; //指示摘机来显还是挂机来显
//资源控制块初始化,不一一写了
for( i = 0; i < VRG_ENDPT_CFG_NUM_CNX; i++) //遍历所有资源
memset( &vrgCnxState[i], 0, sizeof( vrgCnxState[i] ));
vrgCnxState[i].vrgVoiceJBFixed = VRG_FALSE;
vrgCnxState[i].vrgVoiceJBMin = HAPI_PVE_MIN_JITTER_REG_DEFAULT;
vrgCnxState[i].vrgVoiceJBMax = HAPI_PVE_MAX_JITTER_REG_DEFAULT;
vrgCnxState[i].vrgVoiceJBTarget = HAPI_PVE_TARGET_JITTER_REG_DEFAULT;
vrgCnxState[i].vrgDataJBTarget = HAPI_PVE_VBD_TARGET_JITTER_REG_DEFAULT;
vrgCnxState[i].cnxId = CNX_UNUSED;
vrgCnxState[i].lineId = BAD_ENDPTID;
vrgCnxState[i].mode = EPCNXMODE_INACT;
vrgCnxState[i].voiceMode = VM_IDLE;
vrgCnxState[i].vhdMode = NETMODE_IDLE;
vrgCnxState[i].dataMode = EPDATAMODE_VBD;
vrgCnxState[i].cnxStateHBId = -1;
vrgCnxState[i].dspNum = i / (VRG_ENDPT_CFG_NUM_CNX/VRG_GLOBAL_CFG_MAX_NUM_DSP);
vrgCnxState[i].bMuteAudio = VRG_FALSE;
vrgCnxState[i].bMuteT38 = VRG_FALSE;
vrgCnxState[i].t38param.ecMode = EPT38EC_UDPREDUNDANCY;
vrgCnxState[i].VadMode = -1;
vrgCnxState[i].CngMode = -1;
vrgCnxState[i].PlcMode = -1;
vrgCnxState[i].localSsrc = 0;
vrgCnxState[i].ajcCustomReg = HAPI_PVE_AJC_CUSTOM_DEFAULT;
vrgCnxState[i].faxrCustomReg = HAPI_FAXR_CUSTOM_DEFAULT;
RtpInit( &vrgCnxState[i].rtpstate );
//设置每个DSP默认能力
for( i = 0 ; i < VRG_GLOBAL_CFG_MAX_NUM_DSP ; i++ )
vrgEndptCap[i] = defaultVrgEndptCap;
//向监控模块注册一个电源类型的监管病号,这里根据电源类型来设置病号的初始状态
//态(还活着,还是死了),当前我们传入的电源类型为交流电。如果电源类型为电池
//则需要传入病号状态为活着,以通过临控模块帮忙省电,这里即使是交流电也让注册
//监控病号,注释上说为了可后面切换到电池供电
initPowerState = ( ( endptInitCfg->currentPowerSource ) ? HBSTATE_INACTIVE : HBSTATE_ACTIVE );
hbRegisterPatient( initPowerState, HBPATIENT_TYPE_POWER, HBPATIENT_ALL_ENDPT, &endptConfig.acPowerHBId );
//这里把当前DSP真实能力值设置到vrgEndptCap中,其中gDspCfg是上面从供应商提
//供的DSP库头文件中提出来的能力参数
for( i = 0 ; i < VRG_GLOBAL_CFG_MAX_NUM_DSP ; i++ )
for (j=0; j<CODEC_MAX_TYPES; j++)
vrgEndptCap[i].codecCap[j] = gDspCfg.codecCapabilities[j];
vrgEndptCap[i].vbdCap = gDspCfg.vbdCapability;
vrgEndptCap[i].redLvl = gDspCfg.maxRfc2198Level;
//初始化环型缓冲对象,创建所需的信号量,并将各成员串起来
memCircBufInit()
lhapiInit( gDspCfg.isDistributed );
gIsHauswareDistributed = isHauswareDistributed;//true,记载当前是分布式
//根据是否分布式配置,来设置临界区使用哪种机制,如果是分布式的话
//设置为信号量机制,如果是非分布式的话,设置为同时关断中断的自旋
//锁机制(注释上说如果是非分布式的话,DSP数字采样是在中断上下文,
//DSP在处理时,中断需要关闭)
lhapiCritSecInit();
//创建一个DSP调度任务,用于控制DSP的hausware核的的任务可以触发运行
lhapiThreadDispatchInit();
//调度处理任务,主要处理上层命令及DSP上报的事件
VrgDispatcherProcessInit()
//创建两个队列,使用上面的环型缓冲对象机制
vrgEptCmdQ = VrgQueueCreate( 20, sizeof(VRG_EPT_CMD*) );
vrgEventQ = VrgQueueCreate( 150, sizeof(VRG_EVT) );
//创建信号量,防止多任务调度
bosSemCreate("vrgDispSem", 0, 1, &vrgDispSem)
//核心任务函数,最后单拉文档去了解
bosTaskCreateEx( "VRGDISP", (8 * 1024), tskPrio, NULL, VrgDispatcherTaskMain , NULL, 0, &vrgDispProcessTaskId );
//创建所有endpt任务所需信号量
vrgCreateEndptSemaphores();
bosSemCreate("vrgEptCmdSem", 0, 1, &vrgEptCmdSem);
bosSemCreate("vrgEptConsoleCmdSem", 0, 1, &vrgEptConsoleCmdSem);
bosSemCreate("vrgPstnSem", 0, 1, &vrgPstnSem);
bosSemCreate("vrgMltSem", 0, 1, &vrgMltSem);
bosSemCreate("vrgNTESem", 0, 1, &vrgNteSem);
//刷新SLIC升压模块
boardHalSlicHvgRefresh();
//判断是否开启APM模块,当前已经记为开启
( (boardHalProvGetFlags() & BP_FLAG_DSP_APMHAL_ENABLE) != 0)
//根据SLIC类型重新触发升压模块
return boardHalSlicHvgRefreshApm();
//初始化CAS控制任务,其中参数为回调函数,用于CAS控制任务将事件放入上面
//Dispatcher任务的事件队列中
casCtlInit( CasEventCallback )
memset( &casState, 0, sizeof( casState ));
casState.numChannels = CAS_CTL_CFG_NUM_ENDPTS;
casState.callback = callback; //设置外部事件处理回调
for( i = 0; i < CAS_CTL_CFG_NUM_ENDPTS; i++ )
memset( &casState.service[i], 0, sizeof (CASSRV) );
casState.service[i].callbackp = EventCallback; //该回调函数最终调用callback
//配置CAS线路,主要设置如下
// casState.service[chan].localmem.channelinfo.castype = CASTYPE_FXO
// casState.service[chan].localmem. channelinfo.channelnum = i
// casState.service[chan].localmem. channelinfo.state = FXOBITS_IDLE
// casState.service[chan].localmem. channelinfo.substate = FXO_STARTUP_IDLE;
// casState.service[chan].localmem. channelinfo.statechange = VRG_FALSE;
casCmd( i, (VRG_UINT16)CAS_CONFIG, (VRG_UINT32)CASTYPE_FXO, i );
//分配CAS控制命令队列
memCircBufAlloc(&casCtlCmdQ, 10*sizeof(CASCMD)
bosSemCreate("casSynCmdSem", 0, 1, &casSynCmdSem);
核心任务函数,最后单拉文档去了解
bosTaskCreateEx("HCAS", (8 * 1024), tskPrio, casTaskInitCB, casTaskMain, casTaskDeinitCB, 0, &casState.taskId );
//线路诊断任务初始化
mltInit( MltCallback );
//TDP线路诊断任务初始化
tpdCtlInit(tpdCtlCallback);
// 将MltCallback回调函数注册,该回调函数仅启到中转的作用,而线路诊断
//任务直接调用该回调,该回调再去调用mltCallback回调。
tpdState.callback = callback;
for (i=0; i<TPD_NUM_LINES; i++)
tpdState.isEndptEnabled[i] = VRG_TRUE;
hbRegisterPatient( HBSTATE_INACTIVE, HBPATIENT_TYPE_TPD, i,
&tpdState.hbId[i] ); //向监控注册TDP病人
//创建TDP任务,其中tpdTaskInitCB仅仅创建一个消息队列,tpdTaskDeinitCB
//销销消息队列,tdpTaskMain为主任务函数,详见《EDPT_410_tpdTaskMain》
bosTaskCreateEx( "TPD",(8 * 1024), tskPrio, tpdTaskInitCB, tpdTaskMain,
tpdTaskDeinitCB, 0, &tpdState.taskId );
//将MltCallback回调函数注册,该回调主要将线路诊断结果放置到vrgEventQ队列//中,有VrgDispatcherTaskMain任务统一处理。
mltCallback = callback;
//DSP模块初始化
//资源管理回调函数RmEventCallback,该回调函数判断如果是HAPI_ICP_STATS_EVT事
//件(IDEL CPU 分析状态),则先停止ICP,然后打印出ICP统计信息,然后再复位ICP
//,否则为其它事件,则放到事件队列中,由Dispatcher任务去处理,看了一下在
//Dispatcher任务处理RM相关事件,仅仅是打印一些信息,没有做实际动作。
hdspInit( RmEventCallback, VRG_GLOBAL_CFG_MAX_NUM_DSP );
hapiInit( eventCallback, dspNum ); //初始化DSP硬件,没代码
hdspErrorInit( eventCallback ); //初始化DSP出错句柄模块,仅挂了一个回调
//查看是否要加载均衡器,当前为FALSE
Provision( NULL, EPPROV_LoadBalancing, &provItem );
//开启动态加载均衡器,当前应该是不支持的,里面函数为空
xdrvDlbEnable( dlbDrv, provItem );
//通知DSP,IPC已经可用,里面没代码
for( i = 0 ; i < VRG_GLOBAL_CFG_MAX_NUM_DSP ; i++ )
boardHalIpcReady( i );
//给定时器任务注册定时器超时处理回调lhapi_ProcessEntrySync,这函数没代码,不清
//楚做什么用
boardHalDspRegisterCallback( lhapi_ProcessEntrySync );
//启动5MS的定时器任务,这个定时器在上面很早就建立了,在这里才被触发
boardHalDspTaskStartTimer();
for ( j = 0 ; j < VRG_GLOBAL_CFG_MAX_NUM_DSP ; j++ ) //当前DSP总数为1
for ( i = 0; i < VRG_ENDPT_CFG_NUM_CNX/VRG_GLOBAL_CFG_MAX_NUM_DSP; i++ )
VRG_BOOL bLiteWeightCnx = VRG_FALSE; //标记非轻量级资源
if(i>=((VRG_ENDPT_CFG_NUM_CNX-VRG_ENDPT_CFG_NUM_LITE_CNX)/VRG_GLO BAL_CFG_MAX_NUM_DSP) )
{
bLiteWeightCnx = VRG_TRUE; //如果i值得到轻量级索引,则更新标记
}
InitCnxVhd( &vrgCnxState[i], bLiteWeightCnx, country );
//根据是否轻量级CNX确定VHD类型
if ( bLiteWeightCnx )
vhdType = HAPI_CONFERENCE_VHD;
else
vhdType = HAPI_GATEWAY_VHD;
//分别为
//DTMF CID特定参数
//最小能量值检测
//外部进来的声音
pteAdmin = HAPI_PTE_ADM_GENERAL;
Provision(NULL, EPPROV_PteMinDetectPower, &minDetectPower);
Provision(NULL, EPPROV_PveEgressVolume, &pveEgressVolume);
//创建一个VHD连接,其中CnxVhdEventCallback回调用于DSP
//向外发向数据包(RTP/RTCP/T38),并上报一些资源相关事件(如传真
//信号等)。该回调最后调用的是上面传来的endpointdrvPacketCB回
//调来发送数据包,并把事件存入事件队列,让DISP任务去
//来针对CNX相关事件进行处理。CnxVhdEventCallback后面单独
//拉个文档来写
hdspVhdOpen( vhdType, CnxVhdEventCallback, cnx->dspNum,
pteAdmin, minDetectPower, pveEgressVolume,
&(cnx->vhdhdl) );
//最小能最值检测校正
pteMinDetectPower=hdspVhdAdjustIngressPowerLevel( pteMinDetectPower*10 ) / 10;
//在DSP开启一个通道,无代码
*vhdhdl = hapiOpen( vhdType, callbackp, dspNum )
//根据当前VHD类型为会议或网关类型,来向DSP设置detectAdmin、
// minDetectPower、pveEgressVolume,这里没粘代码,都是HAPI不
//开源代码,没写
//获取网络寄存器配置
hdspSendCmdData( cnx->vhdhdl, HAPINET_CONFIG_GETREGS, HSCMDRESP,
sizeof( netregs ), &netregs);
//去除DTMF中继传输
netregs.hsxPtePacketVoiceOptions &= (~HSNET_PTE_OPTION_DIG_RELAY);
//去除DTMF事件上报
netregs.hsxPtePacketVoiceOptions &= (~HSNET_PTE_OPTION_DIG_EVT);
//去除自切换
netregs.hsxNetOptions&= ~(HSNET_NET_OPTION_AUTO_ENC_SWITCH |
HSNET_NET_OPTION_ECAN_CONTROL |
HSNET_NET_OPTION_FAXRELAY);
//去除DTMF事件上报
netregs.hsxPteCallSetupOptions &= (~HSNET_PTE_OPTION_DIG_EVT);
//开启一些MODEM、自定义声音检测
netregs.hsxCallDOptions |= ( HSNET_CDIS_OPTION_V18ACTIVE |
HSNET_CDIS_OPTION_BELL103ACTIVE |
HSNET_CDIS_OPTION_CUSTOMTONEACTIVE );
//使能上面的配置项
cnx->netRegs = netregs;
hdspSendCmdData( cnx->vhdhdl, HAPINET_CONFIG_SETREGS,
HSCMDEXTDATA_SYNC, sizeof( netregs ), &netregs);
//如果DSP支持T38 if(vrgEndptCap[cnx->dspNum].codecCap[CODEC_T38]==CODEC_SUPPORTED )
//设置T38冗余
SetT38EcMode( cnx, &(cnx->t38param) );
//设置T38服务发送传真事件
hdspSendCmd( cnx->vhdhdl, HAPINET_FAXR_SETREG1, HSCMDDATA,
OFFSETOF( HSZFAXRREGS, hsxfaxrsendevents ), 1 );
//设置T38服务自定义寄存器
hdspSendCmd( cnx->vhdhdl, HAPINET_FAXR_SETREG1, HSCMDDATA,
OFFSETOF( HSZFAXRREGS, hsxcustomfeatures ), cnx->faxrCustomReg );
//配置在以下事件检测到时自动切到VBD
hdspSendCmdAsync( cnx->vhdhdl, HAPI_NET_VBD_AUTOSWITCH_MASK,
HSCMDDATA,
(HAPI_NET_AUTOSWITCH_MASK_V18_BAUDOT|
HAPI_NET_AUTOSWITCH_MASK_V18_PREAMBLE),
0 );
cnx->bLiteWeight = bLiteWeightCnx; //备份当前轻量级标记
SetVoiceMode( cnx, VM_IDLE );
//如果之前是空闲模式,或者新值是空闲模式,复位
//VBD状态
if (( mode == VM_IDLE ) || ( cnx->voiceMode == VM_IDLE ))
ResetVBDataState( &cnx->vbData );
vhdMode = NETMODE_IDLE; //这里switch根据入参仅执行这行
//向DSP设置通道模式
hdspVhdSetMode( cnx->vhdhdl, vhdMode, initMode );
cnx->voiceMode = mode; //保存模式
//向监控任务托管一个病号,病号初始值为死去
hbRegisterPatient( HBSTATE_INACTIVE, HBPATIENT_TYPE_CNX,
HBPATIENT_UNKNOWN_ENDPT, &cnx->cnxStateHBId );
//设置JITTER BUFFER
hdspVhdSetJitterBuffer( cnx->vhdhdl, cnx->vrgVoiceJBFixed,
cnx->vrgVoiceJBMin, cnx->vrgVoiceJBMax,
cnx->vrgVoiceJBTarget, cnx->vrgDataJBTarget, cnx->ajcCustomReg );
if ( vrgEndptGetNumFxoEndpoints() > 0 ) //如果存在FXO口
//PSTN控制任务初始化,其中回调函数PstnEventCallback把FXO口相关处理信息
//发送到消息处理队列vrgEventQ中,由消息处理任务VrgDispatcherTaskMain来处
//理。
pstnCtlInit( endptConfig.country, PstnEventCallback );
//保存PSTN事件回调及国家码
pstnCtlState.callback = callback;
pstnCtlState.country = country;
//复位状态信息
for(i = 0; i < PSTN_CTL_MAX_CHANNELS; i++)
pstnCtlState.chanState[i].state = PSTN_CTL_CHAN_STATE_IDLE;
pstnCtlState.chanState[i].vhdHdl = 0xFF;
pstnCtlState.chanState[i].timeoutCount = 0;
//创建PSTN事件信号量
bosSemCreate("pstnEvtQSem", 0, 1, &pstnEvtQSem);
//创建PSTN控制事件消息队列
pstnCtlState.evtQ = eventQueueCreate( PSTN_CTL_MAX_CHANNELS * 2 );
//创建PSTN任务,详见《ENDPT_410_pstnCtlTaskMain》
bosTaskCreateEx( "PSTN", (8 * 1024), PSTN_CTL_CFG_TASK_PRIORITY, NULL,
pstnCtlTaskMain, NULL, 0, &pstnCtlState.taskId ))
ENDPT_410_DoVrgEndptInit
最新推荐文章于 2018-06-07 14:36:14 发布