/* * Copyright (c) 2006-2018, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * 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);