summaryrefslogtreecommitdiffstats
path: root/tinyusb/src/class/audio
diff options
context:
space:
mode:
Diffstat (limited to 'tinyusb/src/class/audio')
-rwxr-xr-xtinyusb/src/class/audio/audio.h945
-rwxr-xr-xtinyusb/src/class/audio/audio_device.c2272
-rwxr-xr-xtinyusb/src/class/audio/audio_device.h627
3 files changed, 3844 insertions, 0 deletions
diff --git a/tinyusb/src/class/audio/audio.h b/tinyusb/src/class/audio/audio.h
new file mode 100755
index 00000000..f99061ea
--- /dev/null
+++ b/tinyusb/src/class/audio/audio.h
@@ -0,0 +1,945 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ * Copyright (c) 2020 Reinhard Panhuber
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+/** \ingroup group_class
+ * \defgroup ClassDriver_Audio Audio
+ * Currently only MIDI subclass is supported
+ * @{ */
+
+#ifndef _TUSB_AUDIO_H__
+#define _TUSB_AUDIO_H__
+
+#include "common/tusb_common.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/// Audio Device Class Codes
+
+/// A.2 - Audio Function Subclass Codes
+typedef enum
+{
+ AUDIO_FUNCTION_SUBCLASS_UNDEFINED = 0x00,
+} audio_function_subclass_type_t;
+
+/// A.3 - Audio Function Protocol Codes
+typedef enum
+{
+ AUDIO_FUNC_PROTOCOL_CODE_UNDEF = 0x00,
+ AUDIO_FUNC_PROTOCOL_CODE_V2 = 0x20, ///< Version 2.0
+} audio_function_protocol_code_t;
+
+/// A.5 - Audio Interface Subclass Codes
+typedef enum
+{
+ AUDIO_SUBCLASS_UNDEFINED = 0x00,
+ AUDIO_SUBCLASS_CONTROL , ///< Audio Control
+ AUDIO_SUBCLASS_STREAMING , ///< Audio Streaming
+ AUDIO_SUBCLASS_MIDI_STREAMING , ///< MIDI Streaming
+} audio_subclass_type_t;
+
+/// A.6 - Audio Interface Protocol Codes
+typedef enum
+{
+ AUDIO_INT_PROTOCOL_CODE_UNDEF = 0x00,
+ AUDIO_INT_PROTOCOL_CODE_V2 = 0x20, ///< Version 2.0
+} audio_interface_protocol_code_t;
+
+/// A.7 - Audio Function Category Codes
+typedef enum
+{
+ AUDIO_FUNC_UNDEF = 0x00,
+ AUDIO_FUNC_DESKTOP_SPEAKER = 0x01,
+ AUDIO_FUNC_HOME_THEATER = 0x02,
+ AUDIO_FUNC_MICROPHONE = 0x03,
+ AUDIO_FUNC_HEADSET = 0x04,
+ AUDIO_FUNC_TELEPHONE = 0x05,
+ AUDIO_FUNC_CONVERTER = 0x06,
+ AUDIO_FUNC_SOUND_RECODER = 0x07,
+ AUDIO_FUNC_IO_BOX = 0x08,
+ AUDIO_FUNC_MUSICAL_INSTRUMENT = 0x09,
+ AUDIO_FUNC_PRO_AUDIO = 0x0A,
+ AUDIO_FUNC_AUDIO_VIDEO = 0x0B,
+ AUDIO_FUNC_CONTROL_PANEL = 0x0C,
+ AUDIO_FUNC_OTHER = 0xFF,
+} audio_function_code_t;
+
+/// A.9 - Audio Class-Specific AC Interface Descriptor Subtypes UAC2
+typedef enum
+{
+ AUDIO_CS_AC_INTERFACE_AC_DESCRIPTOR_UNDEF = 0x00,
+ AUDIO_CS_AC_INTERFACE_HEADER = 0x01,
+ AUDIO_CS_AC_INTERFACE_INPUT_TERMINAL = 0x02,
+ AUDIO_CS_AC_INTERFACE_OUTPUT_TERMINAL = 0x03,
+ AUDIO_CS_AC_INTERFACE_MIXER_UNIT = 0x04,
+ AUDIO_CS_AC_INTERFACE_SELECTOR_UNIT = 0x05,
+ AUDIO_CS_AC_INTERFACE_FEATURE_UNIT = 0x06,
+ AUDIO_CS_AC_INTERFACE_EFFECT_UNIT = 0x07,
+ AUDIO_CS_AC_INTERFACE_PROCESSING_UNIT = 0x08,
+ AUDIO_CS_AC_INTERFACE_EXTENSION_UNIT = 0x09,
+ AUDIO_CS_AC_INTERFACE_CLOCK_SOURCE = 0x0A,
+ AUDIO_CS_AC_INTERFACE_CLOCK_SELECTOR = 0x0B,
+ AUDIO_CS_AC_INTERFACE_CLOCK_MULTIPLIER = 0x0C,
+ AUDIO_CS_AC_INTERFACE_SAMPLE_RATE_CONVERTER = 0x0D,
+} audio_cs_ac_interface_subtype_t;
+
+/// A.10 - Audio Class-Specific AS Interface Descriptor Subtypes UAC2
+typedef enum
+{
+ AUDIO_CS_AS_INTERFACE_AS_DESCRIPTOR_UNDEF = 0x00,
+ AUDIO_CS_AS_INTERFACE_AS_GENERAL = 0x01,
+ AUDIO_CS_AS_INTERFACE_FORMAT_TYPE = 0x02,
+ AUDIO_CS_AS_INTERFACE_ENCODER = 0x03,
+ AUDIO_CS_AS_INTERFACE_DECODER = 0x04,
+} audio_cs_as_interface_subtype_t;
+
+/// A.11 - Effect Unit Effect Types
+typedef enum
+{
+ AUDIO_EFFECT_TYPE_UNDEF = 0x00,
+ AUDIO_EFFECT_TYPE_PARAM_EQ_SECTION = 0x01,
+ AUDIO_EFFECT_TYPE_REVERBERATION = 0x02,
+ AUDIO_EFFECT_TYPE_MOD_DELAY = 0x03,
+ AUDIO_EFFECT_TYPE_DYN_RANGE_COMP = 0x04,
+} audio_effect_unit_effect_type_t;
+
+/// A.12 - Processing Unit Process Types
+typedef enum
+{
+ AUDIO_PROCESS_TYPE_UNDEF = 0x00,
+ AUDIO_PROCESS_TYPE_UP_DOWN_MIX = 0x01,
+ AUDIO_PROCESS_TYPE_DOLBY_PROLOGIC = 0x02,
+ AUDIO_PROCESS_TYPE_STEREO_EXTENDER = 0x03,
+} audio_processing_unit_process_type_t;
+
+/// A.13 - Audio Class-Specific EP Descriptor Subtypes UAC2
+typedef enum
+{
+ AUDIO_CS_EP_SUBTYPE_UNDEF = 0x00,
+ AUDIO_CS_EP_SUBTYPE_GENERAL = 0x01,
+} audio_cs_ep_subtype_t;
+
+/// A.14 - Audio Class-Specific Request Codes
+typedef enum
+{
+ AUDIO_CS_REQ_UNDEF = 0x00,
+ AUDIO_CS_REQ_CUR = 0x01,
+ AUDIO_CS_REQ_RANGE = 0x02,
+ AUDIO_CS_REQ_MEM = 0x03,
+} audio_cs_req_t;
+
+/// A.17 - Control Selector Codes
+
+/// A.17.1 - Clock Source Control Selectors
+typedef enum
+{
+ AUDIO_CS_CTRL_UNDEF = 0x00,
+ AUDIO_CS_CTRL_SAM_FREQ = 0x01,
+ AUDIO_CS_CTRL_CLK_VALID = 0x02,
+} audio_clock_src_control_selector_t;
+
+/// A.17.2 - Clock Selector Control Selectors
+typedef enum
+{
+ AUDIO_CX_CTRL_UNDEF = 0x00,
+ AUDIO_CX_CTRL_CONTROL = 0x01,
+} audio_clock_sel_control_selector_t;
+
+/// A.17.3 - Clock Multiplier Control Selectors
+typedef enum
+{
+ AUDIO_CM_CTRL_UNDEF = 0x00,
+ AUDIO_CM_CTRL_NUMERATOR_CONTROL = 0x01,
+ AUDIO_CM_CTRL_DENOMINATOR_CONTROL = 0x02,
+} audio_clock_mul_control_selector_t;
+
+/// A.17.4 - Terminal Control Selectors
+typedef enum
+{
+ AUDIO_TE_CTRL_UNDEF = 0x00,
+ AUDIO_TE_CTRL_COPY_PROTECT = 0x01,
+ AUDIO_TE_CTRL_CONNECTOR = 0x02,
+ AUDIO_TE_CTRL_OVERLOAD = 0x03,
+ AUDIO_TE_CTRL_CLUSTER = 0x04,
+ AUDIO_TE_CTRL_UNDERFLOW = 0x05,
+ AUDIO_TE_CTRL_OVERFLOW = 0x06,
+ AUDIO_TE_CTRL_LATENCY = 0x07,
+} audio_terminal_control_selector_t;
+
+/// A.17.5 - Mixer Control Selectors
+typedef enum
+{
+ AUDIO_MU_CTRL_UNDEF = 0x00,
+ AUDIO_MU_CTRL_MIXER = 0x01,
+ AUDIO_MU_CTRL_CLUSTER = 0x02,
+ AUDIO_MU_CTRL_UNDERFLOW = 0x03,
+ AUDIO_MU_CTRL_OVERFLOW = 0x04,
+ AUDIO_MU_CTRL_LATENCY = 0x05,
+} audio_mixer_control_selector_t;
+
+/// A.17.6 - Selector Control Selectors
+typedef enum
+{
+ AUDIO_SU_CTRL_UNDEF = 0x00,
+ AUDIO_SU_CTRL_SELECTOR = 0x01,
+ AUDIO_SU_CTRL_LATENCY = 0x02,
+} audio_sel_control_selector_t;
+
+/// A.17.7 - Feature Unit Control Selectors
+typedef enum
+{
+ AUDIO_FU_CTRL_UNDEF = 0x00,
+ AUDIO_FU_CTRL_MUTE = 0x01,
+ AUDIO_FU_CTRL_VOLUME = 0x02,
+ AUDIO_FU_CTRL_BASS = 0x03,
+ AUDIO_FU_CTRL_MID = 0x04,
+ AUDIO_FU_CTRL_TREBLE = 0x05,
+ AUDIO_FU_CTRL_GRAPHIC_EQUALIZER = 0x06,
+ AUDIO_FU_CTRL_AGC = 0x07,
+ AUDIO_FU_CTRL_DELAY = 0x08,
+ AUDIO_FU_CTRL_BASS_BOOST = 0x09,
+ AUDIO_FU_CTRL_LOUDNESS = 0x0A,
+ AUDIO_FU_CTRL_INPUT_GAIN = 0x0B,
+ AUDIO_FU_CTRL_GAIN_PAD = 0x0C,
+ AUDIO_FU_CTRL_INVERTER = 0x0D,
+ AUDIO_FU_CTRL_UNDERFLOW = 0x0E,
+ AUDIO_FU_CTRL_OVERVLOW = 0x0F,
+ AUDIO_FU_CTRL_LATENCY = 0x10,
+} audio_feature_unit_control_selector_t;
+
+/// A.17.8 Effect Unit Control Selectors
+
+/// A.17.8.1 Parametric Equalizer Section Effect Unit Control Selectors
+typedef enum
+{
+ AUDIO_PE_CTRL_UNDEF = 0x00,
+ AUDIO_PE_CTRL_ENABLE = 0x01,
+ AUDIO_PE_CTRL_CENTERFREQ = 0x02,
+ AUDIO_PE_CTRL_QFACTOR = 0x03,
+ AUDIO_PE_CTRL_GAIN = 0x04,
+ AUDIO_PE_CTRL_UNDERFLOW = 0x05,
+ AUDIO_PE_CTRL_OVERFLOW = 0x06,
+ AUDIO_PE_CTRL_LATENCY = 0x07,
+} audio_parametric_equalizer_control_selector_t;
+
+/// A.17.8.2 Reverberation Effect Unit Control Selectors
+typedef enum
+{
+ AUDIO_RV_CTRL_UNDEF = 0x00,
+ AUDIO_RV_CTRL_ENABLE = 0x01,
+ AUDIO_RV_CTRL_TYPE = 0x02,
+ AUDIO_RV_CTRL_LEVEL = 0x03,
+ AUDIO_RV_CTRL_TIME = 0x04,
+ AUDIO_RV_CTRL_FEEDBACK = 0x05,
+ AUDIO_RV_CTRL_PREDELAY = 0x06,
+ AUDIO_RV_CTRL_DENSITY = 0x07,
+ AUDIO_RV_CTRL_HIFREQ_ROLLOFF = 0x08,
+ AUDIO_RV_CTRL_UNDERFLOW = 0x09,
+ AUDIO_RV_CTRL_OVERFLOW = 0x0A,
+ AUDIO_RV_CTRL_LATENCY = 0x0B,
+} audio_reverberation_effect_control_selector_t;
+
+/// A.17.8.3 Modulation Delay Effect Unit Control Selectors
+typedef enum
+{
+ AUDIO_MD_CTRL_UNDEF = 0x00,
+ AUDIO_MD_CTRL_ENABLE = 0x01,
+ AUDIO_MD_CTRL_BALANCE = 0x02,
+ AUDIO_MD_CTRL_RATE = 0x03,
+ AUDIO_MD_CTRL_DEPTH = 0x04,
+ AUDIO_MD_CTRL_TIME = 0x05,
+ AUDIO_MD_CTRL_FEEDBACK = 0x06,
+ AUDIO_MD_CTRL_UNDERFLOW = 0x07,
+ AUDIO_MD_CTRL_OVERFLOW = 0x08,
+ AUDIO_MD_CTRL_LATENCY = 0x09,
+} audio_modulation_delay_control_selector_t;
+
+/// A.17.8.4 Dynamic Range Compressor Effect Unit Control Selectors
+typedef enum
+{
+ AUDIO_DR_CTRL_UNDEF = 0x00,
+ AUDIO_DR_CTRL_ENABLE = 0x01,
+ AUDIO_DR_CTRL_COMPRESSION_RATE = 0x02,
+ AUDIO_DR_CTRL_MAXAMPL = 0x03,
+ AUDIO_DR_CTRL_THRESHOLD = 0x04,
+ AUDIO_DR_CTRL_ATTACK_TIME = 0x05,
+ AUDIO_DR_CTRL_RELEASE_TIME = 0x06,
+ AUDIO_DR_CTRL_UNDERFLOW = 0x07,
+ AUDIO_DR_CTRL_OVERFLOW = 0x08,
+ AUDIO_DR_CTRL_LATENCY = 0x09,
+} audio_dynamic_range_compression_control_selector_t;
+
+/// A.17.9 Processing Unit Control Selectors
+
+/// A.17.9.1 Up/Down-mix Processing Unit Control Selectors
+typedef enum
+{
+ AUDIO_UD_CTRL_UNDEF = 0x00,
+ AUDIO_UD_CTRL_ENABLE = 0x01,
+ AUDIO_UD_CTRL_MODE_SELECT = 0x02,
+ AUDIO_UD_CTRL_CLUSTER = 0x03,
+ AUDIO_UD_CTRL_UNDERFLOW = 0x04,
+ AUDIO_UD_CTRL_OVERFLOW = 0x05,
+ AUDIO_UD_CTRL_LATENCY = 0x06,
+} audio_up_down_mix_control_selector_t;
+
+/// A.17.9.2 Dolby Prologic ™ Processing Unit Control Selectors
+typedef enum
+{
+ AUDIO_DP_CTRL_UNDEF = 0x00,
+ AUDIO_DP_CTRL_ENABLE = 0x01,
+ AUDIO_DP_CTRL_MODE_SELECT = 0x02,
+ AUDIO_DP_CTRL_CLUSTER = 0x03,
+ AUDIO_DP_CTRL_UNDERFLOW = 0x04,
+ AUDIO_DP_CTRL_OVERFLOW = 0x05,
+ AUDIO_DP_CTRL_LATENCY = 0x06,
+} audio_dolby_prologic_control_selector_t;
+
+/// A.17.9.3 Stereo Extender Processing Unit Control Selectors
+typedef enum
+{
+ AUDIO_ST_EXT_CTRL_UNDEF = 0x00,
+ AUDIO_ST_EXT_CTRL_ENABLE = 0x01,
+ AUDIO_ST_EXT_CTRL_WIDTH = 0x02,
+ AUDIO_ST_EXT_CTRL_UNDERFLOW = 0x03,
+ AUDIO_ST_EXT_CTRL_OVERFLOW = 0x04,
+ AUDIO_ST_EXT_CTRL_LATENCY = 0x05,
+} audio_stereo_extender_control_selector_t;
+
+/// A.17.10 Extension Unit Control Selectors
+typedef enum
+{
+ AUDIO_XU_CTRL_UNDEF = 0x00,
+ AUDIO_XU_CTRL_ENABLE = 0x01,
+ AUDIO_XU_CTRL_CLUSTER = 0x02,
+ AUDIO_XU_CTRL_UNDERFLOW = 0x03,
+ AUDIO_XU_CTRL_OVERFLOW = 0x04,
+ AUDIO_XU_CTRL_LATENCY = 0x05,
+} audio_extension_unit_control_selector_t;
+
+/// A.17.11 AudioStreaming Interface Control Selectors
+typedef enum
+{
+ AUDIO_AS_CTRL_UNDEF = 0x00,
+ AUDIO_AS_CTRL_ACT_ALT_SETTING = 0x01,
+ AUDIO_AS_CTRL_VAL_ALT_SETTINGS = 0x02,
+ AUDIO_AS_CTRL_AUDIO_DATA_FORMAT = 0x03,
+} audio_audiostreaming_interface_control_selector_t;
+
+/// A.17.12 Encoder Control Selectors
+typedef enum
+{
+ AUDIO_EN_CTRL_UNDEF = 0x00,
+ AUDIO_EN_CTRL_BIT_RATE = 0x01,
+ AUDIO_EN_CTRL_QUALITY = 0x02,
+ AUDIO_EN_CTRL_VBR = 0x03,
+ AUDIO_EN_CTRL_TYPE = 0x04,
+ AUDIO_EN_CTRL_UNDERFLOW = 0x05,
+ AUDIO_EN_CTRL_OVERFLOW = 0x06,
+ AUDIO_EN_CTRL_ENCODER_ERROR = 0x07,
+ AUDIO_EN_CTRL_PARAM1 = 0x08,
+ AUDIO_EN_CTRL_PARAM2 = 0x09,
+ AUDIO_EN_CTRL_PARAM3 = 0x0A,
+ AUDIO_EN_CTRL_PARAM4 = 0x0B,
+ AUDIO_EN_CTRL_PARAM5 = 0x0C,
+ AUDIO_EN_CTRL_PARAM6 = 0x0D,
+ AUDIO_EN_CTRL_PARAM7 = 0x0E,
+ AUDIO_EN_CTRL_PARAM8 = 0x0F,
+} audio_encoder_control_selector_t;
+
+/// A.17.13 Decoder Control Selectors
+
+/// A.17.13.1 MPEG Decoder Control Selectors
+typedef enum
+{
+ AUDIO_MPD_CTRL_UNDEF = 0x00,
+ AUDIO_MPD_CTRL_DUAL_CHANNEL = 0x01,
+ AUDIO_MPD_CTRL_SECOND_STEREO = 0x02,
+ AUDIO_MPD_CTRL_MULTILINGUAL = 0x03,
+ AUDIO_MPD_CTRL_DYN_RANGE = 0x04,
+ AUDIO_MPD_CTRL_SCALING = 0x05,
+ AUDIO_MPD_CTRL_HILO_SCALING = 0x06,
+ AUDIO_MPD_CTRL_UNDERFLOW = 0x07,
+ AUDIO_MPD_CTRL_OVERFLOW = 0x08,
+ AUDIO_MPD_CTRL_DECODER_ERROR = 0x09,
+} audio_MPEG_decoder_control_selector_t;
+
+/// A.17.13.2 AC-3 Decoder Control Selectors
+typedef enum
+{
+ AUDIO_AD_CTRL_UNDEF = 0x00,
+ AUDIO_AD_CTRL_MODE = 0x01,
+ AUDIO_AD_CTRL_DYN_RANGE = 0x02,
+ AUDIO_AD_CTRL_SCALING = 0x03,
+ AUDIO_AD_CTRL_HILO_SCALING = 0x04,
+ AUDIO_AD_CTRL_UNDERFLOW = 0x05,
+ AUDIO_AD_CTRL_OVERFLOW = 0x06,
+ AUDIO_AD_CTRL_DECODER_ERROR = 0x07,
+} audio_AC3_decoder_control_selector_t;
+
+/// A.17.13.3 WMA Decoder Control Selectors
+typedef enum
+{
+ AUDIO_WD_CTRL_UNDEF = 0x00,
+ AUDIO_WD_CTRL_UNDERFLOW = 0x01,
+ AUDIO_WD_CTRL_OVERFLOW = 0x02,
+ AUDIO_WD_CTRL_DECODER_ERROR = 0x03,
+} audio_WMA_decoder_control_selector_t;
+
+/// A.17.13.4 DTS Decoder Control Selectors
+typedef enum
+{
+ AUDIO_DD_CTRL_UNDEF = 0x00,
+ AUDIO_DD_CTRL_UNDERFLOW = 0x01,
+ AUDIO_DD_CTRL_OVERFLOW = 0x02,
+ AUDIO_DD_CTRL_DECODER_ERROR = 0x03,
+} audio_DTS_decoder_control_selector_t;
+
+/// A.17.14 Endpoint Control Selectors
+typedef enum
+{
+ AUDIO_EP_CTRL_UNDEF = 0x00,
+ AUDIO_EP_CTRL_PITCH = 0x01,
+ AUDIO_EP_CTRL_DATA_OVERRUN = 0x02,
+ AUDIO_EP_CTRL_DATA_UNDERRUN = 0x03,
+} audio_EP_control_selector_t;
+
+/// Terminal Types
+
+/// 2.1 - Audio Class-Terminal Types UAC2
+typedef enum
+{
+ AUDIO_TERM_TYPE_USB_UNDEFINED = 0x0100,
+ AUDIO_TERM_TYPE_USB_STREAMING = 0x0101,
+ AUDIO_TERM_TYPE_USB_VENDOR_SPEC = 0x01FF,
+} audio_terminal_type_t;
+
+/// 2.2 - Audio Class-Input Terminal Types UAC2
+typedef enum
+{
+ AUDIO_TERM_TYPE_IN_UNDEFINED = 0x0200,
+ AUDIO_TERM_TYPE_IN_GENERIC_MIC = 0x0201,
+ AUDIO_TERM_TYPE_IN_DESKTOP_MIC = 0x0202,
+ AUDIO_TERM_TYPE_IN_PERSONAL_MIC = 0x0203,
+ AUDIO_TERM_TYPE_IN_OMNI_MIC = 0x0204,
+ AUDIO_TERM_TYPE_IN_ARRAY_MIC = 0x0205,
+ AUDIO_TERM_TYPE_IN_PROC_ARRAY_MIC = 0x0206,
+} audio_terminal_input_type_t;
+
+/// 2.3 - Audio Class-Output Terminal Types UAC2
+typedef enum
+{
+ AUDIO_TERM_TYPE_OUT_UNDEFINED = 0x0300,
+ AUDIO_TERM_TYPE_OUT_GENERIC_SPEAKER = 0x0301,
+ AUDIO_TERM_TYPE_OUT_HEADPHONES = 0x0302,
+ AUDIO_TERM_TYPE_OUT_HEAD_MNT_DISP_AUIDO = 0x0303,
+ AUDIO_TERM_TYPE_OUT_DESKTOP_SPEAKER = 0x0304,
+ AUDIO_TERM_TYPE_OUT_ROOM_SPEAKER = 0x0305,
+ AUDIO_TERM_TYPE_OUT_COMMUNICATION_SPEAKER = 0x0306,
+ AUDIO_TERM_TYPE_OUT_LOW_FRQ_EFFECTS_SPEAKER = 0x0307,
+} audio_terminal_output_type_t;
+
+/// Rest is yet to be implemented
+
+/// Additional Audio Device Class Codes - Source: Audio Data Formats
+
+/// A.1 - Audio Class-Format Type Codes UAC2
+typedef enum
+{
+ AUDIO_FORMAT_TYPE_UNDEFINED = 0x00,
+ AUDIO_FORMAT_TYPE_I = 0x01,
+ AUDIO_FORMAT_TYPE_II = 0x02,
+ AUDIO_FORMAT_TYPE_III = 0x03,
+ AUDIO_FORMAT_TYPE_IV = 0x04,
+ AUDIO_EXT_FORMAT_TYPE_I = 0x81,
+ AUDIO_EXT_FORMAT_TYPE_II = 0x82,
+ AUDIO_EXT_FORMAT_TYPE_III = 0x83,
+} audio_format_type_t;
+
+// A.2.1 - Audio Class-Audio Data Format Type I UAC2
+typedef enum
+{
+ AUDIO_DATA_FORMAT_TYPE_I_PCM = (uint32_t) (1 << 0),
+ AUDIO_DATA_FORMAT_TYPE_I_PCM8 = (uint32_t) (1 << 1),
+ AUDIO_DATA_FORMAT_TYPE_I_IEEE_FLOAT = (uint32_t) (1 << 2),
+ AUDIO_DATA_FORMAT_TYPE_I_ALAW = (uint32_t) (1 << 3),
+ AUDIO_DATA_FORMAT_TYPE_I_MULAW = (uint32_t) (1 << 4),
+ AUDIO_DATA_FORMAT_TYPE_I_RAW_DATA = 0x80000000,
+} audio_data_format_type_I_t;
+
+/// All remaining definitions are taken from the descriptor descriptions in the UAC2 main specification
+
+/// Isochronous End Point Attributes
+typedef enum
+{
+ TUSB_ISO_EP_ATT_NO_SYNC = 0x00,
+ TUSB_ISO_EP_ATT_ASYNCHRONOUS = 0x04,
+ TUSB_ISO_EP_ATT_ADAPTIVE = 0x08,
+ TUSB_ISO_EP_ATT_SYNCHRONOUS = 0x0C,
+ TUSB_ISO_EP_ATT_DATA = 0x00, ///< Data End Point
+ TUSB_ISO_EP_ATT_EXPLICIT_FB = 0x10, ///< Feedback End Point
+ TUSB_ISO_EP_ATT_IMPLICIT_FB = 0x20, ///< Data endpoint that also serves as an implicit feedback
+} tusb_iso_ep_attribute_t;
+
+/// Audio Class-Control Values UAC2
+typedef enum
+{
+ AUDIO_CTRL_NONE = 0x00, ///< No Host access
+ AUDIO_CTRL_R = 0x01, ///< Host read access only
+ AUDIO_CTRL_RW = 0x03, ///< Host read write access
+} audio_control_t;
+
+/// Audio Class-Specific AC Interface Descriptor Controls UAC2
+typedef enum
+{
+ AUDIO_CS_AS_INTERFACE_CTRL_LATENCY_POS = 0,
+} audio_cs_ac_interface_control_pos_t;
+
+/// Audio Class-Specific AS Interface Descriptor Controls UAC2
+typedef enum
+{
+ AUDIO_CS_AS_INTERFACE_CTRL_ACTIVE_ALT_SET_POS = 0,
+ AUDIO_CS_AS_INTERFACE_CTRL_VALID_ALT_SET_POS = 2,
+} audio_cs_as_interface_control_pos_t;
+
+/// Audio Class-Specific AS Isochronous Data EP Attributes UAC2
+typedef enum
+{
+ AUDIO_CS_AS_ISO_DATA_EP_ATT_MAX_PACKETS_ONLY = 0x80,
+ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK = 0x00,
+} audio_cs_as_iso_data_ep_attribute_t;
+
+/// Audio Class-Specific AS Isochronous Data EP Controls UAC2
+typedef enum
+{
+ AUDIO_CS_AS_ISO_DATA_EP_CTRL_PITCH_POS = 0,
+ AUDIO_CS_AS_ISO_DATA_EP_CTRL_DATA_OVERRUN_POS = 2,
+ AUDIO_CS_AS_ISO_DATA_EP_CTRL_DATA_UNDERRUN_POS = 4,
+} audio_cs_as_iso_data_ep_control_pos_t;
+
+/// Audio Class-Specific AS Isochronous Data EP Lock Delay Units UAC2
+typedef enum
+{
+ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED = 0x00,
+ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_MILLISEC = 0x01,
+ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_PCM_SAMPLES = 0x02,
+} audio_cs_as_iso_data_ep_lock_delay_unit_t;
+
+/// Audio Class-Clock Source Attributes UAC2
+typedef enum
+{
+ AUDIO_CLOCK_SOURCE_ATT_EXT_CLK = 0x00,
+ AUDIO_CLOCK_SOURCE_ATT_INT_FIX_CLK = 0x01,
+ AUDIO_CLOCK_SOURCE_ATT_INT_VAR_CLK = 0x02,
+ AUDIO_CLOCK_SOURCE_ATT_INT_PRO_CLK = 0x03,
+ AUDIO_CLOCK_SOURCE_ATT_CLK_SYC_SOF = 0x04,
+} audio_clock_source_attribute_t;
+
+/// Audio Class-Clock Source Controls UAC2
+typedef enum
+{
+ AUDIO_CLOCK_SOURCE_CTRL_CLK_FRQ_POS = 0,
+ AUDIO_CLOCK_SOURCE_CTRL_CLK_VAL_POS = 2,
+} audio_clock_source_control_pos_t;
+
+/// Audio Class-Clock Selector Controls UAC2
+typedef enum
+{
+ AUDIO_CLOCK_SELECTOR_CTRL_POS = 0,
+} audio_clock_selector_control_pos_t;
+
+/// Audio Class-Clock Multiplier Controls UAC2
+typedef enum
+{
+ AUDIO_CLOCK_MULTIPLIER_CTRL_NUMERATOR_POS = 0,
+ AUDIO_CLOCK_MULTIPLIER_CTRL_DENOMINATOR_POS = 2,
+} audio_clock_multiplier_control_pos_t;
+
+/// Audio Class-Input Terminal Controls UAC2
+typedef enum
+{
+ AUDIO_IN_TERM_CTRL_CPY_PROT_POS = 0,
+ AUDIO_IN_TERM_CTRL_CONNECTOR_POS = 2,
+ AUDIO_IN_TERM_CTRL_OVERLOAD_POS = 4,
+ AUDIO_IN_TERM_CTRL_CLUSTER_POS = 6,
+ AUDIO_IN_TERM_CTRL_UNDERFLOW_POS = 8,
+ AUDIO_IN_TERM_CTRL_OVERFLOW_POS = 10,
+} audio_terminal_input_control_pos_t;
+
+/// Audio Class-Output Terminal Controls UAC2
+typedef enum
+{
+ AUDIO_OUT_TERM_CTRL_CPY_PROT_POS = 0,
+ AUDIO_OUT_TERM_CTRL_CONNECTOR_POS = 2,
+ AUDIO_OUT_TERM_CTRL_OVERLOAD_POS = 4,
+ AUDIO_OUT_TERM_CTRL_UNDERFLOW_POS = 6,
+ AUDIO_OUT_TERM_CTRL_OVERFLOW_POS = 8,
+} audio_terminal_output_control_pos_t;
+
+/// Audio Class-Feature Unit Controls UAC2
+typedef enum
+{
+ AUDIO_FEATURE_UNIT_CTRL_MUTE_POS = 0,
+ AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS = 2,
+ AUDIO_FEATURE_UNIT_CTRL_BASS_POS = 4,
+ AUDIO_FEATURE_UNIT_CTRL_MID_POS = 6,
+ AUDIO_FEATURE_UNIT_CTRL_TREBLE_POS = 8,
+ AUDIO_FEATURE_UNIT_CTRL_GRAPHIC_EQU_POS = 10,
+ AUDIO_FEATURE_UNIT_CTRL_AGC_POS = 12,
+ AUDIO_FEATURE_UNIT_CTRL_DELAY_POS = 14,
+ AUDIO_FEATURE_UNIT_CTRL_BASS_BOOST_POS = 16,
+ AUDIO_FEATURE_UNIT_CTRL_LOUDNESS_POS = 18,
+ AUDIO_FEATURE_UNIT_CTRL_INPUT_GAIN_POS = 20,
+ AUDIO_FEATURE_UNIT_CTRL_INPUT_GAIN_PAD_POS = 22,
+ AUDIO_FEATURE_UNIT_CTRL_PHASE_INV_POS = 24,
+ AUDIO_FEATURE_UNIT_CTRL_UNDERFLOW_POS = 26,
+ AUDIO_FEATURE_UNIT_CTRL_OVERFLOW_POS = 28,
+} audio_feature_unit_control_pos_t;
+
+/// Audio Class-Audio Channel Configuration UAC2
+typedef enum
+{
+ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED = 0x00000000,
+ AUDIO_CHANNEL_CONFIG_FRONT_LEFT = 0x00000001,
+ AUDIO_CHANNEL_CONFIG_FRONT_RIGHT = 0x00000002,
+ AUDIO_CHANNEL_CONFIG_FRONT_CENTER = 0x00000004,
+ AUDIO_CHANNEL_CONFIG_LOW_FRQ_EFFECTS = 0x00000008,
+ AUDIO_CHANNEL_CONFIG_BACK_LEFT = 0x00000010,
+ AUDIO_CHANNEL_CONFIG_BACK_RIGHT = 0x00000020,
+ AUDIO_CHANNEL_CONFIG_FRONT_LEFT_OF_CENTER = 0x00000040,
+ AUDIO_CHANNEL_CONFIG_FRONT_RIGHT_OF_CENTER = 0x00000080,
+ AUDIO_CHANNEL_CONFIG_BACK_CENTER = 0x00000100,
+ AUDIO_CHANNEL_CONFIG_SIDE_LEFT = 0x00000200,
+ AUDIO_CHANNEL_CONFIG_SIDE_RIGHT = 0x00000400,
+ AUDIO_CHANNEL_CONFIG_TOP_CENTER = 0x00000800,
+ AUDIO_CHANNEL_CONFIG_TOP_FRONT_LEFT = 0x00001000,
+ AUDIO_CHANNEL_CONFIG_TOP_FRONT_CENTER = 0x00002000,
+ AUDIO_CHANNEL_CONFIG_TOP_FRONT_RIGHT = 0x00004000,
+ AUDIO_CHANNEL_CONFIG_TOP_BACK_LEFT = 0x00008000,
+ AUDIO_CHANNEL_CONFIG_TOP_BACK_CENTER = 0x00010000,
+ AUDIO_CHANNEL_CONFIG_TOP_BACK_RIGHT = 0x00020000,
+ AUDIO_CHANNEL_CONFIG_TOP_FRONT_LEFT_OF_CENTER = 0x00040000,
+ AUDIO_CHANNEL_CONFIG_TOP_FRONT_RIGHT_OF_CENTER = 0x00080000,
+ AUDIO_CHANNEL_CONFIG_LEFT_LOW_FRQ_EFFECTS = 0x00100000,
+ AUDIO_CHANNEL_CONFIG_RIGHT_LOW_FRQ_EFFECTS = 0x00200000,
+ AUDIO_CHANNEL_CONFIG_TOP_SIDE_LEFT = 0x00400000,
+ AUDIO_CHANNEL_CONFIG_TOP_SIDE_RIGHT = 0x00800000,
+ AUDIO_CHANNEL_CONFIG_BOTTOM_CENTER = 0x01000000,
+ AUDIO_CHANNEL_CONFIG_BACK_LEFT_OF_CENTER = 0x02000000,
+ AUDIO_CHANNEL_CONFIG_BACK_RIGHT_OF_CENTER = 0x04000000,
+ AUDIO_CHANNEL_CONFIG_RAW_DATA = 0x80000000,
+} audio_channel_config_t;
+
+/// AUDIO Channel Cluster Descriptor (4.1)
+typedef struct TU_ATTR_PACKED {
+ uint8_t bNrChannels; ///< Number of channels currently connected.
+ audio_channel_config_t bmChannelConfig; ///< Bitmap according to 'audio_channel_config_t' with a 1 set if channel is connected and 0 else. In case channels are non-predefined ignore them here (see UAC2 specification 4.1 Audio Channel Cluster Descriptor.
+ uint8_t iChannelNames; ///< Index of a string descriptor, describing the name of the first inserted channel with a non-predefined spatial location.
+} audio_desc_channel_cluster_t;
+
+/// AUDIO Class-Specific AC Interface Header Descriptor (4.7.2)
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of this descriptor in bytes: 9.
+ uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE.
+ uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AC_INTERFACE_HEADER.
+ uint16_t bcdADC ; ///< Audio Device Class Specification Release Number in Binary-Coded Decimal. Value: U16_TO_U8S_LE(0x0200).
+ uint8_t bCategory ; ///< Constant, indicating the primary use of this audio function, as intended by the manufacturer. See: audio_function_t.
+ uint16_t wTotalLength ; ///< Total number of bytes returned for the class-specific AudioControl interface descriptor. Includes the combined length of this descriptor header and all Clock Source, Unit and Terminal descriptors.
+ uint8_t bmControls ; ///< See: audio_cs_ac_interface_control_pos_t.
+} audio_desc_cs_ac_interface_t;
+
+/// AUDIO Clock Source Descriptor (4.7.2.1)
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of this descriptor in bytes: 8.
+ uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE.
+ uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AC_INTERFACE_CLOCK_SOURCE.
+ uint8_t bClockID ; ///< Constant uniquely identifying the Clock Source Entity within the audio function. This value is used in all requests to address this Entity.
+ uint8_t bmAttributes ; ///< See: audio_clock_source_attribute_t.
+ uint8_t bmControls ; ///< See: audio_clock_source_control_pos_t.
+ uint8_t bAssocTerminal ; ///< Terminal ID of the Terminal that is associated with this Clock Source.
+ uint8_t iClockSource ; ///< Index of a string descriptor, describing the Clock Source Entity.
+} audio_desc_clock_source_t;
+
+/// AUDIO Clock Selector Descriptor (4.7.2.2) for ONE pin
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of this descriptor, in bytes: 7+p.
+ uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE.
+ uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AC_INTERFACE_CLOCK_SELECTOR.
+ uint8_t bClockID ; ///< Constant uniquely identifying the Clock Selector Entity within the audio function. This value is used in all requests to address this Entity.
+ uint8_t bNrInPins ; ///< Number of Input Pins of this Unit: p = 1 thus bNrInPins = 1.
+ uint8_t baCSourceID ; ///< ID of the Clock Entity to which the first Clock Input Pin of this Clock Selector Entity is connected..
+ uint8_t bmControls ; ///< See: audio_clock_selector_control_pos_t.
+ uint8_t iClockSource ; ///< Index of a string descriptor, describing the Clock Selector Entity.
+} audio_desc_clock_selector_t;
+
+/// AUDIO Clock Selector Descriptor (4.7.2.2) for multiple pins
+#define audio_desc_clock_selector_n_t(source_num) \
+ struct TU_ATTR_PACKED { \
+ uint8_t bLength ; \
+ uint8_t bDescriptorType ; \
+ uint8_t bDescriptorSubType ; \
+ uint8_t bClockID ; \
+ uint8_t bNrInPins ; \
+ struct TU_ATTR_PACKED { \
+ uint8_t baSourceID ; \
+ } sourceID[source_num] ; \
+ uint8_t bmControls ; \
+ uint8_t iClockSource ; \
+}
+
+/// AUDIO Clock Multiplier Descriptor (4.7.2.3)
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of this descriptor, in bytes: 7.
+ uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE.
+ uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AC_INTERFACE_CLOCK_MULTIPLIER.
+ uint8_t bClockID ; ///< Constant uniquely identifying the Clock Multiplier Entity within the audio function. This value is used in all requests to address this Entity.
+ uint8_t bCSourceID ; ///< ID of the Clock Entity to which the last Clock Input Pin of this Clock Selector Entity is connected.
+ uint8_t bmControls ; ///< See: audio_clock_multiplier_control_pos_t.
+ uint8_t iClockSource ; ///< Index of a string descriptor, describing the Clock Multiplier Entity.
+} audio_desc_clock_multiplier_t;
+
+/// AUDIO Input Terminal Descriptor(4.7.2.4)
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of this descriptor, in bytes: 17.
+ uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE.
+ uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AC_INTERFACE_INPUT_TERMINAL.
+ uint16_t wTerminalType ; ///< Constant characterizing the type of Terminal. See: audio_terminal_type_t for USB streaming and audio_terminal_input_type_t for other input types.
+ uint8_t bAssocTerminal ; ///< ID of the Output Terminal to which this Input Terminal is associated.
+ uint8_t bCSourceID ; ///< ID of the Clock Entity to which this Input Terminal is connected.
+ uint8_t bNrChannels ; ///< Number of logical output channels in the Terminal’s output audio channel cluster.
+ uint32_t bmChannelConfig ; ///< Describes the spatial location of the logical channels. See:audio_channel_config_t.
+ uint16_t bmControls ; ///< See: audio_terminal_input_control_pos_t.
+ uint8_t iTerminal ; ///< Index of a string descriptor, describing the Input Terminal.
+} audio_desc_input_terminal_t;
+
+/// AUDIO Output Terminal Descriptor(4.7.2.5)
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of this descriptor, in bytes: 12.
+ uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE.
+ uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AC_INTERFACE_OUTPUT_TERMINAL.
+ uint8_t bTerminalID ; ///< Constant uniquely identifying the Terminal within the audio function. This value is used in all requests to address this Terminal.
+ uint16_t wTerminalType ; ///< Constant characterizing the type of Terminal. See: audio_terminal_type_t for USB streaming and audio_terminal_output_type_t for other output types.
+ uint8_t bAssocTerminal ; ///< Constant, identifying the Input Terminal to which this Output Terminal is associated.
+ uint8_t bSourceID ; ///< ID of the Unit or Terminal to which this Terminal is connected.
+ uint8_t bCSourceID ; ///< ID of the Clock Entity to which this Output Terminal is connected.
+ uint16_t bmControls ; ///< See: audio_terminal_output_type_t.
+ uint8_t iTerminal ; ///< Index of a string descriptor, describing the Output Terminal.
+} audio_desc_output_terminal_t;
+
+/// AUDIO Feature Unit Descriptor(4.7.2.8) for ONE channel
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of this descriptor, in bytes: 14.
+ uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE.
+ uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AC_INTERFACE_FEATURE_UNIT.
+ uint8_t bUnitID ; ///< Constant uniquely identifying the Unit within the audio function. This value is used in all requests to address this Unit.
+ uint8_t bSourceID ; ///< ID of the Unit or Terminal to which this Feature Unit is connected.
+ struct TU_ATTR_PACKED {
+ uint32_t bmaControls ; ///< See: audio_feature_unit_control_pos_t. Controls0 is master channel 0 (always present) and Controls1 is logical channel 1.
+ } controls[2] ;
+ uint8_t iTerminal ; ///< Index of a string descriptor, describing this Feature Unit.
+} audio_desc_feature_unit_t;
+
+/// AUDIO Feature Unit Descriptor(4.7.2.8) for multiple channels
+#define audio_desc_feature_unit_n_t(ch_num)\
+ struct TU_ATTR_PACKED { \
+ uint8_t bLength ; /* 6+(ch_num+1)*4 */\
+ uint8_t bDescriptorType ; \
+ uint8_t bDescriptorSubType ; \
+ uint8_t bUnitID ; \
+ uint8_t bSourceID ; \
+ struct TU_ATTR_PACKED { \
+ uint32_t bmaControls ; \
+ } controls[ch_num+1] ; \
+ uint8_t iTerminal ; \
+}
+
+/// AUDIO Class-Specific AS Interface Descriptor(4.9.2)
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of this descriptor, in bytes: 16.
+ uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE.
+ uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AS_INTERFACE_AS_GENERAL.
+ uint8_t bTerminalLink ; ///< The Terminal ID of the Terminal to which this interface is connected.
+ uint8_t bmControls ; ///< See: audio_cs_as_interface_control_pos_t.
+ uint8_t bFormatType ; ///< Constant identifying the Format Type the AudioStreaming interface is using. See: audio_format_type_t.
+ uint32_t bmFormats ; ///< The Audio Data Format(s) that can be used to communicate with this interface.See: audio_data_format_type_I_t.
+ uint8_t bNrChannels ; ///< Number of physical channels in the AS Interface audio channel cluster.
+ uint32_t bmChannelConfig ; ///< Describes the spatial location of the physical channels. See: audio_channel_config_t.
+ uint8_t iChannelNames ; ///< Index of a string descriptor, describing the name of the first physical channel.
+} audio_desc_cs_as_interface_t;
+
+/// AUDIO Type I Format Type Descriptor(2.3.1.6 - Audio Formats)
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of this descriptor, in bytes: 6.
+ uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE.
+ uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AS_INTERFACE_FORMAT_TYPE.
+ uint8_t bFormatType ; ///< Constant identifying the Format Type the AudioStreaming interface is using. Value: AUDIO_FORMAT_TYPE_I.
+ uint8_t bSubslotSize ; ///< The number of bytes occupied by one audio subslot. Can be 1, 2, 3 or 4.
+ uint8_t bBitResolution ; ///< The number of effectively used bits from the available bits in an audio subslot.
+} audio_desc_type_I_format_t;
+
+/// AUDIO Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2)
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of this descriptor, in bytes: 8.
+ uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_ENDPOINT.
+ uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_EP_SUBTYPE_GENERAL.
+ uint8_t bmAttributes ; ///< See: audio_cs_as_iso_data_ep_attribute_t.
+ uint8_t bmControls ; ///< See: audio_cs_as_iso_data_ep_control_pos_t.
+ uint8_t bLockDelayUnits ; ///< Indicates the units used for the wLockDelay field. See: audio_cs_as_iso_data_ep_lock_delay_unit_t.
+ uint16_t wLockDelay ; ///< Indicates the time it takes this endpoint to reliably lock its internal clock recovery circuitry. Units used depend on the value of the bLockDelayUnits field.
+} audio_desc_cs_as_iso_data_ep_t;
+
+// 5.2.2 Control Request Layout
+typedef struct TU_ATTR_PACKED
+{
+ union
+ {
+ struct TU_ATTR_PACKED
+ {
+ uint8_t recipient : 5; ///< Recipient type tusb_request_recipient_t.
+ uint8_t type : 2; ///< Request type tusb_request_type_t.
+ uint8_t direction : 1; ///< Direction type. tusb_dir_t
+ } bmRequestType_bit;
+
+ uint8_t bmRequestType;
+ };
+
+ uint8_t bRequest; ///< Request type audio_cs_req_t
+ uint8_t bChannelNumber;
+ uint8_t bControlSelector;
+ union
+ {
+ uint8_t bInterface;
+ uint8_t bEndpoint;
+ };
+ uint8_t bEntityID;
+ uint16_t wLength;
+} audio_control_request_t;
+
+//// 5.2.3 Control Request Parameter Block Layout
+
+// 5.2.3.1 1-byte Control CUR Parameter Block
+typedef struct TU_ATTR_PACKED
+{
+ int8_t bCur ; ///< The setting for the CUR attribute of the addressed Control
+} audio_control_cur_1_t;
+
+// 5.2.3.2 2-byte Control CUR Parameter Block
+typedef struct TU_ATTR_PACKED
+{
+ int16_t bCur ; ///< The setting for the CUR attribute of the addressed Control
+} audio_control_cur_2_t;
+
+// 5.2.3.3 4-byte Control CUR Parameter Block
+typedef struct TU_ATTR_PACKED
+{
+ int32_t bCur ; ///< The setting for the CUR attribute of the addressed Control
+} audio_control_cur_4_t;
+
+// Use the following ONLY for RECEIVED data - compiler does not know how many subranges are defined! Use the one below for predefined lengths - or if you know what you are doing do what you like
+// 5.2.3.1 1-byte Control RANGE Parameter Block
+typedef struct TU_ATTR_PACKED {
+ uint16_t wNumSubRanges;
+ struct TU_ATTR_PACKED {
+ int8_t bMin ; /*The setting for the MIN attribute of the nth subrange of the addressed Control*/
+ int8_t bMax ; /*The setting for the MAX attribute of the nth subrange of the addressed Control*/
+ uint8_t bRes ; /*The setting for the RES attribute of the nth subrange of the addressed Control*/
+ } subrange[] ;
+} audio_control_range_1_t;
+
+// 5.2.3.2 2-byte Control RANGE Parameter Block
+typedef struct TU_ATTR_PACKED {
+ uint16_t wNumSubRanges;
+ struct TU_ATTR_PACKED {
+ int16_t bMin ; /*The setting for the MIN attribute of the nth subrange of the addressed Control*/
+ int16_t bMax ; /*The setting for the MAX attribute of the nth subrange of the addressed Control*/
+ uint16_t bRes ; /*The setting for the RES attribute of the nth subrange of the addressed Control*/
+ } subrange[] ;
+} audio_control_range_2_t;
+
+// 5.2.3.3 4-byte Control RANGE Parameter Block
+typedef struct TU_ATTR_PACKED {
+ uint16_t wNumSubRanges;
+ struct TU_ATTR_PACKED {
+ int32_t bMin ; /*The setting for the MIN attribute of the nth subrange of the addressed Control*/
+ int32_t bMax ; /*The setting for the MAX attribute of the nth subrange of the addressed Control*/
+ uint32_t bRes ; /*The setting for the RES attribute of the nth subrange of the addressed Control*/
+ } subrange[] ;
+} audio_control_range_4_t;
+
+// 5.2.3.1 1-byte Control RANGE Parameter Block
+#define audio_control_range_1_n_t(numSubRanges) \
+ struct TU_ATTR_PACKED { \
+ uint16_t wNumSubRanges; \
+ struct TU_ATTR_PACKED { \
+ int8_t bMin ; /*The setting for the MIN attribute of the nth subrange of the addressed Control*/\
+ int8_t bMax ; /*The setting for the MAX attribute of the nth subrange of the addressed Control*/\
+ uint8_t bRes ; /*The setting for the RES attribute of the nth subrange of the addressed Control*/\
+ } subrange[numSubRanges] ; \
+}
+
+/// 5.2.3.2 2-byte Control RANGE Parameter Block
+#define audio_control_range_2_n_t(numSubRanges) \
+ struct TU_ATTR_PACKED { \
+ uint16_t wNumSubRanges; \
+ struct TU_ATTR_PACKED { \
+ int16_t bMin ; /*The setting for the MIN attribute of the nth subrange of the addressed Control*/\
+ int16_t bMax ; /*The setting for the MAX attribute of the nth subrange of the addressed Control*/\
+ uint16_t bRes ; /*The setting for the RES attribute of the nth subrange of the addressed Control*/\
+ } subrange[numSubRanges]; \
+}
+
+// 5.2.3.3 4-byte Control RANGE Parameter Block
+#define audio_control_range_4_n_t(numSubRanges) \
+ struct TU_ATTR_PACKED { \
+ uint16_t wNumSubRanges; \
+ struct TU_ATTR_PACKED { \
+ int32_t bMin ; /*The setting for the MIN attribute of the nth subrange of the addressed Control*/\
+ int32_t bMax ; /*The setting for the MAX attribute of the nth subrange of the addressed Control*/\
+ uint32_t bRes ; /*The setting for the RES attribute of the nth subrange of the addressed Control*/\
+ } subrange[numSubRanges]; \
+}
+
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
+/** @} */
diff --git a/tinyusb/src/class/audio/audio_device.c b/tinyusb/src/class/audio/audio_device.c
new file mode 100755
index 00000000..ff4e312a
--- /dev/null
+++ b/tinyusb/src/class/audio/audio_device.c
@@ -0,0 +1,2272 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Reinhard Panhuber, Jerzy Kasenberg
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+/*
+ * This driver supports at most one out EP, one in EP, one control EP, and one feedback EP and one alternative interface other than zero. Hence, only one input terminal and one output terminal are support, if you need more adjust the driver!
+ * It supports multiple TX and RX channels.
+ *
+ * In case you need more alternate interfaces, you need to define additional defines for this specific alternate interface. Just define them and set them in the set_interface function.
+ *
+ * There are three data flow structures currently implemented, where at least one SW-FIFO is used to decouple the asynchronous processes MCU vs. host
+ *
+ * 1. Input data -> SW-FIFO -> MCU USB
+ *
+ * The most easiest version, available in case the target MCU can handle the software FIFO (SW-FIFO) and if it is implemented in the device driver (if yes then dcd_edpt_xfer_fifo() is available)
+ *
+ * 2. Input data -> SW-FIFO -> Linear buffer -> MCU USB
+ *
+ * In case the target MCU can not handle a SW-FIFO, a linear buffer is used. This uses the default function dcd_edpt_xfer(). In this case more memory is required.
+ *
+ * 3. (Input data 1 | Input data 2 | ... | Input data N) -> (SW-FIFO 1 | SW-FIFO 2 | ... | SW-FIFO N) -> Linear buffer -> MCU USB
+ *
+ * This case is used if you have more channels which need to be combined into one stream. Every channel has its own SW-FIFO. All data is encoded into an Linear buffer.
+ *
+ * The same holds in the RX case.
+ *
+ * */
+
+#include "tusb_option.h"
+
+#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_AUDIO)
+
+//--------------------------------------------------------------------+
+// INCLUDE
+//--------------------------------------------------------------------+
+#include "device/usbd.h"
+#include "device/usbd_pvt.h"
+
+#include "audio_device.h"
+
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF
+//--------------------------------------------------------------------+
+
+// 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 synopsys use non-linear buffer for now
+// Synopsys detection copied from dcd_synopsys.c (refactor later on)
+#if defined (STM32F105x8) || defined (STM32F105xB) || defined (STM32F105xC) || \
+ defined (STM32F107xB) || defined (STM32F107xC)
+#define STM32F1_SYNOPSYS
+#endif
+
+#if defined (STM32L475xx) || defined (STM32L476xx) || \
+ defined (STM32L485xx) || defined (STM32L486xx) || defined (STM32L496xx) || \
+ defined (STM32L4R5xx) || defined (STM32L4R7xx) || defined (STM32L4R9xx) || \
+ defined (STM32L4S5xx) || defined (STM32L4S7xx) || defined (STM32L4S9xx)
+#define STM32L4_SYNOPSYS
+#endif
+
+#if (CFG_TUSB_MCU == OPT_MCU_STM32F1 && defined(STM32F1_SYNOPSYS)) || \
+ CFG_TUSB_MCU == OPT_MCU_STM32F2 || \
+ CFG_TUSB_MCU == OPT_MCU_STM32F4 || \
+ CFG_TUSB_MCU == OPT_MCU_STM32F7 || \
+ CFG_TUSB_MCU == OPT_MCU_STM32H7 || \
+ (CFG_TUSB_MCU == OPT_MCU_STM32L4 && defined(STM32L4_SYNOPSYS)) || \
+ CFG_TUSB_MCU == OPT_MCU_RX63X || \
+ CFG_TUSB_MCU == OPT_MCU_RX65X || \
+ CFG_TUSB_MCU == OPT_MCU_RX72N || \
+ CFG_TUSB_MCU == OPT_MCU_GD32VF103
+#define USE_LINEAR_BUFFER 0
+#else
+#define USE_LINEAR_BUFFER 1
+#endif
+
+// Declaration of buffers
+
+// Check for maximum supported numbers
+#if CFG_TUD_AUDIO > 3
+#error Maximum number of audio functions restricted to three!
+#endif
+
+// EP IN software buffers and mutexes
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_ENCODING
+#if CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ > 0
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t audio_ep_in_sw_buf_1[CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ];
+#if CFG_FIFO_MUTEX
+osal_mutex_def_t ep_in_ff_mutex_wr_1; // No need for read mutex as only USB driver reads from FIFO
+#endif
+#endif // CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ > 0
+#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_IN_SW_BUF_SZ > 0
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t audio_ep_in_sw_buf_2[CFG_TUD_AUDIO_FUNC_2_EP_IN_SW_BUF_SZ];
+#if CFG_FIFO_MUTEX
+osal_mutex_def_t ep_in_ff_mutex_wr_2; // No need for read mutex as only USB driver reads from FIFO
+#endif
+#endif // CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_IN_SW_BUF_SZ > 0
+#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_IN_SW_BUF_SZ > 0
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t audio_ep_in_sw_buf_3[CFG_TUD_AUDIO_FUNC_3_EP_IN_SW_BUF_SZ];
+#if CFG_FIFO_MUTEX
+osal_mutex_def_t ep_in_ff_mutex_wr_3; // No need for read mutex as only USB driver reads from FIFO
+#endif
+#endif // CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_IN_SW_BUF_SZ > 0
+#endif // CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_ENCODING
+
+// 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
+// - the software encoding is used - in this case the linear buffers serve as a target memory where logical channels are encoded into
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && (USE_LINEAR_BUFFER || CFG_TUD_AUDIO_ENABLE_ENCODING)
+#if CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX > 0
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t lin_buf_in_1[CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX];
+#endif
+#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_IN_SZ_MAX > 0
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t lin_buf_in_2[CFG_TUD_AUDIO_FUNC_2_EP_IN_SZ_MAX];
+#endif
+#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_IN_SZ_MAX > 0
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t lin_buf_in_3[CFG_TUD_AUDIO_FUNC_3_EP_IN_SZ_MAX];
+#endif
+#endif // CFG_TUD_AUDIO_ENABLE_EP_IN && (USE_LINEAR_BUFFER || CFG_TUD_AUDIO_ENABLE_DECODING)
+
+// EP OUT software buffers and mutexes
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && !CFG_TUD_AUDIO_ENABLE_DECODING
+#if CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ > 0
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t audio_ep_out_sw_buf_1[CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ];
+#if CFG_FIFO_MUTEX
+osal_mutex_def_t ep_out_ff_mutex_rd_1; // No need for write mutex as only USB driver writes into FIFO
+#endif
+#endif // CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ > 0
+#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_OUT_SW_BUF_SZ > 0
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t audio_ep_out_sw_buf_2[CFG_TUD_AUDIO_FUNC_2_EP_OUT_SW_BUF_SZ];
+#if CFG_FIFO_MUTEX
+osal_mutex_def_t ep_out_ff_mutex_rd_2; // No need for write mutex as only USB driver writes into FIFO
+#endif
+#endif // CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_OUT_SW_BUF_SZ > 0
+#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_OUT_SW_BUF_SZ > 0
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t audio_ep_out_sw_buf_3[CFG_TUD_AUDIO_FUNC_3_EP_OUT_SW_BUF_SZ];
+#if CFG_FIFO_MUTEX
+osal_mutex_def_t ep_out_ff_mutex_rd_3; // No need for write mutex as only USB driver writes into FIFO
+#endif
+#endif // CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_OUT_SW_BUF_SZ > 0
+#endif // CFG_TUD_AUDIO_ENABLE_EP_OUT && !CFG_TUD_AUDIO_ENABLE_DECODING
+
+// 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
+// - the software encoding is used - in this case the linear buffers serve as a target memory where logical channels are encoded into
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && (USE_LINEAR_BUFFER || CFG_TUD_AUDIO_ENABLE_DECODING)
+#if CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX > 0
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t lin_buf_out_1[CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX];
+#endif
+#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_OUT_SZ_MAX > 0
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t lin_buf_out_2[CFG_TUD_AUDIO_FUNC_2_EP_OUT_SZ_MAX];
+#endif
+#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_OUT_SZ_MAX > 0
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t lin_buf_out_3[CFG_TUD_AUDIO_FUNC_3_EP_OUT_SZ_MAX];
+#endif
+#endif // CFG_TUD_AUDIO_ENABLE_EP_OUT && (USE_LINEAR_BUFFER || CFG_TUD_AUDIO_ENABLE_DECODING)
+
+// Control buffers
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t ctrl_buf_1[CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ];
+#if CFG_TUD_AUDIO > 1
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t ctrl_buf_2[CFG_TUD_AUDIO_FUNC_2_CTRL_BUF_SZ];
+#endif
+#if CFG_TUD_AUDIO > 2
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t ctrl_buf_3[CFG_TUD_AUDIO_FUNC_3_CTRL_BUF_SZ];
+#endif
+
+// Active alternate setting of interfaces
+uint8_t alt_setting_1[CFG_TUD_AUDIO_FUNC_1_N_AS_INT];
+#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_N_AS_INT > 0
+uint8_t alt_setting_2[CFG_TUD_AUDIO_FUNC_2_N_AS_INT];
+#endif
+#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_N_AS_INT > 0
+uint8_t alt_setting_3[CFG_TUD_AUDIO_FUNC_3_N_AS_INT];
+#endif
+
+// Software encoding/decoding support FIFOs
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_ENCODING
+#if CFG_TUD_AUDIO_FUNC_1_TX_SUPP_SW_FIFO_SZ > 0
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t tx_supp_ff_buf_1[CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_1_TX_SUPP_SW_FIFO_SZ];
+tu_fifo_t tx_supp_ff_1[CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO];
+#if CFG_FIFO_MUTEX
+osal_mutex_def_t tx_supp_ff_mutex_wr_1[CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO]; // No need for read mutex as only USB driver reads from FIFO
+#endif
+#endif
+#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_TX_SUPP_SW_FIFO_SZ > 0
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t tx_supp_ff_buf_2[CFG_TUD_AUDIO_FUNC_2_N_TX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_2_TX_SUPP_SW_FIFO_SZ];
+tu_fifo_t tx_supp_ff_2[CFG_TUD_AUDIO_FUNC_2_N_TX_SUPP_SW_FIFO];
+#if CFG_FIFO_MUTEX
+osal_mutex_def_t tx_supp_ff_mutex_wr_2[CFG_TUD_AUDIO_FUNC_2_N_TX_SUPP_SW_FIFO]; // No need for read mutex as only USB driver reads from FIFO
+#endif
+#endif
+#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_TX_SUPP_SW_FIFO_SZ > 0
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t tx_supp_ff_buf_3[CFG_TUD_AUDIO_FUNC_3_N_TX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_3_TX_SUPP_SW_FIFO_SZ];
+tu_fifo_t tx_supp_ff_3[CFG_TUD_AUDIO_FUNC_3_N_TX_SUPP_SW_FIFO];
+#if CFG_FIFO_MUTEX
+osal_mutex_def_t tx_supp_ff_mutex_wr_3[CFG_TUD_AUDIO_FUNC_3_N_TX_SUPP_SW_FIFO]; // No need for read mutex as only USB driver reads from FIFO
+#endif
+#endif
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING
+#if CFG_TUD_AUDIO_FUNC_1_RX_SUPP_SW_FIFO_SZ > 0
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t rx_supp_ff_buf_1[CFG_TUD_AUDIO_FUNC_1_N_RX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_1_RX_SUPP_SW_FIFO_SZ];
+tu_fifo_t rx_supp_ff_1[CFG_TUD_AUDIO_FUNC_1_N_RX_SUPP_SW_FIFO];
+#if CFG_FIFO_MUTEX
+osal_mutex_def_t rx_supp_ff_mutex_rd_1[CFG_TUD_AUDIO_FUNC_1_N_RX_SUPP_SW_FIFO]; // No need for write mutex as only USB driver writes into FIFO
+#endif
+#endif
+#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_RX_SUPP_SW_FIFO_SZ > 0
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t rx_supp_ff_buf_2[CFG_TUD_AUDIO_FUNC_2_N_RX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_2_RX_SUPP_SW_FIFO_SZ];
+tu_fifo_t rx_supp_ff_2[CFG_TUD_AUDIO_FUNC_2_N_RX_SUPP_SW_FIFO];
+#if CFG_FIFO_MUTEX
+osal_mutex_def_t rx_supp_ff_mutex_rd_2[CFG_TUD_AUDIO_FUNC_2_N_RX_SUPP_SW_FIFO]; // No need for write mutex as only USB driver writes into FIFO
+#endif
+#endif
+#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_RX_SUPP_SW_FIFO_SZ > 0
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t rx_supp_ff_buf_3[CFG_TUD_AUDIO_FUNC_3_N_RX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_3_RX_SUPP_SW_FIFO_SZ];
+tu_fifo_t rx_supp_ff_3[CFG_TUD_AUDIO_FUNC_3_N_RX_SUPP_SW_FIFO];
+#if CFG_FIFO_MUTEX
+osal_mutex_def_t rx_supp_ff_mutex_rd_3[CFG_TUD_AUDIO_FUNC_3_N_RX_SUPP_SW_FIFO]; // No need for write mutex as only USB driver writes into FIFO
+#endif
+#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_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)
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT
+ uint8_t ep_out; // Incoming (into uC) audio data EP.
+ uint16_t ep_out_sz; // Current size of RX 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)
+
+#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+ uint8_t ep_fb; // Feedback EP.
+#endif
+
+#endif
+
+#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
+ uint8_t ep_int_ctr; // Audio control interrupt EP.
+#endif
+
+ /*------------- From this point, data is not cleared by bus reset -------------*/
+
+ uint16_t desc_length; // Length of audio function descriptor
+
+ // Buffer for control requests
+ uint8_t * ctrl_buf;
+ uint8_t ctrl_buf_sz;
+
+ // Current active alternate settings
+ uint8_t * alt_setting; // 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!
+
+ // EP Transfer buffers and FIFOs
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT
+#if !CFG_TUD_AUDIO_ENABLE_DECODING
+ tu_fifo_t ep_out_ff;
+#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_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_ENCODING
+ tu_fifo_t ep_in_ff;
+#endif
+
+ // Audio control interrupt buffer - no FIFO - 6 Bytes according to UAC 2 specification (p. 74)
+#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
+ CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t ep_int_ctr_buf[CFG_TUD_AUDIO_INT_CTR_EP_IN_SW_BUFFER_SIZE];
+#endif
+
+ // Decoding parameters - parameters are set when alternate AS interface is set by host
+ // Coding is currently only supported for EP. Software coding corresponding to AS interfaces without EPs are not supported currently.
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING
+ audio_format_type_t format_type_rx;
+ uint8_t n_channels_rx;
+
+#if CFG_TUD_AUDIO_ENABLE_TYPE_I_DECODING
+ audio_data_format_type_I_t format_type_I_rx;
+ uint8_t n_bytes_per_sampe_rx;
+ uint8_t n_channels_per_ff_rx;
+ uint8_t n_ff_used_rx;
+#endif
+#endif
+
+ // Encoding parameters - parameters are set when alternate AS interface is set by host
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_ENCODING
+ audio_format_type_t format_type_tx;
+ uint8_t n_channels_tx;
+
+#if CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING
+ audio_data_format_type_I_t format_type_I_tx;
+ uint8_t n_bytes_per_sampe_tx;
+ uint8_t n_channels_per_ff_tx;
+ uint8_t n_ff_used_tx;
+#endif
+#endif
+
+ // Support FIFOs for software encoding and decoding
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING
+ tu_fifo_t * rx_supp_ff;
+ uint8_t n_rx_supp_ff;
+ uint16_t rx_supp_ff_sz_max;
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_ENCODING
+ tu_fifo_t * tx_supp_ff;
+ uint8_t n_tx_supp_ff;
+ uint16_t tx_supp_ff_sz_max;
+#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 OR the support FIFOs are used
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && (USE_LINEAR_BUFFER || CFG_TUD_AUDIO_ENABLE_DECODING)
+ uint8_t * lin_buf_out;
+#define USE_LINEAR_BUFFER_RX 1
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && (USE_LINEAR_BUFFER || CFG_TUD_AUDIO_ENABLE_ENCODING)
+ uint8_t * lin_buf_in;
+#define USE_LINEAR_BUFFER_TX 1
+#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
+#endif
+
+#define ITF_MEM_RESET_SIZE offsetof(audiod_function_t, ctrl_buf)
+
+//--------------------------------------------------------------------+
+// INTERNAL OBJECT & FUNCTION DECLARATION
+//--------------------------------------------------------------------+
+CFG_TUSB_MEM_SECTION audiod_function_t _audiod_fct[CFG_TUD_AUDIO];
+
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT
+static bool audiod_rx_done_cb(uint8_t rhport, audiod_function_t* audio, uint16_t n_bytes_received);
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_DECODING && CFG_TUD_AUDIO_ENABLE_EP_OUT
+static bool audiod_decode_type_I_pcm(uint8_t rhport, audiod_function_t* audio, uint16_t n_bytes_received);
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_IN
+static bool audiod_tx_done_cb(uint8_t rhport, audiod_function_t* audio);
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_ENCODING && CFG_TUD_AUDIO_ENABLE_EP_IN
+static uint16_t audiod_encode_type_I_pcm(uint8_t rhport, audiod_function_t* audio);
+#endif
+
+static bool audiod_get_interface(uint8_t rhport, tusb_control_request_t const * p_request);
+static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const * p_request);
+
+static bool audiod_get_AS_interface_index_global(uint8_t itf, uint8_t *func_id, uint8_t *idxItf, uint8_t const **pp_desc_int);
+static bool audiod_get_AS_interface_index(uint8_t itf, audiod_function_t * audio, uint8_t *idxItf, uint8_t const **pp_desc_int);
+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);
+
+#if CFG_TUD_AUDIO_ENABLE_ENCODING || CFG_TUD_AUDIO_ENABLE_DECODING
+static void audiod_parse_for_AS_params(audiod_function_t* audio, uint8_t const * p_desc, uint8_t const * p_desc_end, uint8_t const as_itf);
+
+static inline uint8_t tu_desc_subtype(void const* desc)
+{
+ return ((uint8_t const*) desc)[2];
+}
+#endif
+
+bool tud_audio_n_mounted(uint8_t func_id)
+{
+ TU_VERIFY(func_id < CFG_TUD_AUDIO);
+ audiod_function_t* audio = &_audiod_fct[func_id];
+
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT
+ if (audio->ep_out == 0) return false;
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_IN
+ if (audio->ep_in == 0) return false;
+#endif
+
+#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
+ if (audio->ep_int_ctr == 0) return false;
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+ if (audio->ep_fb == 0) return false;
+#endif
+
+ return true;
+}
+
+//--------------------------------------------------------------------+
+// READ API
+//--------------------------------------------------------------------+
+
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && !CFG_TUD_AUDIO_ENABLE_DECODING
+
+uint16_t tud_audio_n_available(uint8_t func_id)
+{
+ TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL);
+ return tu_fifo_count(&_audiod_fct[func_id].ep_out_ff);
+}
+
+uint16_t tud_audio_n_read(uint8_t func_id, void* buffer, uint16_t bufsize)
+{
+ TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL);
+ return tu_fifo_read_n(&_audiod_fct[func_id].ep_out_ff, buffer, 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_t* tud_audio_n_get_ep_out_ff(uint8_t func_id)
+{
+ if(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL) return &_audiod_fct[func_id].ep_out_ff;
+ return NULL;
+}
+
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_DECODING && CFG_TUD_AUDIO_ENABLE_EP_OUT
+// Delete all content in the support RX FIFOs
+bool tud_audio_n_clear_rx_support_ff(uint8_t func_id, uint8_t ff_idx)
+{
+ TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL && ff_idx < _audiod_fct[func_id].n_rx_supp_ff);
+ return tu_fifo_clear(&_audiod_fct[func_id].rx_supp_ff[ff_idx]);
+}
+
+uint16_t tud_audio_n_available_support_ff(uint8_t func_id, uint8_t ff_idx)
+{
+ TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL && ff_idx < _audiod_fct[func_id].n_rx_supp_ff);
+ return tu_fifo_count(&_audiod_fct[func_id].rx_supp_ff[ff_idx]);
+}
+
+uint16_t tud_audio_n_read_support_ff(uint8_t func_id, uint8_t ff_idx, void* buffer, uint16_t bufsize)
+{
+ TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL && ff_idx < _audiod_fct[func_id].n_rx_supp_ff);
+ return tu_fifo_read_n(&_audiod_fct[func_id].rx_supp_ff[ff_idx], buffer, bufsize);
+}
+
+tu_fifo_t* tud_audio_n_get_rx_support_ff(uint8_t func_id, uint8_t ff_idx)
+{
+ if(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL && ff_idx < _audiod_fct[func_id].n_rx_supp_ff) return &_audiod_fct[func_id].rx_supp_ff[ff_idx];
+ return NULL;
+}
+#endif
+
+// This function is called once an audio packet is received by the USB and is responsible for putting data from USB memory into EP_OUT_FIFO (or support FIFOs + decoding of received stream into audio channels).
+// If you prefer your own (more efficient) implementation suiting your purpose set CFG_TUD_AUDIO_ENABLE_DECODING = 0.
+
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT
+
+static bool audiod_rx_done_cb(uint8_t rhport, audiod_function_t* audio, uint16_t n_bytes_received)
+{
+ uint8_t idxItf;
+ uint8_t const *dummy2;
+ uint8_t idx_audio_fct = 0;
+
+ if (tud_audio_rx_done_pre_read_cb || tud_audio_rx_done_post_read_cb)
+ {
+ idx_audio_fct = audiod_get_audio_fct_idx(audio);
+ TU_VERIFY(audiod_get_AS_interface_index(audio->ep_out_as_intf_num, audio, &idxItf, &dummy2));
+ }
+
+ // Call a weak callback here - a possibility for user to get informed an audio packet was received and data gets now loaded into EP FIFO (or decoded into support RX software FIFO)
+ if (tud_audio_rx_done_pre_read_cb) TU_VERIFY(tud_audio_rx_done_pre_read_cb(rhport, n_bytes_received, idx_audio_fct, audio->ep_out, audio->alt_setting[idxItf]));
+
+#if CFG_TUD_AUDIO_ENABLE_DECODING && CFG_TUD_AUDIO_ENABLE_EP_OUT
+
+ switch (audio->format_type_rx)
+ {
+ case AUDIO_FORMAT_TYPE_UNDEFINED:
+ // INDIVIDUAL DECODING PROCEDURE REQUIRED HERE!
+ TU_LOG2(" Desired CFG_TUD_AUDIO_FORMAT encoding not implemented!\r\n");
+ TU_BREAKPOINT();
+ break;
+
+ case AUDIO_FORMAT_TYPE_I:
+
+ switch (audio->format_type_I_tx)
+ {
+ case AUDIO_DATA_FORMAT_TYPE_I_PCM:
+ TU_VERIFY(audiod_decode_type_I_pcm(rhport, audio, n_bytes_received));
+ break;
+
+ default:
+ // DESIRED CFG_TUD_AUDIO_FORMAT_TYPE_I_RX NOT IMPLEMENTED!
+ TU_LOG2(" Desired CFG_TUD_AUDIO_FORMAT_TYPE_I_RX encoding not implemented!\r\n");
+ TU_BREAKPOINT();
+ break;
+ }
+ break;
+
+ default:
+ // Desired CFG_TUD_AUDIO_FORMAT_TYPE_RX not implemented!
+ TU_LOG2(" Desired CFG_TUD_AUDIO_FORMAT_TYPE_RX not implemented!\r\n");
+ TU_BREAKPOINT();
+ break;
+ }
+
+ // Prepare for next transmission
+ TU_VERIFY(usbd_edpt_xfer(rhport, audio->ep_out, audio->lin_buf_out, audio->ep_out_sz), false);
+
+#else
+
+#if USE_LINEAR_BUFFER_RX
+ // 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));
+
+ // Schedule for next receive
+ TU_VERIFY(usbd_edpt_xfer(rhport, audio->ep_out, audio->lin_buf_out, audio->ep_out_sz), false);
+#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);
+#endif
+
+#endif
+
+ // Call a weak callback here - a possibility for user to get informed decoding was completed
+ if (tud_audio_rx_done_post_read_cb) TU_VERIFY(tud_audio_rx_done_post_read_cb(rhport, n_bytes_received, idx_audio_fct, audio->ep_out, audio->alt_setting[idxItf]));
+
+ return true;
+}
+
+#endif //CFG_TUD_AUDIO_ENABLE_EP_OUT
+
+// The following functions are used in case CFG_TUD_AUDIO_ENABLE_DECODING != 0
+#if CFG_TUD_AUDIO_ENABLE_DECODING && CFG_TUD_AUDIO_ENABLE_EP_OUT
+
+// Decoding according to 2.3.1.5 Audio Streams
+
+// Helper function
+static inline uint8_t * audiod_interleaved_copy_bytes_fast_decode(uint16_t const nBytesToCopy, void * dst, uint8_t * dst_end, uint8_t * src, uint8_t const n_ff_used)
+{
+
+ // This function is an optimized version of
+ // while((uint8_t *)dst < dst_end)
+ // {
+ // memcpy(dst, src, nBytesToCopy);
+ // dst = (uint8_t *)dst + nBytesToCopy;
+ // src += nBytesToCopy * n_ff_used;
+ // }
+
+ // Optimize for fast half word copies
+ typedef struct{
+ uint16_t val;
+ } __attribute((__packed__)) unaligned_uint16_t;
+
+ // Optimize for fast word copies
+ typedef struct{
+ uint32_t val;
+ } __attribute((__packed__)) unaligned_uint32_t;
+
+ switch (nBytesToCopy)
+ {
+ case 1:
+ while((uint8_t *)dst < dst_end)
+ {
+ *(uint8_t *)dst++ = *src;
+ src += n_ff_used;
+ }
+ break;
+
+ case 2:
+ while((uint8_t *)dst < dst_end)
+ {
+ *(unaligned_uint16_t*)dst = *(unaligned_uint16_t*)src;
+ dst += 2;
+ src += 2 * n_ff_used;
+ }
+ break;
+
+ case 3:
+ while((uint8_t *)dst < dst_end)
+ {
+ // memcpy(dst, src, 3);
+ // dst = (uint8_t *)dst + 3;
+ // src += 3 * n_ff_used;
+
+ // TODO: Is there a faster way to copy 3 bytes?
+ *(uint8_t *)dst++ = *src++;
+ *(uint8_t *)dst++ = *src++;
+ *(uint8_t *)dst++ = *src++;
+
+ src += 3 * (n_ff_used - 1);
+ }
+ break;
+
+ case 4:
+ while((uint8_t *)dst < dst_end)
+ {
+ *(unaligned_uint32_t*)dst = *(unaligned_uint32_t*)src;
+ dst += 4;
+ src += 4 * n_ff_used;
+ }
+ break;
+ }
+
+ return src;
+}
+
+static bool audiod_decode_type_I_pcm(uint8_t rhport, audiod_function_t* audio, uint16_t n_bytes_received)
+{
+ (void) rhport;
+
+ // Determine amount of samples
+ uint8_t const n_ff_used = audio->n_ff_used_rx;
+ uint16_t const nBytesPerFFToRead = n_bytes_received / n_ff_used;
+ uint8_t cnt_ff;
+
+ // Decode
+ uint8_t * src;
+ uint8_t * dst_end;
+
+ tu_fifo_buffer_info_t info;
+
+ for (cnt_ff = 0; cnt_ff < n_ff_used; cnt_ff++)
+ {
+ tu_fifo_get_write_info(&audio->rx_supp_ff[cnt_ff], &info);
+
+ if (info.len_lin != 0)
+ {
+ info.len_lin = tu_min16(nBytesPerFFToRead, info.len_lin);
+ src = &audio->lin_buf_out[cnt_ff*audio->n_channels_per_ff_rx * audio->n_bytes_per_sampe_rx];
+ dst_end = info.ptr_lin + info.len_lin;
+ src = audiod_interleaved_copy_bytes_fast_decode(audio->n_bytes_per_sampe_rx, info.ptr_lin, dst_end, src, n_ff_used);
+
+ // Handle wrapped part of FIFO
+ info.len_wrap = tu_min16(nBytesPerFFToRead - info.len_lin, info.len_wrap);
+ if (info.len_wrap != 0)
+ {
+ dst_end = info.ptr_wrap + info.len_wrap;
+ audiod_interleaved_copy_bytes_fast_decode(audio->n_bytes_per_sampe_rx, info.ptr_wrap, dst_end, src, n_ff_used);
+ }
+ tu_fifo_advance_write_pointer(&audio->rx_supp_ff[cnt_ff], info.len_lin + info.len_wrap);
+ }
+ }
+
+ // Number of bytes should be a multiple of CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX * CFG_TUD_AUDIO_N_CHANNELS_RX but checking makes no sense - no way to correct it
+ // TU_VERIFY(cnt != n_bytes);
+
+ return true;
+}
+#endif //CFG_TUD_AUDIO_ENABLE_DECODING
+
+//--------------------------------------------------------------------+
+// WRITE API
+//--------------------------------------------------------------------+
+
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_ENCODING
+
+/**
+ * \brief Write data to EP in buffer
+ *
+ * Write data to buffer. If it is full, new data can be inserted once a transmit was scheduled. See audiod_tx_done_cb().
+ * If TX FIFOs are used, this function is not available in order to not let the user mess up the encoding process.
+ *
+ * \param[in] func_id: Index of audio function interface
+ * \param[in] data: Pointer to data array to be copied from
+ * \param[in] len: # of array elements to copy
+ * \return Number of bytes actually written
+ */
+uint16_t tud_audio_n_write(uint8_t func_id, const void * data, uint16_t len)
+{
+ TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL);
+ return tu_fifo_write_n(&_audiod_fct[func_id].ep_in_ff, data, len);
+}
+
+bool tud_audio_n_clear_ep_in_ff(uint8_t func_id) // Delete all content in the EP IN FIFO
+{
+ 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_t* tud_audio_n_get_ep_in_ff(uint8_t func_id)
+{
+ if(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL) return &_audiod_fct[func_id].ep_in_ff;
+ return NULL;
+}
+
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_ENCODING && CFG_TUD_AUDIO_ENABLE_EP_IN
+
+uint16_t tud_audio_n_flush_tx_support_ff(uint8_t func_id) // Force all content in the support TX FIFOs to be written into linear buffer and schedule a transmit
+{
+ TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL);
+ audiod_function_t* audio = &_audiod_fct[func_id];
+
+ uint16_t n_bytes_copied = tu_fifo_count(&audio->tx_supp_ff[0]);
+
+ TU_VERIFY(audiod_tx_done_cb(audio->rhport, audio));
+
+ n_bytes_copied -= tu_fifo_count(&audio->tx_supp_ff[0]);
+ n_bytes_copied = n_bytes_copied*audio->tx_supp_ff[0].item_size;
+
+ return n_bytes_copied;
+}
+
+bool tud_audio_n_clear_tx_support_ff(uint8_t func_id, uint8_t ff_idx)
+{
+ TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL && ff_idx < _audiod_fct[func_id].n_tx_supp_ff);
+ return tu_fifo_clear(&_audiod_fct[func_id].tx_supp_ff[ff_idx]);
+}
+
+uint16_t tud_audio_n_write_support_ff(uint8_t func_id, uint8_t ff_idx, const void * data, uint16_t len)
+{
+ TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL && ff_idx < _audiod_fct[func_id].n_tx_supp_ff);
+ return tu_fifo_write_n(&_audiod_fct[func_id].tx_supp_ff[ff_idx], data, len);
+}
+
+tu_fifo_t* tud_audio_n_get_tx_support_ff(uint8_t func_id, uint8_t ff_idx)
+{
+ if(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL && ff_idx < _audiod_fct[func_id].n_tx_supp_ff) return &_audiod_fct[func_id].tx_supp_ff[ff_idx];
+ return NULL;
+}
+
+#endif
+
+
+#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
+
+// If no interrupt transmit is pending bytes get written into buffer and a transmit is scheduled - once transmit completed tud_audio_int_ctr_done_cb() is called in inform user
+uint16_t tud_audio_int_ctr_n_write(uint8_t func_id, uint8_t const* buffer, uint16_t len)
+{
+ TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL);
+
+ // We write directly into the EP's buffer - abort if previous transfer not complete
+ TU_VERIFY(!usbd_edpt_busy(_audiod_fct[func_id].rhport, _audiod_fct[func_id].ep_int_ctr));
+
+ // Check length
+ TU_VERIFY(len <= CFG_TUD_AUDIO_INT_CTR_EP_IN_SW_BUFFER_SIZE);
+
+ memcpy(_audiod_fct[func_id].ep_int_ctr_buf, buffer, len);
+
+ // Schedule transmit
+ TU_VERIFY(usbd_edpt_xfer(_audiod_fct[func_id].rhport, _audiod_fct[func_id].ep_int_ctr, _audiod_fct[func_id].ep_int_ctr_buf, len));
+
+ return true;
+}
+
+#endif
+
+
+// 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_ENABLE_ENCODING = 0 and use tud_audio_n_write.
+
+// 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_ENABLE_EP_IN
+static bool audiod_tx_done_cb(uint8_t rhport, audiod_function_t * audio)
+{
+ uint8_t idxItf;
+ uint8_t const *dummy2;
+
+ uint8_t idx_audio_fct = audiod_get_audio_fct_idx(audio);
+ TU_VERIFY(audiod_get_AS_interface_index(audio->ep_in_as_intf_num, audio, &idxItf, &dummy2));
+
+ // Only send something if current alternate interface is not 0 as in this case nothing is to be sent due to UAC2 specifications
+ if (audio->alt_setting[idxItf] == 0) return false;
+
+ // 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 (tud_audio_tx_done_pre_load_cb) TU_VERIFY(tud_audio_tx_done_pre_load_cb(rhport, idx_audio_fct, audio->ep_in, audio->alt_setting[idxItf]));
+
+ // Send everything in ISO EP FIFO
+ uint16_t n_bytes_tx;
+
+ // If support FIFOs are used, encode and schedule transmit
+#if CFG_TUD_AUDIO_ENABLE_ENCODING && CFG_TUD_AUDIO_ENABLE_EP_IN
+ switch (audio->format_type_tx)
+ {
+ case AUDIO_FORMAT_TYPE_UNDEFINED:
+ // INDIVIDUAL ENCODING PROCEDURE REQUIRED HERE!
+ TU_LOG2(" Desired CFG_TUD_AUDIO_FORMAT encoding not implemented!\r\n");
+ TU_BREAKPOINT();
+ n_bytes_tx = 0;
+ break;
+
+ case AUDIO_FORMAT_TYPE_I:
+
+ switch (audio->format_type_I_tx)
+ {
+ case AUDIO_DATA_FORMAT_TYPE_I_PCM:
+
+ n_bytes_tx = audiod_encode_type_I_pcm(rhport, audio);
+ break;
+
+ default:
+ // YOUR ENCODING IS REQUIRED HERE!
+ TU_LOG2(" Desired CFG_TUD_AUDIO_FORMAT_TYPE_I_TX encoding not implemented!\r\n");
+ TU_BREAKPOINT();
+ n_bytes_tx = 0;
+ break;
+ }
+ break;
+
+ default:
+ // Desired CFG_TUD_AUDIO_FORMAT_TYPE_TX not implemented!
+ TU_LOG2(" Desired CFG_TUD_AUDIO_FORMAT_TYPE_TX not implemented!\r\n");
+ TU_BREAKPOINT();
+ n_bytes_tx = 0;
+ break;
+ }
+
+ TU_VERIFY(usbd_edpt_xfer(rhport, audio->ep_in, audio->lin_buf_in, n_bytes_tx));
+
+#else
+ // No support FIFOs, if no linear buffer required schedule transmit, else put data into linear buffer and schedule
+
+ 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
+
+#if USE_LINEAR_BUFFER_TX
+ 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));
+#else
+ // Send everything in ISO EP FIFO
+ TU_VERIFY(usbd_edpt_xfer_fifo(rhport, audio->ep_in, &audio->ep_in_ff, n_bytes_tx));
+#endif
+
+#endif
+
+ // 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_tx, idx_audio_fct, audio->ep_in, audio->alt_setting[idxItf]));
+
+ return true;
+}
+
+#endif //CFG_TUD_AUDIO_ENABLE_EP_IN
+
+#if CFG_TUD_AUDIO_ENABLE_ENCODING && CFG_TUD_AUDIO_ENABLE_EP_IN
+// Take samples from the support buffer and encode them into the IN EP software FIFO
+// Returns number of bytes written into linear buffer
+
+/* 2.3.1.7.1 PCM Format
+The PCM (Pulse Coded Modulation) format is the most commonly used audio format to represent audio
+data streams. The audio data is not compressed and uses a signed two’s-complement fixed point format. It
+is left-justified (the sign bit is the Msb) and data is padded with trailing zeros to fill the remaining unused
+bits of the subslot. The binary point is located to the right of the sign bit so that all values lie within the
+range [-1, +1)
+ */
+
+/*
+ * This function encodes channels saved within the support FIFOs into one stream by interleaving the PCM samples
+ * in the support FIFOs according to 2.3.1.5 Audio Streams. It does not control justification (left or right) and
+ * does not change the number of bytes per sample.
+ * */
+
+// Helper function
+static inline uint8_t * audiod_interleaved_copy_bytes_fast_encode(uint16_t const nBytesToCopy, uint8_t * src, uint8_t * src_end, uint8_t * dst, uint8_t const n_ff_used)
+{
+ // Optimize for fast half word copies
+ typedef struct{
+ uint16_t val;
+ } __attribute((__packed__)) unaligned_uint16_t;
+
+ // Optimize for fast word copies
+ typedef struct{
+ uint32_t val;
+ } __attribute((__packed__)) unaligned_uint32_t;
+
+ switch (nBytesToCopy)
+ {
+ case 1:
+ while(src < src_end)
+ {
+ *dst = *src++;
+ dst += n_ff_used;
+ }
+ break;
+
+ case 2:
+ while(src < src_end)
+ {
+ *(unaligned_uint16_t*)dst = *(unaligned_uint16_t*)src;
+ src += 2;
+ dst += 2 * n_ff_used;
+ }
+ break;
+
+ case 3:
+ while(src < src_end)
+ {
+ // memcpy(dst, src, 3);
+ // src = (uint8_t *)src + 3;
+ // dst += 3 * n_ff_used;
+
+ // TODO: Is there a faster way to copy 3 bytes?
+ *dst++ = *src++;
+ *dst++ = *src++;
+ *dst++ = *src++;
+
+ dst += 3 * (n_ff_used - 1);
+ }
+ break;
+
+ case 4:
+ while(src < src_end)
+ {
+ *(unaligned_uint32_t*)dst = *(unaligned_uint32_t*)src;
+ src += 4;
+ dst += 4 * n_ff_used;
+ }
+ break;
+ }
+
+ return dst;
+}
+
+static uint16_t audiod_encode_type_I_pcm(uint8_t rhport, audiod_function_t* audio)
+{
+ // This function relies on the fact that the length of the support FIFOs was configured to be a multiple of the active sample size in bytes s.t. no sample is split within a wrap
+ // This is ensured within set_interface, where the FIFOs are reconfigured according to this size
+
+ // We encode directly into IN EP's linear buffer - abort if previous transfer not complete
+ TU_VERIFY(!usbd_edpt_busy(rhport, audio->ep_in));
+
+ // Determine amount of samples
+ uint8_t const n_ff_used = audio->n_ff_used_tx;
+ uint16_t const nBytesToCopy = audio->n_channels_per_ff_tx * audio->n_bytes_per_sampe_tx;
+ uint16_t const capPerFF = audio->ep_in_sz / n_ff_used; // Sample capacity per FIFO in bytes
+ uint16_t nBytesPerFFToSend = tu_fifo_count(&audio->tx_supp_ff[0]);
+ uint8_t cnt_ff;
+
+ for (cnt_ff = 1; cnt_ff < n_ff_used; cnt_ff++)
+ {
+ uint16_t const count = tu_fifo_count(&audio->tx_supp_ff[cnt_ff]);
+ if (count < nBytesPerFFToSend)
+ {
+ nBytesPerFFToSend = count;
+ }
+ }
+
+ // Check if there is enough
+ if (nBytesPerFFToSend == 0) return 0;
+
+ // Limit to maximum sample number - THIS IS A POSSIBLE ERROR SOURCE IF TOO MANY SAMPLE WOULD NEED TO BE SENT BUT CAN NOT!
+ nBytesPerFFToSend = tu_min16(nBytesPerFFToSend, capPerFF);
+
+ // Round to full number of samples (flooring)
+ nBytesPerFFToSend = (nBytesPerFFToSend / nBytesToCopy) * nBytesToCopy;
+
+ // Encode
+ uint8_t * dst;
+ uint8_t * src_end;
+
+ tu_fifo_buffer_info_t info;
+
+ for (cnt_ff = 0; cnt_ff < n_ff_used; cnt_ff++)
+ {
+ dst = &audio->lin_buf_in[cnt_ff*audio->n_channels_per_ff_tx*audio->n_bytes_per_sampe_tx];
+
+ tu_fifo_get_read_info(&audio->tx_supp_ff[cnt_ff], &info);
+
+ if (info.len_lin != 0)
+ {
+ info.len_lin = tu_min16(nBytesPerFFToSend, info.len_lin); // Limit up to desired length
+ src_end = (uint8_t *)info.ptr_lin + info.len_lin;
+ dst = audiod_interleaved_copy_bytes_fast_encode(audio->n_bytes_per_sampe_tx, info.ptr_lin, src_end, dst, n_ff_used);
+
+ // Limit up to desired length
+ info.len_wrap = tu_min16(nBytesPerFFToSend - info.len_lin, info.len_wrap);
+
+ // Handle wrapped part of FIFO
+ if (info.len_wrap != 0)
+ {
+ src_end = (uint8_t *)info.ptr_wrap + info.len_wrap;
+ audiod_interleaved_copy_bytes_fast_encode(audio->n_bytes_per_sampe_tx, info.ptr_wrap, src_end, dst, n_ff_used);
+ }
+
+ tu_fifo_advance_read_pointer(&audio->tx_supp_ff[cnt_ff], info.len_lin + info.len_wrap);
+ }
+ }
+
+ return nBytesPerFFToSend * n_ff_used;
+}
+#endif //CFG_TUD_AUDIO_ENABLE_ENCODING
+
+// This function is called once a transmit of a feedback packet was successfully completed. Here, we get the next feedback value to be sent
+
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+static inline bool audiod_fb_send(uint8_t rhport, audiod_function_t *audio)
+{
+ return usbd_edpt_xfer(rhport, audio->ep_fb, (uint8_t *) &audio->fb_val, 4);
+}
+#endif
+
+//--------------------------------------------------------------------+
+// USBD Driver API
+//--------------------------------------------------------------------+
+void audiod_init(void)
+{
+ tu_memclr(_audiod_fct, sizeof(_audiod_fct));
+
+ 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_1;
+ 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_2;
+ 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_3;
+ audio->ctrl_buf_sz = CFG_TUD_AUDIO_FUNC_3_CTRL_BUF_SZ;
+ break;
+#endif
+ }
+
+ // Initialize active alternate interface buffers
+ switch (i)
+ {
+#if CFG_TUD_AUDIO_FUNC_1_N_AS_INT > 0
+ case 0:
+ audio->alt_setting = alt_setting_1;
+ break;
+#endif
+#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_N_AS_INT > 0
+ case 1:
+ audio->alt_setting = alt_setting_2;
+ break;
+#endif
+#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_N_AS_INT > 0
+ case 2:
+ audio->alt_setting = alt_setting_3;
+ break;
+#endif
+ }
+
+ // Initialize IN EP FIFO if required
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_ENCODING
+
+ switch (i)
+ {
+#if CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ > 0
+ case 0:
+ tu_fifo_config(&audio->ep_in_ff, audio_ep_in_sw_buf_1, CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ, 1, true);
+#if CFG_FIFO_MUTEX
+ tu_fifo_config_mutex(&audio->ep_in_ff, osal_mutex_create(&ep_in_ff_mutex_wr_1), NULL);
+#endif
+ 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, audio_ep_in_sw_buf_2, CFG_TUD_AUDIO_FUNC_2_EP_IN_SW_BUF_SZ, 1, true);
+#if CFG_FIFO_MUTEX
+ tu_fifo_config_mutex(&audio->ep_in_ff, osal_mutex_create(&ep_in_ff_mutex_wr_2), NULL);
+#endif
+ 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, audio_ep_in_sw_buf_3, CFG_TUD_AUDIO_FUNC_3_EP_IN_SW_BUF_SZ, 1, true);
+#if CFG_FIFO_MUTEX
+ tu_fifo_config_mutex(&audio->ep_in_ff, osal_mutex_create(&ep_in_ff_mutex_wr_3), NULL);
+#endif
+ break;
+#endif
+ }
+#endif // CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_ENCODING
+
+ // Initialize linear buffers
+#if USE_LINEAR_BUFFER_TX
+ switch (i)
+ {
+#if CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX > 0
+ case 0:
+ audio->lin_buf_in = lin_buf_in_1;
+ break;
+#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_2;
+ break;
+#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_3;
+ break;
+#endif
+ }
+#endif // USE_LINEAR_BUFFER_TX
+
+ // Initialize OUT EP FIFO if required
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && !CFG_TUD_AUDIO_ENABLE_DECODING
+
+ switch (i)
+ {
+#if CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ > 0
+ case 0:
+ tu_fifo_config(&audio->ep_out_ff, audio_ep_out_sw_buf_1, CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ, 1, true);
+#if CFG_FIFO_MUTEX
+ tu_fifo_config_mutex(&audio->ep_out_ff, NULL, osal_mutex_create(&ep_out_ff_mutex_rd_1));
+#endif
+ 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, audio_ep_out_sw_buf_2, CFG_TUD_AUDIO_FUNC_2_EP_OUT_SW_BUF_SZ, 1, true);
+#if CFG_FIFO_MUTEX
+ tu_fifo_config_mutex(&audio->ep_out_ff, NULL, osal_mutex_create(&ep_out_ff_mutex_rd_2));
+#endif
+ 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, audio_ep_out_sw_buf_3, CFG_TUD_AUDIO_FUNC_3_EP_OUT_SW_BUF_SZ, 1, true);
+#if CFG_FIFO_MUTEX
+ tu_fifo_config_mutex(&audio->ep_out_ff, NULL, osal_mutex_create(&ep_out_ff_mutex_rd_3));
+#endif
+ break;
+#endif
+ }
+#endif // CFG_TUD_AUDIO_ENABLE_EP_OUT && !CFG_TUD_AUDIO_ENABLE_DECODING
+
+ // Initialize linear buffers
+#if USE_LINEAR_BUFFER_RX
+ switch (i)
+ {
+#if CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX > 0
+ case 0:
+ audio->lin_buf_out = lin_buf_out_1;
+ break;
+#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_2;
+ break;
+#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_3;
+ break;
+#endif
+ }
+#endif // USE_LINEAR_BUFFER_TX
+
+ // Initialize TX support FIFOs if required
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_ENCODING
+
+ switch (i)
+ {
+#if CFG_TUD_AUDIO_FUNC_1_TX_SUPP_SW_FIFO_SZ > 0
+ case 0:
+ audio->tx_supp_ff = tx_supp_ff_1;
+ audio->n_tx_supp_ff = CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO;
+ audio->tx_supp_ff_sz_max = CFG_TUD_AUDIO_FUNC_1_TX_SUPP_SW_FIFO_SZ;
+ for (uint8_t cnt = 0; cnt < CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO; cnt++)
+ {
+ tu_fifo_config(&tx_supp_ff_1[cnt], tx_supp_ff_buf_1[cnt], CFG_TUD_AUDIO_FUNC_1_TX_SUPP_SW_FIFO_SZ, 1, true);
+#if CFG_FIFO_MUTEX
+ tu_fifo_config_mutex(&tx_supp_ff_1[cnt], osal_mutex_create(&tx_supp_ff_mutex_wr_1[cnt]), NULL);
+#endif
+ }
+
+ break;
+#endif // CFG_TUD_AUDIO_FUNC_1_TX_SUPP_SW_FIFO_SZ > 0
+
+#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_TX_SUPP_SW_FIFO_SZ > 0
+ case 1:
+ audio->tx_supp_ff = tx_supp_ff_2;
+ audio->n_tx_supp_ff = CFG_TUD_AUDIO_FUNC_2_N_TX_SUPP_SW_FIFO;
+ audio->tx_supp_ff_sz_max = CFG_TUD_AUDIO_FUNC_2_TX_SUPP_SW_FIFO_SZ;
+ for (uint8_t cnt = 0; cnt < CFG_TUD_AUDIO_FUNC_2_N_TX_SUPP_SW_FIFO; cnt++)
+ {
+ tu_fifo_config(&tx_supp_ff_2[cnt], tx_supp_ff_buf_2[cnt], CFG_TUD_AUDIO_FUNC_2_TX_SUPP_SW_FIFO_SZ, 1, true);
+#if CFG_FIFO_MUTEX
+ tu_fifo_config_mutex(&tx_supp_ff_2[cnt], osal_mutex_create(&tx_supp_ff_mutex_wr_2[cnt]), NULL);
+#endif
+ }
+
+ break;
+#endif // CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_TX_SUPP_SW_FIFO_SZ > 0
+
+#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_TX_SUPP_SW_FIFO_SZ > 0
+ case 2:
+ audio->tx_supp_ff = tx_supp_ff_3;
+ audio->n_tx_supp_ff = CFG_TUD_AUDIO_FUNC_3_N_TX_SUPP_SW_FIFO;
+ audio->tx_supp_ff_sz_max = CFG_TUD_AUDIO_FUNC_3_TX_SUPP_SW_FIFO_SZ;
+ for (uint8_t cnt = 0; cnt < CFG_TUD_AUDIO_FUNC_3_N_TX_SUPP_SW_FIFO; cnt++)
+ {
+ tu_fifo_config(&tx_supp_ff_3[cnt], tx_supp_ff_buf_3[cnt], CFG_TUD_AUDIO_FUNC_3_TX_SUPP_SW_FIFO_SZ, 1, true);
+#if CFG_FIFO_MUTEX
+ tu_fifo_config_mutex(&tx_supp_ff_3[cnt], osal_mutex_create(&tx_supp_ff_mutex_wr_3[cnt]), NULL);
+#endif
+ }
+
+ break;
+#endif // CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_TX_SUPP_SW_FIFO_SZ > 0
+ }
+#endif // CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_ENCODING
+
+ // Set encoding parameters for Type_I formats
+#if CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING
+ switch (i)
+ {
+#if CFG_TUD_AUDIO_FUNC_1_TX_SUPP_SW_FIFO_SZ > 0
+ case 0:
+ audio->n_channels_per_ff_tx = CFG_TUD_AUDIO_FUNC_1_CHANNEL_PER_FIFO_TX;
+ break;
+#endif
+#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_TX_SUPP_SW_FIFO_SZ > 0
+ case 1:
+ audio->n_channels_per_ff_tx = CFG_TUD_AUDIO_FUNC_2_CHANNEL_PER_FIFO_TX;
+ break;
+#endif
+#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_TX_SUPP_SW_FIFO_SZ > 0
+ case 2:
+ audio->n_channels_per_ff_tx = CFG_TUD_AUDIO_FUNC_3_CHANNEL_PER_FIFO_TX;
+ break;
+#endif
+ }
+#endif // CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING
+
+ // Initialize RX support FIFOs if required
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING
+
+ switch (i)
+ {
+#if CFG_TUD_AUDIO_FUNC_1_RX_SUPP_SW_FIFO_SZ > 0
+ case 0:
+ audio->rx_supp_ff = rx_supp_ff_1;
+ audio->n_rx_supp_ff = CFG_TUD_AUDIO_FUNC_1_N_RX_SUPP_SW_FIFO;
+ audio->rx_supp_ff_sz_max = CFG_TUD_AUDIO_FUNC_1_RX_SUPP_SW_FIFO_SZ;
+ for (uint8_t cnt = 0; cnt < CFG_TUD_AUDIO_FUNC_1_N_RX_SUPP_SW_FIFO; cnt++)
+ {
+ tu_fifo_config(&rx_supp_ff_1[cnt], rx_supp_ff_buf_1[cnt], CFG_TUD_AUDIO_FUNC_1_RX_SUPP_SW_FIFO_SZ, 1, true);
+#if CFG_FIFO_MUTEX
+ tu_fifo_config_mutex(&rx_supp_ff_1[cnt], osal_mutex_create(&rx_supp_ff_mutex_rd_1[cnt]), NULL);
+#endif
+ }
+
+ break;
+#endif // CFG_TUD_AUDIO_FUNC_1_RX_SUPP_SW_FIFO_SZ > 0
+
+#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_RX_SUPP_SW_FIFO_SZ > 0
+ case 1:
+ audio->rx_supp_ff = rx_supp_ff_2;
+ audio->n_rx_supp_ff = CFG_TUD_AUDIO_FUNC_2_N_RX_SUPP_SW_FIFO;
+ audio->rx_supp_ff_sz_max = CFG_TUD_AUDIO_FUNC_2_RX_SUPP_SW_FIFO_SZ;
+ for (uint8_t cnt = 0; cnt < CFG_TUD_AUDIO_FUNC_2_N_RX_SUPP_SW_FIFO; cnt++)
+ {
+ tu_fifo_config(&rx_supp_ff_2[cnt], rx_supp_ff_buf_2[cnt], CFG_TUD_AUDIO_FUNC_2_RX_SUPP_SW_FIFO_SZ, 1, true);
+#if CFG_FIFO_MUTEX
+ tu_fifo_config_mutex(&rx_supp_ff_2[cnt], osal_mutex_create(&rx_supp_ff_mutex_rd_2[cnt]), NULL);
+#endif
+ }
+
+ break;
+#endif // CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_RX_SUPP_SW_FIFO_SZ > 0
+
+#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_RX_SUPP_SW_FIFO_SZ > 0
+ case 2:
+ audio->rx_supp_ff = rx_supp_ff_3;
+ audio->n_rx_supp_ff = CFG_TUD_AUDIO_FUNC_3_N_RX_SUPP_SW_FIFO;
+ audio->rx_supp_ff_sz_max = CFG_TUD_AUDIO_FUNC_3_RX_SUPP_SW_FIFO_SZ;
+ for (uint8_t cnt = 0; cnt < CFG_TUD_AUDIO_FUNC_3_N_RX_SUPP_SW_FIFO; cnt++)
+ {
+ tu_fifo_config(&rx_supp_ff_3[cnt], rx_supp_ff_buf_3[cnt], CFG_TUD_AUDIO_FUNC_3_RX_SUPP_SW_FIFO_SZ, 1, true);
+#if CFG_FIFO_MUTEX
+ tu_fifo_config_mutex(&rx_supp_ff_3[cnt], osal_mutex_create(&rx_supp_ff_mutex_rd_3[cnt]), NULL);
+#endif
+ }
+
+ break;
+#endif // CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_RX_SUPP_SW_FIFO_SZ > 0
+ }
+#endif // CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_ENCODING
+
+ // Set encoding parameters for Type_I formats
+#if CFG_TUD_AUDIO_ENABLE_TYPE_I_DECODING
+ switch (i)
+ {
+#if CFG_TUD_AUDIO_FUNC_1_RX_SUPP_SW_FIFO_SZ > 0
+ case 0:
+ audio->n_channels_per_ff_rx = CFG_TUD_AUDIO_FUNC_1_CHANNEL_PER_FIFO_RX;
+ break;
+#endif
+#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_RX_SUPP_SW_FIFO_SZ > 0
+ case 1:
+ audio->n_channels_per_ff_rx = CFG_TUD_AUDIO_FUNC_2_CHANNEL_PER_FIFO_RX;
+ break;
+#endif
+#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_RX_SUPP_SW_FIFO_SZ > 0
+ case 2:
+ audio->n_channels_per_ff_rx = CFG_TUD_AUDIO_FUNC_3_CHANNEL_PER_FIFO_RX;
+ break;
+#endif
+ }
+#endif // CFG_TUD_AUDIO_ENABLE_TYPE_I_DECODING
+ }
+}
+
+void audiod_reset(uint8_t rhport)
+{
+ (void) rhport;
+
+ for(uint8_t i=0; i<CFG_TUD_AUDIO; i++)
+ {
+ audiod_function_t* audio = &_audiod_fct[i];
+ tu_memclr(audio, ITF_MEM_RESET_SIZE);
+
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_ENCODING
+ tu_fifo_clear(&audio->ep_in_ff);
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && !CFG_TUD_AUDIO_ENABLE_DECODING
+ tu_fifo_clear(&audio->ep_out_ff);
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_ENCODING
+ for (uint8_t cnt = 0; cnt < audio->n_tx_supp_ff; cnt++)
+ {
+ tu_fifo_clear(&audio->tx_supp_ff[cnt]);
+ }
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING
+ for (uint8_t cnt = 0; cnt < audio->n_rx_supp_ff; cnt++)
+ {
+ tu_fifo_clear(&audio->rx_supp_ff[cnt]);
+ }
+#endif
+ }
+}
+
+uint16_t audiod_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
+{
+ (void) max_len;
+
+ TU_VERIFY ( TUSB_CLASS_AUDIO == itf_desc->bInterfaceClass &&
+ AUDIO_SUBCLASS_CONTROL == itf_desc->bInterfaceSubClass);
+
+ // Verify version is correct - this check can be omitted
+ TU_VERIFY(itf_desc->bInterfaceProtocol == AUDIO_INT_PROTOCOL_CODE_V2);
+
+ // Verify interrupt control EP is enabled if demanded by descriptor - this should be best some static check however - this check can be omitted
+ if (itf_desc->bNumEndpoints == 1) // 0 or 1 EPs are allowed
+ {
+ TU_VERIFY(CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN > 0);
+ }
+
+ // Alternate setting MUST be zero - this check can be omitted
+ TU_VERIFY(itf_desc->bAlternateSetting == 0);
+
+ // Find available audio driver interface
+ uint8_t i;
+ for (i = 0; i < CFG_TUD_AUDIO; i++)
+ {
+ if (!_audiod_fct[i].p_desc)
+ {
+ _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
+ }
+
+ break;
+ }
+ }
+
+ // Verify we found a free one
+ 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
+
+ return drv_len;
+}
+
+static bool audiod_get_interface(uint8_t rhport, tusb_control_request_t const * p_request)
+{
+ uint8_t const itf = tu_u16_low(p_request->wIndex);
+
+ // Find index of audio streaming interface
+ uint8_t func_id, idxItf;
+ uint8_t const *dummy;
+
+ TU_VERIFY(audiod_get_AS_interface_index_global(itf, &func_id, &idxItf, &dummy));
+ TU_VERIFY(tud_control_xfer(rhport, p_request, &_audiod_fct[func_id].alt_setting[idxItf], 1));
+
+ TU_LOG2(" Get itf: %u - current alt: %u\r\n", itf, _audiod_fct[func_id].alt_setting[idxItf]);
+
+ return true;
+}
+
+static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const * p_request)
+{
+ (void) rhport;
+
+ // Here we need to do the following:
+
+ // 1. Find the audio driver assigned to the given interface to be set
+ // Since one audio driver interface has to be able to cover an unknown number of interfaces (AC, AS + its alternate settings), the best memory efficient way to solve this is to always search through the descriptors.
+ // The audio driver is mapped to an audio function by a reference pointer to the corresponding AC interface of this audio function which serves as a starting point for searching
+
+ // 2. Close EPs which are currently open
+ // To do so it is not necessary to know the current active alternate interface since we already save the current EP addresses - we simply close them
+
+ // 3. Open new EP
+
+ uint8_t const itf = tu_u16_low(p_request->wIndex);
+ uint8_t const alt = tu_u16_low(p_request->wValue);
+
+ TU_LOG2(" Set itf: %u - alt: %u\r\n", itf, alt);
+
+ // Find index of audio streaming interface and index of interface
+ uint8_t func_id, idxItf;
+ uint8_t const *p_desc;
+ TU_VERIFY(audiod_get_AS_interface_index_global(itf, &func_id, &idxItf, &p_desc));
+
+ audiod_function_t* audio = &_audiod_fct[func_id];
+
+ // 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_ENABLE_EP_IN
+ if (audio->ep_in_as_intf_num == itf)
+ {
+ audio->ep_in_as_intf_num = 0;
+ usbd_edpt_close(rhport, audio->ep_in);
+
+ // Clear FIFOs, since data is no longer valid
+#if !CFG_TUD_AUDIO_ENABLE_ENCODING
+ tu_fifo_clear(&audio->ep_in_ff);
+#else
+ for (uint8_t cnt = 0; cnt < audio->n_tx_supp_ff; cnt++)
+ {
+ tu_fifo_clear(&audio->tx_supp_ff[cnt]);
+ }
+#endif
+
+ // Invoke callback - can be used to stop data sampling
+ if (tud_audio_set_itf_close_EP_cb) TU_VERIFY(tud_audio_set_itf_close_EP_cb(rhport, p_request));
+
+ audio->ep_in = 0; // Necessary?
+
+ }
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT
+ if (audio->ep_out_as_intf_num == itf)
+ {
+ audio->ep_out_as_intf_num = 0;
+ usbd_edpt_close(rhport, audio->ep_out);
+
+ // Clear FIFOs, since data is no longer valid
+#if !CFG_TUD_AUDIO_ENABLE_DECODING
+ tu_fifo_clear(&audio->ep_out_ff);
+#else
+ for (uint8_t cnt = 0; cnt < audio->n_rx_supp_ff; cnt++)
+ {
+ tu_fifo_clear(&audio->rx_supp_ff[cnt]);
+ }
+#endif
+
+ // Invoke callback - can be used to stop data sampling
+ if (tud_audio_set_itf_close_EP_cb) TU_VERIFY(tud_audio_set_itf_close_EP_cb(rhport, p_request));
+
+ audio->ep_out = 0; // Necessary?
+
+ // Close corresponding feedback EP
+#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+ usbd_edpt_close(rhport, audio->ep_fb);
+ audio->ep_fb = 0; // Necessary?
+#endif
+ }
+#endif
+
+ // Save current alternative interface setting
+ audio->alt_setting[idxItf] = alt;
+
+ // Open new EP if necessary - EPs are only to be closed or opened for AS interfaces - Look for AS interface with correct alternate interface
+ // Get pointer at end
+ uint8_t const *p_desc_end = audio->p_desc + audio->desc_length - TUD_AUDIO_DESC_IAD_LEN;
+
+ // p_desc starts at required interface with alternate setting zero
+ while (p_desc < p_desc_end)
+ {
+ // Find correct interface
+ if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE && ((tusb_desc_interface_t const * )p_desc)->bInterfaceNumber == itf && ((tusb_desc_interface_t const * )p_desc)->bAlternateSetting == alt)
+ {
+#if CFG_TUD_AUDIO_ENABLE_ENCODING || CFG_TUD_AUDIO_ENABLE_DECODING
+ uint8_t const * p_desc_parse_for_params = p_desc;
+#endif
+ // From this point forward follow the EP descriptors associated to the current alternate setting interface - Open EPs if necessary
+ uint8_t foundEPs = 0, nEps = ((tusb_desc_interface_t const * )p_desc)->bNumEndpoints;
+ while (foundEPs < nEps && p_desc < p_desc_end)
+ {
+ if (tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT)
+ {
+ TU_ASSERT(usbd_edpt_open(rhport, (tusb_desc_endpoint_t const *)p_desc));
+
+ uint8_t ep_addr = ((tusb_desc_endpoint_t const *) p_desc)->bEndpointAddress;
+
+ //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
+ 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
+ audio->ep_in = ep_addr;
+ audio->ep_in_as_intf_num = itf;
+ audio->ep_in_sz = ((tusb_desc_endpoint_t const *) p_desc)->wMaxPacketSize.size;
+
+ // If software encoding is enabled, parse for the corresponding parameters - doing this here means only AS interfaces with EPs get scanned for parameters
+#if CFG_TUD_AUDIO_ENABLE_ENCODING
+ audiod_parse_for_AS_params(audio, p_desc_parse_for_params, p_desc_end, itf);
+
+ // Reconfigure size of support FIFOs - this is necessary to avoid samples to get split in case of a wrap
+#if CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING
+ const uint16_t active_fifo_depth = (audio->tx_supp_ff_sz_max / audio->n_bytes_per_sampe_tx) * audio->n_bytes_per_sampe_tx;
+ for (uint8_t cnt = 0; cnt < audio->n_tx_supp_ff; cnt++)
+ {
+ tu_fifo_config(&audio->tx_supp_ff[cnt], audio->tx_supp_ff[cnt].buffer, active_fifo_depth, 1, true);
+ }
+ audio->n_ff_used_tx = audio->n_channels_tx / audio->n_channels_per_ff_tx;
+ TU_ASSERT( audio->n_ff_used_tx <= audio->n_tx_supp_ff );
+#endif
+
+#endif
+ // Invoke callback - can be used to trigger data sampling if not already running
+ if (tud_audio_set_itf_cb) TU_VERIFY(tud_audio_set_itf_cb(rhport, p_request));
+
+ // Schedule first transmit if alternate interface is not zero i.e. streaming is disabled - in case no sample data is available a ZLP is loaded
+ // It is necessary to trigger this here since the refill is done with an RX FIFO empty interrupt which can only trigger if something was in there
+ TU_VERIFY(audiod_tx_done_cb(rhport, &_audiod_fct[func_id]));
+ }
+#endif // CFG_TUD_AUDIO_ENABLE_EP_IN
+
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT
+
+ if (tu_edpt_dir(ep_addr) == TUSB_DIR_OUT) // Checking usage not necessary
+ {
+ // Save address
+ audio->ep_out = ep_addr;
+ audio->ep_out_as_intf_num = itf;
+ audio->ep_out_sz = ((tusb_desc_endpoint_t const *) p_desc)->wMaxPacketSize.size;
+
+#if CFG_TUD_AUDIO_ENABLE_DECODING
+ audiod_parse_for_AS_params(audio, p_desc_parse_for_params, p_desc_end, itf);
+
+ // Reconfigure size of support FIFOs - this is necessary to avoid samples to get split in case of a wrap
+#if CFG_TUD_AUDIO_ENABLE_TYPE_I_DECODING
+ const uint16_t active_fifo_depth = (audio->rx_supp_ff_sz_max / audio->n_bytes_per_sampe_rx) * audio->n_bytes_per_sampe_rx;
+ for (uint8_t cnt = 0; cnt < audio->n_rx_supp_ff; cnt++)
+ {
+ tu_fifo_config(&audio->rx_supp_ff[cnt], audio->rx_supp_ff[cnt].buffer, active_fifo_depth, 1, true);
+ }
+ audio->n_ff_used_rx = audio->n_channels_rx / audio->n_channels_per_ff_rx;
+ TU_ASSERT( audio->n_ff_used_rx <= audio->n_rx_supp_ff );
+#endif
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+ // In case of asynchronous EP, call Cb after ep_fb is set
+ if (!(((tusb_desc_endpoint_t const *) p_desc)->bmAttributes.sync == 0x01 && audio->ep_fb == 0))
+ {
+ if (tud_audio_set_itf_cb) TU_VERIFY(tud_audio_set_itf_cb(rhport, p_request));
+ }
+#else
+ // Invoke callback
+ if (tud_audio_set_itf_cb) TU_VERIFY(tud_audio_set_itf_cb(rhport, p_request));
+#endif
+ // 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);
+#else
+ 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
+ if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN && ((tusb_desc_endpoint_t const *) p_desc)->bmAttributes.usage == 1) // Check if usage is explicit data feedback
+ {
+ audio->ep_fb = ep_addr;
+
+ // Invoke callback after ep_out is set
+ if (audio->ep_out != 0)
+ {
+ if (tud_audio_set_itf_cb) TU_VERIFY(tud_audio_set_itf_cb(rhport, p_request));
+ }
+ }
+#endif
+#endif // CFG_TUD_AUDIO_ENABLE_EP_OUT
+
+ foundEPs += 1;
+ }
+ p_desc = tu_desc_next(p_desc);
+ }
+
+ TU_VERIFY(foundEPs == nEps);
+
+ // We are done - abort loop
+ break;
+ }
+
+ // Moving forward
+ p_desc = tu_desc_next(p_desc);
+ }
+
+ tud_control_status(rhport, p_request);
+
+ return true;
+}
+
+// Invoked when class request DATA stage is finished.
+// return false to stall control EP (e.g Host send non-sense DATA)
+static bool audiod_control_complete(uint8_t rhport, tusb_control_request_t const * p_request)
+{
+ // Handle audio class specific set requests
+ if(p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS && p_request->bmRequestType_bit.direction == TUSB_DIR_OUT)
+ {
+ uint8_t func_id;
+
+ switch (p_request->bmRequestType_bit.recipient)
+ {
+ case TUSB_REQ_RCPT_INTERFACE:
+ {
+ uint8_t itf = TU_U16_LOW(p_request->wIndex);
+ uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
+
+ if (entityID != 0)
+ {
+ if (tud_audio_set_req_entity_cb)
+ {
+ // Check if entity is present and get corresponding driver index
+ TU_VERIFY(audiod_verify_entity_exists(itf, entityID, &func_id));
+
+ // Invoke callback
+ return tud_audio_set_req_entity_cb(rhport, p_request, _audiod_fct[func_id].ctrl_buf);
+ }
+ else
+ {
+ TU_LOG2(" No entity set request callback available!\r\n");
+ return false; // In case no callback function is present or request can not be conducted we stall it
+ }
+ }
+ else
+ {
+ if (tud_audio_set_req_itf_cb)
+ {
+ // 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);
+ }
+ else
+ {
+ TU_LOG2(" No interface set request callback available!\r\n");
+ return false; // In case no callback function is present or request can not be conducted we stall it
+ }
+ }
+ }
+ break;
+
+ case TUSB_REQ_RCPT_ENDPOINT:
+ {
+ uint8_t ep = TU_U16_LOW(p_request->wIndex);
+
+ if (tud_audio_set_req_ep_cb)
+ {
+ // 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);
+ }
+ else
+ {
+ TU_LOG2(" No EP set request callback available!\r\n");
+ return false; // In case no callback function is present or request can not be conducted we stall it
+ }
+ }
+ break;
+ // Unknown/Unsupported recipient
+ default: TU_BREAKPOINT(); return false;
+ }
+ }
+ return true;
+}
+
+// Handle class control request
+// return false to stall control endpoint (e.g unsupported request)
+static bool audiod_control_request(uint8_t rhport, tusb_control_request_t const * p_request)
+{
+ (void) rhport;
+
+ // Handle standard requests - standard set requests usually have no data stage so we also handle set requests here
+ if (p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD)
+ {
+ switch (p_request->bRequest)
+ {
+ case TUSB_REQ_GET_INTERFACE:
+ return audiod_get_interface(rhport, p_request);
+
+ case TUSB_REQ_SET_INTERFACE:
+ return audiod_set_interface(rhport, p_request);
+
+ // Unknown/Unsupported request
+ default: TU_BREAKPOINT(); return false;
+ }
+ }
+
+ // Handle class requests
+ if (p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS)
+ {
+ uint8_t itf = TU_U16_LOW(p_request->wIndex);
+ uint8_t func_id;
+
+ // Conduct checks which depend on the recipient
+ switch (p_request->bmRequestType_bit.recipient)
+ {
+ case TUSB_REQ_RCPT_INTERFACE:
+ {
+ uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
+
+ // Verify if entity is present
+ if (entityID != 0)
+ {
+ // Find index of audio driver structure and verify entity really exists
+ TU_VERIFY(audiod_verify_entity_exists(itf, entityID, &func_id));
+
+ // In case we got a get request invoke callback - callback needs to answer as defined in UAC2 specification page 89 - 5. Requests
+ if (p_request->bmRequestType_bit.direction == TUSB_DIR_IN)
+ {
+ if (tud_audio_get_req_entity_cb)
+ {
+ return tud_audio_get_req_entity_cb(rhport, p_request);
+ }
+ else
+ {
+ TU_LOG2(" No entity get request callback available!\r\n");
+ return false; // Stall
+ }
+ }
+ }
+ else
+ {
+ // Find index of audio driver structure and verify interface really exists
+ TU_VERIFY(audiod_verify_itf_exists(itf, &func_id));
+
+ // In case we got a get request invoke callback - callback needs to answer as defined in UAC2 specification page 89 - 5. Requests
+ if (p_request->bmRequestType_bit.direction == TUSB_DIR_IN)
+ {
+ if (tud_audio_get_req_itf_cb)
+ {
+ return tud_audio_get_req_itf_cb(rhport, p_request);
+ }
+ else
+ {
+ TU_LOG2(" No interface get request callback available!\r\n");
+ return false; // Stall
+ }
+ }
+ }
+ }
+ break;
+
+ case TUSB_REQ_RCPT_ENDPOINT:
+ {
+ uint8_t ep = TU_U16_LOW(p_request->wIndex);
+
+ // Find index of audio driver structure and verify EP really exists
+ TU_VERIFY(audiod_verify_ep_exists(ep, &func_id));
+
+ // In case we got a get request invoke callback - callback needs to answer as defined in UAC2 specification page 89 - 5. Requests
+ if (p_request->bmRequestType_bit.direction == TUSB_DIR_IN)
+ {
+ if (tud_audio_get_req_ep_cb)
+ {
+ return tud_audio_get_req_ep_cb(rhport, p_request);
+ }
+ else
+ {
+ TU_LOG2(" No EP get request callback available!\r\n");
+ return false; // Stall
+ }
+ }
+ }
+ break;
+
+ // Unknown/Unsupported recipient
+ default: TU_LOG2(" Unsupported recipient: %d\r\n", p_request->bmRequestType_bit.recipient); TU_BREAKPOINT(); return false;
+ }
+
+ // 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));
+ return true;
+ }
+
+ // There went something wrong - unsupported control request type
+ TU_BREAKPOINT();
+ return false;
+}
+
+bool audiod_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
+{
+ if ( stage == CONTROL_STAGE_SETUP )
+ {
+ return audiod_control_request(rhport, request);
+ }
+ else if ( stage == CONTROL_STAGE_DATA )
+ {
+ return audiod_control_complete(rhport, request);
+ }
+
+ return true;
+}
+
+bool audiod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
+{
+ (void) result;
+ (void) xferred_bytes;
+
+ // Search for interface belonging to given end point address and proceed as required
+ uint8_t func_id;
+ for (func_id = 0; func_id < CFG_TUD_AUDIO; func_id++)
+ {
+
+#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
+
+ // Data transmission of control interrupt finished
+ if (_audiod_fct[func_id].ep_int_ctr == ep_addr)
+ {
+ // According to USB2 specification, maximum payload of interrupt EP is 8 bytes on low speed, 64 bytes on full speed, and 1024 bytes on high speed (but only if an alternate interface other than 0 is used - see specification p. 49)
+ // In case there is nothing to send we have to return a NAK - this is taken care of by PHY ???
+ // In case of an erroneous transmission a retransmission is conducted - this is taken care of by PHY ???
+
+ // I assume here, that things above are handled by PHY
+ // All transmission is done - what remains to do is to inform job was completed
+
+ if (tud_audio_int_ctr_done_cb) TU_VERIFY(tud_audio_int_ctr_done_cb(rhport, (uint16_t) xferred_bytes));
+ }
+
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_IN
+
+ // Data transmission of audio packet finished
+ if (_audiod_fct[func_id].ep_in == ep_addr && _audiod_fct[func_id].alt_setting != 0)
+ {
+ // USB 2.0, section 5.6.4, third paragraph, states "An isochronous endpoint must specify its required bus access period. However, an isochronous endpoint must be prepared to handle poll rates faster than the one specified."
+ // That paragraph goes on to say "An isochronous IN endpoint must return a zero-length packet whenever data is requested at a faster interval than the specified interval and data is not available."
+ // This can only be solved reliably if we load a ZLP after every IN transmission since we can not say if the host requests samples earlier than we declared! Once all samples are collected we overwrite the loaded ZLP.
+
+ // Check if there is data to load into EPs buffer - if not load it with ZLP
+ // Be aware - we as a device are not able to know if the host polls for data with a faster rate as we stated this in the descriptors. Therefore we always have to put something into the EPs buffer. However, once we did that, there is no way of aborting this or replacing what we put into the buffer before!
+ // This is the only place where we can fill something into the EPs buffer!
+
+ // Load new data
+ TU_VERIFY(audiod_tx_done_cb(rhport, &_audiod_fct[func_id]));
+
+ // Transmission of ZLP is done by audiod_tx_done_cb()
+ return true;
+ }
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT
+
+ // New audio packet received
+ if (_audiod_fct[func_id].ep_out == ep_addr)
+ {
+ TU_VERIFY(audiod_rx_done_cb(rhport, &_audiod_fct[func_id], (uint16_t) xferred_bytes));
+ return true;
+ }
+
+
+#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+ // Transmission of feedback EP finished
+ if (_audiod_fct[func_id].ep_fb == ep_addr)
+ {
+ if (tud_audio_fb_done_cb) TU_VERIFY(tud_audio_fb_done_cb(rhport));
+
+ // Schedule a transmit with the new value if EP is not busy
+ if (!usbd_edpt_busy(rhport, _audiod_fct[func_id].ep_fb))
+ {
+ // Schedule next transmission - value is changed bytud_audio_n_fb_set() in the meantime or the old value gets sent
+ return audiod_fb_send(rhport, &_audiod_fct[func_id]);
+ }
+ }
+#endif
+#endif
+ }
+
+ return false;
+}
+
+bool tud_audio_buffer_and_schedule_control_xfer(uint8_t rhport, tusb_control_request_t const * p_request, void* data, uint16_t len)
+{
+ // Handles only sending of data not receiving
+ if (p_request->bmRequestType_bit.direction == TUSB_DIR_OUT) return false;
+
+ // Get corresponding driver index
+ uint8_t func_id;
+ uint8_t itf = TU_U16_LOW(p_request->wIndex);
+
+ // Conduct checks which depend on the recipient
+ switch (p_request->bmRequestType_bit.recipient)
+ {
+ case TUSB_REQ_RCPT_INTERFACE:
+ {
+ uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
+
+ // Verify if entity is present
+ if (entityID != 0)
+ {
+ // Find index of audio driver structure and verify entity really exists
+ TU_VERIFY(audiod_verify_entity_exists(itf, entityID, &func_id));
+ }
+ else
+ {
+ // Find index of audio driver structure and verify interface really exists
+ TU_VERIFY(audiod_verify_itf_exists(itf, &func_id));
+ }
+ }
+ break;
+
+ case TUSB_REQ_RCPT_ENDPOINT:
+ {
+ uint8_t ep = TU_U16_LOW(p_request->wIndex);
+
+ // Find index of audio driver structure and verify EP really exists
+ TU_VERIFY(audiod_verify_ep_exists(ep, &func_id));
+ }
+ break;
+
+ // Unknown/Unsupported recipient
+ default: TU_LOG2(" Unsupported recipient: %d\r\n", p_request->bmRequestType_bit.recipient); TU_BREAKPOINT(); return false;
+ }
+
+ // Crop length
+ if (len > _audiod_fct[func_id].ctrl_buf_sz) len = _audiod_fct[func_id].ctrl_buf_sz;
+
+ // Copy into buffer
+ memcpy((void *)_audiod_fct[func_id].ctrl_buf, data, (size_t)len);
+
+ // Schedule transmit
+ return tud_control_xfer(rhport, p_request, (void*)_audiod_fct[func_id].ctrl_buf, len);
+}
+
+// This helper function finds for a given audio function and AS interface number the index of the attached driver structure, the index of the interface in the audio function
+// (e.g. the std. AS interface with interface number 15 is the first AS interface for the given audio function and thus gets index zero), and
+// finally a pointer to the std. AS interface, where the pointer always points to the first alternate setting i.e. alternate interface zero.
+static bool audiod_get_AS_interface_index(uint8_t itf, audiod_function_t * audio, uint8_t *idxItf, uint8_t const **pp_desc_int)
+{
+ if (audio->p_desc)
+ {
+ // Get pointer at end
+ uint8_t const *p_desc_end = audio->p_desc + audio->desc_length - TUD_AUDIO_DESC_IAD_LEN;
+
+ // Advance past AC descriptors
+ uint8_t const *p_desc = tu_desc_next(audio->p_desc);
+ p_desc += ((audio_desc_cs_ac_interface_t const *)p_desc)->wTotalLength;
+
+ uint8_t tmp = 0;
+ while (p_desc < p_desc_end)
+ {
+ // We assume the number of alternate settings is increasing thus we return the index of alternate setting zero!
+ if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE && ((tusb_desc_interface_t const * )p_desc)->bAlternateSetting == 0)
+ {
+ if (((tusb_desc_interface_t const * )p_desc)->bInterfaceNumber == itf)
+ {
+ *idxItf = tmp;
+ *pp_desc_int = p_desc;
+ return true;
+ }
+ // Increase index, bytes read, and pointer
+ tmp++;
+ }
+ p_desc = tu_desc_next(p_desc);
+ }
+ }
+ return false;
+}
+
+// This helper function finds for a given AS interface number the index of the attached driver structure, the index of the interface in the audio function
+// (e.g. the std. AS interface with interface number 15 is the first AS interface for the given audio function and thus gets index zero), and
+// finally a pointer to the std. AS interface, where the pointer always points to the first alternate setting i.e. alternate interface zero.
+static bool audiod_get_AS_interface_index_global(uint8_t itf, uint8_t *func_id, uint8_t *idxItf, uint8_t const **pp_desc_int)
+{
+ // Loop over audio driver interfaces
+ uint8_t i;
+ for (i = 0; i < CFG_TUD_AUDIO; i++)
+ {
+ if (audiod_get_AS_interface_index(itf, &_audiod_fct[i], idxItf, pp_desc_int))
+ {
+ *func_id = i;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// Verify an entity with the given ID exists and returns also the corresponding driver index
+static bool audiod_verify_entity_exists(uint8_t itf, uint8_t entityID, uint8_t *func_id)
+{
+ uint8_t i;
+ for (i = 0; i < CFG_TUD_AUDIO; i++)
+ {
+ // Look for the correct driver by checking if the unique standard AC interface number fits
+ 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
+
+ while (p_desc < p_desc_end)
+ {
+ if (p_desc[3] == entityID) // Entity IDs are always at offset 3
+ {
+ *func_id = i;
+ return true;
+ }
+ p_desc = tu_desc_next(p_desc);
+ }
+ }
+ }
+ return false;
+}
+
+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)
+ {
+ // 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;
+
+ while (p_desc < p_desc_end)
+ {
+ if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE && ((tusb_desc_interface_t const *)_audiod_fct[i].p_desc)->bInterfaceNumber == itf)
+ {
+ *func_id = i;
+ return true;
+ }
+ p_desc = tu_desc_next(p_desc);
+ }
+ }
+ }
+ return false;
+}
+
+static bool audiod_verify_ep_exists(uint8_t ep, uint8_t *func_id)
+{
+ uint8_t i;
+ for (i = 0; i < CFG_TUD_AUDIO; i++)
+ {
+ if (_audiod_fct[i].p_desc)
+ {
+ // Get pointer at end
+ 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;
+
+ while (p_desc < p_desc_end)
+ {
+ if (tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT && ((tusb_desc_endpoint_t const * )p_desc)->bEndpointAddress == ep)
+ {
+ *func_id = i;
+ return true;
+ }
+ p_desc = tu_desc_next(p_desc);
+ }
+ }
+ }
+ return false;
+}
+
+#if CFG_TUD_AUDIO_ENABLE_ENCODING || CFG_TUD_AUDIO_ENABLE_DECODING
+// p_desc points to the AS interface of alternate setting zero
+// itf is the interface number of the corresponding interface - we check if the interface belongs to EP in or EP out to see if it is a TX or RX parameter
+// Currently, only AS interfaces with an EP (in or out) are supposed to be parsed for!
+static void audiod_parse_for_AS_params(audiod_function_t* audio, uint8_t const * p_desc, uint8_t const * p_desc_end, uint8_t const as_itf)
+{
+ p_desc = tu_desc_next(p_desc); // Exclude standard AS interface descriptor of current alternate interface descriptor
+
+ while (p_desc < p_desc_end)
+ {
+ // Abort if follow up descriptor is a new standard interface descriptor - indicates the last AS descriptor was already finished
+ if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE) break;
+
+ // 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)
+ {
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_EP_OUT
+ if (as_itf != audio->ep_in_as_intf_num && as_itf != audio->ep_out_as_intf_num) break; // Abort loop, this interface has no EP, this driver does not support this currently
+#endif
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_EP_OUT
+ if (as_itf != audio->ep_in_as_intf_num) break;
+#endif
+#if !CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_EP_OUT
+ if (as_itf != audio->ep_out_as_intf_num) break;
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_IN
+ if (as_itf == audio->ep_in_as_intf_num)
+ {
+ 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);
+
+#if CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING
+ audio->format_type_I_tx = (audio_data_format_type_I_t)(((audio_desc_cs_as_interface_t const * )p_desc)->bmFormats);
+#endif
+ }
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT
+ if (as_itf == audio->ep_out_as_intf_num)
+ {
+ audio->n_channels_rx = ((audio_desc_cs_as_interface_t const * )p_desc)->bNrChannels;
+ audio->format_type_rx = ((audio_desc_cs_as_interface_t const * )p_desc)->bFormatType;
+#if CFG_TUD_AUDIO_ENABLE_TYPE_I_DECODING
+ audio->format_type_I_rx = ((audio_desc_cs_as_interface_t const * )p_desc)->bmFormats;
+#endif
+ }
+#endif
+ }
+
+ // Look for a Type I Format Type Descriptor(2.3.1.6 - Audio Formats)
+#if CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING || CFG_TUD_AUDIO_ENABLE_TYPE_I_DECODING
+ 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)
+ {
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_EP_OUT
+ if (as_itf != audio->ep_in_as_intf_num && as_itf != audio->ep_out_as_intf_num) break; // Abort loop, this interface has no EP, this driver does not support this currently
+#endif
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_EP_OUT
+ if (as_itf != audio->ep_in_as_intf_num) break;
+#endif
+#if !CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_EP_OUT
+ if (as_itf != audio->ep_out_as_intf_num) break;
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_IN
+ if (as_itf == audio->ep_in_as_intf_num)
+ {
+ audio->n_bytes_per_sampe_tx = ((audio_desc_type_I_format_t const * )p_desc)->bSubslotSize;
+ }
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT
+ if (as_itf == audio->ep_out_as_intf_num)
+ {
+ audio->n_bytes_per_sampe_rx = ((audio_desc_type_I_format_t const * )p_desc)->bSubslotSize;
+ }
+#endif
+ }
+#endif
+
+ // Other format types are not supported yet
+
+ p_desc = tu_desc_next(p_desc);
+ }
+}
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+
+// Input value feedback has to be in 16.16 format - the format will be converted according to speed settings automatically
+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);
+
+ // Format the feedback value
+#if !TUD_OPT_HIGH_SPEED
+ uint8_t * fb = (uint8_t *) &_audiod_fct[func_id].fb_val;
+
+ // For FS format is 10.14
+ *(fb++) = (feedback >> 2) & 0xFF;
+ *(fb++) = (feedback >> 10) & 0xFF;
+ *(fb++) = (feedback >> 18) & 0xFF;
+ // 4th byte is needed to work correctly with MS Windows
+ *fb = 0;
+#else
+ // For HS format is 16.16 as originally demanded
+ _audiod_fct[func_id].fb_val = feedback;
+#endif
+
+ // Schedule a transmit with the new value if EP is not busy - this triggers repetitive scheduling of the feedback value
+ if (!usbd_edpt_busy(_audiod_fct[func_id].rhport, _audiod_fct[func_id].ep_fb))
+ {
+ return audiod_fb_send(_audiod_fct[func_id].rhport, &_audiod_fct[func_id]);
+ }
+
+ return true;
+}
+#endif
+
+// No security checks here - internal function only which should always succeed
+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;
+}
+
+#endif //TUSB_OPT_DEVICE_ENABLED && CFG_TUD_AUDIO
diff --git a/tinyusb/src/class/audio/audio_device.h b/tinyusb/src/class/audio/audio_device.h
new file mode 100755
index 00000000..5a469523
--- /dev/null
+++ b/tinyusb/src/class/audio/audio_device.h
@@ -0,0 +1,627 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Ha Thach (tinyusb.org)
+ * Copyright (c) 2020 Reinhard Panhuber
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#ifndef _TUSB_AUDIO_DEVICE_H_
+#define _TUSB_AUDIO_DEVICE_H_
+
+#include "audio.h"
+
+//--------------------------------------------------------------------+
+// Class Driver Configuration
+//--------------------------------------------------------------------+
+
+// All sizes are in bytes!
+
+#ifndef CFG_TUD_AUDIO_FUNC_1_DESC_LEN
+#error You must tell the driver the length of the audio function descriptor including IAD descriptor
+#endif
+#if CFG_TUD_AUDIO > 1
+#ifndef CFG_TUD_AUDIO_FUNC_2_DESC_LEN
+#error You must tell the driver the length of the audio function descriptor including IAD descriptor
+#endif
+#endif
+#if CFG_TUD_AUDIO > 2
+#ifndef CFG_TUD_AUDIO_FUNC_3_DESC_LEN
+#error You must tell the driver the length of the audio function descriptor including IAD descriptor
+#endif
+#endif
+
+// 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
+#ifndef CFG_TUD_AUDIO_FUNC_1_N_AS_INT
+#error You must tell the driver the number of Standard AS Interface Descriptors you have defined in the audio function descriptor!
+#endif
+#if CFG_TUD_AUDIO > 1
+#ifndef CFG_TUD_AUDIO_FUNC_2_N_AS_INT
+#error You must tell the driver the number of Standard AS Interface Descriptors you have defined in the audio function descriptor!
+#endif
+#endif
+#if CFG_TUD_AUDIO > 2
+#ifndef CFG_TUD_AUDIO_FUNC_3_N_AS_INT
+#error You must tell the driver the number of Standard AS Interface Descriptors you have defined in the audio function descriptor!
+#endif
+#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
+#ifndef CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ
+#error You must define an audio class control request buffer size!
+#endif
+
+#if CFG_TUD_AUDIO > 1
+#ifndef CFG_TUD_AUDIO_FUNC_2_CTRL_BUF_SZ
+#error You must define an audio class control request buffer size!
+#endif
+#endif
+
+#if CFG_TUD_AUDIO > 2
+#ifndef CFG_TUD_AUDIO_FUNC_3_CTRL_BUF_SZ
+#error You must define an audio class control request buffer size!
+#endif
+#endif
+
+// End point sizes IN BYTES - Limits: Full Speed <= 1023, High Speed <= 1024
+#ifndef CFG_TUD_AUDIO_ENABLE_EP_IN
+#define CFG_TUD_AUDIO_ENABLE_EP_IN 0 // TX
+#endif
+
+#ifndef CFG_TUD_AUDIO_ENABLE_EP_OUT
+#define CFG_TUD_AUDIO_ENABLE_EP_OUT 0 // RX
+#endif
+
+// Maximum EP sizes for all alternate AS interface settings - used for checks and buffer allocation
+#if CFG_TUD_AUDIO_ENABLE_EP_IN
+#ifndef CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX
+#error You must tell the driver the biggest EP IN size!
+#endif
+#if CFG_TUD_AUDIO > 1
+#ifndef CFG_TUD_AUDIO_FUNC_2_EP_IN_SZ_MAX
+#error You must tell the driver the biggest EP IN size!
+#endif
+#endif
+#if CFG_TUD_AUDIO > 2
+#ifndef CFG_TUD_AUDIO_FUNC_3_EP_IN_SZ_MAX
+#error You must tell the driver the biggest EP IN size!
+#endif
+#endif
+#endif // CFG_TUD_AUDIO_ENABLE_EP_IN
+
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT
+#ifndef CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX
+#error You must tell the driver the biggest EP OUT size!
+#endif
+#if CFG_TUD_AUDIO > 1
+#ifndef CFG_TUD_AUDIO_FUNC_2_EP_OUT_SZ_MAX
+#error You must tell the driver the biggest EP OUT size!
+#endif
+#endif
+#if CFG_TUD_AUDIO > 2
+#ifndef CFG_TUD_AUDIO_FUNC_3_EP_OUT_SZ_MAX
+#error You must tell the driver the biggest EP OUT size!
+#endif
+#endif
+#endif // CFG_TUD_AUDIO_ENABLE_EP_OUT
+
+// Software EP FIFO buffer sizes - must be >= max EP SIZEs!
+#ifndef CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ
+#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ 0
+#endif
+#ifndef CFG_TUD_AUDIO_FUNC_2_EP_IN_SW_BUF_SZ
+#define CFG_TUD_AUDIO_FUNC_2_EP_IN_SW_BUF_SZ 0
+#endif
+#ifndef CFG_TUD_AUDIO_FUNC_3_EP_IN_SW_BUF_SZ
+#define CFG_TUD_AUDIO_FUNC_3_EP_IN_SW_BUF_SZ 0
+#endif
+
+#ifndef CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ
+#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ 0
+#endif
+#ifndef CFG_TUD_AUDIO_FUNC_2_EP_OUT_SW_BUF_SZ
+#define CFG_TUD_AUDIO_FUNC_2_EP_OUT_SW_BUF_SZ 0
+#endif
+#ifndef CFG_TUD_AUDIO_FUNC_3_EP_OUT_SW_BUF_SZ
+#define CFG_TUD_AUDIO_FUNC_3_EP_OUT_SW_BUF_SZ 0
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_IN
+#if CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ < CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX
+#error EP software buffer size MUST BE at least as big as maximum EP size
+#endif
+
+#if CFG_TUD_AUDIO > 1
+#if CFG_TUD_AUDIO_FUNC_2_EP_IN_SW_BUF_SZ < CFG_TUD_AUDIO_FUNC_2_EP_IN_SZ_MAX
+#error EP software buffer size MUST BE at least as big as maximum EP size
+#endif
+#endif
+
+#if CFG_TUD_AUDIO > 2
+#if CFG_TUD_AUDIO_FUNC_3_EP_IN_SW_BUF_SZ < CFG_TUD_AUDIO_FUNC_3_EP_IN_SZ_MAX
+#error EP software buffer size MUST BE at least as big as maximum EP size
+#endif
+#endif
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT
+#if CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ < CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX
+#error EP software buffer size MUST BE at least as big as maximum EP size
+#endif
+
+#if CFG_TUD_AUDIO > 1
+#if CFG_TUD_AUDIO_FUNC_2_EP_OUT_SW_BUF_SZ < CFG_TUD_AUDIO_FUNC_2_EP_OUT_SZ_MAX
+#error EP software buffer size MUST BE at least as big as maximum EP size
+#endif
+#endif
+
+#if CFG_TUD_AUDIO > 2
+#if CFG_TUD_AUDIO_FUNC_3_EP_OUT_SW_BUF_SZ < CFG_TUD_AUDIO_FUNC_3_EP_OUT_SZ_MAX
+#error EP software buffer size MUST BE at least as big as maximum EP size
+#endif
+#endif
+#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 - if required - 6 Bytes according to UAC 2 specification (p. 74)
+#endif
+
+#ifndef CFG_TUD_AUDIO_INT_CTR_EP_IN_SW_BUFFER_SIZE
+#define CFG_TUD_AUDIO_INT_CTR_EP_IN_SW_BUFFER_SIZE 6 // Buffer size of audio control interrupt EP - 6 Bytes according to UAC 2 specification (p. 74)
+#endif
+
+// Use software encoding/decoding
+
+// The software coding feature of the driver is not mandatory. It is useful if, for instance, you have two I2S streams which need to be interleaved
+// into a single PCM stream as SAMPLE_1 | SAMPLE_2 | SAMPLE_3 | SAMPLE_4.
+//
+// Currently, only PCM type I encoding/decoding is supported!
+//
+// If the coding feature is to be used, support FIFOs need to be configured. Their sizes and numbers are defined below.
+
+// 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.
+
+// Enable encoding/decodings - for these to work, support FIFOs need to be setup in appropriate numbers and size
+// The actual coding parameters of active AS alternate interface is parsed from the descriptors
+
+// The item size of the FIFO is always fixed to one i.e. bytes! Furthermore, the actively used FIFO depth is reconfigured such that the depth is a multiple of the current sample size in order to avoid samples to get split up in case of a wrap in the FIFO ring buffer (depth = (max_depth / sampe_sz) * sampe_sz)!
+// This is important to remind in case you use DMAs! If the sample sizes changes, the DMA MUST BE RECONFIGURED just like the FIFOs for a different depth!!!
+
+// For PCM encoding/decoding
+
+#ifndef CFG_TUD_AUDIO_ENABLE_ENCODING
+#define CFG_TUD_AUDIO_ENABLE_ENCODING 0
+#endif
+
+#ifndef CFG_TUD_AUDIO_ENABLE_DECODING
+#define CFG_TUD_AUDIO_ENABLE_DECODING 0
+#endif
+
+// This enabling allows to save the current coding parameters e.g. # of bytes per sample etc. - TYPE_I includes common PCM encoding
+#ifndef CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING
+#define CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING 0
+#endif
+
+#ifndef CFG_TUD_AUDIO_ENABLE_TYPE_I_DECODING
+#define CFG_TUD_AUDIO_ENABLE_TYPE_I_DECODING 0
+#endif
+
+// Type I Coding parameters not given within UAC2 descriptors
+// It would be possible to allow for a more flexible setting and not fix this parameter as done below. However, this is most often not needed and kept for later if really necessary. The more flexible setting could be implemented within set_interface(), however, how the values are saved per alternate setting is to be determined!
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_ENCODING && CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING
+#ifndef CFG_TUD_AUDIO_FUNC_1_CHANNEL_PER_FIFO_TX
+#error You must tell the driver the number of channels per FIFO for the interleaved encoding! E.g. for an I2S interface having two channels, CHANNEL_PER_FIFO = 2 as the I2S stream having two channels is usually saved within one FIFO
+#endif
+#if CFG_TUD_AUDIO > 1
+#ifndef CFG_TUD_AUDIO_FUNC_2_CHANNEL_PER_FIFO_TX
+#error You must tell the driver the number of channels per FIFO for the interleaved encoding! E.g. for an I2S interface having two channels, CHANNEL_PER_FIFO = 2 as the I2S stream having two channels is usually saved within one FIFO
+#endif
+#endif
+#if CFG_TUD_AUDIO > 2
+#ifndef CFG_TUD_AUDIO_FUNC_3_CHANNEL_PER_FIFO_TX
+#error You must tell the driver the number of channels per FIFO for the interleaved encoding! E.g. for an I2S interface having two channels, CHANNEL_PER_FIFO = 2 as the I2S stream having two channels is usually saved within one FIFO
+#endif
+#endif
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING && CFG_TUD_AUDIO_ENABLE_TYPE_I_DECODING
+#ifndef CFG_TUD_AUDIO_FUNC_1_CHANNEL_PER_FIFO_RX
+#error You must tell the driver the number of channels per FIFO for the interleaved encoding! E.g. for an I2S interface having two channels, CHANNEL_PER_FIFO = 2 as the I2S stream having two channels is usually saved within one FIFO
+#endif
+#if CFG_TUD_AUDIO > 1
+#ifndef CFG_TUD_AUDIO_FUNC_2_CHANNEL_PER_FIFO_RX
+#error You must tell the driver the number of channels per FIFO for the interleaved encoding! E.g. for an I2S interface having two channels, CHANNEL_PER_FIFO = 2 as the I2S stream having two channels is usually saved within one FIFO
+#endif
+#endif
+#if CFG_TUD_AUDIO > 2
+#ifndef CFG_TUD_AUDIO_FUNC_3_CHANNEL_PER_FIFO_RX
+#error You must tell the driver the number of channels per FIFO for the interleaved encoding! E.g. for an I2S interface having two channels, CHANNEL_PER_FIFO = 2 as the I2S stream having two channels is usually saved within one FIFO
+#endif
+#endif
+#endif
+
+// Remaining types not support so far
+
+// Number of support FIFOs to set up - multiple channels can be handled by one FIFO - very common is two channels per FIFO stemming from one I2S interface
+#ifndef CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO
+#define CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO 0
+#endif
+#ifndef CFG_TUD_AUDIO_FUNC_2_N_TX_SUPP_SW_FIFO
+#define CFG_TUD_AUDIO_FUNC_2_N_TX_SUPP_SW_FIFO 0
+#endif
+#ifndef CFG_TUD_AUDIO_FUNC_3_N_TX_SUPP_SW_FIFO
+#define CFG_TUD_AUDIO_FUNC_3_N_TX_SUPP_SW_FIFO 0
+#endif
+
+#ifndef CFG_TUD_AUDIO_FUNC_1_N_RX_SUPP_SW_FIFO
+#define CFG_TUD_AUDIO_FUNC_1_N_RX_SUPP_SW_FIFO 0
+#endif
+#ifndef CFG_TUD_AUDIO_FUNC_2_N_RX_SUPP_SW_FIFO
+#define CFG_TUD_AUDIO_FUNC_2_N_RX_SUPP_SW_FIFO 0
+#endif
+#ifndef CFG_TUD_AUDIO_FUNC_3_N_RX_SUPP_SW_FIFO
+#define CFG_TUD_AUDIO_FUNC_3_N_RX_SUPP_SW_FIFO 0
+#endif
+
+// Size of support FIFOs IN BYTES - if size > 0 there are as many FIFOs set up as CFG_TUD_AUDIO_FUNC_X_N_TX_SUPP_SW_FIFO and CFG_TUD_AUDIO_FUNC_X_N_RX_SUPP_SW_FIFO
+#ifndef CFG_TUD_AUDIO_FUNC_1_TX_SUPP_SW_FIFO_SZ
+#define CFG_TUD_AUDIO_FUNC_1_TX_SUPP_SW_FIFO_SZ 0 // FIFO size - minimum size: ceil(f_s/1000) * max(# of TX channels) / (# of TX support FIFOs) * max(# of bytes per sample)
+#endif
+#ifndef CFG_TUD_AUDIO_FUNC_2_TX_SUPP_SW_FIFO_SZ
+#define CFG_TUD_AUDIO_FUNC_2_TX_SUPP_SW_FIFO_SZ 0
+#endif
+#ifndef CFG_TUD_AUDIO_FUNC_3_TX_SUPP_SW_FIFO_SZ
+#define CFG_TUD_AUDIO_FUNC_3_TX_SUPP_SW_FIFO_SZ 0
+#endif
+
+#ifndef CFG_TUD_AUDIO_FUNC_1_RX_SUPP_SW_FIFO_SZ
+#define CFG_TUD_AUDIO_FUNC_1_RX_SUPP_SW_FIFO_SZ 0 // FIFO size - minimum size: ceil(f_s/1000) * max(# of RX channels) / (# of RX support FIFOs) * max(# of bytes per sample)
+#endif
+#ifndef CFG_TUD_AUDIO_FUNC_2_RX_SUPP_SW_FIFO_SZ
+#define CFG_TUD_AUDIO_FUNC_2_RX_SUPP_SW_FIFO_SZ 0
+#endif
+#ifndef CFG_TUD_AUDIO_FUNC_3_RX_SUPP_SW_FIFO_SZ
+#define CFG_TUD_AUDIO_FUNC_3_RX_SUPP_SW_FIFO_SZ 0
+#endif
+
+//static_assert(sizeof(tud_audio_desc_lengths) != CFG_TUD_AUDIO, "Supply audio function descriptor pack length!");
+
+// Supported types of this driver:
+// AUDIO_DATA_FORMAT_TYPE_I_PCM - Required definitions: CFG_TUD_AUDIO_N_CHANNELS and CFG_TUD_AUDIO_BYTES_PER_CHANNEL
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** \addtogroup AUDIO_Serial Serial
+ * @{
+ * \defgroup AUDIO_Serial_Device Device
+ * @{ */
+
+//--------------------------------------------------------------------+
+// Application API (Multiple Interfaces)
+// CFG_TUD_AUDIO > 1
+//--------------------------------------------------------------------+
+bool tud_audio_n_mounted (uint8_t func_id);
+
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && !CFG_TUD_AUDIO_ENABLE_DECODING
+uint16_t tud_audio_n_available (uint8_t func_id);
+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); // Delete all content in the EP OUT FIFO
+tu_fifo_t* tud_audio_n_get_ep_out_ff (uint8_t func_id);
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING
+bool tud_audio_n_clear_rx_support_ff (uint8_t func_id, uint8_t ff_idx); // Delete all content in the support RX FIFOs
+uint16_t tud_audio_n_available_support_ff (uint8_t func_id, uint8_t ff_idx);
+uint16_t tud_audio_n_read_support_ff (uint8_t func_id, uint8_t ff_idx, void* buffer, uint16_t bufsize);
+tu_fifo_t* tud_audio_n_get_rx_support_ff (uint8_t func_id, uint8_t ff_idx);
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_ENCODING
+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); // Delete all content in the EP IN FIFO
+tu_fifo_t* tud_audio_n_get_ep_in_ff (uint8_t func_id);
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_ENCODING
+uint16_t tud_audio_n_flush_tx_support_ff (uint8_t func_id); // Force all content in the support TX FIFOs to be written into EP SW FIFO
+bool tud_audio_n_clear_tx_support_ff (uint8_t func_id, uint8_t ff_idx);
+uint16_t tud_audio_n_write_support_ff (uint8_t func_id, uint8_t ff_idx, const void * data, uint16_t len);
+tu_fifo_t* tud_audio_n_get_tx_support_ff (uint8_t func_id, uint8_t ff_idx);
+#endif
+
+#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
+uint16_t tud_audio_int_ctr_n_write (uint8_t func_id, uint8_t const* buffer, uint16_t len);
+#endif
+
+//--------------------------------------------------------------------+
+// Application API (Interface0)
+//--------------------------------------------------------------------+
+
+static inline bool tud_audio_mounted (void);
+
+// RX API
+
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && !CFG_TUD_AUDIO_ENABLE_DECODING
+static inline uint16_t tud_audio_available (void);
+static inline bool 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);
+static inline tu_fifo_t* tud_audio_get_ep_out_ff (void);
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING
+static inline bool tud_audio_clear_rx_support_ff (uint8_t ff_idx);
+static inline uint16_t tud_audio_available_support_ff (uint8_t ff_idx);
+static inline uint16_t tud_audio_read_support_ff (uint8_t ff_idx, void* buffer, uint16_t bufsize);
+static inline tu_fifo_t* tud_audio_get_rx_support_ff (uint8_t ff_idx);
+#endif
+
+// TX API
+
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_ENCODING
+static inline uint16_t tud_audio_write (const void * data, uint16_t len);
+static inline bool tud_audio_clear_ep_in_ff (void);
+static inline tu_fifo_t* tud_audio_get_ep_in_ff (void);
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_ENCODING
+static inline uint16_t tud_audio_flush_tx_support_ff (void);
+static inline uint16_t tud_audio_clear_tx_support_ff (uint8_t ff_idx);
+static inline uint16_t tud_audio_write_support_ff (uint8_t ff_idx, const void * data, uint16_t len);
+static inline tu_fifo_t* tud_audio_get_tx_support_ff (uint8_t ff_idx);
+#endif
+
+// INT CTR API
+
+#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
+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
+// This function is intended to be used if you do not have a persistent buffer or memory location available (e.g. non-local variables) and need to answer onto a
+// get request. This function buffers your answer request frame into the control buffer of the corresponding audio driver and schedules a transmit for sending it.
+// Since transmission is triggered via interrupts, a persistent memory location is required onto which the buffer pointer in pointing. If you already have such
+// available you may directly use 'tud_control_xfer(...)'. In this case data does not need to be copied into an additional buffer and you save some time.
+// If the request's wLength is zero, a status packet is sent instead.
+bool tud_audio_buffer_and_schedule_control_xfer(uint8_t rhport, tusb_control_request_t const * p_request, void* data, uint16_t len);
+
+//--------------------------------------------------------------------+
+// Application Callback API (weak is optional)
+//--------------------------------------------------------------------+
+
+#if CFG_TUD_AUDIO_ENABLE_EP_IN
+TU_ATTR_WEAK bool tud_audio_tx_done_pre_load_cb(uint8_t rhport, uint8_t func_id, 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 func_id, uint8_t ep_in, uint8_t cur_alt_setting);
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT
+TU_ATTR_WEAK bool tud_audio_rx_done_pre_read_cb(uint8_t rhport, uint16_t n_bytes_received, uint8_t func_id, 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_received, uint8_t func_id, uint8_t ep_out, uint8_t cur_alt_setting);
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && 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.
+// (see Universal Serial Bus Specification Revision 2.0 5.12.4.2).
+// Feedback value will be sent at FB endpoint interval till it's changed.
+bool tud_audio_n_fb_set(uint8_t func_id, uint32_t feedback);
+static inline bool tud_audio_fb_set(uint32_t feedback);
+#endif
+
+#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
+TU_ATTR_WEAK bool tud_audio_int_ctr_done_cb(uint8_t rhport, uint16_t n_bytes_copied);
+#endif
+
+// Invoked when audio set interface request received
+TU_ATTR_WEAK bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request);
+
+// Invoked when audio set interface request received which closes an EP
+TU_ATTR_WEAK bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const * p_request);
+
+// Invoked when audio class specific set request received for an EP
+TU_ATTR_WEAK bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff);
+
+// Invoked when audio class specific set request received for an interface
+TU_ATTR_WEAK bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff);
+
+// Invoked when audio class specific set request received for an entity
+TU_ATTR_WEAK bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff);
+
+// Invoked when audio class specific get request received for an EP
+TU_ATTR_WEAK bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_request);
+
+// Invoked when audio class specific get request received for an interface
+TU_ATTR_WEAK bool tud_audio_get_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request);
+
+// Invoked when audio class specific get request received for an entity
+TU_ATTR_WEAK bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const * p_request);
+
+//--------------------------------------------------------------------+
+// Inline Functions
+//--------------------------------------------------------------------+
+
+static inline bool tud_audio_mounted(void)
+{
+ return tud_audio_n_mounted(0);
+}
+
+// RX API
+
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && !CFG_TUD_AUDIO_ENABLE_DECODING
+
+static inline uint16_t tud_audio_available(void)
+{
+ return tud_audio_n_available(0);
+}
+
+static inline uint16_t tud_audio_read(void* buffer, uint16_t bufsize)
+{
+ return tud_audio_n_read(0, buffer, bufsize);
+}
+
+static inline bool tud_audio_clear_ep_out_ff(void)
+{
+ return tud_audio_n_clear_ep_out_ff(0);
+}
+
+static inline tu_fifo_t* tud_audio_get_ep_out_ff(void)
+{
+ return tud_audio_n_get_ep_out_ff(0);
+}
+
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING
+
+static inline bool tud_audio_clear_rx_support_ff(uint8_t ff_idx)
+{
+ return tud_audio_n_clear_rx_support_ff(0, ff_idx);
+}
+
+static inline uint16_t tud_audio_available_support_ff(uint8_t ff_idx)
+{
+ return tud_audio_n_available_support_ff(0, ff_idx);
+}
+
+static inline uint16_t tud_audio_read_support_ff(uint8_t ff_idx, void* buffer, uint16_t bufsize)
+{
+ return tud_audio_n_read_support_ff(0, ff_idx, buffer, bufsize);
+}
+
+static inline tu_fifo_t* tud_audio_get_rx_support_ff(uint8_t ff_idx)
+{
+ return tud_audio_n_get_rx_support_ff(0, ff_idx);
+}
+
+#endif
+
+// TX API
+
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_ENCODING
+
+static inline uint16_t tud_audio_write(const void * data, uint16_t len)
+{
+ return tud_audio_n_write(0, data, len);
+}
+
+static inline bool tud_audio_clear_ep_in_ff(void)
+{
+ return tud_audio_n_clear_ep_in_ff(0);
+}
+
+static inline tu_fifo_t* tud_audio_get_ep_in_ff(void)
+{
+ return tud_audio_n_get_ep_in_ff(0);
+}
+
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_ENCODING
+
+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 ff_idx)
+{
+ return tud_audio_n_clear_tx_support_ff(0, ff_idx);
+}
+
+static inline uint16_t tud_audio_write_support_ff(uint8_t ff_idx, const void * data, uint16_t len)
+{
+ return tud_audio_n_write_support_ff(0, ff_idx, data, len);
+}
+
+static inline tu_fifo_t* tud_audio_get_tx_support_ff(uint8_t ff_idx)
+{
+ return tud_audio_n_get_tx_support_ff(0, ff_idx);
+}
+
+#endif
+
+#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
+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, len);
+}
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+static inline bool tud_audio_fb_set(uint32_t feedback)
+{
+ return tud_audio_n_fb_set(0, feedback);
+}
+#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);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _TUSB_AUDIO_DEVICE_H_ */
+
+/** @} */
+/** @} */