Zephyr Project API 4.4.99
A Scalable Open Source RTOS
Loading...
Searching...
No Matches
ring_buffer.h
Go to the documentation of this file.
1/*
2 * Copyright (c) 2026 Nicolas Pitre <npitre@baylibre.com>
3 * 2026 Måns Ansgariusson <mansgariusson@gmail.com>
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8#ifndef ZEPHYR_INCLUDE_SYS_RING_BUFFER_H_
9#define ZEPHYR_INCLUDE_SYS_RING_BUFFER_H_
10
11#include <errno.h>
12#include <stdbool.h>
13#include <stdint.h>
14#include <string.h>
15
16#include <zephyr/sys/util.h>
17#include <zephyr/toolchain.h>
18#include <zephyr/sys/__assert.h>
19
20#ifdef __cplusplus
21extern "C" {
22#endif
23
36
38#ifdef CONFIG_RING_BUFFER_LARGE
39typedef uint32_t ring_buf_idx_t;
40typedef uint32_t ring_buf_size_t;
41/* read_idx/write_idx + n must not overflow size_t on 32-bit targets (index < 2N, n <= N) */
42#define RING_BUFFER_MAX_SIZE (UINT32_MAX / 4U)
43#define RING_BUFFER_SIZE_ASSERT_MSG "Size too large"
44#else
45typedef uint16_t ring_buf_idx_t;
46typedef uint16_t ring_buf_size_t;
47/* read_idx/write_idx live in [0, 2N) */
48#define RING_BUFFER_MAX_SIZE (UINT16_MAX / 2U)
49#define RING_BUFFER_SIZE_ASSERT_MSG \
50 "Size too large, please enable CONFIG_RING_BUFFER_LARGE"
51#endif
53
62struct ring_buf {
64 uint8_t *buffer;
65 ring_buf_size_t size;
66 ring_buf_idx_t read_idx;
67 ring_buf_idx_t write_idx;
68#ifdef CONFIG_RING_BUFFER
69 ring_buf_idx_t put_claimed;
70 ring_buf_idx_t get_claimed;
71#endif /* CONFIG_RING_BUFFER */
73};
74
76
77/*
78 * Advance a free-running index into [0, 2N) and convert it to the index type.
79 * Takes the buffer capacity N and forms the 2N wrap limit internally, so the
80 * [0, 2N) convention stays in one place. Callers guarantee value < 4N (index
81 * < 2N plus an increment <= N), so a single conditional subtraction suffices.
82 */
83static ALWAYS_INLINE ring_buf_idx_t rb_idx_advance(size_t value, ring_buf_size_t size)
84{
85 size_t lim = 2U * size;
86
87 return (ring_buf_idx_t)(value >= lim ? value - lim : value);
88}
89
90static ALWAYS_INLINE ring_buf_idx_t rb_load_acquire(const ring_buf_idx_t *p)
91{
92 ring_buf_idx_t v;
93
94#if IS_ENABLED(CONFIG_SMP) && (CONFIG_MP_MAX_NUM_CPUS > 1)
95 v = __atomic_load_n(p, __ATOMIC_ACQUIRE);
96#else
97 v = *p;
98 compiler_barrier();
99#endif
100 return v;
101}
102
103static ALWAYS_INLINE ring_buf_idx_t rb_load_relaxed(const ring_buf_idx_t *p)
104{
105#if IS_ENABLED(CONFIG_SMP) && (CONFIG_MP_MAX_NUM_CPUS > 1)
106 return __atomic_load_n(p, __ATOMIC_RELAXED);
107#else
108 return *p;
109#endif
110}
111
112static ALWAYS_INLINE void rb_store_release(ring_buf_idx_t *p, ring_buf_idx_t v)
113{
114#if IS_ENABLED(CONFIG_SMP) && (CONFIG_MP_MAX_NUM_CPUS > 1)
115 __atomic_store_n(p, v, __ATOMIC_RELEASE);
116#else
117 compiler_barrier();
118 *p = v;
119#endif
120}
121
122static ALWAYS_INLINE void rb_store_relaxed(ring_buf_idx_t *p, ring_buf_idx_t v)
123{
124#if IS_ENABLED(CONFIG_SMP) && (CONFIG_MP_MAX_NUM_CPUS > 1)
125 __atomic_store_n(p, v, __ATOMIC_RELAXED);
126#else
127 *p = v;
128#endif
129}
130
132
142#define RING_BUF_INIT(buf, sz) \
143{ \
144 .buffer = (buf), \
145 .size = (ring_buf_size_t)(sz), \
146}
147
163#define RING_BUF_DECLARE(name, size8) \
164 BUILD_ASSERT((size8) <= RING_BUFFER_MAX_SIZE, RING_BUFFER_SIZE_ASSERT_MSG); \
165 static uint8_t __noinit _ring_buffer_data_##name[(size8)]; \
166 struct ring_buf name = RING_BUF_INIT(_ring_buffer_data_##name, (size8))
167
175static inline uint32_t ring_buf_capacity_get(const struct ring_buf *rb)
176{
177 return rb->size;
178}
179
187static inline uint32_t ring_buf_size_get(const struct ring_buf *rb)
188{
189 ring_buf_idx_t write_idx = rb_load_acquire(&rb->write_idx);
190 ring_buf_idx_t read_idx = rb_load_acquire(&rb->read_idx);
191 ring_buf_size_t occ = write_idx - read_idx;
192
193 if (write_idx < read_idx) {
194 occ += 2U * rb->size;
195 }
196 return occ;
197}
198
206static inline uint32_t ring_buf_space_get(const struct ring_buf *rb)
207{
209}
210
218static inline bool ring_buf_is_empty(const struct ring_buf *rb)
219{
220 ring_buf_idx_t write_idx = rb_load_acquire(&rb->write_idx);
221 ring_buf_idx_t read_idx = rb_load_acquire(&rb->read_idx);
222
223 return write_idx == read_idx;
224}
225
233static inline bool ring_buf_is_full(const struct ring_buf *rb)
234{
236}
237
243static inline void ring_buf_reset(struct ring_buf *rb)
244{
245 rb_store_relaxed(&rb->read_idx, 0);
246 rb_store_relaxed(&rb->write_idx, 0);
247#ifdef CONFIG_RING_BUFFER
248 rb->put_claimed = 0;
249 rb->get_claimed = 0;
250#endif /* CONFIG_RING_BUFFER */
251}
252
265static inline void ring_buf_init(struct ring_buf *rb, uint32_t size, uint8_t *data)
266{
267 __ASSERT(size <= RING_BUFFER_MAX_SIZE, RING_BUFFER_SIZE_ASSERT_MSG);
268
269 rb->size = (ring_buf_size_t)size;
270 rb->buffer = data;
271 ring_buf_reset(rb);
272}
273
291static inline uint32_t ring_buf_put_ptr(struct ring_buf *rb, uint8_t **data, size_t offset)
292{
293 ring_buf_idx_t write_idx = rb_load_relaxed(&rb->write_idx);
294 ring_buf_idx_t read_idx = rb_load_acquire(&rb->read_idx);
295 ring_buf_idx_t off, avail;
296
297 __ASSERT_NO_MSG(offset <= ring_buf_space_get(rb));
298 if (offset > 0) {
299 write_idx = rb_idx_advance((size_t)write_idx + offset, rb->size);
300 }
301
302 if (write_idx >= rb->size) {
303 off = write_idx - rb->size;
304 avail = (read_idx >= rb->size ? rb->size : read_idx) - off;
305 } else {
306 off = write_idx;
307 avail = (read_idx >= rb->size ? read_idx - rb->size : rb->size) - off;
308 }
309 *data = &rb->buffer[off];
310 return avail;
311}
312
322static inline void ring_buf_commit(struct ring_buf *rb, size_t size)
323{
324 size_t write_idx = rb_load_relaxed(&rb->write_idx);
325
326 __ASSERT_NO_MSG(size <= ring_buf_space_get(rb));
327 rb_store_release(&rb->write_idx, rb_idx_advance(write_idx + size, rb->size));
328}
329
347static inline uint32_t ring_buf_get_ptr(struct ring_buf *rb, uint8_t **data, size_t offset)
348{
349 ring_buf_idx_t read_idx = rb_load_relaxed(&rb->read_idx);
350 ring_buf_idx_t write_idx = rb_load_acquire(&rb->write_idx);
351 ring_buf_idx_t off, avail;
352
353 __ASSERT_NO_MSG(offset <= ring_buf_size_get(rb));
354 if (offset > 0) {
355 read_idx = rb_idx_advance((size_t)read_idx + offset, rb->size);
356 }
357
358 if (read_idx >= rb->size) {
359 off = read_idx - rb->size;
360 avail = (write_idx >= rb->size ? write_idx : 2U * rb->size) - read_idx;
361 } else {
362 off = read_idx;
363 avail = (write_idx >= rb->size ? rb->size : write_idx) - read_idx;
364 }
365 *data = &rb->buffer[off];
366 return avail;
367}
368
378static inline void ring_buf_consume(struct ring_buf *rb, size_t size)
379{
380 size_t read_idx = rb_load_relaxed(&rb->read_idx);
381
382 __ASSERT_NO_MSG(size <= ring_buf_size_get(rb));
383 rb_store_release(&rb->read_idx, rb_idx_advance(read_idx + size, rb->size));
384}
385
395static inline uint32_t ring_buf_put(struct ring_buf *rb, const uint8_t *data, uint32_t size)
396{
397 uint8_t *dst;
398 uint32_t total = 0;
399 uint32_t chunk;
400 uint32_t avail;
401
402 do {
403 avail = ring_buf_put_ptr(rb, &dst, total);
404 chunk = MIN(avail, size - total);
405 if (chunk == 0U) {
406 break;
407 }
408 memcpy(dst, &data[total], chunk);
409 total += chunk;
410 } while (total < size);
411 ring_buf_commit(rb, total);
412
413 return total;
414}
415
425static inline uint32_t ring_buf_get(struct ring_buf *rb, uint8_t *data, uint32_t size)
426{
427 uint8_t *src;
428 uint32_t chunk;
429 uint32_t avail;
430 uint32_t total = 0;
431
432 do {
433 avail = ring_buf_get_ptr(rb, &src, total);
434 chunk = MIN(avail, size - total);
435 if (chunk == 0U) {
436 break;
437 }
438 if (!IS_ENABLED(CONFIG_RING_BUFFER) || data != NULL) {
439 memcpy(&data[total], src, chunk);
440 }
441 total += chunk;
442 } while (total < size);
443 ring_buf_consume(rb, total);
444
445 return total;
446}
447
460static inline uint32_t ring_buf_peek(const struct ring_buf *rb, uint8_t *data, uint32_t size)
461{
462 uint8_t *src;
463 uint32_t chunk;
464 uint32_t avail;
465 uint32_t total = 0;
466
467 do {
468 avail = ring_buf_get_ptr((struct ring_buf *)rb, &src, total);
469 chunk = MIN(avail, size - total);
470 if (chunk == 0U) {
471 break;
472 }
473 memcpy(&data[total], src, chunk);
474 total += chunk;
475 } while (total < size);
476
477 return total;
478}
479
480#ifdef CONFIG_RING_BUFFER
481
497#define RING_BUF_ITEM_DECLARE(name, size32) \
498 BUILD_ASSERT((size32) <= RING_BUFFER_MAX_SIZE / 4, RING_BUFFER_SIZE_ASSERT_MSG); \
499 static uint32_t __noinit _ring_buffer_data_##name[(size32)]; \
500 struct ring_buf name = RING_BUF_INIT((uint8_t *)_ring_buffer_data_##name, 4 * (size32))
501
511#define RING_BUF_ITEM_DECLARE_SIZE(name, size32) \
512 RING_BUF_ITEM_DECLARE(name, (size32))
513
524#define RING_BUF_ITEM_DECLARE_POW2(name, pow) \
525 RING_BUF_ITEM_DECLARE(name, BIT(pow))
526
535#define RING_BUF_ITEM_SIZEOF(expr) DIV_ROUND_UP(sizeof(expr), sizeof(uint32_t))
536
550__deprecated /* use #include <zephyr/sys/ringq.h> instead */
551static inline void ring_buf_item_init(struct ring_buf *rb, uint32_t size, uint32_t *data)
552{
553 __ASSERT(size <= RING_BUFFER_MAX_SIZE / 4, RING_BUFFER_SIZE_ASSERT_MSG);
554 ring_buf_init(rb, 4 * size, (uint8_t *)data);
555}
556
564__deprecated /* use #include <zephyr/sys/ringq.h> instead */
565static inline uint32_t ring_buf_item_space_get(const struct ring_buf *rb)
566{
567 return ring_buf_space_get(rb) / 4;
568}
569
594__deprecated /* use ring_buf_put_ptr(...) & ring_buf_commit(...) instead */
595static inline uint32_t ring_buf_put_claim(struct ring_buf *rb, uint8_t **data, uint32_t size)
596{
597 uint32_t claimed = MIN(ring_buf_put_ptr(rb, data, rb->put_claimed), size);
598
599 rb->put_claimed += claimed;
600 return claimed;
601}
602
625__deprecated /* use ring_buf_put_ptr(...) & ring_buf_commit(...) instead */
626static inline int ring_buf_put_finish(struct ring_buf *rb, uint32_t size)
627{
628 if (rb->put_claimed < size) {
629 return -EINVAL;
630 }
631 ring_buf_commit(rb, size);
632 rb->put_claimed = 0;
633 return 0;
634}
635
636
661__deprecated /* use ring_buf_get_ptr(...) & ring_buf_consume(...) instead */
662static inline uint32_t ring_buf_get_claim(struct ring_buf *rb, uint8_t **data, uint32_t size)
663{
664 uint32_t claimed = MIN(ring_buf_get_ptr(rb, data, rb->get_claimed), size);
665
666 rb->get_claimed += claimed;
667 return claimed;
668}
669
692__deprecated /* use ring_buf_get_ptr(...) & ring_buf_consume(...) instead */
693static inline int ring_buf_get_finish(struct ring_buf *rb, uint32_t size)
694{
695 if (rb->get_claimed < size) {
696 return -EINVAL;
697 }
698
699 ring_buf_consume(rb, size);
700 rb->get_claimed = 0;
701 return 0;
702}
703
705struct ring_element {
706 uint32_t type : 16;
707 uint32_t length : 8;
708 uint32_t value : 8;
709};
710
716static inline void z_rb_write_no_commit(struct ring_buf *rb, const uint8_t *data,
717 uint32_t size, uint32_t offset)
718{
719 uint32_t avail;
720 uint32_t chunk;
721 uint8_t *dst;
722 uint32_t off = 0;
723
724 while (off < size) {
725 avail = ring_buf_put_ptr(rb, &dst, offset + off);
726 chunk = MIN(avail, size - off);
727
728 memcpy(dst, &data[off], chunk);
729 off += chunk;
730 }
731}
732
734
756__deprecated /* use #include <zephyr/sys/ringq.h> instead */
757static inline int ring_buf_item_put(struct ring_buf *buf, uint16_t type, uint8_t value,
758 uint32_t *data, uint8_t size32)
759{
760 struct ring_element header;
761 uint32_t size = size32 * 4;
762
763 if (size + sizeof(header) > ring_buf_space_get(buf)) {
764 return -EMSGSIZE;
765 }
766
767 header.type = type;
768 header.length = size32;
769 header.value = value;
770 z_rb_write_no_commit(buf, (const uint8_t *)&header, sizeof(header), 0);
771 z_rb_write_no_commit(buf, (const uint8_t *)data, size, sizeof(header));
772 ring_buf_commit(buf, sizeof(header) + size);
773 return 0;
774}
775
800__deprecated /* use #include <zephyr/sys/ringq.h> instead */
801static inline int ring_buf_item_get(struct ring_buf *buf, uint16_t *type, uint8_t *value,
802 uint32_t *data, uint8_t *size32)
803{
804 struct ring_element header;
805
806 if (ring_buf_is_empty(buf)) {
807 return -EAGAIN;
808 }
809
810 ring_buf_peek(buf, (uint8_t *)&header, sizeof(header));
811 if (data != NULL && (header.length > *size32)) {
812 *size32 = header.length;
813 return -EMSGSIZE;
814 }
815
816 *size32 = header.length;
817 *type = header.type;
818 *value = header.value;
819
820 ring_buf_consume(buf, sizeof(header));
821 ring_buf_get(buf, (uint8_t *)data, header.length * 4);
822 return 0;
823}
824
826
835static inline void ring_buf_internal_reset(struct ring_buf *rb, ring_buf_idx_t value)
836{
837 ring_buf_size_t lim = 2U * rb->size;
838
839 value = (lim != 0U) ? (ring_buf_idx_t)(value % lim) : 0U;
840 rb_store_relaxed(&rb->read_idx, value);
841 rb_store_relaxed(&rb->write_idx, value);
842 rb->put_claimed = 0;
843 rb->get_claimed = 0;
844}
845
847
848#else /* CONFIG_RING_BUFFER */
849
851#define Z_RING_BUF_CLAIM_REMOVED(name) \
852 ({ \
853 BUILD_ASSERT(0, #name "() is deprecated and not available " \
854 "when CONFIG_RING_BUFFER=n. Enable " \
855 "CONFIG_RING_BUFFER to keep it during the " \
856 "deprecation period, or migrate as described in the " \
857 "Zephyr 4.5 migration guide."); \
858 0; \
859 })
860
861#define Z_RING_BUF_DECLARE_REMOVED(name) \
862 BUILD_ASSERT(0, #name " is deprecated and not available when " \
863 "CONFIG_RING_BUFFER=n. Enable CONFIG_RING_BUFFER " \
864 "to keep it during the deprecation period, or migrate as " \
865 "described in the Zephyr 4.5 migration guide.")
866
867#define ring_buf_put_claim(...) Z_RING_BUF_CLAIM_REMOVED(ring_buf_put_claim)
868#define ring_buf_put_finish(...) Z_RING_BUF_CLAIM_REMOVED(ring_buf_put_finish)
869#define ring_buf_get_claim(...) Z_RING_BUF_CLAIM_REMOVED(ring_buf_get_claim)
870#define ring_buf_get_finish(...) Z_RING_BUF_CLAIM_REMOVED(ring_buf_get_finish)
871#define ring_buf_item_init(...) Z_RING_BUF_CLAIM_REMOVED(ring_buf_item_init)
872#define ring_buf_item_put(...) Z_RING_BUF_CLAIM_REMOVED(ring_buf_item_put)
873#define ring_buf_item_get(...) Z_RING_BUF_CLAIM_REMOVED(ring_buf_item_get)
874#define ring_buf_item_space_get(...) Z_RING_BUF_CLAIM_REMOVED(ring_buf_item_space_get)
875#define ring_buf_internal_reset(...) Z_RING_BUF_CLAIM_REMOVED(ring_buf_internal_reset)
876
877#define RING_BUF_ITEM_DECLARE(...) Z_RING_BUF_DECLARE_REMOVED(RING_BUF_ITEM_DECLARE)
878#define RING_BUF_ITEM_DECLARE_SIZE(...) Z_RING_BUF_DECLARE_REMOVED(RING_BUF_ITEM_DECLARE_SIZE)
879#define RING_BUF_ITEM_DECLARE_POW2(...) Z_RING_BUF_DECLARE_REMOVED(RING_BUF_ITEM_DECLARE_POW2)
881#endif /* CONFIG_RING_BUFFER */
882
886
887#ifdef __cplusplus
888}
889#endif
890
891#endif /* ZEPHYR_INCLUDE_SYS_RING_BUFFER_H_ */
irp nz macro MOVR cc s mov cc s endm endr irp aw macro LDR aa off
Definition asm-macro-32-bit-gnu.h:20
System error numbers.
static uint32_t ring_buf_capacity_get(const struct ring_buf *rb)
Return ring buffer capacity.
Definition ring_buffer.h:175
static bool ring_buf_is_empty(const struct ring_buf *rb)
Determine if a ring buffer is empty.
Definition ring_buffer.h:218
static uint32_t ring_buf_size_get(const struct ring_buf *rb)
Determine size of available data in a ring buffer.
Definition ring_buffer.h:187
static uint32_t ring_buf_space_get(const struct ring_buf *rb)
Determine free space in a ring buffer.
Definition ring_buffer.h:206
static void ring_buf_commit(struct ring_buf *rb, size_t size)
Indicate number of bytes written to a ring buffer.
Definition ring_buffer.h:322
static uint32_t ring_buf_get(struct ring_buf *rb, uint8_t *data, uint32_t size)
Read data from a ring buffer.
Definition ring_buffer.h:425
static uint32_t ring_buf_put_ptr(struct ring_buf *rb, uint8_t **data, size_t offset)
Get address of region for writing data to a ring buffer.
Definition ring_buffer.h:291
static void ring_buf_consume(struct ring_buf *rb, size_t size)
Indicate number of bytes consumed from a ring buffer.
Definition ring_buffer.h:378
static bool ring_buf_is_full(const struct ring_buf *rb)
Determine if a ring buffer is full.
Definition ring_buffer.h:233
static void ring_buf_reset(struct ring_buf *rb)
Reset ring buffer state.
Definition ring_buffer.h:243
static void ring_buf_init(struct ring_buf *rb, uint32_t size, uint8_t *data)
Initialize a ring buffer for byte data.
Definition ring_buffer.h:265
static uint32_t ring_buf_get_ptr(struct ring_buf *rb, uint8_t **data, size_t offset)
Get address of valid data within a ring buffer.
Definition ring_buffer.h:347
static uint32_t ring_buf_peek(const struct ring_buf *rb, uint8_t *data, uint32_t size)
Peek at data from a ring buffer without consuming it.
Definition ring_buffer.h:460
static uint32_t ring_buf_put(struct ring_buf *rb, const uint8_t *data, uint32_t size)
Write (copy) data to a ring buffer.
Definition ring_buffer.h:395
#define IS_ENABLED(config_macro)
Check for macro definition in compiler-visible expressions.
Definition util_macro.h:154
#define MIN(a, b)
Obtain the minimum of two values.
Definition util.h:406
#define EINVAL
Invalid argument.
Definition errno.h:61
#define EMSGSIZE
Message size.
Definition errno.h:107
#define EAGAIN
No more contexts.
Definition errno.h:50
#define NULL
Definition iar_missing_defs.h:20
#define ALWAYS_INLINE
Definition common.h:186
__UINT32_TYPE__ uint32_t
Definition stdint.h:90
__UINT8_TYPE__ uint8_t
Definition stdint.h:88
__UINT16_TYPE__ uint16_t
Definition stdint.h:89
void * memcpy(void *ZRESTRICT d, const void *ZRESTRICT s, size_t n)
A structure to represent a ring buffer.
Definition ring_buffer.h:62
Misc utilities.
Macros to abstract toolchain specific capabilities.