Browse Source

[DeviceDriver] Add rbb (ring block buffer) data structure.

armink 6 years ago
parent
commit
dff8a2c513

+ 115 - 0
components/drivers/include/ipc/ringblk_buf.h

@@ -0,0 +1,115 @@
+/*
+ * File      : ringblk_buf.h
+ * This file is part of RT-Thread RTOS
+ * COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2018-08-25     armink       the first version
+ */
+
+#ifndef _RINGBLK_BUF_H_
+#define _RINGBLK_BUF_H_
+
+/*
+ * Introduction:
+ * The rbb is the ring buffer which is composed with many blocks. It is different from the ring buffer.
+ * The ring buffer is only composed with chars. The rbb put and get supported zero copies. So the rbb
+ * is very suitable for put block and get block by a certain order. Such as DMA block transmit,
+ * communicate frame send/recv, and so on.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum rt_rbb_status
+{
+    /* unused status when first initialize or after blk_free() */
+    RT_RBB_BLK_UNUSED,
+    /* initialized status after blk_alloc() */
+    RT_RBB_BLK_INITED,
+    /* put status after blk_put() */
+    RT_RBB_BLK_PUT,
+    /* get status after blk_get() */
+    RT_RBB_BLK_GET,
+};
+typedef enum rt_rbb_status rt_rbb_status_t;
+
+/**
+ * the block of rbb
+ */
+struct rt_rbb_blk
+{
+    rt_rbb_status_t status :8;
+    /* less then 2^24 */
+    rt_size_t size :24;
+    rt_uint8_t *buf;
+    rt_slist_t list;
+};
+typedef struct rt_rbb_blk *rt_rbb_blk_t;
+
+/**
+ * Rbb block queue: the blocks (from block1->buf to blockn->buf) memory which on this queue is continuous.
+ */
+struct rt_rbb_blk_queue
+{
+    rt_rbb_blk_t blocks;
+    rt_size_t blk_num;
+};
+typedef struct rt_rbb_blk_queue *rt_rbb_blk_queue_t;
+
+/**
+ * ring block buffer
+ */
+struct rt_rbb
+{
+    rt_uint8_t *buf;
+    rt_size_t buf_size;
+    /* all of blocks */
+    rt_rbb_blk_t blk_set;
+    rt_size_t blk_max_num;
+    /* saved the initialized and put status blocks */
+    rt_slist_t blk_list;
+};
+typedef struct rt_rbb *rt_rbb_t;
+
+/* rbb (ring block buffer) API */
+void rt_rbb_init(rt_rbb_t rbb, rt_uint8_t *buf, rt_size_t buf_size, rt_rbb_blk_t block_set, rt_size_t blk_max_num);
+rt_rbb_t rt_rbb_create(rt_size_t buf_size, rt_size_t blk_max_num);
+void rt_rbb_destroy(rt_rbb_t rbb);
+rt_size_t rt_rbb_get_buf_size(rt_rbb_t rbb);
+
+/* rbb block API */
+rt_rbb_blk_t rt_rbb_blk_alloc(rt_rbb_t rbb, rt_size_t blk_size);
+void rt_rbb_blk_put(rt_rbb_blk_t block);
+rt_rbb_blk_t rt_rbb_blk_get(rt_rbb_t rbb);
+void rt_rbb_blk_free(rt_rbb_t rbb, rt_rbb_blk_t block);
+
+/* rbb block queue API */
+rt_size_t rt_rbb_blk_queue_get(rt_rbb_t rbb, rt_size_t queue_data_len, rt_rbb_blk_queue_t blk_queue);
+rt_size_t rt_rbb_blk_queue_len(rt_rbb_blk_queue_t blk_queue);
+rt_uint8_t *rt_rbb_blk_queue_buf(rt_rbb_blk_queue_t blk_queue);
+void rt_rbb_blk_queue_free(rt_rbb_t rbb, rt_rbb_blk_queue_t blk_queue);
+rt_size_t rt_rbb_next_blk_queue_len(rt_rbb_t rbb);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RINGBLK_BUF_H_ */

+ 1 - 0
components/drivers/include/rtdevice.h

@@ -35,6 +35,7 @@
 #include "ipc/waitqueue.h"
 #include "ipc/pipe.h"
 #include "ipc/poll.h"
+#include "ipc/ringblk_buf.h"
 
 #ifdef __cplusplus
 extern "C" {

+ 534 - 0
components/drivers/src/ringblk_buf.c

@@ -0,0 +1,534 @@
+/*
+ * File      : ringblk_buf.c
+ * This file is part of RT-Thread RTOS
+ * COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2018-08-25     armink       the first version
+ */
+
+#include <rthw.h>
+#include <rtthread.h>
+#include <rtdevice.h>
+
+/**
+ * ring block buffer object initialization
+ *
+ * @param rbb ring block buffer object
+ * @param buf buffer
+ * @param buf_size buffer size
+ * @param block_set
+ * @param blk_max_num
+ */
+void rt_rbb_init(rt_rbb_t rbb, rt_uint8_t *buf, rt_size_t buf_size, rt_rbb_blk_t block_set, rt_size_t blk_max_num)
+{
+    rt_size_t i;
+
+    RT_ASSERT(rbb);
+    RT_ASSERT(buf);
+    RT_ASSERT(block_set);
+
+    rbb->buf = buf;
+    rbb->buf_size = buf_size;
+    rbb->blk_set = block_set;
+    rbb->blk_max_num = blk_max_num;
+    rt_slist_init(&rbb->blk_list);
+    /* initialize block status */
+    for (i = 0; i < blk_max_num; i++)
+    {
+        block_set[i].status = RT_RBB_BLK_UNUSED;
+    }
+}
+RTM_EXPORT(rt_rbb_init);
+
+/**
+ * ring block buffer object create
+ *
+ * @param buf_size buffer size
+ * @param blk_max_num max block number
+ *
+ * @return != NULL: ring block buffer object
+ *            NULL: create failed
+ */
+rt_rbb_t rt_rbb_create(rt_size_t buf_size, rt_size_t blk_max_num)
+{
+    rt_rbb_t rbb = NULL;
+    rt_uint8_t *buf;
+    rt_rbb_blk_t blk_set;
+
+    rbb = (rt_rbb_t)rt_malloc(sizeof(struct rt_rbb));
+    if (!rbb)
+    {
+        return NULL;
+    }
+
+    buf = (rt_uint8_t *)rt_malloc(buf_size);
+    if (!buf)
+    {
+        rt_free(rbb);
+        return NULL;
+    }
+
+    blk_set = (rt_rbb_blk_t)rt_malloc(sizeof(struct rt_rbb_blk) * blk_max_num);
+    if (!blk_set)
+    {
+        rt_free(buf);
+        rt_free(rbb);
+        return NULL;
+    }
+
+    rt_rbb_init(rbb, buf, buf_size, blk_set, blk_max_num);
+
+    return rbb;
+}
+RTM_EXPORT(rt_rbb_create);
+
+/**
+ * ring block buffer object destroy
+ *
+ * @param rbb ring block buffer object
+ */
+void rt_rbb_destroy(rt_rbb_t rbb)
+{
+    RT_ASSERT(rbb);
+
+    rt_free(rbb);
+    rt_free(rbb->buf);
+    rt_free(rbb->blk_set);
+
+}
+RTM_EXPORT(rt_rbb_destroy);
+
+static rt_rbb_blk_t find_empty_blk_in_set(rt_rbb_t rbb)
+{
+    rt_size_t i;
+
+    RT_ASSERT(rbb);
+
+    for (i = 0; i < rbb->blk_max_num; i ++)
+    {
+        if (rbb->blk_set[i].status == RT_RBB_BLK_UNUSED)
+        {
+            return &rbb->blk_set[i];
+        }
+    }
+
+    return NULL;
+}
+
+/**
+ * Allocate a block by given size. The block will add to blk_list when allocate success.
+ *
+ * @param rbb ring block buffer object
+ * @param blk_size block size
+ *
+ * @return != NULL: allocated block
+ *            NULL: allocate failed
+ */
+rt_rbb_blk_t rt_rbb_blk_alloc(rt_rbb_t rbb, rt_size_t blk_size)
+{
+    rt_base_t level;
+    rt_size_t empty1 = 0, empty2 = 0;
+    rt_rbb_blk_t head, tail, new = NULL;
+
+    RT_ASSERT(rbb);
+    RT_ASSERT(blk_size < 1L << 24);
+
+    level = rt_hw_interrupt_disable();
+
+    new = find_empty_blk_in_set(rbb);
+
+    if (rt_slist_len(&rbb->blk_list) < rbb->blk_max_num && new)
+    {
+        if (rt_slist_len(&rbb->blk_list) > 0)
+        {
+            head = rt_slist_first_entry(&rbb->blk_list, struct rt_rbb_blk, list);
+            tail = rt_slist_tail_entry(&rbb->blk_list, struct rt_rbb_blk, list);
+            if (head->buf <= tail->buf)
+            {
+                /**
+                 *                      head                     tail
+                 * +--------------------------------------+-----------------+------------------+
+                 * |      empty2     | block1 |   block2  |      block3     |       empty1     |
+                 * +--------------------------------------+-----------------+------------------+
+                 *                            rbb->buf
+                 */
+                empty1 = (rbb->buf + rbb->buf_size) - (tail->buf + tail->size);
+                empty2 = head->buf - rbb->buf;
+
+                if (empty1 >= blk_size)
+                {
+                    rt_slist_append(&rbb->blk_list, &new->list);
+                    new->status = RT_RBB_BLK_INITED;
+                    new->buf = tail->buf + tail->size;
+                    new->size = blk_size;
+                }
+                else if (empty2 >= blk_size)
+                {
+                    rt_slist_append(&rbb->blk_list, &new->list);
+                    new->status = RT_RBB_BLK_INITED;
+                    new->buf = rbb->buf;
+                    new->size = blk_size;
+                }
+                else
+                {
+                    /* no space */
+                    new = NULL;
+                }
+            }
+            else
+            {
+                /**
+                 *        tail                                              head
+                 * +----------------+-------------------------------------+--------+-----------+
+                 * |     block3     |                empty1               | block1 |  block2   |
+                 * +----------------+-------------------------------------+--------+-----------+
+                 *                            rbb->buf
+                 */
+                empty1 = head->buf - (tail->buf + tail->size);
+
+                if (empty1 >= blk_size)
+                {
+                    rt_slist_append(&rbb->blk_list, &new->list);
+                    new->status = RT_RBB_BLK_INITED;
+                    new->buf = tail->buf + tail->size;
+                    new->size = blk_size;
+                }
+                else
+                {
+                    /* no space */
+                    new = NULL;
+                }
+            }
+        }
+        else
+        {
+            /* the list is empty */
+            rt_slist_append(&rbb->blk_list, &new->list);
+            new->status = RT_RBB_BLK_INITED;
+            new->buf = rbb->buf;
+            new->size = blk_size;
+        }
+    }
+    else
+    {
+        new = NULL;
+    }
+
+    rt_hw_interrupt_enable(level);
+
+    return new;
+}
+RTM_EXPORT(rt_rbb_blk_alloc);
+
+/**
+ * put a block to ring block buffer object
+ *
+ * @param block the block
+ */
+void rt_rbb_blk_put(rt_rbb_blk_t block)
+{
+    RT_ASSERT(block);
+    RT_ASSERT(block->status == RT_RBB_BLK_INITED);
+
+    block->status = RT_RBB_BLK_PUT;
+}
+RTM_EXPORT(rt_rbb_blk_put);
+
+/**
+ * get a block from the ring block buffer object
+ *
+ * @param rbb ring block buffer object
+ *
+ * @return != NULL: block
+ *            NULL: get failed
+ */
+rt_rbb_blk_t rt_rbb_blk_get(rt_rbb_t rbb)
+{
+    rt_base_t level;
+    rt_rbb_blk_t block = NULL;
+    rt_slist_t *node;
+
+    RT_ASSERT(rbb);
+
+    if (rt_slist_isempty(&rbb->blk_list))
+        return 0;
+
+    level = rt_hw_interrupt_disable();
+
+    for (node = rt_slist_first(&rbb->blk_list); node; node = rt_slist_next(node))
+    {
+        block = rt_slist_entry(node, struct rt_rbb_blk, list);
+        if (block->status == RT_RBB_BLK_PUT)
+        {
+            block->status = RT_RBB_BLK_GET;
+            goto __exit;
+        }
+    }
+    /* not found */
+    block = NULL;
+
+__exit:
+
+    rt_hw_interrupt_enable(level);
+
+    return block;
+}
+RTM_EXPORT(rt_rbb_blk_get);
+
+/**
+ * free the block
+ *
+ * @param rbb ring block buffer object
+ * @param block the block
+ */
+void rt_rbb_blk_free(rt_rbb_t rbb, rt_rbb_blk_t block)
+{
+    rt_base_t level;
+
+    RT_ASSERT(rbb);
+    RT_ASSERT(block);
+    RT_ASSERT(block->status != RT_RBB_BLK_UNUSED);
+
+    level = rt_hw_interrupt_disable();
+
+    /* remove it on rbb block list */
+    rt_slist_remove(&rbb->blk_list, &block->list);
+
+    block->status = RT_RBB_BLK_UNUSED;
+
+    rt_hw_interrupt_enable(level);
+}
+RTM_EXPORT(rt_rbb_blk_free);
+
+/**
+ * get a continuous block to queue by given size
+ *
+ *          tail                         head
+ * +------------------+---------------+--------+----------+--------+
+ * |      block3      |  empty1       | block1 |  block2  |fragment|
+ * +------------------+------------------------+----------+--------+
+ *                                    |<-- return_size -->|    |
+ *                                    |<--- queue_data_len --->|
+ *
+ *         tail                          head
+ * +------------------+---------------+--------+----------+--------+
+ * |      block3      |  empty1       | block1 |  block2  |fragment|
+ * +------------------+------------------------+----------+--------+
+ *                                    |<-- return_size -->|              out of len(b1+b2+b3)    |
+ *                                    |<-------------------- queue_data_len -------------------->|
+ *
+ * @param rbb ring block buffer object
+ * @param queue_data_len The max queue data size, and the return size must less then it.
+ * @param queue continuous block queue
+ *
+ * @return the block queue data total size
+ */
+rt_size_t rt_rbb_blk_queue_get(rt_rbb_t rbb, rt_size_t queue_data_len, rt_rbb_blk_queue_t blk_queue)
+{
+    rt_base_t level;
+    rt_size_t data_total_size = 0;
+    rt_slist_t *node;
+    rt_rbb_blk_t last_block = NULL, block;
+
+    RT_ASSERT(rbb);
+    RT_ASSERT(blk_queue);
+
+    if (rt_slist_isempty(&rbb->blk_list))
+        return 0;
+
+    level = rt_hw_interrupt_disable();
+
+    for (node = rt_slist_first(&rbb->blk_list); node; node = rt_slist_next(node))
+    {
+        if (!last_block)
+        {
+            last_block = rt_slist_entry(node, struct rt_rbb_blk, list);
+            if (last_block->status == RT_RBB_BLK_PUT)
+            {
+                /* save the first put status block to queue */
+                blk_queue->blocks = last_block;
+                blk_queue->blk_num = 0;
+            }
+            else
+            {
+                /* the first block must be put status */
+                last_block = NULL;
+                continue;
+            }
+        }
+        else
+        {
+            block = rt_slist_entry(node, struct rt_rbb_blk, list);
+            /*
+             * these following conditions will break the loop:
+             * 1. the current block is not put status
+             * 2. the last block and current block is not continuous
+             * 3. the data_total_size will out of range
+             */
+            if (block->status != RT_RBB_BLK_PUT ||
+                last_block->buf > block->buf ||
+                data_total_size + block->size > queue_data_len)
+            {
+                break;
+            }
+            /* backup last block */
+            last_block = block;
+        }
+        /* remove current block */
+        rt_slist_remove(&rbb->blk_list, &last_block->list);
+        data_total_size += last_block->size;
+        last_block->status = RT_RBB_BLK_GET;
+        blk_queue->blk_num++;
+    }
+
+    rt_hw_interrupt_enable(level);
+
+    return data_total_size;
+}
+RTM_EXPORT(rt_rbb_blk_queue_get);
+
+/**
+ * get all block length on block queue
+ *
+ * @param blk_queue the block queue
+ *
+ * @return total length
+ */
+rt_size_t rt_rbb_blk_queue_len(rt_rbb_blk_queue_t blk_queue)
+{
+    rt_size_t i, data_total_size = 0;
+
+    RT_ASSERT(blk_queue);
+
+    for (i = 0; i < blk_queue->blk_num; i++)
+    {
+        data_total_size += blk_queue->blocks[i].size;
+    }
+
+    return data_total_size;
+}
+RTM_EXPORT(rt_rbb_blk_queue_len);
+
+/**
+ * return the block queue buffer
+ *
+ * @param blk_queue the block queue
+ *
+ * @return block queue buffer
+ */
+rt_uint8_t *rt_rbb_blk_queue_buf(rt_rbb_blk_queue_t blk_queue)
+{
+    RT_ASSERT(blk_queue);
+
+    return blk_queue->blocks[0].buf;
+}
+RTM_EXPORT(rt_rbb_blk_queue_buf);
+
+/**
+ * free the block queue
+ *
+ * @param rbb ring block buffer object
+ * @param blk_queue the block queue
+ */
+void rt_rbb_blk_queue_free(rt_rbb_t rbb, rt_rbb_blk_queue_t blk_queue)
+{
+    rt_size_t i;
+
+    RT_ASSERT(rbb);
+    RT_ASSERT(blk_queue);
+
+    for (i = 0; i < blk_queue->blk_num; i++)
+    {
+        rt_rbb_blk_free(rbb, &blk_queue->blocks[i]);
+    }
+}
+RTM_EXPORT(rt_rbb_blk_queue_free);
+
+/**
+ * The put status and buffer continuous blocks can be make a block queue.
+ * This function will return the length which from next can be make block queue.
+ *
+ * @param rbb ring block buffer object
+ *
+ * @return the next can be make block queue's length
+ */
+rt_size_t rt_rbb_next_blk_queue_len(rt_rbb_t rbb)
+{
+    rt_base_t level;
+    rt_size_t data_len = 0;
+    rt_slist_t *node;
+    rt_rbb_blk_t last_block = NULL, block;
+
+    RT_ASSERT(rbb);
+
+    if (rt_slist_isempty(&rbb->blk_list))
+        return 0;
+
+    level = rt_hw_interrupt_disable();
+
+    for (node = rt_slist_first(&rbb->blk_list); node; node = rt_slist_next(node))
+    {
+        if (!last_block)
+        {
+            last_block = rt_slist_entry(node, struct rt_rbb_blk, list);
+            if (last_block->status != RT_RBB_BLK_PUT)
+            {
+                /* the first block must be put status */
+                last_block = NULL;
+                continue;
+            }
+        }
+        else
+        {
+            block = rt_slist_entry(node, struct rt_rbb_blk, list);
+            /*
+             * these following conditions will break the loop:
+             * 1. the current block is not put status
+             * 2. the last block and current block is not continuous
+             */
+            if (block->status != RT_RBB_BLK_PUT || last_block->buf > block->buf)
+            {
+                break;
+            }
+            /* backup last block */
+            last_block = block;
+        }
+        data_len += last_block->size;
+    }
+
+    rt_hw_interrupt_enable(level);
+
+    return data_len;
+}
+RTM_EXPORT(rt_rbb_next_blk_queue_len);
+
+/**
+ * get the ring block buffer object buffer size
+ *
+ * @param rbb ring block buffer object
+ *
+ * @return buffer size
+ */
+rt_size_t rt_rbb_get_buf_size(rt_rbb_t rbb)
+{
+    RT_ASSERT(rbb);
+
+    return rbb->buf_size;
+}
+RTM_EXPORT(rt_rbb_get_buf_size);

+ 341 - 0
examples/test/rbb_test.c

@@ -0,0 +1,341 @@
+/*
+ * File      : rbb_test.c
+ * This file is part of RT-Thread RTOS
+ * COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2018-08-31     armink       the first version
+ */
+
+#include <string.h>
+#include <rtthread.h>
+#include <rtdevice.h>
+#include <stdlib.h>
+
+static rt_bool_t put_finish = RT_FALSE;
+
+static void put_thread(void *param)
+{
+    rt_rbb_t rbb = (rt_rbb_t)param;
+    rt_rbb_blk_t block;
+    rt_uint8_t put_count = 0;
+
+    put_finish = RT_FALSE;
+
+    while (put_count < 255)
+    {
+        if (put_count == 10)
+        {
+            put_count = 10;
+        }
+        block = rt_rbb_blk_alloc(rbb, rand() % 10 + 1);
+        if (block)
+        {
+            block->buf[0] = put_count++;
+            rt_rbb_blk_put(block);
+        }
+        rt_thread_mdelay(rand() % 10);
+    }
+    rt_kprintf("Put block data finish.\n");
+
+    put_finish = RT_TRUE;
+}
+
+static void get_thread(void *param)
+{
+    rt_rbb_t rbb = (rt_rbb_t)param;
+    rt_rbb_blk_t block;
+    rt_uint8_t get_count = 0;
+
+    while (get_count < 255)
+    {
+        if (get_count == 10)
+        {
+            get_count = 10;
+        }
+        block = rt_rbb_blk_get(rbb);
+        if (block)
+        {
+            if (block->buf[0] != get_count++)
+            {
+                rt_kprintf("Error: get data (times %d) has an error!\n", get_count);
+            }
+            rt_rbb_blk_free(rbb, block);
+        }
+        else if (put_finish)
+        {
+            break;
+        }
+        rt_thread_mdelay(rand() % 10);
+    }
+    rt_kprintf("Get block data finish.\n");
+    rt_kprintf("\n====================== rbb dynamic test finish =====================\n");
+}
+
+void rbb_test(void)
+{
+    rt_rbb_t rbb;
+    rt_rbb_blk_t blk1, blk2, blk3, blk4, blk5, blk6, _blk1, _blk2;
+    rt_size_t i, j, k, req_size, size;
+    struct rt_rbb_blk_queue blk_queue1;
+    rt_thread_t thread;
+
+    /* create ring block buffer */
+    rt_kprintf("\n====================== rbb create test =====================\n");
+    rbb = rt_rbb_create(52, 6);
+    if (rbb)
+    {
+        rt_kprintf("6 blocks in 52 bytes ring block buffer object create success.\n");
+    }
+    else
+    {
+        rt_kprintf("Test error: 6 blocks in 52 bytes ring block buffer object create failed.\n");
+    }
+    /* allocate block */
+    rt_kprintf("\n====================== rbb alloc test =====================\n");
+    blk1 = rt_rbb_blk_alloc(rbb, 2);
+    if (blk1 && blk1->size == 2)
+    {
+        memset(blk1->buf, 1, blk1->size);
+        rt_kprintf("Block1 (2 bytes) allocate success.\n");
+    }
+    else
+    {
+        rt_kprintf("Test error: block1 (2 bytes) allocate failed.\n");
+        goto __exit;
+    }
+    blk2 = rt_rbb_blk_alloc(rbb, 4);
+    if (blk2 && blk2->size == 4)
+    {
+        memset(blk2->buf, 2, blk2->size);
+        rt_kprintf("Block2 (4 bytes) allocate success.\n");
+    }
+    else
+    {
+        rt_kprintf("Test error: block2 (4 bytes) allocate failed.\n");
+        goto __exit;
+    }
+    blk3 = rt_rbb_blk_alloc(rbb, 8);
+    if (blk3 && blk3->size == 8)
+    {
+        memset(blk3->buf, 3, blk3->size);
+        rt_kprintf("Block3 (8 bytes) allocate success.\n");
+    }
+    else
+    {
+        rt_kprintf("Test error: block3 (8 bytes) allocate failed.\n");
+        goto __exit;
+    }
+    blk4 = rt_rbb_blk_alloc(rbb, 16);
+    if (blk4 && blk4->size == 16)
+    {
+        memset(blk4->buf, 4, blk4->size);
+        rt_kprintf("Block4 (16 bytes) allocate success.\n");
+    }
+    else
+    {
+        rt_kprintf("Test error: block4 (16 bytes) allocate failed.\n");
+        goto __exit;
+    }
+    blk5 = rt_rbb_blk_alloc(rbb, 32);
+    if (blk5 && blk5->size == 32)
+    {
+        memset(blk5->buf, 5, blk5->size);
+        rt_kprintf("Block5 (32 bytes) allocate success.\n");
+    }
+    else
+    {
+        rt_kprintf("Block5 (32 bytes) allocate failed.\n");
+    }
+    blk5 = rt_rbb_blk_alloc(rbb, 18);
+    if (blk5 && blk5->size == 18)
+    {
+        memset(blk5->buf, 5, blk5->size);
+        rt_kprintf("Block5 (18 bytes) allocate success.\n");
+    }
+    else
+    {
+        rt_kprintf("Test error: block5 (18 bytes) allocate failed.\n");
+        goto __exit;
+    }
+    rt_kprintf("Ring block buffer current status:\n");
+    rt_kprintf("next block queue length: %d\n", rt_rbb_next_blk_queue_len(rbb));
+    rt_kprintf("block list length: %d\n", rt_slist_len(&rbb->blk_list));
+    rt_kprintf("|<- 2 -->|<-- 4 -->|<---- 8 ----->|<------- 16 -------->|<------ 18 ------>|<---- 4 ---->|\n");
+    rt_kprintf("+--------+---------+--------------+---------------------+------------------+-------------+\n");
+    rt_kprintf("| blcok1 | block2  |    block3    |       block4        |       block5     |    empty    |\n");
+    rt_kprintf("+--------+---------+--------------+---------------------+------------------+-------------+\n");
+    rt_kprintf("| inited | inited  |    inited    |       inited        |       inited     |             |\n");
+
+    /* put block */
+    rt_kprintf("\n====================== rbb put test =====================\n");
+    rt_rbb_blk_put(blk1);
+    rt_rbb_blk_put(blk2);
+    rt_rbb_blk_put(blk3);
+    rt_rbb_blk_put(blk4);
+    rt_rbb_blk_put(blk5);
+    rt_kprintf("Block1 to block5 put success.\n");
+    rt_kprintf("Ring block buffer current status:\n");
+    rt_kprintf("next block queue length: %d\n", rt_rbb_next_blk_queue_len(rbb));
+    rt_kprintf("block list length: %d\n", rt_slist_len(&rbb->blk_list));
+    rt_kprintf("|<- 2 -->|<-- 4 -->|<---- 8 ----->|<------- 16 -------->|<------ 18 ------>|<---- 4 ---->|\n");
+    rt_kprintf("+--------+---------+--------------+---------------------+------------------+-------------+\n");
+    rt_kprintf("| blcok1 | block2  |    block3    |       block4        |       block5     |    empty    |\n");
+    rt_kprintf("+--------+---------+--------------+---------------------+------------------+-------------+\n");
+    rt_kprintf("|  put   |  put    |     put      |        put          |        put       |             |\n");
+
+    /* get block */
+    rt_kprintf("\n====================== rbb get test =====================\n");
+    _blk1 = rt_rbb_blk_get(rbb);
+    _blk2 = rt_rbb_blk_get(rbb);
+    for (i = 0; i < _blk1->size; i++)
+    {
+        if (_blk1->buf[i] != 1) break;
+    }
+    for (j = 0; j < _blk2->size; j++)
+    {
+        if (_blk2->buf[j] != 2) break;
+    }
+    if (blk1 == _blk1 && blk2 == _blk2 && i == _blk1->size && j == _blk2->size)
+    {
+        rt_kprintf("Block1 and block2 get success.\n");
+    }
+    else
+    {
+        rt_kprintf("Test error: block1 and block2 get failed.\n");
+        goto __exit;
+    }
+    rt_kprintf("Ring block buffer current status:\n");
+    rt_kprintf("next block queue length: %d\n", rt_rbb_next_blk_queue_len(rbb));
+    rt_kprintf("block list length: %d\n", rt_slist_len(&rbb->blk_list));
+    rt_kprintf("|<- 2 -->|<-- 4 -->|<---- 8 ----->|<------- 16 -------->|<------ 18 ------>|<---- 4 ---->|\n");
+    rt_kprintf("+--------+---------+--------------+---------------------+------------------+-------------+\n");
+    rt_kprintf("| blcok1 | block2  |    block3    |       block4        |       block5     |    empty    |\n");
+    rt_kprintf("+--------+---------+--------------+---------------------+------------------+-------------+\n");
+    rt_kprintf("|  get   |   get   |     put      |        put          |        put       |             |\n");
+
+    /* free block */
+    rt_kprintf("\n====================== rbb free test =====================\n");
+    rt_rbb_blk_free(rbb, blk2);
+    rt_kprintf("Block2 free success.\n");
+    rt_rbb_blk_free(rbb, blk1);
+    rt_kprintf("Block1 free success.\n");
+    rt_kprintf("Ring block buffer current status:\n");
+    rt_kprintf("next block queue length: %d\n", rt_rbb_next_blk_queue_len(rbb));
+    rt_kprintf("block list length: %d\n", rt_slist_len(&rbb->blk_list));
+    rt_kprintf("|<------- 6 ------>|<---- 8 ----->|<------- 16 -------->|<------ 18 ------>|<---- 4 ---->|\n");
+    rt_kprintf("+------------------+--------------+---------------------+------------------+-------------+\n");
+    rt_kprintf("|      empty2      |    block3    |       block4        |       block5     |    empty1   |\n");
+    rt_kprintf("+------------------+--------------+---------------------+------------------+-------------+\n");
+    rt_kprintf("|                  |     put      |        put          |        put       |             |\n");
+
+    blk6 = rt_rbb_blk_alloc(rbb, 5);
+    if (blk6)
+    {
+        rt_kprintf("Block6 (5 bytes) allocate success.\n");
+    }
+    else
+    {
+        rt_kprintf("Test error: block6 (5 bytes) allocate failed.\n");
+        goto __exit;
+    }
+
+    rt_rbb_blk_put(blk6);
+    rt_kprintf("Block6 put success.\n");
+    rt_kprintf("Ring block buffer current status:\n");
+    rt_kprintf("next block queue length: %d\n", rt_rbb_next_blk_queue_len(rbb));
+    rt_kprintf("block list length: %d\n", rt_slist_len(&rbb->blk_list));
+    rt_kprintf("|<--- 5 ---->|< 1 >|<---- 8 ----->|<------- 16 -------->|<------ 18 ------>|<---- 4 ---->|\n");
+    rt_kprintf("+------------+-----+--------------+---------------------+------------------+-------------+\n");
+    rt_kprintf("|   block6   |empty|    block3    |       block4        |       block5     |   fragment  |\n");
+    rt_kprintf("+------------+-----+--------------+---------------------+------------------+-------------+\n");
+    rt_kprintf("|     put    |     |     put      |        put          |        put       |             |\n");
+
+    /* get block queue */
+    rt_kprintf("\n====================== rbb block queue get test =====================\n");
+    req_size = rt_rbb_next_blk_queue_len(rbb) + 5;
+    size = rt_rbb_blk_queue_get(rbb, req_size, &blk_queue1);
+    i = j = k = 0;
+    for (; i < blk3->size; i++)
+    {
+        if (rt_rbb_blk_queue_buf(&blk_queue1)[i] != 3) break;
+    }
+    for (; j < blk4->size; j++)
+    {
+        if (rt_rbb_blk_queue_buf(&blk_queue1)[i + j] != 4) break;
+    }
+    for (; k < blk5->size; k++)
+    {
+        if (rt_rbb_blk_queue_buf(&blk_queue1)[i + j + k] != 5) break;
+    }
+    if (size && size == 42 && rt_rbb_blk_queue_len(&blk_queue1) == 42 && k == blk5->size)
+    {
+        rt_kprintf("Block queue (request %d bytes, actual %d) get success.\n", req_size, size);
+    }
+    else
+    {
+        rt_kprintf("Test error: Block queue (request %d bytes, actual %d) get failed.\n", req_size, size);
+        goto __exit;
+    }
+    rt_kprintf("Ring block buffer current status:\n");
+    rt_kprintf("next block queue length: %d\n", rt_rbb_next_blk_queue_len(rbb));
+    rt_kprintf("block list length: %d\n", rt_slist_len(&rbb->blk_list));
+    rt_kprintf("|            |     |<----- block queue1 (42 bytes continuous buffer) ----->|             |\n");
+    rt_kprintf("|<--- 5 ---->|< 1 >|<---- 8 ----->|<------- 16 -------->|<------ 18 ------>|<---- 4 ---->|\n");
+    rt_kprintf("+------------+-----+--------------+---------------------+------------------+-------------+\n");
+    rt_kprintf("|   block6   |empty|    block3    |       block4        |       block5     |   fragment  |\n");
+    rt_kprintf("+------------+-----+--------------+---------------------+------------------+-------------+\n");
+    rt_kprintf("|     put    |     |     get      |        get          |        get       |             |\n");
+
+    /* free block queue */
+    rt_kprintf("\n====================== rbb block queue free test =====================\n");
+    rt_rbb_blk_queue_free(rbb, &blk_queue1);
+    rt_kprintf("Block queue1 free success.\n");
+    rt_kprintf("Ring block buffer current status:\n");
+    rt_kprintf("next block queue length: %d\n", rt_rbb_next_blk_queue_len(rbb));
+    rt_kprintf("block list length: %d\n", rt_slist_len(&rbb->blk_list));
+    rt_kprintf("|<--- 5 ---->|<--------------------------------- 47 ------------------------------------>|\n");
+    rt_kprintf("+------------+---------------------------------------------------------------------------+\n");
+    rt_kprintf("|   block6   |                                 empty                                     |\n");
+    rt_kprintf("+------------+---------------------------------------------------------------------------+\n");
+    rt_kprintf("|     put    |                                                                           |\n");
+    rt_rbb_blk_free(rbb, blk6);
+
+    rt_kprintf("\n====================== rbb static test SUCCESS =====================\n");
+
+    rt_kprintf("\n====================== rbb dynamic test =====================\n");
+
+    thread = rt_thread_create("rbb_put", put_thread, rbb, 1024, 10, 25);
+    if (thread)
+    {
+        rt_thread_startup(thread);
+    }
+
+    thread = rt_thread_create("rbb_get", get_thread, rbb, 1024, 10, 25);
+    if (thread)
+    {
+        rt_thread_startup(thread);
+    }
+
+__exit :
+
+    rt_rbb_destroy(rbb);
+}
+
+MSH_CMD_EXPORT(rbb_test, run ring block buffer testcase)
+