www.pudn.com > uCOS-III.zip > os_pend_multi.c, change:2011-08-02,size:21182b


/* 
************************************************************************************************************************ 
*                                                      uC/OS-III 
*                                                 The Real-Time Kernel 
* 
*                                  (c) Copyright 2009-2011; Micrium, Inc.; Weston, FL 
*                           All rights reserved.  Protected by international copyright laws. 
* 
*                                               PEND ON MULTIPLE OBJECTS 
* 
* File    : OS_PEND_MULTI.C 
* By      : JJL 
* Version : V3.02.00 
* 
* LICENSING TERMS: 
* --------------- 
*           uC/OS-III is provided in source form for FREE short-term evaluation, for educational use or  
*           for peaceful research.  If you plan or intend to use uC/OS-III in a commercial application/ 
*           product then, you need to contact Micrium to properly license uC/OS-III for its use in your  
*           application/product.   We provide ALL the source code for your convenience and to help you  
*           experience uC/OS-III.  The fact that the source is provided does NOT mean that you can use  
*           it commercially without paying a licensing fee. 
* 
*           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, or by phone at +1 (954) 217-2036. 
************************************************************************************************************************ 
*/ 
 
#include <os.h> 
 
#ifdef VSC_INCLUDE_SOURCE_FILE_NAMES 
const  CPU_CHAR  *os_pend_multi__c = "$Id: $"; 
#endif 
 
 
#if (((OS_CFG_Q_EN > 0u) || (OS_CFG_SEM_EN > 0u)) && (OS_CFG_PEND_MULTI_EN > 0u)) 
/* 
************************************************************************************************************************ 
*                                               PEND ON MULTIPLE OBJECTS 
* 
* Description: This function pends on multiple objects.  The objects pended on MUST be either semaphores or message 
*              queues.  If multiple objects are ready at the start of the pend call, then all available objects that 
*              are ready will be indicated to the caller.  If the task must pend on the multiple events then, as soon 
*              as one of the object is either posted, aborted or deleted, the task will be readied. 
* 
*              This function only allows you to pend on semaphores and/or message queues. 
* 
* Arguments  : p_pend_data_tbl   is a pointer to an array of type OS_PEND_DATA which contains a list of all the 
*                                objects we will be waiting on.  The caller must declare an array of OS_PEND_DATA 
*                                and initialize the .PendObjPtr (see below) with a pointer to the object (semaphore or 
*                                message queue) to pend on. 
* 
*                                    OS_PEND_DATA  MyPendArray[?]; 
* 
*                                The OS_PEND_DATA field are as follows: 
* 
*                                    OS_PEND_DATA  *PrevPtr;      Used to link OS_PEND_DATA objects 
*                                    OS_PEND_DATA  *NextPtr;      Used to link OS_PEND_DATA objects 
*                                    OS_TCB        *TCBPtr;       Pointer to the TCB that is pending on multiple objects 
*                                >   OS_PEND_OBJ   *PendObjPtr;   USER supplied field which is a pointer to the 
*                                                                 semaphore or message queue you want to pend on.  When 
*                                                                 you call OSPendMulti() you MUST fill this field for 
*                                                                 each of the objects you want to pend on. 
*                                    OS_PEND_OBJ   *RdyObjPtr;    OSPendMulti() will return the object that was posted, 
*                                                                 aborted or deleted in this field. 
*                                    void          *RdyMsgPtr;    OSPendMulti() will fill in this field if the object 
*                                                                 posted was a message queue.  This corresponds to the 
*                                                                 message posted. 
*                                    OS_MSG_SIZE    RdyMsgSize;   OSPendMulti() will fill in this field if the object 
*                                                                 posted was a message queue.  This corresponds to the 
*                                                                 size of the message posted. 
*                                    CPU_TS         RdyTS;        OSPendMulti() will fill in this field if the object 
*                                                                 was a message queue.  This corresponds to the time 
*                                                                 stamp when the message was posted.  However, if the 
*                                                                 object is a semaphore and the object is already ready 
*                                                                 the this field will be set to (CPU_TS)0 because it's 
*                                                                 not possible to know when the semaphore was posted. 
* 
*              tbl_size      is the size (in number of elements) of the OS_PEND_DATA array passed to this function.  In 
*                            other words, if the called needs to pend on 4 separate objects (semaphores and/or queues) 
*                            then you would pass 4 to this call. 
* 
*              timeout       is an optional timeout period (in clock ticks).  If non-zero, your task will wait any of 
*                            the objects up to the amount of time specified by this argument. If you specify 0, however, 
*                            your task will wait forever for the specified objects or, until an object is posted, 
*                            aborted or deleted. 
* 
*              opt           determines whether the user wants to block if none of the objects are available. 
* 
*                                OS_OPT_PEND_BLOCKING 
*                                OS_OPT_PEND_NON_BLOCKING 
* 
*              p_err         is a pointer to where an error message will be deposited.  Possible error messages are: 
* 
*                                OS_ERR_NONE              The call was successful and your task owns the resources or, 
*                                                         the objects you are waiting for occurred. Check the .RdyObjPtr 
*                                                         fields to know which objects have been posted. 
*                                OS_ERR_OBJ_TYPE          If any of the .PendPtr is NOT a semaphore or a message queue 
*                                OS_ERR_OPT_INVALID       If you specified an invalid option for 'opt' 
*                                OS_ERR_PEND_ABORT        The wait on the events was aborted; check the .RdyObjPtr fields 
*                                                         for which objects were aborted. 
*                                OS_ERR_PEND_DEL          The wait on the events was aborted; check the .RdyObjPtr fields 
*                                                         for which objects were aborted. 
*                                OS_ERR_PEND_ISR          If you called this function from an ISR 
*                                OS_ERR_PEND_LOCKED       If you called this function when the scheduler is locked. 
*                                OS_ERR_PEND_WOULD_BLOCK  If the caller didn't want to block and no object ready 
*                                OS_ERR_STATUS_INVALID    Invalid pend status 
*                                OS_ERR_PTR_INVALID       If you passes a NULL pointer of 'p_pend_data_tbl' 
*                                OS_ERR_TIMEOUT           The objects were not posted within the specified 'timeout'. 
* 
* Returns    : >  0          the number of objects returned as ready, aborted or deleted 
*              == 0          if no events are returned as ready because of timeout or upon error. 
************************************************************************************************************************ 
*/ 
/*$PAGE*/ 
OS_OBJ_QTY  OSPendMulti (OS_PEND_DATA  *p_pend_data_tbl, 
                         OS_OBJ_QTY     tbl_size, 
                         OS_TICK        timeout, 
                         OS_OPT         opt, 
                         OS_ERR        *p_err) 
{ 
    CPU_BOOLEAN   valid; 
    OS_OBJ_QTY    nbr_obj_rdy; 
    CPU_SR_ALLOC(); 
 
 
 
#ifdef OS_SAFETY_CRITICAL 
    if (p_err == (OS_ERR *)0) { 
        OS_SAFETY_CRITICAL_EXCEPTION(); 
        return ((OS_OBJ_QTY)0); 
    } 
#endif 
 
#if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u 
    if (OSIntNestingCtr > (OS_NESTING_CTR)0) {              /* Can't pend from an ISR                                 */ 
       *p_err = OS_ERR_PEND_ISR; 
        return ((OS_OBJ_QTY)0); 
    } 
#endif 
 
#if OS_CFG_ARG_CHK_EN > 0u 
    if (p_pend_data_tbl == (OS_PEND_DATA *)0) {             /* Validate 'p_pend_data_tbl'                             */ 
        *p_err = OS_ERR_PTR_INVALID; 
        return ((OS_OBJ_QTY)0); 
    } 
    if (tbl_size == (OS_OBJ_QTY)0) {                        /* Array size must be > 0                                 */ 
        *p_err = OS_ERR_PTR_INVALID; 
        return ((OS_OBJ_QTY)0); 
    } 
    switch (opt) { 
        case OS_OPT_PEND_BLOCKING: 
        case OS_OPT_PEND_NON_BLOCKING: 
             break; 
 
        default: 
             *p_err = OS_ERR_OPT_INVALID; 
             return ((OS_OBJ_QTY)0); 
    } 
#endif 
 
    valid = OS_PendMultiValidate(p_pend_data_tbl,           /* -------- Validate objects to be OS_SEM or OS_Q ------- */ 
                                 tbl_size); 
    if (valid == DEF_FALSE) { 
        *p_err = OS_ERR_OBJ_TYPE;                           /* Invalid, not OS_SEM or OS_Q                            */ 
        return ((OS_OBJ_QTY)0); 
    } 
 
/*$PAGE*/ 
    CPU_CRITICAL_ENTER(); 
    nbr_obj_rdy = OS_PendMultiGetRdy(p_pend_data_tbl,       /* --------- SEE IF OBJECT(s) HAVE BEEN POSTED ---------- */ 
                                     tbl_size); 
    if (nbr_obj_rdy > (OS_OBJ_QTY)0) { 
        CPU_CRITICAL_EXIT(); 
        *p_err = OS_ERR_NONE; 
        return ((OS_OBJ_QTY)nbr_obj_rdy); 
    } 
 
    if ((opt & OS_OPT_PEND_NON_BLOCKING) != (OS_OPT)0) {    /* Caller wants to block if not available?                */ 
        CPU_CRITICAL_EXIT(); 
        *p_err = OS_ERR_PEND_WOULD_BLOCK;                   /* No                                                     */ 
        return ((OS_OBJ_QTY)0); 
    } else { 
        if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) {    /* Can't pend when the scheduler is locked                */ 
            CPU_CRITICAL_EXIT(); 
            *p_err = OS_ERR_SCHED_LOCKED; 
            return ((OS_OBJ_QTY)0); 
        } 
    } 
    OS_CRITICAL_ENTER_CPU_CRITICAL_EXIT();                  /* Lock the scheduler/re-enable interrupts                */ 
                                                            /* ------ NO OBJECT READY, PEND ON MULTIPLE OBJECTS ----- */ 
    OS_PendMultiWait(p_pend_data_tbl,                       /* Suspend task until object posted or timeout occurs     */ 
                     tbl_size, 
                     timeout); 
 
    OS_CRITICAL_EXIT_NO_SCHED(); 
 
    OSSched();                                              /* Find next highest priority task ready                  */ 
 
    CPU_CRITICAL_ENTER(); 
    switch (OSTCBCurPtr->PendStatus) { 
        case OS_STATUS_PEND_OK:                             /* We got one of the objects posted to                    */ 
             *p_err = OS_ERR_NONE; 
             break; 
 
        case OS_STATUS_PEND_ABORT:                          /* Indicate that the multi-pend was aborted               */ 
             *p_err = OS_ERR_PEND_ABORT; 
             break; 
 
        case OS_STATUS_PEND_TIMEOUT:                        /* Indicate that we didn't get semaphore within timeout   */ 
             *p_err = OS_ERR_TIMEOUT; 
             break; 
 
        case OS_STATUS_PEND_DEL:                            /* Indicate that an object pended on has been deleted     */ 
             *p_err = OS_ERR_OBJ_DEL; 
            break; 
 
        default: 
             *p_err = OS_ERR_STATUS_INVALID; 
             break; 
    } 
 
    OSTCBCurPtr->PendStatus = OS_STATUS_PEND_OK; 
    CPU_CRITICAL_EXIT(); 
 
    return ((OS_OBJ_QTY)1); 
} 
 
/*$PAGE*/ 
/* 
************************************************************************************************************************ 
*                                              GET A LIST OF OBJECTS READY 
* 
* Description: This function is called by OSPendMulti() to obtain the list of object that are ready. 
* 
* Arguments  : p_pend_data_tbl   is a pointer to an array of OS_PEND_DATA 
*              --------------- 
* 
*              tbl_size          is the size of the array 
* 
* Returns    :  > 0              the number of objects ready 
*              == 0              if no object ready 
* 
* Note       : This function is INTERNAL to uC/OS-III and your application should not call it. 
************************************************************************************************************************ 
*/ 
 
OS_OBJ_QTY  OS_PendMultiGetRdy (OS_PEND_DATA  *p_pend_data_tbl, 
                                OS_OBJ_QTY     tbl_size) 
{ 
    OS_OBJ_QTY   i; 
    OS_OBJ_QTY   nbr_obj_rdy; 
#if OS_CFG_Q_EN > 0u 
    OS_ERR       err; 
    OS_MSG_SIZE  msg_size; 
    OS_Q        *p_q; 
    void        *p_void; 
    CPU_TS       ts; 
#endif 
#if OS_CFG_SEM_EN  > 0u 
    OS_SEM      *p_sem; 
#endif 
 
 
 
    nbr_obj_rdy = (OS_OBJ_QTY)0; 
    for (i = 0u; i < tbl_size; i++) { 
        p_pend_data_tbl->RdyObjPtr  = (void         *)0;         /* Clear all fields                                  */ 
        p_pend_data_tbl->RdyMsgPtr  = (void         *)0; 
        p_pend_data_tbl->RdyMsgSize = (OS_MSG_SIZE   )0; 
        p_pend_data_tbl->RdyTS      = (CPU_TS        )0; 
        p_pend_data_tbl->NextPtr    = (OS_PEND_DATA *)0; 
        p_pend_data_tbl->PrevPtr    = (OS_PEND_DATA *)0; 
        p_pend_data_tbl->TCBPtr     = (OS_TCB       *)0; 
#if OS_CFG_Q_EN > 0u 
        p_q = (OS_Q *)((void *)p_pend_data_tbl->PendObjPtr);     /* Assume we are pointing to a message queue object  */ 
        if (p_q->Type == OS_OBJ_TYPE_Q) {                        /* Is it a message queue?                            */ 
            p_void = OS_MsgQGet(&p_q->MsgQ,                      /* Yes, Any message waiting in the message queue?    */ 
                                &msg_size, 
                                &ts, 
                                &err); 
            if (err == OS_ERR_NONE) { 
                p_pend_data_tbl->RdyObjPtr  = p_pend_data_tbl->PendObjPtr; 
                p_pend_data_tbl->RdyMsgPtr  = p_void;            /*      Yes, save the message received               */ 
                p_pend_data_tbl->RdyMsgSize = msg_size; 
                p_pend_data_tbl->RdyTS      = ts; 
                nbr_obj_rdy++; 
            } 
        } 
#endif 
 
#if OS_CFG_SEM_EN > 0u 
        p_sem = (OS_SEM *)((void *)p_pend_data_tbl->PendObjPtr); /* Assume we are pointing to a semaphore object      */ 
        if (p_sem->Type == OS_OBJ_TYPE_SEM) {                    /* Is it a semaphore?                                */ 
            if (p_sem->Ctr > 0u) {                               /* Yes, Semaphore has been signaled?                 */ 
                p_sem->Ctr--;                                    /*      Yes, caller may proceed                      */ 
                p_pend_data_tbl->RdyObjPtr  = p_pend_data_tbl->PendObjPtr; 
                p_pend_data_tbl->RdyTS      = p_sem->TS; 
                nbr_obj_rdy++; 
            } 
        } 
#endif 
 
        p_pend_data_tbl++; 
    } 
    return (nbr_obj_rdy); 
} 
 
/*$PAGE*/ 
/* 
************************************************************************************************************************ 
*                                 VERIFY THAT OBJECTS PENDED ON ARE EITHER SEMAPHORES or QUEUES 
* 
* Description: This function is called by OSPendMulti() to verify that we are multi-pending on either semaphores or 
*              message queues. 
* 
* Arguments  : p_pend_data_tbl    is a pointer to an array of OS_PEND_DATA 
*              --------------- 
* 
*              tbl_size           is the size of the array 
* 
* Returns    : TRUE               if all objects pended on are either semaphores of queues 
*              FALSE              if at least one object is not a semaphore or queue. 
* 
* Note       : This function is INTERNAL to uC/OS-III and your application should not call it. 
************************************************************************************************************************ 
*/ 
 
CPU_BOOLEAN  OS_PendMultiValidate (OS_PEND_DATA  *p_pend_data_tbl, 
                                   OS_OBJ_QTY     tbl_size) 
{ 
    OS_OBJ_QTY  i; 
    OS_OBJ_QTY  ctr; 
#if OS_CFG_SEM_EN  > 0u 
    OS_SEM      *p_sem; 
#endif 
#if OS_CFG_Q_EN > 0u 
    OS_Q        *p_q; 
#endif 
 
 
    for (i = 0u; i < tbl_size; i++) { 
        if (p_pend_data_tbl->PendObjPtr == (OS_PEND_OBJ *)0) {   /* All .PendObjPtr in the table MUST be non NULL     */ 
            return (DEF_FALSE); 
        } 
 
        ctr = 0u; 
#if OS_CFG_SEM_EN  > 0u 
        p_sem = (OS_SEM *)((void *)p_pend_data_tbl->PendObjPtr); /* All objects to pend on must be of type OS_SEM ... */ 
        if (p_sem->Type == OS_OBJ_TYPE_SEM) { 
            ctr++; 
        } 
#endif 
 
#if OS_CFG_Q_EN > 0u 
        p_q = (OS_Q *)((void *)p_pend_data_tbl->PendObjPtr);     /* ... or of type OS_Q                               */ 
        if (p_q->Type == OS_OBJ_TYPE_Q) { 
            ctr++; 
        } 
#endif 
 
        if (ctr == (OS_OBJ_QTY)0) { 
            return (DEF_FALSE);                                  /* Found at least one invalid object type            */ 
        } 
        p_pend_data_tbl++; 
    } 
    return (DEF_TRUE); 
} 
 
/*$PAGE*/ 
/* 
************************************************************************************************************************ 
*                                 MAKE TASK WAIT FOR ANY OF MULTIPLE EVENTS TO OCCUR 
* 
* Description: This function is called by OSPendMulti() to suspend a task because any one of multiple objects that have 
*              not been posted to. 
* 
* Arguments  : p_pend_data_tbl    is a pointer to an array of OS_PEND_DATA 
*              --------------- 
* 
*              tbl_size           is the size of the array 
* 
*              timeout            is the timeout to wait in case none of the objects become ready 
* 
* Returns    : none 
* 
* Note       : This function is INTERNAL to uC/OS-III and your application should not call it. 
************************************************************************************************************************ 
*/ 
 
void  OS_PendMultiWait (OS_PEND_DATA  *p_pend_data_tbl, 
                        OS_OBJ_QTY     tbl_size, 
                        OS_TICK        timeout) 
{ 
    OS_OBJ_QTY      i; 
    OS_PEND_LIST   *p_pend_list; 
 
#if OS_CFG_Q_EN > 0u 
    OS_Q           *p_q; 
#endif 
 
#if OS_CFG_SEM_EN > 0u 
    OS_SEM         *p_sem; 
#endif 
 
 
 
    OSTCBCurPtr->PendOn             = OS_TASK_PEND_ON_MULTI;   /* Resource not available, wait until it is            */ 
    OSTCBCurPtr->PendStatus         = OS_STATUS_PEND_OK; 
    OSTCBCurPtr->PendDataTblEntries = tbl_size; 
    OSTCBCurPtr->PendDataTblPtr     = p_pend_data_tbl; 
 
    OS_TaskBlock(OSTCBCurPtr,                                  /* Block the task waiting for object to be posted ...  */ 
                 timeout);                                     /* ... but with a timeout if not                       */ 
 
    for (i = 0u; i < tbl_size; i++) { 
        p_pend_data_tbl->TCBPtr = OSTCBCurPtr;                 /* Every entry points back to the TCB of the task      */ 
 
#if OS_CFG_SEM_EN > 0u 
        p_sem = (OS_SEM *)((void *)p_pend_data_tbl->PendObjPtr); 
        if (p_sem->Type == OS_OBJ_TYPE_SEM) { 
            p_pend_list = &p_sem->PendList; 
            OS_PendListInsertPrio(p_pend_list, 
                                  p_pend_data_tbl); 
        } 
#endif 
 
#if OS_CFG_Q_EN > 0u 
        p_q = (OS_Q *)((void *)p_pend_data_tbl->PendObjPtr); 
        if (p_q->Type == OS_OBJ_TYPE_Q) { 
            p_pend_list = &p_q->PendList; 
            OS_PendListInsertPrio(p_pend_list, 
                                  p_pend_data_tbl); 
        } 
#endif 
 
        p_pend_data_tbl++; 
    } 
} 
 
#endif