feat(rt/posix): Added FreeRTOS-Plus-POSIX message queues implementation

Note: The current mq_open() implementation is changed to match
      the POSIX standard.
This commit is contained in:
Jakob Hasse 2024-02-01 09:19:46 +08:00
parent 5448703663
commit bddc1e95ef
30 changed files with 2760 additions and 4 deletions

View File

@ -140,6 +140,7 @@
/components/protocomm/ @esp-idf-codeowners/app-utilities/provisioning /components/protocomm/ @esp-idf-codeowners/app-utilities/provisioning
/components/pthread/ @esp-idf-codeowners/system /components/pthread/ @esp-idf-codeowners/system
/components/riscv/ @esp-idf-codeowners/system /components/riscv/ @esp-idf-codeowners/system
/components/rt/ @esp-idf-codeowners/system
/components/sdmmc/ @esp-idf-codeowners/storage /components/sdmmc/ @esp-idf-codeowners/storage
/components/soc/ @esp-idf-codeowners/peripherals @esp-idf-codeowners/system /components/soc/ @esp-idf-codeowners/peripherals @esp-idf-codeowners/system
/components/spi_flash/ @esp-idf-codeowners/peripherals /components/spi_flash/ @esp-idf-codeowners/peripherals

View File

@ -0,0 +1,13 @@
idf_build_get_property(target IDF_TARGET)
if(${target} STREQUAL "linux")
idf_component_register()
if(NOT CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin")
find_library(LIBRT rt)
target_link_libraries(${COMPONENT_LIB} INTERFACE ${LIBRT})
endif()
return()
endif()
idf_component_register(SRCS "FreeRTOS_POSIX_mqueue.c" "FreeRTOS_POSIX_utils.c"
PRIV_INCLUDE_DIRS "private_include"
INCLUDE_DIRS "include")

View File

@ -0,0 +1,924 @@
/*
* 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;
}
/*-----------------------------------------------------------*/

View File

@ -0,0 +1,249 @@
/*
* 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_utils.c
* @brief Implementation of utility functions in utils.h
*/
/* C standard library includes. */
#include <stddef.h>
#include <string.h> /* Added by Espressif */
#include <errno.h>
#include <time.h>
/* FreeRTOS+POSIX includes. */
#include "FreeRTOS_POSIX.h"
#include "FreeRTOS_POSIX/utils.h"
/*-----------------------------------------------------------*/
/* This function has been modified from the original by Espressif */
size_t UTILS_strnlen( const char * const pcString,
size_t xMaxLength )
{
return strnlen( pcString, xMaxLength );
}
/*-----------------------------------------------------------*/
int UTILS_AbsoluteTimespecToTicks( const struct timespec * const pxAbsoluteTime,
TickType_t * const pxResult )
{
int iStatus = 0;
struct timespec xCurrentTime = { 0 }, xDifference = { 0 };
/* Check parameters. */
if( ( pxAbsoluteTime == NULL ) || ( pxResult == NULL ) )
{
iStatus = EINVAL;
}
/* Get the current time. */
if( iStatus == 0 )
{
if( clock_gettime( CLOCK_REALTIME, &xCurrentTime ) != 0 )
{
iStatus = errno;
}
}
/* Calculate the difference between the current time and pxAbsoluteTime. */
if( iStatus == 0 )
{
if( UTILS_TimespecSubtract( &xDifference, pxAbsoluteTime, &xCurrentTime ) != 0 )
{
/* pxAbsoluteTime was in the past. */
iStatus = ETIMEDOUT;
}
}
/* Convert the time difference to ticks. */
if( iStatus == 0 )
{
iStatus = UTILS_TimespecToTicks( &xDifference, pxResult );
}
return iStatus;
}
/*-----------------------------------------------------------*/
int UTILS_TimespecToTicks( const struct timespec * const pxTimespec,
TickType_t * const pxResult )
{
int iStatus = 0;
uint64_t ullTotalTicks = 0;
long lNanoseconds = 0;
/* Check parameters. */
if( ( pxTimespec == NULL ) || ( pxResult == NULL ) )
{
iStatus = EINVAL;
}
else if( ( pxTimespec != NULL ) && ( UTILS_ValidateTimespec( pxTimespec ) == false ) )
{
iStatus = EINVAL;
}
if( iStatus == 0 )
{
/* Convert timespec.tv_sec to ticks. */
ullTotalTicks = ( uint64_t ) configTICK_RATE_HZ * ( uint64_t ) ( pxTimespec->tv_sec );
/* Convert timespec.tv_nsec to ticks. This value does not have to be checked
* for overflow because a valid timespec has 0 <= tv_nsec < 1000000000 and
* NANOSECONDS_PER_TICK > 1. */
lNanoseconds = pxTimespec->tv_nsec / ( long ) NANOSECONDS_PER_TICK + /* Whole nanoseconds. */
( long ) ( pxTimespec->tv_nsec % ( long ) NANOSECONDS_PER_TICK != 0 ); /* Add 1 to round up if needed. */
/* Add the nanoseconds to the total ticks. */
ullTotalTicks += ( uint64_t ) lNanoseconds;
/* Write result. */
*pxResult = ( TickType_t ) ullTotalTicks;
}
return iStatus;
}
/*-----------------------------------------------------------*/
void UTILS_NanosecondsToTimespec( int64_t llSource,
struct timespec * const pxDestination )
{
long lCarrySec = 0;
/* Convert to timespec. */
pxDestination->tv_sec = ( time_t ) ( llSource / NANOSECONDS_PER_SECOND );
pxDestination->tv_nsec = ( long ) ( llSource % NANOSECONDS_PER_SECOND );
/* Subtract from tv_sec if tv_nsec < 0. */
if( pxDestination->tv_nsec < 0L )
{
/* Compute the number of seconds to carry. */
lCarrySec = ( pxDestination->tv_nsec / ( long ) NANOSECONDS_PER_SECOND ) + 1L;
pxDestination->tv_sec -= ( time_t ) ( lCarrySec );
pxDestination->tv_nsec += lCarrySec * ( long ) NANOSECONDS_PER_SECOND;
}
}
/*-----------------------------------------------------------*/
int UTILS_TimespecAdd( struct timespec * const pxResult,
const struct timespec * const x,
const struct timespec * const y )
{
int64_t llResult64 = 0;
/* Check parameters. */
if( ( pxResult == NULL ) || ( x == NULL ) || ( y == NULL ) )
{
return -1;
}
/* Perform addition. */
llResult64 = ( ( ( int64_t ) ( x->tv_sec ) * NANOSECONDS_PER_SECOND ) + ( int64_t ) ( x->tv_nsec ) )
+ ( ( ( int64_t ) ( y->tv_sec ) * NANOSECONDS_PER_SECOND ) + ( int64_t ) ( y->tv_nsec ) );
/* Convert result to timespec. */
UTILS_NanosecondsToTimespec( llResult64, pxResult );
return ( int ) ( llResult64 < 0LL );
}
/*-----------------------------------------------------------*/
int UTILS_TimespecAddNanoseconds( struct timespec * const pxResult,
const struct timespec * const x,
int64_t llNanoseconds )
{
struct timespec y = { .tv_sec = ( time_t ) 0, .tv_nsec = 0L };
/* Check parameters. */
if( ( pxResult == NULL ) || ( x == NULL ) )
{
return -1;
}
/* Convert llNanoseconds to a timespec. */
UTILS_NanosecondsToTimespec( llNanoseconds, &y );
/* Perform addition. */
return UTILS_TimespecAdd( pxResult, x, &y );
}
/*-----------------------------------------------------------*/
int UTILS_TimespecSubtract( struct timespec * const pxResult,
const struct timespec * const x,
const struct timespec * const y )
{
int64_t llResult64 = 0;
/* Check parameters. */
if( ( pxResult == NULL ) || ( x == NULL ) || ( y == NULL ) )
{
return -1;
}
/* Perform addition. */
llResult64 = ( ( ( int64_t ) ( x->tv_sec ) * NANOSECONDS_PER_SECOND ) + ( int64_t ) ( x->tv_nsec ) )
- ( ( ( int64_t ) ( y->tv_sec ) * NANOSECONDS_PER_SECOND ) + ( int64_t ) ( y->tv_nsec ) );
/* Convert result to timespec. */
UTILS_NanosecondsToTimespec( llResult64, pxResult );
return ( int ) ( llResult64 < 0LL );
}
/*-----------------------------------------------------------*/
bool UTILS_ValidateTimespec( const struct timespec * const pxTimespec )
{
bool xReturn = false;
if( pxTimespec != NULL )
{
/* Verify 0 <= tv_nsec < 1000000000. */
if( ( pxTimespec->tv_nsec >= 0 ) &&
( pxTimespec->tv_nsec < NANOSECONDS_PER_SECOND ) )
{
xReturn = true;
}
}
return xReturn;
}
/*-----------------------------------------------------------*/

View File

@ -0,0 +1,46 @@
# ESP-IDF Changes
This document is used to track all changes made to the FreeRTOS-Plus-POSIX V1.0.0 source code used in ESP-IDF.
## License Headers
- Added `SPDX-FileCopyrightText` and `SPDX-FileContributor` tags to all files to pass ESP-IDF pre-commit checks.
## POSIX errno
Instead of relying on the FreeRTOS `errno` scheme by enabling `configUSE_POSIX_ERRNO`, the `errno` provided by newlib is used. `configUSE_POSIX_ERRNO` should stay disabled.
## API Changes
`mq_open()` has been changed to make the arguments `mode` and `attr` optional, as specified by [POSIX](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_open.html).
The following functions are stubs and always return `ENOSYS`:
* `mq_notify()`
* `mq_setattr()`
## Header File Organization
In the original FreeRTOS-Plus-POSIX project, the POSIX header files are provided in the sub directory `FreeRTOS-Plus-POSIX`. In ESP-IDF, however, the POSIX header files do not need any prefix. This increases compatibility with applications originally written for other platforms. All files originating from the FreeRTOS-Plus-POSIX project have been modified to reflect the different include path.
Wherever possible, ESP-IDF is using header files from newlib instead of the header files from FreeRTOS-Plus-POSIX.
In some cases, additional includes have been added to files from FreeRTOS-Plus-POSIX to simplify building.
## Critical Sections
The critical sections have been changed according to the ESP-IDF critical section API.
## Definitions
The following definitions have been added to the private include `utils.h`:
```
MICROSECONDS_PER_SECOND
NANOSECONDS_PER_SECOND
NANOSECONDS_PER_TICK
```
In FreeRTOS-Plus-POSIX, they are located in the custom `time.h`, but ESP-IDF uses newlib's `time.h`, where these definitions are not present.
## Code Format
- Files have been formatted by [`uncrustify`](https://github.com/uncrustify/uncrustify), using [FreeRTOS' `uncrustify.cfc`](../freertos/FreeRTOS-Kernel/uncrustify.cfg).

View File

@ -0,0 +1,155 @@
/*
* 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 mqueue.h
* @brief Message queues.
*
* http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/mqueue.h.html
*/
#pragma once
#include <time.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Message queue descriptor.
*/
typedef void * mqd_t;
/**
* @brief Message queue attributes.
*/
struct mq_attr
{
long mq_flags; /**< Message queue flags. */
long mq_maxmsg; /**< Maximum number of messages. */
long mq_msgsize; /**< Maximum message size. */
long mq_curmsgs; /**< Number of messages currently queued. */
};
/**
* @brief Close a message queue.
*
* Please refer to http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_close.html for more details.
*/
int mq_close( mqd_t mqdes );
/**
* @brief Get message queue attributes.
*
* Please refer to http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_getattr.html for more details.
*/
int mq_getattr( mqd_t mqdes,
struct mq_attr * mqstat );
/**
* @brief Open a message queue.
*
* Please refer to http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_open.html for more details.
*
* @note Supported name pattern: leading &lt;slash&gt; character in name is always required;
* the maximum length (excluding null-terminator) of the name argument can be NAME_MAX + 2.
* @note mode argument is not supported.
* @note Supported oflags: O_RDWR, O_CREAT, O_EXCL, and O_NONBLOCK.
*/
mqd_t mq_open( const char * name,
int oflag,
... );
/**
* @brief Receive a message from a message queue.
*
* Please refer to http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_receive.html for more details.
*
* @note msg_prio argument is not supported. Messages are not checked for corruption.
*/
ssize_t mq_receive( mqd_t mqdes,
char * msg_ptr,
size_t msg_len,
unsigned int * msg_prio );
/**
* @brief Send a message to a message queue.
*
* Please refer to http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_send.html for more details.
*
* @note msg_prio argument is not supported.
*/
int mq_send( mqd_t mqdes,
const char * msg_ptr,
size_t msg_len,
unsigned msg_prio );
/**
* @brief Receive a message from a message queue with timeout.
*
* Please refer to http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_timedreceive.html for more details.
*
* @note msg_prio argument is not supported. Messages are not checked for corruption.
*/
ssize_t mq_timedreceive( mqd_t mqdes,
char * msg_ptr,
size_t msg_len,
unsigned * msg_prio,
const struct timespec * abstime );
/**
* @brief Send a message to a message queue with timeout.
*
* Please refer to http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_timedsend.html for more details.
*
* @note msg_prio argument is not supported.
*/
int mq_timedsend( mqd_t mqdes,
const char * msg_ptr,
size_t msg_len,
unsigned msg_prio,
const struct timespec * abstime );
/**
* @brief Remove a message queue.
*
* Please refer to http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_unlink.html for more details.
*/
int mq_unlink( const char * name );
/* Added by Espressif - specified but not implemented functions, return ENOSYS */
int mq_notify( mqd_t, const struct sigevent * );
int mq_setattr( mqd_t, const struct mq_attr *, struct mq_attr * );
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,56 @@
/*
* 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.h
* @brief FreeRTOS+POSIX header.
*
* This file must be included before all other FreeRTOS+POSIX includes.
*/
#ifndef _FREERTOS_POSIX_H_
#define _FREERTOS_POSIX_H_
/* FreeRTOS+POSIX platform-specific configuration headers. */
#include "FreeRTOS_POSIX_portable.h"
#include "portable/FreeRTOS_POSIX_portable_default.h"
/* FreeRTOS includes. */
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "freertos/semphr.h"
#include "freertos/task.h"
/* FreeRTOS+POSIX data types and internal structs. */
#include <sys/types.h>
#include "FreeRTOS_POSIX_internal.h"
#endif /* _FREERTOS_POSIX_H_ */

View File

@ -0,0 +1,149 @@
/*
* 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 utils.h
* @brief Utility functions used by FreeRTOS+POSIX.
*/
#ifndef _FREERTOS_POSIX_UTILS_
#define _FREERTOS_POSIX_UTILS_
/* C standard library includes. */
#include <stdbool.h>
#include <stdint.h>
#include <time.h>
#define MICROSECONDS_PER_SECOND ( 1000000LL ) /**< Microseconds per second. */
#define NANOSECONDS_PER_SECOND ( 1000000000LL ) /**< Nanoseconds per second. */
#define NANOSECONDS_PER_TICK ( NANOSECONDS_PER_SECOND / configTICK_RATE_HZ )
/**
* @brief Calculates the length of pcString, up to xMaxLength.
*
* @param[in] pcString The string to find the length of.
* @param[in] xMaxLength The limit when searching for the end of pcString.
*
* @return 0 if pcString is NULL; otherwise, the length of pcString or xMaxLength,
* whichever is smaller.
*/
size_t UTILS_strnlen( const char * const pcString,
size_t xMaxLength );
/**
* @brief Calculates the number of ticks between now and a given timespec.
*
* @param[in] pxAbsoluteTime A time in the future, specified as seconds and
* nanoseconds since CLOCK_REALTIME's 0.
* @param[out] pxResult Where the result of the conversion is stored. The result
* is rounded up for fractional ticks.
*
* @return 0 on success. Otherwise, ETIMEDOUT if pxAbsoluteTime is in the past,
* or EINVAL for invalid parameters.
*/
int UTILS_AbsoluteTimespecToTicks( const struct timespec * const pxAbsoluteTime,
TickType_t * const pxResult );
/**
* @brief Converts a struct timespec to FreeRTOS ticks.
*
* @param[in] pxTimespec The timespec to convert.
* @param[out] pxResult Where the result of the conversion is stored. The result is rounded
* up for fractional ticks.
*
* @return 0 on success. Otherwise, EINVAL for invalid parameters.
*/
int UTILS_TimespecToTicks( const struct timespec * const pxTimespec,
TickType_t * const pxResult );
/**
* @brief Converts an integer value to a timespec.
*
* @param[in] llSource The value to convert.
* @param[out] pxDestination Where to store the converted value.
*
* @return No return value.
*/
void UTILS_NanosecondsToTimespec( int64_t llSource,
struct timespec * const pxDestination );
/**
* @brief Calculates pxResult = x + y.
*
* @param[out] pxResult Where the result of the calculation is stored.
* @param[in] x The first argument for addition.
* @param[in] y The second argument for addition.
*
* @return -1 if any argument was NULL; 1 if result is negative; otherwise, 0.
*/
int UTILS_TimespecAdd( struct timespec * const pxResult,
const struct timespec * const x,
const struct timespec * const y );
/**
* @brief Calculates pxResult = x + ( struct timespec ) nanosec.
*
* @param[out] pxResult Where the result of the calculation is stored.
* @param[in] x The first argument for addition.
* @param[in] llNanoseconds The second argument for addition.
*
* @return -1 if pxResult or x was NULL; 1 if result is negative; otherwise, 0.
*/
int UTILS_TimespecAddNanoseconds( struct timespec * const pxResult,
const struct timespec * const x,
int64_t llNanoseconds );
/**
* @brief Calculates pxResult = x - y.
*
* @param[out] pxResult Where the result of the calculation is stored.
* @param[in] x The first argument for subtraction.
* @param[in] y The second argument for subtraction.
*
* @return -1 if any argument was NULL; 1 if result is negative; otherwise, 0.
*/
int UTILS_TimespecSubtract( struct timespec * const pxResult,
const struct timespec * const x,
const struct timespec * const y );
/**
* @brief Checks that a timespec conforms to POSIX.
*
* A valid timespec must have 0 <= tv_nsec < 1000000000.
*
* @param[in] pxTimespec The timespec to validate.
*
* @return true if the pxTimespec is valid, false otherwise.
*/
bool UTILS_ValidateTimespec( const struct timespec * const pxTimespec );
#endif /* ifndef _FREERTOS_POSIX_UTILS_ */

View File

@ -0,0 +1,101 @@
/*
* 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
*/
#ifndef _FREERTOS_POSIX_INTERNAL_H_
#define _FREERTOS_POSIX_INTERNAL_H_
/**
* @file FreeRTOS_POSIX_internal.h
* @brief Internal structs and initializers for FreeRTOS+POSIX.
*/
/* Amazon FreeRTOS includes. */
#include "aws_doubly_linked_list.h"
/**
* @brief Mutex attribute object.
*/
typedef struct pthread_mutexattr_internal
{
int iType; /**< Mutex type. */
} pthread_mutexattr_internal_t;
/**
* @brief Mutex.
*/
typedef struct pthread_mutex_internal
{
BaseType_t xIsInitialized; /**< Set to pdTRUE if this mutex is initialized, pdFALSE otherwise. */
StaticSemaphore_t xMutex; /**< FreeRTOS mutex. */
TaskHandle_t xTaskOwner; /**< Owner; used for deadlock detection and permission checks. */
pthread_mutexattr_internal_t xAttr; /**< Mutex attributes. */
} pthread_mutex_internal_t;
/**
* @brief Compile-time initializer of pthread_mutex_internal_t.
*/
#define FREERTOS_POSIX_MUTEX_INITIALIZER \
( &( ( pthread_mutex_internal_t ) \
{ \
.xIsInitialized = pdFALSE, \
.xMutex = { { 0 } }, \
.xTaskOwner = NULL, \
.xAttr = { .iType = 0 } \
} \
) \
)
/**
* @brief Condition variable.
*/
typedef struct pthread_cond_internal
{
BaseType_t xIsInitialized; /**< Set to pdTRUE if this condition variable is initialized, pdFALSE otherwise. */
StaticSemaphore_t xCondMutex; /**< Prevents concurrent accesses to iWaitingThreads. */
StaticSemaphore_t xCondWaitSemaphore; /**< Threads block on this semaphore in pthread_cond_wait. */
int iWaitingThreads; /**< The number of threads currently waiting on this condition variable. */
} pthread_cond_internal_t;
/**
* @brief Compile-time initializer of pthread_cond_internal_t.
*/
#define FREERTOS_POSIX_COND_INITIALIZER \
( &( ( pthread_cond_internal_t ) \
{ \
.xIsInitialized = pdFALSE, \
.xCondMutex = { { 0 } }, \
.xCondWaitSemaphore = { { 0 } }, \
.iWaitingThreads = 0 \
} \
) \
)
#endif /* _FREERTOS_POSIX_INTERNAL_H_ */

View File

@ -0,0 +1,59 @@
/*
* 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_portable.h
* @brief Port-specific configuration of FreeRTOS+POSIX.
*/
#ifndef _FREERTOS_POSIX_PORTABLE_H_
#define _FREERTOS_POSIX_PORTABLE_H_
/* ESP-IDF already defines the following types. */
#define posixconfigENABLE_CLOCK_T 0
#define posixconfigENABLE_CLOCKID_T 0
#define posixconfigENABLE_MODE_T 0
#define posixconfigENABLE_PTHREAD_ATTR_T 0
#define posixconfigENABLE_PTHREAD_COND_T 0
#define posixconfigENABLE_PTHREAD_CONDATTR_T 0
#define posixconfigENABLE_PTHREAD_MUTEX_T 0
#define posixconfigENABLE_PTHREAD_MUTEXATTR_T 0
#define posixconfigENABLE_PTHREAD_T 0
#define posixconfigENABLE_TIME_T 0
#define posixconfigENABLE_TIMER_T 0
#define posixconfigENABLE_TIMESPEC 0
#define posixconfigENABLE_ITIMERSPEC 0
/* ESP-IDF already provides the header sched.h. Exclude them by
* activating the double inclusion guards. */
#define _FREERTOS_POSIX_SCHED_H_
#endif /* _FREERTOS_POSIX_PORTABLE_H_ */

View File

@ -0,0 +1,249 @@
/*
* Amazon FreeRTOS
* Copyright (C) 2017 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 aws_doubly_linked_list.h
* @brief Doubly Linked List implementation.
*
* A generic implementation of circular Doubly Linked List which consists of a
* list head and some list entries (zero in case of an empty list).
*
* To start with, a structure of type Link_t should be embedded in the structure
* which is to be organized as doubly linked list.
* @code
* typedef struct UserStruct
* {
* uint32_t ulField1;
* uint32_t ulField2;
* Link_t xLink;
* } UserStruct_t;
* @endcode
*
* A List head should then be defined and initialized.
* @code
* Link_t xListHead;
* listINIT_HEAD( &xListHead );
* @endcode
*
* listADD can then be used to add nodes to the list.
* @code
* listADD( &( xListHead ), &( pxUserStruct->xLink ) );
* @endcode
*
* listFOR_EACH can be used for traversing the list.
* @code
* Link_t *pxLink;
* UserStruct_t *pxUserStruct;
* listFOR_EACH( pxLink, &( xListHead ) )
* {
* pxUserStruct = listCONTAINER( pxLink, UserStruct_t, xLink );
* }
* @endcode
*
* listFOR_EACH_SAFE should be used if you want to perform destructive operations
* (like free) on nodes while traversing the list.
* @code
* Link_t *pxLink, *pxTempLink;
* UserStruct_t *pxUserStruct;
* listFOR_EACH( pxLink, pxTempLink, &( xListHead ) )
* {
* pxUserStruct = listCONTAINER( pxLink, UserStruct_t, xLink );
* free( pxUserStruct );
* }
* @endcode
*/
#ifndef _AWS_DOUBLY_LINKED_LIST_H_
#define _AWS_DOUBLY_LINKED_LIST_H_
#include <stddef.h>
#include <stdint.h>
/**
* @brief Struct embedded in any struct to make it a doubly linked
* list.
*
* pxNext in the head points to the first node in the list and pxPrev
* in the head points to the last node in the list. In case of empty
* list, both pxPrev and pxNext in the head point to the head node itself.
*/
typedef struct Link
{
struct Link * pxPrev; /**< Pointer to the previous node. */
struct Link * pxNext; /**< Pointer to the next node. */
} Link_t;
/**
* @brief Initializes the given list head to an empty list.
*
* @param[in] pxHead The given list head to initialize.
*/
#define listINIT_HEAD( pxHead ) \
{ \
( pxHead )->pxPrev = ( pxHead ); \
( pxHead )->pxNext = ( pxHead ); \
}
/**
* @brief Adds the given new node to the given list.
*
* @param[in] pxHead The head of the given list.
* @param[in] pxLink The given new node to be added to the given
* list.
*/
#define listADD( pxHead, pxLink ) \
{ \
Link_t * pxPrevLink = ( pxHead ); \
Link_t * pxNextLink = ( ( pxHead )->pxNext ); \
\
( pxLink )->pxNext = pxNextLink; \
pxNextLink->pxPrev = ( pxLink ); \
pxPrevLink->pxNext = ( pxLink ); \
( pxLink )->pxPrev = ( pxPrevLink ); \
}
/**
* @brief Removes the given node from the list it is part of.
*
* If the given node is not a part of any list (i.e. next and previous
* nodes are NULL), nothing happens.
*
* @param[in] pxLink The given node to remove from the list.
*/
#define listREMOVE( pxLink ) \
{ \
/* If the link is part of a list, remove it from the list. */ \
if( ( pxLink )->pxNext != NULL && ( pxLink )->pxPrev != NULL ) \
{ \
( pxLink )->pxPrev->pxNext = ( pxLink )->pxNext; \
( pxLink )->pxNext->pxPrev = ( pxLink )->pxPrev; \
} \
\
/* Make sure that this link is not part of any list anymore. */ \
( pxLink )->pxPrev = NULL; \
( pxLink )->pxNext = NULL; \
}
/**
* @brief Given the head of a list, checks if the list is empty.
*
* @param[in] pxHead The head of the given list.
*/
#define listIS_EMPTY( pxHead ) ( ( ( pxHead ) == NULL ) || ( ( pxHead )->pxNext == ( pxHead ) ) )
/**
* @brief Removes the first node from the given list and returns it.
*
* Removes the first node from the given list and assigns it to the
* pxLink parameter. If the list is empty, it assigns NULL to the
* pxLink.
*
* @param[in] pxHead The head of the list from which to remove the
* first node.
* @param[out] pxLink The output parameter to receive the removed
* node.
*/
#define listPOP( pxHead, pxLink ) \
{ \
if( listIS_EMPTY( ( pxHead ) ) ) \
{ \
( pxLink ) = NULL; \
} \
else \
{ \
( pxLink ) = ( pxHead )->pxNext; \
/* If the link is part of a list, remove it from the list. */ \
if( ( pxLink )->pxNext != NULL && ( pxLink )->pxPrev != NULL ) \
{ \
( pxLink )->pxPrev->pxNext = ( pxLink )->pxNext; \
( pxLink )->pxNext->pxPrev = ( pxLink )->pxPrev; \
} \
\
/* Make sure that this link is not part of any list anymore. */ \
( pxLink )->pxPrev = NULL; \
( pxLink )->pxNext = NULL; \
} \
}
/**
* @brief Merges a list into a given list.
*
* @param[in] pxHeadResultList The head of the given list into which the
* other list should be merged.
* @param[in] pxHeadListToMerge The head of the list to be merged into the
* given list.
*/
#define listMERGE( pxHeadResultList, pxHeadListToMerge ) \
{ \
if( !listIS_EMPTY( ( pxHeadListToMerge ) ) ) \
{ \
/* Setup links between last node of listToMerge and first node of resultList. */ \
( pxHeadListToMerge )->pxPrev->pxNext = ( pxHeadResultList )->pxNext; \
( pxHeadResultList )->pxNext->pxPrev = ( pxHeadListToMerge )->pxPrev; \
\
/* Setup links between first node of listToMerge and the head of resultList. */ \
( pxHeadListToMerge )->pxNext->pxPrev = ( pxHeadResultList ); \
( pxHeadResultList )->pxNext = ( pxHeadListToMerge )->pxNext; \
/* Empty the merged list. */ \
listINIT_HEAD( ( pxHeadListToMerge ) ); \
} \
}
/**
* @brief Helper macro to iterate over a list. pxLink contains the link node
* in each iteration.
*/
#define listFOR_EACH( pxLink, pxHead ) \
for( ( pxLink ) = ( pxHead )->pxNext; \
( pxLink ) != ( pxHead ); \
( pxLink ) = ( pxLink )->pxNext )
/**
* @brief Helper macro to iterate over a list. It is safe to destroy/free the
* nodes while iterating. pxLink contains the link node in each iteration.
*/
#define listFOR_EACH_SAFE( pxLink, pxTempLink, pxHead ) \
for( ( pxLink ) = ( pxHead )->pxNext, ( pxTempLink ) = ( pxLink )->pxNext; \
( pxLink ) != ( pxHead ); \
( pxLink ) = ( pxTempLink ), ( pxTempLink ) = ( pxLink )->pxNext )
/**
* @brief Given the pointer to the link member (of type Link_t) in a struct,
* extracts the pointer to the containing struct.
*
* @param[in] pxLink The pointer to the link member.
* @param[in] type The type of the containing struct.
* @param[in] member Name of the link member in the containing struct.
*/
#define listCONTAINER( pxLink, type, member ) ( ( type * ) ( ( uint8_t * ) ( pxLink ) - ( uint8_t * ) ( &( ( type * ) 0 )->member ) ) )
#endif /* _AWS_DOUBLY_LINKED_LIST_H_ */

View File

@ -0,0 +1,144 @@
/*
* 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_portable_default.h
* @brief Defaults for port-specific configuration of FreeRTOS+POSIX.
*/
#ifndef _FREERTOS_POSIX_PORTABLE_DEFAULT_H_
#define _FREERTOS_POSIX_PORTABLE_DEFAULT_H_
/**
* @brief The FreeRTOS task name given to pthreads.
*/
#ifndef posixconfigPTHREAD_TASK_NAME
#define posixconfigPTHREAD_TASK_NAME "pthread"
#endif
/**
* @brief the FreeRTOS timer name given to POSIX timers.
*/
#ifndef posixconfigTIMER_NAME
#define posixconfigTIMER_NAME "timer"
#endif
/**
* @defgroup Defaults for POSIX message queue implementation.
*/
/**@{ */
#ifndef posixconfigMQ_MAX_MESSAGES
#define posixconfigMQ_MAX_MESSAGES 10 /**< Maximum number of messages in an mq at one time. */
#endif
#ifndef posixconfigMQ_MAX_SIZE
#define posixconfigMQ_MAX_SIZE 128 /**< Maximum size (in bytes) of each message. */
#endif
/**@} */
/**
* @defgroup POSIX implementation-dependent constants usually defined in limits.h.
*
* They are defined here to provide portability between platforms.
*/
/**@{ */
#ifndef PTHREAD_STACK_MIN
#define PTHREAD_STACK_MIN configMINIMAL_STACK_SIZE * sizeof( StackType_t ) /**< Minimum size in bytes of thread stack storage. */
#endif
#ifndef NAME_MAX
#define NAME_MAX 64 /**< Maximum number of bytes in a filename (not including terminating null). */
#endif
#ifndef SEM_VALUE_MAX
#define SEM_VALUE_MAX 0xFFFFU /**< Maximum value of a sem_t. */
#endif
/**@} */
/**
* @defgroup Enable typedefs of POSIX types.
*
* Set these values to 1 or 0 to enable or disable the typedefs, respectively.
* These typedefs should only be disabled if they conflict with system typedefs.
*/
/**@{ */
#ifndef posixconfigENABLE_CLOCK_T
#define posixconfigENABLE_CLOCK_T 1 /**< clock_t in sys/types.h */
#endif
#ifndef posixconfigENABLE_CLOCKID_T
#define posixconfigENABLE_CLOCKID_T 1 /**< clockid_t in sys/types.h */
#endif
#ifndef posixconfigENABLE_MODE_T
#define posixconfigENABLE_MODE_T 1 /**< mode_t in sys/types.h */
#endif
#ifndef posixconfigENABLE_PID_T
#define posixconfigENABLE_PID_T 1 /**< pid_t in sys/types.h */
#endif
#ifndef posixconfigENABLE_PTHREAD_ATTR_T
#define posixconfigENABLE_PTHREAD_ATTR_T 1 /**< pthread_attr_t in sys/types.h */
#endif
#ifndef posixconfigENABLE_PTHREAD_COND_T
#define posixconfigENABLE_PTHREAD_COND_T 1 /**< pthread_cond_t in sys/types.h */
#endif
#ifndef posixconfigENABLE_PTHREAD_CONDATTR_T
#define posixconfigENABLE_PTHREAD_CONDATTR_T 1 /**< pthread_condattr_t in sys/types.h */
#endif
#ifndef posixconfigENABLE_PTHREAD_MUTEX_T
#define posixconfigENABLE_PTHREAD_MUTEX_T 1 /**< pthread_mutex_t in sys/types.h */
#endif
#ifndef posixconfigENABLE_PTHREAD_MUTEXATTR_T
#define posixconfigENABLE_PTHREAD_MUTEXATTR_T 1 /**< pthread_mutexattr_t in sys/types.h */
#endif
#ifndef posixconfigENABLE_PTHREAD_T
#define posixconfigENABLE_PTHREAD_T 1 /**< pthread_t in sys/types.h */
#endif
#ifndef posixconfigENABLE_SSIZE_T
#define posixconfigENABLE_SSIZE_T 1 /**< ssize_t in sys/types.h */
#endif
#ifndef posixconfigENABLE_TIME_T
#define posixconfigENABLE_TIME_T 1 /**< time_t in sys/types.h */
#endif
#ifndef posixconfigENABLE_TIMER_T
#define posixconfigENABLE_TIMER_T 1 /**< timer_t in sys/types.h */
#endif
#ifndef posixconfigENABLE_USECONDS_T
#define posixconfigENABLE_USECONDS_T 1 /**< useconds_t in sys/types.h */
#endif
#ifndef posixconfigENABLE_TIMESPEC
#define posixconfigENABLE_TIMESPEC 1 /**< struct timespec in time.h */
#endif
#ifndef posixconfigENABLE_ITIMERSPEC
#define posixconfigENABLE_ITIMERSPEC 1 /**< struct itimerspec in time.h */
#endif
#ifndef posixconfigENABLE_TM
#define posixconfigENABLE_TM 1 /**< struct tm in time.h */
#endif
/**@} */
#endif /* ifndef _FREERTOS_POSIX_PORTABLE_DEFAULT_H_ */

5
components/rt/sbom.yml Normal file
View File

@ -0,0 +1,5 @@
name: 'FreeRTOS-Plus-POSIX'
version: '1.0.0'
supplier: 'Organization: Espressif Systems (Shanghai) CO LTD'
originator: 'Organization: Amazon Web Services'
description: Portable Operating System Interface for FreeRTOS

View File

@ -0,0 +1,9 @@
# Documentation: .gitlab/ci/README.md#manifest-file-to-control-the-buildtest-apps
components/rt/test_apps/posix_rt_test:
enable:
- if: IDF_TARGET in ["esp32", "esp32s2", "esp32c3", "esp32p4"]
reason: covers all major arch types, xtensa vs riscv, single vs dual-core
depends_components:
- rt
- freertos

View File

@ -0,0 +1,7 @@
# The following five lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(COMPONENTS main)
project(posix_test)

View File

@ -0,0 +1,2 @@
| Supported Targets | ESP32 | ESP32-C3 | ESP32-P4 | ESP32-S2 |
| ----------------- | ----- | -------- | -------- | -------- |

View File

@ -0,0 +1,4 @@
idf_component_register(SRCS "main.c" "mqueue_test.cpp"
INCLUDE_DIRS "."
PRIV_REQUIRES rt unity
WHOLE_ARCHIVE)

View File

@ -0,0 +1,36 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <stdio.h>
#include <time.h>
#include "unity.h"
#include "unity_test_runner.h"
#include "unity_test_utils_memory.h"
#include "esp_log.h"
static const char *TAG = "Linux_sim";
void setUp(void)
{
unity_utils_set_leak_level(0);
unity_utils_record_free_mem();
}
void tearDown(void)
{
unity_utils_evaluate_leaks();
}
void app_main(void)
{
// The following code avoids memory leaks from initializing libc structures in clock_gettime() calls.
struct timespec tp;
clock_gettime(CLOCK_REALTIME, &tp);
(void) tp;
ESP_LOGI(TAG, "Running FreeRTOS Linux test app");
unity_run_menu();
}

View File

@ -0,0 +1,288 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <thread>
#include "unity.h"
#include "mqueue.h"
// Note: implementation does not support mode argument
// Note: implementation does not support message priorities (msg_prio arguments)
struct MessageQueueFixture {
MessageQueueFixture(const char *name = "/test_queue",
long int maxmsg = 10,
long int msgsize = sizeof(int),
int oflag = O_CREAT | O_WRONLY)
: name(name) {
struct mq_attr conf = {
.mq_flags = 0, // ignored by mq_open
.mq_maxmsg = maxmsg,
.mq_msgsize = msgsize,
.mq_curmsgs = 0 // ignored by mq_open
};
mq = mq_open(name, oflag, S_IRUSR, &conf);
TEST_ASSERT_NOT_EQUAL((mqd_t) -1, mq);
}
~MessageQueueFixture() {
TEST_ASSERT_EQUAL(0, mq_close(mq));
TEST_ASSERT_EQUAL(0, mq_unlink(name));
}
const char *name;
mqd_t mq;
};
TEST_CASE("open with invalid message queue name fails", "[mqueue]")
{
struct mq_attr conf = {
.mq_flags = 0, // ignored by mq_open
.mq_maxmsg = 10,
.mq_msgsize = sizeof(int),
.mq_curmsgs = 0 // ignored by mq_open
};
// 255 + 2 characters, excl. '\0'
#define TOO_LONG_NAME "/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" \
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" \
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" \
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" \
TEST_ASSERT_EQUAL((mqd_t) -1, mq_open("", O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR , &conf));
TEST_ASSERT_EQUAL(EINVAL, errno);
TEST_ASSERT_EQUAL((mqd_t) -1, mq_open("my_queue", O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR , &conf));
TEST_ASSERT_EQUAL(EINVAL, errno);
TEST_ASSERT_EQUAL((mqd_t) -1, mq_open(TOO_LONG_NAME, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR , &conf));
TEST_ASSERT_EQUAL(EINVAL, errno);
}
TEST_CASE("name / is valid", "[mqueue]")
{
mqd_t mq = mq_open("/", O_CREAT | O_WRONLY, S_IRUSR, NULL);
TEST_ASSERT_NOT_EQUAL((mqd_t) -1, mq);
TEST_ASSERT_EQUAL(0, mq_close(mq));
TEST_ASSERT_EQUAL(0, mq_unlink("/"));
}
TEST_CASE("open without create flag fails", "[mqueue]")
{
TEST_ASSERT_EQUAL((mqd_t) -1, mq_open("/my_queue", O_WRONLY));
TEST_ASSERT_EQUAL(ENOENT, errno);
}
TEST_CASE("open mq_maxmsg <= 0", "[mqueue]")
{
struct mq_attr conf = {
.mq_flags = 0, // ignored by mq_open
.mq_maxmsg = 0,
.mq_msgsize = sizeof(int),
.mq_curmsgs = 0 // ignored by mq_open
};
TEST_ASSERT_EQUAL((mqd_t) -1, mq_open("/my_queue", O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR , &conf));
TEST_ASSERT_EQUAL(EINVAL, errno);
conf.mq_maxmsg = -1;
TEST_ASSERT_EQUAL((mqd_t) -1, mq_open("/my_queue", O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR , &conf));
TEST_ASSERT_EQUAL(EINVAL, errno);
}
TEST_CASE("open mq_msgsize <= 0", "[mqueue]")
{
struct mq_attr conf = {
.mq_flags = 0, // ignored by mq_open
.mq_maxmsg = 10,
.mq_msgsize = 0,
.mq_curmsgs = 0 // ignored by mq_open
};
TEST_ASSERT_EQUAL((mqd_t) -1, mq_open("/my_queue", O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR , &conf));
TEST_ASSERT_EQUAL(EINVAL, errno);
conf.mq_msgsize = -1;
TEST_ASSERT_EQUAL((mqd_t) -1, mq_open("/my_queue", O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR , &conf));
TEST_ASSERT_EQUAL(EINVAL, errno);
}
TEST_CASE("creating already existing message queue fails", "[mqueue]")
{
MessageQueueFixture mq_fix;
TEST_ASSERT_EQUAL((mqd_t) -1, mq_open(mq_fix.name, O_CREAT | O_WRONLY | O_EXCL, S_IRUSR | S_IWUSR , NULL));
TEST_ASSERT_EQUAL(EEXIST, errno);
}
TEST_CASE("open with default flags", "[mqueue]")
{
mqd_t mq = mq_open("/my_queue", O_CREAT | O_WRONLY, S_IRUSR, NULL);
TEST_ASSERT_NOT_EQUAL((mqd_t) -1, mq);
struct mq_attr default_conf;
TEST_ASSERT_EQUAL(0, mq_getattr(mq, &default_conf));
TEST_ASSERT_EQUAL(10, default_conf.mq_maxmsg);
TEST_ASSERT_EQUAL(128, default_conf.mq_msgsize);
TEST_ASSERT_EQUAL(0, mq_close(mq));
TEST_ASSERT_EQUAL(0, mq_unlink("/my_queue"));
}
TEST_CASE("open propagates attributes correctly", "[mqueue]")
{
MessageQueueFixture mq_fix("/test", 4, 8);
struct mq_attr conf;
TEST_ASSERT_EQUAL(0, mq_getattr(mq_fix.mq, &conf));
TEST_ASSERT_EQUAL(4, conf.mq_maxmsg);
TEST_ASSERT_EQUAL(8, conf.mq_msgsize);
}
TEST_CASE("message queue does not exceed queue size", "[mqueue]")
{
MessageQueueFixture mq_fix("/test", 1, sizeof(int));
int to_send = 47;
TEST_ASSERT_EQUAL(0, mq_send(mq_fix.mq, (const char*) &to_send, sizeof(to_send), 0));
int to_send_2 = 48;
int received = 0;
const struct timespec zero_time = {0, 0};
TEST_ASSERT_EQUAL(-1, mq_timedsend(mq_fix.mq, (const char*) &to_send_2, sizeof(to_send_2), 0, &zero_time));
TEST_ASSERT_EQUAL(ETIMEDOUT, errno);
TEST_ASSERT_EQUAL(4, mq_receive(mq_fix.mq, (char*) &received, sizeof(received), nullptr));
TEST_ASSERT_EQUAL(47, received);
}
TEST_CASE("sending on invalid mq descriptor fails", "[mqueue]")
{
int to_send = 47;
TEST_ASSERT_EQUAL(-1, mq_send(nullptr, (char*) &to_send, sizeof(to_send), 0));
TEST_ASSERT_EQUAL(EBADF, errno);
}
TEST_CASE("sending with invalid timeout fails", "[mqueue]")
{
MessageQueueFixture mq_fix("/test", 1, sizeof(int));
int to_send = 47;
struct timespec invalid_time = {0, 1'000'000'000};
TEST_ASSERT_EQUAL(-1, mq_timedsend(mq_fix.mq, (char*) &to_send, sizeof(to_send), 0, &invalid_time));
TEST_ASSERT_EQUAL(EINVAL, errno);
invalid_time.tv_nsec = -1;
TEST_ASSERT_EQUAL(-1, mq_timedsend(mq_fix.mq, (char*) &to_send, sizeof(to_send), 0, &invalid_time));
TEST_ASSERT_EQUAL(EINVAL, errno);
}
TEST_CASE("sending with invalid message size fails", "[mqueue]")
{
MessageQueueFixture mq_fix("/test", 1, sizeof(int));
int to_send = 47;
TEST_ASSERT_EQUAL(-1, mq_send(mq_fix.mq, (char*) &to_send, sizeof(to_send) + 1, 0));
TEST_ASSERT_EQUAL(EMSGSIZE, errno);
}
TEST_CASE("sending does not exceed queue size", "[mqueue]")
{
MessageQueueFixture mq_fix("/test", 1, sizeof(int));
int to_send = 47;
TEST_ASSERT_EQUAL(0, mq_send(mq_fix.mq, (const char*) &to_send, sizeof(to_send), 0));
int to_send_2 = 48;
int received = 0;
const struct timespec zero_time = {0, 0};
TEST_ASSERT_EQUAL(-1, mq_timedsend(mq_fix.mq, (const char*) &to_send_2, sizeof(to_send_2), 0, &zero_time));
TEST_ASSERT_EQUAL(ETIMEDOUT, errno);
TEST_ASSERT_EQUAL(4, mq_receive(mq_fix.mq, (char*) &received, sizeof(received), nullptr));
TEST_ASSERT_EQUAL(47, received);
}
TEST_CASE("nonblocking send works", "[mqueue]")
{
MessageQueueFixture mq_fix("/test", 1, sizeof(int), O_CREAT | O_WRONLY | O_NONBLOCK);
int to_send = 47;
int received = 0;
TEST_ASSERT_EQUAL(0, mq_send(mq_fix.mq, (char*) &to_send, sizeof(to_send), 0));
int to_send_2 = 48;
const struct timespec zero_time = {0, 0};
TEST_ASSERT_EQUAL(-1, mq_timedsend(mq_fix.mq, (char*) &to_send_2, sizeof(to_send_2), 0, &zero_time));
TEST_ASSERT_EQUAL(EAGAIN, errno);
TEST_ASSERT_EQUAL(-1, mq_send(mq_fix.mq, (char*) &to_send_2, sizeof(to_send_2), 0));
TEST_ASSERT_EQUAL(EAGAIN, errno);
TEST_ASSERT_EQUAL(4, mq_receive(mq_fix.mq, (char*) &received, sizeof(received), nullptr));
TEST_ASSERT_EQUAL(47, received);
}
TEST_CASE("receiving on invalid mq descriptor fails", "[mqueue]")
{
int received;
TEST_ASSERT_EQUAL(-1, mq_receive(nullptr, (char*) &received, sizeof(received), nullptr));
TEST_ASSERT_EQUAL(EBADF, errno);
}
TEST_CASE("receiving with invalid timeout fails", "[mqueue]")
{
MessageQueueFixture mq_fix("/test", 1, sizeof(int));
int received;
struct timespec invalid_time = {0, 1'000'000'000};
TEST_ASSERT_EQUAL(-1, mq_timedreceive(mq_fix.mq, (char*) &received, sizeof(received), 0, &invalid_time));
TEST_ASSERT_EQUAL(EINVAL, errno);
invalid_time.tv_nsec = -1;
TEST_ASSERT_EQUAL(-1, mq_timedreceive(mq_fix.mq, (char*) &received, sizeof(received), 0, &invalid_time));
TEST_ASSERT_EQUAL(EINVAL, errno);
}
TEST_CASE("receiving with invalid message size fails", "[mqueue]")
{
MessageQueueFixture mq_fix("/test", 1, sizeof(int));
int received;
TEST_ASSERT_EQUAL(-1, mq_send(mq_fix.mq, (char*) &received, sizeof(received) + 1, 0));
TEST_ASSERT_EQUAL(EMSGSIZE, errno);
}
TEST_CASE("receive on empty message queue times out", "[mqueue]")
{
MessageQueueFixture mq_fix("/test", 1, sizeof(int));
int received;
const struct timespec zero_time = {0, 0};
TEST_ASSERT_EQUAL(-1, mq_timedreceive(mq_fix.mq, (char*) &received, sizeof(received), 0, &zero_time));
TEST_ASSERT_EQUAL(ETIMEDOUT, errno);
}
TEST_CASE("nonblocking receive works", "[mqueue]")
{
MessageQueueFixture mq_fix("/test", 1, sizeof(int), O_CREAT | O_WRONLY | O_NONBLOCK);
int received;
const struct timespec zero_time = {0, 0};
TEST_ASSERT_EQUAL(-1, mq_timedreceive(mq_fix.mq, (char*) &received, sizeof(received), 0, &zero_time));
TEST_ASSERT_EQUAL(EAGAIN, errno);
TEST_ASSERT_EQUAL(-1, mq_receive(mq_fix.mq, (char*) &received, sizeof(received), nullptr));
TEST_ASSERT_EQUAL(EAGAIN, errno);
}
TEST_CASE("queue sends and receives multiple messages", "[mqueue]")
{
MessageQueueFixture mq_fix("/test", 2, sizeof(int), O_CREAT | O_WRONLY | O_NONBLOCK);
int received;
int to_send = 47;
TEST_ASSERT_EQUAL(0, mq_send(mq_fix.mq, (char*) &to_send, sizeof(to_send), 0));
to_send = 48;
TEST_ASSERT_EQUAL(0, mq_send(mq_fix.mq, (char*) &to_send, sizeof(to_send), 0));
TEST_ASSERT_EQUAL(4, mq_receive(mq_fix.mq, (char*) &received, sizeof(received), nullptr));
TEST_ASSERT_EQUAL(47, received);
TEST_ASSERT_EQUAL(4, mq_receive(mq_fix.mq, (char*) &received, sizeof(received), nullptr));
TEST_ASSERT_EQUAL(48, received);
}

View File

@ -0,0 +1,13 @@
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: CC0-1.0
import pytest
from pytest_embedded import Dut
@pytest.mark.generic
@pytest.mark.esp32
@pytest.mark.esp32c3
@pytest.mark.esp32p4
@pytest.mark.esp32s2
def test_rt_mqueue(dut: Dut) -> None:
dut.run_all_single_board_cases()

View File

@ -0,0 +1,2 @@
# This "default" configuration is appended to all other configurations
CONFIG_ESP_TASK_WDT_INIT=n

View File

@ -1,5 +1,5 @@
POSIX Threads Support POSIX Support (Including POSIX Threads Support)
===================== ===============================================
:link_to_translation:`zh_CN:[中文]` :link_to_translation:`zh_CN:[中文]`
@ -12,7 +12,11 @@ POSIX Threads are implemented in ESP-IDF as wrappers around equivalent FreeRTOS
Pthreads can be used in ESP-IDF by including standard ``pthread.h`` header, which is included in the toolchain libc. An additional ESP-IDF specific header, ``esp_pthread.h``, provides additional non-POSIX APIs for using some ESP-IDF features with pthreads. Pthreads can be used in ESP-IDF by including standard ``pthread.h`` header, which is included in the toolchain libc. An additional ESP-IDF specific header, ``esp_pthread.h``, provides additional non-POSIX APIs for using some ESP-IDF features with pthreads.
C++ Standard Library implementations for ``std::thread``, ``std::mutex``, ``std::condition_variable``, etc., are realized using pthreads (via GCC libstdc++). Therefore, restrictions mentioned here also apply to the equivalent C++ standard library functionality. Besides POSIX Threads, ESP-IDF also supports :ref:`POSIX message queues <posix_message_queues>`.
C++ Standard Library implementations for ``std::thread``, ``std::mutex``, ``std::condition_variable``, etc., are realized using pthreads and other POSIX APIs (via GCC libstdc++). Therefore, restrictions mentioned here also apply to the equivalent C++ standard library functionality.
If you identify a useful API that you would like to see implemented in ESP-IDF, please open a `feature request on GitHub <https://github.com/espressif/esp-idf/issues>`_ with the details.
RTOS Integration RTOS Integration
---------------- ----------------
@ -23,6 +27,10 @@ Unlike many operating systems using POSIX Threads, ESP-IDF is a real-time operat
When calling a standard libc or C++ sleep function, such as ``usleep`` defined in ``unistd.h``, the task will only block and yield the core if the sleep time is longer than :ref:`one FreeRTOS tick period <CONFIG_FREERTOS_HZ>`. If the time is shorter, the thread will busy-wait instead of yielding to another RTOS task. When calling a standard libc or C++ sleep function, such as ``usleep`` defined in ``unistd.h``, the task will only block and yield the core if the sleep time is longer than :ref:`one FreeRTOS tick period <CONFIG_FREERTOS_HZ>`. If the time is shorter, the thread will busy-wait instead of yielding to another RTOS task.
.. note::
The POSIX ``errno`` is provided by newlib in ESP-IDF. Thus the configuration ``configUSE_POSIX_ERRNO`` is not used and should stay disabled.
By default, all POSIX Threads have the same RTOS priority, but it is possible to change this by calling a :ref:`custom API <esp-pthread>`. By default, all POSIX Threads have the same RTOS priority, but it is possible to change this by calling a :ref:`custom API <esp-pthread>`.
Standard Features Standard Features
@ -169,6 +177,53 @@ Thread-Specific Data
There are other options for thread local storage in ESP-IDF, including options with higher performance. See :doc:`/api-guides/thread-local-storage`. There are other options for thread local storage in ESP-IDF, including options with higher performance. See :doc:`/api-guides/thread-local-storage`.
.. _posix_message_queues:
Message Queues
^^^^^^^^^^^^^^
The message queue implementation is based on the `FreeRTOS-Plus-POSIX <https://www.freertos.org/FreeRTOS-Plus/FreeRTOS_Plus_POSIX/index.html>`_ project. Message queues are not made available in any filesystem on ESP-IDF. Message priorities are not supported.
The following API functions of the POSIX message queue specification are implemented:
* `mq_open() <https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_open.html>`_
- The ``name`` argument has, besides the POSIX specification, the following additional restrictions:
- It has to begin with a leading slash.
- It has to be no more than 255 + 2 characters long (including the leading slash, excluding the terminating null byte). However, memory for ``name`` is dynamically allocated internally, so the shorter it is, the fewer memory it will consume.
- The ``mode`` argument is not implemented and is ignored.
- Supported ``oflags``: ``O_RDWR``, ``O_CREAT``, ``O_EXCL``, and ``O_NONBLOCK``
* `mq_close() <https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_close.html>`_
* `mq_unlink() <https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_unlink.html>`_
* `mq_receive() <https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_receive.html>`_
- Since message priorities are not supported, ``msg_prio`` is unused.
* `mq_timedreceive() <https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_receive.html>`_
- Since message priorities are not supported, ``msg_prio`` is unused.
* `mq_send() <https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_send.html>`_
- Since message priorities are not supported, ``msg_prio`` has no effect.
* `mq_timedsend() <https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_send.html>`_
- Since message priorities are not supported, ``msg_prio`` has no effect.
* `mq_getattr() <https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_getattr.html>`_
`mq_notify() <https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_notify.html>`_ and `mq_setattr() <https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_setattr.html>`_ are not implemented.
Building
........
To use the POSIX message queue API, please add ``rt`` as a requirement in your component's ``CMakeLists.txt``
.. note::
If you have used `FreeRTOS-Plus-POSIX <https://www.freertos.org/FreeRTOS-Plus/FreeRTOS_Plus_POSIX/index.html>`_ in another FreeRTOS project before, please note that the include paths in IDF are POSIX-like. Hence, applications include ``mqueue.h`` directly instead of using the subdirectory include ``FreeRTOS_POSIX/mqueue.h``.
Not Implemented Not Implemented
--------------- ---------------
@ -176,8 +231,10 @@ The ``pthread.h`` header is a standard header and includes additional APIs and f
* ``pthread_cancel()`` returns ``ENOSYS`` if called. * ``pthread_cancel()`` returns ``ENOSYS`` if called.
* ``pthread_condattr_init()`` returns ``ENOSYS`` if called. * ``pthread_condattr_init()`` returns ``ENOSYS`` if called.
* `mq_notify() <https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_notify.html>`_ returns ``ENOSYS`` if called.
* `mq_setattr() <https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_setattr.html>`_ returns ``ENOSYS`` if called.
Other POSIX Threads functions (not listed here) are not implemented and will produce either a compiler or a linker error if referenced from an ESP-IDF application. If you identify a useful API that you would like to see implemented in ESP-IDF, please open a `feature request on GitHub <https://github.com/espressif/esp-idf/issues>`_ with the details. Other POSIX Threads functions (not listed here) are not implemented and will produce either a compiler or a linker error if referenced from an ESP-IDF application.
.. _esp-pthread: .. _esp-pthread:

View File

@ -236,6 +236,13 @@ examples/system/pthread:
depends_components: depends_components:
- pthread - pthread
examples/system/rt_mqueue:
disable_test:
- if: IDF_TARGET != "esp32" and (NIGHTLY_RUN != "1" or IDF_TARGET == "linux")
reason: no target specific functionality, testing on a single target is sufficient
depends_components:
- rt
examples/system/select: examples/system/select:
disable: disable:
- if: IDF_TARGET != "esp32c3" and (NIGHTLY_RUN != "1" or IDF_TARGET == "linux") - if: IDF_TARGET != "esp32c3" and (NIGHTLY_RUN != "1" or IDF_TARGET == "linux")

View File

@ -0,0 +1,8 @@
# For more information about build system see
# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html
# The following five lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(posix_mqueue)

View File

@ -0,0 +1,37 @@
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-H2 | ESP32-P4 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- |
# POSIX Message Queue Example
A simple example using a POSIX message queue. Two tasks are reading from and writing to a POSIX message queue.
## How to use example
### Hardware Required
This example should be able to run on any supported Espressif SoC development board.
### Build and Flash
Enter `idf.py -p PORT flash monitor` to build, flash and monitor the project.
(To exit the serial monitor, type ``Ctrl-]``.)
See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.
## Example Output
If you see the following infinite console output, your example should be running correctly:
```
sending: 0
received: 0
sending: 1
received: 1
sending: 2
received: 2
sending: 3
received: 3
...
```

View File

@ -0,0 +1,3 @@
idf_component_register(SRCS "posix_mqueue_example_main.c"
INCLUDE_DIRS "."
PRIV_REQUIRES rt)

View File

@ -0,0 +1,101 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdbool.h>
#include <pthread.h>
#include <mqueue.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
static int s_counter = 0;
const unsigned int MSG_PRIO = 0;
static void *sender_function(void * arg)
{
mqd_t *write_descr = (mqd_t*) arg;
while (true) {
printf("sending: %d\n", s_counter);
int result = mq_send(*write_descr, (const char*) &s_counter, sizeof(s_counter), MSG_PRIO);
if (result != 0) {
perror("Sending failed");
abort();
}
s_counter++;
sleep(1);
}
return NULL;
}
static void *receiver_function(void * arg)
{
mqd_t *read_descr = (mqd_t*) arg;
while (true) {
int msg;
int result = mq_receive(*read_descr, (char*) &msg, sizeof(msg), NULL);
if (result == -1) {
perror("Sending failed");
abort();
}
printf("received: %d\n", msg);
}
return NULL;
}
void app_main(void)
{
mqd_t write_descr;
mqd_t read_descr;
pthread_t sender;
pthread_t receiver;
struct mq_attr configuration = {
.mq_flags = 0, // ignored by mq_open
.mq_maxmsg = 10,
.mq_msgsize = sizeof(int),
.mq_curmsgs = 0 // ignored by mq_open
};
write_descr = mq_open("/my_queue", O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR , &configuration);
if (write_descr == (mqd_t) -1) {
perror("Creating message queue failed");
abort();
}
read_descr = mq_open("/my_queue", O_RDONLY);
if (read_descr == (mqd_t) -1) {
perror("Opening message queue for reading failed");
abort();
}
int result;
result = pthread_create(&sender, NULL, sender_function, &write_descr);
if (result != 0) {
printf("Creating sender thread failed: %s", strerror(errno));
abort();
}
result = pthread_create(&receiver, NULL, receiver_function, &read_descr);
if (result != 0) {
printf("Creating receiver thread failed: %s", strerror(errno));
abort();
}
while (true) {
sleep(1000);
}
}

View File

@ -0,0 +1,13 @@
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: CC0-1.0
import pytest
from pytest_embedded import Dut
@pytest.mark.supported_targets
@pytest.mark.generic
def test_rt_mqueue_example(dut: Dut) -> None:
dut.expect_exact('sending: 0')
dut.expect_exact('received: 0')
dut.expect_exact('sending: 1')
dut.expect_exact('received: 1')

View File

@ -152,6 +152,8 @@ components_not_formatted_permanent:
- "/components/app_trace/sys_view/Config/" - "/components/app_trace/sys_view/Config/"
- "/components/app_trace/sys_view/Sample/" - "/components/app_trace/sys_view/Sample/"
- "/components/app_trace/sys_view/SEGGER/" - "/components/app_trace/sys_view/SEGGER/"
# FreeRTOS-Plux-POSIX files (upstream source code)
- "/components/rt/"
# SoC header files (generated) # SoC header files (generated)
- "/components/soc/*/include/soc/" - "/components/soc/*/include/soc/"
# Example resource files (generated) # Example resource files (generated)

View File

@ -111,6 +111,22 @@ linux_component:
- Apache-2.0 - Apache-2.0
- BSD-4-Clause-UC - BSD-4-Clause-UC
rt_component:
include:
- 'components/rt/**'
allowed_licenses:
- Apache-2.0
- MIT #FreeRTOS-Plux-POSIX sources and port files
rt_component_tests:
include:
- 'components/rt/test_apps/**'
allowed_licenses:
- Apache-2.0
- Unlicense
- CC0-1.0
license_for_new_files: Unlicense OR CC0-1.0
systemview: systemview:
include: include:
- 'components/app_trace/sys_view' - 'components/app_trace/sys_view'