/*
************************************************************************************************************************
*                                                      uC/OS-III
*                                                 The Real-Time Kernel
*
*                                  (c) Copyright 2009-2011; Micrium, Inc.; Weston, FL
*                          All rights reserved.  Protected by international copyright laws.
*
*                                               MESSAGE HANDLING SERVICES
*
* File    : OS_MSG.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_msg__c = "$Id: $";
#endif


#if OS_MSG_EN > 0u
/*
************************************************************************************************************************
*                                            EXTEND THE POOL OF 'OS_MSG'
*
* Description: This function is called by your application to add OS_MSGs to the free list of OS_MSGs
*
* Arguments  : p_msg        is a pointer to the base address of an array of OS_MSG and should be declared as follows:
*
*                           OS_MSG  MyNewMsgTbl[size];
*
*              size         is the size of the above array
*
*              p_err        is a pointer to a variable that will contain an error code returned by this function.
*
*                               OS_ERR_MSG_POOL_NULL_PTR
*                               OS_ERR_MSG_POOL_EMPTY
*                               OS_ERR_NONE
*
* Returns    : none
************************************************************************************************************************
*/

void  OSMsgPoolExtend (OS_MSG      *p_msg,
                       OS_MSG_QTY   size,
                       OS_ERR      *p_err)
{
#ifdef OS_SAFETY_CRITICAL
    if (p_err == (OS_ERR *)0) {
        OS_SAFETY_CRITICAL_EXCEPTION();
        return;
    }
#endif

#if OS_CFG_ARG_CHK_EN > 0u
    if (p_msg == (OS_MSG *)0) {
       *p_err = OS_ERR_MSG_POOL_NULL_PTR;
        return;
    }
    if (size == (OS_MSG_QTY)0) {
       *p_err = OS_ERR_MSG_POOL_EMPTY;
        return;
    }
#endif

    OS_MsgPoolCreate(p_msg,                                 /* Create the singly linked list                          */
                     size);
    p_msg[size - 1u].NextPtr = OSMsgPool.NextPtr;           /* Link last OS_MSG of new list to current pool           */
    OSMsgPool.NextPtr       = p_msg;
    OSMsgPool.NbrFree      += size;                         /* All new OS_MSGs are now available                      */
   *p_err                   = OS_ERR_NONE;
}

/*$PAGE*/
/*
************************************************************************************************************************
*                                           CREATE A LINKED LIST OF 'OS_MSG'
*
* Description: This function is called to create a singly linked list of OS_MSGs which is used as a pool of available
*              OS_MSGs to be used for sending messages.
*
* Arguments  : p_msg        is a pointer to the base address of an array of OS_MSG and should be declared as follows:
*              -----
*                               OS_MSG  MyMsgTbl[size];
*
*              size         is the size of the above array
*
* Returns    : none
*
* Note(s)    : 1) This function is INTERNAL to uC/OS-III and your application MUST NOT call it.
************************************************************************************************************************
*/

void  OS_MsgPoolCreate (OS_MSG      *p_msg,
                        OS_MSG_QTY   size)
{
    OS_MSG      *p_msg1;
    OS_MSG      *p_msg2;
    OS_MSG_QTY   i;
    OS_MSG_QTY   loops;



    p_msg1 = p_msg;
    p_msg2 = p_msg;
    p_msg2++;
    loops  = size - 1u;
    for (i = 0u; i < loops; i++) {                          /* Init. list of free OS_MSGs                             */
        p_msg1->NextPtr = p_msg2;
        p_msg1->MsgPtr  = (void      *)0;
        p_msg1->MsgSize = (OS_MSG_SIZE)0u;
        p_msg1->MsgTS   = (CPU_TS     )0u;
        p_msg1++;
        p_msg2++;
    }
    p_msg1->NextPtr = (OS_MSG    *)0;                       /* Last OS_MSG                                            */
    p_msg1->MsgPtr  = (void      *)0;
    p_msg1->MsgSize = (OS_MSG_SIZE)0u;
    p_msg1->MsgTS   = (CPU_TS     )0u;
}

/*$PAGE*/
/*
************************************************************************************************************************
*                                            INITIALIZE THE POOL OF 'OS_MSG'
*
* Description: This function is called by OSInit() to initialize the free list of OS_MSGs.
*
* Argument(s): p_err     is a pointer to a variable that will contain an error code returned by this function.
*
*                            OS_ERR_MSG_POOL_NULL_PTR
*                            OS_ERR_MSG_POOL_EMPTY
*                            OS_ERR_NONE
*
* Returns    : none
*
* Note(s)    : 1) This function is INTERNAL to uC/OS-III and your application MUST NOT call it.
************************************************************************************************************************
*/

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

#if OS_CFG_ARG_CHK_EN > 0u
    if (OSCfg_MsgPoolBasePtr == (OS_MSG *)0) {
       *p_err = OS_ERR_MSG_POOL_NULL_PTR;
        return;
    }
    if (OSCfg_MsgPoolSize == (OS_MSG_QTY)0) {
       *p_err = OS_ERR_MSG_POOL_EMPTY;
        return;
    }
#endif

    OS_MsgPoolCreate(OSCfg_MsgPoolBasePtr,
                    OSCfg_MsgPoolSize);
    OSMsgPool.NextPtr =  OSCfg_MsgPoolBasePtr;
    OSMsgPool.NbrFree =  OSCfg_MsgPoolSize;
    OSMsgPool.NbrUsed = (OS_MSG_QTY)0;
   *p_err             =  OS_ERR_NONE;
}

/*$PAGE*/
/*
************************************************************************************************************************
*                                        RESET MESSAGE QUEUE ENTRIES PEAK TRACKING
*
* Description: This function clear the peak detection mechanism of the message queue
*
* Arguments  : p_msg_q       is a pointer to the OS_MSG_Q structure
*              -------
*
* Returns    : none
*
* Note(s)    : 1) This function is INTERNAL to uC/OS-III and your application MUST NOT call it.
************************************************************************************************************************
*/

void  OS_MsgQEntriesPeakReset (OS_MSG_Q  *p_msg_q)
{
    CPU_SR_ALLOC();


    CPU_CRITICAL_ENTER();
    p_msg_q->NbrEntriesMax = (OS_MSG_QTY)0;
    CPU_CRITICAL_EXIT();
}

/*$PAGE*/
/*
************************************************************************************************************************
*                                        RELEASE ALL MESSAGE IN MESSAGE QUEUE
*
* Description: This function returns all the messages in a message queue to the free list.
*
* Arguments  : p_msg_q       is a pointer to the OS_MSG_Q structure containing messages to free.
*              -------
*
* Returns    : the number of OS_MSGs returned to the free list
*
* Note(s)    : 1) This function is INTERNAL to uC/OS-III and your application MUST NOT call it.
************************************************************************************************************************
*/

OS_MSG_QTY  OS_MsgQFreeAll (OS_MSG_Q  *p_msg_q)
{
    OS_MSG      *p_msg;
    OS_MSG_QTY   qty;



    qty = p_msg_q->NbrEntries;                              /* Get the number of OS_MSGs being freed                  */
    if (p_msg_q->NbrEntries > (OS_MSG_QTY)0) {
        p_msg                   = p_msg_q->InPtr;           /* Point to end of message chain                          */
        p_msg->NextPtr          = OSMsgPool.NextPtr;
        OSMsgPool.NextPtr       = p_msg_q->OutPtr;          /* Point to beginning of message chain                    */
        OSMsgPool.NbrUsed      -= p_msg_q->NbrEntries;      /* Update statistics for free list of messages            */
        OSMsgPool.NbrFree      += p_msg_q->NbrEntries;
        p_msg_q->NbrEntries     = (OS_MSG_QTY)0;            /* Flush the message queue                                */
        p_msg_q->NbrEntriesMax  = (OS_MSG_QTY)0;
        p_msg_q->InPtr          = (OS_MSG   *)0;
        p_msg_q->OutPtr         = (OS_MSG   *)0;
    }
    return (qty);
}

/*$PAGE*/
/*
************************************************************************************************************************
*                                               INITIALIZE A MESSAGE QUEUE
*
* Description: This function is called to initialize a message queue
*
* Arguments  : p_msg_q      is a pointer to the message queue to initialize
*              -------
*
*              max          is the maximum number of entries that a message queue can have.
*
* Returns    : none
*
* Note(s)    : 1) This function is INTERNAL to uC/OS-III and your application MUST NOT call it.
************************************************************************************************************************
*/

void  OS_MsgQInit (OS_MSG_Q     *p_msg_q,
                   OS_MSG_QTY    size)
{
    p_msg_q->NbrEntriesSize = (OS_MSG_QTY)size;
    p_msg_q->NbrEntries     = (OS_MSG_QTY)0;
    p_msg_q->NbrEntriesMax  = (OS_MSG_QTY)0;
    p_msg_q->InPtr          = (OS_MSG   *)0;
    p_msg_q->OutPtr         = (OS_MSG   *)0;
}

/*$PAGE*/
/*
************************************************************************************************************************
*                                           RETRIEVE MESSAGE FROM MESSAGE QUEUE
*
* Description: This function retrieves a message from a message queue
*
* Arguments  : p_msg_q     is a pointer to the message queue where we want to extract the message from
*              -------
*
*              p_msg_size  is a pointer to where the size (in bytes) of the message will be placed
*
*              p_ts        is a pointer to where the time stamp will be placed
*
*              p_err       is a pointer to an error code that will be returned from this call.
*
*                              OS_ERR_Q_EMPTY
*                              OS_ERR_NONE
*
* Returns    : The message (a pointer)
*
* Note(s)    : 1) This function is INTERNAL to uC/OS-III and your application MUST NOT call it.
************************************************************************************************************************
*/

void  *OS_MsgQGet (OS_MSG_Q    *p_msg_q,
                   OS_MSG_SIZE *p_msg_size,
                   CPU_TS      *p_ts,
                   OS_ERR      *p_err)
{
    OS_MSG  *p_msg;
    void    *p_void;



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

    if (p_msg_q->NbrEntries == (OS_MSG_QTY)0) {
       *p_msg_size = (OS_MSG_SIZE)0;
        if (p_ts != (CPU_TS *)0) {
           *p_ts  = (CPU_TS  )0;
        }
       *p_err = OS_ERR_Q_EMPTY;
        return ((void *)0);
    }

    p_msg           = p_msg_q->OutPtr;
    p_void          = p_msg->MsgPtr;
    *p_msg_size     = p_msg->MsgSize;
    if (p_ts != (CPU_TS *)0) {
       *p_ts  = p_msg->MsgTS;
    }
    p_msg_q->OutPtr = p_msg->NextPtr;
    if (p_msg_q->OutPtr == (OS_MSG *)0) {
        p_msg_q->InPtr      = (OS_MSG   *)0;
        p_msg_q->NbrEntries = (OS_MSG_QTY)0;
    } else {
        p_msg_q->NbrEntries--;
    }
    p_msg->NextPtr    = OSMsgPool.NextPtr;                  /* Return message control block to free list              */
    OSMsgPool.NextPtr = p_msg;
    OSMsgPool.NbrFree++;
    OSMsgPool.NbrUsed--;
    *p_err            = OS_ERR_NONE;
    return (p_void);
}

/*
************************************************************************************************************************
*                                           DEPOSIT MESSAGE IN MESSAGE QUEUE
*
* Description: This function places a message in a message queue
*
* Arguments  : p_msg_q     is a pointer to the OS_TCB of the task to post the message to
*              -------
*
*              p_void      is a pointer to the message to send.
*
*              msg_size    is the size of the message (in bytes)
*
*              opt         specifies whether the message will be posted in FIFO or LIFO order
*
*                              OS_OPT_POST_FIFO
*                              OS_OPT_POST_LIFO
*
*              ts          is a timestamp as to when the message was posted
*
*              p_err       is a pointer to a variable that will contain an error code returned by this function.
*
*                              OS_ERR_Q_MAX           if the queue is full
*                              OS_ERR_MSG_POOL_EMPTY  if we no longer have any OS_MSG to use
*                              OS_ERR_NONE            the message was deposited in the queue
*
* Returns    : none
*
* Note(s)    : 1) This function is INTERNAL to uC/OS-III and your application MUST NOT call it.
************************************************************************************************************************
*/

void  OS_MsgQPut (OS_MSG_Q    *p_msg_q,
                  void        *p_void,
                  OS_MSG_SIZE  msg_size,
                  OS_OPT       opt,
                  CPU_TS       ts,
                  OS_ERR      *p_err)
{
    OS_MSG  *p_msg;
    OS_MSG  *p_msg_in;



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

    if (p_msg_q->NbrEntries >= p_msg_q->NbrEntriesSize) {
       *p_err = OS_ERR_Q_MAX;                               /* Message queue cannot accept any more messages          */
        return;
    }

    if (OSMsgPool.NbrFree == (OS_MSG_QTY)0) {
       *p_err = OS_ERR_MSG_POOL_EMPTY;                      /* No more OS_MSG to use                                  */
        return;
    }

    p_msg             = OSMsgPool.NextPtr;                  /* Remove message control block from free list            */
    OSMsgPool.NextPtr = p_msg->NextPtr;
    OSMsgPool.NbrFree--;
    OSMsgPool.NbrUsed++;
    if (p_msg_q->NbrEntries == (OS_MSG_QTY)0) {             /* Is this first message placed in the queue?             */
        p_msg_q->InPtr         = p_msg;                     /* Yes                                                    */
        p_msg_q->OutPtr        = p_msg;
        p_msg_q->NbrEntries    = (OS_MSG_QTY)1;
    } else {
        if ((opt & OS_OPT_POST_LIFO) == OS_OPT_POST_FIFO) { /* Assume FIFO if not LIFO                                */
            p_msg_in           = p_msg_q->InPtr;            /* FIFO                                                   */
            p_msg_in->NextPtr  = p_msg;
            p_msg->NextPtr     = (OS_MSG *)0;
            p_msg_q->InPtr     = p_msg;
        } else {
            p_msg->NextPtr     = p_msg_q->OutPtr;           /* LIFO                                                   */
            p_msg_q->OutPtr    = p_msg;
        }
        p_msg_q->NbrEntries++;
    }
    if (p_msg_q->NbrEntries > p_msg_q->NbrEntriesMax) {
        p_msg_q->NbrEntriesMax = p_msg_q->NbrEntries;
    }
    p_msg->MsgPtr  = p_void;                                /* Deposit message in the message queue entry             */
    p_msg->MsgSize = msg_size;
    p_msg->MsgTS   = ts;
   *p_err          = OS_ERR_NONE;
}
#endif
