mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
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:
parent
5448703663
commit
bddc1e95ef
@ -140,6 +140,7 @@
|
||||
/components/protocomm/ @esp-idf-codeowners/app-utilities/provisioning
|
||||
/components/pthread/ @esp-idf-codeowners/system
|
||||
/components/riscv/ @esp-idf-codeowners/system
|
||||
/components/rt/ @esp-idf-codeowners/system
|
||||
/components/sdmmc/ @esp-idf-codeowners/storage
|
||||
/components/soc/ @esp-idf-codeowners/peripherals @esp-idf-codeowners/system
|
||||
/components/spi_flash/ @esp-idf-codeowners/peripherals
|
||||
|
13
components/rt/CMakeLists.txt
Normal file
13
components/rt/CMakeLists.txt
Normal 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")
|
924
components/rt/FreeRTOS_POSIX_mqueue.c
Normal file
924
components/rt/FreeRTOS_POSIX_mqueue.c
Normal 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;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
249
components/rt/FreeRTOS_POSIX_utils.c
Normal file
249
components/rt/FreeRTOS_POSIX_utils.c
Normal 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;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
46
components/rt/idf_changes.md
Normal file
46
components/rt/idf_changes.md
Normal 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).
|
155
components/rt/include/mqueue.h
Normal file
155
components/rt/include/mqueue.h
Normal 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 <slash> 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
|
56
components/rt/private_include/FreeRTOS_POSIX.h
Normal file
56
components/rt/private_include/FreeRTOS_POSIX.h
Normal 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_ */
|
149
components/rt/private_include/FreeRTOS_POSIX/utils.h
Normal file
149
components/rt/private_include/FreeRTOS_POSIX/utils.h
Normal 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_ */
|
101
components/rt/private_include/FreeRTOS_POSIX_internal.h
Normal file
101
components/rt/private_include/FreeRTOS_POSIX_internal.h
Normal 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_ */
|
59
components/rt/private_include/FreeRTOS_POSIX_portable.h
Normal file
59
components/rt/private_include/FreeRTOS_POSIX_portable.h
Normal 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_ */
|
249
components/rt/private_include/aws_doubly_linked_list.h
Normal file
249
components/rt/private_include/aws_doubly_linked_list.h
Normal 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_ */
|
@ -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
5
components/rt/sbom.yml
Normal 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
|
@ -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
|
7
components/rt/test_apps/posix_rt_test/CMakeLists.txt
Normal file
7
components/rt/test_apps/posix_rt_test/CMakeLists.txt
Normal 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)
|
2
components/rt/test_apps/posix_rt_test/README.md
Normal file
2
components/rt/test_apps/posix_rt_test/README.md
Normal file
@ -0,0 +1,2 @@
|
||||
| Supported Targets | ESP32 | ESP32-C3 | ESP32-P4 | ESP32-S2 |
|
||||
| ----------------- | ----- | -------- | -------- | -------- |
|
@ -0,0 +1,4 @@
|
||||
idf_component_register(SRCS "main.c" "mqueue_test.cpp"
|
||||
INCLUDE_DIRS "."
|
||||
PRIV_REQUIRES rt unity
|
||||
WHOLE_ARCHIVE)
|
36
components/rt/test_apps/posix_rt_test/main/main.c
Normal file
36
components/rt/test_apps/posix_rt_test/main/main.c
Normal 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();
|
||||
}
|
288
components/rt/test_apps/posix_rt_test/main/mqueue_test.cpp
Normal file
288
components/rt/test_apps/posix_rt_test/main/mqueue_test.cpp
Normal 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);
|
||||
|
||||
}
|
@ -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()
|
2
components/rt/test_apps/posix_rt_test/sdkconfig.defaults
Normal file
2
components/rt/test_apps/posix_rt_test/sdkconfig.defaults
Normal file
@ -0,0 +1,2 @@
|
||||
# This "default" configuration is appended to all other configurations
|
||||
CONFIG_ESP_TASK_WDT_INIT=n
|
@ -1,5 +1,5 @@
|
||||
POSIX Threads Support
|
||||
=====================
|
||||
POSIX Support (Including POSIX Threads Support)
|
||||
===============================================
|
||||
|
||||
: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.
|
||||
|
||||
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
|
||||
----------------
|
||||
@ -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.
|
||||
|
||||
.. 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>`.
|
||||
|
||||
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`.
|
||||
|
||||
.. _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
|
||||
---------------
|
||||
|
||||
@ -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_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:
|
||||
|
||||
|
@ -236,6 +236,13 @@ examples/system/pthread:
|
||||
depends_components:
|
||||
- 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:
|
||||
disable:
|
||||
- if: IDF_TARGET != "esp32c3" and (NIGHTLY_RUN != "1" or IDF_TARGET == "linux")
|
||||
|
8
examples/system/rt_mqueue/CMakeLists.txt
Normal file
8
examples/system/rt_mqueue/CMakeLists.txt
Normal 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)
|
37
examples/system/rt_mqueue/README.md
Normal file
37
examples/system/rt_mqueue/README.md
Normal 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
|
||||
...
|
||||
```
|
3
examples/system/rt_mqueue/main/CMakeLists.txt
Normal file
3
examples/system/rt_mqueue/main/CMakeLists.txt
Normal file
@ -0,0 +1,3 @@
|
||||
idf_component_register(SRCS "posix_mqueue_example_main.c"
|
||||
INCLUDE_DIRS "."
|
||||
PRIV_REQUIRES rt)
|
101
examples/system/rt_mqueue/main/posix_mqueue_example_main.c
Normal file
101
examples/system/rt_mqueue/main/posix_mqueue_example_main.c
Normal 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);
|
||||
}
|
||||
}
|
13
examples/system/rt_mqueue/pytest_rt_mqueue.py
Normal file
13
examples/system/rt_mqueue/pytest_rt_mqueue.py
Normal 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')
|
@ -152,6 +152,8 @@ components_not_formatted_permanent:
|
||||
- "/components/app_trace/sys_view/Config/"
|
||||
- "/components/app_trace/sys_view/Sample/"
|
||||
- "/components/app_trace/sys_view/SEGGER/"
|
||||
# FreeRTOS-Plux-POSIX files (upstream source code)
|
||||
- "/components/rt/"
|
||||
# SoC header files (generated)
|
||||
- "/components/soc/*/include/soc/"
|
||||
# Example resource files (generated)
|
||||
|
@ -111,6 +111,22 @@ linux_component:
|
||||
- Apache-2.0
|
||||
- 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:
|
||||
include:
|
||||
- 'components/app_trace/sys_view'
|
||||
|
Loading…
Reference in New Issue
Block a user