diff --git a/src/common/tusb_fifo.c b/src/common/tusb_fifo.c index ff2e2c6fd..f6def94ab 100644 --- a/src/common/tusb_fifo.c +++ b/src/common/tusb_fifo.c @@ -354,10 +354,7 @@ 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); @@ -664,10 +661,7 @@ uint16_t tu_fifo_peek_n_into_other_fifo (tu_fifo_t* f, tu_fifo_t* f_target, uint // 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; tu_fifo_lock(f_target); // Lock both read and write pointers - in case of an overwritable FIFO both may be modified @@ -881,3 +875,195 @@ void tu_fifo_advance_read_pointer(tu_fifo_t *f, uint16_t n) { f->rd_idx = advance_pointer(f, f->rd_idx, n); } + +/******************************************************************************/ +/*! + @brief Move back write pointer - intended to be used in combination with DMA. + It is possible to fill the FIFO by use of a DMA in circular mode. Within + DMA ISRs you may update the write pointer to be able to read from the FIFO. + As long as the DMA is the only process writing into the FIFO this is safe + to use. + + USE WITH CARE - WE DO NOT CONDUCT SAFTY CHECKS HERE! + + @param[in] f + Pointer to the FIFO buffer to manipulate + @param[in] n + Number of items the write pointer moves backward + */ +/******************************************************************************/ +void tu_fifo_backward_write_pointer(tu_fifo_t *f, uint16_t n) +{ + f->wr_idx = backward_pointer(f, f->wr_idx, n); +} + +/******************************************************************************/ +/*! + @brief Move back read pointer - intended to be used in combination with DMA. + It is possible to read from the FIFO by use of a DMA in linear mode. Within + DMA ISRs you may update the read pointer to be able to again write into the + FIFO. As long as the DMA is the only process reading from the FIFO this is + safe to use. + + USE WITH CARE - WE DO NOT CONDUCT SAFTY CHECKS HERE! + + @param[in] f + Pointer to the FIFO buffer to manipulate + @param[in] n + Number of items the read pointer moves backward + */ +/******************************************************************************/ +void tu_fifo_backward_read_pointer(tu_fifo_t *f, uint16_t n) +{ + f->rd_idx = backward_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 BYTES 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. lenwr_idx, r = f->rd_idx; + + uint16_t cnt = _tu_fifo_count(f, w, r); + + // Check overflow and correct if required + if (cnt > f->depth) + { + tu_fifo_lock(f); + _tu_fifo_correct_read_pointer(f, w); + tu_fifo_unlock(f); + r = f->rd_idx; + cnt = f->depth; + } + + // Convert to bytes + cnt = cnt * f->item_size; + + // 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; + } + + len = len * f->item_size; + + // 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. lenwr_idx, r = f->rd_idx; + uint16_t free = _tu_fifo_remaining(f, w, r) * f->item_size; + + if (!f->overwritable) + { + // Not overwritable limit up to full + n = tu_min16(n, free); + } + else if (n >= f->depth * f->item_size) + { + // 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 * f->item_size); + + n = f->depth * f->item_size; + // 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; + } + + len = len * f->item_size; + + // 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 7ece30b74..833b3eead 100644 --- a/src/common/tusb_fifo.h +++ b/src/common/tusb_fifo.h @@ -128,13 +128,22 @@ 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. // 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_backward_write_pointer (tu_fifo_t *f, uint16_t n); void tu_fifo_advance_read_pointer (tu_fifo_t *f, uint16_t n); +void tu_fifo_backward_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) {