ringblk_buf.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536
  1. #include <FreeRTOS.h>
  2. #include "list.h"
  3. #include "ringblk_buf.h"
  4. /**
  5. * ring block buffer object initialization
  6. *
  7. * @param rbb ring block buffer object
  8. * @param buf buffer
  9. * @param buf_size buffer size
  10. * @param block_set block set
  11. * @param blk_max_num max block number
  12. *
  13. * @note When your application need align access, please make the buffer address is aligned.
  14. */
  15. void rbb_init(rbb_t rbb, uint8_t *buf, size_t buf_size, rbb_blk_t block_set, size_t blk_max_num)
  16. {
  17. size_t i;
  18. configASSERT(rbb);
  19. configASSERT(buf);
  20. configASSERT(block_set);
  21. rbb->buf = buf;
  22. rbb->buf_size = buf_size;
  23. rbb->blk_set = block_set;
  24. rbb->blk_max_num = blk_max_num;
  25. vListInitialise(&rbb->blk_list);
  26. /* initialize block status */
  27. for (i = 0; i < blk_max_num; i++)
  28. {
  29. block_set[i].status = RBB_BLK_UNUSED;
  30. }
  31. }
  32. /**
  33. * ring block buffer object create
  34. *
  35. * @param buf_size buffer size
  36. * @param blk_max_num max block number
  37. *
  38. * @return != NULL: ring block buffer object
  39. * NULL: create failed
  40. */
  41. rbb_t rbb_create(size_t buf_size, size_t blk_max_num)
  42. {
  43. rbb_t rbb = NULL;
  44. uint8_t *buf;
  45. rbb_blk_t blk_set;
  46. rbb = (rbb_t)pvPortMalloc(sizeof(struct rbb));
  47. if (!rbb)
  48. {
  49. return NULL;
  50. }
  51. buf = (uint8_t *)pvPortMalloc(buf_size);
  52. if (!buf)
  53. {
  54. vPortFree(rbb);
  55. return NULL;
  56. }
  57. blk_set = (rbb_blk_t)pvPortMalloc(sizeof(struct rbb_blk) * blk_max_num);
  58. if (!blk_set)
  59. {
  60. vPortFree(buf);
  61. vPortFree(rbb);
  62. return NULL;
  63. }
  64. rbb_init(rbb, buf, buf_size, blk_set, blk_max_num);
  65. return rbb;
  66. }
  67. /**
  68. * ring block buffer object destroy
  69. *
  70. * @param rbb ring block buffer object
  71. */
  72. void rbb_destroy(rbb_t rbb)
  73. {
  74. configASSERT(rbb);
  75. vPortFree(rbb->buf);
  76. vPortFree(rbb->blk_set);
  77. vPortFree(rbb);
  78. }
  79. static rbb_blk_t find_empty_blk_in_set(rbb_t rbb)
  80. {
  81. size_t i;
  82. configASSERT(rbb);
  83. for (i = 0; i < rbb->blk_max_num; i ++)
  84. {
  85. if (rbb->blk_set[i].status == RBB_BLK_UNUSED)
  86. {
  87. return &rbb->blk_set[i];
  88. }
  89. }
  90. return NULL;
  91. }
  92. /**
  93. * Allocate a block by given size. The block will add to blk_list when allocate success.
  94. *
  95. * @param rbb ring block buffer object
  96. * @param blk_size block size
  97. *
  98. * @note When your application need align access, please make the blk_szie is aligned.
  99. *
  100. * @return != NULL: allocated block
  101. * NULL: allocate failed
  102. */
  103. rbb_blk_t rbb_blk_alloc(rbb_t rbb, size_t blk_size)
  104. {
  105. UBaseType_t uxSavedInterruptStatus;
  106. size_t empty1 = 0, empty2 = 0;
  107. rbb_blk_t head, tail, new_rbb = NULL;
  108. configASSERT(rbb);
  109. configASSERT(blk_size < (1L << 24));
  110. uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
  111. new_rbb = find_empty_blk_in_set(rbb);
  112. if (listCURRENT_LIST_LENGTH(&rbb->blk_list) < rbb->blk_max_num && new_rbb)
  113. {
  114. if (listCURRENT_LIST_LENGTH(&rbb->blk_list) > 0)
  115. {
  116. head = listGET_OWNER_OF_HEAD_ENTRY(&rbb->blk_list);
  117. tail = listGET_LIST_ITEM_OWNER(rbb->blk_list.xListEnd.pxPrevious);
  118. if (head->buf <= tail->buf)
  119. {
  120. /**
  121. * head tail
  122. * +--------------------------------------+-----------------+------------------+
  123. * | empty2 | block1 | block2 | block3 | empty1 |
  124. * +--------------------------------------+-----------------+------------------+
  125. * rbb->buf
  126. */
  127. empty1 = (rbb->buf + rbb->buf_size) - (tail->buf + tail->size);
  128. empty2 = head->buf - rbb->buf;
  129. if (empty1 >= blk_size)
  130. {
  131. listSET_LIST_ITEM_OWNER(&new_rbb->list, new_rbb);
  132. vListInsertEnd(&rbb->blk_list, &new_rbb->list);
  133. new_rbb->status = RBB_BLK_INITED;
  134. new_rbb->buf = tail->buf + tail->size;
  135. new_rbb->size = blk_size;
  136. }
  137. else if (empty2 >= blk_size)
  138. {
  139. listSET_LIST_ITEM_OWNER(&new_rbb->list, new_rbb);
  140. vListInsertEnd(&rbb->blk_list, &new_rbb->list);
  141. new_rbb->status = RBB_BLK_INITED;
  142. new_rbb->buf = rbb->buf;
  143. new_rbb->size = blk_size;
  144. }
  145. else
  146. {
  147. /* no space */
  148. new_rbb = NULL;
  149. }
  150. }
  151. else
  152. {
  153. /**
  154. * tail head
  155. * +----------------+-------------------------------------+--------+-----------+
  156. * | block3 | empty1 | block1 | block2 |
  157. * +----------------+-------------------------------------+--------+-----------+
  158. * rbb->buf
  159. */
  160. empty1 = head->buf - (tail->buf + tail->size);
  161. if (empty1 >= blk_size)
  162. {
  163. listSET_LIST_ITEM_OWNER(&new_rbb->list, new_rbb);
  164. vListInsertEnd(&rbb->blk_list, &new_rbb->list);
  165. new_rbb->status = RBB_BLK_INITED;
  166. new_rbb->buf = tail->buf + tail->size;
  167. new_rbb->size = blk_size;
  168. }
  169. else
  170. {
  171. /* no space */
  172. new_rbb = NULL;
  173. }
  174. }
  175. }
  176. else
  177. {
  178. /* the list is empty */
  179. listSET_LIST_ITEM_OWNER(&new_rbb->list, new_rbb);
  180. vListInsertEnd(&rbb->blk_list, &new_rbb->list);
  181. new_rbb->status = RBB_BLK_INITED;
  182. new_rbb->buf = rbb->buf;
  183. new_rbb->size = blk_size;
  184. }
  185. }
  186. else
  187. {
  188. new_rbb = NULL;
  189. }
  190. portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus);
  191. return new_rbb;
  192. }
  193. /**
  194. * put a block to ring block buffer object
  195. *
  196. * @param block the block
  197. */
  198. void rbb_blk_put(rbb_blk_t block)
  199. {
  200. configASSERT(block);
  201. configASSERT(block->status == RBB_BLK_INITED);
  202. block->status = RBB_BLK_PUT;
  203. }
  204. /**
  205. * get a block from the ring block buffer object
  206. *
  207. * @param rbb ring block buffer object
  208. *
  209. * @return != NULL: block
  210. * NULL: get failed
  211. */
  212. rbb_blk_t rbb_blk_get(rbb_t rbb)
  213. {
  214. UBaseType_t uxSavedInterruptStatus;
  215. rbb_blk_t block = NULL;
  216. ListItem_t *node;
  217. configASSERT(rbb);
  218. if (listLIST_IS_EMPTY(&rbb->blk_list))
  219. return 0;
  220. uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
  221. for (node = listGET_HEAD_ENTRY(&rbb->blk_list); node != listGET_END_MARKER(&rbb->blk_list);
  222. node = listGET_NEXT(node))
  223. {
  224. block = listGET_LIST_ITEM_OWNER(node);
  225. if (block->status == RBB_BLK_PUT)
  226. {
  227. block->status = RBB_BLK_GET;
  228. goto __exit;
  229. }
  230. }
  231. /* not found */
  232. block = NULL;
  233. __exit:
  234. portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus);
  235. return block;
  236. }
  237. /**
  238. * return the block size
  239. *
  240. * @param block the block
  241. *
  242. * @return block size
  243. */
  244. size_t rbb_blk_size(rbb_blk_t block)
  245. {
  246. configASSERT(block);
  247. return block->size;
  248. }
  249. /**
  250. * return the block buffer
  251. *
  252. * @param block the block
  253. *
  254. * @return block buffer
  255. */
  256. uint8_t *rbb_blk_buf(rbb_blk_t block)
  257. {
  258. configASSERT(block);
  259. return block->buf;
  260. }
  261. /**
  262. * free the block
  263. *
  264. * @param rbb ring block buffer object
  265. * @param block the block
  266. */
  267. void rbb_blk_free(rbb_t rbb, rbb_blk_t block)
  268. {
  269. UBaseType_t uxSavedInterruptStatus;
  270. configASSERT(rbb);
  271. configASSERT(block);
  272. configASSERT(block->status != RBB_BLK_UNUSED);
  273. uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
  274. /* remove it on rbb block list */
  275. uxListRemove(&block->list);
  276. block->status = RBB_BLK_UNUSED;
  277. portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus);
  278. }
  279. /**
  280. * get a continuous block to queue by given size
  281. *
  282. * tail head
  283. * +------------------+---------------+--------+----------+--------+
  284. * | block3 | empty1 | block1 | block2 |fragment|
  285. * +------------------+------------------------+----------+--------+
  286. * |<-- return_size -->| |
  287. * |<--- queue_data_len --->|
  288. *
  289. * tail head
  290. * +------------------+---------------+--------+----------+--------+
  291. * | block3 | empty1 | block1 | block2 |fragment|
  292. * +------------------+------------------------+----------+--------+
  293. * |<-- return_size -->| out of len(b1+b2+b3) |
  294. * |<-------------------- queue_data_len -------------------->|
  295. *
  296. * @param rbb ring block buffer object
  297. * @param queue_data_len The max queue data size, and the return size must less then it.
  298. * @param queue continuous block queue
  299. *
  300. * @return the block queue data total size
  301. */
  302. size_t rbb_blk_queue_get(rbb_t rbb, size_t queue_data_len, rbb_blk_queue_t blk_queue)
  303. {
  304. UBaseType_t uxSavedInterruptStatus;
  305. size_t data_total_size = 0;
  306. ListItem_t *node;
  307. rbb_blk_t last_block = NULL, block;
  308. configASSERT(rbb);
  309. configASSERT(blk_queue);
  310. if (listLIST_IS_EMPTY(&rbb->blk_list))
  311. return 0;
  312. uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
  313. for (node = listGET_HEAD_ENTRY(&rbb->blk_list); node != listGET_END_MARKER(&rbb->blk_list);
  314. node = listGET_NEXT(node))
  315. {
  316. if (!last_block)
  317. {
  318. last_block = listGET_LIST_ITEM_OWNER(node);
  319. if (last_block->status == RBB_BLK_PUT)
  320. {
  321. /* save the first put status block to queue */
  322. blk_queue->blocks = last_block;
  323. blk_queue->blk_num = 0;
  324. }
  325. else
  326. {
  327. /* the first block must be put status */
  328. last_block = NULL;
  329. continue;
  330. }
  331. }
  332. else
  333. {
  334. block = listGET_LIST_ITEM_OWNER(node);
  335. /*
  336. * these following conditions will break the loop:
  337. * 1. the current block is not put status
  338. * 2. the last block and current block is not continuous
  339. * 3. the data_total_size will out of range
  340. */
  341. if (block->status != RBB_BLK_PUT ||
  342. last_block->buf > block->buf ||
  343. data_total_size + block->size > queue_data_len)
  344. {
  345. break;
  346. }
  347. /* backup last block */
  348. last_block = block;
  349. }
  350. /* remove current block */
  351. uxListRemove(&last_block->list);
  352. data_total_size += last_block->size;
  353. last_block->status = RBB_BLK_GET;
  354. blk_queue->blk_num++;
  355. }
  356. portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus);
  357. return data_total_size;
  358. }
  359. /**
  360. * get all block length on block queue
  361. *
  362. * @param blk_queue the block queue
  363. *
  364. * @return total length
  365. */
  366. size_t rbb_blk_queue_len(rbb_blk_queue_t blk_queue)
  367. {
  368. size_t i, data_total_size = 0;
  369. configASSERT(blk_queue);
  370. for (i = 0; i < blk_queue->blk_num; i++)
  371. {
  372. data_total_size += blk_queue->blocks[i].size;
  373. }
  374. return data_total_size;
  375. }
  376. /**
  377. * return the block queue buffer
  378. *
  379. * @param blk_queue the block queue
  380. *
  381. * @return block queue buffer
  382. */
  383. uint8_t *rbb_blk_queue_buf(rbb_blk_queue_t blk_queue)
  384. {
  385. configASSERT(blk_queue);
  386. return blk_queue->blocks[0].buf;
  387. }
  388. /**
  389. * free the block queue
  390. *
  391. * @param rbb ring block buffer object
  392. * @param blk_queue the block queue
  393. */
  394. void rbb_blk_queue_free(rbb_t rbb, rbb_blk_queue_t blk_queue)
  395. {
  396. size_t i;
  397. configASSERT(rbb);
  398. configASSERT(blk_queue);
  399. for (i = 0; i < blk_queue->blk_num; i++)
  400. {
  401. rbb_blk_free(rbb, &blk_queue->blocks[i]);
  402. }
  403. }
  404. /**
  405. * The put status and buffer continuous blocks can be make a block queue.
  406. * This function will return the length which from next can be make block queue.
  407. *
  408. * @param rbb ring block buffer object
  409. *
  410. * @return the next can be make block queue's length
  411. */
  412. size_t rbb_next_blk_queue_len(rbb_t rbb)
  413. {
  414. UBaseType_t uxSavedInterruptStatus;
  415. size_t data_len = 0;
  416. ListItem_t *node;
  417. rbb_blk_t last_block = NULL, block;
  418. configASSERT(rbb);
  419. if (listLIST_IS_EMPTY(&rbb->blk_list))
  420. return 0;
  421. uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
  422. for (node = listGET_HEAD_ENTRY(&rbb->blk_list); node != listGET_END_MARKER(&rbb->blk_list);
  423. node = listGET_NEXT(node))
  424. {
  425. if (!last_block)
  426. {
  427. last_block = listGET_LIST_ITEM_OWNER(node);
  428. if (last_block->status != RBB_BLK_PUT)
  429. {
  430. /* the first block must be put status */
  431. last_block = NULL;
  432. continue;
  433. }
  434. }
  435. else
  436. {
  437. block = listGET_LIST_ITEM_OWNER(node);
  438. /*
  439. * these following conditions will break the loop:
  440. * 1. the current block is not put status
  441. * 2. the last block and current block is not continuous
  442. */
  443. if (block->status != RBB_BLK_PUT || last_block->buf > block->buf)
  444. {
  445. break;
  446. }
  447. /* backup last block */
  448. last_block = block;
  449. }
  450. data_len += last_block->size;
  451. }
  452. portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus);
  453. return data_len;
  454. }
  455. /**
  456. * get the ring block buffer object buffer size
  457. *
  458. * @param rbb ring block buffer object
  459. *
  460. * @return buffer size
  461. */
  462. size_t rbb_get_buf_size(rbb_t rbb)
  463. {
  464. configASSERT(rbb);
  465. return rbb->buf_size;
  466. }