/****************************************************************************
* @file    adsTelFwCb.c
*
* Contents: This file contains the framework callback and framework
*           trigger code.
*
* @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
******************************************************************************/

/****************************************************************************/
/******************* INCLUDES ***********************************************/
/****************************************************************************/

#include <signal.h>
#include <time.h>
#include <sched.h>
#include <pthread.h>

#include "adsTel.h"
#include "adsTelCommon.h"


/****************************************************************************/
/******************* DEFINES ************************************************/
/****************************************************************************/

#define SIG_ADS_TICK            SIGRTMAX

/*priority of the ADS timing thread*/
#define PRIO_TIMING_ADS_TASK    95

/*10ms timer setting*/
#define TEN_MSEC                10000000


/****************************************************************************/
/******************* GLOBALS ************************************************/
/****************************************************************************/

/*ADS Trigger thread ID*/
pthread_t ptrTid;

/*ADS Trigger thread attributes*/
pthread_attr_t threadAttr;

/*pthread signal mask*/
static sigset_t sig_mask;

/*timer thread condition tracking*/
unsigned int runThread = 0;


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

/*handler for ADS_TEL_STATUS_GET_PARAMS return status*/
static void frameworkGetParamsHandler(unsigned int transId, void* pData);

/*handler for ADS_TEL_STATUS_TG_CMPLT return status*/
static void frameworkTgCmpltHandler(unsigned int transId, void* pData);

/*handler for ADS_TEL_STATUS_TD_RCV_CMPLT return status*/
static void frameworkTdRcvCmpltHandler(unsigned int transId, void* pData);

/*handler for ADS_TEL_STATUS_TD_RCV_FSK_CMPLT return status*/
static void frameworkTdRcvFskCmpltHandler(unsigned int transId, void* pData);

/*handler for ADS_TEL_STATUS_PLY_CMPLT return status*/
static void frameworkPlyCmpltHandler(unsigned int transId, void* pData);

/*handler for ADS_TEL_STATUS_EVENT return status*/
static void frameworkEventHandler(unsigned int transId, void* pData);

/*handler for payload changed event from the decoder*/
void frameworkPayChange(void* pData);


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

/********************************************************************
 *start the framework timing thread
 ********************************************************************/
AdsTelStatus_t adsTriggerTaskStart(void *triggerFunc, unsigned int rttPriority)
{
    AdsTelStatus_t status = ADS_TEL_STATUS_SUCCESS;
    struct sched_param param;

    if (triggerFunc == NULL)
    {
        printf("\nERROR:%s: no timing function defined\n", __FUNCTION__);
        return ADS_TEL_STATUS_FAIL;
    }

    /*check the priority of timing task is greater than RTT */
    if (PRIO_TIMING_ADS_TASK <= rttPriority)
    {
        printf("\nERROR:%s: Timing thread priority must be greater than"
               " RTTs priority\n", __FUNCTION__);
        return ADS_TEL_STATUS_FAIL;
    }

    /* Set the signal mask*/
    status = sigemptyset(&sig_mask);
    if (status != ADS_TEL_STATUS_SUCCESS)
    {
        printf("\nERROR:%s: sigemptyset failed: %i\n", __FUNCTION__, status);
        return ADS_TEL_STATUS_FAIL;
    }

    status = sigaddset(&sig_mask,SIG_ADS_TICK);
    if (status != ADS_TEL_STATUS_SUCCESS)
    {
        printf("\nERROR:%s: sigaddset failed: %i\n", __FUNCTION__, status);
        return ADS_TEL_STATUS_FAIL;
    }

    status = pthread_sigmask(SIG_BLOCK, &sig_mask, NULL);
    if (status != ADS_TEL_STATUS_SUCCESS)
    {
        printf("\nERROR:%s: failed to set pthread sig mask: %i\n",
               __FUNCTION__, status);
        return ADS_TEL_STATUS_FAIL;
    }

    /*reset the thread exit condition*/
    runThread = 1;

    /* create timing task to trigger the ADS framework*/
    status = pthread_attr_init(&threadAttr);
    if (status != ADS_TEL_STATUS_SUCCESS)
    {
        printf("\nERROR:%s: Failed to init attributes for timing thread: %i\n",
               __FUNCTION__, status);
        return ADS_TEL_STATUS_FAIL;
    }

    status = pthread_attr_setschedpolicy(&threadAttr, SCHED_RR);
    if (status != ADS_TEL_STATUS_SUCCESS)
    {
        printf("\nERROR:%s: Failed to set schedule policy for timing"
               " thread: %i\n", __FUNCTION__, status);
        return ADS_TEL_STATUS_FAIL;
    }

    /*Set the detachable state of the thread*/
    status = pthread_attr_setdetachstate(&threadAttr,
                                         PTHREAD_CREATE_JOINABLE);
    if ( status != ADS_TEL_STATUS_SUCCESS)
    {
        printf("\nERROR:%s: Failed to set detatch state for timing thread:"
               " %i\n", __FUNCTION__, status);
        return ADS_TEL_STATUS_FAIL;
    }

    /*Set the thread priority*/
    param.sched_priority = PRIO_TIMING_ADS_TASK;

    /* set the new scheduling param */
    status = pthread_attr_setschedparam (&threadAttr, &param);

    /*Create the thread*/
    status = pthread_create(&ptrTid, &threadAttr,
                             triggerFunc, NULL);
    if ( status != ADS_TEL_STATUS_SUCCESS)
    {
        printf("\nERROR:%s: Failed to start timing thread: %i\n", __FUNCTION__,
                status);
        return ADS_TEL_STATUS_FAIL;
    }

    return ADS_TEL_STATUS_SUCCESS;
}


/********************************************************************
 *stop the framework timing thread
 ********************************************************************/
void adsTriggerTaskStop(void)
{

    /*set the thread stop condition*/
    runThread = 0;

    /*wait for the thread to be stopped*/
    pthread_join(ptrTid, NULL);

    return;
}


/********************************************************************
 * Function to trigger the framework every 10ms
 ********************************************************************/
void unblockEvery10ms(void)
{
    AdsTelStatus_t status = ADS_TEL_STATUS_SUCCESS;
    struct itimerspec ADStimer;
    struct sigevent event;
    timer_t timerid;
    int sig=0;

    /* Set timer for timer_create */
    ADStimer.it_interval.tv_sec=0;
    ADStimer.it_interval.tv_nsec= TEN_MSEC;
    ADStimer.it_value.tv_sec=0;
    ADStimer.it_value.tv_nsec= TEN_MSEC;

    /* Timer set-up */
    event.sigev_value.sival_ptr=NULL;
    event.sigev_signo=SIG_ADS_TICK;
    event.sigev_notify=0;

    status = timer_create( CLOCK_REALTIME,&event,&timerid);
    if (status != ADS_TEL_STATUS_SUCCESS)
    {
        printf("\nERROR:%s: Problem creating POSIX real-time timer: %i\n",
                __FUNCTION__, status);
        return;
    }

    status = timer_settime(timerid,0 , &ADStimer, NULL );
    if (status != ADS_TEL_STATUS_SUCCESS)
    {
        printf("\nERROR:%s: Problem setting POSIX real-time timer: %i\n",
                __FUNCTION__, status);

        /* delete timer */
        status = timer_delete(timerid);
        if (status != ADS_TEL_STATUS_SUCCESS)
        {
            printf("\nERROR:%s: Problem deleting POSIX real-time timer: %i\n",
                __FUNCTION__, status);
        }


        return;
    }

    /* Block on the signal for 10ms and then trigger the framework*/
    while (runThread == 1)
    {

        /*wait on  SIG_ADS_TICK*/
        status = sigwait(&sig_mask,&sig);
        if (status != ADS_TEL_STATUS_SUCCESS)
        {
            printf("\nERROR:%s: sigwait failed: %i\n", __FUNCTION__, status);
        }

        /*trigger the framework*/
        adsTelFwTrigger();

    }

    /*destroy the timer*/
    status = timer_delete(timerid);
    if (status != ADS_TEL_STATUS_SUCCESS)
    {
        printf("\nERROR:%s: Problem deleting POSIX real-time timer: %i\n",
                __FUNCTION__, status);
        return;
    }

}


/********************************************************************
 *callback function that is called by the framework
 ********************************************************************/
void frameworkCb(unsigned int transId, void* pData)
{
    AdsTelRetAck_t *pBuf = (AdsTelRetAck_t*)pData;

    /*Verify that pData is not Null*/
    if (pData == NULL)
    {
        printf("\nERROR:%s: pData is a NULL pointer\n", __FUNCTION__);
        return;
    }

    switch(pBuf->retStatus)
    {
        case ADS_TEL_STATUS_SUCCESS:
            break;

        case ADS_TEL_STATUS_FAIL:
            printf("\nERROR:%s: Trans ID %u returned ADS_TEL_STATUS_FAIL\n",
                    __FUNCTION__, transId);
            break;

        case ADS_TEL_STATUS_INVALID_PARAM:
            printf("\nERROR:%s: Trans ID %u returned"
                   " ADS_TEL_STATUS_INVALID_PARAM\n", __FUNCTION__, transId);
            break;

        case ADS_TEL_STATUS_GET_PARAMS:
            frameworkGetParamsHandler(transId, pData);
            break;

        case ADS_TEL_STATUS_TG_CMPLT:
            frameworkTgCmpltHandler(transId, pData);
            break;

        case ADS_TEL_STATUS_TD_RCV_CMPLT:
            frameworkTdRcvCmpltHandler(transId, pData);
            break;

        case ADS_TEL_STATUS_TD_RCV_FSK_CMPLT:
            frameworkTdRcvFskCmpltHandler(transId, pData);
            break;

        case ADS_TEL_STATUS_PLY_CMPLT:
            frameworkPlyCmpltHandler(transId, pData);
            break;

        case ADS_TEL_STATUS_EVENT:
            frameworkEventHandler(transId, pData);
            break;

        default:
            printf("\nERROR:%s: Unknown Status %i returned for transId:%u\n",
                    __FUNCTION__, pBuf->retStatus, transId);
            break;
    }

    return;
}


/********************************************************************
 *handler for ADS_TEL_STATUS_GET_PARAMS return status
 ********************************************************************/
static void frameworkGetParamsHandler(unsigned int transId, void* pData)
{
    AdsTelRetGetParams_t* pBuf = pData;
    unsigned int i = 0;

    printf("\nFW Callback - ADS_TEL_STATUS_GET_PARAMS - Trans ID: %u\n",
           transId);
    for (i=0; i<pBuf->numParams; i++)
    {
        printf("Paramter[%u] = %d\n", i, pBuf->paramVals[i]);
    }

    return;
}


/********************************************************************
 *handler for ADS_TEL_STATUS_TG_CMPLT return status
 ********************************************************************/
static void frameworkTgCmpltHandler(unsigned int transId, void* pData)
{

    return;
}


/********************************************************************
 *handler for ADS_TEL_STATUS_TD_RCV_CMPLT return status
 ********************************************************************/
static void frameworkTdRcvCmpltHandler(unsigned int transId, void* pData)
{

    AdsTelRetTd_t* pRetBuf = pData;
    unsigned int i = 0;

    printf("\nFW Callback - ADS_TEL_STATUS_TD_RCV_CMPLT - Trans ID: %u\n",
           transId);
    printf("FW Callback - Reason for Completion: %u\n",
           pRetBuf->reason);
    printf("FW Callback - Number of tones received: %u\n",
           pRetBuf->numTones);

    printf("Tone IDs:\n");
    for (i=0; i<pRetBuf->numTones; i++)
    {
        printf("%u, " ,pRetBuf->tones[i]);
    }
    printf("\n");

    return;
}


/********************************************************************
 *handler for ADS_TEL_STATUS_TD_RCV_FSK_CMPLT return status
 ********************************************************************/
static void frameworkTdRcvFskCmpltHandler(unsigned int transId, void* pData)
{
    AdsTelRetTdFsk_t* pRetBuf = pData;
    unsigned int i = 0;
    printf("\nFW Callback - ADS_TEL_STATUS_TD_RCV_FSK_CMPLT - Trans ID: %u\n",
           transId);
    printf("FW Callback - Reason for Completion: %u\n",
           pRetBuf->reason);
    printf("FSK ASCII string:\n");
    for (i=0; i<pRetBuf->numBytes; i++)
    {
        printf("%c",pRetBuf->data[i]);
    }
    printf("\n");
    return;
}


/********************************************************************
 *handler for ADS_TEL_STATUS_PLY_CMPLT return status
 ********************************************************************/
static void frameworkPlyCmpltHandler(unsigned int transId, void* pData)
{

    return;
}

/********************************************
 * handler for payload changed event from the decoder
 ********************************************/
void frameworkPayChange(void* pData)
{
    AdsTelRetEv_t* pBuf = pData;

    convertEpToRtp(pBuf->epHandle, NULL, &(pBuf->data1));
    if ((pBuf->data1 >= ADS_TEL_PARAM_CODER_TYPE_G722_64) && 
    (pBuf->data1 <= ADS_TEL_PARAM_CODER_TYPE_G722_48))
    {
        printf("\nCoderID= %d\tCodec is Wideband\n", pBuf->data1);
    }
    else
    {
        printf("\nCoderID= %d\tCodec is NB\n", pBuf->data1);
    }
    return;
}
/********************************************************************
 *handler for ADS_TEL_STATUS_EVENT return status
 ********************************************************************/
static void frameworkEventHandler(unsigned int transId, void* pData)
{
    AdsTelRetEv_t* pBuf = pData;
    AdsTelStatus_t status = ADS_TEL_STATUS_SUCCESS;
    unsigned int endpointId = 0;

    printf("\nFW Callback - ADS_TEL_STATUS_EVENT\n");

    /*convert the epHandle to a local endpoint ID*/
    status = epHandleToEpIdConvert(pBuf->epHandle, &endpointId);

    if (status != ADS_TEL_STATUS_SUCCESS)
    {
        printf("\nERROR:%s: Returned EP handle do not match any local"
                " endpoint\n", __FUNCTION__);
        return;
    }

    printf("\nEndpoint: %u PipeDir: %u CompId %u\n",
            endpointId, pBuf->pipeDir, pBuf->compId);

    /*print the event type*/
    switch(pBuf->code)
    {
        case ADS_TEL_EV_TD_TONE_ON:
            printf("\nTONE ON EVENT\n");
            break;
        case ADS_TEL_EV_TD_TONE_OFF:
            printf("\nTONE OFF EVENT\n");
            break;
        case ADS_TEL_EV_LOST_PACKET:
            printf("\nLOST PACKET EVENT\n");
            break;
        case ADS_TEL_EV_DEC_PACKET_CHNG:
            frameworkPayChange(pBuf);
            break;
        default:
            printf("\nERROR:%s: Invalid Event reported: %u", __FUNCTION__,
                    pBuf->code);
    }

    /*print the event data*/
    printf(" | data1 = %u | data2 = %u\n", pBuf->data1, pBuf->data2);
    return;
}

