/*********************************************************************
*               SEGGER MICROCONTROLLER GmbH & Co. KG                 *
*       Solutions for real time microcontroller applications         *
**********************************************************************
*                                                                    *
*       (c) 2015 - 2016  SEGGER Microcontroller GmbH & Co. KG        *
*                                                                    *
*       www.segger.com     Support: support@segger.com               *
*                                                                    *
**********************************************************************
*                                                                    *
*       SEGGER SystemView * Real-time application analysis           *
*                                                                    *
**********************************************************************
*                                                                    *
* All rights reserved.                                               *
*                                                                    *
* * This software may in its unmodified form be freely redistributed *
*   in source form.                                                  *
* * The source code may be modified, provided the source code        *
*   retains the above copyright notice, this list of conditions and  *
*   the following disclaimer.                                        *
* * Modified versions of this software in source or linkable form    *
*   may not be distributed without prior consent of SEGGER.          *
*                                                                    *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "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: V2.40                                    *
*                                                                    *
**********************************************************************
-------------------------- END-OF-HEADER -----------------------------

File    : SEGGER_SYSVIEW_RTThread.c
Purpose : Interface between RT-Thread and System View.
Revision: $Rev: 3745 $
*/

#include "rtthread.h"
#include "SEGGER_SYSVIEW.h"
#include "SEGGER_RTT.h"

#ifndef PKG_USING_SYSTEMVIEW
//#error "SystemView is only works when feature PKG_USING_SYSTEMVIEW is enable."
#endif
static rt_thread_t tidle;

static U64 _cbGetTime(void)
{
    return (U64)(rt_tick_get() * 1000 / RT_TICK_PER_SECOND);
}

static void _cbSendTaskInfo(const rt_thread_t thread)
{
    SEGGER_SYSVIEW_TASKINFO Info;

    rt_enter_critical();
    rt_memset(&Info, 0, sizeof(Info));
    Info.TaskID = (U32)thread;

    Info.sName = thread->name;
    Info.Prio = thread->current_priority;
    Info.StackBase = (U32)thread->stack_addr;
    Info.StackSize = thread->stack_size;

    SEGGER_SYSVIEW_SendTaskInfo(&Info);
    rt_exit_critical();
}

static void _cbSendTaskList(void)
{
    struct rt_thread* thread;
    struct rt_list_node* node;
    struct rt_list_node* list;
    struct rt_object_information *info;

    info = rt_object_get_information(RT_Object_Class_Thread);
    list = &info->object_list;

    tidle = rt_thread_idle_gethandler();

    rt_enter_critical();
    for(node = list->next; node != list; node = node->next)
    {
        thread = rt_list_entry(node, struct rt_thread, list);
        /* skip idle thread */
        if(thread != tidle)
            _cbSendTaskInfo(thread);
    }
    rt_exit_critical();
}

static void _cb_thread_resume(rt_thread_t thread)
{
    SEGGER_SYSVIEW_OnTaskStartReady((unsigned)thread);
}

static void _cb_thread_suspend(rt_thread_t thread)
{
    SEGGER_SYSVIEW_OnTaskStopReady((unsigned)thread, 0);
}

static void _cb_scheduler(rt_thread_t from, rt_thread_t to)
{
    SEGGER_SYSVIEW_OnTaskStopReady((unsigned)from, 0);
    if(to == tidle)
        SEGGER_SYSVIEW_OnIdle();
    else
        SEGGER_SYSVIEW_OnTaskStartExec((unsigned)to);
}

static void _cb_irq_enter(void)
{
    SEGGER_SYSVIEW_RecordEnterISR();
}

static void _cb_irq_leave(void)
{
    rt_thread_t current;
    if(rt_interrupt_get_nest())
    {
        SEGGER_SYSVIEW_RecordExitISR();
        return;
    }

    SEGGER_SYSVIEW_RecordExitISRToScheduler();
    current = rt_thread_self();
    if(current == tidle)
        SEGGER_SYSVIEW_OnIdle();
    else
        SEGGER_SYSVIEW_OnTaskStartExec((unsigned)current);
}

static void _cb_object_attach(struct rt_object* object)
{
    switch(object->type)
    {
    case RT_Object_Class_Thread:
        SEGGER_SYSVIEW_OnTaskCreate((unsigned)object);
        _cbSendTaskInfo((rt_thread_t)object);
        break;
    default:
        break;
    }
}
static void _cb_object_detach(struct rt_object* object)
{
    switch(object->type)
    {
    case RT_Object_Class_Thread:
        SEGGER_SYSVIEW_OnTaskStopExec();
        break;
    default:
        break;
    }
}

void SEGGER_SYSVIEW_RecordObject(unsigned EventID, struct rt_object *object) 
{
	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_EncodeString(pPayload, object->name, RT_NAME_MAX);	// Add object name

    if ((object->type & (~RT_Object_Class_Static)) == RT_Object_Class_Event) 
    {
        pPayload = SEGGER_SYSVIEW_EncodeU32(pPayload, ((rt_event_t)object)->set);
    }
	SEGGER_SYSVIEW_SendPacket(&aPacket[0], pPayload, EventID);			  	// Send the packet
}

static void _cb_object_trytake(struct rt_object *object)
{
    switch (object->type & (~RT_Object_Class_Static))
    {
    case RT_Object_Class_Semaphore:
        SEGGER_SYSVIEW_RecordObject(RTT_TRACE_ID_SEM_TRYTAKE, object);
        break;
    case RT_Object_Class_Mutex:
        SEGGER_SYSVIEW_RecordObject(RTT_TRACE_ID_MUTEX_TRYTAKE, object);
        break;
    case RT_Object_Class_Event:
        SEGGER_SYSVIEW_RecordObject(RTT_TRACE_ID_EVENT_TRYTAKE, object);
        break;
    case RT_Object_Class_MailBox:
        SEGGER_SYSVIEW_RecordObject(RTT_TRACE_ID_MAILBOX_TRYTAKE, object);
        break;
    case RT_Object_Class_MessageQueue:
        SEGGER_SYSVIEW_RecordObject(RTT_TRACE_ID_QUEUE_TRYTAKE, object);
        break;
    }
}


static void _cb_object_take(struct rt_object *object)
{
    switch (object->type & (~RT_Object_Class_Static))
    {
    case RT_Object_Class_Semaphore:
        SEGGER_SYSVIEW_RecordObject(RTT_TRACE_ID_SEM_TAKEN, object);
        break;
    case RT_Object_Class_Mutex:
        SEGGER_SYSVIEW_RecordObject(RTT_TRACE_ID_MUTEX_TAKEN, object);
        break;
    case RT_Object_Class_Event:
        SEGGER_SYSVIEW_RecordObject(RTT_TRACE_ID_EVENT_TAKEN, object);
        break;
    case RT_Object_Class_MailBox:
        SEGGER_SYSVIEW_RecordObject(RTT_TRACE_ID_MAILBOX_TAKEN, object);
        break;
    case RT_Object_Class_MessageQueue:
        SEGGER_SYSVIEW_RecordObject(RTT_TRACE_ID_QUEUE_TAKEN, object);
        break;
    }
}

static void _cb_object_put(struct rt_object *object)
{
    switch (object->type & (~RT_Object_Class_Static))
    {
    case RT_Object_Class_Semaphore:
        SEGGER_SYSVIEW_RecordObject(RTT_TRACE_ID_SEM_RELEASE, object);
        break;
    case RT_Object_Class_Mutex:
        SEGGER_SYSVIEW_RecordObject(RTT_TRACE_ID_MUTEX_RELEASE, object);
        break;
    case RT_Object_Class_Event:
        SEGGER_SYSVIEW_RecordObject(RTT_TRACE_ID_EVENT_RELEASE, object);
        break;
    case RT_Object_Class_MailBox:
        SEGGER_SYSVIEW_RecordObject(RTT_TRACE_ID_MAILBOX_RELEASE, object);
        break;
    case RT_Object_Class_MessageQueue:
        SEGGER_SYSVIEW_RecordObject(RTT_TRACE_ID_QUEUE_RELEASE, object);
        break;
    }
}

// Services provided to SYSVIEW by RT-Thread
const SEGGER_SYSVIEW_OS_API SYSVIEW_X_OS_TraceAPI = {
    _cbGetTime, _cbSendTaskList,
};

// RT-Thread init trace component
int rt_trace_init(void)
{
    tidle = rt_thread_idle_gethandler();
    
    SEGGER_SYSVIEW_Conf();
    
    // register hooks
    rt_object_attach_sethook(_cb_object_attach);
    rt_object_detach_sethook(_cb_object_detach);
    rt_object_trytake_sethook(_cb_object_trytake);
    rt_object_take_sethook(_cb_object_take);
    rt_object_put_sethook(_cb_object_put);
    
    rt_thread_suspend_sethook(_cb_thread_suspend);
    rt_thread_resume_sethook(_cb_thread_resume);

    rt_scheduler_sethook(_cb_scheduler);

    rt_interrupt_enter_sethook(_cb_irq_enter);
    rt_interrupt_leave_sethook(_cb_irq_leave);
    
    rt_kprintf("RTT Control Block Detection Address is 0x%x\n", &_SEGGER_RTT);
    
    return 0;
}
#ifdef PKG_USING_SYSTEMVIEW
INIT_COMPONENT_EXPORT(rt_trace_init);
#endif

int rtt_show_address(int argc, char **argv)
{
    rt_kprintf("RTT Control Block Detection Address is 0x%x\n", &_SEGGER_RTT);
    return RT_EOK;
}
#if(defined(FINSH_USING_MSH) && defined(PKG_USING_SYSTEMVIEW))
FINSH_FUNCTION_EXPORT_ALIAS(rtt_show_address, __cmd_rtt_show_address, Show RTT Control Block Address.);
#endif
/*************************** End of file ****************************/
