]> git.sur5r.net Git - freertos/blob - Source/queue.c
Update to V4.6.1 - including PIC32MX port.
[freertos] / Source / queue.c
1 /*\r
2         FreeRTOS.org V4.6.1 - Copyright (C) 2003-2007 Richard Barry.\r
3 \r
4         This file is part of the FreeRTOS.org distribution.\r
5 \r
6         FreeRTOS.org is free software; you can redistribute it and/or modify\r
7         it under the terms of the GNU General Public License as published by\r
8         the Free Software Foundation; either version 2 of the License, or\r
9         (at your option) any later version.\r
10 \r
11         FreeRTOS.org is distributed in the hope that it will be useful,\r
12         but WITHOUT ANY WARRANTY; without even the implied warranty of\r
13         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
14         GNU General Public License for more details.\r
15 \r
16         You should have received a copy of the GNU General Public License\r
17         along with FreeRTOS.org; if not, write to the Free Software\r
18         Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
19 \r
20         A special exception to the GPL can be applied should you wish to distribute\r
21         a combined work that includes FreeRTOS.org, without being obliged to provide\r
22         the source code for any proprietary components.  See the licensing section\r
23         of http://www.FreeRTOS.org for full details of how and when the exception\r
24         can be applied.\r
25 \r
26         ***************************************************************************\r
27         See http://www.FreeRTOS.org for documentation, latest information, license\r
28         and contact details.  Please ensure to read the configuration and relevant\r
29         port sections of the online documentation.\r
30 \r
31         Also see http://www.SafeRTOS.com a version that has been certified for use\r
32         in safety critical systems, plus commercial licensing, development and\r
33         support options.\r
34         ***************************************************************************\r
35 */\r
36 \r
37 /*\r
38 Changes from V1.01\r
39 \r
40         + More use of 8bit data types.\r
41         + Function name prefixes changed where the data type returned has changed.\r
42 \r
43 Changed from V2.0.0\r
44 \r
45         + Added the queue locking mechanism and make more use of the scheduler\r
46           suspension feature to minimise the time interrupts have to be disabled\r
47           when accessing a queue.\r
48 \r
49 Changed from V2.2.0\r
50 \r
51         + Explicit use of 'signed' qualifier on portCHAR types added.\r
52 \r
53 Changes from V3.0.0\r
54 \r
55         + API changes as described on the FreeRTOS.org WEB site.\r
56 \r
57 Changes from V3.2.3\r
58 \r
59         + Added the queue functions that can be used from co-routines.\r
60 \r
61 Changes from V4.0.5\r
62 \r
63         + Added a loop within xQueueSend() and xQueueReceive() to prevent the\r
64           functions exiting when a block time remains and the function has\r
65           not completed.\r
66 \r
67 Changes from V4.1.2:\r
68 \r
69         + BUG FIX:  Removed the call to prvIsQueueEmpty from within xQueueCRReceive\r
70           as it exited with interrupts enabled.  Thanks Paul Katz.\r
71 \r
72 Changes from V4.1.3:\r
73 \r
74         + Modified xQueueSend() and xQueueReceive() to handle the (very unlikely)\r
75         case whereby a task unblocking due to a temporal event can remove/send an\r
76         item from/to a queue when a higher priority task is     still blocked on the\r
77         queue.  This modification is a result of the SafeRTOS testing.\r
78 */\r
79 \r
80 #include <stdlib.h>\r
81 #include <string.h>\r
82 #include "FreeRTOS.h"\r
83 #include "task.h"\r
84 #include "croutine.h"\r
85 \r
86 /*-----------------------------------------------------------\r
87  * PUBLIC LIST API documented in list.h\r
88  *----------------------------------------------------------*/\r
89 \r
90 /* Constants used with the cRxLock and cTxLock structure members. */\r
91 #define queueUNLOCKED   ( ( signed portBASE_TYPE ) -1 )\r
92 #define queueERRONEOUS_UNBLOCK                                  ( -1 )\r
93 \r
94 /* For internal use only. */\r
95 #define queueSEND_TO_BACK       ( 0 )\r
96 #define queueSEND_TO_FRONT      ( 1 )\r
97 \r
98 /* Effectively make a union out of the xQUEUE structure. */\r
99 #define pxMutexHolder                           pcTail\r
100 #define uxQueueType                                     pcHead\r
101 #define queueQUEUE_IS_MUTEX                     NULL\r
102 \r
103 /*\r
104  * Definition of the queue used by the scheduler.\r
105  * Items are queued by copy, not reference.\r
106  */\r
107 typedef struct QueueDefinition\r
108 {\r
109         signed portCHAR *pcHead;                                /*< Points to the beginning of the queue storage area. */\r
110         signed portCHAR *pcTail;                                /*< Points to the byte at the end of the queue storage area.  Once more byte is allocated than necessary to store the queue items, this is used as a marker. */\r
111 \r
112         signed portCHAR *pcWriteTo;                             /*< Points to the free next place in the storage area. */\r
113         signed portCHAR *pcReadFrom;                    /*< Points to the last place that a queued item was read from. */\r
114 \r
115         xList xTasksWaitingToSend;                              /*< List of tasks that are blocked waiting to post onto this queue.  Stored in priority order. */\r
116         xList xTasksWaitingToReceive;                   /*< List of tasks that are blocked waiting to read from this queue.  Stored in priority order. */\r
117 \r
118         unsigned portBASE_TYPE uxMessagesWaiting;/*< The number of items currently in the queue. */\r
119         unsigned portBASE_TYPE uxLength;                /*< The length of the queue defined as the number of items it will hold, not the number of bytes. */\r
120         unsigned portBASE_TYPE uxItemSize;              /*< The size of each items that the queue will hold. */\r
121 \r
122         signed portBASE_TYPE xRxLock;                   /*< Stores the number of items received from the queue (removed from the queue) while the queue was locked.  Set to queueUNLOCKED when the queue is not locked. */\r
123         signed portBASE_TYPE xTxLock;                   /*< Stores the number of items transmitted to the queue (added to the queue) while the queue was locked.  Set to queueUNLOCKED when the queue is not locked. */\r
124 } xQUEUE;\r
125 /*-----------------------------------------------------------*/\r
126 \r
127 /*\r
128  * Inside this file xQueueHandle is a pointer to a xQUEUE structure.\r
129  * To keep the definition private the API header file defines it as a\r
130  * pointer to void.\r
131  */\r
132 typedef xQUEUE * xQueueHandle;\r
133 \r
134 /*\r
135  * Prototypes for public functions are included here so we don't have to\r
136  * include the API header file (as it defines xQueueHandle differently).  These\r
137  * functions are documented in the API header file.\r
138  */\r
139 xQueueHandle xQueueCreate( unsigned portBASE_TYPE uxQueueLength, unsigned portBASE_TYPE uxItemSize );\r
140 signed portBASE_TYPE xQueueGenericSend( xQueueHandle xQueue, const void * const pvItemToQueue, portTickType xTicksToWait, portBASE_TYPE xCopyPosition );\r
141 unsigned portBASE_TYPE uxQueueMessagesWaiting( const xQueueHandle pxQueue );\r
142 void vQueueDelete( xQueueHandle xQueue );\r
143 signed portBASE_TYPE xQueueGenericSendFromISR( xQueueHandle pxQueue, const void * const pvItemToQueue, signed portBASE_TYPE xTaskPreviouslyWoken, portBASE_TYPE xCopyPosition );\r
144 signed portBASE_TYPE xQueueGenericReceive( xQueueHandle pxQueue, const void * const pvBuffer, portTickType xTicksToWait, portBASE_TYPE xJustPeeking );\r
145 signed portBASE_TYPE xQueueReceiveFromISR( xQueueHandle pxQueue, const void * const pvBuffer, signed portBASE_TYPE *pxTaskWoken );\r
146 xQueueHandle xQueueCreateMutex( void );\r
147 \r
148 #if configUSE_CO_ROUTINES == 1\r
149         signed portBASE_TYPE xQueueCRSendFromISR( xQueueHandle pxQueue, const void *pvItemToQueue, signed portBASE_TYPE xCoRoutinePreviouslyWoken );\r
150         signed portBASE_TYPE xQueueCRReceiveFromISR( xQueueHandle pxQueue, void *pvBuffer, signed portBASE_TYPE *pxTaskWoken );\r
151         signed portBASE_TYPE xQueueCRSend( xQueueHandle pxQueue, const void *pvItemToQueue, portTickType xTicksToWait );\r
152         signed portBASE_TYPE xQueueCRReceive( xQueueHandle pxQueue, void *pvBuffer, portTickType xTicksToWait );\r
153 #endif\r
154 \r
155 /*\r
156  * Unlocks a queue locked by a call to prvLockQueue.  Locking a queue does not\r
157  * prevent an ISR from adding or removing items to the queue, but does prevent\r
158  * an ISR from removing tasks from the queue event lists.  If an ISR finds a\r
159  * queue is locked it will instead increment the appropriate queue lock count\r
160  * to indicate that a task may require unblocking.  When the queue in unlocked\r
161  * these lock counts are inspected, and the appropriate action taken.\r
162  */\r
163 static void prvUnlockQueue( xQueueHandle pxQueue );\r
164 \r
165 /*\r
166  * Uses a critical section to determine if there is any data in a queue.\r
167  *\r
168  * @return pdTRUE if the queue contains no items, otherwise pdFALSE.\r
169  */\r
170 static signed portBASE_TYPE prvIsQueueEmpty( const xQueueHandle pxQueue );\r
171 \r
172 /*\r
173  * Uses a critical section to determine if there is any space in a queue.\r
174  *\r
175  * @return pdTRUE if there is no space, otherwise pdFALSE;\r
176  */\r
177 static signed portBASE_TYPE prvIsQueueFull( const xQueueHandle pxQueue );\r
178 \r
179 /*\r
180  * Copies an item into the queue, either at the front of the queue or the\r
181  * back of the queue.\r
182  */\r
183 static void prvCopyDataToQueue( xQUEUE *pxQueue, const void *pvItemToQueue, portBASE_TYPE xPosition );\r
184 \r
185 /*\r
186  * Copies an item out of a queue.\r
187  */\r
188 static void prvCopyDataFromQueue( xQUEUE * const pxQueue, const void *pvBuffer );\r
189 /*-----------------------------------------------------------*/\r
190 \r
191 /*\r
192  * Macro to mark a queue as locked.  Locking a queue prevents an ISR from\r
193  * accessing the queue event lists.\r
194  */\r
195 #define prvLockQueue( pxQueue )                 \\r
196 {                                                                               \\r
197         taskENTER_CRITICAL();                           \\r
198                 ++( pxQueue->xRxLock );                 \\r
199                 ++( pxQueue->xTxLock );                 \\r
200         taskEXIT_CRITICAL();                            \\r
201 }\r
202 /*-----------------------------------------------------------*/\r
203 \r
204 \r
205 /*-----------------------------------------------------------\r
206  * PUBLIC QUEUE MANAGEMENT API documented in queue.h\r
207  *----------------------------------------------------------*/\r
208 \r
209 xQueueHandle xQueueCreate( unsigned portBASE_TYPE uxQueueLength, unsigned portBASE_TYPE uxItemSize )\r
210 {\r
211 xQUEUE *pxNewQueue;\r
212 size_t xQueueSizeInBytes;\r
213 \r
214         /* Allocate the new queue structure. */\r
215         if( uxQueueLength > ( unsigned portBASE_TYPE ) 0 )\r
216         {\r
217                 pxNewQueue = ( xQUEUE * ) pvPortMalloc( sizeof( xQUEUE ) );\r
218                 if( pxNewQueue != NULL )\r
219                 {\r
220                         /* Create the list of pointers to queue items.  The queue is one byte\r
221                         longer than asked for to make wrap checking easier/faster. */\r
222                         xQueueSizeInBytes = ( size_t ) ( uxQueueLength * uxItemSize ) + ( size_t ) 1;\r
223 \r
224                         pxNewQueue->pcHead = ( signed portCHAR * ) pvPortMalloc( xQueueSizeInBytes );\r
225                         if( pxNewQueue->pcHead != NULL )\r
226                         {\r
227                                 /* Initialise the queue members as described above where the\r
228                                 queue type is defined. */\r
229                                 pxNewQueue->pcTail = pxNewQueue->pcHead + ( uxQueueLength * uxItemSize );\r
230                                 pxNewQueue->uxMessagesWaiting = 0;\r
231                                 pxNewQueue->pcWriteTo = pxNewQueue->pcHead;\r
232                                 pxNewQueue->pcReadFrom = pxNewQueue->pcHead + ( ( uxQueueLength - 1 ) * uxItemSize );\r
233                                 pxNewQueue->uxLength = uxQueueLength;\r
234                                 pxNewQueue->uxItemSize = uxItemSize;\r
235                                 pxNewQueue->xRxLock = queueUNLOCKED;\r
236                                 pxNewQueue->xTxLock = queueUNLOCKED;\r
237 \r
238                                 /* Likewise ensure the event queues start with the correct state. */\r
239                                 vListInitialise( &( pxNewQueue->xTasksWaitingToSend ) );\r
240                                 vListInitialise( &( pxNewQueue->xTasksWaitingToReceive ) );\r
241 \r
242                                 return  pxNewQueue;\r
243                         }\r
244                         else\r
245                         {\r
246                                 vPortFree( pxNewQueue );\r
247                         }\r
248                 }\r
249         }\r
250 \r
251         /* Will only reach here if we could not allocate enough memory or no memory\r
252         was required. */\r
253         return NULL;\r
254 }\r
255 /*-----------------------------------------------------------*/\r
256 \r
257 #if ( configUSE_MUTEXES == 1 )\r
258 \r
259         xQueueHandle xQueueCreateMutex( void )\r
260         {\r
261         xQUEUE *pxNewQueue;\r
262         \r
263                 /* Allocate the new queue structure. */\r
264                 pxNewQueue = ( xQUEUE * ) pvPortMalloc( sizeof( xQUEUE ) );\r
265                 if( pxNewQueue != NULL )\r
266                 {\r
267                         /* Information required for priority inheritance. */\r
268                         pxNewQueue->pxMutexHolder = NULL;\r
269                         pxNewQueue->uxQueueType = queueQUEUE_IS_MUTEX;\r
270         \r
271                         /* Queues used as a mutex no data is actually copied into or out\r
272                         of the queue. */\r
273                         pxNewQueue->pcWriteTo = NULL;\r
274                         pxNewQueue->pcReadFrom = NULL;\r
275                         \r
276                         /* Each mutex has a length of 1 (like a binary semaphore) and\r
277                         an item size of 0 as nothing is actually copied into or out\r
278                         of the mutex. */\r
279                         pxNewQueue->uxMessagesWaiting = 0;\r
280                         pxNewQueue->uxLength = 1;\r
281                         pxNewQueue->uxItemSize = 0;\r
282                         pxNewQueue->xRxLock = queueUNLOCKED;\r
283                         pxNewQueue->xTxLock = queueUNLOCKED;\r
284         \r
285                         /* Ensure the event queues start with the correct state. */\r
286                         vListInitialise( &( pxNewQueue->xTasksWaitingToSend ) );\r
287                         vListInitialise( &( pxNewQueue->xTasksWaitingToReceive ) );\r
288 \r
289                         /* Start with the semaphore in the expected state. */\r
290                         xQueueGenericSend( pxNewQueue, NULL, 0, queueSEND_TO_BACK );\r
291                 }\r
292         \r
293                 return pxNewQueue;\r
294         }\r
295 \r
296 #endif /* configUSE_MUTEXES */\r
297 /*-----------------------------------------------------------*/\r
298 \r
299 signed portBASE_TYPE xQueueGenericSend( xQueueHandle pxQueue, const void * const pvItemToQueue, portTickType xTicksToWait, portBASE_TYPE xCopyPosition )\r
300 {\r
301 signed portBASE_TYPE xReturn = pdPASS;\r
302 xTimeOutType xTimeOut;\r
303 \r
304         /* Make sure other tasks do not access the queue. */\r
305         vTaskSuspendAll();\r
306 \r
307         /* Capture the current time status for future reference. */\r
308         vTaskSetTimeOutState( &xTimeOut );\r
309 \r
310         /* It is important that this is the only thread/ISR that modifies the\r
311         ready or delayed lists until xTaskResumeAll() is called.  Places where\r
312         the ready/delayed lists are modified include:\r
313 \r
314                 + vTaskDelay() -  Nothing can call vTaskDelay as the scheduler is\r
315                   suspended, vTaskDelay() cannot be called from an ISR.\r
316                 + vTaskPrioritySet() - Has a critical section around the access.\r
317                 + vTaskSwitchContext() - This will not get executed while the scheduler\r
318                   is suspended.\r
319                 + prvCheckDelayedTasks() - This will not get executed while the\r
320                   scheduler is suspended.\r
321                 + xTaskCreate() - Has a critical section around the access.\r
322                 + vTaskResume() - Has a critical section around the access.\r
323                 + xTaskResumeAll() - Has a critical section around the access.\r
324                 + xTaskRemoveFromEventList - Checks to see if the scheduler is\r
325                   suspended.  If so then the TCB being removed from the event is\r
326                   removed from the event and added to the xPendingReadyList.\r
327         */\r
328 \r
329         /* Make sure interrupts do not access the queue event list. */\r
330         prvLockQueue( pxQueue );\r
331 \r
332         /* It is important that interrupts to not access the event list of the\r
333         queue being modified here.  Places where the event list is modified\r
334         include:\r
335 \r
336                 + xQueueGenericSendFromISR().  This checks the lock on the queue to see\r
337                   if it has access.  If the queue is locked then the Tx lock count is\r
338                   incremented to signify that a task waiting for data can be made ready\r
339                   once the queue lock is removed.  If the queue is not locked then\r
340                   a task can be moved from the event list, but will not be removed\r
341                   from the delayed list or placed in the ready list until the scheduler\r
342                   is unlocked.\r
343 \r
344                 + xQueueReceiveFromISR().  As per xQueueGenericSendFromISR().\r
345         */\r
346                 \r
347         /* If the queue is already full we may have to block. */\r
348         do\r
349         {\r
350                 if( prvIsQueueFull( pxQueue ) )\r
351                 {\r
352                         /* The queue is full - do we want to block or just leave without\r
353                         posting? */\r
354                         if( xTicksToWait > ( portTickType ) 0 )\r
355                         {\r
356                                 /* We are going to place ourselves on the xTasksWaitingToSend event\r
357                                 list, and will get woken should the delay expire, or space become\r
358                                 available on the queue.\r
359                                 \r
360                                 As detailed above we do not require mutual exclusion on the event\r
361                                 list as nothing else can modify it or the ready lists while we\r
362                                 have the scheduler suspended and queue locked.\r
363                                 \r
364                                 It is possible that an ISR has removed data from the queue since we\r
365                                 checked if any was available.  If this is the case then the data\r
366                                 will have been copied from the queue, and the queue variables\r
367                                 updated, but the event list will not yet have been checked to see if\r
368                                 anything is waiting as the queue is locked. */\r
369                                 vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToSend ), xTicksToWait );\r
370         \r
371                                 /* Force a context switch now as we are blocked.  We can do\r
372                                 this from within a critical section as the task we are\r
373                                 switching to has its own context.  When we return here (i.e. we\r
374                                 unblock) we will leave the critical section as normal.\r
375                                 \r
376                                 It is possible that an ISR has caused an event on an unrelated and\r
377                                 unlocked queue.  If this was the case then the event list for that\r
378                                 queue will have been updated but the ready lists left unchanged -\r
379                                 instead the readied task will have been added to the pending ready\r
380                                 list. */\r
381                                 taskENTER_CRITICAL();\r
382                                 {\r
383                                         /* We can safely unlock the queue and scheduler here as\r
384                                         interrupts are disabled.  We must not yield with anything\r
385                                         locked, but we can yield from within a critical section.\r
386                                         \r
387                                         Tasks that have been placed on the pending ready list cannot\r
388                                         be tasks that are waiting for events on this queue.  See\r
389                                         in comment xTaskRemoveFromEventList(). */\r
390                                         prvUnlockQueue( pxQueue );\r
391         \r
392                                         /* Resuming the scheduler may cause a yield.  If so then there\r
393                                         is no point yielding again here. */\r
394                                         if( !xTaskResumeAll() )\r
395                                         {\r
396                                                 taskYIELD();\r
397                                         }\r
398 \r
399                                         /* We want to check to see if the queue is still full\r
400                                         before leaving the critical section.  This is to prevent\r
401                                         this task placing an item into the queue due to an\r
402                                         interrupt making space on the queue between critical\r
403                                         sections (when there might be a higher priority task\r
404                                         blocked on the queue that cannot run yet because the\r
405                                         scheduler gets suspended). */\r
406                                         if( pxQueue->uxMessagesWaiting == pxQueue->uxLength )\r
407                                         {\r
408                                                 /* We unblocked but there is no space in the queue,\r
409                                                 we probably timed out. */\r
410                                                 xReturn = errQUEUE_FULL;\r
411                                         }\r
412         \r
413                                         /* Before leaving the critical section we have to ensure\r
414                                         exclusive access again. */\r
415                                         vTaskSuspendAll();\r
416                                         prvLockQueue( pxQueue );                                \r
417                                 }\r
418                                 taskEXIT_CRITICAL();\r
419                         }\r
420                 }\r
421                         \r
422                 /* If xReturn is errQUEUE_FULL then we unblocked when the queue\r
423                 was still full.  Don't check it again now as it is possible that\r
424                 an interrupt has removed an item from the queue since we left the\r
425                 critical section and we don't want to write to the queue in case\r
426                 there is a task of higher priority blocked waiting for space to\r
427                 be available on the queue.  If this is the case the higher priority\r
428                 task will execute when the scheduler is unsupended. */\r
429                 if( xReturn != errQUEUE_FULL )\r
430                 {\r
431                         /* When we are here it is possible that we unblocked as space became\r
432                         available on the queue.  It is also possible that an ISR posted to the\r
433                         queue since we left the critical section, so it may be that again there\r
434                         is no space.  This would only happen if a task and ISR post onto the\r
435                         same queue. */\r
436                         taskENTER_CRITICAL();\r
437                         {\r
438                                 if( pxQueue->uxMessagesWaiting < pxQueue->uxLength )\r
439                                 {\r
440                                         /* There is room in the queue, copy the data into the queue. */                 \r
441                                         prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition );\r
442                                         xReturn = pdPASS;\r
443                 \r
444                                         /* Update the TxLock count so prvUnlockQueue knows to check for\r
445                                         tasks waiting for data to become available in the queue. */\r
446                                         ++( pxQueue->xTxLock );\r
447                                 }\r
448                                 else\r
449                                 {\r
450                                         xReturn = errQUEUE_FULL;\r
451                                 }\r
452                         }\r
453                         taskEXIT_CRITICAL();\r
454                 }\r
455 \r
456                 if( xReturn == errQUEUE_FULL )\r
457                 {\r
458                         if( xTicksToWait > 0 )\r
459                         {\r
460                                 if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE )\r
461                                 {\r
462                                         xReturn = queueERRONEOUS_UNBLOCK;\r
463                                 }\r
464                         }\r
465                 }\r
466         }\r
467         while( xReturn == queueERRONEOUS_UNBLOCK );\r
468 \r
469         prvUnlockQueue( pxQueue );\r
470         xTaskResumeAll();\r
471 \r
472         return xReturn;\r
473 }\r
474 /*-----------------------------------------------------------*/\r
475 \r
476 signed portBASE_TYPE xQueueGenericSendFromISR( xQueueHandle pxQueue, const void * const pvItemToQueue, signed portBASE_TYPE xTaskPreviouslyWoken, portBASE_TYPE xCopyPosition )\r
477 {\r
478         /* Similar to xQueueGenericSend, except we don't block if there is no room\r
479         in the queue.  Also we don't directly wake a task that was blocked on a\r
480         queue read, instead we return a flag to say whether a context switch is\r
481         required or not (i.e. has a task with a higher priority than us been woken\r
482         by this post). */\r
483         if( pxQueue->uxMessagesWaiting < pxQueue->uxLength )\r
484         {\r
485                 prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition );\r
486 \r
487                 /* If the queue is locked we do not alter the event list.  This will\r
488                 be done when the queue is unlocked later. */\r
489                 if( pxQueue->xTxLock == queueUNLOCKED )\r
490                 {\r
491                         /* We only want to wake one task per ISR, so check that a task has\r
492                         not already been woken. */\r
493                         if( !xTaskPreviouslyWoken )             \r
494                         {\r
495                                 if( !listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) )\r
496                                 {\r
497                                         if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )\r
498                                         {\r
499                                                 /* The task waiting has a higher priority so record that a\r
500                                                 context switch is required. */\r
501                                                 return pdTRUE;\r
502                                         }\r
503                                 }\r
504                         }\r
505                 }\r
506                 else\r
507                 {\r
508                         /* Increment the lock count so the task that unlocks the queue\r
509                         knows that data was posted while it was locked. */\r
510                         ++( pxQueue->xTxLock );\r
511                 }\r
512         }\r
513 \r
514         return xTaskPreviouslyWoken;\r
515 }\r
516 /*-----------------------------------------------------------*/\r
517 \r
518 signed portBASE_TYPE xQueueGenericReceive( xQueueHandle pxQueue, const void * const pvBuffer, portTickType xTicksToWait, portBASE_TYPE xJustPeeking )\r
519 {\r
520 signed portBASE_TYPE xReturn = pdTRUE;\r
521 xTimeOutType xTimeOut;\r
522 signed portCHAR *pcOriginalReadPosition;\r
523 \r
524         /* This function is very similar to xQueueGenericSend().  See comments\r
525         within xQueueGenericSend() for a more detailed explanation.\r
526 \r
527         Make sure other tasks do not access the queue. */\r
528         vTaskSuspendAll();\r
529 \r
530         /* Capture the current time status for future reference. */\r
531         vTaskSetTimeOutState( &xTimeOut );\r
532 \r
533         /* Make sure interrupts do not access the queue. */\r
534         prvLockQueue( pxQueue );\r
535 \r
536         do\r
537         {\r
538                 /* If there are no messages in the queue we may have to block. */\r
539                 if( prvIsQueueEmpty( pxQueue ) )\r
540                 {\r
541                         /* There are no messages in the queue, do we want to block or just\r
542                         leave with nothing? */                  \r
543                         if( xTicksToWait > ( portTickType ) 0 )\r
544                         {\r
545                                 #if ( configUSE_MUTEXES == 1 )\r
546                                 {\r
547                                         if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX )\r
548                                         {\r
549                                                 portENTER_CRITICAL();\r
550                                                         vTaskPriorityInherit( ( void * const ) pxQueue->pxMutexHolder );\r
551                                                 portEXIT_CRITICAL();\r
552                                         }\r
553                                 }\r
554                                 #endif\r
555                                 \r
556                                 vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait );\r
557                                 taskENTER_CRITICAL();\r
558                                 {\r
559                                         prvUnlockQueue( pxQueue );\r
560                                         if( !xTaskResumeAll() )\r
561                                         {\r
562                                                 taskYIELD();\r
563                                         }\r
564 \r
565                                         if( pxQueue->uxMessagesWaiting == ( unsigned portBASE_TYPE ) 0 )\r
566                                         {\r
567                                                 /* We unblocked but the queue is empty.  We probably\r
568                                                 timed out. */\r
569                                                 xReturn = errQUEUE_EMPTY;\r
570                                         }\r
571         \r
572                                         vTaskSuspendAll();\r
573                                         prvLockQueue( pxQueue );\r
574                                 }\r
575                                 taskEXIT_CRITICAL();\r
576                         }\r
577                 }\r
578         \r
579                 if( xReturn != errQUEUE_EMPTY )\r
580                 {\r
581                         taskENTER_CRITICAL();\r
582                         {\r
583                                 if( pxQueue->uxMessagesWaiting > ( unsigned portBASE_TYPE ) 0 )\r
584                                 {\r
585                                         /* Remember our read position in case we are just peeking. */\r
586                                         pcOriginalReadPosition = pxQueue->pcReadFrom;\r
587 \r
588                                         prvCopyDataFromQueue( pxQueue, pvBuffer );\r
589 \r
590                                         if( xJustPeeking == pdFALSE )\r
591                                         {\r
592                                                 /* We are actually removing data. */\r
593                                                 --( pxQueue->uxMessagesWaiting );\r
594                                                         \r
595                                                 /* Increment the lock count so prvUnlockQueue knows to check for\r
596                                                 tasks waiting for space to become available on the queue. */\r
597                                                 ++( pxQueue->xRxLock );\r
598                                                 \r
599                                                 #if ( configUSE_MUTEXES == 1 )\r
600                                                 {\r
601                                                         if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX )\r
602                                                         {\r
603                                                                 /* Record the information required to implement\r
604                                                                 priority inheritance should it become necessary. */\r
605                                                                 pxQueue->pxMutexHolder = xTaskGetCurrentTaskHandle();\r
606                                                         }\r
607                                                 }\r
608                                                 #endif\r
609                                         }\r
610                                         else\r
611                                         {\r
612                                                 /* We are not removing the data, so reset our read\r
613                                                 pointer. */\r
614                                                 pxQueue->pcReadFrom = pcOriginalReadPosition;\r
615 \r
616                                                 /* The data is being left in the queue, so increment the\r
617                                                 lock count so prvUnlockQueue knows to check for other\r
618                                                 tasks waiting for the data to be available. */\r
619                                                 ++( pxQueue->xTxLock );                                         \r
620                                         }\r
621                                         \r
622                                         xReturn = pdPASS;                                       \r
623                                 }\r
624                                 else\r
625                                 {\r
626                                         xReturn = errQUEUE_EMPTY;\r
627                                 }\r
628                         }\r
629                         taskEXIT_CRITICAL();\r
630                 }\r
631 \r
632                 if( xReturn == errQUEUE_EMPTY )\r
633                 {\r
634                         if( xTicksToWait > 0 )\r
635                         {\r
636                                 if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE )\r
637                                 {\r
638                                         xReturn = queueERRONEOUS_UNBLOCK;\r
639                                 }\r
640                         }\r
641                 }\r
642         } while( xReturn == queueERRONEOUS_UNBLOCK );\r
643 \r
644         /* We no longer require exclusive access to the queue. */\r
645         prvUnlockQueue( pxQueue );\r
646         xTaskResumeAll();\r
647 \r
648         return xReturn;\r
649 }\r
650 /*-----------------------------------------------------------*/\r
651 \r
652 signed portBASE_TYPE xQueueReceiveFromISR( xQueueHandle pxQueue, const void * const pvBuffer, signed portBASE_TYPE *pxTaskWoken )\r
653 {\r
654 signed portBASE_TYPE xReturn;\r
655 \r
656         /* We cannot block from an ISR, so check there is data available. */\r
657         if( pxQueue->uxMessagesWaiting > ( unsigned portBASE_TYPE ) 0 )\r
658         {\r
659                 prvCopyDataFromQueue( pxQueue, pvBuffer );\r
660                 --( pxQueue->uxMessagesWaiting );\r
661 \r
662                 /* If the queue is locked we will not modify the event list.  Instead\r
663                 we update the lock count so the task that unlocks the queue will know\r
664                 that an ISR has removed data while the queue was locked. */\r
665                 if( pxQueue->xRxLock == queueUNLOCKED )\r
666                 {\r
667                         /* We only want to wake one task per ISR, so check that a task has\r
668                         not already been woken. */\r
669                         if( !( *pxTaskWoken ) )\r
670                         {\r
671                                 if( !listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) )\r
672                                 {\r
673                                         if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE )\r
674                                         {\r
675                                                 /* The task waiting has a higher priority than us so\r
676                                                 force a context switch. */\r
677                                                 *pxTaskWoken = pdTRUE;\r
678                                         }\r
679                                 }\r
680                         }\r
681                 }\r
682                 else\r
683                 {\r
684                         /* Increment the lock count so the task that unlocks the queue\r
685                         knows that data was removed while it was locked. */\r
686                         ++( pxQueue->xRxLock );\r
687                 }\r
688 \r
689                 xReturn = pdPASS;\r
690         }\r
691         else\r
692         {\r
693                 xReturn = pdFAIL;\r
694         }\r
695 \r
696         return xReturn;\r
697 }\r
698 /*-----------------------------------------------------------*/\r
699 \r
700 unsigned portBASE_TYPE uxQueueMessagesWaiting( const xQueueHandle pxQueue )\r
701 {\r
702 unsigned portBASE_TYPE uxReturn;\r
703 \r
704         taskENTER_CRITICAL();\r
705                 uxReturn = pxQueue->uxMessagesWaiting;\r
706         taskEXIT_CRITICAL();\r
707 \r
708         return uxReturn;\r
709 }\r
710 /*-----------------------------------------------------------*/\r
711 \r
712 void vQueueDelete( xQueueHandle pxQueue )\r
713 {\r
714         vPortFree( pxQueue->pcHead );\r
715         vPortFree( pxQueue );\r
716 }\r
717 /*-----------------------------------------------------------*/\r
718 \r
719 static void prvCopyDataToQueue( xQUEUE *pxQueue, const void *pvItemToQueue, portBASE_TYPE xPosition )\r
720 {\r
721         if( pxQueue->uxItemSize == 0 )\r
722         {\r
723                 #if ( configUSE_MUTEXES == 1 )\r
724                 {\r
725                         if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX )\r
726                         {\r
727                                 /* The mutex is no longer being held. */\r
728                                 vTaskPriorityDisinherit( ( void * const ) pxQueue->pxMutexHolder );\r
729                         }\r
730                 }\r
731                 #endif\r
732         }\r
733         else if( xPosition == queueSEND_TO_BACK )\r
734         {\r
735                 memcpy( ( void * ) pxQueue->pcWriteTo, pvItemToQueue, ( unsigned ) pxQueue->uxItemSize );\r
736                 pxQueue->pcWriteTo += pxQueue->uxItemSize;\r
737                 if( pxQueue->pcWriteTo >= pxQueue->pcTail )\r
738                 {\r
739                         pxQueue->pcWriteTo = pxQueue->pcHead;\r
740                 }\r
741         }\r
742         else\r
743         {\r
744                 memcpy( ( void * ) pxQueue->pcReadFrom, pvItemToQueue, ( unsigned ) pxQueue->uxItemSize );\r
745                 pxQueue->pcReadFrom -= pxQueue->uxItemSize;\r
746                 if( pxQueue->pcReadFrom < pxQueue->pcHead )\r
747                 {\r
748                         pxQueue->pcReadFrom = ( pxQueue->pcTail - pxQueue->uxItemSize );\r
749                 }               \r
750         }\r
751 \r
752         ++( pxQueue->uxMessagesWaiting );\r
753 }\r
754 /*-----------------------------------------------------------*/\r
755 \r
756 static void prvCopyDataFromQueue( xQUEUE * const pxQueue, const void *pvBuffer )\r
757 {\r
758         if( pxQueue->uxQueueType != queueQUEUE_IS_MUTEX )\r
759         {\r
760                 pxQueue->pcReadFrom += pxQueue->uxItemSize;\r
761                 if( pxQueue->pcReadFrom >= pxQueue->pcTail )\r
762                 {\r
763                         pxQueue->pcReadFrom = pxQueue->pcHead;\r
764                 }\r
765                 memcpy( ( void * ) pvBuffer, ( void * ) pxQueue->pcReadFrom, ( unsigned ) pxQueue->uxItemSize );\r
766         }       \r
767 }\r
768 /*-----------------------------------------------------------*/\r
769 \r
770 static void prvUnlockQueue( xQueueHandle pxQueue )\r
771 {\r
772         /* THIS FUNCTION MUST BE CALLED WITH THE SCHEDULER SUSPENDED. */\r
773 \r
774         /* The lock counts contains the number of extra data items placed or\r
775         removed from the queue while the queue was locked.  When a queue is\r
776         locked items can be added or removed, but the event lists cannot be\r
777         updated. */\r
778         taskENTER_CRITICAL();\r
779         {\r
780                 --( pxQueue->xTxLock );\r
781 \r
782                 /* See if data was added to the queue while it was locked. */\r
783                 if( pxQueue->xTxLock > queueUNLOCKED )\r
784                 {\r
785                         pxQueue->xTxLock = queueUNLOCKED;\r
786 \r
787                         /* Data was posted while the queue was locked.  Are any tasks\r
788                         blocked waiting for data to become available? */\r
789                         if( !listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) )\r
790                         {\r
791                                 /* Tasks that are removed from the event list will get added to\r
792                                 the pending ready list as the scheduler is still suspended. */\r
793                                 if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )\r
794                                 {\r
795                                         /* The task waiting has a higher priority so record that a\r
796                                         context switch is required. */\r
797                                         vTaskMissedYield();\r
798                                 }\r
799                         }                       \r
800                 }\r
801         }\r
802         taskEXIT_CRITICAL();\r
803 \r
804         /* Do the same for the Rx lock. */\r
805         taskENTER_CRITICAL();\r
806         {\r
807                 --( pxQueue->xRxLock );\r
808 \r
809                 if( pxQueue->xRxLock > queueUNLOCKED )\r
810                 {\r
811                         pxQueue->xRxLock = queueUNLOCKED;\r
812 \r
813                         if( !listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) )\r
814                         {\r
815                                 if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE )\r
816                                 {\r
817                                         vTaskMissedYield();\r
818                                 }\r
819                         }                       \r
820                 }\r
821         }\r
822         taskEXIT_CRITICAL();\r
823 }\r
824 /*-----------------------------------------------------------*/\r
825 \r
826 static signed portBASE_TYPE prvIsQueueEmpty( const xQueueHandle pxQueue )\r
827 {\r
828 signed portBASE_TYPE xReturn;\r
829 \r
830         taskENTER_CRITICAL();\r
831                 xReturn = ( pxQueue->uxMessagesWaiting == ( unsigned portBASE_TYPE ) 0 );\r
832         taskEXIT_CRITICAL();\r
833 \r
834         return xReturn;\r
835 }\r
836 /*-----------------------------------------------------------*/\r
837 \r
838 static signed portBASE_TYPE prvIsQueueFull( const xQueueHandle pxQueue )\r
839 {\r
840 signed portBASE_TYPE xReturn;\r
841 \r
842         taskENTER_CRITICAL();\r
843                 xReturn = ( pxQueue->uxMessagesWaiting == pxQueue->uxLength );\r
844         taskEXIT_CRITICAL();\r
845 \r
846         return xReturn;\r
847 }\r
848 /*-----------------------------------------------------------*/\r
849 \r
850 #if configUSE_CO_ROUTINES == 1\r
851 signed portBASE_TYPE xQueueCRSend( xQueueHandle pxQueue, const void *pvItemToQueue, portTickType xTicksToWait )\r
852 {\r
853 signed portBASE_TYPE xReturn;\r
854                 \r
855         /* If the queue is already full we may have to block.  A critical section\r
856         is required to prevent an interrupt removing something from the queue\r
857         between the check to see if the queue is full and blocking on the queue. */\r
858         portDISABLE_INTERRUPTS();\r
859         {\r
860                 if( prvIsQueueFull( pxQueue ) )\r
861                 {\r
862                         /* The queue is full - do we want to block or just leave without\r
863                         posting? */\r
864                         if( xTicksToWait > ( portTickType ) 0 )\r
865                         {\r
866                                 /* As this is called from a coroutine we cannot block directly, but\r
867                                 return indicating that we need to block. */\r
868                                 vCoRoutineAddToDelayedList( xTicksToWait, &( pxQueue->xTasksWaitingToSend ) );                          \r
869                                 portENABLE_INTERRUPTS();\r
870                                 return errQUEUE_BLOCKED;\r
871                         }\r
872                         else\r
873                         {\r
874                                 portENABLE_INTERRUPTS();\r
875                                 return errQUEUE_FULL;\r
876                         }\r
877                 }\r
878         }\r
879         portENABLE_INTERRUPTS();\r
880                 \r
881         portNOP();\r
882 \r
883         portDISABLE_INTERRUPTS();\r
884         {\r
885                 if( pxQueue->uxMessagesWaiting < pxQueue->uxLength )\r
886                 {\r
887                         /* There is room in the queue, copy the data into the queue. */                 \r
888                         prvCopyDataToQueue( pxQueue, pvItemToQueue, queueSEND_TO_BACK );\r
889                         xReturn = pdPASS;\r
890 \r
891                         /* Were any co-routines waiting for data to become available? */\r
892                         if( !listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) )\r
893                         {\r
894                                 /* In this instance the co-routine could be placed directly\r
895                                 into the ready list as we are within a critical section.\r
896                                 Instead the same pending ready list mechanism is used as if\r
897                                 the event were caused from within an interrupt. */\r
898                                 if( xCoRoutineRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )\r
899                                 {\r
900                                         /* The co-routine waiting has a higher priority so record\r
901                                         that a yield might be appropriate. */\r
902                                         xReturn = errQUEUE_YIELD;\r
903                                 }\r
904                         }\r
905                 }\r
906                 else\r
907                 {\r
908                         xReturn = errQUEUE_FULL;\r
909                 }\r
910         }\r
911         portENABLE_INTERRUPTS();\r
912 \r
913         return xReturn;\r
914 }\r
915 #endif\r
916 /*-----------------------------------------------------------*/\r
917 \r
918 #if configUSE_CO_ROUTINES == 1\r
919 signed portBASE_TYPE xQueueCRReceive( xQueueHandle pxQueue, void *pvBuffer, portTickType xTicksToWait )\r
920 {\r
921 signed portBASE_TYPE xReturn;\r
922 \r
923         /* If the queue is already empty we may have to block.  A critical section\r
924         is required to prevent an interrupt adding something to the queue\r
925         between the check to see if the queue is empty and blocking on the queue. */\r
926         portDISABLE_INTERRUPTS();\r
927         {\r
928                 if( pxQueue->uxMessagesWaiting == ( unsigned portBASE_TYPE ) 0 )\r
929                 {\r
930                         /* There are no messages in the queue, do we want to block or just\r
931                         leave with nothing? */                  \r
932                         if( xTicksToWait > ( portTickType ) 0 )\r
933                         {\r
934                                 /* As this is a co-routine we cannot block directly, but return\r
935                                 indicating that we need to block. */\r
936                                 vCoRoutineAddToDelayedList( xTicksToWait, &( pxQueue->xTasksWaitingToReceive ) );\r
937                                 portENABLE_INTERRUPTS();\r
938                                 return errQUEUE_BLOCKED;\r
939                         }\r
940                         else\r
941                         {\r
942                                 portENABLE_INTERRUPTS();\r
943                                 return errQUEUE_FULL;\r
944                         }\r
945                 }\r
946         }\r
947         portENABLE_INTERRUPTS();\r
948 \r
949         portNOP();\r
950 \r
951         portDISABLE_INTERRUPTS();\r
952         {\r
953                 if( pxQueue->uxMessagesWaiting > ( unsigned portBASE_TYPE ) 0 )\r
954                 {\r
955                         /* Data is available from the queue. */\r
956                         pxQueue->pcReadFrom += pxQueue->uxItemSize;\r
957                         if( pxQueue->pcReadFrom >= pxQueue->pcTail )\r
958                         {\r
959                                 pxQueue->pcReadFrom = pxQueue->pcHead;\r
960                         }\r
961                         --( pxQueue->uxMessagesWaiting );\r
962                         memcpy( ( void * ) pvBuffer, ( void * ) pxQueue->pcReadFrom, ( unsigned ) pxQueue->uxItemSize );\r
963 \r
964                         xReturn = pdPASS;\r
965 \r
966                         /* Were any co-routines waiting for space to become available? */\r
967                         if( !listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) )\r
968                         {\r
969                                 /* In this instance the co-routine could be placed directly\r
970                                 into the ready list as we are within a critical section.\r
971                                 Instead the same pending ready list mechanism is used as if\r
972                                 the event were caused from within an interrupt. */\r
973                                 if( xCoRoutineRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE )\r
974                                 {\r
975                                         xReturn = errQUEUE_YIELD;\r
976                                 }\r
977                         }       \r
978                 }\r
979                 else\r
980                 {\r
981                         xReturn = pdFAIL;\r
982                 }\r
983         }\r
984         portENABLE_INTERRUPTS();\r
985 \r
986         return xReturn;\r
987 }\r
988 #endif\r
989 /*-----------------------------------------------------------*/\r
990 \r
991 \r
992 \r
993 #if configUSE_CO_ROUTINES == 1\r
994 signed portBASE_TYPE xQueueCRSendFromISR( xQueueHandle pxQueue, const void *pvItemToQueue, signed portBASE_TYPE xCoRoutinePreviouslyWoken )\r
995 {\r
996         /* Cannot block within an ISR so if there is no space on the queue then\r
997         exit without doing anything. */\r
998         if( pxQueue->uxMessagesWaiting < pxQueue->uxLength )\r
999         {\r
1000                 prvCopyDataToQueue( pxQueue, pvItemToQueue, queueSEND_TO_BACK );\r
1001 \r
1002                 /* We only want to wake one co-routine per ISR, so check that a\r
1003                 co-routine has not already been woken. */\r
1004                 if( !xCoRoutinePreviouslyWoken )                \r
1005                 {\r
1006                         if( !listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) )\r
1007                         {\r
1008                                 if( xCoRoutineRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )\r
1009                                 {\r
1010                                         return pdTRUE;\r
1011                                 }\r
1012                         }\r
1013                 }\r
1014         }\r
1015 \r
1016         return xCoRoutinePreviouslyWoken;\r
1017 }\r
1018 #endif\r
1019 /*-----------------------------------------------------------*/\r
1020 \r
1021 #if configUSE_CO_ROUTINES == 1\r
1022 signed portBASE_TYPE xQueueCRReceiveFromISR( xQueueHandle pxQueue, void *pvBuffer, signed portBASE_TYPE *pxCoRoutineWoken )\r
1023 {\r
1024 signed portBASE_TYPE xReturn;\r
1025 \r
1026         /* We cannot block from an ISR, so check there is data available. If\r
1027         not then just leave without doing anything. */\r
1028         if( pxQueue->uxMessagesWaiting > ( unsigned portBASE_TYPE ) 0 )\r
1029         {\r
1030                 /* Copy the data from the queue. */\r
1031                 pxQueue->pcReadFrom += pxQueue->uxItemSize;\r
1032                 if( pxQueue->pcReadFrom >= pxQueue->pcTail )\r
1033                 {\r
1034                         pxQueue->pcReadFrom = pxQueue->pcHead;\r
1035                 }\r
1036                 --( pxQueue->uxMessagesWaiting );\r
1037                 memcpy( ( void * ) pvBuffer, ( void * ) pxQueue->pcReadFrom, ( unsigned ) pxQueue->uxItemSize );\r
1038 \r
1039                 if( !( *pxCoRoutineWoken ) )\r
1040                 {\r
1041                         if( !listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) )\r
1042                         {\r
1043                                 if( xCoRoutineRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE )\r
1044                                 {\r
1045                                         *pxCoRoutineWoken = pdTRUE;\r
1046                                 }\r
1047                         }\r
1048                 }\r
1049 \r
1050                 xReturn = pdPASS;\r
1051         }\r
1052         else\r
1053         {\r
1054                 xReturn = pdFAIL;\r
1055         }\r
1056 \r
1057         return xReturn;\r
1058 }\r
1059 #endif\r
1060 /*-----------------------------------------------------------*/\r
1061 \r