summaryrefslogtreecommitdiff
path: root/openpicc/os/core/queue.c
diff options
context:
space:
mode:
Diffstat (limited to 'openpicc/os/core/queue.c')
-rw-r--r--openpicc/os/core/queue.c1002
1 files changed, 1002 insertions, 0 deletions
diff --git a/openpicc/os/core/queue.c b/openpicc/os/core/queue.c
new file mode 100644
index 0000000..c2f08cb
--- /dev/null
+++ b/openpicc/os/core/queue.c
@@ -0,0 +1,1002 @@
+/*
+ FreeRTOS.org V4.2.1 - Copyright (C) 2003-2007 Richard Barry.
+
+ This file is part of the FreeRTOS.org distribution.
+
+ FreeRTOS.org is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ FreeRTOS.org is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with FreeRTOS.org; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ A special exception to the GPL can be applied should you wish to distribute
+ a combined work that includes FreeRTOS.org, without being obliged to provide
+ the source code for any proprietary components. See the licensing section
+ of http://www.FreeRTOS.org for full details of how and when the exception
+ can be applied.
+
+ ***************************************************************************
+ See http://www.FreeRTOS.org for documentation, latest information, license
+ and contact details. Please ensure to read the configuration and relevant
+ port sections of the online documentation.
+
+ Also see http://www.SafeRTOS.com for an IEC 61508 compliant version along
+ with commercial development and support options.
+ ***************************************************************************
+*/
+
+/*
+Changes from V1.01
+
+ + More use of 8bit data types.
+ + Function name prefixes changed where the data type returned has changed.
+
+Changed from V2.0.0
+
+ + Added the queue locking mechanism and make more use of the scheduler
+ suspension feature to minimise the time interrupts have to be disabled
+ when accessing a queue.
+
+Changed from V2.2.0
+
+ + Explicit use of 'signed' qualifier on portCHAR types added.
+
+Changes from V3.0.0
+
+ + API changes as described on the FreeRTOS.org WEB site.
+
+Changes from V3.2.3
+
+ + Added the queue functions that can be used from co-routines.
+
+Changes from V4.0.5
+
+ + Added a loop within xQueueSend() and xQueueReceive() to prevent the
+ functions exiting when a block time remains and the function has
+ not completed.
+
+Changes from V4.1.2:
+
+ + BUG FIX: Removed the call to prvIsQueueEmpty from within xQueueCRReceive
+ as it exited with interrupts enabled. Thanks Paul Katz.
+
+Changes from V4.1.3:
+
+ + Modified xQueueSend() and xQueueReceive() to handle the (very unlikely)
+ case whereby a task unblocking due to a temporal event can remove/send an
+ item from/to a queue when a higher priority task is still blocked on the
+ queue. This modification is a result of the SafeRTOS testing.
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include "FreeRTOS.h"
+#include "task.h"
+#include "croutine.h"
+
+/*-----------------------------------------------------------
+ * PUBLIC LIST API documented in list.h
+ *----------------------------------------------------------*/
+
+/* Constants used with the cRxLock and cTxLock structure members. */
+#define queueUNLOCKED ( ( signed portBASE_TYPE ) -1 )
+#define queueERRONEOUS_UNBLOCK ( -1 )
+
+/*
+ * Definition of the queue used by the scheduler.
+ * Items are queued by copy, not reference.
+ */
+typedef struct QueueDefinition
+{
+ signed portCHAR *pcHead; /*< Points to the beginning of the queue storage area. */
+ 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. */
+
+ signed portCHAR *pcWriteTo; /*< Points to the free next place in the storage area. */
+ signed portCHAR *pcReadFrom; /*< Points to the last place that a queued item was read from. */
+
+ xList xTasksWaitingToSend; /*< List of tasks that are blocked waiting to post onto this queue. Stored in priority order. */
+ xList xTasksWaitingToReceive; /*< List of tasks that are blocked waiting to read from this queue. Stored in priority order. */
+
+ unsigned portBASE_TYPE uxMessagesWaiting; /*< The number of items currently in the queue. */
+ unsigned portBASE_TYPE uxLength; /*< The length of the queue defined as the number of items it will hold, not the number of bytes. */
+ unsigned portBASE_TYPE uxItemSize; /*< The size of each items that the queue will hold. */
+
+ 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. */
+ 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. */
+} xQUEUE;
+/*-----------------------------------------------------------*/
+
+/*
+ * Inside this file xQueueHandle is a pointer to a xQUEUE structure.
+ * To keep the definition private the API header file defines it as a
+ * pointer to void.
+ */
+typedef xQUEUE *xQueueHandle;
+
+/*
+ * Prototypes for public functions are included here so we don't have to
+ * include the API header file (as it defines xQueueHandle differently). These
+ * functions are documented in the API header file.
+ */
+xQueueHandle xQueueCreate (unsigned portBASE_TYPE uxQueueLength,
+ unsigned portBASE_TYPE uxItemSize);
+signed portBASE_TYPE xQueueSend (xQueueHandle xQueue,
+ const void *pvItemToQueue,
+ portTickType xTicksToWait);
+unsigned portBASE_TYPE uxQueueMessagesWaiting (xQueueHandle pxQueue);
+void vQueueDelete (xQueueHandle xQueue);
+signed portBASE_TYPE xQueueSendFromISR (xQueueHandle pxQueue,
+ const void *pvItemToQueue,
+ signed portBASE_TYPE
+ xTaskPreviouslyWoken);
+signed portBASE_TYPE xQueueReceive (xQueueHandle pxQueue, void *pvBuffer,
+ portTickType xTicksToWait);
+signed portBASE_TYPE xQueueReceiveFromISR (xQueueHandle pxQueue,
+ void *pvBuffer,
+ signed portBASE_TYPE *
+ pxTaskWoken);
+
+#if configUSE_CO_ROUTINES == 1
+signed portBASE_TYPE xQueueCRSendFromISR (xQueueHandle pxQueue,
+ const void *pvItemToQueue,
+ signed portBASE_TYPE
+ xCoRoutinePreviouslyWoken);
+signed portBASE_TYPE xQueueCRReceiveFromISR (xQueueHandle pxQueue,
+ void *pvBuffer,
+ signed portBASE_TYPE *
+ pxTaskWoken);
+signed portBASE_TYPE xQueueCRSend (xQueueHandle pxQueue,
+ const void *pvItemToQueue,
+ portTickType xTicksToWait);
+signed portBASE_TYPE xQueueCRReceive (xQueueHandle pxQueue, void *pvBuffer,
+ portTickType xTicksToWait);
+#endif
+
+/*
+ * Unlocks a queue locked by a call to prvLockQueue. Locking a queue does not
+ * prevent an ISR from adding or removing items to the queue, but does prevent
+ * an ISR from removing tasks from the queue event lists. If an ISR finds a
+ * queue is locked it will instead increment the appropriate queue lock count
+ * to indicate that a task may require unblocking. When the queue in unlocked
+ * these lock counts are inspected, and the appropriate action taken.
+ */
+static void prvUnlockQueue (xQueueHandle pxQueue);
+
+/*
+ * Uses a critical section to determine if there is any data in a queue.
+ *
+ * @return pdTRUE if the queue contains no items, otherwise pdFALSE.
+ */
+static signed portBASE_TYPE prvIsQueueEmpty (const xQueueHandle pxQueue);
+
+/*
+ * Uses a critical section to determine if there is any space in a queue.
+ *
+ * @return pdTRUE if there is no space, otherwise pdFALSE;
+ */
+static signed portBASE_TYPE prvIsQueueFull (const xQueueHandle pxQueue);
+
+/*
+ * Macro that copies an item into the queue. This is done by copying the item
+ * byte for byte, not by reference. Updates the queue state to ensure it's
+ * integrity after the copy.
+ */
+#define prvCopyQueueData( pxQueue, pvItemToQueue ) \
+{ \
+ memcpy( ( void * ) pxQueue->pcWriteTo, pvItemToQueue, ( unsigned ) pxQueue->uxItemSize ); \
+ ++( pxQueue->uxMessagesWaiting ); \
+ pxQueue->pcWriteTo += pxQueue->uxItemSize; \
+ if( pxQueue->pcWriteTo >= pxQueue->pcTail ) \
+ { \
+ pxQueue->pcWriteTo = pxQueue->pcHead; \
+ } \
+}
+/*-----------------------------------------------------------*/
+
+/*
+ * Macro to mark a queue as locked. Locking a queue prevents an ISR from
+ * accessing the queue event lists.
+ */
+#define prvLockQueue( pxQueue ) \
+{ \
+ taskENTER_CRITICAL(); \
+ ++( pxQueue->xRxLock ); \
+ ++( pxQueue->xTxLock ); \
+ taskEXIT_CRITICAL(); \
+}
+/*-----------------------------------------------------------*/
+
+
+/*-----------------------------------------------------------
+ * PUBLIC QUEUE MANAGEMENT API documented in queue.h
+ *----------------------------------------------------------*/
+
+xQueueHandle
+xQueueCreate (unsigned portBASE_TYPE uxQueueLength,
+ unsigned portBASE_TYPE uxItemSize)
+{
+ xQUEUE *pxNewQueue;
+ size_t xQueueSizeInBytes;
+
+ /* Allocate the new queue structure. */
+ if (uxQueueLength > (unsigned portBASE_TYPE) 0)
+ {
+ pxNewQueue = (xQUEUE *) pvPortMalloc (sizeof (xQUEUE));
+ if (pxNewQueue != NULL)
+ {
+ /* Create the list of pointers to queue items. The queue is one byte
+ longer than asked for to make wrap checking easier/faster. */
+ xQueueSizeInBytes =
+ (size_t) (uxQueueLength * uxItemSize) + (size_t) 1;
+
+ pxNewQueue->pcHead =
+ (signed portCHAR *) pvPortMalloc (xQueueSizeInBytes);
+ if (pxNewQueue->pcHead != NULL)
+ {
+ /* Initialise the queue members as described above where the
+ queue type is defined. */
+ pxNewQueue->pcTail =
+ pxNewQueue->pcHead + (uxQueueLength * uxItemSize);
+ pxNewQueue->uxMessagesWaiting = 0;
+ pxNewQueue->pcWriteTo = pxNewQueue->pcHead;
+ pxNewQueue->pcReadFrom =
+ pxNewQueue->pcHead + ((uxQueueLength - 1) * uxItemSize);
+ pxNewQueue->uxLength = uxQueueLength;
+ pxNewQueue->uxItemSize = uxItemSize;
+ pxNewQueue->xRxLock = queueUNLOCKED;
+ pxNewQueue->xTxLock = queueUNLOCKED;
+
+ /* Likewise ensure the event queues start with the correct state. */
+ vListInitialise (&(pxNewQueue->xTasksWaitingToSend));
+ vListInitialise (&(pxNewQueue->xTasksWaitingToReceive));
+
+ return pxNewQueue;
+ }
+ else
+ {
+ vPortFree (pxNewQueue);
+ }
+ }
+ }
+
+ /* Will only reach here if we could not allocate enough memory or no memory
+ was required. */
+ return NULL;
+}
+
+/*-----------------------------------------------------------*/
+
+signed portBASE_TYPE
+xQueueSend (xQueueHandle pxQueue, const void *pvItemToQueue,
+ portTickType xTicksToWait)
+{
+ signed portBASE_TYPE xReturn = pdPASS;
+ xTimeOutType xTimeOut;
+
+ /* Make sure other tasks do not access the queue. */
+ vTaskSuspendAll ();
+
+ /* Capture the current time status for future reference. */
+ vTaskSetTimeOutState (&xTimeOut);
+
+ /* It is important that this is the only thread/ISR that modifies the
+ ready or delayed lists until xTaskResumeAll() is called. Places where
+ the ready/delayed lists are modified include:
+
+ + vTaskDelay() - Nothing can call vTaskDelay as the scheduler is
+ suspended, vTaskDelay() cannot be called from an ISR.
+ + vTaskPrioritySet() - Has a critical section around the access.
+ + vTaskSwitchContext() - This will not get executed while the scheduler
+ is suspended.
+ + prvCheckDelayedTasks() - This will not get executed while the
+ scheduler is suspended.
+ + xTaskCreate() - Has a critical section around the access.
+ + vTaskResume() - Has a critical section around the access.
+ + xTaskResumeAll() - Has a critical section around the access.
+ + xTaskRemoveFromEventList - Checks to see if the scheduler is
+ suspended. If so then the TCB being removed from the event is
+ removed from the event and added to the xPendingReadyList.
+ */
+
+ /* Make sure interrupts do not access the queue event list. */
+ prvLockQueue (pxQueue);
+
+ /* It is important that interrupts to not access the event list of the
+ queue being modified here. Places where the event list is modified
+ include:
+
+ + xQueueSendFromISR(). This checks the lock on the queue to see if
+ it has access. If the queue is locked then the Tx lock count is
+ incremented to signify that a task waiting for data can be made ready
+ once the queue lock is removed. If the queue is not locked then
+ a task can be moved from the event list, but will not be removed
+ from the delayed list or placed in the ready list until the scheduler
+ is unlocked.
+
+ + xQueueReceiveFromISR(). As per xQueueSendFromISR().
+ */
+
+ /* If the queue is already full we may have to block. */
+ do
+ {
+ if (prvIsQueueFull (pxQueue))
+ {
+ /* The queue is full - do we want to block or just leave without
+ posting? */
+ if (xTicksToWait > (portTickType) 0)
+ {
+ /* We are going to place ourselves on the xTasksWaitingToSend event
+ list, and will get woken should the delay expire, or space become
+ available on the queue.
+
+ As detailed above we do not require mutual exclusion on the event
+ list as nothing else can modify it or the ready lists while we
+ have the scheduler suspended and queue locked.
+
+ It is possible that an ISR has removed data from the queue since we
+ checked if any was available. If this is the case then the data
+ will have been copied from the queue, and the queue variables
+ updated, but the event list will not yet have been checked to see if
+ anything is waiting as the queue is locked. */
+ vTaskPlaceOnEventList (&(pxQueue->xTasksWaitingToSend),
+ xTicksToWait);
+
+ /* Force a context switch now as we are blocked. We can do
+ this from within a critical section as the task we are
+ switching to has its own context. When we return here (i.e. we
+ unblock) we will leave the critical section as normal.
+
+ It is possible that an ISR has caused an event on an unrelated and
+ unlocked queue. If this was the case then the event list for that
+ queue will have been updated but the ready lists left unchanged -
+ instead the readied task will have been added to the pending ready
+ list. */
+ taskENTER_CRITICAL ();
+ {
+ /* We can safely unlock the queue and scheduler here as
+ interrupts are disabled. We must not yield with anything
+ locked, but we can yield from within a critical section.
+
+ Tasks that have been placed on the pending ready list cannot
+ be tasks that are waiting for events on this queue. See
+ in comment xTaskRemoveFromEventList(). */
+ prvUnlockQueue (pxQueue);
+
+ /* Resuming the scheduler may cause a yield. If so then there
+ is no point yielding again here. */
+ if (!xTaskResumeAll ())
+ {
+ taskYIELD ();
+ }
+
+ /* We want to check to see if the queue is still full
+ before leaving the critical section. This is to prevent
+ this task placing an item into the queue due to an
+ interrupt making space on the queue between critical
+ sections (when there might be a higher priority task
+ blocked on the queue that cannot run yet because the
+ scheduler gets suspended). */
+ if (pxQueue->uxMessagesWaiting == pxQueue->uxLength)
+ {
+ /* We unblocked but there is no space in the queue,
+ we probably timed out. */
+ xReturn = errQUEUE_FULL;
+ }
+
+ /* Before leaving the critical section we have to ensure
+ exclusive access again. */
+ vTaskSuspendAll ();
+ prvLockQueue (pxQueue);
+ }
+ taskEXIT_CRITICAL ();
+ }
+ }
+
+ /* If xReturn is errQUEUE_FULL then we unblocked when the queue
+ was still full. Don't check it again now as it is possible that
+ an interrupt has removed an item from the queue since we left the
+ critical section and we don't want to write to the queue in case
+ there is a task of higher priority blocked waiting for space to
+ be available on the queue. If this is the case the higher priority
+ task will execute when the scheduler is unsupended. */
+ if (xReturn != errQUEUE_FULL)
+ {
+ /* When we are here it is possible that we unblocked as space became
+ available on the queue. It is also possible that an ISR posted to the
+ queue since we left the critical section, so it may be that again there
+ is no space. This would only happen if a task and ISR post onto the
+ same queue. */
+ taskENTER_CRITICAL ();
+ {
+ if (pxQueue->uxMessagesWaiting < pxQueue->uxLength)
+ {
+ /* There is room in the queue, copy the data into the queue. */
+ prvCopyQueueData (pxQueue, pvItemToQueue);
+ xReturn = pdPASS;
+
+ /* Update the TxLock count so prvUnlockQueue knows to check for
+ tasks waiting for data to become available in the queue. */
+ ++(pxQueue->xTxLock);
+ }
+ else
+ {
+ xReturn = errQUEUE_FULL;
+ }
+ }
+ taskEXIT_CRITICAL ();
+ }
+
+ if (xReturn == errQUEUE_FULL)
+ {
+ if (xTicksToWait > 0)
+ {
+ if (xTaskCheckForTimeOut (&xTimeOut, &xTicksToWait) == pdFALSE)
+ {
+ xReturn = queueERRONEOUS_UNBLOCK;
+ }
+ }
+ }
+ }
+ while (xReturn == queueERRONEOUS_UNBLOCK);
+
+ prvUnlockQueue (pxQueue);
+ xTaskResumeAll ();
+
+ return xReturn;
+}
+
+/*-----------------------------------------------------------*/
+
+signed portBASE_TYPE
+xQueueSendFromISR (xQueueHandle pxQueue, const void *pvItemToQueue,
+ signed portBASE_TYPE xTaskPreviouslyWoken)
+{
+ /* Similar to xQueueSend, except we don't block if there is no room in the
+ queue. Also we don't directly wake a task that was blocked on a queue
+ read, instead we return a flag to say whether a context switch is required
+ or not (i.e. has a task with a higher priority than us been woken by this
+ post). */
+ if (pxQueue->uxMessagesWaiting < pxQueue->uxLength)
+ {
+ prvCopyQueueData (pxQueue, pvItemToQueue);
+
+ /* If the queue is locked we do not alter the event list. This will
+ be done when the queue is unlocked later. */
+ if (pxQueue->xTxLock == queueUNLOCKED)
+ {
+ /* We only want to wake one task per ISR, so check that a task has
+ not already been woken. */
+ if (!xTaskPreviouslyWoken)
+ {
+ if (!listLIST_IS_EMPTY (&(pxQueue->xTasksWaitingToReceive)))
+ {
+ if (xTaskRemoveFromEventList
+ (&(pxQueue->xTasksWaitingToReceive)) != pdFALSE)
+ {
+ /* The task waiting has a higher priority so record that a
+ context switch is required. */
+ return pdTRUE;
+ }
+ }
+ }
+ }
+ else
+ {
+ /* Increment the lock count so the task that unlocks the queue
+ knows that data was posted while it was locked. */
+ ++(pxQueue->xTxLock);
+ }
+ }
+
+ return xTaskPreviouslyWoken;
+}
+
+/*-----------------------------------------------------------*/
+
+signed portBASE_TYPE
+xQueueReceive (xQueueHandle pxQueue, void *pvBuffer,
+ portTickType xTicksToWait)
+{
+ signed portBASE_TYPE xReturn = pdTRUE;
+ xTimeOutType xTimeOut;
+
+ /* This function is very similar to xQueueSend(). See comments within
+ xQueueSend() for a more detailed explanation.
+
+ Make sure other tasks do not access the queue. */
+ vTaskSuspendAll ();
+
+ /* Capture the current time status for future reference. */
+ vTaskSetTimeOutState (&xTimeOut);
+
+ /* Make sure interrupts do not access the queue. */
+ prvLockQueue (pxQueue);
+
+ do
+ {
+ /* If there are no messages in the queue we may have to block. */
+ if (prvIsQueueEmpty (pxQueue))
+ {
+ /* There are no messages in the queue, do we want to block or just
+ leave with nothing? */
+ if (xTicksToWait > (portTickType) 0)
+ {
+ vTaskPlaceOnEventList (&(pxQueue->xTasksWaitingToReceive),
+ xTicksToWait);
+ taskENTER_CRITICAL ();
+ {
+ prvUnlockQueue (pxQueue);
+ if (!xTaskResumeAll ())
+ {
+ taskYIELD ();
+ }
+
+ if (pxQueue->uxMessagesWaiting == (unsigned portBASE_TYPE) 0)
+ {
+ /* We unblocked but the queue is empty. We probably
+ timed out. */
+ xReturn = errQUEUE_EMPTY;
+ }
+
+ vTaskSuspendAll ();
+ prvLockQueue (pxQueue);
+ }
+ taskEXIT_CRITICAL ();
+ }
+ }
+
+ if (xReturn != errQUEUE_EMPTY)
+ {
+ taskENTER_CRITICAL ();
+ {
+ if (pxQueue->uxMessagesWaiting > (unsigned portBASE_TYPE) 0)
+ {
+ pxQueue->pcReadFrom += pxQueue->uxItemSize;
+ if (pxQueue->pcReadFrom >= pxQueue->pcTail)
+ {
+ pxQueue->pcReadFrom = pxQueue->pcHead;
+ }
+ --(pxQueue->uxMessagesWaiting);
+ memcpy ((void *) pvBuffer, (void *) pxQueue->pcReadFrom,
+ (unsigned) pxQueue->uxItemSize);
+
+ /* Increment the lock count so prvUnlockQueue knows to check for
+ tasks waiting for space to become available on the queue. */
+ ++(pxQueue->xRxLock);
+ xReturn = pdPASS;
+ }
+ else
+ {
+ xReturn = errQUEUE_EMPTY;
+ }
+ }
+ taskEXIT_CRITICAL ();
+ }
+
+ if (xReturn == errQUEUE_EMPTY)
+ {
+ if (xTicksToWait > 0)
+ {
+ if (xTaskCheckForTimeOut (&xTimeOut, &xTicksToWait) == pdFALSE)
+ {
+ xReturn = queueERRONEOUS_UNBLOCK;
+ }
+ }
+ }
+ }
+ while (xReturn == queueERRONEOUS_UNBLOCK);
+
+ /* We no longer require exclusive access to the queue. */
+ prvUnlockQueue (pxQueue);
+ xTaskResumeAll ();
+
+ return xReturn;
+}
+
+/*-----------------------------------------------------------*/
+
+signed portBASE_TYPE
+xQueueReceiveFromISR (xQueueHandle pxQueue, void *pvBuffer,
+ signed portBASE_TYPE * pxTaskWoken)
+{
+ signed portBASE_TYPE xReturn;
+
+ /* We cannot block from an ISR, so check there is data available. */
+ if (pxQueue->uxMessagesWaiting > (unsigned portBASE_TYPE) 0)
+ {
+ /* Copy the data from the queue. */
+ pxQueue->pcReadFrom += pxQueue->uxItemSize;
+ if (pxQueue->pcReadFrom >= pxQueue->pcTail)
+ {
+ pxQueue->pcReadFrom = pxQueue->pcHead;
+ }
+ --(pxQueue->uxMessagesWaiting);
+ memcpy ((void *) pvBuffer, (void *) pxQueue->pcReadFrom,
+ (unsigned) pxQueue->uxItemSize);
+
+ /* If the queue is locked we will not modify the event list. Instead
+ we update the lock count so the task that unlocks the queue will know
+ that an ISR has removed data while the queue was locked. */
+ if (pxQueue->xRxLock == queueUNLOCKED)
+ {
+ /* We only want to wake one task per ISR, so check that a task has
+ not already been woken. */
+ if (!(*pxTaskWoken))
+ {
+ if (!listLIST_IS_EMPTY (&(pxQueue->xTasksWaitingToSend)))
+ {
+ if (xTaskRemoveFromEventList
+ (&(pxQueue->xTasksWaitingToSend)) != pdFALSE)
+ {
+ /* The task waiting has a higher priority than us so
+ force a context switch. */
+ *pxTaskWoken = pdTRUE;
+ }
+ }
+ }
+ }
+ else
+ {
+ /* Increment the lock count so the task that unlocks the queue
+ knows that data was removed while it was locked. */
+ ++(pxQueue->xRxLock);
+ }
+
+ xReturn = pdPASS;
+ }
+ else
+ {
+ xReturn = pdFAIL;
+ }
+
+ return xReturn;
+}
+
+/*-----------------------------------------------------------*/
+
+unsigned portBASE_TYPE
+uxQueueMessagesWaiting (xQueueHandle pxQueue)
+{
+ unsigned portBASE_TYPE uxReturn;
+
+ taskENTER_CRITICAL ();
+ uxReturn = pxQueue->uxMessagesWaiting;
+ taskEXIT_CRITICAL ();
+
+ return uxReturn;
+}
+
+/*-----------------------------------------------------------*/
+
+void
+vQueueDelete (xQueueHandle pxQueue)
+{
+ vPortFree (pxQueue->pcHead);
+ vPortFree (pxQueue);
+}
+
+/*-----------------------------------------------------------*/
+
+static void
+prvUnlockQueue (xQueueHandle pxQueue)
+{
+ /* THIS FUNCTION MUST BE CALLED WITH THE SCHEDULER SUSPENDED. */
+
+ /* The lock counts contains the number of extra data items placed or
+ removed from the queue while the queue was locked. When a queue is
+ locked items can be added or removed, but the event lists cannot be
+ updated. */
+ taskENTER_CRITICAL ();
+ {
+ --(pxQueue->xTxLock);
+
+ /* See if data was added to the queue while it was locked. */
+ if (pxQueue->xTxLock > queueUNLOCKED)
+ {
+ pxQueue->xTxLock = queueUNLOCKED;
+
+ /* Data was posted while the queue was locked. Are any tasks
+ blocked waiting for data to become available? */
+ if (!listLIST_IS_EMPTY (&(pxQueue->xTasksWaitingToReceive)))
+ {
+ /* Tasks that are removed from the event list will get added to
+ the pending ready list as the scheduler is still suspended. */
+ if (xTaskRemoveFromEventList (&(pxQueue->xTasksWaitingToReceive))
+ != pdFALSE)
+ {
+ /* The task waiting has a higher priority so record that a
+ context switch is required. */
+ vTaskMissedYield ();
+ }
+ }
+ }
+ }
+ taskEXIT_CRITICAL ();
+
+ /* Do the same for the Rx lock. */
+ taskENTER_CRITICAL ();
+ {
+ --(pxQueue->xRxLock);
+
+ if (pxQueue->xRxLock > queueUNLOCKED)
+ {
+ pxQueue->xRxLock = queueUNLOCKED;
+
+ if (!listLIST_IS_EMPTY (&(pxQueue->xTasksWaitingToSend)))
+ {
+ if (xTaskRemoveFromEventList (&(pxQueue->xTasksWaitingToSend)) !=
+ pdFALSE)
+ {
+ vTaskMissedYield ();
+ }
+ }
+ }
+ }
+ taskEXIT_CRITICAL ();
+}
+
+/*-----------------------------------------------------------*/
+
+static signed portBASE_TYPE
+prvIsQueueEmpty (const xQueueHandle pxQueue)
+{
+ signed portBASE_TYPE xReturn;
+
+ taskENTER_CRITICAL ();
+ xReturn = (pxQueue->uxMessagesWaiting == (unsigned portBASE_TYPE) 0);
+ taskEXIT_CRITICAL ();
+
+ return xReturn;
+}
+
+/*-----------------------------------------------------------*/
+
+static signed portBASE_TYPE
+prvIsQueueFull (const xQueueHandle pxQueue)
+{
+ signed portBASE_TYPE xReturn;
+
+ taskENTER_CRITICAL ();
+ xReturn = (pxQueue->uxMessagesWaiting == pxQueue->uxLength);
+ taskEXIT_CRITICAL ();
+
+ return xReturn;
+}
+
+/*-----------------------------------------------------------*/
+
+#if configUSE_CO_ROUTINES == 1
+signed portBASE_TYPE
+xQueueCRSend (xQueueHandle pxQueue, const void *pvItemToQueue,
+ portTickType xTicksToWait)
+{
+ signed portBASE_TYPE xReturn;
+
+ /* If the queue is already full we may have to block. A critical section
+ is required to prevent an interrupt removing something from the queue
+ between the check to see if the queue is full and blocking on the queue. */
+ portDISABLE_INTERRUPTS ();
+ {
+ if (prvIsQueueFull (pxQueue))
+ {
+ /* The queue is full - do we want to block or just leave without
+ posting? */
+ if (xTicksToWait > (portTickType) 0)
+ {
+ /* As this is called from a coroutine we cannot block directly, but
+ return indicating that we need to block. */
+ vCoRoutineAddToDelayedList (xTicksToWait,
+ &(pxQueue->xTasksWaitingToSend));
+ portENABLE_INTERRUPTS ();
+ return errQUEUE_BLOCKED;
+ }
+ else
+ {
+ portENABLE_INTERRUPTS ();
+ return errQUEUE_FULL;
+ }
+ }
+ }
+ portENABLE_INTERRUPTS ();
+
+ portNOP ();
+
+ portDISABLE_INTERRUPTS ();
+ {
+ if (pxQueue->uxMessagesWaiting < pxQueue->uxLength)
+ {
+ /* There is room in the queue, copy the data into the queue. */
+ prvCopyQueueData (pxQueue, pvItemToQueue);
+ xReturn = pdPASS;
+
+ /* Were any co-routines waiting for data to become available? */
+ if (!listLIST_IS_EMPTY (&(pxQueue->xTasksWaitingToReceive)))
+ {
+ /* In this instance the co-routine could be placed directly
+ into the ready list as we are within a critical section.
+ Instead the same pending ready list mechansim is used as if
+ the event were caused from within an interrupt. */
+ if (xCoRoutineRemoveFromEventList
+ (&(pxQueue->xTasksWaitingToReceive)) != pdFALSE)
+ {
+ /* The co-routine waiting has a higher priority so record
+ that a yield might be appropriate. */
+ xReturn = errQUEUE_YIELD;
+ }
+ }
+ }
+ else
+ {
+ xReturn = errQUEUE_FULL;
+ }
+ }
+ portENABLE_INTERRUPTS ();
+
+ return xReturn;
+}
+#endif
+/*-----------------------------------------------------------*/
+
+#if configUSE_CO_ROUTINES == 1
+signed portBASE_TYPE
+xQueueCRReceive (xQueueHandle pxQueue, void *pvBuffer,
+ portTickType xTicksToWait)
+{
+ signed portBASE_TYPE xReturn;
+
+ /* If the queue is already empty we may have to block. A critical section
+ is required to prevent an interrupt adding something to the queue
+ between the check to see if the queue is empty and blocking on the queue. */
+ portDISABLE_INTERRUPTS ();
+ {
+ if (pxQueue->uxMessagesWaiting == (unsigned portBASE_TYPE) 0)
+ {
+ /* There are no messages in the queue, do we want to block or just
+ leave with nothing? */
+ if (xTicksToWait > (portTickType) 0)
+ {
+ /* As this is a co-routine we cannot block directly, but return
+ indicating that we need to block. */
+ vCoRoutineAddToDelayedList (xTicksToWait,
+ &(pxQueue->xTasksWaitingToReceive));
+ portENABLE_INTERRUPTS ();
+ return errQUEUE_BLOCKED;
+ }
+ else
+ {
+ portENABLE_INTERRUPTS ();
+ return errQUEUE_FULL;
+ }
+ }
+ }
+ portENABLE_INTERRUPTS ();
+
+ portNOP ();
+
+ portDISABLE_INTERRUPTS ();
+ {
+ if (pxQueue->uxMessagesWaiting > (unsigned portBASE_TYPE) 0)
+ {
+ /* Data is available from the queue. */
+ pxQueue->pcReadFrom += pxQueue->uxItemSize;
+ if (pxQueue->pcReadFrom >= pxQueue->pcTail)
+ {
+ pxQueue->pcReadFrom = pxQueue->pcHead;
+ }
+ --(pxQueue->uxMessagesWaiting);
+ memcpy ((void *) pvBuffer, (void *) pxQueue->pcReadFrom,
+ (unsigned) pxQueue->uxItemSize);
+
+ xReturn = pdPASS;
+
+ /* Were any co-routines waiting for space to become available? */
+ if (!listLIST_IS_EMPTY (&(pxQueue->xTasksWaitingToSend)))
+ {
+ /* In this instance the co-routine could be placed directly
+ into the ready list as we are within a critical section.
+ Instead the same pending ready list mechansim is used as if
+ the event were caused from within an interrupt. */
+ if (xCoRoutineRemoveFromEventList
+ (&(pxQueue->xTasksWaitingToSend)) != pdFALSE)
+ {
+ xReturn = errQUEUE_YIELD;
+ }
+ }
+ }
+ else
+ {
+ xReturn = pdFAIL;
+ }
+ }
+ portENABLE_INTERRUPTS ();
+
+ return xReturn;
+}
+#endif
+/*-----------------------------------------------------------*/
+
+
+
+#if configUSE_CO_ROUTINES == 1
+signed portBASE_TYPE
+xQueueCRSendFromISR (xQueueHandle pxQueue, const void *pvItemToQueue,
+ signed portBASE_TYPE xCoRoutinePreviouslyWoken)
+{
+ /* Cannot block within an ISR so if there is no space on the queue then
+ exit without doing anything. */
+ if (pxQueue->uxMessagesWaiting < pxQueue->uxLength)
+ {
+ prvCopyQueueData (pxQueue, pvItemToQueue);
+
+ /* We only want to wake one co-routine per ISR, so check that a
+ co-routine has not already been woken. */
+ if (!xCoRoutinePreviouslyWoken)
+ {
+ if (!listLIST_IS_EMPTY (&(pxQueue->xTasksWaitingToReceive)))
+ {
+ if (xCoRoutineRemoveFromEventList
+ (&(pxQueue->xTasksWaitingToReceive)) != pdFALSE)
+ {
+ return pdTRUE;
+ }
+ }
+ }
+ }
+
+ return xCoRoutinePreviouslyWoken;
+}
+#endif
+/*-----------------------------------------------------------*/
+
+#if configUSE_CO_ROUTINES == 1
+signed portBASE_TYPE
+xQueueCRReceiveFromISR (xQueueHandle pxQueue, void *pvBuffer,
+ signed portBASE_TYPE * pxCoRoutineWoken)
+{
+ signed portBASE_TYPE xReturn;
+
+ /* We cannot block from an ISR, so check there is data available. If
+ not then just leave without doing anything. */
+ if (pxQueue->uxMessagesWaiting > (unsigned portBASE_TYPE) 0)
+ {
+ /* Copy the data from the queue. */
+ pxQueue->pcReadFrom += pxQueue->uxItemSize;
+ if (pxQueue->pcReadFrom >= pxQueue->pcTail)
+ {
+ pxQueue->pcReadFrom = pxQueue->pcHead;
+ }
+ --(pxQueue->uxMessagesWaiting);
+ memcpy ((void *) pvBuffer, (void *) pxQueue->pcReadFrom,
+ (unsigned) pxQueue->uxItemSize);
+
+ if (!(*pxCoRoutineWoken))
+ {
+ if (!listLIST_IS_EMPTY (&(pxQueue->xTasksWaitingToSend)))
+ {
+ if (xCoRoutineRemoveFromEventList
+ (&(pxQueue->xTasksWaitingToSend)) != pdFALSE)
+ {
+ *pxCoRoutineWoken = pdTRUE;
+ }
+ }
+ }
+
+ xReturn = pdPASS;
+ }
+ else
+ {
+ xReturn = pdFAIL;
+ }
+
+ return xReturn;
+}
+#endif
+/*-----------------------------------------------------------*/
personal git repositories of Harald Welte. Your mileage may vary