/*
 *  Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/ 
 *  
 *  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 Texas Instruments Incorporated 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.
 *  
 */

/*
 *  ======== SemaphoreP_freertos.c ========
 */

#include <ti/drivers/ports//SemaphoreP.h>

#include "FreeRTOS.h"
#include "semphr.h"
#include "queue.h"
#include "task.h"

/*
 *  ======== SemaphoreP_create ========
 */
SemaphoreP_Handle SemaphoreP_create(unsigned int count,
                                    SemaphoreP_Params *params)
{
    SemaphoreHandle_t sem = NULL;
    SemaphoreP_Params semParams;

    if (params == NULL) {
        params = &semParams;
        SemaphoreP_Params_init(params);
    }

    if (params->mode == SemaphoreP_Mode_COUNTING) {
#if (configUSE_COUNTING_SEMAPHORES == 1)
        sem = xSemaphoreCreateCounting((UBaseType_t)params->maxCount,
                (UBaseType_t)count);
#endif
    }
    else {
        sem = xSemaphoreCreateBinary();
        if (count == 0) {
            xSemaphoreTake(sem, 0);
        }else
        {
            xSemaphoreGive(sem);
        }
    }

    return ((SemaphoreP_Handle)sem);
}

/*
 *  ======== SemaphoreP_delete ========
 */
SemaphoreP_Status SemaphoreP_delete(SemaphoreP_Handle handle)
{
    vSemaphoreDelete((SemaphoreHandle_t)handle);
    return (SemaphoreP_OK);
}

/*
 *  ======== SemaphoreP_Params_init ========
 */
void SemaphoreP_Params_init(SemaphoreP_Params *params)
{
    params->mode = SemaphoreP_Mode_BINARY;
    params->name = NULL;
    params->maxCount = 1;
}

/*
 *  ======== SemaphoreP_pend ========
 */
SemaphoreP_Status SemaphoreP_pend(SemaphoreP_Handle handle, uint32_t timeout)
{
    BaseType_t status;
    uint32_t tickRateMS;

    tickRateMS = (configTICK_RATE_HZ / 1000);
    status = xSemaphoreTake((SemaphoreHandle_t)handle,
            timeout / tickRateMS);
    if (status == pdTRUE) {
        return (SemaphoreP_OK);
    }

    return (SemaphoreP_TIMEOUT);
}

/*
 *  ======== SemaphoreP_post ========
 */
SemaphoreP_Status SemaphoreP_post(SemaphoreP_Handle handle)
{
    xSemaphoreGive((SemaphoreHandle_t)handle);
    return (SemaphoreP_OK);
}

/*
 *  ======== SemaphoreP_postFromClock ========
 */
SemaphoreP_Status SemaphoreP_postFromClock(SemaphoreP_Handle handle)
{
    return (SemaphoreP_post(handle));
}

/*
 *  ======== SemaphoreP_postFromISR ========
 */
SemaphoreP_Status SemaphoreP_postFromISR(SemaphoreP_Handle handle)
{
    BaseType_t xHigherPriorityTaskWoken;
    BaseType_t result;
    SemaphoreP_Status status;

    result = xSemaphoreGiveFromISR((SemaphoreHandle_t)handle,
            &xHigherPriorityTaskWoken);
    if (result == pdTRUE) {
        status = SemaphoreP_OK;
    }
    else {
        /* The queue is full */
        status = SemaphoreP_FAILURE;
    }

    if( xHigherPriorityTaskWoken != pdFALSE )
    {
           
        /*
         *  A higher priority task was woken. In this case, a context
         *  switch should be requested before exiting the ISR.
         */
        /*
         *  TODO:  CC3200 osi_freertos SyncObjSignalFromISR() function
         *  calls taskYIELD().
         *
         *  task.h:
         *      #define taskYIELD()  portYIELD()
         *
         *  FreeRTOS/Source/portable/GCC/ARM_CM3/portable.h:
         *      extern void vPortYield( void );
         *      #define portYIELD()  vPortYield()
         *
         *  FreeRTOS/Source/portable/GCC/ARM_CM3/port.c:
         *      void vPortYield( void )
         *      {
         *    	    // Set a PendSV to request a context switch.
         *          portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;
         *
         *          //  Barriers are normally not required but do ensure
         *          //  the code is completely
         *          //  within the specified behaviour for the architecture.
         *          __asm volatile( "dsb" );
         *          __asm volatile( "isb" );
         *      }
         *
         *  Is it ok to call this from the ISR?
         */
        taskYIELD();
    }

    return (status);
}
