/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*\

File: oct6100_playout_buf.c

    Copyright (c) 2001-2008 Octasic Inc.
    
Description: 

	This file contains functions used to manage buffer playout.

This file is part of the Octasic OCT6100 GPL API . The OCT6100 GPL API  is 
free software; you can redistribute it and/or modify it under the terms of 
the GNU General Public License as published by the Free Software Foundation; 
either version 2 of the License, or (at your option) any later version.

The OCT6100 GPL API is distributed in the hope that it will be useful, but 
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 
for more details. 

You should have received a copy of the GNU General Public License 
along with the OCT6100 GPL API; if not, write to the Free Software 
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.

$Octasic_Release: OCT612xAPI-01.01.01 $

$Octasic_Revision: 109 $

\*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/


/*****************************  INCLUDE FILES  *******************************/

#include "octdef.h"

#include "oct6100api/oct6100_defines.h"
#include "oct6100api/oct6100_errors.h"
#include "oct6100api/oct6100_apiud.h"

#include "apilib/octapi_llman.h"

#include "oct6100api/oct6100_tlv_inst.h"
#include "oct6100api/oct6100_chip_open_inst.h"
#include "oct6100api/oct6100_chip_stats_inst.h"
#include "oct6100api/oct6100_interrupts_inst.h"
#include "oct6100api/oct6100_remote_debug_inst.h"
#include "oct6100api/oct6100_debug_inst.h"
#include "oct6100api/oct6100_api_inst.h"
#include "oct6100api/oct6100_channel_inst.h"
#include "oct6100api/oct6100_playout_buf_inst.h"

#include "oct6100api/oct6100_interrupts_pub.h"
#include "oct6100api/oct6100_chip_open_pub.h"
#include "oct6100api/oct6100_channel_pub.h"
#include "oct6100api/oct6100_events_pub.h"
#include "oct6100api/oct6100_playout_buf_pub.h"

#include "oct6100_chip_open_priv.h"
#include "oct6100_miscellaneous_priv.h"
#include "oct6100_memory_priv.h"
#include "oct6100_channel_priv.h"
#include "oct6100_events_priv.h"
#include "oct6100_playout_buf_priv.h"

/****************************  PUBLIC FUNCTIONS  *****************************/

/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*\

Function:		Oct6100BufferPlayoutLoad

Description:    This function loads a playout buffer into external memory.

-------------------------------------------------------------------------------
|	Argument		|	Description
-------------------------------------------------------------------------------
f_pApiInstance			Pointer to API instance. This memory is used to keep the
						present state of the chip and all its resources.

f_pBufferLoad			Pointer to buffer playout load structure.

\*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
UINT32 Oct6100BufferPlayoutLoadDef(
				tPOCT6100_BUFFER_LOAD			f_pBufferLoad )
{
	f_pBufferLoad->pbyBufferPattern = NULL;
	f_pBufferLoad->ulBufferSize = 128;
	f_pBufferLoad->ulBufferPcmLaw = cOCT6100_PCM_U_LAW;
	
	f_pBufferLoad->pulBufferIndex = NULL;
	f_pBufferLoad->pulPlayoutFreeMemSize = NULL;

	return cOCT6100_ERR_OK;
}

UINT32 Oct6100BufferPlayoutLoad(
				tPOCT6100_INSTANCE_API				f_pApiInstance,
				tPOCT6100_BUFFER_LOAD				f_pBufferLoad )
{
	tOCT6100_SEIZE_SERIALIZE_OBJECT		SeizeSerObj;
	tOCT6100_RELEASE_SERIALIZE_OBJECT	ReleaseSerObj;
	UINT32								ulSerRes = cOCT6100_ERR_OK;
	UINT32								ulFncRes = cOCT6100_ERR_OK;

	/* Set the process context of the serialize structure. */
	SeizeSerObj.pProcessContext = f_pApiInstance->pProcessContext;
	ReleaseSerObj.pProcessContext = f_pApiInstance->pProcessContext;

	/* Seize all list semaphores needed by this function. */
	SeizeSerObj.ulSerialObjHndl = f_pApiInstance->ulApiSerObj;
	SeizeSerObj.ulTryTimeMs = cOCT6100_WAIT_INFINITELY;
	ulSerRes = Oct6100UserSeizeSerializeObject( &SeizeSerObj );
	if ( ulSerRes == cOCT6100_ERR_OK )
	{
		/* Call the serialized function. */
		ulFncRes = Oct6100BufferLoadSer( f_pApiInstance, f_pBufferLoad, TRUE, cOCT6100_INVALID_INDEX );
	}
	else
	{
		return ulSerRes;
	}

	/* Release the seized semaphores. */
	ReleaseSerObj.ulSerialObjHndl = f_pApiInstance->ulApiSerObj;
	ulSerRes = Oct6100UserReleaseSerializeObject( &ReleaseSerObj );

	/* If an error occured then return the error code. */
	if ( ulSerRes != cOCT6100_ERR_OK )
		return ulSerRes;
	if ( ulFncRes != cOCT6100_ERR_OK )
		return ulFncRes;

	return cOCT6100_ERR_OK;
}

/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*\

Function:		Oct6100BufferPlayoutLoadBlockInit

Description:    This function allows the user to initialize loading a buffer 
				into external memory using blocks.

-------------------------------------------------------------------------------
|	Argument		|	Description
-------------------------------------------------------------------------------
f_pApiInstance			Pointer to API instance. This memory is used to keep 
						the present state of the chip and all its resources.

f_pBufferLoadBlockInit	Pointer to buffer playout load block init structure.

\*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
UINT32 Oct6100BufferPlayoutLoadBlockInitDef(
				tPOCT6100_BUFFER_LOAD_BLOCK_INIT	f_pBufferLoadBlockInit )
{
	f_pBufferLoadBlockInit->ulBufferSize = 128;
	f_pBufferLoadBlockInit->ulBufferPcmLaw = cOCT6100_PCM_U_LAW;
	
	f_pBufferLoadBlockInit->pulBufferIndex = NULL;
	f_pBufferLoadBlockInit->pulPlayoutFreeMemSize = NULL;

	return cOCT6100_ERR_OK;
}

UINT32 Oct6100BufferPlayoutLoadBlockInit(
				tPOCT6100_INSTANCE_API				f_pApiInstance,
				tPOCT6100_BUFFER_LOAD_BLOCK_INIT	f_pBufferLoadBlockInit )
{
	tOCT6100_SEIZE_SERIALIZE_OBJECT		SeizeSerObj;
	tOCT6100_RELEASE_SERIALIZE_OBJECT	ReleaseSerObj;
	UINT32								ulSerRes = cOCT6100_ERR_OK;
	UINT32								ulFncRes = cOCT6100_ERR_OK;

	/* Set the process context of the serialize structure.*/
	SeizeSerObj.pProcessContext = f_pApiInstance->pProcessContext;
	ReleaseSerObj.pProcessContext = f_pApiInstance->pProcessContext;

	/* Seize all list semaphores needed by this function. */
	SeizeSerObj.ulSerialObjHndl = f_pApiInstance->ulApiSerObj;
	SeizeSerObj.ulTryTimeMs = cOCT6100_WAIT_INFINITELY;
	ulSerRes = Oct6100UserSeizeSerializeObject( &SeizeSerObj );
	if ( ulSerRes == cOCT6100_ERR_OK )
	{
		/* Call the serialized function. */
		ulFncRes = Oct6100BufferLoadBlockInitSer( f_pApiInstance, f_pBufferLoadBlockInit );
	}
	else
	{
		return ulSerRes;
	}

	/* Release the seized semaphores. */
	ReleaseSerObj.ulSerialObjHndl = f_pApiInstance->ulApiSerObj;
	ulSerRes = Oct6100UserReleaseSerializeObject( &ReleaseSerObj );

	/* If an error occured then return the error code. */
	if ( ulSerRes != cOCT6100_ERR_OK )
		return ulSerRes;
	if ( ulFncRes != cOCT6100_ERR_OK )
		return ulFncRes;

	return cOCT6100_ERR_OK;
}


/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*\

Function:		Oct6100BufferPlayoutLoadBlock

Description:	This function allows the user to load a buffer block into 
				external memory.

-------------------------------------------------------------------------------
|	Argument		|	Description
-------------------------------------------------------------------------------
f_pApiInstance			Pointer to API instance. This memory is used to keep 
						the present state of the chip and all its resources.

f_pBufferLoadBlock		Pointer to buffer playout load block structure.

\*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
UINT32 Oct6100BufferPlayoutLoadBlockDef(
				tPOCT6100_BUFFER_LOAD_BLOCK		f_pBufferLoadBlock )
{
	f_pBufferLoadBlock->ulBufferIndex = cOCT6100_INVALID_VALUE;
	f_pBufferLoadBlock->ulBlockLength = cOCT6100_INVALID_VALUE;
	f_pBufferLoadBlock->ulBlockOffset = cOCT6100_INVALID_VALUE;
	
	f_pBufferLoadBlock->pbyBufferPattern = NULL;

	return cOCT6100_ERR_OK;
}

UINT32 Oct6100BufferPlayoutLoadBlock(
				tPOCT6100_INSTANCE_API				f_pApiInstance,
				tPOCT6100_BUFFER_LOAD_BLOCK			f_pBufferLoadBlock )
{
	tOCT6100_SEIZE_SERIALIZE_OBJECT		SeizeSerObj;
	tOCT6100_RELEASE_SERIALIZE_OBJECT	ReleaseSerObj;
	UINT32								ulSerRes = cOCT6100_ERR_OK;
	UINT32								ulFncRes = cOCT6100_ERR_OK;

	/* Set the process context of the serialize structure. */
	SeizeSerObj.pProcessContext = f_pApiInstance->pProcessContext;
	ReleaseSerObj.pProcessContext = f_pApiInstance->pProcessContext;

	/* Seize all list semaphores needed by this function. */
	SeizeSerObj.ulSerialObjHndl = f_pApiInstance->ulApiSerObj;
	SeizeSerObj.ulTryTimeMs = cOCT6100_WAIT_INFINITELY;
	ulSerRes = Oct6100UserSeizeSerializeObject( &SeizeSerObj );
	if ( ulSerRes == cOCT6100_ERR_OK )
	{
		/* Call the serialized function. */
		ulFncRes = Oct6100BufferLoadBlockSer( f_pApiInstance, f_pBufferLoadBlock );
	}
	else
	{
		return ulSerRes;
	}

	/* Release the seized semaphores. */
	ReleaseSerObj.ulSerialObjHndl = f_pApiInstance->ulApiSerObj;
	ulSerRes = Oct6100UserReleaseSerializeObject( &ReleaseSerObj );

	/* If an error occured then return the error code. */
	if ( ulSerRes != cOCT6100_ERR_OK )
		return ulSerRes;
	if ( ulFncRes != cOCT6100_ERR_OK )
		return ulFncRes;

	return cOCT6100_ERR_OK;
}


/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*\

Function:		Oct6100BufferPlayoutUnload

Description:    This function unloads a playout buffer from external memory.

-------------------------------------------------------------------------------
|	Argument		|	Description
-------------------------------------------------------------------------------
f_pApiInstance			Pointer to API instance. This memory is used to keep the
						present state of the chip and all its resources.

f_pBufferUnload			Pointer to buffer playout unload structure.

\*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
UINT32 Oct6100BufferPlayoutUnloadDef(
				tPOCT6100_BUFFER_UNLOAD			f_pBufferUnload )
{
	f_pBufferUnload->ulBufferIndex = cOCT6100_INVALID_VALUE;

	return cOCT6100_ERR_OK;
}

UINT32 Oct6100BufferPlayoutUnload(
				tPOCT6100_INSTANCE_API			f_pApiInstance,
				tPOCT6100_BUFFER_UNLOAD			f_pBufferUnload )
{
	tOCT6100_SEIZE_SERIALIZE_OBJECT		SeizeSerObj;
	tOCT6100_RELEASE_SERIALIZE_OBJECT	ReleaseSerObj;
	UINT32								ulSerRes = cOCT6100_ERR_OK;
	UINT32								ulFncRes = cOCT6100_ERR_OK;

	/* Set the process context of the serialize structure. */
	SeizeSerObj.pProcessContext = f_pApiInstance->pProcessContext;
	ReleaseSerObj.pProcessContext = f_pApiInstance->pProcessContext;

	/* Seize all list semaphores needed by this function. */
	SeizeSerObj.ulSerialObjHndl = f_pApiInstance->ulApiSerObj;
	SeizeSerObj.ulTryTimeMs = cOCT6100_WAIT_INFINITELY;
	ulSerRes = Oct6100UserSeizeSerializeObject( &SeizeSerObj );
	if ( ulSerRes == cOCT6100_ERR_OK )
	{
		/* Call the serialized function. */
		ulFncRes = Oct6100BufferUnloadSer( f_pApiInstance, f_pBufferUnload, TRUE );
	}
	else
	{
		return ulSerRes;
	}

	/* Release the seized semaphores. */
	ReleaseSerObj.ulSerialObjHndl = f_pApiInstance->ulApiSerObj;
	ulSerRes = Oct6100UserReleaseSerializeObject( &ReleaseSerObj );

	/* If an error occured then return the error code. */
	if ( ulSerRes != cOCT6100_ERR_OK )
		return ulSerRes;
	if ( ulFncRes != cOCT6100_ERR_OK )
		return ulFncRes;

	return cOCT6100_ERR_OK;
}


/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*\

Function:		Oct6100BufferPlayoutAdd

Description:    This function adds a buffer to a port's playout list on the 
				selected channel.

-------------------------------------------------------------------------------
|	Argument		|	Description
-------------------------------------------------------------------------------
f_pApiInstance			Pointer to API instance. This memory is used to keep the
						present state of the chip and all its resources.

f_pBufferPlayoutAdd		Pointer to buffer playout add structure.

\*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
UINT32 Oct6100BufferPlayoutAddDef(
				tPOCT6100_BUFFER_PLAYOUT_ADD			f_pBufferPlayoutAdd )
{
	f_pBufferPlayoutAdd->ulChannelHndl = cOCT6100_INVALID_HANDLE;
	f_pBufferPlayoutAdd->ulBufferIndex = cOCT6100_INVALID_VALUE;

	f_pBufferPlayoutAdd->ulPlayoutPort = cOCT6100_CHANNEL_PORT_ROUT;
	f_pBufferPlayoutAdd->ulMixingMode = cOCT6100_MIXING_MINUS_6_DB;
	f_pBufferPlayoutAdd->lGainDb = 0;

	f_pBufferPlayoutAdd->fRepeat = FALSE;
	f_pBufferPlayoutAdd->ulRepeatCount = cOCT6100_REPEAT_INFINITELY;

	f_pBufferPlayoutAdd->ulDuration = cOCT6100_INVALID_VALUE;
	
	f_pBufferPlayoutAdd->ulBufferLength = cOCT6100_AUTO_SELECT;

	return cOCT6100_ERR_OK;
}

UINT32 Oct6100BufferPlayoutAdd(
				tPOCT6100_INSTANCE_API					f_pApiInstance,
				tPOCT6100_BUFFER_PLAYOUT_ADD			f_pBufferPlayoutAdd )
{
	tOCT6100_SEIZE_SERIALIZE_OBJECT		SeizeSerObj;
	tOCT6100_RELEASE_SERIALIZE_OBJECT	ReleaseSerObj;
	UINT32								ulSerRes = cOCT6100_ERR_OK;
	UINT32								ulFncRes = cOCT6100_ERR_OK;

	/* Set the process context of the serialize structure. */
	SeizeSerObj.pProcessContext = f_pApiInstance->pProcessContext;
	ReleaseSerObj.pProcessContext = f_pApiInstance->pProcessContext;

	/* Seize all list semaphores needed by this function. */
	SeizeSerObj.ulSerialObjHndl = f_pApiInstance->ulApiSerObj;
	SeizeSerObj.ulTryTimeMs = cOCT6100_WAIT_INFINITELY;
	ulSerRes = Oct6100UserSeizeSerializeObject( &SeizeSerObj );
	if ( ulSerRes == cOCT6100_ERR_OK )
	{
		/* Call the serialized function. */
		ulFncRes = Oct6100BufferPlayoutAddSer( f_pApiInstance, f_pBufferPlayoutAdd );
	}
	else
	{
		return ulSerRes;
	}

	/* Release the seized semaphores. */
	ReleaseSerObj.ulSerialObjHndl = f_pApiInstance->ulApiSerObj;
	ulSerRes = Oct6100UserReleaseSerializeObject( &ReleaseSerObj );

	/* If an error occured then return the error code. */
	if ( ulSerRes != cOCT6100_ERR_OK )
		return ulSerRes;
	if ( ulFncRes != cOCT6100_ERR_OK )
		return ulFncRes;

	return cOCT6100_ERR_OK;
}


/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*\

Function:		Oct6100BufferPlayoutStart

Description:    This function enables playout of the specified buffer on the 
				requested channel.

-------------------------------------------------------------------------------
|	Argument		|	Description
-------------------------------------------------------------------------------
f_pApiInstance			Pointer to API instance. This memory is used to keep the
						present state of the chip and all its resources.

f_pBufferPlayoutStart	Pointer to buffer playout start structure.

\*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
UINT32 Oct6100BufferPlayoutStartDef(
				tPOCT6100_BUFFER_PLAYOUT_START	f_pBufferPlayoutStart )
{
	f_pBufferPlayoutStart->ulChannelHndl = cOCT6100_INVALID_HANDLE;
	f_pBufferPlayoutStart->ulPlayoutPort = cOCT6100_CHANNEL_PORT_ROUT;
	f_pBufferPlayoutStart->fNotifyOnPlayoutStop = FALSE;
	f_pBufferPlayoutStart->ulUserEventId = cOCT6100_INVALID_VALUE;
	f_pBufferPlayoutStart->fAllowStartWhileActive = FALSE;

	return cOCT6100_ERR_OK;
}

UINT32 Oct6100BufferPlayoutStart(
				tPOCT6100_INSTANCE_API				f_pApiInstance,
				tPOCT6100_BUFFER_PLAYOUT_START		f_pBufferPlayoutStart )
{
	tOCT6100_SEIZE_SERIALIZE_OBJECT		SeizeSerObj;
	tOCT6100_RELEASE_SERIALIZE_OBJECT	ReleaseSerObj;
	UINT32								ulSerRes = cOCT6100_ERR_OK;
	UINT32								ulFncRes = cOCT6100_ERR_OK;

	/* Set the process context of the serialize structure. */
	SeizeSerObj.pProcessContext = f_pApiInstance->pProcessContext;
	ReleaseSerObj.pProcessContext = f_pApiInstance->pProcessContext;

	/* Seize all list semaphores needed by this function. */
	SeizeSerObj.ulSerialObjHndl = f_pApiInstance->ulApiSerObj;
	SeizeSerObj.ulTryTimeMs = cOCT6100_WAIT_INFINITELY;
	ulSerRes = Oct6100UserSeizeSerializeObject( &SeizeSerObj );
	if ( ulSerRes == cOCT6100_ERR_OK )
	{
		/* Call the serialized function. */
		ulFncRes = Oct6100BufferPlayoutStartSer( f_pApiInstance, f_pBufferPlayoutStart, cOCT6100_BUFFER_PLAYOUT_EVENT_STOP );
	}
	else
	{
		return ulSerRes;
	}

	/* Release the seized semaphores. */
	ReleaseSerObj.ulSerialObjHndl = f_pApiInstance->ulApiSerObj;
	ulSerRes = Oct6100UserReleaseSerializeObject( &ReleaseSerObj );

	/* If an error occured then return the error code. */
	if ( ulSerRes != cOCT6100_ERR_OK )
		return ulSerRes;
	if ( ulFncRes != cOCT6100_ERR_OK )
		return ulFncRes;

	return cOCT6100_ERR_OK;
}


/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*\

Function:		Oct6100BufferPlayoutStop

Description:    This function disables playout of a buffer on the specified 
				channel.

-------------------------------------------------------------------------------
|	Argument		|	Description
-------------------------------------------------------------------------------
f_pApiInstance			Pointer to API instance. This memory is used to keep the
						present state of the chip and all its resources.

f_pBufferPlayoutStop	Pointer to buffer playout stop structure.

\*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
UINT32 Oct6100BufferPlayoutStopDef(
				tPOCT6100_BUFFER_PLAYOUT_STOP	f_pBufferPlayoutStop )
{
	f_pBufferPlayoutStop->ulChannelHndl = cOCT6100_INVALID_HANDLE;
	f_pBufferPlayoutStop->ulPlayoutPort = cOCT6100_CHANNEL_PORT_ROUT;
	f_pBufferPlayoutStop->fStopCleanly = TRUE;
	f_pBufferPlayoutStop->pfAlreadyStopped = NULL;
	f_pBufferPlayoutStop->pfNotifyOnPlayoutStop = NULL;

	return cOCT6100_ERR_OK;
}

UINT32 Oct6100BufferPlayoutStop(
				tPOCT6100_INSTANCE_API				f_pApiInstance,
				tPOCT6100_BUFFER_PLAYOUT_STOP	f_pBufferPlayoutStop )
{
	tOCT6100_SEIZE_SERIALIZE_OBJECT		SeizeSerObj;
	tOCT6100_RELEASE_SERIALIZE_OBJECT	ReleaseSerObj;
	UINT32								ulSerRes = cOCT6100_ERR_OK;
	UINT32								ulFncRes = cOCT6100_ERR_OK;

	/* Set the process context of the serialize structure. */
	SeizeSerObj.pProcessContext = f_pApiInstance->pProcessContext;
	ReleaseSerObj.pProcessContext = f_pApiInstance->pProcessContext;

	/* Seize all list semaphores needed by this function. */
	SeizeSerObj.ulSerialObjHndl = f_pApiInstance->ulApiSerObj;
	SeizeSerObj.ulTryTimeMs = cOCT6100_WAIT_INFINITELY;
	ulSerRes = Oct6100UserSeizeSerializeObject( &SeizeSerObj );
	if ( ulSerRes == cOCT6100_ERR_OK )
	{
		/* Call the serialized function. */
		ulFncRes = Oct6100BufferPlayoutStopSer( f_pApiInstance, f_pBufferPlayoutStop );
	}
	else
	{
		return ulSerRes;
	}

	/* Release the seized semaphores. */
	ReleaseSerObj.ulSerialObjHndl = f_pApiInstance->ulApiSerObj;
	ulSerRes = Oct6100UserReleaseSerializeObject( &ReleaseSerObj );

	/* If an error occured then return the error code. */
	if ( ulSerRes != cOCT6100_ERR_OK )
		return ulSerRes;
	if ( ulFncRes != cOCT6100_ERR_OK )
		return ulFncRes;

	return cOCT6100_ERR_OK;
}


/****************************  PRIVATE FUNCTIONS  ****************************/

/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*\

Function:		Oct6100ApiGetPlayoutBufferSwSizes

Description:    Gets the sizes of all portions of the API instance pertinent
				to the management of playout buffers.

-------------------------------------------------------------------------------
|	Argument		|	Description
-------------------------------------------------------------------------------
f_pOpenChip				Pointer to chip configuration struct.
f_pInstSizes			Pointer to struct containing instance sizes.

\*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
UINT32 Oct6100ApiGetPlayoutBufferSwSizes(
				IN		tPOCT6100_CHIP_OPEN				f_pOpenChip,
				OUT		tPOCT6100_API_INSTANCE_SIZES	f_pInstSizes )
{
	UINT32	ulTempVar;
	UINT32	ulResult;

	/* Calculate memory needed for playout buffer list. */
	f_pInstSizes->ulPlayoutBufList = f_pOpenChip->ulMaxPlayoutBuffers * sizeof( tOCT6100_API_BUFFER );

	f_pInstSizes->ulPlayoutBufMemoryNodeList = 0;
	
	/* Calculate memory needed for playout buffer allocation software. */
	if ( f_pOpenChip->ulMaxPlayoutBuffers > 0 )
	{
		ulResult = OctapiLlmAllocGetSize( f_pOpenChip->ulMaxPlayoutBuffers, &f_pInstSizes->ulPlayoutBufAlloc );
		if ( ulResult != cOCT6100_ERR_OK )
			return cOCT6100_ERR_FATAL_3C;
		
		f_pInstSizes->ulPlayoutBufMemoryNodeList = 2 * f_pOpenChip->ulMaxPlayoutBuffers * sizeof( tOCT6100_API_BUFFER_PLAYOUT_MALLOC_NODE );
	}
	else
	{
		f_pInstSizes->ulPlayoutBufAlloc  = 0;
	}

	/* Calculate memory needed for list and allocation software serialization. */
	mOCT6100_ROUND_MEMORY_SIZE( f_pInstSizes->ulPlayoutBufList, ulTempVar )
	mOCT6100_ROUND_MEMORY_SIZE( f_pInstSizes->ulPlayoutBufAlloc, ulTempVar )
	mOCT6100_ROUND_MEMORY_SIZE( f_pInstSizes->ulPlayoutBufMemoryNodeList, ulTempVar )

	return cOCT6100_ERR_OK;
}


/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*\

Function:		Oct6100ApiPlayoutBufferSwInit

Description:    Initializes all elements of the instance structure associated
				to playout buffers.

-------------------------------------------------------------------------------
|	Argument		|	Description
-------------------------------------------------------------------------------
f_pApiInstance			Pointer to API instance. This memory is used to keep
						the present state of the chip and all its resources.

\*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
UINT32 Oct6100ApiPlayoutBufferSwInit(
				IN OUT	tPOCT6100_INSTANCE_API			f_pApiInstance )
{
	tPOCT6100_SHARED_INFO			pSharedInfo;
	tPOCT6100_API_BUFFER			pBufferList;
	PVOID	pBufferPlayoutAlloc;
	UINT32	ulMaxBufferPlayout;
	UINT32	ulResult, i;

	/* Get local pointer to shared portion of instance. */
	pSharedInfo = f_pApiInstance->pSharedInfo;

	/* Get the maximum number of buffer playout. */
	ulMaxBufferPlayout = pSharedInfo->ChipConfig.usMaxPlayoutBuffers;

	/* Set all entries in the buffer playout list to unused. */
	mOCT6100_GET_BUFFER_LIST_PNT( pSharedInfo, pBufferList )

	for ( i = 0; i < ulMaxBufferPlayout; i++ )
	{
		pBufferList[ i ].fReserved = FALSE;
		pBufferList[ i ].ulBufferSize = 0;
		pBufferList[ i ].ulBufferBase = cOCT6100_INVALID_VALUE;
		pBufferList[ i ].usDependencyCnt = 0;
		pBufferList[ i ].byBufferPcmLaw = cOCT6100_PCM_U_LAW;
		
	}

	/* Initialize the buffer playout allocation software to "all free". */
	if ( ulMaxBufferPlayout > 0 )
	{
		mOCT6100_GET_BUFFER_ALLOC_PNT( pSharedInfo, pBufferPlayoutAlloc )
		
		ulResult = OctapiLlmAllocInit( &pBufferPlayoutAlloc, ulMaxBufferPlayout );
		if ( ulResult != cOCT6100_ERR_OK )
			return cOCT6100_ERR_FATAL_3D;
	}

	/* Initialize the amount of free memory used by playout. */
	f_pApiInstance->pSharedInfo->ChipStats.ulPlayoutMemUsed = 0;

	return cOCT6100_ERR_OK;
}


/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*\

Function:		Oct6100BufferLoadSer

Description:    Loads a buffer in external memory.

-------------------------------------------------------------------------------
|	Argument		|	Description
-------------------------------------------------------------------------------
f_pApiInstance			Pointer to API instance. This memory is used to keep the
						present state of the chip and all its resources.

f_pBufferLoad			Pointer to buffer configuration structure. The handle
						identifying the buffer in all future function calls is
						returned in this structure.
						
f_fReserveListStruct	Flag indicating if a list structure should be reserved
						or if the structure has been reserved before.  If this
						is set, the f_ulBufIndex variable must also be set.

f_ulBufIndex			If the f_fReserveListStruct flag is set, this index
						will identify the buffer playout list structure
						that must be used to load the specified buffer.

\*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
UINT32 Oct6100BufferLoadSer(
				IN OUT	tPOCT6100_INSTANCE_API		f_pApiInstance,
				IN OUT	tPOCT6100_BUFFER_LOAD		f_pBufferLoad,
				IN		BOOL						f_fReserveListStruct, 
				IN		UINT32						f_ulBufIndex )
{
	UINT32	ulBufferIndex;
	UINT32	ulBufferBase;
	UINT32	ulResult;

	/* Check the user's configuration of the buffer for errors. */
	ulResult = Oct6100ApiCheckBufferParams( f_pApiInstance, f_pBufferLoad, TRUE );
	if ( ulResult != cOCT6100_ERR_OK )
		return ulResult;

	/* Reserve all resources needed by the buffer. */
	ulResult = Oct6100ApiReserveBufferResources( f_pApiInstance, f_pBufferLoad, f_fReserveListStruct, f_ulBufIndex, &ulBufferIndex, &ulBufferBase );
	if ( ulResult != cOCT6100_ERR_OK )
		return ulResult;
	
	/* Write the buffer in external memory. */
	ulResult = Oct6100ApiWriteBufferInMemory( f_pApiInstance, ulBufferBase, f_pBufferLoad->ulBufferSize, f_pBufferLoad->pbyBufferPattern );
	if ( ulResult != cOCT6100_ERR_OK )
		return ulResult;

	/* Update the new buffer's entry in the buffer list. */
	ulResult = Oct6100ApiUpdateBufferEntry( f_pApiInstance, f_pBufferLoad, ulBufferIndex, ulBufferBase );
	if ( ulResult != cOCT6100_ERR_OK )
		return ulResult;

	return cOCT6100_ERR_OK;
}


/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*\

Function:		Oct6100BufferLoadBlockInitSer

Description:    Reserve resources for loading a buffer into external memory.

-------------------------------------------------------------------------------
|	Argument		|	Description
-------------------------------------------------------------------------------
f_pApiInstance			Pointer to API instance. This memory is used to keep 
						the present state of the chip and all its resources.

f_pBufferLoadBlockInit	Pointer to buffer configuration structure. The 
						handle identifying the buffer in all future 
						function calls is returned in this structure.

\*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
UINT32 Oct6100BufferLoadBlockInitSer(
				IN OUT	tPOCT6100_INSTANCE_API				f_pApiInstance,
				IN OUT	tPOCT6100_BUFFER_LOAD_BLOCK_INIT	f_pBufferLoadBlockInit )
{
	UINT32					ulBufferIndex;
	UINT32					ulBufferBase;
	UINT32					ulResult;
	tOCT6100_BUFFER_LOAD	BufferLoad;

	Oct6100BufferPlayoutLoadDef( &BufferLoad );

	/* Not to replicate the code, we use the BufferLoad functions directly. */
	BufferLoad.pulBufferIndex			= f_pBufferLoadBlockInit->pulBufferIndex;
	BufferLoad.pulPlayoutFreeMemSize	= f_pBufferLoadBlockInit->pulPlayoutFreeMemSize;
	BufferLoad.ulBufferPcmLaw			= f_pBufferLoadBlockInit->ulBufferPcmLaw;
	BufferLoad.ulBufferSize				= f_pBufferLoadBlockInit->ulBufferSize;
	BufferLoad.pbyBufferPattern			= NULL;  /* Must not check this for now */

	/* Check the user's configuration of the buffer for errors, but do */
	/* not check if the buffer pointer is NULL.  It is NULL for sure! */
	ulResult = Oct6100ApiCheckBufferParams( f_pApiInstance, &BufferLoad, FALSE );
	if ( ulResult != cOCT6100_ERR_OK )
		return ulResult;

	/* Reserve all resources needed by the buffer. */
	ulResult = Oct6100ApiReserveBufferResources( f_pApiInstance, &BufferLoad, TRUE, cOCT6100_INVALID_INDEX, &ulBufferIndex, &ulBufferBase );
	if ( ulResult != cOCT6100_ERR_OK )
		return ulResult;

	/* Update the new buffer's entry in the buffer list. */
	ulResult = Oct6100ApiUpdateBufferEntry( f_pApiInstance, &BufferLoad, ulBufferIndex, ulBufferBase );
	if ( ulResult != cOCT6100_ERR_OK )
		return ulResult;

	return cOCT6100_ERR_OK;
}


/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*\

Function:		Oct6100BufferLoadBlockSer

Description:    Loads a buffer in external memory using blocks.

-------------------------------------------------------------------------------
|	Argument		|	Description
-------------------------------------------------------------------------------
f_pApiInstance			Pointer to API instance. This memory is used to keep 
						the present state of the chip and all its resources.

f_pBufferLoadBlock		Pointer to buffer block to be loaded into external 
						memory descriptor. 

\*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
UINT32 Oct6100BufferLoadBlockSer(
				IN OUT	tPOCT6100_INSTANCE_API				f_pApiInstance,
				IN OUT	tPOCT6100_BUFFER_LOAD_BLOCK			f_pBufferLoadBlock )
{
	UINT32	ulBufferBase;
	UINT32	ulResult;

	/* Check the user's configuration for errors. */
	ulResult = Oct6100ApiCheckBufferLoadBlockParams( f_pApiInstance, f_pBufferLoadBlock, &ulBufferBase );
	if ( ulResult != cOCT6100_ERR_OK )
		return ulResult;

	/* Write the buffer in external memory at the appropriate offset - must do some pointer arithmetic. */
	ulResult = Oct6100ApiWriteBufferInMemory( f_pApiInstance, ulBufferBase + f_pBufferLoadBlock->ulBlockOffset, 
					f_pBufferLoadBlock->ulBlockLength, f_pBufferLoadBlock->pbyBufferPattern + f_pBufferLoadBlock->ulBlockOffset );
	if ( ulResult != cOCT6100_ERR_OK )
		return ulResult;

	return cOCT6100_ERR_OK;
}


/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*\

Function:		Oct6100ApiCheckBufferParams

Description:    Checks the user's buffer playout load configuration for errors.

-------------------------------------------------------------------------------
|	Argument		|	Description
-------------------------------------------------------------------------------
f_pApiInstance			Pointer to API instance. This memory is used to keep the
						present state of the chip and all its resources.

f_pBufferLoad			Pointer to buffer configuration structure.
f_fCheckBufferPtr		Check if the buffer pointer is NULL or not.

\*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
UINT32 Oct6100ApiCheckBufferParams(
				IN OUT	tPOCT6100_INSTANCE_API			f_pApiInstance,
				IN OUT	tPOCT6100_BUFFER_LOAD			f_pBufferLoad,
				IN		BOOL							f_fCheckBufferPtr )
{
	/* Check for errors. */
	if ( f_pApiInstance->pSharedInfo->ChipConfig.usMaxPlayoutBuffers == 0 )
		return cOCT6100_ERR_BUFFER_PLAYOUT_DISABLED;

	if ( f_pApiInstance->pSharedInfo->ImageInfo.fBufferPlayout == FALSE )
		return cOCT6100_ERR_NOT_SUPPORTED_BUFFER_PLAYOUT;

	if ( f_pBufferLoad->pulBufferIndex == NULL )
		return cOCT6100_ERR_BUFFER_PLAYOUT_BUF_INDEX;

	if( f_fCheckBufferPtr )
	{
		if ( f_pBufferLoad->pbyBufferPattern == NULL )
			return cOCT6100_ERR_BUFFER_PLAYOUT_PATTERN;
	}

	if ( f_pBufferLoad->ulBufferSize < cOCT6100_MINIMUM_BUFFER_SIZE )
		return cOCT6100_ERR_BUFFER_PLAYOUT_TOO_SMALL;

	if ( ( f_pBufferLoad->ulBufferSize % cOCT6100_BUFFER_SIZE_GRANULARITY ) != 0 )
		return cOCT6100_ERR_BUFFER_PLAYOUT_BUF_SIZE;

	if ( f_pBufferLoad->ulBufferPcmLaw != cOCT6100_PCM_U_LAW && 
		 f_pBufferLoad->ulBufferPcmLaw != cOCT6100_PCM_A_LAW )
		return cOCT6100_ERR_BUFFER_PLAYOUT_PCM_LAW;

	return cOCT6100_ERR_OK;
}


/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*\

Function:		Oct6100ApiCheckBufferLoadBlockParams

Description:    Checks the user's buffer playout load block configuration for 
				errors.

-------------------------------------------------------------------------------
|	Argument		|	Description
-------------------------------------------------------------------------------
f_pApiInstance			Pointer to API instance. This memory is used to keep the
						present state of the chip and all its resources.

f_pBufferLoadBlock		Pointer to buffer block descriptor.
f_pulBufferBase			Pointer to the base address of the buffer in external 
						memory.

\*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
UINT32 Oct6100ApiCheckBufferLoadBlockParams(
				IN OUT	tPOCT6100_INSTANCE_API			f_pApiInstance,
				IN		tPOCT6100_BUFFER_LOAD_BLOCK		f_pBufferLoadBlock,
				OUT		PUINT32							f_pulBufferBase )
{
	/* Check for errors. */
	tPOCT6100_API_BUFFER	pBufEntry;

	if ( f_pBufferLoadBlock->ulBufferIndex >= f_pApiInstance->pSharedInfo->ChipConfig.usMaxPlayoutBuffers )
		return cOCT6100_ERR_BUFFER_PLAYOUT_BUF_INDEX;

	mOCT6100_GET_BUFFER_ENTRY_PNT( f_pApiInstance->pSharedInfo, pBufEntry, f_pBufferLoadBlock->ulBufferIndex )

	if ( pBufEntry->fReserved != TRUE )
		return cOCT6100_ERR_BUFFER_PLAYOUT_NOT_OPEN;

	if ( ( f_pBufferLoadBlock->ulBlockLength % 2 ) != 0 )
		return cOCT6100_ERR_BUFFER_PLAYOUT_BLOCK_LENGTH_INVALID;

	if ( ( f_pBufferLoadBlock->ulBlockOffset % 2 ) != 0 )
		return cOCT6100_ERR_BUFFER_PLAYOUT_BLOCK_OFFSET_INVALID;

	if ( f_pBufferLoadBlock->pbyBufferPattern == NULL )
		return cOCT6100_ERR_BUFFER_PLAYOUT_PATTERN;

	/* Check boundaries */
	if ( ( f_pBufferLoadBlock->ulBlockLength + f_pBufferLoadBlock->ulBlockOffset ) > pBufEntry->ulBufferSize )
		return cOCT6100_ERR_BUFFER_PLAYOUT_BUF_SIZE;

	*f_pulBufferBase = pBufEntry->ulBufferBase;

	return cOCT6100_ERR_OK;
}


/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*\

Function:		Oct6100ApiReserveBufferResources

Description:    Reserves all resources needed for the new buffer.

-------------------------------------------------------------------------------
|	Argument		|	Description
-------------------------------------------------------------------------------
f_pApiInstance			Pointer to API instance. This memory is used to keep the
						present state of the chip and all its resources.
	
f_pBufferLoad			Pointer to buffer configuration structure.

f_fReserveListStruct	Flag indicating if a list structure should be reserved
						or if the structure has been reserved before.

f_ulBufIndex			If the f_fReserveListStruct flag is set, this index
						will identifying the buffer playout list structure
						that must be used to load the specified buffer.

f_pulBufferIndex		Allocated entry in buffer playout list.
	
f_pulBufferBase			Allocated external memory block for the buffer.

\*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
UINT32 Oct6100ApiReserveBufferResources(
				IN OUT	tPOCT6100_INSTANCE_API			f_pApiInstance,
				IN		tPOCT6100_BUFFER_LOAD			f_pBufferLoad,
				IN		BOOL							f_fReserveListStruct,
				IN		UINT32							f_ulBufIndex,
				OUT		PUINT32							f_pulBufferIndex,
				OUT		PUINT32							f_pulBufferBase )
{
	tPOCT6100_SHARED_INFO	pSharedInfo;
	UINT32	ulResult = cOCT6100_ERR_OK;
	UINT32	ulTempVar;

	/* Obtain local pointer to shared portion of instance. */
	pSharedInfo = f_pApiInstance->pSharedInfo;
	
	/* Reserve an entry in the buffer list. */
	if ( f_fReserveListStruct == TRUE )
	{
		ulResult = Oct6100ApiReserveBufPlayoutListEntry( f_pApiInstance, f_pulBufferIndex );
	}
	else
	{
		*f_pulBufferIndex = f_ulBufIndex;
	}
	if ( ulResult == cOCT6100_ERR_OK )
	{
		/* Find a free block to store the buffer. */
		ulResult = Oct6100ApiReserveBufferPlayoutMemory( f_pApiInstance, f_pBufferLoad->ulBufferSize, f_pulBufferBase );
		if ( ulResult != cOCT6100_ERR_OK )
		{
			/* Release the list entry. */
			if ( f_fReserveListStruct == TRUE )
			{
				ulTempVar = Oct6100ApiReleaseBufPlayoutListEntry( f_pApiInstance, *f_pulBufferIndex );
				if ( ulTempVar != cOCT6100_ERR_OK )
					return ulTempVar;
			}
		}
	}

	return ulResult;
}


/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*\

Function:		Oct6100ApiWriteBufferInMemory

Description:    Writes the buffer in external memory.

-------------------------------------------------------------------------------
|	Argument		|	Description
-------------------------------------------------------------------------------
f_pApiInstance			Pointer to API instance. This memory is used to keep
						the present state of the chip and all its resources.

f_ulBufferBase			Allocated external memory address for the buffer.

f_ulBufferLength		Length in bytes of the buffer to be copied in memory.

f_pbyBuffer				Address where the buffer should be copied from.

\*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
UINT32 Oct6100ApiWriteBufferInMemory( 
				IN OUT	tPOCT6100_INSTANCE_API	f_pApiInstance,
				IN		UINT32					f_ulBufferBase,
				IN		UINT32					f_ulBufferLength,
				IN		PUINT8					f_pbyBuffer )
{
	tPOCT6100_SHARED_INFO		pSharedInfo;
	tOCT6100_WRITE_BURST_PARAMS	BurstParams;
	tOCT6100_WRITE_PARAMS		WriteParams;
	UINT32						ulResult;
	UINT32						ulNumWrites;
	PUINT16						pusSuperArray;
	PUINT8						pbyPlayoutBuffer;
	UINT32						ulByteCount = 0;
	UINT32						i;

	/* Get local pointer to shared portion of instance. */
	pSharedInfo = f_pApiInstance->pSharedInfo;

	/* Set the process context and user chip ID parameters once and for all. */
	BurstParams.pProcessContext = f_pApiInstance->pProcessContext;

	BurstParams.ulUserChipId = pSharedInfo->ChipConfig.ulUserChipId;

	WriteParams.pProcessContext = f_pApiInstance->pProcessContext;

	WriteParams.ulUserChipId = pSharedInfo->ChipConfig.ulUserChipId;

	/* Write the buffer in external memory. */
	ulNumWrites = f_ulBufferLength / 2;

	BurstParams.ulWriteAddress = f_ulBufferBase;
	BurstParams.pusWriteData = pSharedInfo->MiscVars.ausSuperArray;

	pusSuperArray = pSharedInfo->MiscVars.ausSuperArray;
	pbyPlayoutBuffer = f_pbyBuffer;
	
	/* Check if we can maximize the bandwidth through the CPU port. */
	if ( f_pApiInstance->pSharedInfo->ChipStats.usNumberChannels == 0 )
	{
		WriteParams.ulWriteAddress = 0x234;
		WriteParams.usWriteData = 0x08ff;
		mOCT6100_DRIVER_WRITE_API( WriteParams, ulResult );
		if ( ulResult != cOCT6100_ERR_OK  )
			return ulResult;
	}

	while ( ulNumWrites != 0 )
	{
		if ( ulNumWrites >= pSharedInfo->ChipConfig.usMaxRwAccesses )
			BurstParams.ulWriteLength = pSharedInfo->ChipConfig.usMaxRwAccesses;
		else
			BurstParams.ulWriteLength = ulNumWrites;

		for ( i = 0; i < BurstParams.ulWriteLength; i++ )
		{
			pusSuperArray[ i ]  = ( UINT16 )(( pbyPlayoutBuffer [ ulByteCount++ ]) << 8);
			pusSuperArray[ i ] |= ( UINT16 )pbyPlayoutBuffer [ ulByteCount++ ];
		}

		mOCT6100_DRIVER_WRITE_BURST_API( BurstParams, ulResult )
		if ( ulResult != cOCT6100_ERR_OK )
			return ulResult;

		BurstParams.ulWriteAddress += 2 * BurstParams.ulWriteLength;
		ulNumWrites -= BurstParams.ulWriteLength;

	}

	/* Make sure we revert back the changes made to the CPU bandwidth register. */
	if ( f_pApiInstance->pSharedInfo->ChipStats.usNumberChannels == 0 )
	{
		WriteParams.ulWriteAddress = 0x234;
		WriteParams.usWriteData = 0x0804;
		mOCT6100_DRIVER_WRITE_API( WriteParams, ulResult );
		if ( ulResult != cOCT6100_ERR_OK )
			return ulResult;
	}

	return cOCT6100_ERR_OK;
}
	

/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*\

Function:		Oct6100ApiUpdateBufferEntry

Description:    Updates the new buffer's entry in the buffer playout list.

-------------------------------------------------------------------------------
|	Argument		|	Description
-------------------------------------------------------------------------------
f_pApiInstance			Pointer to API instance. This memory is used to keep
						the present state of the chip and all its resources.

f_pBufferLoad			Pointer to buffer configuration structure.
f_ulBufferIndex			Allocated entry in buffer playout list.
f_ulBufferBase			Allocated external memory block for the buffer.

\*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
UINT32 Oct6100ApiUpdateBufferEntry(
				IN OUT	tPOCT6100_INSTANCE_API			f_pApiInstance,
				IN OUT	tPOCT6100_BUFFER_LOAD			f_pBufferLoad,
				IN		UINT32							f_ulBufferIndex,
				IN		UINT32							f_ulBufferBase )
{
	tPOCT6100_API_BUFFER	pBufEntry;
	UINT32					ulBufferSize = f_pBufferLoad->ulBufferSize;

	/* Obtain a pointer to the new buffer's list entry. */
	mOCT6100_GET_BUFFER_ENTRY_PNT( f_pApiInstance->pSharedInfo, pBufEntry, f_ulBufferIndex )

	/* Copy the buffer's configuration and allocated resources. */
	pBufEntry->ulBufferSize = f_pBufferLoad->ulBufferSize;
	pBufEntry->byBufferPcmLaw = (UINT8)( f_pBufferLoad->ulBufferPcmLaw & 0xFF );
	pBufEntry->ulBufferBase = f_ulBufferBase;
	
	/* Update the entries flags. */
	pBufEntry->usDependencyCnt = 0;

	/* Mark the buffer as opened. */
	pBufEntry->fReserved = TRUE;

	/* Increment the number of buffer loaded into the chip.*/
	f_pApiInstance->pSharedInfo->ChipStats.usNumberPlayoutBuffers++;
	
	/* Refresh the amount of memory used by buffer playout. */

	/* Reserved size is divisible by 64. */
	if ( ulBufferSize % 64 )
		ulBufferSize = ulBufferSize + ( 64 - ( ulBufferSize % 64 ) );
	f_pApiInstance->pSharedInfo->ChipStats.ulPlayoutMemUsed += ulBufferSize;
	
	/* Return the buffer index to the user. */
	*f_pBufferLoad->pulBufferIndex = f_ulBufferIndex;

	/* Return the amount of free memory left in the chip. */
	/* Note that this value does not give the "fragmentation" state of the available memory. */
	/* This value only gives the amount of free memory */
	if( f_pBufferLoad->pulPlayoutFreeMemSize )
		*f_pBufferLoad->pulPlayoutFreeMemSize = ( f_pApiInstance->pSharedInfo->MiscVars.ulTotalMemSize - ( f_pApiInstance->pSharedInfo->MemoryMap.ulFreeMemBaseAddress - cOCT6100_EXTERNAL_MEM_BASE_ADDRESS ) ) - ( f_pApiInstance->pSharedInfo->ChipStats.ulPlayoutMemUsed );

	return cOCT6100_ERR_OK;
}


/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*\

Function:		Oct6100BufferUnloadSer

Description:    Unloads a buffer from external memory.

-------------------------------------------------------------------------------
|	Argument		|	Description
-------------------------------------------------------------------------------
f_pApiInstance			Pointer to API instance. This memory is used to keep the
						present state of the chip and all its resources.

f_pBufferUnload			Pointer to buffer unload structure.
f_fReleaseListStruct	Whether to release the buffer playout list structure
						or not.

\*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
UINT32 Oct6100BufferUnloadSer(
				IN OUT	tPOCT6100_INSTANCE_API				f_pApiInstance,
				IN OUT	tPOCT6100_BUFFER_UNLOAD				f_pBufferUnload,
				IN		BOOL								f_fReleaseListStruct )
{
	UINT32	ulBufferIndex;
	UINT32	ulBufferBase;
	UINT32	ulResult;

	/* Verify that all the parameters given match the state of the API. */
	ulResult = Oct6100ApiAssertBufferParams( f_pApiInstance, f_pBufferUnload, &ulBufferIndex, &ulBufferBase );
	if ( ulResult != cOCT6100_ERR_OK )
		return ulResult;

	/* Release all resources associated to the unloaded buffer. */
	ulResult = Oct6100ApiReleaseBufferResources( f_pApiInstance, ulBufferIndex, ulBufferBase, f_fReleaseListStruct );
	if ( ulResult != cOCT6100_ERR_OK )
		return ulResult;

	return cOCT6100_ERR_OK;
}


/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*\

Function:		Oct6100ApiAssertBufferParams

Description:    Checks the buffer playout unload configuration for errors.

-------------------------------------------------------------------------------
|	Argument		|	Description
-------------------------------------------------------------------------------
f_pApiInstance			Pointer to API instance. This memory is used to keep the
						present state of the chip and all its resources.

f_pBufferUnload			Pointer to buffer unload structure.
f_pulBufferIndex		Pointer to the index of the buffer in the API's buffers list.
f_pulBufferBase			Pointer to the base address of the buffer in external memory.

\*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
UINT32 Oct6100ApiAssertBufferParams(
				IN OUT	tPOCT6100_INSTANCE_API				f_pApiInstance,
				IN		tPOCT6100_BUFFER_UNLOAD				f_pBufferUnload,
				OUT		PUINT32								f_pulBufferIndex,
				OUT		PUINT32								f_pulBufferBase )
{
	tPOCT6100_API_BUFFER	pBufEntry;

	*f_pulBufferIndex = f_pBufferUnload->ulBufferIndex;

	if ( *f_pulBufferIndex >= f_pApiInstance->pSharedInfo->ChipConfig.usMaxPlayoutBuffers )
		return cOCT6100_ERR_BUFFER_PLAYOUT_BUF_INDEX;

	mOCT6100_GET_BUFFER_ENTRY_PNT( f_pApiInstance->pSharedInfo, pBufEntry, *f_pulBufferIndex )

	/* Check for errors. */
	if ( pBufEntry->fReserved != TRUE )
		return cOCT6100_ERR_BUFFER_PLAYOUT_NOT_OPEN;
	if ( pBufEntry->usDependencyCnt != 0 )
		return cOCT6100_ERR_BUFFER_PLAYOUT_ACTIVE_DEPENDENCIES;
	
	/* Return all info needed to invalidate buffer. */
	*f_pulBufferBase = pBufEntry->ulBufferBase;

	return cOCT6100_ERR_OK;
}


/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*\

Function:		Oct6100ApiReleaseBufferResources

Description:    Release resources needed by the buffer.

-------------------------------------------------------------------------------
|	Argument		|	Description
-------------------------------------------------------------------------------
f_pApiInstance			Pointer to API instance. This memory is used to keep the
						present state of the chip and all its resources.
	
f_ulBufferIndex			Allocated entry in buffer playout list.
f_ulBufferBase			Allocated external memory block for the buffer.
f_fReleaseListStruct	Free the list structure.

\*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
UINT32 Oct6100ApiReleaseBufferResources(
				IN OUT	tPOCT6100_INSTANCE_API			f_pApiInstance,
				IN		UINT32							f_ulBufferIndex,
				IN		UINT32							f_ulBufferBase,
				IN		BOOL							f_fReleaseListStruct )
{
	tPOCT6100_SHARED_INFO	pSharedInfo;
	tPOCT6100_API_BUFFER	pBufEntry;
	UINT32	ulResult;
	UINT32	ulBufferSize;

	/* Obtain local pointer to shared portion of instance. */
	pSharedInfo = f_pApiInstance->pSharedInfo;

	/* Free the external memory reserved for the buffer. */
	ulResult = Oct6100ApiReleaseBufferPlayoutMemory( f_pApiInstance, f_ulBufferBase );
	if ( ulResult != cOCT6100_ERR_OK )
		return cOCT6100_ERR_FATAL_3E;

	/* Release the entry from the buffer list. */
	if ( f_fReleaseListStruct == TRUE )
		ulResult = Oct6100ApiReleaseBufPlayoutListEntry( f_pApiInstance, f_ulBufferIndex );
	if ( ulResult != cOCT6100_ERR_OK )
		return ulResult;

	mOCT6100_GET_BUFFER_ENTRY_PNT( f_pApiInstance->pSharedInfo, pBufEntry, f_ulBufferIndex );

	/* Save buffer size before releasing that entry, will be needed to calculate the amount of */
	/* free memory left for the user. */
	ulBufferSize = pBufEntry->ulBufferSize;
	
	/* Flag the buffer entry as free. */
	pBufEntry->fReserved = FALSE;

	/* Decrement the number of buffer loaded into the chip. */
	f_pApiInstance->pSharedInfo->ChipStats.usNumberPlayoutBuffers--;

	/* Refresh the amount of memory used by buffer playout. */
	/* Reserved size is divisible by 64. */
	if ( ulBufferSize % 64 )
		ulBufferSize = ulBufferSize + ( 64 - ( ulBufferSize % 64 ) );
	f_pApiInstance->pSharedInfo->ChipStats.ulPlayoutMemUsed -= ulBufferSize;

	return cOCT6100_ERR_OK;
}


/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*\

Function:		Oct6100BufferPlayoutAddSer

Description:    This function adds a buffer to a channel buffer list.

-------------------------------------------------------------------------------
|	Argument		|	Description
-------------------------------------------------------------------------------
f_pApiInstance			Pointer to API instance. This memory is used to keep the
						present state of the chip and all its resources.

f_pBufferPlayoutAdd		Pointer to buffer playout add structure.

\*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
UINT32 Oct6100BufferPlayoutAddSer(
				IN OUT	tPOCT6100_INSTANCE_API				f_pApiInstance,
				IN OUT	tPOCT6100_BUFFER_PLAYOUT_ADD		f_pBufferPlayoutAdd )
{
	UINT32	ulBufferIndex;
	UINT32	ulChannelIndex;
	UINT32	ulResult;

	/* Check the user's configuration of the buffer for errors. */
	ulResult = Oct6100ApiCheckPlayoutAddParams( f_pApiInstance, f_pBufferPlayoutAdd, &ulChannelIndex, &ulBufferIndex );
	if ( ulResult != cOCT6100_ERR_OK )
		return ulResult;

	/* Write to  all resources needed to activate buffer playout. */
	ulResult = Oct6100ApiWriteBufferAddStructs( f_pApiInstance, f_pBufferPlayoutAdd, ulChannelIndex, ulBufferIndex );
	if ( ulResult != cOCT6100_ERR_OK )
		return ulResult;

	return cOCT6100_ERR_OK;
}


/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*\

Function:		Oct6100ApiCheckPlayoutAddParams

Description:	Check the validity of the channel and buffer requested.

-------------------------------------------------------------------------------
|	Argument		|	Description
-------------------------------------------------------------------------------
f_pApiInstance			Pointer to API instance. This memory is used to keep the
						present state of the chip and all its resources.

f_pBufferPlayoutAdd		Pointer to buffer playout add structure.  
f_pulChannelIndex		Pointer to the channel index of the selected channel.
f_pulBufferIndex		Pointer to the buffer index within the API's buffer list.

\*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
UINT32 Oct6100ApiCheckPlayoutAddParams(
				IN OUT	tPOCT6100_INSTANCE_API				f_pApiInstance,
				IN		tPOCT6100_BUFFER_PLAYOUT_ADD		f_pBufferPlayoutAdd,
				OUT		PUINT32								f_pulChannelIndex, 
				OUT		PUINT32								f_pulBufferIndex )
{
	tPOCT6100_API_BUFFER			pBufferEntry;
	tPOCT6100_API_CHANNEL			pEchoChannel;
	UINT32	ulEntryOpenCnt;

	/* Check for errors. */
	if ( f_pApiInstance->pSharedInfo->ChipConfig.usMaxPlayoutBuffers == 0 )
		return cOCT6100_ERR_BUFFER_PLAYOUT_DISABLED;
	
	if ( f_pBufferPlayoutAdd->ulChannelHndl == cOCT6100_INVALID_HANDLE )
		return cOCT6100_ERR_BUFFER_PLAYOUT_CHANNEL_HANDLE_INVALID;

	if ( f_pBufferPlayoutAdd->ulPlayoutPort != cOCT6100_CHANNEL_PORT_ROUT && 
		 f_pBufferPlayoutAdd->ulPlayoutPort != cOCT6100_CHANNEL_PORT_SOUT )
		return cOCT6100_ERR_BUFFER_PLAYOUT_PLAYOUT_PORT;

	if ( f_pBufferPlayoutAdd->fRepeat != TRUE && f_pBufferPlayoutAdd->fRepeat != FALSE )
		return cOCT6100_ERR_BUFFER_PLAYOUT_ADD_REPEAT;

	if ( f_pBufferPlayoutAdd->fRepeat == TRUE )
	{
		if ( f_pBufferPlayoutAdd->ulRepeatCount != cOCT6100_REPEAT_INFINITELY )
		{
			if ( f_pBufferPlayoutAdd->ulRepeatCount == 0x0 
				|| f_pBufferPlayoutAdd->ulRepeatCount > cOCT6100_REPEAT_MAX)
			{
				return cOCT6100_ERR_BUFFER_PLAYOUT_ADD_REPEAT_COUNT;
			}
		}
	}

	if ( f_pBufferPlayoutAdd->ulMixingMode != cOCT6100_MIXING_0_DB &&
		 f_pBufferPlayoutAdd->ulMixingMode != cOCT6100_MIXING_MINUS_6_DB &&
		 f_pBufferPlayoutAdd->ulMixingMode != cOCT6100_MIXING_MINUS_12_DB &&
		 f_pBufferPlayoutAdd->ulMixingMode != cOCT6100_MIXING_MUTE )
		return cOCT6100_ERR_BUFFER_PLAYOUT_ADD_MIXING;

	if ( ( f_pBufferPlayoutAdd->lGainDb < -24 )
		|| ( f_pBufferPlayoutAdd->lGainDb > 24 ) )
		return cOCT6100_ERR_BUFFER_PLAYOUT_ADD_GAIN_DB;

	/*=====================================================================*/
	/* Check the channel handle. */

	if ( (f_pBufferPlayoutAdd->ulChannelHndl & cOCT6100_HNDL_TAG_MASK) != cOCT6100_HNDL_TAG_CHANNEL )
		return cOCT6100_ERR_BUFFER_PLAYOUT_CHANNEL_HANDLE_INVALID;

	*f_pulChannelIndex = f_pBufferPlayoutAdd->ulChannelHndl & cOCT6100_HNDL_INDEX_MASK;
	if ( *f_pulChannelIndex >= f_pApiInstance->pSharedInfo->ChipConfig.usMaxChannels )
		return cOCT6100_ERR_BUFFER_PLAYOUT_CHANNEL_HANDLE_INVALID;

	mOCT6100_GET_CHANNEL_ENTRY_PNT( f_pApiInstance->pSharedInfo, pEchoChannel, *f_pulChannelIndex )

	/* Extract the entry open count from the provided handle. */
	ulEntryOpenCnt = (f_pBufferPlayoutAdd->ulChannelHndl >> cOCT6100_ENTRY_OPEN_CNT_SHIFT) & cOCT6100_ENTRY_OPEN_CNT_MASK;

	/* Check for errors. */
	if ( pEchoChannel->fReserved != TRUE )
		return cOCT6100_ERR_BUFFER_PLAYOUT_CHANNEL_NOT_OPEN;
	if ( ulEntryOpenCnt != pEchoChannel->byEntryOpenCnt )
		return cOCT6100_ERR_BUFFER_PLAYOUT_CHANNEL_HANDLE_INVALID;

	/* Check if repeat flag has been used for this port. */
	if ( ( ( f_pBufferPlayoutAdd->ulPlayoutPort == cOCT6100_CHANNEL_PORT_ROUT ) && ( pEchoChannel->fRinBufPlayoutRepeatUsed == TRUE ) )
		|| ( ( f_pBufferPlayoutAdd->ulPlayoutPort == cOCT6100_CHANNEL_PORT_SOUT ) && ( pEchoChannel->fSoutBufPlayoutRepeatUsed == TRUE ) ) )
		return cOCT6100_ERR_BUFFER_PLAYOUT_REPEAT_USED;

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

	/*=====================================================================*/
	/* Check the buffer information. */

	*f_pulBufferIndex = f_pBufferPlayoutAdd->ulBufferIndex;
	if ( *f_pulBufferIndex >= f_pApiInstance->pSharedInfo->ChipConfig.usMaxPlayoutBuffers )
		return cOCT6100_ERR_BUFFER_PLAYOUT_BUF_INDEX;

	mOCT6100_GET_BUFFER_ENTRY_PNT( f_pApiInstance->pSharedInfo, pBufferEntry, *f_pulBufferIndex )

	if ( pBufferEntry->fReserved != TRUE )
		return cOCT6100_ERR_BUFFER_PLAYOUT_NOT_OPEN;

	/* Check if the play length is not larger then the currently uploaded buffer. */
	if ( ( f_pBufferPlayoutAdd->ulBufferLength > pBufferEntry->ulBufferSize ) &&
		  ( f_pBufferPlayoutAdd->ulBufferLength != cOCT6100_AUTO_SELECT ) )
		return cOCT6100_ERR_BUFFER_PLAYOUT_BUF_SIZE;

	if( f_pBufferPlayoutAdd->ulBufferLength != cOCT6100_AUTO_SELECT )
	{
		if ( f_pBufferPlayoutAdd->ulBufferLength < cOCT6100_MINIMUM_BUFFER_SIZE )
			return cOCT6100_ERR_BUFFER_PLAYOUT_TOO_SMALL;

		if ( ( f_pBufferPlayoutAdd->ulBufferLength % cOCT6100_BUFFER_SIZE_GRANULARITY ) != 0 )
			return cOCT6100_ERR_BUFFER_PLAYOUT_BUF_SIZE;
	}

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

	return cOCT6100_ERR_OK;
}


/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*\

Function:		Oct6100ApiWriteBufferAddStructs

Description:    Write the buffer playout event in the channel's port playout
				circular buffer.

-------------------------------------------------------------------------------
|	Argument		|	Description
-------------------------------------------------------------------------------
f_pApiInstance			Pointer to API instance. This memory is used to keep the
						present state of the chip and all its resources.
	
f_pBufferPlayoutAdd		Pointer to buffer playout add structure.  
f_ulChannelIndex		Index of the channel on which the buffer is to be added.
f_ulBufferIndex			Index of the buffer structure within the API's buffer list.

\*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
UINT32 Oct6100ApiWriteBufferAddStructs(
				IN OUT	tPOCT6100_INSTANCE_API				f_pApiInstance,
				IN		tPOCT6100_BUFFER_PLAYOUT_ADD		f_pBufferPlayoutAdd,
				IN		UINT32								f_ulChannelIndex, 
				IN		UINT32								f_ulBufferIndex )
{
	tPOCT6100_API_BUFFER		pBufferEntry;
	tPOCT6100_API_CHANNEL		pEchoChannel;
	tPOCT6100_SHARED_INFO		pSharedInfo;
	tOCT6100_READ_PARAMS		ReadParams;
	UINT32	ulResult;
	UINT32	ulTempData;
	UINT32	ulEventBuffer;

	UINT32	ulReadPtrBytesOfst;
	UINT32	ulReadPtrBitOfst;
	UINT32	ulReadPtrFieldSize;

	UINT32	ulWritePtrBytesOfst;
	UINT32	ulWritePtrBitOfst;
	UINT32	ulWritePtrFieldSize;

	UINT32	ulWritePtr;
	UINT32	ulReadPtr;

	UINT32	ulPlayoutBaseAddress;
	UINT32	ulAddress;
	UINT32	ulEventIndex;
	UINT32	ulMask;

	UINT32	ulRepeatCount = 0;
	BOOL	fRepeatCountSet = FALSE;
	UINT32	ulDurationModulo = 0;
	UINT32	ulEventsToCreate = 1;
	UINT32	ulBufferDurationMs;
	
	UINT32	ulBufferLength;
	UINT16	usTempData = 0;

	UINT16	usReadData;
	UINT32	ulChipWritePtr;
	UINT32	ulReadData;
	UINT32	ulLoopCnt = 0;
	BOOL	fStillPlaying = TRUE;
	BOOL	fCheckHardStop = FALSE;
	BOOL	fOldBufferPlayoutVersion = FALSE;

	UINT32			aulWaitTime[ 2 ];

	/* Obtain local pointer to shared portion of instance. */
	pSharedInfo = f_pApiInstance->pSharedInfo;

	mOCT6100_GET_CHANNEL_ENTRY_PNT( pSharedInfo, pEchoChannel, f_ulChannelIndex );
	mOCT6100_GET_BUFFER_ENTRY_PNT( f_pApiInstance->pSharedInfo, pBufferEntry, f_ulBufferIndex );

	/* Select the buffer of interest. */
	if ( f_pBufferPlayoutAdd->ulPlayoutPort == cOCT6100_CHANNEL_PORT_ROUT )
	{
		ulEventBuffer = pSharedInfo->MemoryMap.ulChanMainRinPlayoutMemOfst;
		ulWritePtr = pEchoChannel->ulRinBufWritePtr;

		ulWritePtrBytesOfst = pSharedInfo->MemoryMap.PlayoutRinWritePtrOfst.usDwordOffset * 4;
		ulWritePtrBitOfst = pSharedInfo->MemoryMap.PlayoutRinWritePtrOfst.byBitOffset;
		ulWritePtrFieldSize = pSharedInfo->MemoryMap.PlayoutRinWritePtrOfst.byFieldSize;

		ulReadPtrBytesOfst = pSharedInfo->MemoryMap.PlayoutRinReadPtrOfst.usDwordOffset * 4;
		ulReadPtrBitOfst = pSharedInfo->MemoryMap.PlayoutRinReadPtrOfst.byBitOffset;
		ulReadPtrFieldSize = pSharedInfo->MemoryMap.PlayoutRinReadPtrOfst.byFieldSize;
	}
	else /* f_pBufferPlayoutAdd->ulPlayoutPort == cOCT6100_CHANNEL_PORT_SOUT */
	{
		ulEventBuffer = pSharedInfo->MemoryMap.ulChanMainSoutPlayoutMemOfst;
		ulWritePtr = pEchoChannel->ulSoutBufWritePtr;

		ulWritePtrBytesOfst = pSharedInfo->MemoryMap.PlayoutSoutWritePtrOfst.usDwordOffset * 4;
		ulWritePtrBitOfst = pSharedInfo->MemoryMap.PlayoutSoutWritePtrOfst.byBitOffset;
		ulWritePtrFieldSize = pSharedInfo->MemoryMap.PlayoutSoutWritePtrOfst.byFieldSize;

		ulReadPtrBytesOfst = pSharedInfo->MemoryMap.PlayoutSoutReadPtrOfst.usDwordOffset * 4;
		ulReadPtrBitOfst = pSharedInfo->MemoryMap.PlayoutSoutReadPtrOfst.byBitOffset;
		ulReadPtrFieldSize = pSharedInfo->MemoryMap.PlayoutSoutReadPtrOfst.byFieldSize;
	}

	/*=======================================================================*/
	/* Calculate the repeat count. */

	/* The buffer length is either the total buffer size or the value specified by the user */
	if ( f_pBufferPlayoutAdd->ulBufferLength == cOCT6100_AUTO_SELECT )
	{
		ulBufferLength = pBufferEntry->ulBufferSize;
	}
	else
	{
		ulBufferLength = f_pBufferPlayoutAdd->ulBufferLength;
	}

	if ( f_pBufferPlayoutAdd->ulDuration != cOCT6100_INVALID_VALUE )
	{
		/* With duration and buffer length, we can find the number of times we must repeat playing this buffer. */
		ulBufferDurationMs = ulBufferLength / cOCT6100_SAMPLES_PER_MS;
		ulRepeatCount = f_pBufferPlayoutAdd->ulDuration / ulBufferDurationMs;
		fRepeatCountSet = TRUE;

		/* Check if buffer is larger then asked duration. */
		if ( ulRepeatCount != 0x0 )
		{
			/* We might have to create more then 1 event to accomodate for the repeat-max limit. */
			ulEventsToCreate = ( ulRepeatCount / cOCT6100_REPEAT_MAX ) + 1;
		}
		else
		{
			/* No repeat event.  Maybe only the duration modulo! */
			ulEventsToCreate = 0x0;
		}

		/* Check if must create a second event for a buffer that cannot be played completely. */
		ulDurationModulo = f_pBufferPlayoutAdd->ulDuration % ulBufferDurationMs;
		if ( ulDurationModulo != 0x0 )
		{
			ulDurationModulo *= cOCT6100_SAMPLES_PER_MS;
			if ( ulDurationModulo / cOCT6100_BUFFER_SIZE_GRANULARITY )
			{
				/* Round the modulo to be on a buffer size granularity. */
				/* This will round down. */
				ulDurationModulo = ( ulDurationModulo / cOCT6100_BUFFER_SIZE_GRANULARITY ) * cOCT6100_BUFFER_SIZE_GRANULARITY;

				/* If the event about to be created is smaller then the minimum buffer size, */
				/* round up to the minimum required by the hardware. */
				if ( ulDurationModulo < cOCT6100_MINIMUM_BUFFER_SIZE )
					ulDurationModulo = cOCT6100_MINIMUM_BUFFER_SIZE;
				ulEventsToCreate++;
			}
			else
			{
				/* The modulo is too small to be played.  Skip. */
				ulDurationModulo = 0;
			}
		}
	}
	else if ( f_pBufferPlayoutAdd->fRepeat == TRUE 
		&& f_pBufferPlayoutAdd->ulRepeatCount != cOCT6100_REPEAT_INFINITELY )
	{
		/* The repeat count is set directly from the user. */
		ulRepeatCount = f_pBufferPlayoutAdd->ulRepeatCount;
		fRepeatCountSet = TRUE;
	}
	
	/*=======================================================================*/

	/* Set the playout feature base address. */
	ulPlayoutBaseAddress = cOCT6100_CHANNEL_ROOT_BASE + ( f_ulChannelIndex * cOCT6100_CHANNEL_ROOT_SIZE ) + pSharedInfo->MemoryMap.ulChanRootConfOfst;

	/* Read the read pointer. */
	ulAddress = ulPlayoutBaseAddress + ulReadPtrBytesOfst;

	/* Must read in memory directly since this value is changed by hardware. */
	ulResult = Oct6100ApiReadDword( f_pApiInstance, ulAddress, &ulTempData );
	if ( ulResult != cOCT6100_ERR_OK )
		return ulResult;
	
	mOCT6100_CREATE_FEATURE_MASK( ulReadPtrFieldSize, ulReadPtrBitOfst, &ulMask );
	
	/* Store the read pointer. */
	ulReadPtr = ( ulTempData & ulMask ) >> ulReadPtrBitOfst;

	/* Compare the pointers...  Are they different?  If so, there is something already in the list.  */
	if ( ulReadPtr != ulWritePtr )
	{
		/* Check if there is enough room for the playout events. */
		if ( ( pSharedInfo->ImageInfo.fRinBufferPlayoutHardSkip == TRUE )
			&& ( pSharedInfo->ImageInfo.fSoutBufferPlayoutHardSkip == TRUE ) )
		{
			/* 127 or 31 events image. */
			if ( (UINT8)( ( ulWritePtr - ulReadPtr ) & ( pSharedInfo->ImageInfo.byMaxNumberPlayoutEvents - 1 ) ) >= ( pSharedInfo->ImageInfo.byMaxNumberPlayoutEvents - (UINT8)ulEventsToCreate ) )
				fCheckHardStop = TRUE;
		}
		else
		{
			/* Old 31 events image. */
			if ( ( ( ulWritePtr - ulReadPtr ) & 0x1F ) >= ( 0x20 - ulEventsToCreate ) )
				fCheckHardStop = TRUE;

			fOldBufferPlayoutVersion = TRUE;
		}

		if ( fCheckHardStop == TRUE )
		{
			/* Ok.  From what was read, the list is full.  But we might still have a chance if the hard-stop */
			/* version was used.  In this case, some of the buffers in the list might */
			/* become free in a couple of milliseconds, so try to wait for this. */
			
			if ( ( ( f_pBufferPlayoutAdd->ulPlayoutPort == cOCT6100_CHANNEL_PORT_ROUT ) && ( pEchoChannel->fRinHardStop == TRUE ) )
				|| ( ( f_pBufferPlayoutAdd->ulPlayoutPort == cOCT6100_CHANNEL_PORT_SOUT ) && ( pEchoChannel->fSoutHardStop == TRUE ) ) )
			{
				/* Read the 'chip' write pointer in the hardware. */
				ReadParams.pProcessContext = f_pApiInstance->pProcessContext;

				ReadParams.ulUserChipId = pSharedInfo->ChipConfig.ulUserChipId;
				ReadParams.pusReadData = &usReadData;
				ReadParams.ulReadAddress = ulPlayoutBaseAddress + ulReadPtrBytesOfst;

				/* Get the write pointer in the chip. */
				ulAddress = ulPlayoutBaseAddress + ulWritePtrBytesOfst;

				mOCT6100_RETRIEVE_NLP_CONF_DWORD( f_pApiInstance, pEchoChannel, ulAddress, &ulReadData, ulResult );
				if ( ulResult != cOCT6100_ERR_OK )
					return ulResult;

				mOCT6100_CREATE_FEATURE_MASK( ulWritePtrFieldSize, ulWritePtrBitOfst, &ulMask );

				/* Store the write pointer. */
				ulChipWritePtr = ( ulReadData & ulMask ) >> ulWritePtrBitOfst;

				/* Optimize this access by only reading the word we are interested in. */
				if ( ulReadPtrBitOfst < 16 )
					ReadParams.ulReadAddress += 2;

				while( fStillPlaying == TRUE )
				{				
					/* Read the read pointer until equals to the write pointer. */
					mOCT6100_DRIVER_READ_API( ReadParams, ulResult )
					if ( ulResult != cOCT6100_ERR_OK )
						return ulResult;

					/* Move data at correct position according to what was read. */
					if ( ulReadPtrBitOfst < 16 )
						ulTempData = usReadData;
					else
						ulTempData = usReadData << 16;
					
					mOCT6100_CREATE_FEATURE_MASK( ulReadPtrFieldSize, ulReadPtrBitOfst, &ulMask );
					
					/* Store the read pointer.*/
					ulReadPtr = ( ulTempData & ulMask ) >> ulReadPtrBitOfst;

					/* Playout has finished when the read pointer reaches the write pointer. */
					if ( ulReadPtr == ulChipWritePtr )
						break;

					ulLoopCnt++;
					if ( ulLoopCnt > cOCT6100_MAX_LOOP )
					{
						return cOCT6100_ERR_FATAL_E7;
					}

					aulWaitTime[ 0 ] = 100;
					aulWaitTime[ 1 ] = 0;
					ulResult = Oct6100ApiWaitForTime( f_pApiInstance, aulWaitTime );
					if ( ulResult != cOCT6100_ERR_OK )
						return ulResult;			
				}

				/* Clear hard-stop flag. */
				if ( f_pBufferPlayoutAdd->ulPlayoutPort == cOCT6100_CHANNEL_PORT_ROUT )
				{
					/* No hard stop for now. */
					pEchoChannel->fRinHardStop = FALSE;
				}
				else /* if ( f_pBufferPlayoutAdd->ulPlayoutPort == cOCT6100_CHANNEL_PORT_SOUT ) */
				{
					/* No hard stop for now. */
					pEchoChannel->fSoutHardStop = FALSE;
				}

				/* Now check again if the event can be added... */
				if ( fOldBufferPlayoutVersion == FALSE )
				{
					if ( (UINT8)( ( ulWritePtr - ulReadPtr ) & ( pSharedInfo->ImageInfo.byMaxNumberPlayoutEvents - 1 ) ) >= ( pSharedInfo->ImageInfo.byMaxNumberPlayoutEvents - (UINT8)ulEventsToCreate ) )
						return cOCT6100_ERR_BUFFER_PLAYOUT_ADD_EVENT_BUF_FULL;
				}
				else /* if ( fOldBufferPlayoutVersion == TRUE ) */
				{
					/* Old 31 events image. */
					if ( ( ( ulWritePtr - ulReadPtr ) & 0x1F ) >= ( 0x20 - ulEventsToCreate ) )
						return cOCT6100_ERR_BUFFER_PLAYOUT_ADD_EVENT_BUF_FULL;
				}

				/* Good, at least another buffer can be added!  Add the buffer to the list. */
			}
			else
			{
				/* Well the list is full! */
				return cOCT6100_ERR_BUFFER_PLAYOUT_ADD_EVENT_BUF_FULL;
			}
		}
	}

	/*=======================================================================*/
	/* Write the events. */

	for ( ulEventIndex = 0; ulEventIndex < ulEventsToCreate; ulEventIndex ++  )
	{
		/* Set the playout event base address. */
		if ( ( pSharedInfo->ImageInfo.fRinBufferPlayoutHardSkip == TRUE )
			&& ( pSharedInfo->ImageInfo.fSoutBufferPlayoutHardSkip == TRUE ) )
		{
			/* 127 or 31 events image. */
			ulAddress = pSharedInfo->MemoryMap.ulChanMainMemBase + (f_ulChannelIndex * pSharedInfo->MemoryMap.ulChanMainMemSize ) + ulEventBuffer + (cOCT6100_PLAYOUT_EVENT_MEM_SIZE * (ulWritePtr & ( pSharedInfo->ImageInfo.byMaxNumberPlayoutEvents - 1 )));
		}
		else
		{
			/* Old 31 events image. */
			ulAddress = pSharedInfo->MemoryMap.ulChanMainMemBase + (f_ulChannelIndex * pSharedInfo->MemoryMap.ulChanMainMemSize ) + ulEventBuffer + (cOCT6100_PLAYOUT_EVENT_MEM_SIZE * (ulWritePtr & 0x1F));
		}
	
		/* EVENT BASE + 0 */
		/* Make sure the xIS and xHS bits are cleared. */
		ulTempData = 0;

		/* Set the repeat count. */
		if ( fRepeatCountSet == TRUE )
		{
			if ( ( ulRepeatCount != 0x0 ) && ( ulRepeatCount <= cOCT6100_REPEAT_MAX ) )
			{
				/* Use repeat count directly. */
				ulTempData |= ulRepeatCount;

				/* Will be used later when creating the duration modulo event. */
				ulRepeatCount = 0;
			}
			else if ( ulRepeatCount != 0x0 )
			{
				/* Get ready for next event. */
				ulRepeatCount -= cOCT6100_REPEAT_MAX;

				/* Set maximum for this event. */
				ulTempData |= cOCT6100_REPEAT_MAX;
			}
			else
			{
				/* Duration modulo case.  Nothing to set here. */
			}
		}
		else /* if ( fRepeatCountSet != TRUE ) */
		{
			/* Repeat only once. */
			ulTempData |= 0x1;
		}

		ulResult = Oct6100ApiWriteDword( f_pApiInstance, ulAddress, ulTempData );
		if ( ulResult != cOCT6100_ERR_OK )
			return ulResult;

		/* EVENT BASE + 4 */
		/* Set the buffer base address and playout configuration. */
		ulAddress += 4;
		ulTempData = pBufferEntry->ulBufferBase & 0x07FFFFFF;

		/* Set play indefinitely or loop N times. */
		if ( ( fRepeatCountSet == FALSE ) && ( f_pBufferPlayoutAdd->fRepeat == TRUE ) )
		{
			/* Repeat indefinitely. */
			ulTempData |= 0x1 << cOCT6100_PLAYOUT_EVENT_REPEAT_OFFSET;

			if ( f_pBufferPlayoutAdd->ulPlayoutPort == cOCT6100_CHANNEL_PORT_ROUT )
				pEchoChannel->fRinBufPlayoutRepeatUsed = TRUE;
			else /* if ( f_pBufferPlayoutAdd->ulPlayoutPort == cOCT6100_CHANNEL_PORT_SOUT ) */
				pEchoChannel->fSoutBufPlayoutRepeatUsed = TRUE;
		}
		
		/* Use loop N times feature. */
		ulTempData |= 0x1 << cOCT6100_PLAYOUT_EVENT_LOOP_TIMES_OFFSET;

		/* Set the law.*/
		ulTempData |= ( pBufferEntry->byBufferPcmLaw << cOCT6100_PLAYOUT_EVENT_LAW_OFFSET );

		/* Set the mixing configuration.*/
		ulTempData |= f_pBufferPlayoutAdd->ulMixingMode << cOCT6100_PLAYOUT_EVENT_MIX_OFFSET;

		ulResult = Oct6100ApiWriteDword( f_pApiInstance, ulAddress, ulTempData );
		if ( ulResult != cOCT6100_ERR_OK )
			return ulResult;

		
		/* EVENT BASE + 8 */
		/* Set the buffer size and playout gain. */
		ulAddress += 4;

		/* Check if we are setting the duration modulo.  This would be the last event and this */
		/* event is of a very specific size. */
		if ( ( fRepeatCountSet == TRUE ) 
			&& ( ulEventIndex == ( ulEventsToCreate - 1 ) ) 
			&& ( ulDurationModulo != 0x0 ) )
		{
			/* The duration modulo variable contains all that is needed here. */
			ulBufferLength = ulDurationModulo;
		}
		ulTempData = ulBufferLength;

		/* Adjust playout gain. */
		if ( f_pBufferPlayoutAdd->lGainDb != 0 )
		{
			/* Convert the dB value into OctFloat format. */
			usTempData = Oct6100ApiDbAmpHalfToOctFloat( f_pBufferPlayoutAdd->lGainDb );
			ulTempData |= ( usTempData & 0xFF00 ) << 16;
		}
		else
		{
			ulTempData |= cOCT6100_PLAYOUT_GAIN;
		}

		ulResult = Oct6100ApiWriteDword( f_pApiInstance, ulAddress, ulTempData );
		if ( ulResult != cOCT6100_ERR_OK )
			return ulResult;
			
		/* EVENT BASE + 0xC */
		ulAddress += 4;
		ulTempData = ( ulBufferLength - 1 ) & 0xFFFFFFC0;	/* Must be multiple of 64 bytes */

		/* Adjust playout gain. */
		if ( f_pBufferPlayoutAdd->lGainDb != 0 )
		{
			ulTempData |= ( usTempData & 0xFF ) << 24;
		}

		ulResult = Oct6100ApiWriteDword( f_pApiInstance, ulAddress, ulTempData );
		if ( ulResult != cOCT6100_ERR_OK )
			return ulResult;

		/* Next event. */
		ulWritePtr++;
	}

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


	/*=======================================================================*/
	/* Increment the write pointer to make it point to the next empty entry. */

	if ( f_pBufferPlayoutAdd->ulPlayoutPort == cOCT6100_CHANNEL_PORT_ROUT )
	{
		pEchoChannel->ulRinBufWritePtr = ( pEchoChannel->ulRinBufWritePtr + ulEventsToCreate ) & ( pSharedInfo->ImageInfo.byMaxNumberPlayoutEvents - 1 );
		/* Remember that a buffer was added on the rin port. */
		pEchoChannel->fRinBufAdded = TRUE;
	}
	else /* f_pBufferPlayoutAdd->ulPlayoutPort == cOCT6100_CHANNEL_PORT_SOUT */
	{
		pEchoChannel->ulSoutBufWritePtr = ( pEchoChannel->ulSoutBufWritePtr + ulEventsToCreate ) & ( pSharedInfo->ImageInfo.byMaxNumberPlayoutEvents - 1 );
		/* Remember that a buffer was added on the sout port. */
		pEchoChannel->fSoutBufAdded = TRUE;
	}

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

	return cOCT6100_ERR_OK;
}


/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*\

Function:		Oct6100BufferPlayoutStartSer

Description:    Starts buffer playout on a channel.

-------------------------------------------------------------------------------
|	Argument		|	Description
-------------------------------------------------------------------------------
f_pApiInstance				Pointer to API instance. This memory is used to keep the
							present state of the chip and all its resources.

f_pBufferPlayoutStart		Pointer to buffer playout start structure.

f_ulPlayoutStopEventType	Playout stop event type to be generated if required.

\*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
UINT32 Oct6100BufferPlayoutStartSer(
				IN OUT	tPOCT6100_INSTANCE_API				f_pApiInstance,
				IN OUT	tPOCT6100_BUFFER_PLAYOUT_START		f_pBufferPlayoutStart,
				IN		UINT32								f_ulPlayoutStopEventType )
{
	UINT32	ulBufferIndex;
	UINT32	ulChannelIndex;
	BOOL	fNotifyOnPlayoutStop;
	UINT32	ulUserEventId;
	BOOL	fAddToCurrentlyPlayingList;
	UINT32	ulResult;

	/* Check the user's configuration of the buffer for errors. */
	ulResult = Oct6100ApiCheckPlayoutStartParams( f_pApiInstance, f_pBufferPlayoutStart, &ulChannelIndex, &ulBufferIndex, &fNotifyOnPlayoutStop, &ulUserEventId, &fAddToCurrentlyPlayingList );
	if ( ulResult != cOCT6100_ERR_OK )
		return ulResult;

	/* Write to all resources needed to activate buffer playout. */
	ulResult = Oct6100ApiWriteChanPlayoutStructs( f_pApiInstance, f_pBufferPlayoutStart, ulChannelIndex, ulBufferIndex, fNotifyOnPlayoutStop, ulUserEventId, fAddToCurrentlyPlayingList, f_ulPlayoutStopEventType );
	if ( ulResult != cOCT6100_ERR_OK )
		return ulResult;

	return cOCT6100_ERR_OK;
}


/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*\

Function:		Oct6100ApiCheckPlayoutStartParams

Description:	Check the validity of the channel and buffer requested.
				Check the validity of the flags requested.

-------------------------------------------------------------------------------
|	Argument		|	Description
-------------------------------------------------------------------------------
f_pApiInstance			Pointer to API instance. This memory is used to keep the
						present state of the chip and all its resources.

f_pBufferPlayoutStart	Pointer to buffer playout start structure.  
f_pulChannelIndex		Pointer to the channel index of the selected channel.
f_pulBufferIndex		Pointer to the buffer index within the API's buffer list.
f_pfNotifyOnPlayoutStop	Pointer to the notify on playout stop flag.
f_pulUserEventId		Pointer to the user event id specified.
f_pfAllowStartIfActive	Pointer to the add to currently playing list flag.

\*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
UINT32 Oct6100ApiCheckPlayoutStartParams(
				IN OUT	tPOCT6100_INSTANCE_API				f_pApiInstance,
				IN OUT	tPOCT6100_BUFFER_PLAYOUT_START		f_pBufferPlayoutStart,
				OUT		PUINT32								f_pulChannelIndex, 
				OUT		PUINT32								f_pulBufferIndex,
				OUT		PBOOL								f_pfNotifyOnPlayoutStop,
				OUT		PUINT32								f_pulUserEventId,
				OUT		PBOOL								f_pfAllowStartIfActive )
{
	tPOCT6100_API_CHANNEL	pEchoChannel;
	UINT32					ulEntryOpenCnt;

	/* Check for errors. */
	if ( f_pApiInstance->pSharedInfo->ChipConfig.usMaxPlayoutBuffers == 0 )
		return cOCT6100_ERR_BUFFER_PLAYOUT_DISABLED;
	
	if ( f_pBufferPlayoutStart->ulChannelHndl == cOCT6100_INVALID_HANDLE )
		return cOCT6100_ERR_BUFFER_PLAYOUT_CHANNEL_HANDLE_INVALID;

	if ( f_pBufferPlayoutStart->ulPlayoutPort != cOCT6100_CHANNEL_PORT_ROUT && 
		 f_pBufferPlayoutStart->ulPlayoutPort != cOCT6100_CHANNEL_PORT_SOUT )
		return cOCT6100_ERR_BUFFER_PLAYOUT_PLAYOUT_PORT;

	if ( f_pBufferPlayoutStart->fNotifyOnPlayoutStop != FALSE
		&& f_pBufferPlayoutStart->fNotifyOnPlayoutStop != TRUE )
		return cOCT6100_ERR_BUFFER_PLAYOUT_NOTIFY_ON_STOP;

	if ( f_pBufferPlayoutStart->fAllowStartWhileActive != FALSE
		&& f_pBufferPlayoutStart->fAllowStartWhileActive != TRUE )
		return cOCT6100_ERR_BUFFER_PLAYOUT_ALLOW_ACTIVE;

	/*=====================================================================*/
	/* Check the channel handle. */

	if ( (f_pBufferPlayoutStart->ulChannelHndl & cOCT6100_HNDL_TAG_MASK) != cOCT6100_HNDL_TAG_CHANNEL )
		return cOCT6100_ERR_BUFFER_PLAYOUT_CHANNEL_HANDLE_INVALID;

	*f_pulChannelIndex = f_pBufferPlayoutStart->ulChannelHndl & cOCT6100_HNDL_INDEX_MASK;
	if ( *f_pulChannelIndex >= f_pApiInstance->pSharedInfo->ChipConfig.usMaxChannels )
		return cOCT6100_ERR_BUFFER_PLAYOUT_CHANNEL_HANDLE_INVALID;

	mOCT6100_GET_CHANNEL_ENTRY_PNT( f_pApiInstance->pSharedInfo, pEchoChannel, *f_pulChannelIndex )

	/* Extract the entry open count from the provided handle. */
	ulEntryOpenCnt = (f_pBufferPlayoutStart->ulChannelHndl >> cOCT6100_ENTRY_OPEN_CNT_SHIFT) & cOCT6100_ENTRY_OPEN_CNT_MASK;

	/* Check for errors. */
	if ( pEchoChannel->fReserved != TRUE )
		return cOCT6100_ERR_BUFFER_PLAYOUT_CHANNEL_NOT_OPEN;
	if ( ulEntryOpenCnt != pEchoChannel->byEntryOpenCnt )
		return cOCT6100_ERR_BUFFER_PLAYOUT_CHANNEL_HANDLE_INVALID;

	/* The channel cannot be in POWER_DOWN or HT_FREEZE to start the playout. */
	if ( ( pEchoChannel->byEchoOperationMode == cOCT6100_ECHO_OP_MODE_POWER_DOWN )
		|| ( pEchoChannel->byEchoOperationMode == cOCT6100_ECHO_OP_MODE_HT_FREEZE ) )
		return cOCT6100_ERR_BUFFER_PLAYOUT_ECHO_OP_MODE;
	
	/* The channel's NLP must be enabled for playout to occur. */
	if ( pEchoChannel->VqeConfig.fEnableNlp == FALSE )
		return cOCT6100_ERR_BUFFER_PLAYOUT_NLP_DISABLED;

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

	/*=====================================================================*/
	/* Check if the user activated the buffer playout events. */

	if ( f_pBufferPlayoutStart->fNotifyOnPlayoutStop == TRUE
		&& f_pApiInstance->pSharedInfo->ChipConfig.ulSoftBufPlayoutEventsBufSize == 0 )
		return cOCT6100_ERR_BUFFER_PLAYOUT_EVENT_DISABLED;

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

	/*=====================================================================*/
	/* Check if there is actually a buffer added in the list. */

	if ( f_pBufferPlayoutStart->ulPlayoutPort == cOCT6100_CHANNEL_PORT_ROUT )
	{
		if ( pEchoChannel->fRinBufAdded == FALSE )
			return cOCT6100_ERR_BUFFER_PLAYOUT_LIST_EMPTY;
	}
	else /* if ( f_pBufferPlayoutStart->ulPlayoutPort == cOCT6100_CHANNEL_PORT_SOUT ) */
	{
		if ( pEchoChannel->fSoutBufAdded == FALSE )
			return cOCT6100_ERR_BUFFER_PLAYOUT_LIST_EMPTY;
	}

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

	/* Return the requested information. */
	*f_pfNotifyOnPlayoutStop = f_pBufferPlayoutStart->fNotifyOnPlayoutStop;
	*f_pulUserEventId = f_pBufferPlayoutStart->ulUserEventId;
	*f_pfAllowStartIfActive = f_pBufferPlayoutStart->fAllowStartWhileActive;

	return cOCT6100_ERR_OK;
}


/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*\

Function:		Oct6100ApiWriteChanPlayoutStructs

Description:    Write the buffer playout event in the channel main structure.

-------------------------------------------------------------------------------
|	Argument		|	Description
-------------------------------------------------------------------------------
f_pApiInstance				Pointer to API instance. This memory is used to keep the
							present state of the chip and all its resources.
	
f_pBufferPlayoutStart		Pointer to buffer playout start structure.
f_ulChannelIndex			Index of the channel within the API's channel list.
f_ulBufferIndex				Index of the buffer within the API's buffer list.
f_fNotifyOnPlayoutStop		Flag for the notify on playout stop.
f_ulUserEventId				User event id passed to the user when a playout event is generated.
f_fAllowStartIfActive		Add to currently playing list flag.
f_ulPlayoutStopEventType	Playout stop event type to be generated if required.

\*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
UINT32 Oct6100ApiWriteChanPlayoutStructs(
				IN OUT	tPOCT6100_INSTANCE_API				f_pApiInstance,
				IN		tPOCT6100_BUFFER_PLAYOUT_START		f_pBufferPlayoutStart,
				IN		UINT32								f_ulChannelIndex, 
				IN		UINT32								f_ulBufferIndex,
				IN		BOOL								f_fNotifyOnPlayoutStop,
				IN		UINT32								f_ulUserEventId,
				IN		BOOL								f_fAllowStartIfActive,
				IN		UINT32								f_ulPlayoutStopEventType )
{
	tPOCT6100_API_BUFFER			pBufferEntry;
	tPOCT6100_API_CHANNEL			pEchoChannel;
	tPOCT6100_SHARED_INFO			pSharedInfo;
	tOCT6100_READ_PARAMS			ReadParams;
	
	UINT32	ulResult;

	UINT32	ulWritePtr;
	UINT32	ulChipWritePtr;
	PUINT32	pulSkipPtr;
	UINT32	ulWritePtrBytesOfst;
	UINT32	ulSkipPtrBytesOfst;
	UINT32	ulWritePtrBitOfst;
	UINT32	ulSkipPtrBitOfst;
	UINT32	ulWritePtrFieldSize;
	UINT32	ulSkipPtrFieldSize;

	UINT32	ulIgnoreBytesOfst;
	UINT32	ulIgnoreBitOfst;
	UINT32	ulIgnoreFieldSize;
	
	UINT32	ulHardSkipBytesOfst;
	UINT32	ulHardSkipBitOfst;
	UINT32	ulHardSkipFieldSize;

	UINT32	ulReadPtrBytesOfst;
	UINT32	ulReadPtrBitOfst;
	UINT32	ulReadPtrFieldSize;

	UINT32	ulPlayoutBaseAddress;
	UINT32	ulAddress;
	UINT32	ulTempData;
	UINT32	ulMask;
	UINT32	ulReadData;
	UINT32	ulReadPtr;
	UINT32	ulLoopCnt = 0;

	UINT16	usReadData;

	BOOL	fBufferPlayoutStopDetected;
	BOOL	fWriteSkipPtr = FALSE;
	BOOL	fStillPlaying = TRUE;

	UINT32			aulWaitTime[ 2 ];

	/* Obtain local pointer to shared portion of instance. */
	pSharedInfo = f_pApiInstance->pSharedInfo;

	mOCT6100_GET_CHANNEL_ENTRY_PNT( pSharedInfo, pEchoChannel, f_ulChannelIndex );
	mOCT6100_GET_BUFFER_ENTRY_PNT( f_pApiInstance->pSharedInfo, pBufferEntry, f_ulBufferIndex );

	/* First off, check for buffer playout events, if requested for this channel/port. */
	/* At the same time, if requested, check that the playout has stopped for this channel/port. */
	if ( ( ( pEchoChannel->fRinBufPlaying == TRUE )
			&& ( ( pEchoChannel->fRinBufPlayoutNotifyOnStop == TRUE ) || ( f_fAllowStartIfActive == FALSE ) )
			&& ( f_pBufferPlayoutStart->ulPlayoutPort == cOCT6100_CHANNEL_PORT_ROUT ) )
		|| ( ( ( pEchoChannel->fSoutBufPlaying == TRUE ) || ( f_fAllowStartIfActive == FALSE ) )
			&& ( pEchoChannel->fSoutBufPlayoutNotifyOnStop == TRUE )
			&& ( f_pBufferPlayoutStart->ulPlayoutPort == cOCT6100_CHANNEL_PORT_SOUT ) ) )
	{
		/* Buffer playout might still be going on for this channel/port. */
		ulResult = Oct6100BufferPlayoutCheckForSpecificEvent(	f_pApiInstance, 
																f_ulChannelIndex, 
																f_pBufferPlayoutStart->ulPlayoutPort,
																pEchoChannel->fRinBufPlayoutNotifyOnStop, 
																&fBufferPlayoutStopDetected );
		if ( ulResult != cOCT6100_ERR_OK )
			return ulResult;

		/* Check if the user requested to only start if playout is over.  Return an error if */
		/* buffer playout is still going on on this channel/port. */
		if ( ( f_fAllowStartIfActive == FALSE ) && ( fBufferPlayoutStopDetected == FALSE ) )
		{
			/* No go!  User should wait for the current list to stop, or call the */
			/* Oct6100BufferPlayoutStop function. */
			return cOCT6100_ERR_BUFFER_PLAYOUT_STILL_ACTIVE;
		}
	}

	/* Select the buffer of interest. */
	if ( f_pBufferPlayoutStart->ulPlayoutPort == cOCT6100_CHANNEL_PORT_ROUT )
	{
		ulWritePtr  = pEchoChannel->ulRinBufWritePtr;
		pulSkipPtr	= &pEchoChannel->ulRinBufSkipPtr;

		ulWritePtrBytesOfst = pSharedInfo->MemoryMap.PlayoutRinWritePtrOfst.usDwordOffset * 4;
		ulSkipPtrBytesOfst  = pSharedInfo->MemoryMap.PlayoutRinSkipPtrOfst.usDwordOffset * 4;
		ulIgnoreBytesOfst	= pSharedInfo->MemoryMap.PlayoutRinIgnoreSkipCleanOfst.usDwordOffset * 4;
		ulHardSkipBytesOfst	= pSharedInfo->MemoryMap.PlayoutRinHardSkipOfst.usDwordOffset * 4;
		ulReadPtrBytesOfst	= pSharedInfo->MemoryMap.PlayoutRinReadPtrOfst.usDwordOffset * 4;

		ulWritePtrBitOfst	= pSharedInfo->MemoryMap.PlayoutRinWritePtrOfst.byBitOffset;
		ulSkipPtrBitOfst	= pSharedInfo->MemoryMap.PlayoutRinSkipPtrOfst.byBitOffset;
		ulIgnoreBitOfst		= pSharedInfo->MemoryMap.PlayoutRinIgnoreSkipCleanOfst.byBitOffset;
		ulHardSkipBitOfst	= pSharedInfo->MemoryMap.PlayoutRinHardSkipOfst.byBitOffset;
		ulReadPtrBitOfst	= pSharedInfo->MemoryMap.PlayoutRinReadPtrOfst.byBitOffset;

		ulWritePtrFieldSize = pSharedInfo->MemoryMap.PlayoutRinWritePtrOfst.byFieldSize;
		ulSkipPtrFieldSize	= pSharedInfo->MemoryMap.PlayoutRinSkipPtrOfst.byFieldSize;
		ulIgnoreFieldSize	= pSharedInfo->MemoryMap.PlayoutRinIgnoreSkipCleanOfst.byFieldSize;
		ulHardSkipFieldSize	= pSharedInfo->MemoryMap.PlayoutRinHardSkipOfst.byFieldSize;
		ulReadPtrFieldSize	= pSharedInfo->MemoryMap.PlayoutRinReadPtrOfst.byFieldSize;
	}
	else /* f_pBufferPlayoutStart->ulPlayoutPort == cOCT6100_CHANNEL_PORT_SOUT */
	{
		ulWritePtr	= pEchoChannel->ulSoutBufWritePtr;
		pulSkipPtr	= &pEchoChannel->ulSoutBufSkipPtr;

		ulWritePtrBytesOfst = pSharedInfo->MemoryMap.PlayoutSoutWritePtrOfst.usDwordOffset * 4;
		ulSkipPtrBytesOfst  = pSharedInfo->MemoryMap.PlayoutSoutSkipPtrOfst.usDwordOffset * 4;
		ulIgnoreBytesOfst	= pSharedInfo->MemoryMap.PlayoutSoutIgnoreSkipCleanOfst.usDwordOffset * 4;
		ulHardSkipBytesOfst	= pSharedInfo->MemoryMap.PlayoutSoutHardSkipOfst.usDwordOffset * 4;
		ulReadPtrBytesOfst	= pSharedInfo->MemoryMap.PlayoutSoutReadPtrOfst.usDwordOffset * 4;

		ulWritePtrBitOfst	= pSharedInfo->MemoryMap.PlayoutSoutWritePtrOfst.byBitOffset;
		ulSkipPtrBitOfst	= pSharedInfo->MemoryMap.PlayoutSoutSkipPtrOfst.byBitOffset;
		ulIgnoreBitOfst		= pSharedInfo->MemoryMap.PlayoutSoutIgnoreSkipCleanOfst.byBitOffset;
		ulHardSkipBitOfst	= pSharedInfo->MemoryMap.PlayoutSoutHardSkipOfst.byBitOffset;
		ulReadPtrBitOfst	= pSharedInfo->MemoryMap.PlayoutSoutReadPtrOfst.byBitOffset;

		ulWritePtrFieldSize = pSharedInfo->MemoryMap.PlayoutSoutWritePtrOfst.byFieldSize;
		ulSkipPtrFieldSize	= pSharedInfo->MemoryMap.PlayoutSoutSkipPtrOfst.byFieldSize;
		ulIgnoreFieldSize	= pSharedInfo->MemoryMap.PlayoutSoutIgnoreSkipCleanOfst.byFieldSize;
		ulHardSkipFieldSize	= pSharedInfo->MemoryMap.PlayoutSoutHardSkipOfst.byFieldSize;
		ulReadPtrFieldSize	= pSharedInfo->MemoryMap.PlayoutSoutReadPtrOfst.byFieldSize;
	}
	


	/* Set the playout feature base address. */
	ulPlayoutBaseAddress = cOCT6100_CHANNEL_ROOT_BASE + ( f_ulChannelIndex * cOCT6100_CHANNEL_ROOT_SIZE ) + pSharedInfo->MemoryMap.ulChanRootConfOfst;

	/* Check if we must wait for stop to complete before starting a new list. */
	if ( pSharedInfo->ImageInfo.fBufferPlayoutSkipInEvents == FALSE )
	{
		if ( ( ( f_pBufferPlayoutStart->ulPlayoutPort == cOCT6100_CHANNEL_PORT_ROUT ) && ( pEchoChannel->fRinHardStop == TRUE ) )
			|| ( ( f_pBufferPlayoutStart->ulPlayoutPort == cOCT6100_CHANNEL_PORT_SOUT ) && ( pEchoChannel->fSoutHardStop == TRUE ) ) )
		{
			/* Read the read pointer. */
			ReadParams.pProcessContext = f_pApiInstance->pProcessContext;

			ReadParams.ulUserChipId = pSharedInfo->ChipConfig.ulUserChipId;
			ReadParams.pusReadData = &usReadData;
			ReadParams.ulReadAddress = ulPlayoutBaseAddress + ulReadPtrBytesOfst;

			/* Get the write pointer in the chip. */
			ulAddress = ulPlayoutBaseAddress + ulWritePtrBytesOfst;

			mOCT6100_RETRIEVE_NLP_CONF_DWORD( f_pApiInstance, pEchoChannel, ulAddress, &ulReadData, ulResult );
			if ( ulResult != cOCT6100_ERR_OK )
				return ulResult;

			mOCT6100_CREATE_FEATURE_MASK( ulWritePtrFieldSize, ulWritePtrBitOfst, &ulMask );

			/* Store the write pointer. */
			ulChipWritePtr = ( ulReadData & ulMask ) >> ulWritePtrBitOfst;

			/* Optimize this access by only reading the word we are interested in. */
			if ( ulReadPtrBitOfst < 16 )
				ReadParams.ulReadAddress += 2;

			while( fStillPlaying == TRUE )
			{				
				/* Read the read pointer until equals to the write pointer. */
				mOCT6100_DRIVER_READ_API( ReadParams, ulResult )
				if ( ulResult != cOCT6100_ERR_OK )
					return ulResult;

				/* Move data at correct position according to what was read. */
				if ( ulReadPtrBitOfst < 16 )
					ulTempData = usReadData;
				else
					ulTempData = usReadData << 16;
				
				mOCT6100_CREATE_FEATURE_MASK( ulReadPtrFieldSize, ulReadPtrBitOfst, &ulMask );
				
				/* Store the read pointer. */
				ulReadPtr = ( ulTempData & ulMask ) >> ulReadPtrBitOfst;

				/* Playout has finished when the read pointer reaches the write pointer. */
				if ( ulReadPtr == ulChipWritePtr )
					break;

				ulLoopCnt++;
				if( ulLoopCnt > cOCT6100_MAX_LOOP )
				{
					return cOCT6100_ERR_FATAL_E6;
				}

				aulWaitTime[ 0 ] = 100;
				aulWaitTime[ 1 ] = 0;
				ulResult = Oct6100ApiWaitForTime( f_pApiInstance, aulWaitTime );
				if ( ulResult != cOCT6100_ERR_OK )
					return ulResult;			
			}
		}
	}

	/* Check if must clear the skip bit. */
	if ( pSharedInfo->ImageInfo.fBufferPlayoutSkipInEvents == FALSE )
	{
		if ( ( pSharedInfo->ImageInfo.fRinBufferPlayoutHardSkip == TRUE )
			&& ( pSharedInfo->ImageInfo.fSoutBufferPlayoutHardSkip == TRUE ) )
		{
			/* Make sure the skip bit is cleared to start playout! */
			ulAddress = ulPlayoutBaseAddress + ulIgnoreBytesOfst;

			mOCT6100_RETRIEVE_NLP_CONF_DWORD( f_pApiInstance, pEchoChannel, ulAddress, &ulTempData, ulResult );
			if ( ulResult != cOCT6100_ERR_OK )
				return ulResult;
			
			mOCT6100_CREATE_FEATURE_MASK( ulIgnoreFieldSize, ulIgnoreBitOfst, &ulMask );

			/* Cleared! */
			ulTempData &= ( ~ulMask );

			mOCT6100_SAVE_NLP_CONF_DWORD(	f_pApiInstance,
											pEchoChannel,
											ulAddress,
											ulTempData,
											ulResult );
			if ( ulResult != cOCT6100_ERR_OK )
				return ulResult;

			/* Make sure the hard skip bit is cleared to start playout! */
			ulAddress = ulPlayoutBaseAddress + ulHardSkipBytesOfst;

			mOCT6100_RETRIEVE_NLP_CONF_DWORD(	f_pApiInstance,
												pEchoChannel,
												ulAddress,
												&ulTempData,
												ulResult );
			if ( ulResult != cOCT6100_ERR_OK )
				return ulResult;
			
			mOCT6100_CREATE_FEATURE_MASK( ulIgnoreFieldSize, ulHardSkipBitOfst, &ulMask );

			/* Cleared! */
			ulTempData &= ( ~ulMask );

			mOCT6100_SAVE_NLP_CONF_DWORD(	f_pApiInstance,
											pEchoChannel,
											ulAddress,
											ulTempData,
											ulResult );
			if ( ulResult != cOCT6100_ERR_OK )
				return ulResult;
		}
	}

	/* Write the skip and write pointer to activate buffer playout. */

	/* Update the skip pointer. */
	if ( ( pSharedInfo->ImageInfo.fRinBufferPlayoutHardSkip == FALSE )
		|| ( pSharedInfo->ImageInfo.fSoutBufferPlayoutHardSkip == FALSE ) )
	{
		/* Old 31 events image. */
		if ( ( ( ulWritePtr - *pulSkipPtr ) & 0x7F ) > 63 )
		{
			*pulSkipPtr = ( ulWritePtr - 63 ) & 0x7F;
			fWriteSkipPtr = TRUE;
		}
	}
	else
	{
		/* No need to update the skip pointer, a bit needs to be set when skipping. */
		/* fWriteSkipPtr set to FALSE from variable declaration. */
	}

	if ( fWriteSkipPtr == TRUE )
	{
		/*=======================================================================*/
		/* Fetch and modify the skip pointer. */	

		ulAddress = ulPlayoutBaseAddress + ulSkipPtrBytesOfst;

		mOCT6100_RETRIEVE_NLP_CONF_DWORD(	f_pApiInstance,
											pEchoChannel,
											ulAddress,
											&ulTempData,
											ulResult );
		if ( ulResult != cOCT6100_ERR_OK )
			return ulResult;
		
		mOCT6100_CREATE_FEATURE_MASK( ulSkipPtrFieldSize, ulSkipPtrBitOfst, &ulMask );
		
		ulTempData &= ( ~ulMask );
		ulTempData |= *pulSkipPtr << ulSkipPtrBitOfst;
		
		mOCT6100_SAVE_NLP_CONF_DWORD(	f_pApiInstance,
										pEchoChannel,
										ulAddress,
										ulTempData,
										ulResult );
		if ( ulResult != cOCT6100_ERR_OK )
			return ulResult;

		/*=======================================================================*/
	}


	/*=======================================================================*/
	/* Fetch and modify the write pointer. */	

	ulAddress = ulPlayoutBaseAddress + ulWritePtrBytesOfst;

	mOCT6100_RETRIEVE_NLP_CONF_DWORD(	f_pApiInstance,
										pEchoChannel,
										ulAddress,
										&ulTempData,
										ulResult );
	if ( ulResult != cOCT6100_ERR_OK )
		return ulResult;
	
	mOCT6100_CREATE_FEATURE_MASK( ulWritePtrFieldSize, ulWritePtrBitOfst, &ulMask );
	
	ulTempData &= ( ~ulMask );
	ulTempData |= ulWritePtr << ulWritePtrBitOfst;
	
	mOCT6100_SAVE_NLP_CONF_DWORD(	f_pApiInstance,
									pEchoChannel,
									ulAddress,
									ulTempData,
									ulResult );
	if ( ulResult != cOCT6100_ERR_OK )
		return ulResult;
	
	/*=======================================================================*/
	

	/*=======================================================================*/
	/* Now update the state of the channel stating that the buffer playout is activated. */

	/* Select the buffer of interest.*/
	if ( f_pBufferPlayoutStart->ulPlayoutPort == cOCT6100_CHANNEL_PORT_ROUT )
	{
		/* Check if the global ports active stat must be incremented. */
		if ( pEchoChannel->fRinBufPlaying == FALSE )
		{
			/* Increment the number of active buffer playout ports. */
			pSharedInfo->ChipStats.usNumberActiveBufPlayoutPorts++;
		}

		pEchoChannel->fRinBufPlaying = TRUE;
		/* Keep the new notify on event flag. */
		pEchoChannel->fRinBufPlayoutNotifyOnStop = (UINT8)( f_fNotifyOnPlayoutStop & 0xFF );
		/* Keep the specified user event id. */
		pEchoChannel->ulRinUserBufPlayoutEventId = f_ulUserEventId;
		/* Keep type of event to be generated. */
		pEchoChannel->byRinPlayoutStopEventType = (UINT8)( f_ulPlayoutStopEventType & 0xFF );
		/* No hard stop for now. */
		pEchoChannel->fRinHardStop = FALSE;
		/* No buffer added in the rin list for now. */
		pEchoChannel->fRinBufAdded = FALSE;
		/* Buffer playout is active on this channel. */
		pEchoChannel->fBufPlayoutActive = TRUE;
	}
	else /* f_pBufferPlayoutStart->ulPlayoutPort == cOCT6100_CHANNEL_PORT_SOUT */
	{
		/* Check if the global ports active stat must be incremented. */
		if ( pEchoChannel->fSoutBufPlaying == FALSE )
		{
			/* Increment the number of active buffer playout ports. */
			pSharedInfo->ChipStats.usNumberActiveBufPlayoutPorts++;
		}

		pEchoChannel->fSoutBufPlaying = TRUE;
		/* Keep the new notify on event flag. */
		pEchoChannel->fSoutBufPlayoutNotifyOnStop = (UINT8)( f_fNotifyOnPlayoutStop & 0xFF );
		/* Keep the specified user event id. */
		pEchoChannel->ulSoutUserBufPlayoutEventId = f_ulUserEventId;
		/* Keep type of event to be generated. */
		pEchoChannel->bySoutPlayoutStopEventType = (UINT8)( f_ulPlayoutStopEventType & 0xFF );
		/* No hard stop for now. */
		pEchoChannel->fSoutHardStop = FALSE;
		/* No buffer added in the sout list for now. */
		pEchoChannel->fSoutBufAdded = FALSE;
		/* Buffer playout is active on this channel. */
		pEchoChannel->fBufPlayoutActive = TRUE;
	}

	return cOCT6100_ERR_OK;
}


/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*\

Function:		Oct6100BufferPlayoutStopSer

Description:    Stops buffer playout on a channel.

-------------------------------------------------------------------------------
|	Argument		|	Description
-------------------------------------------------------------------------------
f_pApiInstance			Pointer to API instance. This memory is used to keep the
						present state of the chip and all its resources.

f_pBufferPlayoutStop	Pointer to buffer playout stop structure.

\*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
UINT32 Oct6100BufferPlayoutStopSer(
				IN OUT	tPOCT6100_INSTANCE_API				f_pApiInstance,
				IN OUT	tPOCT6100_BUFFER_PLAYOUT_STOP		f_pBufferPlayoutStop )
{
	UINT32	ulChannelIndex;
	UINT16	usEchoMemIndex;
	UINT32	ulResult;

	/* Check the user's configuration of the buffer for errors. */
	ulResult = Oct6100ApiAssertPlayoutStopParams( 
												f_pApiInstance, 
												f_pBufferPlayoutStop, 
												&ulChannelIndex, 
												&usEchoMemIndex );
	if ( ulResult != cOCT6100_ERR_OK )
		return ulResult;

	/* Write to  all resources needed to deactivate buffer playout. */
	ulResult = Oct6100ApiInvalidateChanPlayoutStructs( 
												f_pApiInstance, 
												f_pBufferPlayoutStop, 
												ulChannelIndex, 
												usEchoMemIndex 

												);
	if ( ulResult != cOCT6100_ERR_OK )
		return ulResult;

	return cOCT6100_ERR_OK;
}


/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*\

Function:		Oct6100ApiAssertPlayoutStopParams

Description:	Check the validity of the channel and buffer requested.

-------------------------------------------------------------------------------
|	Argument		|	Description
-------------------------------------------------------------------------------
f_pApiInstance			Pointer to API instance. This memory is used to keep the
						present state of the chip and all its resources.

f_pBufferPlayoutStop	Pointer to buffer playout stop structure.  
f_pulChannelIndex		Pointer to the channel index on which playout is to be stopped.
f_pusEchoMemIndex		Pointer to the echo mem index on which playout is to be stopped.

\*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
UINT32 Oct6100ApiAssertPlayoutStopParams(
				IN OUT	tPOCT6100_INSTANCE_API				f_pApiInstance,
				IN		tPOCT6100_BUFFER_PLAYOUT_STOP		f_pBufferPlayoutStop,
				OUT		PUINT32								f_pulChannelIndex,
				OUT		PUINT16								f_pusEchoMemIndex )
{
	tPOCT6100_API_CHANNEL		pEchoChannel;
	UINT32	ulEntryOpenCnt;

	/* Check for errors. */
	if ( f_pApiInstance->pSharedInfo->ChipConfig.usMaxPlayoutBuffers == 0 )
		return cOCT6100_ERR_BUFFER_PLAYOUT_DISABLED;
	
	if ( f_pBufferPlayoutStop->ulChannelHndl == cOCT6100_INVALID_HANDLE )
		return cOCT6100_ERR_BUFFER_PLAYOUT_CHANNEL_HANDLE_INVALID;

	if ( f_pBufferPlayoutStop->ulPlayoutPort != cOCT6100_CHANNEL_PORT_ROUT && 
		 f_pBufferPlayoutStop->ulPlayoutPort != cOCT6100_CHANNEL_PORT_SOUT )
		return cOCT6100_ERR_BUFFER_PLAYOUT_PLAYOUT_PORT;

	if ( f_pBufferPlayoutStop->fStopCleanly != TRUE && f_pBufferPlayoutStop->fStopCleanly != FALSE )
		return cOCT6100_ERR_BUFFER_PLAYOUT_STOP_CLEANLY;
	
	/*=====================================================================*/
	/* Check the channel handle. */

	if ( (f_pBufferPlayoutStop->ulChannelHndl & cOCT6100_HNDL_TAG_MASK) != cOCT6100_HNDL_TAG_CHANNEL )
		return cOCT6100_ERR_BUFFER_PLAYOUT_CHANNEL_HANDLE_INVALID;

	*f_pulChannelIndex = f_pBufferPlayoutStop->ulChannelHndl & cOCT6100_HNDL_INDEX_MASK;
	if ( *f_pulChannelIndex >= f_pApiInstance->pSharedInfo->ChipConfig.usMaxChannels )
		return cOCT6100_ERR_BUFFER_PLAYOUT_CHANNEL_HANDLE_INVALID;

	mOCT6100_GET_CHANNEL_ENTRY_PNT( f_pApiInstance->pSharedInfo, pEchoChannel, *f_pulChannelIndex )

	/* Extract the entry open count from the provided handle. */
	ulEntryOpenCnt = (f_pBufferPlayoutStop->ulChannelHndl >> cOCT6100_ENTRY_OPEN_CNT_SHIFT) & cOCT6100_ENTRY_OPEN_CNT_MASK;

	/* Check for errors. */
	if ( pEchoChannel->fReserved != TRUE )
		return cOCT6100_ERR_BUFFER_PLAYOUT_CHANNEL_NOT_OPEN;
	if ( ulEntryOpenCnt != pEchoChannel->byEntryOpenCnt )
		return cOCT6100_ERR_BUFFER_PLAYOUT_CHANNEL_HANDLE_INVALID;

	/* Return echo memory index. */
	*f_pusEchoMemIndex = pEchoChannel->usEchoMemIndex;

	/* Check if buffer playout is active for the selected port. */
	if ( ( f_pBufferPlayoutStop->ulPlayoutPort == cOCT6100_CHANNEL_PORT_ROUT )
		&& ( pEchoChannel->fRinBufPlaying == FALSE )
		&& ( pEchoChannel->fRinBufAdded == FALSE ) )
		return cOCT6100_ERR_BUFFER_PLAYOUT_NOT_STARTED;

	if ( ( f_pBufferPlayoutStop->ulPlayoutPort == cOCT6100_CHANNEL_PORT_SOUT )
		&& ( pEchoChannel->fSoutBufPlaying == FALSE )
		 && ( pEchoChannel->fSoutBufAdded == FALSE ) )
		return cOCT6100_ERR_BUFFER_PLAYOUT_NOT_STARTED;
	
	/*=====================================================================*/

	return cOCT6100_ERR_OK;
}


/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*\

Function:		Oct6100ApiInvalidateChanPlayoutStructs

Description:    Write the buffer playout event in the channel main structure.

-------------------------------------------------------------------------------
|	Argument		|	Description
-------------------------------------------------------------------------------
f_pApiInstance				Pointer to API instance. This memory is used to keep the
							present state of the chip and all its resources.
	
f_pBufferPlayoutStop		Pointer to buffer playout stop structure.  
f_ulChannelIndex			Index of the channel within the API's channel list.
f_usEchoMemIndex			Index of the echo channel in hardware memory.

\*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
UINT32 Oct6100ApiInvalidateChanPlayoutStructs(
				IN OUT	tPOCT6100_INSTANCE_API				f_pApiInstance,
				IN		tPOCT6100_BUFFER_PLAYOUT_STOP		f_pBufferPlayoutStop,
				IN		UINT32								f_ulChannelIndex,
				IN		UINT16								f_usEchoMemIndex

				)
{
	tPOCT6100_API_CHANNEL	pEchoChannel;
	tPOCT6100_SHARED_INFO	pSharedInfo;
	tOCT6100_READ_PARAMS	ReadParams;
	tOCT6100_WRITE_PARAMS	WriteParams;

	UINT32	ulResult;

	UINT32	ulWritePtrBytesOfst;
	UINT32	ulWritePtrBitOfst;
	UINT32	ulWritePtrFieldSize;
	UINT32	ulSkipPtrBytesOfst;
	UINT32	ulSkipPtrBitOfst;
	UINT32	ulSkipPtrFieldSize;
	UINT32	ulIgnoreBytesOfst;
	UINT32	ulIgnoreBitOfst;
	UINT32	ulIgnoreFieldSize;
	UINT32	ulHardSkipBytesOfst;
	UINT32	ulHardSkipBitOfst;
	UINT32	ulHardSkipFieldSize;
	UINT32	ulReadPtrBytesOfst;
	UINT32	ulReadPtrBitOfst;
	UINT32	ulReadPtrFieldSize;

	UINT32	ulSkipPtr;
	UINT32	ulWritePtr;
	UINT32	ulReadPtr = 0;
	UINT32	ulCurrentPtr;

	UINT32	ulPlayoutBaseAddress;
	UINT32	ulAddress;
	UINT32	ulTempData;
	UINT32	ulMask;
	UINT32	ulReadData;

	UINT16	usReadData;	
	BOOL	fCheckStop = FALSE;

	UINT32	ulEventBuffer;

	/* Obtain local pointer to shared portion of instance. */
	pSharedInfo = f_pApiInstance->pSharedInfo;

	WriteParams.pProcessContext = f_pApiInstance->pProcessContext;

	WriteParams.ulUserChipId = f_pApiInstance->pSharedInfo->ChipConfig.ulUserChipId;

	ReadParams.pProcessContext = f_pApiInstance->pProcessContext;

	ReadParams.ulUserChipId = pSharedInfo->ChipConfig.ulUserChipId;
	ReadParams.pusReadData = &usReadData;

	mOCT6100_GET_CHANNEL_ENTRY_PNT( pSharedInfo, pEchoChannel, f_ulChannelIndex );

	/* Select the port of interest. */
	if ( f_pBufferPlayoutStop->ulPlayoutPort == cOCT6100_CHANNEL_PORT_ROUT )
	{
		ulWritePtr = pEchoChannel->ulRinBufWritePtr; 
		ulSkipPtr  = ulWritePtr;

		ulWritePtrBytesOfst = pSharedInfo->MemoryMap.PlayoutRinWritePtrOfst.usDwordOffset * 4;
		ulWritePtrBitOfst	= pSharedInfo->MemoryMap.PlayoutRinWritePtrOfst.byBitOffset;
		ulWritePtrFieldSize = pSharedInfo->MemoryMap.PlayoutRinWritePtrOfst.byFieldSize;

		ulSkipPtrBytesOfst  = pSharedInfo->MemoryMap.PlayoutRinSkipPtrOfst.usDwordOffset * 4;
		ulSkipPtrBitOfst	= pSharedInfo->MemoryMap.PlayoutRinSkipPtrOfst.byBitOffset;
		ulSkipPtrFieldSize	= pSharedInfo->MemoryMap.PlayoutRinSkipPtrOfst.byFieldSize;

		ulIgnoreBytesOfst	= pSharedInfo->MemoryMap.PlayoutRinIgnoreSkipCleanOfst.usDwordOffset * 4;
		ulIgnoreBitOfst		= pSharedInfo->MemoryMap.PlayoutRinIgnoreSkipCleanOfst.byBitOffset;
		ulIgnoreFieldSize	= pSharedInfo->MemoryMap.PlayoutRinIgnoreSkipCleanOfst.byFieldSize;

		ulHardSkipBytesOfst	= pSharedInfo->MemoryMap.PlayoutRinHardSkipOfst.usDwordOffset * 4;
		ulHardSkipBitOfst	= pSharedInfo->MemoryMap.PlayoutRinHardSkipOfst.byBitOffset;
		ulHardSkipFieldSize	= pSharedInfo->MemoryMap.PlayoutRinHardSkipOfst.byFieldSize;

		ulReadPtrBytesOfst	= pSharedInfo->MemoryMap.PlayoutRinReadPtrOfst.usDwordOffset * 4;
		ulReadPtrBitOfst	= pSharedInfo->MemoryMap.PlayoutRinReadPtrOfst.byBitOffset;
		ulReadPtrFieldSize	= pSharedInfo->MemoryMap.PlayoutRinReadPtrOfst.byFieldSize;
	}
	else /* f_pBufferPlayoutStop->ulPlayoutPort == cOCT6100_CHANNEL_PORT_SOUT */
	{
		ulWritePtr = pEchoChannel->ulSoutBufWritePtr; 
		ulSkipPtr  = ulWritePtr;

		ulWritePtrBytesOfst = pSharedInfo->MemoryMap.PlayoutSoutWritePtrOfst.usDwordOffset * 4;
		ulWritePtrBitOfst	= pSharedInfo->MemoryMap.PlayoutSoutWritePtrOfst.byBitOffset;
		ulWritePtrFieldSize = pSharedInfo->MemoryMap.PlayoutSoutWritePtrOfst.byFieldSize;

		ulSkipPtrBytesOfst  = pSharedInfo->MemoryMap.PlayoutSoutSkipPtrOfst.usDwordOffset * 4;
		ulSkipPtrBitOfst	= pSharedInfo->MemoryMap.PlayoutSoutSkipPtrOfst.byBitOffset;
		ulSkipPtrFieldSize	= pSharedInfo->MemoryMap.PlayoutSoutSkipPtrOfst.byFieldSize;

		ulIgnoreBytesOfst	= pSharedInfo->MemoryMap.PlayoutSoutIgnoreSkipCleanOfst.usDwordOffset * 4;
		ulIgnoreBitOfst		= pSharedInfo->MemoryMap.PlayoutSoutIgnoreSkipCleanOfst.byBitOffset;
		ulIgnoreFieldSize	= pSharedInfo->MemoryMap.PlayoutSoutIgnoreSkipCleanOfst.byFieldSize;

		ulHardSkipBytesOfst	= pSharedInfo->MemoryMap.PlayoutSoutHardSkipOfst.usDwordOffset * 4;
		ulHardSkipBitOfst	= pSharedInfo->MemoryMap.PlayoutSoutHardSkipOfst.byBitOffset;
		ulHardSkipFieldSize	= pSharedInfo->MemoryMap.PlayoutSoutHardSkipOfst.byFieldSize;

		ulReadPtrBytesOfst	= pSharedInfo->MemoryMap.PlayoutSoutReadPtrOfst.usDwordOffset * 4;
		ulReadPtrBitOfst	= pSharedInfo->MemoryMap.PlayoutSoutReadPtrOfst.byBitOffset;
		ulReadPtrFieldSize	= pSharedInfo->MemoryMap.PlayoutSoutReadPtrOfst.byFieldSize;
	}

	/* Set the playout feature base address. */
	ulPlayoutBaseAddress = cOCT6100_CHANNEL_ROOT_BASE + ( f_usEchoMemIndex * cOCT6100_CHANNEL_ROOT_SIZE ) + pSharedInfo->MemoryMap.ulChanRootConfOfst;

	/* Check if something is currently playing. */
	if ( f_pBufferPlayoutStop->ulPlayoutPort == cOCT6100_CHANNEL_PORT_ROUT )
	{
		if ( pEchoChannel->fRinBufPlaying == TRUE )
		{
			/* Check if we are stopping it or if it stopped by itself. */
			fCheckStop = TRUE;
		}
		else
		{
			/* Not playing! */
			if ( f_pBufferPlayoutStop->pfAlreadyStopped != NULL )
				*f_pBufferPlayoutStop->pfAlreadyStopped = TRUE;
		}
	}
	else /* if ( f_pBufferPlayoutStop->ulPlayoutPort == cOCT6100_CHANNEL_PORT_SOUT ) */
	{
		if ( pEchoChannel->fSoutBufPlaying == TRUE )
		{
			/* Check if we are stopping it or if it stopped by itself. */
			fCheckStop = TRUE;
		}
		else
		{
			/* Not playing! */
			if ( f_pBufferPlayoutStop->pfAlreadyStopped != NULL )
				*f_pBufferPlayoutStop->pfAlreadyStopped = TRUE;
		}
	}

	if ( ( fCheckStop == TRUE ) || ( pSharedInfo->ImageInfo.fBufferPlayoutSkipInEvents == TRUE ) )
	{
		/* Read the read pointer. */
		ReadParams.pProcessContext = f_pApiInstance->pProcessContext;

		ReadParams.ulUserChipId = pSharedInfo->ChipConfig.ulUserChipId;
		ReadParams.pusReadData = &usReadData;
		ReadParams.ulReadAddress = ulPlayoutBaseAddress + ulReadPtrBytesOfst;

		/* Optimize this access by only reading the word we are interested in. */
		if ( ulReadPtrBitOfst < 16 )
			ReadParams.ulReadAddress += 2;

		/* Must read in memory directly since this value is changed by hardware */
		mOCT6100_DRIVER_READ_API( ReadParams, ulResult )
		if ( ulResult != cOCT6100_ERR_OK )
			return ulResult;

		/* Move data at correct position according to what was read. */
		if ( ulReadPtrBitOfst < 16 )
			ulTempData = usReadData;
		else
			ulTempData = usReadData << 16;
		
		mOCT6100_CREATE_FEATURE_MASK( ulReadPtrFieldSize, ulReadPtrBitOfst, &ulMask );
		
		/* Store the read pointer. */
		ulReadPtr = ( ulTempData & ulMask ) >> ulReadPtrBitOfst;

		/* Playout has finished when the read pointer reaches the write pointer. */
		if ( f_pBufferPlayoutStop->pfAlreadyStopped != NULL )
		{
			if ( ulReadPtr != ulWritePtr )
				*f_pBufferPlayoutStop->pfAlreadyStopped = FALSE;
			else /* if ( ulReadPtr == ulWritePtr ) */
				*f_pBufferPlayoutStop->pfAlreadyStopped = TRUE;
		}
	}

	/* If the skip bits are located in the event itself, the playout is stopped by setting the */
	/* skip pointer to the hardware chip write pointer.  Read it directly from the NLP configuration. */
	if ( pSharedInfo->ImageInfo.fBufferPlayoutSkipInEvents == TRUE )
	{
		if ( ulReadPtr != ulWritePtr )
		{
			/* Get the write pointer in the chip. */
			ulAddress = ulPlayoutBaseAddress + ulWritePtrBytesOfst;

			mOCT6100_RETRIEVE_NLP_CONF_DWORD( f_pApiInstance, pEchoChannel, ulAddress, &ulReadData, ulResult );
			if ( ulResult != cOCT6100_ERR_OK )
				return ulResult;

			mOCT6100_CREATE_FEATURE_MASK( ulWritePtrFieldSize, ulWritePtrBitOfst, &ulMask );

			/* Store the write pointer. */
			ulWritePtr = ( ulReadData & ulMask ) >> ulWritePtrBitOfst;
			ulSkipPtr = ulWritePtr;
		}
	}

	/* Check if must clear repeat bit. */
	if ( ( ( pEchoChannel->fRinBufPlayoutRepeatUsed == TRUE ) && ( f_pBufferPlayoutStop->ulPlayoutPort == cOCT6100_CHANNEL_PORT_ROUT ) )
		|| ( ( pEchoChannel->fSoutBufPlayoutRepeatUsed == TRUE ) && ( f_pBufferPlayoutStop->ulPlayoutPort == cOCT6100_CHANNEL_PORT_SOUT ) ) )
	{
		if ( ( pSharedInfo->ImageInfo.fBufferPlayoutSkipInEvents == FALSE )
			|| ( ( pSharedInfo->ImageInfo.fBufferPlayoutSkipInEvents == TRUE )
				&& ( ulWritePtr != ulReadPtr ) ) )
		{
			if ( f_pBufferPlayoutStop->ulPlayoutPort == cOCT6100_CHANNEL_PORT_ROUT )
			{
				ulEventBuffer = pSharedInfo->MemoryMap.ulChanMainRinPlayoutMemOfst;
			}
			else /* f_pBufferPlayoutStop->ulPlayoutPort == cOCT6100_CHANNEL_PORT_SOUT */
			{
				ulEventBuffer = pSharedInfo->MemoryMap.ulChanMainSoutPlayoutMemOfst;
			}

			/* Set the playout event base address. */
			if ( ( pSharedInfo->ImageInfo.fRinBufferPlayoutHardSkip == TRUE )
				&& ( pSharedInfo->ImageInfo.fSoutBufferPlayoutHardSkip == TRUE ) )
			{
				/* 127 or 31 events image. */
				ulAddress = pSharedInfo->MemoryMap.ulChanMainMemBase + ( f_usEchoMemIndex * pSharedInfo->MemoryMap.ulChanMainMemSize ) + ulEventBuffer + (cOCT6100_PLAYOUT_EVENT_MEM_SIZE * ( ( ulWritePtr - 1 ) & ( pSharedInfo->ImageInfo.byMaxNumberPlayoutEvents - 1 )));
			}
			else
			{
				/* Old 31 events image. */
				ulAddress = pSharedInfo->MemoryMap.ulChanMainMemBase + ( f_usEchoMemIndex * pSharedInfo->MemoryMap.ulChanMainMemSize ) + ulEventBuffer + (cOCT6100_PLAYOUT_EVENT_MEM_SIZE * ( ( ulWritePtr - 1 ) & 0x1F));
			}

			/* EVENT BASE + 4 */
			/* Playout configuration. */
			ulAddress += 4;

			ReadParams.ulReadAddress = ulAddress;
			mOCT6100_DRIVER_READ_API( ReadParams, ulResult );
			if ( ulResult != cOCT6100_ERR_OK )
				return ulResult;

			/* Read-clear-write the new repeat bit. */
			usReadData &= 0x7FFF;

			WriteParams.ulWriteAddress = ulAddress;
			WriteParams.usWriteData = usReadData;
			mOCT6100_DRIVER_WRITE_API( WriteParams, ulResult );
			if ( ulResult != cOCT6100_ERR_OK )
				return ulResult;
		}
	}

	/* Write the skip to the value of the write pointer to stop buffer playout. */

	/*=======================================================================*/
	/* First set the ignore skip clean bit if required. */	

	if ( ( pSharedInfo->ImageInfo.fRinBufferPlayoutHardSkip == FALSE )
		|| ( pSharedInfo->ImageInfo.fSoutBufferPlayoutHardSkip == FALSE ) )
	{
		ulAddress = ulPlayoutBaseAddress + ulIgnoreBytesOfst;

		mOCT6100_RETRIEVE_NLP_CONF_DWORD(	f_pApiInstance,
											pEchoChannel,
											ulAddress,
											&ulTempData,
											ulResult );
		if ( ulResult != cOCT6100_ERR_OK )
			return ulResult;
		
		mOCT6100_CREATE_FEATURE_MASK( ulIgnoreFieldSize, ulIgnoreBitOfst, &ulMask );
		
		ulTempData &= ( ~ulMask );

		/* Check if the skip need to be clean or not. */
		if ( f_pBufferPlayoutStop->fStopCleanly == FALSE )
			ulTempData |= 0x1 << ulIgnoreBitOfst;
		
		mOCT6100_SAVE_NLP_CONF_DWORD(	f_pApiInstance,
										pEchoChannel,
										ulAddress,
										ulTempData,
										ulResult );
		if ( ulResult != cOCT6100_ERR_OK )
			return ulResult;
	}

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

	
	/*=======================================================================*/
	/* Fetch and modify the write pointer. */	

	ulAddress = ulPlayoutBaseAddress + ulWritePtrBytesOfst;

	mOCT6100_RETRIEVE_NLP_CONF_DWORD( f_pApiInstance, pEchoChannel, ulAddress, &ulTempData, ulResult );
	if ( ulResult != cOCT6100_ERR_OK )
		return ulResult;
	
	mOCT6100_CREATE_FEATURE_MASK( ulWritePtrFieldSize, ulWritePtrBitOfst, &ulMask );
	
	ulTempData &= ( ~ulMask );
	ulTempData |= ulWritePtr << ulWritePtrBitOfst;
	
	mOCT6100_SAVE_NLP_CONF_DWORD(	f_pApiInstance,
									pEchoChannel,
									ulAddress,
									ulTempData,
									ulResult );
	if ( ulResult != cOCT6100_ERR_OK )
		return ulResult;
	
	/*=======================================================================*/

	
	/*=======================================================================*/
	/* Fetch and modify the skip pointer. */	

	ulAddress = ulPlayoutBaseAddress + ulSkipPtrBytesOfst;

	mOCT6100_RETRIEVE_NLP_CONF_DWORD(	f_pApiInstance,
										pEchoChannel,
										ulAddress,
										&ulTempData,
										ulResult );
	if ( ulResult != cOCT6100_ERR_OK )
		return ulResult;
	
	mOCT6100_CREATE_FEATURE_MASK( ulSkipPtrFieldSize, ulSkipPtrBitOfst, &ulMask );
	
	ulTempData &= ( ~ulMask );
	ulTempData |= ulSkipPtr << ulSkipPtrBitOfst;
	
	mOCT6100_SAVE_NLP_CONF_DWORD(	f_pApiInstance,
									pEchoChannel,
									ulAddress,
									ulTempData,
									ulResult );
	if ( ulResult != cOCT6100_ERR_OK )
		return ulResult;
	
	/*=======================================================================*/
	

	/*=======================================================================*/
	/* If in the new buffer playout case, things are in a different order. */	

	if ( pSharedInfo->ImageInfo.fBufferPlayoutSkipInEvents == FALSE )
	{
		if ( ( pSharedInfo->ImageInfo.fRinBufferPlayoutHardSkip == TRUE )
			&& ( pSharedInfo->ImageInfo.fSoutBufferPlayoutHardSkip == TRUE ) )
		{
			ulAddress = ulPlayoutBaseAddress + ulHardSkipBytesOfst;

			mOCT6100_RETRIEVE_NLP_CONF_DWORD(	f_pApiInstance,
												pEchoChannel,
												ulAddress,
												&ulTempData,
												ulResult );
			if ( ulResult != cOCT6100_ERR_OK )
				return ulResult;
			
			mOCT6100_CREATE_FEATURE_MASK( ulHardSkipFieldSize, ulHardSkipBitOfst, &ulMask );
			
			ulTempData &= ( ~ulMask );

			/* Check if the skip need to be clean or not. */
			if ( f_pBufferPlayoutStop->fStopCleanly == FALSE )
				ulTempData |= 0x1 << ulHardSkipBitOfst;
			
			mOCT6100_SAVE_NLP_CONF_DWORD(	f_pApiInstance,
											pEchoChannel,
											ulAddress,
											ulTempData,
											ulResult );
			if ( ulResult != cOCT6100_ERR_OK )
				return ulResult;

			/* Now is the appropriate time to skip! */
			ulAddress = ulPlayoutBaseAddress + ulIgnoreBytesOfst;

			mOCT6100_RETRIEVE_NLP_CONF_DWORD(	f_pApiInstance,
												pEchoChannel,
												ulAddress,
												&ulTempData,
												ulResult );
			if ( ulResult != cOCT6100_ERR_OK )
				return ulResult;
			
			mOCT6100_CREATE_FEATURE_MASK( ulIgnoreFieldSize, ulIgnoreBitOfst, &ulMask );

			ulTempData &= ( ~ulMask );

			/* Set the skip bit. */
			ulTempData |= 0x1 << ulIgnoreBitOfst;
			
			mOCT6100_SAVE_NLP_CONF_DWORD(	f_pApiInstance,
											pEchoChannel,
											ulAddress,
											ulTempData,
											ulResult );
			if ( ulResult != cOCT6100_ERR_OK )
				return ulResult;
		}
	}

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


	/*=======================================================================*/
	/* The API must set the skip bit in all the events that are queued. */

	if ( pSharedInfo->ImageInfo.fBufferPlayoutSkipInEvents == TRUE )
	{
		if ( fCheckStop == TRUE )
		{
			if ( ulReadPtr != ulWritePtr )
			{
				if ( f_pBufferPlayoutStop->ulPlayoutPort == cOCT6100_CHANNEL_PORT_ROUT )
				{
					ulEventBuffer = pSharedInfo->MemoryMap.ulChanMainRinPlayoutMemOfst;
				}
				else /* f_pBufferPlayoutStop->ulPlayoutPort == cOCT6100_CHANNEL_PORT_SOUT */
				{
					ulEventBuffer = pSharedInfo->MemoryMap.ulChanMainSoutPlayoutMemOfst;
				}

				for ( ulCurrentPtr = ulReadPtr; ulCurrentPtr != ulWritePtr; )
				{
					/* Set the playout event base address. */
					
					/* 127 or 31 events image. */
					ulAddress = pSharedInfo->MemoryMap.ulChanMainMemBase + ( f_usEchoMemIndex * pSharedInfo->MemoryMap.ulChanMainMemSize ) + ulEventBuffer + ( cOCT6100_PLAYOUT_EVENT_MEM_SIZE * ulCurrentPtr );
					ulCurrentPtr++;
					ulCurrentPtr &= ( pSharedInfo->ImageInfo.byMaxNumberPlayoutEvents - 1 );

					/* EVENT BASE + 0 playout configuration. */
					WriteParams.ulWriteAddress = ulAddress;

					/* Set skip bit + hard-skip bit. */
					WriteParams.usWriteData = 0x8000;
					if ( f_pBufferPlayoutStop->fStopCleanly == FALSE )
						WriteParams.usWriteData |= 0x4000;
					mOCT6100_DRIVER_WRITE_API( WriteParams, ulResult );
					if ( ulResult != cOCT6100_ERR_OK )
						return ulResult;
				}
			}
		}
	}

	/*=======================================================================*/
	/* If stop immediatly, wait the stop before leaving the function. */

	if ( f_pBufferPlayoutStop->fStopCleanly == FALSE )
	{
		/* Remember that an "hard stop" was used for the next start. */
		if ( f_pBufferPlayoutStop->ulPlayoutPort == cOCT6100_CHANNEL_PORT_ROUT )
			pEchoChannel->fRinHardStop = TRUE;
		else /* if ( f_pBufferPlayoutStop->ulPlayoutPort == cOCT6100_CHANNEL_PORT_SOUT ) */
			pEchoChannel->fSoutHardStop = TRUE;
	}

	/*=======================================================================*/
	/* Update the channel entry to set the playing flag to FALSE. */

	/* Select the port of interest. */
	if ( f_pBufferPlayoutStop->ulPlayoutPort == cOCT6100_CHANNEL_PORT_ROUT )
	{
		/* Check if the global ports active stat must be decremented. */
		if ( pEchoChannel->fRinBufPlaying == TRUE )
		{
			/* Decrement the number of active buffer playout ports. */
			pSharedInfo->ChipStats.usNumberActiveBufPlayoutPorts--;
		}

		pEchoChannel->fRinBufPlaying = FALSE;

		/* Return user information. */
		if ( f_pBufferPlayoutStop->pfNotifyOnPlayoutStop != NULL )
			*f_pBufferPlayoutStop->pfNotifyOnPlayoutStop = pEchoChannel->fRinBufPlayoutNotifyOnStop;

		/* Make sure no new event is recorded for this channel/port. */
		pEchoChannel->fRinBufPlayoutNotifyOnStop = FALSE;
		if ( pSharedInfo->ImageInfo.fBufferPlayoutSkipInEvents == TRUE )
		{
			pEchoChannel->ulRinBufSkipPtr = ulSkipPtr;
			pEchoChannel->ulRinBufWritePtr = ulWritePtr;
		}
		else /* if ( pSharedInfo->ImageInfo.fBufferPlayoutSkipInEvents == FALSE ) */
			pEchoChannel->ulRinBufSkipPtr = pEchoChannel->ulRinBufWritePtr;

		/* The repeat flag can now be used. */
		pEchoChannel->fRinBufPlayoutRepeatUsed = FALSE;

		/* For sure, all buffers have now been cleared on the Rin port. */
		pEchoChannel->fRinBufAdded = FALSE;

		/* Clear optimization flag if possible. */
		if ( ( pEchoChannel->fSoutBufPlaying == FALSE )
			&& ( pEchoChannel->fSoutBufPlayoutNotifyOnStop == FALSE ) )
		{
			/* Buffer playout is no more active on this channel. */
			pEchoChannel->fBufPlayoutActive = FALSE;
		}
	}
	else /* f_pBufferPlayoutStop->ulPlayoutPort == cOCT6100_CHANNEL_PORT_SOUT */
	{
		/* Check if the global ports active stat must be decremented. */
		if ( pEchoChannel->fSoutBufPlaying == TRUE )
		{
			/* Decrement the number of active buffer playout ports. */
			pSharedInfo->ChipStats.usNumberActiveBufPlayoutPorts--;
		}

		pEchoChannel->fSoutBufPlaying = FALSE;

		/* Return user information. */
		if ( f_pBufferPlayoutStop->pfNotifyOnPlayoutStop != NULL )
			*f_pBufferPlayoutStop->pfNotifyOnPlayoutStop = pEchoChannel->fSoutBufPlayoutNotifyOnStop;

		/* Make sure no new event is recorded for this channel/port. */
		pEchoChannel->fSoutBufPlayoutNotifyOnStop = FALSE;
		if ( pSharedInfo->ImageInfo.fBufferPlayoutSkipInEvents == TRUE )
		{
			pEchoChannel->ulSoutBufSkipPtr = ulSkipPtr;
			pEchoChannel->ulSoutBufWritePtr = ulWritePtr;
		}
		else /* if ( pSharedInfo->ImageInfo.fBufferPlayoutSkipInEvents == FALSE ) */
			pEchoChannel->ulSoutBufSkipPtr = pEchoChannel->ulSoutBufWritePtr;

		/* The repeat flag can now be used. */
		pEchoChannel->fSoutBufPlayoutRepeatUsed = FALSE;

		/* For sure, all buffers have now been cleared on the Sout port. */
		pEchoChannel->fSoutBufAdded = FALSE;

		/* Clear optimization flag if possible. */
		if ( ( pEchoChannel->fRinBufPlaying == FALSE )
			&& ( pEchoChannel->fRinBufPlayoutNotifyOnStop == FALSE ) )
		{
			/* Buffer playout is no more active on this channel. */
			pEchoChannel->fBufPlayoutActive = FALSE;
		}
	}

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



	return cOCT6100_ERR_OK;
}


/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*\

Function:		Oct6100ApiReserveBufPlayoutListEntry

Description:    Reserves a free entry in the Buffer playout list.

-------------------------------------------------------------------------------
|	Argument		|	Description
-------------------------------------------------------------------------------
f_pApiInstance			Pointer to API instance. This memory is used to keep the
						present state of the chip and all its resources.

f_pulBufferIndex		List entry reserved.

\*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
UINT32 Oct6100ApiReserveBufPlayoutListEntry(
				IN OUT	tPOCT6100_INSTANCE_API			f_pApiInstance,
				OUT		PUINT32							f_pulBufferIndex )
{
	PVOID	pBufPlayoutAlloc;
	UINT32	ulResult;

	mOCT6100_GET_BUFFER_ALLOC_PNT( f_pApiInstance->pSharedInfo, pBufPlayoutAlloc )

	ulResult = OctapiLlmAllocAlloc( pBufPlayoutAlloc, f_pulBufferIndex );
	if ( ulResult != cOCT6100_ERR_OK )
	{
		if ( ulResult == OCTAPI_LLM_NO_STRUCTURES_LEFT )
			return cOCT6100_ERR_BUFFER_PLAYOUT_ALL_BUFFERS_OPEN;
		else
			return cOCT6100_ERR_FATAL_40;
	}

	return cOCT6100_ERR_OK;
}


/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*\

Function:		Oct6100ApiReleaseBufPlayoutListEntry

Description:    Release an entry from the Buffer playout list.

-------------------------------------------------------------------------------
|	Argument		|	Description
-------------------------------------------------------------------------------
f_pApiInstance			Pointer to API instance. This memory is used to keep the
						present state of the chip and all its resources.

f_ulBufferIndex			List entry to be freed.

\*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
UINT32 Oct6100ApiReleaseBufPlayoutListEntry(
				IN OUT	tPOCT6100_INSTANCE_API			f_pApiInstance,
				IN		UINT32							f_ulBufferIndex )
{
	PVOID	pBufPlayoutAlloc;
	UINT32	ulResult;

	mOCT6100_GET_BUFFER_ALLOC_PNT( f_pApiInstance->pSharedInfo, pBufPlayoutAlloc )

	ulResult = OctapiLlmAllocDealloc( pBufPlayoutAlloc, f_ulBufferIndex );
	if ( ulResult != cOCT6100_ERR_OK )
		return cOCT6100_ERR_FATAL_41;

	return cOCT6100_ERR_OK;
}

