/*********************************************************************
*                    SEGGER Microcontroller GmbH                     *
*                        The Embedded Experts                        *
**********************************************************************
*                                                                    *
*            (c) 1995 - 2021 SEGGER Microcontroller GmbH             *
*                                                                    *
*       www.segger.com     Support: support@segger.com               *
*                                                                    *
**********************************************************************
*                                                                    *
*       SEGGER SystemView * Real-time application analysis           *
*                                                                    *
**********************************************************************
*                                                                    *
* All rights reserved.                                               *
*                                                                    *
* SEGGER strongly recommends to not make any changes                 *
* to or modify the source code of this software in order to stay     *
* compatible with the SystemView and RTT protocol, and J-Link.       *
*                                                                    *
* Redistribution and use in source and binary forms, with or         *
* without modification, are permitted provided that the following    *
* condition is met:                                                  *
*                                                                    *
* o Redistributions of source code must retain the above copyright   *
*   notice, this condition and the following disclaimer.             *
*                                                                    *
* 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 SEGGER Microcontroller 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.                                                            *
*                                                                    *
**********************************************************************
*                                                                    *
*       SystemView version: 3.30                                    *
*                                                                    *
**********************************************************************
-------------------------- END-OF-HEADER -----------------------------

File    : SEGGER_SYSVIEW_uCOSII.c
Purpose : Interface between Micrium uC/OS-II and SystemView.
Revision: $Rev: 9599 $
*/

#include <os_trace_events.h>

#ifndef SYSVIEW_MEMSET
  #include <string.h>
  #define SYSVIEW_MEMSET(p, v, n)   memset(p, v, n)
#endif

#ifndef  OS_CFG_TRACE_MAX_RESOURCES
#define  OS_CFG_TRACE_MAX_RESOURCES              0
#endif

typedef struct SYSVIEW_UCOSII_TASK_STATUS SYSVIEW_UCOSII_TASK_STATUS;

struct SYSVIEW_UCOSII_TASK_STATUS {
  U32          TaskID;
  const char*  OSTCBTaskName;
  INT8U        OSTCBPrio;
  OS_STK*      OSTCBStkBottom;
  INT32U       OSTCBStkSize;
};

typedef struct SYSVIEW_UCOSII_RESOURCE SYSVIEW_UCOSII_RESOURCE;

struct SYSVIEW_UCOSII_RESOURCE {
  U32         ResourceId;
  const char* sResource;
  U32         Registered;
};

static SYSVIEW_UCOSII_TASK_STATUS _aTasks[OS_CFG_TRACE_MAX_TASK];
static unsigned _NumTasks;

#if OS_CFG_TRACE_MAX_RESOURCES > 0
static SYSVIEW_UCOSII_RESOURCE _aResources[OS_CFG_TRACE_MAX_RESOURCES];
static unsigned                _NumResources;
#endif

/*********************************************************************
*
*       _cbSendTaskList()
*
*  Function description
*    This function is part of the link between FreeRTOS and SYSVIEW.
*    Called from SystemView when asked by the host, it uses SYSVIEW
*    functions to send the entire task list to the host.
*/
static void _cbSendTaskList(void) {
  unsigned n;

  for (n = 0; n < _NumTasks; n++) {
    SYSVIEW_SendTaskInfo((U32)_aTasks[n].TaskID, _aTasks[n].OSTCBTaskName, (unsigned)_aTasks[n].OSTCBPrio, (U32)_aTasks[n].OSTCBStkBottom, (unsigned)_aTasks[n].OSTCBStkSize);
  }
}

/*********************************************************************
*
*       _cbGetTime()
*
*  Function description
*    This function is part of the link between FreeRTOS and SYSVIEW.
*    Called from SystemView when asked by the host, returns the
*    current system time in micro seconds.
*/
static U64 _cbGetTime(void) {
  INT32U Tick;

  Tick = OSTimeGet();

  return Tick * 1000;
}

/*********************************************************************
*
*       Global functions
*
**********************************************************************
*/

/*********************************************************************
*
*       SYSVIEW_TaskReady()
*
*  Function description
*    Record when a task is ready for execution.
*/
void SYSVIEW_TaskReady(U32 TaskID) {
  if (TaskID != (U32)OSTCBPrioTbl[OS_TASK_IDLE_PRIO]) {
    SEGGER_SYSVIEW_OnTaskStartReady(TaskID);
  }
}

/*********************************************************************
*
*       SYSVIEW_TaskSwitchedIn()
*
*  Function description
*    Record when a task starts/continues execution.
*    If the idle task continues, record an on idle event.
*/
void SYSVIEW_TaskSwitchedIn(U32 TaskID) {
  if (TaskID != (U32)OSTCBPrioTbl[OS_TASK_IDLE_PRIO]) {
    SEGGER_SYSVIEW_OnTaskStartExec(TaskID);
  } else {
    SEGGER_SYSVIEW_OnIdle();
  }
}

/*********************************************************************
*
*       SYSVIEW_TaskSuspend()
*
*  Function description
*    Record when a task is suspended.
*/
void SYSVIEW_TaskSuspend(U32 TaskID) {
  if (TaskID != (U32)OSTCBPrioTbl[OS_TASK_IDLE_PRIO]) {
    SEGGER_SYSVIEW_OnTaskStopReady(TaskID,  (1u << 2));
  }
}

/*********************************************************************
*
*       SYSVIEW_AddTask()
*
*  Function description
*    Add a task to the internal list and record its information.
*/
void SYSVIEW_AddTask(U32 TaskID, const char* OSTCBTaskName, INT8U OSTCBPrio, OS_STK* OSTCBStkBottom, INT32U OSTCBStkSize) {
  if (TaskID != (U32)OSTCBPrioTbl[OS_TASK_IDLE_PRIO]) {
    if (_NumTasks >= OS_CFG_TRACE_MAX_TASK) {
      SEGGER_SYSVIEW_Warn("SYSTEMVIEW: Could not record task information. Maximum number of tasks reached.");
      return;
    }

    _aTasks[_NumTasks].TaskID         = TaskID;
    _aTasks[_NumTasks].OSTCBTaskName  = OSTCBTaskName;
    _aTasks[_NumTasks].OSTCBPrio      = OSTCBPrio;
    _aTasks[_NumTasks].OSTCBStkBottom = OSTCBStkBottom;
    _aTasks[_NumTasks].OSTCBStkSize   = OSTCBStkSize;

    _NumTasks++;

    SYSVIEW_SendTaskInfo(TaskID, OSTCBTaskName, (unsigned)OSTCBPrio, (U32)OSTCBStkBottom, (unsigned)OSTCBStkSize);
  }
}

/*********************************************************************
*
*       SYSVIEW_UpdateTask()
*
*  Function description
*    Update a task in the internal list and record its information.
*/
void SYSVIEW_UpdateTask(U32 TaskID, const char* OSTCBTaskName, INT8U OSTCBPrio, OS_STK* OSTCBStkBottom, INT32U OSTCBStkSize) {
  unsigned n;

  if (TaskID != (U32)OSTCBPrioTbl[OS_TASK_IDLE_PRIO]) {
    for (n = 0; n < _NumTasks; n++) {
      if (_aTasks[n].TaskID == TaskID) {
        break;
      }
    }
    if (n < _NumTasks) {
      _aTasks[n].OSTCBTaskName  = OSTCBTaskName;
      _aTasks[n].OSTCBPrio      = OSTCBPrio;
      _aTasks[n].OSTCBStkBottom = OSTCBStkBottom;
      _aTasks[n].OSTCBStkSize   = OSTCBStkSize;

      SYSVIEW_SendTaskInfo(TaskID, OSTCBTaskName, (unsigned)OSTCBPrio, (U32)OSTCBStkBottom, (unsigned)OSTCBStkSize);
    } else {
      SYSVIEW_AddTask(TaskID, OSTCBTaskName, OSTCBPrio, OSTCBStkBottom, OSTCBStkSize);
    }
  }
}

/*********************************************************************
*
*       SYSVIEW_SendTaskInfo()
*
*  Function description
*    Record task information.
*/
void SYSVIEW_SendTaskInfo(U32 TaskID, const char* sName, unsigned Prio, U32 StackBase, unsigned StackSize) {
  SEGGER_SYSVIEW_TASKINFO TaskInfo;

  //
  // Fill all elements with 0 to allow extending the structure in future version without breaking the code
  //
  SYSVIEW_MEMSET(&TaskInfo, 0, sizeof(TaskInfo));
  TaskInfo.TaskID     = TaskID;
  TaskInfo.sName      = sName;
  TaskInfo.Prio       = Prio;
  TaskInfo.StackBase  = StackBase;
  TaskInfo.StackSize  = StackSize;
  SEGGER_SYSVIEW_SendTaskInfo(&TaskInfo);
}


/*********************************************************************
*
*       SYSVIEW_UpdateResource()
*
*  Function description
*    Update a kernel object in the internal list and record its information.
*/
void SYSVIEW_UpdateResource(U32 EventID, const char* OSEventName) {
  unsigned n;

  for (n = 0; n < _NumResources; n++) {
      if (_aResources[n].ResourceId == EventID) {
        break;
      }
    }
    if (n < _NumResources) {
      _aResources[n].sResource  = OSEventName;

      SEGGER_SYSVIEW_NameResource(_aResources[n].ResourceId, _aResources[n].sResource);
    } 
}


/*********************************************************************
*
*       SYSVIEW_RecordU32x4()
*
*  Function description
*    Record an event with 4 parameters
*/
void SYSVIEW_RecordU32x4(unsigned Id, U32 Para0, U32 Para1, U32 Para2, U32 Para3) {
      U8  aPacket[SEGGER_SYSVIEW_INFO_SIZE + 4 * SEGGER_SYSVIEW_QUANTA_U32];
      U8* pPayload;
      //
      pPayload = SEGGER_SYSVIEW_PREPARE_PACKET(aPacket);                // Prepare the packet for SystemView
      pPayload = SEGGER_SYSVIEW_EncodeU32(pPayload, Para0);             // Add the first parameter to the packet
      pPayload = SEGGER_SYSVIEW_EncodeU32(pPayload, Para1);             // Add the second parameter to the packet
      pPayload = SEGGER_SYSVIEW_EncodeU32(pPayload, Para2);             // Add the third parameter to the packet
      pPayload = SEGGER_SYSVIEW_EncodeU32(pPayload, Para3);             // Add the fourth parameter to the packet
      //
      SEGGER_SYSVIEW_SendPacket(&aPacket[0], pPayload, Id);             // Send the packet
}

/*********************************************************************
*
*       SYSVIEW_RecordU32x5()
*
*  Function description
*    Record an event with 5 parameters
*/
void SYSVIEW_RecordU32x5(unsigned Id, U32 Para0, U32 Para1, U32 Para2, U32 Para3, U32 Para4) {
      U8  aPacket[SEGGER_SYSVIEW_INFO_SIZE + 5 * SEGGER_SYSVIEW_QUANTA_U32];
      U8* pPayload;
      //
      pPayload = SEGGER_SYSVIEW_PREPARE_PACKET(aPacket);                // Prepare the packet for SystemView
      pPayload = SEGGER_SYSVIEW_EncodeU32(pPayload, Para0);             // Add the first parameter to the packet
      pPayload = SEGGER_SYSVIEW_EncodeU32(pPayload, Para1);             // Add the second parameter to the packet
      pPayload = SEGGER_SYSVIEW_EncodeU32(pPayload, Para2);             // Add the third parameter to the packet
      pPayload = SEGGER_SYSVIEW_EncodeU32(pPayload, Para3);             // Add the fourth parameter to the packet
      pPayload = SEGGER_SYSVIEW_EncodeU32(pPayload, Para4);             // Add the fifth parameter to the packet
      //
      SEGGER_SYSVIEW_SendPacket(&aPacket[0], pPayload, Id);             // Send the packet
}
/*********************************************************************
*
*       SYSVIEW_RecordU32Register()
*
*  Function description
*    Record an event with 1 parameter and register the resource to be 
*    sent in the system description.
*/
void SYSVIEW_RecordU32Register(unsigned EventId, U32 ResourceId, const char* sResource) {  
  SEGGER_SYSVIEW_NameResource(ResourceId, sResource);
  SEGGER_SYSVIEW_RecordU32(EventId, SEGGER_SYSVIEW_ShrinkId(ResourceId));
#if OS_CFG_TRACE_MAX_RESOURCES > 0
  if (_NumResources >= OS_CFG_TRACE_MAX_RESOURCES) {
    SEGGER_SYSVIEW_Warn("SYSTEMVIEW: Could not register resource name. Maximum number of resources reached.");
    return;
  }

  _aResources[_NumResources].ResourceId = ResourceId;
  _aResources[_NumResources].sResource  = sResource;
  _aResources[_NumResources].Registered = 0;

  _NumResources++;
#endif
}

void SYSVIEW_SendResourceList(void) {
#if OS_CFG_TRACE_MAX_RESOURCES > 0
  unsigned int n;

  for (n = 0; n < _NumResources; n++) {
    if (_aResources[n].Registered == 0) {
      SEGGER_SYSVIEW_NameResource(_aResources[n].ResourceId, _aResources[n].sResource);
      _aResources[n].Registered = 1;
    }
  }
#endif
}

/*********************************************************************
*
*       Public API structures
*
**********************************************************************
*/
// Callbacks provided to SYSTEMVIEW by FreeRTOS
const SEGGER_SYSVIEW_OS_API SYSVIEW_X_OS_TraceAPI = {
  _cbGetTime,
  _cbSendTaskList,
};

/*************************** End of file ****************************/