]> granicus.if.org Git - esp-idf/commitdiff
freeRTOS/Re-factor ring buffers
authorDarian Leung <darian@espressif.com>
Wed, 18 Apr 2018 17:20:34 +0000 (01:20 +0800)
committerbot <bot@espressif.com>
Mon, 21 May 2018 01:04:58 +0000 (01:04 +0000)
This fixes multiple bugs with ring buffers and re-factors the code. The public
API has not changed, however the underlying implementation have various private
functions have been changed. The following behavioral changes have been made

-   Size of ring buffers for No-Split/Allow-Split buffers will not be rounded
    up to the nearest 32-bit aligned size. This was done to simplify the
    implementation

-   Item size for No-Split/Allow-Split buffers will also be rounded up to the
    nearest 32-bit aligned size.

The following bugs have been fixed

-   In copyItemToRingbufAllowSplit(), when copying an item where the aligned
    size is smaller than the remaining length, the function does not consider
    the case where the true size of the item is less than 4 bytes.

-   The copy functions will automatically wrap around the write pointers when
    the remaining length of the buffer is not large enough to fit a header, but
    does not consider if wrapping around will cause an overlap with the read
    pointer. This will make a full buffer be mistaken for an empty buffer

closes #1711
-   xRingbufferSend() can get stuck in a infinite loop when the size of the
    free memory is larger than the needed_size, but too small to fit in the ring
    buffer due to alignment and extra overhead of wrapping around.

closes #1846
-   Fixed documentation with ring buffer queue set API

-   Adding and removing from queue set does not consider the case where the
    read/write semaphores actually hold a value.

The following functions have been deprecated
    - xRingbufferIsNextItemWrapped() due to lack of thread safety
    - xRingbufferAddToQueueSetWrite() and xRingbufferRemoveFromQueueSetWrite()
    as adding the queue sets only work under receive operations.

The following functions have been added
    - xRingbufferReceiveSplit() and xRingbufferReceiveSplitFromISR() as a thread
    safe way to receive from allow-split buffers
    - vRingbufferGetInfo()

Documentation for ring buffers has also been added.

17 files changed:
components/freertos/include/freertos/ringbuf.h
components/freertos/ringbuf.c
components/freertos/test/test_ringbuf.c
docs/_static/diagrams/ring-buffer/ring_buffer_read_ret_byte_buf.diag [new file with mode: 0644]
docs/_static/diagrams/ring-buffer/ring_buffer_read_ret_non_byte_buf.diag [new file with mode: 0644]
docs/_static/diagrams/ring-buffer/ring_buffer_send_byte_buf.diag [new file with mode: 0644]
docs/_static/diagrams/ring-buffer/ring_buffer_send_non_byte_buf.diag [new file with mode: 0644]
docs/_static/diagrams/ring-buffer/ring_buffer_wrap_allow_split.diag [new file with mode: 0644]
docs/_static/diagrams/ring-buffer/ring_buffer_wrap_byte_buf.diag [new file with mode: 0644]
docs/_static/diagrams/ring-buffer/ring_buffer_wrap_no_split.diag [new file with mode: 0644]
docs/en/api-guides/freertos-smp.rst
docs/en/api-reference/system/freertos.rst
docs/en/api-reference/system/freertos_additions.rst [new file with mode: 0644]
docs/en/api-reference/system/hooks.rst [deleted file]
docs/en/api-reference/system/index.rst
docs/zh_CN/api-reference/system/freertos_additions.rst [new file with mode: 0644]
docs/zh_CN/api-reference/system/hooks.rst [deleted file]

index 8eae4061c6fdcb542dfb03ff7ab17dbffd219165..de8a3690952ea5a96b864a1a86adaae94bf27536 100644 (file)
@@ -1,3 +1,17 @@
+// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 #ifndef FREERTOS_RINGBUF_H
 #define FREERTOS_RINGBUF_H
 
@@ -11,71 +25,48 @@ extern "C" {
 
 #include <freertos/queue.h>
 
-//An opaque handle for a ringbuff object.
-typedef void * RingbufHandle_t;
-
 /**
- * @brief The various types of buffer
- *
- * A ringbuffer instantiated by these functions essentially acts like a
- * FreeRTOS queue, with the difference that it's strictly FIFO and with
- * the main advantage that you can put in randomly-sized items. The capacity,
- * accordingly, isn't measured in the amount of items, but the amount of
- * memory that is used for storing the items. Dependent on the size of
- * the items, more or less of them will fit in the ring buffer.
- *
- * This ringbuffer tries to be efficient with memory: when inserting an item,
- * the item data will be copied to the ringbuffer memory. When retrieving
- * an item, however, a reference to ringbuffer memory will be returned.
- * The returned memory is guaranteed to be 32-bit aligned and contiguous.
- * The application can use this memory, but as long as it does, ringbuffer
- * writes that would write to this bit of memory will block.
- *
- * The requirement for items to be contiguous is slightly problematic when
- * the only way to place the next item would involve a wraparound from the end
- * to the beginning of the ringbuffer. This can be solved (or not) in a few ways,
- * see descriptions of possible ringbuf_type_t types below.
- *
- * The maximum size of an item will be affected by ringbuffer type.
- * When split items are allowed, it is acceptable to push items of
- * (buffer_size)-16 bytes into the buffer.
- * When it's not allowed, the maximum size is (buffer_size/2)-8 bytes.
- * The bytebuf can fill the entire buffer with data, it has no overhead.
+ * Type by which ring buffers are referenced. For example, a call to xRingbufferCreate()
+ * returns a RingbufHandle_t variable that can then be used as a parameter to
+ * xRingbufferSend(), xRingbufferReceive(), etc.
  */
+typedef void * RingbufHandle_t;
+
 typedef enum {
-       /** The insertion code will leave the room at the end of the ringbuffer
-        * unused and instead will put the entire item at the start of the ringbuffer,
-        * as soon as there is enough free space.
+       /**
+        * 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,
-       /** The insertion code will split the item in two items; one which fits
-        * in the space left at the end of the ringbuffer, one that contains
-        * the remaining data which is placed in the beginning.
-        * Two xRingbufferReceive calls will be needed to retrieve the data.
+       /**
+        * 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,
-       /** This is your conventional byte-based ringbuffer. It does have no
-        * overhead, but it has no item contiguousness either: a read will just
-        * give you the entire written buffer space, or the space up to the end
-        * of the buffer, and writes can be broken up in any way possible.
-        * Note that this type cannot do a 2nd read before returning the memory
-        * of the 1st.
+       /**
+        * 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;
 
-
 /**
- * @brief  Create a ring buffer
+ * @brief       Create a ring buffer
  *
- * @param  buf_length  Length of circular buffer, in bytes. Each entry will
- *                     take up its own length, plus a header that at the moment
- *                     is equal to sizeof(size_t).
- * @param  type  Type of ring buffer, see ringbuf_type_t.
+ * @param[in]   xBufferSize Size of the buffer in bytes. Note that items require
+ *              space for overhead in no-split/allow-split buffers
+ * @param[in]   xBufferType Type of ring buffer, see documentation.
  *
- * @return A RingbufHandle_t handle to the created ringbuffer, or NULL in case of error.
+ * @note    xBufferSize of no-split/allow-split buffers will be rounded up to the nearest 32-bit aligned size.
+ *
+ * @return  A handle to the created ring buffer, or NULL in case of error.
  */
-RingbufHandle_t xRingbufferCreate(size_t buf_length, ringbuf_type_t type);
+RingbufHandle_t xRingbufferCreate(size_t xBufferSize, ringbuf_type_t xBufferType);
 
 /**
  * @brief Create a ring buffer of type RINGBUF_TYPE_NOSPLIT for a fixed item_size
@@ -83,282 +74,338 @@ RingbufHandle_t xRingbufferCreate(size_t buf_length, ringbuf_type_t type);
  * 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
+ * @param[in]   xItemSize   Size of each item to be put into the ring buffer
+ * @param[in]   xItemNum    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.
+ * @return  A RingbufHandle_t handle to the created ring buffer, or NULL in case of error.
  */
-RingbufHandle_t xRingbufferCreateNoSplit(size_t item_size, size_t num_item);
+RingbufHandle_t xRingbufferCreateNoSplit(size_t xItemSize, size_t xItemNum);
 
 /**
- * @brief  Delete a ring buffer
+ * @brief       Insert an item into the ring buffer
  *
- * @param  ringbuf  Ring buffer to delete
- */
-void vRingbufferDelete(RingbufHandle_t ringbuf);
-
-
-/**
- * @brief  Get maximum size of an item that can be placed in 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.
+ *
+ * @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.
+ * @param[in]   xItemSize       Size of data to insert.
+ * @param[in]   xTicksToWait    Ticks to wait for room in the ring buffer.
  *
- * @param  ringbuf  Ring buffer to query
+ * @note    For no-split/allow-split ring buffers, the actual size of memory that
+ *          the item will occupy will be rounded up to the nearest 32-bit aligned
+ *          size. This is done to ensure all items are always stored in 32-bit
+ *          aligned fashion.
  *
- * @return Maximum size, in bytes, of an item that can be placed in a ring buffer.
+ * @return
+ *      - pdTRUE if succeeded
+ *      - pdFALSE on time-out or when the data is larger than the maximum permissible size of the buffer
  */
-size_t xRingbufferGetMaxItemSize(RingbufHandle_t ringbuf);
+BaseType_t xRingbufferSend(RingbufHandle_t xRingbuffer, const void *pvItem, size_t xItemSize, TickType_t xTicksToWait);
 
 /**
- * @brief Get current free size available in the buffer
+ * @brief       Insert an item into the ring buffer in an ISR
  *
- * 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.
+ * Attempt to insert an item into the ring buffer from an ISR. This function
+ * will return immediately if there is insufficient free space in 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[in]   xRingbuffer Ring buffer to insert the item into
+ * @param[in]   pvItem      Pointer to data to insert. NULL is allowed if xItemSize is 0.
+ * @param[in]   xItemSize   Size of data to insert.
+ * @param[out]  pxHigherPriorityTaskWoken   Value pointed to will be set to pdTRUE if the function woke up a higher priority task.
  *
- * @param ringbuf - Ring buffer to query
+ * @note    For no-split/allow-split ring buffers, the actual size of memory that
+ *          the item will occupy will be rounded up to the nearest 32-bit aligned
+ *          size. This is done to ensure all items are always stored in 32-bit
+ *          aligned fashion.
  *
- * @return Current free size, in bytes,  available for an entry
+ * @return
+ *      - pdTRUE if succeeded
+ *      - pdFALSE when the ring buffer does not have space.
  */
-size_t xRingbufferGetCurFreeSize(RingbufHandle_t ringbuf);
+BaseType_t xRingbufferSendFromISR(RingbufHandle_t xRingbuffer, const void *pvItem, size_t xItemSize, BaseType_t *pxHigherPriorityTaskWoken);
 
 /**
- * @brief Check if the next item is wrapped
+ * @brief   Retrieve an item from the ring buffer
  *
- * 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
+ * Attempt to retrieve an item from the ring buffer. This function will block
+ * until an item is available or until it timesout.
  *
- * @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[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.
+ * @param[in]   xTicksToWait    Ticks to wait for items in the ring buffer.
  *
- * @param ringbuf - Ring buffer to query
+ * @note    A call to vRingbufferReturnItem() is required after this to free the item retrieved.
  *
- * @return true if the next item is wrapped around
- * @return false if the next item is not wrapped
+ * @return
+ *      - Pointer to the retrieved item on success; *pxItemSize filled with the length of the item.
+ *      - NULL on timeout, *pxItemSize is untouched in that case.
  */
-bool xRingbufferIsNextItemWrapped(RingbufHandle_t ringbuf);
+void *xRingbufferReceive(RingbufHandle_t xRingbuffer, size_t *pxItemSize, TickType_t xTicksToWait);
 
 /**
- * @brief  Insert an item into the ring buffer
+ * @brief   Retrieve an item from the ring buffer in an ISR
+ *
+ * Attempt to retrieve an item from the ring buffer. This function returns immediately
+ * if there are no items available for retrieval
  *
- * @param  ringbuf  Ring buffer to insert the item into
- * @param  data  Pointer to data to insert. NULL is allowed if data_size is 0.
- * @param  data_size  Size of data to insert. A value of 0 is allowed.
- * @param  ticks_to_wait  Ticks to wait for room in the ringbuffer.
+ * @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.
+ *
+ * @note    A call to vRingbufferReturnItemFromISR() is required after this to free the item retrieved.
+ * @note    Byte buffers do not allow multiple retrievals before returning an item
  *
  * @return
- *      - pdTRUE if succeeded
- *      - pdFALSE on time-out or when the buffer is larger than indicated
- *        by xRingbufferGetMaxItemSize(ringbuf).
+ *      - Pointer to the retrieved item on success; *pxItemSize filled with the length of the item.
+ *      - NULL when the ring buffer is empty, *pxItemSize is untouched in that case.
  */
-BaseType_t xRingbufferSend(RingbufHandle_t ringbuf, void *data, size_t data_size, TickType_t ticks_to_wait);
-
+void *xRingbufferReceiveFromISR(RingbufHandle_t xRingbuffer, size_t *pxItemSize);
 
 /**
- * @brief  Insert an item into the ring buffer from an ISR
+ * @brief   Retrieve a split item from an allow-split ring buffer
+ *
+ * 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.
  *
- * @param  ringbuf  Ring buffer to insert the item into
- * @param  data  Pointer to data to insert. NULL is allowed if data_size is 0.
- * @param  data_size  Size of data to insert. A value of 0 is allowed.
- * @param[out]  higher_prio_task_awoken  Value pointed to will be set to pdTRUE
- *                                  if the push woke up a higher priority task.
+ * @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)
+ * @param[out]  ppvTailItem     Double pointer to second part (set to NULL if item is not split)
+ * @param[out]  pxHeadItemSize  Pointer to size of first part (unmodified if no items were retrieved)
+ * @param[out]  pxTailItemSize  Pointer to size of second part (unmodified if item is not split)
+ * @param[in]   xTicksToWait    Ticks to wait for items in the ring buffer.
  *
- * @return pdTRUE if succeeded, pdFALSE when the ring buffer does not have space.
+ * @note    Call(s) to vRingbufferReturnItem() is required after this to free up the item(s) retrieved.
+ * @note    This function should only be called on allow-split buffers
+ *
+ * @return
+ *      - pdTRUE if an item (split or unsplit) was retrieved
+ *      - pdFALSE when no item was retrieved
  */
-BaseType_t xRingbufferSendFromISR(RingbufHandle_t ringbuf, void *data, size_t data_size, BaseType_t *higher_prio_task_awoken);
+BaseType_t xRingbufferReceiveSplit(RingbufHandle_t xRingbuffer, void **ppvHeadItem, void **ppvTailItem, size_t *pxHeadItemSize, size_t *pxTailItemSize, TickType_t xTicksToWait);
 
 /**
- * @brief  Retrieve an item from the ring buffer
+ * @brief   Retrieve a split item from an allow-split ring buffer in an ISR
  *
- * @note A call to vRingbufferReturnItem() is required after this to free up
- * the data received.
+ * 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 returns immediately if there are no items
+ * available for retrieval
  *
- * @param  ringbuf  Ring buffer to retrieve the item from
- * @param[out] item_size  Pointer to a variable to which the size of the
- *                        retrieved item will be written.
- * @param  ticks_to_wait  Ticks to wait for items in the ringbuffer.
+ * @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)
+ * @param[out]  ppvTailItem     Double pointer to second part (set to NULL if item is not split)
+ * @param[out]  pxHeadItemSize  Pointer to size of first part (unmodified if no items were retrieved)
+ * @param[out]  pxTailItemSize  Pointer to size of second part (unmodified if item is not split)
+ *
+ * @note    Calls to vRingbufferReturnItemFromISR() is required after this to free up the item(s) retrieved.
+ * @note    This function should only be called on allow-split buffers
  *
  * @return
- *      - pointer to the retrieved item on success; *item_size filled with
- *        the length of the item.
- *      - NULL on timeout, *item_size is untouched in that case.
+ *      - pdTRUE if an item (split or unsplit) was retrieved
+ *      - pdFALSE when no item was retrieved
  */
-void *xRingbufferReceive(RingbufHandle_t ringbuf, size_t *item_size, TickType_t ticks_to_wait);
-
+BaseType_t xRingbufferReceiveSplitFromISR(RingbufHandle_t xRingbuffer, void **ppvHeadItem, void **ppvTailItem, size_t *pxHeadItemSize, size_t *pxTailItemSize);
 
 /**
- * @brief  Retrieve an item from the ring buffer from an ISR
+ * @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.
  *
- * @note A call to vRingbufferReturnItemFromISR() is required after this to
- * free up the data received
+ * @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.
+ * @param[in]   xTicksToWait    Ticks to wait for items in the ring buffer.
+ * @param[in]   xMaxSize        Maximum number of bytes to return.
  *
- * @param  ringbuf  Ring buffer to retrieve the item from
- * @param[out] item_size  Pointer to a variable to which the size of the
- *                        retrieved item will be written.
+ * @note    A call to vRingbufferReturnItem() is required after this to free up the data retrieved.
+ * @note    This function should only be called on byte buffers
+ * @note    Byte buffers do not allow multiple retrievals before returning an item
  *
  * @return
- *      - Pointer to the retrieved item on success; *item_size filled with
+ *      - Pointer to the retrieved item on success; *pxItemSize filled with
  *        the length of the item.
- *      - NULL when the ringbuffer is empty, *item_size is untouched in that case.
+ *      - NULL on timeout, *pxItemSize is untouched in that case.
  */
-void *xRingbufferReceiveFromISR(RingbufHandle_t ringbuf, size_t *item_size);
-
+void *xRingbufferReceiveUpTo(RingbufHandle_t xRingbuffer, size_t *pxItemSize, TickType_t xTicksToWait, size_t xMaxSize);
 
 /**
- * @brief  Retrieve bytes from a ByteBuf type of ring buffer,
- * specifying the maximum amount of bytes to return
+ * @brief   Retrieve bytes from a byte buffer, specifying the maximum amount of
+ *          bytes to retrieve. Call this from an ISR.
+ *
+ * Attempt to retrieve bytes from a byte buffer whilst specifying a maximum number
+ * of bytes to retrieve. This function will return immediately if there is no data
+ * available for retrieval.
  *
- * @note A call to vRingbufferReturnItem() is required after this to free up
- * the data received.
+ * @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.
+ * @param[in]   xMaxSize    Maximum number of bytes to return.
  *
- * @param  ringbuf  Ring buffer to retrieve the item from
- * @param[out] item_size  Pointer to a variable to which the size
- *                        of the retrieved item will be written.
- * @param  ticks_to_wait  Ticks to wait for items in the ringbuffer.
- * @param  wanted_size  Maximum number of bytes to return.
+ * @note    A call to vRingbufferReturnItemFromISR() is required after this to free up the data received.
+ * @note    This function should only be called on byte buffers
+ * @note    Byte buffers do not allow multiple retrievals before returning an item
  *
  * @return
- *      - Pointer to the retrieved item on success; *item_size filled with
+ *      - Pointer to the retrieved item on success; *pxItemSize filled with
  *        the length of the item.
- *      - NULL on timeout, *item_size is untouched in that case.
+ *      - NULL when the ring buffer is empty, *pxItemSize is untouched in that case.
  */
-void *xRingbufferReceiveUpTo(RingbufHandle_t ringbuf, size_t *item_size, TickType_t ticks_to_wait, size_t wanted_size);
-
+void *xRingbufferReceiveUpToFromISR(RingbufHandle_t xRingbuffer, size_t *pxItemSize, size_t xMaxSize);
 
 /**
- * @brief  Retrieve bytes from a ByteBuf type of ring buffer,
- * specifying the maximum amount of bytes to return. Call this from an ISR.
+ * @brief   Return a previously-retrieved item to the ring buffer
  *
- * @note A call to vRingbufferReturnItemFromISR() is required after this
- * to free up the data received.
+ * @param[in]   xRingbuffer Ring buffer the item was retrieved from
+ * @param[in]   pvItem      Item that was received earlier
  *
- * @param  ringbuf  Ring buffer to retrieve the item from
- * @param[out] item_size  Pointer to a variable to which the size of the
- *                        retrieved item will be written.
- * @param  wanted_size  Maximum number of bytes to return.
- *
- * @return
- *      - Pointer to the retrieved item on success; *item_size filled with
- *        the length of the item.
- *      - NULL when the ringbuffer is empty, *item_size is untouched in that case.
+ * @note    If a split item is retrieved, both parts should be returned by calling this function twice
  */
-void *xRingbufferReceiveUpToFromISR(RingbufHandle_t ringbuf, size_t *item_size, size_t wanted_size);
-
-
+void vRingbufferReturnItem(RingbufHandle_t xRingbuffer, void *pvItem);
 
 /**
- * @brief  Return a previously-retrieved item to the ringbuffer
+ * @brief   Return a previously-retrieved item to the ring buffer from an ISR
+ *
+ * @param[in]   xRingbuffer Ring buffer the item was retrieved from
+ * @param[in]   pvItem      Item that was received earlier
+ * @param[out]  pxHigherPriorityTaskWoken   Value pointed to will be set to pdTRUE
+ *                                          if the function woke up a higher priority task.
  *
- * @param  ringbuf  Ring buffer the item was retrieved from
- * @param  item  Item that was received earlier
+ * @note    If a split item is retrieved, both parts should be returned by calling this function twice
  */
-void vRingbufferReturnItem(RingbufHandle_t ringbuf, void *item);
-
-
+void vRingbufferReturnItemFromISR(RingbufHandle_t xRingbuffer, void *pvItem, BaseType_t *pxHigherPriorityTaskWoken);
 
 /**
- * @brief  Return a previously-retrieved item to the ringbuffer from an ISR
+ * @brief   Delete a ring buffer
  *
- * @param  ringbuf  Ring buffer the item was retrieved from
- * @param  item  Item that was received earlier
- * @param[out]  higher_prio_task_awoken  Value pointed to will be set to pdTRUE
- *                                  if the push woke up a higher priority task.
+ * @param[in]   xRingbuffer     Ring buffer to delete
  */
-void vRingbufferReturnItemFromISR(RingbufHandle_t ringbuf, void *item, BaseType_t *higher_prio_task_awoken);
-
+void vRingbufferDelete(RingbufHandle_t xRingbuffer);
 
 /**
- * @brief  Add the ringbuffer to a queue set.
+ * @brief   Get maximum size of an item that can be placed in the ring buffer
  *
- * This specifically adds the semaphore that indicates more space
- * has become available in the ringbuffer.
+ * This function returns the maximum size an item can have if it was placed in
+ * an empty ring buffer.
  *
- * @param  ringbuf  Ring buffer to add to the queue set
- * @param  xQueueSet  Queue set to add the ringbuffer to
+ * @param[in]   xRingbuffer     Ring buffer to query
  *
- * @return
- *      - pdTRUE on success, pdFALSE otherwise
+ * @return  Maximum size, in bytes, of an item that can be placed in a ring buffer.
  */
-BaseType_t xRingbufferAddToQueueSetRead(RingbufHandle_t ringbuf, QueueSetHandle_t xQueueSet);
-
+size_t xRingbufferGetMaxItemSize(RingbufHandle_t xRingbuffer);
 
 /**
- * @brief  Add the ringbuffer to a queue set.
+ * @brief   Get current free size available for an item/data in the buffer
  *
- * This specifically adds the semaphore that indicates something has been
- * written into the ringbuffer.
+ * This gives the real time free space available for an item/data in the ring
+ * buffer. This represents the maximum size an item/data can have if it was
+ * currently sent to the ring buffer.
  *
- * @param  ringbuf  Ring buffer to add to the queue set
- * @param  xQueueSet  Queue set to add the ringbuffer to
+ * @warning 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
  *
- * @return pdTRUE on success, pdFALSE otherwise
+ * @param[in]   xRingbuffer     Ring buffer to query
+ *
+ * @return  Current free size, in bytes, available for an entry
  */
-BaseType_t xRingbufferAddToQueueSetWrite(RingbufHandle_t ringbuf, QueueSetHandle_t xQueueSet);
+size_t xRingbufferGetCurFreeSize(RingbufHandle_t xRingbuffer);
 
 /**
- * @brief Check if the selected queue set member is the ringbuffer's read semaphore
+ * @brief   Add the ring buffer's read semaphore to a queue set.
  *
- * This API checks if queue set member returned from xQueueSelectFromSet
- * is the read semaphore of this ring buffer. If so, this indicates the ring buffer
- * has items waiting to be read.
+ * The ring buffer's read semaphore indicates that data has been written
+ * to the ring buffer. This function adds the ring buffer's read semaphore to
+ * a queue set.
  *
- * @param ringbuf Ring buffer which should be checked
- * @param member Member returned from xQueueSelectFromSet
+ * @param[in]   xRingbuffer     Ring buffer to add to the queue set
+ * @param[in]   xQueueSet       Queue set to add the ring buffer's read semaphore to
  *
- * @return pdTRUE when semaphore belongs to ringbuffer, pdFALSE otherwise.
+ * @return
+ *      - pdTRUE on success, pdFALSE otherwise
  */
-BaseType_t xRingbufferCanRead(RingbufHandle_t ringbuf, QueueSetMemberHandle_t member);
+BaseType_t xRingbufferAddToQueueSetRead(RingbufHandle_t xRingbuffer, QueueSetHandle_t xQueueSet);
+
 
 /**
- * @brief Check if the selected queue set member is the ringbuffer's write semaphore
+ * @brief   Check if the selected queue set member is the ring buffer's read semaphore
  *
- * This API checks if queue set member returned from xQueueSelectFromSet
- * is the write semaphore of this ring buffer. If so, this indicates the ring buffer
- * has items waiting for write.
+ * This API checks if queue set member returned from xQueueSelectFromSet()
+ * is the read semaphore of this ring buffer. If so, this indicates the ring buffer
+ * has items waiting to be retrieved.
  *
- * @param ringbuf Ring buffer which should be checked
- * @param member Member returned from xQueueSelectFromSet
+ * @param[in]   xRingbuffer     Ring buffer which should be checked
+ * @param[in]   xMember         Member returned from xQueueSelectFromSet
  *
- * @return pdTRUE when semaphore belongs to ringbuffer, pdFALSE otherwise.
+ * @return
+ *      - pdTRUE when semaphore belongs to ring buffer
+ *      - pdFALSE otherwise.
  */
-BaseType_t xRingbufferCanWrite(RingbufHandle_t ringbuf, QueueSetMemberHandle_t member);
+BaseType_t xRingbufferCanRead(RingbufHandle_t xRingbuffer, QueueSetMemberHandle_t xMember);
 
 /**
- * @brief  Remove the ringbuffer from a queue set.
+ * @brief   Remove the ring buffer's read semaphore from a queue set.
  *
- * This specifically removes the semaphore that indicates more space
- * has become available in the ringbuffer.
+ * This specifically removes a ring buffer's read semaphore from a queue set. The
+ * read semaphore is used to indicate when data has been written to the ring buffer
  *
- * @param  ringbuf  Ring buffer to remove from the queue set
- * @param  xQueueSet  Queue set to remove the ringbuffer from
+ * @param[in]   xRingbuffer     Ring buffer to remove from the queue set
+ * @param[in]   xQueueSet       Queue set to remove the ring buffer's read semaphore from
  *
- * @return pdTRUE on success, pdFALSE otherwise
+ * @return
+ *      - pdTRUE on success
+ *      - pdFALSE otherwise
  */
-BaseType_t xRingbufferRemoveFromQueueSetRead(RingbufHandle_t ringbuf, QueueSetHandle_t xQueueSet);
-
+BaseType_t xRingbufferRemoveFromQueueSetRead(RingbufHandle_t xRingbuffer, QueueSetHandle_t xQueueSet);
 
 /**
- * @brief  Remove the ringbuffer from a queue set.
+ * @brief   Get information about ring buffer status
  *
- * This specifically removes the semaphore that indicates something
- * has been written to the ringbuffer.
+ * Get information of the a ring buffer's current status such as
+ * free/read/write pointer positions, and number of items waiting to be retrieved.
+ * Arguments can be set to NULL if they are not required.
  *
- * @param  ringbuf  Ring buffer to remove from the queue set
- * @param  xQueueSet  Queue set to remove the ringbuffer from
- *
- * @return pdTRUE on success, pdFALSE otherwise
+ * @param[in]   xRingbuffer     Ring buffer to remove from the queue set
+ * @param[out]  uxFree          Pointer use to store free pointer position
+ * @param[out]  uxRead          Pointer use to store read pointer position
+ * @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
  */
-BaseType_t xRingbufferRemoveFromQueueSetWrite(RingbufHandle_t ringbuf, QueueSetHandle_t xQueueSet);
-
+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
+ * @brief   Debugging function to print the internal pointers in the ring buffer
  *
- * @param  ringbuf  Ring buffer to show
+ * @param   xRingbuffer Ring buffer to show
+ */
+void xRingbufferPrintInfo(RingbufHandle_t xRingbuffer);
+
+/* -------------------------------- Deprecated Functions --------------------------- */
+
+/** @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.
  */
-void xRingbufferPrintInfo(RingbufHandle_t ringbuf);
+BaseType_t xRingbufferRemoveFromQueueSetWrite(RingbufHandle_t xRingbuffer, QueueSetHandle_t xQueueSet) __attribute__((deprecated));
+/** @endcond */
 
 #ifdef __cplusplus
 }
index 2f1998bfe6815a9b15947a66c8b41f5ffa29878a..606cea148d324a0914c7a16029535df23d237bb2 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
+// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "freertos/FreeRTOS.h"
-#include "freertos/task.h"
-#include "freertos/semphr.h"
-#include "freertos/queue.h"
-#include "freertos/xtensa_api.h"
-#include "freertos/ringbuf.h"
-#include "esp_attr.h"
-#include <stdint.h>
-#include <string.h>
 #include <stdlib.h>
-#include <stdio.h>
-
-typedef enum {
-    flag_allowsplit = 1,
-    flag_bytebuf = 2,
-} rbflag_t;
-
-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;
-
-
-typedef struct ringbuf_t ringbuf_t;
-
-//The ringbuffer structure
-struct  ringbuf_t {
-    SemaphoreHandle_t free_space_sem;           //Binary semaphore, wakes up writing threads when there's more free space
-    SemaphoreHandle_t items_buffered_sem;       //Binary semaphore, indicates there are new packets in the circular buffer. See remark.
-    size_t size;                                //Size of the data storage
-    uint8_t *write_ptr;                         //Pointer where the next item is written
-    uint8_t *read_ptr;                          //Pointer from where the next item is read
-    uint8_t *free_ptr;                          //Pointer to the last block that hasn't been given back to the ringbuffer yet
-    uint8_t *data;                              //Data storage
-    portMUX_TYPE mux;                           //Spinlock for actual data/ptr/struct modification
-    rbflag_t flags;
-    size_t maxItemSize;
-   //The following keep function pointers to hold different implementations for ringbuffer management.
-    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);
-};
+#include <string.h>
+#include "FreeRTOS.h"
+#include "task.h"
+#include "semphr.h"
+#include "ringbuf.h"
+
+//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 )
 
+//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)
 
+//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
+
+typedef struct {
+    //This size of this structure must be 32-bit aligned
+    size_t xItemLen;
+    UBaseType_t uxItemFlags;
+} ItemHeader_t;
+
+#define rbHEADER_SIZE     sizeof(ItemHeader_t)
+typedef struct Ringbuffer_t 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);
+typedef void *(*GetItemFunction_t)(Ringbuffer_t *pxRingbuffer, BaseType_t *pxIsSplit, size_t xMaxSize, size_t *pxItemSize);
+typedef void (*ReturnItemFunction_t)(Ringbuffer_t *pxRingbuffer, uint8_t *pvItem);
+typedef size_t (*GetCurMaxSizeFunction_t)(Ringbuffer_t *pxRingbuffer);
+
+struct Ringbuffer_t {
+    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
+
+    CheckItemFitsFunction_t xCheckItemFits;     //Function to check if item can currently fit in ring buffer
+    CopyItemFunction_t vCopyItem;               //Function to copy item to ring buffer
+    GetItemFunction_t pvGetItem;                //Function to get item from ring buffer
+    ReturnItemFunction_t vReturnItem;           //Function to return item to ring buffer
+    GetCurMaxSizeFunction_t xGetCurMaxSize;     //Function to get current free size
+
+    uint8_t *pucWrite;                          //Write Pointer. Points to where the next item should be written
+    uint8_t *pucRead;                           //Read Pointer. Points to where the next item should be read from
+    uint8_t *pucFree;                           //Free Pointer. Points to the last item that has yet to be returned to the ring buffer
+    uint8_t *pucHead;                           //Pointer to the start of the ring buffer storage area
+    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.
+    portMUX_TYPE mux;                           //Spinlock required for SMP
+};
 
 /*
 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
-would need to set the maximum to the maximum amount of times a null-byte unit firs in the buffer,
+would need to set the maximum to the maximum amount of times a null-byte unit first in the buffer,
 which is quite high and so would waste a fair amount of memory.
 */
 
+/* ------------------------------------------------ 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)
+ */
 
-//The header prepended to each ringbuffer entry. Size is assumed to be a multiple of 32bits.
-typedef struct {
-    size_t len;
-    itemflag_t flags;
-} buf_entry_hdr_t;
-
-
-//Calculate space free in the buffer
-static int ringbufferFreeMem(ringbuf_t *rb)
-{
-    int free_size = rb->free_ptr-rb->write_ptr;
-    if (free_size <= 0) free_size += rb->size;
-    //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;
-}
-
-//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
-//later or fail.
-//This function by itself is not threadsafe, always call from within a muxed section.
-static BaseType_t copyItemToRingbufNoSplit(ringbuf_t *rb, uint8_t *buffer, size_t buffer_size)
-{
-    size_t rbuffer_size;
-    rbuffer_size=(buffer_size+3)&~3; //Payload length, rounded to next 32-bit value
-    configASSERT(((int)rb->write_ptr&3)==0); //write_ptr needs to be 32-bit aligned
-    configASSERT(rb->write_ptr-(rb->data+rb->size) >= sizeof(buf_entry_hdr_t)); //need to have at least the size
-                                            //of a header to the end of the ringbuff
-    size_t rem_len=(rb->data + rb->size) - rb->write_ptr; //length remaining until end of ringbuffer
-
-    //See if we have enough contiguous space to write the buffer.
-    if (rem_len < rbuffer_size + sizeof(buf_entry_hdr_t)) {
-        //Buffer plus header is not going to fit in the room from wr_pos to the end of the
-        //ringbuffer... but we're not allowed to split the buffer. We need to fill the
-        //rest of the ringbuffer with a dummy item so we can place the data at the _start_ of
-        //the ringbuffer..
-        //First, find out if we actually have enough space at the start of the ringbuffer to
-        //make this work (Again, we need 4 bytes extra because otherwise read_ptr==free_ptr)
-        if (rb->free_ptr-rb->data < rbuffer_size+sizeof(buf_entry_hdr_t)+4) {
-            //Will not fit.
-            return pdFALSE;
-        }
-        //If the read buffer hasn't wrapped around yet, there's no way this will work either.
-        if (rb->free_ptr > rb->write_ptr) {
-            //No luck.
-            return pdFALSE;
-        }
+//Calculate current amount of free space (in bytes) in the ring buffer
+static size_t prvGetFreeSize(Ringbuffer_t *pxRingbuffer);
+
+//Checks if an item/data is currently available for retrieval
+static BaseType_t prvCheckItemAvail(Ringbuffer_t *pxRingbuffer);
+
+//Checks if an item will currently fit in a no-split/allow-split ring buffer
+static BaseType_t prvCheckItemFitsDefault( Ringbuffer_t *pxRingbuffer, size_t xItemSize);
+
+//Checks if an item will currently fit in a byte buffer
+static BaseType_t prvCheckItemFitsByteBuffer( Ringbuffer_t *pxRingbuffer, size_t xItemSize);
+
+//Copies an item to a no-split ring buffer. Only call this function after calling prvCheckItemFitsDefault()
+static void prvCopyItemNoSplit(Ringbuffer_t *pxRingbuffer, const uint8_t *pucItem, size_t xItemSize);
+
+//Copies an item to a allow-split ring buffer. Only call this function after calling prvCheckItemFitsDefault()
+static void prvCopyItemAllowSplit(Ringbuffer_t *pxRingbuffer, const uint8_t *pucItem, size_t xItemSize);
+
+//Copies an item to a byte buffer. Only call this function  after calling prvCheckItemFitsByteBuffer()
+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);
+
+//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);
+
+//Return an item to a split/no-split ring buffer
+static void prvReturnItemDefault(Ringbuffer_t *pxRingbuffer, uint8_t *pucItem);
+
+//Return data to a byte buffer
+static void prvReturnItemByteBuf(Ringbuffer_t *pxRingbuffer, uint8_t *pucItem);
+
+//Get the maximum size an item that can currently have if sent to a no-split ring buffer
+static size_t prvGetCurMaxSizeNoSplit(Ringbuffer_t *pxRingbuffer);
+
+//Get the maximum size an item that can currently have if sent to a allow-split ring buffer
+static size_t prvGetCurMaxSizeAllowSplit(Ringbuffer_t *pxRingbuffer);
+
+//Get the maximum size an item that can currently have if sent to a byte buffer
+static size_t prvGetCurMaxSizeByteBuf(Ringbuffer_t *pxRingbuffer);
+
+/**
+ * Generic function used to retrieve an item/data from ring buffers. If called on
+ * an allow-split buffer, and pvItem2 and xItemSize2 are not NULL, both parts of
+ * 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);
+
+//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 Definitions ------------------------------------------- */
 
-        //Okay, it will fit. Mark the rest of the ringbuffer space with a dummy packet.
-        buf_entry_hdr_t *hdr=(buf_entry_hdr_t *)rb->write_ptr;
-        hdr->flags=iflag_dummydata;
-        //Reset the write pointer to the start of the ringbuffer so the code later on can
-        //happily write the data.
-        rb->write_ptr=rb->data;
+static size_t prvGetFreeSize(Ringbuffer_t *pxRingbuffer)
+{
+    size_t xReturn;
+    if (pxRingbuffer->uxRingbufferFlags & rbBUFFER_FULL_FLAG) {
+        xReturn =  0;
     } else {
-        //No special handling needed. Checking if it's gonna fit probably still is a good idea.
-        if (ringbufferFreeMem(rb) < sizeof(buf_entry_hdr_t)+rbuffer_size) {
-            //Buffer is not going to fit, period.
-            return pdFALSE;
+        BaseType_t xFreeSize = pxRingbuffer->pucFree - pxRingbuffer->pucWrite;
+        //Check if xFreeSize has underflowed
+        if (xFreeSize <= 0) {
+            xFreeSize += pxRingbuffer->xSize;
         }
+        xReturn = xFreeSize;
     }
+    configASSERT(xReturn <= pxRingbuffer->xSize);
+    return xReturn;
+}
 
-    //If we are here, the buffer is guaranteed to fit in the space starting at the write pointer.
-    buf_entry_hdr_t *hdr=(buf_entry_hdr_t *)rb->write_ptr;
-    hdr->len=buffer_size;
-    hdr->flags=0;
-    rb->write_ptr+=sizeof(buf_entry_hdr_t);
-    memcpy(rb->write_ptr, buffer, buffer_size);
-    rb->write_ptr+=rbuffer_size;
-
-    //The buffer will wrap around if we don't have room for a header anymore.
-    if ((rb->data+rb->size)-rb->write_ptr < sizeof(buf_entry_hdr_t)) {
-        //'Forward' the write buffer until we are at the start of the ringbuffer.
-        //The read pointer will always be at the start of a full header, which cannot
-        //exist at the point of the current write pointer, so there's no chance of overtaking
-        //that.
-        rb->write_ptr=rb->data;
-    }
-    return pdTRUE;
-}
-
-//Copies a single item to the ring buffer; allows 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
-//later or fail.
-//This function by itself is not threadsafe, always call from within a muxed section.
-static BaseType_t copyItemToRingbufAllowSplit(ringbuf_t *rb, uint8_t *buffer, size_t buffer_size)
-{
-    size_t rbuffer_size;
-    rbuffer_size=(buffer_size+3)&~3; //Payload length, rounded to next 32-bit value
-    configASSERT(((int)rb->write_ptr&3)==0); //write_ptr needs to be 32-bit aligned
-    configASSERT(rb->write_ptr-(rb->data+rb->size) >= sizeof(buf_entry_hdr_t)); //need to have at least the size
-                                            //of a header to the end of the ringbuff
-    size_t rem_len=(rb->data + rb->size) - rb->write_ptr; //length remaining until end of ringbuffer
-
-    //See if we have enough contiguous space to write the buffer.
-    if (rem_len < rbuffer_size + sizeof(buf_entry_hdr_t)) {
-        //The buffer can't be contiguously written to the ringbuffer, but needs special handling. Do
-        //that depending on how the ringbuffer is configured.
-        //The code here is also expected to check if the buffer, mangled in whatever way is implemented,
-        //will still fit, and return pdFALSE if that is not the case.
-        //Buffer plus header is not going to fit in the room from wr_pos to the end of the
-        //ringbuffer... we need to split the write in two.
-        //First, see if this will fit at all.
-        if (ringbufferFreeMem(rb) < (sizeof(buf_entry_hdr_t)*2)+rbuffer_size) {
-            //Will not fit.
-            return pdFALSE;
-        }
-         //Because the code at the end of the function makes sure we always have
-        //room for a header, this should never assert.
-        configASSERT(rem_len>=sizeof(buf_entry_hdr_t));
-         //Okay, it should fit. Write everything.
-        //First, place bit of buffer that does fit. Write header first...
-        buf_entry_hdr_t *hdr=(buf_entry_hdr_t *)rb->write_ptr;
-        hdr->flags=0;
-        hdr->len=rem_len-sizeof(buf_entry_hdr_t);
-        rb->write_ptr+=sizeof(buf_entry_hdr_t);
-        rem_len-=sizeof(buf_entry_hdr_t);
-        if (rem_len!=0) {
-            //..then write the data bit that fits.
-            memcpy(rb->write_ptr, buffer, rem_len);
-            //Update vars so the code later on will write the rest of the data.
-            buffer+=rem_len;
-            buffer_size-=rem_len;
-            //Re-adjust the rbuffer value to be 4 byte aligned
-            rbuffer_size=(buffer_size+3)&~3;
-            //It is possible that we are here because we checked for 4byte aligned
-            //size, but actual data was smaller.
-            //Eg. For buffer_size = 34, rbuffer_size will be 36. Suppose we had only
-            //42 bytes of memory available, the top level check will fail, as it will
-            //check for availability of 36 + 8 = 44 bytes.
-            //However, the 42 bytes available memory is sufficient for 34 + 8 bytes data
-            //and so, we can return after writing the data. Hence, this check
-            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
-            //an useless zero-byte packet.
-            hdr->flags|=iflag_dummydata;
-        }
-        rb->write_ptr=rb->data;
+static BaseType_t prvCheckItemFitsDefault( Ringbuffer_t *pxRingbuffer, size_t xItemSize)
+{
+    //Check arguments and buffer state
+    configASSERT(rbCHECK_ALIGNED(pxRingbuffer->pucWrite));              //pucWrite is always aligned in no-split ring buffers
+    configASSERT(pxRingbuffer->pucWrite >= pxRingbuffer->pucHead && pxRingbuffer->pucWrite < pxRingbuffer->pucTail);    //Check write pointer is within bounds
+
+    size_t xTotalItemSize = rbALIGN_SIZE(xItemSize) + rbHEADER_SIZE;    //Rounded up aligned item size with header
+    if (pxRingbuffer->pucWrite == pxRingbuffer->pucFree) {
+        //Buffer is either complete empty or completely full
+        return (pxRingbuffer->uxRingbufferFlags & rbBUFFER_FULL_FLAG) ? pdFALSE : pdTRUE;
+    }
+    if (pxRingbuffer->pucFree > pxRingbuffer->pucWrite) {
+        //Free space does not wrap around
+        return (xTotalItemSize <= pxRingbuffer->pucFree - pxRingbuffer->pucWrite) ? pdTRUE : pdFALSE;
+    }
+    //Free space wraps around
+    if (xTotalItemSize <= pxRingbuffer->pucTail - pxRingbuffer->pucWrite) {
+        return pdTRUE;      //Item fits without wrapping around
+    }
+    //Check if item fits by wrapping
+    if (pxRingbuffer->uxRingbufferFlags & rbALLOW_SPLIT_FLAG) {
+        //Allow split wrapping incurs an extra header
+        return (xTotalItemSize + rbHEADER_SIZE <= pxRingbuffer->xSize - (pxRingbuffer->pucWrite - pxRingbuffer->pucFree)) ? pdTRUE : pdFALSE;
     } else {
-        //No special handling needed. Checking if it's gonna fit probably still is a good idea.
-        if (ringbufferFreeMem(rb) < sizeof(buf_entry_hdr_t)+rbuffer_size) {
-            //Buffer is not going to fit, period.
-            return pdFALSE;
-        }
+        return (xTotalItemSize <= pxRingbuffer->pucFree - pxRingbuffer->pucHead) ? pdTRUE : pdFALSE;
     }
+}
 
-    //If we are here, the buffer is guaranteed to fit in the space starting at the write pointer.
-    buf_entry_hdr_t *hdr=(buf_entry_hdr_t *)rb->write_ptr;
-    hdr->len=buffer_size;
-    hdr->flags=0;
-    rb->write_ptr+=sizeof(buf_entry_hdr_t);
-    memcpy(rb->write_ptr, buffer, buffer_size);
-    rb->write_ptr+=rbuffer_size;
+static BaseType_t prvCheckItemFitsByteBuffer( Ringbuffer_t *pxRingbuffer, size_t xItemSize)
+{
+    //Check arguments and buffer state
+    configASSERT(pxRingbuffer->pucWrite >= pxRingbuffer->pucHead && pxRingbuffer->pucWrite < pxRingbuffer->pucTail);    //Check write pointer is within bounds
 
-    //The buffer will wrap around if we don't have room for a header anymore.
-    if ((rb->data+rb->size)-rb->write_ptr < sizeof(buf_entry_hdr_t)) {
-        //'Forward' the write buffer until we are at the start of the ringbuffer.
-        //The read pointer will always be at the start of a full header, which cannot
-        //exist at the point of the current write pointer, so there's no chance of overtaking
-        //that.
-        rb->write_ptr=rb->data;
+    if (pxRingbuffer->pucWrite == pxRingbuffer->pucFree) {
+        //Buffer is either complete empty or completely full
+        return (pxRingbuffer->uxRingbufferFlags & rbBUFFER_FULL_FLAG) ? pdFALSE : pdTRUE;
     }
-    return pdTRUE;
+    if (pxRingbuffer->pucFree > pxRingbuffer->pucWrite) {
+        //Free space does not wrap around
+        return (xItemSize <= pxRingbuffer->pucFree - pxRingbuffer->pucWrite) ? pdTRUE : pdFALSE;
+    }
+    //Free space wraps around
+    return (xItemSize <= pxRingbuffer->xSize - (pxRingbuffer->pucWrite - pxRingbuffer->pucFree)) ? pdTRUE : pdFALSE;
 }
 
+static void prvCopyItemNoSplit(Ringbuffer_t *pxRingbuffer, const uint8_t *pucItem, size_t xItemSize)
+{
+    //Check arguments and buffer state
+    size_t xAlignedItemSize = rbALIGN_SIZE(xItemSize);                  //Rounded up aligned item size
+    size_t xRemLen = pxRingbuffer->pucTail - pxRingbuffer->pucWrite;    //Length from pucWrite until end of buffer
+    configASSERT(rbCHECK_ALIGNED(pxRingbuffer->pucWrite));              //pucWrite is always aligned in no-split ring buffers
+    configASSERT(pxRingbuffer->pucWrite >= pxRingbuffer->pucHead && pxRingbuffer->pucWrite < pxRingbuffer->pucTail);    //Check write pointer is within bounds
+    configASSERT(xRemLen >= rbHEADER_SIZE);                             //Remaining length must be able to at least fit an item header
+
+    //If remaining length can't fit item, set as dummy data and wrap around
+    if (xRemLen < xAlignedItemSize + rbHEADER_SIZE) {
+        ItemHeader_t *pxDummy = (ItemHeader_t *)pxRingbuffer->pucWrite;
+        pxDummy->uxItemFlags = rbITEM_DUMMY_DATA_FLAG;      //Set remaining length as dummy data
+        pxDummy->xItemLen = 0;                              //Dummy data should have no length
+        pxRingbuffer->pucWrite = pxRingbuffer->pucHead;     //Reset write pointer to wrap around
+    }
+
+    //Item should be guaranteed to fit at this point. Set item header and copy data
+    ItemHeader_t *pxHeader = (ItemHeader_t *)pxRingbuffer->pucWrite;
+    pxHeader->xItemLen = xItemSize;
+    pxHeader->uxItemFlags = 0;
+    pxRingbuffer->pucWrite += rbHEADER_SIZE;    //Advance pucWrite past header
+    memcpy(pxRingbuffer->pucWrite, pucItem, xItemSize);
+    pxRingbuffer->xItemsWaiting++;
+    pxRingbuffer->pucWrite += xAlignedItemSize; //Advance pucWrite past item to next aligned address
+
+    //If current remaining length can't fit a header, wrap around write pointer
+    if (pxRingbuffer->pucTail - pxRingbuffer->pucWrite < rbHEADER_SIZE) {
+        pxRingbuffer->pucWrite = pxRingbuffer->pucHead;   //Wrap around pucWrite
+    }
+    //Check if buffer is full
+    if (pxRingbuffer->pucWrite == pxRingbuffer->pucFree) {
+        //Mark the buffer as full to distinguish with an empty buffer
+        pxRingbuffer->uxRingbufferFlags |= rbBUFFER_FULL_FLAG;
+    }
+}
 
-//Copies a bunch of daya to the ring bytebuffer. 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
-//later or fail.
-//This function by itself is not threadsafe, always call from within a muxed section.
-static BaseType_t copyItemToRingbufByteBuf(ringbuf_t *rb, uint8_t *buffer, size_t buffer_size)
+static void prvCopyItemAllowSplit(Ringbuffer_t *pxRingbuffer, const uint8_t *pucItem, size_t xItemSize)
 {
-    size_t rem_len=(rb->data + rb->size) - rb->write_ptr; //length remaining until end of ringbuffer
+    //Check arguments and buffer state
+    size_t xAlignedItemSize = rbALIGN_SIZE(xItemSize);                  //Rounded up aligned item size
+    size_t xRemLen = pxRingbuffer->pucTail - pxRingbuffer->pucWrite;    //Length from pucWrite until end of buffer
+    configASSERT(rbCHECK_ALIGNED(pxRingbuffer->pucWrite));              //pucWrite is always aligned in split ring buffers
+    configASSERT(pxRingbuffer->pucWrite >= pxRingbuffer->pucHead && pxRingbuffer->pucWrite < pxRingbuffer->pucTail);    //Check write pointer is within bounds
+    configASSERT(xRemLen >= rbHEADER_SIZE);                             //Remaining length must be able to at least fit an item header
+
+    //Split item if necessary
+    if (xRemLen < xAlignedItemSize + rbHEADER_SIZE) {
+        //Write first part of the item
+        ItemHeader_t *pxFirstHeader = (ItemHeader_t *)pxRingbuffer->pucWrite;
+        pxFirstHeader->uxItemFlags = 0;
+        pxFirstHeader->xItemLen = xRemLen - rbHEADER_SIZE;  //Fill remaining length with first part
+        pxRingbuffer->pucWrite += rbHEADER_SIZE;            //Advance pucWrite past header
+        xRemLen -= rbHEADER_SIZE;
+        if (xRemLen > 0) {
+            memcpy(pxRingbuffer->pucWrite, pucItem, xRemLen);
+            pxRingbuffer->xItemsWaiting++;
+            //Update item arguments to account for data already copied
+            pucItem += xRemLen;
+            xItemSize -= xRemLen;
+            xAlignedItemSize -= xRemLen;
+            pxFirstHeader->uxItemFlags |= rbITEM_SPLIT_FLAG;        //There must be more data
+        } else {
+            //Remaining length was only large enough to fit header
+            pxFirstHeader->uxItemFlags |= rbITEM_DUMMY_DATA_FLAG;   //Item will completely be stored in 2nd part
+        }
+        pxRingbuffer->pucWrite = pxRingbuffer->pucHead;             //Reset write pointer to start of buffer
+    }
+
+    //Item (whole or second part) should be guaranteed to fit at this point
+    ItemHeader_t *pxSecondHeader = (ItemHeader_t *)pxRingbuffer->pucWrite;
+    pxSecondHeader->xItemLen = xItemSize;
+    pxSecondHeader->uxItemFlags = 0;
+    pxRingbuffer->pucWrite += rbHEADER_SIZE;     //Advance write pointer past header
+    memcpy(pxRingbuffer->pucWrite, pucItem, xItemSize);
+    pxRingbuffer->xItemsWaiting++;
+    pxRingbuffer->pucWrite += xAlignedItemSize;  //Advance pucWrite past item to next aligned address
+
+    //If current remaining length can't fit a header, wrap around write pointer
+    if (pxRingbuffer->pucTail - pxRingbuffer->pucWrite < rbHEADER_SIZE) {
+        pxRingbuffer->pucWrite = pxRingbuffer->pucHead;   //Wrap around pucWrite
+    }
+    //Check if buffer is full
+    if (pxRingbuffer->pucWrite == pxRingbuffer->pucFree) {
+        //Mark the buffer as full to distinguish with an empty buffer
+        pxRingbuffer->uxRingbufferFlags |= rbBUFFER_FULL_FLAG;
+    }
+}
 
-    //See if we have enough contiguous space to write the buffer.
-    if (rem_len < buffer_size) {
-        //...Nope. Write the data bit that fits.
-        memcpy(rb->write_ptr, buffer, rem_len);
-        //Update vars so the code later on will write the rest of the data.
-        buffer+=rem_len;
-        buffer_size-=rem_len;
-        rb->write_ptr=rb->data;
+static void prvCopyItemByteBuf(Ringbuffer_t *pxRingbuffer, const uint8_t *pucItem, size_t xItemSize)
+{
+    //Check arguments and buffer state
+    configASSERT(pxRingbuffer->pucWrite >= pxRingbuffer->pucHead && pxRingbuffer->pucWrite < pxRingbuffer->pucTail);    //Check write pointer is within bounds
+
+    size_t xRemLen = pxRingbuffer->pucTail - pxRingbuffer->pucWrite;    //Length from pucWrite until end of buffer
+    if (xRemLen < xItemSize) {
+        //Copy as much as possible into remaining length
+        memcpy(pxRingbuffer->pucWrite, pucItem, xRemLen);
+        pxRingbuffer->xItemsWaiting += xRemLen;
+        //Update item arguments to account for data already written
+        pucItem += xRemLen;
+        xItemSize -= xRemLen;
+        pxRingbuffer->pucWrite = pxRingbuffer->pucHead;     //Reset write pointer to start of buffer
+    }
+    //Copy all or remaining portion of the item
+    memcpy(pxRingbuffer->pucWrite, pucItem, xItemSize);
+    pxRingbuffer->xItemsWaiting += xItemSize;
+    pxRingbuffer->pucWrite += xItemSize;
+
+    //Wrap around pucWrite if it reaches the end
+    if (pxRingbuffer->pucWrite == pxRingbuffer->pucTail) {
+        pxRingbuffer->pucWrite = pxRingbuffer->pucHead;
+    }
+    //Check if buffer is full
+    if (pxRingbuffer->pucWrite == pxRingbuffer->pucFree) {
+        pxRingbuffer->uxRingbufferFlags |= rbBUFFER_FULL_FLAG;      //Mark the buffer as full to avoid confusion with an empty buffer
     }
+}
 
-    //If we are here, the buffer is guaranteed to fit in the space starting at the write pointer.
-    memcpy(rb->write_ptr, buffer, buffer_size);
-    rb->write_ptr+=buffer_size;
-    //The buffer will wrap around if we're at the end.
-    if ((rb->data+rb->size)==rb->write_ptr) {
-        rb->write_ptr=rb->data;
+static BaseType_t prvCheckItemAvail(Ringbuffer_t *pxRingbuffer)
+{
+    if ((pxRingbuffer->uxRingbufferFlags & rbBYTE_BUFFER_FLAG) && pxRingbuffer->pucRead != pxRingbuffer->pucFree) {
+        return pdFALSE;     //Byte buffers do not allow multiple retrievals before return
+    }
+    if ((pxRingbuffer->xItemsWaiting > 0) && ((pxRingbuffer->pucRead != pxRingbuffer->pucWrite) || (pxRingbuffer->uxRingbufferFlags & rbBUFFER_FULL_FLAG))) {
+        return pdTRUE;      //Items/data available for retrieval
+    } else {
+        return pdFALSE;     //No items/data available for retrieval
     }
-    return pdTRUE;
 }
 
-//Retrieves a pointer to the data of the next item, or NULL if this is not possible.
-//This function by itself is not threadsafe, always call from within a muxed section.
-//Because we always return one item, this function ignores the wanted_length variable.
-static uint8_t *getItemFromRingbufDefault(ringbuf_t *rb, size_t *length, int wanted_length)
+static void *prvGetItemDefault(Ringbuffer_t *pxRingbuffer, BaseType_t *pxIsSplit, size_t xUnusedParam, size_t *pxItemSize)
 {
-    uint8_t *ret;
-    configASSERT(((int)rb->read_ptr&3)==0);
-    if (rb->read_ptr == rb->write_ptr) {
-        //No data available.
-        return NULL;
+    //Check arguments and buffer state
+    ItemHeader_t *pxHeader = (ItemHeader_t *)pxRingbuffer->pucRead;
+    configASSERT(pxIsSplit != NULL);
+    configASSERT((pxRingbuffer->xItemsWaiting > 0) && ((pxRingbuffer->pucRead != pxRingbuffer->pucWrite) || (pxRingbuffer->uxRingbufferFlags & rbBUFFER_FULL_FLAG)));   //Check there are items to be read
+    configASSERT(rbCHECK_ALIGNED(pxRingbuffer->pucRead));           //pucRead is always aligned in split ring buffers
+    configASSERT(pxRingbuffer->pucRead >= pxRingbuffer->pucHead && pxRingbuffer->pucRead < pxRingbuffer->pucTail);      //Check read pointer is within bounds
+    configASSERT((pxHeader->xItemLen <= pxRingbuffer->xMaxItemSize) || (pxHeader->uxItemFlags & rbITEM_DUMMY_DATA_FLAG));
+
+    uint8_t *pcReturn;
+    //Wrap around if dummy data (dummy data indicates wrap around in no-split buffers)
+    if (pxHeader->uxItemFlags & rbITEM_DUMMY_DATA_FLAG) {
+        pxRingbuffer->pucRead = pxRingbuffer->pucHead;
+        //Check for errors with the next item
+        pxHeader = (ItemHeader_t *)pxRingbuffer->pucRead;
+        configASSERT(pxHeader->xItemLen <= pxRingbuffer->xMaxItemSize);
     }
-    //The item written at the point of the read pointer may be a dummy item.
-    //We need to skip past it first, if that's the case.
-    buf_entry_hdr_t *hdr=(buf_entry_hdr_t *)rb->read_ptr;
-    configASSERT((hdr->len < rb->size) || (hdr->flags & iflag_dummydata));
-    if (hdr->flags & iflag_dummydata) {
-        //Hdr is dummy data. Reset to start of ringbuffer.
-        rb->read_ptr=rb->data;
-        //Get real header
-        hdr=(buf_entry_hdr_t *)rb->read_ptr;
-        configASSERT(hdr->len < rb->size);
-        //No need to re-check if the ringbuffer is empty: the write routine will
-        //always write a dummy item plus the real data item in one go, so now we must
-        //be at the real data item by definition.
-    }
-    //Okay, pass the data back.
-    ret=rb->read_ptr+sizeof(buf_entry_hdr_t);
-    *length=hdr->len;
-    //...and move the read pointer past the data.
-    rb->read_ptr+=sizeof(buf_entry_hdr_t)+((hdr->len+3)&~3);
-    //The buffer will wrap around if we don't have room for a header anymore.
-    //Integer typecasting is used because the first operand can result into a -ve
-    //value for cases wherein the ringbuffer size is not a multiple of 4, but the
-    //implementation logic aligns read_ptr to 4-byte boundary
-    if ((int)((rb->data + rb->size) - rb->read_ptr) < (int)sizeof(buf_entry_hdr_t)) {
-        rb->read_ptr=rb->data;
-    }
-    return ret;
-}
-
-//Retrieves a pointer to the data in the buffer, or NULL if this is not possible.
-//This function by itself is not threadsafe, always call from within a muxed section.
-//This function honours the wanted_length and will never return more data than this.
-static uint8_t *getItemFromRingbufByteBuf(ringbuf_t *rb, size_t *length, int wanted_length)
-{
-    uint8_t *ret;
-    if (rb->read_ptr != rb->free_ptr) {
-        //This type of ringbuff does not support multiple outstanding buffers.
-        return NULL;
+    pcReturn = pxRingbuffer->pucRead + rbHEADER_SIZE;    //Get pointer to part of item containing data (point past the header)
+    if (pxHeader->xItemLen == 0) {
+        //Inclusive of pucTail for special case where item of zero length just fits at the end of the buffer
+        configASSERT(pcReturn >= pxRingbuffer->pucHead && pcReturn <= pxRingbuffer->pucTail);
+    } else {
+        //Exclusive of pucTali if length is larger than zero, pcReturn should never point to pucTail
+        configASSERT(pcReturn >= pxRingbuffer->pucHead && pcReturn < pxRingbuffer->pucTail);
     }
-    if (rb->read_ptr == rb->write_ptr) {
-        //No data available.
-        return NULL;
+    *pxItemSize = pxHeader->xItemLen;   //Get length of item
+    pxRingbuffer->xItemsWaiting --;     //Update item count
+    *pxIsSplit = (pxHeader->uxItemFlags & rbITEM_SPLIT_FLAG) ? pdTRUE : pdFALSE;
+
+    pxRingbuffer->pucRead += rbHEADER_SIZE + rbALIGN_SIZE(pxHeader->xItemLen);   //Update pucRead
+    //Check if pucRead requires wrap around
+    if ((pxRingbuffer->pucTail - pxRingbuffer->pucRead) < rbHEADER_SIZE) {
+        pxRingbuffer->pucRead = pxRingbuffer->pucHead;
     }
-    ret=rb->read_ptr;
-    if (rb->read_ptr > rb->write_ptr) {
-        //Available data wraps around. Give data until the end of the buffer.
-        *length=rb->size-(rb->read_ptr - rb->data);
-        if (wanted_length != 0 && *length > wanted_length) {
-            *length=wanted_length;
-            rb->read_ptr+=wanted_length;
+    return (void *)pcReturn;
+}
+
+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
+    configASSERT(pxRingbuffer->pucRead >= pxRingbuffer->pucHead && pxRingbuffer->pucRead < pxRingbuffer->pucTail);    //Check read pointer is within bounds
+    configASSERT(pxRingbuffer->pucRead == pxRingbuffer->pucFree);
+
+    uint8_t *ret = pxRingbuffer->pucRead;
+    if ((pxRingbuffer->pucRead > pxRingbuffer->pucWrite) || (pxRingbuffer->uxRingbufferFlags & rbBUFFER_FULL_FLAG)) {     //Available data wraps around
+        //Return contiguous piece from read pointer until buffer tail, or xMaxSize
+        if (xMaxSize == 0 || pxRingbuffer->pucTail - pxRingbuffer->pucRead <= xMaxSize) {
+            //All contiguous data from read pointer to tail
+            *pxItemSize = pxRingbuffer->pucTail - pxRingbuffer->pucRead;
+            pxRingbuffer->xItemsWaiting -= pxRingbuffer->pucTail - pxRingbuffer->pucRead;
+            pxRingbuffer->pucRead = pxRingbuffer->pucHead;  //Wrap around read pointer
         } else {
-            rb->read_ptr=rb->data;
+            //Return xMaxSize amount of data
+            *pxItemSize = xMaxSize;
+            pxRingbuffer->xItemsWaiting -= xMaxSize;
+            pxRingbuffer->pucRead += xMaxSize;  //Advance read pointer past retrieved data
         }
-    } else {
-        //Return data up to write pointer.
-        *length=rb->write_ptr -rb->read_ptr;
-        if (wanted_length != 0 && *length > wanted_length) {
-            *length=wanted_length;
-            rb->read_ptr+=wanted_length;
+    } else {    //Available data is contiguous between read and write pointer
+        if (xMaxSize == 0 || pxRingbuffer->pucWrite - pxRingbuffer->pucRead <= xMaxSize) {
+            //Return all contiguous data from read to write pointer
+            *pxItemSize = pxRingbuffer->pucWrite - pxRingbuffer->pucRead;
+            pxRingbuffer->xItemsWaiting -= pxRingbuffer->pucWrite - pxRingbuffer->pucRead;
+            pxRingbuffer->pucRead = pxRingbuffer->pucWrite;
         } else {
-            rb->read_ptr=rb->write_ptr;
+            //Return xMaxSize data from read pointer
+            *pxItemSize = xMaxSize;
+            pxRingbuffer->xItemsWaiting -= xMaxSize;
+            pxRingbuffer->pucRead += xMaxSize;  //Advance read pointer past retrieved data
+
         }
     }
-    return ret;
-}
-
-
-//Returns an item to the ringbuffer. Will mark the item as free, and will see if the free pointer
-//can be increase.
-//This function by itself is not threadsafe, always call from within a muxed section.
-static void returnItemToRingbufDefault(ringbuf_t *rb, void *item) {
-    uint8_t *data=(uint8_t*)item;
-    configASSERT(((int)rb->free_ptr&3)==0);
-    configASSERT(data >= rb->data);
-    configASSERT(data <= rb->data+rb->size);
-    //Grab the buffer entry that preceeds the buffer
-    buf_entry_hdr_t *hdr=(buf_entry_hdr_t*)(data-sizeof(buf_entry_hdr_t));
-    configASSERT(hdr->len < rb->size);
-    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.
-    hdr=(buf_entry_hdr_t *)rb->free_ptr;
-    //basically forward free_ptr until we run into either a block that is still in use or the write pointer.
-    while (((hdr->flags & iflag_free) || (hdr->flags & iflag_dummydata)) && rb->free_ptr != rb->write_ptr) {
-        if (hdr->flags & iflag_dummydata) {
-            //Rest is dummy data. Reset to start of ringbuffer.
-            rb->free_ptr=rb->data;
+    return (void *)ret;
+}
+
+static void prvReturnItemDefault(Ringbuffer_t *pxRingbuffer, uint8_t *pucItem)
+{
+    //Check arguments and buffer state
+    configASSERT(rbCHECK_ALIGNED(pucItem));
+    configASSERT(pucItem >= pxRingbuffer->pucHead);
+    configASSERT(pucItem <= pxRingbuffer->pucTail);     //Inclusive of pucTail in the case of zero length item at the very end
+
+    //Get and check header of the item
+    ItemHeader_t *pxCurHeader = (ItemHeader_t *)(pucItem - rbHEADER_SIZE);
+    configASSERT(pxCurHeader->xItemLen <= pxRingbuffer->xMaxItemSize);
+    configASSERT((pxCurHeader->uxItemFlags & rbITEM_DUMMY_DATA_FLAG) == 0); //Dummy items should never have been read
+    configASSERT((pxCurHeader->uxItemFlags & rbITEM_FREE_FLAG) == 0);       //Indicates item has already been returned before
+    pxCurHeader->uxItemFlags &= ~rbITEM_SPLIT_FLAG;                         //Clear wrap flag if set (not strictly necessary)
+    pxCurHeader->uxItemFlags |= rbITEM_FREE_FLAG;                           //Mark as free
+
+    /*
+     * Items might not be returned in the order they were retrieved. Move the free pointer
+     * up to the next item that has not been marked as free (by free flag) or up
+     * till the read pointer. When advancing the free pointer, items that have already been
+     * freed or items with dummy data should be skipped over
+     */
+    pxCurHeader = (ItemHeader_t *)pxRingbuffer->pucFree;
+    //Skip over Items that have already been freed or are dummy items
+    while (((pxCurHeader->uxItemFlags & rbITEM_FREE_FLAG) || (pxCurHeader->uxItemFlags & rbITEM_DUMMY_DATA_FLAG)) && pxRingbuffer->pucFree != pxRingbuffer->pucRead) {
+        if (pxCurHeader->uxItemFlags & rbITEM_DUMMY_DATA_FLAG) {
+            pxCurHeader->uxItemFlags |= rbITEM_FREE_FLAG;   //Mark as freed (not strictly necessary but adds redundancy)
+            pxRingbuffer->pucFree = pxRingbuffer->pucHead;    //Wrap around due to dummy data
         } else {
-            //Skip past item
-            rb->free_ptr+=sizeof(buf_entry_hdr_t);
-            //Check if the free_ptr overshoots the buffer.
-            //Checking this before aligning free_ptr since it is possible that alignment
-            //will cause pointer to overshoot, if the ringbuf size is not a multiple of 4
-            configASSERT(rb->free_ptr+hdr->len<=rb->data+rb->size);
-            //Align free_ptr to 4 byte boundary. Overshoot condition will result in wrap around below
-            size_t len=(hdr->len+3)&~3;
-            rb->free_ptr+=len;
+            //Item with data that has already been freed, advance free pointer past this item
+            size_t xAlignedItemSize = rbALIGN_SIZE(pxCurHeader->xItemLen);
+            pxRingbuffer->pucFree += xAlignedItemSize + rbHEADER_SIZE;
+            //Redundancy check to ensure free pointer has not overshot buffer bounds
+            configASSERT(pxRingbuffer->pucFree <= pxRingbuffer->pucHead + pxRingbuffer->xSize);
         }
-        //The buffer will wrap around if we don't have room for a header anymore.
-        //Integer typecasting is used because the first operand can result into a -ve
-        //value for cases wherein the ringbuffer size is not a multiple of 4, but the
-        //implementation logic aligns free_ptr to 4-byte boundary
-        if ((int)((rb->data+rb->size)-rb->free_ptr) < (int)sizeof(buf_entry_hdr_t)) {
-            rb->free_ptr=rb->data;
+        //Check if pucRead requires wrap around
+        if ((pxRingbuffer->pucTail - pxRingbuffer->pucFree) < rbHEADER_SIZE) {
+            pxRingbuffer->pucFree = pxRingbuffer->pucHead;
         }
-        //The free_ptr can not exceed read_ptr, otherwise write_ptr might overwrite read_ptr.
-        //Read_ptr can not set to rb->data with free_ptr, otherwise write_ptr might wrap around to rb->data.
-        if(rb->free_ptr == rb->read_ptr) break;
-        //Next header
-        hdr=(buf_entry_hdr_t *)rb->free_ptr;
+        pxCurHeader = (ItemHeader_t *)pxRingbuffer->pucFree;      //Update header to point to item
     }
-}
 
-
-//Returns an item to the ringbuffer. Will mark the item as free, and will see if the free pointer
-//can be increase.
-//This function by itself is not threadsafe, always call from within a muxed section.
-static void returnItemToRingbufBytebuf(ringbuf_t *rb, void *item) {
-    configASSERT((uint8_t *)item >= rb->data);
-    configASSERT((uint8_t *)item < rb->data+rb->size);
-    //Free the read memory.
-    rb->free_ptr=rb->read_ptr;
+    //Check if the buffer full flag should be reset
+    if (pxRingbuffer->uxRingbufferFlags & rbBUFFER_FULL_FLAG) {
+        if (pxRingbuffer->pucFree != pxRingbuffer->pucWrite) {
+            pxRingbuffer->uxRingbufferFlags &= ~rbBUFFER_FULL_FLAG;
+        } else if (pxRingbuffer->pucFree == pxRingbuffer->pucWrite && pxRingbuffer->pucFree == pxRingbuffer->pucRead) {
+            //Special case where a full buffer is completely freed in one go
+            pxRingbuffer->uxRingbufferFlags &= ~rbBUFFER_FULL_FLAG;
+        }
+    }
 }
-/*
-    Check if the selected queue set member is the ringbuffer's read semaphore
-*/
-BaseType_t xRingbufferCanRead(RingbufHandle_t ringbuf, QueueSetMemberHandle_t member)
+
+static void prvReturnItemByteBuf(Ringbuffer_t *pxRingbuffer, uint8_t *pucItem)
 {
-    ringbuf_t *rb=(ringbuf_t *)ringbuf;
-    configASSERT(rb);
-    return (rb->items_buffered_sem == member)? pdTRUE : pdFALSE;
+    //Check pointer points to address inside buffer
+    configASSERT((uint8_t *)pucItem >= pxRingbuffer->pucHead);
+    configASSERT((uint8_t *)pucItem < pxRingbuffer->pucTail);
+    //Free the read memory. Simply moves free pointer to read pointer as byte buffers do not allow multiple outstanding reads
+    pxRingbuffer->pucFree = pxRingbuffer->pucRead;
+    //If buffer was full before, reset full flag as free pointer has moved
+    if (pxRingbuffer->uxRingbufferFlags & rbBUFFER_FULL_FLAG) {
+        pxRingbuffer->uxRingbufferFlags &= ~rbBUFFER_FULL_FLAG;
+    }
 }
-/*
-    Check if the selected queue set member is the ringbuffer's write semaphore
-*/
-BaseType_t xRingbufferCanWrite(RingbufHandle_t ringbuf, QueueSetMemberHandle_t member)
+
+static size_t prvGetCurMaxSizeNoSplit(Ringbuffer_t *pxRingbuffer)
 {
-    ringbuf_t *rb=(ringbuf_t *)ringbuf;
-    configASSERT(rb);
-    return (rb->free_space_sem == member)? pdTRUE : pdFALSE;
+    BaseType_t xFreeSize;
+    //Check if buffer is full
+    if (pxRingbuffer->uxRingbufferFlags & rbBUFFER_FULL_FLAG) {
+        return 0;
+    }
+    if (pxRingbuffer->pucWrite < pxRingbuffer->pucFree) {
+        //Free space is contiguous between pucWrite and pucFree
+        xFreeSize = pxRingbuffer->pucFree - pxRingbuffer->pucWrite;
+    } else {
+        //Free space wraps around (or overlapped at pucHead), select largest
+        //contiguous free space as no-split items require contiguous space
+        size_t xSize1 = pxRingbuffer->pucTail - pxRingbuffer->pucWrite;
+        size_t xSize2 = pxRingbuffer->pucFree - pxRingbuffer->pucHead;
+        xFreeSize = (xSize1 > xSize2) ? xSize1 : xSize2;
+    }
+
+    //No-split ring buffer items need space for a header
+    xFreeSize -= rbHEADER_SIZE;
+    //Limit free size to be within bounds
+    if (xFreeSize > pxRingbuffer->xMaxItemSize) {
+        xFreeSize = pxRingbuffer->xMaxItemSize;
+    } else if (xFreeSize < 0) {
+        //Occurs when free space is less than header size
+        xFreeSize = 0;
+    }
+    return xFreeSize;
 }
 
-void xRingbufferPrintInfo(RingbufHandle_t ringbuf)
+static size_t prvGetCurMaxSizeAllowSplit(Ringbuffer_t *pxRingbuffer)
 {
-    ringbuf_t *rb=(ringbuf_t *)ringbuf;
-    configASSERT(rb);
-    ets_printf("Rb size %d free %d rptr %d freeptr %d wptr %d\n",
-            rb->size, ringbufferFreeMem(rb), rb->read_ptr-rb->data, rb->free_ptr-rb->data, rb->write_ptr-rb->data);
-}
+    BaseType_t xFreeSize;
+    //Check if buffer is full
+    if (pxRingbuffer->uxRingbufferFlags & rbBUFFER_FULL_FLAG) {
+        return 0;
+    }
+    if (pxRingbuffer->pucWrite == pxRingbuffer->pucHead && pxRingbuffer->pucFree == pxRingbuffer->pucHead) {
+        //Check for special case where pucWrite and pucFree are both at pucHead
+        xFreeSize = pxRingbuffer->xSize - rbHEADER_SIZE;
+    } else if (pxRingbuffer->pucWrite < pxRingbuffer->pucFree) {
+        //Free space is contiguous between pucWrite and pucFree, requires single header
+        xFreeSize = (pxRingbuffer->pucFree - pxRingbuffer->pucWrite) - rbHEADER_SIZE;
+    } else {
+        //Free space wraps around, requires two headers
+        xFreeSize = (pxRingbuffer->pucFree - pxRingbuffer->pucHead) +
+                    (pxRingbuffer->pucTail - pxRingbuffer->pucWrite) -
+                    (rbHEADER_SIZE * 2);
+    }
 
+    //Limit free size to be within bounds
+    if (xFreeSize > pxRingbuffer->xMaxItemSize) {
+        xFreeSize = pxRingbuffer->xMaxItemSize;
+    } else if (xFreeSize < 0) {
+        xFreeSize = 0;
+    }
+    return xFreeSize;
+}
 
-size_t xRingbufferGetCurFreeSize(RingbufHandle_t ringbuf)
+static size_t prvGetCurMaxSizeByteBuf(Ringbuffer_t *pxRingbuffer)
 {
-    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;
+    BaseType_t xFreeSize;
+    //Check if buffer is full
+    if (pxRingbuffer->uxRingbufferFlags & rbBUFFER_FULL_FLAG) {
+        return 0;
+    }
+
+    /*
+     * 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
+     */
+    xFreeSize = pxRingbuffer->pucFree - pxRingbuffer->pucWrite;
+    if (xFreeSize <= 0) {
+        xFreeSize += pxRingbuffer->xSize;
+    }
+    return xFreeSize;
 }
 
-static size_t getCurFreeSizeByteBuf(ringbuf_t *rb)
+static BaseType_t prvReceiveGeneric(Ringbuffer_t *pxRingbuffer, void **pvItem1, void **pvItem2, size_t *xItemSize1, size_t *xItemSize2, size_t xMaxSize, TickType_t xTicksToWait)
 {
-    //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;
+    BaseType_t xReturn = pdFALSE;
+    BaseType_t xReturnSemaphore = pdFALSE;
+    TickType_t xTicksEnd = xTaskGetTickCount() + xTicksToWait;
+    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) {
+            xReturn = pdFALSE;     //Timed out attempting to get semaphore
+            break;
+        }
+
+        //Semaphore obtained, check if item can be retrieved
+        taskENTER_CRITICAL(&pxRingbuffer->mux);
+        if (prvCheckItemAvail(pxRingbuffer) == pdTRUE) {
+            //Item is available for retrieval
+            BaseType_t xIsSplit;
+            if (pxRingbuffer->uxRingbufferFlags & rbBYTE_BUFFER_FLAG) {
+                //Second argument (pxIsSplit) is unused for byte buffers
+                *pvItem1 = pxRingbuffer->pvGetItem(pxRingbuffer, NULL, xMaxSize, xItemSize1);
+            } else {
+                //Third argument (xMaxSize) is unused for no-split/allow-split buffers
+                *pvItem1 = pxRingbuffer->pvGetItem(pxRingbuffer, &xIsSplit, 0, xItemSize1);
+            }
+            //Check for item split if configured to do so
+            if ((pxRingbuffer->uxRingbufferFlags & rbALLOW_SPLIT_FLAG) && (pvItem2 != NULL) && (xItemSize2 != NULL)) {
+                if (xIsSplit == pdTRUE) {
+                    *pvItem2 = pxRingbuffer->pvGetItem(pxRingbuffer, &xIsSplit, 0, xItemSize2);
+                    configASSERT(*pvItem2 < *pvItem1);  //Check wrap around has occurred
+                    configASSERT(xIsSplit == pdFALSE);  //Second part should not have wrapped flag
+                } else {
+                    *pvItem2 = NULL;
+                }
+            }
+            xReturn = pdTRUE;
+            if (pxRingbuffer->xItemsWaiting > 0) {
+                xReturnSemaphore = pdTRUE;
+            }
+            taskEXIT_CRITICAL(&pxRingbuffer->mux);
+            break;
+        }
+        //No item available for retrieval, adjust ticks and take the semaphore again
+        if (xTicksToWait != portMAX_DELAY) {
+            xTicksRemaining = xTicksEnd - xTaskGetTickCount();
+        }
+        taskEXIT_CRITICAL(&pxRingbuffer->mux);
+        /*
+         * Gap between critical section and re-acquiring of the semaphore. If
+         * semaphore is given now, priority inversion might occur (see docs)
+         */
+    }
+
+    if (xReturnSemaphore == pdTRUE) {
+        xSemaphoreGive(pxRingbuffer->xItemsBufferedSemaphore);  //Give semaphore back so other tasks can retrieve
+    }
+    return xReturn;
 }
 
-static size_t getCurFreeSizeAllowSplit(ringbuf_t *rb)
+static BaseType_t prvReceiveGenericFromISR(Ringbuffer_t *pxRingbuffer, void **pvItem1, void **pvItem2, size_t *xItemSize1, size_t *xItemSize2, size_t xMaxSize)
 {
-    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));
+    BaseType_t xReturn = pdFALSE;
+    BaseType_t xReturnSemaphore = pdFALSE;
+
+    taskENTER_CRITICAL_ISR(&pxRingbuffer->mux);
+    if(prvCheckItemAvail(pxRingbuffer) == pdTRUE) {
+        BaseType_t xIsSplit;
+        if (pxRingbuffer->uxRingbufferFlags & rbBYTE_BUFFER_FLAG) {
+            //Second argument (pxIsSplit) is unused for byte buffers
+            *pvItem1 = pxRingbuffer->pvGetItem(pxRingbuffer, NULL, xMaxSize, xItemSize1);
+        } else {
+            //Third argument (xMaxSize) is unused for no-split/allow-split buffers
+            *pvItem1 = pxRingbuffer->pvGetItem(pxRingbuffer, &xIsSplit, 0, xItemSize1);
+        }
+        //Check for item split if configured to do so
+        if ((pxRingbuffer->uxRingbufferFlags & rbALLOW_SPLIT_FLAG) && pvItem2 != NULL && xItemSize2 != NULL) {
+            if (xIsSplit == pdTRUE) {
+                *pvItem2 = pxRingbuffer->pvGetItem(pxRingbuffer, &xIsSplit, 0, xItemSize2);
+                configASSERT(*pvItem2 < *pvItem1);  //Check wrap around has occurred
+                configASSERT(xIsSplit == pdFALSE);  //Second part should not have wrapped flag
+            } else {
+                *pvItem2 = NULL;
+            }
+        }
+        xReturn = pdTRUE;
+        if (pxRingbuffer->xItemsWaiting > 0) {
+            xReturnSemaphore = pdTRUE;
+        }
     }
-    return free_size;
+    taskEXIT_CRITICAL_ISR(&pxRingbuffer->mux);
+
+    if (xReturnSemaphore == pdTRUE) {
+        xSemaphoreGiveFromISR(pxRingbuffer->xItemsBufferedSemaphore, NULL);  //Give semaphore back so other tasks can retrieve
+    }
+    return xReturn;
 }
 
-static size_t getCurFreeSizeNoSplit(ringbuf_t *rb)
+/* ------------------------------------------------- Public Definitions -------------------------------------------- */
+
+RingbufHandle_t xRingbufferCreate(size_t xBufferSize, ringbuf_type_t xBufferType)
 {
-    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)
-{
-    ringbuf_t *rb = malloc(sizeof(ringbuf_t));
-    if (rb==NULL) goto err;
-    memset(rb, 0, sizeof(ringbuf_t));
-    rb->data = malloc(buf_length);
-    if (rb->data == NULL) goto err;
-    rb->size = buf_length;
-    rb->free_ptr = rb->data;
-    rb->read_ptr = rb->data;
-    rb->write_ptr = rb->data;
-    rb->free_space_sem = xSemaphoreCreateBinary();
-    rb->items_buffered_sem = xSemaphoreCreateBinary();
-    rb->flags=0;
-    if (type==RINGBUF_TYPE_ALLOWSPLIT) {
-        rb->flags|=flag_allowsplit;
-        rb->copyItemToRingbufImpl=copyItemToRingbufAllowSplit;
-        rb->getItemFromRingbufImpl=getItemFromRingbufDefault;
-        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;
-        rb->getItemFromRingbufImpl=getItemFromRingbufByteBuf;
-        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;
-        rb->returnItemToRingbufImpl=returnItemToRingbufDefault;
-        //Calculate max item size. Worst case, we have the write ptr in such a position that we are lacking four bytes of free
-        //memory to put an item into the rest of the memory. If this happens, we have to dummy-fill
-        //(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;
+    //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) {
+        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 (rb->free_space_sem == NULL || rb->items_buffered_sem == NULL) goto err;
-    vPortCPUInitializeMutex(&rb->mux);
 
-    return (RingbufHandle_t)rb;
+    if (pxRingbuffer->xFreeSpaceSemaphore == NULL || pxRingbuffer->xItemsBufferedSemaphore == NULL) {
+        goto err;
+    }
+    xSemaphoreGive(pxRingbuffer->xFreeSpaceSemaphore);
+    vPortCPUInitializeMutex(&pxRingbuffer->mux);
+
+    return (RingbufHandle_t)pxRingbuffer;
 
 err:
     //Some error has happened. Free/destroy all allocated things and return NULL.
-    if (rb) {
-        free(rb->data);
-        if (rb->free_space_sem) vSemaphoreDelete(rb->free_space_sem);
-        if (rb->items_buffered_sem) vSemaphoreDelete(rb->items_buffered_sem);
+    if (pxRingbuffer) {
+        free(pxRingbuffer->pucHead);
+        if (pxRingbuffer->xFreeSpaceSemaphore) {
+            vSemaphoreDelete(pxRingbuffer->xFreeSpaceSemaphore);
+        }
+        if (pxRingbuffer->xItemsBufferedSemaphore) {
+            vSemaphoreDelete(pxRingbuffer->xItemsBufferedSemaphore);
+        }
     }
-    free(rb);
+    free(pxRingbuffer);
     return NULL;
 }
 
-RingbufHandle_t xRingbufferCreateNoSplit(size_t item_size, size_t num_item)
+RingbufHandle_t xRingbufferCreateNoSplit(size_t xItemSize, size_t xItemNum)
 {
-    size_t aligned_size = (item_size+3)&~3;
-    return xRingbufferCreate((aligned_size + sizeof(buf_entry_hdr_t)) * num_item, RINGBUF_TYPE_NOSPLIT);
+    return xRingbufferCreate((rbALIGN_SIZE(xItemSize) + rbHEADER_SIZE) * xItemNum, RINGBUF_TYPE_NOSPLIT);
 }
 
-void vRingbufferDelete(RingbufHandle_t ringbuf) {
-    ringbuf_t *rb=(ringbuf_t *)ringbuf;
-    if (rb) {
-        free(rb->data);
-        if (rb->free_space_sem) vSemaphoreDelete(rb->free_space_sem);
-        if (rb->items_buffered_sem) vSemaphoreDelete(rb->items_buffered_sem);
+BaseType_t xRingbufferSend(RingbufHandle_t xRingbuffer, const void *pvItem, size_t xItemSize, TickType_t xTicksToWait)
+{
+    //Check arguments
+    Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
+    configASSERT(pxRingbuffer);
+    configASSERT(pvItem != NULL || xItemSize == 0);
+    if (xItemSize > pxRingbuffer->xMaxItemSize) {
+        return pdFALSE;     //Data will never ever fit in the queue.
+    }
+    if ((pxRingbuffer->uxRingbufferFlags & rbBYTE_BUFFER_FLAG) && xItemSize == 0) {
+        return pdTRUE;      //Sending 0 bytes to byte buffer has no effect
+    }
+
+    //Attempt to send an item
+    BaseType_t xReturn = pdFALSE;
+    BaseType_t xReturnSemaphore = pdFALSE;
+    TickType_t xTicksEnd = xTaskGetTickCount() + xTicksToWait;
+    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) {
+            xReturn = pdFALSE;
+            break;
+        }
+        //Semaphore obtained, check if item can fit
+        taskENTER_CRITICAL(&pxRingbuffer->mux);
+        if(pxRingbuffer->xCheckItemFits(pxRingbuffer, xItemSize) == pdTRUE) {
+            //Item will fit, copy item
+            pxRingbuffer->vCopyItem(pxRingbuffer, pvItem, xItemSize);
+            xReturn = pdTRUE;
+            //Check if the free semaphore should be returned to allow other tasks to send
+            if (prvGetFreeSize(pxRingbuffer) > 0) {
+                xReturnSemaphore = pdTRUE;
+            }
+            taskEXIT_CRITICAL(&pxRingbuffer->mux);
+            break;
+        }
+        //Item doesn't fit, adjust ticks and take the semaphore again
+        if (xTicksToWait != portMAX_DELAY) {
+            xTicksRemaining = xTicksEnd - xTaskGetTickCount();
+        }
+        taskEXIT_CRITICAL(&pxRingbuffer->mux);
+        /*
+         * Gap between critical section and re-acquiring of the semaphore. If
+         * semaphore is given now, priority inversion might occur (see docs)
+         */
+    }
+
+    if (xReturn == pdTRUE) {
+        //Indicate item was successfully sent
+        xSemaphoreGive(pxRingbuffer->xItemsBufferedSemaphore);
+    }
+    if (xReturnSemaphore == pdTRUE) {
+        xSemaphoreGive(pxRingbuffer->xFreeSpaceSemaphore);  //Give back semaphore so other tasks can send
     }
-    free(rb);
+    return xReturn;
 }
 
-size_t xRingbufferGetMaxItemSize(RingbufHandle_t ringbuf)
+BaseType_t xRingbufferSendFromISR(RingbufHandle_t xRingbuffer, const void *pvItem, size_t xItemSize, BaseType_t *pxHigherPriorityTaskWoken)
 {
-    ringbuf_t *rb=(ringbuf_t *)ringbuf;
-    configASSERT(rb);
-    return rb->maxItemSize;
+    //Check arguments
+    Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
+    configASSERT(pxRingbuffer);
+    configASSERT(pvItem != NULL || xItemSize == 0);
+    if (xItemSize > pxRingbuffer->xMaxItemSize) {
+        return pdFALSE;     //Data will never ever fit in the queue.
+    }
+    if ((pxRingbuffer->uxRingbufferFlags & rbBYTE_BUFFER_FLAG) && xItemSize == 0) {
+        return pdTRUE;      //Sending 0 bytes to byte buffer has no effect
+    }
+
+    //Attempt to send an item
+    BaseType_t xReturn;
+    BaseType_t xReturnSemaphore = pdFALSE;
+    taskENTER_CRITICAL_ISR(&pxRingbuffer->mux);
+    if (pxRingbuffer->xCheckItemFits(xRingbuffer, xItemSize) == pdTRUE) {
+        pxRingbuffer->vCopyItem(xRingbuffer, pvItem, xItemSize);
+        xReturn = pdTRUE;
+        //Check if the free semaphore should be returned to allow other tasks to send
+        if (prvGetFreeSize(pxRingbuffer) > 0) {
+            xReturnSemaphore = pdTRUE;
+        }
+    } else {
+        xReturn = pdFALSE;
+    }
+    taskEXIT_CRITICAL_ISR(&pxRingbuffer->mux);
+
+    if (xReturn == pdTRUE) {
+        //Indicate item was successfully sent
+        xSemaphoreGiveFromISR(pxRingbuffer->xItemsBufferedSemaphore, pxHigherPriorityTaskWoken);
+    }
+    if (xReturnSemaphore == pdTRUE) {
+        xSemaphoreGiveFromISR(pxRingbuffer->xFreeSpaceSemaphore, pxHigherPriorityTaskWoken);  //Give back semaphore so other tasks can send
+    }
+    return xReturn;
 }
 
-bool xRingbufferIsNextItemWrapped(RingbufHandle_t ringbuf)
+void *xRingbufferReceive(RingbufHandle_t xRingbuffer, size_t *pxItemSize, TickType_t xTicksToWait)
 {
-    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;
+    //Check arguments
+    Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
+    configASSERT(pxRingbuffer);
+
+    //Attempt to retrieve an item
+    void *pvTempItem;
+    size_t xTempSize;
+    if (prvReceiveGeneric(pxRingbuffer, &pvTempItem, NULL, &xTempSize, NULL, 0, xTicksToWait) == pdTRUE) {
+        if (pxItemSize != NULL) {
+            *pxItemSize = xTempSize;
+        }
+        return pvTempItem;
+    } else {
+        return NULL;
+    }
 }
 
-
-BaseType_t xRingbufferSend(RingbufHandle_t ringbuf, void *data, size_t dataSize, TickType_t ticks_to_wait)
+void *xRingbufferReceiveFromISR(RingbufHandle_t xRingbuffer, size_t *pxItemSize)
 {
-    ringbuf_t *rb=(ringbuf_t *)ringbuf;
-    size_t needed_size=dataSize+sizeof(buf_entry_hdr_t);
-    BaseType_t done=pdFALSE;
-    TickType_t ticks_end = xTaskGetTickCount() + ticks_to_wait;
-    TickType_t ticks_remaining = ticks_to_wait;
-
-    configASSERT(rb);
+    //Check arguments
+    Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
+    configASSERT(pxRingbuffer);
+
+    //Attempt to retrieve an item
+    void *pvTempItem;
+    size_t xTempSize;
+    if (prvReceiveGenericFromISR(pxRingbuffer, &pvTempItem, NULL, &xTempSize, NULL, 0) == pdTRUE) {
+        if (pxItemSize != NULL) {
+            *pxItemSize = xTempSize;
+        }
+        return pvTempItem;
+    } else {
+        return NULL;
+    }
+}
 
-    if (dataSize > xRingbufferGetMaxItemSize(ringbuf)) {
-        //Data will never ever fit in the queue.
+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;
+    configASSERT(pxRingbuffer);
+    configASSERT(pxRingbuffer->uxRingbufferFlags & rbALLOW_SPLIT_FLAG);
+    configASSERT(ppvHeadItem != NULL && ppvTailItem != NULL);
+
+    //Attempt to retrieve multiple items
+    void *pvTempHeadItem, *pvTempTailItem;
+    size_t xTempHeadSize, xTempTailSize;
+    if (prvReceiveGeneric(pxRingbuffer, &pvTempHeadItem, &pvTempTailItem, &xTempHeadSize, &xTempTailSize, 0, xTicksToWait) == pdTRUE) {
+        //At least one item was retrieved
+        *ppvHeadItem = pvTempHeadItem;
+        if(pxHeadItemSize != NULL){
+            *pxHeadItemSize = xTempHeadSize;
+        }
+        //Check to see if a second item was also retrieved
+        if (pvTempTailItem != NULL) {
+            *ppvTailItem = pvTempTailItem;
+            if (pxTailItemSize != NULL) {
+                *pxTailItemSize = xTempTailSize;
+            }
+        } else {
+            *ppvTailItem = NULL;
+        }
+        return pdTRUE;
+    } else {
+        //No items retrieved
+        *ppvHeadItem = NULL;
+        *ppvTailItem = NULL;
         return pdFALSE;
     }
+}
 
-    while (!done) {
-        //Check if there is enough room in the buffer. If not, wait until there is.
-        do {
-            if (ringbufferFreeMem(rb) < needed_size) {
-                //Data does not fit yet. Wait until the free_space_sem is given, then re-evaluate.
-
-                BaseType_t r = xSemaphoreTake(rb->free_space_sem, ticks_remaining);
-                if (r == pdFALSE) {
-                    //Timeout.
-                    return pdFALSE;
-                }
-                //Adjust ticks_remaining; we may have waited less than that and in the case the free memory still is not enough,
-                //we will need to wait some more.
-                if (ticks_to_wait != portMAX_DELAY) {
-                    ticks_remaining = ticks_end - xTaskGetTickCount();
-
-                    // ticks_remaining will always be less than or equal to the original ticks_to_wait,
-                    // unless the timeout is reached - in which case it unsigned underflows to a much
-                    // higher value.
-                    //
-                    // (Check is written this non-intuitive way to allow for the case where xTaskGetTickCount()
-                    // has overflowed but the ticks_end value has not overflowed.)
-                    if(ticks_remaining > ticks_to_wait) {
-                        //Timeout, but there is not enough free space for the item that need to be sent.
-                        xSemaphoreGive(rb->free_space_sem);
-                        return pdFALSE;
-                    }
-                }
-
+BaseType_t xRingbufferReceiveSplitFromISR(RingbufHandle_t xRingbuffer, void **ppvHeadItem, void **ppvTailItem, size_t *pxHeadItemSize, size_t *pxTailItemSize)
+{
+    //Check arguments
+    Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
+    configASSERT(pxRingbuffer);
+    configASSERT(pxRingbuffer->uxRingbufferFlags & rbALLOW_SPLIT_FLAG);
+    configASSERT(ppvHeadItem != NULL && ppvTailItem != NULL);
+
+    //Attempt to retrieve multiple items
+    void *pvTempHeadItem, *pvTempTailItem;
+    size_t xTempHeadSize, xTempTailSize;
+    if (prvReceiveGenericFromISR(pxRingbuffer, &pvTempHeadItem, &pvTempTailItem, &xTempHeadSize, &xTempTailSize, 0) == pdTRUE) {
+        //At least one item was received
+        *ppvHeadItem = pvTempHeadItem;
+        if (pxHeadItemSize != NULL) {
+            *pxHeadItemSize = xTempHeadSize;
+        }
+        //Check to see if a second item was also retrieved
+        if (pvTempTailItem != NULL) {
+            *ppvTailItem = pvTempTailItem;
+            if (pxTailItemSize != NULL) {
+                *pxTailItemSize = xTempTailSize;
             }
-        } while (ringbufferFreeMem(rb) < needed_size);
-
-        //Lock the mux in order to make sure no one else is messing with the ringbuffer and do the copy.
-        portENTER_CRITICAL(&rb->mux);
-        //Another thread may have been able to sneak its write first. Check again now we locked the ringbuff, and retry
-        //everything if this is the case. Otherwise, we can write and are done.
-        done=rb->copyItemToRingbufImpl(rb, data, dataSize);
-        portEXIT_CRITICAL(&rb->mux);
+        } else {
+            *ppvTailItem = NULL;
+        }
+        return pdTRUE;
+    } else {
+        *ppvHeadItem = NULL;
+        *ppvTailItem = NULL;
+        return pdFALSE;
     }
-    xSemaphoreGive(rb->items_buffered_sem);
-    return pdTRUE;
 }
 
-
-BaseType_t xRingbufferSendFromISR(RingbufHandle_t ringbuf, void *data, size_t dataSize, BaseType_t *higher_prio_task_awoken)
+void *xRingbufferReceiveUpTo(RingbufHandle_t xRingbuffer, size_t *pxItemSize, TickType_t xTicksToWait, size_t xMaxSize)
 {
-    ringbuf_t *rb=(ringbuf_t *)ringbuf;
-    BaseType_t write_succeeded;
-    configASSERT(rb);
-    size_t needed_size=dataSize+sizeof(buf_entry_hdr_t);
-    portENTER_CRITICAL_ISR(&rb->mux);
-    if (needed_size>ringbufferFreeMem(rb)) {
-        //Does not fit in the remaining space in the ringbuffer.
-        write_succeeded=pdFALSE;
-    } else {
-        write_succeeded = rb->copyItemToRingbufImpl(rb, data, dataSize);
+    //Check arguments
+    Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
+    configASSERT(pxRingbuffer);
+    configASSERT(pxRingbuffer->uxRingbufferFlags & rbBYTE_BUFFER_FLAG);    //This function should only be called for byte buffers
+    if (xMaxSize == 0) {
+        return NULL;
     }
-    portEXIT_CRITICAL_ISR(&rb->mux);
-    if (write_succeeded) {
-        xSemaphoreGiveFromISR(rb->items_buffered_sem, higher_prio_task_awoken);
+
+    //Attempt to retrieve up to xMaxSize bytes
+    void *pvTempItem;
+    size_t xTempSize;
+    if (prvReceiveGeneric(pxRingbuffer, &pvTempItem, NULL, &xTempSize, NULL, xMaxSize, xTicksToWait) == pdTRUE) {
+        if (pxItemSize != NULL) {
+            *pxItemSize = xTempSize;
+        }
+        return pvTempItem;
+    } else {
+        return NULL;
     }
-    return write_succeeded;
 }
 
-
-static void *xRingbufferReceiveGeneric(RingbufHandle_t ringbuf, size_t *item_size, TickType_t ticks_to_wait, size_t wanted_size)
+void *xRingbufferReceiveUpToFromISR(RingbufHandle_t xRingbuffer, size_t *pxItemSize, size_t xMaxSize)
 {
-    ringbuf_t *rb=(ringbuf_t *)ringbuf;
-    uint8_t *itemData;
-    BaseType_t done=pdFALSE;
-    configASSERT(rb);
-    while(!done) {
-        //See if there's any data available. If not, wait until there is.
-        while (rb->read_ptr == rb->write_ptr) {
-            BaseType_t r=xSemaphoreTake(rb->items_buffered_sem, ticks_to_wait);
-            if (r == pdFALSE) {
-                //Timeout.
-                return NULL;
-            }
-        }
-        //Okay, we seem to have data in the buffer. Grab the mux and copy it out if it's still there.
-        portENTER_CRITICAL(&rb->mux);
-        itemData=rb->getItemFromRingbufImpl(rb, item_size, wanted_size);
-        portEXIT_CRITICAL(&rb->mux);
-        if (itemData) {
-            //We managed to get an item.
-            done=pdTRUE;
+    //Check arguments
+    Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
+    configASSERT(pxRingbuffer);
+    configASSERT(pxRingbuffer->uxRingbufferFlags & rbBYTE_BUFFER_FLAG);    //This function should only be called for byte buffers
+    if (xMaxSize == 0) {
+        return NULL;
+    }
+
+    //Attempt to retrieve up to xMaxSize bytes
+    void *pvTempItem;
+    size_t xTempSize;
+    if (prvReceiveGenericFromISR(pxRingbuffer, &pvTempItem, NULL, &xTempSize, NULL, xMaxSize) == pdTRUE) {
+        if (pxItemSize != NULL) {
+            *pxItemSize = xTempSize;
         }
+        return pvTempItem;
+    } else {
+        return NULL;
     }
-    return (void*)itemData;
 }
 
-void *xRingbufferReceive(RingbufHandle_t ringbuf, size_t *item_size, TickType_t ticks_to_wait)
+void vRingbufferReturnItem(RingbufHandle_t xRingbuffer, void *pvItem)
 {
-    return xRingbufferReceiveGeneric(ringbuf, item_size, ticks_to_wait, 0);
+    Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
+    configASSERT(pxRingbuffer);
+    configASSERT(pvItem != NULL);
+
+    taskENTER_CRITICAL(&pxRingbuffer->mux);
+    pxRingbuffer->vReturnItem(pxRingbuffer, (uint8_t *)pvItem);
+    taskEXIT_CRITICAL(&pxRingbuffer->mux);
+    xSemaphoreGive(pxRingbuffer->xFreeSpaceSemaphore);
 }
 
-
-void *xRingbufferReceiveFromISR(RingbufHandle_t ringbuf, size_t *item_size)
+void vRingbufferReturnItemFromISR(RingbufHandle_t xRingbuffer, void *pvItem, BaseType_t *pxHigherPriorityTaskWoken)
 {
-    ringbuf_t *rb=(ringbuf_t *)ringbuf;
-    uint8_t *itemData;
-    configASSERT(rb);
-    portENTER_CRITICAL_ISR(&rb->mux);
-    itemData=rb->getItemFromRingbufImpl(rb, item_size, 0);
-    portEXIT_CRITICAL_ISR(&rb->mux);
-    return (void*)itemData;
+    Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
+    configASSERT(pxRingbuffer);
+    configASSERT(pvItem != NULL);
+
+    taskENTER_CRITICAL_ISR(&pxRingbuffer->mux);
+    pxRingbuffer->vReturnItem(pxRingbuffer, (uint8_t *)pvItem);
+    taskEXIT_CRITICAL_ISR(&pxRingbuffer->mux);
+    xSemaphoreGiveFromISR(pxRingbuffer->xFreeSpaceSemaphore, pxHigherPriorityTaskWoken);
 }
 
-void *xRingbufferReceiveUpTo(RingbufHandle_t ringbuf, size_t *item_size, TickType_t ticks_to_wait, size_t wanted_size) {
-    if (wanted_size == 0) return NULL;
-    configASSERT(ringbuf);
-    configASSERT(((ringbuf_t *)ringbuf)->flags & flag_bytebuf);
-    return xRingbufferReceiveGeneric(ringbuf, item_size, ticks_to_wait, wanted_size);
+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);
+        }
+    }
+    free(pxRingbuffer);
 }
 
-void *xRingbufferReceiveUpToFromISR(RingbufHandle_t ringbuf, size_t *item_size, size_t wanted_size)
+size_t xRingbufferGetMaxItemSize(RingbufHandle_t xRingbuffer)
 {
-    ringbuf_t *rb=(ringbuf_t *)ringbuf;
-    uint8_t *itemData;
-    if (wanted_size == 0) return NULL;
-    configASSERT(rb);
-    configASSERT(rb->flags & flag_bytebuf);
-    portENTER_CRITICAL_ISR(&rb->mux);
-    itemData=rb->getItemFromRingbufImpl(rb, item_size, wanted_size);
-    portEXIT_CRITICAL_ISR(&rb->mux);
-    return (void*)itemData;
+    Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
+    configASSERT(pxRingbuffer);
+    return pxRingbuffer->xMaxItemSize;
 }
 
+size_t xRingbufferGetCurFreeSize(RingbufHandle_t xRingbuffer)
+{
+    Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
+    configASSERT(pxRingbuffer);
+
+    size_t xFreeSize;
+    taskENTER_CRITICAL(&pxRingbuffer->mux);
+    xFreeSize = pxRingbuffer->xGetCurMaxSize(pxRingbuffer);
+    taskEXIT_CRITICAL(&pxRingbuffer->mux);
+    return xFreeSize;
+}
 
-void vRingbufferReturnItem(RingbufHandle_t ringbuf, void *item)
+BaseType_t xRingbufferAddToQueueSetRead(RingbufHandle_t xRingbuffer, QueueSetHandle_t xQueueSet)
 {
-    ringbuf_t *rb=(ringbuf_t *)ringbuf;
-    portENTER_CRITICAL(&rb->mux);
-    rb->returnItemToRingbufImpl(rb, item);
-    portEXIT_CRITICAL(&rb->mux);
-    xSemaphoreGive(rb->free_space_sem);
+    Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
+    configASSERT(pxRingbuffer);
+
+    BaseType_t xReturn;
+    taskENTER_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);
+    if (xHoldSemaphore == pdTRUE) {
+        //Return semaphore if temporarily held
+        configASSERT(xSemaphoreGive(pxRingbuffer->xItemsBufferedSemaphore) == pdTRUE);
+    }
+    taskEXIT_CRITICAL(&pxRingbuffer->mux);
+    return xReturn;
 }
 
+BaseType_t xRingbufferCanRead(RingbufHandle_t xRingbuffer, QueueSetMemberHandle_t xMember)
+{
+    //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;
+}
 
-void vRingbufferReturnItemFromISR(RingbufHandle_t ringbuf, void *item, BaseType_t *higher_prio_task_awoken)
+BaseType_t xRingbufferRemoveFromQueueSetRead(RingbufHandle_t xRingbuffer, QueueSetHandle_t xQueueSet)
 {
-    ringbuf_t *rb=(ringbuf_t *)ringbuf;
-    portENTER_CRITICAL_ISR(&rb->mux);
-    rb->returnItemToRingbufImpl(rb, item);
-    portEXIT_CRITICAL_ISR(&rb->mux);
-    xSemaphoreGiveFromISR(rb->free_space_sem, higher_prio_task_awoken);
+    Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
+    configASSERT(pxRingbuffer);
+
+    BaseType_t xReturn;
+    taskENTER_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);
+    if (xHoldSemaphore == pdTRUE) {
+        //Return semaphore if temporarily held
+        configASSERT(xSemaphoreGive(pxRingbuffer->xItemsBufferedSemaphore) == pdTRUE);
+    }
+    taskEXIT_CRITICAL(&pxRingbuffer->mux);
+    return xReturn;
 }
 
+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);
 
-BaseType_t xRingbufferAddToQueueSetRead(RingbufHandle_t ringbuf, QueueSetHandle_t xQueueSet)
+    taskENTER_CRITICAL(&pxRingbuffer->mux);
+    if (uxFree != NULL) {
+        *uxFree = (UBaseType_t)(pxRingbuffer->pucFree - pxRingbuffer->pucHead);
+    }
+    if (uxRead != NULL) {
+        *uxRead = (UBaseType_t)(pxRingbuffer->pucRead - pxRingbuffer->pucHead);
+    }
+    if (uxWrite != NULL) {
+        *uxWrite = (UBaseType_t)(pxRingbuffer->pucWrite - pxRingbuffer->pucHead);
+    }
+    if (uxItemsWaiting != NULL) {
+        *uxItemsWaiting = (UBaseType_t)(pxRingbuffer->xItemsWaiting);
+    }
+    taskEXIT_CRITICAL(&pxRingbuffer->mux);
+}
+
+void xRingbufferPrintInfo(RingbufHandle_t xRingbuffer)
 {
-    ringbuf_t *rb=(ringbuf_t *)ringbuf;
-    configASSERT(rb);
-    return xQueueAddToSet(rb->items_buffered_sem, xQueueSet);
+    Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer;
+    configASSERT(pxRingbuffer);
+    printf("Rb size:%d\tfree: %d\trptr: %d\tfreeptr: %d\twptr: %d\n",
+           pxRingbuffer->xSize, prvGetFreeSize(pxRingbuffer),
+           pxRingbuffer->pucRead - pxRingbuffer->pucHead,
+           pxRingbuffer->pucFree - pxRingbuffer->pucHead,
+           pxRingbuffer->pucWrite - pxRingbuffer->pucHead);
 }
 
+/* --------------------------------- Deprecated Functions ------------------------------ */
+//Todo: Remove the following deprecated functions in next release
 
-BaseType_t xRingbufferAddToQueueSetWrite(RingbufHandle_t ringbuf, QueueSetHandle_t xQueueSet)
+bool xRingbufferIsNextItemWrapped(RingbufHandle_t xRingbuffer)
 {
-    ringbuf_t *rb=(ringbuf_t *)ringbuf;
-    configASSERT(rb);
-    return xQueueAddToSet(rb->free_space_sem, xQueueSet);
+    //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 xRingbufferRemoveFromQueueSetRead(RingbufHandle_t ringbuf, QueueSetHandle_t xQueueSet)
+BaseType_t xRingbufferAddToQueueSetWrite(RingbufHandle_t xRingbuffer, QueueSetHandle_t xQueueSet)
 {
-    ringbuf_t *rb=(ringbuf_t *)ringbuf;
-    configASSERT(rb);
-    return xQueueRemoveFromSet(rb->items_buffered_sem, 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 ringbuf, QueueSetHandle_t xQueueSet)
+BaseType_t xRingbufferRemoveFromQueueSetWrite(RingbufHandle_t xRingbuffer, QueueSetHandle_t xQueueSet)
 {
-    ringbuf_t *rb=(ringbuf_t *)ringbuf;
-    configASSERT(rb);
-    return xQueueRemoveFromSet(rb->free_space_sem, 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;
 }
 
index 6a721aae39307cea68ce1bd1767c56ff40e508d1..e47512d8eccd38e0bd2240066a76a6071af98324 100644 (file)
-/*
- Test for multicore FreeRTOS ringbuffer.
-*/
-
-#include <string.h>
 #include <stdio.h>
-#include "rom/ets_sys.h"
-
+#include <stdlib.h>
 #include "freertos/FreeRTOS.h"
 #include "freertos/task.h"
-#include "freertos/semphr.h"
 #include "freertos/queue.h"
+#include "freertos/semphr.h"
 #include "freertos/ringbuf.h"
-#include "freertos/xtensa_api.h"
+#include "driver/timer.h"
 #include "unity.h"
-#include "soc/uart_reg.h"
-#include "soc/dport_reg.h"
-#include "soc/io_mux_reg.h"
-#include "esp_intr_alloc.h"
-
-
-static RingbufHandle_t rb;
-typedef enum {
-    TST_MOSTLYFILLED,
-    TST_MOSTLYEMPTY,
-    TST_INTTOTASK,
-    TST_TASKTOINT,
-} testtype_t;
-
-static volatile testtype_t testtype;
-
-intr_handle_t s_intr_handle;
-
-static void task1(void *arg)
-{
-    testtype_t oldtest;
-    char buf[100];
-    int i = 0;
-    int x, r;
-    while (1) {
-        oldtest = testtype;
-        if (testtype == TST_MOSTLYFILLED || testtype == TST_MOSTLYEMPTY) {
-            for (x = 0; x < 10; x++) {
-                sprintf(buf, "This is test %d item %d.", (int)testtype,  i++);
-                ets_printf("TSK w");
-                xRingbufferPrintInfo(rb);
-                r = xRingbufferSend(rb, buf, strlen(buf) + 1, 2000 / portTICK_PERIOD_MS);
-                if (!r) {
-                    printf("Test %d: Timeout on send!\n", (int)testtype);
-                }
-                if (testtype == TST_MOSTLYEMPTY) {
-                    vTaskDelay(300 / portTICK_PERIOD_MS);
-                }
-            }
-            //Send NULL event to stop other side.
-            r = xRingbufferSend(rb, NULL, 0, 10000 / portTICK_PERIOD_MS);
-        }
-        while (oldtest == testtype) {
-            vTaskDelay(300 / portTICK_PERIOD_MS);
-        }
+
+//Definitions used in multiple test cases
+#define TIMEOUT_TICKS               10
+#define NO_OF_RB_TYPES              3
+#define ITEM_HDR_SIZE               8
+#define SMALL_ITEM_SIZE             8
+#define LARGE_ITEM_SIZE             (2 * SMALL_ITEM_SIZE)
+#define BUFFER_SIZE                 160     //4Byte aligned size
+
+static const uint8_t small_item[SMALL_ITEM_SIZE] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};
+static const uint8_t large_item[LARGE_ITEM_SIZE] = { 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+                                                     0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17};
+static RingbufHandle_t buffer_handles[NO_OF_RB_TYPES];
+static SemaphoreHandle_t done_sem;
+
+static void send_item_and_check(RingbufHandle_t handle, const uint8_t *item,  size_t item_size, TickType_t ticks_to_wait, bool in_isr)
+{
+    BaseType_t ret;
+    if (in_isr) {
+        ret = xRingbufferSendFromISR(handle, (void *)item, item_size, NULL);
+    } else {
+        ret = xRingbufferSend(handle, (void *)item, item_size, ticks_to_wait);
     }
+    TEST_ASSERT_MESSAGE(ret == pdTRUE, "Failed to send item");
 }
 
-static void task2(void *arg)
-{
-    testtype_t oldtest;
-    char *buf;
-    size_t len;
-    while (1) {
-        oldtest = testtype;
-        if (testtype == TST_MOSTLYFILLED || testtype == TST_MOSTLYEMPTY) {
-            while (1) {
-                ets_printf("TSK r");
-                xRingbufferPrintInfo(rb);
-                buf = xRingbufferReceive(rb, &len, 2000 / portTICK_PERIOD_MS);
-                if (buf == NULL) {
-                    printf("Test %d: Timeout on recv!\n", (int)testtype);
-                } else if (len == 0) {
-                    printf("End packet received.\n");
-                    vRingbufferReturnItem(rb, buf);
-                    break;
-                } else {
-                    printf("Received: %s (%d bytes, %p)\n", buf, len, buf);
-                    vRingbufferReturnItem(rb, buf);
-                }
-                if (testtype == TST_MOSTLYFILLED) {
-                    vTaskDelay(300 / portTICK_PERIOD_MS);
-                }
-            }
+static void receive_check_and_return_item_no_split(RingbufHandle_t handle, const uint8_t *expected_data, size_t expected_size, TickType_t ticks_to_wait, bool in_isr)
+{
+    //Receive item from no-split buffer
+    size_t item_size;
+    uint8_t *item;
+    if (in_isr) {
+        item = (uint8_t *)xRingbufferReceiveFromISR(handle, &item_size);
+    } else {
+        item = (uint8_t *)xRingbufferReceive(handle, &item_size, ticks_to_wait);
+    }
+    TEST_ASSERT_MESSAGE(item != NULL, "Failed to receive item");
+    TEST_ASSERT_MESSAGE(item_size == expected_size, "Item size is incorrect");
+    //Check data of received item
+    for (int i = 0; i < item_size; i++) {
+        TEST_ASSERT_MESSAGE(item[i] == expected_data[i], "Item data is invalid");
+    }
+    //Return item
+    if (in_isr) {
+        vRingbufferReturnItemFromISR(handle, (void *)item, NULL);
+    } else {
+        vRingbufferReturnItem(handle, (void *)item);
+    }
+
+}
+
+static void receive_check_and_return_item_allow_split(RingbufHandle_t handle, const uint8_t *expected_data, size_t expected_size, TickType_t ticks_to_wait, bool in_isr)
+{
+    //Receive item
+    size_t item_size1, item_size2;
+    uint8_t *item1, *item2;
+    BaseType_t ret;
+    if (in_isr) {
+        ret = xRingbufferReceiveSplitFromISR(handle, (void**)&item1, (void **)&item2, &item_size1, &item_size2);
+    } else {
+        ret = xRingbufferReceiveSplit(handle, (void**)&item1, (void **)&item2, &item_size1, &item_size2, ticks_to_wait);
+    }
+    //= xRingbufferReceiveSplit(handle, (void**)&item1, (void **)&item2, &item_size1, &item_size2, ticks_to_wait);
+    TEST_ASSERT_MESSAGE(ret == pdTRUE, "Failed to receive item");
+    TEST_ASSERT_MESSAGE(item1 != NULL, "Failed to receive item");
+
+    //Check data of received item(s) and return them
+    if (item2 == NULL) {
+        TEST_ASSERT_MESSAGE(item_size1 == expected_size, "Item size is incorrect");
+        for (int i = 0; i < item_size1; i++) {
+            TEST_ASSERT_MESSAGE(item1[i] == expected_data[i], "Item data is invalid");
+        }
+        //Return item
+        if (in_isr) {
+            vRingbufferReturnItemFromISR(handle, (void *)item1, NULL);
+        } else {
+            vRingbufferReturnItem(handle, (void *)item1);
+        }
+    } else {
+        //Item was split
+        TEST_ASSERT_MESSAGE(item_size1 + item_size2 == expected_size, "Total item size is incorrect");
+        for (int i = 0; i < item_size1; i++) {
+            TEST_ASSERT_MESSAGE(item1[i] == expected_data[i], "Head item data is invalid");
+        }
+        for (int i = 0; i < item_size2; i++) {
+            TEST_ASSERT_MESSAGE(item2[i] == expected_data[item_size1 + i], "Head item data is invalid");
         }
-        while (oldtest == testtype) {
-            vTaskDelay(300 / portTICK_PERIOD_MS);
+        //Return Items
+        if (in_isr) {
+            vRingbufferReturnItemFromISR(handle, (void *)item1, NULL);
+            vRingbufferReturnItemFromISR(handle, (void *)item2, NULL);
+        } else {
+            vRingbufferReturnItem(handle, (void *)item1);
+            vRingbufferReturnItem(handle, (void *)item2);
         }
     }
 }
 
+static void receive_check_and_return_item_byte_buffer(RingbufHandle_t handle, const uint8_t *expected_data, size_t expected_size, TickType_t ticks_to_wait, bool in_isr)
+{
+    //Receive item
+    size_t item_size;
+    uint8_t *item;
+    if (in_isr) {
+        item = (uint8_t *)xRingbufferReceiveUpToFromISR(handle, &item_size, expected_size);
+    } else {
+        item = (uint8_t *)xRingbufferReceiveUpTo(handle, &item_size, ticks_to_wait, expected_size);   //Limit amount of bytes returned to the size of one item
+    }
+    TEST_ASSERT_MESSAGE(item != NULL, "Failed to receive item");
 
+    //Check data of received item
+    for (int i = 0; i < item_size; i++) {
+        TEST_ASSERT_MESSAGE(item[i] == expected_data[i], "Item data is invalid");
+    }
+    //Return item
+    if (in_isr) {
+        vRingbufferReturnItemFromISR(handle, (void *)item, NULL);
+    } else {
+        vRingbufferReturnItem(handle, (void *)item);
+    }
 
-static void uartIsrHdl(void *arg)
-{
-    char c;
-    char buf[50];
-    char *item;
-    int r;
-    size_t len;
-    BaseType_t xHigherPriorityTaskWoken;
-    SET_PERI_REG_MASK(UART_INT_CLR_REG(0), UART_RXFIFO_FULL_INT_CLR);
-    while (READ_PERI_REG(UART_STATUS_REG(0)) & (UART_RXFIFO_CNT << UART_RXFIFO_CNT_S)) {
-        c = READ_PERI_REG(UART_FIFO_REG(0));
-        if (c == 'r') {
-            ets_printf("ISR r");
-            xRingbufferPrintInfo(rb);
-            item = xRingbufferReceiveFromISR(rb, &len);
-            if (item == NULL) {
-                ets_printf("ISR recv fail!\n");
-            } else if (len == 0) {
-                ets_printf("ISR recv NULL!\n");
-                vRingbufferReturnItemFromISR(rb, item, &xHigherPriorityTaskWoken);
-            } else {
-                ets_printf("ISR recv '%s' (%d bytes, %p)\n", buf, len, buf);
-                vRingbufferReturnItemFromISR(rb, item, &xHigherPriorityTaskWoken);
-            }
+    //Check if item wrapped around
+    if (item_size < expected_size) {
+        //Item is wrapped, receive second portion
+        size_t item_size2;
+        uint8_t *item2;
+        if (in_isr) {
+            item2 = (uint8_t *)xRingbufferReceiveUpToFromISR(handle, &item_size2, expected_size - item_size);
         } else {
-            sprintf(buf, "UART: %c", c);
-            ets_printf("ISR w");
-            xRingbufferPrintInfo(rb);
-            r = xRingbufferSendFromISR(rb, buf, strlen(buf) + 1, &xHigherPriorityTaskWoken);
-            if (!r) {
-                ets_printf("ISR send fail\n");
-            }
+            item2 = (uint8_t *)xRingbufferReceiveUpTo(handle, &item_size2, ticks_to_wait, expected_size - item_size);
         }
+        //= (uint8_t *)xRingbufferReceiveUpTo(handle, &item_size2, ticks_to_wait, expected_size - item_size);
+        TEST_ASSERT_MESSAGE(item2 != NULL, "Failed to receive item");
+        TEST_ASSERT_MESSAGE(item_size + item_size2 == expected_size, "Total item size is incorrect");
+        for (int i = 0; i < item_size2; i++) {
+            TEST_ASSERT_MESSAGE(item2[i] == expected_data[item_size + i], "Item data is invalid");
+        }
+        if (in_isr) {
+            vRingbufferReturnItemFromISR(handle, (void *)item2, NULL);
+        } else {
+            vRingbufferReturnItem(handle, (void *)item2);
+        }
+    } else {
+        TEST_ASSERT_MESSAGE(item_size == expected_size, "Item size is incorrect");
     }
-    if (xHigherPriorityTaskWoken) {
-        portYIELD_FROM_ISR();
+}
+
+/* ----------------- Basic ring buffer behavior tests cases --------------------
+ * The following test cases will test basic send, receive, and wrap around
+ * behavior of each type of ring buffer. Each test case will do the following
+ * 1) Send multiple items (nearly fill the buffer)
+ * 2) Receive and check the sent items (also prepares the buffer for a wrap around
+ * 3) Send a final item that causes a wrap around
+ * 4) Receive and check the wrapped item
+ */
+
+TEST_CASE("Test ring buffer No-Split", "[freertos]")
+{
+    //Create buffer
+    RingbufHandle_t buffer_handle = xRingbufferCreate(BUFFER_SIZE, RINGBUF_TYPE_NOSPLIT);
+    TEST_ASSERT_MESSAGE(buffer_handle != NULL, "Failed to create ring buffer");
+    //Calculate number of items to send. Aim to almost fill buffer to setup for wrap around
+    int no_of_items = (BUFFER_SIZE - (ITEM_HDR_SIZE + SMALL_ITEM_SIZE)) / (ITEM_HDR_SIZE + SMALL_ITEM_SIZE);
+
+    //Test sending items
+    for (int i = 0; i < no_of_items; i++) {
+        send_item_and_check(buffer_handle, small_item, SMALL_ITEM_SIZE, TIMEOUT_TICKS, false);
+    }
+    //Test receiving items
+    for (int i = 0; i < no_of_items; i++) {
+        receive_check_and_return_item_no_split(buffer_handle, small_item, SMALL_ITEM_SIZE, TIMEOUT_TICKS, false);
     }
+
+    //Write pointer should be near the end, test wrap around
+    uint32_t write_pos_before, write_pos_after;
+    vRingbufferGetInfo(buffer_handle, NULL, NULL, &write_pos_before, NULL);
+    //Send large item that causes wrap around
+    send_item_and_check(buffer_handle, large_item, LARGE_ITEM_SIZE, TIMEOUT_TICKS, false);
+    //Receive wrapped item
+    receive_check_and_return_item_no_split(buffer_handle, large_item, LARGE_ITEM_SIZE, TIMEOUT_TICKS, false);
+    vRingbufferGetInfo(buffer_handle, NULL, NULL, &write_pos_after, NULL);
+    TEST_ASSERT_MESSAGE(write_pos_after < write_pos_before, "Failed to wrap around");
+
+    //Cleanup
+    vRingbufferDelete(buffer_handle);
 }
 
-static void uartRxInit()
+TEST_CASE("Test ring buffer Allow-Split", "[freertos]")
 {
-    WRITE_PERI_REG(UART_CONF1_REG(0), 1 << UART_RXFIFO_FULL_THRHD_S);
-    CLEAR_PERI_REG_MASK(UART_INT_ENA_REG(0), UART_TXFIFO_EMPTY_INT_ENA | UART_RXFIFO_TOUT_INT_ENA);
-    SET_PERI_REG_MASK(UART_INT_ENA_REG(0), UART_RXFIFO_FULL_INT_ENA);
+    //Create buffer
+    RingbufHandle_t buffer_handle = xRingbufferCreate(BUFFER_SIZE, RINGBUF_TYPE_ALLOWSPLIT);
+    TEST_ASSERT_MESSAGE(buffer_handle != NULL, "Failed to create ring buffer");
+    //Calculate number of items to send. Aim to almost fill buffer to setup for wrap around
+    int no_of_items = (BUFFER_SIZE - (ITEM_HDR_SIZE + SMALL_ITEM_SIZE)) / (ITEM_HDR_SIZE + SMALL_ITEM_SIZE);
 
-    ESP_ERROR_CHECK(esp_intr_alloc(ETS_UART0_INTR_SOURCE, 0, &uartIsrHdl, NULL, &s_intr_handle));
+    //Test sending items
+    for (int i = 0; i < no_of_items; i++) {
+        send_item_and_check(buffer_handle, small_item, SMALL_ITEM_SIZE, TIMEOUT_TICKS, false);
+    }
+    //Test receiving items
+    for (int i = 0; i < no_of_items; i++) {
+        receive_check_and_return_item_allow_split(buffer_handle, small_item, SMALL_ITEM_SIZE, TIMEOUT_TICKS, false);
+    }
+
+    //Write pointer should be near the end, test wrap around
+    uint32_t write_pos_before, write_pos_after;
+    vRingbufferGetInfo(buffer_handle, NULL, NULL, &write_pos_before, NULL);
+    //Send large item that causes wrap around
+    send_item_and_check(buffer_handle, large_item, LARGE_ITEM_SIZE, TIMEOUT_TICKS, false);
+    //Receive wrapped item
+    receive_check_and_return_item_allow_split(buffer_handle, large_item, LARGE_ITEM_SIZE, TIMEOUT_TICKS, false);
+    vRingbufferGetInfo(buffer_handle, NULL, NULL, &write_pos_after, NULL);
+    TEST_ASSERT_MESSAGE(write_pos_after < write_pos_before, "Failed to wrap around");
+
+    //Cleanup
+    vRingbufferDelete(buffer_handle);
 }
 
-static void uartRxDeinit()
+TEST_CASE("Test ring buffer Byte Buffer", "[freertos]")
 {
-    esp_intr_free(s_intr_handle);
+    //Create buffer
+    RingbufHandle_t buffer_handle = xRingbufferCreate(BUFFER_SIZE, RINGBUF_TYPE_BYTEBUF);
+    TEST_ASSERT_MESSAGE(buffer_handle != NULL, "Failed to create ring buffer");
+    //Calculate number of items to send. Aim to almost fill buffer to setup for wrap around
+    int no_of_items = (BUFFER_SIZE - SMALL_ITEM_SIZE) / SMALL_ITEM_SIZE;
+
+    //Test sending items
+    for (int i = 0; i < no_of_items; i++) {
+        send_item_and_check(buffer_handle, small_item, SMALL_ITEM_SIZE, TIMEOUT_TICKS, false);
+    }
+    //Test receiving items
+    for (int i = 0; i < no_of_items; i++) {
+        receive_check_and_return_item_byte_buffer(buffer_handle, small_item, SMALL_ITEM_SIZE, TIMEOUT_TICKS, false);
+    }
+
+    //Write pointer should be near the end, test wrap around
+    uint32_t write_pos_before, write_pos_after;
+    vRingbufferGetInfo(buffer_handle, NULL, NULL, &write_pos_before, NULL);
+    //Send large item that causes wrap around
+    send_item_and_check(buffer_handle, large_item, LARGE_ITEM_SIZE, TIMEOUT_TICKS, false);
+    //Receive wrapped item
+    receive_check_and_return_item_byte_buffer(buffer_handle, large_item, LARGE_ITEM_SIZE, TIMEOUT_TICKS, false);
+    vRingbufferGetInfo(buffer_handle, NULL, NULL, &write_pos_after, NULL);
+    TEST_ASSERT_MESSAGE(write_pos_after < write_pos_before, "Failed to wrap around");
+
+    //Cleanup
+    vRingbufferDelete(buffer_handle);
 }
 
-static void testRingbuffer(int type, bool arbitrary)
+/* ----------------------- Ring buffer queue sets test ------------------------
+ * The following test case will test receiving from ring buffers that have been
+ * added to a queue set. The test case will do the following...
+ * 1) Ring buffer of each type is created and added to the queue set
+ * 2) A receiving task is created to select from the queue set and read from the appropriate ring buffer
+ */
+
+static void queue_set_receiving_task(void *queue_set_handle)
 {
-    TaskHandle_t th[2];
-    int i;
-    /* Arbitrary Length means buffer length which is not a multiple of 4 */
-    if (arbitrary) {
-        rb = xRingbufferCreate(31 * 3, type);
-    } else {
-        rb = xRingbufferCreate(32 * 3, type);
+    QueueSetHandle_t queue_set = (QueueSetHandle_t)queue_set_handle;
+
+    //Receive multiple items via queue set
+    BaseType_t done = pdFALSE;
+    int no_of_items = BUFFER_SIZE / SMALL_ITEM_SIZE;
+    int items_rec_count[NO_OF_RB_TYPES] = {0};
+    while (done != pdTRUE) {
+        xQueueSetMemberHandle member = xQueueSelectFromSet(queue_set, TIMEOUT_TICKS);
+        //Read from selected ring buffer
+        if (xRingbufferCanRead(buffer_handles[0], member) == pdTRUE) {
+            //No-split buffer
+            receive_check_and_return_item_no_split(buffer_handles[0], small_item, SMALL_ITEM_SIZE, 0, false);
+            items_rec_count[0] ++;
+        } else if (xRingbufferCanRead(buffer_handles[1], member) == pdTRUE) {
+            //Allow-split buffer
+            receive_check_and_return_item_allow_split(buffer_handles[1], small_item, SMALL_ITEM_SIZE, 0, false);
+            items_rec_count[1] ++;
+        } else if (xRingbufferCanRead(buffer_handles[2], member) == pdTRUE){
+            //Byte buffer
+            receive_check_and_return_item_byte_buffer(buffer_handles[2], small_item, SMALL_ITEM_SIZE, 0, false);
+            items_rec_count[2] ++;
+        } else {
+            TEST_ASSERT_MESSAGE( false, "Error with queue set member");
+        }
+
+        //Check for completion
+        if (items_rec_count[0] == no_of_items &&
+            items_rec_count[1] == no_of_items &&
+            items_rec_count[2] == no_of_items) {
+            done = pdTRUE;
+        }
     }
 
-    testtype = TST_MOSTLYFILLED;
+    xSemaphoreGive(done_sem);
+    vTaskDelete(NULL);
+}
 
-    xTaskCreatePinnedToCore(task1, "tskone", 2048, NULL, 3, &th[0], 0);
-    xTaskCreatePinnedToCore(task2, "tsktwo", 2048, NULL, 3, &th[1], 0);
-    uartRxInit();
+TEST_CASE("Test ring buffer with queue sets", "[freertos]")
+{
+    QueueSetHandle_t queue_set = xQueueCreateSet(NO_OF_RB_TYPES);
+    done_sem = xSemaphoreCreateBinary();
+
+    //Create ring buffer of each type, then add them to a queue set
+    for (int i = 0; i < NO_OF_RB_TYPES; i++) {
+        buffer_handles[i] = xRingbufferCreate(BUFFER_SIZE, i);
+        TEST_ASSERT_MESSAGE(buffer_handles[i] != NULL, "Failed to create ring buffer");
+        TEST_ASSERT_MESSAGE(xRingbufferAddToQueueSetRead(buffer_handles[i], queue_set) == pdPASS, "Failed to add to read queue set");
+    }
+    //Create a task to send items to each ring buffer
+    int no_of_items = BUFFER_SIZE / SMALL_ITEM_SIZE;
+    xTaskCreatePinnedToCore(queue_set_receiving_task, "rec tsk", 2048, (void *)queue_set, UNITY_FREERTOS_PRIORITY + 1 , NULL, 0);
 
-    printf("Press 'r' to read an event in isr, any other key to write one.\n");
-    printf("Test: mostlyfilled; putting 10 items in ringbuff ASAP, reading 1 a second\n");
-    vTaskDelay(5000 / portTICK_PERIOD_MS);
-    printf("Test: mostlyempty; putting 10 items in ringbuff @ 1/sec, reading as fast as possible\n");
-    testtype = TST_MOSTLYEMPTY;
-    vTaskDelay(5000 / portTICK_PERIOD_MS);
+    //Send multiple items to each type of ring buffer
+    for (int i = 0; i < no_of_items; i++) {
+        for (int j = 0; j < NO_OF_RB_TYPES; j++) {
+            send_item_and_check(buffer_handles[j], small_item, SMALL_ITEM_SIZE, TIMEOUT_TICKS, false);
+        }
+    }
 
-    //Shut down all the tasks
-    for (i = 0; i < 2; i++) {
-        vTaskDelete(th[i]);
+    xSemaphoreTake(done_sem, portMAX_DELAY);
+    vSemaphoreDelete(done_sem);
+   //Remove and delete ring buffers from queue sets
+    for (int i = 0; i < NO_OF_RB_TYPES; i++) {
+        TEST_ASSERT_MESSAGE(xRingbufferRemoveFromQueueSetRead(buffer_handles[i], queue_set) == pdTRUE, "Failed to remove from read queue set");
+        vRingbufferDelete(buffer_handles[i]);
     }
-    vRingbufferDelete(rb);
-    uartRxDeinit();
+    vQueueDelete(queue_set);
 }
 
-// TODO: split this thing into separate orthogonal tests
-TEST_CASE("FreeRTOS ringbuffer test, no splitting items", "[freertos]")
+/* -------------------------- Test ring buffer ISR -----------------------------
+ * The following test case tests ring buffer ISR API. A timer is used to trigger
+ * the ISR. The test case will do the following
+ * 1) ISR will be triggered periodically by timer
+ * 2) The ISR will iterate through all ring buffer types where each iteration
+ *    will send then receive an item to a ring buffer.
+ */
+
+#define TIMER_GROUP     0
+#define TIMER_NUMBER    0
+#define ISR_ITERATIONS  ((BUFFER_SIZE / SMALL_ITEM_SIZE) * 2)
+
+intr_handle_t ringbuffer_isr_handle;
+static int buf_type;
+static int iterations;
+
+static void ringbuffer_isr(void *arg)
 {
-    testRingbuffer(0, false);
+    //Clear timer interrupt
+    TIMERG0.int_clr_timers.t0 = 1;
+    TIMERG0.hw_timer[xPortGetCoreID()].config.alarm_en = 1;
+
+    //Test sending to buffer from ISR from ISR
+    if (buf_type < NO_OF_RB_TYPES) {
+        send_item_and_check(buffer_handles[buf_type], (void *)small_item, SMALL_ITEM_SIZE, 0, true);
+    }
+
+    //Receive item from ISR
+    if (buf_type == RINGBUF_TYPE_NOSPLIT) {
+        //Test receive from ISR for no-split buffer
+        receive_check_and_return_item_no_split(buffer_handles[buf_type], (void *)small_item, SMALL_ITEM_SIZE, 0, true);
+        buf_type++;
+    } else if (buf_type == RINGBUF_TYPE_ALLOWSPLIT) {
+        //Test send from ISR to allow-split buffer
+        receive_check_and_return_item_allow_split(buffer_handles[buf_type], (void *)small_item, SMALL_ITEM_SIZE, 0, true);
+        buf_type++;
+    } else if (buf_type == RINGBUF_TYPE_BYTEBUF) {
+        //Test receive from ISR for byte buffer
+        receive_check_and_return_item_byte_buffer(buffer_handles[buf_type], (void *)small_item, SMALL_ITEM_SIZE, 0, true);
+        buf_type++;
+    } else if (buf_type == NO_OF_RB_TYPES) {
+        //Check if all iterations complete
+        if (iterations < ISR_ITERATIONS) {
+            iterations++;
+            buf_type = 0;   //Reset and iterate through each buffer type again
+            return;
+        } else {
+            //Signal complete
+            BaseType_t task_woken = pdFALSE;
+            xSemaphoreGiveFromISR(done_sem, &task_woken);
+            if (task_woken == pdTRUE) {
+                buf_type++;
+                portYIELD_FROM_ISR();
+            }
+        }
+    }
 }
 
-TEST_CASE("FreeRTOS ringbuffer test, w/ splitting items", "[freertos]")
+static void setup_timer()
 {
-    testRingbuffer(1, false);
+    //Setup timer for ISR
+    int timer_group = TIMER_GROUP;
+    int timer_idx = TIMER_NUMBER;
+    timer_config_t config;
+    config.alarm_en = 1;
+    config.auto_reload = 1;
+    config.counter_dir = TIMER_COUNT_UP;
+    config.divider = 10000;
+    config.intr_type = TIMER_INTR_LEVEL;
+    config.counter_en = TIMER_PAUSE;
+    timer_init(timer_group, timer_idx, &config);    //Configure timer
+    timer_pause(timer_group, timer_idx);    //Stop timer counter
+    timer_set_counter_value(timer_group, timer_idx, 0x00000000ULL); //Load counter value
+    timer_set_alarm_value(timer_group, timer_idx, 20); //Set alarm value
+    timer_enable_intr(timer_group, timer_idx);  //Enable timer interrupt
+    timer_set_auto_reload(timer_group, timer_idx, 1);   //Auto Reload
+    timer_isr_register(timer_group, timer_idx, ringbuffer_isr, NULL, 0, &ringbuffer_isr_handle);    //Set ISR handler
 }
 
-TEST_CASE("FreeRTOS ringbuffer test, no splitting items, arbitrary length buffer", "[freertos]")
+static void cleanup_timer()
 {
-    testRingbuffer(0, true);
+    timer_disable_intr(TIMER_GROUP, TIMER_NUMBER);
+    esp_intr_free(ringbuffer_isr_handle);
 }
 
-TEST_CASE("FreeRTOS ringbuffer test, w/ splitting items, arbitrary length buffer", "[freertos]")
+TEST_CASE("Test ring buffer ISR", "[freertos]")
 {
-    testRingbuffer(1, true);
+    for (int i = 0; i < NO_OF_RB_TYPES; i++) {
+        buffer_handles[i] = xRingbufferCreate(BUFFER_SIZE, i);
+    }
+    done_sem = xSemaphoreCreateBinary();
+    buf_type = 0;
+    iterations = 0;
+    setup_timer();
+    //Start timer to trigger ISR
+    timer_start(TIMER_GROUP, TIMER_NUMBER);
+    //Wait for ISR to complete multiple iterations
+    xSemaphoreTake(done_sem, portMAX_DELAY);
+
+    //Cleanup
+    cleanup_timer();
+    vSemaphoreDelete(done_sem);
+    for (int i = 0; i < NO_OF_RB_TYPES; i++) {
+        vRingbufferDelete(buffer_handles[i]);
+    }
 }
 
+/* ---------------------------- Test ring buffer SMP ---------------------------
+ * The following test case tests each type of ring buffer in an SMP fashion. A
+ * sending task and a receiving task is created. The sending task will split
+ * a continuous piece of data into items of random length and send it to a ring
+ * buffer. The receiving task will receive and check those items.
+ * Every permutation of core pinning of the sending and receiving task will be
+ * tested.
+ */
+
+#define SRAND_SEED                      3   //Arbitrarily chosen srand() seed
+#define SMP_TEST_ITERATIONS             4
+
+static const char continuous_data[] = {"A_very_long_string_that_will_be_split_into_"
+                                       "items_of_random_lengths_and_sent_to_the_ring_"
+                                       "buffer._The_maximum_random_length_will_also_"
+                                       "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
 
-TEST_CASE("FreeRTOS ringbuffer test, check if zero-length items are handled correctly", "[freertos]")
+typedef struct {
+    RingbufHandle_t buffer;
+    ringbuf_type_t type;
+} task_args_t;
+
+static SemaphoreHandle_t tasks_done;
+static SemaphoreHandle_t tx_done;
+static SemaphoreHandle_t rx_done;
+
+static void send_to_buffer(RingbufHandle_t buffer, size_t max_item_size)
 {
-    rb = xRingbufferCreate(32, 0);
-    int r;
-    void *v;
-    size_t sz;
-    for (int x=0; x<128; x++) {
-        if (x!=127) {
-            //Send an item
-            r = xRingbufferSend(rb, NULL, 0, 10000 / portTICK_PERIOD_MS);
-            assert(r==pdTRUE);
+    for (int iter = 0; iter < SMP_TEST_ITERATIONS; iter++) {
+        size_t bytes_sent = 0;      //Number of data bytes sent in this iteration
+        size_t next_item_size;      //Size of next item to send
+
+        while (bytes_sent < CONT_DATA_LEN) {
+            //Get size of next item
+            next_item_size = rand() % (max_item_size + 1);
+            if (next_item_size + bytes_sent > CONT_DATA_LEN) {
+                next_item_size = CONT_DATA_LEN - bytes_sent;
+            }
+
+            //Send item
+            TEST_ASSERT_MESSAGE(xRingbufferSend(buffer, (void *)&(continuous_data[bytes_sent]), next_item_size, TIMEOUT_TICKS) == pdTRUE, "Failed to send an item");
+            bytes_sent += next_item_size;
         }
-        if (x!=0) {
-            //Receive an item
-            v=xRingbufferReceive(rb, &sz, 10000 / portTICK_PERIOD_MS);
-            assert(sz==0);
-            vRingbufferReturnItem(rb, v); //actually not needed for NULL data...
+        xSemaphoreGive(tx_done);
+        xSemaphoreTake(rx_done, portMAX_DELAY);
+    }
+}
+
+static void read_from_buffer(RingbufHandle_t buffer, ringbuf_type_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
+        while (bytes_rec < CONT_DATA_LEN) {
+            size_t item_size, item_size2;    //Possible for allow split buffers to receive two items
+            char *item_data, *item_data2;
+
+            //Select appropriate receive function for type of ring buffer
+            if (buf_type ==  RINGBUF_TYPE_NOSPLIT) {
+                item_data = (char *)xRingbufferReceive(buffer, &item_size, TIMEOUT_TICKS);
+            } else if (buf_type == RINGBUF_TYPE_ALLOWSPLIT) {
+                BaseType_t ret = xRingbufferReceiveSplit(buffer, (void **)&item_data, (void **)&item_data2, &item_size, &item_size2, TIMEOUT_TICKS);
+                TEST_ASSERT_MESSAGE(ret == pdTRUE, "Failed to receive any item");
+            } else {
+                item_data = (char *)xRingbufferReceiveUpTo(buffer, &item_size, TIMEOUT_TICKS, max_rec_size);
+            }
+
+            //Check received item and return it
+            TEST_ASSERT_MESSAGE(item_data != NULL, "Failed to receive an item");
+            if (buf_type == RINGBUF_TYPE_BYTEBUF) {
+                TEST_ASSERT_MESSAGE(item_size <= max_rec_size, "Received data exceeds max size");
+            }
+            for (int i = 0; i < item_size; i++) {
+                //Check item_data is valid
+                TEST_ASSERT_MESSAGE(item_data[i] == continuous_data[bytes_rec + i], "Received data is corrupted");
+            }
+            bytes_rec += item_size;
+            vRingbufferReturnItem(buffer, item_data);
+            if (buf_type == RINGBUF_TYPE_ALLOWSPLIT && item_data2 != NULL) {
+                //Check item_data2 is valid
+                for (int i = 0; i < item_size2; i++) {
+                    TEST_ASSERT_MESSAGE(item_data2[i] == continuous_data[bytes_rec + i], "Received split data is corrupted");
+                }
+                bytes_rec += item_size2;
+                vRingbufferReturnItem(buffer, item_data2);
+            }
         }
+        TEST_ASSERT_MESSAGE(bytes_rec == CONT_DATA_LEN, "Total length of received data is incorrect");
+        xSemaphoreGive(rx_done);
+        xSemaphoreTake(tx_done, portMAX_DELAY);
     }
-    vRingbufferDelete(rb);
 }
 
+static void send_task(void *args)
+{
+    RingbufHandle_t buffer = ((task_args_t *)args)->buffer;
+    size_t max_item_len = xRingbufferGetMaxItemSize(buffer);
+
+    //Test sending short length items
+    send_to_buffer(buffer, 1);
+    //Test sending mid length items
+    send_to_buffer(buffer, max_item_len/2);
+    //Test sending long length items
+    send_to_buffer(buffer, max_item_len);
+    vTaskDelete(NULL);
+}
+
+static void rec_task(void *args)
+{
+    RingbufHandle_t buffer = ((task_args_t *)args)->buffer;
+    size_t max_rec_len = xRingbufferGetMaxItemSize(buffer);
+
+    //Test receiving short length items
+    read_from_buffer(buffer, ((task_args_t *)args)->type, 1);
+    //Test receiving mid length items
+    read_from_buffer(buffer, ((task_args_t *)args)->type, max_rec_len/2);
+    //Test receiving long length items
+    read_from_buffer(buffer, ((task_args_t *)args)->type, max_rec_len);
+
+    xSemaphoreGive(tasks_done);
+    vTaskDelete(NULL);
+}
+
+TEST_CASE("Test ring buffer SMP", "[freertos]")
+{
+    ets_printf("size of buf %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
+    srand(SRAND_SEED);                                  //Seed RNG
+
+    //Iterate through buffer types (No split, split, then byte buff)
+    for (ringbuf_type_t buf_type = 0; buf_type <= RINGBUF_TYPE_BYTEBUF; 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;
+
+        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);
+        vTaskDelay(10);
+    }
+
+    //Cleanup
+    vSemaphoreDelete(tx_done);
+    vSemaphoreDelete(rx_done);
+    vSemaphoreDelete(tasks_done);
+}
diff --git a/docs/_static/diagrams/ring-buffer/ring_buffer_read_ret_byte_buf.diag b/docs/_static/diagrams/ring-buffer/ring_buffer_read_ret_byte_buf.diag
new file mode 100644 (file)
index 0000000..9ad33b0
--- /dev/null
@@ -0,0 +1,30 @@
+#Diagram demonstrating reading and returning an item in a byte buffer
+#Buffer of 128 bytes, with 68 bytes occupied but wrapped. All data is read
+
+packetdiag ring_buffer_read_ret_byte_buf {
+    node_width = 6
+    node_height = 24
+    default_fontsize = 12
+    colwidth = 128
+
+    #Initial
+    0-29: 30 [color = lightyellow];
+    30-89: 60 Free
+    90-127: 38 [color = lightyellow];
+
+    #Read all continuous data
+    128-157: 30 [color = lightyellow];
+    158-217: 60 Free
+    218-255: 38 [color = pink];
+
+    #Return data
+    256-285: 30 [color = lightyellow];
+    286-383: 98 Free
+
+    #Read remaining data
+    384-413: 30 [color = pink];
+    414-511: 98 Free
+
+    #Return data
+    512-639: 128 Free
+}
\ No newline at end of file
diff --git a/docs/_static/diagrams/ring-buffer/ring_buffer_read_ret_non_byte_buf.diag b/docs/_static/diagrams/ring-buffer/ring_buffer_read_ret_non_byte_buf.diag
new file mode 100644 (file)
index 0000000..a7773b9
--- /dev/null
@@ -0,0 +1,86 @@
+#Diagram demonstrating reading and returning an item in a No-Split/Allow-Split ring buffer
+#Buffer of 128 bytes, with 4 items of 16, 20, 8 and 24 bytes. First 3 items are read and returned
+
+packetdiag ring_buffer_read_ret_non_byte_buf {
+    node_width = 6
+    node_height = 24
+    default_fontsize = 12
+    colwidth = 128
+
+    #Initial
+    0-7: 8 [color = lightblue];
+    8-23: 16 [color = lightyellow];
+    24-31: 8 [color = lightblue];
+    32-51: 20 [color = lightyellow];
+    52-59: 8 [color = lightblue];
+    60-67: 8 [color = lightyellow];
+    68-75: 8 [color = lightblue];
+    76-99: 24 [color = lightyellow];
+    100-127: 28 Free
+
+    #Read item 1
+    128-135: 8 [color = pink];
+    136-151: 16 [color = pink];
+    152-159: 8 [color = lightblue];
+    160-179: 20 [color = lightyellow];
+    180-187: 8 [color = lightblue];
+    188-195: 8 [color = lightyellow];
+    196-203: 8 [color = lightblue];
+    204-227: 24 [color = lightyellow];
+    228-255: 28 Free
+
+    #Read item 2
+    256-263: 8 [color = pink];
+    264-279: 16 [color = pink];
+    280-287: 8 [color = pink];
+    288-307: 20 [color = pink];
+    308-315: 8 [color = lightblue];
+    316-323: 8 [color = lightyellow];
+    324-331: 8 [color = lightblue];
+    332-355: 24 [color = lightyellow];
+    356-383: 28 Free
+
+    #Read item 3
+    384-391: 8 [color = pink];
+    392-407: 16 [color = pink];
+    408-415: 8 [color = pink];
+    416-435: 20 [color = pink];
+    436-443: 8 [color = pink];
+    444-451: 8 [color = pink];
+    452-459: 8 [color = lightblue];
+    460-483: 24 [color = lightyellow];
+    484-511: 28 Free
+
+    #Return item 2
+    512-519: 8 [color = pink];
+    520-535: 16 [color = pink];
+    536-563: Ret [color = lightgrey];
+    564-571: 8 [color = pink];
+    572-579: 8 [color = pink];
+    580-587: 8 [color = lightblue];
+    588-611: 24 [color = lightyellow];
+    612-639: 28 Free
+
+    #Return item 3
+    640-647: 8 [color = pink];
+    648-663: 16 [color = pink];
+    664-691: Ret [color = lightgrey];
+    692-707: Ret [color = lightgrey];
+    708-715: 8 [color = lightblue];
+    716-739: 24 [color = lightyellow];
+    740-767: 28 Free
+
+    #Return item 1
+    768-791: Ret [color = lightgrey];
+    792-819: Ret [color = lightgrey];
+    820-835: Ret [color = lightgrey];
+    836-843: 8 [color = lightblue];
+    844-867: 24 [color = lightyellow];
+    868-895: 28 Free
+
+    #End state
+    896-963: 68 Free
+    964-971: 8 [color = lightblue];
+    972-995: 24 [color = lightyellow];
+    996-1023: 28 Free
+}
\ No newline at end of file
diff --git a/docs/_static/diagrams/ring-buffer/ring_buffer_send_byte_buf.diag b/docs/_static/diagrams/ring-buffer/ring_buffer_send_byte_buf.diag
new file mode 100644 (file)
index 0000000..9bac570
--- /dev/null
@@ -0,0 +1,21 @@
+#Diagram demonstrating sending in byte buffer
+#Buffer of 128 bytes, and 3 items of size 18, 3, and 27 bytes sent
+
+packetdiag ring_buffer_send_byte_buf {
+    node_width = 6
+    node_height = 24
+    default_fontsize = 12
+    colwidth = 128
+
+    #Add 18 byte item
+    0-17: 18 [color = lightyellow];
+    18-127: 110 Free
+
+    #Add 3 byte item
+    128-148: 21 [color = lightyellow];
+    149-255: 107 Free
+
+    #Add 27 byte item
+    256-303: 48 [color = lightyellow];
+    304-383: 80 Free
+}
\ No newline at end of file
diff --git a/docs/_static/diagrams/ring-buffer/ring_buffer_send_non_byte_buf.diag b/docs/_static/diagrams/ring-buffer/ring_buffer_send_non_byte_buf.diag
new file mode 100644 (file)
index 0000000..25cb71e
--- /dev/null
@@ -0,0 +1,30 @@
+#Diagram demonstrating sending in a No-Split/Allow-Split ring buffer
+#Buffer of 128 bytes, and 3 items of size 18, 3, and 27 bytes sent
+
+packetdiag ring_buffer_send_non_byte_buf {
+    node_width = 6
+    node_height = 24
+    default_fontsize = 12
+    colwidth = 128
+
+    #Add 18 byte item
+    0-7: 8 [color = lightblue];
+    8-27: 20 [color = lightyellow];
+    28-127: 100 Free
+
+    #Add 3 byte item
+    128-135: 8 [color = lightblue];
+    136-155: 20 [color = lightyellow];
+    156-163: 8 [color = lightblue];
+    164-167: 4 [color = lightyellow];
+    168-255: 88 Free
+
+    #Add 27 byte item
+    256-263: 8 [color = lightblue];
+    264-283: 20 [color = lightyellow];
+    284-291: 8 [color = lightblue];
+    292-295: 4 [color = lightyellow];
+    296-303: 8 [color = lightblue];
+    304-331: 28 [color = lightyellow];
+    332-383: 52 Free
+}
\ No newline at end of file
diff --git a/docs/_static/diagrams/ring-buffer/ring_buffer_wrap_allow_split.diag b/docs/_static/diagrams/ring-buffer/ring_buffer_wrap_allow_split.diag
new file mode 100644 (file)
index 0000000..94e26e8
--- /dev/null
@@ -0,0 +1,37 @@
+#Diagram demonstrating wrap around in a Allow-Split ring buffer
+#Buffer of 128 bytes, with 56 bytes free, and 28 bytes sent
+
+packetdiag ring_buffer_wrap_allow_split {
+    node_width = 6
+    node_height = 24
+    default_fontsize = 12
+    colwidth = 128
+
+    #Initial state
+    0-39: 40 Free
+    40-47: 8 [color = lightblue];
+    48-63: 16 [color = lightyellow];
+    64-71: 8 [color = lightblue];
+    72-111: 40 [color = lightyellow];
+    112-127: 16 Free
+
+    #Send first part
+    128-167: 40 Free
+    168-175: 8 [color = lightblue];
+    176-191: 16 [color = lightyellow];
+    192-199: 8 [color = lightblue];
+    200-239: 40 [color = lightyellow];
+    240-247: 8 [color = lightblue];
+    248-255: 8 [color = lightyellow];
+
+    #Send second part
+    256-263: 8 [color = lightblue];
+    264-283: 20 [color = lightyellow];
+    284-295: 12 Free 
+    296-303: 8 [color = lightblue];
+    304-319: 16 [color = lightyellow];
+    320-327: 8 [color = lightblue];
+    328-367: 40 [color = lightyellow];
+    368-375: 8 [color = lightblue];
+    376-383: 8 [color = lightyellow];
+}
\ No newline at end of file
diff --git a/docs/_static/diagrams/ring-buffer/ring_buffer_wrap_byte_buf.diag b/docs/_static/diagrams/ring-buffer/ring_buffer_wrap_byte_buf.diag
new file mode 100644 (file)
index 0000000..bf69377
--- /dev/null
@@ -0,0 +1,23 @@
+#Diagram demonstrating wrap around in byte buffer
+#Buffer of 128 bytes, with 56 bytes free, and 28 bytes sent
+
+packetdiag ring_buffer_wrap_byte_buf {
+    node_width = 6
+    node_height = 24
+    default_fontsize = 12
+    colwidth = 128
+
+    #Initial state
+    0-39: 40 Free
+    40-111: 72 [color = lightyellow];
+    112-127: 16 Free
+
+    #Fill up free space at the end of the buffer
+    128-167: 40 Free
+    168-255: 88 [color = lightyellow];
+
+    #Wrap around remaining data
+    256-267: 12 [color = lightyellow];
+    268-295: 28 Free
+    296-383: 88 [color = lightyellow];
+}
\ No newline at end of file
diff --git a/docs/_static/diagrams/ring-buffer/ring_buffer_wrap_no_split.diag b/docs/_static/diagrams/ring-buffer/ring_buffer_wrap_no_split.diag
new file mode 100644 (file)
index 0000000..ff4a11a
--- /dev/null
@@ -0,0 +1,35 @@
+#Diagram demonstrating wrap around in a No-Split ring buffer
+#Buffer of 128 bytes, with 56 bytes free, and 28 bytes sent
+
+packetdiag ring_buffer_wrap_no_split {
+    node_width = 6
+    node_height = 24
+    default_fontsize = 12
+    colwidth = 128
+
+    #Initial state
+    0-39: 40 Free
+    40-47: 8 [color = lightblue];
+    48-63: 16 [color = lightyellow];
+    64-71: 8 [color = lightblue];
+    72-111: 40 [color = lightyellow];
+    112-127: 16 Free
+
+    #Set dummy data
+    128-167: 40 Free
+    168-175: 8 [color = lightblue];
+    176-191: 16 [color = lightyellow];
+    192-199: 8 [color = lightblue];
+    200-239: 40 [color = lightyellow];
+    240-255: Dummy [color = lightgrey];
+
+    #Send wrap around item
+    256-263: 8 [color = lightblue];
+    264-291: 28 [color = lightyellow];
+    292-295: 4 Free
+    296-303: 8 [color = lightblue];
+    304-319: 16 [color = lightyellow];
+    320-327: 8 [color = lightblue];
+    328-367: 40 [color = lightyellow];
+    368-383: Dummy [color = lightgrey];
+}
\ No newline at end of file
index 9851929af7e798600df1570bc9faad0a0994c566..40665d9c5b69a7bdbddaca47d6b04ac8cc18084c 100644 (file)
@@ -16,6 +16,9 @@ of FreeRTOS v8.2.0. This guide outlines the major differences between vanilla
 FreeRTOS and ESP-IDF FreeRTOS. The API reference for vanilla FreeRTOS can be 
 found via http://www.freertos.org/a00106.html
 
+For information regarding features that are exclusive to ESP-IDF FreeRTOS,
+see :doc:`ESP-IDF FreeRTOS Additions<../api-reference/system/freertos_additions>`.
+
 :ref:`backported-features`: Although ESP-IDF FreeRTOS is based on the Xtensa 
 port of FreeRTOS v8.2.0, a number of FreeRTOS v9.0.0 features have been backported
 to ESP-IDF.
@@ -70,10 +73,6 @@ used to free memory pointed to by TLSP. Call
 :cpp:func:`vTaskSetThreadLocalStoragePointerAndDelCallback()` to set TLSP and Deletion
 Callbacks.
 
-:ref:`FreeRTOS Hooks<hooks_api_reference>`: Vanilla FreeRTOS Hooks were not designed for SMP.
-ESP-IDF provides its own Idle and Tick Hooks in addition to the Vanilla FreeRTOS
-hooks. For full details, see the ESP-IDF Hooks API Reference.
-
 :ref:`esp-idf-freertos-configuration`: Several aspects of ESP-IDF FreeRTOS can be 
 configured using ``make meunconfig`` such as running ESP-IDF in Unicore Mode,
 or configuring the number of Thread Local Storage Pointers each task will have.
index 2b0b0929fbf5d9f36576b166058a6b2cb03cc44c..f5b2b8dc00a13a9e9f143bf9c7e9617610474988 100644 (file)
@@ -6,7 +6,8 @@ Overview
 
 This section contains documentation of FreeRTOS types, functions, and macros. It is automatically generated from FreeRTOS header files.
 
-For more information about FreeRTOS features specific to ESP-IDF, see :doc:`ESP-IDF FreeRTOS SMP Changes<../../api-guides/freertos-smp>`.
+For more information about FreeRTOS features specific to ESP-IDF, see :doc:`ESP-IDF FreeRTOS SMP Changes<../../api-guides/freertos-smp>`
+and :doc:`ESP-IDF FreeRTOS Additions<freertos_additions>`.
 
 
 Task API
@@ -35,8 +36,4 @@ Event Group API
 
 .. include:: /_build/inc/event_groups.inc
 
-Ringbuffer API
---------------
-
-.. include:: /_build/inc/ringbuf.inc
 
diff --git a/docs/en/api-reference/system/freertos_additions.rst b/docs/en/api-reference/system/freertos_additions.rst
new file mode 100644 (file)
index 0000000..57d25e9
--- /dev/null
@@ -0,0 +1,403 @@
+FreeRTOS Additions
+==================
+
+Overview
+--------
+
+ESP-IDF FreeRTOS is based on the Xtensa port of FreeRTOS v8.2.0 with significant modifications
+for SMP compatibility (see :doc:`ESP-IDF FreeRTOS SMP Changes<../../api-guides/freertos-smp>`).
+However various features specific to ESP-IDF FreeRTOS have been added. The features are as follows:
+
+:ref:`ring-buffers`: Ring buffers were added to provide a form of buffer that could accept
+entries of arbitrary lengths.
+
+:ref:`hooks`: ESP-IDF FreeRTOS hooks provides support for registering extra Idle and
+Tick hooks at run time. Moreover, the hooks can be asymmetric amongst both CPUs.
+
+
+.. _ring-buffers:
+
+Ring Buffers
+------------
+
+The ESP-IDF FreeRTOS ring buffer is a strictly FIFO buffer that supports arbitrarily sized items.
+Ring buffers are a more memory efficient alternative to FreeRTOS queues in situations where the
+size of items is variable. The capacity of a ring buffer is not measured by the number of items
+it can store, but rather by the amount of memory used for storing items. Items are sent to 
+ring buffers by copy, however for efficiency reasons **items are retrieved by reference**. As a
+result, all retrieved items **must also be returned** in order for them to be removed from
+the ring buffer completely. The ring buffers are split into the three following types:
+
+**No-Split** buffers will guarantee that an item is stored in contiguous memory and will not 
+attempt to split an item under any circumstances. Use no-split buffers when items must occupy
+contiguous memory. 
+
+**Allow-Split** buffers will allow an item to be split when wrapping around if doing so will allow
+the item to be stored. Allow-split buffers are more memory efficient than no-split buffers but
+can return an item in two parts when retrieving.
+
+**Byte buffers** do not store data as separate items. All data is stored as a sequence of bytes,
+and any number of bytes and be sent or retrieved each time. Use byte buffers when separate items
+do not need to be maintained (e.g. a byte stream).
+
+.. note::
+    No-split/allow-split buffers will always store items at 32-bit aligned addresses. Therefore when
+    retrieving an item, the item pointer is guaranteed to be 32-bit aligned.
+
+.. note::
+    Each item stored in no-split/allow-split buffers will **require an additional 8 bytes for a header**.
+    Item sizes will also be rounded up to a 32-bit aligned size (multiple of 4 bytes), however the true
+    item size is recorded within the header. The sizes of no-split/allow-split buffers will also
+    be rounded up when created.
+
+Usage
+^^^^^
+
+The following example demonstrates the usage of :cpp:func:`xRingbufferCreate`
+and :cpp:func:`xRingbufferSend` to create a ring buffer then send an item to it.
+
+.. code-block:: c
+
+    #include "freertos/ringbuf.h"
+    static char tx_item[] = "test_item";
+
+    ...
+
+        //Create ring buffer
+        RingbufHandle_t buf_handle;
+        buf_handle = xRingbufferCreate(1028, RINGBUF_TYPE_NOSPLIT);
+        if (buf_handle == NULL) {
+            printf("Failed to create ring buffer\n");
+        }
+
+        //Send an item
+        UBaseType_t res =  xRingbufferSend(buf_handle, tx_item, sizeof(tx_item), pdMS_TO_TICKS(1000));
+        if (res != pdTRUE) {
+            printf("Failed to send item\n");
+        }
+
+
+The following example demonstrates retrieving and returning an item from a **no-split ring buffer**
+using :cpp:func:`xRingbufferReceive` and :cpp:func:`vRingbufferReturnItem`
+
+.. code-block:: c
+
+    ...
+        
+        //Receive an item from no-split ring buffer
+        size_t item_size;
+        char *item = (char *)xRingbufferReceive(buf_handle, &item_size, pdMS_TO_TICKS(1000));
+
+        //Check received item
+        if (item != NULL) {
+            //Print item
+            for (int i = 0; i < item_size; i++) {
+                printf("%c", item[i]);
+            }
+            printf("\n");
+            //Return Item
+            vRingbufferReturnItem(buf_handle, (void *)item);
+        } else {
+            //Failed to receive item
+            printf("Failed to receive item\n");
+        }
+
+
+The following example demonstrates retrieving and returning an item from an **allow-split ring buffer**
+using :cpp:func:`xRingbufferReceiveSplit` and :cpp:func:`vRingbufferReturnItem`
+
+.. code-block:: c
+
+    ...
+
+        //Receive an item from allow-split ring buffer
+        size_t item_size1, item_size2;
+        char *item1, *item2;
+        BaseType_t ret = xRingbufferReceiveSplit(buf_handle, (void **)&item1, (void **)&item2, &item_size1, &item_size2, pdMS_TO_TICKS(1000));
+
+        //Check received item
+        if (ret == pdTRUE && item1 != NULL) {
+            for (int i = 0; i < item_size1; i++) {
+                printf("%c", item1[i]);
+            }
+            vRingbufferReturnItem(buf_handle, (void *)item1);
+            //Check if item was split
+            if (item2 != NULL) {
+                for (int i = 0; i < item_size2; i++) {
+                    printf("%c", item2[i]);
+                }
+                vRingbufferReturnItem(buf_handle, (void *)item2);
+            }
+            printf("\n");
+        } else {
+            //Failed to receive item
+            printf("Failed to receive item\n");
+        }
+
+
+The following example demonstrates retrieving and returning an item from a **byte buffer**
+using :cpp:func:`xRingbufferReceiveUpTo` and :cpp:func:`vRingbufferReturnItem`
+
+.. code-block:: c
+
+    ...
+
+        //Receive data from byte buffer
+        size_t item_size;
+        char *item = (char *)xRingbufferReceiveUpTo(buf_handle, &item_size, pdMS_TO_TICKS(1000), sizeof(tx_item));
+
+        //Check received data
+        if (item != NULL) {
+            //Print item
+            for (int i = 0; i < item_size; i++) {
+                printf("%c", item[i]);
+            }
+            printf("\n");
+            //Return Item
+            vRingbufferReturnItem(buf_handle, (void *)item);
+        } else {
+            //Failed to receive item
+            printf("Failed to receive item\n");
+        }
+
+
+For ISR safe versions of the functions used above, call :cpp:func:`xRingbufferSendFromISR`, :cpp:func:`xRingbufferReceiveFromISR`,
+:cpp:func:`xRingbufferReceiveSplitFromISR`, :cpp:func:`xRingbufferReceiveUpToFromISR`, and :cpp:func:`vRingbufferReturnItemFromISR` 
+
+
+Sending to Ring Buffer
+^^^^^^^^^^^^^^^^^^^^^^
+
+The following diagrams illustrate the differences between no-split/allow-split buffers 
+and byte buffers with regards to sending items/data. The diagrams assume that three 
+items of sizes **18, 3, and 27 bytes** are sent respectively to a **buffer of 128 bytes**.
+
+.. packetdiag:: ../../../_static/diagrams/ring-buffer/ring_buffer_send_non_byte_buf.diag
+    :caption: Sending items to no-split/allow-split ring buffers
+    :align: center
+
+For no-split/allow-split buffers, a header of 8 bytes precedes every data item. Furthermore, the space 
+occupied by each item is **rounded up to the nearest 32-bit aligned size** in order to maintain overall
+32-bit alignment. However the true size of the item is recorded inside the header which will be 
+returned when the item is retrieved.
+
+Referring to the diagram above, the 18, 3, and 27 byte items are **rounded up to 20, 4, and 28 bytes**
+respectively. An 8 byte header is then added in front of each item.
+
+.. packetdiag:: ../../../_static/diagrams/ring-buffer/ring_buffer_send_byte_buf.diag
+    :caption: Sending items to byte buffers
+    :align: center
+
+Byte buffers treat data as a sequence of bytes and does not incur any overhead 
+(no headers). As a result, all data sent to a byte buffer is merged into a single item.
+
+Referring to the diagram above, the 18, 3, and 27 byte items are sequentially written to the
+byte buffer and **merged into a single item of 48 bytes**.
+
+Wrap around
+^^^^^^^^^^^
+
+The following diagrams illustrate the differences between no-split, allow-split, and byte
+buffers when a sent item requires a wrap around. The diagrams assumes a buffer of **128 bytes**
+with **56 bytes of free space that wraps around** and a sent item of **28 bytes**.
+
+.. packetdiag:: ../../../_static/diagrams/ring-buffer/ring_buffer_wrap_no_split.diag
+    :caption: Wrap around in no-split buffers
+    :align: center
+
+No-split buffers will **only store an item in continuous free space and will not split
+an item under any circumstances**. When the free space at the tail of the buffer is insufficient
+to completely store the item and its header, the free space at the tail will be **marked as dummy data**. 
+The buffer will then wrap around and store the item in the free space at the head of the buffer.
+
+Referring to the diagram above, the 16 bytes of free space at the tail of the buffer is 
+insufficient to store the 28 byte item. Therefore the 16 bytes is marked as dummy data and
+the item is written to the free space at the head of the buffer instead.
+
+.. packetdiag:: ../../../_static/diagrams/ring-buffer/ring_buffer_wrap_allow_split.diag
+    :caption: Wrap around in allow-split buffers 
+    :align: center
+
+Allow-split buffers will attempt to **split the item into two parts** when the free space at the tail
+of the buffer is insufficient to store the item data and its header. Both parts of the
+split item will have their own headers (therefore incurring an extra 8 bytes of overhead).
+
+Referring to the diagram above, the 16 bytes of free space at the tail of the buffer is insufficient
+to store the 28 byte item. Therefore the item is split into two parts (8 and 20 bytes) and written
+as two parts to the buffer.
+
+.. note::
+    Allow-split buffers treats the both parts of the split item as two separate items, therefore call
+    :cpp:func:`xRingbufferReceiveSplit` instead of :cpp:func:`xRingbufferReceive` to receive both
+    parts of a split item in a thread safe manner.
+
+.. packetdiag:: ../../../_static/diagrams/ring-buffer/ring_buffer_wrap_byte_buf.diag
+    :caption: Wrap around in byte buffers 
+    :align: center
+
+Byte buffers will **store as much data as possible into the free space at the tail of buffer**. The remaining 
+data will then be stored in the free space at the head of the buffer. No overhead is incurred when wrapping
+around in byte buffers.
+
+Referring to the diagram above, the 16 bytes of free space at the tail of the buffer is insufficient to
+completely store the 28 bytes of data. Therefore the 16 bytes of free space is filled with data, and the
+remaining 12 bytes are written to the free space at the head of the buffer. The buffer now contains
+data in two separate continuous parts, and each part continuous will be treated as a separate item by the 
+byte buffer.
+
+Retrieving/Returning
+^^^^^^^^^^^^^^^^^^^^
+
+The following diagrams illustrates the differences between no-split/allow-split and
+byte buffers in retrieving and returning data.
+
+.. packetdiag:: ../../../_static/diagrams/ring-buffer/ring_buffer_read_ret_non_byte_buf.diag
+    :caption: Retrieving/Returning items in no-split/allow-split ring buffers
+    :align: center
+
+Items in no-split/allow-split buffers are **retrieved in strict FIFO order** and **must be returned** 
+for the occupied space to be freed. Multiple items can be retrieved before returning, and the items 
+do not necessarily need to be returned in the order they were retrieved. However the freeing of space
+must occur in FIFO order, therefore not returning the earliest retrieved item will prevent the space 
+of subsequent items from being freed.
+
+Referring to the diagram above, the **16, 20, and 8 byte items are retrieved in FIFO order**. However the items
+are not returned in they were retrieved (20, 8, 16). As such, the space is not freed until the first item
+(16 byte) is returned.
+
+.. packetdiag:: ../../../_static/diagrams/ring-buffer/ring_buffer_read_ret_byte_buf.diag
+    :caption: Retrieving/Returning data in byte buffers
+    :align: center
+
+Byte buffers **do not allow multiple retrievals before returning** (every retrieval must be followed by a return
+before another retrieval is permitted). When using :cpp:func:`xRingbufferReceive` or 
+:cpp:func:`xRingbufferReceiveFromISR`, all continuous stored data will be retrieved. :cpp:func:`xRingbufferReceiveUpTo`
+or :cpp:func:`xRingbufferReceiveUpToFromISR` can be used to restrict the maximum number of bytes retrieved. Since
+every retrieval must be followed by a return, the space will be freed as soon as the data is returned.
+
+Referring to the diagram above, the 38 bytes of continuous stored data at the tail of the buffer is retrieved, 
+returned, and freed. The next call to :cpp:func:`xRingbufferReceive` or :cpp:func:`xRingbufferReceiveFromISR` 
+then wraps around and does the same to the 30 bytes of continuous stored data at the head of the buffer.
+
+Ring Buffers with Queue Sets
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Ring buffers can be added to FreeRTOS queue sets using :cpp:func:`xRingbufferAddToQueueSetRead` such that every
+time a ring buffer receives an item or data, the queue set is notified. Once added to a queue set, every
+attempt to retrieve an item from a ring buffer should be preceded by a call to :cpp:func:`xQueueSelectFromSet`.
+To check whether the selected queue set member is the ring buffer, call :cpp:func:`xRingbufferCanRead`.
+
+The following example demonstrates queue set usage with ring buffers.
+
+.. code-block:: c
+
+    #include "freertos/queue.h"
+    #include "freertos/ringbuf.h"
+
+    ...
+
+        //Create ring buffer and queue set
+        RingbufHandle_t buf_handle = xRingbufferCreate(1028, RINGBUF_TYPE_NOSPLIT);
+        QueueSetHandle_t queue_set = xQueueCreateSet(3);
+
+        //Add ring buffer to queue set
+        if (xRingbufferAddToQueueSetRead(buf_handle, queue_set) != pdTRUE) {
+            printf("Failed to add to queue set\n");
+        }
+
+    ...
+
+        //Block on queue set
+        xQueueSetMemberHandle member = xQueueSelectFromSet(queue_set, pdMS_TO_TICKS(1000));
+
+        //Check if member is ring buffer
+        if (member != NULL && xRingbufferCanRead(buf_handle, member) == pdTRUE) {
+            //Member is ring buffer, receive item from ring buffer
+            size_t item_size;
+            char *item = (char *)xRingbufferReceive(buf_handle, &item_size, 0);
+
+            //Handle item
+            ...
+
+        } else {
+            ...
+        }
+
+
+Ring Buffer API Reference
+-------------------------
+
+.. note::
+    Ideally, ring buffers can be used with multiple tasks in an SMP fashion where the **highest
+    priority task will always be serviced first.** However due to the usage of binary semaphores
+    in the ring buffer's underlying implementation, priority inversion may occur under very 
+    specific circumstances.
+
+    The ring buffer governs sending by a binary semaphore which is given whenever space is 
+    freed on the ring buffer. The highest priority task waiting to send will repeatedly take
+    the semaphore until sufficient free space becomes available or until it times out. Ideally
+    this should prevent any lower priority tasks from being serviced as the semaphore should
+    always be given to the highest priority task.
+
+    However in between iterations of acquiring the semaphore, there is a **gap in the critical
+    section** which may permit another task (on the other core or with an even higher priority) to 
+    free some space on the ring buffer and as a result give the semaphore. Therefore the semaphore
+    will be given before the highest priority task can re-acquire the semaphore. This will result
+    in the **semaphore being acquired by the second highest priority task** waiting to send, hence 
+    causing priority inversion.
+
+    This side effect will not affect ring buffer performance drastically given if the number
+    of tasks using the ring buffer simultaneously is low, and the ring buffer is not operating
+    near maximum capacity.
+
+.. include:: /_build/inc/ringbuf.inc
+
+
+.. _hooks:
+
+Hooks
+-----
+
+FreeRTOS consists of Idle Hooks and Tick Hooks which allow for application 
+specific functionality to be added to the Idle Task and Tick Interrupt. 
+ESP-IDF provides its own Idle and Tick Hook API in addition to the hooks 
+provided by Vanilla FreeRTOS. ESP-IDF hooks have the added benefit of
+being run time configurable and asymmetrical.
+
+Vanilla FreeRTOS Hooks
+^^^^^^^^^^^^^^^^^^^^^^
+
+Idle and Tick Hooks in vanilla FreeRTOS are implemented by the user
+defining the functions ``vApplicationIdleHook()`` and  ``vApplicationTickHook()``
+respectively somewhere in the application. Vanilla FreeRTOS will run the user
+defined Idle Hook and Tick Hook on every iteration of the Idle Task and Tick
+Interrupt respectively. 
+
+Vanilla FreeRTOS hooks are referred to as **Legacy Hooks** in ESP-IDF FreeRTOS. 
+To enable legacy hooks, :ref:`CONFIG_FREERTOS_LEGACY_HOOKS`, 
+:ref:`CONFIG_FREERTOS_LEGACY_IDLE_HOOK`, and :ref:`CONFIG_FREERTOS_LEGACY_TICK_HOOK` 
+should all be enabled in ``make menuconfig``.
+
+Due to vanilla FreeRTOS being designed for single core, ``vApplicationIdleHook()``
+and ``vApplicationTickHook()`` can only be defined once. However, the ESP32 is dual core
+in nature, therefore same Idle Hook and Tick Hook are used for both cores (in other words,
+the hooks are symmetrical for both cores).
+
+ESP-IDF Idle and Tick Hooks
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Due to the the dual core nature of the ESP32, it may be necessary for some
+applications to have separate hooks for each core. Furthermore, it may
+be necessary for the Idle Tasks or Tick Interrupts to execute multiple hooks
+that are configurable at run time. Therefore the ESP-IDF provides it's own hooks
+API in addition to the legacy hooks provided by Vanilla FreeRTOS. 
+
+The ESP-IDF tick/idle hooks are registered at run time, and each tick/idle hook 
+must be registered to a specific CPU. When the idle task runs/tick Interrupt
+occurs on a particular CPU, the CPU will run each of its registered idle/tick hooks
+in turn.
+
+
+Hooks API Reference
+-------------------
+
+.. include:: /_build/inc/esp_freertos_hooks.inc
diff --git a/docs/en/api-reference/system/hooks.rst b/docs/en/api-reference/system/hooks.rst
deleted file mode 100644 (file)
index 5a65b29..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-.. _hooks_api_reference:
-
-FreeRTOS Hooks
-==============
-
-Overview
---------
-
-FreeRTOS consists of Idle Hooks and Tick Hooks which allow for application 
-specific funtiionality to be added to the Idle Task and Tick Interrupt. The
-ESP32 is dual core in nature, hence the ESP-IDF provides its own Idle and Tick
-Hooks that are dual core compatible in addition to the hooks provided by Vanilla
-FreeRTOS. 
-
-Vanilla FreeRTOS Hooks
-----------------------
-
-Idle and Tick Hooks in vanilla FreeRTOS are implemented by defining
-implementations for the functions ``vApplicationIdleHook`` and 
-``vApplicationTickHook`` respectively somewhere in the application. Vanilla 
-FreeRTOS will run the user defined Idle Hook every iteration of the Idle Task, 
-whereas the user defined Tick Hook will run once per tick interrupt (given that 
-there are no pended ticks).
-
-Due to vanilla FreeRTOS being designed for single core, ``vApplicationIdleHook``
-and ``vApplicationTickHook`` will be run in both cores on the ESP32. In 
-other words, the same Idle Hook and Tick Hook are used for both cores.
-
-To enable the vanilla FreeRTOS hooks in ESP-IDF, :ref:`CONFIG_FREERTOS_LEGACY_HOOKS` 
-must be enabled in ``make menuconfig``. :ref:`CONFIG_FREERTOS_LEGACY_IDLE_HOOK`
-and :ref:`CONFIG_FREERTOS_LEGACY_TICK_HOOK` should also be enabled.
-
-ESP-IDF Idle and Tick Hooks
----------------------------
-
-Due to the dual core nature of the ESP32, it may be necessary for some
-applications to have seperate Idle Hooks for each core. Furthermore, it may
-be necessary for Idle and Tick Hooks to have execute multiple functionalities
-that are configurable at run time. Therefore the ESP-IDF provides it's own Idle 
-and Tick Hooks in addition to the hooks provided by Vanilla FreeRTOS. 
-
-The ESP-IDF Hooks do not operate in the same way as Vanilla FreeRTOS Hooks
-where users provide a definition for each of the hooks. Instead, the ESP-IDF
-Hooks are predefined to call a list of user registered callbacks specific to 
-each core. Users can register and deregister callbacks which are run on the 
-Idle or Tick Hook of a specific core. 
-
-API Reference
--------------
-
-.. include:: /_build/inc/esp_freertos_hooks.inc
\ No newline at end of file
index 7dd9991c45a1ac9b28fce63feb91141486345aea..bdc5d68e2297408c12c37dee982922f1ef996955 100644 (file)
@@ -5,7 +5,7 @@ System API
    :maxdepth: 1
 
    FreeRTOS <freertos>
-   FreeRTOS Hooks <hooks>
+   FreeRTOS Additions <freertos_additions>
    Heap Memory Allocation <mem_alloc>
    Heap Memory Debugging <heap_debug>
    Interrupt Allocation <intr_alloc>
diff --git a/docs/zh_CN/api-reference/system/freertos_additions.rst b/docs/zh_CN/api-reference/system/freertos_additions.rst
new file mode 100644 (file)
index 0000000..36e4800
--- /dev/null
@@ -0,0 +1 @@
+.. include:: ../../../en/api-reference/system/freertos_additions.rst
\ No newline at end of file
diff --git a/docs/zh_CN/api-reference/system/hooks.rst b/docs/zh_CN/api-reference/system/hooks.rst
deleted file mode 100644 (file)
index 487fb99..0000000
+++ /dev/null
@@ -1 +0,0 @@
-.. include:: ../../../en/api-reference/system/hooks.rst
\ No newline at end of file