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