185 lines
5.5 KiB
ReStructuredText
185 lines
5.5 KiB
ReStructuredText
.. _ring_buffers_v2:
|
|
|
|
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.
|
|
|
|
.. contents::
|
|
:local:
|
|
:depth: 2
|
|
|
|
Concepts
|
|
********
|
|
|
|
Any number of ring buffers can be defined. Each ring buffer is referenced
|
|
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 size**, measured in 32-bit words. 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.
|
|
|
|
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.
|
|
|
|
Concurrency
|
|
===========
|
|
|
|
The ring buffer APIs do not provide any concurrency control.
|
|
Depending on usage (particularly with respect to number of concurrent
|
|
readers/writers) applications may need to protect the ring buffer with
|
|
mutexes and/or use semaphores to notify consumers that there is data to
|
|
read.
|
|
|
|
For the trivial case of one producer and one consumer, concurrency
|
|
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.
|
|
|
|
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.
|
|
|
|
Implementation
|
|
**************
|
|
|
|
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:`sys_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
|
|
is capable of holding 64 words of data and metadata information.
|
|
|
|
.. code-block:: c
|
|
|
|
#define MY_RING_BUF_SIZE 64
|
|
|
|
struct my_struct {
|
|
struct ring_buf rb;
|
|
uint32_t buffer[MY_RING_BUF_SIZE];
|
|
...
|
|
};
|
|
struct my_struct ms;
|
|
|
|
void init_my_struct {
|
|
sys_ring_buf_init(&ms.rb, sizeof(ms.buffer), ms.buffer);
|
|
...
|
|
}
|
|
|
|
Alternatively, a ring buffer can be defined and initialized at compile time
|
|
using one of two macros at file scope. Each macro defines both the ring
|
|
buffer itself and its data buffer.
|
|
|
|
The following code defines a ring buffer with a power-of-two sized data buffer,
|
|
which can be accessed using efficient masking operations.
|
|
|
|
.. code-block:: c
|
|
|
|
/* Buffer with 2^8 (or 256) words */
|
|
SYS_RING_BUF_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.
|
|
|
|
.. code-block:: c
|
|
|
|
#define MY_RING_BUF_WORDS 93
|
|
SYS_RING_BUF_DECLARE_SIZE(my_ring_buf, MY_RING_BUF_WORDS);
|
|
|
|
Enqueuing Data
|
|
==============
|
|
|
|
A data item is added to a ring buffer by calling :cpp:func:`sys_ring_buf_put()`.
|
|
|
|
.. code-block:: c
|
|
|
|
uint32_t my_data[MY_DATA_WORDS];
|
|
int ret;
|
|
|
|
ret = sys_ring_buf_put(&ring_buf, TYPE_FOO, 0, my_data, SIZE32_OF(my_data));
|
|
if (ret == -EMSGSIZE) {
|
|
/* not enough room for the data item */
|
|
...
|
|
}
|
|
|
|
If the data item requires only the type or application-specific integer value
|
|
(i.e. it has no data array), a size of 0 and data pointer of :c:macro:`NULL`
|
|
can be specified.
|
|
|
|
.. code-block:: c
|
|
|
|
int ret;
|
|
|
|
ret = sys_ring_buf_put(&ring_buf, TYPE_BAR, 17, NULL, 0);
|
|
if (ret == -EMSGSIZE) {
|
|
/* not enough room for the data item */
|
|
...
|
|
}
|
|
|
|
Retrieving Data
|
|
===============
|
|
|
|
A data item is removed from a ring buffer by calling
|
|
:cpp:func:`sys_ring_buf_get()`.
|
|
|
|
.. code-block:: c
|
|
|
|
uint32_t my_data[MY_DATA_WORDS];
|
|
uint16_t my_type;
|
|
uint8_t my_value;
|
|
uint8_t my_size;
|
|
int ret;
|
|
|
|
my_size = SIZE32_OF(my_data);
|
|
ret = sys_ring_buf_get(&ring_buf, &my_type, &my_value, my_data, &my_size);
|
|
if (ret == -EMSGSIZE) {
|
|
printk("Buffer is too small, need %d uint32_t\n", my_size);
|
|
} else if (ret == -EAGAIN) {
|
|
printk("Ring buffer is empty\n");
|
|
} else {
|
|
printk("Got item of type %u value &u of size %u dwords\n",
|
|
my_type, my_value, my_size);
|
|
...
|
|
}
|
|
|
|
APIs
|
|
****
|
|
|
|
The following ring buffer APIs are provided by :file:`misc/ring_buffer.h`:
|
|
|
|
* :cpp:func:`SYS_RING_BUF_DECLARE_POW2()`
|
|
* :cpp:func:`SYS_RING_BUF_DECLARE_SIZE()`
|
|
* :cpp:func:`sys_ring_buf_init()`
|
|
* :cpp:func:`sys_ring_buf_is_empty()`
|
|
* :cpp:func:`sys_ring_buf_space_get()`
|
|
* :cpp:func:`sys_ring_buf_put()`
|
|
* :cpp:func:`sys_ring_buf_get()`
|