mirror of
				https://github.com/espressif/esp-idf.git
				synced 2025-10-31 04:59:55 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			925 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			925 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * Amazon FreeRTOS+POSIX V1.0.0
 | |
|  * Copyright (C) 2018 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
 | |
|  *
 | |
|  * SPDX-FileCopyrightText: 2018 Amazon.com, Inc. or its affiliates
 | |
|  *
 | |
|  * SPDX-License-Identifier: MIT
 | |
|  *
 | |
|  * SPDX-FileContributor: 2024 Espressif Systems (Shanghai) CO LTD
 | |
|  *
 | |
|  * Permission is hereby granted, free of charge, to any person obtaining a copy of
 | |
|  * this software and associated documentation files (the "Software"), to deal in
 | |
|  * the Software without restriction, including without limitation the rights to
 | |
|  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 | |
|  * the Software, and to permit persons to whom the Software is furnished to do so,
 | |
|  * subject to the following conditions:
 | |
|  *
 | |
|  * The above copyright notice and this permission notice shall be included in all
 | |
|  * copies or substantial portions of the Software.
 | |
|  *
 | |
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | |
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 | |
|  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 | |
|  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 | |
|  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 | |
|  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 | |
|  *
 | |
|  * http://aws.amazon.com/freertos
 | |
|  * http://www.FreeRTOS.org
 | |
|  */
 | |
| 
 | |
| /**
 | |
|  * @file FreeRTOS_POSIX_mqueue.c
 | |
|  * @brief Implementation of message queue functions in mqueue.h
 | |
|  */
 | |
| 
 | |
| /* C standard library includes. */
 | |
| #include <string.h>
 | |
| 
 | |
| #include <errno.h>
 | |
| #include <fcntl.h>
 | |
| #include <mqueue.h>
 | |
| #include <stdarg.h>
 | |
| 
 | |
| /* FreeRTOS+POSIX includes. */
 | |
| #include "FreeRTOS_POSIX.h"
 | |
| #include "FreeRTOS_POSIX/utils.h"
 | |
| 
 | |
| #include "aws_doubly_linked_list.h"
 | |
| #include "esp_private/critical_section.h"
 | |
| 
 | |
| /**
 | |
|  * @brief Element of the FreeRTOS queues that store mq data.
 | |
|  */
 | |
| typedef struct QueueElement
 | |
| {
 | |
|     char * pcData;    /**< Data in queue. Type char* to match msg_ptr. */
 | |
|     size_t xDataSize; /**< Size of data pointed by pcData. */
 | |
| } QueueElement_t;
 | |
| 
 | |
| /**
 | |
|  * @brief Data structure of an mq.
 | |
|  *
 | |
|  * FreeRTOS isn't guaranteed to have a file-like abstraction, so message
 | |
|  * queues in this implementation are stored as a linked list (in RAM).
 | |
|  */
 | |
| typedef struct QueueListElement
 | |
| {
 | |
|     Link_t xLink;              /**< Pointer to the next element in the list. */
 | |
|     QueueHandle_t xQueue;      /**< FreeRTOS queue handle. */
 | |
|     size_t xOpenDescriptors;   /**< Number of threads that have opened this queue. */
 | |
|     char * pcName;             /**< Null-terminated queue name. */
 | |
|     struct mq_attr xAttr;      /**< Queue attributes. */
 | |
|     BaseType_t xPendingUnlink; /**< If pdTRUE, this queue will be unlinked once all descriptors close. */
 | |
| } QueueListElement_t;
 | |
| 
 | |
| /*-----------------------------------------------------------*/
 | |
| 
 | |
| /**
 | |
|  * @brief Convert an absolute timespec into a tick timeout, taking into account
 | |
|  * queue flags.
 | |
|  *
 | |
|  * @param[in] lMessageQueueFlags Message queue flags to consider.
 | |
|  * @param[in] pxAbsoluteTimeout The absolute timespec to convert.
 | |
|  * @param[out] pxTimeoutTicks Output parameter of the timeout in ticks.
 | |
|  *
 | |
|  * @return 0 if successful; EINVAL if pxAbsoluteTimeout is invalid, or ETIMEDOUT
 | |
|  * if pxAbsoluteTimeout is in the past.
 | |
|  */
 | |
| static int prvCalculateTickTimeout( long lMessageQueueFlags,
 | |
|                                     const struct timespec * const pxAbsoluteTimeout,
 | |
|                                     TickType_t * pxTimeoutTicks );
 | |
| 
 | |
| /**
 | |
|  * @brief Add a new queue to the queue list.
 | |
|  *
 | |
|  * @param[out] ppxMessageQueue Pointer to new queue.
 | |
|  * @param[in] pxAttr mq_attr of the new queue.
 | |
|  * @param[in] pcName Name of new queue.
 | |
|  * @param[in] xNameLength Length of pcName.
 | |
|  *
 | |
|  * @return pdTRUE if the queue is found; pdFALSE otherwise.
 | |
|  */
 | |
| static BaseType_t prvCreateNewMessageQueue( QueueListElement_t ** ppxMessageQueue,
 | |
|                                             const struct mq_attr * const pxAttr,
 | |
|                                             const char * const pcName,
 | |
|                                             size_t xNameLength );
 | |
| 
 | |
| /**
 | |
|  * @brief Free all the resources used by a message queue.
 | |
|  *
 | |
|  * @param[out] pxMessageQueue Pointer to queue to free.
 | |
|  *
 | |
|  * @return nothing
 | |
|  */
 | |
| static void prvDeleteMessageQueue( const QueueListElement_t * const pxMessageQueue );
 | |
| 
 | |
| /**
 | |
|  * @brief Attempt to find the queue identified by pcName or xMqId in the queue list.
 | |
|  *
 | |
|  * Matches queues by pcName first; if pcName is NULL, matches by xMqId.
 | |
|  * @param[out] ppxQueueListElement Output parameter set when queue is found.
 | |
|  * @param[in] pcName A queue name to match.
 | |
|  * @param[in] xMessageQueueDescriptor A queue descriptor to match.
 | |
|  *
 | |
|  * @return pdTRUE if the queue is found; pdFALSE otherwise.
 | |
|  */
 | |
| static BaseType_t prvFindQueueInList( QueueListElement_t ** const ppxQueueListElement,
 | |
|                                       const char * const pcName,
 | |
|                                       mqd_t xMessageQueueDescriptor );
 | |
| 
 | |
| /**
 | |
|  * @brief Initialize the queue list.
 | |
|  *
 | |
|  * Performs initialization of the queue list mutex and queue list head.
 | |
|  *
 | |
|  * @return nothing
 | |
|  */
 | |
| static void prvInitializeQueueList( void );
 | |
| 
 | |
| /**
 | |
|  * @brief Checks that pcName is a valid name for a message queue.
 | |
|  *
 | |
|  * Also outputs the length of pcName.
 | |
|  * @param[in] pcName The name to check.
 | |
|  * @param[out] pxNameLength Output parameter for name length.
 | |
|  *
 | |
|  * @return pdTRUE if the name is valid; pdFALSE otherwise.
 | |
|  */
 | |
| static BaseType_t prvValidateQueueName( const char * const pcName,
 | |
|                                         size_t * pxNameLength );
 | |
| 
 | |
| /**
 | |
|  * @brief Guards access to the list of message queues.
 | |
|  */
 | |
| static StaticSemaphore_t xQueueListMutex = { { 0 }, .u = { 0 } };
 | |
| 
 | |
| /**
 | |
|  * @brief Head of the linked list of queues.
 | |
|  */
 | |
| static Link_t xQueueListHead = { 0 };
 | |
| 
 | |
| DEFINE_CRIT_SECTION_LOCK_STATIC( critical_section_lock );
 | |
| 
 | |
| /*-----------------------------------------------------------*/
 | |
| 
 | |
| static int prvCalculateTickTimeout( long lMessageQueueFlags,
 | |
|                                     const struct timespec * const pxAbsoluteTimeout,
 | |
|                                     TickType_t * pxTimeoutTicks )
 | |
| {
 | |
|     int iStatus = 0;
 | |
| 
 | |
|     /* Check for nonblocking queue. */
 | |
|     if( lMessageQueueFlags & O_NONBLOCK )
 | |
|     {
 | |
|         /* No additional checks are done for nonblocking queues. Timeout is 0. */
 | |
|         *pxTimeoutTicks = 0;
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         /* No absolute timeout given. Block forever. */
 | |
|         if( pxAbsoluteTimeout == NULL )
 | |
|         {
 | |
|             *pxTimeoutTicks = portMAX_DELAY;
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             /* Check that the given timespec is valid. */
 | |
|             if( UTILS_ValidateTimespec( pxAbsoluteTimeout ) == false )
 | |
|             {
 | |
|                 iStatus = EINVAL;
 | |
|             }
 | |
| 
 | |
|             /* Convert absolute timespec to ticks. */
 | |
|             if( ( iStatus == 0 ) &&
 | |
|                 ( UTILS_AbsoluteTimespecToTicks( pxAbsoluteTimeout, pxTimeoutTicks ) != 0 ) )
 | |
|             {
 | |
|                 iStatus = ETIMEDOUT;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     return iStatus;
 | |
| }
 | |
| 
 | |
| /*-----------------------------------------------------------*/
 | |
| 
 | |
| static BaseType_t prvCreateNewMessageQueue( QueueListElement_t ** ppxMessageQueue,
 | |
|                                             const struct mq_attr * const pxAttr,
 | |
|                                             const char * const pcName,
 | |
|                                             size_t xNameLength )
 | |
| {
 | |
|     BaseType_t xStatus = pdTRUE;
 | |
| 
 | |
|     /* Allocate space for a new queue element. */
 | |
|     *ppxMessageQueue = pvPortMalloc( sizeof( QueueListElement_t ) );
 | |
| 
 | |
|     /* Check that memory allocation succeeded. */
 | |
|     if( *ppxMessageQueue == NULL )
 | |
|     {
 | |
|         xStatus = pdFALSE;
 | |
|     }
 | |
| 
 | |
|     /* Create the FreeRTOS queue. */
 | |
|     if( xStatus == pdTRUE )
 | |
|     {
 | |
|         ( *ppxMessageQueue )->xQueue =
 | |
|             xQueueCreate( pxAttr->mq_maxmsg, sizeof( QueueElement_t ) );
 | |
| 
 | |
|         /* Check that queue creation succeeded. */
 | |
|         if( ( *ppxMessageQueue )->xQueue == NULL )
 | |
|         {
 | |
|             vPortFree( *ppxMessageQueue );
 | |
|             xStatus = pdFALSE;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if( xStatus == pdTRUE )
 | |
|     {
 | |
|         /* Allocate space for the queue name plus null-terminator. */
 | |
|         ( *ppxMessageQueue )->pcName = pvPortMalloc( xNameLength + 1 );
 | |
| 
 | |
|         /* Check that memory was successfully allocated for queue name. */
 | |
|         if( ( *ppxMessageQueue )->pcName == NULL )
 | |
|         {
 | |
|             vQueueDelete( ( *ppxMessageQueue )->xQueue );
 | |
|             vPortFree( *ppxMessageQueue );
 | |
|             xStatus = pdFALSE;
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             /* Copy queue name. Copying xNameLength+1 will cause strncpy to add
 | |
|              * the null-terminator. */
 | |
|             ( void ) strncpy( ( *ppxMessageQueue )->pcName, pcName, xNameLength + 1 );
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if( xStatus == pdTRUE )
 | |
|     {
 | |
|         /* Copy attributes. */
 | |
|         ( *ppxMessageQueue )->xAttr = *pxAttr;
 | |
| 
 | |
|         /* A newly-created queue will have 1 open descriptor for it. */
 | |
|         ( *ppxMessageQueue )->xOpenDescriptors = 1;
 | |
| 
 | |
|         /* A newly-created queue will not be pending unlink. */
 | |
|         ( *ppxMessageQueue )->xPendingUnlink = pdFALSE;
 | |
| 
 | |
|         /* Add the new queue to the list. */
 | |
|         listADD( &xQueueListHead, &( *ppxMessageQueue )->xLink );
 | |
|     }
 | |
| 
 | |
|     return xStatus;
 | |
| }
 | |
| 
 | |
| /*-----------------------------------------------------------*/
 | |
| 
 | |
| static void prvDeleteMessageQueue( const QueueListElement_t * const pxMessageQueue )
 | |
| {
 | |
|     QueueElement_t xQueueElement = { 0 };
 | |
| 
 | |
|     /* Free all data in the queue. It's assumed that no more data will be added
 | |
|      * to the queue, so xQueueReceive does not block. */
 | |
|     while( xQueueReceive( pxMessageQueue->xQueue,
 | |
|                           ( void * ) &xQueueElement,
 | |
|                           0 ) == pdTRUE )
 | |
|     {
 | |
|         vPortFree( xQueueElement.pcData );
 | |
|     }
 | |
| 
 | |
|     /* Free memory used by this message queue. */
 | |
|     vQueueDelete( pxMessageQueue->xQueue );
 | |
|     vPortFree( ( void * ) pxMessageQueue->pcName );
 | |
|     vPortFree( ( void * ) pxMessageQueue );
 | |
| }
 | |
| 
 | |
| /*-----------------------------------------------------------*/
 | |
| 
 | |
| static BaseType_t prvFindQueueInList( QueueListElement_t ** const ppxQueueListElement,
 | |
|                                       const char * const pcName,
 | |
|                                       mqd_t xMessageQueueDescriptor )
 | |
| {
 | |
|     Link_t * pxQueueListLink = NULL;
 | |
|     QueueListElement_t * pxMessageQueue = NULL;
 | |
|     BaseType_t xQueueFound = pdFALSE;
 | |
| 
 | |
|     /* Iterate through the list of queues. */
 | |
|     listFOR_EACH( pxQueueListLink, &xQueueListHead )
 | |
|     {
 | |
|         pxMessageQueue = listCONTAINER( pxQueueListLink, QueueListElement_t, xLink );
 | |
| 
 | |
|         /* Match by name first if provided. */
 | |
|         if( ( pcName != NULL ) && ( strcmp( pxMessageQueue->pcName, pcName ) == 0 ) )
 | |
|         {
 | |
|             xQueueFound = pdTRUE;
 | |
|             break;
 | |
|         }
 | |
|         /* If name doesn't match, match by descriptor. */
 | |
|         else
 | |
|         {
 | |
|             if( ( mqd_t ) pxMessageQueue == xMessageQueueDescriptor )
 | |
|             {
 | |
|                 xQueueFound = pdTRUE;
 | |
|                 break;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /* If the queue was found, set the output parameter. */
 | |
|     if( ( xQueueFound == pdTRUE ) && ( ppxQueueListElement != NULL ) )
 | |
|     {
 | |
|         *ppxQueueListElement = pxMessageQueue;
 | |
|     }
 | |
| 
 | |
|     return xQueueFound;
 | |
| }
 | |
| 
 | |
| /*-----------------------------------------------------------*/
 | |
| 
 | |
| static void prvInitializeQueueList( void )
 | |
| {
 | |
|     /* Keep track of whether the queue list has been initialized. */
 | |
|     static BaseType_t xQueueListInitialized = pdFALSE;
 | |
| 
 | |
|     /* Check if queue list needs to be initialized. */
 | |
|     if( xQueueListInitialized == pdFALSE )
 | |
|     {
 | |
|         /* Initialization must be in a critical section to prevent two threads
 | |
|          * from initializing at the same time. */
 | |
|         esp_os_enter_critical( &critical_section_lock );
 | |
| 
 | |
|         /* Check again that queue list is still uninitialized, i.e. it wasn't
 | |
|          * initialized while this function was waiting to enter the critical
 | |
|          * section. */
 | |
|         if( xQueueListInitialized == pdFALSE )
 | |
|         {
 | |
|             /* Initialize the queue list mutex and list head. */
 | |
|             ( void ) xSemaphoreCreateMutexStatic( &xQueueListMutex );
 | |
|             listINIT_HEAD( &xQueueListHead );
 | |
|             xQueueListInitialized = pdTRUE;
 | |
|         }
 | |
| 
 | |
|         /* Exit the critical section. */
 | |
|         esp_os_exit_critical( &critical_section_lock );
 | |
|     }
 | |
| }
 | |
| 
 | |
| /*-----------------------------------------------------------*/
 | |
| 
 | |
| static BaseType_t prvValidateQueueName( const char * const pcName,
 | |
|                                         size_t * pxNameLength )
 | |
| {
 | |
|     BaseType_t xStatus = pdTRUE;
 | |
|     size_t xNameLength = 0;
 | |
| 
 | |
|     /* All message queue names must start with '/'. */
 | |
|     if( pcName[ 0 ] != '/' )
 | |
|     {
 | |
|         xStatus = pdFALSE;
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         /* Get the length of pcName, excluding the first '/' and null-terminator. */
 | |
|         xNameLength = UTILS_strnlen( pcName, NAME_MAX + 2 );
 | |
| 
 | |
|         if( xNameLength == NAME_MAX + 2 )
 | |
|         {
 | |
|             /* Name too long. */
 | |
|             xStatus = pdFALSE;
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             /* Name length passes, set output parameter. */
 | |
|             *pxNameLength = xNameLength;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     return xStatus;
 | |
| }
 | |
| 
 | |
| /*-----------------------------------------------------------*/
 | |
| 
 | |
| int mq_close( mqd_t mqdes )
 | |
| {
 | |
|     int iStatus = 0;
 | |
|     QueueListElement_t * pxMessageQueue = ( QueueListElement_t * ) mqdes;
 | |
|     BaseType_t xQueueRemoved = pdFALSE;
 | |
| 
 | |
|     /* Initialize the queue list, if needed. */
 | |
|     prvInitializeQueueList();
 | |
| 
 | |
|     /* Lock the mutex that guards access to the queue list. This call will
 | |
|      * never fail because it blocks forever. */
 | |
|     ( void ) xSemaphoreTake( ( SemaphoreHandle_t ) &xQueueListMutex, portMAX_DELAY );
 | |
| 
 | |
|     /* Attempt to find the message queue based on the given descriptor. */
 | |
|     if( prvFindQueueInList( NULL, NULL, mqdes ) == pdTRUE )
 | |
|     {
 | |
|         /* Decrement the number of open descriptors. */
 | |
|         if( pxMessageQueue->xOpenDescriptors > 0 )
 | |
|         {
 | |
|             pxMessageQueue->xOpenDescriptors--;
 | |
|         }
 | |
| 
 | |
|         /* Check if the queue has any more open descriptors. */
 | |
|         if( pxMessageQueue->xOpenDescriptors == 0 )
 | |
|         {
 | |
|             /* If no open descriptors remain and mq_unlink has already been called,
 | |
|              * remove the queue. */
 | |
|             if( pxMessageQueue->xPendingUnlink == pdTRUE )
 | |
|             {
 | |
|                 listREMOVE( &pxMessageQueue->xLink );
 | |
| 
 | |
|                 /* Set the flag to delete the queue. Deleting the queue is deferred
 | |
|                  * until xQueueListMutex is released. */
 | |
|                 xQueueRemoved = pdTRUE;
 | |
|             }
 | |
|             /* Otherwise, wait for the call to mq_unlink. */
 | |
|             else
 | |
|             {
 | |
|                 pxMessageQueue->xPendingUnlink = pdTRUE;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         /* Queue not found; bad descriptor. */
 | |
|         errno = EBADF;
 | |
|         iStatus = -1;
 | |
|     }
 | |
| 
 | |
|     /* Release the mutex protecting the queue list. */
 | |
|     ( void ) xSemaphoreGive( ( SemaphoreHandle_t ) &xQueueListMutex );
 | |
| 
 | |
|     /* Delete all resources used by the queue if needed. */
 | |
|     if( xQueueRemoved == pdTRUE )
 | |
|     {
 | |
|         prvDeleteMessageQueue( pxMessageQueue );
 | |
|     }
 | |
| 
 | |
|     return iStatus;
 | |
| }
 | |
| 
 | |
| /*-----------------------------------------------------------*/
 | |
| 
 | |
| int mq_getattr( mqd_t mqdes,
 | |
|                 struct mq_attr * mqstat )
 | |
| {
 | |
|     int iStatus = 0;
 | |
|     QueueListElement_t * pxMessageQueue = ( QueueListElement_t * ) mqdes;
 | |
| 
 | |
|     /* Lock the mutex that guards access to the queue list. This call will
 | |
|      * never fail because it blocks forever. */
 | |
|     ( void ) xSemaphoreTake( ( SemaphoreHandle_t ) &xQueueListMutex, portMAX_DELAY );
 | |
| 
 | |
|     /* Find the mq referenced by mqdes. */
 | |
|     if( prvFindQueueInList( NULL, NULL, mqdes ) == pdTRUE )
 | |
|     {
 | |
|         /* Update the number of messages in the queue and copy the attributes
 | |
|          * into mqstat. */
 | |
|         pxMessageQueue->xAttr.mq_curmsgs = ( long ) uxQueueMessagesWaiting( pxMessageQueue->xQueue );
 | |
|         *mqstat = pxMessageQueue->xAttr;
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         /* Queue not found; bad descriptor. */
 | |
|         errno = EBADF;
 | |
|         iStatus = -1;
 | |
|     }
 | |
| 
 | |
|     /* Release the mutex protecting the queue list. */
 | |
|     ( void ) xSemaphoreGive( ( SemaphoreHandle_t ) &xQueueListMutex );
 | |
| 
 | |
|     return iStatus;
 | |
| }
 | |
| 
 | |
| /*-----------------------------------------------------------*/
 | |
| 
 | |
| /* Changed function signature and argument processing to match the POSIX standard */
 | |
| mqd_t mq_open( const char * name,
 | |
|                int oflag,
 | |
|                ... )
 | |
| {
 | |
|     mode_t mode = 0;
 | |
|     struct mq_attr * attr = NULL;
 | |
| 
 | |
|     if( oflag & O_CREAT )
 | |
|     {
 | |
|         va_list args;
 | |
|         va_start( args, oflag );
 | |
|         mode = va_arg( args, mode_t );
 | |
|         attr = va_arg( args, struct mq_attr * );
 | |
|         va_end( args );
 | |
|     }
 | |
| 
 | |
|     mqd_t xMessageQueue = NULL;
 | |
|     size_t xNameLength = 0;
 | |
| 
 | |
|     /* Default mq_attr. */
 | |
|     struct mq_attr xQueueCreationAttr =
 | |
|     {
 | |
|         .mq_flags   = 0,
 | |
|         .mq_maxmsg  = posixconfigMQ_MAX_MESSAGES,
 | |
|         .mq_msgsize = posixconfigMQ_MAX_SIZE,
 | |
|         .mq_curmsgs = 0
 | |
|     };
 | |
| 
 | |
|     /* Silence warnings about unused parameters. */
 | |
|     ( void ) mode;
 | |
| 
 | |
|     /* Initialize the queue list, if needed. */
 | |
|     prvInitializeQueueList();
 | |
| 
 | |
|     /* Check queue name. */
 | |
|     if( prvValidateQueueName( name, &xNameLength ) == pdFALSE )
 | |
|     {
 | |
|         /* Invalid name. */
 | |
|         errno = EINVAL;
 | |
|         xMessageQueue = ( mqd_t ) -1;
 | |
|     }
 | |
| 
 | |
|     /* Check attributes, if O_CREATE is specified and attr is given. */
 | |
|     if( xMessageQueue == NULL )
 | |
|     {
 | |
|         if( ( oflag & O_CREAT ) && ( attr != NULL ) && ( ( attr->mq_maxmsg <= 0 ) || ( attr->mq_msgsize <= 0 ) ) )
 | |
|         {
 | |
|             /* Invalid mq_attr.mq_maxmsg or mq_attr.mq_msgsize. */
 | |
|             errno = EINVAL;
 | |
|             xMessageQueue = ( mqd_t ) -1;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if( xMessageQueue == NULL )
 | |
|     {
 | |
|         /* Lock the mutex that guards access to the queue list. This call will
 | |
|          * never fail because it blocks forever. */
 | |
|         ( void ) xSemaphoreTake( ( SemaphoreHandle_t ) &xQueueListMutex, portMAX_DELAY );
 | |
| 
 | |
|         /* Search the queue list to check if the queue exists. */
 | |
|         if( prvFindQueueInList( ( QueueListElement_t ** ) &xMessageQueue,
 | |
|                                 name,
 | |
|                                 ( mqd_t ) NULL ) == pdTRUE )
 | |
|         {
 | |
|             /* If the mq exists, check that this function wasn't called with
 | |
|              * O_CREAT and O_EXCL. */
 | |
|             if( ( oflag & O_EXCL ) && ( oflag & O_CREAT ) )
 | |
|             {
 | |
|                 errno = EEXIST;
 | |
|                 xMessageQueue = ( mqd_t ) -1;
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 /* Check if the mq has been unlinked and is pending removal. */
 | |
|                 if( ( ( QueueListElement_t * ) xMessageQueue )->xPendingUnlink == pdTRUE )
 | |
|                 {
 | |
|                     /* Queue pending deletion. Don't allow it to be re-opened. */
 | |
|                     errno = EINVAL;
 | |
|                     xMessageQueue = ( mqd_t ) -1;
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     /* Increase count of open file descriptors for queue. */
 | |
|                     ( ( QueueListElement_t * ) xMessageQueue )->xOpenDescriptors++;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         /* Queue does not exist. */
 | |
|         else
 | |
|         {
 | |
|             /* Only create the new queue if O_CREAT was specified. */
 | |
|             if( oflag & O_CREAT )
 | |
|             {
 | |
|                 /* Copy attributes if provided. */
 | |
|                 if( attr != NULL )
 | |
|                 {
 | |
|                     xQueueCreationAttr = *attr;
 | |
|                 }
 | |
| 
 | |
|                 /* Copy oflags. */
 | |
|                 xQueueCreationAttr.mq_flags = ( long ) oflag;
 | |
| 
 | |
|                 /* Create the new message queue. */
 | |
|                 if( prvCreateNewMessageQueue( ( QueueListElement_t ** ) &xMessageQueue,
 | |
|                                               &xQueueCreationAttr,
 | |
|                                               name,
 | |
|                                               xNameLength ) == pdFALSE )
 | |
|                 {
 | |
|                     errno = ENOSPC;
 | |
|                     xMessageQueue = ( mqd_t ) -1;
 | |
|                 }
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 errno = ENOENT;
 | |
|                 xMessageQueue = ( mqd_t ) -1;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         /* Release the mutex protecting the queue list. */
 | |
|         ( void ) xSemaphoreGive( ( SemaphoreHandle_t ) &xQueueListMutex );
 | |
|     }
 | |
| 
 | |
|     return xMessageQueue;
 | |
| }
 | |
| 
 | |
| /*-----------------------------------------------------------*/
 | |
| 
 | |
| ssize_t mq_receive( mqd_t mqdes,
 | |
|                     char * msg_ptr,
 | |
|                     size_t msg_len,
 | |
|                     unsigned int * msg_prio )
 | |
| {
 | |
|     return mq_timedreceive( mqdes, msg_ptr, msg_len, msg_prio, NULL );
 | |
| }
 | |
| 
 | |
| /*-----------------------------------------------------------*/
 | |
| 
 | |
| int mq_send( mqd_t mqdes,
 | |
|              const char * msg_ptr,
 | |
|              size_t msg_len,
 | |
|              unsigned msg_prio )
 | |
| {
 | |
|     return mq_timedsend( mqdes, msg_ptr, msg_len, msg_prio, NULL );
 | |
| }
 | |
| 
 | |
| /*-----------------------------------------------------------*/
 | |
| 
 | |
| ssize_t mq_timedreceive( mqd_t mqdes,
 | |
|                          char * msg_ptr,
 | |
|                          size_t msg_len,
 | |
|                          unsigned * msg_prio,
 | |
|                          const struct timespec * abstime )
 | |
| {
 | |
|     ssize_t xStatus = 0;
 | |
|     int iCalculateTimeoutReturn = 0;
 | |
|     TickType_t xTimeoutTicks = 0;
 | |
|     QueueListElement_t * pxMessageQueue = ( QueueListElement_t * ) mqdes;
 | |
|     QueueElement_t xReceiveData = { 0 };
 | |
| 
 | |
|     /* Silence warnings about unused parameters. */
 | |
|     ( void ) msg_prio;
 | |
| 
 | |
|     /* Lock the mutex that guards access to the queue list. This call will
 | |
|      * never fail because it blocks forever. */
 | |
|     ( void ) xSemaphoreTake( ( SemaphoreHandle_t ) &xQueueListMutex, portMAX_DELAY );
 | |
| 
 | |
|     /* Find the mq referenced by mqdes. */
 | |
|     if( prvFindQueueInList( NULL, NULL, mqdes ) == pdFALSE )
 | |
|     {
 | |
|         /* Queue not found; bad descriptor. */
 | |
|         errno = EBADF;
 | |
|         xStatus = -1;
 | |
|     }
 | |
| 
 | |
|     /* Verify that msg_len is large enough. */
 | |
|     if( xStatus == 0 )
 | |
|     {
 | |
|         if( msg_len < ( size_t ) pxMessageQueue->xAttr.mq_msgsize )
 | |
|         {
 | |
|             /* msg_len too small. */
 | |
|             errno = EMSGSIZE;
 | |
|             xStatus = -1;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if( xStatus == 0 )
 | |
|     {
 | |
|         /* Convert abstime to a tick timeout. */
 | |
|         iCalculateTimeoutReturn = prvCalculateTickTimeout( pxMessageQueue->xAttr.mq_flags,
 | |
|                                                            abstime,
 | |
|                                                            &xTimeoutTicks );
 | |
| 
 | |
|         if( iCalculateTimeoutReturn != 0 )
 | |
|         {
 | |
|             errno = iCalculateTimeoutReturn;
 | |
|             xStatus = -1;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /* Release the mutex protecting the queue list. */
 | |
|     ( void ) xSemaphoreGive( ( SemaphoreHandle_t ) &xQueueListMutex );
 | |
| 
 | |
|     if( xStatus == 0 )
 | |
|     {
 | |
|         /* Receive data from the FreeRTOS queue. */
 | |
|         if( xQueueReceive( pxMessageQueue->xQueue,
 | |
|                            &xReceiveData,
 | |
|                            xTimeoutTicks ) == pdFALSE )
 | |
|         {
 | |
|             /* If queue receive fails, set the appropriate errno. */
 | |
|             if( pxMessageQueue->xAttr.mq_flags & O_NONBLOCK )
 | |
|             {
 | |
|                 /* Set errno to EAGAIN for nonblocking mq. */
 | |
|                 errno = EAGAIN;
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 /* Otherwise, set errno to ETIMEDOUT. */
 | |
|                 errno = ETIMEDOUT;
 | |
|             }
 | |
| 
 | |
|             xStatus = -1;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if( xStatus == 0 )
 | |
|     {
 | |
|         /* Get the length of data for return value. */
 | |
|         xStatus = ( ssize_t ) xReceiveData.xDataSize;
 | |
| 
 | |
|         /* Copy received data into given buffer, then free it. */
 | |
|         ( void ) memcpy( msg_ptr, xReceiveData.pcData, xReceiveData.xDataSize );
 | |
|         vPortFree( xReceiveData.pcData );
 | |
|     }
 | |
| 
 | |
|     return xStatus;
 | |
| }
 | |
| 
 | |
| /*-----------------------------------------------------------*/
 | |
| 
 | |
| int mq_timedsend( mqd_t mqdes,
 | |
|                   const char * msg_ptr,
 | |
|                   size_t msg_len,
 | |
|                   unsigned int msg_prio,
 | |
|                   const struct timespec * abstime )
 | |
| {
 | |
|     int iStatus = 0, iCalculateTimeoutReturn = 0;
 | |
|     TickType_t xTimeoutTicks = 0;
 | |
|     QueueListElement_t * pxMessageQueue = ( QueueListElement_t * ) mqdes;
 | |
|     QueueElement_t xSendData = { 0 };
 | |
| 
 | |
|     /* Silence warnings about unused parameters. */
 | |
|     ( void ) msg_prio;
 | |
| 
 | |
|     /* Lock the mutex that guards access to the queue list. This call will
 | |
|      * never fail because it blocks forever. */
 | |
|     ( void ) xSemaphoreTake( ( SemaphoreHandle_t ) &xQueueListMutex, portMAX_DELAY );
 | |
| 
 | |
|     /* Find the mq referenced by mqdes. */
 | |
|     if( prvFindQueueInList( NULL, NULL, mqdes ) == pdFALSE )
 | |
|     {
 | |
|         /* Queue not found; bad descriptor. */
 | |
|         errno = EBADF;
 | |
|         iStatus = -1;
 | |
|     }
 | |
| 
 | |
|     /* Verify that mq_msgsize is large enough. */
 | |
|     if( iStatus == 0 )
 | |
|     {
 | |
|         if( msg_len > ( size_t ) pxMessageQueue->xAttr.mq_msgsize )
 | |
|         {
 | |
|             /* msg_len too large. */
 | |
|             errno = EMSGSIZE;
 | |
|             iStatus = -1;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if( iStatus == 0 )
 | |
|     {
 | |
|         /* Convert abstime to a tick timeout. */
 | |
|         iCalculateTimeoutReturn = prvCalculateTickTimeout( pxMessageQueue->xAttr.mq_flags,
 | |
|                                                            abstime,
 | |
|                                                            &xTimeoutTicks );
 | |
| 
 | |
|         if( iCalculateTimeoutReturn != 0 )
 | |
|         {
 | |
|             errno = iCalculateTimeoutReturn;
 | |
|             iStatus = -1;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /* Release the mutex protecting the queue list. */
 | |
|     ( void ) xSemaphoreGive( ( SemaphoreHandle_t ) &xQueueListMutex );
 | |
| 
 | |
|     /* Allocate memory for the message. */
 | |
|     if( iStatus == 0 )
 | |
|     {
 | |
|         xSendData.xDataSize = msg_len;
 | |
|         xSendData.pcData = pvPortMalloc( msg_len );
 | |
| 
 | |
|         /* Check that memory allocation succeeded. */
 | |
|         if( xSendData.pcData == NULL )
 | |
|         {
 | |
|             /* msg_len too large. */
 | |
|             errno = EMSGSIZE;
 | |
|             iStatus = -1;
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             /* Copy the data to send. */
 | |
|             ( void ) memcpy( xSendData.pcData, msg_ptr, msg_len );
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if( iStatus == 0 )
 | |
|     {
 | |
|         /* Send data to the FreeRTOS queue. */
 | |
|         if( xQueueSend( pxMessageQueue->xQueue,
 | |
|                         &xSendData,
 | |
|                         xTimeoutTicks ) == pdFALSE )
 | |
|         {
 | |
|             /* If queue send fails, set the appropriate errno. */
 | |
|             if( pxMessageQueue->xAttr.mq_flags & O_NONBLOCK )
 | |
|             {
 | |
|                 /* Set errno to EAGAIN for nonblocking mq. */
 | |
|                 errno = EAGAIN;
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 /* Otherwise, set errno to ETIMEDOUT. */
 | |
|                 errno = ETIMEDOUT;
 | |
|             }
 | |
| 
 | |
|             /* Free the allocated queue data. */
 | |
|             vPortFree( xSendData.pcData );
 | |
| 
 | |
|             iStatus = -1;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     return iStatus;
 | |
| }
 | |
| 
 | |
| /*-----------------------------------------------------------*/
 | |
| 
 | |
| int mq_unlink( const char * name )
 | |
| {
 | |
|     int iStatus = 0;
 | |
|     size_t xNameSize = 0;
 | |
|     BaseType_t xQueueRemoved = pdFALSE;
 | |
|     QueueListElement_t * pxMessageQueue = NULL;
 | |
| 
 | |
|     /* Initialize the queue list, if needed. */
 | |
|     prvInitializeQueueList();
 | |
| 
 | |
|     /* Check queue name. */
 | |
|     if( prvValidateQueueName( name, &xNameSize ) == pdFALSE )
 | |
|     {
 | |
|         /* Error with mq name. */
 | |
|         errno = EINVAL;
 | |
|         iStatus = -1;
 | |
|     }
 | |
| 
 | |
|     if( iStatus == 0 )
 | |
|     {
 | |
|         /* Lock the mutex that guards access to the queue list. This call will
 | |
|          * never fail because it blocks forever. */
 | |
|         ( void ) xSemaphoreTake( ( SemaphoreHandle_t ) &xQueueListMutex, portMAX_DELAY );
 | |
| 
 | |
|         /* Check if the named queue exists. */
 | |
|         if( prvFindQueueInList( &pxMessageQueue, name, ( mqd_t ) NULL ) == pdTRUE )
 | |
|         {
 | |
|             /* If the queue exists and there are no open descriptors to it,
 | |
|              * remove it from the list. */
 | |
|             if( pxMessageQueue->xOpenDescriptors == 0 )
 | |
|             {
 | |
|                 listREMOVE( &pxMessageQueue->xLink );
 | |
| 
 | |
|                 /* Set the flag to delete the queue. Deleting the queue is deferred
 | |
|                  * until xQueueListMutex is released. */
 | |
|                 xQueueRemoved = pdTRUE;
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 /* If the queue has open descriptors, set the pending unlink flag
 | |
|                  * so that mq_close will free its resources. */
 | |
|                 pxMessageQueue->xPendingUnlink = pdTRUE;
 | |
|             }
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             /* The named message queue doesn't exist. */
 | |
|             errno = ENOENT;
 | |
|             iStatus = -1;
 | |
|         }
 | |
| 
 | |
|         /* Release the mutex protecting the queue list. */
 | |
|         ( void ) xSemaphoreGive( ( SemaphoreHandle_t ) &xQueueListMutex );
 | |
|     }
 | |
| 
 | |
|     /* Delete all resources used by the queue if needed. */
 | |
|     if( xQueueRemoved == pdTRUE )
 | |
|     {
 | |
|         prvDeleteMessageQueue( pxMessageQueue );
 | |
|     }
 | |
| 
 | |
|     return iStatus;
 | |
| }
 | |
| 
 | |
| /* specified but not implemented functions, return ENOSYS */
 | |
| int mq_notify( mqd_t,
 | |
|                const struct sigevent * )
 | |
| {
 | |
|     return ENOSYS;
 | |
| }
 | |
| 
 | |
| int mq_setattr( mqd_t,
 | |
|                 const struct mq_attr * restrict,
 | |
|                 struct mq_attr * restrict )
 | |
| {
 | |
|     return ENOSYS;
 | |
| }
 | |
| 
 | |
| /*-----------------------------------------------------------*/
 | 
