blob: ac98d1b840ff9f5a3f512c8f63be98d08900f641 [file] [log] [blame]
Kumar Galad12d8af2016-10-05 12:01:54 -05001/* ring_buffer.c: Simple ring buffer API */
2
3/*
4 * Copyright (c) 2015 Intel Corporation
5 *
David B. Kinderac74d8b2017-01-18 17:01:01 -08006 * SPDX-License-Identifier: Apache-2.0
Kumar Galad12d8af2016-10-05 12:01:54 -05007 */
8
Anas Nashif7435e5e2019-06-25 15:57:18 -04009#include <sys/ring_buffer.h>
Krzysztof Chruscinski26031f72018-09-07 15:30:56 +020010#include <string.h>
Kumar Galad12d8af2016-10-05 12:01:54 -050011
Kumar Galaa1b77fd2020-05-27 11:26:57 -050012uint32_t ring_buf_put_claim(struct ring_buf *buf, uint8_t **data, uint32_t size)
Krzysztof Chruscinski26031f72018-09-07 15:30:56 +020013{
Nicolas Pitre099850e2022-02-13 22:16:33 -050014 uint32_t free_space, wrap_size;
15 int32_t base;
Krzysztof Chruscinski44339532021-12-16 11:56:38 +010016
Nicolas Pitre099850e2022-02-13 22:16:33 -050017 base = buf->put_base;
18 wrap_size = buf->put_head - base;
19 if (unlikely(wrap_size >= buf->size)) {
20 /* put_base is not yet adjusted */
21 wrap_size -= buf->size;
22 base += buf->size;
Krzysztof Chruscinski44339532021-12-16 11:56:38 +010023 }
Nicolas Pitre099850e2022-02-13 22:16:33 -050024 wrap_size = buf->size - wrap_size;
Krzysztof Chruscinski26031f72018-09-07 15:30:56 +020025
Nicolas Pitre099850e2022-02-13 22:16:33 -050026 free_space = ring_buf_space_get(buf);
27 size = MIN(size, free_space);
28 size = MIN(size, wrap_size);
Krzysztof Chruscinski26031f72018-09-07 15:30:56 +020029
Nicolas Pitre099850e2022-02-13 22:16:33 -050030 *data = &buf->buffer[buf->put_head - base];
31 buf->put_head += size;
Krzysztof Chruscinski1e46bb32020-07-27 11:00:23 +020032
Nicolas Pitre099850e2022-02-13 22:16:33 -050033 return size;
Krzysztof Chruscinski26031f72018-09-07 15:30:56 +020034}
35
Kumar Galaa1b77fd2020-05-27 11:26:57 -050036int ring_buf_put_finish(struct ring_buf *buf, uint32_t size)
Krzysztof Chruscinski26031f72018-09-07 15:30:56 +020037{
Nicolas Pitre099850e2022-02-13 22:16:33 -050038 uint32_t finish_space, wrap_size;
Krzysztof Chruscinski58942f32021-07-30 08:29:48 +020039
Nicolas Pitre099850e2022-02-13 22:16:33 -050040 finish_space = buf->put_head - buf->put_tail;
41 if (unlikely(size > finish_space)) {
Krzysztof Chruscinski26031f72018-09-07 15:30:56 +020042 return -EINVAL;
43 }
44
Nicolas Pitre099850e2022-02-13 22:16:33 -050045 buf->put_tail += size;
Nicolas Pitreeee5b8e2022-02-25 13:20:23 -050046 buf->put_head = buf->put_tail;
Krzysztof Chruscinski58942f32021-07-30 08:29:48 +020047
Nicolas Pitre099850e2022-02-13 22:16:33 -050048 wrap_size = buf->put_tail - buf->put_base;
49 if (unlikely(wrap_size >= buf->size)) {
50 /* we wrapped: adjust put_base */
51 buf->put_base += buf->size;
52 }
Krzysztof Chruscinski26031f72018-09-07 15:30:56 +020053
54 return 0;
55}
56
Kumar Galaa1b77fd2020-05-27 11:26:57 -050057uint32_t ring_buf_put(struct ring_buf *buf, const uint8_t *data, uint32_t size)
Krzysztof Chruscinski26031f72018-09-07 15:30:56 +020058{
Kumar Galaa1b77fd2020-05-27 11:26:57 -050059 uint8_t *dst;
60 uint32_t partial_size;
61 uint32_t total_size = 0U;
Krzysztof Chruscinski9502b8b2019-03-07 11:56:01 +010062 int err;
Krzysztof Chruscinski26031f72018-09-07 15:30:56 +020063
64 do {
65 partial_size = ring_buf_put_claim(buf, &dst, size);
66 memcpy(dst, data, partial_size);
67 total_size += partial_size;
68 size -= partial_size;
69 data += partial_size;
70 } while (size && partial_size);
71
Krzysztof Chruscinski9502b8b2019-03-07 11:56:01 +010072 err = ring_buf_put_finish(buf, total_size);
73 __ASSERT_NO_MSG(err == 0);
Krzysztof Chruscinski26031f72018-09-07 15:30:56 +020074
75 return total_size;
76}
77
Kumar Galaa1b77fd2020-05-27 11:26:57 -050078uint32_t ring_buf_get_claim(struct ring_buf *buf, uint8_t **data, uint32_t size)
Krzysztof Chruscinski26031f72018-09-07 15:30:56 +020079{
Nicolas Pitre099850e2022-02-13 22:16:33 -050080 uint32_t available_size, wrap_size;
81 int32_t base;
Krzysztof Chruscinski58942f32021-07-30 08:29:48 +020082
Nicolas Pitre099850e2022-02-13 22:16:33 -050083 base = buf->get_base;
84 wrap_size = buf->get_head - base;
85 if (unlikely(wrap_size >= buf->size)) {
86 /* get_base is not yet adjusted */
87 wrap_size -= buf->size;
88 base += buf->size;
Krzysztof Chruscinski58942f32021-07-30 08:29:48 +020089 }
Nicolas Pitre099850e2022-02-13 22:16:33 -050090 wrap_size = buf->size - wrap_size;
Krzysztof Chruscinski26031f72018-09-07 15:30:56 +020091
Nicolas Pitre099850e2022-02-13 22:16:33 -050092 available_size = ring_buf_size_get(buf);
93 size = MIN(size, available_size);
94 size = MIN(size, wrap_size);
Krzysztof Chruscinski26031f72018-09-07 15:30:56 +020095
Nicolas Pitre099850e2022-02-13 22:16:33 -050096 *data = &buf->buffer[buf->get_head - base];
97 buf->get_head += size;
Krzysztof Chruscinski26031f72018-09-07 15:30:56 +020098
Nicolas Pitre099850e2022-02-13 22:16:33 -050099 return size;
Krzysztof Chruscinski26031f72018-09-07 15:30:56 +0200100}
101
Kumar Galaa1b77fd2020-05-27 11:26:57 -0500102int ring_buf_get_finish(struct ring_buf *buf, uint32_t size)
Krzysztof Chruscinski26031f72018-09-07 15:30:56 +0200103{
Nicolas Pitre099850e2022-02-13 22:16:33 -0500104 uint32_t finish_space, wrap_size;
Krzysztof Chruscinski58942f32021-07-30 08:29:48 +0200105
Nicolas Pitre099850e2022-02-13 22:16:33 -0500106 finish_space = buf->get_head - buf->get_tail;
107 if (unlikely(size > finish_space)) {
Krzysztof Chruscinski26031f72018-09-07 15:30:56 +0200108 return -EINVAL;
109 }
110
Nicolas Pitre099850e2022-02-13 22:16:33 -0500111 buf->get_tail += size;
Nicolas Pitreeee5b8e2022-02-25 13:20:23 -0500112 buf->get_head = buf->get_tail;
Nicolas Pitre099850e2022-02-13 22:16:33 -0500113
114 wrap_size = buf->get_tail - buf->get_base;
115 if (unlikely(wrap_size >= buf->size)) {
116 /* we wrapped: adjust get_base */
117 buf->get_base += buf->size;
118 }
Krzysztof Chruscinski26031f72018-09-07 15:30:56 +0200119
120 return 0;
121}
122
Kumar Galaa1b77fd2020-05-27 11:26:57 -0500123uint32_t ring_buf_get(struct ring_buf *buf, uint8_t *data, uint32_t size)
Krzysztof Chruscinski26031f72018-09-07 15:30:56 +0200124{
Kumar Galaa1b77fd2020-05-27 11:26:57 -0500125 uint8_t *src;
126 uint32_t partial_size;
127 uint32_t total_size = 0U;
Krzysztof Chruscinski9502b8b2019-03-07 11:56:01 +0100128 int err;
Krzysztof Chruscinski26031f72018-09-07 15:30:56 +0200129
130 do {
131 partial_size = ring_buf_get_claim(buf, &src, size);
Jordan Yatesa4afedb2021-03-19 11:40:05 +1000132 if (data) {
133 memcpy(data, src, partial_size);
134 data += partial_size;
135 }
Krzysztof Chruscinski26031f72018-09-07 15:30:56 +0200136 total_size += partial_size;
137 size -= partial_size;
Krzysztof Chruscinski26031f72018-09-07 15:30:56 +0200138 } while (size && partial_size);
139
Krzysztof Chruscinski9502b8b2019-03-07 11:56:01 +0100140 err = ring_buf_get_finish(buf, total_size);
141 __ASSERT_NO_MSG(err == 0);
Krzysztof Chruscinski26031f72018-09-07 15:30:56 +0200142
143 return total_size;
144}
Christopher Friedt0ca511a2021-07-22 11:09:24 -0400145
146uint32_t ring_buf_peek(struct ring_buf *buf, uint8_t *data, uint32_t size)
147{
148 uint8_t *src;
149 uint32_t partial_size;
150 uint32_t total_size = 0U;
151 int err;
152
153 size = MIN(size, ring_buf_size_get(buf));
154
155 do {
156 partial_size = ring_buf_get_claim(buf, &src, size);
157 __ASSERT_NO_MSG(data != NULL);
158 memcpy(data, src, partial_size);
159 data += partial_size;
160 total_size += partial_size;
161 size -= partial_size;
162 } while (size && partial_size);
163
164 /* effectively unclaim total_size bytes */
165 err = ring_buf_get_finish(buf, 0);
166 __ASSERT_NO_MSG(err == 0);
167
168 return total_size;
169}
Nicolas Pitre81dbc9d2022-02-15 23:14:03 -0500170
171/**
172 * Internal data structure for a buffer header.
173 *
174 * We want all of this to fit in a single uint32_t. Every item stored in the
175 * ring buffer will be one of these headers plus any extra data supplied
176 */
177struct ring_element {
178 uint32_t type :16; /**< Application-specific */
179 uint32_t length :8; /**< length in 32-bit chunks */
180 uint32_t value :8; /**< Room for small integral values */
181};
182
183int ring_buf_item_put(struct ring_buf *buf, uint16_t type, uint8_t value,
184 uint32_t *data32, uint8_t size32)
185{
186 uint8_t *dst, *data = (uint8_t *)data32;
187 struct ring_element *header;
188 uint32_t space, size, partial_size, total_size;
189 int ret;
190
191 space = ring_buf_space_get(buf);
192 size = size32 * 4;
193 if (size + sizeof(struct ring_element) > space) {
194 return -EMSGSIZE;
195 }
196
197 ret = ring_buf_put_claim(buf, &dst, sizeof(struct ring_element));
198 __ASSERT_NO_MSG(ret == sizeof(struct ring_element));
199
200 header = (struct ring_element *)dst;
201 header->type = type;
202 header->length = size32;
203 header->value = value;
204 total_size = sizeof(struct ring_element);
205
206 do {
207 partial_size = ring_buf_put_claim(buf, &dst, size);
208 memcpy(dst, data, partial_size);
209 size -= partial_size;
210 total_size += partial_size;
211 data += partial_size;
212 } while (size && partial_size);
213 __ASSERT_NO_MSG(size == 0);
214
215 ret = ring_buf_put_finish(buf, total_size);
216 __ASSERT_NO_MSG(ret == 0);
217
218 return 0;
219}
220
221int ring_buf_item_get(struct ring_buf *buf, uint16_t *type, uint8_t *value,
222 uint32_t *data32, uint8_t *size32)
223{
224 uint8_t *src, *data = (uint8_t *)data32;
225 struct ring_element *header;
226 uint32_t size, partial_size, total_size;
227 int ret;
228
229 if (ring_buf_is_empty(buf)) {
230 return -EAGAIN;
231 }
232
233 ret = ring_buf_get_claim(buf, &src, sizeof(struct ring_element));
234 __ASSERT_NO_MSG(ret == sizeof(struct ring_element));
235
236 header = (struct ring_element *)src;
237
238 if (data && (header->length > *size32)) {
239 *size32 = header->length;
240 ring_buf_get_finish(buf, 0);
241 return -EMSGSIZE;
242 }
243
244 *size32 = header->length;
245 *type = header->type;
246 *value = header->value;
247 total_size = sizeof(struct ring_element);
248
249 size = *size32 * 4;
250
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
261 ret = ring_buf_get_finish(buf, total_size);
262 __ASSERT_NO_MSG(ret == 0);
263
264 return 0;
265}