/******************************************************************************
* @file    adsTelEpALSA.c
*
* Contents: This file contains the ALSA functions.
*
* @par
* INTEL CONFIDENTIAL
* Copyright 2009 Intel Corporation All Rights Reserved.
* 
* The source code contained or described herein and all documents related to the
* source code ("Material") are owned by Intel Corporation or its suppliers or
* licensors. Title to the Material remains with Intel Corporation or its
* suppliers and licensors. The Material may contain trade secrets and proprietary
* and confidential information of Intel Corporation and its suppliers and
* licensors, and is protected by worldwide copyright and trade secret laws and
* treaty provisions. No part of the Material may be used, copied, reproduced,
* modified, published, uploaded, posted, transmitted, distributed, or disclosed
* in any way without Intels prior express written permission.
* No license under any patent, copyright, trade secret or other intellectual
* property right is granted to or conferred upon you by disclosure or delivery
* of the Materials, either expressly, by implication, inducement, estoppel or
* otherwise. Any license under such intellectual property rights must be
* express and approved by Intel in writing.
* 
* Include any supplier copyright notices as supplier requires Intel to use.
* Include supplier trademarks or logos as supplier requires Intel to use,
* preceded by an asterisk.
* An asterisked footnote can be added as follows: 
*   *Third Party trademarks are the property of their respective owners.
* 
* Unless otherwise agreed by Intel in writing, you may not remove or alter this
* notice or any other notice embedded in Materials by Intel or Intels suppliers
* or licensors in any way.
* 
*  version: ADS.L.1.1.0-160
******************************************************************************/

#include <pthread.h>
#include <sys/time.h>
#include "adsTelEpALSA.h"
#include "adsTelEp.h"
#include "adsTelCommon.h"

extern unsigned int runThread;

/****************************************************************************/
/******************* GLOBAL DECLARATIONS ************************************/
/****************************************************************************/

/* handle for playback and capture streams */
static snd_pcm_t *pPlyHandle[MAX_PCM_LINES_SUPPORTED];
static snd_pcm_t *pCptHandle[MAX_PCM_LINES_SUPPORTED];

/*Structures for Hardware configs*/
static snd_pcm_hw_params_t *pPlyHwParams[MAX_PCM_LINES_SUPPORTED];
static snd_pcm_hw_params_t *pCptHwParams[MAX_PCM_LINES_SUPPORTED];

/*Structures for Software configs*/
static snd_pcm_sw_params_t *pPlySwParams[MAX_PCM_LINES_SUPPORTED];
static snd_pcm_sw_params_t *pCptSwParams[MAX_PCM_LINES_SUPPORTED];

/* pcm endpoint descriptor */
AdsTelPcmChan_t *pPcmEp[MAX_PCM_LINES_SUPPORTED];

/* mutex variables to control trigger thread */
pthread_mutex_t alsaMutexStopThread = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t alsaMutexReading = PTHREAD_MUTEX_INITIALIZER;

/* number of active pcm endpoints */
unsigned int numberActiveEndpoints = 0;

/*pcm endpoint handles*/
extern AdsTelEpHandle_t pcmEp[MAX_PCM_LINES_SUPPORTED];

/****************************************************************************/
/******************* FUNCTION DECLARATIONS **********************************/
/****************************************************************************/

static int adsTelALSASetHwParams(snd_pcm_t           *pHandle,
                                 snd_pcm_hw_params_t *pParams,
                                 snd_pcm_access_t    access,
                                 snd_pcm_format_t    format,
                                 unsigned int        rate,
                                 unsigned int        numHwChans);

static int adsTelALSASetSwParams(snd_pcm_t           *pHandle,
                                 snd_pcm_sw_params_t *pSwParams,
                                 snd_pcm_hw_params_t *pHwParams,
                                 unsigned int        rate);

/****************************************************************************/
/******************* FUNCTION DEFINITIONS ***********************************/
/****************************************************************************/

/*
 * Initialize any array and structures that are used
 */
int adsTelAlsaInit(void)
{
    int i = 0;
    AdsTelPcmChan_t *pPcmEpTemp = NULL;
    char *pPingPongTemp = NULL;

    /* allocate memory for ep structures */
    pPcmEpTemp = (AdsTelPcmChan_t *)malloc(MAX_PCM_LINES_SUPPORTED
                                    *sizeof(AdsTelPcmChan_t));
    if (pPcmEpTemp == NULL) 
    {
        printf("\nERROR:%s: Unable to allocate memory for ep\n", __FUNCTION__);
        return ADS_TEL_STATUS_FAIL;
    }
    
    /* allocate memory for ping-pong buffers*/
    pPingPongTemp = (char *)malloc(MAX_PCM_LINES_SUPPORTED *
                           sizeof(AdsTelPipoBuff_t) *
                           NUMBER_PINGPONG_BUFFERS);

    if (pPingPongTemp == NULL)
    {
        printf("\nERROR:%s: Unable to allocate pipo memory\n", __FUNCTION__);
        free(pPcmEpTemp);
        return ADS_TEL_STATUS_FAIL;
    }
        

    pPcmEp[0] = pPcmEpTemp;
    pPcmEp[0]->pReadPing = (AdsTelPipoBuff_t *) pPingPongTemp;

    for (i=0; i < MAX_PCM_LINES_SUPPORTED; i++)
    {
        pPlyHandle[i] = NULL;
        pCptHandle[i] = NULL;
        pPlyHwParams[i] = NULL;
        pCptHwParams[i] = NULL;
        pPlySwParams[i] = NULL;
        pCptSwParams[i] = NULL;
        pPcmEp[i] = pPcmEpTemp + i;
        pPcmEp[i]->pReadPing = (AdsTelPipoBuff_t *) (pPingPongTemp +
                                                (i* sizeof(AdsTelPipoBuff_t) *
                                                NUMBER_PINGPONG_BUFFERS ));
        pPcmEp[i]->pReadPong = (AdsTelPipoBuff_t *) (pPingPongTemp +
                                                (i* sizeof(AdsTelPipoBuff_t) *
                                                NUMBER_PINGPONG_BUFFERS )
                               + SECOND * sizeof(AdsTelPipoBuff_t));
        pPcmEp[i]->pWritePing = (AdsTelPipoBuff_t *) (pPingPongTemp +
                                                (i* sizeof(AdsTelPipoBuff_t) *
                                                NUMBER_PINGPONG_BUFFERS )
                               + THIRD * sizeof(AdsTelPipoBuff_t));
        pPcmEp[i]->pWritePong = (AdsTelPipoBuff_t *) (pPingPongTemp +
                                                (i* sizeof(AdsTelPipoBuff_t) *
                                                NUMBER_PINGPONG_BUFFERS )
                               + FOURTH * sizeof(AdsTelPipoBuff_t));
    }
    /* initialise mutexes used for start/stop of trigger thread */
    pthread_mutex_init(&alsaMutexStopThread, NULL);
    pthread_mutex_init(&alsaMutexReading, NULL);

    /*  stop trigger thread */
    adsTelALSAStopTrigger();

    return ADS_TEL_STATUS_SUCCESS;
}


/*
 * Open an ALSA hardware device interface
 */
int adsTelAlsaOpenChan(unsigned int chan, char *pDevice,
                        snd_pcm_format_t format, unsigned int rate)
{
    int err = 0;

    if (chan >= MAX_PCM_LINES_SUPPORTED)
    {
        printf("\nERROR:%s: Invalid channel\n", __FUNCTION__);
        return ADS_TEL_STATUS_FAIL;
    }

    /* Open in playback mode. */
    err = snd_pcm_open(&pPlyHandle[chan], pDevice,
                       SND_PCM_STREAM_PLAYBACK, BLOCKING);
    if (err  < 0)
    {
        printf("\nERROR:%s: Playback open error: %s\n",
                __FUNCTION__, snd_strerror(err));
        return ADS_TEL_STATUS_FAIL;
    }

    /* Open in capture mode. */
    err = snd_pcm_open(&pCptHandle[chan], pDevice,
                       SND_PCM_STREAM_CAPTURE, BLOCKING);
    if (err  < 0)
    {
        printf("\nERROR:%s: Capture open error: %s\n",
                __FUNCTION__, snd_strerror(err));
        return ADS_TEL_STATUS_FAIL;
    }

    /* Allocate a hardware parameters object for each stream */
    snd_pcm_hw_params_alloca(&pPlyHwParams[chan]);
    snd_pcm_hw_params_alloca(&pCptHwParams[chan]);

    /* Fill them in with default values. */
    snd_pcm_hw_params_any(pPlyHandle[chan], pPlyHwParams[chan]);
    snd_pcm_hw_params_any(pCptHandle[chan], pCptHwParams[chan]);

    /* Allocate the SW parameters */
    snd_pcm_sw_params_alloca(&pPlySwParams[chan]);
    snd_pcm_sw_params_alloca(&pCptSwParams[chan]);

    /* Set the hardware parameters for playback*/
    err = adsTelALSASetHwParams(pPlyHandle[chan], pPlyHwParams[chan],
                                ALSA_ACCESS_MODE, format, rate,
                                ALSA_NUM_HW_CHANS);
    if (err  !=  ADS_TEL_STATUS_SUCCESS)
    {
        printf("\nERROR:%s: Failed to set playback hardware paramters for"
                " chan %i\n", __FUNCTION__, chan);
        return ADS_TEL_STATUS_FAIL;
    }

    /* Set the hardware parameters for capture*/
    err = adsTelALSASetHwParams(pCptHandle[chan], pCptHwParams[chan],
                                ALSA_ACCESS_MODE, format, rate,
                                ALSA_NUM_HW_CHANS);
    if (err  !=  ADS_TEL_STATUS_SUCCESS)
    {
        printf("\nERROR:%s: Failed to set capture hardware paramters for"
                " chan %i\n", __FUNCTION__, chan);
        return ADS_TEL_STATUS_FAIL;
    }

    /* Set the software parameters for playback*/
    err = adsTelALSASetSwParams(pPlyHandle[chan], pPlySwParams[chan],
                                pPlyHwParams[chan], rate);
    if (err  !=  ADS_TEL_STATUS_SUCCESS)
    {
        printf("\nERROR:%s: Failed to set playback software paramters for"
                " chan %i\n", __FUNCTION__, chan);
        return ADS_TEL_STATUS_FAIL;
    }

    /* Set the software parameters for capture*/
    err = adsTelALSASetSwParams(pCptHandle[chan], pCptSwParams[chan],
                                pCptHwParams[chan], rate);
    if (err  !=  ADS_TEL_STATUS_SUCCESS)
    {
        printf("\nERROR:%s: Failed to set capture software paramters for"
                " chan %i\n", __FUNCTION__, chan);
        return ADS_TEL_STATUS_FAIL;
    }

    /*store the pcm endpoint configuration*/
    pPcmEp[chan]->freq = rate;
    pPcmEp[chan]->interval = ALSA_PKT_INTERVAL; /* msec */
    pPcmEp[chan]->pReadPing->expectedFrames = pPcmEp[chan]->freq *
                                       pPcmEp[chan]->interval
                                       / PCM_RATE_1K;
    pPcmEp[chan]->pReadPong->expectedFrames = pPcmEp[chan]->freq *
                                       pPcmEp[chan]->interval
                                       / PCM_RATE_1K;
    pPcmEp[chan]->pWritePing->expectedFrames = pPcmEp[chan]->freq *
                                       pPcmEp[chan]->interval
                                       / PCM_RATE_1K;
    pPcmEp[chan]->pWritePong->expectedFrames = pPcmEp[chan]->freq *
                                       pPcmEp[chan]->interval
                                       / PCM_RATE_1K;
    pPcmEp[chan]->pReadPing->actualFrames = 0;
    pPcmEp[chan]->pReadPong->actualFrames = 0;
    pPcmEp[chan]->pWritePing->actualFrames = 0;
    pPcmEp[chan]->pWritePong->actualFrames = 0;

    /* Prepare PCM for use */
    err = snd_pcm_prepare(pCptHandle[chan]);
    if (err < 0)
    {
        printf("\nERROR:%s: Prepare error for capture: %s\n", __FUNCTION__,
                snd_strerror(err));
        return ADS_TEL_STATUS_FAIL;
    }

    err = snd_pcm_prepare(pPlyHandle[chan]);
    if (err < 0)
    {
        printf("\nERROR:%s: Prepare error for playback: %s\n", __FUNCTION__,
                snd_strerror(err));
        return ADS_TEL_STATUS_FAIL;
    }

    pPcmEp[chan]->bConfigured = CONFIGURED;

    numberActiveEndpoints++;

    /* if this is the first pcm endpoint unblock trigger thread */
    if (numberActiveEndpoints == ONE_ACTIVE_CHAN)
    {
        adsTelALSAStartTrigger();
    }

    return ADS_TEL_STATUS_SUCCESS;

}


/*
 * Close an ALSA hardware device interface
 */
int adsTelALSACloseChan(unsigned int chan)
{

    int err = 0;

    /* block trigger thread */
    adsTelALSAStopTrigger();

    pPcmEp[chan]->bConfigured = UNCONFIGURED;

    /* Remove PCM hardware configuration and free associated resources */
    snd_pcm_hw_free(pPlyHandle[chan]);
    snd_pcm_hw_free(pCptHandle[chan]);

    err = snd_pcm_close(pPlyHandle[chan]);
    if (err < 0)
    {
        printf("\nERROR:%s: ALSA Playback close for chan %i: %s\n",
                __FUNCTION__, chan, snd_strerror(err));
        return ADS_TEL_STATUS_FAIL;
    }

    err = snd_pcm_close(pCptHandle[chan]);
    if (err < 0)
    {
        printf("\nERROR:%s: ALSA Capture close for chan %i: %s\n",
               __FUNCTION__, chan, snd_strerror(err));
        return ADS_TEL_STATUS_FAIL;
    }

    /*reset the pointers as the memory they point to has been freed */
    pPlyHandle[chan] = NULL;
    pCptHandle[chan] = NULL;
    pPlyHwParams[chan] = NULL;
    pCptHwParams[chan] = NULL;
    pPlySwParams[chan] = NULL;
    pCptSwParams[chan] = NULL;

    numberActiveEndpoints--;
    if (numberActiveEndpoints != 0)
    {
        adsTelALSAStartTrigger();

    }

    return ADS_TEL_STATUS_SUCCESS;
}

/*
 * Set the hardware parameters for an ALSA stream
 */
static int adsTelALSASetHwParams(snd_pcm_t           *pHandle,
                                 snd_pcm_hw_params_t *pParams,
                                 snd_pcm_access_t    access,
                                 snd_pcm_format_t    format,
                                 unsigned int        rate,
                                 unsigned int        numHwChans)
 {
    unsigned int rrate = 0; /* Real sampling rate */
    int err = 0;

    /* Fill them in with default values. */
    snd_pcm_hw_params_any(pHandle, pParams);

    /* Set access mode */
    err = snd_pcm_hw_params_set_access(pHandle, pParams, access);
    if (err < 0)
    {
        printf("\nERROR:%s: Access type not available: %s\n",
                __FUNCTION__, snd_strerror(err));
        return ADS_TEL_STATUS_FAIL;
    }

    /* Set sample format */
    err = snd_pcm_hw_params_set_format(pHandle, pParams, format);
    if (err < 0)
    {
        printf("\nERROR:%s: Sample format not available: %s\n",
                __FUNCTION__, snd_strerror(err));
        return ADS_TEL_STATUS_FAIL;
    }

    /* Set Number of channels for the stream */
    err = snd_pcm_hw_params_set_channels(pHandle, pParams, numHwChans);
    if (err < 0)
    {
        printf("\nERROR:%s: Channel count %i not available: %s\n",
                __FUNCTION__, numHwChans, snd_strerror(err));
        return ADS_TEL_STATUS_FAIL;
    }

    /* Set sampling rate in Hz*/
    rrate = rate;

    err = snd_pcm_hw_params_set_rate_near(pHandle,pParams,&rrate,TRY_EXACT);
    if (err < 0)
    {
        printf("\nERROR:%s: Rate %uHz not available: %s\n", __FUNCTION__,
                rate, snd_strerror(err));
        return ADS_TEL_STATUS_FAIL;
    }

    if (rrate != rate)
    {
        printf("\nERROR:%s: rate doesn't match (requested %uHz,"
               " got %uHz)\n", __FUNCTION__, rate, rrate);
        return ADS_TEL_STATUS_FAIL;
    }


    /* Write the parameters to the driver */
    err = snd_pcm_hw_params(pHandle, pParams);
    if (err < 0)
    {
        printf("\nERROR:%s: Unable to set hw params: %s\n", __FUNCTION__,
                snd_strerror(err));
        return ADS_TEL_STATUS_FAIL;
    }

    return ADS_TEL_STATUS_SUCCESS;

}

/*
 * Set the software parameters for an ALSA stream
 */
static int adsTelALSASetSwParams(snd_pcm_t           *pHandle,
                                 snd_pcm_sw_params_t *pSwParams,
                                 snd_pcm_hw_params_t *pHwParams,
                                 unsigned int        rate)
{
    unsigned int threshold = 0;
    snd_pcm_uframes_t minVal = 0;
    int err = 0;


    /* Calculate the number of packets to buffer to achieve the desired
     * buffering of PCM_BUFFER_LEN
     * Calculated by:
     * Num pkts = Rate(Hz) x Delay(ms)
     *            ------------------------
     *                    1000
     */
    threshold = (rate * PCM_BUFFER_LEN)/PCM_RATE_1K;


    /* Associate SW params with handle */
    err = snd_pcm_sw_params_current(pHandle, pSwParams);
    if (err < 0)
    {
        printf("\nERROR:%s: Unable to determine current sw params: %s\n",
                __FUNCTION__, snd_strerror(err));
        return ADS_TEL_STATUS_FAIL;
    }


    /* Set start threshold*/
    err = snd_pcm_sw_params_set_start_threshold(pHandle,
                                                pSwParams,
                                                threshold);
    if (err < 0)
    {
        printf("\nERROR:%s: Unable to set start threshold: %s\n",
                __FUNCTION__, snd_strerror(err));
        return ADS_TEL_STATUS_FAIL;
    }


    /* Get period size to use for available min */
    err = snd_pcm_hw_params_get_period_size(pHwParams, &minVal, NULL);
    if (err < 0)
    {
        printf("\nERROR:%s: Unable to get period size: %s\n",
                __FUNCTION__, snd_strerror(err));
        return ADS_TEL_STATUS_FAIL;
    }


    /* Set available min - for when ALSA notifies us that it is ready */
    err = snd_pcm_sw_params_set_avail_min(pHandle, pSwParams, minVal);
    if (err < 0)
    {
        printf("\nERROR:%s: Unable to set available min: %s\n",
                __FUNCTION__, snd_strerror(err));
        return ADS_TEL_STATUS_FAIL;
    }

    /* Write the SW params to the driver */
    err = snd_pcm_sw_params(pHandle, pSwParams);
    if (err < 0)
    {
        printf("\nERROR:%s: Unable to set sw params: %s\n", __FUNCTION__,
                snd_strerror(err));
        return ADS_TEL_STATUS_FAIL;
    }

    return ADS_TEL_STATUS_SUCCESS;
}

/*
 * ALSA trigger loop 
 */
void alsaTrigger(void)
{
    unsigned int frequency =  0;
    unsigned int interval = 0;
    long int expectedFramesRead = 0;
    long int actualFramesRead = 0;
    long int actualFramesWrote = 0;
    unsigned int chan = 0;
    struct timeval inboundTime;
    AdsTelPipoBuff_t  *pTemp = NULL;

    while(runThread)
    {

        /*ALSA is not reading */
        pthread_mutex_unlock(&alsaMutexReading);

        /* if this mutex is locked, the trigger thread blocks here */
        pthread_mutex_lock(&alsaMutexStopThread);
        pthread_mutex_unlock(&alsaMutexStopThread);

        /* alsa is reading */
        pthread_mutex_lock(&alsaMutexReading);

        /* loop through all pcm channels */
        for (chan=0; chan<MAX_PCM_LINES_SUPPORTED; chan++)
        {
            /* if channel not configured */
            if (pPcmEp[chan]->bConfigured == UNCONFIGURED)
            {
                continue;
            }

            frequency = pPcmEp[chan]->freq;
            interval = pPcmEp[chan]->interval; /* msec */
            expectedFramesRead = pPcmEp[chan]->pReadPing->expectedFrames;

            /* first we read */
            actualFramesRead = snd_pcm_readi(pCptHandle[chan],
                                         pPcmEp[chan]->pReadPing->Data,
                                         expectedFramesRead);

            gettimeofday(&inboundTime, NULL);
            pPcmEp[chan]->pReadPing->data1 = inboundTime.tv_sec;
            pPcmEp[chan]->pReadPing->data2 = inboundTime.tv_usec;

            /* if the read returns an error */
            if (actualFramesRead <0)
            {
                if ( actualFramesRead == -EPIPE )
                {
                    if ( snd_pcm_prepare(pCptHandle[chan]) < 0)
                    {
                        printf("\nERROR:%s: ALSA can't recover from overrun "
                               "on channel %u\n",
                               __FUNCTION__, chan);
                    }
                    else
                    {
                        printf("INFO:%s: ALSA recovered from overrun on "
                               "channel %u\n", __FUNCTION__, chan);
                    }

                }
                else if ( actualFramesRead == -EBADFD )
                {
                    printf("\nERROR:%s: ALSA can't recover as device is in a "
                           "bad state"
                           " for channel %u\n", __FUNCTION__, chan);
                }
                else if ( actualFramesRead == -ESTRPIPE )
                {
                    printf("\nERROR:%s: ALSA can't recover as device has "
                           "been suspended"
                           " for channel %u\n", __FUNCTION__, chan);
                }
                else if ( actualFramesRead == -ENOTTY ||
                     actualFramesRead == -ENODEV )
                {
                    printf("\nERROR:%s ALSA can't recover as device has "
                           "been removed"
                           " for channel %u\n", __FUNCTION__, chan);
                }
                else
                {
                    printf("\nERROR:%s: ALSA unknown read error %li.\n",
                           __FUNCTION__, actualFramesRead);
                }
            }

            if( (actualFramesRead >= 0) &&
                (actualFramesRead!=pPcmEp[chan]->pReadPing->expectedFrames))
            {
                printf("\nERROR:%s: ALSA unexpected read size of %li "
                       "for channel %i\n",
                       __FUNCTION__, actualFramesRead, chan);
            }

            /* set actual frames read */
            pPcmEp[chan]->pReadPing->actualFrames = actualFramesRead;

            /* swap ping and pong buffers */
            pTemp = pPcmEp[chan]->pReadPing;
            pPcmEp[chan]->pReadPing = pPcmEp[chan]->pReadPong;
            pPcmEp[chan]->pReadPong = pTemp;

            actualFramesWrote = pPcmEp[chan]->pWritePong->actualFrames;
            if (actualFramesWrote < 0) /* from last write */
            {
                if (actualFramesWrote == -EPIPE)
                {
                    if ( snd_pcm_prepare(pPlyHandle[chan]) < 0 )
                    {
                        printf("\nERROR:%s: ALSA can't recover from underrun "
                               "on channel"
                               " %u\n", __FUNCTION__, chan);
                    }
                    printf("INFO:%s: ALSA recovered from underrun on "
                           "channel %u\n", __FUNCTION__, chan);
                }
                else if (actualFramesWrote == -EBADFD)
                {
                    printf("\nERROR:%s: ALSA can't recover as device is in a "
                           "bad state"
                           " for channel %u\n", __FUNCTION__, chan);
                }
                else if ( actualFramesWrote == -ESTRPIPE )
                {
                    printf("\nERROR:%s: ALSA can't recover as device has been"
                           " suspended"
                           "for channel %u\n", __FUNCTION__, chan);
                }
                else if ( actualFramesWrote == -ENOTTY ||
                          actualFramesWrote == -ENODEV )
                {
                    printf("\nERROR:%s: ALSA can't recover as device has been"
                           " removed"
                           "for channel %u\n", __FUNCTION__, chan);
                }
            }

            /* then we write */
            actualFramesWrote = snd_pcm_writei(pPlyHandle[chan],
                                     pPcmEp[chan]->pWritePing->Data,
                                     pPcmEp[chan]->pWritePing->expectedFrames);
            if ( (actualFramesWrote >= 0) &&
                 (actualFramesWrote!=pPcmEp[chan]->pWritePing->expectedFrames))
            {
                printf("\nERROR:%s: ALSA Unexpected write size of %li on "
                       "channel %u\n",
                       __FUNCTION__,  actualFramesWrote, chan);
            }

            /* set actual frames wrote */
            pPcmEp[chan]->pWritePing->actualFrames = actualFramesWrote;

            /* swap ping and pong buffers */
            pTemp = pPcmEp[chan]->pWritePing;
            memset((char*)pTemp->Data, 0, MAX_PCM_PAYLOAD_BYTES);
            pPcmEp[chan]->pWritePing = pPcmEp[chan]->pWritePong;
            pPcmEp[chan]->pWritePong = pTemp;


        } /* for chan loop */

        /* trigger the framework */
        adsTelFwTrigger();

    } /* while(runThread) */


}

/*
 * ADS inbound callback function  
 */
AdsTelStatus_t ads_inbound_cb(AdsTelEpHandle_t epHandle, void *pBuffer1)
{
    AdsTelStatus_t rc = ADS_TEL_STATUS_SUCCESS;
    AdsTelPCMPacket_t *pPacket = pBuffer1;
    unsigned int chan = 0;

    /* find ALSA channel number using epHandle */
    for (chan=0; chan<MAX_PCM_LINES_SUPPORTED; chan++)
    {
        if (pcmEp[chan] == epHandle)
        {
            break;
        }
    }

    if (chan == MAX_PCM_LINES_SUPPORTED)
    {
        printf("INFO:%s: No ALSA channel match found for epHandle: %u\n",
                __FUNCTION__, epHandle);
        return ADS_TEL_STATUS_FAIL;
    }

    /* if alsa read was successful */
    if(pPcmEp[chan]->pReadPong->actualFrames==
            pPcmEp[chan]->pReadPong->expectedFrames)
    {
        /* copy buffer to framework */
        memcpy(pPacket->payload,
               pPcmEp[chan]->pReadPong->Data,
               MAX_PCM_PAYLOAD_BYTES);

        pPacket->privateData1=pPcmEp[chan]->pReadPong->data1;
        pPacket->privateData2=pPcmEp[chan]->pReadPong->data2;
        rc = ADS_TEL_STATUS_SUCCESS;
    }
    else
    {
        rc = ADS_TEL_STATUS_FAIL;
    }

    return rc;


}

/*
 * ADS outbound callback 
 */
AdsTelStatus_t ads_outbound_cb(AdsTelEpHandle_t epHandle, void *pBuffer1)
{
    AdsTelPCMPacket_t *pPacket=pBuffer1;
    unsigned int chan = 0;

    /* find ALSA channel number using epHandle */
    for (chan=0; chan<MAX_PCM_LINES_SUPPORTED; chan++)
    {
        if (pcmEp[chan] == epHandle)
        {
            break;
        }
    }

    if (chan == MAX_PCM_LINES_SUPPORTED)
    {
        printf("INFO:%s: No ALSA channel match found for epHandle: %u\n",
                __FUNCTION__, epHandle);
        return ADS_TEL_STATUS_FAIL;
    }

    /* copy buffer from framework */
    memcpy(pPcmEp[chan]->pWritePong->Data,
           pPacket->payload,
           MAX_PCM_PAYLOAD_BYTES);

    pPcmEp[chan]->pWritePong->data1=pPacket->privateData1;
    pPcmEp[chan]->pWritePong->data2=pPacket->privateData2;

    return ADS_TEL_STATUS_SUCCESS;
}

/*
 * Starts the ALSA trigger function 
 */
void adsTelALSAStartTrigger(void)
{
    /* this will start trigger function */
    pthread_mutex_unlock(&alsaMutexStopThread);

    /* unlock as it is safe to read/write */
    pthread_mutex_unlock(&alsaMutexReading);

}

/*
 * Stops the ALSA trigger function 
 */
void adsTelALSAStopTrigger(void)
{
    /* this will stop trigger function on next tick */
    pthread_mutex_lock(&alsaMutexStopThread);

    /* lock, no read/write */
    pthread_mutex_lock(&alsaMutexReading);

}

