ringblk_buf.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534
  1. /*
  2. * File : ringblk_buf.c
  3. * This file is part of RT-Thread RTOS
  4. * COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation; either version 2 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License along
  17. * with this program; if not, write to the Free Software Foundation, Inc.,
  18. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  19. *
  20. * Change Logs:
  21. * Date Author Notes
  22. * 2018-08-25 armink the first version
  23. */
  24. #include <rthw.h>
  25. #include <rtthread.h>
  26. #include <rtdevice.h>
  27. /**
  28. * ring block buffer object initialization
  29. *
  30. * @param rbb ring block buffer object
  31. * @param buf buffer
  32. * @param buf_size buffer size
  33. * @param block_set
  34. * @param blk_max_num
  35. */
  36. 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)
  37. {
  38. rt_size_t i;
  39. RT_ASSERT(rbb);
  40. RT_ASSERT(buf);
  41. RT_ASSERT(block_set);
  42. rbb->buf = buf;
  43. rbb->buf_size = buf_size;
  44. rbb->blk_set = block_set;
  45. rbb->blk_max_num = blk_max_num;
  46. rt_slist_init(&rbb->blk_list);
  47. /* initialize block status */
  48. for (i = 0; i < blk_max_num; i++)
  49. {
  50. block_set[i].status = RT_RBB_BLK_UNUSED;
  51. }
  52. }
  53. RTM_EXPORT(rt_rbb_init);
  54. /**
  55. * ring block buffer object create
  56. *
  57. * @param buf_size buffer size
  58. * @param blk_max_num max block number
  59. *
  60. * @return != NULL: ring block buffer object
  61. * NULL: create failed
  62. */
  63. rt_rbb_t rt_rbb_create(rt_size_t buf_size, rt_size_t blk_max_num)
  64. {
  65. rt_rbb_t rbb = NULL;
  66. rt_uint8_t *buf;
  67. rt_rbb_blk_t blk_set;
  68. rbb = (rt_rbb_t)rt_malloc(sizeof(struct rt_rbb));
  69. if (!rbb)
  70. {
  71. return NULL;
  72. }
  73. buf = (rt_uint8_t *)rt_malloc(buf_size);
  74. if (!buf)
  75. {
  76. rt_free(rbb);
  77. return NULL;
  78. }
  79. blk_set = (rt_rbb_blk_t)rt_malloc(sizeof(struct rt_rbb_blk) * blk_max_num);
  80. if (!blk_set)
  81. {
  82. rt_free(buf);
  83. rt_free(rbb);
  84. return NULL;
  85. }
  86. rt_rbb_init(rbb, buf, buf_size, blk_set, blk_max_num);
  87. return rbb;
  88. }
  89. RTM_EXPORT(rt_rbb_create);
  90. /**
  91. * ring block buffer object destroy
  92. *
  93. * @param rbb ring block buffer object
  94. */
  95. void rt_rbb_destroy(rt_rbb_t rbb)
  96. {
  97. RT_ASSERT(rbb);
  98. rt_free(rbb);
  99. rt_free(rbb->buf);
  100. rt_free(rbb->blk_set);
  101. }
  102. RTM_EXPORT(rt_rbb_destroy);
  103. static rt_rbb_blk_t find_empty_blk_in_set(rt_rbb_t rbb)
  104. {
  105. rt_size_t i;
  106. RT_ASSERT(rbb);
  107. for (i = 0; i < rbb->blk_max_num; i ++)
  108. {
  109. if (rbb->blk_set[i].status == RT_RBB_BLK_UNUSED)
  110. {
  111. return &rbb->blk_set[i];
  112. }
  113. }
  114. return NULL;
  115. }
  116. /**
  117. * Allocate a block by given size. The block will add to blk_list when allocate success.
  118. *
  119. * @param rbb ring block buffer object
  120. * @param blk_size block size
  121. *
  122. * @return != NULL: allocated block
  123. * NULL: allocate failed
  124. */
  125. rt_rbb_blk_t rt_rbb_blk_alloc(rt_rbb_t rbb, rt_size_t blk_size)
  126. {
  127. rt_base_t level;
  128. rt_size_t empty1 = 0, empty2 = 0;
  129. rt_rbb_blk_t head, tail, new = NULL;
  130. RT_ASSERT(rbb);
  131. RT_ASSERT(blk_size < 1L << 24);
  132. level = rt_hw_interrupt_disable();
  133. new = find_empty_blk_in_set(rbb);
  134. if (rt_slist_len(&rbb->blk_list) < rbb->blk_max_num && new)
  135. {
  136. if (rt_slist_len(&rbb->blk_list) > 0)
  137. {
  138. head = rt_slist_first_entry(&rbb->blk_list, struct rt_rbb_blk, list);
  139. tail = rt_slist_tail_entry(&rbb->blk_list, struct rt_rbb_blk, list);
  140. if (head->buf <= tail->buf)
  141. {
  142. /**
  143. * head tail
  144. * +--------------------------------------+-----------------+------------------+
  145. * | empty2 | block1 | block2 | block3 | empty1 |
  146. * +--------------------------------------+-----------------+------------------+
  147. * rbb->buf
  148. */
  149. empty1 = (rbb->buf + rbb->buf_size) - (tail->buf + tail->size);
  150. empty2 = head->buf - rbb->buf;
  151. if (empty1 >= blk_size)
  152. {
  153. rt_slist_append(&rbb->blk_list, &new->list);
  154. new->status = RT_RBB_BLK_INITED;
  155. new->buf = tail->buf + tail->size;
  156. new->size = blk_size;
  157. }
  158. else if (empty2 >= blk_size)
  159. {
  160. rt_slist_append(&rbb->blk_list, &new->list);
  161. new->status = RT_RBB_BLK_INITED;
  162. new->buf = rbb->buf;
  163. new->size = blk_size;
  164. }
  165. else
  166. {
  167. /* no space */
  168. new = NULL;
  169. }
  170. }
  171. else
  172. {
  173. /**
  174. * tail head
  175. * +----------------+-------------------------------------+--------+-----------+
  176. * | block3 | empty1 | block1 | block2 |
  177. * +----------------+-------------------------------------+--------+-----------+
  178. * rbb->buf
  179. */
  180. empty1 = head->buf - (tail->buf + tail->size);
  181. if (empty1 >= blk_size)
  182. {
  183. rt_slist_append(&rbb->blk_list, &new->list);
  184. new->status = RT_RBB_BLK_INITED;
  185. new->buf = tail->buf + tail->size;
  186. new->size = blk_size;
  187. }
  188. else
  189. {
  190. /* no space */
  191. new = NULL;
  192. }
  193. }
  194. }
  195. else
  196. {
  197. /* the list is empty */
  198. rt_slist_append(&rbb->blk_list, &new->list);
  199. new->status = RT_RBB_BLK_INITED;
  200. new->buf = rbb->buf;
  201. new->size = blk_size;
  202. }
  203. }
  204. else
  205. {
  206. new = NULL;
  207. }
  208. rt_hw_interrupt_enable(level);
  209. return new;
  210. }
  211. RTM_EXPORT(rt_rbb_blk_alloc);
  212. /**
  213. * put a block to ring block buffer object
  214. *
  215. * @param block the block
  216. */
  217. void rt_rbb_blk_put(rt_rbb_blk_t block)
  218. {
  219. RT_ASSERT(block);
  220. RT_ASSERT(block->status == RT_RBB_BLK_INITED);
  221. block->status = RT_RBB_BLK_PUT;
  222. }
  223. RTM_EXPORT(rt_rbb_blk_put);
  224. /**
  225. * get a block from the ring block buffer object
  226. *
  227. * @param rbb ring block buffer object
  228. *
  229. * @return != NULL: block
  230. * NULL: get failed
  231. */
  232. rt_rbb_blk_t rt_rbb_blk_get(rt_rbb_t rbb)
  233. {
  234. rt_base_t level;
  235. rt_rbb_blk_t block = NULL;
  236. rt_slist_t *node;
  237. RT_ASSERT(rbb);
  238. if (rt_slist_isempty(&rbb->blk_list))
  239. return 0;
  240. level = rt_hw_interrupt_disable();
  241. for (node = rt_slist_first(&rbb->blk_list); node; node = rt_slist_next(node))
  242. {
  243. block = rt_slist_entry(node, struct rt_rbb_blk, list);
  244. if (block->status == RT_RBB_BLK_PUT)
  245. {
  246. block->status = RT_RBB_BLK_GET;
  247. goto __exit;
  248. }
  249. }
  250. /* not found */
  251. block = NULL;
  252. __exit:
  253. rt_hw_interrupt_enable(level);
  254. return block;
  255. }
  256. RTM_EXPORT(rt_rbb_blk_get);
  257. /**
  258. * free the block
  259. *
  260. * @param rbb ring block buffer object
  261. * @param block the block
  262. */
  263. void rt_rbb_blk_free(rt_rbb_t rbb, rt_rbb_blk_t block)
  264. {
  265. rt_base_t level;
  266. RT_ASSERT(rbb);
  267. RT_ASSERT(block);
  268. RT_ASSERT(block->status != RT_RBB_BLK_UNUSED);
  269. level = rt_hw_interrupt_disable();
  270. /* remove it on rbb block list */
  271. rt_slist_remove(&rbb->blk_list, &block->list);
  272. block->status = RT_RBB_BLK_UNUSED;
  273. rt_hw_interrupt_enable(level);
  274. }
  275. RTM_EXPORT(rt_rbb_blk_free);
  276. /**
  277. * get a continuous block to queue by given size
  278. *
  279. * tail head
  280. * +------------------+---------------+--------+----------+--------+
  281. * | block3 | empty1 | block1 | block2 |fragment|
  282. * +------------------+------------------------+----------+--------+
  283. * |<-- return_size -->| |
  284. * |<--- queue_data_len --->|
  285. *
  286. * tail head
  287. * +------------------+---------------+--------+----------+--------+
  288. * | block3 | empty1 | block1 | block2 |fragment|
  289. * +------------------+------------------------+----------+--------+
  290. * |<-- return_size -->| out of len(b1+b2+b3) |
  291. * |<-------------------- queue_data_len -------------------->|
  292. *
  293. * @param rbb ring block buffer object
  294. * @param queue_data_len The max queue data size, and the return size must less then it.
  295. * @param queue continuous block queue
  296. *
  297. * @return the block queue data total size
  298. */
  299. 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)
  300. {
  301. rt_base_t level;
  302. rt_size_t data_total_size = 0;
  303. rt_slist_t *node;
  304. rt_rbb_blk_t last_block = NULL, block;
  305. RT_ASSERT(rbb);
  306. RT_ASSERT(blk_queue);
  307. if (rt_slist_isempty(&rbb->blk_list))
  308. return 0;
  309. level = rt_hw_interrupt_disable();
  310. for (node = rt_slist_first(&rbb->blk_list); node; node = rt_slist_next(node))
  311. {
  312. if (!last_block)
  313. {
  314. last_block = rt_slist_entry(node, struct rt_rbb_blk, list);
  315. if (last_block->status == RT_RBB_BLK_PUT)
  316. {
  317. /* save the first put status block to queue */
  318. blk_queue->blocks = last_block;
  319. blk_queue->blk_num = 0;
  320. }
  321. else
  322. {
  323. /* the first block must be put status */
  324. last_block = NULL;
  325. continue;
  326. }
  327. }
  328. else
  329. {
  330. block = rt_slist_entry(node, struct rt_rbb_blk, list);
  331. /*
  332. * these following conditions will break the loop:
  333. * 1. the current block is not put status
  334. * 2. the last block and current block is not continuous
  335. * 3. the data_total_size will out of range
  336. */
  337. if (block->status != RT_RBB_BLK_PUT ||
  338. last_block->buf > block->buf ||
  339. data_total_size + block->size > queue_data_len)
  340. {
  341. break;
  342. }
  343. /* backup last block */
  344. last_block = block;
  345. }
  346. /* remove current block */
  347. rt_slist_remove(&rbb->blk_list, &last_block->list);
  348. data_total_size += last_block->size;
  349. last_block->status = RT_RBB_BLK_GET;
  350. blk_queue->blk_num++;
  351. }
  352. rt_hw_interrupt_enable(level);
  353. return data_total_size;
  354. }
  355. RTM_EXPORT(rt_rbb_blk_queue_get);
  356. /**
  357. * get all block length on block queue
  358. *
  359. * @param blk_queue the block queue
  360. *
  361. * @return total length
  362. */
  363. rt_size_t rt_rbb_blk_queue_len(rt_rbb_blk_queue_t blk_queue)
  364. {
  365. rt_size_t i, data_total_size = 0;
  366. RT_ASSERT(blk_queue);
  367. for (i = 0; i < blk_queue->blk_num; i++)
  368. {
  369. data_total_size += blk_queue->blocks[i].size;
  370. }
  371. return data_total_size;
  372. }
  373. RTM_EXPORT(rt_rbb_blk_queue_len);
  374. /**
  375. * return the block queue buffer
  376. *
  377. * @param blk_queue the block queue
  378. *
  379. * @return block queue buffer
  380. */
  381. rt_uint8_t *rt_rbb_blk_queue_buf(rt_rbb_blk_queue_t blk_queue)
  382. {
  383. RT_ASSERT(blk_queue);
  384. return blk_queue->blocks[0].buf;
  385. }
  386. RTM_EXPORT(rt_rbb_blk_queue_buf);
  387. /**
  388. * free the block queue
  389. *
  390. * @param rbb ring block buffer object
  391. * @param blk_queue the block queue
  392. */
  393. void rt_rbb_blk_queue_free(rt_rbb_t rbb, rt_rbb_blk_queue_t blk_queue)
  394. {
  395. rt_size_t i;
  396. RT_ASSERT(rbb);
  397. RT_ASSERT(blk_queue);
  398. for (i = 0; i < blk_queue->blk_num; i++)
  399. {
  400. rt_rbb_blk_free(rbb, &blk_queue->blocks[i]);
  401. }
  402. }
  403. RTM_EXPORT(rt_rbb_blk_queue_free);
  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. rt_size_t rt_rbb_next_blk_queue_len(rt_rbb_t rbb)
  413. {
  414. rt_base_t level;
  415. rt_size_t data_len = 0;
  416. rt_slist_t *node;
  417. rt_rbb_blk_t last_block = NULL, block;
  418. RT_ASSERT(rbb);
  419. if (rt_slist_isempty(&rbb->blk_list))
  420. return 0;
  421. level = rt_hw_interrupt_disable();
  422. for (node = rt_slist_first(&rbb->blk_list); node; node = rt_slist_next(node))
  423. {
  424. if (!last_block)
  425. {
  426. last_block = rt_slist_entry(node, struct rt_rbb_blk, list);
  427. if (last_block->status != RT_RBB_BLK_PUT)
  428. {
  429. /* the first block must be put status */
  430. last_block = NULL;
  431. continue;
  432. }
  433. }
  434. else
  435. {
  436. block = rt_slist_entry(node, struct rt_rbb_blk, list);
  437. /*
  438. * these following conditions will break the loop:
  439. * 1. the current block is not put status
  440. * 2. the last block and current block is not continuous
  441. */
  442. if (block->status != RT_RBB_BLK_PUT || last_block->buf > block->buf)
  443. {
  444. break;
  445. }
  446. /* backup last block */
  447. last_block = block;
  448. }
  449. data_len += last_block->size;
  450. }
  451. rt_hw_interrupt_enable(level);
  452. return data_len;
  453. }
  454. RTM_EXPORT(rt_rbb_next_blk_queue_len);
  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. rt_size_t rt_rbb_get_buf_size(rt_rbb_t rbb)
  463. {
  464. RT_ASSERT(rbb);
  465. return rbb->buf_size;
  466. }
  467. RTM_EXPORT(rt_rbb_get_buf_size);