/* * FreeRTOS Kernel * Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * SPDX-License-Identifier: MIT * * 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. * * https://www.FreeRTOS.org * https://github.com/FreeRTOS * */ /* Standard includes. */ #include /* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining * all the API functions to use the MPU wrappers. That should only be done when * task.h is included from an application file. */ #define MPU_WRAPPERS_INCLUDED_FROM_API_FILE /* FreeRTOS includes. */ #include "FreeRTOS.h" #include "task.h" #include "stream_buffer.h" #if ( configUSE_TASK_NOTIFICATIONS != 1 ) #error configUSE_TASK_NOTIFICATIONS must be set to 1 to build stream_buffer.c #endif #if ( INCLUDE_xTaskGetCurrentTaskHandle != 1 ) #error INCLUDE_xTaskGetCurrentTaskHandle must be set to 1 to build stream_buffer.c #endif /* The MPU ports require MPU_WRAPPERS_INCLUDED_FROM_API_FILE to be defined * for the header files above, but not in this file, in order to generate the * correct privileged Vs unprivileged linkage and placement. */ #undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE /* If the user has not provided application specific Rx notification macros, * or #defined the notification macros away, then provide default implementations * that uses task notifications. */ #ifndef sbRECEIVE_COMPLETED #define sbRECEIVE_COMPLETED( pxStreamBuffer ) \ do \ { \ vTaskSuspendAll(); \ { \ if( ( pxStreamBuffer )->xTaskWaitingToSend != NULL ) \ { \ ( void ) xTaskNotifyIndexed( ( pxStreamBuffer )->xTaskWaitingToSend, \ ( pxStreamBuffer )->uxNotificationIndex, \ ( uint32_t ) 0, \ eNoAction ); \ ( pxStreamBuffer )->xTaskWaitingToSend = NULL; \ } \ } \ ( void ) xTaskResumeAll(); \ } while( 0 ) #endif /* sbRECEIVE_COMPLETED */ /* If user has provided a per-instance receive complete callback, then * invoke the callback else use the receive complete macro which is provided by default for all instances. */ #if ( configUSE_SB_COMPLETED_CALLBACK == 1 ) #define prvRECEIVE_COMPLETED( pxStreamBuffer ) \ do { \ if( ( pxStreamBuffer )->pxReceiveCompletedCallback != NULL ) \ { \ ( pxStreamBuffer )->pxReceiveCompletedCallback( ( pxStreamBuffer ), pdFALSE, NULL ); \ } \ else \ { \ sbRECEIVE_COMPLETED( ( pxStreamBuffer ) ); \ } \ } while( 0 ) #else /* if ( configUSE_SB_COMPLETED_CALLBACK == 1 ) */ #define prvRECEIVE_COMPLETED( pxStreamBuffer ) sbRECEIVE_COMPLETED( ( pxStreamBuffer ) ) #endif /* if ( configUSE_SB_COMPLETED_CALLBACK == 1 ) */ #ifndef sbRECEIVE_COMPLETED_FROM_ISR #define sbRECEIVE_COMPLETED_FROM_ISR( pxStreamBuffer, \ pxHigherPriorityTaskWoken ) \ do { \ UBaseType_t uxSavedInterruptStatus; \ \ uxSavedInterruptStatus = taskENTER_CRITICAL_FROM_ISR(); \ { \ if( ( pxStreamBuffer )->xTaskWaitingToSend != NULL ) \ { \ ( void ) xTaskNotifyIndexedFromISR( ( pxStreamBuffer )->xTaskWaitingToSend, \ ( pxStreamBuffer )->uxNotificationIndex, \ ( uint32_t ) 0, \ eNoAction, \ ( pxHigherPriorityTaskWoken ) ); \ ( pxStreamBuffer )->xTaskWaitingToSend = NULL; \ } \ } \ taskEXIT_CRITICAL_FROM_ISR( uxSavedInterruptStatus ); \ } while( 0 ) #endif /* sbRECEIVE_COMPLETED_FROM_ISR */ #if ( configUSE_SB_COMPLETED_CALLBACK == 1 ) #define prvRECEIVE_COMPLETED_FROM_ISR( pxStreamBuffer, \ pxHigherPriorityTaskWoken ) \ do { \ if( ( pxStreamBuffer )->pxReceiveCompletedCallback != NULL ) \ { \ ( pxStreamBuffer )->pxReceiveCompletedCallback( ( pxStreamBuffer ), pdTRUE, ( pxHigherPriorityTaskWoken ) ); \ } \ else \ { \ sbRECEIVE_COMPLETED_FROM_ISR( ( pxStreamBuffer ), ( pxHigherPriorityTaskWoken ) ); \ } \ } while( 0 ) #else /* if ( configUSE_SB_COMPLETED_CALLBACK == 1 ) */ #define prvRECEIVE_COMPLETED_FROM_ISR( pxStreamBuffer, pxHigherPriorityTaskWoken ) \ sbRECEIVE_COMPLETED_FROM_ISR( ( pxStreamBuffer ), ( pxHigherPriorityTaskWoken ) ) #endif /* if ( configUSE_SB_COMPLETED_CALLBACK == 1 ) */ /* If the user has not provided an application specific Tx notification macro, * or #defined the notification macro away, then provide a default * implementation that uses task notifications. */ #ifndef sbSEND_COMPLETED #define sbSEND_COMPLETED( pxStreamBuffer ) \ vTaskSuspendAll(); \ { \ if( ( pxStreamBuffer )->xTaskWaitingToReceive != NULL ) \ { \ ( void ) xTaskNotifyIndexed( ( pxStreamBuffer )->xTaskWaitingToReceive, \ ( pxStreamBuffer )->uxNotificationIndex, \ ( uint32_t ) 0, \ eNoAction ); \ ( pxStreamBuffer )->xTaskWaitingToReceive = NULL; \ } \ } \ ( void ) xTaskResumeAll() #endif /* sbSEND_COMPLETED */ /* If user has provided a per-instance send completed callback, then * invoke the callback else use the send complete macro which is provided by default for all instances. */ #if ( configUSE_SB_COMPLETED_CALLBACK == 1 ) #define prvSEND_COMPLETED( pxStreamBuffer ) \ do { \ if( ( pxStreamBuffer )->pxSendCompletedCallback != NULL ) \ { \ ( pxStreamBuffer )->pxSendCompletedCallback( ( pxStreamBuffer ), pdFALSE, NULL ); \ } \ else \ { \ sbSEND_COMPLETED( ( pxStreamBuffer ) ); \ } \ } while( 0 ) #else /* if ( configUSE_SB_COMPLETED_CALLBACK == 1 ) */ #define prvSEND_COMPLETED( pxStreamBuffer ) sbSEND_COMPLETED( ( pxStreamBuffer ) ) #endif /* if ( configUSE_SB_COMPLETED_CALLBACK == 1 ) */ #ifndef sbSEND_COMPLETE_FROM_ISR #define sbSEND_COMPLETE_FROM_ISR( pxStreamBuffer, pxHigherPriorityTaskWoken ) \ do { \ UBaseType_t uxSavedInterruptStatus; \ \ uxSavedInterruptStatus = taskENTER_CRITICAL_FROM_ISR(); \ { \ if( ( pxStreamBuffer )->xTaskWaitingToReceive != NULL ) \ { \ ( void ) xTaskNotifyIndexedFromISR( ( pxStreamBuffer )->xTaskWaitingToReceive, \ ( pxStreamBuffer )->uxNotificationIndex, \ ( uint32_t ) 0, \ eNoAction, \ ( pxHigherPriorityTaskWoken ) ); \ ( pxStreamBuffer )->xTaskWaitingToReceive = NULL; \ } \ } \ taskEXIT_CRITICAL_FROM_ISR( uxSavedInterruptStatus ); \ } while( 0 ) #endif /* sbSEND_COMPLETE_FROM_ISR */ #if ( configUSE_SB_COMPLETED_CALLBACK == 1 ) #define prvSEND_COMPLETE_FROM_ISR( pxStreamBuffer, pxHigherPriorityTaskWoken ) \ do { \ if( ( pxStreamBuffer )->pxSendCompletedCallback != NULL ) \ { \ ( pxStreamBuffer )->pxSendCompletedCallback( ( pxStreamBuffer ), pdTRUE, ( pxHigherPriorityTaskWoken ) ); \ } \ else \ { \ sbSEND_COMPLETE_FROM_ISR( ( pxStreamBuffer ), ( pxHigherPriorityTaskWoken ) ); \ } \ } while( 0 ) #else /* if ( configUSE_SB_COMPLETED_CALLBACK == 1 ) */ #define prvSEND_COMPLETE_FROM_ISR( pxStreamBuffer, pxHigherPriorityTaskWoken ) \ sbSEND_COMPLETE_FROM_ISR( ( pxStreamBuffer ), ( pxHigherPriorityTaskWoken ) ) #endif /* if ( configUSE_SB_COMPLETED_CALLBACK == 1 ) */ /* The number of bytes used to hold the length of a message in the buffer. */ #define sbBYTES_TO_STORE_MESSAGE_LENGTH ( sizeof( configMESSAGE_BUFFER_LENGTH_TYPE ) ) /* Bits stored in the ucFlags field of the stream buffer. */ #define sbFLAGS_IS_MESSAGE_BUFFER ( ( uint8_t ) 1 ) /* Set if the stream buffer was created as a message buffer, in which case it holds discrete messages rather than a stream. */ #define sbFLAGS_IS_STATICALLY_ALLOCATED ( ( uint8_t ) 2 ) /* Set if the stream buffer was created using statically allocated memory. */ /*-----------------------------------------------------------*/ /* Structure that hold state information on the buffer. */ typedef struct StreamBufferDef_t { volatile size_t xTail; /* Index to the next item to read within the buffer. */ volatile size_t xHead; /* Index to the next item to write within the buffer. */ size_t xLength; /* The length of the buffer pointed to by pucBuffer. */ size_t xTriggerLevelBytes; /* The number of bytes that must be in the stream buffer before a task that is waiting for data is unblocked. */ volatile TaskHandle_t xTaskWaitingToReceive; /* Holds the handle of a task waiting for data, or NULL if no tasks are waiting. */ volatile TaskHandle_t xTaskWaitingToSend; /* Holds the handle of a task waiting to send data to a message buffer that is full. */ uint8_t * pucBuffer; /* Points to the buffer itself - that is - the RAM that stores the data passed through the buffer. */ uint8_t ucFlags; #if ( configUSE_TRACE_FACILITY == 1 ) UBaseType_t uxStreamBufferNumber; /* Used for tracing purposes. */ #endif #if ( configUSE_SB_COMPLETED_CALLBACK == 1 ) StreamBufferCallbackFunction_t pxSendCompletedCallback; /* Optional callback called on send complete. sbSEND_COMPLETED is called if this is NULL. */ StreamBufferCallbackFunction_t pxReceiveCompletedCallback; /* Optional callback called on receive complete. sbRECEIVE_COMPLETED is called if this is NULL. */ #endif UBaseType_t uxNotificationIndex; /* The index we are using for notification, by default tskDEFAULT_INDEX_TO_NOTIFY. */ } StreamBuffer_t; /* * The number of bytes available to be read from the buffer. */ static size_t prvBytesInBuffer( const StreamBuffer_t * const pxStreamBuffer ) PRIVILEGED_FUNCTION; /* * Add xCount bytes from pucData into the pxStreamBuffer's data storage area. * This function does not update the buffer's xHead pointer, so multiple writes * may be chained together "atomically". This is useful for Message Buffers where * the length and data bytes are written in two separate chunks, and we don't want * the reader to see the buffer as having grown until after all data is copied over. * This function takes a custom xHead value to indicate where to write to (necessary * for chaining) and returns the the resulting xHead position. * To mark the write as complete, manually set the buffer's xHead field with the * returned xHead from this function. */ static size_t prvWriteBytesToBuffer( StreamBuffer_t * const pxStreamBuffer, const uint8_t * pucData, size_t xCount, size_t xHead ) PRIVILEGED_FUNCTION; /* * If the stream buffer is being used as a message buffer, then reads an entire * message out of the buffer. If the stream buffer is being used as a stream * buffer then read as many bytes as possible from the buffer. * prvReadBytesFromBuffer() is called to actually extract the bytes from the * buffer's data storage area. */ static size_t prvReadMessageFromBuffer( StreamBuffer_t * pxStreamBuffer, void * pvRxData, size_t xBufferLengthBytes, size_t xBytesAvailable ) PRIVILEGED_FUNCTION; /* * If the stream buffer is being used as a message buffer, then writes an entire * message to the buffer. If the stream buffer is being used as a stream * buffer then write as many bytes as possible to the buffer. * prvWriteBytestoBuffer() is called to actually send the bytes to the buffer's * data storage area. */ static size_t prvWriteMessageToBuffer( StreamBuffer_t * const pxStreamBuffer, const void * pvTxData, size_t xDataLengthBytes, size_t xSpace, size_t xRequiredSpace ) PRIVILEGED_FUNCTION; /* * Copies xCount bytes from the pxStreamBuffer's data storage area to pucData. * This function does not update the buffer's xTail pointer, so multiple reads * may be chained together "atomically". This is useful for Message Buffers where * the length and data bytes are read in two separate chunks, and we don't want * the writer to see the buffer as having more free space until after all data is * copied over, especially if we have to abort the read due to insufficient receiving space. * This function takes a custom xTail value to indicate where to read from (necessary * for chaining) and returns the the resulting xTail position. * To mark the read as complete, manually set the buffer's xTail field with the * returned xTail from this function. */ static size_t prvReadBytesFromBuffer( StreamBuffer_t * pxStreamBuffer, uint8_t * pucData, size_t xCount, size_t xTail ) PRIVILEGED_FUNCTION; /* * Called by both pxStreamBufferCreate() and pxStreamBufferCreateStatic() to * initialise the members of the newly created stream buffer structure. */ static void prvInitialiseNewStreamBuffer( StreamBuffer_t * const pxStreamBuffer, uint8_t * const pucBuffer, size_t xBufferSizeBytes, size_t xTriggerLevelBytes, uint8_t ucFlags, StreamBufferCallbackFunction_t pxSendCompletedCallback, StreamBufferCallbackFunction_t pxReceiveCompletedCallback ) PRIVILEGED_FUNCTION; /*-----------------------------------------------------------*/ #if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) StreamBufferHandle_t xStreamBufferGenericCreate( size_t xBufferSizeBytes, size_t xTriggerLevelBytes, BaseType_t xIsMessageBuffer, StreamBufferCallbackFunction_t pxSendCompletedCallback, StreamBufferCallbackFunction_t pxReceiveCompletedCallback ) { void * pvAllocatedMemory; uint8_t ucFlags; traceENTER_xStreamBufferGenericCreate( xBufferSizeBytes, xTriggerLevelBytes, xIsMessageBuffer, pxSendCompletedCallback, pxReceiveCompletedCallback ); /* In case the stream buffer is going to be used as a message buffer * (that is, it will hold discrete messages with a little meta data that * says how big the next message is) check the buffer will be large enough * to hold at least one message. */ if( xIsMessageBuffer == pdTRUE ) { /* Is a message buffer but not statically allocated. */ ucFlags = sbFLAGS_IS_MESSAGE_BUFFER; configASSERT( xBufferSizeBytes > sbBYTES_TO_STORE_MESSAGE_LENGTH ); } else { /* Not a message buffer and not statically allocated. */ ucFlags = 0; configASSERT( xBufferSizeBytes > 0 ); } configASSERT( xTriggerLevelBytes <= xBufferSizeBytes ); /* A trigger level of 0 would cause a waiting task to unblock even when * the buffer was empty. */ if( xTriggerLevelBytes == ( size_t ) 0 ) { xTriggerLevelBytes = ( size_t ) 1; } /* A stream buffer requires a StreamBuffer_t structure and a buffer. * Both are allocated in a single call to pvPortMalloc(). The * StreamBuffer_t structure is placed at the start of the allocated memory * and the buffer follows immediately after. The requested size is * incremented so the free space is returned as the user would expect - * this is a quirk of the implementation that means otherwise the free * space would be reported as one byte smaller than would be logically * expected. */ if( xBufferSizeBytes < ( xBufferSizeBytes + 1U + sizeof( StreamBuffer_t ) ) ) { xBufferSizeBytes++; pvAllocatedMemory = pvPortMalloc( xBufferSizeBytes + sizeof( StreamBuffer_t ) ); } else { pvAllocatedMemory = NULL; } if( pvAllocatedMemory != NULL ) { /* MISRA Ref 11.5.1 [Malloc memory assignment] */ /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-115 */ /* coverity[misra_c_2012_rule_11_5_violation] */ prvInitialiseNewStreamBuffer( ( StreamBuffer_t * ) pvAllocatedMemory, /* Structure at the start of the allocated memory. */ /* MISRA Ref 11.5.1 [Malloc memory assignment] */ /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-115 */ /* coverity[misra_c_2012_rule_11_5_violation] */ ( ( uint8_t * ) pvAllocatedMemory ) + sizeof( StreamBuffer_t ), /* Storage area follows. */ xBufferSizeBytes, xTriggerLevelBytes, ucFlags, pxSendCompletedCallback, pxReceiveCompletedCallback ); traceSTREAM_BUFFER_CREATE( ( ( StreamBuffer_t * ) pvAllocatedMemory ), xIsMessageBuffer ); } else { traceSTREAM_BUFFER_CREATE_FAILED( xIsMessageBuffer ); } traceRETURN_xStreamBufferGenericCreate( pvAllocatedMemory ); /* MISRA Ref 11.5.1 [Malloc memory assignment] */ /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-115 */ /* coverity[misra_c_2012_rule_11_5_violation] */ return ( StreamBufferHandle_t ) pvAllocatedMemory; } #endif /* configSUPPORT_DYNAMIC_ALLOCATION */ /*-----------------------------------------------------------*/ #if ( configSUPPORT_STATIC_ALLOCATION == 1 ) StreamBufferHandle_t xStreamBufferGenericCreateStatic( size_t xBufferSizeBytes, size_t xTriggerLevelBytes, BaseType_t xIsMessageBuffer, uint8_t * const pucStreamBufferStorageArea, StaticStreamBuffer_t * const pxStaticStreamBuffer, StreamBufferCallbackFunction_t pxSendCompletedCallback, StreamBufferCallbackFunction_t pxReceiveCompletedCallback ) { /* MISRA Ref 11.3.1 [Misaligned access] */ /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-113 */ /* coverity[misra_c_2012_rule_11_3_violation] */ StreamBuffer_t * const pxStreamBuffer = ( StreamBuffer_t * ) pxStaticStreamBuffer; StreamBufferHandle_t xReturn; uint8_t ucFlags; traceENTER_xStreamBufferGenericCreateStatic( xBufferSizeBytes, xTriggerLevelBytes, xIsMessageBuffer, pucStreamBufferStorageArea, pxStaticStreamBuffer, pxSendCompletedCallback, pxReceiveCompletedCallback ); configASSERT( pucStreamBufferStorageArea ); configASSERT( pxStaticStreamBuffer ); configASSERT( xTriggerLevelBytes <= xBufferSizeBytes ); /* A trigger level of 0 would cause a waiting task to unblock even when * the buffer was empty. */ if( xTriggerLevelBytes == ( size_t ) 0 ) { xTriggerLevelBytes = ( size_t ) 1; } /* In case the stream buffer is going to be used as a message buffer * (that is, it will hold discrete messages with a little meta data that * says how big the next message is) check the buffer will be large enough * to hold at least one message. */ if( xIsMessageBuffer != pdFALSE ) { /* Statically allocated message buffer. */ ucFlags = sbFLAGS_IS_MESSAGE_BUFFER | sbFLAGS_IS_STATICALLY_ALLOCATED; configASSERT( xBufferSizeBytes > sbBYTES_TO_STORE_MESSAGE_LENGTH ); } else { /* Statically allocated stream buffer. */ ucFlags = sbFLAGS_IS_STATICALLY_ALLOCATED; } #if ( configASSERT_DEFINED == 1 ) { /* Sanity check that the size of the structure used to declare a * variable of type StaticStreamBuffer_t equals the size of the real * message buffer structure. */ volatile size_t xSize = sizeof( StaticStreamBuffer_t ); configASSERT( xSize == sizeof( StreamBuffer_t ) ); } #endif /* configASSERT_DEFINED */ if( ( pucStreamBufferStorageArea != NULL ) && ( pxStaticStreamBuffer != NULL ) ) { prvInitialiseNewStreamBuffer( pxStreamBuffer, pucStreamBufferStorageArea, xBufferSizeBytes, xTriggerLevelBytes, ucFlags, pxSendCompletedCallback, pxReceiveCompletedCallback ); /* Remember this was statically allocated in case it is ever deleted * again. */ pxStreamBuffer->ucFlags |= sbFLAGS_IS_STATICALLY_ALLOCATED; traceSTREAM_BUFFER_CREATE( pxStreamBuffer, xIsMessageBuffer ); /* MISRA Ref 11.3.1 [Misaligned access] */ /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-113 */ /* coverity[misra_c_2012_rule_11_3_violation] */ xReturn = ( StreamBufferHandle_t ) pxStaticStreamBuffer; } else { xReturn = NULL; traceSTREAM_BUFFER_CREATE_STATIC_FAILED( xReturn, xIsMessageBuffer ); } traceRETURN_xStreamBufferGenericCreateStatic( xReturn ); return xReturn; } #endif /* ( configSUPPORT_STATIC_ALLOCATION == 1 ) */ /*-----------------------------------------------------------*/ #if ( configSUPPORT_STATIC_ALLOCATION == 1 ) BaseType_t xStreamBufferGetStaticBuffers( StreamBufferHandle_t xStreamBuffer, uint8_t ** ppucStreamBufferStorageArea, StaticStreamBuffer_t ** ppxStaticStreamBuffer ) { BaseType_t xReturn; StreamBuffer_t * const pxStreamBuffer = xStreamBuffer; traceENTER_xStreamBufferGetStaticBuffers( xStreamBuffer, ppucStreamBufferStorageArea, ppxStaticStreamBuffer ); configASSERT( pxStreamBuffer ); configASSERT( ppucStreamBufferStorageArea ); configASSERT( ppxStaticStreamBuffer ); if( ( pxStreamBuffer->ucFlags & sbFLAGS_IS_STATICALLY_ALLOCATED ) != ( uint8_t ) 0 ) { *ppucStreamBufferStorageArea = pxStreamBuffer->pucBuffer; /* MISRA Ref 11.3.1 [Misaligned access] */ /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-113 */ /* coverity[misra_c_2012_rule_11_3_violation] */ *ppxStaticStreamBuffer = ( StaticStreamBuffer_t * ) pxStreamBuffer; xReturn = pdTRUE; } else { xReturn = pdFALSE; } traceRETURN_xStreamBufferGetStaticBuffers( xReturn ); return xReturn; } #endif /* configSUPPORT_STATIC_ALLOCATION */ /*-----------------------------------------------------------*/ void vStreamBufferDelete( StreamBufferHandle_t xStreamBuffer ) { StreamBuffer_t * pxStreamBuffer = xStreamBuffer; traceENTER_vStreamBufferDelete( xStreamBuffer ); configASSERT( pxStreamBuffer ); traceSTREAM_BUFFER_DELETE( xStreamBuffer ); if( ( pxStreamBuffer->ucFlags & sbFLAGS_IS_STATICALLY_ALLOCATED ) == ( uint8_t ) pdFALSE ) { #if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) { /* Both the structure and the buffer were allocated using a single call * to pvPortMalloc(), hence only one call to vPortFree() is required. */ vPortFree( ( void * ) pxStreamBuffer ); } #else { /* Should not be possible to get here, ucFlags must be corrupt. * Force an assert. */ configASSERT( xStreamBuffer == ( StreamBufferHandle_t ) ~0 ); } #endif } else { /* The structure and buffer were not allocated dynamically and cannot be * freed - just scrub the structure so future use will assert. */ ( void ) memset( pxStreamBuffer, 0x00, sizeof( StreamBuffer_t ) ); } traceRETURN_vStreamBufferDelete(); } /*-----------------------------------------------------------*/ BaseType_t xStreamBufferReset( StreamBufferHandle_t xStreamBuffer ) { StreamBuffer_t * const pxStreamBuffer = xStreamBuffer; BaseType_t xReturn = pdFAIL; StreamBufferCallbackFunction_t pxSendCallback = NULL, pxReceiveCallback = NULL; #if ( configUSE_TRACE_FACILITY == 1 ) UBaseType_t uxStreamBufferNumber; #endif traceENTER_xStreamBufferReset( xStreamBuffer ); configASSERT( pxStreamBuffer ); #if ( configUSE_TRACE_FACILITY == 1 ) { /* Store the stream buffer number so it can be restored after the * reset. */ uxStreamBufferNumber = pxStreamBuffer->uxStreamBufferNumber; } #endif /* Can only reset a message buffer if there are no tasks blocked on it. */ taskENTER_CRITICAL(); { if( ( pxStreamBuffer->xTaskWaitingToReceive == NULL ) && ( pxStreamBuffer->xTaskWaitingToSend == NULL ) ) { #if ( configUSE_SB_COMPLETED_CALLBACK == 1 ) { pxSendCallback = pxStreamBuffer->pxSendCompletedCallback; pxReceiveCallback = pxStreamBuffer->pxReceiveCompletedCallback; } #endif prvInitialiseNewStreamBuffer( pxStreamBuffer, pxStreamBuffer->pucBuffer, pxStreamBuffer->xLength, pxStreamBuffer->xTriggerLevelBytes, pxStreamBuffer->ucFlags, pxSendCallback, pxReceiveCallback ); #if ( configUSE_TRACE_FACILITY == 1 ) { pxStreamBuffer->uxStreamBufferNumber = uxStreamBufferNumber; } #endif traceSTREAM_BUFFER_RESET( xStreamBuffer ); xReturn = pdPASS; } } taskEXIT_CRITICAL(); traceRETURN_xStreamBufferReset( xReturn ); return xReturn; } /*-----------------------------------------------------------*/ BaseType_t xStreamBufferSetTriggerLevel( StreamBufferHandle_t xStreamBuffer, size_t xTriggerLevel ) { StreamBuffer_t * const pxStreamBuffer = xStreamBuffer; BaseType_t xReturn; traceENTER_xStreamBufferSetTriggerLevel( xStreamBuffer, xTriggerLevel ); configASSERT( pxStreamBuffer ); /* It is not valid for the trigger level to be 0. */ if( xTriggerLevel == ( size_t ) 0 ) { xTriggerLevel = ( size_t ) 1; } /* The trigger level is the number of bytes that must be in the stream * buffer before a task that is waiting for data is unblocked. */ if( xTriggerLevel < pxStreamBuffer->xLength ) { pxStreamBuffer->xTriggerLevelBytes = xTriggerLevel; xReturn = pdPASS; } else { xReturn = pdFALSE; } traceRETURN_xStreamBufferSetTriggerLevel( xReturn ); return xReturn; } /*-----------------------------------------------------------*/ size_t xStreamBufferSpacesAvailable( StreamBufferHandle_t xStreamBuffer ) { const StreamBuffer_t * const pxStreamBuffer = xStreamBuffer; size_t xSpace; size_t xOriginalTail; traceENTER_xStreamBufferSpacesAvailable( xStreamBuffer ); configASSERT( pxStreamBuffer ); /* The code below reads xTail and then xHead. This is safe if the stream * buffer is updated once between the two reads - but not if the stream buffer * is updated more than once between the two reads - hence the loop. */ do { xOriginalTail = pxStreamBuffer->xTail; xSpace = pxStreamBuffer->xLength + pxStreamBuffer->xTail; xSpace -= pxStreamBuffer->xHead; } while( xOriginalTail != pxStreamBuffer->xTail ); xSpace -= ( size_t ) 1; if( xSpace >= pxStreamBuffer->xLength ) { xSpace -= pxStreamBuffer->xLength; } else { mtCOVERAGE_TEST_MARKER(); } traceRETURN_xStreamBufferSpacesAvailable( xSpace ); return xSpace; } /*-----------------------------------------------------------*/ size_t xStreamBufferBytesAvailable( StreamBufferHandle_t xStreamBuffer ) { const StreamBuffer_t * const pxStreamBuffer = xStreamBuffer; size_t xReturn; traceENTER_xStreamBufferBytesAvailable( xStreamBuffer ); configASSERT( pxStreamBuffer ); xReturn = prvBytesInBuffer( pxStreamBuffer ); traceRETURN_xStreamBufferBytesAvailable( xReturn ); return xReturn; } /*-----------------------------------------------------------*/ size_t xStreamBufferSend( StreamBufferHandle_t xStreamBuffer, const void * pvTxData, size_t xDataLengthBytes, TickType_t xTicksToWait ) { StreamBuffer_t * const pxStreamBuffer = xStreamBuffer; size_t xReturn, xSpace = 0; size_t xRequiredSpace = xDataLengthBytes; TimeOut_t xTimeOut; size_t xMaxReportedSpace = 0; traceENTER_xStreamBufferSend( xStreamBuffer, pvTxData, xDataLengthBytes, xTicksToWait ); configASSERT( pvTxData ); configASSERT( pxStreamBuffer ); /* The maximum amount of space a stream buffer will ever report is its length * minus 1. */ xMaxReportedSpace = pxStreamBuffer->xLength - ( size_t ) 1; /* This send function is used to write to both message buffers and stream * buffers. If this is a message buffer then the space needed must be * increased by the amount of bytes needed to store the length of the * message. */ if( ( pxStreamBuffer->ucFlags & sbFLAGS_IS_MESSAGE_BUFFER ) != ( uint8_t ) 0 ) { xRequiredSpace += sbBYTES_TO_STORE_MESSAGE_LENGTH; /* Overflow? */ configASSERT( xRequiredSpace > xDataLengthBytes ); /* If this is a message buffer then it must be possible to write the * whole message. */ if( xRequiredSpace > xMaxReportedSpace ) { /* The message would not fit even if the entire buffer was empty, * so don't wait for space. */ xTicksToWait = ( TickType_t ) 0; } else { mtCOVERAGE_TEST_MARKER(); } } else { /* If this is a stream buffer then it is acceptable to write only part * of the message to the buffer. Cap the length to the total length of * the buffer. */ if( xRequiredSpace > xMaxReportedSpace ) { xRequiredSpace = xMaxReportedSpace; } else { mtCOVERAGE_TEST_MARKER(); } } if( xTicksToWait != ( TickType_t ) 0 ) { vTaskSetTimeOutState( &xTimeOut ); do { /* Wait until the required number of bytes are free in the message * buffer. */ taskENTER_CRITICAL(); { xSpace = xStreamBufferSpacesAvailable( pxStreamBuffer ); if( xSpace < xRequiredSpace ) { /* Clear notification state as going to wait for space. */ ( void ) xTaskNotifyStateClearIndexed( NULL, pxStreamBuffer->uxNotificationIndex ); /* Should only be one writer. */ configASSERT( pxStreamBuffer->xTaskWaitingToSend == NULL ); pxStreamBuffer->xTaskWaitingToSend = xTaskGetCurrentTaskHandle(); } else { taskEXIT_CRITICAL(); break; } } taskEXIT_CRITICAL(); traceBLOCKING_ON_STREAM_BUFFER_SEND( xStreamBuffer ); ( void ) xTaskNotifyWaitIndexed( pxStreamBuffer->uxNotificationIndex, ( uint32_t ) 0, ( uint32_t ) 0, NULL, xTicksToWait ); pxStreamBuffer->xTaskWaitingToSend = NULL; } while( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ); } else { mtCOVERAGE_TEST_MARKER(); } if( xSpace == ( size_t ) 0 ) { xSpace = xStreamBufferSpacesAvailable( pxStreamBuffer ); } else { mtCOVERAGE_TEST_MARKER(); } xReturn = prvWriteMessageToBuffer( pxStreamBuffer, pvTxData, xDataLengthBytes, xSpace, xRequiredSpace ); if( xReturn > ( size_t ) 0 ) { traceSTREAM_BUFFER_SEND( xStreamBuffer, xReturn ); /* Was a task waiting for the data? */ if( prvBytesInBuffer( pxStreamBuffer ) >= pxStreamBuffer->xTriggerLevelBytes ) { prvSEND_COMPLETED( pxStreamBuffer ); } else { mtCOVERAGE_TEST_MARKER(); } } else { mtCOVERAGE_TEST_MARKER(); traceSTREAM_BUFFER_SEND_FAILED( xStreamBuffer ); } traceRETURN_xStreamBufferSend( xReturn ); return xReturn; } /*-----------------------------------------------------------*/ size_t xStreamBufferSendFromISR( StreamBufferHandle_t xStreamBuffer, const void * pvTxData, size_t xDataLengthBytes, BaseType_t * const pxHigherPriorityTaskWoken ) { StreamBuffer_t * const pxStreamBuffer = xStreamBuffer; size_t xReturn, xSpace; size_t xRequiredSpace = xDataLengthBytes; traceENTER_xStreamBufferSendFromISR( xStreamBuffer, pvTxData, xDataLengthBytes, pxHigherPriorityTaskWoken ); configASSERT( pvTxData ); configASSERT( pxStreamBuffer ); /* This send function is used to write to both message buffers and stream * buffers. If this is a message buffer then the space needed must be * increased by the amount of bytes needed to store the length of the * message. */ if( ( pxStreamBuffer->ucFlags & sbFLAGS_IS_MESSAGE_BUFFER ) != ( uint8_t ) 0 ) { xRequiredSpace += sbBYTES_TO_STORE_MESSAGE_LENGTH; } else { mtCOVERAGE_TEST_MARKER(); } xSpace = xStreamBufferSpacesAvailable( pxStreamBuffer ); xReturn = prvWriteMessageToBuffer( pxStreamBuffer, pvTxData, xDataLengthBytes, xSpace, xRequiredSpace ); if( xReturn > ( size_t ) 0 ) { /* Was a task waiting for the data? */ if( prvBytesInBuffer( pxStreamBuffer ) >= pxStreamBuffer->xTriggerLevelBytes ) { prvSEND_COMPLETE_FROM_ISR( pxStreamBuffer, pxHigherPriorityTaskWoken ); } else { mtCOVERAGE_TEST_MARKER(); } } else { mtCOVERAGE_TEST_MARKER(); } traceSTREAM_BUFFER_SEND_FROM_ISR( xStreamBuffer, xReturn ); traceRETURN_xStreamBufferSendFromISR( xReturn ); return xReturn; } /*-----------------------------------------------------------*/ static size_t prvWriteMessageToBuffer( StreamBuffer_t * const pxStreamBuffer, const void * pvTxData, size_t xDataLengthBytes, size_t xSpace, size_t xRequiredSpace ) { size_t xNextHead = pxStreamBuffer->xHead; configMESSAGE_BUFFER_LENGTH_TYPE xMessageLength; if( ( pxStreamBuffer->ucFlags & sbFLAGS_IS_MESSAGE_BUFFER ) != ( uint8_t ) 0 ) { /* This is a message buffer, as opposed to a stream buffer. */ /* Convert xDataLengthBytes to the message length type. */ xMessageLength = ( configMESSAGE_BUFFER_LENGTH_TYPE ) xDataLengthBytes; /* Ensure the data length given fits within configMESSAGE_BUFFER_LENGTH_TYPE. */ configASSERT( ( size_t ) xMessageLength == xDataLengthBytes ); if( xSpace >= xRequiredSpace ) { /* There is enough space to write both the message length and the message * itself into the buffer. Start by writing the length of the data, the data * itself will be written later in this function. */ xNextHead = prvWriteBytesToBuffer( pxStreamBuffer, ( const uint8_t * ) &( xMessageLength ), sbBYTES_TO_STORE_MESSAGE_LENGTH, xNextHead ); } else { /* Not enough space, so do not write data to the buffer. */ xDataLengthBytes = 0; } } else { /* This is a stream buffer, as opposed to a message buffer, so writing a * stream of bytes rather than discrete messages. Plan to write as many * bytes as possible. */ xDataLengthBytes = configMIN( xDataLengthBytes, xSpace ); } if( xDataLengthBytes != ( size_t ) 0 ) { /* Write the data to the buffer. */ /* MISRA Ref 11.5.5 [Void pointer assignment] */ /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-115 */ /* coverity[misra_c_2012_rule_11_5_violation] */ pxStreamBuffer->xHead = prvWriteBytesToBuffer( pxStreamBuffer, ( const uint8_t * ) pvTxData, xDataLengthBytes, xNextHead ); } return xDataLengthBytes; } /*-----------------------------------------------------------*/ size_t xStreamBufferReceive( StreamBufferHandle_t xStreamBuffer, void * pvRxData, size_t xBufferLengthBytes, TickType_t xTicksToWait ) { StreamBuffer_t * const pxStreamBuffer = xStreamBuffer; size_t xReceivedLength = 0, xBytesAvailable, xBytesToStoreMessageLength; traceENTER_xStreamBufferReceive( xStreamBuffer, pvRxData, xBufferLengthBytes, xTicksToWait ); configASSERT( pvRxData ); configASSERT( pxStreamBuffer ); /* This receive function is used by both message buffers, which store * discrete messages, and stream buffers, which store a continuous stream of * bytes. Discrete messages include an additional * sbBYTES_TO_STORE_MESSAGE_LENGTH bytes that hold the length of the * message. */ if( ( pxStreamBuffer->ucFlags & sbFLAGS_IS_MESSAGE_BUFFER ) != ( uint8_t ) 0 ) { xBytesToStoreMessageLength = sbBYTES_TO_STORE_MESSAGE_LENGTH; } else { xBytesToStoreMessageLength = 0; } if( xTicksToWait != ( TickType_t ) 0 ) { /* Checking if there is data and clearing the notification state must be * performed atomically. */ taskENTER_CRITICAL(); { xBytesAvailable = prvBytesInBuffer( pxStreamBuffer ); /* If this function was invoked by a message buffer read then * xBytesToStoreMessageLength holds the number of bytes used to hold * the length of the next discrete message. If this function was * invoked by a stream buffer read then xBytesToStoreMessageLength will * be 0. */ if( xBytesAvailable <= xBytesToStoreMessageLength ) { /* Clear notification state as going to wait for data. */ ( void ) xTaskNotifyStateClearIndexed( NULL, pxStreamBuffer->uxNotificationIndex ); /* Should only be one reader. */ configASSERT( pxStreamBuffer->xTaskWaitingToReceive == NULL ); pxStreamBuffer->xTaskWaitingToReceive = xTaskGetCurrentTaskHandle(); } else { mtCOVERAGE_TEST_MARKER(); } } taskEXIT_CRITICAL(); if( xBytesAvailable <= xBytesToStoreMessageLength ) { /* Wait for data to be available. */ traceBLOCKING_ON_STREAM_BUFFER_RECEIVE( xStreamBuffer ); ( void ) xTaskNotifyWaitIndexed( pxStreamBuffer->uxNotificationIndex, ( uint32_t ) 0, ( uint32_t ) 0, NULL, xTicksToWait ); pxStreamBuffer->xTaskWaitingToReceive = NULL; /* Recheck the data available after blocking. */ xBytesAvailable = prvBytesInBuffer( pxStreamBuffer ); } else { mtCOVERAGE_TEST_MARKER(); } } else { xBytesAvailable = prvBytesInBuffer( pxStreamBuffer ); } /* Whether receiving a discrete message (where xBytesToStoreMessageLength * holds the number of bytes used to store the message length) or a stream of * bytes (where xBytesToStoreMessageLength is zero), the number of bytes * available must be greater than xBytesToStoreMessageLength to be able to * read bytes from the buffer. */ if( xBytesAvailable > xBytesToStoreMessageLength ) { xReceivedLength = prvReadMessageFromBuffer( pxStreamBuffer, pvRxData, xBufferLengthBytes, xBytesAvailable ); /* Was a task waiting for space in the buffer? */ if( xReceivedLength != ( size_t ) 0 ) { traceSTREAM_BUFFER_RECEIVE( xStreamBuffer, xReceivedLength ); prvRECEIVE_COMPLETED( xStreamBuffer ); } else { mtCOVERAGE_TEST_MARKER(); } } else { traceSTREAM_BUFFER_RECEIVE_FAILED( xStreamBuffer ); mtCOVERAGE_TEST_MARKER(); } traceRETURN_xStreamBufferReceive( xReceivedLength ); return xReceivedLength; } /*-----------------------------------------------------------*/ size_t xStreamBufferNextMessageLengthBytes( StreamBufferHandle_t xStreamBuffer ) { StreamBuffer_t * const pxStreamBuffer = xStreamBuffer; size_t xReturn, xBytesAvailable; configMESSAGE_BUFFER_LENGTH_TYPE xTempReturn; traceENTER_xStreamBufferNextMessageLengthBytes( xStreamBuffer ); configASSERT( pxStreamBuffer ); /* Ensure the stream buffer is being used as a message buffer. */ if( ( pxStreamBuffer->ucFlags & sbFLAGS_IS_MESSAGE_BUFFER ) != ( uint8_t ) 0 ) { xBytesAvailable = prvBytesInBuffer( pxStreamBuffer ); if( xBytesAvailable > sbBYTES_TO_STORE_MESSAGE_LENGTH ) { /* The number of bytes available is greater than the number of bytes * required to hold the length of the next message, so another message * is available. */ ( void ) prvReadBytesFromBuffer( pxStreamBuffer, ( uint8_t * ) &xTempReturn, sbBYTES_TO_STORE_MESSAGE_LENGTH, pxStreamBuffer->xTail ); xReturn = ( size_t ) xTempReturn; } else { /* The minimum amount of bytes in a message buffer is * ( sbBYTES_TO_STORE_MESSAGE_LENGTH + 1 ), so if xBytesAvailable is * less than sbBYTES_TO_STORE_MESSAGE_LENGTH the only other valid * value is 0. */ configASSERT( xBytesAvailable == 0 ); xReturn = 0; } } else { xReturn = 0; } traceRETURN_xStreamBufferNextMessageLengthBytes( xReturn ); return xReturn; } /*-----------------------------------------------------------*/ size_t xStreamBufferReceiveFromISR( StreamBufferHandle_t xStreamBuffer, void * pvRxData, size_t xBufferLengthBytes, BaseType_t * const pxHigherPriorityTaskWoken ) { StreamBuffer_t * const pxStreamBuffer = xStreamBuffer; size_t xReceivedLength = 0, xBytesAvailable, xBytesToStoreMessageLength; traceENTER_xStreamBufferReceiveFromISR( xStreamBuffer, pvRxData, xBufferLengthBytes, pxHigherPriorityTaskWoken ); configASSERT( pvRxData ); configASSERT( pxStreamBuffer ); /* This receive function is used by both message buffers, which store * discrete messages, and stream buffers, which store a continuous stream of * bytes. Discrete messages include an additional * sbBYTES_TO_STORE_MESSAGE_LENGTH bytes that hold the length of the * message. */ if( ( pxStreamBuffer->ucFlags & sbFLAGS_IS_MESSAGE_BUFFER ) != ( uint8_t ) 0 ) { xBytesToStoreMessageLength = sbBYTES_TO_STORE_MESSAGE_LENGTH; } else { xBytesToStoreMessageLength = 0; } xBytesAvailable = prvBytesInBuffer( pxStreamBuffer ); /* Whether receiving a discrete message (where xBytesToStoreMessageLength * holds the number of bytes used to store the message length) or a stream of * bytes (where xBytesToStoreMessageLength is zero), the number of bytes * available must be greater than xBytesToStoreMessageLength to be able to * read bytes from the buffer. */ if( xBytesAvailable > xBytesToStoreMessageLength ) { xReceivedLength = prvReadMessageFromBuffer( pxStreamBuffer, pvRxData, xBufferLengthBytes, xBytesAvailable ); /* Was a task waiting for space in the buffer? */ if( xReceivedLength != ( size_t ) 0 ) { prvRECEIVE_COMPLETED_FROM_ISR( pxStreamBuffer, pxHigherPriorityTaskWoken ); } else { mtCOVERAGE_TEST_MARKER(); } } else { mtCOVERAGE_TEST_MARKER(); } traceSTREAM_BUFFER_RECEIVE_FROM_ISR( xStreamBuffer, xReceivedLength ); traceRETURN_xStreamBufferReceiveFromISR( xReceivedLength ); return xReceivedLength; } /*-----------------------------------------------------------*/ static size_t prvReadMessageFromBuffer( StreamBuffer_t * pxStreamBuffer, void * pvRxData, size_t xBufferLengthBytes, size_t xBytesAvailable ) { size_t xCount, xNextMessageLength; configMESSAGE_BUFFER_LENGTH_TYPE xTempNextMessageLength; size_t xNextTail = pxStreamBuffer->xTail; if( ( pxStreamBuffer->ucFlags & sbFLAGS_IS_MESSAGE_BUFFER ) != ( uint8_t ) 0 ) { /* A discrete message is being received. First receive the length * of the message. */ xNextTail = prvReadBytesFromBuffer( pxStreamBuffer, ( uint8_t * ) &xTempNextMessageLength, sbBYTES_TO_STORE_MESSAGE_LENGTH, xNextTail ); xNextMessageLength = ( size_t ) xTempNextMessageLength; /* Reduce the number of bytes available by the number of bytes just * read out. */ xBytesAvailable -= sbBYTES_TO_STORE_MESSAGE_LENGTH; /* Check there is enough space in the buffer provided by the * user. */ if( xNextMessageLength > xBufferLengthBytes ) { /* The user has provided insufficient space to read the message. */ xNextMessageLength = 0; } else { mtCOVERAGE_TEST_MARKER(); } } else { /* A stream of bytes is being received (as opposed to a discrete * message), so read as many bytes as possible. */ xNextMessageLength = xBufferLengthBytes; } /* Use the minimum of the wanted bytes and the available bytes. */ xCount = configMIN( xNextMessageLength, xBytesAvailable ); if( xCount != ( size_t ) 0 ) { /* Read the actual data and update the tail to mark the data as officially consumed. */ /* MISRA Ref 11.5.5 [Void pointer assignment] */ /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-115 */ /* coverity[misra_c_2012_rule_11_5_violation] */ pxStreamBuffer->xTail = prvReadBytesFromBuffer( pxStreamBuffer, ( uint8_t * ) pvRxData, xCount, xNextTail ); } return xCount; } /*-----------------------------------------------------------*/ BaseType_t xStreamBufferIsEmpty( StreamBufferHandle_t xStreamBuffer ) { const StreamBuffer_t * const pxStreamBuffer = xStreamBuffer; BaseType_t xReturn; size_t xTail; traceENTER_xStreamBufferIsEmpty( xStreamBuffer ); configASSERT( pxStreamBuffer ); /* True if no bytes are available. */ xTail = pxStreamBuffer->xTail; if( pxStreamBuffer->xHead == xTail ) { xReturn = pdTRUE; } else { xReturn = pdFALSE; } traceRETURN_xStreamBufferIsEmpty( xReturn ); return xReturn; } /*-----------------------------------------------------------*/ BaseType_t xStreamBufferIsFull( StreamBufferHandle_t xStreamBuffer ) { BaseType_t xReturn; size_t xBytesToStoreMessageLength; const StreamBuffer_t * const pxStreamBuffer = xStreamBuffer; traceENTER_xStreamBufferIsFull( xStreamBuffer ); configASSERT( pxStreamBuffer ); /* This generic version of the receive function is used by both message * buffers, which store discrete messages, and stream buffers, which store a * continuous stream of bytes. Discrete messages include an additional * sbBYTES_TO_STORE_MESSAGE_LENGTH bytes that hold the length of the message. */ if( ( pxStreamBuffer->ucFlags & sbFLAGS_IS_MESSAGE_BUFFER ) != ( uint8_t ) 0 ) { xBytesToStoreMessageLength = sbBYTES_TO_STORE_MESSAGE_LENGTH; } else { xBytesToStoreMessageLength = 0; } /* True if the available space equals zero. */ if( xStreamBufferSpacesAvailable( xStreamBuffer ) <= xBytesToStoreMessageLength ) { xReturn = pdTRUE; } else { xReturn = pdFALSE; } traceRETURN_xStreamBufferIsFull( xReturn ); return xReturn; } /*-----------------------------------------------------------*/ BaseType_t xStreamBufferSendCompletedFromISR( StreamBufferHandle_t xStreamBuffer, BaseType_t * pxHigherPriorityTaskWoken ) { StreamBuffer_t * const pxStreamBuffer = xStreamBuffer; BaseType_t xReturn; UBaseType_t uxSavedInterruptStatus; traceENTER_xStreamBufferSendCompletedFromISR( xStreamBuffer, pxHigherPriorityTaskWoken ); configASSERT( pxStreamBuffer ); uxSavedInterruptStatus = taskENTER_CRITICAL_FROM_ISR(); { if( ( pxStreamBuffer )->xTaskWaitingToReceive != NULL ) { ( void ) xTaskNotifyIndexedFromISR( ( pxStreamBuffer )->xTaskWaitingToReceive, ( pxStreamBuffer )->uxNotificationIndex, ( uint32_t ) 0, eNoAction, pxHigherPriorityTaskWoken ); ( pxStreamBuffer )->xTaskWaitingToReceive = NULL; xReturn = pdTRUE; } else { xReturn = pdFALSE; } } taskEXIT_CRITICAL_FROM_ISR( uxSavedInterruptStatus ); traceRETURN_xStreamBufferSendCompletedFromISR( xReturn ); return xReturn; } /*-----------------------------------------------------------*/ BaseType_t xStreamBufferReceiveCompletedFromISR( StreamBufferHandle_t xStreamBuffer, BaseType_t * pxHigherPriorityTaskWoken ) { StreamBuffer_t * const pxStreamBuffer = xStreamBuffer; BaseType_t xReturn; UBaseType_t uxSavedInterruptStatus; traceENTER_xStreamBufferReceiveCompletedFromISR( xStreamBuffer, pxHigherPriorityTaskWoken ); configASSERT( pxStreamBuffer ); uxSavedInterruptStatus = taskENTER_CRITICAL_FROM_ISR(); { if( ( pxStreamBuffer )->xTaskWaitingToSend != NULL ) { ( void ) xTaskNotifyIndexedFromISR( ( pxStreamBuffer )->xTaskWaitingToSend, ( pxStreamBuffer )->uxNotificationIndex, ( uint32_t ) 0, eNoAction, pxHigherPriorityTaskWoken ); ( pxStreamBuffer )->xTaskWaitingToSend = NULL; xReturn = pdTRUE; } else { xReturn = pdFALSE; } } taskEXIT_CRITICAL_FROM_ISR( uxSavedInterruptStatus ); traceRETURN_xStreamBufferReceiveCompletedFromISR( xReturn ); return xReturn; } /*-----------------------------------------------------------*/ static size_t prvWriteBytesToBuffer( StreamBuffer_t * const pxStreamBuffer, const uint8_t * pucData, size_t xCount, size_t xHead ) { size_t xFirstLength; configASSERT( xCount > ( size_t ) 0 ); /* Calculate the number of bytes that can be added in the first write - * which may be less than the total number of bytes that need to be added if * the buffer will wrap back to the beginning. */ xFirstLength = configMIN( pxStreamBuffer->xLength - xHead, xCount ); /* Write as many bytes as can be written in the first write. */ configASSERT( ( xHead + xFirstLength ) <= pxStreamBuffer->xLength ); ( void ) memcpy( ( void * ) ( &( pxStreamBuffer->pucBuffer[ xHead ] ) ), ( const void * ) pucData, xFirstLength ); /* If the number of bytes written was less than the number that could be * written in the first write... */ if( xCount > xFirstLength ) { /* ...then write the remaining bytes to the start of the buffer. */ configASSERT( ( xCount - xFirstLength ) <= pxStreamBuffer->xLength ); ( void ) memcpy( ( void * ) pxStreamBuffer->pucBuffer, ( const void * ) &( pucData[ xFirstLength ] ), xCount - xFirstLength ); } else { mtCOVERAGE_TEST_MARKER(); } xHead += xCount; if( xHead >= pxStreamBuffer->xLength ) { xHead -= pxStreamBuffer->xLength; } else { mtCOVERAGE_TEST_MARKER(); } return xHead; } /*-----------------------------------------------------------*/ static size_t prvReadBytesFromBuffer( StreamBuffer_t * pxStreamBuffer, uint8_t * pucData, size_t xCount, size_t xTail ) { size_t xFirstLength; configASSERT( xCount != ( size_t ) 0 ); /* Calculate the number of bytes that can be read - which may be * less than the number wanted if the data wraps around to the start of * the buffer. */ xFirstLength = configMIN( pxStreamBuffer->xLength - xTail, xCount ); /* Obtain the number of bytes it is possible to obtain in the first * read. Asserts check bounds of read and write. */ configASSERT( xFirstLength <= xCount ); configASSERT( ( xTail + xFirstLength ) <= pxStreamBuffer->xLength ); ( void ) memcpy( ( void * ) pucData, ( const void * ) &( pxStreamBuffer->pucBuffer[ xTail ] ), xFirstLength ); /* If the total number of wanted bytes is greater than the number * that could be read in the first read... */ if( xCount > xFirstLength ) { /* ...then read the remaining bytes from the start of the buffer. */ ( void ) memcpy( ( void * ) &( pucData[ xFirstLength ] ), ( void * ) ( pxStreamBuffer->pucBuffer ), xCount - xFirstLength ); } else { mtCOVERAGE_TEST_MARKER(); } /* Move the tail pointer to effectively remove the data read from the buffer. */ xTail += xCount; if( xTail >= pxStreamBuffer->xLength ) { xTail -= pxStreamBuffer->xLength; } return xTail; } /*-----------------------------------------------------------*/ static size_t prvBytesInBuffer( const StreamBuffer_t * const pxStreamBuffer ) { /* Returns the distance between xTail and xHead. */ size_t xCount; xCount = pxStreamBuffer->xLength + pxStreamBuffer->xHead; xCount -= pxStreamBuffer->xTail; if( xCount >= pxStreamBuffer->xLength ) { xCount -= pxStreamBuffer->xLength; } else { mtCOVERAGE_TEST_MARKER(); } return xCount; } /*-----------------------------------------------------------*/ static void prvInitialiseNewStreamBuffer( StreamBuffer_t * const pxStreamBuffer, uint8_t * const pucBuffer, size_t xBufferSizeBytes, size_t xTriggerLevelBytes, uint8_t ucFlags, StreamBufferCallbackFunction_t pxSendCompletedCallback, StreamBufferCallbackFunction_t pxReceiveCompletedCallback ) { /* Assert here is deliberately writing to the entire buffer to ensure it can * be written to without generating exceptions, and is setting the buffer to a * known value to assist in development/debugging. */ #if ( configASSERT_DEFINED == 1 ) { /* The value written just has to be identifiable when looking at the * memory. Don't use 0xA5 as that is the stack fill value and could * result in confusion as to what is actually being observed. */ #define STREAM_BUFFER_BUFFER_WRITE_VALUE ( 0x55 ) configASSERT( memset( pucBuffer, ( int ) STREAM_BUFFER_BUFFER_WRITE_VALUE, xBufferSizeBytes ) == pucBuffer ); } #endif ( void ) memset( ( void * ) pxStreamBuffer, 0x00, sizeof( StreamBuffer_t ) ); pxStreamBuffer->pucBuffer = pucBuffer; pxStreamBuffer->xLength = xBufferSizeBytes; pxStreamBuffer->xTriggerLevelBytes = xTriggerLevelBytes; pxStreamBuffer->ucFlags = ucFlags; pxStreamBuffer->uxNotificationIndex = tskDEFAULT_INDEX_TO_NOTIFY; #if ( configUSE_SB_COMPLETED_CALLBACK == 1 ) { pxStreamBuffer->pxSendCompletedCallback = pxSendCompletedCallback; pxStreamBuffer->pxReceiveCompletedCallback = pxReceiveCompletedCallback; } #else { /* MISRA Ref 11.1.1 [Object type casting] */ /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-111 */ /* coverity[misra_c_2012_rule_11_1_violation] */ ( void ) pxSendCompletedCallback; /* MISRA Ref 11.1.1 [Object type casting] */ /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-111 */ /* coverity[misra_c_2012_rule_11_1_violation] */ ( void ) pxReceiveCompletedCallback; } #endif /* if ( configUSE_SB_COMPLETED_CALLBACK == 1 ) */ } /*-----------------------------------------------------------*/ UBaseType_t uxStreamBufferGetStreamBufferNotificationIndex( StreamBufferHandle_t xStreamBuffer ) { StreamBuffer_t * const pxStreamBuffer = xStreamBuffer; traceENTER_uxStreamBufferGetStreamBufferNotificationIndex( xStreamBuffer ); configASSERT( pxStreamBuffer ); traceRETURN_uxStreamBufferGetStreamBufferNotificationIndex( pxStreamBuffer->uxNotificationIndex ); return pxStreamBuffer->uxNotificationIndex; } /*-----------------------------------------------------------*/ void vStreamBufferSetStreamBufferNotificationIndex( StreamBufferHandle_t xStreamBuffer, UBaseType_t uxNotificationIndex ) { StreamBuffer_t * const pxStreamBuffer = xStreamBuffer; traceENTER_vStreamBufferSetStreamBufferNotificationIndex( xStreamBuffer, uxNotificationIndex ); configASSERT( pxStreamBuffer ); /* There should be no task waiting otherwise we'd never resume them. */ configASSERT( pxStreamBuffer->xTaskWaitingToReceive == NULL ); configASSERT( pxStreamBuffer->xTaskWaitingToSend == NULL ); /* Check that the task notification index is valid. */ configASSERT( uxNotificationIndex < configTASK_NOTIFICATION_ARRAY_ENTRIES ); pxStreamBuffer->uxNotificationIndex = uxNotificationIndex; traceRETURN_vStreamBufferSetStreamBufferNotificationIndex(); } /*-----------------------------------------------------------*/ #if ( configUSE_TRACE_FACILITY == 1 ) UBaseType_t uxStreamBufferGetStreamBufferNumber( StreamBufferHandle_t xStreamBuffer ) { traceENTER_uxStreamBufferGetStreamBufferNumber( xStreamBuffer ); traceRETURN_uxStreamBufferGetStreamBufferNumber( xStreamBuffer->uxStreamBufferNumber ); return xStreamBuffer->uxStreamBufferNumber; } #endif /* configUSE_TRACE_FACILITY */ /*-----------------------------------------------------------*/ #if ( configUSE_TRACE_FACILITY == 1 ) void vStreamBufferSetStreamBufferNumber( StreamBufferHandle_t xStreamBuffer, UBaseType_t uxStreamBufferNumber ) { traceENTER_vStreamBufferSetStreamBufferNumber( xStreamBuffer, uxStreamBufferNumber ); xStreamBuffer->uxStreamBufferNumber = uxStreamBufferNumber; traceRETURN_vStreamBufferSetStreamBufferNumber(); } #endif /* configUSE_TRACE_FACILITY */ /*-----------------------------------------------------------*/ #if ( configUSE_TRACE_FACILITY == 1 ) uint8_t ucStreamBufferGetStreamBufferType( StreamBufferHandle_t xStreamBuffer ) { traceENTER_ucStreamBufferGetStreamBufferType( xStreamBuffer ); traceRETURN_ucStreamBufferGetStreamBufferType( ( uint8_t ) ( xStreamBuffer->ucFlags & sbFLAGS_IS_MESSAGE_BUFFER ) ); return( ( uint8_t ) ( xStreamBuffer->ucFlags & sbFLAGS_IS_MESSAGE_BUFFER ) ); } #endif /* configUSE_TRACE_FACILITY */ /*-----------------------------------------------------------*/