/*
*********************************************************************************************************
*                                              uC/OS-III
*                                        The Real-Time Kernel
*
*                    Copyright 2009-2021 Silicon Laboratories Inc. www.silabs.com
*
*                                 SPDX-License-Identifier: APACHE-2.0
*
*               This software is subject to an open source license and is distributed by
*                Silicon Laboratories Inc. pursuant to the terms of the Apache License,
*                    Version 2.0 available at www.apache.org/licenses/LICENSE-2.0.
*
*********************************************************************************************************
*/

/*
*********************************************************************************************************
*                                         EVENT FLAG MANAGEMENT
*
* File    : os_flag.c
* Version : V3.08.01
*********************************************************************************************************
*/

#define  MICRIUM_SOURCE
#include "os.h"

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


#if (OS_CFG_FLAG_EN > 0u)

/*
************************************************************************************************************************
*                                                 CREATE AN EVENT FLAG
*
* Description: This function is called to create an event flag group.
*
* Arguments  : p_grp          is a pointer to the event flag group to create
*
*              p_name         is the name of the event flag group
*
*              flags          contains the initial value to store in the event flag group (typically 0).
*
*              p_err          is a pointer to an error code which will be returned to your application:
*
*                                 OS_ERR_NONE                    If the call was successful
*                                 OS_ERR_CREATE_ISR              If you attempted to create an Event Flag from an ISR
*                                 OS_ERR_ILLEGAL_CREATE_RUN_TIME If you are trying to create the Event Flag after you
*                                                                   called OSSafetyCriticalStart().
*                                 OS_ERR_OBJ_PTR_NULL            If 'p_grp' is a NULL pointer
*                                 OS_ERR_OBJ_CREATED             If the event flag was already created
*
* Returns    : none
************************************************************************************************************************
*/

void  OSFlagCreate (OS_FLAG_GRP  *p_grp,
                    CPU_CHAR     *p_name,
                    OS_FLAGS      flags,
                    OS_ERR       *p_err)
{
    CPU_SR_ALLOC();


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

#ifdef OS_SAFETY_CRITICAL_IEC61508
    if (OSSafetyCriticalStartFlag == OS_TRUE) {
       *p_err = OS_ERR_ILLEGAL_CREATE_RUN_TIME;
        return;
    }
#endif

#if (OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u)
    if (OSIntNestingCtr > 0u) {                                 /* See if called from ISR ...                           */
       *p_err = OS_ERR_CREATE_ISR;                              /* ... can't CREATE from an ISR                         */
        return;
    }
#endif

#if (OS_CFG_ARG_CHK_EN > 0u)
    if (p_grp == (OS_FLAG_GRP *)0) {                            /* Validate 'p_grp'                                     */
       *p_err = OS_ERR_OBJ_PTR_NULL;
        return;
    }
#endif

    CPU_CRITICAL_ENTER();
#if (OS_OBJ_TYPE_REQ > 0u)
#if (OS_CFG_OBJ_CREATED_CHK_EN > 0u)
    if (p_grp->Type == OS_OBJ_TYPE_FLAG) {
        CPU_CRITICAL_EXIT();
        *p_err = OS_ERR_OBJ_CREATED;
        return;
    }
#endif
    p_grp->Type    = OS_OBJ_TYPE_FLAG;                          /* Set to event flag group type                         */
#endif
#if (OS_CFG_DBG_EN > 0u)
    p_grp->NamePtr = p_name;
#else
    (void)p_name;
#endif
    p_grp->Flags   = flags;                                     /* Set to desired initial value                         */
#if (OS_CFG_TS_EN > 0u)
    p_grp->TS      = 0u;
#endif
    OS_PendListInit(&p_grp->PendList);

#if (OS_CFG_DBG_EN > 0u)
    OS_FlagDbgListAdd(p_grp);
    OSFlagQty++;
#endif

    OS_TRACE_FLAG_CREATE(p_grp, p_name);

    CPU_CRITICAL_EXIT();
   *p_err = OS_ERR_NONE;
}


/*
************************************************************************************************************************
*                                             DELETE AN EVENT FLAG GROUP
*
* Description: This function deletes an event flag group and readies all tasks pending on the event flag group.
*
* Arguments  : p_grp     is a pointer to the desired event flag group.
*
*              opt       determines delete options as follows:
*
*                            OS_OPT_DEL_NO_PEND           Deletes the event flag group ONLY if no task pending
*                            OS_OPT_DEL_ALWAYS            Deletes the event flag group even if tasks are waiting.
*                                                         In this case, all the tasks pending will be readied.
*
*              p_err     is a pointer to an error code that can contain one of the following values:
*
*                            OS_ERR_NONE                    The call was successful and the event flag group was deleted
*                            OS_ERR_DEL_ISR                 If you attempted to delete the event flag group from an ISR
*                            OS_ERR_ILLEGAL_DEL_RUN_TIME    If you are trying to delete the event flag group after you
*                                                             called OSStart()
*                            OS_ERR_OBJ_PTR_NULL            If 'p_grp' is a NULL pointer
*                            OS_ERR_OBJ_TYPE                If you didn't pass a pointer to an event flag group
*                            OS_ERR_OPT_INVALID             An invalid option was specified
*                            OS_ERR_OS_NOT_RUNNING          If uC/OS-III is not running yet
*                            OS_ERR_TASK_WAITING            One or more tasks were waiting on the event flag group
*
* Returns    : == 0          if no tasks were waiting on the event flag group, or upon error.
*              >  0          if one or more tasks waiting on the event flag group are now readied and informed.
*
* Note(s)    : 1) This function must be used with care.  Tasks that would normally expect the presence of the event flag
*                 group MUST check the return code of OSFlagPost and OSFlagPend().
************************************************************************************************************************
*/

#if (OS_CFG_FLAG_DEL_EN > 0u)
OS_OBJ_QTY  OSFlagDel (OS_FLAG_GRP  *p_grp,
                       OS_OPT        opt,
                       OS_ERR       *p_err)
{
    OS_OBJ_QTY     nbr_tasks;
    OS_PEND_LIST  *p_pend_list;
    OS_TCB        *p_tcb;
    CPU_TS         ts;
    CPU_SR_ALLOC();


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

    OS_TRACE_FLAG_DEL_ENTER(p_grp, opt);

#ifdef OS_SAFETY_CRITICAL_IEC61508
    if (OSSafetyCriticalStartFlag == OS_TRUE) {
        OS_TRACE_FLAG_DEL_EXIT(OS_ERR_ILLEGAL_DEL_RUN_TIME);
       *p_err = OS_ERR_ILLEGAL_DEL_RUN_TIME;
        return (0u);
    }
#endif

#if (OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u)
    if (OSIntNestingCtr > 0u) {                                 /* See if called from ISR ...                           */
       *p_err = OS_ERR_DEL_ISR;                                 /* ... can't DELETE from an ISR                         */
        OS_TRACE_FLAG_DEL_EXIT(OS_ERR_DEL_ISR);
        return (0u);
    }
#endif

#if (OS_CFG_INVALID_OS_CALLS_CHK_EN > 0u)
    if (OSRunning != OS_STATE_OS_RUNNING) {                     /* Is the kernel running?                               */
        OS_TRACE_FLAG_DEL_EXIT(OS_ERR_OS_NOT_RUNNING);
       *p_err = OS_ERR_OS_NOT_RUNNING;
        return (0u);
    }
#endif

#if (OS_CFG_ARG_CHK_EN > 0u)
    if (p_grp == (OS_FLAG_GRP *)0) {                            /* Validate 'p_grp'                                     */
        OS_TRACE_FLAG_DEL_EXIT(OS_ERR_OBJ_PTR_NULL);
       *p_err  = OS_ERR_OBJ_PTR_NULL;
        return (0u);
    }
#endif

#if (OS_CFG_OBJ_TYPE_CHK_EN > 0u)
    if (p_grp->Type != OS_OBJ_TYPE_FLAG) {                      /* Validate event group object                          */
        OS_TRACE_FLAG_DEL_EXIT(OS_ERR_OBJ_TYPE);
       *p_err = OS_ERR_OBJ_TYPE;
        return (0u);
    }
#endif
    CPU_CRITICAL_ENTER();
    p_pend_list = &p_grp->PendList;
    nbr_tasks   = 0u;
    switch (opt) {
        case OS_OPT_DEL_NO_PEND:                                /* Delete group if no task waiting                      */
             if (p_pend_list->HeadPtr == (OS_TCB *)0) {
#if (OS_CFG_DBG_EN > 0u)
                 OS_FlagDbgListRemove(p_grp);
                 OSFlagQty--;
#endif
                 OS_TRACE_FLAG_DEL(p_grp);
                 OS_FlagClr(p_grp);

                 CPU_CRITICAL_EXIT();

                *p_err = OS_ERR_NONE;
             } else {
                 CPU_CRITICAL_EXIT();
                *p_err = OS_ERR_TASK_WAITING;
             }
             break;

        case OS_OPT_DEL_ALWAYS:                                 /* Always delete the event flag group                   */
#if (OS_CFG_TS_EN > 0u)
             ts = OS_TS_GET();                                  /* Get local time stamp so all tasks get the same time  */
#else
             ts = 0u;
#endif
             while (p_pend_list->HeadPtr != (OS_TCB *)0) {      /* Remove all tasks from the pend list                  */
                 p_tcb = p_pend_list->HeadPtr;
                 OS_PendAbort(p_tcb,
                              ts,
                              OS_STATUS_PEND_DEL);
                 nbr_tasks++;
             }
#if (OS_CFG_DBG_EN > 0u)
             OS_FlagDbgListRemove(p_grp);
             OSFlagQty--;
#endif
             OS_TRACE_FLAG_DEL(p_grp);

             OS_FlagClr(p_grp);
             CPU_CRITICAL_EXIT();

             OSSched();                                         /* Find highest priority task ready to run              */
            *p_err = OS_ERR_NONE;
             break;

        default:
             CPU_CRITICAL_EXIT();
            *p_err = OS_ERR_OPT_INVALID;
             break;
    }

    OS_TRACE_FLAG_DEL_EXIT(*p_err);

    return (nbr_tasks);
}
#endif


/*
************************************************************************************************************************
*                                             WAIT ON AN EVENT FLAG GROUP
*
* Description: This function is called to wait for a combination of bits to be set in an event flag group.  Your
*              application can wait for ANY bit to be set or ALL bits to be set.
*
* Arguments  : p_grp         is a pointer to the desired event flag group.
*
*              flags         Is a bit pattern indicating which bit(s) (i.e. flags) you wish to wait for.
*                            The bits you want are specified by setting the corresponding bits in 'flags'.
*                            e.g. if your application wants to wait for bits 0 and 1 then 'flags' would contain 0x03.
*
*              timeout       is an optional timeout (in clock ticks) that your task will wait for the
*                            desired bit combination.  If you specify 0, however, your task will wait
*                            forever at the specified event flag group or, until a message arrives.
*
*              opt           specifies whether you want ALL bits to be set or ANY of the bits to be set.
*                            You can specify the 'ONE' of the following arguments:
*
*                                OS_OPT_PEND_FLAG_CLR_ALL   You will wait for ALL bits in 'flags' to be clear (0)
*                                OS_OPT_PEND_FLAG_CLR_ANY   You will wait for ANY bit  in 'flags' to be clear (0)
*                                OS_OPT_PEND_FLAG_SET_ALL   You will wait for ALL bits in 'flags' to be set   (1)
*                                OS_OPT_PEND_FLAG_SET_ANY   You will wait for ANY bit  in 'flags' to be set   (1)
*
*                            You can 'ADD' OS_OPT_PEND_FLAG_CONSUME if you want the event flag to be 'consumed' by
*                                      the call.  Example, to wait for any flag in a group AND then clear
*                                      the flags that are present, set 'wait_opt' to:
*
*                                      OS_OPT_PEND_FLAG_SET_ANY + OS_OPT_PEND_FLAG_CONSUME
*
*                            You can also 'ADD' the type of pend with 'ONE' of the two option:
*
*                                OS_OPT_PEND_NON_BLOCKING   Task will NOT block if flags are not available
*                                OS_OPT_PEND_BLOCKING       Task will     block if flags are not available
*
*              p_ts          is a pointer to a variable that will receive the timestamp of when the event flag group was
*                            posted, aborted or the event flag group deleted.  If you pass a NULL pointer (i.e. (CPU_TS *)0)
*                            then you will not get the timestamp.  In other words, passing a NULL pointer is valid and
*                            indicates that you don't need the timestamp.
*
*              p_err         is a pointer to an error code and can be:
*
*                                OS_ERR_NONE                The desired bits have been set within the specified 'timeout'
*                                OS_ERR_OBJ_DEL             If the event group was deleted
*                                OS_ERR_OBJ_PTR_NULL        If 'p_grp' is a NULL pointer.
*                                OS_ERR_OBJ_TYPE            You are not pointing to an event flag group
*                                OS_ERR_OPT_INVALID         You didn't specify a proper 'opt' argument
*                                OS_ERR_OS_NOT_RUNNING      If uC/OS-III is not running yet
*                                OS_ERR_PEND_ABORT          The wait on the flag was aborted
*                                OS_ERR_PEND_ISR            If you tried to PEND from an ISR
*                                OS_ERR_PEND_WOULD_BLOCK    If you specified non-blocking but the flags were not
*                                                           available
*                                OS_ERR_SCHED_LOCKED        If you called this function when the scheduler is locked
*                                OS_ERR_STATUS_INVALID      If the pend status has an invalid value
*                                OS_ERR_TIMEOUT             The bit(s) have not been set in the specified 'timeout'
*                                OS_ERR_TICK_DISABLED       If kernel ticks are disabled and a timeout is specified
*
* Returns    : The flags in the event flag group that made the task ready or, 0 if a timeout or an error
*              occurred.
*
* Note(s)    : This API 'MUST NOT' be called from a timer callback function.
************************************************************************************************************************
*/

OS_FLAGS  OSFlagPend (OS_FLAG_GRP  *p_grp,
                      OS_FLAGS      flags,
                      OS_TICK       timeout,
                      OS_OPT        opt,
                      CPU_TS       *p_ts,
                      OS_ERR       *p_err)
{
    CPU_BOOLEAN  consume;
    OS_FLAGS     flags_rdy;
    OS_OPT       mode;
    CPU_SR_ALLOC();


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

    OS_TRACE_FLAG_PEND_ENTER(p_grp, flags, timeout, opt, p_ts);

#if (OS_CFG_TICK_EN == 0u)
    if (timeout != 0u) {
       *p_err = OS_ERR_TICK_DISABLED;
        OS_TRACE_FLAG_PEND_FAILED(p_grp);
        OS_TRACE_FLAG_PEND_EXIT(OS_ERR_TICK_DISABLED);
        return ((OS_FLAGS)0);
    }
#endif

#if (OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u)
    if (OSIntNestingCtr > 0u) {                                 /* See if called from ISR ...                           */
        if ((opt & OS_OPT_PEND_NON_BLOCKING) != OS_OPT_PEND_NON_BLOCKING) {
           *p_err = OS_ERR_PEND_ISR;                            /* ... can't PEND from an ISR                           */
            OS_TRACE_FLAG_PEND_FAILED(p_grp);
            OS_TRACE_FLAG_PEND_EXIT(OS_ERR_PEND_ISR);
            return ((OS_FLAGS)0);
        }
    }
#endif

#if (OS_CFG_INVALID_OS_CALLS_CHK_EN > 0u)
    if (OSRunning != OS_STATE_OS_RUNNING) {                     /* Is the kernel running?                               */
        OS_TRACE_FLAG_PEND_EXIT(OS_ERR_OS_NOT_RUNNING);
       *p_err = OS_ERR_OS_NOT_RUNNING;
        return (0u);
    }
#endif

#if (OS_CFG_ARG_CHK_EN > 0u)
    if (p_grp == (OS_FLAG_GRP *)0) {                            /* Validate 'p_grp'                                     */
        OS_TRACE_FLAG_PEND_FAILED(p_grp);
        OS_TRACE_FLAG_PEND_EXIT(OS_ERR_OBJ_PTR_NULL);
       *p_err = OS_ERR_OBJ_PTR_NULL;
        return (0u);
    }
    switch (opt) {                                              /* Validate 'opt'                                       */
        case OS_OPT_PEND_FLAG_CLR_ALL:
        case OS_OPT_PEND_FLAG_CLR_ANY:
        case OS_OPT_PEND_FLAG_SET_ALL:
        case OS_OPT_PEND_FLAG_SET_ANY:
        case OS_OPT_PEND_FLAG_CLR_ALL | OS_OPT_PEND_FLAG_CONSUME:
        case OS_OPT_PEND_FLAG_CLR_ANY | OS_OPT_PEND_FLAG_CONSUME:
        case OS_OPT_PEND_FLAG_SET_ALL | OS_OPT_PEND_FLAG_CONSUME:
        case OS_OPT_PEND_FLAG_SET_ANY | OS_OPT_PEND_FLAG_CONSUME:
        case OS_OPT_PEND_FLAG_CLR_ALL | OS_OPT_PEND_NON_BLOCKING:
        case OS_OPT_PEND_FLAG_CLR_ANY | OS_OPT_PEND_NON_BLOCKING:
        case OS_OPT_PEND_FLAG_SET_ALL | OS_OPT_PEND_NON_BLOCKING:
        case OS_OPT_PEND_FLAG_SET_ANY | OS_OPT_PEND_NON_BLOCKING:
        case OS_OPT_PEND_FLAG_CLR_ALL | (OS_OPT)(OS_OPT_PEND_FLAG_CONSUME | OS_OPT_PEND_NON_BLOCKING):
        case OS_OPT_PEND_FLAG_CLR_ANY | (OS_OPT)(OS_OPT_PEND_FLAG_CONSUME | OS_OPT_PEND_NON_BLOCKING):
        case OS_OPT_PEND_FLAG_SET_ALL | (OS_OPT)(OS_OPT_PEND_FLAG_CONSUME | OS_OPT_PEND_NON_BLOCKING):
        case OS_OPT_PEND_FLAG_SET_ANY | (OS_OPT)(OS_OPT_PEND_FLAG_CONSUME | OS_OPT_PEND_NON_BLOCKING):
             break;

        default:
             OS_TRACE_FLAG_PEND_FAILED(p_grp);
             OS_TRACE_FLAG_PEND_EXIT(OS_ERR_OPT_INVALID);
            *p_err = OS_ERR_OPT_INVALID;
             return (0u);
    }
#endif

#if (OS_CFG_OBJ_TYPE_CHK_EN > 0u)
    if (p_grp->Type != OS_OBJ_TYPE_FLAG) {                      /* Validate that we are pointing at an event flag       */
        OS_TRACE_FLAG_PEND_FAILED(p_grp);
        OS_TRACE_FLAG_PEND_EXIT(OS_ERR_OBJ_TYPE);
       *p_err = OS_ERR_OBJ_TYPE;
        return (0u);
    }
#endif

    if ((opt & OS_OPT_PEND_FLAG_CONSUME) != 0u) {               /* See if we need to consume the flags                  */
        consume = OS_TRUE;
    } else {
        consume = OS_FALSE;
    }

    if (p_ts != (CPU_TS *)0) {
       *p_ts = 0u;                                              /* Initialize the returned timestamp                    */
    }

    mode = opt & OS_OPT_PEND_FLAG_MASK;
    CPU_CRITICAL_ENTER();
    switch (mode) {
        case OS_OPT_PEND_FLAG_SET_ALL:                          /* See if all required flags are set                    */
             flags_rdy = (p_grp->Flags & flags);                /* Extract only the bits we want                        */
             if (flags_rdy == flags) {                          /* Must match ALL the bits that we want                 */
                 if (consume == OS_TRUE) {                      /* See if we need to consume the flags                  */
                     p_grp->Flags &= ~flags_rdy;                /* Clear ONLY the flags that we wanted                  */
                 }
                 OSTCBCurPtr->FlagsRdy = flags_rdy;             /* Save flags that were ready                           */
#if (OS_CFG_TS_EN > 0u)
                 if (p_ts != (CPU_TS *)0) {
                    *p_ts = p_grp->TS;
                 }
#endif
                 CPU_CRITICAL_EXIT();                           /* Yes, condition met, return to caller                 */
                 OS_TRACE_FLAG_PEND(p_grp);
                 OS_TRACE_FLAG_PEND_EXIT(OS_ERR_NONE);
                *p_err = OS_ERR_NONE;
                 return (flags_rdy);
             } else {                                           /* Block task until events occur or timeout             */
                 if ((opt & OS_OPT_PEND_NON_BLOCKING) != 0u) {
                     CPU_CRITICAL_EXIT();
                     OS_TRACE_FLAG_PEND_FAILED(p_grp);
                     OS_TRACE_FLAG_PEND_EXIT(OS_ERR_PEND_WOULD_BLOCK);
                    *p_err = OS_ERR_PEND_WOULD_BLOCK;           /* Specified non-blocking so task would block           */
                     return ((OS_FLAGS)0);
                 } else {                                       /* Specified blocking so check is scheduler is locked   */
                     if (OSSchedLockNestingCtr > 0u) {          /* See if called with scheduler locked ...        */
                         CPU_CRITICAL_EXIT();
                         OS_TRACE_FLAG_PEND_FAILED(p_grp);
                         OS_TRACE_FLAG_PEND_EXIT(OS_ERR_SCHED_LOCKED);
                        *p_err = OS_ERR_SCHED_LOCKED;           /* ... can't PEND when locked                           */
                         return (0u);
                     }
                 }
                                                                /* Lock the scheduler/re-enable interrupts              */
                 OS_FlagBlock(p_grp,
                              flags,
                              opt,
                              timeout);
                 CPU_CRITICAL_EXIT();
             }
             break;

        case OS_OPT_PEND_FLAG_SET_ANY:
             flags_rdy = (p_grp->Flags & flags);                /* Extract only the bits we want                        */
             if (flags_rdy != 0u) {                             /* See if any flag set                                  */
                 if (consume == OS_TRUE) {                      /* See if we need to consume the flags                  */
                     p_grp->Flags &= ~flags_rdy;                /* Clear ONLY the flags that we got                     */
                 }
                 OSTCBCurPtr->FlagsRdy = flags_rdy;             /* Save flags that were ready                           */
#if (OS_CFG_TS_EN > 0u)
                 if (p_ts != (CPU_TS *)0) {
                    *p_ts  = p_grp->TS;
                 }
#endif
                 CPU_CRITICAL_EXIT();                           /* Yes, condition met, return to caller                 */
                 OS_TRACE_FLAG_PEND(p_grp);
                 OS_TRACE_FLAG_PEND_EXIT(OS_ERR_NONE);
                *p_err = OS_ERR_NONE;
                 return (flags_rdy);
             } else {                                           /* Block task until events occur or timeout             */
                 if ((opt & OS_OPT_PEND_NON_BLOCKING) != 0u) {
                     CPU_CRITICAL_EXIT();
                     OS_TRACE_FLAG_PEND_EXIT(OS_ERR_PEND_WOULD_BLOCK);
                    *p_err = OS_ERR_PEND_WOULD_BLOCK;           /* Specified non-blocking so task would block           */
                     return ((OS_FLAGS)0);
                 } else {                                       /* Specified blocking so check is scheduler is locked   */
                     if (OSSchedLockNestingCtr > 0u) {          /* See if called with scheduler locked ...        */
                         CPU_CRITICAL_EXIT();
                         OS_TRACE_FLAG_PEND_EXIT(OS_ERR_SCHED_LOCKED);
                        *p_err = OS_ERR_SCHED_LOCKED;           /* ... can't PEND when locked                           */
                         return ((OS_FLAGS)0);
                     }
                 }

                 OS_FlagBlock(p_grp,
                              flags,
                              opt,
                              timeout);
                 CPU_CRITICAL_EXIT();
             }
             break;

#if (OS_CFG_FLAG_MODE_CLR_EN > 0u)
        case OS_OPT_PEND_FLAG_CLR_ALL:                          /* See if all required flags are cleared                */
             flags_rdy = (OS_FLAGS)(~p_grp->Flags & flags);     /* Extract only the bits we want                        */
             if (flags_rdy == flags) {                          /* Must match ALL the bits that we want                 */
                 if (consume == OS_TRUE) {                      /* See if we need to consume the flags                  */
                     p_grp->Flags |= flags_rdy;                 /* Set ONLY the flags that we wanted                    */
                 }
                 OSTCBCurPtr->FlagsRdy = flags_rdy;             /* Save flags that were ready                           */
#if (OS_CFG_TS_EN > 0u)
                 if (p_ts != (CPU_TS *)0) {
                    *p_ts  = p_grp->TS;
                 }
#endif
                 CPU_CRITICAL_EXIT();                           /* Yes, condition met, return to caller                 */
                 OS_TRACE_FLAG_PEND(p_grp);
                 OS_TRACE_FLAG_PEND_EXIT(OS_ERR_NONE);
                *p_err = OS_ERR_NONE;
                 return (flags_rdy);
             } else {                                           /* Block task until events occur or timeout             */
                 if ((opt & OS_OPT_PEND_NON_BLOCKING) != 0u) {
                     CPU_CRITICAL_EXIT();
                     OS_TRACE_FLAG_PEND_EXIT(OS_ERR_PEND_WOULD_BLOCK);
                    *p_err = OS_ERR_PEND_WOULD_BLOCK;           /* Specified non-blocking so task would block           */
                     return ((OS_FLAGS)0);
                 } else {                                       /* Specified blocking so check is scheduler is locked   */
                     if (OSSchedLockNestingCtr > 0u) {          /* See if called with scheduler locked ...        */
                         CPU_CRITICAL_EXIT();
                         OS_TRACE_FLAG_PEND_EXIT(OS_ERR_SCHED_LOCKED);
                        *p_err = OS_ERR_SCHED_LOCKED;           /* ... can't PEND when locked                           */
                         return (0);
                     }
                 }

                 OS_FlagBlock(p_grp,
                              flags,
                              opt,
                              timeout);
                 CPU_CRITICAL_EXIT();
             }
             break;

        case OS_OPT_PEND_FLAG_CLR_ANY:
             flags_rdy = (~p_grp->Flags & flags);               /* Extract only the bits we want                        */
             if (flags_rdy != 0u) {                             /* See if any flag cleared                              */
                 if (consume == OS_TRUE) {                      /* See if we need to consume the flags                  */
                     p_grp->Flags |= flags_rdy;                 /* Set ONLY the flags that we got                       */
                 }
                 OSTCBCurPtr->FlagsRdy = flags_rdy;             /* Save flags that were ready                           */
#if (OS_CFG_TS_EN > 0u)
                 if (p_ts != (CPU_TS *)0) {
                    *p_ts  = p_grp->TS;
                 }
#endif
                 CPU_CRITICAL_EXIT();                           /* Yes, condition met, return to caller                 */
                 OS_TRACE_FLAG_PEND(p_grp);
                 OS_TRACE_FLAG_PEND_EXIT(OS_ERR_NONE);
                *p_err = OS_ERR_NONE;
                 return (flags_rdy);
             } else {                                           /* Block task until events occur or timeout             */
                 if ((opt & OS_OPT_PEND_NON_BLOCKING) != 0u) {
                     CPU_CRITICAL_EXIT();
                     OS_TRACE_FLAG_PEND_EXIT(OS_ERR_PEND_WOULD_BLOCK);
                    *p_err = OS_ERR_PEND_WOULD_BLOCK;           /* Specified non-blocking so task would block           */
                     return ((OS_FLAGS)0);
                 } else {                                       /* Specified blocking so check is scheduler is locked   */
                     if (OSSchedLockNestingCtr > 0u) {          /* See if called with scheduler locked ...        */
                         CPU_CRITICAL_EXIT();
                         OS_TRACE_FLAG_PEND_EXIT(OS_ERR_SCHED_LOCKED);
                        *p_err = OS_ERR_SCHED_LOCKED;           /* ... can't PEND when locked                           */
                         return (0u);
                     }
                 }

                 OS_FlagBlock(p_grp,
                              flags,
                              opt,
                              timeout);
                 CPU_CRITICAL_EXIT();
             }
             break;
#endif

        default:
             CPU_CRITICAL_EXIT();
             OS_TRACE_FLAG_PEND_FAILED(p_grp);
             OS_TRACE_FLAG_PEND_EXIT(OS_ERR_OPT_INVALID);
            *p_err = OS_ERR_OPT_INVALID;
             return (0u);
    }

    OS_TRACE_FLAG_PEND_BLOCK(p_grp);

    OSSched();                                                  /* Find next HPT ready to run                           */

    CPU_CRITICAL_ENTER();
    switch (OSTCBCurPtr->PendStatus) {
        case OS_STATUS_PEND_OK:                                 /* We got the event flags                               */
#if (OS_CFG_TS_EN > 0u)
             if (p_ts != (CPU_TS *)0) {
                *p_ts = OSTCBCurPtr->TS;
             }
#endif
             OS_TRACE_FLAG_PEND(p_grp);
            *p_err = OS_ERR_NONE;
             break;

        case OS_STATUS_PEND_ABORT:                              /* Indicate that we aborted                             */
#if (OS_CFG_TS_EN > 0u)
             if (p_ts != (CPU_TS *)0) {
                *p_ts = OSTCBCurPtr->TS;
             }
#endif
             CPU_CRITICAL_EXIT();
             OS_TRACE_FLAG_PEND_FAILED(p_grp);
            *p_err = OS_ERR_PEND_ABORT;
             break;

        case OS_STATUS_PEND_TIMEOUT:                            /* Indicate that we didn't get semaphore within timeout */
             if (p_ts != (CPU_TS *)0) {
                *p_ts = 0u;
             }
             CPU_CRITICAL_EXIT();
             OS_TRACE_FLAG_PEND_FAILED(p_grp);
            *p_err = OS_ERR_TIMEOUT;
             break;

        case OS_STATUS_PEND_DEL:                                /* Indicate that object pended on has been deleted      */
#if (OS_CFG_TS_EN > 0u)
             if (p_ts != (CPU_TS *)0) {
                *p_ts = OSTCBCurPtr->TS;
             }
#endif
             CPU_CRITICAL_EXIT();
             OS_TRACE_FLAG_PEND_FAILED(p_grp);
            *p_err = OS_ERR_OBJ_DEL;
             break;

        default:
             CPU_CRITICAL_EXIT();
             OS_TRACE_FLAG_PEND_FAILED(p_grp);
            *p_err = OS_ERR_STATUS_INVALID;
             break;
    }
    if (*p_err != OS_ERR_NONE) {
        OS_TRACE_FLAG_PEND_EXIT(*p_err);
        return (0u);
    }

    flags_rdy = OSTCBCurPtr->FlagsRdy;
    if (consume == OS_TRUE) {                                   /* See if we need to consume the flags                  */
        switch (mode) {
            case OS_OPT_PEND_FLAG_SET_ALL:
            case OS_OPT_PEND_FLAG_SET_ANY:                      /* Clear ONLY the flags we got                          */
                 p_grp->Flags &= ~flags_rdy;
                 break;

#if (OS_CFG_FLAG_MODE_CLR_EN > 0u)
            case OS_OPT_PEND_FLAG_CLR_ALL:
            case OS_OPT_PEND_FLAG_CLR_ANY:                      /* Set   ONLY the flags we got                          */
                 p_grp->Flags |=  flags_rdy;
                 break;
#endif
            default:
                 CPU_CRITICAL_EXIT();
                 OS_TRACE_FLAG_PEND_EXIT(OS_ERR_OPT_INVALID);
                *p_err = OS_ERR_OPT_INVALID;
                 return (0u);
        }
    }
    CPU_CRITICAL_EXIT();
    OS_TRACE_FLAG_PEND_EXIT(OS_ERR_NONE);
   *p_err = OS_ERR_NONE;                                        /* Event(s) must have occurred                          */
    return (flags_rdy);
}


/*
************************************************************************************************************************
*                                          ABORT WAITING ON AN EVENT FLAG GROUP
*
* Description: This function aborts & readies any tasks currently waiting on an event flag group.  This function should
*              be used to fault-abort the wait on the event flag group, rather than to normally post to the event flag
*              group OSFlagPost().
*
* Arguments  : p_grp     is a pointer to the event flag group
*
*              opt       determines the type of ABORT performed:
*
*                            OS_OPT_PEND_ABORT_1          ABORT wait for a single task (HPT) waiting on the event flag
*                            OS_OPT_PEND_ABORT_ALL        ABORT wait for ALL tasks that are  waiting on the event flag
*                            OS_OPT_POST_NO_SCHED         Do not call the scheduler
*
*              p_err     is a pointer to a variable that will contain an error code returned by this function.
*
*                            OS_ERR_NONE                  At least one task waiting on the event flag group and was
*                                                         readied and informed of the aborted wait; check return value
*                                                         for the number of tasks whose wait on the event flag group
*                                                         was aborted
*                            OS_ERR_OBJ_PTR_NULL          If 'p_grp' is a NULL pointer
*                            OS_ERR_OBJ_TYPE              If 'p_grp' is not pointing at an event flag group
*                            OS_ERR_OPT_INVALID           If you specified an invalid option
*                            OS_ERR_OS_NOT_RUNNING        If uC/OS-III is not running yet
*                            OS_ERR_PEND_ABORT_ISR        If you called this function from an ISR
*                            OS_ERR_PEND_ABORT_NONE       No task were pending
*
* Returns    : == 0          if no tasks were waiting on the event flag group, or upon error.
*              >  0          if one or more tasks waiting on the event flag group are now readied and informed.
*
* Note(s)    : none
************************************************************************************************************************
*/

#if (OS_CFG_FLAG_PEND_ABORT_EN > 0u)
OS_OBJ_QTY  OSFlagPendAbort (OS_FLAG_GRP  *p_grp,
                             OS_OPT        opt,
                             OS_ERR       *p_err)
{
    OS_PEND_LIST  *p_pend_list;
    OS_TCB        *p_tcb;
    CPU_TS         ts;
    OS_OBJ_QTY     nbr_tasks;
    CPU_SR_ALLOC();


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

#if (OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u)
    if (OSIntNestingCtr > 0u) {                                 /* Not allowed to Pend Abort from an ISR                */
       *p_err = OS_ERR_PEND_ABORT_ISR;
        return (0u);
    }
#endif

#if (OS_CFG_INVALID_OS_CALLS_CHK_EN > 0u)
    if (OSRunning != OS_STATE_OS_RUNNING) {                     /* Is the kernel running?                               */
       *p_err = OS_ERR_OS_NOT_RUNNING;
        return (0u);
    }
#endif

#if (OS_CFG_ARG_CHK_EN > 0u)
    if (p_grp == (OS_FLAG_GRP *)0) {                            /* Validate 'p_grp'                                     */
       *p_err  =  OS_ERR_OBJ_PTR_NULL;
        return (0u);
    }
    switch (opt) {                                              /* Validate 'opt'                                       */
        case OS_OPT_PEND_ABORT_1:
        case OS_OPT_PEND_ABORT_ALL:
        case OS_OPT_PEND_ABORT_1   | OS_OPT_POST_NO_SCHED:
        case OS_OPT_PEND_ABORT_ALL | OS_OPT_POST_NO_SCHED:
             break;

        default:
            *p_err = OS_ERR_OPT_INVALID;
             return (0u);
    }
#endif

#if (OS_CFG_OBJ_TYPE_CHK_EN > 0u)
    if (p_grp->Type != OS_OBJ_TYPE_FLAG) {                      /* Make sure event flag group was created               */
       *p_err = OS_ERR_OBJ_TYPE;
        return (0u);
    }
#endif

    CPU_CRITICAL_ENTER();
    p_pend_list = &p_grp->PendList;
    if (p_pend_list->HeadPtr == (OS_TCB *)0) {                  /* Any task waiting on flag group?                      */
        CPU_CRITICAL_EXIT();                                    /* No                                                   */
       *p_err = OS_ERR_PEND_ABORT_NONE;
        return (0u);
    }

    nbr_tasks = 0u;
#if (OS_CFG_TS_EN > 0u)
    ts        = OS_TS_GET();                                    /* Get local time stamp so all tasks get the same time  */
#else
    ts        = 0u;
#endif

    while (p_pend_list->HeadPtr != (OS_TCB *)0) {
        p_tcb = p_pend_list->HeadPtr;
        OS_PendAbort(p_tcb,
                     ts,
                     OS_STATUS_PEND_ABORT);
        nbr_tasks++;
        if (opt != OS_OPT_PEND_ABORT_ALL) {                     /* Pend abort all tasks waiting?                        */
            break;                                              /* No                                                   */
        }
    }
    CPU_CRITICAL_EXIT();

    if ((opt & OS_OPT_POST_NO_SCHED) == 0u) {
        OSSched();                                              /* Run the scheduler                                    */
    }

   *p_err = OS_ERR_NONE;
    return (nbr_tasks);
}
#endif


/*
************************************************************************************************************************
*                                       GET FLAGS WHO CAUSED TASK TO BECOME READY
*
* Description: This function is called to obtain the flags that caused the task to become ready to run.
*              In other words, this function allows you to tell "Who done it!".
*
* Arguments  : p_err     is a pointer to an error code
*
*                            OS_ERR_NONE              If the call was successful
*                            OS_ERR_OS_NOT_RUNNING    If uC/OS-III is not running yet
*                            OS_ERR_PEND_ISR          If called from an ISR
*
* Returns    : The flags that caused the task to be ready.
*
* Note(s)    : none
************************************************************************************************************************
*/

OS_FLAGS  OSFlagPendGetFlagsRdy (OS_ERR  *p_err)
{
    OS_FLAGS  flags;
    CPU_SR_ALLOC();


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

#if (OS_CFG_INVALID_OS_CALLS_CHK_EN > 0u)
    if (OSRunning != OS_STATE_OS_RUNNING) {                     /* Is the kernel running?                               */
       *p_err = OS_ERR_OS_NOT_RUNNING;
        return (0u);
    }
#endif

#if (OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u)
    if (OSIntNestingCtr > 0u) {                                 /* See if called from ISR ...                           */
       *p_err = OS_ERR_PEND_ISR;                                /* ... can't get from an ISR                            */
        return (0u);
    }
#endif

    CPU_CRITICAL_ENTER();
    flags = OSTCBCurPtr->FlagsRdy;
    CPU_CRITICAL_EXIT();
   *p_err = OS_ERR_NONE;
    return (flags);
}


/*
************************************************************************************************************************
*                                                POST EVENT FLAG BIT(S)
*
* Description: This function is called to set or clear some bits in an event flag group.  The bits to set or clear are
*              specified by a 'bit mask'.
*
* Arguments  : p_grp         is a pointer to the desired event flag group.
*
*              flags         If 'opt' (see below) is OS_OPT_POST_FLAG_SET, each bit that is set in 'flags' will
*                            set the corresponding bit in the event flag group.  e.g. to set bits 0, 4
*                            and 5 you would set 'flags' to:
*
*                                0x31     (note, bit 0 is least significant bit)
*
*                            If 'opt' (see below) is OS_OPT_POST_FLAG_CLR, each bit that is set in 'flags' will
*                            CLEAR the corresponding bit in the event flag group.  e.g. to clear bits 0,
*                            4 and 5 you would specify 'flags' as:
*
*                                0x31     (note, bit 0 is least significant bit)
*
*              opt           indicates whether the flags will be:
*
*                                OS_OPT_POST_FLAG_SET       set
*                                OS_OPT_POST_FLAG_CLR       cleared
*
*                            you can also 'add' OS_OPT_POST_NO_SCHED to prevent the scheduler from being called.
*
*              p_err         is a pointer to an error code and can be:
*
*                                OS_ERR_NONE                The call was successful
*                                OS_ERR_OBJ_PTR_NULL        You passed a NULL pointer
*                                OS_ERR_OBJ_TYPE            You are not pointing to an event flag group
*                                OS_ERR_OPT_INVALID         You specified an invalid option
*                                OS_ERR_OS_NOT_RUNNING      If uC/OS-III is not running yet
*
* Returns    : the new value of the event flags bits that are still set.
*
* Note(s)    : 1) The execution time of this function depends on the number of tasks waiting on the event flag group.
************************************************************************************************************************
*/

OS_FLAGS  OSFlagPost (OS_FLAG_GRP  *p_grp,
                      OS_FLAGS      flags,
                      OS_OPT        opt,
                      OS_ERR       *p_err)
{

    OS_FLAGS       flags_cur;
    OS_FLAGS       flags_rdy;
    OS_OPT         mode;
    OS_PEND_LIST  *p_pend_list;
    OS_TCB        *p_tcb;
    OS_TCB        *p_tcb_next;
    CPU_TS         ts;
    CPU_SR_ALLOC();


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

    OS_TRACE_FLAG_POST_ENTER(p_grp, flags, opt);

#if (OS_CFG_INVALID_OS_CALLS_CHK_EN > 0u)
    if (OSRunning != OS_STATE_OS_RUNNING) {                     /* Is the kernel running?                               */
        OS_TRACE_FLAG_POST_EXIT(OS_ERR_OS_NOT_RUNNING);
       *p_err = OS_ERR_OS_NOT_RUNNING;
        return (0u);
    }
#endif

#if (OS_CFG_ARG_CHK_EN > 0u)
    if (p_grp == (OS_FLAG_GRP *)0) {                            /* Validate 'p_grp'                                     */
        OS_TRACE_FLAG_POST_FAILED(p_grp);
        OS_TRACE_FLAG_POST_EXIT(OS_ERR_OBJ_PTR_NULL);
       *p_err  = OS_ERR_OBJ_PTR_NULL;
        return (0u);
    }
#endif

#if (OS_CFG_OBJ_TYPE_CHK_EN > 0u)
    if (p_grp->Type != OS_OBJ_TYPE_FLAG) {                      /* Make sure we are pointing to an event flag grp       */
        OS_TRACE_FLAG_POST_FAILED(p_grp);
        OS_TRACE_FLAG_POST_EXIT(OS_ERR_OBJ_TYPE);
       *p_err = OS_ERR_OBJ_TYPE;
        return (0u);
    }
#endif

#if (OS_CFG_TS_EN > 0u)
    ts = OS_TS_GET();                                           /* Get timestamp                                        */
#else
    ts = 0u;
#endif

    OS_TRACE_FLAG_POST(p_grp);

    switch (opt) {
        case OS_OPT_POST_FLAG_SET:
        case OS_OPT_POST_FLAG_SET | OS_OPT_POST_NO_SCHED:
             CPU_CRITICAL_ENTER();
             p_grp->Flags |=  flags;                            /* Set   the flags specified in the group               */
             break;

        case OS_OPT_POST_FLAG_CLR:
        case OS_OPT_POST_FLAG_CLR | OS_OPT_POST_NO_SCHED:
             CPU_CRITICAL_ENTER();
             p_grp->Flags &= ~flags;                            /* Clear the flags specified in the group               */
             break;

        default:
            *p_err = OS_ERR_OPT_INVALID;                        /* INVALID option                                       */
             OS_TRACE_FLAG_POST_EXIT(*p_err);
             return (0u);
    }
#if (OS_CFG_TS_EN > 0u)
    p_grp->TS   = ts;
#endif
    p_pend_list = &p_grp->PendList;
    if (p_pend_list->HeadPtr == (OS_TCB *)0) {                  /* Any task waiting on event flag group?                */
        CPU_CRITICAL_EXIT();                                    /* No                                                   */
       *p_err = OS_ERR_NONE;
        OS_TRACE_FLAG_POST_EXIT(*p_err);
        return (p_grp->Flags);
    }

    p_tcb = p_pend_list->HeadPtr;
    while (p_tcb != (OS_TCB *)0) {                              /* Go through all tasks waiting on event flag(s)        */
        p_tcb_next = p_tcb->PendNextPtr;
        mode       = p_tcb->FlagsOpt & OS_OPT_PEND_FLAG_MASK;
        switch (mode) {
            case OS_OPT_PEND_FLAG_SET_ALL:                      /* See if all req. flags are set for current node       */
                 flags_rdy = (p_grp->Flags & p_tcb->FlagsPend);
                 if (flags_rdy == p_tcb->FlagsPend) {
                     OS_FlagTaskRdy(p_tcb,                      /* Make task RTR, event(s) Rx'd                         */
                                    flags_rdy,
                                    ts);
                 }
                 break;

            case OS_OPT_PEND_FLAG_SET_ANY:                      /* See if any flag set                                  */
                 flags_rdy = (p_grp->Flags & p_tcb->FlagsPend);
                 if (flags_rdy != 0u) {
                     OS_FlagTaskRdy(p_tcb,                      /* Make task RTR, event(s) Rx'd                         */
                                    flags_rdy,
                                    ts);
                 }
                 break;

#if (OS_CFG_FLAG_MODE_CLR_EN > 0u)
            case OS_OPT_PEND_FLAG_CLR_ALL:                      /* See if all req. flags are set for current node       */
                 flags_rdy = (OS_FLAGS)(~p_grp->Flags & p_tcb->FlagsPend);
                 if (flags_rdy == p_tcb->FlagsPend) {
                     OS_FlagTaskRdy(p_tcb,                      /* Make task RTR, event(s) Rx'd                         */
                                    flags_rdy,
                                    ts);
                 }
                 break;

            case OS_OPT_PEND_FLAG_CLR_ANY:                      /* See if any flag set                                  */
                 flags_rdy = (OS_FLAGS)(~p_grp->Flags & p_tcb->FlagsPend);
                 if (flags_rdy != 0u) {
                     OS_FlagTaskRdy(p_tcb,                      /* Make task RTR, event(s) Rx'd                         */
                                    flags_rdy,
                                    ts);
                 }
                 break;
#endif
            default:
                 CPU_CRITICAL_EXIT();
                *p_err = OS_ERR_FLAG_PEND_OPT;
                 OS_TRACE_FLAG_POST_EXIT(*p_err);
                 return (0u);
        }
                                                                /* Point to next task waiting for event flag(s)         */
        p_tcb = p_tcb_next;
    }
    CPU_CRITICAL_EXIT();

    if ((opt & OS_OPT_POST_NO_SCHED) == 0u) {
        OSSched();
    }

    CPU_CRITICAL_ENTER();
    flags_cur = p_grp->Flags;
    CPU_CRITICAL_EXIT();
   *p_err     = OS_ERR_NONE;

    OS_TRACE_FLAG_POST_EXIT(*p_err);
    return (flags_cur);
}


/*
************************************************************************************************************************
*                         SUSPEND TASK UNTIL EVENT FLAG(s) RECEIVED OR TIMEOUT OCCURS
*
* Description: This function is internal to uC/OS-III and is used to put a task to sleep until the desired
*              event flag bit(s) are set.
*
* Arguments  : p_grp         is a pointer to the desired event flag group.
*              -----
*
*              flags         Is a bit pattern indicating which bit(s) (i.e. flags) you wish to check.
*                            The bits you want are specified by setting the corresponding bits in
*                            'flags'.  e.g. if your application wants to wait for bits 0 and 1 then
*                            'flags' would contain 0x03.
*
*              opt           specifies whether you want ALL bits to be set/cleared or ANY of the bits
*                            to be set/cleared.
*                            You can specify the following argument:
*
*                                OS_OPT_PEND_FLAG_CLR_ALL   You will check ALL bits in 'mask' to be clear (0)
*                                OS_OPT_PEND_FLAG_CLR_ANY   You will check ANY bit  in 'mask' to be clear (0)
*                                OS_OPT_PEND_FLAG_SET_ALL   You will check ALL bits in 'mask' to be set   (1)
*                                OS_OPT_PEND_FLAG_SET_ANY   You will check ANY bit  in 'mask' to be set   (1)
*
*              timeout       is the desired amount of time that the task will wait for the event flag
*                            bit(s) to be set.
*
* Returns    : none
*
* Note(s)    : This function is INTERNAL to uC/OS-III and your application should not call it.
************************************************************************************************************************
*/

void  OS_FlagBlock (OS_FLAG_GRP  *p_grp,
                    OS_FLAGS      flags,
                    OS_OPT        opt,
                    OS_TICK       timeout)
{
    OSTCBCurPtr->FlagsPend = flags;                             /* Save the flags that we need to wait for              */
    OSTCBCurPtr->FlagsOpt  = opt;                               /* Save the type of wait we are doing                   */
    OSTCBCurPtr->FlagsRdy  = 0u;

    OS_Pend((OS_PEND_OBJ *)((void *)p_grp),
             OSTCBCurPtr,
             OS_TASK_PEND_ON_FLAG,
             timeout);
}


/*
************************************************************************************************************************
*                                      CLEAR THE CONTENTS OF AN EVENT FLAG GROUP
*
* Description: This function is called by OSFlagDel() to clear the contents of an event flag group
*

* Argument(s): p_grp     is a pointer to the event flag group to clear
*              -----
*
* Returns    : none
*
* Note(s)    : This function is INTERNAL to uC/OS-III and your application should not call it.
************************************************************************************************************************
*/

void  OS_FlagClr (OS_FLAG_GRP  *p_grp)
{
    OS_PEND_LIST  *p_pend_list;


#if (OS_OBJ_TYPE_REQ > 0u)
    p_grp->Type             = OS_OBJ_TYPE_NONE;
#endif
#if (OS_CFG_DBG_EN > 0u)
    p_grp->NamePtr          = (CPU_CHAR *)((void *)"?FLAG");    /* Unknown name                                         */
#endif
    p_grp->Flags            =  0u;
    p_pend_list             = &p_grp->PendList;
    OS_PendListInit(p_pend_list);
}


/*
************************************************************************************************************************
*                                    ADD/REMOVE EVENT FLAG GROUP TO/FROM DEBUG LIST
*
* Description: These functions are called by uC/OS-III to add or remove an event flag group from the event flag debug
*              list.
*
* Arguments  : p_grp     is a pointer to the event flag group to add/remove
*
* Returns    : none
*
* Note(s)    : These functions are INTERNAL to uC/OS-III and your application should not call it.
************************************************************************************************************************
*/

#if (OS_CFG_DBG_EN > 0u)
void  OS_FlagDbgListAdd (OS_FLAG_GRP  *p_grp)
{
    p_grp->DbgNamePtr                = (CPU_CHAR *)((void *)" ");
    p_grp->DbgPrevPtr                = (OS_FLAG_GRP *)0;
    if (OSFlagDbgListPtr == (OS_FLAG_GRP *)0) {
        p_grp->DbgNextPtr            = (OS_FLAG_GRP *)0;
    } else {
        p_grp->DbgNextPtr            = OSFlagDbgListPtr;
        OSFlagDbgListPtr->DbgPrevPtr = p_grp;
    }
    OSFlagDbgListPtr                 = p_grp;
}


void  OS_FlagDbgListRemove (OS_FLAG_GRP  *p_grp)
{
    OS_FLAG_GRP  *p_grp_next;
    OS_FLAG_GRP  *p_grp_prev;


    p_grp_prev = p_grp->DbgPrevPtr;
    p_grp_next = p_grp->DbgNextPtr;

    if (p_grp_prev == (OS_FLAG_GRP *)0) {
        OSFlagDbgListPtr = p_grp_next;
        if (p_grp_next != (OS_FLAG_GRP *)0) {
            p_grp_next->DbgPrevPtr = (OS_FLAG_GRP *)0;
        }
        p_grp->DbgNextPtr = (OS_FLAG_GRP *)0;

    } else if (p_grp_next == (OS_FLAG_GRP *)0) {
        p_grp_prev->DbgNextPtr = (OS_FLAG_GRP *)0;
        p_grp->DbgPrevPtr      = (OS_FLAG_GRP *)0;

    } else {
        p_grp_prev->DbgNextPtr =  p_grp_next;
        p_grp_next->DbgPrevPtr =  p_grp_prev;
        p_grp->DbgNextPtr      = (OS_FLAG_GRP *)0;
        p_grp->DbgPrevPtr      = (OS_FLAG_GRP *)0;
    }
}
#endif


/*
************************************************************************************************************************
*                                        MAKE TASK READY-TO-RUN, EVENT(s) OCCURRED
*
* Description: This function is internal to uC/OS-III and is used to make a task ready-to-run because the desired event
*              flag bits have been set.
*
* Arguments  : p_tcb         is a pointer to the OS_TCB of the task to remove
*              -----
*
*              flags_rdy     contains the bit pattern of the event flags that cause the task to become ready-to-run.
*
*              ts            is a timestamp associated with the post
*
* Returns    : none
*
* Note(s)    : This function is INTERNAL to uC/OS-III and your application should not call it.
************************************************************************************************************************
*/

void   OS_FlagTaskRdy (OS_TCB    *p_tcb,
                       OS_FLAGS   flags_rdy,
                       CPU_TS     ts)
{
#if (OS_CFG_TS_EN == 0u)
    (void)ts;                                                   /* Prevent compiler warning for not using 'ts'          */
#endif

    p_tcb->FlagsRdy   = flags_rdy;
    p_tcb->PendStatus = OS_STATUS_PEND_OK;                      /* Clear pend status                                    */
    p_tcb->PendOn     = OS_TASK_PEND_ON_NOTHING;                /* Indicate no longer pending                           */
#if (OS_CFG_TS_EN > 0u)
    p_tcb->TS         = ts;
#endif
    switch (p_tcb->TaskState) {
        case OS_TASK_STATE_PEND:
        case OS_TASK_STATE_PEND_TIMEOUT:
#if (OS_CFG_TICK_EN > 0u)
             if (p_tcb->TaskState == OS_TASK_STATE_PEND_TIMEOUT) {
                 OS_TickListRemove(p_tcb);                      /* Remove from tick list                                */
             }
#endif
             OS_RdyListInsert(p_tcb);                           /* Insert the task in the ready list                    */
             p_tcb->TaskState = OS_TASK_STATE_RDY;
             break;

        case OS_TASK_STATE_PEND_SUSPENDED:
        case OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED:
             p_tcb->TaskState = OS_TASK_STATE_SUSPENDED;
             break;

        case OS_TASK_STATE_RDY:
        case OS_TASK_STATE_DLY:
        case OS_TASK_STATE_DLY_SUSPENDED:
        case OS_TASK_STATE_SUSPENDED:
        default:
                                                                /* Default case.                                        */
             break;
    }
    OS_PendListRemove(p_tcb);
}
#endif
