1 /* ring_buffer.c: Simple ring buffer API */
2
3 /*
4 * Copyright (c) 2015 Intel Corporation
5 *
6 * SPDX-License-Identifier: Apache-2.0
7 */
8
9 #include <zephyr/sys/ring_buffer.h>
10 #include <string.h>
11
ring_buf_area_claim(struct ring_buf * buf,struct ring_buf_index * ring,uint8_t ** data,uint32_t size)12 uint32_t ring_buf_area_claim(struct ring_buf *buf, struct ring_buf_index *ring,
13 uint8_t **data, uint32_t size)
14 {
15 ring_buf_idx_t head_offset, wrap_size;
16
17 head_offset = ring->head - ring->base;
18 if (unlikely(head_offset >= buf->size)) {
19 /* ring->base is not yet adjusted */
20 head_offset -= buf->size;
21 }
22 wrap_size = buf->size - head_offset;
23 size = MIN(size, wrap_size);
24
25 *data = &buf->buffer[head_offset];
26 ring->head += size;
27
28 return size;
29 }
30
ring_buf_area_finish(struct ring_buf * buf,struct ring_buf_index * ring,uint32_t size)31 int ring_buf_area_finish(struct ring_buf *buf, struct ring_buf_index *ring,
32 uint32_t size)
33 {
34 ring_buf_idx_t claimed_size, tail_offset;
35
36 claimed_size = ring->head - ring->tail;
37 if (unlikely(size > claimed_size)) {
38 return -EINVAL;
39 }
40
41 ring->tail += size;
42 ring->head = ring->tail;
43
44 tail_offset = ring->tail - ring->base;
45 if (unlikely(tail_offset >= buf->size)) {
46 /* we wrapped: adjust ring->base */
47 ring->base += buf->size;
48 }
49
50 return 0;
51 }
52
ring_buf_put(struct ring_buf * buf,const uint8_t * data,uint32_t size)53 uint32_t ring_buf_put(struct ring_buf *buf, const uint8_t *data, uint32_t size)
54 {
55 uint8_t *dst;
56 uint32_t partial_size;
57 uint32_t total_size = 0U;
58 int err;
59
60 do {
61 partial_size = ring_buf_put_claim(buf, &dst, size);
62 if (partial_size == 0) {
63 break;
64 }
65 memcpy(dst, data, partial_size);
66 total_size += partial_size;
67 size -= partial_size;
68 data += partial_size;
69 } while (size != 0);
70
71 err = ring_buf_put_finish(buf, total_size);
72 __ASSERT_NO_MSG(err == 0);
73 ARG_UNUSED(err);
74
75 return total_size;
76 }
77
ring_buf_get(struct ring_buf * buf,uint8_t * data,uint32_t size)78 uint32_t ring_buf_get(struct ring_buf *buf, uint8_t *data, uint32_t size)
79 {
80 uint8_t *src;
81 uint32_t partial_size;
82 uint32_t total_size = 0U;
83 int err;
84
85 do {
86 partial_size = ring_buf_get_claim(buf, &src, size);
87 if (partial_size == 0) {
88 break;
89 }
90 if (data) {
91 memcpy(data, src, partial_size);
92 data += partial_size;
93 }
94 total_size += partial_size;
95 size -= partial_size;
96 } while (size != 0);
97
98 err = ring_buf_get_finish(buf, total_size);
99 __ASSERT_NO_MSG(err == 0);
100 ARG_UNUSED(err);
101
102 return total_size;
103 }
104
ring_buf_peek(struct ring_buf * buf,uint8_t * data,uint32_t size)105 uint32_t ring_buf_peek(struct ring_buf *buf, uint8_t *data, uint32_t size)
106 {
107 uint8_t *src;
108 uint32_t partial_size;
109 uint32_t total_size = 0U;
110 int err;
111
112 do {
113 partial_size = ring_buf_get_claim(buf, &src, size);
114 if (partial_size == 0) {
115 break;
116 }
117 __ASSERT_NO_MSG(data != NULL);
118 memcpy(data, src, partial_size);
119 data += partial_size;
120 total_size += partial_size;
121 size -= partial_size;
122 } while (size != 0);
123
124 /* effectively unclaim total_size bytes */
125 err = ring_buf_get_finish(buf, 0);
126 __ASSERT_NO_MSG(err == 0);
127 ARG_UNUSED(err);
128
129 return total_size;
130 }
131
132 /**
133 * Internal data structure for a buffer header.
134 *
135 * We want all of this to fit in a single uint32_t. Every item stored in the
136 * ring buffer will be one of these headers plus any extra data supplied
137 */
138 struct ring_element {
139 uint32_t type :16; /**< Application-specific */
140 uint32_t length :8; /**< length in 32-bit chunks */
141 uint32_t value :8; /**< Room for small integral values */
142 };
143
ring_buf_item_put(struct ring_buf * buf,uint16_t type,uint8_t value,uint32_t * data32,uint8_t size32)144 int ring_buf_item_put(struct ring_buf *buf, uint16_t type, uint8_t value,
145 uint32_t *data32, uint8_t size32)
146 {
147 uint8_t *dst, *data = (uint8_t *)data32;
148 struct ring_element *header;
149 uint32_t space, size, partial_size, total_size;
150 int err;
151
152 space = ring_buf_space_get(buf);
153 size = size32 * 4;
154 if (size + sizeof(struct ring_element) > space) {
155 return -EMSGSIZE;
156 }
157
158 err = ring_buf_put_claim(buf, &dst, sizeof(struct ring_element));
159 __ASSERT_NO_MSG(err == sizeof(struct ring_element));
160
161 header = (struct ring_element *)dst;
162 header->type = type;
163 header->length = size32;
164 header->value = value;
165 total_size = sizeof(struct ring_element);
166
167 do {
168 partial_size = ring_buf_put_claim(buf, &dst, size);
169 if (partial_size == 0) {
170 break;
171 }
172 memcpy(dst, data, partial_size);
173 size -= partial_size;
174 total_size += partial_size;
175 data += partial_size;
176 } while (size != 0);
177 __ASSERT_NO_MSG(size == 0);
178
179 err = ring_buf_put_finish(buf, total_size);
180 __ASSERT_NO_MSG(err == 0);
181 ARG_UNUSED(err);
182
183 return 0;
184 }
185
ring_buf_item_get(struct ring_buf * buf,uint16_t * type,uint8_t * value,uint32_t * data32,uint8_t * size32)186 int ring_buf_item_get(struct ring_buf *buf, uint16_t *type, uint8_t *value,
187 uint32_t *data32, uint8_t *size32)
188 {
189 uint8_t *src, *data = (uint8_t *)data32;
190 struct ring_element *header;
191 uint32_t size, partial_size, total_size;
192 int err;
193
194 if (ring_buf_is_empty(buf)) {
195 return -EAGAIN;
196 }
197
198 err = ring_buf_get_claim(buf, &src, sizeof(struct ring_element));
199 __ASSERT_NO_MSG(err == sizeof(struct ring_element));
200
201 header = (struct ring_element *)src;
202
203 if (data && (header->length > *size32)) {
204 *size32 = header->length;
205 ring_buf_get_finish(buf, 0);
206 return -EMSGSIZE;
207 }
208
209 *size32 = header->length;
210 *type = header->type;
211 *value = header->value;
212 total_size = sizeof(struct ring_element);
213
214 size = *size32 * 4;
215
216 do {
217 partial_size = ring_buf_get_claim(buf, &src, size);
218 if (partial_size == 0) {
219 break;
220 }
221 if (data) {
222 memcpy(data, src, partial_size);
223 data += partial_size;
224 }
225 total_size += partial_size;
226 size -= partial_size;
227 } while (size != 0);
228
229 err = ring_buf_get_finish(buf, total_size);
230 __ASSERT_NO_MSG(err == 0);
231 ARG_UNUSED(err);
232
233 return 0;
234 }
235