lib: ring_buffer: add raw byte access mode
Extended ring buffer to allow storing raw bytes in it. API has been extended keeping 'data item' mode untouched. Signed-off-by: Krzysztof Chruscinski <krzysztof.chruscinski@nordicsemi.no>
This commit is contained in:
parent
ff5f00f2c3
commit
26031f7bfd
5 changed files with 675 additions and 66 deletions
|
@ -3,11 +3,15 @@
|
|||
Ring Buffers
|
||||
############
|
||||
|
||||
A :dfn:`ring buffer` is a circular buffer of 32-bit words, whose contents
|
||||
are stored in first-in-first-out order. Data items can be enqueued and dequeued
|
||||
from a ring buffer in chunks of up to 1020 bytes. Each data item also has
|
||||
two associated metadata values: a type identifier and a 16-bit integer value,
|
||||
both of which are application-specific.
|
||||
A :dfn:`ring buffer` is a circular buffer, whose contents are stored in
|
||||
first-in-first-out order. Two content data modes are supported:
|
||||
|
||||
* **Data item mode**: Multiple 32-bit word data items with metadata
|
||||
can be enqueued and dequeued from the ring buffer in
|
||||
chunks of up to 1020 bytes. Each data item also has two
|
||||
associated metadata values: a type identifier and a 16-bit
|
||||
integer value, both of which are application-specific.
|
||||
* **Byte mode**: raw bytes can be enqueued and dequeued.
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
|
@ -21,27 +25,67 @@ by its memory address.
|
|||
|
||||
A ring buffer has the following key properties:
|
||||
|
||||
* A **data buffer** of 32-bit words. The data buffer contains the data items
|
||||
that have been added to the ring buffer but not yet removed.
|
||||
* A **data buffer** of 32-bit words or bytes. The data buffer contains the data
|
||||
items or raw bytes that have been added to the ring buffer but not yet
|
||||
removed.
|
||||
|
||||
* A **data buffer size**, measured in 32-bit words. This governs the maximum
|
||||
amount of data (including metadata values) the ring buffer can hold.
|
||||
* A **data buffer size**, measured in 32-bit words or bytes. This governs the
|
||||
maximum amount of data (including metadata values) the ring buffer can hold.
|
||||
|
||||
A ring buffer must be initialized before it can be used. This sets its
|
||||
data buffer to empty.
|
||||
|
||||
A ring buffer **data item** is an array of 32-bit words from 0 to 1020 bytes
|
||||
in length. When a data item is **enqueued** its contents are copied
|
||||
to the data buffer, along with its associated metadata values (which occupy
|
||||
one additional 32-bit word).
|
||||
If the ring buffer has insufficient space to hold the new data item
|
||||
the enqueue operation fails.
|
||||
Data item mode
|
||||
==============
|
||||
|
||||
A data items is **dequeued** from a ring buffer by removing the oldest
|
||||
enqueued item. The contents of the dequeued data item, as well as its
|
||||
two metadata values, are copied to areas supplied by the retriever.
|
||||
If the ring buffer is empty, or if the data array supplied by the retriever
|
||||
is not large enough to hold the data item's data, the dequeue operation fails.
|
||||
A **data item mode** ring buffer instance is declared using
|
||||
:cpp:func:`RING_BUF_ITEM_DECLARE_POW2()` or
|
||||
:cpp:func:`RING_BUF_ITEM_DECLARE_SIZE()` and accessed using
|
||||
:cpp:func:`ring_buf_item_put()` and :cpp:func:`ring_buf_item_get()`.
|
||||
|
||||
A ring buffer **data item** is an array of 32-bit words from 0 to 1020 bytes
|
||||
in length. When a data item is **enqueued** (:cpp:func:`ring_buf_item_put()`)
|
||||
its contents are copied to the data buffer, along with its associated metadata
|
||||
values (which occupy one additional 32-bit word). If the ring buffer has
|
||||
insufficient space to hold the new data item the enqueue operation fails.
|
||||
|
||||
A data items is **dequeued** (:cpp:func:`ring_buf_item_get()`) from a ring
|
||||
buffer by removing the oldest enqueued item. The contents of the dequeued data
|
||||
item, as well as its two metadata values, are copied to areas supplied by the
|
||||
retriever. If the ring buffer is empty, or if the data array supplied by the
|
||||
retriever is not large enough to hold the data item's data, the dequeue
|
||||
operation fails.
|
||||
|
||||
Byte mode
|
||||
=========
|
||||
|
||||
A **byte mode** ring buffer instance is declared using
|
||||
:cpp:func:`RING_BUF_DECLARE_SIZE()` and accessed using:
|
||||
:cpp:func:`ring_buf_put_claim()`, :cpp:func:`ring_buf_put_finish()`,
|
||||
:cpp:func:`ring_buf_get_claim()`, :cpp:func:`ring_buf_get_finish()`,
|
||||
:cpp:func:`ring_buf_put()` and :cpp:func:`ring_buf_get()`.
|
||||
|
||||
Data can be copied into the ring buffer (see
|
||||
:cpp:func:`ring_buf_put()`) or ring buffer memory can be used
|
||||
directly by the user. In the latter case, the operation is split into three stages:
|
||||
|
||||
1. allocating the buffer (:cpp:func:`ring_buf_put_claim()`) when
|
||||
user requests the destination location where data can be written.
|
||||
#. writing the data by the user (e.g. buffer written by DMA).
|
||||
#. indicating the amount of data written to the provided buffer
|
||||
(:cpp:func:`ring_buf_put_finish()`). The amount
|
||||
can be less than or equal to the allocated amount.
|
||||
|
||||
Data can be retrieved from a ring buffer through copying
|
||||
(see :cpp:func:`ring_buf_get()`) or accessed directly by address. In the latter
|
||||
case, the operation is split
|
||||
into three stages:
|
||||
|
||||
1. retrieving source location with valid data written to a ring buffer
|
||||
(see :cpp:func:`ring_buf_get_claim()`).
|
||||
#. processing data
|
||||
#. freeing processed data (see :cpp:func:`ring_buf_get_finish()`).
|
||||
The amount freed can be less than or equal or to the retrieved amount.
|
||||
|
||||
Concurrency
|
||||
===========
|
||||
|
@ -58,12 +102,13 @@ shouldn't be needed.
|
|||
Internal Operation
|
||||
==================
|
||||
|
||||
The ring buffer always maintains an empty 32-bit word in its data buffer
|
||||
to allow it to distinguish between empty and full states.
|
||||
The ring buffer always maintains an empty 32-bit word (byte in bytes mode) in
|
||||
its data buffer to allow it to distinguish between empty and full states.
|
||||
|
||||
If the size of the data buffer is a power of two, the ring buffer
|
||||
uses efficient masking operations instead of expensive modulo operations
|
||||
when enqueuing and dequeuing data items.
|
||||
when enqueuing and dequeuing data items. This option is applicable only for
|
||||
data item mode.
|
||||
|
||||
Implementation
|
||||
**************
|
||||
|
@ -74,8 +119,8 @@ Defining a Ring Buffer
|
|||
A ring buffer is defined using a variable of type :c:type:`struct ring_buf`.
|
||||
It must then be initialized by calling :cpp:func:`ring_buf_init()`.
|
||||
|
||||
The following code defines and initializes an empty ring buffer
|
||||
(which is part of a larger data structure). The ring buffer's data buffer
|
||||
The following code defines and initializes an empty **data item mode** ring
|
||||
buffer (which is part of a larger data structure). The ring buffer's data buffer
|
||||
is capable of holding 64 words of data and metadata information.
|
||||
|
||||
.. code-block:: c
|
||||
|
@ -106,14 +151,23 @@ which can be accessed using efficient masking operations.
|
|||
/* Buffer with 2^8 (or 256) words */
|
||||
RING_BUF_ITEM_DECLARE_POW2(my_ring_buf, 8);
|
||||
|
||||
The following code defines a ring buffer with an arbitrary-sized data buffer,
|
||||
which can be accessed using less efficient modulo operations.
|
||||
The following code defines an application-specific sized **byte mode** ring
|
||||
buffer enqueued and dequeued as raw bytes:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
#define MY_RING_BUF_WORDS 93
|
||||
RING_BUF_ITEM_DECLARE_SIZE(my_ring_buf, MY_RING_BUF_WORDS);
|
||||
|
||||
The following code defines a ring buffer with an arbitrary-sized data buffer,
|
||||
which can be accessed using less efficient modulo operations. Ring buffer is
|
||||
intended to be used for raw bytes.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
#define MY_RING_BUF_BYTES 93
|
||||
RING_BUF_DECLARE_SIZE(my_ring_buf, MY_RING_BUF_BYTES);
|
||||
|
||||
Enqueuing Data
|
||||
==============
|
||||
|
||||
|
@ -145,6 +199,43 @@ can be specified.
|
|||
...
|
||||
}
|
||||
|
||||
Bytes are copied to a **byte mode** ring buffer by calling
|
||||
:cpp:func:`ring_buf_put()`.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
u8_t my_data[MY_RING_BUF_BYTES];
|
||||
u32_t ret;
|
||||
|
||||
ret = ring_buf_put(&ring_buf, my_data, SIZE_OF(my_data));
|
||||
if (ret != SIZE_OF(my_data)) {
|
||||
/* not enough room, partial copy. */
|
||||
...
|
||||
}
|
||||
|
||||
Data can be added to a **byte mode** ring buffer by directly accessing the
|
||||
ring buffer's memory. For example:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
u32_t size;
|
||||
u32_t rx_size;
|
||||
u8_t *data;
|
||||
int err;
|
||||
|
||||
/* Allocate buffer within a ring buffer memory. */
|
||||
size = ring_buf_put_claim(&ring_buf, &data, MY_RING_BUF_BYTES);
|
||||
|
||||
/* Work directly on a ring buffer memory. */
|
||||
rx_size = uart_rx(data, size);
|
||||
|
||||
/* Indicate amount of valid data. rx_size can be equal or less than size. */
|
||||
err = ring_buf_put_finish(&ring_buf, rx_size);
|
||||
if (err != 0) {
|
||||
/* No space to put requested amount of data to ring buffer. */
|
||||
...
|
||||
}
|
||||
|
||||
Retrieving Data
|
||||
===============
|
||||
|
||||
|
@ -171,6 +262,47 @@ A data item is removed from a ring buffer by calling
|
|||
...
|
||||
}
|
||||
|
||||
Data bytes are copied out from a **byte mode** ring buffer by calling
|
||||
:cpp:func:`ring_buf_get()`. For example:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
u8_t my_data[MY_DATA_BYTES];
|
||||
size_t ret;
|
||||
|
||||
ret = ring_buf_get(&ring_buf, my_data, sizeof(my_data));
|
||||
if (ret != sizeof(my_size)) {
|
||||
/* Less bytes copied. */
|
||||
} else {
|
||||
/* Requested amount of bytes retrieved. */
|
||||
...
|
||||
}
|
||||
|
||||
Data can be retrieved from a **byte mode** ring buffer by direct
|
||||
operations on the ring buffer's memory. For example:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
u32_t size;
|
||||
u32_t proc_size;
|
||||
u8_t *data;
|
||||
int err;
|
||||
|
||||
/* Get buffer within a ring buffer memory. */
|
||||
size = ring_buf_get_claim(&ring_buf, &data, MY_RING_BUF_BYTES);
|
||||
|
||||
/* Work directly on a ring buffer memory. */
|
||||
proc_size = process(data, size);
|
||||
|
||||
/* Indicate amount of data that can be freed. proc_size can be equal or less
|
||||
* than size.
|
||||
*/
|
||||
err = ring_buf_get_finish(&ring_buf, proc_size);
|
||||
if (err != 0) {
|
||||
/* proc_size exceeds amount of valid data in a ring buffer. */
|
||||
...
|
||||
}
|
||||
|
||||
APIs
|
||||
****
|
||||
|
||||
|
@ -178,8 +310,15 @@ The following ring buffer APIs are provided by :file:`include/ring_buffer.h`:
|
|||
|
||||
* :cpp:func:`RING_BUF_ITEM_DECLARE_POW2()`
|
||||
* :cpp:func:`RING_BUF_ITEM_DECLARE_SIZE()`
|
||||
* :cpp:func:`RING_BUF_DECLARE_SIZE()`
|
||||
* :cpp:func:`ring_buf_init()`
|
||||
* :cpp:func:`ring_buf_is_empty()`
|
||||
* :cpp:func:`ring_buf_space_get()`
|
||||
* :cpp:func:`ring_buf_item_put()`
|
||||
* :cpp:func:`ring_buf_item_get()`
|
||||
* :cpp:func:`ring_buf_put()`
|
||||
* :cpp:func:`ring_buf_put_claim()`
|
||||
* :cpp:func:`ring_buf_put_finish()`
|
||||
* :cpp:func:`ring_buf_get()`
|
||||
* :cpp:func:`ring_buf_get_claim()`
|
||||
* :cpp:func:`ring_buf_get_finish()`
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <kernel.h>
|
||||
#include <misc/util.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
@ -26,11 +27,24 @@ extern "C" {
|
|||
struct ring_buf {
|
||||
u32_t head; /**< Index in buf for the head element */
|
||||
u32_t tail; /**< Index in buf for the tail element */
|
||||
u32_t dropped_put_count; /**< Running tally of the number of failed
|
||||
* put attempts
|
||||
*/
|
||||
union ring_buf_misc {
|
||||
struct ring_buf_misc_item_mode {
|
||||
u32_t dropped_put_count; /**< Running tally of the
|
||||
* number of failed put
|
||||
* attempts.
|
||||
*/
|
||||
} item_mode;
|
||||
struct ring_buf_misc_byte_mode {
|
||||
u32_t tmp_tail;
|
||||
u32_t tmp_head;
|
||||
} byte_mode;
|
||||
} misc;
|
||||
u32_t size; /**< Size of buf in 32-bit chunks */
|
||||
u32_t *buf; /**< Memory region for stored entries */
|
||||
|
||||
union ring_buf_buffer {
|
||||
u32_t *buf32; /**< Memory region for stored entries */
|
||||
u8_t *buf8;
|
||||
} buf;
|
||||
u32_t mask; /**< Modulo mask if size is a power of 2 */
|
||||
};
|
||||
|
||||
|
@ -61,8 +75,8 @@ struct ring_buf {
|
|||
struct ring_buf name = { \
|
||||
.size = (1 << (pow)), \
|
||||
.mask = (1 << (pow)) - 1, \
|
||||
.buf = _ring_buffer_data_##name \
|
||||
};
|
||||
.buf = { .buf32 = _ring_buffer_data_##name } \
|
||||
}
|
||||
|
||||
/** @deprecated Renamed to RING_BUF_ITEM_DECLARE_POW2. */
|
||||
#define SYS_RING_BUF_DECLARE_POW2(name, pow) \
|
||||
|
@ -86,35 +100,55 @@ struct ring_buf {
|
|||
static u32_t _ring_buffer_data_##name[size32]; \
|
||||
struct ring_buf name = { \
|
||||
.size = size32, \
|
||||
.buf = _ring_buffer_data_##name \
|
||||
};
|
||||
.buf = { .buf32 = _ring_buffer_data_##name} \
|
||||
}
|
||||
|
||||
/** @deprecated Renamed to RING_BUF_ITEM_DECLARE_SIZE. */
|
||||
#define SYS_RING_BUF_DECLARE_SIZE(name, size32) \
|
||||
__DEPRECATED_MACRO RING_BUF_ITEM_DECLARE_SIZE(name, size32)
|
||||
|
||||
/**
|
||||
* @brief Statically define and initialize a ring buffer for byte data.
|
||||
*
|
||||
* This macro establishes a ring buffer of an arbitrary size.
|
||||
*
|
||||
* The ring buffer can be accessed outside the module where it is defined
|
||||
* using:
|
||||
*
|
||||
* @code extern struct ring_buf <name>; @endcode
|
||||
*
|
||||
* @param name Name of the ring buffer.
|
||||
* @param size8 Size of ring buffer (in bytes).
|
||||
*/
|
||||
#define RING_BUF_DECLARE(name, size8) \
|
||||
static u8_t _ring_buffer_data_##name[size8]; \
|
||||
struct ring_buf name = { \
|
||||
.size = size8, \
|
||||
.buf = { .buf8 = _ring_buffer_data_##name} \
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Initialize a ring buffer.
|
||||
*
|
||||
* This routine initializes a ring buffer, prior to its first use. It is only
|
||||
* used for ring buffers not defined using RING_BUF_ITEM_DECLARE_POW2 or
|
||||
* RING_BUF_ITEM_DECLARE_SIZE.
|
||||
* used for ring buffers not defined using RING_BUF_DECLARE,
|
||||
* RING_BUF_ITEM_DECLARE_POW2 or RING_BUF_ITEM_DECLARE_SIZE.
|
||||
*
|
||||
* Setting @a size to a power of 2 establishes a high performance ring buffer
|
||||
* that doesn't require the use of modulo arithmetic operations to maintain
|
||||
* itself.
|
||||
*
|
||||
* @param buf Address of ring buffer.
|
||||
* @param size Ring buffer size (in 32-bit words).
|
||||
* @param data Ring buffer data area (typically u32_t data[size]).
|
||||
* @param size Ring buffer size (in 32-bit words or bytes).
|
||||
* @param data Ring buffer data area (u32_t data[size] or u8_t data[size] for
|
||||
* bytes mode).
|
||||
*/
|
||||
static inline void ring_buf_init(struct ring_buf *buf, u32_t size, u32_t *data)
|
||||
static inline void ring_buf_init(struct ring_buf *buf, u32_t size, void *data)
|
||||
{
|
||||
buf->head = 0;
|
||||
buf->tail = 0;
|
||||
buf->dropped_put_count = 0;
|
||||
memset(buf, 0, sizeof(struct ring_buf));
|
||||
buf->size = size;
|
||||
buf->buf = data;
|
||||
buf->buf.buf32 = data;
|
||||
if (is_power_of_two(size)) {
|
||||
buf->mask = size - 1;
|
||||
} else {
|
||||
|
@ -129,6 +163,27 @@ __deprecated static inline void sys_ring_buf_init(struct ring_buf *buf,
|
|||
ring_buf_init(buf, size, data);
|
||||
}
|
||||
|
||||
/** @brief Determine free space based on ring buffer parameters.
|
||||
*
|
||||
* @note Function for internal use.
|
||||
*
|
||||
* @param size Ring buffer size.
|
||||
* @param head Ring buffer head.
|
||||
* @param tail Ring buffer tail.
|
||||
*
|
||||
* @return Ring buffer free space (in 32-bit words or bytes).
|
||||
*/
|
||||
static inline int z_ring_buf_custom_space_get(u32_t size, u32_t head,
|
||||
u32_t tail)
|
||||
{
|
||||
if (tail < head) {
|
||||
return head - tail - 1;
|
||||
}
|
||||
|
||||
/* buf->tail > buf->head */
|
||||
return (size - tail) + head - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Determine if a ring buffer is empty.
|
||||
*
|
||||
|
@ -140,7 +195,6 @@ static inline int ring_buf_is_empty(struct ring_buf *buf)
|
|||
{
|
||||
return (buf->head == buf->tail);
|
||||
}
|
||||
|
||||
/** @deprecated Renamed to ring_buf_is_empty. */
|
||||
__deprecated static inline int sys_ring_buf_is_empty(struct ring_buf *buf)
|
||||
{
|
||||
|
@ -152,20 +206,11 @@ __deprecated static inline int sys_ring_buf_is_empty(struct ring_buf *buf)
|
|||
*
|
||||
* @param buf Address of ring buffer.
|
||||
*
|
||||
* @return Ring buffer free space (in 32-bit words).
|
||||
* @return Ring buffer free space (in 32-bit words or bytes).
|
||||
*/
|
||||
static inline int ring_buf_space_get(struct ring_buf *buf)
|
||||
{
|
||||
if (ring_buf_is_empty(buf)) {
|
||||
return buf->size - 1;
|
||||
}
|
||||
|
||||
if (buf->tail < buf->head) {
|
||||
return buf->head - buf->tail - 1;
|
||||
}
|
||||
|
||||
/* buf->tail > buf->head */
|
||||
return (buf->size - buf->tail) + buf->head - 1;
|
||||
return z_ring_buf_custom_space_get(buf->size, buf->head, buf->tail);
|
||||
}
|
||||
|
||||
/** @deprecated Renamed to ring_buf_space_get. */
|
||||
|
@ -242,6 +287,142 @@ __deprecated static inline int sys_ring_buf_get(struct ring_buf *buf,
|
|||
return ring_buf_item_get(buf, type, value, data, size32);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Allocate buffer for writing data to a ring buffer.
|
||||
*
|
||||
* With this routine, memory copying can be reduced since internal ring buffer
|
||||
* can be used directly by the user. Once data is written to allocated area
|
||||
* number of bytes written can be confirmed (see @ref ring_buf_put_finish).
|
||||
*
|
||||
* @warning
|
||||
* Use cases involving multiple writers to the ring buffer must prevent
|
||||
* concurrent write operations, either by preventing all writers from
|
||||
* being preempted or by using a mutex to govern writes to the ring buffer.
|
||||
*
|
||||
* @warning
|
||||
* Ring buffer instance should not mix byte access and item access
|
||||
* (calls prefixed with ring_buf_item_).
|
||||
*
|
||||
* @param[in] buf Address of ring buffer.
|
||||
* @param[out] data Pointer to the address. It is set to a location within
|
||||
* ring buffer.
|
||||
* @param[in] size Requested allocation size (in bytes).
|
||||
*
|
||||
* @return Size of allocated buffer which can be smaller than requested if
|
||||
* there is not enough free space or buffer wraps.
|
||||
*/
|
||||
u32_t ring_buf_put_claim(struct ring_buf *buf, u8_t **data, u32_t size);
|
||||
|
||||
/**
|
||||
* @brief Indicate number of bytes written to allocated buffers.
|
||||
*
|
||||
* @warning
|
||||
* Use cases involving multiple writers to the ring buffer must prevent
|
||||
* concurrent write operations, either by preventing all writers from
|
||||
* being preempted or by using a mutex to govern writes to the ring buffer.
|
||||
*
|
||||
* @warning
|
||||
* Ring buffer instance should not mix byte access and item access
|
||||
* (calls prefixed with ring_buf_item_).
|
||||
*
|
||||
* @param buf Address of ring buffer.
|
||||
* @param size Number of valid bytes in the allocated buffers.
|
||||
*
|
||||
* @retval 0 Successful operation.
|
||||
* @retval -EINVAL Provided @a size exceeds free space in the ring buffer.
|
||||
*/
|
||||
int ring_buf_put_finish(struct ring_buf *buf, u32_t size);
|
||||
|
||||
/**
|
||||
* @brief Write (copy) data to a ring buffer.
|
||||
*
|
||||
* This routine writes data to a ring buffer @a buf.
|
||||
*
|
||||
* @warning
|
||||
* Use cases involving multiple writers to the ring buffer must prevent
|
||||
* concurrent write operations, either by preventing all writers from
|
||||
* being preempted or by using a mutex to govern writes to the ring buffer.
|
||||
*
|
||||
* @warning
|
||||
* Ring buffer instance should not mix byte access and item access
|
||||
* (calls prefixed with ring_buf_item_).
|
||||
*
|
||||
* @param buf Address of ring buffer.
|
||||
* @param data Address of data.
|
||||
* @param size Data size (in bytes).
|
||||
*
|
||||
* @retval Number of bytes written.
|
||||
*/
|
||||
u32_t ring_buf_put(struct ring_buf *buf, const u8_t *data, u32_t size);
|
||||
|
||||
/**
|
||||
* @brief Get address of a valid data in a ring buffer.
|
||||
*
|
||||
* With this routine, memory copying can be reduced since internal ring buffer
|
||||
* can be used directly by the user. Once data is processed it can be freed
|
||||
* using @ref ring_buf_get_finish.
|
||||
*
|
||||
* @warning
|
||||
* Use cases involving multiple reads of the ring buffer must prevent
|
||||
* concurrent read operations, either by preventing all readers from
|
||||
* being preempted or by using a mutex to govern reads to the ring buffer.
|
||||
*
|
||||
* @warning
|
||||
* Ring buffer instance should not mix byte access and item access
|
||||
* (calls prefixed with ring_buf_item_).
|
||||
*
|
||||
* @param[in] buf Address of ring buffer.
|
||||
* @param[out] data Pointer to the address. It is set to a location within
|
||||
* ring buffer.
|
||||
* @param[in] size Requested size (in bytes).
|
||||
*
|
||||
* @return Number of valid bytes in the provided buffer which can be smaller
|
||||
* than requested if there is not enough free space or buffer wraps.
|
||||
*/
|
||||
u32_t ring_buf_get_claim(struct ring_buf *buf, u8_t **data, u32_t size);
|
||||
|
||||
/**
|
||||
* @brief Indicate number of bytes read from claimed buffer.
|
||||
*
|
||||
* @warning
|
||||
* Use cases involving multiple reads of the ring buffer must prevent
|
||||
* concurrent read operations, either by preventing all readers from
|
||||
* being preempted or by using a mutex to govern reads to the ring buffer.
|
||||
*
|
||||
* @warning
|
||||
* Ring buffer instance should not mix byte access and item mode
|
||||
* (calls prefixed with ring_buf_item_).
|
||||
*
|
||||
* @param buf Address of ring buffer.
|
||||
* @param size Number of bytes that can be freed.
|
||||
*
|
||||
* @retval 0 Successful operation.
|
||||
* @retval -EINVAL Provided @a size exceeds valid bytes in the ring buffer.
|
||||
*/
|
||||
int ring_buf_get_finish(struct ring_buf *buf, u32_t size);
|
||||
|
||||
/**
|
||||
* @brief Read data from a ring buffer.
|
||||
*
|
||||
* This routine reads data from a ring buffer @a buf.
|
||||
*
|
||||
* @warning
|
||||
* Use cases involving multiple reads of the ring buffer must prevent
|
||||
* concurrent read operations, either by preventing all readers from
|
||||
* being preempted or by using a mutex to govern reads to the ring buffer.
|
||||
*
|
||||
* @warning
|
||||
* Ring buffer instance should not mix byte access and item mode
|
||||
* (calls prefixed with ring_buf_item_).
|
||||
*
|
||||
* @param buf Address of ring buffer.
|
||||
* @param data Address of the output buffer.
|
||||
* @param size Data size (in bytes).
|
||||
*
|
||||
* @retval Number of bytes written to the output buffer.
|
||||
*/
|
||||
u32_t ring_buf_get(struct ring_buf *buf, u8_t *data, u32_t size);
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
*/
|
||||
|
||||
#include <ring_buffer.h>
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* Internal data structure for a buffer header.
|
||||
|
@ -28,7 +29,7 @@ int ring_buf_item_put(struct ring_buf *buf, u16_t type, u8_t value,
|
|||
space = ring_buf_space_get(buf);
|
||||
if (space >= (size32 + 1)) {
|
||||
struct ring_element *header =
|
||||
(struct ring_element *)&buf->buf[buf->tail];
|
||||
(struct ring_element *)&buf->buf.buf32[buf->tail];
|
||||
header->type = type;
|
||||
header->length = size32;
|
||||
header->value = value;
|
||||
|
@ -36,19 +37,19 @@ int ring_buf_item_put(struct ring_buf *buf, u16_t type, u8_t value,
|
|||
if (likely(buf->mask)) {
|
||||
for (i = 0; i < size32; ++i) {
|
||||
index = (i + buf->tail + 1) & buf->mask;
|
||||
buf->buf[index] = data[i];
|
||||
buf->buf.buf32[index] = data[i];
|
||||
}
|
||||
buf->tail = (buf->tail + size32 + 1) & buf->mask;
|
||||
} else {
|
||||
for (i = 0; i < size32; ++i) {
|
||||
index = (i + buf->tail + 1) % buf->size;
|
||||
buf->buf[index] = data[i];
|
||||
buf->buf.buf32[index] = data[i];
|
||||
}
|
||||
buf->tail = (buf->tail + size32 + 1) % buf->size;
|
||||
}
|
||||
rc = 0;
|
||||
} else {
|
||||
buf->dropped_put_count++;
|
||||
buf->misc.item_mode.dropped_put_count++;
|
||||
rc = -EMSGSIZE;
|
||||
}
|
||||
|
||||
|
@ -65,7 +66,7 @@ int ring_buf_item_get(struct ring_buf *buf, u16_t *type, u8_t *value,
|
|||
return -EAGAIN;
|
||||
}
|
||||
|
||||
header = (struct ring_element *) &buf->buf[buf->head];
|
||||
header = (struct ring_element *) &buf->buf.buf32[buf->head];
|
||||
|
||||
if (header->length > *size32) {
|
||||
*size32 = header->length;
|
||||
|
@ -79,16 +80,136 @@ int ring_buf_item_get(struct ring_buf *buf, u16_t *type, u8_t *value,
|
|||
if (likely(buf->mask)) {
|
||||
for (i = 0; i < header->length; ++i) {
|
||||
index = (i + buf->head + 1) & buf->mask;
|
||||
data[i] = buf->buf[index];
|
||||
data[i] = buf->buf.buf32[index];
|
||||
}
|
||||
buf->head = (buf->head + header->length + 1) & buf->mask;
|
||||
} else {
|
||||
for (i = 0; i < header->length; ++i) {
|
||||
index = (i + buf->head + 1) % buf->size;
|
||||
data[i] = buf->buf[index];
|
||||
data[i] = buf->buf.buf32[index];
|
||||
}
|
||||
buf->head = (buf->head + header->length + 1) % buf->size;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** @brief Wraps index if it exceeds the limit.
|
||||
*
|
||||
* @param val Value
|
||||
* @param max Max.
|
||||
*
|
||||
* @return value % max.
|
||||
*/
|
||||
static inline u32_t wrap(u32_t val, u32_t max)
|
||||
{
|
||||
return val >= max ? (val - max) : val;
|
||||
}
|
||||
|
||||
u32_t ring_buf_put_claim(struct ring_buf *buf, u8_t **data, u32_t size)
|
||||
{
|
||||
u32_t space, trail_size, allocated;
|
||||
|
||||
space = z_ring_buf_custom_space_get(buf->size, buf->head,
|
||||
buf->misc.byte_mode.tmp_tail);
|
||||
|
||||
/* Limit requested size to available size. */
|
||||
size = min(size, space);
|
||||
trail_size = buf->size - buf->misc.byte_mode.tmp_tail;
|
||||
|
||||
/* Limit allocated size to trail size. */
|
||||
allocated = min(trail_size, size);
|
||||
|
||||
*data = &buf->buf.buf8[buf->misc.byte_mode.tmp_tail];
|
||||
buf->misc.byte_mode.tmp_tail =
|
||||
wrap(buf->misc.byte_mode.tmp_tail + allocated, buf->size);
|
||||
|
||||
return allocated;
|
||||
}
|
||||
|
||||
int ring_buf_put_finish(struct ring_buf *buf, u32_t size)
|
||||
{
|
||||
if (size > ring_buf_space_get(buf)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
buf->tail = wrap(buf->tail + size, buf->size);
|
||||
buf->misc.byte_mode.tmp_tail = buf->tail;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32_t ring_buf_put(struct ring_buf *buf, const u8_t *data, u32_t size)
|
||||
{
|
||||
u8_t *dst;
|
||||
u32_t partial_size;
|
||||
u32_t total_size = 0;
|
||||
|
||||
do {
|
||||
partial_size = ring_buf_put_claim(buf, &dst, size);
|
||||
memcpy(dst, data, partial_size);
|
||||
total_size += partial_size;
|
||||
size -= partial_size;
|
||||
data += partial_size;
|
||||
} while (size && partial_size);
|
||||
|
||||
ring_buf_put_finish(buf, total_size);
|
||||
|
||||
return total_size;
|
||||
}
|
||||
|
||||
u32_t ring_buf_get_claim(struct ring_buf *buf, u8_t **data, u32_t size)
|
||||
{
|
||||
u32_t space, granted_size, trail_size;
|
||||
|
||||
space = (buf->size - 1) -
|
||||
z_ring_buf_custom_space_get(buf->size,
|
||||
buf->misc.byte_mode.tmp_head,
|
||||
buf->tail);
|
||||
trail_size = buf->size - buf->misc.byte_mode.tmp_head;
|
||||
|
||||
/* Limit requested size to available size. */
|
||||
granted_size = min(size, space);
|
||||
|
||||
/* Limit allocated size to trail size. */
|
||||
granted_size = min(trail_size, granted_size);
|
||||
|
||||
*data = &buf->buf.buf8[buf->misc.byte_mode.tmp_head];
|
||||
buf->misc.byte_mode.tmp_head =
|
||||
wrap(buf->misc.byte_mode.tmp_head + granted_size, buf->size);
|
||||
|
||||
return granted_size;
|
||||
}
|
||||
|
||||
int ring_buf_get_finish(struct ring_buf *buf, u32_t size)
|
||||
{
|
||||
u32_t allocated = (buf->size - 1) - ring_buf_space_get(buf);
|
||||
|
||||
if (size > allocated) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
buf->head = wrap(buf->head + size, buf->size);
|
||||
buf->misc.byte_mode.tmp_head = buf->head;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32_t ring_buf_get(struct ring_buf *buf, u8_t *data, u32_t size)
|
||||
{
|
||||
u8_t *src;
|
||||
u32_t partial_size;
|
||||
u32_t total_size = 0;
|
||||
|
||||
do {
|
||||
partial_size = ring_buf_get_claim(buf, &src, size);
|
||||
memcpy(data, src, partial_size);
|
||||
total_size += partial_size;
|
||||
size -= partial_size;
|
||||
data += partial_size;
|
||||
} while (size && partial_size);
|
||||
|
||||
ring_buf_get_finish(buf, total_size);
|
||||
|
||||
return total_size;
|
||||
}
|
||||
|
|
|
@ -71,7 +71,8 @@ void test_ring_buffer_main(void)
|
|||
}
|
||||
|
||||
getsize = INITIAL_SIZE - 1;
|
||||
ret = ring_buf_item_get(&ring_buf1, &gettype, &getval, getdata, &getsize);
|
||||
ret = ring_buf_item_get(&ring_buf1, &gettype, &getval,
|
||||
getdata, &getsize);
|
||||
if (ret != -EMSGSIZE) {
|
||||
SYS_LOG_DBG("Allowed retreival with insufficient destination buffer space");
|
||||
zassert_true((getsize == INITIAL_SIZE), "Correct size wasn't reported back to the caller");
|
||||
|
@ -104,6 +105,8 @@ RING_BUF_ITEM_DECLARE_POW2(ringbuf_pow2, POW);
|
|||
/**TESTPOINT: init via RING_BUF_ITEM_DECLARE_SIZE*/
|
||||
RING_BUF_ITEM_DECLARE_SIZE(ringbuf_size, RINGBUFFER_SIZE);
|
||||
|
||||
RING_BUF_DECLARE(ringbuf_raw, RINGBUFFER_SIZE);
|
||||
|
||||
static struct ring_buf ringbuf, *pbuf;
|
||||
|
||||
static u32_t buffer[RINGBUFFER_SIZE];
|
||||
|
@ -227,6 +230,167 @@ void test_ringbuffer_size_put_get_thread_isr(void)
|
|||
irq_offload(tringbuf_get, (void *)2);
|
||||
}
|
||||
|
||||
void test_ringbuffer_raw(void)
|
||||
{
|
||||
int i;
|
||||
u8_t inbuf[RINGBUFFER_SIZE];
|
||||
u8_t outbuf[RINGBUFFER_SIZE];
|
||||
size_t in_size;
|
||||
size_t out_size;
|
||||
|
||||
/* Initialize test buffer. */
|
||||
for (i = 0; i < RINGBUFFER_SIZE; i++) {
|
||||
inbuf[i] = i;
|
||||
}
|
||||
|
||||
for (i = 0; i < 10; i++) {
|
||||
memset(outbuf, 0, sizeof(outbuf));
|
||||
in_size = ring_buf_put(&ringbuf_raw, inbuf,
|
||||
RINGBUFFER_SIZE - 2);
|
||||
out_size = ring_buf_get(&ringbuf_raw, outbuf,
|
||||
RINGBUFFER_SIZE - 2);
|
||||
|
||||
zassert_true(in_size == RINGBUFFER_SIZE - 2, NULL);
|
||||
zassert_true(in_size == out_size, NULL);
|
||||
zassert_true(memcmp(inbuf, outbuf, RINGBUFFER_SIZE - 2) == 0,
|
||||
NULL);
|
||||
}
|
||||
|
||||
in_size = ring_buf_put(&ringbuf_raw, inbuf,
|
||||
RINGBUFFER_SIZE);
|
||||
zassert_equal(in_size, RINGBUFFER_SIZE - 1, NULL);
|
||||
|
||||
in_size = ring_buf_put(&ringbuf_raw, inbuf,
|
||||
1);
|
||||
zassert_equal(in_size, 0, NULL);
|
||||
|
||||
out_size = ring_buf_get(&ringbuf_raw, outbuf,
|
||||
RINGBUFFER_SIZE);
|
||||
|
||||
zassert_true(out_size == RINGBUFFER_SIZE - 1, NULL);
|
||||
|
||||
out_size = ring_buf_get(&ringbuf_raw, outbuf,
|
||||
RINGBUFFER_SIZE + 1);
|
||||
zassert_true(out_size == 0, NULL);
|
||||
|
||||
|
||||
}
|
||||
|
||||
void test_ringbuffer_alloc_put(void)
|
||||
{
|
||||
u8_t outputbuf[RINGBUFFER_SIZE];
|
||||
u8_t inputbuf[] = {1, 2, 3, 4};
|
||||
u32_t read_size;
|
||||
u32_t allocated;
|
||||
u32_t sum_allocated;
|
||||
u8_t *data;
|
||||
int err;
|
||||
|
||||
ring_buf_init(&ringbuf_raw, RINGBUFFER_SIZE, ringbuf_raw.buf.buf8);
|
||||
|
||||
allocated = ring_buf_put_claim(&ringbuf_raw, &data, 1);
|
||||
sum_allocated = allocated;
|
||||
zassert_true(allocated == 1, NULL);
|
||||
|
||||
|
||||
allocated = ring_buf_put_claim(&ringbuf_raw, &data,
|
||||
RINGBUFFER_SIZE - 1);
|
||||
sum_allocated += allocated;
|
||||
zassert_true(allocated == RINGBUFFER_SIZE - 2, NULL);
|
||||
|
||||
/* Putting too much returns error */
|
||||
err = ring_buf_put_finish(&ringbuf_raw, RINGBUFFER_SIZE);
|
||||
zassert_true(err != 0, NULL);
|
||||
|
||||
err = ring_buf_put_finish(&ringbuf_raw, 1);
|
||||
zassert_true(err == 0, NULL);
|
||||
|
||||
err = ring_buf_put_finish(&ringbuf_raw, RINGBUFFER_SIZE - 2);
|
||||
zassert_true(err == 0, NULL);
|
||||
|
||||
read_size = ring_buf_get(&ringbuf_raw, outputbuf,
|
||||
RINGBUFFER_SIZE - 1);
|
||||
zassert_true(read_size == (RINGBUFFER_SIZE - 1), NULL);
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
allocated = ring_buf_put_claim(&ringbuf_raw, &data, 2);
|
||||
if (allocated == 2) {
|
||||
data[0] = inputbuf[0];
|
||||
data[1] = inputbuf[1];
|
||||
} else {
|
||||
data[0] = inputbuf[0];
|
||||
ring_buf_put_claim(&ringbuf_raw, &data, 1);
|
||||
data[0] = inputbuf[1];
|
||||
}
|
||||
|
||||
allocated = ring_buf_put_claim(&ringbuf_raw, &data, 2);
|
||||
if (allocated == 2) {
|
||||
data[0] = inputbuf[2];
|
||||
data[1] = inputbuf[3];
|
||||
} else {
|
||||
data[0] = inputbuf[2];
|
||||
ring_buf_put_claim(&ringbuf_raw, &data, 1);
|
||||
data[0] = inputbuf[3];
|
||||
}
|
||||
|
||||
err = ring_buf_put_finish(&ringbuf_raw, 4);
|
||||
zassert_true(err == 0, NULL);
|
||||
|
||||
read_size = ring_buf_get(&ringbuf_raw,
|
||||
outputbuf, 4);
|
||||
zassert_true(read_size == 4, NULL);
|
||||
|
||||
zassert_true(memcmp(outputbuf, inputbuf, 4) == 0, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void test_byte_put_free(void)
|
||||
{
|
||||
u8_t indata[] = {1, 2, 3, 4, 5};
|
||||
int err;
|
||||
u32_t granted;
|
||||
u8_t *data;
|
||||
|
||||
ring_buf_init(&ringbuf_raw, RINGBUFFER_SIZE, ringbuf_raw.buf.buf8);
|
||||
|
||||
/* Ring buffer is empty */
|
||||
granted = ring_buf_get_claim(&ringbuf_raw, &data, RINGBUFFER_SIZE);
|
||||
zassert_true(granted == 0, NULL);
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
ring_buf_put(&ringbuf_raw, indata,
|
||||
RINGBUFFER_SIZE-2);
|
||||
|
||||
granted = ring_buf_get_claim(&ringbuf_raw, &data,
|
||||
RINGBUFFER_SIZE);
|
||||
|
||||
if (granted == (RINGBUFFER_SIZE-2)) {
|
||||
zassert_true(memcmp(indata, data, granted) == 0, NULL);
|
||||
} else if (granted < (RINGBUFFER_SIZE-2)) {
|
||||
/* When buffer wraps, operation is split. */
|
||||
u32_t granted_1 = granted;
|
||||
|
||||
zassert_true(memcmp(indata, data, granted) == 0, NULL);
|
||||
granted = ring_buf_get_claim(&ringbuf_raw, &data,
|
||||
RINGBUFFER_SIZE);
|
||||
|
||||
zassert_true((granted + granted_1) ==
|
||||
RINGBUFFER_SIZE - 2, NULL);
|
||||
zassert_true(memcmp(&indata[granted_1], data, granted)
|
||||
== 0, NULL);
|
||||
} else {
|
||||
zassert_true(false, NULL);
|
||||
}
|
||||
|
||||
/* Freeing more than possible case. */
|
||||
err = ring_buf_get_finish(&ringbuf_raw, RINGBUFFER_SIZE-1);
|
||||
zassert_true(err != 0, NULL);
|
||||
|
||||
err = ring_buf_get_finish(&ringbuf_raw, RINGBUFFER_SIZE-2);
|
||||
zassert_true(err == 0, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/*test case main entry*/
|
||||
void test_main(void)
|
||||
{
|
||||
|
@ -239,6 +403,10 @@ void test_main(void)
|
|||
ztest_unit_test(test_ringbuffer_put_get_thread_isr),
|
||||
ztest_unit_test(test_ringbuffer_pow2_put_get_thread_isr),
|
||||
ztest_unit_test(test_ringbuffer_size_put_get_thread_isr),
|
||||
ztest_unit_test(test_ring_buffer_main));
|
||||
ztest_unit_test(test_ring_buffer_main),
|
||||
ztest_unit_test(test_ringbuffer_raw),
|
||||
ztest_unit_test(test_ringbuffer_alloc_put),
|
||||
ztest_unit_test(test_byte_put_free)
|
||||
);
|
||||
ztest_run_test_suite(test_ringbuffer_api);
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ int logger_put(struct log_cbuffer *logger, char *data, u32_t data_size)
|
|||
|
||||
key = irq_lock();
|
||||
ret = ring_buf_item_put(&logger->ring_buffer, 0, 0,
|
||||
(u32_t *)data, size32);
|
||||
(u32_t *)data, size32);
|
||||
irq_unlock(key);
|
||||
|
||||
return ret;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue