fifo_flush_locked(): New function;
[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 devlib/README 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.9  2004/07/20 23:54:27  bernie
47  * fifo_flush_locked(): New function;
48  * Revamp documentation.
49  *
50  * Revision 1.8  2004/07/20 23:47:39  bernie
51  * Finally remove redundant protos.
52  *
53  * Revision 1.7  2004/07/20 23:46:29  bernie
54  * Finally remove redundant protos.
55  *
56  * Revision 1.6  2004/06/06 17:18:04  bernie
57  * Remove redundant declaration of fifo_isempty_locked().
58  *
59  * Revision 1.5  2004/06/06 16:50:35  bernie
60  * Import fixes for race conditions from project_ks.
61  *
62  * Revision 1.4  2004/06/06 16:11:17  bernie
63  * Protect MetroWerks specific pragmas with #ifdef's
64  */
65
66 #ifndef MWARE_FIFO_H
67 #define MWARE_FIFO_H
68
69 #include "cpu.h"
70
71 typedef struct FIFOBuffer
72 {
73         unsigned char * volatile head;
74         unsigned char * volatile tail;
75         unsigned char *begin;
76         unsigned char *end;
77 } FIFOBuffer;
78
79
80 /*!
81  * Check whether the fifo is empty
82  *
83  * \note Calling fifo_isempty() is safe while a concurrent
84  *       execution context is calling fifo_push() or fifo_pop()
85  *       only if the CPU can atomically update a pointer
86  *       (which the AVR and other 8-bit processors can't do).
87  *
88  * \sa fifo_isempty_locked
89  */
90 INLINE bool fifo_isempty(const FIFOBuffer *fb)
91 {
92         return fb->head == fb->tail;
93 }
94
95
96 /*!
97  * Check whether the fifo is full
98  *
99  * \note Calling fifo_isfull() is safe while a concurrent
100  *       execution context is calling fifo_pop() and the
101  *       CPU can update a pointer atomically.
102  *       It is NOT safe when the other context calls
103  *       fifo_push().
104  *       This limitation is not usually problematic in a
105  *       consumer/producer scenario because the
106  *       fifo_isfull() and fifo_push() are usually called
107  *       in the producer context.
108  */
109 INLINE bool fifo_isfull(const FIFOBuffer *fb)
110 {
111         return
112                 ((fb->head == fb->begin) && (fb->tail == fb->end))
113                 || (fb->tail == fb->head - 1);
114 }
115
116
117 /*!
118  * Pop a character from the fifo buffer.
119  *
120  * \note Calling \c fifo_push() on a full buffer is undefined.
121  *       The caller must make sure the buffer has at least
122  *       one free slot before calling this function.
123  *
124  * \note It is safe to call fifo_pop() and fifo_push() from
125  *       concurrent contexts, unless the CPU can't update
126  *       a pointer atomically (which the AVR and other 8-bit
127  *       processors can't do).
128  *
129  * \sa fifo_push_locked
130  */
131 INLINE void fifo_push(FIFOBuffer *fb, unsigned char c)
132 {
133 #ifdef __MWERKS__
134 #pragma interrupt called
135 #endif
136         /* Write at tail position */
137         *(fb->tail) = c;
138
139         if (fb->tail == fb->end)
140                 /* wrap tail around */
141                 fb->tail = fb->begin;
142         else
143                 /* Move tail forward */
144                 fb->tail++;
145 }
146
147
148 /*!
149  * Pop a character from the fifo buffer.
150  *
151  * \note Calling \c fifo_pop() on an empty buffer is undefined.
152  *       The caller must make sure the buffer contains at least
153  *       one character before calling this function.
154  *
155  * \note It is safe to call fifo_pop() and fifo_push() from
156  *       concurrent contexts.
157  */
158 INLINE unsigned char fifo_pop(FIFOBuffer *fb)
159 {
160 #ifdef __MWERKS__
161 #pragma interrupt called
162 #endif
163         if (fb->head == fb->end)
164         {
165                 /* wrap head around */
166                 fb->head = fb->begin;
167                 return *(fb->end);
168         }
169         else
170                 /* move head forward */
171                 return *(fb->head++);
172 }
173
174
175 /*!
176  * Make the fifo empty, discarding all its current contents.
177  */
178 INLINE void fifo_flush(FIFOBuffer *fb)
179 {
180         fb->head = fb->tail;
181 }
182
183
184 #if !defined(__AVR__)
185
186         /* No tricks needed on 16/32bit CPUs */
187 #       define fifo_isempty_locked(fb) fifo_isempty((fb))
188 #       define fifo_push_locked(fb, c) fifo_push((fb), (c))
189 #       define fifo_flush_locked(fb, c) fifo_flush((fb), (c))
190
191 #else /* !__AVR__ */
192
193         /*!
194          * Similar to fifo_isempty(), but with stronger guarantees for
195          * concurrent access between user and interrupt code.
196          * This is actually only needed for 8-bit processors.
197          *
198          * \sa fifo_isempty()
199          */
200         INLINE bool fifo_isempty_locked(const FIFOBuffer *fb)
201         {
202                 bool result;
203                 cpuflags_t flags;
204
205                 DISABLE_IRQSAVE(flags);
206                 result = fifo_isempty(fb);
207                 ENABLE_IRQRESTORE(flags);
208
209                 return result;
210         }
211
212         /*!
213          * Similar to fifo_push(), but with stronger guarantees for
214          * concurrent access between user and interrupt code.
215          *
216          * \note This is actually only needed for 8-bit processors.
217          *
218          * \sa fifo_push()
219          */
220         INLINE void fifo_push_locked(FIFOBuffer *fb, unsigned char c)
221         {
222                 cpuflags_t flags;
223                 DISABLE_IRQSAVE(flags);
224                 fifo_push(fb, c);
225                 ENABLE_IRQRESTORE(flags);
226         }
227
228         /*!
229          * Similar to fifo_flush(), but with stronger guarantees for
230          * concurrent access between user and interrupt code.
231          *
232          * \note This is actually only needed for 8-bit processors.
233          *
234          * \sa fifo_flush()
235          */
236         INLINE void fifo_flush_locked(FIFOBuffer *fb)
237         {
238                 cpuflags_t flags;
239                 DISABLE_IRQSAVE(flags);
240                 fifo_flush(fb);
241                 ENABLE_IRQRESTORE(flags);
242         }
243
244 #endif /* !__AVR__ */
245
246
247 /*!
248  * Thread safe version of fifo_isfull()
249  */
250 INLINE bool fifo_isfull_locked(const FIFOBuffer *_fb)
251 {
252         bool _result;
253         cpuflags_t _flags;
254
255         DISABLE_IRQSAVE(_flags);
256         _result = fifo_isfull(_fb);
257         ENABLE_IRQRESTORE(_flags);
258
259         return _result;
260 }
261
262
263 /*!
264  * FIFO Initialization.
265  */
266 INLINE void fifo_init(FIFOBuffer *fb, unsigned char *buf, size_t size)
267 {
268         fb->head = fb->tail = fb->begin = buf;
269         fb->end = buf + size - 1;
270 }
271
272
273 #if 0
274
275 /*
276  * UNTESTED: if uncommented, to be moved in fifobuf.c
277  */
278 void fifo_pushblock(FIFOBuffer *fb, unsigned char *block, size_t len)
279 {
280         size_t freelen;
281
282         /* Se c'e' spazio da tail alla fine del buffer */
283         if (fb->tail >= fb->head)
284         {
285                 freelen = fb->end - fb->tail + 1;
286
287                 /* C'e' abbastanza spazio per scrivere tutto il blocco? */
288                 if (freelen < len)
289                 {
290                         /* Scrivi quello che entra fino alla fine del buffer */
291                         memcpy(fb->tail, block, freelen);
292                         block += freelen;
293                         len -= freelen;
294                         fb->tail = fb->begin;
295                 }
296                 else
297                 {
298                         /* Scrivi tutto il blocco */
299                         memcpy(fb->tail, block, len);
300                         fb->tail += len;
301                         return;
302                 }
303         }
304
305         for(;;)
306         {
307                 while (!(freelen = fb->head - fb->tail - 1))
308                         Delay(FIFO_POLLDELAY);
309
310                 /* C'e' abbastanza spazio per scrivere tutto il blocco? */
311                 if (freelen < len)
312                 {
313                         /* Scrivi quello che entra fino alla fine del buffer */
314                         memcpy(fb->tail, block, freelen);
315                         block += freelen;
316                         len -= freelen;
317                         fb->tail += freelen;
318                 }
319                 else
320                 {
321                         /* Scrivi tutto il blocco */
322                         memcpy(fb->tail, block, len);
323                         fb->tail += len;
324                         return;
325                 }
326         }
327 }
328 #endif
329
330 #endif /* MWARE_FIFO_H */
331