.. _nanokernel_ring_buffers: Nanokernel Ring Buffers ####################### Definition ********** The ring buffer is defined in :file:`include/misc/ring_buffer.h` and :file:`kernel/nanokernel/ring_buffer.c`. This is an array-based circular buffer, stored in first-in-first-out order. The APIs allow for enqueueing and retrieval of chunks of data up to 1024 bytes in size, along with two metadata values (type ID and an app-specific integer). Unlike nanokernel FIFOs, storage of enqueued items and their metadata is managed in a fixed buffer and there are no preconditions on the data enqueued (other than the size limit). Since the size annotation is only an 8-bit value, sizes are expressed in terms of 32-bit chunks. Internally, the ring buffer always maintains an empty 32-bit block in the buffer to distinguish between empty and full buffers. Any given entry in the buffer will use a 32-bit block for metadata plus any data attached. If the size of the buffer array is a power of two, the ring buffer will use more efficient masking instead of expensive modulo operations to maintain itself. Concurrency *********** Concurrency control of ring buffers is not implemented at this level. 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. Example: Initializing a Ring Buffer =================================== There are three ways to initialize a ring buffer. The first two are through use of macros which defines one (and an associated private buffer) in file scope. You can declare a fast ring buffer that uses mask operations by declaring a power-of-two sized buffer: .. code-block:: c /* Buffer with 2^8 or 256 elements */ SYS_RING_BUF_DECLARE_POW2(my_ring_buf, 8); Arbitrary-sized buffers may also be declared with a different macro, but these will always be slower due to use of modulo operations: .. code-block:: c #define MY_RING_BUF_SIZE 93 SYS_RING_BUF_DECLARE_SIZE(my_ring_buf, MY_RING_BUF_SIZE); Alternatively, a ring buffer may be initialized manually. Whether the buffer will use modulo or mask operations will be detected automatically: .. code-block:: c #define MY_RING_BUF_SIZE 64 struct my_struct { struct ring_buffer 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); ... } Example: Enqueuing data ======================= .. code-block:: c int ret; ret = sys_ring_buf_put(&ring_buf, TYPE_FOO, 0, &my_foo, SIZE32_OF(my_foo)); if (ret == -EMSGSIZE) { ... not enough room for the message .. } If the type or value fields are sufficient, the data pointer and size may be 0. .. 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 message .. } Example: Retrieving data ======================== .. code-block:: c int ret; uint32_t data[6]; size = SIZE32_OF(data); ret = sys_ring_buf_get(&ring_buf, &type, &value, data, &size); if (ret == -EMSGSIZE) { printk("Buffer is too small, need %d uint32_t\n", 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", type, value, size); ... } APIs **** The following APIs for ring buffers are provided by :file:`ring_buffer.h`: :cpp:func:`sys_ring_buf_init()` Initializes a ring buffer. :c:func:`SYS_RING_BUF_DECLARE_POW2()`, :c:func:`SYS_RING_BUF_DECLARE_SIZE()` Declare and init a file-scope ring buffer. :cpp:func:`sys_ring_buf_space_get()` Returns the amount of free buffer storage space in 32-bit dwords. :cpp:func:`sys_ring_buf_is_empty()` Indicates whether a buffer is empty. :cpp:func:`sys_ring_buf_put()` Enqueues an item. :cpp:func:`sys_ring_buf_get()` De-queues an item.