From dff8a2c513386e58bc0810d4351436a35dcde7bc Mon Sep 17 00:00:00 2001 From: armink Date: Tue, 11 Sep 2018 19:56:38 +0800 Subject: [PATCH] [DeviceDriver] Add rbb (ring block buffer) data structure. --- components/drivers/include/ipc/ringblk_buf.h | 115 ++++ components/drivers/include/rtdevice.h | 1 + components/drivers/src/ringblk_buf.c | 534 +++++++++++++++++++ examples/test/rbb_test.c | 341 ++++++++++++ 4 files changed, 991 insertions(+) create mode 100644 components/drivers/include/ipc/ringblk_buf.h create mode 100644 components/drivers/src/ringblk_buf.c create mode 100644 examples/test/rbb_test.c diff --git a/components/drivers/include/ipc/ringblk_buf.h b/components/drivers/include/ipc/ringblk_buf.h new file mode 100644 index 0000000000..26ffbdbe21 --- /dev/null +++ b/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_ */ diff --git a/components/drivers/include/rtdevice.h b/components/drivers/include/rtdevice.h index e3c826fe53..eb28714891 100644 --- a/components/drivers/include/rtdevice.h +++ b/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" { diff --git a/components/drivers/src/ringblk_buf.c b/components/drivers/src/ringblk_buf.c new file mode 100644 index 0000000000..1212b088d8 --- /dev/null +++ b/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 +#include +#include + +/** + * 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); diff --git a/examples/test/rbb_test.c b/examples/test/rbb_test.c new file mode 100644 index 0000000000..f276c993da --- /dev/null +++ b/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 +#include +#include +#include + +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) +