/*******************************************************************************
 * FreeRTOS+Trace v2.3.0 Recorder Library
 * Percepio AB, www.percepio.com
 *
 * trcKernel.c
 *
 * Functions for integration of the trace recorder library in the FreeRTOS 
 * kernel (requires FreeRTOS v7.1.0 or later).
 * 
 * Terms of Use
 * This software is copyright Percepio AB. The recorder library is free for
 * use together with Percepio products. You may distribute the recorder library
 * in its original form, including modifications in trcPort.c and trcPort.h
 * given that these modification are clearly marked as your own modifications
 * and documented in the initial comment section of these source files. 
 * This software is the intellectual property of Percepio AB and may not be 
 * sold or in other ways commercially redistributed without explicit written 
 * permission by Percepio AB.
 *
 * Disclaimer 
 * The trace tool and recorder library is being delivered to you AS IS and 
 * Percepio AB makes no warranty as to its use or performance. Percepio AB does 
 * not and cannot warrant the performance or results you may obtain by using the 
 * software or documentation. Percepio AB make no warranties, express or 
 * implied, as to noninfringement of third party rights, merchantability, or 
 * fitness for any particular purpose. In no event will Percepio AB, its 
 * technology partners, or distributors be liable to you for any consequential, 
 * incidental or special damages, including any lost profits or lost savings, 
 * even if a representative of Percepio AB has been advised of the possibility 
 * of such damages, or for any claim by any third party. Some jurisdictions do 
 * not allow the exclusion or limitation of incidental, consequential or special 
 * damages, or the exclusion of implied warranties or limitations on how long an 
 * implied warranty may last, so the above limitations may not apply to you.
 *
 * FreeRTOS+Trace is available as Free Edition and in two premium editions.
 * You may use the premium features during 30 days for evaluation.
 * Download FreeRTOS+Trace at http://www.percepio.com/products/downloads/
 *
 * Copyright Percepio AB, 2012.
 * www.percepio.com
 ******************************************************************************/

#include "trcUser.h"
#include "task.h"

#if (configUSE_TRACE_FACILITY == 1)



/******************************************************************************
 * TraceObjectClassTable
 * Translates a FreeRTOS QueueType into trace objects classes (TRACE_CLASS_).
 * This was added since we want to map both types of Mutex and both types of 
 * Semaphores on common classes for all Mutexes and all Semaphores respectively. 
 * 
 * FreeRTOS Queue types
 * #define queueQUEUE_TYPE_BASE                  ( 0U ) => TRACE_CLASS_QUEUE
 * #define queueQUEUE_TYPE_MUTEX                 ( 1U ) => TRACE_CLASS_MUTEX
 * #define queueQUEUE_TYPE_COUNTING_SEMAPHORE    ( 2U ) => TRACE_CLASS_SEMAPHORE
 * #define queueQUEUE_TYPE_BINARY_SEMAPHORE      ( 3U ) => TRACE_CLASS_SEMAPHORE
 * #define queueQUEUE_TYPE_RECURSIVE_MUTEX       ( 4U ) => TRACE_CLASS_MUTEX 
 ******************************************************************************/
traceObjectClass TraceObjectClassTable[5]        =  {TRACE_CLASS_QUEUE,     
                                                     TRACE_CLASS_MUTEX,      
                                                     TRACE_CLASS_SEMAPHORE,  
                                                     TRACE_CLASS_SEMAPHORE,
                                                     TRACE_CLASS_MUTEX };

/* This is defined in FreeRTOS! */
extern volatile void * volatile pxCurrentTCB; 

/* Internal variables */
uint8_t nISRactive = 0;
objectHandleType handle_of_last_logged_task = 0;
uint8_t inExcludedTask = 0;

static uint8_t prvTraceIsObjectExcluded(traceObjectClass, uint32_t);

/*******************************************************************************
 * prvTraceIsObjectExcluded
 *
 * Private function that accepts an object class and an object number and uses
 * that to determine if the object has been flagged as excluded.
 ******************************************************************************/
static uint8_t prvTraceIsObjectExcluded(traceObjectClass objectClass, uint32_t objectNumber)
{
    switch(objectClass)
    {
    case TRACE_CLASS_QUEUE:
        return GET_QUEUE_FLAG_ISEXCLUDED(objectNumber);
        break;
    case TRACE_CLASS_SEMAPHORE:
        return GET_SEMAPHORE_FLAG_ISEXCLUDED(objectNumber);
        break;
    case TRACE_CLASS_MUTEX:
        return GET_MUTEX_FLAG_ISEXCLUDED(objectNumber);
        break;
    case TRACE_CLASS_TASK:
        return GET_TASK_FLAG_ISEXCLUDED(objectNumber);
        break;
    }
    return 0;
}

#if !defined INCLUDE_READY_EVENTS || INCLUDE_READY_EVENTS == 1
/*******************************************************************************
 * vTraceStoreTaskReady
 *
 * This function stores a ready state for the task handle sent in as parameter.
 ******************************************************************************/
void vTraceStoreTaskReady(objectHandleType handle)
{
    uint16_t dts3;
    TREvent* tr;

    if (!GET_TASK_FLAG_ISEXCLUDED(handle))
    {
        dts3 = (uint16_t)prvTraceGetDTS(0xFFFF);
        if (RecorderDataPtr->recorderActive) /* Need to repeat this check! */
        {
            tr = (TREvent*)xTraceNextFreeEventBufferSlot();

            if (tr != NULL)
            {
                tr->type = TR_TASK_READY;
                tr->dts = dts3;
                tr->objHandle = handle;

                prvTraceUpdateCounters();    
            }
        }
    }
}
#endif

/*******************************************************************************
 * vTraceStoreKernelCall
 *
 * This is the main integration point for storing FreeRTOS kernel calls, and
 * is called by the hooks in FreeRTOS.h (see trcKernel.h for event codes).
 ******************************************************************************/
void vTraceStoreKernelCall(uint32_t ecode, traceObjectClass objectClass, uint32_t objectNumber)
{
    KernelCall * kse;
    uint16_t dts1;

    if (handle_of_last_logged_task == 0)
    {
        return;
    }
    
    if (RecorderDataPtr->recorderActive)
    {
        
        /* If it is an ISR or NOT an excluded task, this kernel call will be stored in the trace */
        if (nISRactive || !inExcludedTask)
        {
            /* Make sure ISRs never change the IFE flags of tasks */
            if (!nISRactive)
            {
                /* This checks if this is the first kernel call after a call to
                vTraceTaskInstanceIsFinished. In that case, calls to this kernel service 
                with this specific kernel object become the "instance finish event"
                (IFE) of the calling task.*/
                if (GET_TASK_FLAG_MARKIFE(handle_of_last_logged_task))
                {
                    /* Reset the flag - this has been handled now */
                    CLEAR_TASK_FLAG_MARKIFE(handle_of_last_logged_task);

                    /* Store the kernel service tagged as instance finished event */
                    PROPERTY_TASK_IFE_SERVICECODE(handle_of_last_logged_task) = 
                      (uint8_t)ecode;                

                    /* Store the handle of the specific kernel object */
                    PROPERTY_TASK_IFE_OBJHANDLE(handle_of_last_logged_task) =
                      (objectHandleType)objectNumber;    
                }
            }
            
            /* Check if the referenced object or the event code is excluded */
            if (!prvTraceIsObjectExcluded(objectClass, objectNumber) && !GET_EVENT_CODE_FLAG_ISEXCLUDED(ecode))
            {
                trcCRITICAL_SECTION_BEGIN();
                dts1 = (uint16_t)prvTraceGetDTS(0xFFFF);

                if (RecorderDataPtr->recorderActive) /* Need to repeat this check! */
                {           
                    kse = (KernelCall*) xTraceNextFreeEventBufferSlot();
                    if (kse != NULL)
                    {
                        kse->dts = dts1;
                        kse->type = (uint8_t)ecode;
                        kse->objHandle = (uint8_t)objectNumber;
                        prvTraceUpdateCounters();
                    }
                }
                trcCRITICAL_SECTION_END();
            }
        }
    }
}

/*******************************************************************************
 * vTraceStoreKernelCallWithParam
 *
 * Used for storing kernel calls with a handle and a numeric parameter. This is
 * only used for traceTASK_PRIORITY_SET at the moment.
 ******************************************************************************/
void vTraceStoreKernelCallWithParam(uint32_t evtcode,
                                    traceObjectClass objectClass,
                                    uint32_t objectNumber,
                                    uint8_t param)
{
    KernelCallWithParamAndHandle * kse;
    uint8_t dts2;

    if (RecorderDataPtr->recorderActive && handle_of_last_logged_task && 
        (! inExcludedTask || nISRactive))
    {
        /* Check if the referenced object or the event code is excluded */
        if (!prvTraceIsObjectExcluded(objectClass, objectNumber) && !GET_EVENT_CODE_FLAG_ISEXCLUDED(evtcode))
        {
            trcCRITICAL_SECTION_BEGIN();
            dts2 = (uint8_t)prvTraceGetDTS(0xFF);

            if (RecorderDataPtr->recorderActive) /* Need to repeat this check! */
            {                
                kse = (KernelCallWithParamAndHandle*) xTraceNextFreeEventBufferSlot();
                if (kse != NULL)
                {
                    kse->dts = dts2;
                    kse->type = (uint8_t)evtcode;
                    kse->objHandle = (uint8_t)objectNumber;
                    kse->param = param;
                    prvTraceUpdateCounters();    
                }
            }
            trcCRITICAL_SECTION_END();
        }
    }
}


/*******************************************************************************
 * vTraceStoreKernelCallWithNumericParamOnly
 *
 * Used for storing kernel calls with numeric parameters only. This is
 * only used for traceTASK_DELAY and traceDELAY_UNTIL at the moment.
 ******************************************************************************/
void vTraceStoreKernelCallWithNumericParamOnly(uint32_t evtcode, uint16_t param)
{
    KernelCallWithParam16 * kse;
    uint8_t dts6;

    if (RecorderDataPtr->recorderActive && handle_of_last_logged_task 
        && (! inExcludedTask || nISRactive))
    {
        /* Check if the event code is excluded */
        if (!GET_EVENT_CODE_FLAG_ISEXCLUDED(evtcode))
        {
            trcCRITICAL_SECTION_BEGIN();
            dts6 = (uint8_t)prvTraceGetDTS(0xFF);

            if (RecorderDataPtr->recorderActive) /* Need to repeat this check! */
            {                
                kse = (KernelCallWithParam16*) xTraceNextFreeEventBufferSlot();
                if (kse != NULL)
                {
                    kse->dts = dts6;
                    kse->type = (uint8_t)evtcode;
                    kse->param = param;
                    prvTraceUpdateCounters();    
                }
            }
            trcCRITICAL_SECTION_END();
        }
    }
}

objectHandleType handle_of_running_task = 0;

/*******************************************************************************
 * vTraceStoreTaskswitch
 * Called by the scheduler from the SWITCHED_OUT hook, and by uiTraceStart.
 * At this point interrupts are assumed to be disabled!
 ******************************************************************************/
void vTraceStoreTaskswitch(void)
{
    uint16_t dts3;
    TSEvent* ts;        
    int8_t skipEvent = 0;
    uint32_t schedulerState = 0;
    
    /*************************************************************************** 
    This is used to detect if a high-priority ISRs is illegally using the 
    recorder ISR trace functions (vTraceStoreISRBegin and ...End) while the 
    recorder is busy with a task-level event or lower priority ISR event.
    
    If this is detected, it triggers a call to vTraceError with the error 
    "Illegal call to vTraceStoreISRBegin/End". If you get this error, it means
    that the macro taskENTER_CRITICAL does not disable this ISR, as required.
    You can solve this by adjusting the value of the FreeRTOS constant
    configMAX_SYSCALL_INTERRUPT_PRIORITY, which is defined in FreeRTOSConfig.h

    Note: Setting recorder_busy is normally handled in our macros
    trcCRITICAL_SECTION_BEGIN and _END, but is needed explicitly in this 
    function since critical sections should not be used in the context switch 
    event...)    
    ***************************************************************************/
    recorder_busy++; 
    
    schedulerState = xTaskGetSchedulerState();

    if (schedulerState == 0)
    {
        /* This occurs on the very first taskswitch event, generated by 
        vTraceStart and uiTraceStart if the scheduler is not yet started.
        This creates a dummy "(startup)" task entry internally in the
        recorder */
        if (handle_of_running_task == 0)
        {
            handle_of_running_task = xTraceGetObjectHandle(TRACE_CLASS_TASK);

            vTraceSetObjectName(TRACE_CLASS_TASK, 
                handle_of_running_task,
                "(startup)");

            vTraceSetPriorityProperty(TRACE_CLASS_TASK,
                handle_of_running_task,
                0);
        }        
    }
    else
    {    
        handle_of_running_task = 
        (objectHandleType)uxTaskGetTaskNumber(xTaskGetCurrentTaskHandle());
    }
    
    /* Skip the event if the task has been excluded, using vTraceExcludeTask */
    if (GET_TASK_FLAG_ISEXCLUDED(handle_of_running_task))
    {    
        skipEvent = 1;
        inExcludedTask = 1;            
    }
    else
        inExcludedTask = 0;
        

    /* Skip the event if the same task is scheduled */
    if (handle_of_running_task == handle_of_last_logged_task)
    {
        skipEvent = 1;
    }
  
    if (! RecorderDataPtr->recorderActive)
    {
        skipEvent = 1;
    }

    /* If this event should be logged, log it! */
    if (skipEvent == 0)    
    {    
        dts3 = (uint16_t)prvTraceGetDTS(0xFFFF);
        
        if (RecorderDataPtr->recorderActive) /* Need to repeat this check! */
        {
            handle_of_last_logged_task = handle_of_running_task;            
            ts = (TSEvent*)xTraceNextFreeEventBufferSlot();

            if (ts != NULL)
            {
                if (uiTraceGetObjectState(TRACE_CLASS_TASK,
                    handle_of_last_logged_task) == TASK_STATE_INSTANCE_ACTIVE)
                {
                    ts->type = TS_TASK_RESUME;
                }
                else
                {
                    ts->type = TS_TASK_BEGIN;
                }

                ts->dts = dts3;
                ts->objHandle = handle_of_last_logged_task;

                vTraceSetObjectState(TRACE_CLASS_TASK, 
                                     handle_of_last_logged_task, 
                                     TASK_STATE_INSTANCE_ACTIVE);

                prvTraceUpdateCounters();    
            }
        }
    }    

    /* See comment on recorder_busy++ above. */
    recorder_busy--; 
}

/*******************************************************************************
 * vTraceStoreNameCloseEvent
 *
 * Updates the symbol table with the name of this object from the dynamic
 * objects table and stores a "close" event, holding the mapping between handle
 * and name (a symbol table handle). The stored name-handle mapping is thus the
 * "old" one, valid up until this point.
 ******************************************************************************/
#if (INCLUDE_OBJECT_DELETE == 1)
void vTraceStoreObjectNameOnCloseEvent(objectHandleType handle, 
                                       traceObjectClass objectclass)
{    
    ObjCloseNameEvent * ce;
    const char * name;
    traceLabel idx;

    name = PROPERTY_NAME_GET(objectclass, handle);

    idx = prvTraceOpenSymbol(name, 0);
    
    // Interrupt disable not necessary, already done in trcHooks.h macro
    ce = (ObjCloseNameEvent*) xTraceNextFreeEventBufferSlot(); 
    if (ce != NULL)
    {
        ce->type = EVENTGROUP_OBJCLOSE_NAME + objectclass;
        ce->objHandle = handle;
        ce->symbolIndex = idx;
        prvTraceUpdateCounters();
    }
    
}

void vTraceStoreObjectPropertiesOnCloseEvent(objectHandleType handle, 
                                             traceObjectClass objectclass)
{
    ObjClosePropEvent * pe;

    if (objectclass == TRACE_CLASS_ISR)
    {        
        /* ISR handles should not be closed - never called for ISR */
        return;
    }

    // Interrupt disable not necessary, already done in trcHooks.h macro
    pe = (ObjClosePropEvent*) xTraceNextFreeEventBufferSlot();
    if (pe != NULL)
    {
        if (objectclass == TRACE_CLASS_TASK)
        {
            pe->arg1 = PROPERTY_ACTOR_PRIORITY(objectclass, handle);
            pe->arg2 = PROPERTY_TASK_IFE_SERVICECODE(handle);
            pe->arg3 = PROPERTY_TASK_IFE_OBJHANDLE(handle);
            PROPERTY_TASK_IFE_SERVICECODE(handle) = 0;
            PROPERTY_TASK_IFE_OBJHANDLE(handle) = 0;
        }else{
            pe->arg1 = PROPERTY_OBJECT_STATE(objectclass, handle);
        }
        pe->type = EVENTGROUP_OBJCLOSE_PROP + objectclass;    
        prvTraceUpdateCounters();
    }
}
#endif

void vTraceSetPriorityProperty(uint8_t objectclass, uint8_t id, uint8_t value)
{
    PROPERTY_ACTOR_PRIORITY(objectclass, id) = value;
}

uint8_t uiTraceGetPriorityProperty(uint8_t objectclass, uint8_t id)
{
    return PROPERTY_ACTOR_PRIORITY(objectclass, id);
}

void vTraceSetObjectState(uint8_t objectclass, uint8_t id, uint8_t value)
{
    PROPERTY_OBJECT_STATE(objectclass, id) = value;
}

void vTraceSetTaskInstanceFinished(objectHandleType handle)
{
#if (USE_IMPLICIT_IFE_RULES == 1)
    if (PROPERTY_TASK_IFE_SERVICECODE(handle) == 0)
    {
        PROPERTY_OBJECT_STATE(TRACE_CLASS_TASK, handle) = 0;
    }
#endif
}

uint8_t uiTraceGetObjectState(uint8_t objectclass, uint8_t id)
{
    return PROPERTY_OBJECT_STATE(objectclass, id);
}

#endif