summaryrefslogtreecommitdiff
path: root/components/drivers/ipc/ringbuffer.c
diff options
context:
space:
mode:
Diffstat (limited to 'components/drivers/ipc/ringbuffer.c')
-rw-r--r--components/drivers/ipc/ringbuffer.c465
1 files changed, 465 insertions, 0 deletions
diff --git a/components/drivers/ipc/ringbuffer.c b/components/drivers/ipc/ringbuffer.c
new file mode 100644
index 0000000000..deb8979930
--- /dev/null
+++ b/components/drivers/ipc/ringbuffer.c
@@ -0,0 +1,465 @@
+/*
+ * Copyright (c) 2006-2021, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2012-09-30 Bernard first version.
+ * 2013-05-08 Grissiom reimplement
+ * 2016-08-18 heyuanjie add interface
+ * 2021-07-20 arminker fix write_index bug in function rt_ringbuffer_put_force
+ * 2021-08-14 Jackistang add comments for function interface.
+ */
+
+#include <rtthread.h>
+#include <rtdevice.h>
+#include <string.h>
+
+rt_inline enum rt_ringbuffer_state rt_ringbuffer_status(struct rt_ringbuffer *rb)
+{
+ if (rb->read_index == rb->write_index)
+ {
+ if (rb->read_mirror == rb->write_mirror)
+ return RT_RINGBUFFER_EMPTY;
+ else
+ return RT_RINGBUFFER_FULL;
+ }
+ return RT_RINGBUFFER_HALFFULL;
+}
+
+/**
+ * @brief Initialize the ring buffer object.
+ *
+ * @param rb A pointer to the ring buffer object.
+ * @param pool A pointer to the buffer.
+ * @param size The size of the buffer in bytes.
+ */
+void rt_ringbuffer_init(struct rt_ringbuffer *rb,
+ rt_uint8_t *pool,
+ rt_int16_t size)
+{
+ RT_ASSERT(rb != RT_NULL);
+ RT_ASSERT(size > 0);
+
+ /* initialize read and write index */
+ rb->read_mirror = rb->read_index = 0;
+ rb->write_mirror = rb->write_index = 0;
+
+ /* set buffer pool and size */
+ rb->buffer_ptr = pool;
+ rb->buffer_size = RT_ALIGN_DOWN(size, RT_ALIGN_SIZE);
+}
+RTM_EXPORT(rt_ringbuffer_init);
+
+/**
+ * @brief Put a block of data into the ring buffer. If the capacity of ring buffer is insufficient, it will discard out-of-range data.
+ *
+ * @param rb A pointer to the ring buffer object.
+ * @param ptr A pointer to the data buffer.
+ * @param length The size of data in bytes.
+ *
+ * @return Return the data size we put into the ring buffer.
+ */
+rt_size_t rt_ringbuffer_put(struct rt_ringbuffer *rb,
+ const rt_uint8_t *ptr,
+ rt_uint16_t length)
+{
+ rt_uint16_t size;
+
+ RT_ASSERT(rb != RT_NULL);
+
+ /* whether has enough space */
+ size = rt_ringbuffer_space_len(rb);
+
+ /* no space */
+ if (size == 0)
+ return 0;
+
+ /* drop some data */
+ if (size < length)
+ length = size;
+
+ if (rb->buffer_size - rb->write_index > length)
+ {
+ /* read_index - write_index = empty space */
+ rt_memcpy(&rb->buffer_ptr[rb->write_index], ptr, length);
+ /* this should not cause overflow because there is enough space for
+ * length of data in current mirror */
+ rb->write_index += length;
+ return length;
+ }
+
+ rt_memcpy(&rb->buffer_ptr[rb->write_index],
+ &ptr[0],
+ rb->buffer_size - rb->write_index);
+ rt_memcpy(&rb->buffer_ptr[0],
+ &ptr[rb->buffer_size - rb->write_index],
+ length - (rb->buffer_size - rb->write_index));
+
+ /* we are going into the other side of the mirror */
+ rb->write_mirror = ~rb->write_mirror;
+ rb->write_index = length - (rb->buffer_size - rb->write_index);
+
+ return length;
+}
+RTM_EXPORT(rt_ringbuffer_put);
+
+/**
+ * @brief Put a block of data into the ring buffer. If the capacity of ring buffer is insufficient, it will overwrite the existing data in the ring buffer.
+ *
+ * @param rb A pointer to the ring buffer object.
+ * @param ptr A pointer to the data buffer.
+ * @param length The size of data in bytes.
+ *
+ * @return Return the data size we put into the ring buffer.
+ */
+rt_size_t rt_ringbuffer_put_force(struct rt_ringbuffer *rb,
+ const rt_uint8_t *ptr,
+ rt_uint16_t length)
+{
+ rt_uint16_t space_length;
+
+ RT_ASSERT(rb != RT_NULL);
+
+ space_length = rt_ringbuffer_space_len(rb);
+
+ if (length > rb->buffer_size)
+ {
+ ptr = &ptr[length - rb->buffer_size];
+ length = rb->buffer_size;
+ }
+
+ if (rb->buffer_size - rb->write_index > length)
+ {
+ /* read_index - write_index = empty space */
+ rt_memcpy(&rb->buffer_ptr[rb->write_index], ptr, length);
+ /* this should not cause overflow because there is enough space for
+ * length of data in current mirror */
+ rb->write_index += length;
+
+ if (length > space_length)
+ rb->read_index = rb->write_index;
+
+ return length;
+ }
+
+ rt_memcpy(&rb->buffer_ptr[rb->write_index],
+ &ptr[0],
+ rb->buffer_size - rb->write_index);
+ rt_memcpy(&rb->buffer_ptr[0],
+ &ptr[rb->buffer_size - rb->write_index],
+ length - (rb->buffer_size - rb->write_index));
+
+ /* we are going into the other side of the mirror */
+ rb->write_mirror = ~rb->write_mirror;
+ rb->write_index = length - (rb->buffer_size - rb->write_index);
+
+ if (length > space_length)
+ {
+ if (rb->write_index <= rb->read_index)
+ rb->read_mirror = ~rb->read_mirror;
+ rb->read_index = rb->write_index;
+ }
+
+ return length;
+}
+RTM_EXPORT(rt_ringbuffer_put_force);
+
+/**
+ * @brief Get data from the ring buffer.
+ *
+ * @param rb A pointer to the ring buffer.
+ * @param ptr A pointer to the data buffer.
+ * @param length The size of the data we want to read from the ring buffer.
+ *
+ * @return Return the data size we read from the ring buffer.
+ */
+rt_size_t rt_ringbuffer_get(struct rt_ringbuffer *rb,
+ rt_uint8_t *ptr,
+ rt_uint16_t length)
+{
+ rt_size_t size;
+
+ RT_ASSERT(rb != RT_NULL);
+
+ /* whether has enough data */
+ size = rt_ringbuffer_data_len(rb);
+
+ /* no data */
+ if (size == 0)
+ return 0;
+
+ /* less data */
+ if (size < length)
+ length = size;
+
+ if (rb->buffer_size - rb->read_index > length)
+ {
+ /* copy all of data */
+ rt_memcpy(ptr, &rb->buffer_ptr[rb->read_index], length);
+ /* this should not cause overflow because there is enough space for
+ * length of data in current mirror */
+ rb->read_index += length;
+ return length;
+ }
+
+ rt_memcpy(&ptr[0],
+ &rb->buffer_ptr[rb->read_index],
+ rb->buffer_size - rb->read_index);
+ rt_memcpy(&ptr[rb->buffer_size - rb->read_index],
+ &rb->buffer_ptr[0],
+ length - (rb->buffer_size - rb->read_index));
+
+ /* we are going into the other side of the mirror */
+ rb->read_mirror = ~rb->read_mirror;
+ rb->read_index = length - (rb->buffer_size - rb->read_index);
+
+ return length;
+}
+RTM_EXPORT(rt_ringbuffer_get);
+
+/**
+ * @brief Get the first readable byte of the ring buffer.
+ *
+ * @param rb A pointer to the ringbuffer.
+ * @param ptr When this function return, *ptr is a pointer to the first readable byte of the ring buffer.
+ *
+ * @note It is recommended to read only one byte, otherwise it may cause buffer overflow.
+ *
+ * @return Return the size of the ring buffer.
+ */
+rt_size_t rt_ringbuffer_peak(struct rt_ringbuffer *rb, rt_uint8_t **ptr)
+{
+ RT_ASSERT(rb != RT_NULL);
+
+ *ptr = RT_NULL;
+
+ /* whether has enough data */
+ rt_size_t size = rt_ringbuffer_data_len(rb);
+
+ /* no data */
+ if (size == 0)
+ return 0;
+
+ *ptr = &rb->buffer_ptr[rb->read_index];
+
+ if((rt_size_t)(rb->buffer_size - rb->read_index) > size)
+ {
+ rb->read_index += size;
+ return size;
+ }
+
+ size = rb->buffer_size - rb->read_index;
+
+ /* we are going into the other side of the mirror */
+ rb->read_mirror = ~rb->read_mirror;
+ rb->read_index = 0;
+
+ return size;
+}
+RTM_EXPORT(rt_ringbuffer_peak);
+
+/**
+ * @brief Put a byte into the ring buffer. If ring buffer is full, this operation will fail.
+ *
+ * @param rb A pointer to the ring buffer object.
+ * @param ch A byte put into the ring buffer.
+ *
+ * @return Return the data size we put into the ring buffer. The ring buffer is full if returns 0. Otherwise, it will return 1.
+ */
+rt_size_t rt_ringbuffer_putchar(struct rt_ringbuffer *rb, const rt_uint8_t ch)
+{
+ RT_ASSERT(rb != RT_NULL);
+
+ /* whether has enough space */
+ if (!rt_ringbuffer_space_len(rb))
+ return 0;
+
+ rb->buffer_ptr[rb->write_index] = ch;
+
+ /* flip mirror */
+ if (rb->write_index == rb->buffer_size-1)
+ {
+ rb->write_mirror = ~rb->write_mirror;
+ rb->write_index = 0;
+ }
+ else
+ {
+ rb->write_index++;
+ }
+
+ return 1;
+}
+RTM_EXPORT(rt_ringbuffer_putchar);
+
+/**
+ * @brief Put a byte into the ring buffer. If ring buffer is full, it will discard an old data and put into a new data.
+ *
+ * @param rb A pointer to the ring buffer object.
+ * @param ch A byte put into the ring buffer.
+ *
+ * @return Return the data size we put into the ring buffer. Always return 1.
+ */
+rt_size_t rt_ringbuffer_putchar_force(struct rt_ringbuffer *rb, const rt_uint8_t ch)
+{
+ enum rt_ringbuffer_state old_state;
+
+ RT_ASSERT(rb != RT_NULL);
+
+ old_state = rt_ringbuffer_status(rb);
+
+ rb->buffer_ptr[rb->write_index] = ch;
+
+ /* flip mirror */
+ if (rb->write_index == rb->buffer_size-1)
+ {
+ rb->write_mirror = ~rb->write_mirror;
+ rb->write_index = 0;
+ if (old_state == RT_RINGBUFFER_FULL)
+ {
+ rb->read_mirror = ~rb->read_mirror;
+ rb->read_index = rb->write_index;
+ }
+ }
+ else
+ {
+ rb->write_index++;
+ if (old_state == RT_RINGBUFFER_FULL)
+ rb->read_index = rb->write_index;
+ }
+
+ return 1;
+}
+RTM_EXPORT(rt_ringbuffer_putchar_force);
+
+/**
+ * @brief Get a byte from the ring buffer.
+ *
+ * @param rb The pointer to the ring buffer object.
+ * @param ch A pointer to the buffer, used to store one byte.
+ *
+ * @return 0 The ring buffer is empty.
+ * @return 1 Success
+ */
+rt_size_t rt_ringbuffer_getchar(struct rt_ringbuffer *rb, rt_uint8_t *ch)
+{
+ RT_ASSERT(rb != RT_NULL);
+
+ /* ringbuffer is empty */
+ if (!rt_ringbuffer_data_len(rb))
+ return 0;
+
+ /* put byte */
+ *ch = rb->buffer_ptr[rb->read_index];
+
+ if (rb->read_index == rb->buffer_size-1)
+ {
+ rb->read_mirror = ~rb->read_mirror;
+ rb->read_index = 0;
+ }
+ else
+ {
+ rb->read_index++;
+ }
+
+ return 1;
+}
+RTM_EXPORT(rt_ringbuffer_getchar);
+
+/**
+ * @brief Get the size of data in the ring buffer in bytes.
+ *
+ * @param rb The pointer to the ring buffer object.
+ *
+ * @return Return the size of data in the ring buffer in bytes.
+ */
+rt_size_t rt_ringbuffer_data_len(struct rt_ringbuffer *rb)
+{
+ switch (rt_ringbuffer_status(rb))
+ {
+ case RT_RINGBUFFER_EMPTY:
+ return 0;
+ case RT_RINGBUFFER_FULL:
+ return rb->buffer_size;
+ case RT_RINGBUFFER_HALFFULL:
+ default:
+ {
+ rt_size_t wi = rb->write_index, ri = rb->read_index;
+
+ if (wi > ri)
+ return wi - ri;
+ else
+ return rb->buffer_size - (ri - wi);
+ }
+ }
+}
+RTM_EXPORT(rt_ringbuffer_data_len);
+
+/**
+ * @brief Reset the ring buffer object, and clear all contents in the buffer.
+ *
+ * @param rb A pointer to the ring buffer object.
+ */
+void rt_ringbuffer_reset(struct rt_ringbuffer *rb)
+{
+ RT_ASSERT(rb != RT_NULL);
+
+ rb->read_mirror = 0;
+ rb->read_index = 0;
+ rb->write_mirror = 0;
+ rb->write_index = 0;
+}
+RTM_EXPORT(rt_ringbuffer_reset);
+
+#ifdef RT_USING_HEAP
+
+/**
+ * @brief Create a ring buffer object with a given size.
+ *
+ * @param size The size of the buffer in bytes.
+ *
+ * @return Return a pointer to ring buffer object. When the return value is RT_NULL, it means this creation failed.
+ */
+struct rt_ringbuffer *rt_ringbuffer_create(rt_uint16_t size)
+{
+ struct rt_ringbuffer *rb;
+ rt_uint8_t *pool;
+
+ RT_ASSERT(size > 0);
+
+ size = RT_ALIGN_DOWN(size, RT_ALIGN_SIZE);
+
+ rb = (struct rt_ringbuffer *)rt_malloc(sizeof(struct rt_ringbuffer));
+ if (rb == RT_NULL)
+ goto exit;
+
+ pool = (rt_uint8_t *)rt_malloc(size);
+ if (pool == RT_NULL)
+ {
+ rt_free(rb);
+ rb = RT_NULL;
+ goto exit;
+ }
+ rt_ringbuffer_init(rb, pool, size);
+
+exit:
+ return rb;
+}
+RTM_EXPORT(rt_ringbuffer_create);
+
+/**
+ * @brief Destroy the ring buffer object, which is created by rt_ringbuffer_create() .
+ *
+ * @param rb A pointer to the ring buffer object.
+ */
+void rt_ringbuffer_destroy(struct rt_ringbuffer *rb)
+{
+ RT_ASSERT(rb != RT_NULL);
+
+ rt_free(rb->buffer_ptr);
+ rt_free(rb);
+}
+RTM_EXPORT(rt_ringbuffer_destroy);
+
+#endif