From 8a597b865f45a4161a0df775276b195d4d9825a2 Mon Sep 17 00:00:00 2001 From: Darian Leung Date: Mon, 4 Mar 2019 14:27:06 +0800 Subject: [PATCH] esp_ringbuf: Add static allocation, refactor, and update API reference This commit updates does the following: - xRingbufferCreateStatic() added to allow ringbuffers via to be statically allocated. Docs and unit tests update accordingly. Closes #3064 - Fix doc typos, closes #3248. Updated API reference regarding to mention item size limits of no-split buffers when calling xRingbufferGetCurFreeSize() or xRingbufferGetMaxItemSize(), closes #3117. - Remove the following deprecated types/functions for v4.0 - xRingbufferIsNextItemWrapped() - xRingbufferAddToQueueSetWrite() - xRingbufferRemoveFromQueueSetWrite() --- .../esp_ringbuf/include/freertos/ringbuf.h | 177 +++++--- components/esp_ringbuf/ringbuf.c | 424 +++++++++++------- components/esp_ringbuf/test/test_ringbuf.c | 98 +++- .../system/freertos_additions.rst | 45 ++ 4 files changed, 497 insertions(+), 247 deletions(-) diff --git a/components/esp_ringbuf/include/freertos/ringbuf.h b/components/esp_ringbuf/include/freertos/ringbuf.h index de8a36909..fd3407e4a 100644 --- a/components/esp_ringbuf/include/freertos/ringbuf.h +++ b/components/esp_ringbuf/include/freertos/ringbuf.h @@ -16,7 +16,7 @@ #define FREERTOS_RINGBUF_H #ifndef INC_FREERTOS_H - #error "include FreeRTOS.h" must appear in source files before "include ringbuf.h" + #error "include FreeRTOS.h" must appear in source files before "include ringbuf.h" #endif #ifdef __cplusplus @@ -33,27 +33,52 @@ extern "C" { typedef void * RingbufHandle_t; typedef enum { - /** - * No-split buffers will only store an item in contiguous memory and will - * never split an item. Each item requires an 8 byte overhead for a header - * and will always internally occupy a 32-bit aligned size of space. - */ - RINGBUF_TYPE_NOSPLIT = 0, - /** - * Allow-split buffers will split an item into two parts if necessary in - * order to store it. Each item requires an 8 byte overhead for a header, - * splitting incurs an extra header. Each item will always internally occupy - * a 32-bit aligned size of space. - */ - RINGBUF_TYPE_ALLOWSPLIT, - /** - * Byte buffers store data as a sequence of bytes and do not maintain separate - * items, therefore byte buffers have no overhead. All data is stored as a - * sequence of byte and any number of bytes can be sent or retrieved each - * time. - */ - RINGBUF_TYPE_BYTEBUF -} ringbuf_type_t; + /** + * No-split buffers will only store an item in contiguous memory and will + * never split an item. Each item requires an 8 byte overhead for a header + * and will always internally occupy a 32-bit aligned size of space. + */ + RINGBUF_TYPE_NOSPLIT = 0, + /** + * Allow-split buffers will split an item into two parts if necessary in + * order to store it. Each item requires an 8 byte overhead for a header, + * splitting incurs an extra header. Each item will always internally occupy + * a 32-bit aligned size of space. + */ + RINGBUF_TYPE_ALLOWSPLIT, + /** + * Byte buffers store data as a sequence of bytes and do not maintain separate + * items, therefore byte buffers have no overhead. All data is stored as a + * sequence of byte and any number of bytes can be sent or retrieved each + * time. + */ + RINGBUF_TYPE_BYTEBUF, + RINGBUF_TYPE_MAX, +} RingbufferType_t; + +/** + * @brief Struct that is equivalent in size to the ring buffer's data structure + * + * The contents of this struct are not meant to be used directly. This + * structure is meant to be used when creating a statically allocated ring + * buffer where this struct is of the exact size required to store a ring + * buffer's control data structure. + * + * @note The CONFIG_FREERTOS_SUPPORT_STATIC_ALLOCATION option must be enabled for + * this structure to be available. + */ +#if ( configSUPPORT_STATIC_ALLOCATION == 1) +typedef struct xSTATIC_RINGBUFFER { + /** @cond */ //Doxygen command to hide this structure from API Reference + size_t xDummy1[2]; + UBaseType_t uxDummy2; + BaseType_t xDummy3; + void *pvDummy4[10]; + StaticSemaphore_t xDummy5[2]; + portMUX_TYPE muxDummy; + /** @endcond */ +} StaticRingbuffer_t; +#endif /** * @brief Create a ring buffer @@ -66,7 +91,7 @@ typedef enum { * * @return A handle to the created ring buffer, or NULL in case of error. */ -RingbufHandle_t xRingbufferCreate(size_t xBufferSize, ringbuf_type_t xBufferType); +RingbufHandle_t xRingbufferCreate(size_t xBufferSize, RingbufferType_t xBufferType); /** * @brief Create a ring buffer of type RINGBUF_TYPE_NOSPLIT for a fixed item_size @@ -81,11 +106,36 @@ RingbufHandle_t xRingbufferCreate(size_t xBufferSize, ringbuf_type_t xBufferType */ RingbufHandle_t xRingbufferCreateNoSplit(size_t xItemSize, size_t xItemNum); + +/** + * @brief Create a ring buffer but manually provide the required memory + * + * @param[in] xBufferSize Size of the buffer in bytes. + * @param[in] xBufferType Type of ring buffer, see documentation + * @param[in] pucRingbufferStorage Pointer to the ring buffer's storage area. + * Storage area must of the same size as specified by xBufferSize + * @param[in] pxStaticRingbuffer Pointed to a struct of type StaticRingbuffer_t + * which will be used to hold the ring buffer's data structure + * + * @note The CONFIG_FREERTOS_SUPPORT_STATIC_ALLOCATION option must be enabled + * for this to be available + * + * @note xBufferSize of no-split/allow-split buffers MUST be 32-bit aligned. + * + * @return A handle to the created ring buffer + */ +#if ( configSUPPORT_STATIC_ALLOCATION == 1) +RingbufHandle_t xRingbufferCreateStatic(size_t xBufferSize, + RingbufferType_t xBufferType, + uint8_t *pucRingbufferStorage, + StaticRingbuffer_t *pxStaticRingbuffer); +#endif + /** * @brief Insert an item into the ring buffer * * Attempt to insert an item into the ring buffer. This function will block until - * enough free space is available or until it timesout. + * enough free space is available or until it times out. * * @param[in] xRingbuffer Ring buffer to insert the item into * @param[in] pvItem Pointer to data to insert. NULL is allowed if xItemSize is 0. @@ -101,7 +151,10 @@ RingbufHandle_t xRingbufferCreateNoSplit(size_t xItemSize, size_t xItemNum); * - pdTRUE if succeeded * - pdFALSE on time-out or when the data is larger than the maximum permissible size of the buffer */ -BaseType_t xRingbufferSend(RingbufHandle_t xRingbuffer, const void *pvItem, size_t xItemSize, TickType_t xTicksToWait); +BaseType_t xRingbufferSend(RingbufHandle_t xRingbuffer, + const void *pvItem, + size_t xItemSize, + TickType_t xTicksToWait); /** * @brief Insert an item into the ring buffer in an ISR @@ -123,13 +176,16 @@ BaseType_t xRingbufferSend(RingbufHandle_t xRingbuffer, const void *pvItem, size * - pdTRUE if succeeded * - pdFALSE when the ring buffer does not have space. */ -BaseType_t xRingbufferSendFromISR(RingbufHandle_t xRingbuffer, const void *pvItem, size_t xItemSize, BaseType_t *pxHigherPriorityTaskWoken); +BaseType_t xRingbufferSendFromISR(RingbufHandle_t xRingbuffer, + const void *pvItem, + size_t xItemSize, + BaseType_t *pxHigherPriorityTaskWoken); /** * @brief Retrieve an item from the ring buffer * * Attempt to retrieve an item from the ring buffer. This function will block - * until an item is available or until it timesout. + * until an item is available or until it times out. * * @param[in] xRingbuffer Ring buffer to retrieve the item from * @param[out] pxItemSize Pointer to a variable to which the size of the retrieved item will be written. @@ -168,7 +224,7 @@ void *xRingbufferReceiveFromISR(RingbufHandle_t xRingbuffer, size_t *pxItemSize) * Attempt to retrieve a split item from an allow-split ring buffer. If the item * is not split, only a single item is retried. If the item is split, both parts * will be retrieved. This function will block until an item is available or - * until it timesout. + * until it times out. * * @param[in] xRingbuffer Ring buffer to retrieve the item from * @param[out] ppvHeadItem Double pointer to first part (set to NULL if no items were retrieved) @@ -184,7 +240,12 @@ void *xRingbufferReceiveFromISR(RingbufHandle_t xRingbuffer, size_t *pxItemSize) * - pdTRUE if an item (split or unsplit) was retrieved * - pdFALSE when no item was retrieved */ -BaseType_t xRingbufferReceiveSplit(RingbufHandle_t xRingbuffer, void **ppvHeadItem, void **ppvTailItem, size_t *pxHeadItemSize, size_t *pxTailItemSize, TickType_t xTicksToWait); +BaseType_t xRingbufferReceiveSplit(RingbufHandle_t xRingbuffer, + void **ppvHeadItem, + void **ppvTailItem, + size_t *pxHeadItemSize, + size_t *pxTailItemSize, + TickType_t xTicksToWait); /** * @brief Retrieve a split item from an allow-split ring buffer in an ISR @@ -207,14 +268,18 @@ BaseType_t xRingbufferReceiveSplit(RingbufHandle_t xRingbuffer, void **ppvHeadIt * - pdTRUE if an item (split or unsplit) was retrieved * - pdFALSE when no item was retrieved */ -BaseType_t xRingbufferReceiveSplitFromISR(RingbufHandle_t xRingbuffer, void **ppvHeadItem, void **ppvTailItem, size_t *pxHeadItemSize, size_t *pxTailItemSize); +BaseType_t xRingbufferReceiveSplitFromISR(RingbufHandle_t xRingbuffer, + void **ppvHeadItem, + void **ppvTailItem, + size_t *pxHeadItemSize, + size_t *pxTailItemSize); /** * @brief Retrieve bytes from a byte buffer, specifying the maximum amount of bytes to retrieve * * Attempt to retrieve data from a byte buffer whilst specifying a maximum number * of bytes to retrieve. This function will block until there is data available - * for retrieval or until it timesout. + * for retrieval or until it times out. * * @param[in] xRingbuffer Ring buffer to retrieve the item from * @param[out] pxItemSize Pointer to a variable to which the size of the retrieved item will be written. @@ -230,7 +295,10 @@ BaseType_t xRingbufferReceiveSplitFromISR(RingbufHandle_t xRingbuffer, void **pp * the length of the item. * - NULL on timeout, *pxItemSize is untouched in that case. */ -void *xRingbufferReceiveUpTo(RingbufHandle_t xRingbuffer, size_t *pxItemSize, TickType_t xTicksToWait, size_t xMaxSize); +void *xRingbufferReceiveUpTo(RingbufHandle_t xRingbuffer, + size_t *pxItemSize, + TickType_t xTicksToWait, + size_t xMaxSize); /** * @brief Retrieve bytes from a byte buffer, specifying the maximum amount of @@ -281,6 +349,10 @@ void vRingbufferReturnItemFromISR(RingbufHandle_t xRingbuffer, void *pvItem, Bas * @brief Delete a ring buffer * * @param[in] xRingbuffer Ring buffer to delete + * + * @note This function will not deallocate any memory if the ring buffer was + * created using xRingbufferCreateStatic(). Deallocation must be done + * manually be the user. */ void vRingbufferDelete(RingbufHandle_t xRingbuffer); @@ -292,6 +364,12 @@ void vRingbufferDelete(RingbufHandle_t xRingbuffer); * * @param[in] xRingbuffer Ring buffer to query * + * @note The max item size for a no-split buffer is limited to + * ((buffer_size/2)-header_size). This limit is imposed so that an item + * of max item size can always be sent to the an empty no-split buffer + * regardless of the internal positions of the buffer's read/write/free + * pointers. + * * @return Maximum size, in bytes, of an item that can be placed in a ring buffer. */ size_t xRingbufferGetMaxItemSize(RingbufHandle_t xRingbuffer); @@ -307,6 +385,10 @@ size_t xRingbufferGetMaxItemSize(RingbufHandle_t xRingbuffer); * the same ring buffer, it is the application's responsibility to * ensure atomic access to this API and the subsequent Send * + * @note An empty no-split buffer has a max current free size for an item + * that is limited to ((buffer_size/2)-header_size). See API reference + * for xRingbufferGetMaxItemSize(). + * * @param[in] xRingbuffer Ring buffer to query * * @return Current free size, in bytes, available for an entry @@ -373,7 +455,11 @@ BaseType_t xRingbufferRemoveFromQueueSetRead(RingbufHandle_t xRingbuffer, QueueS * @param[out] uxWrite Pointer use to store write pointer position * @param[out] uxItemsWaiting Pointer use to store number of items (bytes for byte buffer) waiting to be retrieved */ -void vRingbufferGetInfo(RingbufHandle_t xRingbuffer, UBaseType_t *uxFree, UBaseType_t *uxRead, UBaseType_t *uxWrite, UBaseType_t *uxItemsWaiting); +void vRingbufferGetInfo(RingbufHandle_t xRingbuffer, + UBaseType_t *uxFree, + UBaseType_t *uxRead, + UBaseType_t *uxWrite, + UBaseType_t *uxItemsWaiting); /** * @brief Debugging function to print the internal pointers in the ring buffer @@ -382,30 +468,9 @@ void vRingbufferGetInfo(RingbufHandle_t xRingbuffer, UBaseType_t *uxFree, UBaseT */ void xRingbufferPrintInfo(RingbufHandle_t xRingbuffer); -/* -------------------------------- Deprecated Functions --------------------------- */ +/* ------------------------------- Deprecated ------------------------------- */ -/** @cond */ //Doxygen command to hide deprecated function from API Reference -/* - * Deprecated as function is not thread safe and does not check if an item is - * actually available for retrieval. Use xRingbufferReceiveSplit() instead for - * thread safe method of retrieve a split item. - */ -bool xRingbufferIsNextItemWrapped(RingbufHandle_t xRingbuffer) __attribute__((deprecated)); - -/* - * Deprecated as queue sets are not meant to be used for writing to buffers. Adding - * the ring buffer write semaphore to a queue set will break queue set usage rules, - * as every read of a semaphore must be preceded by a call to xQueueSelectFromSet(). - * QueueSetWrite no longer supported. - */ -BaseType_t xRingbufferAddToQueueSetWrite(RingbufHandle_t xRingbuffer, QueueSetHandle_t xQueueSet) __attribute__((deprecated)); - -/* - * Deprecated as queue sets are not meant to be used for writing to buffers. - * QueueSetWrite no longer supported. - */ -BaseType_t xRingbufferRemoveFromQueueSetWrite(RingbufHandle_t xRingbuffer, QueueSetHandle_t xQueueSet) __attribute__((deprecated)); -/** @endcond */ +typedef RingbufferType_t ringbuf_type_t __attribute__((deprecated)); #ifdef __cplusplus } diff --git a/components/esp_ringbuf/ringbuf.c b/components/esp_ringbuf/ringbuf.c index 558b61c6d..b3b43b0aa 100644 --- a/components/esp_ringbuf/ringbuf.c +++ b/components/esp_ringbuf/ringbuf.c @@ -21,18 +21,28 @@ //32-bit alignment macros #define rbALIGN_SIZE( xSize ) ( ( xSize + portBYTE_ALIGNMENT_MASK ) & ~portBYTE_ALIGNMENT_MASK ) -#define rbCHECK_ALIGNED( pvPtr ) ( ( ( UBaseType_t ) pvPtr & portBYTE_ALIGNMENT_MASK ) == 0 ) +#define rbCHECK_ALIGNED( pvPtr ) ( ( ( UBaseType_t ) ( pvPtr ) & portBYTE_ALIGNMENT_MASK ) == 0 ) //Ring buffer flags #define rbALLOW_SPLIT_FLAG ( ( UBaseType_t ) 1 ) //The ring buffer allows items to be split #define rbBYTE_BUFFER_FLAG ( ( UBaseType_t ) 2 ) //The ring buffer is a byte buffer #define rbBUFFER_FULL_FLAG ( ( UBaseType_t ) 4 ) //The ring buffer is currently full (write pointer == free pointer) +#define rbBUFFER_STATIC_FLAG ( ( UBaseType_t ) 8 ) //The ring buffer is statically allocated //Item flags #define rbITEM_FREE_FLAG ( ( UBaseType_t ) 1 ) //Item has been retrieved and returned by application, free to overwrite #define rbITEM_DUMMY_DATA_FLAG ( ( UBaseType_t ) 2 ) //Data from here to end of the ring buffer is dummy data. Restart reading at start of head of the buffer #define rbITEM_SPLIT_FLAG ( ( UBaseType_t ) 4 ) //Valid for RINGBUF_TYPE_ALLOWSPLIT, indicating that rest of the data is wrapped around +//Static allocation related +#if ( configSUPPORT_STATIC_ALLOCATION == 1 ) +#define rbGET_TX_SEM_HANDLE( pxRingbuffer ) ( (SemaphoreHandle_t) &(pxRingbuffer->xTransSemStatic) ) +#define rbGET_RX_SEM_HANDLE( pxRingbuffer ) ( (SemaphoreHandle_t) &(pxRingbuffer->xRecvSemStatic) ) +#else +#define rbGET_TX_SEM_HANDLE( pxRingbuffer ) ( pxRingbuffer->xTransSemHandle ) +#define rbGET_RX_SEM_HANDLE( pxRingbuffer ) ( pxRingbuffer->xRecvSemHandle ) +#endif + typedef struct { //This size of this structure must be 32-bit aligned size_t xItemLen; @@ -40,7 +50,7 @@ typedef struct { } ItemHeader_t; #define rbHEADER_SIZE sizeof(ItemHeader_t) -typedef struct Ringbuffer_t Ringbuffer_t; +typedef struct RingbufferDefinition Ringbuffer_t; typedef BaseType_t (*CheckItemFitsFunction_t)(Ringbuffer_t *pxRingbuffer, size_t xItemSize); typedef void (*CopyItemFunction_t)(Ringbuffer_t *pxRingbuffer, const uint8_t *pcItem, size_t xItemSize); typedef BaseType_t (*CheckItemAvailFunction_t) (Ringbuffer_t *pxRingbuffer); @@ -48,10 +58,10 @@ typedef void *(*GetItemFunction_t)(Ringbuffer_t *pxRingbuffer, BaseType_t *pxIsS typedef void (*ReturnItemFunction_t)(Ringbuffer_t *pxRingbuffer, uint8_t *pvItem); typedef size_t (*GetCurMaxSizeFunction_t)(Ringbuffer_t *pxRingbuffer); -struct Ringbuffer_t { +typedef struct RingbufferDefinition { size_t xSize; //Size of the data storage - UBaseType_t uxRingbufferFlags; //Flags to indicate the type and status of ring buffer size_t xMaxItemSize; //Maximum item size + UBaseType_t uxRingbufferFlags; //Flags to indicate the type and status of ring buffer CheckItemFitsFunction_t xCheckItemFits; //Function to check if item can currently fit in ring buffer CopyItemFunction_t vCopyItem; //Function to copy item to ring buffer @@ -66,11 +76,37 @@ struct Ringbuffer_t { uint8_t *pucTail; //Pointer to the end of the ring buffer storage area BaseType_t xItemsWaiting; //Number of items/bytes(for byte buffers) currently in ring buffer that have not yet been read - SemaphoreHandle_t xFreeSpaceSemaphore; //Binary semaphore, wakes up writing threads when more free space becomes available or when another thread times out attempting to write - SemaphoreHandle_t xItemsBufferedSemaphore; //Binary semaphore, indicates there are new packets in the circular buffer. See remark. + /* + * TransSem: Binary semaphore used to indicate to a blocked transmitting tasks + * that more free space has become available or that the block has + * timed out. + * + * RecvSem: Binary semaphore used to indicate to a blocked receiving task that + * new data/item has been written to the ring buffer. + * + * Note - When static allocation is enabled, the two semaphores are always + * statically stored in the ring buffer's control structure + * regardless of whether the ring buffer is allocated dynamically or + * statically. When static allocation is disabled, the two semaphores + * are allocated dynamically and their handles stored instead, thus + * making the ring buffer's control structure slightly smaller when + * static allocation is disabled. + */ +#if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + StaticSemaphore_t xTransSemStatic; + StaticSemaphore_t xRecvSemStatic; +#else + SemaphoreHandle_t xTransSemHandle; + SemaphoreHandle_t xRecvSemHandle; +#endif portMUX_TYPE mux; //Spinlock required for SMP -}; +} Ringbuffer_t; +#if ( configSUPPORT_STATIC_ALLOCATION == 1 ) +#if __GNUC_PREREQ(4, 6) +_Static_assert(sizeof(StaticRingbuffer_t) == sizeof(Ringbuffer_t), "StaticRingbuffer_t != Ringbuffer_t"); +#endif +#endif /* Remark: A counting semaphore for items_buffered_sem would be more logical, but counting semaphores in FreeRTOS need a maximum count, and allocate more memory the larger the maximum count is. Here, we @@ -78,13 +114,20 @@ would need to set the maximum to the maximum amount of times a null-byte unit fi which is quite high and so would waste a fair amount of memory. */ -/* ------------------------------------------------ Static Declarations ------------------------------------------ */ +/* --------------------------- Static Declarations -------------------------- */ /* * WARNING: All of the following static functions (except generic functions) * ARE NOT THREAD SAFE. Therefore they should only be called within a critical * section (using spin locks) */ + +//Initialize a ring buffer after space has been allocated for it +static void prvInitializeNewRingbuffer(size_t xBufferSize, + RingbufferType_t xBufferType, + Ringbuffer_t *pxNewRingbuffer, + uint8_t *pucRingbufferStorage); + //Calculate current amount of free space (in bytes) in the ring buffer static size_t prvGetFreeSize(Ringbuffer_t *pxRingbuffer); @@ -107,10 +150,16 @@ static void prvCopyItemAllowSplit(Ringbuffer_t *pxRingbuffer, const uint8_t *puc static void prvCopyItemByteBuf(Ringbuffer_t *pxRingbuffer, const uint8_t *pucItem, size_t xItemSize); //Retrieve item from no-split/allow-split ring buffer. *pxIsSplit is set to pdTRUE if the retrieved item is split -static void *prvGetItemDefault(Ringbuffer_t *pxRingbuffer, BaseType_t *pxIsSplit, size_t xUnusedParam, size_t *pxItemSize); +static void *prvGetItemDefault(Ringbuffer_t *pxRingbuffer, + BaseType_t *pxIsSplit, + size_t xUnusedParam, + size_t *pxItemSize); //Retrieve data from byte buffer. If xMaxSize is 0, all continuous data is retrieved -static void *prvGetItemByteBuf(Ringbuffer_t *pxRingbuffer, BaseType_t *pxUnusedParam ,size_t xMaxSize, size_t *pxItemSize); +static void *prvGetItemByteBuf(Ringbuffer_t *pxRingbuffer, + BaseType_t *pxUnusedParam, + size_t xMaxSize, + size_t *pxItemSize); //Return an item to a split/no-split ring buffer static void prvReturnItemDefault(Ringbuffer_t *pxRingbuffer, uint8_t *pucItem); @@ -133,12 +182,73 @@ static size_t prvGetCurMaxSizeByteBuf(Ringbuffer_t *pxRingbuffer); * a split item will be retrieved. xMaxSize will only take effect if called on * byte buffers. */ -static BaseType_t prvReceiveGeneric(Ringbuffer_t *pxRingbuffer, void **pvItem1, void **pvItem2, size_t *xItemSize1, size_t *xItemSize2, size_t xMaxSize, TickType_t xTicksToWait); +static BaseType_t prvReceiveGeneric(Ringbuffer_t *pxRingbuffer, + void **pvItem1, + void **pvItem2, + size_t *xItemSize1, + size_t *xItemSize2, + size_t xMaxSize, + TickType_t xTicksToWait); //Generic function used to retrieve an item/data from ring buffers in an ISR -static BaseType_t prvReceiveGenericFromISR(Ringbuffer_t *pxRingbuffer, void **pvItem1, void **pvItem2, size_t *xItemSize1, size_t *xItemSize2, size_t xMaxSize); +static BaseType_t prvReceiveGenericFromISR(Ringbuffer_t *pxRingbuffer, + void **pvItem1, + void **pvItem2, + size_t *xItemSize1, + size_t *xItemSize2, + size_t xMaxSize); -/* ------------------------------------------------ Static Definitions ------------------------------------------- */ +/* --------------------------- Static Definitions --------------------------- */ + +static void prvInitializeNewRingbuffer(size_t xBufferSize, + RingbufferType_t xBufferType, + Ringbuffer_t *pxNewRingbuffer, + uint8_t *pucRingbufferStorage) +{ + //Initialize values + pxNewRingbuffer->xSize = xBufferSize; + pxNewRingbuffer->pucHead = pucRingbufferStorage; + pxNewRingbuffer->pucTail = pucRingbufferStorage + xBufferSize; + pxNewRingbuffer->pucFree = pucRingbufferStorage; + pxNewRingbuffer->pucRead = pucRingbufferStorage; + pxNewRingbuffer->pucWrite = pucRingbufferStorage; + pxNewRingbuffer->xItemsWaiting = 0; + pxNewRingbuffer->uxRingbufferFlags = 0; + + //Initialize type dependent values and function pointers + if (xBufferType == RINGBUF_TYPE_NOSPLIT) { + pxNewRingbuffer->xCheckItemFits = prvCheckItemFitsDefault; + pxNewRingbuffer->vCopyItem = prvCopyItemNoSplit; + pxNewRingbuffer->pvGetItem = prvGetItemDefault; + pxNewRingbuffer->vReturnItem = prvReturnItemDefault; + /* + * Worst case scenario is when the read/write/free pointers are all + * pointing to the halfway point of the buffer. + */ + pxNewRingbuffer->xMaxItemSize = rbALIGN_SIZE(pxNewRingbuffer->xSize / 2) - rbHEADER_SIZE; + pxNewRingbuffer->xGetCurMaxSize = prvGetCurMaxSizeNoSplit; + } else if (xBufferType == RINGBUF_TYPE_ALLOWSPLIT) { + pxNewRingbuffer->uxRingbufferFlags |= rbALLOW_SPLIT_FLAG; + pxNewRingbuffer->xCheckItemFits = prvCheckItemFitsDefault; + pxNewRingbuffer->vCopyItem = prvCopyItemAllowSplit; + pxNewRingbuffer->pvGetItem = prvGetItemDefault; + pxNewRingbuffer->vReturnItem = prvReturnItemDefault; + //Worst case an item is split into two, incurring two headers of overhead + pxNewRingbuffer->xMaxItemSize = pxNewRingbuffer->xSize - (sizeof(ItemHeader_t) * 2); + pxNewRingbuffer->xGetCurMaxSize = prvGetCurMaxSizeAllowSplit; + } else { //Byte Buffer + pxNewRingbuffer->uxRingbufferFlags |= rbBYTE_BUFFER_FLAG; + pxNewRingbuffer->xCheckItemFits = prvCheckItemFitsByteBuffer; + pxNewRingbuffer->vCopyItem = prvCopyItemByteBuf; + pxNewRingbuffer->pvGetItem = prvGetItemByteBuf; + pxNewRingbuffer->vReturnItem = prvReturnItemByteBuf; + //Byte buffers do not incur any overhead + pxNewRingbuffer->xMaxItemSize = pxNewRingbuffer->xSize; + pxNewRingbuffer->xGetCurMaxSize = prvGetCurMaxSizeByteBuf; + } + xSemaphoreGive(rbGET_TX_SEM_HANDLE(pxNewRingbuffer)); + vPortCPUInitializeMutex(&pxNewRingbuffer->mux); +} static size_t prvGetFreeSize(Ringbuffer_t *pxRingbuffer) { @@ -333,7 +443,10 @@ static BaseType_t prvCheckItemAvail(Ringbuffer_t *pxRingbuffer) } } -static void *prvGetItemDefault(Ringbuffer_t *pxRingbuffer, BaseType_t *pxIsSplit, size_t xUnusedParam, size_t *pxItemSize) +static void *prvGetItemDefault(Ringbuffer_t *pxRingbuffer, + BaseType_t *pxIsSplit, + size_t xUnusedParam, + size_t *pxItemSize) { //Check arguments and buffer state ItemHeader_t *pxHeader = (ItemHeader_t *)pxRingbuffer->pucRead; @@ -371,7 +484,10 @@ static void *prvGetItemDefault(Ringbuffer_t *pxRingbuffer, BaseType_t *pxIsSplit return (void *)pcReturn; } -static void *prvGetItemByteBuf(Ringbuffer_t *pxRingbuffer, BaseType_t *pxUnusedParam ,size_t xMaxSize, size_t *pxItemSize) +static void *prvGetItemByteBuf(Ringbuffer_t *pxRingbuffer, + BaseType_t *pxUnusedParam, + size_t xMaxSize, + size_t *pxItemSize) { //Check arguments and buffer state configASSERT((pxRingbuffer->xItemsWaiting > 0) && ((pxRingbuffer->pucRead != pxRingbuffer->pucWrite) || (pxRingbuffer->uxRingbufferFlags & rbBUFFER_FULL_FLAG))); //Check there are items to be read @@ -552,7 +668,13 @@ static size_t prvGetCurMaxSizeByteBuf(Ringbuffer_t *pxRingbuffer) return xFreeSize; } -static BaseType_t prvReceiveGeneric(Ringbuffer_t *pxRingbuffer, void **pvItem1, void **pvItem2, size_t *xItemSize1, size_t *xItemSize2, size_t xMaxSize, TickType_t xTicksToWait) +static BaseType_t prvReceiveGeneric(Ringbuffer_t *pxRingbuffer, + void **pvItem1, + void **pvItem2, + size_t *xItemSize1, + size_t *xItemSize2, + size_t xMaxSize, + TickType_t xTicksToWait) { BaseType_t xReturn = pdFALSE; BaseType_t xReturnSemaphore = pdFALSE; @@ -560,7 +682,7 @@ static BaseType_t prvReceiveGeneric(Ringbuffer_t *pxRingbuffer, void **pvItem1, TickType_t xTicksRemaining = xTicksToWait; while (xTicksRemaining <= xTicksToWait) { //xTicksToWait will underflow once xTaskGetTickCount() > ticks_end //Block until more free space becomes available or timeout - if (xSemaphoreTake(pxRingbuffer->xItemsBufferedSemaphore, xTicksRemaining) != pdTRUE) { + if (xSemaphoreTake(rbGET_RX_SEM_HANDLE(pxRingbuffer), xTicksRemaining) != pdTRUE) { xReturn = pdFALSE; //Timed out attempting to get semaphore break; } @@ -606,12 +728,17 @@ static BaseType_t prvReceiveGeneric(Ringbuffer_t *pxRingbuffer, void **pvItem1, } if (xReturnSemaphore == pdTRUE) { - xSemaphoreGive(pxRingbuffer->xItemsBufferedSemaphore); //Give semaphore back so other tasks can retrieve + xSemaphoreGive(rbGET_RX_SEM_HANDLE(pxRingbuffer)); //Give semaphore back so other tasks can retrieve } return xReturn; } -static BaseType_t prvReceiveGenericFromISR(Ringbuffer_t *pxRingbuffer, void **pvItem1, void **pvItem2, size_t *xItemSize1, size_t *xItemSize2, size_t xMaxSize) +static BaseType_t prvReceiveGenericFromISR(Ringbuffer_t *pxRingbuffer, + void **pvItem1, + void **pvItem2, + size_t *xItemSize1, + size_t *xItemSize2, + size_t xMaxSize) { BaseType_t xReturn = pdFALSE; BaseType_t xReturnSemaphore = pdFALSE; @@ -644,95 +771,54 @@ static BaseType_t prvReceiveGenericFromISR(Ringbuffer_t *pxRingbuffer, void **pv portEXIT_CRITICAL_ISR(&pxRingbuffer->mux); if (xReturnSemaphore == pdTRUE) { - xSemaphoreGiveFromISR(pxRingbuffer->xItemsBufferedSemaphore, NULL); //Give semaphore back so other tasks can retrieve + xSemaphoreGiveFromISR(rbGET_RX_SEM_HANDLE(pxRingbuffer), NULL); //Give semaphore back so other tasks can retrieve } return xReturn; } -/* ------------------------------------------------- Public Definitions -------------------------------------------- */ +/* --------------------------- Public Definitions --------------------------- */ -RingbufHandle_t xRingbufferCreate(size_t xBufferSize, ringbuf_type_t xBufferType) +RingbufHandle_t xRingbufferCreate(size_t xBufferSize, RingbufferType_t xBufferType) { + configASSERT(xBufferSize > 0); + configASSERT(xBufferType < RINGBUF_TYPE_MAX); + //Allocate memory - Ringbuffer_t *pxRingbuffer = calloc(1, sizeof(Ringbuffer_t)); - if (pxRingbuffer == NULL) { - goto err; - } if (xBufferType != RINGBUF_TYPE_BYTEBUF) { xBufferSize = rbALIGN_SIZE(xBufferSize); //xBufferSize is rounded up for no-split/allow-split buffers } - pxRingbuffer->pucHead = malloc(xBufferSize); - if (pxRingbuffer->pucHead == NULL) { + Ringbuffer_t *pxNewRingbuffer = calloc(1, sizeof(Ringbuffer_t)); + uint8_t *pucRingbufferStorage = malloc(xBufferSize); + if (pxNewRingbuffer == NULL || pucRingbufferStorage == NULL) { goto err; } - //Initialize values - pxRingbuffer->xSize = xBufferSize; - pxRingbuffer->pucTail = pxRingbuffer->pucHead + xBufferSize; - pxRingbuffer->pucFree = pxRingbuffer->pucHead; - pxRingbuffer->pucRead = pxRingbuffer->pucHead; - pxRingbuffer->pucWrite = pxRingbuffer->pucHead; - pxRingbuffer->xItemsWaiting = 0; - pxRingbuffer->xFreeSpaceSemaphore = xSemaphoreCreateBinary(); - pxRingbuffer->xItemsBufferedSemaphore = xSemaphoreCreateBinary(); - pxRingbuffer->uxRingbufferFlags = 0; - - //Initialize type dependent values and function pointers - if (xBufferType == RINGBUF_TYPE_NOSPLIT) { - pxRingbuffer->xCheckItemFits = prvCheckItemFitsDefault; - pxRingbuffer->vCopyItem = prvCopyItemNoSplit; - pxRingbuffer->pvGetItem = prvGetItemDefault; - pxRingbuffer->vReturnItem = prvReturnItemDefault; - /* - * Buffer lengths are always aligned. No-split buffer (read/write/free) - * pointers are also always aligned. Therefore worse case scenario is - * the write pointer is at the most aligned halfway point. - */ - pxRingbuffer->xMaxItemSize = rbALIGN_SIZE(pxRingbuffer->xSize / 2) - rbHEADER_SIZE; - pxRingbuffer->xGetCurMaxSize = prvGetCurMaxSizeNoSplit; - } else if (xBufferType == RINGBUF_TYPE_ALLOWSPLIT) { - pxRingbuffer->uxRingbufferFlags |= rbALLOW_SPLIT_FLAG; - pxRingbuffer->xCheckItemFits = prvCheckItemFitsDefault; - pxRingbuffer->vCopyItem = prvCopyItemAllowSplit; - pxRingbuffer->pvGetItem = prvGetItemDefault; - pxRingbuffer->vReturnItem = prvReturnItemDefault; - //Worst case an item is split into two, incurring two headers of overhead - pxRingbuffer->xMaxItemSize = pxRingbuffer->xSize - (sizeof(ItemHeader_t) * 2); - pxRingbuffer->xGetCurMaxSize = prvGetCurMaxSizeAllowSplit; - } else if (xBufferType == RINGBUF_TYPE_BYTEBUF) { - pxRingbuffer->uxRingbufferFlags |= rbBYTE_BUFFER_FLAG; - pxRingbuffer->xCheckItemFits = prvCheckItemFitsByteBuffer; - pxRingbuffer->vCopyItem = prvCopyItemByteBuf; - pxRingbuffer->pvGetItem = prvGetItemByteBuf; - pxRingbuffer->vReturnItem = prvReturnItemByteBuf; - //Byte buffers do not incur any overhead - pxRingbuffer->xMaxItemSize = pxRingbuffer->xSize; - pxRingbuffer->xGetCurMaxSize = prvGetCurMaxSizeByteBuf; - } else { - //Unsupported type - configASSERT(0); - } - - if (pxRingbuffer->xFreeSpaceSemaphore == NULL || pxRingbuffer->xItemsBufferedSemaphore == NULL) { + //Initialize Semaphores +#if ( configSUPPORT_STATIC_ALLOCATION == 1) + //We don't use the handles for static semaphores, and xSemaphoreCreateBinaryStatic will never fail thus no need to check static case + xSemaphoreCreateBinaryStatic(&(pxNewRingbuffer->xTransSemStatic)); + xSemaphoreCreateBinaryStatic(&(pxNewRingbuffer->xRecvSemStatic)); +#else + pxNewRingbuffer->xTransSemHandle = xSemaphoreCreateBinary(); + pxNewRingbuffer->xRecvSemHandle = xSemaphoreCreateBinary(); + if (pxNewRingbuffer->xTransSemHandle == NULL || pxNewRingbuffer->xRecvSemHandle == NULL) { + if (pxNewRingbuffer->xTransSemHandle != NULL) { + vSemaphoreDelete(pxNewRingbuffer->xTransSemHandle); + } + if (pxNewRingbuffer->xRecvSemHandle != NULL) { + vSemaphoreDelete(pxNewRingbuffer->xRecvSemHandle); + } goto err; } - xSemaphoreGive(pxRingbuffer->xFreeSpaceSemaphore); - vPortCPUInitializeMutex(&pxRingbuffer->mux); +#endif - return (RingbufHandle_t)pxRingbuffer; + prvInitializeNewRingbuffer(xBufferSize, xBufferType, pxNewRingbuffer, pucRingbufferStorage); + return (RingbufHandle_t)pxNewRingbuffer; err: - //Some error has happened. Free/destroy all allocated things and return NULL. - if (pxRingbuffer) { - free(pxRingbuffer->pucHead); - if (pxRingbuffer->xFreeSpaceSemaphore) { - vSemaphoreDelete(pxRingbuffer->xFreeSpaceSemaphore); - } - if (pxRingbuffer->xItemsBufferedSemaphore) { - vSemaphoreDelete(pxRingbuffer->xItemsBufferedSemaphore); - } - } - free(pxRingbuffer); + //An error has occurred, Free memory and return NULL + free(pxNewRingbuffer); + free(pucRingbufferStorage); return NULL; } @@ -741,7 +827,34 @@ RingbufHandle_t xRingbufferCreateNoSplit(size_t xItemSize, size_t xItemNum) return xRingbufferCreate((rbALIGN_SIZE(xItemSize) + rbHEADER_SIZE) * xItemNum, RINGBUF_TYPE_NOSPLIT); } -BaseType_t xRingbufferSend(RingbufHandle_t xRingbuffer, const void *pvItem, size_t xItemSize, TickType_t xTicksToWait) +#if ( configSUPPORT_STATIC_ALLOCATION == 1 ) +RingbufHandle_t xRingbufferCreateStatic(size_t xBufferSize, + RingbufferType_t xBufferType, + uint8_t *pucRingbufferStorage, + StaticRingbuffer_t *pxStaticRingbuffer) +{ + //Check arguments + configASSERT(xBufferSize > 0); + configASSERT(xBufferType < RINGBUF_TYPE_MAX); + configASSERT(pucRingbufferStorage != NULL && pxStaticRingbuffer != NULL); + if (xBufferType != RINGBUF_TYPE_BYTEBUF) { + //No-split/allow-split buffer sizes must be 32-bit aligned + configASSERT(rbCHECK_ALIGNED(xBufferSize)); + } + + Ringbuffer_t *pxNewRingbuffer = (Ringbuffer_t *)pxStaticRingbuffer; + xSemaphoreCreateBinaryStatic(&(pxNewRingbuffer->xTransSemStatic)); + xSemaphoreCreateBinaryStatic(&(pxNewRingbuffer->xRecvSemStatic)); + prvInitializeNewRingbuffer(xBufferSize, xBufferType, pxNewRingbuffer, pucRingbufferStorage); + pxNewRingbuffer->uxRingbufferFlags |= rbBUFFER_STATIC_FLAG; + return (RingbufHandle_t)pxNewRingbuffer; +} +#endif + +BaseType_t xRingbufferSend(RingbufHandle_t xRingbuffer, + const void *pvItem, + size_t xItemSize, + TickType_t xTicksToWait) { //Check arguments Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer; @@ -761,7 +874,7 @@ BaseType_t xRingbufferSend(RingbufHandle_t xRingbuffer, const void *pvItem, size TickType_t xTicksRemaining = xTicksToWait; while (xTicksRemaining <= xTicksToWait) { //xTicksToWait will underflow once xTaskGetTickCount() > ticks_end //Block until more free space becomes available or timeout - if (xSemaphoreTake(pxRingbuffer->xFreeSpaceSemaphore, xTicksRemaining) != pdTRUE) { + if (xSemaphoreTake(rbGET_TX_SEM_HANDLE(pxRingbuffer), xTicksRemaining) != pdTRUE) { xReturn = pdFALSE; break; } @@ -791,15 +904,18 @@ BaseType_t xRingbufferSend(RingbufHandle_t xRingbuffer, const void *pvItem, size if (xReturn == pdTRUE) { //Indicate item was successfully sent - xSemaphoreGive(pxRingbuffer->xItemsBufferedSemaphore); + xSemaphoreGive(rbGET_RX_SEM_HANDLE(pxRingbuffer)); } if (xReturnSemaphore == pdTRUE) { - xSemaphoreGive(pxRingbuffer->xFreeSpaceSemaphore); //Give back semaphore so other tasks can send + xSemaphoreGive(rbGET_TX_SEM_HANDLE(pxRingbuffer)); //Give back semaphore so other tasks can send } return xReturn; } -BaseType_t xRingbufferSendFromISR(RingbufHandle_t xRingbuffer, const void *pvItem, size_t xItemSize, BaseType_t *pxHigherPriorityTaskWoken) +BaseType_t xRingbufferSendFromISR(RingbufHandle_t xRingbuffer, + const void *pvItem, + size_t xItemSize, + BaseType_t *pxHigherPriorityTaskWoken) { //Check arguments Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer; @@ -830,10 +946,10 @@ BaseType_t xRingbufferSendFromISR(RingbufHandle_t xRingbuffer, const void *pvIte if (xReturn == pdTRUE) { //Indicate item was successfully sent - xSemaphoreGiveFromISR(pxRingbuffer->xItemsBufferedSemaphore, pxHigherPriorityTaskWoken); + xSemaphoreGiveFromISR(rbGET_RX_SEM_HANDLE(pxRingbuffer), pxHigherPriorityTaskWoken); } if (xReturnSemaphore == pdTRUE) { - xSemaphoreGiveFromISR(pxRingbuffer->xFreeSpaceSemaphore, pxHigherPriorityTaskWoken); //Give back semaphore so other tasks can send + xSemaphoreGiveFromISR(rbGET_TX_SEM_HANDLE(pxRingbuffer), pxHigherPriorityTaskWoken); //Give back semaphore so other tasks can send } return xReturn; } @@ -876,7 +992,12 @@ void *xRingbufferReceiveFromISR(RingbufHandle_t xRingbuffer, size_t *pxItemSize) } } -BaseType_t xRingbufferReceiveSplit(RingbufHandle_t xRingbuffer, void **ppvHeadItem, void **ppvTailItem, size_t *pxHeadItemSize, size_t *pxTailItemSize, TickType_t xTicksToWait) +BaseType_t xRingbufferReceiveSplit(RingbufHandle_t xRingbuffer, + void **ppvHeadItem, + void **ppvTailItem, + size_t *pxHeadItemSize, + size_t *pxTailItemSize, + TickType_t xTicksToWait) { //Check arguments Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer; @@ -911,7 +1032,11 @@ BaseType_t xRingbufferReceiveSplit(RingbufHandle_t xRingbuffer, void **ppvHeadIt } } -BaseType_t xRingbufferReceiveSplitFromISR(RingbufHandle_t xRingbuffer, void **ppvHeadItem, void **ppvTailItem, size_t *pxHeadItemSize, size_t *pxTailItemSize) +BaseType_t xRingbufferReceiveSplitFromISR(RingbufHandle_t xRingbuffer, + void **ppvHeadItem, + void **ppvTailItem, + size_t *pxHeadItemSize, + size_t *pxTailItemSize) { //Check arguments Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer; @@ -945,7 +1070,10 @@ BaseType_t xRingbufferReceiveSplitFromISR(RingbufHandle_t xRingbuffer, void **pp } } -void *xRingbufferReceiveUpTo(RingbufHandle_t xRingbuffer, size_t *pxItemSize, TickType_t xTicksToWait, size_t xMaxSize) +void *xRingbufferReceiveUpTo(RingbufHandle_t xRingbuffer, + size_t *pxItemSize, + TickType_t xTicksToWait, + size_t xMaxSize) { //Check arguments Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer; @@ -1000,7 +1128,7 @@ void vRingbufferReturnItem(RingbufHandle_t xRingbuffer, void *pvItem) portENTER_CRITICAL(&pxRingbuffer->mux); pxRingbuffer->vReturnItem(pxRingbuffer, (uint8_t *)pvItem); portEXIT_CRITICAL(&pxRingbuffer->mux); - xSemaphoreGive(pxRingbuffer->xFreeSpaceSemaphore); + xSemaphoreGive(rbGET_TX_SEM_HANDLE(pxRingbuffer)); } void vRingbufferReturnItemFromISR(RingbufHandle_t xRingbuffer, void *pvItem, BaseType_t *pxHigherPriorityTaskWoken) @@ -1012,7 +1140,7 @@ void vRingbufferReturnItemFromISR(RingbufHandle_t xRingbuffer, void *pvItem, Bas portENTER_CRITICAL_ISR(&pxRingbuffer->mux); pxRingbuffer->vReturnItem(pxRingbuffer, (uint8_t *)pvItem); portEXIT_CRITICAL_ISR(&pxRingbuffer->mux); - xSemaphoreGiveFromISR(pxRingbuffer->xFreeSpaceSemaphore, pxHigherPriorityTaskWoken); + xSemaphoreGiveFromISR(rbGET_TX_SEM_HANDLE(pxRingbuffer), pxHigherPriorityTaskWoken); } void vRingbufferDelete(RingbufHandle_t xRingbuffer) @@ -1020,15 +1148,16 @@ void vRingbufferDelete(RingbufHandle_t xRingbuffer) Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer; configASSERT(pxRingbuffer); - if (pxRingbuffer) { - free(pxRingbuffer->pucHead); - if (pxRingbuffer->xFreeSpaceSemaphore) { - vSemaphoreDelete(pxRingbuffer->xFreeSpaceSemaphore); - } - if (pxRingbuffer->xItemsBufferedSemaphore) { - vSemaphoreDelete(pxRingbuffer->xItemsBufferedSemaphore); - } + vSemaphoreDelete(rbGET_TX_SEM_HANDLE(pxRingbuffer)); + vSemaphoreDelete(rbGET_RX_SEM_HANDLE(pxRingbuffer)); + +#if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + if (pxRingbuffer->uxRingbufferFlags & rbBUFFER_STATIC_FLAG) { + //Ring buffer was statically allocated, no need to free + return; } +#endif + free(pxRingbuffer->pucHead); free(pxRingbuffer); } @@ -1059,11 +1188,11 @@ BaseType_t xRingbufferAddToQueueSetRead(RingbufHandle_t xRingbuffer, QueueSetHan BaseType_t xReturn; portENTER_CRITICAL(&pxRingbuffer->mux); //Cannot add semaphore to queue set if semaphore is not empty. Temporarily hold semaphore - BaseType_t xHoldSemaphore = xSemaphoreTake(pxRingbuffer->xItemsBufferedSemaphore, 0); - xReturn = xQueueAddToSet(pxRingbuffer->xItemsBufferedSemaphore, xQueueSet); + BaseType_t xHoldSemaphore = xSemaphoreTake(rbGET_RX_SEM_HANDLE(pxRingbuffer), 0); + xReturn = xQueueAddToSet(rbGET_RX_SEM_HANDLE(pxRingbuffer), xQueueSet); if (xHoldSemaphore == pdTRUE) { //Return semaphore if temporarily held - configASSERT(xSemaphoreGive(pxRingbuffer->xItemsBufferedSemaphore) == pdTRUE); + configASSERT(xSemaphoreGive(rbGET_RX_SEM_HANDLE(pxRingbuffer)) == pdTRUE); } portEXIT_CRITICAL(&pxRingbuffer->mux); return xReturn; @@ -1074,7 +1203,7 @@ BaseType_t xRingbufferCanRead(RingbufHandle_t xRingbuffer, QueueSetMemberHandle_ //Check if the selected queue set member is the ring buffer's read semaphore Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer; configASSERT(pxRingbuffer); - return (pxRingbuffer->xItemsBufferedSemaphore == xMember) ? pdTRUE : pdFALSE; + return (rbGET_RX_SEM_HANDLE(pxRingbuffer) == xMember) ? pdTRUE : pdFALSE; } BaseType_t xRingbufferRemoveFromQueueSetRead(RingbufHandle_t xRingbuffer, QueueSetHandle_t xQueueSet) @@ -1085,17 +1214,21 @@ BaseType_t xRingbufferRemoveFromQueueSetRead(RingbufHandle_t xRingbuffer, QueueS BaseType_t xReturn; portENTER_CRITICAL(&pxRingbuffer->mux); //Cannot remove semaphore from queue set if semaphore is not empty. Temporarily hold semaphore - BaseType_t xHoldSemaphore = xSemaphoreTake(pxRingbuffer->xItemsBufferedSemaphore, 0); - xReturn = xQueueRemoveFromSet(pxRingbuffer->xItemsBufferedSemaphore, xQueueSet); + BaseType_t xHoldSemaphore = xSemaphoreTake(rbGET_RX_SEM_HANDLE(pxRingbuffer), 0); + xReturn = xQueueRemoveFromSet(rbGET_RX_SEM_HANDLE(pxRingbuffer), xQueueSet); if (xHoldSemaphore == pdTRUE) { //Return semaphore if temporarily held - configASSERT(xSemaphoreGive(pxRingbuffer->xItemsBufferedSemaphore) == pdTRUE); + configASSERT(xSemaphoreGive(rbGET_RX_SEM_HANDLE(pxRingbuffer)) == pdTRUE); } portEXIT_CRITICAL(&pxRingbuffer->mux); return xReturn; } -void vRingbufferGetInfo(RingbufHandle_t xRingbuffer, UBaseType_t *uxFree, UBaseType_t *uxRead, UBaseType_t *uxWrite, UBaseType_t *uxItemsWaiting) +void vRingbufferGetInfo(RingbufHandle_t xRingbuffer, + UBaseType_t *uxFree, + UBaseType_t *uxRead, + UBaseType_t *uxWrite, + UBaseType_t *uxItemsWaiting) { Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer; configASSERT(pxRingbuffer); @@ -1127,58 +1260,3 @@ void xRingbufferPrintInfo(RingbufHandle_t xRingbuffer) pxRingbuffer->pucWrite - pxRingbuffer->pucHead); } -/* --------------------------------- Deprecated Functions ------------------------------ */ -//Todo: Remove the following deprecated functions in next release - -bool xRingbufferIsNextItemWrapped(RingbufHandle_t xRingbuffer) -{ - //This function is deprecated, use xRingbufferReceiveSplit() instead - Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer; - configASSERT(pxRingbuffer); - bool is_wrapped; - - portENTER_CRITICAL(&pxRingbuffer->mux); - ItemHeader_t *xHeader = (ItemHeader_t *)pxRingbuffer->pucRead; - is_wrapped = xHeader->uxItemFlags & rbITEM_SPLIT_FLAG; - portEXIT_CRITICAL(&pxRingbuffer->mux); - return is_wrapped; -} - -BaseType_t xRingbufferAddToQueueSetWrite(RingbufHandle_t xRingbuffer, QueueSetHandle_t xQueueSet) -{ - //This function is deprecated. QueueSetWrite no longer supported - Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer; - configASSERT(pxRingbuffer); - - BaseType_t xReturn; - portENTER_CRITICAL(&pxRingbuffer->mux); - //Cannot add semaphore to queue set if semaphore is not empty. Temporary hold semaphore - BaseType_t xHoldSemaphore = xSemaphoreTake(pxRingbuffer->xFreeSpaceSemaphore, 0); - xReturn = xQueueAddToSet(pxRingbuffer->xFreeSpaceSemaphore, xQueueSet); - if (xHoldSemaphore == pdTRUE) { - //Return semaphore is temporarily held - configASSERT(xSemaphoreGive(pxRingbuffer->xFreeSpaceSemaphore) == pdTRUE); - } - portEXIT_CRITICAL(&pxRingbuffer->mux); - return xReturn; -} - -BaseType_t xRingbufferRemoveFromQueueSetWrite(RingbufHandle_t xRingbuffer, QueueSetHandle_t xQueueSet) -{ - //This function is deprecated. QueueSetWrite no longer supported - Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer; - configASSERT(pxRingbuffer); - - BaseType_t xReturn; - portENTER_CRITICAL(&pxRingbuffer->mux); - //Cannot remove semaphore from queue set if semaphore is not empty. Temporary hold semaphore - BaseType_t xHoldSemaphore = xSemaphoreTake(pxRingbuffer->xFreeSpaceSemaphore, 0); - xReturn = xQueueRemoveFromSet(pxRingbuffer->xFreeSpaceSemaphore, xQueueSet); - if (xHoldSemaphore == pdTRUE) { - //Return semaphore is temporarily held - configASSERT(xSemaphoreGive(pxRingbuffer->xFreeSpaceSemaphore) == pdTRUE); - } - portEXIT_CRITICAL(&pxRingbuffer->mux); - return xReturn; -} - diff --git a/components/esp_ringbuf/test/test_ringbuf.c b/components/esp_ringbuf/test/test_ringbuf.c index 4acf63ba8..d48f54d27 100644 --- a/components/esp_ringbuf/test/test_ringbuf.c +++ b/components/esp_ringbuf/test/test_ringbuf.c @@ -6,6 +6,7 @@ #include "freertos/semphr.h" #include "freertos/ringbuf.h" #include "driver/timer.h" +#include "esp_heap_caps.h" #include "esp_spi_flash.h" #include "unity.h" #include "test_utils.h" @@ -165,7 +166,7 @@ static void receive_check_and_return_item_byte_buffer(RingbufHandle_t handle, co * 4) Receive and check the wrapped item */ -TEST_CASE("Test ring buffer No-Split", "[freertos]") +TEST_CASE("Test ring buffer No-Split", "[esp_ringbuf]") { //Create buffer RingbufHandle_t buffer_handle = xRingbufferCreate(BUFFER_SIZE, RINGBUF_TYPE_NOSPLIT); @@ -196,7 +197,7 @@ TEST_CASE("Test ring buffer No-Split", "[freertos]") vRingbufferDelete(buffer_handle); } -TEST_CASE("Test ring buffer Allow-Split", "[freertos]") +TEST_CASE("Test ring buffer Allow-Split", "[esp_ringbuf]") { //Create buffer RingbufHandle_t buffer_handle = xRingbufferCreate(BUFFER_SIZE, RINGBUF_TYPE_ALLOWSPLIT); @@ -227,7 +228,7 @@ TEST_CASE("Test ring buffer Allow-Split", "[freertos]") vRingbufferDelete(buffer_handle); } -TEST_CASE("Test ring buffer Byte Buffer", "[freertos]") +TEST_CASE("Test ring buffer Byte Buffer", "[esp_ringbuf]") { //Create buffer RingbufHandle_t buffer_handle = xRingbufferCreate(BUFFER_SIZE, RINGBUF_TYPE_BYTEBUF); @@ -304,7 +305,7 @@ static void queue_set_receiving_task(void *queue_set_handle) vTaskDelete(NULL); } -TEST_CASE("Test ring buffer with queue sets", "[freertos]") +TEST_CASE("Test ring buffer with queue sets", "[esp_ringbuf]") { QueueSetHandle_t queue_set = xQueueCreateSet(NO_OF_RB_TYPES); done_sem = xSemaphoreCreateBinary(); @@ -421,7 +422,7 @@ static void cleanup_timer() esp_intr_free(ringbuffer_isr_handle); } -TEST_CASE("Test ring buffer ISR", "[freertos]") +TEST_CASE("Test ring buffer ISR", "[esp_ringbuf]") { for (int i = 0; i < NO_OF_RB_TYPES; i++) { buffer_handles[i] = xRingbufferCreate(BUFFER_SIZE, i); @@ -461,11 +462,12 @@ static const char continuous_data[] = {"A_very_long_string_that_will_be_split_in "be_increased_over_multiple_iterations_in_this" "_test"}; #define CONT_DATA_LEN sizeof(continuous_data) -#define CONT_DATA_TEST_BUFF_LEN (CONT_DATA_LEN/2) //This will guarantee that the buffer must do a wrap around at some point +//32-bit aligned size that guarantees a wrap around at some point +#define CONT_DATA_TEST_BUFF_LEN (((CONT_DATA_LEN/2) + 0x03) & ~0x3) typedef struct { RingbufHandle_t buffer; - ringbuf_type_t type; + RingbufferType_t type; } task_args_t; static SemaphoreHandle_t tasks_done; @@ -494,7 +496,7 @@ static void send_to_buffer(RingbufHandle_t buffer, size_t max_item_size) } } -static void read_from_buffer(RingbufHandle_t buffer, ringbuf_type_t buf_type, size_t max_rec_size) +static void read_from_buffer(RingbufHandle_t buffer, RingbufferType_t buf_type, size_t max_rec_size) { for (int iter = 0; iter < SMP_TEST_ITERATIONS; iter++) { size_t bytes_rec = 0; //Number of data bytes received in this iteration @@ -568,20 +570,33 @@ static void rec_task(void *args) vTaskDelete(NULL); } -TEST_CASE("Test ring buffer SMP", "[freertos]") +static void setup() { - ets_printf("size of buf %d\n", CONT_DATA_LEN); + ets_printf("Size of test data: %d\n", CONT_DATA_LEN); tx_done = xSemaphoreCreateBinary(); //Semaphore to indicate send is done for a particular iteration rx_done = xSemaphoreCreateBinary(); //Semaphore to indicate receive is done for a particular iteration - tasks_done = xSemaphoreCreateBinary(); //Semaphore used to to indicate send and receive tasks completed running + tasks_done = xSemaphoreCreateBinary(); //Semaphore used to to indicate send and receive tasks completed running srand(SRAND_SEED); //Seed RNG +} +static void cleanup() +{ + //Cleanup + vSemaphoreDelete(tx_done); + vSemaphoreDelete(rx_done); + vSemaphoreDelete(tasks_done); +} + +TEST_CASE("Test ring buffer SMP", "[esp_ringbuf]") +{ + setup(); //Iterate through buffer types (No split, split, then byte buff) - for (ringbuf_type_t buf_type = 0; buf_type <= RINGBUF_TYPE_BYTEBUF; buf_type++) { + for (RingbufferType_t buf_type = 0; buf_type < RINGBUF_TYPE_MAX; buf_type++) { //Create buffer task_args_t task_args; task_args.buffer = xRingbufferCreate(CONT_DATA_TEST_BUFF_LEN, buf_type); //Create buffer of selected type task_args.type = buf_type; + TEST_ASSERT_MESSAGE(task_args.buffer != NULL, "Failed to create ring buffer"); for (int prior_mod = -1; prior_mod < 2; prior_mod++) { //Test different relative priorities //Test every permutation of core affinity @@ -600,13 +615,60 @@ TEST_CASE("Test ring buffer SMP", "[freertos]") vRingbufferDelete(task_args.buffer); vTaskDelay(10); } - - //Cleanup - vSemaphoreDelete(tx_done); - vSemaphoreDelete(rx_done); - vSemaphoreDelete(tasks_done); + cleanup(); } +#if ( configSUPPORT_STATIC_ALLOCATION == 1 ) +TEST_CASE("Test static ring buffer SMP", "[esp_ringbuf]") +{ + setup(); + //Iterate through buffer types (No split, split, then byte buff) + for (RingbufferType_t buf_type = 0; buf_type < RINGBUF_TYPE_MAX; buf_type++) { + StaticRingbuffer_t *buffer_struct; + uint8_t *buffer_storage; + //Allocate memory and create semaphores +#if CONFIG_SPIRAM_USE_CAPS_ALLOC //When SPIRAM can only be allocated using heap_caps_malloc() + buffer_struct = (StaticRingbuffer_t *)heap_caps_malloc(sizeof(StaticRingbuffer_t), MALLOC_CAP_SPIRAM); + buffer_storage = (uint8_t *)heap_caps_malloc(sizeof(uint8_t)*CONT_DATA_TEST_BUFF_LEN, MALLOC_CAP_SPIRAM); +#else //Case where SPIRAM is disabled or when SPIRAM is allocatable through malloc() + buffer_struct = (StaticRingbuffer_t *)malloc(sizeof(StaticRingbuffer_t)); + buffer_storage = (uint8_t *)malloc(sizeof(uint8_t)*CONT_DATA_TEST_BUFF_LEN); +#endif + TEST_ASSERT(buffer_struct != NULL && buffer_storage != NULL); + + //Create buffer + task_args_t task_args; + task_args.buffer = xRingbufferCreateStatic(CONT_DATA_TEST_BUFF_LEN, buf_type, buffer_storage, buffer_struct); //Create buffer of selected type + task_args.type = buf_type; + TEST_ASSERT_MESSAGE(task_args.buffer != NULL, "Failed to create ring buffer"); + + for (int prior_mod = -1; prior_mod < 2; prior_mod++) { //Test different relative priorities + //Test every permutation of core affinity + for (int send_core = 0; send_core < portNUM_PROCESSORS; send_core++) { + for (int rec_core = 0; rec_core < portNUM_PROCESSORS; rec_core ++) { + ets_printf("Type: %d, PM: %d, SC: %d, RC: %d\n", buf_type, prior_mod, send_core, rec_core); + xTaskCreatePinnedToCore(send_task, "send tsk", 2048, (void *)&task_args, 10 + prior_mod, NULL, send_core); + xTaskCreatePinnedToCore(rec_task, "rec tsk", 2048, (void *)&task_args, 10, NULL, rec_core); + xSemaphoreTake(tasks_done, portMAX_DELAY); + vTaskDelay(5); //Allow idle to clean up + } + } + } + + //Delete ring buffer + vRingbufferDelete(task_args.buffer); + + //Deallocate memory + free(buffer_storage); + free(buffer_struct); + vTaskDelay(10); + } + cleanup(); +} +#endif + +/* -------------------------- Test ring buffer IRAM ------------------------- */ + static IRAM_ATTR __attribute__((noinline)) bool iram_ringbuf_test() { bool result = true; @@ -621,7 +683,7 @@ static IRAM_ATTR __attribute__((noinline)) bool iram_ringbuf_test() return result; } -TEST_CASE("Test ringbuffer functions work with flash cache disabled", "[freertos]") +TEST_CASE("Test ringbuffer functions work with flash cache disabled", "[esp_ringbuf]") { TEST_ASSERT( iram_ringbuf_test() ); } diff --git a/docs/en/api-reference/system/freertos_additions.rst b/docs/en/api-reference/system/freertos_additions.rst index 969a7ce14..337bf3bb2 100644 --- a/docs/en/api-reference/system/freertos_additions.rst +++ b/docs/en/api-reference/system/freertos_additions.rst @@ -323,6 +323,51 @@ The following example demonstrates queue set usage with ring buffers. ... } +Ring Buffers with Static Allocation +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The :cpp:func:`xRingbufferCreateStatic` can be used to create ring buffers with specific memory requirements (such as a ring buffer being allocated in external RAM). All blocks of memory used by a ring buffer must be manually allocated beforehand then passed to the :cpp:func:`xRingbufferCreateStatic` to be initialized as a ring buffer. These blocks include the following: + +- The ring buffer's data structure of type :cpp:type:`StaticRingbuffer_t` +- The ring buffer's storage area of size ``xBufferSize``. Note that ``xBufferSize`` must be 32-bit aligned for no-split/allow-split buffers. + +The manner in which these blocks are allocated will depend on the users requirements (e.g. all blocks being statically declared, or dynamically allocated with specific capabilities such as external RAM). + +.. note:: + The :ref:`CONFIG_FREERTOS_SUPPORT_STATIC_ALLOCATION` option must be enabled in `menuconfig` for statically allocated ring buffers to be available. + +.. note:: + When deleting a ring buffer created via :cpp:func:`xRingbufferCreateStatic`, + the function :cpp:func:`vRingbufferDelete` will not free any of the memory blocks. This must be done manually by the user after :cpp:func:`vRingbufferDelete` is called. + +The code snippet below demonstrates a ring buffer being allocated entirely in external RAM. + +.. code-block:: c + + #include "freertos/ringbuf.h" + #include "freertos/semphr.h" + #include "esp_heap_caps.h" + + #define BUFFER_SIZE 400 //32-bit aligned size + #define BUFFER_TYPE RINGBUF_TYPE_NOSPLIT + ... + + //Allocate ring buffer data structure and storage area into external RAM + StaticRingbuffer_t *buffer_struct = (StaticRingbuffer_t *)heap_caps_malloc(sizeof(StaticRingbuffer_t), MALLOC_CAP_SPIRAM); + uint8_t *buffer_storage = (uint8_t *)heap_caps_malloc(sizeof(uint8_t)*BUFFER_SIZE, MALLOC_CAP_SPIRAM); + + //Create a ring buffer with manually allocated memory + RingbufHandle_t handle = xRingbufferCreateStatic(BUFFER_SIZE, BUFFER_TYPE, buffer_storage, buffer_struct); + + ... + + //Delete the ring buffer after used + vRingbufferDelete(handle); + + //Manually free all blocks of memory + free(buffer_struct); + free(buffer_storage); + Ring Buffer API Reference -------------------------