blob: 5756082931f45f635d071a7f52eebea8af176f8b [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 if (unlikely(size == 0)) {
41 /* claim is cancelled */
42 buf->put_head = buf->put_tail;
43 return 0;
44 }
45
46 finish_space = buf->put_head - buf->put_tail;
47 if (unlikely(size > finish_space)) {
Krzysztof Chruscinski26031f72018-09-07 15:30:56 +020048 return -EINVAL;
49 }
50
Nicolas Pitre099850e2022-02-13 22:16:33 -050051 buf->put_tail += size;
Krzysztof Chruscinski58942f32021-07-30 08:29:48 +020052
Nicolas Pitre099850e2022-02-13 22:16:33 -050053 wrap_size = buf->put_tail - buf->put_base;
54 if (unlikely(wrap_size >= buf->size)) {
55 /* we wrapped: adjust put_base */
56 buf->put_base += buf->size;
57 }
Krzysztof Chruscinski26031f72018-09-07 15:30:56 +020058
59 return 0;
60}
61
Kumar Galaa1b77fd2020-05-27 11:26:57 -050062uint32_t ring_buf_put(struct ring_buf *buf, const uint8_t *data, uint32_t size)
Krzysztof Chruscinski26031f72018-09-07 15:30:56 +020063{
Kumar Galaa1b77fd2020-05-27 11:26:57 -050064 uint8_t *dst;
65 uint32_t partial_size;
66 uint32_t total_size = 0U;
Krzysztof Chruscinski9502b8b2019-03-07 11:56:01 +010067 int err;
Krzysztof Chruscinski26031f72018-09-07 15:30:56 +020068
69 do {
70 partial_size = ring_buf_put_claim(buf, &dst, size);
71 memcpy(dst, data, partial_size);
72 total_size += partial_size;
73 size -= partial_size;
74 data += partial_size;
75 } while (size && partial_size);
76
Krzysztof Chruscinski9502b8b2019-03-07 11:56:01 +010077 err = ring_buf_put_finish(buf, total_size);
78 __ASSERT_NO_MSG(err == 0);
Krzysztof Chruscinski26031f72018-09-07 15:30:56 +020079
80 return total_size;
81}
82
Kumar Galaa1b77fd2020-05-27 11:26:57 -050083uint32_t ring_buf_get_claim(struct ring_buf *buf, uint8_t **data, uint32_t size)
Krzysztof Chruscinski26031f72018-09-07 15:30:56 +020084{
Nicolas Pitre099850e2022-02-13 22:16:33 -050085 uint32_t available_size, wrap_size;
86 int32_t base;
Krzysztof Chruscinski58942f32021-07-30 08:29:48 +020087
Nicolas Pitre099850e2022-02-13 22:16:33 -050088 base = buf->get_base;
89 wrap_size = buf->get_head - base;
90 if (unlikely(wrap_size >= buf->size)) {
91 /* get_base is not yet adjusted */
92 wrap_size -= buf->size;
93 base += buf->size;
Krzysztof Chruscinski58942f32021-07-30 08:29:48 +020094 }
Nicolas Pitre099850e2022-02-13 22:16:33 -050095 wrap_size = buf->size - wrap_size;
Krzysztof Chruscinski26031f72018-09-07 15:30:56 +020096
Nicolas Pitre099850e2022-02-13 22:16:33 -050097 available_size = ring_buf_size_get(buf);
98 size = MIN(size, available_size);
99 size = MIN(size, wrap_size);
Krzysztof Chruscinski26031f72018-09-07 15:30:56 +0200100
Nicolas Pitre099850e2022-02-13 22:16:33 -0500101 *data = &buf->buffer[buf->get_head - base];
102 buf->get_head += size;
Krzysztof Chruscinski26031f72018-09-07 15:30:56 +0200103
Nicolas Pitre099850e2022-02-13 22:16:33 -0500104 return size;
Krzysztof Chruscinski26031f72018-09-07 15:30:56 +0200105}
106
Kumar Galaa1b77fd2020-05-27 11:26:57 -0500107int ring_buf_get_finish(struct ring_buf *buf, uint32_t size)
Krzysztof Chruscinski26031f72018-09-07 15:30:56 +0200108{
Nicolas Pitre099850e2022-02-13 22:16:33 -0500109 uint32_t finish_space, wrap_size;
Krzysztof Chruscinski58942f32021-07-30 08:29:48 +0200110
Nicolas Pitre099850e2022-02-13 22:16:33 -0500111 if (unlikely(size == 0)) {
112 /* claim is cancelled */
113 buf->get_head = buf->get_tail;
114 return 0;
Krzysztof Chruscinski58942f32021-07-30 08:29:48 +0200115 }
116
Nicolas Pitre099850e2022-02-13 22:16:33 -0500117 finish_space = buf->get_head - buf->get_tail;
118 if (unlikely(size > finish_space)) {
Krzysztof Chruscinski26031f72018-09-07 15:30:56 +0200119 return -EINVAL;
120 }
121
Nicolas Pitre099850e2022-02-13 22:16:33 -0500122 buf->get_tail += size;
123
124 wrap_size = buf->get_tail - buf->get_base;
125 if (unlikely(wrap_size >= buf->size)) {
126 /* we wrapped: adjust get_base */
127 buf->get_base += buf->size;
128 }
Krzysztof Chruscinski26031f72018-09-07 15:30:56 +0200129
130 return 0;
131}
132
Kumar Galaa1b77fd2020-05-27 11:26:57 -0500133uint32_t ring_buf_get(struct ring_buf *buf, uint8_t *data, uint32_t size)
Krzysztof Chruscinski26031f72018-09-07 15:30:56 +0200134{
Kumar Galaa1b77fd2020-05-27 11:26:57 -0500135 uint8_t *src;
136 uint32_t partial_size;
137 uint32_t total_size = 0U;
Krzysztof Chruscinski9502b8b2019-03-07 11:56:01 +0100138 int err;
Krzysztof Chruscinski26031f72018-09-07 15:30:56 +0200139
140 do {
141 partial_size = ring_buf_get_claim(buf, &src, size);
Jordan Yatesa4afedb2021-03-19 11:40:05 +1000142 if (data) {
143 memcpy(data, src, partial_size);
144 data += partial_size;
145 }
Krzysztof Chruscinski26031f72018-09-07 15:30:56 +0200146 total_size += partial_size;
147 size -= partial_size;
Krzysztof Chruscinski26031f72018-09-07 15:30:56 +0200148 } while (size && partial_size);
149
Krzysztof Chruscinski9502b8b2019-03-07 11:56:01 +0100150 err = ring_buf_get_finish(buf, total_size);
151 __ASSERT_NO_MSG(err == 0);
Krzysztof Chruscinski26031f72018-09-07 15:30:56 +0200152
153 return total_size;
154}
Christopher Friedt0ca511a2021-07-22 11:09:24 -0400155
156uint32_t ring_buf_peek(struct ring_buf *buf, uint8_t *data, uint32_t size)
157{
158 uint8_t *src;
159 uint32_t partial_size;
160 uint32_t total_size = 0U;
161 int err;
162
163 size = MIN(size, ring_buf_size_get(buf));
164
165 do {
166 partial_size = ring_buf_get_claim(buf, &src, size);
167 __ASSERT_NO_MSG(data != NULL);
168 memcpy(data, src, partial_size);
169 data += partial_size;
170 total_size += partial_size;
171 size -= partial_size;
172 } while (size && partial_size);
173
174 /* effectively unclaim total_size bytes */
175 err = ring_buf_get_finish(buf, 0);
176 __ASSERT_NO_MSG(err == 0);
177
178 return total_size;
179}
Nicolas Pitre81dbc9d2022-02-15 23:14:03 -0500180
181/**
182 * Internal data structure for a buffer header.
183 *
184 * We want all of this to fit in a single uint32_t. Every item stored in the
185 * ring buffer will be one of these headers plus any extra data supplied
186 */
187struct ring_element {
188 uint32_t type :16; /**< Application-specific */
189 uint32_t length :8; /**< length in 32-bit chunks */
190 uint32_t value :8; /**< Room for small integral values */
191};
192
193int ring_buf_item_put(struct ring_buf *buf, uint16_t type, uint8_t value,
194 uint32_t *data32, uint8_t size32)
195{
196 uint8_t *dst, *data = (uint8_t *)data32;
197 struct ring_element *header;
198 uint32_t space, size, partial_size, total_size;
199 int ret;
200
201 space = ring_buf_space_get(buf);
202 size = size32 * 4;
203 if (size + sizeof(struct ring_element) > space) {
204 return -EMSGSIZE;
205 }
206
207 ret = ring_buf_put_claim(buf, &dst, sizeof(struct ring_element));
208 __ASSERT_NO_MSG(ret == sizeof(struct ring_element));
209
210 header = (struct ring_element *)dst;
211 header->type = type;
212 header->length = size32;
213 header->value = value;
214 total_size = sizeof(struct ring_element);
215
216 do {
217 partial_size = ring_buf_put_claim(buf, &dst, size);
218 memcpy(dst, data, partial_size);
219 size -= partial_size;
220 total_size += partial_size;
221 data += partial_size;
222 } while (size && partial_size);
223 __ASSERT_NO_MSG(size == 0);
224
225 ret = ring_buf_put_finish(buf, total_size);
226 __ASSERT_NO_MSG(ret == 0);
227
228 return 0;
229}
230
231int ring_buf_item_get(struct ring_buf *buf, uint16_t *type, uint8_t *value,
232 uint32_t *data32, uint8_t *size32)
233{
234 uint8_t *src, *data = (uint8_t *)data32;
235 struct ring_element *header;
236 uint32_t size, partial_size, total_size;
237 int ret;
238
239 if (ring_buf_is_empty(buf)) {
240 return -EAGAIN;
241 }
242
243 ret = ring_buf_get_claim(buf, &src, sizeof(struct ring_element));
244 __ASSERT_NO_MSG(ret == sizeof(struct ring_element));
245
246 header = (struct ring_element *)src;
247
248 if (data && (header->length > *size32)) {
249 *size32 = header->length;
250 ring_buf_get_finish(buf, 0);
251 return -EMSGSIZE;
252 }
253
254 *size32 = header->length;
255 *type = header->type;
256 *value = header->value;
257 total_size = sizeof(struct ring_element);
258
259 size = *size32 * 4;
260
261 do {
262 partial_size = ring_buf_get_claim(buf, &src, size);
263 if (data) {
264 memcpy(data, src, partial_size);
265 data += partial_size;
266 }
267 total_size += partial_size;
268 size -= partial_size;
269 } while (size && partial_size);
270
271 ret = ring_buf_get_finish(buf, total_size);
272 __ASSERT_NO_MSG(ret == 0);
273
274 return 0;
275}