|
|
@@ -39,34 +39,38 @@
|
|
|
// implement mutex lock and unlock
|
|
|
#if CFG_FIFO_MUTEX
|
|
|
|
|
|
-static void tu_fifo_lock(tu_fifo_t *f)
|
|
|
+static inline void _ff_lock(tu_fifo_mutex_t mutex)
|
|
|
{
|
|
|
- if (f->mutex)
|
|
|
- {
|
|
|
- osal_mutex_lock(f->mutex, OSAL_TIMEOUT_WAIT_FOREVER);
|
|
|
- }
|
|
|
+ if (mutex) osal_mutex_lock(mutex, OSAL_TIMEOUT_WAIT_FOREVER);
|
|
|
}
|
|
|
|
|
|
-static void tu_fifo_unlock(tu_fifo_t *f)
|
|
|
+static inline void _ff_unlock(tu_fifo_mutex_t mutex)
|
|
|
{
|
|
|
- if (f->mutex)
|
|
|
- {
|
|
|
- osal_mutex_unlock(f->mutex);
|
|
|
- }
|
|
|
+ if (mutex) osal_mutex_unlock(mutex);
|
|
|
}
|
|
|
|
|
|
#else
|
|
|
|
|
|
-#define tu_fifo_lock(_ff)
|
|
|
-#define tu_fifo_unlock(_ff)
|
|
|
+#define _ff_lock(_mutex)
|
|
|
+#define _ff_unlock(_mutex)
|
|
|
|
|
|
#endif
|
|
|
|
|
|
+/** \enum tu_fifo_copy_mode_t
|
|
|
+ * \brief Write modes intended to allow special read and write functions to be able to copy data to and from USB hardware FIFOs as needed for e.g. STM32s and others
|
|
|
+ */
|
|
|
+typedef enum
|
|
|
+{
|
|
|
+ TU_FIFO_COPY_INC, ///< Copy from/to an increasing source/destination address - default mode
|
|
|
+ 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
|
|
|
+} tu_fifo_copy_mode_t;
|
|
|
+
|
|
|
bool tu_fifo_config(tu_fifo_t *f, void* buffer, uint16_t depth, uint16_t item_size, bool overwritable)
|
|
|
{
|
|
|
if (depth > 0x8000) return false; // Maximum depth is 2^15 items
|
|
|
|
|
|
- tu_fifo_lock(f);
|
|
|
+ _ff_lock(f->mutex_wr);
|
|
|
+ _ff_lock(f->mutex_rd);
|
|
|
|
|
|
f->buffer = (uint8_t*) buffer;
|
|
|
f->depth = depth;
|
|
|
@@ -78,66 +82,236 @@ bool tu_fifo_config(tu_fifo_t *f, void* buffer, uint16_t depth, uint16_t item_si
|
|
|
|
|
|
f->rd_idx = f->wr_idx = 0;
|
|
|
|
|
|
- tu_fifo_unlock(f);
|
|
|
+ _ff_unlock(f->mutex_wr);
|
|
|
+ _ff_unlock(f->mutex_rd);
|
|
|
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
// Static functions are intended to work on local variables
|
|
|
-
|
|
|
static inline uint16_t _ff_mod(uint16_t idx, uint16_t depth)
|
|
|
{
|
|
|
while ( idx >= depth) idx -= depth;
|
|
|
return idx;
|
|
|
}
|
|
|
|
|
|
-// send one item to FIFO WITHOUT updating write pointer
|
|
|
-static inline void _ff_push(tu_fifo_t* f, void const * data, uint16_t wRel)
|
|
|
+// Intended to be used to read from hardware USB FIFO in e.g. STM32 where all data is read from a constant address
|
|
|
+// Code adapted from dcd_synopsis.c
|
|
|
+// TODO generalize with configurable 1 byte or 4 byte each read
|
|
|
+static void _ff_push_const_addr(uint8_t * ff_buf, const void * app_buf, uint16_t len)
|
|
|
{
|
|
|
- memcpy(f->buffer + (wRel * f->item_size), data, f->item_size);
|
|
|
+ volatile uint32_t * rx_fifo = (volatile uint32_t *) app_buf;
|
|
|
+
|
|
|
+ // Reading full available 32 bit words from const app address
|
|
|
+ uint16_t full_words = len >> 2;
|
|
|
+ while(full_words--)
|
|
|
+ {
|
|
|
+ tu_unaligned_write32(ff_buf, *rx_fifo);
|
|
|
+ ff_buf += 4;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Read the remaining 1-3 bytes from const app address
|
|
|
+ uint8_t const bytes_rem = len & 0x03;
|
|
|
+ if ( bytes_rem )
|
|
|
+ {
|
|
|
+ uint32_t tmp32 = *rx_fifo;
|
|
|
+ memcpy(ff_buf, &tmp32, bytes_rem);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
-// send n items to FIFO WITHOUT updating write pointer
|
|
|
-static void _ff_push_n(tu_fifo_t* f, void const * data, uint16_t n, uint16_t wRel)
|
|
|
+// Intended to be used to write to hardware USB FIFO in e.g. STM32
|
|
|
+// where all data is written to a constant address in full word copies
|
|
|
+static void _ff_pull_const_addr(void * app_buf, const uint8_t * ff_buf, uint16_t len)
|
|
|
{
|
|
|
- if(wRel + n <= f->depth) // Linear mode only
|
|
|
+ volatile uint32_t * tx_fifo = (volatile uint32_t *) app_buf;
|
|
|
+
|
|
|
+ // Pushing full available 32 bit words to const app address
|
|
|
+ uint16_t full_words = len >> 2;
|
|
|
+ while(full_words--)
|
|
|
{
|
|
|
- memcpy(f->buffer + (wRel * f->item_size), data, n*f->item_size);
|
|
|
+ *tx_fifo = tu_unaligned_read32(ff_buf);
|
|
|
+ ff_buf += 4;
|
|
|
}
|
|
|
- else // Wrap around
|
|
|
+
|
|
|
+ // Write the remaining 1-3 bytes into const app address
|
|
|
+ uint8_t const bytes_rem = len & 0x03;
|
|
|
+ if ( bytes_rem )
|
|
|
{
|
|
|
- uint16_t nLin = f->depth - wRel;
|
|
|
+ uint32_t tmp32 = 0;
|
|
|
+ memcpy(&tmp32, ff_buf, bytes_rem);
|
|
|
+
|
|
|
+ *tx_fifo = tmp32;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// send one item to FIFO WITHOUT updating write pointer
|
|
|
+static inline void _ff_push(tu_fifo_t* f, void const * app_buf, uint16_t rel)
|
|
|
+{
|
|
|
+ memcpy(f->buffer + (rel * f->item_size), app_buf, f->item_size);
|
|
|
+}
|
|
|
+
|
|
|
+// send n items to FIFO WITHOUT updating write pointer
|
|
|
+static 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)
|
|
|
+{
|
|
|
+ uint16_t const nLin = f->depth - rel;
|
|
|
+ uint16_t const nWrap = n - nLin;
|
|
|
+
|
|
|
+ uint16_t nLin_bytes = nLin * f->item_size;
|
|
|
+ uint16_t nWrap_bytes = nWrap * f->item_size;
|
|
|
|
|
|
- // Write data to linear part of buffer
|
|
|
- memcpy(f->buffer + (wRel * f->item_size), data, nLin*f->item_size);
|
|
|
+ // current buffer of fifo
|
|
|
+ uint8_t* ff_buf = f->buffer + (rel * f->item_size);
|
|
|
|
|
|
- // Write data wrapped around
|
|
|
- memcpy(f->buffer, ((uint8_t const*) data) + nLin*f->item_size, (n - nLin) * f->item_size);
|
|
|
+ switch (copy_mode)
|
|
|
+ {
|
|
|
+ case TU_FIFO_COPY_INC:
|
|
|
+ if(n <= nLin)
|
|
|
+ {
|
|
|
+ // Linear only
|
|
|
+ memcpy(ff_buf, app_buf, n*f->item_size);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ // Wrap around
|
|
|
+
|
|
|
+ // Write data to linear part of buffer
|
|
|
+ memcpy(ff_buf, app_buf, nLin_bytes);
|
|
|
+
|
|
|
+ // Write data wrapped around
|
|
|
+ memcpy(f->buffer, ((uint8_t const*) app_buf) + nLin_bytes, nWrap_bytes);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+
|
|
|
+ case TU_FIFO_COPY_CST_FULL_WORDS:
|
|
|
+ // Intended for hardware buffers from which it can be read word by word only
|
|
|
+ if(n <= nLin)
|
|
|
+ {
|
|
|
+ // Linear only
|
|
|
+ _ff_push_const_addr(ff_buf, app_buf, n*f->item_size);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ // Wrap around case
|
|
|
+
|
|
|
+ // Write full words to linear part of buffer
|
|
|
+ uint16_t nLin_4n_bytes = nLin_bytes & 0xFFFC;
|
|
|
+ _ff_push_const_addr(ff_buf, app_buf, nLin_4n_bytes);
|
|
|
+ ff_buf += nLin_4n_bytes;
|
|
|
+
|
|
|
+ // There could be odd 1-3 bytes before the wrap-around boundary
|
|
|
+ volatile uint32_t * rx_fifo = (volatile uint32_t *) app_buf;
|
|
|
+ uint8_t rem = nLin_bytes & 0x03;
|
|
|
+ if (rem > 0)
|
|
|
+ {
|
|
|
+ uint8_t remrem = tu_min16(nWrap_bytes, 4-rem);
|
|
|
+ nWrap_bytes -= remrem;
|
|
|
+
|
|
|
+ uint32_t tmp32 = *rx_fifo;
|
|
|
+ uint8_t * src_u8 = ((uint8_t *) &tmp32);
|
|
|
+
|
|
|
+ // Write 1-3 bytes before wrapped boundary
|
|
|
+ while(rem--) *ff_buf++ = *src_u8++;
|
|
|
+
|
|
|
+ // Read more bytes to beginning to complete a word
|
|
|
+ ff_buf = f->buffer;
|
|
|
+ while(remrem--) *ff_buf++ = *src_u8++;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ ff_buf = f->buffer; // wrap around to beginning
|
|
|
+ }
|
|
|
+
|
|
|
+ // Write data wrapped part
|
|
|
+ if (nWrap_bytes > 0) _ff_push_const_addr(ff_buf, app_buf, nWrap_bytes);
|
|
|
+ }
|
|
|
+ break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// get one item from FIFO WITHOUT updating read pointer
|
|
|
-static inline void _ff_pull(tu_fifo_t* f, void * p_buffer, uint16_t rRel)
|
|
|
+static inline void _ff_pull(tu_fifo_t* f, void * app_buf, uint16_t rel)
|
|
|
{
|
|
|
- memcpy(p_buffer, f->buffer + (rRel * f->item_size), f->item_size);
|
|
|
+ memcpy(app_buf, f->buffer + (rel * f->item_size), f->item_size);
|
|
|
}
|
|
|
|
|
|
// get n items from FIFO WITHOUT updating read pointer
|
|
|
-static void _ff_pull_n(tu_fifo_t* f, void * p_buffer, uint16_t n, uint16_t rRel)
|
|
|
+static void _ff_pull_n(tu_fifo_t* f, void* app_buf, uint16_t n, uint16_t rel, tu_fifo_copy_mode_t copy_mode)
|
|
|
{
|
|
|
- if(rRel + n <= f->depth) // Linear mode only
|
|
|
- {
|
|
|
- memcpy(p_buffer, f->buffer + (rRel * f->item_size), n*f->item_size);
|
|
|
- }
|
|
|
- else // Wrap around
|
|
|
- {
|
|
|
- uint16_t nLin = f->depth - rRel;
|
|
|
+ uint16_t const nLin = f->depth - rel;
|
|
|
+ uint16_t const nWrap = n - nLin; // only used if wrapped
|
|
|
+
|
|
|
+ uint16_t nLin_bytes = nLin * f->item_size;
|
|
|
+ uint16_t nWrap_bytes = nWrap * f->item_size;
|
|
|
|
|
|
- // Read data from linear part of buffer
|
|
|
- memcpy(p_buffer, f->buffer + (rRel * f->item_size), nLin*f->item_size);
|
|
|
+ // current buffer of fifo
|
|
|
+ uint8_t* ff_buf = f->buffer + (rel * f->item_size);
|
|
|
|
|
|
- // Read data wrapped part
|
|
|
- memcpy((uint8_t*)p_buffer + nLin*f->item_size, f->buffer, (n - nLin) * f->item_size);
|
|
|
+ switch (copy_mode)
|
|
|
+ {
|
|
|
+ case TU_FIFO_COPY_INC:
|
|
|
+ if ( n <= nLin )
|
|
|
+ {
|
|
|
+ // Linear only
|
|
|
+ memcpy(app_buf, ff_buf, n*f->item_size);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ // Wrap around
|
|
|
+
|
|
|
+ // Read data from linear part of buffer
|
|
|
+ memcpy(app_buf, ff_buf, nLin_bytes);
|
|
|
+
|
|
|
+ // Read data wrapped part
|
|
|
+ memcpy((uint8_t*) app_buf + nLin_bytes, f->buffer, nWrap_bytes);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+
|
|
|
+ case TU_FIFO_COPY_CST_FULL_WORDS:
|
|
|
+ if ( n <= nLin )
|
|
|
+ {
|
|
|
+ // Linear only
|
|
|
+ _ff_pull_const_addr(app_buf, ff_buf, n*f->item_size);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ // Wrap around case
|
|
|
+
|
|
|
+ // Read full words from linear part of buffer
|
|
|
+ uint16_t nLin_4n_bytes = nLin_bytes & 0xFFFC;
|
|
|
+ _ff_pull_const_addr(app_buf, ff_buf, nLin_4n_bytes);
|
|
|
+ ff_buf += nLin_4n_bytes;
|
|
|
+
|
|
|
+ // There could be odd 1-3 bytes before the wrap-around boundary
|
|
|
+ volatile uint32_t * tx_fifo = (volatile uint32_t *) app_buf;
|
|
|
+ uint8_t rem = nLin_bytes & 0x03;
|
|
|
+ if (rem > 0)
|
|
|
+ {
|
|
|
+ uint8_t remrem = tu_min16(nWrap_bytes, 4-rem);
|
|
|
+ nWrap_bytes -= remrem;
|
|
|
+
|
|
|
+ uint32_t tmp32=0;
|
|
|
+ uint8_t * dst_u8 = (uint8_t *)&tmp32;
|
|
|
+
|
|
|
+ // Read 1-3 bytes before wrapped boundary
|
|
|
+ while(rem--) *dst_u8++ = *ff_buf++;
|
|
|
+
|
|
|
+ // Read more bytes from beginning to complete a word
|
|
|
+ ff_buf = f->buffer;
|
|
|
+ while(remrem--) *dst_u8++ = *ff_buf++;
|
|
|
+
|
|
|
+ *tx_fifo = tmp32;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ ff_buf = f->buffer; // wrap around to beginning
|
|
|
+ }
|
|
|
+
|
|
|
+ // Read data wrapped part
|
|
|
+ if (nWrap_bytes > 0) _ff_pull_const_addr(app_buf, ff_buf, nWrap_bytes);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+
|
|
|
+ default: break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -179,7 +353,7 @@ static uint16_t get_relative_pointer(tu_fifo_t* f, uint16_t p, uint16_t offset)
|
|
|
return _ff_mod(advance_pointer(f, p, offset), f->depth);
|
|
|
}
|
|
|
|
|
|
-// Works on local copies of w and r
|
|
|
+// Works on local copies of w and r - return only the difference and as such can be used to determine an overflow
|
|
|
static inline uint16_t _tu_fifo_count(tu_fifo_t* f, uint16_t wAbs, uint16_t rAbs)
|
|
|
{
|
|
|
uint16_t cnt = wAbs-rAbs;
|
|
|
@@ -246,7 +420,7 @@ static bool _tu_fifo_peek_at(tu_fifo_t* f, uint16_t offset, void * p_buffer, uin
|
|
|
|
|
|
// Works on local copies of w and r
|
|
|
// Must be protected by mutexes since in case of an overflow read pointer gets modified
|
|
|
-static uint16_t _tu_fifo_peek_at_n(tu_fifo_t* f, uint16_t offset, void * p_buffer, uint16_t n, uint16_t wAbs, uint16_t rAbs)
|
|
|
+static uint16_t _tu_fifo_peek_at_n(tu_fifo_t* f, uint16_t offset, void * p_buffer, uint16_t n, uint16_t wAbs, uint16_t rAbs, tu_fifo_copy_mode_t copy_mode)
|
|
|
{
|
|
|
uint16_t cnt = _tu_fifo_count(f, wAbs, rAbs);
|
|
|
|
|
|
@@ -263,15 +437,12 @@ static uint16_t _tu_fifo_peek_at_n(tu_fifo_t* f, uint16_t offset, void * p_buffe
|
|
|
|
|
|
// Check if we can read something at and after offset - if too less is available we read what remains
|
|
|
cnt -= offset;
|
|
|
- if (cnt < n) {
|
|
|
- if (cnt == 0) return 0;
|
|
|
- n = cnt;
|
|
|
- }
|
|
|
+ if (cnt < n) n = cnt;
|
|
|
|
|
|
uint16_t rRel = get_relative_pointer(f, rAbs, offset);
|
|
|
|
|
|
// Peek data
|
|
|
- _ff_pull_n(f, p_buffer, n, rRel);
|
|
|
+ _ff_pull_n(f, p_buffer, n, rRel, copy_mode);
|
|
|
|
|
|
return n;
|
|
|
}
|
|
|
@@ -282,12 +453,67 @@ static inline uint16_t _tu_fifo_remaining(tu_fifo_t* f, uint16_t wAbs, uint16_t
|
|
|
return f->depth - _tu_fifo_count(f, wAbs, rAbs);
|
|
|
}
|
|
|
|
|
|
+static uint16_t _tu_fifo_write_n(tu_fifo_t* f, const void * data, uint16_t n, tu_fifo_copy_mode_t copy_mode)
|
|
|
+{
|
|
|
+ if ( n == 0 ) return 0;
|
|
|
+
|
|
|
+ _ff_lock(f->mutex_wr);
|
|
|
+
|
|
|
+ uint16_t w = f->wr_idx, r = f->rd_idx;
|
|
|
+ uint8_t const* buf8 = (uint8_t const*) data;
|
|
|
+
|
|
|
+ if (!f->overwritable)
|
|
|
+ {
|
|
|
+ // Not overwritable limit up to full
|
|
|
+ n = tu_min16(n, _tu_fifo_remaining(f, w, r));
|
|
|
+ }
|
|
|
+ else if (n >= f->depth)
|
|
|
+ {
|
|
|
+ // Only copy last part
|
|
|
+ buf8 = buf8 + (n - f->depth) * f->item_size;
|
|
|
+ n = f->depth;
|
|
|
+
|
|
|
+ // We start writing at the read pointer's position since we fill the complete
|
|
|
+ // buffer and we do not want to modify the read pointer within a write function!
|
|
|
+ // This would end up in a race condition with read functions!
|
|
|
+ w = r;
|
|
|
+ }
|
|
|
+
|
|
|
+ uint16_t wRel = get_relative_pointer(f, w, 0);
|
|
|
+
|
|
|
+ // Write data
|
|
|
+ _ff_push_n(f, buf8, n, wRel, copy_mode);
|
|
|
+
|
|
|
+ // Advance pointer
|
|
|
+ f->wr_idx = advance_pointer(f, w, n);
|
|
|
+
|
|
|
+ _ff_unlock(f->mutex_wr);
|
|
|
+
|
|
|
+ return n;
|
|
|
+}
|
|
|
+
|
|
|
+static uint16_t _tu_fifo_read_n(tu_fifo_t* f, void * buffer, uint16_t n, tu_fifo_copy_mode_t copy_mode)
|
|
|
+{
|
|
|
+ _ff_lock(f->mutex_rd);
|
|
|
+
|
|
|
+ // Peek the data
|
|
|
+ n = _tu_fifo_peek_at_n(f, 0, buffer, n, f->wr_idx, f->rd_idx, copy_mode); // f->rd_idx might get modified in case of an overflow so we can not use a local variable
|
|
|
+
|
|
|
+ // Advance read pointer
|
|
|
+ f->rd_idx = advance_pointer(f, f->rd_idx, n);
|
|
|
+
|
|
|
+ _ff_unlock(f->mutex_rd);
|
|
|
+ return n;
|
|
|
+}
|
|
|
+
|
|
|
/******************************************************************************/
|
|
|
/*!
|
|
|
@brief Get number of items in FIFO.
|
|
|
|
|
|
As this function only reads the read and write pointers once, this function is
|
|
|
- reentrant and thus thread and ISR save without any mutexes.
|
|
|
+ reentrant and thus thread and ISR save without any mutexes. In case an
|
|
|
+ overflow occurred, this function return f.depth at maximum. Overflows are
|
|
|
+ checked and corrected for in the read functions!
|
|
|
|
|
|
@param[in] f
|
|
|
Pointer to the FIFO buffer to manipulate
|
|
|
@@ -297,7 +523,7 @@ static inline uint16_t _tu_fifo_remaining(tu_fifo_t* f, uint16_t wAbs, uint16_t
|
|
|
/******************************************************************************/
|
|
|
uint16_t tu_fifo_count(tu_fifo_t* f)
|
|
|
{
|
|
|
- return _tu_fifo_count(f, f->wr_idx, f->rd_idx);
|
|
|
+ return tu_min16(_tu_fifo_count(f, f->wr_idx, f->rd_idx), f->depth);
|
|
|
}
|
|
|
|
|
|
/******************************************************************************/
|
|
|
@@ -361,7 +587,7 @@ uint16_t tu_fifo_remaining(tu_fifo_t* f)
|
|
|
BE AWARE - THIS FUNCTION MIGHT NOT GIVE A CORRECT ANSWERE IN CASE WRITE POINTER "OVERFLOWS"
|
|
|
Only one overflow is allowed for this function to work e.g. if depth = 100, you must not
|
|
|
write more than 2*depth-1 items in one rush without updating write pointer. Otherwise
|
|
|
- write pointer wraps and you pointer states are messed up. This can only happen if you
|
|
|
+ write pointer wraps and your pointer states are messed up. This can only happen if you
|
|
|
use DMAs, write functions do not allow such an error. Avoid such nasty things!
|
|
|
|
|
|
All reading functions (read, peek) check for overflows and correct read pointer on their own such
|
|
|
@@ -383,9 +609,9 @@ bool tu_fifo_overflowed(tu_fifo_t* f)
|
|
|
// Only use in case tu_fifo_overflow() returned true!
|
|
|
void tu_fifo_correct_read_pointer(tu_fifo_t* f)
|
|
|
{
|
|
|
- tu_fifo_lock(f);
|
|
|
+ _ff_lock(f->mutex_rd);
|
|
|
_tu_fifo_correct_read_pointer(f, f->wr_idx);
|
|
|
- tu_fifo_unlock(f);
|
|
|
+ _ff_unlock(f->mutex_rd);
|
|
|
}
|
|
|
|
|
|
/******************************************************************************/
|
|
|
@@ -406,7 +632,7 @@ void tu_fifo_correct_read_pointer(tu_fifo_t* f)
|
|
|
/******************************************************************************/
|
|
|
bool tu_fifo_read(tu_fifo_t* f, void * buffer)
|
|
|
{
|
|
|
- tu_fifo_lock(f); // TODO: Here we may distinguish for read and write pointer mutexes!
|
|
|
+ _ff_lock(f->mutex_rd);
|
|
|
|
|
|
// Peek the data
|
|
|
bool ret = _tu_fifo_peek_at(f, 0, buffer, f->wr_idx, f->rd_idx); // f->rd_idx might get modified in case of an overflow so we can not use a local variable
|
|
|
@@ -414,7 +640,7 @@ bool tu_fifo_read(tu_fifo_t* f, void * buffer)
|
|
|
// Advance pointer
|
|
|
f->rd_idx = advance_pointer(f, f->rd_idx, ret);
|
|
|
|
|
|
- tu_fifo_unlock(f);
|
|
|
+ _ff_unlock(f->mutex_rd);
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
@@ -428,24 +654,20 @@ bool tu_fifo_read(tu_fifo_t* f, void * buffer)
|
|
|
Pointer to the FIFO buffer to manipulate
|
|
|
@param[in] buffer
|
|
|
The pointer to data location
|
|
|
- @param[in] count
|
|
|
+ @param[in] n
|
|
|
Number of element that buffer can afford
|
|
|
|
|
|
@returns number of items read from the FIFO
|
|
|
*/
|
|
|
/******************************************************************************/
|
|
|
-uint16_t tu_fifo_read_n(tu_fifo_t* f, void * buffer, uint16_t count)
|
|
|
+uint16_t tu_fifo_read_n(tu_fifo_t* f, void * buffer, uint16_t n)
|
|
|
{
|
|
|
- tu_fifo_lock(f); // TODO: Here we may distinguish for read and write pointer mutexes!
|
|
|
-
|
|
|
- // Peek the data
|
|
|
- count = _tu_fifo_peek_at_n(f, 0, buffer, count, f->wr_idx, f->rd_idx); // f->rd_idx might get modified in case of an overflow so we can not use a local variable
|
|
|
-
|
|
|
- // Advance read pointer
|
|
|
- f->rd_idx = advance_pointer(f, f->rd_idx, count);
|
|
|
+ return _tu_fifo_read_n(f, buffer, n, TU_FIFO_COPY_INC);
|
|
|
+}
|
|
|
|
|
|
- tu_fifo_unlock(f);
|
|
|
- return count;
|
|
|
+uint16_t tu_fifo_read_n_const_addr_full_words(tu_fifo_t* f, void * buffer, uint16_t n)
|
|
|
+{
|
|
|
+ return _tu_fifo_read_n(f, buffer, n, TU_FIFO_COPY_CST_FULL_WORDS);
|
|
|
}
|
|
|
|
|
|
/******************************************************************************/
|
|
|
@@ -465,9 +687,9 @@ uint16_t tu_fifo_read_n(tu_fifo_t* f, void * buffer, uint16_t count)
|
|
|
/******************************************************************************/
|
|
|
bool tu_fifo_peek_at(tu_fifo_t* f, uint16_t offset, void * p_buffer)
|
|
|
{
|
|
|
- tu_fifo_lock(f); // TODO: Here we may distinguish for read and write pointer mutexes!
|
|
|
+ _ff_lock(f->mutex_rd);
|
|
|
bool ret = _tu_fifo_peek_at(f, offset, p_buffer, f->wr_idx, f->rd_idx);
|
|
|
- tu_fifo_unlock(f);
|
|
|
+ _ff_unlock(f->mutex_rd);
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
@@ -490,9 +712,9 @@ bool tu_fifo_peek_at(tu_fifo_t* f, uint16_t offset, void * p_buffer)
|
|
|
/******************************************************************************/
|
|
|
uint16_t tu_fifo_peek_at_n(tu_fifo_t* f, uint16_t offset, void * p_buffer, uint16_t n)
|
|
|
{
|
|
|
- tu_fifo_lock(f); // TODO: Here we may distinguish for read and write pointer mutexes!
|
|
|
- bool ret = _tu_fifo_peek_at_n(f, offset, p_buffer, n, f->wr_idx, f->rd_idx);
|
|
|
- tu_fifo_unlock(f);
|
|
|
+ _ff_lock(f->mutex_rd);
|
|
|
+ bool ret = _tu_fifo_peek_at_n(f, offset, p_buffer, n, f->wr_idx, f->rd_idx, TU_FIFO_COPY_INC);
|
|
|
+ _ff_unlock(f->mutex_rd);
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
@@ -514,7 +736,7 @@ uint16_t tu_fifo_peek_at_n(tu_fifo_t* f, uint16_t offset, void * p_buffer, uint1
|
|
|
/******************************************************************************/
|
|
|
bool tu_fifo_write(tu_fifo_t* f, const void * data)
|
|
|
{
|
|
|
- tu_fifo_lock(f);
|
|
|
+ _ff_lock(f->mutex_wr);
|
|
|
|
|
|
uint16_t w = f->wr_idx;
|
|
|
|
|
|
@@ -528,7 +750,7 @@ bool tu_fifo_write(tu_fifo_t* f, const void * data)
|
|
|
// Advance pointer
|
|
|
f->wr_idx = advance_pointer(f, w, 1);
|
|
|
|
|
|
- tu_fifo_unlock(f);
|
|
|
+ _ff_unlock(f->mutex_wr);
|
|
|
|
|
|
return true;
|
|
|
}
|
|
|
@@ -547,43 +769,29 @@ bool tu_fifo_write(tu_fifo_t* f, const void * data)
|
|
|
@return Number of written elements
|
|
|
*/
|
|
|
/******************************************************************************/
|
|
|
-uint16_t tu_fifo_write_n(tu_fifo_t* f, const void * data, uint16_t count)
|
|
|
+uint16_t tu_fifo_write_n(tu_fifo_t* f, const void * data, uint16_t n)
|
|
|
{
|
|
|
- if ( count == 0 ) return 0;
|
|
|
-
|
|
|
- tu_fifo_lock(f);
|
|
|
-
|
|
|
- uint16_t w = f->wr_idx, r = f->rd_idx;
|
|
|
- uint8_t const* buf8 = (uint8_t const*) data;
|
|
|
-
|
|
|
- if (!f->overwritable)
|
|
|
- {
|
|
|
- // Not overwritable limit up to full
|
|
|
- count = tu_min16(count, _tu_fifo_remaining(f, w, r));
|
|
|
- }
|
|
|
- else if (count > f->depth)
|
|
|
- {
|
|
|
- // Only copy last part
|
|
|
- buf8 = buf8 + (count - f->depth) * f->item_size;
|
|
|
- count = f->depth;
|
|
|
-
|
|
|
- // We start writing at the read pointer's position since we fill the complete
|
|
|
- // buffer and we do not want to modify the read pointer within a write function!
|
|
|
- // This would end up in a race condition with read functions!
|
|
|
- f->wr_idx = r;
|
|
|
- }
|
|
|
-
|
|
|
- uint16_t wRel = get_relative_pointer(f, w, 0);
|
|
|
-
|
|
|
- // Write data
|
|
|
- _ff_push_n(f, buf8, count, wRel);
|
|
|
-
|
|
|
- // Advance pointer
|
|
|
- f->wr_idx = advance_pointer(f, w, count);
|
|
|
+ return _tu_fifo_write_n(f, data, n, TU_FIFO_COPY_INC);
|
|
|
+}
|
|
|
|
|
|
- tu_fifo_unlock(f);
|
|
|
+/******************************************************************************/
|
|
|
+/*!
|
|
|
+ @brief This function will write n elements into the array index specified by
|
|
|
+ the write pointer and increment the write index. The source address will
|
|
|
+ not be incremented which is useful for reading from registers.
|
|
|
|
|
|
- return count;
|
|
|
+ @param[in] f
|
|
|
+ Pointer to the FIFO buffer to manipulate
|
|
|
+ @param[in] data
|
|
|
+ The pointer to data to add to the FIFO
|
|
|
+ @param[in] count
|
|
|
+ Number of element
|
|
|
+ @return Number of written elements
|
|
|
+ */
|
|
|
+/******************************************************************************/
|
|
|
+uint16_t tu_fifo_write_n_const_addr_full_words(tu_fifo_t* f, const void * data, uint16_t n)
|
|
|
+{
|
|
|
+ return _tu_fifo_write_n(f, data, n, TU_FIFO_COPY_CST_FULL_WORDS);
|
|
|
}
|
|
|
|
|
|
/******************************************************************************/
|
|
|
@@ -596,12 +804,15 @@ uint16_t tu_fifo_write_n(tu_fifo_t* f, const void * data, uint16_t count)
|
|
|
/******************************************************************************/
|
|
|
bool tu_fifo_clear(tu_fifo_t *f)
|
|
|
{
|
|
|
- tu_fifo_lock(f);
|
|
|
+ _ff_lock(f->mutex_wr);
|
|
|
+ _ff_lock(f->mutex_rd);
|
|
|
+
|
|
|
f->rd_idx = f->wr_idx = 0;
|
|
|
f->max_pointer_idx = 2*f->depth-1;
|
|
|
f->non_used_index_space = UINT16_MAX - f->max_pointer_idx;
|
|
|
- tu_fifo_unlock(f);
|
|
|
|
|
|
+ _ff_unlock(f->mutex_wr);
|
|
|
+ _ff_unlock(f->mutex_rd);
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
@@ -613,15 +824,17 @@ bool tu_fifo_clear(tu_fifo_t *f)
|
|
|
Pointer to the FIFO buffer to manipulate
|
|
|
@param[in] overwritable
|
|
|
Overwritable mode the fifo is set to
|
|
|
-*/
|
|
|
+ */
|
|
|
/******************************************************************************/
|
|
|
bool tu_fifo_set_overwritable(tu_fifo_t *f, bool overwritable)
|
|
|
{
|
|
|
- tu_fifo_lock(f);
|
|
|
+ _ff_lock(f->mutex_wr);
|
|
|
+ _ff_lock(f->mutex_rd);
|
|
|
|
|
|
f->overwritable = overwritable;
|
|
|
|
|
|
- tu_fifo_unlock(f);
|
|
|
+ _ff_unlock(f->mutex_wr);
|
|
|
+ _ff_unlock(f->mutex_rd);
|
|
|
|
|
|
return true;
|
|
|
}
|
|
|
@@ -667,3 +880,146 @@ void tu_fifo_advance_read_pointer(tu_fifo_t *f, uint16_t n)
|
|
|
{
|
|
|
f->rd_idx = advance_pointer(f, f->rd_idx, n);
|
|
|
}
|
|
|
+
|
|
|
+/******************************************************************************/
|
|
|
+/*!
|
|
|
+ @brief Get linear read info
|
|
|
+
|
|
|
+ Returns the length and pointer from which bytes can be read in a linear manner.
|
|
|
+ This is of major interest for DMA transmissions. If returned length is zero the
|
|
|
+ corresponding pointer is invalid. The returned length is limited to the number
|
|
|
+ of ITEMS n which the user wants to write into the buffer.
|
|
|
+ The write pointer does NOT get advanced, use tu_fifo_advance_read_pointer() to
|
|
|
+ do so! If the length returned is less than n i.e. len<n, then a wrap occurs
|
|
|
+ and you need to execute this function a second time to get a pointer to the
|
|
|
+ wrapped part!
|
|
|
+ @param[in] f
|
|
|
+ Pointer to FIFO
|
|
|
+ @param[in] offset
|
|
|
+ Number of ITEMS to ignore before start writing
|
|
|
+ @param[out] **ptr
|
|
|
+ Pointer to start writing to
|
|
|
+ @param[in] n
|
|
|
+ Number of ITEMS to read from buffer
|
|
|
+ @return len
|
|
|
+ Length of linear part IN ITEMS, if zero corresponding pointer ptr is invalid
|
|
|
+ */
|
|
|
+/******************************************************************************/
|
|
|
+uint16_t tu_fifo_get_linear_read_info(tu_fifo_t *f, uint16_t offset, void **ptr, uint16_t n)
|
|
|
+{
|
|
|
+ // Operate on temporary values in case they change in between
|
|
|
+ uint16_t w = f->wr_idx, r = f->rd_idx;
|
|
|
+
|
|
|
+ uint16_t cnt = _tu_fifo_count(f, w, r);
|
|
|
+
|
|
|
+ // Check overflow and correct if required
|
|
|
+ if (cnt > f->depth)
|
|
|
+ {
|
|
|
+ _ff_lock(f->mutex_rd);
|
|
|
+ _tu_fifo_correct_read_pointer(f, w);
|
|
|
+ _ff_unlock(f->mutex_rd);
|
|
|
+ r = f->rd_idx;
|
|
|
+ cnt = f->depth;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Skip beginning of buffer
|
|
|
+ if (cnt == 0 || offset >= cnt) return 0;
|
|
|
+
|
|
|
+ // Check if we can read something at and after offset - if too less is available we read what remains
|
|
|
+ cnt -= offset;
|
|
|
+ if (cnt < n) n = cnt;
|
|
|
+
|
|
|
+ // Get relative pointers
|
|
|
+ w = get_relative_pointer(f, w, 0);
|
|
|
+ r = get_relative_pointer(f, r, offset);
|
|
|
+
|
|
|
+ // Check if there is a wrap around necessary
|
|
|
+ uint16_t len;
|
|
|
+
|
|
|
+ if (w > r) {
|
|
|
+ len = w - r;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ len = f->depth - r; // Also the case if FIFO was full
|
|
|
+ }
|
|
|
+
|
|
|
+ // Limit to required length
|
|
|
+ len = tu_min16(n, len);
|
|
|
+
|
|
|
+ // Copy pointer to buffer to start reading from
|
|
|
+ *ptr = &f->buffer[r];
|
|
|
+
|
|
|
+ return len;
|
|
|
+}
|
|
|
+
|
|
|
+/******************************************************************************/
|
|
|
+/*!
|
|
|
+ @brief Get linear write info
|
|
|
+
|
|
|
+ Returns the length and pointer from which bytes can be written into buffer array in a linear manner.
|
|
|
+ This is of major interest for DMA transmissions not using circular mode. If returned length is zero the
|
|
|
+ corresponding pointer is invalid. The returned length is limited to the number of BYTES n which the user
|
|
|
+ wants to write into the buffer.
|
|
|
+ The write pointer does NOT get advanced, use tu_fifo_advance_write_pointer() to do so! If the length
|
|
|
+ returned is less than n i.e. len<n, then a wrap occurs and you need to execute this function a second
|
|
|
+ time to get a pointer to the wrapped part!
|
|
|
+ @param[in] f
|
|
|
+ Pointer to FIFO
|
|
|
+ @param[in] offset
|
|
|
+ Number of ITEMS to ignore before start writing
|
|
|
+ @param[out] **ptr
|
|
|
+ Pointer to start writing to
|
|
|
+ @param[in] n
|
|
|
+ Number of ITEMS to write into buffer
|
|
|
+ @return len
|
|
|
+ Length of linear part IN ITEMS, if zero corresponding pointer ptr is invalid
|
|
|
+ */
|
|
|
+/******************************************************************************/
|
|
|
+uint16_t tu_fifo_get_linear_write_info(tu_fifo_t *f, uint16_t offset, void **ptr, uint16_t n)
|
|
|
+{
|
|
|
+ uint16_t w = f->wr_idx, r = f->rd_idx;
|
|
|
+ uint16_t free = _tu_fifo_remaining(f, w, r);
|
|
|
+
|
|
|
+ if (!f->overwritable)
|
|
|
+ {
|
|
|
+ // Not overwritable limit up to full
|
|
|
+ n = tu_min16(n, free);
|
|
|
+ }
|
|
|
+ else if (n >= f->depth)
|
|
|
+ {
|
|
|
+ // If overwrite is allowed it must be less than or equal to 2 x buffer length, otherwise the overflow can not be resolved by the read functions
|
|
|
+ TU_VERIFY(n <= 2*f->depth);
|
|
|
+
|
|
|
+ n = f->depth;
|
|
|
+ // We start writing at the read pointer's position since we fill the complete
|
|
|
+ // buffer and we do not want to modify the read pointer within a write function!
|
|
|
+ // This would end up in a race condition with read functions!
|
|
|
+ w = r;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Check if there is room to write to
|
|
|
+ if (free == 0 || offset >= free) return 0;
|
|
|
+
|
|
|
+ // Get relative pointers
|
|
|
+ w = get_relative_pointer(f, w, offset);
|
|
|
+ r = get_relative_pointer(f, r, 0);
|
|
|
+ uint16_t len;
|
|
|
+
|
|
|
+ if (w < r)
|
|
|
+ {
|
|
|
+ len = r-w;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ len = f->depth - w;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Limit to required length
|
|
|
+ len = tu_min16(n, len);
|
|
|
+
|
|
|
+ // Copy pointer to buffer to start reading from
|
|
|
+ *ptr = &f->buffer[w];
|
|
|
+
|
|
|
+ return len;
|
|
|
+}
|