diff options
| author | armink <[email protected]> | 2018-09-11 19:56:38 +0800 |
|---|---|---|
| committer | armink <[email protected]> | 2018-09-12 08:59:58 +0800 |
| commit | dff8a2c513386e58bc0810d4351436a35dcde7bc (patch) | |
| tree | 5b2133f5a1fe6f0fd3389568a729cc85c1b01a81 /components/drivers/src | |
| parent | 7d3f38e4f6c05df90323a1a392463541fd52d629 (diff) | |
[DeviceDriver] Add rbb (ring block buffer) data structure.
Diffstat (limited to 'components/drivers/src')
| -rw-r--r-- | components/drivers/src/ringblk_buf.c | 534 |
1 files changed, 534 insertions, 0 deletions
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 <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); |
