Rework audio driver

This commit is contained in:
Reinhard Panhuber 2021-01-31 19:08:23 +01:00
parent f1551d7a5f
commit 84406f1654
6 changed files with 442 additions and 363 deletions

View File

@ -32,8 +32,6 @@
*
* */
// TODO: Rename CFG_TUD_AUDIO_EPSIZE_IN to CFG_TUD_AUDIO_EP_IN_BUFFER_SIZE
#include "tusb_option.h"
#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_AUDIO)
@ -49,30 +47,17 @@
// 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;
uint8_t const * p_desc; // Pointer pointing to Standard AC Interface Descriptor(4.7.1) - Audio Control descriptor defining audio function
#if CFG_TUD_AUDIO_EPSIZE_IN
uint8_t ep_in; // Outgoing (out of uC) audio data EP.
uint16_t epin_buf_cnt; // Count filling status of EP in buffer - this is a shared state currently and is intended to be removed once EP buffers can be implemented as FIFOs!
#if CFG_TUD_AUDIO_EP_IN_SW_BUFFER_SIZE
uint8_t ep_in; // TX audio data EP.
uint8_t ep_in_as_intf_num; // Corresponding Standard AS Interface Descriptor (4.9.1) belonging to output terminal to which this EP belongs - 0 is invalid (this fits to UAC2 specification since AS interfaces can not have interface number equal to zero)
#endif
#if CFG_TUD_AUDIO_EPSIZE_OUT
#if CFG_TUD_AUDIO_EP_OUT_SW_BUFFER_SIZE
uint8_t ep_out; // Incoming (into uC) audio data EP.
uint8_t ep_out_as_intf_num; // Corresponding Standard AS Interface Descriptor (4.9.1) belonging to input terminal to which this EP belongs - 0 is invalid (this fits to UAC2 specification since AS interfaces can not have interface number equal to zero)
@ -89,25 +74,51 @@ typedef struct
#if CFG_TUD_AUDIO_N_AS_INT
uint8_t altSetting[CFG_TUD_AUDIO_N_AS_INT]; // We need to save the current alternate setting this way, because it is possible that there are AS interfaces which do not have an EP!
#endif
/*------------- From this point, data is not cleared by bus reset -------------*/
// Buffer for control requests
CFG_TUSB_MEM_ALIGN uint8_t ctrl_buf[CFG_TUD_AUDIO_CTRL_BUF_SIZE];
// FIFO
#if CFG_TUD_AUDIO_EPSIZE_IN && CFG_TUD_AUDIO_TX_FIFO_SIZE
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];
// EP Transfer buffers and FIFOs
#if CFG_TUD_AUDIO_EP_OUT_SW_BUFFER_SIZE
CFG_TUSB_MEM_ALIGN uint8_t ep_out_buf[CFG_TUD_AUDIO_EP_OUT_SW_BUFFER_SIZE];
tu_fifo_t ep_out_ff;
#if CFG_FIFO_MUTEX
osal_mutex_def_t tx_ff_mutex[CFG_TUD_AUDIO_TX_FIFO_COUNT];
osal_mutex_def_t ep_out_ff_mutex;
#endif
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
uint32_t fb_val; // Feedback value for asynchronous mode (in 16.16 format).
#endif
#endif
#if CFG_TUD_AUDIO_EP_IN_SW_BUFFER_SIZE
CFG_TUSB_MEM_ALIGN uint8_t ep_in_buf[CFG_TUD_AUDIO_EP_IN_SW_BUFFER_SIZE];
tu_fifo_t ep_in_ff;
#if CFG_FIFO_MUTEX
osal_mutex_def_t ep_in_ff_mutex;
#endif
#endif
// Support FIFOs
#if CFG_TUD_AUDIO_EPSIZE_IN && CFG_TUD_AUDIO_TX_SUPPORT_SW_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_SUPPORT_SW_FIFO_SIZE];
#if CFG_FIFO_MUTEX
osal_mutex_def_t tx_ff_mutex[CFG_TUD_AUDIO_N_CHANNELS_TX];
#endif
#endif
#if CFG_TUD_AUDIO_EPSIZE_OUT && CFG_TUD_AUDIO_RX_FIFO_SIZE
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_TUD_AUDIO_EPSIZE_OUT && CFG_TUD_AUDIO_RX_SUPPORT_SW_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_SUPPORT_SW_FIFO_SIZE];
#if CFG_FIFO_MUTEX
osal_mutex_def_t rx_ff_mutex[CFG_TUD_AUDIO_RX_FIFO_COUNT];
osal_mutex_def_t rx_ff_mutex[CFG_TUD_AUDIO_N_CHANNELS_RX];
#endif
#endif
@ -119,21 +130,6 @@ typedef struct
#endif
#endif
// Endpoint Transfer buffers
#if CFG_TUD_AUDIO_EPSIZE_OUT
CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_AUDIO_EPSIZE_OUT]; // Bigger makes no sense for isochronous EP's (but technically possible here)
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
uint32_t fb_val; // Feedback value for asynchronous mode (in 16.16 format).
#endif
#endif
#if CFG_TUD_AUDIO_EPSIZE_IN
CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_AUDIO_EPSIZE_IN]; // Bigger makes no sense for isochronous EP's (but technically possible here)
tu_fifo_t epin_ff;
#endif
#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
CFG_TUSB_MEM_ALIGN uint8_t ep_int_ctr_buf[CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN];
#endif
@ -149,11 +145,11 @@ CFG_TUSB_MEM_SECTION audiod_interface_t _audiod_itf[CFG_TUD_AUDIO];
extern const uint16_t tud_audio_desc_lengths[];
#if CFG_TUD_AUDIO_EPSIZE_OUT
#if CFG_TUD_AUDIO_EP_OUT_SW_BUFFER_SIZE
static bool audio_rx_done_type_I_pcm_ff_cb(uint8_t rhport, audiod_interface_t* audio, uint8_t * buffer, uint16_t bufsize);
#endif
#if CFG_TUD_AUDIO_EPSIZE_IN
#if CFG_TUD_AUDIO_EP_IN_SW_BUFFER_SIZE
static bool audiod_tx_done_type_I_pcm_ff_cb(uint8_t rhport, audiod_interface_t* audio);
#endif
@ -167,34 +163,23 @@ static bool audiod_verify_ep_exists(uint8_t ep, uint8_t *idxDriver);
bool tud_audio_n_mounted(uint8_t itf)
{
TU_VERIFY(itf < CFG_TUD_AUDIO);
audiod_interface_t* audio = &_audiod_itf[itf];
#if CFG_TUD_AUDIO_EPSIZE_OUT
if (audio->ep_out == 0)
{
return false;
}
#if CFG_TUD_AUDIO_EP_OUT_SW_BUFFER_SIZE
if (audio->ep_out == 0) return false;
#endif
#if CFG_TUD_AUDIO_EPSIZE_IN
if (audio->ep_in == 0)
{
return false;
}
#if CFG_TUD_AUDIO_EP_IN_SW_BUFFER_SIZE
if (audio->ep_in == 0) return false;
#endif
#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
if (audio->ep_int_ctr == 0)
{
return false;
}
if (audio->ep_int_ctr == 0) return false;
#endif
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
if (audio->ep_fb == 0)
{
return false;
}
if (audio->ep_fb == 0) return false;
#endif
return true;
@ -204,43 +189,58 @@ bool tud_audio_n_mounted(uint8_t itf)
// READ API
//--------------------------------------------------------------------+
#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);
return tu_fifo_count(&_audiod_itf[itf].rx_ff[channelId]);
}
#if CFG_TUD_AUDIO_EP_OUT_SW_BUFFER_SIZE
uint16_t tud_audio_n_read(uint8_t itf, uint8_t channelId, void* buffer, uint16_t bufsize)
{
TU_VERIFY(channelId < CFG_TUD_AUDIO_N_CHANNELS_RX);
return tu_fifo_read_n(&_audiod_itf[itf].rx_ff[channelId], buffer, bufsize);
}
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]);
TU_VERIFY(itf < CFG_TUD_AUDIO && _audiod_itf[itf].p_desc != NULL, );
return tu_fifo_count(&_audiod_itf[itf].ep_out_ff);
}
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);
TU_VERIFY(itf < CFG_TUD_AUDIO && _audiod_itf[itf].p_desc != NULL, );
return tu_fifo_read_n(&_audiod_itf[itf].ep_out_ff, buffer, bufsize);
}
void tud_audio_n_read_flush (uint8_t itf)
void tud_audio_n_clear_ep_out_ff(uint8_t itf)
{
tu_fifo_clear(&_audiod_itf[itf].rx_ff[0]);
TU_VERIFY(itf < CFG_TUD_AUDIO && _audiod_itf[itf].p_desc != NULL, );
return tu_fifo_clear(&_audiod_itf[itf].ep_out_ff);
}
#if CFG_TUD_AUDIO_RX_SUPPORT_SW_FIFO_SIZE
// Delete all content in the support RX FIFOs
void tud_audio_n_clear_rx_support_ff(uint8_t itf, uint8_t channelId)
{
TU_VERIFY(itf < CFG_TUD_AUDIO && _audiod_itf[itf].p_desc != NULL, channelId < CFG_TUD_AUDIO_N_CHANNELS_RX, );
tu_fifo_clear(&_audiod_itf[itf].rx_ff[channelId]);
}
uint16_t tud_audio_n_available_support_ff(uint8_t itf, uint8_t channelId)
{
TU_VERIFY(itf < CFG_TUD_AUDIO && _audiod_itf[itf].p_desc != NULL, channelId < CFG_TUD_AUDIO_N_CHANNELS_RX, );
tu_fifo_count(&_audiod_itf[itf].rx_ff[channelId]);
}
uint16_t tud_audio_n_read_support_ff(uint8_t itf, uint8_t channelId, void* buffer, uint16_t bufsize)
{
TU_VERIFY(itf < CFG_TUD_AUDIO && _audiod_itf[itf].p_desc != NULL, channelId < CFG_TUD_AUDIO_N_CHANNELS_RX, );
return tu_fifo_read_n(&_audiod_itf[itf].rx_ff[channelId], buffer, bufsize);
}
#endif
#endif
#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
uint16_t tud_audio_int_ctr_n_available(uint8_t itf)
@ -261,9 +261,9 @@ void tud_audio_int_ctr_n_read_flush (uint8_t itf)
#endif
// This function is called once something is received by USB and is responsible for decoding received stream into audio channels.
// If you prefer your own (more efficient) implementation suiting your purpose set CFG_TUD_AUDIO_RX_FIFO_SIZE = 0.
// If you prefer your own (more efficient) implementation suiting your purpose set CFG_TUD_AUDIO_RX_SUPPORT_SW_FIFO_SIZE = 0.
#if CFG_TUD_AUDIO_EPSIZE_OUT
#if CFG_TUD_AUDIO_EP_OUT_SW_BUFFER_SIZE
static bool audio_rx_done_cb(uint8_t rhport, audiod_interface_t* audio, uint8_t* buffer, uint16_t bufsize)
{
@ -281,7 +281,7 @@ static bool audio_rx_done_cb(uint8_t rhport, audiod_interface_t* audio, uint8_t*
{
case AUDIO_DATA_FORMAT_TYPE_I_PCM:
#if CFG_TUD_AUDIO_RX_FIFO_SIZE
#if CFG_TUD_AUDIO_RX_SUPPORT_SW_FIFO_SIZE
TU_VERIFY(audio_rx_done_type_I_pcm_ff_cb(rhport, audio, buffer, bufsize));
#else
#error YOUR DECODING AND BUFFERING IS REQUIRED HERE!
@ -309,11 +309,11 @@ static bool audio_rx_done_cb(uint8_t rhport, audiod_interface_t* audio, uint8_t*
return true;
}
#endif //CFG_TUD_AUDIO_EPSIZE_OUT
#endif //CFG_TUD_AUDIO_EP_OUT_SW_BUFFER_SIZE
// 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
// The following functions are used in case CFG_TUD_AUDIO_RX_SUPPORT_SW_FIFO_SIZE != 0
#if CFG_TUD_AUDIO_RX_SUPPORT_SW_FIFO_SIZE
#if CFG_TUD_AUDIO_N_CHANNELS_RX > 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;
@ -365,13 +365,53 @@ static bool audio_rx_done_type_I_pcm_ff_cb(uint8_t rhport, audiod_interface_t *a
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
#endif // CFG_TUD_AUDIO_N_CHANNELS_RX > 1
#endif //CFG_TUD_AUDIO_RX_SUPPORT_SW_FIFO_SIZE
//--------------------------------------------------------------------+
// WRITE API
//--------------------------------------------------------------------+
#if CFG_TUD_AUDIO_EP_IN_SW_BUFFER_SIZE
uint16_t tud_audio_n_write(uint8_t itf, const void * data, uint16_t len)
{
TU_VERIFY(itf < CFG_TUD_AUDIO && _audiod_itf[itf].p_desc != NULL, );
return tu_fifo_write_n(&_audiod_itf[itf].ep_in_ff, data, len);
}
void tud_audio_n_clear_ep_in_ff(uint8_t itf) // Delete all content in the EP IN FIFO
{
TU_VERIFY(itf < CFG_TUD_AUDIO && _audiod_itf[itf].p_desc != NULL, );
tu_fifo_clear(&_audiod_itf[itf].ep_in_ff);
}
#if CFG_TUD_AUDIO_TX_SUPPORT_SW_FIFO_SIZE
uint16_t tud_audio_n_flush_tx_support_ff(uint8_t itf) // Force all content in the support TX FIFOs to be written into EP SW FIFO
{
TU_VERIFY(itf < CFG_TUD_AUDIO && _audiod_itf[itf].p_desc != NULL, );
audiod_interface_t* audio = &_audiod_itf[itf];
uint16_t n_bytes_copied;
TU_VERIFY(audiod_tx_done_cb(audio->rhport, audio, &n_bytes_copied));
return n_bytes_copied;
}
uint16_t tud_audio_n_clear_tx_support_ff (uint8_t itf, uint8_t channelId);
uint16_t tud_audio_n_write_support_ff(uint8_t itf, uint8_t channelId, const void * data, uint16_t len)
{
TU_VERIFY(itf < CFG_TUD_AUDIO && _audiod_itf[itf].p_desc != NULL, channelId < CFG_TUD_AUDIO_N_CHANNELS_TX, );
return tu_fifo_write_n(&audio->tx_ff[channelId], data, len);
}
#endif
#endif
/**
* \brief Write data to EP in buffer
*
@ -383,50 +423,28 @@ 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_EP_IN_SW_BUFFER_SIZE
#if !CFG_TUD_AUDIO_TX_SUPPORT_SW_FIFO_SIZE
This function is intended for later use once EP buffers (at least for ISO EPs) are implemented as ring buffers
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];
if (audio->p_desc == NULL) {
return 0;
}
if (audio->p_desc == NULL) return 0;
// THIS IS A CRITICAL SECTION - audio->epin_buf_cnt MUST NOT BE MODIFIED FROM HERE - happens if audiod_tx_done_cb() is executed in between!
// FOR SINGLE THREADED OPERATION:
// AS LONG AS THIS FUNCTION IS NOT EXECUTED WITHIN AN INTERRUPT ALL IS FINE!
// Determine free space
uint16_t free = CFG_TUD_AUDIO_EPSIZE_IN - audio->epin_buf_cnt;
// Clip length if needed
if (len > free) len = free;
// Write data
memcpy((void *) &audio->epin_buf[audio->epin_buf_cnt], data, len);
audio->epin_buf_cnt += len;
// Return number of bytes written
return len;
return tu_fifo_write_n(&audio->ep_in_ff, data, len);
}
*/
#else
#if CFG_TUD_AUDIO_TX_FIFO_COUNT == 1
#if CFG_TUD_AUDIO_N_CHANNELS_TX == 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)
{
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);
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)
@ -471,11 +489,11 @@ uint32_t tud_audio_int_ctr_n_write(uint8_t itf, uint8_t const* buffer, uint32_t
// This function is called once a transmit of an audio packet was successfully completed. Here, we encode samples and place it in IN EP's buffer for next transmission.
// If you prefer your own (more efficient) implementation suiting your purpose set CFG_TUD_AUDIO_TX_FIFO_SIZE = 0 and use tud_audio_n_write_ep_in_buffer() (NOT IMPLEMENTED SO FAR).
// If you prefer your own (more efficient) implementation suiting your purpose set CFG_TUD_AUDIO_TX_SUPPORT_SW_FIFO_SIZE = 0 and use tud_audio_n_write_ep_in_buffer() (NOT IMPLEMENTED SO FAR).
// n_bytes_copied - Informs caller how many bytes were loaded. In case n_bytes_copied = 0, a ZLP is scheduled to inform host no data is available for current frame.
#if CFG_TUD_AUDIO_EPSIZE_IN
static bool audiod_tx_done_cb(uint8_t rhport, audiod_interface_t* audio, uint16_t * n_bytes_copied)
#if CFG_TUD_AUDIO_EP_IN_SW_BUFFER_SIZE
static bool audiod_tx_done_cb(uint8_t rhport, audiod_interface_t * audio, uint16_t * n_bytes_copied)
{
uint8_t idxDriver, idxItf;
uint8_t const *dummy2;
@ -488,10 +506,10 @@ static bool audiod_tx_done_cb(uint8_t rhport, audiod_interface_t* audio, uint16_
}
// Call a weak callback here - a possibility for user to get informed former TX was completed and data gets now loaded into EP in buffer (in case FIFOs are used) or
// if no FIFOs are used the user may use this call back to load its data into the EP in buffer by use of tud_audio_n_write_ep_in_buffer().
// if no FIFOs are used the user may use this call back to load its data into the EP IN buffer by use of tud_audio_n_write_ep_in_buffer().
if (tud_audio_tx_done_pre_load_cb) TU_VERIFY(tud_audio_tx_done_pre_load_cb(rhport, idxDriver, audio->ep_in, audio->altSetting[idxItf]));
#if CFG_TUD_AUDIO_TX_FIFO_SIZE
#if CFG_TUD_AUDIO_TX_SUPPORT_SW_FIFO_SIZE
switch (CFG_TUD_AUDIO_FORMAT_TYPE_TX)
{
case AUDIO_FORMAT_TYPE_UNDEFINED:
@ -526,30 +544,11 @@ static bool audiod_tx_done_cb(uint8_t rhport, audiod_interface_t* audio, uint16_
}
#endif
// THIS IS A CRITICAL SECTION - audio->epin_buf_cnt MUST NOT BE MODIFIED FROM HERE - happens if tud_audio_n_write_ep_in_buffer() is executed in between!
// THIS IS NOT SOLVED SO FAR!
// FOR SINGLE THREADED OPERATION:
// THIS FUNCTION IS NOT EXECUTED WITHIN AN INTERRUPT SO IT DOES NOT INTERRUPT tud_audio_n_write_ep_in_buffer()! AS LONG AS tud_audio_n_write_ep_in_buffer() IS NOT EXECUTED WITHIN AN INTERRUPT ALL IS FINE!
// Inform how many bytes will be copied
*n_bytes_copied = tu_fifo_count(&audio->ep_in_ff);
// Schedule transmit
// TU_VERIFY(usbd_edpt_xfer(rhport, audio->ep_in, audio->epin_buf, audio->epin_buf_cnt));
TU_VERIFY(usbd_edpt_iso_xfer(rhport, audio->ep_in, &audio->epin_ff));
// Inform how many bytes were copied
*n_bytes_copied = audio->epin_buf_cnt;
// Declare EP in buffer empty
audio->epin_buf_cnt = 0;
// TO HERE
TU_VERIFY(usbd_edpt_iso_xfer(rhport, audio->ep_in, &audio->ep_in_ff, *n_bytes_copied));
// Call a weak callback here - a possibility for user to get informed former TX was completed and how many bytes were loaded for the next frame
if (tud_audio_tx_done_post_load_cb) TU_VERIFY(tud_audio_tx_done_post_load_cb(rhport, *n_bytes_copied, idxDriver, audio->ep_in, audio->altSetting[idxItf]));
@ -557,19 +556,18 @@ static bool audiod_tx_done_cb(uint8_t rhport, audiod_interface_t* audio, uint16_
return true;
}
#endif //CFG_TUD_AUDIO_EPSIZE_IN
#endif //CFG_TUD_AUDIO_EP_IN_SW_BUFFER_SIZE
#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)
#if CFG_TUD_AUDIO_TX_SUPPORT_SW_FIFO_SIZE
#if CFG_TUD_AUDIO_N_CHANNELS_TX > 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
// We encode directly into IN EP's FIFO - abort if previous transfer not complete
TU_VERIFY(!usbd_edpt_busy(rhport, audio->ep_in));
// 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 const nEndpointSampleCapacity = CFG_TUD_AUDIO_EP_IN_SW_BUFFER_SIZE / CFG_TUD_AUDIO_N_CHANNELS_TX / CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX;
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++)
@ -582,58 +580,47 @@ static bool audiod_tx_done_type_I_pcm_ff_cb(uint8_t rhport, audiod_interface_t*
}
// Check if there is enough
if (nSamplesPerChannelToSend == 0)
{
audio->epin_buf_cnt = 0;
return true;
}
if (nSamplesPerChannelToSend == 0) return true;
// Limit to maximum sample number - THIS IS A POSSIBLE ERROR SOURCE IF TOO MANY SAMPLE WOULD NEED TO BE SENT BUT CAN NOT!
nSamplesPerChannelToSend = tu_min16(nSamplesPerChannelToSend, nEndpointSampleCapacity);
nBytesToSend = nSamplesPerChannelToSend * CFG_TUD_AUDIO_N_CHANNELS_TX * CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX;
// Encode
uint16_t cntSample;
uint8_t * pBuff = audio->epin_buf;
#if CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX == 1
uint8_t sample;
#elif CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX == 2
uint16_t sample;
#else
uint32_t sample;
#endif
// TODO: Big endianess handling
for (cntSample = 0; cntSample < nSamplesPerChannelToSend; cntSample++)
{
for (cntChannel = 0; cntChannel < CFG_TUD_AUDIO_N_CHANNELS_TX; cntChannel++)
{
// If 8, 16, or 32 bit values are to be copied
#if CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX == CFG_TUD_AUDIO_TX_ITEMSIZE
tu_fifo_read_n_into_other_fifo(&audio->tx_ff[cntChannel], &audio->ep_in_ff, 0, CFG_TUD_AUDIO_TX_ITEMSIZE);
#else
// TODO: Implement a left and right justified 24 to 32 and vice versa copy process from FIFO to FIFO
uint32_t sample = 0;
// Get sample from buffer
tu_fifo_read_n(&audio->tx_ff[cntChannel], &sample, CFG_TUD_AUDIO_TX_ITEMSIZE);
tu_fifo_read_n(&audio->tx_ff[cntChannel], &sample, CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX);
// Put it into EP's buffer - Let alignment problems be handled by memcpy
memcpy(pBuff, &sample, CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX);
// Advance pointer
pBuff += CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX;
tu_fifo_write_n(&audio->ep_in_ff, &sample, CFG_TUD_AUDIO_TX_ITEMSIZE);
#endif
}
}
audio->epin_buf_cnt = nBytesToSend;
return true;
}
#else
static bool audiod_tx_done_type_I_pcm_ff_cb(uint8_t rhport, audiod_interface_t* audio)
{
// TODO GET RID OF SINGLE TX_FIFO!
// 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);
nByteCount = tu_min16(nByteCount, CFG_TUD_AUDIO_EP_IN_SW_BUFFER_SIZE);
// Check if there is enough
if (nByteCount == 0)
@ -641,20 +628,17 @@ static bool audiod_tx_done_type_I_pcm_ff_cb(uint8_t rhport, audiod_interface_t*
return true;
}
nByteCount = tu_fifo_read_n_into_other_fifo(&audio->tx_ff[0], &audio->epin_ff, 0, nByteCount);
// nByteCount = tu_fifo_read_n(&audio->tx_ff[0], audio->epin_buf, nByteCount);
audio->epin_buf_cnt = nByteCount;
nByteCount = tu_fifo_read_n_into_other_fifo(&audio->tx_ff[0], &audio->ep_in_ff, 0, 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_N_CHANNELS_TX > 1 || (CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX != CFG_TUD_AUDIO_TX_ITEMSIZE)
#endif //CFG_TUD_AUDIO_TX_FIFO_SIZE
#endif //CFG_TUD_AUDIO_TX_SUPPORT_SW_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
#if CFG_TUD_AUDIO_EPSIZE_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
#if CFG_TUD_AUDIO_EP_OUT_SW_BUFFER_SIZE && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
static bool audio_fb_send(uint8_t rhport, audiod_interface_t *audio)
{
uint8_t fb[4];
@ -738,28 +722,34 @@ void audiod_init(void)
{
audiod_interface_t* audio = &_audiod_itf[i];
// Initialize IN EP FIFO if required
#if CFG_TUD_AUDIO_EP_IN_SW_BUFFER_SIZE
// Initialize IN EP FIFO
tu_fifo_config(&audio->ep_in_ff, &audio->ep_in_buf, CFG_TUD_AUDIO_EP_IN_SW_BUFFER_SIZE, 1, true);
#endif
// Initialize TX FIFOs if required
#if CFG_TUD_AUDIO_EPSIZE_IN && CFG_TUD_AUDIO_TX_FIFO_SIZE
for (uint8_t cnt = 0; cnt < CFG_TUD_AUDIO_TX_FIFO_COUNT; cnt++)
#if CFG_TUD_AUDIO_EP_IN_SW_BUFFER_SIZE && CFG_TUD_AUDIO_TX_SUPPORT_SW_FIFO_SIZE
for (uint8_t cnt = 0; cnt < CFG_TUD_AUDIO_N_CHANNELS_TX; cnt++)
{
tu_fifo_config(&audio->tx_ff[cnt], &audio->tx_ff_buf[cnt], CFG_TUD_AUDIO_TX_FIFO_SIZE, 1, true);
tu_fifo_config(&audio->tx_ff[cnt], &audio->tx_ff_buf[cnt], CFG_TUD_AUDIO_TX_SUPPORT_SW_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
}
// Initialize IN EP FIFO
tu_fifo_config(&audio->epin_ff, &audio->epin_buf, CFG_TUD_AUDIO_EPSIZE_IN, 1, true);
#endif
#if CFG_TUD_AUDIO_EPSIZE_OUT && CFG_TUD_AUDIO_RX_FIFO_SIZE
for (uint8_t cnt = 0; cnt < CFG_TUD_AUDIO_RX_FIFO_COUNT; cnt++)
#if CFG_TUD_AUDIO_EP_OUT_SW_BUFFER_SIZE && CFG_TUD_AUDIO_RX_SUPPORT_SW_FIFO_SIZE
for (uint8_t cnt = 0; cnt < CFG_TUD_AUDIO_N_CHANNELS_RX; cnt++)
{
tu_fifo_config(&audio->rx_ff[cnt], &audio->rx_ff_buf[cnt], CFG_TUD_AUDIO_RX_FIFO_SIZE, 1, true);
tu_fifo_config(&audio->rx_ff[cnt], &audio->rx_ff_buf[cnt], CFG_TUD_AUDIO_RX_SUPPORT_SW_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
// Initialize OUT EP FIFO
tu_fifo_config(&audio->ep_out_ff, &audio->ep_out_buf, CFG_TUD_AUDIO_EP_OUT_SW_BUFFER_SIZE, 1, true);
}
#endif
@ -781,19 +771,19 @@ void audiod_reset(uint8_t rhport)
audiod_interface_t* audio = &_audiod_itf[i];
tu_memclr(audio, ITF_MEM_RESET_SIZE);
#if CFG_TUD_AUDIO_EPSIZE_IN
tu_fifo_clear(&audio->epin_ff);
#if CFG_TUD_AUDIO_EP_IN_SW_BUFFER_SIZE
tu_fifo_clear(&audio->ep_in_ff);
#endif
#if CFG_TUD_AUDIO_EPSIZE_IN && CFG_TUD_AUDIO_TX_FIFO_SIZE
for (uint8_t cnt = 0; cnt < CFG_TUD_AUDIO_TX_FIFO_COUNT; cnt++)
#if CFG_TUD_AUDIO_EP_IN_SW_BUFFER_SIZE && CFG_TUD_AUDIO_TX_SUPPORT_SW_FIFO_SIZE
for (uint8_t cnt = 0; cnt < CFG_TUD_AUDIO_N_CHANNELS_TX; cnt++)
{
tu_fifo_clear(&audio->tx_ff[cnt]);
}
#endif
#if CFG_TUD_AUDIO_EPSIZE_OUT && CFG_TUD_AUDIO_RX_FIFO_SIZE
for (uint8_t cnt = 0; cnt < CFG_TUD_AUDIO_RX_FIFO_COUNT; cnt++)
#if CFG_TUD_AUDIO_EP_OUT_SW_BUFFER_SIZE && CFG_TUD_AUDIO_RX_SUPPORT_SW_FIFO_SIZE
for (uint8_t cnt = 0; cnt < CFG_TUD_AUDIO_N_CHANNELS_RX; cnt++)
{
tu_fifo_clear(&audio->rx_ff[cnt]);
}
@ -891,7 +881,7 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *
TU_VERIFY(audiod_get_AS_interface_index(itf, &idxDriver, &idxItf, &p_desc));
// Look if there is an EP to be closed - for this driver, there are only 3 possible EPs which may be closed (only AS related EPs can be closed, AC EP (if present) is always open)
#if CFG_TUD_AUDIO_EPSIZE_IN > 0
#if CFG_TUD_AUDIO_EP_IN_SW_BUFFER_SIZE > 0
if (_audiod_itf[idxDriver].ep_in_as_intf_num == itf)
{
_audiod_itf[idxDriver].ep_in_as_intf_num = 0;
@ -904,7 +894,7 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *
}
#endif
#if CFG_TUD_AUDIO_EPSIZE_OUT
#if CFG_TUD_AUDIO_EP_OUT_SW_BUFFER_SIZE
if (_audiod_itf[idxDriver].ep_out_as_intf_num == itf)
{
_audiod_itf[idxDriver].ep_out_as_intf_num = 0;
@ -945,7 +935,7 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *
// We need to set EP non busy since this is not taken care of right now in ep_close() - THIS IS A WORKAROUND!
usbd_edpt_clear_stall(rhport, ep_addr);
#if CFG_TUD_AUDIO_EPSIZE_IN > 0
#if CFG_TUD_AUDIO_EP_IN_SW_BUFFER_SIZE > 0
if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN && ((tusb_desc_endpoint_t const *) p_desc)->bmAttributes.usage == 0x00) // Check if usage is data EP
{
// Save address
@ -961,7 +951,7 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *
}
#endif
#if CFG_TUD_AUDIO_EPSIZE_OUT
#if CFG_TUD_AUDIO_EP_OUT_SW_BUFFER_SIZE
if (tu_edpt_dir(ep_addr) == TUSB_DIR_OUT) // Checking usage not necessary
{
@ -973,7 +963,7 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *
if (tud_audio_set_itf_cb) TU_VERIFY(tud_audio_set_itf_cb(rhport, p_request));
// Prepare for incoming data
TU_ASSERT(usbd_edpt_xfer(rhport, ep_addr, _audiod_itf[idxDriver].epout_buf, CFG_TUD_AUDIO_EPSIZE_OUT), false);
TU_ASSERT(usbd_edpt_xfer(rhport, ep_addr, _audiod_itf[idxDriver].ep_out_buf, CFG_TUD_AUDIO_EP_OUT_SW_BUFFER_SIZE), false);
}
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
@ -1242,7 +1232,7 @@ bool audiod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint3
#endif
#if CFG_TUD_AUDIO_EPSIZE_IN
#if CFG_TUD_AUDIO_EP_IN_SW_BUFFER_SIZE
// Data transmission of audio packet finished
if (_audiod_itf[idxDriver].ep_in == ep_addr)
@ -1264,16 +1254,16 @@ bool audiod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint3
}
#endif
#if CFG_TUD_AUDIO_EPSIZE_OUT
#if CFG_TUD_AUDIO_EP_OUT_SW_BUFFER_SIZE
// New audio packet received
if (_audiod_itf[idxDriver].ep_out == ep_addr)
{
// Save into buffer - do whatever has to be done
TU_VERIFY(audio_rx_done_cb(rhport, &_audiod_itf[idxDriver], _audiod_itf[idxDriver].epout_buf, xferred_bytes));
TU_VERIFY(audio_rx_done_cb(rhport, &_audiod_itf[idxDriver], _audiod_itf[idxDriver].ep_out_buf, xferred_bytes));
// prepare for next transmission
TU_ASSERT(usbd_edpt_xfer(rhport, ep_addr, _audiod_itf[idxDriver].epout_buf, CFG_TUD_AUDIO_EPSIZE_OUT), false);
TU_ASSERT(usbd_edpt_xfer(rhport, ep_addr, _audiod_itf[idxDriver].ep_out_buf, CFG_TUD_AUDIO_EP_OUT_SW_BUFFER_SIZE), false);
return true;
}

View File

@ -38,9 +38,11 @@
// Class Driver Configuration
//--------------------------------------------------------------------+
// All sizes are in bytes!
// Number of Standard AS Interface Descriptors (4.9.1) defined per audio function - this is required to be able to remember the current alternate settings of these interfaces - We restrict us here to have a constant number for all audio functions (which means this has to be the maximum number of AS interfaces an audio function has and a second audio function with less AS interfaces just waste a few bytes)
#ifndef CFG_TUD_AUDIO_N_AS_INT
#define CFG_TUD_AUDIO_N_AS_INT 0
#error You must tell the driver the number of Standard AS Interface Descriptors you have defined in the descriptors!
#endif
// Size of control buffer used to receive and send control messages via EP0 - has to be big enough to hold your biggest request structure e.g. range requests with multiple intervals defined or cluster descriptors
@ -48,20 +50,6 @@
#error You must define an audio class control request buffer size!
#endif
// Use of TX/RX FIFOs - If sizes are not zero, audio.c implements FIFOs for RX and TX (whatever defined).
// For RX: the input stream gets decoded into its corresponding channels, where for each channel a FIFO is setup to hold its data -> see: audio_rx_done_cb().
// For TX: the output stream is composed from CFG_TUD_AUDIO_N_CHANNELS_TX channels, where for each channel a FIFO is defined.
// Further, it implements encoding and decoding of the individual channels (parameterized by the defines below).
// If you don't use the FIFOs you need to handle encoding and decoding on your own in audio_rx_done_cb() and audio_tx_done_cb(). This, however, allows for optimizations.
#ifndef CFG_TUD_AUDIO_TX_FIFO_SIZE
#define CFG_TUD_AUDIO_TX_FIFO_SIZE 0 // Buffer size per channel
#endif
#ifndef CFG_TUD_AUDIO_RX_FIFO_SIZE
#define CFG_TUD_AUDIO_RX_FIFO_SIZE 0 // Buffer size per channel
#endif
// End point sizes - Limits: Full Speed <= 1023, High Speed <= 1024
#ifndef CFG_TUD_AUDIO_EPSIZE_IN
#define CFG_TUD_AUDIO_EPSIZE_IN 0 // TX
@ -71,12 +59,80 @@
#define CFG_TUD_AUDIO_EPSIZE_OUT 0 // RX
#endif
#ifndef CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
#define CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP 0 // Feedback
// Software EP FIFO buffer sizes - must be >= EP SIZEs!
#if CFG_TUD_AUDIO_EPSIZE_IN
#ifndef CFG_TUD_AUDIO_EP_IN_SW_BUFFER_SIZE
#define CFG_TUD_AUDIO_EP_IN_SW_BUFFER_SIZE CFG_TUD_AUDIO_EPSIZE_IN // TX
#endif
#endif
#if CFG_TUD_AUDIO_EPSIZE_OUT
#ifndef CFG_TUD_AUDIO_EP_OUT_SW_BUFFER_SIZE
#define CFG_TUD_AUDIO_EP_OUT_SW_BUFFER_SIZE CFG_TUD_AUDIO_EPSIZE_OUT // RX
#endif
#endif
// General information of number of TX and/or RX channels - is used in case support FIFOs (see below) are used and can be used for descriptor definitions
#ifndef CFG_TUD_AUDIO_N_CHANNELS_TX
#define CFG_TUD_AUDIO_N_CHANNELS_TX 0
#endif
#ifndef CFG_TUD_AUDIO_N_CHANNELS_RX
#define CFG_TUD_AUDIO_N_CHANNELS_RX 0
#endif
// Use of TX/RX support FIFOs
// Support FIFOs are not mandatory for the audio driver, rather they are intended to be of use in
// - TX case: CFG_TUD_AUDIO_N_CHANNELS_TX channels need to be encoded into one USB output stream (currently PCM type I is implemented)
// - RX case: CFG_TUD_AUDIO_N_CHANNELS_RX channels need to be decoded from a single USB input stream (currently PCM type I is implemented)
//
// This encoding/decoding is done in software and thus time consuming. If you can encode/decode your stream more efficiently do not use the
// support FIFOs but write/read directly into/from the EP_X_SW_BUFFER_FIFOs using
// - tud_audio_n_write() or
// - tud_audio_n_read().
// To write/read to/from the support FIFOs use
// - tud_audio_n_write_support_ff() or
// - tud_audio_n_read_support_ff().
//
// The encoding/decoding format type done is defined below.
//
// The encoding/decoding starts when the private callback functions
// - audio_tx_done_cb()
// - audio_rx_done_cb()
// are invoked. If support FIFOs are used the corresponding encoding/decoding functions are called from there.
// Once encoding/decoding is done the result is put directly into the EP_X_SW_BUFFER_FIFOs. You can use the public callback functions
// - tud_audio_tx_done_pre_load_cb() or tud_audio_tx_done_post_load_cb()
// - tud_audio_rx_done_pre_read_cb() or tud_audio_rx_done_post_read_cb()
// if you want to get informed what happened.
//
// If you don't use the support FIFOs you may use the public callback functions
// - tud_audio_tx_done_pre_load_cb() or tud_audio_tx_done_post_load_cb()
// - tud_audio_rx_done_pre_read_cb() or tud_audio_rx_done_post_read_cb()
// to write/read from/into the EP_X_SW_BUFFER_FIFOs at the right time.
//
// If you need a different encoding which is not support so far implement it in the
// - audio_tx_done_cb()
// - audio_rx_done_cb()
// functions.
// Size of support FIFOs - if size > 0 there are as many FIFOs set up as TX/RX channels defined
#ifndef CFG_TUD_AUDIO_TX_SUPPORT_SW_FIFO_SIZE
#define CFG_TUD_AUDIO_TX_SUPPORT_SW_FIFO_SIZE 0 // Buffer size per channel - minimum size: ceil(f_s/1000)*CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX
#endif
#ifndef CFG_TUD_AUDIO_RX_SUPPORT_SW_FIFO_SIZE
#define CFG_TUD_AUDIO_RX_SUPPORT_SW_FIFO_SIZE 0 // Buffer size per channel - minimum size: ceil(f_s/1000)*CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX
#endif
// Enable/disable feedback EP (required for asynchronous RX applications)
#ifndef CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
#define CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP 0 // Feedback - 0 or 1
#endif
// Audio interrupt control EP size - disabled if 0
#ifndef CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
#define CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN 0 // Audio interrupt control
#define CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN 0 // Audio interrupt control - if required - 6 Bytes according to UAC 2 specification (p. 74)
#endif
#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
@ -85,15 +141,8 @@
#endif
#endif
#ifndef CFG_TUD_AUDIO_N_CHANNELS_TX
#define CFG_TUD_AUDIO_N_CHANNELS_TX 1
#endif
#ifndef CFG_TUD_AUDIO_N_CHANNELS_RX
#define CFG_TUD_AUDIO_N_CHANNELS_RX 1
#endif
// Audio data format types
// Audio data format types - look in audio.h for existing types
// Used in case support FIFOs are used
#ifndef CFG_TUD_AUDIO_FORMAT_TYPE_TX
#define CFG_TUD_AUDIO_FORMAT_TYPE_TX AUDIO_FORMAT_TYPE_UNDEFINED // If this option is used, an encoding function has to be implemented in audio_device.c
#endif
@ -110,8 +159,8 @@
#define CFG_TUD_AUDIO_FORMAT_TYPE_I_TX AUDIO_DATA_FORMAT_TYPE_I_PCM
#endif
#ifndef CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX // bSubslotSize
#define CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX 1
#ifndef CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX // bSubslotSize
#define CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX 1
#endif
#ifndef CFG_TUD_AUDIO_TX_ITEMSIZE
@ -136,8 +185,8 @@
#define CFG_TUD_AUDIO_FORMAT_TYPE_I_RX AUDIO_DATA_FORMAT_TYPE_I_PCM
#endif
#ifndef CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX // bSubslotSize
#define CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX 1
#ifndef CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX // bSubslotSize
#define CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX 1
#endif
#if CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX == 1
@ -170,69 +219,84 @@ 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
#if CFG_TUD_AUDIO_EP_OUT_SW_BUFFER_SIZE
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_clear_ep_out_ff (uint8_t itf); // Delete all content in the EP OUT FIFO
#if CFG_TUD_AUDIO_RX_SUPPORT_SW_FIFO_SIZE
void tud_audio_n_clear_rx_support_ff (uint8_t itf, uint8_t channelId); // Delete all content in the support RX FIFOs
uint16_t tud_audio_n_available_support_ff (uint8_t itf, uint8_t channelId);
uint16_t tud_audio_n_read_support_ff (uint8_t itf, uint8_t channelId, void* buffer, uint16_t bufsize);
#endif
/* 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)
#endif
*/
#ifndef CFG_TUD_AUDIO_TX_FIFO_COUNT
#define CFG_TUD_AUDIO_TX_FIFO_COUNT 1
#endif
#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);
#if CFG_TUD_AUDIO_EP_IN_SW_BUFFER_SIZE
uint16_t tud_audio_n_write (uint8_t itf, const void * data, uint16_t len);
void tud_audio_n_clear_ep_in_ff (uint8_t itf); // Delete all content in the EP IN FIFO
#if CFG_TUD_AUDIO_TX_SUPPORT_SW_FIFO_SIZE
uint16_t tud_audio_n_flush_tx_support_ff (uint8_t itf); // Force all content in the support TX FIFOs to be written into EP SW FIFO
uint16_t tud_audio_n_clear_tx_support_ff (uint8_t itf, uint8_t channelId);
uint16_t tud_audio_n_write_support_ff (uint8_t itf, uint8_t channelId, const void * data, uint16_t len);
#endif
#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN > 0
uint16_t tud_audio_int_ctr_n_available (uint8_t itf);
uint16_t tud_audio_int_ctr_n_read (uint8_t itf, void* buffer, uint16_t bufsize);
void tud_audio_int_ctr_n_read_flush (uint8_t itf);
uint16_t tud_audio_int_ctr_n_write (uint8_t itf, uint8_t const* buffer, uint16_t bufsize);
#endif
#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
uint16_t tud_audio_int_ctr_n_available (uint8_t itf);
uint16_t tud_audio_int_ctr_n_read (uint8_t itf, void* buffer, uint16_t bufsize);
void tud_audio_int_ctr_n_clear (uint8_t itf); // Delete all content in the AUDIO_INT_CTR FIFO
uint16_t tud_audio_int_ctr_n_write (uint8_t itf, uint8_t const* buffer, uint16_t len);
#endif
//--------------------------------------------------------------------+
// Application API (Interface0)
//--------------------------------------------------------------------+
static inline bool tud_audio_mounted (void);
static inline bool tud_audio_mounted (void);
#if CFG_TUD_AUDIO_EPSIZE_OUT && CFG_TUD_AUDIO_RX_FIFO_SIZE
static inline uint16_t tud_audio_available (void);
static inline uint16_t tud_audio_read (void* buffer, uint16_t bufsize);
static inline void tud_audio_read_flush (void);
// RX API
#if CFG_TUD_AUDIO_EP_OUT_SW_BUFFER_SIZE
static inline uint16_t tud_audio_available (void);
static inline void tud_audio_clear_ep_out_ff (void); // Delete all content in the EP OUT FIFO
static inline uint16_t tud_audio_read (void* buffer, uint16_t bufsize);
#if CFG_TUD_AUDIO_RX_SUPPORT_SW_FIFO_SIZE
static inline void tud_audio_clear_rx_support_ff (uint8_t channelId);
static inline uint16_t tud_audio_available_support_ff (uint8_t channelId);
static inline uint16_t tud_audio_read_support_ff (uint8_t channelId, void* buffer, uint16_t bufsize);
#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
static inline uint32_t tud_audio_int_ctr_available (void);
static inline uint32_t tud_audio_int_ctr_read (void* buffer, uint32_t bufsize);
static inline void tud_audio_int_ctr_read_flush (void);
static inline uint32_t tud_audio_int_ctr_write (uint8_t const* buffer, uint32_t bufsize);
// TX API
#if CFG_TUD_AUDIO_EP_IN_SW_BUFFER_SIZE
static inline uint16_t tud_audio_write (const void * data, uint16_t len);
static inline uint16_t tud_audio_clear_ep_in_ff (void);
#if CFG_TUD_AUDIO_TX_SUPPORT_SW_FIFO_SIZE
static inline uint16_t tud_audio_flush_tx_support_ff (void);
static inline uint16_t tud_audio_clear_tx_support_ff (uint8_t channelId);
static inline uint16_t tud_audio_write_support_ff (uint8_t channelId, const void * data, uint16_t len);
#endif
#endif
// INT CTR API
#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
static inline uint16_t tud_audio_int_ctr_available (void);
static inline uint16_t tud_audio_int_ctr_read (void* buffer, uint16_t bufsize);
static inline void tud_audio_int_ctr_clear (void);
static inline uint16_t tud_audio_int_ctr_write (uint8_t const* buffer, uint16_t len);
#endif
// Buffer control EP data and schedule a transmit
@ -247,16 +311,17 @@ bool tud_audio_buffer_and_schedule_control_xfer(uint8_t rhport, tusb_control_req
// Application Callback API (weak is optional)
//--------------------------------------------------------------------+
#if CFG_TUD_AUDIO_EPSIZE_IN
#if CFG_TUD_AUDIO_EP_IN_SW_BUFFER_SIZE
TU_ATTR_WEAK bool tud_audio_tx_done_pre_load_cb(uint8_t rhport, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting);
TU_ATTR_WEAK bool tud_audio_tx_done_post_load_cb(uint8_t rhport, uint16_t n_bytes_copied, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting);
#endif
#if CFG_TUD_AUDIO_EPSIZE_OUT
TU_ATTR_WEAK bool tud_audio_rx_done_cb(uint8_t rhport, uint8_t * buffer, uint16_t bufsize);
#if CFG_TUD_AUDIO_EP_OUT_SW_BUFFER_SIZE
TU_ATTR_WEAK bool tud_audio_rx_done_pre_read_cb(uint8_t rhport, uint8_t itf, uint8_t ep_out, uint8_t cur_alt_setting);
TU_ATTR_WEAK bool tud_audio_rx_done_post_read_cb(uint8_t rhport, uint16_t n_bytes_copied, uint8_t itf, uint8_t ep_out, uint8_t cur_alt_setting);
#endif
#if CFG_TUD_AUDIO_EPSIZE_OUT > 0 && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
#if CFG_TUD_AUDIO_EP_OUT_SW_BUFFER_SIZE && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
TU_ATTR_WEAK bool tud_audio_fb_done_cb(uint8_t rhport);
// User code should call this function with feedback value in 16.16 format for FS and HS.
// Value will be corrected for FS to 10.14 format automatically.
@ -302,64 +367,82 @@ static inline bool tud_audio_mounted(void)
return tud_audio_n_mounted(0);
}
#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, 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
// RX API
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_EP_OUT_SW_BUFFER_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);
}
static inline uint16_t tud_audio_read(uint8_t channelId, void* buffer, uint16_t bufsize)
{
return tud_audio_n_read(0, channelId, buffer, bufsize);
}
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)
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)
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)
static inline uint16_t tud_audio_clear_ep_out_ff (void)
{
tud_audio_n_read_flush(0);
return tud_audio_n_clear_ep_out_ff(0);
}
#endif
#if CFG_TUD_AUDIO_RX_SUPPORT_SW_FIFO_SIZE
static inline void tud_audio_clear_rx_support_ff (uint8_t channelId)
{
tud_audio_n_clear_rx_support_ff(0, channelId);
}
static inline uint16_t tud_audio_available_support_ff (uint8_t channelId)
{
return tud_audio_n_available_support_ff(0, channelId);
}
static inline uint16_t tud_audio_read_support_ff (uint8_t channelId, void* buffer, uint16_t bufsize)
{
return tud_audio_n_read_support_ff(0, channelId, buffer, bufsize);
}
#endif
#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN > 0
#endif
// TX API
#if CFG_TUD_AUDIO_EP_IN_SW_BUFFER_SIZE
static inline uint16_t tud_audio_write (const void * data, uint16_t len)
{
return tud_audio_n_write(0, data, len);
}
static inline uint16_t tud_audio_clear_ep_in_ff (void)
{
return tud_audio_n_clear_ep_in_ff(0);
}
#if CFG_TUD_AUDIO_TX_SUPPORT_SW_FIFO_SIZE
static inline uint16_t tud_audio_flush_tx_support_ff (void)
{
return tud_audio_n_flush_tx_support_ff(0);
}
static inline uint16_t tud_audio_clear_tx_support_ff (uint8_t channelId)
{
return tud_audio_n_clear_tx_support_ff(0, channelId);
}
static inline uint16_t tud_audio_write_support_ff (uint8_t channelId, const void * data, uint16_t len)
{
return tud_audio_n_write_support_ff(0, channelId, data, len);
}
#endif
#endif
#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
static inline uint16_t tud_audio_int_ctr_available(void)
{
return tud_audio_int_ctr_n_available(0);
@ -370,25 +453,25 @@ static inline uint16_t tud_audio_int_ctr_read(void* buffer, uint16_t bufsize)
return tud_audio_int_ctr_n_read(0, buffer, bufsize);
}
static inline void tud_audio_int_ctr_read_flush(void)
static inline void tud_audio_int_ctr_clear(void)
{
return tud_audio_int_ctr_n_read_flush(0);
return tud_audio_int_ctr_n_clear(0);
}
static inline uint16_t tud_audio_int_ctr_write(uint8_t const* buffer, uint16_t bufsize)
static inline uint16_t tud_audio_int_ctr_write(uint8_t const* buffer, uint16_t len)
{
return tud_audio_int_ctr_n_write(0, buffer, bufsize);
return tud_audio_int_ctr_n_write(0, buffer, len);
}
#endif
//--------------------------------------------------------------------+
// Internal Class Driver API
//--------------------------------------------------------------------+
void audiod_init (void);
void audiod_reset (uint8_t rhport);
uint16_t audiod_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
bool audiod_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
bool audiod_xfer_cb (uint8_t rhport, uint8_t edpt_addr, xfer_result_t result, uint32_t xferred_bytes);
void audiod_init (void);
void audiod_reset (uint8_t rhport);
uint16_t audiod_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
bool audiod_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
bool audiod_xfer_cb (uint8_t rhport, uint8_t edpt_addr, xfer_result_t result, uint32_t xferred_bytes);
#ifdef __cplusplus
}

View File

@ -125,23 +125,23 @@ void dcd_disconnect(uint8_t rhport) TU_ATTR_WEAK;
void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const * request) TU_ATTR_WEAK;
// Configure endpoint's registers according to descriptor
bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc);
bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc);
// Close an endpoint.
// Since it is weak, caller must TU_ASSERT this function's existence before calling it.
void dcd_edpt_close (uint8_t rhport, uint8_t ep_addr) TU_ATTR_WEAK;
void dcd_edpt_close (uint8_t rhport, uint8_t ep_addr) TU_ATTR_WEAK;
// Submit a transfer, When complete dcd_event_xfer_complete() is invoked to notify the stack
bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes);
bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes);
// Submit an ISO transfer, When complete dcd_event_xfer_complete() is invoked to notify the stack
bool dcd_edpt_iso_xfer (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff);
bool dcd_edpt_iso_xfer (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes);
// Stall endpoint
void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr);
void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr);
// clear stall, data toggle is also reset to DATA0
void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr);
void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr);
//--------------------------------------------------------------------+
// Event API (implemented by stack)

View File

@ -1206,12 +1206,16 @@ bool usbd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
}
}
bool usbd_edpt_iso_xfer(uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff)
// The number of bytes has to be given explicitly to allow more flexible control of how many
// bytes should be written and second to keep the return value free to give back a boolean
// success message. If total_bytes is too big, the FIFO will copy only what is available
// into the USB buffer!
bool usbd_edpt_iso_xfer(uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes)
{
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
TU_LOG2(" Queue ISO EP %02X with %u bytes ... ", ep_addr, tu_fifo_count(ff));
TU_LOG2(" Queue ISO EP %02X with %u bytes ... ", ep_addr, count);
// Attempt to transfer on a busy endpoint, sound like an race condition !
TU_ASSERT(_usbd_dev.ep_status[epnum][dir].busy == 0);
@ -1220,7 +1224,7 @@ bool usbd_edpt_iso_xfer(uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff)
// and usbd task can preempt and clear the busy
_usbd_dev.ep_status[epnum][dir].busy = true;
if ( dcd_edpt_iso_xfer(rhport, ep_addr, ff) )
if (dcd_edpt_iso_xfer(rhport, ep_addr, ff, total_bytes))
{
TU_LOG2("OK\r\n");
return true;

View File

@ -73,7 +73,7 @@ void usbd_edpt_close(uint8_t rhport, uint8_t ep_addr);
bool usbd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes);
// Submit a usb ISO transfer by use of a FIFO (ring buffer) - all bytes in FIFO get transmitted
bool usbd_edpt_iso_xfer(uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff);
bool usbd_edpt_iso_xfer(uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes);
// Claim an endpoint before submitting a transfer.
// If caller does not make any transfer, it must release endpoint for others.

View File

@ -670,13 +670,15 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
return true;
}
bool dcd_edpt_iso_xfer (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff)
// The number of bytes has to be given explicitly to allow more flexible control of how many
// bytes should be written and second to keep the return value free to give back a boolean
// success message. If total_bytes is too big, the FIFO will copy only what is available
// into the USB buffer!
bool dcd_edpt_iso_xfer (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes)
{
// USB buffers always work in bytes so to avoid unnecessary divisions we demand item_size = 1
TU_ASSERT(ff->item_size == 1);
uint16_t total_bytes = tu_fifo_count(ff);
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);