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