xref: /nrf52832-nimble/rt-thread/components/drivers/src/ringbuffer.c (revision 104654410c56c573564690304ae786df310c91fc)
1 /*
2  * Copyright (c) 2006-2018, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2012-09-30     Bernard      first version.
9  * 2013-05-08     Grissiom     reimplement
10  * 2016-08-18     heyuanjie    add interface
11  */
12 
13 #include <rtthread.h>
14 #include <rtdevice.h>
15 #include <string.h>
16 
rt_ringbuffer_status(struct rt_ringbuffer * rb)17 rt_inline enum rt_ringbuffer_state rt_ringbuffer_status(struct rt_ringbuffer *rb)
18 {
19     if (rb->read_index == rb->write_index)
20     {
21         if (rb->read_mirror == rb->write_mirror)
22             return RT_RINGBUFFER_EMPTY;
23         else
24             return RT_RINGBUFFER_FULL;
25     }
26     return RT_RINGBUFFER_HALFFULL;
27 }
28 
rt_ringbuffer_init(struct rt_ringbuffer * rb,rt_uint8_t * pool,rt_int16_t size)29 void rt_ringbuffer_init(struct rt_ringbuffer *rb,
30                         rt_uint8_t           *pool,
31                         rt_int16_t            size)
32 {
33     RT_ASSERT(rb != RT_NULL);
34     RT_ASSERT(size > 0);
35 
36     /* initialize read and write index */
37     rb->read_mirror = rb->read_index = 0;
38     rb->write_mirror = rb->write_index = 0;
39 
40     /* set buffer pool and size */
41     rb->buffer_ptr = pool;
42     rb->buffer_size = RT_ALIGN_DOWN(size, RT_ALIGN_SIZE);
43 }
44 RTM_EXPORT(rt_ringbuffer_init);
45 
46 /**
47  * put a block of data into ring buffer
48  */
rt_ringbuffer_put(struct rt_ringbuffer * rb,const rt_uint8_t * ptr,rt_uint16_t length)49 rt_size_t rt_ringbuffer_put(struct rt_ringbuffer *rb,
50                             const rt_uint8_t     *ptr,
51                             rt_uint16_t           length)
52 {
53     rt_uint16_t size;
54 
55     RT_ASSERT(rb != RT_NULL);
56 
57     /* whether has enough space */
58     size = rt_ringbuffer_space_len(rb);
59 
60     /* no space */
61     if (size == 0)
62         return 0;
63 
64     /* drop some data */
65     if (size < length)
66         length = size;
67 
68     if (rb->buffer_size - rb->write_index > length)
69     {
70         /* read_index - write_index = empty space */
71         memcpy(&rb->buffer_ptr[rb->write_index], ptr, length);
72         /* this should not cause overflow because there is enough space for
73          * length of data in current mirror */
74         rb->write_index += length;
75         return length;
76     }
77 
78     memcpy(&rb->buffer_ptr[rb->write_index],
79            &ptr[0],
80            rb->buffer_size - rb->write_index);
81     memcpy(&rb->buffer_ptr[0],
82            &ptr[rb->buffer_size - rb->write_index],
83            length - (rb->buffer_size - rb->write_index));
84 
85     /* we are going into the other side of the mirror */
86     rb->write_mirror = ~rb->write_mirror;
87     rb->write_index = length - (rb->buffer_size - rb->write_index);
88 
89     return length;
90 }
91 RTM_EXPORT(rt_ringbuffer_put);
92 
93 /**
94  * put a block of data into ring buffer
95  *
96  * When the buffer is full, it will discard the old data.
97  */
rt_ringbuffer_put_force(struct rt_ringbuffer * rb,const rt_uint8_t * ptr,rt_uint16_t length)98 rt_size_t rt_ringbuffer_put_force(struct rt_ringbuffer *rb,
99                             const rt_uint8_t     *ptr,
100                             rt_uint16_t           length)
101 {
102     rt_uint16_t space_length;
103 
104     RT_ASSERT(rb != RT_NULL);
105 
106     space_length = rt_ringbuffer_space_len(rb);
107 
108     if (length > rb->buffer_size)
109     {
110         ptr = &ptr[length - rb->buffer_size];
111         length = rb->buffer_size;
112     }
113 
114     if (rb->buffer_size - rb->write_index > length)
115     {
116         /* read_index - write_index = empty space */
117         memcpy(&rb->buffer_ptr[rb->write_index], ptr, length);
118         /* this should not cause overflow because there is enough space for
119          * length of data in current mirror */
120         rb->write_index += length;
121 
122         if (length > space_length)
123             rb->read_index = rb->write_index;
124 
125         return length;
126     }
127 
128     memcpy(&rb->buffer_ptr[rb->write_index],
129            &ptr[0],
130            rb->buffer_size - rb->write_index);
131     memcpy(&rb->buffer_ptr[0],
132            &ptr[rb->buffer_size - rb->write_index],
133            length - (rb->buffer_size - rb->write_index));
134 
135     /* we are going into the other side of the mirror */
136     rb->write_mirror = ~rb->write_mirror;
137     rb->write_index = length - (rb->buffer_size - rb->write_index);
138 
139     if (length > space_length)
140     {
141         rb->read_mirror = ~rb->read_mirror;
142         rb->read_index = rb->write_index;
143     }
144 
145     return length;
146 }
147 RTM_EXPORT(rt_ringbuffer_put_force);
148 
149 /**
150  *  get data from ring buffer
151  */
rt_ringbuffer_get(struct rt_ringbuffer * rb,rt_uint8_t * ptr,rt_uint16_t length)152 rt_size_t rt_ringbuffer_get(struct rt_ringbuffer *rb,
153                             rt_uint8_t           *ptr,
154                             rt_uint16_t           length)
155 {
156     rt_size_t size;
157 
158     RT_ASSERT(rb != RT_NULL);
159 
160     /* whether has enough data  */
161     size = rt_ringbuffer_data_len(rb);
162 
163     /* no data */
164     if (size == 0)
165         return 0;
166 
167     /* less data */
168     if (size < length)
169         length = size;
170 
171     if (rb->buffer_size - rb->read_index > length)
172     {
173         /* copy all of data */
174         memcpy(ptr, &rb->buffer_ptr[rb->read_index], length);
175         /* this should not cause overflow because there is enough space for
176          * length of data in current mirror */
177         rb->read_index += length;
178         return length;
179     }
180 
181     memcpy(&ptr[0],
182            &rb->buffer_ptr[rb->read_index],
183            rb->buffer_size - rb->read_index);
184     memcpy(&ptr[rb->buffer_size - rb->read_index],
185            &rb->buffer_ptr[0],
186            length - (rb->buffer_size - rb->read_index));
187 
188     /* we are going into the other side of the mirror */
189     rb->read_mirror = ~rb->read_mirror;
190     rb->read_index = length - (rb->buffer_size - rb->read_index);
191 
192     return length;
193 }
194 RTM_EXPORT(rt_ringbuffer_get);
195 
196 /**
197  * put a character into ring buffer
198  */
rt_ringbuffer_putchar(struct rt_ringbuffer * rb,const rt_uint8_t ch)199 rt_size_t rt_ringbuffer_putchar(struct rt_ringbuffer *rb, const rt_uint8_t ch)
200 {
201     RT_ASSERT(rb != RT_NULL);
202 
203     /* whether has enough space */
204     if (!rt_ringbuffer_space_len(rb))
205         return 0;
206 
207     rb->buffer_ptr[rb->write_index] = ch;
208 
209     /* flip mirror */
210     if (rb->write_index == rb->buffer_size-1)
211     {
212         rb->write_mirror = ~rb->write_mirror;
213         rb->write_index = 0;
214     }
215     else
216     {
217         rb->write_index++;
218     }
219 
220     return 1;
221 }
222 RTM_EXPORT(rt_ringbuffer_putchar);
223 
224 /**
225  * put a character into ring buffer
226  *
227  * When the buffer is full, it will discard one old data.
228  */
rt_ringbuffer_putchar_force(struct rt_ringbuffer * rb,const rt_uint8_t ch)229 rt_size_t rt_ringbuffer_putchar_force(struct rt_ringbuffer *rb, const rt_uint8_t ch)
230 {
231     enum rt_ringbuffer_state old_state;
232 
233     RT_ASSERT(rb != RT_NULL);
234 
235     old_state = rt_ringbuffer_status(rb);
236 
237     rb->buffer_ptr[rb->write_index] = ch;
238 
239     /* flip mirror */
240     if (rb->write_index == rb->buffer_size-1)
241     {
242         rb->write_mirror = ~rb->write_mirror;
243         rb->write_index = 0;
244         if (old_state == RT_RINGBUFFER_FULL)
245         {
246             rb->read_mirror = ~rb->read_mirror;
247             rb->read_index = rb->write_index;
248         }
249     }
250     else
251     {
252         rb->write_index++;
253         if (old_state == RT_RINGBUFFER_FULL)
254             rb->read_index = rb->write_index;
255     }
256 
257     return 1;
258 }
259 RTM_EXPORT(rt_ringbuffer_putchar_force);
260 
261 /**
262  * get a character from a ringbuffer
263  */
rt_ringbuffer_getchar(struct rt_ringbuffer * rb,rt_uint8_t * ch)264 rt_size_t rt_ringbuffer_getchar(struct rt_ringbuffer *rb, rt_uint8_t *ch)
265 {
266     RT_ASSERT(rb != RT_NULL);
267 
268     /* ringbuffer is empty */
269     if (!rt_ringbuffer_data_len(rb))
270         return 0;
271 
272     /* put character */
273     *ch = rb->buffer_ptr[rb->read_index];
274 
275     if (rb->read_index == rb->buffer_size-1)
276     {
277         rb->read_mirror = ~rb->read_mirror;
278         rb->read_index = 0;
279     }
280     else
281     {
282         rb->read_index++;
283     }
284 
285     return 1;
286 }
287 RTM_EXPORT(rt_ringbuffer_getchar);
288 
289 /**
290  * get the size of data in rb
291  */
rt_ringbuffer_data_len(struct rt_ringbuffer * rb)292 rt_size_t rt_ringbuffer_data_len(struct rt_ringbuffer *rb)
293 {
294     switch (rt_ringbuffer_status(rb))
295     {
296     case RT_RINGBUFFER_EMPTY:
297         return 0;
298     case RT_RINGBUFFER_FULL:
299         return rb->buffer_size;
300     case RT_RINGBUFFER_HALFFULL:
301     default:
302         if (rb->write_index > rb->read_index)
303             return rb->write_index - rb->read_index;
304         else
305             return rb->buffer_size - (rb->read_index - rb->write_index);
306     };
307 }
308 RTM_EXPORT(rt_ringbuffer_data_len);
309 
310 /**
311  * empty the rb
312  */
rt_ringbuffer_reset(struct rt_ringbuffer * rb)313 void rt_ringbuffer_reset(struct rt_ringbuffer *rb)
314 {
315     RT_ASSERT(rb != RT_NULL);
316 
317     rb->read_mirror = 0;
318     rb->read_index = 0;
319     rb->write_mirror = 0;
320     rb->write_index = 0;
321 }
322 RTM_EXPORT(rt_ringbuffer_reset);
323 
324 #ifdef RT_USING_HEAP
325 
rt_ringbuffer_create(rt_uint16_t size)326 struct rt_ringbuffer* rt_ringbuffer_create(rt_uint16_t size)
327 {
328     struct rt_ringbuffer *rb;
329     rt_uint8_t *pool;
330 
331 	RT_ASSERT(size > 0);
332 
333     size = RT_ALIGN_DOWN(size, RT_ALIGN_SIZE);
334 
335     rb = rt_malloc(sizeof(struct rt_ringbuffer));
336     if (rb == RT_NULL)
337         goto exit;
338 
339     pool = rt_malloc(size);
340     if (pool == RT_NULL)
341     {
342         rt_free(rb);
343         rb = RT_NULL;
344         goto exit;
345     }
346     rt_ringbuffer_init(rb, pool, size);
347 
348 exit:
349     return rb;
350 }
351 RTM_EXPORT(rt_ringbuffer_create);
352 
rt_ringbuffer_destroy(struct rt_ringbuffer * rb)353 void rt_ringbuffer_destroy(struct rt_ringbuffer *rb)
354 {
355     RT_ASSERT(rb != RT_NULL);
356 
357     rt_free(rb->buffer_ptr);
358     rt_free(rb);
359 }
360 RTM_EXPORT(rt_ringbuffer_destroy);
361 
362 #endif
363