/*
 * Copyright 2010 Trident Microsystems (Far East) Ltd. All rights reserved.
 * All prior copyright rights in this work and the accompanying software
 * products transferred to Trident Microsystems (Far East) Ltd. by written
 * agreement.
 */
/*****************************************************************************/
/* Copyright (c) 2010 NXP B.V.  All rights are reserved.                     */
/*                                                                           */
/* Redistribution and use in source and binary forms, with or without        */
/* modification, are permitted provided that the following conditions        */
/* are met:                                                                  */
/*                                                                           */
/* Redistributions of source code must retain the above copyright            */
/* notice, this list of conditions and the following disclaimer.             */
/*                                                                           */
/* Redistributions in binary form must reproduce the above copyright         */
/* notice, this list of conditions and the following disclaimer in the       */
/* documentation and/or other materials provided with the distribution.      */
/*                                                                           */
/* Neither the name of NXP nor the names of its                              */
/* contributors may be used to endorse or promote products derived from      */
/* this software without specific prior written permission.                  */
/*                                                                           */
/* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS       */
/* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT         */
/* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR     */
/* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT      */
/* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,     */
/* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT          */
/* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,     */
/* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY     */
/* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT       */
/* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE         */
/* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH          */
/* DAMAGE.                                                                   */
/*                                                                           */
/*****************************************************************************/
/**
    @file   sem.c
    @brief  STB225 Demo Application semaphore code

@b  Application:  exStbDemo

*//*

Rev Date        Author      Comments
--------------------------------------------------------------------------------
  1 20061006    neill       Ported from STB225
  2 20071003    neill       Fix issues found using QAC
  3 20071023    neill       More QAC fixing
  4 20080116    laird       Use phStbDbg. And Fix PR785# Sem release import from STB810
  5 20080206    batelaan    Update file copyright/license text
  6 20080414    neill       Add events
  7 20090115    neill       QAC improvements
--------------------------------------------------------------------------------
    For consistency and standardisation retain the Section Separators.
*/

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


#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <assert.h>
#include <pthread.h>

#include <sys/time.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>

#include <fcntl.h>

#include "sem.h"

#define PH_STB_DBG_ENABLED (0)
#include <phStbDbg.h>

/***********************************************
 LOCAL MACROS                                  *
************************************************/

/** Debug unit for this file, allows fine control of demo application debug.*/
DBG_UNIT(gMediaDemux_Sem_DbgUnit)
#define MY_DBG_UNIT gMediaDemux_Sem_DbgUnit

/******************************************************************
* LOCAL TYPEDEFS                                                  *
*******************************************************************/

/******************************************************************
* STATIC FUNCTION PROTOTYPES                  <Module>_<Word>+    *
*******************************************************************/

/******************************************************************
* STATIC DATA                  g[k|p|kp|pk|kpk]<Module>_<Word>+   *
*******************************************************************/

/**************************************************************************
* EXPORTED DATA      g[k|p|kp|pk|kpk]ph[<lnx|tm|NONE>]StbTemplate_<Word>+ *
***************************************************************************/

/*******************************************************************************
* FUNCTION IMPLEMENTATION  <Module>[_<Word>+] for static functions             *
*                          tm[<layer>]<Module>[_<Word>+] for exported functions*
********************************************************************************/

/********************************************************************************/
int sem_get(psem_t semaphore)
{
    int rc;

    rc = pthread_mutex_lock(&(semaphore->mutex));
    if (rc != 0)
    {
        DBG_PRINT2(MY_DBG_UNIT, DBG_WARNING, "%s ERROR: pthread_mutex_lock => %d!\n", __FUNCTION__, rc);
        return rc;
    }

    if (semaphore->semCount == 0)
    {
        DBG_PRINT1(MY_DBG_UNIT, DBG_LEVEL_1, "%s semCount==0, so waiting!\n", __FUNCTION__);
        do
        {
            rc = pthread_cond_wait(&(semaphore->condition), &(semaphore->mutex));
            if ((rc!=0) && (errno != EINTR))
            {
                DBG_PRINT2(MY_DBG_UNIT, DBG_WARNING, "%s ERROR: pthread_cond_wait => %d!\n", __FUNCTION__, rc);
                (void)pthread_mutex_unlock(&(semaphore->mutex));
                return rc;
            }
        } while (semaphore->semCount == 0);
        DBG_PRINT1(MY_DBG_UNIT, DBG_LEVEL_1, "%s semCount now != 0, waiting finished\n", __FUNCTION__);
    }

    semaphore->semCount = 0;

    rc = pthread_mutex_unlock(&(semaphore->mutex));
    if (rc != 0)
    {
        DBG_PRINT2(MY_DBG_UNIT, DBG_WARNING, "%s ERROR: (void)pthread_mutex_unlock => %d!\n", __FUNCTION__, rc);
        return rc;
    }

    return 0;
}

/********************************************************************************/
int sem_release(psem_t semaphore)
{
    int rc;

    rc = pthread_mutex_lock(&(semaphore->mutex));
    if (rc != 0)
    {
        DBG_PRINT2(MY_DBG_UNIT, DBG_WARNING, "%s ERROR: pthread_mutex_lock => %d!\n", __FUNCTION__, rc);
        return rc;
    }


    if (semaphore->semCount == 0)
    {
        semaphore->semCount = 1;
        if ((rc = pthread_cond_signal(&(semaphore->condition)))!=0)
        {
            DBG_PRINT2(MY_DBG_UNIT, DBG_WARNING, "%s ERROR: pthread_cond_signal => %d!\n", __FUNCTION__, rc);
            (void)pthread_mutex_unlock(&(semaphore->mutex));
            return rc;
        }
    }

    rc = pthread_mutex_unlock(&(semaphore->mutex));
    if (rc != 0)
    {
        DBG_PRINT2(MY_DBG_UNIT, DBG_WARNING, "%s ERROR: (void)pthread_mutex_unlock => %d!\n", __FUNCTION__, rc);
        return rc;
    }

    return 0;
}

/********************************************************************************/
int sem_create(psem_t* semaphore)
{
    int rc;
    psem_t thisSemaphore;
    thisSemaphore = (psem_t) malloc(sizeof(sem_t));

    rc = pthread_mutex_init(&(thisSemaphore->mutex), NULL);
    if(rc!=0)
    {
        DBG_PRINT2(MY_DBG_UNIT, DBG_WARNING, "%s ERROR: pthread_mutex_init => %d!\n", __FUNCTION__, rc);
        free(thisSemaphore);
        return rc;
    }

    rc = pthread_cond_init(&(thisSemaphore->condition), NULL);
    if(rc!=0)
    {
        DBG_PRINT2(MY_DBG_UNIT, DBG_WARNING, "%s ERROR: pthread_cond_init => %d!\n", __FUNCTION__, rc);
        (void)pthread_mutex_destroy( &(thisSemaphore->mutex) );
        free(thisSemaphore);
        return rc;
    }

    thisSemaphore->semCount = 1;
    *semaphore = thisSemaphore;
    return 0;
}

/********************************************************************************/
int sem_destroy(psem_t semaphore)
{
    (void)pthread_mutex_destroy(&(semaphore->mutex));
    (void)pthread_cond_destroy(&(semaphore->condition));
    free(semaphore);
    return 0;
}

int event_wait(psem_t semaphore)
{
    int rc;
    unsigned int locked = 0;

    DBG_PRINT1(MY_DBG_UNIT, DBG_LEVEL_1, "%s event wait\n", __FUNCTION__);
    if (pthread_mutex_trylock( &semaphore->mutex ) == 0)
    {
        /* Check for a pending event */
        if (semaphore->semCount == 1)
        {
            /* Clear the event */
            semaphore->semCount = 0;
            (void)pthread_mutex_unlock ( &semaphore->mutex );
            return 0;
        }
        locked = 1;
    }

    if (!locked)
    {
        rc = pthread_mutex_lock(&(semaphore->mutex));
        if (rc != 0)
        {
            DBG_PRINT2(MY_DBG_UNIT, DBG_WARNING, "%s ERROR: pthread_mutex_lock => %d!\n", __FUNCTION__, rc);
            return rc;
        }
    }

    if (semaphore->semCount == 0)
    {
        DBG_PRINT1(MY_DBG_UNIT, DBG_LEVEL_1, "%s semCount==0, so waiting!\n", __FUNCTION__);

        /* Wait for the event to occur */
        rc = pthread_cond_wait( &semaphore->condition,
                                &semaphore->mutex);

        DBG_PRINT1(MY_DBG_UNIT, DBG_LEVEL_1, "%s semCount==0, Got event\n", __FUNCTION__);
    }

    /* Clear the event */
    semaphore->semCount = 0;

    rc = pthread_mutex_unlock(&(semaphore->mutex));
    if (rc != 0)
    {
        DBG_PRINT2(MY_DBG_UNIT, DBG_WARNING, "%s ERROR: (void)pthread_mutex_unlock => %d!\n", __FUNCTION__, rc);
        return rc;
    }

    return 0;
}

/********************************************************************************/
int event_waitWithTimeout(psem_t semaphore, unsigned int seconds, unsigned int milli_seconds )
{
    int rc;
    struct timeval  now;
    struct timespec timeout;
    int  nano_seconds = (int)milli_seconds * 1000000;
    unsigned int locked = 0;

    DBG_PRINT1(MY_DBG_UNIT, DBG_LEVEL_1, "%s event wait with timeout\n", __FUNCTION__);
    if (pthread_mutex_trylock( &semaphore->mutex ) == 0)
    {
        DBG_PRINT1(MY_DBG_UNIT, DBG_LEVEL_1, "%s not locked\n", __FUNCTION__);
        /* Check for a pending event */
        if (semaphore->semCount == 1)
        {
            DBG_PRINT1(MY_DBG_UNIT, DBG_LEVEL_1, "%s got event\n", __FUNCTION__);
            /* Clear the event */
            semaphore->semCount = 0;
            (void)pthread_mutex_unlock ( &semaphore->mutex );
            return 0;
        }
        locked = 1;
    }

    /* Set up the timeout */
    (void)gettimeofday( &now, NULL );

    timeout.tv_sec  = (time_t)now.tv_sec + (time_t)seconds;
    timeout.tv_nsec = (now.tv_usec * 1000) + nano_seconds;

    timeout.tv_sec  += timeout.tv_nsec / 1000000000;
    timeout.tv_nsec %= 1000000000;

    if (!locked)
    {
        rc = pthread_mutex_lock(&(semaphore->mutex));
        if (rc != 0)
        {
            DBG_PRINT2(MY_DBG_UNIT, DBG_WARNING, "%s ERROR: pthread_mutex_lock => %d!\n", __FUNCTION__, rc);
            return rc;
        }
    }

    if (semaphore->semCount == 0)
    {
        DBG_PRINT1(MY_DBG_UNIT, DBG_LEVEL_1, "%s semCount==0, so waiting!\n", __FUNCTION__);

        /* Wait for the event to occur */
        rc = pthread_cond_timedwait( &semaphore->condition,
                                     &semaphore->mutex,
                                     &timeout );

        if (rc == ETIMEDOUT)
        {
            DBG_PRINT1(MY_DBG_UNIT, DBG_LEVEL_1, "%s semCount==0, timedout!\n", __FUNCTION__);
            (void)pthread_mutex_unlock(&(semaphore->mutex));
            return rc;
        }
        DBG_PRINT1(MY_DBG_UNIT, DBG_LEVEL_1, "%s semCount==0, Got event\n", __FUNCTION__);
    }

    /* Clear the event */
    semaphore->semCount = 0;

    rc = pthread_mutex_unlock(&(semaphore->mutex));
    if (rc != 0)
    {
        DBG_PRINT2(MY_DBG_UNIT, DBG_WARNING, "%s ERROR: (void)pthread_mutex_unlock => %d!\n", __FUNCTION__, rc);
        return rc;
    }

    return 0;
}

/********************************************************************************/
int event_send(psem_t semaphore)
{
    int rc;

    rc = pthread_mutex_lock(&(semaphore->mutex));
    if (rc != 0)
    {
        DBG_PRINT2(MY_DBG_UNIT, DBG_WARNING, "%s ERROR: pthread_mutex_lock => %d!\n", __FUNCTION__, rc);
        return rc;
    }

    semaphore->semCount = 1;
    if ((rc = pthread_cond_signal(&(semaphore->condition)))!=0)
    {
        DBG_PRINT2(MY_DBG_UNIT, DBG_WARNING, "%s ERROR: pthread_cond_signal => %d!\n", __FUNCTION__, rc);
        (void)pthread_mutex_unlock(&(semaphore->mutex));
        return rc;
    }

    rc = pthread_mutex_unlock(&(semaphore->mutex));
    if (rc != 0)
    {
        DBG_PRINT2(MY_DBG_UNIT, DBG_WARNING, "%s ERROR: (void)pthread_mutex_unlock => %d!\n", __FUNCTION__, rc);
        return rc;
    }

    return 0;
}

/********************************************************************************/
int event_create(psem_t* semaphore)
{
    int retValue;

    retValue = sem_create(semaphore);

    if (retValue == 0)
    {
        (*semaphore)->semCount = 0;
    }

    return retValue;
}

/********************************************************************************/
int event_destroy(psem_t semaphore)
{
    return sem_destroy(semaphore);
}

