audio_device: Allow one FIFO for N channels

This allow to build with single FIFO for devices with multiple channels.
Having just one FIFO greatly reduces time needed to feed endpoint.

This change also allows to have one FIFO with 24 bit samples that
is not rounded up to 32 bit elements.
CFG_TUD_AUDIO_RX_ITEMSIZE and CFG_TUD_AUDIO_TX_ITEMSIZE can be manually
defined. This allows to use FIFO more efficiently when 24 bits samples
are already using 3 bytes, in this case there is no need to put them
into FIFO one by one.
For 8, 16, 32 bits samples size efficient FIFO access is always used
when single FIFO is selected.

This also changes FIFO element size to 1, FIFO usage was confusing
in some place it treated content as byte base in other it looked like
ITEM size is to be used. Also bufsize that in most (maybe all) cases
was really meaning item count.
bufsize now mean buffer size in bytes so there is no confusion.
This commit is contained in:
Jerzy Kasenberg 2020-09-11 13:42:52 +02:00
parent 28cf63c7db
commit 759d530506
2 changed files with 179 additions and 28 deletions

View File

@ -46,6 +46,19 @@
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
#if CFG_TUD_AUDIO_EPSIZE_IN && CFG_TUD_AUDIO_TX_FIFO_SIZE
#ifndef CFG_TUD_AUDIO_TX_FIFO_COUNT
#define CFG_TUD_AUDIO_TX_FIFO_COUNT CFG_TUD_AUDIO_N_CHANNELS_TX
#endif
#endif
#if CFG_TUD_AUDIO_EPSIZE_OUT && CFG_TUD_AUDIO_RX_FIFO_SIZE
#ifndef CFG_TUD_AUDIO_RX_FIFO_COUNT
#define CFG_TUD_AUDIO_RX_FIFO_COUNT CFG_TUD_AUDIO_N_CHANNELS_RX
#endif
#endif
typedef struct
{
uint8_t rhport;
@ -81,18 +94,18 @@ typedef struct
// FIFO
#if CFG_TUD_AUDIO_EPSIZE_IN && CFG_TUD_AUDIO_TX_FIFO_SIZE
tu_fifo_t tx_ff[CFG_TUD_AUDIO_N_CHANNELS_TX];
CFG_TUSB_MEM_ALIGN uint8_t tx_ff_buf[CFG_TUD_AUDIO_N_CHANNELS_TX][CFG_TUD_AUDIO_TX_FIFO_SIZE * CFG_TUD_AUDIO_TX_ITEMSIZE];
tu_fifo_t tx_ff[CFG_TUD_AUDIO_TX_FIFO_COUNT];
CFG_TUSB_MEM_ALIGN uint8_t tx_ff_buf[CFG_TUD_AUDIO_TX_FIFO_COUNT][CFG_TUD_AUDIO_TX_FIFO_SIZE];
#if CFG_FIFO_MUTEX
osal_mutex_def_t tx_ff_mutex[CFG_TUD_AUDIO_N_CHANNELS_TX];
osal_mutex_def_t tx_ff_mutex[CFG_TUD_AUDIO_TX_FIFO_COUNT];
#endif
#endif
#if CFG_TUD_AUDIO_EPSIZE_OUT && CFG_TUD_AUDIO_RX_FIFO_SIZE
tu_fifo_t rx_ff[CFG_TUD_AUDIO_N_CHANNELS_RX];
CFG_TUSB_MEM_ALIGN uint8_t rx_ff_buf[CFG_TUD_AUDIO_N_CHANNELS_RX][CFG_TUD_AUDIO_RX_FIFO_SIZE * CFG_TUD_AUDIO_RX_ITEMSIZE];
tu_fifo_t rx_ff[CFG_TUD_AUDIO_RX_FIFO_COUNT];
CFG_TUSB_MEM_ALIGN uint8_t rx_ff_buf[CFG_TUD_AUDIO_RX_FIFO_COUNT][CFG_TUD_AUDIO_RX_FIFO_SIZE];
#if CFG_FIFO_MUTEX
osal_mutex_def_t rx_ff_mutex[CFG_TUD_AUDIO_N_CHANNELS_RX];
osal_mutex_def_t rx_ff_mutex[CFG_TUD_AUDIO_RX_FIFO_COUNT];
#endif
#endif
@ -190,7 +203,7 @@ bool tud_audio_n_mounted(uint8_t itf)
//--------------------------------------------------------------------+
#if CFG_TUD_AUDIO_EPSIZE_OUT && CFG_TUD_AUDIO_RX_FIFO_SIZE
#if CFG_TUD_AUDIO_RX_FIFO_COUNT > 1
uint16_t tud_audio_n_available(uint8_t itf, uint8_t channelId)
{
TU_VERIFY(channelId < CFG_TUD_AUDIO_N_CHANNELS_RX);
@ -208,7 +221,22 @@ void tud_audio_n_read_flush (uint8_t itf, uint8_t channelId)
TU_VERIFY(channelId < CFG_TUD_AUDIO_N_CHANNELS_RX, );
tu_fifo_clear(&_audiod_itf[itf].rx_ff[channelId]);
}
#else
uint16_t tud_audio_n_available(uint8_t itf)
{
return tu_fifo_count(&_audiod_itf[itf].rx_ff[0]);
}
uint16_t tud_audio_n_read(uint8_t itf, void* buffer, uint16_t bufsize)
{
return tu_fifo_read_n(&_audiod_itf[itf].rx_ff[0], buffer, bufsize);
}
void tud_audio_n_read_flush (uint8_t itf)
{
tu_fifo_clear(&_audiod_itf[itf].rx_ff[0]);
}
#endif
#endif
#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
@ -283,6 +311,7 @@ static bool audio_rx_done_cb(uint8_t rhport, audiod_interface_t* audio, uint8_t*
// The following functions are used in case CFG_TUD_AUDIO_RX_FIFO_SIZE != 0
#if CFG_TUD_AUDIO_RX_FIFO_SIZE
#if CFG_TUD_AUDIO_RX_FIFO_COUNT > 1
static bool audio_rx_done_type_I_pcm_ff_cb(uint8_t rhport, audiod_interface_t* audio, uint8_t * buffer, uint16_t bufsize)
{
(void) rhport;
@ -318,7 +347,23 @@ static bool audio_rx_done_type_I_pcm_ff_cb(uint8_t rhport, audiod_interface_t* a
chId = 0;
}
}
} }
return true;
}
#else
static bool audio_rx_done_type_I_pcm_ff_cb(uint8_t rhport, audiod_interface_t *audio, uint8_t *buffer, uint16_t bufsize)
{
(void) rhport;
// We expect to get a multiple of CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX * CFG_TUD_AUDIO_N_CHANNELS_RX per channel
if (bufsize % (CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX * CFG_TUD_AUDIO_N_CHANNELS_RX) != 0)
{
return false;
}
tu_fifo_write_n(&audio->rx_ff[0], buffer, bufsize);
return true;
}
#endif // CFG_TUD_AUDIO_RX_FIFO_COUNT > 1
#endif //CFG_TUD_AUDIO_RX_FIFO_SIZE
//--------------------------------------------------------------------+
@ -336,9 +381,9 @@ static bool audio_rx_done_type_I_pcm_ff_cb(uint8_t rhport, audiod_interface_t* a
* \param[in] len: # of array elements to copy
* \return Number of bytes actually written
*/
#if CFG_TUD_AUDIO_EPSIZE_IN
#if !CFG_TUD_AUDIO_TX_FIFO_SIZE
/* This function is intended for later use once EP buffers (at least for ISO EPs) are implemented as ring buffers
#if CFG_TUD_AUDIO_EPSIZE_IN && !CFG_TUD_AUDIO_TX_FIFO_SIZE
uint16_t tud_audio_n_write_ep_in_buffer(uint8_t itf, const void * data, uint16_t len)
{
audiod_interface_t* audio = &_audiod_itf[itf];
@ -365,11 +410,23 @@ uint16_t tud_audio_n_write_ep_in_buffer(uint8_t itf, const void * data, uint16_t
// Return number of bytes written
return len;
}
#endif
*/
#if CFG_TUD_AUDIO_EPSIZE_IN && CFG_TUD_AUDIO_TX_FIFO_SIZE
#else
#if CFG_TUD_AUDIO_TX_FIFO_COUNT == 1
uint16_t tud_audio_n_write(uint8_t itf, void const* data, uint16_t len)
{
{
audiod_interface_t* audio = &_audiod_itf[itf];
if (audio->p_desc == NULL)
{
return 0;
}
return tu_fifo_write_n(&audio->tx_ff[0], data, len);
}
}
#else
uint16_t tud_audio_n_write(uint8_t itf, uint8_t channelId, const void * data, uint16_t len)
{
audiod_interface_t* audio = &_audiod_itf[itf];
@ -381,6 +438,23 @@ uint16_t tud_audio_n_write(uint8_t itf, uint8_t channelId, const void * data, ui
}
#endif
static bool audiod_tx_done_cb(uint8_t rhport, audiod_interface_t* audio, uint16_t * n_bytes_copied);
uint16_t tud_audio_n_write_flush(uint8_t itf)
{
audiod_interface_t *audio = &_audiod_itf[itf];
if (audio->p_desc == NULL) {
return 0;
}
uint16_t n_bytes_copied;
TU_VERIFY(audiod_tx_done_cb(audio->rhport, audio, &n_bytes_copied));
return n_bytes_copied;
}
#endif
#endif
#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN > 0
uint32_t tud_audio_int_ctr_n_write(uint8_t itf, uint8_t const* buffer, uint32_t bufsize)
{
@ -477,6 +551,7 @@ static bool audiod_tx_done_cb(uint8_t rhport, audiod_interface_t* audio, uint16_
#endif //CFG_TUD_AUDIO_EPSIZE_IN
#if CFG_TUD_AUDIO_TX_FIFO_SIZE
#if CFG_TUD_AUDIO_TX_FIFO_COUNT > 1 || (CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX != CFG_TUD_AUDIO_TX_ITEMSIZE)
static bool audiod_tx_done_type_I_pcm_ff_cb(uint8_t rhport, audiod_interface_t* audio)
{
// We encode directly into IN EP's buffer - abort if previous transfer not complete
@ -484,15 +559,15 @@ static bool audiod_tx_done_type_I_pcm_ff_cb(uint8_t rhport, audiod_interface_t*
// Determine amount of samples
uint16_t const nEndpointSampleCapacity = CFG_TUD_AUDIO_EPSIZE_IN / CFG_TUD_AUDIO_N_CHANNELS_TX / CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX;
uint16_t nSamplesPerChannelToSend = tu_fifo_count(&audio->tx_ff[0]);
uint16_t nSamplesPerChannelToSend = tu_fifo_count(&audio->tx_ff[0]) / CFG_TUD_AUDIO_TX_ITEMSIZE;
uint16_t nBytesToSend;
uint8_t cntChannel;
for (cntChannel = 1; cntChannel < CFG_TUD_AUDIO_N_CHANNELS_TX; cntChannel++)
{
if (audio->tx_ff[cntChannel].count < nSamplesPerChannelToSend)
if (audio->tx_ff[cntChannel].count / CFG_TUD_AUDIO_TX_ITEMSIZE < nSamplesPerChannelToSend)
{
nSamplesPerChannelToSend = audio->tx_ff[cntChannel].count;
nSamplesPerChannelToSend = audio->tx_ff[cntChannel].count * CFG_TUD_AUDIO_TX_ITEMSIZE;
}
}
@ -524,7 +599,7 @@ static bool audiod_tx_done_type_I_pcm_ff_cb(uint8_t rhport, audiod_interface_t*
for (cntChannel = 0; cntChannel < CFG_TUD_AUDIO_N_CHANNELS_TX; cntChannel++)
{
// Get sample from buffer
tu_fifo_read(&audio->tx_ff[cntChannel], &sample);
tu_fifo_read_n(&audio->tx_ff[cntChannel], &sample, CFG_TUD_AUDIO_TX_ITEMSIZE);
// Put it into EP's buffer - Let alignment problems be handled by memcpy
memcpy(pBuff, &sample, CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX);
@ -539,6 +614,30 @@ static bool audiod_tx_done_type_I_pcm_ff_cb(uint8_t rhport, audiod_interface_t*
return true;
}
#else
static bool audiod_tx_done_type_I_pcm_ff_cb(uint8_t rhport, audiod_interface_t* audio)
{
// We encode directly into IN EP's buffer - abort if previous transfer not complete
TU_VERIFY(!usbd_edpt_busy(rhport, audio->ep_in));
// Determine amount of samples
uint16_t nByteCount = tu_fifo_count(&audio->tx_ff[0]);
nByteCount = tu_min16(nByteCount, CFG_TUD_AUDIO_EPSIZE_IN);
// Check if there is enough
if (nByteCount == 0)
{
return true;
}
nByteCount = tu_fifo_read_n(&audio->tx_ff[0], audio->epin_buf, nByteCount);
audio->epin_buf_cnt = nByteCount;
return true;
}
#endif // CFG_TUD_AUDIO_TX_FIFO_COUNT > 1 || (CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX != CFG_TUD_AUDIO_TX_ITEMSIZE)
#endif //CFG_TUD_AUDIO_TX_FIFO_SIZE
// This function is called once a transmit of an feedback packet was successfully completed. Here, we get the next feedback value to be sent
@ -588,7 +687,6 @@ static bool audio_int_ctr_done_cb(uint8_t rhport, audiod_interface_t* audio, uin
//--------------------------------------------------------------------+
void audiod_init(void)
{
uint8_t cnt;
tu_memclr(_audiod_itf, sizeof(_audiod_itf));
for(uint8_t i=0; i<CFG_TUD_AUDIO; i++)
@ -597,9 +695,9 @@ void audiod_init(void)
// Initialize TX FIFOs if required
#if CFG_TUD_AUDIO_EPSIZE_IN && CFG_TUD_AUDIO_TX_FIFO_SIZE
for (cnt = 0; cnt < CFG_TUD_AUDIO_N_CHANNELS_TX; cnt++)
for (uint8_t cnt = 0; cnt < CFG_TUD_AUDIO_TX_FIFO_COUNT; cnt++)
{
tu_fifo_config(&audio->tx_ff[cnt], &audio->tx_ff_buf[cnt], CFG_TUD_AUDIO_TX_FIFO_SIZE, CFG_TUD_AUDIO_TX_ITEMSIZE, true);
tu_fifo_config(&audio->tx_ff[cnt], &audio->tx_ff_buf[cnt], CFG_TUD_AUDIO_TX_FIFO_SIZE, 1, true);
#if CFG_FIFO_MUTEX
tu_fifo_config_mutex(&audio->tx_ff[cnt], osal_mutex_create(&audio->tx_ff_mutex[cnt]));
#endif
@ -607,9 +705,9 @@ void audiod_init(void)
#endif
#if CFG_TUD_AUDIO_EPSIZE_OUT && CFG_TUD_AUDIO_RX_FIFO_SIZE
for (cnt = 0; cnt < CFG_TUD_AUDIO_N_CHANNELS_RX; cnt++)
for (uint8_t cnt = 0; cnt < CFG_TUD_AUDIO_RX_FIFO_COUNT; cnt++)
{
tu_fifo_config(&audio->rx_ff[cnt], &audio->rx_ff_buf[cnt], CFG_TUD_AUDIO_RX_FIFO_SIZE, CFG_TUD_AUDIO_RX_ITEMSIZE, true);
tu_fifo_config(&audio->rx_ff[cnt], &audio->rx_ff_buf[cnt], CFG_TUD_AUDIO_RX_FIFO_SIZE, 1, true);
#if CFG_FIFO_MUTEX
tu_fifo_config_mutex(&audio->rx_ff[cnt], osal_mutex_create(&audio->rx_ff_mutex[cnt]));
#endif
@ -634,16 +732,15 @@ void audiod_reset(uint8_t rhport)
audiod_interface_t* audio = &_audiod_itf[i];
tu_memclr(audio, ITF_MEM_RESET_SIZE);
uint8_t cnt;
#if CFG_TUD_AUDIO_EPSIZE_IN && CFG_TUD_AUDIO_TX_FIFO_SIZE
for (cnt = 0; cnt < CFG_TUD_AUDIO_N_CHANNELS_TX; cnt++)
for (uint8_t cnt = 0; cnt < CFG_TUD_AUDIO_TX_FIFO_COUNT; cnt++)
{
tu_fifo_clear(&audio->tx_ff[cnt]);
}
#endif
#if CFG_TUD_AUDIO_EPSIZE_OUT && CFG_TUD_AUDIO_RX_FIFO_SIZE
for (cnt = 0; cnt < CFG_TUD_AUDIO_N_CHANNELS_RX; cnt++)
for (uint8_t cnt = 0; cnt < CFG_TUD_AUDIO_RX_FIFO_COUNT; cnt++)
{
tu_fifo_clear(&audio->rx_ff[cnt]);
}

View File

@ -119,6 +119,7 @@
#define CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX 1
#endif
#ifndef CFG_TUD_AUDIO_TX_ITEMSIZE
#if CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX == 1
#define CFG_TUD_AUDIO_TX_ITEMSIZE 1
#elif CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX == 2
@ -126,6 +127,11 @@
#else
#define CFG_TUD_AUDIO_TX_ITEMSIZE 4
#endif
#endif
#if CFG_TUD_AUDIO_TX_ITEMSIZE < CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX
#error FIFO element size (ITEMSIZE) must not be smaller then sample size
#endif
#endif
@ -170,9 +176,15 @@ extern "C" {
bool tud_audio_n_mounted (uint8_t itf);
#if CFG_TUD_AUDIO_EPSIZE_OUT && CFG_TUD_AUDIO_RX_FIFO_SIZE
#if CFG_TUD_AUDIO_RX_FIFO_COUNT > 1
uint16_t tud_audio_n_available (uint8_t itf, uint8_t channelId);
uint16_t tud_audio_n_read (uint8_t itf, uint8_t channelId, void* buffer, uint16_t bufsize);
void tud_audio_n_read_flush (uint8_t itf, uint8_t channelId);
#else
uint16_t tud_audio_n_available (uint8_t itf);
uint16_t tud_audio_n_read (uint8_t itf, void* buffer, uint16_t bufsize);
void tud_audio_n_read_flush (uint8_t itf);
#endif
#endif
/* This function is intended for later use once EP buffers (at least for ISO EPs) are implemented as ring buffers
@ -182,7 +194,12 @@ uint16_t tud_audio_n_write_ep_in_buffer(uint8_t itf, const void * data, uint16_t
*/
#if CFG_TUD_AUDIO_EPSIZE_IN && CFG_TUD_AUDIO_TX_FIFO_SIZE
#if CFG_TUD_AUDIO_TX_FIFO_COUNT > 1
uint16_t tud_audio_n_write (uint8_t itf, uint8_t channelId, const void * data, uint16_t len);
#else
uint16_t tud_audio_n_write (uint8_t itf, const void * data, uint16_t len);
#endif
uint16_t tud_audio_n_write_flush(uint8_t itf);
#endif
#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN > 0
@ -205,7 +222,11 @@ static inline void tud_audio_read_flush (void);
#endif
#if CFG_TUD_AUDIO_EPSIZE_IN && CFG_TUD_AUDIO_TX_FIFO_SIZE
#if CFG_TUD_AUDIO_TX_FIFO_COUNT > 1
static inline uint16_t tud_audio_write (uint8_t channelId, uint8_t const* buffer, uint16_t bufsize);
#else
static inline uint16_t tud_audio_write (uint8_t const* buffer, uint16_t bufsize);
#endif
#endif
#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN > 0
@ -274,14 +295,31 @@ static inline bool tud_audio_mounted(void)
return tud_audio_n_mounted(0);
}
#if CFG_TUD_AUDIO_EPSIZE_IN && CFG_TUD_AUDIO_TX_FIFO_SIZE
static inline uint16_t tud_audio_write (uint8_t channelId, uint8_t const* buffer, uint16_t bufsize) // Short version if only one audio function is used
#if CFG_TUD_AUDIO_EPSIZE_IN
#if CFG_TUD_AUDIO_TX_FIFO_SIZE && CFG_TUD_AUDIO_TX_FIFO_COUNT > 1
static inline uint16_t tud_audio_write (uint8_t channelId, uint8_t const* buffer, uint16_t n_bytes) // Short version if only one audio function is used
{
return tud_audio_n_write(0, channelId, buffer, bufsize);
return tud_audio_n_write(0, channelId, buffer, n_bytes);
}
#else
static inline uint16_t tud_audio_write (uint8_t const* buffer, uint16_t n_bytes) // Short version if only one audio function is used
{
return tud_audio_n_write(0, buffer, n_bytes);
}
#endif
static inline uint16_t tud_audio_write_flush (void) // Short version if only one audio function is used
{
#if CFG_TUD_AUDIO_TX_FIFO_SIZE
return tud_audio_n_write_flush(0);
#else
return 0;
#endif
}
#endif // CFG_TUD_AUDIO_EPSIZE_IN && CFG_TUD_AUDIO_TX_FIFO_SIZE
#if CFG_TUD_AUDIO_EPSIZE_OUT && CFG_TUD_AUDIO_RX_FIFO_SIZE
#if CFG_TUD_AUDIO_RX_FIFO_COUNT > 1
static inline uint16_t tud_audio_available(uint8_t channelId)
{
return tud_audio_n_available(0, channelId);
@ -296,6 +334,22 @@ static inline void tud_audio_read_flush(uint8_t channelId)
{
tud_audio_n_read_flush(0, channelId);
}
#else
static inline uint16_t tud_audio_available(void)
{
return tud_audio_n_available(0);
}
static inline uint16_t tud_audio_read(void *buffer, uint16_t bufsize)
{
return tud_audio_n_read(0, buffer, bufsize);
}
static inline void tud_audio_read_flush(void)
{
tud_audio_n_read_flush(0);
}
#endif
#endif
#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN > 0