summaryrefslogtreecommitdiff
path: root/src/common
diff options
context:
space:
mode:
authorHa Thach <[email protected]>2021-04-16 01:59:47 +0700
committerGitHub <[email protected]>2021-04-16 01:59:47 +0700
commitc611199632b9288eff76c1c4bfdce5d8cc024748 (patch)
tree8ccd5c0b34b4f1eb89276fce866ee34a9ff608e1 /src/common
parent93dffba0ac905d8525c3092a52888e1a5db96390 (diff)
parentc7e4a8616640165b48cfb0ba4f9346982faa4344 (diff)
Merge pull request #593 from hathach/edpt_ISO_xfer
Edpt iso xfer
Diffstat (limited to 'src/common')
-rw-r--r--src/common/tusb_fifo.c580
-rw-r--r--src/common/tusb_fifo.h31
2 files changed, 490 insertions, 121 deletions
diff --git a/src/common/tusb_fifo.c b/src/common/tusb_fifo.c
index 30629af71..098d54801 100644
--- a/src/common/tusb_fifo.c
+++ b/src/common/tusb_fifo.c
@@ -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);
- // Write data to linear part of buffer
- memcpy(f->buffer + (wRel * f->item_size), data, nLin*f->item_size);
+ *tx_fifo = tmp32;
+ }
+}
- // Write data wrapped around
- memcpy(f->buffer, ((uint8_t const*) data) + nLin*f->item_size, (n - nLin) * f->item_size);
+// 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;
+
+ // current buffer of fifo
+ uint8_t* ff_buf = f->buffer + (rel * 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 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;
+
+ // current buffer of fifo
+ uint8_t* ff_buf = f->buffer + (rel * f->item_size);
+
+ switch (copy_mode)
{
- uint16_t nLin = f->depth - rRel;
+ 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 data from linear part of buffer
- memcpy(p_buffer, f->buffer + (rRel * f->item_size), nLin*f->item_size);
+ // Read more bytes from beginning to complete a word
+ ff_buf = f->buffer;
+ while(remrem--) *dst_u8++ = *ff_buf++;
- // Read data wrapped part
- memcpy((uint8_t*)p_buffer + nLin*f->item_size, f->buffer, (n - nLin) * f->item_size);
+ *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;
+}
diff --git a/src/common/tusb_fifo.h b/src/common/tusb_fifo.h
index 294378496..b2d0b5be9 100644
--- a/src/common/tusb_fifo.h
+++ b/src/common/tusb_fifo.h
@@ -2,6 +2,7 @@
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
+ * Copyright (c) 2020 Reinhard Panhuber - rework to unmasked pointers
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -52,10 +53,10 @@ extern "C" {
#endif
#if CFG_FIFO_MUTEX
+#include "osal/osal.h"
#define tu_fifo_mutex_t osal_mutex_t
#endif
-
/** \struct tu_fifo_t
* \brief Simple Circular FIFO
*/
@@ -66,14 +67,15 @@ typedef struct
uint16_t item_size ; ///< size of each item
bool overwritable ;
- uint16_t max_pointer_idx ; ///< maximum absolute pointer index
uint16_t non_used_index_space ; ///< required for non-power-of-two buffer length
+ uint16_t max_pointer_idx ; ///< maximum absolute pointer index
volatile uint16_t wr_idx ; ///< write pointer
volatile uint16_t rd_idx ; ///< read pointer
#if CFG_FIFO_MUTEX
- tu_fifo_mutex_t mutex;
+ tu_fifo_mutex_t mutex_wr;
+ tu_fifo_mutex_t mutex_rd;
#endif
} tu_fifo_t;
@@ -85,29 +87,33 @@ typedef struct
.item_size = sizeof(_type), \
.overwritable = _overwritable, \
.max_pointer_idx = 2*(_depth)-1, \
- .non_used_index_space = UINT16_MAX - (2*(_depth)-1) \
+ .non_used_index_space = UINT16_MAX - (2*(_depth)-1), \
}
#define TU_FIFO_DEF(_name, _depth, _type, _overwritable) \
uint8_t _name##_buf[_depth*sizeof(_type)]; \
tu_fifo_t _name = TU_FIFO_INIT(_name##_buf, _depth, _type, _overwritable)
+
bool tu_fifo_set_overwritable(tu_fifo_t *f, bool overwritable);
bool tu_fifo_clear(tu_fifo_t *f);
bool tu_fifo_config(tu_fifo_t *f, void* buffer, uint16_t depth, uint16_t item_size, bool overwritable);
#if CFG_FIFO_MUTEX
-static inline void tu_fifo_config_mutex(tu_fifo_t *f, tu_fifo_mutex_t mutex_hdl)
+static inline void tu_fifo_config_mutex(tu_fifo_t *f, tu_fifo_mutex_t write_mutex_hdl, tu_fifo_mutex_t read_mutex_hdl)
{
- f->mutex = mutex_hdl;
+ f->mutex_wr = write_mutex_hdl;
+ f->mutex_rd = read_mutex_hdl;
}
#endif
bool tu_fifo_write (tu_fifo_t* f, void const * p_data);
-uint16_t tu_fifo_write_n (tu_fifo_t* f, void const * p_data, uint16_t count);
+uint16_t tu_fifo_write_n (tu_fifo_t* f, void const * p_data, uint16_t n);
+uint16_t tu_fifo_write_n_const_addr_full_words (tu_fifo_t* f, const void * data, uint16_t n);
bool tu_fifo_read (tu_fifo_t* f, void * p_buffer);
-uint16_t tu_fifo_read_n (tu_fifo_t* f, void * p_buffer, uint16_t count);
+uint16_t tu_fifo_read_n (tu_fifo_t* f, void * p_buffer, uint16_t n);
+uint16_t tu_fifo_read_n_const_addr_full_words (tu_fifo_t* f, void * buffer, uint16_t n);
bool tu_fifo_peek_at (tu_fifo_t* f, uint16_t pos, void * p_buffer);
uint16_t tu_fifo_peek_at_n (tu_fifo_t* f, uint16_t pos, void * p_buffer, uint16_t n);
@@ -116,7 +122,7 @@ uint16_t tu_fifo_count (tu_fifo_t* f);
bool tu_fifo_empty (tu_fifo_t* f);
bool tu_fifo_full (tu_fifo_t* f);
uint16_t tu_fifo_remaining (tu_fifo_t* f);
-bool tu_fifo_overflowed (tu_fifo_t* f);
+bool tu_fifo_overflowed (tu_fifo_t* f);
void tu_fifo_correct_read_pointer (tu_fifo_t* f);
// Pointer modifications intended to be used in combinations with DMAs.
@@ -124,6 +130,13 @@ void tu_fifo_correct_read_pointer (tu_fifo_t* f);
void tu_fifo_advance_write_pointer (tu_fifo_t *f, uint16_t n);
void tu_fifo_advance_read_pointer (tu_fifo_t *f, uint16_t n);
+// If you want to read/write from/to the FIFO by use of a DMA, you may need to conduct two copies to handle a possible wrapping part
+// This functions deliver a pointer to start reading/writing from/to and a valid linear length along which no wrap occurs.
+// In case not all of your data is available within one read/write, update the read/write pointer by
+// tu_fifo_advance_read_pointer()/tu_fifo_advance_write_pointer and conduct a second read/write operation
+uint16_t tu_fifo_get_linear_read_info (tu_fifo_t *f, uint16_t offset, void **ptr, uint16_t n);
+uint16_t tu_fifo_get_linear_write_info (tu_fifo_t *f, uint16_t offset, void **ptr, uint16_t n);
+
static inline bool tu_fifo_peek(tu_fifo_t* f, void * p_buffer)
{
return tu_fifo_peek_at(f, 0, p_buffer);