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