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:
Krzysztof Chruscinski 2018-09-07 15:30:56 +02:00 committed by Anas Nashif
commit 26031f7bfd
5 changed files with 675 additions and 66 deletions

View file

@ -3,11 +3,15 @@
Ring Buffers Ring Buffers
############ ############
A :dfn:`ring buffer` is a circular buffer of 32-bit words, whose contents A :dfn:`ring buffer` is a circular buffer, whose contents are stored in
are stored in first-in-first-out order. Data items can be enqueued and dequeued first-in-first-out order. Two content data modes are supported:
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, * **Data item mode**: Multiple 32-bit word data items with metadata
both of which are application-specific. 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:: .. contents::
:local: :local:
@ -21,27 +25,67 @@ by its memory address.
A ring buffer has the following key properties: A ring buffer has the following key properties:
* A **data buffer** of 32-bit words. The data buffer contains the data items * A **data buffer** of 32-bit words or bytes. The data buffer contains the data
that have been added to the ring buffer but not yet removed. 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 * A **data buffer size**, measured in 32-bit words or bytes. This governs the
amount of data (including metadata values) the ring buffer can hold. 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 A ring buffer must be initialized before it can be used. This sets its
data buffer to empty. data buffer to empty.
A ring buffer **data item** is an array of 32-bit words from 0 to 1020 bytes Data item mode
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.
A data items is **dequeued** from a ring buffer by removing the oldest A **data item mode** ring buffer instance is declared using
enqueued item. The contents of the dequeued data item, as well as its :cpp:func:`RING_BUF_ITEM_DECLARE_POW2()` or
two metadata values, are copied to areas supplied by the retriever. :cpp:func:`RING_BUF_ITEM_DECLARE_SIZE()` and accessed using
If the ring buffer is empty, or if the data array supplied by the retriever :cpp:func:`ring_buf_item_put()` and :cpp:func:`ring_buf_item_get()`.
is not large enough to hold the data item's data, the dequeue operation fails.
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 Concurrency
=========== ===========
@ -58,12 +102,13 @@ shouldn't be needed.
Internal Operation Internal Operation
================== ==================
The ring buffer always maintains an empty 32-bit word in its data buffer The ring buffer always maintains an empty 32-bit word (byte in bytes mode) in
to allow it to distinguish between empty and full states. 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 If the size of the data buffer is a power of two, the ring buffer
uses efficient masking operations instead of expensive modulo operations 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 Implementation
************** **************
@ -74,8 +119,8 @@ Defining a Ring Buffer
A ring buffer is defined using a variable of type :c:type:`struct ring_buf`. 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()`. It must then be initialized by calling :cpp:func:`ring_buf_init()`.
The following code defines and initializes an empty ring buffer The following code defines and initializes an empty **data item mode** ring
(which is part of a larger data structure). The ring buffer's data buffer 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. is capable of holding 64 words of data and metadata information.
.. code-block:: c .. code-block:: c
@ -106,14 +151,23 @@ which can be accessed using efficient masking operations.
/* Buffer with 2^8 (or 256) words */ /* Buffer with 2^8 (or 256) words */
RING_BUF_ITEM_DECLARE_POW2(my_ring_buf, 8); RING_BUF_ITEM_DECLARE_POW2(my_ring_buf, 8);
The following code defines a ring buffer with an arbitrary-sized data buffer, The following code defines an application-specific sized **byte mode** ring
which can be accessed using less efficient modulo operations. buffer enqueued and dequeued as raw bytes:
.. code-block:: c .. code-block:: c
#define MY_RING_BUF_WORDS 93 #define MY_RING_BUF_WORDS 93
RING_BUF_ITEM_DECLARE_SIZE(my_ring_buf, MY_RING_BUF_WORDS); 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 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 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 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_POW2()`
* :cpp:func:`RING_BUF_ITEM_DECLARE_SIZE()` * :cpp:func:`RING_BUF_ITEM_DECLARE_SIZE()`
* :cpp:func:`RING_BUF_DECLARE_SIZE()`
* :cpp:func:`ring_buf_init()` * :cpp:func:`ring_buf_init()`
* :cpp:func:`ring_buf_is_empty()` * :cpp:func:`ring_buf_is_empty()`
* :cpp:func:`ring_buf_space_get()` * :cpp:func:`ring_buf_space_get()`
* :cpp:func:`ring_buf_item_put()` * :cpp:func:`ring_buf_item_put()`
* :cpp:func:`ring_buf_item_get()` * :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()`

View file

@ -13,6 +13,7 @@
#include <kernel.h> #include <kernel.h>
#include <misc/util.h> #include <misc/util.h>
#include <errno.h> #include <errno.h>
#include <string.h>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@ -26,11 +27,24 @@ extern "C" {
struct ring_buf { struct ring_buf {
u32_t head; /**< Index in buf for the head element */ u32_t head; /**< Index in buf for the head element */
u32_t tail; /**< Index in buf for the tail element */ u32_t tail; /**< Index in buf for the tail element */
u32_t dropped_put_count; /**< Running tally of the number of failed union ring_buf_misc {
* put attempts 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 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 */ u32_t mask; /**< Modulo mask if size is a power of 2 */
}; };
@ -61,8 +75,8 @@ struct ring_buf {
struct ring_buf name = { \ struct ring_buf name = { \
.size = (1 << (pow)), \ .size = (1 << (pow)), \
.mask = (1 << (pow)) - 1, \ .mask = (1 << (pow)) - 1, \
.buf = _ring_buffer_data_##name \ .buf = { .buf32 = _ring_buffer_data_##name } \
}; }
/** @deprecated Renamed to RING_BUF_ITEM_DECLARE_POW2. */ /** @deprecated Renamed to RING_BUF_ITEM_DECLARE_POW2. */
#define SYS_RING_BUF_DECLARE_POW2(name, pow) \ #define SYS_RING_BUF_DECLARE_POW2(name, pow) \
@ -86,35 +100,55 @@ struct ring_buf {
static u32_t _ring_buffer_data_##name[size32]; \ static u32_t _ring_buffer_data_##name[size32]; \
struct ring_buf name = { \ struct ring_buf name = { \
.size = size32, \ .size = size32, \
.buf = _ring_buffer_data_##name \ .buf = { .buf32 = _ring_buffer_data_##name} \
}; }
/** @deprecated Renamed to RING_BUF_ITEM_DECLARE_SIZE. */ /** @deprecated Renamed to RING_BUF_ITEM_DECLARE_SIZE. */
#define SYS_RING_BUF_DECLARE_SIZE(name, size32) \ #define SYS_RING_BUF_DECLARE_SIZE(name, size32) \
__DEPRECATED_MACRO RING_BUF_ITEM_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. * @brief Initialize a ring buffer.
* *
* This routine initializes a ring buffer, prior to its first use. It is only * 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 * used for ring buffers not defined using RING_BUF_DECLARE,
* RING_BUF_ITEM_DECLARE_SIZE. * 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 * 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 * that doesn't require the use of modulo arithmetic operations to maintain
* itself. * itself.
* *
* @param buf Address of ring buffer. * @param buf Address of ring buffer.
* @param size Ring buffer size (in 32-bit words). * @param size Ring buffer size (in 32-bit words or bytes).
* @param data Ring buffer data area (typically u32_t data[size]). * @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; memset(buf, 0, sizeof(struct ring_buf));
buf->tail = 0;
buf->dropped_put_count = 0;
buf->size = size; buf->size = size;
buf->buf = data; buf->buf.buf32 = data;
if (is_power_of_two(size)) { if (is_power_of_two(size)) {
buf->mask = size - 1; buf->mask = size - 1;
} else { } else {
@ -129,6 +163,27 @@ __deprecated static inline void sys_ring_buf_init(struct ring_buf *buf,
ring_buf_init(buf, size, data); 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. * @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); return (buf->head == buf->tail);
} }
/** @deprecated Renamed to ring_buf_is_empty. */ /** @deprecated Renamed to ring_buf_is_empty. */
__deprecated static inline int sys_ring_buf_is_empty(struct ring_buf *buf) __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. * @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) static inline int ring_buf_space_get(struct ring_buf *buf)
{ {
if (ring_buf_is_empty(buf)) { return z_ring_buf_custom_space_get(buf->size, buf->head, buf->tail);
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;
} }
/** @deprecated Renamed to ring_buf_space_get. */ /** @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); 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);
/** /**
* @} * @}
*/ */

View file

@ -7,6 +7,7 @@
*/ */
#include <ring_buffer.h> #include <ring_buffer.h>
#include <string.h>
/** /**
* Internal data structure for a buffer header. * 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); space = ring_buf_space_get(buf);
if (space >= (size32 + 1)) { if (space >= (size32 + 1)) {
struct ring_element *header = struct ring_element *header =
(struct ring_element *)&buf->buf[buf->tail]; (struct ring_element *)&buf->buf.buf32[buf->tail];
header->type = type; header->type = type;
header->length = size32; header->length = size32;
header->value = value; 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)) { if (likely(buf->mask)) {
for (i = 0; i < size32; ++i) { for (i = 0; i < size32; ++i) {
index = (i + buf->tail + 1) & buf->mask; 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; buf->tail = (buf->tail + size32 + 1) & buf->mask;
} else { } else {
for (i = 0; i < size32; ++i) { for (i = 0; i < size32; ++i) {
index = (i + buf->tail + 1) % buf->size; 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; buf->tail = (buf->tail + size32 + 1) % buf->size;
} }
rc = 0; rc = 0;
} else { } else {
buf->dropped_put_count++; buf->misc.item_mode.dropped_put_count++;
rc = -EMSGSIZE; rc = -EMSGSIZE;
} }
@ -65,7 +66,7 @@ int ring_buf_item_get(struct ring_buf *buf, u16_t *type, u8_t *value,
return -EAGAIN; return -EAGAIN;
} }
header = (struct ring_element *) &buf->buf[buf->head]; header = (struct ring_element *) &buf->buf.buf32[buf->head];
if (header->length > *size32) { if (header->length > *size32) {
*size32 = header->length; *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)) { if (likely(buf->mask)) {
for (i = 0; i < header->length; ++i) { for (i = 0; i < header->length; ++i) {
index = (i + buf->head + 1) & buf->mask; 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; buf->head = (buf->head + header->length + 1) & buf->mask;
} else { } else {
for (i = 0; i < header->length; ++i) { for (i = 0; i < header->length; ++i) {
index = (i + buf->head + 1) % buf->size; 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; buf->head = (buf->head + header->length + 1) % buf->size;
} }
return 0; 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;
}

View file

@ -71,7 +71,8 @@ void test_ring_buffer_main(void)
} }
getsize = INITIAL_SIZE - 1; 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) { if (ret != -EMSGSIZE) {
SYS_LOG_DBG("Allowed retreival with insufficient destination buffer space"); SYS_LOG_DBG("Allowed retreival with insufficient destination buffer space");
zassert_true((getsize == INITIAL_SIZE), "Correct size wasn't reported back to the caller"); 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*/ /**TESTPOINT: init via RING_BUF_ITEM_DECLARE_SIZE*/
RING_BUF_ITEM_DECLARE_SIZE(ringbuf_size, RINGBUFFER_SIZE); RING_BUF_ITEM_DECLARE_SIZE(ringbuf_size, RINGBUFFER_SIZE);
RING_BUF_DECLARE(ringbuf_raw, RINGBUFFER_SIZE);
static struct ring_buf ringbuf, *pbuf; static struct ring_buf ringbuf, *pbuf;
static u32_t buffer[RINGBUFFER_SIZE]; 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); 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*/ /*test case main entry*/
void test_main(void) 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_put_get_thread_isr),
ztest_unit_test(test_ringbuffer_pow2_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_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); ztest_run_test_suite(test_ringbuffer_api);
} }

View file

@ -38,7 +38,7 @@ int logger_put(struct log_cbuffer *logger, char *data, u32_t data_size)
key = irq_lock(); key = irq_lock();
ret = ring_buf_item_put(&logger->ring_buffer, 0, 0, ret = ring_buf_item_put(&logger->ring_buffer, 0, 0,
(u32_t *)data, size32); (u32_t *)data, size32);
irq_unlock(key); irq_unlock(key);
return ret; return ret;