blob: 564687e027afa57f48d1ea4f02b04f3031227aca [file] [log] [blame]
Austin Schuh41baf202022-01-01 14:33:40 -08001/*
2 * The MIT License (MIT)
3 *
4 * Copyright (c) 2019 Ha Thach (tinyusb.org)
5 * Copyright (c) 2020 Reinhard Panhuber - rework to unmasked pointers
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 * THE SOFTWARE.
24 *
25 * This file is part of the TinyUSB stack.
26 */
27
28#include "osal/osal.h"
29#include "tusb_fifo.h"
30
31// Supress IAR warning
32// Warning[Pa082]: undefined behavior: the order of volatile accesses is undefined in this statement
33#if defined(__ICCARM__)
34#pragma diag_suppress = Pa082
35#endif
36
37// implement mutex lock and unlock
38#if CFG_FIFO_MUTEX
39
40static inline void _ff_lock(tu_fifo_mutex_t mutex)
41{
42 if (mutex) osal_mutex_lock(mutex, OSAL_TIMEOUT_WAIT_FOREVER);
43}
44
45static inline void _ff_unlock(tu_fifo_mutex_t mutex)
46{
47 if (mutex) osal_mutex_unlock(mutex);
48}
49
50#else
51
52#define _ff_lock(_mutex)
53#define _ff_unlock(_mutex)
54
55#endif
56
57/** \enum tu_fifo_copy_mode_t
58 * \brief Write modes intended to allow special read and write functions to be able to
59 * copy data to and from USB hardware FIFOs as needed for e.g. STM32s and others
60 */
61typedef enum
62{
63 TU_FIFO_COPY_INC, ///< Copy from/to an increasing source/destination address - default mode
64 TU_FIFO_COPY_CST_FULL_WORDS, ///< Copy from/to a constant source/destination address - required for e.g. STM32 to write into USB hardware FIFO
65} tu_fifo_copy_mode_t;
66
67bool tu_fifo_config(tu_fifo_t *f, void* buffer, uint16_t depth, uint16_t item_size, bool overwritable)
68{
69 if (depth > 0x8000) return false; // Maximum depth is 2^15 items
70
71 _ff_lock(f->mutex_wr);
72 _ff_lock(f->mutex_rd);
73
74 f->buffer = (uint8_t*) buffer;
75 f->depth = depth;
76 f->item_size = item_size;
77 f->overwritable = overwritable;
78
79 // Limit index space to 2*depth - this allows for a fast "modulo" calculation
80 // but limits the maximum depth to 2^16/2 = 2^15 and buffer overflows are detectable
81 // only if overflow happens once (important for unsupervised DMA applications)
82 f->max_pointer_idx = 2*depth - 1;
83 f->non_used_index_space = UINT16_MAX - f->max_pointer_idx;
84
85 f->rd_idx = f->wr_idx = 0;
86
87 _ff_unlock(f->mutex_wr);
88 _ff_unlock(f->mutex_rd);
89
90 return true;
91}
92
93// Static functions are intended to work on local variables
94static inline uint16_t _ff_mod(uint16_t idx, uint16_t depth)
95{
96 while ( idx >= depth) idx -= depth;
97 return idx;
98}
99
100// Intended to be used to read from hardware USB FIFO in e.g. STM32 where all data is read from a constant address
101// Code adapted from dcd_synopsis.c
102// TODO generalize with configurable 1 byte or 4 byte each read
103static void _ff_push_const_addr(uint8_t * ff_buf, const void * app_buf, uint16_t len)
104{
105 volatile const uint32_t * rx_fifo = (volatile const uint32_t *) app_buf;
106
107 // Reading full available 32 bit words from const app address
108 uint16_t full_words = len >> 2;
109 while(full_words--)
110 {
111 tu_unaligned_write32(ff_buf, *rx_fifo);
112 ff_buf += 4;
113 }
114
115 // Read the remaining 1-3 bytes from const app address
116 uint8_t const bytes_rem = len & 0x03;
117 if ( bytes_rem )
118 {
119 uint32_t tmp32 = *rx_fifo;
120 memcpy(ff_buf, &tmp32, bytes_rem);
121 }
122}
123
124// Intended to be used to write to hardware USB FIFO in e.g. STM32
125// where all data is written to a constant address in full word copies
126static void _ff_pull_const_addr(void * app_buf, const uint8_t * ff_buf, uint16_t len)
127{
128 volatile uint32_t * tx_fifo = (volatile uint32_t *) app_buf;
129
130 // Pushing full available 32 bit words to const app address
131 uint16_t full_words = len >> 2;
132 while(full_words--)
133 {
134 *tx_fifo = tu_unaligned_read32(ff_buf);
135 ff_buf += 4;
136 }
137
138 // Write the remaining 1-3 bytes into const app address
139 uint8_t const bytes_rem = len & 0x03;
140 if ( bytes_rem )
141 {
142 uint32_t tmp32 = 0;
143 memcpy(&tmp32, ff_buf, bytes_rem);
144
145 *tx_fifo = tmp32;
146 }
147}
148
149// send one item to FIFO WITHOUT updating write pointer
150static inline void _ff_push(tu_fifo_t* f, void const * app_buf, uint16_t rel)
151{
152 memcpy(f->buffer + (rel * f->item_size), app_buf, f->item_size);
153}
154
155// send n items to FIFO WITHOUT updating write pointer
156static void _ff_push_n(tu_fifo_t* f, void const * app_buf, uint16_t n, uint16_t rel, tu_fifo_copy_mode_t copy_mode)
157{
158 uint16_t const nLin = f->depth - rel;
159 uint16_t const nWrap = n - nLin;
160
161 uint16_t nLin_bytes = nLin * f->item_size;
162 uint16_t nWrap_bytes = nWrap * f->item_size;
163
164 // current buffer of fifo
165 uint8_t* ff_buf = f->buffer + (rel * f->item_size);
166
167 switch (copy_mode)
168 {
169 case TU_FIFO_COPY_INC:
170 if(n <= nLin)
171 {
172 // Linear only
173 memcpy(ff_buf, app_buf, n*f->item_size);
174 }
175 else
176 {
177 // Wrap around
178
179 // Write data to linear part of buffer
180 memcpy(ff_buf, app_buf, nLin_bytes);
181
182 // Write data wrapped around
183 memcpy(f->buffer, ((uint8_t const*) app_buf) + nLin_bytes, nWrap_bytes);
184 }
185 break;
186
187 case TU_FIFO_COPY_CST_FULL_WORDS:
188 // Intended for hardware buffers from which it can be read word by word only
189 if(n <= nLin)
190 {
191 // Linear only
192 _ff_push_const_addr(ff_buf, app_buf, n*f->item_size);
193 }
194 else
195 {
196 // Wrap around case
197
198 // Write full words to linear part of buffer
199 uint16_t nLin_4n_bytes = nLin_bytes & 0xFFFC;
200 _ff_push_const_addr(ff_buf, app_buf, nLin_4n_bytes);
201 ff_buf += nLin_4n_bytes;
202
203 // There could be odd 1-3 bytes before the wrap-around boundary
204 volatile const uint32_t * rx_fifo = (volatile const uint32_t *) app_buf;
205 uint8_t rem = nLin_bytes & 0x03;
206 if (rem > 0)
207 {
208 uint8_t remrem = tu_min16(nWrap_bytes, 4-rem);
209 nWrap_bytes -= remrem;
210
211 uint32_t tmp32 = *rx_fifo;
212 uint8_t * src_u8 = ((uint8_t *) &tmp32);
213
214 // Write 1-3 bytes before wrapped boundary
215 while(rem--) *ff_buf++ = *src_u8++;
216
217 // Read more bytes to beginning to complete a word
218 ff_buf = f->buffer;
219 while(remrem--) *ff_buf++ = *src_u8++;
220 }
221 else
222 {
223 ff_buf = f->buffer; // wrap around to beginning
224 }
225
226 // Write data wrapped part
227 if (nWrap_bytes > 0) _ff_push_const_addr(ff_buf, app_buf, nWrap_bytes);
228 }
229 break;
230 }
231}
232
233// get one item from FIFO WITHOUT updating read pointer
234static inline void _ff_pull(tu_fifo_t* f, void * app_buf, uint16_t rel)
235{
236 memcpy(app_buf, f->buffer + (rel * f->item_size), f->item_size);
237}
238
239// get n items from FIFO WITHOUT updating read pointer
240static void _ff_pull_n(tu_fifo_t* f, void* app_buf, uint16_t n, uint16_t rel, tu_fifo_copy_mode_t copy_mode)
241{
242 uint16_t const nLin = f->depth - rel;
243 uint16_t const nWrap = n - nLin; // only used if wrapped
244
245 uint16_t nLin_bytes = nLin * f->item_size;
246 uint16_t nWrap_bytes = nWrap * f->item_size;
247
248 // current buffer of fifo
249 uint8_t* ff_buf = f->buffer + (rel * f->item_size);
250
251 switch (copy_mode)
252 {
253 case TU_FIFO_COPY_INC:
254 if ( n <= nLin )
255 {
256 // Linear only
257 memcpy(app_buf, ff_buf, n*f->item_size);
258 }
259 else
260 {
261 // Wrap around
262
263 // Read data from linear part of buffer
264 memcpy(app_buf, ff_buf, nLin_bytes);
265
266 // Read data wrapped part
267 memcpy((uint8_t*) app_buf + nLin_bytes, f->buffer, nWrap_bytes);
268 }
269 break;
270
271 case TU_FIFO_COPY_CST_FULL_WORDS:
272 if ( n <= nLin )
273 {
274 // Linear only
275 _ff_pull_const_addr(app_buf, ff_buf, n*f->item_size);
276 }
277 else
278 {
279 // Wrap around case
280
281 // Read full words from linear part of buffer
282 uint16_t nLin_4n_bytes = nLin_bytes & 0xFFFC;
283 _ff_pull_const_addr(app_buf, ff_buf, nLin_4n_bytes);
284 ff_buf += nLin_4n_bytes;
285
286 // There could be odd 1-3 bytes before the wrap-around boundary
287 volatile uint32_t * tx_fifo = (volatile uint32_t *) app_buf;
288 uint8_t rem = nLin_bytes & 0x03;
289 if (rem > 0)
290 {
291 uint8_t remrem = tu_min16(nWrap_bytes, 4-rem);
292 nWrap_bytes -= remrem;
293
294 uint32_t tmp32=0;
295 uint8_t * dst_u8 = (uint8_t *)&tmp32;
296
297 // Read 1-3 bytes before wrapped boundary
298 while(rem--) *dst_u8++ = *ff_buf++;
299
300 // Read more bytes from beginning to complete a word
301 ff_buf = f->buffer;
302 while(remrem--) *dst_u8++ = *ff_buf++;
303
304 *tx_fifo = tmp32;
305 }
306 else
307 {
308 ff_buf = f->buffer; // wrap around to beginning
309 }
310
311 // Read data wrapped part
312 if (nWrap_bytes > 0) _ff_pull_const_addr(app_buf, ff_buf, nWrap_bytes);
313 }
314 break;
315
316 default: break;
317 }
318}
319
320// Advance an absolute pointer
321static uint16_t advance_pointer(tu_fifo_t* f, uint16_t p, uint16_t offset)
322{
323 // We limit the index space of p such that a correct wrap around happens
324 // Check for a wrap around or if we are in unused index space - This has to be checked first!!
325 // We are exploiting the wrap around to the correct index
326 if ((p > (uint16_t)(p + offset)) || ((uint16_t)(p + offset) > f->max_pointer_idx))
327 {
328 p = (p + offset) + f->non_used_index_space;
329 }
330 else
331 {
332 p += offset;
333 }
334 return p;
335}
336
337// Backward an absolute pointer
338static uint16_t backward_pointer(tu_fifo_t* f, uint16_t p, uint16_t offset)
339{
340 // We limit the index space of p such that a correct wrap around happens
341 // Check for a wrap around or if we are in unused index space - This has to be checked first!!
342 // We are exploiting the wrap around to the correct index
343 if ((p < (uint16_t)(p - offset)) || ((uint16_t)(p - offset) > f->max_pointer_idx))
344 {
345 p = (p - offset) - f->non_used_index_space;
346 }
347 else
348 {
349 p -= offset;
350 }
351 return p;
352}
353
354// get relative from absolute pointer
355static uint16_t get_relative_pointer(tu_fifo_t* f, uint16_t p)
356{
357 return _ff_mod(p, f->depth);
358}
359
360// Works on local copies of w and r - return only the difference and as such can be used to determine an overflow
361static inline uint16_t _tu_fifo_count(tu_fifo_t* f, uint16_t wAbs, uint16_t rAbs)
362{
363 uint16_t cnt = wAbs-rAbs;
364
365 // In case we have non-power of two depth we need a further modification
366 if (rAbs > wAbs) cnt -= f->non_used_index_space;
367
368 return cnt;
369}
370
371// Works on local copies of w and r
372static inline bool _tu_fifo_empty(uint16_t wAbs, uint16_t rAbs)
373{
374 return wAbs == rAbs;
375}
376
377// Works on local copies of w and r
378static inline bool _tu_fifo_full(tu_fifo_t* f, uint16_t wAbs, uint16_t rAbs)
379{
380 return (_tu_fifo_count(f, wAbs, rAbs) == f->depth);
381}
382
383// Works on local copies of w and r
384// BE AWARE - THIS FUNCTION MIGHT NOT GIVE A CORRECT ANSWERE IN CASE WRITE POINTER "OVERFLOWS"
385// Only one overflow is allowed for this function to work e.g. if depth = 100, you must not
386// write more than 2*depth-1 items in one rush without updating write pointer. Otherwise
387// write pointer wraps and you pointer states are messed up. This can only happen if you
388// use DMAs, write functions do not allow such an error.
389static inline bool _tu_fifo_overflowed(tu_fifo_t* f, uint16_t wAbs, uint16_t rAbs)
390{
391 return (_tu_fifo_count(f, wAbs, rAbs) > f->depth);
392}
393
394// Works on local copies of w
395// For more details see _tu_fifo_overflow()!
396static inline void _tu_fifo_correct_read_pointer(tu_fifo_t* f, uint16_t wAbs)
397{
398 f->rd_idx = backward_pointer(f, wAbs, f->depth);
399}
400
401// Works on local copies of w and r
402// Must be protected by mutexes since in case of an overflow read pointer gets modified
403static bool _tu_fifo_peek(tu_fifo_t* f, void * p_buffer, uint16_t wAbs, uint16_t rAbs)
404{
405 uint16_t cnt = _tu_fifo_count(f, wAbs, rAbs);
406
407 // Check overflow and correct if required
408 if (cnt > f->depth)
409 {
410 _tu_fifo_correct_read_pointer(f, wAbs);
411 cnt = f->depth;
412 }
413
414 // Skip beginning of buffer
415 if (cnt == 0) return false;
416
417 uint16_t rRel = get_relative_pointer(f, rAbs);
418
419 // Peek data
420 _ff_pull(f, p_buffer, rRel);
421
422 return true;
423}
424
425// Works on local copies of w and r
426// Must be protected by mutexes since in case of an overflow read pointer gets modified
427static uint16_t _tu_fifo_peek_n(tu_fifo_t* f, void * p_buffer, uint16_t n, uint16_t wAbs, uint16_t rAbs, tu_fifo_copy_mode_t copy_mode)
428{
429 uint16_t cnt = _tu_fifo_count(f, wAbs, rAbs);
430
431 // Check overflow and correct if required
432 if (cnt > f->depth)
433 {
434 _tu_fifo_correct_read_pointer(f, wAbs);
435 rAbs = f->rd_idx;
436 cnt = f->depth;
437 }
438
439 // Skip beginning of buffer
440 if (cnt == 0) return 0;
441
442 // Check if we can read something at and after offset - if too less is available we read what remains
443 if (cnt < n) n = cnt;
444
445 uint16_t rRel = get_relative_pointer(f, rAbs);
446
447 // Peek data
448 _ff_pull_n(f, p_buffer, n, rRel, copy_mode);
449
450 return n;
451}
452
453// Works on local copies of w and r
454static inline uint16_t _tu_fifo_remaining(tu_fifo_t* f, uint16_t wAbs, uint16_t rAbs)
455{
456 return f->depth - _tu_fifo_count(f, wAbs, rAbs);
457}
458
459static uint16_t _tu_fifo_write_n(tu_fifo_t* f, const void * data, uint16_t n, tu_fifo_copy_mode_t copy_mode)
460{
461 if ( n == 0 ) return 0;
462
463 _ff_lock(f->mutex_wr);
464
465 uint16_t w = f->wr_idx, r = f->rd_idx;
466 uint8_t const* buf8 = (uint8_t const*) data;
467
468 if (!f->overwritable)
469 {
470 // Not overwritable limit up to full
471 n = tu_min16(n, _tu_fifo_remaining(f, w, r));
472 }
473 else if (n >= f->depth)
474 {
475 // Only copy last part
476 buf8 = buf8 + (n - f->depth) * f->item_size;
477 n = f->depth;
478
479 // We start writing at the read pointer's position since we fill the complete
480 // buffer and we do not want to modify the read pointer within a write function!
481 // This would end up in a race condition with read functions!
482 w = r;
483 }
484
485 uint16_t wRel = get_relative_pointer(f, w);
486
487 // Write data
488 _ff_push_n(f, buf8, n, wRel, copy_mode);
489
490 // Advance pointer
491 f->wr_idx = advance_pointer(f, w, n);
492
493 _ff_unlock(f->mutex_wr);
494
495 return n;
496}
497
498static uint16_t _tu_fifo_read_n(tu_fifo_t* f, void * buffer, uint16_t n, tu_fifo_copy_mode_t copy_mode)
499{
500 _ff_lock(f->mutex_rd);
501
502 // Peek the data
503 // f->rd_idx might get modified in case of an overflow so we can not use a local variable
504 n = _tu_fifo_peek_n(f, buffer, n, f->wr_idx, f->rd_idx, copy_mode);
505
506 // Advance read pointer
507 f->rd_idx = advance_pointer(f, f->rd_idx, n);
508
509 _ff_unlock(f->mutex_rd);
510 return n;
511}
512
513/******************************************************************************/
514/*!
515 @brief Get number of items in FIFO.
516
517 As this function only reads the read and write pointers once, this function is
518 reentrant and thus thread and ISR save without any mutexes. In case an
519 overflow occurred, this function return f.depth at maximum. Overflows are
520 checked and corrected for in the read functions!
521
522 @param[in] f
523 Pointer to the FIFO buffer to manipulate
524
525 @returns Number of items in FIFO
526 */
527/******************************************************************************/
528uint16_t tu_fifo_count(tu_fifo_t* f)
529{
530 return tu_min16(_tu_fifo_count(f, f->wr_idx, f->rd_idx), f->depth);
531}
532
533/******************************************************************************/
534/*!
535 @brief Check if FIFO is empty.
536
537 As this function only reads the read and write pointers once, this function is
538 reentrant and thus thread and ISR save without any mutexes.
539
540 @param[in] f
541 Pointer to the FIFO buffer to manipulate
542
543 @returns Number of items in FIFO
544 */
545/******************************************************************************/
546bool tu_fifo_empty(tu_fifo_t* f)
547{
548 return _tu_fifo_empty(f->wr_idx, f->rd_idx);
549}
550
551/******************************************************************************/
552/*!
553 @brief Check if FIFO is full.
554
555 As this function only reads the read and write pointers once, this function is
556 reentrant and thus thread and ISR save without any mutexes.
557
558 @param[in] f
559 Pointer to the FIFO buffer to manipulate
560
561 @returns Number of items in FIFO
562 */
563/******************************************************************************/
564bool tu_fifo_full(tu_fifo_t* f)
565{
566 return _tu_fifo_full(f, f->wr_idx, f->rd_idx);
567}
568
569/******************************************************************************/
570/*!
571 @brief Get remaining space in FIFO.
572
573 As this function only reads the read and write pointers once, this function is
574 reentrant and thus thread and ISR save without any mutexes.
575
576 @param[in] f
577 Pointer to the FIFO buffer to manipulate
578
579 @returns Number of items in FIFO
580 */
581/******************************************************************************/
582uint16_t tu_fifo_remaining(tu_fifo_t* f)
583{
584 return _tu_fifo_remaining(f, f->wr_idx, f->rd_idx);
585}
586
587/******************************************************************************/
588/*!
589 @brief Check if overflow happened.
590
591 BE AWARE - THIS FUNCTION MIGHT NOT GIVE A CORRECT ANSWERE IN CASE WRITE POINTER "OVERFLOWS"
592 Only one overflow is allowed for this function to work e.g. if depth = 100, you must not
593 write more than 2*depth-1 items in one rush without updating write pointer. Otherwise
594 write pointer wraps and your pointer states are messed up. This can only happen if you
595 use DMAs, write functions do not allow such an error. Avoid such nasty things!
596
597 All reading functions (read, peek) check for overflows and correct read pointer on their own such
598 that latest items are read.
599 If required (e.g. for DMA use) you can also correct the read pointer by
600 tu_fifo_correct_read_pointer().
601
602 @param[in] f
603 Pointer to the FIFO buffer to manipulate
604
605 @returns True if overflow happened
606 */
607/******************************************************************************/
608bool tu_fifo_overflowed(tu_fifo_t* f)
609{
610 return _tu_fifo_overflowed(f, f->wr_idx, f->rd_idx);
611}
612
613// Only use in case tu_fifo_overflow() returned true!
614void tu_fifo_correct_read_pointer(tu_fifo_t* f)
615{
616 _ff_lock(f->mutex_rd);
617 _tu_fifo_correct_read_pointer(f, f->wr_idx);
618 _ff_unlock(f->mutex_rd);
619}
620
621/******************************************************************************/
622/*!
623 @brief Read one element out of the buffer.
624
625 This function will return the element located at the array index of the
626 read pointer, and then increment the read pointer index.
627 This function checks for an overflow and corrects read pointer if required.
628
629 @param[in] f
630 Pointer to the FIFO buffer to manipulate
631 @param[in] buffer
632 Pointer to the place holder for data read from the buffer
633
634 @returns TRUE if the queue is not empty
635 */
636/******************************************************************************/
637bool tu_fifo_read(tu_fifo_t* f, void * buffer)
638{
639 _ff_lock(f->mutex_rd);
640
641 // Peek the data
642 // f->rd_idx might get modified in case of an overflow so we can not use a local variable
643 bool ret = _tu_fifo_peek(f, buffer, f->wr_idx, f->rd_idx);
644
645 // Advance pointer
646 f->rd_idx = advance_pointer(f, f->rd_idx, ret);
647
648 _ff_unlock(f->mutex_rd);
649 return ret;
650}
651
652/******************************************************************************/
653/*!
654 @brief This function will read n elements from the array index specified by
655 the read pointer and increment the read index.
656 This function checks for an overflow and corrects read pointer if required.
657
658 @param[in] f
659 Pointer to the FIFO buffer to manipulate
660 @param[in] buffer
661 The pointer to data location
662 @param[in] n
663 Number of element that buffer can afford
664
665 @returns number of items read from the FIFO
666 */
667/******************************************************************************/
668uint16_t tu_fifo_read_n(tu_fifo_t* f, void * buffer, uint16_t n)
669{
670 return _tu_fifo_read_n(f, buffer, n, TU_FIFO_COPY_INC);
671}
672
673uint16_t tu_fifo_read_n_const_addr_full_words(tu_fifo_t* f, void * buffer, uint16_t n)
674{
675 return _tu_fifo_read_n(f, buffer, n, TU_FIFO_COPY_CST_FULL_WORDS);
676}
677
678/******************************************************************************/
679/*!
680 @brief Read one item without removing it from the FIFO.
681 This function checks for an overflow and corrects read pointer if required.
682
683 @param[in] f
684 Pointer to the FIFO buffer to manipulate
685 @param[in] offset
686 Position to read from in the FIFO buffer with respect to read pointer
687 @param[in] p_buffer
688 Pointer to the place holder for data read from the buffer
689
690 @returns TRUE if the queue is not empty
691 */
692/******************************************************************************/
693bool tu_fifo_peek(tu_fifo_t* f, void * p_buffer)
694{
695 _ff_lock(f->mutex_rd);
696 bool ret = _tu_fifo_peek(f, p_buffer, f->wr_idx, f->rd_idx);
697 _ff_unlock(f->mutex_rd);
698 return ret;
699}
700
701/******************************************************************************/
702/*!
703 @brief Read n items without removing it from the FIFO
704 This function checks for an overflow and corrects read pointer if required.
705
706 @param[in] f
707 Pointer to the FIFO buffer to manipulate
708 @param[in] p_buffer
709 Pointer to the place holder for data read from the buffer
710 @param[in] n
711 Number of items to peek
712
713 @returns Number of bytes written to p_buffer
714 */
715/******************************************************************************/
716uint16_t tu_fifo_peek_n(tu_fifo_t* f, void * p_buffer, uint16_t n)
717{
718 _ff_lock(f->mutex_rd);
719 bool ret = _tu_fifo_peek_n(f, p_buffer, n, f->wr_idx, f->rd_idx, TU_FIFO_COPY_INC);
720 _ff_unlock(f->mutex_rd);
721 return ret;
722}
723
724/******************************************************************************/
725/*!
726 @brief Write one element into the buffer.
727
728 This function will write one element into the array index specified by
729 the write pointer and increment the write index.
730
731 @param[in] f
732 Pointer to the FIFO buffer to manipulate
733 @param[in] data
734 The byte to add to the FIFO
735
736 @returns TRUE if the data was written to the FIFO (overwrittable
737 FIFO will always return TRUE)
738 */
739/******************************************************************************/
740bool tu_fifo_write(tu_fifo_t* f, const void * data)
741{
742 _ff_lock(f->mutex_wr);
743
744 uint16_t w = f->wr_idx;
745
746 if ( _tu_fifo_full(f, w, f->rd_idx) && !f->overwritable ) return false;
747
748 uint16_t wRel = get_relative_pointer(f, w);
749
750 // Write data
751 _ff_push(f, data, wRel);
752
753 // Advance pointer
754 f->wr_idx = advance_pointer(f, w, 1);
755
756 _ff_unlock(f->mutex_wr);
757
758 return true;
759}
760
761/******************************************************************************/
762/*!
763 @brief This function will write n elements into the array index specified by
764 the write pointer and increment the write index.
765
766 @param[in] f
767 Pointer to the FIFO buffer to manipulate
768 @param[in] data
769 The pointer to data to add to the FIFO
770 @param[in] count
771 Number of element
772 @return Number of written elements
773 */
774/******************************************************************************/
775uint16_t tu_fifo_write_n(tu_fifo_t* f, const void * data, uint16_t n)
776{
777 return _tu_fifo_write_n(f, data, n, TU_FIFO_COPY_INC);
778}
779
780/******************************************************************************/
781/*!
782 @brief This function will write n elements into the array index specified by
783 the write pointer and increment the write index. The source address will
784 not be incremented which is useful for reading from registers.
785
786 @param[in] f
787 Pointer to the FIFO buffer to manipulate
788 @param[in] data
789 The pointer to data to add to the FIFO
790 @param[in] count
791 Number of element
792 @return Number of written elements
793 */
794/******************************************************************************/
795uint16_t tu_fifo_write_n_const_addr_full_words(tu_fifo_t* f, const void * data, uint16_t n)
796{
797 return _tu_fifo_write_n(f, data, n, TU_FIFO_COPY_CST_FULL_WORDS);
798}
799
800/******************************************************************************/
801/*!
802 @brief Clear the fifo read and write pointers
803
804 @param[in] f
805 Pointer to the FIFO buffer to manipulate
806 */
807/******************************************************************************/
808bool tu_fifo_clear(tu_fifo_t *f)
809{
810 _ff_lock(f->mutex_wr);
811 _ff_lock(f->mutex_rd);
812
813 f->rd_idx = f->wr_idx = 0;
814 f->max_pointer_idx = 2*f->depth-1;
815 f->non_used_index_space = UINT16_MAX - f->max_pointer_idx;
816
817 _ff_unlock(f->mutex_wr);
818 _ff_unlock(f->mutex_rd);
819 return true;
820}
821
822/******************************************************************************/
823/*!
824 @brief Change the fifo mode to overwritable or not overwritable
825
826 @param[in] f
827 Pointer to the FIFO buffer to manipulate
828 @param[in] overwritable
829 Overwritable mode the fifo is set to
830 */
831/******************************************************************************/
832bool tu_fifo_set_overwritable(tu_fifo_t *f, bool overwritable)
833{
834 _ff_lock(f->mutex_wr);
835 _ff_lock(f->mutex_rd);
836
837 f->overwritable = overwritable;
838
839 _ff_unlock(f->mutex_wr);
840 _ff_unlock(f->mutex_rd);
841
842 return true;
843}
844
845/******************************************************************************/
846/*!
847 @brief Advance write pointer - intended to be used in combination with DMA.
848 It is possible to fill the FIFO by use of a DMA in circular mode. Within
849 DMA ISRs you may update the write pointer to be able to read from the FIFO.
850 As long as the DMA is the only process writing into the FIFO this is safe
851 to use.
852
853 USE WITH CARE - WE DO NOT CONDUCT SAFTY CHECKS HERE!
854
855 @param[in] f
856 Pointer to the FIFO buffer to manipulate
857 @param[in] n
858 Number of items the write pointer moves forward
859 */
860/******************************************************************************/
861void tu_fifo_advance_write_pointer(tu_fifo_t *f, uint16_t n)
862{
863 f->wr_idx = advance_pointer(f, f->wr_idx, n);
864}
865
866/******************************************************************************/
867/*!
868 @brief Advance read pointer - intended to be used in combination with DMA.
869 It is possible to read from the FIFO by use of a DMA in linear mode. Within
870 DMA ISRs you may update the read pointer to be able to again write into the
871 FIFO. As long as the DMA is the only process reading from the FIFO this is
872 safe to use.
873
874 USE WITH CARE - WE DO NOT CONDUCT SAFTY CHECKS HERE!
875
876 @param[in] f
877 Pointer to the FIFO buffer to manipulate
878 @param[in] n
879 Number of items the read pointer moves forward
880 */
881/******************************************************************************/
882void tu_fifo_advance_read_pointer(tu_fifo_t *f, uint16_t n)
883{
884 f->rd_idx = advance_pointer(f, f->rd_idx, n);
885}
886
887/******************************************************************************/
888/*!
889 @brief Get read info
890
891 Returns the length and pointer from which bytes can be read in a linear manner.
892 This is of major interest for DMA transmissions. If returned length is zero the
893 corresponding pointer is invalid.
894 The read pointer does NOT get advanced, use tu_fifo_advance_read_pointer() to
895 do so!
896 @param[in] f
897 Pointer to FIFO
898 @param[out] *info
899 Pointer to struct which holds the desired infos
900 */
901/******************************************************************************/
902void tu_fifo_get_read_info(tu_fifo_t *f, tu_fifo_buffer_info_t *info)
903{
904 // Operate on temporary values in case they change in between
905 uint16_t w = f->wr_idx, r = f->rd_idx;
906
907 uint16_t cnt = _tu_fifo_count(f, w, r);
908
909 // Check overflow and correct if required - may happen in case a DMA wrote too fast
910 if (cnt > f->depth)
911 {
912 _ff_lock(f->mutex_rd);
913 _tu_fifo_correct_read_pointer(f, w);
914 _ff_unlock(f->mutex_rd);
915 r = f->rd_idx;
916 cnt = f->depth;
917 }
918
919 // Check if fifo is empty
920 if (cnt == 0)
921 {
922 info->len_lin = 0;
923 info->len_wrap = 0;
924 info->ptr_lin = NULL;
925 info->ptr_wrap = NULL;
926 return;
927 }
928
929 // Get relative pointers
930 w = get_relative_pointer(f, w);
931 r = get_relative_pointer(f, r);
932
933 // Copy pointer to buffer to start reading from
934 info->ptr_lin = &f->buffer[r];
935
936 // Check if there is a wrap around necessary
937 if (w > r) {
938 // Non wrapping case
939 info->len_lin = cnt;
940 info->len_wrap = 0;
941 info->ptr_wrap = NULL;
942 }
943 else
944 {
945 info->len_lin = f->depth - r; // Also the case if FIFO was full
946 info->len_wrap = cnt - info->len_lin;
947 info->ptr_wrap = f->buffer;
948 }
949}
950
951/******************************************************************************/
952/*!
953 @brief Get linear write info
954
955 Returns the length and pointer to which bytes can be written into FIFO in a linear manner.
956 This is of major interest for DMA transmissions not using circular mode. If a returned length is zero the
957 corresponding pointer is invalid. The returned lengths summed up are the currently free space in the FIFO.
958 The write pointer does NOT get advanced, use tu_fifo_advance_write_pointer() to do so!
959 TAKE CARE TO NOT OVERFLOW THE BUFFER MORE THAN TWO TIMES THE FIFO DEPTH - IT CAN NOT RECOVERE OTHERWISE!
960 @param[in] f
961 Pointer to FIFO
962 @param[out] *info
963 Pointer to struct which holds the desired infos
964 */
965/******************************************************************************/
966void tu_fifo_get_write_info(tu_fifo_t *f, tu_fifo_buffer_info_t *info)
967{
968 uint16_t w = f->wr_idx, r = f->rd_idx;
969 uint16_t free = _tu_fifo_remaining(f, w, r);
970
971 if (free == 0)
972 {
973 info->len_lin = 0;
974 info->len_wrap = 0;
975 info->ptr_lin = NULL;
976 info->ptr_wrap = NULL;
977 return;
978 }
979
980 // Get relative pointers
981 w = get_relative_pointer(f, w);
982 r = get_relative_pointer(f, r);
983
984 // Copy pointer to buffer to start writing to
985 info->ptr_lin = &f->buffer[w];
986
987 if (w < r)
988 {
989 // Non wrapping case
990 info->len_lin = r-w;
991 info->len_wrap = 0;
992 info->ptr_wrap = NULL;
993 }
994 else
995 {
996 info->len_lin = f->depth - w;
997 info->len_wrap = free - info->len_lin; // Remaining length - n already was limited to free or FIFO depth
998 info->ptr_wrap = f->buffer; // Always start of buffer
999 }
1000}