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