/*************************************************************
 * wanec_cmd.c   WANPIPE Echo Canceller Layer (WANEC_LIP)
 *
 *
 *
 * ===========================================================
 *
 * May 10 2006		Alex Feldman	Initial Version
 *
 * March 19, 2006	Alex Feldman	Enable Sout Adaptive Noise
 *					Reduction for all channel by
 *					default.
 *
 * January 9, 2008	David Rokhvarg
 *			Added support for Sangoma MS Windows Driver
 *
 */


/*=============================================================
 * Includes
 */


#include "wanpipe_includes.h"
#include "wanpipe_defines.h"
#include "wanpipe_debug.h"
#include "wanpipe.h"
#if defined(__LINUX__)
# include "if_wanpipe.h"
#endif

int verbose;

#include "oct6100_api.h"
#include "oct6100_version.h"

#include "wanec_iface.h"
#include "wanec_tones.h"

/*=============================================================
 * Definitions
 */
#define WANEC_MAX_PORT_RANGE		32
#define WANEC_MAX_BRI_PORT_RANGE	2
#define WANEC_READ_LIMIT		0x10000

#define WANEC_MAX_CONFBRIDGE_DEF	32
#define WANEC_MAC_PLAYOUT_BUFFERS	32

#define WANEC_MAX_TONEEVENTS		8
#define WANEC_MAX_PLAYOUTEVENTS		8

/*=============================================================
 * Global Parameters
 */
UINT32 WanEcToneListDTMF[] =
{
	SOUT_DTMF_0,
	SOUT_DTMF_1,
	SOUT_DTMF_2,
	SOUT_DTMF_3,
	SOUT_DTMF_4,
	SOUT_DTMF_5,
	SOUT_DTMF_6,
	SOUT_DTMF_7,
	SOUT_DTMF_8,
	SOUT_DTMF_9,
	SOUT_DTMF_A,
	SOUT_DTMF_B,
	SOUT_DTMF_C,
	SOUT_DTMF_D,
	SOUT_DTMF_STAR,
	SOUT_DTMF_POUND,
	ROUT_DTMF_0,
	ROUT_DTMF_1,
	ROUT_DTMF_2,
	ROUT_DTMF_3,
	ROUT_DTMF_4,
	ROUT_DTMF_5,
	ROUT_DTMF_6,
	ROUT_DTMF_7,
	ROUT_DTMF_8,
	ROUT_DTMF_9,
	ROUT_DTMF_A,
	ROUT_DTMF_B,
	ROUT_DTMF_C,
	ROUT_DTMF_D,
	ROUT_DTMF_STAR,
	ROUT_DTMF_POUND
};
UINT32 WanEcToneListFaxCalling[] =
{
	SOUT_G168_1100GB_ON,
	ROUT_G168_1100GB_ON
};

UINT32 WanEcToneListFaxCalled[] =
{
	SOUT_G168_2100GB_ON,
	ROUT_G168_2100GB_ON,
	SOUT_G168_2100GB_WSPR,
	ROUT_G168_2100GB_WSPR
};
/*=============================================================
 * Function prototype
*/

int wanec_ChipOpenPrep(wan_ec_dev_t *ec_dev, char *devname, wanec_config_t *config, int verbose);
int wanec_ChipOpen(wan_ec_dev_t*, int);
int wanec_ChipClose(wan_ec_dev_t*, int verbose);
int wanec_ChipStats(wan_ec_dev_t *ec_dev, wanec_chip_stats_t *chip_stats, int reset, int verbose);
int wanec_ChipImage(wan_ec_dev_t *ec_dev, wanec_chip_image_t *chip_image, int verbose);


int wanec_ChannelOpen(wan_ec_dev_t*, INT, int);
int wanec_ChannelClose(wan_ec_dev_t*, INT, int);
int wanec_ChannelModifyOpmode(wan_ec_dev_t*, INT, UINT32, int verbose);
int wanec_ChannelModifyCustom(wan_ec_dev_t*, INT, wanec_chan_custom_t*, int verbose);
int wanec_ChannelStats(wan_ec_dev_t*, INT channel, wanec_chan_stats_t *chan_stats, int reset);

int wanec_ChannelMute(wan_ec_dev_t*, INT channel, wanec_chan_mute_t*, int);
int wanec_ChannelUnMute(wan_ec_dev_t*, INT channel, wanec_chan_mute_t*, int);

int wanec_DtmfRemoval(wan_ec_dev_t *ec_dev, INT channel, int enable, int verbose);

int wanec_TonesCtrl(wan_ec_t *ec, int cmd, int ec_chan, wanec_tone_config_t*, int verbose);

int wanec_DebugChannel(wan_ec_dev_t*, INT channel, int verbose);
int wanec_DebugGetData(wan_ec_dev_t*, wanec_chan_monitor_t *chan_monitor, int verbose);

int wanec_BufferLoad(wan_ec_dev_t *ec_dev, wanec_buffer_config_t *buffer_config, int verbose);
int wanec_BufferUnload(wan_ec_dev_t *ec_dev, wanec_buffer_config_t *buffer_config, int verbose);
int wanec_BufferPlayoutAdd(wan_ec_t *ec, int channel, wanec_playout_t *playout, int verbose);
int wanec_BufferPlayoutStart(wan_ec_t *ec, int channel, wanec_playout_t *playout, int verbose);
int wanec_BufferPlayoutStop(wan_ec_t *ec, int channel, wanec_playout_t *playout, int verbose);

int wanec_ConfBridgeOpen(wan_ec_t *ec, wan_ec_confbridge_t *confbridge, int verbose);
int wanec_ConfBridgeClose(wan_ec_t *ec, wan_ec_confbridge_t *confbridge, int verbose);
int wanec_ConfBridgeChanAdd(wan_ec_t *ec, wan_ec_confbridge_t *confbridge, int channel, int verbose);
int wanec_ConfBridgeChanRemove(wan_ec_t *ec, wan_ec_confbridge_t *confbridge, int channel, int verbose);
int wanec_ConfBridgeChanMute(wan_ec_t *ec, wan_ec_confbridge_t *confbridge, int channel, int verbose);
int wanec_ConfBridgeChanUnMute(wan_ec_t *ec, wan_ec_confbridge_t *confbridge, int channel, int verbose);
int wanec_ConfBridgeDominantSpeakerSet(wan_ec_t *ec, wan_ec_confbridge_t *confbridge, int channel, int enable, int verbose);
int wanec_ConfBridgeMaskChange(wan_ec_t *ec, wan_ec_confbridge_t *confbridge, int channel, UINT32 mask, int verbose);
int wanec_ConfBridgeGetStats(wan_ec_t *ec, wan_ec_confbridge_t *confbridge, int verbose);

int wanec_ToneEvent(wan_ec_t *ec, int verbose);
int wanec_PlayoutEvent(wan_ec_t *ec, int verbose);
int wanec_ISR(wan_ec_t *ec, int verbose);

int wanec_fe2ec_channel(wan_ec_dev_t*, int);
static int wanec_hndl2ec_channel(wan_ec_t *ec, UINT32 ChannelHndl);
static int wanec_ec2fe_channel(wan_ec_t*, int, wan_ec_dev_t**);

extern int wanec_ChipParam(wan_ec_t*,tPOCT6100_CHIP_OPEN, wan_custom_conf_t*, int);
extern int wanec_ChanParam(wan_ec_t*,tPOCT6100_CHANNEL_MODIFY, wan_custom_conf_t*, int);
extern int wanec_ChanParamList(wan_ec_t *ec);

u32 wanec_req_write(void*, u32 write_addr, u16 write_data);
u32 wanec_req_write_smear(void*, u32 addr, u16 data, u32 len);
u32 wanec_req_write_burst(void*, u32 addr, u16 *data, u32 len);
u32 wanec_req_read(void*, u32 addr, u16 *data);
u32 wanec_req_read_burst(void*, u32 addr, u16 *data, u32 len);

extern int wan_ec_write_internal_dword(wan_ec_dev_t *ec_dev, u32 addr1, u32 data);
extern int wan_ec_read_internal_dword(wan_ec_dev_t *ec_dev, u32 addr1, u32 *data);

static int wanec_ToneEnable(wan_ec_t *ec, int ec_chan, UINT32 tone_id, int verbose);
static int wanec_ToneDisable(wan_ec_t *ec, int ec_chan, UINT32 tone_id, int verbose);

/*=============================================================
 * Function definition
*/
static int wanec_hndl2ec_channel(wan_ec_t *ec, UINT32 ChannelHndl)
{
	int	ec_chan = 0;

	for(ec_chan = 0; ec_chan < MAX_EC_CHANS; ec_chan++) {
		if (ec->pEchoChannelHndl[ec_chan] == ChannelHndl){
			return ec_chan;
		}
	}
	return 0;
}
int wanec_fe2ec_channel(wan_ec_dev_t *ec_dev, int fe_channel)
{
	int	ec_channel = 0;

	/* WANEC_MAX_BRI_PORT_RANGE = 2  */
	/* WANEC_MAX_PORT_RANGE	= 32 */
	/* For A500, fe_lineno 1-> 24 */

	if (ec_dev->card->adptr_type == AFT_ADPTR_FLEXBRI) {
		if (ec_dev->fe_lineno == 4){
		//if (ec_dev->fe_media == WAN_MEDIA_FXOFXS){
			ec_channel = WANEC_MAX_PORT_RANGE + (fe_channel-1);
		} else {
			ec_channel = (ec_dev->fe_lineno * WANEC_MAX_BRI_PORT_RANGE + (fe_channel-1));
		}
	} else {
		if (ec_dev->fe_media == WAN_MEDIA_BRI){
			if (ec_dev->fe_lineno >= 12){
				ec_channel = WANEC_MAX_PORT_RANGE;
			}
			ec_channel += (ec_dev->fe_lineno * WANEC_MAX_BRI_PORT_RANGE + (fe_channel-1));
		}else if (ec_dev->fe_media == WAN_MEDIA_T1 || 
				  ec_dev->fe_media == WAN_MEDIA_FXOFXS || 
				  ec_dev->card->adptr_type == AFT_ADPTR_B601) {
			ec_channel = ec_dev->fe_lineno * WANEC_MAX_PORT_RANGE + (fe_channel-1);
		} else {
			/*ec_channel = ec_dev->fe_lineno * ec_dev->fe_max_chans + channel;*/
			ec_channel = ec_dev->fe_lineno * WANEC_MAX_PORT_RANGE + fe_channel;
		}
	}
	return ec_channel;
}

static int wanec_ec2fe_channel(wan_ec_t *ec, int ec_chan, wan_ec_dev_t **ec_dev)
{
	wan_ec_dev_t	*ec_dev_tmp;
	int		fe_chan;

	ec_dev_tmp = ec->pEcDevMap[ec_chan];
	if (ec_dev_tmp == NULL) return 0;

	fe_chan = ec_chan % WANEC_MAX_PORT_RANGE;
	if (ec_dev_tmp->fe_media == WAN_MEDIA_BRI){
		fe_chan = fe_chan % WANEC_MAX_BRI_PORT_RANGE;
		fe_chan++;
	}else{
		if (ec_dev_tmp->fe_media == WAN_MEDIA_T1 ||
		    ec_dev_tmp->fe_media == WAN_MEDIA_FXOFXS ||
			ec_dev_tmp->card->adptr_type == AFT_ADPTR_B601){
			fe_chan++;
		}
	}
	if (ec_dev && *ec_dev) *ec_dev = ec_dev_tmp;
	return fe_chan;
}


/******************************************************************
**
**
*******************************************************************/
int wanec_ChipStats(wan_ec_dev_t *ec_dev, wanec_chip_stats_t *chip_stats, int reset, int verbose)
{
	wan_ec_t		*ec;
	tOCT6100_CHIP_STATS	f_ChipStats;
	UINT32			ulResult;

	WAN_ASSERT(ec_dev == NULL);
	WAN_ASSERT(ec_dev->ec == NULL);
	ec = ec_dev->ec;

	if (chip_stats){
		PRINT2(verbose, "%s: Reading chip statistics...\n",
					ec->name);
	}
	Oct6100ChipGetStatsDef( &f_ChipStats );
	f_ChipStats.fResetChipStats	= reset;
	ulResult = Oct6100ChipGetStats(
				ec->pChipInstance,
				&f_ChipStats);
	if ( ulResult != cOCT6100_ERR_OK ){
		DEBUG_EVENT(
		"%s: Reading chip statistics...\tFailed (err=0x%X)\n",
					ec->name, ulResult);
		return -EINVAL;
	}
	if (chip_stats){
		memcpy(	&chip_stats->f_ChipStats,
			&f_ChipStats,
			sizeof(tOCT6100_CHIP_STATS));
	}

	return 0;
}

/******************************************************************
**
**
*******************************************************************/
tOCT6100_CHIP_IMAGE_INFO	f_ChipImageInfo;
#if 0
tOCT6100_GET_HW_REVISION	f_Revision;
#endif
int wanec_ChipImage(wan_ec_dev_t *ec_dev, wanec_chip_image_t *chip_image, int verbose)
{
	wan_ec_t	*ec;
	UINT32		ulResult;
	int			err;

	WAN_ASSERT(ec_dev == NULL);
	WAN_ASSERT(ec_dev->ec == NULL);
	ec = ec_dev->ec;

	if (chip_image){
		PRINT2(verbose, "%s: Reading chip image info...\n",
					ec->name);
	}

	Oct6100ChipGetImageInfoDef( &f_ChipImageInfo );
	ulResult = Oct6100ChipGetImageInfo(
				ec->pChipInstance,
				&f_ChipImageInfo);
	if ( ulResult != cOCT6100_ERR_OK ){
		DEBUG_EVENT(
		"%s: Reading chip image info...\tFailed (err=0x%X)\n",
					ec->name, ulResult);
		return -EINVAL;
	}

	if (chip_image){

		if (chip_image->f_ChipImageInfo){

			/* If user initialized chip_image->f_ChipImageInfo pointer, then
			 * copy image information to chip_image->f_ChipImageInfo. */

			err = WAN_COPY_TO_USER(
					chip_image->f_ChipImageInfo,
					&f_ChipImageInfo,
					sizeof(tOCT6100_CHIP_IMAGE_INFO));
			if (err){
				DEBUG_EVENT(
				"%s: Failed to copy chip image info to user space [%s:%d]!\n",
						ec->name, __FUNCTION__,__LINE__);
				return -EINVAL;
			}
		}else{

			/* If user set chip_image->f_ChipImageInfo to NULL, then
			 * print image information to wanpipelog. */

			DEBUG_EVENT(
			"%s: Echo Canceller image description:\n%s\n",
					ec->name, f_ChipImageInfo.szVersionNumber);
			DEBUG_EVENT(
			"%s: Echo Canceller image build ID\t\t\t%08X\n",
					ec->name, f_ChipImageInfo.ulBuildId);
			DEBUG_EVENT(
			"%s: Echo Canceller maximum number of channels\t%d\n",
					ec->name, f_ChipImageInfo.ulMaxChannels);
#if 0
			PRINT2(verbose,
			"%s: Echo Canceller maximum tail displacement\t\t%d\n",
					ec->name, f_ChipImageInfo.ulMaxTailDisplacement);
			PRINT2(verbose,
			"%s: Echo Canceller per channel tail displacement\t%s\n",
					ec->name,
					(f_ChipImageInfo.fPerChannelTailDisplacement == TRUE)?"TRUE":"FALSE");
			PRINT2(verbose,
			"%s: Echo Canceller per channel tail length\t\t%s\n",
					ec->name,
					(f_ChipImageInfo.fPerChannelTailLength == TRUE)?"TRUE":"FALSE");
			PRINT2(verbose,
			"%s: Echo Canceller maximum tail length\t\t%d\n",
					ec->name, f_ChipImageInfo.ulMaxTailLength);
			PRINT2(verbose,
			"%s: Echo Canceller buffer Playout support\t\t%s\n",
					ec->name,
					(f_ChipImageInfo.fBufferPlayout == TRUE)?"TRUE":"FALSE");
			PRINT2(verbose,
			"%s: Echo Canceller adaptive noise reduction\t\t%s\n",
					ec->name,
					(f_ChipImageInfo.fAdaptiveNoiseReduction==TRUE)?"TRUE":"FALSE");
			PRINT2(verbose,
			"%s: Echo Canceller SOUT noise bleaching\t\t%s\n",
					ec->name,
					(f_ChipImageInfo.fSoutNoiseBleaching==TRUE)?"TRUE":"FALSE");
			PRINT2(verbose,
			"%s: Echo Canceller ROUT noise reduction\t\t%s\n",
					ec->name,
					(f_ChipImageInfo.fRoutNoiseReduction==TRUE)?"TRUE":"FALSE");
			PRINT2(verbose,
			"%s: Echo Canceller ROUT noise reduction level\t\t%s\n",
					ec->name,
					(f_ChipImageInfo.fRoutNoiseReductionLevel==TRUE)?"TRUE":"FALSE");
			PRINT2(verbose,
			"%s: Echo Canceller automatic level control\t\t%s\n",
					ec->name,
					(f_ChipImageInfo.fAutoLevelControl==TRUE)?"TRUE":"FALSE");
			PRINT2(verbose,
			"%s: Echo Canceller acoustic echo cancellation\t\t%s\n",
					ec->name,
					(f_ChipImageInfo.fAcousticEcho==TRUE)?"TRUE":"FALSE");
			PRINT2(verbose,
			"%s: Echo Canceller conferencing\t\t%s\n",
					ec->name,
					(f_ChipImageInfo.fConferencing==TRUE)?"TRUE":"FALSE");
			PRINT2(verbose,
			"%s: Echo Canceller conferencing noise reduction\t\t%s\n",
					ec->name,
					(f_ChipImageInfo.fConferencingNoiseReduction==TRUE)?"TRUE":"FALSE");
#endif
		}
	}
	if (f_ChipImageInfo.ulMaxChannels < (unsigned int)ec->max_ec_chans){
		ec->max_ec_chans = f_ChipImageInfo.ulMaxChannels-1;
	}

//	DEBUG_EVENT(verbose, "%s: Reading hw revision...\n",
//					ec->name);
#if 0
	DEBUG_EVENT(verbose, "%s: Reading hw revision...\n",
					ec->name);
	ec->f_Revision.ulUserChipId = 0;
	ec->f_Revision.pProcessContext = &ec->f_Context;
	ulResult = Oct6100GetHwRevision(&ec->f_Revision);
	if ( ulResult != cOCT6100_ERR_OK ){
		DEBUG_EVENT(
		"%s: Reading hw revision...\tFailed (err=0x%X)\n",
					ec->name, ulResult);
		return EINVAL;
	}
#endif
	return 0;
}

int wanec_ChipOpenPrep(wan_ec_dev_t *ec_dev, char *devname, wanec_config_t *config, int verbose)
{
	tOCT6100_GET_INSTANCE_SIZE	InstanceSize;
	wan_ec_t			*ec;
	UINT32				ulResult;

	WAN_ASSERT(ec_dev == NULL);
	WAN_ASSERT(ec_dev->ec == NULL);
	WAN_ASSERT(config->imageData == NULL);
	ec	= ec_dev->ec;

	ulResult = Oct6100ChipOpenDef( &ec->f_OpenChip );

	/*==============================================================*/
	/* Configure clocks */

	/* upclk oscillator is at 33.33 Mhz */
	ec->f_OpenChip.ulUpclkFreq = cOCT6100_UPCLK_FREQ_33_33_MHZ;

	/* mclk will be generated by internal PLL at 133 Mhz */
	ec->f_OpenChip.fEnableMemClkOut = TRUE;
#if 1
	ec->f_OpenChip.ulMemClkFreq = cOCT6100_MCLK_FREQ_133_MHZ;
#else
	ec->f_OpenChip.ulMemClkFreq = cOCT6100_MCLK_FREQ_125_MHZ; /*125*/
#endif

	/*==============================================================*/

	/*==============================================================*/
	/* General parameters */

	/* Chip ID.*/
	ec->f_OpenChip.ulUserChipId	= ec->chip_no;

	/* Set the max number of accesses to 1024 to speed things up */
	ec->f_OpenChip.ulMaxRwAccesses	= 1024;

	/* Set the maximums that the chip needs to support for this test */
	ec->f_OpenChip.ulMaxChannels	= config->max_channels;

	ec->f_OpenChip.ulMaxBiDirChannels	= 0;
	ec->f_OpenChip.ulMaxConfBridges		= 0;	//WANEC_MAX_CONFBRIDGE_DEF;
	ec->f_OpenChip.ulMaxPhasingTssts	= 0;
	ec->f_OpenChip.ulMaxTdmStreams		= 32;
	ec->f_OpenChip.ulMaxTsiCncts		= 2;

	/*==============================================================*/

	/*==============================================================*/
	/* External Memory Settings */

	/* Use DDR memory.*/
#if 1
	ec->f_OpenChip.ulMemoryType	= cOCT6100_MEM_TYPE_SDR;
#else
	ec->f_OpenChip.ulMemoryType	= cOCT6100_MEM_TYPE_DDR;
#endif
	/*
	**f_OpenChip.ulMemoryChipSize	= cOCT6100_MEMORY_CHIP_SIZE_8MB;
	**f_OpenChip.ulMemoryChipSize	= cOCT6100_MEMORY_CHIP_SIZE_16MB;*/
	ec->f_OpenChip.ulMemoryChipSize	= config->memory_chip_size;
	ec->f_OpenChip.ulNumMemoryChips	= 2;

	ec->f_OpenChip.ulSoftBufferPlayoutEventsBufSize = 2048;

	ec->f_OpenChip.fEnableChannelRecording	= TRUE;

#if defined(ENABLE_ACOUSTICECHO)
	ec->f_OpenChip.fEnableAcousticEcho	= TRUE;
#endif

#if defined(ENABLE_PRODBIST)
	/* Enable production bist mode */
	ec->f_OpenChip.fEnableProductionBist	= TRUE;
	ec->f_OpenChip.ulNumProductionBistLoops	= 0x1;
#endif

	ec->f_OpenChip.ulMaxPlayoutBuffers	= WANEC_MAC_PLAYOUT_BUFFERS;

	ec->f_OpenChip.pbyImageFile	= ec->pImageData;
	ec->f_OpenChip.ulImageSize	= ec->ImageSize;

	/* Assign board index (0). */
 	ec->f_Context.ulBoardId = ec->chip_no;

	/* Handle to driver */
	ec->f_Context.ec_dev = ec_dev;

	/* Interface name to driver */
	wp_strlcpy(ec->f_Context.devname, devname, WAN_DRVNAME_SZ);

	ulResult = Oct6100GetInstanceSize(&ec->f_OpenChip, &InstanceSize);
	if ( ulResult != cOCT6100_ERR_OK ){
		DEBUG_ERROR(
		"ERROR: %s: Failed to get EC chip instance size (err=0x%X)!\n",
					ec->name, ulResult);
		return -EINVAL;
	}

	/* Allocate memory needed for API instance. */
	ec->pChipInstance =
		(tPOCT6100_INSTANCE_API)wan_vmalloc(InstanceSize.ulApiInstanceSize);
	if (ec->pChipInstance == NULL){
		DEBUG_ERROR(
		"ERROR: %s: Failed to allocate memory for EC chip (%d bytes)!\n",
					ec->name,InstanceSize.ulApiInstanceSize);
		return -EINVAL;
	}

	/* Open the OCT6100 on the evaluation board. */
	ec->f_OpenChip.pProcessContext	= (PVOID)&ec->f_Context;

	/* parse advanced params (global custom configuration) */
	if (ec->custom_conf.param_no){
		wanec_ChipParam(ec, &ec->f_OpenChip, &ec->custom_conf, verbose);
	}
	/* parse advanced params (command line custom configuration) */
	if (config->custom_conf.param_no){
		wanec_ChipParam(ec, &ec->f_OpenChip, &config->custom_conf, verbose);
	}

	ec->ulDebugChannelHndl	= cOCT6100_INVALID_HANDLE;
	ec->ulDebugDataMode	= config->debug_data_mode;
	return 0;
}

int wanec_ChipOpen(wan_ec_dev_t *ec_dev, int verbose)
{
	wan_ec_t	*ec;
	UINT32		ulResult, i = 0;
	INT		ec_chan = 0;

	WAN_ASSERT(ec_dev == NULL);
	WAN_ASSERT(ec_dev->ec == NULL);
	ec	= ec_dev->ec;

	PRINT2(verbose,
	"%s: Opening Echo Canceller Chip ...\n",
					ec->name);
	if (ec->f_OpenChip.pbyImageFile == NULL){
		DEBUG_ERROR(
		"ERROR: %s: Invalid EC image pointer\n",
					ec->name);
		return -EINVAL;
	}
	ulResult = Oct6100ChipOpen(
			ec->pChipInstance,	/* API instance memory. */
			&ec->f_OpenChip );		/* Open chip structure. */
	if ( ulResult != cOCT6100_ERR_OK ){
		if (ec->imageLast == WANOPT_YES ||
		    ulResult != cOCT6100_ERR_OPEN_INVALID_FIRMWARE_OR_CAPACITY_PINS){
			DEBUG_ERROR(
			"ERROR: %s: Failed to open Echo Canceller Chip (err=0x%X)\n",
					ec->name, ulResult);
		}
		return -EINVAL;
	}

	if (wanec_ChipImage(ec_dev, NULL, verbose)){
		DEBUG_ERROR(
		"ERROR: %s: Failed to read EC chip statistics!\n",
					ec->name);
		wanec_ChipClose(ec_dev, verbose);
		return EINVAL;
	}

	ec->pToneBufferIndexes =
			wan_malloc(sizeof(UINT32) * ec->f_OpenChip.ulMaxPlayoutBuffers);
	if (ec->pToneBufferIndexes == NULL){
		DEBUG_ERROR(
		"ERROR: %s: Failed allocate memory for playout handles!\n",
					ec->name);
		wanec_ChipClose(ec_dev, verbose);
		return EINVAL;
	}
	i = 0;
	while(i < ec->f_OpenChip.ulMaxPlayoutBuffers){
		ec->pToneBufferIndexes[i++] = cOCT6100_INVALID_VALUE;
	}
	ec->pEchoChannelHndl =
			wan_malloc(sizeof(UINT32) * MAX_EC_CHANS);
	if (ec->pEchoChannelHndl == NULL){
		DEBUG_ERROR(
		"ERROR: %s: Failed allocate memory for channel handle!\n",
					ec->name);
		wan_free(ec->pToneBufferIndexes);
		wanec_ChipClose(ec_dev, verbose);
		return EINVAL;
	}

	ec->pEcDevMap = wan_malloc(sizeof(wan_ec_dev_t*) * MAX_EC_CHANS);

	if (ec->pEcDevMap == NULL){
		DEBUG_ERROR(
		"ERROR: %s: Failed allocate memory for ec channel map!\n",
					ec->name);
		wan_free(ec->pToneBufferIndexes);
		wan_free(ec->pEchoChannelHndl);
		wanec_ChipClose(ec_dev, verbose);
		return -EINVAL;
	}
	for(ec_chan = 0; ec_chan < MAX_EC_CHANS; ec_chan++){
		ec->pEchoChannelHndl[ec_chan] = cOCT6100_INVALID_HANDLE;
		ec->pEcDevMap[ec_chan] = NULL;
	}
	return 0;
}

int wanec_ChipClose(wan_ec_dev_t *ec_dev, int verbose)
{
	wan_ec_t		*ec;
	tOCT6100_CHIP_CLOSE	f_CloseChip;
	UINT32			ulResult;

	WAN_ASSERT(ec_dev == NULL);
	WAN_ASSERT(ec_dev->ec == NULL);
	ec = ec_dev->ec;
	PRINT2(verbose,
	"%s: Closing Echo Canceller Chip ...\n",
				ec->name);
	Oct6100ChipCloseDef( &f_CloseChip );
	ulResult = Oct6100ChipClose(
				ec->pChipInstance,
				&f_CloseChip );
	if ( ulResult != cOCT6100_ERR_OK ){
		DEBUG_ERROR(
		"ERROR: %s: Failed to close Echo Canceller chip (err=0x%X)!\n",
						ec->name, ulResult);
		return -EINVAL;
	}
	if (ec->pChipInstance){
		wan_vfree(ec->pChipInstance);
		ec->pChipInstance = NULL;
	}
	if (ec->pToneBufferIndexes){
		wan_free(ec->pToneBufferIndexes);
		ec->pToneBufferIndexes = NULL;
	}
	if (ec->pEchoChannelHndl){
		wan_free(ec->pEchoChannelHndl);
		ec->pEchoChannelHndl = NULL;
	}

	if (ec->pEcDevMap){
		wan_free(ec->pEcDevMap);
		ec->pEcDevMap = NULL;
	}

	return 0;
}

int wanec_ChannelOpen(wan_ec_dev_t *ec_dev, INT ec_chan, int verbose)
{
	tOCT6100_CHANNEL_OPEN	EchoChannelOpen;
	wan_ec_t		*ec;
	sdla_t			*card;
	UINT32			ulResult;
	UINT32			stream = 0,timeslot=0;
	INT			pcm_law_type;

	WAN_ASSERT(ec_dev == NULL);
	WAN_ASSERT(ec_dev->ec == NULL);
	WAN_ASSERT(ec_dev->card == NULL);
	ec = ec_dev->ec;
	card = (sdla_t*)ec_dev->card;

	pcm_law_type = (WAN_FE_TDMV_LAW(&card->fe) == WAN_TDMV_MULAW) ?
				cOCT6100_PCM_U_LAW : cOCT6100_PCM_A_LAW;
	PRINT2(verbose,
	"%s: Openning Echo Canceller channel %d (%s)...\n",
				ec->name, ec_chan,
				(pcm_law_type == cOCT6100_PCM_U_LAW) ?
						"MULAW":"ALAW");




	Oct6100ChannelOpenDef( &EchoChannelOpen );

	/* Assign the handle memory.*/
	EchoChannelOpen.pulChannelHndl = &ec->pEchoChannelHndl[ec_chan];

		/* Make sure the channel does not perform echo cancellation */
#if defined(WANEC_BYDEFAULT_NORMAL)
	EchoChannelOpen.ulEchoOperationMode =
					cOCT6100_ECHO_OP_MODE_NORMAL;
#else
	/* purposely fail the compile */
#error "No supported mode"
	EchoChannelOpen.ulEchoOperationMode =
					cOCT6100_ECHO_OP_MODE_POWER_DOWN;
#endif
	EchoChannelOpen.fEnableToneDisabler = 	TRUE;

	stream = (ec_chan % 2 == 1) ? 4 : 0;
	timeslot = ec_chan / 2;

	/* Configure the TDM interface.*/
	EchoChannelOpen.TdmConfig.ulRinPcmLaw		= pcm_law_type;
	EchoChannelOpen.TdmConfig.ulRoutPcmLaw		= pcm_law_type;
	EchoChannelOpen.TdmConfig.ulSinPcmLaw		= pcm_law_type;
	EchoChannelOpen.TdmConfig.ulSoutPcmLaw		= pcm_law_type;
	EchoChannelOpen.TdmConfig.ulRinStream		= stream + 0;
	EchoChannelOpen.TdmConfig.ulRinTimeslot		= timeslot;
	EchoChannelOpen.TdmConfig.ulRoutStream		= stream + 1;
	EchoChannelOpen.TdmConfig.ulRoutTimeslot	= timeslot;
	EchoChannelOpen.TdmConfig.ulSinStream		= stream + 2;
	EchoChannelOpen.TdmConfig.ulSinTimeslot		= timeslot;
	EchoChannelOpen.TdmConfig.ulSoutStream		= stream + 3;
	EchoChannelOpen.TdmConfig.ulSoutTimeslot	= timeslot;

	/* Set the desired VQE features (TRUE/FALSE).*/
	EchoChannelOpen.VqeConfig.fEnableNlp		= TRUE;
	EchoChannelOpen.VqeConfig.fRinDcOffsetRemoval	= TRUE;
	EchoChannelOpen.VqeConfig.fSinDcOffsetRemoval	= TRUE;


	if (card->hwec_conf.acustic_echo) {
		EchoChannelOpen.VqeConfig.fAcousticEcho		= TRUE;
    } else {
		EchoChannelOpen.VqeConfig.fAcousticEcho		= FALSE;
	}

	if (card->hwec_conf.nlp_disable) {
		EchoChannelOpen.VqeConfig.fEnableNlp		= FALSE;	
	} else {
		EchoChannelOpen.VqeConfig.fEnableNlp		= TRUE;	
	}
	
	if (card->hwec_conf.operation_mode) {
    	switch (card->hwec_conf.operation_mode) {
			case WANOPT_OCT_CHAN_OPERMODE_SPEECH:
				EchoChannelOpen.ulEchoOperationMode = cOCT6100_ECHO_OP_MODE_SPEECH_RECOGNITION;
				break;
			default:
				EchoChannelOpen.ulEchoOperationMode=cOCT6100_ECHO_OP_MODE_NORMAL;
				break;
		}
	} else {
       	EchoChannelOpen.ulEchoOperationMode=cOCT6100_ECHO_OP_MODE_NORMAL; 
	}


	if (card->tdmv_conf.hw_dtmf && card->hwec_conf.dtmf_removal) {
    	EchoChannelOpen.VqeConfig.fDtmfToneRemoval = TRUE;
		ec_dev->fe_dtmf_removal_map = 0xFFFFFFFF;
	} else {
    	EchoChannelOpen.VqeConfig.fDtmfToneRemoval = FALSE;
		ec_dev->fe_dtmf_removal_map = 0;
	}

	if (card->hwec_conf.tone_disabler_delay) {
		UINT32	delay = card->hwec_conf.tone_disabler_delay;
		delay = ((delay / 512) + 1) * 512 + 300;
                EchoChannelOpen.VqeConfig.ulToneDisablerVqeActivationDelay = delay;
		PRINT1(verbose,
		"%s: Enable special fax detection option on channel %d (%d)\n",
			ec->name, ec_chan,
			EchoChannelOpen.VqeConfig.ulToneDisablerVqeActivationDelay);
	}

	if (card->hwec_conf.noise_reduction) {
		EchoChannelOpen.VqeConfig.fSoutAdaptiveNoiseReduction = TRUE;
	} else {
		EchoChannelOpen.VqeConfig.fSoutAdaptiveNoiseReduction = FALSE;
	}

	if (card->hwec_conf.rx_gain) {
    	EchoChannelOpen.VqeConfig.fSoutLevelControl = TRUE;
        EchoChannelOpen.VqeConfig.lSoutLevelControlGainDb = card->hwec_conf.rx_gain; 
	} else {

		if (card->hwec_conf.rx_auto_gain) {
			EchoChannelOpen.VqeConfig.fSoutAutomaticLevelControl = TRUE;
			EchoChannelOpen.VqeConfig.lSoutAutomaticLevelControlTargetDb = card->hwec_conf.rx_auto_gain;
		} else {
			EchoChannelOpen.VqeConfig.fSoutAutomaticLevelControl = FALSE;
		}
	}
    
	if (card->hwec_conf.tx_gain) { 
    	EchoChannelOpen.VqeConfig.fRinLevelControl = TRUE;
        EchoChannelOpen.VqeConfig.lRinLevelControlGainDb = card->hwec_conf.tx_gain;
	} else {
		if (card->hwec_conf.tx_auto_gain) {
			EchoChannelOpen.VqeConfig.fRinAutomaticLevelControl = TRUE;
			EchoChannelOpen.VqeConfig.lRinAutomaticLevelControlTargetDb = card->hwec_conf.tx_auto_gain;
		} else {
			EchoChannelOpen.VqeConfig.fRinAutomaticLevelControl = FALSE;
		}
	}

	EchoChannelOpen.VqeConfig.ulToneDisablerVqeActivationDelay = ((UINT16)(1500 / 512) + 1) * 512 + 300; /*300;*/

	DEBUG_EVENT("%s: Opening HW Echo: [Mode=%s NoiseRed=%s VQE=%i DtmfRmv=%s Acust=%s NLP=%s ]\n",
			ec->name,
			EchoChannelOpen.ulEchoOperationMode==cOCT6100_ECHO_OP_MODE_NORMAL?"Normal":"Speech",
			(EchoChannelOpen.VqeConfig.fSoutAdaptiveNoiseReduction == TRUE)?"On":"Off",
			EchoChannelOpen.VqeConfig.ulToneDisablerVqeActivationDelay,
			EchoChannelOpen.VqeConfig.fDtmfToneRemoval==TRUE?"On":"Off",
			EchoChannelOpen.VqeConfig.fAcousticEcho==TRUE?"On":"Off",
			EchoChannelOpen.VqeConfig.fEnableNlp== TRUE?"On":"Off");

	EchoChannelOpen.VqeConfig.ulComfortNoiseMode	=
				cOCT6100_COMFORT_NOISE_NORMAL;
			/*	cOCT6100_COMFORT_NOISE_NORMAL
			**	cOCT6100_COMFORT_NOISE_EXTENDED,
			**	cOCT6100_COMFORT_NOISE_OFF,
			**	cOCT6100_COMFORT_NOISE_FAST_LATCH */

	PRINT2(verbose,
	"%s: Openning Echo Canceller channel %d (%s)...\n",
				ec->name,
				ec_chan,
				(pcm_law_type == cOCT6100_PCM_U_LAW) ?
						"MULAW":"ALAW");
	/* Open the channel.*/
	ulResult = Oct6100ChannelOpen(	ec->pChipInstance,
					&EchoChannelOpen );
	if (ulResult != cOCT6100_ERR_OK){
		DEBUG_ERROR(
		"ERROR: %s: Failed to open Echo Canceller channel %d (err=0x%X)!\n",
						ec->name,
						ec_chan,
						ulResult);
		return ulResult;
	}

	/* Init debug channel handle */
	ec->ulDebugChannelHndl = cOCT6100_INVALID_HANDLE;
	return 0;
}

int wanec_ChannelClose(wan_ec_dev_t *ec_dev, INT ec_chan, int verbose)
{
	wan_ec_t		*ec;
	tOCT6100_CHANNEL_CLOSE	EchoChannelClose;
	UINT32			ulResult;

	WAN_ASSERT(ec_dev == NULL);
	WAN_ASSERT(ec_dev->ec == NULL);
	ec = ec_dev->ec;
	PRINT2(verbose,
	"%s: Closing Echo Canceller channel %d ...\n",
					ec->name, ec_chan);

	Oct6100ChannelCloseDef( &EchoChannelClose );
	EchoChannelClose.ulChannelHndl =
				ec->pEchoChannelHndl[ec_chan];
	ulResult = Oct6100ChannelClose(	ec->pChipInstance,
				&EchoChannelClose );
	if (ulResult != cOCT6100_ERR_OK){
		DEBUG_ERROR(
		"ERROR: %s: Failed to close Echo Canceller channel %d (err=0x%X)!\n",
							ec->name,
							ec_chan,
							ulResult);
		return -EINVAL;
	}
	ec->pEchoChannelHndl[ec_chan] = cOCT6100_INVALID_HANDLE;
	return 0;
}

int wanec_ChannelModifyOpmode(	wan_ec_dev_t		*ec_dev,
				INT			channel,
				UINT32			opmode,
				int			verbose)
{
	wan_ec_t		*ec;
	tOCT6100_CHANNEL_MODIFY	EchoChannelModify;
	UINT32			ulResult;

	WAN_ASSERT(ec_dev == NULL);
	WAN_ASSERT(ec_dev->ec == NULL);
	ec = ec_dev->ec;

	PRINT2(verbose, "%s: Modifing EC Channel OPMODE to %d on ec_chan:%d...\n",
				ec->name, opmode, channel);

	Oct6100ChannelModifyDef( &EchoChannelModify );
	/* Assign the handle memory.*/
	EchoChannelModify.ulChannelHndl = ec->pEchoChannelHndl[channel];

	/* Echo Channel Operation Mode */
	EchoChannelModify.ulEchoOperationMode = opmode;

	/* Open the channel.*/
	ulResult = Oct6100ChannelModify(
					ec->pChipInstance,
					&EchoChannelModify );
	if (ulResult != cOCT6100_ERR_OK){
		DEBUG_EVENT(
		"%s: Failed to modify EC Channel OPMOde for ec_chan:%d (err=0x%X)\n",
				ec->name, channel, ulResult);
		return EINVAL;
	}
	return	0;
}

int wanec_DtmfRemoval(wan_ec_dev_t *ec_dev, INT channel, int enable, int verbose)
{
	wan_ec_t		*ec;
	tOCT6100_CHANNEL_MODIFY	EchoChannelModify;
	UINT32			ulResult;

	WAN_ASSERT(ec_dev == NULL);
	WAN_ASSERT(ec_dev->ec == NULL);

	ec = ec_dev->ec;

	if (enable) {
		if (ec_dev->fe_dtmf_removal_map & (1 << channel)) {
			return 0;
		}
		ec_dev->fe_dtmf_removal_map |= (1 << channel);
	} else { /* Disable */
		if (!(ec_dev->fe_dtmf_removal_map & (1 << channel))) {
			return 0;
		}
		ec_dev->fe_dtmf_removal_map &= ~(1 << channel);
	}

	PRINT2(verbose, "%s: %s EC Channel DTMF Removal ec_chan:%d...\n",
		   enable ? "Enable" : "Disable", ec->name, channel);

	Oct6100ChannelModifyDef(&EchoChannelModify);
	/* Assign the handle memory.*/
	EchoChannelModify.ulChannelHndl = ec->pEchoChannelHndl[channel];

	EchoChannelModify.fVqeConfigModified = TRUE;
	EchoChannelModify.VqeConfig.fDtmfToneRemoval = enable ? TRUE : FALSE;

	/* Open the channel.*/
	ulResult = Oct6100ChannelModify(ec->pChipInstance, &EchoChannelModify);
	if (ulResult != cOCT6100_ERR_OK){
		DEBUG_EVENT("%s: Failed to %s EC Channel DTMF Removal for ec_chan:%d (err=0x%X)\n",
					ec->name, enable ? "Enable" : "Disable", channel, ulResult);
		return EINVAL;
	}
	return	0;
}

int wanec_ChannelModifyCustom(	wan_ec_dev_t		*ec_dev,
				INT			channel,
				wanec_chan_custom_t	*chan_custom,
				int			verbose)
{
	wan_ec_t		*ec;
	tOCT6100_CHANNEL_MODIFY	EchoChannelModify;
	UINT32			ulResult;
	int			err;

	WAN_ASSERT(ec_dev == NULL);
	WAN_ASSERT(ec_dev->ec == NULL);
	WAN_ASSERT(chan_custom == NULL);
	WAN_ASSERT(chan_custom->custom_conf.param_no == 0);
	ec = ec_dev->ec;

	PRINT2(verbose, "%s: Modifing EC Channel Config parameters for ec_chan:%d...\n",
				ec->name, channel);

	Oct6100ChannelModifyDef( &EchoChannelModify );
	/* Assign the handle memory.*/
	EchoChannelModify.ulChannelHndl = ec->pEchoChannelHndl[channel];
	/* parse advianced params */
	err = wanec_ChanParam(ec, &EchoChannelModify, &chan_custom->custom_conf, verbose);
	if (err){
		DEBUG_EVENT(
		"%s: WARNING: Unsupported parameter for channel %d!\n",
				ec->name, channel);
		return -EINVAL;
	}

	/* Open the channel.*/
	ulResult = Oct6100ChannelModify(
					ec->pChipInstance,
					&EchoChannelModify );
	if (ulResult != cOCT6100_ERR_OK){
		DEBUG_EVENT(
		"%s: Failed to modify EC channel config parameters for ec_chan:%d (err=0x%X)\n",
				ec->name, channel, ulResult);
		return EINVAL;
	}
	return	0;
}

/******************************************************************************
**			CONFERENCE BRIDGE FUNCTIONS
******************************************************************************/
int wanec_ChannelMute(wan_ec_dev_t* ec_dev, INT ec_chan, wanec_chan_mute_t *mute, int verbose)
{
	wan_ec_t		*ec;
	tOCT6100_CHANNEL_MUTE	f_ChannelMute;
	UINT32			ulResult;

	WAN_ASSERT(ec_dev == NULL);
	WAN_ASSERT(ec_dev->ec == NULL);
	ec = ec_dev->ec;

	Oct6100ChannelMuteDef( &f_ChannelMute );
	f_ChannelMute.ulChannelHndl	= ec->pEchoChannelHndl[ec_chan];
	f_ChannelMute.ulPortMask	= cOCT6100_CHANNEL_MUTE_PORT_NONE;
	if (mute == NULL || mute->port_map & WAN_EC_CHANNEL_PORT_SOUT)
		f_ChannelMute.ulPortMask |= cOCT6100_CHANNEL_MUTE_PORT_SOUT;
	if (mute == NULL || mute->port_map & WAN_EC_CHANNEL_PORT_SIN)
		f_ChannelMute.ulPortMask |= cOCT6100_CHANNEL_MUTE_PORT_SIN;
	if (mute == NULL || mute->port_map & WAN_EC_CHANNEL_PORT_ROUT)
		f_ChannelMute.ulPortMask |= cOCT6100_CHANNEL_MUTE_PORT_ROUT;
	if (mute == NULL || mute->port_map & WAN_EC_CHANNEL_PORT_RIN)
		f_ChannelMute.ulPortMask |= cOCT6100_CHANNEL_MUTE_PORT_RIN;
	PRINT2(verbose, "%s: Muting EC Channel %d on port:%X...\n",
				ec->name, ec_chan, f_ChannelMute.ulPortMask);
	ulResult = Oct6100ChannelMute(
				ec->pChipInstance,
				&f_ChannelMute);
	if ( ulResult != cOCT6100_ERR_OK ){
		DEBUG_ERROR(
		"ERROR: %s: Failed to mute EC channel %d on port %X (%08X)!\n",
					ec->name, ec_chan, f_ChannelMute.ulPortMask, ulResult);
		return EINVAL;
	}
	return 0;
}
int wanec_ChannelUnMute(wan_ec_dev_t *ec_dev, INT ec_chan, wanec_chan_mute_t *mute, int verbose)
{
	wan_ec_t		*ec;
	tOCT6100_CHANNEL_UNMUTE	f_ChannelUnMute;
	UINT32			ulResult;

	WAN_ASSERT(ec_dev == NULL);
	WAN_ASSERT(ec_dev->ec == NULL);
	ec = ec_dev->ec;

	Oct6100ChannelUnMuteDef( &f_ChannelUnMute );
	f_ChannelUnMute.ulChannelHndl	= ec->pEchoChannelHndl[ec_chan];
	f_ChannelUnMute.ulPortMask	= cOCT6100_CHANNEL_MUTE_PORT_NONE;
	if (mute == NULL || mute->port_map & WAN_EC_CHANNEL_PORT_SOUT)
		f_ChannelUnMute.ulPortMask |= cOCT6100_CHANNEL_MUTE_PORT_SOUT;
	if (mute == NULL || mute->port_map & WAN_EC_CHANNEL_PORT_SIN)
		f_ChannelUnMute.ulPortMask |= cOCT6100_CHANNEL_MUTE_PORT_SIN;
	if (mute == NULL || mute->port_map & WAN_EC_CHANNEL_PORT_ROUT)
		f_ChannelUnMute.ulPortMask |= cOCT6100_CHANNEL_MUTE_PORT_ROUT;
	if (mute == NULL || mute->port_map & WAN_EC_CHANNEL_PORT_RIN)
		f_ChannelUnMute.ulPortMask |= cOCT6100_CHANNEL_MUTE_PORT_RIN;
	PRINT2(verbose, "%s: Un-Muting EC channel %d on port:%X...\n",
				ec->name, ec_chan, f_ChannelUnMute.ulPortMask);
	ulResult = Oct6100ChannelUnMute(
				ec->pChipInstance,
				&f_ChannelUnMute);
	if ( ulResult != cOCT6100_ERR_OK ){
		DEBUG_ERROR(
		"ERROR: %s: Failed to un-mute channel %d on port %X (%08X)!\n",
					ec->name, ec_chan, f_ChannelUnMute.ulPortMask, ulResult);
		return EINVAL;
	}
	return 0;
}

int wanec_ChannelStats(wan_ec_dev_t *ec_dev, INT channel, wanec_chan_stats_t *chan_stats, int verbose)
{
	wan_ec_t		*ec;
	tOCT6100_CHANNEL_STATS	f_ChannelStats;
	UINT32			ulResult;

	WAN_ASSERT(ec_dev == NULL);
	WAN_ASSERT(ec_dev->ec == NULL);
	ec = ec_dev->ec;

	PRINT2(verbose, "%s: Reading EC statistics for channel %d...\n",
				ec->name, channel);
	Oct6100ChannelGetStatsDef( &f_ChannelStats );
	f_ChannelStats.ulChannelHndl = ec->pEchoChannelHndl[channel];
	f_ChannelStats.fResetStats = chan_stats->reset;
	ulResult = Oct6100ChannelGetStats(
				ec->pChipInstance,
				&f_ChannelStats);
	if ( ulResult != cOCT6100_ERR_OK ){
		DEBUG_ERROR(
		"ERROR: %s: Failed to read EC stats for channel %d (%08X)!\n",
						ec->name, channel, ulResult);
		return EINVAL;
	}
	if (chan_stats){
		memcpy(	&chan_stats->f_ChannelStats,
			&f_ChannelStats,
			sizeof(tOCT6100_CHANNEL_STATS));
	}
	return 0;
}


int wanec_TonesCtrl(wan_ec_t *ec, int cmd, int ec_chan, wanec_tone_config_t *tone, int verbose)
{
	UINT32	*tones=NULL;
	int	tone_no = 0, i = 0, err = 0;
	uint8_t	port_map = (tone) ? tone->port_map : (WAN_EC_CHANNEL_PORT_ROUT|WAN_EC_CHANNEL_PORT_SOUT);	

	if (tone->id == WP_API_EVENT_TONE_DTMF){
		tones = WanEcToneListDTMF;
		tone_no = sizeof(WanEcToneListDTMF)/sizeof(UINT32);
	}else if (tone->id == WP_API_EVENT_TONE_FAXCALLING){
		tones = WanEcToneListFaxCalling;
		tone_no = sizeof(WanEcToneListFaxCalling)/sizeof(UINT32);
	}else if (tone->id == WP_API_EVENT_TONE_FAXCALLED){
		tones = WanEcToneListFaxCalled;
		tone_no = sizeof(WanEcToneListFaxCalled)/sizeof(UINT32);
	}else{
		return -EINVAL;
	}

	for(i = 0; i < tone_no; i++){
		if (((port_map & WAN_EC_CHANNEL_PORT_ROUT) && (tones[i] & WANEC_TONE_ROUT_MASK)) ||
		    ((port_map & WAN_EC_CHANNEL_PORT_SOUT) && (tones[i] & WANEC_TONE_SOUT_MASK)) ){

			if (cmd == WAN_TRUE){
				err = wanec_ToneEnable(ec, ec_chan, tones[i], verbose);
			}else{
				err = wanec_ToneDisable(ec, ec_chan, tones[i], verbose);
			}
		}
		if (err) return -EINVAL;
	}
	return 0;

}


static int wanec_ToneEnable(wan_ec_t *ec, int ec_chan, UINT32 tone_id, int verbose)
{
	tOCT6100_TONE_DETECTION_ENABLE	f_ToneDetectionEnable;
	UINT32				ulResult;

	PRINT2(verbose, "%s: Enable EC tone (0x%08X) detection on chan %d ...\n",
				ec->name, tone_id, ec_chan);

	Oct6100ToneDetectionEnableDef( &f_ToneDetectionEnable );
	f_ToneDetectionEnable.ulChannelHndl = ec->pEchoChannelHndl[ec_chan];
	f_ToneDetectionEnable.ulToneNumber  = tone_id;
	ulResult = Oct6100ToneDetectionEnable (
						ec->pChipInstance,
						&f_ToneDetectionEnable);
	if ( ulResult != cOCT6100_ERR_OK ){
		if (ulResult != cOCT6100_ERR_TONE_DETECTION_TONE_ACTIVATED){
			DEBUG_ERROR(
			"ERROR: %s: Failed to enable EC Tone (0x%08X) detection on ec chan %d (err=0x%08X)!\n",
						ec->name, tone_id, ec_chan, (unsigned int)ulResult);
			return -EINVAL;
		}
		PRINT2(verbose,
		"%s: EC Tone (0x%08X) detection is already enabled on channel %d!\n",
					ec->name, tone_id, ec_chan);
	}
	return 0;
}

static int wanec_ToneDisable(wan_ec_t *ec, int ec_chan, UINT32 tone_id, int verbose)
{
	tOCT6100_TONE_DETECTION_DISABLE	f_ToneDetectionDisable;
	UINT32				ulResult;

	PRINT2(verbose, "%s: Disable EC tone (0x%08X) detection on chan %d ...\n",
				ec->name, tone_id, ec_chan);
	Oct6100ToneDetectionDisableDef( &f_ToneDetectionDisable );
	f_ToneDetectionDisable.ulChannelHndl = ec->pEchoChannelHndl[ec_chan];
	if (ec_chan >= 0){
		f_ToneDetectionDisable.ulToneNumber = tone_id;
	}else{
		f_ToneDetectionDisable.fDisableAll = TRUE;
	}
	ulResult = Oct6100ToneDetectionDisable (
					ec->pChipInstance,
					&f_ToneDetectionDisable);
	if ( ulResult != cOCT6100_ERR_OK ){
		if (ulResult != cOCT6100_ERR_TONE_DETECTION_TONE_NOT_ACTIVATED){
			DEBUG_ERROR(
			"ERROR: %s: Failed to disable EC Tone (0x%08X) detection on ec chan %d (err=0x%08X)!\n",
						ec->name, tone_id, ec_chan, (unsigned int)ulResult);
			return -EINVAL;
		}
	}

	return 0;
}

static CHAR* wanec_ToneId2Str(UINT32 f_ulToneId)
{
	switch (f_ulToneId){
	/* DTMF Section */
	case ROUT_DTMF_0: return "ROUT_DTMF_0";
	case ROUT_DTMF_1: return "ROUT_DTMF_1";
	case ROUT_DTMF_2: return "ROUT_DTMF_2";
	case ROUT_DTMF_3: return "ROUT_DTMF_3";
	case ROUT_DTMF_4: return "ROUT_DTMF_4";
	case ROUT_DTMF_5: return "ROUT_DTMF_5";
	case ROUT_DTMF_6: return "ROUT_DTMF_6";
	case ROUT_DTMF_7: return "ROUT_DTMF_7";
	case ROUT_DTMF_8: return "ROUT_DTMF_8";
	case ROUT_DTMF_9: return "ROUT_DTMF_9";
	case ROUT_DTMF_A: return "ROUT_DTMF_A";
	case ROUT_DTMF_B: return "ROUT_DTMF_B";
	case ROUT_DTMF_C: return "ROUT_DTMF_C";
	case ROUT_DTMF_D: return "ROUT_DTMF_D";
	case ROUT_DTMF_STAR: return "ROUT_DTMF_STAR";
	case ROUT_DTMF_POUND: return "ROUT_DTMF_POUND";
	case SOUT_DTMF_0: return "SOUT_DTMF_0";
	case SOUT_DTMF_1: return "SOUT_DTMF_1";
	case SOUT_DTMF_2: return "SOUT_DTMF_2";
	case SOUT_DTMF_3: return "SOUT_DTMF_3";
	case SOUT_DTMF_4: return "SOUT_DTMF_4";
	case SOUT_DTMF_5: return "SOUT_DTMF_5";
	case SOUT_DTMF_6: return "SOUT_DTMF_6";
	case SOUT_DTMF_7: return "SOUT_DTMF_7";
	case SOUT_DTMF_8: return "SOUT_DTMF_8";
	case SOUT_DTMF_9: return "SOUT_DTMF_9";
	case SOUT_DTMF_A: return "SOUT_DTMF_A";
	case SOUT_DTMF_B: return "SOUT_DTMF_B";
	case SOUT_DTMF_C: return "SOUT_DTMF_C";
	case SOUT_DTMF_D: return "SOUT_DTMF_D";
	case SOUT_DTMF_STAR: return "SOUT_DTMF_STAR";
	case SOUT_DTMF_POUND: return "SOUT_DTMF_POUND";

	/* System 5/6/7 Section */
	case SIN_SYSTEM5_2400: return "SIN_SYSTEM5_2400";
	case SIN_SYSTEM5_2600: return "SIN_SYSTEM5_2600";
	case SIN_SYSTEM5_2400_2600: return "SIN_SYSTEM5_2400_2600";
	case SIN_SYSTEM7_2000: return "SIN_SYSTEM7_2000";
	case SIN_SYSTEM7_1780: return "SIN_SYSTEM7_1780";

	case SOUT_G168_2100GB_ON:	return "SOUT_G168_2100GB_ON";
	case SOUT_G168_2100GB_WSPR:	return "SOUT_G168_2100GB_WSPR";
	case ROUT_G168_2100GB_ON:	return "ROUT_G168_2100GB_ON";
	case ROUT_G168_2100GB_WSPR:	return "ROUT_G168_2100GB_WSPR";
	case SOUT_G168_1100GB_ON:	return "SOUT_G168_1100GB_ON";
	case ROUT_G168_1100GB_ON:	return "ROUT_G168_1100GB_ON";

	case ROUT_SOUT_G168_2100HB_END:	return "ROUT_SOUT_G168_2100HB_END";

	default: return "INVALID TONE ID!";
	}
}

static int wanec_ConvertToneId(UINT32 f_ulToneId, unsigned char *ec_tone_port, unsigned char *digit, u_int16_t *type)
{
	int err=0;

	switch (f_ulToneId){
	/* DTMF Section */
	case ROUT_DTMF_0:	
		*ec_tone_port = WAN_EC_CHANNEL_PORT_ROUT; 
		*digit		  ='0';
		*type 		  = WAN_EVENT_EC_DTMF;
		break;
	case ROUT_DTMF_1:	
		*ec_tone_port = WAN_EC_CHANNEL_PORT_ROUT; 
		*digit		  ='1';
		*type 		  = WAN_EVENT_EC_DTMF;
		break;
	case ROUT_DTMF_2:	
		*ec_tone_port = WAN_EC_CHANNEL_PORT_ROUT; 
		*digit		  ='2';
		*type 		  = WAN_EVENT_EC_DTMF;
		break;
	case ROUT_DTMF_3:	
		*ec_tone_port = WAN_EC_CHANNEL_PORT_ROUT; 
		*digit		  ='3';
		*type 		  = WAN_EVENT_EC_DTMF;
		break;
	case ROUT_DTMF_4:	
		*ec_tone_port = WAN_EC_CHANNEL_PORT_ROUT; 
		*digit		  ='4';
		*type 		  = WAN_EVENT_EC_DTMF;
		break;
	case ROUT_DTMF_5:	
		*ec_tone_port = WAN_EC_CHANNEL_PORT_ROUT; 
		*digit		  ='5';
		*type 		  = WAN_EVENT_EC_DTMF;
		break;
	case ROUT_DTMF_6:	
		*ec_tone_port = WAN_EC_CHANNEL_PORT_ROUT; 
		*digit		  ='6';
		*type 		  = WAN_EVENT_EC_DTMF;
		break;
	case ROUT_DTMF_7:	
		*ec_tone_port = WAN_EC_CHANNEL_PORT_ROUT; 
		*digit		  ='7';
		*type 		  = WAN_EVENT_EC_DTMF;
		break;
	case ROUT_DTMF_8:	
		*ec_tone_port = WAN_EC_CHANNEL_PORT_ROUT; 
		*digit		  ='8';
		*type 		  = WAN_EVENT_EC_DTMF;
		break;
	case ROUT_DTMF_9:	
		*ec_tone_port = WAN_EC_CHANNEL_PORT_ROUT; 
		*digit		  ='9';
		*type 		  = WAN_EVENT_EC_DTMF;
		break;
	case ROUT_DTMF_A:	
		*ec_tone_port = WAN_EC_CHANNEL_PORT_ROUT; 
		*digit		  ='A';
		*type 		  = WAN_EVENT_EC_DTMF;
		break;
	case ROUT_DTMF_B:	
		*ec_tone_port = WAN_EC_CHANNEL_PORT_ROUT; 
		*digit		  ='B';
		*type 		  = WAN_EVENT_EC_DTMF;
		break;
	case ROUT_DTMF_C:	
		*ec_tone_port = WAN_EC_CHANNEL_PORT_ROUT;
		*digit		  ='C';
		*type 		  = WAN_EVENT_EC_DTMF;
		break;
	case ROUT_DTMF_D:	
		*ec_tone_port = WAN_EC_CHANNEL_PORT_ROUT;
		*digit		  ='D';
		*type 		  = WAN_EVENT_EC_DTMF;
		break;
	case ROUT_DTMF_STAR:	
		*ec_tone_port = WAN_EC_CHANNEL_PORT_ROUT; 
		*digit		  ='*';
		*type 		  = WAN_EVENT_EC_DTMF;
		break;
	case ROUT_DTMF_POUND:	
		*ec_tone_port = WAN_EC_CHANNEL_PORT_ROUT; 
		*digit		  ='#';
		*type 		  = WAN_EVENT_EC_DTMF;
		break;
	case SOUT_DTMF_0:	
		*ec_tone_port = WAN_EC_CHANNEL_PORT_SOUT; 
		*digit		  ='0';
		*type 		  = WAN_EVENT_EC_DTMF;
		break;
	case SOUT_DTMF_1:	
		*ec_tone_port = WAN_EC_CHANNEL_PORT_SOUT; 
		*digit		  ='1';
		*type 		  = WAN_EVENT_EC_DTMF;
		break;
	case SOUT_DTMF_2:	
		*ec_tone_port = WAN_EC_CHANNEL_PORT_SOUT;
		*digit		  ='2';
		*type 		  = WAN_EVENT_EC_DTMF;
		break;
	case SOUT_DTMF_3:	
		*ec_tone_port = WAN_EC_CHANNEL_PORT_SOUT;
		*digit		  ='3';
		*type 		  = WAN_EVENT_EC_DTMF;
		break;
	case SOUT_DTMF_4:	
		*ec_tone_port = WAN_EC_CHANNEL_PORT_SOUT;
		*digit		  ='4';
		*type 		  = WAN_EVENT_EC_DTMF;
		break;
	case SOUT_DTMF_5:	
		*ec_tone_port = WAN_EC_CHANNEL_PORT_SOUT;
		*digit		  ='5';
		*type 		  = WAN_EVENT_EC_DTMF;
		break;
	case SOUT_DTMF_6:	
		*ec_tone_port = WAN_EC_CHANNEL_PORT_SOUT;
		*digit		  ='6';
		*type 		  = WAN_EVENT_EC_DTMF;
		break;
	case SOUT_DTMF_7:	
		*ec_tone_port = WAN_EC_CHANNEL_PORT_SOUT;
		*digit		  ='7';
		*type 		  = WAN_EVENT_EC_DTMF;
		break;
	case SOUT_DTMF_8:	
		*ec_tone_port = WAN_EC_CHANNEL_PORT_SOUT;
		*digit		  ='8';
		*type 		  = WAN_EVENT_EC_DTMF;
		break;
	case SOUT_DTMF_9:
		*ec_tone_port = WAN_EC_CHANNEL_PORT_SOUT;
		*digit		  ='9';
		*type 		  = WAN_EVENT_EC_DTMF;
		break;
	case SOUT_DTMF_A:	
		*ec_tone_port = WAN_EC_CHANNEL_PORT_SOUT;
		*digit		  ='A';
		*type 		  = WAN_EVENT_EC_DTMF;
		break;
	case SOUT_DTMF_B:	
		*ec_tone_port = WAN_EC_CHANNEL_PORT_SOUT;
		*digit		  ='B';
		*type 		  = WAN_EVENT_EC_DTMF;
		break;
	case SOUT_DTMF_C:	
		*ec_tone_port = WAN_EC_CHANNEL_PORT_SOUT; 
		*digit		  ='C';
		*type 		  = WAN_EVENT_EC_DTMF;
		break;
	case SOUT_DTMF_D:	
		*ec_tone_port = WAN_EC_CHANNEL_PORT_SOUT; 
		*digit		  ='D';
		*type 		  = WAN_EVENT_EC_DTMF;
		break;
	case SOUT_DTMF_STAR:	
		*ec_tone_port = WAN_EC_CHANNEL_PORT_SOUT; 
		*digit		  ='*';
		*type 		  = WAN_EVENT_EC_DTMF;
		break;
	case SOUT_DTMF_POUND:	
		*ec_tone_port = WAN_EC_CHANNEL_PORT_SOUT; 
		*digit		  ='#';
		*type 		  = WAN_EVENT_EC_DTMF;
		break;
	case SOUT_G168_2100GB_ON:	
		*ec_tone_port = WAN_EC_CHANNEL_PORT_SOUT; 
		*digit		  ='f';
		*type 		  = WAN_EVENT_EC_FAX_2100;
		break;
	case SOUT_G168_2100GB_WSPR:	
		*ec_tone_port = WAN_EC_CHANNEL_PORT_SOUT; 
		*digit		  ='f';
		*type 		  = WAN_EVENT_EC_FAX_2100_WSPR;
		break;
	case ROUT_G168_2100GB_ON:	
		*ec_tone_port = WAN_EC_CHANNEL_PORT_ROUT; 
		*digit		  ='f';
		*type 		  = WAN_EVENT_EC_FAX_2100;
		break;
	case ROUT_G168_2100GB_WSPR:	
		*ec_tone_port = WAN_EC_CHANNEL_PORT_ROUT; 
		*digit		  ='f';
		*type 		  = WAN_EVENT_EC_FAX_2100_WSPR;
		break;
	case SOUT_G168_1100GB_ON:	
		*ec_tone_port = WAN_EC_CHANNEL_PORT_SOUT; 
		*digit		  ='f';
		*type 		  = WAN_EVENT_EC_FAX_1100;
		break;
	case ROUT_G168_1100GB_ON:	
		*ec_tone_port = WAN_EC_CHANNEL_PORT_ROUT; 
		*digit		  ='f';
		*type 		  = WAN_EVENT_EC_FAX_1100;
		break;
	default:
		err=-1;
	}
	return err;
}

#if 1
//#if defined(WAN_DEBUG_HWEC)
static CHAR* wanec_ToneType2Str(UINT32 f_ulToneType)
{
	switch (f_ulToneType){
	case cOCT6100_TONE_PRESENT:	return "cOCT6100_TONE_PRESENT";
	case cOCT6100_TONE_STOP:	return "cOCT6100_TONE_STOP";
	default: return "INVALID TONE TYPE!";
	}
}
#endif
static unsigned char wanec_ConvertToneType(UINT32 f_ulToneType)
{
	switch (f_ulToneType){
	case cOCT6100_TONE_PRESENT:	return WAN_EC_TONE_PRESENT;
	case cOCT6100_TONE_STOP:	return WAN_EC_TONE_STOP;
	}
	return 0x00;
}

static CHAR* wanec_BufferPlayoutType2Str(UINT32 f_ulBufferPlayoutType)
{
	switch (f_ulBufferPlayoutType){
	case cOCT6100_BUFFER_PLAYOUT_EVENT_STOP:		return "PLAYOUT_EVENT_STOP";
#if 0
	case cOCT6100_BUFFER_PLAYOUT_EVENT_CALLER_ID_STOP:	return "PLAYOUT_EVENT_CALLER_ID_STOP";
	case cOCT6100_BUFFER_PLAYOUT_EVENT_CALLER_ID_AS_STOP:	return "PLAYOUT_EVENT_CALLER_ID_AS_STOP";
#endif
	default: return "INVALID BUFFER_PLAYOUT TYPE!";
	}
}


#if 0
/* Implemented fax debouncing but
   decieded not to use it. Code is here in case
   we change our mind */
static int wanec_fax_detect_debounce(wan_ec_dev_t *ec_dev, wan_event_t *event)
{
	sdla_t *card = (sdla_t*)ec_dev->card;

	if (event->digit == 'f') {

		if (card->tdmv_conf.hw_fax_detect == WANOPT_NO) {
			
		} else if (card->tdmv_conf.hw_fax_detect == WANOPT_YES) {
			card->tdmv_conf.hw_fax_detect=4;  /* number of sec between events */
		} 
		if (!card->hwec_conf.hw_fax_detect_cnt) {
			card->hwec_conf.hw_fax_detect_cnt=4; /* number of events PRESENT & STOP */ 
		}

		if (SYSTEM_TICKS - ec_dev->fax_detect_timeout >= card->tdmv_conf.hw_fax_detect*HZ) {
			ec_dev->fax_detect_timeout=SYSTEM_TICKS;
			ec_dev->fax_detect_cnt=1;
			DEBUG_EVENT("%s: Ignore fax tone cnt=%i, waiting for another one [fax type=%s]\n",
			  	ec_dev->devname,ec_dev->fax_detect_cnt,event->tone_type == WAN_EC_TONE_PRESENT?"PRESENT":"STOP");
			return 1;
		} else {
			ec_dev->fax_detect_timeout=SYSTEM_TICKS;
			ec_dev->fax_detect_cnt++;
			if (ec_dev->fax_detect_cnt <= card->hwec_conf.hw_fax_detect_cnt) {
				DEBUG_EVENT("%s: Ignore fax tone cnt=%i, waiting for another one [fax type=%s]\n",
					ec_dev->devname,ec_dev->fax_detect_cnt,event->tone_type == WAN_EC_TONE_PRESENT?"PRESENT":"STOP");
				return 1;
			}
		}
	}

	DEBUG_EVENT("%s: Pass on the tone [fax type=%s]\n",
				ec_dev->devname,event->tone_type == WAN_EC_TONE_PRESENT?"PRESENT":"STOP");

	return 0;
}
#endif

/*
**				wanec_ToneEvent()
**
** Return:	0  - on success
**		<0 - on error
**		1  - pending tone events
**/

int wanec_ToneEvent(wan_ec_t *ec, int verbose)
{
	tOCT6100_EVENT_GET_TONE	f_GetToneEvent;
	tOCT6100_TONE_EVENT		ToneEvent[32];
	wan_event_t				event;
	UINT32					ulResult;
	wan_ec_dev_t			*ec_dev;
	sdla_t					*card;
	UINT32					i;
	int						ec_chan;
	u8						fe_chan;

	PRINT2(verbose, "%s: Getting Tone events ...\n",
					ec->name);
	
	Oct6100EventGetToneDef( &f_GetToneEvent );
	f_GetToneEvent.fResetBufs = FALSE;
	f_GetToneEvent.ulMaxToneEvent = WANEC_MAX_TONEEVENTS;
	f_GetToneEvent.pToneEvent = ToneEvent;
	ulResult = Oct6100EventGetTone(
					ec->pChipInstance,
					&f_GetToneEvent);
	if ( ulResult != cOCT6100_ERR_OK ){
		if ( ulResult != cOCT6100_ERR_EVENTS_TONE_BUF_EMPTY ){
			PRINT2(verbose, "%s: There are not tone events!\n",
							ec->name);
			return 0;
		}
		DEBUG_ERROR(
		"ERROR: %s: Failed to get tone events (err=0x%X)!\n",
						ec->name, ulResult);
		return -EINVAL;
	}

	/* No tone tone event returned */
	if (!f_GetToneEvent.ulNumValidToneEvent) return 0;

	for(i = 0; i < f_GetToneEvent.ulNumValidToneEvent; i++){
		ec_chan = wanec_hndl2ec_channel(ec, ToneEvent[i].ulChannelHndl);
		ec_dev = ec->pEcDevMap[ec_chan];
		fe_chan = (u8)wanec_ec2fe_channel(ec, ec_chan, &ec_dev);
		if (ec_dev == NULL || ec_dev->card == NULL){
			DEBUG_ERROR(
			"%s: Internal Error: Failed to find fe channel (ec_chan=%d)\n",
						ec->name, ec_chan);
			continue;
		}

		PRINT2(verbose,
		"%s: Tone event %s %s on fe_chan=%d ec_chan=%d\n",
			ec_dev->devname,
			wanec_ToneId2Str(ToneEvent[i].ulToneDetected),
			wanec_ToneType2Str(f_GetToneEvent.pToneEvent[i].ulEventType),
			fe_chan, ec_chan);

		card = (sdla_t*)ec_dev->card;
		if (card->wandev.event_callback.tone){
			int err;
			event.channel	= fe_chan;
			event.tone_type = wanec_ConvertToneType(ToneEvent[i].ulEventType);

			err=wanec_ConvertToneId(
						ToneEvent[i].ulToneDetected,
						&event.tone_port, &event.digit, &event.type);

			if (err != 0) {
             	DEBUG_ERROR("%s: Error: Unsupported Tone Detected: chan=%i tone=0x%X\n",
						ec->name, fe_chan, ToneEvent[i].ulToneDetected);
               	goto ignore_tone;
			}

#if 0
/* Implemented fax debouncing but
   decieded not to use it. Code is here in case
   we change our mind */
           	if (wanec_fax_detect_debounce(ec_dev, &event)) {
               	goto ignore_tone;
		   	}
#endif
			card->wandev.event_callback.tone(card, &event);

ignore_tone:
			;

		}
	}

	/* Return 1 if more tone event are present, otherwise - 0 */
	return (f_GetToneEvent.fMoreEvents == TRUE) ? 1 : 0;
}

/*
**				wanec_PlayoutEvent()
**
** Return:	0  - on success
**		<0 - on error
**		1  - pending tone events
**/
int wanec_PlayoutEvent(wan_ec_t *ec, int verbose)
{
	tOCT6100_BUFFER_PLAYOUT_GET_EVENT	f_BufferPlayoutGetEvent;
	tOCT6100_BUFFER_PLAYOUT_EVENT		PlayoutEvent[32];
	UINT32					ulResult;
	wan_ec_dev_t				*ec_dev;
	UINT32					i;
	int					ec_chan,fe_chan;

	PRINT2(verbose, "%s: Getting EC Buffer Playout events ...\n",
					ec->name);
	Oct6100BufferPlayoutGetEventDef( &f_BufferPlayoutGetEvent );
	f_BufferPlayoutGetEvent.pBufferPlayoutEvent = PlayoutEvent;
	f_BufferPlayoutGetEvent.ulMaxEvent = WANEC_MAX_PLAYOUTEVENTS;
	f_BufferPlayoutGetEvent.fResetBufs = FALSE;

	ulResult = Oct6100BufferPlayoutGetEvent(
					ec->pChipInstance,
					&f_BufferPlayoutGetEvent);
	if ( ulResult != cOCT6100_ERR_OK ){
		if ( ulResult != cOCT6100_ERR_BUFFER_PLAYOUT_EVENT_BUF_EMPTY ){
			PRINT2(verbose, "%s: There are not buffer playout events!\n",
							ec->name);
			return 0;
		}
		DEBUG_ERROR(
		"ERROR: %s: Failed to get buffer playout events (err=0x%X)!\n",
						ec->name, ulResult);
		return -EINVAL;
	}

	/* No dtmf tone event returned */
	if (!f_BufferPlayoutGetEvent.ulNumValidEvent) return 0;

	for(i = 0; i < f_BufferPlayoutGetEvent.ulNumValidEvent; i++){
		ec_chan = wanec_hndl2ec_channel(ec, PlayoutEvent[i].ulChannelHndl);
		ec_dev = ec->pEcDevMap[ec_chan];
		fe_chan = wanec_ec2fe_channel(ec, ec_chan, &ec_dev);
		if (ec_dev == NULL || ec_dev->card == NULL){
			DEBUG_ERROR(
			"%s: Internal Error: Failed to find fe channel (ec_chan=%d)\n",
						ec->name, ec_chan);
			continue;
		}

		PRINT2(verbose,
		"%s: EC Buffer Playout event id %d %s on fe_chan=%d ec_chan=%d port=%s\n",
			ec_dev->devname,
			PlayoutEvent[i].ulUserEventId,
			wanec_BufferPlayoutType2Str(PlayoutEvent[i].ulEventType),
			fe_chan, ec_chan,
			(PlayoutEvent[i].ulChannelPort==cOCT6100_CHANNEL_PORT_ROUT) ? "ROUT" : "SOUT");
#if 0
		card = (sdla_t*)ec_dev->card;
		if (card->wandev.event_callback.playout){
		}
#endif
	}

	/* Return 1 if more playout event are present, otherwise - 0 */
	return (f_BufferPlayoutGetEvent.fMoreEvents == TRUE) ? 1 : 0;
}

#define wan_ec_isr_error_print(_format, _message, ...) if (!wan_test_and_set_bit(1,&silent)) { DEBUG_ERROR(_format, _message, ## __VA_ARGS__) };


/*
**				wanec_ISR()
**
** Return:	0  - on success
**		<0 - on error
**		1  - pending tone events
**/
int wanec_ISR(wan_ec_t *ec, int verbose)
{
	UINT32	ulResult;
	int	ret = 0;
	unsigned int silent=0;

	WAN_ASSERT(ec == NULL);

	PRINT2(verbose, "%s: Executing EC interrupt routine ...\n",
					ec->name);
	Oct6100InterruptServiceRoutineDef(&ec->f_InterruptFlag);

	ulResult = Oct6100InterruptServiceRoutine(
					ec->pChipInstance,
					&ec->f_InterruptFlag );
	if ( ulResult != cOCT6100_ERR_OK ){
		/* The 0x001DFF02D does not mean that the EC is dead. 
		   It means that a problem has occurred while doing a read burst to get the detected tones */
		if (ulResult != 0x001DF02D) {
			wan_ec_isr_error_print(
			"ERROR: %s: Failed to execute interrupt Service Routine (err=%08X)!\n",
						ec->name,
						ulResult);
		}
		return -EINVAL;
	}
	/* Critical errors */
	if (ec->f_InterruptFlag.fFatalGeneral == TRUE){
		/* Ignore cOCT6100_FATAL_GENERAL_ERROR_TYPE_5 timeout errors */
		if (ec->f_InterruptFlag.ulFatalGeneralFlags != cOCT6100_FATAL_GENERAL_ERROR_TYPE_5) {
			wan_ec_isr_error_print(
			"%s: An internal fatal chip error detected (0x%X)!\n",
					ec->name,
					ec->f_InterruptFlag.ulFatalGeneralFlags);
		}
	}
	if (ec->f_InterruptFlag.fFatalReadTimeout == TRUE){
		wan_ec_isr_error_print(
		"%s: A read to the external memory has failed!\n %s",
				ec->name, " ");
	}
	if (ec->f_InterruptFlag.fErrorRefreshTooLate == TRUE){
		wan_ec_isr_error_print(
		"%s: Error Refresh Too Late!\n %s",
				ec->name, " ");
	}
	if (ec->f_InterruptFlag.fErrorPllJitter == TRUE){
		wan_ec_isr_error_print(
		"%s: Error Pll Jitter\n %s",
				ec->name, " ");
	}
	if (ec->f_InterruptFlag.fErrorH100OutOfSync == TRUE && !ec->ignore_H100){
		wan_ec_isr_error_print(
		"%s: The H100 slave has lost its framing on the bus!\n %s",
				ec->name, " ");
	}
	if (ec->f_InterruptFlag.fErrorH100ClkA == TRUE){
		wan_ec_isr_error_print(
		"%s: The CT_C8_A clock behavior does not conform to the H.100 spec!\n %s",
				ec->name, " ");
	}
	if (ec->f_InterruptFlag.fErrorH100FrameA == TRUE){
		wan_ec_isr_error_print(
		"%s: The CT_FRAME_A clock behavior does not comform to the H.100 spec!\n %s",
				ec->name, " ");
	}
	if (ec->f_InterruptFlag.fErrorH100ClkB == TRUE){
		wan_ec_isr_error_print(
		"%s: The CT_C8_B clock is not running a 16.384 MHz!\n %s",
				ec->name, " ");
	}
	if (ec->f_InterruptFlag.fErrorOverflowToneEvents == TRUE){
		wan_ec_isr_error_print(
		"%s: Error: Tone Event buffer has overflowed\n %s",
				ec->name, " ");
	}
	if (ec->f_InterruptFlag.fToneEventsPending == TRUE){
		PRINT2(verbose, "%s: Tone Event pending....\n",
				ec->name);
		ret = wanec_ToneEvent(ec, ec->tone_verbose);
	}
	if (ec->f_InterruptFlag.fBufferPlayoutEventsPending == TRUE){
		PRINT2(verbose,
		"%s: Buffer Playout Events Pending\n",
				ec->name);
		ret = wanec_PlayoutEvent(ec, ec->playout_verbose);
	}
	if (ec->f_InterruptFlag.fApiSynch == TRUE){
		PRINT2(verbose,
		"%s: The chip interrupted the API for purpose of maintaining sync!\n",
				ec->name);
	}
	return ret;
}

int wanec_DebugChannel(wan_ec_dev_t *ec_dev, INT channel, int verbose)
{
	wan_ec_t			*ec = NULL;
	tOCT6100_DEBUG_SELECT_CHANNEL	DebugSelectChannel;
	wanec_chan_stats_t		chan_stats;
	UINT32				ulResult;

	WAN_ASSERT(ec_dev == NULL);
	WAN_ASSERT(ec_dev->ec == NULL);
	ec = ec_dev->ec;
	/* Verify Echo Canceller Channel operation mode */
	memset(&chan_stats, 0, sizeof(wanec_chan_stats_t));
	ulResult = wanec_ChannelStats(ec_dev, channel, &chan_stats, 0);
	if (chan_stats.f_ChannelStats.ulEchoOperationMode == cOCT6100_ECHO_OP_MODE_POWER_DOWN){
		DEBUG_EVENT(
		"ERROR: %s: Invalid Echo Channel %d operation mode (POWER DOWN)!\n",
				ec_dev->name, channel);
		return -EINVAL;
	}

	if (ec_dev->ec->ulDebugChannelHndl != cOCT6100_INVALID_HANDLE){
		DEBUG_ERROR(
		"ERROR: %s: Echo Canceller daemon can monitor only one ec channel (%d)!\n",
				ec_dev->name, channel);
		return -EINVAL;
	}
	Oct6100DebugSelectChannelDef( &DebugSelectChannel );

	PRINT2(verbose, "%s: Select ec channel %d for monitoring...\n",
			ec_dev->name,
			channel);
	/* Set selected debug channel */
	ec->DebugChannel		= channel;
	ec->ulDebugChannelHndl		= ec->pEchoChannelHndl[channel];
	DebugSelectChannel.ulChannelHndl= ec->pEchoChannelHndl[channel];

	/* Select Debug channel */
	ulResult = Oct6100DebugSelectChannel(
					ec->pChipInstance,
					&DebugSelectChannel );
	if (ulResult != cOCT6100_ERR_OK){
		DEBUG_ERROR(
		"ERROR: %s: Failed to select debug ec channel %d for monitoring (err=0x%X)\n",
				ec->name,
				channel,
				ulResult);
		return -EINVAL;
	}
	return	0;
}

int wanec_DebugGetData(wan_ec_dev_t *ec_dev, wanec_chan_monitor_t *chan_monitor, int verbose)
{
	wan_ec_t			*ec = NULL;
	tOCT6100_DEBUG_GET_DATA	fDebugGetData;
	UINT32			ulResult;

	WAN_ASSERT(ec_dev == NULL);
	WAN_ASSERT(ec_dev->ec == NULL);
	ec = ec_dev->ec;
	if (ec->ulDebugChannelHndl == cOCT6100_INVALID_HANDLE){
		DEBUG_ERROR(
		"ERROR: %s: No Debug channel was selected!\n",
					ec->name);
		return -EINVAL;
	}

	Oct6100DebugGetDataDef( &fDebugGetData );

	if (!chan_monitor->remain_len){
		PRINT1(verbose,
		"%s: Retrieves debug data for ec channel %d (%s)...\n",
			ec->name,
			ec->DebugChannel,
			(chan_monitor->data_mode == cOCT6100_DEBUG_GET_DATA_MODE_120S) ? "120s" :
			(chan_monitor->data_mode == cOCT6100_DEBUG_GET_DATA_MODE_120S_LITE) ? "120s-lite" :
			(chan_monitor->data_mode == cOCT6100_DEBUG_GET_DATA_MODE_16S) ? "16s" :
			(chan_monitor->data_mode == cOCT6100_DEBUG_GET_DATA_MODE_16S_LITE) ? "16s-lite" :
				"unknown");
	}else{
		PRINT2(verbose,
		"%s: Retrieves remain debug data (%d bytes) for ec channel %d...\n",
			ec->name,
			chan_monitor->remain_len, ec->DebugChannel);
	}

	memset(&chan_monitor->data[0], 0,
				sizeof(UINT8) * (MAX_MONITOR_DATA_LEN+1));
	/* Set selected debug channel */
	fDebugGetData.ulGetDataMode	= chan_monitor->data_mode;	//ec->ulDebugDataMode;
	fDebugGetData.ulMaxBytes	= chan_monitor->max_len;
	fDebugGetData.pbyData		= &chan_monitor->data[0];

	/* Select Debug channel */
	ulResult = Oct6100DebugGetData(
			ec->pChipInstance,
			&fDebugGetData );
	if (ulResult != cOCT6100_ERR_OK){
		DEBUG_ERROR(
		"ERROR: %s: Failed to get debug data for ec channel %d (err=0x%X)\n",
				ec->name,
				ec->DebugChannel,
				ulResult);
		return -EINVAL;
	}
	chan_monitor->data_len	= fDebugGetData.ulValidNumBytes;
	chan_monitor->remain_len= fDebugGetData.ulRemainingNumBytes;
	//chan_monitor->fe_chan	= ec->DebugChannel;
	chan_monitor->fe_chan	= wanec_ec2fe_channel(ec, ec->DebugChannel, NULL);

	if (fDebugGetData.ulRemainingNumBytes == 0){
		/* Last read */
		ec->ulDebugChannelHndl = cOCT6100_INVALID_HANDLE;
	}

	return	0;
}

static PUINT32 wanec_search_bufferindex(wan_ec_t *ec, UINT32 index)
{
	UINT32	i = 0;

	while(i < ec->f_OpenChip.ulMaxPlayoutBuffers){
		if (ec->pToneBufferIndexes[i] == index){
			return &ec->pToneBufferIndexes[i];
		}
		i++;
	}
	return NULL;
}

int wanec_BufferLoad(wan_ec_dev_t *ec_dev, wanec_buffer_config_t *buffer_config, int verbose)
{
	wan_ec_t		*ec;
	tOCT6100_BUFFER_LOAD	BufferLoad;
	UINT32			size, ulResult, pcmlaw = WAN_EC_PCM_U_LAW;
	PUINT8			pData = NULL;
	int			err;

	WAN_ASSERT(ec_dev == NULL);
	WAN_ASSERT(ec_dev->ec == NULL);
	ec = ec_dev->ec;
	pcmlaw = (buffer_config->pcmlaw) ? buffer_config->pcmlaw : WAN_EC_PCM_U_LAW;
	PRINT2(verbose,
	"%s: Loading Tone buffer (%s) law=%s into Echo Canceller Chip ...\n",
					ec->name,
					buffer_config->buffer,
					WAN_EC_DECODE_PCM_LAW(pcmlaw));
	size = buffer_config->size * sizeof(INT8);
	pData = wan_vmalloc(size);
	if (pData == NULL){
		DEBUG_ERROR(
		"ERROR: %s: Failed to allocate memory for buffer!\n",
					ec->name);
		return -EINVAL;
	}
	err = WAN_COPY_FROM_USER(pData, buffer_config->data, size);
	if (err){
		DEBUG_ERROR(
		"ERROR: %s: Failed to copy EC buffer from user space [%s:%d]!\n",
				ec->name,
				__FUNCTION__,__LINE__);
		wan_vfree(pData);
		return -EINVAL;
	}

	Oct6100BufferPlayoutLoadDef( &BufferLoad );
	BufferLoad.pulBufferIndex = wanec_search_bufferindex(ec, cOCT6100_INVALID_VALUE);
	/* FIXME: Can be alaw/mulaw */
	BufferLoad.ulBufferPcmLaw = (pcmlaw == WAN_EC_PCM_U_LAW) ?
					cOCT6100_PCM_U_LAW : cOCT6100_PCM_A_LAW;
	BufferLoad.pbyBufferPattern = pData;
	BufferLoad.ulBufferSize = size;
	ulResult = Oct6100BufferPlayoutLoad (
					ec->pChipInstance,
					&BufferLoad);
	if ( ulResult != cOCT6100_ERR_OK ){
		if (ulResult == cOCT6100_ERR_BUFFER_PLAYOUT_ALL_BUFFERS_OPEN){
			goto buffer_load_done;
		}
		DEBUG_ERROR(
		"%s: ERROR: Failed to load buffer into EC Chip (err=0x%X)\n",
						ec->name, ulResult);
		wan_vfree(pData);
		return -EINVAL;
	}
buffer_load_done:
	wan_vfree(pData);
	buffer_config->buffer_index = *BufferLoad.pulBufferIndex;
	PRINT2(verbose,
	"%s: Current buffer index is %d\n",
			ec->name, buffer_config->buffer_index);
	return 0;
}

int wanec_BufferUnload(wan_ec_dev_t *ec_dev, wanec_buffer_config_t *buffer_config, int verbose)
{
	wan_ec_t		*ec;
	tOCT6100_BUFFER_UNLOAD	BufferUnload;
	PUINT32			pBufferIndex = NULL;
	UINT32			index = 0, ulResult;

	WAN_ASSERT(ec_dev == NULL);
	WAN_ASSERT(ec_dev->ec == NULL);
	ec = ec_dev->ec;
	PRINT2(verbose,
	"%s: Unloading buffer from EC chip ...\n",
					ec->name);

try_next_index:
	Oct6100BufferPlayoutUnloadDef( &BufferUnload );
	if (buffer_config->buffer_index != cOCT6100_INVALID_VALUE){
		pBufferIndex = wanec_search_bufferindex(ec, buffer_config->buffer_index);
		if (pBufferIndex == NULL){
			DEBUG_ERROR(
			"ERROR: %s: Invalid buffer index %X!\n",
					ec->name, buffer_config->buffer_index);
			return EINVAL;
		}
	}else{
		if (index > ec->f_OpenChip.ulMaxPlayoutBuffers){
			goto buffer_unload_done;
		}
		if (ec->pToneBufferIndexes[index] == cOCT6100_INVALID_VALUE){
			index++;
			goto try_next_index;
		}
		pBufferIndex = &ec->pToneBufferIndexes[index];
	}

	BufferUnload.ulBufferIndex = *pBufferIndex;
	ulResult = Oct6100BufferPlayoutUnload (
					ec->pChipInstance,
					&BufferUnload);
	if ( ulResult != cOCT6100_ERR_OK ){
		if (ulResult == cOCT6100_ERR_BUFFER_PLAYOUT_NOT_OPEN){
			goto buffer_unload_done;
		}
		DEBUG_ERROR(
		"ERROR: %s: Failed to unload buffer from EC Chip (err=0x%X)!\n",
				ec->name, (unsigned int)ulResult);
		return EINVAL;
	}
	*pBufferIndex = cOCT6100_INVALID_VALUE;
	if (!buffer_config->buffer_index){
		index++;
		goto try_next_index;
	}

buffer_unload_done:
	return 0;
}

int wanec_BufferPlayoutAdd(wan_ec_t *ec, int channel, wanec_playout_t *playout, int verbose)
{
	tOCT6100_BUFFER_PLAYOUT_ADD	BufferPlayoutAdd;
	UINT32				ulResult;

	PRINT2(verbose,
	"%s: Add Tone buffer to ec channel %d port %s duration %d:%d...\n",
					ec->name,
					channel,
					WAN_EC_DECODE_CHANNEL_PORT(playout->port),
					playout->duration,
					playout->repeat_cnt);
	if (playout->index == cOCT6100_INVALID_VALUE||
	    wanec_search_bufferindex(ec, playout->index) == NULL){
		DEBUG_ERROR(
		"ERROR: %s: Invalid playout buffer index for ec channel %d!\n",
					ec->name, channel);
		return -EINVAL;
	}
	Oct6100BufferPlayoutAddDef( &BufferPlayoutAdd );
	if (playout->repeat_cnt == 1){
		BufferPlayoutAdd.fRepeat = FALSE;
	}else{
		BufferPlayoutAdd.fRepeat = TRUE;
	}
	BufferPlayoutAdd.ulPlayoutPort	=
			(playout->port == WAN_EC_CHANNEL_PORT_ROUT) ?
						cOCT6100_CHANNEL_PORT_ROUT :
						cOCT6100_CHANNEL_PORT_SOUT;
	BufferPlayoutAdd.ulMixingMode	= cOCT6100_MIXING_MUTE;
	BufferPlayoutAdd.ulChannelHndl	= ec->pEchoChannelHndl[channel];
	BufferPlayoutAdd.ulBufferIndex	= playout->index;
	BufferPlayoutAdd.fRepeat	= (playout->repeat_cnt) ? TRUE : FALSE;
	BufferPlayoutAdd.ulRepeatCount	= playout->repeat_cnt;
	BufferPlayoutAdd.ulDuration	= (playout->duration) ?
						playout->duration : cOCT6100_INVALID_VALUE;
	BufferPlayoutAdd.ulBufferLength	= (playout->buffer_length) ?
						playout->buffer_length :
						cOCT6100_AUTO_SELECT;
	ulResult = Oct6100BufferPlayoutAdd(
					ec->pChipInstance,
					&BufferPlayoutAdd);
	if ( ulResult != cOCT6100_ERR_OK ){
		DEBUG_ERROR(
		"ERROR: %s: Failed to add playout buffer to ec channel %d (err=%08X)\n",
					ec->name, channel, ulResult);
		return -EINVAL;
	}
	return 0;
}

int wanec_BufferPlayoutStart(wan_ec_t *ec, int channel, wanec_playout_t *playout, int verbose)
{
	tOCT6100_BUFFER_PLAYOUT_START	BufferPlayoutStart;
	UINT32				ulResult;

	PRINT2(verbose,
	"%s: Active playout buffer on ec channel %d port %s ...\n",
					ec->name,
					channel,
					WAN_EC_DECODE_CHANNEL_PORT(playout->port));
	Oct6100BufferPlayoutStartDef( &BufferPlayoutStart );
	BufferPlayoutStart.ulChannelHndl = ec->pEchoChannelHndl[channel];
	BufferPlayoutStart.ulPlayoutPort	=
			(playout->port == WAN_EC_CHANNEL_PORT_ROUT) ?
						cOCT6100_CHANNEL_PORT_ROUT :
						cOCT6100_CHANNEL_PORT_SOUT;
	BufferPlayoutStart.fNotifyOnPlayoutStop = playout->notifyonstop;
	BufferPlayoutStart.ulUserEventId = playout->user_event_id;
	ulResult = Oct6100BufferPlayoutStart(
					ec->pChipInstance,
					&BufferPlayoutStart);
	if ( ulResult != cOCT6100_ERR_OK ){
		DEBUG_ERROR(
		"ERROR: %s: Failed to active playout buffer on ec channel %d (err=%08X)\n",
					ec->name,
					channel,
					ulResult);
		return -EINVAL;
	}
	return 0;
}

int wanec_BufferPlayoutStop(wan_ec_t *ec, int channel, wanec_playout_t *playout, int verbose)
{
	tOCT6100_BUFFER_PLAYOUT_STOP	BufferPlayoutStop;
	UINT32				ulResult;

	PRINT2(verbose,
	"%s: Deactive playout buffer on ec channel %d port %s...\n",
					ec->name,
					channel,
					WAN_EC_DECODE_CHANNEL_PORT(playout->port));
	
	Oct6100BufferPlayoutStopDef( &BufferPlayoutStop );/* set default values */

	BufferPlayoutStop.fStopCleanly = FALSE; /* DavidR:	set to FALSE, following advice from Sebastien Trottier,
											 *			bug 6353, February 28, 2011. */
	BufferPlayoutStop.ulChannelHndl = ec->pEchoChannelHndl[channel];
	BufferPlayoutStop.ulPlayoutPort	=
			(playout->port == WAN_EC_CHANNEL_PORT_ROUT) ?
						cOCT6100_CHANNEL_PORT_ROUT :
						cOCT6100_CHANNEL_PORT_SOUT;
	ulResult = Oct6100BufferPlayoutStop(
					ec->pChipInstance,
					&BufferPlayoutStop);
	if ( ulResult != cOCT6100_ERR_OK ){
		if (ulResult != cOCT6100_ERR_BUFFER_PLAYOUT_NOT_STARTED){
			DEBUG_ERROR(
			"ERROR: %s: Failed to deactive playout buffer on ec channel %d (err=%08X)\n",
						ec->name,
						channel,
						ulResult);
			return -EINVAL;
		}
	}
	return 0;
}


/******************************************************************************
**			CONFERENCE BRIDGE FUNCTIONS
******************************************************************************/
int wanec_ConfBridgeOpen(wan_ec_t *ec, wan_ec_confbridge_t *confbridge, int verbose)
{
	tOCT6100_CONF_BRIDGE_OPEN	ConfBridgeOpen;
	UINT32				ulResult;

	PRINT2(verbose,
	"%s: Opening Conference Bridge...\n", ec->name);

	if ((unsigned int)ec->confbridges_no >= ec->f_OpenChip.ulMaxConfBridges){
		DEBUG_ERROR(
		"ERROR: %s: Trying to open too many conference bridges (%d:%d)\n",
					ec->name,
					ec->confbridges_no,
					ec->f_OpenChip.ulMaxConfBridges);
		return -EINVAL;
	}

	Oct6100ConfBridgeOpenDef( &ConfBridgeOpen );
	ConfBridgeOpen.pulConfBridgeHndl	= &confbridge->ulHndl;
	ConfBridgeOpen.fFlexibleConferencing	= FALSE;
	ulResult = Oct6100ConfBridgeOpen(
				ec->pChipInstance,
				&ConfBridgeOpen);
	if ( ulResult != cOCT6100_ERR_OK ){
		DEBUG_ERROR(
		"ERROR: %s: Failed to open new conference bridge (err=%08X)\n",
					ec->name,
					ulResult);
		return -EINVAL;
	}
	return 0;
}

int wanec_ConfBridgeClose(wan_ec_t *ec, wan_ec_confbridge_t *confbridge, int verbose)
{
	tOCT6100_CONF_BRIDGE_CLOSE	ConfBridgeClose;
	UINT32				ulResult;

	PRINT2(verbose,
	"%s: Closing Conference Bridge...\n", ec->name);

	Oct6100ConfBridgeCloseDef( &ConfBridgeClose );
	ConfBridgeClose.ulConfBridgeHndl = confbridge->ulHndl;
	ulResult = Oct6100ConfBridgeClose(
				ec->pChipInstance,
				&ConfBridgeClose);
	if ( ulResult != cOCT6100_ERR_OK ){
		DEBUG_ERROR(
		"ERROR: %s: Failed to close conference bridge (%X, err=%08X)\n",
					ec->name,
					confbridge->ulHndl,
					ulResult);
		return -EINVAL;
	}
	return 0;
}

int wanec_ConfBridgeChanAdd(wan_ec_t *ec, wan_ec_confbridge_t *confbridge, int channel, int verbose)
{
	tOCT6100_CONF_BRIDGE_CHAN_ADD	ConfBridgeChanAdd;
	UINT32				ulResult;

	PRINT2(verbose,
	"%s: Add channel %d to Conference Bridge %X...\n",
					ec->name, channel, confbridge->ulHndl);

	Oct6100ConfBridgeChanAddDef( &ConfBridgeChanAdd );
	ConfBridgeChanAdd.ulConfBridgeHndl	= confbridge->ulHndl;
	ConfBridgeChanAdd.ulChannelHndl		= ec->pEchoChannelHndl[channel];
	ulResult = Oct6100ConfBridgeChanAdd(
				ec->pChipInstance,
				&ConfBridgeChanAdd);
	if ( ulResult != cOCT6100_ERR_OK ){
		DEBUG_ERROR(
		"ERROR: %s: Failed to add channel %d to conference bridge (%X, err=%08X)\n",
					ec->name, channel,
					confbridge->ulHndl,
					ulResult);
		return -EINVAL;
	}
	return 0;
}

int wanec_ConfBridgeChanRemove(wan_ec_t *ec, wan_ec_confbridge_t *confbridge, int channel, int verbose)
{
	tOCT6100_CONF_BRIDGE_CHAN_REMOVE	ConfBridgeChanRemove;
	UINT32					ulResult;

	PRINT2(verbose,
	"%s: Remove channel %d from Conference Bridge %X...\n",
					ec->name, channel, confbridge->ulHndl);

	Oct6100ConfBridgeChanRemoveDef( &ConfBridgeChanRemove );
	ConfBridgeChanRemove.ulConfBridgeHndl	= confbridge->ulHndl;
	ConfBridgeChanRemove.ulChannelHndl	= ec->pEchoChannelHndl[channel];
	ulResult = Oct6100ConfBridgeChanRemove(
				ec->pChipInstance,
				&ConfBridgeChanRemove);
	if ( ulResult != cOCT6100_ERR_OK ){
		DEBUG_ERROR(
		"ERROR: %s: Failed to remove channel %d from conference bridge (%X, err=%08X)\n",
					ec->name, channel,
					confbridge->ulHndl,
					ulResult);
		return -EINVAL;
	}
	return 0;
}

int wanec_ConfBridgeChanMute(wan_ec_t *ec, wan_ec_confbridge_t *confbridge, int channel, int verbose)
{
	tOCT6100_CONF_BRIDGE_CHAN_MUTE	ConfBridgeChanMute;
	UINT32				ulResult;

	PRINT2(verbose,
	"%s: Mute channel %d on a conference bridge %X...\n",
				ec->name, channel, confbridge->ulHndl);

	Oct6100ConfBridgeChanMuteDef( &ConfBridgeChanMute );
	ConfBridgeChanMute.ulChannelHndl = ec->pEchoChannelHndl[channel];
	ulResult = Oct6100ConfBridgeChanMute(
				ec->pChipInstance,
				&ConfBridgeChanMute);
	if ( ulResult != cOCT6100_ERR_OK ){
		DEBUG_ERROR(
		"ERROR: %s: Failed to mute channel %d on a conference bridge (%X, err=%08X)\n",
					ec->name, channel,
					confbridge->ulHndl,
					ulResult);
		return -EINVAL;
	}
	return 0;
}

int wanec_ConfBridgeChanUnMute(wan_ec_t *ec, wan_ec_confbridge_t *confbridge, int channel, int verbose)
{
	tOCT6100_CONF_BRIDGE_CHAN_UNMUTE	ConfBridgeChanUnMute;
	UINT32					ulResult;

	PRINT2(verbose,
	"%s: UnMute channel %d on a Conference Bridge %X...\n",
					ec->name, channel, confbridge->ulHndl);

	Oct6100ConfBridgeChanUnMuteDef( &ConfBridgeChanUnMute );
	ConfBridgeChanUnMute.ulChannelHndl = ec->pEchoChannelHndl[channel];
	ulResult = Oct6100ConfBridgeChanUnMute(
				ec->pChipInstance,
				&ConfBridgeChanUnMute);
	if ( ulResult != cOCT6100_ERR_OK ){
		DEBUG_ERROR(
		"ERROR: %s: Failed to unmute channel %d from conference bridge (%X, err=%08X)\n",
					ec->name, channel,
					confbridge->ulHndl,
					ulResult);
		return -EINVAL;
	}
	return 0;
}

int wanec_ConfBridgeDominantSpeakerSet(wan_ec_t *ec, wan_ec_confbridge_t *confbridge, int channel, int enable, int verbose)
{
	tOCT6100_CONF_BRIDGE_DOMINANT_SPEAKER_SET	ConfBridgeDominantSpeaker;
	UINT32						ulResult;

	PRINT2(verbose,
	"%s: %s Dominant speaker (channel %d) to a Conference Bridge %X...\n",
					ec->name,
					(enable) ? "Enable":"Disable",
					channel,
					confbridge->ulHndl);

	Oct6100ConfBridgeDominantSpeakerSetDef( &ConfBridgeDominantSpeaker );
	ConfBridgeDominantSpeaker.ulConfBridgeHndl	= confbridge->ulHndl;
	if (enable){
		ConfBridgeDominantSpeaker.ulChannelHndl = ec->pEchoChannelHndl[channel];
	}else{
		ConfBridgeDominantSpeaker.ulChannelHndl = cOCT6100_CONF_NO_DOMINANT_SPEAKER_HNDL;
	}
	ulResult = Oct6100ConfBridgeDominantSpeakerSet(
					ec->pChipInstance,
					&ConfBridgeDominantSpeaker);
	if ( ulResult != cOCT6100_ERR_OK ){
		DEBUG_ERROR(
		"ERROR: %s: Failed to %s dominant speaker to conference bridge (%X, err=%08X)\n",
					ec->name,
					(enable) ? "enable" : "disable",
					confbridge->ulHndl,
					ulResult);
		return -EINVAL;
	}
	return 0;
}


int wanec_ConfBridgeMaskChange(wan_ec_t *ec, wan_ec_confbridge_t *confbridge, int channel, UINT32 mask, int verbose)
{
	tOCT6100_CONF_BRIDGE_MASK_CHANGE	ConfBridgeMaskChange;
	UINT32					ulResult;

	PRINT2(verbose,
	"%s: Changing the listener (channel=%d) mask of bridge participant (%d)...\n",
					ec->name,
					channel, confbridge->ulHndl);

	Oct6100ConfBridgeMaskChangeDef( &ConfBridgeMaskChange );
	ConfBridgeMaskChange.ulChannelHndl	= ec->pEchoChannelHndl[channel];
	ConfBridgeMaskChange.ulNewListenerMask	= mask;
	ulResult = Oct6100ConfBridgeMaskChange(
					ec->pChipInstance,
					&ConfBridgeMaskChange);
	if ( ulResult != cOCT6100_ERR_OK ){
		DEBUG_ERROR(
		"ERROR: %s: Failed to change the listener mask of bridge participant %d (err=%X)!\n",
					ec->name,
					channel,
					ulResult);
		return -EINVAL;
	}
	return 0;
}

int wanec_ConfBridgeGetStats(wan_ec_t *ec, wan_ec_confbridge_t *confbridge, int verbose)
{
	tOCT6100_CONF_BRIDGE_STATS	ConfBridgeStats;
	UINT32				ulResult;

	PRINT2(verbose,
	"%s: Getting bridge statistics %X...\n",
					ec->name, confbridge->ulHndl);

	Oct6100ConfBridgeGetStatsDef( &ConfBridgeStats );
	ConfBridgeStats.ulConfBridgeHndl	= confbridge->ulHndl;
	ulResult = Oct6100ConfBridgeGetStats(
					ec->pChipInstance,
					&ConfBridgeStats);
	if ( ulResult != cOCT6100_ERR_OK ){
		DEBUG_ERROR(
		"ERROR: %s: Failed to get conference bridge statistics (err=%X)!\n",
					ec->name,
					ulResult);
		return -EINVAL;
	}
	return 0;
}

/*===========================================================================*\
  Oct6100Read
\*===========================================================================*/

static int
wan_ec_read(wan_ec_dev_t *ec_dev, u32 read_addr, u16 *read_data)
{
	u32	data;
	wan_ec_read_internal_dword(ec_dev, read_addr, &data);
	*read_data = (u16)(data & 0xFFFF);
	return 0;
}

/*===========================================================================*\
  Oct6100Write
\*===========================================================================*/

static int
wan_ec_write(wan_ec_dev_t *ec_dev, u32 write_addr, u32 write_data)
{
	return wan_ec_write_internal_dword(ec_dev, write_addr, write_data);
}


/*===========================================================================*\
  Oct6100WriteSequenceSub
\*===========================================================================*/
static u32
wan_ec_write_seq(wan_ec_dev_t *ec_dev, u32 write_addr, u16 write_data)
{
	u32	ulResult;
	u32	ulData;
	u16	usData;
	u32	ulWordAddress;
	u32	i;

	/* Create the word address from the provided byte address. */
	ulWordAddress = write_addr >> 1;

	/* 16-bit indirect access. */

	/* First write to the address indirection registers. */
	ulData = ( ulWordAddress >> 19 ) & 0x1FFF;
	ulResult = wan_ec_write( ec_dev, 0x8, ulData );
	if (ulResult)
		return ulResult;

	ulData = ( ulWordAddress >> 3 ) & 0xFFFF;
	ulResult = wan_ec_write( ec_dev, 0xA, ulData );
	if (ulResult)
		return ulResult;

	/* Next, write data word to read/write data registers. */
	ulData = write_data & 0xFFFF;
	ulResult = wan_ec_write( ec_dev, 0x4, ulData );
	if ( ulResult )
		return ulResult;


	/* Write the parities and write enables, as well as last three bits
	** of wadd and request the write access. */
	ulData = ( ( 0x0 & 0x3 ) << 14 ) | ( ( 0x3 & 0x3 ) << 12 ) | ( ( ulWordAddress & 0x7 ) << 9 ) | 0x0100;
	ulResult = wan_ec_write( ec_dev, 0x0, ulData );
	if ( ulResult )
		return ulResult;

	/* Keep polling register contol0 for the access_req bit to go low. */
	for ( i = 0; i < WANEC_READ_LIMIT; i++ )
	{
		ulResult = wan_ec_read( ec_dev, 0, &usData );
		if ( ulResult )
			return ulResult;

		if ( ( ( usData >> 8 ) & 0x1 ) == 0x0 )
			break;
	}

	if ( i == WANEC_READ_LIMIT ){
		DEBUG_ERROR("%s: Error: EC write command reached re-try limit!\n",
				ec_dev->name);
		return WAN_EC_RC_CPU_INTERFACE_NO_RESPONSE;
	}
	return 0;
}


/*===========================================================================*\
  HandleReqWriteOct6100
\*===========================================================================*/
u32 wanec_req_write(void *arg, u32 write_addr, u16 write_data)
{
	wan_ec_dev_t	*ec_dev = (wan_ec_dev_t*)arg;
	u32		ulResult;

	DEBUG_TEST("%s: EC WRITE API addr=%X data=%X\n",
				ec_dev->ec->name, write_addr, write_data);
	ulResult = wan_ec_write_seq(ec_dev, write_addr, write_data);
	if (ulResult){
		DEBUG_ERROR("%s: Error: Failed to write %04X to addr %08X\n",
						ec_dev->name,
						write_addr,
						write_data);
	}
	return ulResult;
}


/*===========================================================================*\
  HandleReqWriteSmearOct6100
\*===========================================================================*/
u32 wanec_req_write_smear(void *arg, u32 addr, u16 data, u32 len)
{
	wan_ec_dev_t	*ec_dev = (wan_ec_dev_t*)arg;
	u32		i, ulResult = WAN_EC_RC_OK;

	WAN_ASSERT(ec_dev == NULL);
	for ( i = 0; i < len; i++ ){
		ulResult = wan_ec_write_seq(ec_dev, addr + (i*2), data);
		if (ulResult){
			DEBUG_ERROR("%s: Error: Failed to write %04X to addr %08X\n",
						ec_dev->name,
						addr + (i*2),
						data);
			break;
		}
	}
	return ulResult;
}


/*===========================================================================*\
  HandleReqWriteBurstOct6100
\*===========================================================================*/
u32 wanec_req_write_burst(void *arg, u32 addr, u16 *data, u32 len)
{
	wan_ec_dev_t	*ec_dev = (wan_ec_dev_t*)arg;
	u32		i, ulResult = WAN_EC_RC_OK;

	WAN_ASSERT(ec_dev == NULL);

	for ( i = 0; i < len; i++ ){
		ulResult = wan_ec_write_seq(ec_dev, addr + (i * 2), data[i]);
		if (ulResult){
			DEBUG_ERROR("%s: Error: Failed to write %04X to addr %08X\n",
						ec_dev->name,
						addr + (i*2),
						data[i]);
			break;
		}
	}
	return ulResult;
}


/*===========================================================================*\
  Oct6100ReadSequenceSub
\*===========================================================================*/
static u32
wan_ec_read_seq(wan_ec_dev_t *ec_dev, u32 read_addr, u16 *read_data, u32 read_len)
{
	u32	ulResult;
	u32	ulData;
	u32	ulWordAddress;
	u32	ulReadBurstLength;
	u16	usData;
	u32	i;

	/* Create the word address from the provided byte address. */
	ulWordAddress = read_addr >> 1;

	/* Indirect accesses. */

	/* First write to the address indirection registers. */
	ulData = ( ulWordAddress >> 19 ) & 0x1FFF;
	ulResult = wan_ec_write( ec_dev, 0x8, ulData );
	if (ulResult)
		return ulResult;

	ulData = ( ulWordAddress >> 3 ) & 0xFFFF;
	ulResult = wan_ec_write( ec_dev, 0xA, ulData );
	if (ulResult)
		return ulResult;

	/* Request access. */
	if ( read_len >= 128 )
	{
		ulData = 0x100 | ( ( ulWordAddress & 0x7 ) << 9);
		ulReadBurstLength = 0;
	}
	else
	{
		ulData = 0x100 | ( ( ulWordAddress & 0x7 ) << 9) | read_len;
		ulReadBurstLength = read_len;
	}
	ulResult = wan_ec_write( ec_dev, 0x0, ulData );
	if (ulResult)
		return ulResult;

	/* Keep polling register contol0 for the access_req bit to go low. */
	for ( i = 0; i < WANEC_READ_LIMIT; i++ )
	{
		ulResult = wan_ec_read( ec_dev, 0x0, &usData );
		if (ulResult)
			return ulResult;

		if ( ( ( usData >> 8 ) & 0x1 ) == 0x0 )
			break;
	}
	if ( i == WANEC_READ_LIMIT ){
		DEBUG_ERROR("%s: Error: EC read command reached re-try limit!\n",
				ec_dev->name);
		return WAN_EC_RC_CPU_INTERFACE_NO_RESPONSE;
	}

	if ( ( usData & 0xFF ) == 0x1 )
	{
		i = 0;
	}

	/* Retrieve read data. */
	ulResult = wan_ec_read( ec_dev, 0x4, &usData );
	if (ulResult)
		return ulResult;

	if ( ( usData & 0xFF ) == 0x1 )
	{
		i = 0;
	}

	*read_data = usData;
	return 0;
}

u32 wanec_req_read(void *arg, u32 addr, u16 *data)
{
	wan_ec_dev_t	*ec_dev = (wan_ec_dev_t*)arg;
	u32		ulResult;

	WAN_ASSERT(ec_dev == NULL);
	DEBUG_TEST("%s: EC READ API addr=%X data=????\n",
			ec_dev->ec->name, addr);
	ulResult = wan_ec_read_seq(
				ec_dev,
				addr,
				data,
				1);
	if (ulResult){
		DEBUG_ERROR("%s: Error: Failed to read data from addr %08X\n",
					ec_dev->name,
					addr);
		if (ec_dev->ec){
			wan_set_bit(WAN_EC_BIT_CRIT_ERROR, &ec_dev->ec->critical);
		}
	}
	DEBUG_TEST("%s: EC READ API addr=%X data=%X\n",
			ec_dev->ec->name,
			addr, *data);
	return ulResult;
}

u32 wanec_req_read_burst(void *arg, u32 addr, u16 *data, u32 len)
{
	wan_ec_dev_t	*ec_dev = (wan_ec_dev_t*)arg;
	u32		i, ulResult = WAN_EC_RC_OK;
	u16		read_data;

	for ( i = 0; i < len; i++ ){
		ulResult = wan_ec_read_seq(ec_dev, addr, &read_data, 1);
		if (ulResult){
			DEBUG_EVENT("%s: Failed to read from addr %X\n",
						ec_dev->name,
						addr);
			if (ec_dev->ec){
				wan_set_bit(WAN_EC_BIT_CRIT_ERROR, &ec_dev->ec->critical);
			}
			break;
		}
		data[i]  = (u16)read_data;
		addr += 2;
	}
	return ulResult;
}
