summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorHa Thach <[email protected]>2021-04-16 01:59:47 +0700
committerGitHub <[email protected]>2021-04-16 01:59:47 +0700
commitc611199632b9288eff76c1c4bfdce5d8cc024748 (patch)
tree8ccd5c0b34b4f1eb89276fce866ee34a9ff608e1 /src
parent93dffba0ac905d8525c3092a52888e1a5db96390 (diff)
parentc7e4a8616640165b48cfb0ba4f9346982faa4344 (diff)
Merge pull request #593 from hathach/edpt_ISO_xfer
Edpt iso xfer
Diffstat (limited to 'src')
-rw-r--r--src/class/audio/audio.h68
-rw-r--r--src/class/audio/audio_device.c1631
-rw-r--r--src/class/audio/audio_device.h515
-rw-r--r--src/class/cdc/cdc_device.c4
-rw-r--r--src/class/midi/midi_device.c4
-rw-r--r--src/class/vendor/vendor_device.c4
-rw-r--r--src/common/tusb_fifo.c580
-rw-r--r--src/common/tusb_fifo.h31
-rw-r--r--src/device/dcd.h15
-rw-r--r--src/device/usbd.c37
-rw-r--r--src/device/usbd.h59
-rw-r--r--src/device/usbd_pvt.h3
-rw-r--r--src/osal/osal_pico.h4
-rw-r--r--src/portable/espressif/esp32s2/dcd_esp32s2.c172
-rw-r--r--src/portable/microchip/samg/dcd_samg.c35
-rw-r--r--src/portable/nuvoton/nuc120/dcd_nuc120.c60
-rw-r--r--src/portable/nuvoton/nuc121/dcd_nuc121.c59
-rw-r--r--src/portable/nuvoton/nuc505/dcd_nuc505.c82
-rw-r--r--src/portable/raspberrypi/rp2040/dcd_rp2040.c3
-rw-r--r--src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c160
-rw-r--r--src/portable/st/synopsys/dcd_synopsys.c73
-rw-r--r--src/portable/template/dcd_template.c11
-rw-r--r--src/portable/ti/msp430x5xx/dcd_msp430x5xx.c81
23 files changed, 2797 insertions, 894 deletions
diff --git a/src/class/audio/audio.h b/src/class/audio/audio.h
index 05e61f8df..936f09104 100644
--- a/src/class/audio/audio.h
+++ b/src/class/audio/audio.h
@@ -469,44 +469,28 @@ typedef enum
/// 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;
-
-#define AUDIO_FORMAT_TYPE_UNDEFINED 0x00
-#define AUDIO_FORMAT_TYPE_I 0x01
-#define AUDIO_FORMAT_TYPE_II 0x02
-#define AUDIO_FORMAT_TYPE_III 0x03
-#define AUDIO_FORMAT_TYPE_IV 0x04
-#define AUDIO_EXT_FORMAT_TYPE_I 0x81
-#define AUDIO_EXT_FORMAT_TYPE_II 0x82
-#define AUDIO_EXT_FORMAT_TYPE_III 0x83
-
-/// 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 = 0x100000000,
-//} audio_data_format_type_I_t;
+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;
-#define AUDIO_DATA_FORMAT_TYPE_I_PCM ((uint32_t) (1 << 0))
-#define AUDIO_DATA_FORMAT_TYPE_I_PCM8 ((uint32_t) (1 << 1))
-#define AUDIO_DATA_FORMAT_TYPE_I_IEEE_FLOAT ((uint32_t) (1 << 2))
-#define AUDIO_DATA_FORMAT_TYPE_I_ALAW ((uint32_t) (1 << 3))
-#define AUDIO_DATA_FORMAT_TYPE_I_MULAW ((uint32_t) (1 << 4))
-#define AUDIO_DATA_FORMAT_TYPE_I_RAW_DATA 0x100000000
+// 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 = 0x100000000,
+} audio_data_format_type_I_t;
/// All remaining definitions are taken from the descriptor descriptions in the UAC2 main specification
@@ -901,7 +885,7 @@ typedef struct TU_ATTR_PACKED {
} subrange[numSubRanges] ; \
}
- /// 5.2.3.2 2-byte Control RANGE Parameter Block
+/// 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; \
@@ -912,7 +896,7 @@ typedef struct TU_ATTR_PACKED {
} subrange[numSubRanges]; \
}
- // 5.2.3.3 4-byte Control RANGE Parameter Block
+// 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; \
@@ -923,12 +907,12 @@ typedef struct TU_ATTR_PACKED {
} subrange[numSubRanges]; \
}
- /** @} */
+/** @} */
#ifdef __cplusplus
- }
+}
#endif
#endif
- /** @} */
+/** @} */
diff --git a/src/class/audio/audio_device.c b/src/class/audio/audio_device.c
index 8581925e4..4057f5f64 100644
--- a/src/class/audio/audio_device.c
+++ b/src/class/audio/audio_device.c
@@ -30,6 +30,22 @@
*
* 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"
@@ -47,15 +63,179 @@
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
-#if CFG_TUD_AUDIO_EPSIZE_IN && CFG_TUD_AUDIO_TX_FIFO_SIZE
-#ifndef CFG_TUD_AUDIO_TX_FIFO_COUNT
-#define CFG_TUD_AUDIO_TX_FIFO_COUNT CFG_TUD_AUDIO_N_CHANNELS_TX
+// 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))
+#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
-#if CFG_TUD_AUDIO_EPSIZE_OUT && CFG_TUD_AUDIO_RX_FIFO_SIZE
-#ifndef CFG_TUD_AUDIO_RX_FIFO_COUNT
-#define CFG_TUD_AUDIO_RX_FIFO_COUNT CFG_TUD_AUDIO_N_CHANNELS_RX
+// 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_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_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_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_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_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_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_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_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_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_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_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_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_ALIGN uint8_t ctrl_buf_1[CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ];
+#if CFG_TUD_AUDIO > 1
+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_ALIGN uint8_t ctrl_buf_3[CFG_TUD_AUDIO_FUNC_3_CTRL_BUF_SZ];
+#endif
+
+// Active alternate setting of interfaces
+CFG_TUSB_MEM_ALIGN 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
+CFG_TUSB_MEM_ALIGN 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
+CFG_TUSB_MEM_ALIGN 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_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_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_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_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_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_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
@@ -64,14 +244,15 @@ typedef struct
uint8_t rhport;
uint8_t const * p_desc; // Pointer pointing to Standard AC Interface Descriptor(4.7.1) - Audio Control descriptor defining audio function
-#if CFG_TUD_AUDIO_EPSIZE_IN
- uint8_t ep_in; // Outgoing (out of uC) audio data EP.
- uint16_t epin_buf_cnt; // Count filling status of EP in buffer - this is a shared state currently and is intended to be removed once EP buffers can be implemented as FIFOs!
+#if CFG_TUD_AUDIO_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_EPSIZE_OUT
+#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
@@ -84,59 +265,98 @@ typedef struct
uint8_t ep_int_ctr; // Audio control interrupt EP.
#endif
-#if CFG_TUD_AUDIO_N_AS_INT
- uint8_t altSetting[CFG_TUD_AUDIO_N_AS_INT]; // We need to save the current alternate setting this way, because it is possible that there are AS interfaces which do not have an EP!
-#endif
+ 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!
+
/*------------- From this point, data is not cleared by bus reset -------------*/
+ //
+ uint16_t desc_length; // Length of audio function descriptor
+
// Buffer for control requests
- CFG_TUSB_MEM_ALIGN uint8_t ctrl_buf[CFG_TUD_AUDIO_CTRL_BUF_SIZE];
+ uint8_t * ctrl_buf;
+ uint8_t ctrl_buf_sz;
- // FIFO
-#if CFG_TUD_AUDIO_EPSIZE_IN && CFG_TUD_AUDIO_TX_FIFO_SIZE
- tu_fifo_t tx_ff[CFG_TUD_AUDIO_TX_FIFO_COUNT];
- CFG_TUSB_MEM_ALIGN uint8_t tx_ff_buf[CFG_TUD_AUDIO_TX_FIFO_COUNT][CFG_TUD_AUDIO_TX_FIFO_SIZE];
-#if CFG_FIFO_MUTEX
- osal_mutex_def_t tx_ff_mutex[CFG_TUD_AUDIO_TX_FIFO_COUNT];
-#endif
+ // 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_EPSIZE_OUT && CFG_TUD_AUDIO_RX_FIFO_SIZE
- tu_fifo_t rx_ff[CFG_TUD_AUDIO_RX_FIFO_COUNT];
- CFG_TUSB_MEM_ALIGN uint8_t rx_ff_buf[CFG_TUD_AUDIO_RX_FIFO_COUNT][CFG_TUD_AUDIO_RX_FIFO_SIZE];
-#if CFG_FIFO_MUTEX
- osal_mutex_def_t rx_ff_mutex[CFG_TUD_AUDIO_RX_FIFO_COUNT];
+#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
- tu_fifo_t int_ctr_ff;
- CFG_TUSB_MEM_ALIGN uint8_t int_ctr_ff_buf[CFG_TUD_AUDIO_INT_CTR_BUFSIZE];
-#if CFG_FIFO_MUTEX
- osal_mutex_def_t int_ctr_ff_mutex;
+ 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
- // Endpoint Transfer buffers
-#if CFG_TUD_AUDIO_EPSIZE_OUT
- CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_AUDIO_EPSIZE_OUT]; // Bigger makes no sense for isochronous EP's (but technically possible here)
+ // 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_FEEDBACK_EP
- uint32_t fb_val; // Feedback value for asynchronous mode (in 16.16 format).
+#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_EPSIZE_IN
- CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_AUDIO_EPSIZE_IN]; // Bigger makes no sense for isochronous EP's (but technically possible here)
+#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
-#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
- CFG_TUSB_MEM_ALIGN uint8_t ep_int_ctr_buf[CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN];
+ // 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_interface_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_interface_t, ctrl_buf)
//--------------------------------------------------------------------+
@@ -144,14 +364,20 @@ typedef struct
//--------------------------------------------------------------------+
CFG_TUSB_MEM_SECTION audiod_interface_t _audiod_itf[CFG_TUD_AUDIO];
-extern const uint16_t tud_audio_desc_lengths[];
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT
+static bool audiod_rx_done_cb(uint8_t rhport, audiod_interface_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_interface_t* audio, uint16_t n_bytes_received);
+#endif
-#if CFG_TUD_AUDIO_EPSIZE_OUT
-static bool audio_rx_done_type_I_pcm_ff_cb(uint8_t rhport, audiod_interface_t* audio, uint8_t * buffer, uint16_t bufsize);
+#if CFG_TUD_AUDIO_ENABLE_EP_IN
+static bool audiod_tx_done_cb(uint8_t rhport, audiod_interface_t* audio);
#endif
-#if CFG_TUD_AUDIO_EPSIZE_IN
-static bool audiod_tx_done_type_I_pcm_ff_cb(uint8_t rhport, audiod_interface_t* audio);
+#if CFG_TUD_AUDIO_ENABLE_ENCODING && CFG_TUD_AUDIO_ENABLE_EP_IN
+static uint16_t audiod_encode_type_I_pcm(uint8_t rhport, audiod_interface_t* audio);
#endif
static bool audiod_get_interface(uint8_t rhport, tusb_control_request_t const * p_request);
@@ -162,36 +388,34 @@ static bool audiod_verify_entity_exists(uint8_t itf, uint8_t entityID, uint8_t *
static bool audiod_verify_itf_exists(uint8_t itf, uint8_t *idxDriver);
static bool audiod_verify_ep_exists(uint8_t ep, uint8_t *idxDriver);
+#if CFG_TUD_AUDIO_ENABLE_ENCODING || CFG_TUD_AUDIO_ENABLE_DECODING
+static void audiod_parse_for_AS_params(audiod_interface_t* audio, uint8_t const * p_desc, uint8_t const * p_desc_end, uint8_t const itf);
+#endif
+
+static inline uint8_t tu_desc_subtype(void const* desc)
+{
+ return ((uint8_t const*) desc)[2];
+}
+
bool tud_audio_n_mounted(uint8_t itf)
{
+ TU_VERIFY(itf < CFG_TUD_AUDIO);
audiod_interface_t* audio = &_audiod_itf[itf];
-#if CFG_TUD_AUDIO_EPSIZE_OUT
- if (audio->ep_out == 0)
- {
- return false;
- }
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT
+ if (audio->ep_out == 0) return false;
#endif
-#if CFG_TUD_AUDIO_EPSIZE_IN
- if (audio->ep_in == 0)
- {
- return false;
- }
+#if CFG_TUD_AUDIO_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;
- }
+ if (audio->ep_int_ctr == 0) return false;
#endif
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
- if (audio->ep_fb == 0)
- {
- return false;
- }
+ if (audio->ep_fb == 0) return false;
#endif
return true;
@@ -201,70 +425,68 @@ bool tud_audio_n_mounted(uint8_t itf)
// READ API
//--------------------------------------------------------------------+
-#if CFG_TUD_AUDIO_EPSIZE_OUT && CFG_TUD_AUDIO_RX_FIFO_SIZE
-#if CFG_TUD_AUDIO_RX_FIFO_COUNT > 1
-uint16_t tud_audio_n_available(uint8_t itf, uint8_t channelId)
-{
- TU_VERIFY(channelId < CFG_TUD_AUDIO_N_CHANNELS_RX);
- return tu_fifo_count(&_audiod_itf[itf].rx_ff[channelId]);
-}
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && !CFG_TUD_AUDIO_ENABLE_DECODING
-uint16_t tud_audio_n_read(uint8_t itf, uint8_t channelId, void* buffer, uint16_t bufsize)
-{
- TU_VERIFY(channelId < CFG_TUD_AUDIO_N_CHANNELS_RX);
- return tu_fifo_read_n(&_audiod_itf[itf].rx_ff[channelId], buffer, bufsize);
-}
-
-void tud_audio_n_read_flush (uint8_t itf, uint8_t channelId)
-{
- TU_VERIFY(channelId < CFG_TUD_AUDIO_N_CHANNELS_RX, );
- tu_fifo_clear(&_audiod_itf[itf].rx_ff[channelId]);
-}
-#else
uint16_t tud_audio_n_available(uint8_t itf)
{
- return tu_fifo_count(&_audiod_itf[itf].rx_ff[0]);
+ TU_VERIFY(itf < CFG_TUD_AUDIO && _audiod_itf[itf].p_desc != NULL);
+ return tu_fifo_count(&_audiod_itf[itf].ep_out_ff);
}
uint16_t tud_audio_n_read(uint8_t itf, void* buffer, uint16_t bufsize)
{
- return tu_fifo_read_n(&_audiod_itf[itf].rx_ff[0], buffer, bufsize);
+ TU_VERIFY(itf < CFG_TUD_AUDIO && _audiod_itf[itf].p_desc != NULL);
+ return tu_fifo_read_n(&_audiod_itf[itf].ep_out_ff, buffer, bufsize);
}
-void tud_audio_n_read_flush (uint8_t itf)
+bool tud_audio_n_clear_ep_out_ff(uint8_t itf)
{
- tu_fifo_clear(&_audiod_itf[itf].rx_ff[0]);
+ TU_VERIFY(itf < CFG_TUD_AUDIO && _audiod_itf[itf].p_desc != NULL);
+ return tu_fifo_clear(&_audiod_itf[itf].ep_out_ff);
}
-#endif
-#endif
-#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
+#endif
-uint16_t tud_audio_int_ctr_n_available(uint8_t itf)
+#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 itf, uint8_t channelId)
{
- return tu_fifo_count(&_audiod_itf[itf].int_ctr_ff);
+ TU_VERIFY(itf < CFG_TUD_AUDIO && _audiod_itf[itf].p_desc != NULL, channelId < _audiod_itf[itf].n_rx_supp_ff);
+ return tu_fifo_clear(&_audiod_itf[itf].rx_supp_ff[channelId]);
}
-uint16_t tud_audio_int_ctr_n_read(uint8_t itf, void* buffer, uint16_t bufsize)
+uint16_t tud_audio_n_available_support_ff(uint8_t itf, uint8_t channelId)
{
- return tu_fifo_read_n(&_audiod_itf[itf].int_ctr_ff, buffer, bufsize);
+ TU_VERIFY(itf < CFG_TUD_AUDIO && _audiod_itf[itf].p_desc != NULL, channelId < _audiod_itf[itf].n_rx_supp_ff);
+ return tu_fifo_count(&_audiod_itf[itf].rx_supp_ff[channelId]);
}
-void tud_audio_int_ctr_n_read_flush (uint8_t itf)
+uint16_t tud_audio_n_read_support_ff(uint8_t itf, uint8_t channelId, void* buffer, uint16_t bufsize)
{
- tu_fifo_clear(&_audiod_itf[itf].int_ctr_ff);
+ TU_VERIFY(itf < CFG_TUD_AUDIO && _audiod_itf[itf].p_desc != NULL, channelId < _audiod_itf[itf].n_rx_supp_ff);
+ return tu_fifo_read_n(&_audiod_itf[itf].rx_supp_ff[channelId], buffer, bufsize);
}
-
#endif
-// This function is called once something is received by USB and is responsible for decoding received stream into audio channels.
-// If you prefer your own (more efficient) implementation suiting your purpose set CFG_TUD_AUDIO_RX_FIFO_SIZE = 0.
+// 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_EPSIZE_OUT
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT
-static bool audio_rx_done_cb(uint8_t rhport, audiod_interface_t* audio, uint8_t* buffer, uint16_t bufsize)
+static bool audiod_rx_done_cb(uint8_t rhport, audiod_interface_t* audio, uint16_t n_bytes_received)
{
- switch (CFG_TUD_AUDIO_FORMAT_TYPE_RX)
+ uint8_t idxDriver, idxItf;
+ uint8_t const *dummy2;
+
+ // Find index of audio streaming interface and index of interface
+ TU_VERIFY(audiod_get_AS_interface_index(audio->ep_out_as_intf_num, &idxDriver, &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, idxDriver, 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!
@@ -274,15 +496,10 @@ static bool audio_rx_done_cb(uint8_t rhport, audiod_interface_t* audio, uint8_t*
case AUDIO_FORMAT_TYPE_I:
- switch (CFG_TUD_AUDIO_FORMAT_TYPE_I_RX)
+ switch (audio->format_type_I_tx)
{
case AUDIO_DATA_FORMAT_TYPE_I_PCM:
-
-#if CFG_TUD_AUDIO_RX_FIFO_SIZE
- TU_VERIFY(audio_rx_done_type_I_pcm_ff_cb(rhport, audio, buffer, bufsize));
-#else
-#error YOUR DECODING AND BUFFERING IS REQUIRED HERE!
-#endif
+ TU_VERIFY(audiod_decode_type_I_pcm(rhport, audio, n_bytes_received));
break;
default:
@@ -300,75 +517,159 @@ static bool audio_rx_done_cb(uint8_t rhport, audiod_interface_t* audio, uint8_t*
break;
}
- // Call a weak callback here - a possibility for user to get informed RX was completed
- if (tud_audio_rx_done_cb) TU_VERIFY(tud_audio_rx_done_cb(rhport, buffer, bufsize));
+ // 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_iso_xfer(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, idxDriver, audio->ep_out, audio->alt_setting[idxItf]));
return true;
}
-#endif //CFG_TUD_AUDIO_EPSIZE_OUT
+#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
-// The following functions are used in case CFG_TUD_AUDIO_RX_FIFO_SIZE != 0
-#if CFG_TUD_AUDIO_RX_FIFO_SIZE
-#if CFG_TUD_AUDIO_RX_FIFO_COUNT > 1
-static bool audio_rx_done_type_I_pcm_ff_cb(uint8_t rhport, audiod_interface_t* audio, uint8_t * buffer, uint16_t bufsize)
+// 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)
{
- (void) rhport;
- // We expect to get a multiple of CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX * CFG_TUD_AUDIO_N_CHANNELS_RX per channel
- if (bufsize % (CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX * CFG_TUD_AUDIO_N_CHANNELS_RX) != 0)
- {
- return false;
- }
+ // 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;
+ // }
- uint8_t chId = 0;
- uint16_t cnt;
-#if CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX == 1
- uint8_t sample = 0;
-#elif CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX == 2
- uint16_t sample = 0;
-#else
- uint32_t sample = 0;
-#endif
+ // Optimize for fast half word copies
+ typedef struct{
+ uint16_t val;
+ } __attribute((__packed__)) unaligned_uint16_t;
- for(cnt = 0; cnt < bufsize; cnt += CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX)
+ // Optimize for fast word copies
+ typedef struct{
+ uint32_t val;
+ } __attribute((__packed__)) unaligned_uint32_t;
+
+ switch (nBytesToCopy)
{
- // Let alignment problems be handled by memcpy
- memcpy(&sample, &buffer[cnt], CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX);
- if(tu_fifo_write_n(&audio->rx_ff[chId++], &sample, CFG_TUD_AUDIO_RX_ITEMSIZE) != CFG_TUD_AUDIO_RX_ITEMSIZE)
- {
- // Buffer overflow
- return false;
- }
+ case 1:
+ while((uint8_t *)dst < dst_end)
+ {
+ *(uint8_t *)dst++ = *src;
+ src += n_ff_used;
+ }
+ break;
- if (chId == CFG_TUD_AUDIO_N_CHANNELS_RX)
- {
- chId = 0;
- }
+ 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 true;
+
+ return src;
}
-#else
-static bool audio_rx_done_type_I_pcm_ff_cb(uint8_t rhport, audiod_interface_t *audio, uint8_t *buffer, uint16_t bufsize)
+
+static bool audiod_decode_type_I_pcm(uint8_t rhport, audiod_interface_t* audio, uint16_t n_bytes_received)
{
(void) rhport;
- // We expect to get a multiple of CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX * CFG_TUD_AUDIO_N_CHANNELS_RX per channel
- if (bufsize % (CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX * CFG_TUD_AUDIO_N_CHANNELS_RX) != 0)
+ // Determine amount of samples
+ uint8_t const n_ff_used = audio->n_ff_used_rx;
+ uint16_t const nBytesToCopy = audio->n_channels_per_ff_rx * audio->n_bytes_per_sampe_rx;
+ uint16_t const nBytesPerFFToRead = n_bytes_received / n_ff_used;
+ uint8_t cnt_ff;
+
+ // Decode
+ void * dst;
+ uint8_t * src;
+ uint8_t * dst_end;
+ uint16_t len;
+
+ for (cnt_ff = 0; cnt_ff < n_ff_used; cnt_ff++)
{
- return false;
+ src = &audio->lin_buf_out[cnt_ff*audio->n_channels_per_ff_rx * audio->n_bytes_per_sampe_rx];
+
+ len = tu_fifo_get_linear_write_info(&audio->rx_supp_ff[cnt_ff], 0, &dst, nBytesPerFFToRead);
+ tu_fifo_advance_write_pointer(&audio->rx_supp_ff[cnt_ff], len);
+
+ dst_end = dst + len;
+
+ src = audiod_interleaved_copy_bytes_fast_decode(nBytesToCopy, dst, dst_end, src, n_ff_used);
+
+ // Handle wrapped part of FIFO
+ if (len < nBytesPerFFToRead)
+ {
+ len = tu_fifo_get_linear_write_info(&audio->rx_supp_ff[cnt_ff], 0, &dst, nBytesPerFFToRead - len);
+ tu_fifo_advance_write_pointer(&audio->rx_supp_ff[cnt_ff], len);
+
+ dst_end = dst + len;
+
+ audiod_interleaved_copy_bytes_fast_decode(nBytesToCopy, dst, dst_end, src, n_ff_used);
+ }
}
- tu_fifo_write_n(&audio->rx_ff[0], buffer, bufsize);
+ // 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_RX_FIFO_COUNT > 1
-#endif //CFG_TUD_AUDIO_RX_FIFO_SIZE
+#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
*
@@ -380,137 +681,118 @@ static bool audio_rx_done_type_I_pcm_ff_cb(uint8_t rhport, audiod_interface_t *a
* \param[in] len: # of array elements to copy
* \return Number of bytes actually written
*/
-#if CFG_TUD_AUDIO_EPSIZE_IN
-#if !CFG_TUD_AUDIO_TX_FIFO_SIZE
-/* This function is intended for later use once EP buffers (at least for ISO EPs) are implemented as ring buffers
-uint16_t tud_audio_n_write_ep_in_buffer(uint8_t itf, const void * data, uint16_t len)
+uint16_t tud_audio_n_write(uint8_t itf, const void * data, uint16_t len)
{
- audiod_interface_t* audio = &_audiod_itf[itf];
- if (audio->p_desc == NULL) {
- return 0;
- }
+ TU_VERIFY(itf < CFG_TUD_AUDIO && _audiod_itf[itf].p_desc != NULL);
+ return tu_fifo_write_n(&_audiod_itf[itf].ep_in_ff, data, len);
+}
- // THIS IS A CRITICAL SECTION - audio->epin_buf_cnt MUST NOT BE MODIFIED FROM HERE - happens if audiod_tx_done_cb() is executed in between!
+bool tud_audio_n_clear_ep_in_ff(uint8_t itf) // Delete all content in the EP IN FIFO
+{
+ TU_VERIFY(itf < CFG_TUD_AUDIO && _audiod_itf[itf].p_desc != NULL);
+ return tu_fifo_clear(&_audiod_itf[itf].ep_in_ff);
+}
- // FOR SINGLE THREADED OPERATION:
- // AS LONG AS THIS FUNCTION IS NOT EXECUTED WITHIN AN INTERRUPT ALL IS FINE!
+#endif
- // Determine free space
- uint16_t free = CFG_TUD_AUDIO_EPSIZE_IN - audio->epin_buf_cnt;
+#if CFG_TUD_AUDIO_ENABLE_ENCODING && CFG_TUD_AUDIO_ENABLE_EP_IN
+uint16_t tud_audio_n_flush_tx_support_ff(uint8_t itf) // Force all content in the support TX FIFOs to be written into linear buffer and schedule a transmit
+{
+ TU_VERIFY(itf < CFG_TUD_AUDIO && _audiod_itf[itf].p_desc != NULL);
+ audiod_interface_t* audio = &_audiod_itf[itf];
- // Clip length if needed
- if (len > free) len = free;
+ uint16_t n_bytes_copied = tu_fifo_count(&audio->tx_supp_ff[0]);
- // Write data
- memcpy((void *) &audio->epin_buf[audio->epin_buf_cnt], data, len);
+ TU_VERIFY(audiod_tx_done_cb(audio->rhport, audio));
- audio->epin_buf_cnt += len;
+ 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 number of bytes written
- return len;
+ return n_bytes_copied;
}
-*/
-
-#else
-#if CFG_TUD_AUDIO_TX_FIFO_COUNT == 1
-uint16_t tud_audio_n_write(uint8_t itf, void const* data, uint16_t len)
+bool tud_audio_n_clear_tx_support_ff(uint8_t itf, uint8_t channelId)
{
- {
- audiod_interface_t* audio = &_audiod_itf[itf];
- if (audio->p_desc == NULL)
- {
- return 0;
- }
- return tu_fifo_write_n(&audio->tx_ff[0], data, len);
- }
+ TU_VERIFY(itf < CFG_TUD_AUDIO && _audiod_itf[itf].p_desc != NULL, channelId < _audiod_itf[itf].n_tx_supp_ff);
+ return tu_fifo_clear(&_audiod_itf[itf].tx_supp_ff[channelId]);
}
-#else
-uint16_t tud_audio_n_write(uint8_t itf, uint8_t channelId, const void * data, uint16_t len)
-{
- audiod_interface_t* audio = &_audiod_itf[itf];
- if (audio->p_desc == NULL) {
- return 0;
- }
- return tu_fifo_write_n(&audio->tx_ff[channelId], data, len);
+uint16_t tud_audio_n_write_support_ff(uint8_t itf, uint8_t channelId, const void * data, uint16_t len)
+{
+ TU_VERIFY(itf < CFG_TUD_AUDIO && _audiod_itf[itf].p_desc != NULL, channelId < _audiod_itf[itf].n_tx_supp_ff);
+ return tu_fifo_write_n(&_audiod_itf[itf].tx_supp_ff[channelId], data, len);
}
#endif
-static bool audiod_tx_done_cb(uint8_t rhport, audiod_interface_t* audio, uint16_t * n_bytes_copied);
-uint16_t tud_audio_n_write_flush(uint8_t itf)
+#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 itf, uint8_t const* buffer, uint16_t len)
{
- audiod_interface_t *audio = &_audiod_itf[itf];
- if (audio->p_desc == NULL) {
- return 0;
- }
+ TU_VERIFY(itf < CFG_TUD_AUDIO && _audiod_itf[itf].p_desc != NULL);
- uint16_t n_bytes_copied;
- TU_VERIFY(audiod_tx_done_cb(audio->rhport, audio, &n_bytes_copied));
- return n_bytes_copied;
-}
+ // We write directly into the EP's buffer - abort if previous transfer not complete
+ TU_VERIFY(!usbd_edpt_busy(_audiod_itf[itf].rhport, _audiod_itf[itf].ep_int_ctr));
-#endif
-#endif
+ // Check length
+ TU_VERIFY(len <= CFG_TUD_AUDIO_INT_CTR_EP_IN_SW_BUFFER_SIZE);
-#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN > 0
-uint32_t tud_audio_int_ctr_n_write(uint8_t itf, uint8_t const* buffer, uint32_t bufsize)
-{
- audiod_interface_t* audio = &_audiod_itf[itf];
- if (audio->p_desc == NULL) {
- return 0;
- }
+ memcpy(_audiod_itf[itf].ep_int_ctr_buf, buffer, len);
- return tu_fifo_write_n(&audio->int_ctr_ff, buffer, bufsize);
+ // Schedule transmit
+ TU_VERIFY(usbd_edpt_xfer(_audiod_itf[itf].rhport, _audiod_itf[itf].ep_int_ctr, _audiod_itf[itf].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_TX_FIFO_SIZE = 0 and use tud_audio_n_write_ep_in_buffer() (NOT IMPLEMENTED SO FAR).
+// If you prefer your own (more efficient) implementation suiting your purpose set CFG_TUD_AUDIO_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_EPSIZE_IN
-static bool audiod_tx_done_cb(uint8_t rhport, audiod_interface_t* audio, uint16_t * n_bytes_copied)
+#if CFG_TUD_AUDIO_ENABLE_EP_IN
+static bool audiod_tx_done_cb(uint8_t rhport, audiod_interface_t * audio)
{
uint8_t idxDriver, idxItf;
uint8_t const *dummy2;
- // If a callback is used determine current alternate setting of
- if (tud_audio_tx_done_pre_load_cb || tud_audio_tx_done_post_load_cb)
- {
- // Find index of audio streaming interface and index of interface
- TU_VERIFY(audiod_get_AS_interface_index(audio->ep_in_as_intf_num, &idxDriver, &idxItf, &dummy2));
- }
+ // If a callback is used determine current alternate setting of - find index of audio streaming interface and index of interface
+ if (tud_audio_tx_done_pre_load_cb || tud_audio_tx_done_post_load_cb) TU_VERIFY(audiod_get_AS_interface_index(audio->ep_in_as_intf_num, &idxDriver, &idxItf, &dummy2));
// 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, idxDriver, audio->ep_in, audio->altSetting[idxItf]));
+ // if no FIFOs are used the user may use this call back to load its data into the EP IN buffer by use of tud_audio_n_write_ep_in_buffer().
+ if (tud_audio_tx_done_pre_load_cb) TU_VERIFY(tud_audio_tx_done_pre_load_cb(rhport, idxDriver, audio->ep_in, audio->alt_setting[idxItf]));
-#if CFG_TUD_AUDIO_TX_FIFO_SIZE
- switch (CFG_TUD_AUDIO_FORMAT_TYPE_TX)
+ // 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 (CFG_TUD_AUDIO_FORMAT_TYPE_I_TX)
+ switch (audio->format_type_I_tx)
{
case AUDIO_DATA_FORMAT_TYPE_I_PCM:
- TU_VERIFY(audiod_tx_done_type_I_pcm_ff_cb(rhport, audio));
-
+ 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;
@@ -519,199 +801,186 @@ static bool audiod_tx_done_cb(uint8_t rhport, audiod_interface_t* audio, uint16_
// 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;
}
-#endif
-
- // THIS IS A CRITICAL SECTION - audio->epin_buf_cnt MUST NOT BE MODIFIED FROM HERE - happens if tud_audio_n_write_ep_in_buffer() is executed in between!
- // THIS IS NOT SOLVED SO FAR!
+ TU_VERIFY(usbd_edpt_xfer(rhport, audio->ep_in, audio->lin_buf_in, n_bytes_tx));
- // FOR SINGLE THREADED OPERATION:
- // THIS FUNCTION IS NOT EXECUTED WITHIN AN INTERRUPT SO IT DOES NOT INTERRUPT tud_audio_n_write_ep_in_buffer()! AS LONG AS tud_audio_n_write_ep_in_buffer() IS NOT EXECUTED WITHIN AN INTERRUPT ALL IS FINE!
-
- // Schedule transmit
- TU_VERIFY(usbd_edpt_xfer(rhport, audio->ep_in, audio->epin_buf, audio->epin_buf_cnt));
+#else
+ // No support FIFOs, if no linear buffer required schedule transmit, else put data into linear buffer and schedule
- // Inform how many bytes were copied
- *n_bytes_copied = audio->epin_buf_cnt;
+ 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
- // Declare EP in buffer empty
- audio->epin_buf_cnt = 0;
+#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_iso_xfer(rhport, audio->ep_in, &audio->ep_in_ff, n_bytes_tx));
+#endif
- // TO HERE
+#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_copied, idxDriver, audio->ep_in, audio->altSetting[idxItf]));
+ if (tud_audio_tx_done_post_load_cb) TU_VERIFY(tud_audio_tx_done_post_load_cb(rhport, n_bytes_tx, idxDriver, audio->ep_in, audio->alt_setting[idxItf]));
return true;
}
-#endif //CFG_TUD_AUDIO_EPSIZE_IN
+#endif //CFG_TUD_AUDIO_ENABLE_EP_IN
-#if CFG_TUD_AUDIO_TX_FIFO_SIZE
-#if CFG_TUD_AUDIO_TX_FIFO_COUNT > 1 || (CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX != CFG_TUD_AUDIO_TX_ITEMSIZE)
-static bool audiod_tx_done_type_I_pcm_ff_cb(uint8_t rhport, audiod_interface_t* audio)
-{
- // We encode directly into IN EP's buffer - abort if previous transfer not complete
- TU_VERIFY(!usbd_edpt_busy(rhport, audio->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
- // Determine amount of samples
- uint16_t const nEndpointSampleCapacity = CFG_TUD_AUDIO_EPSIZE_IN / CFG_TUD_AUDIO_N_CHANNELS_TX / CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX;
- uint16_t nSamplesPerChannelToSend = tu_fifo_count(&audio->tx_ff[0]) / CFG_TUD_AUDIO_TX_ITEMSIZE;
- uint16_t nBytesToSend;
- uint8_t cntChannel;
+/* 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)
+ */
- for (cntChannel = 1; cntChannel < CFG_TUD_AUDIO_N_CHANNELS_TX; cntChannel++)
- {
- uint16_t const count = tu_fifo_count(&audio->tx_ff[cntChannel]);
- if (count / CFG_TUD_AUDIO_TX_ITEMSIZE < nSamplesPerChannelToSend)
- {
- nSamplesPerChannelToSend = count * CFG_TUD_AUDIO_TX_ITEMSIZE;
- }
- }
+/*
+ * 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.
+ * */
- // Check if there is enough
- if (nSamplesPerChannelToSend == 0)
+// Helper function
+static inline uint8_t * audiod_interleaved_copy_bytes_fast_encode(uint16_t const nBytesToCopy, void * 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)
{
- audio->epin_buf_cnt = 0;
- return true;
- }
+ case 1:
+ while((uint8_t *)src < src_end)
+ {
+ *dst = *(uint8_t *)src++;
+ dst += n_ff_used;
+ }
+ break;
- // Limit to maximum sample number - THIS IS A POSSIBLE ERROR SOURCE IF TOO MANY SAMPLE WOULD NEED TO BE SENT BUT CAN NOT!
- nSamplesPerChannelToSend = tu_min16(nSamplesPerChannelToSend, nEndpointSampleCapacity);
- nBytesToSend = nSamplesPerChannelToSend * CFG_TUD_AUDIO_N_CHANNELS_TX * CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX;
+ case 2:
+ while((uint8_t *)src < src_end)
+ {
+ *(unaligned_uint16_t*)dst = *(unaligned_uint16_t*)src;
+ src += 2;
+ dst += 2 * n_ff_used;
+ }
+ break;
- // Encode
- uint16_t cntSample;
- uint8_t * pBuff = audio->epin_buf;
-#if CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX == 1
- uint8_t sample;
-#elif CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX == 2
- uint16_t sample;
-#else
- uint32_t sample;
-#endif
+ case 3:
+ while((uint8_t *)src < src_end)
+ {
+ // memcpy(dst, src, 3);
+ // src = (uint8_t *)src + 3;
+ // dst += 3 * n_ff_used;
- // TODO: Big endianess handling
- for (cntSample = 0; cntSample < nSamplesPerChannelToSend; cntSample++)
- {
- for (cntChannel = 0; cntChannel < CFG_TUD_AUDIO_N_CHANNELS_TX; cntChannel++)
- {
- // Get sample from buffer
- tu_fifo_read_n(&audio->tx_ff[cntChannel], &sample, CFG_TUD_AUDIO_TX_ITEMSIZE);
+ // TODO: Is there a faster way to copy 3 bytes?
+ *dst++ = *(uint8_t *)src++;
+ *dst++ = *(uint8_t *)src++;
+ *dst++ = *(uint8_t *)src++;
- // Put it into EP's buffer - Let alignment problems be handled by memcpy
- memcpy(pBuff, &sample, CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX);
+ dst += 3 * (n_ff_used - 1);
+ }
+ break;
- // Advance pointer
- pBuff += CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX;
- }
+ case 4:
+ while((uint8_t *)src < src_end)
+ {
+ *(unaligned_uint32_t*)dst = *(unaligned_uint32_t*)src;
+ src += 4;
+ dst += 4 * n_ff_used;
+ }
+ break;
}
- audio->epin_buf_cnt = nBytesToSend;
-
- return true;
+ return dst;
}
-#else
-static bool audiod_tx_done_type_I_pcm_ff_cb(uint8_t rhport, audiod_interface_t* audio)
+static uint16_t audiod_encode_type_I_pcm(uint8_t rhport, audiod_interface_t* audio)
{
- // We encode directly into IN EP's buffer - abort if previous transfer not complete
+ // 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
- uint16_t nByteCount = tu_fifo_count(&audio->tx_ff[0]);
-
- nByteCount = tu_min16(nByteCount, CFG_TUD_AUDIO_EPSIZE_IN);
+ 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;
- // Check if there is enough
- if (nByteCount == 0)
+ for (cnt_ff = 1; cnt_ff < n_ff_used; cnt_ff++)
{
- return true;
+ uint16_t const count = tu_fifo_count(&audio->tx_supp_ff[cnt_ff]);
+ if (count < nBytesPerFFToSend)
+ {
+ nBytesPerFFToSend = count;
+ }
}
- nByteCount = tu_fifo_read_n(&audio->tx_ff[0], audio->epin_buf, nByteCount);
- audio->epin_buf_cnt = nByteCount;
-
- return true;
-}
-#endif // CFG_TUD_AUDIO_TX_FIFO_COUNT > 1 || (CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX != CFG_TUD_AUDIO_TX_ITEMSIZE)
+ // Check if there is enough
+ if (nBytesPerFFToSend == 0) return 0;
-#endif //CFG_TUD_AUDIO_TX_FIFO_SIZE
+ // 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);
-// This function is called once a transmit of an feedback packet was successfully completed. Here, we get the next feedback value to be sent
+ // Round to full number of samples (flooring)
+ nBytesPerFFToSend = (nBytesPerFFToSend / nBytesToCopy) * nBytesToCopy;
-#if CFG_TUD_AUDIO_EPSIZE_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
-static bool audio_fb_send(uint8_t rhport, audiod_interface_t *audio)
-{
- uint8_t fb[4];
+ // Encode
+ void * src;
+ uint8_t * dst;
+ uint8_t * src_end;
uint16_t len;
- if (audio->fb_val == 0)
- {
- len = 0;
- return true;
- }
- else
+ for (cnt_ff = 0; cnt_ff < n_ff_used; cnt_ff++)
{
- len = 4;
- // Here we need to return the feedback value
- if (rhport == 0)
- {
- // For FS format is 10.14
- fb[0] = (audio->fb_val >> 2) & 0xFF;
- fb[1] = (audio->fb_val >> 10) & 0xFF;
- fb[2] = (audio->fb_val >> 18) & 0xFF;
- // 4th byte is needed to work correctly with MS Windows
- fb[3] = 0;
- }
- else
- {
- // For HS format is 16.16
- fb[0] = (audio->fb_val >> 0) & 0xFF;
- fb[1] = (audio->fb_val >> 8) & 0xFF;
- fb[2] = (audio->fb_val >> 16) & 0xFF;
- fb[3] = (audio->fb_val >> 24) & 0xFF;
- }
- return usbd_edpt_xfer(rhport, audio->ep_fb, fb, len);
- }
+ dst = &audio->lin_buf_in[cnt_ff*audio->n_channels_per_ff_tx*audio->n_bytes_per_sampe_tx];
-}
-
-//static uint16_t audio_fb_done_cb(uint8_t rhport, audiod_interface_t* audio)
-//{
-// (void) rhport;
-// (void) audio;
-//
-// if (tud_audio_fb_done_cb) TU_VERIFY(tud_audio_fb_done_cb(rhport));
-// return 0;
-//}
+ len = tu_fifo_get_linear_read_info(&audio->tx_supp_ff[cnt_ff], 0, &src, nBytesPerFFToSend);
+ tu_fifo_advance_read_pointer(&audio->tx_supp_ff[cnt_ff], len);
-#endif
+ src_end = src + len;
-// This function is called once a transmit of an interrupt control packet was successfully completed. Here, we get the remaining bytes to send
+ dst = audiod_interleaved_copy_bytes_fast_encode(nBytesToCopy, src, src_end, dst, n_ff_used);
-#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
-static bool audio_int_ctr_done_cb(uint8_t rhport, audiod_interface_t* audio, uint16_t * n_bytes_copied)
-{
- // We write directly into the EP's buffer - abort if previous transfer not complete
- TU_VERIFY(!usbd_edpt_busy(rhport, audio->ep_int_ctr));
+ // Handle wrapped part of FIFO
+ if (len < nBytesPerFFToSend)
+ {
+ len = tu_fifo_get_linear_read_info(&audio->tx_supp_ff[cnt_ff], 0, &src, nBytesPerFFToSend - len);
+ tu_fifo_advance_read_pointer(&audio->tx_supp_ff[cnt_ff], len);
- // TODO: Big endianess handling
- uint16_t cnt = tu_fifo_read_n(audio->int_ctr_ff, audio->ep_int_ctr_buf, CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN);
+ src_end = src + len;
- if (cnt > 0)
- {
- // Schedule transmit
- TU_VERIFY(usbd_edpt_xfer(rhport, audio->ep_int_ctr, audio->ep_int_ctr_buf, cnt));
+ audiod_interleaved_copy_bytes_fast_encode(nBytesToCopy, src, src_end, dst, n_ff_used);
+ }
}
- *n_bytes_copied = cnt;
+ return nBytesPerFFToSend * n_ff_used;
+}
+#endif //CFG_TUD_AUDIO_ENABLE_ENCODING
- if (tud_audio_int_ctr_done_cb) TU_VERIFY(tud_audio_int_ctr_done_cb(rhport, n_bytes_copied));
+// This function is called once a transmit of a feedback packet was successfully completed. Here, we get the next feedback value to be sent
- return true;
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+static inline bool audiod_fb_send(uint8_t rhport, audiod_interface_t *audio)
+{
+ return usbd_edpt_xfer(rhport, audio->ep_fb, (uint8_t *) &audio->fb_val, 4);
}
#endif
@@ -726,33 +995,308 @@ void audiod_init(void)
{
audiod_interface_t* audio = &_audiod_itf[i];
- // Initialize TX FIFOs if required
-#if CFG_TUD_AUDIO_EPSIZE_IN && CFG_TUD_AUDIO_TX_FIFO_SIZE
- for (uint8_t cnt = 0; cnt < CFG_TUD_AUDIO_TX_FIFO_COUNT; cnt++)
+ // Initialize control buffers
+ switch (i)
{
- tu_fifo_config(&audio->tx_ff[cnt], &audio->tx_ff_buf[cnt], CFG_TUD_AUDIO_TX_FIFO_SIZE, 1, true);
+ 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->tx_ff[cnt], osal_mutex_create(&audio->tx_ff_mutex[cnt]));
+ 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
-#if CFG_TUD_AUDIO_EPSIZE_OUT && CFG_TUD_AUDIO_RX_FIFO_SIZE
- for (uint8_t cnt = 0; cnt < CFG_TUD_AUDIO_RX_FIFO_COUNT; cnt++)
+ switch (i)
{
- tu_fifo_config(&audio->rx_ff[cnt], &audio->rx_ff_buf[cnt], CFG_TUD_AUDIO_RX_FIFO_SIZE, 1, true);
+#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(&audio->rx_ff[cnt], osal_mutex_create(&audio->rx_ff_mutex[cnt]));
+ 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_INT_CTR_EPSIZE_IN > 0
- tu_fifo_config(&audio->int_ctr_ff, &audio->int_ctr_ff_buf, CFG_TUD_AUDIO_INT_CTR_BUFSIZE, 1, true);
+#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(&audio->int_ctr_ff, osal_mutex_create(&audio->int_ctr_ff_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
}
}
@@ -765,17 +1309,25 @@ void audiod_reset(uint8_t rhport)
audiod_interface_t* audio = &_audiod_itf[i];
tu_memclr(audio, ITF_MEM_RESET_SIZE);
-#if CFG_TUD_AUDIO_EPSIZE_IN && CFG_TUD_AUDIO_TX_FIFO_SIZE
- for (uint8_t cnt = 0; cnt < CFG_TUD_AUDIO_TX_FIFO_COUNT; cnt++)
+#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_ff[cnt]);
+ tu_fifo_clear(&audio->tx_supp_ff[cnt]);
}
#endif
-#if CFG_TUD_AUDIO_EPSIZE_OUT && CFG_TUD_AUDIO_RX_FIFO_SIZE
- for (uint8_t cnt = 0; cnt < CFG_TUD_AUDIO_RX_FIFO_COUNT; cnt++)
+#if CFG_TUD_AUDIO_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_ff[cnt]);
+ tu_fifo_clear(&audio->rx_supp_ff[cnt]);
}
#endif
}
@@ -808,6 +1360,25 @@ uint16_t audiod_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uin
{
_audiod_itf[i].p_desc = (uint8_t const *)itf_desc; // Save pointer to AC descriptor which is by specification always the first one
_audiod_itf[i].rhport = rhport;
+
+ // Setup descriptor lengths
+ switch (i)
+ {
+ case 0:
+ _audiod_itf[i].desc_length = CFG_TUD_AUDIO_FUNC_1_DESC_LEN;
+ break;
+#if CFG_TUD_AUDIO > 1
+ case 1:
+ _audiod_itf[i].desc_length = CFG_TUD_AUDIO_FUNC_2_DESC_LEN;
+ break;
+#endif
+#if CFG_TUD_AUDIO > 2
+ case 2:
+ _audiod_itf[i].desc_length = CFG_TUD_AUDIO_FUNC_3_DESC_LEN;
+ break;
+#endif
+ }
+
break;
}
}
@@ -816,15 +1387,13 @@ uint16_t audiod_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uin
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)
- // TODO: Find a way to find end of current audio function and avoid necessity of tud_audio_desc_lengths - since now max_length is available we could do this surely somehow
- uint16_t drv_len = tud_audio_desc_lengths[i] - TUD_AUDIO_DESC_IAD_LEN; // - TUD_AUDIO_DESC_IAD_LEN since tinyUSB already handles the IAD descriptor
+ uint16_t drv_len = _audiod_itf[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)
{
-#if CFG_TUD_AUDIO_N_AS_INT > 0
uint8_t const itf = tu_u16_low(p_request->wIndex);
// Find index of audio streaming interface
@@ -832,17 +1401,11 @@ static bool audiod_get_interface(uint8_t rhport, tusb_control_request_t const *
uint8_t const *dummy;
TU_VERIFY(audiod_get_AS_interface_index(itf, &idxDriver, &idxItf, &dummy));
- TU_VERIFY(tud_control_xfer(rhport, p_request, &_audiod_itf[idxDriver].altSetting[idxItf], 1));
+ TU_VERIFY(tud_control_xfer(rhport, p_request, &_audiod_itf[idxDriver].alt_setting[idxItf], 1));
- TU_LOG2(" Get itf: %u - current alt: %u\r\n", itf, _audiod_itf[idxDriver].altSetting[idxItf]);
+ TU_LOG2(" Get itf: %u - current alt: %u\r\n", itf, _audiod_itf[idxDriver].alt_setting[idxItf]);
return true;
-
-#else
- (void) rhport;
- (void) p_request;
- return false;
-#endif
}
static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const * p_request)
@@ -870,41 +1433,60 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *
uint8_t const *p_desc;
TU_VERIFY(audiod_get_AS_interface_index(itf, &idxDriver, &idxItf, &p_desc));
+ audiod_interface_t* audio = &_audiod_itf[idxDriver];
+
// Look if there is an EP to be closed - for this driver, there are only 3 possible EPs which may be closed (only AS related EPs can be closed, AC EP (if present) is always open)
-#if CFG_TUD_AUDIO_EPSIZE_IN > 0
- if (_audiod_itf[idxDriver].ep_in_as_intf_num == itf)
+#if CFG_TUD_AUDIO_ENABLE_EP_IN
+ if (audio->ep_in_as_intf_num == itf)
{
- _audiod_itf[idxDriver].ep_in_as_intf_num = 0;
- usbd_edpt_close(rhport, _audiod_itf[idxDriver].ep_in);
+ audio->ep_in_as_intf_num = 0;
+ usbd_edpt_close(rhport, audio->ep_in);
// 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));
- _audiod_itf[idxDriver].ep_in = 0; // Necessary?
+ audio->ep_in = 0; // Necessary?
+
+ // Clear support FIFOs if used
+#if 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
+
}
#endif
-#if CFG_TUD_AUDIO_EPSIZE_OUT
- if (_audiod_itf[idxDriver].ep_out_as_intf_num == itf)
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT
+ if (audio->ep_out_as_intf_num == itf)
{
- _audiod_itf[idxDriver].ep_out_as_intf_num = 0;
- usbd_edpt_close(rhport, _audiod_itf[idxDriver].ep_out);
- _audiod_itf[idxDriver].ep_out = 0; // Necessary?
+ audio->ep_out_as_intf_num = 0;
+ usbd_edpt_close(rhport, audio->ep_out);
+ audio->ep_out = 0; // Necessary?
+
+ // Clear support FIFOs if used
+#if 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
// Close corresponding feedback EP
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
- usbd_edpt_close(rhport, _audiod_itf[idxDriver].ep_fb);
- _audiod_itf[idxDriver].ep_fb = 0; // Necessary?
+ usbd_edpt_close(rhport, audio->ep_fb);
+ audio->ep_fb = 0; // Necessary?
#endif
}
#endif
// Save current alternative interface setting
- _audiod_itf[idxDriver].altSetting[idxItf] = alt;
+ 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 = _audiod_itf[idxDriver].p_desc + tud_audio_desc_lengths[idxDriver] - TUD_AUDIO_DESC_IAD_LEN;
+ 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)
@@ -912,6 +1494,9 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *
// 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)
@@ -922,51 +1507,87 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *
uint8_t ep_addr = ((tusb_desc_endpoint_t const *) p_desc)->bEndpointAddress;
- // We need to set EP non busy since this is not taken care of right now in ep_close() - THIS IS A WORKAROUND!
+ //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_EPSIZE_IN > 0
+#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
- _audiod_itf[idxDriver].ep_in = ep_addr;
- _audiod_itf[idxDriver].ep_in_as_intf_num = itf;
+ 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 - in case no sample data is available a ZLP is loaded
- uint16_t n_bytes_copied;
- TU_VERIFY(audiod_tx_done_cb(rhport, &_audiod_itf[idxDriver], &n_bytes_copied));
+ // 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_itf[idxDriver]));
}
-#endif
+#endif // CFG_TUD_AUDIO_ENABLE_EP_IN
-#if CFG_TUD_AUDIO_EPSIZE_OUT
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT
if (tu_edpt_dir(ep_addr) == TUSB_DIR_OUT) // Checking usage not necessary
{
// Save address
- _audiod_itf[idxDriver].ep_out = ep_addr;
- _audiod_itf[idxDriver].ep_out_as_intf_num = itf;
+ 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
// Invoke callback
if (tud_audio_set_itf_cb) TU_VERIFY(tud_audio_set_itf_cb(rhport, p_request));
// Prepare for incoming data
- TU_ASSERT(usbd_edpt_xfer(rhport, ep_addr, _audiod_itf[idxDriver].epout_buf, CFG_TUD_AUDIO_EPSIZE_OUT), false);
+#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_iso_xfer(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
{
- _audiod_itf[idxDriver].ep_fb = ep_addr;
+ audio->ep_fb = ep_addr;
// Invoke callback
if (tud_audio_set_itf_cb) TU_VERIFY(tud_audio_set_itf_cb(rhport, p_request));
}
#endif
+#endif // CFG_TUD_AUDIO_ENABLE_EP_OUT
-#endif
foundEPs += 1;
}
p_desc = tu_desc_next(p_desc);
@@ -1166,7 +1787,7 @@ static bool audiod_control_request(uint8_t rhport, tusb_control_request_t const
}
// If we end here, the received request is a set request - we schedule a receive for the data stage and return true here. We handle the rest later in audiod_control_complete() once the data stage was finished
- TU_VERIFY(tud_control_xfer(rhport, p_request, _audiod_itf[idxDriver].ctrl_buf, CFG_TUD_AUDIO_CTRL_BUF_SIZE));
+ TU_VERIFY(tud_control_xfer(rhport, p_request, _audiod_itf[idxDriver].ctrl_buf, _audiod_itf[idxDriver].ctrl_buf_sz));
return true;
}
@@ -1208,21 +1829,15 @@ bool audiod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint3
// 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 ???
- // Load new data
- uint16 *n_bytes_copied;
- TU_VERIFY(audio_int_ctr_done_cb(rhport, &_audiod_itf[idxDriver], n_bytes_copied));
+ // 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 (*n_bytes_copied == 0 && xferred_bytes && (0 == (xferred_bytes % CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN)))
- {
- // There is no data left to send, a ZLP should be sent if
- // xferred_bytes is multiple of EP size and not zero
- return usbd_edpt_xfer(rhport, ep_addr, NULL, 0);
- }
+ 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_EPSIZE_IN
+#if CFG_TUD_AUDIO_ENABLE_EP_IN
// Data transmission of audio packet finished
if (_audiod_itf[idxDriver].ep_in == ep_addr)
@@ -1236,25 +1851,19 @@ bool audiod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint3
// This is the only place where we can fill something into the EPs buffer!
// Load new data
- uint16_t n_bytes_copied;
- TU_VERIFY(audiod_tx_done_cb(rhport, &_audiod_itf[idxDriver], &n_bytes_copied));
+ TU_VERIFY(audiod_tx_done_cb(rhport, &_audiod_itf[idxDriver]));
// Transmission of ZLP is done by audiod_tx_done_cb()
return true;
}
#endif
-#if CFG_TUD_AUDIO_EPSIZE_OUT
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT
// New audio packet received
if (_audiod_itf[idxDriver].ep_out == ep_addr)
{
- // Save into buffer - do whatever has to be done
- TU_VERIFY(audio_rx_done_cb(rhport, &_audiod_itf[idxDriver], _audiod_itf[idxDriver].epout_buf, xferred_bytes));
-
- // prepare for next transmission
- TU_ASSERT(usbd_edpt_xfer(rhport, ep_addr, _audiod_itf[idxDriver].epout_buf, CFG_TUD_AUDIO_EPSIZE_OUT), false);
-
+ TU_VERIFY(audiod_rx_done_cb(rhport, &_audiod_itf[idxDriver], (uint16_t) xferred_bytes));
return true;
}
@@ -1265,14 +1874,14 @@ bool audiod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint3
{
if (tud_audio_fb_done_cb) TU_VERIFY(tud_audio_fb_done_cb(rhport));
- return audio_fb_send(rhport, &_audiod_itf[idxDriver]);
+ // 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_itf[idxDriver]);
}
#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)
@@ -1317,7 +1926,7 @@ bool tud_audio_buffer_and_schedule_control_xfer(uint8_t rhport, tusb_control_req
}
// Crop length
- if (len > CFG_TUD_AUDIO_CTRL_BUF_SIZE) len = CFG_TUD_AUDIO_CTRL_BUF_SIZE;
+ if (len > _audiod_itf[idxDriver].ctrl_buf_sz) len = _audiod_itf[idxDriver].ctrl_buf_sz;
// Copy into buffer
memcpy((void *)_audiod_itf[idxDriver].ctrl_buf, data, (size_t)len);
@@ -1338,7 +1947,7 @@ static bool audiod_get_AS_interface_index(uint8_t itf, uint8_t *idxDriver, uint8
if (_audiod_itf[i].p_desc)
{
// Get pointer at end
- uint8_t const *p_desc_end = _audiod_itf[i].p_desc + tud_audio_desc_lengths[i] - TUD_AUDIO_DESC_IAD_LEN;
+ uint8_t const *p_desc_end = _audiod_itf[i].p_desc + _audiod_itf[i].desc_length - TUD_AUDIO_DESC_IAD_LEN;
// Advance past AC descriptors
uint8_t const *p_desc = tu_desc_next(_audiod_itf[i].p_desc);
@@ -1403,7 +2012,7 @@ static bool audiod_verify_itf_exists(uint8_t itf, uint8_t *idxDriver)
{
// Get pointer at beginning and end
uint8_t const *p_desc = _audiod_itf[i].p_desc;
- uint8_t const *p_desc_end = _audiod_itf[i].p_desc + tud_audio_desc_lengths[i] - TUD_AUDIO_DESC_IAD_LEN;
+ uint8_t const *p_desc_end = _audiod_itf[i].p_desc + _audiod_itf[i].desc_length - TUD_AUDIO_DESC_IAD_LEN;
while (p_desc < p_desc_end)
{
@@ -1427,7 +2036,7 @@ static bool audiod_verify_ep_exists(uint8_t ep, uint8_t *idxDriver)
if (_audiod_itf[i].p_desc)
{
// Get pointer at end
- uint8_t const *p_desc_end = _audiod_itf[i].p_desc + tud_audio_desc_lengths[i];
+ uint8_t const *p_desc_end = _audiod_itf[i].p_desc + _audiod_itf[i].desc_length;
// Advance past AC descriptors - EP we look for are streaming EPs
uint8_t const *p_desc = tu_desc_next(_audiod_itf[i].p_desc);
@@ -1447,15 +2056,125 @@ static bool audiod_verify_ep_exists(uint8_t ep, uint8_t *idxDriver)
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_interface_t* audio, uint8_t const * p_desc, uint8_t const * p_desc_end, uint8_t const 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 (itf != audio->ep_in_as_intf_num && 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 (itf != audio->ep_in_as_intf_num) break;
+#endif
+#if !CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_EP_OUT
+ if (itf != audio->ep_out_as_intf_num) break;
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_IN
+ if (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_desc_cs_as_interface_t const * )p_desc)->bFormatType;
+
+#if CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING
+ audio->format_type_I_tx = ((audio_desc_cs_as_interface_t const * )p_desc)->bmFormats;
+#endif
+ }
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT
+ if (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 (itf != audio->ep_in_as_intf_num && 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 (itf != audio->ep_in_as_intf_num) break;
+#endif
+#if !CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_EP_OUT
+ if (itf != audio->ep_out_as_intf_num) break;
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_IN
+ if (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 (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
-bool tud_audio_fb_set(uint8_t rhport, uint32_t feedback)
+
+// 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 itf, uint32_t feedback)
{
- audiod_interface_t *audio = &_audiod_itf[0];
+ TU_VERIFY(itf < CFG_TUD_AUDIO && _audiod_itf[itf].p_desc != NULL);
- audio->fb_val = feedback;
- TU_VERIFY(!usbd_edpt_busy(rhport, audio->ep_fb), true);
+ // Format the feedback value
+ if (_audiod_itf[itf].rhport == 0)
+ {
+ uint8_t * fb = (uint8_t *) &_audiod_itf[itf].fb_val;
- return audio_fb_send(rhport, audio);
+ // 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_itf[itf].fb_val = feedback;
+ }
+
+ // 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_itf[itf].rhport, _audiod_itf[itf].ep_fb))
+ {
+ return audiod_fb_send(_audiod_itf[itf].rhport, &_audiod_itf[itf]);
+ }
+
+ return true;
}
#endif
diff --git a/src/class/audio/audio_device.h b/src/class/audio/audio_device.h
index 5061501ce..f91540b64 100644
--- a/src/class/audio/audio_device.h
+++ b/src/class/audio/audio_device.h
@@ -38,116 +38,306 @@
// Class Driver Configuration
//--------------------------------------------------------------------+
-// Number of Standard AS Interface Descriptors (4.9.1) defined per audio function - this is required to be able to remember the current alternate settings of these interfaces - We restrict us here to have a constant number for all audio functions (which means this has to be the maximum number of AS interfaces an audio function has and a second audio function with less AS interfaces just waste a few bytes)
-#ifndef CFG_TUD_AUDIO_N_AS_INT
-#define CFG_TUD_AUDIO_N_AS_INT 0
+// 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_CTRL_BUF_SIZE
+#ifndef CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ
#error You must define an audio class control request buffer size!
#endif
-// Use of TX/RX FIFOs - If sizes are not zero, audio.c implements FIFOs for RX and TX (whatever defined).
-// For RX: the input stream gets decoded into its corresponding channels, where for each channel a FIFO is setup to hold its data -> see: audio_rx_done_cb().
-// For TX: the output stream is composed from CFG_TUD_AUDIO_N_CHANNELS_TX channels, where for each channel a FIFO is defined.
-// Further, it implements encoding and decoding of the individual channels (parameterized by the defines below).
-// If you don't use the FIFOs you need to handle encoding and decoding on your own in audio_rx_done_cb() and audio_tx_done_cb(). This, however, allows for optimizations.
+#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
-#ifndef CFG_TUD_AUDIO_TX_FIFO_SIZE
-#define CFG_TUD_AUDIO_TX_FIFO_SIZE 0 // Buffer size per channel
+#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
-#ifndef CFG_TUD_AUDIO_RX_FIFO_SIZE
-#define CFG_TUD_AUDIO_RX_FIFO_SIZE 0 // Buffer size per channel
+// 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
-// End point sizes - Limits: Full Speed <= 1023, High Speed <= 1024
-#ifndef CFG_TUD_AUDIO_EPSIZE_IN
-#define CFG_TUD_AUDIO_EPSIZE_IN 0 // TX
+#ifndef CFG_TUD_AUDIO_ENABLE_EP_OUT
+#define CFG_TUD_AUDIO_ENABLE_EP_OUT 0 // RX
#endif
-#ifndef CFG_TUD_AUDIO_EPSIZE_OUT
-#define CFG_TUD_AUDIO_EPSIZE_OUT 0 // RX
+// 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
-#ifndef CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
-#define CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP 0 // Feedback
+#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
-#ifndef CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
-#define CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN 0 // Audio interrupt control
+// 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
-#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
-#ifndef CFG_TUD_AUDIO_INT_CTR_BUFSIZE
-#define CFG_TUD_AUDIO_INT_CTR_BUFSIZE 6 // Buffer size of audio control interrupt EP - 6 Bytes according to UAC 2 specification (p. 74)
+#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
-#ifndef CFG_TUD_AUDIO_N_CHANNELS_TX
-#define CFG_TUD_AUDIO_N_CHANNELS_TX 1
+#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
-#ifndef CFG_TUD_AUDIO_N_CHANNELS_RX
-#define CFG_TUD_AUDIO_N_CHANNELS_RX 1
+#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
-// Audio data format types
-#ifndef CFG_TUD_AUDIO_FORMAT_TYPE_TX
-#define CFG_TUD_AUDIO_FORMAT_TYPE_TX AUDIO_FORMAT_TYPE_UNDEFINED // If this option is used, an encoding function has to be implemented in audio_device.c
+#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
-#ifndef CFG_TUD_AUDIO_FORMAT_TYPE_RX
-#define CFG_TUD_AUDIO_FORMAT_TYPE_RX AUDIO_FORMAT_TYPE_UNDEFINED // If this option is used, a decoding function has to be implemented in audio_device.c
+#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
-// Audio data format type I specifications
-#if CFG_TUD_AUDIO_FORMAT_TYPE_TX == AUDIO_FORMAT_TYPE_I
+#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
-// Type definitions - for possible formats see: audio_data_format_type_I_t and further in UAC2 specifications.
-#ifndef CFG_TUD_AUDIO_FORMAT_TYPE_I_TX
-#define CFG_TUD_AUDIO_FORMAT_TYPE_I_TX AUDIO_DATA_FORMAT_TYPE_I_PCM
+#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
-#ifndef CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX // bSubslotSize
-#define CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX 1
+// 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
-#ifndef CFG_TUD_AUDIO_TX_ITEMSIZE
-#if CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX == 1
-#define CFG_TUD_AUDIO_TX_ITEMSIZE 1
-#elif CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX == 2
-#define CFG_TUD_AUDIO_TX_ITEMSIZE 2
-#else
-#define CFG_TUD_AUDIO_TX_ITEMSIZE 4
+// 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
-#if CFG_TUD_AUDIO_TX_ITEMSIZE < CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX
-#error FIFO element size (ITEMSIZE) must not be smaller then sample size
+// 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
-#if CFG_TUD_AUDIO_FORMAT_TYPE_RX == AUDIO_FORMAT_TYPE_I
+// Remaining types not support so far
-#ifndef CFG_TUD_AUDIO_FORMAT_TYPE_I_RX
-#define CFG_TUD_AUDIO_FORMAT_TYPE_I_RX AUDIO_DATA_FORMAT_TYPE_I_PCM
+// 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_N_BYTES_PER_SAMPLE_RX // bSubslotSize
-#define CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX 1
+#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
-#if CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX == 1
-#define CFG_TUD_AUDIO_RX_ITEMSIZE 1
-#elif CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX == 2
-#define CFG_TUD_AUDIO_RX_ITEMSIZE 2
-#else
-#define CFG_TUD_AUDIO_RX_ITEMSIZE 4
+// 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!");
@@ -170,69 +360,70 @@ extern "C" {
//--------------------------------------------------------------------+
bool tud_audio_n_mounted (uint8_t itf);
-#if CFG_TUD_AUDIO_EPSIZE_OUT && CFG_TUD_AUDIO_RX_FIFO_SIZE
-#if CFG_TUD_AUDIO_RX_FIFO_COUNT > 1
-uint16_t tud_audio_n_available (uint8_t itf, uint8_t channelId);
-uint16_t tud_audio_n_read (uint8_t itf, uint8_t channelId, void* buffer, uint16_t bufsize);
-void tud_audio_n_read_flush (uint8_t itf, uint8_t channelId);
-#else
-uint16_t tud_audio_n_available (uint8_t itf);
-uint16_t tud_audio_n_read (uint8_t itf, void* buffer, uint16_t bufsize);
-void tud_audio_n_read_flush (uint8_t itf);
-#endif
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && !CFG_TUD_AUDIO_ENABLE_DECODING
+uint16_t tud_audio_n_available (uint8_t itf);
+uint16_t tud_audio_n_read (uint8_t itf, void* buffer, uint16_t bufsize);
+bool tud_audio_n_clear_ep_out_ff (uint8_t itf); // Delete all content in the EP OUT FIFO
#endif
-/* This function is intended for later use once EP buffers (at least for ISO EPs) are implemented as ring buffers
-#if CFG_TUD_AUDIO_EPSIZE_IN && !CFG_TUD_AUDIO_TX_FIFO_SIZE
-uint16_t tud_audio_n_write_ep_in_buffer(uint8_t itf, const void * data, uint16_t len)
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING
+bool tud_audio_n_clear_rx_support_ff (uint8_t itf, uint8_t channelId); // Delete all content in the support RX FIFOs
+uint16_t tud_audio_n_available_support_ff (uint8_t itf, uint8_t channelId);
+uint16_t tud_audio_n_read_support_ff (uint8_t itf, uint8_t channelId, void* buffer, uint16_t bufsize);
#endif
-*/
-#ifndef CFG_TUD_AUDIO_TX_FIFO_COUNT
-#define CFG_TUD_AUDIO_TX_FIFO_COUNT 1
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_ENCODING
+uint16_t tud_audio_n_write (uint8_t itf, const void * data, uint16_t len);
+bool tud_audio_n_clear_ep_in_ff (uint8_t itf); // Delete all content in the EP IN FIFO
#endif
-#if CFG_TUD_AUDIO_EPSIZE_IN && CFG_TUD_AUDIO_TX_FIFO_SIZE
-#if CFG_TUD_AUDIO_TX_FIFO_COUNT > 1
-uint16_t tud_audio_n_write (uint8_t itf, uint8_t channelId, const void * data, uint16_t len);
-#else
-uint16_t tud_audio_n_write (uint8_t itf, const void * data, uint16_t len);
-#endif
-uint16_t tud_audio_n_write_flush(uint8_t itf);
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_ENCODING
+uint16_t tud_audio_n_flush_tx_support_ff (uint8_t itf); // Force all content in the support TX FIFOs to be written into EP SW FIFO
+bool tud_audio_n_clear_tx_support_ff (uint8_t itf, uint8_t channelId);
+uint16_t tud_audio_n_write_support_ff (uint8_t itf, uint8_t channelId, const void * data, uint16_t len);
#endif
-#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN > 0
-uint16_t tud_audio_int_ctr_n_available (uint8_t itf);
-uint16_t tud_audio_int_ctr_n_read (uint8_t itf, void* buffer, uint16_t bufsize);
-void tud_audio_int_ctr_n_read_flush (uint8_t itf);
-uint16_t tud_audio_int_ctr_n_write (uint8_t itf, uint8_t const* buffer, uint16_t bufsize);
+#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
+uint16_t tud_audio_int_ctr_n_write (uint8_t itf, uint8_t const* buffer, uint16_t len);
#endif
//--------------------------------------------------------------------+
// Application API (Interface0)
//--------------------------------------------------------------------+
-static inline bool tud_audio_mounted (void);
+static inline bool tud_audio_mounted (void);
+
+// RX API
-#if CFG_TUD_AUDIO_EPSIZE_OUT && CFG_TUD_AUDIO_RX_FIFO_SIZE
-static inline uint16_t tud_audio_available (void);
-static inline uint16_t tud_audio_read (void* buffer, uint16_t bufsize);
-static inline void tud_audio_read_flush (void);
+#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);
#endif
-#if CFG_TUD_AUDIO_EPSIZE_IN && CFG_TUD_AUDIO_TX_FIFO_SIZE
-#if CFG_TUD_AUDIO_TX_FIFO_COUNT > 1
-static inline uint16_t tud_audio_write (uint8_t channelId, uint8_t const* buffer, uint16_t bufsize);
-#else
-static inline uint16_t tud_audio_write (uint8_t const* buffer, uint16_t bufsize);
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING
+static inline bool tud_audio_clear_rx_support_ff (uint8_t channelId);
+static inline uint16_t tud_audio_available_support_ff (uint8_t channelId);
+static inline uint16_t tud_audio_read_support_ff (uint8_t channelId, void* buffer, uint16_t bufsize);
#endif
+
+// 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);
#endif
-#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN > 0
-static inline uint32_t tud_audio_int_ctr_available (void);
-static inline uint32_t tud_audio_int_ctr_read (void* buffer, uint32_t bufsize);
-static inline void tud_audio_int_ctr_read_flush (void);
-static inline uint32_t tud_audio_int_ctr_write (uint8_t const* buffer, uint32_t bufsize);
+#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 channelId);
+static inline uint16_t tud_audio_write_support_ff (uint8_t channelId, const void * data, uint16_t len);
+#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
@@ -247,26 +438,28 @@ bool tud_audio_buffer_and_schedule_control_xfer(uint8_t rhport, tusb_control_req
// Application Callback API (weak is optional)
//--------------------------------------------------------------------+
-#if CFG_TUD_AUDIO_EPSIZE_IN
+#if CFG_TUD_AUDIO_ENABLE_EP_IN
TU_ATTR_WEAK bool tud_audio_tx_done_pre_load_cb(uint8_t rhport, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting);
TU_ATTR_WEAK bool tud_audio_tx_done_post_load_cb(uint8_t rhport, uint16_t n_bytes_copied, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting);
#endif
-#if CFG_TUD_AUDIO_EPSIZE_OUT
-TU_ATTR_WEAK bool tud_audio_rx_done_cb(uint8_t rhport, uint8_t * buffer, uint16_t bufsize);
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT
+TU_ATTR_WEAK bool tud_audio_rx_done_pre_read_cb(uint8_t rhport, uint16_t n_bytes_received, uint8_t itf, uint8_t ep_out, uint8_t cur_alt_setting);
+TU_ATTR_WEAK bool tud_audio_rx_done_post_read_cb(uint8_t rhport, uint16_t n_bytes_received, uint8_t itf, uint8_t ep_out, uint8_t cur_alt_setting);
#endif
-#if CFG_TUD_AUDIO_EPSIZE_OUT > 0 && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+#if CFG_TUD_AUDIO_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_fb_set(uint8_t rhport, uint32_t feedback);
+bool tud_audio_n_fb_set(uint8_t itf, 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);
+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
@@ -302,93 +495,103 @@ static inline bool tud_audio_mounted(void)
return tud_audio_n_mounted(0);
}
-#if CFG_TUD_AUDIO_EPSIZE_IN
-#if CFG_TUD_AUDIO_TX_FIFO_SIZE && CFG_TUD_AUDIO_TX_FIFO_COUNT > 1
-static inline uint16_t tud_audio_write (uint8_t channelId, uint8_t const* buffer, uint16_t n_bytes) // Short version if only one audio function is used
+// 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_write(0, channelId, buffer, n_bytes);
+ return tud_audio_n_available(0);
}
-#else
-static inline uint16_t tud_audio_write (uint8_t const* buffer, uint16_t n_bytes) // Short version if only one audio function is used
+
+static inline uint16_t tud_audio_read(void* buffer, uint16_t bufsize)
{
- return tud_audio_n_write(0, buffer, n_bytes);
+ return tud_audio_n_read(0, buffer, bufsize);
}
-#endif
-static inline uint16_t tud_audio_write_flush (void) // Short version if only one audio function is used
+static inline bool tud_audio_clear_ep_out_ff(void)
{
-#if CFG_TUD_AUDIO_TX_FIFO_SIZE
- return tud_audio_n_write_flush(0);
-#else
- return 0;
-#endif
+ return tud_audio_n_clear_ep_out_ff(0);
}
-#endif // CFG_TUD_AUDIO_EPSIZE_IN && CFG_TUD_AUDIO_TX_FIFO_SIZE
-#if CFG_TUD_AUDIO_EPSIZE_OUT && CFG_TUD_AUDIO_RX_FIFO_SIZE
-#if CFG_TUD_AUDIO_RX_FIFO_COUNT > 1
-static inline uint16_t tud_audio_available(uint8_t channelId)
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING
+
+static inline bool tud_audio_clear_rx_support_ff(uint8_t channelId)
{
- return tud_audio_n_available(0, channelId);
+ return tud_audio_n_clear_rx_support_ff(0, channelId);
}
-static inline uint16_t tud_audio_read(uint8_t channelId, void* buffer, uint16_t bufsize)
+static inline uint16_t tud_audio_available_support_ff(uint8_t channelId)
{
- return tud_audio_n_read(0, channelId, buffer, bufsize);
+ return tud_audio_n_available_support_ff(0, channelId);
}
-static inline void tud_audio_read_flush(uint8_t channelId)
+static inline uint16_t tud_audio_read_support_ff(uint8_t channelId, void* buffer, uint16_t bufsize)
{
- tud_audio_n_read_flush(0, channelId);
+ return tud_audio_n_read_support_ff(0, channelId, buffer, bufsize);
}
-#else
-static inline uint16_t tud_audio_available(void)
+
+#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_available(0);
+ return tud_audio_n_write(0, data, len);
}
-static inline uint16_t tud_audio_read(void *buffer, uint16_t bufsize)
+static inline bool tud_audio_clear_ep_in_ff(void)
{
- return tud_audio_n_read(0, buffer, bufsize);
+ return tud_audio_n_clear_ep_in_ff(0);
}
-static inline void tud_audio_read_flush(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)
{
- tud_audio_n_read_flush(0);
+ return tud_audio_n_flush_tx_support_ff(0);
}
-#endif
-#endif
-#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN > 0
-static inline uint16_t tud_audio_int_ctr_available(void)
+static inline uint16_t tud_audio_clear_tx_support_ff(uint8_t channelId)
{
- return tud_audio_int_ctr_n_available(0);
+ return tud_audio_n_clear_tx_support_ff(0, channelId);
}
-static inline uint16_t tud_audio_int_ctr_read(void* buffer, uint16_t bufsize)
+static inline uint16_t tud_audio_write_support_ff(uint8_t channelId, const void * data, uint16_t len)
{
- return tud_audio_int_ctr_n_read(0, buffer, bufsize);
+ return tud_audio_n_write_support_ff(0, channelId, data, len);
}
-static inline void tud_audio_int_ctr_read_flush(void)
+#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_read_flush(0);
+ return tud_audio_int_ctr_n_write(0, buffer, len);
}
+#endif
-static inline uint16_t tud_audio_int_ctr_write(uint8_t const* buffer, uint16_t bufsize)
+#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_int_ctr_n_write(0, buffer, bufsize);
+ 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);
+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
}
diff --git a/src/class/cdc/cdc_device.c b/src/class/cdc/cdc_device.c
index c24edb757..0a7691916 100644
--- a/src/class/cdc/cdc_device.c
+++ b/src/class/cdc/cdc_device.c
@@ -244,8 +244,8 @@ void cdcd_init(void)
tu_fifo_config(&p_cdc->tx_ff, p_cdc->tx_ff_buf, TU_ARRAY_SIZE(p_cdc->tx_ff_buf), 1, true);
#if CFG_FIFO_MUTEX
- tu_fifo_config_mutex(&p_cdc->rx_ff, osal_mutex_create(&p_cdc->rx_ff_mutex));
- tu_fifo_config_mutex(&p_cdc->tx_ff, osal_mutex_create(&p_cdc->tx_ff_mutex));
+ tu_fifo_config_mutex(&p_cdc->rx_ff, NULL, osal_mutex_create(&p_cdc->rx_ff_mutex));
+ tu_fifo_config_mutex(&p_cdc->tx_ff, osal_mutex_create(&p_cdc->tx_ff_mutex), NULL);
#endif
}
}
diff --git a/src/class/midi/midi_device.c b/src/class/midi/midi_device.c
index 9ccbb733c..2c50bc4f3 100644
--- a/src/class/midi/midi_device.c
+++ b/src/class/midi/midi_device.c
@@ -378,8 +378,8 @@ void midid_init(void)
tu_fifo_config(&midi->tx_ff, midi->tx_ff_buf, CFG_TUD_MIDI_TX_BUFSIZE, 1, false); // OBVS.
#if CFG_FIFO_MUTEX
- tu_fifo_config_mutex(&midi->rx_ff, osal_mutex_create(&midi->rx_ff_mutex));
- tu_fifo_config_mutex(&midi->tx_ff, osal_mutex_create(&midi->tx_ff_mutex));
+ tu_fifo_config_mutex(&midi->rx_ff, NULL, osal_mutex_create(&midi->rx_ff_mutex));
+ tu_fifo_config_mutex(&midi->tx_ff, osal_mutex_create(&midi->tx_ff_mutex), NULL);
#endif
}
}
diff --git a/src/class/vendor/vendor_device.c b/src/class/vendor/vendor_device.c
index 3fcea89c4..9b0a3c25f 100644
--- a/src/class/vendor/vendor_device.c
+++ b/src/class/vendor/vendor_device.c
@@ -146,8 +146,8 @@ void vendord_init(void)
tu_fifo_config(&p_itf->tx_ff, p_itf->tx_ff_buf, CFG_TUD_VENDOR_TX_BUFSIZE, 1, false);
#if CFG_FIFO_MUTEX
- tu_fifo_config_mutex(&p_itf->rx_ff, osal_mutex_create(&p_itf->rx_ff_mutex));
- tu_fifo_config_mutex(&p_itf->tx_ff, osal_mutex_create(&p_itf->tx_ff_mutex));
+ tu_fifo_config_mutex(&p_itf->rx_ff, NULL, osal_mutex_create(&p_itf->rx_ff_mutex));
+ tu_fifo_config_mutex(&p_itf->tx_ff, osal_mutex_create(&p_itf->tx_ff_mutex), NULL);
#endif
}
}
diff --git a/src/common/tusb_fifo.c b/src/common/tusb_fifo.c
index 30629af71..098d54801 100644
--- a/src/common/tusb_fifo.c
+++ b/src/common/tusb_fifo.c
@@ -39,34 +39,38 @@
// implement mutex lock and unlock
#if CFG_FIFO_MUTEX
-static void tu_fifo_lock(tu_fifo_t *f)
+static inline void _ff_lock(tu_fifo_mutex_t mutex)
{
- if (f->mutex)
- {
- osal_mutex_lock(f->mutex, OSAL_TIMEOUT_WAIT_FOREVER);
- }
+ if (mutex) osal_mutex_lock(mutex, OSAL_TIMEOUT_WAIT_FOREVER);
}
-static void tu_fifo_unlock(tu_fifo_t *f)
+static inline void _ff_unlock(tu_fifo_mutex_t mutex)
{
- if (f->mutex)
- {
- osal_mutex_unlock(f->mutex);
- }
+ if (mutex) osal_mutex_unlock(mutex);
}
#else
-#define tu_fifo_lock(_ff)
-#define tu_fifo_unlock(_ff)
+#define _ff_lock(_mutex)
+#define _ff_unlock(_mutex)
#endif
+/** \enum tu_fifo_copy_mode_t
+ * \brief Write modes intended to allow special read and write functions to be able to copy data to and from USB hardware FIFOs as needed for e.g. STM32s and others
+ */
+typedef enum
+{
+ TU_FIFO_COPY_INC, ///< Copy from/to an increasing source/destination address - default mode
+ TU_FIFO_COPY_CST_FULL_WORDS, ///< Copy from/to a constant source/destination address - required for e.g. STM32 to write into USB hardware FIFO
+} tu_fifo_copy_mode_t;
+
bool tu_fifo_config(tu_fifo_t *f, void* buffer, uint16_t depth, uint16_t item_size, bool overwritable)
{
if (depth > 0x8000) return false; // Maximum depth is 2^15 items
- tu_fifo_lock(f);
+ _ff_lock(f->mutex_wr);
+ _ff_lock(f->mutex_rd);
f->buffer = (uint8_t*) buffer;
f->depth = depth;
@@ -78,66 +82,236 @@ bool tu_fifo_config(tu_fifo_t *f, void* buffer, uint16_t depth, uint16_t item_si
f->rd_idx = f->wr_idx = 0;
- tu_fifo_unlock(f);
+ _ff_unlock(f->mutex_wr);
+ _ff_unlock(f->mutex_rd);
return true;
}
// Static functions are intended to work on local variables
-
static inline uint16_t _ff_mod(uint16_t idx, uint16_t depth)
{
while ( idx >= depth) idx -= depth;
return idx;
}
-// send one item to FIFO WITHOUT updating write pointer
-static inline void _ff_push(tu_fifo_t* f, void const * data, uint16_t wRel)
+// Intended to be used to read from hardware USB FIFO in e.g. STM32 where all data is read from a constant address
+// Code adapted from dcd_synopsis.c
+// TODO generalize with configurable 1 byte or 4 byte each read
+static void _ff_push_const_addr(uint8_t * ff_buf, const void * app_buf, uint16_t len)
{
- memcpy(f->buffer + (wRel * f->item_size), data, f->item_size);
+ volatile uint32_t * rx_fifo = (volatile uint32_t *) app_buf;
+
+ // Reading full available 32 bit words from const app address
+ uint16_t full_words = len >> 2;
+ while(full_words--)
+ {
+ tu_unaligned_write32(ff_buf, *rx_fifo);
+ ff_buf += 4;
+ }
+
+ // Read the remaining 1-3 bytes from const app address
+ uint8_t const bytes_rem = len & 0x03;
+ if ( bytes_rem )
+ {
+ uint32_t tmp32 = *rx_fifo;
+ memcpy(ff_buf, &tmp32, bytes_rem);
+ }
}
-// send n items to FIFO WITHOUT updating write pointer
-static void _ff_push_n(tu_fifo_t* f, void const * data, uint16_t n, uint16_t wRel)
+// Intended to be used to write to hardware USB FIFO in e.g. STM32
+// where all data is written to a constant address in full word copies
+static void _ff_pull_const_addr(void * app_buf, const uint8_t * ff_buf, uint16_t len)
{
- if(wRel + n <= f->depth) // Linear mode only
+ volatile uint32_t * tx_fifo = (volatile uint32_t *) app_buf;
+
+ // Pushing full available 32 bit words to const app address
+ uint16_t full_words = len >> 2;
+ while(full_words--)
{
- memcpy(f->buffer + (wRel * f->item_size), data, n*f->item_size);
+ *tx_fifo = tu_unaligned_read32(ff_buf);
+ ff_buf += 4;
}
- else // Wrap around
+
+ // Write the remaining 1-3 bytes into const app address
+ uint8_t const bytes_rem = len & 0x03;
+ if ( bytes_rem )
{
- uint16_t nLin = f->depth - wRel;
+ uint32_t tmp32 = 0;
+ memcpy(&tmp32, ff_buf, bytes_rem);
- // Write data to linear part of buffer
- memcpy(f->buffer + (wRel * f->item_size), data, nLin*f->item_size);
+ *tx_fifo = tmp32;
+ }
+}
- // Write data wrapped around
- memcpy(f->buffer, ((uint8_t const*) data) + nLin*f->item_size, (n - nLin) * f->item_size);
+// send one item to FIFO WITHOUT updating write pointer
+static inline void _ff_push(tu_fifo_t* f, void const * app_buf, uint16_t rel)
+{
+ memcpy(f->buffer + (rel * f->item_size), app_buf, f->item_size);
+}
+
+// send n items to FIFO WITHOUT updating write pointer
+static void _ff_push_n(tu_fifo_t* f, void const * app_buf, uint16_t n, uint16_t rel, tu_fifo_copy_mode_t copy_mode)
+{
+ uint16_t const nLin = f->depth - rel;
+ uint16_t const nWrap = n - nLin;
+
+ uint16_t nLin_bytes = nLin * f->item_size;
+ uint16_t nWrap_bytes = nWrap * f->item_size;
+
+ // current buffer of fifo
+ uint8_t* ff_buf = f->buffer + (rel * f->item_size);
+
+ switch (copy_mode)
+ {
+ case TU_FIFO_COPY_INC:
+ if(n <= nLin)
+ {
+ // Linear only
+ memcpy(ff_buf, app_buf, n*f->item_size);
+ }
+ else
+ {
+ // Wrap around
+
+ // Write data to linear part of buffer
+ memcpy(ff_buf, app_buf, nLin_bytes);
+
+ // Write data wrapped around
+ memcpy(f->buffer, ((uint8_t const*) app_buf) + nLin_bytes, nWrap_bytes);
+ }
+ break;
+
+ case TU_FIFO_COPY_CST_FULL_WORDS:
+ // Intended for hardware buffers from which it can be read word by word only
+ if(n <= nLin)
+ {
+ // Linear only
+ _ff_push_const_addr(ff_buf, app_buf, n*f->item_size);
+ }
+ else
+ {
+ // Wrap around case
+
+ // Write full words to linear part of buffer
+ uint16_t nLin_4n_bytes = nLin_bytes & 0xFFFC;
+ _ff_push_const_addr(ff_buf, app_buf, nLin_4n_bytes);
+ ff_buf += nLin_4n_bytes;
+
+ // There could be odd 1-3 bytes before the wrap-around boundary
+ volatile uint32_t * rx_fifo = (volatile uint32_t *) app_buf;
+ uint8_t rem = nLin_bytes & 0x03;
+ if (rem > 0)
+ {
+ uint8_t remrem = tu_min16(nWrap_bytes, 4-rem);
+ nWrap_bytes -= remrem;
+
+ uint32_t tmp32 = *rx_fifo;
+ uint8_t * src_u8 = ((uint8_t *) &tmp32);
+
+ // Write 1-3 bytes before wrapped boundary
+ while(rem--) *ff_buf++ = *src_u8++;
+
+ // Read more bytes to beginning to complete a word
+ ff_buf = f->buffer;
+ while(remrem--) *ff_buf++ = *src_u8++;
+ }
+ else
+ {
+ ff_buf = f->buffer; // wrap around to beginning
+ }
+
+ // Write data wrapped part
+ if (nWrap_bytes > 0) _ff_push_const_addr(ff_buf, app_buf, nWrap_bytes);
+ }
+ break;
}
}
// get one item from FIFO WITHOUT updating read pointer
-static inline void _ff_pull(tu_fifo_t* f, void * p_buffer, uint16_t rRel)
+static inline void _ff_pull(tu_fifo_t* f, void * app_buf, uint16_t rel)
{
- memcpy(p_buffer, f->buffer + (rRel * f->item_size), f->item_size);
+ memcpy(app_buf, f->buffer + (rel * f->item_size), f->item_size);
}
// get n items from FIFO WITHOUT updating read pointer
-static void _ff_pull_n(tu_fifo_t* f, void * p_buffer, uint16_t n, uint16_t rRel)
+static void _ff_pull_n(tu_fifo_t* f, void* app_buf, uint16_t n, uint16_t rel, tu_fifo_copy_mode_t copy_mode)
{
- if(rRel + n <= f->depth) // Linear mode only
- {
- memcpy(p_buffer, f->buffer + (rRel * f->item_size), n*f->item_size);
- }
- else // Wrap around
+ uint16_t const nLin = f->depth - rel;
+ uint16_t const nWrap = n - nLin; // only used if wrapped
+
+ uint16_t nLin_bytes = nLin * f->item_size;
+ uint16_t nWrap_bytes = nWrap * f->item_size;
+
+ // current buffer of fifo
+ uint8_t* ff_buf = f->buffer + (rel * f->item_size);
+
+ switch (copy_mode)
{
- uint16_t nLin = f->depth - rRel;
+ case TU_FIFO_COPY_INC:
+ if ( n <= nLin )
+ {
+ // Linear only
+ memcpy(app_buf, ff_buf, n*f->item_size);
+ }
+ else
+ {
+ // Wrap around
+
+ // Read data from linear part of buffer
+ memcpy(app_buf, ff_buf, nLin_bytes);
+
+ // Read data wrapped part
+ memcpy((uint8_t*) app_buf + nLin_bytes, f->buffer, nWrap_bytes);
+ }
+ break;
+
+ case TU_FIFO_COPY_CST_FULL_WORDS:
+ if ( n <= nLin )
+ {
+ // Linear only
+ _ff_pull_const_addr(app_buf, ff_buf, n*f->item_size);
+ }
+ else
+ {
+ // Wrap around case
+
+ // Read full words from linear part of buffer
+ uint16_t nLin_4n_bytes = nLin_bytes & 0xFFFC;
+ _ff_pull_const_addr(app_buf, ff_buf, nLin_4n_bytes);
+ ff_buf += nLin_4n_bytes;
+
+ // There could be odd 1-3 bytes before the wrap-around boundary
+ volatile uint32_t * tx_fifo = (volatile uint32_t *) app_buf;
+ uint8_t rem = nLin_bytes & 0x03;
+ if (rem > 0)
+ {
+ uint8_t remrem = tu_min16(nWrap_bytes, 4-rem);
+ nWrap_bytes -= remrem;
+
+ uint32_t tmp32=0;
+ uint8_t * dst_u8 = (uint8_t *)&tmp32;
+
+ // Read 1-3 bytes before wrapped boundary
+ while(rem--) *dst_u8++ = *ff_buf++;
- // Read data from linear part of buffer
- memcpy(p_buffer, f->buffer + (rRel * f->item_size), nLin*f->item_size);
+ // Read more bytes from beginning to complete a word
+ ff_buf = f->buffer;
+ while(remrem--) *dst_u8++ = *ff_buf++;
- // Read data wrapped part
- memcpy((uint8_t*)p_buffer + nLin*f->item_size, f->buffer, (n - nLin) * f->item_size);
+ *tx_fifo = tmp32;
+ }
+ else
+ {
+ ff_buf = f->buffer; // wrap around to beginning
+ }
+
+ // Read data wrapped part
+ if (nWrap_bytes > 0) _ff_pull_const_addr(app_buf, ff_buf, nWrap_bytes);
+ }
+ break;
+
+ default: break;
}
}
@@ -179,7 +353,7 @@ static uint16_t get_relative_pointer(tu_fifo_t* f, uint16_t p, uint16_t offset)
return _ff_mod(advance_pointer(f, p, offset), f->depth);
}
-// Works on local copies of w and r
+// Works on local copies of w and r - return only the difference and as such can be used to determine an overflow
static inline uint16_t _tu_fifo_count(tu_fifo_t* f, uint16_t wAbs, uint16_t rAbs)
{
uint16_t cnt = wAbs-rAbs;
@@ -246,7 +420,7 @@ static bool _tu_fifo_peek_at(tu_fifo_t* f, uint16_t offset, void * p_buffer, uin
// Works on local copies of w and r
// Must be protected by mutexes since in case of an overflow read pointer gets modified
-static uint16_t _tu_fifo_peek_at_n(tu_fifo_t* f, uint16_t offset, void * p_buffer, uint16_t n, uint16_t wAbs, uint16_t rAbs)
+static uint16_t _tu_fifo_peek_at_n(tu_fifo_t* f, uint16_t offset, void * p_buffer, uint16_t n, uint16_t wAbs, uint16_t rAbs, tu_fifo_copy_mode_t copy_mode)
{
uint16_t cnt = _tu_fifo_count(f, wAbs, rAbs);
@@ -263,15 +437,12 @@ static uint16_t _tu_fifo_peek_at_n(tu_fifo_t* f, uint16_t offset, void * p_buffe
// Check if we can read something at and after offset - if too less is available we read what remains
cnt -= offset;
- if (cnt < n) {
- if (cnt == 0) return 0;
- n = cnt;
- }
+ if (cnt < n) n = cnt;
uint16_t rRel = get_relative_pointer(f, rAbs, offset);
// Peek data
- _ff_pull_n(f, p_buffer, n, rRel);
+ _ff_pull_n(f, p_buffer, n, rRel, copy_mode);
return n;
}
@@ -282,12 +453,67 @@ static inline uint16_t _tu_fifo_remaining(tu_fifo_t* f, uint16_t wAbs, uint16_t
return f->depth - _tu_fifo_count(f, wAbs, rAbs);
}
+static uint16_t _tu_fifo_write_n(tu_fifo_t* f, const void * data, uint16_t n, tu_fifo_copy_mode_t copy_mode)
+{
+ if ( n == 0 ) return 0;
+
+ _ff_lock(f->mutex_wr);
+
+ uint16_t w = f->wr_idx, r = f->rd_idx;
+ uint8_t const* buf8 = (uint8_t const*) data;
+
+ if (!f->overwritable)
+ {
+ // Not overwritable limit up to full
+ n = tu_min16(n, _tu_fifo_remaining(f, w, r));
+ }
+ else if (n >= f->depth)
+ {
+ // Only copy last part
+ buf8 = buf8 + (n - f->depth) * f->item_size;
+ n = f->depth;
+
+ // We start writing at the read pointer's position since we fill the complete
+ // buffer and we do not want to modify the read pointer within a write function!
+ // This would end up in a race condition with read functions!
+ w = r;
+ }
+
+ uint16_t wRel = get_relative_pointer(f, w, 0);
+
+ // Write data
+ _ff_push_n(f, buf8, n, wRel, copy_mode);
+
+ // Advance pointer
+ f->wr_idx = advance_pointer(f, w, n);
+
+ _ff_unlock(f->mutex_wr);
+
+ return n;
+}
+
+static uint16_t _tu_fifo_read_n(tu_fifo_t* f, void * buffer, uint16_t n, tu_fifo_copy_mode_t copy_mode)
+{
+ _ff_lock(f->mutex_rd);
+
+ // Peek the data
+ n = _tu_fifo_peek_at_n(f, 0, buffer, n, f->wr_idx, f->rd_idx, copy_mode); // f->rd_idx might get modified in case of an overflow so we can not use a local variable
+
+ // Advance read pointer
+ f->rd_idx = advance_pointer(f, f->rd_idx, n);
+
+ _ff_unlock(f->mutex_rd);
+ return n;
+}
+
/******************************************************************************/
/*!
@brief Get number of items in FIFO.
As this function only reads the read and write pointers once, this function is
- reentrant and thus thread and ISR save without any mutexes.
+ reentrant and thus thread and ISR save without any mutexes. In case an
+ overflow occurred, this function return f.depth at maximum. Overflows are
+ checked and corrected for in the read functions!
@param[in] f
Pointer to the FIFO buffer to manipulate
@@ -297,7 +523,7 @@ static inline uint16_t _tu_fifo_remaining(tu_fifo_t* f, uint16_t wAbs, uint16_t
/******************************************************************************/
uint16_t tu_fifo_count(tu_fifo_t* f)
{
- return _tu_fifo_count(f, f->wr_idx, f->rd_idx);
+ return tu_min16(_tu_fifo_count(f, f->wr_idx, f->rd_idx), f->depth);
}
/******************************************************************************/
@@ -361,7 +587,7 @@ uint16_t tu_fifo_remaining(tu_fifo_t* f)
BE AWARE - THIS FUNCTION MIGHT NOT GIVE A CORRECT ANSWERE IN CASE WRITE POINTER "OVERFLOWS"
Only one overflow is allowed for this function to work e.g. if depth = 100, you must not
write more than 2*depth-1 items in one rush without updating write pointer. Otherwise
- write pointer wraps and you pointer states are messed up. This can only happen if you
+ write pointer wraps and your pointer states are messed up. This can only happen if you
use DMAs, write functions do not allow such an error. Avoid such nasty things!
All reading functions (read, peek) check for overflows and correct read pointer on their own such
@@ -383,9 +609,9 @@ bool tu_fifo_overflowed(tu_fifo_t* f)
// Only use in case tu_fifo_overflow() returned true!
void tu_fifo_correct_read_pointer(tu_fifo_t* f)
{
- tu_fifo_lock(f);
+ _ff_lock(f->mutex_rd);
_tu_fifo_correct_read_pointer(f, f->wr_idx);
- tu_fifo_unlock(f);
+ _ff_unlock(f->mutex_rd);
}
/******************************************************************************/
@@ -406,7 +632,7 @@ void tu_fifo_correct_read_pointer(tu_fifo_t* f)
/******************************************************************************/
bool tu_fifo_read(tu_fifo_t* f, void * buffer)
{
- tu_fifo_lock(f); // TODO: Here we may distinguish for read and write pointer mutexes!
+ _ff_lock(f->mutex_rd);
// Peek the data
bool ret = _tu_fifo_peek_at(f, 0, buffer, f->wr_idx, f->rd_idx); // f->rd_idx might get modified in case of an overflow so we can not use a local variable
@@ -414,7 +640,7 @@ bool tu_fifo_read(tu_fifo_t* f, void * buffer)
// Advance pointer
f->rd_idx = advance_pointer(f, f->rd_idx, ret);
- tu_fifo_unlock(f);
+ _ff_unlock(f->mutex_rd);
return ret;
}
@@ -428,24 +654,20 @@ bool tu_fifo_read(tu_fifo_t* f, void * buffer)
Pointer to the FIFO buffer to manipulate
@param[in] buffer
The pointer to data location
- @param[in] count
+ @param[in] n
Number of element that buffer can afford
@returns number of items read from the FIFO
*/
/******************************************************************************/
-uint16_t tu_fifo_read_n(tu_fifo_t* f, void * buffer, uint16_t count)
+uint16_t tu_fifo_read_n(tu_fifo_t* f, void * buffer, uint16_t n)
{
- tu_fifo_lock(f); // TODO: Here we may distinguish for read and write pointer mutexes!
-
- // Peek the data
- count = _tu_fifo_peek_at_n(f, 0, buffer, count, f->wr_idx, f->rd_idx); // f->rd_idx might get modified in case of an overflow so we can not use a local variable
-
- // Advance read pointer
- f->rd_idx = advance_pointer(f, f->rd_idx, count);
+ return _tu_fifo_read_n(f, buffer, n, TU_FIFO_COPY_INC);
+}
- tu_fifo_unlock(f);
- return count;
+uint16_t tu_fifo_read_n_const_addr_full_words(tu_fifo_t* f, void * buffer, uint16_t n)
+{
+ return _tu_fifo_read_n(f, buffer, n, TU_FIFO_COPY_CST_FULL_WORDS);
}
/******************************************************************************/
@@ -465,9 +687,9 @@ uint16_t tu_fifo_read_n(tu_fifo_t* f, void * buffer, uint16_t count)
/******************************************************************************/
bool tu_fifo_peek_at(tu_fifo_t* f, uint16_t offset, void * p_buffer)
{
- tu_fifo_lock(f); // TODO: Here we may distinguish for read and write pointer mutexes!
+ _ff_lock(f->mutex_rd);
bool ret = _tu_fifo_peek_at(f, offset, p_buffer, f->wr_idx, f->rd_idx);
- tu_fifo_unlock(f);
+ _ff_unlock(f->mutex_rd);
return ret;
}
@@ -490,9 +712,9 @@ bool tu_fifo_peek_at(tu_fifo_t* f, uint16_t offset, void * p_buffer)
/******************************************************************************/
uint16_t tu_fifo_peek_at_n(tu_fifo_t* f, uint16_t offset, void * p_buffer, uint16_t n)
{
- tu_fifo_lock(f); // TODO: Here we may distinguish for read and write pointer mutexes!
- bool ret = _tu_fifo_peek_at_n(f, offset, p_buffer, n, f->wr_idx, f->rd_idx);
- tu_fifo_unlock(f);
+ _ff_lock(f->mutex_rd);
+ bool ret = _tu_fifo_peek_at_n(f, offset, p_buffer, n, f->wr_idx, f->rd_idx, TU_FIFO_COPY_INC);
+ _ff_unlock(f->mutex_rd);
return ret;
}
@@ -514,7 +736,7 @@ uint16_t tu_fifo_peek_at_n(tu_fifo_t* f, uint16_t offset, void * p_buffer, uint1
/******************************************************************************/
bool tu_fifo_write(tu_fifo_t* f, const void * data)
{
- tu_fifo_lock(f);
+ _ff_lock(f->mutex_wr);
uint16_t w = f->wr_idx;
@@ -528,7 +750,7 @@ bool tu_fifo_write(tu_fifo_t* f, const void * data)
// Advance pointer
f->wr_idx = advance_pointer(f, w, 1);
- tu_fifo_unlock(f);
+ _ff_unlock(f->mutex_wr);
return true;
}
@@ -547,43 +769,29 @@ bool tu_fifo_write(tu_fifo_t* f, const void * data)
@return Number of written elements
*/
/******************************************************************************/
-uint16_t tu_fifo_write_n(tu_fifo_t* f, const void * data, uint16_t count)
+uint16_t tu_fifo_write_n(tu_fifo_t* f, const void * data, uint16_t n)
{
- if ( count == 0 ) return 0;
-
- tu_fifo_lock(f);
-
- uint16_t w = f->wr_idx, r = f->rd_idx;
- uint8_t const* buf8 = (uint8_t const*) data;
-
- if (!f->overwritable)
- {
- // Not overwritable limit up to full
- count = tu_min16(count, _tu_fifo_remaining(f, w, r));
- }
- else if (count > f->depth)
- {
- // Only copy last part
- buf8 = buf8 + (count - f->depth) * f->item_size;
- count = f->depth;
-
- // We start writing at the read pointer's position since we fill the complete
- // buffer and we do not want to modify the read pointer within a write function!
- // This would end up in a race condition with read functions!
- f->wr_idx = r;
- }
-
- uint16_t wRel = get_relative_pointer(f, w, 0);
-
- // Write data
- _ff_push_n(f, buf8, count, wRel);
-
- // Advance pointer
- f->wr_idx = advance_pointer(f, w, count);
+ return _tu_fifo_write_n(f, data, n, TU_FIFO_COPY_INC);
+}
- tu_fifo_unlock(f);
+/******************************************************************************/
+/*!
+ @brief This function will write n elements into the array index specified by
+ the write pointer and increment the write index. The source address will
+ not be incremented which is useful for reading from registers.
- return count;
+ @param[in] f
+ Pointer to the FIFO buffer to manipulate
+ @param[in] data
+ The pointer to data to add to the FIFO
+ @param[in] count
+ Number of element
+ @return Number of written elements
+ */
+/******************************************************************************/
+uint16_t tu_fifo_write_n_const_addr_full_words(tu_fifo_t* f, const void * data, uint16_t n)
+{
+ return _tu_fifo_write_n(f, data, n, TU_FIFO_COPY_CST_FULL_WORDS);
}
/******************************************************************************/
@@ -596,12 +804,15 @@ uint16_t tu_fifo_write_n(tu_fifo_t* f, const void * data, uint16_t count)
/******************************************************************************/
bool tu_fifo_clear(tu_fifo_t *f)
{
- tu_fifo_lock(f);
+ _ff_lock(f->mutex_wr);
+ _ff_lock(f->mutex_rd);
+
f->rd_idx = f->wr_idx = 0;
f->max_pointer_idx = 2*f->depth-1;
f->non_used_index_space = UINT16_MAX - f->max_pointer_idx;
- tu_fifo_unlock(f);
+ _ff_unlock(f->mutex_wr);
+ _ff_unlock(f->mutex_rd);
return true;
}
@@ -613,15 +824,17 @@ bool tu_fifo_clear(tu_fifo_t *f)
Pointer to the FIFO buffer to manipulate
@param[in] overwritable
Overwritable mode the fifo is set to
-*/
+ */
/******************************************************************************/
bool tu_fifo_set_overwritable(tu_fifo_t *f, bool overwritable)
{
- tu_fifo_lock(f);
+ _ff_lock(f->mutex_wr);
+ _ff_lock(f->mutex_rd);
f->overwritable = overwritable;
- tu_fifo_unlock(f);
+ _ff_unlock(f->mutex_wr);
+ _ff_unlock(f->mutex_rd);
return true;
}
@@ -667,3 +880,146 @@ void tu_fifo_advance_read_pointer(tu_fifo_t *f, uint16_t n)
{
f->rd_idx = advance_pointer(f, f->rd_idx, n);
}
+
+/******************************************************************************/
+/*!
+ @brief Get linear read info
+
+ Returns the length and pointer from which bytes can be read in a linear manner.
+ This is of major interest for DMA transmissions. If returned length is zero the
+ corresponding pointer is invalid. The returned length is limited to the number
+ of ITEMS n which the user wants to write into the buffer.
+ The write pointer does NOT get advanced, use tu_fifo_advance_read_pointer() to
+ do so! If the length returned is less than n i.e. len<n, then a wrap occurs
+ and you need to execute this function a second time to get a pointer to the
+ wrapped part!
+ @param[in] f
+ Pointer to FIFO
+ @param[in] offset
+ Number of ITEMS to ignore before start writing
+ @param[out] **ptr
+ Pointer to start writing to
+ @param[in] n
+ Number of ITEMS to read from buffer
+ @return len
+ Length of linear part IN ITEMS, if zero corresponding pointer ptr is invalid
+ */
+/******************************************************************************/
+uint16_t tu_fifo_get_linear_read_info(tu_fifo_t *f, uint16_t offset, void **ptr, uint16_t n)
+{
+ // Operate on temporary values in case they change in between
+ uint16_t w = f->wr_idx, r = f->rd_idx;
+
+ uint16_t cnt = _tu_fifo_count(f, w, r);
+
+ // Check overflow and correct if required
+ if (cnt > f->depth)
+ {
+ _ff_lock(f->mutex_rd);
+ _tu_fifo_correct_read_pointer(f, w);
+ _ff_unlock(f->mutex_rd);
+ r = f->rd_idx;
+ cnt = f->depth;
+ }
+
+ // Skip beginning of buffer
+ if (cnt == 0 || offset >= cnt) return 0;
+
+ // Check if we can read something at and after offset - if too less is available we read what remains
+ cnt -= offset;
+ if (cnt < n) n = cnt;
+
+ // Get relative pointers
+ w = get_relative_pointer(f, w, 0);
+ r = get_relative_pointer(f, r, offset);
+
+ // Check if there is a wrap around necessary
+ uint16_t len;
+
+ if (w > r) {
+ len = w - r;
+ }
+ else
+ {
+ len = f->depth - r; // Also the case if FIFO was full
+ }
+
+ // Limit to required length
+ len = tu_min16(n, len);
+
+ // Copy pointer to buffer to start reading from
+ *ptr = &f->buffer[r];
+
+ return len;
+}
+
+/******************************************************************************/
+/*!
+ @brief Get linear write info
+
+ Returns the length and pointer from which bytes can be written into buffer array in a linear manner.
+ This is of major interest for DMA transmissions not using circular mode. If returned length is zero the
+ corresponding pointer is invalid. The returned length is limited to the number of BYTES n which the user
+ wants to write into the buffer.
+ The write pointer does NOT get advanced, use tu_fifo_advance_write_pointer() to do so! If the length
+ returned is less than n i.e. len<n, then a wrap occurs and you need to execute this function a second
+ time to get a pointer to the wrapped part!
+ @param[in] f
+ Pointer to FIFO
+ @param[in] offset
+ Number of ITEMS to ignore before start writing
+ @param[out] **ptr
+ Pointer to start writing to
+ @param[in] n
+ Number of ITEMS to write into buffer
+ @return len
+ Length of linear part IN ITEMS, if zero corresponding pointer ptr is invalid
+ */
+/******************************************************************************/
+uint16_t tu_fifo_get_linear_write_info(tu_fifo_t *f, uint16_t offset, void **ptr, uint16_t n)
+{
+ uint16_t w = f->wr_idx, r = f->rd_idx;
+ uint16_t free = _tu_fifo_remaining(f, w, r);
+
+ if (!f->overwritable)
+ {
+ // Not overwritable limit up to full
+ n = tu_min16(n, free);
+ }
+ else if (n >= f->depth)
+ {
+ // If overwrite is allowed it must be less than or equal to 2 x buffer length, otherwise the overflow can not be resolved by the read functions
+ TU_VERIFY(n <= 2*f->depth);
+
+ n = f->depth;
+ // We start writing at the read pointer's position since we fill the complete
+ // buffer and we do not want to modify the read pointer within a write function!
+ // This would end up in a race condition with read functions!
+ w = r;
+ }
+
+ // Check if there is room to write to
+ if (free == 0 || offset >= free) return 0;
+
+ // Get relative pointers
+ w = get_relative_pointer(f, w, offset);
+ r = get_relative_pointer(f, r, 0);
+ uint16_t len;
+
+ if (w < r)
+ {
+ len = r-w;
+ }
+ else
+ {
+ len = f->depth - w;
+ }
+
+ // Limit to required length
+ len = tu_min16(n, len);
+
+ // Copy pointer to buffer to start reading from
+ *ptr = &f->buffer[w];
+
+ return len;
+}
diff --git a/src/common/tusb_fifo.h b/src/common/tusb_fifo.h
index 294378496..b2d0b5be9 100644
--- a/src/common/tusb_fifo.h
+++ b/src/common/tusb_fifo.h
@@ -2,6 +2,7 @@
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
+ * Copyright (c) 2020 Reinhard Panhuber - rework to unmasked pointers
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -52,10 +53,10 @@ extern "C" {
#endif
#if CFG_FIFO_MUTEX
+#include "osal/osal.h"
#define tu_fifo_mutex_t osal_mutex_t
#endif
-
/** \struct tu_fifo_t
* \brief Simple Circular FIFO
*/
@@ -66,14 +67,15 @@ typedef struct
uint16_t item_size ; ///< size of each item
bool overwritable ;
- uint16_t max_pointer_idx ; ///< maximum absolute pointer index
uint16_t non_used_index_space ; ///< required for non-power-of-two buffer length
+ uint16_t max_pointer_idx ; ///< maximum absolute pointer index
volatile uint16_t wr_idx ; ///< write pointer
volatile uint16_t rd_idx ; ///< read pointer
#if CFG_FIFO_MUTEX
- tu_fifo_mutex_t mutex;
+ tu_fifo_mutex_t mutex_wr;
+ tu_fifo_mutex_t mutex_rd;
#endif
} tu_fifo_t;
@@ -85,29 +87,33 @@ typedef struct
.item_size = sizeof(_type), \
.overwritable = _overwritable, \
.max_pointer_idx = 2*(_depth)-1, \
- .non_used_index_space = UINT16_MAX - (2*(_depth)-1) \
+ .non_used_index_space = UINT16_MAX - (2*(_depth)-1), \
}
#define TU_FIFO_DEF(_name, _depth, _type, _overwritable) \
uint8_t _name##_buf[_depth*sizeof(_type)]; \
tu_fifo_t _name = TU_FIFO_INIT(_name##_buf, _depth, _type, _overwritable)
+
bool tu_fifo_set_overwritable(tu_fifo_t *f, bool overwritable);
bool tu_fifo_clear(tu_fifo_t *f);
bool tu_fifo_config(tu_fifo_t *f, void* buffer, uint16_t depth, uint16_t item_size, bool overwritable);
#if CFG_FIFO_MUTEX
-static inline void tu_fifo_config_mutex(tu_fifo_t *f, tu_fifo_mutex_t mutex_hdl)
+static inline void tu_fifo_config_mutex(tu_fifo_t *f, tu_fifo_mutex_t write_mutex_hdl, tu_fifo_mutex_t read_mutex_hdl)
{
- f->mutex = mutex_hdl;
+ f->mutex_wr = write_mutex_hdl;
+ f->mutex_rd = read_mutex_hdl;
}
#endif
bool tu_fifo_write (tu_fifo_t* f, void const * p_data);
-uint16_t tu_fifo_write_n (tu_fifo_t* f, void const * p_data, uint16_t count);
+uint16_t tu_fifo_write_n (tu_fifo_t* f, void const * p_data, uint16_t n);
+uint16_t tu_fifo_write_n_const_addr_full_words (tu_fifo_t* f, const void * data, uint16_t n);
bool tu_fifo_read (tu_fifo_t* f, void * p_buffer);
-uint16_t tu_fifo_read_n (tu_fifo_t* f, void * p_buffer, uint16_t count);
+uint16_t tu_fifo_read_n (tu_fifo_t* f, void * p_buffer, uint16_t n);
+uint16_t tu_fifo_read_n_const_addr_full_words (tu_fifo_t* f, void * buffer, uint16_t n);
bool tu_fifo_peek_at (tu_fifo_t* f, uint16_t pos, void * p_buffer);
uint16_t tu_fifo_peek_at_n (tu_fifo_t* f, uint16_t pos, void * p_buffer, uint16_t n);
@@ -116,7 +122,7 @@ uint16_t tu_fifo_count (tu_fifo_t* f);
bool tu_fifo_empty (tu_fifo_t* f);
bool tu_fifo_full (tu_fifo_t* f);
uint16_t tu_fifo_remaining (tu_fifo_t* f);
-bool tu_fifo_overflowed (tu_fifo_t* f);
+bool tu_fifo_overflowed (tu_fifo_t* f);
void tu_fifo_correct_read_pointer (tu_fifo_t* f);
// Pointer modifications intended to be used in combinations with DMAs.
@@ -124,6 +130,13 @@ void tu_fifo_correct_read_pointer (tu_fifo_t* f);
void tu_fifo_advance_write_pointer (tu_fifo_t *f, uint16_t n);
void tu_fifo_advance_read_pointer (tu_fifo_t *f, uint16_t n);
+// If you want to read/write from/to the FIFO by use of a DMA, you may need to conduct two copies to handle a possible wrapping part
+// This functions deliver a pointer to start reading/writing from/to and a valid linear length along which no wrap occurs.
+// In case not all of your data is available within one read/write, update the read/write pointer by
+// tu_fifo_advance_read_pointer()/tu_fifo_advance_write_pointer and conduct a second read/write operation
+uint16_t tu_fifo_get_linear_read_info (tu_fifo_t *f, uint16_t offset, void **ptr, uint16_t n);
+uint16_t tu_fifo_get_linear_write_info (tu_fifo_t *f, uint16_t offset, void **ptr, uint16_t n);
+
static inline bool tu_fifo_peek(tu_fifo_t* f, void * p_buffer)
{
return tu_fifo_peek_at(f, 0, p_buffer);
diff --git a/src/device/dcd.h b/src/device/dcd.h
index b7e5a8da0..1e5b3ff1e 100644
--- a/src/device/dcd.h
+++ b/src/device/dcd.h
@@ -32,6 +32,7 @@
#define _TUSB_DCD_H_
#include "common/tusb_common.h"
+#include "common/tusb_fifo.h"
#ifdef __cplusplus
extern "C" {
@@ -124,20 +125,24 @@ void dcd_disconnect(uint8_t rhport) TU_ATTR_WEAK;
void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const * request) TU_ATTR_WEAK;
// Configure endpoint's registers according to descriptor
-bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc);
+bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc);
// Close an endpoint.
// Since it is weak, caller must TU_ASSERT this function's existence before calling it.
-void dcd_edpt_close (uint8_t rhport, uint8_t ep_addr) TU_ATTR_WEAK;
+void dcd_edpt_close (uint8_t rhport, uint8_t ep_addr) TU_ATTR_WEAK;
// Submit a transfer, When complete dcd_event_xfer_complete() is invoked to notify the stack
-bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes);
+bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes);
+
+// Submit an transfer using fifo, When complete dcd_event_xfer_complete() is invoked to notify the stack
+// This API is optional, may be useful for register-based for transferring data.
+bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes) TU_ATTR_WEAK;
// Stall endpoint
-void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr);
+void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr);
// clear stall, data toggle is also reset to DATA0
-void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr);
+void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr);
//--------------------------------------------------------------------+
// Event API (implemented by stack)
diff --git a/src/device/usbd.c b/src/device/usbd.c
index ca5a482fc..8a3d8ec2f 100644
--- a/src/device/usbd.c
+++ b/src/device/usbd.c
@@ -29,9 +29,9 @@
#if TUSB_OPT_DEVICE_ENABLED
#include "tusb.h"
-#include "usbd.h"
+#include "device/usbd.h"
#include "device/usbd_pvt.h"
-#include "dcd.h"
+#include "device/dcd.h"
#ifndef CFG_TUD_TASK_QUEUE_SZ
#define CFG_TUD_TASK_QUEUE_SZ 16
@@ -1241,6 +1241,39 @@ bool usbd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
}
}
+// The number of bytes has to be given explicitly to allow more flexible control of how many
+// bytes should be written and second to keep the return value free to give back a boolean
+// success message. If total_bytes is too big, the FIFO will copy only what is available
+// into the USB buffer!
+bool usbd_edpt_iso_xfer(uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes)
+{
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+
+ TU_LOG2(" Queue ISO EP %02X with %u bytes ... ", ep_addr, total_bytes);
+
+ // Attempt to transfer on a busy endpoint, sound like an race condition !
+ TU_ASSERT(_usbd_dev.ep_status[epnum][dir].busy == 0);
+
+ // Set busy first since the actual transfer can be complete before dcd_edpt_xfer() could return
+ // and usbd task can preempt and clear the busy
+ _usbd_dev.ep_status[epnum][dir].busy = true;
+
+ if (dcd_edpt_xfer_fifo(rhport, ep_addr, ff, total_bytes))
+ {
+ TU_LOG2("OK\r\n");
+ return true;
+ }else
+ {
+ // DCD error, mark endpoint as ready to allow next transfer
+ _usbd_dev.ep_status[epnum][dir].busy = false;
+ _usbd_dev.ep_status[epnum][dir].claimed = 0;
+ TU_LOG2("failed\r\n");
+ TU_BREAKPOINT();
+ return false;
+ }
+}
+
bool usbd_edpt_busy(uint8_t rhport, uint8_t ep_addr)
{
(void) rhport;
diff --git a/src/device/usbd.h b/src/device/usbd.h
index 56615b081..7c50a6db0 100644
--- a/src/device/usbd.h
+++ b/src/device/usbd.h
@@ -353,6 +353,10 @@ TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb
#define TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL_LEN (6+(2+1)*4)
#define TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL(_unitid, _srcid, _ctrlch0master, _ctrlch1, _ctrlch2, _stridx) \
TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL_LEN, TUSB_DESC_CS_INTERFACE, AUDIO_CS_AC_INTERFACE_FEATURE_UNIT, _unitid, _srcid, U32_TO_U8S_LE(_ctrlch0master), U32_TO_U8S_LE(_ctrlch1), U32_TO_U8S_LE(_ctrlch2), _stridx
+// 4 - Channels
+#define TUD_AUDIO_DESC_FEATURE_UNIT_FOUR_CHANNEL_LEN (6+(4+1)*4)
+#define TUD_AUDIO_DESC_FEATURE_UNIT_FOUR_CHANNEL(_unitid, _srcid, _ctrlch0master, _ctrlch1, _ctrlch2, _ctrlch3, _ctrlch4, _stridx) \
+ TUD_AUDIO_DESC_FEATURE_UNIT_FOUR_CHANNEL_LEN, TUSB_DESC_CS_INTERFACE, AUDIO_CS_AC_INTERFACE_FEATURE_UNIT, _unitid, _srcid, U32_TO_U8S_LE(_ctrlch0master), U32_TO_U8S_LE(_ctrlch1), U32_TO_U8S_LE(_ctrlch2), U32_TO_U8S_LE(_ctrlch3), U32_TO_U8S_LE(_ctrlch4), _stridx
// For more channels, add definitions here
@@ -389,7 +393,7 @@ TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb
// AUDIO simple descriptor (UAC2) for 1 microphone input
// - 1 Input Terminal, 1 Feature Unit (Mute and Volume Control), 1 Output Terminal, 1 Clock Source
-#define TUD_AUDIO_MIC_DESC_LEN (TUD_AUDIO_DESC_IAD_LEN\
+#define TUD_AUDIO_MIC_ONE_CH_DESC_LEN (TUD_AUDIO_DESC_IAD_LEN\
+ TUD_AUDIO_DESC_STD_AC_LEN\
+ TUD_AUDIO_DESC_CS_AC_LEN\
+ TUD_AUDIO_DESC_CLK_SRC_LEN\
@@ -403,9 +407,9 @@ TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb
+ TUD_AUDIO_DESC_STD_AS_ISO_EP_LEN\
+ TUD_AUDIO_DESC_CS_AS_ISO_EP_LEN)
-#define TUD_AUDIO_MIC_DESC_N_AS_INT 1 // Number of AS interfaces
+#define TUD_AUDIO_MIC_ONE_CH_DESC_N_AS_INT 1 // Number of AS interfaces
-#define TUD_AUDIO_MIC_DESCRIPTOR(_itfnum, _stridx, _nBytesPerSample, _nBitsUsedPerSample, _epin, _epsize) \
+#define TUD_AUDIO_MIC_ONE_CH_DESCRIPTOR(_itfnum, _stridx, _nBytesPerSample, _nBitsUsedPerSample, _epin, _epsize) \
/* Standard Interface Association Descriptor (IAD) */\
TUD_AUDIO_DESC_IAD(/*_firstitfs*/ _itfnum, /*_nitfs*/ 0x02, /*_stridx*/ 0x00),\
/* Standard AC Interface Descriptor(4.7.1) */\
@@ -435,6 +439,55 @@ TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb
/* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */\
TUD_AUDIO_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO_CTRL_NONE, /*_lockdelayunit*/ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, /*_lockdelay*/ 0x0000)
+// AUDIO simple descriptor (UAC2) for 4 microphone input
+// - 1 Input Terminal, 1 Feature Unit (Mute and Volume Control), 1 Output Terminal, 1 Clock Source
+
+#define TUD_AUDIO_MIC_FOUR_CH_DESC_LEN (TUD_AUDIO_DESC_IAD_LEN\
+ + TUD_AUDIO_DESC_STD_AC_LEN\
+ + TUD_AUDIO_DESC_CS_AC_LEN\
+ + TUD_AUDIO_DESC_CLK_SRC_LEN\
+ + TUD_AUDIO_DESC_INPUT_TERM_LEN\
+ + TUD_AUDIO_DESC_OUTPUT_TERM_LEN\
+ + TUD_AUDIO_DESC_FEATURE_UNIT_FOUR_CHANNEL_LEN\
+ + TUD_AUDIO_DESC_STD_AS_INT_LEN\
+ + TUD_AUDIO_DESC_STD_AS_INT_LEN\
+ + TUD_AUDIO_DESC_CS_AS_INT_LEN\
+ + TUD_AUDIO_DESC_TYPE_I_FORMAT_LEN\
+ + TUD_AUDIO_DESC_STD_AS_ISO_EP_LEN\
+ + TUD_AUDIO_DESC_CS_AS_ISO_EP_LEN)
+
+#define TUD_AUDIO_MIC_FOUR_CH_DESC_N_AS_INT 1 // Number of AS interfaces
+
+#define TUD_AUDIO_MIC_FOUR_CH_DESCRIPTOR(_itfnum, _stridx, _nBytesPerSample, _nBitsUsedPerSample, _epin, _epsize) \
+ /* Standard Interface Association Descriptor (IAD) */\
+ TUD_AUDIO_DESC_IAD(/*_firstitfs*/ _itfnum, /*_nitfs*/ 0x02, /*_stridx*/ 0x00),\
+ /* Standard AC Interface Descriptor(4.7.1) */\
+ TUD_AUDIO_DESC_STD_AC(/*_itfnum*/ _itfnum, /*_nEPs*/ 0x00, /*_stridx*/ _stridx),\
+ /* Class-Specific AC Interface Header Descriptor(4.7.2) */\
+ TUD_AUDIO_DESC_CS_AC(/*_bcdADC*/ 0x0200, /*_category*/ AUDIO_FUNC_MICROPHONE, /*_totallen*/ TUD_AUDIO_DESC_CLK_SRC_LEN+TUD_AUDIO_DESC_INPUT_TERM_LEN+TUD_AUDIO_DESC_OUTPUT_TERM_LEN+TUD_AUDIO_DESC_FEATURE_UNIT_FOUR_CHANNEL_LEN, /*_ctrl*/ AUDIO_CS_AS_INTERFACE_CTRL_LATENCY_POS),\
+ /* Clock Source Descriptor(4.7.2.1) */\
+ TUD_AUDIO_DESC_CLK_SRC(/*_clkid*/ 0x04, /*_attr*/ AUDIO_CLOCK_SOURCE_ATT_INT_FIX_CLK, /*_ctrl*/ (AUDIO_CTRL_R << AUDIO_CLOCK_SOURCE_CTRL_CLK_FRQ_POS), /*_assocTerm*/ 0x01, /*_stridx*/ 0x00),\
+ /* Input Terminal Descriptor(4.7.2.4) */\
+ TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ 0x01, /*_termtype*/ AUDIO_TERM_TYPE_IN_GENERIC_MIC, /*_assocTerm*/ 0x03, /*_clkid*/ 0x04, /*_nchannelslogical*/ 0x04, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS, /*_stridx*/ 0x00),\
+ /* Output Terminal Descriptor(4.7.2.5) */\
+ TUD_AUDIO_DESC_OUTPUT_TERM(/*_termid*/ 0x03, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ 0x01, /*_srcid*/ 0x02, /*_clkid*/ 0x04, /*_ctrl*/ 0x0000, /*_stridx*/ 0x00),\
+ /* Feature Unit Descriptor(4.7.2.8) */\
+ TUD_AUDIO_DESC_FEATURE_UNIT_FOUR_CHANNEL(/*_unitid*/ 0x02, /*_srcid*/ 0x01, /*_ctrlch0master*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch1*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch2*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch3*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch4*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_stridx*/ 0x00),\
+ /* Standard AS Interface Descriptor(4.9.1) */\
+ /* Interface 1, Alternate 0 - default alternate setting with 0 bandwidth */\
+ TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum)+1), /*_altset*/ 0x00, /*_nEPs*/ 0x00, /*_stridx*/ 0x00),\
+ /* Standard AS Interface Descriptor(4.9.1) */\
+ /* Interface 1, Alternate 1 - alternate interface for data streaming */\
+ TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum)+1), /*_altset*/ 0x01, /*_nEPs*/ 0x01, /*_stridx*/ 0x00),\
+ /* Class-Specific AS Interface Descriptor(4.9.2) */\
+ TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ 0x03, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ 0x04, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00),\
+ /* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */\
+ TUD_AUDIO_DESC_TYPE_I_FORMAT(_nBytesPerSample, _nBitsUsedPerSample),\
+ /* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */\
+ TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epin, /*_attr*/ (TUSB_XFER_ISOCHRONOUS | TUSB_ISO_EP_ATT_ASYNCHRONOUS | TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ _epsize, /*_interval*/ (CFG_TUSB_RHPORT0_MODE & OPT_MODE_HIGH_SPEED) ? 0x04 : 0x01),\
+ /* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */\
+ TUD_AUDIO_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO_CTRL_NONE, /*_lockdelayunit*/ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, /*_lockdelay*/ 0x0000)
+
// AUDIO simple descriptor (UAC2) for mono speaker
// - 1 Input Terminal, 2 Feature Unit (Mute and Volume Control), 3 Output Terminal, 4 Clock Source
diff --git a/src/device/usbd_pvt.h b/src/device/usbd_pvt.h
index 212c3a202..412a97a5d 100644
--- a/src/device/usbd_pvt.h
+++ b/src/device/usbd_pvt.h
@@ -72,6 +72,9 @@ void usbd_edpt_close(uint8_t rhport, uint8_t ep_addr);
// Submit a usb transfer
bool usbd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes);
+// Submit a usb ISO transfer by use of a FIFO (ring buffer) - all bytes in FIFO get transmitted
+bool usbd_edpt_iso_xfer(uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes);
+
// Claim an endpoint before submitting a transfer.
// If caller does not make any transfer, it must release endpoint for others.
bool usbd_edpt_claim(uint8_t rhport, uint8_t ep_addr);
diff --git a/src/osal/osal_pico.h b/src/osal/osal_pico.h
index c277af271..bae1217eb 100644
--- a/src/osal/osal_pico.h
+++ b/src/osal/osal_pico.h
@@ -143,7 +143,7 @@ static inline bool osal_queue_receive(osal_queue_t qhdl, void* data)
// TODO: revisit... docs say that mutexes are never used from IRQ context,
// however osal_queue_recieve may be. therefore my assumption is that
// the fifo mutex is not populated for queues used from an IRQ context
- assert(!qhdl->ff.mutex);
+ //assert(!qhdl->ff.mutex);
_osal_q_lock(qhdl);
bool success = tu_fifo_read(&qhdl->ff, data);
@@ -157,7 +157,7 @@ static inline bool osal_queue_send(osal_queue_t qhdl, void const * data, bool in
// TODO: revisit... docs say that mutexes are never used from IRQ context,
// however osal_queue_recieve may be. therefore my assumption is that
// the fifo mutex is not populated for queues used from an IRQ context
- assert(!qhdl->ff.mutex);
+ //assert(!qhdl->ff.mutex);
_osal_q_lock(qhdl);
bool success = tu_fifo_write(&qhdl->ff, data);
diff --git a/src/portable/espressif/esp32s2/dcd_esp32s2.c b/src/portable/espressif/esp32s2/dcd_esp32s2.c
index 703841759..edbc872fe 100644
--- a/src/portable/espressif/esp32s2/dcd_esp32s2.c
+++ b/src/portable/espressif/esp32s2/dcd_esp32s2.c
@@ -27,6 +27,7 @@
*/
#include "tusb_option.h"
+#include "common/tusb_fifo.h"
#if CFG_TUSB_MCU == OPT_MCU_ESP32S2 && TUSB_OPT_DEVICE_ENABLED
@@ -59,6 +60,7 @@
typedef struct {
uint8_t *buffer;
+ // tu_fifo_t * ff; // TODO support dcd_edpt_xfer_fifo API
uint16_t total_len;
uint16_t queued_len;
uint16_t max_size;
@@ -319,6 +321,7 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t to
xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, dir);
xfer->buffer = buffer;
+ // xfer->ff = NULL; // TODO support dcd_edpt_xfer_fifo API
xfer->total_len = total_bytes;
xfer->queued_len = 0;
xfer->short_packet = false;
@@ -354,6 +357,56 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t to
return true;
}
+#if 0 // TODO support dcd_edpt_xfer_fifo API
+bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes)
+{
+ (void)rhport;
+
+ // USB buffers always work in bytes so to avoid unnecessary divisions we demand item_size = 1
+ TU_ASSERT(ff->item_size == 1);
+
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+
+ xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, dir);
+ xfer->buffer = NULL;
+ xfer->ff = ff;
+ xfer->total_len = total_bytes;
+ xfer->queued_len = 0;
+ xfer->short_packet = false;
+
+ uint16_t num_packets = (total_bytes / xfer->max_size);
+ uint8_t short_packet_size = total_bytes % xfer->max_size;
+
+ // Zero-size packet is special case.
+ if (short_packet_size > 0 || (total_bytes == 0)) {
+ num_packets++;
+ }
+
+ ESP_LOGV(TAG, "Transfer <-> EP%i, %s, pkgs: %i, bytes: %i",
+ epnum, ((dir == TUSB_DIR_IN) ? "USB0.HOST (in)" : "HOST->DEV (out)"),
+ num_packets, total_bytes);
+
+ // IN and OUT endpoint xfers are interrupt-driven, we just schedule them
+ // here.
+ if (dir == TUSB_DIR_IN) {
+ // A full IN transfer (multiple packets, possibly) triggers XFRC.
+ USB0.in_ep_reg[epnum].dieptsiz = (num_packets << USB_D_PKTCNT0_S) | total_bytes;
+ USB0.in_ep_reg[epnum].diepctl |= USB_D_EPENA1_M | USB_D_CNAK1_M; // Enable | CNAK
+
+ // Enable fifo empty interrupt only if there are something to put in the fifo.
+ if(total_bytes != 0) {
+ USB0.dtknqr4_fifoemptymsk |= (1 << epnum);
+ }
+ } else {
+ // Each complete packet for OUT xfers triggers XFRC.
+ USB0.out_ep_reg[epnum].doeptsiz |= USB_PKTCNT0_M | ((xfer->max_size & USB_XFERSIZE0_V) << USB_XFERSIZE0_S);
+ USB0.out_ep_reg[epnum].doepctl |= USB_EPENA0_M | USB_CNAK0_M;
+ }
+ return true;
+}
+#endif
+
void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
{
(void)rhport;
@@ -462,35 +515,46 @@ static void receive_packet(xfer_ctl_t *xfer, /* usb_out_endpoint_t * out_ep, */
to_recv_size = (xfer_size > xfer->max_size) ? xfer->max_size : xfer_size;
}
- uint8_t to_recv_rem = to_recv_size % 4;
- uint16_t to_recv_size_aligned = to_recv_size - to_recv_rem;
+ // Common buffer read
+#if 0 // TODO support dcd_edpt_xfer_fifo API
+ if (xfer->ff)
+ {
+ // Ring buffer
+ tu_fifo_write_n_const_addr_full_words(xfer->ff, (const void *) rx_fifo, to_recv_size);
+ }
+ else
+#endif
+ {
+ uint8_t to_recv_rem = to_recv_size % 4;
+ uint16_t to_recv_size_aligned = to_recv_size - to_recv_rem;
- // Do not assume xfer buffer is aligned.
- uint8_t *base = (xfer->buffer + xfer->queued_len);
+ // Do not assume xfer buffer is aligned.
+ uint8_t *base = (xfer->buffer + xfer->queued_len);
- // This for loop always runs at least once- skip if less than 4 bytes
- // to collect.
- if (to_recv_size >= 4) {
- for (uint16_t i = 0; i < to_recv_size_aligned; i += 4) {
- uint32_t tmp = (*rx_fifo);
- base[i] = tmp & 0x000000FF;
- base[i + 1] = (tmp & 0x0000FF00) >> 8;
- base[i + 2] = (tmp & 0x00FF0000) >> 16;
- base[i + 3] = (tmp & 0xFF000000) >> 24;
+ // This for loop always runs at least once- skip if less than 4 bytes
+ // to collect.
+ if (to_recv_size >= 4) {
+ for (uint16_t i = 0; i < to_recv_size_aligned; i += 4) {
+ uint32_t tmp = (*rx_fifo);
+ base[i] = tmp & 0x000000FF;
+ base[i + 1] = (tmp & 0x0000FF00) >> 8;
+ base[i + 2] = (tmp & 0x00FF0000) >> 16;
+ base[i + 3] = (tmp & 0xFF000000) >> 24;
+ }
}
- }
- // Do not read invalid bytes from RX FIFO.
- if (to_recv_rem != 0) {
- uint32_t tmp = (*rx_fifo);
- uint8_t *last_32b_bound = base + to_recv_size_aligned;
+ // Do not read invalid bytes from RX FIFO.
+ if (to_recv_rem != 0) {
+ uint32_t tmp = (*rx_fifo);
+ uint8_t *last_32b_bound = base + to_recv_size_aligned;
- last_32b_bound[0] = tmp & 0x000000FF;
- if (to_recv_rem > 1) {
- last_32b_bound[1] = (tmp & 0x0000FF00) >> 8;
- }
- if (to_recv_rem > 2) {
- last_32b_bound[2] = (tmp & 0x00FF0000) >> 16;
+ last_32b_bound[0] = tmp & 0x000000FF;
+ if (to_recv_rem > 1) {
+ last_32b_bound[1] = (tmp & 0x0000FF00) >> 8;
+ }
+ if (to_recv_rem > 2) {
+ last_32b_bound[2] = (tmp & 0x00FF0000) >> 16;
+ }
}
}
@@ -510,37 +574,47 @@ static void transmit_packet(xfer_ctl_t *xfer, volatile usb_in_endpoint_t *in_ep,
xfer->queued_len = xfer->total_len - remaining;
uint16_t to_xfer_size = (remaining > xfer->max_size) ? xfer->max_size : remaining;
- uint8_t to_xfer_rem = to_xfer_size % 4;
- uint16_t to_xfer_size_aligned = to_xfer_size - to_xfer_rem;
- // Buffer might not be aligned to 32b, so we need to force alignment
- // by copying to a temp var.
- uint8_t *base = (xfer->buffer + xfer->queued_len);
-
- // This for loop always runs at least once- skip if less than 4 bytes
- // to send off.
- if (to_xfer_size >= 4) {
- for (uint16_t i = 0; i < to_xfer_size_aligned; i += 4) {
- uint32_t tmp = base[i] | (base[i + 1] << 8) |
- (base[i + 2] << 16) | (base[i + 3] << 24);
- (*tx_fifo) = tmp;
- }
+#if 0 // TODO support dcd_edpt_xfer_fifo API
+ if (xfer->ff)
+ {
+ tu_fifo_read_n_const_addr_full_words(xfer->ff, (void *) tx_fifo, to_xfer_size);
}
+ else
+#endif
+ {
+ uint8_t to_xfer_rem = to_xfer_size % 4;
+ uint16_t to_xfer_size_aligned = to_xfer_size - to_xfer_rem;
- // Do not read beyond end of buffer if not divisible by 4.
- if (to_xfer_rem != 0) {
- uint32_t tmp = 0;
- uint8_t *last_32b_bound = base + to_xfer_size_aligned;
+ // Buffer might not be aligned to 32b, so we need to force alignment
+ // by copying to a temp var.
+ uint8_t *base = (xfer->buffer + xfer->queued_len);
- tmp |= last_32b_bound[0];
- if (to_xfer_rem > 1) {
- tmp |= (last_32b_bound[1] << 8);
- }
- if (to_xfer_rem > 2) {
- tmp |= (last_32b_bound[2] << 16);
+ // This for loop always runs at least once- skip if less than 4 bytes
+ // to send off.
+ if (to_xfer_size >= 4) {
+ for (uint16_t i = 0; i < to_xfer_size_aligned; i += 4) {
+ uint32_t tmp = base[i] | (base[i + 1] << 8) |
+ (base[i + 2] << 16) | (base[i + 3] << 24);
+ (*tx_fifo) = tmp;
+ }
}
- (*tx_fifo) = tmp;
+ // Do not read beyond end of buffer if not divisible by 4.
+ if (to_xfer_rem != 0) {
+ uint32_t tmp = 0;
+ uint8_t *last_32b_bound = base + to_xfer_size_aligned;
+
+ tmp |= last_32b_bound[0];
+ if (to_xfer_rem > 1) {
+ tmp |= (last_32b_bound[1] << 8);
+ }
+ if (to_xfer_rem > 2) {
+ tmp |= (last_32b_bound[2] << 16);
+ }
+
+ (*tx_fifo) = tmp;
+ }
}
}
diff --git a/src/portable/microchip/samg/dcd_samg.c b/src/portable/microchip/samg/dcd_samg.c
index 2f9f1097f..62fab8d17 100644
--- a/src/portable/microchip/samg/dcd_samg.c
+++ b/src/portable/microchip/samg/dcd_samg.c
@@ -25,6 +25,7 @@
*/
#include "tusb_option.h"
+#include "common/tusb_fifo.h"
#if CFG_TUSB_MCU == OPT_MCU_SAMG
@@ -43,6 +44,7 @@
typedef struct
{
uint8_t* buffer;
+ // tu_fifo_t* ff; // TODO support dcd_edpt_xfer_fifo API
uint16_t total_len;
volatile uint16_t actual_len;
uint16_t epsize;
@@ -59,6 +61,7 @@ void xfer_epsize_set(xfer_desc_t* xfer, uint16_t epsize)
void xfer_begin(xfer_desc_t* xfer, uint8_t * buffer, uint16_t total_bytes)
{
xfer->buffer = buffer;
+ // xfer->ff = NULL; // TODO support dcd_edpt_xfer_fifo API
xfer->total_len = total_bytes;
xfer->actual_len = 0;
}
@@ -66,6 +69,7 @@ void xfer_begin(xfer_desc_t* xfer, uint8_t * buffer, uint16_t total_bytes)
void xfer_end(xfer_desc_t* xfer)
{
xfer->buffer = NULL;
+ // xfer->ff = NULL; // TODO support dcd_edpt_xfer_fifo API
xfer->total_len = 0;
xfer->actual_len = 0;
}
@@ -293,6 +297,14 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
return true;
}
+#if 0 // TODO support dcd_edpt_xfer_fifo API
+bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes)
+{
+ (void) rhport;
+ return true;
+}
+#endif
+
// Stall endpoint
void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr)
{
@@ -402,7 +414,16 @@ void dcd_int_handler(uint8_t rhport)
if (xact_len)
{
// write to EP fifo
- xact_ep_write(epnum, xfer->buffer, xact_len);
+#if 0 // TODO support dcd_edpt_xfer_fifo
+ if (xfer->ff)
+ {
+ tu_fifo_read_n_const_addr_full_words(xfer->ff, (void *) &UDP->UDP_FDR[epnum], xact_len);
+ }
+ else
+#endif
+ {
+ xact_ep_write(epnum, xfer->buffer, xact_len);
+ }
// TX ready for transfer
csr_set(epnum, UDP_CSR_TXPKTRDY_Msk);
@@ -428,7 +449,17 @@ void dcd_int_handler(uint8_t rhport)
uint16_t const xact_len = (uint16_t) ((UDP->UDP_CSR[epnum] & UDP_CSR_RXBYTECNT_Msk) >> UDP_CSR_RXBYTECNT_Pos);
// Read from EP fifo
- xact_ep_read(epnum, xfer->buffer, xact_len);
+#if 0 // TODO support dcd_edpt_xfer_fifo API
+ if (xfer->ff)
+ {
+ tu_fifo_write_n_const_addr_full_words(xfer->ff, (const void *) &UDP->UDP_FDR[epnum], xact_len);
+ }
+ else
+#endif
+ {
+ xact_ep_read(epnum, xfer->buffer, xact_len);
+ }
+
xfer_packet_done(xfer);
if ( 0 == xfer_packet_len(xfer) )
diff --git a/src/portable/nuvoton/nuc120/dcd_nuc120.c b/src/portable/nuvoton/nuc120/dcd_nuc120.c
index dc48e54cc..5dc3bca21 100644
--- a/src/portable/nuvoton/nuc120/dcd_nuc120.c
+++ b/src/portable/nuvoton/nuc120/dcd_nuc120.c
@@ -34,6 +34,7 @@
*/
#include "tusb_option.h"
+#include "common/tusb_fifo.h"
#if TUSB_OPT_DEVICE_ENABLED && (CFG_TUSB_MCU == OPT_MCU_NUC120)
@@ -76,6 +77,7 @@ static bool active_ep0_xfer;
static struct xfer_ctl_t
{
uint8_t *data_ptr; /* data_ptr tracks where to next copy data to (for OUT) or from (for IN) */
+ // tu_fifo_t * ff; /* pointer to FIFO required for dcd_edpt_xfer_fifo() */ // TODO support dcd_edpt_xfer_fifo API
union {
uint16_t in_remaining_bytes; /* for IN endpoints, we track how many bytes are left to transfer */
uint16_t out_bytes_so_far; /* but for OUT endpoints, we track how many bytes we've transferred so far */
@@ -142,7 +144,17 @@ static void dcd_in_xfer(struct xfer_ctl_t *xfer, USBD_EP_T *ep)
{
uint16_t bytes_now = tu_min16(xfer->in_remaining_bytes, xfer->max_packet_size);
- memcpy((uint8_t *)(USBD_BUF_BASE + ep->BUFSEG), xfer->data_ptr, bytes_now);
+#if 0 // TODO support dcd_edpt_xfer_fifo API
+ if (xfer->ff)
+ {
+ tu_fifo_read_n(xfer->ff, (void *) (USBD_BUF_BASE + ep->BUFSEG), bytes_now);
+ }
+ else
+#endif
+ {
+ memcpy((uint8_t *)(USBD_BUF_BASE + ep->BUFSEG), xfer->data_ptr, bytes_now);
+ }
+
ep->MXPLD = bytes_now;
}
@@ -267,6 +279,7 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t to
/* store away the information we'll needing now and later */
xfer->data_ptr = buffer;
+ // xfer->ff = NULL; // TODO support dcd_edpt_xfer_fifo API
xfer->in_remaining_bytes = total_bytes;
xfer->total_bytes = total_bytes;
@@ -286,6 +299,36 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t to
return true;
}
+#if 0 // TODO support dcd_edpt_xfer_fifo API
+bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes)
+{
+ (void) rhport;
+
+ /* mine the data for the information we need */
+ tusb_dir_t dir = tu_edpt_dir(ep_addr);
+ USBD_EP_T *ep = ep_entry(ep_addr, false);
+ struct xfer_ctl_t *xfer = &xfer_table[ep - USBD->EP];
+
+ /* store away the information we'll needing now and later */
+ xfer->data_ptr = NULL; // Indicates a FIFO shall be used
+ xfer->ff = ff;
+ xfer->in_remaining_bytes = total_bytes;
+ xfer->total_bytes = total_bytes;
+
+ if (TUSB_DIR_IN == dir)
+ {
+ dcd_in_xfer(xfer, ep);
+ }
+ else
+ {
+ xfer->out_bytes_so_far = 0;
+ ep->MXPLD = xfer->max_packet_size;
+ }
+
+ return true;
+}
+#endif
+
void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
{
(void) rhport;
@@ -389,9 +432,19 @@ void dcd_int_handler(uint8_t rhport)
if (out_ep)
{
/* copy the data from the PC to the previously provided buffer */
- memcpy(xfer->data_ptr, (uint8_t *)(USBD_BUF_BASE + ep->BUFSEG), available_bytes);
+#if 0 // // TODO support dcd_edpt_xfer_fifo API
+ if (xfer->ff)
+ {
+ tu_fifo_write_n(xfer->ff, (const void *) (USBD_BUF_BASE + ep->BUFSEG), available_bytes);
+ }
+ else
+#endif
+ {
+ memcpy(xfer->data_ptr, (uint8_t *)(USBD_BUF_BASE + ep->BUFSEG), available_bytes);
+ xfer->data_ptr += available_bytes;
+ }
+
xfer->out_bytes_so_far += available_bytes;
- xfer->data_ptr += available_bytes;
/* when the transfer is finished, alert TinyUSB; otherwise, accept more data */
if ( (xfer->total_bytes == xfer->out_bytes_so_far) || (available_bytes < xfer->max_packet_size) )
@@ -403,6 +456,7 @@ void dcd_int_handler(uint8_t rhport)
{
/* update the bookkeeping to reflect the data that has now been sent to the PC */
xfer->in_remaining_bytes -= available_bytes;
+
xfer->data_ptr += available_bytes;
/* if more data to send, send it; otherwise, alert TinyUSB that we've finished */
diff --git a/src/portable/nuvoton/nuc121/dcd_nuc121.c b/src/portable/nuvoton/nuc121/dcd_nuc121.c
index c9ead6de0..d50e82846 100644
--- a/src/portable/nuvoton/nuc121/dcd_nuc121.c
+++ b/src/portable/nuvoton/nuc121/dcd_nuc121.c
@@ -34,6 +34,7 @@
*/
#include "tusb_option.h"
+#include "common/tusb_fifo.h"
#if TUSB_OPT_DEVICE_ENABLED && ( (CFG_TUSB_MCU == OPT_MCU_NUC121) || (CFG_TUSB_MCU == OPT_MCU_NUC126) )
@@ -78,6 +79,7 @@ static bool active_ep0_xfer;
static struct xfer_ctl_t
{
uint8_t *data_ptr; /* data_ptr tracks where to next copy data to (for OUT) or from (for IN) */
+ // tu_fifo_t * ff; // TODO support dcd_edpt_xfer_fifo API
union {
uint16_t in_remaining_bytes; /* for IN endpoints, we track how many bytes are left to transfer */
uint16_t out_bytes_so_far; /* but for OUT endpoints, we track how many bytes we've transferred so far */
@@ -144,7 +146,17 @@ static void dcd_in_xfer(struct xfer_ctl_t *xfer, USBD_EP_T *ep)
{
uint16_t bytes_now = tu_min16(xfer->in_remaining_bytes, xfer->max_packet_size);
- memcpy((uint8_t *)(USBD_BUF_BASE + ep->BUFSEG), xfer->data_ptr, bytes_now);
+#if 0 // TODO support dcd_edpt_xfer_fifo API
+ if (xfer->ff)
+ {
+ tu_fifo_read_n(xfer->ff, (void *) (USBD_BUF_BASE + ep->BUFSEG), bytes_now);
+ }
+ else
+#endif
+ {
+ memcpy((uint8_t *)(USBD_BUF_BASE + ep->BUFSEG), xfer->data_ptr, bytes_now);
+ }
+
ep->MXPLD = bytes_now;
}
@@ -273,6 +285,7 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t to
/* store away the information we'll needing now and later */
xfer->data_ptr = buffer;
+ // xfer->ff = NULL; // TODO support dcd_edpt_xfer_fifo API
xfer->in_remaining_bytes = total_bytes;
xfer->total_bytes = total_bytes;
@@ -292,6 +305,36 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t to
return true;
}
+#if 0 // TODO support dcd_edpt_xfer_fifo API
+bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes)
+{
+ (void) rhport;
+
+ /* mine the data for the information we need */
+ tusb_dir_t dir = tu_edpt_dir(ep_addr);
+ USBD_EP_T *ep = ep_entry(ep_addr, false);
+ struct xfer_ctl_t *xfer = &xfer_table[ep - USBD->EP];
+
+ /* store away the information we'll needing now and later */
+ xfer->data_ptr = NULL; // Indicates a FIFO shall be used
+ xfer->ff = ff;
+ xfer->in_remaining_bytes = total_bytes;
+ xfer->total_bytes = total_bytes;
+
+ if (TUSB_DIR_IN == dir)
+ {
+ dcd_in_xfer(xfer, ep);
+ }
+ else
+ {
+ xfer->out_bytes_so_far = 0;
+ ep->MXPLD = xfer->max_packet_size;
+ }
+
+ return true;
+}
+#endif
+
void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
{
(void) rhport;
@@ -400,9 +443,19 @@ void dcd_int_handler(uint8_t rhport)
if (out_ep)
{
/* copy the data from the PC to the previously provided buffer */
- memcpy(xfer->data_ptr, (uint8_t *)(USBD_BUF_BASE + ep->BUFSEG), available_bytes);
+#if 0 // TODO support dcd_edpt_xfer_fifo API
+ if (xfer->ff)
+ {
+ tu_fifo_write_n(xfer->ff, (const void *) (USBD_BUF_BASE + ep->BUFSEG), available_bytes);
+ }
+ else
+#endif
+ {
+ memcpy(xfer->data_ptr, (uint8_t *)(USBD_BUF_BASE + ep->BUFSEG), available_bytes);
+ xfer->data_ptr += available_bytes;
+ }
+
xfer->out_bytes_so_far += available_bytes;
- xfer->data_ptr += available_bytes;
/* when the transfer is finished, alert TinyUSB; otherwise, accept more data */
if ( (xfer->total_bytes == xfer->out_bytes_so_far) || (available_bytes < xfer->max_packet_size) )
diff --git a/src/portable/nuvoton/nuc505/dcd_nuc505.c b/src/portable/nuvoton/nuc505/dcd_nuc505.c
index a7961a687..b7bbb020b 100644
--- a/src/portable/nuvoton/nuc505/dcd_nuc505.c
+++ b/src/portable/nuvoton/nuc505/dcd_nuc505.c
@@ -34,6 +34,7 @@
*/
#include "tusb_option.h"
+#include "common/tusb_fifo.h"
#if TUSB_OPT_DEVICE_ENABLED && (CFG_TUSB_MCU == OPT_MCU_NUC505)
@@ -94,6 +95,7 @@ static uint32_t bufseg_addr;
static struct xfer_ctl_t
{
uint8_t *data_ptr; /* data_ptr tracks where to next copy data to (for OUT) or from (for IN) */
+ // tu_fifo_t* ff; // TODO support dcd_edpt_xfer_fifo API
union {
uint16_t in_remaining_bytes; /* for IN endpoints, we track how many bytes are left to transfer */
uint16_t out_bytes_so_far; /* but for OUT endpoints, we track how many bytes we've transferred so far */
@@ -163,8 +165,7 @@ static USBD_EP_T *ep_entry(uint8_t ep_addr, bool add)
/* perform a non-control IN endpoint transfer; this is called by the ISR */
static void dcd_userEP_in_xfer(struct xfer_ctl_t *xfer, USBD_EP_T *ep)
{
- uint16_t bytes_now = tu_min16(xfer->in_remaining_bytes, xfer->max_packet_size);
- uint16_t countdown = bytes_now;
+ uint16_t const bytes_now = tu_min16(xfer->in_remaining_bytes, xfer->max_packet_size);
/* precompute what amount of data will be left */
xfer->in_remaining_bytes -= bytes_now;
@@ -180,20 +181,29 @@ static void dcd_userEP_in_xfer(struct xfer_ctl_t *xfer, USBD_EP_T *ep)
}
/* provided buffers are thankfully 32-bit aligned, allowing most data to be transfered as 32-bit */
- while (countdown > 3)
+#if 0 // TODO support dcd_edpt_xfer_fifo API
+ if (xfer->ff)
{
- uint32_t u32;
- memcpy(&u32, xfer->data_ptr, 4);
+ tu_fifo_read_n_const_addr_full_words(xfer->ff, (void *) (&ep->EPDAT_BYTE), bytes_now);
+ }
+ else
+#endif
+ {
+ uint16_t countdown = bytes_now;
+ while (countdown > 3)
+ {
+ uint32_t u32;
+ memcpy(&u32, xfer->data_ptr, 4);
+
+ ep->EPDAT = u32;
+ xfer->data_ptr += 4; countdown -= 4;
+ }
- ep->EPDAT = u32;
- xfer->data_ptr += 4; countdown -= 4;
+ while (countdown--) ep->EPDAT_BYTE = *xfer->data_ptr++;
}
- while (countdown--)
- ep->EPDAT_BYTE = *xfer->data_ptr++;
/* for short packets, we must nudge the peripheral to say 'that's all folks' */
- if (bytes_now != xfer->max_packet_size)
- ep->EPRSPCTL = USBD_EPRSPCTL_SHORTTXEN_Msk;
+ if (bytes_now != xfer->max_packet_size) ep->EPRSPCTL = USBD_EPRSPCTL_SHORTTXEN_Msk;
}
/* called by dcd_init() as well as by the ISR during a USB bus reset */
@@ -385,6 +395,7 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t to
/* store away the information we'll needing now and later */
xfer->data_ptr = buffer;
+ // xfer->ff = NULL; // TODO support dcd_edpt_xfer_fifo API
xfer->in_remaining_bytes = total_bytes;
xfer->total_bytes = total_bytes;
@@ -402,6 +413,38 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t to
return true;
}
+#if 0 // TODO support dcd_edpt_xfer_fifo API
+bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes)
+{
+ (void) rhport;
+
+ TU_ASSERT(0x80 != ep_addr && 0x00 != ep_addr); // Must not be used for control stuff
+
+ /* mine the data for the information we need */
+ tusb_dir_t dir = tu_edpt_dir(ep_addr);
+ USBD_EP_T *ep = ep_entry(ep_addr, false);
+ struct xfer_ctl_t *xfer = &xfer_table[ep - USBD->EP];
+
+ /* store away the information we'll needing now and later */
+ xfer->data_ptr = NULL; // Indicates a FIFO shall be used
+ xfer->ff = ff;
+ xfer->in_remaining_bytes = total_bytes;
+ xfer->total_bytes = total_bytes;
+
+ if (TUSB_DIR_IN == dir)
+ {
+ ep->EPINTEN = USBD_EPINTEN_BUFEMPTYIEN_Msk;
+ }
+ else
+ {
+ xfer->out_bytes_so_far = 0;
+ ep->EPINTEN = USBD_EPINTEN_RXPKIEN_Msk;
+ }
+
+ return true;
+}
+#endif
+
void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
{
(void) rhport;
@@ -615,12 +658,25 @@ void dcd_int_handler(uint8_t rhport)
#else
uint16_t const available_bytes = ep->EPDATCNT & USBD_EPDATCNT_DATCNT_Msk;
/* copy the data from the PC to the previously provided buffer */
- for (int count = 0; (count < available_bytes) && (xfer->out_bytes_so_far < xfer->total_bytes); count++, xfer->out_bytes_so_far++)
- *xfer->data_ptr++ = ep->EPDAT_BYTE;
+#if 0 // TODO support dcd_edpt_xfer_fifo API
+ if (xfer->ff)
+ {
+ tu_fifo_write_n_const_addr_full_words(xfer->ff, (const void *) &ep->EPDAT_BYTE, tu_min16(available_bytes, xfer->total_bytes - xfer->out_bytes_so_far));
+ }
+ else
+#endif
+ {
+ for (int count = 0; (count < available_bytes) && (xfer->out_bytes_so_far < xfer->total_bytes); count++, xfer->out_bytes_so_far++)
+ {
+ *xfer->data_ptr++ = ep->EPDAT_BYTE;
+ }
+ }
/* when the transfer is finished, alert TinyUSB; otherwise, continue accepting more data */
if ( (xfer->total_bytes == xfer->out_bytes_so_far) || (available_bytes < xfer->max_packet_size) )
+ {
dcd_event_xfer_complete(0, ep_addr, xfer->out_bytes_so_far, XFER_RESULT_SUCCESS, true);
+ }
#endif
}
diff --git a/src/portable/raspberrypi/rp2040/dcd_rp2040.c b/src/portable/raspberrypi/rp2040/dcd_rp2040.c
index f844a0c65..7731078b5 100644
--- a/src/portable/raspberrypi/rp2040/dcd_rp2040.c
+++ b/src/portable/raspberrypi/rp2040/dcd_rp2040.c
@@ -35,7 +35,8 @@
#include "pico/fix/rp2040_usb_device_enumeration.h"
#endif
-
+#include "osal/osal.h"
+#include "common/tusb_fifo.h"
#include "device/dcd.h"
/*------------------------------------------------------------------*/
diff --git a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c
index 18f0bc8e1..b8b0fc104 100644
--- a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c
+++ b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c
@@ -102,6 +102,7 @@
*/
#include "tusb_option.h"
+#include "common/tusb_fifo.h"
#if defined(STM32F102x6) || defined(STM32F102xB) || \
defined(STM32F103x6) || defined(STM32F103xB) || \
@@ -165,6 +166,7 @@ TU_VERIFY_STATIC(((DCD_STM32_BTABLE_BASE) % 8) == 0, "BTABLE base must be aligne
typedef struct
{
uint8_t * buffer;
+ // tu_fifo_t * ff; // TODO support dcd_edpt_xfer_fifo API
uint16_t total_len;
uint16_t queued_len;
uint16_t pma_ptr;
@@ -197,6 +199,9 @@ static void dcd_pma_free(uint8_t ep_addr);
static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, size_t wNBytes);
static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, size_t wNBytes);
+//static bool dcd_write_packet_memory_ff(tu_fifo_t * ff, uint16_t dst, uint16_t wNBytes);
+//static bool dcd_read_packet_memory_ff(tu_fifo_t * ff, uint16_t src, uint16_t wNBytes);
+
// Using a function due to better type checks
// This seems better than having to do type casts everywhere else
static inline void reg16_clear_bits(__IO uint16_t *reg, uint16_t mask) {
@@ -476,8 +481,17 @@ static void dcd_ep_ctr_rx_handler(uint32_t wIstr)
if (count != 0U)
{
- dcd_read_packet_memory(&(xfer->buffer[xfer->queued_len]),
- *pcd_ep_rx_address_ptr(USB,EPindex), count);
+#if 0 // TODO support dcd_edpt_xfer_fifo API
+ if (xfer->ff)
+ {
+ dcd_read_packet_memory_ff(xfer->ff, *pcd_ep_rx_address_ptr(USB,EPindex), count);
+ }
+ else
+#endif
+ {
+ dcd_read_packet_memory(&(xfer->buffer[xfer->queued_len]), *pcd_ep_rx_address_ptr(USB,EPindex), count);
+ }
+
xfer->queued_len = (uint16_t)(xfer->queued_len + count);
}
@@ -804,7 +818,17 @@ static void dcd_transmit_packet(xfer_ctl_t * xfer, uint16_t ep_ix)
len = xfer->max_packet_size;
}
uint16_t oldAddr = *pcd_ep_tx_address_ptr(USB,ep_ix);
- dcd_write_packet_memory(oldAddr, &(xfer->buffer[xfer->queued_len]), len);
+
+#if 0 // TODO support dcd_edpt_xfer_fifo API
+ if (xfer->ff)
+ {
+ dcd_write_packet_memory_ff(xfer->ff, oldAddr, len);
+ }
+ else
+#endif
+ {
+ dcd_write_packet_memory(oldAddr, &(xfer->buffer[xfer->queued_len]), len);
+ }
xfer->queued_len = (uint16_t)(xfer->queued_len + len);
pcd_set_ep_tx_cnt(USB,ep_ix,len);
@@ -821,6 +845,7 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
xfer_ctl_t * xfer = xfer_ctl_ptr(epnum,dir);
xfer->buffer = buffer;
+ // xfer->ff = NULL; // TODO support dcd_edpt_xfer_fifo API
xfer->total_len = total_bytes;
xfer->queued_len = 0;
@@ -847,6 +872,39 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
return true;
}
+#if 0 // TODO support dcd_edpt_xfer_fifo API
+bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes)
+{
+ (void) rhport;
+
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+
+ xfer_ctl_t * xfer = xfer_ctl_ptr(epnum,dir);
+
+ xfer->buffer = NULL;
+ // xfer->ff = ff; // TODO support dcd_edpt_xfer_fifo API
+ xfer->total_len = total_bytes;
+ xfer->queued_len = 0;
+
+ if ( dir == TUSB_DIR_OUT )
+ {
+ if(total_bytes > xfer->max_packet_size)
+ {
+ pcd_set_ep_rx_cnt(USB,epnum,xfer->max_packet_size);
+ } else {
+ pcd_set_ep_rx_cnt(USB,epnum,total_bytes);
+ }
+ pcd_set_ep_rx_status(USB, epnum, USB_EP_RX_VALID);
+ }
+ else // IN
+ {
+ dcd_transmit_packet(xfer,epnum);
+ }
+ return true;
+}
+#endif
+
void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr)
{
(void)rhport;
@@ -920,8 +978,55 @@ static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, si
return true;
}
+#if 0 // TODO support dcd_edpt_xfer_fifo API
/**
- * @brief Copy a buffer from user memory area to packet memory area (PMA).
+ * @brief Copy from FIFO to packet memory area (PMA).
+ * Uses byte-access of system memory and 16-bit access of packet memory
+ * @param wNBytes no. of bytes to be copied.
+ * @retval None
+ */
+
+// THIS FUNCTION IS UNTESTED
+
+static bool dcd_write_packet_memory_ff(tu_fifo_t * ff, uint16_t dst, uint16_t wNBytes)
+{
+ // Since we copy from a ring buffer FIFO, a wrap might occur making it necessary to conduct two copies
+ // Check for first linear part
+ void * src;
+ uint16_t len = tu_fifo_get_linear_read_info(ff, 0, &src, wNBytes); // We want to read from the FIFO
+ TU_VERIFY(len && dcd_write_packet_memory(dst, src, len)); // and write it into the PMA
+ tu_fifo_advance_read_pointer(ff, len);
+
+ // Check for wrapped part
+ if (len < wNBytes)
+ {
+ // Get remaining wrapped length
+ uint16_t len2 = tu_fifo_get_linear_read_info(ff, 0, &src, wNBytes - len);
+ TU_VERIFY(len2);
+
+ // Update destination pointer
+ dst += len;
+
+ // Since PMA is accessed 16-bit wise we need to handle the case when a 16 bit value was split
+ if (len % 2) // If len is uneven there is a byte left to copy
+ {
+ // Since PMA can accessed only 16 bit-wise we copy the last byte again
+ tu_fifo_backward_read_pointer(ff, 1); // Move one byte back and copy two bytes for the PMA
+ tu_fifo_read_n(ff, (void *) &pma[PMA_STRIDE*(dst>>1)], 2); // Since EP FIFOs must be of item size 1 this is safe to do
+ dst++;
+ len2--;
+ }
+
+ TU_VERIFY(dcd_write_packet_memory(dst, src, len2));
+ tu_fifo_advance_write_pointer(ff, len2);
+ }
+
+ return true;
+}
+#endif
+
+/**
+ * @brief Copy a buffer from packet memory area (PMA) to user memory area.
* Uses byte-access of system memory and 16-bit access of packet memory
* @param wNBytes no. of bytes to be copied.
* @retval None
@@ -955,5 +1060,52 @@ static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, size_t wN
return true;
}
+#if 0 // TODO support dcd_edpt_xfer_fifo API
+/**
+ * @brief Copy a buffer from user packet memory area (PMA) to FIFO.
+ * Uses byte-access of system memory and 16-bit access of packet memory
+ * @param wNBytes no. of bytes to be copied.
+ * @retval None
+ */
+
+// THIS FUNCTION IS UNTESTED
+
+static bool dcd_read_packet_memory_ff(tu_fifo_t * ff, uint16_t src, uint16_t wNBytes)
+{
+ // Since we copy into a ring buffer FIFO, a wrap might occur making it necessary to conduct two copies
+ // Check for first linear part
+ void * dst;
+ uint16_t len = tu_fifo_get_linear_write_info(ff, 0, &dst, wNBytes);
+ TU_VERIFY(len && dcd_read_packet_memory(dst, src, len));
+ tu_fifo_advance_write_pointer(ff, len);
+
+ // Check for wrapped part
+ if (len < wNBytes)
+ {
+ // Get remaining wrapped length
+ uint16_t len2 = tu_fifo_get_linear_write_info(ff, 0, &dst, wNBytes - len);
+ TU_VERIFY(len2);
+
+ // Update source pointer
+ src += len;
+
+ // Since PMA is accessed 16-bit wise we need to handle the case when a 16 bit value was split
+ if (len % 2) // If len is uneven there is a byte left to copy
+ {
+ uint32_t temp = pma[PMA_STRIDE*(src>>1)];
+ *((uint8_t *)dst++) = ((temp >> 8) & 0xFF);
+ src++;
+ len2--;
+ }
+
+ TU_VERIFY(dcd_read_packet_memory(dst, src, len2));
+ tu_fifo_advance_write_pointer(ff, len2);
+ }
+
+ return true;
+}
+
+#endif
+
#endif
diff --git a/src/portable/st/synopsys/dcd_synopsys.c b/src/portable/st/synopsys/dcd_synopsys.c
index 548438b68..0fe7c2a3c 100644
--- a/src/portable/st/synopsys/dcd_synopsys.c
+++ b/src/portable/st/synopsys/dcd_synopsys.c
@@ -28,6 +28,7 @@
*/
#include "tusb_option.h"
+#include "common/tusb_fifo.h"
// Since TinyUSB doesn't use SOF for now, and this interrupt too often (1ms interval)
// We disable SOF for now until needed later on
@@ -135,6 +136,7 @@ static TU_ATTR_ALIGNED(4) uint32_t _setup_packet[2];
typedef struct {
uint8_t * buffer;
+ tu_fifo_t * ff;
uint16_t total_len;
uint16_t max_size;
uint8_t interval;
@@ -644,6 +646,7 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, dir);
xfer->buffer = buffer;
+ xfer->ff = NULL;
xfer->total_len = total_bytes;
// EP0 can only handle one packet
@@ -668,6 +671,35 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
return true;
}
+// The number of bytes has to be given explicitly to allow more flexible control of how many
+// bytes should be written and second to keep the return value free to give back a boolean
+// success message. If total_bytes is too big, the FIFO will copy only what is available
+// into the USB buffer!
+bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes)
+{
+ // USB buffers always work in bytes so to avoid unnecessary divisions we demand item_size = 1
+ TU_ASSERT(ff->item_size == 1);
+
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+
+ xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, dir);
+ xfer->buffer = NULL;
+ xfer->ff = ff;
+ xfer->total_len = total_bytes;
+
+ uint16_t num_packets = (total_bytes / xfer->max_size);
+ uint8_t const short_packet_size = total_bytes % xfer->max_size;
+
+ // Zero-size packet is special case.
+ if(short_packet_size > 0 || (total_bytes == 0)) num_packets++;
+
+ // Schedule packets to be sent within interrupt
+ edpt_schedule_packets(rhport, epnum, dir, num_packets, total_bytes);
+
+ return true;
+}
+
static void dcd_edpt_disable (uint8_t rhport, uint8_t ep_addr, bool stall)
{
(void) rhport;
@@ -867,10 +899,19 @@ static void handle_rxflvl_ints(uint8_t rhport, USB_OTG_OUTEndpointTypeDef * out_
xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, TUSB_DIR_OUT);
// Read packet off RxFIFO
- read_fifo_packet(rhport, xfer->buffer, bcnt);
+ if (xfer->ff)
+ {
+ // Ring buffer
+ tu_fifo_write_n_const_addr_full_words(xfer->ff, (const void *) rx_fifo, bcnt);
+ }
+ else
+ {
+ // Linear buffer
+ read_fifo_packet(rhport, xfer->buffer, bcnt);
- // Increment pointer to xfer data
- xfer->buffer += bcnt;
+ // Increment pointer to xfer data
+ xfer->buffer += bcnt;
+ }
// Truncate transfer length in case of short packet
if(bcnt < xfer->max_size) {
@@ -966,22 +1007,30 @@ static void handle_epin_ints(uint8_t rhport, USB_OTG_DeviceTypeDef * dev, USB_OT
uint16_t remaining_packets = (in_ep[n].DIEPTSIZ & USB_OTG_DIEPTSIZ_PKTCNT_Msk) >> USB_OTG_DIEPTSIZ_PKTCNT_Pos;
// Process every single packet (only whole packets can be written to fifo)
- for(uint16_t i = 0; i < remaining_packets; i++){
- uint16_t remaining_bytes = (in_ep[n].DIEPTSIZ & USB_OTG_DIEPTSIZ_XFRSIZ_Msk) >> USB_OTG_DIEPTSIZ_XFRSIZ_Pos;
+ for(uint16_t i = 0; i < remaining_packets; i++)
+ {
+ uint16_t const remaining_bytes = (in_ep[n].DIEPTSIZ & USB_OTG_DIEPTSIZ_XFRSIZ_Msk) >> USB_OTG_DIEPTSIZ_XFRSIZ_Pos;
+
// Packet can not be larger than ep max size
- uint16_t packet_size = tu_min16(remaining_bytes, xfer->max_size);
+ uint16_t const packet_size = tu_min16(remaining_bytes, xfer->max_size);
// It's only possible to write full packets into FIFO. Therefore DTXFSTS register of current
// EP has to be checked if the buffer can take another WHOLE packet
- if(packet_size > ((in_ep[n].DTXFSTS & USB_OTG_DTXFSTS_INEPTFSAV_Msk) << 2)){
- break;
- }
+ if(packet_size > ((in_ep[n].DTXFSTS & USB_OTG_DTXFSTS_INEPTFSAV_Msk) << 2)) break;
// Push packet to Tx-FIFO
- write_fifo_packet(rhport, n, xfer->buffer, packet_size);
+ if (xfer->ff)
+ {
+ usb_fifo_t tx_fifo = FIFO_BASE(rhport, n);
+ tu_fifo_read_n_const_addr_full_words(xfer->ff, (void *) tx_fifo, packet_size);
+ }
+ else
+ {
+ write_fifo_packet(rhport, n, xfer->buffer, packet_size);
- // Increment pointer to xfer data
- xfer->buffer += packet_size;
+ // Increment pointer to xfer data
+ xfer->buffer += packet_size;
+ }
}
// Turn off TXFE if all bytes are written.
diff --git a/src/portable/template/dcd_template.c b/src/portable/template/dcd_template.c
index 618812416..d8c8753d3 100644
--- a/src/portable/template/dcd_template.c
+++ b/src/portable/template/dcd_template.c
@@ -25,6 +25,7 @@
*/
#include "tusb_option.h"
+#include "common/tusb_fifo.h"
#if CFG_TUSB_MCU == OPT_MCU_NONE
@@ -104,6 +105,16 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
return false;
}
+// Submit a transfer where is managed by FIFO, When complete dcd_event_xfer_complete() is invoked to notify the stack - optional, however, must be listed in usbd.c
+bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes)
+{
+ (void) rhport;
+ (void) ep_addr;
+ (void) ff;
+ (void) total_bytes;
+ return false;
+}
+
// Stall endpoint
void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr)
{
diff --git a/src/portable/ti/msp430x5xx/dcd_msp430x5xx.c b/src/portable/ti/msp430x5xx/dcd_msp430x5xx.c
index e08c3536a..48e9dd592 100644
--- a/src/portable/ti/msp430x5xx/dcd_msp430x5xx.c
+++ b/src/portable/ti/msp430x5xx/dcd_msp430x5xx.c
@@ -26,6 +26,7 @@
*/
#include "tusb_option.h"
+#include "common/tusb_fifo.h"
#if TUSB_OPT_DEVICE_ENABLED && ( CFG_TUSB_MCU == OPT_MCU_MSP430x5xx )
@@ -48,6 +49,7 @@ uint8_t _setup_packet[8];
typedef struct
{
uint8_t * buffer;
+ // tu_fifo_t * ff; // TODO support dcd_edpt_xfer_fifo API
uint16_t total_len;
uint16_t queued_len;
uint16_t max_size;
@@ -306,6 +308,7 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, dir);
xfer->buffer = buffer;
+ // xfer->ff = NULL; // TODO support dcd_edpt_xfer_fifo API
xfer->total_len = total_bytes;
xfer->queued_len = 0;
xfer->short_packet = false;
@@ -344,6 +347,36 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
return true;
}
+#if 0 // TODO support dcd_edpt_xfer_fifo API
+bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes)
+{
+ (void) rhport;
+
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+
+ xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, dir);
+ xfer->buffer = NULL;
+ xfer->ff = ff;
+ xfer->total_len = total_bytes;
+ xfer->queued_len = 0;
+ xfer->short_packet = false;
+
+ ep_regs_t ep_regs = EP_REGS(epnum, dir);
+
+ if(dir == TUSB_DIR_OUT)
+ {
+ ep_regs[BCTX] &= ~NAK;
+ }
+ else
+ {
+ USBIEPIFG |= (1 << epnum);
+ }
+
+ return true;
+}
+#endif
+
void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr)
{
(void) rhport;
@@ -443,22 +476,32 @@ static void receive_packet(uint8_t ep_num)
to_recv_size = (xfer_size > xfer->max_size) ? xfer->max_size : xfer_size;
}
- uint8_t * base = (xfer->buffer + xfer->queued_len);
-
- if(ep_num == 0)
+#if 0 // TODO support dcd_edpt_xfer_fifo API
+ if (xfer->ff)
{
- volatile uint8_t * ep0out_buf = &USBOEP0BUF;
- for(uint16_t i = 0; i < to_recv_size; i++)
- {
- base[i] = ep0out_buf[i];
- }
+ volatile uint8_t * ep_buf = (ep_num == 0) ? &USBOEP0BUF : (&USBSTABUFF + (ep_regs[BBAX] << 3));
+ tu_fifo_write_n(xfer->ff, (const void *) ep_buf, to_recv_size);
}
else
+#endif
{
- volatile uint8_t * ep_buf = &USBSTABUFF + (ep_regs[BBAX] << 3);
- for(uint16_t i = 0; i < to_recv_size ; i++)
+ uint8_t * base = (xfer->buffer + xfer->queued_len);
+
+ if(ep_num == 0)
+ {
+ volatile uint8_t * ep0out_buf = &USBOEP0BUF;
+ for(uint16_t i = 0; i < to_recv_size; i++)
+ {
+ base[i] = ep0out_buf[i];
+ }
+ }
+ else
{
- base[i] = ep_buf[i];
+ volatile uint8_t * ep_buf = &USBSTABUFF + (ep_regs[BBAX] << 3);
+ for(uint16_t i = 0; i < to_recv_size ; i++)
+ {
+ base[i] = ep_buf[i];
+ }
}
}
@@ -499,7 +542,6 @@ static void transmit_packet(uint8_t ep_num)
}
// Then actually commit to transmit a packet.
- uint8_t * base = (xfer->buffer + xfer->queued_len);
uint16_t remaining = xfer->total_len - xfer->queued_len;
uint8_t xfer_size = (xfer->max_size < xfer->total_len) ? xfer->max_size : remaining;
@@ -513,6 +555,7 @@ static void transmit_packet(uint8_t ep_num)
if(ep_num == 0)
{
volatile uint8_t * ep0in_buf = &USBIEP0BUF;
+ uint8_t * base = (xfer->buffer + xfer->queued_len);
for(uint16_t i = 0; i < xfer_size; i++)
{
ep0in_buf[i] = base[i];
@@ -526,9 +569,19 @@ static void transmit_packet(uint8_t ep_num)
ep_regs_t ep_regs = EP_REGS(ep_num, TUSB_DIR_IN);
volatile uint8_t * ep_buf = &USBSTABUFF + (ep_regs[BBAX] << 3);
- for(int i = 0; i < xfer_size; i++)
+#if 0 // TODO support dcd_edpt_xfer_fifo API
+ if (xfer->ff)
+ {
+ tu_fifo_read_n(xfer->ff, (void *) ep_buf, xfer_size);
+ }
+ else
+#endif
{
- ep_buf[i] = base[i];
+ uint8_t * base = (xfer->buffer + xfer->queued_len);
+ for(int i = 0; i < xfer_size; i++)
+ {
+ ep_buf[i] = base[i];
+ }
}
ep_regs[BCTX] = (ep_regs[BCTX] & 0x80) + (xfer_size & 0x7F);