ring_buffer.c 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. /* ring_buffer.c: Simple ring buffer API */
  2. /*
  3. * Copyright (c) 2015 Intel Corporation
  4. *
  5. * SPDX-License-Identifier: Apache-2.0
  6. */
  7. #include <sys/ring_buffer.h>
  8. #include <string.h>
  9. /* LCOV_EXCL_START */
  10. /* The weak function used to allow overwriting it in the test and trigger
  11. * rewinding earlier.
  12. */
  13. uint32_t __weak ring_buf_get_rewind_threshold(void)
  14. {
  15. return RING_BUFFER_MAX_SIZE;
  16. }
  17. /* LCOV_EXCL_STOP */
  18. /**
  19. * Internal data structure for a buffer header.
  20. *
  21. * We want all of this to fit in a single uint32_t. Every item stored in the
  22. * ring buffer will be one of these headers plus any extra data supplied
  23. */
  24. struct ring_element {
  25. uint32_t type :16; /**< Application-specific */
  26. uint32_t length :8; /**< length in 32-bit chunks */
  27. uint32_t value :8; /**< Room for small integral values */
  28. };
  29. static uint32_t mod(struct ring_buf *buf, uint32_t val)
  30. {
  31. return likely(buf->mask) ? val & buf->mask : val % buf->size;
  32. }
  33. static uint32_t get_rewind_value(uint32_t buf_size, uint32_t threshold)
  34. {
  35. return buf_size * (threshold / buf_size);
  36. }
  37. int ring_buf_is_empty(struct ring_buf *buf)
  38. {
  39. uint32_t tail = buf->tail;
  40. uint32_t head = buf->head;
  41. if (tail < head) {
  42. tail += get_rewind_value(buf->size,
  43. ring_buf_get_rewind_threshold());
  44. }
  45. return (head == tail);
  46. }
  47. uint32_t ring_buf_size_get(struct ring_buf *buf)
  48. {
  49. uint32_t tail = buf->tail;
  50. uint32_t head = buf->head;
  51. if (tail < head) {
  52. tail += get_rewind_value(buf->size,
  53. ring_buf_get_rewind_threshold());
  54. }
  55. return tail - head;
  56. }
  57. uint32_t ring_buf_space_get(struct ring_buf *buf)
  58. {
  59. return buf->size - ring_buf_size_get(buf);
  60. }
  61. int ring_buf_item_put(struct ring_buf *buf, uint16_t type, uint8_t value,
  62. uint32_t *data, uint8_t size32)
  63. {
  64. uint32_t i, space, index, rc;
  65. uint32_t threshold = ring_buf_get_rewind_threshold();
  66. uint32_t rew;
  67. space = ring_buf_space_get(buf);
  68. if (space >= (size32 + 1)) {
  69. struct ring_element *header =
  70. (struct ring_element *)&buf->buf.buf32[mod(buf, buf->tail)];
  71. header->type = type;
  72. header->length = size32;
  73. header->value = value;
  74. if (likely(buf->mask)) {
  75. for (i = 0U; i < size32; ++i) {
  76. index = (i + buf->tail + 1) & buf->mask;
  77. buf->buf.buf32[index] = data[i];
  78. }
  79. } else {
  80. for (i = 0U; i < size32; ++i) {
  81. index = (i + buf->tail + 1) % buf->size;
  82. buf->buf.buf32[index] = data[i];
  83. }
  84. }
  85. /* Check if indexes shall be rewound. */
  86. if (buf->tail > threshold) {
  87. rew = get_rewind_value(buf->size, threshold);
  88. } else {
  89. rew = 0;
  90. }
  91. buf->tail = buf->tail + (size32 + 1 - rew);
  92. rc = 0U;
  93. } else {
  94. buf->misc.item_mode.dropped_put_count++;
  95. rc = -EMSGSIZE;
  96. }
  97. return rc;
  98. }
  99. int ring_buf_item_get(struct ring_buf *buf, uint16_t *type, uint8_t *value,
  100. uint32_t *data, uint8_t *size32)
  101. {
  102. struct ring_element *header;
  103. uint32_t i, index;
  104. uint32_t tail = buf->tail;
  105. uint32_t rew;
  106. /* Tail is always ahead, if it is not, it's only because it got rewound. */
  107. if (tail < buf->head) {
  108. /* Locally undo rewind to get tail aligned with head. */
  109. rew = get_rewind_value(buf->size,
  110. ring_buf_get_rewind_threshold());
  111. tail += rew;
  112. } else if (ring_buf_is_empty(buf)) {
  113. return -EAGAIN;
  114. } else {
  115. rew = 0;
  116. }
  117. header = (struct ring_element *) &buf->buf.buf32[mod(buf, buf->head)];
  118. if (data && (header->length > *size32)) {
  119. *size32 = header->length;
  120. return -EMSGSIZE;
  121. }
  122. *size32 = header->length;
  123. *type = header->type;
  124. *value = header->value;
  125. if (data) {
  126. if (likely(buf->mask)) {
  127. for (i = 0U; i < header->length; ++i) {
  128. index = (i + buf->head + 1) & buf->mask;
  129. data[i] = buf->buf.buf32[index];
  130. }
  131. } else {
  132. for (i = 0U; i < header->length; ++i) {
  133. index = (i + buf->head + 1) % buf->size;
  134. data[i] = buf->buf.buf32[index];
  135. }
  136. }
  137. }
  138. /* Include potential rewinding */
  139. buf->head = buf->head + header->length + 1 - rew;
  140. return 0;
  141. }
  142. /** @brief Wraps index if it exceeds the limit.
  143. *
  144. * @param val Value
  145. * @param max Max.
  146. *
  147. * @return value % max.
  148. */
  149. static inline uint32_t wrap(uint32_t val, uint32_t max)
  150. {
  151. return val >= max ? (val - max) : val;
  152. }
  153. uint32_t ring_buf_put_claim(struct ring_buf *buf, uint8_t **data, uint32_t size)
  154. {
  155. uint32_t space, trail_size, allocated, tmp_trail_mod;
  156. tmp_trail_mod = mod(buf, buf->misc.byte_mode.tmp_tail);
  157. space = (buf->head + buf->size) - buf->misc.byte_mode.tmp_tail;
  158. trail_size = buf->size - tmp_trail_mod;
  159. /* Limit requested size to available size. */
  160. size = MIN(size, space);
  161. trail_size = buf->size - (tmp_trail_mod);
  162. /* Limit allocated size to trail size. */
  163. allocated = MIN(trail_size, size);
  164. *data = &buf->buf.buf8[tmp_trail_mod];
  165. buf->misc.byte_mode.tmp_tail =
  166. buf->misc.byte_mode.tmp_tail + allocated;
  167. return allocated;
  168. }
  169. int ring_buf_put_finish(struct ring_buf *buf, uint32_t size)
  170. {
  171. uint32_t rew;
  172. uint32_t threshold = ring_buf_get_rewind_threshold();
  173. if ((buf->tail + size) > (buf->head + buf->size)) {
  174. return -EINVAL;
  175. }
  176. /* Check if indexes shall be rewind. */
  177. if (buf->tail > threshold) {
  178. rew = get_rewind_value(buf->size, threshold);
  179. } else {
  180. rew = 0;
  181. }
  182. buf->tail += (size - rew);
  183. buf->misc.byte_mode.tmp_tail = buf->tail;
  184. return 0;
  185. }
  186. uint32_t ring_buf_put(struct ring_buf *buf, const uint8_t *data, uint32_t size)
  187. {
  188. uint8_t *dst;
  189. uint32_t partial_size;
  190. uint32_t total_size = 0U;
  191. int err;
  192. do {
  193. partial_size = ring_buf_put_claim(buf, &dst, size);
  194. memcpy(dst, data, partial_size);
  195. total_size += partial_size;
  196. size -= partial_size;
  197. data += partial_size;
  198. } while (size && partial_size);
  199. err = ring_buf_put_finish(buf, total_size);
  200. __ASSERT_NO_MSG(err == 0);
  201. return total_size;
  202. }
  203. uint32_t ring_buf_get_claim(struct ring_buf *buf, uint8_t **data, uint32_t size)
  204. {
  205. uint32_t space, granted_size, trail_size, tmp_head_mod;
  206. uint32_t tail = buf->tail;
  207. /* Tail is always ahead, if it is not, it's only because it got rewinded. */
  208. if (tail < buf->misc.byte_mode.tmp_head) {
  209. /* Locally, increment it to pre-rewind value */
  210. tail += get_rewind_value(buf->size,
  211. ring_buf_get_rewind_threshold());
  212. }
  213. tmp_head_mod = mod(buf, buf->misc.byte_mode.tmp_head);
  214. space = tail - buf->misc.byte_mode.tmp_head;
  215. trail_size = buf->size - tmp_head_mod;
  216. /* Limit requested size to available size. */
  217. granted_size = MIN(size, space);
  218. /* Limit allocated size to trail size. */
  219. granted_size = MIN(trail_size, granted_size);
  220. *data = &buf->buf.buf8[tmp_head_mod];
  221. buf->misc.byte_mode.tmp_head += granted_size;
  222. return granted_size;
  223. }
  224. int ring_buf_get_finish(struct ring_buf *buf, uint32_t size)
  225. {
  226. uint32_t tail = buf->tail;
  227. uint32_t rew;
  228. /* Tail is always ahead, if it is not, it's only because it got rewinded. */
  229. if (tail < buf->misc.byte_mode.tmp_head) {
  230. /* tail was rewinded. Locally, increment it to pre-rewind value */
  231. rew = get_rewind_value(buf->size,
  232. ring_buf_get_rewind_threshold());
  233. tail += rew;
  234. } else {
  235. rew = 0;
  236. }
  237. if ((buf->head + size) > tail) {
  238. return -EINVAL;
  239. }
  240. /* Include potential rewinding. */
  241. buf->head += (size - rew);
  242. buf->misc.byte_mode.tmp_head = buf->head;
  243. return 0;
  244. }
  245. uint32_t ring_buf_get(struct ring_buf *buf, uint8_t *data, uint32_t size)
  246. {
  247. uint8_t *src;
  248. uint32_t partial_size;
  249. uint32_t total_size = 0U;
  250. int err;
  251. do {
  252. partial_size = ring_buf_get_claim(buf, &src, size);
  253. if (data) {
  254. memcpy(data, src, partial_size);
  255. data += partial_size;
  256. }
  257. total_size += partial_size;
  258. size -= partial_size;
  259. } while (size && partial_size);
  260. err = ring_buf_get_finish(buf, total_size);
  261. __ASSERT_NO_MSG(err == 0);
  262. return total_size;
  263. }
  264. uint32_t ring_buf_peek(struct ring_buf *buf, uint8_t *data, uint32_t size)
  265. {
  266. uint8_t *src;
  267. uint32_t partial_size;
  268. uint32_t total_size = 0U;
  269. int err;
  270. size = MIN(size, ring_buf_size_get(buf));
  271. do {
  272. partial_size = ring_buf_get_claim(buf, &src, size);
  273. __ASSERT_NO_MSG(data != NULL);
  274. memcpy(data, src, partial_size);
  275. data += partial_size;
  276. total_size += partial_size;
  277. size -= partial_size;
  278. } while (size && partial_size);
  279. /* effectively unclaim total_size bytes */
  280. err = ring_buf_get_finish(buf, 0);
  281. __ASSERT_NO_MSG(err == 0);
  282. return total_size;
  283. }