summaryrefslogtreecommitdiff
path: root/src/class/audio/audio_device.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/class/audio/audio_device.c')
-rw-r--r--src/class/audio/audio_device.c842
1 files changed, 456 insertions, 386 deletions
diff --git a/src/class/audio/audio_device.c b/src/class/audio/audio_device.c
index 701411401..995bf8a3e 100644
--- a/src/class/audio/audio_device.c
+++ b/src/class/audio/audio_device.c
@@ -65,41 +65,6 @@
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
-// Use ring buffer if it's available, some MCUs need extra RAM requirements
-// For DWC2 enable ring buffer will disable DMA (if available)
-#ifndef TUD_AUDIO_PREFER_RING_BUFFER
- #if CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT1XXX || \
- defined(TUP_USBIP_DWC2)
- #define TUD_AUDIO_PREFER_RING_BUFFER 0
- #else
- #define TUD_AUDIO_PREFER_RING_BUFFER 1
- #endif
-#endif
-
-// Linear buffer in case target MCU is not capable of handling a ring buffer FIFO e.g. no hardware buffer
-// is available or driver is would need to be changed dramatically
-
-// Only STM32 and dcd_transdimension use non-linear buffer for now
-// dwc2 except esp32sx (since it may use dcd_esp32sx)
-// Ring buffer is incompatible with dcache, since neither address nor size is aligned to cache line
-#if (defined(TUP_USBIP_DWC2) && !TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3)) || \
- defined(TUP_USBIP_FSDEV) || \
- CFG_TUSB_MCU == OPT_MCU_RX63X || \
- CFG_TUSB_MCU == OPT_MCU_RX65X || \
- CFG_TUSB_MCU == OPT_MCU_RX72N || \
- CFG_TUSB_MCU == OPT_MCU_LPC18XX || \
- CFG_TUSB_MCU == OPT_MCU_LPC43XX || \
- CFG_TUSB_MCU == OPT_MCU_MIMXRT1XXX || \
- CFG_TUSB_MCU == OPT_MCU_MSP432E4
- #if TUD_AUDIO_PREFER_RING_BUFFER && !CFG_TUD_MEM_DCACHE_ENABLE
- #define USE_LINEAR_BUFFER 0
- #else
- #define USE_LINEAR_BUFFER 1
- #endif
-#else
- #define USE_LINEAR_BUFFER 1
-#endif
-
// Declaration of buffers
// Check for maximum supported numbers
@@ -108,12 +73,12 @@
#endif
// Put swap buffer in USB section only if necessary
-#if USE_LINEAR_BUFFER
+#if !CFG_TUD_EDPT_DEDICATED_HWFIFO
#define IN_SW_BUF_MEM_ATTR TU_ATTR_ALIGNED(4)
#else
#define IN_SW_BUF_MEM_ATTR CFG_TUD_MEM_SECTION CFG_TUD_MEM_ALIGN
#endif
-#if USE_LINEAR_BUFFER
+#if !CFG_TUD_EDPT_DEDICATED_HWFIFO
#define OUT_SW_BUF_MEM_ATTR TU_ATTR_ALIGNED(4)
#else
#define OUT_SW_BUF_MEM_ATTR CFG_TUD_MEM_SECTION CFG_TUD_MEM_ALIGN
@@ -136,7 +101,7 @@ tu_static IN_SW_BUF_MEM_ATTR struct {
// Linear buffer TX in case:
// - target MCU is not capable of handling a ring buffer FIFO e.g. no hardware buffer is available or driver is would need to be changed dramatically OR
-#if CFG_TUD_AUDIO_ENABLE_EP_IN && USE_LINEAR_BUFFER
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_EDPT_DEDICATED_HWFIFO
tu_static CFG_TUD_MEM_SECTION struct {
#if CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX > 0
TUD_EPBUF_DEF(buf_1, CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX);
@@ -148,7 +113,7 @@ tu_static CFG_TUD_MEM_SECTION struct {
TUD_EPBUF_DEF(buf_3, CFG_TUD_AUDIO_FUNC_3_EP_IN_SZ_MAX);
#endif
} lin_buf_in;
-#endif// CFG_TUD_AUDIO_ENABLE_EP_IN && USE_LINEAR_BUFFER
+#endif// CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_EDPT_DEDICATED_HWFIFO
// EP OUT software buffers
#if CFG_TUD_AUDIO_ENABLE_EP_OUT
@@ -167,7 +132,7 @@ tu_static OUT_SW_BUF_MEM_ATTR struct {
// Linear buffer RX in case:
// - target MCU is not capable of handling a ring buffer FIFO e.g. no hardware buffer is available or driver is would need to be changed dramatically OR
-#if CFG_TUD_AUDIO_ENABLE_EP_OUT && USE_LINEAR_BUFFER
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && !CFG_TUD_EDPT_DEDICATED_HWFIFO
tu_static CFG_TUD_MEM_SECTION struct {
#if CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX > 0
TUD_EPBUF_DEF(buf_1, CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX);
@@ -179,18 +144,12 @@ tu_static CFG_TUD_MEM_SECTION struct {
TUD_EPBUF_DEF(buf_3, CFG_TUD_AUDIO_FUNC_3_EP_OUT_SZ_MAX);
#endif
} lin_buf_out;
-#endif// CFG_TUD_AUDIO_ENABLE_EP_OUT && USE_LINEAR_BUFFER
+#endif// CFG_TUD_AUDIO_ENABLE_EP_OUT && !CFG_TUD_EDPT_DEDICATED_HWFIFO
-// Control buffers
-tu_static CFG_TUD_MEM_SECTION struct {
- TUD_EPBUF_DEF(buf1, CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ);
- #if CFG_TUD_AUDIO > 1
- TUD_EPBUF_DEF(buf2, CFG_TUD_AUDIO_FUNC_2_CTRL_BUF_SZ);
- #endif
- #if CFG_TUD_AUDIO > 2
- TUD_EPBUF_DEF(buf3, CFG_TUD_AUDIO_FUNC_3_CTRL_BUF_SZ);
- #endif
-} ctrl_buf;
+// Control buffer
+#if CFG_TUD_AUDIO_CTRL_BUF_SZ > CFG_TUD_ENDPOINT0_BUFSIZE
+tu_static CFG_TUD_MEM_ALIGN uint8_t ctrl_buf[CFG_TUD_AUDIO_CTRL_BUF_SZ];
+#endif
// Aligned buffer for feedback EP
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
@@ -218,12 +177,15 @@ 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
+ uint8_t const *p_desc_as;// Pointer pointing to 1st Standard AS Interface Descriptor(4.9.1) - Audio Streaming descriptor defining audio function
+ uint16_t desc_length;// Length of audio function descriptor
#if CFG_TUD_AUDIO_ENABLE_EP_IN
uint8_t ep_in; // TX audio data EP.
uint16_t ep_in_sz; // Current size of TX 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)
uint8_t ep_in_alt; // Current alternate setting of TX EP
+ uint16_t ep_in_fifo_threshold;// Target size for the EP IN FIFO.
#endif
#if CFG_TUD_AUDIO_ENABLE_EP_OUT
@@ -243,8 +205,6 @@ typedef struct
bool mounted;// Device opened
- uint16_t desc_length;// Length of audio function descriptor
-
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
struct {
uint32_t value; // Feedback value for asynchronous mode (in 16.16 format).
@@ -253,7 +213,6 @@ typedef struct
uint8_t frame_shift;// bInterval-1 in unit of frame (FS), micro-frame (HS)
uint8_t compute_method;
- bool format_correction;
union {
uint8_t power_of_2;// pre-computed power of 2 shift
float float_const; // pre-computed float constant
@@ -279,21 +238,13 @@ typedef struct
uint16_t packet_sz_tx[3];
uint8_t bclock_id_tx;
uint8_t interval_tx;
-#endif
-
-// Encoding parameters - parameters are set when alternate AS interface is set by host
-#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
- audio_format_type_t format_type_tx;
+ uint8_t format_type_tx;
uint8_t n_channels_tx;
uint8_t n_bytes_per_sample_tx;
#endif
/*------------- From this point, data is not cleared by bus reset -------------*/
- // Buffer for control requests
- uint8_t *ctrl_buf;
- uint8_t ctrl_buf_sz;
-
// EP Transfer buffers and FIFOs
#if CFG_TUD_AUDIO_ENABLE_EP_OUT
tu_fifo_t ep_out_ff;
@@ -304,14 +255,12 @@ typedef struct
#endif
// Linear buffer in case target MCU is not capable of handling a ring buffer FIFO e.g. no hardware buffer is available or driver is would need to be changed dramatically
-#if CFG_TUD_AUDIO_ENABLE_EP_OUT && USE_LINEAR_BUFFER
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && !CFG_TUD_EDPT_DEDICATED_HWFIFO
uint8_t *lin_buf_out;
- #define USE_LINEAR_BUFFER_RX 1
#endif
-#if CFG_TUD_AUDIO_ENABLE_EP_IN && USE_LINEAR_BUFFER
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_EDPT_DEDICATED_HWFIFO
uint8_t *lin_buf_in;
- #define USE_LINEAR_BUFFER_TX 1
#endif
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
@@ -319,16 +268,12 @@ typedef struct
#endif
} audiod_function_t;
-#ifndef USE_LINEAR_BUFFER_TX
- #define USE_LINEAR_BUFFER_TX 0
-#endif
-
-#ifndef USE_LINEAR_BUFFER_RX
- #define USE_LINEAR_BUFFER_RX 0
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT
+#define ITF_MEM_RESET_SIZE offsetof(audiod_function_t, ep_out_ff)
+#else
+#define ITF_MEM_RESET_SIZE offsetof(audiod_function_t, ep_in_ff)
#endif
-#define ITF_MEM_RESET_SIZE offsetof(audiod_function_t, ctrl_buf)
-
//--------------------------------------------------------------------+
// WEAK FUNCTION STUBS
//--------------------------------------------------------------------+
@@ -363,11 +308,6 @@ TU_ATTR_WEAK void tud_audio_feedback_params_cb(uint8_t func_id, uint8_t alt_itf,
feedback_param->method = AUDIO_FEEDBACK_METHOD_DISABLED;
}
-TU_ATTR_WEAK bool tud_audio_feedback_format_correction_cb(uint8_t func_id) {
- (void) func_id;
- return CFG_TUD_AUDIO_ENABLE_FEEDBACK_FORMAT_CORRECTION;
-}
-
TU_ATTR_WEAK TU_ATTR_FAST_FUNC void tud_audio_feedback_interval_isr(uint8_t func_id, uint32_t frame_number, uint8_t interval_shift) {
(void) func_id;
(void) frame_number;
@@ -376,7 +316,7 @@ TU_ATTR_WEAK TU_ATTR_FAST_FUNC void tud_audio_feedback_interval_isr(uint8_t func
#endif
#if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP
-TU_ATTR_WEAK void tud_audio_int_xfer_cb(uint8_t rhport) {
+TU_ATTR_WEAK void tud_audio_int_done_cb(uint8_t rhport) {
(void) rhport;
}
#endif
@@ -465,16 +405,16 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *p
static bool audiod_verify_entity_exists(uint8_t itf, uint8_t entityID, uint8_t *func_id);
static bool audiod_verify_itf_exists(uint8_t itf, uint8_t *func_id);
static bool audiod_verify_ep_exists(uint8_t ep, uint8_t *func_id);
-static uint8_t audiod_get_audio_fct_idx(audiod_function_t *audio);
+static inline uint8_t audiod_get_audio_fct_idx(audiod_function_t *audio);
#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
static void audiod_parse_flow_control_params(audiod_function_t *audio, uint8_t const *p_desc);
static bool audiod_calc_tx_packet_sz(audiod_function_t *audio);
-static uint16_t audiod_tx_packet_size(const uint16_t *norminal_size, uint16_t data_count, uint16_t fifo_depth, uint16_t max_size);
+static uint16_t audiod_tx_packet_size(const uint16_t *nominal_size, uint16_t data_count, uint16_t fifo_depth, uint16_t fifo_threshold, uint16_t max_size);
#endif
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
-static bool audiod_set_fb_params_freq(audiod_function_t *audio, uint32_t sample_freq, uint32_t mclk_freq);
+static bool audiod_fb_params_prepare(uint8_t func_id, uint8_t alt);
static void audiod_fb_fifo_count_update(audiod_function_t *audio, uint16_t lvl_new);
#endif
@@ -485,6 +425,15 @@ bool tud_audio_n_mounted(uint8_t func_id) {
return audio->mounted;
}
+static inline uint8_t* get_ctrl_buffer(void) {
+ // Use EP0 buffer if it is large enough, otherwise use dedicated buffer
+ #if CFG_TUD_AUDIO_CTRL_BUF_SZ > CFG_TUD_ENDPOINT0_BUFSIZE
+ return ctrl_buf;
+ #else
+ return usbd_get_ctrl_buf();
+ #endif
+}
+
//--------------------------------------------------------------------+
// READ API
//--------------------------------------------------------------------+
@@ -503,7 +452,8 @@ uint16_t tud_audio_n_read(uint8_t func_id, void *buffer, uint16_t bufsize) {
bool tud_audio_n_clear_ep_out_ff(uint8_t func_id) {
TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL);
- return tu_fifo_clear(&_audiod_fct[func_id].ep_out_ff);
+ tu_fifo_clear(&_audiod_fct[func_id].ep_out_ff);
+ return true;
}
tu_fifo_t *tud_audio_n_get_ep_out_ff(uint8_t func_id) {
@@ -516,15 +466,15 @@ tu_fifo_t *tud_audio_n_get_ep_out_ff(uint8_t func_id) {
static bool audiod_rx_xfer_isr(uint8_t rhport, audiod_function_t* audio, uint16_t n_bytes_received) {
uint8_t idx_audio_fct = audiod_get_audio_fct_idx(audio);
- #if USE_LINEAR_BUFFER_RX
+ #if !CFG_TUD_EDPT_DEDICATED_HWFIFO
// Data currently is in linear buffer, copy into EP OUT FIFO
- TU_VERIFY(tu_fifo_write_n(&audio->ep_out_ff, audio->lin_buf_out, n_bytes_received));
+ TU_VERIFY(0 < tu_fifo_write_n(&audio->ep_out_ff, audio->lin_buf_out, n_bytes_received));
// Schedule for next receive
- TU_VERIFY(usbd_edpt_xfer(rhport, audio->ep_out, audio->lin_buf_out, audio->ep_out_sz), false);
+ TU_VERIFY(usbd_edpt_xfer(rhport, audio->ep_out, audio->lin_buf_out, audio->ep_out_sz, true));
#else
// Data is already placed in EP FIFO, schedule for next receive
- TU_VERIFY(usbd_edpt_xfer_fifo(rhport, audio->ep_out, &audio->ep_out_ff, audio->ep_out_sz), false);
+ TU_VERIFY(usbd_edpt_xfer_fifo(rhport, audio->ep_out, &audio->ep_out_ff, audio->ep_out_sz, true));
#endif
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
@@ -554,7 +504,8 @@ uint16_t tud_audio_n_write(uint8_t func_id, const void *data, uint16_t len) {
bool tud_audio_n_clear_ep_in_ff(uint8_t func_id) {
TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL);
- return tu_fifo_clear(&_audiod_fct[func_id].ep_in_ff);
+ tu_fifo_clear(&_audiod_fct[func_id].ep_in_ff);
+ return true;
}
tu_fifo_t *tud_audio_n_get_ep_in_ff(uint8_t func_id) {
@@ -564,6 +515,17 @@ tu_fifo_t *tud_audio_n_get_ep_in_ff(uint8_t func_id) {
return NULL;
}
+uint16_t tud_audio_n_get_ep_in_fifo_threshold(uint8_t func_id) {
+ if (func_id < CFG_TUD_AUDIO) return _audiod_fct[func_id].ep_in_fifo_threshold;
+ return 0;
+}
+
+void tud_audio_n_set_ep_in_fifo_threshold(uint8_t func_id, uint16_t threshold) {
+ if (func_id < CFG_TUD_AUDIO && threshold < _audiod_fct[func_id].ep_in_ff.depth) {
+ _audiod_fct[func_id].ep_in_fifo_threshold = threshold;
+ }
+}
+
static bool audiod_tx_xfer_isr(uint8_t rhport, audiod_function_t * audio, uint16_t n_bytes_sent) {
uint8_t idx_audio_fct = audiod_get_audio_fct_idx(audio);
@@ -575,16 +537,16 @@ static bool audiod_tx_xfer_isr(uint8_t rhport, audiod_function_t * audio, uint16
#if CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
// packet_sz_tx is based on total packet size, here we want size for each support buffer.
- n_bytes_tx = audiod_tx_packet_size(audio->packet_sz_tx, tu_fifo_count(&audio->ep_in_ff), audio->ep_in_ff.depth, audio->ep_in_sz);
+ n_bytes_tx = audiod_tx_packet_size(audio->packet_sz_tx, tu_fifo_count(&audio->ep_in_ff), audio->ep_in_ff.depth, audio->ep_in_fifo_threshold, audio->ep_in_sz);
#else
n_bytes_tx = tu_min16(tu_fifo_count(&audio->ep_in_ff), audio->ep_in_sz);// Limit up to max packet size, more can not be done for ISO
#endif
- #if USE_LINEAR_BUFFER_TX
+ #if !CFG_TUD_EDPT_DEDICATED_HWFIFO
tu_fifo_read_n(&audio->ep_in_ff, audio->lin_buf_in, n_bytes_tx);
- TU_VERIFY(usbd_edpt_xfer(rhport, audio->ep_in, audio->lin_buf_in, n_bytes_tx));
+ TU_VERIFY(usbd_edpt_xfer(rhport, audio->ep_in, audio->lin_buf_in, n_bytes_tx, true));
#else
// Send everything in ISO EP FIFO
- TU_VERIFY(usbd_edpt_xfer_fifo(rhport, audio->ep_in, &audio->ep_in_ff, n_bytes_tx));
+ TU_VERIFY(usbd_edpt_xfer_fifo(rhport, audio->ep_in, &audio->ep_in_ff, n_bytes_tx, true));
#endif
// 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
@@ -595,6 +557,10 @@ static bool audiod_tx_xfer_isr(uint8_t rhport, audiod_function_t * audio, uint16
#endif
+//--------------------------------------------------------------------+
+// OTHER API
+//--------------------------------------------------------------------+
+
#if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP
// If no interrupt transmit is pending bytes get written into buffer and a transmit is scheduled - once transmit completed tud_audio_int_done_cb() is called in inform user
bool tud_audio_int_n_write(uint8_t func_id, const audio_interrupt_data_t *data) {
@@ -605,10 +571,15 @@ bool tud_audio_int_n_write(uint8_t func_id, const audio_interrupt_data_t *data)
// We write directly into the EP's buffer - abort if previous transfer not complete
TU_VERIFY(usbd_edpt_claim(_audiod_fct[func_id].rhport, _audiod_fct[func_id].ep_int));
+ uint8_t size = tud_audio_n_version(func_id) == 2 ? sizeof(audio20_interrupt_data_t) : sizeof(audio10_interrupt_data_t);
+
+ // INT EP buffer must be large enough
+ TU_ASSERT(size <= sizeof(int_ep_buf[func_id].buf));
+
// Check length
- if (tu_memcpy_s(int_ep_buf[func_id].buf, sizeof(int_ep_buf[func_id].buf), data, sizeof(audio_interrupt_data_t)) == 0) {
+ if (tu_memcpy_s(int_ep_buf[func_id].buf, sizeof(int_ep_buf[func_id].buf), data, size) == 0) {
// Schedule transmit
- TU_ASSERT(usbd_edpt_xfer(_audiod_fct[func_id].rhport, _audiod_fct[func_id].ep_int, int_ep_buf[func_id].buf, sizeof(int_ep_buf[func_id].buf)), 0);
+ TU_ASSERT(usbd_edpt_xfer(_audiod_fct[func_id].rhport, _audiod_fct[func_id].ep_int, int_ep_buf[func_id].buf, size, false));
} else {
// Release endpoint since we don't make any transfer
usbd_edpt_release(_audiod_fct[func_id].rhport, _audiod_fct[func_id].ep_int);
@@ -620,10 +591,11 @@ bool tud_audio_int_n_write(uint8_t func_id, const audio_interrupt_data_t *data)
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
// This function is called once a transmit of a feedback packet was successfully completed. Here, we get the next feedback value to be sent
-static inline bool audiod_fb_send(audiod_function_t *audio) {
- bool apply_correction = (TUSB_SPEED_FULL == tud_speed_get()) && audio->feedback.format_correction;
+static inline bool audiod_fb_send(uint8_t func_id, bool is_isr) {
+ audiod_function_t *audio = &_audiod_fct[func_id];
+ uint8_t uac_version = tud_audio_n_version(func_id);
// Format the feedback value
- if (apply_correction) {
+ if (uac_version == 1) {
uint8_t *fb = (uint8_t *) audio->fb_buf;
// For FS format is 10.14
@@ -635,23 +607,70 @@ static inline bool audiod_fb_send(audiod_function_t *audio) {
*audio->fb_buf = audio->feedback.value;
}
- // About feedback format on FS
- //
- // 3 variables: Format | packetSize | sendSize | Working OS:
- // 16.16 4 4 Linux, Windows
- // 16.16 4 3 Linux
- // 16.16 3 4 Linux
- // 16.16 3 3 Linux
- // 10.14 4 4 Linux
- // 10.14 4 3 Linux
- // 10.14 3 4 Linux, OSX
- // 10.14 3 3 Linux, OSX
- //
- // We send 3 bytes since sending packet larger than wMaxPacketSize is pretty ugly
- return usbd_edpt_xfer(audio->rhport, audio->ep_fb, (uint8_t *) audio->fb_buf, apply_correction ? 3 : 4);
+ return usbd_edpt_xfer(audio->rhport, audio->ep_fb, (uint8_t *) audio->fb_buf, uac_version == 1 ? 3 : 4, is_isr);
+}
+
+uint32_t tud_audio_feedback_update(uint8_t func_id, uint32_t cycles) {
+ audiod_function_t *audio = &_audiod_fct[func_id];
+ uint32_t feedback;
+
+ switch (audio->feedback.compute_method) {
+ case AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2:
+ feedback = (cycles << audio->feedback.compute.power_of_2);
+ break;
+
+ case AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT:
+ feedback = (uint32_t) ((float) cycles * audio->feedback.compute.float_const);
+ break;
+
+ case AUDIO_FEEDBACK_METHOD_FREQUENCY_FIXED: {
+ uint64_t fb64 = (((uint64_t) cycles) * audio->feedback.compute.fixed.sample_freq) << (16 - (audio->feedback.frame_shift - 1));
+ feedback = (uint32_t) (fb64 / audio->feedback.compute.fixed.mclk_freq);
+ } break;
+
+ default:
+ return 0;
+ }
+
+ // For Windows: https://docs.microsoft.com/en-us/windows-hardware/drivers/audio/usb-2-0-audio-drivers
+ // The size of isochronous packets created by the device must be within the limits specified in FMT-2.0 section 2.3.1.1.
+ // This means that the deviation of actual packet size from nominal size must not exceed +/- one audio slot
+ // (audio slot = channel count samples).
+ if (feedback > audio->feedback.max_value) {
+ feedback = audio->feedback.max_value;
+ }
+ if (feedback < audio->feedback.min_value) {
+ feedback = audio->feedback.min_value;
+ }
+
+ tud_audio_n_fb_set(func_id, feedback);
+
+ return feedback;
+}
+
+bool tud_audio_n_fb_set(uint8_t func_id, uint32_t feedback) {
+ TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL);
+
+ _audiod_fct[func_id].feedback.value = feedback;
+
+ return true;
}
#endif
+uint8_t tud_audio_n_version(uint8_t func_id) {
+ TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL);
+
+ uint8_t bIntfProtocol = ((tusb_desc_interface_t const *)_audiod_fct[func_id].p_desc)->bInterfaceProtocol;
+
+ if (bIntfProtocol == AUDIO_INT_PROTOCOL_CODE_V1) {
+ return 1;
+ } else if (bIntfProtocol == AUDIO_INT_PROTOCOL_CODE_V2) {
+ return 2;
+ } else {
+ return 0; // Unknown version
+ }
+}
+
//--------------------------------------------------------------------+
// USBD Driver API
//--------------------------------------------------------------------+
@@ -661,111 +680,89 @@ void audiod_init(void) {
for (uint8_t i = 0; i < CFG_TUD_AUDIO; i++) {
audiod_function_t *audio = &_audiod_fct[i];
- // Initialize control buffers
- switch (i) {
- case 0:
- audio->ctrl_buf = ctrl_buf.buf1;
- audio->ctrl_buf_sz = CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ;
- break;
-#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_CTRL_BUF_SZ > 0
- case 1:
- audio->ctrl_buf = ctrl_buf.buf2;
- audio->ctrl_buf_sz = CFG_TUD_AUDIO_FUNC_2_CTRL_BUF_SZ;
- break;
-#endif
-#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_CTRL_BUF_SZ > 0
- case 2:
- audio->ctrl_buf = ctrl_buf.buf3;
- audio->ctrl_buf_sz = CFG_TUD_AUDIO_FUNC_3_CTRL_BUF_SZ;
- break;
-#endif
- }
-
// Initialize IN EP FIFO if required
#if CFG_TUD_AUDIO_ENABLE_EP_IN
-
switch (i) {
#if CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ > 0
case 0:
- tu_fifo_config(&audio->ep_in_ff, ep_in_sw_buf.buf_1, CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ, 1, true);
+ tu_fifo_config(&audio->ep_in_ff, ep_in_sw_buf.buf_1, CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ, true);
break;
#endif
#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_IN_SW_BUF_SZ > 0
case 1:
- tu_fifo_config(&audio->ep_in_ff, ep_in_sw_buf.buf_2, CFG_TUD_AUDIO_FUNC_2_EP_IN_SW_BUF_SZ, 1, true);
+ tu_fifo_config(&audio->ep_in_ff, ep_in_sw_buf.buf_2, CFG_TUD_AUDIO_FUNC_2_EP_IN_SW_BUF_SZ, true);
break;
#endif
#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_IN_SW_BUF_SZ > 0
case 2:
- tu_fifo_config(&audio->ep_in_ff, ep_in_sw_buf.buf_3, CFG_TUD_AUDIO_FUNC_3_EP_IN_SW_BUF_SZ, 1, true);
+ tu_fifo_config(&audio->ep_in_ff, ep_in_sw_buf.buf_3, CFG_TUD_AUDIO_FUNC_3_EP_IN_SW_BUF_SZ, true);
break;
#endif
}
-#endif// CFG_TUD_AUDIO_ENABLE_EP_IN
- // Initialize linear buffers
-#if USE_LINEAR_BUFFER_TX
+ // Initialize linear buffers
+ #if !CFG_TUD_EDPT_DEDICATED_HWFIFO
switch (i) {
- #if CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX > 0
+ #if CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX > 0
case 0:
audio->lin_buf_in = lin_buf_in.buf_1;
break;
- #endif
- #if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_IN_SZ_MAX > 0
+ #endif
+ #if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_IN_SZ_MAX > 0
case 1:
audio->lin_buf_in = lin_buf_in.buf_2;
break;
- #endif
- #if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_IN_SZ_MAX > 0
+ #endif
+ #if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_IN_SZ_MAX > 0
case 2:
audio->lin_buf_in = lin_buf_in.buf_3;
break;
- #endif
+ #endif
}
-#endif// USE_LINEAR_BUFFER_TX
+ #endif// !CFG_TUD_EDPT_DEDICATED_HWFIFO
+#endif// CFG_TUD_AUDIO_ENABLE_EP_IN
// Initialize OUT EP FIFO if required
#if CFG_TUD_AUDIO_ENABLE_EP_OUT
-
switch (i) {
#if CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ > 0
case 0:
- tu_fifo_config(&audio->ep_out_ff, ep_out_sw_buf.buf_1, CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ, 1, true);
+ tu_fifo_config(&audio->ep_out_ff, ep_out_sw_buf.buf_1, CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ, true);
break;
#endif
#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_OUT_SW_BUF_SZ > 0
case 1:
- tu_fifo_config(&audio->ep_out_ff, ep_out_sw_buf.buf_2, CFG_TUD_AUDIO_FUNC_2_EP_OUT_SW_BUF_SZ, 1, true);
+ tu_fifo_config(&audio->ep_out_ff, ep_out_sw_buf.buf_2, CFG_TUD_AUDIO_FUNC_2_EP_OUT_SW_BUF_SZ, true);
break;
#endif
#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_OUT_SW_BUF_SZ > 0
case 2:
- tu_fifo_config(&audio->ep_out_ff, ep_out_sw_buf.buf_3, CFG_TUD_AUDIO_FUNC_3_EP_OUT_SW_BUF_SZ, 1, true);
+ tu_fifo_config(&audio->ep_out_ff, ep_out_sw_buf.buf_3, CFG_TUD_AUDIO_FUNC_3_EP_OUT_SW_BUF_SZ, true);
break;
#endif
}
-#endif// CFG_TUD_AUDIO_ENABLE_EP_OUT
- // Initialize linear buffers
-#if USE_LINEAR_BUFFER_RX
+ #if !CFG_TUD_EDPT_DEDICATED_HWFIFO
+ // Initialize linear buffers
switch (i) {
- #if CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX > 0
+ #if CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX > 0
case 0:
audio->lin_buf_out = lin_buf_out.buf_1;
break;
- #endif
- #if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_OUT_SZ_MAX > 0
+ #endif
+ #if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_OUT_SZ_MAX > 0
case 1:
audio->lin_buf_out = lin_buf_out.buf_2;
break;
- #endif
- #if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_OUT_SZ_MAX > 0
+ #endif
+ #if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_OUT_SZ_MAX > 0
case 2:
audio->lin_buf_out = lin_buf_out.buf_3;
break;
- #endif
+ #endif
}
-#endif// USE_LINEAR_BUFFER_RX
+ #endif// !CFG_TUD_EDPT_DEDICATED_HWFIFO
+#endif// CFG_TUD_AUDIO_ENABLE_EP_OUT
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
switch (i) {
@@ -814,19 +811,44 @@ uint16_t audiod_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint
(void) max_len;
TU_VERIFY(TUSB_CLASS_AUDIO == itf_desc->bInterfaceClass &&
- AUDIO_SUBCLASS_CONTROL == itf_desc->bInterfaceSubClass);
+ AUDIO_SUBCLASS_CONTROL == itf_desc->bInterfaceSubClass, 0);
// Verify version is correct - this check can be omitted
- TU_VERIFY(itf_desc->bInterfaceProtocol == AUDIO_INT_PROTOCOL_CODE_V2);
+ TU_VERIFY(itf_desc->bInterfaceProtocol == AUDIO_INT_PROTOCOL_CODE_V1 ||
+ itf_desc->bInterfaceProtocol == AUDIO_INT_PROTOCOL_CODE_V2, 0);
+
+ // Verify 2nd interface descriptor is Audio Streaming to avoid mess with MIDI class
+ // Audio Control interface is followed by Audio Streaming interface(s)
+ // MIDI class also starts with Audio Control but is followed by MIDI Streaming
+ {
+ uint8_t const *p_desc = (uint8_t const *) itf_desc;
+ uint8_t const *p_desc_end = p_desc + max_len;
+
+ // Advance to next interface descriptor
+ p_desc = tu_desc_next(p_desc);
+ while (tu_desc_in_bounds(p_desc, p_desc_end) && tu_desc_type(p_desc) != TUSB_DESC_INTERFACE) {
+ p_desc = tu_desc_next(p_desc);
+ }
+
+ // Verify next interface is Audio Streaming (subclass 2), not MIDI Streaming (subclass 3)
+ if (p_desc_end - p_desc >= (int)sizeof(tusb_desc_interface_t)) {
+ tusb_desc_interface_t const *next_itf = (tusb_desc_interface_t const *) p_desc;
+ TU_VERIFY(next_itf->bInterfaceClass == TUSB_CLASS_AUDIO &&
+ next_itf->bInterfaceSubClass == AUDIO_SUBCLASS_STREAMING, 0);
+ } else {
+ // No further interface found or not enough bytes for interface descriptor
+ return 0;
+ }
+ }
// Verify interrupt control EP is enabled if demanded by descriptor
- TU_ASSERT(itf_desc->bNumEndpoints <= 1);// 0 or 1 EPs are allowed
+ TU_ASSERT(itf_desc->bNumEndpoints <= 1, 0);// 0 or 1 EPs are allowed
if (itf_desc->bNumEndpoints == 1) {
- TU_ASSERT(CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP);
+ TU_ASSERT(CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP, 0);
}
// Alternate setting MUST be zero - this check can be omitted
- TU_VERIFY(itf_desc->bAlternateSetting == 0);
+ TU_VERIFY(itf_desc->bAlternateSetting == 0, 0);
// Find available audio driver interface
uint8_t i;
@@ -835,21 +857,32 @@ uint16_t audiod_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint
_audiod_fct[i].p_desc = (uint8_t const *) itf_desc;// Save pointer to AC descriptor which is by specification always the first one
_audiod_fct[i].rhport = rhport;
- // Setup descriptor lengths
- switch (i) {
- case 0:
- _audiod_fct[i].desc_length = CFG_TUD_AUDIO_FUNC_1_DESC_LEN;
- break;
-#if CFG_TUD_AUDIO > 1
- case 1:
- _audiod_fct[i].desc_length = CFG_TUD_AUDIO_FUNC_2_DESC_LEN;
- break;
-#endif
-#if CFG_TUD_AUDIO > 2
- case 2:
- _audiod_fct[i].desc_length = CFG_TUD_AUDIO_FUNC_3_DESC_LEN;
- break;
-#endif
+ // Calculate descriptor length
+ {
+ uint8_t const *p_desc = (uint8_t const *) itf_desc;
+ uint8_t const *p_desc_end = p_desc + max_len;
+ uint16_t total_len = sizeof(tusb_desc_interface_t);
+ // Skip Standard AC interface descriptor
+ p_desc = tu_desc_next(p_desc);
+ while (p_desc_end - p_desc > 0) {
+ // Stop if:
+ // - Non audio streaming interface descriptor found
+ // - IAD found
+ if ((tu_desc_type(p_desc) == TUSB_DESC_INTERFACE &&
+ !(((tusb_desc_interface_t const *) p_desc)->bInterfaceClass == TUSB_CLASS_AUDIO && ((tusb_desc_interface_t const *) p_desc)->bInterfaceSubClass == AUDIO_SUBCLASS_STREAMING))
+ || tu_desc_type(p_desc) == TUSB_DESC_INTERFACE_ASSOCIATION) {
+ break;
+ } else if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE && ((tusb_desc_interface_t const *) p_desc)->bInterfaceSubClass == AUDIO_SUBCLASS_STREAMING) {
+ if (_audiod_fct[i].p_desc_as == NULL) {
+ _audiod_fct[i].p_desc_as = p_desc;
+ }
+ } else {
+ // nothing to do
+ }
+ total_len += p_desc[0];
+ p_desc = tu_desc_next(p_desc);
+ }
+ _audiod_fct[i].desc_length = total_len;
}
#ifdef TUP_DCD_EDPT_ISO_ALLOC
@@ -868,54 +901,67 @@ uint16_t audiod_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint
uint8_t ep_fb = 0;
#endif
uint8_t const *p_desc = _audiod_fct[i].p_desc;
- uint8_t const *p_desc_end = p_desc + _audiod_fct[i].desc_length - TUD_AUDIO_DESC_IAD_LEN;
+ uint8_t const *p_desc_end = p_desc + _audiod_fct[i].desc_length;
// Condition modified from p_desc < p_desc_end to prevent gcc>=12 strict-overflow warning
while (p_desc_end - p_desc > 0) {
if (tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT) {
+ // Unified UAC1/UAC2 endpoint processing
tusb_desc_endpoint_t const *desc_ep = (tusb_desc_endpoint_t const *) p_desc;
- if (desc_ep->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS) {
- #if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
- // Explicit feedback EP
- if (desc_ep->bmAttributes.usage == 1) {
- ep_fb = desc_ep->bEndpointAddress;
- }
- #endif
- #if CFG_TUD_AUDIO_ENABLE_EP_IN
- // Data or data with implicit feedback IN EP
- if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN
- && (desc_ep->bmAttributes.usage == 0 || desc_ep->bmAttributes.usage == 2)) {
- ep_in = desc_ep->bEndpointAddress;
- ep_in_size = TU_MAX(tu_edpt_packet_size(desc_ep), ep_in_size);
- }
- #endif
- #if CFG_TUD_AUDIO_ENABLE_EP_OUT
- // Data OUT EP
- if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_OUT
- && desc_ep->bmAttributes.usage == 0) {
- ep_out = desc_ep->bEndpointAddress;
- ep_out_size = TU_MAX(tu_edpt_packet_size(desc_ep), ep_out_size);
- }
- #endif
+ bool is_feedback_ep = false;
+ bool is_data_ep = false;
+
+ if (tud_audio_n_version(i) == 1) {
+ // UAC1: Use bRefresh field to distinguish endpoint types
+ audio10_desc_as_iso_data_ep_t const *desc_ep_uac1 = (audio10_desc_as_iso_data_ep_t const *) p_desc;
+ is_data_ep = (desc_ep_uac1->bmAttributes.sync != TUSB_ISO_EP_ATT_NO_SYNC);
+ is_feedback_ep = (desc_ep_uac1->bmAttributes.sync == TUSB_ISO_EP_ATT_NO_SYNC);
+ } else {
+ // UAC2: Use bmAttributes.usage to distinguish endpoint types
+ is_data_ep = (desc_ep->bmAttributes.usage == 0 || desc_ep->bmAttributes.usage == 2);
+ is_feedback_ep = (desc_ep->bmAttributes.usage == 1);
}
+
+ #if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+ // Explicit feedback EP
+ if (is_feedback_ep) {
+ ep_fb = desc_ep->bEndpointAddress;
+ }
+ #else
+ (void) is_feedback_ep;
+ #endif
+ #if CFG_TUD_AUDIO_ENABLE_EP_IN
+ // Data or data with implicit feedback IN EP
+ if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN && is_data_ep) {
+ ep_in = desc_ep->bEndpointAddress;
+ ep_in_size = TU_MAX(tu_edpt_packet_size(desc_ep), ep_in_size);
+ }
+ #endif
+ #if CFG_TUD_AUDIO_ENABLE_EP_OUT
+ // Data OUT EP
+ if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_OUT && is_data_ep) {
+ ep_out = desc_ep->bEndpointAddress;
+ ep_out_size = TU_MAX(tu_edpt_packet_size(desc_ep), ep_out_size);
+ }
+ #endif
}
p_desc = tu_desc_next(p_desc);
}
#if CFG_TUD_AUDIO_ENABLE_EP_IN
- if (ep_in) {
+ if (ep_in != 0) {
usbd_edpt_iso_alloc(rhport, ep_in, ep_in_size);
}
#endif
#if CFG_TUD_AUDIO_ENABLE_EP_OUT
- if (ep_out) {
+ if (ep_out != 0) {
usbd_edpt_iso_alloc(rhport, ep_out, ep_out_size);
}
#endif
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
- if (ep_fb) {
+ if (ep_fb != 0) {
usbd_edpt_iso_alloc(rhport, ep_fb, 4);
}
#endif
@@ -925,22 +971,27 @@ uint16_t audiod_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint
#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
{
uint8_t const *p_desc = _audiod_fct[i].p_desc;
- uint8_t const *p_desc_end = p_desc + _audiod_fct[i].desc_length - TUD_AUDIO_DESC_IAD_LEN;
+ uint8_t const *p_desc_end = p_desc + _audiod_fct[i].desc_length;
// Condition modified from p_desc < p_desc_end to prevent gcc>=12 strict-overflow warning
while (p_desc_end - p_desc > 0) {
if (tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT) {
tusb_desc_endpoint_t const *desc_ep = (tusb_desc_endpoint_t const *) p_desc;
if (desc_ep->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS) {
// For data or data with implicit feedback IN EP
+ // For UAC1 this is always the case since there is no usage field
if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN
&& (desc_ep->bmAttributes.usage == 0 || desc_ep->bmAttributes.usage == 2)) {
_audiod_fct[i].interval_tx = desc_ep->bInterval;
}
}
- } else if (tu_desc_type(p_desc) == TUSB_DESC_CS_INTERFACE && tu_desc_subtype(p_desc) == AUDIO_CS_AC_INTERFACE_OUTPUT_TERMINAL) {
+ } else if (tud_audio_n_version(i) == 2 &&
+ tu_desc_type(p_desc) == TUSB_DESC_CS_INTERFACE && tu_desc_subtype(p_desc) == AUDIO20_CS_AC_INTERFACE_OUTPUT_TERMINAL) {
+ // For UAC2 only, UAC1 doesn't have a clock source
if (tu_unaligned_read16(p_desc + 4) == AUDIO_TERM_TYPE_USB_STREAMING) {
_audiod_fct[i].bclock_id_tx = p_desc[8];
}
+ } else {
+ // nothing to do
}
p_desc = tu_desc_next(p_desc);
}
@@ -950,7 +1001,7 @@ uint16_t audiod_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint
#if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP
{
uint8_t const *p_desc = _audiod_fct[i].p_desc;
- uint8_t const *p_desc_end = p_desc + _audiod_fct[i].desc_length - TUD_AUDIO_DESC_IAD_LEN;
+ uint8_t const *p_desc_end = p_desc + _audiod_fct[i].desc_length;
// Condition modified from p_desc < p_desc_end to prevent gcc>=12 strict-overflow warning
while (p_desc_end - p_desc > 0) {
// For each endpoint
@@ -978,7 +1029,7 @@ uint16_t audiod_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint
TU_ASSERT(i < CFG_TUD_AUDIO);
// This is all we need so far - the EPs are setup by a later set_interface request (as per UAC2 specification)
- uint16_t drv_len = _audiod_fct[i].desc_length - TUD_AUDIO_DESC_IAD_LEN;// - TUD_AUDIO_DESC_IAD_LEN since tinyUSB already handles the IAD descriptor
+ uint16_t drv_len = _audiod_fct[i].desc_length;
return drv_len;
}
@@ -1088,11 +1139,10 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *p
#endif// CFG_TUD_AUDIO_ENABLE_EP_OUT
// Open new EP if necessary - EPs are only to be closed or opened for AS interfaces - Look for AS interface with correct alternate interface
- uint8_t const *p_desc = tu_desc_next(audio->p_desc);
- // Skip entire AC descriptor block
- p_desc += ((audio_desc_cs_ac_interface_t const *) p_desc)->wTotalLength;
+
+ uint8_t const *p_desc = audio->p_desc_as;
// Get pointer at end
- uint8_t const *p_desc_end = audio->p_desc + audio->desc_length - TUD_AUDIO_DESC_IAD_LEN;
+ uint8_t const *p_desc_end = audio->p_desc + audio->desc_length;
// p_desc starts at required interface with alternate setting zero
// Condition modified from p_desc < p_desc_end to prevent gcc>=12 strict-overflow warning
@@ -1115,36 +1165,52 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *p
#endif
uint8_t const ep_addr = desc_ep->bEndpointAddress;
+ bool is_feedback_ep = false;
+ bool is_data_ep = false;
+
+ if (tud_audio_n_version(func_id) == 1) {
+ // UAC1: Use bRefresh field to distinguish endpoint types
+ audio10_desc_as_iso_data_ep_t const *desc_ep_uac1 = (audio10_desc_as_iso_data_ep_t const *) p_desc;
+ is_data_ep = (desc_ep_uac1->bmAttributes.sync != TUSB_ISO_EP_ATT_NO_SYNC);
+ is_feedback_ep = (desc_ep_uac1->bmAttributes.sync == TUSB_ISO_EP_ATT_NO_SYNC);
+ } else {
+ // UAC2: Use bmAttributes.usage to distinguish endpoint types
+ is_data_ep = (desc_ep->bmAttributes.usage == 0 || desc_ep->bmAttributes.usage == 2);
+ is_feedback_ep = (desc_ep->bmAttributes.usage == 1);
+ }
+
//TODO: 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_ENABLE_EP_IN
// For data or data with implicit feedback IN EP
- if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN && (desc_ep->bmAttributes.usage == 0 || desc_ep->bmAttributes.usage == 2))
+ if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN && is_data_ep)
{
// Save address
audio->ep_in = ep_addr;
audio->ep_in_as_intf_num = itf;
audio->ep_in_alt = alt;
audio->ep_in_sz = tu_edpt_packet_size(desc_ep);
+ // Set the default EP IN FIFO threshold to half fifo depth.
+ audio->ep_in_fifo_threshold = audio->ep_in_ff.depth / 2;
// If flow control is enabled, parse for the corresponding parameters - doing this here means only AS interfaces with EPs get scanned for parameters
#if CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
audiod_parse_flow_control_params(audio, p_desc_parse_for_params);
#endif
// Schedule first transmit if alternate interface is not zero, as sample data is available a ZLP is loaded
- #if USE_LINEAR_BUFFER_TX
- TU_VERIFY(usbd_edpt_xfer(rhport, audio->ep_in, audio->lin_buf_in, 0));
+ #if !CFG_TUD_EDPT_DEDICATED_HWFIFO
+ TU_VERIFY(usbd_edpt_xfer(rhport, audio->ep_in, audio->lin_buf_in, 0, false));
#else
// Send everything in ISO EP FIFO
- TU_VERIFY(usbd_edpt_xfer_fifo(rhport, audio->ep_in, &audio->ep_in_ff, 0));
+ TU_VERIFY(usbd_edpt_xfer_fifo(rhport, audio->ep_in, &audio->ep_in_ff, 0, false));
#endif
}
#endif// CFG_TUD_AUDIO_ENABLE_EP_IN
#if CFG_TUD_AUDIO_ENABLE_EP_OUT
// Checking usage not necessary
- if (tu_edpt_dir(ep_addr) == TUSB_DIR_OUT) {
+ if (tu_edpt_dir(ep_addr) == TUSB_DIR_OUT && is_data_ep) {
// Save address
audio->ep_out = ep_addr;
audio->ep_out_as_intf_num = itf;
@@ -1152,22 +1218,26 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *p
audio->ep_out_sz = tu_edpt_packet_size(desc_ep);
// Prepare for incoming data
- #if USE_LINEAR_BUFFER_RX
- TU_VERIFY(usbd_edpt_xfer(rhport, audio->ep_out, audio->lin_buf_out, audio->ep_out_sz), false);
+ #if !CFG_TUD_EDPT_DEDICATED_HWFIFO
+ TU_VERIFY(usbd_edpt_xfer(rhport, audio->ep_out, audio->lin_buf_out, audio->ep_out_sz, false));
#else
- TU_VERIFY(usbd_edpt_xfer_fifo(rhport, audio->ep_out, &audio->ep_out_ff, audio->ep_out_sz), false);
+ TU_VERIFY(usbd_edpt_xfer_fifo(rhport, audio->ep_out, &audio->ep_out_ff, audio->ep_out_sz, false));
#endif
}
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
// Check if usage is explicit data feedback
- if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN && desc_ep->bmAttributes.usage == 1) {
+ if (is_feedback_ep) {
audio->ep_fb = ep_addr;
audio->feedback.frame_shift = desc_ep->bInterval - 1;
// Schedule first feedback transmit
- audiod_fb_send(audio);
+ audiod_fb_send(func_id, false);
}
+ #else
+ (void) is_feedback_ep;
#endif
+#else
+ (void) is_feedback_ep;
#endif// CFG_TUD_AUDIO_ENABLE_EP_OUT
foundEPs += 1;
@@ -1181,50 +1251,8 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *p
TU_VERIFY(tud_audio_set_itf_cb(rhport, p_request));
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
- // Prepare feedback computation if endpoint is available
- if (audio->ep_fb != 0) {
- audio_feedback_params_t fb_param;
-
- tud_audio_feedback_params_cb(func_id, alt, &fb_param);
- audio->feedback.compute_method = fb_param.method;
-
- if (TUSB_SPEED_FULL == tud_speed_get())
- audio->feedback.format_correction = tud_audio_feedback_format_correction_cb(func_id);
-
- // Minimal/Maximum value in 16.16 format for full speed (1ms per frame) or high speed (125 us per frame)
- uint32_t const frame_div = (TUSB_SPEED_FULL == tud_speed_get()) ? 1000 : 8000;
- audio->feedback.min_value = ((fb_param.sample_freq - 1) / frame_div) << 16;
- audio->feedback.max_value = (fb_param.sample_freq / frame_div + 1) << 16;
-
- switch (fb_param.method) {
- case AUDIO_FEEDBACK_METHOD_FREQUENCY_FIXED:
- case AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT:
- case AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2:
- audiod_set_fb_params_freq(audio, fb_param.sample_freq, fb_param.frequency.mclk_freq);
- break;
-
- case AUDIO_FEEDBACK_METHOD_FIFO_COUNT: {
- // Initialize the threshold level to half filled
- uint16_t fifo_lvl_thr = tu_fifo_depth(&audio->ep_out_ff) / 2;
- audio->feedback.compute.fifo_count.fifo_lvl_thr = fifo_lvl_thr;
- audio->feedback.compute.fifo_count.fifo_lvl_avg = ((uint32_t) fifo_lvl_thr) << 16;
- // Avoid 64bit division
- uint32_t nominal = ((fb_param.sample_freq / 100) << 16) / (frame_div / 100);
- audio->feedback.compute.fifo_count.nom_value = nominal;
- audio->feedback.compute.fifo_count.rate_const[0] = (uint16_t) ((audio->feedback.max_value - nominal) / fifo_lvl_thr);
- audio->feedback.compute.fifo_count.rate_const[1] = (uint16_t) ((nominal - audio->feedback.min_value) / fifo_lvl_thr);
- // On HS feedback is more sensitive since packet size can vary every MSOF, could cause instability
- if (tud_speed_get() == TUSB_SPEED_HIGH) {
- audio->feedback.compute.fifo_count.rate_const[0] /= 8;
- audio->feedback.compute.fifo_count.rate_const[1] /= 8;
- }
- } break;
-
- // nothing to do
- default:
- break;
- }
- }
+ // Prepare feedback computation parameters
+ TU_VERIFY(audiod_fb_params_prepare(func_id, alt));
#endif// CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
// We are done - abort loop
@@ -1276,20 +1304,23 @@ static bool audiod_control_complete(uint8_t rhport, tusb_control_request_t const
TU_VERIFY(audiod_verify_entity_exists(itf, entityID, &func_id));
#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
- uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
- if (_audiod_fct[func_id].bclock_id_tx == entityID && ctrlSel == AUDIO_CS_CTRL_SAM_FREQ && p_request->bRequest == AUDIO_CS_REQ_CUR) {
- _audiod_fct[func_id].sample_rate_tx = tu_unaligned_read32(_audiod_fct[func_id].ctrl_buf);
+ if (tud_audio_n_version(func_id) == 2) {
+ uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
+ if (_audiod_fct[func_id].bclock_id_tx == entityID && ctrlSel == AUDIO20_CS_CTRL_SAM_FREQ && p_request->bRequest == AUDIO20_CS_REQ_CUR) {
+ _audiod_fct[func_id].sample_rate_tx = tu_unaligned_read32(get_ctrl_buffer());
+ audiod_calc_tx_packet_sz(&_audiod_fct[func_id]);
+ }
}
#endif
// Invoke callback
- return tud_audio_set_req_entity_cb(rhport, p_request, _audiod_fct[func_id].ctrl_buf);
+ return tud_audio_set_req_entity_cb(rhport, p_request, get_ctrl_buffer());
} else {
// Find index of audio driver structure and verify interface really exists
TU_VERIFY(audiod_verify_itf_exists(itf, &func_id));
// Invoke callback
- return tud_audio_set_req_itf_cb(rhport, p_request, _audiod_fct[func_id].ctrl_buf);
+ return tud_audio_set_req_itf_cb(rhport, p_request, get_ctrl_buffer());
}
} break;
@@ -1299,8 +1330,32 @@ static bool audiod_control_complete(uint8_t rhport, tusb_control_request_t const
// Check if entity is present and get corresponding driver index
TU_VERIFY(audiod_verify_ep_exists(ep, &func_id));
- // Invoke callback
- return tud_audio_set_req_ep_cb(rhport, p_request, _audiod_fct[func_id].ctrl_buf);
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
+ if (tud_audio_n_version(func_id) == 1) {
+ if (_audiod_fct[func_id].ep_in == ep) {
+ uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
+ if (ctrlSel == AUDIO10_EP_CTRL_SAMPLING_FREQ && p_request->bRequest == AUDIO10_CS_REQ_SET_CUR) {
+ _audiod_fct[func_id].sample_rate_tx = tu_unaligned_read32(get_ctrl_buffer()) & 0x00FFFFFF;
+ audiod_calc_tx_packet_sz(&_audiod_fct[func_id]);
+ }
+ }
+ }
+#endif
+
+ // Invoke callback
+ bool ret = tud_audio_set_req_ep_cb(rhport, p_request, get_ctrl_buffer());
+
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+ if (ret && tud_audio_n_version(func_id) == 1) {
+ if (_audiod_fct[func_id].ep_out == ep) {
+ uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
+ if (ctrlSel == AUDIO10_EP_CTRL_SAMPLING_FREQ && p_request->bRequest == AUDIO10_CS_REQ_SET_CUR) {
+ audiod_fb_params_prepare(func_id, _audiod_fct[func_id].ep_out_alt);
+ }
+ }
+ }
+#endif
+ return ret;
} break;
// Unknown/Unsupported recipient
default:
@@ -1385,7 +1440,7 @@ static bool audiod_control_request(uint8_t rhport, tusb_control_request_t const
}
// If we end here, the received request is a set request - we schedule a receive for the data stage and return true here. We handle the rest later in audiod_control_complete() once the data stage was finished
- TU_VERIFY(tud_control_xfer(rhport, p_request, _audiod_fct[func_id].ctrl_buf, _audiod_fct[func_id].ctrl_buf_sz));
+ TU_VERIFY(tud_control_xfer(rhport, p_request, get_ctrl_buffer(), CFG_TUD_AUDIO_CTRL_BUF_SZ));
return true;
}
@@ -1399,6 +1454,8 @@ bool audiod_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_
return audiod_control_request(rhport, request);
} else if (stage == CONTROL_STAGE_DATA) {
return audiod_control_complete(rhport, request);
+ } else {
+ // nothing to do
}
return true;
@@ -1422,7 +1479,7 @@ bool audiod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint3
// I assume here, that things above are handled by PHY
// All transmission is done - what remains to do is to inform job was completed
- tud_audio_int_xfer_cb(rhport);
+ tud_audio_int_done_cb(rhport);
return true;
}
@@ -1473,7 +1530,7 @@ bool audiod_xfer_isr(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint
if (audio->ep_fb == ep_addr) {
// Schedule a transmit with the new value if EP is not busy
// Schedule next transmission - value is changed bytud_audio_n_fb_set() in the meantime or the old value gets sent
- audiod_fb_send(audio);
+ audiod_fb_send(func_id, true);
return true;
}
#endif
@@ -1485,30 +1542,72 @@ bool audiod_xfer_isr(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
-static bool audiod_set_fb_params_freq(audiod_function_t *audio, uint32_t sample_freq, uint32_t mclk_freq) {
- // Check if frame interval is within sane limits
- // The interval value n_frames was taken from the descriptors within audiod_set_interface()
+static bool audiod_fb_params_prepare(uint8_t func_id, uint8_t alt) {
+ audiod_function_t *audio = &_audiod_fct[func_id];
- // n_frames_min is ceil(2^10 * f_s / f_m) for full speed and ceil(2^13 * f_s / f_m) for high speed
- // this lower limit ensures the measures feedback value has sufficient precision
- uint32_t const k = (TUSB_SPEED_FULL == tud_speed_get()) ? 10 : 13;
- uint32_t const n_frame = (1UL << audio->feedback.frame_shift);
+ // Prepare feedback computation if endpoint is available
+ if (audio->ep_fb != 0) {
+ audio_feedback_params_t fb_param = {0};
- if ((((1UL << k) * sample_freq / mclk_freq) + 1) > n_frame) {
- TU_LOG1(" UAC2 feedback interval too small\r\n");
- TU_BREAKPOINT();
- return false;
- }
+ tud_audio_feedback_params_cb(func_id, alt, &fb_param);
+ audio->feedback.compute_method = fb_param.method;
- // Check if parameters really allow for a power of two division
- if ((mclk_freq % sample_freq) == 0 && tu_is_power_of_two(mclk_freq / sample_freq)) {
- audio->feedback.compute_method = AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2;
- audio->feedback.compute.power_of_2 = (uint8_t) (16 - (audio->feedback.frame_shift - 1) - tu_log2(mclk_freq / sample_freq));
- } else if (audio->feedback.compute_method == AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT) {
- audio->feedback.compute.float_const = (float) sample_freq / (float) mclk_freq * (1UL << (16 - (audio->feedback.frame_shift - 1)));
- } else {
- audio->feedback.compute.fixed.sample_freq = sample_freq;
- audio->feedback.compute.fixed.mclk_freq = mclk_freq;
+ // Minimal/Maximum value in 16.16 format for full speed (1ms per frame) or high speed (125 us per frame)
+ uint32_t const frame_div = (TUSB_SPEED_FULL == tud_speed_get()) ? 1000 : 8000;
+ audio->feedback.min_value = ((fb_param.sample_freq - 1) / frame_div) << 16;
+ audio->feedback.max_value = (fb_param.sample_freq / frame_div + 1) << 16;
+
+ switch (fb_param.method) {
+ case AUDIO_FEEDBACK_METHOD_FREQUENCY_FIXED:
+ case AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT:
+ case AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2: {
+ // Check if frame interval is within sane limits
+ // The interval value n_frames was taken from the descriptors within audiod_set_interface()
+
+ // n_frames_min is ceil(2^10 * f_s / f_m) for full speed and ceil(2^13 * f_s / f_m) for high speed
+ // this lower limit ensures the measures feedback value has sufficient precision
+ uint32_t const k = (TUSB_SPEED_FULL == tud_speed_get()) ? 10 : 13;
+ uint32_t const n_frame = (1UL << audio->feedback.frame_shift);
+
+ if ((((1UL << k) * fb_param.sample_freq / fb_param.frequency.mclk_freq) + 1) > n_frame) {
+ TU_LOG1(" UAC2 feedback interval too small\r\n");
+ TU_BREAKPOINT();
+ return false;
+ }
+
+ // Check if parameters really allow for a power of two division
+ if ((fb_param.frequency.mclk_freq % fb_param.sample_freq) == 0 && tu_is_power_of_two(fb_param.frequency.mclk_freq / fb_param.sample_freq)) {
+ audio->feedback.compute_method = AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2;
+ audio->feedback.compute.power_of_2 = (uint8_t) (16 - (audio->feedback.frame_shift - 1) - tu_log2(fb_param.frequency.mclk_freq / fb_param.sample_freq));
+ } else if (audio->feedback.compute_method == AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT) {
+ audio->feedback.compute.float_const = (float) fb_param.sample_freq / (float) fb_param.frequency.mclk_freq * (1UL << (16 - (audio->feedback.frame_shift - 1)));
+ } else {
+ audio->feedback.compute.fixed.sample_freq = fb_param.sample_freq;
+ audio->feedback.compute.fixed.mclk_freq = fb_param.frequency.mclk_freq;
+ }
+ } break;
+
+ case AUDIO_FEEDBACK_METHOD_FIFO_COUNT: {
+ // Determine FIFO threshold
+ uint16_t fifo_threshold = fb_param.fifo_count.fifo_threshold ? fb_param.fifo_count.fifo_threshold : tu_fifo_depth(&audio->ep_out_ff) / 2;
+ audio->feedback.compute.fifo_count.fifo_lvl_thr = fifo_threshold;
+ audio->feedback.compute.fifo_count.fifo_lvl_avg = ((uint32_t) fifo_threshold) << 16;
+ // Avoid 64bit division
+ uint32_t nominal = ((fb_param.sample_freq / 100) << 16) / (frame_div / 100);
+ audio->feedback.compute.fifo_count.nom_value = nominal;
+ audio->feedback.compute.fifo_count.rate_const[0] = (uint16_t) ((audio->feedback.max_value - nominal) / fifo_threshold);
+ audio->feedback.compute.fifo_count.rate_const[1] = (uint16_t) ((nominal - audio->feedback.min_value) / fifo_threshold);
+ // On HS feedback is more sensitive since packet size can vary every MSOF, could cause instability
+ if (tud_speed_get() == TUSB_SPEED_HIGH) {
+ audio->feedback.compute.fifo_count.rate_const[0] /= 8;
+ audio->feedback.compute.fifo_count.rate_const[1] /= 8;
+ }
+ } break;
+
+ // nothing to do
+ default:
+ break;
+ }
}
return true;
@@ -1532,52 +1631,15 @@ static void audiod_fb_fifo_count_update(audiod_function_t *audio, uint16_t lvl_n
feedback = audio->feedback.compute.fifo_count.nom_value - (ff_lvl - ff_thr) * rate[1];
}
- if (feedback > audio->feedback.max_value) feedback = audio->feedback.max_value;
- if (feedback < audio->feedback.min_value) feedback = audio->feedback.min_value;
- audio->feedback.value = feedback;
-}
-
-uint32_t tud_audio_feedback_update(uint8_t func_id, uint32_t cycles) {
- audiod_function_t *audio = &_audiod_fct[func_id];
- uint32_t feedback;
-
- switch (audio->feedback.compute_method) {
- case AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2:
- feedback = (cycles << audio->feedback.compute.power_of_2);
- break;
-
- case AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT:
- feedback = (uint32_t) ((float) cycles * audio->feedback.compute.float_const);
- break;
-
- case AUDIO_FEEDBACK_METHOD_FREQUENCY_FIXED: {
- uint64_t fb64 = (((uint64_t) cycles) * audio->feedback.compute.fixed.sample_freq) << (16 - (audio->feedback.frame_shift - 1));
- feedback = (uint32_t) (fb64 / audio->feedback.compute.fixed.mclk_freq);
- } break;
-
- default:
- return 0;
+ if (feedback > audio->feedback.max_value) {
+ feedback = audio->feedback.max_value;
}
-
- // For Windows: https://docs.microsoft.com/en-us/windows-hardware/drivers/audio/usb-2-0-audio-drivers
- // The size of isochronous packets created by the device must be within the limits specified in FMT-2.0 section 2.3.1.1.
- // This means that the deviation of actual packet size from nominal size must not exceed +/- one audio slot
- // (audio slot = channel count samples).
- if (feedback > audio->feedback.max_value) feedback = audio->feedback.max_value;
- if (feedback < audio->feedback.min_value) feedback = audio->feedback.min_value;
-
- tud_audio_n_fb_set(func_id, feedback);
-
- return feedback;
+ if (feedback < audio->feedback.min_value) {
+ feedback = audio->feedback.min_value;
+ }
+ audio->feedback.value = feedback;
}
-bool tud_audio_n_fb_set(uint8_t func_id, uint32_t feedback) {
- TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL);
-
- _audiod_fct[func_id].feedback.value = feedback;
-
- return true;
-}
#endif
TU_ATTR_FAST_FUNC void audiod_sof_isr(uint8_t rhport, uint32_t frame_count) {
@@ -1644,25 +1706,25 @@ bool tud_audio_buffer_and_schedule_control_xfer(uint8_t rhport, tusb_control_req
return false;
}
- // Crop length
- if (len > _audiod_fct[func_id].ctrl_buf_sz) len = _audiod_fct[func_id].ctrl_buf_sz;
-
// Copy into buffer
- TU_VERIFY(0 == tu_memcpy_s(_audiod_fct[func_id].ctrl_buf, _audiod_fct[func_id].ctrl_buf_sz, data, (size_t) len));
+ TU_VERIFY(0 == tu_memcpy_s(get_ctrl_buffer(), CFG_TUD_AUDIO_CTRL_BUF_SZ, data, (size_t) len));
#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
- // Find data for sampling_frequency_control
- if (p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS && p_request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE) {
- uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
- uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
- if (_audiod_fct[func_id].bclock_id_tx == entityID && ctrlSel == AUDIO_CS_CTRL_SAM_FREQ && p_request->bRequest == AUDIO_CS_REQ_CUR) {
- _audiod_fct[func_id].sample_rate_tx = tu_unaligned_read32(_audiod_fct[func_id].ctrl_buf);
+ if (tud_audio_n_version(func_id) == 2) {
+ // Find data for sampling_frequency_control
+ if (p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS && p_request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE) {
+ uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
+ uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
+ if (_audiod_fct[func_id].bclock_id_tx == entityID && ctrlSel == AUDIO20_CS_CTRL_SAM_FREQ && p_request->bRequest == AUDIO20_CS_REQ_CUR) {
+ _audiod_fct[func_id].sample_rate_tx = tu_unaligned_read32(get_ctrl_buffer());
+ audiod_calc_tx_packet_sz(&_audiod_fct[func_id]);
+ }
}
}
#endif
// Schedule transmit
- return tud_control_xfer(rhport, p_request, (void *) _audiod_fct[func_id].ctrl_buf, len);
+ return tud_control_xfer(rhport, p_request, get_ctrl_buffer(), len);
}
// Verify an entity with the given ID exists and returns also the corresponding driver index
@@ -1673,11 +1735,9 @@ static bool audiod_verify_entity_exists(uint8_t itf, uint8_t entityID, uint8_t *
if (_audiod_fct[i].p_desc && ((tusb_desc_interface_t const *) _audiod_fct[i].p_desc)->bInterfaceNumber == itf) {
// Get pointers after class specific AC descriptors and end of AC descriptors - entities are defined in between
uint8_t const *p_desc = tu_desc_next(_audiod_fct[i].p_desc);// Points to CS AC descriptor
- uint8_t const *p_desc_end = ((audio_desc_cs_ac_interface_t const *) p_desc)->wTotalLength + p_desc;
p_desc = tu_desc_next(p_desc);// Get past CS AC descriptor
- // Condition modified from p_desc < p_desc_end to prevent gcc>=12 strict-overflow warning
- while (p_desc_end - p_desc > 0) {
+ while (_audiod_fct[i].p_desc_as - p_desc > 0) {
// Entity IDs are always at offset 3
if (p_desc[3] == entityID) {
*func_id = i;
@@ -1693,10 +1753,10 @@ static bool audiod_verify_entity_exists(uint8_t itf, uint8_t entityID, uint8_t *
static bool audiod_verify_itf_exists(uint8_t itf, uint8_t *func_id) {
uint8_t i;
for (i = 0; i < CFG_TUD_AUDIO; i++) {
- if (_audiod_fct[i].p_desc) {
+ if (_audiod_fct[i].p_desc != NULL) {
// Get pointer at beginning and end
uint8_t const *p_desc = _audiod_fct[i].p_desc;
- uint8_t const *p_desc_end = _audiod_fct[i].p_desc + _audiod_fct[i].desc_length - TUD_AUDIO_DESC_IAD_LEN;
+ uint8_t const *p_desc_end = _audiod_fct[i].p_desc + _audiod_fct[i].desc_length;
// Condition modified from p_desc < p_desc_end to prevent gcc>=12 strict-overflow warning
while (p_desc_end - p_desc > 0) {
if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE && ((tusb_desc_interface_t const *)p_desc)->bInterfaceNumber == itf) {
@@ -1718,8 +1778,7 @@ static bool audiod_verify_ep_exists(uint8_t ep, uint8_t *func_id) {
uint8_t const *p_desc_end = _audiod_fct[i].p_desc + _audiod_fct[i].desc_length;
// Advance past AC descriptors - EP we look for are streaming EPs
- uint8_t const *p_desc = tu_desc_next(_audiod_fct[i].p_desc);
- p_desc += ((audio_desc_cs_ac_interface_t const *) p_desc)->wTotalLength;
+ uint8_t const *p_desc = _audiod_fct[i].p_desc_as;
// Condition modified from p_desc < p_desc_end to prevent gcc>=12 strict-overflow warning
while (p_desc_end - p_desc > 0) {
@@ -1739,20 +1798,34 @@ static void audiod_parse_flow_control_params(audiod_function_t *audio, uint8_t c
p_desc = tu_desc_next(p_desc);// Exclude standard AS interface descriptor of current alternate interface descriptor
- // Look for a Class-Specific AS Interface Descriptor(4.9.2) to verify format type and format and also to get number of physical channels
- if (tu_desc_type(p_desc) == TUSB_DESC_CS_INTERFACE && tu_desc_subtype(p_desc) == AUDIO_CS_AS_INTERFACE_AS_GENERAL) {
- audio->n_channels_tx = ((audio_desc_cs_as_interface_t const *) p_desc)->bNrChannels;
- audio->format_type_tx = (audio_format_type_t) (((audio_desc_cs_as_interface_t const *) p_desc)->bFormatType);
- // Look for a Type I Format Type Descriptor(2.3.1.6 - Audio Formats)
- p_desc = tu_desc_next(p_desc);
- if (tu_desc_type(p_desc) == TUSB_DESC_CS_INTERFACE && tu_desc_subtype(p_desc) == AUDIO_CS_AS_INTERFACE_FORMAT_TYPE && ((audio_desc_type_I_format_t const *) p_desc)->bFormatType == AUDIO_FORMAT_TYPE_I) {
- audio->n_bytes_per_sample_tx = ((audio_desc_type_I_format_t const *) p_desc)->bSubslotSize;
+ if (tud_audio_n_version(audiod_get_audio_fct_idx(audio)) == 1) {
+ p_desc = tu_desc_next(p_desc);// Exclude Class-Specific AS Interface Descriptor(4.5.2) to get to format type descriptor
+ if (tu_desc_type(p_desc) == TUSB_DESC_CS_INTERFACE && tu_desc_subtype(p_desc) == AUDIO10_CS_AS_INTERFACE_FORMAT_TYPE) {
+ audio->format_type_tx = ((audio10_desc_type_I_format_n_t(1) const *) p_desc)->bFormatType;
+ if (audio->format_type_tx == AUDIO10_FORMAT_TYPE_I) {
+ audio->n_channels_tx = ((audio10_desc_type_I_format_n_t(1) const *) p_desc)->bNrChannels;
+ audio->n_bytes_per_sample_tx = ((audio10_desc_type_I_format_n_t(1) const *) p_desc)->bSubFrameSize;
+ // Save sample rate - needed when EP doesn't support setting sample rate
+ audio->sample_rate_tx = tu_unaligned_read32(((audio10_desc_type_I_format_n_t(1) const *) p_desc)->tSamFreq) & 0x00FFFFFF;
+ }
+ }
+ } else {
+ // Look for a Class-Specific AS Interface Descriptor(4.9.2) to verify format type and format and also to get number of physical channels
+ if (tu_desc_type(p_desc) == TUSB_DESC_CS_INTERFACE && tu_desc_subtype(p_desc) == AUDIO20_CS_AS_INTERFACE_AS_GENERAL) {
+ audio->n_channels_tx = ((audio20_desc_cs_as_interface_t const *) p_desc)->bNrChannels;
+ audio->format_type_tx = ((audio20_desc_cs_as_interface_t const *) p_desc)->bFormatType;
+ // Look for a Type I Format Type Descriptor(2.3.1.6 - Audio Formats)
+ p_desc = tu_desc_next(p_desc);
+ if (tu_desc_type(p_desc) == TUSB_DESC_CS_INTERFACE && tu_desc_subtype(p_desc) == AUDIO20_CS_AS_INTERFACE_FORMAT_TYPE && ((audio20_desc_type_I_format_t const *) p_desc)->bFormatType == AUDIO20_FORMAT_TYPE_I) {
+ audio->n_bytes_per_sample_tx = ((audio20_desc_type_I_format_t const *) p_desc)->bSubslotSize;
+ }
}
}
}
static bool audiod_calc_tx_packet_sz(audiod_function_t *audio) {
- TU_VERIFY(audio->format_type_tx == AUDIO_FORMAT_TYPE_I);
+ // AUDIO20_FORMAT_TYPE_I = AUDIO10_FORMAT_TYPE_I
+ TU_VERIFY(audio->format_type_tx == AUDIO20_FORMAT_TYPE_I);
TU_VERIFY(audio->n_channels_tx);
TU_VERIFY(audio->n_bytes_per_sample_tx);
TU_VERIFY(audio->interval_tx);
@@ -1787,22 +1860,22 @@ static bool audiod_calc_tx_packet_sz(audiod_function_t *audio) {
return true;
}
-static uint16_t audiod_tx_packet_size(const uint16_t *norminal_size, uint16_t data_count, uint16_t fifo_depth, uint16_t max_depth) {
+static uint16_t audiod_tx_packet_size(const uint16_t *nominal_size, uint16_t data_count, uint16_t fifo_depth, uint16_t fifo_threshold, uint16_t max_depth) {
// Flow control need a FIFO size of at least 4*Navg
- if (norminal_size[1] && norminal_size[1] <= fifo_depth * 4) {
+ if (nominal_size[1] && nominal_size[1] <= fifo_depth * 4) {
// Use blackout to prioritize normal size packet
static int ctrl_blackout = 0;
uint16_t packet_size;
- uint16_t slot_size = norminal_size[2] - norminal_size[1];
- if (data_count < norminal_size[0]) {
+ uint16_t slot_size = nominal_size[2] - nominal_size[1];
+ if (data_count < nominal_size[0]) {
// If you get here frequently, then your I2S clock deviation is too big !
packet_size = 0;
- } else if (data_count < fifo_depth / 2 - slot_size && !ctrl_blackout) {
- packet_size = norminal_size[0];
+ } else if (data_count < (fifo_threshold - slot_size) && !ctrl_blackout) {
+ packet_size = nominal_size[0];
ctrl_blackout = 10;
- } else if (data_count > fifo_depth / 2 + slot_size && !ctrl_blackout) {
- packet_size = norminal_size[2];
- if (norminal_size[0] == norminal_size[1]) {
+ } else if (data_count > (fifo_threshold + slot_size) && !ctrl_blackout) {
+ packet_size = nominal_size[2];
+ if (nominal_size[0] == nominal_size[1]) {
// nav > INT(nav), eg. 44.1k, 88.2k
ctrl_blackout = 0;
} else {
@@ -1810,7 +1883,7 @@ static uint16_t audiod_tx_packet_size(const uint16_t *norminal_size, uint16_t da
ctrl_blackout = 10;
}
} else {
- packet_size = norminal_size[1];
+ packet_size = nominal_size[1];
if (ctrl_blackout) {
ctrl_blackout--;
}
@@ -1825,11 +1898,8 @@ static uint16_t audiod_tx_packet_size(const uint16_t *norminal_size, uint16_t da
#endif
// No security checks here - internal function only which should always succeed
-static uint8_t audiod_get_audio_fct_idx(audiod_function_t *audio) {
- for (uint8_t cnt = 0; cnt < CFG_TUD_AUDIO; cnt++) {
- if (&_audiod_fct[cnt] == audio) return cnt;
- }
- return 0;
+static inline uint8_t audiod_get_audio_fct_idx(audiod_function_t *audio) {
+ return (uint8_t) (audio - _audiod_fct);
}
#endif // (CFG_TUD_ENABLED && CFG_TUD_AUDIO)