os_pend_multi.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  1. /*
  2. ************************************************************************************************************************
  3. * uC/OS-III
  4. * The Real-Time Kernel
  5. *
  6. * (c) Copyright 2009-2011; Micrium, Inc.; Weston, FL
  7. * All rights reserved. Protected by international copyright laws.
  8. *
  9. * PEND ON MULTIPLE OBJECTS
  10. *
  11. * File : OS_PEND_MULTI.C
  12. * By : JJL
  13. * Version : V3.02.00
  14. *
  15. * LICENSING TERMS:
  16. * ---------------
  17. * uC/OS-III is provided in source form for FREE short-term evaluation, for educational use or
  18. * for peaceful research. If you plan or intend to use uC/OS-III in a commercial application/
  19. * product then, you need to contact Micrium to properly license uC/OS-III for its use in your
  20. * application/product. We provide ALL the source code for your convenience and to help you
  21. * experience uC/OS-III. The fact that the source is provided does NOT mean that you can use
  22. * it commercially without paying a licensing fee.
  23. *
  24. * Knowledge of the source code may NOT be used to develop a similar product.
  25. *
  26. * Please help us continue to provide the embedded community with the finest software available.
  27. * Your honesty is greatly appreciated.
  28. *
  29. * You can contact us at www.micrium.com, or by phone at +1 (954) 217-2036.
  30. ************************************************************************************************************************
  31. */
  32. #include <os.h>
  33. #ifdef VSC_INCLUDE_SOURCE_FILE_NAMES
  34. const CPU_CHAR *os_pend_multi__c = "$Id: $";
  35. #endif
  36. #if (((OS_CFG_Q_EN > 0u) || (OS_CFG_SEM_EN > 0u)) && (OS_CFG_PEND_MULTI_EN > 0u))
  37. /*
  38. ************************************************************************************************************************
  39. * PEND ON MULTIPLE OBJECTS
  40. *
  41. * Description: This function pends on multiple objects. The objects pended on MUST be either semaphores or message
  42. * queues. If multiple objects are ready at the start of the pend call, then all available objects that
  43. * are ready will be indicated to the caller. If the task must pend on the multiple events then, as soon
  44. * as one of the object is either posted, aborted or deleted, the task will be readied.
  45. *
  46. * This function only allows you to pend on semaphores and/or message queues.
  47. *
  48. * Arguments : p_pend_data_tbl is a pointer to an array of type OS_PEND_DATA which contains a list of all the
  49. * objects we will be waiting on. The caller must declare an array of OS_PEND_DATA
  50. * and initialize the .PendObjPtr (see below) with a pointer to the object (semaphore or
  51. * message queue) to pend on.
  52. *
  53. * OS_PEND_DATA MyPendArray[?];
  54. *
  55. * The OS_PEND_DATA field are as follows:
  56. *
  57. * OS_PEND_DATA *PrevPtr; Used to link OS_PEND_DATA objects
  58. * OS_PEND_DATA *NextPtr; Used to link OS_PEND_DATA objects
  59. * OS_TCB *TCBPtr; Pointer to the TCB that is pending on multiple objects
  60. * > OS_PEND_OBJ *PendObjPtr; USER supplied field which is a pointer to the
  61. * semaphore or message queue you want to pend on. When
  62. * you call OSPendMulti() you MUST fill this field for
  63. * each of the objects you want to pend on.
  64. * OS_PEND_OBJ *RdyObjPtr; OSPendMulti() will return the object that was posted,
  65. * aborted or deleted in this field.
  66. * void *RdyMsgPtr; OSPendMulti() will fill in this field if the object
  67. * posted was a message queue. This corresponds to the
  68. * message posted.
  69. * OS_MSG_SIZE RdyMsgSize; OSPendMulti() will fill in this field if the object
  70. * posted was a message queue. This corresponds to the
  71. * size of the message posted.
  72. * CPU_TS RdyTS; OSPendMulti() will fill in this field if the object
  73. * was a message queue. This corresponds to the time
  74. * stamp when the message was posted. However, if the
  75. * object is a semaphore and the object is already ready
  76. * the this field will be set to (CPU_TS)0 because it's
  77. * not possible to know when the semaphore was posted.
  78. *
  79. * tbl_size is the size (in number of elements) of the OS_PEND_DATA array passed to this function. In
  80. * other words, if the called needs to pend on 4 separate objects (semaphores and/or queues)
  81. * then you would pass 4 to this call.
  82. *
  83. * timeout is an optional timeout period (in clock ticks). If non-zero, your task will wait any of
  84. * the objects up to the amount of time specified by this argument. If you specify 0, however,
  85. * your task will wait forever for the specified objects or, until an object is posted,
  86. * aborted or deleted.
  87. *
  88. * opt determines whether the user wants to block if none of the objects are available.
  89. *
  90. * OS_OPT_PEND_BLOCKING
  91. * OS_OPT_PEND_NON_BLOCKING
  92. *
  93. * p_err is a pointer to where an error message will be deposited. Possible error messages are:
  94. *
  95. * OS_ERR_NONE The call was successful and your task owns the resources or,
  96. * the objects you are waiting for occurred. Check the .RdyObjPtr
  97. * fields to know which objects have been posted.
  98. * OS_ERR_OBJ_TYPE If any of the .PendPtr is NOT a semaphore or a message queue
  99. * OS_ERR_OPT_INVALID If you specified an invalid option for 'opt'
  100. * OS_ERR_PEND_ABORT The wait on the events was aborted; check the .RdyObjPtr fields
  101. * for which objects were aborted.
  102. * OS_ERR_PEND_DEL The wait on the events was aborted; check the .RdyObjPtr fields
  103. * for which objects were aborted.
  104. * OS_ERR_PEND_ISR If you called this function from an ISR
  105. * OS_ERR_PEND_LOCKED If you called this function when the scheduler is locked.
  106. * OS_ERR_PEND_WOULD_BLOCK If the caller didn't want to block and no object ready
  107. * OS_ERR_STATUS_INVALID Invalid pend status
  108. * OS_ERR_PTR_INVALID If you passes a NULL pointer of 'p_pend_data_tbl'
  109. * OS_ERR_TIMEOUT The objects were not posted within the specified 'timeout'.
  110. *
  111. * Returns : > 0 the number of objects returned as ready, aborted or deleted
  112. * == 0 if no events are returned as ready because of timeout or upon error.
  113. ************************************************************************************************************************
  114. */
  115. /*$PAGE*/
  116. OS_OBJ_QTY OSPendMulti (OS_PEND_DATA *p_pend_data_tbl,
  117. OS_OBJ_QTY tbl_size,
  118. OS_TICK timeout,
  119. OS_OPT opt,
  120. OS_ERR *p_err)
  121. {
  122. CPU_BOOLEAN valid;
  123. OS_OBJ_QTY nbr_obj_rdy;
  124. CPU_SR_ALLOC();
  125. #ifdef OS_SAFETY_CRITICAL
  126. if (p_err == (OS_ERR *)0) {
  127. OS_SAFETY_CRITICAL_EXCEPTION();
  128. return ((OS_OBJ_QTY)0);
  129. }
  130. #endif
  131. #if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u
  132. if (OSIntNestingCtr > (OS_NESTING_CTR)0) { /* Can't pend from an ISR */
  133. *p_err = OS_ERR_PEND_ISR;
  134. return ((OS_OBJ_QTY)0);
  135. }
  136. #endif
  137. #if OS_CFG_ARG_CHK_EN > 0u
  138. if (p_pend_data_tbl == (OS_PEND_DATA *)0) { /* Validate 'p_pend_data_tbl' */
  139. *p_err = OS_ERR_PTR_INVALID;
  140. return ((OS_OBJ_QTY)0);
  141. }
  142. if (tbl_size == (OS_OBJ_QTY)0) { /* Array size must be > 0 */
  143. *p_err = OS_ERR_PTR_INVALID;
  144. return ((OS_OBJ_QTY)0);
  145. }
  146. switch (opt) {
  147. case OS_OPT_PEND_BLOCKING:
  148. case OS_OPT_PEND_NON_BLOCKING:
  149. break;
  150. default:
  151. *p_err = OS_ERR_OPT_INVALID;
  152. return ((OS_OBJ_QTY)0);
  153. }
  154. #endif
  155. valid = OS_PendMultiValidate(p_pend_data_tbl, /* -------- Validate objects to be OS_SEM or OS_Q ------- */
  156. tbl_size);
  157. if (valid == DEF_FALSE) {
  158. *p_err = OS_ERR_OBJ_TYPE; /* Invalid, not OS_SEM or OS_Q */
  159. return ((OS_OBJ_QTY)0);
  160. }
  161. /*$PAGE*/
  162. CPU_CRITICAL_ENTER();
  163. nbr_obj_rdy = OS_PendMultiGetRdy(p_pend_data_tbl, /* --------- SEE IF OBJECT(s) HAVE BEEN POSTED ---------- */
  164. tbl_size);
  165. if (nbr_obj_rdy > (OS_OBJ_QTY)0) {
  166. CPU_CRITICAL_EXIT();
  167. *p_err = OS_ERR_NONE;
  168. return ((OS_OBJ_QTY)nbr_obj_rdy);
  169. }
  170. if ((opt & OS_OPT_PEND_NON_BLOCKING) != (OS_OPT)0) { /* Caller wants to block if not available? */
  171. CPU_CRITICAL_EXIT();
  172. *p_err = OS_ERR_PEND_WOULD_BLOCK; /* No */
  173. return ((OS_OBJ_QTY)0);
  174. } else {
  175. if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) { /* Can't pend when the scheduler is locked */
  176. CPU_CRITICAL_EXIT();
  177. *p_err = OS_ERR_SCHED_LOCKED;
  178. return ((OS_OBJ_QTY)0);
  179. }
  180. }
  181. OS_CRITICAL_ENTER_CPU_CRITICAL_EXIT(); /* Lock the scheduler/re-enable interrupts */
  182. /* ------ NO OBJECT READY, PEND ON MULTIPLE OBJECTS ----- */
  183. OS_PendMultiWait(p_pend_data_tbl, /* Suspend task until object posted or timeout occurs */
  184. tbl_size,
  185. timeout);
  186. OS_CRITICAL_EXIT_NO_SCHED();
  187. OSSched(); /* Find next highest priority task ready */
  188. CPU_CRITICAL_ENTER();
  189. switch (OSTCBCurPtr->PendStatus) {
  190. case OS_STATUS_PEND_OK: /* We got one of the objects posted to */
  191. *p_err = OS_ERR_NONE;
  192. break;
  193. case OS_STATUS_PEND_ABORT: /* Indicate that the multi-pend was aborted */
  194. *p_err = OS_ERR_PEND_ABORT;
  195. break;
  196. case OS_STATUS_PEND_TIMEOUT: /* Indicate that we didn't get semaphore within timeout */
  197. *p_err = OS_ERR_TIMEOUT;
  198. break;
  199. case OS_STATUS_PEND_DEL: /* Indicate that an object pended on has been deleted */
  200. *p_err = OS_ERR_OBJ_DEL;
  201. break;
  202. default:
  203. *p_err = OS_ERR_STATUS_INVALID;
  204. break;
  205. }
  206. OSTCBCurPtr->PendStatus = OS_STATUS_PEND_OK;
  207. CPU_CRITICAL_EXIT();
  208. return ((OS_OBJ_QTY)1);
  209. }
  210. /*$PAGE*/
  211. /*
  212. ************************************************************************************************************************
  213. * GET A LIST OF OBJECTS READY
  214. *
  215. * Description: This function is called by OSPendMulti() to obtain the list of object that are ready.
  216. *
  217. * Arguments : p_pend_data_tbl is a pointer to an array of OS_PEND_DATA
  218. * ---------------
  219. *
  220. * tbl_size is the size of the array
  221. *
  222. * Returns : > 0 the number of objects ready
  223. * == 0 if no object ready
  224. *
  225. * Note : This function is INTERNAL to uC/OS-III and your application should not call it.
  226. ************************************************************************************************************************
  227. */
  228. OS_OBJ_QTY OS_PendMultiGetRdy (OS_PEND_DATA *p_pend_data_tbl,
  229. OS_OBJ_QTY tbl_size)
  230. {
  231. OS_OBJ_QTY i;
  232. OS_OBJ_QTY nbr_obj_rdy;
  233. #if OS_CFG_Q_EN > 0u
  234. OS_ERR err;
  235. OS_MSG_SIZE msg_size;
  236. OS_Q *p_q;
  237. void *p_void;
  238. CPU_TS ts;
  239. #endif
  240. #if OS_CFG_SEM_EN > 0u
  241. OS_SEM *p_sem;
  242. #endif
  243. nbr_obj_rdy = (OS_OBJ_QTY)0;
  244. for (i = 0u; i < tbl_size; i++) {
  245. p_pend_data_tbl->RdyObjPtr = (void *)0; /* Clear all fields */
  246. p_pend_data_tbl->RdyMsgPtr = (void *)0;
  247. p_pend_data_tbl->RdyMsgSize = (OS_MSG_SIZE )0;
  248. p_pend_data_tbl->RdyTS = (CPU_TS )0;
  249. p_pend_data_tbl->NextPtr = (OS_PEND_DATA *)0;
  250. p_pend_data_tbl->PrevPtr = (OS_PEND_DATA *)0;
  251. p_pend_data_tbl->TCBPtr = (OS_TCB *)0;
  252. #if OS_CFG_Q_EN > 0u
  253. p_q = (OS_Q *)((void *)p_pend_data_tbl->PendObjPtr); /* Assume we are pointing to a message queue object */
  254. if (p_q->Type == OS_OBJ_TYPE_Q) { /* Is it a message queue? */
  255. p_void = OS_MsgQGet(&p_q->MsgQ, /* Yes, Any message waiting in the message queue? */
  256. &msg_size,
  257. &ts,
  258. &err);
  259. if (err == OS_ERR_NONE) {
  260. p_pend_data_tbl->RdyObjPtr = p_pend_data_tbl->PendObjPtr;
  261. p_pend_data_tbl->RdyMsgPtr = p_void; /* Yes, save the message received */
  262. p_pend_data_tbl->RdyMsgSize = msg_size;
  263. p_pend_data_tbl->RdyTS = ts;
  264. nbr_obj_rdy++;
  265. }
  266. }
  267. #endif
  268. #if OS_CFG_SEM_EN > 0u
  269. p_sem = (OS_SEM *)((void *)p_pend_data_tbl->PendObjPtr); /* Assume we are pointing to a semaphore object */
  270. if (p_sem->Type == OS_OBJ_TYPE_SEM) { /* Is it a semaphore? */
  271. if (p_sem->Ctr > 0u) { /* Yes, Semaphore has been signaled? */
  272. p_sem->Ctr--; /* Yes, caller may proceed */
  273. p_pend_data_tbl->RdyObjPtr = p_pend_data_tbl->PendObjPtr;
  274. p_pend_data_tbl->RdyTS = p_sem->TS;
  275. nbr_obj_rdy++;
  276. }
  277. }
  278. #endif
  279. p_pend_data_tbl++;
  280. }
  281. return (nbr_obj_rdy);
  282. }
  283. /*$PAGE*/
  284. /*
  285. ************************************************************************************************************************
  286. * VERIFY THAT OBJECTS PENDED ON ARE EITHER SEMAPHORES or QUEUES
  287. *
  288. * Description: This function is called by OSPendMulti() to verify that we are multi-pending on either semaphores or
  289. * message queues.
  290. *
  291. * Arguments : p_pend_data_tbl is a pointer to an array of OS_PEND_DATA
  292. * ---------------
  293. *
  294. * tbl_size is the size of the array
  295. *
  296. * Returns : TRUE if all objects pended on are either semaphores of queues
  297. * FALSE if at least one object is not a semaphore or queue.
  298. *
  299. * Note : This function is INTERNAL to uC/OS-III and your application should not call it.
  300. ************************************************************************************************************************
  301. */
  302. CPU_BOOLEAN OS_PendMultiValidate (OS_PEND_DATA *p_pend_data_tbl,
  303. OS_OBJ_QTY tbl_size)
  304. {
  305. OS_OBJ_QTY i;
  306. OS_OBJ_QTY ctr;
  307. #if OS_CFG_SEM_EN > 0u
  308. OS_SEM *p_sem;
  309. #endif
  310. #if OS_CFG_Q_EN > 0u
  311. OS_Q *p_q;
  312. #endif
  313. for (i = 0u; i < tbl_size; i++) {
  314. if (p_pend_data_tbl->PendObjPtr == (OS_PEND_OBJ *)0) { /* All .PendObjPtr in the table MUST be non NULL */
  315. return (DEF_FALSE);
  316. }
  317. ctr = 0u;
  318. #if OS_CFG_SEM_EN > 0u
  319. p_sem = (OS_SEM *)((void *)p_pend_data_tbl->PendObjPtr); /* All objects to pend on must be of type OS_SEM ... */
  320. if (p_sem->Type == OS_OBJ_TYPE_SEM) {
  321. ctr++;
  322. }
  323. #endif
  324. #if OS_CFG_Q_EN > 0u
  325. p_q = (OS_Q *)((void *)p_pend_data_tbl->PendObjPtr); /* ... or of type OS_Q */
  326. if (p_q->Type == OS_OBJ_TYPE_Q) {
  327. ctr++;
  328. }
  329. #endif
  330. if (ctr == (OS_OBJ_QTY)0) {
  331. return (DEF_FALSE); /* Found at least one invalid object type */
  332. }
  333. p_pend_data_tbl++;
  334. }
  335. return (DEF_TRUE);
  336. }
  337. /*$PAGE*/
  338. /*
  339. ************************************************************************************************************************
  340. * MAKE TASK WAIT FOR ANY OF MULTIPLE EVENTS TO OCCUR
  341. *
  342. * Description: This function is called by OSPendMulti() to suspend a task because any one of multiple objects that have
  343. * not been posted to.
  344. *
  345. * Arguments : p_pend_data_tbl is a pointer to an array of OS_PEND_DATA
  346. * ---------------
  347. *
  348. * tbl_size is the size of the array
  349. *
  350. * timeout is the timeout to wait in case none of the objects become ready
  351. *
  352. * Returns : none
  353. *
  354. * Note : This function is INTERNAL to uC/OS-III and your application should not call it.
  355. ************************************************************************************************************************
  356. */
  357. void OS_PendMultiWait (OS_PEND_DATA *p_pend_data_tbl,
  358. OS_OBJ_QTY tbl_size,
  359. OS_TICK timeout)
  360. {
  361. OS_OBJ_QTY i;
  362. OS_PEND_LIST *p_pend_list;
  363. #if OS_CFG_Q_EN > 0u
  364. OS_Q *p_q;
  365. #endif
  366. #if OS_CFG_SEM_EN > 0u
  367. OS_SEM *p_sem;
  368. #endif
  369. OSTCBCurPtr->PendOn = OS_TASK_PEND_ON_MULTI; /* Resource not available, wait until it is */
  370. OSTCBCurPtr->PendStatus = OS_STATUS_PEND_OK;
  371. OSTCBCurPtr->PendDataTblEntries = tbl_size;
  372. OSTCBCurPtr->PendDataTblPtr = p_pend_data_tbl;
  373. OS_TaskBlock(OSTCBCurPtr, /* Block the task waiting for object to be posted ... */
  374. timeout); /* ... but with a timeout if not */
  375. for (i = 0u; i < tbl_size; i++) {
  376. p_pend_data_tbl->TCBPtr = OSTCBCurPtr; /* Every entry points back to the TCB of the task */
  377. #if OS_CFG_SEM_EN > 0u
  378. p_sem = (OS_SEM *)((void *)p_pend_data_tbl->PendObjPtr);
  379. if (p_sem->Type == OS_OBJ_TYPE_SEM) {
  380. p_pend_list = &p_sem->PendList;
  381. OS_PendListInsertPrio(p_pend_list,
  382. p_pend_data_tbl);
  383. }
  384. #endif
  385. #if OS_CFG_Q_EN > 0u
  386. p_q = (OS_Q *)((void *)p_pend_data_tbl->PendObjPtr);
  387. if (p_q->Type == OS_OBJ_TYPE_Q) {
  388. p_pend_list = &p_q->PendList;
  389. OS_PendListInsertPrio(p_pend_list,
  390. p_pend_data_tbl);
  391. }
  392. #endif
  393. p_pend_data_tbl++;
  394. }
  395. }
  396. #endif