diff options
| author | Ha Thach <[email protected]> | 2021-05-02 15:29:38 +0700 |
|---|---|---|
| committer | GitHub <[email protected]> | 2021-05-02 15:29:38 +0700 |
| commit | 42a1d2f3b9d40b57a7ef8364c138d8274f1a0a8d (patch) | |
| tree | a7ad018e09c53335a69dbffa7666a13837a84102 /src/common | |
| parent | 30b7988efa7b2468a51e8f5619f1108cef3a1421 (diff) | |
| parent | 1e1bcda00338194275eee4aae5280b7b5203f8d0 (diff) | |
Merge pull request #804 from hathach/audio_make_dma_ready
Implement functions to allow for DMA usage in audio driver.
Diffstat (limited to 'src/common')
| -rw-r--r-- | src/common/tusb_fifo.c | 189 | ||||
| -rw-r--r-- | src/common/tusb_fifo.h | 57 |
2 files changed, 111 insertions, 135 deletions
diff --git a/src/common/tusb_fifo.c b/src/common/tusb_fifo.c index 098d54801..81e11eb01 100644 --- a/src/common/tusb_fifo.c +++ b/src/common/tusb_fifo.c @@ -57,7 +57,8 @@ static inline void _ff_unlock(tu_fifo_mutex_t 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 + * \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 { @@ -77,7 +78,10 @@ bool tu_fifo_config(tu_fifo_t *f, void* buffer, uint16_t depth, uint16_t item_si f->item_size = item_size; f->overwritable = overwritable; - f->max_pointer_idx = 2*depth - 1; // Limit index space to 2*depth - this allows for a fast "modulo" calculation but limits the maximum depth to 2^16/2 = 2^15 and buffer overflows are detectable only if overflow happens once (important for unsupervised DMA applications) + // Limit index space to 2*depth - this allows for a fast "modulo" calculation + // but limits the maximum depth to 2^16/2 = 2^15 and buffer overflows are detectable + // only if overflow happens once (important for unsupervised DMA applications) + f->max_pointer_idx = 2*depth - 1; f->non_used_index_space = UINT16_MAX - f->max_pointer_idx; f->rd_idx = f->wr_idx = 0; @@ -319,7 +323,8 @@ static void _ff_pull_n(tu_fifo_t* f, void* app_buf, uint16_t n, uint16_t rel, tu static uint16_t advance_pointer(tu_fifo_t* f, uint16_t p, uint16_t offset) { // We limit the index space of p such that a correct wrap around happens - // Check for a wrap around or if we are in unused index space - This has to be checked first!! We are exploiting the wrap around to the correct index + // Check for a wrap around or if we are in unused index space - This has to be checked first!! + // We are exploiting the wrap around to the correct index if ((p > p + offset) || (p + offset > f->max_pointer_idx)) { p = (p + offset) + f->non_used_index_space; @@ -335,7 +340,8 @@ static uint16_t advance_pointer(tu_fifo_t* f, uint16_t p, uint16_t offset) static uint16_t backward_pointer(tu_fifo_t* f, uint16_t p, uint16_t offset) { // We limit the index space of p such that a correct wrap around happens - // Check for a wrap around or if we are in unused index space - This has to be checked first!! We are exploiting the wrap around to the correct index + // Check for a wrap around or if we are in unused index space - This has to be checked first!! + // We are exploiting the wrap around to the correct index if ((p < p - offset) || (p - offset > f->max_pointer_idx)) { p = (p - offset) - f->non_used_index_space; @@ -348,9 +354,9 @@ static uint16_t backward_pointer(tu_fifo_t* f, uint16_t p, uint16_t offset) } // get relative from absolute pointer -static uint16_t get_relative_pointer(tu_fifo_t* f, uint16_t p, uint16_t offset) +static uint16_t get_relative_pointer(tu_fifo_t* f, uint16_t p) { - return _ff_mod(advance_pointer(f, p, offset), f->depth); + return _ff_mod(p, f->depth); } // Works on local copies of w and r - return only the difference and as such can be used to determine an overflow @@ -396,7 +402,7 @@ static inline void _tu_fifo_correct_read_pointer(tu_fifo_t* f, uint16_t wAbs) // Works on local copies of w and r // Must be protected by mutexes since in case of an overflow read pointer gets modified -static bool _tu_fifo_peek_at(tu_fifo_t* f, uint16_t offset, void * p_buffer, uint16_t wAbs, uint16_t rAbs) +static bool _tu_fifo_peek(tu_fifo_t* f, void * p_buffer, uint16_t wAbs, uint16_t rAbs) { uint16_t cnt = _tu_fifo_count(f, wAbs, rAbs); @@ -408,9 +414,9 @@ static bool _tu_fifo_peek_at(tu_fifo_t* f, uint16_t offset, void * p_buffer, uin } // Skip beginning of buffer - if (cnt == 0 || offset >= cnt) return false; + if (cnt == 0) return false; - uint16_t rRel = get_relative_pointer(f, rAbs, offset); + uint16_t rRel = get_relative_pointer(f, rAbs); // Peek data _ff_pull(f, p_buffer, rRel); @@ -420,7 +426,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, tu_fifo_copy_mode_t copy_mode) +static 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) { uint16_t cnt = _tu_fifo_count(f, wAbs, rAbs); @@ -433,13 +439,12 @@ static uint16_t _tu_fifo_peek_at_n(tu_fifo_t* f, uint16_t offset, void * p_buffe } // Skip beginning of buffer - if (cnt == 0 || offset >= cnt) return 0; + if (cnt == 0) 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; - uint16_t rRel = get_relative_pointer(f, rAbs, offset); + uint16_t rRel = get_relative_pointer(f, rAbs); // Peek data _ff_pull_n(f, p_buffer, n, rRel, copy_mode); @@ -479,7 +484,7 @@ static uint16_t _tu_fifo_write_n(tu_fifo_t* f, const void * data, uint16_t n, tu w = r; } - uint16_t wRel = get_relative_pointer(f, w, 0); + uint16_t wRel = get_relative_pointer(f, w); // Write data _ff_push_n(f, buf8, n, wRel, copy_mode); @@ -497,7 +502,8 @@ static uint16_t _tu_fifo_read_n(tu_fifo_t* f, void * buffer, uint16_t n, tu_fifo _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 + // f->rd_idx might get modified in case of an overflow so we can not use a local variable + n = _tu_fifo_peek_n(f, buffer, n, f->wr_idx, f->rd_idx, copy_mode); // Advance read pointer f->rd_idx = advance_pointer(f, f->rd_idx, n); @@ -635,7 +641,8 @@ bool tu_fifo_read(tu_fifo_t* f, void * buffer) _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 + // f->rd_idx might get modified in case of an overflow so we can not use a local variable + bool ret = _tu_fifo_peek(f, buffer, f->wr_idx, f->rd_idx); // Advance pointer f->rd_idx = advance_pointer(f, f->rd_idx, ret); @@ -685,10 +692,10 @@ uint16_t tu_fifo_read_n_const_addr_full_words(tu_fifo_t* f, void * buffer, uint1 @returns TRUE if the queue is not empty */ /******************************************************************************/ -bool tu_fifo_peek_at(tu_fifo_t* f, uint16_t offset, void * p_buffer) +bool tu_fifo_peek(tu_fifo_t* f, void * p_buffer) { _ff_lock(f->mutex_rd); - bool ret = _tu_fifo_peek_at(f, offset, p_buffer, f->wr_idx, f->rd_idx); + bool ret = _tu_fifo_peek(f, p_buffer, f->wr_idx, f->rd_idx); _ff_unlock(f->mutex_rd); return ret; } @@ -700,8 +707,6 @@ bool tu_fifo_peek_at(tu_fifo_t* f, uint16_t offset, void * p_buffer) @param[in] f Pointer to the FIFO buffer to manipulate - @param[in] offset - Position to read from in the FIFO buffer with respect to read pointer @param[in] p_buffer Pointer to the place holder for data read from the buffer @param[in] n @@ -710,10 +715,10 @@ bool tu_fifo_peek_at(tu_fifo_t* f, uint16_t offset, void * p_buffer) @returns Number of bytes written to p_buffer */ /******************************************************************************/ -uint16_t tu_fifo_peek_at_n(tu_fifo_t* f, uint16_t offset, void * p_buffer, uint16_t n) +uint16_t tu_fifo_peek_n(tu_fifo_t* f, void * p_buffer, uint16_t n) { _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); + bool ret = _tu_fifo_peek_n(f, p_buffer, n, f->wr_idx, f->rd_idx, TU_FIFO_COPY_INC); _ff_unlock(f->mutex_rd); return ret; } @@ -742,7 +747,7 @@ bool tu_fifo_write(tu_fifo_t* f, const void * data) if ( _tu_fifo_full(f, w, f->rd_idx) && !f->overwritable ) return false; - uint16_t wRel = get_relative_pointer(f, w, 0); + uint16_t wRel = get_relative_pointer(f, w); // Write data _ff_push(f, data, wRel); @@ -883,36 +888,27 @@ void tu_fifo_advance_read_pointer(tu_fifo_t *f, uint16_t n) /******************************************************************************/ /*! - @brief Get linear read info + @brief Get 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! + corresponding pointer is invalid. + The read pointer does NOT get advanced, use tu_fifo_advance_read_pointer() to + do so! @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 + @param[out] *info + Pointer to struct which holds the desired infos */ /******************************************************************************/ -uint16_t tu_fifo_get_linear_read_info(tu_fifo_t *f, uint16_t offset, void **ptr, uint16_t n) +void tu_fifo_get_read_info(tu_fifo_t *f, tu_fifo_buffer_info_t *info) { // 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 + // Check overflow and correct if required - may happen in case a DMA wrote too fast if (cnt > f->depth) { _ff_lock(f->mutex_rd); @@ -922,104 +918,85 @@ uint16_t tu_fifo_get_linear_read_info(tu_fifo_t *f, uint16_t offset, void **ptr, 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; + // Check if fifo is empty + if (cnt == 0) + { + info->len_lin = 0; + info->len_wrap = 0; + info->ptr_lin = NULL; + info->ptr_wrap = NULL; + return; + } // Get relative pointers - w = get_relative_pointer(f, w, 0); - r = get_relative_pointer(f, r, offset); + w = get_relative_pointer(f, w); + r = get_relative_pointer(f, r); - // Check if there is a wrap around necessary - uint16_t len; + // Copy pointer to buffer to start reading from + info->ptr_lin = &f->buffer[r]; + // Check if there is a wrap around necessary if (w > r) { - len = w - r; + // Non wrapping case + info->len_lin = cnt; + info->len_wrap = 0; + info->ptr_wrap = NULL; } else { - len = f->depth - r; // Also the case if FIFO was full + info->len_lin = f->depth - r; // Also the case if FIFO was full + info->len_wrap = cnt - info->len_lin; + info->ptr_wrap = f->buffer; } - - // 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! + Returns the length and pointer to which bytes can be written into FIFO in a linear manner. + This is of major interest for DMA transmissions not using circular mode. If a returned length is zero the + corresponding pointer is invalid. The returned lengths summed up are the currently free space in the FIFO. + The write pointer does NOT get advanced, use tu_fifo_advance_write_pointer() to do so! + TAKE CARE TO NOT OVERFLOW THE BUFFER MORE THAN TWO TIMES THE FIFO DEPTH - IT CAN NOT RECOVERE OTHERWISE! @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 + @param[out] *info + Pointer to struct which holds the desired infos */ /******************************************************************************/ -uint16_t tu_fifo_get_linear_write_info(tu_fifo_t *f, uint16_t offset, void **ptr, uint16_t n) +void tu_fifo_get_write_info(tu_fifo_t *f, tu_fifo_buffer_info_t *info) { uint16_t w = f->wr_idx, r = f->rd_idx; uint16_t free = _tu_fifo_remaining(f, w, r); - if (!f->overwritable) + if (free == 0) { - // 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; + info->len_lin = 0; + info->len_wrap = 0; + info->ptr_lin = NULL; + info->ptr_wrap = NULL; + return; } - // 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; + w = get_relative_pointer(f, w); + r = get_relative_pointer(f, r); + + // Copy pointer to buffer to start writing to + info->ptr_lin = &f->buffer[w]; if (w < r) { - len = r-w; + // Non wrapping case + info->len_lin = r-w; + info->len_wrap = 0; + info->ptr_wrap = NULL; } else { - len = f->depth - w; + info->len_lin = f->depth - w; + info->len_wrap = free - info->len_lin; // Remaining length - n already was limited to free or FIFO depth + info->ptr_wrap = f->buffer; // Always start of buffer } - - // 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 b2d0b5be9..8d73911f0 100644 --- a/src/common/tusb_fifo.h +++ b/src/common/tusb_fifo.h @@ -25,10 +25,6 @@ * This file is part of the TinyUSB stack. */ -/** \ingroup Group_Common - * \defgroup group_fifo fifo - * @{ */ - #ifndef _TUSB_FIFO_H_ #define _TUSB_FIFO_H_ @@ -62,16 +58,16 @@ extern "C" { */ typedef struct { - uint8_t* buffer ; ///< buffer pointer - uint16_t depth ; ///< max items - uint16_t item_size ; ///< size of each item - bool overwritable ; + uint8_t* buffer ; ///< buffer pointer + uint16_t depth ; ///< max items + uint16_t item_size ; ///< size of each item + bool overwritable ; - uint16_t non_used_index_space ; ///< required for non-power-of-two buffer length - 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 + volatile uint16_t wr_idx ; ///< write pointer + volatile uint16_t rd_idx ; ///< read pointer #if CFG_FIFO_MUTEX tu_fifo_mutex_t mutex_wr; @@ -80,6 +76,14 @@ typedef struct } tu_fifo_t; +typedef struct +{ + uint16_t len_lin ; ///< linear length in item size + uint16_t len_wrap ; ///< wrapped length in item size + void * ptr_lin ; ///< linear part start pointer + void * ptr_wrap ; ///< wrapped part start pointer +} tu_fifo_buffer_info_t; + #define TU_FIFO_INIT(_buffer, _depth, _type, _overwritable) \ { \ .buffer = _buffer, \ @@ -115,8 +119,8 @@ 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 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); +bool tu_fifo_peek (tu_fifo_t* f, void * p_buffer); +uint16_t tu_fifo_peek_n (tu_fifo_t* f, void * p_buffer, uint16_t n); uint16_t tu_fifo_count (tu_fifo_t* f); bool tu_fifo_empty (tu_fifo_t* f); @@ -125,27 +129,22 @@ uint16_t tu_fifo_remaining (tu_fifo_t* f); bool tu_fifo_overflowed (tu_fifo_t* f); void tu_fifo_correct_read_pointer (tu_fifo_t* f); +static inline uint16_t tu_fifo_depth(tu_fifo_t* f) +{ + return f->depth; +} + // Pointer modifications intended to be used in combinations with DMAs. // USE WITH CARE - NO SAFTY CHECKS CONDUCTED HERE! NOT MUTEX PROTECTED! 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); -} +// 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. These functions deliver a pointer to start +// reading/writing from/to and a valid linear length along which no wrap occurs. +void tu_fifo_get_read_info (tu_fifo_t *f, tu_fifo_buffer_info_t *info); +void tu_fifo_get_write_info(tu_fifo_t *f, tu_fifo_buffer_info_t *info); -static inline uint16_t tu_fifo_depth(tu_fifo_t* f) -{ - return f->depth; -} #ifdef __cplusplus } |
