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