/*
 * Copyright (c) 2013 - 2014, Freescale Semiconductor, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * o Redistributions of source code must retain the above copyright notice, this list
 *   of conditions and the following disclaimer.
 *
 * o 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.
 *
 * o Neither the name of Freescale Semiconductor, Inc. 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 HOLDER 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.
 */
#if defined (FSL_RTOS_FREE_RTOS)

#include <string.h>
#include <assert.h>
#include "fsl_os_abstraction.h"
#include "fsl_interrupt_manager.h"

/*FUNCTION**********************************************************************
 *
 * Function Name : sync_create
 * Description   : This function is used to create a sync object.
 * Return kSuccess if create successfully, otherwise return kError.
 *
 *END**************************************************************************/
fsl_rtos_status sync_create(sync_object_t *obj, uint8_t initValue)
{
    assert(obj);

    *obj = xSemaphoreCreateCounting(0xFF, initValue);
    if (*obj==NULL)
    {
        return kError; /* creating semaphore failed */
    }
    return kSuccess;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : sync_wait
 * Description   : This function checks the sync object's counting value, if it
 * is positive, decreases it and returns kSuccess, otherwise, timeout will be
 * used for wait. Pass the #kSyncWaitForever constant to wait indefinitely,
 * timeout should not be 0.
 * Return kSuccess if the sync object is signaled before the timeout is exhausted,
 * return kTimeout if the sync object has not been signaled before the timeout
 * is exhausted, return kError if the parameter is invalid.
 *
 *END**************************************************************************/
fsl_rtos_status sync_wait(sync_object_t *obj, uint32_t timeout)
{
    uint32_t timeoutTicks;
    assert(obj);

    if (timeout==0)   /* Timeout should not be 0 */
    {
        return kError;
    }

    /* Convert timeout from millisecond to tick. */
    if (timeout == kSyncWaitForever)
    {
        timeoutTicks = portMAX_DELAY;
    }
    else
    {
        timeoutTicks = timeout/portTICK_RATE_MS;
    }

    if (xSemaphoreTake(*obj, timeoutTicks)==pdFALSE)
    {
        return kTimeout; /* timeout */
    }
    else
    {
        return kSuccess; /* semaphore taken */
    }
}

/*FUNCTION**********************************************************************
 *
 * Function Name : sync_poll
 * Description   : This function is used to poll a sync object's status.
 * If the sync object's counting value is positive, decrease it and return
 * kSuccess. If the object's counting value is 0, the function will
 * return kIdle immediately. If the parameter is invalid, return kError.
 *
 *END**************************************************************************/
fsl_rtos_status sync_poll(sync_object_t *obj)
{
    assert(obj);

    if (xSemaphoreTake(*obj, 0)==pdFALSE)
    {
        return kIdle; /* sempahore not taken/timeout */
    }
    else
    {
        return kSuccess; /* semaphore taken */
    }
}

/*FUNCTION**********************************************************************
 *
 * Function Name : sync_signal
 * Description   : This function is used to signal a sync object.
 * Return kSuccess if the sync object is signaled successfully, otherwise
 * return kError.
 * This function should not be called from isr.
 *
 *END**************************************************************************/
fsl_rtos_status sync_signal(sync_object_t *obj)
{
    assert(obj);

    if (xSemaphoreGive(*obj)==pdTRUE)
    {
        return kSuccess; /* sync object given */
    }
    else
    {
        return kError;
    }
}

/*FUNCTION**********************************************************************
 *
 * Function Name : sync_signal_from_isr
 * Description   : This function is used to signal a sync object.
 * Return kSuccess if the sync object is signaled successfully, otherwise
 * return kError.
 * This function should be called from isr only.
 *
 *END**************************************************************************/
fsl_rtos_status sync_signal_from_isr(sync_object_t *obj)
{
    assert(obj);
    portBASE_TYPE taskToWake = pdFALSE;

    if (xSemaphoreGiveFromISR(*obj, &taskToWake)==pdTRUE)
    {
        if (pdFALSE != taskToWake)
        {
            vPortYieldFromISR();
        }
        return kSuccess;
    }
    else
    {
        return kError;
    }
}

/*FUNCTION**********************************************************************
 *
 * Function Name : sync_destroy
 * Description   : This function is used to destroy a sync object.
 * For FreeRTOS, this function always return kSuccess.
 *
 *END**************************************************************************/
fsl_rtos_status sync_destroy(sync_object_t *obj)
{
    assert(obj);
    assert(*obj);

    vSemaphoreDelete(*obj);
    return kSuccess;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : lock_create
 * Description   : This function is used to create a lock object.
 * Return kSuccess if create successfully, otherwise return kError.
 *
 *END**************************************************************************/
fsl_rtos_status lock_create(lock_object_t *obj)
{
    assert(obj);
    *obj = xSemaphoreCreateMutex();
    if (NULL == *obj)
    {
        return kError;
    }
    else
    {
        return kSuccess;
    }
}

/*FUNCTION**********************************************************************
 *
 * Function Name : lock_wait
 * Description   : This function is used to wait to obtain a lock object.
 * Pass the #kSyncWaitForever constant to wait indefinitely for someone to
 * unlock the object. 0 should not be passed to this function.
 * Return kSuccess if the lock object is obtained before the timeout is exhausted,
 * return kTimeout if has not obtained the lock object before the timeout
 * is exhausted, return kError if the parameter is invalid.
 *
 *END**************************************************************************/
fsl_rtos_status lock_wait(lock_object_t *obj, uint32_t timeout)
{
    uint32_t timeoutTicks;

    assert(obj);

    if (timeout==0)
    {
        return kError;  /* Timeout should not be 0. */
    }

    /* If object has been locked by current task, return error. */
    if (xSemaphoreGetMutexHolder(*obj) == xTaskGetCurrentTaskHandle())
    {
        return kError;
    }

    /* Convert timeout from millisecond to tick. */
    if (timeout == kSyncWaitForever)
    {
        timeoutTicks = portMAX_DELAY;
    }
    else
    {
        timeoutTicks = timeout/portTICK_RATE_MS;
    }

    if (xSemaphoreTake(*obj, timeoutTicks)==pdFALSE)
    {
      return kTimeout; /* timeout */
    }
    else
    {
      return kSuccess; /* semaphore taken */
    }
}

/*FUNCTION**********************************************************************
 *
 * Function Name : lock_poll
 * Description   : This function is used to poll a lock object's status.
 * If the lock object is unlocked, this function will return kSuccess and lock
 * the object. Otherwise return kIdle.
 *
 *END**************************************************************************/
fsl_rtos_status lock_poll(lock_object_t *obj)
{
    assert(obj);

    if (xSemaphoreTake(*obj, 0)==pdFALSE)
    {
      return kIdle; /* timeout */
    }
    else
    {
      return kSuccess; /* semaphore taken */
    }
}

/*FUNCTION**********************************************************************
 *
 * Function Name : lock_release
 * Description   : This function is used to unlock a lock object.
 * Return kSuccess if the lock object is unlocked successfully, otherwise
 * return kError.
 *
 *END**************************************************************************/
fsl_rtos_status lock_release(lock_object_t *obj)
{
    assert(obj);

    if (xSemaphoreGive(*obj)==pdPASS)
    {
        return kSuccess;
    }
    else
    {
        return kError;
    }
}

/*FUNCTION**********************************************************************
 *
 * Function Name : lock_destroy
 * Description   : This function is used to destroy a lock object.
 * For FreeRTOS, this function always return kSuccess.
 *
 *END**************************************************************************/
fsl_rtos_status lock_destroy(lock_object_t *obj)
{
    assert(obj);
    assert(*obj);

    vSemaphoreDelete(*obj);
    return kSuccess;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : event_create
 * Description   : This function is used to create a event object. The event can
 * be auto clear or manual clear.
 * Return kSuccess if create successfully, otherwise return kError.
 *
 *END**************************************************************************/
fsl_rtos_status event_create(event_object_t *obj, event_clear_type clearType)
{
    assert(obj);

    vSemaphoreCreateBinary(obj->flagsSem)

    if (NULL == obj->flagsSem)
    {
        return kError;
    }
    /* Set the binary semaphore to 0. */
    if (pdFALSE == xSemaphoreTake(obj->flagsSem, 0))
    {
        return kError;
    }

    obj->flags = 0u;
    obj->clearType = clearType;
    return kSuccess;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : event_wait
 * Description   : This function is used to wait a event.
 * Pass the #kSyncWaitForever constant to wait indefinitely. 0 should not be
 * passed to this function. Return kSuccess if any flag bit of the event object is
 * set before the timeout is exhausted, return kTimeout if no flag is set before
 * the timeout is exhausted, return kError if the parameter is invalid. If setFlags
 * is not NULL, the flags that have been set will be get by setFlags.
 *
 *END**************************************************************************/
fsl_rtos_status event_wait(event_object_t *obj, uint32_t timeout, event_group_t *setFlags)
{
    event_group_t eventLocal;
    xTimeOutType xTimeOut;
    portTickType timeoutTicks;

    assert(obj);
    assert(obj->flagsSem);

    /* Convert timeout from millisecond to tick. */
    if (timeout == kSyncWaitForever)
    {
        timeoutTicks = portMAX_DELAY;
    }
    else
    {
        timeoutTicks = timeout/portTICK_RATE_MS;
    }
    vTaskSetTimeOutState(&xTimeOut);

    for (;;)
    {
        taskENTER_CRITICAL();
        eventLocal = obj->flags;
        taskEXIT_CRITICAL();

        if (eventLocal) /* If the event is ready */
        {
            if (setFlags)
            {
                *setFlags = eventLocal;
            }
            if (obj->clearType == kEventAutoClr)
            {
                taskENTER_CRITICAL();
                obj->flags = 0u;
                taskEXIT_CRITICAL();
                xSemaphoreTake(obj->flagsSem, 0); /* clear the semaphore */
            }
            return kSuccess;
        }
        else /* If the event is not ready, wait here */
        {
            if (xTaskCheckForTimeOut(&xTimeOut, &timeoutTicks) == pdTRUE)
            {
                return kTimeout;
            }
            if (pdFALSE == xSemaphoreTake(obj->flagsSem, timeoutTicks)) /* event get */
            {
                return kTimeout;
            }
        }
    }
}

/*FUNCTION**********************************************************************
 *
 * Function Name : event_set
 * Description   : Set one or more event flags of an event object.
 * Return kSuccess if set successfully, kError if failed.
 * This function should not be called from an ISR.
 *
 *END**************************************************************************/
fsl_rtos_status event_set(event_object_t *obj, event_group_t flags)
{
    event_group_t flagsTmp;
    assert(obj);
    assert(obj->flagsSem);

    taskENTER_CRITICAL();
    flagsTmp = obj->flags;
    obj->flags |= flags;
    taskEXIT_CRITICAL();

    /*
     * If the number of set flags changes from 0 to X, notice the semaphore.
     */
    if ((0==flagsTmp) && (0!=flags))
    {
        return (pdTRUE==xSemaphoreGive(obj->flagsSem)) ? kSuccess : kError;
    }
    else
    {
        return kSuccess;
    }
}

/*FUNCTION**********************************************************************
 *
 * Function Name : event_set_from_isr
 * Description   : Set one or more event flags of an event object.
 * Return kSuccess if set successfully, kError if failed.
 * This function should only be called from an ISR.
 *
 *END**************************************************************************/
fsl_rtos_status event_set_from_isr(event_object_t *obj, event_group_t flags)
{
    event_group_t flagsTmp;
    portBASE_TYPE taskToWake = pdFALSE;
    assert(obj);
    assert(obj->flagsSem);

    flagsTmp = obj->flags;
    obj->flags |= flags;
    /*
     * If the number of set flags changes from 0 to X, notice the semaphore.
     */
    if ((0==flagsTmp) && (0!=flags))
    {
        if (pdTRUE==xSemaphoreGiveFromISR(obj->flagsSem, &taskToWake))
        {
            if (pdFALSE != taskToWake)
            {
                vPortYieldFromISR();
            }
            return kSuccess;
        }
        else
        {
            return kError;
        }
    }
    else
    {
        return kSuccess;
    }
}

/*FUNCTION**********************************************************************
 *
 * Function Name : event_clear
 * Description   : Clear one or more event flags of an event object.
 * Return kSuccess if clear successfully, kError if failed.
 *
 *END**************************************************************************/
fsl_rtos_status event_clear(event_object_t *obj, event_group_t flags)
{
    event_group_t flagsTmp;
    assert(obj);
    assert(obj->flagsSem);

    taskENTER_CRITICAL();
    obj->flags &= ~flags;
    flagsTmp = obj->flags;
    taskEXIT_CRITICAL();

    /*
     * If all event flags are cleared, set the semaphore to 0.
     * Because obj->flagsSem is a binary semaphore, so poll it once can
     * always set it to 0.
     */
    if (!flagsTmp)
    {
        xSemaphoreTake(obj->flagsSem, 0);
    }
    return kSuccess;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : event_check_flags
 * Description   : Check the specific event flags are set or not.
 * Return kFlagSet if any flags are set, otherwise return kFlagNotSet.
 *
 *END**************************************************************************/
event_status event_check_flags(event_object_t *obj, event_group_t flag)
{
    event_group_t flagLocal;
    assert(obj);

    taskENTER_CRITICAL();
    flagLocal = obj->flags;
    taskEXIT_CRITICAL();

    if (flagLocal & flag)
    {
        return kFlagSet;
    }
    else
    {
        return kFlagNotSet;
    }
}

/*FUNCTION**********************************************************************
 *
 * Function Name : event_destroy
 * Description   : This function is used to destroy a event object.
 * For FreeRTOS, this function will always return kSuccess.
 *
 *END**************************************************************************/
fsl_rtos_status event_destroy(event_object_t *obj)
{
    assert(obj);
    assert(obj->flagsSem);

    vSemaphoreDelete(obj->flagsSem);
    obj->flagsSem = NULL;
    obj->flags = 0;

    return kSuccess;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : __task_create
 * Description   : This function is used by the macro task_create. Return
 * kSuccess if the task is created successfully, kError if failed.
 * Large priority number indicate high priority in FreeRTOS, this is different
 * from other RTOSes, for compatible, priority is converted by configMAX_PRIORITIES,
 * please set configMAX_PRIORITIES large enough in configure file.
 *
 *END**************************************************************************/
fsl_rtos_status __task_create(task_t          task,
                              uint8_t        *name,
                              uint16_t        stackSize,
                              task_stack_t   *stackMem,
                              uint16_t        priority,
                              void           *param,
                              bool            usesFloat,
                              task_handler_t *handler)
{
      if (xTaskCreate(
            task,  /* pointer to the task */
            (signed char const*)name, /* task name for kernel awareness debugging */
            stackSize/sizeof(portSTACK_TYPE), /* task stack size */
            param, /* optional task startup argument */
            configMAX_PRIORITIES - priority -1,  /* initial priority */
            handler /* optional task handle to create */
          ) != pdPASS)
      {
        return kError; /* error! probably out of memory */
      }
      return kSuccess;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : task_destroy
 * Description   : This function destroy a task. Return kSuccess if the task
 * is destroied, or return kError.
 *
 *END**************************************************************************/
fsl_rtos_status task_destroy(task_handler_t handler)
{
#if INCLUDE_vTaskDelete /* vTaskDelete() enabled */
      vTaskDelete(handler);
      return kSuccess;
#else
      return kError; /* vTaskDelete() not available */
#endif
}

/*FUNCTION**********************************************************************
 *
 * Function Name : msg_queue_create
 * Description   : This function is used to create a message queue.
 * Return the handle to the message queue if create successfully, other wise
 * return 0.
 *
 *END**************************************************************************/
msg_queue_handler_t msg_queue_create(msg_queue_t *queue, uint16_t number, uint16_t size)
{
#if (__FSL_RTOS_MSGQ_COPY_MSG__)
    *queue=xQueueCreate(number, size*sizeof(uint32_t));
#else
    *queue=xQueueCreate(number, sizeof(void*));
#endif
    return *queue;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : msg_queue_put
 * Description   : This function is used to put a message to a message queue.
 * Return kSuccess if the message is put successfully, other wise return kError.
 *
 *END**************************************************************************/
fsl_rtos_status msg_queue_put(msg_queue_handler_t handler, msg_queue_item_t item)
{
    if (handler==NULL) {
      return kError; /* queue not allocated? */
    }
#if (__FSL_RTOS_MSGQ_COPY_MSG__)
    if (xQueueSendToBack(handler, item, 0)!=pdPASS)
#else
    if (xQueueSendToBack(handler, &item, 0)!=pdPASS)
#endif
    {
      return kError; /* not able to send it to the queue? */
    }
    else
    {
      return kSuccess;
    }
}

/*FUNCTION**********************************************************************
 *
 * Function Name : msg_queue_get
 * Description   : This function is used to get a message from a message queue.
 * If the message queue is empty, set timeout to 0 to return immediately, set
 * timeout to #kSyncWaitForever to wait indefinitely.
 * Return kSuccess if the message is get before the timeout is exhausted, return
 * kTimeout if no message is get before the timeout is exhausted, return kError
 * if the parameter is invalid.
 * The unit of timeout is millisecond.
 *
 *END**************************************************************************/
fsl_rtos_status msg_queue_get(msg_queue_handler_t handler,
                              msg_queue_item_t   *item,
                              uint32_t            timeout)
{
    uint32_t timeoutTicks;

    if (handler==NULL) {
        return kError; /* queue not allocated? */
    }

    if (timeout == kSyncWaitForever)
    {
        timeoutTicks = portMAX_DELAY;
    }
    else
    {
        timeoutTicks = timeout/portTICK_RATE_MS;
    }
#if (__FSL_RTOS_MSGQ_COPY_MSG__)
    if (xQueueReceive(handler, *item, timeoutTicks)!=pdPASS)
#else
    if (xQueueReceive(handler, item, timeoutTicks)!=pdPASS)
#endif
    {
        return kTimeout; /* not able to send it to the queue? */
    }
    else
    {
        return kSuccess;
    }
}
/*FUNCTION**********************************************************************
 *
 * Function Name : msg_queue_flush
 * Description   : This function is used to flush the message queue.
 * Return kSuccess if the message queue is flushed successfully, other wise
 * return kError.
 *
 *END**************************************************************************/
fsl_rtos_status msg_queue_flush(msg_queue_handler_t handler)
{
    if (handler==NULL)
    {
        return kError; /* queue not allocated? */
    }
    if (xQueueReset(handler)==pdPASS)
    {
        return kSuccess;
    }
    else
    {
        return kError;
    }
}

/*FUNCTION**********************************************************************
 *
 * Function Name : msg_queue_destroy
 * Description   : This function is used to destroy the message queue.
 * For FreeRTOS, this function always returns kSuccess.
 *
 *END**************************************************************************/
fsl_rtos_status msg_queue_destroy(msg_queue_handler_t handler)
{
    vQueueDelete(handler);
    return kSuccess;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : mem_allocate
 * Description   : This function is used to allocate amount of memory in bytes.
 * Return the pointer to the memory if success, otherwise return NULL;
 *
 *END**************************************************************************/
void *mem_allocate(size_t size)
{
    return pvPortMalloc(size);
}

/*FUNCTION**********************************************************************
 *
 * Function Name : mem_allocate_zero
 * Description   : This function is used to allocate amount of memory in bytes
 * and initializes it to 0.
 * Return the pointer to the memory if success, otherwise return NULL;
 *
 *END**************************************************************************/
void * mem_allocate_zero(size_t size)
{
    void *ptr = pvPortMalloc(size);

    if (ptr==NULL)
    {
      return NULL; /* failed allocating memory */
    }
    return memset(ptr, 0, size);
}

/*FUNCTION**********************************************************************
 *
 * Function Name : mem_free
 * Description   : This function is used to free the memory previously allocated.
 *
 *END**************************************************************************/
fsl_rtos_status mem_free(void *ptr)
{
    vPortFree(ptr);
    return kSuccess;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : time_delay
 * Description   : This function is used to delay for a number of milliseconds.
 *
 *END**************************************************************************/
void time_delay(uint32_t delay)
{
    vTaskDelay(delay/portTICK_RATE_MS);
}

/*FUNCTION**********************************************************************
 *
 * Function Name : interrupt_handler_register
 * Description   : This function is used to install interrupt handler.
 * For FreeRTOS, this function will always return kSuccess.
 *
 *END**************************************************************************/
fsl_rtos_status interrupt_handler_register(int32_t irqNumber, void (*handler)(void))
{
    interrupt_register_handler((IRQn_Type)irqNumber, handler);

    return kSuccess;
}
#endif /* FSL_RTOS_FREE_RTOS */

/*******************************************************************************
 * EOF
 ******************************************************************************/
