/*
************************************************************************************************************************
*                                                      uC/OS-III
*                                                 The Real-Time Kernel
*
*                                  (c) Copyright 2009-2011; Micrium, Inc.; Weston, FL
*                          All rights reserved.  Protected by international copyright laws.
*
*                                                  ISR QUEUE MANAGEMENT
*
* File    : OS_INT.C
* By      : JJL
* Version : V3.02.0
*
* LICENSING TERMS:
* ---------------
*               uC/OS-III is provided in source form to registered licensees ONLY.  It is
*               illegal to distribute this source code to any third party unless you receive
*               written permission by an authorized Micrium representative.  Knowledge of
*               the source code may NOT be used to develop a similar product.
*
*               Please help us continue to provide the Embedded community with the finest
*               software available.  Your honesty is greatly appreciated.
*
*               You can contact us at www.micrium.com.
************************************************************************************************************************
*/

#include  <os.h>

#ifdef VSC_INCLUDE_SOURCE_FILE_NAMES
const  CPU_CHAR  *os_int__c = "$Id: $";
#endif


#if OS_CFG_ISR_POST_DEFERRED_EN > 0u
/*$PAGE*/
/*
************************************************************************************************************************
*                                                   POST TO ISR QUEUE
*
* Description: This function places contents of posts into an intermediate queue to help defer processing of interrupts
*              at the task level.
*
* Arguments  : type       is the type of kernel object the post is destined to:
*
*                             OS_OBJ_TYPE_SEM
*                             OS_OBJ_TYPE_Q
*                             OS_OBJ_TYPE_FLAG
*                             OS_OBJ_TYPE_TASK_MSG
*                             OS_OBJ_TYPE_TASK_SIGNAL
*
*              p_obj      is a pointer to the kernel object to post to.  This can be a pointer to a semaphore,
*              -----      a message queue or a task control clock.
*
*              p_void     is a pointer to a message that is being posted.  This is used when posting to a message
*                         queue or directly to a task.
*
*              msg_size   is the size of the message being posted
*
*              flags      if the post is done to an event flag group then this corresponds to the flags being
*                         posted
*
*              ts         is a timestamp as to when the post was done
*
*              opt        this corresponds to post options and applies to:
*
*                             OSFlagPost()
*                             OSSemPost()
*                             OSQPost()
*                             OSTaskQPost()
*
*              p_err      is a pointer to a variable that will contain an error code returned by this function.
*
*                             OS_ERR_NONE         if the post to the ISR queue was successful
*                             OS_ERR_INT_Q_FULL   if the ISR queue is full and cannot accepts any further posts.  This
*                                                 generally indicates that you are receiving interrupts faster than you
*                                                 can process them or, that you didn't make the ISR queue large enough.
*
* Returns    : none
*
* Note(s)    : none
************************************************************************************************************************
*/

void  OS_IntQPost (OS_OBJ_TYPE    type,
                   void          *p_obj,
                   void          *p_void,
                   OS_MSG_SIZE    msg_size,
                   OS_FLAGS       flags,
                   OS_OPT         opt,
                   CPU_TS         ts,
                   OS_ERR        *p_err)
{
    CPU_SR_ALLOC();



#ifdef OS_SAFETY_CRITICAL
    if (p_err == (OS_ERR *)0) {
        OS_SAFETY_CRITICAL_EXCEPTION();
        return;
    }
#endif

    CPU_CRITICAL_ENTER();
    if (OSIntQNbrEntries < OSCfg_IntQSize) {                /* Make sure we haven't already filled the ISR queue      */
        OSIntQNbrEntries++;

        if (OSIntQMaxNbrEntries < OSIntQNbrEntries) {
            OSIntQMaxNbrEntries = OSIntQNbrEntries;
        }

        OSIntQInPtr->Type       = type;                     /* Save object type being posted                          */
        OSIntQInPtr->ObjPtr     = p_obj;                    /* Save pointer to object being posted                    */
        OSIntQInPtr->MsgPtr     = p_void;                   /* Save pointer to message if posting to a message queue  */
        OSIntQInPtr->MsgSize    = msg_size;                 /* Save the message size   if posting to a message queue  */
        OSIntQInPtr->Flags      = flags;                    /* Save the flags if posting to an event flag group       */
        OSIntQInPtr->Opt        = opt;                      /* Save post options                                      */
        OSIntQInPtr->TS         = ts;                       /* Save time stamp                                        */

        OSIntQInPtr             =  OSIntQInPtr->NextPtr;    /* Point to the next interrupt handler queue entry        */

        OSRdyList[0].NbrEntries = (OS_OBJ_QTY)1;            /* Make the interrupt handler task ready to run           */
        OSRdyList[0].HeadPtr    = &OSIntQTaskTCB;
        OSRdyList[0].TailPtr    = &OSIntQTaskTCB;
        OS_PrioInsert(0u);                                  /* Add task priority 0 in the priority table              */
        OSPrioSaved             = OSPrioCur;                /* Save current priority                                  */

       *p_err                   = OS_ERR_NONE;
    } else {
        OSIntQOvfCtr++;                                     /* Count the number of ISR queue overflows                */
       *p_err                   = OS_ERR_INT_Q_FULL;
    }
    CPU_CRITICAL_EXIT();
}

/*$PAGE*/
/*
************************************************************************************************************************
*                                               INTERRUPT QUEUE MANAGEMENT TASK
*
* Description: This task is created by OS_IntQTaskInit().
*
* Arguments  : p_arg     is a pointer to an optional argument that is passed during task creation.  For this function
*                        the argument is not used and will be a NULL pointer.
*
* Returns    : none
************************************************************************************************************************
*/

void  OS_IntQRePost (void)
{
    void         *p_obj;
    void         *p_void;
    OS_ERR        err;
    OS_FLAGS      flags;
    CPU_TS        ts;
    OS_OBJ_TYPE   type;
    OS_OPT        opt;
    OS_MSG_SIZE   msg_size;
    CPU_SR_ALLOC();



    CPU_CRITICAL_ENTER();
    type         = OSIntQOutPtr->Type;                      /* Get local copy of queue item contents                  */
    p_obj        = OSIntQOutPtr->ObjPtr;
    p_void       = OSIntQOutPtr->MsgPtr;
    msg_size     = OSIntQOutPtr->MsgSize;
    flags        = OSIntQOutPtr->Flags;
    opt          = OSIntQOutPtr->Opt;
    ts           = OSIntQOutPtr->TS;
    OSIntQOutPtr = OSIntQOutPtr->NextPtr;                   /* Point to next item in the ISR queue                    */
    CPU_CRITICAL_EXIT();
    switch (type) {                                         /* Re-post to task                                        */
        case OS_OBJ_TYPE_FLAG:
#if OS_CFG_FLAG_EN > 0u
             (void)OS_FlagPost((OS_FLAG_GRP *)p_obj,
                               (OS_FLAGS     )flags,
                               (OS_OPT       )opt,
                               (CPU_TS       )ts,
                               (OS_ERR      *)&err);
#endif
             break;

        case OS_OBJ_TYPE_Q:
#if OS_CFG_Q_EN > 0u
             OS_QPost((OS_Q      *)p_obj,
                      (void      *)p_void,
                      (OS_MSG_SIZE)msg_size,
                      (OS_OPT     )opt,
                      (CPU_TS     )ts,
                      (OS_ERR    *)&err);
#endif
             break;

        case OS_OBJ_TYPE_SEM:
#if OS_CFG_SEM_EN > 0u
             (void)OS_SemPost((OS_SEM *)p_obj,
                              (OS_OPT  )opt,
                              (CPU_TS  )ts,
                              (OS_ERR *)&err);
#endif
             break;

        case OS_OBJ_TYPE_TASK_MSG:
#if OS_CFG_TASK_Q_EN > 0u
             OS_TaskQPost((OS_TCB    *)p_obj,
                          (void      *)p_void,
                          (OS_MSG_SIZE)msg_size,
                          (OS_OPT     )opt,
                          (CPU_TS     )ts,
                          (OS_ERR    *)&err);
#endif
             break;

        case OS_OBJ_TYPE_TASK_SIGNAL:
             (void)OS_TaskSemPost((OS_TCB *)p_obj,
                                  (OS_OPT  )opt,
                                  (CPU_TS  )ts,
                                  (OS_ERR *)&err);
             break;

        case OS_OBJ_TYPE_TICK:
#if OS_CFG_SCHED_ROUND_ROBIN_EN > 0u
             OS_SchedRoundRobin(&OSRdyList[OSPrioSaved]);
#endif

             (void)OS_TaskSemPost((OS_TCB *)&OSTickTaskTCB,                /* Signal tick task                        */
                                  (OS_OPT  )OS_OPT_POST_NONE,
                                  (CPU_TS  )ts,
                                  (OS_ERR *)&err);
#if OS_CFG_TMR_EN > 0u
             OSTmrUpdateCtr--;
             if (OSTmrUpdateCtr == (OS_CTR)0u) {
                 OSTmrUpdateCtr = OSTmrUpdateCnt;
                 ts             = OS_TS_GET();                             /* Get timestamp                           */
                 (void)OS_TaskSemPost((OS_TCB *)&OSTmrTaskTCB,             /* Signal timer task                       */
                                      (OS_OPT  )OS_OPT_POST_NONE,
                                      (CPU_TS  )ts,
                                      (OS_ERR *)&err);
             }
#endif
             break;

        default:
             break;
    }
}

/*$PAGE*/
/*
************************************************************************************************************************
*                                               INTERRUPT QUEUE MANAGEMENT TASK
*
* Description: This task is created by OS_IntQTaskInit().
*
* Arguments  : p_arg     is a pointer to an optional argument that is passed during task creation.  For this function
*                        the argument is not used and will be a NULL pointer.
*
* Returns    : none
************************************************************************************************************************
*/

void  OS_IntQTask (void *p_arg)
{
    CPU_BOOLEAN  done;
    CPU_TS       ts_start;
    CPU_TS       ts_end;
    CPU_SR_ALLOC();



    p_arg = p_arg;                                          /* Not using 'p_arg', prevent compiler warning            */
    while (DEF_ON) {
        done = DEF_FALSE;
        while (done == DEF_FALSE) {
            if (OSIntQNbrEntries == (OS_OBJ_QTY)0u) {
                CPU_CRITICAL_ENTER();
                OSRdyList[0].NbrEntries = (OS_OBJ_QTY)0u;   /* Remove from ready list                                 */
                OSRdyList[0].HeadPtr    = (OS_TCB   *)0;
                OSRdyList[0].TailPtr    = (OS_TCB   *)0;
                OS_PrioRemove(0u);                          /* Remove from the priority table                         */
                CPU_CRITICAL_EXIT();
                OSSched();
                done = DEF_TRUE;                            /* No more entries in the queue, we are done              */
            } else {
                ts_start = OS_TS_GET();
                OS_IntQRePost();
                ts_end   = OS_TS_GET() - ts_start;          /* Measure execution time of tick task                    */
                if (ts_end > OSIntQTaskTimeMax) {
                    OSIntQTaskTimeMax = ts_end;
                }
                CPU_CRITICAL_ENTER();
                OSIntQNbrEntries--;
                CPU_CRITICAL_EXIT();
            }
        }
    }
}

/*$PAGE*/
/*
************************************************************************************************************************
*                                                 INITIALIZE THE ISR QUEUE
*
* Description: This function is called by OSInit() to initialize the ISR queue.
*
* Arguments  : p_err    is a pointer to a variable that will contain an error code returned by this function.
*
*                           OS_ERR_INT_Q             If you didn't provide an ISR queue in OS_CFG.C
*                           OS_ERR_INT_Q_SIZE        If you didn't specify a large enough ISR queue.
*                           OS_ERR_STK_INVALID       If you specified a NULL pointer for the task of the ISR task
*                                                    handler
*                           OS_ERR_STK_SIZE_INVALID  If you didn't specify a stack size greater than the minimum
*                                                    specified by OS_CFG_STK_SIZE_MIN
*                           OS_ERR_???               An error code returned by OSTaskCreate().
*
* Returns    : none
*
* Note(s)    : none
************************************************************************************************************************
*/

void  OS_IntQTaskInit (OS_ERR  *p_err)
{
    OS_INT_Q      *p_int_q;
    OS_INT_Q      *p_int_q_next;
    OS_OBJ_QTY     i;



#ifdef OS_SAFETY_CRITICAL
    if (p_err == (OS_ERR *)0) {
        OS_SAFETY_CRITICAL_EXCEPTION();
        return;
    }
#endif

    OSIntQOvfCtr = (OS_QTY)0u;                              /* Clear the ISR queue overflow counter                   */

    if (OSCfg_IntQBasePtr == (OS_INT_Q *)0) {
       *p_err = OS_ERR_INT_Q;
        return;
    }

    if (OSCfg_IntQSize < (OS_OBJ_QTY)2u) {
       *p_err = OS_ERR_INT_Q_SIZE;
        return;
    }

    OSIntQTaskTimeMax = (CPU_TS)0;

    p_int_q           = OSCfg_IntQBasePtr;                  /* Initialize the circular ISR queue                      */
    p_int_q_next      = p_int_q;
    p_int_q_next++;
    for (i = 0u; i < OSCfg_IntQSize; i++) {
        p_int_q->Type    =  OS_OBJ_TYPE_NONE;
        p_int_q->ObjPtr  = (void      *)0;
        p_int_q->MsgPtr  = (void      *)0;
        p_int_q->MsgSize = (OS_MSG_SIZE)0u;
        p_int_q->Flags   = (OS_FLAGS   )0u;
        p_int_q->Opt     = (OS_OPT     )0u;
        p_int_q->NextPtr = p_int_q_next;
        p_int_q++;
        p_int_q_next++;
    }
    p_int_q--;
    p_int_q_next        = OSCfg_IntQBasePtr;
    p_int_q->NextPtr    = p_int_q_next;
    OSIntQInPtr         = p_int_q_next;
    OSIntQOutPtr        = p_int_q_next;
    OSIntQNbrEntries    = (OS_OBJ_QTY)0u;
    OSIntQMaxNbrEntries = (OS_OBJ_QTY)0u;

                                                            /* -------------- CREATE THE ISR QUEUE TASK ------------- */
    if (OSCfg_IntQTaskStkBasePtr == (CPU_STK *)0) {
       *p_err = OS_ERR_STK_INVALID;
        return;
    }

    if (OSCfg_IntQTaskStkSize < OSCfg_StkSizeMin) {
       *p_err = OS_ERR_STK_SIZE_INVALID;
        return;
    }

    OSTaskCreate((OS_TCB     *)&OSIntQTaskTCB,
                 (CPU_CHAR   *)((void *)"uC/OS-III ISR Queue Task"),
                 (OS_TASK_PTR )OS_IntQTask,
                 (void       *)0,
                 (OS_PRIO     )0u,                          /* This task is ALWAYS at priority '0' (i.e. highest)     */
                 (CPU_STK    *)OSCfg_IntQTaskStkBasePtr,
                 (CPU_STK_SIZE)OSCfg_IntQTaskStkLimit,
                 (CPU_STK_SIZE)OSCfg_IntQTaskStkSize,
                 (OS_MSG_QTY  )0u,
                 (OS_TICK     )0u,
                 (void       *)0,
                 (OS_OPT      )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),
                 (OS_ERR     *)p_err);
}

#endif
