Remove \version svn tag.
[bertos.git] / bertos / struct / fifobuf.h
1 /**
2  * \file
3  * <!--
4  * This file is part of BeRTOS.
5  *
6  * Bertos is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  * As a special exception, you may use this file as part of a free software
21  * library without restriction.  Specifically, if other files instantiate
22  * templates or use macros or inline functions from this file, or you compile
23  * this file and link it with other files to produce an executable, this
24  * file does not by itself cause the resulting executable to be covered by
25  * the GNU General Public License.  This exception does not however
26  * invalidate any other reasons why the executable file might be covered by
27  * the GNU General Public License.
28  *
29  * Copyright 2003, 2004 Develer S.r.l. (http://www.develer.com/)
30  * Copyright 2001, 2008 Bernie Innocenti <bernie@codewiz.org>
31  * -->
32  *
33  * \brief General pourpose FIFO buffer implemented with a ring buffer
34  *
35  * \li \c begin points to the first buffer element;
36  * \li \c end points to the last buffer element (unlike the STL convention);
37  * \li \c head points to the element to be extracted next;
38  * \li \c tail points to the location following the last insertion;
39  * \li when any of the pointers advances beyond \c end, it is reset
40  *     back to \c begin.
41  *
42  * \code
43  *
44  *  +-----------------------------------+
45  *  |  empty  |   valid data   |  empty |
46  *  +-----------------------------------+
47  *  ^         ^                ^        ^
48  *  begin    head             tail     end
49  *
50  * \endcode
51  *
52  * The buffer is EMPTY when \c head and \c tail point to the same location:
53  *              \code head == tail \endcode
54  *
55  * The buffer is FULL when \c tail points to the location immediately
56  * after \c head:
57  *              \code tail == head - 1 \endcode
58  *
59  * The buffer is also FULL when \c tail points to the last buffer
60  * location and head points to the first one:
61  *              \code head == begin && tail == end \endcode
62  *
63  * \author Bernie Innocenti <bernie@codewiz.org>
64  */
65
66 #ifndef STRUCT_FIFO_H
67 #define STRUCT_FIFO_H
68
69 #include <cpu/types.h>
70 #include <cpu/irq.h>
71 #include <cfg/debug.h>
72
73 typedef struct FIFOBuffer
74 {
75         unsigned char * volatile head;
76         unsigned char * volatile tail;
77         unsigned char *begin;
78         unsigned char *end;
79 } FIFOBuffer;
80
81
82 #define ASSERT_VALID_FIFO(fifo) \
83         ATOMIC( \
84                 ASSERT((fifo)->head >= (fifo)->begin); \
85                 ASSERT((fifo)->head <= (fifo)->end); \
86                 ASSERT((fifo)->tail >= (fifo)->begin); \
87                 ASSERT((fifo)->tail <= (fifo)->end); \
88         )
89
90
91 /**
92  * Check whether the fifo is empty
93  *
94  * \note Calling fifo_isempty() is safe while a concurrent
95  *       execution context is calling fifo_push() or fifo_pop()
96  *       only if the CPU can atomically update a pointer
97  *       (which the AVR and other 8-bit processors can't do).
98  *
99  * \sa fifo_isempty_locked
100  */
101 INLINE bool fifo_isempty(const FIFOBuffer *fb)
102 {
103         //ASSERT_VALID_FIFO(fb);
104         return fb->head == fb->tail;
105 }
106
107
108 /**
109  * Check whether the fifo is full
110  *
111  * \note Calling fifo_isfull() is safe while a concurrent
112  *       execution context is calling fifo_pop() and the
113  *       CPU can update a pointer atomically.
114  *       It is NOT safe when the other context calls
115  *       fifo_push().
116  *       This limitation is not usually problematic in a
117  *       consumer/producer scenario because the
118  *       fifo_isfull() and fifo_push() are usually called
119  *       in the producer context.
120  */
121 INLINE bool fifo_isfull(const FIFOBuffer *fb)
122 {
123         //ASSERT_VALID_FIFO(fb);
124         return
125                 ((fb->head == fb->begin) && (fb->tail == fb->end))
126                 || (fb->tail == fb->head - 1);
127 }
128
129
130 /**
131  * Push a character on the fifo buffer.
132  *
133  * \note Calling \c fifo_push() on a full buffer is undefined.
134  *       The caller must make sure the buffer has at least
135  *       one free slot before calling this function.
136  *
137  * \note It is safe to call fifo_pop() and fifo_push() from
138  *       concurrent contexts, unless the CPU can't update
139  *       a pointer atomically (which the AVR and other 8-bit
140  *       processors can't do).
141  *
142  * \sa fifo_push_locked
143  */
144 INLINE void fifo_push(FIFOBuffer *fb, unsigned char c)
145 {
146 #ifdef __MWERKS__
147 #pragma interrupt called
148 #endif
149         //ASSERT_VALID_FIFO(fb);
150
151         /* Write at tail position */
152         *(fb->tail) = c;
153
154         if (UNLIKELY(fb->tail == fb->end))
155                 /* wrap tail around */
156                 fb->tail = fb->begin;
157         else
158                 /* Move tail forward */
159                 fb->tail++;
160 }
161
162
163 /**
164  * Pop a character from the fifo buffer.
165  *
166  * \note Calling \c fifo_pop() on an empty buffer is undefined.
167  *       The caller must make sure the buffer contains at least
168  *       one character before calling this function.
169  *
170  * \note It is safe to call fifo_pop() and fifo_push() from
171  *       concurrent contexts.
172  */
173 INLINE unsigned char fifo_pop(FIFOBuffer *fb)
174 {
175 #ifdef __MWERKS__
176 #pragma interrupt called
177 #endif
178         //ASSERT_VALID_FIFO(fb);
179
180         if (UNLIKELY(fb->head == fb->end))
181         {
182                 /* wrap head around */
183                 fb->head = fb->begin;
184                 return *(fb->end);
185         }
186         else
187                 /* move head forward */
188                 return *(fb->head++);
189 }
190
191
192 /**
193  * Make the fifo empty, discarding all its current contents.
194  */
195 INLINE void fifo_flush(FIFOBuffer *fb)
196 {
197         //ASSERT_VALID_FIFO(fb);
198         fb->head = fb->tail;
199 }
200
201
202 #if CPU_REG_BITS >= CPU_BITS_PER_PTR
203
204         /*
205          * 16/32bit CPUs that can update a pointer with a single write
206          * operation, no need to disable interrupts.
207          */
208         #define fifo_isempty_locked(fb) fifo_isempty((fb))
209         #define fifo_push_locked(fb, c) fifo_push((fb), (c))
210         #define fifo_pop_locked(fb)     fifo_pop((fb))
211         #define fifo_flush_locked(fb)   fifo_flush((fb))
212
213 #else /* CPU_REG_BITS < CPU_BITS_PER_PTR */
214
215         /**
216          * Similar to fifo_isempty(), but with stronger guarantees for
217          * concurrent access between user and interrupt code.
218          *
219          * \note This is actually only needed for 8-bit processors.
220          *
221          * \sa fifo_isempty()
222          */
223         INLINE bool fifo_isempty_locked(const FIFOBuffer *fb)
224         {
225                 bool result;
226                 ATOMIC(result = fifo_isempty(fb));
227                 return result;
228         }
229
230
231         /**
232          * Similar to fifo_push(), but with stronger guarantees for
233          * concurrent access between user and interrupt code.
234          *
235          * \note This is actually only needed for 8-bit processors.
236          *
237          * \sa fifo_push()
238          */
239         INLINE void fifo_push_locked(FIFOBuffer *fb, unsigned char c)
240         {
241                 ATOMIC(fifo_push(fb, c));
242         }
243
244         /* Probably not really needed, but hard to prove. */
245         INLINE unsigned char fifo_pop_locked(FIFOBuffer *fb)
246         {
247                 unsigned char c;
248                 ATOMIC(c = fifo_pop(fb));
249                 return c;
250         }
251
252         /**
253          * Similar to fifo_flush(), but with stronger guarantees for
254          * concurrent access between user and interrupt code.
255          *
256          * \note This is actually only needed for 8-bit processors.
257          *
258          * \sa fifo_flush()
259          */
260         INLINE void fifo_flush_locked(FIFOBuffer *fb)
261         {
262                 ATOMIC(fifo_flush(fb));
263         }
264
265 #endif /* CPU_REG_BITS < BITS_PER_PTR */
266
267
268 /**
269  * Thread safe version of fifo_isfull()
270  */
271 INLINE bool fifo_isfull_locked(const FIFOBuffer *_fb)
272 {
273         bool result;
274         ATOMIC(result = fifo_isfull(_fb));
275         return result;
276 }
277
278
279 /**
280  * FIFO Initialization.
281  */
282 INLINE void fifo_init(FIFOBuffer *fb, unsigned char *buf, size_t size)
283 {
284         /* FIFO buffers have a known bug with 1-byte buffers. */
285         ASSERT(size > 1);
286
287         fb->head = fb->tail = fb->begin = buf;
288         fb->end = buf + size - 1;
289 }
290
291 /**
292  * \return Lenght of the FIFOBuffer \a fb.
293  */
294 INLINE size_t fifo_len(FIFOBuffer *fb)
295 {
296         return fb->end - fb->begin;
297 }
298
299
300 #if 0
301
302 /*
303  * UNTESTED: if uncommented, to be moved in fifobuf.c
304  */
305 void fifo_pushblock(FIFOBuffer *fb, unsigned char *block, size_t len)
306 {
307         size_t freelen;
308
309         /* Se c'e' spazio da tail alla fine del buffer */
310         if (fb->tail >= fb->head)
311         {
312                 freelen = fb->end - fb->tail + 1;
313
314                 /* C'e' abbastanza spazio per scrivere tutto il blocco? */
315                 if (freelen < len)
316                 {
317                         /* Scrivi quello che entra fino alla fine del buffer */
318                         memcpy(fb->tail, block, freelen);
319                         block += freelen;
320                         len -= freelen;
321                         fb->tail = fb->begin;
322                 }
323                 else
324                 {
325                         /* Scrivi tutto il blocco */
326                         memcpy(fb->tail, block, len);
327                         fb->tail += len;
328                         return;
329                 }
330         }
331
332         for(;;)
333         {
334                 while (!(freelen = fb->head - fb->tail - 1))
335                         Delay(FIFO_POLLDELAY);
336
337                 /* C'e' abbastanza spazio per scrivere tutto il blocco? */
338                 if (freelen < len)
339                 {
340                         /* Scrivi quello che entra fino alla fine del buffer */
341                         memcpy(fb->tail, block, freelen);
342                         block += freelen;
343                         len -= freelen;
344                         fb->tail += freelen;
345                 }
346                 else
347                 {
348                         /* Scrivi tutto il blocco */
349                         memcpy(fb->tail, block, len);
350                         fb->tail += len;
351                         return;
352                 }
353         }
354 }
355 #endif
356
357 #endif /* STRUCT_FIFO_H */