summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorhathach <[email protected]>2020-10-11 16:22:12 +0700
committerhathach <[email protected]>2020-10-11 16:22:12 +0700
commitb5d4c8185d81b8d7f59625ad19e43f80fe309e29 (patch)
tree543cc4d8f537d0da1abcbfd314a040d6c82c668d /src
parent2b54dcb9f621e52c5fd15a485a6ee423e208b186 (diff)
parent6ce539594739e9c29239b965f8e2ba2f93b78ec7 (diff)
Merge branch 'master' into host-async-control
Diffstat (limited to 'src')
-rw-r--r--src/class/audio/audio.h892
-rw-r--r--src/class/audio/audio_device.c1448
-rw-r--r--src/class/audio/audio_device.h401
-rw-r--r--src/class/cdc/cdc_device.c87
-rw-r--r--src/class/cdc/cdc_device.h2
-rw-r--r--src/class/hid/hid_device.c99
-rw-r--r--src/class/hid/hid_device.h78
-rw-r--r--src/class/midi/midi_device.c41
-rw-r--r--src/common/tusb_common.h8
-rw-r--r--src/common/tusb_compiler.h4
-rw-r--r--src/common/tusb_fifo.c521
-rw-r--r--src/common/tusb_fifo.h90
-rw-r--r--src/common/tusb_verify.h14
-rw-r--r--src/device/usbd.c115
-rw-r--r--src/device/usbd.h208
-rw-r--r--src/device/usbd_pvt.h7
-rw-r--r--src/portable/dialog/da146xx/dcd_da146xx.c408
-rw-r--r--src/portable/nordic/nrf5x/dcd_nrf5x.c167
-rw-r--r--src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c58
-rw-r--r--src/portable/st/stm32_fsdev/dcd_stm32_fsdev_pvt_st.h8
-rw-r--r--src/portable/st/synopsys/dcd_synopsys.c129
-rw-r--r--src/tusb.h4
-rw-r--r--src/tusb_option.h12
23 files changed, 4326 insertions, 475 deletions
diff --git a/src/class/audio/audio.h b/src/class/audio/audio.h
index 5bec14d88..05e61f8df 100644
--- a/src/class/audio/audio.h
+++ b/src/class/audio/audio.h
@@ -2,6 +2,7 @@
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
+ * Copyright (c) 2020 Reinhard Panhuber
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -35,28 +36,44 @@
#include "common/tusb_common.h"
#ifdef __cplusplus
- extern "C" {
+extern "C" {
#endif
-/// Audio Interface Subclass Codes
+/// Audio Device Class Codes
+
+/// A.2 - Audio Function Subclass Codes
+typedef enum
+{
+ AUDIO_FUNCTION_SUBCLASS_UNDEFINED = 0x00,
+} audio_function_subclass_type_t;
+
+/// A.3 - Audio Function Protocol Codes
+typedef enum
+{
+ AUDIO_FUNC_PROTOCOL_CODE_UNDEF = 0x00,
+ AUDIO_FUNC_PROTOCOL_CODE_V2 = 0x20, ///< Version 2.0
+} audio_function_protocol_code_t;
+
+/// A.5 - Audio Interface Subclass Codes
typedef enum
{
- AUDIO_SUBCLASS_CONTROL = 0x01 , ///< Audio Control
+ AUDIO_SUBCLASS_UNDEFINED = 0x00,
+ AUDIO_SUBCLASS_CONTROL , ///< Audio Control
AUDIO_SUBCLASS_STREAMING , ///< Audio Streaming
AUDIO_SUBCLASS_MIDI_STREAMING , ///< MIDI Streaming
} audio_subclass_type_t;
-/// Audio Protocol Codes
+/// A.6 - Audio Interface Protocol Codes
typedef enum
{
- AUDIO_PROTOCOL_V1 = 0x00, ///< Version 1.0
- AUDIO_PROTOCOL_V2 = 0x20, ///< Version 2.0
- AUDIO_PROTOCOL_V3 = 0x30, ///< Version 3.0
-} audio_protocol_type_t;
+ AUDIO_INT_PROTOCOL_CODE_UNDEF = 0x00,
+ AUDIO_INT_PROTOCOL_CODE_V2 = 0x20, ///< Version 2.0
+} audio_interface_protocol_code_t;
-/// Audio Function Category Codes
+/// A.7 - Audio Function Category Codes
typedef enum
{
+ AUDIO_FUNC_UNDEF = 0x00,
AUDIO_FUNC_DESKTOP_SPEAKER = 0x01,
AUDIO_FUNC_HOME_THEATER = 0x02,
AUDIO_FUNC_MICROPHONE = 0x03,
@@ -68,33 +85,850 @@ typedef enum
AUDIO_FUNC_MUSICAL_INSTRUMENT = 0x09,
AUDIO_FUNC_PRO_AUDIO = 0x0A,
AUDIO_FUNC_AUDIO_VIDEO = 0x0B,
- AUDIO_FUNC_CONTROL_PANEL = 0x0C
-} audio_function_t;
+ AUDIO_FUNC_CONTROL_PANEL = 0x0C,
+ AUDIO_FUNC_OTHER = 0xFF,
+} audio_function_code_t;
+
+/// A.9 - Audio Class-Specific AC Interface Descriptor Subtypes UAC2
+typedef enum
+{
+ AUDIO_CS_AC_INTERFACE_AC_DESCRIPTOR_UNDEF = 0x00,
+ AUDIO_CS_AC_INTERFACE_HEADER = 0x01,
+ AUDIO_CS_AC_INTERFACE_INPUT_TERMINAL = 0x02,
+ AUDIO_CS_AC_INTERFACE_OUTPUT_TERMINAL = 0x03,
+ AUDIO_CS_AC_INTERFACE_MIXER_UNIT = 0x04,
+ AUDIO_CS_AC_INTERFACE_SELECTOR_UNIT = 0x05,
+ AUDIO_CS_AC_INTERFACE_FEATURE_UNIT = 0x06,
+ AUDIO_CS_AC_INTERFACE_EFFECT_UNIT = 0x07,
+ AUDIO_CS_AC_INTERFACE_PROCESSING_UNIT = 0x08,
+ AUDIO_CS_AC_INTERFACE_EXTENSION_UNIT = 0x09,
+ AUDIO_CS_AC_INTERFACE_CLOCK_SOURCE = 0x0A,
+ AUDIO_CS_AC_INTERFACE_CLOCK_SELECTOR = 0x0B,
+ AUDIO_CS_AC_INTERFACE_CLOCK_MULTIPLIER = 0x0C,
+ AUDIO_CS_AC_INTERFACE_SAMPLE_RATE_CONVERTER = 0x0D,
+} audio_cs_ac_interface_subtype_t;
+
+/// A.10 - Audio Class-Specific AS Interface Descriptor Subtypes UAC2
+typedef enum
+{
+ AUDIO_CS_AS_INTERFACE_AS_DESCRIPTOR_UNDEF = 0x00,
+ AUDIO_CS_AS_INTERFACE_AS_GENERAL = 0x01,
+ AUDIO_CS_AS_INTERFACE_FORMAT_TYPE = 0x02,
+ AUDIO_CS_AS_INTERFACE_ENCODER = 0x03,
+ AUDIO_CS_AS_INTERFACE_DECODER = 0x04,
+} audio_cs_as_interface_subtype_t;
+
+/// A.11 - Effect Unit Effect Types
+typedef enum
+{
+ AUDIO_EFFECT_TYPE_UNDEF = 0x00,
+ AUDIO_EFFECT_TYPE_PARAM_EQ_SECTION = 0x01,
+ AUDIO_EFFECT_TYPE_REVERBERATION = 0x02,
+ AUDIO_EFFECT_TYPE_MOD_DELAY = 0x03,
+ AUDIO_EFFECT_TYPE_DYN_RANGE_COMP = 0x04,
+} audio_effect_unit_effect_type_t;
+
+/// A.12 - Processing Unit Process Types
+typedef enum
+{
+ AUDIO_PROCESS_TYPE_UNDEF = 0x00,
+ AUDIO_PROCESS_TYPE_UP_DOWN_MIX = 0x01,
+ AUDIO_PROCESS_TYPE_DOLBY_PROLOGIC = 0x02,
+ AUDIO_PROCESS_TYPE_STEREO_EXTENDER = 0x03,
+} audio_processing_unit_process_type_t;
+
+/// A.13 - Audio Class-Specific EP Descriptor Subtypes UAC2
+typedef enum
+{
+ AUDIO_CS_EP_SUBTYPE_UNDEF = 0x00,
+ AUDIO_CS_EP_SUBTYPE_GENERAL = 0x01,
+} audio_cs_ep_subtype_t;
+
+/// A.14 - Audio Class-Specific Request Codes
+typedef enum
+{
+ AUDIO_CS_REQ_UNDEF = 0x00,
+ AUDIO_CS_REQ_CUR = 0x01,
+ AUDIO_CS_REQ_RANGE = 0x02,
+ AUDIO_CS_REQ_MEM = 0x03,
+} audio_cs_req_t;
+
+/// A.17 - Control Selector Codes
+
+/// A.17.1 - Clock Source Control Selectors
+typedef enum
+{
+ AUDIO_CS_CTRL_UNDEF = 0x00,
+ AUDIO_CS_CTRL_SAM_FREQ = 0x01,
+ AUDIO_CS_CTRL_CLK_VALID = 0x02,
+} audio_clock_src_control_selector_t;
+
+/// A.17.2 - Clock Selector Control Selectors
+typedef enum
+{
+ AUDIO_CX_CTRL_UNDEF = 0x00,
+ AUDIO_CX_CTRL_CONTROL = 0x01,
+} audio_clock_sel_control_selector_t;
+
+/// A.17.3 - Clock Multiplier Control Selectors
+typedef enum
+{
+ AUDIO_CM_CTRL_UNDEF = 0x00,
+ AUDIO_CM_CTRL_NUMERATOR_CONTROL = 0x01,
+ AUDIO_CM_CTRL_DENOMINATOR_CONTROL = 0x02,
+} audio_clock_mul_control_selector_t;
+
+/// A.17.4 - Terminal Control Selectors
+typedef enum
+{
+ AUDIO_TE_CTRL_UNDEF = 0x00,
+ AUDIO_TE_CTRL_COPY_PROTECT = 0x01,
+ AUDIO_TE_CTRL_CONNECTOR = 0x02,
+ AUDIO_TE_CTRL_OVERLOAD = 0x03,
+ AUDIO_TE_CTRL_CLUSTER = 0x04,
+ AUDIO_TE_CTRL_UNDERFLOW = 0x05,
+ AUDIO_TE_CTRL_OVERFLOW = 0x06,
+ AUDIO_TE_CTRL_LATENCY = 0x07,
+} audio_terminal_control_selector_t;
+
+/// A.17.5 - Mixer Control Selectors
+typedef enum
+{
+ AUDIO_MU_CTRL_UNDEF = 0x00,
+ AUDIO_MU_CTRL_MIXER = 0x01,
+ AUDIO_MU_CTRL_CLUSTER = 0x02,
+ AUDIO_MU_CTRL_UNDERFLOW = 0x03,
+ AUDIO_MU_CTRL_OVERFLOW = 0x04,
+ AUDIO_MU_CTRL_LATENCY = 0x05,
+} audio_mixer_control_selector_t;
+
+/// A.17.6 - Selector Control Selectors
+typedef enum
+{
+ AUDIO_SU_CTRL_UNDEF = 0x00,
+ AUDIO_SU_CTRL_SELECTOR = 0x01,
+ AUDIO_SU_CTRL_LATENCY = 0x02,
+} audio_sel_control_selector_t;
+
+/// A.17.7 - Feature Unit Control Selectors
+typedef enum
+{
+ AUDIO_FU_CTRL_UNDEF = 0x00,
+ AUDIO_FU_CTRL_MUTE = 0x01,
+ AUDIO_FU_CTRL_VOLUME = 0x02,
+ AUDIO_FU_CTRL_BASS = 0x03,
+ AUDIO_FU_CTRL_MID = 0x04,
+ AUDIO_FU_CTRL_TREBLE = 0x05,
+ AUDIO_FU_CTRL_GRAPHIC_EQUALIZER = 0x06,
+ AUDIO_FU_CTRL_AGC = 0x07,
+ AUDIO_FU_CTRL_DELAY = 0x08,
+ AUDIO_FU_CTRL_BASS_BOOST = 0x09,
+ AUDIO_FU_CTRL_LOUDNESS = 0x0A,
+ AUDIO_FU_CTRL_INPUT_GAIN = 0x0B,
+ AUDIO_FU_CTRL_GAIN_PAD = 0x0C,
+ AUDIO_FU_CTRL_INVERTER = 0x0D,
+ AUDIO_FU_CTRL_UNDERFLOW = 0x0E,
+ AUDIO_FU_CTRL_OVERVLOW = 0x0F,
+ AUDIO_FU_CTRL_LATENCY = 0x10,
+} audio_feature_unit_control_selector_t;
+
+/// A.17.8 Effect Unit Control Selectors
+
+/// A.17.8.1 Parametric Equalizer Section Effect Unit Control Selectors
+typedef enum
+{
+ AUDIO_PE_CTRL_UNDEF = 0x00,
+ AUDIO_PE_CTRL_ENABLE = 0x01,
+ AUDIO_PE_CTRL_CENTERFREQ = 0x02,
+ AUDIO_PE_CTRL_QFACTOR = 0x03,
+ AUDIO_PE_CTRL_GAIN = 0x04,
+ AUDIO_PE_CTRL_UNDERFLOW = 0x05,
+ AUDIO_PE_CTRL_OVERFLOW = 0x06,
+ AUDIO_PE_CTRL_LATENCY = 0x07,
+} audio_parametric_equalizer_control_selector_t;
+
+/// A.17.8.2 Reverberation Effect Unit Control Selectors
+typedef enum
+{
+ AUDIO_RV_CTRL_UNDEF = 0x00,
+ AUDIO_RV_CTRL_ENABLE = 0x01,
+ AUDIO_RV_CTRL_TYPE = 0x02,
+ AUDIO_RV_CTRL_LEVEL = 0x03,
+ AUDIO_RV_CTRL_TIME = 0x04,
+ AUDIO_RV_CTRL_FEEDBACK = 0x05,
+ AUDIO_RV_CTRL_PREDELAY = 0x06,
+ AUDIO_RV_CTRL_DENSITY = 0x07,
+ AUDIO_RV_CTRL_HIFREQ_ROLLOFF = 0x08,
+ AUDIO_RV_CTRL_UNDERFLOW = 0x09,
+ AUDIO_RV_CTRL_OVERFLOW = 0x0A,
+ AUDIO_RV_CTRL_LATENCY = 0x0B,
+} audio_reverberation_effect_control_selector_t;
+
+/// A.17.8.3 Modulation Delay Effect Unit Control Selectors
+typedef enum
+{
+ AUDIO_MD_CTRL_UNDEF = 0x00,
+ AUDIO_MD_CTRL_ENABLE = 0x01,
+ AUDIO_MD_CTRL_BALANCE = 0x02,
+ AUDIO_MD_CTRL_RATE = 0x03,
+ AUDIO_MD_CTRL_DEPTH = 0x04,
+ AUDIO_MD_CTRL_TIME = 0x05,
+ AUDIO_MD_CTRL_FEEDBACK = 0x06,
+ AUDIO_MD_CTRL_UNDERFLOW = 0x07,
+ AUDIO_MD_CTRL_OVERFLOW = 0x08,
+ AUDIO_MD_CTRL_LATENCY = 0x09,
+} audio_modulation_delay_control_selector_t;
+
+/// A.17.8.4 Dynamic Range Compressor Effect Unit Control Selectors
+typedef enum
+{
+ AUDIO_DR_CTRL_UNDEF = 0x00,
+ AUDIO_DR_CTRL_ENABLE = 0x01,
+ AUDIO_DR_CTRL_COMPRESSION_RATE = 0x02,
+ AUDIO_DR_CTRL_MAXAMPL = 0x03,
+ AUDIO_DR_CTRL_THRESHOLD = 0x04,
+ AUDIO_DR_CTRL_ATTACK_TIME = 0x05,
+ AUDIO_DR_CTRL_RELEASE_TIME = 0x06,
+ AUDIO_DR_CTRL_UNDERFLOW = 0x07,
+ AUDIO_DR_CTRL_OVERFLOW = 0x08,
+ AUDIO_DR_CTRL_LATENCY = 0x09,
+} audio_dynamic_range_compression_control_selector_t;
+
+/// A.17.9 Processing Unit Control Selectors
+
+/// A.17.9.1 Up/Down-mix Processing Unit Control Selectors
+typedef enum
+{
+ AUDIO_UD_CTRL_UNDEF = 0x00,
+ AUDIO_UD_CTRL_ENABLE = 0x01,
+ AUDIO_UD_CTRL_MODE_SELECT = 0x02,
+ AUDIO_UD_CTRL_CLUSTER = 0x03,
+ AUDIO_UD_CTRL_UNDERFLOW = 0x04,
+ AUDIO_UD_CTRL_OVERFLOW = 0x05,
+ AUDIO_UD_CTRL_LATENCY = 0x06,
+} audio_up_down_mix_control_selector_t;
+
+/// A.17.9.2 Dolby Prologic ™ Processing Unit Control Selectors
+typedef enum
+{
+ AUDIO_DP_CTRL_UNDEF = 0x00,
+ AUDIO_DP_CTRL_ENABLE = 0x01,
+ AUDIO_DP_CTRL_MODE_SELECT = 0x02,
+ AUDIO_DP_CTRL_CLUSTER = 0x03,
+ AUDIO_DP_CTRL_UNDERFLOW = 0x04,
+ AUDIO_DP_CTRL_OVERFLOW = 0x05,
+ AUDIO_DP_CTRL_LATENCY = 0x06,
+} audio_dolby_prologic_control_selector_t;
+
+/// A.17.9.3 Stereo Extender Processing Unit Control Selectors
+typedef enum
+{
+ AUDIO_ST_EXT_CTRL_UNDEF = 0x00,
+ AUDIO_ST_EXT_CTRL_ENABLE = 0x01,
+ AUDIO_ST_EXT_CTRL_WIDTH = 0x02,
+ AUDIO_ST_EXT_CTRL_UNDERFLOW = 0x03,
+ AUDIO_ST_EXT_CTRL_OVERFLOW = 0x04,
+ AUDIO_ST_EXT_CTRL_LATENCY = 0x05,
+} audio_stereo_extender_control_selector_t;
+
+/// A.17.10 Extension Unit Control Selectors
+typedef enum
+{
+ AUDIO_XU_CTRL_UNDEF = 0x00,
+ AUDIO_XU_CTRL_ENABLE = 0x01,
+ AUDIO_XU_CTRL_CLUSTER = 0x02,
+ AUDIO_XU_CTRL_UNDERFLOW = 0x03,
+ AUDIO_XU_CTRL_OVERFLOW = 0x04,
+ AUDIO_XU_CTRL_LATENCY = 0x05,
+} audio_extension_unit_control_selector_t;
+
+/// A.17.11 AudioStreaming Interface Control Selectors
+typedef enum
+{
+ AUDIO_AS_CTRL_UNDEF = 0x00,
+ AUDIO_AS_CTRL_ACT_ALT_SETTING = 0x01,
+ AUDIO_AS_CTRL_VAL_ALT_SETTINGS = 0x02,
+ AUDIO_AS_CTRL_AUDIO_DATA_FORMAT = 0x03,
+} audio_audiostreaming_interface_control_selector_t;
+
+/// A.17.12 Encoder Control Selectors
+typedef enum
+{
+ AUDIO_EN_CTRL_UNDEF = 0x00,
+ AUDIO_EN_CTRL_BIT_RATE = 0x01,
+ AUDIO_EN_CTRL_QUALITY = 0x02,
+ AUDIO_EN_CTRL_VBR = 0x03,
+ AUDIO_EN_CTRL_TYPE = 0x04,
+ AUDIO_EN_CTRL_UNDERFLOW = 0x05,
+ AUDIO_EN_CTRL_OVERFLOW = 0x06,
+ AUDIO_EN_CTRL_ENCODER_ERROR = 0x07,
+ AUDIO_EN_CTRL_PARAM1 = 0x08,
+ AUDIO_EN_CTRL_PARAM2 = 0x09,
+ AUDIO_EN_CTRL_PARAM3 = 0x0A,
+ AUDIO_EN_CTRL_PARAM4 = 0x0B,
+ AUDIO_EN_CTRL_PARAM5 = 0x0C,
+ AUDIO_EN_CTRL_PARAM6 = 0x0D,
+ AUDIO_EN_CTRL_PARAM7 = 0x0E,
+ AUDIO_EN_CTRL_PARAM8 = 0x0F,
+} audio_encoder_control_selector_t;
+
+/// A.17.13 Decoder Control Selectors
+
+/// A.17.13.1 MPEG Decoder Control Selectors
+typedef enum
+{
+ AUDIO_MPD_CTRL_UNDEF = 0x00,
+ AUDIO_MPD_CTRL_DUAL_CHANNEL = 0x01,
+ AUDIO_MPD_CTRL_SECOND_STEREO = 0x02,
+ AUDIO_MPD_CTRL_MULTILINGUAL = 0x03,
+ AUDIO_MPD_CTRL_DYN_RANGE = 0x04,
+ AUDIO_MPD_CTRL_SCALING = 0x05,
+ AUDIO_MPD_CTRL_HILO_SCALING = 0x06,
+ AUDIO_MPD_CTRL_UNDERFLOW = 0x07,
+ AUDIO_MPD_CTRL_OVERFLOW = 0x08,
+ AUDIO_MPD_CTRL_DECODER_ERROR = 0x09,
+} audio_MPEG_decoder_control_selector_t;
+
+/// A.17.13.2 AC-3 Decoder Control Selectors
+typedef enum
+{
+ AUDIO_AD_CTRL_UNDEF = 0x00,
+ AUDIO_AD_CTRL_MODE = 0x01,
+ AUDIO_AD_CTRL_DYN_RANGE = 0x02,
+ AUDIO_AD_CTRL_SCALING = 0x03,
+ AUDIO_AD_CTRL_HILO_SCALING = 0x04,
+ AUDIO_AD_CTRL_UNDERFLOW = 0x05,
+ AUDIO_AD_CTRL_OVERFLOW = 0x06,
+ AUDIO_AD_CTRL_DECODER_ERROR = 0x07,
+} audio_AC3_decoder_control_selector_t;
+
+/// A.17.13.3 WMA Decoder Control Selectors
+typedef enum
+{
+ AUDIO_WD_CTRL_UNDEF = 0x00,
+ AUDIO_WD_CTRL_UNDERFLOW = 0x01,
+ AUDIO_WD_CTRL_OVERFLOW = 0x02,
+ AUDIO_WD_CTRL_DECODER_ERROR = 0x03,
+} audio_WMA_decoder_control_selector_t;
+
+/// A.17.13.4 DTS Decoder Control Selectors
+typedef enum
+{
+ AUDIO_DD_CTRL_UNDEF = 0x00,
+ AUDIO_DD_CTRL_UNDERFLOW = 0x01,
+ AUDIO_DD_CTRL_OVERFLOW = 0x02,
+ AUDIO_DD_CTRL_DECODER_ERROR = 0x03,
+} audio_DTS_decoder_control_selector_t;
+
+/// A.17.14 Endpoint Control Selectors
+typedef enum
+{
+ AUDIO_EP_CTRL_UNDEF = 0x00,
+ AUDIO_EP_CTRL_PITCH = 0x01,
+ AUDIO_EP_CTRL_DATA_OVERRUN = 0x02,
+ AUDIO_EP_CTRL_DATA_UNDERRUN = 0x03,
+} audio_EP_control_selector_t;
-/// Audio Class-Specific AC Interface Descriptor Subtypes
+/// Terminal Types
+
+/// 2.1 - Audio Class-Terminal Types UAC2
+typedef enum
+{
+ AUDIO_TERM_TYPE_USB_UNDEFINED = 0x0100,
+ AUDIO_TERM_TYPE_USB_STREAMING = 0x0101,
+ AUDIO_TERM_TYPE_USB_VENDOR_SPEC = 0x01FF,
+} audio_terminal_type_t;
+
+/// 2.2 - Audio Class-Input Terminal Types UAC2
+typedef enum
+{
+ AUDIO_TERM_TYPE_IN_UNDEFINED = 0x0200,
+ AUDIO_TERM_TYPE_IN_GENERIC_MIC = 0x0201,
+ AUDIO_TERM_TYPE_IN_DESKTOP_MIC = 0x0202,
+ AUDIO_TERM_TYPE_IN_PERSONAL_MIC = 0x0203,
+ AUDIO_TERM_TYPE_IN_OMNI_MIC = 0x0204,
+ AUDIO_TERM_TYPE_IN_ARRAY_MIC = 0x0205,
+ AUDIO_TERM_TYPE_IN_PROC_ARRAY_MIC = 0x0206,
+} audio_terminal_input_type_t;
+
+/// 2.3 - Audio Class-Output Terminal Types UAC2
+typedef enum
+{
+ AUDIO_TERM_TYPE_OUT_UNDEFINED = 0x0300,
+ AUDIO_TERM_TYPE_OUT_GENERIC_SPEAKER = 0x0301,
+ AUDIO_TERM_TYPE_OUT_HEADPHONES = 0x0302,
+ AUDIO_TERM_TYPE_OUT_HEAD_MNT_DISP_AUIDO = 0x0303,
+ AUDIO_TERM_TYPE_OUT_DESKTOP_SPEAKER = 0x0304,
+ AUDIO_TERM_TYPE_OUT_ROOM_SPEAKER = 0x0305,
+ AUDIO_TERM_TYPE_OUT_COMMUNICATION_SPEAKER = 0x0306,
+ AUDIO_TERM_TYPE_OUT_LOW_FRQ_EFFECTS_SPEAKER = 0x0307,
+} audio_terminal_output_type_t;
+
+/// Rest is yet to be implemented
+
+/// Additional Audio Device Class Codes - Source: Audio Data Formats
+
+/// A.1 - Audio Class-Format Type Codes UAC2
+//typedef enum
+//{
+// AUDIO_FORMAT_TYPE_UNDEFINED = 0x00,
+// AUDIO_FORMAT_TYPE_I = 0x01,
+// AUDIO_FORMAT_TYPE_II = 0x02,
+// AUDIO_FORMAT_TYPE_III = 0x03,
+// AUDIO_FORMAT_TYPE_IV = 0x04,
+// AUDIO_EXT_FORMAT_TYPE_I = 0x81,
+// AUDIO_EXT_FORMAT_TYPE_II = 0x82,
+// AUDIO_EXT_FORMAT_TYPE_III = 0x83,
+//} audio_format_type_t;
+
+#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;
+
+#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
+
+/// All remaining definitions are taken from the descriptor descriptions in the UAC2 main specification
+
+/// Isochronous End Point Attributes
+typedef enum
+{
+ TUSB_ISO_EP_ATT_NO_SYNC = 0x00,
+ TUSB_ISO_EP_ATT_ASYNCHRONOUS = 0x04,
+ TUSB_ISO_EP_ATT_ADAPTIVE = 0x08,
+ TUSB_ISO_EP_ATT_SYNCHRONOUS = 0x0C,
+ TUSB_ISO_EP_ATT_DATA = 0x00, ///< Data End Point
+ TUSB_ISO_EP_ATT_EXPLICIT_FB = 0x10, ///< Feedback End Point
+ TUSB_ISO_EP_ATT_IMPLICIT_FB = 0x20, ///< Data endpoint that also serves as an implicit feedback
+} tusb_iso_ep_attribute_t;
+
+/// Audio Class-Control Values UAC2
+typedef enum
+{
+ AUDIO_CTRL_NONE = 0x00, ///< No Host access
+ AUDIO_CTRL_R = 0x01, ///< Host read access only
+ AUDIO_CTRL_RW = 0x03, ///< Host read write access
+} audio_control_t;
+
+/// Audio Class-Specific AC Interface Descriptor Controls UAC2
+typedef enum
+{
+ AUDIO_CS_AS_INTERFACE_CTRL_LATENCY_POS = 0,
+} audio_cs_ac_interface_control_pos_t;
+
+/// Audio Class-Specific AS Interface Descriptor Controls UAC2
+typedef enum
+{
+ AUDIO_CS_AS_INTERFACE_CTRL_ACTIVE_ALT_SET_POS = 0,
+ AUDIO_CS_AS_INTERFACE_CTRL_VALID_ALT_SET_POS = 2,
+} audio_cs_as_interface_control_pos_t;
+
+/// Audio Class-Specific AS Isochronous Data EP Attributes UAC2
+typedef enum
+{
+ AUDIO_CS_AS_ISO_DATA_EP_ATT_MAX_PACKETS_ONLY = 0x80,
+ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK = 0x00,
+} audio_cs_as_iso_data_ep_attribute_t;
+
+/// Audio Class-Specific AS Isochronous Data EP Controls UAC2
+typedef enum
+{
+ AUDIO_CS_AS_ISO_DATA_EP_CTRL_PITCH_POS = 0,
+ AUDIO_CS_AS_ISO_DATA_EP_CTRL_DATA_OVERRUN_POS = 2,
+ AUDIO_CS_AS_ISO_DATA_EP_CTRL_DATA_UNDERRUN_POS = 4,
+} audio_cs_as_iso_data_ep_control_pos_t;
+
+/// Audio Class-Specific AS Isochronous Data EP Lock Delay Units UAC2
+typedef enum
+{
+ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED = 0x00,
+ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_MILLISEC = 0x01,
+ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_PCM_SAMPLES = 0x02,
+} audio_cs_as_iso_data_ep_lock_delay_unit_t;
+
+/// Audio Class-Clock Source Attributes UAC2
+typedef enum
+{
+ AUDIO_CLOCK_SOURCE_ATT_EXT_CLK = 0x00,
+ AUDIO_CLOCK_SOURCE_ATT_INT_FIX_CLK = 0x01,
+ AUDIO_CLOCK_SOURCE_ATT_INT_VAR_CLK = 0x02,
+ AUDIO_CLOCK_SOURCE_ATT_INT_PRO_CLK = 0x03,
+ AUDIO_CLOCK_SOURCE_ATT_CLK_SYC_SOF = 0x04,
+} audio_clock_source_attribute_t;
+
+/// Audio Class-Clock Source Controls UAC2
+typedef enum
+{
+ AUDIO_CLOCK_SOURCE_CTRL_CLK_FRQ_POS = 0,
+ AUDIO_CLOCK_SOURCE_CTRL_CLK_VAL_POS = 2,
+} audio_clock_source_control_pos_t;
+
+/// Audio Class-Clock Selector Controls UAC2
+typedef enum
+{
+ AUDIO_CLOCK_SELECTOR_CTRL_POS = 0,
+} audio_clock_selector_control_pos_t;
+
+/// Audio Class-Clock Multiplier Controls UAC2
typedef enum
{
- AUDIO_CS_INTERFACE_HEADER = 0x01,
- AUDIO_CS_INTERFACE_INPUT_TERMINAL = 0x02,
- AUDIO_CS_INTERFACE_OUTPUT_TERMINAL = 0x03,
- AUDIO_CS_INTERFACE_MIXER_UNIT = 0x04,
- AUDIO_CS_INTERFACE_SELECTOR_UNIT = 0x05,
- AUDIO_CS_INTERFACE_FEATURE_UNIT = 0x06,
- AUDIO_CS_INTERFACE_EFFECT_UNIT = 0x07,
- AUDIO_CS_INTERFACE_PROCESSING_UNIT = 0x08,
- AUDIO_CS_INTERFACE_EXTENSION_UNIT = 0x09,
- AUDIO_CS_INTERFACE_CLOCK_SOURCE = 0x0A,
- AUDIO_CS_INTERFACE_CLOCK_SELECTOR = 0x0B,
- AUDIO_CS_INTERFACE_CLOCK_MULTIPLIER = 0x0C,
- AUDIO_CS_INTERFACE_SAMPLE_RATE_CONVERTER = 0x0D,
-} audio_cs_interface_subtype_t;
+ AUDIO_CLOCK_MULTIPLIER_CTRL_NUMERATOR_POS = 0,
+ AUDIO_CLOCK_MULTIPLIER_CTRL_DENOMINATOR_POS = 2,
+} audio_clock_multiplier_control_pos_t;
+
+/// Audio Class-Input Terminal Controls UAC2
+typedef enum
+{
+ AUDIO_IN_TERM_CTRL_CPY_PROT_POS = 0,
+ AUDIO_IN_TERM_CTRL_CONNECTOR_POS = 2,
+ AUDIO_IN_TERM_CTRL_OVERLOAD_POS = 4,
+ AUDIO_IN_TERM_CTRL_CLUSTER_POS = 6,
+ AUDIO_IN_TERM_CTRL_UNDERFLOW_POS = 8,
+ AUDIO_IN_TERM_CTRL_OVERFLOW_POS = 10,
+} audio_terminal_input_control_pos_t;
+
+/// Audio Class-Output Terminal Controls UAC2
+typedef enum
+{
+ AUDIO_OUT_TERM_CTRL_CPY_PROT_POS = 0,
+ AUDIO_OUT_TERM_CTRL_CONNECTOR_POS = 2,
+ AUDIO_OUT_TERM_CTRL_OVERLOAD_POS = 4,
+ AUDIO_OUT_TERM_CTRL_UNDERFLOW_POS = 6,
+ AUDIO_OUT_TERM_CTRL_OVERFLOW_POS = 8,
+} audio_terminal_output_control_pos_t;
+
+/// Audio Class-Feature Unit Controls UAC2
+typedef enum
+{
+ AUDIO_FEATURE_UNIT_CTRL_MUTE_POS = 0,
+ AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS = 2,
+ AUDIO_FEATURE_UNIT_CTRL_BASS_POS = 4,
+ AUDIO_FEATURE_UNIT_CTRL_MID_POS = 6,
+ AUDIO_FEATURE_UNIT_CTRL_TREBLE_POS = 8,
+ AUDIO_FEATURE_UNIT_CTRL_GRAPHIC_EQU_POS = 10,
+ AUDIO_FEATURE_UNIT_CTRL_AGC_POS = 12,
+ AUDIO_FEATURE_UNIT_CTRL_DELAY_POS = 14,
+ AUDIO_FEATURE_UNIT_CTRL_BASS_BOOST_POS = 16,
+ AUDIO_FEATURE_UNIT_CTRL_LOUDNESS_POS = 18,
+ AUDIO_FEATURE_UNIT_CTRL_INPUT_GAIN_POS = 20,
+ AUDIO_FEATURE_UNIT_CTRL_INPUT_GAIN_PAD_POS = 22,
+ AUDIO_FEATURE_UNIT_CTRL_PHASE_INV_POS = 24,
+ AUDIO_FEATURE_UNIT_CTRL_UNDERFLOW_POS = 26,
+ AUDIO_FEATURE_UNIT_CTRL_OVERFLOW_POS = 28,
+} audio_feature_unit_control_pos_t;
+
+/// Audio Class-Audio Channel Configuration UAC2
+typedef enum
+{
+ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED = 0x00000000,
+ AUDIO_CHANNEL_CONFIG_FRONT_LEFT = 0x00000001,
+ AUDIO_CHANNEL_CONFIG_FRONT_RIGHT = 0x00000002,
+ AUDIO_CHANNEL_CONFIG_FRONT_CENTER = 0x00000004,
+ AUDIO_CHANNEL_CONFIG_LOW_FRQ_EFFECTS = 0x00000008,
+ AUDIO_CHANNEL_CONFIG_BACK_LEFT = 0x00000010,
+ AUDIO_CHANNEL_CONFIG_BACK_RIGHT = 0x00000020,
+ AUDIO_CHANNEL_CONFIG_FRONT_LEFT_OF_CENTER = 0x00000040,
+ AUDIO_CHANNEL_CONFIG_FRONT_RIGHT_OF_CENTER = 0x00000080,
+ AUDIO_CHANNEL_CONFIG_BACK_CENTER = 0x00000100,
+ AUDIO_CHANNEL_CONFIG_SIDE_LEFT = 0x00000200,
+ AUDIO_CHANNEL_CONFIG_SIDE_RIGHT = 0x00000400,
+ AUDIO_CHANNEL_CONFIG_TOP_CENTER = 0x00000800,
+ AUDIO_CHANNEL_CONFIG_TOP_FRONT_LEFT = 0x00001000,
+ AUDIO_CHANNEL_CONFIG_TOP_FRONT_CENTER = 0x00002000,
+ AUDIO_CHANNEL_CONFIG_TOP_FRONT_RIGHT = 0x00004000,
+ AUDIO_CHANNEL_CONFIG_TOP_BACK_LEFT = 0x00008000,
+ AUDIO_CHANNEL_CONFIG_TOP_BACK_CENTER = 0x00010000,
+ AUDIO_CHANNEL_CONFIG_TOP_BACK_RIGHT = 0x00020000,
+ AUDIO_CHANNEL_CONFIG_TOP_FRONT_LEFT_OF_CENTER = 0x00040000,
+ AUDIO_CHANNEL_CONFIG_TOP_FRONT_RIGHT_OF_CENTER = 0x00080000,
+ AUDIO_CHANNEL_CONFIG_LEFT_LOW_FRQ_EFFECTS = 0x00100000,
+ AUDIO_CHANNEL_CONFIG_RIGHT_LOW_FRQ_EFFECTS = 0x00200000,
+ AUDIO_CHANNEL_CONFIG_TOP_SIDE_LEFT = 0x00400000,
+ AUDIO_CHANNEL_CONFIG_TOP_SIDE_RIGHT = 0x00800000,
+ AUDIO_CHANNEL_CONFIG_BOTTOM_CENTER = 0x01000000,
+ AUDIO_CHANNEL_CONFIG_BACK_LEFT_OF_CENTER = 0x02000000,
+ AUDIO_CHANNEL_CONFIG_BACK_RIGHT_OF_CENTER = 0x04000000,
+ AUDIO_CHANNEL_CONFIG_RAW_DATA = 0x80000000,
+} audio_channel_config_t;
+
+/// AUDIO Channel Cluster Descriptor (4.1)
+typedef struct TU_ATTR_PACKED {
+ uint8_t bNrChannels; ///< Number of channels currently connected.
+ audio_channel_config_t bmChannelConfig; ///< Bitmap according to 'audio_channel_config_t' with a 1 set if channel is connected and 0 else. In case channels are non-predefined ignore them here (see UAC2 specification 4.1 Audio Channel Cluster Descriptor.
+ uint8_t iChannelNames; ///< Index of a string descriptor, describing the name of the first inserted channel with a non-predefined spatial location.
+} audio_desc_channel_cluster_t;
+
+/// AUDIO Class-Specific AC Interface Header Descriptor (4.7.2)
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of this descriptor in bytes: 9.
+ uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE.
+ uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AC_INTERFACE_HEADER.
+ uint16_t bcdADC ; ///< Audio Device Class Specification Release Number in Binary-Coded Decimal. Value: U16_TO_U8S_LE(0x0200).
+ uint8_t bCategory ; ///< Constant, indicating the primary use of this audio function, as intended by the manufacturer. See: audio_function_t.
+ uint16_t wTotalLength ; ///< Total number of bytes returned for the class-specific AudioControl interface descriptor. Includes the combined length of this descriptor header and all Clock Source, Unit and Terminal descriptors.
+ uint8_t bmControls ; ///< See: audio_cs_ac_interface_control_pos_t.
+} audio_desc_cs_ac_interface_t;
+
+/// AUDIO Clock Source Descriptor (4.7.2.1)
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of this descriptor in bytes: 8.
+ uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE.
+ uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AC_INTERFACE_CLOCK_SOURCE.
+ uint8_t bClockID ; ///< Constant uniquely identifying the Clock Source Entity within the audio function. This value is used in all requests to address this Entity.
+ uint8_t bmAttributes ; ///< See: audio_clock_source_attribute_t.
+ uint8_t bmControls ; ///< See: audio_clock_source_control_pos_t.
+ uint8_t bAssocTerminal ; ///< Terminal ID of the Terminal that is associated with this Clock Source.
+ uint8_t iClockSource ; ///< Index of a string descriptor, describing the Clock Source Entity.
+} audio_desc_clock_source_t;
+
+/// AUDIO Clock Selector Descriptor (4.7.2.2) for ONE pin
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of this descriptor, in bytes: 7+p.
+ uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE.
+ uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AC_INTERFACE_CLOCK_SELECTOR.
+ uint8_t bClockID ; ///< Constant uniquely identifying the Clock Selector Entity within the audio function. This value is used in all requests to address this Entity.
+ uint8_t bNrInPins ; ///< Number of Input Pins of this Unit: p = 1 thus bNrInPins = 1.
+ uint8_t baCSourceID ; ///< ID of the Clock Entity to which the first Clock Input Pin of this Clock Selector Entity is connected..
+ uint8_t bmControls ; ///< See: audio_clock_selector_control_pos_t.
+ uint8_t iClockSource ; ///< Index of a string descriptor, describing the Clock Selector Entity.
+} audio_desc_clock_selector_t;
+
+/// AUDIO Clock Selector Descriptor (4.7.2.2) for multiple pins
+#define audio_desc_clock_selector_n_t(source_num) \
+ struct TU_ATTR_PACKED { \
+ uint8_t bLength ; \
+ uint8_t bDescriptorType ; \
+ uint8_t bDescriptorSubType ; \
+ uint8_t bClockID ; \
+ uint8_t bNrInPins ; \
+ struct TU_ATTR_PACKED { \
+ uint8_t baSourceID ; \
+ } sourceID[source_num] ; \
+ uint8_t bmControls ; \
+ uint8_t iClockSource ; \
+}
+
+/// AUDIO Clock Multiplier Descriptor (4.7.2.3)
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of this descriptor, in bytes: 7.
+ uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE.
+ uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AC_INTERFACE_CLOCK_MULTIPLIER.
+ uint8_t bClockID ; ///< Constant uniquely identifying the Clock Multiplier Entity within the audio function. This value is used in all requests to address this Entity.
+ uint8_t bCSourceID ; ///< ID of the Clock Entity to which the last Clock Input Pin of this Clock Selector Entity is connected.
+ uint8_t bmControls ; ///< See: audio_clock_multiplier_control_pos_t.
+ uint8_t iClockSource ; ///< Index of a string descriptor, describing the Clock Multiplier Entity.
+} audio_desc_clock_multiplier_t;
+
+/// AUDIO Input Terminal Descriptor(4.7.2.4)
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of this descriptor, in bytes: 17.
+ uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE.
+ uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AC_INTERFACE_INPUT_TERMINAL.
+ uint16_t wTerminalType ; ///< Constant characterizing the type of Terminal. See: audio_terminal_type_t for USB streaming and audio_terminal_input_type_t for other input types.
+ uint8_t bAssocTerminal ; ///< ID of the Output Terminal to which this Input Terminal is associated.
+ uint8_t bCSourceID ; ///< ID of the Clock Entity to which this Input Terminal is connected.
+ uint8_t bNrChannels ; ///< Number of logical output channels in the Terminal’s output audio channel cluster.
+ uint32_t bmChannelConfig ; ///< Describes the spatial location of the logical channels. See:audio_channel_config_t.
+ uint16_t bmControls ; ///< See: audio_terminal_input_control_pos_t.
+ uint8_t iTerminal ; ///< Index of a string descriptor, describing the Input Terminal.
+} audio_desc_input_terminal_t;
+
+/// AUDIO Output Terminal Descriptor(4.7.2.5)
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of this descriptor, in bytes: 12.
+ uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE.
+ uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AC_INTERFACE_OUTPUT_TERMINAL.
+ uint8_t bTerminalID ; ///< Constant uniquely identifying the Terminal within the audio function. This value is used in all requests to address this Terminal.
+ uint16_t wTerminalType ; ///< Constant characterizing the type of Terminal. See: audio_terminal_type_t for USB streaming and audio_terminal_output_type_t for other output types.
+ uint8_t bAssocTerminal ; ///< Constant, identifying the Input Terminal to which this Output Terminal is associated.
+ uint8_t bSourceID ; ///< ID of the Unit or Terminal to which this Terminal is connected.
+ uint8_t bCSourceID ; ///< ID of the Clock Entity to which this Output Terminal is connected.
+ uint16_t bmControls ; ///< See: audio_terminal_output_type_t.
+ uint8_t iTerminal ; ///< Index of a string descriptor, describing the Output Terminal.
+} audio_desc_output_terminal_t;
+
+/// AUDIO Feature Unit Descriptor(4.7.2.8) for ONE channel
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of this descriptor, in bytes: 14.
+ uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE.
+ uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AC_INTERFACE_FEATURE_UNIT.
+ uint8_t bUnitID ; ///< Constant uniquely identifying the Unit within the audio function. This value is used in all requests to address this Unit.
+ uint8_t bSourceID ; ///< ID of the Unit or Terminal to which this Feature Unit is connected.
+ struct TU_ATTR_PACKED {
+ uint32_t bmaControls ; ///< See: audio_feature_unit_control_pos_t. Controls0 is master channel 0 (always present) and Controls1 is logical channel 1.
+ } controls[2] ;
+ uint8_t iTerminal ; ///< Index of a string descriptor, describing this Feature Unit.
+} audio_desc_feature_unit_t;
+
+/// AUDIO Feature Unit Descriptor(4.7.2.8) for multiple channels
+#define audio_desc_feature_unit_n_t(ch_num)\
+ struct TU_ATTR_PACKED { \
+ uint8_t bLength ; /* 6+(ch_num+1)*4 */\
+ uint8_t bDescriptorType ; \
+ uint8_t bDescriptorSubType ; \
+ uint8_t bUnitID ; \
+ uint8_t bSourceID ; \
+ struct TU_ATTR_PACKED { \
+ uint32_t bmaControls ; \
+ } controls[ch_num+1] ; \
+ uint8_t iTerminal ; \
+}
+
+/// AUDIO Class-Specific AS Interface Descriptor(4.9.2)
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of this descriptor, in bytes: 16.
+ uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE.
+ uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AS_INTERFACE_AS_GENERAL.
+ uint8_t bTerminalLink ; ///< The Terminal ID of the Terminal to which this interface is connected.
+ uint8_t bmControls ; ///< See: audio_cs_as_interface_control_pos_t.
+ uint8_t bFormatType ; ///< Constant identifying the Format Type the AudioStreaming interface is using. See: audio_format_type_t.
+ uint32_t bmFormats ; ///< The Audio Data Format(s) that can be used to communicate with this interface.See: audio_data_format_type_I_t.
+ uint8_t bNrChannels ; ///< Number of physical channels in the AS Interface audio channel cluster.
+ uint32_t bmChannelConfig ; ///< Describes the spatial location of the physical channels. See: audio_channel_config_t.
+ uint8_t iChannelNames ; ///< Index of a string descriptor, describing the name of the first physical channel.
+} audio_desc_cs_as_interface_t;
+
+/// AUDIO Type I Format Type Descriptor(2.3.1.6 - Audio Formats)
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of this descriptor, in bytes: 6.
+ uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE.
+ uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AS_INTERFACE_FORMAT_TYPE.
+ uint8_t bFormatType ; ///< Constant identifying the Format Type the AudioStreaming interface is using. Value: AUDIO_FORMAT_TYPE_I.
+ uint8_t bSubslotSize ; ///< The number of bytes occupied by one audio subslot. Can be 1, 2, 3 or 4.
+ uint8_t bBitResolution ; ///< The number of effectively used bits from the available bits in an audio subslot.
+} audio_desc_type_I_format_t;
+
+/// AUDIO Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2)
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength ; ///< Size of this descriptor, in bytes: 8.
+ uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_ENDPOINT.
+ uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_EP_SUBTYPE_GENERAL.
+ uint8_t bmAttributes ; ///< See: audio_cs_as_iso_data_ep_attribute_t.
+ uint8_t bmControls ; ///< See: audio_cs_as_iso_data_ep_control_pos_t.
+ uint8_t bLockDelayUnits ; ///< Indicates the units used for the wLockDelay field. See: audio_cs_as_iso_data_ep_lock_delay_unit_t.
+ uint16_t wLockDelay ; ///< Indicates the time it takes this endpoint to reliably lock its internal clock recovery circuitry. Units used depend on the value of the bLockDelayUnits field.
+} audio_desc_cs_as_iso_data_ep_t;
+
+//// 5.2.3 Control Request Parameter Block Layout
+
+// 5.2.3.1 1-byte Control CUR Parameter Block
+typedef struct TU_ATTR_PACKED
+{
+ int8_t bCur ; ///< The setting for the CUR attribute of the addressed Control
+} audio_control_cur_1_t;
+
+// 5.2.3.2 2-byte Control CUR Parameter Block
+typedef struct TU_ATTR_PACKED
+{
+ int16_t bCur ; ///< The setting for the CUR attribute of the addressed Control
+} audio_control_cur_2_t;
+
+// 5.2.3.3 4-byte Control CUR Parameter Block
+typedef struct TU_ATTR_PACKED
+{
+ int32_t bCur ; ///< The setting for the CUR attribute of the addressed Control
+} audio_control_cur_4_t;
+
+// Use the following ONLY for RECEIVED data - compiler does not know how many subranges are defined! Use the one below for predefined lengths - or if you know what you are doing do what you like
+// 5.2.3.1 1-byte Control RANGE Parameter Block
+typedef struct TU_ATTR_PACKED {
+ uint16_t wNumSubRanges;
+ struct TU_ATTR_PACKED {
+ int8_t bMin ; /*The setting for the MIN attribute of the nth subrange of the addressed Control*/
+ int8_t bMax ; /*The setting for the MAX attribute of the nth subrange of the addressed Control*/
+ uint8_t bRes ; /*The setting for the RES attribute of the nth subrange of the addressed Control*/
+ } subrange[] ;
+} audio_control_range_1_t;
+
+// 5.2.3.2 2-byte Control RANGE Parameter Block
+typedef struct TU_ATTR_PACKED {
+ uint16_t wNumSubRanges;
+ struct TU_ATTR_PACKED {
+ int16_t bMin ; /*The setting for the MIN attribute of the nth subrange of the addressed Control*/
+ int16_t bMax ; /*The setting for the MAX attribute of the nth subrange of the addressed Control*/
+ uint16_t bRes ; /*The setting for the RES attribute of the nth subrange of the addressed Control*/
+ } subrange[] ;
+} audio_control_range_2_t;
+
+// 5.2.3.3 4-byte Control RANGE Parameter Block
+typedef struct TU_ATTR_PACKED {
+ uint16_t wNumSubRanges;
+ struct TU_ATTR_PACKED {
+ int32_t bMin ; /*The setting for the MIN attribute of the nth subrange of the addressed Control*/
+ int32_t bMax ; /*The setting for the MAX attribute of the nth subrange of the addressed Control*/
+ uint32_t bRes ; /*The setting for the RES attribute of the nth subrange of the addressed Control*/
+ } subrange[] ;
+} audio_control_range_4_t;
+
+// 5.2.3.1 1-byte Control RANGE Parameter Block
+#define audio_control_range_1_n_t(numSubRanges) \
+ struct TU_ATTR_PACKED { \
+ uint16_t wNumSubRanges; \
+ struct TU_ATTR_PACKED { \
+ int8_t bMin ; /*The setting for the MIN attribute of the nth subrange of the addressed Control*/\
+ int8_t bMax ; /*The setting for the MAX attribute of the nth subrange of the addressed Control*/\
+ uint8_t bRes ; /*The setting for the RES attribute of the nth subrange of the addressed Control*/\
+ } subrange[numSubRanges] ; \
+}
+
+ /// 5.2.3.2 2-byte Control RANGE Parameter Block
+#define audio_control_range_2_n_t(numSubRanges) \
+ struct TU_ATTR_PACKED { \
+ uint16_t wNumSubRanges; \
+ struct TU_ATTR_PACKED { \
+ int16_t bMin ; /*The setting for the MIN attribute of the nth subrange of the addressed Control*/\
+ int16_t bMax ; /*The setting for the MAX attribute of the nth subrange of the addressed Control*/\
+ uint16_t bRes ; /*The setting for the RES attribute of the nth subrange of the addressed Control*/\
+ } subrange[numSubRanges]; \
+}
+
+ // 5.2.3.3 4-byte Control RANGE Parameter Block
+#define audio_control_range_4_n_t(numSubRanges) \
+ struct TU_ATTR_PACKED { \
+ uint16_t wNumSubRanges; \
+ struct TU_ATTR_PACKED { \
+ int32_t bMin ; /*The setting for the MIN attribute of the nth subrange of the addressed Control*/\
+ int32_t bMax ; /*The setting for the MAX attribute of the nth subrange of the addressed Control*/\
+ uint32_t bRes ; /*The setting for the RES attribute of the nth subrange of the addressed Control*/\
+ } subrange[numSubRanges]; \
+}
-/** @} */
+ /** @} */
#ifdef __cplusplus
- }
+ }
#endif
#endif
-/** @} */
+ /** @} */
diff --git a/src/class/audio/audio_device.c b/src/class/audio/audio_device.c
new file mode 100644
index 000000000..2c8ba300d
--- /dev/null
+++ b/src/class/audio/audio_device.c
@@ -0,0 +1,1448 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Reinhard Panhuber, Jerzy Kasenberg
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+/*
+ * This driver supports at most one out EP, one in EP, one control EP, and one feedback EP and one alternative interface other than zero. Hence, only one input terminal and one output terminal are support, if you need more adjust the driver!
+ * It supports multiple TX and RX channels.
+ *
+ * In case you need more alternate interfaces, you need to define additional defines for this specific alternate interface. Just define them and set them in the set_interface function.
+ *
+ * */
+
+#include "tusb_option.h"
+
+#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_AUDIO)
+
+//--------------------------------------------------------------------+
+// INCLUDE
+//--------------------------------------------------------------------+
+#include "audio_device.h"
+#include "class/audio/audio.h"
+#include "device/usbd_pvt.h"
+
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF
+//--------------------------------------------------------------------+
+
+#if CFG_TUD_AUDIO_EPSIZE_IN && CFG_TUD_AUDIO_TX_FIFO_SIZE
+#ifndef CFG_TUD_AUDIO_TX_FIFO_COUNT
+#define CFG_TUD_AUDIO_TX_FIFO_COUNT CFG_TUD_AUDIO_N_CHANNELS_TX
+#endif
+#endif
+
+#if CFG_TUD_AUDIO_EPSIZE_OUT && CFG_TUD_AUDIO_RX_FIFO_SIZE
+#ifndef CFG_TUD_AUDIO_RX_FIFO_COUNT
+#define CFG_TUD_AUDIO_RX_FIFO_COUNT CFG_TUD_AUDIO_N_CHANNELS_RX
+#endif
+#endif
+
+typedef struct
+{
+ uint8_t rhport;
+ uint8_t const * p_desc; // Pointer pointing to Standard AC Interface Descriptor(4.7.1) - Audio Control descriptor defining audio function
+
+#if CFG_TUD_AUDIO_EPSIZE_IN
+ uint8_t ep_in; // Outgoing (out of uC) audio data EP.
+ uint16_t epin_buf_cnt; // Count filling status of EP in buffer - this is a shared state currently and is intended to be removed once EP buffers can be implemented as FIFOs!
+ 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
+ uint8_t ep_out; // Incoming (into uC) audio data EP.
+ uint8_t ep_out_as_intf_num; // Corresponding Standard AS Interface Descriptor (4.9.1) belonging to input terminal to which this EP belongs - 0 is invalid (this fits to UAC2 specification since AS interfaces can not have interface number equal to zero)
+
+#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+ uint8_t ep_fb; // Feedback EP.
+#endif
+
+#endif
+
+#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
+ uint8_t ep_int_ctr; // Audio control interrupt EP.
+#endif
+
+#if CFG_TUD_AUDIO_N_AS_INT
+ uint8_t altSetting[CFG_TUD_AUDIO_N_AS_INT]; // We need to save the current alternate setting this way, because it is possible that there are AS interfaces which do not have an EP!
+#endif
+ /*------------- From this point, data is not cleared by bus reset -------------*/
+
+ // Buffer for control requests
+ CFG_TUSB_MEM_ALIGN uint8_t ctrl_buf[CFG_TUD_AUDIO_CTRL_BUF_SIZE];
+
+ // FIFO
+#if CFG_TUD_AUDIO_EPSIZE_IN && CFG_TUD_AUDIO_TX_FIFO_SIZE
+ tu_fifo_t tx_ff[CFG_TUD_AUDIO_TX_FIFO_COUNT];
+ CFG_TUSB_MEM_ALIGN uint8_t tx_ff_buf[CFG_TUD_AUDIO_TX_FIFO_COUNT][CFG_TUD_AUDIO_TX_FIFO_SIZE];
+#if CFG_FIFO_MUTEX
+ osal_mutex_def_t tx_ff_mutex[CFG_TUD_AUDIO_TX_FIFO_COUNT];
+#endif
+#endif
+
+#if CFG_TUD_AUDIO_EPSIZE_OUT && CFG_TUD_AUDIO_RX_FIFO_SIZE
+ tu_fifo_t rx_ff[CFG_TUD_AUDIO_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];
+#endif
+#endif
+
+#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;
+#endif
+#endif
+
+ // Endpoint Transfer buffers
+#if CFG_TUD_AUDIO_EPSIZE_OUT
+ CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_AUDIO_EPSIZE_OUT]; // Bigger makes no sense for isochronous EP's (but technically possible here)
+
+#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+ uint32_t fb_val; // Feedback value for asynchronous mode (in 16.16 format).
+#endif
+
+#endif
+
+#if CFG_TUD_AUDIO_EPSIZE_IN
+ CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_AUDIO_EPSIZE_IN]; // Bigger makes no sense for isochronous EP's (but technically possible here)
+#endif
+
+#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
+ CFG_TUSB_MEM_ALIGN uint8_t ep_int_ctr_buf[CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN];
+#endif
+
+} audiod_interface_t;
+
+#define ITF_MEM_RESET_SIZE offsetof(audiod_interface_t, ctrl_buf)
+
+//--------------------------------------------------------------------+
+// INTERNAL OBJECT & FUNCTION DECLARATION
+//--------------------------------------------------------------------+
+CFG_TUSB_MEM_SECTION audiod_interface_t _audiod_itf[CFG_TUD_AUDIO];
+
+extern const uint16_t tud_audio_desc_lengths[];
+
+#if CFG_TUD_AUDIO_EPSIZE_OUT
+static bool audio_rx_done_type_I_pcm_ff_cb(uint8_t rhport, audiod_interface_t* audio, uint8_t * buffer, uint16_t bufsize);
+#endif
+
+#if CFG_TUD_AUDIO_EPSIZE_IN
+static bool audiod_tx_done_type_I_pcm_ff_cb(uint8_t rhport, audiod_interface_t* audio);
+#endif
+
+static bool audiod_get_interface(uint8_t rhport, tusb_control_request_t const * p_request);
+static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const * p_request);
+
+static bool audiod_get_AS_interface_index(uint8_t itf, uint8_t *idxDriver, uint8_t *idxItf, uint8_t const **pp_desc_int);
+static bool audiod_verify_entity_exists(uint8_t itf, uint8_t entityID, uint8_t *idxDriver);
+static bool audiod_verify_itf_exists(uint8_t itf, uint8_t *idxDriver);
+static bool audiod_verify_ep_exists(uint8_t ep, uint8_t *idxDriver);
+
+bool tud_audio_n_mounted(uint8_t itf)
+{
+ audiod_interface_t* audio = &_audiod_itf[itf];
+
+#if CFG_TUD_AUDIO_EPSIZE_OUT
+ if (audio->ep_out == 0)
+ {
+ return false;
+ }
+#endif
+
+#if CFG_TUD_AUDIO_EPSIZE_IN
+ if (audio->ep_in == 0)
+ {
+ return false;
+ }
+#endif
+
+#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
+ if (audio->ep_int_ctr == 0)
+ {
+ return false;
+ }
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+ if (audio->ep_fb == 0)
+ {
+ return false;
+ }
+#endif
+
+ return true;
+}
+
+//--------------------------------------------------------------------+
+// READ API
+//--------------------------------------------------------------------+
+
+#if CFG_TUD_AUDIO_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]);
+}
+
+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]);
+}
+
+uint16_t tud_audio_n_read(uint8_t itf, void* buffer, uint16_t bufsize)
+{
+ return tu_fifo_read_n(&_audiod_itf[itf].rx_ff[0], buffer, bufsize);
+}
+
+void tud_audio_n_read_flush (uint8_t itf)
+{
+ tu_fifo_clear(&_audiod_itf[itf].rx_ff[0]);
+}
+#endif
+#endif
+
+#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
+
+uint16_t tud_audio_int_ctr_n_available(uint8_t itf)
+{
+ return tu_fifo_count(&_audiod_itf[itf].int_ctr_ff);
+}
+
+uint16_t tud_audio_int_ctr_n_read(uint8_t itf, void* buffer, uint16_t bufsize)
+{
+ return tu_fifo_read_n(&_audiod_itf[itf].int_ctr_ff, buffer, bufsize);
+}
+
+void tud_audio_int_ctr_n_read_flush (uint8_t itf)
+{
+ tu_fifo_clear(&_audiod_itf[itf].int_ctr_ff);
+}
+
+#endif
+
+// This function is called once something is received by USB and is responsible for decoding received stream into audio channels.
+// If you prefer your own (more efficient) implementation suiting your purpose set CFG_TUD_AUDIO_RX_FIFO_SIZE = 0.
+
+#if CFG_TUD_AUDIO_EPSIZE_OUT
+
+static bool audio_rx_done_cb(uint8_t rhport, audiod_interface_t* audio, uint8_t* buffer, uint16_t bufsize)
+{
+ switch (CFG_TUD_AUDIO_FORMAT_TYPE_RX)
+ {
+ case AUDIO_FORMAT_TYPE_UNDEFINED:
+ // INDIVIDUAL DECODING PROCEDURE REQUIRED HERE!
+ TU_LOG2(" Desired CFG_TUD_AUDIO_FORMAT encoding not implemented!\r\n");
+ TU_BREAKPOINT();
+ break;
+
+ case AUDIO_FORMAT_TYPE_I:
+
+ switch (CFG_TUD_AUDIO_FORMAT_TYPE_I_RX)
+ {
+ 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
+ break;
+
+ default:
+ // DESIRED CFG_TUD_AUDIO_FORMAT_TYPE_I_RX NOT IMPLEMENTED!
+ TU_LOG2(" Desired CFG_TUD_AUDIO_FORMAT_TYPE_I_RX encoding not implemented!\r\n");
+ TU_BREAKPOINT();
+ break;
+ }
+ break;
+
+ default:
+ // Desired CFG_TUD_AUDIO_FORMAT_TYPE_RX not implemented!
+ TU_LOG2(" Desired CFG_TUD_AUDIO_FORMAT_TYPE_RX not implemented!\r\n");
+ TU_BREAKPOINT();
+ break;
+ }
+
+ // 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));
+
+ return true;
+}
+
+#endif //CFG_TUD_AUDIO_EPSIZE_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)
+{
+ (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;
+ }
+
+ 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
+
+ for(cnt = 0; cnt < bufsize; cnt += CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX)
+ {
+ // 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;
+ }
+
+ if (chId == CFG_TUD_AUDIO_N_CHANNELS_RX)
+ {
+ chId = 0;
+ }
+ }
+ return true;
+}
+#else
+static bool audio_rx_done_type_I_pcm_ff_cb(uint8_t rhport, audiod_interface_t *audio, uint8_t *buffer, uint16_t bufsize)
+{
+ (void) rhport;
+
+ // We expect to get a multiple of CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX * CFG_TUD_AUDIO_N_CHANNELS_RX per channel
+ if (bufsize % (CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX * CFG_TUD_AUDIO_N_CHANNELS_RX) != 0)
+ {
+ return false;
+ }
+
+ tu_fifo_write_n(&audio->rx_ff[0], buffer, bufsize);
+ return true;
+}
+#endif // CFG_TUD_AUDIO_RX_FIFO_COUNT > 1
+#endif //CFG_TUD_AUDIO_RX_FIFO_SIZE
+
+//--------------------------------------------------------------------+
+// WRITE API
+//--------------------------------------------------------------------+
+
+/**
+ * \brief Write data to EP in buffer
+ *
+ * Write data to buffer. If it is full, new data can be inserted once a transmit was scheduled. See audiod_tx_done_cb().
+ * If TX FIFOs are used, this function is not available in order to not let the user mess up the encoding process.
+ *
+ * \param[in] itf: Index of audio function interface
+ * \param[in] data: Pointer to data array to be copied from
+ * \param[in] len: # of array elements to copy
+ * \return Number of bytes actually written
+ */
+#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)
+{
+ audiod_interface_t* audio = &_audiod_itf[itf];
+ if (audio->p_desc == NULL) {
+ return 0;
+ }
+
+ // THIS IS A CRITICAL SECTION - audio->epin_buf_cnt MUST NOT BE MODIFIED FROM HERE - happens if audiod_tx_done_cb() is executed in between!
+
+ // FOR SINGLE THREADED OPERATION:
+ // AS LONG AS THIS FUNCTION IS NOT EXECUTED WITHIN AN INTERRUPT ALL IS FINE!
+
+ // Determine free space
+ uint16_t free = CFG_TUD_AUDIO_EPSIZE_IN - audio->epin_buf_cnt;
+
+ // Clip length if needed
+ if (len > free) len = free;
+
+ // Write data
+ memcpy((void *) &audio->epin_buf[audio->epin_buf_cnt], data, len);
+
+ audio->epin_buf_cnt += len;
+
+ // Return number of bytes written
+ return len;
+}
+*/
+
+#else
+
+#if CFG_TUD_AUDIO_TX_FIFO_COUNT == 1
+uint16_t tud_audio_n_write(uint8_t itf, void const* data, uint16_t len)
+{
+ {
+ audiod_interface_t* audio = &_audiod_itf[itf];
+ if (audio->p_desc == NULL)
+ {
+ return 0;
+ }
+ return tu_fifo_write_n(&audio->tx_ff[0], data, len);
+ }
+}
+#else
+uint16_t tud_audio_n_write(uint8_t itf, uint8_t channelId, const void * data, uint16_t len)
+{
+ audiod_interface_t* audio = &_audiod_itf[itf];
+ if (audio->p_desc == NULL) {
+ return 0;
+ }
+
+ return tu_fifo_write_n(&audio->tx_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)
+{
+ audiod_interface_t *audio = &_audiod_itf[itf];
+ if (audio->p_desc == NULL) {
+ return 0;
+ }
+
+ uint16_t n_bytes_copied;
+ TU_VERIFY(audiod_tx_done_cb(audio->rhport, audio, &n_bytes_copied));
+ return n_bytes_copied;
+}
+
+#endif
+#endif
+
+#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN > 0
+uint32_t tud_audio_int_ctr_n_write(uint8_t itf, uint8_t const* buffer, uint32_t bufsize)
+{
+ audiod_interface_t* audio = &_audiod_itf[itf];
+ if (audio->p_desc == NULL) {
+ return 0;
+ }
+
+ return tu_fifo_write_n(&audio->int_ctr_ff, buffer, bufsize);
+}
+#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).
+
+// 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)
+{
+ 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));
+ }
+
+ // 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 CFG_TUD_AUDIO_TX_FIFO_SIZE
+ switch (CFG_TUD_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();
+ break;
+
+ case AUDIO_FORMAT_TYPE_I:
+
+ switch (CFG_TUD_AUDIO_FORMAT_TYPE_I_TX)
+ {
+ case AUDIO_DATA_FORMAT_TYPE_I_PCM:
+
+ TU_VERIFY(audiod_tx_done_type_I_pcm_ff_cb(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();
+ break;
+ }
+ break;
+
+ default:
+ // Desired CFG_TUD_AUDIO_FORMAT_TYPE_TX not implemented!
+ TU_LOG2(" Desired CFG_TUD_AUDIO_FORMAT_TYPE_TX not implemented!\r\n");
+ TU_BREAKPOINT();
+ 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!
+
+ // 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));
+
+ // Inform how many bytes were copied
+ *n_bytes_copied = audio->epin_buf_cnt;
+
+ // Declare EP in buffer empty
+ audio->epin_buf_cnt = 0;
+
+ // TO HERE
+
+ // 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]));
+
+ return true;
+}
+
+#endif //CFG_TUD_AUDIO_EPSIZE_IN
+
+#if CFG_TUD_AUDIO_TX_FIFO_SIZE
+#if CFG_TUD_AUDIO_TX_FIFO_COUNT > 1 || (CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX != CFG_TUD_AUDIO_TX_ITEMSIZE)
+static bool audiod_tx_done_type_I_pcm_ff_cb(uint8_t rhport, audiod_interface_t* audio)
+{
+ // We encode directly into IN EP's buffer - abort if previous transfer not complete
+ TU_VERIFY(!usbd_edpt_busy(rhport, audio->ep_in));
+
+ // Determine amount of samples
+ uint16_t const nEndpointSampleCapacity = CFG_TUD_AUDIO_EPSIZE_IN / CFG_TUD_AUDIO_N_CHANNELS_TX / CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX;
+ uint16_t nSamplesPerChannelToSend = tu_fifo_count(&audio->tx_ff[0]) / CFG_TUD_AUDIO_TX_ITEMSIZE;
+ uint16_t nBytesToSend;
+ uint8_t cntChannel;
+
+ for (cntChannel = 1; cntChannel < CFG_TUD_AUDIO_N_CHANNELS_TX; cntChannel++)
+ {
+ 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;
+ }
+ }
+
+ // Check if there is enough
+ if (nSamplesPerChannelToSend == 0)
+ {
+ audio->epin_buf_cnt = 0;
+ return true;
+ }
+
+ // Limit to maximum sample number - THIS IS A POSSIBLE ERROR SOURCE IF TOO MANY SAMPLE WOULD NEED TO BE SENT BUT CAN NOT!
+ nSamplesPerChannelToSend = tu_min16(nSamplesPerChannelToSend, nEndpointSampleCapacity);
+ nBytesToSend = nSamplesPerChannelToSend * CFG_TUD_AUDIO_N_CHANNELS_TX * CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX;
+
+ // Encode
+ uint16_t cntSample;
+ uint8_t * pBuff = audio->epin_buf;
+#if CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX == 1
+ uint8_t sample;
+#elif CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX == 2
+ uint16_t sample;
+#else
+ uint32_t sample;
+#endif
+
+ // TODO: Big endianess handling
+ for (cntSample = 0; cntSample < nSamplesPerChannelToSend; cntSample++)
+ {
+ for (cntChannel = 0; cntChannel < CFG_TUD_AUDIO_N_CHANNELS_TX; cntChannel++)
+ {
+ // Get sample from buffer
+ tu_fifo_read_n(&audio->tx_ff[cntChannel], &sample, CFG_TUD_AUDIO_TX_ITEMSIZE);
+
+ // Put it into EP's buffer - Let alignment problems be handled by memcpy
+ memcpy(pBuff, &sample, CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX);
+
+ // Advance pointer
+ pBuff += CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX;
+ }
+ }
+
+ audio->epin_buf_cnt = nBytesToSend;
+
+ return true;
+}
+
+#else
+static bool audiod_tx_done_type_I_pcm_ff_cb(uint8_t rhport, audiod_interface_t* audio)
+{
+ // We encode directly into IN EP's buffer - abort if previous transfer not complete
+ TU_VERIFY(!usbd_edpt_busy(rhport, audio->ep_in));
+
+ // Determine amount of samples
+ uint16_t nByteCount = tu_fifo_count(&audio->tx_ff[0]);
+
+ nByteCount = tu_min16(nByteCount, CFG_TUD_AUDIO_EPSIZE_IN);
+
+ // Check if there is enough
+ if (nByteCount == 0)
+ {
+ return true;
+ }
+
+ nByteCount = tu_fifo_read_n(&audio->tx_ff[0], audio->epin_buf, nByteCount);
+ audio->epin_buf_cnt = nByteCount;
+
+ return true;
+}
+#endif // CFG_TUD_AUDIO_TX_FIFO_COUNT > 1 || (CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX != CFG_TUD_AUDIO_TX_ITEMSIZE)
+
+#endif //CFG_TUD_AUDIO_TX_FIFO_SIZE
+
+// This function is called once a transmit of an feedback packet was successfully completed. Here, we get the next feedback value to be sent
+
+#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];
+ uint16_t len;
+
+ if (audio->fb_val == 0)
+ {
+ len = 0;
+ return true;
+ }
+ else
+ {
+ 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);
+ }
+
+}
+
+//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;
+//}
+
+#endif
+
+// This function is called once a transmit of an interrupt control packet was successfully completed. Here, we get the remaining bytes to send
+
+#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));
+
+ // 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);
+
+ if (cnt > 0)
+ {
+ // Schedule transmit
+ TU_VERIFY(usbd_edpt_xfer(rhport, audio->ep_int_ctr, audio->ep_int_ctr_buf, cnt));
+ }
+
+ *n_bytes_copied = cnt;
+
+ if (tud_audio_int_ctr_done_cb) TU_VERIFY(tud_audio_int_ctr_done_cb(rhport, n_bytes_copied));
+
+ return true;
+}
+#endif
+
+//--------------------------------------------------------------------+
+// USBD Driver API
+//--------------------------------------------------------------------+
+void audiod_init(void)
+{
+ tu_memclr(_audiod_itf, sizeof(_audiod_itf));
+
+ for(uint8_t i=0; i<CFG_TUD_AUDIO; i++)
+ {
+ 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++)
+ {
+ tu_fifo_config(&audio->tx_ff[cnt], &audio->tx_ff_buf[cnt], CFG_TUD_AUDIO_TX_FIFO_SIZE, 1, true);
+#if CFG_FIFO_MUTEX
+ tu_fifo_config_mutex(&audio->tx_ff[cnt], osal_mutex_create(&audio->tx_ff_mutex[cnt]));
+#endif
+ }
+#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++)
+ {
+ tu_fifo_config(&audio->rx_ff[cnt], &audio->rx_ff_buf[cnt], CFG_TUD_AUDIO_RX_FIFO_SIZE, 1, true);
+#if CFG_FIFO_MUTEX
+ tu_fifo_config_mutex(&audio->rx_ff[cnt], osal_mutex_create(&audio->rx_ff_mutex[cnt]));
+#endif
+ }
+#endif
+
+#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_FIFO_MUTEX
+ tu_fifo_config_mutex(&audio->int_ctr_ff, osal_mutex_create(&audio->int_ctr_ff_mutex));
+#endif
+#endif
+ }
+}
+
+void audiod_reset(uint8_t rhport)
+{
+ (void) rhport;
+
+ for(uint8_t i=0; i<CFG_TUD_AUDIO; i++)
+ {
+ 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++)
+ {
+ tu_fifo_clear(&audio->tx_ff[cnt]);
+ }
+#endif
+
+#if CFG_TUD_AUDIO_EPSIZE_OUT && CFG_TUD_AUDIO_RX_FIFO_SIZE
+ for (uint8_t cnt = 0; cnt < CFG_TUD_AUDIO_RX_FIFO_COUNT; cnt++)
+ {
+ tu_fifo_clear(&audio->rx_ff[cnt]);
+ }
+#endif
+ }
+}
+
+uint16_t audiod_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
+{
+ (void) max_len;
+
+ TU_VERIFY ( TUSB_CLASS_AUDIO == itf_desc->bInterfaceClass &&
+ AUDIO_SUBCLASS_CONTROL == itf_desc->bInterfaceSubClass);
+
+ // Verify version is correct - this check can be omitted
+ TU_VERIFY(itf_desc->bInterfaceProtocol == AUDIO_INT_PROTOCOL_CODE_V2);
+
+ // Verify interrupt control EP is enabled if demanded by descriptor - this should be best some static check however - this check can be omitted
+ if (itf_desc->bNumEndpoints == 1) // 0 or 1 EPs are allowed
+ {
+ TU_VERIFY(CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN > 0);
+ }
+
+ // Alternate setting MUST be zero - this check can be omitted
+ TU_VERIFY(itf_desc->bAlternateSetting == 0);
+
+ // Find available audio driver interface
+ uint8_t i;
+ for (i = 0; i < CFG_TUD_AUDIO; i++)
+ {
+ if (!_audiod_itf[i].p_desc)
+ {
+ _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;
+ break;
+ }
+ }
+
+ // Verify we found a free one
+ TU_ASSERT( i < CFG_TUD_AUDIO );
+
+ // This is all we need so far - the EPs are setup by a later set_interface request (as per UAC2 specification)
+ // 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
+
+ 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
+ uint8_t idxDriver, idxItf;
+ 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_LOG2(" Get itf: %u - current alt: %u\r\n", itf, _audiod_itf[idxDriver].altSetting[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)
+{
+ (void) rhport;
+
+ // Here we need to do the following:
+
+ // 1. Find the audio driver assigned to the given interface to be set
+ // Since one audio driver interface has to be able to cover an unknown number of interfaces (AC, AS + its alternate settings), the best memory efficient way to solve this is to always search through the descriptors.
+ // The audio driver is mapped to an audio function by a reference pointer to the corresponding AC interface of this audio function which serves as a starting point for searching
+
+ // 2. Close EPs which are currently open
+ // To do so it is not necessary to know the current active alternate interface since we already save the current EP addresses - we simply close them
+
+ // 3. Open new EP
+
+ uint8_t const itf = tu_u16_low(p_request->wIndex);
+ uint8_t const alt = tu_u16_low(p_request->wValue);
+
+ TU_LOG2(" Set itf: %u - alt: %u\r\n", itf, alt);
+
+ // Find index of audio streaming interface and index of interface
+ uint8_t idxDriver, idxItf;
+ uint8_t const *p_desc;
+ TU_VERIFY(audiod_get_AS_interface_index(itf, &idxDriver, &idxItf, &p_desc));
+
+ // Look if there is an EP to be closed - for this driver, there are only 3 possible EPs which may be closed (only AS related EPs can be closed, AC EP (if present) is always open)
+#if CFG_TUD_AUDIO_EPSIZE_IN > 0
+ if (_audiod_itf[idxDriver].ep_in_as_intf_num == itf)
+ {
+ _audiod_itf[idxDriver].ep_in_as_intf_num = 0;
+ usbd_edpt_close(rhport, _audiod_itf[idxDriver].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?
+ }
+#endif
+
+#if CFG_TUD_AUDIO_EPSIZE_OUT
+ if (_audiod_itf[idxDriver].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?
+
+ // 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?
+#endif
+ }
+#endif
+
+ // Save current alternative interface setting
+ _audiod_itf[idxDriver].altSetting[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;
+
+ // p_desc starts at required interface with alternate setting zero
+ while (p_desc < p_desc_end)
+ {
+ // Find correct interface
+ if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE && ((tusb_desc_interface_t const * )p_desc)->bInterfaceNumber == itf && ((tusb_desc_interface_t const * )p_desc)->bAlternateSetting == alt)
+ {
+ // From this point forward follow the EP descriptors associated to the current alternate setting interface - Open EPs if necessary
+ uint8_t foundEPs = 0, nEps = ((tusb_desc_interface_t const * )p_desc)->bNumEndpoints;
+ while (foundEPs < nEps && p_desc < p_desc_end)
+ {
+ if (tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT)
+ {
+ TU_ASSERT(usbd_edpt_open(rhport, (tusb_desc_endpoint_t const *)p_desc));
+
+ uint8_t ep_addr = ((tusb_desc_endpoint_t const *) p_desc)->bEndpointAddress;
+
+ // 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 (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;
+
+ // 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));
+ }
+#endif
+
+#if CFG_TUD_AUDIO_EPSIZE_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;
+
+ // 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 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;
+
+ // Invoke callback
+ if (tud_audio_set_itf_cb) TU_VERIFY(tud_audio_set_itf_cb(rhport, p_request));
+ }
+#endif
+
+#endif
+ foundEPs += 1;
+ }
+ p_desc = tu_desc_next(p_desc);
+ }
+
+ TU_VERIFY(foundEPs == nEps);
+
+ // We are done - abort loop
+ break;
+ }
+
+ // Moving forward
+ p_desc = tu_desc_next(p_desc);
+ }
+
+ tud_control_status(rhport, p_request);
+
+ return true;
+}
+
+// Invoked when class request DATA stage is finished.
+// return false to stall control EP (e.g Host send non-sense DATA)
+bool audiod_control_complete(uint8_t rhport, tusb_control_request_t const * p_request)
+{
+ // Handle audio class specific set requests
+ if(p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS && p_request->bmRequestType_bit.direction == TUSB_DIR_OUT)
+ {
+ uint8_t idxDriver;
+
+ switch (p_request->bmRequestType_bit.recipient)
+ {
+ case TUSB_REQ_RCPT_INTERFACE: ; // The semicolon is there to enable a declaration right after the label
+
+ uint8_t itf = TU_U16_LOW(p_request->wIndex);
+ uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
+
+ if (entityID != 0)
+ {
+ if (tud_audio_set_req_entity_cb)
+ {
+ // Check if entity is present and get corresponding driver index
+ TU_VERIFY(audiod_verify_entity_exists(itf, entityID, &idxDriver));
+
+ // Invoke callback
+ return tud_audio_set_req_entity_cb(rhport, p_request, _audiod_itf[idxDriver].ctrl_buf);
+ }
+ else
+ {
+ TU_LOG2(" No entity set request callback available!\r\n");
+ return false; // In case no callback function is present or request can not be conducted we stall it
+ }
+ }
+ else
+ {
+ if (tud_audio_set_req_itf_cb)
+ {
+ // Find index of audio driver structure and verify interface really exists
+ TU_VERIFY(audiod_verify_itf_exists(itf, &idxDriver));
+
+ // Invoke callback
+ return tud_audio_set_req_itf_cb(rhport, p_request, _audiod_itf[idxDriver].ctrl_buf);
+ }
+ else
+ {
+ TU_LOG2(" No interface set request callback available!\r\n");
+ return false; // In case no callback function is present or request can not be conducted we stall it
+ }
+ }
+
+ break;
+
+ case TUSB_REQ_RCPT_ENDPOINT: ; // The semicolon is there to enable a declaration right after the label
+
+ uint8_t ep = TU_U16_LOW(p_request->wIndex);
+
+ if (tud_audio_set_req_ep_cb)
+ {
+ // Check if entity is present and get corresponding driver index
+ TU_VERIFY(audiod_verify_ep_exists(ep, &idxDriver));
+
+ // Invoke callback
+ return tud_audio_set_req_ep_cb(rhport, p_request, _audiod_itf[idxDriver].ctrl_buf);
+ }
+ else
+ {
+ TU_LOG2(" No EP set request callback available!\r\n");
+ return false; // In case no callback function is present or request can not be conducted we stall it
+ }
+
+ // Unknown/Unsupported recipient
+ default: TU_BREAKPOINT(); return false;
+ }
+ }
+ return true;
+}
+
+// Handle class control request
+// return false to stall control endpoint (e.g unsupported request)
+bool audiod_control_request(uint8_t rhport, tusb_control_request_t const * p_request)
+{
+ (void) rhport;
+
+ // Handle standard requests - standard set requests usually have no data stage so we also handle set requests here
+ if (p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD)
+ {
+ switch (p_request->bRequest)
+ {
+ case TUSB_REQ_GET_INTERFACE:
+ return audiod_get_interface(rhport, p_request);
+
+ case TUSB_REQ_SET_INTERFACE:
+ return audiod_set_interface(rhport, p_request);
+
+ // Unknown/Unsupported request
+ default: TU_BREAKPOINT(); return false;
+ }
+ }
+
+ // Handle class requests
+ if (p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS)
+ {
+ uint8_t itf = TU_U16_LOW(p_request->wIndex);
+ uint8_t idxDriver;
+
+ // Conduct checks which depend on the recipient
+ switch (p_request->bmRequestType_bit.recipient)
+ {
+ case TUSB_REQ_RCPT_INTERFACE: ; // The semicolon is there to enable a declaration right after the label
+
+ uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
+
+ // Verify if entity is present
+ if (entityID != 0)
+ {
+ // Find index of audio driver structure and verify entity really exists
+ TU_VERIFY(audiod_verify_entity_exists(itf, entityID, &idxDriver));
+
+ // In case we got a get request invoke callback - callback needs to answer as defined in UAC2 specification page 89 - 5. Requests
+ if (p_request->bmRequestType_bit.direction == TUSB_DIR_IN)
+ {
+ if (tud_audio_get_req_entity_cb)
+ {
+ return tud_audio_get_req_entity_cb(rhport, p_request);
+ }
+ else
+ {
+ TU_LOG2(" No entity get request callback available!\r\n");
+ return false; // Stall
+ }
+ }
+ }
+ else
+ {
+ // Find index of audio driver structure and verify interface really exists
+ TU_VERIFY(audiod_verify_itf_exists(itf, &idxDriver));
+
+ // In case we got a get request invoke callback - callback needs to answer as defined in UAC2 specification page 89 - 5. Requests
+ if (p_request->bmRequestType_bit.direction == TUSB_DIR_IN)
+ {
+ if (tud_audio_get_req_itf_cb)
+ {
+ return tud_audio_get_req_itf_cb(rhport, p_request);
+ }
+ else
+ {
+ TU_LOG2(" No interface get request callback available!\r\n");
+ return false; // Stall
+ }
+ }
+ }
+ break;
+
+ case TUSB_REQ_RCPT_ENDPOINT: ; // The semicolon is there to enable a declaration right after the label
+
+ uint8_t ep = TU_U16_LOW(p_request->wIndex);
+
+ // Find index of audio driver structure and verify EP really exists
+ TU_VERIFY(audiod_verify_ep_exists(ep, &idxDriver));
+
+ // In case we got a get request invoke callback - callback needs to answer as defined in UAC2 specification page 89 - 5. Requests
+ if (p_request->bmRequestType_bit.direction == TUSB_DIR_IN)
+ {
+ if (tud_audio_get_req_ep_cb)
+ {
+ return tud_audio_get_req_ep_cb(rhport, p_request);
+ }
+ else
+ {
+ TU_LOG2(" No EP get request callback available!\r\n");
+ return false; // Stall
+ }
+ }
+ break;
+
+ // Unknown/Unsupported recipient
+ default: TU_LOG2(" Unsupported recipient: %d\r\n", p_request->bmRequestType_bit.recipient); TU_BREAKPOINT(); return false;
+ }
+
+ // If we end here, the received request is a set request - we schedule a receive for the data stage and return true here. We handle the rest later in audiod_control_complete() once the data stage was finished
+ TU_VERIFY(tud_control_xfer(rhport, p_request, _audiod_itf[idxDriver].ctrl_buf, CFG_TUD_AUDIO_CTRL_BUF_SIZE));
+ return true;
+ }
+
+ // There went something wrong - unsupported control request type
+ TU_BREAKPOINT();
+ return false;
+}
+
+bool audiod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
+{
+ (void) result;
+ (void) xferred_bytes;
+
+ // Search for interface belonging to given end point address and proceed as required
+ uint8_t idxDriver;
+ for (idxDriver = 0; idxDriver < CFG_TUD_AUDIO; idxDriver++)
+ {
+
+#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
+
+ // Data transmission of control interrupt finished
+ if (_audiod_itf[idxDriver].ep_int_ctr == ep_addr)
+ {
+ // According to USB2 specification, maximum payload of interrupt EP is 8 bytes on low speed, 64 bytes on full speed, and 1024 bytes on high speed (but only if an alternate interface other than 0 is used - see specification p. 49)
+ // In case there is nothing to send we have to return a NAK - this is taken care of by PHY ???
+ // In case of an erroneous transmission a retransmission is conducted - this is taken care of by PHY ???
+
+ // Load new data
+ uint16 *n_bytes_copied;
+ TU_VERIFY(audio_int_ctr_done_cb(rhport, &_audiod_itf[idxDriver], n_bytes_copied));
+
+ 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);
+ }
+ }
+
+#endif
+
+#if CFG_TUD_AUDIO_EPSIZE_IN
+
+ // Data transmission of audio packet finished
+ if (_audiod_itf[idxDriver].ep_in == ep_addr)
+ {
+ // USB 2.0, section 5.6.4, third paragraph, states "An isochronous endpoint must specify its required bus access period. However, an isochronous endpoint must be prepared to handle poll rates faster than the one specified."
+ // That paragraph goes on to say "An isochronous IN endpoint must return a zero-length packet whenever data is requested at a faster interval than the specified interval and data is not available."
+ // This can only be solved reliably if we load a ZLP after every IN transmission since we can not say if the host requests samples earlier than we declared! Once all samples are collected we overwrite the loaded ZLP.
+
+ // Check if there is data to load into EPs buffer - if not load it with ZLP
+ // Be aware - we as a device are not able to know if the host polls for data with a faster rate as we stated this in the descriptors. Therefore we always have to put something into the EPs buffer. However, once we did that, there is no way of aborting this or replacing what we put into the buffer before!
+ // This is the only place where we can fill something into the EPs buffer!
+
+ // Load new data
+ uint16_t n_bytes_copied;
+ TU_VERIFY(audiod_tx_done_cb(rhport, &_audiod_itf[idxDriver], &n_bytes_copied));
+
+ // Transmission of ZLP is done by audiod_tx_done_cb()
+ return true;
+ }
+#endif
+
+#if CFG_TUD_AUDIO_EPSIZE_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);
+
+ return true;
+ }
+
+
+#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+ // Transmission of feedback EP finished
+ if (_audiod_itf[idxDriver].ep_fb == ep_addr)
+ {
+ if (tud_audio_fb_done_cb) TU_VERIFY(tud_audio_fb_done_cb(rhport));
+
+ return audio_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)
+{
+ // Handles only sending of data not receiving
+ if (p_request->bmRequestType_bit.direction == TUSB_DIR_OUT) return false;
+
+ // Get corresponding driver index
+ uint8_t idxDriver;
+ uint8_t itf = TU_U16_LOW(p_request->wIndex);
+
+ // Conduct checks which depend on the recipient
+ switch (p_request->bmRequestType_bit.recipient)
+ {
+ case TUSB_REQ_RCPT_INTERFACE: ; // The semicolon is there to enable a declaration right after the label
+
+ uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
+
+ // Verify if entity is present
+ if (entityID != 0)
+ {
+ // Find index of audio driver structure and verify entity really exists
+ TU_VERIFY(audiod_verify_entity_exists(itf, entityID, &idxDriver));
+ }
+ else
+ {
+ // Find index of audio driver structure and verify interface really exists
+ TU_VERIFY(audiod_verify_itf_exists(itf, &idxDriver));
+ }
+ break;
+
+ case TUSB_REQ_RCPT_ENDPOINT: ; // The semicolon is there to enable a declaration right after the label
+
+ uint8_t ep = TU_U16_LOW(p_request->wIndex);
+
+ // Find index of audio driver structure and verify EP really exists
+ TU_VERIFY(audiod_verify_ep_exists(ep, &idxDriver));
+ break;
+
+ // Unknown/Unsupported recipient
+ default: TU_LOG2(" Unsupported recipient: %d\r\n", p_request->bmRequestType_bit.recipient); TU_BREAKPOINT(); return false;
+ }
+
+ // Crop length
+ if (len > CFG_TUD_AUDIO_CTRL_BUF_SIZE) len = CFG_TUD_AUDIO_CTRL_BUF_SIZE;
+
+ // Copy into buffer
+ memcpy((void *)_audiod_itf[idxDriver].ctrl_buf, data, (size_t)len);
+
+ // Schedule transmit
+ return tud_control_xfer(rhport, p_request, (void*)_audiod_itf[idxDriver].ctrl_buf, len);
+}
+
+// This helper function finds for a given AS interface number the index of the attached driver structure, the index of the interface in the audio function
+// (e.g. the std. AS interface with interface number 15 is the first AS interface for the given audio function and thus gets index zero), and
+// finally a pointer to the std. AS interface, where the pointer always points to the first alternate setting i.e. alternate interface zero.
+static bool audiod_get_AS_interface_index(uint8_t itf, uint8_t *idxDriver, uint8_t *idxItf, uint8_t const **pp_desc_int)
+{
+ // Loop over audio driver interfaces
+ uint8_t i;
+ for (i = 0; i < CFG_TUD_AUDIO; i++)
+ {
+ if (_audiod_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;
+
+ // Advance past AC descriptors
+ uint8_t const *p_desc = tu_desc_next(_audiod_itf[i].p_desc);
+ p_desc += ((audio_desc_cs_ac_interface_t const *)p_desc)->wTotalLength;
+
+ uint8_t tmp = 0;
+ while (p_desc < p_desc_end)
+ {
+ // We assume the number of alternate settings is increasing thus we return the index of alternate setting zero!
+ if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE && ((tusb_desc_interface_t const * )p_desc)->bInterfaceNumber == itf)
+ {
+ *idxItf = tmp;
+ *idxDriver = i;
+ *pp_desc_int = p_desc;
+ return true;
+ }
+
+ // Increase index, bytes read, and pointer
+ tmp++;
+ p_desc = tu_desc_next(p_desc);
+ }
+ }
+ }
+
+ return false;
+}
+
+// Verify an entity with the given ID exists and returns also the corresponding driver index
+static bool audiod_verify_entity_exists(uint8_t itf, uint8_t entityID, uint8_t *idxDriver)
+{
+ uint8_t i;
+ for (i = 0; i < CFG_TUD_AUDIO; i++)
+ {
+ // Look for the correct driver by checking if the unique standard AC interface number fits
+ if (_audiod_itf[i].p_desc && ((tusb_desc_interface_t const *)_audiod_itf[i].p_desc)->bInterfaceNumber == itf)
+ {
+ // Get pointers after class specific AC descriptors and end of AC descriptors - entities are defined in between
+ uint8_t const *p_desc = tu_desc_next(_audiod_itf[i].p_desc); // Points to CS AC descriptor
+ uint8_t const *p_desc_end = ((audio_desc_cs_ac_interface_t const *)p_desc)->wTotalLength + p_desc;
+ p_desc = tu_desc_next(p_desc); // Get past CS AC descriptor
+
+ while (p_desc < p_desc_end)
+ {
+ if (p_desc[3] == entityID) // Entity IDs are always at offset 3
+ {
+ *idxDriver = i;
+ return true;
+ }
+ p_desc = tu_desc_next(p_desc);
+ }
+ }
+ }
+ return false;
+}
+
+static bool audiod_verify_itf_exists(uint8_t itf, uint8_t *idxDriver)
+{
+ uint8_t i;
+ for (i = 0; i < CFG_TUD_AUDIO; i++)
+ {
+ if (_audiod_itf[i].p_desc)
+ {
+ // 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;
+
+ while (p_desc < p_desc_end)
+ {
+ if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE && ((tusb_desc_interface_t const *)_audiod_itf[i].p_desc)->bInterfaceNumber == itf)
+ {
+ *idxDriver = i;
+ return true;
+ }
+ p_desc = tu_desc_next(p_desc);
+ }
+ }
+ }
+ return false;
+}
+
+static bool audiod_verify_ep_exists(uint8_t ep, uint8_t *idxDriver)
+{
+ uint8_t i;
+ for (i = 0; i < CFG_TUD_AUDIO; i++)
+ {
+ 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];
+
+ // Advance past AC descriptors - EP we look for are streaming EPs
+ uint8_t const *p_desc = tu_desc_next(_audiod_itf[i].p_desc);
+ p_desc += ((audio_desc_cs_ac_interface_t const *)p_desc)->wTotalLength;
+
+ while (p_desc < p_desc_end)
+ {
+ if (tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT && ((tusb_desc_endpoint_t const * )p_desc)->bEndpointAddress == ep)
+ {
+ *idxDriver = i;
+ return true;
+ }
+ p_desc = tu_desc_next(p_desc);
+ }
+ }
+ }
+ return false;
+}
+
+#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+bool tud_audio_fb_set(uint8_t rhport, uint32_t feedback)
+{
+ audiod_interface_t *audio = &_audiod_itf[0];
+
+ audio->fb_val = feedback;
+ TU_VERIFY(!usbd_edpt_busy(rhport, audio->ep_fb), true);
+
+ return audio_fb_send(rhport, audio);
+}
+#endif
+
+#endif //TUSB_OPT_DEVICE_ENABLED && CFG_TUD_AUDIO
diff --git a/src/class/audio/audio_device.h b/src/class/audio/audio_device.h
new file mode 100644
index 000000000..d86232720
--- /dev/null
+++ b/src/class/audio/audio_device.h
@@ -0,0 +1,401 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Ha Thach (tinyusb.org)
+ * Copyright (c) 2020 Reinhard Panhuber
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#ifndef _TUSB_AUDIO_DEVICE_H_
+#define _TUSB_AUDIO_DEVICE_H_
+
+#include "assert.h"
+#include "common/tusb_common.h"
+#include "device/usbd.h"
+
+#include "audio.h"
+
+//--------------------------------------------------------------------+
+// 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
+#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
+#error You must define an audio class control request buffer size!
+#endif
+
+// Use of TX/RX FIFOs - If sizes are not zero, audio.c implements FIFOs for RX and TX (whatever defined).
+// For RX: the input stream gets decoded into its corresponding channels, where for each channel a FIFO is setup to hold its data -> see: audio_rx_done_cb().
+// For TX: the output stream is composed from CFG_TUD_AUDIO_N_CHANNELS_TX channels, where for each channel a FIFO is defined.
+// Further, it implements encoding and decoding of the individual channels (parameterized by the defines below).
+// If you don't use the FIFOs you need to handle encoding and decoding on your own in audio_rx_done_cb() and audio_tx_done_cb(). This, however, allows for optimizations.
+
+#ifndef CFG_TUD_AUDIO_TX_FIFO_SIZE
+#define CFG_TUD_AUDIO_TX_FIFO_SIZE 0 // Buffer size per channel
+#endif
+
+#ifndef CFG_TUD_AUDIO_RX_FIFO_SIZE
+#define CFG_TUD_AUDIO_RX_FIFO_SIZE 0 // Buffer size per channel
+#endif
+
+// End point sizes - Limits: Full Speed <= 1023, High Speed <= 1024
+#ifndef CFG_TUD_AUDIO_EPSIZE_IN
+#define CFG_TUD_AUDIO_EPSIZE_IN 0 // TX
+#endif
+
+#ifndef CFG_TUD_AUDIO_EPSIZE_OUT
+#define CFG_TUD_AUDIO_EPSIZE_OUT 0 // RX
+#endif
+
+#ifndef CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+#define CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP 0 // Feedback
+#endif
+
+#ifndef CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
+#define CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN 0 // Audio interrupt control
+#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)
+#endif
+#endif
+
+#ifndef CFG_TUD_AUDIO_N_CHANNELS_TX
+#define CFG_TUD_AUDIO_N_CHANNELS_TX 1
+#endif
+
+#ifndef CFG_TUD_AUDIO_N_CHANNELS_RX
+#define CFG_TUD_AUDIO_N_CHANNELS_RX 1
+#endif
+
+// Audio data format types
+#ifndef CFG_TUD_AUDIO_FORMAT_TYPE_TX
+#define CFG_TUD_AUDIO_FORMAT_TYPE_TX AUDIO_FORMAT_TYPE_UNDEFINED // If this option is used, an encoding function has to be implemented in audio_device.c
+#endif
+
+#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
+#endif
+
+// Audio data format type I specifications
+#if CFG_TUD_AUDIO_FORMAT_TYPE_TX == AUDIO_FORMAT_TYPE_I
+
+// 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
+#endif
+
+#ifndef CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX // bSubslotSize
+#define CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX 1
+#endif
+
+#ifndef CFG_TUD_AUDIO_TX_ITEMSIZE
+#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
+#endif
+#endif
+
+#if CFG_TUD_AUDIO_TX_ITEMSIZE < CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX
+#error FIFO element size (ITEMSIZE) must not be smaller then sample size
+#endif
+
+#endif
+
+#if CFG_TUD_AUDIO_FORMAT_TYPE_RX == AUDIO_FORMAT_TYPE_I
+
+#ifndef CFG_TUD_AUDIO_FORMAT_TYPE_I_RX
+#define CFG_TUD_AUDIO_FORMAT_TYPE_I_RX AUDIO_DATA_FORMAT_TYPE_I_PCM
+#endif
+
+#ifndef CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX // bSubslotSize
+#define CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX 1
+#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
+#endif
+
+#endif
+
+//static_assert(sizeof(tud_audio_desc_lengths) != CFG_TUD_AUDIO, "Supply audio function descriptor pack length!");
+
+// Supported types of this driver:
+// AUDIO_DATA_FORMAT_TYPE_I_PCM - Required definitions: CFG_TUD_AUDIO_N_CHANNELS and CFG_TUD_AUDIO_BYTES_PER_CHANNEL
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** \addtogroup AUDIO_Serial Serial
+ * @{
+ * \defgroup AUDIO_Serial_Device Device
+ * @{ */
+
+//--------------------------------------------------------------------+
+// Application API (Multiple Interfaces)
+// CFG_TUD_AUDIO > 1
+//--------------------------------------------------------------------+
+bool tud_audio_n_mounted (uint8_t itf);
+
+#if CFG_TUD_AUDIO_EPSIZE_OUT && CFG_TUD_AUDIO_RX_FIFO_SIZE
+#if CFG_TUD_AUDIO_RX_FIFO_COUNT > 1
+uint16_t tud_audio_n_available (uint8_t itf, uint8_t channelId);
+uint16_t tud_audio_n_read (uint8_t itf, uint8_t channelId, void* buffer, uint16_t bufsize);
+void tud_audio_n_read_flush (uint8_t itf, uint8_t channelId);
+#else
+uint16_t tud_audio_n_available (uint8_t itf);
+uint16_t tud_audio_n_read (uint8_t itf, void* buffer, uint16_t bufsize);
+void tud_audio_n_read_flush (uint8_t itf);
+#endif
+#endif
+
+/* This function is intended for later use once EP buffers (at least for ISO EPs) are implemented as ring buffers
+#if CFG_TUD_AUDIO_EPSIZE_IN && !CFG_TUD_AUDIO_TX_FIFO_SIZE
+uint16_t tud_audio_n_write_ep_in_buffer(uint8_t itf, const void * data, uint16_t len)
+#endif
+*/
+
+#ifndef CFG_TUD_AUDIO_TX_FIFO_COUNT
+#define CFG_TUD_AUDIO_TX_FIFO_COUNT 1
+#endif
+
+#if CFG_TUD_AUDIO_EPSIZE_IN && CFG_TUD_AUDIO_TX_FIFO_SIZE
+#if CFG_TUD_AUDIO_TX_FIFO_COUNT > 1
+uint16_t tud_audio_n_write (uint8_t itf, uint8_t channelId, const void * data, uint16_t len);
+#else
+uint16_t tud_audio_n_write (uint8_t itf, const void * data, uint16_t len);
+#endif
+uint16_t tud_audio_n_write_flush(uint8_t itf);
+#endif
+
+#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN > 0
+uint16_t tud_audio_int_ctr_n_available (uint8_t itf);
+uint16_t tud_audio_int_ctr_n_read (uint8_t itf, void* buffer, uint16_t bufsize);
+void tud_audio_int_ctr_n_read_flush (uint8_t itf);
+uint16_t tud_audio_int_ctr_n_write (uint8_t itf, uint8_t const* buffer, uint16_t bufsize);
+#endif
+
+//--------------------------------------------------------------------+
+// Application API (Interface0)
+//--------------------------------------------------------------------+
+
+static inline bool tud_audio_mounted (void);
+
+#if CFG_TUD_AUDIO_EPSIZE_OUT && CFG_TUD_AUDIO_RX_FIFO_SIZE
+static inline uint16_t tud_audio_available (void);
+static inline uint16_t tud_audio_read (void* buffer, uint16_t bufsize);
+static inline void tud_audio_read_flush (void);
+#endif
+
+#if CFG_TUD_AUDIO_EPSIZE_IN && CFG_TUD_AUDIO_TX_FIFO_SIZE
+#if CFG_TUD_AUDIO_TX_FIFO_COUNT > 1
+static inline uint16_t tud_audio_write (uint8_t channelId, uint8_t const* buffer, uint16_t bufsize);
+#else
+static inline uint16_t tud_audio_write (uint8_t const* buffer, uint16_t bufsize);
+#endif
+#endif
+
+#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN > 0
+static inline uint32_t tud_audio_int_ctr_available (void);
+static inline uint32_t tud_audio_int_ctr_read (void* buffer, uint32_t bufsize);
+static inline void tud_audio_int_ctr_read_flush (void);
+static inline uint32_t tud_audio_int_ctr_write (uint8_t const* buffer, uint32_t bufsize);
+#endif
+
+// Buffer control EP data and schedule a transmit
+// This function is intended to be used if you do not have a persistent buffer or memory location available (e.g. non-local variables) and need to answer onto a
+// get request. This function buffers your answer request frame into the control buffer of the corresponding audio driver and schedules a transmit for sending it.
+// Since transmission is triggered via interrupts, a persistent memory location is required onto which the buffer pointer in pointing. If you already have such
+// available you may directly use 'tud_control_xfer(...)'. In this case data does not need to be copied into an additional buffer and you save some time.
+// If the request's wLength is zero, a status packet is sent instead.
+bool tud_audio_buffer_and_schedule_control_xfer(uint8_t rhport, tusb_control_request_t const * p_request, void* data, uint16_t len);
+
+//--------------------------------------------------------------------+
+// Application Callback API (weak is optional)
+//--------------------------------------------------------------------+
+
+#if CFG_TUD_AUDIO_EPSIZE_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);
+#endif
+
+#if CFG_TUD_AUDIO_EPSIZE_OUT > 0 && 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);
+#endif
+
+#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
+TU_ATTR_WEAK bool tud_audio_int_ctr_done_cb(uint8_t rhport, uint16_t * n_bytes_copied);
+#endif
+
+// Invoked when audio set interface request received
+TU_ATTR_WEAK bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request);
+
+// Invoked when audio set interface request received which closes an EP
+TU_ATTR_WEAK bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const * p_request);
+
+// Invoked when audio class specific set request received for an EP
+TU_ATTR_WEAK bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff);
+
+// Invoked when audio class specific set request received for an interface
+TU_ATTR_WEAK bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff);
+
+// Invoked when audio class specific set request received for an entity
+TU_ATTR_WEAK bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff);
+
+// Invoked when audio class specific get request received for an EP
+TU_ATTR_WEAK bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_request);
+
+// Invoked when audio class specific get request received for an interface
+TU_ATTR_WEAK bool tud_audio_get_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request);
+
+// Invoked when audio class specific get request received for an entity
+TU_ATTR_WEAK bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const * p_request);
+
+//--------------------------------------------------------------------+
+// Inline Functions
+//--------------------------------------------------------------------+
+
+static inline bool tud_audio_mounted(void)
+{
+ return tud_audio_n_mounted(0);
+}
+
+#if CFG_TUD_AUDIO_EPSIZE_IN
+#if CFG_TUD_AUDIO_TX_FIFO_SIZE && CFG_TUD_AUDIO_TX_FIFO_COUNT > 1
+static inline uint16_t tud_audio_write (uint8_t channelId, uint8_t const* buffer, uint16_t n_bytes) // Short version if only one audio function is used
+{
+ return tud_audio_n_write(0, channelId, buffer, n_bytes);
+}
+#else
+static inline uint16_t tud_audio_write (uint8_t const* buffer, uint16_t n_bytes) // Short version if only one audio function is used
+{
+ return tud_audio_n_write(0, buffer, n_bytes);
+}
+#endif
+
+static inline uint16_t tud_audio_write_flush (void) // Short version if only one audio function is used
+{
+#if CFG_TUD_AUDIO_TX_FIFO_SIZE
+ return tud_audio_n_write_flush(0);
+#else
+ return 0;
+#endif
+}
+#endif // CFG_TUD_AUDIO_EPSIZE_IN && CFG_TUD_AUDIO_TX_FIFO_SIZE
+
+#if CFG_TUD_AUDIO_EPSIZE_OUT && CFG_TUD_AUDIO_RX_FIFO_SIZE
+#if CFG_TUD_AUDIO_RX_FIFO_COUNT > 1
+static inline uint16_t tud_audio_available(uint8_t channelId)
+{
+ return tud_audio_n_available(0, channelId);
+}
+
+static inline uint16_t tud_audio_read(uint8_t channelId, void* buffer, uint16_t bufsize)
+{
+ return tud_audio_n_read(0, channelId, buffer, bufsize);
+}
+
+static inline void tud_audio_read_flush(uint8_t channelId)
+{
+ tud_audio_n_read_flush(0, channelId);
+}
+#else
+static inline uint16_t tud_audio_available(void)
+{
+ return tud_audio_n_available(0);
+}
+
+static inline uint16_t tud_audio_read(void *buffer, uint16_t bufsize)
+{
+ return tud_audio_n_read(0, buffer, bufsize);
+}
+
+static inline void tud_audio_read_flush(void)
+{
+ tud_audio_n_read_flush(0);
+}
+#endif
+#endif
+
+#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN > 0
+static inline uint16_t tud_audio_int_ctr_available(void)
+{
+ return tud_audio_int_ctr_n_available(0);
+}
+
+static inline uint16_t tud_audio_int_ctr_read(void* buffer, uint16_t bufsize)
+{
+ return tud_audio_int_ctr_n_read(0, buffer, bufsize);
+}
+
+static inline void tud_audio_int_ctr_read_flush(void)
+{
+ return tud_audio_int_ctr_n_read_flush(0);
+}
+
+static inline uint16_t tud_audio_int_ctr_write(uint8_t const* buffer, uint16_t bufsize)
+{
+ return tud_audio_int_ctr_n_write(0, buffer, bufsize);
+}
+#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_request (uint8_t rhport, tusb_control_request_t const * request);
+bool audiod_control_complete (uint8_t rhport, tusb_control_request_t const * request);
+bool audiod_xfer_cb (uint8_t rhport, uint8_t edpt_addr, xfer_result_t result, uint32_t xferred_bytes);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _TUSB_AUDIO_DEVICE_H_ */
+
+/** @} */
+/** @} */
diff --git a/src/class/cdc/cdc_device.c b/src/class/cdc/cdc_device.c
index b4ebfc92e..e39adc5fa 100644
--- a/src/class/cdc/cdc_device.c
+++ b/src/class/cdc/cdc_device.c
@@ -73,18 +73,29 @@ typedef struct
//--------------------------------------------------------------------+
CFG_TUSB_MEM_SECTION static cdcd_interface_t _cdcd_itf[CFG_TUD_CDC];
-static void _prep_out_transaction (uint8_t itf)
+static void _prep_out_transaction (cdcd_interface_t* p_cdc)
{
- cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
-
- // skip if previous transfer not complete
- if ( usbd_edpt_busy(TUD_OPT_RHPORT, p_cdc->ep_out) ) return;
+ uint8_t const rhport = TUD_OPT_RHPORT;
+ uint16_t available = tu_fifo_remaining(&p_cdc->rx_ff);
// Prepare for incoming data but only allow what we can store in the ring buffer.
- uint16_t max_read = tu_fifo_remaining(&p_cdc->rx_ff);
- if ( max_read >= sizeof(p_cdc->epout_buf) )
+ // TODO Actually we can still carry out the transfer, keeping count of received bytes
+ // and slowly move it to the FIFO when read().
+ // This pre-check reduces endpoint claiming
+ TU_VERIFY(available >= sizeof(p_cdc->epout_buf), );
+
+ // claim endpoint
+ TU_VERIFY(usbd_edpt_claim(rhport, p_cdc->ep_out), );
+
+ // fifo can be changed before endpoint is claimed
+ available = tu_fifo_remaining(&p_cdc->rx_ff);
+
+ if ( available >= sizeof(p_cdc->epout_buf) ) {
+ usbd_edpt_xfer(rhport, p_cdc->ep_out, p_cdc->epout_buf, sizeof(p_cdc->epout_buf));
+ }else
{
- usbd_edpt_xfer(TUD_OPT_RHPORT, p_cdc->ep_out, p_cdc->epout_buf, sizeof(p_cdc->epout_buf));
+ // Release endpoint since we don't make any transfer
+ usbd_edpt_release(rhport, p_cdc->ep_out);
}
}
@@ -123,8 +134,9 @@ uint32_t tud_cdc_n_available(uint8_t itf)
uint32_t tud_cdc_n_read(uint8_t itf, void* buffer, uint32_t bufsize)
{
- uint32_t num_read = tu_fifo_read_n(&_cdcd_itf[itf].rx_ff, buffer, bufsize);
- _prep_out_transaction(itf);
+ cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
+ uint32_t num_read = tu_fifo_read_n(&p_cdc->rx_ff, buffer, bufsize);
+ _prep_out_transaction(p_cdc);
return num_read;
}
@@ -135,8 +147,9 @@ bool tud_cdc_n_peek(uint8_t itf, int pos, uint8_t* chr)
void tud_cdc_n_read_flush (uint8_t itf)
{
- tu_fifo_clear(&_cdcd_itf[itf].rx_ff);
- _prep_out_transaction(itf);
+ cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
+ tu_fifo_clear(&p_cdc->rx_ff);
+ _prep_out_transaction(p_cdc);
}
//--------------------------------------------------------------------+
@@ -144,11 +157,12 @@ void tud_cdc_n_read_flush (uint8_t itf)
//--------------------------------------------------------------------+
uint32_t tud_cdc_n_write(uint8_t itf, void const* buffer, uint32_t bufsize)
{
- uint16_t ret = tu_fifo_write_n(&_cdcd_itf[itf].tx_ff, buffer, bufsize);
+ cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
+ uint16_t ret = tu_fifo_write_n(&p_cdc->tx_ff, buffer, bufsize);
#if 0 // TODO issue with circuitpython's REPL
// flush if queue more than endpoint size
- if ( tu_fifo_count(&_cdcd_itf[itf].tx_ff) >= CFG_TUD_CDC_EP_BUFSIZE )
+ if ( tu_fifo_count(&p_cdc->tx_ff) >= CFG_TUD_CDC_EP_BUFSIZE )
{
tud_cdc_n_write_flush(itf);
}
@@ -161,17 +175,28 @@ uint32_t tud_cdc_n_write_flush (uint8_t itf)
{
cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
- // skip if previous transfer not complete yet
- TU_VERIFY( !usbd_edpt_busy(TUD_OPT_RHPORT, p_cdc->ep_in), 0 );
+ // No data to send
+ if ( !tu_fifo_count(&p_cdc->tx_ff) ) return 0;
+
+ uint8_t const rhport = TUD_OPT_RHPORT;
+
+ // Claim the endpoint
+ TU_VERIFY( usbd_edpt_claim(rhport, p_cdc->ep_in), 0 );
- uint16_t count = tu_fifo_read_n(&p_cdc->tx_ff, p_cdc->epin_buf, sizeof(p_cdc->epin_buf));
- if ( count )
+ // Pull data from FIFO
+ uint16_t const count = tu_fifo_read_n(&p_cdc->tx_ff, p_cdc->epin_buf, sizeof(p_cdc->epin_buf));
+
+ if ( count && tud_cdc_n_connected(itf) )
+ {
+ TU_ASSERT( usbd_edpt_xfer(rhport, p_cdc->ep_in, p_cdc->epin_buf, count), 0 );
+ return count;
+ }else
{
- TU_VERIFY( tud_cdc_n_connected(itf), 0 ); // fifo is empty if not connected
- TU_ASSERT( usbd_edpt_xfer(TUD_OPT_RHPORT, p_cdc->ep_in, p_cdc->epin_buf, count), 0 );
+ // Release endpoint since we don't make any transfer
+ // Note: data is dropped if terminal is not connected
+ usbd_edpt_release(rhport, p_cdc->ep_in);
+ return 0;
}
-
- return count;
}
uint32_t tud_cdc_n_write_available (uint8_t itf)
@@ -194,7 +219,7 @@ void cdcd_init(void)
p_cdc->wanted_char = -1;
// default line coding is : stop bit = 1, parity = none, data bits = 8
- p_cdc->line_coding.bit_rate = 115200;
+ p_cdc->line_coding.bit_rate = 115200;
p_cdc->line_coding.stop_bits = 0;
p_cdc->line_coding.parity = 0;
p_cdc->line_coding.data_bits = 8;
@@ -233,8 +258,7 @@ uint16_t cdcd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint1
// Find available interface
cdcd_interface_t * p_cdc = NULL;
- uint8_t cdc_id;
- for(cdc_id=0; cdc_id<CFG_TUD_CDC; cdc_id++)
+ for(uint8_t cdc_id=0; cdc_id<CFG_TUD_CDC; cdc_id++)
{
if ( _cdcd_itf[cdc_id].ep_in == 0 )
{
@@ -283,7 +307,7 @@ uint16_t cdcd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint1
}
// Prepare for incoming data
- _prep_out_transaction(cdc_id);
+ _prep_out_transaction(p_cdc);
return drv_len;
}
@@ -408,7 +432,7 @@ bool cdcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_
if (tud_cdc_rx_cb && tu_fifo_count(&p_cdc->rx_ff) ) tud_cdc_rx_cb(itf);
// prepare for OUT transaction
- _prep_out_transaction(itf);
+ _prep_out_transaction(p_cdc);
}
// Data sent to host, we continue to fetch from tx fifo to send.
@@ -421,12 +445,15 @@ bool cdcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_
if ( 0 == tud_cdc_n_write_flush(itf) )
{
- // There is no data left, a ZLP should be sent if
+ // If there is no data left, a ZLP should be sent if
// xferred_bytes is multiple of EP size and not zero
// FIXME CFG_TUD_CDC_EP_BUFSIZE is not Endpoint packet size
- if ( xferred_bytes && (0 == (xferred_bytes % CFG_TUD_CDC_EP_BUFSIZE)) )
+ if ( !tu_fifo_count(&p_cdc->tx_ff) && xferred_bytes && (0 == (xferred_bytes % CFG_TUD_CDC_EP_BUFSIZE)) )
{
- usbd_edpt_xfer(rhport, p_cdc->ep_in, NULL, 0);
+ if ( usbd_edpt_claim(rhport, p_cdc->ep_in) )
+ {
+ usbd_edpt_xfer(rhport, p_cdc->ep_in, NULL, 0);
+ }
}
}
}
diff --git a/src/class/cdc/cdc_device.h b/src/class/cdc/cdc_device.h
index 145381c42..3c679c48e 100644
--- a/src/class/cdc/cdc_device.h
+++ b/src/class/cdc/cdc_device.h
@@ -92,7 +92,7 @@ uint32_t tud_cdc_n_write (uint8_t itf, void const* buffer, uint32_t bu
static inline
uint32_t tud_cdc_n_write_char (uint8_t itf, char ch);
-// Write a nul-terminated string
+// Write a null-terminated string
static inline
uint32_t tud_cdc_n_write_str (uint8_t itf, char const* str);
diff --git a/src/class/hid/hid_device.c b/src/class/hid/hid_device.c
index c46fc591a..11da5f220 100644
--- a/src/class/hid/hid_device.c
+++ b/src/class/hid/hid_device.c
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
@@ -57,33 +57,34 @@ typedef struct
CFG_TUSB_MEM_SECTION static hidd_interface_t _hidd_itf[CFG_TUD_HID];
/*------------- Helpers -------------*/
-static inline hidd_interface_t* get_interface_by_itfnum(uint8_t itf_num)
+static inline uint8_t get_index_by_itfnum(uint8_t itf_num)
{
- for (uint8_t i=0; i < CFG_TUD_HID; i++ )
- {
- if ( itf_num == _hidd_itf[i].itf_num ) return &_hidd_itf[i];
- }
+ for (uint8_t i=0; i < CFG_TUD_HID; i++ )
+ {
+ if ( itf_num == _hidd_itf[i].itf_num ) return i;
+ }
- return NULL;
+ return 0xFF;
}
//--------------------------------------------------------------------+
// APPLICATION API
//--------------------------------------------------------------------+
-bool tud_hid_ready(void)
+bool tud_hid_n_ready(uint8_t itf)
{
- uint8_t itf = 0;
uint8_t const ep_in = _hidd_itf[itf].ep_in;
- return tud_ready() && (ep_in != 0) && usbd_edpt_ready(TUD_OPT_RHPORT, ep_in);
+ return tud_ready() && (ep_in != 0) && !usbd_edpt_busy(TUD_OPT_RHPORT, ep_in);
}
-bool tud_hid_report(uint8_t report_id, void const* report, uint8_t len)
+bool tud_hid_n_report(uint8_t itf, uint8_t report_id, void const* report, uint8_t len)
{
- TU_VERIFY( tud_hid_ready() );
-
- uint8_t itf = 0;
+ uint8_t const rhport = 0;
hidd_interface_t * p_hid = &_hidd_itf[itf];
+ // claim endpoint
+ TU_VERIFY( usbd_edpt_claim(rhport, p_hid->ep_in) );
+
+ // prepare data
if (report_id)
{
len = tu_min8(len, CFG_TUD_HID_EP_BUFSIZE-1);
@@ -101,16 +102,15 @@ bool tud_hid_report(uint8_t report_id, void const* report, uint8_t len)
return usbd_edpt_xfer(TUD_OPT_RHPORT, p_hid->ep_in, p_hid->epin_buf, len);
}
-bool tud_hid_boot_mode(void)
+bool tud_hid_n_boot_mode(uint8_t itf)
{
- uint8_t itf = 0;
return _hidd_itf[itf].boot_mode;
}
//--------------------------------------------------------------------+
// KEYBOARD API
//--------------------------------------------------------------------+
-bool tud_hid_keyboard_report(uint8_t report_id, uint8_t modifier, uint8_t keycode[6])
+bool tud_hid_n_keyboard_report(uint8_t itf, uint8_t report_id, uint8_t modifier, uint8_t keycode[6])
{
hid_keyboard_report_t report;
@@ -124,13 +124,13 @@ bool tud_hid_keyboard_report(uint8_t report_id, uint8_t modifier, uint8_t keycod
tu_memclr(report.keycode, 6);
}
- return tud_hid_report(report_id, &report, sizeof(report));
+ return tud_hid_n_report(itf, report_id, &report, sizeof(report));
}
//--------------------------------------------------------------------+
// MOUSE APPLICATION API
//--------------------------------------------------------------------+
-bool tud_hid_mouse_report(uint8_t report_id, uint8_t buttons, int8_t x, int8_t y, int8_t vertical, int8_t horizontal)
+bool tud_hid_n_mouse_report(uint8_t itf, uint8_t report_id, uint8_t buttons, int8_t x, int8_t y, int8_t vertical, int8_t horizontal)
{
hid_mouse_report_t report =
{
@@ -141,7 +141,7 @@ bool tud_hid_mouse_report(uint8_t report_id, uint8_t buttons, int8_t x, int8_t y
.pan = horizontal
};
- return tud_hid_report(report_id, &report, sizeof(report));
+ return tud_hid_n_report(itf, report_id, &report, sizeof(report));
}
//--------------------------------------------------------------------+
@@ -194,7 +194,7 @@ uint16_t hidd_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, uint1
p_hid->boot_mode = false; // default mode is REPORT
p_hid->itf_num = desc_itf->bInterfaceNumber;
-
+
// Use offsetof to avoid pointer to the odd/misaligned address
memcpy(&p_hid->report_desc_len, (uint8_t*) p_hid->hid_descriptor + offsetof(tusb_hid_descriptor_hid_t, wReportLength), 2);
@@ -217,8 +217,10 @@ bool hidd_control_request(uint8_t rhport, tusb_control_request_t const * request
{
TU_VERIFY(request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE);
- hidd_interface_t* p_hid = get_interface_by_itfnum( (uint8_t) request->wIndex );
- TU_ASSERT(p_hid);
+ uint8_t const hid_itf = get_index_by_itfnum((uint8_t) request->wIndex);
+ TU_VERIFY(hid_itf < CFG_TUD_HID);
+
+ hidd_interface_t* p_hid = &_hidd_itf[hid_itf];
if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD)
{
@@ -234,7 +236,11 @@ bool hidd_control_request(uint8_t rhport, tusb_control_request_t const * request
}
else if (request->bRequest == TUSB_REQ_GET_DESCRIPTOR && desc_type == HID_DESC_TYPE_REPORT)
{
- uint8_t const * desc_report = tud_hid_descriptor_report_cb();
+ uint8_t const * desc_report = tud_hid_descriptor_report_cb(
+ #if CFG_TUD_HID > 1
+ hid_itf // TODO for backward compatible callback, remove later when appropriate
+ #endif
+ );
tud_control_xfer(rhport, request, (void*) desc_report, p_hid->report_desc_len);
}
else
@@ -253,7 +259,12 @@ bool hidd_control_request(uint8_t rhport, tusb_control_request_t const * request
uint8_t const report_type = tu_u16_high(request->wValue);
uint8_t const report_id = tu_u16_low(request->wValue);
- uint16_t xferlen = tud_hid_get_report_cb(report_id, (hid_report_type_t) report_type, p_hid->epin_buf, request->wLength);
+ uint16_t xferlen = tud_hid_get_report_cb(
+ #if CFG_TUD_HID > 1
+ hid_itf, // TODO for backward compatible callback, remove later when appropriate
+ #endif
+ report_id, (hid_report_type_t) report_type, p_hid->epin_buf, request->wLength
+ );
TU_ASSERT( xferlen > 0 );
tud_control_xfer(rhport, request, p_hid->epin_buf, xferlen);
@@ -270,7 +281,12 @@ bool hidd_control_request(uint8_t rhport, tusb_control_request_t const * request
if ( tud_hid_set_idle_cb )
{
// stall request if callback return false
- if ( !tud_hid_set_idle_cb(p_hid->idle_rate) ) return false;
+ TU_VERIFY( tud_hid_set_idle_cb(
+ #if CFG_TUD_HID > 1
+ hid_itf, // TODO for backward compatible callback, remove later when appropriate
+ #endif
+ p_hid->idle_rate)
+ );
}
tud_control_status(rhport, request);
@@ -291,7 +307,15 @@ bool hidd_control_request(uint8_t rhport, tusb_control_request_t const * request
case HID_REQ_CONTROL_SET_PROTOCOL:
p_hid->boot_mode = 1 - request->wValue; // 0 is Boot, 1 is Report protocol
- if (tud_hid_boot_mode_cb) tud_hid_boot_mode_cb(p_hid->boot_mode);
+ if (tud_hid_boot_mode_cb)
+ {
+ tud_hid_boot_mode_cb(
+ #if CFG_TUD_HID > 1
+ hid_itf, // TODO for backward compatible callback, remove later when appropriate
+ #endif
+ p_hid->boot_mode
+ );
+ }
tud_control_status(rhport, request);
break;
@@ -311,8 +335,11 @@ bool hidd_control_request(uint8_t rhport, tusb_control_request_t const * request
bool hidd_control_complete(uint8_t rhport, tusb_control_request_t const * p_request)
{
(void) rhport;
- hidd_interface_t* p_hid = get_interface_by_itfnum( (uint8_t) p_request->wIndex );
- TU_ASSERT(p_hid);
+
+ uint8_t const hid_itf = get_index_by_itfnum((uint8_t) p_request->wIndex);
+ TU_VERIFY(hid_itf < CFG_TUD_HID);
+
+ hidd_interface_t* p_hid = &_hidd_itf[hid_itf];
if (p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS &&
p_request->bRequest == HID_REQ_CONTROL_SET_REPORT)
@@ -321,7 +348,12 @@ bool hidd_control_complete(uint8_t rhport, tusb_control_request_t const * p_requ
uint8_t const report_type = tu_u16_high(p_request->wValue);
uint8_t const report_id = tu_u16_low(p_request->wValue);
- tud_hid_set_report_cb(report_id, (hid_report_type_t) report_type, p_hid->epout_buf, p_request->wLength);
+ tud_hid_set_report_cb(
+ #if CFG_TUD_HID > 1
+ hid_itf, // TODO for backward compatible callback, remove later when appropriate
+ #endif
+ report_id, (hid_report_type_t) report_type, p_hid->epout_buf, p_request->wLength
+ );
}
return true;
@@ -343,7 +375,12 @@ bool hidd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_
if (ep_addr == p_hid->ep_out)
{
- tud_hid_set_report_cb(0, HID_REPORT_TYPE_INVALID, p_hid->epout_buf, xferred_bytes);
+ tud_hid_set_report_cb(
+ #if CFG_TUD_HID > 1
+ itf, // TODO for backward compatible callback, remove later when appropriate
+ #endif
+ 0, HID_REPORT_TYPE_INVALID, p_hid->epout_buf, xferred_bytes
+ );
TU_ASSERT(usbd_edpt_xfer(rhport, p_hid->ep_out, p_hid->epout_buf, sizeof(p_hid->epout_buf)));
}
diff --git a/src/class/hid/hid_device.h b/src/class/hid/hid_device.h
index f7ad38bab..d5b5b7296 100644
--- a/src/class/hid/hid_device.h
+++ b/src/class/hid/hid_device.h
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
@@ -50,51 +50,104 @@
#endif
//--------------------------------------------------------------------+
-// Application API
+// Application API (Multiple Ports)
+// CFG_TUD_HID > 1
//--------------------------------------------------------------------+
// Check if the interface is ready to use
-bool tud_hid_ready(void);
+bool tud_hid_n_ready(uint8_t itf);
// Check if current mode is Boot (true) or Report (false)
-bool tud_hid_boot_mode(void);
+bool tud_hid_n_boot_mode(uint8_t itf);
// Send report to host
-bool tud_hid_report(uint8_t report_id, void const* report, uint8_t len);
+bool tud_hid_n_report(uint8_t itf, uint8_t report_id, void const* report, uint8_t len);
// KEYBOARD: convenient helper to send keyboard report if application
// use template layout report as defined by hid_keyboard_report_t
-bool tud_hid_keyboard_report(uint8_t report_id, uint8_t modifier, uint8_t keycode[6]);
+bool tud_hid_n_keyboard_report(uint8_t itf, uint8_t report_id, uint8_t modifier, uint8_t keycode[6]);
// MOUSE: convenient helper to send mouse report if application
// use template layout report as defined by hid_mouse_report_t
-bool tud_hid_mouse_report(uint8_t report_id, uint8_t buttons, int8_t x, int8_t y, int8_t vertical, int8_t horizontal);
+bool tud_hid_n_mouse_report(uint8_t itf, uint8_t report_id, uint8_t buttons, int8_t x, int8_t y, int8_t vertical, int8_t horizontal);
+
+//--------------------------------------------------------------------+
+// Application API (Single Port)
+//--------------------------------------------------------------------+
+static inline bool tud_hid_ready(void);
+static inline bool tud_hid_boot_mode(void);
+static inline bool tud_hid_report(uint8_t report_id, void const* report, uint8_t len);
+static inline bool tud_hid_keyboard_report(uint8_t report_id, uint8_t modifier, uint8_t keycode[6]);
+static inline bool tud_hid_mouse_report(uint8_t report_id, uint8_t buttons, int8_t x, int8_t y, int8_t vertical, int8_t horizontal);
//--------------------------------------------------------------------+
// Callbacks (Weak is optional)
//--------------------------------------------------------------------+
+#if CFG_TUD_HID > 1
+
// Invoked when received GET HID REPORT DESCRIPTOR request
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
-uint8_t const * tud_hid_descriptor_report_cb(void);
+uint8_t const * tud_hid_descriptor_report_cb(uint8_t itf);
// Invoked when received GET_REPORT control request
// Application must fill buffer report's content and return its length.
// Return zero will cause the stack to STALL request
-uint16_t tud_hid_get_report_cb(uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen);
+uint16_t tud_hid_get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen);
// Invoked when received SET_REPORT control request or
// received data on OUT endpoint ( Report ID = 0, Type = 0 )
-void tud_hid_set_report_cb(uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize);
+void tud_hid_set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize);
// Invoked when received SET_PROTOCOL request ( mode switch Boot <-> Report )
-TU_ATTR_WEAK void tud_hid_boot_mode_cb(uint8_t boot_mode);
+TU_ATTR_WEAK void tud_hid_boot_mode_cb(uint8_t itf, uint8_t boot_mode);
// Invoked when received SET_IDLE request. return false will stall the request
// - Idle Rate = 0 : only send report if there is changes, i.e skip duplication
// - Idle Rate > 0 : skip duplication, but send at least 1 report every idle rate (in unit of 4 ms).
+TU_ATTR_WEAK bool tud_hid_set_idle_cb(uint8_t itf, uint8_t idle_rate);
+
+#else
+
+// TODO for backward compatible callback, remove later when appropriate
+uint8_t const * tud_hid_descriptor_report_cb(void);
+uint16_t tud_hid_get_report_cb(uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen);
+void tud_hid_set_report_cb(uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize);
+
+TU_ATTR_WEAK void tud_hid_boot_mode_cb(uint8_t boot_mode);
TU_ATTR_WEAK bool tud_hid_set_idle_cb(uint8_t idle_rate);
+#endif
+
+
+//--------------------------------------------------------------------+
+// Inline Functions
+//--------------------------------------------------------------------+
+static inline bool tud_hid_ready(void)
+{
+ return tud_hid_n_ready(0);
+}
+
+static inline bool tud_hid_boot_mode(void)
+{
+ return tud_hid_n_boot_mode(0);
+}
+
+static inline bool tud_hid_report(uint8_t report_id, void const* report, uint8_t len)
+{
+ return tud_hid_n_report(0, report_id, report, len);
+}
+
+static inline bool tud_hid_keyboard_report(uint8_t report_id, uint8_t modifier, uint8_t keycode[6])
+{
+ return tud_hid_n_keyboard_report(0, report_id, modifier, keycode);
+}
+
+static inline bool tud_hid_mouse_report(uint8_t report_id, uint8_t buttons, int8_t x, int8_t y, int8_t vertical, int8_t horizontal)
+{
+ return tud_hid_n_mouse_report(0, report_id, buttons, x, y, vertical, horizontal);
+}
+
/* --------------------------------------------------------------------+
* HID Report Descriptor Template
*
@@ -264,7 +317,7 @@ TU_ATTR_WEAK bool tud_hid_set_idle_cb(uint8_t idle_rate);
HID_LOGICAL_MAX ( 1 ) ,\
HID_REPORT_COUNT ( 16 ) ,\
HID_REPORT_SIZE ( 1 ) ,\
- HID_INPUT ( HID_DATA | HID_ARRAY | HID_ABSOLUTE ) ,\
+ HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\
/* X, Y, Z, Rz (min -127, max 127 ) */ \
HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\
HID_LOGICAL_MIN ( 0x81 ) ,\
@@ -318,4 +371,3 @@ bool hidd_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t e
#endif
#endif /* _TUSB_HID_DEVICE_H_ */
-
diff --git a/src/class/midi/midi_device.c b/src/class/midi/midi_device.c
index 376b3670a..a07acf0b8 100644
--- a/src/class/midi/midi_device.c
+++ b/src/class/midi/midi_device.c
@@ -153,15 +153,25 @@ void midi_rx_done_cb(midid_interface_t* midi, uint8_t const* buffer, uint32_t bu
static uint32_t write_flush(midid_interface_t* midi)
{
+ // No data to send
+ if ( !tu_fifo_count(&midi->tx_ff) ) return 0;
+
+ uint8_t const rhport = TUD_OPT_RHPORT;
+
// skip if previous transfer not complete
- TU_VERIFY( !usbd_edpt_busy(TUD_OPT_RHPORT, midi->ep_in) );
+ TU_VERIFY( usbd_edpt_claim(rhport, midi->ep_in), 0 );
uint16_t count = tu_fifo_read_n(&midi->tx_ff, midi->epin_buf, CFG_TUD_MIDI_EP_BUFSIZE);
if (count > 0)
{
- TU_ASSERT( usbd_edpt_xfer(TUD_OPT_RHPORT, midi->ep_in, midi->epin_buf, count) );
+ TU_ASSERT( usbd_edpt_xfer(rhport, midi->ep_in, midi->epin_buf, count), 0 );
+ return count;
+ }else
+ {
+ // Release endpoint since we don't make any transfer
+ usbd_edpt_release(rhport, midi->ep_in);
+ return 0;
}
- return count;
}
uint32_t tud_midi_n_write(uint8_t itf, uint8_t jack_id, uint8_t const* buffer, uint32_t bufsize)
@@ -183,7 +193,7 @@ uint32_t tud_midi_n_write(uint8_t itf, uint8_t jack_id, uint8_t const* buffer, u
if (data == 0xf7) {
midi->write_buffer[0] = 0x5;
} else {
- midi->write_buffer_length = 4;
+ midi->write_target_length = 4;
}
} else if ((msg >= 0x8 && msg <= 0xB) || msg == 0xE) {
midi->write_buffer[0] = jack_id << 4 | msg;
@@ -290,9 +300,9 @@ void midid_reset(uint8_t rhport)
uint16_t midid_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, uint16_t max_len)
{
// 1st Interface is Audio Control v1
- TU_VERIFY(TUSB_CLASS_AUDIO == desc_itf->bInterfaceClass &&
- AUDIO_SUBCLASS_CONTROL == desc_itf->bInterfaceSubClass &&
- AUDIO_PROTOCOL_V1 == desc_itf->bInterfaceProtocol, 0);
+ TU_VERIFY(TUSB_CLASS_AUDIO == desc_itf->bInterfaceClass &&
+ AUDIO_SUBCLASS_CONTROL == desc_itf->bInterfaceSubClass &&
+ AUDIO_FUNC_PROTOCOL_CODE_UNDEF == desc_itf->bInterfaceProtocol, 0);
uint16_t drv_len = tu_desc_len(desc_itf);
uint8_t const * p_desc = tu_desc_next(desc_itf);
@@ -308,9 +318,9 @@ uint16_t midid_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, uint
TU_VERIFY(TUSB_DESC_INTERFACE == tu_desc_type(p_desc), 0);
tusb_desc_interface_t const * desc_midi = (tusb_desc_interface_t const *) p_desc;
- TU_VERIFY(TUSB_CLASS_AUDIO == desc_midi->bInterfaceClass &&
- AUDIO_SUBCLASS_MIDI_STREAMING == desc_midi->bInterfaceSubClass &&
- AUDIO_PROTOCOL_V1 == desc_midi->bInterfaceProtocol, 0);
+ TU_VERIFY(TUSB_CLASS_AUDIO == desc_midi->bInterfaceClass &&
+ AUDIO_SUBCLASS_MIDI_STREAMING == desc_midi->bInterfaceSubClass &&
+ AUDIO_FUNC_PROTOCOL_CODE_UNDEF == desc_midi->bInterfaceProtocol, 0);
// Find available interface
midid_interface_t * p_midi = NULL;
@@ -405,17 +415,22 @@ bool midid_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32
if (tud_midi_rx_cb) tud_midi_rx_cb(itf);
// prepare for next
+ // TODO for now ep_out is not used by public API therefore there is no race condition,
+ // and does not need to claim like ep_in
TU_ASSERT(usbd_edpt_xfer(rhport, p_midi->ep_out, p_midi->epout_buf, CFG_TUD_MIDI_EP_BUFSIZE), false);
}
else if ( ep_addr == p_midi->ep_in )
{
if (0 == write_flush(p_midi))
{
- // There is no data left, a ZLP should be sent if
+ // If there is no data left, a ZLP should be sent if
// xferred_bytes is multiple of EP size and not zero
- if ( xferred_bytes && (0 == (xferred_bytes % CFG_TUD_MIDI_EP_BUFSIZE)) )
+ if ( !tu_fifo_count(&p_midi->tx_ff) && xferred_bytes && (0 == (xferred_bytes % CFG_TUD_MIDI_EP_BUFSIZE)) )
{
- usbd_edpt_xfer(rhport, p_midi->ep_in, NULL, 0);
+ if ( usbd_edpt_claim(rhport, p_midi->ep_in) )
+ {
+ usbd_edpt_xfer(rhport, p_midi->ep_in, NULL, 0);
+ }
}
}
}
diff --git a/src/common/tusb_common.h b/src/common/tusb_common.h
index d95c0ffc4..15892fa33 100644
--- a/src/common/tusb_common.h
+++ b/src/common/tusb_common.h
@@ -47,10 +47,10 @@
#define U16_TO_U8S_BE(u16) TU_U16_HIGH(u16), TU_U16_LOW(u16)
#define U16_TO_U8S_LE(u16) TU_U16_LOW(u16), TU_U16_HIGH(u16)
-#define U32_B1_U8(u32) ((uint8_t) (((u32) >> 24) & 0x000000ff)) // MSB
-#define U32_B2_U8(u32) ((uint8_t) (((u32) >> 16) & 0x000000ff))
-#define U32_B3_U8(u32) ((uint8_t) (((u32) >> 8) & 0x000000ff))
-#define U32_B4_U8(u32) ((uint8_t) ((u32) & 0x000000ff)) // LSB
+#define U32_B1_U8(u32) ((uint8_t) ((((uint32_t) u32) >> 24) & 0x000000ff)) // MSB
+#define U32_B2_U8(u32) ((uint8_t) ((((uint32_t) u32) >> 16) & 0x000000ff))
+#define U32_B3_U8(u32) ((uint8_t) ((((uint32_t) u32) >> 8) & 0x000000ff))
+#define U32_B4_U8(u32) ((uint8_t) (((uint32_t) u32) & 0x000000ff)) // LSB
#define U32_TO_U8S_BE(u32) U32_B1_U8(u32), U32_B2_U8(u32), U32_B3_U8(u32), U32_B4_U8(u32)
#define U32_TO_U8S_LE(u32) U32_B4_U8(u32), U32_B3_U8(u32), U32_B2_U8(u32), U32_B1_U8(u32)
diff --git a/src/common/tusb_compiler.h b/src/common/tusb_compiler.h
index 6f6aa3c39..505542078 100644
--- a/src/common/tusb_compiler.h
+++ b/src/common/tusb_compiler.h
@@ -44,8 +44,10 @@
#endif
// Compile-time Assert
-#if __STDC_VERSION__ >= 201112L
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
#define TU_VERIFY_STATIC _Static_assert
+#elif defined (__cplusplus) && __cplusplus >= 201103L
+ #define TU_VERIFY_STATIC static_assert
#else
#define TU_VERIFY_STATIC(const_expr, _mess) enum { TU_XSTRCAT(_verify_static_, _TU_COUNTER_) = 1/(!!(const_expr)) }
#endif
diff --git a/src/common/tusb_fifo.c b/src/common/tusb_fifo.c
index 6ab158cca..f41cf9f6c 100644
--- a/src/common/tusb_fifo.c
+++ b/src/common/tusb_fifo.c
@@ -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
@@ -57,6 +58,8 @@ static void tu_fifo_unlock(tu_fifo_t *f)
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);
f->buffer = (uint8_t*) buffer;
@@ -64,55 +67,328 @@ bool tu_fifo_config(tu_fifo_t *f, void* buffer, uint16_t depth, uint16_t item_si
f->item_size = item_size;
f->overwritable = overwritable;
- f->rd_idx = f->wr_idx = f->count = 0;
+ f->max_pointer_idx = 2*depth - 1; // Limit index space to 2*depth - this allows for a fast "modulo" calculation but limits the maximum depth to 2^16/2 = 2^15 and buffer overflows are detectable only if overflow happens once (important for unsupervised DMA applications)
+ f->non_used_index_space = 0xFFFF - f->max_pointer_idx;
+
+ f->rd_idx = f->wr_idx = 0;
tu_fifo_unlock(f);
return true;
}
+// Static functions are intended to work on local variables
+
static inline uint16_t _ff_mod(uint16_t idx, uint16_t depth)
{
- return (idx < depth) ? idx : (idx-depth);
+ while ( idx >= depth) idx -= depth;
+ return idx;
}
-// retrieve data from fifo
-static inline void _ff_pull(tu_fifo_t* f, void * buffer, uint16_t n)
+// send one item to FIFO WITHOUT updating write pointer
+static inline void _ff_push(tu_fifo_t* f, void const * data, uint16_t wRel)
{
- memcpy(buffer,
- f->buffer + (f->rd_idx * f->item_size),
- f->item_size*n);
+ memcpy(f->buffer + (wRel * f->item_size), data, f->item_size);
+}
+
+// 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)
+{
+ if(wRel + n <= f->depth) // Linear mode only
+ {
+ memcpy(f->buffer + (wRel * f->item_size), data, n*f->item_size);
+ }
+ else // Wrap around
+ {
+ uint16_t nLin = f->depth - wRel;
- f->rd_idx = _ff_mod(f->rd_idx + n, f->depth);
- f->count -= n;
+ // Write data to linear part of buffer
+ memcpy(f->buffer + (wRel * f->item_size), data, nLin*f->item_size);
+
+ // Write data wrapped around
+ memcpy(f->buffer, data + nLin*f->item_size, (n - nLin) * f->item_size);
+ }
}
-// send data to fifo
-static inline void _ff_push(tu_fifo_t* f, void const * data, uint16_t n)
+// get one item from FIFO WITHOUT updating read pointer
+static inline void _ff_pull(tu_fifo_t* f, void * p_buffer, uint16_t rRel)
{
- memcpy(f->buffer + (f->wr_idx * f->item_size),
- data,
- f->item_size*n);
+ memcpy(p_buffer, f->buffer + (rRel * f->item_size), f->item_size);
+}
- f->wr_idx = _ff_mod(f->wr_idx + n, f->depth);
+// 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)
+{
+ 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 nLin = f->depth - rRel;
- if (tu_fifo_full(f))
+ // Read data from linear part of buffer
+ memcpy(p_buffer, f->buffer + (rRel * f->item_size), nLin*f->item_size);
+
+ // Read data wrapped part
+ memcpy(p_buffer + nLin*f->item_size, f->buffer, (n - nLin) * f->item_size);
+ }
+}
+
+// Advance an absolute pointer
+static uint16_t advance_pointer(tu_fifo_t* f, uint16_t p, uint16_t offset)
+{
+ // We limit the index space of p such that a correct wrap around happens
+ // Check for a wrap around or if we are in unused index space - This has to be checked first!! We are exploiting the wrap around to the correct index
+ if ((p > p + offset) || (p + offset > f->max_pointer_idx))
{
- f->rd_idx = f->wr_idx; // keep the full state (rd == wr && count = depth)
+ p = (p + offset) + f->non_used_index_space;
}
else
{
- f->count += n;
+ p += offset;
}
+ return p;
+}
+
+// Backward an absolute pointer
+static uint16_t backward_pointer(tu_fifo_t* f, uint16_t p, uint16_t offset)
+{
+ // We limit the index space of p such that a correct wrap around happens
+ // Check for a wrap around or if we are in unused index space - This has to be checked first!! We are exploiting the wrap around to the correct index
+ if ((p < p - offset) || (p - offset > f->max_pointer_idx))
+ {
+ p = (p - offset) - f->non_used_index_space;
+ }
+ else
+ {
+ p -= offset;
+ }
+ return p;
+}
+
+// get relative from absolute pointer
+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
+static inline uint16_t _tu_fifo_count(tu_fifo_t* f, uint16_t wAbs, uint16_t rAbs)
+{
+ uint16_t cnt = wAbs-rAbs;
+
+ // In case we have non-power of two depth we need a further modification
+ if (rAbs > wAbs) cnt -= f->non_used_index_space;
+
+ return cnt;
+}
+
+// Works on local copies of w and r
+static inline bool _tu_fifo_empty(uint16_t wAbs, uint16_t rAbs)
+{
+ return wAbs == rAbs;
+}
+
+// Works on local copies of w and r
+static inline bool _tu_fifo_full(tu_fifo_t* f, uint16_t wAbs, uint16_t rAbs)
+{
+ return (_tu_fifo_count(f, wAbs, rAbs) == f->depth);
+}
+
+// Works on local copies of w and r
+// 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
+// use DMAs, write functions do not allow such an error.
+static inline bool _tu_fifo_overflowed(tu_fifo_t* f, uint16_t wAbs, uint16_t rAbs)
+{
+ return (_tu_fifo_count(f, wAbs, rAbs) > f->depth);
+}
+
+// Works on local copies of w
+// For more details see _tu_fifo_overflow()!
+static inline void _tu_fifo_correct_read_pointer(tu_fifo_t* f, uint16_t wAbs)
+{
+ f->rd_idx = backward_pointer(f, wAbs, f->depth);
+}
+
+// Works on local copies of w and r
+// Must be protected by mutexes since in case of an overflow read pointer gets modified
+static bool _tu_fifo_peek_at(tu_fifo_t* f, uint16_t offset, void * p_buffer, uint16_t wAbs, uint16_t rAbs)
+{
+ uint16_t cnt = _tu_fifo_count(f, wAbs, rAbs);
+
+ // Check overflow and correct if required
+ if (cnt > f->depth)
+ {
+ _tu_fifo_correct_read_pointer(f, wAbs);
+ cnt = f->depth;
+ }
+
+ // Skip beginning of buffer
+ if (cnt == 0 || offset >= cnt) return false;
+
+ uint16_t rRel = get_relative_pointer(f, rAbs, offset);
+
+ // Peek data
+ _ff_pull(f, p_buffer, rRel);
+
+ return true;
+}
+
+// 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)
+{
+ uint16_t cnt = _tu_fifo_count(f, wAbs, rAbs);
+
+ // Check overflow and correct if required
+ if (cnt > f->depth)
+ {
+ _tu_fifo_correct_read_pointer(f, wAbs);
+ rAbs = 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) {
+ if (cnt == 0) return 0;
+ n = cnt;
+ }
+
+ uint16_t rRel = get_relative_pointer(f, rAbs, offset);
+
+ // Peek data
+ _ff_pull_n(f, p_buffer, n, rRel);
+
+ return n;
+}
+
+// Works on local copies of w and r
+static inline uint16_t _tu_fifo_remaining(tu_fifo_t* f, uint16_t wAbs, uint16_t rAbs)
+{
+ return f->depth - _tu_fifo_count(f, wAbs, rAbs);
+}
+
+/******************************************************************************/
+/*!
+ @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.
+
+ @param[in] f
+ Pointer to the FIFO buffer to manipulate
+
+ @returns Number of items in FIFO
+ */
+/******************************************************************************/
+uint16_t tu_fifo_count(tu_fifo_t* f)
+{
+ return _tu_fifo_count(f, f->wr_idx, f->rd_idx);
+}
+
+/******************************************************************************/
+/*!
+ @brief Check if FIFO is empty.
+
+ As this function only reads the read and write pointers once, this function is
+ reentrant and thus thread and ISR save without any mutexes.
+
+ @param[in] f
+ Pointer to the FIFO buffer to manipulate
+
+ @returns Number of items in FIFO
+ */
+/******************************************************************************/
+bool tu_fifo_empty(tu_fifo_t* f)
+{
+ return _tu_fifo_empty(f->wr_idx, f->rd_idx);
+}
+
+/******************************************************************************/
+/*!
+ @brief Check if FIFO is full.
+
+ As this function only reads the read and write pointers once, this function is
+ reentrant and thus thread and ISR save without any mutexes.
+
+ @param[in] f
+ Pointer to the FIFO buffer to manipulate
+
+ @returns Number of items in FIFO
+ */
+/******************************************************************************/
+bool tu_fifo_full(tu_fifo_t* f)
+{
+ return _tu_fifo_full(f, f->wr_idx, f->rd_idx);
}
/******************************************************************************/
/*!
- @brief Read one element out of the RX buffer.
+ @brief Get remaining space 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.
+
+ @param[in] f
+ Pointer to the FIFO buffer to manipulate
+
+ @returns Number of items in FIFO
+ */
+/******************************************************************************/
+uint16_t tu_fifo_remaining(tu_fifo_t* f)
+{
+ return _tu_fifo_remaining(f, f->wr_idx, f->rd_idx);
+}
+
+/******************************************************************************/
+/*!
+ @brief Check if overflow happened.
+
+ 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
+ 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
+ that latest items are read.
+ If required (e.g. for DMA use) you can also correct the read pointer by
+ tu_fifo_correct_read_pointer().
+
+ @param[in] f
+ Pointer to the FIFO buffer to manipulate
+
+ @returns True if overflow happened
+ */
+/******************************************************************************/
+bool tu_fifo_overflowed(tu_fifo_t* f)
+{
+ return _tu_fifo_overflowed(f, f->wr_idx, f->rd_idx);
+}
+
+// Only use in case tu_fifo_overflow() returned true!
+void tu_fifo_correct_read_pointer(tu_fifo_t* f)
+{
+ tu_fifo_lock(f);
+ _tu_fifo_correct_read_pointer(f, f->wr_idx);
+ tu_fifo_unlock(f);
+}
+
+/******************************************************************************/
+/*!
+ @brief Read one element out of the buffer.
This function will return the element located at the array index of the
- read pointer, and then increment the read pointer index. If the read
- pointer exceeds the maximum buffer size, it will roll over to zero.
+ read pointer, and then increment the read pointer index.
+ This function checks for an overflow and corrects read pointer if required.
@param[in] f
Pointer to the FIFO buffer to manipulate
@@ -120,26 +396,27 @@ static inline void _ff_push(tu_fifo_t* f, void const * data, uint16_t n)
Pointer to the place holder for data read from the buffer
@returns TRUE if the queue is not empty
-*/
+ */
/******************************************************************************/
bool tu_fifo_read(tu_fifo_t* f, void * buffer)
{
- if( tu_fifo_empty(f) ) return false;
+ tu_fifo_lock(f); // TODO: Here we may distinguish for read and write pointer mutexes!
- tu_fifo_lock(f);
+ // 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
- _ff_pull(f, buffer, 1);
+ // Advance pointer
+ f->rd_idx = advance_pointer(f, f->rd_idx, ret);
tu_fifo_unlock(f);
-
- return true;
+ return ret;
}
/******************************************************************************/
/*!
@brief This function will read n elements from the array index specified by
- the read pointer and increment the read index. If the read index
- exceeds the max buffer size, then it will roll over to zero.
+ the read pointer and increment the read index.
+ This function checks for an overflow and corrects read pointer if required.
@param[in] f
Pointer to the FIFO buffer to manipulate
@@ -149,76 +426,76 @@ bool tu_fifo_read(tu_fifo_t* f, void * buffer)
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 count)
{
- if(tu_fifo_empty(f)) return 0;
-
- tu_fifo_lock(f);
+ tu_fifo_lock(f); // TODO: Here we may distinguish for read and write pointer mutexes!
- // Limit up to fifo's count
- if(count > f->count) count = f->count;
+ // 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
- if(count + f->rd_idx <= f->depth)
- {
- _ff_pull(f, buffer, count);
- }
- else
- {
- uint16_t const part1 = f->depth - f->rd_idx;
-
- // Part 1: from rd_idx to end
- _ff_pull(f, buffer, part1);
- buffer = ((uint8_t*) buffer) + part1*f->item_size;
-
- // Part 2: start to remaining
- _ff_pull(f, buffer, count-part1);
- }
+ // Advance read pointer
+ f->rd_idx = advance_pointer(f, f->rd_idx, count);
tu_fifo_unlock(f);
-
return count;
}
/******************************************************************************/
/*!
- @brief Read one item without removing it from the FIFO
+ @brief Read one item without removing it from the FIFO.
+ This function checks for an overflow and corrects read pointer if required.
@param[in] f
Pointer to the FIFO buffer to manipulate
- @param[in] pos
- Position to read from in the FIFO buffer
+ @param[in] offset
+ Position to read from in the FIFO buffer with respect to read pointer
@param[in] p_buffer
Pointer to the place holder for data read from the buffer
@returns TRUE if the queue is not empty
-*/
+ */
/******************************************************************************/
-bool tu_fifo_peek_at(tu_fifo_t* f, uint16_t pos, void * p_buffer)
+bool tu_fifo_peek_at(tu_fifo_t* f, uint16_t offset, void * p_buffer)
{
- if ( pos >= f->count ) return false;
+ tu_fifo_lock(f); // TODO: Here we may distinguish for read and write pointer mutexes!
+ bool ret = _tu_fifo_peek_at(f, offset, p_buffer, f->wr_idx, f->rd_idx);
+ tu_fifo_unlock(f);
+ return ret;
+}
- tu_fifo_lock(f);
+/******************************************************************************/
+/*!
+ @brief Read n items without removing it from the FIFO
+ This function checks for an overflow and corrects read pointer if required.
- // rd_idx is pos=0
- uint16_t index = _ff_mod(f->rd_idx + pos, f->depth);
- memcpy(p_buffer,
- f->buffer + (index * f->item_size),
- f->item_size);
+ @param[in] f
+ Pointer to the FIFO buffer to manipulate
+ @param[in] offset
+ Position to read from in the FIFO buffer with respect to read pointer
+ @param[in] p_buffer
+ Pointer to the place holder for data read from the buffer
+ @param[in] n
+ Number of items to peek
+ @returns Number of bytes written to 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);
-
- return true;
+ return ret;
}
/******************************************************************************/
/*!
- @brief Write one element into the RX buffer.
+ @brief Write one element into the buffer.
This function will write one element into the array index specified by
- the write pointer and increment the write index. If the write index
- exceeds the max buffer size, then it will roll over to zero.
+ the write pointer and increment the write index.
@param[in] f
Pointer to the FIFO buffer to manipulate
@@ -227,15 +504,23 @@ bool tu_fifo_peek_at(tu_fifo_t* f, uint16_t pos, void * p_buffer)
@returns TRUE if the data was written to the FIFO (overwrittable
FIFO will always return TRUE)
-*/
+ */
/******************************************************************************/
-bool tu_fifo_write (tu_fifo_t* f, const void * data)
+bool tu_fifo_write(tu_fifo_t* f, const void * data)
{
- if ( tu_fifo_full(f) && !f->overwritable ) return false;
-
tu_fifo_lock(f);
- _ff_push(f, data, 1);
+ uint16_t w = f->wr_idx;
+
+ if ( _tu_fifo_full(f, w, f->rd_idx) && !f->overwritable ) return false;
+
+ uint16_t wRel = get_relative_pointer(f, w, 0);
+
+ // Write data
+ _ff_push(f, data, wRel);
+
+ // Advance pointer
+ f->wr_idx = advance_pointer(f, w, 1);
tu_fifo_unlock(f);
@@ -245,8 +530,7 @@ bool tu_fifo_write (tu_fifo_t* f, const void * data)
/******************************************************************************/
/*!
@brief This function will write n elements into the array index specified by
- the write pointer and increment the write index. If the write index
- exceeds the max buffer size, then it will roll over to zero.
+ the write pointer and increment the write index.
@param[in] f
Pointer to the FIFO buffer to manipulate
@@ -255,47 +539,42 @@ bool tu_fifo_write (tu_fifo_t* f, const void * data)
@param[in] count
Number of element
@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 count)
{
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));
+ 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;
- f->wr_idx = 0;
- f->rd_idx = 0;
- f->count = 0;
- }
- if (count + f->wr_idx <= f->depth )
- {
- _ff_push(f, buf8, count);
+ // 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;
}
- else
- {
- uint16_t const part1 = f->depth - f->wr_idx;
- // Part 1: from wr_idx to end
- _ff_push(f, buf8, part1);
- buf8 += part1*f->item_size;
+ 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);
- // Part 2: start to remaining
- _ff_push(f, buf8, count-part1);
- }
-
tu_fifo_unlock(f);
return count;
@@ -303,19 +582,59 @@ uint16_t tu_fifo_write_n (tu_fifo_t* f, const void * data, uint16_t count)
/******************************************************************************/
/*!
- @brief Clear the fifo read and write pointers and set length to zero
+ @brief Clear the fifo read and write pointers
@param[in] f
Pointer to the FIFO buffer to manipulate
-*/
+ */
/******************************************************************************/
bool tu_fifo_clear(tu_fifo_t *f)
{
tu_fifo_lock(f);
-
- f->rd_idx = f->wr_idx = f->count = 0;
-
+ f->rd_idx = f->wr_idx = 0;
tu_fifo_unlock(f);
return true;
}
+
+/******************************************************************************/
+/*!
+ @brief Advance write pointer - intended to be used in combination with DMA.
+ It is possible to fill the FIFO by use of a DMA in circular mode. Within
+ DMA ISRs you may update the write pointer to be able to read from the FIFO.
+ As long as the DMA is the only process writing into the FIFO this is safe
+ to use.
+
+ USE WITH CARE - WE DO NOT CONDUCT SAFTY CHECKS HERE!
+
+ @param[in] f
+ Pointer to the FIFO buffer to manipulate
+ @param[in] n
+ Number of items the write pointer moves forward
+ */
+/******************************************************************************/
+void tu_fifo_advance_write_pointer(tu_fifo_t *f, uint16_t n)
+{
+ f->wr_idx = advance_pointer(f, f->wr_idx, n);
+}
+
+/******************************************************************************/
+/*!
+ @brief Advance read pointer - intended to be used in combination with DMA.
+ It is possible to read from the FIFO by use of a DMA in linear mode. Within
+ DMA ISRs you may update the read pointer to be able to again write into the
+ FIFO. As long as the DMA is the only process reading from the FIFO this is
+ safe to use.
+
+ USE WITH CARE - WE DO NOT CONDUCT SAFTY CHECKS HERE!
+
+ @param[in] f
+ Pointer to the FIFO buffer to manipulate
+ @param[in] n
+ Number of items the read pointer moves forward
+ */
+/******************************************************************************/
+void tu_fifo_advance_read_pointer(tu_fifo_t *f, uint16_t n)
+{
+ f->rd_idx = advance_pointer(f, f->rd_idx, n);
+}
diff --git a/src/common/tusb_fifo.h b/src/common/tusb_fifo.h
index fb0c896f3..b87695743 100644
--- a/src/common/tusb_fifo.h
+++ b/src/common/tusb_fifo.h
@@ -31,6 +31,15 @@
#ifndef _TUSB_FIFO_H_
#define _TUSB_FIFO_H_
+// Due to the use of unmasked pointers, this FIFO does not suffer from loosing
+// one item slice. Furthermore, write and read operations are completely
+// decoupled as write and read functions do not modify a common state. Henceforth,
+// writing or reading from the FIFO within an ISR is safe as long as no other
+// process (thread or ISR) interferes.
+// Also, this FIFO is ready to be used in combination with a DMA as the write and
+// read pointers can be updated from within a DMA ISR. Overflows are detectable
+// within a certain number (see tu_fifo_overflow()).
+
// mutex is only needed for RTOS
// for OS None, we don't get preempted
#define CFG_FIFO_MUTEX (CFG_TUSB_OS != OPT_OS_NONE)
@@ -39,7 +48,7 @@
#include <stdbool.h>
#ifdef __cplusplus
- extern "C" {
+extern "C" {
#endif
#if CFG_FIFO_MUTEX
@@ -52,14 +61,16 @@
*/
typedef struct
{
- uint8_t* buffer ; ///< buffer pointer
- uint16_t depth ; ///< max items
- uint16_t item_size ; ///< size of each item
- bool overwritable ;
+ uint8_t* buffer ; ///< buffer pointer
+ uint16_t depth ; ///< max items
+ uint16_t item_size ; ///< size of each item
+ bool overwritable ;
+
+ 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 count ; ///< number of items in queue
- volatile uint16_t wr_idx ; ///< write pointer
- volatile uint16_t rd_idx ; ///< read pointer
+ volatile uint16_t wr_idx ; ///< write pointer
+ volatile uint16_t rd_idx ; ///< read pointer
#if CFG_FIFO_MUTEX
tu_fifo_mutex_t mutex;
@@ -67,14 +78,16 @@ typedef struct
} tu_fifo_t;
-#define TU_FIFO_DEF(_name, _depth, _type, _overwritable) \
- uint8_t _name##_buf[_depth*sizeof(_type)]; \
- tu_fifo_t _name = { \
- .buffer = _name##_buf, \
- .depth = _depth, \
- .item_size = sizeof(_type), \
- .overwritable = _overwritable, \
- }
+#define TU_FIFO_DEF(_name, _depth, _type, _overwritable) \
+ uint8_t _name##_buf[_depth*sizeof(_type)]; \
+ tu_fifo_t _name = { \
+ .buffer = _name##_buf, \
+ .depth = _depth, \
+ .item_size = sizeof(_type), \
+ .overwritable = _overwritable, \
+ .max_pointer_idx = 2*_depth-1, \
+ .non_used_index_space = 0xFFFF - 2*_depth-1, \
+ }
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);
@@ -86,37 +99,30 @@ static inline void tu_fifo_config_mutex(tu_fifo_t *f, tu_fifo_mutex_t 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);
+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);
-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);
+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);
-bool tu_fifo_peek_at (tu_fifo_t* f, uint16_t pos, void * p_buffer);
+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);
-static inline bool tu_fifo_peek(tu_fifo_t* f, void * p_buffer)
-{
- return tu_fifo_peek_at(f, 0, p_buffer);
-}
+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);
+void tu_fifo_correct_read_pointer (tu_fifo_t* f);
-static inline bool tu_fifo_empty(tu_fifo_t* f)
-{
- return (f->count == 0);
-}
+// Pointer modifications intended to be used in combinations with DMAs.
+// USE WITH CARE - NO SAFTY CHECKS CONDUCTED HERE! NOT MUTEX PROTECTED!
+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);
-static inline bool tu_fifo_full(tu_fifo_t* f)
-{
- return (f->count == f->depth);
-}
-
-static inline uint16_t tu_fifo_count(tu_fifo_t* f)
-{
- return f->count;
-}
-
-static inline uint16_t tu_fifo_remaining(tu_fifo_t* f)
+static inline bool tu_fifo_peek(tu_fifo_t* f, void * p_buffer)
{
- return f->depth - f->count;
+ return tu_fifo_peek_at(f, 0, p_buffer);
}
static inline uint16_t tu_fifo_depth(tu_fifo_t* f)
@@ -125,7 +131,7 @@ static inline uint16_t tu_fifo_depth(tu_fifo_t* f)
}
#ifdef __cplusplus
- }
+}
#endif
#endif /* _TUSB_FIFO_H_ */
diff --git a/src/common/tusb_verify.h b/src/common/tusb_verify.h
index 406f5e6ee..8b724aef8 100644
--- a/src/common/tusb_verify.h
+++ b/src/common/tusb_verify.h
@@ -76,8 +76,8 @@
#if CFG_TUSB_DEBUG
#include <stdio.h>
- #define _MESS_ERR(_err) printf("%s %d: failed, error = %s\r\n", __func__, __LINE__, tusb_strerr[_err])
- #define _MESS_FAILED() printf("%s %d: assert failed\r\n", __func__, __LINE__)
+ #define _MESS_ERR(_err) tu_printf("%s %d: failed, error = %s\r\n", __func__, __LINE__, tusb_strerr[_err])
+ #define _MESS_FAILED() tu_printf("%s %d: assert failed\r\n", __func__, __LINE__)
#else
#define _MESS_ERR(_err) do {} while (0)
#define _MESS_FAILED() do {} while (0)
@@ -142,7 +142,9 @@
#define ASSERT_1ARGS(_cond) TU_VERIFY_DEFINE(_cond, _MESS_FAILED(); TU_BREAKPOINT(), false)
#define ASSERT_2ARGS(_cond, _ret) TU_VERIFY_DEFINE(_cond, _MESS_FAILED(); TU_BREAKPOINT(), _ret)
+#ifndef TU_ASSERT
#define TU_ASSERT(...) GET_3RD_ARG(__VA_ARGS__, ASSERT_2ARGS, ASSERT_1ARGS,UNUSED)(__VA_ARGS__)
+#endif
// TODO remove TU_ASSERT_ERR() later
@@ -163,10 +165,12 @@
/* ASSERT Error
* basically TU_VERIFY Error with TU_BREAKPOINT() as handler
*------------------------------------------------------------------*/
-#define ASERT_ERR_1ARGS(_error) TU_VERIFY_ERR_DEF2(_error, TU_BREAKPOINT())
-#define ASERT_ERR_2ARGS(_error, _ret) TU_VERIFY_ERR_DEF3(_error, TU_BREAKPOINT(), _ret)
+#define ASSERT_ERR_1ARGS(_error) TU_VERIFY_ERR_DEF2(_error, TU_BREAKPOINT())
+#define ASSERT_ERR_2ARGS(_error, _ret) TU_VERIFY_ERR_DEF3(_error, TU_BREAKPOINT(), _ret)
-#define TU_ASSERT_ERR(...) GET_3RD_ARG(__VA_ARGS__, ASERT_ERR_2ARGS, ASERT_ERR_1ARGS,UNUSED)(__VA_ARGS__)
+#ifndef TU_ASSERT_ERR
+#define TU_ASSERT_ERR(...) GET_3RD_ARG(__VA_ARGS__, ASSERT_ERR_2ARGS, ASSERT_ERR_1ARGS,UNUSED)(__VA_ARGS__)
+#endif
/*------------------------------------------------------------------*/
/* ASSERT HDLR
diff --git a/src/device/usbd.c b/src/device/usbd.c
index 6b91048b6..90edc3dde 100644
--- a/src/device/usbd.c
+++ b/src/device/usbd.c
@@ -37,6 +37,10 @@
#define CFG_TUD_TASK_QUEUE_SZ 16
#endif
+#ifndef CFG_TUD_EP_MAX
+#define CFG_TUD_EP_MAX 9
+#endif
+
//--------------------------------------------------------------------+
// Device Data
//--------------------------------------------------------------------+
@@ -46,7 +50,6 @@ typedef struct
{
volatile uint8_t connected : 1;
volatile uint8_t addressed : 1;
- volatile uint8_t configured : 1;
volatile uint8_t suspended : 1;
uint8_t remote_wakeup_en : 1; // enable/disable by host
@@ -54,18 +57,21 @@ typedef struct
uint8_t self_powered : 1; // configuration descriptor's attribute
};
+ volatile uint8_t cfg_num; // current active configuration (0x00 is not configured)
uint8_t speed;
uint8_t itf2drv[16]; // map interface number to driver (0xff is invalid)
- uint8_t ep2drv[8][2]; // map endpoint to driver ( 0xff is invalid )
+ uint8_t ep2drv[CFG_TUD_EP_MAX][2]; // map endpoint to driver ( 0xff is invalid )
struct TU_ATTR_PACKED
{
volatile bool busy : 1;
volatile bool stalled : 1;
+ volatile bool claimed : 1;
// TODO merge ep2drv here, 4-bit should be sufficient
- }ep_status[8][2];
+ }ep_status[CFG_TUD_EP_MAX][2];
+
}usbd_device_t;
static usbd_device_t _usbd_dev;
@@ -124,6 +130,19 @@ static usbd_class_driver_t const _usbd_driver[] =
},
#endif
+#if CFG_TUD_AUDIO
+{
+ DRIVER_NAME("AUDIO")
+ .init = audiod_init,
+ .reset = audiod_reset,
+ .open = audiod_open,
+ .control_request = audiod_control_request,
+ .control_complete = audiod_control_complete,
+ .xfer_cb = audiod_xfer_cb,
+ .sof = NULL
+},
+#endif
+
#if CFG_TUD_MIDI
{
DRIVER_NAME("MIDI")
@@ -237,10 +256,17 @@ static inline usbd_class_driver_t const * get_driver(uint8_t drvid)
OSAL_QUEUE_DEF(OPT_MODE_DEVICE, _usbd_qdef, CFG_TUD_TASK_QUEUE_SZ, dcd_event_t);
static osal_queue_t _usbd_q;
+// Mutex for claiming endpoint, only needed when using with preempted RTOS
+#if CFG_TUSB_OS != OPT_OS_NONE
+static osal_mutex_def_t _ubsd_mutexdef;
+static osal_mutex_t _usbd_mutex;
+#endif
+
+
//--------------------------------------------------------------------+
// Prototypes
//--------------------------------------------------------------------+
-static void mark_interface_endpoint(uint8_t ep2drv[8][2], uint8_t const* p_desc, uint16_t desc_len, uint8_t driver_id);
+static void mark_interface_endpoint(uint8_t ep2drv[][2], uint8_t const* p_desc, uint16_t desc_len, uint8_t driver_id);
static bool process_control_request(uint8_t rhport, tusb_control_request_t const * p_request);
static bool process_set_config(uint8_t rhport, uint8_t cfg_num);
static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const * p_request);
@@ -312,7 +338,7 @@ tusb_speed_t tud_speed_get(void)
bool tud_mounted(void)
{
- return _usbd_dev.configured;
+ return _usbd_dev.cfg_num ? 1 : 0;
}
bool tud_suspended(void)
@@ -351,9 +377,15 @@ bool tud_init (void)
tu_varclr(&_usbd_dev);
+#if CFG_TUSB_OS != OPT_OS_NONE
+ // Init device mutex
+ _usbd_mutex = osal_mutex_create(&_ubsd_mutexdef);
+ TU_ASSERT(_usbd_mutex);
+#endif
+
// Init device queue & task
_usbd_q = osal_queue_create(&_usbd_qdef);
- TU_ASSERT(_usbd_q != NULL);
+ TU_ASSERT(_usbd_q);
// Get application driver if available
if ( usbd_app_driver_get_cb )
@@ -478,6 +510,7 @@ void tud_task (void)
TU_LOG2("on EP %02X with %u bytes\r\n", ep_addr, (unsigned int) event.xfer_complete.len);
_usbd_dev.ep_status[epnum][ep_dir].busy = false;
+ _usbd_dev.ep_status[epnum][ep_dir].claimed = 0;
if ( 0 == epnum )
{
@@ -598,8 +631,8 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const
case TUSB_REQ_GET_CONFIGURATION:
{
- uint8_t cfgnum = _usbd_dev.configured ? 1 : 0;
- tud_control_xfer(rhport, p_request, &cfgnum, 1);
+ uint8_t cfg_num = _usbd_dev.cfg_num;
+ tud_control_xfer(rhport, p_request, &cfg_num, 1);
}
break;
@@ -607,8 +640,8 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const
{
uint8_t const cfg_num = (uint8_t) p_request->wValue;
- if ( !_usbd_dev.configured && cfg_num ) TU_ASSERT( process_set_config(rhport, cfg_num) );
- _usbd_dev.configured = cfg_num ? 1 : 0;
+ if ( !_usbd_dev.cfg_num && cfg_num ) TU_ASSERT( process_set_config(rhport, cfg_num) );
+ _usbd_dev.cfg_num = cfg_num;
tud_control_status(rhport, p_request);
}
@@ -826,7 +859,7 @@ static bool process_set_config(uint8_t rhport, uint8_t cfg_num)
}
// Helper marking endpoint of interface belongs to class driver
-static void mark_interface_endpoint(uint8_t ep2drv[8][2], uint8_t const* p_desc, uint16_t desc_len, uint8_t driver_id)
+static void mark_interface_endpoint(uint8_t ep2drv[][2], uint8_t const* p_desc, uint16_t desc_len, uint8_t driver_id)
{
uint16_t len = 0;
@@ -955,7 +988,7 @@ void dcd_event_handler(dcd_event_t const * event, bool in_isr)
case DCD_EVENT_UNPLUGGED:
_usbd_dev.connected = 0;
_usbd_dev.addressed = 0;
- _usbd_dev.configured = 0;
+ _usbd_dev.cfg_num = 0;
_usbd_dev.suspended = 0;
osal_queue_send(_usbd_q, event, in_isr);
break;
@@ -1076,6 +1109,59 @@ bool usbd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * desc_ep)
return dcd_edpt_open(rhport, desc_ep);
}
+bool usbd_edpt_claim(uint8_t rhport, uint8_t ep_addr)
+{
+ (void) rhport;
+
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+
+#if CFG_TUSB_OS != OPT_OS_NONE
+ // pre-check to help reducing mutex lock
+ TU_VERIFY((_usbd_dev.ep_status[epnum][dir].busy == 0) && (_usbd_dev.ep_status[epnum][dir].claimed == 0));
+
+ osal_mutex_lock(_usbd_mutex, OSAL_TIMEOUT_WAIT_FOREVER);
+#endif
+
+ // can only claim the endpoint if it is not busy and not claimed yet.
+ bool const ret = (_usbd_dev.ep_status[epnum][dir].busy == 0) && (_usbd_dev.ep_status[epnum][dir].claimed == 0);
+ if (ret)
+ {
+ _usbd_dev.ep_status[epnum][dir].claimed = 1;
+ }
+
+#if CFG_TUSB_OS != OPT_OS_NONE
+ osal_mutex_unlock(_usbd_mutex);
+#endif
+
+ return ret;
+}
+
+bool usbd_edpt_release(uint8_t rhport, uint8_t ep_addr)
+{
+ (void) rhport;
+
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+
+#if CFG_TUSB_OS != OPT_OS_NONE
+ osal_mutex_lock(_usbd_mutex, OSAL_TIMEOUT_WAIT_FOREVER);
+#endif
+
+ // can only release the endpoint if it is claimed and not busy
+ bool const ret = (_usbd_dev.ep_status[epnum][dir].busy == 0) && (_usbd_dev.ep_status[epnum][dir].claimed == 1);
+ if (ret)
+ {
+ _usbd_dev.ep_status[epnum][dir].claimed = 0;
+ }
+
+#if CFG_TUSB_OS != OPT_OS_NONE
+ osal_mutex_unlock(_usbd_mutex);
+#endif
+
+ return ret;
+}
+
bool usbd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
{
uint8_t const epnum = tu_edpt_number(ep_addr);
@@ -1083,6 +1169,9 @@ bool usbd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
TU_LOG2(" Queue 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;
@@ -1093,7 +1182,9 @@ bool usbd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
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;
diff --git a/src/device/usbd.h b/src/device/usbd.h
index bbdd1be99..05df28545 100644
--- a/src/device/usbd.h
+++ b/src/device/usbd.h
@@ -31,7 +31,7 @@
#define _TUSB_USBD_H_
#ifdef __cplusplus
- extern "C" {
+extern "C" {
#endif
#include "common/tusb_common.h"
@@ -166,7 +166,7 @@ TU_ATTR_WEAK bool tud_vendor_control_complete_cb(uint8_t rhport, tusb_control_re
TUD_BOS_PLATFORM_DESCRIPTOR(TUD_BOS_MS_OS_20_UUID, U32_TO_U8S_LE(0x06030000), U16_TO_U8S_LE(_desc_set_len), _vendor_code, 0)
#define TUD_BOS_MS_OS_20_UUID \
- 0xDF, 0x60, 0xDD, 0xD8, 0x89, 0x45, 0xC7, 0x4C, \
+ 0xDF, 0x60, 0xDD, 0xD8, 0x89, 0x45, 0xC7, 0x4C, \
0x9C, 0xD2, 0x65, 0x9D, 0x9E, 0x64, 0x8A, 0x9F
//--------------------------------------------------------------------+
@@ -258,11 +258,11 @@ TU_ATTR_WEAK bool tud_vendor_control_complete_cb(uint8_t rhport, tusb_control_re
#define TUD_MIDI_DESC_HEAD_LEN (9 + 9 + 9 + 7)
#define TUD_MIDI_DESC_HEAD(_itfnum, _stridx, _numcables) \
/* Audio Control (AC) Interface */\
- 9, TUSB_DESC_INTERFACE, _itfnum, 0, 0, TUSB_CLASS_AUDIO, AUDIO_SUBCLASS_CONTROL, AUDIO_PROTOCOL_V1, _stridx,\
+ 9, TUSB_DESC_INTERFACE, _itfnum, 0, 0, TUSB_CLASS_AUDIO, AUDIO_SUBCLASS_CONTROL, AUDIO_FUNC_PROTOCOL_CODE_UNDEF, _stridx,\
/* AC Header */\
- 9, TUSB_DESC_CS_INTERFACE, AUDIO_CS_INTERFACE_HEADER, U16_TO_U8S_LE(0x0100), U16_TO_U8S_LE(0x0009), 1, (uint8_t)((_itfnum) + 1),\
+ 9, TUSB_DESC_CS_INTERFACE, AUDIO_CS_AC_INTERFACE_HEADER, U16_TO_U8S_LE(0x0100), U16_TO_U8S_LE(0x0009), 1, (uint8_t)((_itfnum) + 1),\
/* MIDI Streaming (MS) Interface */\
- 9, TUSB_DESC_INTERFACE, (uint8_t)((_itfnum) + 1), 0, 2, TUSB_CLASS_AUDIO, AUDIO_SUBCLASS_MIDI_STREAMING, AUDIO_PROTOCOL_V1, 0,\
+ 9, TUSB_DESC_INTERFACE, (uint8_t)((_itfnum) + 1), 0, 2, TUSB_CLASS_AUDIO, AUDIO_SUBCLASS_MIDI_STREAMING, AUDIO_FUNC_PROTOCOL_CODE_UNDEF, 0,\
/* MS Header */\
7, TUSB_DESC_CS_INTERFACE, MIDI_CS_INTERFACE_HEADER, U16_TO_U8S_LE(0x0100), U16_TO_U8S_LE(7 + (_numcables) * TUD_MIDI_DESC_JACK_LEN)
@@ -310,6 +310,174 @@ TU_ATTR_WEAK bool tud_vendor_control_complete_cb(uint8_t rhport, tusb_control_re
TUD_MIDI_DESC_EP(_epin, _epsize, 1),\
TUD_MIDI_JACKID_OUT_EMB(1)
+//------------- AUDIO -------------//
+
+/* Standard Interface Association Descriptor (IAD) */
+#define TUD_AUDIO_DESC_IAD_LEN 8
+#define TUD_AUDIO_DESC_IAD(_firstitfs, _nitfs, _stridx) \
+ TUD_AUDIO_DESC_IAD_LEN, TUSB_DESC_INTERFACE_ASSOCIATION, _firstitfs, _nitfs, TUSB_CLASS_AUDIO, AUDIO_FUNCTION_SUBCLASS_UNDEFINED, AUDIO_FUNC_PROTOCOL_CODE_V2, _stridx
+
+/* Standard AC Interface Descriptor(4.7.1) */
+#define TUD_AUDIO_DESC_STD_AC_LEN 9
+#define TUD_AUDIO_DESC_STD_AC(_itfnum, _nEPs, _stridx) /* _nEPs is 0 or 1 */\
+ TUD_AUDIO_DESC_STD_AC_LEN, TUSB_DESC_INTERFACE, _itfnum, /* fixed to zero */ 0x00, _nEPs, TUSB_CLASS_AUDIO, AUDIO_SUBCLASS_CONTROL, AUDIO_INT_PROTOCOL_CODE_V2, _stridx
+
+/* Class-Specific AC Interface Header Descriptor(4.7.2) */
+#define TUD_AUDIO_DESC_CS_AC_LEN 9
+#define TUD_AUDIO_DESC_CS_AC(_bcdADC, _category, _totallen, _ctrl) /* _bcdADC : Audio Device Class Specification Release Number in Binary-Coded Decimal, _category : see audio_function_t, _totallen : Total number of bytes returned for the class-specific AudioControl interface i.e. Clock Source, Unit and Terminal descriptors - Do not include TUD_AUDIO_DESC_CS_AC_LEN, we already do this here*/ \
+ TUD_AUDIO_DESC_CS_AC_LEN, TUSB_DESC_CS_INTERFACE, AUDIO_CS_AC_INTERFACE_HEADER, U16_TO_U8S_LE(_bcdADC), _category, U16_TO_U8S_LE(_totallen + TUD_AUDIO_DESC_CS_AC_LEN), _ctrl
+
+/* Clock Source Descriptor(4.7.2.1) */
+#define TUD_AUDIO_DESC_CLK_SRC_LEN 8
+#define TUD_AUDIO_DESC_CLK_SRC(_clkid, _attr, _ctrl, _assocTerm, _stridx) \
+ TUD_AUDIO_DESC_CLK_SRC_LEN, TUSB_DESC_CS_INTERFACE, AUDIO_CS_AC_INTERFACE_CLOCK_SOURCE, _clkid, _attr, _ctrl, _assocTerm, _stridx
+
+/* Input Terminal Descriptor(4.7.2.4) */
+#define TUD_AUDIO_DESC_INPUT_TERM_LEN 17
+#define TUD_AUDIO_DESC_INPUT_TERM(_termid, _termtype, _assocTerm, _clkid, _nchannelslogical, _channelcfg, _idxchannelnames, _ctrl, _stridx) \
+ TUD_AUDIO_DESC_INPUT_TERM_LEN, TUSB_DESC_CS_INTERFACE, AUDIO_CS_AC_INTERFACE_INPUT_TERMINAL, _termid, U16_TO_U8S_LE(_termtype), _assocTerm, _clkid, _nchannelslogical, U32_TO_U8S_LE(_channelcfg), _idxchannelnames, U16_TO_U8S_LE(_ctrl), _stridx
+
+/* Output Terminal Descriptor(4.7.2.5) */
+#define TUD_AUDIO_DESC_OUTPUT_TERM_LEN 12
+#define TUD_AUDIO_DESC_OUTPUT_TERM(_termid, _termtype, _assocTerm, _srcid, _clkid, _ctrl, _stridx) \
+ TUD_AUDIO_DESC_OUTPUT_TERM_LEN, TUSB_DESC_CS_INTERFACE, AUDIO_CS_AC_INTERFACE_OUTPUT_TERMINAL, _termid, U16_TO_U8S_LE(_termtype), _assocTerm, _srcid, _clkid, U16_TO_U8S_LE(_ctrl), _stridx
+
+/* Feature Unit Descriptor(4.7.2.8) */
+// 1 - Channel
+#define TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL_LEN 6+(1+1)*4
+#define TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL(_unitid, _srcid, _ctrlch0master, _ctrlch1, _stridx) \
+ TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL_LEN, TUSB_DESC_CS_INTERFACE, AUDIO_CS_AC_INTERFACE_FEATURE_UNIT, _unitid, _srcid, U32_TO_U8S_LE(_ctrlch0master), U32_TO_U8S_LE(_ctrlch1), _stridx
+
+// For more channels, add definitions here
+
+/* Standard AS Interface Descriptor(4.9.1) */
+#define TUD_AUDIO_DESC_STD_AS_INT_LEN 9
+#define TUD_AUDIO_DESC_STD_AS_INT(_itfnum, _altset, _nEPs, _stridx) \
+ TUD_AUDIO_DESC_STD_AS_INT_LEN, TUSB_DESC_INTERFACE, _itfnum, _altset, _nEPs, TUSB_CLASS_AUDIO, AUDIO_SUBCLASS_STREAMING, AUDIO_INT_PROTOCOL_CODE_V2, _stridx
+
+/* Class-Specific AS Interface Descriptor(4.9.2) */
+#define TUD_AUDIO_DESC_CS_AS_INT_LEN 16
+#define TUD_AUDIO_DESC_CS_AS_INT(_termid, _ctrl, _formattype, _formats, _nchannelsphysical, _channelcfg, _stridx) \
+ TUD_AUDIO_DESC_CS_AS_INT_LEN, TUSB_DESC_CS_INTERFACE, AUDIO_CS_AS_INTERFACE_AS_GENERAL, _termid, _ctrl, _formattype, U32_TO_U8S_LE(_formats), _nchannelsphysical, U32_TO_U8S_LE(_channelcfg), _stridx
+
+/* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */
+#define TUD_AUDIO_DESC_TYPE_I_FORMAT_LEN 6
+#define TUD_AUDIO_DESC_TYPE_I_FORMAT(_subslotsize, _bitresolution) /* _subslotsize is number of bytes per sample (i.e. subslot) and can be 1,2,3, or 4 */\
+ TUD_AUDIO_DESC_TYPE_I_FORMAT_LEN, TUSB_DESC_CS_INTERFACE, AUDIO_CS_AS_INTERFACE_FORMAT_TYPE, AUDIO_FORMAT_TYPE_I, _subslotsize, _bitresolution
+
+/* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */
+#define TUD_AUDIO_DESC_STD_AS_ISO_EP_LEN 7
+#define TUD_AUDIO_DESC_STD_AS_ISO_EP(_ep, _attr, _maxEPsize, _interval) \
+ TUD_AUDIO_DESC_STD_AS_ISO_EP_LEN, TUSB_DESC_ENDPOINT, _ep, _attr, U16_TO_U8S_LE(_maxEPsize), _interval
+
+/* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */
+#define TUD_AUDIO_DESC_CS_AS_ISO_EP_LEN 8
+#define TUD_AUDIO_DESC_CS_AS_ISO_EP(_attr, _ctrl, _lockdelayunit, _lockdelay) \
+ TUD_AUDIO_DESC_CS_AS_ISO_EP_LEN, TUSB_DESC_CS_ENDPOINT, AUDIO_CS_EP_SUBTYPE_GENERAL, _attr, _ctrl, _lockdelayunit, U16_TO_U8S_LE(_lockdelay)
+
+/* Standard AS Isochronous Feedback Endpoint Descriptor(4.10.2.1) */
+#define TUD_AUDIO_DESC_STD_AS_ISO_FB_EP_LEN 7
+#define TUD_AUDIO_DESC_STD_AS_ISO_FB_EP(_ep, _interval) \
+ TUD_AUDIO_DESC_STD_AS_ISO_FB_EP_LEN, TUSB_DESC_ENDPOINT, _ep, (TUSB_XFER_ISOCHRONOUS | TUSB_ISO_EP_ATT_NO_SYNC | TUSB_ISO_EP_ATT_EXPLICIT_FB), U16_TO_U8S_LE(4), _interval
+
+// 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\
+ + 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_ONE_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_DESC_N_AS_INT 1 // Number of AS interfaces
+
+#define TUD_AUDIO_MIC_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_ONE_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*/ 0x01, /*_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_ONE_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, /*_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*/ 0x01, /*_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
+
+#define TUD_AUDIO_SPEAKER_MONO_FB_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_ONE_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\
+ + TUD_AUDIO_DESC_STD_AS_ISO_FB_EP_LEN)
+
+#define TUD_AUDIO_SPEAKER_MONO_FB_DESCRIPTOR(_itfnum, _stridx, _nBytesPerSample, _nBitsUsedPerSample, _epout, _epsize, _epfb) \
+ /* 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_DESKTOP_SPEAKER, /*_totallen*/ TUD_AUDIO_DESC_CLK_SRC_LEN+TUD_AUDIO_DESC_INPUT_TERM_LEN+TUD_AUDIO_DESC_OUTPUT_TERM_LEN+TUD_AUDIO_DESC_FEATURE_UNIT_ONE_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_USB_STREAMING, /*_assocTerm*/ 0x00, /*_clkid*/ 0x04, /*_nchannelslogical*/ 0x01, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ 0 * (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_OUT_DESKTOP_SPEAKER, /*_assocTerm*/ 0x01, /*_srcid*/ 0x02, /*_clkid*/ 0x04, /*_ctrl*/ 0x0000, /*_stridx*/ 0x00),\
+ /* Feature Unit Descriptor(4.7.2.8) */\
+ TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL(/*_unitid*/ 0x02, /*_srcid*/ 0x01, /*_ctrlch0master*/ 0 * (AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS), /*_ctrlch1*/ 0 * (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*/ 0x02, /*_stridx*/ 0x00),\
+ /* Class-Specific AS Interface Descriptor(4.9.2) */\
+ TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ 0x01, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ 0x01, /*_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*/ _epout, /*_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),\
+ /* Standard AS Isochronous Feedback Endpoint Descriptor(4.10.2.1) */\
+ TUD_AUDIO_DESC_STD_AS_ISO_FB_EP(/*_ep*/ _epfb, /*_interval*/ 1)\
//------------- TUD_USBTMC/USB488 -------------//
#define TUD_USBTMC_APP_CLASS (TUSB_CLASS_APPLICATION_SPECIFIC)
@@ -403,15 +571,15 @@ TU_ATTR_WEAK bool tud_vendor_control_complete_cb(uint8_t rhport, tusb_control_re
//------------- RNDIS -------------//
#if 0
- /* Windows XP */
- #define TUD_RNDIS_ITF_CLASS TUSB_CLASS_CDC
- #define TUD_RNDIS_ITF_SUBCLASS CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL
- #define TUD_RNDIS_ITF_PROTOCOL 0xFF /* CDC_COMM_PROTOCOL_MICROSOFT_RNDIS */
+/* Windows XP */
+#define TUD_RNDIS_ITF_CLASS TUSB_CLASS_CDC
+#define TUD_RNDIS_ITF_SUBCLASS CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL
+#define TUD_RNDIS_ITF_PROTOCOL 0xFF /* CDC_COMM_PROTOCOL_MICROSOFT_RNDIS */
#else
- /* Windows 7+ */
- #define TUD_RNDIS_ITF_CLASS TUSB_CLASS_WIRELESS_CONTROLLER
- #define TUD_RNDIS_ITF_SUBCLASS 0x01
- #define TUD_RNDIS_ITF_PROTOCOL 0x03
+/* Windows 7+ */
+#define TUD_RNDIS_ITF_CLASS TUSB_CLASS_WIRELESS_CONTROLLER
+#define TUD_RNDIS_ITF_SUBCLASS 0x01
+#define TUD_RNDIS_ITF_PROTOCOL 0x03
#endif
// Length of template descriptor: 66 bytes
@@ -477,18 +645,18 @@ TU_ATTR_WEAK bool tud_vendor_control_complete_cb(uint8_t rhport, tusb_control_re
#define TUD_BTH_ISO_ITF_0(_itfnum, ...)
#define TUD_BTH_ISO_ITF_1(_itfnum, _ep_in, _ep_out, ...) TUD_BTH_ISO_ITF(_itfnum, (CFG_TUD_BTH_ISO_ALT_COUNT) - 1, _ep_in, _ep_out, _FIRST(__VA_ARGS__))
#define TUD_BTH_ISO_ITF_2(_itfnum, _ep_in, _ep_out, ...) TUD_BTH_ISO_ITF(_itfnum, (CFG_TUD_BTH_ISO_ALT_COUNT) - 2, _ep_in, _ep_out, _FIRST(__VA_ARGS__)) \
- TUD_BTH_ISO_ITF_1(_itfnum, _ep_in, _ep_out, _REST(__VA_ARGS__))
+ TUD_BTH_ISO_ITF_1(_itfnum, _ep_in, _ep_out, _REST(__VA_ARGS__))
#define TUD_BTH_ISO_ITF_3(_itfnum, _ep_in, _ep_out, ...) TUD_BTH_ISO_ITF(_itfnum, (CFG_TUD_BTH_ISO_ALT_COUNT) - 3, _ep_in, _ep_out, _FIRST(__VA_ARGS__)) \
- TUD_BTH_ISO_ITF_2(_itfnum, _ep_in, _ep_out, _REST(__VA_ARGS__))
+ TUD_BTH_ISO_ITF_2(_itfnum, _ep_in, _ep_out, _REST(__VA_ARGS__))
#define TUD_BTH_ISO_ITF_4(_itfnum, _ep_in, _ep_out, ...) TUD_BTH_ISO_ITF(_itfnum, (CFG_TUD_BTH_ISO_ALT_COUNT) - 4, _ep_in, _ep_out, _FIRST(__VA_ARGS__)) \
- TUD_BTH_ISO_ITF_3(_itfnum, _ep_in, _ep_out, _REST(__VA_ARGS__))
+ TUD_BTH_ISO_ITF_3(_itfnum, _ep_in, _ep_out, _REST(__VA_ARGS__))
#define TUD_BTH_ISO_ITF_5(_itfnum, _ep_in, _ep_out, ...) TUD_BTH_ISO_ITF(_itfnum, (CFG_TUD_BTH_ISO_ALT_COUNT) - 5, _ep_in, _ep_out, _FIRST(__VA_ARGS__)) \
- TUD_BTH_ISO_ITF_4(_itfnum, _ep_in, _ep_out, _REST(__VA_ARGS__))
+ TUD_BTH_ISO_ITF_4(_itfnum, _ep_in, _ep_out, _REST(__VA_ARGS__))
#define TUD_BTH_ISO_ITF_6(_itfnum, _ep_in, _ep_out, ...) TUD_BTH_ISO_ITF(_itfnum, (CFG_TUD_BTH_ISO_ALT_COUNT) - 6, _ep_in, _ep_out, _FIRST(__VA_ARGS__)) \
- TUD_BTH_ISO_ITF_5(_itfnum, _ep_in, _ep_out, _REST(__VA_ARGS__))
+ TUD_BTH_ISO_ITF_5(_itfnum, _ep_in, _ep_out, _REST(__VA_ARGS__))
#define TUD_BTH_ISO_ITFS(_itfnum, _ep_in, _ep_out, ...) \
- TU_XSTRCAT(TUD_BTH_ISO_ITF_, CFG_TUD_BTH_ISO_ALT_COUNT)(_itfnum, _ep_in, _ep_out, __VA_ARGS__)
+ TU_XSTRCAT(TUD_BTH_ISO_ITF_, CFG_TUD_BTH_ISO_ALT_COUNT)(_itfnum, _ep_in, _ep_out, __VA_ARGS__)
// BT Primary controller descriptor
// Interface number, string index, attributes, event endpoint, event endpoint size, interval, data in, data out, data endpoint size, iso endpoint sizes
@@ -497,7 +665,7 @@ TU_ATTR_WEAK bool tud_vendor_control_complete_cb(uint8_t rhport, tusb_control_re
TUD_BTH_ISO_ITFS(_itfnum + 1, _ep_in + 1, _ep_out + 1, __VA_ARGS__)
#ifdef __cplusplus
- }
+}
#endif
#endif /* _TUSB_USBD_H_ */
diff --git a/src/device/usbd_pvt.h b/src/device/usbd_pvt.h
index a5d223329..09b285581 100644
--- a/src/device/usbd_pvt.h
+++ b/src/device/usbd_pvt.h
@@ -70,6 +70,13 @@ 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);
+// 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);
+
+// Release an endpoint without submitting a transfer
+bool usbd_edpt_release(uint8_t rhport, uint8_t ep_addr);
+
// Check if endpoint transferring is complete
bool usbd_edpt_busy(uint8_t rhport, uint8_t ep_addr);
diff --git a/src/portable/dialog/da146xx/dcd_da146xx.c b/src/portable/dialog/da146xx/dcd_da146xx.c
index 602836e3a..42ba867d7 100644
--- a/src/portable/dialog/da146xx/dcd_da146xx.c
+++ b/src/portable/dialog/da146xx/dcd_da146xx.c
@@ -40,6 +40,24 @@
// We disable SOF for now until needed later on
#define USE_SOF 0
+// Size of RX or TX FIFO.
+#define FIFO_SIZE 64
+
+#ifndef TU_DA1469X_FIFO_READ_THRESHOLD
+// RX FIFO is 64 bytes. When endpoint size is greater then 64, FIFO warning interrupt
+// is enabled to allow read incoming data during frame reception.
+// It is possible to stay in interrupt reading whole packet at once, but it may be
+// more efficient for MCU to read as much data as possible and when FIFO is hardly
+// filled exit interrupt handler waiting for next FIFO warning level interrupt
+// or packet end.
+// When running at 96MHz code that reads FIFO based on number of bytes stored in
+// USB_RXSx_REG.USB_RXCOUNT takes enough time to fill FIFO with two additional bytes.
+// Settings this threshold above this allows to leave interrupt handler and wait
+// for more bytes to before next ISR. This allows reduce overall ISR time to 1/3
+// of time that would be needed if ISR read as fast as possible.
+#define TU_DA1469X_FIFO_READ_THRESHOLD 4
+#endif
+
#define EP_MAX 4
#define NFSR_NODE_RESET 0
@@ -119,6 +137,54 @@ typedef struct
#define EP_REGS(first_ep_reg) (EPx_REGS*)(&USB->first_ep_reg)
+// DMA channel pair to use, channel 6 will be used for RX channel 7 for TX direction.
+#ifndef TU_DA146XX_DMA_RX_CHANNEL
+#define TU_DA146XX_DMA_RX_CHANNEL 6
+#endif
+#define DA146XX_DMA_USB_MUX (0x6 << (TU_DA146XX_DMA_RX_CHANNEL * 2))
+#define DA146XX_DMA_USB_MUX_MASK (0xF << (TU_DA146XX_DMA_RX_CHANNEL * 2))
+
+typedef struct
+{
+ __IOM uint32_t DMAx_A_START_REG;
+ __IOM uint32_t DMAx_B_START_REG;
+ __IOM uint32_t DMAx_INT_REG;
+ __IOM uint32_t DMAx_LEN_REG;
+ __IOM uint32_t DMAx_CTRL_REG;
+ __IOM uint32_t DMAx_IDX_REG;
+ __IM uint32_t RESERVED[2]; // Extend structure size for array like usage, registers for each channel are 0x20 bytes apart.
+} da146xx_dma_channel_t;
+
+#define DMA_CHANNEL_REGS(n) ((da146xx_dma_channel_t *)(DMA) + n)
+#define RX_DMA_REGS DMA_CHANNEL_REGS(TU_DA146XX_DMA_RX_CHANNEL)
+#define TX_DMA_REGS DMA_CHANNEL_REGS((TU_DA146XX_DMA_RX_CHANNEL) + 1)
+
+#define RX_DMA_START ((1 << DMA_DMA0_CTRL_REG_DMA_ON_Pos) |\
+ (0 << DMA_DMA0_CTRL_REG_BW_Pos) | \
+ (1 << DMA_DMA0_CTRL_REG_DREQ_MODE_Pos) | \
+ (1 << DMA_DMA0_CTRL_REG_BINC_Pos) | \
+ (0 << DMA_DMA0_CTRL_REG_AINC_Pos) | \
+ (0 << DMA_DMA0_CTRL_REG_CIRCULAR_Pos) | \
+ (2 << DMA_DMA0_CTRL_REG_DMA_PRIO_Pos) | \
+ (0 << DMA_DMA0_CTRL_REG_DMA_IDLE_Pos) | \
+ (0 << DMA_DMA0_CTRL_REG_DMA_INIT_Pos) | \
+ (0 << DMA_DMA0_CTRL_REG_REQ_SENSE_Pos) | \
+ (0 << DMA_DMA0_CTRL_REG_BURST_MODE_Pos) | \
+ (0 << DMA_DMA0_CTRL_REG_BUS_ERROR_DETECT_Pos))
+
+#define TX_DMA_START ((1 << DMA_DMA0_CTRL_REG_DMA_ON_Pos) |\
+ (0 << DMA_DMA0_CTRL_REG_BW_Pos) | \
+ (1 << DMA_DMA0_CTRL_REG_DREQ_MODE_Pos) | \
+ (0 << DMA_DMA0_CTRL_REG_BINC_Pos) | \
+ (1 << DMA_DMA0_CTRL_REG_AINC_Pos) | \
+ (0 << DMA_DMA0_CTRL_REG_CIRCULAR_Pos) | \
+ (2 << DMA_DMA0_CTRL_REG_DMA_PRIO_Pos) | \
+ (0 << DMA_DMA0_CTRL_REG_DMA_IDLE_Pos) | \
+ (0 << DMA_DMA0_CTRL_REG_DMA_INIT_Pos) | \
+ (1 << DMA_DMA0_CTRL_REG_REQ_SENSE_Pos) | \
+ (0 << DMA_DMA0_CTRL_REG_BURST_MODE_Pos) | \
+ (0 << DMA_DMA0_CTRL_REG_BUS_ERROR_DETECT_Pos))
+
// Dialog register fields and bit mask are very long. Filed masks repeat register names.
// Those convenience macros are a way to reduce complexity of register modification lines.
#define GET_BIT(val, field) (val & field ## _Msk) >> field ## _Pos
@@ -144,6 +210,8 @@ typedef struct {
uint8_t data1 : 1;
// Endpoint is stalled
uint8_t stall : 1;
+ // ISO endpoint
+ uint8_t iso : 1;
} xfer_ctl_t;
static struct
@@ -151,6 +219,8 @@ static struct
bool vbus_present;
bool in_reset;
xfer_ctl_t xfer_status[EP_MAX][2];
+ // Endpoints that use DMA, one for each direction
+ uint8_t dma_ep[2];
} _dcd =
{
.vbus_present = false,
@@ -215,15 +285,12 @@ void tusb_vbus_changed(bool present)
}
}
-static void transmit_packet(xfer_ctl_t * xfer)
+static void fill_tx_fifo(xfer_ctl_t * xfer)
{
int left_to_send;
uint8_t const *src;
EPx_REGS *regs = xfer->regs;
- uint32_t txc;
-
- txc = USB_USB_TXC1_REG_USB_TX_EN_Msk;
- if (xfer->data1) txc |= USB_USB_TXC1_REG_USB_TOGGLE_TX_Msk;
+ uint8_t const epnum = tu_edpt_number(xfer->ep_addr);
src = &xfer->buffer[xfer->transferred];
left_to_send = xfer->total_len - xfer->transferred;
@@ -240,53 +307,149 @@ static void transmit_packet(xfer_ctl_t * xfer)
xfer->last_packet_size++;
left_to_send--;
}
- if (tu_edpt_number(xfer->ep_addr) != 0)
+ if (epnum != 0)
{
if (left_to_send > 0)
{
// Max packet size is set to value greater then FIFO. Enable fifo level warning
// to handle larger packets.
- txc |= USB_USB_TXC1_REG_USB_TFWL_Msk;
+ regs->txc |= (3 << USB_USB_TXC1_REG_USB_TFWL_Pos);
+ USB->USB_FWMSK_REG |= 1 << (epnum - 1 + USB_USB_FWMSK_REG_USB_M_TXWARN31_Pos);
}
else
{
+ xfer->regs->txc &= ~USB_USB_TXC1_REG_USB_TFWL_Msk;
+ USB->USB_FWMSK_REG &= ~(1 << (epnum - 1 + USB_USB_FWMSK_REG_USB_M_TXWARN31_Pos));
// Whole packet already in fifo, no need to refill it later. Mark last.
- txc |= USB_USB_TXC1_REG_USB_LAST_Msk;
+ regs->txc |= USB_USB_TXC1_REG_USB_LAST_Msk;
}
}
- // Enable transfer with correct interrupts enabled
- regs->txc = txc;
}
-static void receive_packet(xfer_ctl_t *xfer, uint16_t bytes_in_fifo)
+static bool try_allocate_dma(uint8_t epnum, uint8_t dir)
{
- EPx_REGS *regs = xfer->regs;
+ // TODO: Disable interrupts while checking
+ if (_dcd.dma_ep[dir] == 0)
+ {
+ _dcd.dma_ep[dir] = epnum;
+ if (dir == TUSB_DIR_OUT)
+ USB->USB_DMA_CTRL_REG = (USB->USB_DMA_CTRL_REG & ~USB_USB_DMA_CTRL_REG_USB_DMA_RX_Msk) |
+ ((epnum - 1) << USB_USB_DMA_CTRL_REG_USB_DMA_RX_Pos);
+ else
+ USB->USB_DMA_CTRL_REG = (USB->USB_DMA_CTRL_REG & ~USB_USB_DMA_CTRL_REG_USB_DMA_TX_Msk) |
+ ((epnum - 1) << USB_USB_DMA_CTRL_REG_USB_DMA_TX_Pos);
+ USB->USB_DMA_CTRL_REG |= USB_USB_DMA_CTRL_REG_USB_DMA_EN_Msk;
+ }
+ return _dcd.dma_ep[dir] == epnum;
+}
+
+static void start_rx_dma(volatile void *src, void *dst, uint16_t size)
+{
+ // Setup SRC and DST registers
+ RX_DMA_REGS->DMAx_A_START_REG = (uint32_t)src;
+ RX_DMA_REGS->DMAx_B_START_REG = (uint32_t)dst;
+ // Don't need DMA interrupt, read end is determined by RX_LAST or RX_ERR events.
+ RX_DMA_REGS->DMAx_INT_REG = size - 1;
+ RX_DMA_REGS->DMAx_LEN_REG = size - 1;
+ RX_DMA_REGS->DMAx_CTRL_REG = RX_DMA_START;
+}
+
+static void start_rx_packet(xfer_ctl_t *xfer)
+{
+ uint8_t const epnum = tu_edpt_number(xfer->ep_addr);
uint16_t remaining = xfer->total_len - xfer->transferred;
+ uint16_t size = tu_min16(remaining, xfer->max_packet_size);
+
+ xfer->last_packet_size = 0;
+ if (xfer->max_packet_size > FIFO_SIZE && remaining > FIFO_SIZE)
+ {
+ if (try_allocate_dma(epnum, TUSB_DIR_OUT))
+ {
+ start_rx_dma(&xfer->regs->rxd, xfer->buffer + xfer->transferred, size);
+ }
+ else
+ {
+ // Other endpoint is using DMA in that direction, fall back to interrupts.
+ // For endpoint size greater then FIFO size enable FIFO level warning interrupt
+ // when FIFO has less then 17 bytes free.
+ xfer->regs->rxc |= USB_USB_RXC1_REG_USB_RFWL_Msk;
+ USB->USB_FWMSK_REG |= 1 << (epnum - 1 + USB_USB_FWMSK_REG_USB_M_RXWARN31_Pos);
+ }
+ }
+ else if (epnum != 0)
+ {
+ // If max_packet_size would fit in FIFO no need for FIFO level warning interrupt.
+ xfer->regs->rxc &= ~USB_USB_RXC1_REG_USB_RFWL_Msk;
+ USB->USB_FWMSK_REG &= ~(1 << (epnum - 1 + USB_USB_FWMSK_REG_USB_M_RXWARN31_Pos));
+ }
+ xfer->regs->rxc |= USB_USB_RXC1_REG_USB_RX_EN_Msk;
+}
+
+static void start_tx_dma(void *src, volatile void *dst, uint16_t size)
+{
+ // Setup SRC and DST registers
+ TX_DMA_REGS->DMAx_A_START_REG = (uint32_t)src;
+ TX_DMA_REGS->DMAx_B_START_REG = (uint32_t)dst;
+ // Interrupt not needed
+ TX_DMA_REGS->DMAx_INT_REG = size;
+ TX_DMA_REGS->DMAx_LEN_REG = size - 1;
+ TX_DMA_REGS->DMAx_CTRL_REG = TX_DMA_START;
+}
+
+static void start_tx_packet(xfer_ctl_t *xfer)
+{
+ uint8_t const epnum = tu_edpt_number(xfer->ep_addr);
+ uint16_t remaining = xfer->total_len - xfer->transferred;
+ uint16_t size = tu_min16(remaining, xfer->max_packet_size);
+ EPx_REGS *regs = xfer->regs;
+
+ xfer->last_packet_size = 0;
+
+ regs->txc = USB_USB_TXC1_REG_USB_FLUSH_Msk;
+ regs->txc = USB_USB_TXC1_REG_USB_IGN_ISOMSK_Msk;
+ if (xfer->data1) xfer->regs->txc |= USB_USB_TXC1_REG_USB_TOGGLE_TX_Msk;
+
+ if (xfer->max_packet_size > FIFO_SIZE && remaining > FIFO_SIZE && try_allocate_dma(epnum, TUSB_DIR_IN))
+ {
+ // Whole packet will be put in FIFO by DMA. Set LAST bit before start.
+ start_tx_dma(xfer->buffer + xfer->transferred, &regs->txd, size);
+ regs->txc |= USB_USB_TXC1_REG_USB_LAST_Msk;
+ }
+ else
+ {
+ fill_tx_fifo(xfer);
+ }
+ regs->txc |= USB_USB_TXC1_REG_USB_TX_EN_Msk;
+}
+
+static void read_rx_fifo(xfer_ctl_t *xfer, uint16_t bytes_in_fifo)
+{
+ EPx_REGS *regs = xfer->regs;
+ uint16_t remaining = xfer->total_len - xfer->transferred - xfer->last_packet_size;
uint16_t receive_this_time = bytes_in_fifo;
- if (remaining <= bytes_in_fifo) receive_this_time = remaining;
+ if (remaining < bytes_in_fifo) receive_this_time = remaining;
uint8_t *buf = xfer->buffer + xfer->transferred + xfer->last_packet_size;
for (int i = 0; i < receive_this_time; ++i) buf[i] = regs->rxd;
- xfer->transferred += receive_this_time;
xfer->last_packet_size += receive_this_time;
}
static void handle_ep0_rx(void)
{
- int packet_size;
+ int fifo_bytes;
uint32_t rxs0 = USB->USB_RXS0_REG;
xfer_ctl_t *xfer = XFER_CTL_BASE(0, TUSB_DIR_OUT);
- packet_size = GET_BIT(rxs0, USB_USB_RXS0_REG_USB_RCOUNT);
+ fifo_bytes = GET_BIT(rxs0, USB_USB_RXS0_REG_USB_RCOUNT);
if (rxs0 & USB_USB_RXS0_REG_USB_SETUP_Msk)
{
xfer_ctl_t *xfer_in = XFER_CTL_BASE(0, TUSB_DIR_IN);
// Setup packet is in
- for (int i = 0; i < packet_size; ++i) _setup_packet[i] = USB->USB_RXD0_REG;
+ for (int i = 0; i < fifo_bytes; ++i) _setup_packet[i] = USB->USB_RXD0_REG;
xfer->stall = 0;
xfer->data1 = 1;
@@ -302,21 +465,26 @@ static void handle_ep0_rx(void)
{
// Toggle bit does not match discard packet
REG_SET_BIT(USB_RXC0_REG, USB_FLUSH);
+ xfer->last_packet_size = 0;
}
else
{
- receive_packet(xfer, packet_size);
- xfer->data1 ^= 1;
-
- if (xfer->total_len == xfer->transferred || xfer->last_packet_size < xfer->max_packet_size)
- {
- dcd_event_xfer_complete(0, 0, xfer->transferred, XFER_RESULT_SUCCESS, true);
- }
- else
+ read_rx_fifo(xfer, fifo_bytes);
+ if (rxs0 & USB_USB_RXS0_REG_USB_RX_LAST_Msk)
{
+ xfer->transferred += xfer->last_packet_size;
+ xfer->data1 ^= 1;
+
+ if (xfer->total_len == xfer->transferred || xfer->last_packet_size < xfer->max_packet_size)
+ {
+ dcd_event_xfer_complete(0, 0, xfer->transferred, XFER_RESULT_SUCCESS, true);
+ }
+ else
+ {
+ // Re-enable reception
+ REG_SET_BIT(USB_RXC0_REG, USB_RX_EN);
+ }
xfer->last_packet_size = 0;
- // Re-enable reception
- REG_SET_BIT(USB_RXC0_REG, USB_RX_EN);
}
}
}
@@ -350,51 +518,79 @@ static void handle_ep0_tx(void)
// Start from the beginning
xfer->last_packet_size = 0;
}
- transmit_packet(xfer);
+ fill_tx_fifo(xfer);
}
}
static void handle_epx_rx_ev(uint8_t ep)
{
uint32_t rxs;
- int packet_size;
+ int fifo_bytes;
xfer_ctl_t *xfer = XFER_CTL_BASE(ep, TUSB_DIR_OUT);
EPx_REGS *regs = xfer->regs;
- rxs = regs->rxs;
-
- if (GET_BIT(rxs, USB_USB_RXS1_REG_USB_RX_ERR))
+ do
{
- regs->rxc |= USB_USB_RXC1_REG_USB_FLUSH_Msk;
- }
- else
- {
- packet_size = GET_BIT(rxs, USB_USB_RXS1_REG_USB_RXCOUNT);
- receive_packet(xfer, packet_size);
- if (GET_BIT(rxs, USB_USB_RXS1_REG_USB_RX_LAST))
+ rxs = regs->rxs;
+
+ if (GET_BIT(rxs, USB_USB_RXS1_REG_USB_RX_ERR))
{
- if (GET_BIT(rxs, USB_USB_RXS1_REG_USB_TOGGLE_RX) != xfer->data1)
+ regs->rxc |= USB_USB_RXC1_REG_USB_FLUSH_Msk;
+ xfer->last_packet_size = 0;
+ if (_dcd.dma_ep[TUSB_DIR_OUT] == ep)
{
- // Toggle bit does not match discard packet
- regs->rxc |= USB_USB_RXC1_REG_USB_FLUSH_Msk;
+ // Stop DMA
+ RX_DMA_REGS->DMAx_CTRL_REG &= ~DMA_DMA0_CTRL_REG_DMA_ON_Msk;
+ // Restart DMA since packet was dropped, all parameters should still work.
+ RX_DMA_REGS->DMAx_CTRL_REG |= DMA_DMA0_CTRL_REG_DMA_ON_Msk;
}
- else
+ break;
+ }
+ else
+ {
+ if (_dcd.dma_ep[TUSB_DIR_OUT] == ep)
{
- xfer->data1 ^= 1;
- if (xfer->total_len == xfer->transferred || xfer->last_packet_size < xfer->max_packet_size)
+ // Disable DMA and update last_packet_size with what DMA reported.
+ RX_DMA_REGS->DMAx_CTRL_REG &= ~DMA_DMA0_CTRL_REG_DMA_ON_Msk;
+ xfer->last_packet_size = RX_DMA_REGS->DMAx_IDX_REG;
+ // When DMA did not finished (packet was smaller then MPS), DMAx_IDX_REG holds exact number of bytes transmitted.
+ // When DMA finished value in DMAx_IDX_REG is one less then actual number of transmitted bytes.
+ if (xfer->last_packet_size == RX_DMA_REGS->DMAx_LEN_REG) xfer->last_packet_size++;
+ // Release DMA to use by other endpoints.
+ _dcd.dma_ep[TUSB_DIR_OUT] = 0;
+ }
+ fifo_bytes = GET_BIT(rxs, USB_USB_RXS1_REG_USB_RXCOUNT);
+ // FIFO maybe empty if DMA read it before or it's final iteration and function already read all that was to read.
+ if (fifo_bytes > 0)
+ {
+ read_rx_fifo(xfer, fifo_bytes);
+ }
+ if (GET_BIT(rxs, USB_USB_RXS1_REG_USB_RX_LAST))
+ {
+ if (!xfer->iso && GET_BIT(rxs, USB_USB_RXS1_REG_USB_TOGGLE_RX) != xfer->data1)
{
- dcd_event_xfer_complete(0, xfer->ep_addr, xfer->transferred, XFER_RESULT_SUCCESS, true);
+ // Toggle bit does not match discard packet
+ regs->rxc |= USB_USB_RXC1_REG_USB_FLUSH_Msk;
}
else
{
- xfer->last_packet_size = 0;
- // Re-enable reception
- regs->rxc |= USB_USB_RXC1_REG_USB_RX_EN_Msk;
+ xfer->data1 ^= 1;
+ xfer->transferred += xfer->last_packet_size;
+ if (xfer->total_len == xfer->transferred || xfer->last_packet_size < xfer->max_packet_size || xfer->iso)
+ {
+ dcd_event_xfer_complete(0, xfer->ep_addr, xfer->transferred, XFER_RESULT_SUCCESS, true);
+ }
+ else
+ {
+ // Re-enable reception
+ start_rx_packet(xfer);
+ }
}
+ xfer->last_packet_size = 0;
}
}
- }
+ } while (fifo_bytes > TU_DA1469X_FIFO_READ_THRESHOLD);
}
static void handle_rx_ev(void)
@@ -409,14 +605,23 @@ static void handle_rx_ev(void)
static void handle_epx_tx_ev(xfer_ctl_t *xfer)
{
- uint32_t usb_txs1_reg;
+ uint8_t const epnum = tu_edpt_number(xfer->ep_addr);
+ uint32_t txs;
EPx_REGS *regs = xfer->regs;
- usb_txs1_reg = regs->USB_TXS1_REG;
+ txs = regs->txs;
- if (GET_BIT(usb_txs1_reg, USB_USB_TXS1_REG_USB_TX_DONE))
+ if (GET_BIT(txs, USB_USB_TXS1_REG_USB_TX_DONE))
{
- if (GET_BIT(usb_txs1_reg, USB_USB_TXS1_REG_USB_ACK_STAT))
+ if (_dcd.dma_ep[TUSB_DIR_IN] == epnum)
+ {
+ // Disable DMA and update last_packet_size with what DMA reported.
+ TX_DMA_REGS->DMAx_CTRL_REG &= ~DMA_DMA1_CTRL_REG_DMA_ON_Msk;
+ xfer->last_packet_size = TX_DMA_REGS->DMAx_IDX_REG + 1;
+ // Release DMA to used by other endpoints.
+ _dcd.dma_ep[TUSB_DIR_IN] = 0;
+ }
+ if (GET_BIT(txs, USB_USB_TXS1_REG_USB_ACK_STAT))
{
// ACK received, update transfer state and DATA0/1 bit
xfer->transferred += xfer->last_packet_size;
@@ -429,12 +634,13 @@ static void handle_epx_tx_ev(xfer_ctl_t *xfer)
return;
}
}
- else
- {
- xfer->last_packet_size = 0;
- }
- transmit_packet(xfer);
}
+ if (txs & USB_USB_TXS1_REG_USB_TX_URUN_Msk)
+ {
+ TU_LOG1("EP %d FIFO underrun\n", epnum);
+ }
+ // Start next or repeated packet.
+ start_tx_packet(xfer);
}
static void handle_tx_ev(void)
@@ -459,6 +665,7 @@ static void handle_bus_reset(void)
_dcd.in_reset = true;
dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true);
+ USB->USB_DMA_CTRL_REG = 0;
USB->USB_MAMSK_REG = USB_USB_MAMSK_REG_USB_M_INTR_Msk |
#if USE_SOF
@@ -503,9 +710,9 @@ static void handle_alt_ev(void)
}
}
-static void handle_epx_tx_refill(uint8_t ep)
+static void handle_epx_tx_warn_ev(uint8_t ep)
{
- transmit_packet(XFER_CTL_BASE(ep, TUSB_DIR_IN));
+ fill_tx_fifo(XFER_CTL_BASE(ep, TUSB_DIR_IN));
}
static void handle_fifo_warning(void)
@@ -513,11 +720,11 @@ static void handle_fifo_warning(void)
uint32_t fifo_warning = USB->USB_FWEV_REG;
if (fifo_warning & 0x01)
- handle_epx_tx_refill(1);
+ handle_epx_tx_warn_ev(1);
if (fifo_warning & 0x02)
- handle_epx_tx_refill(2);
+ handle_epx_tx_warn_ev(2);
if (fifo_warning & 0x04)
- handle_epx_tx_refill(3);
+ handle_epx_tx_warn_ev(3);
if (fifo_warning & 0x10)
handle_epx_rx_ev(1);
if (fifo_warning & 0x20)
@@ -595,6 +802,9 @@ void dcd_connect(uint8_t rhport)
(void)rhport;
REG_SET_BIT(USB_MCTRL_REG, USB_NAT);
+
+ // Select chosen DMA to be triggered by USB.
+ DMA->DMA_REQ_MUX_REG = (DMA->DMA_REQ_MUX_REG & ~DA146XX_DMA_USB_MUX_MASK) | DA146XX_DMA_USB_MUX;
}
void dcd_disconnect(uint8_t rhport)
@@ -624,8 +834,13 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
xfer->max_packet_size = desc_edpt->wMaxPacketSize.size;
xfer->ep_addr = desc_edpt->bEndpointAddress;
xfer->data1 = 0;
+ xfer->iso = 0;
- if (epnum != 0 && desc_edpt->bmAttributes.xfer == 1) iso_mask = USB_USB_EPC1_REG_USB_ISO_Msk;
+ if (epnum != 0 && desc_edpt->bmAttributes.xfer == 1)
+ {
+ iso_mask = USB_USB_EPC1_REG_USB_ISO_Msk;
+ xfer->iso = 1;
+ }
if (epnum == 0)
{
@@ -651,7 +866,7 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
return true;
}
-bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
+void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr)
{
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
@@ -659,33 +874,62 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t t
(void)rhport;
- xfer->buffer = buffer;
- xfer->total_len = total_bytes;
- xfer->last_packet_size = 0;
- xfer->transferred = 0;
+ TU_ASSERT(epnum < EP_MAX,);
- if (dir == TUSB_DIR_OUT)
+ if (epnum == 0)
{
- if (epnum != 0)
+ USB->USB_MAMSK_REG &= ~(USB_USB_MAMSK_REG_USB_M_EP0_RX_Msk |
+ USB_USB_MAMSK_REG_USB_M_EP0_TX_Msk);
+ }
+ else
+ {
+ if (dir == TUSB_DIR_OUT)
{
- if (xfer->max_packet_size > 64)
+ xfer->regs->rxc = USB_USB_RXC1_REG_USB_FLUSH_Msk;
+ xfer->regs->epc_out = 0;
+ USB->USB_RXMSK_REG &= ~(0x101 << (epnum - 1));
+ // Release DMA if needed
+ if (_dcd.dma_ep[TUSB_DIR_OUT] == epnum)
{
- // For endpoint size greater then FIFO size enable FIFO level warning interrupt
- // when FIFO has less then 17 bytes free.
- xfer->regs->rxc |= USB_USB_RXC1_REG_USB_RFWL_Msk;
+ RX_DMA_REGS->DMAx_CTRL_REG &= ~DMA_DMA0_CTRL_REG_DMA_ON_Msk;
+ _dcd.dma_ep[TUSB_DIR_OUT] = 0;
}
- else
+ }
+ else
+ {
+ xfer->regs->txc = USB_USB_TXC1_REG_USB_FLUSH_Msk;
+ xfer->regs->epc_in = 0;
+ USB->USB_TXMSK_REG &= ~(0x101 << (epnum - 1));
+ // Release DMA if needed
+ if (_dcd.dma_ep[TUSB_DIR_IN] == epnum)
{
- // If max_packet_size would fit in FIFO no need for FIFO level warning interrupt.
- xfer->regs->rxc &= ~USB_USB_RXC1_REG_USB_RFWL_Msk;
+ TX_DMA_REGS->DMAx_CTRL_REG &= ~DMA_DMA1_CTRL_REG_DMA_ON_Msk;
+ _dcd.dma_ep[TUSB_DIR_IN] = 0;
}
}
- // USB_RX_EN bit is in same place for all endpoints.
- xfer->regs->rxc = USB_USB_RXC0_REG_USB_RX_EN_Msk;
+ }
+}
+
+bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
+{
+ 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);
+
+ (void)rhport;
+
+ xfer->buffer = buffer;
+ xfer->total_len = total_bytes;
+ xfer->last_packet_size = 0;
+ xfer->transferred = 0;
+
+ if (dir == TUSB_DIR_OUT)
+ {
+ start_rx_packet(xfer);
}
else // IN
{
- transmit_packet(xfer);
+ start_tx_packet(xfer);
}
return true;
diff --git a/src/portable/nordic/nrf5x/dcd_nrf5x.c b/src/portable/nordic/nrf5x/dcd_nrf5x.c
index cd35f2e5b..b85d3776f 100644
--- a/src/portable/nordic/nrf5x/dcd_nrf5x.c
+++ b/src/portable/nordic/nrf5x/dcd_nrf5x.c
@@ -53,6 +53,9 @@ enum
enum
{
+ // Endpoint number is fixed (8) for ISOOUT and ISOIN.
+ EP_ISO_NUM = 8,
+ // CBI endpoints count
EP_COUNT = 8
};
@@ -62,11 +65,14 @@ typedef struct
uint8_t* buffer;
uint16_t total_len;
volatile uint16_t actual_len;
- uint8_t mps; // max packet size
+ uint16_t mps; // max packet size
// nrf52840 will auto ACK OUT packet after DMA is done
// indicate packet is already ACK
volatile bool data_received;
+ // Set to true when data was transferred from RAM to ISO IN output buffer.
+ // New data can be put in ISO IN output buffer after SOF.
+ bool iso_in_transfer_ready;
} xfer_td_t;
@@ -74,7 +80,8 @@ typedef struct
static struct
{
// All 8 endpoints including control IN & OUT (offset 1)
- xfer_td_t xfer[EP_COUNT][2];
+ // +1 for ISO endpoints
+ xfer_td_t xfer[EP_COUNT + 1][2];
// Number of pending DMA that is started but not handled yet by dcd_int_handler().
// Since nRF can only carry one DMA can run at a time, this value is normally be either 0 or 1.
@@ -122,7 +129,7 @@ static void edpt_dma_start(volatile uint32_t* reg_startep)
// for the DMA complete by comparing current pending DMA with number of ENDED Events
uint32_t ended = 0;
- while ( _dcd.dma_pending < ((uint8_t) ended) )
+ while ( _dcd.dma_pending > ((uint8_t) ended) )
{
ended = NRF_USBD->EVENTS_ENDISOIN + NRF_USBD->EVENTS_ENDISOOUT;
@@ -173,6 +180,7 @@ static void xact_out_prepare(uint8_t epnum)
{
// Write zero value to SIZE register will allow hw to ACK (accept data)
// If it is not already done by DMA
+ // SIZE.ISOOUT can also be accessed this way
NRF_USBD->SIZE.EPOUT[epnum] = 0;
}
@@ -183,15 +191,32 @@ static void xact_out_prepare(uint8_t epnum)
static void xact_out_dma(uint8_t epnum)
{
xfer_td_t* xfer = get_td(epnum, TUSB_DIR_OUT);
+ uint32_t xact_len;
- uint8_t const xact_len = NRF_USBD->SIZE.EPOUT[epnum];
+ if (epnum == EP_ISO_NUM)
+ {
+ xact_len = NRF_USBD->SIZE.ISOOUT;
+ // If ZERO bit is set, ignore ISOOUT length
+ if (xact_len & USBD_SIZE_ISOOUT_ZERO_Msk) xact_len = 0;
+ else
+ {
+ // Trigger DMA move data from Endpoint -> SRAM
+ NRF_USBD->ISOOUT.PTR = (uint32_t) xfer->buffer;
+ NRF_USBD->ISOOUT.MAXCNT = xact_len;
- // Trigger DMA move data from Endpoint -> SRAM
- NRF_USBD->EPOUT[epnum].PTR = (uint32_t) xfer->buffer;
- NRF_USBD->EPOUT[epnum].MAXCNT = xact_len;
+ edpt_dma_start(&NRF_USBD->TASKS_STARTISOOUT);
+ }
+ }
+ else
+ {
+ xact_len = (uint8_t)NRF_USBD->SIZE.EPOUT[epnum];
- edpt_dma_start(&NRF_USBD->TASKS_STARTEPOUT[epnum]);
+ // Trigger DMA move data from Endpoint -> SRAM
+ NRF_USBD->EPOUT[epnum].PTR = (uint32_t) xfer->buffer;
+ NRF_USBD->EPOUT[epnum].MAXCNT = xact_len;
+ edpt_dma_start(&NRF_USBD->TASKS_STARTEPOUT[epnum]);
+ }
xfer->buffer += xact_len;
xfer->actual_len += xact_len;
}
@@ -205,7 +230,7 @@ static void xact_in_prepare(uint8_t epnum)
xfer_td_t* xfer = get_td(epnum, TUSB_DIR_IN);
// Each transaction is up to Max Packet Size
- uint8_t const xact_len = tu_min16(xfer->total_len - xfer->actual_len, xfer->mps);
+ uint16_t const xact_len = tu_min16(xfer->total_len - xfer->actual_len, xfer->mps);
NRF_USBD->EPIN[epnum].PTR = (uint32_t) xfer->buffer;
NRF_USBD->EPIN[epnum].MAXCNT = xact_len;
@@ -296,20 +321,94 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
_dcd.xfer[epnum][dir].mps = desc_edpt->wMaxPacketSize.size;
- if ( dir == TUSB_DIR_OUT )
+ if (desc_edpt->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS)
{
- NRF_USBD->INTENSET = TU_BIT(USBD_INTEN_ENDEPOUT0_Pos + epnum);
- NRF_USBD->EPOUTEN |= TU_BIT(epnum);
- }else
+ if (dir == TUSB_DIR_OUT)
+ {
+ NRF_USBD->INTENSET = TU_BIT(USBD_INTEN_ENDEPOUT0_Pos + epnum);
+ NRF_USBD->EPOUTEN |= TU_BIT(epnum);
+ }else
+ {
+ NRF_USBD->INTENSET = TU_BIT(USBD_INTEN_ENDEPIN0_Pos + epnum);
+ NRF_USBD->EPINEN |= TU_BIT(epnum);
+ }
+ }
+ else
{
- NRF_USBD->INTENSET = TU_BIT(USBD_INTEN_ENDEPIN0_Pos + epnum);
- NRF_USBD->EPINEN |= TU_BIT(epnum);
+ TU_ASSERT(epnum == EP_ISO_NUM);
+ if (dir == TUSB_DIR_OUT)
+ {
+ // SPLIT ISO buffer when ISO IN endpoint is already opened.
+ if (_dcd.xfer[EP_ISO_NUM][TUSB_DIR_IN].mps) NRF_USBD->ISOSPLIT = USBD_ISOSPLIT_SPLIT_HalfIN;
+ // Clear old events
+ NRF_USBD->EVENTS_ENDISOOUT = 0;
+ // Clear SOF event in case interrupt was not enabled yet.
+ if ((NRF_USBD->INTEN & USBD_INTEN_SOF_Msk) == 0) NRF_USBD->EVENTS_SOF = 0;
+ // Enable SOF and ISOOUT interrupts, and ISOOUT endpoint.
+ NRF_USBD->INTENSET = USBD_INTENSET_ENDISOOUT_Msk | USBD_INTENSET_SOF_Msk;
+ NRF_USBD->EPOUTEN |= USBD_EPOUTEN_ISOOUT_Msk;
+ }
+ else
+ {
+ NRF_USBD->EVENTS_ENDISOIN = 0;
+ // SPLIT ISO buffer when ISO OUT endpoint is already opened.
+ if (_dcd.xfer[EP_ISO_NUM][TUSB_DIR_OUT].mps) NRF_USBD->ISOSPLIT = USBD_ISOSPLIT_SPLIT_HalfIN;
+ // Clear SOF event in case interrupt was not enabled yet.
+ if ((NRF_USBD->INTEN & USBD_INTEN_SOF_Msk) == 0) NRF_USBD->EVENTS_SOF = 0;
+ // Enable SOF and ISOIN interrupts, and ISOIN endpoint.
+ NRF_USBD->INTENSET = USBD_INTENSET_ENDISOIN_Msk | USBD_INTENSET_SOF_Msk;
+ NRF_USBD->EPINEN |= USBD_EPINEN_ISOIN_Msk;
+ }
}
__ISB(); __DSB();
return true;
}
+void dcd_edpt_close (uint8_t rhport, uint8_t ep_addr)
+{
+ (void) rhport;
+
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+
+ if (epnum != EP_ISO_NUM)
+ {
+ // CBI
+ if (dir == TUSB_DIR_OUT)
+ {
+ NRF_USBD->INTENCLR = TU_BIT(USBD_INTEN_ENDEPOUT0_Pos + epnum);
+ NRF_USBD->EPOUTEN &= ~TU_BIT(epnum);
+ }
+ else
+ {
+ NRF_USBD->INTENCLR = TU_BIT(USBD_INTEN_ENDEPIN0_Pos + epnum);
+ NRF_USBD->EPINEN &= ~TU_BIT(epnum);
+ }
+ }
+ else
+ {
+ _dcd.xfer[EP_ISO_NUM][dir].mps = 0;
+ // ISO
+ if (dir == TUSB_DIR_OUT)
+ {
+ NRF_USBD->INTENCLR = USBD_INTENCLR_ENDISOOUT_Msk;
+ NRF_USBD->EPOUTEN &= ~USBD_EPOUTEN_ISOOUT_Msk;
+ NRF_USBD->EVENTS_ENDISOOUT = 0;
+ }
+ else
+ {
+ NRF_USBD->INTENCLR = USBD_INTENCLR_ENDISOIN_Msk;
+ NRF_USBD->EPINEN &= ~USBD_EPINEN_ISOIN_Msk;
+ }
+ // One of the ISO endpoints closed, no need to split buffers any more.
+ NRF_USBD->ISOSPLIT = USBD_ISOSPLIT_SPLIT_OneDir;
+ // When both ISO endpoint are close there is no need for SOF any more.
+ if (_dcd.xfer[EP_ISO_NUM][TUSB_DIR_IN].mps + _dcd.xfer[EP_ISO_NUM][TUSB_DIR_OUT].mps == 0) NRF_USBD->INTENCLR = USBD_INTENCLR_SOF_Msk;
+ }
+ __ISB(); __DSB();
+}
+
bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
{
(void) rhport;
@@ -361,11 +460,12 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr)
{
(void) rhport;
+ uint8_t const epnum = tu_edpt_number(ep_addr);
- if ( tu_edpt_number(ep_addr) == 0 )
+ if ( epnum == 0 )
{
NRF_USBD->TASKS_EP0STALL = 1;
- }else
+ }else if (epnum != EP_ISO_NUM)
{
NRF_USBD->EPSTALL = (USBD_EPSTALL_STALL_Stall << USBD_EPSTALL_STALL_Pos) | ep_addr;
}
@@ -376,8 +476,9 @@ void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr)
void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr)
{
(void) rhport;
+ uint8_t const epnum = tu_edpt_number(ep_addr);
- if ( tu_edpt_number(ep_addr) )
+ if ( epnum != 0 && epnum != EP_ISO_NUM )
{
// clear stall
NRF_USBD->EPSTALL = (USBD_EPSTALL_STALL_UnStall << USBD_EPSTALL_STALL_Pos) | ep_addr;
@@ -435,8 +536,31 @@ void dcd_int_handler(uint8_t rhport)
dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true);
}
+ // ISOIN: Data was moved to endpoint buffer, client will be notified in SOF
+ if ( int_status & USBD_INTEN_ENDISOIN_Msk )
+ {
+ xfer_td_t* xfer = get_td(EP_ISO_NUM, TUSB_DIR_IN);
+
+ xfer->actual_len = NRF_USBD->ISOIN.AMOUNT;
+ // Data transferred from RAM to endpoint output buffer.
+ // Next transfer can be scheduled after SOF.
+ xfer->iso_in_transfer_ready = true;
+ }
+
if ( int_status & USBD_INTEN_SOF_Msk )
{
+ // ISOOUT: Transfer data gathered in previous frame from buffer to RAM
+ if (NRF_USBD->EPOUTEN & USBD_EPOUTEN_ISOOUT_Msk)
+ {
+ xact_out_dma(EP_ISO_NUM);
+ }
+ // ISOIN: Notify client that data was transferred
+ xfer_td_t* xfer = get_td(EP_ISO_NUM, TUSB_DIR_IN);
+ if ( xfer->iso_in_transfer_ready )
+ {
+ xfer->iso_in_transfer_ready = false;
+ dcd_event_xfer_complete(0, EP_ISO_NUM | TUSB_DIR_IN_MASK, xfer->actual_len, XFER_RESULT_SUCCESS, true);
+ }
dcd_event_bus_signal(0, DCD_EVENT_SOF, true);
}
@@ -518,8 +642,11 @@ void dcd_int_handler(uint8_t rhport)
* Note: Since nRF controller auto ACK next packet without SW awareness
* We must handle this stage before Host -> Endpoint just in case
* 2 event happens at once
+ * ISO OUT: Transaction must fit in single packed, it can be shorter then total
+ * len if Host decides to sent fewer bytes, it this case transaction is also
+ * complete and next transfer is not initiated here like for CBI.
*/
- for(uint8_t epnum=0; epnum<8; epnum++)
+ for(uint8_t epnum=0; epnum<EP_COUNT+1; epnum++)
{
if ( tu_bit_test(int_status, USBD_INTEN_ENDEPOUT0_Pos+epnum))
{
@@ -530,7 +657,7 @@ void dcd_int_handler(uint8_t rhport)
xfer->data_received = false;
// Transfer complete if transaction len < Max Packet Size or total len is transferred
- if ( (xact_len == xfer->mps) && (xfer->actual_len < xfer->total_len) )
+ if ( (epnum != EP_ISO_NUM) && (xact_len == xfer->mps) && (xfer->actual_len < xfer->total_len) )
{
// Prepare for next transaction
xact_out_prepare(epnum);
diff --git a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c
index 32179874e..dc125a758 100644
--- a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c
+++ b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c
@@ -203,6 +203,11 @@ static inline void reg16_clear_bits(__IO uint16_t *reg, uint16_t mask) {
*reg = (uint16_t)(*reg & ~mask);
}
+// Bits in ISTR are cleared upon writing 0
+static inline void clear_istr_bits(uint16_t mask) {
+ USB->ISTR = ~mask;
+}
+
void dcd_init (uint8_t rhport)
{
/* Clocks should already be enabled */
@@ -231,7 +236,7 @@ void dcd_init (uint8_t rhport)
USB->BTABLE = DCD_STM32_BTABLE_BASE;
- reg16_clear_bits(&USB->ISTR, USB_ISTR_ALL_EVENTS); // Clear pending interrupts
+ USB->ISTR = 0; // Clear pending interrupts
// Reset endpoints to disabled
for(uint32_t i=0; i<STFSDEV_EP_COUNT; i++)
@@ -276,9 +281,23 @@ void dcd_int_enable (uint8_t rhport)
#if CFG_TUSB_MCU == OPT_MCU_STM32F0 || CFG_TUSB_MCU == OPT_MCU_STM32L0
NVIC_EnableIRQ(USB_IRQn);
#elif CFG_TUSB_MCU == OPT_MCU_STM32F3
- NVIC_EnableIRQ(USB_HP_CAN_TX_IRQn);
- NVIC_EnableIRQ(USB_LP_CAN_RX0_IRQn);
- NVIC_EnableIRQ(USBWakeUp_IRQn);
+ // Some STM32F302/F303 devices allow to remap the USB interrupt vectors from
+ // shared USB/CAN IRQs to separate CAN and USB IRQs.
+ // This dynamically checks if this remap is active to enable the right IRQs.
+ #ifdef SYSCFG_CFGR1_USB_IT_RMP
+ if (SYSCFG->CFGR1 & SYSCFG_CFGR1_USB_IT_RMP)
+ {
+ NVIC_EnableIRQ(USB_HP_IRQn);
+ NVIC_EnableIRQ(USB_LP_IRQn);
+ NVIC_EnableIRQ(USBWakeUp_RMP_IRQn);
+ }
+ else
+ #endif
+ {
+ NVIC_EnableIRQ(USB_HP_CAN_TX_IRQn);
+ NVIC_EnableIRQ(USB_LP_CAN_RX0_IRQn);
+ NVIC_EnableIRQ(USBWakeUp_IRQn);
+ }
#elif CFG_TUSB_MCU == OPT_MCU_STM32F1
NVIC_EnableIRQ(USB_HP_CAN1_TX_IRQn);
NVIC_EnableIRQ(USB_LP_CAN1_RX0_IRQn);
@@ -296,9 +315,23 @@ void dcd_int_disable(uint8_t rhport)
#if CFG_TUSB_MCU == OPT_MCU_STM32F0 || CFG_TUSB_MCU == OPT_MCU_STM32L0
NVIC_DisableIRQ(USB_IRQn);
#elif CFG_TUSB_MCU == OPT_MCU_STM32F3
- NVIC_DisableIRQ(USB_HP_CAN_TX_IRQn);
- NVIC_DisableIRQ(USB_LP_CAN_RX0_IRQn);
- NVIC_DisableIRQ(USBWakeUp_IRQn);
+ // Some STM32F302/F303 devices allow to remap the USB interrupt vectors from
+ // shared USB/CAN IRQs to separate CAN and USB IRQs.
+ // This dynamically checks if this remap is active to disable the right IRQs.
+ #ifdef SYSCFG_CFGR1_USB_IT_RMP
+ if (SYSCFG->CFGR1 & SYSCFG_CFGR1_USB_IT_RMP)
+ {
+ NVIC_DisableIRQ(USB_HP_IRQn);
+ NVIC_DisableIRQ(USB_LP_IRQn);
+ NVIC_DisableIRQ(USBWakeUp_RMP_IRQn);
+ }
+ else
+ #endif
+ {
+ NVIC_DisableIRQ(USB_HP_CAN_TX_IRQn);
+ NVIC_DisableIRQ(USB_LP_CAN_RX0_IRQn);
+ NVIC_DisableIRQ(USBWakeUp_IRQn);
+ }
#elif CFG_TUSB_MCU == OPT_MCU_STM32F1
NVIC_DisableIRQ(USB_HP_CAN1_TX_IRQn);
NVIC_DisableIRQ(USB_LP_CAN1_RX0_IRQn);
@@ -512,7 +545,7 @@ void dcd_int_handler(uint8_t rhport) {
if(int_status & USB_ISTR_RESET) {
// USBRST is start of reset.
- reg16_clear_bits(&USB->ISTR, USB_ISTR_RESET);
+ clear_istr_bits(USB_ISTR_RESET);
dcd_handle_bus_reset();
dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true);
return; // Don't do the rest of the things here; perhaps they've been cleared?
@@ -523,14 +556,13 @@ void dcd_int_handler(uint8_t rhport) {
/* servicing of the endpoint correct transfer interrupt */
/* clear of the CTR flag into the sub */
dcd_ep_ctr_handler();
- reg16_clear_bits(&USB->ISTR, USB_ISTR_CTR);
}
if (int_status & USB_ISTR_WKUP)
{
reg16_clear_bits(&USB->CNTR, USB_CNTR_LPMODE);
reg16_clear_bits(&USB->CNTR, USB_CNTR_FSUSP);
- reg16_clear_bits(&USB->ISTR, USB_ISTR_WKUP);
+ clear_istr_bits(USB_ISTR_WKUP);
dcd_event_bus_signal(0, DCD_EVENT_RESUME, true);
}
@@ -544,13 +576,13 @@ void dcd_int_handler(uint8_t rhport) {
USB->CNTR |= USB_CNTR_LPMODE;
/* clear of the ISTR bit must be done after setting of CNTR_FSUSP */
- reg16_clear_bits(&USB->ISTR, USB_ISTR_SUSP);
+ clear_istr_bits(USB_ISTR_SUSP);
dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true);
}
#if USE_SOF
if(int_status & USB_ISTR_SOF) {
- reg16_clear_bits(&USB->ISTR, USB_ISTR_SOF);
+ clear_istr_bits(USB_ISTR_SOF);
dcd_event_bus_signal(0, DCD_EVENT_SOF, true);
}
#endif
@@ -564,7 +596,7 @@ void dcd_int_handler(uint8_t rhport) {
{
remoteWakeCountdown--;
}
- reg16_clear_bits(&USB->ISTR, USB_ISTR_ESOF);
+ clear_istr_bits(USB_ISTR_ESOF);
}
}
diff --git a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev_pvt_st.h b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev_pvt_st.h
index 4f2b2294e..d26c700d2 100644
--- a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev_pvt_st.h
+++ b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev_pvt_st.h
@@ -143,13 +143,17 @@ static inline uint32_t pcd_get_eptype(USB_TypeDef * USBx, uint32_t bEpNum)
static inline void pcd_clear_rx_ep_ctr(USB_TypeDef * USBx, uint32_t bEpNum)
{
uint32_t regVal = pcd_get_endpoint(USBx, bEpNum);
- regVal &= 0x7FFFu & USB_EPREG_MASK;
+ regVal &= USB_EPREG_MASK;
+ regVal &= ~USB_EP_CTR_RX;
+ regVal |= USB_EP_CTR_TX; // preserve CTR_TX (clears on writing 0)
pcd_set_endpoint(USBx, bEpNum, regVal);
}
static inline void pcd_clear_tx_ep_ctr(USB_TypeDef * USBx, uint32_t bEpNum)
{
uint32_t regVal = pcd_get_endpoint(USBx, bEpNum);
- regVal &= regVal & 0xFF7FU & USB_EPREG_MASK;
+ regVal &= USB_EPREG_MASK;
+ regVal &= ~USB_EP_CTR_TX;
+ regVal |= USB_EP_CTR_RX; // preserve CTR_RX (clears on writing 0)
pcd_set_endpoint(USBx, bEpNum,regVal);
}
/**
diff --git a/src/portable/st/synopsys/dcd_synopsys.c b/src/portable/st/synopsys/dcd_synopsys.c
index cc7181c3d..ca306ff8f 100644
--- a/src/portable/st/synopsys/dcd_synopsys.c
+++ b/src/portable/st/synopsys/dcd_synopsys.c
@@ -4,6 +4,7 @@
* Copyright (c) 2018 Scott Shawcroft, 2019 William D. Jones for Adafruit Industries
* Copyright (c) 2019 Ha Thach (tinyusb.org)
* Copyright (c) 2020 Jan Duempelmann
+ * Copyright (c) 2020 Reinhard Panhuber
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -46,53 +47,53 @@
#if TUSB_OPT_DEVICE_ENABLED && \
( (CFG_TUSB_MCU == OPT_MCU_STM32F1 && defined(STM32F1_SYNOPSYS)) || \
- CFG_TUSB_MCU == OPT_MCU_STM32F2 || \
- CFG_TUSB_MCU == OPT_MCU_STM32F4 || \
- CFG_TUSB_MCU == OPT_MCU_STM32F7 || \
- CFG_TUSB_MCU == OPT_MCU_STM32H7 || \
- (CFG_TUSB_MCU == OPT_MCU_STM32L4 && defined(STM32L4_SYNOPSYS)) \
+ CFG_TUSB_MCU == OPT_MCU_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)) \
)
// EP_MAX : Max number of bi-directional endpoints including EP0
// EP_FIFO_SIZE : Size of dedicated USB SRAM
#if CFG_TUSB_MCU == OPT_MCU_STM32F1
- #include "stm32f1xx.h"
- #define EP_MAX_FS 4
- #define EP_FIFO_SIZE_FS 1280
+#include "stm32f1xx.h"
+#define EP_MAX_FS 4
+#define EP_FIFO_SIZE_FS 1280
#elif CFG_TUSB_MCU == OPT_MCU_STM32F2
- #include "stm32f2xx.h"
- #define EP_MAX_FS USB_OTG_FS_MAX_IN_ENDPOINTS
- #define EP_FIFO_SIZE_FS USB_OTG_FS_TOTAL_FIFO_SIZE
+#include "stm32f2xx.h"
+#define EP_MAX_FS USB_OTG_FS_MAX_IN_ENDPOINTS
+#define EP_FIFO_SIZE_FS USB_OTG_FS_TOTAL_FIFO_SIZE
#elif CFG_TUSB_MCU == OPT_MCU_STM32F4
- #include "stm32f4xx.h"
- #define EP_MAX_FS USB_OTG_FS_MAX_IN_ENDPOINTS
- #define EP_FIFO_SIZE_FS USB_OTG_FS_TOTAL_FIFO_SIZE
- #define EP_MAX_HS USB_OTG_HS_MAX_IN_ENDPOINTS
- #define EP_FIFO_SIZE_HS USB_OTG_HS_TOTAL_FIFO_SIZE
+#include "stm32f4xx.h"
+#define EP_MAX_FS USB_OTG_FS_MAX_IN_ENDPOINTS
+#define EP_FIFO_SIZE_FS USB_OTG_FS_TOTAL_FIFO_SIZE
+#define EP_MAX_HS USB_OTG_HS_MAX_IN_ENDPOINTS
+#define EP_FIFO_SIZE_HS USB_OTG_HS_TOTAL_FIFO_SIZE
#elif CFG_TUSB_MCU == OPT_MCU_STM32H7
- #include "stm32h7xx.h"
- #define EP_MAX_FS 9
- #define EP_FIFO_SIZE_FS 4096
- #define EP_MAX_HS 9
- #define EP_FIFO_SIZE_HS 4096
+#include "stm32h7xx.h"
+#define EP_MAX_FS 9
+#define EP_FIFO_SIZE_FS 4096
+#define EP_MAX_HS 9
+#define EP_FIFO_SIZE_HS 4096
#elif CFG_TUSB_MCU == OPT_MCU_STM32F7
- #include "stm32f7xx.h"
- #define EP_MAX_FS 6
- #define EP_FIFO_SIZE_FS 1280
- #define EP_MAX_HS 9
- #define EP_FIFO_SIZE_HS 4096
+#include "stm32f7xx.h"
+#define EP_MAX_FS 6
+#define EP_FIFO_SIZE_FS 1280
+#define EP_MAX_HS 9
+#define EP_FIFO_SIZE_HS 4096
#elif CFG_TUSB_MCU == OPT_MCU_STM32L4
- #include "stm32l4xx.h"
- #define EP_MAX_FS 6
- #define EP_FIFO_SIZE_FS 1280
+#include "stm32l4xx.h"
+#define EP_MAX_FS 6
+#define EP_FIFO_SIZE_FS 1280
#else
- #error "Unsupported MCUs"
+#error "Unsupported MCUs"
#endif
@@ -104,16 +105,16 @@
// On STM32 we associate Port0 to OTG_FS, and Port1 to OTG_HS
#if TUD_OPT_RHPORT == 0
- #define EP_MAX EP_MAX_FS
- #define EP_FIFO_SIZE EP_FIFO_SIZE_FS
- #define RHPORT_REGS_BASE USB_OTG_FS_PERIPH_BASE
- #define RHPORT_IRQn OTG_FS_IRQn
+#define EP_MAX EP_MAX_FS
+#define EP_FIFO_SIZE EP_FIFO_SIZE_FS
+#define RHPORT_REGS_BASE USB_OTG_FS_PERIPH_BASE
+#define RHPORT_IRQn OTG_FS_IRQn
#else
- #define EP_MAX EP_MAX_HS
- #define EP_FIFO_SIZE EP_FIFO_SIZE_HS
- #define RHPORT_REGS_BASE USB_OTG_HS_PERIPH_BASE
- #define RHPORT_IRQn OTG_HS_IRQn
+#define EP_MAX EP_MAX_HS
+#define EP_FIFO_SIZE EP_FIFO_SIZE_HS
+#define RHPORT_REGS_BASE USB_OTG_HS_PERIPH_BASE
+#define RHPORT_IRQn OTG_HS_IRQn
#endif
#define GLOBAL_BASE(_port) ((USB_OTG_GlobalTypeDef*) RHPORT_REGS_BASE)
@@ -135,8 +136,18 @@ typedef struct {
uint8_t * buffer;
uint16_t total_len;
uint16_t max_size;
+ uint8_t interval;
} xfer_ctl_t;
+// EP size and transfer type report
+typedef struct TU_ATTR_PACKED {
+ // The following format may look complicated but it is the most elegant way of addressing the required fields: EP number, EP direction, and EP transfer type.
+ // The codes assigned to those fields, according to the USB specification, can be neatly used as indices.
+ uint16_t ep_size[EP_MAX][2]; ///< dim 1: EP number, dim 2: EP direction denoted by TUSB_DIR_OUT (= 0) and TUSB_DIR_IN (= 1)
+ bool ep_transfer_type[EP_MAX][2][4]; ///< dim 1: EP number, dim 2: EP direction, dim 3: transfer type, where 0 = Control, 1 = Isochronous, 2 = Bulk, and 3 = Interrupt
+ ///< I know very well that EP0 can only be used as control EP and we waste space here but for the sake of simplicity we accept that. It is used in a non-persistent way anyway!
+} ep_sz_tt_report_t;
+
typedef volatile uint32_t * usb_fifo_t;
xfer_ctl_t xfer_status[EP_MAX][2];
@@ -300,7 +311,6 @@ static void set_speed(uint8_t rhport, tusb_speed_t speed)
dev->DCFG |= (bitvalue << USB_OTG_DCFG_DSPD_Pos);
}
-
#if defined(USB_HS_PHYC)
static bool USB_HS_PHYCInit(void)
{
@@ -366,7 +376,7 @@ static void edpt_schedule_packets(uint8_t rhport, uint8_t const epnum, uint8_t c
in_ep[epnum].DIEPCTL |= USB_OTG_DIEPCTL_EPENA | USB_OTG_DIEPCTL_CNAK;
// For ISO endpoint set correct odd/even bit for next frame.
- if ((in_ep[epnum].DIEPCTL & USB_OTG_DIEPCTL_EPTYP) == USB_OTG_DIEPCTL_EPTYP_0)
+ if ((in_ep[epnum].DIEPCTL & USB_OTG_DIEPCTL_EPTYP) == USB_OTG_DIEPCTL_EPTYP_0 && (XFER_CTL_BASE(epnum, dir))->interval == 1)
{
// Take odd/even bit from frame counter.
uint32_t const odd_frame_now = (dev->DSTS & (1u << USB_OTG_DSTS_FNSOF_Pos));
@@ -383,6 +393,12 @@ static void edpt_schedule_packets(uint8_t rhport, uint8_t const epnum, uint8_t c
((total_bytes << USB_OTG_DOEPTSIZ_XFRSIZ_Pos) & USB_OTG_DOEPTSIZ_XFRSIZ_Msk);
out_ep[epnum].DOEPCTL |= USB_OTG_DOEPCTL_EPENA | USB_OTG_DOEPCTL_CNAK;
+ if ((out_ep[epnum].DOEPCTL & USB_OTG_DOEPCTL_EPTYP) == USB_OTG_DOEPCTL_EPTYP_0 && (XFER_CTL_BASE(epnum, dir))->interval == 1)
+ {
+ // Take odd/even bit from frame counter.
+ uint32_t const odd_frame_now = (dev->DSTS & (1u << USB_OTG_DSTS_FNSOF_Pos));
+ out_ep[epnum].DOEPCTL |= (odd_frame_now ? USB_OTG_DOEPCTL_SD0PID_SEVNFRM_Msk : USB_OTG_DOEPCTL_SODDFRM_Msk);
+ }
}
}
@@ -455,8 +471,8 @@ void dcd_init (uint8_t rhport)
if ( rhport == 0 ) usb_otg->GCCFG |= USB_OTG_GCCFG_PWRDWN;
usb_otg->GINTMSK |= USB_OTG_GINTMSK_USBRST | USB_OTG_GINTMSK_ENUMDNEM |
- USB_OTG_GINTMSK_USBSUSPM | USB_OTG_GINTMSK_WUIM |
- USB_OTG_GINTMSK_RXFLVLM | (USE_SOF ? USB_OTG_GINTMSK_SOFM : 0);
+ USB_OTG_GINTMSK_USBSUSPM | USB_OTG_GINTMSK_WUIM |
+ USB_OTG_GINTMSK_RXFLVLM | (USE_SOF ? USB_OTG_GINTMSK_SOFM : 0);
// Enable global interrupt
usb_otg->GAHBCFG |= USB_OTG_GAHBCFG_GINT;
@@ -534,12 +550,13 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, dir);
xfer->max_size = desc_edpt->wMaxPacketSize.size;
+ xfer->interval = desc_edpt->bInterval;
if(dir == TUSB_DIR_OUT)
{
out_ep[epnum].DOEPCTL |= (1 << USB_OTG_DOEPCTL_USBAEP_Pos) |
- (desc_edpt->bmAttributes.xfer << USB_OTG_DOEPCTL_EPTYP_Pos) |
- (desc_edpt->wMaxPacketSize.size << USB_OTG_DOEPCTL_MPSIZ_Pos);
+ (desc_edpt->bmAttributes.xfer << USB_OTG_DOEPCTL_EPTYP_Pos) |
+ (desc_edpt->wMaxPacketSize.size << USB_OTG_DOEPCTL_MPSIZ_Pos);
dev->DAINTMSK |= (1 << (USB_OTG_DAINTMSK_OEPM_Pos + epnum));
}
@@ -571,7 +588,7 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
// - Bulk/ISO is max(EPSize, remaining-fifo / non-opened-EPIN)
uint16_t const fifo_remaining = EP_FIFO_SIZE/4 - _allocated_fifo_words;
- uint16_t fifo_size = desc_edpt->wMaxPacketSize.size / 4;
+ uint16_t fifo_size = (desc_edpt->wMaxPacketSize.size + 3) / 4; // +3 for rounding up to next full word
if ( desc_edpt->bmAttributes.xfer != TUSB_XFER_INTERRUPT )
{
@@ -595,10 +612,10 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
_allocated_fifo_words += fifo_size;
in_ep[epnum].DIEPCTL |= (1 << USB_OTG_DIEPCTL_USBAEP_Pos) |
- (epnum << USB_OTG_DIEPCTL_TXFNUM_Pos) |
- (desc_edpt->bmAttributes.xfer << USB_OTG_DIEPCTL_EPTYP_Pos) |
- (desc_edpt->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS ? USB_OTG_DOEPCTL_SD0PID_SEVNFRM : 0) |
- (desc_edpt->wMaxPacketSize.size << USB_OTG_DIEPCTL_MPSIZ_Pos);
+ (epnum << USB_OTG_DIEPCTL_TXFNUM_Pos) |
+ (desc_edpt->bmAttributes.xfer << USB_OTG_DIEPCTL_EPTYP_Pos) |
+ (desc_edpt->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS ? USB_OTG_DOEPCTL_SD0PID_SEVNFRM : 0) |
+ (desc_edpt->wMaxPacketSize.size << USB_OTG_DIEPCTL_MPSIZ_Pos);
dev->DAINTMSK |= (1 << (USB_OTG_DAINTMSK_IEPM_Pos + epnum));
}
@@ -821,7 +838,7 @@ static void handle_rxflvl_ints(uint8_t rhport, USB_OTG_OUTEndpointTypeDef * out_
switch(pktsts) {
case 0x01: // Global OUT NAK (Interrupt)
- break;
+ break;
case 0x02: // Out packet recvd
{
@@ -849,18 +866,18 @@ static void handle_rxflvl_ints(uint8_t rhport, USB_OTG_OUTEndpointTypeDef * out_
case 0x04: // Setup packet done (Interrupt)
out_ep[epnum].DOEPTSIZ |= (3 << USB_OTG_DOEPTSIZ_STUPCNT_Pos);
- break;
+ break;
case 0x06: // Setup packet recvd
// We can receive up to three setup packets in succession, but
// only the last one is valid.
_setup_packet[0] = (* rx_fifo);
_setup_packet[1] = (* rx_fifo);
- break;
+ break;
default: // Invalid
TU_BREAKPOINT();
- break;
+ break;
}
}
@@ -1040,6 +1057,12 @@ void dcd_int_handler(uint8_t rhport)
// IEPINT bit read-only
handle_epin_ints(rhport, dev, in_ep);
}
+
+// // Check for Incomplete isochronous IN transfer
+// if(int_status & USB_OTG_GINTSTS_IISOIXFR) {
+// printf(" IISOIXFR!\r\n");
+//// TU_LOG2(" IISOIXFR!\r\n");
+// }
}
#endif
diff --git a/src/tusb.h b/src/tusb.h
index c252a5789..8b0dd103c 100644
--- a/src/tusb.h
+++ b/src/tusb.h
@@ -76,6 +76,10 @@
#include "class/msc/msc_device.h"
#endif
+#if CFG_TUD_AUDIO
+ #include "class/audio/audio_device.h"
+#endif
+
#if CFG_TUD_MIDI
#include "class/midi/midi_device.h"
#endif
diff --git a/src/tusb_option.h b/src/tusb_option.h
index 7e486bff0..393940282 100644
--- a/src/tusb_option.h
+++ b/src/tusb_option.h
@@ -110,11 +110,13 @@
// Allow to use command line to change the config name/location
-#ifndef CFG_TUSB_CONFIG_FILE
- #define CFG_TUSB_CONFIG_FILE "tusb_config.h"
+#ifdef CFG_TUSB_CONFIG_FILE
+ #include CFG_TUSB_CONFIG_FILE
+#else
+ #include "tusb_config.h"
#endif
-#include CFG_TUSB_CONFIG_FILE
+
/** \addtogroup group_configuration
* @{ */
@@ -207,6 +209,10 @@
#define CFG_TUD_HID 0
#endif
+#ifndef CFG_TUD_AUDIO
+ #define CFG_TUD_AUDIO 0
+#endif
+
#ifndef CFG_TUD_MIDI
#define CFG_TUD_MIDI 0
#endif