Add demo application.
[bertos.git] / mware / fifobuf.h
1 /*!
2  * \file
3  * <!--
4  * Copyright 2003, 2004 Develer S.r.l. (http://www.develer.com/)
5  * Copyright 2001 Bernardo Innocenti <bernie@develer.com>
6  * This file is part of DevLib - See README.devlib for information.
7  * -->
8  *
9  * \version $Id$
10  *
11  * \author Bernardo Innocenti <bernie@develer.com>
12  *
13  * \brief General pourpose FIFO buffer implemented with a ring buffer
14  *
15  * \li \c begin points to the first buffer element;
16  * \li \c end points to the last buffer element (unlike the STL convention);
17  * \li \c head points to the element to be extracted next;
18  * \li \c tail points to the location following the last insertion;
19  * \li when any of the pointers advances beyond \c end, it is reset
20  *     back to \c begin.
21  *
22  * \code
23  *
24  *  +-----------------------------------+
25  *  |  empty  |   valid data   |  empty |
26  *  +-----------------------------------+
27  *  ^         ^                ^        ^
28  *  begin    head             tail     end
29  *
30  * \endcode
31  *
32  * The buffer is EMPTY when \c head and \c tail point to the same location:
33  *              \code head == tail \endcode
34  *
35  * The buffer is FULL when \c tail points to the location immediately
36  * after \c head:
37  *              \code tail == head - 1 \endcode
38  *
39  * The buffer is also FULL when \c tail points to the last buffer
40  * location and head points to the first one:
41  *              \code head == begin && tail == end \endcode
42  */
43
44 /*#*
45  *#* $Log$
46  *#* Revision 1.21  2005/11/04 16:20:02  bernie
47  *#* Fix reference to README.devlib in header.
48  *#*
49  *#* Revision 1.20  2005/04/11 19:10:28  bernie
50  *#* Include top-level headers from cfg/ subdir.
51  *#*
52  *#* Revision 1.19  2004/12/08 08:30:12  bernie
53  *#* Add missing header.
54  *#*
55  *#* Revision 1.18  2004/11/16 21:55:12  bernie
56  *#* Workaround for a known fifobuf bug.
57  *#*
58  *#* Revision 1.17  2004/09/14 20:57:00  bernie
59  *#* Use debug.h instead of kdebug.h.
60  *#*
61  *#* Revision 1.16  2004/09/06 21:39:08  bernie
62  *#* Simplify code using ATOMIC().
63  *#*
64  *#* Revision 1.15  2004/08/29 22:05:16  bernie
65  *#* Rename BITS_PER_PTR to CPU_BITS_PER_PTR.
66  *#*
67  *#* Revision 1.14  2004/08/25 14:12:09  rasky
68  *#* Aggiornato il comment block dei log RCS
69  *#*
70  *#* Revision 1.13  2004/08/24 13:16:11  bernie
71  *#* Add type-size definitions for preprocessor.
72  *#*
73  *#* Revision 1.12  2004/08/02 20:20:29  aleph
74  *#* Merge from project_ks
75  *#*
76  *#* Revision 1.11  2004/07/30 14:15:53  rasky
77  *#* Nuovo supporto unificato per detect della CPU
78  *#*
79  *#* Revision 1.10  2004/07/29 22:57:09  bernie
80  *#* Doxygen fix.
81  *#*
82  *#* Revision 1.9  2004/07/20 23:54:27  bernie
83  *#* fifo_flush_locked(): New function;
84  *#* Revamp documentation.
85  *#*
86  *#* Revision 1.8  2004/07/20 23:47:39  bernie
87  *#* Finally remove redundant protos.
88  *#*
89  *#* Revision 1.7  2004/07/20 23:46:29  bernie
90  *#* Finally remove redundant protos.
91  *#*
92  *#* Revision 1.6  2004/06/06 17:18:04  bernie
93  *#* Remove redundant declaration of fifo_isempty_locked().
94  *#*
95  *#* Revision 1.5  2004/06/06 16:50:35  bernie
96  *#* Import fixes for race conditions from project_ks.
97  *#*
98  *#* Revision 1.4  2004/06/06 16:11:17  bernie
99  *#* Protect MetroWerks specific pragmas with #ifdef's
100  *#*/
101
102 #ifndef MWARE_FIFO_H
103 #define MWARE_FIFO_H
104
105 #include <cfg/cpu.h>
106 #include <cfg/debug.h>
107
108 typedef struct FIFOBuffer
109 {
110         unsigned char * volatile head;
111         unsigned char * volatile tail;
112         unsigned char *begin;
113         unsigned char *end;
114 } FIFOBuffer;
115
116
117 #define ASSERT_VALID_FIFO(fifo) \
118         ATOMIC( \
119                 ASSERT((fifo)->head >= (fifo)->begin); \
120                 ASSERT((fifo)->head <= (fifo)->end); \
121                 ASSERT((fifo)->tail >= (fifo)->begin); \
122                 ASSERT((fifo)->tail <= (fifo)->end); \
123         )
124
125
126 /*!
127  * Check whether the fifo is empty
128  *
129  * \note Calling fifo_isempty() is safe while a concurrent
130  *       execution context is calling fifo_push() or fifo_pop()
131  *       only if the CPU can atomically update a pointer
132  *       (which the AVR and other 8-bit processors can't do).
133  *
134  * \sa fifo_isempty_locked
135  */
136 INLINE bool fifo_isempty(const FIFOBuffer *fb)
137 {
138         //ASSERT_VALID_FIFO(fb);
139         return fb->head == fb->tail;
140 }
141
142
143 /*!
144  * Check whether the fifo is full
145  *
146  * \note Calling fifo_isfull() is safe while a concurrent
147  *       execution context is calling fifo_pop() and the
148  *       CPU can update a pointer atomically.
149  *       It is NOT safe when the other context calls
150  *       fifo_push().
151  *       This limitation is not usually problematic in a
152  *       consumer/producer scenario because the
153  *       fifo_isfull() and fifo_push() are usually called
154  *       in the producer context.
155  */
156 INLINE bool fifo_isfull(const FIFOBuffer *fb)
157 {
158         //ASSERT_VALID_FIFO(fb);
159         return
160                 ((fb->head == fb->begin) && (fb->tail == fb->end))
161                 || (fb->tail == fb->head - 1);
162 }
163
164
165 /*!
166  * Pop a character from the fifo buffer.
167  *
168  * \note Calling \c fifo_push() on a full buffer is undefined.
169  *       The caller must make sure the buffer has at least
170  *       one free slot before calling this function.
171  *
172  * \note It is safe to call fifo_pop() and fifo_push() from
173  *       concurrent contexts, unless the CPU can't update
174  *       a pointer atomically (which the AVR and other 8-bit
175  *       processors can't do).
176  *
177  * \sa fifo_push_locked
178  */
179 INLINE void fifo_push(FIFOBuffer *fb, unsigned char c)
180 {
181 #ifdef __MWERKS__
182 #pragma interrupt called
183 #endif
184         //ASSERT_VALID_FIFO(fb);
185
186         /* Write at tail position */
187         *(fb->tail) = c;
188
189         if (UNLIKELY(fb->tail == fb->end))
190                 /* wrap tail around */
191                 fb->tail = fb->begin;
192         else
193                 /* Move tail forward */
194                 fb->tail++;
195 }
196
197
198 /*!
199  * Pop a character from the fifo buffer.
200  *
201  * \note Calling \c fifo_pop() on an empty buffer is undefined.
202  *       The caller must make sure the buffer contains at least
203  *       one character before calling this function.
204  *
205  * \note It is safe to call fifo_pop() and fifo_push() from
206  *       concurrent contexts.
207  */
208 INLINE unsigned char fifo_pop(FIFOBuffer *fb)
209 {
210 #ifdef __MWERKS__
211 #pragma interrupt called
212 #endif
213         //ASSERT_VALID_FIFO(fb);
214
215         if (UNLIKELY(fb->head == fb->end))
216         {
217                 /* wrap head around */
218                 fb->head = fb->begin;
219                 return *(fb->end);
220         }
221         else
222                 /* move head forward */
223                 return *(fb->head++);
224 }
225
226
227 /*!
228  * Make the fifo empty, discarding all its current contents.
229  */
230 INLINE void fifo_flush(FIFOBuffer *fb)
231 {
232         //ASSERT_VALID_FIFO(fb);
233         fb->head = fb->tail;
234 }
235
236
237 #if CPU_REG_BITS >= CPU_BITS_PER_PTR
238
239         /*
240          * 16/32bit CPUs that can update a pointer with a single write
241          * operation, no need to disable interrupts.
242          */
243         #define fifo_isempty_locked(fb) fifo_isempty((fb))
244         #define fifo_push_locked(fb, c) fifo_push((fb), (c))
245         #define fifo_pop_locked(fb)     fifo_pop((fb))
246         #define fifo_flush_locked(fb)   fifo_flush((fb))
247
248 #else /* CPU_REG_BITS < CPU_BITS_PER_PTR */
249
250         /*!
251          * Similar to fifo_isempty(), but with stronger guarantees for
252          * concurrent access between user and interrupt code.
253          *
254          * \note This is actually only needed for 8-bit processors.
255          *
256          * \sa fifo_isempty()
257          */
258         INLINE bool fifo_isempty_locked(const FIFOBuffer *fb)
259         {
260                 bool result;
261                 ATOMIC(result = fifo_isempty(fb));
262                 return result;
263         }
264
265
266         /*!
267          * Similar to fifo_push(), but with stronger guarantees for
268          * concurrent access between user and interrupt code.
269          *
270          * \note This is actually only needed for 8-bit processors.
271          *
272          * \sa fifo_push()
273          */
274         INLINE void fifo_push_locked(FIFOBuffer *fb, unsigned char c)
275         {
276                 ATOMIC(fifo_push(fb, c));
277         }
278
279         /* Probably not really needed, but hard to prove. */
280         INLINE unsigned char fifo_pop_locked(FIFOBuffer *fb)
281         {
282                 unsigned char c;
283                 ATOMIC(c = fifo_pop(fb));
284                 return c;
285         }
286
287         /*!
288          * Similar to fifo_flush(), but with stronger guarantees for
289          * concurrent access between user and interrupt code.
290          *
291          * \note This is actually only needed for 8-bit processors.
292          *
293          * \sa fifo_flush()
294          */
295         INLINE void fifo_flush_locked(FIFOBuffer *fb)
296         {
297                 ATOMIC(fifo_flush(fb));
298         }
299
300 #endif /* CPU_REG_BITS < BITS_PER_PTR */
301
302
303 /*!
304  * Thread safe version of fifo_isfull()
305  */
306 INLINE bool fifo_isfull_locked(const FIFOBuffer *_fb)
307 {
308         bool result;
309         ATOMIC(result = fifo_isfull(_fb));
310         return result;
311 }
312
313
314 /*!
315  * FIFO Initialization.
316  */
317 INLINE void fifo_init(FIFOBuffer *fb, unsigned char *buf, size_t size)
318 {
319         /* FIFO buffers have a known bug with 1-byte buffers. */
320         ASSERT(size > 1);
321
322         fb->head = fb->tail = fb->begin = buf;
323         fb->end = buf + size - 1;
324 }
325
326
327 #if 0
328
329 /*
330  * UNTESTED: if uncommented, to be moved in fifobuf.c
331  */
332 void fifo_pushblock(FIFOBuffer *fb, unsigned char *block, size_t len)
333 {
334         size_t freelen;
335
336         /* Se c'e' spazio da tail alla fine del buffer */
337         if (fb->tail >= fb->head)
338         {
339                 freelen = fb->end - fb->tail + 1;
340
341                 /* C'e' abbastanza spazio per scrivere tutto il blocco? */
342                 if (freelen < len)
343                 {
344                         /* Scrivi quello che entra fino alla fine del buffer */
345                         memcpy(fb->tail, block, freelen);
346                         block += freelen;
347                         len -= freelen;
348                         fb->tail = fb->begin;
349                 }
350                 else
351                 {
352                         /* Scrivi tutto il blocco */
353                         memcpy(fb->tail, block, len);
354                         fb->tail += len;
355                         return;
356                 }
357         }
358
359         for(;;)
360         {
361                 while (!(freelen = fb->head - fb->tail - 1))
362                         Delay(FIFO_POLLDELAY);
363
364                 /* C'e' abbastanza spazio per scrivere tutto il blocco? */
365                 if (freelen < len)
366                 {
367                         /* Scrivi quello che entra fino alla fine del buffer */
368                         memcpy(fb->tail, block, freelen);
369                         block += freelen;
370                         len -= freelen;
371                         fb->tail += freelen;
372                 }
373                 else
374                 {
375                         /* Scrivi tutto il blocco */
376                         memcpy(fb->tail, block, len);
377                         fb->tail += len;
378                         return;
379                 }
380         }
381 }
382 #endif
383
384 #endif /* MWARE_FIFO_H */
385