From cd5fb054c74b900ee44258763989247980f43651 Mon Sep 17 00:00:00 2001 From: Piyush Shah Date: Mon, 20 Nov 2017 19:23:25 +0530 Subject: [PATCH 1/3] freertos/ringbuf: Add an API xRingbufferGetCurFreeSize() to fetch current free size available The earlier available API (xRingbufferGetMaxItemSize())just gives a static max entry value possible for given ring buffer. There was a feature request for an API which could provide a real time available buffer size. See below: https://github.com/espressif/esp-idf/issues/806 Signed-off-by: Piyush Shah --- .../freertos/include/freertos/ringbuf.h | 15 +++++ components/freertos/ringbuf.c | 66 ++++++++++++++++++- 2 files changed, 80 insertions(+), 1 deletion(-) diff --git a/components/freertos/include/freertos/ringbuf.h b/components/freertos/include/freertos/ringbuf.h index 88e570aa59..f0cf82fca5 100644 --- a/components/freertos/include/freertos/ringbuf.h +++ b/components/freertos/include/freertos/ringbuf.h @@ -94,6 +94,21 @@ void vRingbufferDelete(RingbufHandle_t ringbuf); */ size_t xRingbufferGetMaxItemSize(RingbufHandle_t ringbuf); +/** + * @brief Get current free size available in the buffer + * + * This gives the real time free space available in the ring buffer. So basically, + * this will be the maximum size of the entry that can be sent into the buffer. + * + * @note This API is not thread safe. So, if multiple threads are accessing the same + * ring buffer, it is the application's responsibility to ensure atomic access to this + * API and the subsequent Send + * + * @param ringbuf - Ring buffer to query + * + * @return Current free size, in bytes, available for an entry + */ +size_t xRingbufferGetCurFreeSize(RingbufHandle_t ringbuf); /** * @brief Insert an item into the ring buffer diff --git a/components/freertos/ringbuf.c b/components/freertos/ringbuf.c index d7ee790a5e..f79bf3ed96 100644 --- a/components/freertos/ringbuf.c +++ b/components/freertos/ringbuf.c @@ -53,6 +53,7 @@ struct ringbuf_t { BaseType_t (*copyItemToRingbufImpl)(ringbuf_t *rb, uint8_t *buffer, size_t buffer_size); uint8_t *(*getItemFromRingbufImpl)(ringbuf_t *rb, size_t *length, int wanted_length); void (*returnItemToRingbufImpl)(ringbuf_t *rb, void *item); + size_t (*getFreeSizeImpl)(ringbuf_t *rb); }; @@ -82,7 +83,6 @@ static int ringbufferFreeMem(ringbuf_t *rb) return free_size-1; } - //Copies a single item to the ring buffer; refuses to split items. Assumes there is space in the ringbuffer and //the ringbuffer is locked. Increases write_ptr to the next item. Returns pdTRUE on //success, pdFALSE if it can't make the item fit and the calling routine needs to retry @@ -415,6 +415,67 @@ void xRingbufferPrintInfo(RingbufHandle_t ringbuf) } +size_t xRingbufferGetCurFreeSize(RingbufHandle_t ringbuf) +{ + ringbuf_t *rb=(ringbuf_t *)ringbuf; + configASSERT(rb); + configASSERT(rb->getFreeSizeImpl); + int free_size = rb->getFreeSizeImpl(rb); + //Reserve one byte. If we do not do this and the entire buffer is filled, we get a situation + //where read_ptr == free_ptr, messing up the next calculation. + return free_size - 1; +} + +static size_t getCurFreeSizeByteBuf(ringbuf_t *rb) +{ + //Return whatever space is available depending on relative positions of + //the free pointer and write pointer. There is no overhead of headers in + //this mode + int free_size = rb->free_ptr-rb->write_ptr; + if (free_size <= 0) + free_size += rb->size; + return free_size; +} + +static size_t getCurFreeSizeAllowSplit(ringbuf_t *rb) +{ + int free_size; + //If Both, the write and free pointer are at the start. Hence, the entire buffer + //is available (minus the space for the header) + if (rb->write_ptr == rb->free_ptr && rb->write_ptr == rb->data) { + free_size = rb->size - sizeof(buf_entry_hdr_t); + } else if (rb->write_ptr < rb->free_ptr) { + //Else if the free pointer is beyond the write pointer, only the space between + //them would be available (minus the space for the header) + free_size = rb->free_ptr - rb->write_ptr - sizeof(buf_entry_hdr_t); + } else { + //Else the data can wrap around and 2 headers will be required + free_size = rb->free_ptr - rb->write_ptr + rb->size - (2 * sizeof(buf_entry_hdr_t)); + } + return free_size; +} + +static size_t getCurFreeSizeNoSplit(ringbuf_t *rb) +{ + int free_size; + //If the free pointer is beyond the write pointer, only the space between + //them would be available + if (rb->write_ptr < rb->free_ptr) { + free_size = rb->free_ptr - rb->write_ptr; + } else { + //Else check which one is bigger amongst the below 2 + //1) Space from the write pointer to the end of buffer + int size1 = rb->data + rb->size - rb->write_ptr; + //2) Space from the start of buffer to the free pointer + int size2 = rb->free_ptr - rb->data; + //And then select the larger of the two + free_size = size1 > size2 ? size1 : size2; + } + //In any case, a single header will be used, so subtracting the space that + //would be required for it + return free_size - sizeof(buf_entry_hdr_t); +} + RingbufHandle_t xRingbufferCreate(size_t buf_length, ringbuf_type_t type) { @@ -437,6 +498,7 @@ RingbufHandle_t xRingbufferCreate(size_t buf_length, ringbuf_type_t type) rb->returnItemToRingbufImpl=returnItemToRingbufDefault; //Calculate max item size. Worst case, we need to split an item into two, which means two headers of overhead. rb->maxItemSize=rb->size-(sizeof(buf_entry_hdr_t)*2)-4; + rb->getFreeSizeImpl=getCurFreeSizeAllowSplit; } else if (type==RINGBUF_TYPE_BYTEBUF) { rb->flags|=flag_bytebuf; rb->copyItemToRingbufImpl=copyItemToRingbufByteBuf; @@ -444,6 +506,7 @@ RingbufHandle_t xRingbufferCreate(size_t buf_length, ringbuf_type_t type) rb->returnItemToRingbufImpl=returnItemToRingbufBytebuf; //Calculate max item size. We have no headers and can split anywhere -> size is total size minus one. rb->maxItemSize=rb->size-1; + rb->getFreeSizeImpl=getCurFreeSizeByteBuf; } else if (type==RINGBUF_TYPE_NOSPLIT) { rb->copyItemToRingbufImpl=copyItemToRingbufNoSplit; rb->getItemFromRingbufImpl=getItemFromRingbufDefault; @@ -453,6 +516,7 @@ RingbufHandle_t xRingbufferCreate(size_t buf_length, ringbuf_type_t type) //(item_data-4) bytes of buffer, then we only have (size-(item_data-4) bytes left to fill //with the real item. (item size being header+data) rb->maxItemSize=(rb->size/2)-sizeof(buf_entry_hdr_t)-4; + rb->getFreeSizeImpl=getCurFreeSizeNoSplit; } else { configASSERT(0); } From 3c199b0de65df8f58e79e19ef6e40945f2eefe81 Mon Sep 17 00:00:00 2001 From: Piyush Shah Date: Mon, 20 Nov 2017 20:22:20 +0530 Subject: [PATCH 2/3] freertos/ringbuf: Add an API xRingbufferIsNextItemWrapped() Useful to check if the next item to receive is wrapped or not. This is valid only if the ring buffer is initialised with type RINGBUF_TYPE_ALLOWSPLIT. This is as per the feature request here: https://github.com/espressif/esp-idf/issues/806 Signed-off-by: Piyush Shah --- components/freertos/include/freertos/ringbuf.h | 17 +++++++++++++++++ components/freertos/ringbuf.c | 14 ++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/components/freertos/include/freertos/ringbuf.h b/components/freertos/include/freertos/ringbuf.h index f0cf82fca5..259ce0f16b 100644 --- a/components/freertos/include/freertos/ringbuf.h +++ b/components/freertos/include/freertos/ringbuf.h @@ -110,6 +110,23 @@ size_t xRingbufferGetMaxItemSize(RingbufHandle_t ringbuf); */ size_t xRingbufferGetCurFreeSize(RingbufHandle_t ringbuf); +/** + * @brief Check if the next item is wrapped + * + * This API tells if the next item that is available for a Receive is wrapped + * or not. This is valid only if the ring buffer type is RINGBUF_TYPE_ALLOWSPLIT + * + * @note This API is not thread safe. So, if multiple threads are accessing the same + * ring buffer, it is the application's responsibility to ensure atomic access to this + * API and the subsequent Receive + * + * @param ringbuf - Ring buffer to query + * + * @return true if the next item is wrapped around + * @return false if the next item is not wrapped + */ +bool xRingbufferIsNextItemWrapped(RingbufHandle_t ringbuf); + /** * @brief Insert an item into the ring buffer * diff --git a/components/freertos/ringbuf.c b/components/freertos/ringbuf.c index f79bf3ed96..7ae1723fb9 100644 --- a/components/freertos/ringbuf.c +++ b/components/freertos/ringbuf.c @@ -32,6 +32,7 @@ typedef enum { typedef enum { iflag_free = 1, //Buffer is not read and given back by application, free to overwrite iflag_dummydata = 2, //Data from here to end of ringbuffer is dummy. Restart reading at start of ringbuffer. + iflag_wrap = 4, //Valid for RINGBUF_TYPE_ALLOWSPLIT, indicating that rest of the data is wrapped around } itemflag_t; @@ -203,6 +204,9 @@ static BaseType_t copyItemToRingbufAllowSplit(ringbuf_t *rb, uint8_t *buffer, si if (buffer_size == 0) { rb->write_ptr=rb->data; return pdTRUE; + } else { + /* Indicate the wrapping */ + hdr->flags|=iflag_wrap; } } else { //Huh, only the header fit. Mark as dummy so the receive function doesn't receive @@ -359,6 +363,7 @@ static void returnItemToRingbufDefault(ringbuf_t *rb, void *item) { configASSERT((hdr->flags & iflag_dummydata)==0); configASSERT((hdr->flags & iflag_free)==0); //Mark the buffer as free. + hdr->flags&=~iflag_wrap; hdr->flags|=iflag_free; //Do a cleanup pass. @@ -553,6 +558,15 @@ size_t xRingbufferGetMaxItemSize(RingbufHandle_t ringbuf) return rb->maxItemSize; } +bool xRingbufferIsNextItemWrapped(RingbufHandle_t ringbuf) +{ + ringbuf_t *rb=(ringbuf_t *)ringbuf; + configASSERT(rb); + buf_entry_hdr_t *hdr=(buf_entry_hdr_t *)rb->read_ptr; + return hdr->flags & iflag_wrap; +} + + BaseType_t xRingbufferSend(RingbufHandle_t ringbuf, void *data, size_t dataSize, TickType_t ticks_to_wait) { ringbuf_t *rb=(ringbuf_t *)ringbuf; From 91968ef464626707d161df1e6f1aeadad65ebb07 Mon Sep 17 00:00:00 2001 From: Piyush Shah Date: Thu, 23 Nov 2017 15:39:17 +0530 Subject: [PATCH 3/3] freertos/ringbuf: Added an API xRingbufferCreateNoSplit() This is a wrapper API for creating a Ring Buffer, which ensures that the ringbuffer can hold the given number of items, each item being of the same given length. Signed-off-by: Piyush Shah --- components/freertos/include/freertos/ringbuf.h | 13 +++++++++++++ components/freertos/ringbuf.c | 6 ++++++ 2 files changed, 19 insertions(+) diff --git a/components/freertos/include/freertos/ringbuf.h b/components/freertos/include/freertos/ringbuf.h index 259ce0f16b..0f23a44e6b 100644 --- a/components/freertos/include/freertos/ringbuf.h +++ b/components/freertos/include/freertos/ringbuf.h @@ -77,6 +77,19 @@ typedef enum { */ RingbufHandle_t xRingbufferCreate(size_t buf_length, ringbuf_type_t type); +/** + * @brief Create a ring buffer of type RINGBUF_TYPE_NOSPLIT for a fixed item_size + * + * This API is similar to xRingbufferCreate(), but it will internally allocate + * additional space for the headers. + * + * @param item_size Size of each item to be put into the ring buffer + * @param num_item Maximum number of items the buffer needs to hold simultaneously + * + * @return A RingbufHandle_t handle to the created ringbuffer, or NULL in case of error. + */ +RingbufHandle_t xRingbufferCreateNoSplit(size_t item_size, size_t num_item); + /** * @brief Delete a ring buffer * diff --git a/components/freertos/ringbuf.c b/components/freertos/ringbuf.c index 7ae1723fb9..57ca1c6cda 100644 --- a/components/freertos/ringbuf.c +++ b/components/freertos/ringbuf.c @@ -541,6 +541,12 @@ err: return NULL; } +RingbufHandle_t xRingbufferCreateNoSplit(size_t item_size, size_t num_item) +{ + size_t aligned_size = (item_size+3)&~3; + return xRingbufferCreate((aligned_size + sizeof(buf_entry_hdr_t)) * num_item, RINGBUF_TYPE_NOSPLIT); +} + void vRingbufferDelete(RingbufHandle_t ringbuf) { ringbuf_t *rb=(ringbuf_t *)ringbuf; if (rb) {